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