;  SYSTEM SEGMENT:  DEBUG.RCP
;  SYSTEM:  ARIES-1
;  CUSTOMIZED BY:  RICHARD CONN

;
;  PROGRAM:  DEBUGRCP.ASM
;  AUTHOR:  RICHARD CONN
;  VERSION:  1.0
;  DATE:  30 JUNE 84
;  PREVIOUS VERSIONS:  NONE
;
VERS    EQU     10
RCPID   EQU     'A'

;
;       DEBUGRCP is a resident debug command package for ZCPR3.  As with
; all resident command processors, DEBUGRCP performs the following functions:
;
;               1.  Assuming that the EXTFCB contains the name of the
;                       command, DEBUGRCP looks to see if the first character
;                       of the file name field in the EXTFCB is a question
;                       mark; if so, it returns with the Zero Flag Set and
;                       HL pointing to the internal routine which prints
;                       its list of commands
;               2.  The resident command list in DEBUGRCP is scanned for
;                       the entry contained in the file name field of
;                       EXTFCB; if found, DEBUGRCP returns with the Zero Flag
;                       Set and HL pointing to the internal routine which
;                       implements the function; if not found, DEBUGRCP returns
;                       with the Zero Flag Reset (NZ)
;

;
;  Global Library which Defines Addresses for DEBUGRCP
;
       MACLIB  Z3BASE

;
CTRLC   EQU     'C'-'@'
BS      EQU     08H
TAB     EQU     09H
LF      EQU     0AH
FF      EQU     0CH
CR      EQU     0DH
CTRLX   EQU     'X'-'@'
;
WBOOT   EQU     BASE+0000H              ;CP/M WARM BOOT ADDRESS
UDFLAG  EQU     BASE+0004H              ;USER NUM IN HIGH NYBBLE, DISK IN LOW
BDOS    EQU     BASE+0005H              ;BDOS FUNCTION CALL ENTRY PT
TFCB    EQU     BASE+005CH              ;DEFAULT FCB BUFFER
FCB1    EQU     TFCB                    ;1st and 2nd FCBs
FCB2    EQU     TFCB+16
TBUFF   EQU     BASE+0080H              ;DEFAULT DISK I/O BUFFER
TPA     EQU     BASE+0100H              ;BASE OF TPA
;
;  SYSTEM Entry Point
;
       org     rcp             ; passed for Z3BASE

       db      'Z3RCP'         ; Flag for Package Loader
;
;  **** Command Table for RCP ****
;       This table is RCP-dependent!
;
;       The command name table is structured as follows:
;
;       ctable:
;               DB      'CMNDNAME'      ; Table Record Structure is
;               DW      cmndaddress     ; 8 Chars for Name and 2 Bytes for Adr
;               ...
;               DB      0       ; End of Table
;
cnsize  equ     4               ; NUMBER OF CHARS IN COMMAND NAME
       db      cnsize  ; size of text entries
ctab:
       db      'H   '  ; Help for RCP
       dw      clist
ctab1:
       db      'MU  '  ; Memory Utility
       dw      mu
;
       db      0
;
;  BANNER NAME OF RCP
;
rcp$name:
       db      'DEBUG '
       db      (vers/10)+'0','.',(vers mod 10)+'0'
       db      RCPID
       db      0

;
;  Command List Routine
;
clist:
       lxi     h,rcp$name      ; print RCP Name
       call    print1
       lxi     h,ctab1         ; print table entries
       mvi     c,1             ; set count for new line
clist1:
       mov     a,m             ; done?
       ora     a
       rz
       dcr     c               ; count down
       jnz     clist1a
       call    crlf            ; new line
       mvi     c,4             ; set count
clist1a:
       lxi     d,entryname     ; copy command name into message buffer
       mvi     b,cnsize        ; number of chars
clist2:
       mov     a,m             ; copy
       stax    d
       inx     h               ; pt to next
       inx     d
       dcr     b
       jnz     clist2
       inx     h               ; skip to next entry
       inx     h
       push    h               ; save ptr
       lxi     h,entrymsg      ; print message
       call    print1
       pop     h               ; get ptr
       jmp     clist1
;
;  Print String (terminated in 0 or MSB Set) at Return Address
;
vprint:
eprint:
       xthl                    ; get address
       call    print1
       xthl                    ; put address
       ret
;
;  Print String (terminated in 0 or MSB Set) pted to by HL
;
print1:
       mov     a,m             ; done?
       inx     h               ; pt to next
       ora     a               ; 0 terminator
       rz
       cpi     dim             ; standout?
       jz      print1d
       cpi     bright          ; standend?
       jz      print1b
       call    cout            ; print char
       ora     a               ; set MSB
       rm                      ; MSB terminator
       jmp     print1
print1d:
       call    stndout         ; dim
       jmp     print1
print1b:
       call    stndend         ; bright
       jmp     print1
;
;  New Line
;
crlf:
       mvi     a,cr
       call    cout
       mvi     a,lf    ;fall thru
;
;  Character Output
;
cout:
       push    psw
       push    b
       push    d
       push    h
       mov     e,a
       mvi     c,2             ; use BDOS
       call    bdos
       pop     h
       pop     d
       pop     b
       pop     psw
       ret
;
;  Get char in A
;
cin:
       push    h
       push    d
       push    b
       mvi     c,1
       call    bdos
       ani     7fh
       push    psw
       mvi     a,bs    ;overwrite
       call    cout
       pop     psw
       pop     b
       pop     d
       pop     h
       ret
;
;  CLIST Messages
;
entrymsg:
       db      '  '            ; command name prefix
entryname:
       ds      cnsize  ; command name
       db      0       ; terminator

;
;  General Equates
;
bel     equ     07h
bs      equ     08h
cr      equ     0dh
lf      equ     0ah
fcb     equ     5ch

DIM     EQU     1
BRIGHT  EQU     2

EOLCH   EQU     0       ;END OF LINE CHAR
SEPCH   EQU     ','     ;SEPARATOR CHAR
EROW    EQU     6       ;FIRST ROW OF EDITOR DISPLAY
ECOL    EQU     4       ;FIRST COL OF EDITOR DISPLAY
ECOLC   EQU     ECOL+16*3+8     ;FIRST COL OF EDITOR CHAR DISPLAY
ECURS   EQU     '>'     ;EDITOR CURSOR
PRROW   EQU     22      ;PROMPT ROW
PRCOL   EQU     10      ;PROMPT COLUMN
PRCOLI  EQU     PRCOL+15        ;PROMPT INPUT COL
ERROW   EQU     23      ;ERROR MESSAGE ROW
ERCOL   EQU     15      ;ERROR MESSAGE COLUMN

;
; DEFINE FREE SPACE
;
MU:
       LXI     H,TBUFF ;DETERMINE ADDRESS
       MVI     M,126   ;126 CHARS INPUT ALLOWED
       SHLD    BUFFER  ;SET PTR
;
; SET UP ARROW KEYS
;
       LXI     H,Z3ENV ;PT TO ENVIRONMENT DESCRIPTOR
       LXI     D,80H+10H       ;PT TO ARROW KEY INFO
       DAD     D
       LXI     D,EDCURT        ;PT TO CURSOR TABLE
       MVI     B,4     ;4 ARROW KEYS
ARROW:
       MOV     A,M     ;GET CHAR
       STAX    D       ;STORE CHAR
       INX     H       ;PT TO NEXT
       INX     D       ;PT TO NEXT ENTRY
       INX     D
       INX     D
       DCR     B       ;COUNT DOWN
       JNZ     ARROW
;
; Initialize Terminal
;
       call    tinit
;
; Check for Command Line Parameter
;
       lxi     h,fcb+1 ;pt to first char
       mov     a,m     ;get char
       cpi     ' '     ;no param?
       jnz     pcheck
       lxi     h,tpa   ;pt to TPA
       jmp     mu3
;
; We have a parameter
;
pcheck:
       call    hexin   ;convert to binary
       xchg            ;HL=value
       jmp     mu3
;
; Erase to EOL
;  If fct not supported, send out B spaces and B backspaces
;
vereol:
       call    ereol   ;try erase
       rnz
       push    b       ;save B
       mvi     a,' '   ;send spaces
       call    vereol1
       pop     b       ;get B
       mvi     a,bs    ;send backspaces
vereol1:
       call    cout    ;send char in A
       dcr     b
       jnz     vereol1
       ret
;
; Clear Screen
;  If fct not supported, write 24 CRLFs
;
vcls:
       call    cls     ;try clear
       rnz
       push    b       ;save B
       mvi     b,24    ;count
vcls1:
       call    crlf
       dcr     b
       jnz     vcls1
       pop     b
       ret
;
; Run MU3
;       HL contains starting address
;
mu3:
       SHLD    BLOCK   ;SAVE PTR TO BLOCK
;
; REFRESH EDIT SCREEN
;
EDIT0:
       CALL    VCLS    ;NEW SCREEN
       CALL    AT
       DB      2,35    ;ROW 2, COL 35
       CALL    VPRINT  ;BANNER
       DB      'MU RCP '
       DB      (VERS/10)+'0','.',(VERS MOD 10)+'0',RCPID
       DB      0
;
; REENTER MU3 WITH PTRS RESET
;
MU3R:
       XRA     A       ;A=0
       STA     EINDEX  ;SET INDEX TO 0 (FIRST ELEMENT)
       CALL    EDPLOT  ;PLOT BUFFER DATA
;
; INPUT EDITOR COMMAND
;
EDITCMD:
       CALL    PRMSG   ;POSITION AT PROMPT MESSAGE
       DB      'MU Command?',0
       CALL    PRINP   ;POSITION AT PROMPT INPUT
       DB      0
       CALL    CIN     ;GET CHAR
       CALL    CAPS    ;CAPITALIZE
       MOV     B,A     ;COMMAND IN B
       LXI     H,EDCURT        ;PROCESS CURSOR COMMANDS FIRST
       CALL    CMD     ;PROCESS COMMAND
       LXI     H,ECMDTBL       ;EDITOR COMMAND TABLE
       CALL    CMD     ;PROCESS COMMAND
       CALL    VPRINT  ;ERROR MESSAGE
       DB      BEL,0
       JMP     EDITCMD
;
; Position at Prompt Message and Print it
;
PRMSG:
       CALL    AT      ;POSITION
       DB      PRROW,PRCOL
       JMP     VPRINT  ;PRINT IT
;
; Position at Prompt Input and Print Prompt
;
PRINP:
       CALL    AT      ;POSITION
       DB      PRROW,PRCOLI
       JMP     VPRINT  ;PRINT IT
;
;INPUT ERROR
;
WHAT:
       CALL    VPRINT
       DB      BEL,0
       JMP     EDITCMD
;
;Command Table Search and Execute
;
CMD:
       MOV     A,M     ;CHECK FOR END OF TABLE
       ORA     A
       RZ              ;COMMAND NOT FOUND
       CMP     B       ;MATCH?
       JZ      CMDRUN
       INX     H       ;SKIP TO NEXT ENTRY IN TABLE
       INX     H
       INX     H
       JMP     CMD
;
;RUN COMMAND
;
CMDRUN:
       INX     H       ;PT TO LOW ADDRESS
       MOV     E,M
       INX     H       ;PT TO HIGH ADDRESS
       MOV     D,M
       XCHG
       POP     PSW     ;CLEAR STACK
       PCHL            ;RUN ROUTINE
;
;PLOT BUFFER DATA
;
EDPLOT:
       MVI     H,EROW-1        ;SET ROW
       MVI     L,ECOL  ;SET COLUMN
       CALL    GOTOXY  ;POSITION CURSOR
       CALL    VPRINT
       DB      DIM
       DB      '       0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F'
       DB      BRIGHT,0
       INR     H       ;NEXT ROW
       CALL    GOTOXY  ;POSITION CURSOR
       XCHG            ;POSITION IN DE
       LHLD    BLOCK   ;PT TO DATA
       MVI     B,8     ;8 LINES
;
;Print Next Line on Screen
;
EDIT00:
       CALL    STNDOUT ;GO DIM
       MOV     A,H     ;OUTPUT ADDRESS
       CALL    PA2HC
       MOV     A,L
       CALL    PA2HC
       CALL    VPRINT
       DB      ':',BRIGHT,' ',0
       MVI     C,16    ;16 ELEMENTS
EDIT01:
       MOV     A,M     ;GET BYTE
       CALL    PA2HC   ;PRINT AS HEX
       CALL    SPACE   ;PRINT 1 SPACE
       INX     H       ;PT TO NEXT
       DCR     C       ;COUNT DOWN
       JNZ     EDIT01
       XCHG            ;POSITION AGAIN
       INR     H       ;NEXT ROW
       CALL    GOTOXY
       XCHG
       DCR     B       ;COUNT DOWN
       JNZ     EDIT00
       MVI     H,EROW  ;RESET ROW
       MVI     L,ECOLC ;RESET COL
       CALL    GOTOXY  ;POSITION CURSOR
       XCHG            ;POSITION IN DE
       LHLD    BLOCK   ;PT TO DATA
       MVI     B,8     ;8 LINES
EDIT02:
       CALL    BAR     ;PRINT BAR
       MVI     C,16    ;16 ELEMENTS
EDIT03:
       MOV     A,M     ;GET BYTE
       ANI     7FH     ;MASK MSB
       CPI     7FH     ;DON'T PRINT 7FH
       JZ      EDIT7F
       CPI     ' '     ;SPACE OR MORE?
       JNC     EDIT04
EDIT7F:
       MVI     A,'.'   ;PRINT DOT
EDIT04:
       CALL    COUT    ;PRINT BYTE
       INX     H       ;PT TO NEXT
       DCR     C       ;COUNT DOWN
       JNZ     EDIT03
       CALL    BAR     ;PRINT ENDING BAR
       XCHG            ;POSITION AGAIN
       INR     H       ;NEXT ROW
       CALL    GOTOXY
       XCHG
       DCR     B       ;COUNT DOWN
       JNZ     EDIT02
       CALL    EDCUR   ;POSITION CURSOR
       RET
;
;EDITOR COMMAND TABLE
;
ECMDTBL:
       DB      CR      ;NOP
       DW      EDITCMD
       DB      'C'-'@' ;^C = EXIT MU3
       DW      EDCC
       DB      'R'-'@' ;^R = REFRESH
       DW      EDIT0
       DB      'E'-'@' ;^E=UP
       DW      EDUP
       DB      'X'-'@' ;^X=DOWN
       DW      EDDOWN
       DB      'D'-'@' ;^D=RIGHT
       DW      EDRIGHT
       DB      'S'-'@' ;^S=LEFT
       DW      EDLEFT
       DB      ' '     ;NOP
       DW      EDITCMD
       DB      '+'     ;ADVANCE
       DW      EDITPLUS
       DB      '-'     ;BACKUP
       DW      EDITMINUS
       DB      'A'     ;ADDRESS
       DW      EDITADR
       DB      'C'     ;COMMAND LINE
       DW      EDITCL
       DB      'N'     ;CHANGE NUMBERS
       DW      EDITHEX
       DB      'T'     ;CHANGE TEXT
       DW      EDITALP
       DB      0       ;END OF TABLE
;
;  ARROW KEY DEFINITONS FROM TCAP
;
EDCURT:
       DB      0       ;0 INDICATES NO ARROW KEYS
       DW      EDUP
       DB      0
       DW      EDDOWN
       DB      0
       DW      EDRIGHT
       DB      0
       DW      EDLEFT
       DB      0       ;END OF TABLE
;
;Enter Command Line
;
EDITCL:
       CALL    VPRINT  ;PROMPT INPUT
       DB      CR,LF,'Command Line? ',0
       CALL    RDBUF   ;INPUT TEXT
       CALL    PUTCL   ;STORE COMMAND LINE
       JMP     CRLF    ;NEW LINE
;
; STORE COMMAND LINE
;
PUTCL:
       XCHG            ;PTR TO NEW LINE IN DE
       CALL    GETCL1  ;GET COMMAND LINE DATA
       MOV     B,A     ;CHAR COUNT IN B
       XCHG            ;HL PTS TO NEW LINE
       PUSH    H       ;SAVE PTR TO NEXT LINE
PCL1:
       MOV     A,M     ;GO TO END OF LINE
       ORA     A       ;AT END?
       JZ      PCL2
       INX     H       ;PT TO NEXT
       DCR     B       ;COUNT DOWN
       JNZ     PCL1
       POP     H       ;CLEAR STACK
       RET             ;COMMAND LINE TOO LONG - ABORT
;
; AT END OF NEW COMMAND LINE
;       PTR TO FIRST CHAR OF NEW COMMAND LINE ON STACK
;       HL PTS TO ENDING 0 OF NEW COMMAND LINE
;       B = NUMBER OF CHARS REMAINING BEFORE COMMAND LINE OVERFLOW
;
PCL2:
       XCHG            ;DE PTS TO LAST BYTE
       PUSH    D       ;SAVE PTR IN CASE OF ERROR
       CALL    GETCL2  ;PT TO TAIL OF COMMAND LINE BUFFER
       MOV     A,M     ;GET FIRST CHAR OF TAIL
       CPI     ';'     ;CONTINUATION?
       JZ      PCL3
       ORA     A       ;DONE?
       JZ      PCL3
       MVI     A,';'   ;SET CONTINUATION CHAR
       STAX    D
       INX     D
       DCR     B       ;COUNT DOWN
       JZ      PCL4    ;OVERFLOW
;
; COPY TAIL ONTO END OF NEW COMMAND LINE
;
PCL3:
       MOV     A,M     ;GET NEXT CHAR
       STAX    D       ;STORE IT
       INX     H       ;PT TO NEXT
       INX     D
       ORA     A       ;DONE?
       JZ      PCL5
       DCR     B       ;COUNT DOWN
       JNZ     PCL3
;
; COMMAND LINE TOO LONG
;
PCL4:
       POP     H       ;GET PTR TO END OF OLD LINE
       MVI     M,0     ;STORE ENDING 0
       POP     PSW     ;CLEAR STACK
       RET
;
; NEW COMMAND LINE OK
;
PCL5:
       POP     PSW     ;CLEAR STACK
       CALL    GETCL1  ;GET PTR TO BUFFER
       LXI     D,4     ;PT TO FIRST CHAR IN BUFFER
       XCHG
       DAD     D
       XCHG
       MOV     M,E     ;STORE ADDRESS
       INX     H
       MOV     M,D
       POP     H       ;HL PTS TO FIRST CHAR OF NEW LINE
;
; COPY COMMAND LINE INTO BUFFER
;
PCL6:
       MOV     A,M     ;COPY
       STAX    D
       INX     H
       INX     D
       ORA     A       ;DONE?
       JNZ     PCL6
       RET
;
; GETCL1
;
GETCL1:
       LHLD    Z3ENV+18H       ;GET ADDRESS OF COMMAND LINE BUFFER
       PUSH    H       ;SAVE IT
       INX     H       ;GET SIZE IN A
       INX     H
       MOV     A,M
       POP     H
       RET
;
; GETCL2
;
GETCL2:
       LHLD    Z3ENV+18H       ;GET ADDRESS OF COMMAND LINE BUFFER
       MOV     A,M             ;GET ADDRESS OF NEXT CHAR
       INX     H
       MOV     H,M
       MOV     L,A             ;HL PTS TO NEXT CHAR
       MOV     A,M             ;GET IT
       RET

;
;Enter ASCII Chars
;
EDITALP:
       CALL    PRINP   ;PROMPT INPUT
       DB      DIM,'Enter Text',BRIGHT
       DB      CR,LF,' --> ',0
       CALL    RDBUF   ;INPUT TEXT WITHOUT PROMPT
       CALL    EDPRCL  ;CLEAR PROMPT LINE
       LDA     EINDEX  ;PT TO POSITION
       XCHG
       LHLD    BLOCK   ;COMPUTE OFFSET
       XCHG
       ADD     E
       MOV     E,A
       MOV     A,D
       ACI     0
       MOV     D,A     ;DE PTS TO BYTE, HL PTS TO TEXT
EDITA1:
       MOV     A,M     ;GET CHAR
       CPI     EOLCH   ;EOL?
       JZ      EDITA2  ;REFRESH SCREEN
       CALL    GETAHV  ;GET ASCII OR <HEX> VALUE
       STAX    D       ;UPDATE BYTE
       INX     H       ;PT TO NEXT INPUT CHAR
       INR     E       ;PT TO NEXT BUFFER BYTE
       JNZ     EDITA1
EDITA2:
       CALL    EDPLOT  ;REPLOT
       JMP     EDITCMD ;DONE-REFRESH SCREEN
;
;Enter Numbers
;
EDITHEX:
       CALL    PRINP   ;PROMPT INPUT
       DB      DIM,'Enter Hex Numbers'
       DB      BRIGHT
       DB      CR,LF,' --> ',0
       CALL    RDBUF   ;INPUT TEXT WITHOUT PROMPT
       CALL    EDPRCL  ;CLEAR PROMPT LINE
       LDA     EINDEX  ;PT TO POSITION
       XCHG
       LHLD    BLOCK   ;COMPUTE OFFSET
       XCHG
       ADD     E
       MOV     E,A
       MOV     A,D
       ACI     0
       MOV     D,A     ;DE PTS TO BYTE, HL PTS TO TEXT
EDITH1:
       MOV     A,M     ;GET HEX DIGIT
       CPI     EOLCH   ;EOL?
       JZ      EDITA2  ;REFRESH SCREEN
       CPI     ' '     ;SKIP SPACES
       JNZ     EDITH2
       INX     H       ;SKIP SPACE
       JMP     EDITH1
EDITH2:
       PUSH    D       ;SAVE PTR
       CALL    HEXIN   ;GET VALUE AND POSITION HL
       MOV     A,E     ;... IN A
       POP     D       ;GET PTR
       STAX    D       ;PUT BYTE
       INR     E       ;ADVANCE TO NEXT BYTE
       JNZ     EDITH1
       JMP     EDITA2  ;DONE-REFRESH
;
;CLEAR PROMPT LINE
;
EDPRCL:
       CALL    PRINP   ;PROMPT LINE
       DB      0
       MVI     B,40    ;40 POSITIONS
       CALL    VEREOL  ;CLEAR TO EOL OR 40 CHARS
       CALL    AT      ;USER INPUT
       DB      ERROW,1
       MVI     B,79    ;79 POSITIONS
       JMP     VEREOL
;
;Input Address
;
EDITADR:
       CALL    VPRINT
       DB      'Address? ',0
       CALL    RDBUF   ;GET USER INPUT
       CALL    SKSP    ;SKIP LEADING SPACES
       MOV     A,M     ;EMPTY LINE?
       ORA     A
       JZ      EDIT0
       CALL    HEXIN   ;CONVERT FROM HEX
       XCHG            ;HL = ADDRESS
       SHLD    BLOCK
       JMP     EDIT0   ;REENTER
;
;Advance to Next Block
;
EDITPLUS:
       LHLD    BLOCK   ;ADVANCE TO NEXT BLOCK
       LXI     D,128   ;128 BYTES
       DAD     D
       SHLD    BLOCK
       JMP     MU3R
;
;Backup to Last Block
;
EDITMINUS:
       LHLD    BLOCK   ;BACKUP TO LAST BLOCK
       LXI     D,-128  ;128 BYTES
       DAD     D
       SHLD    BLOCK
       JMP     MU3R
;
;Exit MU3
;
EDCC:
       CALL    DINIT   ;DEINIT TERM
       JMP     CRLF    ;NEW LINE
;
;EDIT MOVE: UP
;
EDUP:
       CALL    EDCCUR  ;CLEAR CURSOR
       LDA     EINDEX  ;BACKUP INDEX BY 16
       SUI     16
;
;Common EDIT MOVE Routine - on input, A=new index
;
EDMOVE:
       ANI     7FH     ;MOD 128
       STA     EINDEX
       CALL    EDCUR   ;SET CURSOR
       JMP     EDITCMD
;
;EDIT MOVE: DOWN
;
EDDOWN:
       CALL    EDCCUR  ;CLEAR CURSOR
       LDA     EINDEX  ;INCREMENT INDEX BY 16
       ADI     16
       JMP     EDMOVE  ;COMMON ROUTINE
;
;EDIT MOVE: RIGHT
;
EDRIGHT:
       CALL    EDCCUR  ;CLEAR CURSOR
       LDA     EINDEX  ;INCREMENT INDEX BY 1
       INR     A
       JMP     EDMOVE  ;COMMON ROUTINE
;
;EDIT MOVE: LEFT
;
EDLEFT:
       CALL    EDCCUR  ;CLEAR CURSOR
       LDA     EINDEX  ;DECREMENT INDEX BY 1
       DCR     A
       JMP     EDMOVE  ;COMMON ROUTINE
;
;EDIT SUBROUTINE: EDCUR
; Position Editor Cursor at EINDEX
;EDIT SUBROUTINE: EDCCUR
; Clear Editor Cursor at EINDEX
;
EDCUR:
       PUSH    H       ;SAVE HL
       MVI     C,ECURS ;CURSOR CHAR
       CALL    EDSETCUR
       CALL    AT      ;UPDATE DATA
       DB      3,74
       LDA     EINDEX  ;PT TO BYTE AT CURSOR
       LHLD    BLOCK
       ADD     L
       MOV     L,A
       MOV     A,H
       ACI     0
       MOV     H,A     ;HL PTS TO BYTE AT CURSOR
       MOV     A,M     ;GET BYTE
       CALL    PA2HC   ;PRINT AS HEX
       CALL    SPACE
       MOV     A,M     ;GET BYTE
       POP     H       ;RESTORE HL
       ANI     7FH     ;MASK
       CPI     7FH     ;7FH AS DOT
       JZ      EDC7F
       CPI     ' '     ;OUTPUT CHAR OR DOT
       JNC     COUT
EDC7F:
       MVI     A,'.'   ;DOT
       JMP     COUT
EDCCUR:
       MVI     C,' '   ;CLEAR CURSOR
EDSETCUR:
       CALL    EDROW   ;COMPUTE ROW
       ANI     0FH     ;COMPUTE COL MOD 16
       MOV     B,A     ;RESULT IN B
       ADD     A       ;*2
       ADD     B       ;*3
       ADI     ECOL+6  ;ADD IN COL
       DCR     A       ;SUBTRACT 1
       MOV     L,A     ;COL POSITION SET
       CALL    GOTOXY  ;POSITION CURSOR
       MOV     A,C     ;OUTPUT CHAR
       JMP     COUT
;
;Compute Row from EINDEX
;
EDROW:
       LDA     EINDEX  ;GET INDEX
       MOV     B,A     ;SAVE IN B
       RRC             ;DIVIDE BY 16
       RRC
       RRC
       RRC
       ANI     0FH     ;MASK FOR LSB ONLY
       ADI     EROW    ;COMPUTE ROW
       MOV     H,A     ;ROW SET
       MOV     A,B     ;GET INDEX
       RET

;
;PRINT A SPACE
;
SPACE:
       MVI     A,' '
       JMP     COUT
;
;PRINT AN BARISK IN REV VIDEO
;
BAR:
       CALL    VPRINT
       DB      DIM,'|',BRIGHT,0
       RET
;
;Get value from input buffer
;
GETAHV:
       MOV     A,M     ;GET NEXT CHAR
       CPI     '<'     ;HEX ESCAPE?
       RNZ             ;NO, RETURN
;"<<" means one "<"
       INX     H
       MOV     A,M
       CPI     '<'
       RZ
;Got hex
       PUSH    D
       CALL    HEXIN   ;GET VALUE
       CPI     '>'     ;PROPER DELIM?
       MOV     A,E     ;GET VALUE
       POP     D
       RZ
;
;ERROR CONDITION IN SUBROUTINE - CLEAR STACK AND FLAG ERROR
;
SERR:
       POP     PSW     ;CLEAR STACK
       JMP     WHAT    ;ERROR
;
;Input Number from Command Line -- Assume it to be Hex
;  Number returned in DE
;
HEXIN:
       LXI     D,0     ;INIT VALUE
       MOV     A,M
       CPI     '#'     ;DECIMAL?
       JZ      HDIN    ;MAKE DECIMAL
;
HINLP:
       MOV     A,M     ;GET CHAR
       CALL    CAPS    ;CAPITALIZE
       CPI     CR      ;EOL?
       RZ
       CPI     EOLCH   ;EOL?
       RZ
       CPI     SEPCH
       RZ
       CPI     ' '     ;SPACE?
       RZ
       CPI     '-'     ;'THRU'?
       RZ
       CPI     '>'
       RZ
       INX     H       ;PT TO NEXT CHAR
       CPI     '0'     ;RANGE?
       JC      SERR
       CPI     '9'+1   ;RANGE?
       JC      HINNUM
       CPI     'A'     ;RANGE?
       JC      SERR
       CPI     'F'+1   ;RANGE?
       JNC     SERR
       SUI     7       ;ADJUST FROM A-F TO 10-15
;
HINNUM:
       SUI     '0'     ;CONVERT FROM ASCII TO BINARY
       XCHG
       DAD     H       ;MULT PREVIOUS VALUE BY 16
       DAD     H
       DAD     H
       DAD     H
       ADD     L       ;ADD IN NEW DIGIT
       MOV     L,A
       XCHG
       JMP     HINLP
;
HDIN:
       INX     H       ;SKIP '#'
;
;Input Number in Command Line as Decimal
;  Number is returned
in DE
;
DECIN:
       LXI     D,0
       MOV     A,M     ; GET 1ST CHAR
       CPI     '#'     ; HEX?
       JNZ     DINLP
       INX     H       ; PT TO DIGIT
       JMP     HINLP   ; DO HEX PROCESSING
;
DINLP:
       MOV     A,M     ;GET DIGIT
       CALL    CAPS    ;CAPITALIZE
       CPI     '0'     ;RANGE?
       RC
       CPI     '9'+1   ;RANGE?
       RNC
       SUI     '0'     ;CONVERT TO BINARY
       INX     H       ;PT TO NEXT
       PUSH    H
       MOV     H,D
       MOV     L,E
       DAD     H       ;X2
       DAD     H       ;X4
       DAD     D       ;X5
       DAD     H       ;X10
       ADD     L       ;ADD IN DIGIT
       MOV     L,A
       MOV     A,H
       ACI     0
       MOV     H,A
       XCHG            ;RESULT IN DE
       POP     H
       JMP     DINLP
;
; READ LINE FROM USER INTO INPUT LINE BUFFER
;
RDBUF:
       LHLD    BUFFER  ;PT TO BUFFER
       XCHG            ;SET DE AS PTR TO BUFFER
       MVI     C,10    ;BDOS READLN
       PUSH    D       ;SAVE PTR
       CALL    BDOS
       POP     H       ;PT TO CHAR COUNT
       INX     H
       MOV     E,M     ;GET CHAR COUNT
       MVI     D,0
       INX     H       ;PT TO FIRST CHAR
       PUSH    H       ;SAVE PTR
       DAD     D       ;PT TO AFTER LAST CHAR
       MVI     M,0     ;STORE ENDING 0
       POP     H       ;PT TO FIRST CHAR
       RET

;
; Capitalize char in A
;
caps:
       ani     7fh
       cpi     'a'     ;range?
       rc
       cpi     'z'+1
       rnc
       ani     5fh     ;mask to caps
       ret
;
; CLEAR SCREEN ON TERMINAL
;
cls:
       push    h       ;save regs
       push    d
       lxi     h,z3env+80H     ;pt to environment
       mov     a,m     ;no terminal?
       cpi     ' '+1
       jc      clserr
       lxi     d,14h   ;pt to cls delay
       dad     d
       mov     d,m     ;get it
       inx     h       ;pt to cls string
       inx     h
       inx     h
       mov     a,m     ;get first char of string
       ora     a       ;if no string, error
       jz      clserr
       call    vidout  ;output string with delay
       pop     d       ;done
       pop     h
       xra     a       ;return NZ
       dcr     a
       ret
clserr:
       pop     d       ;done
       pop     h
       xra     a       ;return Z
       ret

;
; Erase to End of Line
;       Return with A=0 and Zero Flag Set if not done
;
ereol:
       push    b       ;save regs
       push    d
       push    h
       lxi     h,z3env+80h     ;pt to environment
       mov     a,m     ;no terminal?
       cpi     ' '+1
       jc      err
       lxi     d,16h   ;pt to ereol delay
       dad     d
       mov     d,m     ;get it
       inx     h       ;pt to cls string
       call    vidskp  ;skip over it
       call    vidskp  ;skip over CM string
       mov     a,m     ;get first char of ereol string
       ora     a       ;if no string, error
       jz      err
       call    vidout  ;output string with delay
       jmp     noerr

;
; GOTO XY
;       HL = Row/Col, with Home=1/1
;       Return with A=0 and Zero Flag Set if not done
;
gotoxy:
       push    b       ;save regs
       push    d
       push    h
       lxi     h,z3env+80h     ;pt to environment
       mov     a,m     ;no terminal?
       cpi     ' '+1
       jc      err
       lxi     d,15h   ;pt to CM delay
       dad     d
       mov     a,m     ;get it
       sta     cmdelay ;save it
       inx     h       ;pt to CL string
       inx     h
       call    vidskp  ;skip CL string
       mov     a,m     ;get first char of CM string
       ora     a       ;if no string, error
       jz      err
       xchg            ;DE=address of CM string
       pop     h       ;get coordinates in HL
       push    h
       call    gxy     ;output xy string with delay
       lda     cmdelay ;pause
       call    videlay
noerr:
       pop     h       ;done
       pop     d
       pop     b
       xra     a       ;return NZ
       dcr     a
       ret
err:
       pop     h       ;done
       pop     d
       pop     b
       xra     a       ;return Z
       ret

;
; Position Cursor at Location Specified by Return Address
; Usage:
;       call    at
;       db      row,col ;location
;
at:
       xthl            ;pt to address
       push    d       ;save DE
       mov     d,m     ;get row
       inx     h
       mov     e,m
       inx     h       ;HL pts to return byte
       xchg            ;DE pts to return byte, HL contains screen loc
       call    gotoxy  ;position cursor
       xchg            ;HL pts to return byte
       pop     d       ;restore registers
       xthl            ;restore stack ptr
       ret

;
; GOTOXY
;   On input, H=Row and L=Column to Position To (1,1 is Home)
;   On input, DE=address of CM string
;
gxy:
       dcr     h       ;adjust to 0,0 for home
       dcr     l
       xra     a       ;set row/column
       sta     rcorder ;row before column
       sta     rcbase  ;add 0 to base
;
; Cycle thru string
;
gxyloop:
       ldax    d       ;get next char
       inx     d       ;pt to next
       ora     a       ;done?
       rz
       cpi     '%'     ;command?
       jz      gxycmd
       cpi     '\'     ;escape?
       jz      gxyesc
       call    cout    ;send char
       jmp     gxyloop

;
; Escape - output following byte literally
;
gxyesc:
       ldax    d       ;get next char
       call    cout    ;output literally
       inx     d       ;pt to next
       jmp     gxyloop
;
; Interpret next character as a command character
;
gxycmd:
       ldax    d       ;get command char
       inx     d       ;pt to next
       cpi     'd'     ;%d
       jz      gxyout1
       cpi     '2'     ;%2
       jz      gxyout2
       cpi     '3'     ;%3
       jz      gxyout3
       cpi     '.'     ;%.
       jz      gxyout4
       cpi     '+'     ;%+v
       jz      gxyout5
       cpi     '>'     ;%>xy
       jz      gxygt
       cpi     'r'     ;%r
       jz      gxyrev
       cpi     'i'     ;%i
       jz      gxyinc
       call    cout    ;output char if nothing else
       jmp     gxyloop
;
; Set row/col home to 1,1 rather than 0,0
;
gxyinc:
       mvi     a,1     ;set rcbase to 1
       sta     rcbase
       jmp     gxyloop
;
; Reverse order of output to column then row (default is row then column)
;
gxyrev:
       mvi     a,1     ;set column and row order
       sta     rcorder
       jmp     gxyloop
;
; Command: >xy
;   If value of row/col is greater than x, add y to it
;
gxygt:
       call    getval  ;get value
       mov     c,a     ;save value
       ldax    d       ;get value to test
       inx     d       ;pt to next
       cmp     c       ;if carry, value>x
       jnc     gxygt1
       ldax    d       ;get value to add
       add     c
       call    putval  ;put value back
gxygt1:
       inx     d       ;pt to next
       jmp     gxyloop ;resume
;
; Command: +n
;   Add n to next value and output
;
gxyout5:
       ldax    d       ;get value to add
       inx     d       ;pt to next
       mov     b,a     ;save in B
       call    getval  ;get value
       add     b       ;add in B
       call    cout    ;output value
rcmark:
       lda     rcorder ;mark output
       ori     80h
       sta     rcorder
       jmp     gxyloop
;
; Command: .
;   Output next value
;
gxyout4:
       call    getval  ;get value
       call    cout    ;output value
       jmp     rcmark
;
; Command: 3
;   Output next value as 3 decimal digits
;
gxyout3:
       call    getval  ;get value
       mvi     b,100   ;output 100's
       mvi     c,1     ;leading zeroes
       call    digout
gxyot3:
       mvi     b,10    ;output 10's
       mvi     c,1     ;leading zeroes
gxyot2:
       call    digout
       adi     '0'     ;output 1's
       call    cout
       jmp     rcmark
;
; Command: 2
;   Output next value as 2 decimal digits
;
gxyout2:
       call    getval  ;get value
       jmp     gxyot3
;
; Command: d
;   Output next value as n decimal digits with no leading zeroes
;
gxyout1:
       call    getval  ;get value
       mvi     b,100   ;output 100's
       mvi     c,0     ;no leading zeroes
       call    digout
       mvi     b,10    ;output 10's
       mvi     c,0     ;no leading zeroes
       jmp     gxyot2
;
; Return next value in A
;
getval:
       lda     rcorder ;get order flag
       ora     a       ;already output the first value?
       jm      getval2
       ani     1       ;look at lsb
       jz      getvalr ;if 0, row first
getvalc:
       lda     rcbase  ;get base offset
       add     l       ;get column
       ret
getvalr:
       lda     rcbase  ;get base offset
       add     h       ;get row
       ret
getval2:
       ani     1       ;look at lsb
       jz      getvalc
       jmp     getvalr
;
; Store A as next value
;
putval:
       mov     c,a     ;save value
       lda     rcorder ;get order flag
       ora     a       ;already output the first value?
       jm      putval2
       ani     1       ;look at lsb
       jz      putvalr ;if 0, row first
putvalc:
       mov     l,c     ;set column
       ret
putvalr:
       mov     h,c     ;set row
       ret
putval2:
       ani     1       ;look at lsb
       jz      putvalc
       jmp     putvalr
;
; Output A as decimal digit char
;   B=Quantity to Subtract from A, C=0 if no leading zero
;
digout:
       push    d       ;save DE
       mvi     d,'0'   ;char
decot1:
       sub     b       ;subtract
       jc      decot2
       inr     d       ;increment char
       jmp     decot1
decot2:
       add     b       ;add back in
       push    psw     ;save result
       mov     a,d     ;get digit
       cpi     '0'     ;zero?
       jnz     decot3
       mov     a,c     ;get zero flag
       ora     a       ;0=no zero
       jz      decot4
decot3:
       mov     a,d     ;get digit
       call    cout    ;print it
decot4:
       pop     psw     ;get A
       pop     d       ;restore DE
       ret
;
; GXY Buffers
;
rcorder:
       ds      1       ;0=row/col, else col/row
rcbase:
       ds      1       ;0=org is 0,0, else org is 1,1
cmdelay:
       ds      1       ;number of milliseconds to delay for CM

;
; Begin Standout Mode
;       Return with A=0 and Zero Flag Set if not done
;
stndout:
       push    b
       push    d
       push    h       ;save regs
       lxi     h,z3env+80h     ;pt to environment
       mov     a,m     ;no terminal?
       cpi     ' '+1
       jc      err
       lxi     d,17h   ;pt to cls string
       dad     d
       mvi     d,0     ;no delay
       call    vidskp  ;skip over CL string
       call    vidskp  ;skip over CM string
       call    vidskp  ;skip over CE string
       mov     a,m     ;get first char of SO string
       ora     a       ;if no string, error
       jz      err
       call    vidout  ;output string with delay
       jmp     noerr

;
; Terminate Standout Mode
;       Return with A=0 and Zero Flag Set if not done
;
stndend:
       push    b
       push    d
       push    h       ;save regs
       lxi     h,z3env+80h     ;pt to environment
       mov     a,m     ;no terminal?
       cpi     ' '+1
       jc      err
       lxi     d,17h   ;pt to cls string
       dad     d
       mvi     d,0     ;no delay
       call    vidskp  ;skip over CL string
       call    vidskp  ;skip over CM string
       call    vidskp  ;skip over CE string
       call    vidskp  ;skip over SO string
       mov     a,m     ;get first char of SE string
       ora     a       ;if no string, error
       jz      err
       call    vidout  ;output string with delay
       jmp     noerr

;
; Initialize Terminal
;       Affect No Registers
;
tinit:
       push    h       ;save regs
       push    d
       push    psw
       lxi     h,z3env+80h     ;pt to environment
       mov     a,m     ;no terminal?
       cpi     ' '+1
       jc      tid
       lxi     d,17h   ;pt to cls string
       dad     d
       mvi     d,0     ;no delay
       call    vidskp  ;skip over CL string
       call    vidskp  ;skip over CM string
       call    vidskp  ;skip over CE string
       call    vidskp  ;skip over SO string
       call    vidskp  ;skip over SE string
       mov     a,m     ;get first char of TI string
       ora     a       ;if no string, error
       jz      tid
       call    vidout  ;output string with delay
tid:
       pop     psw     ;done
       pop     d
       pop     h
       ret

;
; De-Initialize Terminal
;       Affect No Registers
;
dinit:
       push    h       ;save regs
       push    d
       push    psw
       lxi     h,z3env+80h     ;pt to environment
       mov     a,m     ;no terminal?
       cpi     ' '+1
       jc      tid
       lxi     d,17h   ;pt to cls string
       dad     d
       mvi     d,0     ;no delay
       call    vidskp  ;skip over CL string
       call    vidskp  ;skip over CM string
       call    vidskp  ;skip over CE string
       call    vidskp  ;skip over SO string
       call    vidskp  ;skip over SE string
       call    vidskp  ;skip over TI string
       mov     a,m     ;get first char of TE string
       ora     a       ;if no string, error
       jz      tid
       call    vidout  ;output string with delay
       jmp     tid

;
;  VIDOUT - Output video string pted to by HL
;       Output also a delay contained in the D register
;
vidout:
       mov     a,m     ;get next char
       ora     a       ;done if zero
       jz      vid2
       inx     h       ;pt to next
       cpi     '\'     ;literal value?
       jnz     vid1
       mov     a,m     ;get literal char
       inx     h       ;pt to after it
vid1:
       call    cout    ;output char
       jmp     vidout
vid2:
       mov     a,d     ;output delay and fall thru to VIDELAY

;
;       VIDELAY pauses for the number of milliseconds indicated by the A
; register.  VIDELAY assumes a ZCPR3 environment and uses it to determine
; processor speed.
;
videlay:
       push    psw     ;save regs
       push    b
       push    d
       push    h
       mov     c,a     ;save count in C
       ora     a       ;no delay?
       jz      done
       lxi     h,z3env ;pt to environment
       lxi     d,2Bh   ;offset to processor speed
       dad     d
       mov     a,m     ;get processor speed
       ora     a       ;zero?
       jnz     vidl1
       mvi     a,4     ;assume 4 MHz
vidl1:
       mov     b,a     ;processor speed in B
vidl2:
       push    b       ;delay 1 ms
       call    delay
       pop     b
       dcr     c       ;count down
       jnz     vidl2
done:
       pop     h       ;restore regs
       pop     d
       pop     b
       pop     psw
       ret
;
;  Delay 1 ms at Clock speed
;
delay:
       call    del1    ;delay 1 ms at 1MHz
       dcr     b       ;count down clock speed
       jnz     delay
       ret
;
;  Delay 1 ms at 1MHz
;
del1:
       mvi     c,20    ;20 loops of 51 cycles each ~ 1000 cycles
del1a:
       xthl            ;18 cycles
       xthl            ;+18 = 36 cycles
       dcr     c       ;+ 5 = 41 cycles
       jnz     del1a   ;+10 = 51 cycles
       ret

;
;  VIDSKP - Skip over video string pted to by HL; pt to byte after string
;
vidskp:
       mov     a,m     ;get next char
       inx     h       ;pt to next
       ora     a       ;done if zero
       rz
       cpi     '\'     ;literal value?
       jnz     vidskp  ;continue if not
       inx     h       ;pt to after literal value
       jmp     vidskp

;
; Print A as 2 Hex Chars
;
pa2hc:
       push    psw
       push    b
       mov     b,a     ;value in B
       rlc
       rlc
       rlc
       rlc
       call    pa2hc1
       mov     a,b     ;get value
       call    pa2hc1
       pop     b
       pop     psw
       ret
pa2hc1:
       ani     0fh
       adi     '0'     ;to ASCII
       cpi     '9'+1
       jc      pa2hc2
       adi     7       ;to letter
pa2hc2:
       jmp     cout

;
; Skip Spaces
;
sksp:
       mov     a,m     ;skip to non-space
       cpi     ' '
       rnz
       inx     h
       jmp     sksp

;
;EDITOR BUFFERS
;
BLOCK:
       DS      2       ;ADDRESS OF CURRENT BLOCK
BUFFER:
       DS      2       ;PTR TO FREE SPACE
EINDEX:
       DS      1       ;INDEX ENTRY
EDRUN:
       DS      1       ;FLAG SAYING THAT EDITOR IS RUNNING

       end