f8ef8ff7 |
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 | #include "../../PicoInt.h" |
137 | |
138 | #define rX ssp->gr[SSP_X].l |
139 | #define rY ssp->gr[SSP_Y].l |
140 | #define rA ssp->gr[SSP_A] // 4 |
141 | #define rST ssp->gr[SSP_ST].l |
142 | #define rSTACK ssp->gr[SSP_STACK].l |
143 | #define rPC ssp->gr[SSP_PC].l |
144 | #define rP ssp->gr[SSP_P] // 8 |
145 | #define rPM0 ssp->gr[SSP_PM0].l |
146 | #define rPM1 ssp->gr[SSP_PM1].l |
147 | #define rPM2 ssp->gr[SSP_PM2].l |
148 | #define rXST ssp->gr[SSP_XST].l // 12 |
149 | #define rPM4 ssp->gr[SSP_PM4].l // 14 |
150 | #define rPMC ssp->gr[SSP_PMC].l |
151 | #define rAL ssp->gr[SSP_A].l |
152 | |
153 | #define GET_PC() (PC - (unsigned short *)Pico.rom) |
154 | #define SET_PC() PC = (unsigned short *)Pico.rom + rPC |
155 | |
156 | void ssp1601_reset(ssp1601_t *ssp) |
157 | { |
158 | ssp->emu_status = 0; |
159 | ssp->gr[SSP_GR0].v = 0xffff; |
160 | rPC = 0x400; |
161 | rSTACK = 5; // ? |
162 | } |
163 | |
164 | |
165 | void ssp1601_run(ssp1601_t *ssp, int cycles) |
166 | { |
167 | unsigned short *PC; |
168 | int op; |
169 | |
170 | SET_PC(); |
171 | |
172 | while (cycles > 0) |
173 | { |
174 | op = *PC; |
175 | switch (op >> 9) |
176 | { |
177 | // ld d, s |
178 | case 0: |
179 | { |
180 | int s, d, opdata = 0; |
181 | if (op == 0) break; // nop |
182 | s = op & 0x0f; |
183 | d = (op & 0xf0) >> 4; |
184 | if (s == SSP_A || s == SSP_P) opdata |= 1; // src is 32bit |
185 | if (d == SSP_A || d == SSP_P) opdata |= 2; // dst is 32bit |
186 | if (s == SSP_STACK) opdata |= 4; // src is stack |
187 | if (d == SSP_STACK) opdata |= 8; // dst is stack |
188 | switch (opdata) |
189 | { |
190 | case 0x0: ssp->gr[d].l = ssp->gr[s].l; break; // 16 <- 16 |
191 | case 0x1: ssp->gr[d].l = ssp->gr[s].h; break; // 16 <- 32 |
192 | case 0x2: ssp->gr[d].h = ssp->gr[s].l; break; // 32 <- 16 |
193 | // TODO: MAME claims that only hi word is transfered. Go figure. |
194 | case 0x3: ssp->gr[d].v = ssp->gr[s].v; break; // 32 <- 32 |
195 | case 0x4: ; // TODO |
196 | } |
197 | if (d == SSP_PC) |
198 | { |
199 | SET_PC(); |
200 | cycles--; |
201 | } |
202 | break; |
203 | } |
204 | |
205 | default: |
206 | elprintf(0xffff, "ssp: unhandled op %04x @ %04x", op, GET_PC()<<1); |
207 | break; |
208 | } |
209 | cycles--; |
210 | PC++; |
211 | } |
212 | |
213 | rPC = GET_PC(); |
214 | } |
215 | |