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