In this post I look at the beginning of game sequence.
Prompt Player Screen
In the original game, when the game starts, the screen is cleared, and the following is displayed on the screen, with just the 1-pixel line at the bottom (the "ground"), the total number of player 1 lives and x2 of the player sprite image at the bottom left hand corner. There are no aliens displayed nor the actual player at this stage.
SCORE<1> HI-SCORE SCORE<2>
0000 0000
PLAY PLAYER<1>
This screen remains in place for a few seconds, with the "0000" representing player 1 score flashing.
Then the PLAY PLAYER<1> text disappears, and the aliens are spawned (one by one, every refresh).
Once all the aliens are spawned, the rack immediately starts to move. However, it is a little bit later that the player sprite is displayed towards the left hand side of the image. And that's how the game begins.
So that's what happens in price; but before I get into recreating that in Javascript, I look at the code that gets executed at the beginning of the game - the very beginning.
The very beginning
At the very beginning of each game, a bunch of code is executed to set up the various data for a new game, including the following (this is not the entire initialisation of game code) - refer to Computer Archeology ("CA")
07D4: 32 FF 21 LD (p1ShipsRem),A ; Player-1 ships
07D7: 32 FF 22 LD (p2ShipsRem),A ; Player-2 ships
07DA: CD D7 00 CALL $00D7 ; Set player-1 and player-2 alien racks going right
07DD: AF XOR A ; Make a 0
07DE: 32 FE 21 LD (p1RackCnt),A ; Player 1 is on first rack of aliens
07E1: 32 FE 22 LD (p2RackCnt),A ; Player 2 is on first rack of aliens
07E4: CD C0 01 CALL InitAliens ; Initialize 55 aliens for player 1
07E7: CD 04 19 CALL InitAliensP2 ; Initialize 55 aliens for player 2
07EA: 21 78 38 LD HL,$3878 ; Screen coordinates for lower-left alien
07ED: 22 FC 21 LD (p1RefAlienY),HL ; Initialize reference alien for player 1
07F0: 22 FC 22 LD (p2RefAlienYr),HL ; Initialize reference alien for player 2
07F3: CD E4 01 CALL CopyRAMMirror ; Copy ROM mirror to RAM (2000 - 20C0)
07F6: CD 7F 1A CALL RemoveShip ; Initialize ship hold indicator
Then the following lines calls the PromptPlayer code which displays the player prompt screen.
07F9: CD 8D 08 CALL PromptPlayer ; Prompt with "PLAY PLAYER "
07FC: CD D6 09 CALL ClearPlayField ; Clear the playfield
07FF: 00 NOP ; % Why?
0800: AF XOR A ; Make a 0
0801: 32 C1 20 LD (isrSplashTask),A ; Disable isr splash-task animation
0804: CD CF 01 CALL DrawBottomLine ; Draw line across screen under player
0807: 3A 67 20 LD A,(playerDataMSB) ; Current player
080A: 0F RRCA ; Right bit tells all
080B: DA 72 08 JP C,$0872 ; Go do player 1
Prompt Player
The relevant piece of code for is shown below.
First, the screen location at which "PLAY PLAYER<1>" is printed is loaded into register-HL, and the actual string data location is loaded into register-DE, together with number of characters to be printed into register-C. Specifically, $2B11 in Javascript world is (56, 112) in terms of the top-left hand corner of the text.
Then $B0 = 176 x 1/60 second = 2933 refreshed cycles are looped, creating roughly a 3 seconds delay.
PromptPlayer:
; Print "PLAY PLAYER <n>" and blink score for 2 seconds.
;
088D: 21 11 2B LD HL,$2B11 ; Screen coordinates
0890: 11 70 1B LD DE,$1B70 ; Message "PLAY PLAYER<1>"
0893: 0E 0E LD C,$0E ; 14 bytes in message
0895: CD F3 08 CALL PrintMessage ; Print the message
0898: 3A 67 20 LD A,(playerDataMSB) ; Get the player number
089B: 0F RRCA ; C will be set for player 1
089C: 3E 1C LD A,$1C ; The "2" character
089E: 21 11 37 LD HL,$3711 ; Replace the "<1>" with "<2">
08A1: D4 FF 08 CALL NC,DrawChar ; If player 2 ... change the message
08A4: 3E B0 LD A,$B0 ; Delay of 176 (roughly 2 seconds)
08A6: 32 C0 20 LD (isrDelay),A ; Set the ISR delay value
;
08A9: 3A C0 20 LD A,(isrDelay) ; Get the ISR delay value
08AC: A7 AND A ; Has the 2 second delay expired?
08AD: C8 RET Z ; Yes ... done
08AE: E6 04 AND $04 ; Every 4 ISRs ...
08B0: C2 BC 08 JP NZ,$08BC ; ... flash the player's score
08B3: CD CA 09 CALL $09CA ; Get the score descriptor for the active player
08B6: CD 31 19 CALL DrawScore ; Draw the score
08B9: C3 A9 08 JP $08A9 ; Back to the top of the wait loop
During this 3 second loop, the RED code above calls the following routine every 4 refresh cycles. The following code sets location for the flashing score at $271C = (24, 24), and "erases" the space where "0000" is displayed. Then the code returns to $08B3, where the score is drawn, and the loop repeats for a total of $B0 refresh cycles.
08BC: 06 20 LD B,$20 ; 32 rows (4 characters * 8 bytes each)
08BE: 21 1C 27 LD HL,$271C ; Player-1 score on the screen
08C1: 3A 67 20 LD A,(playerDataMSB) ; Get the player number
08C4: 0F RRCA ; C will be set for player 1
08C5: DA CB 08 JP C,$08CB ; We have the right score coordinates
08C8: 21 1C 39 LD HL,$391C ; Use coordinates for player-2's score
08CB: CD CB 14 CALL ClearSmallSprite ; Clear a one byte sprite at HL
08CE: C3 A9 08 JP $08A9 ; Back to the top of the wait loop
Javascript / Phaser 3 version
I experimented with various ways of implementing the flashing "0000" in Javascript and Phaser, including the use of timer events. However, in the end the easiest way to mimic the code was to use the same logic, by creating the following method, and calling it from the update function.
flash() {
if (this.flashCount === 0) {
this.p1Score.setVisible(!this.p1Score.visible);
this.flashCount = 4;
}
this.flashCount--;
}
Not very sophisticated. If someone has a more elegant way of doing this, I would be grateful to know.
Here is the Codepen.
Opmerkingen