some more SVP work
[picodrive.git] / Pico / carthw / svp / ssp16.c
1 // basic, incomplete SSP160x (SSP1601?) interpreter
2
3 /*
4  * Register info
5  * most names taken from MAME code
6  *
7  * 0. "-"
8  *   size: 16
9  *   desc: Constant register with all bits set (0xffff).
10  *
11  * 1. "X"
12  *   size: 16
13  *   desc: Generic register. When set, updates P (P = X * Y * 2) ??
14  *
15  * 2. "Y"
16  *   size: 16
17  *   desc: Generic register. When set, updates P (P = X * Y * 2) ??
18  *
19  * 3. "A"
20  *   size: 32
21  *   desc: Accumulator.
22  *
23  * 4. "ST"
24  *   size: 16
25  *   desc: Status register. From MAME: bits 0-9 are CONTROL, other FLAG
26  *     fedc ba98 7654 3210
27  *       210 - RPL (?)       (e: "loop size", fir16_32.sc)
28  *       43  - RB (?)
29  *       5   - GP0_0 (ST5?)  Changed before acessing AL (affects banking?).
30  *       6   - GP0_1 (ST6?)  Cleared before acessing AL (affects banking?). Set after.
31  *       7   - IE (?)        Not used by SVP code (never set, but preserved)?
32  *       8   - OP (?)        Not used by SVP code (only cleared)?
33  *       9   - MACS (?)      Not used by SVP code (only cleared)? (e: "mac shift")
34  *       a   - GPI_0         Interrupt 0 enable/status?
35  *       b   - GPI_1         Interrupt 1 enable/status?
36  *       c   - L             L flag. Carry?
37  *       d   - Z             Zero flag.
38  *       e   - OV            Overflow flag.
39  *       f   - N             Negative flag.
40  *     seen directly changing code sequences:
41  *       ldi ST, 0      ld  A, ST     ld  A, ST     ld  A, ST     ldi st, 20h
42  *       ldi ST, 60h    ori A, 60h    and A, E8h    and A, E8h
43  *                      ld  ST, A     ld  ST, A     ori 3
44  *                                                  ld  ST, A
45  *
46  * 5. "STACK"
47  *   size: 16
48  *   desc: hw stack of 6 levels (according to datasheet)
49  *
50  * 6. "PC"
51  *   size: 16
52  *   desc: Program counter.
53  *
54  * 7. "P"
55  *   size: 32
56  *   desc: multiply result register. Updated after mp* instructions,
57  *         or writes to X or Y (P = X * Y * 2) ??
58  *         probably affected by MACS bit in ST.
59  *
60  * 8. "PM0" (PM from PMAR name from Tasco's docs)
61  *   size: 16?
62  *   desc: Programmable Memory access register.
63  *         On reset, or when one (both?) GP0 bits are clear,
64  *         acts as some additional status reg?
65  *
66  * 9. "PM1"
67  *   size: 16?
68  *   desc: Programmable Memory access register.
69  *         This reg. is only used as PMAR.
70  *
71  * 10. "PM2"
72  *   size: 16?
73  *   desc: Programmable Memory access register.
74  *         This reg. is only used as PMAR.
75  *
76  * 11. "XST"
77  *   size: 16?
78  *   desc: eXternal STate. Mapped to a15000 at 68k side.
79  *         Can be programmed as PMAR? (only seen in test mode code)
80  *
81  * 12. "PM4"
82  *   size: 16?
83  *   desc: Programmable Memory access register.
84  *         This reg. is only used as PMAR. The most used PMAR by VR.
85  *
86  * 13. (unused by VR)
87  *
88  * 14. "PMC" (PMC from PMAC name from Tasco's docs)
89  *   size: 32?
90  *   desc: Programmable Memory access Control. Set using 2 16bit writes,
91  *         first address, then mode word. After setting PMAC, PMAR sould
92  *         be accessed to program it.
93  *
94  * 15. "AL"
95  *   size: 16
96  *   desc: Accumulator Low. 16 least significant bits of accumulator (not 100% sure)
97  *         (normally reading acc (ld X, A) you get 16 most significant bits).
98  *
99  *
100  * There are 8 8-bit pointer registers rX. r0-r3 (ri) point to RAM0, r4-r7 (rj) point to RAM1.
101  * They can be accessed directly, or 2 indirection levels can be used [ (r0), ((r0)) ],
102  * which work similar to * and ** operators in C.
103  *
104  * r0,r1,r2,r4,r5,r6 can be modified [ex: ldi r0, 5].
105  * 3 modifiers can be applied (optional):
106  *  + : post-increment [ex: ld a, (r0+) ]
107  *  - : post-decrement
108  *  +!: same as '+' ???
109  *
110  * r3 and r7 are special and can not be changed (at least Samsung samples and SVP code never do).
111  * They are fixed to the start of their RAM banks. (They are probably changeable for ssp1605+,
112  * Samsung's old DSP page claims that).
113  * 1 of these 4 modifiers must be used (short form direct addressing?):
114  *  |00: RAMx[0] [ex: (r3|00), 0] (based on sample code)
115  *  |01: RAMx[1]
116  *  |10: RAMx[2] ? maybe 10h? accortding to Div_c_dp.sc, 2
117  *  |11: RAMx[3]
118  *
119  *
120  * Instruction notes
121  *
122  * mld (rj), (ri) [, b]
123  *   operation: A = 0; P = (rj) * (ri)
124  *   notes: based on IIR_4B.SC sample. flags? what is b???
125  *   TODO: figure out if (rj) and (ri) get loaded in X and Y
126  *
127  * mpya (rj), (ri) [, b]
128  *   name: multiply and add?
129  *   operation: A += P; P = (rj) * (ri)
130  *
131  * mpys (rj), (ri), b
132  *   name: multiply and subtract?
133  *   notes: not used by VR code.
134  *
135  *
136  * Assumptions in this code
137  *   P is not directly writeable
138  */
139
140 #include "../../PicoInt.h"
141
142 #define u32 unsigned int
143
144 // 0
145 #define rX     ssp->gr[SSP_X].h
146 #define rY     ssp->gr[SSP_Y].h
147 #define rA     ssp->gr[SSP_A].h
148 #define rST    ssp->gr[SSP_ST].h        // 4
149 #define rSTACK ssp->gr[SSP_STACK].h
150 #define rPC    ssp->gr[SSP_PC].h
151 #define rP     ssp->gr[SSP_P]
152 #define rPM0   ssp->gr[SSP_PM0].h       // 8
153 #define rPM1   ssp->gr[SSP_PM1].h
154 #define rPM2   ssp->gr[SSP_PM2].h
155 #define rXST   ssp->gr[SSP_XST].h
156 #define rPM4   ssp->gr[SSP_PM4].h       // 12
157 // 13
158 #define rPMC   ssp->gr[SSP_PMC]         // will keep addr in .h, mode in .l
159 #define rAL    ssp->gr[SSP_A].l
160
161 #define GET_PC() (PC - (unsigned short *)Pico.rom)
162 #define GET_PC_OFFS() ((unsigned int)PC - (unsigned int)Pico.rom)
163 #define SET_PC(d) PC = (unsigned short *)Pico.rom + d
164
165 #define REG_READ(r) (((r) <= 4) ? ssp->gr[r].h : read_handlers[r]())
166 #define REG_WRITE(r,d) { \
167         int r1 = r; \
168         if (r1 > 4) write_handlers[r1](d); \
169         else if (r1 > 0) ssp->gr[r1].h = d; \
170 }
171
172 static ssp1601_t *ssp = NULL;
173 static unsigned short *PC;
174 static int g_cycles;
175
176 // -----------------------------------------------------
177 // register i/o handlers
178
179 // 0-4, 13
180 static u32 read_unknown(void)
181 {
182         elprintf(EL_ANOMALY|EL_SVP, "ssp16: unknown read @ %04x", GET_PC_OFFS());
183         return 0;
184 }
185
186 static void write_unknown(u32 d)
187 {
188         elprintf(EL_ANOMALY|EL_SVP, "ssp16: unknown write @ %04x", GET_PC_OFFS());
189 }
190
191 // 5
192 static u32 read_STACK(void)
193 {
194         u32 d = 0;
195         if (rSTACK < 6) {
196                 d = ssp->stack[rSTACK];
197                 rSTACK++;
198         } else
199                 elprintf(EL_ANOMALY|EL_SVP, "ssp16: stack underflow! (%i) @ %04x", rSTACK, GET_PC_OFFS());
200         return d;
201 }
202
203 static void write_STACK(u32 d)
204 {
205         if (rSTACK > 0) {
206                 rSTACK--;
207                 ssp->stack[rSTACK] = d;
208         } else
209                 elprintf(EL_ANOMALY|EL_SVP, "ssp16: stack overflow! (%i) @ %04x", rSTACK, GET_PC_OFFS());
210 }
211
212 // 6
213 static u32 read_PC(void)
214 {
215         return GET_PC();
216 }
217
218 static void write_PC(u32 d)
219 {
220         SET_PC(d);
221         g_cycles--;
222 }
223
224 // 7
225 static u32 read_P(void)
226 {
227         rP.v = (u32)rX * rY * 2;
228         return rP.h;
229 }
230
231 static u32 pm_io(int reg, int write, u32 d)
232 {
233         if (ssp->emu_status & SSP_PMC_SET) {
234                 elprintf(EL_SVP, "PM%i (%c) set to %08x @ %04x", reg, write ? 'w' : 'r', rPMC.v, GET_PC_OFFS());
235                 ssp->pmac_read[write ? reg + 6 : reg] = rPMC.v;
236                 ssp->emu_status &= ~SSP_PMC_SET;
237                 return 0;
238         }
239
240         if (ssp->pmac_read[reg] != 0) {
241                 elprintf(EL_SVP, "PM%i %c @ %04x", reg, write ? 'w' : 'r', GET_PC_OFFS());
242                 // do something depending on mode
243                 return 0;
244         }
245
246         return (u32)-1;
247 }
248
249 // 8
250 static u32 read_PM0(void)
251 {
252         u32 d = pm_io(0, 0, 0);
253         if (d != (u32)-1) return d;
254         elprintf(EL_SVP, "PM0 raw r %04x @ %04x", rPM0, GET_PC_OFFS());
255         return rPM0;
256 }
257
258 static void write_PM0(u32 d)
259 {
260         u32 r = pm_io(0, 1, d);
261         if (r != (u32)-1) return;
262         elprintf(EL_SVP, "PM0 raw w %04x @ %04x", d, GET_PC_OFFS());
263         rPM0 = d;
264 }
265
266 // 9
267 static u32 read_PM1(void)
268 {
269         u32 d = pm_io(1, 0, 0);
270         if (d != (u32)-1) return d;
271         // can be removed?
272         elprintf(EL_SVP, "PM1 raw r %04x @ %04x", rPM1, GET_PC_OFFS());
273         return rPM0;
274 }
275
276 static void write_PM1(u32 d)
277 {
278         u32 r = pm_io(1, 1, d);
279         if (r != (u32)-1) return;
280         // can be removed?
281         elprintf(EL_SVP, "PM1 raw w %04x @ %04x", d, GET_PC_OFFS());
282         rPM0 = d;
283 }
284
285 // 10
286 static u32 read_PM2(void)
287 {
288         u32 d = pm_io(2, 0, 0);
289         if (d != (u32)-1) return d;
290         // can be removed?
291         elprintf(EL_SVP, "PM2 raw r %04x @ %04x", rPM2, GET_PC_OFFS());
292         return rPM0;
293 }
294
295 static void write_PM2(u32 d)
296 {
297         u32 r = pm_io(2, 1, d);
298         if (r != (u32)-1) return;
299         // can be removed?
300         elprintf(EL_SVP, "PM2 raw w %04x @ %04x", d, GET_PC_OFFS());
301         rPM0 = d;
302 }
303
304 // 11
305 static u32 read_XST(void)
306 {
307         // can be removed?
308         u32 d = pm_io(3, 0, 0);
309         if (d != (u32)-1) return d;
310
311         elprintf(EL_SVP, "XST raw r %04x @ %04x", rXST, GET_PC_OFFS());
312         return rPM0;
313 }
314
315 static void write_XST(u32 d)
316 {
317         // can be removed?
318         u32 r = pm_io(3, 1, d);
319         if (r != (u32)-1) return;
320
321         elprintf(EL_SVP, "XST raw w %04x @ %04x", d, GET_PC_OFFS());
322         rPM0 = d;
323 }
324
325 // 12
326 static u32 read_PM4(void)
327 {
328         u32 d = pm_io(4, 0, 0);
329         if (d != (u32)-1) return d;
330         // can be removed?
331         elprintf(EL_SVP, "PM4 raw r %04x @ %04x", rPM4, GET_PC_OFFS());
332         return rPM0;
333 }
334
335 static void write_PM4(u32 d)
336 {
337         u32 r = pm_io(4, 1, d);
338         if (r != (u32)-1) return;
339         // can be removed?
340         elprintf(EL_SVP, "PM4 raw w %04x @ %04x", d, GET_PC_OFFS());
341         rPM0 = d;
342 }
343
344 // 14
345 static u32 read_PMC(void)
346 {
347         if (ssp->emu_status & SSP_PMC_HAVE_ADDR) {
348                 if (ssp->emu_status & SSP_PMC_SET)
349                         elprintf(EL_ANOMALY|EL_SVP, "prev PMC not used @ %04x", GET_PC_OFFS());
350                 ssp->emu_status |= SSP_PMC_SET;
351                 return rPMC.l;
352         } else {
353                 ssp->emu_status |= SSP_PMC_HAVE_ADDR;
354                 return rPMC.h;
355         }
356 }
357
358 static void write_PMC(u32 d)
359 {
360         if (ssp->emu_status & SSP_PMC_HAVE_ADDR) {
361                 if (ssp->emu_status & SSP_PMC_SET)
362                         elprintf(EL_ANOMALY|EL_SVP, "prev PMC not used @ %04x", GET_PC_OFFS());
363                 ssp->emu_status |= SSP_PMC_SET;
364                 rPMC.l = d;
365         } else {
366                 ssp->emu_status |= SSP_PMC_HAVE_ADDR;
367                 rPMC.h = d;
368         }
369 }
370
371 // 15
372 static u32 read_AL(void)
373 {
374         // TODO: figure out what's up with those blind reads..
375         return rAL;
376 }
377
378 static void write_AL(u32 d)
379 {
380         rAL = d;
381 }
382
383
384 typedef u32 (*read_func_t)(void);
385 typedef void (*write_func_t)(u32 d);
386
387 static read_func_t read_handlers[16] =
388 {
389         read_unknown, read_unknown, read_unknown, read_unknown, // -, X, Y, A
390         read_unknown,   // 4 ST
391         read_STACK,
392         read_PC,
393         read_P,
394         read_PM0,       // 8
395         read_PM1,
396         read_PM2,
397         read_XST,
398         read_PM4,       // 12
399         read_unknown,   // 13 gr13
400         read_PMC,
401         read_AL
402 };
403
404 static write_func_t write_handlers[16] =
405 {
406         write_unknown, write_unknown, write_unknown, write_unknown, // -, X, Y, A
407         write_unknown,  // 4 ST
408         write_STACK,
409         write_PC,
410         write_unknown,  // 7 P
411         write_PM0,      // 8
412         write_PM1,
413         write_PM2,
414         write_XST,
415         write_PM4,      // 12
416         write_unknown,  // 13 gr13
417         write_PMC,
418         write_AL
419 };
420
421 void ssp1601_reset(ssp1601_t *l_ssp)
422 {
423         ssp = l_ssp;
424         ssp->emu_status = 0;
425         ssp->gr[SSP_GR0].v = 0xffff0000;
426         rPC = 0x400;
427         rSTACK = 6; // ? using descending stack
428 }
429
430
431 void ssp1601_run(int cycles)
432 {
433         int op;
434
435         SET_PC(rPC);
436         g_cycles = cycles;
437
438         while (g_cycles > 0)
439         {
440                 op = *PC;
441                 switch (op >> 9)
442                 {
443                         // ld d, s
444                         case 0:
445                                 if (op == 0) break; // nop
446                                 if (op == ((SSP_A<<4)|SSP_P)) { // A <- P
447                                         // not sure. MAME claims that only hi word is transfered.
448                                         read_P(); // update P
449                                         ssp->gr[SSP_A].v = ssp->gr[SSP_P].v;
450                                         break;
451                                 }
452                                 {
453                                         u32 d = REG_READ(op & 0x0f);
454                                         REG_WRITE((op & 0xf0) >> 4, d);
455                                 }
456                                 // flags?
457                                 break;
458
459                         default:
460                                 elprintf(EL_ANOMALY|EL_SVP, "ssp16: unhandled op %04x @ %04x", op, GET_PC_OFFS());
461                                 break;
462                 }
463                 g_cycles--;
464                 PC++;
465         }
466
467         read_P(); // update P
468         rPC = GET_PC();
469
470         if (ssp->gr[SSP_GR0].v != 0xffff0000)
471                 elprintf(EL_ANOMALY|EL_SVP, "ssp16: REG 0 corruption! %08x", ssp->gr[SSP_GR0].v);
472 }
473