; ; Note: This program controls an LCD module through the parellel port ; ; DB 25 male AND 771 LCD Display module ; 1----------------------------6 ; 2----------------------------7 ; 3----------------------------8 ; 4----------------------------9 ; 5----------------------------10 ; 6----------------------------11 ; 7----------------------------12 ; 8----------------------------13 5.1 v ; 9----------------------------14 Zener Diode ; 16----------------------------4 1N4733 ; 25----------------------------1-----5---+--->|-----2-----------------------+ ; | | | | | ; +--\/\/\---3---------| | | | | |---+ ; 470 ohm | | | ; Resistor (-) (+) ; 1/4 watt 9 VOLT ; Battery ; NOTE: Pin 5 of the LCD Module the R/W is GROUNDED. This is very important ; If the LCD Module were ever allowed to assert data against the non ; tristateable IBM parallel port the LCD Module would surely loose the fight. ; ; The AND 771 LCD Display module is very similar to the above. The ; difference being pin 3 is positive bias not negative. ; ; Copyright (C) 1990 Jim Phillips ; ; To assemble, Link, and convert this program to a COM file: ; ; C>MASM LCD; ; C>LINK LCD; ; C>EXE2BIN LCD.EXE LCD.COM ; C>DEL LCD.EXE ; CSEG SEGMENT PARA PUBLIC 'CODE' ; ORG 0100H ; ; ASSUME CS:CSEG, DS:CSEG, SS:CSEG, ES:CSEG ; INIT PROC NEAR ; JMP INITST ; Jump arround imbedded parameters ; PPORT DW 003BCH ; Address of parrellel port LnsPerCol DW 25 ; DB ' Setting' DB 'params in DEBUG ' DB 'Byte 100-102H is' DB 'JMP inst. 103 is' DB 'low byte of port' DB 'address. 104 is' DB 'high byt of port' DB 'address. 105 is' DB 'max column numbr' DB 'I.E. 19H for 24 ' DB 'column wide LCD ' DB 'Good luck... ' ; PPRTC DB ? ; ; ; These are set by program at run time CpuSpeed DW ? ; Speed of CPU measured at run time Settel_time DW ? ; Execut_time DW ? ; Column DW ? ; SIGNON DB 'Instaling LCD driver...',0DH,0AH DB 'Copyright (C) 1990 Jim Phillips',0DH,0AH,0DH,0AH DB '$' SUCCES DB 'Signaling LCD device...',0DH,0AH DB '$' ; INITST: PUSH DS ; Set up for simple .COM file terminate XOR AX,AX ; PUSH AX ; ; MOV DX,OFFSET TITLEpg ;Print Title Page MOV AH,09H ; INT 021H ; ; CALL MeasureCPU ; Get CPU speed MOV CpuSpeed,AX ; Save Speed SHR AX,1 ; Divide by two MOV Execut_time,AX ; Set delay count for LCD execution ; time to 27 milliseconds (13.5 reqd) ; SHR AX,1 ; Divide by 16 SHR AX,1 ; SHR AX,1 ; SHR AX,1 ; MOV Settel_time,AX ; Set delay count for LCD setteling ; time to 1736 microseconds (120 reqd) ; MOV DX,OFFSET SIGNON ;Print identification message MOV AH,09H ; INT 021H ; ; MOV AH,035H ; Get Inturrupt BDOS function number MOV AL,017H ; Interrupt number for BIOS printer INT 021H ; Execute function MOV WORD PTR PrevISRV,BX ; Store target address MOV WORD PTR PrevISRV+2,ES ; in double word MOV AL,017H ; Revector Interrupt 17 hex, printer MOV DX,OFFSET ISRV ; vector to address of new tsr MOV AH,025H ; handler by using function 25H INT 021H ; Call DOS and do it ; CALL LCD_Init ; ; MOV DX,OFFSET SUCCES ; Print Success message MOV AH,09H ; INT 021H ; MOV DX,((OFFSET PGM_LEN+15)/16)+10H ; Paragraphs of memory to ; reserve MOV AX,3100H ; Exit and stay resident command INT 021H ; w/return code 0 INIT ENDP ; ; ; Printer data port 3BC - Read/Write ;Bit 0 - 7 (pin 2 - 9) - 0 = logic low at printer data input, 1 = logic high ; ; Printer status port 3BD - Read only ; R Read polarity. I = Inverted N = Normal ; : ;Bit 0 - 2 Not used ;Bit 3 N Error (pin 15) - 0 = printer error, 1 = normal opperation ;Bit 4 N Select (pin 13) 0 = printer not attentive, 1 = printer listening ;Bit 5 N Paper (pin 12) - 0 = normal operation, 1 = out of paper ;Bit 6 N Ack (pin 10) - - 0 = accepting data, 1 = ready for more ;Bit 7 I Busy (pin 11) - 0 = busy, not selected, or in error, 1 = normal ; ; Printer control port 3BE - Read/Write ; W/R Open collector Write/Read polarity. I = Inverted N = Normal ; :/: ;Bit 0 I/I Strobe (pin 1) - 0 = strobe printer to accept data, 1 = release strb ;Bit 1 I/I Auto LF (pin 14) 0 = auto line feed, 1 = remote line feed ;Bit 2 N/N Init (pin 16) - 0 = initalize printer, 1 = release printer ;Bit 3 I/I Select (pin 17) 0 = deselect printer, 1 = select printer ;Bit 4 : INT (IRQ 7) - - 0 = mask off IRQ7, 1 = enable IRQ7 ;Bit 5 - 7 Not used ; ; Base addresses for other parallel ports ; LPT1: = 378, LPT2: = 278, LPT3: = 3BC ; ; Note: Most parallel ports follow this standard but some weirdos endowed by ; a lesser, or, uncaring god do exist. To drive a centronics printer they ; require a minimum of ; (1) Printer data port but read back may not exist. ; (2) Printer status port but Error (pin 15) may not exist. ; (3) Printer control port but signals may not be open collector, signals ; may not have read back, Auto LF (pin 14) and Select (pin 17) may ; not exist. IRQ7 enable/disable may work but with out read back, not ; do you not have any clue of it's existance but it's current status ; is invisible, you therefore haven't a prayer of restoring it! ; PrevISRV DD ? ; ; Printer Interrupt Service Routine ISRV PROC FAR ; PUSH DX ; Save the world PUSH CX ; PUSH BX ; PUSH DS ; PUSH CS ; POP DS ; CMP DX,00000H ; Check for LPT1 JNZ Not_4_us ; and pass control on to BIOS if not CMP AH,000H ; Check for Print char JNZ Not_Send_ch ; CALL LCD_Send ; If so it must be normal ASCII MOV AH,0D0H ; Be Happy (never error) JMP Done_W_Call ; and return Not_Send_ch: CMP AH,001H ; Check for init Printer JNZ Not_Init_Pr ; CALL LCD_Init ; If so Init the LCD module MOV AH,0D0H ; Be Happy (never error) JMP Done_W_Call ; and return Not_Init_Pr: CMP AH,002H ; Check for Printer Sataus JNZ Not_Get_Stat ; MOV AH,0D0H ; Be Happy (never error) JMP Done_W_Call ; and return Not_Get_Stat: CMP AH,002H ; Check valid Printer Fcn call JMP Done_W_Call ; and return Done_W_Call: POP DS ; Restore the world POP BX ; POP CX ; POP DX ; IRET ; Not_4_us: POP DS ; Restore the world POP BX ; POP CX ; POP DX ; PUSHF ; simulate an INT CLI ; CALL CS:PrevISRV ; Call origonal vidio interrupt code IRET ; ISRV ENDP ; LCD_Init PROC ; PUSH AX ; MOV AH,038H ; Init LCD to 8 bit data 2 lines 5 x 7 CALL CmdOut ; MOV AH,00CH ; Turn LCD on CALL CmdOut ; MOV AH,001H ; Clear LCD display CALL CmdOut ; CALL Execut ; Wait for LCD to finish last command MOV AH,00EH ; Turn cursor on CALL CmdOut ; MOV AH,00FH ; Make it blink CALL CmdOut ; MOV AX,00000H ; MOV Column,AX ; Reset colunm counter POP AX ; RET ; LCD_Init ENDP ; LCD_Send PROC ; PUSH BX ; PUSH AX ; CMP AL,00CH ; Form Feed ? JNZ LCD_SndNFF ; No? Then skip Form Feed code CALL LCD_Init ; Yes? Init LCD to clear LCD screen JMP LCD_SndCRXIT ; and exit LCD_SndNFF: CMP AL,00AH ; Line Feed ? JZ LCD_SndCRXIT ; Exit Do nothing, Zap line feeds INC Column ; Bump Column MOV BX,Column ; Get column AND BX,0003FH ; Strip row and command code CMP BX,CS:LnsPerCol ; Are we past the last character I.E. ; off the end of the display ? ; Note: For a 24 column display the ; proper value is 25 decimal JNZ LCD_SndNW ; If not go to No Wrap AND Column,000C0H ; If so reset column position XOR Column,00040H ; Switch to other row OR Column,00080H ; Set bit to form cursor address command MOV BX,AX ; Save AX reg MOV AX,Column ; Place cursor command where CmdOut MOV AH,AL ; expects it to be CALL CmdOut ; Send command INC Column ; Bump Column because DatOut will ; move cursor on the display MOV AX,BX ; Restore AX reg LCD_SndNW: CMP AL,00DH ; Carriage Return ? JNZ LCD_SndNCR ; No? Then skip New Line code AND Column,000C0H ; Reset column position XOR Column,00040H ; Switch to other line OR Column,00080H ; Set bit to form cursor address command MOV AX,Column ; Place cursor command where CmdOut MOV AH,AL ; expects it to be CALL CmdOut ; Send command JMP LCD_SndCRXIT ; and exit LCD_SndNCR: MOV AH,AL ; If we made it here it's normal ASCII CALL DatOut ; Send it LCD_SndCRXIT: POP AX ; POP BX ; RET ; LCD_Send ENDP ; CmdOut PROC ; PUSH DX ; Save the wo... PUSH AX ; MOV DX,PPORT ; Get port base addr ADD DX,00002H ; Compute Control port offset IN AL,DX ; Read bits MOV AL,PPRTC ; Read bits AND AL,010H ; Preserve Interrupt control bit OR AL,000H ; Put LCD in command mode MOV PPRTC,AL OUT DX,AL ; Send control CALL Settel ; Wait for bits to settel MOV DX,PPORT ; Get port base addr MOV AL,AH ; Put Command code in AL reg OUT DX,AL ; Write AL to LCD data lines CALL Settel ; Wait for bits to settel MOV DX,PPORT ; Get port base addr ADD DX,00002H ; Compute Control port offset IN AL,DX ; Read bits MOV AL,PPRTC ; Read bits AND AL,010H ; Preserve Interrupt control bit OR AL,001H ; Glue in the strobe bit MOV PPRTC,AL OUT DX,AL ; Send control POP AX ; Restore the wo... POP DX ; RET ; CmdOut ENDP ; DatOut PROC ; PUSH DX ; Save the wo... PUSH AX ; MOV DX,PPORT ; ADD DX,00002H ; IN AL,DX ; MOV AL,PPRTC ; Read bits AND AL,010H ; Same as above except DON'T OR AL,004H ; put LCD in command mode MOV PPRTC,AL OUT DX,AL ; CALL Settel ; MOV DX,PPORT ; MOV AL,AH ; OUT DX,AL ; CALL Settel ; MOV DX,PPORT ; ADD DX,00002H ; IN AL,DX ; MOV AL,PPRTC ; Read bits AND AL,010H ; OR AL,005H ; MOV PPRTC,AL OUT DX,AL ; POP AX ; Restore the wo... POP DX ; RET ; DatOut ENDP ; ; Execut PROC ; PUSH BX ; Save the w.. MOV BX,Execut_time ; Get time factor for Executing Delay INC BX ; Execut2: DEC BX ; Wait a while JNZ Execut2 ; POP BX ; Restore the w.. RET ; Execut ENDP ; ; Settel PROC ; PUSH BX ; Save the w.. MOV BX,Settel_time ; Get time factor for Setteling Delay INC BX ; Settel2: DEC BX ; Wait a while JNZ Settel2 ; POP BX ; Restore the w.. RET ; Settel ENDP ; ; PGM_LEN EQU $-INIT ; ; When MeasureCPU is called the CPU's simple looping ability is measured ; against the timer irrespective of timer speed (this is compensated for ; after the measurement is taken). Further the timer speed is not affected. ; The result is a figure of merit returned in the AX register. ; This value is 0FF6 hex +/- 000D hex for a 4.77 mhz machine TimerMax DW 00000H ; CpuLoops DD 000000000H ; TicksInCk DB 000H ; CurTickInCk DW 00000H ; PrevISRT DD ? ; TimerIntCnt DW 00000H ; ; MeasureCPU PROC NEAR ; PUSH BX ; Save BX CX and DX PUSH CX ; on entry to proc PUSH DX ; ; PUSH ES ; Save ES before BDOS funct call MOV AH,035H ; Get Inturrupt BDOS function number MOV AL,008H ; Interrupt number for hardware timer INT 021H ; Execute function MOV WORD PTR CS:PrevISRT,BX ; Store target address MOV WORD PTR CS:PrevISRT+2,ES ; in double word POP ES ; Restore ES now that funct concluded MOV AL,008H ; Revector Interrupt 8, timer tick MOV DX,OFFSET ISRT ; vector to address of new ISR MOV AH,025H ; handler by using function 25H INT 021H ; Call DOS and do it ; MOV AX,00000H ; MOV CS:TicksInCk,AL ; Reset number of 18.2 Hz count MOV CS:TimerMax,AX ; Reset max count MOV WORD PTR CS:CpuLoops,AX ; Reset CPU speed count MOV WORD PTR CS:CpuLoops+2,AX ; double word DEC CS:TicksInCk ; Compensate for pre-increment TimerTicked: INC CS:TicksInCk ; CMP CS:TicksInCk,007 ; Have we been trying for 1/3 second JNZ ContnuReaching JMP MesurCpuIndex ; ContnuReaching: MOV AX,WORD PTR CS:TimerIntCnt ; Get 18.2 Hz tick count MOV CS:CurTickInCk,AX ; Set our copy of it ReachForTop: MOV AX,CS:CurTickInCk CMP AX,WORD PTR CS:TimerIntCnt ; Did 18.2 Hz tick count change JNZ TimerTicked ; Wait till it does MOV AL,000H ; control byte latchs current count of ;timer zero. It must be read as a straight binary 16 bit integer as two bytes ;low byte first high byte second. Ref. Intel Microprocessor and peripheral ;Handbook. Volume II-peripheral. Order number 230843-006 page 6-21 ;Note: the handbook lies! it suggests there is another way to read the timer ;if you try it the timer locks up. Presumably waitng for output to it. OUT 043H,AL ; send it PUSH AX ; \____ Kill one microsecond POP AX ; / IN AL,040H ; Read low byte MOV BL,AL ; PUSH AX ; \____ Kill one microsecond POP AX ; / IN AL,040H ; Read high byte MOV AH,AL ; MOV AL,BL ; CMP CS:TimerMax,AX ; Is this count bigger than last time JA ReachForTop ; No? Well try again. MOV CS:TimerMax,AX ; Yes? Make this count the standard. JMP ReachForTop ; and try again. ; MesurCpuIndex: MOV AX,WORD PTR CS:TimerIntCnt ; Get 18.2 Hz tick count CMP AX,WORD PTR CS:TimerIntCnt ; Did 18.2 Hz tick count change JZ MesurCpuIndex ; Wait till it does MOV AX,CS:TimerIntCnt ; Get current 18.2 Hz tick count MOV CX,00000H ; DEC CX ; MesurCpuHiCnt: INC CX ; MOV BX,00000H ; MesurCpuLoCnt: INC BX ; JZ MesurCpuHiCnt ; CMP AX,WORD PTR CS:TimerIntCnt ; Did 18.2 Hz tick count change JZ MesurCpuLoCnt ; MOV WORD PTR CS:CpuLoops,BX ; Set CPU speed count MOV WORD PTR CS:CpuLoops+2,CX ; double word PUSH DS ; Save DS before BDOS funct call MOV AL,008H ; Revector Interrupt 8, timer tick MOV DX,WORD PTR CS:PrevISRT ; vector back to the address of MOV DS,WORD PTR CS:PrevISRT+2 ; the origonal ISR MOV AH,025H ; handler by using function 25H INT 021H ; Call DOS and do it POP DS ; Restore DS now that funct concluded ; MOV AX,WORD PTR CS:CpuLoops ; Get CPU speed count MOV DX,WORD PTR CS:CpuLoops+2 ; double word MOV BX,00400H ; MUL BX ; Mulpiply by 400 hex MOV WORD PTR CS:CpuLoops,AX ; Store result MOV WORD PTR CS:CpuLoops+2,DX ; double word MOV DX,00000H ; MOV AX,WORD PTR CS:TimerMax ; Get timer max MOV BX,00040H ; DIV BX ; Divide by 40 hex MOV BX,AX ; CMP BX,00000H ; Prevent divide by zero JNZ NoDivideZero ; MOV BX,00001H ; NoDivideZero: MOV AX,WORD PTR CS:CpuLoops ; Get normalized CPU speed MOV DX,WORD PTR CS:CpuLoops+2 ; count double word DIV BX ; Divide by normalized TimerMax MOV WORD PTR CS:CpuLoops,AX ; Store result MOV WORD PTR CS:CpuLoops+2,DX ; double word POP DX ; Restore DX CX and BX POP CX ; as we exit proc POP BX ; RET ; Return result in AX reg. MeasureCPU ENDP ; ; ; The only usefull thing this Proc does is bump a counter every time the ; timer chip pulls an interrupt ISRT PROC FAR ; INC WORD PTR CS:TimerIntCnt ; PUSHF ; simulate an INT CLI ; CALL CS:PrevISRT ; Call origonal timer interrupt code IRET ; ISRT ENDP ; ; TITLEpg DB ' DB 25 male Densitron LCD ' DB 'ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»',0DH,0AH DB ' Parrallel LM23B2C24CTW ' DB 'º I',027H,'m Jim Phillips starving programmer extro-º' DB 0DH,0AH DB ' Port 2 Line Display ' DB 'º rdinaire. Please contribute. Box 38001 º',0DH,0AH DB ' 1 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 6 ' DB 'º Denver, CO 80238. Use debug to change portº',0DH,0AH DB ' 2 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 7 ' DB 'º Address. LPT1 = 378, LPT2 = 278, LPT3 = 3BCº',0DH,0AH DB ' 3 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 8 ' DB 'º Byte 103 Hex is low byte, 104 is high byte.º',0DH,0AH DB ' 4 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 9 ' DB 'º Many LCD display modules will work with º',0DH,0AH DB ' 5 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 10 ' DB 'º this program as long as they are based on º',0DH,0AH DB ' 6 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 11 ' DB 'º the same controller chip. Hitachi number º',0DH,0AH DB ' 7 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 12 ' DB 'º HD44780A00 or equivelent. This TSR driver º',0DH,0AH DB ' 8 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 13 ' DB 'º diverts PRN: data to the LCD module. Form-º',0DH,0AH DB ' 9 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 14 ' DB 'º Feed clears display, Carriage return moves º',0DH,0AH DB ' 16 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 4 ' DB 'º to next line, Line Feeds are ignored. º',0DH,0AH DB ' 25 ÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄ 1 ' DB 'ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ',0DH,0AH DB ' ÃÄÄÄÄÄÄÄÄÄÄ 5 Ú' DB 'ÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿',0DH,0AH DB ' 1N4733 ÃÄÄÄ>ÃÄÄÄÄÄ 2 ÄÄÄÙ' DB ' ³ ³ ³ ³ ³ 9 Volt ³Note: See README.TXT ³',0DH,0AH DB ' 1.2K ohm ÀÄÄ\/\/\ÄÄÄ 3 ÄÄÄÄ' DB 'ÄÄ´³³³³³³ÃÄÄÙ Battery ³for circuit details. ³',0DH,0AH DB ' (' DB '-) ³ ³ ³ ³ (+) ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ',0DH,0AH DB '$' ; Densitron 2 line X 24 Negative contrast display LM23B2C24CTW ; CSEG ENDS ; END INIT