;Interface Age Floppy ROM load/dump program
; Interface Age May 78
;   
; Modified for Sol-20 by R. Parsons
; 4-29-78
;   
 ORG 100H
 XEQ CLEAR
;   
SOUT  EQU 0C019H
SINP  EQU 0C01FH
STACK EQU 0CBFFH
SOLOS EQU 0C004H
PTDOS EQU 0BCB0H
CPM   EQU 0
MONIT EQU SOLOS
FSTAT EQU 0FAH  ;file status port
FDATA EQU 0FBH  ;file data port
FIMSK EQU 040H  ;input mask
TSTAT EQU 0FAH  ;tape status port
TDATA EQU 0FBH  ;tape data port
TOMSK EQU 080H  ;tape output mask
CSTAT EQU 0FAH  ;console status
CDATA EQU 0FCH  ;console data
CIMSK EQU 001H  ;console input mask
SOH   EQU 1
STX   EQU 2
ETX   EQU 3
EOT   EQU 4
DLE   EQU 16
CR    EQU 13
LF    EQU 10
CTRLZ EQU 1AH
;   
CLEAR: MVI A,11  ;clear screen
 CALL OUTT
START: LXI SP,STACK
 CALL TOFF  ;tape unit off
 CALL CRLF
 LXI H,CMSG  ;command message
 CALL SENDM  ;print it
 XRA A      ;get a zero
 STA LFLAG  ;reset load flag
 STA EFLAG  ;zero error count
 CALL CRLF
 MVI A,':'  ;prompt
 CALL OUTT  ;print a prompt
 CALL READ  ;input task from console
 CPI 'D'    ;dump file to tape
 JZ DUMP
 CPI 'L'    ;load to memory
 JZ LOAD
 CPI 'C'    ;copy all to memory
 JNZ ERROR
 MVI A,1
 STA LFLAG  ;set load flag
;   
; input address from terminal and load into memory
; HL is memory pointer; BC is data sum
;   
LOAD: CALL READHL  ;input start address
 CALL GO    ;wait for CR
 CALL TON  ;tape unit on
 XCHG
 LXI H,CSUMT  ;start of checksum table
 SHLD CPNTR   ;reset pointer to start
 XCHG
LOADN: CALL INBYTE  ;get a byte
 CPI EOT  ;end of file?
 JZ DONE  ;yes
 CPI SOH  ;start of block?
 JNZ LOADN  ;loop until start
 XRA A  ;get a zero
 MOV B,A  ;zero the data sum
 MOV C,A
 LXI D,BLOCK  ;current block number
;   
; input the block number (1-8 hex characters)
; NOTE: there is no check for > 8 characters
;   
BLCKN: PUSH D
 CALL INBYTE  ;get a byte
 POP D
 STAX D  ;save digit of block number
 INX D
 CPI STX  ;end of number?
 JNZ BLCKN  ;loop until end
 DCX D  ;back pointer to STX
 MVI A,' '
 STAX D  ;clear STX
;   
; input the text
;   
TEXT: CALL INBYTE  ;get a byte
 CPI ETX  ;end of block?
 JZ DEND
 CPI DLE  ;control character?
 JNZ MOVE  ;no, put byte in memory
 CALL BYTE  ;get the control character
 CALL INBY2  ;add to check sum
 ANI 1FH  ;convert to control character
MOVE: MOV E,A  ;
 LDA LFLAG  ;see if loading all
 ORA A
 JNZ TEXT  ;skip second prnting
 MOV M,E  ;put byte in memory
 MOV A,E  ;echo to screen while reading
 CALL OUTT
 INX H  ;increment memory pointer
 JMP TEXT  ;next byte
;   
; end of block - get checksum and compare
; to calculated sum
;   
DEND: LDA LFLAG  ;check load flag
 ORA A  ;see if zero
 JNZ DEN2  ;skip if full load
 MVI M,CR  ;put CR in memory
 CALL CRLF ;put CR on screen
 INX H  ;increment pointer
DEN2: PUSH B  ;save sum
 CALL FIXSM  ;get first two characters
 MOV D,A  ;put in H
 CALL FIXSM  ;get second two characters
 MOV E,A  ;put in L
 POP B  ;retrieve sum
 XCHG  ;  ;check sum to HL
 DAD B  ;add data sum to checksum
 MOV A,H
 ORA L  ;see if both H & L are zero
 XCHG  ;  ;restore pointer to HL
 JZ LOADN  ;ok, start next block
;   
; checksum error. save block number in buffer for
; later listing
;   
 PUSH H
 LXI D,BLOCK  ;block number
 LHLD CPNTR  ;pointer to checksum table
NCHAR: LDAX D  ;get block character
 MOV M,A  ;put in table
 INX D
 INX H
 SHLD CPNTR  ;save pointer
 CPI ' '
 JNZ NCHAR  ;next character
 MVI M,CTRLZ  ;put ctrl-Z at end of table
 LDA EFLAG  ;fetch error count
 INR A
 DAA  ;  ;convert to decimal
 STA EFLAG  ;save new value
 POP H  ;restore text pointer
 JMP LOADN
;   
; input two bytes for half of checksum
; convert to one binary byte in A
;   
FIXSM: CALL BYTE5  ;get first byte
 RLC  ;rotate to upper half
 RLC
 RLC
 RLC
 MOV B,A  ;save in B
 CALL BYTE5  ;get second part
 ORA B  ;combine both parts
 RET
;   
; get a byte without altering checksum
;   
BYTE5: CALL BYTE
 MOV E,A
 CALL INBY3  ;see if loading all
 ANI 0FH  ;keep lower four bits
 RET
;   
; input a byte from file, add to checksum
; check for end of file
;   
INBYTE: CALL BYTE  ;get the byte
INBY2:  MOV E,A   ;save byte in E
 ADD C  ;add to sum
 MOV C,A  ;put new sum back in C
 JNC INBY3  ;done if no carry
 INR B  ;add carry to B
INBY3: LDA LFLAG  ;ckeck load flag
 ORA A
 MOV A,E
 RZ 
 MOV M,A  ;put control bytes in memory
 INX H  ;increment pointer
 RET
;   
; input a byte from file
;   
BYTE: IN FSTAT  ;ckeck file status
 ANI FIMSK
 JZ BYTE  ;loop until ready
 IN FDATA  ;get byte
 ANI 127  ;strip parity
 RET
;   
; end of file, print pointer
;   
DONE: CALL OUTHL
 MVI M,CTRLZ  ;ctrl-Z marks end
 LDA EFLAG  ;see if any checksum errors
 ORA A
 JZ START  ;no, restart
 MOV B,A   ;save error count
 CALL CRLF
 MOV A,B
 CALL OUTHX  ;print number of errors
 LXI H,EMESG  ;point to error message
 CALL SENDM  ;print it
;   
; print block numbers which had checksum errors
;   
PLINES: CALL CRLF
PLIN2:  MOV A,M  ;get character
 INX H  ;increment pointer
 CPI 1  ;binary 1 at end of table
 JZ START  ;done
 CPI ' '  ;blank at end of number
 JZ PLINES  ;start on next number
 CALL OUTT  ;print character
 JMP PLIN2  ;next character
;   
; routine to send text in memory to tape
; ad time delay after CR
;   
DUMP: CALL READHL  ;get starting address
 CALL GO  ;wait for CR
 CALL TON  ;tape unit on
DMP2: MOV A,M  ;fetch byte
 INX H  ;increment pointer
 CPI CTRLZ  ;ctrl-Z at buffer end
 JZ START  ;done
 CALL TOUT  ;output byte
 JMP DMP2  ;continue - NO delay
*CPI CR  ;check for CR
*JNZ DMP2  ;continue if not
*MVI D,120  ;outer timing loop
*DMP3: MVI E,200  ;inner timing loop
*DMP4: DCR E
*JNZ DMP4  ;loop on E
*DCR D
*JNZ DMP3  ;loop on D
*JMP DMP2  ;delay done
;   
; routine to output a byte to tape
;   
TOUT: PUSH PSW
TOUTW: IN TSTAT  ;check status
 ANI TOMSK  ;mask unwanted bits
 JZ TOUTW  ;loop until ready
 POP PSW  ;get byte
 OUT TDATA
 RET
;   
; console input routine
;   
READ: CALL SINP  ;SOLOS input
 JZ READ
 ANI 127  ;strip parity
 CPI 3  ;ctrl-C?
 JZ START  ;restart
 CPI 24  ;ctrl-X?
 JZ MONIT  ;return to monitor
;   
; console output routine
;   
OUTT: PUSH PSW
OUTW: PUSH B
 MOV B,A
 CALL SOUT  ;SOLOS output
 POP B
 POP PSW
 RET
;   
; input an address to HL from console
;   
READHL: PUSH D
 CALL RDHEX  ;input high half
 MOV H,A
 CALL RDHEX  ;input low half
 MOV L,A
 POP D
 RET
;   
; input two hex characters and convert to
; a binary byte in E
;   
RDHEX: CALL HEX2  ;read upper character
 RLC
 RAL  ;rotate
 RAL
 RAL
 MOV E,A
 CALL HEX2  ;read lower half
 ADD E  ;combine in both
 MOV E,A  ;save in E
 RET
;   
; input a hex character to A
;   
HEX2: CALL READ  ;console input
 SUI '0'  ;remove ASCII bias
 JC ERROR  ;error, less than '0'
 CPI 23
 JNC ERROR  ;error, greater than 'F'
 CPI 10
 RC  ;  ;a number 0-9
 SUI 7
 CPI 10
 RNC  ;  ;a letter A-F
ERROR: MVI A,'?'
 CALL OUTT  ;print ? for error
 JMP START
;   
; output a double byte in hex
;   
OUTHL: MOV A,H  ;get H
 CALL OUTHX  ;print it
 MOV A,L  ;get L
;   
; convert a binary byte to two hex characters
; and print them
;   
OUTHX: PUSH PSW
 PUSH PSW
 RAR  ;  ;rotate upper character to lower
 RAR
 RAR
 RAR
 CALL HEX1  ;output upper character
 POP PSW
 CALL HEX1  ;output lower character
 POP PSW
 RET
;   
; output a hex character from
; lower four bits
;   
HEX1: ANI 0FH  ;mask upper four bits
 ADI 90H
 DAA
 ACI 40H
 DAA
 JMP OUTT
;   
; look for a character
;  return at end of console input line
;   
GO: CALL READ
 CPI CR
 JNZ ERROR
;   
; line feed and carriage return to console
;   
CRLF: MVI A,LF
 CALL OUTT
 MVI A,CR
 CALL OUTT
 RET
;   
; send message to console until binary
; zero is found.  HL points to message
;   
SENDM: MOV A,M  ;fetch byte
 CALL OUTT  ;print it
 INX H  ;increment pointer
 ORA A  ;zero at end
 JNZ SENDM  ;keep going
 RET
;   
TON: MVI A,80H+20H  ;set 300 baud, unit 1 on
 OUT TSTAT
 MVI B,2
DELAY: LXI D,0  ;delay as in SOLOS
DLOP1: DCX D
 MOV A,D
 ORA E
 JNZ DLOP1
 DCR B
 JNZ DELAY
 RET
;   
TOFF: MVI B,1  ;turn units off
 CALL DELAY
 XRA A
 OUT TSTAT
 RET
;   
;   
;   
EFLAG DB 0  ;checksum error count
LFLAG DB 0  ;load flag
CPNTR DW CSUMT  ;pointer to checksum table
CMSG  ASC 'Type C, L, or D followed by a four digit hex address'
      DB LF,CR
      ASC 'C = copy as is; L = load as text; D = dump memory'
      DB 0
EMESG ASC ' Checksum errors'
      DB 0
BLOCK DS 8  ;store current block number
      DS 1  ;pad for STX - bug in original
CSUMT DS 40  ;table of error records
;   

