Mercurial > repos > blastem
comparison lc8951.c @ 2062:07ed42bd7b4c segacd
Some progress on CDC and CDD emulation. Now passes first 3 "CDC INIT" tests in mcd-verificator
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 28 Jan 2022 00:50:17 -0800 |
parents | 70260f6051dd |
children | 02a9846668d1 |
comparison
equal
deleted
inserted
replaced
2061:7c1760b5b3e5 | 2062:07ed42bd7b4c |
---|---|
1 #include "lc8951.h" | 1 #include "lc8951.h" |
2 #include "backend.h" | |
2 | 3 |
3 enum { | 4 enum { |
4 COMIN, | 5 COMIN, |
5 IFSTAT, | 6 IFSTAT, |
6 DBCL, | 7 DBCL, |
34 }; | 35 }; |
35 | 36 |
36 //IFCTRL | 37 //IFCTRL |
37 #define BIT_CMDIEN 0x80 | 38 #define BIT_CMDIEN 0x80 |
38 #define BIT_DTEIEN 0x40 | 39 #define BIT_DTEIEN 0x40 |
39 #define BIT_CECIEN 0x20 | 40 #define BIT_DECIEN 0x20 |
40 #define BIT_CMDBK 0x10 | 41 #define BIT_CMDBK 0x10 |
41 #define BIT_DTWAI 0x08 | 42 #define BIT_DTWAI 0x08 |
42 #define BIT_STWAI 0x04 | 43 #define BIT_STWAI 0x04 |
43 #define BIT_DOUTEN 0x02 | 44 #define BIT_DOUTEN 0x02 |
44 #define BIT_SOUTEN 0x01 | 45 #define BIT_SOUTEN 0x01 |
50 #define BIT_DTBSY 0x08 | 51 #define BIT_DTBSY 0x08 |
51 #define BIT_STBSY 0x04 | 52 #define BIT_STBSY 0x04 |
52 #define BIT_DTEN 0x02 | 53 #define BIT_DTEN 0x02 |
53 #define BIT_STEN 0x01 | 54 #define BIT_STEN 0x01 |
54 | 55 |
56 //CTRL0 | |
57 #define BIT_DECEN 0x80 | |
58 #define BIT_WRRQ 0x04 | |
59 | |
60 //STAT3 | |
61 #define BIT_VALST 0x80 | |
62 | |
55 //datasheet timing info | 63 //datasheet timing info |
56 //3 cycles for memory operation | 64 //3 cycles for memory operation |
57 //6 cycles min for DMA-mode host transfer | 65 //6 cycles min for DMA-mode host transfer |
58 | 66 |
59 void lc8951_init(lc8951 *context) | 67 void lc8951_init(lc8951 *context) |
60 { | 68 { |
61 //This seems to vary somewhat between Sega CD models | 69 //This seems to vary somewhat between Sega CD models |
62 //unclear if the difference is in the lc8951 or gate array | 70 //unclear if the difference is in the lc8951 or gate array |
63 context->regs[IFSTAT] = 0xFF; | 71 context->regs[IFSTAT] = 0xFF; |
64 context->ar_mask = 0x1F; | 72 context->ar_mask = 0x1F; |
73 context->clock_step = (2 + 2) * 6; // external divider, internal divider + DMA period | |
65 } | 74 } |
66 | 75 |
67 void lc8951_reg_write(lc8951 *context, uint8_t value) | 76 void lc8951_reg_write(lc8951 *context, uint8_t value) |
68 { | 77 { |
78 printf("CDC write %X: %X\n", context->ar, value); | |
69 switch (context->ar) | 79 switch (context->ar) |
70 { | 80 { |
71 case SBOUT: | 81 case SBOUT: |
72 context->regs[context->ar] = value; | 82 context->regs[context->ar] = value; |
73 if (context->ifctrl & BIT_SOUTEN) { | 83 if (context->ifctrl & BIT_SOUTEN) { |
98 context->dac |= value << 8; | 108 context->dac |= value << 8; |
99 break; | 109 break; |
100 case DTTRG: | 110 case DTTRG: |
101 if (value & BIT_DOUTEN) { | 111 if (value & BIT_DOUTEN) { |
102 context->regs[IFSTAT] &= ~BIT_DTBSY; | 112 context->regs[IFSTAT] &= ~BIT_DTBSY; |
113 uint16_t transfer_size = context->regs[DBCL] | (context->regs[DBCH] << 8); | |
114 context->transfer_end = context->cycle + transfer_size * context->clock_step; | |
103 } | 115 } |
104 break; | 116 break; |
105 case DTACK: | 117 case DTACK: |
106 context->regs[IFSTAT] |= BIT_DTEI; | 118 context->regs[IFSTAT] |= BIT_DTEI; |
107 break; | 119 break; |
109 context->regs[WAL] = value; | 121 context->regs[WAL] = value; |
110 break; | 122 break; |
111 case WAH_WRITE: | 123 case WAH_WRITE: |
112 context->regs[WAH] = value; | 124 context->regs[WAH] = value; |
113 break; | 125 break; |
126 case CTRL0: | |
127 context->ctrl0 = value; | |
128 break; | |
129 case CTRL1: | |
130 context->ctrl1 = value; | |
131 break; | |
114 case PTL_WRITE: | 132 case PTL_WRITE: |
115 context->regs[PTL] = value; | 133 context->regs[PTL] = value; |
116 break; | 134 break; |
117 case PTH_WRITE: | 135 case PTH_WRITE: |
118 context->regs[PTH] = value; | 136 context->regs[PTH] = value; |
137 context->ptl_internal = (context->regs[PTL] | (context->regs[PTH] << 8)) & (sizeof(context->buffer) - 1); | |
138 context->decoding = 1; | |
139 context->decode_end = context->cycle + 2352 * context->clock_step * 4; | |
119 break; | 140 break; |
120 case RESET: | 141 case RESET: |
121 context->comin_count = 0; | 142 context->comin_count = 0; |
122 context->regs[IFSTAT] = 0xFF; | 143 context->regs[IFSTAT] = 0xFF; |
123 break; | 144 break; |
150 if (context->ar >= sizeof(context->regs)) { | 171 if (context->ar >= sizeof(context->regs)) { |
151 value = 0xFF; | 172 value = 0xFF; |
152 } else { | 173 } else { |
153 value = context->regs[context->ar]; | 174 value = context->regs[context->ar]; |
154 } | 175 } |
176 printf("CDC read %X: %X\n", context->ar, value); | |
155 context->ar++; | 177 context->ar++; |
156 context->ar &= context->ar_mask; | 178 context->ar &= context->ar_mask; |
157 return value; | 179 return value; |
158 } | 180 } |
159 | 181 |
160 void lc8951_ar_write(lc8951 *context, uint8_t value) | 182 void lc8951_ar_write(lc8951 *context, uint8_t value) |
161 { | 183 { |
162 context->ar = value & context->ar_mask; | 184 context->ar = value & context->ar_mask; |
163 } | 185 } |
186 | |
187 //25 MHz clock input (1/2 SCD MCLK) | |
188 //internal /2 divider | |
189 //3 cycles for each SRAM access (though might be crystal frequency rather than internal frequency) | |
190 //6 cycle period for DMA transfer out | |
191 // | |
192 | |
193 void lc8951_run(lc8951 *context, uint32_t cycle) | |
194 { | |
195 for(; context->cycle < cycle; context->cycle += context->clock_step) | |
196 { | |
197 if (context->cycle >= context->decode_end) { | |
198 context->decode_end = CYCLE_NEVER; | |
199 context->regs[IFSTAT] &= ~BIT_DECI; | |
200 context->regs[STAT3] &= ~BIT_VALST; | |
201 uint16_t block_start = (context->regs[PTL] | (context->regs[PTH] << 8)) & (sizeof(context->buffer)-1); | |
202 for (int reg = HEAD0; reg < PTL; reg++) | |
203 { | |
204 printf("Setting HEAD%d to buffer[%X]\n", reg - HEAD0, block_start); | |
205 context->regs[reg] =context->buffer[block_start++]; | |
206 block_start &= (sizeof(context->buffer)-1); | |
207 } | |
208 printf("Decode done %X:%X:%X mode %X\n", context->regs[HEAD0], context->regs[HEAD1], context->regs[HEAD2], context->regs[HEAD3]); | |
209 } | |
210 if (context->transfer_end != CYCLE_NEVER) { | |
211 //TODO: transfer byte to gate array destination | |
212 context->dac++; | |
213 context->regs[DBCL]--; | |
214 if (!context->regs[DBCL]) { | |
215 context->regs[DBCH]--; | |
216 if (!context->regs[DBCH]) { | |
217 context->regs[IFSTAT] &= ~BIT_DTEI; | |
218 if (context->cycle != context->transfer_end) { | |
219 printf("Expected transfer end at %u but ended at %u\n", context->transfer_end, context->cycle); | |
220 } | |
221 context->transfer_end = CYCLE_NEVER; | |
222 } | |
223 } | |
224 } | |
225 } | |
226 } | |
227 | |
228 void lc8951_write_byte(lc8951 *context, uint32_t cycle, int sector_offset, uint8_t byte) | |
229 { | |
230 lc8951_run(context, cycle); | |
231 uint16_t current_write_addr = context->regs[WAL] | (context->regs[WAH] << 8); | |
232 if (sector_offset == 12) { | |
233 //we've recevied the sync pattern for the next block | |
234 | |
235 //header/status regs no longer considered "valid" | |
236 context->regs[STAT3] |= BIT_VALST; | |
237 if ((context->ctrl0 & (BIT_DECEN|BIT_WRRQ)) == (BIT_DECEN|BIT_WRRQ)) { | |
238 uint16_t block_start = current_write_addr - 2352; | |
239 context->regs[PTL] = block_start; | |
240 context->regs[PTH] = block_start >> 8; | |
241 printf("Decoding block starting at %X\n", block_start); | |
242 context->ptl_internal = block_start & (sizeof(context->buffer)-1); | |
243 context->decode_end = context->cycle + 2352 * context->clock_step * 4; | |
244 } | |
245 } | |
246 if (sector_offset >= 12 && sector_offset < 16) { | |
247 //TODO: Handle SHDREN = 1 | |
248 if ((context->ctrl0 & (BIT_DECEN|BIT_WRRQ)) == (BIT_DECEN)) { | |
249 //monitor only mode | |
250 context->regs[HEAD0 + sector_offset - 12] = byte; | |
251 } | |
252 } | |
253 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; | |
256 context->regs[WAL]++; | |
257 if (!context->regs[WAL]) { | |
258 context->regs[WAH]++; | |
259 } | |
260 } | |
261 } | |
262 | |
263 uint32_t lc8951_next_interrupt(lc8951 *context) | |
264 { | |
265 if ((!context->regs[IFSTAT]) & context->ifctrl & (BIT_CMDI|BIT_DTEI|BIT_DECI)) { | |
266 //interrupt already pending | |
267 return context->cycle; | |
268 } | |
269 uint32_t deci_cycle = CYCLE_NEVER; | |
270 if (context->ifctrl & BIT_DECI) { | |
271 deci_cycle = context->decode_end; | |
272 } | |
273 uint32_t dtei_cycle = CYCLE_NEVER; | |
274 if (context->ifctrl & BIT_DTEI) { | |
275 dtei_cycle = context->transfer_end; | |
276 } | |
277 return deci_cycle < dtei_cycle ? deci_cycle : dtei_cycle; | |
278 } |