Pointer table: Difference between revisions
(Initial draft, saving it so I don't loose it.) |
(Removal of syntax highlighter) |
||
(8 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
==Preface== | |||
A pointer table is any area of memory that is filled with the addresses of other specific areas of memory. When we store the address of an area of memory this is referred to as a "pointer", because it "points to" that area of memory. This article demonstrates how a pointer table can be used to access variable-length data. | A pointer table is any area of memory that is filled with the addresses of other specific areas of memory. When we store the address of an area of memory this is referred to as a "pointer", because it "points to" that area of memory. This article demonstrates how a pointer table can be used to access variable-length data. | ||
=The | ==The problem== | ||
When we have multiple pieces of variable-length data, such as complex sprite meta-tile definitions, we need to be able to access those pieces of data using some index number. This allows us to uniquely identify each meta-tile compactly. This also allows us to go to the previous or next meta-tile without having to know anything about the underlying data. | When we have multiple pieces of variable-length data, such as complex sprite meta-tile definitions, we need to be able to access those pieces of data using some index number. This allows us to uniquely identify each meta-tile compactly. This also allows us to go to the previous or next meta-tile without having to know anything about the underlying data. | ||
=Organization of the | ==Organization of the data file== | ||
Typically the pointer table and the data it points to is defined together within an assembly file. This allows us to easily change the data and re-arrange the pointer table as needed. | Typically the pointer table and the data it points to is defined together within an assembly file. This allows us to easily change the data and re-arrange the pointer table as needed. | ||
Line 17: | Line 19: | ||
Finally the length of the data must be stored so we know how much data to copy. We use the assembler's ability to perform math on labels for this. Not all assemblers support this however. | Finally the length of the data must be stored so we know how much data to copy. We use the assembler's ability to perform math on labels for this. Not all assemblers support this however. | ||
<pre> | |||
; Pointer table for our frames | |||
frame_pointers_lo: | |||
.byte <_frame01, <_frame02, <_frame03, <_frame04 | |||
frame_pointers_hi: | |||
.byte >_frame01, >_frame02, >_frame03, >_frame04 | |||
; Data lengths | |||
frame_data_lengths: | |||
.byte _frame02 - _frame01, _frame03 - _frame02 | |||
.byte _frame04 - _frame03, _frame_end - _frame04 | |||
; Frame Data | |||
_frame01: | |||
.byte "Some random data" | |||
_frame02: | |||
.byte "related to the frames" | |||
_frame03: | |||
.byte "that is of variable" | |||
_frame04: | |||
.byte "length", $00 | |||
_frame_end: | |||
</pre> | |||
=Using the | ==Using the pointer table== | ||
To use the pointer table to access our data we will need to use the frame index as an offset into the pointer table. We then load this pointer into a zero-page variable so we can use it to load data. | To use the pointer table to access our data we will need to use the frame index as an offset into the pointer table. We then load this pointer into a zero-page variable so we can use it to load data. | ||
Line 45: | Line 49: | ||
Example: | Example: | ||
<pre> | |||
; Routine to load the sprite frame definition for a given frame. | |||
; Y = The frame to load data for | |||
.proc load_frame_data | |||
; Local variables | |||
.segment "zp" | |||
frame_pointer: .word 0 ; Our zero-page pointer for the frame data. | |||
.segment "ram" | |||
frame_length: .byte 0 ; The number of bytes in our frame data | |||
; Routine Start | |||
.segment "prg" | |||
; Frame pointer low byte | |||
lda frame_pointers_lo,y | |||
sta frame_pointer | |||
; Frame pointer high byte | |||
lda frame_pointers_hi,y | |||
sta frame_pointer | |||
; Iterate over each byte | |||
lda frame_data_lengths,y | |||
sta frame_length | |||
ldy #0 | |||
data_loop: | |||
lda (frame_pointer),y | |||
; Do something with the frame byte in A, like transform it into OAM data | |||
iny | |||
cpy frame_length | |||
bne data_loop | |||
rts | |||
.endproc | |||
</pre> | |||
== Code pointer tables == | |||
Code is another form of data, and subroutines can be accessed through pointer tables as well. | |||
See [[Jump table]]. |
Latest revision as of 04:18, 11 September 2014
Preface
A pointer table is any area of memory that is filled with the addresses of other specific areas of memory. When we store the address of an area of memory this is referred to as a "pointer", because it "points to" that area of memory. This article demonstrates how a pointer table can be used to access variable-length data.
The problem
When we have multiple pieces of variable-length data, such as complex sprite meta-tile definitions, we need to be able to access those pieces of data using some index number. This allows us to uniquely identify each meta-tile compactly. This also allows us to go to the previous or next meta-tile without having to know anything about the underlying data.
Organization of the data file
Typically the pointer table and the data it points to is defined together within an assembly file. This allows us to easily change the data and re-arrange the pointer table as needed.
Tip: If you prefer to keep this data in a external file format such as XML and process it with a custom tool, have the tool generate the assembly file described below rather than a binary format. This is often much easier to debug and address.
In our sprite meta-tile example we might have several sprite frame definitions at the bottom of the file. These definitions will use the assembler's BYTE directive (typically .db or .byte) to declare the data. Each frame's data will be preceded by a unique label that we will reference from within the pointer table.
The pointer table usually comes at the top of the file. The pointer table will use the assembler's BYTE directive again to store the high and low bytes of our frame labels in two different tables.
Finally the length of the data must be stored so we know how much data to copy. We use the assembler's ability to perform math on labels for this. Not all assemblers support this however.
; Pointer table for our frames frame_pointers_lo: .byte <_frame01, <_frame02, <_frame03, <_frame04 frame_pointers_hi: .byte >_frame01, >_frame02, >_frame03, >_frame04 ; Data lengths frame_data_lengths: .byte _frame02 - _frame01, _frame03 - _frame02 .byte _frame04 - _frame03, _frame_end - _frame04 ; Frame Data _frame01: .byte "Some random data" _frame02: .byte "related to the frames" _frame03: .byte "that is of variable" _frame04: .byte "length", $00 _frame_end:
Using the pointer table
To use the pointer table to access our data we will need to use the frame index as an offset into the pointer table. We then load this pointer into a zero-page variable so we can use it to load data.
Example:
; Routine to load the sprite frame definition for a given frame. ; Y = The frame to load data for .proc load_frame_data ; Local variables .segment "zp" frame_pointer: .word 0 ; Our zero-page pointer for the frame data. .segment "ram" frame_length: .byte 0 ; The number of bytes in our frame data ; Routine Start .segment "prg" ; Frame pointer low byte lda frame_pointers_lo,y sta frame_pointer ; Frame pointer high byte lda frame_pointers_hi,y sta frame_pointer ; Iterate over each byte lda frame_data_lengths,y sta frame_length ldy #0 data_loop: lda (frame_pointer),y ; Do something with the frame byte in A, like transform it into OAM data iny cpy frame_length bne data_loop rts .endproc
Code pointer tables
Code is another form of data, and subroutines can be accessed through pointer tables as well. See Jump table.