Skip to content

Instantly share code, notes, and snippets.

@Solvalou
Created May 17, 2010 18:13
Show Gist options
  • Save Solvalou/404047 to your computer and use it in GitHub Desktop.
Save Solvalou/404047 to your computer and use it in GitHub Desktop.
Space Invaders NES Reconstruction
; iNES - Header
.inesprg 1 ; size of PRG-ROM, 16kB
.ineschr 1 ; size of CHR-ROM/RAM, 8kB
.inesmir 0 ; type of mirroring, 0 = horizontal, 1 = vertical
.inesmap 0 ; used mapper, 0 = no mapper
.bank 1
.org $FFFA
.dw int_return ; NMI_Routine($FFFA)
.dw start ; Reset_Routine($FFFC)
.dw int_return ; IRQ_Routine($FFFE)
.bank 0
.org $0000 ; RAM, zero-page
missile_status: .db 0
alien_status: .db 0
a_status: .db 0 ; status of the a-button
death_status: .db 0
index: .db 0
line_index: .db 0
collision_index: .db 0
collision_line_index: .db 0
collision_offset: .db 0
line_address: .db 0
line_carry: .db 0
line_offset: .db 0
vblank_count: .db 0
death_count: .db 0
temp: .db 0
anim_var: .db 0
direction: .db 0
anim0_status: .db 0
anim1_status: .db 0
anim2_status: .db 0
anim3_status: .db 0
alien_pos: .db 0
alien_max: .db 0
alien_min: .db 0
line_lo: .db 0
line_hi: .db 0
missile_left: .db 0
missile_right: .db 0
missile_top: .db 0
alien_left: .db 0
alien_right: .db 0
alien_bottom: .db 0
.org $0200
alien_data: .db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.db $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
.org $0300 ; OAM-area
player_y: .db 0 ; y-position
player_t: .db 0 ; tile-number
player_f: .db 0 ; special flags
player_x: .db 0 ; x-position
_player_y: .db 0
_player_t: .db 0
_player_f: .db 0
_player_x: .db 0
missile_y: .db 0
missile_t: .db 0
missile_f: .db 0
missile_x: .db 0
explosion_y: .db 0
explosion_t: .db 0
explosion_f: .db 0
explosion_x: .db 0
death_y: .db 0
death_t: .db 0
death_f: .db 0
death_x: .db 0
_death_y: .db 0
_death_t: .db 0
_death_f: .db 0
_death_x: .db 0
hitbox_y: .db 0
hitbox_t: .db 0
hitbox_f: .db 0
hitbox_x: .db 0
; -------------------------------------------------------------------------------------
.org $8000 ; start of the PRG-ROM
start:
var_reset:
ldx #$00
stx a_status
stx vblank_count
stx death_count
stx death_status
stx anim_var
stx anim0_status
stx anim1_status
stx anim2_status
stx anim3_status
stx direction
stx alien_pos
stx alien_min
stx alien_max
stx index
stx temp
stx line_index
stx line_address
stx line_offset
stx line_carry
stx collision_index
stx collision_offset
stx collision_line_index
temp_var: ; must be erased later
lda #$07
sta alien_max
lda #$03
sta alien_min
CPU_init:
cld ; clear decimal mode, not supported
sei ; disable interrupts
stack_init:
ldx #$FF ; set x to $FF = top of stack
txs ; transfer x to stack
palette_init:
lda #$3F ; sets the start-address of the
sta $2006 ; palette-data at $3F00
lda #$00
sta $2006
ldx #$00
palette_load:
lda palette_data, x
sta $2007
inx
cpx #32
bne palette_load
nametable_init:
lda #$20
sta $2006
lda #$00
sta $2006 ; address set to $2000
ldx #$00 ; x reset
lda #$00 ; a reset
; clear 960 bytes
nametable_clear1:
sta $2007
sta $2007
sta $2007
inx
bne nametable_clear1 ; clear 3 x 256 bytes / 768 bytes
ldx #$00
nametable_clear2:
sta $2007
inx
cpx #$C0
bne nametable_clear2 ; clear 192 bytes
jsr scanline_reset
PPU_init:
lda #%00001000
sta $2000 ; background pattern table at $0000
; sprite pattern table at $1000
lda #%00011110
sta $2001 ; #%76543210
; 4 - sprites visible
; 3 - background visible
; 2 - no sprite clipping
; 1 - no background clipping
; 0 - color display
; -------------------------------------------------------------------------------------
; initialize variables
jsr sprite_init
jsr alien_init
jsr alien_data_init
lda #$01
sta anim0_status
infinite: ; infinite loop
; jsr vblank_reset
jsr death_reset
jsr input ; check input
sprite_loading:
jsr player_load ; load the player-ship
jsr missile_load ; load the missile-sprite, only when a-button was pressed
jsr vblank ; wait vor vertical blank
jsr sprite_load ; sprite DMA loading-routine
nametable_loading:
; jsr scrolling
jsr animation
jsr alien_load ; load nametable with aliens
; lda #$01
; sta first_run
collision_detection:
lda missile_status ; check if a missile was fired
beq infinite ; if not, jmp back to beginning of the infinite-loop
lda #$00
sta collision_index
sta collision_line_index
collision_loop:
jsr collision_offset_calc
; lda #1
; sta collision_line_index
; lda #11
; sta collision_offset
lda collision_index ; if fired, ldy with the collision_index
adc collision_offset
tay
lda alien_data, y ; check for alien
beq a_inc ; if (no alien/0) jmp y_inc
; beq b_inc
; inc collision_index
jsr missile_hitbox
jsr alien_hitbox
jsr draw_hitbox
; jsr vblank
; jsr sprite_load
jsr check_collision
lda #$00
sta collision_line_index
inc collision_index
ldy collision_index
cpy #$0B
bne collision_loop ; if (collision_index = 11) jmp collision_end
jmp infinite
; .....................
a_inc:
inc collision_line_index
lda collision_line_index
cmp #$05
beq collision_line_index_reset
jmp collision_loop
b_inc:
inc collision_index ; increment y, used as index
lda collision_index
cmp #$0B
beq collision_index_reset
jmp collision_loop ; next alien_data-position gets checked
; .....................
collision_index_reset:
lda #$00
sta collision_index
jmp infinite
collision_line_index_reset:
; inc line_index
lda #$00
sta collision_line_index
jmp infinite
; .....................
collision_offset_calc:
lda #$00
sta collision_offset
ldx collision_line_index
beq collision_offset_end
collision_offset_loop:
lda collision_offset
adc #$0B
sta collision_offset
dex
bne collision_offset_loop
collision_offset_end:
rts
; -------------------------------------------------------------------------------------
sprite_init:
lda #207
sta player_y ; set player_y position to 207
sta _player_y ; this is a solid position, it never gets changed
lda #16 ; set player_x position to 16
sta player_x ; this is the start position of the player
clc
adc #$08
sta _player_x
lda #$FF ; set missile_y position to $FF
sta missile_y ; $FF = off screen
lda #$00
sta player_t ; set the player-tile (0)
sta player_f ; reset the special-flags
sta _player_f
sta missile_f
sta death_f
sta _death_f
sta explosion_f
lda #$01
sta _player_t
lda #$02
sta missile_t
lda #$03
sta explosion_t
lda #$04
sta death_t
lda #$05
sta _death_t
rts
player_load:
lda player_x ; load the x-position for the first player sprite
clc
adc #$08 ; add 8
sta _player_x ; store the x-position for the second player sprite
lda #$FF ; set the explosion sprite off screen
sta explosion_y
rts
missile_load:
lda missile_status ; check missile_status, 1 = fired / 0 = unfired
beq sprite_end ; rts if it isn't fired
dec missile_y ; decrement y-position 2 times
dec missile_y ; missile flies towards top of the screen
lda missile_y
cmp #16
bcs sprite_end ; if (missile_y > 16) jmp sprite_end = rts
jsr explosion ; jump subroutine explosion (line 262)
lda #$00 ; reset the missile_status / unfired
sta missile_status
lda #$FF
sta missile_y ; set missile y-position off screen
rts
explosion:
; sets the explosion-position to the current missile-position
lda missile_y
sta explosion_y
lda missile_x
sbc #$03
sta explosion_x
rts
sprite_end:
rts
sprite_load:
lda #$03 ; set destination to $0300
sta $4014 ; beginn sprite-DMA
rts
death_reset:
lda death_y
cmp #$FF
bcs death_reset_end
lda death_status
beq death_reset_end
lda death_count
cmp #05
bne death_count_inc
lda #$00
sta death_count
lda #$FF
sta death_y
sta _death_y
death_count_inc:
inc death_count
death_reset_end:
rts
; -------------------------------------------------------------------------------------
alien_init:
; sets the load-position of the nametable
lda #$21
sta line_lo
lda #$C0
sta line_hi
lda #$05
sta alien_pos ; x-position of the first alien-tile
rts
alien_data_init:
ldx #$00 ; reset x, loop-index
lda #$01 ; set a to 1, 1 = alien enabled
alien_data_init_rtn:
; set the alien_data-table to "1"
sta alien_data, x
inx
cpx #55
bne alien_data_init_rtn
rts
animation:
lda vblank_count
cmp #$05
beq animation0
cmp #$0A
beq animation1
cmp #$0F
beq animation2
cmp #$14
beq animation3
jmp animation_end
animation0:
lda #$00
sta anim0_status
lda #$01
sta anim1_status
jmp animation_end
animation1:
lda #$00
sta anim1_status
lda #$01
sta anim2_status
jmp animation_end
animation2:
; inc alien_pos
lda #$00
sta anim2_status
lda #$01
sta anim3_status
jmp animation_end
animation3:
lda #$00
sta anim3_status
lda #$01
sta anim0_status
lda #$00
sta vblank_count
jmp animation_end
animation_end:
rts
; initialize actual alien-position
alien_load:
line_address_calc:
lda line_index
asl a
asl a
asl a
asl a
asl a
asl a
sta line_address
line_carry_calc:
lda line_index
cmp #$04
bne line_carry_reset
lda #$01
sta line_carry
jmp alien_load_init
line_carry_reset:
lda #$00
sta line_carry
; ----------------
alien_load_init:
lda line_lo
sec
sbc line_carry
sta $2006
lda line_hi
clc
adc alien_pos
sec
sbc #$01
sta $2006
lda #$00
sta $2007
; ----------------
alien_load_loop:
lda index
cmp #$0B
beq index_reset ; if (x = 12) jmp index_reset
jsr line_offset_calc
lda index
adc line_offset
tax
lda alien_data, x ; load alien_data, 1 = alien enabled / 2 = alien disabled
bne load_tile ; if (a = 1) jmp load_tile
; if (a = 0) jmp load_empty
load_empty:
lda #$00 ; draw to empty tiles at the current position
sta $2007
sta $2007
load_tile_rtn:
inc index ; increase index
jmp alien_load_loop
load_tile_end:
inc anim_var
jsr scanline_reset
rts
index_reset:
inc vblank_count
lda #$00
sta index
inc line_index
lda line_index
cmp #$05
beq line_index_reset
jmp index_end
line_index_reset:
lda direction
beq jmp0
jsr alien_pos_dec
jmp ovr_jmp
jmp0:
jsr alien_pos_inc
ovr_jmp:
jsr direction_init
lda #$00
sta line_index
index_end:
jsr scanline_reset
inc anim_var
rts
load_tile:
lda anim_var
beq load_tile_end
lda direction ; 0 = right, 1 = left
beq direction_right
jmp direction_left
direction_right:
jsr alien_load_init1
jsr alien_load_init0
lda anim0_status
bne load_tile0
lda anim1_status
bne load_tile1
lda anim2_status
bne load_tile2
lda anim3_status
bne load_tile3
load_tile0:
lda #$40
sta $2007
lda #$41
sta $2007
lda #$00
sta anim_var
jmp load_tile_rtn
load_tile1:
lda #$50
sta $2007
lda #$51
sta $2007
lda #$00
sta anim_var
jmp load_tile_rtn
load_tile2:
lda #$00
sta $2007
lda #$52
sta $2007
lda #$00
sta anim_var
jmp load_tile_rtn
load_tile3:
lda #$53
sta $2007
lda #$54
sta $2007
lda #$00
sta anim_var
jmp load_tile_rtn
direction_left:
jsr alien_load_init1
jsr alien_load_init0
lda anim0_status
bne load_tile3
lda anim1_status
bne load_tile2
lda anim2_status
bne load_tile1
lda anim3_status
bne load_tile0
alien_pos_inc:
lda anim2_status
beq alien_pos_inc_end
inc alien_pos
alien_pos_inc_end:
rts
alien_pos_dec:
lda anim0_status
beq alien_pos_dec_end
dec alien_pos
alien_pos_dec_end:
rts
direction_init:
check_right:
lda anim0_status
beq check_left
lda alien_pos
cmp alien_max
bne check_left
lda #$01
sta direction
lda line_hi
adc #$20
sta line_hi
lda #$0F
sta vblank_count
jmp direction_end
check_left:
lda anim3_status
beq direction_end
lda alien_pos
cmp alien_min
bne direction_end
lda #$00
sta direction
lda line_hi
adc #$20
sta line_hi
direction_end:
rts
; ----------------
alien_load_init0:
lda line_lo ; lo-part of the actual line address
sec
sbc line_carry
sta $2006
lda line_hi ; load the hi-part of the actual line address
sec
sbc line_address
clc
adc alien_pos ; add the actual alien-position (x tile-position)
adc index ; add the current index twice
adc index
sta $2006
rts
; ----------------
alien_load_init1:
lda line_lo ; lo-part of the actual line address
sec
sbc line_carry
sta $2006
lda line_hi ; load the hi-part of the actual line address
sec
sbc line_address
sbc #$20
clc
adc alien_pos ; add the actual alien-position (x tile-position)
adc index ; add the current index twice
adc index
sta $2006
lda #$00
sta $2007
sta $2007
rts
; ......................
line_offset_calc:
lda #$00
sta line_offset
ldx line_index
beq line_offset_end
line_offset_loop:
lda line_offset
adc #$0B
sta line_offset
dex
bne line_offset_loop
line_offset_end:
rts
; -------------------------------------------------------------------------------------
missile_hitbox:
lda missile_y
clc
adc #$04
sta missile_top
lda missile_x
sta missile_left
adc #$01
sta missile_right
rts
alien_hitbox:
lda collision_index
asl a
clc
adc alien_pos
tax
jsr address_calc
rts
; {
address_calc:
; ldx alien_pos
lda #$00
loop0:
adc #$08
dex
bne loop0
sta alien_left
adc #$10
sta alien_right
lda line_lo
and #$0F
beq ovr_jmp0
tax
lda #$00
loop1:
adc #64
dex
bne loop1
tay
ovr_jmp0:
lda line_hi
beq ovr_jmp1
lsr a
lsr a
lsr a
lsr a
lsr a
sta temp
tax
inx
lda #$00
loop2:
adc #$08
dex
bne loop2
sta alien_bottom
tya
adc alien_bottom
sta alien_bottom
lda collision_line_index
asl a
beq ovr_jmp1
tax
lda #$00
loop3:
adc #$08
dex
bne loop3
sta temp
lda alien_bottom
sec
sbc temp
sta alien_bottom
ovr_jmp1:
rts
; }
draw_hitbox:
lda #$10
sta hitbox_t
lda alien_left
sta hitbox_x
lda alien_bottom
clc
sbc #$08
sta hitbox_y
rts
; -------------------------------------------------------------------------------------
check_collision:
clc
;if (top1 > bottom2) return(0);
lda missile_top
cmp alien_bottom
bcs check_collision_end
;if (right1 < left2) return(0);
lda missile_right
cmp alien_left
bcc check_collision_end
;if (left1 > right2) return(0);
lda missile_left
cmp alien_right
bcs check_collision_end
collision_detected:
lda #$00
sta missile_status
lda #$FF
sta missile_y
lda #$01
sta death_status
lda alien_left
sta death_x
clc
adc #$08
sta _death_x
lda alien_bottom
clc
sbc #$08
sta death_y
sta _death_y
jsr vblank
lda line_lo
lda #$21
sta $2006
clc
lda line_hi
adc alien_pos
adc collision_index
adc collision_index
sta $2006
lda #$00
sta $2007
sta $2007
jsr scanline_reset
clc
lda collision_index
adc collision_offset
tay
lda #$00
sta alien_data, y
check_collision_end:
rts
; -------------------------------------------------------------------------------------
input:
input_init:
lda #$01 ; strobe the joypad before read
sta $4016
lda #$00
sta $4016
input_read:
lda $4016 ; a-button
and #1
bne a_button
lda missile_status
bne a_return
lda #$00
sta a_status
a_return:
lda $4016 ; b-button
lda $4016 ; select-button
lda $4016 ; start-button
lda $4016 ; d-pad up
lda $4016 ; d-pad down
lda $4016 ; d-pad left
and #1
bne left
left_return:
lda $4016 ; d-pad right
and #1
bne right
right_return:
rts
a_button:
lda a_status
bne a_return
lda #$01
sta a_status
sta missile_status
lda player_x
clc
adc #$08
sta missile_x
lda player_y
sta missile_y
jmp a_return
left:
lda player_x
cmp #16
beq left_return
dec player_x
jmp left_return
right:
lda player_x
cmp #224
beq right_return
inc player_x
jmp right_return
; -------------------------------------------------------------------------------------
vblank:
lda $2002 ; waits for vertical blank
bpl vblank
rts
scanline_reset:
lda #$00
sta $2005
sta $2005
rts
int_return:
rti
; -------------------------------------------------------------------------------------
palette_data: .incbin "data\palette.pal"
nametable: .incbin "data\nametable.map"
.bank 2
.org $0000
.incbin "data\background.chr" ; 4kB of background sprites
.incbin "data\sprite.chr" ; 4kB of sprite-data
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment