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