I suggest making all operating system API calls through
COP. The method I am entertaining to make firmware API calls in my POC V2 unit is:
Code:
;firmware API invocation template using registers for parameter passing...
;
lda #parm1 ;parameter, if needed
ldx #parm2 ;parameter, if needed
ldy #parm3 ;parameter, if needed
pea #api_index ;desired service's index (1, 2, 3, etc.)
cop #0 ;invoke API (signature byte can be anything)
bcs error ;where the meaning of "error" depends on API call
An alternate method to be used would be:
Code:
;firmware API invocation template using the stack for parameter passing...
;
pea #parm1 ;1st parameter
pea #parm2 ;parameter
pea #parm3 ;parameter
...
pea #parmN ;last parameter
pea #api_index ;desired service's index (1, 2, 3, etc.)
pea #n_parm ;number of parameters passed
cop #0 ;invoke API (signature byte can be anything)
bcs error ;where the meaning of "error" depends on API call
In both cases, the API backend will take care of stack housekeeping. I also developed a macro to generate the parameter stack frame (Kowalski assembler):
Code:
;STACK FRAME GENERATOR
;
; syntax: ppsetup parm1,parm2,parm3,...
;
; ————————————————————————————————————————————————————————————————————————
; This macro pushes any parameters that are passed to it in a way that is
; consistent with how a kernel call would be handled. It is permissible
; to invoke it without any arguments, in which case only the parameter
; count (zero) would be pushed. Parameters are not validated, only proc-
; esssed. In other words, garbage in, garbage out!
; ————————————————————————————————————————————————————————————————————————
;
ppsetup .macro ... ;variable number of args allowed
.ct .= 0 ;arg counter
.rept %0 ;loop for each arg, %0 = number of args
.ct .= .ct+1 ;bump counter
.byte $f4 ;PEA # opcode
.word %.ct ;16-bit parameter
.endr ;end of loop
.byte $f4 ;PEA # opcode
.word .ct ;parameter count
.endm
The main value in using
COP for API calls is nothing special has to be done to call an API from another bank. All of that is handled for you due to
COP being just another interrupt.
One thing I did do was explore using
COP's signature byte as the API index. I concluded less code would be required to pick the index off the stack than would be required to go back to the bank from which the API call was made to retrieve the index—the latter would require fiddling around with
DB. Using that idea, I also developed a macro for making an API call:
Code:
;FIRMWARE or OS API INVOCATION
;
; callapi [parm1[,parm2...[parmN,]]] api_index
;
callapi .macro ... ;1 or more args allowed
.if %0 ;if at least 1 arg passed...
.ct .= 0 ;arg counter
.rept %0 ;loop for each arg
.ct .= .ct+1 ;bump counter
.byte $f4 ;PEA # opcode
.word %.ct ;16-bit parameter
.endr ;end of loop
.byte $f4 ;PEA # opcode
.word .ct-1 ;parameter count, doesn't include API index
cop #0 ;invoke API (signature byte can be anything)
bcs error ;where the meaning of "error" depends on API call
.else
.error "callapi: missing arguments"
.endif
In the above, the parameter count will be on the bottom of the stack, and right above it will be the API index. Such an arrangement makes it easy to cherry-pick these two stack frame elements, since they are common to all internal code that is accessible through an API.
In the Kowalski assembler, the
.error pseudo-op will halt assembly and emit an error message if
callapi is invoked without arguments.