Mercurial > repos > blastem
comparison vdp.c @ 75:108e587165c0
Implement DMA (untested)
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 21 Dec 2012 20:56:32 -0800 |
parents | aef6302770c2 |
children | 2c7267617d71 |
comparison
equal
deleted
inserted
replaced
74:6396dc91f61e | 75:108e587165c0 |
---|---|
1 #include "vdp.h" | 1 #include "vdp.h" |
2 #include "blastem.h" | |
2 #include <stdlib.h> | 3 #include <stdlib.h> |
3 #include <string.h> | 4 #include <string.h> |
4 | 5 |
5 #define NTSC_ACTIVE 225 | 6 #define NTSC_ACTIVE 225 |
6 #define PAL_ACTIVE 241 | 7 #define PAL_ACTIVE 241 |
7 #define BUF_BIT_PRIORITY 0x40 | 8 #define BUF_BIT_PRIORITY 0x40 |
8 #define MAP_BIT_PRIORITY 0x8000 | 9 #define MAP_BIT_PRIORITY 0x8000 |
9 #define MAP_BIT_H_FLIP 0x800 | 10 #define MAP_BIT_H_FLIP 0x800 |
10 #define MAP_BIT_V_FLIP 0x1000 | 11 #define MAP_BIT_V_FLIP 0x1000 |
11 | 12 |
12 #define BIT_PAL 0x8 | 13 #define BIT_PAL 0x8 |
13 #define BIT_H40 0x1 | 14 #define BIT_DMA_ENABLE 0x4 |
15 #define BIT_H40 0x1 | |
14 | 16 |
15 #define SCROLL_BUFFER_SIZE 32 | 17 #define SCROLL_BUFFER_SIZE 32 |
16 #define SCROLL_BUFFER_DRAW 16 | 18 #define SCROLL_BUFFER_DRAW 16 |
17 | |
18 #define FLAG_DOT_OFLOW 0x1 | |
19 #define FLAG_CAN_MASK 0x2 | |
20 #define FLAG_MASKED 0x4 | |
21 #define FLAG_WINDOW 0x8 | |
22 #define FLAG_PENDING 0x10 | |
23 #define FLAG_UNUSED_SLOT 0x20 | |
24 | 19 |
25 #define FIFO_SIZE 4 | 20 #define FIFO_SIZE 4 |
26 | 21 |
27 void init_vdp_context(vdp_context * context) | 22 void init_vdp_context(vdp_context * context) |
28 { | 23 { |
180 #define VRAM_WRITE 1 | 175 #define VRAM_WRITE 1 |
181 #define CRAM_READ 8 | 176 #define CRAM_READ 8 |
182 #define CRAM_WRITE 3 | 177 #define CRAM_WRITE 3 |
183 #define VSRAM_READ 4 | 178 #define VSRAM_READ 4 |
184 #define VSRAM_WRITE 5 | 179 #define VSRAM_WRITE 5 |
180 #define DMA_START 0x20 | |
185 | 181 |
186 void external_slot(vdp_context * context) | 182 void external_slot(vdp_context * context) |
187 { | 183 { |
188 fifo_entry * start = (context->fifo_end - FIFO_SIZE); | 184 //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode |
189 //TODO: Implement DMA | 185 //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations |
190 if (context->fifo_cur != start && start->cycle <= context->cycles) { | 186 //TODO: Figure out what happens if DMA gets disabled part way through a DMA fill or DMA copy |
191 switch (context->cd & 0x7) | 187 if((context->regs[REG_MODE_2] & BIT_DMA_ENABLE) && (context->flags & FLAG_DMA_RUN)) { |
188 uint16_t dma_len; | |
189 switch(context->regs[REG_DMASRC_H] & 0xC0) | |
192 { | 190 { |
193 case VRAM_WRITE: | 191 //68K -> VDP |
194 if (start->partial) { | 192 case 0: |
195 //printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1); | 193 case 0x40: |
196 context->vdpmem[context->address ^ 1] = start->value; | 194 switch(context->cd & 0xF) |
195 { | |
196 case VRAM_WRITE: | |
197 if (context->flags & FLAG_DMA_PROG) { | |
198 context->vdpmem[context->address ^ 1] = context->dma_val; | |
199 context->flags &= ~FLAG_DMA_PROG; | |
200 } else { | |
201 context->dma_val = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); | |
202 context->vdpmem[context->address] = context->dma_val >> 8; | |
203 context->flags |= FLAG_DMA_PROG; | |
204 } | |
205 break; | |
206 case CRAM_WRITE: | |
207 context->cram[(context->address/2) & (CRAM_SIZE-1)] = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); | |
208 break; | |
209 case VSRAM_WRITE: | |
210 if (((context->address/2) & 63) < VSRAM_SIZE) { | |
211 context->vsram[(context->address/2) & 63] = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); | |
212 } | |
213 break; | |
214 } | |
215 break; | |
216 //Fill | |
217 case 0x80: | |
218 switch(context->cd & 0xF) | |
219 { | |
220 case VRAM_WRITE: | |
221 //Charles MacDonald's VDP doc says that the low byte gets written first | |
222 //this doesn't make a lot of sense to me, but until I've had a change to | |
223 //verify it myself, I'll assume it's true | |
224 if (context->flags & FLAG_DMA_PROG) { | |
225 context->vdpmem[context->address ^ 1] = context->dma_val >> 8; | |
226 context->flags &= ~FLAG_DMA_PROG; | |
227 } else { | |
228 context->dma_val = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); | |
229 context->vdpmem[context->address] = context->dma_val; | |
230 context->flags |= FLAG_DMA_PROG; | |
231 } | |
232 break; | |
233 case CRAM_WRITE: | |
234 context->cram[(context->address/2) & (CRAM_SIZE-1)] = context->dma_val; | |
235 break; | |
236 case VSRAM_WRITE: | |
237 if (((context->address/2) & 63) < VSRAM_SIZE) { | |
238 context->vsram[(context->address/2) & 63] = context->dma_val; | |
239 } | |
240 break; | |
241 } | |
242 break; | |
243 //Copy | |
244 case 0xC0: | |
245 if (context->flags & FLAG_DMA_PROG) { | |
246 switch(context->cd & 0xF) | |
247 { | |
248 case VRAM_WRITE: | |
249 context->vdpmem[context->address] = context->dma_val; | |
250 break; | |
251 case CRAM_WRITE: | |
252 context->cram[(context->address/2) & (CRAM_SIZE-1)] = context->dma_val; | |
253 break; | |
254 case VSRAM_WRITE: | |
255 if (((context->address/2) & 63) < VSRAM_SIZE) { | |
256 context->vsram[(context->address/2) & 63] = context->dma_val; | |
257 } | |
258 break; | |
259 } | |
260 context->flags &= ~FLAG_DMA_PROG; | |
197 } else { | 261 } else { |
198 //printf("VRAM Write High: %X to %X\n", start->value >> 8, context->address); | 262 //I assume, that DMA copy copies from the same RAM as the destination |
199 context->vdpmem[context->address] = start->value >> 8; | 263 //but it's possible I'm mistaken |
200 start->partial = 1; | 264 switch(context->cd & 0xF) |
201 //skip auto-increment and removal of entry from fifo | 265 { |
202 return; | 266 case VRAM_WRITE: |
267 context->dma_val = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]]; | |
268 break; | |
269 case CRAM_WRITE: | |
270 context->dma_val = context->cram[context->regs[REG_DMASRC_L] & (CRAM_SIZE-1)]; | |
271 break; | |
272 case VSRAM_WRITE: | |
273 if ((context->regs[REG_DMASRC_L] & 63) < VSRAM_SIZE) { | |
274 context->dma_val = context->vsram[context->regs[REG_DMASRC_L] & 63]; | |
275 } else { | |
276 context->dma_val = 0; | |
277 } | |
278 break; | |
279 } | |
280 context->flags |= FLAG_DMA_PROG; | |
203 } | 281 } |
204 break; | 282 break; |
205 case CRAM_WRITE: | 283 } |
206 //printf("CRAM Write: %X to %X\n", start->value, context->address); | 284 if (!(context->flags & FLAG_DMA_PROG)) { |
207 context->cram[(context->address/2) & (CRAM_SIZE-1)] = start->value; | 285 context->address += context->regs[REG_AUTOINC]; |
208 break; | 286 context->regs[REG_DMASRC_L] += 1; |
209 case VSRAM_WRITE: | 287 dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1; |
210 if (((context->address/2) & 63) < VSRAM_SIZE) { | 288 context->regs[REG_DMALEN_H] = dma_len >> 8; |
211 //printf("VSRAM Write: %X to %X\n", start->value, context->address); | 289 context->regs[REG_DMALEN_L] = dma_len; |
212 context->vsram[(context->address/2) & 63] = start->value; | 290 if (!dma_len) { |
213 } | 291 context->flags &= ~FLAG_DMA_RUN; |
214 break; | 292 } |
215 } | 293 } |
216 context->address += context->regs[REG_AUTOINC]; | |
217 fifo_entry * cur = start+1; | |
218 if (cur < context->fifo_cur) { | |
219 memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur)); | |
220 } | |
221 context->fifo_cur -= 1; | |
222 } else { | 294 } else { |
223 context->flags |= FLAG_UNUSED_SLOT; | 295 fifo_entry * start = (context->fifo_end - FIFO_SIZE); |
296 if (context->fifo_cur != start && start->cycle <= context->cycles) { | |
297 if ((context->regs[REG_MODE_2] & BIT_DMA_ENABLE) && (context->cd & DMA_START)) { | |
298 context->flags |= FLAG_DMA_RUN; | |
299 context->dma_val = start->value; | |
300 } else { | |
301 switch (context->cd & 0xF) | |
302 { | |
303 case VRAM_WRITE: | |
304 if (start->partial) { | |
305 //printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1); | |
306 context->vdpmem[context->address ^ 1] = start->value; | |
307 } else { | |
308 //printf("VRAM Write High: %X to %X\n", start->value >> 8, context->address); | |
309 context->vdpmem[context->address] = start->value >> 8; | |
310 start->partial = 1; | |
311 //skip auto-increment and removal of entry from fifo | |
312 return; | |
313 } | |
314 break; | |
315 case CRAM_WRITE: | |
316 //printf("CRAM Write: %X to %X\n", start->value, context->address); | |
317 context->cram[(context->address/2) & (CRAM_SIZE-1)] = start->value; | |
318 break; | |
319 case VSRAM_WRITE: | |
320 if (((context->address/2) & 63) < VSRAM_SIZE) { | |
321 //printf("VSRAM Write: %X to %X\n", start->value, context->address); | |
322 context->vsram[(context->address/2) & 63] = start->value; | |
323 } | |
324 break; | |
325 } | |
326 context->address += context->regs[REG_AUTOINC]; | |
327 } | |
328 fifo_entry * cur = start+1; | |
329 if (cur < context->fifo_cur) { | |
330 memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur)); | |
331 } | |
332 context->fifo_cur -= 1; | |
333 } else { | |
334 context->flags |= FLAG_UNUSED_SLOT; | |
335 } | |
224 } | 336 } |
225 } | 337 } |
226 | 338 |
227 #define WINDOW_RIGHT 0x80 | 339 #define WINDOW_RIGHT 0x80 |
228 #define WINDOW_DOWN 0x80 | 340 #define WINDOW_DOWN 0x80 |
792 void latch_mode(vdp_context * context) | 904 void latch_mode(vdp_context * context) |
793 { | 905 { |
794 context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL); | 906 context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL); |
795 } | 907 } |
796 | 908 |
797 #define DISPLAY_ENABLE 0x40 | |
798 | |
799 int is_refresh(vdp_context * context) | 909 int is_refresh(vdp_context * context) |
800 { | 910 { |
801 uint32_t linecyc = context->cycles % MCLKS_LINE; | 911 uint32_t linecyc = context->cycles % MCLKS_LINE; |
802 if (context->latched_mode & BIT_H40) { | 912 if (context->latched_mode & BIT_H40) { |
803 linecyc = linecyc/16; | 913 linecyc = linecyc/16; |
884 uint32_t target_cycles = ((context->latched_mode & BIT_PAL) ? PAL_ACTIVE : NTSC_ACTIVE) * MCLKS_LINE; | 994 uint32_t target_cycles = ((context->latched_mode & BIT_PAL) ? PAL_ACTIVE : NTSC_ACTIVE) * MCLKS_LINE; |
885 vdp_run_context(context, target_cycles); | 995 vdp_run_context(context, target_cycles); |
886 return context->cycles; | 996 return context->cycles; |
887 } | 997 } |
888 | 998 |
889 void vdp_control_port_write(vdp_context * context, uint16_t value) | 999 void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles) |
1000 { | |
1001 for(;;) { | |
1002 uint32_t dmalen = (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]; | |
1003 if (!dmalen) { | |
1004 dmalen = 0x10000; | |
1005 } | |
1006 uint32_t min_dma_complete = dmalen * (context->latched_mode & BIT_H40 ? 16 : 20); | |
1007 if ((context->regs[REG_DMASRC_H] & 0xC0) == 0xC0 || (context->cd & 0xF) == VRAM_WRITE) { | |
1008 //DMA copies take twice as long to complete since they require a read and a write | |
1009 //DMA Fills and transfers to VRAM also take twice as long as it requires 2 writes for a single word | |
1010 min_dma_complete *= 2; | |
1011 } | |
1012 min_dma_complete += context->cycles; | |
1013 if (target_cycles < min_dma_complete) { | |
1014 vdp_run_context(context, target_cycles); | |
1015 return; | |
1016 } else { | |
1017 vdp_run_context(context, min_dma_complete); | |
1018 if (!(context->flags & FLAG_DMA_RUN)) { | |
1019 return; | |
1020 } | |
1021 } | |
1022 } | |
1023 } | |
1024 | |
1025 int vdp_control_port_write(vdp_context * context, uint16_t value) | |
890 { | 1026 { |
891 //printf("control port write: %X\n", value); | 1027 //printf("control port write: %X\n", value); |
892 if (context->flags & FLAG_PENDING) { | 1028 if (context->flags & FLAG_PENDING) { |
893 context->address = (context->address & 0x3FFF) | (value << 14); | 1029 context->address = (context->address & 0x3FFF) | (value << 14); |
894 context->cd = (context->cd & 0x3) | ((value >> 2) & 0x3C); | 1030 context->cd = (context->cd & 0x3) | ((value >> 2) & 0x3C); |
895 if (context->cd & 0x30) { | 1031 context->flags &= ~FLAG_PENDING; |
896 puts("attempt to use DMA detected!"); | |
897 } | |
898 //printf("New Address: %X, New CD: %X\n", context->address, context->cd); | 1032 //printf("New Address: %X, New CD: %X\n", context->address, context->cd); |
899 context->flags &= ~FLAG_PENDING; | 1033 if (context->cd & 0x20) { |
1034 if((context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { | |
1035 //DMA copy or 68K -> VDP, transfer starts immediately | |
1036 context->flags |= FLAG_DMA_RUN; | |
1037 if (!(context->regs[REG_DMASRC_H] & 0x80)) { | |
1038 return 1; | |
1039 } | |
1040 } | |
1041 } | |
900 } else { | 1042 } else { |
901 if ((value & 0xC000) == 0x8000) { | 1043 if ((value & 0xC000) == 0x8000) { |
902 //Register write | 1044 //Register write |
903 uint8_t reg = (value >> 8) & 0x1F; | 1045 uint8_t reg = (value >> 8) & 0x1F; |
904 if (reg < VDP_REGS) { | 1046 if (reg < VDP_REGS) { |
912 context->flags |= FLAG_PENDING; | 1054 context->flags |= FLAG_PENDING; |
913 context->address = (context->address &0xC000) | (value & 0x3FFF); | 1055 context->address = (context->address &0xC000) | (value & 0x3FFF); |
914 context->cd = (context->cd &0x3C) | (value >> 14); | 1056 context->cd = (context->cd &0x3C) | (value >> 14); |
915 } | 1057 } |
916 } | 1058 } |
1059 return 0; | |
917 } | 1060 } |
918 | 1061 |
919 void vdp_data_port_write(vdp_context * context, uint16_t value) | 1062 void vdp_data_port_write(vdp_context * context, uint16_t value) |
920 { | 1063 { |
921 //printf("data port write: %X\n", value); | 1064 //printf("data port write: %X\n", value); |
939 if (context->fifo_cur == (context->fifo_end - FIFO_SIZE)) { | 1082 if (context->fifo_cur == (context->fifo_end - FIFO_SIZE)) { |
940 value |= 0x200; | 1083 value |= 0x200; |
941 } | 1084 } |
942 if (context->fifo_cur == context->fifo_end) { | 1085 if (context->fifo_cur == context->fifo_end) { |
943 value |= 0x100; | 1086 value |= 0x100; |
1087 } | |
1088 if (context->flags & FLAG_DMA_RUN) { | |
1089 value |= 0x20; | |
944 } | 1090 } |
945 //TODO: Lots of other bits in status port | 1091 //TODO: Lots of other bits in status port |
946 return value; | 1092 return value; |
947 } | 1093 } |
948 | 1094 |