1 // basic, incomplete SSP160x (SSP1601?) interpreter
5 * most names taken from MAME code
9 * desc: Constant register with all bits set (0xffff).
13 * desc: Generic register. When set, updates P (P = X * Y * 2) ??
17 * desc: Generic register. When set, updates P (P = X * Y * 2) ??
25 * desc: Status register. From MAME: bits 0-9 are CONTROL, other FLAG
27 * 210 - RPL (?) (e: "loop size", fir16_32.sc)
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?
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
48 * desc: hw stack of 6 levels (according to datasheet)
52 * desc: Program counter.
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.
60 * 8. "PM0" (PM from PMAR name from Tasco's docs)
62 * desc: Programmable Memory access register.
63 * On reset, or when one (both?) GP0 bits are clear,
64 * acts as some additional status reg?
68 * desc: Programmable Memory access register.
69 * This reg. is only used as PMAR.
73 * desc: Programmable Memory access register.
74 * This reg. is only used as PMAR.
78 * desc: eXternal STate. Mapped to a15000 at 68k side.
79 * Can be programmed as PMAR? (only seen in test mode code)
83 * desc: Programmable Memory access register.
84 * This reg. is only used as PMAR. The most used PMAR by VR.
88 * 14. "PMC" (PMC from PMAC name from Tasco's docs)
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.
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).
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.
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+) ]
108 * +!: same as '+' ???
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)
116 * |10: RAMx[2] ? maybe 10h? accortding to Div_c_dp.sc, 2
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
127 * mpya (rj), (ri) [, b]
128 * name: multiply and add?
129 * operation: A += P; P = (rj) * (ri)
132 * name: multiply and subtract?
133 * notes: not used by VR code.
136 * Assumptions in this code
137 * P is not directly writeable
140 #include "../../PicoInt.h"
142 #define u32 unsigned int
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
158 #define rPMC ssp->gr[SSP_PMC] // will keep addr in .h, mode in .l
159 #define rAL ssp->gr[SSP_A].l
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
165 #define REG_READ(r) (((r) <= 4) ? ssp->gr[r].h : read_handlers[r]())
166 #define REG_WRITE(r,d) { \
168 if (r1 > 4) write_handlers[r1](d); \
169 else if (r1 > 0) ssp->gr[r1].h = d; \
172 static ssp1601_t *ssp = NULL;
173 static unsigned short *PC;
176 // -----------------------------------------------------
177 // register i/o handlers
180 static u32 read_unknown(void)
182 elprintf(EL_ANOMALY|EL_SVP, "ssp16: unknown read @ %04x", GET_PC_OFFS());
186 static void write_unknown(u32 d)
188 elprintf(EL_ANOMALY|EL_SVP, "ssp16: unknown write @ %04x", GET_PC_OFFS());
192 static u32 read_STACK(void)
196 d = ssp->stack[rSTACK];
199 elprintf(EL_ANOMALY|EL_SVP, "ssp16: stack underflow! (%i) @ %04x", rSTACK, GET_PC_OFFS());
203 static void write_STACK(u32 d)
207 ssp->stack[rSTACK] = d;
209 elprintf(EL_ANOMALY|EL_SVP, "ssp16: stack overflow! (%i) @ %04x", rSTACK, GET_PC_OFFS());
213 static u32 read_PC(void)
218 static void write_PC(u32 d)
225 static u32 read_P(void)
227 rP.v = (u32)rX * rY * 2;
231 static u32 pm_io(int reg, int write, u32 d)
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;
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
250 static u32 read_PM0(void)
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());
258 static void write_PM0(u32 d)
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());
267 static u32 read_PM1(void)
269 u32 d = pm_io(1, 0, 0);
270 if (d != (u32)-1) return d;
272 elprintf(EL_SVP, "PM1 raw r %04x @ %04x", rPM1, GET_PC_OFFS());
276 static void write_PM1(u32 d)
278 u32 r = pm_io(1, 1, d);
279 if (r != (u32)-1) return;
281 elprintf(EL_SVP, "PM1 raw w %04x @ %04x", d, GET_PC_OFFS());
286 static u32 read_PM2(void)
288 u32 d = pm_io(2, 0, 0);
289 if (d != (u32)-1) return d;
291 elprintf(EL_SVP, "PM2 raw r %04x @ %04x", rPM2, GET_PC_OFFS());
295 static void write_PM2(u32 d)
297 u32 r = pm_io(2, 1, d);
298 if (r != (u32)-1) return;
300 elprintf(EL_SVP, "PM2 raw w %04x @ %04x", d, GET_PC_OFFS());
305 static u32 read_XST(void)
308 u32 d = pm_io(3, 0, 0);
309 if (d != (u32)-1) return d;
311 elprintf(EL_SVP, "XST raw r %04x @ %04x", rXST, GET_PC_OFFS());
315 static void write_XST(u32 d)
318 u32 r = pm_io(3, 1, d);
319 if (r != (u32)-1) return;
321 elprintf(EL_SVP, "XST raw w %04x @ %04x", d, GET_PC_OFFS());
326 static u32 read_PM4(void)
328 u32 d = pm_io(4, 0, 0);
329 if (d != (u32)-1) return d;
331 elprintf(EL_SVP, "PM4 raw r %04x @ %04x", rPM4, GET_PC_OFFS());
335 static void write_PM4(u32 d)
337 u32 r = pm_io(4, 1, d);
338 if (r != (u32)-1) return;
340 elprintf(EL_SVP, "PM4 raw w %04x @ %04x", d, GET_PC_OFFS());
345 static u32 read_PMC(void)
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;
353 ssp->emu_status |= SSP_PMC_HAVE_ADDR;
358 static void write_PMC(u32 d)
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;
366 ssp->emu_status |= SSP_PMC_HAVE_ADDR;
372 static u32 read_AL(void)
374 // TODO: figure out what's up with those blind reads..
378 static void write_AL(u32 d)
384 typedef u32 (*read_func_t)(void);
385 typedef void (*write_func_t)(u32 d);
387 static read_func_t read_handlers[16] =
389 read_unknown, read_unknown, read_unknown, read_unknown, // -, X, Y, A
390 read_unknown, // 4 ST
399 read_unknown, // 13 gr13
404 static write_func_t write_handlers[16] =
406 write_unknown, write_unknown, write_unknown, write_unknown, // -, X, Y, A
407 write_unknown, // 4 ST
410 write_unknown, // 7 P
416 write_unknown, // 13 gr13
421 void ssp1601_reset(ssp1601_t *l_ssp)
425 ssp->gr[SSP_GR0].v = 0xffff0000;
427 rSTACK = 6; // ? using descending stack
431 void ssp1601_run(int cycles)
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;
453 u32 d = REG_READ(op & 0x0f);
454 REG_WRITE((op & 0xf0) >> 4, d);
460 elprintf(EL_ANOMALY|EL_SVP, "ssp16: unhandled op %04x @ %04x", op, GET_PC_OFFS());
467 read_P(); // update P
470 if (ssp->gr[SSP_GR0].v != 0xffff0000)
471 elprintf(EL_ANOMALY|EL_SVP, "ssp16: REG 0 corruption! %08x", ssp->gr[SSP_GR0].v);