Annotation of hatari/src/ioMem.c, revision 1.1.1.15

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.