TITLE 'LP: Device Driver for the C. Itoh Prowriter' H84IO EQU 0 Assemble for 8250 * * Glenn F. Roberts 10-Apr-83 * * SPACE 4 *** LP: * * LP: is a C. Itoh 'Prowriter' interfaced via an H-8-4 serial card * at the configured port. * STL 'System equates' XTEXT HOSDEF HDOS scall definitions XTEXT ASCII ASCII character equivalences XTEXT DDDEF Device driver communucation flags XTEXT MTR PAM/8 monitor equivalences XTEXT HOSEQU HDOS system equivalences XTEXT DIRDEF Directory entry format XTEXT ESINT System internal workareas XTEXT ESVAL System internal masks and values XTEXT ECDEF System error codes XTEXT PICDEF PIC format equivalences (bytes 0-5) XTEXT DEVDEF Device table entries XTEXT DVDDEF Device driver equivalences XTEXT U8251 8251 UART bit masks XTEXT U8250 8250 UART bit masks XTEXT SETCAL Fixed address routines in SET * * ----- Begin position independent code * CODE PIC * * ----- Device header (bytes 6-21) * DB DVDFLV Flag as a Device Driver DB DT.CW Device capability: Write DB 00000001B Mounted unit mask DB 1 Only one unit (unit 0) DB DT.CW Unit 0 : capable of write DS 7 Units 1-7 : don't exist DB DVDFLV Unit capable of SET options DW 0 Pointer to INIT code . SET 025Q Header must be exactly 21 bytes ERRNZ *-. * * ----- Heath reserved area * DS DVD.STE-. Reserve through SeT Entry point EJECT STL 'Flags and assembly constants' * * ----- Flags * F.FORM EQU 00000001B Form feed on LP: close SPACE 4,10 * * ----- Constant LP: parameters * PORT EQU 340Q Port BAUD EQU 30A Baud rate = 4800 baud SPACE 4,10 * * ----- Default LP: parameters * DFLT.FG EQU F.FORM Form feed on LP: close DFLT.LI EQU 6 Spacing 6 lines per inch DFLT.WD EQU 80 80 characters per line DFLT.LC EQU 60 60 lines per page DFLT.MA EQU 0 Left margin = 0 DFLT.LX EQU 1 Start on line 1 DFLT.CX EQU 1 Start in column 1 EJECT STL 'SET processing' *** SET processing entry point * * SET commands enter code here * * ENTRY: (DE) = line pointer * (A) = unit number * * EXIT: 'C' clear : no error * 'C' set : error * (A) = error code * * USES: ALL * SETNTR EQU * ERRNZ *-DVD.STE Must be at SET entry point ANA A Must call unit 0 (only one) JNZ SET1 if not, then return with error MOV B,D Else MOV C,E (BC) = parameter list address LXI D,PRCTAB (DE) = processor table address LXI H,OPTTAB (HL) = option table address CALL $SOP Call Set Option Processor RC If error, return CALL $SNA Else Scan for Next Argument RZ if none, then return normally MVI A,EC.ILO else flag error for ILlegal Option STC and set carry to flag error. RET Return. SET1 MVI A,EC.UUN Unknown Unit Number error STC set carry to flag error. RET Return. SPACE 4,10 *** FLAG * * Process Byte Flag * * ENTRY: (HL) = address of table vector * * EXIT: 'C' clear : no error * 'C' set : error * * USES: ALL * * FLAG EQU $PBF SPACE 4,10 *** VAL * * Process Value Flag option specification * * ENTRY: (BC) = next character address * (HL) = table vector index * * EXIT: (BC) updated * 'C' clear : no error * 'C' set : error * * USES: ALL * * VAL EQU $PBV LPI SPACE 4,10 *** LPI * * Process lines per inch option (either 6 or 8) * LPI EQU * MVI A,10 Convert (using base 10) CALL $CNA Numeric Argument. JC LPI1 (carry set if not numeric) * ----- Check size of LPI MOV A,H If it is greater ANA A than 256 then JNZ LPI1 it is much too big. MOV A,L If it is less CPI 6 than 6 then JC LPI1 it is too small. CPI 7 If it is 7 then JZ LPI1 it is also illegal. CPI 8+1 If it is greater than JNC LPI1 8 then it is too big * ----- Otherwise, it is legal STA TLP.LPI Store lines per inch ANA A and clear carry (no error) RET then return * ----- If illegal LPI come here LPI1 MVI A,EC.ILV Set illegal value error code STC and carry bit to flag error RET then return. HELP SPACE 4,10 ** HELP * * Type help (list valid options) on colsole * HELP CALL $TYPTX DB NL,NL,'Set Options:',NL,NL DB 'HELP Type this text',NL DB 'LPI n Lines/Inch (6 or 8)',NL DB 'PAGE n Lines/Page',NL DB 'WIDTH n Characters/Line [0-132]',NL DB 'MARGIN n Left margin',NL DB 'FORM Form-Feed at Close',NL DB 'NOFORM No Form-Feed at Close',NL DB ENL XRA A Clear carry (no error) RET EJECT STL 'Option table' *** OPTTAB - option table * * OPTTAB DW OPTTABE Address of end of table end, DB 6 6 bytes per table entry DB 'FOR','M'+200Q,FLAGI,F.FORM,F.FORM DW TLP.FLG,0 DB 'NOFOR','M'+200Q,FLAGI,F.FORM,0 DW TLP.FLG,0 DB 'PAG','E'+200Q,VALI,10,0,255 DW TLP.LC DB 'WIDT','H'+200Q,VALI,10,0,132 DW TLP.WID DB 'MARGI','N'+200Q,VALI,10,0,132 DW TLP.MAR DB 'LP','I'+200Q,LPII DB 0,0,0,0,0 DB 'HEL','P'+200Q,HELPI DB 0,0,0,0,0 OPTTABE DB 0 Option table end. SPACE 4,10 *** PRCTAB - processor table * * PRCTAB DS 0 FLAGI EQU *-PRCTAB/2 DW FLAG VALI EQU *-PRCTAB/2 DW VAL LPII EQU *-PRCTAB/2 DW LPI HELPI EQU *-PRCTAB/2 DW HELP SPACE 4 * * Force address to DVD.ENT * CODE -REL Turn off relocation temporarily, . SET * save current address, CODE +REL and turn relocation back on. DS DVD.ENT-. Blank space to DVD entry address EJECT STL 'Main driver entry routine' *** LP: entry point * * ENTRY: (A) = Process code * (BC) = Byte count * (DE) = Buffer address * * EXIT: (PSW) = 'C' clear : no errors * = 'C' set : error * (A) = error code * * USES: ALL * LPDVD EQU * ERRNZ *-DVD.ENT Must be at DVD entry point! CPI DC.MAX Compare process code to max JNC LPDVD1 Jump if too big, CALL $TBRA else branch in table: DB LPNSUIT-* 0 = Read from device DB LPWRITE-* 1 = Write to device DB LPNSUIT-* 2 = Read regardless DB LPNSUIT-* 3 = Open for reads DB LPOPENW-* 4 = Open for writes DB LPNSUIT-* 5 = Open for updates DB LPCLOSE-* 6 = Close channel to device DB LPABORT-* 7 = Abort; treat as close DB LPABORT-* 8 = Mount the device DB LPLOADD-* 9 = Load the device LPDVD1 MVI A,EC.ILR Process code illegal (illegal request) STC set carry to flag error. RET EJECT STL 'LPNSUIT, LPABORT, or LPLOADD' *** LPNSUIT * * Line printer not suited for requested operation. * * ENTRY: NONE * * EXIT: (PSW) = 'C' set (flags an error) * (A) = error code * * USES: PSW * LPNSUIT EQU * MVI A,EC.DNS Device not suitable error code. STC Set carry to flag error, RET and return LPABORT SPACE 4,10 *** LPABORT * * Line printer abort * * ENTRY: NONE * * EXIT: (PSW) = 'C' set (flags an error) * (A) = error code * * USES: PSW * LPABORT EQU * CALL LPCLOSE Close the LP: device, MVI A,EC.DDA and return with Driver Abort error STC and carry bit set. RET LPLOADD SPACE 4,10 *** LPLOADD * * Load the LP: device driver. * * * ENTRY: NONE * * EXIT: NONE * * USES: (F) * LPLOADD EQU * ANA A Clear carry bit RET and return EJECT STL 'LPOPENW - LINE PRINTER OPEN FOR WRITE' *** LPOPENW * * Open line printer for write * * ENTRY NONE * * EXIT (PSW) = 'C' clear : no error * 'C' set : error * (A) = error code * * USES ALL * LPOPENW EQU * CALL UNITASS Attempt to assign unit STC get ready for possible error MVI A,EC.UNA load unit assigned error code RNZ return with error if assigned, * ----- Unit not already assigned MVI A,10000000B Flag the unit as STA TLP.AS assigned, MVI A,1 and set line index STA TLP.LX and column index STA TLP.CX to one. * ----- Set up the serial port LDA TLP.POR Get port address, LHLD TLP.BAU and baud divisor, CALL I8250 and initialize UART LDA TLP.POR Get port address, MOV H,A and MVI L,UR.MCR offset for Modem Control Register * then set bits for "Data Terminal Ready", * "Request To Send", and "Out 2" lines * MVI A,UC.DTR+UC.RTS+UC.OU2 CALL OUT Set the lines. * ----- Initialize the LP: CALL INITLP Do any initialization here MVI A,CR CALL LPOUTCH RET EJECT STL 'LPWRITE write to LP:' *** LPWRITE * * Write to LP: device * * * ENTRY: (BC) = Byte count * (DE) = Address of data buffer * * EXIT: (PSW) = 'C' clear : no error * = 'C' set : error * (A) = error code * (BC) = unused byte count * (DE) = address of next byte to be written * * USES: ALL * LPWRITE EQU * CALL UNITASS Attempt to assign unit STC Set up for possible error, MVI A,EC.UNA Device unavailable code RZ and return if actually an error. LPW1 MOV A,B Check the count of ORA C bytes written and RZ return when last is written. LDA S.CAADR+1 Check periodically for console ANA A interrupt (CTL-A,B,C, or Z) JNZ LPW5 Jump if interrupted, LDAX D else get byte to be written CALL LPOUTCH and output it. INX D Point to next byte DCX B Decrement the count JMP LPW1 and loop. LPW5 EQU * Come here when interrupt PUSH H is received from console. PUSH PSW CALL LPCLOS. Close the LP: and POP PSW POP H RET return. EJECT STL 'LPCLOSE Close LP:' *** LPCLOSE * * Remove LP: from table of active devices * * ENTRY: NONE * * EXIT: 'C' clear: no error * 'C' set: error * (A) = error code * * USES ALL * LPCLOSE EQU * CALL UNITASS Check if unit assigned. MVI A,EC.UNA Prepare for not assigned error STC set carry to flag error RZ and return if error. LDA TLP.AS Get assignment byte ANI 01111111B clear bit 7, STA TLP.AS and save. LPCLOS. LDA TLP.FLG Check if form feed desired on ANI F.FORM close. Return if not, RZ LDA TLP.POR else get port address MOV H,A in H and add offset for MVI L,UR.LSR Line Status Register. LPC1 CALL IN Read line status and wait ANI UC.THE until Transmitter Holding JZ LPC1 Register is empty MVI L,UR.THR Then point to Transmitter MVI A,FF Holding Register and output a CALL OUT Feed Form character. RET Return. EJECT STL 'LP: subroutines' SPACE 4,14 *** LPOUTCH * * Output a character to LP: * * ENTRY: (A) = byte to write * (HL) = unit of device * * EXIT: Column Index updated * * USES: (PSW) * LPOUTCH EQU * PUSH H * ----- Check for Form Feed CPI FF Is character a form feed? JNZ LPOT1 No, next section CALL OUTCHAR Yes, output the FF MVI A,1 Then set: STA TLP.LX Line index = 1 STA TLP.CX Column index = 1 JMP LPOT9 and jump to exit * ----- Check if at end of page LPOT1 PUSH H PUSH PSW LDA TLP.LC Get the PAGE length parameter ORA A If it is zero then the USER is JZ LPOT2 keeping track of pages - jump. LXI H,TLP.LX Else get line index CMP M and compare with PAGE length. JNC LPOT2 Jump if Line index <= PAGE MVI A,FF else output a Form Feed and CALL LPOUTCH reset line and column counters. LPOT2 POP PSW POP H * ----- Next check if TAB character CPI TAB Is it a TAB ? JNZ LPOT5 No, jump. MVI A,' ' Yes, print a blank. CALL LPOUTCH LPOT3 LDA TLP.CX Get current column position DCR A and ANI 7 check if multiple of eight. JZ LPOT9 No. Jump. MVI A,' ' Yes. Loop printing CALL LPOUTCH blanks until next tab stop JMP LPOT3 is reached. * ----- Now check for Carriage Return LPOT5 CPI CR Is character a CR ? JNZ LPOT6 No. Jump. CALL OUTCHAR Yes. Output the character MVI A,1 and reset the STA TLP.CX column index to 1, JMP LPOT9 then jump to exit. * ----- Next check for New Line (LF) LPOT6 CPI NL It character a Line Feed ? JNZ LPOT7 No. Jump. MVI A,CR First print a Carriage Return, CALL LPOUTCH (use LPOUTCH to check if FF needed) MVI A,LF then print a Line Feed, CALL OUTCHAR (use OUTCHAR to avoid infinite loop) LDA TLP.LX then get the line counter, INR A increment it by one, STA TLP.LX and store it. JMP LPOT9 Jump to exit. * ----- Print the character LPOT7 CPI ' ' Compare to blank JC LPOT8 Jump if character < ' ' CPI RUBOUT or if >= ' ' and equal JNC LPOT8 to RUBout. * ----- Printable character PUSH PSW PUSH H LDA TLP.WID Get the WIDTH setting ANA A If it is zero then user is JZ LPOT7.5 handling it: jump. * ----- Check for line wrap LXI H,TLP.CX Get column index CMP M and compare with WIDTH. MVI A,NL Output a NL CC LPOUTCH if column index > WIDTH. * ----- Increment column index LPOT7.5 LDA TLP.CX Get the column index INR A and increment it. STA TLP.CX POP H POP PSW * ----- Finally, output the character. LPOT8 CALL OUTCHAR Output the character. * ----- Normal exit LPOT9 POP H RET EJECT SPACE 4,11 *** UNITASS * * Check LP: device table to see if specified unit is assigned. * * ENTRY (HL) = unit number * * EXIT (PSW) = 'Z' set => unit free * = 'Z' clear => unit assigned * * USES (PSW) * UNITASS EQU * LDA TLP.AS Look up in device table ANI 10000000B and check unit assigned bit [7]. RET SPACE 4,12 *** WAIT * * Wait for "handshake" indicating device ready * * ENTRY NONE * * EXIT NONE * * USES (PSW) * WAIT EQU * PUSH H WAIT0 LDA S.CAADR+1 Check flag for console ANA A (CTL-A,B,C, or Z) interrupt JNZ WAIT3 if so, jump. * ----- No console interrupt LDA TLP.POR Else get port address MOV H,A and MVI L,UR.MSR offset (Modem Status Register) CALL IN and try to read. ANI UC.DSR Is "Data Set Ready"? JZ WAIT0 No, keep looping. * ----- DSR (handshake) or console interrupt received WAIT3 POP H RET EJECT SPACE 4,12 *** INITLP * * Initialize LP: device driver * * * ENTRY NONE * * EXIT NONE * * USES NONE * INITLP EQU * * ----- Set line spacing LDA TLP.LPI Fetch lines per inch CPI 6 and compare to 6. MVI A,'A' If LPI=6 then we'll JZ LPI0 output ESC A, else MVI A,'B' we'll output ESC B. LPI0 STA INIA+1 Store A or B. * ----- Output the string LXI H,INIA Point to the string INI1 MOV A,M Fetch a character CPI 377Q is it the last? RZ If so, return, CALL OUTCHAR else output it INX H and point to next, JMP INI1 then loop. INIA DB ESC,0,CR String setup area. DB 377Q End of string flag byte. EJECT STL 'Common decks' *** * HDOS common decks * XTEXT DVDIO Device driver I/O XTEXT TBRA Table branch routine XTEXT TYPTX Routine to type to console EJECT SPACE 4,12 *** TLP.UNT - Table of LP: unit constants * * TLP.UNA EQU * TLP.UNT DB 0 Unit number TLP.AS EQU TLP.UNT Device assignment byte TLP.FLG DB DFLT.FG General flag byte TLP.POR DB PORT Port address D.PORT EQU TLP.POR TLP.BAU DW BAUD Baud divisor TLP.LPI DB DFLT.LI Lines per inch TLP.WID DB DFLT.WD Characters per line TLP.MAR DB DFLT.MA Left margin TLP.LC DB DFLT.LC Line counter TLP.LX DB DFLT.LX Line index TLP.CX DB DFLT.CX Column index EJECT STL 'PIC table listing' DW 'GR' Dummy address for relocation DS 64 Room for patches LON G Turn on PIC table listing END