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