1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
;;;
;;; TODO: reduce the scope of this to just:
;;; - Movement through subpixels.
;;; - Shooting (no collision or anything)
;;; -> link to jetpac.nes for more stuff
;;;
;; This is similar to the sprite.s example, but it expands on it greatly into a
;; full game by:
;; - Having a moving background.
;; - The ship can be moved:
;; - The movement is done through subpixels for a smoother experience.
;; - The ship's sprites are updated accordingly: resting, acceleration, full
;; speed.
;; - Random asteroids will appear from time to time and they can collide with
;; the ship:
;; - A collision decreases the live status from the ship (cracks will appear
;; to the sprite).
;; - When the live status reaches 0 -> game over.
;; - The ship can shoot and destroy asteroids.
;; - There is a score.
;;;
.segment "HEADER"
.byte 'N', 'E', 'S', $1A
;; 2x PRG-ROM; 1x CHR-ROM
.byte $02
.byte $01
;; Horizontal mirroring, no special mapper.
.byte $00
.byte $00
.segment "VECTORS"
.addr nmi, reset, irq
.segment "CHARS"
.incbin "../assets/space.chr"
.segment "STARTUP"
.segment "CODE"
.include "../include/apu.s"
.include "../include/oam.s"
.include "../include/ppu.s"
.include "../include/joypad.s"
.include "states/game.s"
.include "states/player.s"
.include "vectors/reset.s"
.include "vectors/nmi.s"
.include "vectors/irq.s"
;;;
;; This is our main subroutine, the reset procedure will call at the very end of
;; initializing the hardware.
;;;
.proc main
;; Before starting the game loop proper we initialize all our assets: load
;; the palettes, nametables and sprites for this game.
jsr init_palettes
jsr init_nametable
jsr Player::init
;; Reset scroll.
bit PPU::STATUS
lda #$00
sta PPU::SCROLL
sta PPU::SCROLL
cli
;; 7: allow NMI; 5: sprite size is 8x16; 4: background pattern table starts
;; at $1000.
lda #%10110000
sta PPU::CONTROL
;; 4: show sprites; 3: show background; 2: show sprites in leftmost pixels
;; on the screen; 1: same as 2 but for background.
lda #%00011110
sta PPU::MASK
@main_game_loop:
jsr Joypad::read
jsr Player::Movement::update
jsr Player::Sprite::update
;; This is a hand-shake between the code on `main` and the code on the
;; `nmi`. See Game::flags for more.
SET_RENDER_FLAG
@wait_for_render:
bit Game::flags
bmi @wait_for_render
;; Rendering is done, we can perform another iteration of the loop!
jmp @main_game_loop
.endproc
;; init_palettes copies all the palettes for our game into the proper PPU
;; address.
.proc init_palettes
PPU_ADDR $3F00
ldx #0
@load_palettes_loop:
lda palettes, x
sta PPU::DATA
inx
cpx #$20
bne @load_palettes_loop
rts
palettes:
;; Background
.byte $0F, $12, $22, $32
.byte $0F, $00, $28, $30
.byte $0F, $28, $16, $2D
.byte $0F, $28, $16, $2D
;; Foreground
.byte $0F, $00, $05, $30
.byte $0F, $00, $00, $00
.byte $0F, $00, $00, $00
.byte $0F, $00, $00, $00
.endproc
;; init_nametable loads the relevant data to the nametable that is then going to
;; be used in order to build up the background.
.proc init_nametable
bit PPU::STATUS
;; Big stars.
WRITE_PPU_DATA $20C8, $02
WRITE_PPU_DATA $227A, $02
;; Small stars.
WRITE_PPU_DATA $20B9, $04
WRITE_PPU_DATA $21CE, $04
WRITE_PPU_DATA $21BA, $04
WRITE_PPU_DATA $22B8, $04
WRITE_PPU_DATA $22E7, $04
;; Select palette 1 for one of the small stars, giving it a red-ish look.
WRITE_PPU_DATA $23CE, %00000001
rts
.endproc
|