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