;
;               EDUCATOR-8080
;
;Originally written by the Digital Group
;Converted to run on CP/M BY Keith Petersen,
;W8SDZ, July 28, 1980.
;
BKSPC   EQU     08H     ;BACKSPACE
CR      EQU     0DH     ;CARRIAGE RETURN
LF      EQU     0AH     ;LINE FEED
CLR     EQU     1AH     ;SCREEN CLEAR CHARACTER
DEL     EQU     7FH     ;DELETE (RUBOUT)
;
       ORG     100H
;
;THE CONTROL ROUTINE IS THE TOP OF THE STRUCTURE AND
;CONTROLS THE OPERATION OF THE ENTIRE PROGRAM.
;
CNTRL:  LXI     SP,STACK ;SET STACK POINTER
       LHLD    1       ;GET CBIOS POINTER
       LXI     D,3     ;READY FOR DAD
       DAD     D       ;CONSTAT
       DAD     D       ;CONIN
       SHLD    VCONIN+1 ;MODIFY CALL ADDRESS
       DAD     D       ;CONOUT
       SHLD    VCONOUT+1 ;MODIFY CALL ADDRESS
;
NOTZER: CALL    DSPLY   ;DISPLAY CONTENTS OF REGISTERS
       CALL    CMDNT   ;ENTER A COMMAND
       CALL    FETCH   ;FETCH THE CORRECT OPCODE
       ORA     A       ;SET ZERO FLAG AS PER CONTENTS
       JNZ     NOTZER  ;JUMP IF NOT ZERO ERROR OCCURED
       CALL    XQTER   ;GO EXECUTE THE CURRENT COMMAND
       JMP     NOTZER  ;LOOP FOREVER
;
;THIS DISPLAY ROUTINE CONTROLS THE GENERATION OF THE DYNAMIC
;DISPLAY.
;
DSPLY:  LXI     H,TITLS ;LOAD ADDRESS OF TITLES INTO HL
       CALL    CHEDT   ;DISPLAY TITLES
       LXI     H,BLINE ;LOAD ADDR OF BLINE TITLE
       LDA     BREG    ;LOAD CONTENTS OF BREG INTO A
       CALL    DSPCV   ;CONVERT AND DISPLAY
       LXI     H,CLINE ;LOAD ADDR OF CLINE TITLE
       LDA     CREG    ;LOAD CONTENTS OF CREG INTO A
       CALL    DSPCV   ;CONVERT AND DISPLAY
       LXI     H,AFHDR ;LOAD ADDR OF A FLAGS TITLE
       CALL    CHEDT   ;DISPLAY TITLES
       LHLD    PSWA    ;LOAD FLAGS AND A INTO HL
       MOV     A,L     ;MOVE FLAGS TO A
       ANI     4       ;AND OFF ALL BUT PARITY FLAG
       CALL    DSPFG   ;DISPLAY THE FLAG VALUE
       MOV     A,L     ;MOVE FLAGS TO A
       ANI     40H     ;AND OFF ALL BUT ZERO FLAG
       CALL    DSPFG   ;DISPLAY THE FLAG VALUE
       MOV     A,L     ;MOVE FLAGS TO A
       ANI     80H     ;AND OFF ALL BUT SIGN FLAG
       CALL    DSPFG   ;DISPLAY THE FLAG VALUE
       MOV     A,L     ;MOVE FLAGS TO A
       ANI     10H     ;AND OFF ALL BUT AUX. CARRY FLAG
       CALL    DSPFG   ;DISPLAY THE FLAG VALUE
       MOV     A,L     ;MOVE FLAGS TO A
       ANI     1       ;AND OFF ALL BUT CARRY FLAG
       CALL    DSPFG   ;DISPLAY THE FLAG VALUE
       MOV     A,H     ;MOVE A REGISTER VALUE TO A
       CALL    DSPCN   ;DISPLAY WITH NO TITLE PRINT
       RET             ;RETURN TO THE CNTRL ROUTINE
;
;THE DISPLAY CONVERSION ROUTINE PRINTS BINARY,
;OCTAL AND HEXADECIMAL.
;
DSPCV:  PUSH    PSW     ;SAVE OUTPUT VALUE FOR CHEDT
       CALL    CHEDT   ;DISPLAY LINE TITLE ADDR IN HL
       POP     PSW     ;RETRIEVE SAVED OUTPUT VALUE
;
DSPCN:  MVI     E,8     ;MOVE 8 TO E REGISTER
;
DSPBT:  RLC             ;ROTATE MSB INTO CARRY AND LSB
       PUSH    PSW     ;SAVE CURRENT VALUE
       ANI     1       ;AND OFF ALL BUT LSB
       CALL    DSPFG   ;GO DISPLAY BIT VALUE
       POP     PSW     ;RETRIEVE SAVED CURRENT VALUE
       DCR     E       ;DECREMENT LOOP COUNT
       JNZ     DSPBT   ;JUMP IF LOOP COUNT NOT ZERO
       ORA     A       ;RESET CARRY
       MVI     E,3     ;MOVE 3 TO E REGISTER
;
DSPQT:  RAL             ;MSB TO CARRY, CARRY TO LSB
       RAL             ;DO IT AGAIN,
       RAL             ; THREE TIMES FOR OCTAL DIGIT
       PUSH    PSW     ;SAVE CURRENT VALUE
       ANI     7       ;AND OFF ALL BUT OCTAL LSD
       ORI     '0'     ;MAKE ASCII
       CALL    CHRPR   ;OUTPUT THE CHARACTER
       POP     PSW     ;RETRIEVE SAVED CURRENT VALUE
       DCR     E       ;DECREMENT LOOP COUNT
       JNZ     DSPQT   ;JUMP IF LOOP COUNT NOT ZERO
       CALL    DSPSP   ;OUTPUT A SPACE
       MVI     E,2     ;MOVE 2 TO E
;
DSPHT:  RLC             ;ROTATE MSB INTO CARRY AND LSB
       RLC             ;DO IT AGAIN
       RLC             ; FOUR TIMES FOR
       RLC             ;  HEXADECIMAL SHIFT
       PUSH    PSW     ;SAVE CURRENT VALUE
       ANI     0FH     ;AND OFF ALL BUT HEX LSD
       ADI     '0'     ;ADD ON BITS TO MAKE ASCII
       CPI     '9'+1   ;COMPARE RESULT TO ONE MORE THAN 9
       JC      DSPHS   ;IF NUMERIC THEN SKIP ADJUSTMENT
       ADI     7       ;ADD 7 GIVING ASCII A-F CODES
;
DSPHS:  CALL    CHRPR   ;OUTPUT THE CHARACTER
       POP     PSW     ;RETRIEVE SAVED CURRENT VALUE
       NOP
       DCR     E       ;DECREMENT LOOP COUNT
       JNZ     DSPHT   ;JUMP IF LOOP COUNT NOT ZERO
       RET             ;RETURN TO CALLING ROUTINE
;
;DISPLAY FLAG OR BINARY DIGIT FOLLOWED BY A SPACE.  ALTERNATE
;ENTRY IS USED TO DISPLAY A SPACE.
;
DSPFG:  JZ      DSPFZ   ;JUMP IF PASSED VALUE IS A ZERO
       MVI     A,1     ;OTHERWISE MOVE A 1 INTO A
;
DSPFZ:  ADI     '0'     ;CONVERT INTO ASCII NUMERIC CHAR.
       CALL    CHRPR   ;OUTPUT THE CHARACTER
;
DSPSP:  PUSH    PSW     ;SAVE THE FLAGS AND VALUE IN A
       MVI     A,' '   ;MOVE SPACE INTO A
       CALL    CHRPR   ;OUTPUT THE SPACE
       POP     PSW     ;RETRIEVE THE SAVED FLAGS AND A
       RET             ;RETURN TO THE CALLING ROUTINE
;
;THE CHARACTER STRING OUTPUT EDIT ROUTINE.
;
CHEDT:  MOV     A,M     ;MOVE NEXT CHARACTER INTO A
       CPI     80H     ;COMPARE IT TO 200 OCTAL
       RZ              ;RETURN IF EQUAL IT'S END OF STRING
       JNC     CHSPA   ;JUMP IF GREATER FOR SPACE ROUTINE
       CALL    CHRPR   ;ELSE GO OUTPUT THE CHARACTER
;
CHEND:  INX     H       ;INCREMENT THE STRING INDEX
       JMP     CHEDT   ;LOOP FOR NEXT CHARACTER
;
CHSPA:  SUI     80H     ;SUBTRACT 200 OCTAL FROM VALUE
       MOV     B,A     ;MOVE SPACE COUNT TO B
;
CHSPL:  MVI     A,' '   ;MOVE SPACE INTO A
       CALL    CHRPR   ;OUTPUT THE SPACE
       DCR     B       ;DECREMENT SPACE COUNT
       JNZ     CHSPL   ;JUMP IF COUNT NOT ZERO TO START OF LOOP
       JMP     CHEND   ;JUMP BACK INTO CHEDT LOOP
;
;THE COMMAND ENTRY ROUTINE ACCEPTS INPUT FROM THE KEYBOARD
;FOR COMMANDS.
;
CMDNT:  LXI     H,CMDMS ;MOVE ADDRESS OF COMMAND? INTO HL
       CALL    CHEDT   ;DISPLAY THE MESSAGE
       LXI     H,CMDAR ;MOVE ADRS OF COMMAND INPUT AREA HL
       MVI     B,22    ;MOVE MAXIMUM LENGTH TO B
;
CMDKB:  CALL    KEYBD   ;GET AN INPUT CHARACTER
       CPI     'L'-40H ;IS IT A CONTROL-L LINE DELETE?
       JZ      CNTRL   ;IF SO THEN RESTART PROGRAM
       CPI     CR      ;IS IT A CARRIAGE RETURN?
       JZ      CMDND   ;IF SO THEN GO COMPRESS INPUT
       CPI     DEL     ;IS IT A DELETE CHARACTER?
       JNZ     CMDST   ;IF NOT THEN GO STORE THE CHARACTER
       MVI     A,BKSPC ;IF SO REPLACE WITH BACKSPACE
;
CMDST:  MOV     M,A     ;STORE INPUT CHAR. IN COMMAND BUFFER
       CALL    CHRPR   ;DISPLAY THE INPUT CHARACTER
       INX     H       ;INCREMENT COMMAND WORK AREA INDEX
       DCR     B       ;DECREMENT COMMAND LENGTH COUNT
       JNZ     CMDKB   ;IF NOT FULL THEN REITERATE
       MVI     A,1     ;IF BUFFER FULL THEN SELECT ERROR
       CALL    ERROR   ; NUMBER 1 AND PRINT ITS MSG.
       JMP     CNTRL   ;RESTART THE PROGRAM
;
;THE COMMAND COMPRESS ROUTINE ELIMINATES ALL BUT LETTERS
;AND NUMBERS.
;
CMDND:  LXI     H,CMDAR ;LOAD HL WITH ADRS OF WORK AREA
       PUSH    H       ;PUSH & POP MOVE IT TO DE
       POP     D       ; AS THE COMPRESSION POINTER
       MVI     A,22    ;LOAD A WITH MAXIMUM LENGTH
       SUB     B       ;SUBTRACT REMAINING LENGTH FROM B
       MOV     B,A     ;MOVE ACTUAL LENGTH TO B
;
CMDNX:  MOV     A,M     ;MOVE COMMAND CHARACTER TO A
       CPI     BKSPC   ;IS IT BACKSPACE?
       JNZ     CMDCH   ;IF NOT THEN GO TO OTHER TESTS
       MVI     A,CMDAR AND 0FFH ;LOW ADRS BYTE OF CMDAR TO A
       CMP     E       ;COMPARE TO CURRENT LOW ADRS BYTE
       JNC     CMDNS   ;IF NOT GREATER THEN SKIP SAVE
       DCX     D       ;ELSE BACK UP COMPRESSION POINTER
       JMP     CMDNS   ;SKIP SAVING THE CHARACTER
;
CMDCH:  CPI     '0'     ;IS THE CHARACTER LESS THAN '0'?
       JC      CMDNS   ;IF SO THEN SKIP SAVING IT
       CPI     '9'+1   ;IS THE CHARACTER LESS THAN '9'+1?
       JC      CMDSV   ;IF SO THEN SAVE NUMERIC VALUE
       CPI     'A'     ;IS THE CHARACTER LESS THAN 'A'?
       JC      CMDNS   ;IF SO THEN SKIP SAVING IT
       CPI     'Z'+1   ;IS THE CHARACTER GREATER THAN 'Z'?
       JNC     CMDNS   ;IF SO THEN SKIP SAVING IT
;
CMDSV:  STAX    D       ;STORE CHARACTER IN COMPRESSED AREA
       INX     D       ;INCREMENT COMPRESSION POINTER INDEX
;
CMDNS:  INX     H       ;INCREMENT INPUT STRING POINTER
       DCR     B       ;DECREMENT ACTUAL LENGTH COUNT
       JNZ     CMDNX   ;IF LENGTH IS NOT ZERO THEN REITERATE
       RET             ;ELSE RETURN TO CNTRL CALLING POINT
;
;THE FETCH INSTRUCTION/COMMAND ROUTINE VALIDATES AND BUILDS THE
;OBJECT CODE.
;
FETCH:  LXI     H,OPTAB ;LOAD ADDRESS OF OPCODE TABLE HL
       MVI     E,31    ;MOVE TABLE ELEMENT COUNT TO E
;
FLOOP:  PUSH    H       ;SAVE CURRENT ELEMENT ADDRESS
       LXI     B,CMDAR ;LOAD ADDRESS OF CMDAR INTO BC
       MVI     D,3     ;MOVE OPCODE LENGTH TO D
;
FCOMP:  LDAX    B       ;LOAD COMMAND CHARACTER TO A INDEXED BY B
       CMP     M       ;COMPARE IT TO TABLE CHARACTER
       JNZ     FNXEL   ;IF NOT EQUAL THEN GO TO NEXT ELEMENT
       INX     B       ;INCREMENT COMMAND CHARACTER INDEX
       INX     H       ;INCREMENT TABLE CHARACTER INDEX
       DCR     D       ;DECREMENT OPCODE LENGTH COUNTER
       JNZ     FCOMP   ;IF NOT ZERO CONTINUE TEST LOOP
       XTHL            ;EXCHANGE HL WITH TOP OF STACK
       POP     H       ;POP HL FROM STACK TO CLEAR IT
       MOV     E,M     ;MOVE NAKED OPCODE TO E,D IS ZERO
       PUSH    D       ;SAVE NAKED OPCODE
       INX     H       ;INCREMENT TABLE POINTER
       MOV     E,M     ;DECODE ROUTINE LOW ADDRESS BYTE TO E
       INX     H       ;INCREMENT TABLE POINTER
       MOV     D,M     ;DECODE ROUTINE HIGH ADDRESS BYTE TO D
       XCHG            ;MOVE DECODE ROUTINE ADDRESS TO HL
       POP     D       ;UNSAVE NAKED OPCODE TO DE
       XRA     A       ;CLEAR A, NO ERROR CODE
       PCHL            ;JUMP TO ADDRESS OF DECODE ROUTINE
;
FNXEL:  LXI     B,6     ;LOAD DOUBLE LENGTH 6 INTO BC
       POP     H       ;UNSAVE CURRENT ELEMENT ADDRESS
       DAD     B       ;ADD 6 TO IT
       DCR     E       ;DECREMENT TABLE ELEMENT COUNT
       JNZ     FLOOP   ;REITERATE TO TEST NEXT ELEMENT
       MVI     A,2     ;MOVE ERROR CODE 2 TO A
       JMP     ERROR   ;GO DISPLAY ERROR 2, OPCODE UNKNOWN
       NOP             ;NO OPERATION FILLER
;
;THE INSTRUCTION DECODER ROUTINES FOLLOW
;INSTRUCTIONS USING THE DIRCT ROUTINE REQUIRE NO DECODING.
;EXAMPLE: RAL, CMA, ETC.
;
DIRCT:  RET             ;RETURN TO CNTRL FOR EXECUTION
;
;THE MOVRT IS USED ONLY BE THE MOV COMMAND
;
MOVRT:  CALL    RG543   ;VALIDATE DESTINATION REGISTER
       ORA     A       ;SET FLAGS BASED ON A CONTENTS
       RNZ             ;RETURN NOT ZERO WITH ERROR
;
;INSTRUCTIONS USING THE RG210 ROUTINE REQUIRE A SOURCE REGISTER
;
RG210:  LDAX    B       ;LOAD NEXT COMMAND CHARACTER INTO A
       INX     B       ;INCREMENT COMMAND CHARACTER INDEX
       CALL    REGAN   ;ANALYZE FOR VALID REGISTER
       JNC     RGERR   ;IF CY=0 THEN REGISTER NOT VALID
       ADD     E       ;ADD NAKED OPCODE TO REGISTER VALUE
       MOV     E,A     ;MOVE RESULT BACK TO E
       XRA     A       ;CLEAR A INDICATING NO ERRORS
       RET             ;RETURN TO CNTRL
;
;THE REGISTER ERROR ROUTINE IS USED TO INDICATE REGISTER
;DESIGNATION ERRORS
;
RGERR:  MVI     A,3     ;MOV ERROR CODE 3 TO A
       JMP     ERROR   ;GO DISPLAY ERROR 3, INVALID REGISTER
;
;THE REGISTER ANALYSIS AND VALIDATION ROUTINE IS USED BY RG543,
;RG210, AND RG54B
;
REGAN:  SUI     'A'     ;SUBTRACT AN 'A' FROM THE CHARACTER
       CPI     3       ;COMPARE THE RESULT TO 3
       RNC             ;IF NOT LESS THAN 3 RETURN WITH CY=0
       DCR     A       ;DECREMENT RESULT: A=377,B=000,C=001
       ANI     7       ;AND OFF ALL BUT OCTAL LSD
       STC             ;SET CY=1 INDICATING NO ERROR
       RET             ;RETURN TO CALLING ROUTINE
;
;THE MVIRT IS USED ONLY BY THE MVI COMMAND
;
MVIRT:  CALL    RG543   ;VALIDATE DESTINATION REGISTER
       ORA     A       ;SET FLAGS BASED ON A CONTENTS
       RNZ             ;RETURN NOT ZERO WITH ERROR
;
;INSTRUCTIONS REQUIRING AN IMMEDIATE OPERAND USE THE IMMED
;ROUTINE
;
IMMED:  LDAX    B       ;LOAD NEXT COMMAND CHARACTER INTO A
       INX     B       ;INCREMENT COMMAND CHARACTER INDEX
       CPI     'B'     ;IS THE CMND CHAR. A 'B' ?
       JZ      BINRY   ;IF SO THEN PROCESS AS BINARY
       CPI     'Q'     ;IS THE COMMAND CHARACTER A 'Q'?
       JZ      OCTAL   ;IF SO THEN PROCESS AS OCTAL
       CPI     'H'     ;IS THE CMND CHAR. AN 'H' ?
       JZ      HEX     ;IF SO THEN PROCESS AS HEXADECIMAL
       CPI     '8'     ;IS THE COMMAND CHARACTER LESS THAN '8'?
       JC      OCTAD   ;IF SO THEN TREAT AS OCTAL
       MVI     A,5     ;MOVE ERROR CODE 5 TO A
       JMP     ERROR   ;GO DISPLAY ERROR 5, INVALID IMMEDIATE
;
;INSTRUCTIONS USING THE RG543 ROUTINE REQUIRE A DESTINATION
;REGISTER
;
RG543:  LDAX    B       ;LOAD NEXT COMMAND CHARACTER INTO A
       INX     B       ;INCREMENT COMMAND CHARACTER INDEX
       CALL    REGAN   ;ANALYZE FOR VALID REGISTER
       JNC     RGERR   ;IF CY=0 THEN REGISTER NOT VALID
       RLC             ;SHIFT OCTAL REGISTER VALUE
       RLC             ;LEFT THREE
       RLC             ; PLACES
       ADD     E       ;ADD NAKED OPCODE TO SHIFTED VALUE
       MOV     E,A     ;MOVE RESULT BACK TO E
       XRA     A       ;CLEAR A INDICATING NO ERRORS
       RET             ;RETURN TO CALLING ROUTINE
;
;INSTRUCTIONS USING THE RG54B ROUTINE ARE INX AND DCX
;
RG54B:  LDAX    B       ;LOAD NEXT COMMAND CHARACTER INTO A
       INX     B       ;INCREMENT COMMAND CHARACTER INDEX
       CALL    REGAN   ;ANALYZE FOR VALID REGISTER
       CPI     0       ;IS THE REGISTER A ZERO?
       RZ              ;IF SO IT'S 'B' SO RETURN
       MVI     A,4     ;MOVE ERROR CODE 4 TO A
       JMP     ERROR   ;GO DISPLAY ERROR 4, INVALID REGISTER
;
;THE BINARY ROUTINE CONVERTS A BINARY IMMEDIATE VALUE INTO
;USABLE FORM
;
BINRY:  MVI     H,8     ;MOVE 8 TO H FOR COUNT
;
BLOOP:  LDAX    B       ;LOAD NEXT COMMAND CHARACTER INTO A
       SUI     '0'     ;SUBTRACT A '0' FROM IT
       CPI     2       ;IS THE RESULT LESS THAN 2?
       JNC     IMMER   ;IF NOT THEN GO DISPLAY IMMEDIATE ERROR
       PUSH    H       ;SAVE THE COUNT
       MOV     L,D     ;MOVE D TO L (IMMEDIATE BYTE)
       DAD     H       ;SHIFT HL LEFT ONE BIT
       ADD     L       ;ADD L TO BIT IN A
       MOV     D,A     ;MOVE THE RESULT BACK TO D
       POP     H       ;UNSAVE THE COUNT
       INX     B       ;INCREMENT COMMAND CHARACTER INDEX
       DCR     H       ;DECREMENT THE COUNT
       JNZ     BLOOP   ;IF NOT ZERO THEN REITERATE
       XRA     A       ;CLEAR A INDICATING NO ERRORS
       RET             ;RETURN TO CNTRL
;
;THE IMMEDIATE ERROR ROUTINE IS USED TO INDICATE IMMEDIATE VALUE
;ERRORS
;
IMMER:  MVI     A,6     ;MOVE ERROR CODE 3 TO A
       JMP     ERROR   ;GO DISPLAY ERROR 3, INVALID IMMEDIATE
;
;THE OCTAD ENTRY POINT TO THE OCTAL ROUTINE IS FOR THE DEFAULT
;CONDITION
;
OCTAD:  DCX     B       ;DECREMENT COMMAND CHARACTER INDEX
;
;THE OCTAL ROUTINE CONVERTS AN OCTAL IMMEDIATE VALUE INTO
;USABLE FORM
;
OCTAL:  MVI     H,3     ;MOVE A 3 INTO H FOR COUNT
;
OLOOP:  LDAX    B       ;LOAD NEXT COMMAND CHARACTER INTO A
       SUI     '0'     ;SUBTRACT A '0' FROM IT
       CPI     8       ;IS CMND LESS THAN 8?
       JNC     IMMER   ;IF NOT GO DISP IMMED ERROR
       PUSH    H       ;SAVE THE COUNT
       MOV     L,D     ;MOVE D TO L IMMED BYTE
       DAD     H       ;SHIFT IMMEDIATE
       DAD     H       ;  BYTE LEFT
       DAD     H       ;    THREE BITS
       ADD     L       ;ADD L TO VALUE IN A
       MOV     D,A     ;MOVE RESULT BACK TO D
       POP     H       ;UNSAVE THE COUNT
       INX     B       ;INCREMENT COMND CHAR INDEX
       DCR     H       ;DECREMENT THE COUNT
       JNZ     OLOOP   ;IF NOT ZERO THEN REITERATE
       XRA     A       ;CLEAR A INDICATING NO ERRORS
       RET             ;RETURN TO CNTRL
;
;THE HEX ROUTINE COVERTS A HEXADECIMAL IMMEDIATE VALUE INTO
;USABLE FORM.
;
HEX:    MVI     H,2     ;MOVE A 2 INTO H FOR COUNT
;
HLOOP:  LDAX    B       ;LOAD NEXT CMND CHAR INTO A
       SUI     '0'     ;SUBTRACT '0' FROM IT
       CPI     9+1     ;IS IT LESS THAN '9'+1?
       JC      HCHOK   ;IF SO THEN NUMERIC CHARACTER IS OK
       SUI     7       ;ELSE CONVERT ALPHABETIC TO NUMERIC
       CPI     15+1    ;IS CHARACTER VALUE GREATER THAN 15?
       JNC     IMMER   ;IF SO THAN INVALID HEXADECIMAL VALUE
;
HCHOK:  PUSH    H       ;SAVE THE COUNT
       MOV     L,D     ;MOVE D TO L IMMEDIATE BYTE
       DAD     H       ;SHIFT IMMEDIATE
       DAD     H       ; BYTE LEFT
       DAD     H       ;  FOUR
       DAD     H       ;   BITS
       ADD     L       ;ADD L TO VALUE IN A
       MOV     D,A     ;MOVE RESULT BACK TO D
       POP     H       ;UNSAVE THE COUNT
       INX     B       ;INCREMENT COMMAND CHARACTER INDEX
       DCR     H       ;DECREMENT THE COUNT
       JNZ     HLOOP   ;IF NOT ZERO THAN REITERATE
       XRA     A       ;CLEAR A INDICATING NO ERRORS
       RET             ;RETURN TO CNTRL
;
;THE XQTER ROUTINE EXECUTES THE GENERATED OBJECT CODE FOR
;EDUCATOR 8080
;
XQTER:  XCHG            ;MOVE GENERATED OPCODE TO HL
       SHLD    XQTOP   ;STORE IT AT EXECUTION POINT
       LHLD    PSWA    ;LOAD WORKING PSW & A INTO HL
       PUSH    H       ;PUSH & POP SETS VALUES FOR
       POP     PSW     ; WORKING REGISTER AND FLAGS
       LHLD    BANDC   ;LOAD WORKING B AND C INTO HL
       PUSH    H       ;PUSH & POP SETS VALUES FOR
       POP     B       ; WORKING B AND C REGISTERS
;
XQTOP:  NOP             ;THE COMMAND TO BE EXECUTED
       NOP             ;IMMEDIATE VALUE OR NOP
       PUSH    B       ;PUSH B AND C WORKING REGISTER VALUES
       POP     H       ;POP THEM INTO HL
       SHLD    BANDC   ;STORE THEM IN SAVE AREA
       PUSH    PSW     ;PUSH PSW AND A WORKING VALUES
       POP     H       ;POP THEM INTO HL
       SHLD    PSWA    ;STORE THEM IN SAVE AREA
       RET             ;RETURN TO CNTRL FOR NEXT COMMAND
;
;THE ERROR ROUTINE IS USED TO DISPLAY ERROR MESSAGES
;
ERROR:  PUSH    PSW     ;SAVE ERROR CODE IN A
       LXI     H,ERRSP ;LOAD ADDRESS OF ERROR HEADER SPACES
       CALL    CHEDT   ;GO OUTPUT ERROR HEADER SPACES
       POP     PSW     ;UNSAVE ERROR CODE
       LXI     H,ERTAB ;LOAD ADDRESS OF ERROR MESSAGE TABLE
       ADD     L       ;ADD LOW ADDRESS BYTE TO ERROR CODE
       MOV     L,A     ;MOVE RESULT TO L, POINTS TO OFFSET
       MOV     L,M     ;MOVE OFFSET TO L
;
;NOTE: HL NOW CONTAINS THE ADDRESS OR THE ERROR MESSAGE
;
       CALL    CHEDT   ;OUTPUT THE ERROR MESSAGE
;
ERTIM:  LXI     D,0     ;LOAD DE WITH TIMING LOOP VALUE
       DCR     E       ;DECREMENT VALUE IN E 256 TIMES
       JNZ     ERTIM+1 ;REITERATE LOOP 256 TIMES
;
;THE ABOVE JMP GOES TO THE FIRST 000 IN THE LXI COMMAND WHICH
;IS AN EFFECTIVE NOP
;
       DCR     D       ;DECREMENT D
       JNZ     ERTIM+1 ;REITERATE OUTER LOOP 256 TIMES
       MVI     A,0FFH  ;MOVE A 377 TO A INDICATING ERROR
       RET             ;RETURN TO CNTRL
;
OPTAB:  DB      'ACI',0CEH
       DW      IMMED
       DB      'ADC',88H
       DW      RG210
       DB      'ADD',80H
       DW      RG210
       DB      'ADI',0C6H
       DW      IMMED
       DB      'ANA',0A0H
       DW      RG210
       DB      'ANI',0E6H
       DW      IMMED
       DB      'CMA',2FH
       DW      DIRCT
       DB      'CMC',3FH
       DW      DIRCT
       DB      'CMP',0B8H
       DW      RG210
       DB      'CPI',0FEH
       DW      IMMED
       DB      'DAA',27H
       DW      DIRCT
       DB      'DCR',05H
       DW      RG543
       DB      'DCX',0BH
       DW      RG54B
       DB      'INR',04H
       DW      RG543
       DB      'INX',03H
       DW      RG54B
       DB      'MOV',40H
       DW      MOVRT
       DB      'MVI',06H
       DW      MVIRT
       DB      'NOP',00H
       DW      DIRCT
       DB      'ORA',0B0H
       DW      RG210
       DB      'ORI',0F6H
       DW      IMMED
       DB      'RAL',17H
       DW      DIRCT
       DB      'RAR',1FH
       DW      DIRCT
       DB      'RLC',07H
       DW      DIRCT
       DB      'RRC',0FH
       DW      DIRCT
       DB      'SBB',98H
       DW      RG210
       DB      'SBI',0DEH
       DW      IMMED
       DB      'STC',37H
       DW      DIRCT
       DB      'SUB',90H
       DW      RG210
       DB      'SUI',0D6H
       DW      IMMED
       DB      'XRA',0A8H
       DW      RG210
       DB      'XRI',0EEH
       DW      IMMED
;
ERTAB:  DB      ERR AND 0FFH,ITL AND 0FFH,ICM AND 0FFH
       DB      IRR AND 0FFH,IRR AND 0FFH,IIT AND 0FFH
       DB      IIV AND 0FFH,ERR AND 0FFH
;
ITL:    DB      'INPUT TOO LONG',80H
;
ICM:    DB      'INVALID COMMAND',80H
;
IRR:    DB      'INVALID REGISTER',80H
;
IIT:    DB      'INVALID IMMED TYPE',80H
;
IIV:    DB      'INVALID IMMED VALUE',80H
;
ERR:    DB      'ERROR!',80H
;
ERRSP:  DB      CR,LF,90H,80H
;
TITLS:  DB      CLR,CR,LF
       DB      9BH,'EDUCATOR-8080',CR,LF
       DB      C
R,LF,9AH,'<---'
       DB      'BINARY-----'
       DB      ' OCT HX',CR,LF
       DB      9AH,'7 6 5 4 3 2 1 0',80H
;
BLINE:  DB      CR,LF,CR,LF,90H
       DB      'B-REG >>  ',80H
;
CLINE:  DB      CR,LF,CR,LF,90H
       DB      'C-REG >>  ',80H
;
AFHDR:  DB      CR,LF,CR,LF,90H
       DB      'FLAGS & ACC',CR,LF,90H
       DB      'P Z S A C',CR,LF,90H,80H
;
CMDMS:  DB      CR,LF,CR,LF,90H
       DB      'COMMAND ? ',80H
;
;CHARACTER OUTPUT ROUTINE
;
CHRPR   PUSH    PSW
       PUSH    B
       PUSH    D
       PUSH    H
       MOV     C,A     ;CHARACTER TO C FOR CP/M
VCONOUT CALL    $-$     ;MODIFIED AT INIT
       POP     H
       POP     D
       POP     B
       POP     PSW
       RET
;
;KEYBOARD INPUT ROUTINE
;
KEYBD   PUSH    B
       PUSH    D
       PUSH    H
VCONIN  CALL    $-$     ;MODIFIED AT INIT
       POP     H
       POP     D
       POP     B
       CPI     'C'-40H ;CONTROL-C FOR EXIT?
       JZ      0       ;YES, REBOOT CP/M
       RET
;
;       DEFINE TEMPORARY STORAGE AREA
;
BANDC:                  ;B AND C REGISTERS
CREG:   DB      0
BREG:   DB      0
PSWA:   DW      0
CMDAR:  DS      40
STACK   EQU     $+60
;
       END