ASEG ORG 0100H ; -------------------------------------------------------------- ; The control routine is the top of the structure and controls the operation of the entire program. CNTRL: LXI SP,STACK ; Set stack pointer to programmable memory; 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 occurred; 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 00000100b ; AND off all but parity flag; CALL DSPFG ; Display the flag value; MOV A,L ; Move flags to A; ANI 01000000b ; AND off all but zero flag; CALL DSPFG ; Display the flag value; MOV A,L ; Move flags to A; ANI 10000000b ; AND off all but sign flag; CALL DSPFG ; Display the flag value; MOV A,L ; Move flags to A; ANI 00010000b ; AND off all but auxiliary carry flag; CALL DSPFG ; Display the flag value; MOV A,L ; Move flags to A; ANI 00000001b ; 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 fnd 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,010o ; Move 8 to E register; DSPBT: RLC ; Rotate MSB into Carry and LSB; PUSH PSW ; Save current value; ANI 001o ; 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,003o ; Move 3 to E register; DSPQT: RAL ; MSB to Carry. Carry to LSB. RAL ; do it again, RAL ; three times for octal digit shift; PUSH PSW ; Save current value; ANI 007o ; AND off all but octal LSD; ORI 060o ; OR on bits to make ASCII numeric character; 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,002o ; 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 00001111b ; AND off all but hexadecimal LSD; ADI 060o ; Add on bits to make ASCII numeric character CPI 072o ; Compare result to one more than 9; JC DSPHS ; If numeric then skip adjustment; ADI 007o ; Add 7 giving ASCI I 'A' thru '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,001o ; Otherwise move a 1 into A; DSPFZ: ADI 060o ; Convert into ASCII numeric character; CALL CHRPR ; Output the character; DSPSP: PUSH PSW ; Save the flags and value in A; MVI A,040o ; 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 200o ; 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 200o ; Subtract 200 octal from value; MOV B,A ; Move space count to B; CHSPL: MVI A,040o ; Move space to 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 ?' to HL; CALL CHEDT ; Display the message; LXI H,CMDAR ; Move address of command input area HL; MVI B,026o ; Move maximum length to B; CMDKB: CALL KEYBD ; Get an input character; CPI 014o ; Is it a control-1 line delete? JZ CNTRL ; If so then restart program; CPI 015o ; Is it a carriage return? JZ CMDND ; If so then go compress input; CPI 177o ; Is it a delete character? JNZ CMDST ; If not then go store the character; MVI A,033o ; If so replace with back arrow; CMDST: MOV M,A ; Store input character 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,001o ; If buffer full then select error CALL ERROR ; number 1 and print its message; JMP CNTRL ; Restart the program; ; The command compress routine eliminates all but letters and numbers. CMDND: LXI H,CMDAR ; Load HL with address of work area; PUSH H ; Push & pop move it to DE POP D ; as the compression pointer; MVI A,026o ; 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 033o ; Is it a back arrow (character delete)? JNZ CMDCH ; If not then go to other tests; MVI A,LOW CMDAR ; Low address byte of CMDAR to A; CMP E ; Compare to current low address byte; JNC CMDNS ; If not greater then skip save; DCX D ; Else back up compression pointer; JMP CMDNS ; Skip saving the character; CMDCH: CPI 060o ; Is the character less than '0'? JC CMDNS ; If so then skip saving it; CPI 072o ; Is the character less than '9' + 1? JC CMDSV ; If so then save numeric value; CPI 101o ; Is the character leu than 'A'? JC CMDNS ; If so then skip saving it; CPI 133o ; 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,037o ; Move table element count to E; FLOOP: PUSH H ; Save current element address; LXI B,CMDAR ; Load address of CMDAR into BC; MVI D,003o ; 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,000006o ; 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,002o ; 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 by the MOV command. MOVRT: CALL RG543 ; Validate destination register; ORA A ; Set flags based on A contents; RNZ ; Return not zero with error; ; Else fall thru to RG210; ; 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,003o ; Move 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 101o ; Subtract an 'A' from the character; CPI 003o ; 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 007o ; 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; ; Else fall thru to IMMED; ; 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 102o ; Is the command character a 'B'? JZ BINRY ; If so then process as binary; CPI 121o ; Is the command character a'Q'? JZ OCTAL ; If so then process as octal; CPI 110o ; Is the command character an 'H'? JZ HEX ; If so then process as hexadecimal; CPI 070o ; Is the command character less than '8'? JC OCTAD ; If so then treat as octal; MVI A,005o ; 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 000o ; Is the register a zero? RZ ; If so it's 'B' so return; MVI A,004o ; Move error code 4 to A; JMP ERROR ; Go display error 4, invalid register; ; The BINRY routine converts a binary immediate value into usable form. BINRY: MVI H,010o ; Move 8 to H for count; BLOOP: LDAX B ; Load next command character into A; SUI 060o ; Subtract a '0' from it; CPI 002o ; 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,006o ; Move error code 3 to A; JMP ERROR ; Go display error 3, invalid immediate; ; The OCTAD entry point to the OCTAL routine it 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,003o ; Move a 3 into H for count; OLOOP: LDAX B ; Load next command character into A; SUI 060o ; Subtract a '0' from it; CPI 010o ; Is command character less than 8? 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 immediate DAD H ; byte left DAD H ; three bits; ADD L ; Add L to value in A; MOV D,A ; Move result beck to D; POP H ; Unsave the count; INX B ; Increment command character 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 converts a hexadecimal immediate value into usable form. HEX: MVI H,002o ; Move a 2 into H for count; HLOOP: LDAX B ; Load next command character into A; SUI 060o ; Subtract a '0' from it; CPI 012o ; Is it less then '9' + 1? JC HCHOK ; If so then numeric character is OK; SUI 007o ; Else convert alphabetic to numeric; CPI 020o ; Is character value greater than 15? JNC IMMER ; If so then 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 then reiterate; XRA A ; Clear A indicating no errors; RET ; Return to CNTRL; ; The XQTER routine executes the generated object code for Educator-6080. 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 of the error message. CALL CHEDT ; Output the error message; ERTIM: LXI D,000000o ; 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,377o ; Move a 377 to A indicating error; RET ; Return to CNTRL; ; Note: for Teletype or hard copy output bytes <2>/104 thru <2>/116 can be replaced by 000 NOPs. OPTAB: DB 'ACI' DB 316o DW IMMED DB 'ADC' DB 210o DW RG210 DB 'ADD' DB 200o DW RG210 DB 'ADI' DB 306o DW IMMED DB 'ANA' DB 240o DW RG210 DB 'ANI' DB 346o DW IMMED DB 'CMA' DB 057o DW DIRCT DB 'CMC' DB 077o DW DIRCT DB 'CMP' DB 270o DW RG210 DB 'CPI' DB 376o DW IMMED DB 'DAA' DB 047o DW DIRCT DB 'DCR' DB 005o DW RG543 DB 'DCX' DB 013o DW RG54B DB 'INR' DB 004o DW RG543 DB 'INX' DB 003o DW RG54B DB 'MOV' DB 100o DW MOVRT DB 'MVI' DB 006o DW MVIRT DB 'NOP' DB 000o DW DIRCT DB 'ORA' DB 260o DW RG210 DB 'ORI' DB 366o DW IMMED DB 'RAL' DB 027o DW DIRCT DB 'RAR' DB 037o DW DIRCT DB 'RLC' DB 073o DW DIRCT DB 'RRC' DB 017o DW DIRCT DB 'SBB' DB 230o DW RG210 DB 'SBI' DB 336o DW IMMED DB 'STC' DB 067o DW DIRCT DB 'SUB' DB 220o DW RG210 DB 'SUI' DB 326o DW IMMED DB 'XRA' DB 250o DW RG210 DB 'XRI' DB 356o DW IMMED ; Error messages ERTAB: ; Address Offsets for messages 0 through 7 DB LOW ERR6 DB LOW ERR1 DB LOW ERR2 DB LOW ERR2 DB LOW ERR3 DB LOW ERR4 DB LOW ERR5 DB LOW ERR6 ERR1: DB 'INPUT TOO LONG',200o ERR2: DB 'INVALID COMMAND',200o ERR3: DB 'INVALID REGISTER',200o ERR4: DB 'INVALID IMMED TYPE',200o ERR5: DB 'INVALID IMMED VALUE',200o ERR6: DB 'ERROR!',200o ; The following string is given the name "ERRSP" and is used to clear the screen, then space down to the ; center prior to displaying an error message ERRSP: DB 177o,377o,377o,211o,200o TITLS: DB 177o,211o DB 'EDUCATOR-8080',264o DB '____BINARY_____ OCT HX',212o DB '7 6 5 4 3 2 1 0',250o,200o BLINE: DB 'B-REG >> ',200o CLINE: DB 241o DB 'C-REG >> ',200o AFHDR: DB 240o DB 'FLAGS&ACC' DB 227o DB 'P Z S A C',227o,200o CMDMS: DB 240o DB 'COMMAND ? ',200o ; Work area PSWA: DB 10000b DS 2 ; BANDC EQU $ CREG: DS 1 BREG: DS 1 ; CMDAR: DS 22 ; -------------------------------------------------------------- ; ---------------------- CP/M REFERENCES ----------------------- ; -------------------------------------------------------------- OS EQU 0000H ; CP/M warm start BDOS EQU 0005H ; CP/M OS call .CONS EQU 6 ; BDOS function accessing console _GET EQU 0FDH ; Read subfunction ; -------------------------------------------------------------- ; ----------------------- CP/M INTERFACE ----------------------- ; -------------------------------------------------------------- ; Put character in Accu to console CHRPR: CPI 0177O ; Test special clear screen JZ CLR ; Do it if so PUSH PSW ; Save character PUSH PSW ; Dtto. PUSH PSW ; Dtto. CALL CONSOLE ; Put it to console POP PSW ; Get back character CPI ' ' ; Test printable CNC INCCOL ; Update column if so POP PSW ; Get back character CPI 015o ; Test carriage return CZ RESCOL ; Clear column if so POP PSW ; Get back character RET ; Exit ; Get character from console to Accu KEYBD: MVI A,_GET ; Get control CALL CONSOLE ; Read character from console CPI 'C'-'@' ; Test abort JZ OS ; Return to CP/M if so CALL UPCASE ; Convert to upper case PUSH PSW ; Save character CPI 015o ; Test return CZ NL ; Advanve line if so POP PSW ; Get back character RET ; Exit ; -------------------------------------------------------------- ; CP/M console I/O CONSOLE: PUSH B ; Save registers PUSH D PUSH H MOV E,A ; Load character or code MVI C,.CONS ; Load BDOS function CALL BDOS ; Get or put character POP H ; Restore registers POP D POP B RET ; Exit ; Increment console column INCCOL: PUSH H ; Save register LXI H,COLUMS ; Point to column count DCR M ; Decrement it CZ NL ; New line if this line filled POP H ; Restore register RET ; Exit ; Output new line on console NL: MVI A,015o ; Load carriage return CALL CHRPR ; Put to console MVI A,012o ; Load line feed CALL CHRPR ; Put to console ; Reset colun counter RESCOL: PUSH PSW ; Save character MVI A,32 ; Load initial count STA COLUMS ; Save it POP PSW ; Restore character RET ; Exit ; Clear screen - here: advance some lines CLR: CALL NL ; Output new line PUSH B ; Save register MVI B,12 ; Load count CLR1: MVI A,012o ; Load line feed CALL CHRPR ; Put to console DCR B ; Count down JNZ CLR1 ; Loop on POP B ; Restore register RET ; Exit COLUMS: DB 32 ; Convert character to upper case UPCASE: CPI 'a' ; Test range RC ; Not lower case CPI 'z'+1 ; Test again RNC ; Still not lower case ADI 'A'-'a' ; Convert RET ; Exit ; -------------------------------------------------------------- DS 2*32 STACK: END