From 03454f01cba356fe90bebacebca859432e50d045 Mon Sep 17 00:00:00 2001 From: Tyrel Souza Date: Thu, 8 Dec 2022 23:34:27 -0500 Subject: [PATCH] spawning --- atlantico/Makefile | 1 + atlantico/atlantico.asm | 1031 ++++++++++++++++++++------------------- atlantico/atlantico.nes | Bin 40976 -> 40976 bytes atlantico/atlantico.o | Bin 51673 -> 52416 bytes 4 files changed, 523 insertions(+), 509 deletions(-) diff --git a/atlantico/Makefile b/atlantico/Makefile index d8efe12..12f762d 100755 --- a/atlantico/Makefile +++ b/atlantico/Makefile @@ -15,4 +15,5 @@ clean: # Rule to run the final cartridge .nes file in the FCEUX emulator ############################################################################### run: + #on-workspace 4 "fceux atlantico.nes && i3-msg '[id=$(xdotool getactivewindow)] floating enable'" on-workspace 4 "fceux atlantico.nes" diff --git a/atlantico/atlantico.asm b/atlantico/atlantico.asm index 22a1246..610ef11 100755 --- a/atlantico/atlantico.asm +++ b/atlantico/atlantico.asm @@ -4,41 +4,44 @@ .include "reset.inc" .include "utils.inc" +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Variables declared in RAM zero-page +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .segment "ZEROPAGE" -Buttons: .res 1 ; Pressed buttons (A|B|Select|Start|Up|Dwn|Lft|Rgt) -PrevButtons: .res 1 ; Pressed buttons (A|B|Select|Start|Up|Dwn|Lft|Rgt) PREVIOUSLY +Buttons: .res 1 ; Pressed buttons (A|B|Sel|Start|Up|Dwn|Lft|Rgt) +PrevButtons: .res 1 ; Stores the previous buttons from the last frame -XPos: .res 1 ; Player X 16-bit position (8.8 fixed-point): hi+lo/256px -YPos: .res 1 ; Player Y 16-bit position (8.8 fixed-point): hi+lo/256px +XPos: .res 1 ; Player X 16-bit position (8.8 fixed-point): hi+lo/256px +YPos: .res 1 ; Player Y 16-bit position (8.8 fixed-point): hi+lo/256px -XVel: .res 1 ; Player X (signed) velocity (in pixels per 256 frames) -YVel: .res 1 ; Player Y (signed) velocity (in pixels per 256 frames) +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 ; how long ago we added previous submarine in seconds +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 -Frame: .res 1 ; Counts frames (0 to 255 and repeats) -IsDrawComplete: .res 1 ; Flag to indicate when vblank is done drawing -Clock60: .res 1 ; Counter that increments per second (60 frames) +Frame: .res 1 ; Counts frames (0 to 255 and repeats) +IsDrawComplete: .res 1 ; Flag to indicate when VBlank is done drawing +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) +BgPtr: .res 2 ; Pointer to background address - 16bits (lo,hi) +SprPtr: .res 2 ; Pointer to the sprite address - 16bits (lo,hi) -XScroll: .res 1 ; Store the horizontal scroll position -CurrNametable: .res 1 ; Store the current starting nametable (0 or 1) -Column: .res 1 ; Stores the column (of tiles) we are in the level -NewColAddr: .res 2 ; The destination address of the new column in PPU -SourceAddr: .res 2 ; The source address in ROM of the new column tiles +XScroll: .res 1 ; Store the horizontal scroll position +CurrNametable: .res 1 ; Store the current starting nametable (0 or 1) +Column: .res 1 ; Stores the column (of tiles) we are in the level +NewColAddr: .res 2 ; The destination address of the new column in PPU +SourceAddr: .res 2 ; The source address in ROM of the new column tiles -ParamType: .res 1 ; Used as param to subrouting -ParamXPos: .res 1 ; Used as param to subrouting -ParamYPos: .res 1 ; Used as param to subrouting -ParamTileNum: .res 1 ; Used as param to subrouting -ParamNumTiles: .res 1 ; Used as param to subrouting -ParamAttribs: .res 1 ; Used as param to subrouting +ParamType: .res 1 ; Used as parameter to subroutine +ParamXPos: .res 1 ; Used as parameter to subroutine +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 -PrevOAMCount: .res 1 ; Store previous number of bytes sent to the OAM +PrevOAMCount: .res 1 ; Store the previous number of bytes that were sent to the OAM -; Store enough space for an array of actors ActorsArray: .res MAX_ACTORS * .sizeof(Actor) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -46,26 +49,24 @@ ActorsArray: .res MAX_ACTORS * .sizeof(Actor) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .segment "CODE" -;ReadControllers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Routine to read controller state and store it inside "Buttons" in RAM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .proc ReadControllers - lda #1 ; A = 1 - sta Buttons ; Buttons = 1 - sta JOYPAD1 ; Set Latch=1 to begin 'Input'/collection mode - lsr ; A = 0 - sta JOYPAD1 ; Set Latch=0 to begin 'Output' mode + lda #1 ; A = 1 + sta Buttons ; Buttons = 1 + sta JOYPAD1 ; Set Latch=1 to begin 'Input'/collection mode + lsr ; A = 0 + sta JOYPAD1 ; Set Latch=0 to begin 'Output' mode LoopButtons: - lda JOYPAD1 ; This reads a bit from the controller data line and inverts its value, - ; And also sends a signal to the Clock line to shift the bits - lsr ; We shift-right to place that 1-bit we just read into the Carry flag - rol Buttons ; Rotate bits left, placing the Carry value into the 1st bit of 'Buttons' in RAM - bcc LoopButtons ; Loop until Carry is set (from that initial 1 we loaded inside Buttons) + lda JOYPAD1 ; This reads a bit from the controller data line and inverts its value, + ; And also sends a signal to the Clock line to shift the bits + lsr ; We shift-right to place that 1-bit we just read into the Carry flag + rol Buttons ; Rotate bits left, placing the Carry value into the 1st bit of 'Buttons' in RAM + bcc LoopButtons ; Loop until Carry is set (from that initial 1 we loaded inside Buttons) rts .endproc -;LoadPalette ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Subroutine to load all 32 color palette values from ROM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -80,406 +81,437 @@ LoopButtons: rts ; Return from subroutine .endproc - -;DrawNewColumn ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Routine to draw a new column of tiles off-screen as we scroll horizontally +;; Routine to draw a new column of tiles off-screen every 8 pixels ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .proc DrawNewColumn - ; [ Destination ] - lda XScroll ;Set newColAddr low and hi byte + lda XScroll ; We'll set the NewColAddr lo-byte and hi-byte lsr lsr - lsr ; shift left three times to divide Xscroll by 3 - sta NewColAddr ; set the lo byte + lsr ; Shift right 3 times to divide XScroll by 8 + sta NewColAddr ; Set the lo-byte of the column address - lda CurrNametable ; hi byte comes from nametable - eor #1 ; invert low bit (0 or 1) + lda CurrNametable ; The hi-byte comes from the nametable + eor #1 ; Invert the low bit (0 or 1) asl - asl ; multiply by 4 (A is $00 or $04) + asl ; Multiply by 4 (A is $00 or $04) clc - adc #$20 ; add $20 (A is $20 or $24) for nametable 0 or 1 - sta NewColAddr+1 ; set the hi0-byte of the column address ($20xx or $24xx) - ;;;;;;;;;;; + adc #$20 ; Add $20 (A is $20 or $24) for nametabe 0 or 1 + sta NewColAddr+1 ; Set the hi-byte of the column address ($20xx or $24xx) - ; [SourceAddr low and hi byte] - lda Column ; Multiply by 32 to compute the offset - asl ; *2 - asl ; *4 - asl ; *8 - asl ; *16 - asl ; *32 - sta SourceAddr ; store lo byte of column source address + lda Column ; Multiply (col * 32) to compute the data offset + asl + asl + asl + asl + asl + sta SourceAddr ; Store lo-byte (--XX) of column source address lda Column - lsr ; /2 - lsr ; /4 - lsr ; Divide current column by 8 using 3 shift rights - sta SourceAddr+1 ; store hi byte of column source address + lsr + lsr + lsr ; Divide current Column by 8 (using 3 shift rights) + sta SourceAddr+1 ; Store hi-byte (XX--) of column source addres - ; here we'll add the offset the column source address with the address where the backggroun - lda SourceAddr ; lo byte of the column data + offset = address to load column data from + ; Here we'll add the offset the column source address with the address of where the BackgroundData + lda SourceAddr ; Lo-byte of the column data start + offset = address to load column data from clc - adc #BackgroundData ; add the hi byte - sta SourceAddr+1 ; add the result of the offset back to the source address hi byte + lda SourceAddr+1 ; Hi-byte of the column source address + adc #>BackgroundData ; Add the hi-byte + sta SourceAddr+1 ; Add the result of the offset back to the source address hi-byte - ;;; Loop all tiles from column, sending them to destination DrawColumn: - lda #%00000100 ; - sta PPU_CTRL ; Tell the PPU that the increments will be +32 mode + lda #%00000100 + sta PPU_CTRL ; Tell the PPU that the increments will be +32 mode - lda PPU_STATUS ; reset latch - lda NewColAddr+1 ; - sta PPU_ADDR ; set hi-byte of start address - lda NewColAddr ; - sta PPU_ADDR ; set lo-byte of start address - - ldx #30 ; Looping 30 times - ldy #0 ; - DrawColumnLoop: ; - lda (SourceAddr), y ; Copy from the address of the column source + y offset - sta PPU_DATA ; - iny ; Y++ - dex ; X-- - bne DrawColumnLoop ; loop 30 times + lda PPU_STATUS ; Hit PPU_STATUS to reset hi/lo address latch + lda NewColAddr+1 + sta PPU_ADDR ; Set the hi-byte of the new column start address + lda NewColAddr + sta PPU_ADDR ; Set the lo-byte of the new column start address + ldx #30 ; We'll loop 30 times (=30 rows) + ldy #0 + DrawColumnLoop: + lda (SourceAddr),y ; Copy from the address of the column source + y offset + sta PPU_DATA + iny ; Y++ + dex ; X-- + bne DrawColumnLoop ; Loop 30 times to draw all 30 rows of this column rts .endproc -;DrawNewAttribs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Routine to draw a attributes off-screen eveery 32 pixels +;; Routine to draw attributes off-screen every 32 pixels ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .proc DrawNewAttribs - lda CurrNametable - eor #1 ; invert low bit - asl ; Multiply by 2 (00 or 02) - asl ; multiply by 2 again (00, or 04) - clc - adc #$23 ; Add high byte of attribute 23-- or 27-- - sta NewColAddr+1 + lda CurrNametable + eor #1 ; Invert low bit (0 or 1) + asl ; Multiuply by 2, ($00 or $02) + asl ; Multiply by 2 again ($00 or $04) + clc + adc #$23 ; Add high byte of attribute base address ($23-- or $27--) + sta NewColAddr+1 ; The hi-byte now has address = $23 or $27 for nametable 0 or 1 - lda XScroll - lsr - lsr - lsr - lsr - lsr - clc - adc #$C0 - sta NewColAddr ; Low Byte + lda XScroll + lsr + lsr + lsr + lsr + lsr ; Divide by 32 (shift right 5 times) + clc + adc #$C0 + sta NewColAddr ; The lo-byte contains (attribute base + XScroll/32) - ;; Source Address - lda Column ; Column/4 *8 since each row is 8bytes - and #%11111100 ; mask the lowest two bits to get the closest lowest multiple of 4 - asl ; one shift left - multiply by 2 - sta SourceAddr ; store low byte of offset in rom + lda Column ; (Column/4) * 8, since each row of attribute data in ROM is 8 bytes + and #%11111100 ; Mask the lowest two bits to get the closest lowest multiple of 4 + asl ; One shift left equivelant to a multiplication by 2 + sta SourceAddr ; Stores the lo-byte of the source attribute address offset (in ROM) - lda Column ; compute high - lsr ;/2 - lsr - lsr - lsr - lsr - lsr - lsr ; /128 - sta SourceAddr+1 ; store high byte + lda Column ; Proceed to compute the hi-byte of the source address offset in ROM + lsr ; /2 + lsr ; /4 + lsr ; /8 + lsr ; /16 + lsr ; /32 + lsr ; /64 + lsr ; /128, shift right 7 times to divide by 128 + sta SourceAddr+1 ; Stores the hi-byte of the Source address offset - lda SourceAddr - clc - adc #AttributeData ; hi byte of where attrib is in rom - sta SourceAddr+1 + lda SourceAddr+1 + adc #>AttributeData ; Add the hi-byte of the base address where AttributeData is in ROM + sta SourceAddr+1 ; Stores the result of the add back into the hi-byte of the SourceAddr - DrawAttribute: - bit PPU_STATUS - ldy #0 ; reset latch and back to 0 - DrawAttribLoop: - lda NewColAddr+1 - sta PPU_ADDR ; high byte of ppu dest - lda NewColAddr - sta PPU_ADDR ; low byte - lda (SourceAddr),y ; fetch attribute data - sta PPU_DATA ; store new data into ppu memory - iny - cpy #8 - beq :+ ; loop 8 times + DrawAttribute: + bit PPU_STATUS ; Hit PPU_STATUS to reset the high/low address latch + ldy #0 ; Y = 0 + DrawAttribLoop: + lda NewColAddr+1 + sta PPU_ADDR ; Write the hi-byte of attribute PPU destination address lda NewColAddr - clc - adc #8 - sta NewColAddr ; next addr will be at NewColAddr +8 - jmp DrawAttribLoop - : - rts + sta PPU_ADDR ; Write the lo-byte of attribute PPU destination address + lda (SourceAddr),y ; Fetch attribute byte from ROM + sta PPU_DATA ; Stores new attribute data into the PPU memory + iny ; Y++ + cpy #8 + beq :+ ; Loop 8 times (to copy 8 attribute bytes) + lda NewColAddr + clc + adc #8 + sta NewColAddr ; Next attribute will be at (NewColAddr + 8) + jmp DrawAttribLoop + : + rts .endproc -;AddNewActor ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Subroutine to add new actor to the array in the first empty slot found -;; Params: ParamType, ParamXPos, ParamYPos, ParamYVel +;; Params = ParamType, ParamXPos, ParamYPos ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .proc AddNewActor - ldx #0 - ArrayLoop: - cpx #MAX_ACTORS * .sizeof(Actor); too many actors, skip - beq EndRoutine - lda ActorsArray+Actor::Type,x - cmp #ActorType::NULL - beq AddNewActorToArray ; If found empty slot, add to position - NextActor: + ldx #0 ; X = 0 + ArrayLoop: + cpx #MAX_ACTORS * .sizeof(Actor) ; Reached maximum number of actors allowed in the array? + beq EndRoutine ; Then we skip and don't add a new actor + lda ActorsArray+Actor::Type,x + cmp #ActorType::NULL ; If the actor type of this array position is NULL + beq AddNewActorToArray ; Then: we found an empty slot, proceed to add actor to position [x] + NextActor: + txa + clc + adc #.sizeof(Actor) ; Otherwise, we offset to the check the next actor in the array + tax ; X += sizeof(Actor) + jmp ArrayLoop + + AddNewActorToArray: ; Here we add a new actor at index [x] of the array + lda ParamType ; Fetch parameter "actor type" from RAM + sta ActorsArray+Actor::Type,x + lda ParamXPos ; Fetch parameter "actor position X" from RAM + sta ActorsArray+Actor::XPos,x + lda ParamYPos ; Fetch parameter "actor position Y" from RAM + sta ActorsArray+Actor::YPos,x +EndRoutine: + rts +.endproc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Subroutine to spawn actors when certain conditions are met +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc SpawnActors + SpawnSubmarine: + lda Clock60 ; Submarines are added in intervals of 3 seconds + sec + sbc PrevSubmarine + 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 + lda #223 + sta ParamXPos ; Load parameter for actor position X + lda #185 + sta ParamYPos + + jsr AddNewActor ; Call the subroutine to add the new missile actor + + lda Clock60 + sta PrevSubmarine ; Save the current Clock60 time as the submarine last spawn time + : + + SpawnAirplane: + lda Clock60 ; Submarines are added in intervals of 3 seconds + sec + sbc PrevAirplane + 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 + lda #235 + sta ParamXPos ; Load parameter for actor position X + lda #60 + sta ParamYPos + + jsr AddNewActor ; Call the subroutine to add the new missile actor + + lda Clock60 + sta PrevAirplane ; Save the current Clock60 time as the submarine last spawn time + : + rts +.endproc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Subroutine to loop all actors and update them (position, velocity, etc.) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc UpdateActors + ldx #0 + ActorsLoop: + lda ActorsArray+Actor::Type,x + + cmp #ActorType::MISSILE + bne :+ + lda ActorsArray+Actor::YPos,x + sec + sbc #1 ; Decrement Y position of missiles by 1 + sta ActorsArray+Actor::YPos,x + bcs SkipMissile + lda #ActorType::NULL + sta ActorsArray+Actor::Type,x ; Remove the missile from the array + SkipMissile: + jmp NextActor + : + + cmp #ActorType::SUBMARINE + bne :+ + lda ActorsArray+Actor::XPos,x + sec + sbc #1 ; Decrement Y position of submarine by 1 + sta ActorsArray+Actor::XPos,x + bcs SkipSubmarine + lda #ActorType::NULL + sta ActorsArray+Actor::Type,x ; Remove the submarine from the array + SkipSubmarine: + jmp NextActor + : + + cmp #ActorType::AIRPLANE + bne :+ + lda ActorsArray+Actor::XPos,x + sec + sbc #1 ; Decrement Y position of submarine by 1 + sta ActorsArray+Actor::XPos,x + bcs SkipAirplane + lda #ActorType::NULL + sta ActorsArray+Actor::Type,x ; Remove the submarine from the array + SkipAirplane: + jmp NextActor + : + + NextActor: txa clc adc #.sizeof(Actor) tax - jmp ArrayLoop - - AddNewActorToArray: - lda ParamType - sta ActorsArray+Actor::Type,x - lda ParamXPos - sta ActorsArray+Actor::XPos,x - lda ParamYPos - sta ActorsArray+Actor::YPos,x - EndRoutine: - rts -.endproc - -.proc SpawnActors - SpawnSubmarine: - lda Clock60 ; Submarines added in 3 second intervals - sec ; - sbc PrevSubmarine ; - cmp #3 ; Only add if 3 seconds have pased - bne :+ ; - lda #ActorType::SUBMARINE ; - sta ParamType ; Load Param for the actor type - lda #223 ; - sta ParamXPos ; Load X Pos - lda #185 ; - sta ParamYPos ; - ; - jsr AddNewActor ; Call subroutine - ; - lda Clock60 ; - sta PrevSubmarine ; Save the current clocktime as the submarine clock spawn time - : + cmp #MAX_ACTORS * .sizeof(Actor) + bne ActorsLoop rts .endproc - -.proc UpdateActors - ldx #0 ; counter of how many actors - ActorsLoop: - lda ActorsArray+Actor::Type,x - - cmp #ActorType::MISSILE - bne :+ - ;;Update y Pos - ;; y-- - lda ActorsArray+Actor::YPos,x - sec - sbc #1 ; Decrement Y Position - sta ActorsArray+Actor::YPos,x ; - bcs SkipMissile - ;; Delete Missile - lda #ActorType::NULL - sta ActorsArray+Actor::Type,x ; - SkipMissile: - jmp NextActor - : - - cmp #ActorType::SUBMARINE - bne :+ - ;;Update x Pos - ;; x-- - lda ActorsArray+Actor::XPos,x - sec - sbc #1 ; Decrement X Position - sta ActorsArray+Actor::XPos,x ; - bcs SkipSubmarine - ;; Delete SUB - lda #ActorType::NULL - sta ActorsArray+Actor::Type,x ; - SkipSubmarine: - jmp NextActor - : - - NextActor: - txa - clc - adc #.sizeof(Actor) - tax - cmp #MAX_ACTORS * .sizeof(Actor) - bne ActorsLoop - rts -.endproc - - - +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Subroutine to loop all actors and send their tiles to the OAM-RAM at $200 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .proc RenderActors - lda #$02 ; - sta SprPtr+1 ; - lda #$00 ; - sta SprPtr ; set SprPtr to $0200 + lda #$02 + sta SprPtr+1 + lda #$00 + sta SprPtr ; Point SprPtr to $0200 - ldy #0 ; counter of how many tiles - ldx #0 ; counter of how many actors + ldy #0 ; Count how many tiles we are sending + ldx #0 ; Counts how many actors we are looping ActorsLoop: - lda ActorsArray+Actor::Type,x + lda ActorsArray+Actor::Type,x + + cmp #ActorType::SPRITE0 + bne :+ + lda ActorsArray+Actor::XPos,x + sta ParamXPos + lda ActorsArray+Actor::YPos,x + sta ParamYPos + lda #$70 + sta ParamTileNum + lda #%00100000 + sta ParamAttribs + lda #1 + sta ParamNumTiles + jsr DrawSprite ; Call routine to draw 1 SPRITE0 tile to the OAM + jmp NextActor + : + cmp #ActorType::PLAYER + bne :+ + lda ActorsArray+Actor::XPos,x + sta ParamXPos + lda ActorsArray+Actor::YPos,x + sta ParamYPos + lda #$60 + sta ParamTileNum + lda #%00000000 + sta ParamAttribs + lda #4 + sta ParamNumTiles + jsr DrawSprite ; Call routine to draw 4 PLAYER tiles to the OAM + jmp NextActor + : - cmp #ActorType::SPRITE0 - bne :+ - ;; Load Params with Y, TileNumber, Attribs, X,NumTiles=4 - lda ActorsArray+Actor::XPos,x - sta ParamXPos - lda ActorsArray+Actor::YPos,x - sta ParamYPos - lda #$70 - sta ParamTileNum - lda #%00100000 - sta ParamAttribs - lda #1 - sta ParamNumTiles - jsr DrawSprite - jmp NextActor + cmp #ActorType::SUBMARINE + bne :+ + lda ActorsArray+Actor::XPos,x + sta ParamXPos + lda ActorsArray+Actor::YPos,x + sta ParamYPos + lda #$04 + sta ParamTileNum + lda #%00100000 + sta ParamAttribs + lda #4 + sta ParamNumTiles + jsr DrawSprite ; Call routine to draw 4 SUBMARINE tile to the OAM + jmp NextActor + : + + cmp #ActorType::AIRPLANE + bne :+ + lda ActorsArray+Actor::XPos,x + sta ParamXPos + lda ActorsArray+Actor::YPos,x + sta ParamYPos + lda #$10 + sta ParamTileNum + lda #%00000011 + sta ParamAttribs + lda #3 + sta ParamNumTiles + jsr DrawSprite ; Call routine to draw 4 SUBMARINE tile to the OAM + jmp NextActor + : + + cmp #ActorType::MISSILE + bne :+ + lda ActorsArray+Actor::XPos,x + sta ParamXPos + lda ActorsArray+Actor::YPos,x + sta ParamYPos + lda #$50 + sta ParamTileNum + lda #%00000001 + sta ParamAttribs + lda #1 + sta ParamNumTiles + jsr DrawSprite ; Call routine to draw 1 MISSILE tile to the OAM + jmp NextActor + : + + NextActor: + txa + clc + adc #.sizeof(Actor) + tax + cmp #MAX_ACTORS * .sizeof(Actor) + + beq :+ + jmp ActorsLoop ; Use absolute jump to avoid branch limit of [-128..127] + : + + tya + pha ; Save the Y register to the stack + + LoopTrailingTiles: + cpy PrevOAMCount + bcs :+ + lda #$FF + sta (SprPtr),y ; Set Y position to $FF (to hide tile) + iny + sta (SprPtr),y ; Set tile number as $FF + iny + sta (SprPtr),y ; Set attribs as $FF + iny + sta (SprPtr),y ; Set X position to $FF (to hide tile) + iny + jmp LoopTrailingTiles : - cmp #ActorType::PLAYER - bne :+ - ;; Load Params with Y, TileNumber, Attribs, X,NumTiles=4 - lda ActorsArray+Actor::XPos,x - sta ParamXPos - lda ActorsArray+Actor::YPos,x - sta ParamYPos - lda #$60 - sta ParamTileNum - lda #%00000000 - sta ParamAttribs - lda #4 - sta ParamNumTiles - jsr DrawSprite - jmp NextActor - : + pla ; Save the previous value of Y into PrevOAMCount + sta PrevOAMCount ; This is the total number of bytes that we just sent to the OAM - cmp #ActorType::MISSILE - bne :+ - ;; Load Params with Y, TileNumber, Attribs, X,NumTiles=1 - lda ActorsArray+Actor::XPos,x - sta ParamXPos - lda ActorsArray+Actor::YPos,x - sta ParamYPos - lda #$50 - sta ParamTileNum - lda #%00000001 - sta ParamAttribs - lda #1 - sta ParamNumTiles - jsr DrawSprite - jmp NextActor - : - - cmp #ActorType::SUBMARINE - bne :+ - ;; Load Params with Y, TileNumber, Attribs, X,NumTiles=1 - lda ActorsArray+Actor::XPos,x - sta ParamXPos - lda ActorsArray+Actor::YPos,x - sta ParamYPos - lda #$04 - sta ParamTileNum - lda #%00000000 - sta ParamAttribs - lda #4 - sta ParamNumTiles - jsr DrawSprite - jmp NextActor - : - - NextActor: - txa - clc - adc #.sizeof(Actor) - tax - cmp #MAX_ACTORS * .sizeof(Actor) - - beq :+ - jmp ActorsLoop - : - - tya - pha ; Save Y to the Stack - - LoopTrailingTiles: - ;; Loop all bytes until we reach PrevOAMCount - cpy PrevOAMCount - bcs :+ - lda #$FF - sta (SprPtr),y ; set Y position to $ff to hide - iny ; - sta (SprPtr),y ; Set tile number as $FF - iny ; - sta (SprPtr),y ; Set attribs as $FF - iny ; - sta (SprPtr),y ; Setr X Position to $FF to hide tile - iny ; - jmp LoopTrailingTiles - : - - pla - sta PrevOAMCount - rts + rts .endproc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Routine to loop "NumTiles" times, sending bytes to the OAM-RAM +;; Routine to loop "NumTiles" times, sending bytes to the OAM-RAM. ;; Params = ParamXPos, ParamYPos, ParamTileNum, ParamAttribs, ParamNumTiles ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .proc DrawSprite - txa ;push x to stack - pha ; save value of X register before anything - + txa + pha ; Save the value of the X register before anything + ldx #0 TileLoop: - lda ParamYPos - sta (SprPtr),y ; Send Y Position to Ram - iny + lda ParamYPos ; Send Y position to the OAM + sta (SprPtr),y + iny - lda ParamTileNum ; Send Tile# to the OAM - sta (SprPtr),y ; - inc ParamTileNum ; ParamTileNum++ - iny + lda ParamTileNum ; Send the Tile # to the OAM + sta (SprPtr),y + inc ParamTileNum ; ParamTileNum++ + iny - lda ParamAttribs ; Send Attribs to the OAM - sta (SprPtr),y - iny + lda ParamAttribs ; Send the attributes to the OAM + sta (SprPtr),y + iny - lda ParamXPos ; Send X Position to the OAM - sta (SprPtr),y - clc - adc #8 - sta ParamXPos ; increment X Pos 8 pixels + lda ParamXPos ; Send X position to the OAM + sta (SprPtr),y + clc + adc #8 + sta ParamXPos ; ParamXPos += 8 - iny + iny - inx ; X++ - cpx ParamNumTiles ; loop NumTiles times - bne TileLoop + inx ; X++ + cpx ParamNumTiles ; Loop until X == NumTiles + bne TileLoop - ; pop x from stack pla - tax ; pull value of X from stack and transfer to x + tax ; Restore the previous value of X register rts .endproc - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Reset handler (called when the NES resets or powers on) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -491,15 +523,15 @@ InitVariables: sta Frame ; Frame = 0 sta Clock60 ; Clock60 = 0 sta XScroll ; XScroll = 0 - sta CurrNametable ; CurrNametable = 0 - sta Column ; Column = 0 + sta CurrNametable ; CurrNametable = 0 + sta Column ; Column = 0 lda #113 sta XPos lda #165 sta YPos Main: - jsr LoadPalette ; Call LoadPalette subroutine to load 32 colors into our palette + jsr LoadPalette ; Call LoadPalette subroutine to load 32 colors into our palette AddSprite0: lda #ActorType::SPRITE0 @@ -508,7 +540,7 @@ AddSprite0: sta ParamXPos lda #27 sta ParamYPos - jsr AddNewActor + jsr AddNewActor ; Add new actor to array AddPlayer: lda #ActorType::PLAYER @@ -517,168 +549,164 @@ AddPlayer: sta ParamXPos lda YPos sta ParamYPos - jsr AddNewActor + jsr AddNewActor ; Add new actor to array InitBackgroundTiles: lda #1 - sta CurrNametable ;current nametable= 1 + sta CurrNametable ; CurrNametable = 1 lda #0 - sta XScroll - sta Column + sta XScroll ; XScroll = 0 + sta Column ; Column = 0 InitBackgroundLoop: - jsr DrawNewColumn ; draw all rows of new column + jsr DrawNewColumn ; Draw all rows of new column (from top to bottom) lda XScroll clc adc #8 - sta XScroll ; xscroll += 8 - - inc Column ;column++ - + sta XScroll ; XScroll += 8 + + inc Column ; Column++ + lda Column cmp #32 - bne InitBackgroundLoop ; loop 32 times to repeat all 32 columns of the first nametable + bne InitBackgroundLoop ; Repeat all 32 columns of the first nametable lda #0 - sta CurrNametable ;current nametable = 0 + sta CurrNametable ; CurrNametable = 0 lda #1 - sta XScroll ;Scroll = 1 + sta XScroll ; Scroll = 1 - jsr DrawNewColumn ; draw first column of 2nd nametable - inc Column ; Column ++ + jsr DrawNewColumn ; Draw first column of the second nametable + inc Column ; Column++ lda #%00000000 - sta PPU_CTRL ; PPU back to +1 mode + sta PPU_CTRL ; Set PPU increment to +1 mode InitAttribs: lda #1 - sta CurrNametable ;current nametable= 1 + sta CurrNametable lda #0 sta XScroll sta Column InitAttribsLoop: - jsr DrawNewAttribs ; draw all rows of new column + jsr DrawNewAttribs ; Draw attributes in the correct place of the first nametable lda XScroll clc adc #32 - sta XScroll ; xscroll += 32 + sta XScroll ; XScroll += 32 - lda Column + lda Column ; Repeat for all elements of the first nametable clc adc #4 - sta Column + sta Column ; Column += 4 cmp #32 - bne InitAttribsLoop ; loop 32 times to repeat all 32 columns of the first nametable + bne InitAttribsLoop ; Loop until we reach Column 32 lda #0 - sta CurrNametable ;current nametable = 0 + sta CurrNametable lda #1 - sta XScroll ;Scroll = 1 - jsr DrawNewAttribs ; draw first column of 2nd nametable + sta XScroll + jsr DrawNewAttribs ; Draw first attributes of second nametable - inc Column + inc Column ; Column = 33 EnableRendering: - lda #%10010000 ; Enable NMI and set background to use the 2nd pattern table (at $1000) + lda #%10010000 ; Enable NMI and set background to use the 2nd pattern table (at $1000) sta PPU_CTRL lda #0 - sta PPU_SCROLL ; Disable scroll in X - sta PPU_SCROLL ; Disable scroll in Y + sta PPU_SCROLL ; Disable scroll in X + sta PPU_SCROLL ; Disable scroll in Y lda #%00011110 - sta PPU_MASK ; Set PPU_MASK bits to render the background + sta PPU_MASK ; Set PPU_MASK bits to render the background -;;;;;;; START GAME LOOP GameLoop: lda Buttons - sta PrevButtons + sta PrevButtons ; Stores the previously pressed buttons - ;Perform Game Logic Here - jsr ReadControllers - -CheckAButton: + jsr ReadControllers ; Read joypad and load button state + + CheckAButton: lda Buttons and #BUTTON_A beq :+ - lda Buttons - and #BUTTON_A ; DEBOUNCE A PRESS - cmp PrevButtons ; Compare current buttons and check if A was previously pressed - beq:+ - lda #ActorType::MISSILE - sta ParamType - lda XPos - sta ParamXPos - lda YPos - jsr AddNewActor + lda Buttons + and #BUTTON_A + cmp PrevButtons ; Compare the current buttons and check if button A was previously pressed + beq :+ ; If it was, we bypass the creation of a new missile + lda #ActorType::MISSILE + sta ParamType + lda XPos + sta ParamXPos + lda YPos + sta ParamYPos + jsr AddNewActor ; Call the subroutine to add a new missile actor : jsr SpawnActors jsr UpdateActors jsr RenderActors - WaitForVBlank: ; we lock execution - lda IsDrawComplete ; check and only perform game loop call once NMI is done drawing - beq WaitForVBlank ; otherwise keep looping + WaitForVBlank: ; We lock the execution of the game logic here + lda IsDrawComplete ; Here we check and only perform a game loop call once NMI is done drawing + beq WaitForVBlank ; Otherwise, we keep looping - lda #0 ; - sta IsDrawComplete ; set back to 0 + lda #0 + sta IsDrawComplete ; Once we're done, we set the DrawComplete flag back to 0 - jmp GameLoop; Force an infinite execution loop -;;;;;;; END GAME LOOP + jmp GameLoop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; NMI interrupt handler ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; NMI: - PUSH_REGS ; push registers to the stack + PUSH_REGS ; Macro to save register values by pushing them to the stack - inc Frame ; Frame++ + inc Frame ; Frame++ -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 +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 NewColumnCheck: lda XScroll - and #%00000111 ; Check if the scroll a multiple of 8 - bne EndColCheck ; If it isn't, we still don't need to draw a new column - jsr DrawNewColumn ; If it is a multiple of 8, we proceed to draw a new column of tiles! - + and #%00000111 ; Check if the scroll a multiple of 8 + bne :+ ; If it isn't, we still don't need to draw a new column + jsr DrawNewColumn ; If it is a multiple of 8, we proceed to draw a new column of tiles! Clamp128Cols: lda Column clc - adc #1 - and #%01111111 ; drop left most bit to wrap around 128 - sta Column ; clamping the value to never go over 128 - - EndColCheck: + adc #1 ; Column++ + and #%01111111 ; Drop the left-most bit to wrap around 128 + sta Column ; Clamping the value to never go beyond 128 + : NewAttribsCheck: - lda XScroll - and #%00011111 - bne :+ - jsr DrawNewAttribs - : + lda XScroll + and #%00011111 ; Check if the scroll is a multiple of 32 (lowest 5 bits are 00000) + bne :+ ; If it isn't, we still don't need to draw new attributes + jsr DrawNewAttribs ; It it is a multiple of 32, we draw the new attributes! + : SetPPUNoScroll: - lda #0 - sta PPU_SCROLL - sta PPU_SCROLL + lda #0 + sta PPU_SCROLL + sta PPU_SCROLL ; Set *no* scroll for the status bar EnablePPUSprite0: - lda #%10010000 - sta PPU_CTRL - lda #%00011110 - sta PPU_MASK + lda #%10010000 ; Enable PPU sprites for sprite 0 + sta PPU_CTRL + lda #%00011110 ; Enable sprites and enable background + sta PPU_MASK WaitForNoSprite0: - lda PPU_STATUS - and #%01000000 - bne WaitForNoSprite0 + lda PPU_STATUS + and #%01000000 ; PPU address $2002 bit 6 is the sprite 0 hit flag + bne WaitForNoSprite0 ; Loop until we do *not* have a sprite 0 hit WaitForSprite0: - lda PPU_STATUS - and #%01000000 ; PPU address $2002 bit 6 is the sprite hit flag - beq WaitForSprite0 ; loop until we do NOT have a sprite 0 hit + lda PPU_STATUS + and #%01000000 ; PPU address $2002 bit 6 is the sprite 0 hit flag + beq WaitForSprite0 ; Loop until we do have a sprite 0 hit ScrollBackground: inc XScroll ; XScroll++ @@ -711,11 +739,10 @@ SetGameClock: SetDrawComplete: lda #1 - sta IsDrawComplete - inc Frame - + sta IsDrawComplete ; Set the DrawComplete flag to indicate we are done drawing to the PPU PULL_REGS + rti ; Return from interrupt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -751,10 +778,10 @@ BackgroundData: .byte $13,$13,$6c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$59,$56,$00,$00 .byte $13,$13,$78,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$15,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 .byte $13,$13,$7b,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$15,$15,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$53,$56,$00,$00 -.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$15,$21,$21,$25,$35,$15,$15,$12,$00,$60,$00,$00,$54,$56,$00,$00 +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$15,$21,$21,$25,$35,$15,$15,$12,$00,$00,$00,$00,$54,$56,$00,$00 .byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$26,$36,$15,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 .byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$27,$37,$15,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 -.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$48,$21,$21,$15,$27,$37,$15,$15,$12,$00,$00,$00,$00,$5d,$55,$00,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$48,$21,$21,$15,$27,$37,$15,$15,$12,$00,$00,$00,$00,$5d,$56,$00,$00 .byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$28,$38,$3e,$21,$12,$00,$00,$00,$00,$58,$56,$00,$00 .byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$22,$35,$3f,$21,$12,$00,$00,$00,$00,$58,$56,$00,$00 .byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$26,$36,$3f,$21,$12,$00,$00,$00,$00,$57,$56,$00,$00 @@ -784,7 +811,7 @@ BackgroundData: .byte $13,$13,$6c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$21,$21,$21,$21,$27,$37,$15,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 .byte $13,$13,$78,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$15,$21,$21,$21,$28,$38,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$7b,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$15,$15,$21,$21,$29,$39,$15,$15,$12,$00,$00,$00,$00,$53,$56,$aa,$00 -.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$15,$21,$1f,$2a,$3a,$3c,$15,$12,$00,$61,$00,$00,$54,$56,$aa,$00 +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$15,$21,$1f,$2a,$3a,$3c,$15,$12,$00,$00,$00,$00,$54,$56,$aa,$00 .byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$28,$3b,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$48,$21,$21,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$5d,$56,$aa,$00 @@ -817,7 +844,7 @@ BackgroundData: .byte $13,$13,$6c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$78,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$7b,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 -.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$62,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$29,$39,$15,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 .byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$1f,$2a,$3a,$3d,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$48,$21,$15,$2d,$2a,$3a,$3c,$15,$12,$00,$00,$00,$00,$5b,$56,$aa,$00 @@ -850,7 +877,7 @@ BackgroundData: .byte $13,$13,$6c,$13,$20,$21,$21,$21,$21,$21,$48,$21,$15,$21,$21,$21,$21,$1d,$1e,$2a,$3a,$3c,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$78,$13,$20,$21,$21,$21,$21,$21,$49,$21,$21,$21,$21,$21,$21,$21,$21,$2b,$3b,$3e,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$7b,$13,$20,$21,$21,$21,$21,$21,$4a,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$4e,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 -.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$15,$48,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$4e,$15,$12,$00,$63,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$15,$48,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$4e,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$49,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$3f,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 .byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$4a,$21,$21,$21,$21,$21,$21,$15,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 .byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$15,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 @@ -868,55 +895,41 @@ BackgroundData: .byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$22,$32,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 AttributeData: -.byte $FF,$AA,$AA,$AA,$9A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$6A,$A6,$00,$00,$00 -.byte $FF,$AA,$AA,$9A,$59,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$9A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$9a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$6a,$a6,$00,$00,$00 +.byte $ff,$aa,$aa,$9a,$59,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$9a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 -.byte $FF,$AA,$AA,$5A,$9A,$00,$00,$00 -.byte $FF,$AA,$AA,$9A,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$6A,$56,$00,$00,$00 -.byte $FF,$AA,$AA,$9A,$59,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $ff,$aa,$aa,$5a,$9a,$00,$00,$00 +.byte $ff,$aa,$aa,$9a,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$6a,$56,$00,$00,$00 +.byte $ff,$aa,$aa,$9a,$59,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$9A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$AA,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$56,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$9A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$59,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$9a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$aa,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$56,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$9a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$59,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$9A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$59,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$56,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$59,$00,$00,$00 -.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; This is the OAM sprite attribute data data we will use in our game. -;; We have only one big metasprite that is composed of 2x4 hardware sprites. -;; The OAM is organized in sets of 4 bytes per tile. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -SpriteData: -; Y tile# attributes -.byte $27, $70, %00100001, $6 ; [] Sprite 0, used to split screen -; Y tile# attributes X -.byte $A6, $60, %00000000, $70 ; _______________ -.byte $A6, $61, %00000000, $78 ; \ o o o o o / <-- Ship (4 tiles) -.byte $A6, $62, %00000000, $80 ; \___________/ -.byte $A6, $63, %00000000, $88 ; +.byte $ff,$aa,$aa,$aa,$9a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$59,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$56,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$59,$00,$00,$00 +.byte $ff,$aa,$aa,$aa,$5a,$00,$00,$00 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Here we add the CHR-ROM data, included from an external .CHR file diff --git a/atlantico/atlantico.nes b/atlantico/atlantico.nes index c003e5019f50cef27f23eccda9710ed6393e4243..18fe21fd8a8538fd05d4f5998121ebda80e31d92 100644 GIT binary patch delta 931 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 XfU@$6!hwSZR8$s6aMZ&Q%W?i6^Bii}