Page 1 of 1

Assemler - efficient alternative to self-modifying code?

Posted: Fri Dec 19, 2025 2:04 pm
by pifu
Hi,
I need to assemble a double-indirect jump. I solved it with self-modifying code:

Code: Select all

;SWITCH:
;A:X (lo:hi) = adr, OFS:OFS+1 (lo:hi) = ofs
;jump to address stored in MEM[adr+ofs]:MEM[adr+ofs+1]

SWITCH  CLC
        ADC OFS                                 ;A <- lo(adr + ofs)
        STA @jmp + 1                            ;save it as JMP() arg, lo
        TXA
        ADC OFS + 1                             ;A <- hi(adr + ofs)
        STA @jmp + 2                            ;save it as JMP() arg, hi
@jmp    JMP ($FFFF)                             ;SMC: JMP (adr + ofs)

but that's not "ROMable", so I sketched up this one:

Code: Select all

;SWITCH:
;A:X (lo:hi) = adr, OFS:OFS+1 (lo:hi) = ofs
;jump to address stored in MEM[adr+ofs]:MEM[adr+ofs+1]

SWITCH  CLC
        ADC OFS                                 ;A <- lo(adr + ofs)
        STA TMP                                 ;TMP <- lo(adr + ofs)
        TXA
        ADC OFS + 1                             ;A <- hi(adr + ofs)
        STA TMP + 1                             ;TMP + 1 <- hi(adr + ofs)
        LDY # 1
        LDA (TMP),Y
        TAX                                     ;X <- MEM[adr+ofs+1]
        DEY
        LDA (TMP),Y                             ;A <- MEM[adr+ofs]
        STA TMP                                 ;TMP <- MEM[adr+ofs]
        STX TMP + 1                             ;TMP + 1 <- MEM[adr+ofs+1]
        JMP (TMP)
May someone write something better, please?

Re: Assemler - efficient alternative to self-modifying code?

Posted: Fri Dec 19, 2025 3:17 pm
by drogon
pifu wrote:
May someone write something better, please?
Better? Who knows Might just be different...

One way to make something that's normally ROMmable is to copy it to RAM - if you can spare a few bytes in ZP then it would work just fine - also using ZP addressing you might save a byte or 2 in ROM...

Another way:

If you have a table of addresses, then you can simply push them into the stack and RTS. Note that the table of addresses would be the address-1. From http://6502.org/tutorials/6502opcodes.html#RTS:

Code: Select all

 LDX #1 ; Index to jump to
 JSR EXEC
 JMP SOMEWHERE

LOBYTE
 .BYTE <ROUTINE0-1,<ROUTINE1-1
 .BYTE <ROUTINE2-1,<ROUTINE3-1

HIBYTE
 .BYTE >ROUTINE0-1,>ROUTINE1-1
 .BYTE >ROUTINE2-1,>ROUTINE3-1

EXEC
 LDA HIBYTE,X
 PHA
 LDA LOBYTE,X
 PHA
 RTS
This would assume a table size of <= 256 entries.

Another way might be possible if you are using a 65C02 and goes along the lines of:

Code: Select all

   asl ; Index in A, we double it
  tax
  jmp (table,x)

table:  .word target0
  .word target1
  .word target2
... etc.
the limitation here is that the table has to be <= 128 entries although checking for > 128 is possible with another line of code or 2. I do this in my 65c816 bytecode interpreter with the advantage there that the index is 16-bits wide so I can accommodate all 256 bytecodes without a secondary check.

-Gordon

Re: Assemler - efficient alternative to self-modifying code?

Posted: Fri Dec 19, 2025 4:04 pm
by pifu
drogon wrote:
One way to make something that's normally ROMmable is to copy it to RAM - if you can spare a few bytes in ZP then it would work just fine
You right! So I just need to move the JMP() instruction to 3 bytes in ZP and jump there from my first routine... Easy :o
Many thanks!

Re: Assemler - efficient alternative to self-modifying code?

Posted: Fri Dec 19, 2025 5:03 pm
by teamtempest
Expanding slightly on drogon's answer, you could also use RTS to make the jump this way:

Code: Select all

;SWITCH:
;A:X (lo:hi) = adr, OFS:OFS+1 (lo:hi) = ofs
;jump to address stored in MEM[adr+ofs]:MEM[adr+ofs+1] (must actually be address-1 of target)

SWITCH  CLC
        ADC OFS                                 ;A <- lo(adr + ofs)
        STA TMP                                 ;TMP <- lo(adr + ofs)
        TXA
        ADC OFS + 1                             ;A <- hi(adr + ofs)
        STA TMP + 1                             ;TMP + 1 <- hi(adr + ofs)
        LDY # 1
        LDA (TMP),Y
        PHA                                     ;STACK <- MEM[adr+ofs+1]
        DEY
        LDA (TMP),Y                             ;A <- MEM[adr+ofs]
        PHA                                     ;STACK <- MEM[adr+ofs]
        RTS
You do still need a couple of bytes of zero page, but you need those anyway. There's no self-modifying code and no need to copy anything from ROM.

Re: Assemler - efficient alternative to self-modifying code?

Posted: Sat Dec 20, 2025 11:30 am
by jgharston
Quote:
If you have a table of addresses, then you can simply push them into the stack and RTS. Note that the table of addresses would be the address-1.
Or push the absolute address and then jump to it with PHP:RTI