Signature for ROM based programs
Signature for ROM based programs
@kktos Why the boldface "No, please"? What is so horrible about a jump list? In what way would it be less easily expandable than a function id parameter? I don't see that. And I see a number of downsides of a function parameter in A, or the .db function_x, .dw parms_ptr
@BruceMcF Ah, interesting; I realize that my own 65C02 fluidity still needs a good deal of work; I wasn't familiar with JMP (abs, x). Still, this would occupy X, leaving one register less for parameter passing.
In general, a function_id parameter goes against one of my design principles, which is not to needlessly join code execution paths, only to divide them apart again based on a parameter that comes from a constant.
Signature for ROM based programs
1 hour ago, pzembrod said:
In general, a function_id parameter goes against one of my design principles, which is not to needlessly join code execution paths, only to divide them apart again based on a parameter that comes from a constant
Don't forget the C16 has 32 bytes of memory for passing parameters, starting at address 02. The first 12 bytes are intended for passing volatile parameters, the next 12 are "safe" parameters (ie, not altered by the caller). The full description of the recommended mechanism for passing parameters is in the Programmer's Reference, "New API for the Commander X16"
I do tend to think of the C000 startup address as a "cold start" routine, though. That should be the one that you use the first time you boot the program and to set up the initial memory map and configure the software. I actually do agree that, generally speaking, it should be immune to the values in the registers or the ABI.
For specific applications, such as "open with a file" or "open and print", I would expect those to live in a jump table at the end of the function ROM. Putting the jump table at the end allows you to expand the jump table by growing downward, toward the program code, and (of course) the program code can grow upward, toward the jump table.
Just remember that the last half-page or so should be copies of the KERNAL jump table and should trampoline over to the KERNAL routines, so code living in User land can consistently call KERNAL routines, no matter which bank is active. So you'd probably want to end your function ROM jump table at $FEFF and grow it downward.
So...
C000: jump to the startup routine
C003: program signature: text with program name and version
C010 (approx): start of the program
FE00: empty space, followed by jump table
FEFF: End of Function ROM jump table
FF00: Start of KERNAL jump table and CPU vectors
Signature for ROM based programs
40 minutes ago, TomXP411 said:
Don't forget the C16 has 32 bytes of memory for passing parameters, starting at address 02. The first 12 bytes are intended for passing volatile parameters, the next 12 are "safe" parameters (ie, not altered by the caller). The full description of the recommended mechanism for passing parameters is in the Programmer's Reference, "New API for the Commander X16"
Yes, I'm aware. The reference also mentions A, X, Y for byte-sized parms. Quoting:
The 16 bit ABI generally follows the following conventions:
arguments
word-sized arguments: passed in r0-r5
byte-sized arguments: if three or less, passed in .A, .X, .Y; otherwise in 16 bit registers
boolean arguments: .C, .N
Incidentally, @Stefan's open-file call at $c003 uses r0 and r1 for the filename pointer and length.
Quote
For specific applications, such as "open with a file" or "open and print", I would expect those to live in a jump table at the end of the function ROM. Putting the jump table at the end allows you to expand the jump table by growing downward, toward the program code, and (of course) the program code can grow upward, toward the jump table.
I'm sorry but I still don't see the point. The program can start after the growing jump table at the beginning of the ROM just as well. If the API is the jump table, then nobody gets hurt if the non-API program code gets shifted by 3 bytes after a new jump gets added to the jump table. It's not like a stack and a heap growing towards each other in a shared RAM space.
The point, as I see it, of a jump table at the end of the ROM is that its position can be established independent of the ROM size, esp. in a case of 6502 machines where the ROM will be at the end of the address space. So, the same jump table works equally well and without change e.g. in 8k KERNALs (VIC20, C64) or 16k KERNALs (C16, C128, X16).
Quote
Just remember that the last page or so should be copies of the KERNAL jump table and should trampoline over to the KERNAL routines, so code living in User land can consistently call KERNAL routines, no matter which bank is active. So you'd probably want to start your function ROM jump table at $FE00
Exactly the point why I would not place my own jump table at the end of the ROM. It even goes against extensibility. If I start my own ROM jump table at $FE00, I'd be in trouble as soon as the X16 KERNAL table grows down beyond that point. So, the two fixed-address things I would have grow towards each other would be my own jump table and the KERNAL jump table. My program could live happily in whatever space it finds in between.
Signature for ROM based programs
6 hours ago, pzembrod said:
@kktos Why the boldface "No, please"? What is so horrible about a jump list? In what way would it be less easily expandable than a function id parameter? I don't see that. And I see a number of downsides of a function parameter in A, or the .db function_x, .dw parms_ptr
@BruceMcF Ah, interesting; I realize that my own 65C02 fluidity still needs a good deal of work; I wasn't familiar with JMP (abs, x). Still, this would occupy X, leaving one register less for parameter passing.
In general, a function_id parameter goes against one of my design principles, which is not to needlessly join code execution paths, only to divide them apart again based on a parameter that comes from a constant.
Though it's an option, not a requirement ... given a vector table, JMP (opvector3) also works, if you know the address of "opvector3".
I understand the benefit of a jump table at the end of the ROM if it's an actual EPROM being written to incrementally, since you can add code to the not yet burned space following the code at the beginning and add the next entry in the jump table to the net yet burned space below the already written jumps, and also when you have page-boundary-sensitive 6502 code and don't want your carefully stacked house of cards to come tumbling down, but I reckon with flash ROM and with the 65c02 fixing the vector page crossing bug, it's more six of one and half dozen of the other with putting the vector / jump table at the beginning or the end.
Signature for ROM based programs
3 hours ago, BruceMcF said:
I understand the benefit of a jump table at the end of the ROM if it's an actual EPROM being written to incrementally, since you can add code to the not yet burned space following the code at the beginning and add the next entry in the jump table to the net yet burned space below the already written jumps,
Ah! I never thought of that work flow. Thanks for the insight!
3 hours ago, BruceMcF said:
and also when you have page-boundary-sensitive 6502 code and don't want your carefully stacked house of cards to come tumbling down,
I just realize that the page boundary bug is something of which I may have been not actively aware enough. Good reminder.
3 hours ago, BruceMcF said:
but I reckon with flash ROM and with the 65c02 fixing the vector page crossing bug, it's more six of one and half dozen of the other with putting the vector / jump table at the beginning or the end.
Signature for ROM based programs
10 hours ago, pzembrod said:
@kktos Why the boldface "No, please"? What is so horrible about a jump list? In what way would it be less easily expandable than a function id parameter? I don't see that. And I see a number of downsides of a function parameter in A, or the .db function_x, .dw parms_ptr
@BruceMcF Ah, interesting; I realize that my own 65C02 fluidity still needs a good deal of work; I wasn't familiar with JMP (abs, x). Still, this would occupy X, leaving one register less for parameter passing.
In general, a function_id parameter goes against one of my design principles, which is not to needlessly join code execution paths, only to divide them apart again based on a parameter that comes from a constant.
? It was me saying oh no, not again ? But I'm smiling while writing it down. No worries ?
So you asked a very good question: why ? Good, let's think about it.
- list of JMPs
-1- (Weak argument) you'll have a 3 bytes * n addresses, bigger than a 2 bytes * n.
-2- (Weak argument) you'll have to remember a lot of addresses
- vectors table
-1- you'll have a single point of entry
-2- your API could be very easily consistent and coherent. You can have a stub code dealing with the parms and then calling the internal function. you're making a toolbox rather than a bag of tricks.
-3- for the dev, once he gets how to deal with your entrypoint API, job's done. Pretty easy to use.
-4- (Weak argument) pretty easy to add, change or remove a function as all calls go thru the stub.
About how to pass the parms, it's open.
From a dev point of view, I like to call "things" that are not messing with my code. In other words, I have only a few registers on my 6502. please, don't scrambled them.
Therefore, I prefer to go towards the parms after the the JSR entrypoint so I'm not using the registers for the call.
But there are other ways to do that.
The main point here is that I want the call to have the minimalistic impact on my code.
Signature for ROM based programs
8 hours ago, kktos said:
About how to pass the parms, it's open.
From a dev point of view, I like to call "things" that are not messing with my code. In other words, I have only a few registers on my 6502. please, don't scrambled them.
Therefore, I prefer to go towards the parms after the the JSR entrypoint so I'm not using the registers for the call.
But there are other ways to do that.
The main point here is that I want the call to have the minimalistic impact on my code.
I don't think how to pass parameters can be anything but open, but it was not lack of awareness of how coding the 6502 works which accounts for the way that KERNAL calls use the registers, but familiarity.
The high overhead of passing parameters in a data block after the subroutine call only gets higher if you force it to have a generic place other than the stack to stash the A register as you do: STA TA : STY TY : PLA : STA PARMS : PLA : STA PARMS+1 : LDY #1 : STA TA : LDY #1 : LDA (PARMS),Y : CLC : ADC PARMS : TAY : LDA PARMS+1 : ADC #0 : PHA : PHY : .... and the LDY TY : LDA TA before returning.
And your code may be using A, may be using Y, may be using both, may be using neither, it is much cleaner, if it is using Y, to:
PHY : JSR op5 : .byte 15," ParameterString" : PLY ...
Indeed, it is lower overhead if you branch over the parameter string yourself, and it is known that the parameter string is 3 bytes after the subroutine vector:
PHX : LDX #10 : JSR ROMSTART : BRA ret1 : .byte "ParameterString", 0
ret1: PLX ...
... calling JMP (OP0,X) ...
... which as a parameter string routine, does: TSX : LDA $101,X : CLC : ADC #3 : STA PARMS : LDA $102,X : ADC #0 : STA PARMS+1 ...
But all of that is programmer preference. The API is open in the sense that there is no way to enforce an API. Enforcing a ROM signature format is only done by having tools which work using that format.
Signature for ROM based programs
6 minutes ago, BruceMcF said:
But all of that is programmer preference.
? That's the very definition of the developer main trait. :):)
So, yes, the API is open. And to each choice, we have PROS and CONS. Either for the sake of readability or for the sake of cycles count.
-1- fastcall
all in registers, A, X and Y.
lda #function_id
ldx #<parms
ldy #<parms
jsr ENTRYPOINT
bcc :+
sta error
rts
:keep_going
scrambled: A, X, Y
that means you are to manage our registers, save them if needed. And this for ALL our code. Meaning that the cleanup is on us.
That's the lazy way from the point of view of the library developer.
And quite an annoyance for the developer using it.
The call is fast but we will have to manage an overhead on each call.
-2- stack call (pascal/c)
lda #function_id
pha
lda #<parms
pha
lda #<parms
pha
jsr ENTRYPOINT
bcc :+
sta error
rts
:keep_going
scrambled: A
Here, the stack is used to pass all parms. The stack cleanup has to be done either by the callee or the caller.
-3- "magic" stack call
jsr ENTRYPOINT
.byte function_id
.word parms
bcc :+
sta error
rts
:keep_going
scrambled: none
like the stack call but using the RTS address as pointer. Very compact and could be applied on any calls.
Those are the cleanest ways to do API. The 1 and 2 are the ones used by a lot of compilers, if not all. The 3rd is very ASM oriented. And I only saw this method on 6502. With 6809 and 68K, stack is the way. But we have 2 stack pointers ?
Signature for ROM based programs
To be sure, if you want, efficiency can be called laziness ... IT CAN be laziness, but it can also be freeing up clock cycles to get more done. Only the caller knows which registers are in use at the time of the call, and saving & restoring a register that doesn't have a value in it that will be used by the caller is a pure inefficiency.
But this only has anything to do with signing ROM blocks if the specific opcode at the traditional ROM startup up point is a defined part of the signature to tell whether the signature is present in the block. If it's just a reserved three bytes before the signature is given, it's entirely on the ROM code author how to access their ROM.
IF what the menu selection of a named ROM does is do a far call to that ROM block at $C000, it could well be an initialization routine.
Signature for ROM based programs
56 minutes ago, BruceMcF said:
To be sure, if you want, efficiency can be called laziness ... IT CAN be laziness, but it can also be freeing up clock cycles to get more done. Only the caller knows which registers are in use at the time of the call, and saving & restoring a register that doesn't have a value in it that will be used by the caller is a pure inefficiency.
oops. Looks like I felt into the web trap. ?
Bruce, my apologies. Laziness is too strong a word to be used here. I shouldn't have use it. Around a beer, talking with you, yes. But not in writing.
Again, apologies.