8080 monitor command line processor help needed
All — I’m working on a reconstruction project (an unpublished time slicing OS for an 8080-target) and the command dispatcher code is missing. So, I’m trying to write a functional equivalent and I’m having some problems that I could use some help with. The OS is like a monitor but has a callable API (which I’m ignoring for now). The command loop is pretty generic — set the stack, get a line from the console, parse it and execute, and jump back. The “get line” is pretty generic and actually works fine. Grab chars and put them in a zero-terminated buffer; return on CR. The table of commands is structured like the following. The target address right now simply prints a unique character on the screen and jumps back to the top of the monitor loop via a RET (since the parser is call’ed). ; Command dispatch table, padded to max chars of 4 CMD_LEN equ 5 ; including NULL NUM_CMDS equ 26 CTABLE: db 'AS ',0 ; assign dw ASSIGN ; target address {25 additional entries} Here’s what I’ve come up with for the parse and execute routine, which admittedly is based on some AI stuff. ; ----------------------------- ; SEARCH - see of the command entered is valid. ; ----------------------------- PARSE: lxi H,TBUFF ; get ptr of command buffer MVI B,NUM_CMDS ; Set command count LXI D,CTABLE ; Point to command table LOOP: PUSH H ; Save input buffer pointer PUSH D ; Save table pointer CALL STR_COMPARE ; Compare input with table entry JZ CMD_FOUND ; If Zero flag set, strings match POP D ; Restore table pointer POP H ; Restore input pointer ; Move to next table entry LXI H,CMD_LEN + 2 ; Size of entry (string + address) DAD D XCHG DCR B ; Decrement command count JNZ LOOP ; Continue if more commands JMP CMD_NOT_FOUND CMD_FOUND: POP D ; Clean stack (table pointer) POP H ; Clean stack (input pointer) INX D INX D ; DE points to target address in table XCHG ; move to HL MOV A,M ; Get low byte of routine address MOV E,A ; set (DE) to command number MVI D,0 DAD D DAD D ; (HL)=(CMDADR)+2*(command number) MOV A,M ; now pick out this address INX H MOV H,M MOV L,A PCHL ;now execute it. ; Routines return to the monitor via a RET ; RET CMD_NOT_FOUND: XRA A ; sets Z-flag ret ;--- String Comparison Subroutine --- ; Inputs: HL = Input Buffer, DE = Table Entry ; Destroys: A, HL, DE ; Z=1 if match STR_COMPARE: MVI C,CMD_LEN ; Comparison length COMP_LOOP: LDAX D CMP M RNZ ; Return if not equal INX D INX H DCR C ; decrement count JNZ COMP_LOOP ; Loop XRA A ; Match found: set Zero flag RET In no case that I’ve experimented with does the parser print the right character for the routine that’s typed in. The character case is right — upper-case is returned from the keyboard input routine. It’s been a long time since I’ve worked in 8080 so a different set of eyes would be greatly appreciated. Thanks! Rich -- Rich Cini http://cini.classiccmp.org https://github.com/RichCini
All commands are two characters? (table entries are the two characters, a NULL and then the target address for a total of 5, right?) If so STR_COMPARE should only look at the first two characters for a command match, not all 5 (CMD_LEN) as it currently does.? -----Original Message----- From: Richard Cini via vcf-midatlantic <vcf-midatlantic@lists.vcfed.org> Sent: Thursday, May 14, 2026 9:11 PM To: VCF-Midatlantic <vcf-midatlantic@lists.vcfed.org> Cc: Richard Cini <rich.cini@gmail.com> Subject: [vcf-midatlantic] 8080 monitor command line processor help needed All — I’m working on a reconstruction project (an unpublished time slicing OS for an 8080-target) and the command dispatcher code is missing. So, I’m trying to write a functional equivalent and I’m having some problems that I could use some help with. The OS is like a monitor but has a callable API (which I’m ignoring for now). The command loop is pretty generic — set the stack, get a line from the console, parse it and execute, and jump back. The “get line” is pretty generic and actually works fine. Grab chars and put them in a zero-terminated buffer; return on CR. The table of commands is structured like the following. The target address right now simply prints a unique character on the screen and jumps back to the top of the monitor loop via a RET (since the parser is call’ed). ; Command dispatch table, padded to max chars of 4 CMD_LEN equ 5 ; including NULL NUM_CMDS equ 26 CTABLE: db 'AS ',0 ; assign dw ASSIGN ; target address {25 additional entries} Here’s what I’ve come up with for the parse and execute routine, which admittedly is based on some AI stuff. ; ----------------------------- ; SEARCH - see of the command entered is valid. ; ----------------------------- PARSE: lxi H,TBUFF ; get ptr of command buffer MVI B,NUM_CMDS ; Set command count LXI D,CTABLE ; Point to command table LOOP: PUSH H ; Save input buffer pointer PUSH D ; Save table pointer CALL STR_COMPARE ; Compare input with table entry JZ CMD_FOUND ; If Zero flag set, strings match POP D ; Restore table pointer POP H ; Restore input pointer ; Move to next table entry LXI H,CMD_LEN + 2 ; Size of entry (string + address) DAD D XCHG DCR B ; Decrement command count JNZ LOOP ; Continue if more commands JMP CMD_NOT_FOUND CMD_FOUND: POP D ; Clean stack (table pointer) POP H ; Clean stack (input pointer) INX D INX D ; DE points to target address in table XCHG ; move to HL MOV A,M ; Get low byte of routine address MOV E,A ; set (DE) to command number MVI D,0 DAD D DAD D ; (HL)=(CMDADR)+2*(command number) MOV A,M ; now pick out this address INX H MOV H,M MOV L,A PCHL ;now execute it. ; Routines return to the monitor via a RET ; RET CMD_NOT_FOUND: XRA A ; sets Z-flag ret ;--- String Comparison Subroutine --- ; Inputs: HL = Input Buffer, DE = Table Entry ; Destroys: A, HL, DE ; Z=1 if match STR_COMPARE: MVI C,CMD_LEN ; Comparison length COMP_LOOP: LDAX D CMP M RNZ ; Return if not equal INX D INX H DCR C ; decrement count JNZ COMP_LOOP ; Loop XRA A ; Match found: set Zero flag RET In no case that I’ve experimented with does the parser print the right character for the routine that’s typed in. The character case is right — upper-case is returned from the keyboard input routine. It’s been a long time since I’ve worked in 8080 so a different set of eyes would be greatly appreciated. Thanks! Rich -- Rich Cini http://cini.classiccmp.org https://github.com/RichCini
Commands vary from 1 to 4 characters, space-padded to 4, plus a null terminator. I only showed one because the table is fairly long (26 commands). Rich -- Rich Cini http://cini.classiccmp.org https://github.com/RichCini From: glenn.f.roberts@gmail.com <glenn.f.roberts@gmail.com> Date: Thursday, May 14, 2026 at 9:27 PM To: 'vcf-midatlantic' <vcf-midatlantic@lists.vcfed.org> Cc: 'Richard Cini' <rich.cini@gmail.com> Subject: RE: [vcf-midatlantic] 8080 monitor command line processor help needed All commands are two characters? (table entries are the two characters, a NULL and then the target address for a total of 5, right?) If so STR_COMPARE should only look at the first two characters for a command match, not all 5 (CMD_LEN) as it currently does.? -----Original Message----- From: Richard Cini via vcf-midatlantic <vcf-midatlantic@lists.vcfed.org> Sent: Thursday, May 14, 2026 9:11 PM To: VCF-Midatlantic <vcf-midatlantic@lists.vcfed.org> Cc: Richard Cini <rich.cini@gmail.com> Subject: [vcf-midatlantic] 8080 monitor command line processor help needed All — I’m working on a reconstruction project (an unpublished time slicing OS for an 8080-target) and the command dispatcher code is missing. So, I’m trying to write a functional equivalent and I’m having some problems that I could use some help with. The OS is like a monitor but has a callable API (which I’m ignoring for now). The command loop is pretty generic — set the stack, get a line from the console, parse it and execute, and jump back. The “get line” is pretty generic and actually works fine. Grab chars and put them in a zero-terminated buffer; return on CR. The table of commands is structured like the following. The target address right now simply prints a unique character on the screen and jumps back to the top of the monitor loop via a RET (since the parser is call’ed). ; Command dispatch table, padded to max chars of 4 CMD_LEN equ 5 ; including NULL NUM_CMDS equ 26 CTABLE: db 'AS ',0 ; assign dw ASSIGN ; target address {25 additional entries} Here’s what I’ve come up with for the parse and execute routine, which admittedly is based on some AI stuff. ; ----------------------------- ; SEARCH - see of the command entered is valid. ; ----------------------------- PARSE: lxi H,TBUFF ; get ptr of command buffer MVI B,NUM_CMDS ; Set command count LXI D,CTABLE ; Point to command table LOOP: PUSH H ; Save input buffer pointer PUSH D ; Save table pointer CALL STR_COMPARE ; Compare input with table entry JZ CMD_FOUND ; If Zero flag set, strings match POP D ; Restore table pointer POP H ; Restore input pointer ; Move to next table entry LXI H,CMD_LEN + 2 ; Size of entry (string + address) DAD D XCHG DCR B ; Decrement command count JNZ LOOP ; Continue if more commands JMP CMD_NOT_FOUND CMD_FOUND: POP D ; Clean stack (table pointer) POP H ; Clean stack (input pointer) INX D INX D ; DE points to target address in table XCHG ; move to HL MOV A,M ; Get low byte of routine address MOV E,A ; set (DE) to command number MVI D,0 DAD D DAD D ; (HL)=(CMDADR)+2*(command number) MOV A,M ; now pick out this address INX H MOV H,M MOV L,A PCHL ;now execute it. ; Routines return to the monitor via a RET ; RET CMD_NOT_FOUND: XRA A ; sets Z-flag ret ;--- String Comparison Subroutine --- ; Inputs: HL = Input Buffer, DE = Table Entry ; Destroys: A, HL, DE ; Z=1 if match STR_COMPARE: MVI C,CMD_LEN ; Comparison length COMP_LOOP: LDAX D CMP M RNZ ; Return if not equal INX D INX H DCR C ; decrement count JNZ COMP_LOOP ; Loop XRA A ; Match found: set Zero flag RET In no case that I’ve experimented with does the parser print the right character for the routine that’s typed in. The character case is right — upper-case is returned from the keyboard input routine. It’s been a long time since I’ve worked in 8080 so a different set of eyes would be greatly appreciated. Thanks! Rich -- Rich Cini http://cini.classiccmp.org https://github.com/RichCini
On Thu, May 14, 2026 at 9:15 PM Richard Cini via vcf-midatlantic < vcf-midatlantic@lists.vcfed.org> wrote:
POP D ; Restore table pointer POP H ; Restore input pointer
; Move to next table entry LXI H,CMD_LEN + 2 ; Size of entry (string + address) DAD D XCHG
I think the "POP H" needs to be moved after "XCHG". otherwise LXI H obliterates it and you lose track of the input pointer. -ken
Thanks Ken. I just tried that and it doesn’t help. I set a breakpoint at the command found address and typed a valid command name and it never reaches it. So, it has to be around where you’re looking. I’ve been using SIMH which is not great IMHO for tracing so I’m going to see if I can run this in another emulator with a debugger. Rich -- Rich Cini http://cini.classiccmp.org https://github.com/RichCini From: Kenneth Gober <kgober@gmail.com> Date: Friday, May 15, 2026 at 12:52 AM To: vcf-midatlantic <vcf-midatlantic@lists.vcfed.org> Cc: Richard Cini <rich.cini@gmail.com> Subject: Re: [vcf-midatlantic] 8080 monitor command line processor help needed On Thu, May 14, 2026 at 9:15 PM Richard Cini via vcf-midatlantic <vcf-midatlantic@lists.vcfed.org<mailto:vcf-midatlantic@lists.vcfed.org>> wrote: POP D ; Restore table pointer POP H ; Restore input pointer ; Move to next table entry LXI H,CMD_LEN + 2 ; Size of entry (string + address) DAD D XCHG I think the "POP H" needs to be moved after "XCHG". otherwise LXI H obliterates it and you lose track of the input pointer. -ken
SIMH simulators I have used have tracing. Looks like Altair doesn't but AltairZ80 does. ./altairz80 sim> set cpu history=2000 sim> step 100 Step expired, PC: 00064 (NOP) sim> show history CPU: C0Z0M0E0I0 A=00 B=0000 D=0000 H=0000 S=0000 P=0000 NOP CPU: C0Z0M0E0I0 A=00 B=0000 D=0000 H=0000 S=0000 P=0001 NOP CPU: C0Z0M0E0I0 A=00 B=0000 D=0000 H=0000 S=0000 P=0002 NOP CPU: C0Z0M0E0I0 A=00 B=0000 D=0000 H=0000 S=0000 P=0003 NOP ... To save to file show @file history output will be in FILE On Fri, May 15, 2026 at 11:51:08AM +0000, Richard Cini via vcf-midatlantic wrote:
Thanks Ken. I just tried that and it doesn’t help. I set a breakpoint at the command found address and typed a valid command name and it never reaches it. So, it has to be around where you’re looking. I’ve been using SIMH which is not great IMHO for tracing so I’m going to see if I can run this in another emulator with a debugger.
Rich
-- Rich Cini http://cini.classiccmp.org https://github.com/RichCini
From: Kenneth Gober <kgober@gmail.com> Date: Friday, May 15, 2026 at 12:52 AM To: vcf-midatlantic <vcf-midatlantic@lists.vcfed.org> Cc: Richard Cini <rich.cini@gmail.com> Subject: Re: [vcf-midatlantic] 8080 monitor command line processor help needed
On Thu, May 14, 2026 at 9:15 PM Richard Cini via vcf-midatlantic <vcf-midatlantic@lists.vcfed.org<mailto:vcf-midatlantic@lists.vcfed.org>> wrote: POP D ; Restore table pointer POP H ; Restore input pointer
; Move to next table entry LXI H,CMD_LEN + 2 ; Size of entry (string + address) DAD D XCHG
I think the "POP H" needs to be moved after "XCHG". otherwise LXI H obliterates it and you lose track of the input pointer.
-ken
On Fri, May 15, 2026 at 7:51 AM Richard Cini <rich.cini@gmail.com> wrote:
Thanks Ken. I just tried that and it doesn’t help. I set a breakpoint at the command found address and typed a valid command name and it never reaches it. So, it has to be around where you’re looking. I’ve been using SIMH which is not great IMHO for tracing so I’m going to see if I can run this in another emulator with a debugger.
I think CMD_FOUND needs to increment DE 5 times, not 2. after XCHG, HL should then have the address needed for PCHL and none of the extra statements should be needed. It looks like the extra statements are if the command table contains indexes into another table of addresses. But if your command table contains subroutine addresses directly then "MOV A,M" through "MOV L,A" are unneeded. I pasted this code into https://eliben.org/js8080/ and it seemed to run ok. Note that I primed TBUFF with "AS " including the 2 trailing blanks that the STR_COMPARE routine requires. I assume that needing to put 2 spaces after "AS" is probably not desired but STR_COMPARE as written requires it. There is room for improvement in this code but I assume the first priority is to make it work. Making it small and fast can come later. lxi h, TBUFF mvi b, 1 lxi d, CTABLE LOOP: push h push d call STR_COMPARE jz CMD_FOUND pop d lxi h, 7 dad d xchg pop h dcr b jnz LOOP jmp CMD_NOT_FOUND CMD_FOUND: pop d pop h inx d inx d inx d inx d inx d xchg pchl CMD_NOT_FOUND: xra a ret STR_COMPARE: mvi c, 5 COMP_LOOP: ldax d cmp m rnz inx d inx h dcr c jnz COMP_LOOP xra a ret CTABLE: db 'AS ' db 0 dw 66 TBUFF: db 'AS ' db 0 ASSIGN: mvi a, 153 ret
On Fri, May 15, 2026 at 9:33 AM Kenneth Gober <kgober@gmail.com> wrote:
I think CMD_FOUND needs to increment DE 5 times, not 2. after XCHG, HL should then have the address needed for PCHL and none of the extra statements should be needed. It looks like the extra statements are if the command table contains indexes into another table of addresses. But if your command table contains subroutine addresses directly then "MOV A,M" through "MOV L,A" are unneeded.
I did not fully understand what PCHL does; instead of loading PC from the 2 bytes pointed to by HL, it loads HL into PC directly. So extra statements were needed, just different ones than what was there. Here is a simpler version of the whole thing that removes the counters and the need to pad commands to fixed sizes with spaces. Command-not-found is indicated by returning with the carry flag set. If you don't want to have to type full command names, you can just put the prefixes into CTABLE and a command will be considered matched if the prefix matches (e.g. input of "ASSIGN" will match a CTABLE entry with DB 'AS',0, leaving DE pointing at the second "S"). CTABLE: DB 'ASSIGN', 0 DW ASSIGN DB 'DEASSIGN', 0 DW DEASSIGN DB 0 ; End of CTABLE marked by a single null byte CMD_DISPATCH: LXI H, CTABLE NX_CMD: MOV A, M ; If HL points to a null byte, the end of CTABLE has been reached CPI 0 JZ CMD_NF ; Command was not found in CTABLE LXI D, TBUFF; DE = start of text buffer NX_CH: LDAX D ; Check next character in text buffer CMP M JNZ MATCH INX D INX H JMP NX_CH MATCH: MOV A, M ; If HL points to a null byte, this command matched CPI 0 INX H ; (advance HL regardless) JNZ SKIP ; If HL did not point to a null byte, skip to next CTABLE entry PUSH D ; Save DE on stack MOV E, M ; Load lower byte of address into E INX H MOV D, M ; Load upper byte of address into D XCHG ; Load full address into HL POP D ; Restore DE PCHL ; Jump to command address SKIP: MOV A, M ; On a mismatch, skip the rest of the unmatched command CPI 0 INX H JNZ SKIP INX H ; Skip the 2-byte address after the null byte INX H JMP NX_CMD ; Attempt to match the next entry in CTABLE CMD_NF: STC ; Set carry flag to indicate command not found RET ; return to monitor ; On entry, commands can assume: ; DE points to the first character in TBUFF that followed the command name ; flags are clear (last flag-affecting operation was CPI 0, with A==0) ASSIGN: MVI A, 1 RET DEASSIGN: MVI A, 2 RET
Thanks Ken! I’m going to give this a try. I was doing some tracing and discovered that HL wasn’t being loaded indirectly and thus went into Lala land. Rich -- Rich Cini http://cini.classiccmp.org https://github.com/RichCini From: Kenneth Gober <kgober@gmail.com> Date: Friday, May 15, 2026 at 1:09 PM To: Richard Cini <rich.cini@gmail.com> Cc: vcf-midatlantic <vcf-midatlantic@lists.vcfed.org> Subject: Re: [vcf-midatlantic] 8080 monitor command line processor help needed On Fri, May 15, 2026 at 9:33 AM Kenneth Gober <kgober@gmail.com<mailto:kgober@gmail.com>> wrote: I think CMD_FOUND needs to increment DE 5 times, not 2. after XCHG, HL should then have the address needed for PCHL and none of the extra statements should be needed. It looks like the extra statements are if the command table contains indexes into another table of addresses. But if your command table contains subroutine addresses directly then "MOV A,M" through "MOV L,A" are unneeded. I did not fully understand what PCHL does; instead of loading PC from the 2 bytes pointed to by HL, it loads HL into PC directly. So extra statements were needed, just different ones than what was there. Here is a simpler version of the whole thing that removes the counters and the need to pad commands to fixed sizes with spaces. Command-not-found is indicated by returning with the carry flag set. If you don't want to have to type full command names, you can just put the prefixes into CTABLE and a command will be considered matched if the prefix matches (e.g. input of "ASSIGN" will match a CTABLE entry with DB 'AS',0, leaving DE pointing at the second "S"). CTABLE: DB 'ASSIGN', 0 DW ASSIGN DB 'DEASSIGN', 0 DW DEASSIGN DB 0 ; End of CTABLE marked by a single null byte CMD_DISPATCH: LXI H, CTABLE NX_CMD: MOV A, M ; If HL points to a null byte, the end of CTABLE has been reached CPI 0 JZ CMD_NF ; Command was not found in CTABLE LXI D, TBUFF; DE = start of text buffer NX_CH: LDAX D ; Check next character in text buffer CMP M JNZ MATCH INX D INX H JMP NX_CH MATCH: MOV A, M ; If HL points to a null byte, this command matched CPI 0 INX H ; (advance HL regardless) JNZ SKIP ; If HL did not point to a null byte, skip to next CTABLE entry PUSH D ; Save DE on stack MOV E, M ; Load lower byte of address into E INX H MOV D, M ; Load upper byte of address into D XCHG ; Load full address into HL POP D ; Restore DE PCHL ; Jump to command address SKIP: MOV A, M ; On a mismatch, skip the rest of the unmatched command CPI 0 INX H JNZ SKIP INX H ; Skip the 2-byte address after the null byte INX H JMP NX_CMD ; Attempt to match the next entry in CTABLE CMD_NF: STC ; Set carry flag to indicate command not found RET ; return to monitor ; On entry, commands can assume: ; DE points to the first character in TBUFF that followed the command name ; flags are clear (last flag-affecting operation was CPI 0, with A==0) ASSIGN: MVI A, 1 RET DEASSIGN: MVI A, 2 RET
On Fri, May 15, 2026 at 1:44 PM Richard Cini <rich.cini@gmail.com> wrote:
Thanks Ken! I’m going to give this a try. I was doing some tracing and discovered that HL wasn’t being loaded indirectly and thus went into Lala land.
One more change: insert the following two lines after JNZ MATCH: CPI 0 ; end of text buffer? JZ CMD_NF This is to avoid reading past the end of TBUFF. -ken
participants (4)
-
David Gesswein -
glenn.f.roberts@gmail.com -
Kenneth Gober -
Richard Cini