aboutsummaryrefslogtreecommitdiff
path: root/basics/chr-ram.s
blob: bb2186efabb8b79e71a4862d68e49856ee8032a9 (plain)
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
;;;
;; This is the same example as `basics/sprite.s` but ported to the UNROM chip.
;; This example could have been done with other chips, but the main idea is to
;; render the same thing as `basics/sprite.s` but with CHR-RAM instead of
;; CHR-ROM. You can take a look at the difference between these two approaches
;; here: https://www.nesdev.org/wiki/CHR_ROM_vs._CHR_RAM.

;; Same as the `basics/unrom.s` example. Just annotating what's relevant for
;; this example.
.segment "HEADER"
    .byte 'N', 'E', 'S', $1A
    .byte $08                   ; 128KB  of PRG-ROM (8 x 16KB)
    .byte $00                   ; No CHR-ROM.

    .byte $20, $08              ; Mapper 2, horizontal mirroring, NES 2.0

    .byte $00
    .byte $00
    .byte $00
    .byte $07                   ; 8192 (64 * 2^7) bytes CHR RAM, no battery

.segment "VECTORS"
    .addr nmi, reset, irq

;;;
;; Bank names as defined on the `basics/unrom.s` example. For this example we
;; use BANK0 to store the graphics that will be moved into `CHR-RAM` by calling
;; `transfer_to_chr_ram`. This is the main point to be taken from this example.

.segment "BANK0"

chr:
    .incbin "../assets/basic.chr"

.segment "BANK1"
.byte $00

.segment "BANK2"
.byte $00

.segment "BANK3"
.byte $00

.segment "BANK4"
.byte $00

.segment "BANK5"
.byte $00

.segment "BANK6"
.byte $00

;;;
;; Just like in the `basics/unrom.s` example, we use the fixed bank for the core
;; functionality.

.segment "FIXED"

;;;
;; From here on the code is basically the same as `basics/sprite.s`, but with a
;; special twist that will be commented in. For comments on the rest of the code
;; just check `basics/sprite.s`.

.proc reset
    sei
    cld

    ldx #$FF
    stx $4017

    ldx #$FF
    txs
    inx
    stx $2000
    stx $2001
    stx $4010

@vblankwait1:
    bit $2002
    bpl @vblankwait1

    ldx #0
    lda #0
@ram_reset_loop:
    sta $000, x
    sta $100, x
    sta $300, x
    sta $400, x
    sta $500, x
    sta $600, x
    sta $700, x
    inx
    bne @ram_reset_loop

    lda #$ef
@sprite_reset_loop:
    sta $200, x
    inx
    bne @sprite_reset_loop

    ;; NOTE: after sprites have been reset we can transfer data from PRG-ROM
    ;; into RAM so the rest of the code can assume that the data is there. This
    ;; is the main difference with the `basics/sprite.s` example.
    jsr transfer_to_chr_ram

    lda #$00
    sta $2003
    lda #$02
    sta $4014

@vblankwait2:
    bit $2002
    bpl @vblankwait2

    lda #$3F
    sta $2006
    lda #$00
    sta $2006

    lda #$0F
    ldx #$20
@palettes_reset_loop:
    sta $2007
    dex
    bne @palettes_reset_loop

    jmp main
.endproc

;;;
;; Transfer the CHR data from PRG-ROM into RAM.
.proc transfer_to_chr_ram
    ;;;
    ;; The code is pretty much taken from the NESDev wiki (see:
    ;; https://www.nesdev.org/wiki/CHR_ROM_vs._CHR_RAM).

    ;; First we set a 16-bit pointer that points `chr`, which contains the
    ;; actual data. This pointer will be stored at $00-$01, which is probably
    ;; not ideal, but this is just an example.
    lda #<chr
    sta $00
    lda #>chr
    sta $01

    ;; Load the destination address into the PPU. We have to initialize both
    ;; pattern tables, one starting at $0000, and the other at $1000. Hence we
    ;; start by simply pointing at PPU address $0000, and the loop will simply
    ;; fill both tables from here.
    ldy #0
    sty $2006
    sty $2006

    ;; - x contains the number of 256-byte pages to copy.
    ;; - y will index within the page ($00-$FF).
    ldx #32
@loop:
    ;; First part of the loop: copy each byte of the current page.
    lda ($00), y
    sta $2007
    iny
    bne @loop

    ;; Go to the next page and repeat the first part of the loop.
    inc $01
    dex
    bne @loop

    rts
.endproc

;;;
;; And from here on it's pretty much copied from `basics/sprite.s`.

.proc main
    jsr init_palettes
    jsr init_nametable
    jsr init_sprites

    cli
    lda #%10110000
    sta $2000
    lda #%00011110
    sta $2001

@main_game_loop:
    lda #%10000000
    ora $20
    sta $20
@wait_for_render:
    bit $20
    bmi @wait_for_render

    jmp @main_game_loop
.endproc

.proc init_palettes
    lda #$3F
    sta $2006
    lda #$00
    sta $2006

    ldx #0
@load_palettes_loop:
    lda palettes, x
    sta $2007
    inx
    cpx #$20
    bne @load_palettes_loop
    rts
palettes:
    .byte $0F, $12, $22, $32
    .byte $0F, $00, $28, $30
    .byte $0F, $28, $16, $2D
    .byte $0F, $28, $16, $2D

    .byte $0F, $00, $05, $30
    .byte $0F, $00, $00, $00
    .byte $0F, $00, $00, $00
    .byte $0F, $00, $00, $00
.endproc

.macro WRITE_PPU_DATA address, value
    bit $2002
    lda #.HIBYTE(address)
    sta $2006
    lda #.LOBYTE(address)
    sta $2006
    lda #value
    sta $2007
.endmacro

.proc init_nametable
    bit $2002

    WRITE_PPU_DATA $20C8, $02
    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
    WRITE_PPU_DATA $227A, $02

    WRITE_PPU_DATA $23CE, %00000001

    rts
.endproc

.proc init_sprites
    NUM_SPRITES = 2

    ldx #$00
@load_sprites_loop:
    lda initial_sprite_data, x
    sta $0200, x
    inx
    cpx #(4 * NUM_SPRITES)
    bne @load_sprites_loop
    rts
initial_sprite_data:
    .byte $B0, $00, %00000000, $7A
    .byte $B0, $00, %01000000, $82
.endproc

.proc nmi
    bit $20
    bpl @next

    pha
    txa
    pha
    tya
    pha

    lda #$00
    sta $2003
    lda #$02
    sta $4014

    bit $2002
    lda #$00
    sta $2005
    sta $2005

    lda #%01111111
    and $20
    sta $20

    pla
    tay
    pla
    tax
    pla
@next:
    rti
.endproc

.proc irq
    rti
.endproc