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