how to wladx compile memorycode

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
how to wladx compile memorycode
by on (#236812)
Here is my code,I want to copy this code to ram to execute.
but the code “JSR fun” will compile into to JSR 91xx(it will jump out ram).
I need this code compile into like "JSR 14XX"
How can I dO?
Code:
.BANK 0 SLOT 0
.ORG $1400
.SECTION "CallMemCode" FORCE

JSR fun;//this code will conpiler to JSR 91XX
fun:
   RTS
   
.ENDS
Re: how to wladx compile memorycode
by on (#236831)
Whenever I've executed code into RAM, I've tried to make sure it doesn't depend on location in any way, so it could be copied to any location and work just the same. That way you don't have to worry about where you load it from, and you don't have to pay attention to how you structure your ROM. It should just be as simple as having no subroutines within your RAM subroutine.

If you want to execute code copied to a particular location, you can use the offset directly:
Code:
jsr  $1400

For your example, you might even use something like:
Code:
jsr  mylabel - $8000
to adjust for the layout of the memory map, but in that case you would have to ensure that the destination address is the same as the assembled address if the code isn't relocatable.

I'd be happy just copying the whole routine and calling it directly, assuming it's just one routine. You can also use variables and arithmetic operations to achieve some level of relativity. There might be better ways however, and there can be room for a lot of creativity depending on your use case. If you're using nested subroutines or a vector pointing to RAM then things might get a bit tricky.

One semi-related tip is that you can get a small speed boost by enabling the fast ROM access speed ($420D.0) and jumping to a fast bank. Something like:
Code:
slow: ; bank $00
    jml  $800000 + fast
fast: ; bank $80
Which can be useful to have in interrupt handlers.
Re: how to wladx compile memorycode
by on (#236833)
Kingizor wrote:
Whenever I've executed code into RAM, I've tried to make sure it doesn't depend on location in any way, so it could be copied to any location and work just the same. That way you don't have to worry about where you load it from, and you don't have to pay attention to how you structure your ROM. It should just be as simple as having no subroutines within your RAM subroutine.

That would be a bit trickier for, say, a Super NES game that gets copied from a Super Game Boy cart into WRAM, as in Space Invaders. Even something more typical, such as a game using the Super FX or CX4 coprocessor, would need to run code in RAM to give ROM access to the coprocessor, and it isn't making good use of the system resources to make it just a wait loop.
Re: how to wladx compile memorycode
by on (#236841)
Those use cases might be a bit more exotic than I had in mind. Certainly for the kind of things I've been doing it's been sufficient. I'd be quite content to pretend coprocessors and other such addons don't exist though.

In one of those scenarios however I think it should be enough to use the base address for all jumps.

Code:
.define ram_base $1400

ram_start:
.accu 8
.index 8

mysub1:
    nop
    nop
    rts
mysub2:
    lda  #$12
    sta  $34
    rts
ram_entry:
    jsr  ram_base + (mysub1 - ram_start)
    jsr  ram_base + (mysub2 - ram_start)
    rts
ram_end:

start:
    sei
    clc
    xce

    sep  #$30
    ldx  #$00
-   lda  ram_start.w,x
    sta  ram_base.w,x
    inx
    cpx  #(ram_end - ram_start)
    bne  -

    jsr  ram_base + (ram_entry - ram_start)

-   bra  -


Is there a prettier way?
Re: how to wladx compile memorycode
by on (#236842)
Any decent assembler should be able to handle this case without crazy workarounds. That said, I don't have the experience with WLA-DX to say how to fix this.
Re: how to wladx compile memorycode
by on (#236843)
Code:
slow: ; bank $00
    jml  $800000 + fast
fast: ; bank $80


Code:
.define ram_base $1400

ram_start:
.accu 8
.index 8

mysub1:
    nop
    nop
    rts
mysub2:
    lda  #$12
    sta  $34
    rts
ram_entry:
    jsr  ram_base + (mysub1 - ram_start)
    jsr  ram_base + (mysub2 - ram_start)
    rts
ram_end:

It's enough for me.
Thanks every one
Re: how to wladx compile memorycode
by on (#236844)
How about using ".orga" or the ".base" directives? I've no experience with WLA either but on a quick glance one of those might work.
Re: how to wladx compile memorycode
by on (#236851)
Nicole wrote:
Any decent assembler should be able to handle this case without crazy workarounds. That said, I don't have the experience with WLA-DX to say how to fix this.

How would you tackle the same problem with your assembler of choice?

noyen1973 wrote:
How about using ".orga" or the ".base" directives? I've no experience with WLA either but on a quick glance one of those might work.

.base seems a nice equivalent to the manual bank jumping. The example in the readme makes it sound as though it were specifically designed for that purpose. I might be apt to forget why I'd have JSL scattered around.

I've been playing with .orga for a while but I can't get it to behave. There seems to be some connection between .orga, slots and sections, but it's far from obvious how these are supposed to interact or overlap. If I ever manage to get my test to assemble, it either wants to place the code at the specified location in ROM and/or break any combination of outside references. On one occasion everything seemed to work as intended, but just adding a few NOPs shortly before the relevant area caused everything to break again.

It's very likely I'm doing any number of things wrong, but I'm not entirely convinced this way leads to a workable solution.
Re: how to wladx compile memorycode
by on (#236854)
Kingizor wrote:
How would you tackle the same problem with your assembler of choice?

To tell you the truth, for SNES I don't really have an assembler of choice at the moment, since ca65, WLA-DX, and bass all bother me in different ways. Generally speaking something like asm6 is what I'm looking for, though:
  • Direct control over file layout with .base and .org, with no assumptions regarding output (no .bank, .segment, etc.)
  • No dealing with object files, linkers, or exporting symbols
  • Relatively standard syntax (please don't make me use // for comments)
  • Macro system powerful enough to implement e.g. Garth's structure macros

For something like asm6 I could do something like this:
Code:
.macro reloc start
    reloc_old_base = $
    reloc_new_base = start
    .base start
.endm

.macro endreloc
    .base reloc_old_base + ($ - reloc_new_base)
.endm

ram_base = $001400

; This is the start of the first bank
.base $008000

ram_start:
reloc ram_base
    mysub1:
        nop
        nop
        rts
    mysub2:
        lda  #$12
        sta  $34
        rts
    ram_entry:
        jsr  mysub1
        jsr  mysub2
        rts
endreloc
ram_end:

start:
    sei
    clc
    xce

    sep  #$30
    ldx  #$00
-   lda  ram_start.w,x
    sta  ram_base.w,x
    inx
    cpx  #(ram_end - ram_start)
    bne  -

    jsr  ram_entry

-   bra  -

Note how none of the JSRs have to do any arithmetic.
Re: how to wladx compile memorycode
by on (#236857)
There is a 65816 assembler (and later on, supporting other architectures) actively being developed which I believe will meet all 4 of those desires, plus more while keeping it simple (i..e going back to the style of early-to-late-90s days of assemblers). Can't really talk about it since it's private right now, but it's actively being worked on.
Re: how to wladx compile memorycode
by on (#236865)
In ca65, I just put the code that will run from RAM in the DATA (i.e. initialized RAM) segment. Then I just have to copy the initializer table:
Code:
        ;; finally, initialize DATA segment
        ldx #(__DATA_LOAD__ & $FFFF)
        ldy #(__DATA_RUN__ & $FFFF)
        lda #(__DATA_SIZE__)
        mvn ^__DATA_LOAD__, ^__DATA_RUN__

and then I can jump into RAM like this:
Code:
        jml bitInRAM
.endproc

.DATA
.proc bitInRAM 
loop:
        seta8
Re: how to wladx compile memorycode
by on (#236964)
Quote:
Relatively standard syntax (please don't make me use // for comments)


So ... patch bass to use ; for comments and some other symbol instead for multiple statements on a line? (I would offer to change it if someone could think of a good separator, but I already made a promise I wouldn't change the syntax again as of the latest release ...)

Not sure if my macros would work with those structure macros, but mine are Turing complete and recursive, just ... somewhat unwieldy as can be expected from any attempts to implement an ad hoc programming language inside of an assembler.

In any case, I think it's a developer rite of passage to write one's own cross assembler, just as it is to write one's own cooperative threading library ^-^
Re: how to wladx compile memorycode
by on (#236965)
Nicole wrote:
Kingizor wrote:
How would you tackle the same problem with your assembler of choice?

To tell you the truth, for SNES I don't really have an assembler of choice at the moment, since ca65, WLA-DX, and bass all bother me in different ways. Generally speaking something like asm6 is what I'm looking for, though:
  • Direct control over file layout with .base and .org, with no assumptions regarding output (no .bank, .segment, etc.)
  • No dealing with object files, linkers, or exporting symbols
  • Relatively standard syntax (please don't make me use // for comments)
  • Macro system powerful enough to implement e.g. Garth's structure macros


My old X816 assembler used the .base directive for relocated code and I've kept that for the updated version I've been working on so I can flip flop from 65816 to SPC700 assembly within the same source file without having to include preassembled binaries.
Code:
.cpu 65816
.org $808000
...do stuff in 65816

spc700start
  .cpu spc700
  .base $0000
    ...do stuff in spc700
  .endb
spc700end
.cpu 65816

mymacro .macro num
  lda #num
  tax
  tay
.endm

sayhello .macro "name","number","address","city"
  bra  +
  namedata    .dcb "name",0
  numberdata  .dcb "number",0
  addressdata .dcb "address",0
  citydata    .dcb "city",0
  cityagain   .dcb @4,0 ; 4th argument
  +
  mymacro 3; macro within a macro
  .repeat narg ; narg=number of arguments
    nop
  .endr
.endm

temp .equ    $200
temp   = ++ ; temp=201
temp   = -- ; temp=200
temp   = --16 ; temp=1f0

.if temp=1f0
  mymacro 12
.else
  mymacro 23
.endif

start:
  sayhello ""Jane Doe"",""604-123-4567"",""123 Main St"",""Vancouver""
  rts

.savebin "a.bin",$80,$8000 to $ffff ; bank $80, $8000-ffff
.savebin "a2.bin",^spc700start,!spc700start to !spc700end ; save spc700 binary
.savebin "a-all.bin" ; just save from the lowest assembled address to the highest

No linkers or object files either in the new version of my assembler. In the above example you can save binaries in the last pass at any given point in the assembly process, ideally at at the end your source. You also get an idea of the other directives. X816 was just a 65816 assembler but the updated version is able to handle SPC700, 6502 and potentially more.

The backslash is my choice for multiple statements on a line.