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