PEEK and POKE in AGD

Tutorial by Luca Bordoni, thanks to David Saphier and Allan Turvey
 
This tutorial requires a bit of assembly basics, it’s at least important to know the meaning of the PEEK and POKE commands in Basic and how the ASM calls work in AGD. It’s suggested to read and understand the tutorials about the ASM calls and Store ASM into AGD character set before continuing here.
 
 
READ/WRITE MEMORY DATA USING AGD
 
Every time an AGD game starts, the values stored in all variables, including LIVES and SCORE, are resetted. There’re just two circumstances to consider:
 
– the game ends after loosing all lives (lost game event);
– the game ends in victory (completed game event).
 
In the first case, the reset is ok. Instead, in case of victory, there could be the need to retrieve some variables if the game has to continue (e.g. residual lives and score). Since AGD doesn’t consider this recovery, it’s possible to “hang” some variables into memory locations and do the magic.
 
 
SOME ESSENTIAL CONCEPTS
 
ASM 62
It reads a single byte (0 to 255) declared by the next ASM, storing the value into the accumulator.
Assembly equivalent: LD a,N
 
ASM 58
It means “PEEK” (read) the byte stored into a memory address declared by the next couple of ASM commands (because a memory address is greater than 255), storing the byte into the accumulator.
Assembly equivalent: LD a,(NN)
 
ASM 50
It means “POKE” (write) into the memory address declared by the next couple of ASM commands, a byte previously stored into the accumulator (e.g. through an ASM 62 or ASM 58 command).
Assembly equivalent: LD (NN),a
 
 
GLOBAL VARIABLES REFERENCES IN AGD 4.7

SCORE – it’s a sequence of six ascii characters, it doesn’t handle a real number format. It stores six bytes from 34495 to 34500; each byte represents the ascii value of a number (e.g. it starts with values 48,48,48,48,48,48 because 48 is the ascii code of the “0” character)
 
SCREEN – stores a byte at 32027
LIVES  – stores a byte at 32028
LINE   – stores a byte at 32049
COLUMN – stores a byte at 32050
CLOCK  – stores a byte at 32051
RND    – stores a byte at 32052
OBJ    – stores a byte at 32053
OPT    – stores a byte at 32054
 
– stores a byte at 32029
– stores a byte at 32030
– stores a byte at 32031
– stores a byte at 32032
– stores a byte at 32033
– stores a byte at 32034
– stores a byte at 32035
– stores a byte at 32036
– stores a byte at 32037
– stores a byte at 32038
– stores a byte at 32039
– stores a byte at 32040
– stores a byte at 32041
– stores a byte at 32042
– stores a byte at 32043
– stores a byte at 32044
– stores a byte at 32045
– stores a byte at 32046
– stores a byte at 32047
– stores a byte at 32048

So, the instruction “LET LIVES = 3” is equivalent to this sequence of ASM commands:

load the following byte into accumulator
ASM 62
 
the byte is 3
ASM 3
 
POKE to the following address
ASM 50
 
the address is 32028 (AGD reserved address for LIVES)
ASM 28
ASM 125

 
 
RECOVER REMAINING LIVES AFTER A VICTORY
 
The LIVES variable is usually managed in the game initialisation and kill player events. The residual value has to be handled just in case of victory, so the events involved will be the game initialisation and the completed game.
 
In the completed game event we’ll read the byte stored in the reserved LIVES memory address. Then the value will be copied in a custom free memory address. Just for test purposes, let’s choose the last usable address 64767. Now take in mind that:
 
– when all lives are lost, the byte stored in the address 64767 will be equal to zero;
– as long as the game continues, the LIVES value (and the byte stored in high memory) will be greater than zero.

in completed game event, read and store lives value
 
PEEK 32028 (read LIVES variable)
ASM 58
ASM 28
ASM 125
 
POKE 64767 (store the previous byte in high memory)
ASM 50
ASM 255
ASM 252

Next step, in the game initialisation event, we need to detect if the play comes from a previous victory, holding the recovered LIVES value; otherwise, the lives have to be resetted.

in game initialisation event, PEEK 64767
that is, read residual lives previously stored in high memory; if it’s a new play, the result will be 0

ASM 58
ASM 255
ASM 252
 
POKE 32028
write the previously recovered byte into the LIVES reserved memory address

ASM 50
ASM 28
ASM 125
 
IF LIVES > 0
no action, just hold the recovered value
ELSE
LIVES is equal to zero when a new game starts (first play or lost all lives)
in that case load the starting value
LET LIVES = 3
ENDIF

 
 
RECOVER THE SCORE AFTER A VICTORY
 
As said, the SCORE variable stores a sequence of six ascii characters, without handling a real number format. The six bytes are stored from 34495 to 34500 and each one represents the ascii value for a character number.
 
Every time a new game starts the SCORE is resetted to zero by AGD in this way:
 
34495, 48
34496, 48
34497, 48
34498, 48
34499, 48
34500, 48
 
The number 48 is the ascii correspondence for the “0” character (find all the ascii values in the Basic Programming manual, Appendix A).
 
If we need to continue a play after a victory the score has to skip the reset by AGD; let’s explain how.
 
It’s possible to recover each byte from the above addresses using the same method explained for the LIVES variable, or using an assembly routine which does the magic. The following assembly code was written by the brilliant Allan Turvey. Few lines of assembly code to fullfill a double task:
 
– store the six bytes in high memory;
– restore them again into the SCORE addresses skipping the default reset at the game start.
 
The routine can be stored directly into the AGD character set. This is the assembly, including the comments explaining the steps:

; AGD SCORE RECALL (21 bytes)
; ***************************
; this routine starts at 31440 (address for the “:” symbol in AGD charset)
; AGD stores six ascii chrs at 34495->34500, the reserved addresses for SCORE
; the routine moves the six bytes to 64761->64766
; so data can be read starting from address 64761
;
; USR 31440 (ASM 205,208,122) stores the score in high memory 64761->64766 (use in completed game event)
; USR 31449 (ASM 205,217,122) recalls the score from high memory into 34495->34500 (use in game initialisation event)
;

score equ 34495
storescore equ 64761
org 31440
putscore: ld hl,score
ld de,storescore
jp movescore
getscore: ld hl,storescore
ld de,score
movescore: ld bc,6
ldir
ret

 
 
SCORE RESET
 
AGD doesn’t allow to reset the score through a specific instruction. Having implemented the above “score recovery” special routine, it’s also needed to reset the values if the game ends.
 
Here’s a simple routine by Allan Turvey to save in the chr-set and call from the game initialisation event:

; AGD SCORE RESET (11 bytes)
; **************************
; this routine starts at 31464 (the “=” symbol in AGD charset)
; the SCORE variable stores six ascii chrs at 34495
; the routine resets the ascii values to zero (value 48)
; use in game initialisation event, USR 31464 (ASM 205,232,122)
; after the LIVES restore, in order to check if the new play comes from a victory
;

org 31464
ld hl,34495
ld b,6
dscore: ld (hl),48
inc l
djnz dscore
ret

So here’s how the completed game and game initialisation events should finally look:

completed game event
store lives and score in high memory
 
PEEK 32028 (read LIVES variable)
ASM 58
ASM 28
ASM 125
 
POKE 64767 (store the previous byte in high memory)
ASM 50
ASM 255
ASM 252
 
USR 31440: store six SCORE bytes in high memory
ASM 205
ASM 208
ASM 122

USR 32000: force restart (don’t exit to menu)
ASM 205
ASM 0
ASM 125
 
game initialisation event
recover lives and score if play comes from a victory
otherwise reset initial values
 
PEEK 64767: read available LIVES
ASM 58
ASM 255
ASM 252
 
POKE 32028: store the result in AGD-reserved LIVES variable
ASM 50
ASM 28
ASM 125
 
if coming from a victory, the LIVES variable stores a byte greater than 0
IF LIVES > 0
  USR 31449: SCORE recall routine if game restarted from a victory
  ASM 205
  ASM 217
  ASM 122
ELSE
  it wasn’t a start from a victory (first play or lost all lives), reset
  LET LIVES 3
  USR 31464: SCORE reset routine
  ASM 205
  ASM 232
  ASM 122
ENDIF
 
CLS
 
show score previously recovered or resetted
LET LINE …
LET COLUMN …
COLOUR …
SHOWSCORE
 
show lives previously recovered or resetted
LET LINE …
LET COLUMN …
COLOUR …
DISPLAY LIVES

If we’re ok using the AGD character-set to store the score routines, download the binary file directly from here, then just inject the bin file into the AGD character-set.
Exit from the charset menu and re-enter, the changes will look like this:
 

 
Now all we have to do is to call the routines from the events, as explained.