address octal-code label op operand commentary *The control routine is the top of the structure and controls the operation of the entire program. <0>/000 061 xxx xxx CNTRL LXI SP,STACK Set stack pointer to programmable memory; <0>/003 315 026 <0> NOTZER CALL DSPLY Display contents of registers; <0>/006 315 316 <0> CALL CMDNT Enter a command; <0>/011 315 063 <1> CALL FETCH Fetch the correct opcode; <0>/014 267 ORA A Set zero flag as per contents; <0>/015 302 003 <0> JNZ NOTZER Jump if not zero error occurred; <0>/020 315 030 <2> CALL XQTER Go execute the current command; <0>/023 303 003 <0> JMP NOTZER Loop forever; *This display routine controls the generation of the dynamic display. <0>/026 041 167 <3> DSPLY LXI H,TITLS Load address of titles into HL; <0>/031 315 261 <0> CALL CHEDT Display titles; <0>/034 041 257 <3> LXI H,BLINE Load addr of BLINE title; <0>/037 072 351 <3> LDA BREG Load contents of BREG into A; <0>/042 315 132 <0> CALL DSPCV Convert and display; <0>/045 041 271 <3> LXI H,CLINE Load addr of CLINE title; <0>/050 072 350 <3> LDA CREG Load contents of CREG into A; <0>/053 315 132 <0> CALL DSPCV Convert and display; <0>/056 041 004 <0> LXI A,AFHDR Load addr of A flags title; <0>/061 315 261 <0> CALL CHEDT Display titles; <0>/064 052 346 <3> LHLD PSWA Load flags and A into HL; <0>/067 175 MOV A,L Move flags to A; <0>/070 346 004 ANI B'00000100' AND off all but parity flag; <0>/072 315 237 <0> CALL DSPFG Display the flag value; <0>/075 175 MOV A,L Move flags to A; <0>/076 346 100 ANI B'01000000' AND off all but zero flag; <0>/100 315 237 <0> CALL DSPFG Display the flag value; <0>/103 175 MOV A,L Move flags to A; <0>/104 346 200 ANI B'10000000' AND off all but sign flag; <0>/106 315 237 <0> CALL DSPFG Display the flag value; <0>/111 175 MOV A,L Move flags to A; <0>/112 346 020 ANI B'00010000' AND off all but auxiliary carry flag; <0>/114 315 237 <0> CALL DSPFG Display the flag value; <0>/117 175 MOV A,L Move flags to A; <0>/120 346 001 ANI B'00000001' AND off all but carry flag; <0>/122 315 237 <0> CALL DSPFG Display the flag value; <0>/125 174 MOV A,H Move A register value to A; <0>/126 315 137 <0> CALL DSPCN Display with no title print; <0>/131 311 RET Return to the CNTRL routine; *The display conversion routine prints binary, octal fnd hexadecimal. <0>/132 365 DSPCV PUSH PSW Save output value for CHEDT; <0>/133 315 261 <0> CALL CHEDT Display line title addr in HL; <0>/136 361 POP PSW Retrieve saved output value; <0>/137 036 010 DSPCN MVI E,Q#010' Move 8 to E register; <0>/141 007 DSPBT RLC Rotate MSB into Carry and LSB; <0>/142 365 PUSH PSW Save current value; <0>/143 346 001 ANI Q'001' AND off all but LSB; <0>/145 315 237 <0> CALL DSPFG Go display bit value; <0>/150 361 POP PSW Retrieve saved current value; <0>/151 035 DCR E Decrement loop count; <0>/152 302 141 <0> JNZ DSPBT Jump if loop count not zero; <0>/155 267 ORA A Reset carry; <0>/156 036 003 MVI E,Q'003' Move 3 to E register; <0>/160 027 DSPQT RAL MSB to Carry. Carry to LSB. <0>/161 027 RAL do it again, t <0>/162 027 RAL three times for octal digit shift; <0>/163 365 PUSH PSW Save current value; <0>/164 346 007 ANI Q'007' AND off all but octal LSD; <0>/166 366 060 ORI Q'060' OR on bits to make ASCII numeric character; <0>/170 315 xxx xxx CALL CHRPR Output the character; <0>/173 361 POP PSW Retrieve saved current value; <0>/174 035 OCR E Decrement loop count; <0>/175 302 160 <0> JNZ DSPQT Jump if loop count not zero; <0>/200 315 251 <0> CALL DSPSP Output a space; <0>/203 036 002 MVI E,Q'002' Move 2 to E ; <0>/205 007 DSPHT RLC Rotate MSB into Carry and LSB, <0>/206 007 RLC do it again. <0>/207 007 RLC four times for. <0>/210 007 RLC hexadecimal shift; <0>/211 365 PUSH PSW Save current value; <0>/212 346 017 ANI B'00001111' AND off all but hexadecimal LSD; <0>/214 306 060 ADI Q'060' Add on bits to make ASCII numeric character <0>/216 376 072 CPI Q'072' Compare result to one more than 9; <0>/220 332 225 <0> JC DSPHS If numeric then skip adjustment; <0>/223 306 007 ADI Q'007' Add 7 giving ASCII 'A' thru 'F' codes;Listing 1, continued:
address octal-code label op operand commentary → <0>/225 315 xxx xxx DSPHS CALL CHRPR Output the character; <0>/230 361 POP PSW Retrieve saved current value; <0>/231 000 NOP <0>/232 035 DCR E Decrement loop count; <0>/233 302 205 <0> JNZ DSPHT Jump if loop count not zero; <0>/236 311 RET Return to calling routine; *Display flag or binary digit followed by a space. Alternate entry is used to display a space. <0>/237 312 244 <0> DSPFG JZ DSPFZ Jump if passed value is a zero; <0>/242 076 001 MVI A,Q'001' Otherwise move a 1 into A; <0>/244 306 060 DSPFZ ADI Q'060' Convert into ASCII numeric character; → <0>/246 315 xxx xxx CALL CHRPR Output the character; <0>/251 365 DSPSP PUSH PSW Save the flags and value in A; <0>/252 076 040 MVI A,Q'040' Move space into A; → <0>/254 315 xxx xxx CALL CHRPR Output the space; <0>/257 361 POP PSW Retrieve the saved flags and A; <0>/260 311 RET Return to the calling routine; *The character string output edit routine. <0>/261 176 CHEDT MOV A,M Move next character into A; <0>/262 376 200 CPI Q'200' Compare it to 200 octal; <0>/264 310 RZ Return if equal it's end of string; <0>/265 322 277 <0> JNC CHSPA Jump if greater for space routine; → <0>/270 315 xxx xxx CALL CHRPR Else go output the character; <0>/273 043 CHEND INX H Increment the string index; <0>/274 303 261 <0> JMP CHEDT Loop for next character; <0>/277 326 200 CHSPA SUI Q'200' Subtract 200 octal from value; <0>/301 107 MOV B,A Move space count to B; <0>/302 076 040 CHSPL MVI A,Q'040' Move space to A; → <0>/304 315 xxx xxx CALL CHRPR Output the space; <0>/307 005 DCR B Decrement space count; <0>/310 302 302 <0> JNZ CHSPL Jump if count not zero to start of loop; <0>/313 303 273 <0> JMP CHEND Jump back into CHEDT loop; *The command entry routine accepts input from the keyboard for commands. <0>/316 041 332 <3> CMDNT LXI H,CMDMS Move address of 'COMMAND?' to HL; <0>/321 315 261 <0> CALL CHEDT Display the message; <0>/324 041 352 <3> LXI H,CMDAR Move address of command input area HL; <0>/327 006 026 MVI B,Q'026' Move maximum length to B; → <0>/331 315 xxx xxx CMDKB CALL KEYBD Get an input character; <0>/334 376 014 CPI Q'014' Is it a control-1 line delete? <0>/336 312 000 <0> JZ CNTRL If so then restart program; <0>/341 376 015 CPI Q'015' Is it a carriage return? <0>/343 312 376 <0> JZ CMDND If so then go compress input; <0>/346 376 177 CPI Q'177' Is it a delete character? <0>/350 302 355 <0> JNZ CMDST If not then go store the character; <0>/353 076 033 MVI A,Q'033' If so replace with back arrow; <0>/355 167 CMDST MOV M,A Store input character in command buffer; → <0>/356 315 xxx xxx CALL CHRPR Display the input character; <0>/361 043 INX H Increment command work area index; <0>/362 005 DCR B Decrement command length count; <0>/363 302 331 <0> JNZ CMDKB If not full then reiterate; <0>/366 076 001 MVI A,Q'001' If buffer full then select error <0>/370 315 063 <2> CALL ERROR number 1 and print its message; <0>/373 303 000 <0> JMP CNTRL Restart the program; *The command compress routine eliminates all but letters and numbers. <0>/376 041 352 <3> CMDND LXI H,CMDAR Load HL with address of work area; <1>/001 345 PUSH H Push & pop move it to DE <1>/002 321 POP D as the compression pointer; <1>/003 076 026 MVI A,Q'026' Load A with maximum length; <1>/005 220 SUB B Subtract remaining length from B; <1>/006 107 MOV B.A Move actual length to B; <1>7007 176 CMDNX MOV A,M Move command character to A; <1>/010 376 033 CPI Q'033' Is it a back arrow (character delete)? <1>/012 302 027 <1> JNZ CMDCH If not then go to other tests; <1>/015 076 352 MVI A,CMDAR-L Low address byte of CMDAR to A; <1>/017 273 CMP E Compare to current low address byte; <1>/020 322 055 <1> JNC CMDNS If not greater then skip save; <1>/023 033 DCX D Else back up compression pointer; <1>/024 303 055 <1> JMP CMDNS Skip saving the character; <1>/027 376 060 CMDCH CPI Q'060' Is the character less than 'O'? <1>/031 332 055 <1> JC CMDNS If so then skip saving it; <1>/034 376 072 CPI Q'072' Is the character less than '9' + 1? <1>/036 332 053 <1> JC CMDSV If so then save numeric value; <1>/041 376 101 CPI Q'101' Is the character leu than 'A'? <1>/043 332 055 <1> JC CMDNS If so then skip saving it; <1>/046 376 133 CPI Q'133' Is the character greater than 'Z'? <1>/050 322 055 <1> JNC CMDNS If so then skip saving it; <1>/053 022 CMDSV STAX D Store character in compressed area; <1>/054 023 INX D Increment compression pointer index; <1>/055 043 CMDNS INX H Increment input string pointer; <1>/056 005 DCR B Decrement actual length count; <1>/057 302 007 <1> JNZ CMDNX If length is not zero then reiterate; <1>/062 311 RET Else return to CNTRL calling point; *The FETCH instruction/command routine validates and builds the object code. <1>/063 041 122 <2> FETCH LXI H,OPTAB Load address of opcode table HL; <1>/066 036 037 MVI E,Q'037' Move table element count to E; <1>/070 345 FLOOP PUSH H Save current element address; <1>/071 001 352 <3> LXI B,CMDAR Load address of CMDAR into BC; <1>/074 026 003 MVI D,Q'003' Move opcode length to D; <1>/076 012 FCOMP LDAX B Load command character to A indexed by B; <1>/077 276 CMP M Compare it to table character; <1>/100 302 125 <1> JNZ FNXEL lf not equal then go to next element; <1>/103 003 INX B Increment command character index; <1>/104 043 INX H Increment table character index; <1>/105 025 DCR D Decrement opcode length counter; <1>/106 302 076 <1> JNZ FCOMP If not zero continue test loop; <1>/111 343 XTHL Exchange HL with top of stack; <1>/112 341 POP H Pop HL from stack to clear it; <1>/113 136 MOV E,M Move naked opcode to E, D is zero; <1>/114 325 PUSH D Save naked opcode; <1>/115 043 INX H Increment table pointer; <1>/116 136 MOV E,M Decode routine low address byte to E; <1>/l17 043 INX H Increment table pointer; <1>/120 126 MOV D,M Decode routine high address byte to D; <1>/121 353 XCHG Move decode routine address to HL;Listing 1, continued:
address octal-code label op operand commentary <1>/122 321 POP D Unsave naked opcode to DE; <1>/123 257 XRA A Clear A, no error code; <1>/124 351 PCHL Jump to address of decode routine; <1>/125 001 006 000 FNXEL LXI B,Q'000006' Load double length 6 into BC; <1>/130 341 POP H Unsave current element address; <1>/131 011 DAD B Add 6 to it; <1>/132 035 DCR E Decrement table element count; <1>/133 302 070 <1> JNZ F LOOP Reiterate to test next element; <1>/136 076 002 MVI A,Q'002' Move error code 2 to A; <1>/140 303 063 <2> JMP ERROR Go display error 2, opcode unknown; <1>/143 000 NOP No operation filler; *The instruction decoder routines follow. *Instructions using the DIRCT routine require no decoding. Example RAL, CMA, etc. <1>/144 311 DIRCT RET Return to CNTRL for execution; * The MOVRT is used only by the MOV command. <1>/145 315 245 <1> MOVRT CALL RG543 Validate destination register; <1>/150 267 ORA A Set flags based on A contents; <1>/151 300 RNZ Return not zero with error; Else fall thru to RG210; *Instructions using the RG210 routine require a source register. <1>/152 012 RG210 LDAX B Load next command character into A; <1>/153 003 INX B Increment command character index; <1>/154 315 173 <1> CALL REGAN Analyze for valid register; <1>/157 322 166 <1> JNC RGERR If CY=0 then register not valid; <1>/162 203 ADD E Add naked opcode to register value; <1>/163 137 MOV E,A Move result back to E; <1>/164 257 XRA A Clear A indicating no errors; <1>/165 311 RET Return to CNTRL; *The register error routine is used to indicate register designation errors. <1>/166 076 003 RGERR MVI A,Q'003' Move error code 3 to A; <1>/170 303 063 <2> JMP ERROR Go display error 3, invalid register; *The register analysis and validation routine is used by RG543, RG210 and RG54B. <1>/173 326 101 REGAN SUI Q'101' Subtract an 'A' from the character; <1>/175 376 003 CPI Q'003' Compare the result to 3; <1>/177 320 RNC If not less than 3 return with CY=0; <1>/200 075 DCR A Decrement result: A=377, B=000, C=001; <1>/201 346 007 ANI Q'007' AND off all but octal LSD; <1>/203 067 STC Set CY=1 indicating no error; <1>/204 311 RET Return to calling routine; *The MVIRT is used only by the MVI command. <1>/205 315 245 <1> MVIRT CALL RG543 Validate destination register; <1>/210 267 ORA A Set flags based on A contents; <1>/211 300 RNZ Return not zero with error; Else fall thru to IMMED; *Instructions requiring an immediate operand use the IMMED routine. <1>/212 012 IMMED LDAX B Load next command character into A; <1>/213 003 INX B Increment command character index; <1>/214 376 102 CPI Q'102' Is the command character a 'B'? <1>/216 312 301 <1> JZ BINRY If so then process as binary; <1>/221 376 121 CPI Q'121' Is the command character a 'Q'? <1>1222 312 336 <1> JZ OCTAL If so then process as octal; <1>/226 376 110 CPI Q'110' Is the command character an 'H'? <1>/230 312 367 <1> JZ HEX If so then process as hexadecimal; <1>/233 376 070 CPI Q'070' Is the command character less than '8'? <1>/235 332 335 <1> JC OCTAD If so then treat as octal; <1>/240 076 005 MVI A,Q'005' Move error code 5 to A; <1>/242 303 063 <2> JMP ERROR Go display error 5, invalid immediate; *Instructions using the RG543 routine require a destination register. <1>/245 012 RG543 LDAX B Load next command character into A; <1>/246 003 INX B Increment command character index; <1>/247 315 173 <1> CALL REGAN Analyze for valid register; <1>/252 322 166 <1> JNC RGERR If CY=0 then register not valid; <1>/255 007 RLC Shift octal register value <1>7256 007 RLC left three <1>/257 007 RLC places; <1>/260 203 ADD E Add naked opcode to shifted value; <1>/261 137 MOV E,A Move result back to E; <1>/262 257 XRA A Clear A indicating no errors; <1>/263 311 RET Return to calling routine; *Instructions using the RG54B routine are INX and DCX. <1>/264 012 RG54B LDAX B Load next command character into A; <1>/265 003 INX B Increment command character index; <1>/266 315 173 <1> CALL REGAN Analyze for valid register; <1>/271 376 000 CPI Q'000' Is the register a zero? <1>/273 310 RZ If so it's 'B' so return; <1>/274 076 004 MVI A,Q'004' Move error code 4 to A; <1>/276 303 063 <2> JMP ERROR Go display error 4, invalid register; *The BINRY routine converts a binary immediate value into usable form. <1>/301 046 010 BINRY MVI H,Q'010' Move 8 to H for count; <1>/303 012 BLOOP LDAX B Load next command character into A; <1>/304 326 060 SUI Q'060' Subtract a '0' from it; <1>/306 376 002 CPI Q'002' Is the result less than 2? <1>/310 322 330 <1> JNC IMMER If not then go display immediate error; <1>/313 345 PUSH H Save the count; <1>/314 152 MOV L,D Move D to L (immediate byte); <1>/315 051 DAD H Shift HL left one bit; <1>/316 205 ADD L Add L to bit in A;Listing 1, continued:
address octal-code label op operand commentary <1>/317 127 MOV D,A Move the result back to D; <1>/320 341 POP H Unseve the count; <1>/321 003 INX B Increment command character index; <1>/322 045 DCR H Decrement the count; <1>/323 302 303 <1> JNZ BLOOP If not zero then reiterate; <1>/326 257 XRA A Clear A indicating no errors; <1>/327 311 RET Return to CNTRL; *The immediate error routine is used to indicate immediate value errors. <1>/330 076 006 IMMER MVI A,Q'006' Move error code 3 to A; <1>/332 303 063 <2> JMP ERROR Go display error 3, invalid immediate; *The OCTAD entry point to the OCTAL routine it for the default condition. <1>/335 013 OCTAD DCX B Decrement command character index; *The OCTAL routine converts an octal immediate value into usable form. <1>/336 046 003 OCTAL MVI H,Q'003' Move a 3 into H for count; <1>/340 012 OLOOP LDAX B Load next command character into A; <1>/341 326 060 SUI Q'060' Subtract a '0' from it; <1>/343 376 010 CPI Q'010' Is command character less than 8? <1>/345 322 330 <1> JNC IMMER If not then go display immediate error; <1>/350 345 PUSH H Save the count; <1>/351 152 MOV L,D Move D to L immediate byte; <1>/352 051 DAD H Shift immediate <1>/353 051 DAD H byte left <1>/364 051 DAD H three bits; <1>/355 206 ADD L Add L to value in A; <1>/356 127 MOV D,A Move result beck to D; <1>/357 341 POP H Unsave the count; <1>/360 003 INX B Increment command character index; <1>/361 045 DCR H Decrement the count; <1>/362 302 340 <1> JNZ OLOOP If not zero then reiterate; <1>/365 257 XRA A Clear A indicating no errors; <1>/366 311 RET Return to CNTRL; *The HEX routine converts a hexadecimal immediate value into usable form. <1>/367 046 002 HEX MVI H,Q'002' Move a 2 into H for count; <1>/371 012 HLOOP LDAX B Load next command character into A; <1>/372 326 060 SUI Q'060' Subtract a '0' from it; <1>/374 376 012 CPI Q'012' Is it less then '9' + 1? <1>/376 332 010 <2> JC HCHOK If so then numeric character is OK; <2>/001 326 007 SUI Q'007' Else convert alphabetic to numeric; <2>/003 376 020 CPI Q'020' Is character value greater than 15? <2>/005 322 330 <1> JNC IMMER If so then invalid hexadecimal value; <2>/010 345 HCHOK PUSH H Save the count; <2>/011 152 MOV L,D Move D to L immediate byte; <2>/012 051 DAD H Shift immediate <2>/013 051 DAD H byte left <2>/014 051 DAD H four <2>/015 051 DAD H bits; <2>/016 205 ADD L Add L to value in A; <2>/017 127 MOV D,A Move result back to D; <2>/020 341 POP H Unsave the count; <2>/021 003 INX B Increment command character index; <2>/022 045 DCR H Decrement the count; <2>/023 302 371 <1> JNZ HLOOP If not zero then reiterate; <2>/026 257 XRA A Clear A indicating no errors; <2>/027 311 RET Return to CNTRL; *The XQTER routine executes the generated object code for Educator-8080. <2>/030 353 XQTER XCHG Move generated opcode to HL; <2>/031 042 046 <2> SHLD XQTOP Store it at execution point; <2>/034 052 346 <3> LHLD PSWA Load working PSW & A into HL; <2>/037 345 PUSH H Push & pop sets values for <2>/040 361 POP PSW working register and flags; <2>/041 052 350 <3> LHLD BANDC Load working B and C into HL; <2>/044 345 PUSH H Push & pop sets values for <2>/045 301 POP B working B and C registers; <2>/046 000 XQTOP NOP The command to be executed; <2>/047 000 NOP Immediate value or NOP; <2>/050 305 PUSH B Push B and C working register values; <2>/051 341 POP H Pop them into HL; <2>/052 042 350 <3> SHLD BANDC Store them in save area; <2>/055 365 PUSH PSW Push PSW and A working values; <2>/056 341 POP H Pop them into HL; <2>/057 042 346 <3> SHLD PSWA Store them in save area; <2>/062 311 RET Return to CNTRL for next command;I; * The ERROR routine is used to display error messages. <2>/063 365 ERROR PUSH PSW Save error code in A; <2>/064 041 162 <3> LXI H,ERRSP Load address of error header spaces; <2>/067 315 261 <0> CALL CHEDT Go output error header spaces <2>/072 361 POP PSW Unsave error code; <2>/073 041 014 <3> LXI H,ERTAB Load address of error message table; <2>/076 205 ADD L Add low address byte to error code; <2>/077 157 MOV L,A Move result to L, points to offset; <2>/100 156 MOV L,M Move offset to L; *Note: HL now contains the address of the error message. <2>/101 315 261 <0> CALL CHEDT Output the error message; <2>/104 021 000 000 ERTIM LXI D,Q'000000' Load DE with timing loop value; <2>/107 035 DCR E Decrement value in E 256 times; <2>/110 302 105 <2> JNZ ERTIM+1 Reiterate loop 256 times; *The above JMP goes to the first 000 in the LXI command which is an effective NOP. <2>/113 025 DCR D Decrement D; <2>/114 302 105 <2> JNZ ERTIM+1 Reiterate outer loop 256 times; <2>/117 076 377 MVI A,Q'377' Move a 377 to A indicating error; <2>/121 311 RET Return to CNTRL; *Note: for Teletype or hard copy output bytes <2>/104 thru <2>/116 can be replaced by 000 NOPs.Programm Listing. [CP/M Quelle]
Eingescanned von
Werner Cirsovius
Dezember 2013
© BYTE Publications Inc.