Open Rangi42 opened 3 years ago
This was discussed previously in Discord #rgbds
: https://discord.com/channels/303217943234215948/661193788802203688/803226228655521824
Here's a tentative implementation:
cpu_command
to cpu_instr
, grrr)cpu_instr
return the bytes/patches to be written instead of directly outputting them to the object fileplain_directive
handle outputting those byte and the patchesT_OPCODE '(' cpu_command ')'
to relocexpr
, which produces a 32-bit value from the (little-endian) bytes returned by that cpu_command
relocexpr
s to const
s if they don't contain any patchesThe main problem is how to handle the patches, though.
jr
?I think that this would require the lazy expression evaluator (#663) anyway, though we may want to design it with this in mind..?
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.
If we go with a
T_OPCODE '(' cpu_command ')'
implementation, I'd like to also allowT_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, @)
).
I think OPCODE(jr nz)
would be more readable than LOW(OPCODE(jr nz, @))
, and worth the extra parser implementation.
Doubt it, it's not very DRY, especially if some syntax regarding expressions is changed. (Shortcuts, or whatever.) I prefer the dummy argument solution.
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
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:And another:
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:<[ nop ]>
(inspired by HTML CDATA)'nop'
(not currently in use)OPCODE(nop)
(likeHIGH(bc)
orDEF(Symbol)
)(I think
OPCODE
would be clearest and fit in best with existingrgbasm
syntax; too much meaningful punctuation ends up like Perl.)Some tricky details:
rl h
acts likedb $CB, $14
; this could be pretty easily handled withHIGH
andLOW
, likeld a, LOW(OPCODE(rl h)) / ld [hli], a / ld [hl], HIGH(OPCODE(rl h))
. (Little-endian order to be consistent, sodw OPCODE(rl h)
would act like plainrl h
.) Three-byte ones would still be feasible, if less convenient: dodef op = OPCODE(ld hl, $abcd)
, then work withLOW(op)
($21),HIGH(op)
($cd), andop>>16
($ab).@
: e.g. jumping ahead 5 bytes without a label is done asjr (@ + 2) + 5
. Here,OPCODE(jr 5)
could evaluate to$0518
, orOPCODE(jr z, $ff)
to$ff28
(aka the notional "rst z, $38
"). Or just disallow the destination here, soOPCODE(jr) == $18
andOPCODE(jr z) == $28
.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.