gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-lib/libc/targets/mos6502/nes/vram_transfer_buffer.s
1 ;
2 ; Routines for performing safe buffered writes via the vram transfer buffer.
3 ;
4 .include "global.s"
5
6 .title "VRAMBuffer"
7 .module VRAMBuffer
8
9 .include "mapper_macros.s"
10
11 ;
12 ; Format of transfer buffer
13 ;
14 ; 0: Data length
15 ; 1: 4 if inc-by-32, 0 if inc-by-1
16 ; 2: PPUADDR_HI
17 ; 3: PPUADDR_LO
18 ; 4: ...N data bytes...
19 ;
20 VRAM_HDR_SIZEOF = 4
21 VRAM_HDR_JMPADDR = 0
22 VRAM_HDR_DIRECTION = 1
23 VRAM_HDR_PPUHI = 2
24 VRAM_HDR_PPULO = 3
25 VRAM_MAX_BYTES = 32
26 VRAM_MAX_STRIPE_SIZE = VRAM_HDR_SIZEOF + VRAM_MAX_BYTES
27
28 ;
29 ; Locks the VRAM buffer for writing, causing the vblank handler to ignore it.
30 ;
31 .macro VRAM_BUFFER_LOCK
32 clc
33 ror *__vram_transfer_buffer_valid
34 .endm
35
36 ;
37 ; Unlocks the VRAM buffer for writing.
38 ;
39 .macro VRAM_BUFFER_UNLOCK
40 sec
41 ror *__vram_transfer_buffer_valid
42 .endm
43
44 ;
45 ; Converts data length into JMP address for unrolled loop
46 ;
47 ; jmp addr = 128-4*num_bytes + 1 = NOT(4*num_bytes)+1+128 + 1 = NOT(4*num_bytes)+130
48 ;
49 .macro LENGTH_TO_JMPADDR
50 asl
51 asl
52 eor #0xFF
53 adc #130
54 .endm
55
56 .area GBDKOVR (PAG, OVR)
57 _set_vram_byte_PARM_2:: .ds 1
58
59 .area _ZP (PAG)
60 __vram_transfer_buffer_valid:: .ds 1
61 __vram_transfer_buffer_num_cycles_x8:: .ds 1
62 __vram_transfer_buffer_pos_w:: .ds 1
63 __vram_transfer_buffer_pos_old:: .ds 1
64
65 .define __vram_transfer_buffer_temp "(REGTEMP+6)"
66 .define ppu_addr ".tmp"
67
68 .area _HOME
69
70 ;
71 ; Begin a horizontal stripe
72 ;
73 .ppu_stripe_begin_horizontal::
74 clc
75 bit *__oam_valid_display_on
76 bvc .ppu_stripe_begin_indirect
77 ; Direct write
78 stx PPUADDR
79 sta PPUADDR
80 .ifdef VRAM_MAPPER_CFG_TRANSFER
81 jsr __switch_win_addr_temp
82 .endif
83 ; Clear inc-by-32 bit in both PPUCTRL and _shadow_PPUCTRL, as NMI may re-write PPUCTRL
84 lda *_shadow_PPUCTRL
85 and #~PPUCTRL_INC32
86 sta *_shadow_PPUCTRL
87 sta PPUCTRL
88 rts
89
90 ;
91 ; Begin a vertical stripe
92 ;
93 .ppu_stripe_begin_vertical::
94 sec
95 bit *__oam_valid_display_on
96 bvc .ppu_stripe_begin_indirect
97 ; Direct write
98 stx PPUADDR
99 sta PPUADDR
100 .ifdef VRAM_MAPPER_CFG_TRANSFER
101 jsr __switch_win_addr_temp
102 .endif
103 ; Set inc-by-32 bit in both PPUCTRL and _shadow_PPUCTRL, as NMI may re-write PPUCTRL
104 lda *_shadow_PPUCTRL
105 ora #PPUCTRL_INC32
106 sta *_shadow_PPUCTRL
107 sta PPUCTRL
108 rts
109
110 .ppu_stripe_begin_indirect:
111 ; Indirect write via transfer buffer
112 sty *__vram_transfer_buffer_temp
113 pha
114 txa
115 pha
116 .ifdef NES_WINDOW_LAYER
117 lda *__current_vram_cfg_write
118 and #MAPPER_CFG_NT_MASK
119 ora .identity,x
120 sta *ppu_addr+1
121 tay
122 .endif
123 lda #0
124 rol
125 asl
126 asl
127 pha
128 ; Ensure there's at least VRAM_MAX_STRIPE_SIZE bytes remaining to write before progressing
129 ; This conservative limit simplifies conditions for rest of stripe in order to write single bytes with no checks
130 2$:
131 lda *__vram_transfer_buffer_pos_w
132 cmp #128-VRAM_MAX_STRIPE_SIZE
133 bcs 2$
134 .ifdef NES_WINDOW_LAYER
135 ldx *__vram_transfer_buffer_pos_w
136 ; if transfer buffer is empty, we must always re-initialize mapper with fresh write
137 beq 3$
138 ; otherwise check the previous address. if there's a difference in bank bits, we need to re-initialize mapper
139 ldx *__vram_transfer_buffer_pos_old
140 tya
141 eor __vram_transfer_buffer+VRAM_HDR_PPUHI,x
142 and #PPUHI_WIN
143 beq 4$
144 3$:
145 jsr .ppu_write_mapper_indirect
146 4$:
147 .endif
148 ; Lock buffer and store current write pointer for later
149 VRAM_BUFFER_LOCK
150 ldy *__vram_transfer_buffer_pos_w
151 sty *__vram_transfer_buffer_pos_old
152 ; Write direction
153 pla
154 sta __vram_transfer_buffer+VRAM_HDR_DIRECTION,y
155 ; Write address
156 pla
157 sta __vram_transfer_buffer+VRAM_HDR_PPUHI,y
158 pla
159 sta __vram_transfer_buffer+VRAM_HDR_PPULO,y
160 tya
161 clc
162 adc #VRAM_HDR_SIZEOF
163 sta *__vram_transfer_buffer_pos_w
164 ; __vram_transfer_buffer_num_cycles_x8 -= 5 (assumes carry clear)
165 lda *__vram_transfer_buffer_num_cycles_x8
166 sbc #4
167 sta *__vram_transfer_buffer_num_cycles_x8
168 ldy *__vram_transfer_buffer_temp
169 rts
170
171 ;
172 ; End a stripe (be it direct or via transfer buffer)
173 ;
174 .ppu_stripe_end::
175 bit *__oam_valid_display_on
176 bvc 1$
177 ; For direct writes there's nothing more to do
178 rts
179 1$:
180 .ppu_stripe_end_indirect:
181 sty *__vram_transfer_buffer_temp
182 ldy *__vram_transfer_buffer_pos_w
183 ; Write terminator byte
184 lda #0
185 sta __vram_transfer_buffer,y
186 ; Write number of data bytes
187 tya
188 sec
189 sbc *__vram_transfer_buffer_pos_old
190 sbc #VRAM_HDR_SIZEOF
191 pha
192 LENGTH_TO_JMPADDR
193 ldy *__vram_transfer_buffer_pos_old
194 sta __vram_transfer_buffer+VRAM_HDR_JMPADDR,y
195 pla
196 eor #0xFF
197 sec
198 adc *__vram_transfer_buffer_num_cycles_x8
199 sta *__vram_transfer_buffer_num_cycles_x8
200 VRAM_BUFFER_UNLOCK
201 ldy *__vram_transfer_buffer_temp
202 rts
203
204 ;
205 ; Writes a byte of an in-progress horizontal / vertical stripe (be it direct or via transfer buffer)
206 ;
207 .ppu_stripe_write_byte::
208 bit *__oam_valid_display_on
209 bvc 1$
210 sta PPUDATA ; Direct write
211 rts
212 1$:
213 .ppu_stripe_write_byte_indirect:
214 sty *__vram_transfer_buffer_temp
215 ldy *__vram_transfer_buffer_pos_w
216 sta __vram_transfer_buffer,y
217 inc *__vram_transfer_buffer_pos_w
218 ldy *__vram_transfer_buffer_temp
219 rts
220
221 ;
222 ; Appends a single byte to the last finished stripe when possible, temporarily re-opening it.
223 ;
224 ; The last stripe will only be re-used if it is possible to do so.
225 ; The following conditions must be true:
226 ; - The transfer buffer is not empty (a "last stripe" must exist in buffer)
227 ; - The last stripe's number of bytes isn't already VRAM_MAX_BYTES number of bytes
228 ; - The last stripe is either horizontal or single-byte, and the new byte's address is last_address+1
229 ; - The last stripe is either vertical or single-byte, and the next byte's address is last_address+32
230 ;
231 ; If any of these conditions aren't true, a new stripe will be created instead.
232 ;
233 ; The current version has a further simplification: The high byte of PPU address must be unchanged.
234 ; This simplification is there to avoid doing exhaustive and slow 16-bit comparisons.
235 ; This means a vertical stripe which is automatically appended to by this code can be no longer than 8 bytes.
236 ;
237 _set_vram_byte::
238 .ppu_stripe_append::
239 ;
240 bit *__oam_valid_display_on
241 bvc .ppu_stripe_append_indirect
242 ; Direct write
243 stx PPUADDR
244 sta PPUADDR
245 .ifdef VRAM_MAPPER_CFG_TRANSFER
246 jsr __switch_win_addr_temp
247 .endif
248 ldy *_set_vram_byte_PARM_2
249 sty PPUDATA
250 rts
251
252 .ppu_stripe_wait_for_flush:
253 ; Unlock and wait for flush by NMI handler
254 VRAM_BUFFER_UNLOCK
255 1$:
256 ldx *__vram_transfer_buffer_pos_w
257 bmi 1$
258 ; Re-lock buffer and jump straight to new-stripe code
259 VRAM_BUFFER_LOCK
260 jmp .ppu_stripe_append_failed
261
262 .ppu_stripe_append_indirect:
263 ; Indirect write via transfer buffer
264 sta *ppu_addr
265 stx *ppu_addr+1
266 ; Lock buffer first, to make sure our checks don't get invalidated by NMI
267 VRAM_BUFFER_LOCK
268 ; Now that buffer is safely locked, first check if it's almost full
269 ldx *__vram_transfer_buffer_pos_w
270 bmi .ppu_stripe_wait_for_flush
271 ; check that it's not empty
272 beq .ppu_stripe_append_failed
273 ldx *__vram_transfer_buffer_pos_old
274 ; Don't handle appending when high address changed
275 lda __vram_transfer_buffer+VRAM_HDR_PPUHI,x
276 cmp *ppu_addr+1
277 bne .ppu_stripe_append_failed
278 ; Don't handle appending if old size has reached VRAM_MAX_BYTES
279 lda __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
280 cmp #(128-4*VRAM_MAX_BYTES + 1)
281 beq .ppu_stripe_append_failed
282 ; if last stripe only contains a single byte, branch to go-either-direction routine
283 cmp #(128-4*1 + 1)
284 beq .ppu_stripe_append_second_byte
285 ; Test for horizontal stripe
286 lda __vram_transfer_buffer+VRAM_HDR_DIRECTION,x
287 bne 2$
288 ; Horizontal stripe - check if new address is 1*LENGTH
289 lda *ppu_addr
290 sec
291 sbc __vram_transfer_buffer+VRAM_HDR_PPULO,x
292 LENGTH_TO_JMPADDR
293 cmp __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
294 beq 0$
295 jmp .ppu_stripe_append_failed
296 0$:
297 ; Append byte
298 jmp .ppu_stripe_append_1byte
299 2$:
300 ; Vertical stripe - check if new address is 32*LENGTH
301 lda *ppu_addr
302 lsr
303 lsr
304 lsr
305 lsr
306 lsr
307 sec
308 sbc __vram_transfer_buffer+VRAM_HDR_PPULO,x
309 LENGTH_TO_JMPADDR
310 cmp __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
311 bne .ppu_stripe_append_failed
312 ; Append 1 byte
313 jmp .ppu_stripe_append_1byte
314
315 .ppu_stripe_append_failed:
316 .ifdef NES_WINDOW_LAYER
317 jsr .ppu_write_mapper_indirect
318 .endif
319 ; Just create a new stripe with a single byte
320 ldy *__vram_transfer_buffer_pos_w
321 sty *__vram_transfer_buffer_pos_old
322 ; Write direction (assume horizontal) and new terminator byte
323 lda #0
324 sta __vram_transfer_buffer+VRAM_HDR_DIRECTION,y
325 sta __vram_transfer_buffer+VRAM_HDR_SIZEOF+1,y
326 ; Write jmp addr
327 lda #(128-4*1 + 1)
328 sta __vram_transfer_buffer+VRAM_HDR_JMPADDR,y
329 ; Write address
330 lda *ppu_addr+1
331 sta __vram_transfer_buffer+VRAM_HDR_PPUHI,y
332 lda *ppu_addr
333 sta __vram_transfer_buffer+VRAM_HDR_PPULO,y
334 ; write data byte
335 lda *_set_vram_byte_PARM_2
336 sta __vram_transfer_buffer+VRAM_HDR_SIZEOF,y
337 ; Increase write pointer
338 tya
339 clc
340 adc #VRAM_HDR_SIZEOF+1
341 ; store new write pointer
342 sta *__vram_transfer_buffer_pos_w
343 ; __vram_transfer_buffer_num_cycles_x8 -= 6 (assumes carry clear)
344 lda *__vram_transfer_buffer_num_cycles_x8
345 sbc #5
346 sta *__vram_transfer_buffer_num_cycles_x8
347 VRAM_BUFFER_UNLOCK
348 ; Return PPU address
349 lda *ppu_addr
350 ldx *ppu_addr+1
351 rts
352
353 .ppu_stripe_append_second_byte:
354 ; We only have one previous byte, so ppu_addr being +1 or +32 would both work
355 lda *ppu_addr
356 sec
357 sbc __vram_transfer_buffer+VRAM_HDR_PPULO,x
358 cmp #1
359 bne 1$
360 ; Convert to horizontal stripe
361 lda #0
362 sta __vram_transfer_buffer+VRAM_HDR_DIRECTION,x
363 jmp .ppu_stripe_append_1byte
364 1$:
365 cmp #32
366 bne 2$
367 ; Convert to vertical stripe
368 lda #4
369 sta __vram_transfer_buffer+VRAM_HDR_DIRECTION,x
370 jmp .ppu_stripe_append_1byte
371 2$:
372 jmp .ppu_stripe_append_failed
373
374 .ppu_stripe_append_1byte:
375 ; Append 1 byte
376 dec __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
377 dec __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
378 dec __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
379 dec __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
380 lda *_set_vram_byte_PARM_2
381 ldx *__vram_transfer_buffer_pos_w
382 sta __vram_transfer_buffer,x
383 inx
384 ; Write terminator byte
385 lda #0
386 sta __vram_transfer_buffer,x
387 stx *__vram_transfer_buffer_pos_w
388 ; Decrease x8 cycle delay by 1
389 dec *__vram_transfer_buffer_num_cycles_x8
390 ; unlock buffer
391 VRAM_BUFFER_UNLOCK
392 ; Return PPU address
393 lda *ppu_addr
394 ldx *ppu_addr+1
395 rts
396
397 .ifdef NES_WINDOW_LAYER
398 .ppu_write_mapper:
399 clc
400 bit *__oam_valid_display_on
401 bvc .ppu_write_mapper_indirect
402 .ppu_write_mapper_direct:
403 ; Direct write
404 SWITCH_PRG0_A
405 rts
406
407 .ppu_write_mapper_indirect:
408 pha
409 ; Unlock buffer (if this routine was called while unlocked)
410 VRAM_BUFFER_UNLOCK
411 ; Ensure there's at least VRAM_MAX_STRIPE_SIZE bytes remaining to write before progressing
412 ; This conservative limit simplifies conditions for rest of stripe in order to write single bytes with no checks
413 2$:
414 lda *__vram_transfer_buffer_pos_w
415 cmp #128-VRAM_MAX_STRIPE_SIZE
416 bcs 2$
417 ; Lock buffer and store current write pointer for later
418 VRAM_BUFFER_LOCK
419 ldy *__vram_transfer_buffer_pos_w
420 sty *__vram_transfer_buffer_pos_old
421
422 ; Write direction (assume horizontal) and new terminator byte
423 lda #0
424 sta __vram_transfer_buffer+VRAM_HDR_DIRECTION,y
425 sta __vram_transfer_buffer+VRAM_HDR_SIZEOF+1,y
426 ; Write jmp addr
427 lda #<ProcessDrawList_mapper_switch
428 sta __vram_transfer_buffer+VRAM_HDR_JMPADDR,y
429 ; Write mapper value to PPUADDR_LO
430 lda *ppu_addr+1
431 ora *__current_vram_cfg_write
432 and #PPUHI_WIN
433 sta __vram_transfer_buffer+VRAM_HDR_PPULO,y
434
435 ; Increase write pointer
436 tya
437 clc
438 adc #VRAM_HDR_SIZEOF
439 ; store new write pointer
440 sta *__vram_transfer_buffer_pos_w
441
442 ; __vram_transfer_buffer_num_cycles_x8 -= 5 (assumes carry clear)
443 lda *__vram_transfer_buffer_num_cycles_x8
444 sbc #4
445 sta *__vram_transfer_buffer_num_cycles_x8
446 ldy *__vram_transfer_buffer_temp
447
448 dec *__vram_transfer_buffer_num_cycles_x8
449
450 VRAM_BUFFER_UNLOCK
451 pla
452 rts
453 .endif
454
455 .ifdef NES_WINDOW_LAYER
456 __switch_bkg::
457 lda *__current_vram_cfg_write
458 and #~PPUHI_WIN
459 sta *__current_vram_cfg_write
460 rts
461
462 __switch_win::
463 lda *__current_vram_cfg_write
464 ora #PPUHI_WIN
465 sta *__current_vram_cfg_write
466 rts
467 .endif
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.