ROM *commands

bbc micro/electron/atom/risc os coding queries and routines
Post Reply
iainfm
Posts: 448
Joined: Thu Jan 02, 2020 8:31 pm
Location: Dumbarton
Contact:

ROM *commands

Post by iainfm »

Hi,

I'm trying to improve the ROM framework in Bruce Smith's book. The things it didn't address were lower/mixed case command and abbreviations. I've got these working, but have one thing left to fix.

Namely, if my * command is *MYCOMMAND, *MYCOMMAND works (in any case), *MY., *MYC. etc all work. But, *MYCOMMAND. (ie with a period after the full command name) also works...but shouldn't.

Is anyone able to suggest what I could add to make it work please? I think I've been staring at it for too long. I tried a few things, but none had the desired effect!

Here's my current code:

Code: Select all

.unrecognised
	 TYA
	 PHA
	 TXA
	 PHA
	 LDX      #&FF
	 DEY
	 STY      stack

.ctloop
	 INX
	 INY
	 LDA      table, X
	 BMI      found         \ end of string?
	 LDA      (comline), Y
	 
	 CMP      #&2E			\ ASC"." - check for abbreviations
	 BNE      not_dot
	 
.skip_if_dot                \ Skip the rest of the current rom command
	 INX                    
	 LDA      table, X      
	 BMI      found         \ Jump to found when the jump address is encountered
	 BPL      skip_if_dot   \ Loop around until the table command is exhausted
	 
.not_dot
     AND      #&DF
	 CMP      table, X      \ keep testing for matches
	 BEQ      ctloop

.again                      \ fetch next command in table
	 INX
	 LDA      table, X
	 BPL      again
	 CMP      #&FF          \ no more?
	 BEQ      out           \ none matched; exit
	 INX
	 LDY      stack
	 JMP      ctloop
	 
.out
.not_this_rom
	 PLA
	 TAX
	 PLA
	 TAY
	 PLA
	 RTS

.found
     
	 CMP      #&FF
	 BEQ      not_this_rom

	 STA      jump+1
	 INX
	 LDA      table, X
	 STA      jump
	 JMP      (jump)
	 
.table
	 EQUS     "MYCOMMAND",&0D  \ &0D *MYCOMMANDxxxx doesn't match
	 EQUB     jumpaddress DIV 256
	 EQUB     jumpaddress MOD 256
	 EQUB     &FF
Thanks in advance,
Iain
User avatar
arg
Posts: 267
Joined: Tue Feb 16, 2021 2:07 pm
Location: Cambridge
Contact:

Re: ROM *commands

Post by arg »

*MYCOMMAND. is matching because your table has &0D terminators on the commands - so the '.' matches against the &0D.

Take away the &0D and it will no longer match the spurious dot, but you haven't then checked for whitespace after the command (which is probably better done separately, as you want to accept &0D for just *MYCOMMAND or &20 if it has parameters (assuming that makes sense where this code is used).

Anyhow, you can't have it both ways - if you treat the &0D as part of the command then dot will match it, if you don't then you need to check for end of command explicitly.
iainfm
Posts: 448
Joined: Thu Jan 02, 2020 8:31 pm
Location: Dumbarton
Contact:

Re: ROM *commands

Post by iainfm »

Hi,

Thanks for that. I put the &0d in because without it the code was matching *MYCOMMANDplusanyothercharactersattheendofit. :lol:

I'll try to work out on paper what I need to do. I think I'll have a look at JG Harston's code as well. It has some kind of dot and space processing in it, but I've not looked at it closely yet.

Cheers,
Iain
User avatar
sweh
Posts: 2467
Joined: Sat Mar 10, 2012 12:05 pm
Location: New York, New York
Contact:

Re: ROM *commands

Post by sweh »

This made me look at my own code from BITD ( https://github.com/sweharris/ROM_Source ... anager.src ) and I realised I also didn't check for space/return at the end of the command. So "*TUBEONANDONANDONANDON" would match "*TUBEON". So I fixed that.. I think :-)

My code uses a table such as

Code: Select all

        EQUS "BYPASS"
        EQUB &82
        EQUW dirct
        EQUS "FILECOM"
        EQUB &85
        EQUW flecom
        EQUS "FILESYS"
        EQUB &81
        EQUW flesys
The high bit number does double duty; it tells the command line parser that the command name has ended. It also tells the "*HELP" routine what to add after the command name.

Code: Select all

.mssge1 EQUB 13
.mssge2 EQUS " <rom> <command>",13
.mssge3 EQUS " <bank>",13
.mssge4 EQUS " <rom>",13
.mssge5 EQUS " <command>",13
You can see the parser starting around line 890 (".cmpcom"). It's a bit messy!
Rgds
Stephen
iainfm
Posts: 448
Joined: Thu Jan 02, 2020 8:31 pm
Location: Dumbarton
Contact:

Re: ROM *commands

Post by iainfm »

Hi, thanks for that. I'll take a look :)

I sat down with a bit of paper last night, and came up with an algorithm that deals with a single * command with no additional parameter. I've not implemented it in the ROM yet, and I'm still considering pinching someone else's code for what I need as it's more than likely going to be better. :lol:

It may not be the most slick way of doing it, but it matches *MYCOMMAND, *mycommand, *MY., *my., but doesn't match *MYCOMMAND. or *MYCOMMANDS etc.

Code: Select all

LDX#0:LDY#&FF         \ initialise counters

.loop
     INY
     LDA (comline),Y  \ get command line byte
     CMP #ASC"."      \ is it abbreviated?
     BEQ do_dot       \ branch if so
     AND #&DF         \ upper-caseify
     CMP table,X      \ compare to command table
     BNE no_match     \ exit if mismatch
     INX:LDA table,X  \ get next byte from command table
     BPL loop         \ Loop if it's not a jump address (>=&80)
     JMP match        \ if we get to here we have a match

.do_dot               \ process abbreviations
     LDA table,X      \ Get the current command table byte
     CMP #&0D         \ Is it CR?
     BEQ no_match     \ Don't match "*VALIDCOMMAND."
     
.ddloop               \ otherwise...
     INX:LDA table,X  \ keep fetching the next command table byte
     BPL ddloop       \ until we get to the jump address we need
     JMP match        \ and exit
     
.match
\ do whatever is required when a match is found
RTS

.no_match
\ do whatever is required when a match is found
RTS
Example program attached that can be used to test edge cases etc. I guess the same (or similar) code could be used to deal with spaces, but my brain is suitably fried from thinking about this for the moment!
Attachments
strcmp.txt
(762 Bytes) Downloaded 8 times
User avatar
arg
Posts: 267
Joined: Tue Feb 16, 2021 2:07 pm
Location: Cambridge
Contact:

Re: ROM *commands

Post by arg »

This code has a vulnerability that if the user manages to type *VALIDCOMMAND<x> where <x> is a top-bit-set character that matches the entry in the table, and if the low byte of the jump address happens to also have the top bit set, then it will take a wild jump to an address partly taken from the next table entry. Fairly unlikely, but can be fixed by making your force-to-lowercase AND #&5F rather than &DF.

Since you haven't shown the .match code, I'm assuming there that your table has the bytes of the jump address reversed to ensure it has the top bit set in the first byte, or else you are using a scheme like @sweh's.
User avatar
Richard Russell
Posts: 2406
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: ROM *commands

Post by Richard Russell »

arg wrote:
Tue Jun 15, 2021 1:12 pm
can be fixed by making your force-to-lowercase AND #&5F rather than &DF.
Stating the obvious, AND #&5F forces to capitals, not to lowercase.
I am suffering from 'cognitive decline' and depression. If you have a comment about the style or tone of this message please report it to the moderators by clicking the exclamation mark icon, rather than complaining on the public forum.
User avatar
arg
Posts: 267
Joined: Tue Feb 16, 2021 2:07 pm
Location: Cambridge
Contact:

Re: ROM *commands

Post by arg »

Richard Russell wrote:
Tue Jun 15, 2021 1:45 pm
Stating the obvious, AND #&5F forces to capitals, not to lowercase.
Indeed it does, sorry. But force-to-capitals is what the original actually wanted, so it's only my description that's wrong..
iainfm
Posts: 448
Joined: Thu Jan 02, 2020 8:31 pm
Location: Dumbarton
Contact:

Re: ROM *commands

Post by iainfm »

Ah, interesting. I was wondering why some code examples had &5F and others had &DF, but hadn't sat down to think about why one would be better than the other!
Coeus
Posts: 2248
Joined: Mon Jul 25, 2016 12:05 pm
Contact:

Re: ROM *commands

Post by Coeus »

This has prompted me to look at some code I have. This copies an idea I saw somewhere else of using the case of the letters in the command table to enable a minimum abbreviation to be specified. Reading the thread I think this may also suffer from someone being able to put the high byte of the exec address in the command line, though I am not sure how likely that is.

Code: Select all

;;; Operating system command parser.  This is called in response to
;;; ROM service call 04.

.oscmd
{
        ldx     #&ff
        dey
        tya
        pha
.cmdlp  pla
        pha
        tay
.chrlp1 inx
        iny
        lda     cmdtab,x        ; character from command table.
        cmp     #'a'            ; if lower case then abbrevations are now ok.
        bcs     abbrok
        eor     (&f2),y         ; case-insenitive comparison.
        and     #&5f
        beq     chrlp1
.next   inx                     ; skip forward to the exec address.
        lda     cmdtab,x
        bpl     next
        inx                     ; skip past one byte of the address.
        cpx     #(cmdend-cmdtab-1)
        bne     cmdlp
.notfnd pla
        tay
        iny
        lda     #&04            ; restoring the service code 04 tells the
        rts                     ; OS we did not claim this command.
.done   pla
        tay
        iny
        lda     #&00            ; setting A=00 says we did claim this command.
        rts
.chrlp2 inx
        iny
.abbrok lda     (&f2),y
        cmp     #&2e
        beq     gotdot
        eor     cmdtab,x        ; case-insensitive comparison.
        and     #&5f
        beq     chrlp2
        lda     cmdtab,x
        bpl     next
        lda     (&f2),y
        cmp     #&0d
        beq     found
        cmp     #' '
        bne     next
.splp   iny                     ; skip spaces before calling the subroutine
        lda     (&f2),y         ; to handle this command.
        cmp     #' '
        beq     splp
.found  lda     #>(done-1)      ; set the return address that the subroutine
        pha                     ; implementing this command will return to.
        lda     #<(done-1)
        pha
        lda     cmdtab,x        ; push the subroutine address ready for RTS
        pha                     ; to go to it.  It is stored MSB first as that
        lda     cmdtab+1,x      ; is negative and thus marks the end of the
        pha                     ; command.
        rts
.enlp   inx
.gotdot lda     cmdtab,x        ; as the command was abbreviated, skip past
        bpl     enlp            ; the remaining letters to find the address
        bmi     splp
}
Post Reply

Return to “programming”