Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow declaration of data structures #98

Open
PikalaxALT opened this issue Oct 2, 2016 · 21 comments
Open

Allow declaration of data structures #98

PikalaxALT opened this issue Oct 2, 2016 · 21 comments
Labels
enhancement Typically new features; lesser priority than bugs rgbasm This affects RGBASM

Comments

@PikalaxALT
Copy link
Contributor

PikalaxALT commented Oct 2, 2016

Definition:
foo_struct: STRUCT foo, bar

Declaration:
foo_struct Name, $01, $2345

Use:
dbw Name.foo, Name.bar

@AntonioND
Copy link
Member

AntonioND commented Oct 2, 2016

I think that you can pretty much get the same effect with macros.

ELEMENT : MACRO
\1::
    DB \2
    DW \3
ENDM

    ELEMENT element1, 1, 1234
    ELEMENT element2, 1, 1234

    ld a,[element1+0] ; etc

Yes, sure, it is not very convenient to get the value of a specific field, but that's how I usually do this.

@AntonioND AntonioND added enhancement Typically new features; lesser priority than bugs rgbasm This affects RGBASM labels Apr 2, 2018
@pinobatch
Copy link
Member

Does ISSOtm/rgbds-structs do what you want?

@Rangi42
Copy link
Contributor

Rangi42 commented Feb 1, 2021

Sjasm has structure support similar to how rgbds-structs works.

@Rangi42
Copy link
Contributor

Rangi42 commented Feb 23, 2021

Here's a rough first draft for how structs might be implemented in rgbasm. I'm trying to reuse existing keywords and keep the syntax similar to what's already there. Suggestions welcome.

struct Foo begins defining a structure's members; endstruct stops that. Inside, you declare labels and allocate space with db/dw/dl/ds as if you're in a RAM section; union/nextu/endu would also be supported. This would define offset constants, rsset-style (although without affecting the value of _RS). No labels are defined and no space is allocated yet though.

Inside a RAM section, you can use dstruct Foo like the db/dw/dl/ds directives. It will then define the struct's member labels and allocate space, prefixing each label with the closest defined label above (which could be a .local one). If that above label is :: exported, so will be all the members.

struct can take a second argument like struct Foo, _ or struct Bar, _m for the separator between its prefix and each member suffix. This would make it compatible with different projects' styles of labeling.

I'm not sure about how sizeof should work yet. TBH I'd be fine without it; you can define a member at the very end of your struct for that, and then do either e.g. ld bc, NPC_Sizeof or ld bc, Player_Sizeof - Player.

Another problem: it would be nice to allow . in member names for creating local member labels (e.g. person.x, person.y, person.z), but a . isn't allowed in constant names for the offset constants.

Example 1:

struct Color
R: db
G: db
B: db ; should not get confused with register B
End:
endstruct

wColorData:
	dstruct Color
.two:
	ds 1 ; padding
	dstruct Color

This would act like:

ColorR EQU 0
ColorG EQU 1
ColorB EQU 2
; TODO: SIZEOF_Color ? #Color ? function-style SIZEOF(Color) ?

wColorData:
wColorDataR: db
wColorDataG: db
wColorDataB: db
wColorDataEnd:
.two:
	ds 1 ; padding
.twoR: db
.twoG: db
.twoB: db
.twoEnd:

Example 2:

struct NPC, _
	YPos:  dw
	XPos:  dw
	YBox:  db
	XBox:  db
	GfxID: db 2
	union
		MovementData: dl 2
	nextu
		AnimData: dl 1
		PalData:  dstruct Color
	endu
endstruct

wMapID::   ds 2
wTileset:: db
wPlayer:: dstruct NPC
wRival:   dstruct NPC

This would act like:

wMapID::   ds 2
wTileset:: db
wPlayer::
wPlayer_YPos::  dw
wPlayer_XPos::  dw
wPlayer_YBox::  db
wPlayer_XBox::  db
wPlayer_GfxID:: db 2
UNION
wPlayer_MovementData:: dl 2
NEXTU
wPlayer_AnimData:: dl 1
wPlayer_PalData::
wPlayer_PalDataR:: db
wPlayer_PalDataG:: db
wPlayer_PalDataB:: db
wPlayer_PalDataEnd::
ENDU
wRival:
wRival_YPos:  dw
wRival_XPos:  dw
wRival_YBox:  db
wRival_XBox:  db
wRival_GfxID: db 2
UNION
wRival_MovementData: dl 2
NEXTU
wRival_AnimData: dl 1
wRival_PalData:
wRival_PalDataR: db
wRival_PalDataG: db
wRival_PalDataB: db
wRival_PalDataEnd:
ENDU

@ISSOtm
Copy link
Member

ISSOtm commented Feb 23, 2021

Honestly, end<thing> to end blocks is getting kinda ridiculous. Given that this isn't "flow" syntax, I think using something closer to real blocks would be better—if only due to less global state?

Braces are obviously ineligible, but (single) brackets are fair game. I know it would feel very different from the rest of the syntax, but it's also, in concept, nothing like it anyway.

@Rangi42
Copy link
Contributor

Rangi42 commented Feb 23, 2021

There's if/endc, rept/endr, macro/endm, union/endu, and load/endl. Other pairs are pushc/popc, pushs/pops, and pusho/popo. I would suggest ends instead of endstruct, but the word "ends" seems likely to be used as a name by someone already.

[[ Double brackets ]] are tentatively going to be for inline fragments, which on the one hand is unusual for multi-line constructs, but on the other hand it goes with inline [ dereferencing ] or ( grouping ).

I don't think a new block construct like this should use brace-style delimiters unless the others are being changed to do the same, like rept 42 [[ ... ]] or macro mac { ... }. Which would be a major version update.

"nothing like it anyway": eh, it reminds me of load/endl blocks, where the labels are declared in one spot but end up going in another.

@evie-calico
Copy link
Contributor

evie-calico commented Feb 23, 2021

I prefer RGBDS Structs' use of bytes, words, and longs over db. A C-like syntax would be ideal either way

struct Structure [
    byte FieldByte
    ; or
    byte[2], FieldWord
]

Then you could do something like this:

u8 EQU byte

struct Enemy [
    u8 health
]

@ISSOtm
Copy link
Member

ISSOtm commented Feb 23, 2021

Or just byte[2] FieldWord.

@Rangi42
Copy link
Contributor

Rangi42 commented Feb 23, 2021

Something like struct Enemy [ byte[2] FieldWord ] looks way too different from typical rgbasm syntax to me.

I'd appreciate if someone could refactor existing WRAM labels into a struct with minimal editing. Like turning this from pokecrystal:

wPlayerSubStatus1:: db
wPlayerSubStatus2:: db
wPlayerSubStatus3:: db
wPlayerSubStatus4:: db
wPlayerSubStatus5:: db

to this:

struct Substatus
SubStatus1:: db
SubStatus2:: db
SubStatus3:: db
SubStatus4:: db
SubStatus5:: db
endstruct

wPlayer:: dstruct Substatus

(That does raise the issue of, "wPlayer is a pretty generic label to define there, people might want it as the prefix to more than one struct." For that the rgbds-structs syntax of dstruct Substatus, wPlayer is better, and there's a parallel between "struct Name, Infix" and "dstruct Name, Prefix".)

@evie-calico
Copy link
Contributor

evie-calico commented Feb 23, 2021

I'm worried about the readability of that. There isn't much point in implementing structs if they're going to be that similar, since you might as well just use macros.

Our goal should be to break the mold a bit, since RGBDS Structs already does most of this.

@ISSOtm
Copy link
Member

ISSOtm commented Feb 23, 2021

If #635 is merged, this would look fine:

struct Substatus [
    SubStatus1:: db
    SubStatus2:: db
    SubStatus3:: db
    SubStatus4:: db
    SubStatus5:: db
]

wPlayer:: dstruct Substatus

@Rangi42
Copy link
Contributor

Rangi42 commented Feb 23, 2021

That looks alright to me. Maybe use double brackets so they stand out more? Presumably it would support union inside. I'd also go with rgbds-structs' dstruct Substatus, wPlayer as said above.

@ISSOtm
Copy link
Member

ISSOtm commented Feb 23, 2021

I don't like that syntax because it fails to convey which is the type and which is the name.

Besides, we'd probably also want initializer syntax for ROM data.

PlayerData: struct Substatus [
    0, 1, 2, 3, 4, ; Allow trailing commas
]

EnemyData: struct Substatus [
    Substatus1 = 8, Substatus2 = 7, Substatus3 = 6, Substatus4 = 5, Substatus5 = 4
]

PeonData: struct Substatus [
    Substatus4 = NPC_SUBSTAT4 ; Commas or newlines, or both
    Substatus2 = HIGH(PEON_CONST), Substatus3 = LOW(PEON_CONST),

    ... = 0 ; Syntax for default init...

    Substatus1 = 69 ; ...not necessarily last
]

(RGBDS-structs has a more clunky syntax for initializers, since it's constrained by macro syntax.)

@Rangi42
Copy link
Contributor

Rangi42 commented Feb 23, 2021

(I edited those wLabels to LabelData, it was confusing at first because they're not WRAM.)

Agreed that a ROM initializer syntax would be useful too. Although I think dstruct should be used for that, so struct defines a new kind of structure, and dstruct uses an existing structure whether in ROM or in RAM.

@evie-calico
Copy link
Contributor

This was being discussed again in the discord so I figured I'd contribute my own idea for syntax:

STRUCT NPC
  Name:  ds 16 ; Allocate 16 bytes for a name string
  Health: db 10 ; Default of 10 health, allocate one byte
  Logic: far_pointer $0000 ; Supports macros too!
ENDS

SECTION "ROM definition", ROM0
NewNPC: object NPC [
  Name: "<=16 Letter Name" ; Even if this string is <16 characters, the ds will pad it.
  ; Health is left out, so it just defaults to 10
]

BrokenNPC: object NPC [
  Name: "This name is way too long!!!" ; Error/Warning because the string is too large
]

SECTION "RAM definition", WRAM0
RamNPC: object NPC
  ; Nothing further is needed, RAM will just go off of the default values.

This will allow the use of macros and doesn't require the user to initialize every value if they don't need to.

@Rangi42
Copy link
Contributor

Rangi42 commented Apr 25, 2021

I like that syntax; struct ... ends for definition, struct [[ ... ]] (or object or dstruct) for declaration.

@Rangi42 Rangi42 added this to the v0.8.0 milestone Nov 3, 2023
@Rangi42
Copy link
Contributor

Rangi42 commented Dec 14, 2023

Syntax from WLA-DX for comparison:

.STRUCT water
    name   ds 8
    age    db
    weight dw
.ENDST

.DSTRUCT waterdrop INSTANCEOF water VALUES
    name:   .db "tingle"
    age:    .db 40
    weight: .dw 120
.ENDST

.STRUCT drop_pair
    waterdrops: instanceof water 2
.ENDST

.DSTRUCT drops INSTANCEOF drop_pair VALUES
    waterdrops.1:        .db "qwertyui" 40
                         .dw 120
    waterdrops.2.name:   .db "tingle"
    waterdrops.2.age:    .db 40
    waterdrops.2.weight: .dw 12
.ENDST

@Rangi42
Copy link
Contributor

Rangi42 commented Dec 19, 2023

And syntax from NASM:

struc   mytype 
  mt_long:      resd    1 
  mt_word:      resw    1 
  mt_byte:      resb    1 
  mt_str:       resb    32
endstruc

mystruc: 
    istruc mytype 
        at mt_long, dd      123456 
        at mt_word, dw      1024 
        at mt_byte, db      'x' 
        at mt_str,  db      'hello, world', 13, 10, 0 
    iend

@Rangi42
Copy link
Contributor

Rangi42 commented Dec 19, 2023

And from ca65:

.struct Point
      xcoord  .word
      ycoord  .word
.endstruct

P1:      .tag    Point
P2:      .tag    Point

@aaaaaa123456789
Copy link
Member

I should probably comment at this point that struc, at, istruc and the like are all macros in NASM :P

("What do they do", I hear? struc is the easiest to explain: it creates a discarded non-output section with an offset of zero.)

@Rangi42
Copy link
Contributor

Rangi42 commented Dec 19, 2023

If we come across a third use case, maybe I'll push to actually implement DISCARD.

Edit: Hmm. I think even with DISCARDable sections, we couldn't write a macro to work that way: it can't iterate over all the labels in a section and use them as locals inside another label (e.g. you type field: ds 3 after a struct NPC, then when you do dstruct Jane, NPC it would define Jane.field). You would have to run macros for each label at the point of definition, and then the invisible section isn't actually gaining you anything since you can't use label syntax (have to do bytes 3, field as rgbds-structs already does).

@Rangi42 Rangi42 removed this from the v0.9.0 milestone Aug 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Typically new features; lesser priority than bugs rgbasm This affects RGBASM
Projects
None yet
Development

No branches or pull requests

7 participants