|
|
1.1 ! root 1: ;------------------------------------------------------------------------------ ! 2: ; Talking BIOS device driver for the AT&T PC6300. ! 3: ; Copyright (C) Karl Dahlke 1987 ! 4: ; This software may be freely used and distributed ! 5: ; for any non-profit purpose. ! 6: ;------------------------------------------------------------------------------ ! 7: ! 8: ; talkbios.asm: assembly base and support for talking BIOS routines ! 9: ! 10: ! 11: WDFIXLEN equ 4096 ; length of word correction table ! 12: KBSIZ equ 120 ; length of type ahead keyboard buffer ! 13: ADDR equ offset DGROUP ! 14: ! 15: ; These routines are bundled into a pseudo device driver, ! 16: ; which is installed at init time. ! 17: ; The device cannot be meaningfully read or written. ! 18: ! 19: ; Compatible with ansi.sys, ctrm, and most other applications. ! 20: ! 21: ; Device Driver skeleton taken from public domain, ! 22: ; written by Frank Whaley. ! 23: ! 24: ; The real time interrupt switches stacks, ! 25: ; since there are a lot of function calls in it, ! 26: ; and it holds the CPU for quite a while. ! 27: ; The other interrupts and the device driver ! 28: ; use the existing stacks. In general, there is very little ! 29: ; documentation regarding stacks. How much room is required ! 30: ; for DOS interrupts, worst case? How much room is available on system ! 31: ; stacks (e.g. when device drivers get control)? ! 32: ; It makes it difficult to write robust programs. ! 33: ; Well, MS-DOS drivers seem quite unconcerned about these things, so, ! 34: ; For now, we just press on. ! 35: ; The real time interrupt stack tromps right over the ! 36: ; initialization code, saving memory. ! 37: ! 38: ; here come the segments ! 39: ; segment structure is compatible with microsoft C, small memory model ! 40: _TEXT segment byte public 'CODE' ! 41: _TEXT ends ! 42: ; empty segments, but C uses them, so declare them ! 43: CONST segment word public 'CONST' ! 44: CONST ends ! 45: _BSS segment word public 'BSS' ! 46: _BSS ends ! 47: _DATA segment word public 'DATA' ! 48: _DATA ends ! 49: _EDATA segment para public 'EDATA' ! 50: _EDATA ends ! 51: ! 52: DGROUP group CONST, _BSS, _DATA, _EDATA ! 53: assume cs: _TEXT, ds: DGROUP ! 54: ! 55: ! 56: ; make microsoft C/link phase happy (see crt0.asm) ! 57: public __acrtused ! 58: __acrtused = 9876h ; funny value not easily matched in SYMDEB ! 59: ! 60: ! 61: _TEXT segment ! 62: ! 63: extrn _sdoutchar:near, _sdinkey:near, _sdioctl:near ! 64: extrn _sdload:near, _sdtime:near, _dosline:near ! 65: public myds ! 66: ! 67: org 0 ; Device drivers start at 0 ! 68: ! 69: HDR proc far ! 70: ; Device Header ! 71: dd -1 ; -> next device ! 72: dw 0c000h ; attributes: character, ioctl ! 73: DW strategy ; -> device strategy ! 74: DW interrupt ; -> device interrupt ! 75: db "SPEAK$ " ; talking BIOS routines ! 76: ! 77: ; the following variables are accessed through cs. ! 78: rh1ptr label word ; to access by words ! 79: rhptr dd 0 ; -> Request Header ! 80: orig_rti dw 0,0 ; original real time interrupt vector ! 81: orig_kbi dw 0,0 ; original keyboard interrupt vector ! 82: orig_bkb dw 0,0 ; original bios keyboard interrupt vector ! 83: orig_bsc dw 0,0 ; original bios screen interrupt vector ! 84: ; save ss and sp when switching to real time interrupt stack ! 85: save_ss dw 0 ! 86: save_sp dw 0 ! 87: myds dw 0 ; ds segment when running this system ! 88: nestrti db 1 ; check for nested real time interrupts ! 89: ! 90: ; functions for device driver commands ! 91: functab label word ! 92: DW init ! 93: dw badf ! 94: dw badf ! 95: DW ioctl ! 96: dw nulf, nulf, nulf, nulf, nulf, nulf, nulf, nulf ! 97: ! 98: ! 99: ! 100: strategy: ! 101: ; squirrel away pointer to the msdos request ! 102: ; I don't understand why dos doesn't just call the interrupt routine directly, ! 103: ; and bypass this intermediate, seemingly unnecessary strategy routine. ! 104: mov rh1ptr,BX ; save request header ptr ! 105: mov rh1ptr + 2,ES ! 106: ret ! 107: ! 108: interrupt: ! 109: ; device driver interrupt routine, *not* a true interrupt routine. ! 110: ; Perform the action requested by dos, ! 111: ; request structure is pointed to by rhptr. ! 112: ; We save all registers, gather relevant information ! 113: ; from the request header, ! 114: ; call the appropriate routine, ! 115: ; restore registers, and return. ! 116: ! 117: pushf ! 118: push ds ! 119: push es ! 120: push ax ! 121: push bx ! 122: push cx ! 123: push dx ! 124: push si ! 125: push di ! 126: cld ! 127: sti ! 128: mov ds,myds ! 129: ! 130: ; get relevant information from request header, ! 131: ; for the functions to use ! 132: les di,rhptr ! 133: mov al,es:[di+2] ; ax = Command Code ! 134: cbw ! 135: ; set default status = DONE ! 136: mov es:[di+3],100h ! 137: mov cx,es:[di+18] ! 138: les di,es:[di+14] ! 139: ! 140: ; call our functions ! 141: cmp al,12 ! 142: jb cmdok ! 143: ; command out of range ! 144: mov al,2 ; replace with an illegal command ! 145: cmdok: mov si,offset _TEXT:functab ! 146: add si,ax ! 147: add si,ax ! 148: call cs:[si] ! 149: ! 150: pop di ! 151: pop si ! 152: pop dx ! 153: pop cx ! 154: pop bx ! 155: pop ax ! 156: pop es ! 157: pop ds ! 158: popf ! 159: ret ! 160: ! 161: HDR endp ! 162: ! 163: ! 164: ; coherent kernel functions ! 165: cohker proc near ! 166: public _sphi, _spl, _inb, _outb, _isin, _kalloc ! 167: public _memcpy, _setivec, _read_t0 ! 168: ! 169: _read_t0: pushf ! 170: cli ! 171: mov al,0 ! 172: out 43h,al ! 173: out 0e0h,al ! 174: in al,40h ! 175: mov ah,al ! 176: out 0e0h,al ! 177: in al,40h ! 178: out 0e0h,al ! 179: xchg al,ah ! 180: popf ! 181: ret ! 182: ! 183: ; a simplistic emulation of setivec(), clrivec() is not provided. ! 184: ; may only be called once, with either 2 or 3. ! 185: _setivec: ! 186: push bp ! 187: mov bp,sp ! 188: ; change parameters if on com2 ! 189: cmp word ptr [bp+4],4 ! 190: jz oncom1 ! 191: mov intofs,2ch ! 192: mov irm,0f7h ! 193: dec eoi ! 194: oncom1: ! 195: mov ax,[bp+6] ! 196: mov ivec_fn,ax ! 197: ! 198: ; set interrupt vector ! 199: xor ax,ax ! 200: mov es,ax ! 201: mov bx,intofs ! 202: mov word ptr es:[bx],offset _TEXT: ih_com ! 203: mov es:[bx+2],cs ! 204: ! 205: in al,21h ;set enable bit on 8259 ! 206: and al,irm ! 207: nop ; timing ! 208: nop ! 209: out 21h,al ! 210: ! 211: pop bp ! 212: ret ! 213: ! 214: _sphi: pushf ! 215: cli ! 216: pop ax ! 217: ret ! 218: ! 219: _spl: push bp ! 220: mov bp,sp ! 221: mov ax,[bp+4] ! 222: push ax ! 223: popf ! 224: pop bp ! 225: ret ! 226: ! 227: _inb: push bp ! 228: mov bp,sp ! 229: mov dx,[bp+4] ! 230: mov ah,0 ! 231: in al,dx ! 232: pop bp ! 233: ret ! 234: ! 235: _outb: push bp ! 236: mov bp,sp ! 237: mov ax,[bp+6] ! 238: mov dx,[bp+4] ! 239: out dx,al ! 240: pop bp ! 241: ret ! 242: ! 243: _memcpy: push bp ! 244: mov bp,sp ! 245: push ds ! 246: pop es ! 247: push si ! 248: push di ! 249: mov si,[bp+6] ! 250: mov di,[bp+4] ! 251: mov cx,[bp+8] ! 252: repz movsb ! 253: pop di ! 254: pop si ! 255: pop bp ! 256: ret ! 257: ! 258: ; store character in our internal BIOS-like keyboard buffer ! 259: _isin: push bp ! 260: mov bp,sp ! 261: pushf ! 262: cli ! 263: mov ax,[bp+4] ! 264: ; -1 means flush the buffer ! 265: cmp ax,-1 ! 266: jnz isinflush ! 267: mov bx,ADDR: kbbuf ! 268: mov kbhead,bx ! 269: mov kbtail,bx ! 270: mov ax, 0 ! 271: jmp short endisin ! 272: isinflush: ! 273: ; work around an MSDOS bug, control C goes right to the top ! 274: ; of the input queue, so you can blow away a running program, ! 275: ; even if you have accidentally typed another character. ! 276: cmp al,3 ! 277: jnz normchar ! 278: mov bx,ADDR: kbbuf ! 279: mov kbhead,bx ! 280: mov kbtail,bx ! 281: normchar: mov bx,kbhead ! 282: mov [bx],ax ! 283: inc bx ! 284: inc bx ! 285: cmp bx,ADDR: kbbuf+KBSIZ*2 ! 286: jnz wrap3 ! 287: mov bx,ADDR: kbbuf ! 288: wrap3: cmp bx,kbtail ! 289: mov ax,-1 ! 290: jz endisin ! 291: mov ax,0 ! 292: mov kbhead,bx ! 293: endisin: popf ! 294: pop bp ! 295: ret ! 296: ! 297: _kalloc: push bp ! 298: mov bp,sp ! 299: test byte ptr inload,0ffh ! 300: jz kalerr ! 301: mov cx,[bp+4] ! 302: mov ax,endbound ! 303: add ax,cx ! 304: jc kalerr ! 305: xchg ax,endbound ! 306: jmp short kalend ! 307: kalerr: mov ax,0 ! 308: kalend: pop bp ! 309: ret ! 310: cohker endp ! 311: ! 312: ; null function, for irrelevant comands ! 313: badf proc near ! 314: les di,rhptr ! 315: mov es:[di+3],8103h ; unknown function for this device driver ! 316: nulf: ret ! 317: badf endp ! 318: ! 319: ; init function, set buffer, set interrupt vectors, set memory usage ! 320: init proc near ! 321: ; set up our own ds ! 322: mov ax,offset _TEXT: DGROUP ! 323: mov cl,4 ! 324: shr ax,cl ! 325: mov bx,cs ! 326: add ax,bx ! 327: mov ds,ax ! 328: mov myds,ax ! 329: ! 330: ; zero .bss area ! 331: mov es,ax ! 332: xor ax,ax ! 333: mov cx,ADDR:databeg ! 334: mov di,ADDR:bssbeg ! 335: sub cx,di ! 336: shr cx,1 ! 337: rep stosw ! 338: ! 339: ; get buffer size from config.sys line ! 340: ; size specified in 1024 byte blocks, consistent with ramdisk.dev ! 341: les di,rhptr ! 342: les di,es:[di+18] ; es:di = config.sys command line ! 343: push es ! 344: push di ! 345: call _dosline ! 346: add sp,4 ! 347: ! 348: ; C initialization routine ! 349: call _sdload ! 350: ! 351: les si,rhptr ! 352: mov ax,endbound ! 353: mov es:[si+14],ax ! 354: mov es:[si+16],ds ! 355: mov inload,0 ! 356: ! 357: ; set interrupt vectors ! 358: ; should be done using dos calls 25h and 35h, ! 359: ; but only calls 00h through 0ch and 30h are supported ! 360: ; during device driver init phase. ! 361: ; Disk Operating System Technical Reference Programming Manual ! 362: ; [IBM inc.] chapter 3. ! 363: ; Therefore, we disable interrupts, ! 364: ; and write the lowmem vectors directly ! 365: xor bx,bx ! 366: mov ds,bx ! 367: cli ; rest of init procedure has interrupts disabled ! 368: mov ax,[bx+20h] ; save original real time interrupt vector ! 369: mov orig_rti,ax ! 370: mov ax,[bx+22h] ! 371: mov orig_rti+2,ax ! 372: mov word ptr [bx+20h],offset _TEXT: ih_rti ; real time interrupt ! 373: mov [bx+22h],cs ! 374: mov ax,[bx+24h] ; save original keyboard interrupt vector ! 375: mov orig_kbi,ax ! 376: mov ax,[bx+26h] ! 377: mov orig_kbi+2,ax ! 378: mov word ptr [bx+24h], offset _TEXT: ih_kbi ! 379: mov [bx+26h],cs ! 380: ! 381: ; take over BIOS keyboard interrupt, ! 382: ; to accommodate our internal type ahead buffer (long). ! 383: mov ax,[bx+58h] ; save original bios keyboard interrupt vector ! 384: mov orig_bkb,ax ! 385: mov ax,[bx+5ah] ! 386: mov orig_bkb+2,ax ! 387: mov word ptr [bx+58h],offset _TEXT: ih_bkb ; BIOS keyboard interrupt ! 388: mov [bx+5ah],cs ! 389: ! 390: ; same for screen BIOS routine ! 391: mov ax,[bx+40h] ; save original bios screen interrupt vector ! 392: mov orig_bsc,ax ! 393: mov ax,[bx+42h] ! 394: mov orig_bsc+2,ax ! 395: mov word ptr [bx+40h],offset _TEXT: ih_bsc ; BIOS screen interrupt ! 396: mov [bx+42h],cs ! 397: ! 398: ; empty our own keyboard buffer ! 399: mov ds,myds ! 400: mov kbhead,ADDR: kbbuf ! 401: mov kbtail,ADDR: kbbuf ! 402: sti ! 403: ! 404: ; call original bios routine to clear any accumulated MS-DOS characters ! 405: drain: mov ah,1 ! 406: pushf ! 407: ; call far ptr cs:[orig_bkb], I can't figure out the masm syntax for this ! 408: db 2eh, 0ffh, 01eh ! 409: dw orig_bkb ! 410: jz endinit ! 411: mov ah,0 ; remove character ! 412: pushf ! 413: ; call far ptr cs:[orig_bkb] ! 414: db 2eh, 0ffh, 01eh ! 415: dw orig_bkb ! 416: jmp short drain ! 417: ! 418: endinit: ret ! 419: init endp ! 420: ! 421: ; read ioctl values ! 422: ; used by a calling process to copy the text buffer to a file ! 423: ioctl proc near ! 424: ; returns pointers to the start and end of the internal text buffer ! 425: ; plus the head and tail pointers. ! 426: ; thus only 16-byte ioctl reads are allowed ! 427: ; calling process must understand this driver implicitly. ! 428: cmp cx,16 ! 429: jnz badioctl ! 430: ; call C routine ! 431: push es ! 432: push di ! 433: mov ax, 0 ! 434: push ax ! 435: call _sdioctl ! 436: add sp,6 ! 437: or ax,ax ! 438: jz ioctlok ! 439: badioctl: ! 440: les di,rhptr ! 441: mov word ptr es:[di+18],0 ; 0 bytes transfered ! 442: mov es:[di+3],810bh ; read fault, cannot return that many bytes ! 443: ioctlok: ! 444: ret ! 445: ioctl endp ! 446: ! 447: ; screen BIOS routine ! 448: ; save characters written to screen, and pass all other commands through ! 449: ih_bsc proc far ! 450: cmp ah,14 ! 451: jnz go10 ! 452: cmp bx,7 ! 453: jz scrchar ! 454: ! 455: ; jmp far ptr cs:[orig_bsc] ! 456: go10: db 2eh, 0ffh, 02eh ! 457: dw orig_bsc ! 458: ! 459: scrchar: ! 460: push ds ! 461: push ax ! 462: push bx ! 463: push cx ! 464: push dx ! 465: mov ds,myds ! 466: pushf ! 467: sti ! 468: ! 469: push ax ! 470: ; console is owner ! 471: mov ax,0 ! 472: push ax ! 473: call _sdoutchar ! 474: add sp,4 ! 475: popf ! 476: ! 477: test al,2 ! 478: jz noesc ! 479: pop dx ! 480: pop cx ! 481: push cx ! 482: push dx ! 483: push ax ! 484: mov ax,0e1bh ! 485: mov bx,7 ! 486: pushf ! 487: ; call far ptr cs:[orig_bsc] ! 488: db 2eh, 0ffh, 01eh ! 489: dw orig_bsc ! 490: pop ax ! 491: noesc: test al,1 ! 492: pop dx ! 493: pop cx ! 494: pop bx ! 495: pop ax ! 496: pop ds ! 497: jz noscreen ! 498: jmp short go10 ! 499: ! 500: noscreen: iret ! 501: ih_bsc endp ! 502: ! 503: ; interrupt routines ! 504: ! 505: ; read the virtual screen, in real time ! 506: ; called at real time interrupt level ! 507: ; Since this mess could easily take more than 50ms, ! 508: ; and I don't feel like counting instructions for all possible paths, ! 509: ; we call the original real time interrupt right away, ! 510: ; then we interpret keyboard commands, read text, etc. ! 511: ih_rti proc far ; user real time interrupt handler ! 512: ! 513: pushf ; call the original interrupt routine ! 514: ; call far ptr cs:[orig_rti] ! 515: db 2eh, 0ffh, 01eh ! 516: dw orig_rti ! 517: ! 518: ; simulate Coherent lbolt ! 519: push ds ! 520: mov ds,myds ! 521: add word ptr _lbolt,5 ! 522: adc word ptr _lbolt+2,0 ! 523: pop ds ! 524: ! 525: ; check for nested real time interrupts ! 526: dec nestrti ! 527: jz go_rti ! 528: inc nestrti ! 529: iret ; previous routine still reading, or whatever ! 530: go_rti: ! 531: ! 532: ; switch stacks ! 533: mov save_ss,ss ! 534: mov save_sp,sp ! 535: ; New stack overlays initialization code, about 200 bytes. ! 536: push cs ! 537: pop ss ! 538: mov sp,offset _TEXT: drain ! 539: ! 540: cld ! 541: push ds ! 542: push ax ! 543: push bx ! 544: push cx ! 545: push dx ! 546: push es ! 547: ! 548: mov ds,myds ! 549: sti ! 550: ! 551: call _sdtime ! 552: ! 553: cli ! 554: pop es ! 555: pop dx ! 556: pop cx ! 557: pop bx ! 558: pop ax ! 559: pop ds ! 560: mov ss,save_ss ; restore old stack ! 561: mov sp,save_sp ! 562: inc nestrti ! 563: iret ! 564: ih_rti endp ! 565: ! 566: ! 567: ih_com proc far ! 568: cld ! 569: push ds ! 570: push ax ! 571: push bx ! 572: push cx ! 573: push dx ! 574: ! 575: mov ds,myds ! 576: cld ! 577: ! 578: mov bx,ivec_fn ! 579: call bx ! 580: ! 581: mov al,eoi ! 582: out 20h,al ! 583: pop dx ! 584: pop cx ! 585: pop bx ! 586: pop ax ! 587: pop ds ! 588: iret ! 589: ih_com endp ! 590: ! 591: ! 592: ih_kbi proc far ; keyboard interrupt handler ! 593: pushf ; call the original hardware interrupt routine ! 594: ; call far ptr cs:[orig_kbi] ! 595: db 2eh, 0ffh, 01eh ! 596: dw orig_kbi ! 597: ; if a key was entered into the MS-DOS buffer, ! 598: ; and the key represents a reading command, ! 599: ; remove it from the buffer, and place the corresponding ! 600: ; command into the talkcmd location ! 601: ; we assume no real time (or other) interrupt routine will ! 602: ; grab the key from the buffer before this routine can snag it. ! 603: cld ! 604: push ax ! 605: push bx ! 606: push cx ! 607: push dx ! 608: push ds ! 609: ; C routines preserve si, di, and bp ! 610: ! 611: ; call original bios routine to get the entered character ! 612: mov ah,1 ! 613: pushf ! 614: ; call far ptr cs:[orig_bkb] ! 615: db 2eh, 0ffh, 01eh ! 616: dw orig_bkb ! 617: jz nokey ; no new characters typed ! 618: ; now get the character ! 619: mov ah,0 ! 620: pushf ! 621: ; call far ptr cs:[orig_bkb] ! 622: db 2eh, 0ffh, 01eh ! 623: dw orig_bkb ! 624: ! 625: mov ds,myds ! 626: push ax ! 627: cmp word ptr _sdcontrol,0 ! 628: jz notactive ! 629: ; console is owner ! 630: mov ax,0 ! 631: push ax ! 632: call _sdinkey ; turn key into command code ! 633: add sp,4 ! 634: jmp short nokey ! 635: notactive: ! 636: call _isin ! 637: add sp,2 ! 638: ! 639: nokey: pop ds ! 640: pop dx ! 641: pop cx ! 642: pop bx ! 643: pop ax ! 644: iret ! 645: ih_kbi endp ! 646: ! 647: ; new BIOS keyboarde routine, to accommodate new type ahead buffer. ! 648: ; ! 649: ; the original bios routine enables interrupts, ! 650: ; and when ah = 1, leaves them that way, ! 651: ; regardless of the initial conditions. ! 652: ; don't know if this is a feature or a bug, but I emulate it. ! 653: ; ! 654: ; For some bizarre reason, dos4.0 calls this BIOS routine ! 655: ; with ah = 10h, 11h, or 12h, instead of 0, 1, or 2. ! 656: ; I have no idea what the extra bit means. ! 657: ; I simply clear it out, and things seem to work. ! 658: ; Thus this bios routine works under all versions of dos, ! 659: ; as of this writing. ! 660: ih_bkb proc far ! 661: sti ! 662: and ah,0efh ! 663: cmp ah,1 ! 664: jb getc ! 665: jz nbget ! 666: ; jmp far ptr cs:[orig_bkb] ! 667: db 2eh, 0ffh, 02eh ! 668: dw orig_bkb ! 669: ! 670: nbget: ; non blocking getchar ! 671: push ds ! 672: push bx ! 673: mov ds,myds ! 674: mov bx,kbtail ! 675: cmp bx,kbhead ! 676: mov ax,[bx] ! 677: pop bx ! 678: pop ds ! 679: ret 2 ! 680: ! 681: getc: ; blocking getchar ! 682: push ds ! 683: push bx ! 684: mov ds,myds ! 685: mov bx,kbtail ! 686: cmp bx,kbhead ! 687: mov ax,[bx] ! 688: jz getc ! 689: inc bx ! 690: inc bx ! 691: cmp bx,ADDR: kbbuf+KBSIZ*2 ! 692: jnz wrap6 ! 693: mov bx,ADDR: kbbuf ! 694: wrap6: mov kbtail,bx ! 695: pop bx ! 696: pop ds ! 697: iret ! 698: ih_bkb endp ! 699: ! 700: _TEXT ends ! 701: ! 702: ! 703: _BSS segment ! 704: bssbeg dw ? ! 705: _BSS ends ! 706: ! 707: ! 708: _DATA segment ! 709: ! 710: extrn _lbolt:word, _sdcontrol:word ! 711: ! 712: databeg label word ! 713: ! 714: ; we implement our own interrupt level keyboard buffer ! 715: ; the ms-dos buffer (15 characters) is frustratingly small ! 716: kbbuf dw KBSIZ dup(?) ! 717: kbhead dw kbbuf ! 718: kbtail dw kbbuf ! 719: ! 720: ivec_fn dw 0 ; setivec function ! 721: ! 722: endbound dw ADDR: boundary ! 723: inload db 1 ; are we in load phase? ! 724: ! 725: intofs dw 30h ; offset for interrupt vector ! 726: irm db 0efh ; interrupt register mask ! 727: eoi db 64h ; end of interrupt code ! 728: ! 729: _DATA ends ! 730: ! 731: ! 732: ; to find the end of DGROUP, and the start of word table and buffer ! 733: _EDATA segment ! 734: boundary label word ! 735: _EDATA ends ! 736: ! 737: end HDR
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.