| |
oziphantom
Registered: Oct 2014 Posts: 478 |
safe way to call something in ROM
So given a 128, and an internal function rom ( or external )
A say way to call a routine and pass params to it.
I though about using BRK to trigger a call, where you
lda
ldx
ldy
brk XX to call function XX
however one can not accurately detect a brk on a 128. because the stack is not guaranteed.
I also though about using the Processor flags to hold a value temporarily, however the PHP returns bits 5 and 4 as 1 so you can't hold an 8 bit value in it.
I'm fine with also having a GEO/NEO RAM installed which gives ram at DFXX but that also means you need IO visible.
Storing "function + vars" at say $100- mostly safe but again can't guarantee that $100 is actually $100..
doing params stored as bytes after the function is probably mostly fine, only if the code is under the ROM then it needs a trampoline somewhere, or if there is GEO/NEO RAM then if it is under IO it needs a trampoline..
anybody got any ideas? maybe an obscure trick I've not thought of, some other device I can add in VICE/real life that gets me out of the problem? |
|
| |
Oswald
Registered: Apr 2002 Posts: 5022 |
params on stack ? on c64 you pass file name as pointer to filename and length iirc. |
| |
oziphantom
Registered: Oct 2014 Posts: 478 |
to get something from the stack after a jsr, you need to do
tsx
lda $103+y,x where Y is the param you want
on a 128 the Stack might not be at $100. |
| |
Oswald
Registered: Apr 2002 Posts: 5022 |
use pla / pha to get the params, and push back the return address. then stack might be anywhere. |
| |
TWW
Registered: Jul 2009 Posts: 541 |
You can use a stack param. handler. Just pull the return address from the stack, add it to a indirect parameter pointer and fetch your parameters. Add Y to rts return address and puh it back to the stack. This way you don't need to care where the stack is, only where the jsr i executed.
Here is the one I currently use:
//-------------------------------------------------------------------------
// StackEntry & StackExit
// ========== =========
//
// Entry fetches .PC to byte after last jsr call and places it into ZP_Reg1
// Parameters are then passed to the routine via indirect fetches (ZP_Reg1)
// StackFix is called to push exit address after the parameters (by Y-Reg)
// Exit adjust the ZP_Reg1 vector to return the .PC to last parameter
//
// The idea is to not spend a lot of memory passing parameters to subr.
// but instead by using some overhead code to save bytes by calling
// Subroutines throughout a code project.
//
// By using registers or ZP the preparation code for each call would grow
// in each call. A MemSet routine needs two 16 bit values and a fillbyte
// set up for each call to the Subroutine wich will quickly add up.
//
//
// Example:
//
// jsr Subroutine
// .byte Parameter1, Parameter2
// rts
//
// Subroutine:
// jsr StackEntry // Arrange ZP_Reg1 to point to parameters
// // Fetch parameters
// ldy #$01
// lda (ZP_Reg1),y
// sta Parameter1
// iny
// lda (ZP_Reg1),y
// sta Parameter2
// jsr StackFix // Adds Y-Reg to ZP_Reg1 to set return address
//
// // Subroutine Code
// lda Parameter1
// clc
// adc Parameter2
// sta result
//
// // Handle Subroutine Exit
// jsr Stackexit // Push ZP_Reg1 back to the Stack
// rts
//
// 40 bytes added by the stack routines and 9 bytes in sub.
// Overhead in sub is situation dependent. Few bytes at worst.
// These routines are universal and can be used by all subroutines.
// The routine also handles variable parameters (like textstrings).
//-------------------------------------------------------------------------
#importonce
StackFix:
tya
clc
adc ZP_Reg1Lo
bcc !+
inc ZP_Reg1Hi
!: sta ZP_Reg1Lo
rts
StackExit:
clc
.byte $24 // BIT OP Code (skips the sec after StackEntry)
StackEntry:
sec
// Store Return Address
pla
tax
pla
tay
pla // pla is used by Entry and Exit routine (common)
bcc !+
// Entry Code
sta ZP_Reg1Lo
pla
sta ZP_Reg1Hi // Write pointer to first parameter to ZP_Reg1
bcs !Done+
// Exit Code
!: pla // ignore old return address caller (before parameters)
lda ZP_Reg1Hi
!Done:
pha
lda ZP_Reg1Lo
pha // Restore return address of caller (last parameter byte)
// Restore return address
tya
pha
txa
pha
rts
|
| |
oziphantom
Registered: Oct 2014 Posts: 478 |
Quoting Oswalduse pla / pha to get the params, and push back the return address. then stack might be anywhere.
I guess with the NeoRam I can stash the return + the trampoline return + memory config into its RAM, then restore them for the return. |
| |
oziphantom
Registered: Oct 2014 Posts: 478 |
Quoting TWWYou can use a stack param. handler. Just pull the return address from the stack, add it to a indirect parameter pointer and fetch your parameters. Add Y to rts return address and puh it back to the stack. This way you don't need to care where the stack is, only where the jsr i executed.
the JSR is a lot bigger problem than where the Stack is :D say the initial jsr ( to the trampoline to call functions ) is under the ROM, or under IO or in another bank, under ROM... |
| |
Oswald
Registered: Apr 2002 Posts: 5022 |
tww, why not just pass in x/y ? |
| |
TWW
Registered: Jul 2009 Posts: 541 |
@Ozi: Could you not store the return-info at a fixed bank/RAM-address instead of NeoRam?
@Oswald: If you f.ex. need to pass more than 2-3 bytes to the subroutine. Like this mem-copy routine (which could be called quite often in a project):
MemCpy:
jsr StackEntry
ldy #$01
!: lda (ZP_Reg1),y // Address From Lo
sta ZP_Reg2Lo-1,y
iny
cpy #$05 // The consessive parameters will automatically be put into ZP_Reg2Hi, ZP_Reg3Lo & ZP_RegHi
bne !-
lda (ZP_Reg1),y // Size Lo
sta ZP_Temp // Hold # of bytes less than 256 to copy
iny
jsr StackFix // Add Y to ZP_Reg1
ldy #$00
lda (ZP_Reg1),y // Size Hi
beq !LessThan256+
tax
!: lda (ZP_Reg2),y
sta (ZP_Reg3),y
iny
bne !-
inc ZP_Reg2Hi
inc ZP_Reg3Hi
dex
bne !-
!LessThan256:
lda ZP_Temp
cmp #$00
beq !End+
!: lda (ZP_Reg2),y
sta (ZP_Reg3),y
iny
cpy ZP_Temp
bne !-
!End:
jsr StackExit
rts
So instead of setting up all ZP vectors and parameters each time you call the routine, you do:
jsr MemCpy
.word from, to, length
or:
:MemCPY($0400,1000,$01)
.macro MemCpy(AddressFrom, AddressTo, Quantity) {
jsr MemCpy
.word AddressFrom, AddressTo, Quantity
}
Each call cost you 9 bytes vs 39 bytes if you lda #$nn sta $ZP x 6 byte + jsr. Then imagine calling it several times. It can then also be used by any subroutine in the same way to pass parameters. I guess it's many way's to handle this though. |
| |
Oswald
Registered: Apr 2002 Posts: 5022 |
tww, no I mean a pointer to a data structure. |
| |
oziphantom
Registered: Oct 2014 Posts: 478 |
@TWW This is a debug rom, so I want it to be as invisible as possible.
you know how you write a routine to draw an X/Y at the top the screen, so as you move things you can track positions etc But you need to put 16 chars of 0-F and normally it has to be multi colour and you have to worry about CRAM etc etc
128 has a whole VDC just sitting there doing nothing with its own 16K of RAM. so debug strings, I upload all the strings into the VDC. Given a 80x25 screen needs 2K, the char set uses 4K that leaves 10K for data. So to print a string, it just looks up the X/Y then sets the Src/Dest on the VDC and uses the internal DMA to copy N chars from Src -> Screen location, in parallel, as once you start the DMA the CPU is free. This make it easy and somewhat fast to print out debug info. I'm at the point of doing collision detection and having, a bunch of x/ys and "player state" strings on display while I debug is going to make life easier. I'm sure I will find a pile more uses for it, so I want to keep it "I can just drop call in here and not worry about trashing this register/what bank I'm in etc"
brk XX YY ZZ WW etc was ideal as it allows me to move the handler anywhere I want with ease,and uses minimal in code bytes. I just insert some code into the IRQ routine to detect and fix up, but BRK is tricky on a 128 |
... 2 posts hidden. Click here to view all posts.... |
Previous - 1 | 2 - Next |