https://gbdev.gg8.se/wiki/index.php?title=Struct&feed=atom&action=historyStruct - Revision history2024-03-29T02:36:37ZRevision history for this page on the wikiMediaWiki 1.25.1https://gbdev.gg8.se/wiki/index.php?title=Struct&diff=935&oldid=prevPinoBatch: Two actor struct examples2020-02-19T13:28:57Z<p>Two actor struct examples</p>
<table class='diff diff-contentalign-left'>
<col class='diff-marker' />
<col class='diff-content' />
<col class='diff-marker' />
<col class='diff-content' />
<tr style='vertical-align: top;'>
<td colspan='2' style="background-color: white; color:black; text-align: center;">← Older revision</td>
<td colspan='2' style="background-color: white; color:black; text-align: center;">Revision as of 13:28, 19 February 2020</td>
</tr><tr><td colspan="2" class="diff-lineno" id="L108" >Line 108:</td>
<td colspan="2" class="diff-lineno">Line 108:</td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"></td><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"></td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div>Load and store instructions on the Z80, 68000, MIPS, and ARM architectures support offset addressing, which temporarily adds a small number to a pointer before the load or store. This allows leaving the pointer to the start of the struct in a register and specifying the offset for each read or write. On Z80 and 68000, this is slightly slower than sequential access.</div></td><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div>Load and store instructions on the Z80, 68000, MIPS, and ARM architectures support offset addressing, which temporarily adds a small number to a pointer before the load or store. This allows leaving the pointer to the start of the struct in a register and specifying the offset for each read or write. On Z80 and 68000, this is slightly slower than sequential access.</div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;"></ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;">== Examples ==</ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;">Examples of actor structs in games for 8080 family </ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;"></ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;">* [https://www.smspower.org/Development/AlexKiddInMiracleWorld-SMS#EntityData Actor structs in ''Alex Kidd in Miracle World''] on SMS Power</ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;">* [https://datacrystal.romhacking.net/wiki/Wario_Land:_Super_Mario_Land_3:RAM_map ''Wario Land'' RAM map including the actor struct] on Data Crystal</ins></div></td></tr>
</table>PinoBatchhttps://gbdev.gg8.se/wiki/index.php?title=Struct&diff=852&oldid=prevPinoBatch: /* Seeking to struct fields */ See also ASM snippets2019-02-13T00:50:57Z<p><span dir="auto"><span class="autocomment">Seeking to struct fields: </span> See also ASM snippets</span></p>
<table class='diff diff-contentalign-left'>
<col class='diff-marker' />
<col class='diff-content' />
<col class='diff-marker' />
<col class='diff-content' />
<tr style='vertical-align: top;'>
<td colspan='2' style="background-color: white; color:black; text-align: center;">← Older revision</td>
<td colspan='2' style="background-color: white; color:black; text-align: center;">Revision as of 00:50, 13 February 2019</td>
</tr><tr><td colspan="2" class="diff-lineno" id="L93" >Line 93:</td>
<td colspan="2" class="diff-lineno">Line 93:</td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"></td><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"></td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div>A load or store after any of these seeks adds 2 cycles.</div></td><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div>A load or store after any of these seeks adds 2 cycles.</div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;"></ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;">To move to the next struct in an array of structs (see also [[ASM Snippets]]): 5 bytes, 5 to 6 cycles</ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;"><pre></ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;">  ld a, l</ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;">  or $1F  ; seek to the LAST field</ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;">  ld l, a</ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;">  inc hl  ; or inc l if all instances fit in a page</ins></div></td></tr>
<tr><td colspan="2"> </td><td class='diff-marker'>+</td><td style="color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div><ins style="font-weight: bold; text-decoration: none;"></pre></ins></div></td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"></td><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"></td></tr>
<tr><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div>== Comparison to other architectures ==</div></td><td class='diff-marker'> </td><td style="background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;"><div>== Comparison to other architectures ==</div></td></tr>
</table>PinoBatchhttps://gbdev.gg8.se/wiki/index.php?title=Struct&diff=829&oldid=prevPinoBatch: rgbds-structs and how to access them2019-01-19T00:40:40Z<p>rgbds-structs and how to access them</p>
<p><b>New page</b></p><div>A [[wikipedia:Record (computer science)|record]] or [[wikipedia:struct (C programming language)|struct]] is a compound data type consisting of a sequence of fields. These fields define different properties of an entity, with different dimensions, units, etc. For an actor in a video game, these properties might be displacements, velocities, time counters, animation frame IDs, hit points, and the like.<br />
<br />
To a CPU, an array of structs appears as a 2-dimensional array of bytes. Each row is one instance, and the fields of that instance appear as columns. Reading and writing structs or other 2D arrays on the 8080 family isn't quite as efficient as on some other architectures.<br />
<br />
== Defining structures ==<br />
The [https://github.com/ISSOtm/rgbds-structs rgbds-structs] macro pack by ISSOtm for RGBDS can be used to make constants for field offsets within a struct. An example from its documentation:<br />
<pre><br />
struct NPC<br />
words 1, YPos ; 2 bytes<br />
words 1, XPos ; 2 bytes<br />
bytes 1, YBox ; 1 byte<br />
bytes 1, PlayStation ; 1 byte<br />
bytes 2, GfxID ; 2 bytes<br />
longs 2, MovementData ; 8 bytes<br />
end_struct<br />
</pre><br />
<br />
The macro pack transforms the above into the following constants:<br />
<pre><br />
NPC_YPos equ 0<br />
NPC_XPos equ 2<br />
NPC_YBox equ 4<br />
NPC_PlayStation equ 5<br />
NPC_GfxID equ 6<br />
NPC_MovementData equ 8<br />
sizeof_NPC equ 16<br />
</pre><br />
<br />
It also provides a <code>dstruct</code> macro to define a single named instance of a struct with a label for each of its fields, as described in its documentation.<br />
<br />
== Seeking to struct fields ==<br />
Imagine that you have a bunch of actors (player, enemies, etc.) in a game. Each loaded actor's state is stored in a 32-byte struct aligned to a 32-byte boundary. Given a pointer to some field in the struct, there are several ways to make HL point to the offset of a different field. Each has its own cost in bytes, cycles, and register use. The best in a particular case depends on the circumstances and the struct's layout.<br />
<br />
(All cycle counts below refer to memory cycles or mcycles, which are 1.05 MHz on a Game Boy or single-speed GBC.)<br />
<br />
Offset differs by 1: 1 byte, 1 cycle.<br />
<pre><br />
inc l<br />
</pre><br />
Sequential access to fields is always fastest. If the previous access was between A and [HL], seeking might not even cost that, as the Game Boy CPU supports post-increment and post-decrement. However, it may prove difficult to predict the order of accesses months in advance, particularly for branchy enemy AI code.<br />
<br />
Offset differs by 1 bit, such as $05 to $0D or $17 to $07: 2 bytes, 2 cycles.<br />
<pre><br />
set 4, l<br />
</pre><br />
<br />
From any known offset, clobbering A: 4 bytes, 4 cycles.<br />
<pre><br />
ld a, new_offset - old_offset<br />
add l<br />
ld l, a<br />
</pre><br />
This and other methods that clobber A are more useful for loads (<code>ld a, [hl]</code>) than for stores (<code>ld [hl], a</code>).<br />
<br />
If the low byte of a known offset is in B, C, D, or E, the same technique can be used. This might prove useful if you have a bunch of structs in the same 256-byte page.<br />
<pre><br />
ld a, new_offset - old_offset<br />
add b<br />
ld l, a<br />
</pre><br />
<br />
If DE or BC points at a known offset (the <code>this</code>-in-DE pattern): 4 bytes, 5 cycles.<br />
<pre><br />
ld hl, new_offset - old_offset<br />
add hl, de<br />
</pre><br />
<br />
From an unknown offset into the struct, clobbering A: 6 bytes, 6 cycles.<br />
<pre><br />
ld a, %11100000<br />
and a, l<br />
or a, new_offset<br />
ld l, a<br />
</pre><br />
This might be used if a subroutine left HL at some arbitrary offset into the struct.<br />
<br />
From an unknown offset into the struct, clobbering nothing: 10 bytes, 10 cycles.<br />
<pre><br />
res 0, l<br />
res 1, l<br />
res 2, l<br />
res 3, l<br />
res 4, l<br />
; Change these res to set depending on the bits of the offset<br />
</pre><br />
Not very efficient; included for completeness.<br />
<br />
If the structs are stored with 224 bytes of padding between them: 2 bytes, 2 cycles.<br />
<pre><br />
ld l, offset<br />
</pre><br />
This is the "2-dimensional RAM" pattern that many games for Z80-based computers, such as ZX Spectrum and MSX, use to save about one mcycle compared to keeping <code>this</code> in IX. The actor ID is in H, the field ID in L. To use this in practice, a game would interleave the actor structs with other data that fit in the padding, such as other structs or smaller 1-dimensional arrays. On RGBDS, interleaving this array with several other arrays may require fixing the arrays' addresses at particular locations in WRAM in order to work around RGBASM's arbitrary dichotomy between labels and constants. In any case, it does not work on 1K machines, such as ColecoVision or SG-1000.<br />
<br />
A load or store after any of these seeks adds 2 cycles.<br />
<br />
== Comparison to other architectures ==<br />
Architectures other than 8080 family have different strengths and weaknesses, which imposes tradeoffs on how structs are stored.<br />
<br />
For example, the indexed addressing modes of the 6502 and 65816 are better suited toward a structure of arrays, which transposes the 2D array to be column-major: the first fields of all instances are stored together, the second fields of all instances are stored together, and so on. Even multi-byte fields get broken up into separate arrays for the low and high bytes. Indexed mode performs a seek and load in 4 cycles or a seek and store in 5. However, the 6502 is weaker cycle-for-cycle than the 8080 at reading through large arrays, as its pointer-based addressing mode is slower. (See also [[CPU speed comparison]].)<br />
<br />
Load and store instructions on the Z80, 68000, MIPS, and ARM architectures support offset addressing, which temporarily adds a small number to a pointer before the load or store. This allows leaving the pointer to the start of the struct in a register and specifying the offset for each read or write. On Z80 and 68000, this is slightly slower than sequential access.</div>PinoBatch