1 /**
2 * @ingroup lib_emu68
3 * @file emu68/mem68.h
4 * @brief 68k memory and IO manager header.
5 * @author Benjamin Gerard
6 * @date 1999/03/13
7 */
8
9 /* Copyright (c) 1998-2015 Benjamin Gerard */
10
11 #ifndef EMU68_MEM68_H
12 #define EMU68_MEM68_H
13
14 #include "emu68_api.h"
15 #include "struct68.h"
16
17 /**
18 * @defgroup lib_emu68_mem 68k memory and IO manager
19 * @ingroup lib_emu68
20 * @brief Handling memory and I/O access.
21 *
22 * EMU68 memory manager assumes that all addresses in the lowest
23 * half part of address space are memory access. A simple bit test
24 * over most signifiant bit (23) of address allow to choose beetween
25 * memory or eventual IO access. In case of memory access, address
26 * is masked to fit available 68K onboard memory. Overflow does NOT
27 * generate address error. IO access are performed towards quick
28 * access tables. There are 6 acccess tables: for each read and
29 * write access in 3 sizes (byte, word and long). Each of this 6
30 * tables has 256 entries filled with a pointer to suitable
31 * function. At init time, the entries of all tables are initialized
32 * to access 68K onboard memory. When an IO is plugged by user, it
33 * is mapped somewhere in 68K address space. EMU68 memory manager
34 * get bit 8 to 15 of address to make an index to be used in the
35 * suitable table (R/W for B/W/L).
36 *
37 * Featuring :
38 * - Onboard memory byte, word and long read/write access.
39 * - Optimized IO warm mapping/unmapping.
40 * - Optionnal (compile time) enhanced memory control with RWX access
41 * tag and hardware breakpoints.
42 *
43 * Limitations :
44 * - For optimization purposes IO must be mapped in high half memory
45 * (bit 23 of address setted).
46 * - Two IO can not shared the same memory location for bit 8 to 15.
47 * Conflicts could be resolved by creating an intermediate IO which
48 * dispatches to others. This mechanism has not been implemented yet,
49 * so users must do it themself if needed.
50 *
51 * Atari-ST & Amiga IO areas :
52 * - @p FF8800-FF88FF : YM2149 (ST)
53 * - @p FF8900-FF89FF : STE DMA and LMC1992
54 * - @p FF8200-FF82FF : Shifter (ST)
55 * - @p FFFA00-FFFAFF : MFP (ST)
56 * - @p DFF000-DFF0DF : Paula (AMIGA)
57 *
58 * @{
59 */
60
61 /**
62 * Memory access flags for emu68_t::chk (debug mode only).
63 */
64 enum {
65 EMU68_R = 0x01, /**< Memory location has been read. */
66 EMU68_W = 0x02, /**< Memory location has been written. */
67 EMU68_X = 0x04, /**< Memory location has been executed. */
68 EMU68_A = EMU68_R|EMU68_W|EMU68_X, /**< All memory access bits. */
69 };
70
71 /**
72 * @name Memory/IO quick access tables.
73 * @{
74 */
75
76
77 /**
78 * @}
79 */
80
81 /**
82 * @name Effective address calculation tables.
83 *
84 * The get_ab[bwl] tables are used by EMU68 to calculate operand
85 * effective address. Each of them is indexed by operand addressing
86 * mode. Each entry is a pointer to a function which do everything
87 * neccessary to update processor state (e.g. address register
88 * increment or decrement). reg parameter is register number coded
89 * in the three first bit (0 to 2) of 68k opcode. When the mode is
90 * 7, register parameter is used as an index in a second level
91 * function table for extended addressing mode.
92 *
93 * @{
94 */
95
96 EMU68_EXTERN
97 /**
98 * Byte operand effective address calculation function table.
99 */
100 addr68_t (*const get_eab68[8])(emu68_t * const,int reg);
101
102 EMU68_EXTERN
103 /**
104 * Word operand effective address calculation function table.
105 */
106 addr68_t (*const get_eaw68[8])(emu68_t * const,int reg);
107
108 EMU68_EXTERN
109 /**
110 * Long operand effective address calculation function table.
111 */
112 addr68_t (*const get_eal68[8])(emu68_t * const,int reg);
113
114
115 #define get_EAB(MODE,REG) get_eab68[MODE](emu68,REG)
116 #define get_EAW(MODE,REG) get_eaw68[MODE](emu68,REG)
117 #define get_EAL(MODE,REG) get_eal68[MODE](emu68,REG)
118
119
120 /**
121 * @name 68K onboard memory access.
122 * @{
123 */
124
125 EMU68_EXTERN
126 /**
127 * Read memory byte.
128 */
129 void mem68_read_b(emu68_t * const emu68);
130
131 EMU68_EXTERN
132 /**
133 * Read memory word.
134 */
135 void mem68_read_w(emu68_t * const emu68);
136
137 EMU68_EXTERN
138 /**
139 * Read memory long.
140 */
141 void mem68_read_l(emu68_t * const emu68);
142
143 EMU68_EXTERN
144 /**
145 * Write memory byte.
146 */
147 void mem68_write_b(emu68_t * const emu68);
148
149 EMU68_EXTERN
150 /**
151 * Write memory word.
152 */
153 void mem68_write_w(emu68_t * const emu68);
154
155 EMU68_EXTERN
156 /**
157 * Write memory long.
158 */
159 void mem68_write_l(emu68_t * const emu68);
160
_read_B(emu68_t * const emu68,const addr68_t addr)161 static inline uint68_t _read_B(emu68_t * const emu68,
162 const addr68_t addr)
163 {
164 emu68->bus_addr = addr;
165 mem68_read_b(emu68);
166 return (u8) emu68->bus_data;
167 }
168
_read_EAB(emu68_t * const emu68,const int mode,const int reg)169 static inline uint68_t _read_EAB(emu68_t * const emu68,
170 const int mode, const int reg)
171 {
172 emu68->bus_addr = get_eab68[mode](emu68,reg);
173 mem68_read_b(emu68);
174 return (u8) emu68->bus_data;
175 }
176
177
_read_W(emu68_t * const emu68,const addr68_t addr)178 static inline uint68_t _read_W(emu68_t * const emu68,
179 const addr68_t addr)
180 {
181 emu68->bus_addr = addr;
182 mem68_read_w(emu68);
183 return (u16) emu68->bus_data;
184 }
185
_read_EAW(emu68_t * const emu68,const int mode,const int reg)186 static inline uint68_t _read_EAW(emu68_t * const emu68,
187 const int mode, const int reg)
188 {
189 emu68->bus_addr = get_eaw68[mode](emu68,reg);
190 mem68_read_w(emu68);
191 return (u16) emu68->bus_data;
192 }
193
194
_read_L(emu68_t * const emu68,const addr68_t addr)195 static inline uint68_t _read_L(emu68_t * const emu68,
196 const addr68_t addr)
197 {
198 emu68->bus_addr = addr;
199 mem68_read_l(emu68);
200 return (u32) emu68->bus_data;
201 }
202
_read_EAL(emu68_t * const emu68,const int mode,const int reg)203 static inline uint68_t _read_EAL(emu68_t * const emu68,
204 const int mode, const int reg)
205 {
206 emu68->bus_addr = get_eal68[mode](emu68,reg);
207 mem68_read_l(emu68);
208 return (u32) emu68->bus_data;
209 }
210
211 /**
212 * Read memory byte.
213 */
214 #define read_B(ADDR) _read_B(emu68,(ADDR))
215
216 /**
217 * Read memory word.
218 */
219 #define read_W(ADDR) _read_W(emu68,(ADDR))
220
221 /**
222 * Read memory long.
223 */
224 #define read_L(ADDR) _read_L(emu68,(ADDR))
225
226 /**
227 * Read memory byte from AE mode.
228 */
229 #define read_EAB(MODE,PARM) _read_EAB(emu68,(MODE),(PARM))
230
231 /**
232 * Read memory word from AE mode.
233 */
234 #define read_EAW(MODE,PARM) _read_EAW(emu68,(MODE),(PARM))
235
236 /**
237 * Read memory long from AE mode.
238 */
239 #define read_EAL(MODE,PARM) _read_EAL(emu68,(MODE),(PARM))
240
_write_B(emu68_t * const emu68,const addr68_t addr,const int68_t v)241 static inline void _write_B(emu68_t * const emu68,
242 const addr68_t addr, const int68_t v)
243 {
244 emu68->bus_addr = addr;
245 emu68->bus_data = v;
246 mem68_write_b(emu68);
247 }
248
_write_EAB(emu68_t * const emu68,const int mode,const int reg,const int68_t v)249 static inline void _write_EAB(emu68_t * const emu68,
250 const int mode, const int reg, const int68_t v)
251 {
252 emu68->bus_addr = get_eab68[mode](emu68,reg);
253 emu68->bus_data = v;
254 mem68_write_b(emu68);
255 }
256
257
_write_W(emu68_t * const emu68,addr68_t addr,int68_t v)258 static inline void _write_W(emu68_t * const emu68, addr68_t addr, int68_t v)
259 {
260 emu68->bus_addr = addr;
261 emu68->bus_data = v;
262 mem68_write_w(emu68);
263 }
264
_write_EAW(emu68_t * const emu68,const int mode,const int reg,const int68_t v)265 static inline void _write_EAW(emu68_t * const emu68,
266 const int mode, const int reg, const int68_t v)
267 {
268 emu68->bus_addr = get_eaw68[mode](emu68,reg);
269 emu68->bus_data = v;
270 mem68_write_w(emu68);
271 }
272
273
_write_L(emu68_t * const emu68,addr68_t addr,int68_t v)274 static inline void _write_L(emu68_t * const emu68, addr68_t addr, int68_t v)
275 {
276 emu68->bus_addr = addr;
277 emu68->bus_data = v;
278 mem68_write_l(emu68);
279 }
280
_write_EAL(emu68_t * const emu68,const int mode,const int reg,const int68_t v)281 static inline void _write_EAL(emu68_t * const emu68,
282 const int mode, const int reg, const int68_t v)
283 {
284 emu68->bus_addr = get_eal68[mode](emu68,reg);
285 emu68->bus_data = v;
286 mem68_write_l(emu68);
287 }
288
289
290 /**
291 * Write memory byte.
292 */
293 #define write_B(ADDR,V) _write_B(emu68,(ADDR),(V))
294
295 /**
296 * Write memory word.
297 */
298 #define write_W(ADDR,V) _write_W(emu68,(ADDR),(V))
299
300 /**
301 * Write memory long.
302 */
303 #define write_L(ADDR,V) _write_L(emu68,(ADDR),(V))
304
305 /**
306 * Write memory byte at EA mode.
307 */
308 #define write_EAB(MODE,PARM,V) _write_EAB(emu68,(MODE),(PARM),(V))
309
310 /**
311 * Write memory word at EA mode.
312 */
313 #define write_EAW(MODE,PARM,V) _write_EAW(emu68,(MODE),(PARM),(V))
314
315 /**
316 * Write memory long at EA mode.
317 */
318 #define write_EAL(MODE,PARM,V) _write_EAL(emu68,(MODE),(PARM),(V))
319
320 /**
321 * @}
322 */
323
324
325 /**
326 * @name Instruction read.
327 * @{
328 */
329 EMU68_EXTERN
330 int68_t mem68_nextw(emu68_t * const emu68); /**< Decode word and update PC */
331 EMU68_EXTERN
332 int68_t mem68_nextl(emu68_t * const emu68); /**< Decode long and update PC */
333
334 /**
335 * mem68_nextw() convenience macro.
336 */
337 #define get_nextw() mem68_nextw(emu68)
338
339 /**
340 * mem68_nextl() convenience macro.
341 */
342 #define get_nextl() mem68_nextl(emu68)
343
344 /**
345 * @}
346 */
347
348
349 /**
350 * @name Stack access.
351 * @{
352 */
353
354 EMU68_EXTERN
355 /**
356 * Push long.
357 */
358 void mem68_pushl(emu68_t * const emu68, const int68_t v);
359
360 /**
361 * Push word.
362 */
363 EMU68_EXTERN
364 void mem68_pushw(emu68_t * const emu68, const int68_t v);
365
366 EMU68_EXTERN
367 /**
368 * Pop long.
369 */
370 int68_t mem68_popl(emu68_t * const emu68);
371
372 EMU68_EXTERN
373 /**
374 * Pop word.
375 */
376 int68_t mem68_popw(emu68_t * const emu68);
377
378 /**
379 * mem68_pushl() convenience macro.
380 */
381 #define pushl(V) mem68_pushl(emu68,(V));
382
383 /**
384 * mem68_pushw() convenience macro.
385 */
386 #define pushw(V) mem68_pushw(emu68,(V));
387
388 /**
389 * mem68_popl() convenience macro.
390 */
391 #define popl() mem68_popl(emu68);
392
393 /**
394 * mem68_popw() convenience macro.
395 */
396 #define popw() mem68_popw(emu68);
397
398 /**
399 * @}
400 */
401
402
403 /**
404 * Test for direct memory access or IO quick table access
405 */
mem68_is_io(const addr68_t addr)406 static inline int mem68_is_io(const addr68_t addr) {
407 return addr & 0x800000;
408 }
409
410 /**
411 * Set memory access check flags.
412 */
chkframe(emu68_t * const emu68,addr68_t addr,const int flags)413 static inline void chkframe(emu68_t * const emu68,
414 addr68_t addr, const int flags)
415 {
416 int chgchk, newchk;
417 /* assert( ! mem68_is_io(addr) ); */
418 addr &= MEMMSK68;
419 chgchk = emu68->chk[addr]; /* current value */
420 newchk = chgchk | flags; /* new value */
421 chgchk ^= newchk; /* what's changed ? */
422 if (chgchk) {
423 emu68->lst_chk.pc = emu68->inst_pc;
424 emu68->lst_chk.ad = addr;
425 emu68->lst_chk.fl = chgchk;
426 if (!emu68->frm_chk_fl)
427 emu68->fst_chk = emu68->lst_chk;
428 emu68->frm_chk_fl |= chgchk;
429 emu68->chk[addr] = newchk;
430 }
431 }
432
chk_buseven(emu68_t * const emu68)433 static inline void chk_buseven(emu68_t * const emu68)
434 {
435 if (emu68->bus_addr & 1)
436 ;
437 }
438
chkframe_b(emu68_t * const emu68,const int flags)439 static inline void chkframe_b(emu68_t * const emu68, const int flags)
440 {
441 chkframe(emu68, emu68->bus_addr, flags);
442 }
443
chkframe_w(emu68_t * const emu68,const int flags)444 static inline void chkframe_w(emu68_t * const emu68, const int flags)
445 {
446 chk_buseven(emu68);
447 chkframe(emu68, emu68->bus_addr+0, flags);
448 chkframe(emu68, emu68->bus_addr+1, flags);
449 }
450
chkframe_l(emu68_t * const emu68,const int flags)451 static inline void chkframe_l(emu68_t * const emu68, const int flags)
452 {
453 chk_buseven(emu68);
454 chkframe(emu68, emu68->bus_addr+0, flags);
455 chkframe(emu68, emu68->bus_addr+1, flags);
456 chkframe(emu68, emu68->bus_addr+2, flags);
457 chkframe(emu68, emu68->bus_addr+3, flags);
458 }
459
460
461 EMU68_EXTERN
462 /**
463 * Init memory quick access table.
464 *
465 * The emu68_mem_init() function must be call at init time.
466 * Currently this function only call the emu68_mem_reset()
467 * function.
468 *
469 * @see emu68_mem_reset()
470 */
471 void emu68_mem_init(emu68_t * const emu68);
472
473 EMU68_EXTERN
474 /**
475 * Destroy memory quick access table.
476 */
477 void emu68_mem_destroy(emu68_t * const emu68);
478
479 EMU68_EXTERN
480 /**
481 * Reset memory quick access table.
482 *
483 * The emu68_mem_reset() function restores all memory access to
484 * default. All mapped IO will be discard and replace by onboard
485 * memory access.
486 */
487 void emu68_mem_reset(emu68_t * const emu68);
488
489 EMU68_EXTERN
490 /**
491 * Add a new memory access control area (for new IO).
492 *
493 * @param emu68 emu68 instance
494 * @param area which area (bit 16 to 23 of address) to change
495 * @param read_bwl read function table (byte, word and long in this order)
496 * @param write_bwl idem read_bwl for write access.
497 *
498 * @see emu68_mem_reset_area()
499 */
500 void emu68_mem_new_area(emu68_t * const emu68, u8 area,
501 memfunc68_t *read_bwl,
502 memfunc68_t *write_bwl);
503
504 EMU68_EXTERN
505 /**
506 * Reset memory access control area to default state.
507 *
508 * @param emu68 emu68 instance
509 * @param area which area (bit 16 to 23 of address) to reset
510 *
511 * @see emu68_mem_new_area()
512 */
513 void emu68_mem_reset_area(emu68_t * const emu68, u8 area);
514
515 /**
516 * @}
517 */
518
519 #endif
520