FAME Fast and Accurate Morolora 68000 Emulation Library

F.A.M.E.
Fast and Accurate Morolora 68000 Emulation Library
Copyright (c) 2002-2005 Oscar Orallo Peláez / Daniel Lancha García. All rights reserved.
March 14th, 2006

Table of Contents

> 0. Introduction
> 1. Terms of Use
> 2. Version History
> 3. What is emulated
> 4. Using the emulation library
> 4.1. Data structure
> 4.1.1. CPU context
> 4.1.2. Memory map definition example
> 4.2. Memory handling
> 4.3. Running the CPU
> 5. Interrupts and exceptions
> 5.1. Interrupt acknowledge
> 5.2. Customizing processing (HLE)
> 5.3. IRQ lowering
> 6. Function Reference
> 6.1. General Purpose Functions
> 6.2. Hardware interrupt handling functions
> 6.3. CPU context handling functions
> 6.4. Timing functions
> 7. Multi-CPU systems
> 8. Helpful tips
> 9. Troubleshooting
> 10. Known bugs
> 11. Special thanks

0. Introduction

This is the documentation for FAME library, please read it.

FAME is an extremely fast and accurate Motorola 68000 Emulation Library. It is currently available for Intel x86-based systems (80386 or better processor) and SH-4 based systems.

The x86 version was designed to work under any win32 development environment such as Microsoft Visual Basic, Microsoft Visual C++, Borland Delphi or Borland C++ Builder.

The SH-4 version was specially designed for the Dreamcast videogame console but it can be used in any SH-4 based system.

This manual tries to be a guide to get the emulation library working in your development environment. I hope you find it useful. If you use FAME in your project I would like to hear your opinion about it.

The package contains one example (C++ program) to show how the library should be called and used. It was compiled successfully in Microsoft Visual C++ 6.0 SP5, Borland C++ Builder 5/6 and Borland C++ Compiler 5.5.

If you have any questions about how it works in your favorite compiler send me an email. I'd like to help you with FAME.
If you find any bug in FAME, it would be nice that you inform me about that via email. Any feedback, comments and suggestions will also be appreciated.

How to contact Oscar Orallo:

> E-mail: oscar@m68k.com
> Web site: http://www.m68k.com/fame

FAME Distribution: http://www.m68k.com/fame/fame.zip (latest)
FAME Development Package: http://www.m68k.com/fame/famedev.zip (latest)

Here we go folks, have fun :)

1. Terms of use

FAME is a development package that contains the following files:

File | Description
--- | ---
/x86/win32/fame.dll | Microsoft win32 dinamic link library
/x86/linux/libfame.a | x86 static ELF library
/x86/cygwin/libfame.a | x86 static win32 library
/sh4/libfame.a | SH-4 static ELF library
/delphi/fame.pas | Delphi unit
/doc/fame.html | Documentation file
/example/main.c | C source code example
/example/makefile.cygwin | Cygwin example makefile
/example/makefile.dc | Dreamcast example makefile
/example/makefile.linux | Linux example makefile
/example/romdisk/bubble.bin | Motorola 68000 binary code file
/lib/bc/fame.lib | Borland C++ 32-bit import library
/lib/vc/fame.exp | Microsoft Visual C++ 32-bit export file
/include/fame.h | C/C++ header file

FAME may be distributed freely in unmodified form, as long as this document file is included.

Nothing may be charged for this library. If you want to use it in a shareware or commercial application, contact me.

The author will not be held liable for damages. FAME comes with absolutely NO WARRANTY. Anyway i will try to help you with any problem you have using FAME.

If you do not agree with all of these terms, please remove FAME from your computer.

You are encouraged to contact the author if you wish to use FAME in a commercial product (to negotiate licensing).

Any program which uses FAME must include in its documentation or in the program itself the following credit text:

FAME Motorola 68000 Emulation Library by Oscar Orallo (oscar@m68k.com)

2. Version History

Intel® 80386 CISC engine | Super H® SH-4 RISC engine
Version 2.0a (14th, march, 2006) - Stupid bug fixed in IRQ management (thanks Martin Kresse). - Accurate DIV timing implemented (thanks Jorge Cwik). - Overflow detection fixed in signed DIV instruction. - Added makefiles for linux and cygwin environments. Version 2.0 (11th, january, 2006) - set_irq_type API function removed. IRQs will be automatically lowered once it was attended. - Many flag calculations fixed. - Some minor tweaks. Version 1.23 (5th April, 2005) - Timing fixed in MOVEM instructions. Version 1.22 (7th March, 2005) - Fixed a stupid bug in fetch function. Version 1.21 (19th February, 2005) - set_irq_type function changed for flexible use. - Pointer to data structure parameter removed from memory handlers to increase throughtput. Version 1.2 (17th December, 2004) - Fixed the PC base calculation for fetch memory regions beyond the first allocated area. - Fixed a tiny error in the status register masking. Several instructions could generate an invalid value. - Fixed JSR instruction when jumping to a fetch bank different to the current one. - Tiny error fixed in interrupt acknowledge function parameter. - Fixed the set_context function when setting status register. - Fixed the PC restoring in HLE feature. - Fixed DIVS instruction operation. Version 1.1 (7th October, 2004) - New static ELF library available. - Interrupt acknowledge calling bug fixed. - Custom exception processing (HLE) feature added. - Some little changes in CPU context (register ordering). - Flag N calculation in CHK instruction fixed. - Some little code tweaks. - New sections added to this document. - Some defines added to header file. - Some return values have been changed. - Set/get context functions simplified. Some changes have been applied. Version 1.0g (2nd August, 2004) - Speed emulation increased once more. The fetch/decode/execute loop has been inlined. - Some API functions added: add_cycles and release_cycles. - Memory handling section added to this document (thanks Richard Hollstein for requesting it). - Faster memory access (both program code and data). Memory regions must be 4 KB aligned now. - Overhead reduced in emulate function calls (entry/exit code optimized). - Interrupt acknowledge function added. - Some code tweaks here and there. - Static library for Borland C++ compilers added to the package. - DLL file size reduced: internal compression (UPX). - Fixed a bug in STOP instruction: the processor started up after an interrupt request even if its interrupt level was not higher than current PPL. - Ver little optimization in branch instructions. Version 1.0f (23th February, 2003) - API functions added: get_register and set_register to retrieve and set register values. - Small optimizations for improved speed. - CPU context modified: execinfo added for more complete CPU state handling support. - LIB file added to package to support implicit linking :). - Changes in documentation. - C header file (fame.h) and Delphi unit (fame.pas) modified. Version 1.0e (18th February, 2003) - Emulation core speed increased lightly (faster entry/exit code). Now the library is pretty fast. - Some changes in function and variable naming (odometer changed to cycles_counter). Version 1.0d (20th December, 2002) - Fetch function speed incremented a bit. - Fixed memory boundary for byte data accesses. Version 1.0c (27th August, 2002) - Fixed a stupid bug in group 0 exceptions management. Version 1.0b (16th August, 2002) - Many errors corrected in documentation about memory mapping. - The function fetch has now capability to access to the data address space. Version 1.0a (24th July, 2002) - First public release. | Version 2.0a (14th, march, 2006) - Timing fixed for DIV and signed MUL instructions. - Improved overflow detection in signed DIV instruction. - Added makefile for Dreamcast system (requires KOS). Version 2.0 (11th, january, 2006) - Tons of bugs fixed (thanks Chui). - Accurate DIV timing implemented (thanks Jorge Cwik). - set_irq_type API function removed. IRQs will be automatically lowered once it was attended. - Great speed improvements. Version 0.04 (5th April, 2005) - Lightweighted entry/exit code. - Fixed sign/zero flag calculation when moving long data from memory to memory. - Privilege violation exception fixed. - Faster interrupt/exception management. - Timing fixed in MOVEM instructions. Version 0.03 (7th March, 2005) - Sign flag calculation in inmediate logical instruction fixed. - Carry flag calculation in NEG instruction fixed. - Overflow flag in operations with X flag fixed. - Fixed CPU state stop bit. - Speed up by about 20%. - Tiny tweaks here and there and everywhere. Version 0.02 (19th February, 2005) - First beta release. - set_irq_type function changed for flexible use. - Greatly improved internal memory management. - Pointer to data structure parameter removed from memory handlers to increase throughtput. - DIV/DIVS instructions fixed. - ABCD/SBCD adjusted result fixed. - MOVEM (control addressing mode) instruction fixed. - BTST with memory addressing mode fixed. - Fixed Z flag calculation in NEGX instruction. - Fixed TAS instruction. - Fixed RESET instruction (external handler calling). - Fixed ILLEGAL instruction (exception generation). - Quick ADD to address register fixed. - EXG instruction fixed. - V flag calculation fixed in ASL instruction. - Some tiny tweaks & improvements. Version 0.01b (17th December, 2004) - Memory map cache generation fixed. - Fixed the PC restoring in HLE feature. - Many opcodes fixed. - Lots of bugs fixed. Version 0.01a (7th October, 2004) - First public release. Alpha development state!

3. What is emulated

This library emulates the Motorola 68000 microprocessor. The main emulation features are the following:

- Written in 100% 32-bit assembly language.
- Support for all opcodes.
- Calculates 100% of flags correctly, even undocumented ones.
- Excellent accurate timing emulation for all opcodes. All instructions have perfect timing emulation according to Motorola references. Take a look at Motorola manuals for more information about this fact.
- Complete hardware interrupt support.
- Accurate exception support allowing an appropriate emulation of home computer systems.
- Priorities between interrupts and exceptions are fully emulated.

4. Using the emulation library

4.1. Data structure

The data structures used in the emulation core is defined in the C file header fame.h. In this file you will get all the data structures needed to use the library.
If you cannot use this file because you are not using a C/C++ compliant compiler you have to define this structures by yourself in your code.

Here I describe these data structures.

> struct M68K_PROGRAM
> {
> unsigned low_addr;
> unsigned high_addr;
> unsigned offset;
> }

This structure defines the memory regions for 68000 program code. The fields low_addr and high_addr are 32-bit values used for determine the low and high address of the memory block in the 68000 memory map.

The last field is a 32-bit pointer to the data of the memory region. The data pointed by it must be allocated in native (Motorola) format. If not, the data will be fetched incorrectly. Make sure of this fact.

> struct M68K_DATA
> {
> unsigned low_addr;
> unsigned high_addr;
> void *mem_handler;
> void *data;
> }

This one is used for 68000 data code. This structure has an appearance very similar to the last one but has a diference in the way you can give the control of the memory to FAME. The pointer called mem_handler is a function pointer. This pointer is used for memory management, so when you want to take control in the reading/writing of a memory region, you have to set this pointer to the appropriate value. If you do not want to use this funcionality you have to set this pointer to NULL and set data pointing to the data itself. The different ways to perform memory handling will be described with more detail in memory handling section.

4.1.1. CPU context

> struct M68K_CONTEXT
> {
> struct M68K_PROGRAM *fetch;
> struct M68K_DATA *read_byte;
> struct M68K_DATA *read_word;
> struct M68K_DATA *write_byte;
> struct M68K_DATA *write_word;
> struct M68K_PROGRAM *sv_fetch;
> struct M68K_DATA *sv_read_byte;
> struct M68K_DATA *sv_read_word;
> struct M68K_DATA *sv_write_byte;
> struct M68K_DATA *sv_write_word;
> struct M68K_PROGRAM *user_fetch;
> struct M68K_DATA *user_read_byte;
> struct M68K_DATA *user_read_word;
> struct M68K_DATA *user_write_byte;
> struct M68K_DATA *user_write_word;
> void (*reset_handler)(void);
> void (*iack_handler)(unsigned level);
> unsigned *icust_handler;
> unsigned dreg[8];
> unsigned areg[8];
> unsigned asp;
> unsigned pc;
> unsigned cycles_counter;
> unsigned char interrupts[8];
> unsigned short sr;
> unsigned short execinfo;
> }

This structure defines a CPU context. You have to declare a variable of this type. It contains all information related with the context of the CPU.

You have to set pointer values of sv* which defines the supervisor memory map. In order to get the CPU into user mode, set the user* pointers.

The pointer reset_handler is called when the RESET instruction is executed. In this way, you can reset all external devices in the calling to this function. If you do not want to use this feature remember to set this pointer to NULL.

The pointer iack_handler is called whenever a hardware interrupt is handled by the CPU. This feature will be covered later in Interrupts and exceptions section.

The pointer icust_handler is intented to point to an array of function pointers to handle customized interrupt/exception processing (known as High Level Emulation or HLE for short). See Interrupts and exceptions section to set up this feature.

The rest of the structure is managed by FAME so you can read it in execution time to retrieve information about the CPU.

Here I describe some interesting fields for the 68000 programmer:

> - dreg[8] holds the eight data registers in order (d0 - d7).
> - areg[8] holds the eight address registers in order (a0 - a7).
> - pc is the current PC address.
> - asp stands for Alternative Stack Pointer. It is used to store the not currently used stack pointer. In supervisor mode, asp is the user stack pointer, in user mode it is the supervisor stack pointer.
> - cycles_counter holds the number of cycles executed so far.
> - interrupts is an array that contains information about interrupts.
> - sr is the status register.

4.1.2 Memory map definition example

As an example of an address space definition, consider the following simple memory map:

- ROM: 000000-01FFFF
- RAM-1: 300000-407FFF
- RAM-2: 500000-50FFFF
- RAM-3: 600000-601FFF
- RAM-4: 800000-80AFFF

This is the structure for the program address space. I will suppose that ROM, RAM-1 and RAM-2 contains program code.

> struct M68K_PROGRAM prg_fetch[] = {
> {0x000000, 0x01FFFF, (unsigned)rom},
> {0x300000, 0x407FFF, (unsigned)ram1 - 0x300000},
> {0x500000, 0x500FFF, (unsigned)ram2 - 0x500000},
> {-1, -1, NULL}
> }

Note that the last entry must be {-1, -1, NULL}.

Now, I will set up the data address space. In this case, I will suppose that all memory areas will be accesed and that RAM-3 is accessed by the routine mem_access. To do this, you will have to set up the following:

- One structure for read byte operations:

> struct M68K_DATA data_rb[] = {
> {0x000000, 0x01FFFF, NULL, rom},
> {0x300000, 0x407FFF, NULL, ram1 - 0x300000},
> {0x500000, 0x507FFF, NULL, ram2 - 0x500000},
> {0x600000, 0x601FFF, mem_access, NULL},
> {0x800000, 0x80AFFF, NULL, ram4 - 0x800000},
> {-1, -1, NULL, NULL}
> }

- One structure for write byte operations:

> struct M68K_DATA data_wb[] = {
> {0x000000, 0x01FFFF, NULL, rom},
> {0x300000, 0x407FFF, NULL, ram1 - 0x300000},
> {0x500000, 0x507FFF, NULL, ram2 - 0x500000},
> {0x600000, 0x601FFF, mem_access, NULL},
> {0x800000, 0x80AFFF, NULL, ram4 - 0x800000},
> {-1, -1, NULL, NULL}
> }

- One structure for read word operations:

> struct M68K_DATA data_rw[] = {
> {0x000000, 0x01FFFF, NULL, rom},
> {0x300000, 0x407FFF, NULL, ram1 - 0x300000},
> {0x500000, 0x507FFF, NULL, ram2 - 0x500000},
> {0x600000, 0x601FFF, mem_access, NULL},
> {0x800000, 0x80AFFF, NULL, ram4 - 0x800000},
> {-1, -1, NULL, NULL}
> }

- One structure for write word operations:

> struct M68K_DATA data_ww[] = {
> {0x000000, 0x01FFFF, NULL, rom},
> {0x300000, 0x407FFF, NULL, ram1 - 0x300000},
> {0x500000, 0x507FFF, NULL, ram2 - 0x500000},
> {0x600000, 0x601FFF, mem_access, NULL},
> {0x800000, 0x80AFFF, NULL, ram4 - 0x800000},
> {-1, -1, NULL, NULL}
> }

In the example, the routine used for access to ram3 area is the same in all the structures defined but it could be different.

And now the last step is to fill the CPU context with the defined address spaces. This is accomplished in the following way:

> struct M68K_CONTEXT cpu_contxt;

> cpu_contxt.sv_fetch = prg_fetch;
> cpu_contxt.user_fetch = prg_fetch;

> cpu_contxt.sv_read_byte = data_rb;
> cpu_contxt.user_read_byte = data_rb;
> cpu_contxt.sv_read_word = data_rw;
> cpu_contxt.user_read_word = data_rw;
> cpu_contxt.sv_write_byte = data_wb;
> cpu_contxt.user_write_byte = data_wb;
> cpu_contxt.sv_write_word = data_ww;
> cpu_contxt.user_write_word = data_ww;

Note that the memory address spaces for supervisor and user are the same. This is very common but remember they could be different.

And that is all.

4.2. Memory handling

The emulation library provides two ways to perform the access to the memory map: built-in and custom.

The built-in memory handling is ideal to get the maximun speed to the memory map but at the cost of less control. To use it you have to set data pointing to the beginning of the native memory region and set mem_handler to NULL.

> struct M68K_DATA
> {
> unsigned low_addr;
> unsigned high_addr;
> void *mem_handler;
> void *data;
> }

The custom memory handling gives you total control over memory accesses but its use could create a bottleneck in the emulated system if it is used inappropriately. To use this feature you have to set up mem_handler pointer to the handling function. That function will be called whenever a memory access is done.
There is a restriction in the definition of a memory region: it must be 4 KB aligned. So it must start on 0XXX000h and end on 0YYYFFFh.

Memory handling functions have the following structure:

int read_xxxx (int address);
void write_xxxx(int address, int data);

where xxxx stands for byte, word or long depending on data size, address is the memory address accessed and data is the data itself.

Using memory handling functions might be a good way to customize emulated memory space. You have to read/write data in the way FAME expects. This process could become confusing. To avoid undesired problems in this point, i have written some simple routines to make your life easier:

> int readbyte(int address)
> {
> return ram[address^1];
> }
> int readword(int address)
> {
> return ((unsigned short *)ram)[address>>1];
> }
> void writebyte(int address, int data)
> {
> ram[address^1] = data & 0xFF;
> }
> void writeword(int address, int data)
> {
> ((unsigned short *)ram)[address>>1] = data & 0xFFFF;
> }

I am considering you have your emulated memory region (pointed by ram here) in native endian format (this is, big endian for the 68000 processor). Note the required endianess switch in the byte accesses, since we are reading in a little endian machine (x86 and SH4 processors).

4.3. Running the CPU

In order to get the 68000 CPU running, you have to do the following steps:

> 1. Initialize the emulation library. Call m68k_init() to perform this task.
> 2. Set up the memory map (see section 4.1.2).
> 3. Reset the processor calling the m68k_reset() function.
> 4. Execute code calling m68k_emulate(n) function where the parameter n means the number of clock cycles to execute.

Note: See Function Reference section for more information about how API functions work.

5. Interrupts and exceptions

The library currently emulates the group 0 exceptions (address error and bus error), group 1 exceptions (trace mode, external interrupts, illegal opcode and privilege violation) and group 2 exceptions.

The reset exception is not emulated. This is due to performance facts. If this exception was emulated, the performance of the library would fall notably. If you need this exception be emulated, contact me.

Hardware interrupts can be raised at any time, but it will be attended only in the entry code of the emulate function. To manage interrupts, please refer to the section Function Reference bellow.

If you have any doubt about how these events work, I recommend you to take a look at M68000 Microprocessors User's Manual (english) or at the book Sistemas Digitales (spanish).

5.1. Interrupt acknowledge

Sometimes could be useful to be warned when a hardware interrupt is being attended. This feature is frequently called interrupt acknowledging and allows you to take specific actions when an interrupt is handled, signaling a device to lower the interrupt request, for example.
This function accepts one parameter, the interrupt level, and returns no value.

> void iackhandler(unsigned int_level);

Once you have defined your function, set up the CPU context:

> struct M68K_CONTEXT cpu_contxt;

> cpu_contxt.iack_handler = iackhandler;
> m68k_set_context(&cpu_contxt);

If you do not need this feature, set this pointer to NULL to avoid undesired results.

5.2. Customizing processing (HLE)

Sometimes it is needed to trap an exception to perform some native tasks overriding target system tasks (system BIOS calls, for example).

To customize interrupt and exception processing use icusthandler table pointer. This pointer must point to a table of a total of 256 function pointers, each one handling each vector exception presented in the 68000 system starting from address $000000. The index of the table is the vector number.

The handling function accepts one parameter, the vector exception number, and returns no value.

> void icusthandler(unsigned vector);

The array of pointers could be used in this fashion:

> /* Function to customize CHK exception */
> void chk_handler(unsigned vector)
> {
> . . .
> (some actions)
> . . .
> }

> unsigned fpa[256]; /* Function Pointer Array declaration */
> struct M68K_CONTEXT cpu_context;
> fpa[6] = chk_handler; /* Customizing CHK exception */
> cpu_context.icust_handler = fpa; /* Setting up function pointers */

Take in account the following when you use this feature:

- Remember to set to NULL those exceptions you do not want to be customized in the array of function pointers.
- Group 0 exceptions are a special type of exception. Since they are raised when something has gone seriously wrong with the system, they can not be customized.

If you do not need this feature, set this pointer to NULL to avoid undesired results.

5.3. IRQ lowering

Every IRQ will be automatically lowered once it has been attended. User selectable IRQ lowering type has been removed.

6. Function Reference

This is a brief description of the library functions.

- For C/C++ programmers:

  - They are declared in fame.h to include in your C/C++ application. You can take a look at the sample program included.

- For Delphi programmers:

  - Copy fame.pas and fame.dll into your project's directory. Add fame.pas to your project.

Remember that this is a brief overview. If you do not find answers to your questions, contact me.

6.1. General Purpose Functions

- void m68k_init (void)

This function initialize the emulation library. Must be called before any other function library.

- unsigned m68k_reset (void)

Resets the CPU. You must set up the memory map before call this function.

Return values:

> M68K_OK (0): Success.
> M68K_RUNNING (1): The function failures because the CPU is running. Stop the CPU first.
> M68K_NO_SUP_ADDR_SPACE (2): The CPU could not be resetted because there is no supervisor memory map for opcode fetching.

- void m68k_emulate (int n)

Starts the emulation and executes n clock cycles. This is the function you have to call to execute 68000's code. The number of elapsed CPU cycles is the lowest number equal or greater than n.

- unsigned m68k_get_pc (void)

Returns the current PC address. The value returned by this function does not have to be equal to the beginning of an instruction.

- unsigned m68k_get_cpu_state (void)

Returns information about the CPU current state. It could be called at any time to retrieve interesting and useful information about the CPU state.

The data returned has the following format:

> Bits | Meaning
> --- | ---
> 0 | Internal use. Should be zero.
> 1 | Processing a group 0 exception (address error or bus error).
> 2 | Double bus fault has happened.
> 3 | Trace mode is being processed.
> 4 | Processing trace mode exception.
> 5 | Processing bus error exception.
> 6 | Processing address error exception.
> 7 | CPU stopped by the STOP instruction.
> 8-31 | Reserved for future use. Should be zero.

- int m68k_fetch(unsigned address, unsigned memory_space)

Fetches the word pointed by the specified address using the given memory space from the fetch memory array. The memory space means the following:

- Supervisor address space (M68K_SUP_ADDR_SPACE)
- User address space (M68K_USER_ADDR_SPACE)
- Data address space (M68K_DATA_ADDR_SPACE)
- Program address space (M68K_PROG_ADDR_SPACE)

Generally, you will want to fetch a word from a memory map joining two of those primitives types. For example:

> Supervisor Data Address Space (Supervisor & Data)

To accomplish this, you have to use a bitwise OR operation:

> M68K_SUP_ADDR_SPACE | M68K_DATA_ADDR_SPACE

Return value:

> FFFFFFFFh: The address specified is out of bounds in the given
> memory space.
> 0000xxxxh: The fetched word.

6.2 Hardware interrupt handling functions

- int m68k_raise_irq (int level, int vector)

This function allows you to generate a hardware interrupt. This event is external to the CPU and generally activated by an external device. The possible values for the parameter level are between 1 and 7, both inclusive.

For vector the values are the following:

> M68K_AUTOVECTORED_IRQ (-1): Autovectored interrupt.
> M68K_SPURIOUS_IRQ (-2): Spurious interrupt.
> 0-255: Vector number.

Return value:

> M68K_OK (0): Success.
> M68K_INT_LEVEL_ERROR (-1): The function failures because there is another interrupt activated at the given level.
> M68K_INT_INV_PARAMS (-2): Invalid parameter values. The vector value is not valid or the level is equal to zero.

int m68k_lower_irq (int level)

This function is used to deactivate an interrupt.

Return value:

> M68K_OK (0): The interrupt has been deactivated successfully.
> M68K_IRQ_LEVEL_ERROR (-1): The function failures because the interrupt is not activated.
> M68K_IRQ_INV_PARAMS (-2): Invalid interrupt level value.

- int m68k_get_irq_vector (int level)

Calling this function you will get the vector of a generated interrupt at the given interrupt level.

Return value:

> > -1: Requested interrupt vector.
> M68K_IRQ_LEVEL_ERROR (-1): The function failures because the interrupt is not activated.
> M68K_IRQ_INV_PARAMS (-2): Invalid interrupt level.

- int m68k_change_irq_vector (int level, int vector)

It allows you to change the vector of a generated interrupt. Remember that the interrupt must be already activated when you call this function.
The possible values for vector are between 0 and 255, both inclusive.

Return value:

> M68K_OK (0): Success.
> M68K_IRQ_LEVEL_ERROR (-1): The interrupt at the given vector was not activated.
> M68K_IRQ_INV_PARAMS (-2): Invalid interrupt vector value.

6.3. CPU context handling functions

These functions are intented for handling the CPU context.

- int m68k_get_context_size (void)

Returns the size in bytes of the CPU context.

- void m68k_get_context (void *context)

Fills the context pointed by the pointer with the current CPU context. You must deserve memory space in order to allocate the CPU context.

- void m68k_set_context (void *context)

Allows you to set up the CPU context. The parameter is a pointer to the context structure.

- int m68k_get_register (m68k_register reg)

Returns the value of the specified register. If the value of the reg parameter is not valid, the function will return -1.

Note that the value returned by the function when the register specified is not valid (-1) is a valid 32-bit register value. This may be cause for concern.

- int m68k_set_register (m68k_register reg, unsigned value)

Sets the value of the specified register.

Return values:

> M68K_OK (0): Success.
> M68K_INV_REG (-1): The register specified is not valid.

6.4. Timing functions

These functions allows you to control the CPU cycles executed in the emulation. This way, you can adjust the emulation speed. The cycles_counter is the variable used in the library to count the CPU cycles. For each calling to function emulate, the executed CPU cycles are added to cycles_counter.

- unsigned m68k_get_cycles_counter (void)

Returns the current value of the cycles_counter.

- unsigned m68k_trip_cycles_counter (void)

Returns the current value of the cycles_counter variable and resets it to zero.

- unsigned m68k_control_cycles_counter (int n)

If the parameter n is equal to zero, the function returns the cycles_counter.
Otherwise, it returns the cycles_counter resetting it to zero.

- void m68k_release_timeslice (void)

Calling this function you will request the CPU to finish its execution as soon as possible. The premature exit will be reflected in the cycles_counter.

- void m68k_add_cycles (int cycles)

Call this function when you want to increase the clock cycles counting (cycles_counter variable).
This function could be useful when emulating systems equipped with DMA capabilities, keeping track of how many clock cycles the CPU was frozen by any device doing a DMA operation.

- void m68k_release_cycles (int cycles)

Call this function when you want to decrease the clock cycles counting (cycles_counter variable).

7. Multi-CPU systems

Emulating multiple 68000 processors is fairly simple. If you want to emulate more than one 68000 processor, you have to set up a CPU context and a memory map for each one (see memory map example).

For example, you would do this:

> struct M68K_CONTEXT my_contexts[NUMBER_OF_PROCESSORS];

> for (int i = 0; i < NUMBER_OF_PROCESSORS; i++)
> {
> m68k_set_context(&my_contexts[i]);
> m68k_emulate(100);
> m68k_get_context(&my_contexts[i]);
> }

Try to compensate the overhead due to the copying of the contexts emulating the CPUs in large timeslices.

FAME is non-reentrant so you cannot multi-thread several processors. If you need FAME running in this way, contact me.

8. Helpful tips

- It is recommended to use built-in memory handlers as much as possible because they should be much faster than others coded into high level languages.

- Use timeslices as large as possible because this way you will reduce the overhead produced by the entry and exit code of the library.

- Try to avoid context swapping. It will reduce performance notably.

- It is a good idea to call the emulate function with a variable number of cycles instead of a fixed one. Keep track of how many cycles overflowed from the last call to emulate and subtract them in the next calling:

> #define CPU_TIMESLICE 100

> cpu_context.cycles_counter = 0;
> while(!done)
> {
> if (cpu_context.cycles_counter < CPU_TIMESLICE)
> {
> m68k_emulate(CPU_TIMESLICE - cpu_context.cycles_counter);
> }
> cpu_context.cycles_counter -= CPU_TIMESLICE;
> }

- Library routines were designed with accuracy and speed in mind. Use them as much as possible in order to reach a fast and accurate emulated system.

- The object code contains many symbols for program relocation. Strip your executable when you are done.

9. Troubleshooting

This section tries to help you to get the library working correctly. I hope you find this section useful.

- Remember to call init function before any other function library. It initialize the library setting up the emulator.

- You must call reset function before starting the emulation in order to get the library working appropriately.

- Set up your memory map before reset the CPU. The reset function look up the vector table.

- Ensure that the CPU context has been set correctly after the calling to set_context.

- Check if memory maps are well-constructed. Every memory region must be 4 KB aligned. This is a common pitfall.

- Check if your emulated processor is accessing memory correctly specially when you have to use memory handling functions. Take a look at the Memory handling section if you are having problems in this point.

- Make sure to set reset_handler, iack_handler and icust_handler to NULL if you are not using these features. It would be a good idea to set every byte of a new context to zero to avoid any problem.

- Remember to set every handler not used in the array of function pointers (icust_handler) to NULL to avoid undesired results.

- Remember to include fame.h in any C module that use FAME. This header file is subject to change in future versions.

- Make sure to instruct your compiler configuration to treat enum types as 32-bit ints when using m68k_get_register and m68k_set_register functions.

10. Known bugs

- The bit I/N (specific information about the processor activity) saved on the supervisor stack when an address or bus error happens is not calculated and its value is fixed to one (instruction). This tiny detail will be implemented in future versions if needed.

11. Special thanks

Many thanks go out to those who helped me out with this library or contributed to the project in any form in no special order.

- Chui for his invaluable work to get this thing up into his NeoGeo emulator (Neo4All) and for helping me to fix loads of errors.
- Bart Trzynadlowski (trzynadl@unr.nevada.edu) for his notes about 68000 undocumented behavior.
- Julio César Álvarez Acosta (julio_a_a@yahoo.es) for his help to build the import library.
- Richard Hollstein for let me know that memory handling functions were not documented in previous releases.
- Jorge Cwik for figuring out the algorithm to calculate the exact number of cycles in DIV instructions.
- Neill Corlett for his excellent Starscream 680x0 emulation library which give me a lot of understandings and ideas on CPU emulation.
- Stéphane Dallongeville for Gens (probably the best Genesis/Mega Drive emulator ever programmed) and for giving me his opinion about several aspects of 68000 emulation.
- BlackAura and Ian Micheal for telling me about the high level emulation (HLE) feature.
- Juan Carlos Hernández Martín (jmartin@uax.es) for his interest in this project.
- Antonio García Guerra for his great book Sistemas Digitales.
- The creators of the 68000 microprocessor, because without their work nothing of this might be a reality.
Thank you too! for your interest in the library. If you have any suggestions, comments or contributions do not hesitate to get in contact with me.

Have a nice day!