If you have something like
struct Monster {
unsigned char hitPoints;
unsigned char damage;
unsigned char shieldLevel;
char* name;
};
static Monster s_monsters[] = {
{ 5, 1, 0, "orc", },
{ 50, 10, 5, "dragon", },
{ 10, 3, 1, "goblin", },
};
that's a no-no on 6502. You generally need to store those in parallel byte arrays including the string pointer's upper and lower bytes each in separate arrays. That was usually done with assembler macros or compile time tools but it was important not to have to generate addresses that have to manually assembled in 6502. Similarly there's no multiply so math like sizeof(Monster) * index.Edit: Found it online here: https://github.com/ESWAT/john-carmack-plan-archive/blob/mast...
I wonder what Carmack had in mind.
---
P.S. In my comment there, I didn't mention the possibility of having the arrays interleaved. Then it looks like a struct in memory (byte 0 is X and byte is DX, say), and you add the field offset to the base of the array. It's not uncommon for assemblers to have some syntax to make it easy to specify the layout of each struct, to simplify future changes, e.g.:
STRUCT ITEM
X DS.B 1
DX DS.B 1
ENDSTRUCT
CLC
LDA TABLE+ITEM.X,X
ADC TABLE+ITEM.DX,X
STA TABLE+ITEM.X,X
LDA #0
STA TABLE+ITEM.DX,X
The disadvantages of this are that it reduces the number of accessible items (the X register is only 8 bits...); it makes moving from item to item more difficult (all you can do with the X register is increment or decrement it, but now you need to add arbitrary values to it); and it means there are invalid item indexes (e.g., in the example above, index 1 is not valid).And the advantages... well, there aren't any, really, I don't think? Everything has to be done 1 byte at a time, so it makes no difference where the bytes are relative to one another... the code ends up the same anyway. (If anything, because you can do INX/DEX to get from item to item, it's actually slightly neater to have a separate table for each byte.) So you might as well do that.
https://stackoverflow.com/questions/45667636/advantages-of-a...
I heard there were C compilers for the Game Boy as well but as far I know they were never widely used.
That said, with a little work you could likely coax the linker into doing a lot of the work for you. You would have to be a little careful though, as code in different banks could not effectively call each-other directly, even though the compiler would likely not yell at you. But you could just organize your code so that the code for each bank is in it's own section, and then tell the linker to link each of those sections at the address that it will be switched in at (Likely the same address for most of them). It might just barf at you, but a linker specifically for systems that do memory banking should have a way to express this.
That's why, even if you hacked so that Goomba's death behavior should point to the same as that of a Drybones, it would fail as the subroutine for that behavior isn't in the same bank as the Goomba.
They had the same features up until at least Xbox 360; they're just called 'overlays' on non execute from ROM systems. It doesn't really take an esoteric compiler or environment.
On old (16-bit) x86 you used "far" pointers, which were larger for cross segment access. These were larger and I assume slower since they encoded both the within-segment address and the segment number.
And admittedly, it can be hard to see where the hand-coded assembly ends and the compiled C code begins. There are occasional stylistic clues, but I'm generally spending more time figuring out what something does and why it's written than I am which language each piece was coded in. Most of the file and memory handling seems to be done through calls to the C library in that game.
I've had some thoughts on this before and I think it boils down to the requirements. Beyond the obvious (the speed and memory requirements for your application):
If a 256 byte separate data stack is large enough, and particularly if you can put that all in zero page you can save many cycles. Then you can use the X index register as a stack pointer, benefiting from shorter and faster zero page indexed addressing for stack-relative loading and storing and using values on the stack directly for indirection using the indirect addressing modes without first copying stuff to zero page.
That's not to say that Z80 isn't a much better target for a C compiler. Among 16-bit 6800-likes, 6809 doesn't seem half bad, though.
I've seen people say this a repeatedly when talking about "embedded" environments, but never understood why - won't it end up compiling to the same thing either way?
'a++' (postfix) and '++a' (prefix) have slightly different meanings in terms of return values. Postfix will return the value of 'a' before the increment, while prefix will return 'a' after the increment operation.
In a simple compiler, you would store the previous value of 'a' in the case of postfix in case you need to assign it. This results in a superfluous store instruction in most cases.
Of course, a smarter compiler could see the lack of assignment and throw away the store instruction during an optimization pass.
/* a = (b * 2) + 1 */
a = b; /* mov a, b */
a <<= 1; /* shl a, 1 */
a |= 1; /* or a, 1 */The difference in the two is that the latter has to produce the original value, presumably by keeping it in a temporary variable. A compiler which looks at all the code in a function when optimising, rather than just blindly emitting code for each individual operation, will see that the value is not needed, and delete the temporary.
I guess cc65 is a bit more basic. Compilers for more unusual embedded environments are often a bit more basic, hence the general advice for the embedded context.
I think you could probably fix the compiler for this trivial case in the time it takes you to write down the advice though.
If you mean "increment g" then write "++g"
If you mean "temp = g; increment g; return g" then write "g++"
It doesn't really matter that the compiler may optimize out the "temp". If you're writing "g++" you're indicating you want the value before it's been incremented.
g++;
++g;if(g++) {
//do something
}
Which obviously will do something if g was non-zero before incrementing it by one. It's a really poor way of writing code, but I have seen it done.
However, it's an altogether different situation to the one I wrote about, given that the value of the incrementing expression is specifically used in that case.
I keep meaning to test this in C++. I presume that it would for fundamental types, but wouldn't be able to be objects, because the side effects of that operation could be in another module. I use ++i for C++ iterators for that reason, and the habit tends to spill over to C.
In my not so humble opinion, almost all of those are limitations of a compiler, not of the CPU. Then yes, on a 8-bit CPU, you'd better avoid larger words unless you really have to or it will be slow.
Then it also depends on the IO and video architecture. The PC ones were a bit of a pain.
In any case, 64K address space is not the real limiter with a 6502 --- it's other things, like the tiny number of registers, relative shortage of 16-bit addressing modes, and fixed(!) 256-byte stack which make it difficult to program in C. Even the 6800, with its 16-bit index and stack pointer registers, would be easier.
With this important note: "clean unaltered C code will compile into very slow code that takes up too much of the limited memory space."
In other words, "C" written for such a CPU will be in a vastly different style from more "normal" C, so that it might be better to just use Asm.
Then again, 8051s, PICs, and other extremely constrained MCUs have been targeted by "C" (subsets), so it's definitely possible if a bit awkward. Personally, I think something like an 8080 would be the minimum to do relatively comfortable C programming in, with a Z80 being far more preferable.