home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Oakland CPM Archive
/
oakcpm.iso
/
cpmug
/
cpmug038.ark
/
BVIOS.ASM
< prev
next >
Wrap
Assembly Source File
|
1985-02-10
|
28KB
|
1,078 lines
;
; TITLE BOB VAN VALZAH'S INPUT/OUTPUT SYSTEM (FOR CP/M)
; AND FOR UCSD PASCAL, TOO (AS OF VERSION 8)
; FILENAME BVIOS.ASM
; AUTHOR ROBERT A. VAN VALZAH 2/22/79
; LAST REVISOR R. A. V. 12/15/79
; REASON CONDITIONALIZE DIABLO DRIVER
;
;
;
CBIVERS EQU 14 ;CBIOS VERSION NUMBER
CPMVERS EQU 14 ;CP/M VERSION NUMBER
;
; CONDITIONAL ASSEMBLY EQUATES
;
FALSE EQU 0
TRUE EQU NOT FALSE
;
REL EQU FALSE ;TRUE TO GENERATE RELOCATABLE CODE
MSIZE EQU 47 ;MEMORY AND CP/M SYSTEM SIZE
NODISKS EQU 1 ;NUMBER OF PHYSICAL DRIVES IN SYSTEM
; NOTE: BACKSPC CAN'T BE TRUE IF PASCAL IS TRUE & NODISKS=1
BACKSPC EQU FALSE ;TRUE TO GENERATE CURSOR BACKUP CODE
PASCAL EQU FALSE ;TRUE TO GENERATE UCSD PASCAL I/O CODE
LCASE EQU TRUE ;TRUE TO GENERATE SOFTWARE LOWER CASE
VDM EQU TRUE ;TRUE TO GENERATE VDM CONSOLE OUT
DIABLO EQU FALSE ;TRUE TO GENERATE DIABLO Hy-Type DRIVER
;
; BASE OF DOS (CCP) LOAD COMPUTATION
;
IF REL
BIAS EQU 0100H ;FOR RELOCATION, ASSUME CCP STARTS AT 0
ENDIF
;
IF NOT REL ;OTHERWISE USE REAL CCP STARTING ADDR.
BIAS EQU (MSIZE-16)*1024+2900H
ENDIF
;
; OTHER ENTRY CP/M ENTRY POINTS
;
CPMB EQU BIAS ;CCP START
BDOS EQU CPMB+806H ;BDOS ENTRY POINT
CBIOS EQU CPMB+1500H ;CBIOS START
DOSEND EQU CPMB+1700H ;END OF DOS LOAD +1
;
; COMPUTE NUMBER OF SECTORS TO LOAD
;
DOSSECT EQU (CBIOS-CPMB)/128
;
; DISK I/O PORT NUMBERS
;
DBASE EQU 0F8H ;BASE ADDRESS OF CONTROLLER CARD
COMMAND EQU DBASE ;FD1771 COMMAND OUTPUT
STATUS EQU DBASE ;FD1771 STATUS INPUT
TRACK EQU DBASE+1 ;TRACK INPUT/OUTPUT
SECT EQU DBASE+2 ;SECTOR OUTPUT
DATA EQU DBASE+3 ;DATA INPUT & OUTPUT
WAIT EQU DBASE+4 ;DATA SYNCRONIZATION & INTRQ INPUT
CONTROL EQU DBASE+4 ;CONTROL OUTPUT
;
; FD1771 COMMANDS
;
HOMECMD EQU 0 ;RESTORE (HOME) COMMAND
SEEKCMD EQU 10H ;SEEK TRACK COMMAND
READCMD EQU 88H ;READ IBM FORMAT
WRITCMD EQU 0A8H ;WRITE IN IBM FORMAT
FINTCMD EQU 0D0H ;FORCE INTERRUPT
;
; OPTION BITS WITHIN COMMANDS
;
LOADHEAD EQU 08H ;LOAD HEAD WHILE SEEKING
RATE EQU 10B ;STEP RATE OF 10MS.
;
; VDM EQUATES
;
SCREEN EQU 0CC00H ;STARTING SCREEN ADDRESS
SCRLPT EQU 0C8H ;SCROLL PORT
;
;
; DEFINE CBIOS RAM AREAS
;
; NON-INITIALIZED RAM
; SEE ALSO - AREA PAST JUMP TABLE FOR INITIALIZED RAM
;
ORG 4H ;THIS IS WHERE CP/M STORES THE
DISKNO: DS 1 ;CURRENTLY LOGGED DRIVE
;
ORG 40H ;SCRATCH AREA FOR CBIOS
IOD DS 2 ;DMA ADDRESS
;
ORG 4CH ;DIABLO PARAMETER RAM
;
; ram parameter area
;
linhgt ds 1 ;line height in increments
chwid ds 1 ;character width in increments
lpp ds 1 ;lines per page
;
;
; ROUTINES WITHIN THE CBIOS MAY BE INTERNALLY REFERENCED (CALLED
; FROM WITHIN THE CBIOS), EXTERNALLY REFERENCED (CALLED BY CP/M)
; OR BOTH. ANY ROUTINE WHICH IS EXTERNALLY REFERENCED IS
; ASSUMED TO CLOBBER ALL REGISTERS. REGISTER USAGE OF ROUTINES
; REFERENCED ONLY INTERNALLY IS DECLARED IN THE PRE-ROUTINE
; COMMENT BLOCK. ANY REGISTERS NOT MENTIONED ARE PRESERVED.
;
;
ORG CBIOS ;ORG TO START OF CBIOS AREA
;
; I/O JUMP VECTOR
; THIS IS WHERE CPM CALLS WHENEVER IT NEEDS
; TO DO ANY INPUT/OUTPUT OPERATION.
; USER PROGRAMS MAY USE THESE ENTRY POINTS
; ALSO, BUT NOTE THAT THE LOCATION OF THIS
; VECTOR CHANGES WITH THE MEMORY SIZE.
;
IF PASCAL
JMP $ ;NOT USED IN PASCAL SYSTEM
JMP $ ;HANG IF USER COMES HERE
ELSE
JMP CBOOT ;FROM COLD START LOADER
WBOOTE:
JMP WBOOT ;WARM BOOT ENTRY
ENDIF
JMP CONST ;CONSOLE STATUS
;REG A = 000H IF NO HCARACTER READY
;REG A = 0FFH IF CHARACTER IS READY
JMP CONIN ;CONSOLE CHARACTER IN (TO REG A)
JMP CONOUT ;CONSOLE CHARACTER OUT (FROM REG C)
JMP LIST ;LIST OUT (FROM REG C)
JMP PUNCH ;PUNCH OUT (FROM REG C)
JMP READER ;PAPER TAPE READER IN (TO REG A)
JMP HOME ;MOVE DISK HEAD TO TRACK 00
JMP SELDSK ;SELECT THE DISK GIVEN BY REG C
JMP SETTRK ;SET TRACK ADDRESS (0,...76) FOR I/O
JMP SETSEC ;SET SECTOR ADDRESS (1,...26) FOR I/O
JMP SETDMA ;SET DMA ADDRESS FOR I/O
JMP READ ;READ A SECTOR
JMP WRITE ;WRITE A SECTOR
;
; INITIALIZED RAM AREAS
;
;***********************NOTE: THE FOLLOWING BYTES MUST
; BE IN ADJACENT RAM LOCATIONS
; AND NOT CROSS A 256 BYTE PAGE BOUNDRY
IOT DS 1 ;TRACK
IOS DS 1 ;SECTOR
SELREQ DS 1 ;CURRENT DRIVE SELECT REQUEST
LASTSEL DB 0 ;LAST DRIVE ACTUALLY SELECTED
DHPOS DB 1 ;HEAD POSITION TABLE
DS NODISKS-1 ;STORAGE FOR DISKS B THRU D
IF VDM
CURSOR DW SCREEN+3C0H
ENDIF
IF BACKSPC
RUBFLG DB 0 ;NON ZERO IF RUBOUT WAS LAST CHR TYPED
ENDIF
IF PASCAL
BKCHR DB 0 ;BREAK CHARACTER, 0 IF EMPTY
FLUSH DB 0 ;FLUSH OUTPUT IF NON ZERO
ENDIF
IF LCASE
ULTG DB 0FFH ;UPPER CASE=0, LOWER CASE OTHERWISE
ENDIF
;
; COLD BOOT
; EXTERNALLY REFERENCED
; THIS SECTION IS EXECUTED WHENEVER RESET AND RUN
; IS PUSHED, AFTER THE COLDSTART LOADER READS IN
; THE CPM SYSTEM.
;
IF NOT PASCAL
CBOOT:
LXI SP,100H ;SET STACK FOR INITILIZATION
;NOTE: SOME RAM INITIALIZATION HAS BEEN DB'D
;INTO THE RAM DEFINITION ABOVE
if diablo
mvi a,6 ;init diablo character width
sta chwid
mvi a,8 ;init diablo line height
sta linhgt
mvi a,66 ;init diablo lines per page
sta lpp
endif
XRA A ;COME UP ON DRIVE A
STA DISKNO
IF VDM
OUT SCRLPT ;INITIALIZE VDM SCROLL PORT
ENDIF
CALL MSGP ;PRINT SINGNON MESSAGE
IF REL
DB '00' ;A MESSAGE THAT DOESN'T CHANGE WITH
;DIFFERENT MEMORY SIZES
ENDIF
IF NOT REL
DB '0'+MSIZE/10, '0'+MSIZE MOD 10
ENDIF
DB 'K CP/M VERS '
DB '0'+CPMVERS/10, '.', '0'+CPMVERS MOD 10
DB '-'
DB '0'+CBIVERS/10, '.', '0'+CBIVERS MOD 10 + 80H
JMP GOCPM ;SET LO MEM JMPS AND ENTER CP/M
;
; WARM BOOT
; EXTERNALLY REFERENCED
; READ THE CCP AND BDOS IN TO MEMORY AFTER TRANSIENT EXECUTION
;
WBOOT:
LXI SP,100H ;SET STACK DURING BOOT
XRA A ;BOOT FROM DRIVE A
STA SELREQ
CALL HOME ;BOOT FROM TRACK 0
;NEXT SECTOR TO READ - 1 KEPT IN REG D
;NUMBER OF SECTORS TO GO IN REG E
LXI D,(1 SHL 8) + DOSSECT
LXI H,CPMB ;START LOADING AT BASE OF CCP
BOOTSEC:
INR D ;ADD ONE TO SECTOR NUMBER
MOV A,D ;END OF FIRST TRACK?
CPI 26+1
JNZ SAMTRK ;NO - STAY ON SAME TRACK
MVI D,1 ;YES - RESET SECTOR NUMBER TO 1
MOV A,D ;AND STEP IN TO TRACK 1
STA IOT
SAMTRK:
STA IOS ;STORE SECTOR FOR READ
SHLD IOD ;STORE DMA ADDRESS FOR READ
PUSH D ;SAVE SECTOR & COUNT WHILE READING
CALL READ ;READ A SECTOR
POP D ;RESTORE SECTOR & COUNT
DCR E ;DECREMENT COUNT (DONE LOADING?)
JNZ BOOTSEC ;NO - KEEP LOADING
;YES - FALL THRU TO BRING UP CP/M
;
; DONE WITH LOAD, SO SET UP LOW MEMORY JMP VECTORS AND
; RESET DMA ADDRESS
;
GOCPM:
IF DIABLO
CALL INIT ;INTIALIZE DIABLO
ENDIF
LXI H,80H ;SET DEFAULT DMA ADDRESS
SHLD IOD
MVI L,0 ;NOW REG HL = 0
MVI M,JMP ;PUT 'JMP WBOOTE' AT LOCATION 0
INX H
MVI M,LOW WBOOTE
INX H
MVI M,HIGH WBOOTE
INX H
INX H ;NOW POINTING TO DISKNO (REG HL = 4)
MOV C,M ;GET LAST LOGGED DISK NUMBER FOR CCP
INX H ;POINT TO BDOS JUMP (REG HL = 5)
MVI M,JMP ;PUT 'JMP BDOS' AT LOCATION 5
INX H
MVI M,LOW BDOS
INX H
MVI M,HIGH BDOS
JMP CPMB
ENDIF ;END OF IF NOT PASCAL CONDITIONAL
;
; HOME DISK HEAD TO TRACK 0
; INTERNALLY AND EXTERNALLY REFERENCED
;
HOME:
IF NODISKS NE 1 ;IF MULTI-DRIVE SYSTEM
CALL DO$SEL ;SELECT CORRECT DRIVE BEFORE HOMING
MVI A,RATE ;ISSUE HOME COMMAND
CALL CMD$TY1
;IGNORE ERRORS
ENDIF
MVI C,0 ;AND FALL THRU TO SET TRACK
IF NODISKS NE 1
CALL HPADR ;RESET HEAD POSITION BYTE FOR DRIVE
MOV M,C
ENDIF
;
; SET TRACK NUMBER FOR DISK I/O
; EXTERNALLY REFERENCED
; ENTRY C TRACK NUMBER (0...76)
;
SETTRK:
MVI L,IOT AND 0FFH
DB 11H ;GENERATE LXI D TO SKIP NEXT MVI L
;
; SET SECTOR NUMBER FOR DISK I/O
; EXTERNALLY REFERENCED
; ENTRY C SECTOR NUMBER (1...26)
;
SETSEC:
MVI L,IOS AND 0FFH
DB 11H ;GENERATE LXI D TO SKIP NEXT MVI L
;
; SET DISK DRIVE NUMBER FOR DISK I/O
; EXTERNALLY REFERENCED
; ENTRY C DRIVE NUMBER (0...3)=(A...D)
;
SELDSK:
MVI L,SELREQ AND 0FFH
MVI H,IOT SHR 8
MOV M,C
RET
;
; SET DMA ADDRESS FOR DISK I/O
; EXTERNALLY REFERENCED
; ENTRY BC DMA ADDRESS (0000H-0FFFFH)
;
SETDMA:
MOV H,B
MOV L,C
SHLD IOD
RET
;
; WRITE A SECTOR TO DISK
; EXTERNALLY REFERENCED
;
WRITE:
MVI E,WRITCMD
DB 1 ;GENERATE LXI B TO SKIP MVI E FOLLOWING
;
; READ A SECTOR FROM DISK
; INTERNALLY AND EXTERNALLY REFERENCED
; EXIT HL (IOD)+128 DMA ADDRESS + 128
; B,C,D CLOBBERED
;
READ:
MVI E,READCMD
CALL DO$SEL ;SELECT CORRECT DRIVE F╧R I/O
IF PASCAL
MVI A,1 ;READY TO RETURN FAILURE CODE
RC ;EXIT IF SELECTED DRIVE NOT READY
ENDIF
RTRY: ;RETRY RE-ENTRY POINT
IF NODISKS EQ 1
LDA IOT ;GET REQUESTED TRACK
ELSE
CALL HPADR ;GET ADDRESS OF HEAD POSITION BYTE
LDA IOT ;GET REQUESTED TRACK
MOV M,A ;STORE IT AS NEW HEAD POSITION
ENDIF
OUT DATA ;SEND REQUESTED TRACK TO 1771
MVI A,SEEKCMD+LOADHEAD+RATE
CALL CMD$TY1 ;COMMAND DISK TO SEEK REQUESTED TRACK
JNZ ERROR ;ISSUE ERROR IF SEEK FAILED
LDA IOS ;SEND REQUESTED SECTOR TO 1771
OUT SECT
LHLD IOD ;GET REQUESTED DMA ADDRESS
CALL XFER ;PERFROM DISK I/O TRANSFER
IN STATUS ;SEE IF SUCCUSSFULL
ANI 1111$1100B ;CHECK FOLLOWING BITS X/X/X/X$X/X/X/XB
;NOT READY/WRITE PROTECT/WRITE FAULT/
;RECORD NOT FOUND$CRC ERROR/LOST DATA//
RZ ;YES - RETURN WITH REG A = 0
ERROR:
PUSH PSW ;SAVE ERROR BITS
CALL MSGP ;PRINT FIRST PART OF ERROR MESSAGE
DB 13, 10, 'Error bits', '='+80H
POP PSW ;GET ERROR BITS BACK
CALL PHEX ;PRINT THEM AS A HEX NUMBER
FOOL:
CALL MSGP ;PRINT REST OF ERROR MESSAGE
DB ',Retry or Ignore', '?'+80H
CALL CONIN ;GET USERS RESPONSE
ANI 5FH ;CONVERT LOWER TO UPPER CASE
PUSH PSW ;SAVE DURRING ECHO
MOV C,A ;ECHO RESPONSE
CALL CONOUT
POP PSW ;GET RESPONSE BACK
SUI 'I' ;IGNORE?
RZ ;YES - SEND ALL OK FLAG BACK TO CP/M
CPI 'R'-'I' ;RETRY?
JNZ FOOL ;NO - RE-ISSUE MESSAGE
MVI A,RATE ;YES - SEEK HOME AND TRY AGAIN
CALL CMD$TY1 ;ISSUE HOME COMMAND
JNZ ERROR ;ERROR IN HOMING
JMP RTRY ;NO ERROR - TRY OPERATION AGAIN
;
; SEND A TYPE ONE COMMAND TO THE CONTROLLER, WAIT FOR FINISH
; INTERNALLY REFERENCED
; EXIT A ERROR BITS
; ZERO SET IF SUCCESS
;
CMD$TY1:
OUT COMMAND ;SEND COMMAND
IN WAIT
IN STATUS ;GET ALL STATUS BITS
ANI 1001$1001B ;LEAVE ONLY THE FOLLOWING
;NOT READY///SEEK ERROR$
;CRC ERROR///BUSY
RET ;RETURN ERROR BITS TO CALLER
;
; SEND COMMAND TO 1771 AND PERFORM DISK I/O TRANSFER
; HEAD WILL BE LOADED FIRST IF HARDWARE TIMEOUT HAS UNLOADED IT
; INTERNALLY REFERENCED
; ENTRY E 1771 COMMAND
; HL I/O ADDRESS
; EXIT E PRESERVED
; HL I/O ADDRESS+128
; A,FLAGS CLOBBERED
;
XFER:
MVI A,FINTCMD ;INTERRUPT 1771 TO GENERATE STATUS
OUT COMMAND
XTHL
XTHL
IN STATUS ;GET NEW STATUS
ANI 0010$0000B ;TRANSFORM HEAD LOADED STATUS INTO
XRI 0010$0000B
RAR ;HEAD LOAD BIT FOR COMMAND
RAR
RAR
ORA E ;OR HEAD LOAD BIT IN WITH COMMAND
OUT COMMAND ;SEND COMMAND TO CONTROLLER
CPI WRITCMD ;SEE IF READING OR WRITING
JNC XWLOOP ;WRITING
XRLOOP:
IN WAIT ;WAIT FOR DATA OR INTRQ
ORA A ;SET POSITIVE IF INTRQ
RP
IN DATA ;DATA IS READ - GO GET IT
MOV M,A ;LOAD INTO DMA ADDR
INX H
JMP XRLOOP
XWLOOP:
IN WAIT ;WAIT FOR DRQ OR INTRQ
ORA A ;SET POSITIVE IF INTRQ
RP
MOV A,M ;GET DATA FOR DISK
OUT DATA
INX H
JMP XWLOOP
;
; ACTUALLY PERFORM DRIVE SELECTION
; INTERNALLY REFERENCED
; ENTRY (SELREQ) DRIVE TO BE SELECTED
; EXIT A,C,FLAGS,HL CLOBBERED
; IN PASCAL VERSIONS: AS ABOVE EXCEPT
; EXIT CARRY SET IF DRIVE NOT READY OR NON-EXISTANT
;
DO$SEL:
IF NODISKS EQ 1 ;GENERATE LOGICAL SEL CODE
LXI H,SELREQ ;GET REQUEST
MOV A,M
IF PASCAL
CPI 1 ;CLEAR CARRY IF DRIVE B THRU D SELECTED
CMC ;SET CARRY IF ABOVE
RC
ENDIF
INX H ;POINT TO LAST DRIVE SELECTED
CMP M ;IS REQUEST SAME AS LAST SELECTED?
RZ ;IF SO - SELECT DOES NOTHING
MOV M,A ;IF NOT - UPDATE LAST SELECTED=REQUEST
CALL MSGP ;GIVE MOUNT MESSAGE
DB 13, 10, 'MOUNT', ' '+80H
MVI A,'A' ;CONVERT REQUEST TO ASCII DRIVE NAME
ADD M
MOV C,A ;PRINT REQUESTED DRIVE NAME
CALL CONOUT
IN 0 ;CLEAR ANY CHAR WHICH MIGHT BE WAITING
;GO TO CONIN TO WAIT FOR MOUNTING
ELSE ;GENERATE PHYSICAL DRIVE SELECT CODE
LDA SELREQ ;GET DRIVE TO SELECT TO REG A
CMA ;COMPLIMENT FOR CONTROL LATCH PORT
ADD A ! ADD A ! ADD A ! ADD A ;ROTATE INTO POSITION
ORI 2 ;MAKE IT A DRIVE SELECT COMMAND
OUT CONTROL ;SEND COMMAND TO CARD
CALL HPADR ;TELL 1771 WHERE WE LEFT THE HEAD OF
MOV A,M ;THE NEW DRIVE BEING SELECTED
OUT TRACK
MVI A,FINTCMD ;INTERRUPT 1771 TO GENERATE STATUS
OUT COMMAND
XTHL ;WAIT FOR STATUS TO MATERIALIZE
XTHL
IN STATUS ;GET ALL STATUS BITS
RAL ;DRIVE NOT READY BIT INTO CARRY
RET
ENDIF
;
; READ A CHARACTER FROM CONSOLE
; INTERNALLY AND EXTERNALLY REFERENCED
; EXIT A CHARACTER READ, PARITY RESET
; FLAGS CLOBBERED
;
CONIN:
IF PASCAL
XRA A ;CLEAR FLUSH OUTPUT FLAG
STA FLUSH
PUSH H ;SAVE CALLERS REG HL
LXI H,BKCHR ;SEE IF A GOBBLED CHR IS WAITING
MOV A,M
MVI M,0 ;RESET IT IF IT WAS
POP H ;RESTORE CALLERS REG HL
ORA A ;RESET Z IF ONE WAS WAITING
RNZ ;RETURN GOBBLED CHR
HCONIN: ;HARD CONSOLE IN ENTRY, BKCHR IS IGNORED
ENDIF
IN 1
RAR
JNC CONIN
IN 0
ANI 0111$1111B ;MASK PARITY
IF BACKSPC
CPI 7FH ;WAS RUBOUT TYPED?
IF LCASE
JNZ CSTEST ;NO - CHECK FOR CASE CONVERSION
ELSE
RNZ ;NO - LEAVE FLAG ALONE
ENDIF
STA RUBFLG ;YES - SET RUBOUT FLAG
ENDIF
IF LCASE
CSTEST:
MOV B,A ;SAVE CHR TYPED IN REG B
CPI 11H ;ESCAPED TYPED?
LDA ULTG
JZ ESCP ;YES - GO COMPLIMENT FLAG
ORA A ;NO - IS MODE = UPPER?
MOV A,B ;PREPARE TO RETURN CHR IF SO
RZ ;MODE IS UPPER
CPI 'A' ;IS CHR A LETTER?
RC ;NO - SKIP CONVERSION
CPI 'Z'+1
RNC ;STILL NOT A LETTER
ADI 20H ;IS A LETTER - CONVERT TO LOWER CASE
ENDIF
RET
IF LCASE
ESCP:
CMA ;COMPLIMENT CASE TOGGLE
STA ULTG
JMP CONIN
ENDIF
;
; CALCULATE HEAD POSITION BYTE ADDRESS
; INTERNALLY REFERENCED
; ENTRY (SELREQ) DESIRED DRIVE NUMBER
; EXIT HL ADDRESS OF HEAD POSITION BYTE FOR DRIVE
; A,FLAGS CLOBBERED
;
IF NODISKS NE 1
HPADR:
LDA SELREQ ;GET REQUESTED DRIVE
ADI LOW DHPOS ;ADD TO BASE OF HEAD POSITION TABLE
MOV L,A ;RESULT OF ADD TO REG HL
MVI H,HIGH DHPOS
RET
ENDIF
;
; CHECK CONSOLE INPUT STATUS
; INTERNALLY AND EXTERNALLY REFERENCED
; EXIT A 0FFH IF CHRACTER IS READY, 0H IF NOT READY
; FLAGS CLOBBERED
;
CONST:
IF PASCAL
LDA BKCHR ;IF CONOUT GOBBLED A CHARACTER
ORA A ;RESET ZERO
MVI A,0FFH ;PREPARE TO RETURN READY FLAG
RNZ ;SEND READY FLAG FOR GOBBLED CHR
HCONST: ;HARD CONSOLE STATUS CHECK ENTRY
;CONTENTS OF BKCHR IS IGNORED
ENDIF
IN 1
RAR
SBB A
PUNCH: ;DUMMY DEVICE
READER: ;DUMMY DEVICE
RET
;
; OUTPUT CHARACTER TO LIST DEVICE
; EXTERNALLY REFERENCED
; ENTRY C CHARACTER
; EXIT A,FLAGS CLOBBERED
;
LIST:
IF PASCAL
LDA FLUSH ;ARE WE FLUSHING OUTPUT?
ORA A
RNZ ;YES - FLUSH PRINTER TOO
ENDIF
;
; YOUR PRINTER DRI╓ER HERE
;
IF DIABLO
JMP PRINT ;SEND TO DIABLO
ENDIF
;
; IN-LINE MESSAGE PRINTER
; THE MESSAGE MUST BE TERMINATED BY BIT 7 HIGH ON THE LAST
; CHARACTER.
; INTERNALLY REFERENCED
; ENTRY STACK TOP MESSAGE ADDRESS (RETURN ADDRESS)
; EXIT C,A,FLAGS CLOBBERED
; NOTE: MODIFYS RETURN ADDRESS AND RETURNS ONE BYTE PAST
; END OF MESSAGE.
;
MSGP:
XTHL ;GET MESSAGE ADDRESS TO REG HL
MSG1:
MOV C,M ;GET A CHR FROM MESSAGE
CALL CONOUT ;PRINT IT
MOV A,M ;GET CHR BACK
INX H ;POINT TO NEXT
ORA A ;WAS BIT 7 SET ON LAST CHR?
JP MSG1 ;YES - KEEP PRINTING
XTHL ;NO - PUT RETURN ADDRESS BACK
RET
;
; PRINT TWO HEX DIGITS
; INTERNALLY REFERENCED ROUTINE
; ENTRY A 8-BIT VALUE TO BE PRINTED AS TWO HEX DIGITS
; EXIT A,C,FLAGS CLOBBERED
;
PHEX:
PUSH PSW ;SAVE LOW NIBBLE
RAR ! RAR ! RAR ! RAR ;POSITION HIGH NIBBLE FOR PRINT
CALL PNIB ;PRINT HIGH NIBBLE
POP PSW ;GET LOW NIBBLE BACK AND . .
;FALL THRU TO PRINT IT
;
; PRINT ONE HEX DIGIT
; INTERNALLY REFERENCED
; ENTRY A B3-B0 NIBBLE TO BE PRINTED AS A HEX DIGIT
; EXIT A,C,FLAGS CLOBBERED
;
PNIB:
ANI 0000$1111B ;LEAVE ONLY LOW NIBBLE
ADI 90H ;CAUSE CARRY IF > 9
DAA
ADI 40H
DAA
MOV C,A ;PASS ASCII CHAR TO CONOUT
;FALL THRU TO CONOUT
;
; OUTPUT A CHARACTER TO CONSOLE
; INTERNALLY AND EXTERNALLY REFERENCED
; ENTRY C ASCII CHARACTER TO BE SENT
; EXIT A,FLAGS CLOBBERED
;
CONOUT:
IF PASCAL
LDA FLUSH ;IS FLUSH FLAG SET?
ORA A ;RESET Z IF SO
JNZ FTEST ;YES - SKIP OUTPUT OF CHARACTER
ENDIF
IF NOT VDM ;THEN GENERATE NORMAL CONSOLE OUT
IF BACKSPC
MOV A,C ;SET Z IF RUBOUT
CPI 7FH ;IGNORE RUBOUTS
IF PASCAL
JZ FTEST ;GO CHECK FOR FLUSH
ELSE
RZ
ENDIF
LDA RUBFLG ;WAS RUBOUT THE LAST CHR INPUT?
ORA A
JZ CONO1 ;NO - JUST OUTPUT NORMALY
XRA A ;YES - RESET RUBOUT FLAG
STA RUBFLG
; WE NOW KNOW THAT CP/M IS ECHOING THE DELETED
; CHARACTER WHICH WE CAN IGNORE
MVI C,8 ;MOVE CURSOR OVER DELETED CHR
CALL CONO1
MVI C,' ' ;OVERWRITE IT WITH A BLANK
CALL CONO1 ;DELETING IT
MVI C,8 ;ADJUST CURSOR TO NEW END OF LINE
;FALL THRU TO CONO1
ENDIF ;END OF IF BACKSPC CONDITIONAL
CONO1:
IN 0
RAR
RAR
JNC CONO1
MOV A,C
OUT 1
ELSE ;ELSE OF IF NOT VDM, SO HERE VDM IS T
MOV A,C
ANI 0111$1111B ;MASK PARITY
CPI 13 ;IGNORE CARRIAGE RETURNS
RZ
IF BACKSPC
CPI 7FH ;IGNORE RUBOUTS
RZ
ENDIF
PUSH H
LHLD CURSOR
CPI 10 ;LF?
JZ NEWLINE
MOV M,A
IF BACKSPC
LDA RUBFLG ;WAS RUBOUT LAST THING TYPED?
ORA A
JNZ CURLEFT ;YES - GO MOVE CURSOR LEFT
ENDIF
INX H
MOV A,H
CPI SCREEN/256+4
JNZ EXIT
NEWLINE:
MVI M,' ' ;TURN OFF OLD CURSOR
PUSH D
LXI H,SCREEN
LXI D,SCREEN+40H
BLOCKMOVE:
LDAX D
MOV M,A
INX D
INX H
MOV A,D
CPI SCREEN/256+4
JNZ BLOCKMOVE
PUSH H
BLNK:
MVI M,' '
INX H
MOV A,H
CPI SCREEN/256+4
JNZ BLNK
POP H
POP D
EXIT:
MVI M,' '+80H
SHLD CURSOR
POP H
ENDIF ;END OF IF NOT VDM CONDITIONAL
;
IF PASCAL
FTEST:
CALL HCONST ;SEE IF CHR IS WAITING
RZ ;NO - JUST EXIT FROM CONOUT
CALL HCONIN ;YES - GO GET IT
CPI 'S'-40H ;CONTROL S?
JZ CONIN ;YES - GO WAIT FOR ANOTHER KEY TO CONT
CPI 'F'-40H ;CONTROL F?
JZ CNTLF ;YES - GO COMPLIMENT FLUSH FLAG
STA BKCHR ;NO - STORE IT AS THE BREAK CHARACTER
RET
CNTLF:
LDA FLUSH ;GET CURRENT FLUSH FLAG
CMA ;COMPLIMENT
STA FLUSH ;WRITE IT BACK
RET
ELSE ;NOT PASCAL I/O
RET
ENDIF ;END OF IF PASCAL CONDITIONAL
;
IF VDM AND BACKSPC ;GENERATE VDM CURSOR LEFT CODE
CURLEFT:
XRA A ;RESET RUBOUT FLAG
STA RUBFLG
MVI M,' ' ;BLANK EXISTING CHARACTER
DCX H ;BACK UP TO THE LEFT
JMP EXIT ;TURN ON NEW CURSOR & EXIT
ENDIF ;END OF IF VDM AND BACKSPC CONDITIONAL
IF DIABLO
;
;
; TITLE BI-DIRECTIONAL DIABLO PRINTER DRIVER
; FILENAME BIDI.LIB
; AUTHOR Robert A. Van Valzah 9/30/79
; LAST REVISOR R.A.V. 11/10/79
; REASON byte squeezing
;
;
; plan of attack:
; ===============
; characters come in one at a time and are stored into
; a buffer until a line feed comes in. at this point,
; the line in the buffer is analized and a decision is
; made to print it forward or backward so as to mininmize
; the head movement.
;
; the gory details:
; =================
; characters which are printable are just stored in the
; buffer. blanks, on the other hand, are accumulated
; and the number of blanks to move is stored with a bias
; of 80h. the first byte of the buffer is an exception:
; it is initialized to 0h and is used to accumulate the
; number of spaces between the left margin and the first
; printable character (this is the location leolpos).
; the position of the rightmost printable character is
; keep track of as characters come in the location reolpos.
;
; as the head is moved across the page, its position is
; recorded in hpos. this information is used in
; conjunction with leolpos and reolpos to determine if
; printing forward or backward will cause the shortest
; printhead movement. if the printhead is currently to
; the left of the centerpoint of the line, then it is
; shortest to move to the left end and start printing.
; otherwise, it is shortest to move to the right end
; and print backward. if the printhead is exactly at
; the midpoint, the line is printed backward so as to
; leave the printhead as close to the left margin
; as possible.
;
;
; port i/o number equates
;
base equ 0f4h
datal equ base
datah equ base+1
cmand equ base+2
stats equ base
;
; print formatting equates
;
ncols equ 120 ;max number of cols/line (must be <=126)
;
; command bits
;
restr equ 1 ;restore carriage
chstb equ 2 ;character strobe
xstb equ 4 ;carriage strobe
ystb equ 8 ;paper feed strobe
selpr equ 10h ;select printer
selry equ 20h ;select ready
rblft equ 40h ;ribbon lift
;
; status bits
;
chrdy equ 8 ;character ready
xrdy equ 10h ;carriage ready
yrdy equ 20h ;paper feed ready
;
;
; print character in reg c
;
print:
mov a,c ;get char to print to reg a
ani 7fh ;strip parity
mov c,a
lhld nbufad ;and pointer to next buffer address
cpi 13 ;test for special characters
rz ;ignore carriage return
cpi 10
jz plf ;line feed
cpi 12
jz pff ;form feed
inr m ;assume a space
cpi ' '
rz ;was a space, all done
dcr m ;un-do assumption
rc ;was some other control char, ignore
;must be a printable character
mov a,l ;see if buffer is about overflow
cpi low(buf+ncols-2)
rz
cpi low(buf) ;see if this is first character
mov a,m ;get amt to move before printing
jz gotamt ;jump if first character
cpi 81h
jz noblank ;no blanks between last & this char
sui 80h ;subtract flag value
gotamt: ;amount to move in reg a
inx h ;move over number of blanks
db 11h ;lxi trick to skip following mvi a
noblank:
mvi a,1 ;like one blank between character
mov m,c ;store the character comming in
inx h ;point to next buffer location
mvi m,81h ;init to one blank to next char
shld nbufad ;update buffer pointer
lxi h,reolpos ;update right end of line position
add m
mov m,a
ret
;
; print line feed
;
plf:
mov a,l ;see if nbufad = buf
cpi low(buf) ;if = then blank line
jz lfend ;=, so don't print anything
lda leolpos ;get left end of line position
mov b,a ;save it in reg b
lda reolpos ;get right end of line position
add b ;add leolpos to reolpos
rar ;divide by two to find midpoint
mov b,a ;save line center point in reg b
lda hpos ;get current head position
sub b ;hpos-(leolpos+reolpos)/2
jc forward ;middle > hpos
;
; print the buffer backward
;
backward:
mvi a,80h ;put end of buffer marker at left end
sta buf
lda reolpos ;get absolute position of right eol
lxi h,hpos ;compute amount to move to get there
sub m ;reolpos-hpos
lhld nbufad ;get right most character to print
dcx h
bkwd1:
;signed distance to move is now in reg a
call movprt ;move as needed and print a character
dcx h ;point to movement amount
mov a,m ;amount to move to reg a
sui 80h ;test for eob mark
jz lfend ;hit end of line
dcx h ;assume this is a movement
cma ;two's comp for leftward movement
inr a
jnc bkwd1 ;it was, reg a has amount to move
inx h ;it wasn't, fix buffer pointer
mvi a,0ffh ;move one space to the left
jmp bkwd1
;
; print buffer forwards
;
forward:
mvi m,80h ;put in eob mark (also ignores trailing blanks)
lxi h,buf ;pointer into buffer
mov a,m ;absolute pos of leftmost char to reg a
inx h ;point to first printable character
push h ;save buffer pointer
lxi h,hpos ;compute amount to move to get to leolpos
sub m ;leolpos-hpos
pop h ;restore buffer pointer
fwd1:
;signed distance to move is now in reg a
call movprt ;move and print a character
inx h ;point to next character
mov a,m ;get it
sui 80h ;test for end of line
jz lfend ;hit end of line
inx h ;point to character
jnc fwd1 ;was a movement, reg a has distance
dcx h ;was a character, backup pointer
mvi a,1 ;and set amount to move to 1
jmp fwd1 ;go print next character
;
; common finish up routine for print line feed
;
lfend:
lxi h,lfstodo ;add one to line feeds to do before
inr m ;printing next line
jmp init1 ;reset pointers
;
; print a form feed
;
pff:
lda lpp ;get lines per page
lxi h,lonp ;subtract lines printed on this page
sub m ;leaving lines left on this page
sta lfstodo ;which is the number of lfs to do
ret
;
; move the number of character positions in reg a (taken as a
; signed number, + to the right, - to the left) and
; print the character pointed to by reg hl.
;
movprt:
mov c,m ;get character to print
push h ;save while printing
mov e,a ;save amount to move in reg e
movr:
in stats ;wait for all movement to stop
ani chrdy+xrdy+yrdy
jnz movr
lxi h,hpos ;update the head position byte
mov a,e
add m ;add amount we are moving
mov m,a
mvi b,xstb ;ready x strobe for movstb
lda chwid ;load up character muliplicaton factor
call movstb ;send necessary x movement
lda lpp ;lines per page to reg b
mov b,a
lxi h,lfstodo ;number of pending line feeds
mov a,m ;skip movement if zero
ora a
jz noymov ;no y movement
mov e,a ;movement to reg e for movstb
mvi m,0 ;there will be none to do now
lxi h,lonp ;keep track of line on page
add m
mov m,a
sub b ;see if started new page
jc samepage ;nope
mov m,a ;yes - take lonp mod lpp
samepage:
lda linhgt ;load up line multiplicaton factor
mvi b,ystb ;ready y strobe for movstb
call movstb ;send necessary y movement
noymov:
mov e,c ;send character
mvi b,chstb ;send character strobe
mvi a,1 ;dummy multiplication factor
call movstb
pop h
ret
;
; move the signed amount in reg e using value in reg a as
; a mulitplication factor to get the number of increments
; then send strobe in reg b.
;
movstb:
push psw ;save multiplication factor
mov a,e ;form a 16 bit amount to move in reg de
ral ;sign of movement into carry
sbb a ;generate sign extention
mov d,a ;16-bit signed movement now in reg de
pop psw ;get multiplication factor
lxi h,0 ;initialize product
mulbyw: ;multiply
dad d
dcr a
jnz mulbyw ;keep multiplying
mov a,h ;is this leftward movement?
ora a
jp posmov ;no - distance is ok
xra a ;yes - negate distance
sub l ;reg a = 0 - low order
mov l,a ;which is low order compliment
sbb h ;now subtract out high order and
sub l ;subtract out excess low order
;complimented distance now in reg a and reg l
ori 4 ;and set negative motion bit
posmov:
cma ;high order data is active low
out datah ;send high order data
mov a,l ;get low order data
cma ;it too is active low
out datal ;send low order data
;fall thru to strobe
;
; pulse strobe in reg b
;
strobe:
xra a ;delay a while for data set-up time
dlay:
dcr a
jnz dlay
mvi a,0ffh-selpr-rblft-selry
push psw ;save bits which are allways high
sub b ;lower appropriate strobe bit
out cmand ;send strobe bit low
pop psw ;and send it high
out cmand
ret
;
; init for printing
;
init:
mvi b,restr ;pulse restore line to
call strobe ;reset printer
xra a ;set software head position to zero
sta hpos
sta lonp ;reset line counter to top of page
sta lfstodo ;zero out number of line feeds to do
init1: ;empty buffer entry point
xra a ;reset right end of line position
sta reolpos
lxi h,buf ;next buffer address pointer
shld nbufad
mov m,a ;inits left end of line position
ret
;
; ram areas
;
lonp ds 1 ;current line on page, 0 = first line
lfstodo ds 1 ;line feeds to do before printing
hpos ds 1
reolpos ds 1
nbufad ds 2
buf:
leolpos: ;address of byte containing left eol
ds 1
lmostch: ;address of left most character
ds ncols
;
ENDIF ;END OF IF DIABLO
;
END