1 /*
2 Hatari - ioMem.c
3
4 This file is distributed under the GNU General Public License, version 2
5 or at your option any later version. Read the file gpl.txt for details.
6
7 This is where we intercept read/writes to/from the hardware. The ST's memory
8 is nicely split into four main parts - the bottom area of RAM is for user
9 programs. This is followed by a large area which causes a Bus Error. After
10 this is the ROM addresses for TOS and finally an area for hardware mapping.
11 To gain speed any address in the user area can simply read/write, but anything
12 above this range needs to be checked for validity and sent to the various
13 handlers.
14 A big problem for ST emulation is the use of the hardware registers. These
15 often consist of an 'odd' byte in memory and is usually addressed as a single
16 byte. A number of applications, however, write to the address using a word or
17 even long word. So we have a list of handlers that take care of each address
18 that has to be intercepted. Eg, a long write to a PSG register (which access
19 two registers) will write the long into IO memory space and then call the two
20 handlers which read off the bytes for each register.
21 This means that any access to any hardware register in such a way will work
22 correctly - it certainly fixes a lot of bugs and means writing just one
23 routine for each hardware register we mean to intercept! Phew!
24 You have also to take into consideration that some hardware registers are
25 bigger than 1 byte (there are also word and longword registers) and that
26 a lot of addresses in between can cause a bus error - so it's not so easy
27 to cope with all type of handlers in a straight forward way.
28 Also note the 'mirror' (or shadow) registers of the PSG - this is used by most
29 games.
30 */
31 const char IoMem_fileid[] = "Hatari ioMem.c : " __DATE__ " " __TIME__;
32
33 #include "main.h"
34 #include "configuration.h"
35 #include "ioMem.h"
36 #include "ioMemTables.h"
37 #include "memorySnapShot.h"
38 #include "m68000.h"
39 #include "sysdeps.h"
40 #include "newcpu.h"
41 #include "log.h"
42 #include "scc.h"
43
44
45 static void (*pInterceptReadTable[0x8000])(void); /* Table with read access handlers */
46 static void (*pInterceptWriteTable[0x8000])(void); /* Table with write access handlers */
47
48 int nIoMemAccessSize; /* Set to 1, 2 or 4 according to byte, word or long word access */
49 Uint32 IoAccessFullAddress; /* Store the complete 32 bit address received in the IoMem_xxx() handler */
50 /* (this is the address to write on the stack in case of a bus error) */
51 Uint32 IoAccessBaseAddress; /* Stores the base address of the IO mem access (masked on 24 bits) */
52 Uint32 IoAccessCurrentAddress; /* Current byte address while handling WORD and LONG accesses (masked on 24 bits) */
53 static int nBusErrorAccesses; /* Needed to count bus error accesses */
54
55
56 /*
57 Heuristics for better cycle accuracy when "cycle exact mode" is not used
58
59 Some instructions can do several IO accesses that will be seen as several independent accesses,
60 instead of one whole word or long word access as in the size of the instruction.
61 For example :
62 - movep.w and move.l will do 2 or 4 BYTE accesses (and not 1 WORD or LONG WORD access)
63 - move.l will do 2 WORD accesses (and not 1 LONG WORD, because ST's bus is 16 bit)
64
65 So, when a BYTE access is made, we need to know if it comes from an instruction where size=byte
66 or if it comes from a word or long word instruction.
67
68 In order to emulate correct read/write cycles when IO regs are accessed this way, we need to
69 keep track of how many accesses were made by the same instruction.
70 This will be used when CPU runs in "prefetch mode" and we try to approximate internal cycles
71 (see cycles.c for heuristics using this).
72
73 When CPU runs in "cycle exact mode", this is not used because the internal cycles will be computed
74 precisely at the CPU emulation level.
75 */
76 static Uint64 IoAccessInstrPrevClock;
77 int IoAccessInstrCount; /* Number of the accesses made in the current instruction (1..4) */
78 /* 0 means no multiple accesses in the current instruction */
79
80
81 /* Falcon bus mode (Falcon STe compatible bus or Falcon only bus) */
82 static enum FALCON_BUS_MODE falconBusMode = FALCON_ONLY_BUS;
83
84 /*-----------------------------------------------------------------------*/
85 /**
86 * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
87 */
IoMem_MemorySnapShot_Capture(bool bSave)88 void IoMem_MemorySnapShot_Capture(bool bSave)
89 {
90 enum FALCON_BUS_MODE mode = falconBusMode;
91
92 /* Save/Restore details */
93 MemorySnapShot_Store(&mode, sizeof(mode));
94 if (!bSave)
95 IoMem_SetFalconBusMode(mode);
96 }
97
98 /*-----------------------------------------------------------------------*/
99 /**
100 * Fill a region with bus error handlers.
101 */
IoMem_SetBusErrorRegion(Uint32 startaddr,Uint32 endaddr)102 static void IoMem_SetBusErrorRegion(Uint32 startaddr, Uint32 endaddr)
103 {
104 Uint32 a;
105
106 for (a = startaddr; a <= endaddr; a++)
107 {
108 if (a & 1)
109 {
110 pInterceptReadTable[a - 0xff8000] = IoMem_BusErrorOddReadAccess; /* For 'read' */
111 pInterceptWriteTable[a - 0xff8000] = IoMem_BusErrorOddWriteAccess; /* and 'write' */
112 }
113 else
114 {
115 pInterceptReadTable[a - 0xff8000] = IoMem_BusErrorEvenReadAccess; /* For 'read' */
116 pInterceptWriteTable[a - 0xff8000] = IoMem_BusErrorEvenWriteAccess; /* and 'write' */
117 }
118 }
119 }
120
121
122 /**
123 * Fill a region with void handlers.
124 */
IoMem_SetVoidRegion(Uint32 startaddr,Uint32 endaddr)125 static void IoMem_SetVoidRegion(Uint32 startaddr, Uint32 endaddr)
126 {
127 Uint32 addr;
128
129 for (addr = startaddr; addr <= endaddr; addr++)
130 {
131 pInterceptReadTable[addr - 0xff8000] = IoMem_VoidRead;
132 pInterceptWriteTable[addr - 0xff8000] = IoMem_VoidWrite;
133 }
134 }
135
136
137 /**
138 * Normal ST (with Ricoh chipset) has two address which don't generate a bus
139 * error when compared to the Mega-ST (with IMP chipset). Mark them as void
140 * handlers here.
141 */
IoMem_FixVoidAccessForST(void)142 static void IoMem_FixVoidAccessForST(void)
143 {
144 IoMem_SetVoidRegion(0xff820f, 0xff820f);
145 IoMem_SetVoidRegion(0xff860f, 0xff860f);
146 }
147
148 /**
149 * We emulate the Mega-ST with IMP chipset, and this has slightly different
150 * behavior with regards to bus errors compared to the normal ST, which we
151 * emulate with Ricoh chipset. Here we fix up the table accordingly.
152 * Note that there are also normal STs with the IMP chipset, and Mega-STs
153 * with the Ricoh chipset available, so in real life this can also be the
154 * other way round. But since the Ricoh chipset is likely the older one
155 * and the Mega-STs are the later machines, we've chosen to use IMP for the
156 * Mega and Ricoh for normal STs in Hatari.
157 */
IoMem_FixVoidAccessForMegaST(void)158 static void IoMem_FixVoidAccessForMegaST(void)
159 {
160 int i;
161 Uint32 no_be_addrs[] =
162 {
163 0xff8200, 0xff8202, 0xff8204, 0xff8206, 0xff8208,
164 0xff820c, 0xff8608, 0xff860a, 0xff860c, 0
165 };
166 Uint32 no_be_regions[][2] =
167 {
168 { 0xff8000, 0xff8000 },
169 { 0xff8002, 0xff800d },
170 { 0xff8a3e, 0xff8a3f },
171 { 0, 0 }
172 };
173
174 for (i = 0; no_be_addrs[i] != 0; i++)
175 {
176 IoMem_SetVoidRegion(no_be_addrs[i], no_be_addrs[i]);
177 }
178 for (i = 0; no_be_regions[i][0] != 0; i++)
179 {
180 IoMem_SetVoidRegion(no_be_regions[i][0], no_be_regions[i][1]);
181 }
182 }
183
184
185 /**
186 * Fix up the IO memory access table for the Mega STE.
187 */
IoMem_FixAccessForMegaSTE(void)188 static void IoMem_FixAccessForMegaSTE(void)
189 {
190 int addr;
191
192 /* Mega-STE has an additional Cache/CPU control register compared to
193 * the normal STE. The addresses before and after 0xff8e21 also do not
194 * produce a bus error on the Mega-STE. */
195 pInterceptReadTable[0xff8e20 - 0xff8000] = IoMem_VoidRead;
196 pInterceptWriteTable[0xff8e20 - 0xff8000] = IoMem_VoidWrite;
197 pInterceptReadTable[0xff8e21 - 0xff8000] = IoMem_ReadWithoutInterception;
198 pInterceptWriteTable[0xff8e21 - 0xff8000] = IoMemTabMegaSTE_CacheCpuCtrl_WriteByte;
199 pInterceptReadTable[0xff8e22 - 0xff8000] = IoMem_VoidRead;
200 pInterceptWriteTable[0xff8e22 - 0xff8000] = IoMem_VoidWrite;
201 pInterceptReadTable[0xff8e23 - 0xff8000] = IoMem_VoidRead;
202 pInterceptWriteTable[0xff8e23 - 0xff8000] = IoMem_VoidWrite;
203
204 /* VME bus - we don't support it yet, but TOS uses FF8E09 to detect the Mega-STE */
205 for (addr = 0xff8e01; addr <= 0xff8e0f; addr += 2)
206 {
207 pInterceptReadTable[addr - 0xff8000] = IoMem_ReadWithoutInterception;
208 pInterceptWriteTable[addr - 0xff8000] = IoMem_WriteWithoutInterception;
209 }
210
211 /* The Mega-STE has a Z85C30 SCC serial port, too: */
212 for (addr = 0xff8c80; addr <= 0xff8c87; addr++)
213 {
214 pInterceptReadTable[addr - 0xff8000] = SCC_IoMem_ReadByte;
215 pInterceptWriteTable[addr - 0xff8000] = SCC_IoMem_WriteByte;
216 }
217 }
218
219
220 /**
221 * Fix up table for Falcon in STE compatible bus mode (i.e. less bus errors)
222 */
IoMem_FixVoidAccessForCompatibleFalcon(void)223 static void IoMem_FixVoidAccessForCompatibleFalcon(void)
224 {
225 int i;
226 Uint32 no_be_regions[][2] =
227 {
228 { 0xff8002, 0xff8005 },
229 { 0xff8008, 0xff800b },
230 { 0xff800e, 0xff805f },
231 { 0xff8064, 0xff81ff },
232 { 0xff82c4, 0xff83ff },
233 { 0xff8804, 0xff88ff },
234 { 0xff8964, 0xff896f },
235 { 0xff8c00, 0xff8c7f },
236 { 0xff8c88, 0xff8cff },
237 { 0xff9000, 0xff91ff },
238 { 0xff9204, 0xff920f },
239 { 0xff9218, 0xff921f },
240 { 0xff9224, 0xff97ff },
241 { 0xff9c00, 0xff9fff },
242 { 0xffa200, 0xffa207 },
243 { 0, 0 }
244 };
245
246 for (i = 0; no_be_regions[i][0] != 0; i++)
247 {
248 IoMem_SetVoidRegion(no_be_regions[i][0], no_be_regions[i][1]);
249 }
250 }
251
252
253 /**
254 * Create 'intercept' tables for hardware address access. Each 'intercept
255 * table is a list of 0x8000 pointers to a list of functions to call when
256 * that location in the ST's memory is accessed.
257 */
IoMem_Init(void)258 void IoMem_Init(void)
259 {
260 Uint32 addr;
261 int i;
262 const INTERCEPT_ACCESS_FUNC *pInterceptAccessFuncs = NULL;
263
264 /* Set default IO access handler (-> bus error) */
265 IoMem_SetBusErrorRegion(0xff8000, 0xffffff);
266
267 switch (ConfigureParams.System.nMachineType)
268 {
269 case MACHINE_ST:
270 IoMem_FixVoidAccessForST();
271 pInterceptAccessFuncs = IoMemTable_ST;
272 break;
273 case MACHINE_MEGA_ST:
274 IoMem_FixVoidAccessForMegaST();
275 pInterceptAccessFuncs = IoMemTable_ST;
276 break;
277 case MACHINE_STE:
278 pInterceptAccessFuncs = IoMemTable_STE;
279 break;
280 case MACHINE_MEGA_STE:
281 IoMem_FixAccessForMegaSTE();
282 pInterceptAccessFuncs = IoMemTable_STE;
283 break;
284 case MACHINE_TT:
285 pInterceptAccessFuncs = IoMemTable_TT;
286 break;
287 case MACHINE_FALCON:
288 if (falconBusMode == STE_BUS_COMPATIBLE)
289 IoMem_FixVoidAccessForCompatibleFalcon();
290 pInterceptAccessFuncs = IoMemTable_Falcon;
291 break;
292 default:
293 abort(); /* bug */
294 }
295
296 /* Now set the correct handlers */
297 for (addr=0xff8000; addr <= 0xffffff; addr++)
298 {
299 /* Does this hardware location/span appear in our list of possible intercepted functions? */
300 for (i=0; pInterceptAccessFuncs[i].Address != 0; i++)
301 {
302 if (addr >= pInterceptAccessFuncs[i].Address
303 && addr < pInterceptAccessFuncs[i].Address+pInterceptAccessFuncs[i].SpanInBytes)
304 {
305 /* Security checks... */
306 if (pInterceptReadTable[addr-0xff8000] != IoMem_BusErrorEvenReadAccess && pInterceptReadTable[addr-0xff8000] != IoMem_BusErrorOddReadAccess)
307 Log_Printf(LOG_WARN, "IoMem_Init: $%x (R) already defined\n", addr);
308 if (pInterceptWriteTable[addr-0xff8000] != IoMem_BusErrorEvenWriteAccess && pInterceptWriteTable[addr-0xff8000] != IoMem_BusErrorOddWriteAccess)
309 Log_Printf(LOG_WARN, "IoMem_Init: $%x (W) already defined\n", addr);
310
311 /* This location needs to be intercepted, so add entry to list */
312 pInterceptReadTable[addr-0xff8000] = pInterceptAccessFuncs[i].ReadFunc;
313 pInterceptWriteTable[addr-0xff8000] = pInterceptAccessFuncs[i].WriteFunc;
314 }
315 }
316 }
317
318 /* Set registers for Falcon DSP emulation */
319 if (Config_IsMachineFalcon())
320 {
321 switch (ConfigureParams.System.nDSPType)
322 {
323 #if ENABLE_DSP_EMU
324 case DSP_TYPE_EMU:
325 IoMemTabFalcon_DSPemulation(pInterceptReadTable,
326 pInterceptWriteTable);
327 break;
328 #endif
329 case DSP_TYPE_DUMMY:
330 IoMemTabFalcon_DSPdummy(pInterceptReadTable,
331 pInterceptWriteTable);
332 break;
333 default:
334 /* none */
335 IoMemTabFalcon_DSPnone(pInterceptReadTable,
336 pInterceptWriteTable);
337 }
338 }
339
340 /* Disable blitter? */
341 if (!ConfigureParams.System.bBlitter && ConfigureParams.System.nMachineType == MACHINE_ST)
342 {
343 IoMem_SetBusErrorRegion(0xff8a00, 0xff8a3f);
344 }
345
346 /* Disable real time clock on non-Mega machines */
347 if (ConfigureParams.System.nMachineType == MACHINE_ST
348 || ConfigureParams.System.nMachineType == MACHINE_STE)
349 {
350 for (addr = 0xfffc21; addr <= 0xfffc3f; addr++)
351 {
352 pInterceptReadTable[addr - 0xff8000] = IoMem_VoidRead; /* For 'read' */
353 pInterceptWriteTable[addr - 0xff8000] = IoMem_VoidWrite; /* and 'write' */
354 }
355 }
356
357 /* Falcon PSG shadow register range setup (to void access) is already
358 * done above as part of the IoMem_FixVoidAccessForCompatibleFalcon()
359 * call (in STE bus compatible mode, otherwise they bus error)
360 */
361 if (!Config_IsMachineFalcon())
362 {
363 /* Initialize PSG shadow registers for ST, STe, TT machines */
364 for (addr = 0xff8804; addr < 0xff8900; addr++)
365 {
366 pInterceptReadTable[addr - 0xff8000] = pInterceptReadTable[(addr & 0xfff803) - 0xff8000];
367 pInterceptWriteTable[addr - 0xff8000] = pInterceptWriteTable[(addr & 0xfff803) - 0xff8000];
368 }
369 }
370 }
371
372
373 /**
374 * Uninitialize the IoMem code (currently unused).
375 */
IoMem_UnInit(void)376 void IoMem_UnInit(void)
377 {
378 }
379
380
381 /**
382 * This function is called to fix falconBusMode. This value comes from register
383 * $ff8007.b (Bit 5) and is called from ioMemTabFalcon.c.
384 */
IoMem_SetFalconBusMode(enum FALCON_BUS_MODE mode)385 void IoMem_SetFalconBusMode(enum FALCON_BUS_MODE mode)
386 {
387 if (mode != falconBusMode)
388 {
389 falconBusMode = mode;
390 IoMem_UnInit();
391 IoMem_Init();
392 }
393 }
394
IoMem_IsFalconBusMode(void)395 bool IoMem_IsFalconBusMode(void)
396 {
397 return falconBusMode == FALCON_ONLY_BUS;
398 }
399
400
401 /**
402 * During (cold) reset, we have to clean up the Falcon bus mode if necessary.
403 */
IoMem_Reset(void)404 void IoMem_Reset(void)
405 {
406 if (Config_IsMachineFalcon())
407 {
408 IoMem_SetFalconBusMode(FALCON_ONLY_BUS);
409 }
410 }
411
412 /*-----------------------------------------------------------------------*/
413 /**
414 * Handle byte read access from IO memory.
415 */
IoMem_bget(uaecptr addr)416 uae_u32 REGPARAM3 IoMem_bget(uaecptr addr)
417 {
418 Uint8 val;
419
420 IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
421
422 /* Check if access is made by a new instruction or by the same instruction doing multiple byte accesses */
423 if ( IoAccessInstrPrevClock == CyclesGlobalClockCounter )
424 IoAccessInstrCount++; /* Same instruction, increase access count */
425 else
426 {
427 IoAccessInstrPrevClock = CyclesGlobalClockCounter;
428 if ( table68k[ M68000_CurrentOpcode ].size == 0 )
429 IoAccessInstrCount = 0; /* Instruction size is byte : no multiple accesses */
430 else
431 IoAccessInstrCount = 1; /* 1st access */
432 }
433
434 addr &= 0x00ffffff; /* Use a 24 bit address */
435
436 if (addr < 0xff8000 || !regs.s)
437 {
438 /* invalid memory addressing --> bus error */
439 M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
440 return -1;
441 }
442
443 IoAccessBaseAddress = addr; /* Store access location */
444 nIoMemAccessSize = SIZE_BYTE;
445 nBusErrorAccesses = 0;
446
447 IoAccessCurrentAddress = addr;
448 pInterceptReadTable[addr-0xff8000](); /* Call handler */
449
450 /* Check if we read from a bus-error region */
451 if (nBusErrorAccesses == 1)
452 {
453 M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
454 return -1;
455 }
456
457 val = IoMem[addr];
458
459 LOG_TRACE(TRACE_IOMEM_RD, "IO read.b $%08x = $%02x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
460
461 return val;
462 }
463
464
465 /*-----------------------------------------------------------------------*/
466 /**
467 * Handle word read access from IO memory.
468 */
IoMem_wget(uaecptr addr)469 uae_u32 REGPARAM3 IoMem_wget(uaecptr addr)
470 {
471 Uint32 idx;
472 Uint16 val;
473
474 IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
475
476 /* Check if access is made by a new instruction or by the same instruction doing multiple word accesses */
477 if ( IoAccessInstrPrevClock == CyclesGlobalClockCounter )
478 IoAccessInstrCount++; /* Same instruction, increase access count */
479 else
480 {
481 IoAccessInstrPrevClock = CyclesGlobalClockCounter;
482 if ( ( table68k[ M68000_CurrentOpcode ].size == 1 )
483 && ( OpcodeFamily != i_MVMEL ) && ( OpcodeFamily != i_MVMLE ) )
484 IoAccessInstrCount = 0; /* Instruction size is word and not a movem : no multiple accesses */
485 else
486 IoAccessInstrCount = 1; /* 1st access of a long or movem.w */
487 }
488
489 addr &= 0x00ffffff; /* Use a 24 bit address */
490
491 if (addr < 0xff8000 || !regs.s)
492 {
493 /* invalid memory addressing --> bus error */
494 M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
495 return -1;
496 }
497 if (addr > 0xfffffe)
498 {
499 Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_wget($%x)\n", addr);
500 return -1;
501 }
502
503 IoAccessBaseAddress = addr; /* Store for exception frame */
504 nIoMemAccessSize = SIZE_WORD;
505 nBusErrorAccesses = 0;
506 idx = addr - 0xff8000;
507
508 IoAccessCurrentAddress = addr;
509 pInterceptReadTable[idx](); /* Call 1st handler */
510
511 if (pInterceptReadTable[idx+1] != pInterceptReadTable[idx])
512 {
513 IoAccessCurrentAddress = addr + 1;
514 pInterceptReadTable[idx+1](); /* Call 2nd handler */
515 }
516
517 /* Check if we completely read from a bus-error region */
518 if (nBusErrorAccesses == 2)
519 {
520 M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
521 return -1;
522 }
523
524 val = IoMem_ReadWord(addr);
525
526 LOG_TRACE(TRACE_IOMEM_RD, "IO read.w $%08x = $%04x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
527
528 return val;
529 }
530
531
532 /*-----------------------------------------------------------------------*/
533 /**
534 * Handle long-word read access from IO memory.
535 */
IoMem_lget(uaecptr addr)536 uae_u32 REGPARAM3 IoMem_lget(uaecptr addr)
537 {
538 Uint32 idx;
539 Uint32 val;
540 int n;
541
542 IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
543
544 /* Check if access is made by a new instruction or by the same instruction doing multiple long accesses */
545 if ( IoAccessInstrPrevClock == CyclesGlobalClockCounter )
546 IoAccessInstrCount++; /* Same instruction, increase access count */
547 else
548 {
549 IoAccessInstrPrevClock = CyclesGlobalClockCounter;
550 if ( ( OpcodeFamily != i_MVMEL ) && ( OpcodeFamily != i_MVMLE ) )
551 IoAccessInstrCount = 0; /* Instruction is not a movem : no multiple accesses */
552 else
553 IoAccessInstrCount = 1; /* 1st access of a movem.l */
554 }
555
556 addr &= 0x00ffffff; /* Use a 24 bit address */
557
558 if (addr < 0xff8000 || !regs.s)
559 {
560 /* invalid memory addressing --> bus error */
561 M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
562 return -1;
563 }
564 if (addr > 0xfffffc)
565 {
566 Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_lget($%x)\n", addr);
567 return -1;
568 }
569
570 IoAccessBaseAddress = addr; /* Store for exception frame */
571 nIoMemAccessSize = SIZE_LONG;
572 nBusErrorAccesses = 0;
573 idx = addr - 0xff8000;
574
575 IoAccessCurrentAddress = addr;
576 pInterceptReadTable[idx](); /* Call 1st handler */
577
578 for (n = 1; n < nIoMemAccessSize; n++)
579 {
580 if (pInterceptReadTable[idx+n] != pInterceptReadTable[idx+n-1])
581 {
582 IoAccessCurrentAddress = addr + n;
583 pInterceptReadTable[idx+n](); /* Call n-th handler */
584 }
585 }
586
587 /* Check if we completely read from a bus-error region */
588 if (nBusErrorAccesses == 4)
589 {
590 M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
591 return -1;
592 }
593
594 val = IoMem_ReadLong(addr);
595
596 LOG_TRACE(TRACE_IOMEM_RD, "IO read.l $%08x = $%08x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
597
598 return val;
599 }
600
601
602 /*-----------------------------------------------------------------------*/
603 /**
604 * Handle byte write access to IO memory.
605 */
IoMem_bput(uaecptr addr,uae_u32 val)606 void REGPARAM3 IoMem_bput(uaecptr addr, uae_u32 val)
607 {
608 IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
609
610 /* Check if access is made by a new instruction or by the same instruction doing multiple byte accesses */
611 if ( IoAccessInstrPrevClock == CyclesGlobalClockCounter )
612 IoAccessInstrCount++; /* Same instruction, increase access count */
613 else
614 {
615 IoAccessInstrPrevClock = CyclesGlobalClockCounter;
616 if ( table68k[ M68000_CurrentOpcode ].size == 0 )
617 IoAccessInstrCount = 0; /* Instruction size is byte : no multiple accesses */
618 else
619 IoAccessInstrCount = 1; /* 1st access */
620 }
621
622 addr &= 0x00ffffff; /* Use a 24 bit address */
623
624 LOG_TRACE(TRACE_IOMEM_WR, "IO write.b $%08x = $%02x pc=%x\n", IoAccessFullAddress, val&0xff, M68000_GetPC());
625
626 if (addr < 0xff8000 || !regs.s)
627 {
628 /* invalid memory addressing --> bus error */
629 M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
630 return;
631 }
632
633 IoAccessBaseAddress = addr; /* Store for exception frame, just in case */
634 nIoMemAccessSize = SIZE_BYTE;
635 nBusErrorAccesses = 0;
636
637 IoMem[addr] = val;
638
639 IoAccessCurrentAddress = addr;
640 pInterceptWriteTable[addr-0xff8000](); /* Call handler */
641
642 /* Check if we wrote to a bus-error region */
643 if (nBusErrorAccesses == 1)
644 {
645 M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
646 }
647 }
648
649
650 /*-----------------------------------------------------------------------*/
651 /**
652 * Handle word write access to IO memory.
653 */
IoMem_wput(uaecptr addr,uae_u32 val)654 void REGPARAM3 IoMem_wput(uaecptr addr, uae_u32 val)
655 {
656 Uint32 idx;
657
658 IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
659
660 /* Check if access is made by a new instruction or by the same instruction doing multiple word accesses */
661 if ( IoAccessInstrPrevClock == CyclesGlobalClockCounter )
662 IoAccessInstrCount++; /* Same instruction, increase access count */
663 else
664 {
665 IoAccessInstrPrevClock = CyclesGlobalClockCounter;
666 if ( ( table68k[ M68000_CurrentOpcode ].size == 1 )
667 && ( OpcodeFamily != i_MVMEL ) && ( OpcodeFamily != i_MVMLE ) )
668 IoAccessInstrCount = 0; /* Instruction size is word and not a movem : no multiple accesses */
669 else
670 IoAccessInstrCount = 1; /* 1st access of a long or movem.w */
671 }
672
673 addr &= 0x00ffffff; /* Use a 24 bit address */
674
675 LOG_TRACE(TRACE_IOMEM_WR, "IO write.w $%08x = $%04x pc=%x\n", IoAccessFullAddress, val&0xffff, M68000_GetPC());
676
677 if (addr < 0x00ff8000 || !regs.s)
678 {
679 /* invalid memory addressing --> bus error */
680 M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
681 return;
682 }
683 if (addr > 0xfffffe)
684 {
685 Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_wput($%x)\n", addr);
686 return;
687 }
688
689 IoAccessBaseAddress = addr; /* Store for exception frame, just in case */
690 nIoMemAccessSize = SIZE_WORD;
691 nBusErrorAccesses = 0;
692
693 IoMem_WriteWord(addr, val);
694 idx = addr - 0xff8000;
695
696 IoAccessCurrentAddress = addr;
697 pInterceptWriteTable[idx](); /* Call 1st handler */
698
699 if (pInterceptWriteTable[idx+1] != pInterceptWriteTable[idx])
700 {
701 IoAccessCurrentAddress = addr + 1;
702 pInterceptWriteTable[idx+1](); /* Call 2nd handler */
703 }
704
705 /* Check if we wrote to a bus-error region */
706 if (nBusErrorAccesses == 2)
707 {
708 M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
709 }
710 }
711
712
713 /*-----------------------------------------------------------------------*/
714 /**
715 * Handle long-word write access to IO memory.
716 */
IoMem_lput(uaecptr addr,uae_u32 val)717 void REGPARAM3 IoMem_lput(uaecptr addr, uae_u32 val)
718 {
719 Uint32 idx;
720 int n;
721
722 IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
723
724 /* Check if access is made by a new instruction or by the same instruction doing multiple long accesses */
725 if ( IoAccessInstrPrevClock == CyclesGlobalClockCounter )
726 IoAccessInstrCount++; /* Same instruction, increase access count */
727 else
728 {
729 IoAccessInstrPrevClock = CyclesGlobalClockCounter;
730 if ( ( OpcodeFamily != i_MVMEL ) && ( OpcodeFamily != i_MVMLE ) )
731 IoAccessInstrCount = 0; /* Instruction is not a movem : no multiple accesses */
732 else
733 IoAccessInstrCount = 1; /* 1st access of a movem.l */
734 }
735
736 addr &= 0x00ffffff; /* Use a 24 bit address */
737
738 LOG_TRACE(TRACE_IOMEM_WR, "IO write.l $%08x = $%08x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
739
740 if (addr < 0xff8000 || !regs.s)
741 {
742 /* invalid memory addressing --> bus error */
743 M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
744 return;
745 }
746 if (addr > 0xfffffc)
747 {
748 Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_lput($%x)\n", addr);
749 return;
750 }
751
752 IoAccessBaseAddress = addr; /* Store for exception frame, just in case */
753 nIoMemAccessSize = SIZE_LONG;
754 nBusErrorAccesses = 0;
755
756 IoMem_WriteLong(addr, val);
757 idx = addr - 0xff8000;
758
759 IoAccessCurrentAddress = addr;
760 pInterceptWriteTable[idx](); /* Call first handler */
761
762 for (n = 1; n < nIoMemAccessSize; n++)
763 {
764 if (pInterceptWriteTable[idx+n] != pInterceptWriteTable[idx+n-1])
765 {
766 IoAccessCurrentAddress = addr + n;
767 pInterceptWriteTable[idx+n](); /* Call n-th handler */
768 }
769 }
770
771 /* Check if we wrote to a bus-error region */
772 if (nBusErrorAccesses == 4)
773 {
774 M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
775 }
776 }
777
778
779 /*-------------------------------------------------------------------------*/
780 /**
781 * This handler will be called if a ST program tries to read from an address
782 * that causes a bus error on a real ST. However, we can't call M68000_BusError()
783 * directly: For example, a "move.b $ff8204,d0" triggers a bus error on a real ST,
784 * while a "move.w $ff8204,d0" works! So we have to count the accesses to bus error
785 * addresses and we only trigger a bus error later if the count matches the complete
786 * access size (e.g. nBusErrorAccesses==4 for a long word access).
787 */
IoMem_BusErrorEvenReadAccess(void)788 void IoMem_BusErrorEvenReadAccess(void)
789 {
790 nBusErrorAccesses += 1;
791 IoMem[IoAccessCurrentAddress] = 0xff;
792 }
793
794 /**
795 * We need two handler so that the IoMem_*get functions can distinguish
796 * consecutive addresses.
797 */
IoMem_BusErrorOddReadAccess(void)798 void IoMem_BusErrorOddReadAccess(void)
799 {
800 nBusErrorAccesses += 1;
801 IoMem[IoAccessCurrentAddress] = 0xff;
802 }
803
804 /*-------------------------------------------------------------------------*/
805 /**
806 * Same as IoMem_BusErrorReadAccess() but for write access this time.
807 */
IoMem_BusErrorEvenWriteAccess(void)808 void IoMem_BusErrorEvenWriteAccess(void)
809 {
810 nBusErrorAccesses += 1;
811 }
812
813 /**
814 * We need two handler so that the IoMem_*put functions can distinguish
815 * consecutive addresses.
816 */
IoMem_BusErrorOddWriteAccess(void)817 void IoMem_BusErrorOddWriteAccess(void)
818 {
819 nBusErrorAccesses += 1;
820 }
821
822
823 /*-------------------------------------------------------------------------*/
824 /**
825 * This is the read handler for the IO memory locations without an assigned
826 * IO register and which also do not generate a bus error. Reading from such
827 * a register will return the result 0xff.
828 */
IoMem_VoidRead(void)829 void IoMem_VoidRead(void)
830 {
831 Uint32 a;
832
833 /* handler is probably called only once, so we have to take care of the neighbour "void IO registers" */
834 for (a = IoAccessBaseAddress; a < IoAccessBaseAddress + nIoMemAccessSize; a++)
835 {
836 if (pInterceptReadTable[a - 0xff8000] == IoMem_VoidRead)
837 {
838 IoMem[a] = 0xff;
839 }
840 }
841 }
842
843 /*-------------------------------------------------------------------------*/
844 /**
845 * This is the same function as IoMem_VoidRead, but for IO registers that
846 * return 0x00 instead of 0xff when read (this is the case for some video
847 * registers on STE, Falcon, ...)
848 */
IoMem_VoidRead_00(void)849 void IoMem_VoidRead_00(void)
850 {
851 Uint32 a;
852
853 /* handler is probably called only once, so we have to take care of the neighbour "void IO registers" */
854 for (a = IoAccessBaseAddress; a < IoAccessBaseAddress + nIoMemAccessSize; a++)
855 {
856 if (pInterceptReadTable[a - 0xff8000] == IoMem_VoidRead_00)
857 {
858 IoMem[a] = 0x00;
859 }
860 }
861 }
862
863 /*-------------------------------------------------------------------------*/
864 /**
865 * This is the write handler for the IO memory locations without an assigned
866 * IO register and which also do not generate a bus error. We simply ignore
867 * a write access to these registers.
868 */
IoMem_VoidWrite(void)869 void IoMem_VoidWrite(void)
870 {
871 /* Nothing... */
872 }
873
874
875 /*-------------------------------------------------------------------------*/
876 /**
877 * A dummy function that does nothing at all - for memory regions that don't
878 * need a special handler for read access.
879 */
IoMem_ReadWithoutInterception(void)880 void IoMem_ReadWithoutInterception(void)
881 {
882 /* Nothing... */
883 }
884
885 /*-------------------------------------------------------------------------*/
886 /**
887 * A dummy function that does nothing at all - for memory regions that don't
888 * need a special handler for write access.
889 */
IoMem_WriteWithoutInterception(void)890 void IoMem_WriteWithoutInterception(void)
891 {
892 /* Nothing... */
893 }
894