[Feature Request] Add special eReader Z80 Mode
I know this is a lot of effort listed below but mainly I'm curious if its something that would even be considered. I thought about just making my own assembler but its such a daunting task when something already exists that's 98% of the way there. Feel free to just close this if it doesn't make sense.
rgbds is surprisingly useful for developing and reverse engineering eReader Z80 applications but there are a few hurdles that must be hacked around. The addition of a flag like -ereader along with the following changes would be a huge boost to productivity and code cleanliness.
Opcodes supported by eReader but missing from gb
https://github.com/FexCollects/pokecarde/blob/master/macros.asm#L4
- Load Indirect HL:
ld [nn], hl - Load HL Indirect:
ld hl, [nn] - Load Indirect HL A:
ld [hl], a - Load Indirect HL B:
ld [hl], b - Load Indirect HL C:
ld [hl], c - Load A Indirect:
ld a, [nn] - Load A Indirect DE:
ld a, [de] - Load Indirect A:
ld [nn], a - Load A Indirect HL:
ld a, [hl] - Load B Indirect HL:
ld b, [hl] - Load C Indirect HL:
ld c, [hl] - Load D Indirect HL:
ld d, [hl] - Load E Indirect HL:
ld e, [hl] - Load L Indirect HL:
ld l, [hl] - Add A Indirect HL:
add a, [hl] - Exchange DE HL:
ex de, hl
Opcdoes not supported by eReader but present in gb (should be an error)
- https://ereader.miraheze.org/wiki/Z80_Emulator_Limitations
- https://problemkaputt.de/gbatek-gba-cart-e-reader-program-code.htm (Under
Z80/8080 Format)
Nice way to extract just the machine code
While the first two items are significantly more important, it would be really nice if there was a way to dump just the assembled machine code. Right now a lot of effort is put into stripping out all the gbc stuff added.
Basically "support the Z80 CPU as an alternative to the SM83 CPU". That sounds at least possible; however, what about the final ROM structure? Are the section types (ROM0, ROMX, WRAM0, WRAMX, etc) applicable to the e-Reader, or would we need to support different ones? It might have to wait for #524.
The final ROM structure is very basic for the eReader Z80 emulator. There is just a single section that starts at address $100 and iirc everything is ram. The final resulting output (that goes into special ereader tools) is just machine code that assumes org $100
There are actually a bunch of non-trivial z80 eReader projects that use rgbds as the toolchain with a few hacks slapped on top to make it work. Its pretty much just 1. define macros to fill in the missing opcodes 2. run some python code to strip off all the extra gb bits from the rom 3. hope you didn't accidently use an unsupported op code
I'm more than happy with the answer being no btw, not trying to cause stress or headaches. Just thought if it was something that made since here before trekking off into the wilderness to make my own and then have to port over all the code
Just to clarify, re: "a single section that starts at address $100", is this sufficient for e-Reader?
SECTION "Code+Data", ROM0[$100]
...
SECTION "RAM", WRAM0[$c000]
...
Assembling and linking this would produce a ROM with $100 padding bytes at the beginning of the file, before all the actual content; and it would still produce ROMs in multiples of $4000 bytes.
If that's fine for e-Reader purposes, then I think this could just be an rgbasm feature, to support Z80 opcodes and not support SM83 ones. (Suggestion: call it -z/--z80, then have -Z/--sm83, and support opt z and opt Z so you could mix both instruction sets in the same ROM.) And it wouldn't even break any backwards compatibility, it's purely a new opt-in feature.
Yep, sorry!
Actually the vast majority of programs will only ever use the ROM0[$100] section for the following reasons.
- that section is writable on the eReader (TBH I don't think there is any read only memory in the eReader's z80 emulator)
- The single most important thing for the eReader is total binary size. Every ~2k bytes (after compression) requires a new dotcode strip so padding is avoided like the plague
- Looks like
ROM0is 16KiB which is about 8 dotcode strips, and honestly 2 is really what most programs see as the upper limit (since 2 strips fit nicely on a single printed card, 3 strips requires another card)
I'm working right now, but I throw together a minimal working eReader project this evening for you to take a look at. For the most part its fairly straight forward
https://github.com/FexCollects/minimal-ereader-z80 as promised!
I see you trim the first $100 bytes after assembling+linking. At least for now we won't have a way to avoid that, since they're necessary for making the subsequent addresses correct. Same goes for building into the smallest multiple of $2000 bytes. Unclear what nevpk and nedcmake do, and if rgbds can or should be doing any of that (maybe it's the sort of thing rgbfix could be handling?).
Unclear what nevpk and nedcmake do, and if rgbds can or should be doing any of that
nevpk applies a custom compression algorithm that the eReader natively understands
nedcmake converts the binary to the format that actually gets printed on to the card, lots of metadata stuff in there for the eReader and I think that is also the step that chunks everything out and adds checksums/error recovery
IMO neither of those make sense to add to rgbds, they are quite complex tools and very specific to the eReader.
For me the biggest wins are supporting the opcodes and stripping the binary. Stripping $100 from the front is honestly a super easy task so that doesn't need anything special but stripping the back has been an annoying process. If rgbds must do the $2000 byte padding is there any way it could signal the address where the padding started? Anything that could be used by later tools to more easily stripped the back padding.
You can read the .map file to see where sections actually cover. Although that's a nontrivial parsing job. It might be feasible to add as an rgblink feature.
The different output layout should be covered by #524, and z80 support by .setcpu none (but I don't know if we have an issue tracking that)
Yeah, I think you and/or someone else has suggested a "disable all CPU instructions" feature before, so then users could implement any instruction set they want with macros, but there's no feature request (yet?).
However, I do think Z80 could warrant built-in support. It largely overlaps with the SM83 instructions, so people wouldn't have to reinvent a lot of built-in ones with macros. Macros also would have a hard time matching all the conveniences of built-in instructions, like recognizing LOW(BC) and low ( bc ) and so on as the c register, or (potentially) warning when jp can be jr, etc.
The argument I see against it is that Z80 isn't directly Game Boy related, so supporting it opens us up to requests for support for all kinds of retro architectures, and losing focus on GB-specific convenience. Not sure if the e-Reader's existence is enough justification for making Z80 an exception.
My vote goes to “no”. I'd rather improve the macro system so that macro packs can be written more conveniently, but not upstream support for a multi-system assembler. It's fine to rely on just the base notation for registers, anyway—even GB code hardly ever uses them.
To ease the letdown should the answer end up "no":
Could e-Reader app developers use ca65 with something like my z80isa macro pack? That exists as a proof of concept that produces a ROM that runs on a Game Gear, though a custom linker configuration could support the e-Reader virtual machine's memory layout.
That z80isa macro pack starts with ".setcpu none", and is basically the same kind of solution you'd need to use RGBDS for e-Reader with the proposed "disable all instructions" feature.
@ISSOtm If we do end up going with a setcpu none-esque feature: check out the other ca65 features that macro pack is using to have macros be more like built-in instructions. In particular the .xmatch function to compare two token sequences; we could even have some limited form that only accepts certain kinds of tokens (rather than totally arbitrary sets, which could get really tricky to parse), and could let users say LoW ( dE /*mo*/ ) as equivalent to e etc.
...another limitation of macros versus built-in syntax is that they're case-sensitive, so the macro pack implementer would be deciding whether people have to write EX or ex (and they still couldn't write Ex or eX). No comment on whether it would be worth adding some kind of opt-in case-insensitivity to macro names (let alone identifiers in general); just pointing out the discrepancy for when we do eventually consider if/how to support this.
If it helps make the decision any easier, rgbds's current feature set fully supports my use cases, its just a bit hacky. While I'd like to have proper syntax for specifying the op codes and less hacks around the final binary output, I'm still pretty happy using rgbds because of the other nice features
A macro pack can afford to be more opinionated than the upstream distribution, because it can simply cater to its implementer's preferences; and if another user prefers another set of conventions, they have the option of forking the macro pack to apply their changes. (Yes, arguably that's suboptimal because forks make it difficult to track updates, but let's be real, vendoring has been the most prevalent and effective option for GB projects; consider hardware.inc or rgbds-structs, for example.)
tl;dr I think that the macro system limitations are acceptable for this scope, and what would be best to add is a way to undefine the builtin instructions so they can be overridden.