1 /*
2   Hatari - ioMem.c
3 
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.
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 const char IoMem_fileid[] = "Hatari ioMem.c : " __DATE__ " " __TIME__;
32 
33 #include "main.h"
34 #include "configuration.h"
35 #include "ioMem.h"
36 #include "ioMemTables.h"
37 #include "memorySnapShot.h"
38 #include "m68000.h"
39 #include "sysdeps.h"
40 #include "newcpu.h"
41 #include "log.h"
42 #include "scc.h"
43 
44 
45 static void (*pInterceptReadTable[0x8000])(void);	/* Table with read access handlers */
46 static void (*pInterceptWriteTable[0x8000])(void);	/* Table with write access handlers */
47 
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 */
54 
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 
81 /* Falcon bus mode (Falcon STe compatible bus or Falcon only bus) */
82 static enum FALCON_BUS_MODE falconBusMode = FALCON_ONLY_BUS;
83 
84 /*-----------------------------------------------------------------------*/
85 /**
86  * Save/Restore snapshot of local variables ('MemorySnapShot_Store' handles type)
87  */
IoMem_MemorySnapShot_Capture(bool bSave)88 void IoMem_MemorySnapShot_Capture(bool bSave)
89 {
90 	enum FALCON_BUS_MODE mode = falconBusMode;
91 
92 	/* Save/Restore details */
93 	MemorySnapShot_Store(&mode, sizeof(mode));
94 	if (!bSave)
95 		IoMem_SetFalconBusMode(mode);
96 }
97 
98 /*-----------------------------------------------------------------------*/
99 /**
100  * Fill a region with bus error handlers.
101  */
IoMem_SetBusErrorRegion(Uint32 startaddr,Uint32 endaddr)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 
122 /**
123  * Fill a region with void handlers.
124  */
IoMem_SetVoidRegion(Uint32 startaddr,Uint32 endaddr)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 /**
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.
141  */
IoMem_FixVoidAccessForST(void)142 static void IoMem_FixVoidAccessForST(void)
143 {
144 	IoMem_SetVoidRegion(0xff820f, 0xff820f);
145 	IoMem_SetVoidRegion(0xff860f, 0xff860f);
146 }
147 
148 /**
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.
157  */
IoMem_FixVoidAccessForMegaST(void)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  */
IoMem_FixAccessForMegaSTE(void)188 static void IoMem_FixAccessForMegaSTE(void)
189 {
190 	int addr;
191 
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;
197 	pInterceptReadTable[0xff8e21 - 0xff8000] = IoMem_ReadWithoutInterception;
198 	pInterceptWriteTable[0xff8e21 - 0xff8000] = IoMemTabMegaSTE_CacheCpuCtrl_WriteByte;
199 	pInterceptReadTable[0xff8e22 - 0xff8000] = IoMem_VoidRead;
200 	pInterceptWriteTable[0xff8e22 - 0xff8000] = IoMem_VoidWrite;
201 	pInterceptReadTable[0xff8e23 - 0xff8000] = IoMem_VoidRead;
202 	pInterceptWriteTable[0xff8e23 - 0xff8000] = IoMem_VoidWrite;
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 	}
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 	}
217 }
218 
219 
220 /**
221  * Fix up table for Falcon in STE compatible bus mode (i.e. less bus errors)
222  */
IoMem_FixVoidAccessForCompatibleFalcon(void)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 
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  */
IoMem_Init(void)258 void IoMem_Init(void)
259 {
260 	Uint32 addr;
261 	int i;
262 	const INTERCEPT_ACCESS_FUNC *pInterceptAccessFuncs = NULL;
263 
264 	/* Set default IO access handler (-> bus error) */
265 	IoMem_SetBusErrorRegion(0xff8000, 0xffffff);
266 
267 	switch (ConfigureParams.System.nMachineType)
268 	{
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 */
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)
307 					Log_Printf(LOG_WARN, "IoMem_Init: $%x (R) already defined\n", addr);
308 				if (pInterceptWriteTable[addr-0xff8000] != IoMem_BusErrorEvenWriteAccess && pInterceptWriteTable[addr-0xff8000] != IoMem_BusErrorOddWriteAccess)
309 					Log_Printf(LOG_WARN, "IoMem_Init: $%x (W) already defined\n", addr);
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 
318 	/* Set registers for Falcon DSP emulation */
319 	if (Config_IsMachineFalcon())
320 	{
321 		switch (ConfigureParams.System.nDSPType)
322 		{
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 
340 	/* Disable blitter? */
341 	if (!ConfigureParams.System.bBlitter && ConfigureParams.System.nMachineType == MACHINE_ST)
342 	{
343 		IoMem_SetBusErrorRegion(0xff8a00, 0xff8a3f);
344 	}
345 
346 	/* Disable real time clock on non-Mega machines */
347 	if (ConfigureParams.System.nMachineType == MACHINE_ST
348 	    || ConfigureParams.System.nMachineType == MACHINE_STE)
349 	{
350 		for (addr = 0xfffc21; addr <= 0xfffc3f; addr++)
351 		{
352 			pInterceptReadTable[addr - 0xff8000] = IoMem_VoidRead;     /* For 'read' */
353 			pInterceptWriteTable[addr - 0xff8000] = IoMem_VoidWrite;   /* and 'write' */
354 		}
355 	}
356 
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 	 */
361 	if (!Config_IsMachineFalcon())
362 	{
363 		/* Initialize PSG shadow registers for ST, STe, TT machines */
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 		}
369 	}
370 }
371 
372 
373 /**
374  * Uninitialize the IoMem code (currently unused).
375  */
IoMem_UnInit(void)376 void IoMem_UnInit(void)
377 {
378 }
379 
380 
381 /**
382  * This function is called to fix falconBusMode. This value comes from register
383  * $ff8007.b (Bit 5) and is called from ioMemTabFalcon.c.
384  */
IoMem_SetFalconBusMode(enum FALCON_BUS_MODE mode)385 void IoMem_SetFalconBusMode(enum FALCON_BUS_MODE mode)
386 {
387 	if (mode != falconBusMode)
388 	{
389 		falconBusMode = mode;
390 		IoMem_UnInit();
391 		IoMem_Init();
392 	}
393 }
394 
IoMem_IsFalconBusMode(void)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  */
IoMem_Reset(void)404 void IoMem_Reset(void)
405 {
406 	if (Config_IsMachineFalcon())
407 	{
408 		IoMem_SetFalconBusMode(FALCON_ONLY_BUS);
409 	}
410 }
411 
412 /*-----------------------------------------------------------------------*/
413 /**
414  * Handle byte read access from IO memory.
415  */
IoMem_bget(uaecptr addr)416 uae_u32 REGPARAM3 IoMem_bget(uaecptr addr)
417 {
418 	Uint8 val;
419 
420 	IoAccessFullAddress = addr;			/* Store initial 32 bits address (eg for bus error stack) */
421 
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 
434 	addr &= 0x00ffffff;                           /* Use a 24 bit address */
435 
436 	if (addr < 0xff8000 || !regs.s)
437 	{
438 		/* invalid memory addressing --> bus error */
439 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
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 	{
453 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
454 		return -1;
455 	}
456 
457 	val = IoMem[addr];
458 
459 	LOG_TRACE(TRACE_IOMEM_RD, "IO read.b $%08x = $%02x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
460 
461 	return val;
462 }
463 
464 
465 /*-----------------------------------------------------------------------*/
466 /**
467  * Handle word read access from IO memory.
468  */
IoMem_wget(uaecptr addr)469 uae_u32 REGPARAM3 IoMem_wget(uaecptr addr)
470 {
471 	Uint32 idx;
472 	Uint16 val;
473 
474 	IoAccessFullAddress = addr;			/* Store initial 32 bits address (eg for bus error stack) */
475 
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 
489 	addr &= 0x00ffffff;                           /* Use a 24 bit address */
490 
491 	if (addr < 0xff8000 || !regs.s)
492 	{
493 		/* invalid memory addressing --> bus error */
494 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
495 		return -1;
496 	}
497 	if (addr > 0xfffffe)
498 	{
499 		Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_wget($%x)\n", addr);
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 	{
520 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
521 		return -1;
522 	}
523 
524 	val = IoMem_ReadWord(addr);
525 
526 	LOG_TRACE(TRACE_IOMEM_RD, "IO read.w $%08x = $%04x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
527 
528 	return val;
529 }
530 
531 
532 /*-----------------------------------------------------------------------*/
533 /**
534  * Handle long-word read access from IO memory.
535  */
IoMem_lget(uaecptr addr)536 uae_u32 REGPARAM3 IoMem_lget(uaecptr addr)
537 {
538 	Uint32 idx;
539 	Uint32 val;
540 	int n;
541 
542 	IoAccessFullAddress = addr;			/* Store initial 32 bits address (eg for bus error stack) */
543 
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 
556 	addr &= 0x00ffffff;                           /* Use a 24 bit address */
557 
558 	if (addr < 0xff8000 || !regs.s)
559 	{
560 		/* invalid memory addressing --> bus error */
561 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
562 		return -1;
563 	}
564 	if (addr > 0xfffffc)
565 	{
566 		Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_lget($%x)\n", addr);
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 
578 	for (n = 1; n < nIoMemAccessSize; n++)
579 	{
580 		if (pInterceptReadTable[idx+n] != pInterceptReadTable[idx+n-1])
581 		{
582 			IoAccessCurrentAddress = addr + n;
583 			pInterceptReadTable[idx+n]();     /* Call n-th handler */
584 		}
585 	}
586 
587 	/* Check if we completely read from a bus-error region */
588 	if (nBusErrorAccesses == 4)
589 	{
590 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
591 		return -1;
592 	}
593 
594 	val = IoMem_ReadLong(addr);
595 
596 	LOG_TRACE(TRACE_IOMEM_RD, "IO read.l $%08x = $%08x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
597 
598 	return val;
599 }
600 
601 
602 /*-----------------------------------------------------------------------*/
603 /**
604  * Handle byte write access to IO memory.
605  */
IoMem_bput(uaecptr addr,uae_u32 val)606 void REGPARAM3 IoMem_bput(uaecptr addr, uae_u32 val)
607 {
608 	IoAccessFullAddress = addr;			/* Store initial 32 bits address (eg for bus error stack) */
609 
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 
622 	addr &= 0x00ffffff;                           /* Use a 24 bit address */
623 
624 	LOG_TRACE(TRACE_IOMEM_WR, "IO write.b $%08x = $%02x pc=%x\n", IoAccessFullAddress, val&0xff, M68000_GetPC());
625 
626 	if (addr < 0xff8000 || !regs.s)
627 	{
628 		/* invalid memory addressing --> bus error */
629 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
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 	{
645 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
646 	}
647 }
648 
649 
650 /*-----------------------------------------------------------------------*/
651 /**
652  * Handle word write access to IO memory.
653  */
IoMem_wput(uaecptr addr,uae_u32 val)654 void REGPARAM3 IoMem_wput(uaecptr addr, uae_u32 val)
655 {
656 	Uint32 idx;
657 
658 	IoAccessFullAddress = addr;			/* Store initial 32 bits address (eg for bus error stack) */
659 
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 
673 	addr &= 0x00ffffff;                           /* Use a 24 bit address */
674 
675 	LOG_TRACE(TRACE_IOMEM_WR, "IO write.w $%08x = $%04x pc=%x\n", IoAccessFullAddress, val&0xffff, M68000_GetPC());
676 
677 	if (addr < 0x00ff8000 || !regs.s)
678 	{
679 		/* invalid memory addressing --> bus error */
680 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
681 		return;
682 	}
683 	if (addr > 0xfffffe)
684 	{
685 		Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_wput($%x)\n", addr);
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 	{
708 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_WORD, BUS_ERROR_ACCESS_DATA);
709 	}
710 }
711 
712 
713 /*-----------------------------------------------------------------------*/
714 /**
715  * Handle long-word write access to IO memory.
716  */
IoMem_lput(uaecptr addr,uae_u32 val)717 void REGPARAM3 IoMem_lput(uaecptr addr, uae_u32 val)
718 {
719 	Uint32 idx;
720 	int n;
721 
722 	IoAccessFullAddress = addr;			/* Store initial 32 bits address (eg for bus error stack) */
723 
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 
736 	addr &= 0x00ffffff;                           /* Use a 24 bit address */
737 
738 	LOG_TRACE(TRACE_IOMEM_WR, "IO write.l $%08x = $%08x pc=%x\n", IoAccessFullAddress, val, M68000_GetPC());
739 
740 	if (addr < 0xff8000 || !regs.s)
741 	{
742 		/* invalid memory addressing --> bus error */
743 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
744 		return;
745 	}
746 	if (addr > 0xfffffc)
747 	{
748 		Log_Printf(LOG_WARN, "Illegal IO memory access: IoMem_lput($%x)\n", addr);
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;
760 	pInterceptWriteTable[idx]();                  /* Call first handler */
761 
762 	for (n = 1; n < nIoMemAccessSize; n++)
763 	{
764 		if (pInterceptWriteTable[idx+n] != pInterceptWriteTable[idx+n-1])
765 		{
766 			IoAccessCurrentAddress = addr + n;
767 			pInterceptWriteTable[idx+n]();   /* Call n-th handler */
768 		}
769 	}
770 
771 	/* Check if we wrote to a bus-error region */
772 	if (nBusErrorAccesses == 4)
773 	{
774 		M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_LONG, BUS_ERROR_ACCESS_DATA);
775 	}
776 }
777 
778 
779 /*-------------------------------------------------------------------------*/
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  */
IoMem_BusErrorEvenReadAccess(void)788 void IoMem_BusErrorEvenReadAccess(void)
789 {
790 	nBusErrorAccesses += 1;
791 	IoMem[IoAccessCurrentAddress] = 0xff;
792 }
793 
794 /**
795  * We need two handler so that the IoMem_*get functions can distinguish
796  * consecutive addresses.
797  */
IoMem_BusErrorOddReadAccess(void)798 void IoMem_BusErrorOddReadAccess(void)
799 {
800 	nBusErrorAccesses += 1;
801 	IoMem[IoAccessCurrentAddress] = 0xff;
802 }
803 
804 /*-------------------------------------------------------------------------*/
805 /**
806  * Same as IoMem_BusErrorReadAccess() but for write access this time.
807  */
IoMem_BusErrorEvenWriteAccess(void)808 void IoMem_BusErrorEvenWriteAccess(void)
809 {
810 	nBusErrorAccesses += 1;
811 }
812 
813 /**
814  * We need two handler so that the IoMem_*put functions can distinguish
815  * consecutive addresses.
816  */
IoMem_BusErrorOddWriteAccess(void)817 void IoMem_BusErrorOddWriteAccess(void)
818 {
819 	nBusErrorAccesses += 1;
820 }
821 
822 
823 /*-------------------------------------------------------------------------*/
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  */
IoMem_VoidRead(void)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 /*-------------------------------------------------------------------------*/
844 /**
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  */
IoMem_VoidRead_00(void)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 /**
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  */
IoMem_VoidWrite(void)869 void IoMem_VoidWrite(void)
870 {
871 	/* Nothing... */
872 }
873 
874 
875 /*-------------------------------------------------------------------------*/
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  */
IoMem_ReadWithoutInterception(void)880 void IoMem_ReadWithoutInterception(void)
881 {
882 	/* Nothing... */
883 }
884 
885 /*-------------------------------------------------------------------------*/
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  */
IoMem_WriteWithoutInterception(void)890 void IoMem_WriteWithoutInterception(void)
891 {
892 	/* Nothing... */
893 }
894