;*************************************** ; MOON MADNESS - EPISODE 2 ; An example game for ASSEMBLE IT ; ; Written by RICHARD BAYLISS ; (C)2018 The New Dimension ;http://tnd64.unikat.sk/assemble_it.html ;*************************************** ;Variables: MusicInit = $1000 ;Music player initialise MusicPlay = $1003 ;Music player play LEVEL = $02 ;A zeropage to represent the game's level (Game speed) LEVELTIME1 = $03 ;A zeropage to represent the game's playing time before LEVELTIME2 = $04 ;moving on to the next level of the game. (8 Levels max) FIREBUTTON = $05 ;Zeropage to control firebutton pressing SHIELDDIGIT1 = $06 ;First digit for shield counter SHIELDDIGIT2 = $07 ;Second digit for shield counter ANIMDELAY = $08 ;Player animation delay ANIMPOINTER = $09 ;Player animation pointer !TO "MOONMADNESS.PRG",CBM ;Generate PRG file *=$0801 !BASIC 2018,$4000 ;SYS 16384 ;Jump to game code. ;Import some music for the game *=$1000 !BINARY "BIN/MUSIC.PRG",,2 ;My froopy SID disco music for Moon Madness II ;Import game sprites ($2000-$3000) since amount of sprites ;for this game are very small. *=$2000 !BINARY "BIN/SPRITESV1.BIN" ;Raw binary sprite file ;Since no other sprites available here. Place game charset at $2800 (Made in Charpad V2.0 ;exported as raw binary.) *=$2800 !BINARY "BIN/CHARSET.BIN" ;Then place game screen data at $3000-$33e8 (Made in Charpad V2.0 ;exported as raw binary.) *=$3000 Matrix !BINARY "BIN/SCREEN.BIN" ;Then attributes/colour data (Made in Charpad V2.0 ;exported as raw binary.) *=$3400 Attribs !BINARY "BIN/ATTRIBS.BIN" ;Also place the title screen data at $3500 *=$3500 TitleMatrix !BINARY "BIN/TITLESCREEN.BIN" ;Now for the game code. *=$4000 ;A loop for every time the game is over, the player ;can restart a new game. Also this is the RUN address ;of the program. LDA #$00 JSR MusicInit ;Initialise music player ($1000) ;(Only once for this game.) GAMERESTART SEI ;Setup the charset, screen colour properties. LDA #$18 ;Enable screen multicolour STA $D016 LDA #$1A ;Charset at $2800 ... Display STA $D018 LDA #$0B ;Screen multicolour 1 COLOUR STA $D022 LDA #$01 ;Screen multicolour 2 COLOUR STA $D023 LDA #$00 ;Border + background colour STA $D020 STA $D021 LDA #$0B STA $D011 ;Screen off, while drawing ;Now draw the title screen, which is ;basically a repeat of the game screen, but with extra text. ;Also init firebutton process. LDX #$00 DRAWTITLE LDA TitleMatrix,x ;Fetch the first 256 chars of the matrix/screen STA $0400,x ;Position to the screen RAM. LDA TitleMatrix+$100,x ;Fetch the next 256 chars of the matrix/screen STA $0500,x ;Position to the screen RAM + $100 LDA TitleMatrix+$200,x ;... and so on !!! STA $0600,x LDA TitleMatrix+$2e8,x ;Important to use $06e8, instead of $0700 when STA $06e8,x ;filling the screen, as it will set wrong sprite INX ;frames. BNE DRAWTITLE ;Now fetch the screen colour data (ATTRIBS) ;and fill the screen ;with the set charset colours LDX #$00 COLOURTITLE LDY $0400,X ;Call screen RAM data LDA Attribs,Y ;Read the Attribs colour data STA $D800,X ;Store it to the colour RAM LDY $0500,X ;Call next 256 chars of screen RAM LDA Attribs,Y ;Read the colour data from Attribs STA $D900,X ;Store it to the next 256 colours LDY $0600,X ;... and so on! LDA Attribs,Y STA $DA00,X LDY $06E8,X LDA Attribs,Y STA $DAE8,X INX BNE COLOURTITLE LDA #$05 ;Status OK should be green. So STA $D800+19*40+30 ;set correct char position colour STA $D800+19*40+31 ;(Row 19 Columns 31 + 32) GREEN. LDA #$00 ;Flush firebutton in order. STA FIREBUTTON STA $D015 ;NO SPRITES JSR MASKSCORE ;Mask score and hi score to screen once. ;Initialise title and game interrupts LDX #IRQ LDA #$36 STX $0314 STY $0315 STA $D012 LDA #$7F STA $DC0D LDA #$1B STA $D011 LDA #$01 STA $D01A CLI ;Setup the main title LOOP (Nothing special really) TITLELOOP LDA $DC00 ;Read joystick port 2 LSR ;Up - skip LSR ;Down - skip LSR ;Left - skip LSR ;Right - skip LSR ;Fire BIT FIREBUTTON ;Check if the firebutton has been ROR FIREBUTTON ;released. BMI TITLELOOP ;Otherwise go back to the TITLELOOP BVC TITLELOOP ;which waits for fire to be pressed ;FIRE BUTTON HAS BEEN PRESSED. WE ARE READY TO ;PROCEED WITH THE GAME CODE: ;Setup the game sprites defaults, types and colour ;scheme (Simply by reading the colour table and ;then placing it on to the sprite frame/colour ;setting. LDA #$09 ;Sprite multicolour #1 for the game STA $D025 LDA #$01 ;Sprite multicolour #1 for the game STA $D026 LDX #$00 SETUPSPRITES LDA SPRITEFRAMETABLE,X ;Read sprite frame table STA $07F8,X ;Store to default HARDWARE sprite type LDA SPRITECOLOURTABLE,X;Read sprite colour table STA $D027,X ;Store to SPRITE COLOUR INX CPX #$08 ;8 SPRITES MAXIMUM BNE SETUPSPRITES ;Setup start position for sprites by reading ;the X/Y position table, and placing the sprite on ;to the screen. LDX #$00 POSITIONSPRITES LDA POSTABLE,X ;Read sprite position table STA $D000,X ;Store to sprite position INX CPX #$10 ;Or you could use #16 in decimal. There are 16 positions max BNE POSITIONSPRITES ;Enable all of the sprite on to the screen ;and also enable sprite multicolour mode ;Initialise game properties LDA #$01 ;Reset level to LEVEL 1 STA LEVEL LDA #$00 STA LEVELTIME1 ;Delay set until next level commences STA LEVELTIME2 ;where we speed up the game a bit. STA ANIMDELAY ;Zero zp variable ANIMDELAY STA ANIMPOINTER ;Zero zp variable ANIMPOINTER LDX #$00 ZEROSCORE LDA #$30 ;CHARACTER '0' STA SCORE,X INX CPX #$06 BNE ZEROSCORE LDA #$39 ;Set shield digit to $99 STA SHIELDDIGIT1 STA SHIELDDIGIT2 ;Now setup the game sprites and screen ... LDA #%11111111 ;Completely switch on for all sprites: STA $D015 ;Sprites enabled STA $D01C ;Sprites multicolour mode enabled ;Also, just in case, switch OFF the SPRITE EXPANSION ;and sprite behind background properties. LDA #%00000000 ;Completely switch off for all sprites: STA $D017 ;Expand Y STA $D01B ;Sprite behind background STA $D01D ;Expand X ;Draw the main game screen from CHARPAD exported SCREEN DATA ;and paste to SCREEN RAM ($0400-$07E8). LDX #$00 DRAWSCREEN LDA Matrix,X STA $0400,X LDA Matrix+$100,X STA $0500,X LDA Matrix+$200,X STA $0600,X LDA Matrix+$2E8,X STA $06E8,X INX BNE DRAWSCREEN ;Now draw the attributes in to the game LDX #$00 COLOURSCREEN LDY $0400,X LDA Attribs,Y STA $D800,X LDY $0500,X LDA Attribs,Y STA $D900,X LDY $0600,X LDA Attribs,Y STA $DA00,X LDY $06E8,X LDA Attribs,Y STA $DAE8,X INX BNE COLOURSCREEN LDA #$05 ;Status OK should be green STA $D800+19*40+30 STA $D800+19*40+31 ;A simple loop which calls subroutines to perform a game loop. ;Via subroutine This game loop will do as follows: ; ;- Read joystick port 2 - to control the player and store position of the ship ;- Move the moons downwards at a set speed ;- Scoring system ;- Level control ;- Hardware SPRITE/SPRITE collision with the player ;At the end of the source. RTS (Return To Subroutine) GAMELOOP JSR SYNCTIMER ;Synchronise game timer JSR ANIMATEPLAYER ;Animate the player ship JSR PLAYERCONTROL ;Player movement control JSR MOVEMOONS ;Control moon movement JSR SCORING ;Control game scoring JSR MASKSCORE ;Mask game scoring to screen JSR LEVELTIME ;Level time, before moving on to the next level JSR TESTCOLLISION ;Test collision with player to moon ;Position shield pointers to game screen. LDA SHIELDDIGIT2 STA $0400+16*40+31 ;Char position (calculated for shield) LDA SHIELDDIGIT1 STA $0400+16*40+30 ;Char position (calculated for shield) JMP GAMELOOP ;Main interrupts IRQ INC $D019 LDA $DC0D STA $DD0D LDA #$FA STA $D012 LDA #1 STA GAMESYNC JSR PalNTSCPlayer ;Play music player ($1003) JMP $EA7E PalNTSCPlayer LDA $02A6 ;Check for PAL/NTSC machine state check for music CMP #$01 BEQ MachinePal ;C64 version is PAL INC NTSCPOINTER LDA NTSCPOINTER CMP #$06 BEQ RESETNTSC MachinePal JSR MusicPlay RTS RESETNTSC LDA #0 STA NTSCPOINTER RTS ;Synchronise game timer with the IRQ raster interrupt. This ;handy little routine delays the game, so that the speed of ;the overall production, with the raster interrupt syncs well ;together. SYNCTIMER LDA #$00 STA GAMESYNC CMP GAMESYNC BEQ *-3 RTS ;Animate the player ship. This is done, simply by reading the ;animation delay. Once the animation delay ANIMDELAY has reached a delay ;limit, allow the animation pointer ANIMPOINTER to animate the the ;ShipAnimFrame table to the hardware sprite value. ANIMATEPLAYER LDA ANIMDELAY CMP #1 BEQ ANIMOK INC ANIMDELAY RTS ANIMOK LDA #$00 STA ANIMDELAY LDX ANIMPOINTER DOANIM LDA ShipAnimFrame,x STA $07F8 INX CPX #ShipAnimFrameEnd-ShipAnimFrame BEQ RESETANIMATION INC ANIMPOINTER RTS RESETANIMATION LDX #$00 STX ANIMPOINTER RTS ;Player control. The player is controlled by using ;a joystick in port 2. PLAYERCONTROL LDA $DC00 ;Read Joystick port 2 READUP LSR ;Read Up - For this game, we ignore control READDOWN LSR ;Read Down - For this game, we ignore control READLEFT LSR ;Read Left - We want to read this one BCS READRIGHT ;Skips reading left and checks next routine JMP MOVEPLAYERLEFT ;Allow player to move to the left READRIGHT LSR ;Read Right - We also want to read this one BCS NOCONTROL ;Right not read therefore no control required JMP MOVEPLAYERRIGHT ;Allow player to move to the right NOCONTROL RTS ;Subroutine finished ;Move player to the left of the screen, until it reaches the ;left-most position on screen. MOVEPLAYERLEFT LDA $D000 ;Hardware sprite0 position X SEC SBC #4 ;Subtract 2 from current position (NEGATIVE) CMP #$18 ;LEFTMOST POSITION BCS UPDATELEFT ;If position is HIGHER, update left position LEFT. LDA #$18 ;Force LEFTMOST POSITION to SPRITE 0X UPDATELEFT STA $D000 ;Sprite position updated RTS ;Move the player to the right of the screen, until it reaches the ;right most position on screen. MOVEPLAYERRIGHT LDA $D000 ;Hardware sprite0 position X CLC ADC #4 ;Add 2 to current position (POSITIVE) CMP #$E0 ;RIGHT MOST POSITION BCC UPDATERIGHT LDA #$E0 ;Force RIGHTMOST POSITION to SPRITE 0X UPDATERIGHT STA $D000 ;Sprite position updated RTS ;Move the moons, simply by recording the speed of a current level. Basically ;call a simple loop which will move the moons according to Y position only MOVEMOONS LDX #$00 MOVESPRS LDA $D003,X CLC ADC LEVEL ;Level, in which the moons move STA $D003,X INX ;Read each second sprite pos, in order INX ;to skip X position movement of the moons CPX #14 ;14 sprite positions max BNE MOVESPRS RTS ;Game scoring. Check whether any of the moons actually reached ;past the player's Y position $e0. If they have, increase the score ;a little. SCORING LDX #$00 ;New loop to check position of moon position CHECKMOON LDA $D003,X ;Read current Y Position CMP #$FC ;Has the moon reached past the position the player? BCC NOSCORE ;No additional points JMP DOSCORE ;Award points (And of course zero position asteroid) NOSCORE INX INX CPX #14 BNE CHECKMOON RTS ;Scoring process: DOSCORE INC SCORE+3 LDX #$03 SCORELOOP LDA SCORE,X CMP #$3A ;Is past number '9' char from charpad? BNE SCOREOK ;No, score is fine LDA #$30 ;Set next digit as 0 STA SCORE,X INC SCORE-1,X SCOREOK DEX BNE SCORELOOP RTS ;Mask the score on to the screen. For the screen position, refer to the ;X,Y position of the score digits that were placed in the charpad. My ;example was set as: X 30, Y 10. Which means, Horizontal position is 30, and ;Vertical position is 10. MASKSCORE LDX #$00 MASKLOOP LDA SCORE,X STA $0400+10*40+30,X LDA HISCORE,X STA $0400+22*40+30,X INX CPX #6 BNE MASKLOOP RTS ;Set up the game level time. The idea is simply that after a certain ;delay the game should then increase its level to the speed of #$08 ;if however the speed has expired to #$08 the game should be finished. LEVELTIME LDA LEVELTIME1 ;Read the first level time, until it has reached CMP #$FA ;250. BEQ FLIPLEVELTIME2 ;Flip to the next subroutine INC LEVELTIME1 ;Otherwise just increment one byte of the timer RTS ;and exit. FLIPLEVELTIME2 LDA #$00 ;Reset the first level timer, with 0 STA LEVELTIME1 LDA LEVELTIME2 ;Check if duration has been long enough CMP #4 ;Duration of playing time (AGAIN) BEQ NEXTLEVEL ;Move on to next level INC LEVELTIME2 ;Increment the next level timer. RTS ;and exit NEXTLEVEL LDA #$00 ;Reset the second level timer STA LEVELTIME2 LDA LEVEL ;Check current level position CMP #$08 ;One level after level 8 BEQ GAMECOMPLETE ;Game is finished. INC LEVEL ;Else move on to the next level INC $0400+13*40+31 ;Flip next digit on level counter RTS ;The player has successfully completed the game, so ;just add a simple subroutine that moves the player ;up the screen. Still move the moons down the screen. GAMECOMPLETE JSR SYNCTIMER JSR MOVEMOONS LDA $D001 SEC SBC #1 CMP #$02 BEQ SHOWGAMECOMPLETE STA $D001 JMP GAMECOMPLETE SHOWGAMECOMPLETE ;Remove all sprites from screen LDA #%00000000 STA $D015 ;Now display the WELL DONE text ;CONGRATULATIONS PILOT ;YOU HAVE SUCCESSFULLY ;MADE IT TO EARTH IN ;ONE PIECE. LDX #$00 MAKEWELLDONE LDA WELLDONETEXT,X STA $0400+9*40+2,X LDA WELLDONETEXT2,X STA $0400+11*40+2,X LDA WELLDONETEXT3,X STA $0400+13*40+2,X LDA WELLDONETEXT4,X STA $0400+15*40+2,X LDA WELLDONETEXT5,X STA $0400+18*40+2,X LDA #$07 STA $D800+9*40+2,X LDA #$01 STA $D800+11*40+2,X STA $D800+13*40+2,X STA $D800+15*40+2,X STA $D800+16*40+2,X INX CPX #WELLDONETEXTEND-WELLDONETEXT BNE MAKEWELLDONE JMP CHECKHISCORE ;Sprite/Sprite hardware collision. Should there be a ;likely event, where a moon collides into the player's ;ship. Trigger the shield counter to reduce. TESTCOLLISION LDA $D01E CMP #$01 ;ALL SPRITES COLLIDE INTO PLAYER BCS COLLISION LDA #$0D ;Player ship is back to its state STA $D027 RTS COLLISION INC $D027 DEC SHIELDDIGIT2 LDA SHIELDDIGIT2 CMP #$2F ;OOPS, value too low BNE SHIELDOK LDA #$39 ;Set char '9' to shield STA SHIELDDIGIT2 DEC SHIELDDIGIT1 LDA SHIELDDIGIT1 CMP #$32 ;If counter is at 2x alert danger BNE NODANGER JSR ALERTDANGER NODANGER LDA SHIELDDIGIT1 CMP #$2F ;Below char '0'? BNE SHIELDOK JMP DEAD SHIELDOK RTS ;Alert danger on to the player's shield status ALERTDANGER LDX #$00 DANGERLOOP LDA DANGERTEXT,X STA $0400+19*40+30,X LDA #$02 STA $D800+19*40+30,X INX CPX #DANGERTEXTEND-DANGERTEXT BNE DANGERLOOP RTS ;The player is dead. Display DESTROYED text then ;do the explosion animation. DEAD LDA #0 STA ANIMDELAY ;Delay animation STA ANIMPOINTER ;Same with pointer ;SETUP TEXT 'DESTROYED' LDX #$00 SHOWDTEXT LDA DESTROYEDTEXT,X STA $0400+19*40+30,X LDA #$02 STA $D800+19*40+30,X INX CPX #DESTROYEDTEXTEND-DESTROYEDTEXT ;TEXT LENGTH BNE SHOWDTEXT DEADLOOP JSR SYNCTIMER ;Synchronize timer once again JSR MOVEMOONS ;Still move the moons ;Explosion sequence - Controlled a similar way to the ;player ship animation, but ANIMPOINTER doesn't loop ;this time round, after reaching the last frame. LDA ANIMDELAY CMP #3 BEQ EXPDELAYOK INC ANIMDELAY JMP DEADLOOP EXPDELAYOK LDA #0 STA ANIMDELAY LDX ANIMPOINTER LDA ExplosionFrame,x STA $07f8 LDA #$07 STA $D027 INX CPX #ExplosionFrameEnd-ExplosionFrame BEQ GAMEOVER INC ANIMPOINTER JMP DEADLOOP ;Set the game over text on to the screen GAMEOVER LDA #%00000000 STA $D015 LDX #$00 PLACEGOTEXT LDA GAMEOVERTEXT,X STA $05C2,X LDA #$02 STA $D9C2,X INX CPX #GAMEOVERTEXTEND-GAMEOVERTEXT BNE PLACEGOTEXT ;This event will only occur during GAME OVER ;or WELL DONE. Check the player's score and ;high to score to see whether or not the ;player has achieved a high score or not. CHECKHISCORE LDA SCORE SEC LDA HISCORE+5 SBC SCORE+5 LDA HISCORE+4 SBC SCORE+4 LDA HISCORE+3 SBC SCORE+3 LDA HISCORE+2 SBC SCORE+2 LDA HISCORE+1 SBC SCORE+1 LDA HISCORE SBC SCORE BPL NOHISCORE ;No hi score found ;Make new high score for the player MAKEHISCORE LDX #$00 HISCLOOP LDA SCORE,X STA HISCORE,X INX CPX #$06 BNE HISCLOOP NOHISCORE JSR MASKSCORE LDA #$00 ;Reset fire button for player STA FIREBUTTON ;Infinite wait until fire is pressed ;to return to the title screen WAITFIREPROMPT LDA $DC00 ;Read port 2 LSR LSR LSR LSR LSR BIT FIREBUTTON ROR FIREBUTTON BMI WAITFIREPROMPT BVC WAITFIREPROMPT LDX #$31 LDY #$EA STX $0314 STY $0315 LDA #$00 STA $D019 STA $D01A LDA #$81 STA $DC0D STA $DD0D JMP GAMERESTART ;Completely restart the program GAMESYNC ;Pointer for synchonising the game timer !BYTE 0 NTSCPOINTER ;Pointer for timing NTSC machines !BYTE 0 ;Series of tables to set up sprite frame values (From $2000-$4000) and ;also the sprite colour table. SPRITEFRAMETABLE !BYTE $80,$86,$86,$86,$86,$86,$86,$86 ;8 frames (Ship, followed by moons) SPRITECOLOURTABLE !BYTE $0D,$0f,$0f,$0f,$0f,$0f,$0f,$0f ;Green ship, grey moons ;Series of tables to set up sprite position in game. POSTABLE !BYTE $78,$e0 ;Sprite0 X,Y (Player) !BYTE $20,$20 ;Sprite1 X,Y (Moon) !BYTE $40,$a0 ;Sprite2 X,Y (Moon) !BYTE $60,$40 ;Sprite3 X,Y (Moon) !BYTE $80,$00 ;Sprite4 X,Y (Moon) !BYTE $a0,$60 ;Sprite5 X,Y (Moon) !BYTE $c0,$c0 ;Sprite6 X,Y (Moon) !BYTE $e0,$80 ;Sprite7 X,Y (Moon) !BYTE ;Set the animation frame values for the player ship ShipAnimFrame !byte $80,$81,$82,$83,$84,$85 ShipAnimFrameEnd ;Set the explosion frame values for the player death ExplosionFrame !byte $87,$88,$89,$8a,$8b,$9c,$9d,$9e,$9f,$90 ExplosionFrameEnd ;Scoring and text !CT SCR ;Convert all text to usuable screen code ;Player score = 6 digits SCORE !TEXT "000000" SCOREEND ;Hi score = 6 digits HISCORE !TEXT "000000" HISCOREEND ;Status text - Player alive and shield is in a critical state DANGERTEXT !TEXT "danger" DANGERTEXTEND ;Status text - Player is destroyed - Game Over DESTROYEDTEXT !TEXT "destroyed" DESTROYEDTEXTEND ;Game over text GAMEOVERTEXT !TEXT "game over" GAMEOVERTEXTEND WELLDONETEXT !TEXT "congratulations pilot" WELLDONETEXTEND WELLDONETEXT2 !TEXT "you have successfully" WELLDONETEXT3 !TEXT "made it back to earth" WELLDONETEXT4 !TEXT "in one piece. " WELLDONETEXT5 !TEXT " - press fire - "