Yeah, that's a good one... And I also should start dividing my .asm code. I have made some of my own libraries (e.g. a music library that only uses variable space &C200 - &C400), but I cannot change that easily. The .asm code for PAC-01 was 7000 lines, and then it becomes harder to find the piece of code you need...
But in VASM, when I have a 32kb rom (from 4000-BFFF) and I would add ORG #C000 at the end and then define some space with ds it will add to my ROM size. Can that be prevented in some way ?
...
ORG #C000 Enemy1: Enemy1.x DS 1 Enemy1.y DS 1 Enemy1.Pow DS 1
If I need to change the RAM location of this table, just change the ORG and be done.
...
JFYI sjasmplus has STRUCT for this type of stuff:
STRUCT ENEMY x DB y DB pow DW ENDS ; place at address and reserve memory, initialize values: ORG #1234 Enemy1 ENEMY { 23, 12, 456 } ; x = 23, y = 12, pow = 456 ; map with labels already initialized memory elsewhere (at #3456) Enemy2 ENEMY = #3456 ; working with struct instance in code ld ix,Enemy2 ; base address of instance ld e,(ix+ENEMY.y) ; relative delta of member ld hl,(Enemy1.pow) ; absolute address of member
assembles to (listing, with labels table at bottom to see which labels are produced by structures):
# file opened: _.asm 1 0000 STRUCT ENEMY 2 0000 ~ x DB 3 0000 ~ y DB 4 0000 ~ pow DW 5 0000 ENDS 6 0000 7 0000 ; place at address and reserve memory, initialize values: 8 0000 ORG #1234 9 1234 17 0C C8 01 Enemy1 ENEMY { 23, 12, 456 } ; x = 23, y = 12, pow = 456 10 1238 11 1238 ; map with labels already initialized memory elsewhere (at #3456) 12 1238 Enemy2 ENEMY = #3456 13 1238 14 1238 ; working with struct instance in code 15 1238 DD 21 56 34 ld ix,Enemy2 ; base address of instance 16 123C DD 5E 01 ld e,(ix+ENEMY.y) ; relative delta of member 17 123F 2A 36 12 ld hl,(Enemy1.pow) ; absolute address of member 18 1242 # file closed: _.asm Value Label ------ - ----------------------------------------------------------- 0x0004 X ENEMY 0x0002 X ENEMY.pow 0x0000 X ENEMY.x 0x0001 ENEMY.y 0x1234 X Enemy1 0x1236 Enemy1.pow 0x1234 X Enemy1.x 0x1235 X Enemy1.y 0x3456 Enemy2 0x3458 X Enemy2.pow 0x3456 X Enemy2.x 0x3457 X Enemy2.y
does also support compound of nested structures, etc... you can check docs and automated tests for further examples and full list of features, or my tutorial game for ZX Next SpecBong (sadly I don't have any MSX example).
It's exactly designed to avoid too many hard coded magic numbers and have assembler to do the heavy lifting. :)
It might be just me, but to me this sort of code is unreadable. Probably a personal thing, e.g. when programming for Unity in C# I always declare my variables globally even though everybody says it is dangerous and bad coding. But for me it keeps the code clear and easy. Actually my C# code almost looks like MSX-basic. But it works. I agree on trying to avoid magic numbers...
hehehe, global variables are indeed quite dangerous and can lead to many bugs. For example, a common one that I see all the time when grading assignments on my student's code: they write a function with global variables. Then they try to reuse it in a context where this function is called more than once within the same call hierarchy, and then global context of the first call is overwritten by the second call, making it all break. But oh well... it's your code, so, as long as it's just you being affected, it's up to you, haha. But if you are working on a team, where others depend on that code and need to use it, please reconsider
Going back to the variable definition topic. I agree with @ro, "ds" is a better solution here as it allows you to relocate variables, add variables in between, delete, etc. without having to update all the hardcoded variables. Hardcoding addresses might be ok with a small codebase, but as soon as that grows, it'll become unmanageable.
Good one. I usually do like Micha, but reading about ro's way opened my mind...
But in VASM, when I have a 32kb rom (from 4000-BFFF) and I would add ORG #C000 at the end and then define some space with ds it will add to my ROM size. Can that be prevented in some way ?
The assembler should not emit data for DS directives. So source code like this:
org $C000 db $11, $22 ds 2
should generate a two byte output file containing:
C000: 11 22
However, if the output file is straight binary, the assembler will generally fill with $00 any DS areas before the last byte emitted. That is, if you have:
org $C000 db $11, $22 ds 2 db $33
it will probably emit a five byte file containing:
C000: 11 22 C002: 00 00 C004: 33
So you need to make sure you don't have any instructions or DB directives after your DS directives at the end if you don't want that fill happening.
If you can't turn off that fill even for DS directives that are at the very beginning or end of the file, you'll need either to post-process your binary output files to deal with this (which is a pain) or switch to an assembler that handles this properly.
Note that better assemblers can work around this by using their own output file format that permits gaps, giving you output like:
C000: 11 22 C004: 33
(Note the missing $C002-$C003 locations.) But there you'll be forced to use a program that knows how to read these special files and generate whatever binary files you need from that.
Well, actually an assembler SHOULD consider the Defined Space / DS as part of the program. Why else would a programmer define it. If the asm doesn't take DS into account, you'll have a good time bug hunting.
Well, actually an assembler SHOULD consider the Defined Space / DS as part of the program.
It is part of the program; the assembler just doesn't emit code for that part of the program (except under the conditions I noted above). ds
makes it clear that the memory does not need to be initialised, but simply labels a memory location, so there's no need to generate bytes for it.
equ
is another example of a pseudo-op that doesn't emit code. If you assemble
foo equ $C000 bar equ $C001
no code will be generated. It's effectively the same thing as
org $C000 foo ds 1 bar ds 1
Obviously the latter is more convenient when defining symbols for locations used only within your program, since you can, e.g., insert a new symbol definition at the top and the locations of the symbols after it will automatically be updated for you.
For an example of how this works when using an assembler that can generate non-contiguous output, you can assemble the following example.asm
file with Macroassembler AS to produce an output file example.p
:
org $C000 a db $11, $22 b ds 6 c db $33
If you then run plist example.p
you'll see that it's generated code only at $C000-$C001 and $C008-$C008, but not generated code for $C002-$C007:
Code-Type Segment Start-Address Length (Bytes) End-Address ---------------------------------------------------------------- Zx80 CODE 0000C000 0002 0000C001 Zx80 CODE 0000C008 0001 0000C008 creator : AS 1.42 Beta [Bld 243]/x86_64-unknown-linux altogether 3 bytes CODE
That depends on the assembler you are using. Some assemblers will add bytes to your binary even if "ds" is the very last instruction in your file, and have an alternative syntax, e.g. "ds virtual" to define space without adding anything to the binary. So, it all basically depends on which specific assembler you are using