Skip to content

Commit 39b4cd6

Browse files
authored
Upgrade to v1.2.1
Fix named instantations Add warning about readability Misc internal improvements
1 parent 99551ea commit 39b4cd6

File tree

1 file changed

+192
-57
lines changed

1 file changed

+192
-57
lines changed

structs.asm

+192-57
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,19 @@
2323
; SOFTWARE.
2424

2525

26-
; Yes I need this macro, for version checking.
27-
; What did you expect from a macro pack that mostly relies on code generation at compile time?
28-
; Also this is your last chance to turn back. It doesn't get any better.
26+
; !!! WARNING ABOUT READABILITY OF THIS CODE !!!
27+
;
28+
; RGBDS, being the venerable/old/decrepit (pick on depending on mood) assembler that it is, requires
29+
; all label, variable etc. definitions to be on column 0. As in, no whitespace allowed (otherwise, syntax error)
30+
; Meanwhile, these macros tend to use a lot of nesting (requiring indenting for readability),
31+
; as well as variable definitions (requiring none to work).
32+
; As you can probably tell, those two conflict and result in very poor readability
33+
; Sadly, there is nothing I can do against that short of using a special preprocessor,
34+
; which I refuse to do for usability's sake.
35+
; You have all my apologies, how little they may matter, if you are trying to read this code
36+
; I still did my best to use explicit comments and variable names, hope they will help!
37+
38+
2939

3040
; strreplace variable_name, original_char, new_char
3141
strreplace: MACRO
@@ -47,14 +57,15 @@ ENDM
4757
; Call with the expected version string to ensure you're using a compatible version
4858
; Example: rgbds_structs_version 1.0.0
4959
rgbds_structs_version: MACRO
50-
CURRENT_VERSION equs "1,2,0"
60+
CURRENT_VERSION equs "1,2,1"
5161
EXPECTED_VERSION equs "\1"
5262
strreplace EXPECTED_VERSION, ".", "\,"
5363
check_ver: MACRO
5464
IF \1 != \4 || \2 > \5 || \3 > \6
5565
PURGE EXPECTED_VERSION
5666
ENDC
5767
ENDM
68+
5869
CHECK_VER_CALL equs "check_ver {EXPECTED_VERSION},{CURRENT_VERSION}"
5970
CHECK_VER_CALL
6071
IF !DEF(EXPECTED_VERSION)
@@ -84,12 +95,12 @@ ENDM
8495
; end_struct
8596
; Ends a struct declaration
8697
end_struct: MACRO
87-
; Set nb of fields
98+
; Set nb of fields
8899
STRUCT_NB_FIELDS equs "{STRUCT_NAME}_nb_fields"
89100
STRUCT_NB_FIELDS = NB_FIELDS
90101
PURGE STRUCT_NB_FIELDS
91102

92-
; Set size of struct
103+
; Set size of struct
93104
STRUCT_SIZEOF equs "sizeof_{STRUCT_NAME}"
94105
STRUCT_SIZEOF RB 0
95106
PURGE STRUCT_SIZEOF
@@ -99,10 +110,12 @@ STRUCT_SIZEOF RB 0
99110
ENDM
100111

101112

102-
; field_name_from_id field_id
113+
; get_nth_field_info field_id
114+
; Defines EQUS strings pertaining to a struct's Nth field
103115
; For internal use, please do not use externally
104-
field_name_from_id: MACRO
105-
FIELD_ID_STR equs "{\1}"
116+
get_nth_field_info: MACRO
117+
FIELD_ID_STR equs "{\1}" ; ID converted to a string (format: "0x2a")
118+
; Field's name
106119
STRUCT_FIELD equs STRCAT("{STRUCT_NAME}_field", STRSUB("{FIELD_ID_STR}", 2, STRLEN("{FIELD_ID_STR}") - 1))
107120
STRUCT_FIELD_NAME equs "{STRUCT_FIELD}_name"
108121
STRUCT_FIELD_TYPE equs "{STRUCT_FIELD}_type"
@@ -118,22 +131,22 @@ new_field: MACRO
118131
FAIL "Please start defining a struct, using `define_struct`"
119132
ENDC
120133

121-
field_name_from_id NB_FIELDS
122-
; Set field name
134+
get_nth_field_info NB_FIELDS
135+
; Set field name (keep in mind `STRUCT_FIELD_NAME` is *itself* an EQUS!)
123136
STRUCT_FIELD_NAME equs "\"\3\""
124137
PURGE STRUCT_FIELD_NAME
125138

126-
; Set field offset
139+
; Set field offset
127140
STRUCT_FIELD \2 (\1)
128-
; Alias this in a human-comprehensive manner
141+
; Alias this in a human-comprehensive manner
129142
STRUCT_FIELD_NAME equs "{STRUCT_NAME}_\3"
130143
STRUCT_FIELD_NAME = STRUCT_FIELD
131144

132-
; Calculate field size
145+
; Compute field size
133146
CURRENT_RS RB 0
134147
STRUCT_FIELD_SIZE = CURRENT_RS - STRUCT_FIELD
135148

136-
; Set properties
149+
; Set properties
137150
STRUCT_FIELD_NBEL = \1
138151
STRUCT_FIELD_TYPE equs STRSUB("\2", 2, 1)
139152

@@ -167,7 +180,7 @@ longs: MACRO
167180
ENDM
168181

169182

170-
; dstruct struct_type, var_name[, ...]
183+
; dstruct struct_type, INSTANCE_NAME[, ...]
171184
; Allocates space for a struct in memory
172185
; If no further arguments are supplied, the space is simply allocated (using `ds`)
173186
; Otherwise, the data is written to memory using the appropriate types
@@ -176,66 +189,186 @@ dstruct: MACRO
176189
NB_FIELDS equs "\1_nb_fields"
177190
IF !DEF(NB_FIELDS)
178191
FAIL "Struct \1 isn't defined!"
192+
ELIF _NARG != 2 && _NARG != NB_FIELDS + 2 ; We must have either a RAM declaration (no data args) or a ROM one (RAM args + data args)
193+
EXPECTED_NARG = 2 + NB_FIELDS
194+
FAIL "Invalid number of arguments, expected 2 or {EXPECTED_NARG} but got {_NARG}"
195+
ENDC
196+
197+
; Define the two fields required by `get_nth_field_info`
198+
STRUCT_NAME equs "\1" ; Which struct `get_nth_field_info` should pull info about
199+
INSTANCE_NAME equs "\2" ; The instance's base name
200+
201+
202+
; RGBASM always expands `\X` macro args, so `IF _NARG > 2 && STRIN("\3", "=")` will error out when there are only 2 args
203+
; Therefore, the condition is checked here (we can't nest the `IF`s over there because that doesn't translate well to `ELSE`)
204+
IS_NAMED_INVOCATION = 0
205+
IF _NARG > 2
206+
IF STRIN("\3", "=")
207+
IS_NAMED_INVOCATION = 1
208+
ENDC
179209
ENDC
180-
STRUCT_NAME equs "\1" ; Target this struct for `field_name_from_id`
181-
VAR_NAME equs "\2"
182210

183-
VAR_NAME:: ; Declare the struct's root
211+
IF IS_NAMED_INVOCATION
212+
; This is a named instantiation, translate that to an ordered one
213+
; This is needed because data has to be laid out in order, so some translation is needed anyways
214+
; And finally, it's better to re-use the existing code at the cost of a single nested macro, I believe
215+
MACRO_CALL equs "dstruct \1, \2" ; This will be used later, but define it now because `SHIFT` will be run
216+
; In practice `SHIFT` has no effect outside of one when invoked inside of a REPT block, but I hope this behavior is changed (causes a problem elsewhere)
217+
218+
ARG_NUM = 3
219+
REPT NB_FIELDS
220+
; Find out which argument the current one is
221+
CUR_ARG equs "\3"
222+
; Remove all whitespace to obtain something like ".name=value" (whitespace are unnecessary and complexify parsing)
223+
strreplace CUR_ARG, " ", ""
224+
strreplace CUR_ARG, "\t", ""
225+
226+
EQUAL_POS = STRIN("{CUR_ARG}", "=")
227+
IF EQUAL_POS == 0
228+
FAIL "Argument #{ARG_NUM} (\3) does not contain an equal sign in this named instantiation"
229+
ELIF STRCMP(STRSUB("{CUR_ARG}", 1, 1), ".")
230+
FAIL "Argument #{ARG_NUM} (\3) does not start with a period"
231+
ENDC
232+
233+
FIELD_ID = -1
234+
CUR_FIELD_ID = 0
235+
REPT NB_FIELDS
236+
237+
; Get the name of the Nth field and compare
238+
TMP equs STRCAT(STRCAT("{STRUCT_NAME}_field", STRSUB("{CUR_FIELD_ID}", 2, STRLEN("{CUR_FIELD_ID}") - 1)), "_name")
239+
CUR_FIELD_NAME equs TMP
240+
PURGE TMP
241+
242+
IF !STRCMP(STRUPR("{CUR_FIELD_NAME}"), STRUPR(STRSUB("{CUR_ARG}", 2, EQUAL_POS - 2)))
243+
; Match found!
244+
IF FIELD_ID == -1
245+
FIELD_ID = CUR_FIELD_ID
246+
ELSE
247+
TMP equs STRCAT(STRCAT("{STRUCT_NAME}_field", STRSUB("{CUR_FIELD_ID}", 2, STRLEN("{CUR_FIELD_ID}") - 1)), "_name")
248+
CONFLICTING_FIELD_NAME equs TMP
249+
PURGE TMP
250+
FAIL "Fields {CUR_FIELD_NAME} and {CONFLICTING_FIELD_NAME} have conflicting names (case-insensitive), cannot perform named instantiation"
251+
ENDC
252+
ENDC
253+
254+
PURGE CUR_FIELD_NAME
255+
CUR_FIELD_ID = CUR_FIELD_ID + 1
256+
ENDR
257+
PURGE CUR_FIELD_ID
258+
259+
IF FIELD_ID == -1
260+
FAIL "Argument #{ARG_NUM} (\3) does not match any field of the struct"
261+
ENDC
262+
263+
FIELD_ID_STR equs STRSUB("{FIELD_ID}", 2, STRLEN("{FIELD_ID}") - 1)
264+
INITIALIZER_NAME equs "FIELD_{FIELD_ID_STR}_INITIALIZER"
265+
PURGE FIELD_ID_STR
266+
INITIALIZER_NAME equs STRSUB("{CUR_ARG}", EQUAL_POS + 1, STRLEN("{CUR_ARG}") - EQUAL_POS)
267+
PURGE INITIALIZER_NAME
268+
269+
; Go to next arg
270+
ARG_NUM = ARG_NUM + 1
271+
SHIFT
272+
PURGE CUR_ARG
273+
274+
ENDR
275+
276+
; Now that we matched each named initializer to their order, invoke the macro again but without names
277+
FIELD_ID = 0
278+
REPT NB_FIELDS
279+
TMP equs "{MACRO_CALL}"
280+
PURGE MACRO_CALL
281+
FIELD_ID_STR equs STRSUB("{FIELD_ID}", 2, STRLEN("{FIELD_ID}") - 1)
282+
GET_INITIALIZER_VALUE equs "INITIALIZER_VALUE equs \"\{FIELD_{FIELD_ID_STR}_INITIALIZER\}\""
283+
PURGE FIELD_ID_STR
284+
GET_INITIALIZER_VALUE
285+
PURGE GET_INITIALIZER_VALUE
286+
MACRO_CALL equs "{TMP}, {INITIALIZER_VALUE}"
287+
PURGE TMP
288+
PURGE INITIALIZER_VALUE
289+
FIELD_ID = FIELD_ID + 1
290+
ENDR
291+
292+
PURGE FIELD_ID
293+
; Clean up vars for nested invocation, otherwise some `equs` will be expanded
294+
PURGE INSTANCE_NAME
295+
PURGE STRUCT_NAME
296+
PURGE IS_NAMED_INVOCATION
297+
PURGE NB_FIELDS
184298

299+
MACRO_CALL ; Now do call the macro
300+
PURGE MACRO_CALL
301+
302+
303+
ELSE
304+
305+
306+
INSTANCE_NAME:: ; Declare the struct's root
307+
308+
; Start defining fields
185309
FIELD_ID = 0
186-
REPT NB_FIELDS
310+
REPT NB_FIELDS
311+
312+
get_nth_field_info FIELD_ID
187313

188-
field_name_from_id FIELD_ID
189-
FIELD_NAME equs STRCAT("{VAR_NAME}_", STRUCT_FIELD_NAME)
314+
FIELD_NAME equs STRCAT("{INSTANCE_NAME}_", STRUCT_FIELD_NAME)
190315
FIELD_NAME::
191-
IF _NARG == 2 ; RAM definition, no data
192-
ds STRUCT_FIELD_SIZE
193-
ELSE
316+
317+
; We have defined a label, but now we also need the data backing it
318+
; There are basically two options:
319+
IF _NARG == 2 ; RAM definition, no data
320+
ds STRUCT_FIELD_SIZE
321+
ELSE
322+
194323
TMP equs STRCAT("\{", STRCAT("{STRUCT_FIELD_TYPE}", "\}")) ; Temp var for double deref because "{{STRUCT_FIELD_TYPE}}" is a syntax error
195324
DATA_TYPE equs STRCAT("D", TMP)
196-
PURGE TMP
325+
PURGE TMP
326+
197327
SHIFT_FIELDS equs ""
198-
REPT STRUCT_FIELD_NBEL
199-
DATA_TYPE \3
200-
SHIFT
201-
; Stupid hack because RGBDS saves the macro arguments when entering REPT blocks
328+
REPT STRUCT_FIELD_NBEL
329+
DATA_TYPE \3
330+
SHIFT
331+
; Stupid hack because RGBDS saves the macro arguments when entering REPT blocks
202332
TMP equs "{SHIFT_FIELDS}\n\tSHIFT"
203-
PURGE SHIFT_FIELDS
333+
PURGE SHIFT_FIELDS
204334
SHIFT_FIELDS equs "{TMP}"
205-
PURGE TMP
206-
ENDR
207-
SHIFT_FIELDS
208-
PURGE SHIFT_FIELDS
209-
PURGE DATA_TYPE
210-
ENDC
211-
212-
; Clean up vars for next iteration
213-
PURGE FIELD_ID_STR
214-
PURGE STRUCT_FIELD
215-
PURGE STRUCT_FIELD_NAME
216-
PURGE STRUCT_FIELD_TYPE
217-
PURGE STRUCT_FIELD_NBEL
218-
PURGE STRUCT_FIELD_SIZE
219-
PURGE FIELD_NAME
335+
PURGE TMP
336+
ENDR
337+
SHIFT_FIELDS
338+
PURGE SHIFT_FIELDS
339+
PURGE DATA_TYPE
340+
ENDC
341+
342+
; Clean up vars for next iteration
343+
PURGE FIELD_ID_STR
344+
PURGE STRUCT_FIELD
345+
PURGE STRUCT_FIELD_NAME
346+
PURGE STRUCT_FIELD_TYPE
347+
PURGE STRUCT_FIELD_NBEL
348+
PURGE STRUCT_FIELD_SIZE
349+
PURGE FIELD_NAME
220350

221351
FIELD_ID = FIELD_ID + 1
222-
ENDR
352+
ENDR
223353

224354

225-
; Define variable's properties from struct's
355+
; Define variable's properties from struct's
226356
\2_nb_fields = NB_FIELDS
227357
sizeof_\2 = sizeof_\1
228358

229359

230-
; Clean up
231-
PURGE NB_FIELDS
232-
PURGE STRUCT_NAME
233-
PURGE VAR_NAME
234-
PURGE FIELD_ID
360+
; Clean up
361+
PURGE FIELD_ID
362+
; Make sure to keep what's here in sync with cleanup at the end of a named invocation
363+
PURGE INSTANCE_NAME
364+
PURGE STRUCT_NAME
365+
PURGE IS_NAMED_INVOCATION
366+
PURGE NB_FIELDS
367+
ENDC
235368
ENDM
236369

237370

238-
; dstructs struct_type, var_name
371+
; dstructs nb_structs, struct_type, INSTANCE_NAME
239372
; Allocates space for an array of structs in memory
240373
; Each struct will have the index appended to its name **as hex**
241374
; (for example: `dstructs 32, NPC, wNPC` will define wNPC0, wNPC1, and so on until wNPC1F)
@@ -244,10 +377,12 @@ ENDM
244377
dstructs: MACRO
245378
STRUCT_ID = 0
246379
REPT \1
247-
STRUCT_DEF equs STRCAT("dstruct \2, \3", STRSUB("{STRUCT_ID}", 2, STRLEN("{STRUCT_ID}") - 1))
248-
STRUCT_DEF
249-
PURGE STRUCT_DEF
380+
STRUCT_ID_STR equs STRSUB("{STRUCT_ID}", 2, STRLEN("{STRUCT_ID}") - 1)
381+
dstruct \2, \3{STRUCT_ID_STR}
382+
383+
PURGE STRUCT_ID_STR
250384
STRUCT_ID = STRUCT_ID + 1
251385
ENDR
386+
252387
PURGE STRUCT_ID
253388
ENDM

0 commit comments

Comments
 (0)