|
|
1.1 root 1: /*
2: Hatari - ioMem.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
6:
7: This is where we intercept read/writes to/from the hardware. The ST's memory
8: is nicely split into four main parts - the bottom area of RAM is for user
9: programs. This is followed by a large area which causes a Bus Error. After
10: this is the ROM addresses for TOS and finally an area for hardware mapping.
11: To gain speed any address in the user area can simply read/write, but anything
12: above this range needs to be checked for validity and sent to the various
13: handlers.
14: A big problem for ST emulation is the use of the hardware registers. These
15: often consist of an 'odd' byte in memory and is usually addressed as a single
16: byte. A number of applications, however, write to the address using a word or
17: even long word. So we have a list of handlers that take care of each address
18: that has to be intercepted. Eg, a long write to a PSG register (which access
19: two registers) will write the long into IO memory space and then call the two
20: handlers which read off the bytes for each register.
21: This means that any access to any hardware register in such a way will work
22: correctly - it certainly fixes a lot of bugs and means writing just one
23: routine for each hardware register we mean to intercept! Phew!
24: You have also to take into consideration that some hardware registers are
25: bigger than 1 byte (there are also word and longword registers) and that
26: a lot of addresses in between can cause a bus error - so it's not so easy
27: to cope with all type of handlers in a straight forward way.
28: Also note the 'mirror' (or shadow) registers of the PSG - this is used by most
29: games.
30: */
1.1.1.3 ! root 31: const char IoMem_rcsid[] = "Hatari $Id: ioMem.c,v 1.20 2008/02/20 20:07:54 npomarede Exp $";
1.1 root 32:
33: #include "main.h"
34: #include "configuration.h"
35: #include "ioMem.h"
36: #include "ioMemTables.h"
37: #include "m68000.h"
1.1.1.3 ! root 38: #include "sysdeps.h"
1.1 root 39:
40: #define IOMEM_DEBUG 0
41:
42: #if IOMEM_DEBUG
43: #define Dprintf(a) printf a
44: #else
45: #define Dprintf(a)
46: #endif
47:
48:
49:
50: static void (*pInterceptReadTable[0x8000])(void); /* Table with read access handlers */
51: static void (*pInterceptWriteTable[0x8000])(void); /* Table with write access handlers */
52:
53: int nIoMemAccessSize; /* Set to 1, 2 or 4 according to byte, word or long word access */
54: Uint32 IoAccessBaseAddress; /* Stores the base address of the IO mem access */
1.1.1.3 ! root 55: Uint32 IoAccessCurrentAddress; /* Current byte address while handling WORD and LONG accesses */
1.1 root 56: static int nBusErrorAccesses; /* Needed to count bus error accesses */
57:
58:
59: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 60: /**
! 61: * Fill a region with bus error handlers.
! 62: */
1.1 root 63: static void IoMem_SetBusErrorRegion(Uint32 startaddr, Uint32 endaddr)
64: {
65: Uint32 a;
66:
67: for (a = startaddr; a <= endaddr; a++)
68: {
69: if (a & 1)
70: {
71: pInterceptReadTable[a - 0xff8000] = IoMem_BusErrorOddReadAccess; /* For 'read' */
72: pInterceptWriteTable[a - 0xff8000] = IoMem_BusErrorOddWriteAccess; /* and 'write' */
73: }
74: else
75: {
76: pInterceptReadTable[a - 0xff8000] = IoMem_BusErrorEvenReadAccess; /* For 'read' */
77: pInterceptWriteTable[a - 0xff8000] = IoMem_BusErrorEvenWriteAccess; /* and 'write' */
78: }
79: }
80: }
81:
82:
83: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 84: /**
! 85: * Create 'intercept' tables for hardware address access. Each 'intercept
! 86: * table is a list of 0x8000 pointers to a list of functions to call when
! 87: * that location in the ST's memory is accessed.
! 88: */
1.1 root 89: void IoMem_Init(void)
90: {
91: Uint32 addr;
92: int i;
1.1.1.2 root 93: const INTERCEPT_ACCESS_FUNC *pInterceptAccessFuncs = NULL;
1.1 root 94:
95: /* Set default IO access handler (-> bus error) */
96: IoMem_SetBusErrorRegion(0xff8000, 0xffffff);
97:
98: switch (ConfigureParams.System.nMachineType)
99: {
100: case MACHINE_ST: pInterceptAccessFuncs = IoMemTable_ST; break;
101: case MACHINE_STE: pInterceptAccessFuncs = IoMemTable_STE; break;
1.1.1.2 root 102: case MACHINE_TT: pInterceptAccessFuncs = IoMemTable_TT; break;
1.1.1.3 ! root 103: case MACHINE_FALCON: pInterceptAccessFuncs = IoMemTable_Falcon; break;
1.1 root 104: }
105:
106: /* Now set the correct handlers */
107: for (addr=0xff8000; addr <= 0xffffff; addr++)
108: {
109: /* Does this hardware location/span appear in our list of possible intercepted functions? */
110: for (i=0; pInterceptAccessFuncs[i].Address != 0; i++)
111: {
112: if (addr >= pInterceptAccessFuncs[i].Address
113: && addr < pInterceptAccessFuncs[i].Address+pInterceptAccessFuncs[i].SpanInBytes)
114: {
115: /* Security checks... */
116: if (pInterceptReadTable[addr-0xff8000] != IoMem_BusErrorEvenReadAccess && pInterceptReadTable[addr-0xff8000] != IoMem_BusErrorOddReadAccess)
117: fprintf(stderr, "IoMem_Init: Warning: $%x (R) already defined\n", addr);
118: if (pInterceptWriteTable[addr-0xff8000] != IoMem_BusErrorEvenWriteAccess && pInterceptWriteTable[addr-0xff8000] != IoMem_BusErrorOddWriteAccess)
119: fprintf(stderr, "IoMem_Init: Warning: $%x (W) already defined\n", addr);
120:
121: /* This location needs to be intercepted, so add entry to list */
122: pInterceptReadTable[addr-0xff8000] = pInterceptAccessFuncs[i].ReadFunc;
123: pInterceptWriteTable[addr-0xff8000] = pInterceptAccessFuncs[i].WriteFunc;
124: }
125: }
126: }
127:
1.1.1.3 ! root 128: if (ConfigureParams.System.nMachineType == MACHINE_FALCON)
! 129: {
! 130: switch (ConfigureParams.System.nDSPType) {
! 131: #if ENABLE_DSP_EMU
! 132: case DSP_TYPE_EMU:
! 133: IoMemTabFalcon_DSPemulation(pInterceptReadTable,
! 134: pInterceptWriteTable);
! 135: break;
! 136: #endif
! 137: case DSP_TYPE_DUMMY:
! 138: IoMemTabFalcon_DSPdummy(pInterceptReadTable,
! 139: pInterceptWriteTable);
! 140: break;
! 141: default:
! 142: /* none */
! 143: IoMemTabFalcon_DSPnone(pInterceptReadTable,
! 144: pInterceptWriteTable);
! 145: }
! 146: }
! 147:
1.1 root 148: /* Disable blitter? */
1.1.1.3 ! root 149: if (!ConfigureParams.System.bBlitter && ConfigureParams.System.nMachineType == MACHINE_ST)
1.1 root 150: {
151: IoMem_SetBusErrorRegion(0xff8a00, 0xff8a3f);
152: }
153:
154: /* Disable real time clock? */
155: if (!ConfigureParams.System.bRealTimeClock)
156: {
157: for (addr = 0xfffc21; addr <= 0xfffc3f; addr++)
158: {
159: pInterceptReadTable[addr - 0xff8000] = IoMem_VoidRead; /* For 'read' */
160: pInterceptWriteTable[addr - 0xff8000] = IoMem_VoidWrite; /* and 'write' */
161: }
162:
163: }
164: }
165:
166:
167: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 168: /**
! 169: * Uninitialize the IoMem code (currently unused).
! 170: */
1.1 root 171: void IoMem_UnInit(void)
172: {
173: }
174:
175:
176: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 177: /**
! 178: * Check if need to change our address as maybe a mirror register.
! 179: * Currently we only have a PSG mirror area.
! 180: */
1.1 root 181: static Uint32 IoMem_CheckMirrorAddresses(Uint32 addr)
182: {
183: if (addr>=0xff8800 && addr<0xff8900) /* Is a PSG mirror registers? */
184: addr = 0xff8800 + (addr & 3); /* Bring into 0xff8800-0xff8804 range */
185:
186: return addr;
187: }
188:
189:
190:
191: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 192: /**
! 193: * Handle byte read access from IO memory.
! 194: */
1.1 root 195: uae_u32 IoMem_bget(uaecptr addr)
196: {
197: Dprintf(("IoMem_bget($%x)\n", addr));
198:
199: addr &= 0x00ffffff; /* Use a 24 bit address */
200:
201: if (addr < 0xff8000)
202: {
203: /* invalid memory addressing --> bus error */
204: M68000_BusError(addr, 1);
205: return -1;
206: }
207:
208: IoAccessBaseAddress = addr; /* Store access location */
209: nIoMemAccessSize = SIZE_BYTE;
210: nBusErrorAccesses = 0;
211: addr = IoMem_CheckMirrorAddresses(addr);
212:
213: IoAccessCurrentAddress = addr;
214: pInterceptReadTable[addr-0xff8000](); /* Call handler */
215:
216: /* Check if we read from a bus-error region */
217: if (nBusErrorAccesses == 1)
218: {
219: M68000_BusError(addr, 1);
220: return -1;
221: }
222:
223: return IoMem[addr];
224: }
225:
226:
227: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 228: /**
! 229: * Handle word read access from IO memory.
! 230: */
1.1 root 231: uae_u32 IoMem_wget(uaecptr addr)
232: {
233: Uint32 idx;
234:
235: Dprintf(("IoMem_wget($%x)\n", addr));
236:
237: addr &= 0x00ffffff; /* Use a 24 bit address */
238:
239: if (addr < 0xff8000)
240: {
241: /* invalid memory addressing --> bus error */
242: M68000_BusError(addr, 1);
243: return -1;
244: }
245: if (addr > 0xfffffe)
246: {
247: fprintf(stderr, "Illegal IO memory access: IoMem_wget($%x)\n", addr);
248: return -1;
249: }
250:
251: IoAccessBaseAddress = addr; /* Store for exception frame */
252: nIoMemAccessSize = SIZE_WORD;
253: nBusErrorAccesses = 0;
254: addr = IoMem_CheckMirrorAddresses(addr);
255: idx = addr - 0xff8000;
256:
257: IoAccessCurrentAddress = addr;
258: pInterceptReadTable[idx](); /* Call 1st handler */
259:
260: if (pInterceptReadTable[idx+1] != pInterceptReadTable[idx])
261: {
262: IoAccessCurrentAddress = addr + 1;
263: pInterceptReadTable[idx+1](); /* Call 2nd handler */
264: }
265:
266: /* Check if we completely read from a bus-error region */
267: if (nBusErrorAccesses == 2)
268: {
269: M68000_BusError(addr, 1);
270: return -1;
271: }
272:
273: return IoMem_ReadWord(addr);
274: }
275:
276:
277: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 278: /**
! 279: * Handle long-word read access from IO memory.
! 280: */
1.1 root 281: uae_u32 IoMem_lget(uaecptr addr)
282: {
283: Uint32 idx;
284:
285: Dprintf(("IoMem_lget($%x)\n", addr));
286:
287: addr &= 0x00ffffff; /* Use a 24 bit address */
288:
289: if (addr < 0xff8000)
290: {
291: /* invalid memory addressing --> bus error */
292: M68000_BusError(addr, 1);
293: return -1;
294: }
295: if (addr > 0xfffffc)
296: {
297: fprintf(stderr, "Illegal IO memory access: IoMem_lget($%x)\n", addr);
298: return -1;
299: }
300:
301: IoAccessBaseAddress = addr; /* Store for exception frame */
302: nIoMemAccessSize = SIZE_LONG;
303: nBusErrorAccesses = 0;
304: addr = IoMem_CheckMirrorAddresses(addr);
305: idx = addr - 0xff8000;
306:
307: IoAccessCurrentAddress = addr;
308: pInterceptReadTable[idx](); /* Call 1st handler */
309:
310: if (pInterceptReadTable[idx+1] != pInterceptReadTable[idx])
311: {
312: IoAccessCurrentAddress = addr + 1;
313: pInterceptReadTable[idx+1](); /* Call 2nd handler */
314: }
315:
316: if (pInterceptReadTable[idx+2] != pInterceptReadTable[idx+1])
317: {
318: IoAccessCurrentAddress = addr + 2;
319: pInterceptReadTable[idx+2](); /* Call 3rd handler */
320: }
321:
322: if (pInterceptReadTable[idx+3] != pInterceptReadTable[idx+2])
323: {
324: IoAccessCurrentAddress = addr + 3;
325: pInterceptReadTable[idx+3](); /* Call 4th handler */
326: }
327:
328: /* Check if we completely read from a bus-error region */
329: if (nBusErrorAccesses == 4)
330: {
331: M68000_BusError(addr, 1);
332: return -1;
333: }
334:
335: return IoMem_ReadLong(addr);
336: }
337:
338:
339: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 340: /**
! 341: * Handle byte write access to IO memory.
! 342: */
1.1 root 343: void IoMem_bput(uaecptr addr, uae_u32 val)
344: {
345: Dprintf(("IoMem_bput($%x, $%x)\n", addr, val));
346:
347: addr &= 0x00ffffff; /* Use a 24 bit address */
348:
349: if (addr < 0xff8000)
350: {
351: /* invalid memory addressing --> bus error */
352: M68000_BusError(addr, 0);
353: return;
354: }
355:
356: IoAccessBaseAddress = addr; /* Store for exception frame, just in case */
357: nIoMemAccessSize = SIZE_BYTE;
358: nBusErrorAccesses = 0;
359: addr = IoMem_CheckMirrorAddresses(addr);
360:
361: IoMem[addr] = val;
362:
363: IoAccessCurrentAddress = addr;
364: pInterceptWriteTable[addr-0xff8000](); /* Call handler */
365:
366: /* Check if we wrote to a bus-error region */
367: if (nBusErrorAccesses == 1)
368: {
369: M68000_BusError(addr, 0);
370: }
371: }
372:
373:
374: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 375: /**
! 376: * Handle word write access to IO memory.
! 377: */
1.1 root 378: void IoMem_wput(uaecptr addr, uae_u32 val)
379: {
380: Uint32 idx;
381:
382: Dprintf(("IoMem_wput($%x, $%x)\n", addr, val));
383:
384: addr &= 0x00ffffff; /* Use a 24 bit address */
385:
386: if (addr < 0x00ff8000)
387: {
388: /* invalid memory addressing --> bus error */
389: M68000_BusError(addr, 0);
390: return;
391: }
392: if (addr > 0xfffffe)
393: {
394: fprintf(stderr, "Illegal IO memory access: IoMem_wput($%x)\n", addr);
395: return;
396: }
397:
398: IoAccessBaseAddress = addr; /* Store for exception frame, just in case */
399: nIoMemAccessSize = SIZE_WORD;
400: nBusErrorAccesses = 0;
401: addr = IoMem_CheckMirrorAddresses(addr);
402:
403: IoMem_WriteWord(addr, val);
404: idx = addr - 0xff8000;
405:
406: IoAccessCurrentAddress = addr;
407: pInterceptWriteTable[idx](); /* Call 1st handler */
408:
409: if (pInterceptWriteTable[idx+1] != pInterceptWriteTable[idx])
410: {
411: IoAccessCurrentAddress = addr + 1;
412: pInterceptWriteTable[idx+1](); /* Call 2nd handler */
413: }
414:
415: /* Check if we wrote to a bus-error region */
416: if (nBusErrorAccesses == 2)
417: {
418: M68000_BusError(addr, 0);
419: }
420: }
421:
422:
423: /*-----------------------------------------------------------------------*/
1.1.1.3 ! root 424: /**
! 425: * Handle long-word write access to IO memory.
! 426: */
1.1 root 427: void IoMem_lput(uaecptr addr, uae_u32 val)
428: {
429: Uint32 idx;
430:
431: Dprintf(("IoMem_lput($%x, $%x)\n", addr, val));
432:
433: addr &= 0x00ffffff; /* Use a 24 bit address */
434:
435: if (addr < 0xff8000)
436: {
437: /* invalid memory addressing --> bus error */
438: M68000_BusError(addr, 0);
439: return;
440: }
441: if (addr > 0xfffffc)
442: {
443: fprintf(stderr, "Illegal IO memory access: IoMem_lput($%x)\n", addr);
444: return;
445: }
446:
447: IoAccessBaseAddress = addr; /* Store for exception frame, just in case */
448: nIoMemAccessSize = SIZE_LONG;
449: nBusErrorAccesses = 0;
450: addr = IoMem_CheckMirrorAddresses(addr);
451:
452: IoMem_WriteLong(addr, val);
453: idx = addr - 0xff8000;
454:
455: IoAccessCurrentAddress = addr;
456: pInterceptWriteTable[idx](); /* Call handler */
457:
458: if (pInterceptWriteTable[idx+1] != pInterceptWriteTable[idx])
459: {
460: IoAccessCurrentAddress = addr + 1;
461: pInterceptWriteTable[idx+1](); /* Call 2nd handler */
462: }
463:
464: if (pInterceptWriteTable[idx+2] != pInterceptWriteTable[idx+1])
465: {
466: IoAccessCurrentAddress = addr + 2;
467: pInterceptWriteTable[idx+2](); /* Call 3rd handler */
468: }
469:
470: if (pInterceptWriteTable[idx+3] != pInterceptWriteTable[idx+2])
471: {
472: IoAccessCurrentAddress = addr + 3;
473: pInterceptWriteTable[idx+3](); /* Call 4th handler */
474: }
475:
476: /* Check if we wrote to a bus-error region */
477: if (nBusErrorAccesses == 4)
478: {
479: M68000_BusError(addr, 0);
480: }
481: }
482:
483:
484: /*-------------------------------------------------------------------------*/
1.1.1.3 ! root 485: /**
! 486: * This handler will be called if a ST program tries to read from an address
! 487: * that causes a bus error on a real ST. However, we can't call M68000_BusError()
! 488: * directly: For example, a "move.b $ff8204,d0" triggers a bus error on a real ST,
! 489: * while a "move.w $ff8204,d0" works! So we have to count the accesses to bus error
! 490: * addresses and we only trigger a bus error later if the count matches the complete
! 491: * access size (e.g. nBusErrorAccesses==4 for a long word access).
! 492: */
1.1 root 493: void IoMem_BusErrorEvenReadAccess(void)
494: {
495: nBusErrorAccesses += 1;
496: IoMem[IoAccessCurrentAddress] = 0xff;
497: }
498:
1.1.1.3 ! root 499: /**
! 500: * We need two handler so that the IoMem_*get functions can distinguish
! 501: * consecutive addresses.
! 502: */
1.1 root 503: void IoMem_BusErrorOddReadAccess(void)
504: {
505: nBusErrorAccesses += 1;
506: IoMem[IoAccessCurrentAddress] = 0xff;
507: }
508:
509: /*-------------------------------------------------------------------------*/
1.1.1.3 ! root 510: /**
! 511: * Same as IoMem_BusErrorReadAccess() but for write access this time.
! 512: */
1.1 root 513: void IoMem_BusErrorEvenWriteAccess(void)
514: {
515: nBusErrorAccesses += 1;
516: }
517:
1.1.1.3 ! root 518: /**
! 519: * We need two handler so that the IoMem_*put functions can distinguish
! 520: * consecutive addresses.
! 521: */
1.1 root 522: void IoMem_BusErrorOddWriteAccess(void)
523: {
524: nBusErrorAccesses += 1;
525: }
526:
527:
528: /*-------------------------------------------------------------------------*/
1.1.1.3 ! root 529: /**
! 530: * This is the read handler for the IO memory locations without an assigned
! 531: * IO register and which also do not generate a bus error. Reading from such
! 532: * a register will return the result 0xff.
! 533: */
1.1 root 534: void IoMem_VoidRead(void)
535: {
536: Uint32 a;
537:
538: /* handler is probably called only once, so we have to take care of the neighbour "void IO registers" */
539: for (a = IoAccessBaseAddress; a < IoAccessBaseAddress + nIoMemAccessSize; a++)
540: {
541: if (pInterceptReadTable[a - 0xff8000] == IoMem_VoidRead)
542: {
543: IoMem[a] = 0xff;
544: }
545: }
546: }
547:
548: /*-------------------------------------------------------------------------*/
1.1.1.3 ! root 549: /**
! 550: * This is the write handler for the IO memory locations without an assigned
! 551: * IO register and which also do not generate a bus error. We simply ignore
! 552: * a write access to these registers.
! 553: */
1.1 root 554: void IoMem_VoidWrite(void)
555: {
556: /* Nothing... */
557: }
558:
559:
560: /*-------------------------------------------------------------------------*/
1.1.1.3 ! root 561: /**
! 562: * A dummy function that does nothing at all - for memory regions that don't
! 563: * need a special handler for read access.
! 564: */
1.1 root 565: void IoMem_ReadWithoutInterception(void)
566: {
567: /* Nothing... */
568: }
569:
570: /*-------------------------------------------------------------------------*/
1.1.1.3 ! root 571: /**
! 572: * A dummy function that does nothing at all - for memory regions that don't
! 573: * need a special handler for write access.
! 574: */
1.1 root 575: void IoMem_WriteWithoutInterception(void)
576: {
577: /* Nothing... */
578: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.