A 2D array
Three rows of four words, one element reached by row and column, and a whole column added up by stepping ix a row at a time.
Twelve words laid out as three rows of four. The program adds up a whole column, which means
stepping through memory a row at a time, and then reads one element by its row and column. The
column total ends in iy and the element in de.
Every array up to here was one line of memory. A 2D array is the same line read in rows, and the two
numbers you write in C as grid[row][col] have to be turned into one offset before anything can be
read.
You need to know: the "Arrays, strings and ix" lecture and the "Addressing on the Z80" lecture. What is new here is the stride, the distance in bytes between one row and the next, which is what walking a column adds every pass.
The three .dw lines are one array of twelve words at 0x9000, and the rows exist only in how they
are written down. Walking a column is ix parked on the top of it and moved on by STRIDE, eight
bytes, at every pass: one row further down is one whole row of elements further along in memory. The
assembler works COLS * 2 out while assembling, so nothing multiplies at run time, and iy comes
out at 00DE, which is 222, from 2, 20 and 200.
add ix, de is the only way to move an index register by an amount the program computed, because
the displacement in (ix+0) is a constant written into the instruction. That is why ld de, STRIDE
sits inside the loop: de is used to read the element two lines above it, so the stride has to be
put back before the addition.
row * COLS + col is the element's number in that one line, and doubling it turns a number of
elements into a number of bytes, which is what the address arithmetic actually needs. COLS is 4
here, so row * COLS is two add hl, hl, and the doubling for the element size is a third. A
number of columns that is not a power of two would need the shift and add routine from Multiply
and divide, which is what makes a power of two the size every 8 bit program picks for a grid.
de comes out at 00C8, which is 200, the second element of the last row. Reading it takes two
instructions, ld e, (hl) and then ld d, (hl) after an inc hl, because a word is two bytes and
every load here moves one.
Walking a row would be the same loop with inc hl twice and no add at all, since the elements
of a row sit next to each other. A column is the direction the array is not laid out in, and it
costs one addition per pass to say so.
Try changing ld c, 1 to ld c, 3. iy comes out at 01BC, which is 444, and de at 0190,
which is 400: one line moves both halves, because the column is a value both of them read.