;********************************************************
;*                                                      *
;*      BIGBOARD MONITOR ROM, NON-RELOCATABLE VERSION   *
;*                                                      *
;*      Original by Russell Smith (Version 3.3)         *
;*                                                      *
;*      Translated by Willis Gore                       *
;*      Version 3.3          7-June-1982                *
;*      Modifications:                                  *
;*                                                      *
;********************************************************
;
;
        .PABS
ROM     =       0F000H          ;START OF 2K ROM
RAM     =       0FF00H          ;START OF 256 BYTE RAM
CRTMEM  =       3000H           ;BASE OF 4K CRT MEMORY
;
;
        .LOC    ROM
;********************************************************
;*                                                      *
;*      COLD START INITIALIZATION ROUTINE FOR           *
;*      CONFIGURING THE SYSTEM AFTER A POWER-ON         *
;*      OR PUSHBUTTON RESET.                            *
;*                                                      *
;********************************************************
;
;
;       -- MONITOR ENTRY POINT TABLE --
;
COLD:   JMP     INIT            ;MONITOR COLD ENTRY POINT
WARM:   JMP     PROMPT          ;MONITOR WARM ENTRY POINT
CONST:  JMP     KBDST           ;CONSOLE STATUS VECTOR
CONIN:  JMP     KBDIN           ;CONSOLE INPUT VECTOR
CONOUT: JMP     CRTOUT          ;CONSOLE OUTPUT VECTOR
        JMP     CRTOUT          ;CRT OUTPUT VECTOR
        JMP     SIOST           ;SIO CHANEL B STATUS VECTOR
        JMP     SIOIN           ;SIO CHANEL B INPUT VECTOR
        JMP     SIOOUT          ;SIO CHANEL B OUTPUT VECTOR
        JMP     SELECT          ;DISK DRIVE SELECT
        JMP     HOME            ;HOME R/W HEAD
        JMP     SEEK            ;SEEK TO TRACK
        JMP     READ            ;READ SECTOR
        JMP     WRITE           ;WRITE SECTOR
;
;
;
;       DO A SHORT POST-RESET TIME DELAY. ALSO INITIALIZES THE
;       STACK POINTER AND FILLS THE MONITOR SCRATCH RAM WITH ZEROS.
;
INIT:   DI
        LXI     H,RAM           ;POINT TO START OF MONITOR RAM
INIT1:  MVI     M,0             ;FILL 256 BYTE SPACE WITH ZEROS
        SPHL                    ;DO SOMETHING USEFUL TO ADD DELAY
        INR     L
        JRNZ    INIT1           ;LOOP TAKES ABOUT 4 MILLISECONDS
;
;       INITIALIZE THE Z-80 FOR INTERRUPT MODE #2
;
        MOV     A,H
        STAI                    ;LOAD I REG WITH MSB OF VECTOR TABLE
        IM2                     ; AND SELECT INTERRUPT MODE 2
;
        CALL    CLRSCN          ;FILL THE CRT MEMORY WITH BLANKS
;
;       STORE ANY NON-ZERO VALUES FOR VARIABLES IN MEMORY
;
        LXI     H,INTAB         ;POINT TO DEFAULT VARIABLE TABLE
INIT2:  MVI     B,0
        MOV     C,M             ;BC=DATA BLOCK BYTE COUNT
        INX     H
        MOV     E,M             ;DE=DESTINATION FOR DATA
        INX     H
        MOV     D,M
        INX     H
        LDIR                    ;COPY DATA @ HL TO VARIABLES @ DE
        BIT     7,M
        JRZ     INIT2           ;LOOP AGAIN IF NOT AT END OF TABLE
;
;       INITIALIZE THE PROGRAMMABLE I/O DEVICES
;
        INX     H               ;POINT TO I/O INIT DATA TABLE
INIT3:  MOV     B,M             ;B=INIT LOOP BYTE COUNT
        INX     H
        MOV     C,M             ;C=DEVICE CONTROL PORT #
        INX     H
        OUTIR                   ;SEND DATA @ HL TO PORT @ C
        BIT     7,M             ;TEST FOR TABLE END MARKER
        JRZ     INIT3           ;LOOP AGAIN IF NOT AT END
;
;       DETERMINE IF CONSOLE I/O CONFIGURATION WILL BE FOR THE
;       ON-BOARD CRT AND KEYBOARD OR AN EXTERNAL SERIAL TERMINAL.
;
        INP     A               ;TEST SIO READ REGISTER 2 TO DETERMINE
        CPI     00000110B       ; IF THE SIO IS INSTALLED IN THE BOARD
        JRNZ    PARALL          ;SKIP CONFIGURATION TEST IF NO SIO
        IN      KBDDAT          ;MAKE SURE KBD PIO 'READY' IS RESET
        MVI     B,00010000B     ;B=RESET SIO EXTERNAL STATUS COMMAND
DECIDE: OUTP    B               ;TEST FOR ARRIVAL OF A SERIAL
        INP     A               ; INPUT CHARACTER START BIT
        BIT     4,A
        JRNZ    BAUD            ;EXIT LOOP IF START BIT DETECTED
        IN      BITDAT
        BIT     3,A             ;TEST FOR DATA READY STROBE FROM
        JRNZ    DECIDE          ; PARALLEL KBD AND LOOP IF INACTIVE
PARALL: IN      KBDDAT          ;DISCARD FIRST KEYBOARD CHARACTER
        MVI     A,10000011B
        OUT     KBDCTL          ;ENABLE INTERRUPTS FROM KBD PIO
        JMPR    SIGNON          ;GO SIGNON WITH BULIT-IN CONSOLE I/O
;
;
;       AUTOMATIC BAUD RATE SETTING ROUTINE FOR SIO
;
BAUD:   XRA     A
BAUD1:  OUTP    B
        INP     D               ;READ SIO STATUS REGISTER
        BIT     4,D             ;TEST THE SYNC/HUNT BIT
        JRZ     BAUD1           ;LOOP UNTIL IT CHANGES STATE
BAUD2:  INR     A
        OUTP    B               ;RESET REGISTER #0 FLAGS AGAIN
        INP     D               ; AND LOOP TIMING THE SYNC/HUNT BIT
        BIT     4,D
        JRNZ    BAUD2           ;REPEAT UNTIL BIT CHANGES AGAIN
        LXI     H,RATES-1
BAUD3:  INX     H               ;INDEX INTO BAUD RATE TABLE
        RAL                     ; USING COUNT DERRIVED IN A
        JRNC    BAUD3
        MOV     A,M             ;GET BAUD RATE CONTROL BYTE FROM
        OUT     BAUDB           ; TABLE AND OUTPUT TO COM-8116 TIMER
        CALL    SIOIN           ;DISARD FIRST SERIAL INPUT CHARACTER
        MVI     A,1
        OUT     SIOCPB          ;RE-PROGRAM SIO B TO GENERATE
        MVI     A,00011100B     ; INTERRUPTS ON RECIEVED DATA,
        OUT     SIOCPB          ; PARITY DOES NOT AFFECT VECTOR
        LXI     H,SIOOUT
        SHLD    CONOUT+1        ;RE-DIRECT CONSOLE OUTPUT TO SIO
;
;       PRINT SIGNON MESSAGE
;
SIGNON: EI
        CALL    PNEXT
        .BYTE   CR,LF
        .ASCII  '... System Monitor 3.3 ...'
        .BYTE   CR,LF
        .BYTE   EOT
        JMP     WARM            ;GO ENTER MONITOR
;
;
;
;       BAUD RATE CONSTANTS FOR COM 8116 BAUD RATE GENERATOR
;
RATES:  .BYTE   0101B           ;   300 BAUD
        .BYTE   0110B           ;   600 BAUD
        .BYTE   0111B           ;  1200 BAUD
        .BYTE   1010B           ;  2400 BAUD
        .BYTE   1100B           ;  4800 BAUD
        .BYTE   1110B           ;  9600 BAUD
        .BYTE   1111B           ; 19200 BAUD
        .BYTE   1111B           ; 19200 BAUD
;
;
INTAB   =       .               ;INITIALIZATION DATA TABLES
;
;
;       INITIALIZE THE Z-80 'I' REGISTER INTERRUPT VECTOR TABLE
;
        .BYTE   2
        .WORD   SYSVEC+2
        .WORD   KEYSRV          ;PARALLEL KEYBOARD INTERRUPT VECTOR
;
        .BYTE   2
        .WORD   CTCVEC+6
        .WORD   TIMER           ;ONE SECOND TIMER INTERRUPT VECTOR
;
        .BYTE   4
        .WORD   SIOVEC+4
        .WORD   SIOINT          ;SIO RECIEVE INTERRUPT VECTOR
        .WORD   SIOERR          ;SIO PARITY, OVERRUN & FRAMING ERROR
;
;       INITIALIZE DISK I/O DRIVER VARIABLES
;
        .BYTE   8
        .WORD   UNIT
        .BYTE   255             ;FLAG ALL DRIVES AS DE-SELECTED
        .BYTE   255,255,255,255 ;CLEAR HEAD POSITION TABLE
        .BYTE   00000000B       ;SELECT FASTEST SEEK SPEED
        .BYTE   128             ;SELECT 128 BYTE SECTOR LENGTH
        .BYTE   30              ;SET MOTOR TURN-OFF TIMER
;
;       INITIALIZE THE CRT DISPLAY CURSOR
;
        .BYTE   2
        .WORD   CHRSAV
        .BYTE   ' '
        .BYTE   '_'             ;USE NON-BLINKING UNDERSCORE
;
;       SET DEFAULT 'SOFTWARE' INTERRUPT VECTORS
;
        .BYTE   6
        .WORD   TIKVEC
        .WORD   DSKTMR          ;POINT 'TIKVEC' TO DISK MOTOR TIMER
        .WORD   STASH           ;POINT 'PINVEC' TO FIFO INPUT ROUTINE
        .WORD   STASH           ;POINT 'SINVEC' TO FIFO INPUT ROUTINE
;
;       SET FREE MEMORY POINTER
;
        .BYTE   2
        .WORD   FREPTR
        .WORD   ROMEND          ;POINT TO FIRST LOCATION AFTER MONITOR
;
;
        .BYTE   -1              ;END OF VARIABLE INIT TABLE
;
;
;
BAUDA   =       00H             ;CHANEL A BAUD RATE GENETATOR
SIO     =       04H             ;DUAL SERIAL I/O
GENPIO  =       08H             ;GENERAL PURPOSE PARALLEL I/O
BAUDB   =       0CH             ;CHANEL B BAUD RATE GENERATOR
WD1771  =       10H             ;WESTERN DIGITAL DISK CONTROLLER
SCROLL  =       14H             ;CRT SCROLL MEMORY SCROLL REGISTER
CTC     =       18H             ;QUAD COUNTER/TIMER CIRCUIT
SYSPIO  =       1CH             ;SYSTEM PARALLEL I/O
;
;       INITIALIZE SYSTEM PIO FOR USE AS BANK-SWITCH,
;       DISK DRIVE SELECT AND PARALLEL KEYBOARD INPUT
;
BITDAT  =       SYSPIO+0
BITCTL  =       SYSPIO+1
KBDDAT  =       SYSPIO+2
KBDCTL  =       SYSPIO+3
;
        .BYTE   3,BITCTL
        .BYTE   11001111B       ;PUT SYSTEM PIO IN BIT MODE
        .BYTE   00011000B       ;MAKE BITS 4 AND 3 BE INPUTS
        .BYTE   01000000B       ;DISABLE INTERRUPTS
;
        .BYTE   1,BITDAT
        .BYTE   00000000B       ;DE-SELECT ROMS, ENABLE DRIVE 0
;
        .BYTE   2,KBDCTL
        .BYTE   01001111B       ;PUT KEYBOARD PORT IN INPUT MODE
        .BYTE   SYSVEC+2        ;LOAD KEYBOARD INTERRUPT VECTOR
;
;
;       INITIALIZE CHANELS 2 AND 3 OF THE CTC
;       TO GENERATE ONE SECOND INTERRUPTS FROM CTC3
;
CTC0    =       CTC+0           ;CTC CHANEL 0
CTC1    =       CTC+1           ;CTC CHANEL 1
CTC2    =       CTC+2           ;CTC CHANEL 2
CTC3    =       CTC+3           ;CTC CHANEL 3
;
        .BYTE   1,CTC0
        .BYTE   CTCVEC          ;BASE INTERRUPT VECTOR FOR CTC
;
        .BYTE   2,CTC2
        .BYTE   00100111B       ;PUT CTC2 IN TIMER MODE
        .BYTE   105             ;CTC2 PERIOD=105*256*400 NANOSECONDS
;
        .BYTE   2,CTC3
        .BYTE   11000111B       ;PUT CTC3 IN COUNTER MODE
        .BYTE   93              ;CTC3 PERIOD=999936 MICROSECONDS
;
;
;       INITIALIZE SIO CHANEL B FOR ASYNCHRONOUS SERIAL
;       INTERFACE TO PRINTER OR TERMINAL
;
SIODPA  =       SIO+0           ;SIO DATA PORT A
SIODPB  =       SIO+1           ;SIO DATA PORT B
SIOCPA  =       SIO+2           ;SIO CONTROL/STATUS PORT A
SIOCPB  =       SIO+3           ;SIO CONTROL/STATUS PORT B
;
        .BYTE   1,BAUDB
        .BYTE   0101B           ;SET COM 8116 FOR 300 BAUD DEFAULT
;
        .BYTE   11,SIOCPB
        .BYTE   4               ;SELECT REGISTER #4
        .BYTE   01000101B       ;16X CLOCK, 1 STOP BIT, ODD PARITY
        .BYTE   1               ;SELECT REGISTER #1
        .BYTE   00000100B       ;STATUS AFFECTS VECTOR, NO INTERRUPTS
        .BYTE   3               ;SELECT REGISTER #3
        .BYTE   01000001B       ;7 BITS/RX CHARACTER
        .BYTE   5               ;SELECT REGISTER #5
        .BYTE   10101010B       ;7 BITS/TX CHARACTER, ASSERT DTR
        .BYTE   2               ;SELECT REGISTER #2
        .BYTE   SIOVEC          ;LOAD INTERRUPT VECTOR BASE
        .BYTE   2               ;SELECT READ REG #2 FOR SIO TEST
;
        .BYTE   -1              ;END-OF-TABLE
;
;       INIT DONE
;
;
;
;********************************************************
;*                                                      *
;*      BASIC HEX MONITOR FOR Z-80 PROCESSORS           *
;*                                                      *
;********************************************************
;
;
;
;
PROMPT: CALL    PNEXT
        .BYTE   CR,LF
        .ASCII  '* '
        .BYTE   EOT
        LXI     H,LINBUF
        MVI     C,32
        CALL    GETLIN          ;INPUT A BUFERED CONSOLE LINE
        JRC     WHAT            ;PRINT 'WHAT ?' IF INPUT ERROR
;
        XRA     A
        STA     ESCFLG
        CALL    CRLFS
        LDA     LINBUF          ;GET FIRST CHARACTER IN LINE
        CPI     CR
        JRZ     PROMPT          ;JUMP IF A NULL LINE
        LXI     H,CMDTAB        ;SEARCH FOR A MATCHING CHARACTER
        LXI     B,CMDSIZ/3      ; IN COMMAND SEARCH TABLE
        CALL    SEARCH
        JRNZ    WHAT            ;TRY AGAIN IF SEACRH FAILS
        PUSH    B
        LXI     Y,LINBUF+1
        CALL    PARAMS          ;INPUT NUMERIC PARAMETERS FROM
        POP     X               ; LINE BUFFER AND TEST IF ERROR
        JRC     WHAT
        LHLD    PARAM1
        LDED    PARAM2
        LBCD    PARAM3
        CALL    CALLX           ;CALL SUBROUTINE @ IX
        JRNC    PROMPT          ;GO BACK TO PROMPT IF NO ERRORS
;
WHAT:   CALL    PNEXT
        .ASCII  ' what ?'
        .BYTE   'G'-64          ;SAY 'what ?' AND BEEP THE BELL
        .BYTE   EOT
        JMPR    PROMPT
;
;
CALLX:  PCIX                    ;CALL SUBROUTINE @ IX
;
;
;
CMDTAB: .BYTE   'R'
        .BYTE   'O'
        .BYTE   'I'
        .BYTE   'G'
        .BYTE   'T'
        .BYTE   'F'
        .BYTE   'M'
        .BYTE   'C'
        .BYTE   'B'
        .BYTE   'D'
        .BYTE   'S'
;
        .WORD   SWITCH          ;SWITCH CONSOLE OUTPUT VECTOR
        .WORD   MEMDMP          ;DUMP MEMORY IN HEX/ASCII
        .WORD   BOOT            ;BOOT UP CP/M
        .WORD   BLOCK           ;MEMORY BLOCK MOVE
        .WORD   VIEW            ;MEMORY EXAMINE/CHANGE
        .WORD   FILL            ;FILL MEMORY
        .WORD   TEST            ;RAM DIAGNOSTIC
        .WORD   GOTO            ;JUMP TO MEMORY LOCATION
        .WORD   INCMD           ;READ FROM INPUT PORT
        .WORD   OUTCMD          ;WRITE TO OUTPUT PORT
        .WORD   DSKCMD          ;DISPLAY DISK SECTOR DATA
;
CMDSIZ  =       .-CMDTAB
;
;
;********************************************************
;*                                                      *
;*      MONITOR COMMAND ACTION ROUTINES PACKAGE         *
;*                                                      *
;********************************************************
;
;
;
;
;
;       -- DISK BOOT LOADER COMMAND --
;
BOOT:   MVI     C,0             ;SELECT DRIVE 0 FOR BOOT LOAD
        CALL    SELECT
        JRNZ    DSKERR
        CALL    HOME            ;HOME HEAD TO TRACK 0
        JRNZ    DSKERR          ;ERROR IF NOT READY OR AT TR0
        LXI     H,0080H         ;POINT TO CP/M READ BUFFER
        MVI     C,1             ;SELECT SECTOR 1
        CALL    READ            ;READ TRACK 0/ SECTOR 1
        JRNZ    DSKERR
        POP     PSW             ;CLEAN UP STACK
        JMP     0080H           ;GO EXECUTE LOADER
;
;
;       -- DISK SECTOR READ COMMAND --
;
DSKCMD: CPI     3               ;CHECK PARAMETER COUNT
        STC
        RNZ
        MOV     C,L             ;USE FIRST ARG AS UNIT #
        CALL    SELECT
        JRNZ    DSKERR
        LXI     H,PARAM2
        MOV     C,M             ;USE SECOND ARG AS TRACK #
        CALL    SEEK
        JRNZ    DSKERR
        LXI     H,PARAM3
        MOV     C,M             ;USE THIRD ARG AS SECTOR #
        LXI     H,0080H
        CALL    READ
        SET     0,A             ;MARK ERROR BYTE AS DUE TO READ
        JRNZ    DSKERR
        LXI     H,0080H
        LXI     D,8
        JMP     DUMP            ;DUMP DISK READ BUFFER AND RETURN
;
;
DSKERR: MOV     C,A             ;SAVE 1771 STATUS
        CALL    PNEXT
        .ASCII  'disk error '
        .BYTE   EOT
        MVI     B,8             ;PRINT 1771 ERROR BYTE IN BINARY
DSKR2:  XRA     A
        RALR    C
        ACI     '0'             ;TRANSFORM A INTO ASCII '1' OR '0'
        CALL    OUTPUT
        DJNZ    DSKR2           ;REPEAT FOR 8 BITS
        ORA     A
        RET
;
;
;
;       -- MEMORY DUMP COMMAND --
;
MEMDMP: DCR     A               ;CHECK PARAMETER COUNT
        JRZ     MDMP2   
        DCR     A
        JRZ     MDMP3   
MDMP1:  LHLD    LAST
MDMP2:  LXI     D,16
        JMPR    MDMP3B
;
MDMP3:  XCHG
        DSBC    D               ;DERRIVE BYTE COUNT FOR DUMP RANGE
        MVI     B,4
MDMP3A: SRLR    H               ;DIVIDE BYTE COUNT BY 16
        RARR    L
        DJNZ    MDMP3A
        INX     H
        XCHG
MDMP3B: CALL    DUMP            ;DUMP DE*16 BYTES STRTING AT HL
        SHLD    LAST
        RET
;
;
DUMP:   PUSH    H               ;SAVE STARTING ADDRESS
        CALL    PUT4HS          ;PRINT STARTING ADDRESS IN HEX
        CALL    SPACE
        MVI     B,16
DUMP2:  MOV     A,M             ;GET A DATA BYTE @ HL
        INX     H
        CALL    PUT2HS          ;PRINT THE DATA IN HEX
        DJNZ    DUMP2           ;REPEAT 16 TIMES
        POP     H               ;RESTORE STARTING ADDRESS
        MVI     B,16
DUMP3:  MOV     A,M             ;GET BACK DATA BYTE @ HL
        INX     H
        RES     7,A
        CPI     20H
        JRC     DUMP4   
        CPI     7FH
        JRC     DUMP5   
DUMP4:  MVI     A,'.'           ;PRINT A DOT IF DATA < 20 OR > 7F
DUMP5:  CALL    OUTPUT          ;PRINT ASCII CHARACTER IN A
        DJNZ    DUMP3
        CALL    CRLFS
        RNZ                     ;EXIT IF ESCAPE REQUEST IS INDICATED
        DCX     D
        MOV     A,D
        ORA     E
        JRNZ    DUMP    
        RET
;
;
;
;
;       -- MEMORY EXAMINE COMMAND --
;
VIEW:   CALL    MDATA
        CALL    ECHO
        CPI     CR
        JRZ     VIEW4   
        CPI     '-'
        JRZ     VIEW5   
VIEW2:  CALL    ASCHEX
        CMC
        RNC
        RLC
        RLC
        RLC
        RLC
        MOV     C,A
        CALL    ECHO
        CALL    ASCHEX
        CMC
        RNC
        ORA     C
VIEW3:  MOV     M,A
        CALL    CHECK
VIEW4:  INX     H
        INX     H
VIEW5:  DCX     H
        JMPR    VIEW
;
;
;
;       -- JUMP TO MEMORY LOCATION COMMAND --
;
GOTO:   DCR     A               ;CHECK PARAMETER COUNT
        STC
        RNZ
        PUSH    H
        POP     X
        CALL    CALLX           ;CALL ADDRESS PASSED IN HL
        ORA     A
        RET                     ;RETURN IF WE GET BACK AGAIN
;
;
;
;       -- MEMORY READ/WRITE DIAGNOSTIC COMMAND --
;
TEST:   CPI     2               ;CHECK PARAMETER COUNT
        STC
        RNZ
        INX     D
        MOV     E,D             ;GET ENDING PAGE ADDRESS INTO E
        MOV     D,H             ;GET STARTING PAGE ADDRESS INTO D
        MVI     B,0             ;INITIALIZE PASS COUNTER
TEST1:  MOV     H,D             ;POINT HL TO START OF BLOCK
        MVI     L,0
TEST2:  MOV     A,L
        XRA     H               ;GENERATE TEST BYTE
        XRA     B
        MOV     M,A             ;STORE BYTE IN RAM
        INX     H
        MOV     A,H
        CMP     E               ;CHECK FOR END OF TEST BLOCK
        JRNZ    TEST2
;                               NOW READ BACK EACH BYTE & COMPARE
        MOV     H,D
        MVI     L,0             ;POINT HL BACK TO START
TEST3:  MOV     A,L
        XRA     H               ;RE-GENERATE TEST BYTE DATA
        XRA     B
        CALL    CHECK           ;VERIFY MEMORY DATA STILL GOOD
        RNZ                     ;EXIT IF ESCAPE REQUEST IS INDICATED
        INX     H               ; ELSE GO ON TO NEXT BYTE
        MOV     A,H
        CMP     E               ;CHECK FOR END OF BLOCK
        JRNZ    TEST3
        INR     B               ;BUMP PASS COUNT
        MVI     A,'+'
        CALL    OUTPUT          ;PRINT '+' AND ALLOW FOR EXIT
        JRZ     TEST1           ;DO ANOTHER PASS IF NO ESCAPE
        RET
;
;
;
CHECK:  CMP     M
        RZ                      ;RETURN IF M=A
        PUSH    PSW
        CALL    MDATA           ;PRINT WHAT WAS ACTUALLY READ
        CALL    PNEXT
        .ASCII  'should='
        .BYTE   EOT
        POP     PSW
        JMP     PUT2HS          ;PRINT WHAT SHOULD HAVE BEEN READ
;
;
MDATA:  CALL    CRLFS
        CALL    PUT4HS
        MOV     A,M
        JMP     PUT2HS
;
;
;
;       -- FILL MEMORY WITH CONSTANT COMMAND --
;
FILL:   CPI     3               ;CHECK IF PARAMETER COUNT=3
        STC
        RNZ
FILL1:  MOV     M,C
        PUSH    H
        ORA     A
        DSBC    D               ;COMPARE HL TO END ADDRESS IN DE
        POP     H
        INX     H               ;ADVANCE POINTER AFTER COMPARISON
        JRC     FILL1   
        RET
;
;
;
;
;       -- MEMORY BLOCK MOVE COMMAND --
;
BLOCK:  CPI     3               ;CHECK IF PARAMETER COUNT=3
        STC
        RNZ
        CALL    BLOCAD
        MOV     A,C
        ORA     B
        RZ                      ;EXIT NOW IF BC=0
        LDIR
        RET
;
;
;
BLOCAD: XCHG
        ORA     A               ;CLEAR CARRY
        DSBC    D               ;GET DIFFRENCE BETWEEN
        XCHG                    ; HL & DE FOR BYTE COUNT
        PUSH    D
        PUSH    B
        POP     D               ;GET OLD BC INTO DE
        POP     B
        INX     B               ;GET COUNT+1 INTO BC
        RET
;
;
;
;
;       -- READ FROM INPUT PORT COMMAND --
;
INCMD:  DCR     A               ;CHECK IF PARAMETER COUNT=1
        STC
        RNZ
        MOV     C,L             ;POINT C TO INPUT PORT
IN1:    CALL    CRLFS
        MOV     A,C
        CALL    PUT2HS
        INP     A
        CALL    PUT2HS
        CALL    ECHO
        CPI     CR
        JRZ     IN2
        CPI     '-'
        JRZ     IN3
        ORA     A
        RET
;
IN2:    INR     C
        INR     C
IN3:    DCR     C
        JMPR    IN1
;
;
;
;       -- WRITE TO OUTPUT PORT COMMAND --
;
OUTCMD: CPI     2               ;CHECK IF PARAMETER COUNT=2
        STC
        RNZ
        MOV     C,L             ;POINT C TO OUTPUT PORT
        OUTP    E               ;OUTPUT DATA PASSED IN E
        ORA     A
        RET
;
;
;       -- SWITCH CONSOLE OUTPUT DEVICE COMMAND --
;
SWITCH: LXI     H,COFLAG
        INR     M               ;TOGGLE CONSOLE OUTPUT TYPE FLAG
        BIT     0,M
        LXI     H,SIOOUT
        JRZ     SWIT2           ;JUMP IF ZERO TO ONE TRANSITION
        LXI     H,CRTOUT
SWIT2:  SHLD    CONOUT+1        ;STORE NEW CONSOLE OUTPUT ADDRESS
        RET
;
;
;********************************************************
;*                                                      *
;*      CONSOLE I/O PACKAGE AND UTILITY ROUTINES        *
;*                                                      *
;********************************************************
;
;
;
GETLIN: MOV     B,C             ;SAVE MAX LINE LENGTH PARAMETER IN B
GLIN1:  CALL    ECHO            ;GET A CHARACTER FROM THE CONSOLE
        CPI     CR              ;CHECK FOR CARRIAGE RETURN
        JRZ     GLIN2   
        CPI     'H'-64          ;CHECK FOR CTL-H BACKSPACE
        JRZ     GLIN4   
        CPI     ' '
        RC                      ;OTHER CONTROL CHARACTERS ARE ILLEGAL
        MOV     M,A
        INX     H               ;STORE CHARACTER IN BUFFER
        DCR     C
        JRNZ    GLIN1           ;GET ANOTHER IF THERE'S MORE ROOM
        STC
        RET                     ;RETURN WITH CARRY=1 IF TOO
                                ; MANY CHARACTERS ARE ENTERED
GLIN2:  MOV     M,A             ;PUT CARRIAGE RETURN ON END OF LINE
        RET                     ;RETURN WITH CARRY BIT=0
;
GLIN4:  DCX     H               ;DELETE LAST CHARACTER FROM BUFFER
        CALL    PNEXT
        .BYTE   ' ','H'-64      ;PRINT A SPACE TO OVERWRITE THE
        .BYTE   EOT             ; LAST CHARACTER, THEN DO A BACKSPACE
        INR     C
        MOV     A,B             ;MAKE SURE YOU'RE NOT TRYING TO
        SUB     C               ; BACKSPACE PAST THE START OF THE LINE
        JRNC    GLIN1
        RET
;
;
;
SEARCH: CCIR                    ;SEARCH TABLE @HL FOR MATCH WITH A
        RNZ                     ;EXIT NOW IF SEARCH FAILS
        DAD     B
        DAD     B               ;ADD RESIDUE FROM CCIR BYTE COUNT
        DAD     B               ; TO HL 3 TIMES TO GET POINTER
        MOV     C,M             ; TO ADDRESS PART OF TABLE ENTRY
        INX     H
        MOV     B,M
        RET                     ;EXIT WITH Z=1 TO INDICATE MATCH
;
;
;
;
PARAMS: LXI     B,0
        MOV     A,0(Y)
        CPI     CR              ;CHECK IF LINE TERMINATES
        JRNZ    PARA2           ; IMMEDIATELY WITH A RETURN
        XRA     A
        RET                     ;RETURN WITH PARAM COUNT=0 IF SO
;
PARA1:  INR     C
        INR     C
        BIT     3,C
        STC
        RNZ                     ;ERROR IF > 4 NUMBERS ENTERED
PARA2:  PUSH    B               ;SAVE PARAMETER COUNT
        CALL    GETHEX          ;READ A NUMBER FROM LINE BUFFER
        POP     B
PARA4:  RC                      ;ERROR IF RESULT OVER 16 BITS
        LXI     X,PARAM1        ;POINT TO PARAMETER STORAGE AREA
        DADX    B               ;ADD PARAMETER COUNT IN BC
        MOV     0(X),L
        MOV     1(X),H          ;STORE DATA RETURNED FROM 'GETHEX'
        CPI     ' '
        JRZ     PARA1           ;GET ANOTHER ITEM IF SPACE
        CPI     ','
        JRZ     PARA1           ;GET ANOTHER ITEM IF COMMA
        CPI     CR
        STC                     ;ELSE CHECK FOR CARRIAGE RETURN
        RNZ                     ; AND EXIT WITH C=1 IF NOT
PAREND: MOV     A,C
        SRLR    A               ;A=COUNT OF NUMBERS ENTERED
        INR     A
        RET
;
;       GETHEX CONVERTS ASCII TO BINARY AND DOES
;       HIGH LIMIT CHECKS TO LESS THAN 17 BITS.
;       CARRY SET ON ILLEGAL CONVERSION RESULT
;       TERMINATING CHARACTER RETURNS IN A.
;       HL RETURNS WITH 16 BIT BINARY INTEGER
;
GETHEX: LXI     H,0
        JMPR    GNUM3
;
GNUM1:  MVI     B,4
GNUM2:  DAD     H               ;MULTIPLY RESULT BY 16
        RC                      ;RETURN IF IT OVERFLOWS 16 BITS
        DJNZ    GNUM2
        MOV     E,A             ;APPEND NEW LOW ORDER DIGIT
        MVI     D,0             ;AND GET RESULT BACK INTO DE
        DAD     D
        RC                      ;RETURN IF OVERFLOW
GNUM3:  MOV     A,0(Y)          ;GET A CHARACTER FROM LINE INPUT
        INX     Y               ; BUFFER @ IY AND BUMP IY
        MOV     C,A
        CALL    ASCHEX          ;CONVERT ASCII TO NUMERIC
        JRNC    GNUM1
        MOV     A,C
        ORA     A
        RET
;
;
ASCHEX: SUI     '0'
        RC
        CPI     10
        CMC
        RNC
        SUI     7
        CPI     10
        RC
        CPI     16
        CMC
        RET
;
;
;
PUT4HS: MOV     A,H
        CALL    PUT2HX
        MOV     A,L
PUT2HS: CALL    PUT2HX
        JMP     SPACE
;
;
PUT2HX: PUSH    PSW
        RAR
        RAR
        RAR
        RAR
        CALL    PUTNIB
        POP     PSW
PUTNIB: ANI     00001111B
        ADI     90H
        DAA
        ACI     40H
        DAA
        JMP     OUTPUT
;
;
;       PMSG PRINTS THE STRING OF ASCII CHARACTERS
;       POINTED TO BY THE RELATIVE ADDRESS IN DE
;       UNTIL AN EOT IS ENCOUNTERED IN THE STRING.
;
EOT     =       04H
CR      =       0DH
LF      =       0AH
;
;
PNEXT:  XTHL
        CALL    PMSG
        XTHL
        RET
;
PMSG:   MOV     A,M
        INX     H
        CPI     EOT
        RZ
        CALL    OUTPUT
        JMPR    PMSG
;
;
;       CRLFS OUTPUTS A RETURN-LINEFEED-SPACE
;       TO THE CONSOLE DEVICE
;
CRLFS:  CALL    PNEXT
        .BYTE   CR,LF,EOT
SPACE:  MVI     A,' '
        JMP     OUTPUT
;
;
;
;       ECHO INPUTS ONE CHARACTER FROM THE CONSOLE
;       DEVICE, PRINTS IT ON THE CONSOLE OUTPUT AND
;       THEN RETURNS IT IN REGISTER A WITH BIT 7 RESET
;
;       OUTPUT PRINTS THE CHARACTER IN REGISTER A ON
;       THE CONSOLE OUTPUT DEVICE AND THEN DOES A CHECK
;       FOR CONSOLE INPUT TO FREEZE OR ABORT OUTPUT.
;
;
ECHO:   CALL    CONIN           ;INPUT A CHARACTER AND ECHO IT
        PUSH    PSW
        CALL    CONOUT
        POP     PSW
        CPI     'Z'+1
        RC
        SUI     32              ;CONVERT UPPER CASE TO LOWER CASE
        RET
;
;
;
OUTPUT: CALL    CONOUT
        CALL    CONST           ;SEE IF CONSOLE INPUT IS PENDING
        JRZ     OUTP2   
        CALL    CONIN
        CPI     CR              ;SEE IF CARRIAGE RETURN WAS TYPED
        JRZ     OUTP1   
        CALL    CONIN           ;WAIT FOR ANOTHER INPUT CHARACTER
        JMPR    OUTP2           ; THEN RETURN TO CALLING ROUTINE
;
OUTP1:  STA     ESCFLG          ;SET ESCAPE FLAG TO NON-ZERO VALUE
OUTP2:  LDA     ESCFLG
        ORA     A               ;RETURN CURRENT STATUS OF ESCAPE
        RET                     ; FLAG TO CALLING ROUTINE
;
;
;
;
;********************************************************
;*                                                      *
;*      INTERRUPT SERVICE ROUTINES FOR KEYBOARD         *
;*      INPUT AND REAL-TIME CLOCK FUNCTIONS             *
;*                                                      *
;********************************************************
;
;
;
;
KBDST:  LDA     FIFCNT          ;GET INPUT FIFO BYTE COUNT
        ORA     A               ;TEST IF EQUAL ZERO
        RZ                      ;EXIT WITH A=0 IF QUEUE IS EMPTY
        MVI     A,255
        RET                     ;ELSE SET A=255 TO INDICATE DATA READY
;
;
;
KBDIN:  CALL    KBDST
        JRZ     KBDIN           ;LOOP UNTIL KEYBOARD INPUT READY
        PUSH    H
        CALL    REMOVE          ;GET CHARACTER FROM INPUT QUEUE
        POP     H
        RET
;
;
;
;
;
STASH:  LXI     H,LOCK          ;POINT TO SHIFT LOCK VARIABLES
        CMP     M               ;TEST IF A=SHIFT LOCK CHARACTER
        INX     H               ; THEN POINT TO LOCK FLAG
        JRNZ    STASH2          ;JUMP IF NOT THE SHIFT CHARACTER
        INR     M               ; ELSE COMPLIMENT THE SHIFT LOCK
        RET                     ; AND EXIT NOW
;
STASH2: BIT     0,M             ;TEST THE SHIFT LOCK FLAG
        JRZ     STASH3          ;JUMP IF SHIFT LOCK NOT SET
        CPI     40H             ;ELSE CHECK FOR SHIFTABLE CHARACTER
        JRC     STASH3          ; AND JUMP IF NOT EQUAL OR GREATER
        CPI     7FH             ; THAN '@' AND LESS THAN RUBOUT
        JRNC    STASH3
        XRI     00100000B       ;ELSE TOGGLE BIT 5 OF THE CHARACTER
STASH3: MOV     C,A
        LXI     H,FIFCNT        ;BUMP INPUT FIFO CHARACTER COUNT
        MOV     A,M
        INR     A
        CPI     16
        RNC                     ;EXIT NOW IF FIFO IS FULL
        MOV     M,A             ; ELSE INCREMENT FIFO COUNT
        LXI     H,FIFIN         ;POINT HL TO FIFO INPUT OFFSET
        CALL    INDEX
        MOV     M,C             ;STORE CHARACTER IN FIFO @ HL
        RET
;
;
;
;
REMOVE: LXI     H,FIFCNT
        DCR     M
        LXI     H,FIFOUT        ;POINT HL TO FIFO OUTPUT OFFSET
INDEX:  MOV     A,M
        INR     A
        ANI     00001111B       ;INCREMENT FIFO POINTER
        MOV     M,A             ; MODULO 16 AND REPLACE
        LXI     H,FIFO
        ADD     L               ;INDEX INTO FIFO BY OFFSET IN A
        MOV     L,A
        MOV     A,M
        RET
;
;
;       SOFTWARE DISK MOTOR TURN-OFF TIMER ROUTINE
;
DSKTMR: LXI     H,MOTOR         ;DECREMENT DISK TURN-OFF TIMER
        DCR     M
        RNZ                     ;EXIT IF NOT TIMED OUT YET
        IN      BITDAT
        ORI     01000100B       ;DISABLE ALL DRIVE SELECTS AND
        OUT     BITDAT          ; TURN OFF THE SPINDLE MOTORS
        RET
;
;
;       -- INTERRUPT SERVICE ROUTINE FOR PARALLEL KEYBOARD --
;
KEYSRV: SSPD    SPSAVE          ;SAVE USER STACK POINTER AND
        LXI     SP,TMPSTK+32    ; SWITCH TO LOCAL STACK
        PUSH    H
        PUSH    D
        PUSH    B
        PUSH    PSW             ;SAVE MACHINE STATE
        IN      KBDDAT          ;READ KEYBOARD INPUT PORT
        CMA
        LHLD    PINVEC          ;GET KBD INTERRUPT ROUTINE VECTOR
        JMPR    DSPTCH          ; AND JUMP TO DISPATCH POINT
;
;
;
;       -- INTERRUPT SERVICE ROUTINE FOR ONE SECOND TIMER --
;
TIMER:  SSPD    SPSAVE          ;SAVE USER STACK POINTER AND
        LXI     SP,TMPSTK+32    ; SWITCH TO LOCAL STACK
        PUSH    H
        PUSH    D
        PUSH    B
        PUSH    PSW
        LHLD    TIKVEC          ;GET CLOCK INTERRUPT ROUTINE VECTOR
        JMPR    DSPTCH          ; AND JUMP TO DISPATCH POINT
;
;
;
;       -- SERIAL INPUT INTERRUPT SERVICE ROUTINE FOR SIO --
;
SIOINT: SSPD    SPSAVE          ;SAVE USER STACK POINTER AND
        LXI     SP,TMPSTK+32    ; SWITCH TO LOCAL STACK
        PUSH    H
        PUSH    D
        PUSH    B
        PUSH    PSW             ;SAVE MACHINE STATE
        IN      SIODPB          ;READ SIO DATA INPUT PORT
        ANI     01111111B
        LHLD    SINVEC          ;GET SERIAL INPUT ROUTINE VECTOR
DSPTCH: CALL    CALLHL          ;CALL SUBROUTINE ADDRESSED BY H
        POP     PSW
        POP     B
        POP     D
        POP     H
        LSPD    SPSAVE
        EI                      ;RE-ENABLE INTERRUPTS AND RETURN
        RETI
;
;
;       -- ERROR INTERRUPT SERVICE ROUTINE FOR SIO --
;
;       ARRIVE HERE IF RECIEVE INTERRUPT FROM FRAMING, OVERRUN
;       AND PARITY ERRORS. (PARITY CAN BE DISABLED)
;
SIOERR: SSPD    SPSAVE          ;SAVE USER STACK POINTER AND
        LXI     SP,TMPSTK+32    ; SWITCH TO LOCAL STACK
        PUSH    PSW
        CALL    SIOIN2          ;CLEAR BAD CHARACTER FROM SIO
        MVI     A,'G'-64
        CALL    SIOXMT          ;OUTPUT A CTL-G AS A WARNING
        POP     PSW
        LSPD    SPSAVE
        EI
        RETI
;
;
CALLHL: PCHL
;
;
;
;       POLLED MODE I/O ROUTINES FOR SIO CHANEL B
;
SIOST:  IN      SIOCPB          ;GET SIO STATUS REGISTER
        ANI     00000001B
        RZ                      ;ACC=0 IF NO DATA AVAILABLE
        MVI     A,255
        RET
;
;
SIOIN:  CALL    SIOST           ;TEST CONSOLE STATUS
        JRZ     SIOIN           ;LOOP UNTIL DATA IS RECIEVED
SIOIN2: MVI     A,00110000B     ;RESET STATUS BITS IN SIO FOR
        OUT     SIOCPB          ; PARITY/OVERRUN/FRAMING ERRORS,
        IN      SIODPB          ; THEN GET THE INPUT CHARACTER
        ANI     01111111B
        RET
;
;
SIOOUT: CPI     ' '             ;TEST FOR CONTROL CHARACTERS
        JRNC    SIOXMT          ;JUMP IF PRINTABLE CHARACTER
        CALL    SIOXMT          ; ELSE SEND CONTROL CHARACTER
        LDA     NULLS           ; AND THEN SEND NULLS AS PADDING
        INR     A               ;GET NULL PAD COUNT AND FIX SO
        JMPR    PAD1            ; THAT COUNT=0 SENDS NO NULLS
;
PAD:    PUSH    PSW
        XRA     A
        CALL    SIOXMT          ;OUTPUT A NULL TO THE SIO
        POP     PSW
PAD1:   DCR     A
        JRNZ    PAD             ;LOOP SENDING NULLS TO SIO
        RET
;
;
SIOXMT: PUSH    PSW
SIOX1:  IN      SIOCPB
        ANI     00000100B       ;TEST TBE STATUS BIT
        JRZ     SIOX1   
        POP     PSW
        OUT     SIODPB          ;OUTPUT DATA TO SIO
        RET
;
;
;
;
;
;********************************************************
;*                                                      *
;*      MEMORY-MAPPED CRT OUTPUT DRIVER                 *
;*                                                      *
;********************************************************
;
;
CRTBAS  =       30H     ;CRTMEM>8, STARTING PAGE # OF CRT SPACE
CRTTOP  =       3CH     ;CRTMEM+3072>8, ENDING PAGE #
;
;
CRTOUT: PUSH    H
        PUSH    D
        PUSH    B
        RES     7,A
        MOV     C,A
        DI                      ;KEEP THE WOLVES AWAY FOR A WHILE
        SSPD    SPSAVE
        LXI     SP,TMPSTK+32    ;POINT SP TO TOP OF LOCAL STACK
        IN      BITDAT
        SET     7,A             ;SELECT ROM/CRT MEMORY BANK
        OUT     BITDAT
;
;       FIRST REMOVE THE OLD CURSOR CHARACTER FROM THE SCREEN
;
        LXI     H,CHRSAV        ;GET CHARACTER NOW OVERLAYED BY CURSOR
        MOV     B,M
        LHLD    CURSOR          ;LOAD HL WITH CURSOR POINTER
        MOV     A,H
        ANI     00001111B       ;A LITTLE INSURANCE THAT HL CAN'T
        ORI     CRTBAS          ; EVER POINT OUTSIDE THE CRT MEMORY
        MOV     H,A
        MOV     M,B             ;REMOVE CURSOR BY RESTORING CHARACTER
;
;       PROCESS CHARACTER PASSED IN C
;
        CALL    OUTCH
;
;       NOW STORE A NEW CURSOR CHARACTER AT THE CURSOR LOCATION
;
        MOV     A,M             ;GET CHARACTER AT NEW CURSOR LOCATION
        STA     CHRSAV          ;SAVE FOR NEXT TIME 'CRTOUT' IS CALLED
        CPI     ' '             ;TEST IF CHARACTER IS A SPACE
        SET     7,A             ;THEN TURN ON BIT 7 TO ENABLE BLINK
        JRNZ    CRT2            ;JUMP IF CHARACTER IS NON-BLANK
        LDA     CSRCHR          ; ELSE GET CHARACTER USED FOR CURSOR
CRT2:   MOV     M,A             ;STORE CHARACTER IN A AS CURSOR MARK
        SHLD    CURSOR          ;SAVE HL AS CURSOR POINTER
;
        LSPD    SPSAVE
        IN      BITDAT
        RES     7,A             ;SWITCH BACK THE LOWER 16K OF RAM
        OUT     BITDAT
        EI                      ;INTERRUPTS ARE SAFE AGAIN
        POP     B
        POP     D
        POP     H
        RET
;
;
;
OUTCH:  LXI     D,LEADIN
        LDAX    D               ;GET LEAD-IN SEQUENCE STATE
        ORA     A
        JNZ     MULTI           ;JUMP IF IN A LEAD-IN SEQUENCE
        MOV     A,C             ; ELSE PROCESS CHARACTER IN C
        CPI     ' '
        JRC     CONTRL          ;JUMP IF A CONTROL CHARACTER
DISPLA: MOV     M,C             ; ELSE STORE DISPLAYABLE CHARACTER
        INX     H               ; AND ADVANCE POINTER TO NEXT COLUMN
        MOV     A,L
        ANI     01111111B       ;EXTRACT COLUMN # FROM HL
        CPI     80
        RC                      ;EXIT IF NOT PAST COLUMN 79
        CALL    RETURN          ; ELSE DO AUTOMATIC CARRIAGE RETURN
        CALL    LFEED           ; AND LINEFEED
        RET
;
;
;
CONTRL: PUSH    H
        LXI     H,CTLTAB        ;SEARCH FOR CONTROL CHARACTER
        LXI     B,CTLSIZ/3      ; HANDLING SUBROUTINE IN TABLE
        CALL    SEARCH
        POP     H
        RNZ                     ;EXIT IF NOT IMPLEMENTED
        PUSH    B
        RET                     ;DO SNEAKY JUMP TO PRESERVE REGISTERS
;
CTLTAB: .BYTE   '_'-64
        .BYTE   '^'-64
        .BYTE   '['-64
        .BYTE   'Z'-64
        .BYTE   'X'-64
        .BYTE   'Q'-64
        .BYTE   'M'-64
        .BYTE   'L'-64
        .BYTE   'K'-64
        .BYTE   'J'-64
        .BYTE   'I'-64
        .BYTE   'H'-64
        .BYTE   'G'-64
;
        .WORD   BELL            ;CTL-G IS THE BELL
        .WORD   BAKSPC          ;CTL-H IS CURSOR LEFT
        .WORD   TAB             ;CTL-I IS TAB
        .WORD   LFEED           ;CTL-J IS CURSOR DOWN
        .WORD   UPCSR           ;CTL-K IS CURSOR UP
        .WORD   FORSPC          ;CTL-L IS CURSOR RIGHT
        .WORD   RETURN          ;CTL-M IS CARRIAGE RETURN
        .WORD   CLREOS          ;CTL-Q IS CLEAR TO END-OF-SCREEN
        .WORD   CLREOL          ;CTL-X IS CLEAR TO END-OF-LINE
        .WORD   CLRSCN          ;CTL-Z IS CLEAR SCREEN
        .WORD   ESCAPE          ;CTL-[ IS ESCAPE
        .WORD   HOMEUP          ;CTL-^ IS HOME UP
        .WORD   STUFF           ;CTL-_ IS DISPLAY CONTROL CHARS
;
CTLSIZ  =       .-CTLTAB
;
;
ESCAPE: MVI     A,1
        STAX    D               ;SET LEAD-IN SEQUENCE STATE
        RET                     ; FOR XY CURSOR POSITIONING MODE
;
;
STUFF:  MVI     A,4
        STAX    D               ;SET LEAD-IN SEQUENCE STATE
        RET                     ; FOR CONTROL CHAR OUTPUT MODE
;
;
BAKSPC: MOV     A,L             ;CHECK FOR LEFT MARGIN
        ANI     01111111B
        RZ                      ;ABORT IF IN LEFTMOST COLUMN
        DCX     H               ;BACK UP CURSOR POINTER
        RET
;
;
FORSPC: MOV     A,L             ;CHECK FOR RIGHTMOST COLUNM
        ANI     01111111B
        CPI     79
        RNC                     ;DO NOTHING IF ALREADY THERE
        INX     H
        RET                     ;ELSE ADVANCE THE CURSOR POINTER
;
;
TAB:    LXI     D,8             ;TABS ARE EVERY 8 COLUMNS
        MOV     A,L             ;GET COLUMN COMPONENT OF
        ANI     01111000B       ; PREVIOUS TAB POSITION
        ADD     E
        CPI     80              ;EXIT IF NEXT TAB COLUMN WOULD
        RNC                     ; BE PAST THE RIGHT MARGIN
        MOV     A,L
        ANI     11111000B       ;ELSE INCREMENT THE CURSOR
        MOV     L,A             ; POINTER FOR REAL
        DAD     D
        RET
;
;
BELL:   IN      BITDAT
        SET     5,A             ;TOGGLE BIT 5 OF SYSTEM PIO TO
        OUT     BITDAT          ; TRIGGER BELL HARDWARE TO SOUND
        RES     5,A
        OUT     BITDAT
        RET
;
;
RETURN: MOV     A,L
        ANI     10000000B
        MOV     L,A             ;MOVE CURSOR POINTER BACK
        RET                     ; TO START OF LINE
;
;
CLRSCN: LXI     H,CRTMEM
        PUSH    H
        LXI     D,CRTMEM+1
        LXI     B,24*128
        MVI     M,' '
        LDIR                    ;FILL CRT MEMORY WITH SPACES
        POP     H               ;POINT TO HOME CURSOR POSITION
        MVI     A,23
        STA     BASE            ;MAKE BASE LINE # BE 23 AND
        OUT     SCROLL          ; STORE IN SCROLL REGISTER
        RET
;
;
CLREOL: PUSH    H               ;SAVE CURSOR POINTER
        MOV     A,L
        ANI     01111111B       ;GET COLUMN # COMPONENT OF
        MOV     C,A             ; CURSOR POINTER INTO C
        MVI     A,80            ;CALCULATE HOW MANY CHARACTERS
        SUB     C               ; REMAIN ON CURRENT LINE
        MOV     B,A
        CALL    CLR             ;CLEAR REST OF LINE @ HL
        POP     H
        RET
;
;
CLREOS: CALL    CLREOL          ;CLEAR REMAINDER OF CURRENT ROW
        PUSH    H
        LDA     BASE
        MOV     C,A             ;COPY BASE SCREEN ROW # TO C
CLRS1:  MOV     A,L
        RAL
        MOV     A,H
        RAL                     ;GET ROW # COMPONENT OF HL INTO A
        ANI     00011111B
        CMP     C               ;SEE IF HL IS AT BOTTOM ROW OF SCREEN
        JRZ     CLRS2           ; AND LEAVE CLEAR LOOP IF SO
        CALL    DNCSR           ;ELSE POINT HL TO NEXT ROW DOWN
        CALL    CLRLIN          ; AND FILL THAT LINE WITH SPACES
        JMPR    CLRS1
;
CLRS2:  POP     H               ;RESTORE ORIGINAL CURSOR POINTER
        RET
;
;
UPCSR:  LXI     D,-128          ;SUBTRACT 1 FROM ROW # COMPONENT
        DAD     D               ; OF CURSOR POINTER IN HL
        MOV     A,H
        CPI     CRTBAS          ;CHECK FOR UNDERFLOW OF POINTER
        RNC
        MVI     H,CRTTOP-1      ;WRAP CURSOR AROUND MODULO 3K
        RET
;
;
DNCSR:  LXI     D,128           ;ADD 1 TO ROW # COMPONENT
        DAD     D               ; OF CURSOR POINTER IN HL
        MOV     A,H
        CPI     CRTTOP          ;CHECK FOR OVERFLOW OF POINTER
        RC
        MVI     H,CRTBAS        ;RESET POINTER MODULO 128*24
        RET
;
;
;
LFEED:  MOV     A,L
        RAL
        MOV     A,H
        RAL                     ;EXTRACT ROW # COMPONENT OF HL
        ANI     00011111B
        MOV     C,A             ;COPY ROW # INTO C FOR SCROLL TEST
        CALL    DNCSR           ;MOVE CURSOR TO NEXT ROW DOWN
        LDA     BASE            ;TEST IF CURSOR WAS ON BOTTOM ROW
        CMP     C               ; OF SCREEN BEFORE MOVING DOWN
        RNZ                     ;EXIT IF NOT AT BOTTOM
;
        PUSH    H               ;ELSE PREP TO SCROLL SCREEN UP
        CALL    CLRLIN          ;FILL NEW BOTTOM LINE WITH SPACES
        DAD     H
        MOV     A,H             ;GET ROW # COMPONENT OF HL INTO A
        ANI     00011111B
        STA     BASE            ;STORE NEW BASE LINE #
        OUT     SCROLL          ;NOW SCROLL UP NEW BLANK BOTTOM LINE
        POP     H
        RET
;
;
CLRLIN: MOV     A,L
        ANI     10000000B       ;POINT HL TO FIRST COLUMN OF ROW
        MOV     L,A
        MVI     B,80
CLR:    MVI     M,' '           ;STORE ASCII SPACES AT ADDRESS IN HL
        INX     H               ; AND INCREMENT HL
        DJNZ    CLR             ;REPEAT NUMBER OF TIMES GIVEN BY B
        RET
;
;
HOMEUP: MVI     C,' '           ;FAKE-OUT CURSOR ADDRESSING ROUTINE
        JMPR    SETROW          ; TO DO HOMEUP ALMOST FOR FREE
;
;
MULTI:  XCHG                    ;UNCONDITIONALLY RESET THE LEAD-IN
        MVI     M,0             ; STATE TO ZERO BEFORE GOING ON
        XCHG
        CPI     1
        JRNZ    M2TST
SETXY:  MOV     A,C             ;GET SECOND CHAR OF SEQUENCE
        CPI     '='
        RNZ                     ;ABORT SEQUENCE IF NOT '='
        MVI     A,2
        STAX    D               ;MAKE LEADIN=2 NEXT TIME
        RET
;
M2TST:  CPI     2
        JRNZ    M3TST
        MVI     A,3
        STAX    D               ;MAKE LEADIN=3 NEXT TIME
SETROW: LDA     BASE            ;ARRIVE HERE ON THIRD CHARACTER
        ADD     C               ; OF ESC,'=',ROW,COL SEQUENCE
        SUI     ' '-1
SETR2:  SUI     24
        JRNC    SETR2           ;MAKE SURE ROW # IS BETWEEN 0 AND 23
        ADI     24
        ORI     CRTMEM>7        ;MERGE IN MSB'S OF CRT MEMORY
        MOV     H,A
        MVI     L,0
        SRLR    H
        RARR    L
        RET
;
M3TST:  CPI     3
        JRNZ    M4TST
SETCOL: MOV     A,C             ;ARRIVE HERE ON FOURTH CHARACTER
        SUI     ' '             ; OF ESC,'=',ROW,COL SEQUENCE
SETC2:  SUI     80
        JRNC    SETC2           ;MAKE SURE COL # IS BETWEEN 0 AND 79
        ADI     80
        ORA     L               ;MERGE IN COL # WITH L
        MOV     L,A
        RET
;
M4TST:  CALL    DISPLA          ;DISPLAY THE CONTROL CHARACTER
        RET                     ; PASSED IN C
;
;
;
;
;
;********************************************************
;*                                                      *
;*      DISK INPUT/OUTPUT DRIVER SUBROUTINE PACKAGE     *
;*      FOR WESTERN DIGITAL 1771 DISK CONTROLLER        *
;*                                                      *
;********************************************************
;
;
;       EQUATES FOR DISK CONTROLLER PORTS AND COMMAND CODES
;
STSREG  =       WD1771+0        ;STATUS REGISTER
CMDREG  =       WD1771+0        ;COMMAND REGISTER
TRKREG  =       WD1771+1        ;TRACK REGISTER
SECREG  =       WD1771+2        ;SECTOR REGISTER
DATREG  =       WD1771+3        ;DATA REGISTER
;
RDCMD   =       10001000B       ;READ COMMAND
WRTCMD  =       10101000B       ;WRITE COMMAND
SKCMD   =       00011100B       ;SEEK COMMAND
FINCMD  =       11010000B       ;FORCE INTR COMMAND
RSTCMD  =       00001100B       ;RESTORE COMMAND
HLOAD   =       00000100B       ;RD/WRT HEAD LOAD ENABLE
;
RET     =       0C9H            ;SUBROUTINE RETURN INSTR OPCODE
NMIVEC  =       0066H           ;THE NON-MASKABLE INTERRUPT IS
                                ;USED FOR DATA SYNCRONIZATION BETWEEN
                                ;THE Z-80 AND 1771 DISK CONTROLLER
;
;
;
SELECT: MOV     A,C             ;GET UNIT # PASSED IN C AND
        CPI     4               ; CHECK FOR MAXIMUM VALID #
        RNC                     ;ERROR IF NUMBER > 3
        CALL    TURNON          ;MAKE SURE DISKS ARE TURNED ON
        IN      BITDAT
        MOV     B,A             ;SAVE CURRENT DRIVE SELECT DATA
        ANI     11111000B       ;MERGE IN NEW DRIVE UNIT # IN C
        ORA     C               ; IN PLACE OF THE CURRENT ONE
        OUT     BITDAT          ; TO SELECT THE NEW DISK DRIVE
        CALL    FORCE           ;TEST NEW DRIVE'S READY STATUS
        JRZ     SEL2            ; AND CONTINUE IF ITS READY
        MOV     A,B
        OUT     BITDAT          ;ELSE PUT BACK OLD DRIVE SELECT DATA
        MVI     A,10000000B     ; AND RETURN DRIVE-NOT-READY STATUS
        RET
;
SEL2:   LXI     H,UNIT          ;POINT HL TO DRIVE SELECT DATA
        MOV     A,M             ;LOAD A WITH CURRENT UNIT #
        MOV     M,C             ; AND STORE NEW UNIT # FROM C
        CPI     255             ;TEST IF NO DRIVE HAS BEEN SELECTED
        JRZ     SEL3            ; YET AND SKIP NEXT SEGMENT IF SO
        INX     H               ;POINT TO HEAD POSITION TABLE
        ADD     L               ; AND ADD IN NEW UNIT # AS INDEX
        MOV     L,A
        IN      TRKREG          ;GET CURRENT HEAD POSITION
        MOV     M,A             ; AND STORE IN TABLE @ HL
SEL3:   LXI     H,TRKTAB
        MOV     A,L
        ADD     C               ;INDEX INTO TABLE TO GET
        MOV     L,A             ; HEAD POSITION OF NEW DRIVE
        MOV     A,M
        CPI     255             ;TEST IF NEW DRIVE HAS EVER BEEN
        JRZ     HOME            ; SELECTED AND DO A HOME IF NOT
        OUT     TRKREG          ;OUTPUT THE DRIVE'S CURRENT HEAD
        XRA     A               ; POSITION TO THE TRACK REGISTER
        RET
;
;
;
HOME:   CALL    READY           ;CLEAR DISK CONTROLLER
        RNZ                     ;EXIT IF DRIVE NOT READY
        XRA     A
        STA     TRACK           ;SET TRACK # IN MEM TO ZERO
RESTOR: MVI     B,RSTCMD        ;LOAD B WITH A RESTORE COMMAND
        CALL    STEP            ;EXECUTE HEAD MOVING OPERATION
        XRI     00000100B       ;GET TRUE TRACK 0 STATUS
        ANI     10011100B       ;MASK TO ERROR BITS
        RET                     ;RETURN 1771 STATUS IN A
;
;
;
SEEK:   CALL    READY           ;CLEAR DISK CONTROLLER
        RNZ                     ;EXIT IF DRIVE NOT READY
        MOV     A,C             ;GET TRACK # DATA FROM C AND
        CPI     77              ; CHECK FOR MAXIMUM VALID #
        RNC                     ;FORGET IT IF TRACK # > 76
        STA     TRACK           ; ELSE STORE TRACK # FOR SEEK
        OUT     DATREG          ;OUTPUT TRACK # TO 1771
        MVI     B,SKCMD         ;LOAD B WITH A SEEK COMMAND AND
        CALL    STEP            ; GO SEEK WITH PROPER STEP RATE
        ANI     10011000B       ;MASK TO READY,SEEK AND CRC ERROR
        RZ                      ; BITS AND RETURN IF ALL GOOD
;
        CALL    RESTOR          ;ELSE TRY TO RE-CAILBRATE HEAD
        RNZ                     ;ERROR IF WE CAN'T FIND TRACK 0
        MOV     A,C
        OUT     DATREG          ;OUTPUT TRACK # TO 1771
        MVI     B,SKCMD
        CALL    STEP            ;TRY TO SEEK THE TRACK AGAIN
        ANI     10011000B
        RET                     ;RETURN FINAL SEEK STATUS IN A
;
;
;
WRITE:  CALL    READY           ;CLEAR THE DISK CONTROLLER
        RNZ                     ;EXIT IF DRIVE NOT READY
        BIT     6,A
        RNZ                     ;EXIT IF DISK IS WRITE-PROTECTED
        MVI     B,WRTCMD
        JMPR    RDWRT
;
READ:   CALL    READY           ;CLEAR DISK CONTROLLER
        RNZ                     ;EXIT IF DRIVE NOT READY
        MVI     B,RDCMD
RDWRT:  SHLD    IOPTR           ;STORE DISK I/O DATA POINTER
        LXI     H,SECTOR
        MOV     M,C             ;STORE SECTOR # FOR READ/WRITE
        INX     H
        MOV     M,B             ;SAVE READ/WRITE COMMAND BYTE
        INX     H
        MVI     M,2             ;SET DISK OPERATION RE-TRY COUNT
RW1:    DI                      ;NO INTERRUPTS DURING DISK I/O
        LXI     H,NMIVEC        ;SAVE BYTE AT NMI VECTOR LOCATION
        MOV     D,M             ; IN D FOR DURATION OF READ/WRITE
        MVI     M,RET           ; LOOP AND REPLACE IT WITH A RET
        LXI     H,RECLEN
        MOV     B,M             ;B=NUMBER OF BYTES/SECTOR
        MVI     C,DATREG        ;C=1771 DATA REGISTER PORT #
        LHLD    IOPTR           ;HL=DISK READ/WRITE DATA POINTER
        LDA     SECTOR          ;GET SECTOR NUMBER
        OUT     SECREG          ;OUTPUT SECTOR # TO 1771
        CALL    FORCE           ;ISSUE A FORCE INTERRUPT COMMAND
        BIT     5,A             ; TO TEST CURRENT HEAD LOAD STATUS
        LDA     CMDTYP          ;GET READ OR WRITE COMMAND BYTE
        JRNZ    RW2             ;JUMP IF HEAD IS ALREADY LOADED
        ORI     HLOAD           ; ELSE MERGE IN HLD BIT
RW2:    CALL    CMDOUT          ;START THE 1771 DOING IT'S THING
        BIT     5,A             ;TEST IF COMMAND IS A READ OR WRITE
        JRNZ    WLOOP           ; AND JUMP TO THE CORRECT LOOP
RLOOP:  HLT
        INI
        JNZ     RLOOP
        CALL    BUSY            ;LOOP UNTIL 1771 COMES UN-BUSY
        ANI     10011100B       ;MASK OFF TO READY, NOT FOUND, CRC
        JMPR    RW3             ; AND LOST DATA STATUS BITS
;
WLOOP:  HLT
        OUTI
        JNZ     WLOOP
        CALL    BUSY
        ANI     10111100B       ;MASK OFF AS ABOVE PLUS WRITE FAULT
RW3:    LXI     H,NMIVEC
        MOV     M,D             ;RESTORE BYTE @ NMI VECTOR
        EI
        RZ                      ;RETURN IF NO DISK I/O ERRORS
        LXI     H,RETRY
        DCR     M               ;DECREMENT RE-TRY COUNT AND
        JRNZ    RW4             ; EXECUTE COMAND AGAIN IF NOT=0
        ORA     A
        RET                     ;ELSE RETURN 1771 ERROR STATUS
;
RW4:    LXI     H,TRACK
        MOV     C,M             ;GET TRACK # FOR CURRENT OPERATION
        CALL    SEEK            ;TRY TO RE-CAILBRATE THE HEAD
        JMPR    RW1             ; BEFORE READING OR WRITING AGAIN
;
;
;
STEP:   LDA     SPEED           ;GET STEP SPEED VARIABLE
        ANI     00000011B
        ORA     B               ;MERGE WITH SEEK/HOME COMMAND IN B
        CALL    CMDOUT          ;OUTPUT COMMAND AND DELAY
BUSY:   IN      STSREG
        BIT     0,A             ;TEST BUSY BIT FROM
        JRNZ    BUSY            ; 1771 AND LOOP TILL=0
        RET
;
;
;
CMDOUT: OUT     CMDREG          ;OUTPUT A COMMAND TO THE 1771
        CALL    PAUSE           ;WASTE 44 MICROSECONDS
PAUSE:  XTHL
        XTHL
        RET
;
;
;
READY:  CALL    TURNON          ;KEEP THOSE DISKS SPINING FOLKS
FORCE:  MVI     A,FINCMD        ;ISSUE A FORCE INTERRUPT COMMAND
        CALL    CMDOUT
        IN      STSREG          ;READ STATUS REGISTER CONTENTS
        BIT     7,A             ;TEST DRIVE NOT READY BIT
        RET
;
;
;
TURNON: MVI     A,30
        STA     MOTOR           ;RE-LOAD THE MOTOR TURN-OFF TIMER
        CALL    PAUSE
        IN      BITDAT
        BIT     2,A             ;TEST IF MOTORS HAVE BEEN STOPPED
        RZ                      ; AND EXIT IF STILL TURNED ON
        ANI     10111011B       ;ELSE RE-ENABLE THE DRIVE SELECTS
        OUT     BITDAT          ; AND ACTIVATE THE MOTOR RELAY
        PUSH    B
        MVI     B,0             ;SET READY LOOP MAX TIMEOUT
TURN2:  CALL    WAIT            ;WAIT 1/93 SECOND AND TEST READY
        JRZ     TURN3           ;EXIT LOOP IF DRIVE READY
        DJNZ    TURN2           ; ELSE TRY AGAIN UP TO 256 TIMES
TURN3:  MVI     B,9
TURN4:  CALL    WAIT            ;GIVE ABOUT 1/10 SECOND MORE DELAY
        DJNZ    TURN4
        POP     B
        RET
;
;
WAIT:   IN      CTC3            ;GET CURRENT CTC3 COUNT VALUE
        MOV     C,A
WAIT2:  IN      CTC3
        CMP     C               ;TEST IF CTC3 HAS CHANGED BY 1 COUNT
        JRZ     WAIT2           ; AND LOOP UNTIL IT CHANGES
        JMPR    FORCE           ; THEN GO TEST DRIVE READY STATUS
;
;
;
;
;
;
ROMEND: .WORD   0               ;TAIL OF FREE MEMORY LINKED LIST
;
        .LOC    RAM
;********************************************************
;*                                                      *
;*      STORAGE ALLOCATION FOR 256 BYTE SCRATCH RAM     *
;*                                                      *
;********************************************************
;
;
;
VECTAB  =       .               ;INTERRUPT VECTOR TABLE STARTS HERE
SIOVEC: .BLKB   16              ;SPACE FOR 8 VECTORS FOR SIO
CTCVEC: .BLKB   8               ;SPACE FOR 4 VECTORS FOR CTC
SYSVEC: .BLKB   4               ;SPACE FOR 2 VECTORS FOR SYSTEM PIO
GENVEC: .BLKB   4               ;SPACE FOR 2 VECTORS FOR GENERAL PIO
;
;
;       KEYBOARD DATA INPUT FIFO VARIABLES
;
FIFO:   .BLKB   16              ;CONSOLE INPUT FIFO
FIFCNT: .BLKB   1               ;FIFO DATA COUNTER
FIFIN:  .BLKB   1               ;FIFI INPUT POINTER
FIFOUT: .BLKB   1               ;FIFO OUTPUT POINTER
LOCK:   .BLKB   2               ;SHIFT LOCK CHARACTER+FLAG BYTE
;
;
;       STACK POINTER SAVE AND LOCAL STACK FOR INTERRUPT ROUTINES
;
SPSAVE: .BLKB   2               ;USER STACK POINTER SAVE AREA
TMPSTK: .BLKB   32              ;LOCAL STACK FOR INTERRUPTS
;
;
;       'SOFTWARE' VECTORS FOR INTERRUPT SERVICE ROUTINES
;
TIKVEC: .BLKB   2               ;ONE SECOND INTERRUPT ROUTINE VECTOR
PINVEC: .BLKB   2               ;PARALLEL CONSOLE INPUT VECTOR
SINVEC: .BLKB   2               ;SERIAL CONSOLE INPUT VECTOR
;
;
;       CLOCK-TIMER INTERRUPT VARIABLES
;
TIKCNT: .BLKB   2               ;BINARY CLOCK TICK COUNTER
DAY:    .BLKB   1               ;CALENDAR DAY
MONTH:  .BLKB   1               ;         MONTH
YEAR:   .BLKB   1               ;         YEAR
HRS:    .BLKB   1               ;CLOCK HOURS REGISTER
MINS:   .BLKB   1               ;      MINUTES RETISTER
SECS:   .BLKB   1               ;      SECONDS REGISTER
;
;
;       DISK I/O DRIVER VARIABLES
;
UNIT:   .BLKB   1               ;CURRENTLY SELECTED DISK #
TRKTAB: .BLKB   4               ;4 DRIVE HEAD POSITION TABLE
SPEED:  .BLKB   1               ;SEEK SPEED FOR 1771 COMMANDS
RECLEN: .BLKB   1               ;SECTOR RECORD LENGTH VARIABLE
MOTOR:  .BLKB   1               ;DRIVE MOTOR TURN-OFF TIMER
TRACK:  .BLKB   1
SECTOR: .BLKB   1
CMDTYP: .BLKB   1               ;COMMAND BYTE FOR READS/WRITES
RETRY:  .BLKB   1               ;DISK OPERATION RE-TRY COUNT
IOPTR:  .BLKB   2               ;DISK I/O BUFFER POINTER
;
;
;
;       CRT OUTPUT DRIVER VARIABLES
;
CURSOR: .BLKB   2               ;CURSOR POINTER
CHRSAV: .BLKB   1               ;CHARACTER OVERLAYED BY CURSOR
CSRCHR: .BLKB   1               ;CHARACTER USED FOR A CURSOR
BASE:   .BLKB   1               ;CURRENT CONTENTS OF SCROLL REGISTER
LEADIN: .BLKB   1               ;STATE OF LEAD-IN SEQUENCE HANDLER
;
;
;       NULL PAD COUNT FOR SERIAL OUTPUT DELAY
;
NULLS:  .BLKB   1               ;# OF NULLS SENT AFTER CONTROL CHARS.
;
;
;       LISTHEAD POINTER FOR DYNAMIC MEMORY ALLOCATION SCHEME
;
FREPTR: .BLKB   2
;
;
;       CONSOLE MONITOR PROGRAM VARIABLES
;
PARAM1: .BLKB   2               ;STORAGE FOR NUMBERS READ
PARAM2: .BLKB   2               ; FROM LINE INPUT BUFFER
PARAM3: .BLKB   2               ; BY 'PARAMS' SUBROUTINE
PARAM4: .BLKB   2
ESCFLG: .BLKB   1               ;CONSOLE ESCAPE FLAG
COFLAG: .BLKB   1               ;CONSOLE OUTPUT TOGGLE
LAST:   .BLKB   2               ;LAST ADDRESS USED BY 'MEMDMP'
LINBUF: .BLKB   64              ;CONSOLE LINE INPUT BUFFER
;
;
;
;
;
        .END
