Merge pull request #91 from davidgiven/dtrg-m68k
Add a Musashi-based 68020 emulator; use it to run the linux68k tests.
This commit is contained in:
commit
37f466a8f4
2
Makefile
2
Makefile
|
@ -24,7 +24,7 @@ BUILDDIR = $(ACK_TEMP_DIR)/ack-build
|
|||
|
||||
# What build flags do you want to use for native code?
|
||||
|
||||
CFLAGS = -g -O -Wno-return-type
|
||||
CFLAGS = -g -Og -Wno-return-type
|
||||
LDFLAGS =
|
||||
|
||||
# Various commands.
|
||||
|
|
|
@ -18,6 +18,7 @@ vars.plats = {
|
|||
"pdpv7",
|
||||
}
|
||||
vars.plats_with_tests = {
|
||||
"linux68k",
|
||||
"linux386",
|
||||
"linuxppc",
|
||||
--"qemuppc",
|
||||
|
|
58
plat/linux68k/emu/build.lua
Normal file
58
plat/linux68k/emu/build.lua
Normal file
|
@ -0,0 +1,58 @@
|
|||
cprogram {
|
||||
name = "m68kmake",
|
||||
srcs = {
|
||||
"./musashi/m68kmake.c"
|
||||
}
|
||||
}
|
||||
|
||||
normalrule {
|
||||
name = "m68k_engine",
|
||||
ins = {
|
||||
"+m68kmake",
|
||||
"./musashi/m68k_in.c",
|
||||
"./musashi/m68kcpu.h",
|
||||
"./m68kconf.h",
|
||||
"./musashi/m68kcpu.c",
|
||||
"./musashi/m68kdasm.c",
|
||||
"./musashi/m68k.h",
|
||||
},
|
||||
outleaves = {
|
||||
"m68kopac.c",
|
||||
"m68kopdm.c",
|
||||
"m68kopnz.c",
|
||||
"m68kops.c",
|
||||
"m68kops.h",
|
||||
"m68kcpu.h",
|
||||
"m68kconf.h",
|
||||
"m68kcpu.c",
|
||||
"m68kdasm.c",
|
||||
"m68k.h",
|
||||
},
|
||||
commands = {
|
||||
"cp %{ins[2]} %{ins[3]} %{ins[4]} %{ins[5]} %{ins[6]} %{ins[7]} %{dir}",
|
||||
"cd %{dir} && %{ins[1]}"
|
||||
}
|
||||
}
|
||||
|
||||
clibrary {
|
||||
name = "headers",
|
||||
srcs = {},
|
||||
hdrs = {
|
||||
matching(filenamesof("+m68k_engine"), "%.h$"),
|
||||
}
|
||||
}
|
||||
|
||||
cprogram {
|
||||
name = "emu68k",
|
||||
vars = {
|
||||
["+cflags"] = {"-DM68K_COMPILE_FOR_MAME=0"}
|
||||
},
|
||||
srcs = {
|
||||
"./sim.c",
|
||||
matching(filenamesof("+m68k_engine"), "%.c$"),
|
||||
},
|
||||
deps = {
|
||||
"+headers",
|
||||
}
|
||||
}
|
||||
|
193
plat/linux68k/emu/m68kconf.h
Executable file
193
plat/linux68k/emu/m68kconf.h
Executable file
|
@ -0,0 +1,193 @@
|
|||
/* ======================================================================== */
|
||||
/* ========================= LICENSING & COPYRIGHT ======================== */
|
||||
/* ======================================================================== */
|
||||
/*
|
||||
* MUSASHI
|
||||
* Version 3.4
|
||||
*
|
||||
* A portable Motorola M680x0 processor emulation engine.
|
||||
* Copyright 1998-2001 Karl Stenerud. All rights reserved.
|
||||
*
|
||||
* This code may be freely used for non-commercial purposes as long as this
|
||||
* copyright notice remains unaltered in the source code and any binary files
|
||||
* containing this code in compiled form.
|
||||
*
|
||||
* All other lisencing terms must be negotiated with the author
|
||||
* (Karl Stenerud).
|
||||
*
|
||||
* The latest version of this code can be obtained at:
|
||||
* http://kstenerud.cjb.net
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef M68KCONF__HEADER
|
||||
#define M68KCONF__HEADER
|
||||
|
||||
|
||||
/* Configuration switches.
|
||||
* Use OPT_SPECIFY_HANDLER for configuration options that allow callbacks.
|
||||
* OPT_SPECIFY_HANDLER causes the core to link directly to the function
|
||||
* or macro you specify, rather than using callback functions whose pointer
|
||||
* must be passed in using m68k_set_xxx_callback().
|
||||
*/
|
||||
#define OPT_OFF 0
|
||||
#define OPT_ON 1
|
||||
#define OPT_SPECIFY_HANDLER 2
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== MAME STUFF ============================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* If you're compiling this for MAME, only change M68K_COMPILE_FOR_MAME
|
||||
* to OPT_ON and use m68kmame.h to configure the 68k core.
|
||||
*/
|
||||
#ifndef M68K_COMPILE_FOR_MAME
|
||||
#define M68K_COMPILE_FOR_MAME OPT_OFF
|
||||
#endif /* M68K_COMPILE_FOR_MAME */
|
||||
|
||||
|
||||
#if M68K_COMPILE_FOR_MAME == OPT_OFF
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================= CONFIGURATION ============================ */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* Turn ON if you want to use the following M68K variants */
|
||||
#define M68K_EMULATE_010 OPT_ON
|
||||
#define M68K_EMULATE_EC020 OPT_ON
|
||||
#define M68K_EMULATE_020 OPT_ON
|
||||
|
||||
|
||||
/* If ON, the CPU will call m68k_read_immediate_xx() for immediate addressing
|
||||
* and m68k_read_pcrelative_xx() for PC-relative addressing.
|
||||
* If off, all read requests from the CPU will be redirected to m68k_read_xx()
|
||||
*/
|
||||
#define M68K_SEPARATE_READS OPT_OFF
|
||||
|
||||
/* If ON, the CPU will call m68k_write_32_pd() when it executes move.l with a
|
||||
* predecrement destination EA mode instead of m68k_write_32().
|
||||
* To simulate real 68k behavior, m68k_write_32_pd() must first write the high
|
||||
* word to [address+2], and then write the low word to [address].
|
||||
*/
|
||||
#define M68K_SIMULATE_PD_WRITES OPT_OFF
|
||||
|
||||
/* If ON, CPU will call the interrupt acknowledge callback when it services an
|
||||
* interrupt.
|
||||
* If off, all interrupts will be autovectored and all interrupt requests will
|
||||
* auto-clear when the interrupt is serviced.
|
||||
*/
|
||||
#define M68K_EMULATE_INT_ACK OPT_OFF
|
||||
#define M68K_INT_ACK_CALLBACK(A) cpu_irq_ack(A)
|
||||
|
||||
|
||||
/* If ON, CPU will call the breakpoint acknowledge callback when it encounters
|
||||
* a breakpoint instruction and it is running a 68010+.
|
||||
*/
|
||||
#define M68K_EMULATE_BKPT_ACK OPT_OFF
|
||||
#define M68K_BKPT_ACK_CALLBACK() your_bkpt_ack_handler_function()
|
||||
|
||||
|
||||
/* If ON, the CPU will monitor the trace flags and take trace exceptions
|
||||
*/
|
||||
#define M68K_EMULATE_TRACE OPT_OFF
|
||||
|
||||
|
||||
/* If ON, CPU will call the output reset callback when it encounters a reset
|
||||
* instruction.
|
||||
*/
|
||||
#define M68K_EMULATE_RESET OPT_OFF
|
||||
#define M68K_RESET_CALLBACK() cpu_pulse_reset()
|
||||
|
||||
|
||||
/* If ON, CPU will call the set fc callback on every memory access to
|
||||
* differentiate between user/supervisor, program/data access like a real
|
||||
* 68000 would. This should be enabled and the callback should be set if you
|
||||
* want to properly emulate the m68010 or higher. (moves uses function codes
|
||||
* to read/write data from different address spaces)
|
||||
*/
|
||||
#define M68K_EMULATE_FC OPT_OFF
|
||||
#define M68K_SET_FC_CALLBACK(A) cpu_set_fc(A)
|
||||
|
||||
|
||||
/* If ON, CPU will call the pc changed callback when it changes the PC by a
|
||||
* large value. This allows host programs to be nicer when it comes to
|
||||
* fetching immediate data and instructions on a banked memory system.
|
||||
*/
|
||||
#define M68K_MONITOR_PC OPT_OFF
|
||||
#define M68K_SET_PC_CALLBACK(A) your_pc_changed_handler_function(A)
|
||||
|
||||
|
||||
/* If ON, CPU will call the instruction hook callback before every
|
||||
* instruction.
|
||||
*/
|
||||
#define M68K_INSTRUCTION_HOOK OPT_SPECIFY_HANDLER
|
||||
#define M68K_INSTRUCTION_CALLBACK() cpu_instr_callback()
|
||||
|
||||
|
||||
/* If ON, the CPU will emulate the 4-byte prefetch queue of a real 68000 */
|
||||
#define M68K_EMULATE_PREFETCH OPT_ON
|
||||
|
||||
|
||||
/* If ON, the CPU will generate address error exceptions if it tries to
|
||||
* access a word or longword at an odd address.
|
||||
* NOTE: This is only emulated properly for 68000 mode.
|
||||
*/
|
||||
#define M68K_EMULATE_ADDRESS_ERROR OPT_ON
|
||||
|
||||
|
||||
/* Turn ON to enable logging of illegal instruction calls.
|
||||
* M68K_LOG_FILEHANDLE must be #defined to a stdio file stream.
|
||||
* Turn on M68K_LOG_1010_1111 to log all 1010 and 1111 calls.
|
||||
*/
|
||||
#define M68K_LOG_ENABLE OPT_OFF
|
||||
#define M68K_LOG_1010_1111 OPT_OFF
|
||||
#define M68K_LOG_FILEHANDLE stderr
|
||||
|
||||
|
||||
/* ----------------------------- COMPATIBILITY ---------------------------- */
|
||||
|
||||
/* The following options set optimizations that violate the current ANSI
|
||||
* standard, but will be compliant under the forthcoming C9X standard.
|
||||
*/
|
||||
|
||||
|
||||
/* If ON, the enulation core will use 64-bit integers to speed up some
|
||||
* operations.
|
||||
*/
|
||||
#define M68K_USE_64_BIT OPT_OFF
|
||||
|
||||
|
||||
/* Set to your compiler's static inline keyword to enable it, or
|
||||
* set it to blank to disable it.
|
||||
* If you define INLINE in the makefile, it will override this value.
|
||||
* NOTE: not enabling inline functions will SEVERELY slow down emulation.
|
||||
*/
|
||||
#ifndef INLINE
|
||||
//#define INLINE static __inline__
|
||||
#define INLINE static
|
||||
#endif /* INLINE */
|
||||
|
||||
#endif /* M68K_COMPILE_FOR_MAME */
|
||||
|
||||
#include "sim.h"
|
||||
|
||||
#define m68k_read_memory_8(A) cpu_read_byte(A)
|
||||
#define m68k_read_memory_16(A) cpu_read_word(A)
|
||||
#define m68k_read_memory_32(A) cpu_read_long(A)
|
||||
|
||||
#define m68k_read_disassembler_16(A) cpu_read_word_dasm(A)
|
||||
#define m68k_read_disassembler_32(A) cpu_read_long_dasm(A)
|
||||
|
||||
#define m68k_write_memory_8(A, V) cpu_write_byte(A, V)
|
||||
#define m68k_write_memory_16(A, V) cpu_write_word(A, V)
|
||||
#define m68k_write_memory_32(A, V) cpu_write_long(A, V)
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== END OF FILE ============================= */
|
||||
/* ======================================================================== */
|
||||
|
||||
#endif /* M68KCONF__HEADER */
|
3
plat/linux68k/emu/musashi/README.ACK.md
Normal file
3
plat/linux68k/emu/musashi/README.ACK.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
This is a copy of the Karl Stenerud's Musashi m68k emulator, available from
|
||||
https://github.com/kstenerud/Musashi. It's MIT licensed and so is compatible
|
||||
with the ACK.
|
290
plat/linux68k/emu/musashi/example/example.txt
Executable file
290
plat/linux68k/emu/musashi/example/example.txt
Executable file
|
@ -0,0 +1,290 @@
|
|||
EXAMPLE:
|
||||
-------
|
||||
As an example, I'll build an imaginary hardware platform.
|
||||
|
||||
|
||||
The system is fairly simple, comprising a 68000, an input device, an output
|
||||
device, a non-maskable-interrupt device, and an interrupt controller.
|
||||
|
||||
|
||||
The input device receives input from the user and asserts its interrupt
|
||||
request line until its value is read. Reading from the input device's
|
||||
memory-mapped port will both clear its interrupt request and read an ASCII
|
||||
representation (8 bits) of what the user entered.
|
||||
|
||||
The output device reads value when it is selected through its memory-mapped
|
||||
port and outputs it to a display. The value it reads will be interpreted as
|
||||
an ASCII value and output to the display. The output device is fairly slow
|
||||
(it can only process 1 byte per second), and so it asserts its interrupt
|
||||
request line when it is ready to receive a byte. Writing to the output device
|
||||
sends a byte to it. If the output device is not ready, the write is ignored.
|
||||
Reading from the output device returns 0 and clears its interrupt request line
|
||||
until another byte is written to it and 1 second elapses.
|
||||
|
||||
The non-maskable-interrupt (NMI) device, as can be surmised from the name,
|
||||
generates a non-maskable-interrupt. This is connected to some kind of external
|
||||
switch that the user can push to generate a NMI.
|
||||
|
||||
Since there are 3 devices interrupting the CPU, an interrupt controller is
|
||||
needed. The interrupt controller takes 7 inputs and encodes the highest
|
||||
priority asserted line on the 3 output pins. the input device is wired to IN2
|
||||
and the output device is wired to IN1 on the controller. The NMI device is
|
||||
wired to IN7 and all the other inputs are wired low.
|
||||
|
||||
The bus is also connected to a 1K ROM and a 256 byte RAM.
|
||||
Beware: This platform places ROM and RAM in the same address range and uses
|
||||
the FC pins to select the correct address space!
|
||||
(You didn't expect me to make it easy, did you? =)
|
||||
|
||||
|
||||
|
||||
Here is the schematic in all its ASCII splendour:
|
||||
-------------------------------------------------
|
||||
|
||||
NMI TIED
|
||||
SWITCH LOW
|
||||
| |
|
||||
| +-+-+-+
|
||||
| | | | | +------------------------------------------------+
|
||||
| | | | | | +------------------------------------+ |
|
||||
| | | | | | | | |
|
||||
+-------------+ | |
|
||||
|7 6 5 4 3 2 1| | |
|
||||
| | | |
|
||||
| INT CONTRLR | | |
|
||||
| | | |
|
||||
|i i i | | |
|
||||
|2 1 0 | | |
|
||||
+-------------+ | |
|
||||
| | | | |
|
||||
| | | +--------------------------------+--+ | |
|
||||
o o o | | | | |
|
||||
+--------------+ +-------+ +----------+ +---------+ +----------+
|
||||
| I I I a | | | | | | r a i | | i |
|
||||
| 2 1 0 23 | | | | | | e c | | |
|
||||
| | | | | | | a k | | |
|
||||
| | | | | | | d | | |
|
||||
| | | | | | | | | |
|
||||
| M68000 | | ROM | | RAM | | IN | | OUT |
|
||||
| | | | | | | | | |
|
||||
| a9|--|a9 |--| |--| |--| |
|
||||
| a8|--|a8 |--| |--| |--| |
|
||||
| a7|--|a7 |--|a7 |--| |--| |
|
||||
| a6|--|a6 |--|a6 |--| |--| |
|
||||
| a5|--|a5 |--|a5 |--| |--| |
|
||||
| a4|--|a4 |--|a4 |--| |--| |
|
||||
| a3|--|a3 |--|a3 |--| |--| |
|
||||
| a2|--|a2 |--|a2 |--| |--| |
|
||||
| a1|--|a1 |--|a1 |--| |--| |
|
||||
| a0|--|a0 |--|a0 |--| |--| |
|
||||
| | | | | | | | | |
|
||||
| d15|--|d15 |--|d15 |--| |--| |
|
||||
| d14|--|d14 |--|d14 |--| |--| |
|
||||
| d13|--|d13 |--|d13 |--| |--| |
|
||||
| d12|--|d12 |--|d12 |--| |--| |
|
||||
| d11|--|d11 |--|d11 |--| |--| |
|
||||
| d10|--|d10 |--|d10 |--| |--| |
|
||||
| d9|--|d9 |--|d9 |--| |--| |
|
||||
| d8|--|d8 |--|d8 |--| |--| |
|
||||
| d7|--|d7 |--|d7 |--|d7 |--|d7 |
|
||||
| d6|--|d6 |--|d6 |--|d6 |--|d6 |
|
||||
| d5|--|d5 |--|d5 |--|d5 |--|d5 |
|
||||
| d4|--|d4 |--|d4 |--|d4 |--|d4 |
|
||||
| d3|--|d3 |--|d3 |--|d3 |--|d3 |
|
||||
| d2|--|d2 |--|d2 |--|d2 |--|d2 |
|
||||
| d1|--|d1 |--|d1 |--|d1 |--|d1 w |
|
||||
| d0|--|d0 |--|d0 |--|d0 |--|d0 r |
|
||||
| | | | | | | | | i a |
|
||||
| a F F F | | | | | | | | t c |
|
||||
|22 rW 2 1 0 | | cs | | cs rW | | | | e k |
|
||||
+--------------+ +-------+ +----------+ +---------+ +----------+
|
||||
| | | | | | | | | |
|
||||
| | | | | | | | | |
|
||||
| | | | | +-------+ +-----+ | +---+ |
|
||||
| | | | | | IC1 | | IC2 | | |AND| |
|
||||
| | | | | |a b c d| |a b c| | +---+ |
|
||||
| | | | | +-------+ +-----+ | | | |
|
||||
| | | | | | | | | | | | | | +--+
|
||||
| | | | | | | | | | | | | | |
|
||||
| | | | | | | | | | | | | | |
|
||||
| | | | | | | | | | | | | | |
|
||||
| | | | +-----)-)-+-)----)-)-+ | | |
|
||||
| | | +-------)-+---)----)-+ | | |
|
||||
| | +---------+-----)----+ | | |
|
||||
| | | | | |
|
||||
| +------------------+-----------+----------------------+ |
|
||||
| |
|
||||
+-----------------------------------------------------------+
|
||||
|
||||
IC1: output=1 if a=0 and b=1 and c=0 and d=0
|
||||
IC2: output=1 if a=0 and b=0 and c=1
|
||||
|
||||
|
||||
|
||||
Here is the listing for program.bin:
|
||||
-----------------------------------
|
||||
|
||||
INPUT_ADDRESS equ $800000
|
||||
OUTPUT_ADDRESS equ $400000
|
||||
CIRCULAR_BUFFER equ $c0
|
||||
CAN_OUTPUT equ $d0
|
||||
STACK_AREA equ $100
|
||||
|
||||
vector_table:
|
||||
00000000 0000 0100 dc.l STACK_AREA * 0: SP
|
||||
00000004 0000 00c0 dc.l init * 1: PC
|
||||
00000008 0000 0148 dc.l unhandled_exception * 2: bus error
|
||||
0000000c 0000 0148 dc.l unhandled_exception * 3: address error
|
||||
00000010 0000 0148 dc.l unhandled_exception * 4: illegal instruction
|
||||
00000014 0000 0148 dc.l unhandled_exception * 5: zero divide
|
||||
00000018 0000 0148 dc.l unhandled_exception * 6: chk
|
||||
0000001c 0000 0148 dc.l unhandled_exception * 7: trapv
|
||||
00000020 0000 0148 dc.l unhandled_exception * 8: privilege violation
|
||||
00000024 0000 0148 dc.l unhandled_exception * 9: trace
|
||||
00000028 0000 0148 dc.l unhandled_exception * 10: 1010
|
||||
0000002c 0000 0148 dc.l unhandled_exception * 11: 1111
|
||||
00000030 0000 0148 dc.l unhandled_exception * 12: -
|
||||
00000034 0000 0148 dc.l unhandled_exception * 13: -
|
||||
00000038 0000 0148 dc.l unhandled_exception * 14: -
|
||||
0000003c 0000 0148 dc.l unhandled_exception * 15: uninitialized interrupt
|
||||
00000040 0000 0148 dc.l unhandled_exception * 16: -
|
||||
00000044 0000 0148 dc.l unhandled_exception * 17: -
|
||||
00000048 0000 0148 dc.l unhandled_exception * 18: -
|
||||
0000004c 0000 0148 dc.l unhandled_exception * 19: -
|
||||
00000050 0000 0148 dc.l unhandled_exception * 20: -
|
||||
00000054 0000 0148 dc.l unhandled_exception * 21: -
|
||||
00000058 0000 0148 dc.l unhandled_exception * 22: -
|
||||
0000005c 0000 0148 dc.l unhandled_exception * 23: -
|
||||
00000060 0000 0148 dc.l unhandled_exception * 24: spurious interrupt
|
||||
00000064 0000 0136 dc.l output_ready * 25: l1 irq
|
||||
00000068 0000 010e dc.l input_ready * 26: l2 irq
|
||||
0000006c 0000 0148 dc.l unhandled_exception * 27: l3 irq
|
||||
00000070 0000 0148 dc.l unhandled_exception * 28: l4 irq
|
||||
00000074 0000 0148 dc.l unhandled_exception * 29: l5 irq
|
||||
00000078 0000 0148 dc.l unhandled_exception * 30: l6 irq
|
||||
0000007c 0000 014e dc.l nmi * 31: l7 irq
|
||||
00000080 0000 0148 dc.l unhandled_exception * 32: trap 0
|
||||
00000084 0000 0148 dc.l unhandled_exception * 33: trap 1
|
||||
00000088 0000 0148 dc.l unhandled_exception * 34: trap 2
|
||||
0000008c 0000 0148 dc.l unhandled_exception * 35: trap 3
|
||||
00000090 0000 0148 dc.l unhandled_exception * 36: trap 4
|
||||
00000094 0000 0148 dc.l unhandled_exception * 37: trap 5
|
||||
00000098 0000 0148 dc.l unhandled_exception * 38: trap 6
|
||||
0000009c 0000 0148 dc.l unhandled_exception * 39: trap 7
|
||||
000000a0 0000 0148 dc.l unhandled_exception * 40: trap 8
|
||||
000000a4 0000 0148 dc.l unhandled_exception * 41: trap 9
|
||||
000000a8 0000 0148 dc.l unhandled_exception * 42: trap 10
|
||||
000000ac 0000 0148 dc.l unhandled_exception * 43: trap 11
|
||||
000000b0 0000 0148 dc.l unhandled_exception * 44: trap 12
|
||||
000000b4 0000 0148 dc.l unhandled_exception * 45: trap 13
|
||||
000000b8 0000 0148 dc.l unhandled_exception * 46: trap 14
|
||||
000000bc 0000 0148 dc.l unhandled_exception * 47: trap 15
|
||||
* This is the end of the useful part of the table.
|
||||
* We will now do the Capcom thing and put code starting at $c0.
|
||||
|
||||
init:
|
||||
* Copy the exception vector table to RAM.
|
||||
000000c0 227c 0000 0000 move.l #0, a1 * a1 is RAM index
|
||||
000000c6 303c 002f move.w #47, d0 * d0 is counter (48 vectors)
|
||||
000000ca 41fa 0006 lea.l (copy_table,PC), a0 * a0 is scratch
|
||||
000000ce 2208 move.l a0, d1 * d1 is ROM index
|
||||
000000d0 4481 neg.l d1
|
||||
copy_table:
|
||||
000000d2 22fb 18fe dc.l $22fb18fe * stoopid as68k generates 020 code here
|
||||
* move.l (copy_table,PC,d1.l), (a1)+
|
||||
000000d6 5841 addq #4, d1
|
||||
000000d8 51c8 fff8 dbf d0, copy_table
|
||||
|
||||
main_init:
|
||||
* Initialize main program
|
||||
000000dc 11fc 0000 00d0 move.b #0, CAN_OUTPUT
|
||||
000000e2 4df8 00c0 lea.l CIRCULAR_BUFFER, a6
|
||||
000000e6 7c00 moveq #0, d6 * output buffer ptr
|
||||
000000e8 7e00 moveq #0, d7 * input buffer ptr
|
||||
000000ea 027c f8ff andi #$f8ff, SR * clear interrupt mask
|
||||
main:
|
||||
* Main program
|
||||
000000ee 4a38 00d0 tst.b CAN_OUTPUT * can we output?
|
||||
000000f2 67fa beq main
|
||||
000000f4 be06 cmp.b d6, d7 * is there data?
|
||||
000000f6 67f6 beq main
|
||||
000000f8 11fc 0000 00d0 move.b #0, CAN_OUTPUT
|
||||
000000fe 13f6 6000 0040 move.b (0,a6,d6.w), OUTPUT_ADDRESS * write data
|
||||
0000
|
||||
00000106 5246 addq #1, d6
|
||||
00000108 0206 000f andi.b #15, d6 * update circular buffer
|
||||
0000010c 60e0 bra main
|
||||
|
||||
|
||||
input_ready:
|
||||
0000010e 2f00 move.l d0, -(a7)
|
||||
00000110 2f01 move.l d1, -(a7)
|
||||
00000112 1239 0080 0000 move.b INPUT_ADDRESS, d1 * read data
|
||||
00000118 1007 move.b d7, d0 * check if buffer full
|
||||
0000011a 5240 addq #1, d0
|
||||
0000011c 0200 000f andi.b #15, d0
|
||||
00000120 bc00 cmp.b d0, d6
|
||||
00000122 6700 000c beq input_ready_quit * throw away if full
|
||||
00000126 1d81 7000 move.b d1, (0,a6,d7.w) * store the data
|
||||
0000012a 5247 addq #1, d7
|
||||
0000012c 0207 000f andi.b #15, d7 * update circular buffer
|
||||
input_ready_quit:
|
||||
00000130 221f move.l (a7)+, d1
|
||||
00000132 201f move.l (a7)+, d0
|
||||
00000134 4e73 rte
|
||||
|
||||
output_ready:
|
||||
00000136 2f00 move.l d0, -(a7)
|
||||
00000138 11fc 0001 00d0 move.b #1, CAN_OUTPUT
|
||||
0000013e 1039 0040 0000 move.b OUTPUT_ADDRESS, d0 * acknowledge the interrupt
|
||||
00000144 201f move.l (a7)+, d0
|
||||
00000146 4e73 rte
|
||||
|
||||
unhandled_exception:
|
||||
00000148 4e72 2700 stop #$2700 * wait for NMI
|
||||
0000014c 60fa bra unhandled_exception * shouldn't get here
|
||||
|
||||
nmi:
|
||||
* perform a soft reset
|
||||
0000014e 46fc 2700 move #$2700, SR * set status register
|
||||
00000152 2e7a feac move.l (vector_table,PC), a7 * reset stack pointer
|
||||
00000156 4e70 reset * reset peripherals
|
||||
00000158 4efa feaa jmp (vector_table+4,PC) * reset program counter
|
||||
|
||||
END
|
||||
|
||||
|
||||
|
||||
Compiling the example host environment:
|
||||
--------------------------------------
|
||||
|
||||
I've only put in an os-dependant portion for dos/windows, so you'll either
|
||||
have to compile for that system or make your own osd code based on osd_dos.c
|
||||
and modify the makefile accordingly.
|
||||
|
||||
I compiled this example using the compiler from mingw (www.mingw.org) but you
|
||||
could also use djgpp (www.delorie.com).
|
||||
|
||||
- Copy the m68k files to a directory. Then extract the files from example.zip to
|
||||
the same directory, overwriting m68kconf.h. program.bin is the actual 68000
|
||||
program you will be running.
|
||||
- Make your own osd_get_key() in the same fashion as in osd_dos.c if you're not
|
||||
compiling for dos/windows.
|
||||
- Type make
|
||||
- Perform the necessary animal sacrifices.
|
||||
- Type sim program.bin
|
||||
|
||||
|
||||
Keys:
|
||||
ESC - quits the simulator
|
||||
~ - generates an NMI interrupt
|
||||
Any other key - Genearate input for the input device
|
||||
|
||||
|
||||
Note: I've cheated a bit in the emulation. There is no speed control
|
||||
to set the speed the CPU runs at; it simply runs as fast as your
|
||||
processor can run it.
|
||||
To add speed control, you will need a high-precision timestamp
|
||||
function (like the RDTSC instruction for newer Pentium CPUs)
|
||||
and a bit of arithmetic to make the cycles argument for m68k_execute().
|
||||
I'll leave that as an excercise to the reader.
|
192
plat/linux68k/emu/musashi/example/m68kconf.h
Executable file
192
plat/linux68k/emu/musashi/example/m68kconf.h
Executable file
|
@ -0,0 +1,192 @@
|
|||
/* ======================================================================== */
|
||||
/* ========================= LICENSING & COPYRIGHT ======================== */
|
||||
/* ======================================================================== */
|
||||
/*
|
||||
* MUSASHI
|
||||
* Version 3.4
|
||||
*
|
||||
* A portable Motorola M680x0 processor emulation engine.
|
||||
* Copyright 1998-2001 Karl Stenerud. All rights reserved.
|
||||
*
|
||||
* This code may be freely used for non-commercial purposes as long as this
|
||||
* copyright notice remains unaltered in the source code and any binary files
|
||||
* containing this code in compiled form.
|
||||
*
|
||||
* All other lisencing terms must be negotiated with the author
|
||||
* (Karl Stenerud).
|
||||
*
|
||||
* The latest version of this code can be obtained at:
|
||||
* http://kstenerud.cjb.net
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef M68KCONF__HEADER
|
||||
#define M68KCONF__HEADER
|
||||
|
||||
|
||||
/* Configuration switches.
|
||||
* Use OPT_SPECIFY_HANDLER for configuration options that allow callbacks.
|
||||
* OPT_SPECIFY_HANDLER causes the core to link directly to the function
|
||||
* or macro you specify, rather than using callback functions whose pointer
|
||||
* must be passed in using m68k_set_xxx_callback().
|
||||
*/
|
||||
#define OPT_OFF 0
|
||||
#define OPT_ON 1
|
||||
#define OPT_SPECIFY_HANDLER 2
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== MAME STUFF ============================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* If you're compiling this for MAME, only change M68K_COMPILE_FOR_MAME
|
||||
* to OPT_ON and use m68kmame.h to configure the 68k core.
|
||||
*/
|
||||
#ifndef M68K_COMPILE_FOR_MAME
|
||||
#define M68K_COMPILE_FOR_MAME OPT_OFF
|
||||
#endif /* M68K_COMPILE_FOR_MAME */
|
||||
|
||||
|
||||
#if M68K_COMPILE_FOR_MAME == OPT_OFF
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================= CONFIGURATION ============================ */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* Turn ON if you want to use the following M68K variants */
|
||||
#define M68K_EMULATE_010 OPT_ON
|
||||
#define M68K_EMULATE_EC020 OPT_ON
|
||||
#define M68K_EMULATE_020 OPT_ON
|
||||
|
||||
|
||||
/* If ON, the CPU will call m68k_read_immediate_xx() for immediate addressing
|
||||
* and m68k_read_pcrelative_xx() for PC-relative addressing.
|
||||
* If off, all read requests from the CPU will be redirected to m68k_read_xx()
|
||||
*/
|
||||
#define M68K_SEPARATE_READS OPT_OFF
|
||||
|
||||
/* If ON, the CPU will call m68k_write_32_pd() when it executes move.l with a
|
||||
* predecrement destination EA mode instead of m68k_write_32().
|
||||
* To simulate real 68k behavior, m68k_write_32_pd() must first write the high
|
||||
* word to [address+2], and then write the low word to [address].
|
||||
*/
|
||||
#define M68K_SIMULATE_PD_WRITES OPT_OFF
|
||||
|
||||
/* If ON, CPU will call the interrupt acknowledge callback when it services an
|
||||
* interrupt.
|
||||
* If off, all interrupts will be autovectored and all interrupt requests will
|
||||
* auto-clear when the interrupt is serviced.
|
||||
*/
|
||||
#define M68K_EMULATE_INT_ACK OPT_SPECIFY_HANDLER
|
||||
#define M68K_INT_ACK_CALLBACK(A) cpu_irq_ack(A)
|
||||
|
||||
|
||||
/* If ON, CPU will call the breakpoint acknowledge callback when it encounters
|
||||
* a breakpoint instruction and it is running a 68010+.
|
||||
*/
|
||||
#define M68K_EMULATE_BKPT_ACK OPT_OFF
|
||||
#define M68K_BKPT_ACK_CALLBACK() your_bkpt_ack_handler_function()
|
||||
|
||||
|
||||
/* If ON, the CPU will monitor the trace flags and take trace exceptions
|
||||
*/
|
||||
#define M68K_EMULATE_TRACE OPT_OFF
|
||||
|
||||
|
||||
/* If ON, CPU will call the output reset callback when it encounters a reset
|
||||
* instruction.
|
||||
*/
|
||||
#define M68K_EMULATE_RESET OPT_SPECIFY_HANDLER
|
||||
#define M68K_RESET_CALLBACK() cpu_pulse_reset()
|
||||
|
||||
|
||||
/* If ON, CPU will call the set fc callback on every memory access to
|
||||
* differentiate between user/supervisor, program/data access like a real
|
||||
* 68000 would. This should be enabled and the callback should be set if you
|
||||
* want to properly emulate the m68010 or higher. (moves uses function codes
|
||||
* to read/write data from different address spaces)
|
||||
*/
|
||||
#define M68K_EMULATE_FC OPT_SPECIFY_HANDLER
|
||||
#define M68K_SET_FC_CALLBACK(A) cpu_set_fc(A)
|
||||
|
||||
|
||||
/* If ON, CPU will call the pc changed callback when it changes the PC by a
|
||||
* large value. This allows host programs to be nicer when it comes to
|
||||
* fetching immediate data and instructions on a banked memory system.
|
||||
*/
|
||||
#define M68K_MONITOR_PC OPT_OFF
|
||||
#define M68K_SET_PC_CALLBACK(A) your_pc_changed_handler_function(A)
|
||||
|
||||
|
||||
/* If ON, CPU will call the instruction hook callback before every
|
||||
* instruction.
|
||||
*/
|
||||
#define M68K_INSTRUCTION_HOOK OPT_SPECIFY_HANDLER
|
||||
#define M68K_INSTRUCTION_CALLBACK() cpu_instr_callback()
|
||||
|
||||
|
||||
/* If ON, the CPU will emulate the 4-byte prefetch queue of a real 68000 */
|
||||
#define M68K_EMULATE_PREFETCH OPT_ON
|
||||
|
||||
|
||||
/* If ON, the CPU will generate address error exceptions if it tries to
|
||||
* access a word or longword at an odd address.
|
||||
* NOTE: This is only emulated properly for 68000 mode.
|
||||
*/
|
||||
#define M68K_EMULATE_ADDRESS_ERROR OPT_ON
|
||||
|
||||
|
||||
/* Turn ON to enable logging of illegal instruction calls.
|
||||
* M68K_LOG_FILEHANDLE must be #defined to a stdio file stream.
|
||||
* Turn on M68K_LOG_1010_1111 to log all 1010 and 1111 calls.
|
||||
*/
|
||||
#define M68K_LOG_ENABLE OPT_OFF
|
||||
#define M68K_LOG_1010_1111 OPT_OFF
|
||||
#define M68K_LOG_FILEHANDLE some_file_handle
|
||||
|
||||
|
||||
/* ----------------------------- COMPATIBILITY ---------------------------- */
|
||||
|
||||
/* The following options set optimizations that violate the current ANSI
|
||||
* standard, but will be compliant under the forthcoming C9X standard.
|
||||
*/
|
||||
|
||||
|
||||
/* If ON, the enulation core will use 64-bit integers to speed up some
|
||||
* operations.
|
||||
*/
|
||||
#define M68K_USE_64_BIT OPT_OFF
|
||||
|
||||
|
||||
/* Set to your compiler's static inline keyword to enable it, or
|
||||
* set it to blank to disable it.
|
||||
* If you define INLINE in the makefile, it will override this value.
|
||||
* NOTE: not enabling inline functions will SEVERELY slow down emulation.
|
||||
*/
|
||||
#ifndef INLINE
|
||||
#define INLINE static __inline__
|
||||
#endif /* INLINE */
|
||||
|
||||
#endif /* M68K_COMPILE_FOR_MAME */
|
||||
|
||||
#include "sim.h"
|
||||
|
||||
#define m68k_read_memory_8(A) cpu_read_byte(A)
|
||||
#define m68k_read_memory_16(A) cpu_read_word(A)
|
||||
#define m68k_read_memory_32(A) cpu_read_long(A)
|
||||
|
||||
#define m68k_read_disassembler_16(A) cpu_read_word_dasm(A)
|
||||
#define m68k_read_disassembler_32(A) cpu_read_long_dasm(A)
|
||||
|
||||
#define m68k_write_memory_8(A, V) cpu_write_byte(A, V)
|
||||
#define m68k_write_memory_16(A, V) cpu_write_word(A, V)
|
||||
#define m68k_write_memory_32(A, V) cpu_write_long(A, V)
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== END OF FILE ============================= */
|
||||
/* ======================================================================== */
|
||||
|
||||
#endif /* M68KCONF__HEADER */
|
42
plat/linux68k/emu/musashi/example/makefile
Executable file
42
plat/linux68k/emu/musashi/example/makefile
Executable file
|
@ -0,0 +1,42 @@
|
|||
EXENAME = sim
|
||||
|
||||
OSD_DOS = osd_dos.c
|
||||
|
||||
OSDFILES = $(OSD_DOS)
|
||||
MAINFILES = sim.c
|
||||
MUSASHIFILES = m68kcpu.c m68kdasm.c
|
||||
MUSASHIGENCFILES = m68kops.c m68kopac.c m68kopdm.c m68kopnz.c
|
||||
MUSASHIGENHFILES = m68kops.h
|
||||
MUSASHIGENERATOR = m68kmake
|
||||
|
||||
EXE = .exe
|
||||
EXEPATH = .\\
|
||||
# EXE =
|
||||
# EXEPATH = ./
|
||||
|
||||
.CFILES = $(MAINFILES) $(OSDFILES) $(MUSASHIFILES) $(MUSASHIGENCFILES)
|
||||
.OFILES = $(.CFILES:%.c=%.o)
|
||||
|
||||
CC = gcc
|
||||
WARNINGS = -Wall -pedantic
|
||||
CFLAGS = $(WARNINGS)
|
||||
LFLAGS = $(WARNINGS)
|
||||
|
||||
TARGET = $(EXENAME)$(EXE)
|
||||
|
||||
DELETEFILES = $(MUSASHIGENCFILES) $(MUSASHIGENHFILES) $(.OFILES) $(TARGET) $(MUSASHIGENERATOR)$(EXE)
|
||||
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
clean:
|
||||
rm -f $(DELETEFILES)
|
||||
|
||||
$(TARGET): $(MUSASHIGENHFILES) $(.OFILES) makefile
|
||||
$(CC) -o $@ $(.OFILES) $(LFLAGS)
|
||||
|
||||
$(MUSASHIGENCFILES) $(MUSASHIGENHFILES): $(MUSASHIGENERATOR)$(EXE)
|
||||
$(EXEPATH)$(MUSASHIGENERATOR)$(EXE)
|
||||
|
||||
$(MUSASHIGENERATOR)$(EXE): $(MUSASHIGENERATOR).c
|
||||
$(CC) -o $(MUSASHIGENERATOR)$(EXE) $(MUSASHIGENERATOR).c
|
6
plat/linux68k/emu/musashi/example/osd.h
Executable file
6
plat/linux68k/emu/musashi/example/osd.h
Executable file
|
@ -0,0 +1,6 @@
|
|||
#ifndef HEADER__OSD
|
||||
#define HEADER__OSD
|
||||
|
||||
int osd_get_char(void);
|
||||
|
||||
#endif /* HEADER__OSD */
|
16
plat/linux68k/emu/musashi/example/osd_dos.c
Executable file
16
plat/linux68k/emu/musashi/example/osd_dos.c
Executable file
|
@ -0,0 +1,16 @@
|
|||
#include "osd.h"
|
||||
|
||||
/* OS-dependant code to get a character from the user.
|
||||
* This function must not block, and must either return an ASCII code or -1.
|
||||
*/
|
||||
#include <conio.h>
|
||||
int osd_get_char(void)
|
||||
{
|
||||
int ch = -1;
|
||||
if(kbhit())
|
||||
{
|
||||
while(kbhit())
|
||||
ch = getch();
|
||||
}
|
||||
return ch;
|
||||
}
|
BIN
plat/linux68k/emu/musashi/example/program.bin
Executable file
BIN
plat/linux68k/emu/musashi/example/program.bin
Executable file
Binary file not shown.
After Width: | Height: | Size: 348 B |
561
plat/linux68k/emu/musashi/example/sim.c
Executable file
561
plat/linux68k/emu/musashi/example/sim.c
Executable file
|
@ -0,0 +1,561 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include "sim.h"
|
||||
#include "m68k.h"
|
||||
#include "osd.h"
|
||||
|
||||
void disassemble_program();
|
||||
|
||||
/* Memory-mapped IO ports */
|
||||
#define INPUT_ADDRESS 0x800000
|
||||
#define OUTPUT_ADDRESS 0x400000
|
||||
|
||||
/* IRQ connections */
|
||||
#define IRQ_NMI_DEVICE 7
|
||||
#define IRQ_INPUT_DEVICE 2
|
||||
#define IRQ_OUTPUT_DEVICE 1
|
||||
|
||||
/* Time between characters sent to output device (seconds) */
|
||||
#define OUTPUT_DEVICE_PERIOD 1
|
||||
|
||||
/* ROM and RAM sizes */
|
||||
#define MAX_ROM 0xfff
|
||||
#define MAX_RAM 0xff
|
||||
|
||||
|
||||
/* Read/write macros */
|
||||
#define READ_BYTE(BASE, ADDR) (BASE)[ADDR]
|
||||
#define READ_WORD(BASE, ADDR) (((BASE)[ADDR]<<8) | \
|
||||
(BASE)[(ADDR)+1])
|
||||
#define READ_LONG(BASE, ADDR) (((BASE)[ADDR]<<24) | \
|
||||
((BASE)[(ADDR)+1]<<16) | \
|
||||
((BASE)[(ADDR)+2]<<8) | \
|
||||
(BASE)[(ADDR)+3])
|
||||
|
||||
#define WRITE_BYTE(BASE, ADDR, VAL) (BASE)[ADDR] = (VAL)&0xff
|
||||
#define WRITE_WORD(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>8) & 0xff; \
|
||||
(BASE)[(ADDR)+1] = (VAL)&0xff
|
||||
#define WRITE_LONG(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>24) & 0xff; \
|
||||
(BASE)[(ADDR)+1] = ((VAL)>>16)&0xff; \
|
||||
(BASE)[(ADDR)+2] = ((VAL)>>8)&0xff; \
|
||||
(BASE)[(ADDR)+3] = (VAL)&0xff
|
||||
|
||||
|
||||
/* Prototypes */
|
||||
void exit_error(char* fmt, ...);
|
||||
|
||||
unsigned int cpu_read_byte(unsigned int address);
|
||||
unsigned int cpu_read_word(unsigned int address);
|
||||
unsigned int cpu_read_long(unsigned int address);
|
||||
void cpu_write_byte(unsigned int address, unsigned int value);
|
||||
void cpu_write_word(unsigned int address, unsigned int value);
|
||||
void cpu_write_long(unsigned int address, unsigned int value);
|
||||
void cpu_pulse_reset(void);
|
||||
void cpu_set_fc(unsigned int fc);
|
||||
int cpu_irq_ack(int level);
|
||||
|
||||
void nmi_device_reset(void);
|
||||
void nmi_device_update(void);
|
||||
int nmi_device_ack(void);
|
||||
|
||||
void input_device_reset(void);
|
||||
void input_device_update(void);
|
||||
int input_device_ack(void);
|
||||
unsigned int input_device_read(void);
|
||||
void input_device_write(unsigned int value);
|
||||
|
||||
void output_device_reset(void);
|
||||
void output_device_update(void);
|
||||
int output_device_ack(void);
|
||||
unsigned int output_device_read(void);
|
||||
void output_device_write(unsigned int value);
|
||||
|
||||
void int_controller_set(unsigned int value);
|
||||
void int_controller_clear(unsigned int value);
|
||||
|
||||
void get_user_input(void);
|
||||
|
||||
|
||||
/* Data */
|
||||
unsigned int g_quit = 0; /* 1 if we want to quit */
|
||||
unsigned int g_nmi = 0; /* 1 if nmi pending */
|
||||
|
||||
int g_input_device_value = -1; /* Current value in input device */
|
||||
|
||||
unsigned int g_output_device_ready = 0; /* 1 if output device is ready */
|
||||
time_t g_output_device_last_output; /* Time of last char output */
|
||||
|
||||
unsigned int g_int_controller_pending = 0; /* list of pending interrupts */
|
||||
unsigned int g_int_controller_highest_int = 0; /* Highest pending interrupt */
|
||||
|
||||
unsigned char g_rom[MAX_ROM+1]; /* ROM */
|
||||
unsigned char g_ram[MAX_RAM+1]; /* RAM */
|
||||
unsigned int g_fc; /* Current function code from CPU */
|
||||
|
||||
|
||||
/* Exit with an error message. Use printf syntax. */
|
||||
void exit_error(char* fmt, ...)
|
||||
{
|
||||
static int guard_val = 0;
|
||||
char buff[100];
|
||||
unsigned int pc;
|
||||
va_list args;
|
||||
|
||||
if(guard_val)
|
||||
return;
|
||||
else
|
||||
guard_val = 1;
|
||||
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
fprintf(stderr, "\n");
|
||||
pc = m68k_get_reg(NULL, M68K_REG_PPC);
|
||||
m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000);
|
||||
fprintf(stderr, "At %04x: %s\n", pc, buff);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
/* Read data from RAM, ROM, or a device */
|
||||
unsigned int cpu_read_byte(unsigned int address)
|
||||
{
|
||||
if(g_fc & 2) /* Program */
|
||||
{
|
||||
if(address > MAX_ROM)
|
||||
exit_error("Attempted to read byte from ROM address %08x", address);
|
||||
return READ_BYTE(g_rom, address);
|
||||
}
|
||||
|
||||
/* Otherwise it's data space */
|
||||
switch(address)
|
||||
{
|
||||
case INPUT_ADDRESS:
|
||||
return input_device_read();
|
||||
case OUTPUT_ADDRESS:
|
||||
return output_device_read();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(address > MAX_RAM)
|
||||
exit_error("Attempted to read byte from RAM address %08x", address);
|
||||
return READ_BYTE(g_ram, address);
|
||||
}
|
||||
|
||||
unsigned int cpu_read_word(unsigned int address)
|
||||
{
|
||||
if(g_fc & 2) /* Program */
|
||||
{
|
||||
if(address > MAX_ROM)
|
||||
exit_error("Attempted to read word from ROM address %08x", address);
|
||||
return READ_WORD(g_rom, address);
|
||||
}
|
||||
|
||||
/* Otherwise it's data space */
|
||||
switch(address)
|
||||
{
|
||||
case INPUT_ADDRESS:
|
||||
return input_device_read();
|
||||
case OUTPUT_ADDRESS:
|
||||
return output_device_read();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(address > MAX_RAM)
|
||||
exit_error("Attempted to read word from RAM address %08x", address);
|
||||
return READ_WORD(g_ram, address);
|
||||
}
|
||||
|
||||
unsigned int cpu_read_long(unsigned int address)
|
||||
{
|
||||
if(g_fc & 2) /* Program */
|
||||
{
|
||||
if(address > MAX_ROM)
|
||||
exit_error("Attempted to read long from ROM address %08x", address);
|
||||
return READ_LONG(g_rom, address);
|
||||
}
|
||||
|
||||
/* Otherwise it's data space */
|
||||
switch(address)
|
||||
{
|
||||
case INPUT_ADDRESS:
|
||||
return input_device_read();
|
||||
case OUTPUT_ADDRESS:
|
||||
return output_device_read();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(address > MAX_RAM)
|
||||
exit_error("Attempted to read long from RAM address %08x", address);
|
||||
return READ_LONG(g_ram, address);
|
||||
}
|
||||
|
||||
|
||||
unsigned int cpu_read_word_dasm(unsigned int address)
|
||||
{
|
||||
if(address > MAX_ROM)
|
||||
exit_error("Disassembler attempted to read word from ROM address %08x", address);
|
||||
return READ_WORD(g_rom, address);
|
||||
}
|
||||
|
||||
unsigned int cpu_read_long_dasm(unsigned int address)
|
||||
{
|
||||
if(address > MAX_ROM)
|
||||
exit_error("Dasm attempted to read long from ROM address %08x", address);
|
||||
return READ_LONG(g_rom, address);
|
||||
}
|
||||
|
||||
|
||||
/* Write data to RAM or a device */
|
||||
void cpu_write_byte(unsigned int address, unsigned int value)
|
||||
{
|
||||
if(g_fc & 2) /* Program */
|
||||
exit_error("Attempted to write %02x to ROM address %08x", value&0xff, address);
|
||||
|
||||
/* Otherwise it's data space */
|
||||
switch(address)
|
||||
{
|
||||
case INPUT_ADDRESS:
|
||||
input_device_write(value&0xff);
|
||||
return;
|
||||
case OUTPUT_ADDRESS:
|
||||
output_device_write(value&0xff);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(address > MAX_RAM)
|
||||
exit_error("Attempted to write %02x to RAM address %08x", value&0xff, address);
|
||||
WRITE_BYTE(g_ram, address, value);
|
||||
}
|
||||
|
||||
void cpu_write_word(unsigned int address, unsigned int value)
|
||||
{
|
||||
if(g_fc & 2) /* Program */
|
||||
exit_error("Attempted to write %04x to ROM address %08x", value&0xffff, address);
|
||||
|
||||
/* Otherwise it's data space */
|
||||
switch(address)
|
||||
{
|
||||
case INPUT_ADDRESS:
|
||||
input_device_write(value&0xffff);
|
||||
return;
|
||||
case OUTPUT_ADDRESS:
|
||||
output_device_write(value&0xffff);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(address > MAX_RAM)
|
||||
exit_error("Attempted to write %04x to RAM address %08x", value&0xffff, address);
|
||||
WRITE_WORD(g_ram, address, value);
|
||||
}
|
||||
|
||||
void cpu_write_long(unsigned int address, unsigned int value)
|
||||
{
|
||||
if(g_fc & 2) /* Program */
|
||||
exit_error("Attempted to write %08x to ROM address %08x", value, address);
|
||||
|
||||
/* Otherwise it's data space */
|
||||
switch(address)
|
||||
{
|
||||
case INPUT_ADDRESS:
|
||||
input_device_write(value);
|
||||
return;
|
||||
case OUTPUT_ADDRESS:
|
||||
output_device_write(value);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(address > MAX_RAM)
|
||||
exit_error("Attempted to write %08x to RAM address %08x", value, address);
|
||||
WRITE_LONG(g_ram, address, value);
|
||||
}
|
||||
|
||||
/* Called when the CPU pulses the RESET line */
|
||||
void cpu_pulse_reset(void)
|
||||
{
|
||||
nmi_device_reset();
|
||||
output_device_reset();
|
||||
input_device_reset();
|
||||
}
|
||||
|
||||
/* Called when the CPU changes the function code pins */
|
||||
void cpu_set_fc(unsigned int fc)
|
||||
{
|
||||
g_fc = fc;
|
||||
}
|
||||
|
||||
/* Called when the CPU acknowledges an interrupt */
|
||||
int cpu_irq_ack(int level)
|
||||
{
|
||||
switch(level)
|
||||
{
|
||||
case IRQ_NMI_DEVICE:
|
||||
return nmi_device_ack();
|
||||
case IRQ_INPUT_DEVICE:
|
||||
return input_device_ack();
|
||||
case IRQ_OUTPUT_DEVICE:
|
||||
return output_device_ack();
|
||||
}
|
||||
return M68K_INT_ACK_SPURIOUS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Implementation for the NMI device */
|
||||
void nmi_device_reset(void)
|
||||
{
|
||||
g_nmi = 0;
|
||||
}
|
||||
|
||||
void nmi_device_update(void)
|
||||
{
|
||||
if(g_nmi)
|
||||
{
|
||||
g_nmi = 0;
|
||||
int_controller_set(IRQ_NMI_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
int nmi_device_ack(void)
|
||||
{
|
||||
printf("\nNMI\n");fflush(stdout);
|
||||
int_controller_clear(IRQ_NMI_DEVICE);
|
||||
return M68K_INT_ACK_AUTOVECTOR;
|
||||
}
|
||||
|
||||
|
||||
/* Implementation for the input device */
|
||||
void input_device_reset(void)
|
||||
{
|
||||
g_input_device_value = -1;
|
||||
int_controller_clear(IRQ_INPUT_DEVICE);
|
||||
}
|
||||
|
||||
void input_device_update(void)
|
||||
{
|
||||
if(g_input_device_value >= 0)
|
||||
int_controller_set(IRQ_INPUT_DEVICE);
|
||||
}
|
||||
|
||||
int input_device_ack(void)
|
||||
{
|
||||
return M68K_INT_ACK_AUTOVECTOR;
|
||||
}
|
||||
|
||||
unsigned int input_device_read(void)
|
||||
{
|
||||
int value = g_input_device_value > 0 ? g_input_device_value : 0;
|
||||
int_controller_clear(IRQ_INPUT_DEVICE);
|
||||
g_input_device_value = -1;
|
||||
return value;
|
||||
}
|
||||
|
||||
void input_device_write(unsigned int value)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* Implementation for the output device */
|
||||
void output_device_reset(void)
|
||||
{
|
||||
g_output_device_last_output = time(NULL);
|
||||
g_output_device_ready = 0;
|
||||
int_controller_clear(IRQ_OUTPUT_DEVICE);
|
||||
}
|
||||
|
||||
void output_device_update(void)
|
||||
{
|
||||
if(!g_output_device_ready)
|
||||
{
|
||||
if((time(NULL) - g_output_device_last_output) >= OUTPUT_DEVICE_PERIOD)
|
||||
{
|
||||
g_output_device_ready = 1;
|
||||
int_controller_set(IRQ_OUTPUT_DEVICE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int output_device_ack(void)
|
||||
{
|
||||
return M68K_INT_ACK_AUTOVECTOR;
|
||||
}
|
||||
|
||||
unsigned int output_device_read(void)
|
||||
{
|
||||
int_controller_clear(IRQ_OUTPUT_DEVICE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void output_device_write(unsigned int value)
|
||||
{
|
||||
char ch;
|
||||
if(g_output_device_ready)
|
||||
{
|
||||
ch = value & 0xff;
|
||||
printf("%c", ch);
|
||||
g_output_device_last_output = time(NULL);
|
||||
g_output_device_ready = 0;
|
||||
int_controller_clear(IRQ_OUTPUT_DEVICE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Implementation for the interrupt controller */
|
||||
void int_controller_set(unsigned int value)
|
||||
{
|
||||
unsigned int old_pending = g_int_controller_pending;
|
||||
|
||||
g_int_controller_pending |= (1<<value);
|
||||
|
||||
if(old_pending != g_int_controller_pending && value > g_int_controller_highest_int)
|
||||
{
|
||||
g_int_controller_highest_int = value;
|
||||
m68k_set_irq(g_int_controller_highest_int);
|
||||
}
|
||||
}
|
||||
|
||||
void int_controller_clear(unsigned int value)
|
||||
{
|
||||
g_int_controller_pending &= ~(1<<value);
|
||||
|
||||
for(g_int_controller_highest_int = 7;g_int_controller_highest_int > 0;g_int_controller_highest_int--)
|
||||
if(g_int_controller_pending & (1<<g_int_controller_highest_int))
|
||||
break;
|
||||
|
||||
m68k_set_irq(g_int_controller_highest_int);
|
||||
}
|
||||
|
||||
|
||||
/* Parse user input and update any devices that need user input */
|
||||
void get_user_input(void)
|
||||
{
|
||||
static int last_ch = -1;
|
||||
int ch = osd_get_char();
|
||||
|
||||
if(ch >= 0)
|
||||
{
|
||||
switch(ch)
|
||||
{
|
||||
case 0x1b:
|
||||
g_quit = 1;
|
||||
break;
|
||||
case '~':
|
||||
if(last_ch != ch)
|
||||
g_nmi = 1;
|
||||
break;
|
||||
default:
|
||||
g_input_device_value = ch;
|
||||
}
|
||||
}
|
||||
last_ch = ch;
|
||||
}
|
||||
|
||||
/* Disassembler */
|
||||
void make_hex(char* buff, unsigned int pc, unsigned int length)
|
||||
{
|
||||
char* ptr = buff;
|
||||
|
||||
for(;length>0;length -= 2)
|
||||
{
|
||||
sprintf(ptr, "%04x", cpu_read_word_dasm(pc));
|
||||
pc += 2;
|
||||
ptr += 4;
|
||||
if(length > 2)
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
void disassemble_program()
|
||||
{
|
||||
unsigned int pc;
|
||||
unsigned int instr_size;
|
||||
char buff[100];
|
||||
char buff2[100];
|
||||
|
||||
pc = cpu_read_long_dasm(4);
|
||||
|
||||
while(pc <= 0x16e)
|
||||
{
|
||||
instr_size = m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000);
|
||||
make_hex(buff2, pc, instr_size);
|
||||
printf("%03x: %-20s: %s\n", pc, buff2, buff);
|
||||
pc += instr_size;
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void cpu_instr_callback()
|
||||
{
|
||||
/* The following code would print out instructions as they are executed */
|
||||
/*
|
||||
static char buff[100];
|
||||
static char buff2[100];
|
||||
static unsigned int pc;
|
||||
static unsigned int instr_size;
|
||||
|
||||
pc = m68k_get_reg(NULL, M68K_REG_PC);
|
||||
instr_size = m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000);
|
||||
make_hex(buff2, pc, instr_size);
|
||||
printf("E %03x: %-20s: %s\n", pc, buff2, buff);
|
||||
fflush(stdout);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* The main loop */
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
FILE* fhandle;
|
||||
|
||||
if(argc != 2)
|
||||
{
|
||||
printf("Usage: sim <program file>\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if((fhandle = fopen(argv[1], "rb")) == NULL)
|
||||
exit_error("Unable to open %s", argv[1]);
|
||||
|
||||
if(fread(g_rom, 1, MAX_ROM+1, fhandle) <= 0)
|
||||
exit_error("Error reading %s", argv[1]);
|
||||
|
||||
// disassemble_program();
|
||||
|
||||
m68k_init();
|
||||
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
|
||||
m68k_pulse_reset();
|
||||
input_device_reset();
|
||||
output_device_reset();
|
||||
nmi_device_reset();
|
||||
|
||||
g_quit = 0;
|
||||
while(!g_quit)
|
||||
{
|
||||
// Our loop requires some interleaving to allow us to update the
|
||||
// input, output, and nmi devices.
|
||||
|
||||
get_user_input();
|
||||
|
||||
// Values to execute determine the interleave rate.
|
||||
// Smaller values allow for more accurate interleaving with multiple
|
||||
// devices/CPUs but is more processor intensive.
|
||||
// 100000 is usually a good value to start at, then work from there.
|
||||
|
||||
// Note that I am not emulating the correct clock speed!
|
||||
m68k_execute(100000);
|
||||
output_device_update();
|
||||
input_device_update();
|
||||
nmi_device_update();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
15
plat/linux68k/emu/musashi/example/sim.h
Executable file
15
plat/linux68k/emu/musashi/example/sim.h
Executable file
|
@ -0,0 +1,15 @@
|
|||
#ifndef SIM__HEADER
|
||||
#define SIM__HEADER
|
||||
|
||||
unsigned int cpu_read_byte(unsigned int address);
|
||||
unsigned int cpu_read_word(unsigned int address);
|
||||
unsigned int cpu_read_long(unsigned int address);
|
||||
void cpu_write_byte(unsigned int address, unsigned int value);
|
||||
void cpu_write_word(unsigned int address, unsigned int value);
|
||||
void cpu_write_long(unsigned int address, unsigned int value);
|
||||
void cpu_pulse_reset(void);
|
||||
void cpu_set_fc(unsigned int fc);
|
||||
int cpu_irq_ack(int level);
|
||||
void cpu_instr_callback();
|
||||
|
||||
#endif /* SIM__HEADER */
|
115
plat/linux68k/emu/musashi/history.txt
Executable file
115
plat/linux68k/emu/musashi/history.txt
Executable file
|
@ -0,0 +1,115 @@
|
|||
The history of Musashi for anyone who might be interested:
|
||||
---------------------------------------------------------
|
||||
|
||||
Musashi was born out of sheer boredom.
|
||||
I needed something to code, and so having had fun with a few of the emulators
|
||||
around, I decided to try my hand at CPU emulation.
|
||||
I had owned an Amiga for many years and had done some assembly coding on it so
|
||||
I figured it would be the ideal chip to cut my teeth on.
|
||||
Had I known then how much work was involved in emulating a chip like this, I
|
||||
may not have even started ;-)
|
||||
|
||||
|
||||
15-Jul-2013: Musashi license changed to MIT.
|
||||
|
||||
10-Jun-2002: Musashi 3.4 released
|
||||
- Added various undocumented m68k features thanks to Bart
|
||||
Trzynadlowski's experiments.
|
||||
See http://dynarec.com/~bart/files/68knotes.txt for details.
|
||||
- Fixed a bug that caused privilege violation and illegal
|
||||
instruction exceptions to stack the wrong PC value.
|
||||
- Added emulation of address errors (Note: this only works
|
||||
in 68000 mode. All other CPUs require a LOT of overhead
|
||||
to emulate this. I'm not sure if I'll implement them or not.
|
||||
|
||||
27-Jan-2001: Musashi 3.3 released
|
||||
- Fixed problem when displaying negative numbers in disassembler
|
||||
- Fixed cpu type selector - was allowing 020 instructions to be
|
||||
disassembled when in 000 mode.
|
||||
- Fixed opcode jumptable generator (ambiguous operators in the
|
||||
test for f-line ops)
|
||||
- Fixed signed/unsigned problem in divl and mull opcodes (not
|
||||
sure if this was causing an error but best to be sure)
|
||||
- Cleaned up the naming scheme for the opcode handlers
|
||||
|
||||
14-Aug-2000: Musashi 3.2 released
|
||||
- Fixed RTE bug that killed the program counter when in m68020
|
||||
mode.
|
||||
- Minor fixes in negx and nbcd.
|
||||
- renamed d68k.c to m68kdasm.c and merged d68k.h into m68k.h.
|
||||
d68k_read_xxx() instructions have been renamed to
|
||||
m68k_read_xxx_disassembler().
|
||||
- Rewrote exception processing and fixed 68020 stack frame
|
||||
problems.
|
||||
- FINALLY fixed the mull and divl instructions.
|
||||
- Added 64-bit safe code fixes.
|
||||
- Added 64-bit optimizations (these will only be ANSI compliant
|
||||
under c9x, and so to use them you must turn on M68K_USE_64_BIT
|
||||
in m68kconf.h).
|
||||
|
||||
28-May-2000: Musashi 3.1 released
|
||||
- Fixed bug in m68k_get_reg() that retrieved the wrong value for
|
||||
the status register.
|
||||
- Fixed register bug in movec.
|
||||
- Fixed cpu type comparison problem that caused indexed
|
||||
addressing modes to be incorrectly interpreted when in m68ec020
|
||||
mode.
|
||||
- Added code to speed up busy waiting on some branch instructions.
|
||||
- Fixed some bfxxx opcode bugs.
|
||||
|
||||
05-Apr-2000: Musashi 3.0 released
|
||||
- Major code overhaul.
|
||||
- Rewrote code generator program and changed the format of
|
||||
m68k_in.c.
|
||||
- Added support for m68ec020.
|
||||
- Removed timing from the opcode handlers.
|
||||
- Added correct timing for m68000, m68010, and m68020.
|
||||
Note: 68020 timing is the cache timing from the manual.
|
||||
- Removed the m68k_peek_xxx() and m68k_poke_xxx() instructions and
|
||||
replaced them with m68k_get_reg() and m68k_set_reg().
|
||||
- Added support for function codes.
|
||||
- Revamped m68kconf.h to be easier to configure and more powerful.
|
||||
- Added option to separate immediate and normal reads.
|
||||
- Added support for (undocumented) m68000 instruction prefetch.
|
||||
- Rewrote indexed addressing mode handling.
|
||||
- Rewrote interrupt handling.
|
||||
- Fixed a masking bug for m68k_get_reg() when requesting the PC.
|
||||
- Moved the instruction table sorting routine to m68kmake.c so
|
||||
that it is invoked at compile time rather than at runtime.
|
||||
- Rewrote the exception handling routines to support different
|
||||
stack frames (needed for m68020 emulation).
|
||||
- Rewrote faster status register and condition code flag handling
|
||||
functions / macros.
|
||||
- Fixed function code handling to fetch from program space when
|
||||
using pc-relative addressing.
|
||||
- Fixed initial program counter and stack pointer fetching on
|
||||
reset (loads from program space now).
|
||||
- A lot of code cleanup.
|
||||
- LOTS of bugfixes (especially in the m68020 code).
|
||||
|
||||
13-May-1999: Musashi 2.2 released
|
||||
- Added support for m68020.
|
||||
- Lots of bugfixes.
|
||||
|
||||
25-Mar-1999: Musashi 2.1 released
|
||||
- Added support for m68010.
|
||||
- Many bugfixes.
|
||||
|
||||
17-Mar-1999: Musashi 2.0 released
|
||||
- Major code overhaul.
|
||||
- Replaced monolithic codebase with a code generator program.
|
||||
- Added correct m68000 timing.
|
||||
- Moved timing into the opcode handlers.
|
||||
|
||||
06-Jan-1999: Musashi 1.0 released
|
||||
|
||||
20-Dec-1998: Beta release of Musashi v0.5 that could run Rastan Saga under MAME
|
||||
(barely).
|
||||
|
||||
04-Dec-1998: Final prototype v0.4
|
||||
|
||||
20-Nov-1998: First prototype v0.1
|
||||
|
||||
11-Jun-1998: Early disassembler
|
||||
|
||||
12-May-1998: First outline
|
358
plat/linux68k/emu/musashi/m68k.h
Executable file
358
plat/linux68k/emu/musashi/m68k.h
Executable file
|
@ -0,0 +1,358 @@
|
|||
/* ======================================================================== */
|
||||
/* ========================= LICENSING & COPYRIGHT ======================== */
|
||||
/* ======================================================================== */
|
||||
/*
|
||||
* MUSASHI
|
||||
* Version 3.4
|
||||
*
|
||||
* A portable Motorola M680x0 processor emulation engine.
|
||||
* Copyright 1998-2001 Karl Stenerud. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef M68K__HEADER
|
||||
#define M68K__HEADER
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================= CONFIGURATION ============================ */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* Import the configuration for this build */
|
||||
#include "m68kconf.h"
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================ GENERAL DEFINES =========================== */
|
||||
|
||||
/* ======================================================================== */
|
||||
|
||||
/* There are 7 levels of interrupt to the 68K.
|
||||
* A transition from < 7 to 7 will cause a non-maskable interrupt (NMI).
|
||||
*/
|
||||
#define M68K_IRQ_NONE 0
|
||||
#define M68K_IRQ_1 1
|
||||
#define M68K_IRQ_2 2
|
||||
#define M68K_IRQ_3 3
|
||||
#define M68K_IRQ_4 4
|
||||
#define M68K_IRQ_5 5
|
||||
#define M68K_IRQ_6 6
|
||||
#define M68K_IRQ_7 7
|
||||
|
||||
|
||||
/* Special interrupt acknowledge values.
|
||||
* Use these as special returns from the interrupt acknowledge callback
|
||||
* (specified later in this header).
|
||||
*/
|
||||
|
||||
/* Causes an interrupt autovector (0x18 + interrupt level) to be taken.
|
||||
* This happens in a real 68K if VPA or AVEC is asserted during an interrupt
|
||||
* acknowledge cycle instead of DTACK.
|
||||
*/
|
||||
#define M68K_INT_ACK_AUTOVECTOR 0xffffffff
|
||||
|
||||
/* Causes the spurious interrupt vector (0x18) to be taken
|
||||
* This happens in a real 68K if BERR is asserted during the interrupt
|
||||
* acknowledge cycle (i.e. no devices responded to the acknowledge).
|
||||
*/
|
||||
#define M68K_INT_ACK_SPURIOUS 0xfffffffe
|
||||
|
||||
|
||||
/* CPU types for use in m68k_set_cpu_type() */
|
||||
enum
|
||||
{
|
||||
M68K_CPU_TYPE_INVALID,
|
||||
M68K_CPU_TYPE_68000,
|
||||
M68K_CPU_TYPE_68010,
|
||||
M68K_CPU_TYPE_68EC020,
|
||||
M68K_CPU_TYPE_68020,
|
||||
M68K_CPU_TYPE_68030, /* Supported by disassembler ONLY */
|
||||
M68K_CPU_TYPE_68040 /* Supported by disassembler ONLY */
|
||||
};
|
||||
|
||||
/* Registers used by m68k_get_reg() and m68k_set_reg() */
|
||||
typedef enum
|
||||
{
|
||||
/* Real registers */
|
||||
M68K_REG_D0, /* Data registers */
|
||||
M68K_REG_D1,
|
||||
M68K_REG_D2,
|
||||
M68K_REG_D3,
|
||||
M68K_REG_D4,
|
||||
M68K_REG_D5,
|
||||
M68K_REG_D6,
|
||||
M68K_REG_D7,
|
||||
M68K_REG_A0, /* Address registers */
|
||||
M68K_REG_A1,
|
||||
M68K_REG_A2,
|
||||
M68K_REG_A3,
|
||||
M68K_REG_A4,
|
||||
M68K_REG_A5,
|
||||
M68K_REG_A6,
|
||||
M68K_REG_A7,
|
||||
M68K_REG_PC, /* Program Counter */
|
||||
M68K_REG_SR, /* Status Register */
|
||||
M68K_REG_SP, /* The current Stack Pointer (located in A7) */
|
||||
M68K_REG_USP, /* User Stack Pointer */
|
||||
M68K_REG_ISP, /* Interrupt Stack Pointer */
|
||||
M68K_REG_MSP, /* Master Stack Pointer */
|
||||
M68K_REG_SFC, /* Source Function Code */
|
||||
M68K_REG_DFC, /* Destination Function Code */
|
||||
M68K_REG_VBR, /* Vector Base Register */
|
||||
M68K_REG_CACR, /* Cache Control Register */
|
||||
M68K_REG_CAAR, /* Cache Address Register */
|
||||
|
||||
/* Assumed registers */
|
||||
/* These are cheat registers which emulate the 1-longword prefetch
|
||||
* present in the 68000 and 68010.
|
||||
*/
|
||||
M68K_REG_PREF_ADDR, /* Last prefetch address */
|
||||
M68K_REG_PREF_DATA, /* Last prefetch data */
|
||||
|
||||
/* Convenience registers */
|
||||
M68K_REG_PPC, /* Previous value in the program counter */
|
||||
M68K_REG_IR, /* Instruction register */
|
||||
M68K_REG_CPU_TYPE /* Type of CPU being run */
|
||||
} m68k_register_t;
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ====================== FUNCTIONS CALLED BY THE CPU ===================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* You will have to implement these functions */
|
||||
|
||||
/* read/write functions called by the CPU to access memory.
|
||||
* while values used are 32 bits, only the appropriate number
|
||||
* of bits are relevant (i.e. in write_memory_8, only the lower 8 bits
|
||||
* of value should be written to memory).
|
||||
*
|
||||
* NOTE: I have separated the immediate and PC-relative memory fetches
|
||||
* from the other memory fetches because some systems require
|
||||
* differentiation between PROGRAM and DATA fetches (usually
|
||||
* for security setups such as encryption).
|
||||
* This separation can either be achieved by setting
|
||||
* M68K_SEPARATE_READS in m68kconf.h and defining
|
||||
* the read functions, or by setting M68K_EMULATE_FC and
|
||||
* making a function code callback function.
|
||||
* Using the callback offers better emulation coverage
|
||||
* because you can also monitor whether the CPU is in SYSTEM or
|
||||
* USER mode, but it is also slower.
|
||||
*/
|
||||
|
||||
/* Read from anywhere */
|
||||
unsigned int m68k_read_memory_8(unsigned int address);
|
||||
unsigned int m68k_read_memory_16(unsigned int address);
|
||||
unsigned int m68k_read_memory_32(unsigned int address);
|
||||
|
||||
/* Read data immediately following the PC */
|
||||
unsigned int m68k_read_immediate_16(unsigned int address);
|
||||
unsigned int m68k_read_immediate_32(unsigned int address);
|
||||
|
||||
/* Read data relative to the PC */
|
||||
unsigned int m68k_read_pcrelative_8(unsigned int address);
|
||||
unsigned int m68k_read_pcrelative_16(unsigned int address);
|
||||
unsigned int m68k_read_pcrelative_32(unsigned int address);
|
||||
|
||||
/* Memory access for the disassembler */
|
||||
unsigned int m68k_read_disassembler_8 (unsigned int address);
|
||||
unsigned int m68k_read_disassembler_16 (unsigned int address);
|
||||
unsigned int m68k_read_disassembler_32 (unsigned int address);
|
||||
|
||||
/* Write to anywhere */
|
||||
void m68k_write_memory_8(unsigned int address, unsigned int value);
|
||||
void m68k_write_memory_16(unsigned int address, unsigned int value);
|
||||
void m68k_write_memory_32(unsigned int address, unsigned int value);
|
||||
|
||||
/* Special call to simulate undocumented 68k behavior when move.l with a
|
||||
* predecrement destination mode is executed.
|
||||
* To simulate real 68k behavior, first write the high word to
|
||||
* [address+2], and then write the low word to [address].
|
||||
*
|
||||
* Enable this functionality with M68K_SIMULATE_PD_WRITES in m68kconf.h.
|
||||
*/
|
||||
void m68k_write_memory_32_pd(unsigned int address, unsigned int value);
|
||||
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== CALLBACKS =============================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* These functions allow you to set callbacks to the host when specific events
|
||||
* occur. Note that you must enable the corresponding value in m68kconf.h
|
||||
* in order for these to do anything useful.
|
||||
* Note: I have defined default callbacks which are used if you have enabled
|
||||
* the corresponding #define in m68kconf.h but either haven't assigned a
|
||||
* callback or have assigned a callback of NULL.
|
||||
*/
|
||||
|
||||
/* Set the callback for an interrupt acknowledge.
|
||||
* You must enable M68K_EMULATE_INT_ACK in m68kconf.h.
|
||||
* The CPU will call the callback with the interrupt level being acknowledged.
|
||||
* The host program must return either a vector from 0x02-0xff, or one of the
|
||||
* special interrupt acknowledge values specified earlier in this header.
|
||||
* If this is not implemented, the CPU will always assume an autovectored
|
||||
* interrupt, and will automatically clear the interrupt request when it
|
||||
* services the interrupt.
|
||||
* Default behavior: return M68K_INT_ACK_AUTOVECTOR.
|
||||
*/
|
||||
void m68k_set_int_ack_callback(int (*callback)(int int_level));
|
||||
|
||||
|
||||
/* Set the callback for a breakpoint acknowledge (68010+).
|
||||
* You must enable M68K_EMULATE_BKPT_ACK in m68kconf.h.
|
||||
* The CPU will call the callback with whatever was in the data field of the
|
||||
* BKPT instruction for 68020+, or 0 for 68010.
|
||||
* Default behavior: do nothing.
|
||||
*/
|
||||
void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data));
|
||||
|
||||
|
||||
/* Set the callback for the RESET instruction.
|
||||
* You must enable M68K_EMULATE_RESET in m68kconf.h.
|
||||
* The CPU calls this callback every time it encounters a RESET instruction.
|
||||
* Default behavior: do nothing.
|
||||
*/
|
||||
void m68k_set_reset_instr_callback(void (*callback)(void));
|
||||
|
||||
|
||||
/* Set the callback for informing of a large PC change.
|
||||
* You must enable M68K_MONITOR_PC in m68kconf.h.
|
||||
* The CPU calls this callback with the new PC value every time the PC changes
|
||||
* by a large value (currently set for changes by longwords).
|
||||
* Default behavior: do nothing.
|
||||
*/
|
||||
void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc));
|
||||
|
||||
|
||||
/* Set the callback for CPU function code changes.
|
||||
* You must enable M68K_EMULATE_FC in m68kconf.h.
|
||||
* The CPU calls this callback with the function code before every memory
|
||||
* access to set the CPU's function code according to what kind of memory
|
||||
* access it is (supervisor/user, program/data and such).
|
||||
* Default behavior: do nothing.
|
||||
*/
|
||||
void m68k_set_fc_callback(void (*callback)(unsigned int new_fc));
|
||||
|
||||
|
||||
/* Set a callback for the instruction cycle of the CPU.
|
||||
* You must enable M68K_INSTRUCTION_HOOK in m68kconf.h.
|
||||
* The CPU calls this callback just before fetching the opcode in the
|
||||
* instruction cycle.
|
||||
* Default behavior: do nothing.
|
||||
*/
|
||||
void m68k_set_instr_hook_callback(void (*callback)(void));
|
||||
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ====================== FUNCTIONS TO ACCESS THE CPU ===================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* Use this function to set the CPU type you want to emulate.
|
||||
* Currently supported types are: M68K_CPU_TYPE_68000, M68K_CPU_TYPE_68010,
|
||||
* M68K_CPU_TYPE_EC020, and M68K_CPU_TYPE_68020.
|
||||
*/
|
||||
void m68k_set_cpu_type(unsigned int cpu_type);
|
||||
|
||||
/* Do whatever initialisations the core requires. Should be called
|
||||
* at least once at init time.
|
||||
*/
|
||||
void m68k_init(void);
|
||||
|
||||
/* Pulse the RESET pin on the CPU.
|
||||
* You *MUST* reset the CPU at least once to initialize the emulation
|
||||
* Note: If you didn't call m68k_set_cpu_type() before resetting
|
||||
* the CPU for the first time, the CPU will be set to
|
||||
* M68K_CPU_TYPE_68000.
|
||||
*/
|
||||
void m68k_pulse_reset(void);
|
||||
|
||||
/* execute num_cycles worth of instructions. returns number of cycles used */
|
||||
int m68k_execute(int num_cycles);
|
||||
|
||||
/* These functions let you read/write/modify the number of cycles left to run
|
||||
* while m68k_execute() is running.
|
||||
* These are useful if the 68k accesses a memory-mapped port on another device
|
||||
* that requires immediate processing by another CPU.
|
||||
*/
|
||||
int m68k_cycles_run(void); /* Number of cycles run so far */
|
||||
int m68k_cycles_remaining(void); /* Number of cycles left */
|
||||
void m68k_modify_timeslice(int cycles); /* Modify cycles left */
|
||||
void m68k_end_timeslice(void); /* End timeslice now */
|
||||
|
||||
/* Set the IPL0-IPL2 pins on the CPU (IRQ).
|
||||
* A transition from < 7 to 7 will cause a non-maskable interrupt (NMI).
|
||||
* Setting IRQ to 0 will clear an interrupt request.
|
||||
*/
|
||||
void m68k_set_irq(unsigned int int_level);
|
||||
|
||||
|
||||
/* Halt the CPU as if you pulsed the HALT pin. */
|
||||
void m68k_pulse_halt(void);
|
||||
|
||||
|
||||
/* Context switching to allow multiple CPUs */
|
||||
|
||||
/* Get the size of the cpu context in bytes */
|
||||
unsigned int m68k_context_size(void);
|
||||
|
||||
/* Get a cpu context */
|
||||
unsigned int m68k_get_context(void* dst);
|
||||
|
||||
/* set the current cpu context */
|
||||
void m68k_set_context(void* dst);
|
||||
|
||||
/* Register the CPU state information */
|
||||
void m68k_state_register(const char *type);
|
||||
|
||||
|
||||
/* Peek at the internals of a CPU context. This can either be a context
|
||||
* retrieved using m68k_get_context() or the currently running context.
|
||||
* If context is NULL, the currently running CPU context will be used.
|
||||
*/
|
||||
unsigned int m68k_get_reg(void* context, m68k_register_t reg);
|
||||
|
||||
/* Poke values into the internals of the currently running CPU context */
|
||||
void m68k_set_reg(m68k_register_t reg, unsigned int value);
|
||||
|
||||
/* Check if an instruction is valid for the specified CPU type */
|
||||
unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type);
|
||||
|
||||
/* Disassemble 1 instruction using the epecified CPU type at pc. Stores
|
||||
* disassembly in str_buff and returns the size of the instruction in bytes.
|
||||
*/
|
||||
unsigned int m68k_disassemble(char* str_buff, unsigned int pc, unsigned int cpu_type);
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== MAME STUFF ============================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
#if M68K_COMPILE_FOR_MAME == OPT_ON
|
||||
#include "m68kmame.h"
|
||||
#endif /* M68K_COMPILE_FOR_MAME */
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== END OF FILE ============================= */
|
||||
/* ======================================================================== */
|
||||
|
||||
#endif /* M68K__HEADER */
|
10375
plat/linux68k/emu/musashi/m68k_in.c
Executable file
10375
plat/linux68k/emu/musashi/m68k_in.c
Executable file
File diff suppressed because it is too large
Load diff
187
plat/linux68k/emu/musashi/m68kconf.h
Executable file
187
plat/linux68k/emu/musashi/m68kconf.h
Executable file
|
@ -0,0 +1,187 @@
|
|||
/* ======================================================================== */
|
||||
/* ========================= LICENSING & COPYRIGHT ======================== */
|
||||
/* ======================================================================== */
|
||||
/*
|
||||
* MUSASHI
|
||||
* Version 3.4
|
||||
*
|
||||
* A portable Motorola M680x0 processor emulation engine.
|
||||
* Copyright 1998-2001 Karl Stenerud. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef M68KCONF__HEADER
|
||||
#define M68KCONF__HEADER
|
||||
|
||||
|
||||
/* Configuration switches.
|
||||
* Use OPT_SPECIFY_HANDLER for configuration options that allow callbacks.
|
||||
* OPT_SPECIFY_HANDLER causes the core to link directly to the function
|
||||
* or macro you specify, rather than using callback functions whose pointer
|
||||
* must be passed in using m68k_set_xxx_callback().
|
||||
*/
|
||||
#define OPT_OFF 0
|
||||
#define OPT_ON 1
|
||||
#define OPT_SPECIFY_HANDLER 2
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== MAME STUFF ============================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* If you're compiling this for MAME, only change M68K_COMPILE_FOR_MAME
|
||||
* to OPT_ON and use m68kmame.h to configure the 68k core.
|
||||
*/
|
||||
#ifndef M68K_COMPILE_FOR_MAME
|
||||
#define M68K_COMPILE_FOR_MAME OPT_ON
|
||||
#endif /* M68K_COMPILE_FOR_MAME */
|
||||
|
||||
|
||||
#if M68K_COMPILE_FOR_MAME == OPT_OFF
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================= CONFIGURATION ============================ */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* Turn ON if you want to use the following M68K variants */
|
||||
#define M68K_EMULATE_010 OPT_ON
|
||||
#define M68K_EMULATE_EC020 OPT_ON
|
||||
#define M68K_EMULATE_020 OPT_ON
|
||||
|
||||
|
||||
/* If ON, the CPU will call m68k_read_immediate_xx() for immediate addressing
|
||||
* and m68k_read_pcrelative_xx() for PC-relative addressing.
|
||||
* If off, all read requests from the CPU will be redirected to m68k_read_xx()
|
||||
*/
|
||||
#define M68K_SEPARATE_READS OPT_OFF
|
||||
|
||||
/* If ON, the CPU will call m68k_write_32_pd() when it executes move.l with a
|
||||
* predecrement destination EA mode instead of m68k_write_32().
|
||||
* To simulate real 68k behavior, m68k_write_32_pd() must first write the high
|
||||
* word to [address+2], and then write the low word to [address].
|
||||
*/
|
||||
#define M68K_SIMULATE_PD_WRITES OPT_OFF
|
||||
|
||||
/* If ON, CPU will call the interrupt acknowledge callback when it services an
|
||||
* interrupt.
|
||||
* If off, all interrupts will be autovectored and all interrupt requests will
|
||||
* auto-clear when the interrupt is serviced.
|
||||
*/
|
||||
#define M68K_EMULATE_INT_ACK OPT_OFF
|
||||
#define M68K_INT_ACK_CALLBACK(A) your_int_ack_handler_function(A)
|
||||
|
||||
|
||||
/* If ON, CPU will call the breakpoint acknowledge callback when it encounters
|
||||
* a breakpoint instruction and it is running a 68010+.
|
||||
*/
|
||||
#define M68K_EMULATE_BKPT_ACK OPT_OFF
|
||||
#define M68K_BKPT_ACK_CALLBACK() your_bkpt_ack_handler_function()
|
||||
|
||||
|
||||
/* If ON, the CPU will monitor the trace flags and take trace exceptions
|
||||
*/
|
||||
#define M68K_EMULATE_TRACE OPT_OFF
|
||||
|
||||
|
||||
/* If ON, CPU will call the output reset callback when it encounters a reset
|
||||
* instruction.
|
||||
*/
|
||||
#define M68K_EMULATE_RESET OPT_OFF
|
||||
#define M68K_RESET_CALLBACK() your_reset_handler_function()
|
||||
|
||||
|
||||
/* If ON, CPU will call the set fc callback on every memory access to
|
||||
* differentiate between user/supervisor, program/data access like a real
|
||||
* 68000 would. This should be enabled and the callback should be set if you
|
||||
* want to properly emulate the m68010 or higher. (moves uses function codes
|
||||
* to read/write data from different address spaces)
|
||||
*/
|
||||
#define M68K_EMULATE_FC OPT_OFF
|
||||
#define M68K_SET_FC_CALLBACK(A) your_set_fc_handler_function(A)
|
||||
|
||||
|
||||
/* If ON, CPU will call the pc changed callback when it changes the PC by a
|
||||
* large value. This allows host programs to be nicer when it comes to
|
||||
* fetching immediate data and instructions on a banked memory system.
|
||||
*/
|
||||
#define M68K_MONITOR_PC OPT_OFF
|
||||
#define M68K_SET_PC_CALLBACK(A) your_pc_changed_handler_function(A)
|
||||
|
||||
|
||||
/* If ON, CPU will call the instruction hook callback before every
|
||||
* instruction.
|
||||
*/
|
||||
#define M68K_INSTRUCTION_HOOK OPT_OFF
|
||||
#define M68K_INSTRUCTION_CALLBACK() your_instruction_hook_function()
|
||||
|
||||
|
||||
/* If ON, the CPU will emulate the 4-byte prefetch queue of a real 68000 */
|
||||
#define M68K_EMULATE_PREFETCH OPT_OFF
|
||||
|
||||
|
||||
/* If ON, the CPU will generate address error exceptions if it tries to
|
||||
* access a word or longword at an odd address.
|
||||
* NOTE: This is only emulated properly for 68000 mode.
|
||||
*/
|
||||
#define M68K_EMULATE_ADDRESS_ERROR OPT_OFF
|
||||
|
||||
|
||||
/* Turn ON to enable logging of illegal instruction calls.
|
||||
* M68K_LOG_FILEHANDLE must be #defined to a stdio file stream.
|
||||
* Turn on M68K_LOG_1010_1111 to log all 1010 and 1111 calls.
|
||||
*/
|
||||
#define M68K_LOG_ENABLE OPT_OFF
|
||||
#define M68K_LOG_1010_1111 OPT_OFF
|
||||
#define M68K_LOG_FILEHANDLE some_file_handle
|
||||
|
||||
|
||||
/* ----------------------------- COMPATIBILITY ---------------------------- */
|
||||
|
||||
/* The following options set optimizations that violate the current ANSI
|
||||
* standard, but will be compliant under the forthcoming C9X standard.
|
||||
*/
|
||||
|
||||
|
||||
/* If ON, the enulation core will use 64-bit integers to speed up some
|
||||
* operations.
|
||||
*/
|
||||
#define M68K_USE_64_BIT OPT_OFF
|
||||
|
||||
|
||||
/* Set to your compiler's static inline keyword to enable it, or
|
||||
* set it to blank to disable it.
|
||||
* If you define INLINE in the makefile, it will override this value.
|
||||
* NOTE: not enabling inline functions will SEVERELY slow down emulation.
|
||||
*/
|
||||
#ifndef INLINE
|
||||
#define INLINE static __inline__
|
||||
#endif /* INLINE */
|
||||
|
||||
#endif /* M68K_COMPILE_FOR_MAME */
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== END OF FILE ============================= */
|
||||
/* ======================================================================== */
|
||||
|
||||
#endif /* M68KCONF__HEADER */
|
882
plat/linux68k/emu/musashi/m68kcpu.c
Executable file
882
plat/linux68k/emu/musashi/m68kcpu.c
Executable file
|
@ -0,0 +1,882 @@
|
|||
/* ======================================================================== */
|
||||
/* ========================= LICENSING & COPYRIGHT ======================== */
|
||||
/* ======================================================================== */
|
||||
/*
|
||||
* MUSASHI
|
||||
* Version 3.4
|
||||
*
|
||||
* A portable Motorola M680x0 processor emulation engine.
|
||||
* Copyright 1998-2001 Karl Stenerud. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ================================= NOTES ================================ */
|
||||
/* ======================================================================== */
|
||||
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ================================ INCLUDES ============================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
#include "m68kops.h"
|
||||
#include "m68kcpu.h"
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ================================= DATA ================================= */
|
||||
/* ======================================================================== */
|
||||
|
||||
int m68ki_initial_cycles;
|
||||
int m68ki_remaining_cycles = 0; /* Number of clocks remaining */
|
||||
uint m68ki_tracing = 0;
|
||||
uint m68ki_address_space;
|
||||
|
||||
#ifdef M68K_LOG_ENABLE
|
||||
char* m68ki_cpu_names[9] =
|
||||
{
|
||||
"Invalid CPU",
|
||||
"M68000",
|
||||
"M68010",
|
||||
"Invalid CPU",
|
||||
"M68EC020"
|
||||
"Invalid CPU",
|
||||
"Invalid CPU",
|
||||
"Invalid CPU",
|
||||
"M68020"
|
||||
};
|
||||
#endif /* M68K_LOG_ENABLE */
|
||||
|
||||
/* The CPU core */
|
||||
m68ki_cpu_core m68ki_cpu = {0};
|
||||
|
||||
#if M68K_EMULATE_ADDRESS_ERROR
|
||||
jmp_buf m68ki_aerr_trap;
|
||||
#endif /* M68K_EMULATE_ADDRESS_ERROR */
|
||||
|
||||
uint m68ki_aerr_address;
|
||||
uint m68ki_aerr_write_mode;
|
||||
uint m68ki_aerr_fc;
|
||||
|
||||
/* Used by shift & rotate instructions */
|
||||
uint8 m68ki_shift_8_table[65] =
|
||||
{
|
||||
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
uint16 m68ki_shift_16_table[65] =
|
||||
{
|
||||
0x0000, 0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
|
||||
0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff, 0xffff,
|
||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
||||
0xffff, 0xffff
|
||||
};
|
||||
uint m68ki_shift_32_table[65] =
|
||||
{
|
||||
0x00000000, 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, 0xf8000000,
|
||||
0xfc000000, 0xfe000000, 0xff000000, 0xff800000, 0xffc00000, 0xffe00000,
|
||||
0xfff00000, 0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000, 0xffff8000,
|
||||
0xffffc000, 0xffffe000, 0xfffff000, 0xfffff800, 0xfffffc00, 0xfffffe00,
|
||||
0xffffff00, 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0, 0xfffffff8,
|
||||
0xfffffffc, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
|
||||
};
|
||||
|
||||
|
||||
/* Number of clock cycles to use for exception processing.
|
||||
* I used 4 for any vectors that are undocumented for processing times.
|
||||
*/
|
||||
uint8 m68ki_exception_cycle_table[3][256] =
|
||||
{
|
||||
{ /* 000 */
|
||||
4, /* 0: Reset - Initial Stack Pointer */
|
||||
4, /* 1: Reset - Initial Program Counter */
|
||||
50, /* 2: Bus Error (unemulated) */
|
||||
50, /* 3: Address Error (unemulated) */
|
||||
34, /* 4: Illegal Instruction */
|
||||
38, /* 5: Divide by Zero -- ASG: changed from 42 */
|
||||
40, /* 6: CHK -- ASG: chanaged from 44 */
|
||||
34, /* 7: TRAPV */
|
||||
34, /* 8: Privilege Violation */
|
||||
34, /* 9: Trace */
|
||||
34, /* 10: 1010 */
|
||||
34, /* 11: 1111 */
|
||||
4, /* 12: RESERVED */
|
||||
4, /* 13: Coprocessor Protocol Violation (unemulated) */
|
||||
4, /* 14: Format Error */
|
||||
44, /* 15: Uninitialized Interrupt */
|
||||
4, /* 16: RESERVED */
|
||||
4, /* 17: RESERVED */
|
||||
4, /* 18: RESERVED */
|
||||
4, /* 19: RESERVED */
|
||||
4, /* 20: RESERVED */
|
||||
4, /* 21: RESERVED */
|
||||
4, /* 22: RESERVED */
|
||||
4, /* 23: RESERVED */
|
||||
44, /* 24: Spurious Interrupt */
|
||||
44, /* 25: Level 1 Interrupt Autovector */
|
||||
44, /* 26: Level 2 Interrupt Autovector */
|
||||
44, /* 27: Level 3 Interrupt Autovector */
|
||||
44, /* 28: Level 4 Interrupt Autovector */
|
||||
44, /* 29: Level 5 Interrupt Autovector */
|
||||
44, /* 30: Level 6 Interrupt Autovector */
|
||||
44, /* 31: Level 7 Interrupt Autovector */
|
||||
34, /* 32: TRAP #0 -- ASG: chanaged from 38 */
|
||||
34, /* 33: TRAP #1 */
|
||||
34, /* 34: TRAP #2 */
|
||||
34, /* 35: TRAP #3 */
|
||||
34, /* 36: TRAP #4 */
|
||||
34, /* 37: TRAP #5 */
|
||||
34, /* 38: TRAP #6 */
|
||||
34, /* 39: TRAP #7 */
|
||||
34, /* 40: TRAP #8 */
|
||||
34, /* 41: TRAP #9 */
|
||||
34, /* 42: TRAP #10 */
|
||||
34, /* 43: TRAP #11 */
|
||||
34, /* 44: TRAP #12 */
|
||||
34, /* 45: TRAP #13 */
|
||||
34, /* 46: TRAP #14 */
|
||||
34, /* 47: TRAP #15 */
|
||||
4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */
|
||||
4, /* 49: FP Inexact Result (unemulated) */
|
||||
4, /* 50: FP Divide by Zero (unemulated) */
|
||||
4, /* 51: FP Underflow (unemulated) */
|
||||
4, /* 52: FP Operand Error (unemulated) */
|
||||
4, /* 53: FP Overflow (unemulated) */
|
||||
4, /* 54: FP Signaling NAN (unemulated) */
|
||||
4, /* 55: FP Unimplemented Data Type (unemulated) */
|
||||
4, /* 56: MMU Configuration Error (unemulated) */
|
||||
4, /* 57: MMU Illegal Operation Error (unemulated) */
|
||||
4, /* 58: MMU Access Level Violation Error (unemulated) */
|
||||
4, /* 59: RESERVED */
|
||||
4, /* 60: RESERVED */
|
||||
4, /* 61: RESERVED */
|
||||
4, /* 62: RESERVED */
|
||||
4, /* 63: RESERVED */
|
||||
/* 64-255: User Defined */
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
|
||||
},
|
||||
{ /* 010 */
|
||||
4, /* 0: Reset - Initial Stack Pointer */
|
||||
4, /* 1: Reset - Initial Program Counter */
|
||||
126, /* 2: Bus Error (unemulated) */
|
||||
126, /* 3: Address Error (unemulated) */
|
||||
38, /* 4: Illegal Instruction */
|
||||
44, /* 5: Divide by Zero */
|
||||
44, /* 6: CHK */
|
||||
34, /* 7: TRAPV */
|
||||
38, /* 8: Privilege Violation */
|
||||
38, /* 9: Trace */
|
||||
4, /* 10: 1010 */
|
||||
4, /* 11: 1111 */
|
||||
4, /* 12: RESERVED */
|
||||
4, /* 13: Coprocessor Protocol Violation (unemulated) */
|
||||
4, /* 14: Format Error */
|
||||
44, /* 15: Uninitialized Interrupt */
|
||||
4, /* 16: RESERVED */
|
||||
4, /* 17: RESERVED */
|
||||
4, /* 18: RESERVED */
|
||||
4, /* 19: RESERVED */
|
||||
4, /* 20: RESERVED */
|
||||
4, /* 21: RESERVED */
|
||||
4, /* 22: RESERVED */
|
||||
4, /* 23: RESERVED */
|
||||
46, /* 24: Spurious Interrupt */
|
||||
46, /* 25: Level 1 Interrupt Autovector */
|
||||
46, /* 26: Level 2 Interrupt Autovector */
|
||||
46, /* 27: Level 3 Interrupt Autovector */
|
||||
46, /* 28: Level 4 Interrupt Autovector */
|
||||
46, /* 29: Level 5 Interrupt Autovector */
|
||||
46, /* 30: Level 6 Interrupt Autovector */
|
||||
46, /* 31: Level 7 Interrupt Autovector */
|
||||
38, /* 32: TRAP #0 */
|
||||
38, /* 33: TRAP #1 */
|
||||
38, /* 34: TRAP #2 */
|
||||
38, /* 35: TRAP #3 */
|
||||
38, /* 36: TRAP #4 */
|
||||
38, /* 37: TRAP #5 */
|
||||
38, /* 38: TRAP #6 */
|
||||
38, /* 39: TRAP #7 */
|
||||
38, /* 40: TRAP #8 */
|
||||
38, /* 41: TRAP #9 */
|
||||
38, /* 42: TRAP #10 */
|
||||
38, /* 43: TRAP #11 */
|
||||
38, /* 44: TRAP #12 */
|
||||
38, /* 45: TRAP #13 */
|
||||
38, /* 46: TRAP #14 */
|
||||
38, /* 47: TRAP #15 */
|
||||
4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */
|
||||
4, /* 49: FP Inexact Result (unemulated) */
|
||||
4, /* 50: FP Divide by Zero (unemulated) */
|
||||
4, /* 51: FP Underflow (unemulated) */
|
||||
4, /* 52: FP Operand Error (unemulated) */
|
||||
4, /* 53: FP Overflow (unemulated) */
|
||||
4, /* 54: FP Signaling NAN (unemulated) */
|
||||
4, /* 55: FP Unimplemented Data Type (unemulated) */
|
||||
4, /* 56: MMU Configuration Error (unemulated) */
|
||||
4, /* 57: MMU Illegal Operation Error (unemulated) */
|
||||
4, /* 58: MMU Access Level Violation Error (unemulated) */
|
||||
4, /* 59: RESERVED */
|
||||
4, /* 60: RESERVED */
|
||||
4, /* 61: RESERVED */
|
||||
4, /* 62: RESERVED */
|
||||
4, /* 63: RESERVED */
|
||||
/* 64-255: User Defined */
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
|
||||
},
|
||||
{ /* 020 */
|
||||
4, /* 0: Reset - Initial Stack Pointer */
|
||||
4, /* 1: Reset - Initial Program Counter */
|
||||
50, /* 2: Bus Error (unemulated) */
|
||||
50, /* 3: Address Error (unemulated) */
|
||||
20, /* 4: Illegal Instruction */
|
||||
38, /* 5: Divide by Zero */
|
||||
40, /* 6: CHK */
|
||||
20, /* 7: TRAPV */
|
||||
34, /* 8: Privilege Violation */
|
||||
25, /* 9: Trace */
|
||||
20, /* 10: 1010 */
|
||||
20, /* 11: 1111 */
|
||||
4, /* 12: RESERVED */
|
||||
4, /* 13: Coprocessor Protocol Violation (unemulated) */
|
||||
4, /* 14: Format Error */
|
||||
30, /* 15: Uninitialized Interrupt */
|
||||
4, /* 16: RESERVED */
|
||||
4, /* 17: RESERVED */
|
||||
4, /* 18: RESERVED */
|
||||
4, /* 19: RESERVED */
|
||||
4, /* 20: RESERVED */
|
||||
4, /* 21: RESERVED */
|
||||
4, /* 22: RESERVED */
|
||||
4, /* 23: RESERVED */
|
||||
30, /* 24: Spurious Interrupt */
|
||||
30, /* 25: Level 1 Interrupt Autovector */
|
||||
30, /* 26: Level 2 Interrupt Autovector */
|
||||
30, /* 27: Level 3 Interrupt Autovector */
|
||||
30, /* 28: Level 4 Interrupt Autovector */
|
||||
30, /* 29: Level 5 Interrupt Autovector */
|
||||
30, /* 30: Level 6 Interrupt Autovector */
|
||||
30, /* 31: Level 7 Interrupt Autovector */
|
||||
20, /* 32: TRAP #0 */
|
||||
20, /* 33: TRAP #1 */
|
||||
20, /* 34: TRAP #2 */
|
||||
20, /* 35: TRAP #3 */
|
||||
20, /* 36: TRAP #4 */
|
||||
20, /* 37: TRAP #5 */
|
||||
20, /* 38: TRAP #6 */
|
||||
20, /* 39: TRAP #7 */
|
||||
20, /* 40: TRAP #8 */
|
||||
20, /* 41: TRAP #9 */
|
||||
20, /* 42: TRAP #10 */
|
||||
20, /* 43: TRAP #11 */
|
||||
20, /* 44: TRAP #12 */
|
||||
20, /* 45: TRAP #13 */
|
||||
20, /* 46: TRAP #14 */
|
||||
20, /* 47: TRAP #15 */
|
||||
4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */
|
||||
4, /* 49: FP Inexact Result (unemulated) */
|
||||
4, /* 50: FP Divide by Zero (unemulated) */
|
||||
4, /* 51: FP Underflow (unemulated) */
|
||||
4, /* 52: FP Operand Error (unemulated) */
|
||||
4, /* 53: FP Overflow (unemulated) */
|
||||
4, /* 54: FP Signaling NAN (unemulated) */
|
||||
4, /* 55: FP Unimplemented Data Type (unemulated) */
|
||||
4, /* 56: MMU Configuration Error (unemulated) */
|
||||
4, /* 57: MMU Illegal Operation Error (unemulated) */
|
||||
4, /* 58: MMU Access Level Violation Error (unemulated) */
|
||||
4, /* 59: RESERVED */
|
||||
4, /* 60: RESERVED */
|
||||
4, /* 61: RESERVED */
|
||||
4, /* 62: RESERVED */
|
||||
4, /* 63: RESERVED */
|
||||
/* 64-255: User Defined */
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
|
||||
}
|
||||
};
|
||||
|
||||
uint8 m68ki_ea_idx_cycle_table[64] =
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, /* ..01.000 no memory indirect, base NULL */
|
||||
5, /* ..01..01 memory indirect, base NULL, outer NULL */
|
||||
7, /* ..01..10 memory indirect, base NULL, outer 16 */
|
||||
7, /* ..01..11 memory indirect, base NULL, outer 32 */
|
||||
0, 5, 7, 7, 0, 5, 7, 7, 0, 5, 7, 7,
|
||||
2, /* ..10.000 no memory indirect, base 16 */
|
||||
7, /* ..10..01 memory indirect, base 16, outer NULL */
|
||||
9, /* ..10..10 memory indirect, base 16, outer 16 */
|
||||
9, /* ..10..11 memory indirect, base 16, outer 32 */
|
||||
0, 7, 9, 9, 0, 7, 9, 9, 0, 7, 9, 9,
|
||||
6, /* ..11.000 no memory indirect, base 32 */
|
||||
11, /* ..11..01 memory indirect, base 32, outer NULL */
|
||||
13, /* ..11..10 memory indirect, base 32, outer 16 */
|
||||
13, /* ..11..11 memory indirect, base 32, outer 32 */
|
||||
0, 11, 13, 13, 0, 11, 13, 13, 0, 11, 13, 13
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* =============================== CALLBACKS ============================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* Default callbacks used if the callback hasn't been set yet, or if the
|
||||
* callback is set to NULL
|
||||
*/
|
||||
|
||||
/* Interrupt acknowledge */
|
||||
static int default_int_ack_callback_data;
|
||||
static int default_int_ack_callback(int int_level)
|
||||
{
|
||||
default_int_ack_callback_data = int_level;
|
||||
CPU_INT_LEVEL = 0;
|
||||
return M68K_INT_ACK_AUTOVECTOR;
|
||||
}
|
||||
|
||||
/* Breakpoint acknowledge */
|
||||
static unsigned int default_bkpt_ack_callback_data;
|
||||
static void default_bkpt_ack_callback(unsigned int data)
|
||||
{
|
||||
default_bkpt_ack_callback_data = data;
|
||||
}
|
||||
|
||||
/* Called when a reset instruction is executed */
|
||||
static void default_reset_instr_callback(void)
|
||||
{
|
||||
}
|
||||
|
||||
/* Called when the program counter changed by a large value */
|
||||
static unsigned int default_pc_changed_callback_data;
|
||||
static void default_pc_changed_callback(unsigned int new_pc)
|
||||
{
|
||||
default_pc_changed_callback_data = new_pc;
|
||||
}
|
||||
|
||||
/* Called every time there's bus activity (read/write to/from memory */
|
||||
static unsigned int default_set_fc_callback_data;
|
||||
static void default_set_fc_callback(unsigned int new_fc)
|
||||
{
|
||||
default_set_fc_callback_data = new_fc;
|
||||
}
|
||||
|
||||
/* Called every instruction cycle prior to execution */
|
||||
static void default_instr_hook_callback(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#if M68K_EMULATE_ADDRESS_ERROR
|
||||
#include <setjmp.h>
|
||||
jmp_buf m68ki_aerr_trap;
|
||||
#endif /* M68K_EMULATE_ADDRESS_ERROR */
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ================================= API ================================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* Access the internals of the CPU */
|
||||
unsigned int m68k_get_reg(void* context, m68k_register_t regnum)
|
||||
{
|
||||
m68ki_cpu_core* cpu = context != NULL ?(m68ki_cpu_core*)context : &m68ki_cpu;
|
||||
|
||||
switch(regnum)
|
||||
{
|
||||
case M68K_REG_D0: return cpu->dar[0];
|
||||
case M68K_REG_D1: return cpu->dar[1];
|
||||
case M68K_REG_D2: return cpu->dar[2];
|
||||
case M68K_REG_D3: return cpu->dar[3];
|
||||
case M68K_REG_D4: return cpu->dar[4];
|
||||
case M68K_REG_D5: return cpu->dar[5];
|
||||
case M68K_REG_D6: return cpu->dar[6];
|
||||
case M68K_REG_D7: return cpu->dar[7];
|
||||
case M68K_REG_A0: return cpu->dar[8];
|
||||
case M68K_REG_A1: return cpu->dar[9];
|
||||
case M68K_REG_A2: return cpu->dar[10];
|
||||
case M68K_REG_A3: return cpu->dar[11];
|
||||
case M68K_REG_A4: return cpu->dar[12];
|
||||
case M68K_REG_A5: return cpu->dar[13];
|
||||
case M68K_REG_A6: return cpu->dar[14];
|
||||
case M68K_REG_A7: return cpu->dar[15];
|
||||
case M68K_REG_PC: return MASK_OUT_ABOVE_32(cpu->pc);
|
||||
case M68K_REG_SR: return cpu->t1_flag |
|
||||
cpu->t0_flag |
|
||||
(cpu->s_flag << 11) |
|
||||
(cpu->m_flag << 11) |
|
||||
cpu->int_mask |
|
||||
((cpu->x_flag & XFLAG_SET) >> 4) |
|
||||
((cpu->n_flag & NFLAG_SET) >> 4) |
|
||||
((!cpu->not_z_flag) << 2) |
|
||||
((cpu->v_flag & VFLAG_SET) >> 6) |
|
||||
((cpu->c_flag & CFLAG_SET) >> 8);
|
||||
case M68K_REG_SP: return cpu->dar[15];
|
||||
case M68K_REG_USP: return cpu->s_flag ? cpu->sp[0] : cpu->dar[15];
|
||||
case M68K_REG_ISP: return cpu->s_flag && !cpu->m_flag ? cpu->dar[15] : cpu->sp[4];
|
||||
case M68K_REG_MSP: return cpu->s_flag && cpu->m_flag ? cpu->dar[15] : cpu->sp[6];
|
||||
case M68K_REG_SFC: return cpu->sfc;
|
||||
case M68K_REG_DFC: return cpu->dfc;
|
||||
case M68K_REG_VBR: return cpu->vbr;
|
||||
case M68K_REG_CACR: return cpu->cacr;
|
||||
case M68K_REG_CAAR: return cpu->caar;
|
||||
case M68K_REG_PREF_ADDR: return cpu->pref_addr;
|
||||
case M68K_REG_PREF_DATA: return cpu->pref_data;
|
||||
case M68K_REG_PPC: return MASK_OUT_ABOVE_32(cpu->ppc);
|
||||
case M68K_REG_IR: return cpu->ir;
|
||||
case M68K_REG_CPU_TYPE:
|
||||
switch(cpu->cpu_type)
|
||||
{
|
||||
case CPU_TYPE_000: return (unsigned int)M68K_CPU_TYPE_68000;
|
||||
case CPU_TYPE_010: return (unsigned int)M68K_CPU_TYPE_68010;
|
||||
case CPU_TYPE_EC020: return (unsigned int)M68K_CPU_TYPE_68EC020;
|
||||
case CPU_TYPE_020: return (unsigned int)M68K_CPU_TYPE_68020;
|
||||
}
|
||||
return M68K_CPU_TYPE_INVALID;
|
||||
default: return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void m68k_set_reg(m68k_register_t regnum, unsigned int value)
|
||||
{
|
||||
switch(regnum)
|
||||
{
|
||||
case M68K_REG_D0: REG_D[0] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_D1: REG_D[1] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_D2: REG_D[2] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_D3: REG_D[3] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_D4: REG_D[4] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_D5: REG_D[5] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_D6: REG_D[6] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_D7: REG_D[7] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_A0: REG_A[0] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_A1: REG_A[1] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_A2: REG_A[2] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_A3: REG_A[3] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_A4: REG_A[4] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_A5: REG_A[5] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_A6: REG_A[6] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_A7: REG_A[7] = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_PC: m68ki_jump(MASK_OUT_ABOVE_32(value)); return;
|
||||
case M68K_REG_SR: m68ki_set_sr(value); return;
|
||||
case M68K_REG_SP: REG_SP = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_USP: if(FLAG_S)
|
||||
REG_USP = MASK_OUT_ABOVE_32(value);
|
||||
else
|
||||
REG_SP = MASK_OUT_ABOVE_32(value);
|
||||
return;
|
||||
case M68K_REG_ISP: if(FLAG_S && !FLAG_M)
|
||||
REG_SP = MASK_OUT_ABOVE_32(value);
|
||||
else
|
||||
REG_ISP = MASK_OUT_ABOVE_32(value);
|
||||
return;
|
||||
case M68K_REG_MSP: if(FLAG_S && FLAG_M)
|
||||
REG_SP = MASK_OUT_ABOVE_32(value);
|
||||
else
|
||||
REG_MSP = MASK_OUT_ABOVE_32(value);
|
||||
return;
|
||||
case M68K_REG_VBR: REG_VBR = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_SFC: REG_SFC = value & 7; return;
|
||||
case M68K_REG_DFC: REG_DFC = value & 7; return;
|
||||
case M68K_REG_CACR: REG_CACR = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_CAAR: REG_CAAR = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_PPC: REG_PPC = MASK_OUT_ABOVE_32(value); return;
|
||||
case M68K_REG_IR: REG_IR = MASK_OUT_ABOVE_16(value); return;
|
||||
case M68K_REG_CPU_TYPE: m68k_set_cpu_type(value); return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the callbacks */
|
||||
void m68k_set_int_ack_callback(int (*callback)(int int_level))
|
||||
{
|
||||
CALLBACK_INT_ACK = callback ? callback : default_int_ack_callback;
|
||||
}
|
||||
|
||||
void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data))
|
||||
{
|
||||
CALLBACK_BKPT_ACK = callback ? callback : default_bkpt_ack_callback;
|
||||
}
|
||||
|
||||
void m68k_set_reset_instr_callback(void (*callback)(void))
|
||||
{
|
||||
CALLBACK_RESET_INSTR = callback ? callback : default_reset_instr_callback;
|
||||
}
|
||||
|
||||
void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc))
|
||||
{
|
||||
CALLBACK_PC_CHANGED = callback ? callback : default_pc_changed_callback;
|
||||
}
|
||||
|
||||
void m68k_set_fc_callback(void (*callback)(unsigned int new_fc))
|
||||
{
|
||||
CALLBACK_SET_FC = callback ? callback : default_set_fc_callback;
|
||||
}
|
||||
|
||||
void m68k_set_instr_hook_callback(void (*callback)(void))
|
||||
{
|
||||
CALLBACK_INSTR_HOOK = callback ? callback : default_instr_hook_callback;
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
/* Set the CPU type. */
|
||||
void m68k_set_cpu_type(unsigned int cpu_type)
|
||||
{
|
||||
switch(cpu_type)
|
||||
{
|
||||
case M68K_CPU_TYPE_68000:
|
||||
CPU_TYPE = CPU_TYPE_000;
|
||||
CPU_ADDRESS_MASK = 0x00ffffff;
|
||||
CPU_SR_MASK = 0xa71f; /* T1 -- S -- -- I2 I1 I0 -- -- -- X N Z V C */
|
||||
CYC_INSTRUCTION = m68ki_cycles[0];
|
||||
CYC_EXCEPTION = m68ki_exception_cycle_table[0];
|
||||
CYC_BCC_NOTAKE_B = -2;
|
||||
CYC_BCC_NOTAKE_W = 2;
|
||||
CYC_DBCC_F_NOEXP = -2;
|
||||
CYC_DBCC_F_EXP = 2;
|
||||
CYC_SCC_R_TRUE = 2;
|
||||
CYC_MOVEM_W = 2;
|
||||
CYC_MOVEM_L = 3;
|
||||
CYC_SHIFT = 1;
|
||||
CYC_RESET = 132;
|
||||
return;
|
||||
case M68K_CPU_TYPE_68010:
|
||||
CPU_TYPE = CPU_TYPE_010;
|
||||
CPU_ADDRESS_MASK = 0x00ffffff;
|
||||
CPU_SR_MASK = 0xa71f; /* T1 -- S -- -- I2 I1 I0 -- -- -- X N Z V C */
|
||||
CYC_INSTRUCTION = m68ki_cycles[1];
|
||||
CYC_EXCEPTION = m68ki_exception_cycle_table[1];
|
||||
CYC_BCC_NOTAKE_B = -4;
|
||||
CYC_BCC_NOTAKE_W = 0;
|
||||
CYC_DBCC_F_NOEXP = 0;
|
||||
CYC_DBCC_F_EXP = 6;
|
||||
CYC_SCC_R_TRUE = 0;
|
||||
CYC_MOVEM_W = 2;
|
||||
CYC_MOVEM_L = 3;
|
||||
CYC_SHIFT = 1;
|
||||
CYC_RESET = 130;
|
||||
return;
|
||||
case M68K_CPU_TYPE_68EC020:
|
||||
CPU_TYPE = CPU_TYPE_EC020;
|
||||
CPU_ADDRESS_MASK = 0x00ffffff;
|
||||
CPU_SR_MASK = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */
|
||||
CYC_INSTRUCTION = m68ki_cycles[2];
|
||||
CYC_EXCEPTION = m68ki_exception_cycle_table[2];
|
||||
CYC_BCC_NOTAKE_B = -2;
|
||||
CYC_BCC_NOTAKE_W = 0;
|
||||
CYC_DBCC_F_NOEXP = 0;
|
||||
CYC_DBCC_F_EXP = 4;
|
||||
CYC_SCC_R_TRUE = 0;
|
||||
CYC_MOVEM_W = 2;
|
||||
CYC_MOVEM_L = 2;
|
||||
CYC_SHIFT = 0;
|
||||
CYC_RESET = 518;
|
||||
return;
|
||||
case M68K_CPU_TYPE_68020:
|
||||
CPU_TYPE = CPU_TYPE_020;
|
||||
CPU_ADDRESS_MASK = 0xffffffff;
|
||||
CPU_SR_MASK = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */
|
||||
CYC_INSTRUCTION = m68ki_cycles[2];
|
||||
CYC_EXCEPTION = m68ki_exception_cycle_table[2];
|
||||
CYC_BCC_NOTAKE_B = -2;
|
||||
CYC_BCC_NOTAKE_W = 0;
|
||||
CYC_DBCC_F_NOEXP = 0;
|
||||
CYC_DBCC_F_EXP = 4;
|
||||
CYC_SCC_R_TRUE = 0;
|
||||
CYC_MOVEM_W = 2;
|
||||
CYC_MOVEM_L = 2;
|
||||
CYC_SHIFT = 0;
|
||||
CYC_RESET = 518;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute some instructions until we use up num_cycles clock cycles */
|
||||
/* ASG: removed per-instruction interrupt checks */
|
||||
int m68k_execute(int num_cycles)
|
||||
{
|
||||
/* Make sure we're not stopped */
|
||||
if(!CPU_STOPPED)
|
||||
{
|
||||
/* Set our pool of clock cycles available */
|
||||
SET_CYCLES(num_cycles);
|
||||
m68ki_initial_cycles = num_cycles;
|
||||
|
||||
/* ASG: update cycles */
|
||||
USE_CYCLES(CPU_INT_CYCLES);
|
||||
CPU_INT_CYCLES = 0;
|
||||
|
||||
/* Return point if we had an address error */
|
||||
m68ki_set_address_error_trap(); /* auto-disable (see m68kcpu.h) */
|
||||
|
||||
/* Main loop. Keep going until we run out of clock cycles */
|
||||
do
|
||||
{
|
||||
/* Set tracing accodring to T1. (T0 is done inside instruction) */
|
||||
m68ki_trace_t1(); /* auto-disable (see m68kcpu.h) */
|
||||
|
||||
/* Set the address space for reads */
|
||||
m68ki_use_data_space(); /* auto-disable (see m68kcpu.h) */
|
||||
|
||||
/* Call external hook to peek at CPU */
|
||||
m68ki_instr_hook(); /* auto-disable (see m68kcpu.h) */
|
||||
|
||||
/* Record previous program counter */
|
||||
REG_PPC = REG_PC;
|
||||
|
||||
/* Read an instruction and call its handler */
|
||||
REG_IR = m68ki_read_imm_16();
|
||||
m68ki_instruction_jump_table[REG_IR]();
|
||||
USE_CYCLES(CYC_INSTRUCTION[REG_IR]);
|
||||
|
||||
/* Trace m68k_exception, if necessary */
|
||||
m68ki_exception_if_trace(); /* auto-disable (see m68kcpu.h) */
|
||||
} while(GET_CYCLES() > 0);
|
||||
|
||||
/* set previous PC to current PC for the next entry into the loop */
|
||||
REG_PPC = REG_PC;
|
||||
|
||||
/* ASG: update cycles */
|
||||
USE_CYCLES(CPU_INT_CYCLES);
|
||||
CPU_INT_CYCLES = 0;
|
||||
|
||||
/* return how many clocks we used */
|
||||
return m68ki_initial_cycles - GET_CYCLES();
|
||||
}
|
||||
|
||||
/* We get here if the CPU is stopped or halted */
|
||||
SET_CYCLES(0);
|
||||
CPU_INT_CYCLES = 0;
|
||||
|
||||
return num_cycles;
|
||||
}
|
||||
|
||||
|
||||
int m68k_cycles_run(void)
|
||||
{
|
||||
return m68ki_initial_cycles - GET_CYCLES();
|
||||
}
|
||||
|
||||
int m68k_cycles_remaining(void)
|
||||
{
|
||||
return GET_CYCLES();
|
||||
}
|
||||
|
||||
/* Change the timeslice */
|
||||
void m68k_modify_timeslice(int cycles)
|
||||
{
|
||||
m68ki_initial_cycles += cycles;
|
||||
ADD_CYCLES(cycles);
|
||||
}
|
||||
|
||||
|
||||
void m68k_end_timeslice(void)
|
||||
{
|
||||
m68ki_initial_cycles = GET_CYCLES();
|
||||
SET_CYCLES(0);
|
||||
}
|
||||
|
||||
|
||||
/* ASG: rewrote so that the int_level is a mask of the IPL0/IPL1/IPL2 bits */
|
||||
/* KS: Modified so that IPL* bits match with mask positions in the SR
|
||||
* and cleaned out remenants of the interrupt controller.
|
||||
*/
|
||||
void m68k_set_irq(unsigned int int_level)
|
||||
{
|
||||
uint old_level = CPU_INT_LEVEL;
|
||||
CPU_INT_LEVEL = int_level << 8;
|
||||
|
||||
/* A transition from < 7 to 7 always interrupts (NMI) */
|
||||
/* Note: Level 7 can also level trigger like a normal IRQ */
|
||||
if(old_level != 0x0700 && CPU_INT_LEVEL == 0x0700)
|
||||
m68ki_exception_interrupt(7); /* Edge triggered level 7 (NMI) */
|
||||
else
|
||||
m68ki_check_interrupts(); /* Level triggered (IRQ) */
|
||||
}
|
||||
|
||||
void m68k_init(void)
|
||||
{
|
||||
static uint emulation_initialized = 0;
|
||||
|
||||
/* The first call to this function initializes the opcode handler jump table */
|
||||
if(!emulation_initialized)
|
||||
{
|
||||
m68ki_build_opcode_table();
|
||||
emulation_initialized = 1;
|
||||
}
|
||||
|
||||
m68k_set_int_ack_callback(NULL);
|
||||
m68k_set_bkpt_ack_callback(NULL);
|
||||
m68k_set_reset_instr_callback(NULL);
|
||||
m68k_set_pc_changed_callback(NULL);
|
||||
m68k_set_fc_callback(NULL);
|
||||
m68k_set_instr_hook_callback(NULL);
|
||||
}
|
||||
|
||||
/* Pulse the RESET line on the CPU */
|
||||
void m68k_pulse_reset(void)
|
||||
{
|
||||
/* Clear all stop levels and eat up all remaining cycles */
|
||||
CPU_STOPPED = 0;
|
||||
SET_CYCLES(0);
|
||||
|
||||
CPU_RUN_MODE = RUN_MODE_BERR_AERR_RESET;
|
||||
CPU_INSTR_MODE = INSTRUCTION_YES;
|
||||
|
||||
/* Turn off tracing */
|
||||
FLAG_T1 = FLAG_T0 = 0;
|
||||
m68ki_clear_trace();
|
||||
/* Interrupt mask to level 7 */
|
||||
FLAG_INT_MASK = 0x0700;
|
||||
/* Reset VBR */
|
||||
REG_VBR = 0;
|
||||
/* Go to supervisor mode */
|
||||
m68ki_set_sm_flag(SFLAG_SET | MFLAG_CLEAR);
|
||||
|
||||
/* Invalidate the prefetch queue */
|
||||
#if M68K_EMULATE_PREFETCH
|
||||
/* Set to arbitrary number since our first fetch is from 0 */
|
||||
CPU_PREF_ADDR = 0x1000;
|
||||
#endif /* M68K_EMULATE_PREFETCH */
|
||||
|
||||
/* Read the initial stack pointer and program counter */
|
||||
m68ki_jump(0);
|
||||
REG_SP = m68ki_read_imm_32();
|
||||
REG_PC = m68ki_read_imm_32();
|
||||
m68ki_jump(REG_PC);
|
||||
|
||||
CPU_RUN_MODE = RUN_MODE_NORMAL;
|
||||
}
|
||||
|
||||
/* Pulse the HALT line on the CPU */
|
||||
void m68k_pulse_halt(void)
|
||||
{
|
||||
CPU_STOPPED |= STOP_LEVEL_HALT;
|
||||
}
|
||||
|
||||
|
||||
/* Get and set the current CPU context */
|
||||
/* This is to allow for multiple CPUs */
|
||||
unsigned int m68k_context_size()
|
||||
{
|
||||
return sizeof(m68ki_cpu_core);
|
||||
}
|
||||
|
||||
unsigned int m68k_get_context(void* dst)
|
||||
{
|
||||
if(dst) *(m68ki_cpu_core*)dst = m68ki_cpu;
|
||||
return sizeof(m68ki_cpu_core);
|
||||
}
|
||||
|
||||
void m68k_set_context(void* src)
|
||||
{
|
||||
if(src) m68ki_cpu = *(m68ki_cpu_core*)src;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== MAME STUFF ============================== */
|
||||
/* ======================================================================== */
|
||||
|
||||
#if M68K_COMPILE_FOR_MAME == OPT_ON
|
||||
|
||||
#include "state.h"
|
||||
|
||||
static struct {
|
||||
UINT16 sr;
|
||||
int stopped;
|
||||
int halted;
|
||||
} m68k_substate;
|
||||
|
||||
static void m68k_prepare_substate(void)
|
||||
{
|
||||
m68k_substate.sr = m68ki_get_sr();
|
||||
m68k_substate.stopped = (CPU_STOPPED & STOP_LEVEL_STOP) != 0;
|
||||
m68k_substate.halted = (CPU_STOPPED & STOP_LEVEL_HALT) != 0;
|
||||
}
|
||||
|
||||
static void m68k_post_load(void)
|
||||
{
|
||||
m68ki_set_sr_noint_nosp(m68k_substate.sr);
|
||||
CPU_STOPPED = m68k_substate.stopped ? STOP_LEVEL_STOP : 0
|
||||
| m68k_substate.halted ? STOP_LEVEL_HALT : 0;
|
||||
m68ki_jump(REG_PC);
|
||||
}
|
||||
|
||||
void m68k_state_register(const char *type)
|
||||
{
|
||||
int cpu = cpu_getactivecpu();
|
||||
|
||||
state_save_register_UINT32(type, cpu, "D" , REG_D, 8);
|
||||
state_save_register_UINT32(type, cpu, "A" , REG_A, 8);
|
||||
state_save_register_UINT32(type, cpu, "PPC" , ®_PPC, 1);
|
||||
state_save_register_UINT32(type, cpu, "PC" , ®_PC, 1);
|
||||
state_save_register_UINT32(type, cpu, "USP" , ®_USP, 1);
|
||||
state_save_register_UINT32(type, cpu, "ISP" , ®_ISP, 1);
|
||||
state_save_register_UINT32(type, cpu, "MSP" , ®_MSP, 1);
|
||||
state_save_register_UINT32(type, cpu, "VBR" , ®_VBR, 1);
|
||||
state_save_register_UINT32(type, cpu, "SFC" , ®_SFC, 1);
|
||||
state_save_register_UINT32(type, cpu, "DFC" , ®_DFC, 1);
|
||||
state_save_register_UINT32(type, cpu, "CACR" , ®_CACR, 1);
|
||||
state_save_register_UINT32(type, cpu, "CAAR" , ®_CAAR, 1);
|
||||
state_save_register_UINT16(type, cpu, "SR" , &m68k_substate.sr, 1);
|
||||
state_save_register_UINT32(type, cpu, "INT_LEVEL" , &CPU_INT_LEVEL, 1);
|
||||
state_save_register_UINT32(type, cpu, "INT_CYCLES", &CPU_INT_CYCLES, 1);
|
||||
state_save_register_int (type, cpu, "STOPPED" , &m68k_substate.stopped);
|
||||
state_save_register_int (type, cpu, "HALTED" , &m68k_substate.halted);
|
||||
state_save_register_UINT32(type, cpu, "PREF_ADDR" , &CPU_PREF_ADDR, 1);
|
||||
state_save_register_UINT32(type, cpu, "PREF_DATA" , &CPU_PREF_DATA, 1);
|
||||
state_save_register_func_presave(m68k_prepare_substate);
|
||||
state_save_register_func_postload(m68k_post_load);
|
||||
}
|
||||
|
||||
#endif /* M68K_COMPILE_FOR_MAME */
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ============================== END OF FILE ============================= */
|
||||
/* ======================================================================== */
|
1985
plat/linux68k/emu/musashi/m68kcpu.h
Executable file
1985
plat/linux68k/emu/musashi/m68kcpu.h
Executable file
File diff suppressed because it is too large
Load diff
3477
plat/linux68k/emu/musashi/m68kdasm.c
Executable file
3477
plat/linux68k/emu/musashi/m68kdasm.c
Executable file
File diff suppressed because it is too large
Load diff
1429
plat/linux68k/emu/musashi/m68kmake.c
Executable file
1429
plat/linux68k/emu/musashi/m68kmake.c
Executable file
File diff suppressed because it is too large
Load diff
305
plat/linux68k/emu/musashi/readme.txt
Executable file
305
plat/linux68k/emu/musashi/readme.txt
Executable file
|
@ -0,0 +1,305 @@
|
|||
MUSASHI
|
||||
=======
|
||||
|
||||
Version 3.4
|
||||
|
||||
A portable Motorola M680x0 processor emulation engine.
|
||||
Copyright 1998-2002 Karl Stenerud. All rights reserved.
|
||||
|
||||
|
||||
|
||||
INTRODUCTION:
|
||||
------------
|
||||
|
||||
Musashi is a Motorola 68000, 68010, 68EC020, and 68020 emulator written in C.
|
||||
This emulator was written with two goals in mind: portability and speed.
|
||||
|
||||
The emulator is written to ANSI C89 specifications. It also uses inline
|
||||
functions, which are C9X compliant.
|
||||
|
||||
It has been successfully running in the MAME project (www.mame.net) for years
|
||||
and so has had time to mature.
|
||||
|
||||
|
||||
|
||||
LICENSE AND COPYRIGHT:
|
||||
---------------------
|
||||
|
||||
Copyright © 1998-2001 Karl Stenerud
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
|
||||
AVAILABILITY:
|
||||
------------
|
||||
The latest version of this code can be obtained at:
|
||||
https://github.com/kstenerud/Musashi
|
||||
|
||||
|
||||
|
||||
CONTACTING THE AUTHOR:
|
||||
---------------------
|
||||
I can be reached at kstenerud@gmail.com
|
||||
|
||||
|
||||
|
||||
BASIC CONFIGURATION:
|
||||
-------------------
|
||||
The basic configuration will give you a standard 68000 that has sufficient
|
||||
functionality to work in a primitive environment.
|
||||
|
||||
This setup assumes that you only have 1 device interrupting it, that the
|
||||
device will always request an autovectored interrupt, and it will always clear
|
||||
the interrupt before the interrupt service routine finishes (but could
|
||||
possibly re-assert the interrupt).
|
||||
You will have only one address space, no tracing, and no instruction prefetch.
|
||||
|
||||
To implement the basic configuration:
|
||||
|
||||
- Open m68kconf.h and verify that the settings for INLINE will work with your
|
||||
compiler. (Currently set to "static __inline__", which works in gcc 2.9.
|
||||
For C9X compliance, it should be "inline")
|
||||
|
||||
- In your host program, implement the following functions:
|
||||
unsigned int m68k_read_memory_8(unsigned int address);
|
||||
unsigned int m68k_read_memory_16(unsigned int address);
|
||||
unsigned int m68k_read_memory_32(unsigned int address);
|
||||
void m68k_write_memory_8(unsigned int address, unsigned int value);
|
||||
void m68k_write_memory_16(unsigned int address, unsigned int value);
|
||||
void m68k_write_memory_32(unsigned int address, unsigned int value);
|
||||
|
||||
- In your host program, be sure to call m68k_pulse_reset() once before calling
|
||||
any of the other functions as this initializes the core.
|
||||
|
||||
- Use m68k_execute() to execute instructions and m68k_set_irq() to cause an
|
||||
interrupt.
|
||||
|
||||
|
||||
|
||||
ADDING PROPER INTERRUPT HANDLING:
|
||||
--------------------------------
|
||||
The interrupt handling in the basic configuration doesn't emulate the
|
||||
interrupt acknowledge phase of the CPU and automatically clears an interrupt
|
||||
request during interrupt processing.
|
||||
While this works for most systems, you may need more accurate interrupt
|
||||
handling.
|
||||
|
||||
To add proper interrupt handling:
|
||||
|
||||
- In m68kconf.h, set M68K_EMULATE_INT_ACK to OPT_SPECIFY_HANDLER
|
||||
|
||||
- In m68kconf.h, set M68K_INT_ACK_CALLBACK(A) to your interrupt acknowledge
|
||||
routine
|
||||
|
||||
- Your interrupt acknowledge routine must return an interrupt vector,
|
||||
M68K_INT_ACK_AUTOVECTOR, or M68K_INT_ACK_SPURIOUS. most m68k
|
||||
implementations just use autovectored interrupts.
|
||||
|
||||
- When the interrupting device is satisfied, you must call m68k_set_irq(0) to
|
||||
remove the interrupt request.
|
||||
|
||||
|
||||
|
||||
MULTIPLE INTERRUPTS:
|
||||
-------------------
|
||||
The above system will work if you have only one device interrupting the CPU,
|
||||
but if you have more than one device, you must do a bit more.
|
||||
|
||||
To add multiple interrupts:
|
||||
|
||||
- You must make an interrupt arbitration device that will take the highest
|
||||
priority interrupt and encode it onto the IRQ pins on the CPU.
|
||||
|
||||
- The interrupt arbitration device should use m68k_set_irq() to set the
|
||||
highest pending interrupt, or 0 for no interrupts pending.
|
||||
|
||||
|
||||
|
||||
SEPARATE IMMEDIATE READS:
|
||||
------------------------
|
||||
You can write faster memory access functions if you know whether you are
|
||||
fetching from ROM or RAM. Immediate reads are always from the program space
|
||||
(Always in ROM unless it is running self-modifying code).
|
||||
|
||||
To enable separate immediate reads:
|
||||
|
||||
- In m68kconf.h, turn on M68K_SEPARATE_READ_IMM.
|
||||
|
||||
- In your host program, implement the following functions:
|
||||
unsigned int m68k_read_immediate_16(unsigned int address);
|
||||
unsigned int m68k_read_immediate_32(unsigned int address);
|
||||
|
||||
- If you need to know the current PC (for banking and such), set
|
||||
M68K_MONITOR_PC to OPT_SPECIFY_HANDLER, and set M68K_SET_PC_CALLBACK(A) to
|
||||
your routine.
|
||||
|
||||
|
||||
|
||||
ADDRESS SPACES:
|
||||
--------------
|
||||
Most systems will only implement one address space, placing ROM at the lower
|
||||
addresses and RAM at the higher. However, there is the possibility that a
|
||||
system will implement ROM and RAM in the same address range, but in different
|
||||
address spaces.
|
||||
|
||||
In this case, you might get away with assuming that immediate reads are in the
|
||||
program space and all other reads are in the data space, if it weren't for the
|
||||
fact that the exception vectors are fetched from the data space. As a result,
|
||||
anyone implementing this kind of system will have to copy the vector table
|
||||
from ROM to RAM using pc-relative instructions.
|
||||
|
||||
This makes things bad for emulation, because this means that a non-immediate
|
||||
read is not necessarily in the data space.
|
||||
The m68k deals with this by encoding the requested address space on the
|
||||
function code pins:
|
||||
|
||||
FC
|
||||
Address Space 210
|
||||
------------------ ---
|
||||
USER DATA 001
|
||||
USER PROGRAM 010
|
||||
SUPERVISOR DATA 101
|
||||
SUPERVISOR PROGRAM 110
|
||||
CPU SPACE 111 <-- not emulated in this core since we emulate
|
||||
interrupt acknowledge in another way.
|
||||
|
||||
To emulate the function code pins:
|
||||
|
||||
- In m68kconf.h, set M68K_EMULATE_FC to OPT_SPECIFY_HANDLER and set
|
||||
M68K_SET_FC_CALLBACK(A) to your function code handler function.
|
||||
|
||||
- Your function code handler should select the proper address space for
|
||||
subsequent calls to m68k_read_xx (and m68k_write_xx for 68010+).
|
||||
|
||||
Note: immediate reads are always done from program space, so technically you
|
||||
don't need to implement the separate immediate reads, although you could
|
||||
gain more speed improvements leaving them in and doing some clever
|
||||
programming.
|
||||
|
||||
|
||||
|
||||
USING DIFFERENT CPU TYPES:
|
||||
-------------------------
|
||||
The default is to enable only the 68000 cpu type. To change this, change the
|
||||
settings for M68K_EMULATE_010 etc in m68kconf.h.
|
||||
|
||||
To set the CPU type you want to use:
|
||||
|
||||
- Make sure it is enabled in m68kconf.h. Current switches are:
|
||||
M68K_EMULATE_010
|
||||
M68K_EMULATE_EC020
|
||||
M68K_EMULATE_020
|
||||
|
||||
- In your host program, call m68k_set_cpu_type() and then call
|
||||
m68k_pulse_reset(). Valid CPU types are:
|
||||
M68K_CPU_TYPE_68000,
|
||||
M68K_CPU_TYPE_68010,
|
||||
M68K_CPU_TYPE_68EC020,
|
||||
M68K_CPU_TYPE_68020
|
||||
|
||||
|
||||
|
||||
CLOCK FREQUENCY:
|
||||
---------------
|
||||
In order to emulate the correct clock frequency, you will have to calculate
|
||||
how long it takes the emulation to execute a certain number of "cycles" and
|
||||
vary your calls to m68k_execute() accordingly.
|
||||
As well, it is a good idea to take away the CPU's timeslice when it writes to
|
||||
a memory-mapped port in order to give the device it wrote to a chance to
|
||||
react.
|
||||
|
||||
You can use the functions m68k_cycles_run(), m68k_cycles_remaining(),
|
||||
m68k_modify_timeslice(), and m68k_end_timeslice() to do this.
|
||||
Try to use large cycle values in your calls to m68k_execute() since it will
|
||||
increase throughput. You can always take away the timeslice later.
|
||||
|
||||
|
||||
|
||||
MORE CORRECT EMULATION:
|
||||
----------------------
|
||||
You may need to enable these in order to properly emulate some of the more
|
||||
obscure functions of the m68k:
|
||||
|
||||
- M68K_EMULATE_BKPT_ACK causes the CPU to call a breakpoint handler on a BKPT
|
||||
instruction
|
||||
|
||||
- M68K_EMULATE_TRACE causes the CPU to generate trace exceptions when the
|
||||
trace bits are set
|
||||
|
||||
- M68K_EMULATE_RESET causes the CPU to call a reset handler on a RESET
|
||||
instruction.
|
||||
|
||||
- M68K_EMULATE_PREFETCH emulates the 4-word instruction prefetch that is part
|
||||
of the 68000/68010 (needed for Amiga emulation).
|
||||
NOTE: if the CPU fetches a word or longword at an odd address when this
|
||||
option is on, it will yield unpredictable results, which is why a real
|
||||
68000 will generate an address error exception.
|
||||
|
||||
- M68K_EMULATE_ADDRESS_ERROR will cause the CPU to generate address error
|
||||
exceptions if it attempts to read a word or longword at an odd address.
|
||||
|
||||
- call m68k_pulse_halt() to emulate the HALT pin.
|
||||
|
||||
|
||||
|
||||
CONVENIENCE FUNCTIONS:
|
||||
---------------------
|
||||
These are in here for programmer convenience:
|
||||
|
||||
- M68K_INSTRUCTION_HOOK lets you call a handler before each instruction.
|
||||
|
||||
- M68K_LOG_ENABLE and M68K_LOG_1010_1111 lets you log illegal and A/F-line
|
||||
instructions.
|
||||
|
||||
|
||||
|
||||
MULTIPLE CPU EMULATION:
|
||||
----------------------
|
||||
The default is to use only one CPU. To use more than one CPU in this core,
|
||||
there are some things to keep in mind:
|
||||
|
||||
- To have different cpus call different functions, use OPT_ON instead of
|
||||
OPT_SPECIFY_HANDLER, and use the m68k_set_xxx_callback() functions to set
|
||||
your callback handlers on a per-cpu basis.
|
||||
|
||||
- Be sure to call set_cpu_type() for each CPU you use.
|
||||
|
||||
- Use m68k_set_context() and m68k_get_context() to switch to another CPU.
|
||||
|
||||
|
||||
|
||||
LOAD AND SAVE CPU CONTEXTS FROM DISK:
|
||||
------------------------------------
|
||||
You can use them68k_load_context() and m68k_save_context() functions to load
|
||||
and save the CPU state to disk.
|
||||
|
||||
|
||||
|
||||
GET/SET INFORMATION FROM THE CPU:
|
||||
--------------------------------
|
||||
You can use m68k_get_reg() and m68k_set_reg() to gain access to the internals
|
||||
of the CPU.
|
||||
|
||||
|
||||
|
||||
EXAMPLE:
|
||||
-------
|
||||
|
||||
The subdir example contains a full example (currently DOS only).
|
348
plat/linux68k/emu/sim.c
Executable file
348
plat/linux68k/emu/sim.c
Executable file
|
@ -0,0 +1,348 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "sim.h"
|
||||
#include "m68k.h"
|
||||
|
||||
void disassemble_program();
|
||||
|
||||
#define ADDRESS_MASK 0xffffffff
|
||||
#define RAM_BASE 0x08000000
|
||||
#define RAM_TOP 0x08100000
|
||||
|
||||
#define BRK_TOP (RAM_TOP - 0x1000)
|
||||
|
||||
#define INIT_SP RAM_TOP
|
||||
#define INIT_PC 0x08000054
|
||||
|
||||
/* Read/write macros */
|
||||
#define READ_BYTE(BASE, ADDR) (BASE)[ADDR]
|
||||
#define READ_WORD(BASE, ADDR) (((BASE)[ADDR]<<8) | \
|
||||
(BASE)[(ADDR)+1])
|
||||
#define READ_LONG(BASE, ADDR) (((BASE)[ADDR]<<24) | \
|
||||
((BASE)[(ADDR)+1]<<16) | \
|
||||
((BASE)[(ADDR)+2]<<8) | \
|
||||
(BASE)[(ADDR)+3])
|
||||
|
||||
#define WRITE_BYTE(BASE, ADDR, VAL) (BASE)[ADDR] = (VAL)&0xff
|
||||
#define WRITE_WORD(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>8) & 0xff; \
|
||||
(BASE)[(ADDR)+1] = (VAL)&0xff
|
||||
#define WRITE_LONG(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>24) & 0xff; \
|
||||
(BASE)[(ADDR)+1] = ((VAL)>>16)&0xff; \
|
||||
(BASE)[(ADDR)+2] = ((VAL)>>8)&0xff; \
|
||||
(BASE)[(ADDR)+3] = (VAL)&0xff
|
||||
|
||||
|
||||
static void exit_error(char* fmt, ...);
|
||||
static void emulated_syscall(void);
|
||||
|
||||
uint32_t cpu_read_byte(uint32_t address);
|
||||
uint32_t cpu_read_word(uint32_t address);
|
||||
uint32_t cpu_read_long(uint32_t address);
|
||||
void cpu_write_byte(uint32_t address, uint32_t value);
|
||||
void cpu_write_word(uint32_t address, uint32_t value);
|
||||
void cpu_write_long(uint32_t address, uint32_t value);
|
||||
|
||||
unsigned char g_ram[RAM_TOP - RAM_BASE];
|
||||
uint32_t brkbase = RAM_BASE;
|
||||
uint32_t brkpos = RAM_BASE;
|
||||
uint32_t entrypoint = RAM_BASE;
|
||||
|
||||
/* Exit with an error message. Use printf syntax. */
|
||||
void exit_error(char* fmt, ...)
|
||||
{
|
||||
static int guard_val = 0;
|
||||
char buff[100];
|
||||
uint32_t pc;
|
||||
va_list args;
|
||||
|
||||
if(guard_val)
|
||||
return;
|
||||
else
|
||||
guard_val = 1;
|
||||
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
fprintf(stderr, "\n");
|
||||
pc = m68k_get_reg(NULL, M68K_REG_PPC);
|
||||
m68k_disassemble(buff, pc, M68K_CPU_TYPE_68020);
|
||||
fprintf(stderr, "At %04x: %s\n", pc, buff);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static inline uint32_t transform_address(uint32_t address)
|
||||
{
|
||||
uint32_t i = (address & ADDRESS_MASK) - RAM_BASE;
|
||||
if (i >= (uint32_t)(RAM_TOP - RAM_BASE))
|
||||
exit_error("Attempted to read from RAM address %08x", address);
|
||||
return i;
|
||||
}
|
||||
|
||||
uint32_t cpu_read_long(uint32_t address)
|
||||
{
|
||||
switch (address)
|
||||
{
|
||||
case 0x00: return INIT_SP;
|
||||
case 0x04: return entrypoint;
|
||||
case 0x80: emulated_syscall(); return 0x10000;
|
||||
case 0x10000: return 0x4e734e73; /* rte; rte */
|
||||
case 0x10004: return 0;
|
||||
default:
|
||||
{
|
||||
uint32_t value = READ_LONG(g_ram, transform_address(address));
|
||||
#if 0
|
||||
printf("read %08x from %08x\n", value, address);
|
||||
#endif
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t cpu_read_word(uint32_t address)
|
||||
{
|
||||
uint32_t l = cpu_read_long(address & ~3);
|
||||
l >>= 16 - (address & 2)*8;
|
||||
return l & 0xffff;
|
||||
}
|
||||
|
||||
uint32_t cpu_read_byte(uint32_t address)
|
||||
{
|
||||
uint32_t l = cpu_read_long(address & ~3);
|
||||
l >>= 24 - (address & 3)*8;
|
||||
return l & 0xff;
|
||||
}
|
||||
|
||||
uint32_t cpu_read_word_dasm(uint32_t address)
|
||||
{
|
||||
return cpu_read_word(address);
|
||||
}
|
||||
|
||||
uint32_t cpu_read_long_dasm(uint32_t address)
|
||||
{
|
||||
return cpu_read_long(address);
|
||||
}
|
||||
|
||||
|
||||
/* Write data to RAM or a device */
|
||||
void cpu_write_byte(uint32_t address, uint32_t value)
|
||||
{
|
||||
WRITE_BYTE(g_ram, transform_address(address), value);
|
||||
}
|
||||
|
||||
void cpu_write_word(uint32_t address, uint32_t value)
|
||||
{
|
||||
WRITE_WORD(g_ram, transform_address(address), value);
|
||||
}
|
||||
|
||||
void cpu_write_long(uint32_t address, uint32_t value)
|
||||
{
|
||||
WRITE_LONG(g_ram, transform_address(address), value);
|
||||
}
|
||||
|
||||
/* Disassembler */
|
||||
void make_hex(char* buff, uint32_t pc, uint32_t length)
|
||||
{
|
||||
char* ptr = buff;
|
||||
|
||||
for(;length>0;length -= 2)
|
||||
{
|
||||
sprintf(ptr, "%04x", cpu_read_word_dasm(pc));
|
||||
pc += 2;
|
||||
ptr += 4;
|
||||
if(length > 2)
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
void disassemble_program()
|
||||
{
|
||||
uint32_t pc;
|
||||
uint32_t instr_size;
|
||||
char buff[100];
|
||||
char buff2[100];
|
||||
|
||||
pc = cpu_read_long_dasm(4);
|
||||
|
||||
while(pc <= 0x16e)
|
||||
{
|
||||
instr_size = m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000);
|
||||
make_hex(buff2, pc, instr_size);
|
||||
printf("%03x: %-20s: %s\n", pc, buff2, buff);
|
||||
pc += instr_size;
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void cpu_instr_callback()
|
||||
{
|
||||
uint32_t pc = m68k_get_reg(NULL, M68K_REG_PC);
|
||||
if (pc == 0xc)
|
||||
exit_error("address exception");
|
||||
|
||||
/* The following code would print out instructions as they are executed */
|
||||
|
||||
#if 0
|
||||
static char buff[100];
|
||||
static char buff2[100];
|
||||
static uint32_t instr_size;
|
||||
|
||||
instr_size = m68k_disassemble(buff, pc, M68K_CPU_TYPE_68020);
|
||||
make_hex(buff2, pc, instr_size);
|
||||
printf("E %03x: %-20s: %s\n", pc, buff2, buff);
|
||||
printf(" d0: %08x d1: %08x d2: %08x d3: %08x d4: %08x d5: %08x d6: %08x d7: %08x\n",
|
||||
m68k_get_reg(NULL, M68K_REG_D0),
|
||||
m68k_get_reg(NULL, M68K_REG_D1),
|
||||
m68k_get_reg(NULL, M68K_REG_D2),
|
||||
m68k_get_reg(NULL, M68K_REG_D3),
|
||||
m68k_get_reg(NULL, M68K_REG_D4),
|
||||
m68k_get_reg(NULL, M68K_REG_D5),
|
||||
m68k_get_reg(NULL, M68K_REG_D6),
|
||||
m68k_get_reg(NULL, M68K_REG_D7));
|
||||
printf(" a0: %08x a1: %08x a2: %08x a3: %08x a4: %08x a5: %08x a6: %08x a7: %08x\n",
|
||||
m68k_get_reg(NULL, M68K_REG_A0),
|
||||
m68k_get_reg(NULL, M68K_REG_A1),
|
||||
m68k_get_reg(NULL, M68K_REG_A2),
|
||||
m68k_get_reg(NULL, M68K_REG_A3),
|
||||
m68k_get_reg(NULL, M68K_REG_A4),
|
||||
m68k_get_reg(NULL, M68K_REG_A5),
|
||||
m68k_get_reg(NULL, M68K_REG_A6),
|
||||
m68k_get_reg(NULL, M68K_REG_A7));
|
||||
fflush(stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void emulated_syscall(void)
|
||||
{
|
||||
int s = m68k_get_reg(NULL, M68K_REG_D0);
|
||||
switch (s)
|
||||
{
|
||||
case 1: /* exit */
|
||||
exit(m68k_get_reg(NULL, M68K_REG_D1));
|
||||
|
||||
case 4: /* write */
|
||||
{
|
||||
uint32_t fd = m68k_get_reg(NULL, M68K_REG_D1);
|
||||
uint32_t ptr = m68k_get_reg(NULL, M68K_REG_D2);
|
||||
uint32_t len = m68k_get_reg(NULL, M68K_REG_D3);
|
||||
m68k_set_reg(M68K_REG_D0, write(fd, g_ram + transform_address(ptr), len));
|
||||
break;
|
||||
}
|
||||
|
||||
case 45: /* brk */
|
||||
{
|
||||
uint32_t newpos = m68k_get_reg(NULL, M68K_REG_D1);
|
||||
if (newpos == 0)
|
||||
m68k_set_reg(M68K_REG_D0, brkpos);
|
||||
else if ((newpos < brkbase) || (newpos >= BRK_TOP))
|
||||
m68k_set_reg(M68K_REG_D0, -ENOMEM);
|
||||
else
|
||||
{
|
||||
brkpos = newpos;
|
||||
m68k_set_reg(M68K_REG_D0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 20: /* getpid */
|
||||
case 48: /* signal */
|
||||
case 54: /* ioctl */
|
||||
case 78: /* gettimeofday */
|
||||
m68k_set_reg(M68K_REG_D0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
exit_error("unknown system call %d", s);
|
||||
}
|
||||
}
|
||||
|
||||
static void load_program(FILE* fd)
|
||||
{
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
if (fread(g_ram, 1, 0x34, fd) != 0x34)
|
||||
exit_error("couldn't read ELF header");
|
||||
|
||||
uint32_t phoff = READ_LONG(g_ram, 0x1c);
|
||||
uint16_t phentsize = READ_WORD(g_ram, 0x2a);
|
||||
uint16_t phnum = READ_WORD(g_ram, 0x2c);
|
||||
entrypoint = READ_LONG(g_ram, 0x18);
|
||||
if ((phentsize != 0x20) || (phnum != 1))
|
||||
exit_error("unsupported ELF file");
|
||||
|
||||
fseek(fd, phoff, SEEK_SET);
|
||||
if (fread(g_ram, 1, phentsize, fd) != phentsize)
|
||||
exit_error("couldn't read program header");
|
||||
|
||||
uint32_t offset = READ_LONG(g_ram, 0x04);
|
||||
uint32_t vaddr = READ_LONG(g_ram, 0x08);
|
||||
uint32_t filesz = READ_LONG(g_ram, 0x10);
|
||||
uint32_t memsz = READ_LONG(g_ram, 0x14);
|
||||
brkbase = brkpos = vaddr + memsz;
|
||||
|
||||
uint32_t vaddroffset = transform_address(vaddr);
|
||||
transform_address(vaddr + memsz); /* bounds check */
|
||||
memset(g_ram+vaddroffset, 0, memsz);
|
||||
fseek(fd, offset, SEEK_SET);
|
||||
if (fread(g_ram+vaddroffset, 1, filesz, fd) != filesz)
|
||||
exit_error("couldn't read program data");
|
||||
}
|
||||
|
||||
/* The main loop */
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
FILE* fhandle;
|
||||
|
||||
if(argc != 2)
|
||||
{
|
||||
printf("Usage: sim <program file>\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if((fhandle = fopen(argv[1], "rb")) == NULL)
|
||||
exit_error("Unable to open %s", argv[1]);
|
||||
|
||||
load_program(fhandle);
|
||||
|
||||
// disassemble_program();
|
||||
|
||||
m68k_set_cpu_type(M68K_CPU_TYPE_68020);
|
||||
m68k_init();
|
||||
m68k_pulse_reset();
|
||||
|
||||
/* On entry, the Linux stack looks like this.
|
||||
*
|
||||
* sp+.. NULL
|
||||
* sp+8+(4*argc) env (X quads)
|
||||
* sp+4+(4*argc) NULL
|
||||
* sp+4 argv (argc quads)
|
||||
* sp argc
|
||||
*
|
||||
* We'll set it up with a bodgy stack frame with argc=0 just to keep the
|
||||
* startup code happy.
|
||||
*/
|
||||
|
||||
{
|
||||
uint32_t sp = INIT_SP;
|
||||
cpu_write_long(sp -= 4, 0);
|
||||
uint32_t envp = sp;
|
||||
cpu_write_long(sp -= 4, envp);
|
||||
cpu_write_long(sp -= 4, 0);
|
||||
unsigned long argv = sp;
|
||||
cpu_write_long(sp -= 4, argv);
|
||||
cpu_write_long(sp -= 4, 0);
|
||||
m68k_set_reg(M68K_REG_SP, sp);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
m68k_execute(100000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
15
plat/linux68k/emu/sim.h
Executable file
15
plat/linux68k/emu/sim.h
Executable file
|
@ -0,0 +1,15 @@
|
|||
#ifndef SIM__HEADER
|
||||
#define SIM__HEADER
|
||||
|
||||
unsigned int cpu_read_byte(unsigned int address);
|
||||
unsigned int cpu_read_word(unsigned int address);
|
||||
unsigned int cpu_read_long(unsigned int address);
|
||||
void cpu_write_byte(unsigned int address, unsigned int value);
|
||||
void cpu_write_word(unsigned int address, unsigned int value);
|
||||
void cpu_write_long(unsigned int address, unsigned int value);
|
||||
void cpu_pulse_reset(void);
|
||||
void cpu_set_fc(unsigned int fc);
|
||||
int cpu_irq_ack(int level);
|
||||
void cpu_instr_callback();
|
||||
|
||||
#endif /* SIM__HEADER */
|
7
plat/linux68k/tests/build.lua
Normal file
7
plat/linux68k/tests/build.lua
Normal file
|
@ -0,0 +1,7 @@
|
|||
include("tests/plat/build.lua")
|
||||
|
||||
plat_testsuite {
|
||||
name = "tests",
|
||||
plat = "linux68k",
|
||||
method = "plat/linux68k/emu+emu68k"
|
||||
}
|
|
@ -51,16 +51,23 @@ definerule("plat_testsuite",
|
|||
}
|
||||
}
|
||||
|
||||
local methoddep = nil
|
||||
local methodpath = e.method
|
||||
if e.method:find("%+") then
|
||||
methoddep = e.method
|
||||
methodpath = "%{ins[4]}"
|
||||
end
|
||||
tests[#tests+1] = normalrule {
|
||||
name = fs,
|
||||
outleaves = { e.plat.."-"..fs.."-testlog.txt" },
|
||||
ins = {
|
||||
bin,
|
||||
"tests/plat/testdriver.sh",
|
||||
"util/build+testrunner"
|
||||
"util/build+testrunner",
|
||||
methoddep,
|
||||
},
|
||||
commands = {
|
||||
"%{ins[2]} "..e.method.." %{ins[1]} 15 %{ins[3]} > %{outs}; true",
|
||||
"%{ins[2]} "..methodpath.." %{ins[1]} 15 %{ins[3]} > %{outs}; true",
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -4,6 +4,8 @@ img=$2
|
|||
timeout=$3
|
||||
timeoutprog=$4
|
||||
|
||||
set -e
|
||||
|
||||
errcho() {
|
||||
>&2 echo "$*"
|
||||
}
|
||||
|
@ -36,8 +38,7 @@ get_test_output() {
|
|||
;;
|
||||
|
||||
*)
|
||||
errcho "Error: $method not known by testdriver"
|
||||
exit 1
|
||||
$timeoutprog -t $timeout -- $method $img 2>&1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue