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