Rear Gunner XL – Part 2 – Setting up

I think the best way to do this conversion is type in the BASIC listing as comments, and then add our Z80 conversion of each line as we go.  This will all be stored in our main.z80 file.  That way it will be obvious what code each line translates to, and hopefully be easier to follow.

Note that this isn’t going to be the most optimal or efficient solution to converting the game, as optimising everything often leaves the code being somewhat unreadable.  I’ll offer some thoughts on how it can be improved, but I’ll leave that up to the reader 🙂

So to start:

; 5 PAUSE 50
; 10 LET A=5
; 20 LET B=7
; 30 LET C=INT (RND*26)
; 40 LET D=INT (RND*17)
; 50 PRINT AT D,C;”(graphic G)”
; 60 PRINT AT A,B;” + “
; 70 PRINT AT A-1,B;”   “
; 80 PRINT AT A+1,B;”   “
; 90 IF INKEY$=“5” THEN LET B=B-1
; 100 IF INKEY$=“8” THEN LET B=B+1
; 110 IF INKEY$=“7” THEN LET A=A-1
; 120 IF INKEY$=“6” THEN LET A=A+1
; 130 IF INKEY$=“0” THEN GOTO 150
; 140 GOTO 50
; 150 IF D<>A THEN GOTO 50
; 160 IF C=B THEN GOTO 170
; 170 PRINT AT D-1,C;”(graphic Y;SPACE;graphic T)”
; 180 PRINT AT D,C;”(SPACE;inverse SPACE;SPACE)”
; 190 PRINT AT D+1,C;”(graphic T;SPACE;graphic Y)”
; 200 RUN

 

The first line is simply a pause:

5 PAUSE 50

Which the BASIC manual tells us will wait for 50 frames – so a second (if a PAL machine). Luckily for us the ZX81 has a system variable called FRAMES which you can find in template_main, and this decreases as each frame elapses.  You’ll find details of this in the ZX81 manual, which is actually very detailed and tells you just about everything you need to know about writing in both BASIC and machine code, and is very helpful in finding the values of those special ZX81 graphics.  Keep it with you at all times!

Anyway, as the manual says, we can set it to the number of frames we wish to wait for, and loop until it reaches zero.  And, although this game only uses it once, we can put it in a function so we can reuse it another time if we want to.  There’s one gotcha with this method though – you need to keep bit 15 – the highest bit – of the FRAMES value set, otherwise things go wrong.  This equates to bit 7 of H, and so we set this when we start the wait, and ignore it when we test.

 ld hl, 50
 call FUNC_WAITFORFRAMES

; Wait for HL frames
FUNC_WAITFORFRAMES
; Load the system variable FRAMES with the number to wait for
; Remember to keep bit 15 high though
 set 7, h
 ld (FRAMES), hl
FUNC_WAITFORFRAMES_LOOP
; Read the current value
 ld hl, (FRAMES)
; Keep looping until it's zero (ignoring bit 15)
 ld a, h
 and %01111111
 or l
 jp nz, FUNC_WAITFORFRAMES_LOOP
; Return
 ret

 

The next two lines setup variables A & B:

10 LET A=5
20 LET B=7

So here we are setting up the starting position of the player’s sights.  This is simple enough – we need to set aside a byte for each in memory, and set them to the required values.  The variables will need to be at the end of the program, as since as they are not code we can’t ‘run’ them:

 ld a, 5
 ld (VARIABLE_A), a
 ld a, 7
 ld (VARIABLE_B), a
...
...
...
VARIABLE_A
 defb 0
VARIABLE_B
 defb 0

This is then followed by another two lines to setup variable C & D:

30 LET C=INT (RND*26)
40 LET D=INT (RND*17)

Now we setup the position of the enemy.  Again, we’ll need two bytes for storage:

VARIABLE_C
 defb 0
VARIABLE_D
 defb 0

Now comes the tricky bit – the position is random – between 0 and 25 for C, and 0 and 16 for D.  For this we’ll need our own random number generator function, and here’s such a routine:

; Get a random 8-bit number
FUNC_GETRANDOM
; Calculate a new 'random' number from the last one and r-register
 ld a, r
 ld hl, LMATH_GR_SEED
 add a, (hl)
 rrca
 ld (hl), a
 ret
LMATH_GR_SEED
 defb 91

It starts with a set number – 91 – and then every time it is called changes that number.  It’s not completely random, but neither is the version in BASIC.  The r register is set by the machine itself in order to keep the contents of the RAM intact, and will be different depending on when we call the function.

So we can use this function to produce a random number from 0 to 255, but we need much smaller numbers than that, so if the number isn’t within the range we need to begin with, we can keep subtracting from it until it is.

; Get a random 8-bit number within a range
; Call with B as the maximum value + 1
FUNC_GETRANDOM_RANGE
 call FUNC_GETRANDOM
FUNC_GETRANDOM_RANGE_LOOP:
 cp b
 ret c
 sub b
 jp FUNC_GETRANDOM_RANGE_LOOP

So we can now put this all together to get our random numbers:

 ld b, 26
 call FUNC_GETRANDOM_RANGE
 ld (VARIABLE_C), a
 ld b, 17
 call FUNC_GETRANDOM_RANGE
 ld (VARIABLE_D), a

So putting all this in our program gives us:

; 5 PAUSE 50
 ld hl, 50
 call FUNC_WAITFORFRAMES

; 10 LET A=5
 ld a, 5
 ld (VARIABLE_A), a

; 20 LET B=7
 ld a, 7
 ld (VARIABLE_B), a

; 30 LET C=INT (RND*26)
 ld b, 26
 call FUNC_GETRANDOM_RANGE
 ld (VARIABLE_C), a

; 40 LET D=INT (RND*17)
 ld b, 17
 call FUNC_GETRANDOM_RANGE
 ld (VARIABLE_D), a

; 50 PRINT AT D,C;”(graphic G)”
; 60 PRINT AT A,B;” + “
; 70 PRINT AT A-1,B;”   “
; 80 PRINT AT A+1,B;”   “
; 90 IF INKEY$=“5” THEN LET B=B-1
; 100 IF INKEY$=“8” THEN LET B=B+1
; 110 IF INKEY$=“7” THEN LET A=A-1
; 120 IF INKEY$=“6” THEN LET A=A+1
; 130 IF INKEY$=“0” THEN GOTO 150
; 140 GOTO 50
; 150 IF D<>A THEN GOTO 50
; 160 IF C=B THEN GOTO 170
; 170 PRINT AT D-1,C;”(graphic Y;SPACE;graphic T)”
; 180 PRINT AT D,C;”(SPACE;inverse SPACE;SPACE)”
; 190 PRINT AT D+1,C;”(graphic T;SPACE;graphic Y)”
; 200 RUN


; Wait for HL frames
FUNC_WAITFORFRAMES
 push hl
 ld (FRAMES), hl
FUNC_WAITFORFRAMES_LOOP
 ld hl, (FRAMES)
 ld a, h
 or l
 jp nz, FUNC_WAITFORFRAMES_LOOP
 pop hl
 ret


; Get a random 8-bit number
FUNC_GETRANDOM
; Calculate a new 'random' number from the last one and r-register 
 ld a, r
 ld hl, LMATH_GR_SEED
 add a, (hl)
 rrca
 ld (hl), a
 ret
LMATH_GR_SEED
 defb 91


; Get a random 8-bit number within a range
; Call with B as the maximum value + 1
FUNC_GETRANDOM_RANGE
 call FUNC_GETRANDOM
FUNC_GETRANDOM_RANGE_LOOP:
 cp b
 ret c
 sub b
 jp FUNC_GETRANDOM_RANGE_LOOP


VARIABLE_A
 defb 0
VARIABLE_B
 defb 0
VARIABLE_C
 defb 0
VARIABLE_D
 defb 0

That’s the setup done, but we can’t run it yet, as all it does is wait for a second and setup the variables. We need to code the next two sections – displaying the enemy and sights, and movement – before it’ll do anything useful…

Advertisements

2 thoughts on “Rear Gunner XL – Part 2 – Setting up

    1. You could, but it would always give the same result if you used it multiple times in the same frame – so you’d need to add some extra value to avoid possibly having the enemy always appear on a diagonal: 1,1 2,2 3,3 etc

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s