page,132 ;---------------------------Module-Header-------------------------------; ; Module Name: IBMLPT.ASM ; ; Copyright (c) Microsoft Corporation 1985-1990. All Rights Reserved. ; ; General Description: ; ; History: ; ;-----------------------------------------------------------------------; title IBMLpt - IBM PC, PC-XT, PC-AT, PS/2 Parallel Communications Interface .xlist include cmacros.inc include comdev.inc include ins8250.inc include ibmcom.inc .list sBegin Code assumes cs,Code assumes ds,Data externFP GetSystemMsecCount externA __0040H ;----------------------------Private-Routine----------------------------; ; ; DoLPT - Do Function To LPT port ; ; The given function (output or reset) is performed to the ; passed LPT port. ; ; Before a character is sent, a check is made to see if the device ; will be able to accept the character. If it can, then the character ; will be sent. If not, then an error will be returned. If the ; printer is selected and busy and no error, then the code returned ; will be CE_TXFULL and the handshake bits will be set in HSFlag ; to simulate that a handshake was received. ; ; If the BIOS ROM code is examined, you will note that they wait for ; the busy character from the last charcater to be cleared before ; they strobe in the current character. This can take a long time ; on the standard EPSON class printer (1 mSec to greater than ; 300 mSec if the last character actually caused printing). ; ; Because of this, several status read retrys will be made before ; declaring that the device is actually busy. If only one status ; read is performed, the spooler will yeild, take a while to get ; back here, and things will be really slow. What difference does ; it really make if we or the BIOS does the delay, at least we can ; break out of it at some point when it seems hopeless. ; ; The OKIHACK: Okidata reports a 50 ns. 2.2 volt pulse on the paper ; out signal on the trailing edge of the Busy signal. If we see this ; glitch then we report paper out. So we try to get the status twice... ; if it changes between the two tries we keep getting the status. ; ; ; Entry: ; AH = cid ; AL = character to output ; CH = Function request. 0 = Output, 1 = Initialize, 2 = Status ; DS:SI -> DEB for the port ; Returns: ; AX = 0 if no errors occured ; Error Returns: ; AX = error code ; Registers Preserved: ; SI,DI ; Registers Destroyed: ; AX,BX,CX,DX,ES,FLAGS ; History: ;-----------------------------------------------------------------------; assumes ds,Data assumes es,nothing public DoLPT DoLPT proc near mov dx,Port[si] ;Get port address ; DX = port address ; CH = operation: 0 = write, 1 = init, 2 = status ; AL = character or ch, ch jz LPT_OutChar cmp ch, 1 jz LPT_Reset jmp LPT_GetStatus ret LPT_Reset: inc dx inc dx mov al, L_RESET iodelay out dx, al push dx cCall GetSystemMsecCount mov bx, ax LPT_ResetDelay: push bx cCall GetSystemMsecCount pop bx sub ax, bx cmp ax, 300 ; 1/3 sec as good as any jbe LPT_ResetDelay pop dx mov al, L_NORMAL iodelay iodelay out dx, al dec dx dec dx jmp LPT_GetStatus LPT_OutChar: push ax ; save character to be written ; first check to see if printer is ready for us push di push dx call GetSystemMSecCount mov di, ax pop dx LPT_WaitReady: inc dx ; point to status port iodelay in al, dx ; get status bits and al, L_BITS ; mask unused ones xor al, L_BITS_INVERT ; flip a couple xchg al, ah ifndef NOOKIHACK iodelay in al, dx dec dx and al, L_BITS xor al, L_BITS_INVERT cmp al, ah ; did any bits change? jnz LPT_WaitReady else dec dx endif test ah, PS_PaperOut or PS_IOError jnz LPT_PrinterNotReady test ah, PS_Select jz LPT_PrinterNotReady test ah, PS_NotBusy jnz LPT_PrinterReady push ax push dx call GetSystemMSecCount pop dx pop bx sub ax, di cmp ax, 300 ; 1/3 sec timeout jbe LPT_WaitReady ; The device seems to be selected and powered up, but is just ; busy (some printers seem to show selected but busy when they ; are taken offline). Show that the transmit queue is full and ; that the hold handshakes are set. This is so the windows ; spooler will retry (and do yields so that other apps may run). or ComErr[si],CE_TXFULL ;Show queue full mov ah,bh or ah, L_TIMEOUT LPT_PrinterNotReady: pop di pop cx ; throw away character jmp short LPT_ReturnStatus LPT_PrinterReady: pop di ; get di back pop ax ; get character back iodelay out dx, al ; write character to port inc dx ; access status port LPT_Strobe: inc dx ; control port mov al, L_STROBE ; set strobe high iodelay iodelay iodelay iodelay out dx, al ; ... mov al, L_NORMAL ; iodelay iodelay iodelay iodelay out dx, al ; set strobe low sub dx, 2 ; point back to port base ; FALL THRU LPT_GetStatus: inc dx ; point to status port LPT_GS1: iodelay iodelay in al, dx ; get status bits and al, L_BITS ; mask unused ones xor al, L_BITS_INVERT ; flip a couple mov ah, al ifndef NOOKIHACK in al, dx and al, L_BITS xor al, L_BITS_INVERT cmp al, ah jnz LPT_GS1 ; if they changed try again... endif LPT_ReturnStatus: assumes ds,Data and ax,(PS_PaperOut+PS_Select+PS_IOError+PS_Timeout)*256 shr ah,1 adc ah,al ;Get back Timeout bit xor ah,HIGH CE_DNS ;Invert selected bit .errnz LOW CE_DNS or by ComErr+1[si],ah ;Save comm error ret .errnz CE_PTO-0200h .errnz CE_IOE-0400h .errnz CE_DNS-0800h .errnz CE_OOP-1000h DoLPT40: assumes ds,Data or ComErr[si],CE_TXFULL ;Show queue full ret DoLPT endp page CheckStatus proc near in al, dx ; get status bits mov ah, al and al, L_BITS ; mask unused ones xor al, L_BITS_INVERT ; flip a couple xchg al, ah ifndef NOOKIHACK iodelay in al, dx and al, L_BITS xor al, L_BITS_INVERT cmp al, ah ; did any bits change? jnz CheckStatus endif test ah, PS_PaperOut or PS_IOError jz @F stc ret @@: test ah, PS_Select jnz @F stc ret @@: and ah, PS_NotBusy clc ret CheckStatus endp ;----------------------------Public Routine-----------------------------; ; ; StringToLPT - Send string To LPT Port ; ; Entry: ; DS:SI -> DEB ; ES:DI -> string to send ; CX = # of bytes to send ; Returns: ; AX = # of bytes actually sent ; Registers Destroyed: ; AX,BX,CX,DX,ES,FLAGS ; History: ;-----------------------------------------------------------------------; PUBLIC StringToLPT StringToLPT proc near mov dx, Port[si] ; get port address inc dx ; access status port push cx ; save count for later push ds mov bx, __0040H mov ds, bx cld call CheckStatus ; quick status check before slowness jc PrinterError jz PrinterBusy ; if printer not ready for first char ; then just return with CE_TXFULL CharacterToLPT: ;; mov bh, 10 ; will wait 10 clock tics (~ 1/2 sec) mov bh, 3 ; will wait 3 clock tics (~ 1/6 sec) l1: mov bl, ds:[006Ch] ; low byte of tic counter l2: call CheckStatus ; quick status check before slowness jc PrinterError jnz LPT_PrinterRdy cmp bl, ds:[006Ch] jz l2 ; tic count hasn't changed dec bh jz PrinterBusy ; out of tics, timeout jmp short l1 LPT_PrinterRdy: mov al, es:[di] inc di dec dx ; point to data port out dx, al ; write character to port add dx, 2 ; access control port mov al, L_STROBE ; set strobe high out dx, al ; ... mov al, L_NORMAL ; iodelay iodelay out dx, al ; set strobe low dec dx ; point to status port for check loop CharacterToLPT pop ds jmp short LPT_Exit PrinterError: pop ds jmp short ReturnStatus PrinterBusy: pop ds or ComErr[si],CE_TXFULL ; set buffer full bit or al, L_TIMEOUT ; show timeout bit ReturnStatus: and ax,(PS_PaperOut+PS_Select+PS_IOError+PS_Timeout) xchg al, ah shr ah,1 adc ah,al ;Get back Timeout bit xor ah,HIGH CE_DNS ;Invert selected bit .errnz LOW CE_DNS or by ComErr+1[si],ah ;Save comm error LPT_Exit: pop ax ; get total count sub ax, cx ; subtract remaining unsent charts ret StringToLPT endp IFDEF DEBUG ;Publics for debugging public LPT_Reset public LPT_Outchar public LPT_Strobe public LPT_GetStatus public DoLPT40 ENDIF sEnd code End