|
|
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.1.15! root 41: #include "log.h"
! 42: #include "scc.h"
1.1 root 43:
44:
1.1.1.14 root 45: static void (*pInterceptReadTable[0x8000])(void); /* Table with read access handlers */
46: static void (*pInterceptWriteTable[0x8000])(void); /* Table with write access handlers */
1.1 root 47:
1.1.1.14 root 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 */
1.1 root 54:
1.1.1.13 root 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:
1.1.1.8 root 81: /* Falcon bus mode (Falcon STe compatible bus or Falcon only bus) */
1.1.1.15! root 82: static enum FALCON_BUS_MODE falconBusMode = FALCON_ONLY_BUS;
1.1.1.8 root 83:
84: /*-----------------------------------------------------------------------*/
85: /**
86: * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
87: */
88: void IoMem_MemorySnapShot_Capture(bool bSave)
89: {
1.1.1.15! root 90: enum FALCON_BUS_MODE mode = falconBusMode;
! 91:
1.1.1.8 root 92: /* Save/Restore details */
1.1.1.15! root 93: MemorySnapShot_Store(&mode, sizeof(mode));
! 94: if (!bSave)
! 95: IoMem_SetFalconBusMode(mode);
1.1.1.8 root 96: }
1.1 root 97:
98: /*-----------------------------------------------------------------------*/
1.1.1.3 root 99: /**
100: * Fill a region with bus error handlers.
101: */
1.1 root 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:
1.1.1.13 root 122: /**
123: * Fill a region with void handlers.
124: */
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: /**
1.1.1.15! root 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.
1.1.1.13 root 141: */
142: static void IoMem_FixVoidAccessForST(void)
143: {
144: IoMem_SetVoidRegion(0xff820f, 0xff820f);
145: IoMem_SetVoidRegion(0xff860f, 0xff860f);
146: }
147:
148: /**
1.1.1.15! root 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.
1.1.1.13 root 157: */
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: */
188: static void IoMem_FixAccessForMegaSTE(void)
189: {
190: int addr;
191:
1.1.1.15! root 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;
1.1.1.13 root 197: pInterceptReadTable[0xff8e21 - 0xff8000] = IoMem_ReadWithoutInterception;
198: pInterceptWriteTable[0xff8e21 - 0xff8000] = IoMemTabMegaSTE_CacheCpuCtrl_WriteByte;
1.1.1.15! root 199: pInterceptReadTable[0xff8e22 - 0xff8000] = IoMem_VoidRead;
! 200: pInterceptWriteTable[0xff8e22 - 0xff8000] = IoMem_VoidWrite;
! 201: pInterceptReadTable[0xff8e23 - 0xff8000] = IoMem_VoidRead;
! 202: pInterceptWriteTable[0xff8e23 - 0xff8000] = IoMem_VoidWrite;
1.1.1.13 root 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: }
1.1.1.15! root 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: }
1.1.1.13 root 217: }
218:
219:
220: /**
221: * Fix up table for Falcon in STE compatible bus mode (i.e. less bus errors)
222: */
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:
1.1.1.3 root 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: */
1.1 root 258: void IoMem_Init(void)
259: {
260: Uint32 addr;
261: int i;
1.1.1.2 root 262: const INTERCEPT_ACCESS_FUNC *pInterceptAccessFuncs = NULL;
1.1 root 263:
264: /* Set default IO access handler (-> bus error) */
265: IoMem_SetBusErrorRegion(0xff8000, 0xffffff);
266:
267: switch (ConfigureParams.System.nMachineType)
268: {
1.1.1.13 root 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 */
1.1 root 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)
1.1.1.15! root 307: Log_Printf(LOG_WARN, "IoMem_Init: $%x (R) already defined\n", addr);
1.1 root 308: if (pInterceptWriteTable[addr-0xff8000] != IoMem_BusErrorEvenWriteAccess && pInterceptWriteTable[addr-0xff8000] != IoMem_BusErrorOddWriteAccess)
1.1.1.15! root 309: Log_Printf(LOG_WARN, "IoMem_Init: $%x (W) already defined\n", addr);
1.1 root 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:
1.1.1.4 root 318: /* Set registers for Falcon DSP emulation */
1.1.1.13 root 319: if (Config_IsMachineFalcon())
1.1.1.3 root 320: {
1.1.1.4 root 321: switch (ConfigureParams.System.nDSPType)
322: {
1.1.1.3 root 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:
1.1 root 340: /* Disable blitter? */
1.1.1.3 root 341: if (!ConfigureParams.System.bBlitter && ConfigureParams.System.nMachineType == MACHINE_ST)
1.1 root 342: {
343: IoMem_SetBusErrorRegion(0xff8a00, 0xff8a3f);
344: }
345:
1.1.1.13 root 346: /* Disable real time clock on non-Mega machines */
347: if (ConfigureParams.System.nMachineType == MACHINE_ST
348: || ConfigureParams.System.nMachineType == MACHINE_STE)
1.1 root 349: {
1.1.1.4 root 350: for (addr = 0xfffc21; addr <= 0xfffc3f; addr++)
1.1 root 351: {
352: pInterceptReadTable[addr - 0xff8000] = IoMem_VoidRead; /* For 'read' */
353: pInterceptWriteTable[addr - 0xff8000] = IoMem_VoidWrite; /* and 'write' */
354: }
1.1.1.4 root 355: }
356:
1.1.1.14 root 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: */
1.1.1.13 root 361: if (!Config_IsMachineFalcon())
1.1.1.4 root 362: {
1.1.1.14 root 363: /* Initialize PSG shadow registers for ST, STe, TT machines */
1.1.1.4 root 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: }
1.1.1.8 root 369: }
1.1 root 370: }
371:
1.1.1.15! root 372:
1.1.1.8 root 373: /**
1.1.1.15! root 374: * Uninitialize the IoMem code (currently unused).
1.1.1.8 root 375: */
1.1.1.15! root 376: void IoMem_UnInit(void)
1.1.1.8 root 377: {
378: }
1.1 root 379:
1.1.1.15! root 380:
1.1.1.3 root 381: /**
1.1.1.15! root 382: * This function is called to fix falconBusMode. This value comes from register
! 383: * $ff8007.b (Bit 5) and is called from ioMemTabFalcon.c.
1.1.1.3 root 384: */
1.1.1.15! root 385: void IoMem_SetFalconBusMode(enum FALCON_BUS_MODE mode)
1.1 root 386: {
1.1.1.15! root 387: if (mode != falconBusMode)
! 388: {
! 389: falconBusMode = mode;
! 390: IoMem_UnInit();
! 391: IoMem_Init();
! 392: }
1.1 root 393: }
394:
1.1.1.15! root 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: */
! 404: void IoMem_Reset(void)
! 405: {
! 406: if (Config_IsMachineFalcon())
! 407: {
! 408: IoMem_SetFalconBusMode(FALCON_ONLY_BUS);
! 409: }
! 410: }
1.1 root 411:
412: /*-----------------------------------------------------------------------*/
1.1.1.3 root 413: /**
414: * Handle byte read access from IO memory.
415: */
1.1.1.13 root 416: uae_u32 REGPARAM3 IoMem_bget(uaecptr addr)
1.1 root 417: {
1.1.1.6 root 418: Uint8 val;
1.1 root 419:
1.1.1.14 root 420: IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
421:
1.1.1.13 root 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:
1.1 root 434: addr &= 0x00ffffff; /* Use a 24 bit address */
435:
1.1.1.9 root 436: if (addr < 0xff8000 || !regs.s)
1.1 root 437: {
438: /* invalid memory addressing --> bus error */
1.1.1.14 root 439: M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1 root 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: {
1.1.1.14 root 453: M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1 root 454: return -1;
455: }
456:
1.1.1.6 root 457: val = IoMem[addr];
458:
1.1.1.14 root 459: LOG_TRACE(TRACE_IOMEM_RD, "IO read.b $%08x = $%02x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
1.1.1.6 root 460:
461: return val;
1.1 root 462: }
463:
464:
465: /*-----------------------------------------------------------------------*/
1.1.1.3 root 466: /**
467: * Handle word read access from IO memory.
468: */
1.1.1.13 root 469: uae_u32 REGPARAM3 IoMem_wget(uaecptr addr)
1.1 root 470: {
471: Uint32 idx;
1.1.1.6 root 472: Uint16 val;
1.1 root 473:
1.1.1.14 root 474: IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
475:
1.1.1.13 root 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:
1.1 root 489: addr &= 0x00ffffff; /* Use a 24 bit address */
490:
1.1.1.9 root 491: if (addr < 0xff8000 || !regs.s)
1.1 root 492: {
493: /* invalid memory addressing --> bus error */
1.1.1.14 root 494: M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
1.1 root 495: return -1;
496: }
497: if (addr > 0xfffffe)
498: {
1.1.1.15! root 499: Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_wget($%x)\n", addr);
1.1 root 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: {
1.1.1.14 root 520: M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
1.1 root 521: return -1;
522: }
523:
1.1.1.6 root 524: val = IoMem_ReadWord(addr);
525:
1.1.1.14 root 526: LOG_TRACE(TRACE_IOMEM_RD, "IO read.w $%08x = $%04x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
1.1.1.6 root 527:
528: return val;
1.1 root 529: }
530:
531:
532: /*-----------------------------------------------------------------------*/
1.1.1.3 root 533: /**
534: * Handle long-word read access from IO memory.
535: */
1.1.1.13 root 536: uae_u32 REGPARAM3 IoMem_lget(uaecptr addr)
1.1 root 537: {
538: Uint32 idx;
1.1.1.6 root 539: Uint32 val;
1.1.1.12 root 540: int n;
1.1 root 541:
1.1.1.14 root 542: IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
543:
1.1.1.13 root 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:
1.1 root 556: addr &= 0x00ffffff; /* Use a 24 bit address */
557:
1.1.1.9 root 558: if (addr < 0xff8000 || !regs.s)
1.1 root 559: {
560: /* invalid memory addressing --> bus error */
1.1.1.14 root 561: M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
1.1 root 562: return -1;
563: }
564: if (addr > 0xfffffc)
565: {
1.1.1.15! root 566: Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_lget($%x)\n", addr);
1.1 root 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:
1.1.1.12 root 578: for (n = 1; n < nIoMemAccessSize; n++)
1.1 root 579: {
1.1.1.12 root 580: if (pInterceptReadTable[idx+n] != pInterceptReadTable[idx+n-1])
581: {
582: IoAccessCurrentAddress = addr + n;
583: pInterceptReadTable[idx+n](); /* Call n-th handler */
584: }
1.1 root 585: }
586:
587: /* Check if we completely read from a bus-error region */
588: if (nBusErrorAccesses == 4)
589: {
1.1.1.14 root 590: M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
1.1 root 591: return -1;
592: }
593:
1.1.1.6 root 594: val = IoMem_ReadLong(addr);
595:
1.1.1.14 root 596: LOG_TRACE(TRACE_IOMEM_RD, "IO read.l $%08x = $%08x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
1.1.1.6 root 597:
598: return val;
1.1 root 599: }
600:
601:
602: /*-----------------------------------------------------------------------*/
1.1.1.3 root 603: /**
604: * Handle byte write access to IO memory.
605: */
1.1.1.13 root 606: void REGPARAM3 IoMem_bput(uaecptr addr, uae_u32 val)
1.1 root 607: {
1.1.1.14 root 608: IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
609:
1.1.1.13 root 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:
1.1 root 622: addr &= 0x00ffffff; /* Use a 24 bit address */
623:
1.1.1.14 root 624: LOG_TRACE(TRACE_IOMEM_WR, "IO write.b $%08x = $%02x pc=%x\n", IoAccessFullAddress, val&0xff, M68000_GetPC());
1.1.1.6 root 625:
1.1.1.5 root 626: if (addr < 0xff8000 || !regs.s)
1.1 root 627: {
628: /* invalid memory addressing --> bus error */
1.1.1.14 root 629: M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1 root 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: {
1.1.1.14 root 645: M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1 root 646: }
647: }
648:
649:
650: /*-----------------------------------------------------------------------*/
1.1.1.3 root 651: /**
652: * Handle word write access to IO memory.
653: */
1.1.1.13 root 654: void REGPARAM3 IoMem_wput(uaecptr addr, uae_u32 val)
1.1 root 655: {
656: Uint32 idx;
657:
1.1.1.14 root 658: IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
659:
1.1.1.13 root 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:
1.1 root 673: addr &= 0x00ffffff; /* Use a 24 bit address */
674:
1.1.1.14 root 675: LOG_TRACE(TRACE_IOMEM_WR, "IO write.w $%08x = $%04x pc=%x\n", IoAccessFullAddress, val&0xffff, M68000_GetPC());
1.1.1.6 root 676:
1.1.1.5 root 677: if (addr < 0x00ff8000 || !regs.s)
1.1 root 678: {
679: /* invalid memory addressing --> bus error */
1.1.1.14 root 680: M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
1.1 root 681: return;
682: }
683: if (addr > 0xfffffe)
684: {
1.1.1.15! root 685: Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_wput($%x)\n", addr);
1.1 root 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: {
1.1.1.14 root 708: M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
1.1 root 709: }
710: }
711:
712:
713: /*-----------------------------------------------------------------------*/
1.1.1.3 root 714: /**
715: * Handle long-word write access to IO memory.
716: */
1.1.1.13 root 717: void REGPARAM3 IoMem_lput(uaecptr addr, uae_u32 val)
1.1 root 718: {
719: Uint32 idx;
1.1.1.12 root 720: int n;
1.1 root 721:
1.1.1.14 root 722: IoAccessFullAddress = addr; /* Store initial 32 bits address (eg for bus error stack) */
723:
1.1.1.13 root 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:
1.1 root 736: addr &= 0x00ffffff; /* Use a 24 bit address */
737:
1.1.1.14 root 738: LOG_TRACE(TRACE_IOMEM_WR, "IO write.l $%08x = $%08x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
1.1.1.6 root 739:
1.1.1.5 root 740: if (addr < 0xff8000 || !regs.s)
1.1 root 741: {
742: /* invalid memory addressing --> bus error */
1.1.1.14 root 743: M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
1.1 root 744: return;
745: }
746: if (addr > 0xfffffc)
747: {
1.1.1.15! root 748: Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_lput($%x)\n", addr);
1.1 root 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;
1.1.1.12 root 760: pInterceptWriteTable[idx](); /* Call first handler */
1.1 root 761:
1.1.1.12 root 762: for (n = 1; n < nIoMemAccessSize; n++)
1.1 root 763: {
1.1.1.12 root 764: if (pInterceptWriteTable[idx+n] != pInterceptWriteTable[idx+n-1])
765: {
766: IoAccessCurrentAddress = addr + n;
767: pInterceptWriteTable[idx+n](); /* Call n-th handler */
768: }
1.1 root 769: }
770:
771: /* Check if we wrote to a bus-error region */
772: if (nBusErrorAccesses == 4)
773: {
1.1.1.14 root 774: M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
1.1 root 775: }
776: }
777:
778:
779: /*-------------------------------------------------------------------------*/
1.1.1.3 root 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: */
1.1 root 788: void IoMem_BusErrorEvenReadAccess(void)
789: {
790: nBusErrorAccesses += 1;
791: IoMem[IoAccessCurrentAddress] = 0xff;
792: }
793:
1.1.1.3 root 794: /**
795: * We need two handler so that the IoMem_*get functions can distinguish
796: * consecutive addresses.
797: */
1.1 root 798: void IoMem_BusErrorOddReadAccess(void)
799: {
800: nBusErrorAccesses += 1;
801: IoMem[IoAccessCurrentAddress] = 0xff;
802: }
803:
804: /*-------------------------------------------------------------------------*/
1.1.1.3 root 805: /**
806: * Same as IoMem_BusErrorReadAccess() but for write access this time.
807: */
1.1 root 808: void IoMem_BusErrorEvenWriteAccess(void)
809: {
810: nBusErrorAccesses += 1;
811: }
812:
1.1.1.3 root 813: /**
814: * We need two handler so that the IoMem_*put functions can distinguish
815: * consecutive addresses.
816: */
1.1 root 817: void IoMem_BusErrorOddWriteAccess(void)
818: {
819: nBusErrorAccesses += 1;
820: }
821:
822:
823: /*-------------------------------------------------------------------------*/
1.1.1.3 root 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: */
1.1 root 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: /*-------------------------------------------------------------------------*/
1.1.1.3 root 844: /**
1.1.1.8 root 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: */
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: /**
1.1.1.3 root 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: */
1.1 root 869: void IoMem_VoidWrite(void)
870: {
871: /* Nothing... */
872: }
873:
874:
875: /*-------------------------------------------------------------------------*/
1.1.1.3 root 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: */
1.1 root 880: void IoMem_ReadWithoutInterception(void)
881: {
882: /* Nothing... */
883: }
884:
885: /*-------------------------------------------------------------------------*/
1.1.1.3 root 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: */
1.1 root 890: void IoMem_WriteWithoutInterception(void)
891: {
892: /* Nothing... */
893: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.