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