page ,160 title mschar - character and clock devices ; ;---------------------------------------------------------------------------- ; ; ; 26-Feb-1991 sudeepb Ported for NT DOSEm ; ;---------------------------------------------------------------------------- ; .xlist include version.inc ; set build flags include biosseg.inc ; establish bios segment structure include msequ.inc include devsym.inc include ioctl.inc include vint.inc break macro endm include biosbop.inc include error.inc .list include msgroup.inc ; define Bios_Data segment extrn ptrsav:dword extrn altah:byte extrn keyrd_func:byte extrn keysts_func:byte extrn auxnum:word extrn auxbuf:byte extrn wait_count:word extrn printdev:byte extrn Old10:dword extrn spc_mse_int10:dword extrn int29Perf:dword ; close Bios_Data and open Bios_Code segment tocode extrn bc_cmderr:near extrn bc_err_cnt:near MODE_CTRLBRK equ 0ffh ; M013 ;************************************************************************ ;* * ;* device driver dispatch tables * ;* * ;* each table starts with a byte which lists the number of * ;* legal functions, followed by that number of words. Each * ;* word represents an offset of a routine in Bios_Code which * ;* handles the function. The functions are terminated with * ;* a near return. If carry is reset, a 'done' code is returned * ;* to the caller. If carry is set, the ah/al registers are * ;* returned as abnormal completion status. Notice that ds * ;* is assumed to point to the Bios_Data segment throughout. * ;* * ;************************************************************************ public con_table con_table: db (((offset con_table_end) - (offset con_table) - 1)/2) dw bc_exvec ; 00 init dw bc_exvec ; 01 dw bc_exvec ; 02 dw bc_cmderr ; 03 dw con_read ; 04 dw con_rdnd ; 05 dw bc_exvec ; 06 dw con_flush ; 07 dw con_writ ; 08 dw con_writ ; 09 dw bc_exvec ; 0a con_table_end: public prn_table prn_table label byte db (((offset prn_table_end) - (offset prn_table) -1)/2) dw bc_exvec ; 00 init dw bc_exvec ; 01 dw bc_exvec ; 02 dw bc_cmderr ; 03 dw prn_input ; 04 indicate zero chars read dw z_bus_exit ; 05 read non-destructive dw bc_exvec ; 06 dw bc_exvec ; 07 dw prn_writ ; 08 dw prn_writ ; 09 dw prn_stat ; 0a dw bc_exvec ; 0b dw bc_exvec ; 0c dw prn_open ; 0d dw prn_close ; 0e dw bc_exvec ; 0f dw prn_tilbusy ; 10 dw bc_exvec ; 11 dw bc_exvec ; 12 dw prn_genioctl ; 13 dw bc_exvec ; 14 dw bc_exvec ; 15 dw bc_exvec ; 16 dw bc_exvec ; 17 dw bc_exvec ; 18 dw prn_ioctl_query ; 19 prn_table_end: public aux_table aux_table label byte db (((offset aux_table_end) - (offset aux_table) -1)/2) dw bc_exvec ; 00 - init dw bc_exvec ; 01 dw bc_exvec ; 02 dw bc_cmderr ; 03 dw aux_read ; 04 - read dw aux_rdnd ; 05 - read non-destructive dw bc_exvec ; 06 dw aux_flsh ; 07 dw aux_writ ; 08 dw aux_writ ; 09 dw aux_wrst ; 0a aux_table_end: public tim_table tim_table label byte db (((offset tim_table_end) - (offset tim_table) -1)/2) dw bc_exvec ; 00 dw bc_exvec ; 01 dw bc_exvec ; 02 dw bc_cmderr ; 03 dw bc_cmderr ; 04 dw z_bus_exit ; 05 dw bc_exvec ; 06 dw bc_exvec ; 07 dw bc_cmderr ; 08 dw bc_cmderr ; 09 tim_table_end: ;************************************************************************ ;* * ;* con_read - read cx bytes from keyboard into buffer at es:di * ;* * ;************************************************************************ con_read proc near assume ds:Bios_Data,es:nothing jcxz con_exit con_loop: call chrin ;get char in al stosb ;store char at es:di loop con_loop con_exit: clc ret con_read endp ;************************************************************************ ;* * ;* chrin - input single char from keyboard into al * ;* * ;* we are going to issue extended keyboard function, if * ;* supported. the returning value of the extended keystroke * ;* of the extended keyboard function uses 0e0h in al * ;* instead of 00 as in the conventional keyboard function. * ;* this creates a conflict when the user entered real * ;* greek alpha charater (= 0e0h) to distinguish the extended * ;* keystroke and the greek alpha. this case will be handled * ;* in the following manner: * ;* * ;* ah = 16h * ;* int 16h * ;* if al == 0, then extended code (in ah) * ;* else if al == 0e0h, then * ;* if ah <> 0, then extended code (in ah) * ;* else greek_alpha character. * ;* * ;* also, for compatibility reason, if an extended code is * ;* detected, then we are going to change the value in al * ;* from 0e0h to 00h. * ;* * ;************************************************************************ chrin proc near assume ds:Bios_Data,es:nothing mov ah,keyrd_func ; set by msinit. 0 or 10h xor al,al xchg al,altah ;get character & zero altah or al,al jnz keyret int 16h ; do rom bios keyrd function alt10: or ax,ax ;check for non-key after break jz chrin cmp ax,7200h ;check for ctrl-prtsc jnz alt_ext_chk mov al,16 jmp short keyret alt_ext_chk: ;************************************************************** ; if operation was extended function (i.e. keyrd_func != 0) then ; if character read was 0e0h then ; if extended byte was zero (i.e. ah == 0) then ; goto keyret ; else ; set al to zero ; goto alt_save ; endif ; endif ; endif cmp byte ptr keyrd_func,0 jz not_ext cmp al,0e0h jnz not_ext or ah,ah jz keyret ifdef DBCS ifdef KOREA ; Keyl 1990/11/5 cmp ah, 0f0h ; If hangeul code range then jb EngCodeRange1 ; do not modify any value. cmp ah, 0f2h jbe not_ext EngCodeRange1: endif ; KOREA endif ; DBCS xor al,al jmp short alt_save not_ext: or al,al ;special case? jnz keyret alt_save: mov altah,ah ;store special key keyret: ret chrin endp ;************************************************************************ ;* * ;* con_rdnd - keyboard non destructive read, no wait * ;* * ;* pc-convertible-type machine: if bit 10 is set by the dos * ;* in the status word of the request packet, and there is no * ;* character in the input buffer, the driver issues a system * ;* wait request to the rom. on return from the rom, it returns * ;* a 'char-not-found' to the dos. * ;* * ;************************************************************************ con_rdnd proc near assume ds:Bios_Data,es:nothing mov al,[altah] or al,al jnz rdexit mov ah,keysts_func ; keyboard i/o interrupt - get int 16h ; keystroke status (keysts_func) jnz gotchr ; ; pc-convertible checking is not needed on NTVDM ; if no key in buff return immediatly with busy status ;04-Aug-1992 Jonle ; ; cmp fhavek09,0 ; jz z_bus_exit ; return with busy status if not k09 ; ; les bx,[ptrsav] ; assume es:nothing ; test es:[bx].status,0400h ; system wait enabled? ; jz z_bus_exit ; return with busy status if not ; ; need to wait for ibm response to request for code ; on how to use the system wait call. ; ; mov ax,4100h ; wait on an external event ; xor bl,bl ; M055; wait for any event ; int 15h ; call rom for system wait z_bus_exit: stc mov ah,3 ; indicate busy status ret gotchr: or ax,ax jnz notbrk ;check for null after break mov ah,keyrd_func ; issue keyboard read function int 16h jmp con_rdnd ;and get a real status notbrk: cmp ax,7200h ;check for ctrl-prtsc jnz rd_ext_chk mov al,'P' and 1fh ; return control p jmp short rdexit rd_ext_chk: cmp keyrd_func,0 ; extended keyboard function? jz rdexit ; no. normal exit. cmp al,0e0h ; extended key value or greek alpha? jne rdexit ifdef DBCS ifdef KOREA cmp ah, 0f0h ; If hangeul code range then jb EngCodeRange ; do not modify any value. cmp ah, 0f2h jbe rdexit ; Keyl 90/11/5 EngCodeRange: endif ; KOREA endif ; DBCS cmp ah,0 ; scan code exist? jz rdexit ; yes. greek alpha char. mov al,0 ; no. extended key stroke. ; change it for compatibility rdexit: les bx,[ptrsav] assume es:nothing mov es:[bx].media,al ; *** return keyboard character here bc_exvec: clc ; indicate normal termination ret con_rdnd endp ;************************************************************************ ;* * ;* con_write - console write routine * ;* * ;* entry: es:di -> buffer * ;* cx = count * ;* * ;************************************************************************ con_writ proc near assume ds:Bios_Data,es:nothing jcxz bc_exvec push es mov bx,word ptr [int29Perf] mov dx,word ptr [int29Perf+2] ;DX:BX is original INT 29h vector sub ax,ax mov es,ax cmp BX,es:[29h*4+0] jne con_lp1 ; if not the same do single int10s cmp DX,es:[29h*4+2] jne con_lp1 ; if not the same do single int10s mov bx,word ptr [spc_mse_int10] mov dx,word ptr [spc_mse_int10+2] ;DX:BX is original INT 10h vector cmp BX,es:[10h*4+0] jne con_lp1 ; if not the same do single int10s cmp DX,es:[10h*4+2] jne con_lp1 ; if not the same do single int10s pop es ; Sudeepb 21-Jul-1992: We know that no one has hooked in10 so we ; can optimize it by calling a private in1t10h which takes a full ; string, displays it with the same attribute as present on the ; screen and moves the cursor to the end. mov ax,46h ; sounds like a good flag value push ax ; make an iret frame push cs mov ax, offset ret_adr push ax push dx ; dx:bx is pointing to softpc int10 handler push bx ; make the retf frame mov ax,13FFh ; AH = WRITESTRING, AL = subfunction retf ret_adr: jmp short cc_ret con_lp1: pop es con_lp: mov al,es:[di] ;get char inc di int chrout ;output char loop con_lp ;repeat until all through cc_ret: clc ret con_writ endp ;************************************************************************ ;* * ;* con_flush - flush out keyboard queue * ;* * ;************************************************************************ public con_flush ; called from msbio2.asm for floppy swapping con_flush proc near assume ds:Bios_Data,es:nothing mov [altah],0 ;clear out holding buffer ; while (charavail()) charread(); flloop: mov ah,1 ; command code for check status int 16h ; call rom-bios keyboard routine jz cc_ret ; return carry clear if none xor ah,ah ; if zf is nof set, get character int 16h ; call rom-bios to get character jmp flloop con_flush endp ;************************************************************************ ;* * ;* some equates for rom bios printer i/o * ;* * ;************************************************************************ ; ibm rom status bits (i don't trust them, neither should you) ; warning!!! the ibm rom does not return just one bit. it returns a ; whole slew of bits, only one of which is correct. notbusystatus = 10000000b ; not busy nopaperstatus = 00100000b ; no more paper prnselected = 00010000b ; printer selected ioerrstatus = 00001000b ; some kinda error timeoutstatus = 00000001b ; time out. noprinter = 00110000b ; no printer attached ;************************************************************************ ;* * ;* prn_input - return with no error but zero chars read * ;* * ;* enter with cx = number of characters requested * ;* * ;************************************************************************ prn_input proc near assume ds:Bios_Data,es:nothing call bc_err_cnt ; reset count to zero (sub reqpkt.count,cx) clc ; but return with carry reset for no error ret prn_input endp ;************************************************************************ ;* * ;* prn_writ - write cx bytes from es:di to printer device * ;* * ;* auxnum has printer number * ;* * ;************************************************************************ prn_writ proc near assume ds:Bios_Data,es:nothing jcxz prn_done ;no chars to output prn_loop: mov bx,2 ;retry count prn_out: call prnstat ; get status jnz TestPrnError ; error mov al,es:[di] ; get character to print xor ah,ah call prnop ; print to printer jz prn_con ; no error - continue cmp ah, MODE_CTRLBRK ; M013 jne @f ; M013 mov al, error_I24_gen_failure ; M013 mov altah, 0 ; M013 jmp short pmessg ; M013 @@: test ah,timeoutstatus jz prn_con ; not time out - continue TestPrnError: dec bx ;retry until count is exhausted. jnz prn_out pmessg: jmp bc_err_cnt ; return with error ; next character prn_con: inc di ;point to next char and continue loop prn_loop prn_done: clc ret prn_writ endp ;************************************************************************ ;* * ;* prn_stat - device driver entry to return printer status * ;* * ;************************************************************************ prn_stat proc near call prnstat ;device in dx jnz pmessg ; other errors were found test ah,notbusystatus jnz prn_done ;no error. exit jmp z_bus_exit ; return busy status prn_stat endp ;************************************************************************ ;* * ;* prnstat - utilty function to call ROM BIOS to check * ;* printer status. Return meaningful error code * ;* * ;************************************************************************ prnstat proc near assume ds:Bios_Data,es:nothing mov ah, 2 ; set command for get status prnstat endp ; fall into prnop ;************************************************************************ ;* * ;* prnop - call ROM BIOS printer function in ah * ;* return zero true if no error * ;* return zero false if error, al = error code * ;* * ;************************************************************************ prnop proc near assume ds:Bios_Data,es:nothing mov dx,[auxnum] ; get printer number int 17h ; call rom-bios printer routine ; This check was added to see if this is a case of no ; printer being installed. This tests checks to be sure ; the error is noprinter (30h) push ax ; M044 and ah, noprinter ; M044 cmp AH,noprinter ; Chk for no printer pop ax ; M044 jne NextTest and AH,NOT nopaperstatus or AH,ioerrstatus ; examine the status bits to see if an error occurred. unfortunately, several ; of the bits are set so we have to pick and choose. we must be extremely ; careful about breaking basic. NextTest: test ah,(ioerrstatus+nopaperstatus) ; i/o error? jz checknotready ; no, try not ready ; at this point, we know we have an error. the converse is not true. mov al,error_I24_out_of_paper ; first, assume out of paper test ah,nopaperstatus ; out of paper set? jnz ret1 ; yes, error is set inc al ; return al=10 (i/o error) ret1: ret ; return with error checknotready: mov al,2 ; assume not-ready test ah,timeoutstatus ; is time-out set? ret ; if nz then error, else ok prnop endp ;************************************************************************ ;* * ;* prn_open - send bop to disable auto-close, and wait for * ;* a DOS close * ;* * ;* inputs: * ;* outputs: BOP has been issued * ;* * ;************************************************************************ prn_open proc near push si push dx push ds mov dx,40h mov ds,dx test word ptr ds:[FIXED_NTVDMSTATE_REL40], EXEC_BIT_MASK pop ds jnz po_nobop xor dh, dh mov dl, [printdev] or dl, dl jz @f dec dl @@: mov si,PRNIO_OPEN bop %BIOS_PRINTER_IO po_nobop: pop dx pop si ret prn_open endp ;************************************************************************ ;* * ;* prn_close - send bop to close actual printer, and re-enable * ;* autoclose * ;* * ;* inputs: * ;* outputs: BOP has been issued * ;* * ;************************************************************************ prn_close proc near push si push dx push ds mov dx,40h mov ds,dx test word ptr ds:[FIXED_NTVDMSTATE_REL40], EXEC_BIT_MASK pop ds jnz pc_nobop xor dh, dh mov dl, [printdev] or dl, dl jz @f dec dl @@: mov si,PRNIO_CLOSE bop %BIOS_PRINTER_IO pc_nobop: pop dx pop si ret prn_close endp ;************************************************************************ ;* * ;* prn_tilbusy - output until busy. Used by print spooler. * ;* this entry point should never block waiting for * ;* device to come ready. * ;* * ;* inputs: cx = count, es:di -> buffer * ;* outputs: set the number of bytes transferred in the * ;* device driver request packet * ;* * ;************************************************************************ prn_tilbusy proc near mov si,di ; everything is set for lodsb prn_tilbloop: push cx push bx xor bh,bh mov bl,[printdev] shl bx,1 mov cx,wait_count[bx] ; wait count times to come ready pop bx prn_getstat: call prnstat ; get status jnz prn_bperr ; error test ah,10000000b ; ready yet? loopz prn_getstat ; no, go for more pop cx ; get original count jz prn_berr ; still not ready => done lods es:byte ptr [si] xor ah,ah call prnop jnz prn_berr ; error loop prn_tilbloop ; go for more clc ; normal no-error return ret ; from device driver prn_bperr: pop cx ; restore transfer count from stack prn_berr: jmp bc_err_cnt prn_tilbusy endp ;************************************************************************ ;* * ;* prn_genioctl - get/set printer retry count * ;* * ;************************************************************************ prn_genioctl proc near assume ds:Bios_Data,es:nothing les di,[ptrsav] cmp es:[di].majorfunction,ioc_pc jz prnfunc_ok prnfuncerr: jmp bc_cmderr prnfunc_ok: mov al,es:[di].minorfunction les di,es:[di].genericioctl_packet xor bh,bh mov bl,[printdev] ; get index into retry counts shl bx,1 mov cx,wait_count[bx] ; pull out retry count for device cmp al,get_retry_count jz prngetcount cmp al,set_retry_count jnz prnfuncerr mov cx,es:[di].rc_count prngetcount: mov wait_count[bx],cx ; place "new" retry count mov es:[di].rc_count,cx ; return current retry count clc ret prn_genioctl endp ;************************************************************************ ;* * ;* prn_ioctl_query * ;* * ;* Added for 5.00 * ;************************************************************************ prn_ioctl_query PROC NEAR assume ds:Bios_Data,es:nothing les di,[ptrsav] cmp es:[di].majorfunction,ioc_pc jne prn_query_err mov al,es:[di].minorfunction cmp al,get_retry_count je IOCtlSupported cmp al,set_retry_count jne prn_query_err IOCtlSupported: clc ret prn_query_err: stc jmp BC_CmdErr prn_ioctl_query ENDP ;************************************************************************ ;* * ;* aux port driver code -- "aux" == "com1" * ;* * ;* the device driver entry/dispatch code sets up auxnum to * ;* give the com port number to use (0=com1, 1=com2, 2=com3...) * ;* * ;************************************************************************ ; values in ah, requesting function of int 14h in rom bios auxfunc_send equ 1 ;transmit auxfunc_receive equ 2 ;read auxfunc_status equ 3 ;request status ; error flags, reported by int 14h, reported in ah: flag_data_ready equ 01h ;data ready flag_overrun equ 02h ;overrun error flag_parity equ 04h ;parity error flag_frame equ 08h ;framing error flag_break equ 10h ;break detect flag_tranhol_emp equ 20h ;transmit holding register empty flag_timeout equ 80h ;timeout ; these flags reported in al: flag_cts equ 10h ;clear to send flag_dsr equ 20h ;data set ready flag_rec_sig equ 80h ;receive line signal detect ;************************************************************************ ;* * ;* aux_read - read cx bytes from [auxnum] aux port to buffer * ;* at es:di * ;* * ;************************************************************************ aux_read proc near assume ds:Bios_Data,es:nothing jcxz exvec2 ; if no characters, get out call getbx ; put address of auxbuf in bx xor al,al ; clear al register xchg al,[bx] ; get character , if any, from ; buffer and clear buffer or al,al ; if al is nonzero there was a ; character in the buffer jnz aux2 ; if so skip first auxin call aux1: call auxin ; get character from port ; ^^^^^ won't return if error aux2: stosb ; store character loop aux1 ; if more characters, go around again exvec2: clc ; all done, successful exit ret aux_read endp ;************************************************************************ ;* * ;* auxin - call rom bios to read character from aux port * ;* if error occurs, map the error and return one * ;* level up to device driver exit code, setting * ;* the number of bytes transferred appropriately * ;* * ;************************************************************************ ; ; M026 - BEGIN ; auxin proc near mov ah,auxfunc_receive call auxop ;check for frame, parity, or overrun errors ;warning: these error bits are unpredictable ; if timeout (bit 7) is set test ah, flag_frame or flag_parity or flag_overrun jnz arbad ; skip if any error bits set ret ; normal completion, ah=stat, al=char ; error getting character arbad: pop ax ; remove return address (near call) xor al,al or al,flag_rec_sig or flag_dsr or flag_cts jmp bc_err_cnt auxin endp IFDEF COMMENTEDOUT auxin proc near push cx mov cx, 20 ; number of retries on time out errors @@: mov ah,auxfunc_receive call auxop ;check for frame, parity, or overrun errors ;warning: these error bits are unpredictable ; if timeout (bit 7) is set test ah, flag_timeout jz no_timeout loop @b no_timeout: pop cx test ah, flag_timeout or flag_frame or flag_parity or flag_overrun jnz arbad ; skip if any error bits set ret ; normal completion, ah=stat, al=char ; error getting character arbad: pop ax ; remove return address (near call) xor al,al or al,flag_rec_sig or flag_dsr or flag_cts jmp bc_err_cnt auxin endp ENDIF ; ; M026 - END ; ;************************************************************************ ;* * ;* aux_rdnd - non-destructive aux port read * ;* * ;************************************************************************ aux_rdnd proc near assume ds:Bios_Data,es:nothing call getbx ; have bx point to auxbuf mov al,[bx] ; copy contents of buffer to al or al,al ; if al is non-zero (char in buffer) jnz auxrdx ; then return character call auxstat ; if not, get status of aux device test ah,flag_data_ready ; test data ready jz auxbus ; then device is busy (not ready) test al,flag_dsr ;test data set ready jz auxbus ; then device is busy (not ready) call auxin ; else aux is ready, get character mov [bx],al ; save character in buffer auxrdx: jmp rdexit ; return al in [packet.media] auxbus: jmp z_bus_exit ; return busy status aux_rdnd endp ;************************************************************************ ;* * ;* aux_wrst - return aux port write status * ;* * ;************************************************************************ aux_wrst proc near assume ds:Bios_Data,es:nothing call auxstat ; get status of aux in ax test al,flag_dsr ; test data set ready jz auxbus ; then device is busy (not ready) test ah,flag_tranhol_emp ;test transmit hold reg empty jz auxbus ; then device is busy (not ready) clc ret aux_wrst endp ;************************************************************************ ;* * ;* auxstat - call rom bios to determine aux port status * ;* * ;* exit: ax = status * ;* dx = [auxnum] * ;* * ;************************************************************************ auxstat proc near mov ah,auxfunc_status auxstat endp ; fall into auxop ;************************************************************************ ;* * ;* auxop - perform rom-biox aux port interrupt * ;* * ;* entry: ah = int 14h function number * ;* exit: ax = results * ;* dx = [auxnum] * ;* * ;************************************************************************ auxop proc near ;ah=function code ;0=init, 1=send, 2=receive, 3=status mov dx,[auxnum] ; get port number int 14h ; call rom-bios for status ret auxop endp ;************************************************************************ ;* * ;* aux_flsh - flush aux input buffer - set contents of * ;* auxbuf [auxnum] to zero * ;* * ;* cas - shouldn't this code call the rom bios input function * ;* repeatedly until it isn't ready? to flush out any * ;* pending serial input queue if there's a tsr like MODE * ;* which is providing interrupt-buffering of aux port? * ;* * ;************************************************************************ aux_flsh proc near call getbx ; get bx to point to auxbuf mov byte ptr [bx],0 ; zero out buffer clc ; all done, successful return ret aux_flsh endp ;************************************************************************ ;* * ;* aux_writ - write to aux device * ;* * ;************************************************************************ aux_writ proc near assume ds:Bios_Data ; set by aux device driver entry routine jcxz exvec2 ; if cx is zero, no characters ; to be written, jump to exit aux_loop: mov al,es:[di] ; get character to be written inc di ; move di pointer to next character mov ah,auxfunc_send ;value=1, indicates a write call auxop ;send character over aux port test ah,flag_timeout ;check for error jz awok ; then no error mov al,10 ; else indicate write fault jmp bc_err_cnt ; call error routines ; if cx is non-zero, still more awok: loop aux_loop ; more characrter to print clc ; all done, successful return ret aux_writ endp ;************************************************************************ ;* * ;* getbx - return bx -> single byte input buffer for * ;* selected aux port ([auxnum]) * ;* * ;************************************************************************ getbx proc near assume ds:Bios_Data,es:nothing mov bx,[auxnum] add bx,offset auxbuf ret getbx endp Bios_Code ends end