Merge from trunk.

This commit is contained in:
David Given 2018-06-08 00:05:50 +09:00
commit 1d4ddb939d
29 changed files with 20874 additions and 6 deletions

View file

@ -24,7 +24,7 @@ BUILDDIR = $(ACK_TEMP_DIR)/ack-build
# What build flags do you want to use for native code? # What build flags do you want to use for native code?
CFLAGS = -g -O -Wno-return-type CFLAGS = -g -Og -Wno-return-type
LDFLAGS = LDFLAGS =
# Various commands. # Various commands.

View file

@ -18,6 +18,7 @@ vars.plats = {
"pdpv7", "pdpv7",
} }
vars.plats_with_tests = { vars.plats_with_tests = {
"linux68k",
"linux386", "linux386",
"linuxppc", "linuxppc",
--"qemuppc", --"qemuppc",

9
lang/basic/lib/lib.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef LIB_H
#define LIB_H
extern void error(int index);
extern char* salloc(unsigned length);
extern void sfree(char* c);
#endif

View file

@ -1,6 +1,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "bc_string.h" #include "bc_string.h"
#include "lib.h"
/* $Id$ */ /* $Id$ */
@ -11,8 +12,6 @@
if (X == 0) \ if (X == 0) \
return (0); return (0);
extern char* salloc();
int _length(String* str) int _length(String* str)
{ {
okr(str); okr(str);
@ -158,7 +157,7 @@ String* _mid(int i1, int i2, String* s)
/* printf("mid fcn called %d %d %s\n",i1,i2,s->strval);*/ /* printf("mid fcn called %d %d %s\n",i1,i2,s->strval);*/
if (i2 < 0 || i1 < -1) if (i2 < 0 || i1 < -1)
return (s2); /* or error? */ error(3);
if (i1 == -1) if (i1 == -1)
i1 = s->strlength; i1 = s->strlength;
s2 = _newstr(s->strval); s2 = _newstr(s->strval);

View 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
View 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 */

View 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.

View 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.

View 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 */

View 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

View file

@ -0,0 +1,6 @@
#ifndef HEADER__OSD
#define HEADER__OSD
int osd_get_char(void);
#endif /* HEADER__OSD */

View 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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

View 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;
}

View 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 */

View 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
View 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

File diff suppressed because it is too large Load diff

View 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 */

View 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" , &REG_PPC, 1);
state_save_register_UINT32(type, cpu, "PC" , &REG_PC, 1);
state_save_register_UINT32(type, cpu, "USP" , &REG_USP, 1);
state_save_register_UINT32(type, cpu, "ISP" , &REG_ISP, 1);
state_save_register_UINT32(type, cpu, "MSP" , &REG_MSP, 1);
state_save_register_UINT32(type, cpu, "VBR" , &REG_VBR, 1);
state_save_register_UINT32(type, cpu, "SFC" , &REG_SFC, 1);
state_save_register_UINT32(type, cpu, "DFC" , &REG_DFC, 1);
state_save_register_UINT32(type, cpu, "CACR" , &REG_CACR, 1);
state_save_register_UINT32(type, cpu, "CAAR" , &REG_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 ============================= */
/* ======================================================================== */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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
View 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
View 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 */

View file

@ -0,0 +1,7 @@
include("tests/plat/build.lua")
plat_testsuite {
name = "tests",
plat = "linux68k",
method = "plat/linux68k/emu+emu68k",
}

View file

@ -595,7 +595,7 @@ int glob;
char *fptr; char *fptr;
pflag = FALSE; pflag = FALSE;
while (*inptr == SP && *inptr == HT) inptr++; while (*inptr == SP || *inptr == HT) inptr++;
c = *inptr++; c = *inptr++;

View file

@ -24,7 +24,7 @@ STATIC void showinstr(line_p lnp)
* can occur in expressions that are going to be eliminated are * can occur in expressions that are going to be eliminated are
* properly handled. * properly handled.
*/ */
if (INSTR(lnp) < sp_fmnem && INSTR(lnp) > sp_lmnem) { if (INSTR(lnp) < sp_fmnem || INSTR(lnp) > sp_lmnem) {
fprintf(stderr,"*** ?\n"); fprintf(stderr,"*** ?\n");
return; return;
} }