Page 1 of 1

ML design patterns (calling ROM subroutines)

Posted: Wed Dec 13, 2017 10:48 am
by toby405

Code: Select all

        ldx #8
LOOP    ldy #1
        jsr $d3a2               ; y -> FAC
        jsr $e094               ; rnd()
        jsr $d1aa               ; FAC -> a, y
        jsr USEFULSR            ; do something useful with a, y
        dex
        bne LOOP
This code doesn't work because the subroutines change X.

Are there cases where you take the trouble to figure out if a particular ROM subroutine leaves your register untouched so you can save a few bytes or is it better to just always do something like this:

Code: Select all

        lda #8
        sta $fb
LOOP    ldy #1
        jsr $d3a2               ; y -> FAC
        jsr $e094               ; rnd()
        jsr $d1aa               ; FAC -> a, y
        jsr USEFULSR            ; do something useful with a, y
        dec $fb
        bne LOOP

Re: ML design patterns (calling ROM subroutines)

Posted: Wed Dec 13, 2017 11:45 am
by Mike
As a rule of thumb, you can assume that most routines in the BASIC interpreter and KERNAL thrash all registers. Inthusfar, holding state in uncommitted RAM space is a quite common find for 65xx routines.

For the routines in the KERNAL called by the jump table, their use of registers is well documented. In the other cases you'd need to infer this information from the available (documented) assembly listings. That also applies if you go and write replacements for the OS routines (for example, when you want to write a BASIC extension) - the surrounding/calling routines make strict assumptions about the side-effects in the registers, the flag register and memory that you have to get right first.

Re: ML design patterns (calling ROM subroutines)

Posted: Wed Dec 13, 2017 12:47 pm
by srowe
For timing-critical code, yes, I've traced though the source code and/or run a test in VICE and used the monitor to check for register clobbering. You sometimes have to do the same for zero page locations.

Re: ML design patterns (calling ROM subroutines)

Posted: Sat Dec 16, 2017 9:03 pm
by CurtisP
With the rare exception of customized ROMs to fix bugs, I think you can assume that every VIC 20 is going to have the exact same Basic ROM.

So if you want to save those few bytes, I don't see a problem with tracking the register usage.

I'm just not sure it's always worth the trouble.

Re: ML design patterns (calling ROM subroutines)

Posted: Sun Dec 17, 2017 3:22 am
by Mike
Yes and No to that.
CurtisP wrote:With the rare exception of customized ROMs to fix bugs, I think you can assume that every VIC 20 is going to have the exact same Basic ROM.
When I wrote my last post here in this thread, I did not refer to that other project, where I exchanged the BASIC ROM in my VIC-20.

Rather, I pointed to some 'standards' a wedge in the BASIC interpreter has to honor with regard to register usage, flag register usage, RAM usage and other side effects. Any such wedge should in principle behave as if it wasn't there, and from that point on carefully nudge the BASIC interpreter into doing a slightly different, but intended action.

Sadly enough, that's not the case with the USR() function you posted in another thread. You surely don't want a function to print out something during expression evaluation. The Right Thing™ would have been to return a string with the hex data.
So if you want to save those few bytes, I don't see a problem with tracking the register usage.

I'm just not sure it's always worth the trouble.
For the bug-fix I made myself exactly that trouble to make sure the replacement routine returns with exactly the same register and flag values than the original. What's different now is, that the result in FAC#1 is now correct in all cases. I did this without shuffling around code, which would have broken lots of programs. Rather, I just diverted a single jump to the (sometimes malfunctioning) routine that shifts the mantissa one whole byte, and provided a replacement in some otherwise unused space of the ROM.

Re: ML design patterns (calling ROM subroutines)

Posted: Sun Dec 17, 2017 3:34 pm
by CurtisP
Mike wrote: Rather, I pointed to some 'standards' a wedge in the BASIC interpreter has to honor with regard to register usage, flag register usage, RAM usage and other side effects. Any such wedge should in principle behave as if it wasn't there, and from that point on carefully nudge the BASIC interpreter into doing a slightly different, but intended action.

Sadly enough, that's not the case with the USR() function you posted in another thread. You surely don't want a function to print out something during expression evaluation. The Right Thing™ would have been to return a string with the hex data.
I entirely agree about wedges, but a USR() function isn't quite the same as a wedge though. It's specifically designed to execute arbitrary code. And this particular code is not intended to be used inside an expression. Sometimes you just need a quick hack, and doing The Right Thing™ is more trouble than it's worth.

Re: ML design patterns (calling ROM subroutines)

Posted: Sun Dec 17, 2017 5:04 pm
by Mike
CurtisP wrote:a USR() function [is] specifically designed to execute arbitrary code. And this particular code is not intended to be used inside an expression.
I wholeheartedly disagree with that.

USR() gets called during expression evaluation, it receives a parameter in FAC#1, likewise it is expected to return a result in FAC#1.

You are misusing USR() to make side effects not expected during expression evaluation. If you want to execute arbitrary code, use SYS for that.

Re: ML design patterns (calling ROM subroutines)

Posted: Mon Dec 18, 2017 2:04 am
by wimoos
Refer to http://sleepingelephant.com/ipw-web/bul ... =15#p68669 for a collection of interesting facts on interpreter subroutines. I found and used these during my development efforts on the WimBasic extension.

Also, Lee's well-document disassembly listing on https://www.mdawson.net/vic20chrome/vic ... sembly.txt is a good help.