• Please review our updated Terms and Rules here

MASM struct and pointer syntax

pan069

Member
Joined
Jun 4, 2019
Messages
49
Imagine I have a STRUCT that looks like this:
Code:
STUDENT struct
  status db ?
  age db ?
  name 20 dup(?)
STUDENT ends
And that I have a pointer to such a STRUCT in memory:
Code:
student_ptr dd STUDENT
I.e. the high word of student_ptr is the segment, the lower word of student_ptr is the offset (usually zero if we use DOS 21h function 48h to allocate memory for it).

If I want to load the value the age attribute into a register, then I can simply do:
Code:
lds si,student_ptr
mov al,ds:[si].age
The mov instruction will compile to:
Code:
mov al,[si+1]
Which is correct. The age attribute "within" the STUDENT struct is at offset 1 (relative to the address/pointer).

However, now I want to compare the name of the student to something else and do a "repe cmpsb" on it against with what ever is loaded into es:di. So, I want to load ds:si with the address of the name attribute. However, if I do this:
Code:
lds si,student_ptr.name
Then si will not be loaded with 2 but ds:si will be set to the value of the "offset" of student_ptr + 2. E.g. if student_ptr is located in cs at offset 50, ds:si wil be set to the dd located at cs:52.

What is the correct syntax that I can use to load ds:si with the correct address to the name attribute, i.e. ds being the segment stored in student_prt and si set to the offset stored in student_ptr + 2?
 
Last edited:
I don't have access to my documentation at the moment, but take a look at using the MOV instruction with the OFFSET assembler directive.

Dave
 
Hi! Indeed, the pointer just points to the begining of the structure, nothing else. So adding something to the pointer just changes the address the LDS gets to the next addresses after it, not the next addresses of the memory piece it points to. I think this modification may do what you need:

Code:
.model small

.data
    STUDENT struc
        status db ?
        age db ?
        name db 20 dup(?)
    STUDENT ends

    student_ptr dd STUDENT

.code
 
    lds       si,student_ptr
    add     si,STUDENT.name
end

(I added model, end, etc in order to being able to test it. My modification is just add si,STUDENT.name).

By the way, "name" gives me a warning about using a reserved word as a variable but nevertheless it assembled well.
 
Last edited:
Thanks @carlos12 much appreciated.

I see, I did try something like that using the pointer (add si,student_ptr.name) assuming that the assembler would be smart enough the figure it out, but alas. But I see, I should be using the "struct definition itself" to obtain the offset of the attribute instead.

All working now. Many thanks!
 
Last edited:
Ran into another snag... Nested structs... 🤔
Code:
SCORE_CARD struct
  period dw ?
  score db ?
SCORE_CARD ends

STUDENT struct
  status db ?
  score_cards SCORE_CARD 10 dup(<>)
  age db ?
  name 20 dup(?)
STUDENT ends

If now trying to get the offset of "age" it returns what seems a "random" value (its not random as it seems to be always the same, I just can't see/find the relation):
Code:
mov ax,STUDENT.age
I'd expect ax to be 31, but it's not...

However, when I do SIZEOF I get the correct result for the total size of STUDENT:
Code:
mov ax,sizeof STUDENT
ax becomes 52...

Could the nested struct not be initialised correctly?
 
The SIZEOF operator is giving you the size (in bytes) of the structure.

However, your proposed mov instruction doesn't actually refer to any physical memory to hold an instantiation of the structure - so it can't work as you expect...

Hence, I don't think your problem is actually related to nested structures.

Dave
 
Thanks @daver2. What you're saying is true but the question/conversation is about "pointers to structures", see the answer by @carlos12 as it might clarify things.

However, I found the problem to the nested issue. I am actually using OpenWatcom Assembler (WASM) which is MASM compatible, at least, as it turns out only up to a certain degree. There seems to be a bug in that assembler, where, if mutiple STRUCTs have an attribute with the same name, WASM will just refer to the last STRUCT defined with that attribute rather than the STRUCT specified. Hence, I was under the impression the issue was with nested STRUCTs but its simply a bug in WASM.

I guess I have to switch to real MASM... I hate working in DOSbox... The reason I am using OpenWatcom is that I can do my build cycle on Linux... Ah, well...

For the curious, to illustrate the problem. Consider this code:
Code:
STUDENT struct
  status db ?
  age db ?
  name 20 dup(?)
STUDENT ends

TEACHER struct
  status db ?
  name db 20 dup(?)
TEACHER ends

...

mov si,STUDENT.name
In this scenario, since TEACHER also has a "name" attribute defined but the STRUCT is defined after STUDENT, regardless if you do "STUDENT.name", the compiler will simply use the offset value as defined in "TEACHER.name". I.e. using "STUDENT.name" will resolve to 1 rather than 2 since that is the offset of "name" within the TEACHER STRUCT. 🤦‍♂️
 
Its not 100% clear to me to what extend WASM is compatible with MASM, it has a -zqm=masm command line option but there is no indication of version and/or support. I assume its either an old version or not fully compatible.

When I tested to verify if the same behaviour would occur with MASM I used 6.11, which does it correctly, as expected.
 
Last edited:
I never really used WASM either but I am very famlier with the Watcom toolchain, its what I used to use back in the day for C programming. The OpenWatcom version is pretty good and since it run natively on Linux I can run a build with Make in a "normal" terminal (rather than the 80x40, no scolling back option, etc weirdo DOS interface). I.e. I build on Linux and test in DOSbox, its pretty good system.

However, I am working on an optimisation project to turn a C program I wrote into assembler, I know I can use inline assembler but I wanted to brush up on my assembler skills anyway so I am going in 100% assembler. WASM seemed like a good option but it turns out to have some quirks. I guess its under-developed from the rest of the tool chain.

I might give NASM a go and use Watcom Link for linking, if that works, I have to see. What I want to avoid is having to work in an actual DOS environment. There is also the option of setting up a Windows 2000/XP machine, or something, but I love my Linux/Sublime/terminal set up...

Also just notiched that if you use _data as an attribute in a STRUCT, the linker (wlink) does a core dump... Fair enough I guess... :)

The only workaround I have is to make sure that every attribute in a STRUCT is globally unique, which kinda defies the purpose of using STRUCTs in the first place I guess.
 
Found this interesting bit in a random MASM 6.1 document [1] from a google search (on page 96):
Field Names
Structure and union field names must be unique within a nesting level because they represent the
offset from the beginning of the structure to the corresponding field.
A label elsewhere in the code may have the same name as a structure field, but a text macro cannot.
Also, field names between structures need not be unique. Field names must be unique if you place
OPTION M510 or OPTION OLDSTRUCTS in your code or use the /Zm option from the command line,
since versions of MASM prior to 6.0 require unique field names.
(See Appendix A.)
So, I guess that clears that up. Not a bug but WASMs MASM support is probably MASM version 5 or something...

[1] https://www.mikrocontroller.net/attachment/450367/MASM61PROGUIDE.pdf
 
In MASM 5 you have to do it like this:
Code:
STUDENT struct
  student_status db ?
  student_age db ?
  student_name 20 dup(?)
STUDENT ends

TEACHER struct
  teacher_status db ?
  teacher_name db 20 dup(?)
TEACHER ends
I.e. the attribute names within the strucuture must be globally unique (to the file they are in). Weird huh? When I do it like this everything works fine but it's clumbsy as F.

Not sure about anonymous structures, can you give an example?

Edit: I am going through the MASM 5 programmers reference and there is no mention of anonymous structures.
 
Last edited:
As I said, it's hard to remember the old MASM. But no matter, the effect would be the same with our without a name. As an assembler, MASM 6 is not too awful and I've coded thousands of lines in it.
 
It seems that all the problems I have run into with WASM have been resolved by JWASM. No idea that it existed but switching to it has been a good move for me.

 
MASM doesn't run on Linux.
Depends on what you mean by "run". I run it under DOS emulation (DOSemu). It might even run under Wine.

Sonofagun, it runs fine (using Debian Bullseye x64):
Code:
c:\masm32\bin>ml /?
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.


        ML [ /options ] filelist [ /link linkoptions ]

/AT Enable tiny model (.COM file)         /nologo Suppress copyright message
/Bl<linker> Use alternate linker          /Sa Maximize source listing
/c Assemble without linking               /Sc Generate timings in listing
/Cp Preserve case of user identifiers     /Sf Generate first pass listing
/Cu Map all identifiers to upper case     /Sl<width> Set line width
/Cx Preserve case in publics, externs     /Sn Suppress symbol-table listing
/coff generate COFF format object file    /Sp<length> Set page length
/D<name>[=text] Define text macro         /Ss<string> Set subtitle
/EP Output preprocessed listing to stdout /St<string> Set title
/F <hex> Set stack size (bytes)           /Sx List false conditionals
/Fe<file> Name executable                 /Ta<file> Assemble non-.ASM file
/Fl[file] Generate listing                /w Same as /W0 /WX
/Fm[file] Generate map                    /WX Treat warnings as errors
/Fo<file> Name object file                /W<number> Set warning level
/FPi Generate 80x87 emulator encoding     /X Ignore INCLUDE environment path
/Fr[file] Generate limited browser info   /Zd Add line number debug info
/FR[file] Generate full browser info      /Zf Make all symbols public
/G<c|d|z> Use Pascal, C, or Stdcall calls /Zi Add symbolic debug info
/H<number> Set max external name length   /Zm Enable MASM 5.10 compatibility
/I<name> Add include path                 /Zp[n] Set structure alignment
/link <linker options and libraries>      /Zs Perform syntax check only
 
Last edited:
Back
Top