pavone@467
|
1 /*
|
pavone@467
|
2 Copyright 2013 Michael Pavone
|
pavone@470
|
3 This file is part of BlastEm.
|
pavone@467
|
4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
|
pavone@467
|
5 */
|
pavone@20
|
6 #include "vdp.h"
|
pavone@75
|
7 #include "blastem.h"
|
pavone@20
|
8 #include <stdlib.h>
|
pavone@20
|
9 #include <string.h>
|
pavone@426
|
10 #include "render.h"
|
pavone@20
|
11
|
pavone@622
|
12 #define NTSC_INACTIVE_START 224
|
pavone@622
|
13 #define PAL_INACTIVE_START 240
|
pavone@20
|
14 #define BUF_BIT_PRIORITY 0x40
|
pavone@20
|
15 #define MAP_BIT_PRIORITY 0x8000
|
pavone@20
|
16 #define MAP_BIT_H_FLIP 0x800
|
pavone@20
|
17 #define MAP_BIT_V_FLIP 0x1000
|
pavone@20
|
18
|
pavone@39
|
19 #define SCROLL_BUFFER_SIZE 32
|
pavone@436
|
20 #define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1)
|
pavone@436
|
21 #define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2)
|
pavone@39
|
22
|
pavone@328
|
23 #define MCLKS_SLOT_H40 16
|
pavone@328
|
24 #define MCLKS_SLOT_H32 20
|
pavone@622
|
25 #define VINT_SLOT_H40 4 //21 slots before HSYNC, 16 during, 10 after
|
pavone@711
|
26 #define VINT_SLOT_H32 4 //old value was 23, but recent tests suggest the actual value is close to the H40 one
|
pavone@922
|
27 #define HSYNC_SLOT_H40 228
|
pavone@697
|
28 #define HSYNC_END_H40 (HSYNC_SLOT_H40+17)
|
pavone@331
|
29 #define HSYNC_END_H32 (33 * MCLKS_SLOT_H32)
|
pavone@647
|
30 #define HBLANK_START_H40 178 //should be 179 according to Nemesis, but 178 seems to fit slightly better with my test ROM results
|
pavone@647
|
31 #define HBLANK_END_H40 0 //should be 5.5 according to Nemesis, but 0 seems to fit better with my test ROM results
|
pavone@647
|
32 #define HBLANK_START_H32 233 //should be 147 according to Nemesis which is very different from my test ROM result
|
pavone@647
|
33 #define HBLANK_END_H32 0 //should be 5 according to Nemesis, but 0 seems to fit better with my test ROM results
|
pavone@647
|
34 #define LINE_CHANGE_H40 165
|
pavone@647
|
35 #define LINE_CHANGE_H32 132
|
pavone@717
|
36 #define VBLANK_START_H40 (LINE_CHANGE_H40+2)
|
pavone@717
|
37 #define VBLANK_START_H32 (LINE_CHANGE_H32+2)
|
pavone@460
|
38 #define FIFO_LATENCY 3
|
pavone@328
|
39
|
pavone@426
|
40 int32_t color_map[1 << 12];
|
pavone@426
|
41 uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255};
|
pavone@426
|
42
|
pavone@437
|
43 uint8_t debug_base[][3] = {
|
pavone@437
|
44 {127, 127, 127}, //BG
|
pavone@437
|
45 {0, 0, 127}, //A
|
pavone@437
|
46 {127, 0, 0}, //Window
|
pavone@437
|
47 {0, 127, 0}, //B
|
pavone@437
|
48 {127, 0, 127} //Sprites
|
pavone@437
|
49 };
|
pavone@437
|
50
|
pavone@426
|
51 uint8_t color_map_init_done;
|
pavone@426
|
52
|
pavone@623
|
53 void init_vdp_context(vdp_context * context, uint8_t region_pal)
|
pavone@20
|
54 {
|
pavone@58
|
55 memset(context, 0, sizeof(*context));
|
pavone@20
|
56 context->vdpmem = malloc(VRAM_SIZE);
|
pavone@58
|
57 memset(context->vdpmem, 0, VRAM_SIZE);
|
pavone@505
|
58 /*
|
pavone@488
|
59 */
|
pavone@505
|
60 if (headless) {
|
pavone@505
|
61 context->oddbuf = context->framebuf = malloc(FRAMEBUF_ENTRIES * (32 / 8));
|
pavone@505
|
62 memset(context->framebuf, 0, FRAMEBUF_ENTRIES * (32 / 8));
|
pavone@505
|
63 context->evenbuf = malloc(FRAMEBUF_ENTRIES * (32 / 8));
|
pavone@505
|
64 memset(context->evenbuf, 0, FRAMEBUF_ENTRIES * (32 / 8));
|
pavone@505
|
65 } else {
|
pavone@505
|
66 render_alloc_surfaces(context);
|
pavone@505
|
67 }
|
pavone@488
|
68 context->framebuf = context->oddbuf;
|
pavone@39
|
69 context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
|
pavone@54
|
70 memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
|
pavone@20
|
71 context->tmp_buf_a = context->linebuf + LINEBUF_SIZE;
|
pavone@39
|
72 context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE;
|
pavone@26
|
73 context->sprite_draws = MAX_DRAWS;
|
pavone@471
|
74 context->fifo_write = 0;
|
pavone@471
|
75 context->fifo_read = -1;
|
pavone@505
|
76
|
pavone@426
|
77 if (!color_map_init_done) {
|
pavone@426
|
78 uint8_t b,g,r;
|
pavone@426
|
79 for (uint16_t color = 0; color < (1 << 12); color++) {
|
pavone@426
|
80 if (color & FBUF_SHADOW) {
|
pavone@426
|
81 b = levels[(color >> 9) & 0x7];
|
pavone@426
|
82 g = levels[(color >> 5) & 0x7];
|
pavone@426
|
83 r = levels[(color >> 1) & 0x7];
|
pavone@426
|
84 } else if(color & FBUF_HILIGHT) {
|
pavone@426
|
85 b = levels[((color >> 9) & 0x7) + 7];
|
pavone@426
|
86 g = levels[((color >> 5) & 0x7) + 7];
|
pavone@426
|
87 r = levels[((color >> 1) & 0x7) + 7];
|
pavone@426
|
88 } else {
|
pavone@426
|
89 b = levels[(color >> 8) & 0xE];
|
pavone@426
|
90 g = levels[(color >> 4) & 0xE];
|
pavone@426
|
91 r = levels[color & 0xE];
|
pavone@426
|
92 }
|
pavone@426
|
93 color_map[color] = render_map_color(r, g, b);
|
pavone@426
|
94 }
|
pavone@426
|
95 color_map_init_done = 1;
|
pavone@426
|
96 }
|
pavone@437
|
97 for (uint8_t color = 0; color < (1 << (3 + 1 + 1 + 1)); color++)
|
pavone@437
|
98 {
|
pavone@437
|
99 uint8_t src = color & DBG_SRC_MASK;
|
pavone@437
|
100 if (src > DBG_SRC_S) {
|
pavone@437
|
101 context->debugcolors[color] = 0;
|
pavone@437
|
102 } else {
|
pavone@437
|
103 uint8_t r,g,b;
|
pavone@437
|
104 b = debug_base[src][0];
|
pavone@437
|
105 g = debug_base[src][1];
|
pavone@437
|
106 r = debug_base[src][2];
|
pavone@437
|
107 if (color & DBG_PRIORITY)
|
pavone@437
|
108 {
|
pavone@437
|
109 if (b) {
|
pavone@437
|
110 b += 48;
|
pavone@437
|
111 }
|
pavone@437
|
112 if (g) {
|
pavone@437
|
113 g += 48;
|
pavone@437
|
114 }
|
pavone@437
|
115 if (r) {
|
pavone@437
|
116 r += 48;
|
pavone@437
|
117 }
|
pavone@437
|
118 }
|
pavone@437
|
119 if (color & DBG_SHADOW) {
|
pavone@437
|
120 b /= 2;
|
pavone@437
|
121 g /= 2;
|
pavone@437
|
122 r /=2 ;
|
pavone@437
|
123 }
|
pavone@437
|
124 if (color & DBG_HILIGHT) {
|
pavone@437
|
125 if (b) {
|
pavone@437
|
126 b += 72;
|
pavone@437
|
127 }
|
pavone@437
|
128 if (g) {
|
pavone@437
|
129 g += 72;
|
pavone@437
|
130 }
|
pavone@437
|
131 if (r) {
|
pavone@437
|
132 r += 72;
|
pavone@437
|
133 }
|
pavone@437
|
134 }
|
pavone@437
|
135 context->debugcolors[color] = render_map_color(r, g, b);
|
pavone@437
|
136 }
|
pavone@437
|
137 }
|
pavone@623
|
138 if (region_pal) {
|
pavone@623
|
139 context->flags2 |= FLAG2_REGION_PAL;
|
pavone@623
|
140 }
|
pavone@20
|
141 }
|
pavone@20
|
142
|
pavone@884
|
143 void vdp_free(vdp_context *context)
|
pavone@884
|
144 {
|
pavone@884
|
145 free(context->vdpmem);
|
pavone@884
|
146 free(context->linebuf);
|
pavone@884
|
147 if (headless) {
|
pavone@884
|
148 free(context->oddbuf);
|
pavone@884
|
149 free(context->evenbuf);
|
pavone@884
|
150 } else {
|
pavone@884
|
151 render_free_surfaces(context);
|
pavone@884
|
152 }
|
pavone@884
|
153 free(context);
|
pavone@884
|
154 }
|
pavone@884
|
155
|
pavone@460
|
156 int is_refresh(vdp_context * context, uint32_t slot)
|
pavone@460
|
157 {
|
pavone@622
|
158 if (context->regs[REG_MODE_4] & BIT_H40) {
|
pavone@622
|
159 return slot == 250 || slot == 26 || slot == 59 || slot == 90 || slot == 122 || slot == 154;
|
pavone@460
|
160 } else {
|
pavone@460
|
161 //TODO: Figure out which slots are refresh when display is off in 32-cell mode
|
pavone@460
|
162 //These numbers are guesses based on H40 numbers
|
pavone@622
|
163 return slot == 243 || slot == 19 || slot == 51 || slot == 83 || slot == 115;
|
pavone@460
|
164 //The numbers below are the refresh slots during active display
|
pavone@622
|
165 //return (slot == 29 || slot == 61 || slot == 93 || slot == 125);
|
pavone@460
|
166 }
|
pavone@460
|
167 }
|
pavone@460
|
168
|
pavone@21
|
169 void render_sprite_cells(vdp_context * context)
|
pavone@20
|
170 {
|
pavone@21
|
171 if (context->cur_slot >= context->sprite_draws) {
|
pavone@21
|
172 sprite_draw * d = context->sprite_draw_list + context->cur_slot;
|
pavone@450
|
173
|
pavone@20
|
174 uint16_t dir;
|
pavone@20
|
175 int16_t x;
|
pavone@20
|
176 if (d->h_flip) {
|
pavone@20
|
177 x = d->x_pos + 7;
|
pavone@20
|
178 dir = -1;
|
pavone@20
|
179 } else {
|
pavone@20
|
180 x = d->x_pos;
|
pavone@20
|
181 dir = 1;
|
pavone@20
|
182 }
|
pavone@27
|
183 //printf("Draw Slot %d of %d, Rendering sprite cell from %X to x: %d\n", context->cur_slot, context->sprite_draws, d->address, x);
|
pavone@26
|
184 context->cur_slot--;
|
pavone@143
|
185 for (uint16_t address = d->address; address != ((d->address+4) & 0xFFFF); address++) {
|
pavone@27
|
186 if (x >= 0 && x < 320 && !(context->linebuf[x] & 0xF)) {
|
pavone@494
|
187 if (context->linebuf[x] && (context->vdpmem[address] >> 4)) {
|
pavone@494
|
188 context->flags2 |= FLAG2_SPRITE_COLLIDE;
|
pavone@494
|
189 }
|
pavone@20
|
190 context->linebuf[x] = (context->vdpmem[address] >> 4) | d->pal_priority;
|
pavone@20
|
191 }
|
pavone@20
|
192 x += dir;
|
pavone@27
|
193 if (x >= 0 && x < 320 && !(context->linebuf[x] & 0xF)) {
|
pavone@494
|
194 if (context->linebuf[x] && (context->vdpmem[address] & 0xF)) {
|
pavone@494
|
195 context->flags2 |= FLAG2_SPRITE_COLLIDE;
|
pavone@494
|
196 }
|
pavone@20
|
197 context->linebuf[x] = (context->vdpmem[address] & 0xF) | d->pal_priority;
|
pavone@20
|
198 }
|
pavone@20
|
199 x += dir;
|
pavone@20
|
200 }
|
pavone@20
|
201 }
|
pavone@20
|
202 }
|
pavone@20
|
203
|
pavone@322
|
204 void vdp_print_sprite_table(vdp_context * context)
|
pavone@322
|
205 {
|
pavone@322
|
206 uint16_t sat_address = (context->regs[REG_SAT] & 0x7F) << 9;
|
pavone@322
|
207 uint16_t current_index = 0;
|
pavone@322
|
208 uint8_t count = 0;
|
pavone@322
|
209 do {
|
pavone@322
|
210 uint16_t address = current_index * 8 + sat_address;
|
pavone@322
|
211 uint8_t height = ((context->vdpmem[address+2] & 0x3) + 1) * 8;
|
pavone@322
|
212 uint8_t width = (((context->vdpmem[address+2] >> 2) & 0x3) + 1) * 8;
|
pavone@322
|
213 int16_t y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & 0x1FF;
|
pavone@323
|
214 int16_t x = ((context->vdpmem[address+ 6] & 0x3) << 8 | context->vdpmem[address + 7]) & 0x1FF;
|
pavone@322
|
215 uint16_t link = context->vdpmem[address+3] & 0x7F;
|
pavone@323
|
216 uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3;
|
pavone@323
|
217 uint8_t pri = context->vdpmem[address + 4] >> 7;
|
pavone@450
|
218 uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5;
|
pavone@515
|
219 printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern);
|
pavone@322
|
220 current_index = link;
|
pavone@322
|
221 count++;
|
pavone@322
|
222 } while (current_index != 0 && count < 80);
|
pavone@322
|
223 }
|
pavone@322
|
224
|
pavone@705
|
225 #define VRAM_READ 0 //0000
|
pavone@705
|
226 #define VRAM_WRITE 1 //0001
|
pavone@705
|
227 //2 would trigger register write 0010
|
pavone@705
|
228 #define CRAM_WRITE 3 //0011
|
pavone@705
|
229 #define VSRAM_READ 4 //0100
|
pavone@705
|
230 #define VSRAM_WRITE 5//0101
|
pavone@705
|
231 //6 would trigger regsiter write 0110
|
pavone@980
|
232 //7 is a mystery //0111
|
pavone@705
|
233 #define CRAM_READ 8 //1000
|
pavone@705
|
234 //9 is also a mystery //1001
|
pavone@705
|
235 //A would trigger register write 1010
|
pavone@705
|
236 //B is a mystery 1011
|
pavone@705
|
237 #define VRAM_READ8 0xC //1100
|
pavone@705
|
238 //D is a mystery 1101
|
pavone@705
|
239 //E would trigger register write 1110
|
pavone@705
|
240 //F is a mystery 1111
|
pavone@980
|
241
|
pavone@980
|
242 //Possible theory on how bits work
|
pavone@980
|
243 //CD0 = Read/Write flag
|
pavone@980
|
244 //CD2,(CD1|CD3) = RAM type
|
pavone@980
|
245 // 00 = VRAM
|
pavone@980
|
246 // 01 = CRAM
|
pavone@980
|
247 // 10 = VSRAM
|
pavone@980
|
248 // 11 = VRAM8
|
pavone@980
|
249 //Would result in
|
pavone@980
|
250 // 7 = VRAM8 write
|
pavone@980
|
251 // 9 = CRAM write alias
|
pavone@980
|
252 // B = CRAM write alias
|
pavone@980
|
253 // D = VRAM8 write alias
|
pavone@980
|
254 // F = VRAM8 write alais
|
pavone@980
|
255
|
pavone@705
|
256 #define DMA_START 0x20
|
pavone@705
|
257
|
pavone@705
|
258 const char * cd_name(uint8_t cd)
|
pavone@705
|
259 {
|
pavone@705
|
260 switch (cd & 0xF)
|
pavone@705
|
261 {
|
pavone@705
|
262 case VRAM_READ:
|
pavone@705
|
263 return "VRAM read";
|
pavone@705
|
264 case VRAM_WRITE:
|
pavone@705
|
265 return "VRAM write";
|
pavone@705
|
266 case CRAM_WRITE:
|
pavone@705
|
267 return "CRAM write";
|
pavone@705
|
268 case VSRAM_READ:
|
pavone@705
|
269 return "VSRAM read";
|
pavone@705
|
270 case VSRAM_WRITE:
|
pavone@705
|
271 return "VSRAM write";
|
pavone@705
|
272 case VRAM_READ8:
|
pavone@705
|
273 return "VRAM read (undocumented 8-bit mode)";
|
pavone@705
|
274 default:
|
pavone@705
|
275 return "invalid";
|
pavone@705
|
276 }
|
pavone@705
|
277 }
|
pavone@705
|
278
|
pavone@327
|
279 void vdp_print_reg_explain(vdp_context * context)
|
pavone@327
|
280 {
|
pavone@327
|
281 char * hscroll[] = {"full", "7-line", "cell", "line"};
|
pavone@327
|
282 printf("**Mode Group**\n"
|
pavone@327
|
283 "00: %.2X | H-ints %s, Pal Select %d, HVC latch %s, Display gen %s\n"
|
pavone@327
|
284 "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d\n"
|
pavone@327
|
285 "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n"
|
pavone@327
|
286 "0C: %.2X | Width: %d, Shadow/Highlight: %s\n",
|
pavone@757
|
287 context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", (context->regs[REG_MODE_1] & BIT_PAL_SEL) != 0,
|
pavone@327
|
288 context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled",
|
pavone@450
|
289 context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled",
|
pavone@327
|
290 context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4,
|
pavone@327
|
291 context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full",
|
pavone@327
|
292 hscroll[context->regs[REG_MODE_3] & 0x3],
|
pavone@327
|
293 context->regs[REG_MODE_4], context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32, context->regs[REG_MODE_4] & BIT_HILIGHT ? "enabled" : "disabled");
|
pavone@327
|
294 printf("\n**Table Group**\n"
|
pavone@327
|
295 "02: %.2X | Scroll A Name Table: $%.4X\n"
|
pavone@327
|
296 "03: %.2X | Window Name Table: $%.4X\n"
|
pavone@327
|
297 "04: %.2X | Scroll B Name Table: $%.4X\n"
|
pavone@327
|
298 "05: %.2X | Sprite Attribute Table: $%.4X\n"
|
pavone@327
|
299 "0D: %.2X | HScroll Data Table: $%.4X\n",
|
pavone@327
|
300 context->regs[REG_SCROLL_A], (context->regs[REG_SCROLL_A] & 0x38) << 10,
|
pavone@327
|
301 context->regs[REG_WINDOW], (context->regs[REG_WINDOW] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x3C : 0x3E)) << 10,
|
pavone@327
|
302 context->regs[REG_SCROLL_B], (context->regs[REG_SCROLL_B] & 0x7) << 13,
|
pavone@621
|
303 context->regs[REG_SAT], (context->regs[REG_SAT] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x7E : 0x7F)) << 9,
|
pavone@621
|
304 context->regs[REG_HSCROLL], (context->regs[REG_HSCROLL] & 0x3F) << 10);
|
pavone@327
|
305 char * sizes[] = {"32", "64", "invalid", "128"};
|
pavone@327
|
306 printf("\n**Misc Group**\n"
|
pavone@327
|
307 "07: %.2X | Backdrop Color: $%X\n"
|
pavone@327
|
308 "0A: %.2X | H-Int Counter: %u\n"
|
pavone@327
|
309 "0F: %.2X | Auto-increment: $%X\n"
|
pavone@327
|
310 "10: %.2X | Scroll A/B Size: %sx%s\n",
|
pavone@505
|
311 context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR],
|
pavone@450
|
312 context->regs[REG_HINT], context->regs[REG_HINT],
|
pavone@327
|
313 context->regs[REG_AUTOINC], context->regs[REG_AUTOINC],
|
pavone@327
|
314 context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]);
|
pavone@621
|
315 char * src_types[] = {"68K", "68K", "Copy", "Fill"};
|
pavone@621
|
316 printf("\n**DMA Group**\n"
|
pavone@621
|
317 "13: %.2X |\n"
|
pavone@621
|
318 "14: %.2X | DMA Length: $%.4X words\n"
|
pavone@621
|
319 "15: %.2X |\n"
|
pavone@621
|
320 "16: %.2X |\n"
|
pavone@621
|
321 "17: %.2X | DMA Source Address: $%.6X, Type: %s\n",
|
pavone@621
|
322 context->regs[REG_DMALEN_L],
|
pavone@621
|
323 context->regs[REG_DMALEN_H], context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L],
|
pavone@621
|
324 context->regs[REG_DMASRC_L],
|
pavone@621
|
325 context->regs[REG_DMASRC_M],
|
pavone@621
|
326 context->regs[REG_DMASRC_H],
|
pavone@629
|
327 context->regs[REG_DMASRC_H] << 17 | context->regs[REG_DMASRC_M] << 9 | context->regs[REG_DMASRC_L] << 1,
|
pavone@621
|
328 src_types[context->regs[REG_DMASRC_H] >> 6 & 3]);
|
pavone@438
|
329 printf("\n**Internal Group**\n"
|
pavone@438
|
330 "Address: %X\n"
|
pavone@705
|
331 "CD: %X - %s\n"
|
pavone@647
|
332 "Pending: %s\n"
|
pavone@647
|
333 "VCounter: %d\n"
|
pavone@647
|
334 "HCounter: %d\n",
|
pavone@705
|
335 context->address, context->cd, cd_name(context->cd), (context->flags & FLAG_PENDING) ? "true" : "false",
|
pavone@647
|
336 context->vcounter, context->hslot*2);
|
pavone@450
|
337
|
pavone@450
|
338 //TODO: Window Group, DMA Group
|
pavone@327
|
339 }
|
pavone@327
|
340
|
pavone@20
|
341 void scan_sprite_table(uint32_t line, vdp_context * context)
|
pavone@20
|
342 {
|
pavone@20
|
343 if (context->sprite_index && context->slot_counter) {
|
pavone@20
|
344 line += 1;
|
pavone@20
|
345 line &= 0xFF;
|
pavone@413
|
346 uint16_t ymask, ymin;
|
pavone@413
|
347 uint8_t height_mult;
|
pavone@413
|
348 if (context->double_res) {
|
pavone@413
|
349 line *= 2;
|
pavone@413
|
350 if (context->framebuf != context->oddbuf) {
|
pavone@413
|
351 line++;
|
pavone@413
|
352 }
|
pavone@413
|
353 ymask = 0x3FF;
|
pavone@413
|
354 ymin = 256;
|
pavone@413
|
355 height_mult = 16;
|
pavone@413
|
356 } else {
|
pavone@413
|
357 ymask = 0x1FF;
|
pavone@413
|
358 ymin = 128;
|
pavone@413
|
359 height_mult = 8;
|
pavone@413
|
360 }
|
pavone@20
|
361 context->sprite_index &= 0x7F;
|
pavone@622
|
362 if (context->regs[REG_MODE_4] & BIT_H40) {
|
pavone@38
|
363 if (context->sprite_index >= MAX_SPRITES_FRAME) {
|
pavone@38
|
364 context->sprite_index = 0;
|
pavone@38
|
365 return;
|
pavone@38
|
366 }
|
pavone@38
|
367 } else if(context->sprite_index >= MAX_SPRITES_FRAME_H32) {
|
pavone@38
|
368 context->sprite_index = 0;
|
pavone@38
|
369 return;
|
pavone@38
|
370 }
|
pavone@20
|
371 //TODO: Read from SAT cache rather than from VRAM
|
pavone@20
|
372 uint16_t sat_address = (context->regs[REG_SAT] & 0x7F) << 9;
|
pavone@20
|
373 uint16_t address = context->sprite_index * 8 + sat_address;
|
pavone@413
|
374 line += ymin;
|
pavone@415
|
375 uint16_t y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & ymask;
|
pavone@413
|
376 uint8_t height = ((context->vdpmem[address+2] & 0x3) + 1) * height_mult;
|
pavone@21
|
377 //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
|
pavone@21
|
378 if (y <= line && line < (y + height)) {
|
pavone@27
|
379 //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
|
pavone@20
|
380 context->sprite_info_list[--(context->slot_counter)].size = context->vdpmem[address+2];
|
pavone@20
|
381 context->sprite_info_list[context->slot_counter].index = context->sprite_index;
|
pavone@413
|
382 context->sprite_info_list[context->slot_counter].y = y-ymin;
|
pavone@20
|
383 }
|
pavone@20
|
384 context->sprite_index = context->vdpmem[address+3] & 0x7F;
|
pavone@20
|
385 if (context->sprite_index && context->slot_counter)
|
pavone@20
|
386 {
|
pavone@20
|
387 address = context->sprite_index * 8 + sat_address;
|
pavone@415
|
388 y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & ymask;
|
pavone@413
|
389 height = ((context->vdpmem[address+2] & 0x3) + 1) * height_mult;
|
pavone@323
|
390 //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height);
|
pavone@21
|
391 if (y <= line && line < (y + height)) {
|
pavone@27
|
392 //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line);
|
pavone@20
|
393 context->sprite_info_list[--(context->slot_counter)].size = context->vdpmem[address+2];
|
pavone@20
|
394 context->sprite_info_list[context->slot_counter].index = context->sprite_index;
|
pavone@413
|
395 context->sprite_info_list[context->slot_counter].y = y-ymin;
|
pavone@20
|
396 }
|
pavone@21
|
397 context->sprite_index = context->vdpmem[address+3] & 0x7F;
|
pavone@20
|
398 }
|
pavone@20
|
399 }
|
pavone@20
|
400 }
|
pavone@20
|
401
|
pavone@20
|
402 void read_sprite_x(uint32_t line, vdp_context * context)
|
pavone@20
|
403 {
|
pavone@34
|
404 if (context->cur_slot >= context->slot_counter) {
|
pavone@34
|
405 if (context->sprite_draws) {
|
pavone@34
|
406 line += 1;
|
pavone@34
|
407 line &= 0xFF;
|
pavone@34
|
408 //in tiles
|
pavone@34
|
409 uint8_t width = ((context->sprite_info_list[context->cur_slot].size >> 2) & 0x3) + 1;
|
pavone@34
|
410 //in pixels
|
pavone@34
|
411 uint8_t height = ((context->sprite_info_list[context->cur_slot].size & 0x3) + 1) * 8;
|
pavone@413
|
412 if (context->double_res) {
|
pavone@413
|
413 line *= 2;
|
pavone@413
|
414 if (context->framebuf != context->oddbuf) {
|
pavone@413
|
415 line++;
|
pavone@413
|
416 }
|
pavone@413
|
417 height *= 2;
|
pavone@413
|
418 }
|
pavone@34
|
419 uint16_t att_addr = ((context->regs[REG_SAT] & 0x7F) << 9) + context->sprite_info_list[context->cur_slot].index * 8 + 4;
|
pavone@450
|
420 uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1];
|
pavone@34
|
421 uint8_t pal_priority = (tileinfo >> 9) & 0x70;
|
pavone@34
|
422 uint8_t row;
|
pavone@34
|
423 if (tileinfo & MAP_BIT_V_FLIP) {
|
pavone@34
|
424 row = (context->sprite_info_list[context->cur_slot].y + height - 1) - line;
|
pavone@34
|
425 } else {
|
pavone@34
|
426 row = line-context->sprite_info_list[context->cur_slot].y;
|
pavone@34
|
427 }
|
pavone@413
|
428 uint16_t address;
|
pavone@413
|
429 if (context->double_res) {
|
pavone@413
|
430 address = ((tileinfo & 0x3FF) << 6) + row * 4;
|
pavone@413
|
431 } else {
|
pavone@413
|
432 address = ((tileinfo & 0x7FF) << 5) + row * 4;
|
pavone@413
|
433 }
|
pavone@323
|
434 int16_t x = ((context->vdpmem[att_addr+ 2] & 0x3) << 8 | context->vdpmem[att_addr + 3]) & 0x1FF;
|
pavone@36
|
435 if (x) {
|
pavone@36
|
436 context->flags |= FLAG_CAN_MASK;
|
pavone@36
|
437 } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) {
|
pavone@36
|
438 context->flags |= FLAG_MASKED;
|
pavone@36
|
439 }
|
pavone@450
|
440
|
pavone@36
|
441 context->flags &= ~FLAG_DOT_OFLOW;
|
pavone@36
|
442 int16_t i;
|
pavone@36
|
443 if (context->flags & FLAG_MASKED) {
|
pavone@36
|
444 for (i=0; i < width && context->sprite_draws; i++) {
|
pavone@36
|
445 --context->sprite_draws;
|
pavone@36
|
446 context->sprite_draw_list[context->sprite_draws].x_pos = -128;
|
pavone@34
|
447 }
|
pavone@36
|
448 } else {
|
pavone@34
|
449 x -= 128;
|
pavone@34
|
450 int16_t base_x = x;
|
pavone@34
|
451 int16_t dir;
|
pavone@34
|
452 if (tileinfo & MAP_BIT_H_FLIP) {
|
pavone@34
|
453 x += (width-1) * 8;
|
pavone@34
|
454 dir = -8;
|
pavone@34
|
455 } else {
|
pavone@34
|
456 dir = 8;
|
pavone@34
|
457 }
|
pavone@34
|
458 //printf("Sprite %d | x: %d, y: %d, width: %d, height: %d, pal_priority: %X, row: %d, tile addr: %X\n", context->sprite_info_list[context->cur_slot].index, x, context->sprite_info_list[context->cur_slot].y, width, height, pal_priority, row, address);
|
pavone@35
|
459 for (i=0; i < width && context->sprite_draws; i++, x += dir) {
|
pavone@34
|
460 --context->sprite_draws;
|
pavone@34
|
461 context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4;
|
pavone@34
|
462 context->sprite_draw_list[context->sprite_draws].x_pos = x;
|
pavone@34
|
463 context->sprite_draw_list[context->sprite_draws].pal_priority = pal_priority;
|
pavone@34
|
464 context->sprite_draw_list[context->sprite_draws].h_flip = (tileinfo & MAP_BIT_H_FLIP) ? 1 : 0;
|
pavone@34
|
465 }
|
pavone@34
|
466 }
|
pavone@36
|
467 if (i < width) {
|
pavone@36
|
468 context->flags |= FLAG_DOT_OFLOW;
|
pavone@36
|
469 }
|
pavone@36
|
470 context->cur_slot--;
|
pavone@20
|
471 } else {
|
pavone@34
|
472 context->flags |= FLAG_DOT_OFLOW;
|
pavone@20
|
473 }
|
pavone@20
|
474 }
|
pavone@20
|
475 }
|
pavone@20
|
476
|
pavone@427
|
477 void write_cram(vdp_context * context, uint16_t address, uint16_t value)
|
pavone@427
|
478 {
|
pavone@427
|
479 uint16_t addr = (address/2) & (CRAM_SIZE-1);
|
pavone@427
|
480 context->cram[addr] = value;
|
pavone@427
|
481 context->colors[addr] = color_map[value & 0xEEE];
|
pavone@427
|
482 context->colors[addr + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW];
|
pavone@427
|
483 context->colors[addr + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT];
|
pavone@427
|
484 }
|
pavone@427
|
485
|
pavone@980
|
486 #define CRAM_BITS 0xEEE
|
pavone@980
|
487 #define VSRAM_BITS 0x7FF
|
pavone@980
|
488 #define VSRAM_DIRTY_BITS 0xF800
|
pavone@980
|
489
|
pavone@984
|
490 void vdp_advance_dma(vdp_context * context)
|
pavone@984
|
491 {
|
pavone@984
|
492 context->regs[REG_DMASRC_L] += 1;
|
pavone@984
|
493 if (!context->regs[REG_DMASRC_L]) {
|
pavone@984
|
494 context->regs[REG_DMASRC_M] += 1;
|
pavone@984
|
495 }
|
pavone@984
|
496 context->address += context->regs[REG_AUTOINC];
|
pavone@984
|
497 uint16_t dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1;
|
pavone@984
|
498 context->regs[REG_DMALEN_H] = dma_len >> 8;
|
pavone@984
|
499 context->regs[REG_DMALEN_L] = dma_len;
|
pavone@984
|
500 if (!dma_len) {
|
pavone@984
|
501 context->flags &= ~FLAG_DMA_RUN;
|
pavone@984
|
502 context->cd &= 0xF;
|
pavone@984
|
503 }
|
pavone@984
|
504 }
|
pavone@984
|
505
|
pavone@20
|
506 void external_slot(vdp_context * context)
|
pavone@20
|
507 {
|
pavone@984
|
508 if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80 && context->fifo_read < 0) {
|
pavone@984
|
509 context->fifo_read = (context->fifo_write-1) & (FIFO_SIZE-1);
|
pavone@984
|
510 fifo_entry * cur = context->fifo + context->fifo_read;
|
pavone@984
|
511 cur->cycle = context->cycles;
|
pavone@984
|
512 cur->address = context->address;
|
pavone@984
|
513 cur->partial = 2;
|
pavone@984
|
514 vdp_advance_dma(context);
|
pavone@984
|
515 }
|
pavone@471
|
516 fifo_entry * start = context->fifo + context->fifo_read;
|
pavone@471
|
517 if (context->fifo_read >= 0 && start->cycle <= context->cycles) {
|
pavone@460
|
518 switch (start->cd & 0xF)
|
pavone@460
|
519 {
|
pavone@460
|
520 case VRAM_WRITE:
|
pavone@460
|
521 if (start->partial) {
|
pavone@471
|
522 //printf("VRAM Write: %X to %X at %d (line %d, slot %d)\n", start->value, start->address ^ 1, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16);
|
pavone@478
|
523 context->vdpmem[start->address ^ 1] = start->partial == 2 ? start->value >> 8 : start->value;
|
pavone@460
|
524 } else {
|
pavone@471
|
525 //printf("VRAM Write High: %X to %X at %d (line %d, slot %d)\n", start->value >> 8, start->address, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16);
|
pavone@460
|
526 context->vdpmem[start->address] = start->value >> 8;
|
pavone@460
|
527 start->partial = 1;
|
pavone@460
|
528 //skip auto-increment and removal of entry from fifo
|
pavone@460
|
529 return;
|
pavone@460
|
530 }
|
pavone@460
|
531 break;
|
pavone@460
|
532 case CRAM_WRITE: {
|
pavone@460
|
533 //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1));
|
pavone@479
|
534 write_cram(context, start->address, start->partial == 2 ? context->fifo[context->fifo_write].value : start->value);
|
pavone@460
|
535 break;
|
pavone@460
|
536 }
|
pavone@460
|
537 case VSRAM_WRITE:
|
pavone@460
|
538 if (((start->address/2) & 63) < VSRAM_SIZE) {
|
pavone@718
|
539 //printf("VSRAM Write: %X to %X @ vcounter: %d, hslot: %d, cycle: %d\n", start->value, context->address, context->vcounter, context->hslot, context->cycles);
|
pavone@479
|
540 context->vsram[(start->address/2) & 63] = start->partial == 2 ? context->fifo[context->fifo_write].value : start->value;
|
pavone@460
|
541 }
|
pavone@460
|
542
|
pavone@460
|
543 break;
|
pavone@460
|
544 }
|
pavone@471
|
545 context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1);
|
pavone@471
|
546 if (context->fifo_read == context->fifo_write) {
|
pavone@983
|
547 if ((context->cd & 0x20) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
|
pavone@983
|
548 context->flags |= FLAG_DMA_RUN;
|
pavone@983
|
549 }
|
pavone@471
|
550 context->fifo_read = -1;
|
pavone@460
|
551 }
|
pavone@980
|
552 } else if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & 0xC0) == 0xC0) {
|
pavone@980
|
553 if (context->flags & FLAG_READ_FETCHED) {
|
pavone@980
|
554 context->vdpmem[context->address ^ 1] = context->prefetch;
|
pavone@980
|
555
|
pavone@980
|
556 //Update DMA state
|
pavone@984
|
557 vdp_advance_dma(context);
|
pavone@980
|
558
|
pavone@980
|
559 context->flags &= ~FLAG_READ_FETCHED;
|
pavone@980
|
560 } else {
|
pavone@980
|
561 context->prefetch = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1];
|
pavone@980
|
562
|
pavone@980
|
563 context->flags |= FLAG_READ_FETCHED;
|
pavone@980
|
564 }
|
pavone@980
|
565 } else if (!(context->cd & 1) && !(context->flags & FLAG_READ_FETCHED)){
|
pavone@980
|
566 switch(context->cd & 0xF)
|
pavone@980
|
567 {
|
pavone@980
|
568 case VRAM_READ:
|
pavone@980
|
569 if (context->flags2 & FLAG2_READ_PENDING) {
|
pavone@980
|
570 context->prefetch |= context->vdpmem[context->address | 1];
|
pavone@980
|
571 context->flags |= FLAG_READ_FETCHED;
|
pavone@980
|
572 context->flags2 &= ~FLAG2_READ_PENDING;
|
pavone@980
|
573 //Should this happen after the prefetch or after the read?
|
pavone@980
|
574 //context->address += context->regs[REG_AUTOINC];
|
pavone@980
|
575 } else {
|
pavone@980
|
576 context->prefetch = context->vdpmem[context->address & 0xFFFE] << 8;
|
pavone@980
|
577 context->flags2 |= FLAG2_READ_PENDING;
|
pavone@980
|
578 }
|
pavone@980
|
579 break;
|
pavone@980
|
580 case VRAM_READ8:
|
pavone@980
|
581 context->prefetch = context->vdpmem[context->address ^ 1];
|
pavone@980
|
582 context->prefetch |= context->fifo[context->fifo_write].value & 0xFF00;
|
pavone@980
|
583 context->flags |= FLAG_READ_FETCHED;
|
pavone@980
|
584 //Should this happen after the prefetch or after the read?
|
pavone@980
|
585 //context->address += context->regs[REG_AUTOINC];
|
pavone@980
|
586 break;
|
pavone@980
|
587 case CRAM_READ:
|
pavone@980
|
588 context->prefetch = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS;
|
pavone@980
|
589 context->prefetch |= context->fifo[context->fifo_write].value & ~CRAM_BITS;
|
pavone@980
|
590 context->flags |= FLAG_READ_FETCHED;
|
pavone@980
|
591 //Should this happen after the prefetch or after the read?
|
pavone@980
|
592 //context->address += context->regs[REG_AUTOINC];
|
pavone@980
|
593 break;
|
pavone@980
|
594 case VSRAM_READ: {
|
pavone@980
|
595 uint16_t address = (context->address /2) & 63;
|
pavone@980
|
596 if (address >= VSRAM_SIZE) {
|
pavone@980
|
597 address = 0;
|
pavone@980
|
598 }
|
pavone@980
|
599 context->prefetch = context->vsram[address] & VSRAM_BITS;
|
pavone@980
|
600 context->prefetch |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS;
|
pavone@980
|
601 context->flags |= FLAG_READ_FETCHED;
|
pavone@980
|
602 //Should this happen after the prefetch or after the read?
|
pavone@980
|
603 //context->address += context->regs[REG_AUTOINC];
|
pavone@980
|
604 break;
|
pavone@980
|
605 }
|
pavone@980
|
606 }
|
pavone@460
|
607 }
|
pavone@460
|
608 }
|
pavone@460
|
609
|
pavone@924
|
610 void run_dma_src(vdp_context * context, int32_t slot)
|
pavone@460
|
611 {
|
pavone@75
|
612 //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode
|
pavone@75
|
613 //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations
|
pavone@471
|
614 if (context->fifo_write == context->fifo_read) {
|
pavone@460
|
615 return;
|
pavone@460
|
616 }
|
pavone@478
|
617 fifo_entry * cur = NULL;
|
pavone@984
|
618 if (!(context->regs[REG_DMASRC_H] & 0x80))
|
pavone@460
|
619 {
|
pavone@984
|
620 //68K -> VDP
|
pavone@924
|
621 if (slot == -1 || !is_refresh(context, slot-1)) {
|
pavone@478
|
622 cur = context->fifo + context->fifo_write;
|
pavone@622
|
623 cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
|
pavone@478
|
624 cur->address = context->address;
|
pavone@478
|
625 cur->value = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]);
|
pavone@478
|
626 cur->cd = context->cd;
|
pavone@478
|
627 cur->partial = 0;
|
pavone@478
|
628 if (context->fifo_read < 0) {
|
pavone@478
|
629 context->fifo_read = context->fifo_write;
|
pavone@478
|
630 }
|
pavone@478
|
631 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1);
|
pavone@984
|
632 vdp_advance_dma(context);
|
pavone@75
|
633 }
|
pavone@54
|
634 }
|
pavone@20
|
635 }
|
pavone@20
|
636
|
pavone@40
|
637 #define WINDOW_RIGHT 0x80
|
pavone@40
|
638 #define WINDOW_DOWN 0x80
|
pavone@40
|
639
|
pavone@25
|
640 void read_map_scroll(uint16_t column, uint16_t vsram_off, uint32_t line, uint16_t address, uint16_t hscroll_val, vdp_context * context)
|
pavone@20
|
641 {
|
pavone@417
|
642 uint16_t window_line_shift, v_offset_mask, vscroll_shift;
|
pavone@414
|
643 if (context->double_res) {
|
pavone@413
|
644 line *= 2;
|
pavone@413
|
645 if (context->framebuf != context->oddbuf) {
|
pavone@413
|
646 line++;
|
pavone@413
|
647 }
|
pavone@417
|
648 window_line_shift = 4;
|
pavone@417
|
649 v_offset_mask = 0xF;
|
pavone@417
|
650 vscroll_shift = 4;
|
pavone@417
|
651 } else {
|
pavone@417
|
652 window_line_shift = 3;
|
pavone@417
|
653 v_offset_mask = 0x7;
|
pavone@417
|
654 vscroll_shift = 3;
|
pavone@414
|
655 }
|
pavone@40
|
656 if (!vsram_off) {
|
pavone@40
|
657 uint16_t left_col, right_col;
|
pavone@40
|
658 if (context->regs[REG_WINDOW_H] & WINDOW_RIGHT) {
|
pavone@920
|
659 left_col = (context->regs[REG_WINDOW_H] & 0x1F) * 2 + 2;
|
pavone@41
|
660 right_col = 42;
|
pavone@41
|
661 } else {
|
pavone@40
|
662 left_col = 0;
|
pavone@40
|
663 right_col = (context->regs[REG_WINDOW_H] & 0x1F) * 2;
|
pavone@40
|
664 if (right_col) {
|
pavone@40
|
665 right_col += 2;
|
pavone@40
|
666 }
|
pavone@41
|
667 }
|
pavone@41
|
668 uint16_t top_line, bottom_line;
|
pavone@41
|
669 if (context->regs[REG_WINDOW_V] & WINDOW_DOWN) {
|
pavone@417
|
670 top_line = (context->regs[REG_WINDOW_V] & 0x1F) << window_line_shift;
|
pavone@417
|
671 bottom_line = context->double_res ? 481 : 241;
|
pavone@40
|
672 } else {
|
pavone@41
|
673 top_line = 0;
|
pavone@417
|
674 bottom_line = (context->regs[REG_WINDOW_V] & 0x1F) << window_line_shift;
|
pavone@40
|
675 }
|
pavone@41
|
676 if ((column >= left_col && column < right_col) || (line >= top_line && line < bottom_line)) {
|
pavone@41
|
677 uint16_t address = context->regs[REG_WINDOW] << 10;
|
pavone@41
|
678 uint16_t line_offset, offset, mask;
|
pavone@622
|
679 if (context->regs[REG_MODE_4] & BIT_H40) {
|
pavone@41
|
680 address &= 0xF000;
|
pavone@417
|
681 line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF;
|
pavone@41
|
682 mask = 0x7F;
|
pavone@450
|
683
|
pavone@40
|
684 } else {
|
pavone@41
|
685 address &= 0xF800;
|
pavone@417
|
686 line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF;
|
pavone@41
|
687 mask = 0x3F;
|
pavone@40
|
688 }
|
pavone@417
|
689 if (context->double_res) {
|
pavone@417
|
690 mask <<= 1;
|
pavone@417
|
691 mask |= 1;
|
pavone@417
|
692 }
|
pavone@42
|
693 offset = address + line_offset + (((column - 2) * 2) & mask);
|
pavone@41
|
694 context->col_1 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
|
pavone@54
|
695 //printf("Window | top: %d, bot: %d, left: %d, right: %d, base: %X, line: %X offset: %X, tile: %X, reg: %X\n", top_line, bottom_line, left_col, right_col, address, line_offset, offset, ((context->col_1 & 0x3FF) << 5), context->regs[REG_WINDOW]);
|
pavone@42
|
696 offset = address + line_offset + (((column - 1) * 2) & mask);
|
pavone@41
|
697 context->col_2 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
|
pavone@417
|
698 context->v_offset = (line) & v_offset_mask;
|
pavone@41
|
699 context->flags |= FLAG_WINDOW;
|
pavone@41
|
700 return;
|
pavone@40
|
701 }
|
pavone@40
|
702 context->flags &= ~FLAG_WINDOW;
|
pavone@40
|
703 }
|
pavone@20
|
704 uint16_t vscroll;
|
pavone@20
|
705 switch(context->regs[REG_SCROLL] & 0x30)
|
pavone@20
|
706 {
|
pavone@20
|
707 case 0:
|
pavone@20
|
708 vscroll = 0xFF;
|
pavone@20
|
709 break;
|
pavone@20
|
710 case 0x10:
|
pavone@20
|
711 vscroll = 0x1FF;
|
pavone@20
|
712 break;
|
pavone@20
|
713 case 0x20:
|
pavone@20
|
714 //TODO: Verify this behavior
|
pavone@20
|
715 vscroll = 0;
|
pavone@20
|
716 break;
|
pavone@20
|
717 case 0x30:
|
pavone@20
|
718 vscroll = 0x3FF;
|
pavone@20
|
719 break;
|
pavone@20
|
720 }
|
pavone@414
|
721 if (context->double_res) {
|
pavone@413
|
722 vscroll <<= 1;
|
pavone@413
|
723 vscroll |= 1;
|
pavone@414
|
724 }
|
pavone@718
|
725 //TODO: Further research on vscroll latch behavior and the "first column bug"
|
pavone@718
|
726 if (!column) {
|
pavone@718
|
727 if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
|
pavone@718
|
728 if (context->regs[REG_MODE_4] & BIT_H40) {
|
pavone@718
|
729 //Based on observed behavior documented by Eke-Eke, I'm guessing the VDP
|
pavone@718
|
730 //ends up fetching the last value on the VSRAM bus in the H40 case
|
pavone@718
|
731 //getting the last latched value should be close enough for now
|
pavone@718
|
732 if (!vsram_off) {
|
pavone@718
|
733 context->vscroll_latch[0] = context->vscroll_latch[1];
|
pavone@718
|
734 }
|
pavone@718
|
735 } else {
|
pavone@718
|
736 //supposedly it's always forced to 0 in the H32 case
|
pavone@718
|
737 context->vscroll_latch[0] = context->vscroll_latch[1] = 0;
|
pavone@718
|
738 }
|
pavone@718
|
739 } else {
|
pavone@718
|
740 context->vscroll_latch[vsram_off] = context->vsram[vsram_off];
|
pavone@718
|
741 }
|
pavone@718
|
742 } else if (context->regs[REG_MODE_3] & BIT_VSCROLL) {
|
pavone@710
|
743 context->vscroll_latch[vsram_off] = context->vsram[column - 2 + vsram_off];
|
pavone@710
|
744 }
|
pavone@710
|
745 vscroll &= context->vscroll_latch[vsram_off] + line;
|
pavone@414
|
746 context->v_offset = vscroll & v_offset_mask;
|
pavone@26
|
747 //printf("%s | line %d, vsram: %d, vscroll: %d, v_offset: %d\n",(vsram_off ? "B" : "A"), line, context->vsram[context->regs[REG_MODE_3] & 0x4 ? column : 0], vscroll, context->v_offset);
|
pavone@414
|
748 vscroll >>= vscroll_shift;
|
pavone@20
|
749 uint16_t hscroll_mask;
|
pavone@20
|
750 uint16_t v_mul;
|
pavone@20
|
751 switch(context->regs[REG_SCROLL] & 0x3)
|
pavone@20
|
752 {
|
pavone@20
|
753 case 0:
|
pavone@108
|
754 hscroll_mask = 0x1F;
|
pavone@20
|
755 v_mul = 64;
|
pavone@20
|
756 break;
|
pavone@20
|
757 case 0x1:
|
pavone@39
|
758 hscroll_mask = 0x3F;
|
pavone@20
|
759 v_mul = 128;
|
pavone@20
|
760 break;
|
pavone@20
|
761 case 0x2:
|
pavone@20
|
762 //TODO: Verify this behavior
|
pavone@20
|
763 hscroll_mask = 0;
|
pavone@20
|
764 v_mul = 0;
|
pavone@20
|
765 break;
|
pavone@20
|
766 case 0x3:
|
pavone@108
|
767 hscroll_mask = 0x7F;
|
pavone@20
|
768 v_mul = 256;
|
pavone@20
|
769 break;
|
pavone@20
|
770 }
|
pavone@28
|
771 uint16_t hscroll, offset;
|
pavone@28
|
772 for (int i = 0; i < 2; i++) {
|
pavone@39
|
773 hscroll = (column - 2 + i - ((hscroll_val/8) & 0xFFFE)) & hscroll_mask;
|
pavone@39
|
774 offset = address + ((vscroll * v_mul + hscroll*2) & 0x1FFF);
|
pavone@39
|
775 //printf("%s | line: %d, col: %d, x: %d, hs_mask %X, scr reg: %X, tbl addr: %X\n", (vsram_off ? "B" : "A"), line, (column-2+i), hscroll, hscroll_mask, context->regs[REG_SCROLL], offset);
|
pavone@28
|
776 uint16_t col_val = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
|
pavone@28
|
777 if (i) {
|
pavone@28
|
778 context->col_2 = col_val;
|
pavone@28
|
779 } else {
|
pavone@28
|
780 context->col_1 = col_val;
|
pavone@28
|
781 }
|
pavone@28
|
782 }
|
pavone@20
|
783 }
|
pavone@20
|
784
|
pavone@20
|
785 void read_map_scroll_a(uint16_t column, uint32_t line, vdp_context * context)
|
pavone@20
|
786 {
|
pavone@25
|
787 read_map_scroll(column, 0, line, (context->regs[REG_SCROLL_A] & 0x38) << 10, context->hscroll_a, context);
|
pavone@20
|
788 }
|
pavone@20
|
789
|
pavone@20
|
790 void read_map_scroll_b(uint16_t column, uint32_t line, vdp_context * context)
|
pavone@20
|
791 {
|
pavone@25
|
792 read_map_scroll(column, 1, line, (context->regs[REG_SCROLL_B] & 0x7) << 13, context->hscroll_b, context);
|
pavone@20
|
793 }
|
pavone@20
|
794
|
pavone@436
|
795 void render_map(uint16_t col, uint8_t * tmp_buf, uint8_t offset, vdp_context * context)
|
pavone@20
|
796 {
|
pavone@413
|
797 uint16_t address;
|
pavone@413
|
798 uint8_t shift, add;
|
pavone@413
|
799 if (context->double_res) {
|
pavone@413
|
800 address = ((col & 0x3FF) << 6);
|
pavone@413
|
801 shift = 1;
|
pavone@413
|
802 add = context->framebuf != context->oddbuf ? 1 : 0;
|
pavone@413
|
803 } else {
|
pavone@413
|
804 address = ((col & 0x7FF) << 5);
|
pavone@413
|
805 shift = 0;
|
pavone@413
|
806 add = 0;
|
pavone@413
|
807 }
|
pavone@20
|
808 if (col & MAP_BIT_V_FLIP) {
|
pavone@414
|
809 address += 28 - 4 * context->v_offset/*((context->v_offset << shift) + add)*/;
|
pavone@20
|
810 } else {
|
pavone@414
|
811 address += 4 * context->v_offset/*((context->v_offset << shift) + add)*/;
|
pavone@20
|
812 }
|
pavone@20
|
813 uint16_t pal_priority = (col >> 9) & 0x70;
|
pavone@20
|
814 int32_t dir;
|
pavone@20
|
815 if (col & MAP_BIT_H_FLIP) {
|
pavone@436
|
816 offset += 7;
|
pavone@436
|
817 offset &= SCROLL_BUFFER_MASK;
|
pavone@20
|
818 dir = -1;
|
pavone@20
|
819 } else {
|
pavone@20
|
820 dir = 1;
|
pavone@20
|
821 }
|
pavone@20
|
822 for (uint32_t i=0; i < 4; i++, address++)
|
pavone@20
|
823 {
|
pavone@436
|
824 tmp_buf[offset] = pal_priority | (context->vdpmem[address] >> 4);
|
pavone@436
|
825 offset += dir;
|
pavone@436
|
826 offset &= SCROLL_BUFFER_MASK;
|
pavone@436
|
827 tmp_buf[offset] = pal_priority | (context->vdpmem[address] & 0xF);
|
pavone@436
|
828 offset += dir;
|
pavone@436
|
829 offset &= SCROLL_BUFFER_MASK;
|
pavone@20
|
830 }
|
pavone@20
|
831 }
|
pavone@20
|
832
|
pavone@20
|
833 void render_map_1(vdp_context * context)
|
pavone@20
|
834 {
|
pavone@436
|
835 render_map(context->col_1, context->tmp_buf_a, context->buf_a_off, context);
|
pavone@20
|
836 }
|
pavone@20
|
837
|
pavone@20
|
838 void render_map_2(vdp_context * context)
|
pavone@20
|
839 {
|
pavone@436
|
840 render_map(context->col_2, context->tmp_buf_a, context->buf_a_off+8, context);
|
pavone@20
|
841 }
|
pavone@20
|
842
|
pavone@20
|
843 void render_map_3(vdp_context * context)
|
pavone@20
|
844 {
|
pavone@436
|
845 render_map(context->col_1, context->tmp_buf_b, context->buf_b_off, context);
|
pavone@20
|
846 }
|
pavone@20
|
847
|
pavone@20
|
848 void render_map_output(uint32_t line, int32_t col, vdp_context * context)
|
pavone@20
|
849 {
|
pavone@20
|
850 if (line >= 240) {
|
pavone@20
|
851 return;
|
pavone@20
|
852 }
|
pavone@436
|
853 render_map(context->col_2, context->tmp_buf_b, context->buf_b_off+8, context);
|
pavone@719
|
854 uint32_t *dst;
|
pavone@436
|
855 uint8_t *sprite_buf, *plane_a, *plane_b;
|
pavone@436
|
856 int plane_a_off, plane_b_off;
|
pavone@20
|
857 if (col)
|
pavone@20
|
858 {
|
pavone@20
|
859 col-=2;
|
pavone@719
|
860 dst = context->framebuf;
|
pavone@719
|
861 dst += line * 320 + col * 8;
|
pavone@722
|
862 if (context->debug < 2) {
|
pavone@722
|
863 sprite_buf = context->linebuf + col * 8;
|
pavone@722
|
864 uint8_t a_src, src;
|
pavone@722
|
865 if (context->flags & FLAG_WINDOW) {
|
pavone@722
|
866 plane_a_off = context->buf_a_off;
|
pavone@722
|
867 a_src = DBG_SRC_W;
|
pavone@722
|
868 } else {
|
pavone@722
|
869 plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF);
|
pavone@722
|
870 a_src = DBG_SRC_A;
|
pavone@722
|
871 }
|
pavone@722
|
872 plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF);
|
pavone@722
|
873 //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7));
|
pavone@450
|
874
|
pavone@722
|
875 if (context->regs[REG_MODE_4] & BIT_HILIGHT) {
|
pavone@722
|
876 for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) {
|
pavone@722
|
877 plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK);
|
pavone@722
|
878 plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK);
|
pavone@748
|
879 uint8_t pixel = context->regs[REG_BG_COLOR];
|
pavone@748
|
880 uint32_t *colors = context->colors;
|
pavone@722
|
881 src = DBG_SRC_BG;
|
pavone@722
|
882 if (*plane_b & 0xF) {
|
pavone@722
|
883 pixel = *plane_b;
|
pavone@722
|
884 src = DBG_SRC_B;
|
pavone@722
|
885 }
|
pavone@748
|
886 uint8_t intensity = *plane_b & BUF_BIT_PRIORITY;
|
pavone@722
|
887 if (*plane_a & 0xF && (*plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
|
pavone@722
|
888 pixel = *plane_a;
|
pavone@722
|
889 src = DBG_SRC_A;
|
pavone@722
|
890 }
|
pavone@748
|
891 intensity |= *plane_a & BUF_BIT_PRIORITY;
|
pavone@748
|
892 if (*sprite_buf & 0xF && (*sprite_buf & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
|
pavone@748
|
893 if ((*sprite_buf & 0x3F) == 0x3E) {
|
pavone@748
|
894 intensity += BUF_BIT_PRIORITY;
|
pavone@748
|
895 } else if ((*sprite_buf & 0x3F) == 0x3F) {
|
pavone@748
|
896 intensity = 0;
|
pavone@748
|
897 } else {
|
pavone@722
|
898 pixel = *sprite_buf;
|
pavone@722
|
899 src = DBG_SRC_S;
|
pavone@722
|
900 if ((pixel & 0xF) == 0xE) {
|
pavone@748
|
901 intensity = BUF_BIT_PRIORITY;
|
pavone@748
|
902 } else {
|
pavone@748
|
903 intensity |= pixel & BUF_BIT_PRIORITY;
|
pavone@722
|
904 }
|
pavone@722
|
905 }
|
pavone@748
|
906 }
|
pavone@748
|
907 if (!intensity) {
|
pavone@748
|
908 src |= DBG_SHADOW;
|
pavone@437
|
909 colors += CRAM_SIZE;
|
pavone@748
|
910 } else if (intensity == BUF_BIT_PRIORITY*2) {
|
pavone@748
|
911 src |= DBG_HILIGHT;
|
pavone@748
|
912 colors += CRAM_SIZE*2;
|
pavone@722
|
913 }
|
pavone@884
|
914
|
pavone@722
|
915 uint32_t outpixel;
|
pavone@722
|
916 if (context->debug) {
|
pavone@722
|
917 outpixel = context->debugcolors[src];
|
pavone@722
|
918 } else {
|
pavone@748
|
919 outpixel = colors[pixel & 0x3F];
|
pavone@722
|
920 }
|
pavone@722
|
921 *(dst++) = outpixel;
|
pavone@722
|
922 }
|
pavone@722
|
923 } else {
|
pavone@722
|
924 for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) {
|
pavone@722
|
925 plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK);
|
pavone@722
|
926 plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK);
|
pavone@722
|
927 uint8_t pixel = context->regs[REG_BG_COLOR];
|
pavone@722
|
928 src = DBG_SRC_BG;
|
pavone@722
|
929 if (*plane_b & 0xF) {
|
pavone@722
|
930 pixel = *plane_b;
|
pavone@722
|
931 src = DBG_SRC_B;
|
pavone@722
|
932 }
|
pavone@722
|
933 if (*plane_a & 0xF && (*plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
|
pavone@722
|
934 pixel = *plane_a;
|
pavone@722
|
935 src = DBG_SRC_A;
|
pavone@722
|
936 }
|
pavone@722
|
937 if (*sprite_buf & 0xF && (*sprite_buf & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) {
|
pavone@230
|
938 pixel = *sprite_buf;
|
pavone@437
|
939 src = DBG_SRC_S;
|
pavone@230
|
940 }
|
pavone@722
|
941 uint32_t outpixel;
|
pavone@722
|
942 if (context->debug) {
|
pavone@722
|
943 outpixel = context->debugcolors[src];
|
pavone@722
|
944 } else {
|
pavone@722
|
945 outpixel = context->colors[pixel & 0x3F];
|
pavone@722
|
946 }
|
pavone@722
|
947 *(dst++) = outpixel;
|
pavone@230
|
948 }
|
pavone@722
|
949 }
|
pavone@722
|
950 } else if (context->debug == 2) {
|
pavone@722
|
951 if (col < 32) {
|
pavone@722
|
952 *(dst++) = context->colors[col * 2];
|
pavone@722
|
953 *(dst++) = context->colors[col * 2];
|
pavone@722
|
954 *(dst++) = context->colors[col * 2];
|
pavone@722
|
955 *(dst++) = context->colors[col * 2];
|
pavone@722
|
956 *(dst++) = context->colors[col * 2 + 1];
|
pavone@722
|
957 *(dst++) = context->colors[col * 2 + 1];
|
pavone@722
|
958 *(dst++) = context->colors[col * 2 + 1];
|
pavone@722
|
959 *(dst++) = context->colors[col * 2 + 1];
|
pavone@722
|
960 *(dst++) = context->colors[col * 2 + 2];
|
pavone@722
|
961 *(dst++) = context->colors[col * 2 + 2];
|
pavone@722
|
962 *(dst++) = context->colors[col * 2 + 2];
|
pavone@722
|
963 *(dst++) = context->colors[col * 2 + 2];
|
pavone@722
|
964 *(dst++) = context->colors[col * 2 + 3];
|
pavone@722
|
965 *(dst++) = context->colors[col * 2 + 3];
|
pavone@722
|
966 *(dst++) = context->colors[col * 2 + 3];
|
pavone@722
|
967 *(dst++) = context->colors[col * 2 + 3];
|
pavone@722
|
968 } else if (col == 32 || line >= 192) {
|
pavone@722
|
969 for (int32_t i = 0; i < 16; i ++) {
|
pavone@722
|
970 *(dst++) = 0;
|
pavone@437
|
971 }
|
pavone@722
|
972 } else {
|
pavone@722
|
973 for (int32_t i = 0; i < 16; i ++) {
|
pavone@722
|
974 *(dst++) = context->colors[line / 3 + (col - 34) * 0x20];
|
pavone@722
|
975 }
|
pavone@20
|
976 }
|
pavone@230
|
977 } else {
|
pavone@771
|
978 uint32_t base = (context->debug - 3) * 0x200;
|
pavone@771
|
979 uint32_t cell = base + (line / 8) * (context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32) + col;
|
pavone@771
|
980 uint32_t address = (cell * 32 + (line % 8) * 4) & 0xFFFF;
|
pavone@722
|
981 for (int32_t i = 0; i < 4; i ++) {
|
pavone@722
|
982 *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)];
|
pavone@722
|
983 *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)];
|
pavone@722
|
984 address++;
|
pavone@722
|
985 }
|
pavone@722
|
986 cell++;
|
pavone@771
|
987 address = (cell * 32 + (line % 8) * 4) & 0xFFFF;
|
pavone@722
|
988 for (int32_t i = 0; i < 4; i ++) {
|
pavone@722
|
989 *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)];
|
pavone@722
|
990 *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)];
|
pavone@722
|
991 address++;
|
pavone@230
|
992 }
|
pavone@20
|
993 }
|
pavone@20
|
994 }
|
pavone@436
|
995 context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
|
pavone@436
|
996 context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK;
|
pavone@20
|
997 }
|
pavone@20
|
998
|
pavone@822
|
999 uint32_t const h40_hsync_cycles[] = {19, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 19};
|
pavone@822
|
1000
|
pavone@822
|
1001 void vdp_advance_line(vdp_context *context)
|
pavone@822
|
1002 {
|
pavone@822
|
1003 context->vcounter++;
|
pavone@822
|
1004 context->vcounter &= 0x1FF;
|
pavone@822
|
1005 if (context->flags2 & FLAG2_REGION_PAL) {
|
pavone@822
|
1006 if (context->latched_mode & BIT_PAL) {
|
pavone@822
|
1007 if (context->vcounter == 0x10B) {
|
pavone@822
|
1008 context->vcounter = 0x1D2;
|
pavone@822
|
1009 }
|
pavone@822
|
1010 } else if (context->vcounter == 0x103){
|
pavone@822
|
1011 context->vcounter = 0x1CA;
|
pavone@822
|
1012 }
|
pavone@822
|
1013 } else if (!(context->latched_mode & BIT_PAL) && context->vcounter == 0xEB) {
|
pavone@822
|
1014 context->vcounter = 0x1E5;
|
pavone@822
|
1015 }
|
pavone@884
|
1016
|
pavone@822
|
1017 if (context->vcounter > (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) {
|
pavone@822
|
1018 context->hint_counter = context->regs[REG_HINT];
|
pavone@822
|
1019 } else if (context->hint_counter) {
|
pavone@822
|
1020 context->hint_counter--;
|
pavone@822
|
1021 } else {
|
pavone@822
|
1022 context->flags2 |= FLAG2_HINT_PENDING;
|
pavone@822
|
1023 context->pending_hint_start = context->cycles;
|
pavone@822
|
1024 context->hint_counter = context->regs[REG_HINT];
|
pavone@822
|
1025 }
|
pavone@822
|
1026 }
|
pavone@822
|
1027
|
pavone@822
|
1028 #define CHECK_ONLY if (context->cycles >= target_cycles) { return; }
|
pavone@924
|
1029 #define CHECK_LIMIT if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } context->hslot++; context->cycles += slot_cycles; CHECK_ONLY
|
pavone@822
|
1030
|
pavone@20
|
1031 #define COLUMN_RENDER_BLOCK(column, startcyc) \
|
pavone@20
|
1032 case startcyc:\
|
pavone@822
|
1033 read_map_scroll_a(column, context->vcounter, context);\
|
pavone@822
|
1034 CHECK_LIMIT\
|
pavone@922
|
1035 case ((startcyc+1)&0xFF):\
|
pavone@20
|
1036 external_slot(context);\
|
pavone@822
|
1037 CHECK_LIMIT\
|
pavone@922
|
1038 case ((startcyc+2)&0xFF):\
|
pavone@20
|
1039 render_map_1(context);\
|
pavone@822
|
1040 CHECK_LIMIT\
|
pavone@922
|
1041 case ((startcyc+3)&0xFF):\
|
pavone@20
|
1042 render_map_2(context);\
|
pavone@822
|
1043 CHECK_LIMIT\
|
pavone@922
|
1044 case ((startcyc+4)&0xFF):\
|
pavone@822
|
1045 read_map_scroll_b(column, context->vcounter, context);\
|
pavone@822
|
1046 CHECK_LIMIT\
|
pavone@922
|
1047 case ((startcyc+5)&0xFF):\
|
pavone@822
|
1048 read_sprite_x(context->vcounter, context);\
|
pavone@822
|
1049 CHECK_LIMIT\
|
pavone@922
|
1050 case ((startcyc+6)&0xFF):\
|
pavone@20
|
1051 render_map_3(context);\
|
pavone@822
|
1052 CHECK_LIMIT\
|
pavone@922
|
1053 case ((startcyc+7)&0xFF):\
|
pavone@822
|
1054 render_map_output(context->vcounter, column, context);\
|
pavone@822
|
1055 CHECK_LIMIT
|
pavone@20
|
1056
|
pavone@20
|
1057 #define COLUMN_RENDER_BLOCK_REFRESH(column, startcyc) \
|
pavone@20
|
1058 case startcyc:\
|
pavone@822
|
1059 read_map_scroll_a(column, context->vcounter, context);\
|
pavone@822
|
1060 CHECK_LIMIT\
|
pavone@20
|
1061 case (startcyc+1):\
|
pavone@822
|
1062 /* refresh, no don't run dma src */\
|
pavone@822
|
1063 context->hslot++;\
|
pavone@822
|
1064 context->cycles += slot_cycles;\
|
pavone@822
|
1065 CHECK_ONLY\
|
pavone@20
|
1066 case (startcyc+2):\
|
pavone@20
|
1067 render_map_1(context);\
|
pavone@822
|
1068 CHECK_LIMIT\
|
pavone@20
|
1069 case (startcyc+3):\
|
pavone@20
|
1070 render_map_2(context);\
|
pavone@822
|
1071 CHECK_LIMIT\
|
pavone@20
|
1072 case (startcyc+4):\
|
pavone@822
|
1073 read_map_scroll_b(column, context->vcounter, context);\
|
pavone@822
|
1074 CHECK_LIMIT\
|
pavone@20
|
1075 case (startcyc+5):\
|
pavone@822
|
1076 read_sprite_x(context->vcounter, context);\
|
pavone@822
|
1077 CHECK_LIMIT\
|
pavone@20
|
1078 case (startcyc+6):\
|
pavone@20
|
1079 render_map_3(context);\
|
pavone@822
|
1080 CHECK_LIMIT\
|
pavone@20
|
1081 case (startcyc+7):\
|
pavone@822
|
1082 render_map_output(context->vcounter, column, context);\
|
pavone@822
|
1083 CHECK_LIMIT
|
pavone@884
|
1084
|
pavone@822
|
1085 #define SPRITE_RENDER_H40(slot) \
|
pavone@822
|
1086 case slot:\
|
pavone@822
|
1087 render_sprite_cells( context);\
|
pavone@822
|
1088 scan_sprite_table(context->vcounter, context);\
|
pavone@924
|
1089 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
|
pavone@822
|
1090 if (slot == 182) {\
|
pavone@822
|
1091 context->hslot = 229;\
|
pavone@922
|
1092 context->cycles += h40_hsync_cycles[0];\
|
pavone@822
|
1093 } else {\
|
pavone@822
|
1094 context->hslot++;\
|
pavone@822
|
1095 if (slot >= HSYNC_SLOT_H40 && slot < HSYNC_END_H40) {\
|
pavone@822
|
1096 context->cycles += h40_hsync_cycles[slot - HSYNC_SLOT_H40];\
|
pavone@822
|
1097 } else {\
|
pavone@822
|
1098 context->cycles += slot_cycles;\
|
pavone@822
|
1099 }\
|
pavone@822
|
1100 }\
|
pavone@822
|
1101 CHECK_ONLY
|
pavone@884
|
1102
|
pavone@822
|
1103 #define SPRITE_RENDER_H32(slot) \
|
pavone@822
|
1104 case slot:\
|
pavone@822
|
1105 render_sprite_cells( context);\
|
pavone@822
|
1106 scan_sprite_table(context->vcounter, context);\
|
pavone@924
|
1107 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \
|
pavone@822
|
1108 if (slot == 147) {\
|
pavone@822
|
1109 context->hslot = 233;\
|
pavone@822
|
1110 } else {\
|
pavone@822
|
1111 context->hslot++;\
|
pavone@822
|
1112 }\
|
pavone@822
|
1113 context->cycles += slot_cycles;\
|
pavone@822
|
1114 CHECK_ONLY
|
pavone@884
|
1115
|
pavone@20
|
1116
|
pavone@822
|
1117 void vdp_h40(vdp_context * context, uint32_t target_cycles)
|
pavone@20
|
1118 {
|
pavone@20
|
1119 uint16_t address;
|
pavone@20
|
1120 uint32_t mask;
|
pavone@822
|
1121 uint32_t const slot_cycles = MCLKS_SLOT_H40;
|
pavone@822
|
1122 switch(context->hslot)
|
pavone@822
|
1123 {
|
pavone@822
|
1124 for (;;)
|
pavone@20
|
1125 {
|
pavone@922
|
1126 //sprite attribute table scan starts
|
pavone@647
|
1127 case 165:
|
pavone@822
|
1128 context->sprite_index = 0x80;
|
pavone@822
|
1129 context->slot_counter = MAX_SPRITES_LINE;
|
pavone@21
|
1130 render_sprite_cells( context);
|
pavone@822
|
1131 scan_sprite_table(context->vcounter, context);
|
pavone@822
|
1132 CHECK_LIMIT
|
pavone@922
|
1133 SPRITE_RENDER_H40(166)
|
pavone@922
|
1134 SPRITE_RENDER_H40(167)
|
pavone@922
|
1135 case 168:
|
pavone@922
|
1136 external_slot(context);
|
pavone@922
|
1137 CHECK_LIMIT
|
pavone@922
|
1138 SPRITE_RENDER_H40(169)
|
pavone@922
|
1139 SPRITE_RENDER_H40(170)
|
pavone@922
|
1140 SPRITE_RENDER_H40(171)
|
pavone@822
|
1141 SPRITE_RENDER_H40(172)
|
pavone@822
|
1142 SPRITE_RENDER_H40(173)
|
pavone@822
|
1143 SPRITE_RENDER_H40(174)
|
pavone@822
|
1144 SPRITE_RENDER_H40(175)
|
pavone@822
|
1145 SPRITE_RENDER_H40(176)
|
pavone@822
|
1146 SPRITE_RENDER_H40(177)
|
pavone@822
|
1147 SPRITE_RENDER_H40(178)
|
pavone@822
|
1148 SPRITE_RENDER_H40(179)
|
pavone@822
|
1149 SPRITE_RENDER_H40(180)
|
pavone@822
|
1150 SPRITE_RENDER_H40(181)
|
pavone@922
|
1151 //!HSYNC asserted
|
pavone@822
|
1152 SPRITE_RENDER_H40(182)
|
pavone@822
|
1153 SPRITE_RENDER_H40(229)
|
pavone@822
|
1154 SPRITE_RENDER_H40(230)
|
pavone@822
|
1155 SPRITE_RENDER_H40(231)
|
pavone@822
|
1156 SPRITE_RENDER_H40(232)
|
pavone@822
|
1157 SPRITE_RENDER_H40(233)
|
pavone@822
|
1158 SPRITE_RENDER_H40(234)
|
pavone@822
|
1159 SPRITE_RENDER_H40(235)
|
pavone@822
|
1160 SPRITE_RENDER_H40(236)
|
pavone@822
|
1161 SPRITE_RENDER_H40(237)
|
pavone@822
|
1162 SPRITE_RENDER_H40(238)
|
pavone@822
|
1163 SPRITE_RENDER_H40(239)
|
pavone@822
|
1164 SPRITE_RENDER_H40(240)
|
pavone@822
|
1165 SPRITE_RENDER_H40(241)
|
pavone@922
|
1166 case 242:
|
pavone@20
|
1167 address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
|
pavone@20
|
1168 mask = 0;
|
pavone@20
|
1169 if (context->regs[REG_MODE_3] & 0x2) {
|
pavone@20
|
1170 mask |= 0xF8;
|
pavone@20
|
1171 }
|
pavone@20
|
1172 if (context->regs[REG_MODE_3] & 0x1) {
|
pavone@20
|
1173 mask |= 0x7;
|
pavone@20
|
1174 }
|
pavone@822
|
1175 address += (context->vcounter & mask) * 4;
|
pavone@20
|
1176 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
|
pavone@20
|
1177 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
|
pavone@822
|
1178 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b);
|
pavone@924
|
1179 if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); }
|
pavone@822
|
1180 context->hslot++;
|
pavone@822
|
1181 context->cycles += h40_hsync_cycles[14];
|
pavone@822
|
1182 CHECK_ONLY
|
pavone@20
|
1183 //!HSYNC high
|
pavone@922
|
1184 SPRITE_RENDER_H40(243)
|
pavone@922
|
1185 SPRITE_RENDER_H40(244)
|
pavone@922
|
1186 SPRITE_RENDER_H40(245)
|
pavone@922
|
1187 SPRITE_RENDER_H40(246)
|
pavone@922
|
1188 case 247:
|
pavone@822
|
1189 read_map_scroll_a(0, context->vcounter, context);
|
pavone@822
|
1190 CHECK_LIMIT
|
pavone@922
|
1191 SPRITE_RENDER_H40(248)
|
pavone@922
|
1192 case 249:
|
pavone@20
|
1193 render_map_1(context);
|
pavone@822
|
1194 scan_sprite_table(context->vcounter, context);//Just a guess
|
pavone@922
|
1195 CHECK_LIMIT
|
pavone@922
|
1196 case 250:
|
pavone@20
|
1197 render_map_2(context);
|
pavone@822
|
1198 scan_sprite_table(context->vcounter, context);//Just a guess
|
pavone@822
|
1199 CHECK_LIMIT
|
pavone@922
|
1200 case 251:
|
pavone@822
|
1201 read_map_scroll_b(0, context->vcounter, context);
|
pavone@822
|
1202 CHECK_LIMIT
|
pavone@922
|
1203 SPRITE_RENDER_H40(252)
|
pavone@922
|
1204 case 253:
|
pavone@20
|
1205 render_map_3(context);
|
pavone@822
|
1206 scan_sprite_table(context->vcounter, context);//Just a guess
|
pavone@822
|
1207 CHECK_LIMIT
|
pavone@922
|
1208 case 254:
|
pavone@822
|
1209 if (context->vcounter == (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) {
|
pavone@822
|
1210 context->flags2 |= FLAG2_VINT_PENDING;
|
pavone@822
|
1211 context->pending_vint_start = context->cycles;
|
pavone@822
|
1212 }
|
pavone@822
|
1213 render_map_output(context->vcounter, 0, context);
|
pavone@822
|
1214 scan_sprite_table(context->vcounter, context);//Just a guess
|
pavone@20
|
1215 //reverse context slot counter so it counts the number of sprite slots
|
pavone@20
|
1216 //filled rather than the number of available slots
|
pavone@21
|
1217 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
|
pavone@21
|
1218 context->cur_slot = MAX_SPRITES_LINE-1;
|
pavone@20
|
1219 context->sprite_draws = MAX_DRAWS;
|
pavone@36
|
1220 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
|
pavone@822
|
1221 CHECK_LIMIT
|
pavone@922
|
1222 COLUMN_RENDER_BLOCK(2, 255)
|
pavone@922
|
1223 COLUMN_RENDER_BLOCK(4, 7)
|
pavone@922
|
1224 COLUMN_RENDER_BLOCK(6, 15)
|
pavone@922
|
1225 COLUMN_RENDER_BLOCK_REFRESH(8, 23)
|
pavone@922
|
1226 COLUMN_RENDER_BLOCK(10, 31)
|
pavone@922
|
1227 COLUMN_RENDER_BLOCK(12, 39)
|
pavone@922
|
1228 COLUMN_RENDER_BLOCK(14, 47)
|
pavone@922
|
1229 COLUMN_RENDER_BLOCK_REFRESH(16, 55)
|
pavone@922
|
1230 COLUMN_RENDER_BLOCK(18, 63)
|
pavone@922
|
1231 COLUMN_RENDER_BLOCK(20, 71)
|
pavone@922
|
1232 COLUMN_RENDER_BLOCK(22, 79)
|
pavone@922
|
1233 COLUMN_RENDER_BLOCK_REFRESH(24, 87)
|
pavone@922
|
1234 COLUMN_RENDER_BLOCK(26, 95)
|
pavone@922
|
1235 COLUMN_RENDER_BLOCK(28, 103)
|
pavone@922
|
1236 COLUMN_RENDER_BLOCK(30, 111)
|
pavone@922
|
1237 COLUMN_RENDER_BLOCK_REFRESH(32, 119)
|
pavone@922
|
1238 COLUMN_RENDER_BLOCK(34, 127)
|
pavone@922
|
1239 COLUMN_RENDER_BLOCK(36, 135)
|
pavone@922
|
1240 COLUMN_RENDER_BLOCK(38, 143)
|
pavone@922
|
1241 COLUMN_RENDER_BLOCK_REFRESH(40, 151)
|
pavone@922
|
1242 case 159:
|
pavone@922
|
1243 external_slot(context);
|
pavone@922
|
1244 CHECK_LIMIT
|
pavone@922
|
1245 case 160:
|
pavone@922
|
1246 external_slot(context);
|
pavone@922
|
1247 CHECK_LIMIT
|
pavone@922
|
1248 //sprite render to line buffer starts
|
pavone@922
|
1249 case 161:
|
pavone@922
|
1250 context->cur_slot = MAX_DRAWS-1;
|
pavone@922
|
1251 memset(context->linebuf, 0, LINEBUF_SIZE);
|
pavone@922
|
1252 render_sprite_cells(context);
|
pavone@922
|
1253 CHECK_LIMIT
|
pavone@922
|
1254 case 162:
|
pavone@922
|
1255 render_sprite_cells(context);
|
pavone@922
|
1256 CHECK_LIMIT
|
pavone@922
|
1257 case 163:
|
pavone@922
|
1258 render_sprite_cells(context);
|
pavone@922
|
1259 CHECK_LIMIT
|
pavone@922
|
1260 case 164:
|
pavone@922
|
1261 render_sprite_cells(context);
|
pavone@922
|
1262 vdp_advance_line(context);
|
pavone@922
|
1263 if (context->vcounter == (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) {
|
pavone@922
|
1264 context->hslot++;
|
pavone@922
|
1265 context->cycles += slot_cycles;
|
pavone@922
|
1266 return;
|
pavone@922
|
1267 }
|
pavone@922
|
1268 CHECK_LIMIT
|
pavone@20
|
1269 }
|
pavone@822
|
1270 default:
|
pavone@822
|
1271 context->hslot++;
|
pavone@822
|
1272 context->cycles += slot_cycles;
|
pavone@822
|
1273 return;
|
pavone@822
|
1274 }
|
pavone@20
|
1275 }
|
pavone@20
|
1276
|
pavone@822
|
1277 void vdp_h32(vdp_context * context, uint32_t target_cycles)
|
pavone@20
|
1278 {
|
pavone@37
|
1279 uint16_t address;
|
pavone@37
|
1280 uint32_t mask;
|
pavone@822
|
1281 uint32_t const slot_cycles = MCLKS_SLOT_H32;
|
pavone@822
|
1282 switch(context->hslot)
|
pavone@822
|
1283 {
|
pavone@822
|
1284 for (;;)
|
pavone@20
|
1285 {
|
pavone@923
|
1286 //sprite attribute table scan starts
|
pavone@647
|
1287 case 132:
|
pavone@822
|
1288 context->sprite_index = 0x80;
|
pavone@822
|
1289 context->slot_counter = MAX_SPRITES_LINE_H32;
|
pavone@37
|
1290 render_sprite_cells( context);
|
pavone@822
|
1291 scan_sprite_table(context->vcounter, context);
|
pavone@822
|
1292 CHECK_LIMIT
|
pavone@923
|
1293 SPRITE_RENDER_H32(133)
|
pavone@923
|
1294 SPRITE_RENDER_H32(134)
|
pavone@923
|
1295 SPRITE_RENDER_H32(135)
|
pavone@923
|
1296 SPRITE_RENDER_H32(136)
|
pavone@923
|
1297 SPRITE_RENDER_H32(137)
|
pavone@923
|
1298 SPRITE_RENDER_H32(138)
|
pavone@822
|
1299 SPRITE_RENDER_H32(139)
|
pavone@822
|
1300 SPRITE_RENDER_H32(140)
|
pavone@822
|
1301 SPRITE_RENDER_H32(141)
|
pavone@923
|
1302 case 142:
|
pavone@923
|
1303 external_slot(context);
|
pavone@923
|
1304 CHECK_LIMIT
|
pavone@822
|
1305 SPRITE_RENDER_H32(143)
|
pavone@822
|
1306 SPRITE_RENDER_H32(144)
|
pavone@822
|
1307 SPRITE_RENDER_H32(145)
|
pavone@822
|
1308 SPRITE_RENDER_H32(146)
|
pavone@822
|
1309 SPRITE_RENDER_H32(147)
|
pavone@923
|
1310 //HSYNC start
|
pavone@923
|
1311 SPRITE_RENDER_H32(233)
|
pavone@822
|
1312 SPRITE_RENDER_H32(234)
|
pavone@822
|
1313 SPRITE_RENDER_H32(235)
|
pavone@822
|
1314 SPRITE_RENDER_H32(236)
|
pavone@822
|
1315 SPRITE_RENDER_H32(237)
|
pavone@822
|
1316 SPRITE_RENDER_H32(238)
|
pavone@822
|
1317 SPRITE_RENDER_H32(239)
|
pavone@923
|
1318 case 240:
|
pavone@37
|
1319 external_slot(context);
|
pavone@822
|
1320 CHECK_LIMIT
|
pavone@923
|
1321 case 241:
|
pavone@37
|
1322 address = (context->regs[REG_HSCROLL] & 0x3F) << 10;
|
pavone@37
|
1323 mask = 0;
|
pavone@37
|
1324 if (context->regs[REG_MODE_3] & 0x2) {
|
pavone@37
|
1325 mask |= 0xF8;
|
pavone@37
|
1326 }
|
pavone@37
|
1327 if (context->regs[REG_MODE_3] & 0x1) {
|
pavone@37
|
1328 mask |= 0x7;
|
pavone@37
|
1329 }
|
pavone@822
|
1330 address += (context->vcounter & mask) * 4;
|
pavone@37
|
1331 context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
|
pavone@37
|
1332 context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
|
pavone@822
|
1333 //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b);
|
pavone@822
|
1334 CHECK_LIMIT
|
pavone@923
|
1335 SPRITE_RENDER_H32(242)
|
pavone@923
|
1336 SPRITE_RENDER_H32(243)
|
pavone@923
|
1337 SPRITE_RENDER_H32(244)
|
pavone@923
|
1338 SPRITE_RENDER_H32(245)
|
pavone@37
|
1339 //!HSYNC high
|
pavone@923
|
1340 case 246:
|
pavone@822
|
1341 read_map_scroll_a(0, context->vcounter, context);
|
pavone@822
|
1342 CHECK_LIMIT
|
pavone@923
|
1343 SPRITE_RENDER_H32(247)
|
pavone@923
|
1344 case 248:
|
pavone@37
|
1345 render_map_1(context);
|
pavone@822
|
1346 scan_sprite_table(context->vcounter, context);//Just a guess
|
pavone@822
|
1347 CHECK_LIMIT
|
pavone@923
|
1348 case 249:
|
pavone@37
|
1349 render_map_2(context);
|
pavone@822
|
1350 scan_sprite_table(context->vcounter, context);//Just a guess
|
pavone@923
|
1351 CHECK_LIMIT
|
pavone@923
|
1352 case 250:
|
pavone@822
|
1353 read_map_scroll_b(0, context->vcounter, context);
|
pavone@822
|
1354 CHECK_LIMIT
|
pavone@923
|
1355 case 251:
|
pavone@37
|
1356 render_sprite_cells(context);
|
pavone@822
|
1357 scan_sprite_table(context->vcounter, context);
|
pavone@822
|
1358 CHECK_LIMIT
|
pavone@923
|
1359 case 252:
|
pavone@37
|
1360 render_map_3(context);
|
pavone@822
|
1361 scan_sprite_table(context->vcounter, context);//Just a guess
|
pavone@822
|
1362 CHECK_LIMIT
|
pavone@923
|
1363 case 253:
|
pavone@822
|
1364 render_map_output(context->vcounter, 0, context);
|
pavone@822
|
1365 scan_sprite_table(context->vcounter, context);//Just a guess
|
pavone@37
|
1366 //reverse context slot counter so it counts the number of sprite slots
|
pavone@37
|
1367 //filled rather than the number of available slots
|
pavone@37
|
1368 //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter;
|
pavone@37
|
1369 context->cur_slot = MAX_SPRITES_LINE_H32-1;
|
pavone@37
|
1370 context->sprite_draws = MAX_DRAWS_H32;
|
pavone@37
|
1371 context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED);
|
pavone@822
|
1372 CHECK_LIMIT
|
pavone@923
|
1373 COLUMN_RENDER_BLOCK(2, 254)
|
pavone@923
|
1374 COLUMN_RENDER_BLOCK(4, 6)
|
pavone@923
|
1375 COLUMN_RENDER_BLOCK(6, 14)
|
pavone@923
|
1376 COLUMN_RENDER_BLOCK_REFRESH(8, 22)
|
pavone@923
|
1377 COLUMN_RENDER_BLOCK(10, 30)
|
pavone@923
|
1378 COLUMN_RENDER_BLOCK(12, 38)
|
pavone@923
|
1379 COLUMN_RENDER_BLOCK(14, 46)
|
pavone@923
|
1380 COLUMN_RENDER_BLOCK_REFRESH(16, 54)
|
pavone@923
|
1381 COLUMN_RENDER_BLOCK(18, 62)
|
pavone@923
|
1382 COLUMN_RENDER_BLOCK(20, 70)
|
pavone@923
|
1383 COLUMN_RENDER_BLOCK(22, 78)
|
pavone@923
|
1384 COLUMN_RENDER_BLOCK_REFRESH(24, 86)
|
pavone@923
|
1385 COLUMN_RENDER_BLOCK(26, 94)
|
pavone@923
|
1386 COLUMN_RENDER_BLOCK(28, 102)
|
pavone@923
|
1387 COLUMN_RENDER_BLOCK(30, 110)
|
pavone@923
|
1388 COLUMN_RENDER_BLOCK_REFRESH(32, 118)
|
pavone@923
|
1389 case 126:
|
pavone@923
|
1390 external_slot(context);
|
pavone@923
|
1391 CHECK_LIMIT
|
pavone@923
|
1392 case 127:
|
pavone@923
|
1393 external_slot(context);
|
pavone@923
|
1394 CHECK_LIMIT
|
pavone@923
|
1395 //sprite render to line buffer starts
|
pavone@923
|
1396 case 128:
|
pavone@923
|
1397 context->cur_slot = MAX_DRAWS_H32-1;
|
pavone@923
|
1398 memset(context->linebuf, 0, LINEBUF_SIZE);
|
pavone@923
|
1399 render_sprite_cells(context);
|
pavone@923
|
1400 CHECK_LIMIT
|
pavone@923
|
1401 case 129:
|
pavone@923
|
1402 render_sprite_cells(context);
|
pavone@923
|
1403 CHECK_LIMIT
|
pavone@923
|
1404 case 130:
|
pavone@923
|
1405 render_sprite_cells(context);
|
pavone@923
|
1406 CHECK_LIMIT
|
pavone@923
|
1407 case 131:
|
pavone@923
|
1408 render_sprite_cells(context);
|
pavone@923
|
1409 vdp_advance_line(context);
|
pavone@923
|
1410 if (context->vcounter == (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) {
|
pavone@923
|
1411 context->hslot++;
|
pavone@923
|
1412 context->cycles += slot_cycles;
|
pavone@923
|
1413 return;
|
pavone@923
|
1414 }
|
pavone@923
|
1415 CHECK_LIMIT
|
pavone@20
|
1416 }
|
pavone@822
|
1417 default:
|
pavone@822
|
1418 context->hslot++;
|
pavone@822
|
1419 context->cycles += MCLKS_SLOT_H32;
|
pavone@503
|
1420 }
|
pavone@503
|
1421 }
|
pavone@503
|
1422
|
pavone@20
|
1423 void latch_mode(vdp_context * context)
|
pavone@20
|
1424 {
|
pavone@622
|
1425 context->latched_mode = context->regs[REG_MODE_2] & BIT_PAL;
|
pavone@20
|
1426 }
|
pavone@20
|
1427
|
pavone@330
|
1428 void check_render_bg(vdp_context * context, int32_t line, uint32_t slot)
|
pavone@54
|
1429 {
|
pavone@622
|
1430 int starti = -1;
|
pavone@622
|
1431 if (context->regs[REG_MODE_4] & BIT_H40) {
|
pavone@622
|
1432 if (slot >= 12 && slot < 172) {
|
pavone@622
|
1433 uint32_t x = (slot-12)*2;
|
pavone@622
|
1434 starti = line * 320 + x;
|
pavone@622
|
1435 }
|
pavone@622
|
1436 } else {
|
pavone@622
|
1437 if (slot >= 11 && slot < 139) {
|
pavone@622
|
1438 uint32_t x = (slot-11)*2;
|
pavone@622
|
1439 starti = line * 320 + x;
|
pavone@622
|
1440 }
|
pavone@622
|
1441 }
|
pavone@622
|
1442 if (starti >= 0) {
|
pavone@719
|
1443 uint32_t color = context->colors[context->regs[REG_BG_COLOR]];
|
pavone@719
|
1444 uint32_t * start = context->framebuf;
|
pavone@719
|
1445 start += starti;
|
pavone@719
|
1446 for (int i = 0; i < 2; i++) {
|
pavone@719
|
1447 *(start++) = color;
|
pavone@54
|
1448 }
|
pavone@54
|
1449 }
|
pavone@54
|
1450 }
|
pavone@54
|
1451
|
pavone@717
|
1452
|
pavone@20
|
1453 void vdp_run_context(vdp_context * context, uint32_t target_cycles)
|
pavone@20
|
1454 {
|
pavone@20
|
1455 while(context->cycles < target_cycles)
|
pavone@20
|
1456 {
|
pavone@822
|
1457 uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START;
|
pavone@822
|
1458 //line 0x1FF is basically active even though it's not displayed
|
pavone@822
|
1459 uint8_t active_slot = context->vcounter < inactive_start || context->vcounter == 0x1FF;
|
pavone@822
|
1460 uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40;
|
pavone@822
|
1461 if (context->regs[REG_MODE_2] & DISPLAY_ENABLE && active_slot) {
|
pavone@822
|
1462 if (is_h40) {
|
pavone@822
|
1463 vdp_h40(context, target_cycles);
|
pavone@822
|
1464 } else {
|
pavone@822
|
1465 vdp_h32(context, target_cycles);
|
pavone@647
|
1466 }
|
pavone@647
|
1467 } else {
|
pavone@622
|
1468 if (is_h40) {
|
pavone@922
|
1469 if (context->hslot == 161) {
|
pavone@822
|
1470 context->cur_slot = MAX_DRAWS-1;
|
pavone@822
|
1471 memset(context->linebuf, 0, LINEBUF_SIZE);
|
pavone@922
|
1472 } else if (context->hslot == 165) {
|
pavone@822
|
1473 context->sprite_index = 0x80;
|
pavone@822
|
1474 context->slot_counter = MAX_SPRITES_LINE;
|
pavone@503
|
1475 }
|
pavone@20
|
1476 } else {
|
pavone@923
|
1477 if (context->hslot == 128) {
|
pavone@822
|
1478 context->cur_slot = MAX_DRAWS_H32-1;
|
pavone@822
|
1479 memset(context->linebuf, 0, LINEBUF_SIZE);
|
pavone@923
|
1480 } else if (context->hslot == 132) {
|
pavone@822
|
1481 context->sprite_index = 0x80;
|
pavone@822
|
1482 context->slot_counter = MAX_SPRITES_LINE_H32;
|
pavone@822
|
1483 }
|
pavone@20
|
1484 }
|
pavone@822
|
1485 if(context->vcounter == inactive_start) {
|
pavone@822
|
1486 uint32_t intslot = context->regs[REG_MODE_4] & BIT_H40 ? VINT_SLOT_H40 : VINT_SLOT_H32;
|
pavone@822
|
1487 if (context->hslot == intslot) {
|
pavone@822
|
1488 context->flags2 |= FLAG2_VINT_PENDING;
|
pavone@822
|
1489 context->pending_vint_start = context->cycles;
|
pavone@822
|
1490 }
|
pavone@822
|
1491 }
|
pavone@822
|
1492 uint32_t inccycles;
|
pavone@822
|
1493 if (is_h40) {
|
pavone@922
|
1494 if (context->hslot == 182) {
|
pavone@922
|
1495 inccycles = h40_hsync_cycles[0];
|
pavone@922
|
1496 } else if (context->hslot < HSYNC_SLOT_H40 || context->hslot >= HSYNC_END_H40) {
|
pavone@822
|
1497 inccycles = MCLKS_SLOT_H40;
|
pavone@822
|
1498 } else {
|
pavone@822
|
1499 inccycles = h40_hsync_cycles[context->hslot-HSYNC_SLOT_H40];
|
pavone@822
|
1500 }
|
pavone@822
|
1501 } else {
|
pavone@822
|
1502 inccycles = MCLKS_SLOT_H32;
|
pavone@822
|
1503 }
|
pavone@822
|
1504 if (!is_refresh(context, context->hslot)) {
|
pavone@54
|
1505 external_slot(context);
|
pavone@54
|
1506 }
|
pavone@822
|
1507 if (context->vcounter < inactive_start) {
|
pavone@822
|
1508 check_render_bg(context, context->vcounter, context->hslot);
|
pavone@54
|
1509 }
|
pavone@822
|
1510 if (context->flags & FLAG_DMA_RUN && !is_refresh(context, context->hslot)) {
|
pavone@822
|
1511 run_dma_src(context, context->hslot);
|
pavone@822
|
1512 }
|
pavone@822
|
1513 context->cycles += inccycles;
|
pavone@622
|
1514 context->hslot++;
|
pavone@622
|
1515 if (is_h40) {
|
pavone@647
|
1516 if (context->hslot == LINE_CHANGE_H40) {
|
pavone@717
|
1517 vdp_advance_line(context);
|
pavone@699
|
1518 if (context->vcounter == (inactive_start + 8)) {
|
pavone@699
|
1519 context->frame++;
|
pavone@699
|
1520 }
|
pavone@622
|
1521 } else if (context->hslot == 183) {
|
pavone@622
|
1522 context->hslot = 229;
|
pavone@622
|
1523 }
|
pavone@622
|
1524 } else {
|
pavone@647
|
1525 if (context->hslot == LINE_CHANGE_H32) {
|
pavone@717
|
1526 vdp_advance_line(context);
|
pavone@699
|
1527 if (context->vcounter == (inactive_start + 8)) {
|
pavone@699
|
1528 context->frame++;
|
pavone@699
|
1529 }
|
pavone@622
|
1530 } else if (context->hslot == 148) {
|
pavone@622
|
1531 context->hslot = 233;
|
pavone@622
|
1532 }
|
pavone@622
|
1533 }
|
pavone@622
|
1534 }
|
pavone@20
|
1535 }
|
pavone@20
|
1536 }
|
pavone@20
|
1537
|
pavone@20
|
1538 uint32_t vdp_run_to_vblank(vdp_context * context)
|
pavone@20
|
1539 {
|
pavone@622
|
1540 uint32_t target_cycles = ((context->latched_mode & BIT_PAL) ? PAL_INACTIVE_START : NTSC_INACTIVE_START) * MCLKS_LINE;
|
pavone@20
|
1541 vdp_run_context(context, target_cycles);
|
pavone@20
|
1542 return context->cycles;
|
pavone@20
|
1543 }
|
pavone@20
|
1544
|
pavone@75
|
1545 void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles)
|
pavone@75
|
1546 {
|
pavone@75
|
1547 for(;;) {
|
pavone@75
|
1548 uint32_t dmalen = (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L];
|
pavone@75
|
1549 if (!dmalen) {
|
pavone@75
|
1550 dmalen = 0x10000;
|
pavone@75
|
1551 }
|
pavone@622
|
1552 uint32_t min_dma_complete = dmalen * (context->regs[REG_MODE_4] & BIT_H40 ? 16 : 20);
|
pavone@75
|
1553 if ((context->regs[REG_DMASRC_H] & 0xC0) == 0xC0 || (context->cd & 0xF) == VRAM_WRITE) {
|
pavone@75
|
1554 //DMA copies take twice as long to complete since they require a read and a write
|
pavone@75
|
1555 //DMA Fills and transfers to VRAM also take twice as long as it requires 2 writes for a single word
|
pavone@75
|
1556 min_dma_complete *= 2;
|
pavone@75
|
1557 }
|
pavone@75
|
1558 min_dma_complete += context->cycles;
|
pavone@75
|
1559 if (target_cycles < min_dma_complete) {
|
pavone@75
|
1560 vdp_run_context(context, target_cycles);
|
pavone@75
|
1561 return;
|
pavone@75
|
1562 } else {
|
pavone@75
|
1563 vdp_run_context(context, min_dma_complete);
|
pavone@75
|
1564 if (!(context->flags & FLAG_DMA_RUN)) {
|
pavone@75
|
1565 return;
|
pavone@75
|
1566 }
|
pavone@75
|
1567 }
|
pavone@75
|
1568 }
|
pavone@75
|
1569 }
|
pavone@75
|
1570
|
pavone@75
|
1571 int vdp_control_port_write(vdp_context * context, uint16_t value)
|
pavone@54
|
1572 {
|
pavone@471
|
1573 //printf("control port write: %X at %d\n", value, context->cycles);
|
pavone@149
|
1574 if (context->flags & FLAG_DMA_RUN) {
|
pavone@149
|
1575 return -1;
|
pavone@149
|
1576 }
|
pavone@54
|
1577 if (context->flags & FLAG_PENDING) {
|
pavone@54
|
1578 context->address = (context->address & 0x3FFF) | (value << 14);
|
pavone@983
|
1579 //It seems like the DMA enable bit doesn't so much enable DMA so much
|
pavone@983
|
1580 //as it enables changing CD5 from control port writes
|
pavone@983
|
1581 uint8_t preserve = (context->regs[REG_MODE_2] & BIT_DMA_ENABLE) ? 0x3 : 0x23;
|
pavone@983
|
1582 context->cd = (context->cd & preserve) | ((value >> 2) & ~preserve & 0xFF);
|
pavone@75
|
1583 context->flags &= ~FLAG_PENDING;
|
pavone@980
|
1584 //Should these be taken care of here or after the first write?
|
pavone@980
|
1585 context->flags &= ~FLAG_READ_FETCHED;
|
pavone@980
|
1586 context->flags2 &= ~FLAG2_READ_PENDING;
|
pavone@453
|
1587 //printf("New Address: %X, New CD: %X\n", context->address, context->cd);
|
pavone@983
|
1588 if (context->cd & 0x20) {
|
pavone@327
|
1589 //
|
pavone@75
|
1590 if((context->regs[REG_DMASRC_H] & 0xC0) != 0x80) {
|
pavone@75
|
1591 //DMA copy or 68K -> VDP, transfer starts immediately
|
pavone@75
|
1592 context->flags |= FLAG_DMA_RUN;
|
pavone@131
|
1593 context->dma_cd = context->cd;
|
pavone@717
|
1594 //printf("DMA start (length: %X) at cycle %d, frame: %d, vcounter: %d, hslot: %d\n", (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L], context->cycles, context->frame, context->vcounter, context->hslot);
|
pavone@75
|
1595 if (!(context->regs[REG_DMASRC_H] & 0x80)) {
|
pavone@327
|
1596 //printf("DMA Address: %X, New CD: %X, Source: %X, Length: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L]);
|
pavone@75
|
1597 return 1;
|
pavone@327
|
1598 } else {
|
pavone@327
|
1599 //printf("DMA Copy Address: %X, New CD: %X, Source: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]);
|
pavone@75
|
1600 }
|
pavone@327
|
1601 } else {
|
pavone@453
|
1602 //printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd);
|
pavone@75
|
1603 }
|
pavone@63
|
1604 }
|
pavone@54
|
1605 } else {
|
pavone@54
|
1606 if ((value & 0xC000) == 0x8000) {
|
pavone@54
|
1607 //Register write
|
pavone@54
|
1608 uint8_t reg = (value >> 8) & 0x1F;
|
pavone@475
|
1609 if (reg < (context->regs[REG_MODE_2] & BIT_MODE_5 ? VDP_REGS : 0xA)) {
|
pavone@453
|
1610 //printf("register %d set to %X\n", reg, value & 0xFF);
|
pavone@480
|
1611 if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) {
|
pavone@480
|
1612 context->hv_latch = vdp_hv_counter_read(context);
|
pavone@480
|
1613 }
|
pavone@505
|
1614 if (reg == REG_BG_COLOR) {
|
pavone@505
|
1615 value &= 0x3F;
|
pavone@505
|
1616 }
|
pavone@718
|
1617 /*if (reg == REG_MODE_4 && ((value ^ context->regs[reg]) & BIT_H40)) {
|
pavone@717
|
1618 printf("Mode changed from H%d to H%d @ %d, frame: %d\n", context->regs[reg] & BIT_H40 ? 40 : 32, value & BIT_H40 ? 40 : 32, context->cycles, context->frame);
|
pavone@718
|
1619 }*/
|
pavone@54
|
1620 context->regs[reg] = value;
|
pavone@413
|
1621 if (reg == REG_MODE_4) {
|
pavone@413
|
1622 context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
|
pavone@415
|
1623 if (!context->double_res) {
|
pavone@415
|
1624 context->framebuf = context->oddbuf;
|
pavone@415
|
1625 }
|
pavone@983
|
1626 }
|
pavone@476
|
1627 context->cd &= 0x3C;
|
pavone@54
|
1628 }
|
pavone@54
|
1629 } else {
|
pavone@54
|
1630 context->flags |= FLAG_PENDING;
|
pavone@54
|
1631 context->address = (context->address &0xC000) | (value & 0x3FFF);
|
pavone@54
|
1632 context->cd = (context->cd &0x3C) | (value >> 14);
|
pavone@980
|
1633 //Should these be taken care of here or after the second write?
|
pavone@980
|
1634 //context->flags &= ~FLAG_READ_FETCHED;
|
pavone@980
|
1635 //context->flags2 &= ~FLAG2_READ_PENDING;
|
pavone@54
|
1636 }
|
pavone@54
|
1637 }
|
pavone@75
|
1638 return 0;
|
pavone@54
|
1639 }
|
pavone@54
|
1640
|
pavone@149
|
1641 int vdp_data_port_write(vdp_context * context, uint16_t value)
|
pavone@54
|
1642 {
|
pavone@471
|
1643 //printf("data port write: %X at %d\n", value, context->cycles);
|
pavone@460
|
1644 if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) {
|
pavone@149
|
1645 return -1;
|
pavone@149
|
1646 }
|
pavone@980
|
1647 if (context->flags & FLAG_PENDING) {
|
pavone@980
|
1648 context->flags &= ~FLAG_PENDING;
|
pavone@980
|
1649 //Should these be cleared here?
|
pavone@980
|
1650 context->flags &= ~FLAG_READ_FETCHED;
|
pavone@980
|
1651 context->flags2 &= ~FLAG2_READ_PENDING;
|
pavone@980
|
1652 }
|
pavone@109
|
1653 /*if (context->fifo_cur == context->fifo_end) {
|
pavone@54
|
1654 printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
|
pavone@109
|
1655 }*/
|
pavone@460
|
1656 if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
|
pavone@460
|
1657 context->flags &= ~FLAG_DMA_RUN;
|
pavone@460
|
1658 }
|
pavone@471
|
1659 while (context->fifo_write == context->fifo_read) {
|
pavone@622
|
1660 vdp_run_context(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
|
pavone@54
|
1661 }
|
pavone@471
|
1662 fifo_entry * cur = context->fifo + context->fifo_write;
|
pavone@622
|
1663 cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
|
pavone@471
|
1664 cur->address = context->address;
|
pavone@471
|
1665 cur->value = value;
|
pavone@471
|
1666 cur->cd = context->cd;
|
pavone@471
|
1667 cur->partial = 0;
|
pavone@471
|
1668 if (context->fifo_read < 0) {
|
pavone@471
|
1669 context->fifo_read = context->fifo_write;
|
pavone@471
|
1670 }
|
pavone@471
|
1671 context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1);
|
pavone@138
|
1672 context->address += context->regs[REG_AUTOINC];
|
pavone@149
|
1673 return 0;
|
pavone@54
|
1674 }
|
pavone@54
|
1675
|
pavone@470
|
1676 void vdp_test_port_write(vdp_context * context, uint16_t value)
|
pavone@470
|
1677 {
|
pavone@470
|
1678 //TODO: implement test register
|
pavone@470
|
1679 }
|
pavone@470
|
1680
|
pavone@54
|
1681 uint16_t vdp_control_port_read(vdp_context * context)
|
pavone@54
|
1682 {
|
pavone@54
|
1683 context->flags &= ~FLAG_PENDING;
|
pavone@980
|
1684 //Bits 15-10 are not fixed like Charles MacDonald's doc suggests, but instead open bus values that reflect 68K prefetch
|
pavone@981
|
1685 uint16_t value = get_open_bus_value() & 0xFC00;
|
pavone@471
|
1686 if (context->fifo_read < 0) {
|
pavone@54
|
1687 value |= 0x200;
|
pavone@54
|
1688 }
|
pavone@471
|
1689 if (context->fifo_read == context->fifo_write) {
|
pavone@54
|
1690 value |= 0x100;
|
pavone@54
|
1691 }
|
pavone@317
|
1692 if (context->flags2 & FLAG2_VINT_PENDING) {
|
pavone@413
|
1693 value |= 0x80;
|
pavone@413
|
1694 }
|
pavone@494
|
1695 if (context->flags & FLAG_DOT_OFLOW) {
|
pavone@494
|
1696 value |= 0x40;
|
pavone@494
|
1697 }
|
pavone@494
|
1698 if (context->flags2 & FLAG2_SPRITE_COLLIDE) {
|
pavone@494
|
1699 value |= 0x20;
|
pavone@494
|
1700 //TODO: Test when this is actually cleared
|
pavone@494
|
1701 context->flags2 &= ~FLAG2_SPRITE_COLLIDE;
|
pavone@494
|
1702 }
|
pavone@413
|
1703 if ((context->regs[REG_MODE_4] & BIT_INTERLACE) && context->framebuf == context->oddbuf) {
|
pavone@413
|
1704 value |= 0x10;
|
pavone@317
|
1705 }
|
pavone@622
|
1706 uint32_t line= context->vcounter;
|
pavone@622
|
1707 uint32_t slot = context->hslot;
|
pavone@717
|
1708 uint32_t inactive_start = (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START);
|
pavone@921
|
1709 if ((line >= inactive_start && line < 0x1FF) || !(context->regs[REG_MODE_2] & BIT_DISP_EN)) {
|
pavone@318
|
1710 value |= 0x8;
|
pavone@318
|
1711 }
|
pavone@622
|
1712 if (context->regs[REG_MODE_4] & BIT_H40) {
|
pavone@622
|
1713 if (slot < HBLANK_END_H40 || slot > HBLANK_START_H40) {
|
pavone@622
|
1714 value |= 0x4;
|
pavone@622
|
1715 }
|
pavone@622
|
1716 } else {
|
pavone@622
|
1717 if (slot < HBLANK_END_H32 || slot > HBLANK_START_H32) {
|
pavone@622
|
1718 value |= 0x4;
|
pavone@622
|
1719 }
|
pavone@318
|
1720 }
|
pavone@983
|
1721 if (context->cd & 0x20) {
|
pavone@141
|
1722 value |= 0x2;
|
pavone@75
|
1723 }
|
pavone@714
|
1724 if (context->flags2 & FLAG2_REGION_PAL) {
|
pavone@317
|
1725 value |= 0x1;
|
pavone@317
|
1726 }
|
pavone@459
|
1727 //printf("status read at cycle %d returned %X\n", context->cycles, value);
|
pavone@54
|
1728 return value;
|
pavone@54
|
1729 }
|
pavone@54
|
1730
|
pavone@54
|
1731 uint16_t vdp_data_port_read(vdp_context * context)
|
pavone@54
|
1732 {
|
pavone@980
|
1733 if (context->flags & FLAG_PENDING) {
|
pavone@980
|
1734 context->flags &= ~FLAG_PENDING;
|
pavone@980
|
1735 //Should these be cleared here?
|
pavone@980
|
1736 context->flags &= ~FLAG_READ_FETCHED;
|
pavone@980
|
1737 context->flags2 &= ~FLAG2_READ_PENDING;
|
pavone@980
|
1738 }
|
pavone@138
|
1739 if (context->cd & 1) {
|
pavone@54
|
1740 return 0;
|
pavone@54
|
1741 }
|
pavone@980
|
1742 while (!(context->flags & FLAG_READ_FETCHED)) {
|
pavone@622
|
1743 vdp_run_context(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20));
|
pavone@54
|
1744 }
|
pavone@980
|
1745 context->flags &= ~FLAG_READ_FETCHED;
|
pavone@980
|
1746 //Should this happen after the prefetch or after the read?
|
pavone@54
|
1747 context->address += context->regs[REG_AUTOINC];
|
pavone@980
|
1748 return context->prefetch;
|
pavone@54
|
1749 }
|
pavone@54
|
1750
|
pavone@137
|
1751 uint16_t vdp_hv_counter_read(vdp_context * context)
|
pavone@137
|
1752 {
|
pavone@480
|
1753 if (context->regs[REG_MODE_1] & BIT_HVC_LATCH) {
|
pavone@480
|
1754 return context->hv_latch;
|
pavone@480
|
1755 }
|
pavone@622
|
1756 uint32_t line= context->vcounter & 0xFF;
|
pavone@647
|
1757 uint32_t linecyc = context->hslot;
|
pavone@137
|
1758 linecyc &= 0xFF;
|
pavone@413
|
1759 if (context->double_res) {
|
pavone@413
|
1760 line <<= 1;
|
pavone@413
|
1761 if (line & 0x100) {
|
pavone@413
|
1762 line |= 1;
|
pavone@413
|
1763 }
|
pavone@413
|
1764 }
|
pavone@137
|
1765 return (line << 8) | linecyc;
|
pavone@137
|
1766 }
|
pavone@137
|
1767
|
pavone@470
|
1768 uint16_t vdp_test_port_read(vdp_context * context)
|
pavone@470
|
1769 {
|
pavone@470
|
1770 //TODO: Find out what actually gets returned here
|
pavone@470
|
1771 return 0xFFFF;
|
pavone@470
|
1772 }
|
pavone@470
|
1773
|
pavone@65
|
1774 void vdp_adjust_cycles(vdp_context * context, uint32_t deduction)
|
pavone@65
|
1775 {
|
pavone@65
|
1776 context->cycles -= deduction;
|
pavone@717
|
1777 if (context->pending_vint_start >= deduction) {
|
pavone@717
|
1778 context->pending_vint_start -= deduction;
|
pavone@717
|
1779 } else {
|
pavone@717
|
1780 context->pending_vint_start = 0;
|
pavone@717
|
1781 }
|
pavone@717
|
1782 if (context->pending_hint_start >= deduction) {
|
pavone@717
|
1783 context->pending_hint_start -= deduction;
|
pavone@717
|
1784 } else {
|
pavone@717
|
1785 context->pending_hint_start = 0;
|
pavone@717
|
1786 }
|
pavone@471
|
1787 if (context->fifo_read >= 0) {
|
pavone@471
|
1788 int32_t idx = context->fifo_read;
|
pavone@471
|
1789 do {
|
pavone@471
|
1790 if (context->fifo[idx].cycle >= deduction) {
|
pavone@471
|
1791 context->fifo[idx].cycle -= deduction;
|
pavone@471
|
1792 } else {
|
pavone@471
|
1793 context->fifo[idx].cycle = 0;
|
pavone@471
|
1794 }
|
pavone@471
|
1795 idx = (idx+1) & (FIFO_SIZE-1);
|
pavone@471
|
1796 } while(idx != context->fifo_write);
|
pavone@65
|
1797 }
|
pavone@65
|
1798 }
|
pavone@65
|
1799
|
pavone@717
|
1800 uint32_t vdp_cycles_hslot_wrap_h40(vdp_context * context)
|
pavone@717
|
1801 {
|
pavone@717
|
1802 if (context->hslot < 183) {
|
pavone@717
|
1803 return MCLKS_LINE - context->hslot * MCLKS_SLOT_H40;
|
pavone@717
|
1804 } else if (context->hslot < HSYNC_END_H40) {
|
pavone@717
|
1805 uint32_t before_hsync = context->hslot < HSYNC_SLOT_H40 ? (HSYNC_SLOT_H40 - context->hslot) * MCLKS_SLOT_H40 : 0;
|
pavone@717
|
1806 uint32_t hsync = 0;
|
pavone@717
|
1807 for (int i = context->hslot <= HSYNC_SLOT_H40 ? 0 : context->hslot - HSYNC_SLOT_H40; i < sizeof(h40_hsync_cycles)/sizeof(uint32_t); i++)
|
pavone@717
|
1808 {
|
pavone@717
|
1809 hsync += h40_hsync_cycles[i];
|
pavone@717
|
1810 }
|
pavone@717
|
1811 uint32_t after_hsync = (256- HSYNC_END_H40) * MCLKS_SLOT_H40;
|
pavone@717
|
1812 return before_hsync + hsync + after_hsync;
|
pavone@717
|
1813 } else {
|
pavone@717
|
1814 return (256-context->hslot) * MCLKS_SLOT_H40;
|
pavone@717
|
1815 }
|
pavone@717
|
1816 }
|
pavone@717
|
1817
|
pavone@623
|
1818 uint32_t vdp_cycles_next_line(vdp_context * context)
|
pavone@623
|
1819 {
|
pavone@623
|
1820 if (context->regs[REG_MODE_4] & BIT_H40) {
|
pavone@647
|
1821 if (context->hslot < LINE_CHANGE_H40) {
|
pavone@697
|
1822 return (LINE_CHANGE_H40 - context->hslot) * MCLKS_SLOT_H40;
|
pavone@623
|
1823 } else {
|
pavone@717
|
1824 return vdp_cycles_hslot_wrap_h40(context) + LINE_CHANGE_H40 * MCLKS_SLOT_H40;
|
pavone@623
|
1825 }
|
pavone@623
|
1826 } else {
|
pavone@647
|
1827 if (context->hslot < LINE_CHANGE_H32) {
|
pavone@647
|
1828 return (LINE_CHANGE_H32 - context->hslot) * MCLKS_SLOT_H32;
|
pavone@623
|
1829 } else if (context->hslot < 148) {
|
pavone@647
|
1830 return MCLKS_LINE - (context->hslot - LINE_CHANGE_H32) * MCLKS_SLOT_H32;
|
pavone@623
|
1831 } else {
|
pavone@647
|
1832 return (256-context->hslot + LINE_CHANGE_H32) * MCLKS_SLOT_H32;
|
pavone@623
|
1833 }
|
pavone@623
|
1834 }
|
pavone@623
|
1835 }
|
pavone@623
|
1836
|
pavone@623
|
1837 uint32_t vdp_cycles_to_line(vdp_context * context, uint32_t target)
|
pavone@623
|
1838 {
|
pavone@623
|
1839 uint32_t jump_start, jump_dst;
|
pavone@623
|
1840 if (context->flags2 & FLAG2_REGION_PAL) {
|
pavone@623
|
1841 if (context->latched_mode & BIT_PAL) {
|
pavone@623
|
1842 jump_start = 0x10B;
|
pavone@623
|
1843 jump_dst = 0x1D2;
|
pavone@623
|
1844 } else {
|
pavone@623
|
1845 jump_start = 0x103;
|
pavone@623
|
1846 jump_dst = 0x1CA;
|
pavone@623
|
1847 }
|
pavone@623
|
1848 } else {
|
pavone@623
|
1849 if (context->latched_mode & BIT_PAL) {
|
pavone@623
|
1850 jump_start = 0;
|
pavone@623
|
1851 jump_dst = 0;
|
pavone@623
|
1852 } else {
|
pavone@623
|
1853 jump_start = 0xEB;
|
pavone@623
|
1854 jump_dst = 0x1E5;
|
pavone@623
|
1855 }
|
pavone@623
|
1856 }
|
pavone@623
|
1857 uint32_t lines;
|
pavone@623
|
1858 if (context->vcounter < target) {
|
pavone@623
|
1859 if (target < jump_start) {
|
pavone@623
|
1860 lines = target - context->vcounter;
|
pavone@623
|
1861 } else {
|
pavone@623
|
1862 lines = jump_start - context->vcounter + target - jump_dst;
|
pavone@623
|
1863 }
|
pavone@623
|
1864 } else {
|
pavone@623
|
1865 if (context->vcounter < jump_start) {
|
pavone@718
|
1866 lines = jump_start - context->vcounter + 512 - jump_dst;
|
pavone@623
|
1867 } else {
|
pavone@718
|
1868 lines = 512 - context->vcounter;
|
pavone@623
|
1869 }
|
pavone@623
|
1870 if (target < jump_start) {
|
pavone@623
|
1871 lines += target;
|
pavone@623
|
1872 } else {
|
pavone@623
|
1873 lines += jump_start + target - jump_dst;
|
pavone@623
|
1874 }
|
pavone@623
|
1875 }
|
pavone@623
|
1876 return MCLKS_LINE * (lines - 1) + vdp_cycles_next_line(context);
|
pavone@623
|
1877 }
|
pavone@623
|
1878
|
pavone@680
|
1879 uint32_t vdp_frame_end_line(vdp_context * context)
|
pavone@623
|
1880 {
|
pavone@623
|
1881 uint32_t frame_end;
|
pavone@623
|
1882 if (context->flags2 & FLAG2_REGION_PAL) {
|
pavone@623
|
1883 if (context->latched_mode & BIT_PAL) {
|
pavone@623
|
1884 frame_end = PAL_INACTIVE_START + 8;
|
pavone@623
|
1885 } else {
|
pavone@623
|
1886 frame_end = NTSC_INACTIVE_START + 8;
|
pavone@623
|
1887 }
|
pavone@623
|
1888 } else {
|
pavone@623
|
1889 if (context->latched_mode & BIT_PAL) {
|
pavone@623
|
1890 frame_end = 512;
|
pavone@623
|
1891 } else {
|
pavone@623
|
1892 frame_end = NTSC_INACTIVE_START + 8;
|
pavone@623
|
1893 }
|
pavone@623
|
1894 }
|
pavone@680
|
1895 return frame_end;
|
pavone@680
|
1896 }
|
pavone@680
|
1897
|
pavone@680
|
1898 uint32_t vdp_cycles_to_frame_end(vdp_context * context)
|
pavone@680
|
1899 {
|
pavone@680
|
1900 return context->cycles + vdp_cycles_to_line(context, vdp_frame_end_line(context));
|
pavone@680
|
1901 }
|
pavone@680
|
1902
|
pavone@317
|
1903 uint32_t vdp_next_hint(vdp_context * context)
|
pavone@317
|
1904 {
|
pavone@327
|
1905 if (!(context->regs[REG_MODE_1] & BIT_HINT_EN)) {
|
pavone@317
|
1906 return 0xFFFFFFFF;
|
pavone@317
|
1907 }
|
pavone@317
|
1908 if (context->flags2 & FLAG2_HINT_PENDING) {
|
pavone@717
|
1909 return context->pending_hint_start;
|
pavone@317
|
1910 }
|
pavone@622
|
1911 uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START;
|
pavone@623
|
1912 uint32_t hint_line;
|
pavone@708
|
1913 if (context->vcounter + context->hint_counter >= inactive_start) {
|
pavone@724
|
1914 if (context->regs[REG_HINT] > inactive_start) {
|
pavone@724
|
1915 return 0xFFFFFFFF;
|
pavone@724
|
1916 }
|
pavone@623
|
1917 hint_line = context->regs[REG_HINT];
|
pavone@623
|
1918 } else {
|
pavone@623
|
1919 hint_line = context->vcounter + context->hint_counter + 1;
|
pavone@317
|
1920 }
|
pavone@623
|
1921
|
pavone@623
|
1922 return context->cycles + vdp_cycles_to_line(context, hint_line);
|
pavone@317
|
1923 }
|
pavone@317
|
1924
|
pavone@317
|
1925 uint32_t vdp_next_vint(vdp_context * context)
|
pavone@317
|
1926 {
|
pavone@327
|
1927 if (!(context->regs[REG_MODE_2] & BIT_VINT_EN)) {
|
pavone@317
|
1928 return 0xFFFFFFFF;
|
pavone@317
|
1929 }
|
pavone@317
|
1930 if (context->flags2 & FLAG2_VINT_PENDING) {
|
pavone@717
|
1931 return context->pending_vint_start;
|
pavone@317
|
1932 }
|
pavone@623
|
1933
|
pavone@623
|
1934
|
pavone@623
|
1935 return vdp_next_vint_z80(context);
|
pavone@317
|
1936 }
|
pavone@317
|
1937
|
pavone@333
|
1938 uint32_t vdp_next_vint_z80(vdp_context * context)
|
pavone@333
|
1939 {
|
pavone@622
|
1940 uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START;
|
pavone@623
|
1941 if (context->vcounter == inactive_start) {
|
pavone@623
|
1942 if (context->regs[REG_MODE_4] & BIT_H40) {
|
pavone@699
|
1943 if (context->hslot >= LINE_CHANGE_H40) {
|
pavone@717
|
1944 return context->cycles + vdp_cycles_hslot_wrap_h40(context) + VINT_SLOT_H40 * MCLKS_SLOT_H40;
|
pavone@717
|
1945 } else if (context->hslot <= VINT_SLOT_H40) {
|
pavone@623
|
1946 return context->cycles + (VINT_SLOT_H40 - context->hslot) * MCLKS_SLOT_H40;
|
pavone@623
|
1947 }
|
pavone@623
|
1948 } else {
|
pavone@699
|
1949 if (context->hslot >= LINE_CHANGE_H32) {
|
pavone@623
|
1950 if (context->hslot < 148) {
|
pavone@623
|
1951 return context->cycles + (VINT_SLOT_H32 + 148 - context->hslot + 256 - 233) * MCLKS_SLOT_H32;
|
pavone@623
|
1952 } else {
|
pavone@623
|
1953 return context->cycles + (VINT_SLOT_H32 + 256 - context->hslot) * MCLKS_SLOT_H32;
|
pavone@623
|
1954 }
|
pavone@717
|
1955 } else if (context->hslot <= VINT_SLOT_H32) {
|
pavone@623
|
1956 return context->cycles + (VINT_SLOT_H32 - context->hslot) * MCLKS_SLOT_H32;
|
pavone@623
|
1957 }
|
pavone@623
|
1958 }
|
pavone@623
|
1959 }
|
pavone@623
|
1960 int32_t cycles_to_vint = vdp_cycles_to_line(context, inactive_start);
|
pavone@622
|
1961 if (context->regs[REG_MODE_4] & BIT_H40) {
|
pavone@717
|
1962 cycles_to_vint += MCLKS_LINE - (LINE_CHANGE_H40 - VINT_SLOT_H40) * MCLKS_SLOT_H40;
|
pavone@333
|
1963 } else {
|
pavone@699
|
1964 cycles_to_vint += (VINT_SLOT_H32 + 148 - LINE_CHANGE_H32 + 256 - 233) * MCLKS_SLOT_H32;
|
pavone@333
|
1965 }
|
pavone@623
|
1966 return context->cycles + cycles_to_vint;
|
pavone@333
|
1967 }
|
pavone@333
|
1968
|
pavone@953
|
1969 void vdp_int_ack(vdp_context * context)
|
pavone@317
|
1970 {
|
pavone@952
|
1971 //Apparently the VDP interrupt controller is not very smart
|
pavone@952
|
1972 //Instead of paying attention to what interrupt is being acknowledged it just
|
pavone@952
|
1973 //clears the pending flag for whatever interrupt it is currently asserted
|
pavone@952
|
1974 //which may be different from the interrupt it was asserting when the 68k
|
pavone@952
|
1975 //started the interrupt process. The window for this is narrow and depends
|
pavone@952
|
1976 //on the latency between the int enable register write and the interrupt being
|
pavone@952
|
1977 //asserted, but Fatal Rewind depends on this due to some buggy code
|
pavone@952
|
1978 if ((context->flags2 & FLAG2_VINT_PENDING) && (context->regs[REG_MODE_2] & BIT_VINT_EN)) {
|
pavone@317
|
1979 context->flags2 &= ~FLAG2_VINT_PENDING;
|
pavone@952
|
1980 } else if((context->flags2 & FLAG2_HINT_PENDING) && (context->regs[REG_MODE_1] & BIT_HINT_EN)) {
|
pavone@317
|
1981 context->flags2 &= ~FLAG2_HINT_PENDING;
|
pavone@317
|
1982 }
|
pavone@317
|
1983 }
|
pavone@317
|
1984
|