Array: Difference between revisions

Content added Content deleted
Line 43: Line 43:
An array is simply a sequence of values stored in consecutive memory locations. Its beginning is typically defined with some sort of label that points to the address where that array is stored. Whether an array is mutable or immutable depends on the hardware; in older assembly languages, an array is only typically immutable if it's stored in ROM. Home computer software that runs on a disk can define an array at compile time that can be mutable; ROM cartridge programs cannot. The syntax is the same for both, however. ROM cartridge programs will need to construct their array in ROM, copy it to RAM, and alter the copy.
An array is simply a sequence of values stored in consecutive memory locations. Its beginning is typically defined with some sort of label that points to the address where that array is stored. Whether an array is mutable or immutable depends on the hardware; in older assembly languages, an array is only typically immutable if it's stored in ROM. Home computer software that runs on a disk can define an array at compile time that can be mutable; ROM cartridge programs cannot. The syntax is the same for both, however. ROM cartridge programs will need to construct their array in ROM, copy it to RAM, and alter the copy.


<lang 6502asm>;6502 Assembly example
'''Example using [[6502 Assembly]]:'''
ArrayRAM equ $00 ;the beginning of an array, stored in zero page RAM
<lang 6502asm>ArrayRAM equ $00 ;the beginning of an array, stored in zero page RAM
ArrayROM: db 0,5,10,15,20,25,30,35,40,45,50
ArrayROM: db 0,5,10,15,20,25,30,35,40,45,50
;on Commodore 64 (for example) this can be RAM but on the NES or something similar it would be read-only</lang>
;on Commodore 64 (for example) this can be RAM but on the NES or something similar it would be read-only</lang>
Line 52: Line 52:
Almost all assembly languages have a method of loading from a memory address offset by some sort of variable amount. That offset is the index into the array. Depending on the size of each element that index is multiplied by the number of bytes each element takes up. What constitutes an "element," "row," or "column" of the array is entirely decided by the programmer. Arrays in assembly are always zero-indexed.
Almost all assembly languages have a method of loading from a memory address offset by some sort of variable amount. That offset is the index into the array. Depending on the size of each element that index is multiplied by the number of bytes each element takes up. What constitutes an "element," "row," or "column" of the array is entirely decided by the programmer. Arrays in assembly are always zero-indexed.


<lang 68000devpac>;68000 Assembly example
'''Example using [[68000 Assembly]]:'''
LEA myArray,A0 ;loading a labeled array name like this loads the address of the zeroth element
<lang 68000devpac>LEA myArray,A0 ;loading a labeled array name like this loads the address of the zeroth element
MOVE.W #4*5*1,D1 ;five elements per row, so to get the 4th row we multiply the row number by the elements per row,
MOVE.W #4*5*1,D1 ;five elements per row, so to get the 4th row we multiply the row number by the elements per row,
;times the number of bytes per element
;times the number of bytes per element
Line 75: Line 75:
====Iteration====
====Iteration====
Iteration over the elements of an array is fairly straightforward.
Iteration over the elements of an array is fairly straightforward.

<lang 68000devpac>;68000 Assembly example
'''Example using [[68000 Assembly]]:'''
LEA myArray,A0
<lang 68000devpac>LEA myArray,A0
loop:
loop:
MOVE.B (A0)+,D0
MOVE.B (A0)+,D0
Line 83: Line 84:
JMP loop</lang>
JMP loop</lang>


<lang z80>;z80 Assembly example
'''Example using [[z80 Assembly]]:'''
ld hl,myArray ;load the address of myArray into hl
<lang z80>ld hl,myArray ;load the address of myArray into hl
ld de,userRam ;load the address of work RAM into de
ld de,userRam ;load the address of work RAM into de
ld bc,myArrayEnd-myArray ;assembler directive that auto-calculates the array size using labels placed at the beginning and end.
ld bc,myArrayEnd-myArray ;assembler directive that auto-calculates the array size using labels placed at the beginning and end.
Line 91: Line 92:
====Skip-Counting====
====Skip-Counting====
Skipping elements can be easily done with a "dummy read," whereby an auto-incrementing/decrementing addressing mode is used solely for updating the pointer, or by incrementing/decrementing a loop counter multiple times per loop.
Skipping elements can be easily done with a "dummy read," whereby an auto-incrementing/decrementing addressing mode is used solely for updating the pointer, or by incrementing/decrementing a loop counter multiple times per loop.
<lang asm>;8086 Assembly example
'''Example using [[8086 Assembly]]:'''
iterate:
<lang asm>iterate:
movsb ;store [ds:si] into [es:di], increment both pointers, and decrement cx.
movsb ;store [ds:si] into [es:di], increment both pointers, and decrement cx.
lodsb ;dummy read to increment the pointer and decrement cx. The value loaded into AL gets discarded.
lodsb ;dummy read to increment the pointer and decrement cx. The value loaded into AL gets discarded.
Line 119: Line 120:


====Encoding an Array's End====
====Encoding an Array's End====
=====Null Terminator=====
'''Null Terminator'''

This method is most commonly used with strings. An ASCII value that is not associated with any keyboard key, typically 0, is placed at the end of a string. In a typical PrintString assembly routine, the routine is given a pointer to the 0th entry of the string as its only parameter. The routine reads from the pointer, prints that letter, increments the pointer, and repeats until the terminator is read, at which point the routine ends. Without the terminator, the program would not know when to stop reading and eventually crash. A string variable in [[C]] will place a 0 at the end of a string without you having to define it yourself. This method works well for strings and other arrays where the terminator's value is not a possible value for actual data. On more general arrays where the entries represent non-ASCII data, this causes problems where you have a datum that just so happens to equal the terminator.
This method is most commonly used with strings. An ASCII value that is not associated with any keyboard key, typically 0, is placed at the end of a string. In a typical PrintString assembly routine, the routine is given a pointer to the 0th entry of the string as its only parameter. The routine reads from the pointer, prints that letter, increments the pointer, and repeats until the terminator is read, at which point the routine ends. Without the terminator, the program would not know when to stop reading and eventually crash. A string variable in [[C]] will place a 0 at the end of a string without you having to define it yourself. This method works well for strings and other arrays where the terminator's value is not a possible value for actual data. On more general arrays where the entries represent non-ASCII data, this causes problems where you have a datum that just so happens to equal the terminator.


<lang asm>PrintString:; 8086 Assembly example
'''Example using [[8086 Assembly]]:'''
<lang asm>PrintString:
; input: [DS:SI] = string pointer
; input: [DS:SI] = string pointer
mov al,[ds:si]
mov al,[ds:si]
Line 139: Line 142:
The example below shows a simple implementation of an escape character. The first instance of the null terminator gets printed as-is rather than used to end the routine.
The example below shows a simple implementation of an escape character. The first instance of the null terminator gets printed as-is rather than used to end the routine.


<lang asm>PrintString:; 8086 Assembly example
'''Example using [[8086 Assembly]]:'''
<lang asm>PrintString:
; modified to use the \ as an escape character.
; modified to use the \ as an escape character.
; input: [DS:SI] = string pointer
; input: [DS:SI] = string pointer
Line 166: Line 170:




=====End Label=====
'''End Label'''

This method is best for pre-defined arrays. Its usage was shown in earlier examples. Most assemblers can create a constant based off simple arithmetic using labels. A label's numeric value is the address it gets assembled to, decided at assemble time. Placing another label immediately after the end of an array will point it to the next byte after that array. The assembler subtracts the array's beginning label from this value to create a size constant that you don't have to manually adjust if you change the number of entries the array has.
This method is best for pre-defined arrays. Its usage was shown in earlier examples. Most assemblers can create a constant based off simple arithmetic using labels. A label's numeric value is the address it gets assembled to, decided at assemble time. Placing another label immediately after the end of an array will point it to the next byte after that array. The assembler subtracts the array's beginning label from this value to create a size constant that you don't have to manually adjust if you change the number of entries the array has.
This method can be used with array variables in conjunction with a null terminator and padding the distance between the terminator and the end of the range dedicated to storing array variables.
This method can be used with array variables in conjunction with a null terminator and padding the distance between the terminator and the end of the range dedicated to storing array variables.
Line 172: Line 177:
The major Achilles' heel of this method is that it only works on arrays that are of known length prior to assembling. In other words, you <b>cannot</b> make the array larger at runtime.
The major Achilles' heel of this method is that it only works on arrays that are of known length prior to assembling. In other words, you <b>cannot</b> make the array larger at runtime.


'''Example using [[68000 Assembly]]:'''
=====Size Variable=====
<lang 68000devpac>main:
This method is best for arrays that are not pre-defined. Placed before the 0th element of the array is its maximum size. This value's location relative to the 0th element is always the same, regardless of how long the array actually is. As such, the pointer to the 0th element can be offset by a fixed negative amount to get the size. Alternatively, rather than storing the size of the array, a pointer to the last element can also be stored in front of the array. This lets you use pointer arithmetic to calculate the array's size, by subtracting the pointer of the 0th element from the last.
LEA myArray,a0
MOVE.W #(MyArray_End-MyArray)-1,D7 ;the minus 1 corrects for DBRA, the subtraction of the two labels gives us the total byte count
;for .W or .L sized data you may need to right-shift D7 by 1 or 2 respectively, depending on what you're doing
loop:
MOVE.B (A0)+,D0
JSR PrintChar ;or whatever your implementation uses to write to stdout.
DBRA D7,loop
RTS ;exit program

MyArray:
DC.B "Hello World"
MyArray_End:
EVEN ;to get the correct byte count, you'll need the EVEN directive AFTER the end label.</lang>


'''Size Value'''

This method is best for arrays that are not pre-defined. Placed before the 0th element of the array is its maximum size. This value's location relative to the 0th element is always the same, regardless of how long the array actually is. As such, the pointer to the 0th element can be offset by a fixed negative amount to get the size. Alternatively, rather than storing the size of the array, a pointer to the last element can also be stored in front of the array. This lets you use pointer arithmetic to calculate the array's size, by subtracting the pointer of the 0th element from the last. This method is commonly used in picture file formats to mark where the picture data ends.

'''Example using [[68000 Assembly]]:'''

<lang 68000devpac>MyArray:
DC.W 3,5 ;three rows, five columns
;it would have also been sufficient to have "DC.W 15" instead of "DC.W 3,5".
DC.B 1,2,3,4,5
DC.B 6,7,8,9,10
DC.B 11,12,13,14,15</lang>


===[[ATS]]===
===[[ATS]]===