1 | /*****************************************************************************\ |
2 | Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. |
3 | This file is licensed under the Snes9x License. |
4 | For further information, consult the LICENSE file in the root directory. |
5 | \*****************************************************************************/ |
6 | |
7 | #include "snes9x.h" |
8 | #include "memmap.h" |
9 | |
10 | uint8 SA1OpenBus; |
11 | |
12 | static void S9xSA1SetBWRAMMemMap (uint8); |
13 | static void S9xSetSA1MemMap (uint32, uint8); |
14 | static void S9xSA1CharConv2 (void); |
15 | static void S9xSA1DMA (void); |
16 | static void S9xSA1ReadVariableLengthData (bool8, bool8); |
17 | |
18 | |
19 | void S9xSA1Init (void) |
20 | { |
21 | SA1.Cycles = 0; |
22 | SA1.PrevCycles = 0; |
23 | SA1.Flags = 0; |
24 | SA1.WaitingForInterrupt = FALSE; |
25 | |
26 | memset(&Memory.FillRAM[0x2200], 0, 0x200); |
27 | Memory.FillRAM[0x2200] = 0x20; |
28 | Memory.FillRAM[0x2220] = 0x00; |
29 | Memory.FillRAM[0x2221] = 0x01; |
30 | Memory.FillRAM[0x2222] = 0x02; |
31 | Memory.FillRAM[0x2223] = 0x03; |
32 | Memory.FillRAM[0x2228] = 0x0f; |
33 | |
34 | SA1.in_char_dma = FALSE; |
35 | SA1.TimerIRQLastState = FALSE; |
36 | SA1.HTimerIRQPos = 0; |
37 | SA1.VTimerIRQPos = 0; |
38 | SA1.HCounter = 0; |
39 | SA1.VCounter = 0; |
40 | SA1.PrevHCounter = 0; |
41 | SA1.arithmetic_op = 0; |
42 | SA1.op1 = 0; |
43 | SA1.op2 = 0; |
44 | SA1.sum = 0; |
45 | SA1.overflow = FALSE; |
46 | SA1.VirtualBitmapFormat = 4; |
47 | SA1.variable_bit_pos = 0; |
48 | |
49 | SA1Registers.PBPC = 0; |
50 | SA1Registers.PB = 0; |
51 | SA1Registers.PCw = 0; |
52 | SA1Registers.D.W = 0; |
53 | SA1Registers.DB = 0; |
54 | SA1Registers.SH = 1; |
55 | SA1Registers.SL = 0xFF; |
56 | SA1Registers.XH = 0; |
57 | SA1Registers.YH = 0; |
58 | SA1Registers.P.W = 0; |
59 | |
60 | SA1.ShiftedPB = 0; |
61 | SA1.ShiftedDB = 0; |
62 | SA1SetFlags(MemoryFlag | IndexFlag | IRQ | Emulation); |
63 | SA1ClearFlags(Decimal); |
64 | |
65 | SA1.MemSpeed = ONE_CYCLE; |
66 | SA1.MemSpeedx2 = ONE_CYCLE * 2; |
67 | |
68 | SA1.S9xOpcodes = S9xSA1OpcodesM1X1; |
69 | SA1.S9xOpLengths = S9xOpLengthsM1X1; |
70 | |
71 | S9xSA1SetPCBase(SA1Registers.PBPC); |
72 | |
73 | S9xSA1UnpackStatus(); |
74 | S9xSA1FixCycles(); |
75 | |
76 | SA1.BWRAM = Memory.SRAM; |
77 | |
78 | CPU.IRQExternal = FALSE; |
79 | } |
80 | |
81 | static void S9xSA1SetBWRAMMemMap (uint8 val) |
82 | { |
83 | if (val & 0x80) |
84 | { |
85 | for (int c = 0; c < 0x400; c += 16) |
86 | { |
87 | SA1.Map[c + 6] = SA1.Map[c + 0x806] = (uint8 *) CMemory::MAP_BWRAM_BITMAP2; |
88 | SA1.Map[c + 7] = SA1.Map[c + 0x807] = (uint8 *) CMemory::MAP_BWRAM_BITMAP2; |
89 | SA1.WriteMap[c + 6] = SA1.WriteMap[c + 0x806] = (uint8 *) CMemory::MAP_BWRAM_BITMAP2; |
90 | SA1.WriteMap[c + 7] = SA1.WriteMap[c + 0x807] = (uint8 *) CMemory::MAP_BWRAM_BITMAP2; |
91 | } |
92 | |
93 | SA1.BWRAM = Memory.SRAM + (val & 0x7f) * 0x2000 / 4; |
94 | } |
95 | else |
96 | { |
97 | for (int c = 0; c < 0x400; c += 16) |
98 | { |
99 | SA1.Map[c + 6] = SA1.Map[c + 0x806] = (uint8 *) CMemory::MAP_BWRAM; |
100 | SA1.Map[c + 7] = SA1.Map[c + 0x807] = (uint8 *) CMemory::MAP_BWRAM; |
101 | SA1.WriteMap[c + 6] = SA1.WriteMap[c + 0x806] = (uint8 *) CMemory::MAP_BWRAM; |
102 | SA1.WriteMap[c + 7] = SA1.WriteMap[c + 0x807] = (uint8 *) CMemory::MAP_BWRAM; |
103 | } |
104 | |
105 | SA1.BWRAM = Memory.SRAM + (val & 0x1f) * 0x2000; |
106 | } |
107 | } |
108 | |
109 | void S9xSA1PostLoadState (void) |
110 | { |
111 | SA1.ShiftedPB = (uint32) SA1Registers.PB << 16; |
112 | SA1.ShiftedDB = (uint32) SA1Registers.DB << 16; |
113 | |
114 | S9xSA1SetPCBase(SA1Registers.PBPC); |
115 | S9xSA1UnpackStatus(); |
116 | S9xSA1FixCycles(); |
117 | SA1.VirtualBitmapFormat = (Memory.FillRAM[0x223f] & 0x80) ? 2 : 4; |
118 | Memory.BWRAM = Memory.SRAM + (Memory.FillRAM[0x2224] & 0x1f) * 0x2000; |
119 | S9xSA1SetBWRAMMemMap(Memory.FillRAM[0x2225]); |
120 | S9xSetSA1(Memory.FillRAM[0x2220], 0x2220); |
121 | S9xSetSA1(Memory.FillRAM[0x2221], 0x2221); |
122 | S9xSetSA1(Memory.FillRAM[0x2222], 0x2222); |
123 | S9xSetSA1(Memory.FillRAM[0x2223], 0x2223); |
124 | } |
125 | |
126 | static void S9xSetSA1MemMap (uint32 which1, uint8 map) |
127 | { |
128 | int start = which1 * 0x100 + 0xc00; |
129 | int start2 = which1 * 0x200; |
130 | |
131 | if (which1 >= 2) |
132 | start2 += 0x400; |
133 | |
134 | for (int c = 0; c < 0x100; c += 16) |
135 | { |
136 | uint8 *block; |
137 | if (Multi.cartType != 5) |
138 | block = &Memory.ROM[(map & 7) * 0x100000 + (c << 12)]; |
139 | else |
140 | { |
141 | if ((map & 7) < 4) |
142 | block = Memory.ROM + Multi.cartOffsetA + ((map & 7) * 0x100000 + (c << 12)); |
143 | else |
144 | block = Memory.ROM + Multi.cartOffsetB + (((map & 7) - 4) * 0x100000 + (c << 12)); |
145 | } |
146 | for (int i = c; i < c + 16; i++) |
147 | Memory.Map[start + i] = SA1.Map[start + i] = block; |
148 | } |
149 | |
150 | for (int c = 0; c < 0x200; c += 16) |
151 | { |
152 | // conversion to int is needed here - map is promoted but which1 is not |
153 | int32 offset; |
154 | uint8 *block; |
155 | if (Multi.cartType != 5) |
156 | { |
157 | offset = (((map & 0x80) ? map : which1) & 7) * 0x100000 + (c << 11) - 0x8000; |
158 | block = &Memory.ROM[offset]; |
159 | } |
160 | else |
161 | { |
162 | if ((map & 7) < 4) |
163 | { |
164 | offset = (((map & 0x80) ? map : which1) & 7) * 0x100000 + (c << 11) - 0x8000; |
165 | block = Memory.ROM + Multi.cartOffsetA + offset; |
166 | } |
167 | else |
168 | { |
169 | offset = (((map & 0x80) ? (map - 4) : which1) & 7) * 0x100000 + (c << 11) - 0x8000; |
170 | block = Memory.ROM + Multi.cartOffsetB + offset; |
171 | } |
172 | } |
173 | for (int i = c + 8; i < c + 16; i++) |
174 | Memory.Map[start2 + i] = SA1.Map[start2 + i] = block; |
175 | } |
176 | } |
177 | |
178 | uint8 S9xGetSA1 (uint32 address) |
179 | { |
180 | switch (address) |
181 | { |
182 | case 0x2300: // S-CPU flag |
183 | return ((Memory.FillRAM[0x2209] & 0x5f) | (Memory.FillRAM[0x2300] & 0xa0)); |
184 | |
185 | case 0x2301: // SA-1 flag |
186 | return ((Memory.FillRAM[0x2200] & 0x0f) | (Memory.FillRAM[0x2301] & 0xf0)); |
187 | |
188 | case 0x2302: // H counter (L) |
189 | SA1.HTimerIRQPos = SA1.HCounter / ONE_DOT_CYCLE; |
190 | SA1.VTimerIRQPos = SA1.VCounter; |
191 | return ((uint8) SA1.HTimerIRQPos); |
192 | |
193 | case 0x2303: // H counter (H) |
194 | return ((uint8) (SA1.HTimerIRQPos >> 8)); |
195 | |
196 | case 0x2304: // V counter (L) |
197 | return ((uint8) SA1.VTimerIRQPos); |
198 | |
199 | case 0x2305: // V counter (H) |
200 | return ((uint8) (SA1.VTimerIRQPos >> 8)); |
201 | |
202 | case 0x2306: // arithmetic result (LLL) |
203 | return ((uint8) SA1.sum); |
204 | |
205 | case 0x2307: // arithmetic result (LLH) |
206 | return ((uint8) (SA1.sum >> 8)); |
207 | |
208 | case 0x2308: // arithmetic result (LHL) |
209 | return ((uint8) (SA1.sum >> 16)); |
210 | |
211 | case 0x2309: // arithmetic result (LLH) |
212 | return ((uint8) (SA1.sum >> 24)); |
213 | |
214 | case 0x230a: // arithmetic result (HLL) |
215 | return ((uint8) (SA1.sum >> 32)); |
216 | |
217 | case 0x230b: // arithmetic overflow |
218 | return (SA1.overflow ? 0x80 : 0); |
219 | |
220 | case 0x230c: // variable-length data read port (L) |
221 | return (Memory.FillRAM[0x230c]); |
222 | |
223 | case 0x230d: // variable-length data read port (H) |
224 | { |
225 | uint8 byte = Memory.FillRAM[0x230d]; |
226 | |
227 | if (Memory.FillRAM[0x2258] & 0x80) |
228 | S9xSA1ReadVariableLengthData(TRUE, FALSE); |
229 | |
230 | return (byte); |
231 | } |
232 | |
233 | case 0x230e: // version code register |
234 | return (0x23); |
235 | |
236 | default: |
237 | break; |
238 | } |
239 | |
240 | return (Memory.FillRAM[address]); |
241 | } |
242 | |
243 | void S9xSetSA1 (uint8 byte, uint32 address) |
244 | { |
245 | switch (address) |
246 | { |
247 | case 0x2200: // SA-1 control |
248 | #ifdef DEBUGGER |
249 | if (byte & 0x60) |
250 | printf("SA-1 sleep\n" ); |
251 | #endif |
252 | |
253 | // SA-1 reset |
254 | if (!(byte & 0x80) && (Memory.FillRAM[0x2200] & 0x20)) |
255 | { |
256 | #ifdef DEBUGGER |
257 | printf("SA-1 reset\n" ); |
258 | #endif |
259 | SA1Registers.PBPC = 0; |
260 | SA1Registers.PB = 0; |
261 | SA1Registers.PCw = Memory.FillRAM[0x2203] | (Memory.FillRAM[0x2204] << 8); |
262 | S9xSA1SetPCBase(SA1Registers.PBPC); |
263 | } |
264 | |
265 | // SA-1 IRQ control |
266 | if (byte & 0x80) |
267 | { |
268 | Memory.FillRAM[0x2301] |= 0x80; |
269 | if (Memory.FillRAM[0x220a] & 0x80) |
270 | Memory.FillRAM[0x220b] &= ~0x80; |
271 | } |
272 | |
273 | // SA-1 NMI control |
274 | if (byte & 0x10) |
275 | { |
276 | Memory.FillRAM[0x2301] |= 0x10; |
277 | if (Memory.FillRAM[0x220a] & 0x10) |
278 | Memory.FillRAM[0x220b] &= ~0x10; |
279 | } |
280 | |
281 | break; |
282 | |
283 | case 0x2201: // S-CPU interrupt enable |
284 | // S-CPU IRQ enable |
285 | if (((byte ^ Memory.FillRAM[0x2201]) & 0x80) && (Memory.FillRAM[0x2300] & byte & 0x80)) |
286 | { |
287 | Memory.FillRAM[0x2202] &= ~0x80; |
288 | CPU.IRQExternal = TRUE; |
289 | } |
290 | |
291 | // S-CPU CHDMA IRQ enable |
292 | if (((byte ^ Memory.FillRAM[0x2201]) & 0x20) && (Memory.FillRAM[0x2300] & byte & 0x20)) |
293 | { |
294 | Memory.FillRAM[0x2202] &= ~0x20; |
295 | CPU.IRQExternal = TRUE; |
296 | } |
297 | |
298 | break; |
299 | |
300 | case 0x2202: // S-CPU interrupt clear |
301 | // S-CPU IRQ clear |
302 | if (byte & 0x80) |
303 | Memory.FillRAM[0x2300] &= ~0x80; |
304 | |
305 | // S-CPU CHDMA IRQ clear |
306 | if (byte & 0x20) |
307 | Memory.FillRAM[0x2300] &= ~0x20; |
308 | |
309 | if (!(Memory.FillRAM[0x2300] & 0xa0)) |
310 | CPU.IRQExternal = FALSE; |
311 | |
312 | break; |
313 | |
314 | case 0x2203: // SA-1 reset vector (L) |
315 | case 0x2204: // SA-1 reset vector (H) |
316 | case 0x2205: // SA-1 NMI vector (L) |
317 | case 0x2206: // SA-1 NMI vector (H) |
318 | case 0x2207: // SA-1 IRQ vector (L) |
319 | case 0x2208: // SA-1 IRQ vector (H) |
320 | break; |
321 | |
322 | case 0x2209: // S-CPU control |
323 | // 0x40: S-CPU IRQ overwrite |
324 | // 0x20: S-CPU NMI overwrite |
325 | |
326 | // S-CPU IRQ control |
327 | if (byte & 0x80) |
328 | { |
329 | Memory.FillRAM[0x2300] |= 0x80; |
330 | if (Memory.FillRAM[0x2201] & 0x80) |
331 | { |
332 | Memory.FillRAM[0x2202] &= ~0x80; |
333 | CPU.IRQExternal = TRUE; |
334 | } |
335 | } |
336 | |
337 | break; |
338 | |
339 | case 0x220a: // SA-1 interrupt enable |
340 | // SA-1 IRQ enable |
341 | if (((byte ^ Memory.FillRAM[0x220a]) & 0x80) && (Memory.FillRAM[0x2301] & byte & 0x80)) |
342 | Memory.FillRAM[0x220b] &= ~0x80; |
343 | |
344 | // SA-1 timer IRQ enable |
345 | if (((byte ^ Memory.FillRAM[0x220a]) & 0x40) && (Memory.FillRAM[0x2301] & byte & 0x40)) |
346 | Memory.FillRAM[0x220b] &= ~0x40; |
347 | |
348 | // SA-1 DMA IRQ enable |
349 | if (((byte ^ Memory.FillRAM[0x220a]) & 0x20) && (Memory.FillRAM[0x2301] & byte & 0x20)) |
350 | Memory.FillRAM[0x220b] &= ~0x20; |
351 | |
352 | // SA-1 NMI enable |
353 | if (((byte ^ Memory.FillRAM[0x220a]) & 0x10) && (Memory.FillRAM[0x2301] & byte & 0x10)) |
354 | Memory.FillRAM[0x220b] &= ~0x10; |
355 | |
356 | break; |
357 | |
358 | case 0x220b: // SA-1 interrupt clear |
359 | // SA-1 IRQ clear |
360 | if (byte & 0x80) |
361 | Memory.FillRAM[0x2301] &= ~0x80; |
362 | |
363 | // SA-1 timer IRQ clear |
364 | if (byte & 0x40) |
365 | Memory.FillRAM[0x2301] &= ~0x40; |
366 | |
367 | // SA-1 DMA IRQ clear |
368 | if (byte & 0x20) |
369 | Memory.FillRAM[0x2301] &= ~0x20; |
370 | |
371 | // SA-1 NMI clear |
372 | if (byte & 0x10) |
373 | Memory.FillRAM[0x2301] &= ~0x10; |
374 | |
375 | break; |
376 | |
377 | case 0x220c: // S-CPU NMI vector (L) |
378 | case 0x220d: // S-CPU NMI vector (H) |
379 | case 0x220e: // S-CPU IRQ vector (L) |
380 | case 0x220f: // S-CPU IRQ vector (H) |
381 | break; |
382 | |
383 | case 0x2210: // SA-1 timer control |
384 | // 0x80: mode (linear / HV) |
385 | // 0x02: V timer enable |
386 | // 0x01: H timer enable |
387 | #ifdef DEBUGGER |
388 | printf("SA-1 timer control write:%02x\n" , byte); |
389 | #endif |
390 | break; |
391 | |
392 | case 0x2211: // SA-1 timer reset |
393 | SA1.HCounter = 0; |
394 | SA1.VCounter = 0; |
395 | break; |
396 | |
397 | case 0x2212: // SA-1 H-timer (L) |
398 | SA1.HTimerIRQPos = byte | (Memory.FillRAM[0x2213] << 8); |
399 | break; |
400 | |
401 | case 0x2213: // SA-1 H-timer (H) |
402 | SA1.HTimerIRQPos = (byte << 8) | Memory.FillRAM[0x2212]; |
403 | break; |
404 | |
405 | case 0x2214: // SA-1 V-timer (L) |
406 | SA1.VTimerIRQPos = byte | (Memory.FillRAM[0x2215] << 8); |
407 | break; |
408 | |
409 | case 0x2215: // SA-1 V-timer (H) |
410 | SA1.VTimerIRQPos = (byte << 8) | Memory.FillRAM[0x2214]; |
411 | break; |
412 | |
413 | case 0x2220: // MMC bank C |
414 | case 0x2221: // MMC bank D |
415 | case 0x2222: // MMC bank E |
416 | case 0x2223: // MMC bank F |
417 | S9xSetSA1MemMap(address - 0x2220, byte); |
418 | break; |
419 | |
420 | case 0x2224: // S-CPU BW-RAM mapping |
421 | Memory.BWRAM = Memory.SRAM + (byte & 0x1f) * 0x2000; |
422 | break; |
423 | |
424 | case 0x2225: // SA-1 BW-RAM mapping |
425 | if (byte != Memory.FillRAM[0x2225]) |
426 | S9xSA1SetBWRAMMemMap(byte); |
427 | |
428 | break; |
429 | |
430 | case 0x2226: // S-CPU BW-RAM write enable |
431 | case 0x2227: // SA-1 BW-RAM write enable |
432 | case 0x2228: // BW-RAM write-protected area |
433 | case 0x2229: // S-CPU I-RAM write protection |
434 | case 0x222a: // SA-1 I-RAM write protection |
435 | break; |
436 | |
437 | case 0x2230: // DMA control |
438 | // 0x80: enable |
439 | // 0x40: priority (DMA / SA-1) |
440 | // 0x20: character conversion / normal |
441 | // 0x10: BW-RAM -> I-RAM / SA-1 -> I-RAM |
442 | // 0x04: destinatin (BW-RAM / I-RAM) |
443 | // 0x03: source |
444 | break; |
445 | |
446 | case 0x2231: // character conversion DMA parameters |
447 | // 0x80: CHDEND (complete / incomplete) |
448 | // 0x03: color mode |
449 | // (byte >> 2) & 7: virtual VRAM width |
450 | if (byte & 0x80) |
451 | SA1.in_char_dma = FALSE; |
452 | |
453 | break; |
454 | |
455 | case 0x2232: // DMA source start address (LL) |
456 | case 0x2233: // DMA source start address (LH) |
457 | case 0x2234: // DMA source start address (HL) |
458 | break; |
459 | |
460 | case 0x2235: // DMA destination start address (LL) |
461 | break; |
462 | |
463 | case 0x2236: // DMA destination start address (LH) |
464 | Memory.FillRAM[0x2236] = byte; |
465 | |
466 | if ((Memory.FillRAM[0x2230] & 0xa4) == 0x80) // Normal DMA to I-RAM |
467 | S9xSA1DMA(); |
468 | else |
469 | if ((Memory.FillRAM[0x2230] & 0xb0) == 0xb0) // CC1 |
470 | { |
471 | SA1.in_char_dma = TRUE; |
472 | |
473 | Memory.FillRAM[0x2300] |= 0x20; |
474 | if (Memory.FillRAM[0x2201] & 0x20) |
475 | { |
476 | Memory.FillRAM[0x2202] &= ~0x20; |
477 | CPU.IRQExternal = TRUE; |
478 | } |
479 | } |
480 | |
481 | break; |
482 | |
483 | case 0x2237: // DMA destination start address (HL) |
484 | Memory.FillRAM[0x2237] = byte; |
485 | |
486 | if ((Memory.FillRAM[0x2230] & 0xa4) == 0x84) // Normal DMA to BW-RAM |
487 | S9xSA1DMA(); |
488 | |
489 | break; |
490 | |
491 | case 0x2238: // DMA terminal counter (L) |
492 | case 0x2239: // DMA terminal counter (H) |
493 | break; |
494 | |
495 | case 0x223f: // BW-RAM bitmap format |
496 | SA1.VirtualBitmapFormat = (byte & 0x80) ? 2 : 4; |
497 | break; |
498 | |
499 | case 0x2240: // bitmap register 0 |
500 | case 0x2241: // bitmap register 1 |
501 | case 0x2242: // bitmap register 2 |
502 | case 0x2243: // bitmap register 3 |
503 | case 0x2244: // bitmap register 4 |
504 | case 0x2245: // bitmap register 5 |
505 | case 0x2246: // bitmap register 6 |
506 | case 0x2247: // bitmap register 7 |
507 | case 0x2248: // bitmap register 8 |
508 | case 0x2249: // bitmap register 9 |
509 | case 0x224a: // bitmap register A |
510 | case 0x224b: // bitmap register B |
511 | case 0x224c: // bitmap register C |
512 | case 0x224d: // bitmap register D |
513 | case 0x224e: // bitmap register E |
514 | break; |
515 | |
516 | case 0x224f: // bitmap register F |
517 | Memory.FillRAM[0x224f] = byte; |
518 | |
519 | if ((Memory.FillRAM[0x2230] & 0xb0) == 0xa0) // CC2 |
520 | { |
521 | memmove(&Memory.ROM[CMemory::MAX_ROM_SIZE - 0x10000] + SA1.in_char_dma * 16, &Memory.FillRAM[0x2240], 16); |
522 | SA1.in_char_dma = (SA1.in_char_dma + 1) & 7; |
523 | if ((SA1.in_char_dma & 3) == 0) |
524 | S9xSA1CharConv2(); |
525 | } |
526 | |
527 | break; |
528 | |
529 | case 0x2250: // arithmetic control |
530 | if (byte & 2) |
531 | SA1.sum = 0; |
532 | SA1.arithmetic_op = byte & 3; |
533 | break; |
534 | |
535 | case 0x2251: // multiplicand / dividend (L) |
536 | SA1.op1 = (SA1.op1 & 0xff00) | byte; |
537 | break; |
538 | |
539 | case 0x2252: // multiplicand / dividend (H) |
540 | SA1.op1 = (SA1.op1 & 0x00ff) | (byte << 8); |
541 | break; |
542 | |
543 | case 0x2253: // multiplier / divisor (L) |
544 | SA1.op2 = (SA1.op2 & 0xff00) | byte; |
545 | break; |
546 | |
547 | case 0x2254: // multiplier / divisor (H) |
548 | SA1.op2 = (SA1.op2 & 0x00ff) | (byte << 8); |
549 | |
550 | switch (SA1.arithmetic_op) |
551 | { |
552 | case 0: // signed multiplication |
553 | SA1.sum = (int16) SA1.op1 * (int16) SA1.op2; |
554 | SA1.op2 = 0; |
555 | break; |
556 | |
557 | case 1: // unsigned division |
558 | if (SA1.op2 == 0) |
559 | SA1.sum = 0; |
560 | else |
561 | { |
562 | int16 dividend = (int16) SA1.op1; |
563 | uint16 divisor = (uint16) SA1.op2; |
564 | uint16 remainder = (dividend >= 0) ? dividend % divisor : (dividend % divisor) + divisor; |
565 | uint16 quotient = (dividend - remainder) / divisor; |
566 | SA1.sum = (remainder << 16) | quotient; |
567 | } |
568 | |
569 | SA1.op1 = 0; |
570 | SA1.op2 = 0; |
571 | break; |
572 | |
573 | case 2: // cumulative sum |
574 | default: |
575 | SA1.sum += (int16) SA1.op1 * (int16) SA1.op2; |
576 | SA1.overflow = (SA1.sum >= (1ULL << 40)); |
577 | SA1.sum &= (1ULL << 40) - 1; |
578 | SA1.op2 = 0; |
579 | break; |
580 | } |
581 | |
582 | break; |
583 | |
584 | case 0x2258: // variable bit-field length / auto inc / start |
585 | Memory.FillRAM[0x2258] = byte; |
586 | S9xSA1ReadVariableLengthData(TRUE, FALSE); |
587 | return; |
588 | |
589 | case 0x2259: // variable bit-field start address (LL) |
590 | case 0x225a: // variable bit-field start address (LH) |
591 | case 0x225b: // variable bit-field start address (HL) |
592 | Memory.FillRAM[address] = byte; |
593 | // XXX: ??? |
594 | SA1.variable_bit_pos = 0; |
595 | S9xSA1ReadVariableLengthData(FALSE, TRUE); |
596 | return; |
597 | |
598 | default: |
599 | break; |
600 | } |
601 | |
602 | if (address >= 0x2200 && address <= 0x22ff) |
603 | Memory.FillRAM[address] = byte; |
604 | } |
605 | |
606 | static void S9xSA1CharConv2 (void) |
607 | { |
608 | uint32 dest = Memory.FillRAM[0x2235] | (Memory.FillRAM[0x2236] << 8); |
609 | uint32 offset = (SA1.in_char_dma & 7) ? 0 : 1; |
610 | int depth = (Memory.FillRAM[0x2231] & 3) == 0 ? 8 : (Memory.FillRAM[0x2231] & 3) == 1 ? 4 : 2; |
611 | int bytes_per_char = 8 * depth; |
612 | uint8 *p = &Memory.FillRAM[0x3000] + (dest & 0x7ff) + offset * bytes_per_char; |
613 | uint8 *q = &Memory.ROM[CMemory::MAX_ROM_SIZE - 0x10000] + offset * 64; |
614 | |
615 | switch (depth) |
616 | { |
617 | case 2: |
618 | for (int l = 0; l < 8; l++, q += 8) |
619 | { |
620 | for (int b = 0; b < 8; b++) |
621 | { |
622 | uint8 r = *(q + b); |
623 | *(p + 0) = (*(p + 0) << 1) | ((r >> 0) & 1); |
624 | *(p + 1) = (*(p + 1) << 1) | ((r >> 1) & 1); |
625 | } |
626 | |
627 | p += 2; |
628 | } |
629 | |
630 | break; |
631 | |
632 | case 4: |
633 | for (int l = 0; l < 8; l++, q += 8) |
634 | { |
635 | for (int b = 0; b < 8; b++) |
636 | { |
637 | uint8 r = *(q + b); |
638 | *(p + 0) = (*(p + 0) << 1) | ((r >> 0) & 1); |
639 | *(p + 1) = (*(p + 1) << 1) | ((r >> 1) & 1); |
640 | *(p + 16) = (*(p + 16) << 1) | ((r >> 2) & 1); |
641 | *(p + 17) = (*(p + 17) << 1) | ((r >> 3) & 1); |
642 | } |
643 | |
644 | p += 2; |
645 | } |
646 | |
647 | break; |
648 | |
649 | case 8: |
650 | for (int l = 0; l < 8; l++, q += 8) |
651 | { |
652 | for (int b = 0; b < 8; b++) |
653 | { |
654 | uint8 r = *(q + b); |
655 | *(p + 0) = (*(p + 0) << 1) | ((r >> 0) & 1); |
656 | *(p + 1) = (*(p + 1) << 1) | ((r >> 1) & 1); |
657 | *(p + 16) = (*(p + 16) << 1) | ((r >> 2) & 1); |
658 | *(p + 17) = (*(p + 17) << 1) | ((r >> 3) & 1); |
659 | *(p + 32) = (*(p + 32) << 1) | ((r >> 4) & 1); |
660 | *(p + 33) = (*(p + 33) << 1) | ((r >> 5) & 1); |
661 | *(p + 48) = (*(p + 48) << 1) | ((r >> 6) & 1); |
662 | *(p + 49) = (*(p + 49) << 1) | ((r >> 7) & 1); |
663 | } |
664 | |
665 | p += 2; |
666 | } |
667 | |
668 | break; |
669 | } |
670 | } |
671 | |
672 | static void S9xSA1DMA (void) |
673 | { |
674 | uint32 src = Memory.FillRAM[0x2232] | (Memory.FillRAM[0x2233] << 8) | (Memory.FillRAM[0x2234] << 16); |
675 | uint32 dst = Memory.FillRAM[0x2235] | (Memory.FillRAM[0x2236] << 8) | (Memory.FillRAM[0x2237] << 16); |
676 | uint32 len = Memory.FillRAM[0x2238] | (Memory.FillRAM[0x2239] << 8); |
677 | uint8 *s, *d; |
678 | |
679 | switch (Memory.FillRAM[0x2230] & 3) |
680 | { |
681 | case 0: // ROM |
682 | s = SA1.Map[((src & 0xffffff) >> MEMMAP_SHIFT)]; |
683 | if (s >= (uint8 *) CMemory::MAP_LAST) |
684 | s += (src & 0xffff); |
685 | else |
686 | s = Memory.ROM + (src & 0xffff); |
687 | break; |
688 | |
689 | case 1: // BW-RAM |
690 | src &= Memory.SRAMMask; |
691 | len &= Memory.SRAMMask; |
692 | s = Memory.SRAM + src; |
693 | break; |
694 | |
695 | default: |
696 | case 2: |
697 | src &= 0x3ff; |
698 | len &= 0x3ff; |
699 | s = &Memory.FillRAM[0x3000] + src; |
700 | break; |
701 | } |
702 | |
703 | if (Memory.FillRAM[0x2230] & 4) |
704 | { |
705 | dst &= Memory.SRAMMask; |
706 | len &= Memory.SRAMMask; |
707 | d = Memory.SRAM + dst; |
708 | } |
709 | else |
710 | { |
711 | dst &= 0x3ff; |
712 | len &= 0x3ff; |
713 | d = &Memory.FillRAM[0x3000] + dst; |
714 | } |
715 | |
716 | memmove(d, s, len); |
717 | |
718 | // SA-1 DMA IRQ control |
719 | Memory.FillRAM[0x2301] |= 0x20; |
720 | if (Memory.FillRAM[0x220a] & 0x20) |
721 | Memory.FillRAM[0x220b] &= ~0x20; |
722 | } |
723 | |
724 | static void S9xSA1ReadVariableLengthData (bool8 inc, bool8 no_shift) |
725 | { |
726 | uint32 addr = Memory.FillRAM[0x2259] | (Memory.FillRAM[0x225a] << 8) | (Memory.FillRAM[0x225b] << 16); |
727 | uint8 shift = Memory.FillRAM[0x2258] & 15; |
728 | |
729 | if (no_shift) |
730 | shift = 0; |
731 | else |
732 | if (shift == 0) |
733 | shift = 16; |
734 | |
735 | uint8 s = shift + SA1.variable_bit_pos; |
736 | |
737 | if (s >= 16) |
738 | { |
739 | addr += (s >> 4) << 1; |
740 | s &= 15; |
741 | } |
742 | |
743 | uint32 data = S9xSA1GetWord(addr) | (S9xSA1GetWord(addr + 2) << 16); |
744 | |
745 | data >>= s; |
746 | Memory.FillRAM[0x230c] = (uint8) data; |
747 | Memory.FillRAM[0x230d] = (uint8) (data >> 8); |
748 | |
749 | if (inc) |
750 | { |
751 | SA1.variable_bit_pos = (SA1.variable_bit_pos + shift) & 15; |
752 | Memory.FillRAM[0x2259] = (uint8) addr; |
753 | Memory.FillRAM[0x225a] = (uint8) (addr >> 8); |
754 | Memory.FillRAM[0x225b] = (uint8) (addr >> 16); |
755 | } |
756 | } |
757 | |
758 | uint8 S9xSA1GetByte (uint32 address) |
759 | { |
760 | uint8 *GetAddress = SA1.Map[(address & 0xffffff) >> MEMMAP_SHIFT]; |
761 | |
762 | if (GetAddress >= (uint8 *)CMemory::MAP_LAST) |
763 | { |
764 | SA1.Cycles += SA1.MemSpeed; |
765 | return (*(GetAddress + (address & 0xffff))); |
766 | } |
767 | |
768 | switch ((pint) GetAddress) |
769 | { |
770 | case CMemory::MAP_PPU: |
771 | SA1.Cycles += ONE_CYCLE; |
772 | return (S9xGetSA1(address & 0xffff)); |
773 | |
774 | case CMemory::MAP_LOROM_SRAM: |
775 | case CMemory::MAP_HIROM_SRAM: |
776 | case CMemory::MAP_SA1RAM: |
777 | SA1.Cycles += ONE_CYCLE * 2; |
778 | return (*(Memory.SRAM + (address & 0x3ffff))); |
779 | |
780 | case CMemory::MAP_BWRAM: |
781 | SA1.Cycles += ONE_CYCLE * 2; |
782 | return (*(SA1.BWRAM + (address & 0x1fff))); |
783 | |
784 | case CMemory::MAP_BWRAM_BITMAP: |
785 | SA1.Cycles += ONE_CYCLE * 2; |
786 | |
787 | address -= 0x600000; |
788 | if (SA1.VirtualBitmapFormat == 2) |
789 | return ((Memory.SRAM[(address >> 2) & 0x3ffff] >> ((address & 3) << 1)) & 3); |
790 | else |
791 | return ((Memory.SRAM[(address >> 1) & 0x3ffff] >> ((address & 1) << 2)) & 15); |
792 | |
793 | case CMemory::MAP_BWRAM_BITMAP2: |
794 | SA1.Cycles += ONE_CYCLE * 2; |
795 | |
796 | address = (address & 0xffff) - 0x6000; |
797 | if (SA1.VirtualBitmapFormat == 2) |
798 | return ((SA1.BWRAM[(address >> 2) & 0x3ffff] >> ((address & 3) << 1)) & 3); |
799 | else |
800 | return ((SA1.BWRAM[(address >> 1) & 0x3ffff] >> ((address & 1) << 2)) & 15); |
801 | |
802 | default: |
803 | SA1.Cycles += ONE_CYCLE; |
804 | return (SA1OpenBus); |
805 | } |
806 | } |
807 | |
808 | uint16 S9xSA1GetWord (uint32 address, s9xwrap_t w) |
809 | { |
810 | PC_t a; |
811 | |
812 | SA1OpenBus = S9xSA1GetByte(address); |
813 | |
814 | switch (w) |
815 | { |
816 | case WRAP_PAGE: |
817 | a.xPBPC = address; |
818 | a.B.xPCl++; |
819 | return (SA1OpenBus | (S9xSA1GetByte(a.xPBPC) << 8)); |
820 | |
821 | case WRAP_BANK: |
822 | a.xPBPC = address; |
823 | a.W.xPC++; |
824 | return (SA1OpenBus | (S9xSA1GetByte(a.xPBPC) << 8)); |
825 | |
826 | case WRAP_NONE: |
827 | default: |
828 | return (SA1OpenBus | (S9xSA1GetByte(address + 1) << 8)); |
829 | } |
830 | } |
831 | |
832 | void S9xSA1SetByte (uint8 byte, uint32 address) |
833 | { |
834 | uint8 *SetAddress = SA1.WriteMap[(address & 0xffffff) >> MEMMAP_SHIFT]; |
835 | |
836 | if (SetAddress >= (uint8 *) CMemory::MAP_LAST) |
837 | { |
838 | *(SetAddress + (address & 0xffff)) = byte; |
839 | return; |
840 | } |
841 | |
842 | switch ((pint) SetAddress) |
843 | { |
844 | case CMemory::MAP_PPU: |
845 | S9xSetSA1(byte, address & 0xffff); |
846 | return; |
847 | |
848 | case CMemory::MAP_LOROM_SRAM: |
849 | case CMemory::MAP_HIROM_SRAM: |
850 | case CMemory::MAP_SA1RAM: |
851 | *(Memory.SRAM + (address & 0x3ffff)) = byte; |
852 | return; |
853 | |
854 | case CMemory::MAP_BWRAM: |
855 | *(SA1.BWRAM + (address & 0x1fff)) = byte; |
856 | return; |
857 | |
858 | case CMemory::MAP_BWRAM_BITMAP: |
859 | address -= 0x600000; |
860 | if (SA1.VirtualBitmapFormat == 2) |
861 | { |
862 | uint8 *ptr = &Memory.SRAM[(address >> 2) & 0x3ffff]; |
863 | *ptr &= ~(3 << ((address & 3) << 1)); |
864 | *ptr |= (byte & 3) << ((address & 3) << 1); |
865 | } |
866 | else |
867 | { |
868 | uint8 *ptr = &Memory.SRAM[(address >> 1) & 0x3ffff]; |
869 | *ptr &= ~(15 << ((address & 1) << 2)); |
870 | *ptr |= (byte & 15) << ((address & 1) << 2); |
871 | } |
872 | |
873 | return; |
874 | |
875 | case CMemory::MAP_BWRAM_BITMAP2: |
876 | address = (address & 0xffff) - 0x6000; |
877 | if (SA1.VirtualBitmapFormat == 2) |
878 | { |
879 | uint8 *ptr = &SA1.BWRAM[(address >> 2) & 0x3ffff]; |
880 | *ptr &= ~(3 << ((address & 3) << 1)); |
881 | *ptr |= (byte & 3) << ((address & 3) << 1); |
882 | } |
883 | else |
884 | { |
885 | uint8 *ptr = &SA1.BWRAM[(address >> 1) & 0x3ffff]; |
886 | *ptr &= ~(15 << ((address & 1) << 2)); |
887 | *ptr |= (byte & 15) << ((address & 1) << 2); |
888 | } |
889 | |
890 | return; |
891 | |
892 | default: |
893 | return; |
894 | } |
895 | } |
896 | |
897 | void S9xSA1SetWord (uint16 Word, uint32 address, enum s9xwrap_t w, enum s9xwriteorder_t o) |
898 | { |
899 | PC_t a; |
900 | |
901 | if (!o) |
902 | S9xSA1SetByte((uint8) Word, address); |
903 | |
904 | switch (w) |
905 | { |
906 | case WRAP_PAGE: |
907 | a.xPBPC = address; |
908 | a.B.xPCl++; |
909 | S9xSA1SetByte(Word >> 8, a.xPBPC); |
910 | break; |
911 | |
912 | case WRAP_BANK: |
913 | a.xPBPC = address; |
914 | a.W.xPC++; |
915 | S9xSA1SetByte(Word >> 8, a.xPBPC); |
916 | break; |
917 | |
918 | case WRAP_NONE: |
919 | default: |
920 | S9xSA1SetByte(Word >> 8, address + 1); |
921 | break; |
922 | } |
923 | |
924 | if (o) |
925 | S9xSA1SetByte((uint8) Word, address); |
926 | } |
927 | |
928 | void S9xSA1SetPCBase (uint32 address) |
929 | { |
930 | SA1Registers.PBPC = address & 0xffffff; |
931 | SA1.ShiftedPB = address & 0xff0000; |
932 | |
933 | // FIXME |
934 | SA1.MemSpeed = ONE_CYCLE; |
935 | if ((address & 0xc00000) == 0x400000 || (address & 0x40e000) == 0x6000) |
936 | { |
937 | SA1.MemSpeed = TWO_CYCLES; |
938 | } |
939 | |
940 | SA1.MemSpeedx2 = SA1.MemSpeed << 1; |
941 | |
942 | uint8 *GetAddress = SA1.Map[(address & 0xffffff) >> MEMMAP_SHIFT]; |
943 | |
944 | if (GetAddress >= (uint8 *) CMemory::MAP_LAST) |
945 | { |
946 | SA1.PCBase = GetAddress; |
947 | return; |
948 | } |
949 | |
950 | switch ((pint) GetAddress) |
951 | { |
952 | case CMemory::MAP_LOROM_SRAM: |
953 | if ((Memory.SRAMMask & MEMMAP_MASK) != MEMMAP_MASK) |
954 | SA1.PCBase = NULL; |
955 | else |
956 | SA1.PCBase = (Memory.SRAM + ((((address & 0xff0000) >> 1) | (address & 0x7fff)) & Memory.SRAMMask)) - (address & 0xffff); |
957 | return; |
958 | |
959 | case CMemory::MAP_HIROM_SRAM: |
960 | if ((Memory.SRAMMask & MEMMAP_MASK) != MEMMAP_MASK) |
961 | SA1.PCBase = NULL; |
962 | else |
963 | SA1.PCBase = (Memory.SRAM + (((address & 0x7fff) - 0x6000 + ((address & 0xf0000) >> 3)) & Memory.SRAMMask)) - (address & 0xffff); |
964 | return; |
965 | |
966 | case CMemory::MAP_BWRAM: |
967 | SA1.PCBase = SA1.BWRAM - 0x6000 - (address & 0x8000); |
968 | return; |
969 | |
970 | case CMemory::MAP_SA1RAM: |
971 | SA1.PCBase = Memory.SRAM; |
972 | return; |
973 | |
974 | default: |
975 | SA1.PCBase = NULL; |
976 | return; |
977 | } |
978 | } |
979 | |