|
|
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.