gbdev / rgbds

Rednex Game Boy Development System - An assembly toolchain for the Nintendo Game Boy and Game Boy Color
https://rgbds.gbdev.io
MIT License
1.35k stars 172 forks source link

[Feature request] Use the bytes of an opcode in numeric expressions #823

Open Rangi42 opened 3 years ago

Rangi42 commented 3 years ago

LOAD blocks exist for whole chunks of RAM code. However, self-modifying code usually needs more fine-grained control, writing and rewriting individual bytes, not just copying N bytes from ROM to RAM. Here's an example:

; Build a function to write pixels in hAppendVWFText.
; - nothing: or [hl] / ld [hld], a / ld [hl], a / ret
; - invert: xor [hl] / ld [hld], a / ld [hl], a / ret
; - opaque: or [hl] / ld [hld], a / ret
; - invert+opaque: xor [hl] / ld [hld], a / ret
    ld hl, hAppendVWFText
    bit VWF_INVERT_F, b
    ld a, $ae ; xor [hl]
    jr nz, .invert
    ld a, $b6 ; or [hl]
.invert
    ld [hli], a
    ld a, $32 ; ld [hld], a
    ld [hli], a
    bit VWF_OPAQUE_F, b
    jr nz, .opaque
    ld a, $77 ; ld [hl], a
    ld [hli], a
.opaque
    ld [hl], $c9 ; ret

And another:

LD_A_FFXX_OP EQU $f0
JR_C_OP   EQU $38
JP_C_OP   EQU $da
LD_B_XX_OP   EQU $06
RET_OP     EQU $c9
RET_C_OP     EQU $d8

DEC_C_OP     EQU $0d
JR_NZ_OP     EQU $20
LD_A_HLI_OP  EQU $2a
LD_C_XX_OP   EQU $0e
ADD_A_OP     EQU $87

CopyBitreeCode:
    ld a, DEC_C_OP
    ld [hli], a
    ld a, JR_NZ_OP
    ld [hli], a
    ld a, 3
    ld [hli], a
    ld a, LD_A_HLI_OP
    ld [hli], a
    ld a, LD_C_XX_OP
    ld [hli], a
    ld a, 8
    ld [hli], a
    ld a, ADD_A_OP
    ld [hli], a
    ret

rgbasm can already encode instructions as bytes, so it would be convenient to have a syntax allowing those bytes as part of usual numeric expressions. A few ideas:

(I think OPCODE would be clearest and fit in best with existing rgbasm syntax; too much meaningful punctuation ends up like Perl.)

Some tricky details:

However this is done, it should reuse the parser's regular opcode handling, without needing two separate paths. I don't expect that to be a problem.

Rangi42 commented 3 years ago

This was discussed previously in Discord #rgbds: https://discord.com/channels/303217943234215948/661193788802203688/803226228655521824

ISSOtm commented 3 years ago

Here's a tentative implementation:

The main problem is how to handle the patches, though.

I think that this would require the lazy expression evaluator (#663) anyway, though we may want to design it with this in mind..?

Rangi42 commented 3 years ago

If we go with a T_OPCODE '(' cpu_command ')' implementation, I'd like to also allow T_OPCODE '(' T_Z80_JR ')', T_OPCODE '(' T_Z80_JP ccode ')', T_OPCODE '(' T_Z80_CALL ccode ')', etc for encoding the first byte without a target word.

aaaaaa123456789 commented 3 years ago

If we go with a T_OPCODE '(' cpu_command ')' implementation, I'd like to also allow T_OPCODE '(' T_Z80_JR ')', T_OPCODE '(' T_Z80_JP ccode ')', T_OPCODE '(' T_Z80_CALL ccode ')', etc for encoding the first byte without a target word.

That would be trivial with a dummy argument (OPCODE(jr nz, @)).

Rangi42 commented 3 years ago

I think OPCODE(jr nz) would be more readable than LOW(OPCODE(jr nz, @)), and worth the extra parser implementation.

ISSOtm commented 3 years ago

Doubt it, it's not very DRY, especially if some syntax regarding expressions is changed. (Shortcuts, or whatever.) I prefer the dummy argument solution.

Rangi42 commented 2 years ago

Examples using OPCODE as proposed above:

    ld hl, hAppendVWFText
    bit VWF_INVERT_F, b
    ld a, OPCODE(xor [hl])
    jr nz, .invert
    ld a, OPCODE(or [hl])
.invert
    ld [hli], a
    ld a, OPCODE(ld [hld], a)
    ld [hli], a
    bit VWF_OPAQUE_F, b
    jr nz, .opaque
    ld a, OPCODE(ld [hl], a)
    ld [hli], a
.opaque
    ld [hl], OPCODE(ret)
CopyBitreeCode:
    ld a, OPCODE(dec c)
    ld [hli], a
    ld a, LOW(OPCODE(jr nz, @))
    ld [hli], a
    ld a, 3 ; skips to 'add a'
    ld [hli], a
    ld a, OPCODE(ld a, [hli])
    ld [hli], a
    ld a, LOW(OPCODE(ld c, 8))
    ld [hli], a
    ld a, HIGH(OPCODE(ld c, 8))
    ld [hli], a
    ld a, OPCODE(add a)
    ld [hli], a
    ret