So this time I came up with a couple questions....
1) I got that you can store information in registers or in variables defined at the end of a program. I have also learned that the calc's memory is divided into adresses. Does that mean I can simply store a value as follows?:
Code: LD $8000, 25
2) In simple words, how do arrays work? I don't understand how the element address, base address, index, and element size work together.
3) When working with registers, what does it mean to put one into parenthesis? For example (Stacks):
Code: LD HL, (stack_ptr)
DEC HL ; Move stack pointer to next byte of available space
LD (HL), D ; Push the high-order byte
DEC HL
LD (HL), E ; Push the low-order byte
LD (stack_ptr), HL ; Save new stack pointer
All help greatly appreciated!
The parentheses denote indirection - that is, rather the thing inside the parentheses should be treated as a memory address rather than a literal, and the operation should be carried out on memory. For example, when you say
Code: LD HL, $8000
That puts the literal value $8000 into HL. However,
Code: LD HL, ($8000)
reads the the value in memory at address $8000 and puts it into HL. (As this is a 16-bit read it'll read the byte at address $8000 and put the value into L and read the byte at address $8001 and put the value into H; this is because the Z80 is little-endian, meaning the least-significant byte appears first).
The Z80 only allows you to perform certain operations, and storing a literal value directly to memory is not permitted. Your example in question 1 would need to use an intermediate register, like this:
Code: LD A, 25
LD ($8000), A
Alternatively, you could store the address in HL first and then load a literal to that:
Code: LD HL, $8000
LD (HL), 25
Arrays are a bit more fiddly, and require a good understanding of the above. If you assume your array starts at address $8000 and each element stores a single byte then the first element will be at address $8000, the second at $8001, the third at $8002 and so on. If the elements are two bytes each then the first element would be at $8000 (as before), the second at $8002, the third at $8004 and so on. The address in memory for each element can be calculated as array_base + element_index * size_of_element.
To clarify arrays, although you might already know this, the calculator has no inherent idea what an array is or built-in features to handle array manipulation. ASM in 28 Days is showing you the way arrays are commonly implemented, and it's a good lesson, but don't let it confuse you into thinking the z80 has any primitives to handle array access and indexing beyond the index registers.
Index registers are also dreadfully slow and take up more space (generally an extra two bytes, one for the prefix and one for the 8-bit signed offset), it's often better just to use hl or another register when you can. Still, sometimes it can just be more convenient to use them and make programming much easier, even if it'd be more efficient to use another register.
If you have an array at $8000:
1,1,1,1,1
2,2,2,2,2
3,3,3,3,3
4,4,4,4,4
...the first 1 is stored at $8000. The first 2 is stored at $8005, the first 3 at $800A, and the first 4 at $800F. Thus the calculator only sees this in memory:
1,1,1,1,1,2,2,2,2,2,3,3,3,3,3.4,4,4,4,4
It has no idea that it is an array nor what its dimensions might be, it just sees it as a list of bytes in memory. You'll have to handle it as an array yourself. Since the width of each row is 5 columns, you could define:
WIDTH = 5
If you want to access array[0][0], you could do:
ld hl,array+0+(0*WIDTH)
To access array[2][3] ([X][Y]):
ld hl,array+2+(3*WIDTH)
...or essentially ld hl,array+X+Y*WIDTH.