Mercurial > repos > blastem
comparison lc8951.c @ 2065:02a9846668d1 segacd
Implement transfer of data from CDC to elsewhere. Other miscellaneous CDD/CDC improvements
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 29 Jan 2022 17:43:37 -0800 |
parents | 07ed42bd7b4c |
children | a61a8a87410c |
comparison
equal
deleted
inserted
replaced
2064:91e4d2fe5cd9 | 2065:02a9846668d1 |
---|---|
62 | 62 |
63 //datasheet timing info | 63 //datasheet timing info |
64 //3 cycles for memory operation | 64 //3 cycles for memory operation |
65 //6 cycles min for DMA-mode host transfer | 65 //6 cycles min for DMA-mode host transfer |
66 | 66 |
67 void lc8951_init(lc8951 *context) | 67 void lc8951_init(lc8951 *context, lcd8951_byte_recv_fun byte_handler, void *handler_data) |
68 { | 68 { |
69 //This seems to vary somewhat between Sega CD models | 69 //This seems to vary somewhat between Sega CD models |
70 //unclear if the difference is in the lc8951 or gate array | 70 //unclear if the difference is in the lc8951 or gate array |
71 context->regs[IFSTAT] = 0xFF; | 71 context->regs[IFSTAT] = 0xFF; |
72 context->ar_mask = 0x1F; | 72 context->ar_mask = 0x1F; |
73 context->clock_step = (2 + 2) * 6; // external divider, internal divider + DMA period | 73 context->clock_step = (2 + 2) * 6; // external divider, internal divider + DMA period |
74 context->byte_handler = byte_handler; | |
75 context->handler_data = handler_data; | |
76 context->decode_end = CYCLE_NEVER; | |
77 context->transfer_end = CYCLE_NEVER; | |
74 } | 78 } |
75 | 79 |
76 void lc8951_reg_write(lc8951 *context, uint8_t value) | 80 void lc8951_reg_write(lc8951 *context, uint8_t value) |
77 { | 81 { |
78 printf("CDC write %X: %X\n", context->ar, value); | 82 printf("CDC write %X: %X\n", context->ar, value); |
89 if (!(value & BIT_SOUTEN)) { | 93 if (!(value & BIT_SOUTEN)) { |
90 context->regs[IFSTAT] |= BIT_STBSY; | 94 context->regs[IFSTAT] |= BIT_STBSY; |
91 } | 95 } |
92 if (!(value & BIT_DOUTEN)) { | 96 if (!(value & BIT_DOUTEN)) { |
93 context->regs[IFSTAT] |= BIT_DTBSY; | 97 context->regs[IFSTAT] |= BIT_DTBSY; |
98 context->transfer_end = CYCLE_NEVER; | |
94 } | 99 } |
95 break; | 100 break; |
96 case DBCL: | 101 case DBCL: |
97 context->regs[context->ar] = value; | 102 context->regs[context->ar] = value; |
98 break; | 103 break; |
106 case DACH: | 111 case DACH: |
107 context->dac &= 0xFF; | 112 context->dac &= 0xFF; |
108 context->dac |= value << 8; | 113 context->dac |= value << 8; |
109 break; | 114 break; |
110 case DTTRG: | 115 case DTTRG: |
111 if (value & BIT_DOUTEN) { | 116 if (context->ifctrl & BIT_DOUTEN) { |
112 context->regs[IFSTAT] &= ~BIT_DTBSY; | 117 context->regs[IFSTAT] &= ~BIT_DTBSY; |
113 uint16_t transfer_size = context->regs[DBCL] | (context->regs[DBCH] << 8); | 118 uint16_t transfer_size = context->regs[DBCL] | (context->regs[DBCH] << 8); |
114 context->transfer_end = context->cycle + transfer_size * context->clock_step; | 119 context->transfer_end = context->cycle + transfer_size * context->clock_step; |
120 printf("DTTRG: size %u, cycle %u, end %u\n", transfer_size, context->cycle, context->transfer_end); | |
115 } | 121 } |
116 break; | 122 break; |
117 case DTACK: | 123 case DTACK: |
118 context->regs[IFSTAT] |= BIT_DTEI; | 124 context->regs[IFSTAT] |= BIT_DTEI; |
119 break; | 125 break; |
206 block_start &= (sizeof(context->buffer)-1); | 212 block_start &= (sizeof(context->buffer)-1); |
207 } | 213 } |
208 printf("Decode done %X:%X:%X mode %X\n", context->regs[HEAD0], context->regs[HEAD1], context->regs[HEAD2], context->regs[HEAD3]); | 214 printf("Decode done %X:%X:%X mode %X\n", context->regs[HEAD0], context->regs[HEAD1], context->regs[HEAD2], context->regs[HEAD3]); |
209 } | 215 } |
210 if (context->transfer_end != CYCLE_NEVER) { | 216 if (context->transfer_end != CYCLE_NEVER) { |
211 //TODO: transfer byte to gate array destination | 217 if (context->byte_handler(context->handler_data, context->buffer[context->dac & (sizeof(context->buffer)-1)])) { |
212 context->dac++; | 218 context->dac++; |
213 context->regs[DBCL]--; | 219 context->regs[DBCL]--; |
214 if (!context->regs[DBCL]) { | 220 if (context->regs[DBCL] == 0xFF) { |
215 context->regs[DBCH]--; | 221 context->regs[DBCH]--; |
216 if (!context->regs[DBCH]) { | 222 if (context->regs[DBCH] == 0xFF) { |
217 context->regs[IFSTAT] &= ~BIT_DTEI; | 223 context->regs[IFSTAT] &= ~BIT_DTEI; |
218 if (context->cycle != context->transfer_end) { | 224 context->regs[IFSTAT] |= BIT_DTBSY; |
219 printf("Expected transfer end at %u but ended at %u\n", context->transfer_end, context->cycle); | 225 if (context->cycle != context->transfer_end) { |
226 printf("Expected transfer end at %u but ended at %u\n", context->transfer_end, context->cycle); | |
227 } | |
228 context->transfer_end = CYCLE_NEVER; | |
220 } | 229 } |
221 context->transfer_end = CYCLE_NEVER; | |
222 } | 230 } |
231 } else { | |
232 // pause transfer | |
233 context->transfer_end = CYCLE_NEVER; | |
223 } | 234 } |
235 } | |
236 } | |
237 } | |
238 | |
239 void lc8951_resume_transfer(lc8951 *context) | |
240 { | |
241 if (context->transfer_end == CYCLE_NEVER && (context->ifctrl & BIT_DOUTEN)) { | |
242 uint16_t transfer_size = context->regs[DBCL] | (context->regs[DBCH] << 8); | |
243 if (transfer_size) { | |
244 context->transfer_end = context->cycle + transfer_size * context->clock_step; | |
245 printf("RESUME: size %u, cycle %u, end %u\n", transfer_size, context->cycle, context->transfer_end); | |
224 } | 246 } |
225 } | 247 } |
226 } | 248 } |
227 | 249 |
228 void lc8951_write_byte(lc8951 *context, uint32_t cycle, int sector_offset, uint8_t byte) | 250 void lc8951_write_byte(lc8951 *context, uint32_t cycle, int sector_offset, uint8_t byte) |
249 //monitor only mode | 271 //monitor only mode |
250 context->regs[HEAD0 + sector_offset - 12] = byte; | 272 context->regs[HEAD0 + sector_offset - 12] = byte; |
251 } | 273 } |
252 } | 274 } |
253 if ((context->ctrl0 & (BIT_DECEN|BIT_WRRQ)) == (BIT_DECEN|BIT_WRRQ)) { | 275 if ((context->ctrl0 & (BIT_DECEN|BIT_WRRQ)) == (BIT_DECEN|BIT_WRRQ)) { |
254 printf("lc8951_write_byte: %X - [%X] = %X\n", sector_offset, current_write_addr, byte); | |
255 context->buffer[current_write_addr & (sizeof(context->buffer)-1)] = byte; | 276 context->buffer[current_write_addr & (sizeof(context->buffer)-1)] = byte; |
256 context->regs[WAL]++; | 277 context->regs[WAL]++; |
257 if (!context->regs[WAL]) { | 278 if (!context->regs[WAL]) { |
258 context->regs[WAH]++; | 279 context->regs[WAH]++; |
259 } | 280 } |
260 } | 281 } |
261 } | 282 } |
262 | 283 |
263 uint32_t lc8951_next_interrupt(lc8951 *context) | 284 uint32_t lc8951_next_interrupt(lc8951 *context) |
264 { | 285 { |
265 if ((!context->regs[IFSTAT]) & context->ifctrl & (BIT_CMDI|BIT_DTEI|BIT_DECI)) { | 286 if ((~context->regs[IFSTAT]) & context->ifctrl & (BIT_CMDI|BIT_DTEI|BIT_DECI)) { |
266 //interrupt already pending | 287 //interrupt already pending |
267 return context->cycle; | 288 return context->cycle; |
268 } | 289 } |
269 uint32_t deci_cycle = CYCLE_NEVER; | 290 uint32_t deci_cycle = CYCLE_NEVER; |
270 if (context->ifctrl & BIT_DECI) { | 291 if (context->ifctrl & BIT_DECI) { |