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/deferred_isr.s
1 .include "global.s"
2
3 .area _HOME
4
5 .define .lcd_scanline_previous "REGTEMP"
6 .define .lcd_buf_index "REGTEMP+1"
7 .define .lcd_buf_end "REGTEMP+2"
8 .define .last_scanline_value
9
10 ;
11 ; Writes shadow registers to buffer
12 ;
13 ; Input:
14 ; X: Scanline number
15 ;
16 .write_shadow_registers_to_buffer::
17 inx
18 cpx #.SCREENHEIGHT
19 bcc 6$
20 txa
21 sec
22 sbc #.SCREENHEIGHT
23 tax
24 6$:
25 ; Copy shadow registers
26 ldy *.lcd_buf_index
27 .ifdef NES_WINDOW_LAYER
28 lda #0
29 sta __lcd_isr_mapper,y
30 .endif
31 lda *_shadow_PPUMASK
32 sta __lcd_isr_PPUMASK,y
33 lda *_shadow_PPUCTRL
34 sta __lcd_isr_PPUCTRL,y
35 .ifdef NES_WINDOW_LAYER
36 ; is WIN in front of BKG?
37 ; Never in front if not enabled
38 lda *__oam_valid_display_on
39 and #WINDOW_ON_MASK
40 beq 0$
41 ; always in front if _win_pos_y == 0
42 lda *_win_pos_y
43 beq 9$
44 ; in front if current_scanline >= win_pos_y
45 txa
46 cmp #.SCREENHEIGHT
47 bcc 7$
48 sbc #.SCREENHEIGHT
49 7$:
50 cmp *_win_pos_y
51 bcc 0$
52 9$:
53 ; Enable window
54 lda __lcd_isr_mapper,y
55 ora #PPUHI_WIN
56 sta __lcd_isr_mapper,y
57 ; Write window registers
58 lda #0
59 sec
60 sbc *_win_pos_x
61 sta __lcd_isr_scroll_x,y
62 lsr
63 lsr
64 lsr
65 sta __lcd_isr_ppuaddr_lo,y
66 txa
67 sec
68 sbc *_win_pos_y
69 cmp #.SCREENHEIGHT
70 bcc 8$
71 sbc #.SCREENHEIGHT
72 8$:
73 jmp 2$
74 .endif
75 0$:
76 lda *_bkg_scroll_x
77 sta __lcd_isr_scroll_x,y
78 lsr
79 lsr
80 lsr
81 sta __lcd_isr_ppuaddr_lo,y
82 ; Add _bkg_scroll_y to current scanline to generate final Y-scroll, with 239->0 wrap-around
83 txa
84 clc
85 adc *_bkg_scroll_y
86 bcc 1$
87 sbc #.SCREENHEIGHT
88 1$:
89 cmp #.SCREENHEIGHT
90 bcc 2$
91 sbc #.SCREENHEIGHT
92 2$:
93 sta __lcd_isr_scroll_y,y
94 and #0xF8
95 asl
96 asl
97 ora __lcd_isr_ppuaddr_lo,y
98 sta __lcd_isr_ppuaddr_lo,y
99 rts
100
101 ;
102 ; Resets the deferred ISR.
103 ;
104 ; This effectively omits the double-buffering delay and writes the shadow registers to the first buffer.
105 ; It is intended to be used when display is re-enabled after being turned off, to display with reasonable values.
106 ;
107 ; Note that in contrast to .run_deferred_isr_handlers, this will NOT actually run VBL / LCD handlers, so
108 ; can still cause glitches.
109 ;
110 .deferred_isr_reset::
111 ; Prepare VBL buffer data (need to start at scanline -1 = SCREENHEIGHT-1 for correct Y scroll)
112 lda #0
113 sta *__hblank_writes_index
114 sta *.lcd_buf_index
115 sta __lcd_isr_delay_num_scanlines+1
116 ldx #.SCREENHEIGHT-1
117 jmp .write_shadow_registers_to_buffer
118
119 ;
120 ; Resets the deferred ISR, then runs VBL and LCD handlers twice to initialize buffers with valid data.
121 ;
122 .deferred_isr_reset_and_init::
123 jsr .deferred_isr_reset
124 jsr .deferred_isr_run
125 jmp .deferred_isr_run
126
127 ;
128 ; Executes the deferred VBL/LCD handlers.
129 ;
130 ; After each ISR handler has run, PPU shadow registers are written to a buffer
131 ; which is consumed by the vblank NMI handler.
132 ; Double-buffering is used to avoid buffer locking / race conditions.
133 ;
134 .deferred_isr_run::
135 ; Save shadow registers that VBL or LCD isr could change
136 lda *_shadow_PPUMASK
137 pha
138 lda *_shadow_PPUCTRL
139 pha
140 lda *_bkg_scroll_x
141 pha
142 lda *_bkg_scroll_y
143 pha
144 .ifdef NES_WINDOW_LAYER
145 lda *_win_pos_x
146 pha
147 lda *_win_pos_y
148 pha
149 lda *__oam_valid_display_on
150 pha
151 .endif
152
153 ; Allow VBL isr to modify shadow registers if present
154 jsr .jmp_to_VBL_isr
155
156 ; Set initial scanline value
157 lda #0xFF
158 sta *.lcd_scanline_previous
159
160 lda *__hblank_writes_index
161 clc
162 adc #.MAX_DEFERRED_ISR_CALLS
163 cmp #(2*.MAX_DEFERRED_ISR_CALLS)
164 bcc 20$
165 lda #0
166 20$:
167 sta *.lcd_buf_index
168 clc
169 adc #.MAX_DEFERRED_ISR_CALLS
170 sta *.lcd_buf_end
171
172 ; Write shadow registers as first LCD buffer entry (actually VBL)
173 ldy *.lcd_buf_index
174 ldx #.SCREENHEIGHT-1
175 jsr .write_shadow_registers_to_buffer
176 iny
177 sty *.lcd_buf_index
178
179 ; Ensure second entry (actual LCD) starts off with zero (end-of-list)
180 lda #0
181 sta __lcd_isr_delay_num_scanlines,y
182 ; Skip to end if LCD isr functionality is disabled (0x60 = RTS means LCD isr disabled)
183 lda .jmp_to_LCD_isr
184 cmp #0x60
185 beq .deferred_isr_run_done
186
187 lda *.lcd_scanline_previous
188
189 jmp 2$
190 1$:
191 pla
192 sta *.lcd_scanline_previous
193 2$:
194 ; We are done if next scanline is <= the previous one
195 cmp #0xFF
196 beq 3$
197 cmp *__lcd_scanline
198 bcs .deferred_isr_run_done
199 3$:
200 ;
201 ldy *.lcd_buf_index
202 lda *__lcd_scanline
203 ; We are done if next LCD scanline >= SCREENHEIGHT
204 cmp #.SCREENHEIGHT
205 bcs .deferred_isr_run_done
206 pha
207
208 .ifdef NES_WINDOW_LAYER
209 ldx *__lcd_scanline
210 jsr .deferred_isr_handle_window
211 pla
212 txa
213 pha
214 .endif
215
216 sec
217 sbc *.lcd_scanline_previous
218 sta __lcd_isr_delay_num_scanlines,y
219 ; Call LCD isr
220 jsr .jmp_to_LCD_isr
221 ; Grab LCD scanline value from stack and store in X
222 pla
223 tax
224 pha
225 jsr .write_shadow_registers_to_buffer
226
227 30$:
228 iny
229 sty *.lcd_buf_index
230 cpy *.lcd_buf_end
231 bne 1$
232
233 ; Clear last-scanline-value from stack
234 pla
235
236 .deferred_isr_run_done:
237
238 .ifdef NES_WINDOW_LAYER
239 ldx #.SCREENHEIGHT
240 jsr .deferred_isr_handle_window
241 .endif
242
243 ; Flip __hblank_writes_index for NMI handler
244 ldy #0
245 lda *__hblank_writes_index
246 bne 21$
247 ldy #.MAX_DEFERRED_ISR_CALLS
248 21$:
249 sty *__hblank_writes_index
250
251 .ifdef NES_WINDOW_LAYER
252 pla
253 sta *__oam_valid_display_on
254 pla
255 sta *_win_pos_y
256 pla
257 sta *_win_pos_x
258 .endif
259 pla
260 sta *_bkg_scroll_y
261 pla
262 sta *_bkg_scroll_x
263 pla
264 sta *_shadow_PPUCTRL
265 pla
266 sta *_shadow_PPUMASK
267
268 rts
269
270 .ifdef NES_WINDOW_LAYER
271 ;
272 ; Injects an extra scanline for window switch
273 ;
274 .deferred_isr_handle_window:
275 ;;;
276 ; If window is enabled and WY_REG is smaller than next LCD scanline, inject a deferred ISR for Window start / finish
277 lda *__oam_valid_display_on
278 and #WINDOW_ON_MASK
279 beq 21$
280 txa
281 cmp *_win_pos_y
282 bcs 22$
283 ; lcd_scanline_next < win_pos_y
284 jmp 21$
285 22$:
286 ; lcd_scanline_next >= win_pos_y -> win_pos_y < lcd_scanline_next
287 lda *_win_pos_y
288 beq 21$
289 sec
290 sbc *.lcd_scanline_previous
291 sta __lcd_isr_delay_num_scanlines,y
292
293 ; Make window scroll Y coordinate source data from top of nametable
294 ldx *_win_pos_y
295 dex
296 jsr .write_shadow_registers_to_buffer
297
298 ; Grab LCD scanline value from win_pos_y and store in X
299 dex
300 iny
301 sty *.lcd_buf_index
302 ; TODO: Check to not overflow deferred ISR buffer
303 21$:
304 rts
305 .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.