LD IX, $40 ; Load $0040 into IX
LD IY, ($4552) ; Load value at byte $4552 into IY
LD ($8000), IX ; Load the value of IX into byte $8000
ADD IX, BC ; Add BC to IX
DEC IY ; Decrement IY
ADD HL, IY ; Illegal!
LD H, (IX) ; Note that this is okay
When using them to indirectly access memory, you can supply an 8-bit signed offset value:
LD (IX - 5), A ; Load A to the address in IX minus five.
LD B, (IY + 0) ; Load from address in IY. Could also be simply (IY).
IX is free to use at any time, but IY is used by the calculator to access
the system flags. If you modify its value, you can restore it with
LD IY, FlagsBut use of IY is discouraged for now. There are things called interrupts that have to be taken into consideration before it is safe for use.
The index registers are commonly used for addressing when HL is tied up holding something vital, or where using an index would be more efficient or coherent than a bunch of INC/DEC HL tricks. Such a case is accessing complicated structures, such as you saw on Day 5.
The high byte of IX is called either IXH or HX (remember these are unofficial registers so there are no standard names). The low byte is called either IXL or LX. The high and low bytes of IY are named similarly.
To use a part of an index register in an instruction:
.DB $DD
LD E, H
Example: SUB IYL
.DB $FD
SUB L
Be aware that once you specify a prefix, you are locked into using that index register's
half-registers. It is impossible to combine the half-registers of HL, IX, or IY in one
instruction:
.DB $DD ;LD IXH, IXL
LD H, L
DEC SP
LD (SP), H
DEC SP
LD (SP), L
And a POP HL is identical to
LD L, (SP)
INC SP
LD H, (SP)
INC SP
What you don't know is that you can use SP in a number of instructions, even use it
to store data. Using it in this way is discouraged for the same reasons as for IY.
ADC HL, SP
ADD HL, SP
ADD IX, SP
ADD IY, SP
DEC SP
EX (SP), HL
EX (SP), IX
EX (SP), IY
INC SP
LD (imm16), SP
LD SP, (imm16)
LD SP, HL
LD SP, IX
LD SP, IY
LD SP, imm16
SBC HL, SP
We can load a block of memory with one or two values really fast with SP. The DI and EI instructions are needed because the calculator will most likely crash without them:
DI ; You don't need to know why this is necessary yet
LD (save_sp), SP ; Save SP away
LD SP, $1000+1000 ; Have to start at the end because SP is
; decremented before a PUSH
LD HL, $1050 ; Memory block will be 1050 1050 ...
LD B, 125 ; PUSH 125*4=500 times, @ 2 bytes a PUSH = 1000 bytes
Loop:
PUSH HL
PUSH HL
PUSH HL
PUSH HL
DJNZ Loop
LD SP, (save_sp) ; Restore SP
EI ; You don't need to know why this is necessary yet
Here is a more concrete example, clearing out AppBackupScreen fast.
DI
LD (save_sp), SP
LD SP, AppBackupScreen + 768 ; 768 byte area
LD HL, $0000
LD B, 48 ; PUSH 48*8=384 times, @ 2 bytes a PUSH = 768 bytes
Loop:
PUSH HL ; Do multiple PUSHes in the loop to save cycles
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
PUSH HL
DJNZ Loop
LD SP, (save_sp)
EI
RET
save_sp:
.DW 0
The stack pointer can be used to terminate the program instantly like C's exit().
;Start of program
LD (save_sp), SP
.
.
.
;Somewhere within the program
LD SP, (save_sp)
RET
This could be useful if, say, you had to exit from deep within the program because of an
error, and you're unable to remove all the stack pushes (such as from within a procedure).
LD A, ($9000) ;A random number seed — make the result "more random"
LD B, A
LD A, R
ADD A, B
LD ($9000), A
But aside from that it really has no function for the programmer.