diff --git a/atlantico/actor.inc b/atlantico/actor.inc old mode 100644 new mode 100755 index c9139b0..5761a12 --- a/atlantico/actor.inc +++ b/atlantico/actor.inc @@ -1,3 +1,6 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Enumeration with the different types of actors of our game +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .enum ActorType NULL = 0 PLAYER = 1 @@ -6,8 +9,12 @@ MISSILE = 4 BOMB = 5 SPRITE0 = 6 + PARACHUTE = 7 .endenum +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Struct to hold the data for one actor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .struct Actor Type .byte XPos .byte diff --git a/atlantico/atlantico.asm b/atlantico/atlantico.asm index ca21cf9..61d90bd 100755 --- a/atlantico/atlantico.asm +++ b/atlantico/atlantico.asm @@ -8,9 +8,9 @@ ;; Variables declared in RAM zero-page ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .segment "ZEROPAGE" -Score: .res 4 ; 4 bytes for 1s, 10s, 100s, 1000s, For score +Score: .res 4 ; Score (1s, 10s, 100s, and 1000s digits in decimal) -Collision: .res 1 ; 0 or 1, if 0 no collision, if 1, collisison +Collision: .res 1 ; Flag if a collision happened or not Buttons: .res 1 ; Pressed buttons (A|B|Sel|Start|Up|Dwn|Lft|Rgt) PrevButtons: .res 1 ; Stores the previous buttons from the last frame @@ -21,8 +21,8 @@ YPos: .res 1 ; Player Y 16-bit position (8.8 fixed-point): hi+lo XVel: .res 1 ; Player X (signed) velocity (in pixels per 256 frames) YVel: .res 1 ; Player Y (signed) velocity (in pixels per 256 frames) -PrevSubmarine: .res 1 ; Stores the value in seconds that the last submarine was added -PrevAirplane: .res 1 ; Stores the value in seconds that the last airplane was added +PrevSubmarine: .res 1 ; Stores the seconds that the last submarine was added +PrevAirplane: .res 1 ; Stores the seconds that the last airplane was added Frame: .res 1 ; Counts frames (0 to 255 and repeats) IsDrawComplete: .res 1 ; Flag to indicate when VBlank is done drawing @@ -30,7 +30,7 @@ Clock60: .res 1 ; Counter that increments per second (60 frames) BgPtr: .res 2 ; Pointer to background address - 16bits (lo,hi) SprPtr: .res 2 ; Pointer to the sprite address - 16bits (lo,hi) -BufPtr: .res 2 ; Pointer to the Buffer Address - 16bite (lo,hi) +BufPtr: .res 2 ; Pointer to the buffer address - 16bits (lo,hi) XScroll: .res 1 ; Store the horizontal scroll position CurrNametable: .res 1 ; Store the current starting nametable (0 or 1) @@ -44,20 +44,17 @@ ParamYPos: .res 1 ; Used as parameter to subroutine ParamTileNum: .res 1 ; Used as parameter to subroutine ParamNumTiles: .res 1 ; Used as parameter to subroutine ParamAttribs: .res 1 ; Used as parameter to subroutine -ParamRectX1: .res 1 ; Used as parameter to subroutine -ParamRectY1: .res 1 ; Used as parameter to subroutine -ParamRectX2: .res 1 ; Used as parameter to subroutine -ParamRectY2: .res 1 ; Used as parameter to subroutine - +ParamRectX1: .res 1 ; Used as parameter to subroutine +ParamRectY1: .res 1 ; Used as parameter to subroutine +ParamRectX2: .res 1 ; Used as parameter to subroutine +ParamRectY2: .res 1 ; Used as parameter to subroutine PrevOAMCount: .res 1 ; Store the previous number of bytes that were sent to the OAM -Seed: .res 2 ; Initialize the 16-bit seed to any value except 0 - +Seed: .res 2 ; Initialize 16-bit seed to any value except 0 ActorsArray: .res MAX_ACTORS * .sizeof(Actor) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PRG-ROM code located at $8000 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -81,135 +78,129 @@ LoopButtons: rts .endproc - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Increment the score, simulating BCD mode +;; Returns a random 8-bit number inside A (0-255), clobbers Y (0). +;; Requires a 2-byte value on the zero page called "Seed". ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -.proc IncrementScore ; LittleEndian - Increment1stDigit: - lda Score+0 - clc - adc #1 - sta Score+0 - cmp #$A - bne DoneIncrementing - Increment10sDigit: - lda #0 - sta Score+0 - lda Score+1 - clc - adc #1 - sta Score+1 - cmp #$A - bne DoneIncrementing - Increment100sDigit: - lda #0 - sta Score+1 - lda Score+2 - clc - adc #1 - sta Score+2 - cmp #$A - bne DoneIncrementing - Increment1000sDigit: - lda #0 - sta Score+2 - lda Score+3 - clc - adc #1 - sta Score+3 - cmp #$A - bne DoneIncrementing - - DoneIncrementing: - rts +; This is a 16-bit Galois linear feedback shift register with polynomial $0039. +; The sequence of numbers it generates will repeat after 65535 calls. +; Execution time is an average of 125 cycles (excluding jsr and rts) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc GetRandomNumber + ldy #8 ; Loop counter (generates 8 bits) + lda Seed+0 +: asl ; Shift the register + rol Seed+1 + bcc :+ + eor #$39 ; Apply XOR feedback when a 1 bit is shifted out + : + dey + bne :-- + sta Seed+0 ; Saves the value in A into the Seed + cmp #0 ; Set flags + rts .endproc - +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Increment the Score value simulating BCD mode +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc IncrementScore + Increment1sDigit: + lda Score+0 ; Load the lowest digit of the number + clc + adc #1 ; Add 1 + sta Score+0 + cmp #$A ; Check for overflow (equals 10) + bne DoneIncrementing ; If no overflow happened, we're done! + Increment10sDigit: + lda #0 + sta Score+0 ; Reset one's digit from 9 to 0 + lda Score+1 ; Load second digit + clc + adc #1 ; Add 1 (the carry from the previous digit) + sta Score+1 + cmp #$A ; Check for overflow + bne DoneIncrementing ; If no overflow happened, we're done! + Increment100sDigit: + lda #0 + sta Score+1 ; Reset ten's digit from 9 to 0 + lda Score+2 ; Load the third digit + clc + adc #1 ; Add 1 (the carry from the previous digit) + sta Score+2 + cmp #$A ; Check for overflow + bne DoneIncrementing ; If no overflow happened, we're done! + Increment1000sDigit: + lda #0 + sta Score+2 ; Reset hundred's digit from 9 to 0 + lda Score+3 ; Load the last digit + clc + adc #1 ; Add 1 (the carry from the previous digit) + sta Score+3 ; Store the final addition in the last digit +DoneIncrementing: + rts +.endproc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Draw the score in ther nametable/background using buffering +;; Draw the score in the nametable/background using buffering ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Buffer format starting at Ram Address $7000 +;; Buffer format starting at memory address $7000: +;; ;; 03 20 52 00 00 02 01 20 78 00 00 ;; | \___/ \______/ | \___/ | | ;; | | | | | | | -;; | | | | | | Length=0 (End of buffering) -;; | | | | | byte to copy -;; | | | | PPU Address $2078 -;; | | | Length=1 -;; | | bytes to copy -;; | PPU address $2052 -;; Length = +;; | | | | | | Length=0 (end of buffering) +;; | | | | | byte to copy +;; | | | | PPU Address $2078 +;; | | | Length=1 +;; | | bytes to copy +;; | PPU Address $2052 +;; Length=3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .proc DrawScore - lda #$70 - sta BufPtr+1 - lda #$00 - sta BufPtr+0 + lda #$70 + sta BufPtr+1 + lda #$00 + sta BufPtr+0 - ldy #0 + ldy #0 + + lda #3 ; Length = 3 (how many bytes we will send) + sta (BufPtr),y + iny - lda #3 ; send 3 bytes - sta (BufPtr),y - iny + lda #$20 + sta (BufPtr),y ; Hi-Byte of the PPU address to be updated + iny + lda #$52 + sta (BufPtr),y ; Lo-Byte of the PPU address to be updated + iny - lda #$20 - sta (BufPtr),y ; hi byte - iny - lda #$52 - sta (BufPtr),y ; lo byte - iny + ;; Send the 3 digits of the score (from MSB to LSB) 100s, 10s, 1s - ;; Send the 3 digits of the score from MSB to LSB - ;; Offset by #$60 to point to numbers - lda Score+2 ; 100s - clc - adc #$60 - sta (BufPtr),y - iny + lda Score+2 ; 100s digit of the Score + clc + adc #$60 ; Offset by $60 to point to the correct tile + sta (BufPtr),y + iny - lda Score+1 ; 10s - clc - adc #$60 - sta (BufPtr),y - iny - - lda Score+0 ; 1s - clc - adc #$60 - sta (BufPtr),y - iny + lda Score+1 ; 10s digit of the Score + clc + adc #$60 ; Offset by $60 to point to the correct tile + sta (BufPtr),y + iny - lda #0 - sta (BufPtr),y ; Length=0, to signal the end of the buffer - iny + lda Score+0 ; 1s digit of the Score + clc + adc #$60 ; Offset by $60 to point to the correct tile + sta (BufPtr),y + iny - rts -.endproc + lda #0 + sta (BufPtr),y ; Length=0 to signal the end of the buffer + iny - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Returns a random 8-bit number inside A (0-255), clobbers Y (0). -;; Requires a 2-byte value on the Zero page called "Seed" -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; This is a 16-bit Galois Linerar Feedback Shift register with polynomial $0039 -;; The sequence of numbers it generates will repeat after 65535 calls. -;; Execution time is an average of 125 cycles (excluding jsr and rts) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -.proc GetRandomNumber - ldy #8 ; Loop counter (generates 8 bits) - lda Seed+0 -: asl ; Shift the register - rol Seed+1 - bcc :+ - eor #$39 ; Apply XOR feedback when a 1 bit is shifted out - : - dey - bne :-- - sta Seed+0 ; Saves the value in A into the Seed - cmp #0 ; Set flags - rts + rts .endproc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -398,73 +389,67 @@ EndRoutine: cmp #3 ; Only add a new submarine if the difference in time from the previous one is equal to 3 bne :+ lda #ActorType::SUBMARINE - sta ParamType ; Load parameter for the actor type + sta ParamType ; Load parameter for the actor type lda #223 - sta ParamXPos ; Load parameter for actor position X - jsr GetRandomNumber ; load random number into A - - lsr ; Divide by 8 (right shift thrice) - lsr - lsr - clc - adc #180 ; add 180 to lower it + sta ParamXPos ; Load parameter for actor position X + + jsr GetRandomNumber ; Fetch a new random number for the Y position of the submarine + lsr + lsr + lsr ; Divide the random number by 8 to get a healthy Y position range + clc + adc #180 ; And then add 180 to to it to move the submarine to the bottom part of the screen sta ParamYPos - jsr AddNewActor ; Call the subroutine to add the new missile actor + sta ParamYPos ; Load parameter for actor position Y + + jsr AddNewActor ; Call the subroutine to add the new submarine actor lda Clock60 - sta PrevSubmarine ; Save the current Clock60 time as the submarine last spawn time + sta PrevSubmarine ; Save the current Clock60 time as the submarine last spawn time : - SpawnAirplane: - lda Clock60 ; Submarines are added in intervals of 3 seconds + lda Clock60 ; Submarines are added in intervals of 2 seconds sec sbc PrevAirplane - cmp #2 ; Only add a new airplane if the difference in time from the previous one is equal to 2 + cmp #2 ; Only add a new airplane if the difference in time from the previous one is equal to 2 bne :+ lda #ActorType::AIRPLANE - sta ParamType ; Load parameter for the actor type + sta ParamType ; Load parameter for the actor type lda #235 - sta ParamXPos ; Load parameter for actor position X - jsr GetRandomNumber ; load random number into A + sta ParamXPos ; Load parameter for actor position X + + jsr GetRandomNumber + lsr + lsr ; Divide the random number (0-255) by 4 to adjust for the correct Y position range + clc + adc #35 ; And then add 35 to to it to place the airplane in the correct place in the sky - lsr ; Divide by 4 (right shift twice) - lsr - clc - adc #35 ; add 35 to lower it, so its not in the status bar + sta ParamYPos ; Load parameter for actor position Y - ;Adjust the random Y to be between top and bottom - - - sta ParamYPos - - jsr AddNewActor ; Call the subroutine to add the new missile actor + jsr AddNewActor ; Call the subroutine to add the new airplane actor lda Clock60 - sta PrevAirplane ; Save the current Clock60 time as the submarine last spawn time - : + sta PrevAirplane ; Save the current Clock60 time as the airplane last spawn time + : rts .endproc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Subroutine to check collisions -;; PARAMS: ParamXPos, ParamYPos (x and y of missile) +;; Subroutine to loop all enemy actors checking for collision with missile +;; Params = ParamXPos, ParamYPos (are the X and Y position of the missile) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .proc CheckEnemyCollision - txa - pha ; Push and save the X register in the stack - - ldx #0 - stx Collision ; Collision = 0 + txa + pha ; Push and save X register in the stack - EnemiesCollisionLoop: - cpx #MAX_ACTORS * .sizeof(Actor) - beq FinishCollisionCheck - lda ActorsArray+Actor::Type,x - cmp #ActorType::AIRPLANE - bne NextEnemy ; If not an airplane - skip to next enemy type + ldx #0 + stx Collision ; Collision = 0 - lda ActorsArray+Actor::Type,x ; Load the type of the actor we are looping + EnemiesCollisionLoop: + cpx #MAX_ACTORS * .sizeof(Actor) ; We loop all entities, looking for enemies (airplanes) + beq FinishCollisionCheck + lda ActorsArray+Actor::Type,x ; Load the type of the actor we are looping cmp #ActorType::AIRPLANE bne NextEnemy ; If it's NOT Airplane, bypass this enemy and move check the next one @@ -484,69 +469,68 @@ EndRoutine: adc #8 ; Get the bottom of the airplane bounding box by adding 8 sta ParamRectY2 ; Bouding Box Y2 - jsr IsPointInsideBoundingBox + jsr IsPointInsideBoundingBox ; Proceed to test if point is inside bounding box - lda Collision - beq NextEnemy - lda #ActorType::NULL ; Set actor to null - TODO: maybe make an explosion - sta ActorsArray+Actor::Type,x - jmp FinishCollisionCheck + lda Collision + beq NextEnemy ; If no collision, don't do anything + lda #ActorType::NULL ; Else, destroy airplane + sta ActorsArray+Actor::Type,x ; If collision happened, destroy airplane entity + jmp FinishCollisionCheck ; Also, if collision happened we stop looping other enemies and leave the subroutine + + NextEnemy: + txa + clc + adc #.sizeof(Actor) ; X += sizeof(Actor) + tax + jmp EnemiesCollisionLoop ; Loop to check the next actor to see if it's an enemy (airplane) - NextEnemy: - txa - clc - adc #.sizeof(Actor) - tax - jmp EnemiesCollisionLoop - FinishCollisionCheck: - pla - tax + pla + tax ; Pull and restore the old value of X - rts + rts .endproc - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Subroutine to check if a point is inside a bounding box. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Params: -;; (ParamXPos, ParamYPos) - coords to be tested -;; (ParamRectX1,ParamRectY1,ParamRectX2,ParamRectY2) Are rectangle coords) -;; Output: -;; Collision flag is either 1 or 0 +;; Params: +;; (ParamXPos,ParamYPos) are the coords of the point to be tested +;; (ParamRectX1,ParamRectY1,ParamRectX2,ParamRectY2) are rectangle coords +;; Output: +;; Collision flag is either 1 or 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .proc IsPointInsideBoundingBox - lda ParamXPos - cmp ParamRectX1 - bcc PointIsOutside + lda ParamXPos ; Fetch the point X coordinate + cmp ParamRectX1 ; Compare it with the enemy rectangle X + bcc PointIsOutside ; If it's less, we stop checking because there is no collision - lda ParamYPos - cmp ParamRectY1 - bcc PointIsOutside + lda ParamYPos ; Fetch the point Y coordinate + cmp ParamRectY1 ; Compare it with the enemy bounding box Y value + bcc PointIsOutside ; If it's less, we stop checking because there is no collision - lda ParamXPos - cmp ParamRectX2 - bcs PointIsOutside + lda ParamXPos ; Fetch the point X coorrinate + cmp ParamRectX2 ; Compare it with the enemy bounding box right + bcs PointIsOutside ; If it's greater than, we stop checking because there is no collision - lda ParamYPos - cmp ParamRectY2 - bcs PointIsOutside + lda ParamYPos ; Fetch the point X coorrinate + cmp ParamRectY2 ; Compare it with the enemy bounding box right + bcs PointIsOutside ; If it's greater than, we stop checking because there is no collision - PointIsInside: - lda #1 - sta Collision - jmp EndCollisionCheck + PointIsInside: + lda #1 ; If we reach here, the point is inside the bounding box! + sta Collision ; Collision detected! + jmp EndCollisionCheck - PointIsOutside: - lda #0 - sta Collision - - EndCollisionCheck: - rts + PointIsOutside: + lda #0 ; If we branch here, the point was outside the bounding box + sta Collision ; No collision detected! + EndCollisionCheck: + rts .endproc + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Subroutine to loop all actors and update them (position, velocity, etc.) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -566,32 +550,31 @@ FinishCollisionCheck: sta ActorsArray+Actor::Type,x ; Remove the missile from the array SkipMissile: - CheckCollision: - lda ActorsArray+Actor::XPos,x - clc - adc #3 - sta ParamXPos ; Missile position to be checked X += 3 + CheckCollision: + lda ActorsArray+Actor::XPos,x + clc + adc #3 + sta ParamXPos ; Missile position to be checked X += 3 (plus 3 pixels from the left) - lda ActorsArray+Actor::YPos,x - clc - adc #1 - sta ParamYPos ; Missile position to be checked Y += 1 + lda ActorsArray+Actor::YPos,x + clc + adc #1 + sta ParamYPos ; Missile position to be checked Y += 1 (plus 1 pixels from the top) - jsr CheckEnemyCollision ; Perform collision check between the missile and other enemy actor + jsr CheckEnemyCollision ; Perform collision check between the missile and other enemy actor - lda Collision - beq NoCollisionFound - lda #ActorType::NULL ; Delete missile Actor - sta ActorsArray+Actor::Type,x + lda Collision ; If collision happened, delete the missile + beq NoCollisionFound + lda #ActorType::NULL ; Delete missile actor + sta ActorsArray+Actor::Type,x - jsr IncrementScore - jsr DrawScore - NoCollisionFound: + jsr IncrementScore ; Increment the Score variable using BCD-ish mode + jsr DrawScore ; Send the score bytes to the background buffer at $7000 + NoCollisionFound: jmp NextActor : - cmp #ActorType::SUBMARINE bne :+ lda ActorsArray+Actor::XPos,x @@ -604,20 +587,18 @@ FinishCollisionCheck: SkipSubmarine: jmp NextActor : - cmp #ActorType::AIRPLANE bne :+ lda ActorsArray+Actor::XPos,x sec - sbc #1 ; Decrement Y position of submarine by 1 + sbc #1 ; Decrement Y position of airplane by 1 sta ActorsArray+Actor::XPos,x bcs SkipAirplane lda #ActorType::NULL - sta ActorsArray+Actor::Type,x ; Remove the submarine from the array + sta ActorsArray+Actor::Type,x ; Remove the airplane from the array SkipAirplane: jmp NextActor : - NextActor: txa clc @@ -672,7 +653,6 @@ FinishCollisionCheck: jsr DrawSprite ; Call routine to draw 4 PLAYER tiles to the OAM jmp NextActor : - cmp #ActorType::SUBMARINE bne :+ lda ActorsArray+Actor::XPos,x @@ -685,10 +665,9 @@ FinishCollisionCheck: sta ParamAttribs lda #4 sta ParamNumTiles - jsr DrawSprite ; Call routine to draw 4 SUBMARINE tile to the OAM + jsr DrawSprite ; Call routine to draw 4 SUBMARINE tiles to the OAM jmp NextActor : - cmp #ActorType::AIRPLANE bne :+ lda ActorsArray+Actor::XPos,x @@ -701,10 +680,9 @@ FinishCollisionCheck: sta ParamAttribs lda #3 sta ParamNumTiles - jsr DrawSprite ; Call routine to draw 4 SUBMARINE tile to the OAM + jsr DrawSprite ; Call routine to draw 3 AIRPLANE tiles to the OAM jmp NextActor : - cmp #ActorType::MISSILE bne :+ lda ActorsArray+Actor::XPos,x @@ -720,7 +698,6 @@ FinishCollisionCheck: jsr DrawSprite ; Call routine to draw 1 MISSILE tile to the OAM jmp NextActor : - NextActor: txa clc @@ -797,6 +774,14 @@ FinishCollisionCheck: rts .endproc +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Subroutine to switch CHR banks +;; Params = A has the bank number +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc SwitchCHRBank + sta $8000 ; $8000 is the bank switch register of mapper 3 + rts +.endproc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Reset handler (called when the NES resets or powers on) @@ -816,9 +801,9 @@ InitVariables: lda #165 sta YPos - lda #$10 - sta Seed+1 - sta Seed+0 ; Initialize the seed with any value not zero + lda #$10 + sta Seed+1 + sta Seed+0 ; Initialize the Seed with any value different than zero Main: jsr LoadPalette ; Call LoadPalette subroutine to load 32 colors into our palette @@ -932,6 +917,14 @@ GameLoop: jsr AddNewActor ; Call the subroutine to add a new missile actor : + CheckSelectButton: + lda Buttons + and #BUTTON_SELECT + beq :+ + lda #1 + jsr SwitchCHRBank + : + jsr SpawnActors jsr UpdateActors jsr RenderActors @@ -957,40 +950,36 @@ OAMStartDMACopy: ; DMA copy of OAM data from RAM to PPU lda #$02 ; Every frame, we copy spite data starting at $02** sta PPU_OAM_DMA ; The OAM-DMA copy starts when we write to $4014 -;; TODO -;; Loop all bytes of the background buffer at $7000 until we find length=0 -;; Sending the tiles to the PPU nametable in VRAM -BackgroundCopy: - lda #$70 - sta BufPtr+1 - lda #$00 - sta BufPtr +BackgroundCopy: ; Here is where we copy/draw the background buffer from $7000 to the PPU + lda #$70 + sta BufPtr+1 + lda #$00 + sta BufPtr+0 ; Set BufPtr pointer to start at address $7000 - ldy #$00 - BufferLoop: - lda (BufPtr),y ; Fetch the length - beq EndBackgroundCopy ; if len is 0, stop reading from bg buffer + ldy #$00 + BufferLoop: + lda (BufPtr),y ; Fetch the Length + beq EndBackgroundCopy ; If Length is 0, stop reading from background buffer - tax ; x = length + tax ; X = Length - iny - lda (BufPtr),y ; hi byte - sta PPU_ADDR - iny - lda (BufPtr),y ; low byte - sta PPU_ADDR - iny - DataLoop: - lda (BufPtr),y - sta PPU_DATA - iny - dex ; X-- - bne DataLoop + iny + lda (BufPtr),y ; Fetch hi-byte of PPU address to be updated + sta PPU_ADDR + iny + lda (BufPtr),y ; Fetch lo-byte of PPU address to be updated + sta PPU_ADDR + iny + DataLoop: + lda (BufPtr),y + sta PPU_DATA + iny + dex ; X-- + bne DataLoop - jmp BufferLoop + jmp BufferLoop ; Loop back until we finish the buffer (find an entry with Length=0) EndBackgroundCopy: - NewColumnCheck: lda XScroll and #%00000111 ; Check if the scroll a multiple of 8 @@ -1258,8 +1247,11 @@ AttributeData: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Here we add the CHR-ROM data, included from an external .CHR file ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -.segment "CHARS" -.incbin "atlantico.chr" +.segment "CHARS1" +.incbin "atlantico.chr" ; This is the 1st bank of CHR-ROM tiles + +.segment "CHARS2" +.incbin "titlescreen.chr" ; This is the 2nd bank of CHR-ROM tiles ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Vectors with the addresses of the handlers that we always add at $FFFA diff --git a/atlantico/atlantico.nes b/atlantico/atlantico.nes index a4f6eb7..cbf190d 100644 Binary files a/atlantico/atlantico.nes and b/atlantico/atlantico.nes differ diff --git a/atlantico/atlantico.o b/atlantico/atlantico.o index 7ecf7aa..69d69f7 100644 Binary files a/atlantico/atlantico.o and b/atlantico/atlantico.o differ diff --git a/atlantico/consts.inc b/atlantico/consts.inc index 867d4b7..44a9fe7 100755 --- a/atlantico/consts.inc +++ b/atlantico/consts.inc @@ -26,4 +26,7 @@ BUTTON_DOWN = $04 ; 00000100 BUTTON_LEFT = $02 ; 00000010 BUTTON_RIGHT = $01 ; 00000001 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Maximum number of actors that we can have +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MAX_ACTORS = 10 diff --git a/atlantico/header.inc b/atlantico/header.inc index 7dc8f99..632cd96 100755 --- a/atlantico/header.inc +++ b/atlantico/header.inc @@ -1,11 +1,11 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; The iNES header (contains a total of 16 bytes with the flags at $7FF0) +;; The iNES header (contains a total of 16 bytes with the flags at $7F00) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .segment "HEADER" .byte $4E,$45,$53,$1A ; 4 bytes with the characters 'N','E','S','\n' .byte $02 ; How many 16KB of PRG-ROM we'll use (=32KB) -.byte $01 ; How many 8KB of CHR-ROM we'll use (=8KB) -.byte %00000001 ; Vertical mirroring, no battery, mapper 0 +.byte $02 ; How many 8KB of CHR-ROM we'll use (=16KB) +.byte %00110001 ; Mapper 003, Vertical mirroring, no battery .byte %00000000 ; mapper 0, playchoice, NES 2.0 .byte $00 ; No PRG-RAM .byte $00 ; NTSC TV format diff --git a/atlantico/nes.cfg b/atlantico/nes.cfg index 7daa74b..7da3ed3 100755 --- a/atlantico/nes.cfg +++ b/atlantico/nes.cfg @@ -1,16 +1,18 @@ MEMORY { - ZP: start = $0000, size = $0100, type = rw, file = ""; - OAM: start = $0200, size = $0100, type = rw, file = ""; - RAM: start = $0300, size = $0500, type = rw, file = ""; - HDR: start = $7FF0, size = $0010, type = ro, file = %O, fill = yes, fillval = $00; - PRG: start = $8000, size = $8000, type = ro, file = %O, fill = yes, fillval = $00; - CHR: start = $0000, size = $2000, type = ro, file = %O, fill = yes, fillval = $00; + ZP: start = $0000, size = $0100, type = rw, file = ""; + OAM: start = $0200, size = $0100, type = rw, file = ""; + RAM: start = $0300, size = $0500, type = rw, file = ""; + HDR: start = $7FF0, size = $0010, type = ro, file = %O, fill = yes, fillval = $00; + PRG: start = $8000, size = $8000, type = ro, file = %O, fill = yes, fillval = $00; + CHR1: start = $0000, size = $2000, type = ro, file = %O, fill = yes, fillval = $00; + CHR2: start = $0000, size = $2000, type = ro, file = %O, fill = yes, fillval = $00; } SEGMENTS { - ZEROPAGE: load = ZP, type = zp; - HEADER: load = HDR, type = ro; - CODE: load = PRG, type = ro, start = $8000; - CHARS: load = CHR, type = ro, optional = yes; - VECTORS: load = PRG, type = ro, start = $FFFA; -} \ No newline at end of file + ZEROPAGE: load = ZP, type = zp; + HEADER: load = HDR, type = ro; + CODE: load = PRG, type = ro, start = $8000; + CHARS1: load = CHR1, type = ro, optional = yes; + CHARS2: load = CHR2, type = ro, optional = yes; + VECTORS: load = PRG, type = ro, start = $FFFA; +} diff --git a/atlantico/titlescreen.chr b/atlantico/titlescreen.chr new file mode 100755 index 0000000..7da32cf Binary files /dev/null and b/atlantico/titlescreen.chr differ diff --git a/atlantico/utils.inc b/atlantico/utils.inc index b21f1f6..2320f11 100755 --- a/atlantico/utils.inc +++ b/atlantico/utils.inc @@ -17,21 +17,26 @@ sta PPU_DATA ; Send value to PPU register at $2007 .endmacro - +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Macro to push and preserv registers A, X, Y, and status flags on the Stack. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .macro PUSH_REGS - pha ; Push A to the stack - txa ; transfer x to acc - pha ; Push X to the stack - tya ; transfer y to the acc - pha ; Push Y to the stack - php ; Push process status flags to the stack + pha ; Push A to the stack + txa + pha ; Push X to the stack + tya + pha ; Push Y to the stack + php ; Push Processor Status flags to the stack .endmacro +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Macro to pull and restore registers A,X,Y, and status flags from the Stack. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .macro PULL_REGS - plp ; restore the status flags from the stack - pla ; restore the old value of Y from the stack into A - tay ; transfer - pla ; restore the old value of X from the stack into A - tax ; transfer - pla ; Pull a from the stack + plp ; Restore the the status flags from the stack + pla ; Restore the old value of X from the stack + tay + pla ; Restore the old value of X from the stack + tax + pla ; Pull A from the stack .endmacro