Info about assembly "variables"

Page 2/2
1 |

By Micha

Expert (103)

Micha's picture

21-03-2023, 10:07

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 ?

By Ped7g

Expert (67)

Ped7g's picture

21-03-2023, 13:08

ro wrote:

...

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. :)

By Micha

Expert (103)

Micha's picture

21-03-2023, 14:32

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...

By santiontanon

Paragon (1805)

santiontanon's picture

21-03-2023, 16:55

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 Wink

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.

By thegeps

Paragon (1189)

thegeps's picture

22-03-2023, 08:53

Good one. I usually do like Micha, but reading about ro's way opened my mind...

By cjs

Expert (106)

cjs's picture

27-04-2023, 14:23

Micha wrote:

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.

By ro

Scribe (4963)

ro's picture

27-04-2023, 16:48

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.

By cjs

Expert (106)

cjs's picture

27-04-2023, 17:43

ro wrote:

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

By santiontanon

Paragon (1805)

santiontanon's picture

27-04-2023, 19:09

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 Smile

Page 2/2
1 |