├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── VDP.h ├── hqx ├── common.h ├── hq2x.c ├── hq3x.c ├── hq4x.c ├── hqx.h └── init.c ├── images ├── kiwi.gif ├── pad.png ├── pad2.png └── pad_big.png ├── input.c ├── input.h ├── kiwi.py ├── m68k ├── history.txt ├── m68k.h ├── m68k_in.c ├── m68kconf.h ├── m68kcpu.c ├── m68kcpu.h ├── m68kdasm.c ├── m68kmake.c ├── m68kopac.c ├── m68kopdm.c ├── m68kopnz.c ├── m68kops.c ├── m68kops.h └── readme.txt ├── macos_app.py ├── megadrive.c ├── scale.c ├── tests ├── __init__.py ├── ristar │ ├── Ristar (UE) [!].zip │ ├── __init__.py │ ├── debug-output.txt │ ├── ristar-screenshot-1x-epx.bmp │ ├── ristar-screenshot-1x-hqx.bmp │ ├── ristar-screenshot-1x-none.bmp │ ├── ristar-screenshot-2x-epx.bmp │ ├── ristar-screenshot-2x-hqx.bmp │ ├── ristar-screenshot-2x-none.bmp │ ├── ristar-screenshot-3x-epx.bmp │ ├── ristar-screenshot-3x-hqx.bmp │ ├── ristar-screenshot-3x-none.bmp │ ├── ristar-screenshot-4x-epx.bmp │ ├── ristar-screenshot-4x-hqx.bmp │ ├── ristar-screenshot-4x-none.bmp │ └── test_ristar.py └── test_gui.py ├── vdp.c ├── z80.c └── z80.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.sw? 3 | *.o 4 | *.so 5 | *.pyc 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | cache: pip 3 | python: 4 | - "2.7" 5 | before_install: 6 | - pip install cpp-coveralls # has to be installed before coveralls because of binary shadowing 7 | - pip install coveralls 8 | - "export DISPLAY=:99.0" 9 | - "sh -e /etc/init.d/xvfb start" 10 | - sleep 3 # sleep so xvfb can start 11 | install: 12 | - pip install PySide pytest-qt pytest-mock 13 | - make COVERAGE=1 14 | script: coverage run --source . -m py.test 15 | after_success: 16 | - cpp-coveralls --exclude m68k --exclude hqx --gcov-options '\-lp' --dump cpp.json 17 | - coveralls --merge=cpp.json 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Luke Zapart 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | 9 | 10 | 11 | The hqx library. 12 | 13 | Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com ) 14 | Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net) 15 | 16 | See hqx/hqx.h for license details (GPL 2.1) 17 | 18 | 19 | The Musashi library. 20 | 21 | The Musashi M680x0 emulator is copyright 1998-2001 Karl Stenerud. 22 | 23 | See m68k/readme.txt for license details (a simple attribution non-commercial license). 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OS := $(shell uname) 2 | 3 | CC = cc 4 | WARNINGS = -Wall -pedantic -Wno-unused-function 5 | CFLAGS = $(WARNINGS) -c -Im68k -I. -O2 --std=c99 -fPIC 6 | CFLAGS_M68K = $(WARNINGS) -c -Im68k -I. -O2 --std=c99 -fPIC 7 | LDFLAGS = -shared 8 | 9 | ifdef COVERAGE 10 | ifeq ($(OS),Darwin) 11 | CFLAGS += -fprofile-instr-generate -fcoverage-mapping 12 | LDFLAGS += -fprofile-instr-generate 13 | else 14 | CFLAGS += -fprofile-arcs -ftest-coverage 15 | LDFLAGS += -lgcov 16 | endif 17 | endif 18 | 19 | all: megadrive.so 20 | 21 | clean: 22 | rm megadrive.so *.o m68k/*.o hqx/*.o m68k/m68kops.h m68k/m68kmake 23 | 24 | megadrive.so: m68k/m68kcpu.o m68k/m68kops.o m68k/m68kopac.o m68k/m68kopdm.o m68k/m68kopnz.o m68k/m68kdasm.o megadrive.o vdp.o input.o scale.o z80.o hqx/init.o hqx/hq2x.o hqx/hq3x.o hqx/hq4x.o 25 | @echo "Linking megadrive.so" 26 | @$(CC) m68k/m68kcpu.o m68k/m68kops.o m68k/m68kopac.o m68k/m68kopdm.o m68k/m68kopnz.o m68k/m68kdasm.o megadrive.o vdp.o input.o scale.o z80.o hqx/init.o hqx/hq2x.o hqx/hq3x.o hqx/hq4x.o $(LDFLAGS) -o megadrive.so 27 | 28 | %.o: %.c 29 | @echo "Compiling $<" 30 | $(CC) $(CFLAGS) $^ -std=c99 -o $@ 31 | 32 | m68k/m68kcpu.o: m68k/m68kops.h m68k/m68k.h m68k/m68kconf.h m68k/m68kcpu.c 33 | @echo "Compiling m68k/m68kcpu.c" 34 | $(CC) $(CFLAGS_M68K) m68k/m68kcpu.c -o m68k/m68kcpu.o 35 | 36 | m68k/m68kdasm.o: m68k/m68kdasm.c m68k/m68k.h m68k/m68kconf.h 37 | @echo "Compiling m68k/m68kdasm.c" 38 | @$(CC) $(CFLAGS_M68K) m68k/m68kdasm.c -o m68k/m68kdasm.o 39 | 40 | m68k/m68kops.o: m68k/m68kmake m68k/m68kops.h m68k/m68kops.c m68k/m68k.h m68k/m68kconf.h 41 | @echo "Compiling m68k/m68kops.c" 42 | @$(CC) $(CFLAGS_M68K) m68k/m68kops.c -o m68k/m68kops.o 43 | 44 | m68k/m68kopac.o: m68k/m68kmake m68k/m68kops.h m68k/m68kopac.c m68k/m68k.h m68k/m68kconf.h 45 | @echo "Compiling m68k/m68kopac.c" 46 | @$(CC) $(CFLAGS_M68K) m68k/m68kopac.c -o m68k/m68kopac.o 47 | 48 | m68k/m68kopdm.o: m68k/m68kmake m68k/m68kops.h m68k/m68kopdm.c m68k/m68k.h m68k/m68kconf.h 49 | @echo "Compiling m68k/m68kopdm.c" 50 | @$(CC) $(CFLAGS_M68K) m68k/m68kopdm.c -o m68k/m68kopdm.o 51 | 52 | m68k/m68kopnz.o: m68k/m68kmake m68k/m68kops.h m68k/m68kopnz.c m68k/m68k.h m68k/m68kconf.h 53 | @echo "Compiling m68k/m68kopnz.c" 54 | @$(CC) $(CFLAGS_M68K) m68k/m68kopnz.c -o m68k/m68kopnz.o 55 | 56 | m68k/m68kops.h: m68k/m68kmake 57 | @echo "Generating m68k/m68kops.h" 58 | @m68k/m68kmake m68k m68k/m68k_in.c >/dev/null 59 | 60 | m68k/m68kmake: m68k/m68kmake.c m68k/m68k_in.c 61 | @echo "Compiling m68k/m68kmake" 62 | @$(CC) $(WARNINGS) m68k/m68kmake.c -o m68k/m68kmake 63 | 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kiwi   [![Coveralls](https://img.shields.io/coveralls/drx/kiwi.svg)](https://coveralls.io/github/drx/kiwi) 2 | 3 | Kiwi is a simple Sega Genesis emulator. 4 | 5 |

Kiwi in action
6 | 7 | ### Quick start 8 | 9 | #### Binary 10 | * [macOS app](https://github.com/drx/kiwi/releases/download/v0.1-alpha/kiwi-v0.1-alpha-macos.zip) 11 | 12 | #### Running from source 13 | 1. `$ git clone git://github.com/drx/kiwi.git` or [download the latest version](https://github.com/drx/kiwi/zipball/master) 14 | 2. `$ brew install qt@4` (macOS)
`$ sudo apt-get install libqt4-dev` (Linux) 15 | 2. `$ pip install PySide` 16 | 2. `$ make` 17 | 3. `$ ./kiwi.py` 18 | 19 | ### Requirements 20 | 21 | * clang/gcc, make, Python 2.7 22 | * Qt 4, PySide 23 | 24 | ### Acknowledgements 25 | 26 | * The [hqx library](http://code.google.com/p/hqx/) is by Maxim Stepin and Cameron Zemek 27 | * The Musashi library is by Karl Stenerud 28 | 29 | ### Notes 30 | 31 | * Kiwi was written in 2013, with some bugs fixed and tests added more recently. 32 | * There is no sound support. The Z80 processor is handled by dummy code. Some games do not work at all because of this. 33 | * Tested on Linux and macOS. 34 | * The bundled macOS doesn't work on some machines, in which case you have to run from source. 35 | -------------------------------------------------------------------------------- /VDP.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Megadrive VDP emulation 3 | */ 4 | 5 | void draw_cell_pixel(unsigned int cell, int cell_x, int cell_y, int x, int y); 6 | void vdp_render_bg(int line, int priority); 7 | void vdp_render_sprite(int sprite_index, int line); 8 | void vdp_render_sprites(int line, int priority); 9 | void vdp_render_line(int line); 10 | 11 | void vdp_set_buffers(unsigned char *screen_buffer, unsigned char *scaled_buffer); 12 | void vdp_debug_status(char *s); 13 | 14 | enum ram_type { 15 | T_VRAM, 16 | T_CRAM, 17 | T_VSRAM 18 | }; 19 | 20 | void vdp_data_write(unsigned int value, enum ram_type type, int dma); 21 | void vdp_data_port_write(unsigned int value); 22 | void vdp_set_reg(int reg, unsigned char value); 23 | unsigned int vdp_get_reg(int reg); 24 | void vdp_control_write(unsigned int value); 25 | void vdp_write(unsigned int address, unsigned int value); 26 | unsigned int vdp_read(unsigned int address); 27 | 28 | unsigned int vdp_get_status(); 29 | unsigned short vdp_get_cram(int index); 30 | 31 | void vdp_set_hblank(); 32 | void vdp_clear_hblank(); 33 | void vdp_set_vblank(); 34 | void vdp_clear_vblank(); 35 | -------------------------------------------------------------------------------- /hqx/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com ) 3 | * 4 | * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net) 5 | * Copyright (C) 2011 Francois Gannaz 6 | * 7 | * This program is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2.1 of the License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public 18 | * License along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | */ 21 | 22 | #ifndef __HQX_COMMON_H_ 23 | #define __HQX_COMMON_H_ 24 | 25 | #include 26 | #include 27 | 28 | #define MASK_2 0x0000FF00 29 | #define MASK_13 0x00FF00FF 30 | #define MASK_RGB 0x00FFFFFF 31 | #define MASK_ALPHA 0xFF000000 32 | 33 | #define Ymask 0x00FF0000 34 | #define Umask 0x0000FF00 35 | #define Vmask 0x000000FF 36 | #define trY 0x00300000 37 | #define trU 0x00000700 38 | #define trV 0x00000006 39 | 40 | /* RGB to YUV lookup table */ 41 | extern uint32_t RGBtoYUV[16777216]; 42 | 43 | static inline uint32_t rgb_to_yuv(uint32_t c) 44 | { 45 | // Mask against MASK_RGB to discard the alpha channel 46 | return RGBtoYUV[MASK_RGB & c]; 47 | } 48 | 49 | /* Test if there is difference in color */ 50 | static inline int yuv_diff(uint32_t yuv1, uint32_t yuv2) { 51 | return (( abs((yuv1 & Ymask) - (yuv2 & Ymask)) > trY ) || 52 | ( abs((yuv1 & Umask) - (yuv2 & Umask)) > trU ) || 53 | ( abs((yuv1 & Vmask) - (yuv2 & Vmask)) > trV ) ); 54 | } 55 | 56 | static inline int Diff(uint32_t c1, uint32_t c2) 57 | { 58 | return yuv_diff(rgb_to_yuv(c1), rgb_to_yuv(c2)); 59 | } 60 | 61 | /* Interpolate functions */ 62 | static inline uint32_t Interpolate_2(uint32_t c1, int w1, uint32_t c2, int w2, int s) 63 | { 64 | if (c1 == c2) { 65 | return c1; 66 | } 67 | return 68 | (((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2) << (24-s)) & MASK_ALPHA) + 69 | ((((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2) >> s) & MASK_2) + 70 | ((((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2) >> s) & MASK_13); 71 | } 72 | 73 | static inline uint32_t Interpolate_3(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s) 74 | { 75 | return 76 | (((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2 + ((c3 & MASK_ALPHA) >> 24) * w3) << (24-s)) & MASK_ALPHA) + 77 | ((((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2 + (c3 & MASK_2) * w3) >> s) & MASK_2) + 78 | ((((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2 + (c3 & MASK_13) * w3) >> s) & MASK_13); 79 | } 80 | 81 | static inline void Interp1(uint32_t * pc, uint32_t c1, uint32_t c2) 82 | { 83 | //*pc = (c1*3+c2) >> 2; 84 | *pc = Interpolate_2(c1, 3, c2, 1, 2); 85 | } 86 | 87 | static inline void Interp2(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) 88 | { 89 | //*pc = (c1*2+c2+c3) >> 2; 90 | *pc = Interpolate_3(c1, 2, c2, 1, c3, 1, 2); 91 | } 92 | 93 | static inline void Interp3(uint32_t * pc, uint32_t c1, uint32_t c2) 94 | { 95 | //*pc = (c1*7+c2)/8; 96 | *pc = Interpolate_2(c1, 7, c2, 1, 3); 97 | } 98 | 99 | static inline void Interp4(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) 100 | { 101 | //*pc = (c1*2+(c2+c3)*7)/16; 102 | *pc = Interpolate_3(c1, 2, c2, 7, c3, 7, 4); 103 | } 104 | 105 | static inline void Interp5(uint32_t * pc, uint32_t c1, uint32_t c2) 106 | { 107 | //*pc = (c1+c2) >> 1; 108 | *pc = Interpolate_2(c1, 1, c2, 1, 1); 109 | } 110 | 111 | static inline void Interp6(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) 112 | { 113 | //*pc = (c1*5+c2*2+c3)/8; 114 | *pc = Interpolate_3(c1, 5, c2, 2, c3, 1, 3); 115 | } 116 | 117 | static inline void Interp7(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) 118 | { 119 | //*pc = (c1*6+c2+c3)/8; 120 | *pc = Interpolate_3(c1, 6, c2, 1, c3, 1, 3); 121 | } 122 | 123 | static inline void Interp8(uint32_t * pc, uint32_t c1, uint32_t c2) 124 | { 125 | //*pc = (c1*5+c2*3)/8; 126 | *pc = Interpolate_2(c1, 5, c2, 3, 3); 127 | } 128 | 129 | static inline void Interp9(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) 130 | { 131 | //*pc = (c1*2+(c2+c3)*3)/8; 132 | *pc = Interpolate_3(c1, 2, c2, 3, c3, 3, 3); 133 | } 134 | 135 | static inline void Interp10(uint32_t * pc, uint32_t c1, uint32_t c2, uint32_t c3) 136 | { 137 | //*pc = (c1*14+c2+c3)/16; 138 | *pc = Interpolate_3(c1, 14, c2, 1, c3, 1, 4); 139 | } 140 | 141 | #endif 142 | -------------------------------------------------------------------------------- /hqx/hqx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com ) 3 | * 4 | * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net) 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #ifndef __HQX_H_ 22 | #define __HQX_H_ 23 | 24 | #include 25 | 26 | #if defined( __GNUC__ ) 27 | #ifdef __MINGW32__ 28 | #define HQX_CALLCONV __stdcall 29 | #else 30 | #define HQX_CALLCONV 31 | #endif 32 | #else 33 | #define HQX_CALLCONV 34 | #endif 35 | 36 | #if defined(_WIN32) 37 | #ifdef DLL_EXPORT 38 | #define HQX_API __declspec(dllexport) 39 | #else 40 | #define HQX_API __declspec(dllimport) 41 | #endif 42 | #else 43 | #define HQX_API 44 | #endif 45 | 46 | HQX_API void HQX_CALLCONV hqxInit(void); 47 | HQX_API void HQX_CALLCONV hq2x_32( uint32_t * src, uint32_t * dest, int width, int height ); 48 | HQX_API void HQX_CALLCONV hq3x_32( uint32_t * src, uint32_t * dest, int width, int height ); 49 | HQX_API void HQX_CALLCONV hq4x_32( uint32_t * src, uint32_t * dest, int width, int height ); 50 | 51 | HQX_API void HQX_CALLCONV hq2x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height ); 52 | HQX_API void HQX_CALLCONV hq3x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height ); 53 | HQX_API void HQX_CALLCONV hq4x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height ); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /hqx/init.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net) 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 2.1 of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | */ 18 | 19 | #include 20 | #include "hqx.h" 21 | 22 | uint32_t RGBtoYUV[16777216]; 23 | uint32_t YUV1, YUV2; 24 | 25 | HQX_API void HQX_CALLCONV hqxInit(void) 26 | { 27 | /* Initalize RGB to YUV lookup table */ 28 | uint32_t c, r, g, b, y, u, v; 29 | for (c = 0; c < 16777215; c++) { 30 | r = (c & 0xFF0000) >> 16; 31 | g = (c & 0x00FF00) >> 8; 32 | b = c & 0x0000FF; 33 | y = (uint32_t)(0.299*r + 0.587*g + 0.114*b); 34 | u = (uint32_t)(-0.169*r - 0.331*g + 0.5*b) + 128; 35 | v = (uint32_t)(0.5*r - 0.419*g - 0.081*b) + 128; 36 | RGBtoYUV[c] = (y << 16) + (u << 8) + v; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /images/kiwi.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/images/kiwi.gif -------------------------------------------------------------------------------- /images/pad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/images/pad.png -------------------------------------------------------------------------------- /images/pad2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/images/pad2.png -------------------------------------------------------------------------------- /images/pad_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/images/pad_big.png -------------------------------------------------------------------------------- /input.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "input.h" 3 | 4 | /* 5 | * Sega Megadrive joypad support 6 | */ 7 | 8 | enum pad_button 9 | { 10 | PAD_UP, 11 | PAD_DOWN, 12 | PAD_LEFT, 13 | PAD_RIGHT, 14 | PAD_B, 15 | PAD_C, 16 | PAD_A, 17 | PAD_S 18 | }; 19 | 20 | unsigned short button_state[3]; 21 | unsigned short pad_state[3]; 22 | unsigned char io_reg[16] = {0xa0, 0x7f, 0x7f, 0x7f, 0, 0, 0, 0xff, 0, 0, 0xff, 0, 0, 0xff, 0, 0}; /* initial state */ 23 | 24 | void pad_press_button(int pad, int button) 25 | { 26 | button_state[pad] |= (1<> 2) & 0x30)); 56 | } 57 | return value; 58 | } 59 | 60 | void io_write_memory(unsigned int address, unsigned int value) 61 | { 62 | address >>= 1; 63 | 64 | if (address >= 0x1 && address < 0x4) 65 | { 66 | /* port data */ 67 | io_reg[address] = value; 68 | pad_write(address-1, value); 69 | return; 70 | } 71 | else if (address >= 0x4 && address < 0x7) 72 | { 73 | /* port ctrl */ 74 | if (io_reg[address] != value) 75 | { 76 | io_reg[address] = value; 77 | pad_write(address-4, io_reg[address-3]); 78 | } 79 | return; 80 | } 81 | 82 | printf("io_write_memory(%x, %x)\n", address, value); 83 | } 84 | 85 | unsigned int io_read_memory(unsigned int address) 86 | { 87 | address >>= 1; 88 | 89 | if (address >= 0x1 && address < 0x4) 90 | { 91 | unsigned char mask = 0x80 | io_reg[address+3]; 92 | unsigned char value; 93 | value = io_reg[address] & mask; 94 | value |= pad_read(address-1) & ~mask; 95 | return value; 96 | } 97 | else 98 | { 99 | return io_reg[address]; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /input.h: -------------------------------------------------------------------------------- 1 | void io_write_memory(unsigned int address, unsigned int value); 2 | unsigned int io_read_memory(unsigned int address); 3 | -------------------------------------------------------------------------------- /kiwi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import re 4 | import sys 5 | from ctypes import * 6 | from zipfile import is_zipfile, ZipFile 7 | from collections import OrderedDict 8 | from PySide.QtCore import * 9 | from PySide.QtGui import * 10 | 11 | ''' 12 | The Kiwi emulator 13 | ''' 14 | 15 | md = CDLL('./megadrive.so') 16 | render_filters = ('None', 'EPX', 'hqx') 17 | 18 | screen_buffer = create_string_buffer(320*240*4) # allocate screen buffer 19 | 20 | # keyboard mappings 21 | buttons = ['up', 'down', 'left', 'right', 'b', 'c', 'a', 'start'] 22 | keymap = OrderedDict(( 23 | ('left', (Qt.Key_Left, Qt.Key_J)), 24 | ('right', (Qt.Key_Right, Qt.Key_L)), 25 | ('up', (Qt.Key_Up, Qt.Key_I)), 26 | ('down', (Qt.Key_Down, Qt.Key_K)), 27 | ('a', (Qt.Key_Z, Qt.Key_F)), 28 | ('b', (Qt.Key_X, Qt.Key_G)), 29 | ('c', (Qt.Key_C, Qt.Key_H)), 30 | ('start', (Qt.Key_Q, Qt.Key_W)), 31 | )) 32 | 33 | keymap_r = {} 34 | for button, keys in keymap.items(): 35 | keymap_r[keys[0]] = (button, 0) 36 | keymap_r[keys[1]] = (button, 1) 37 | 38 | def blit_screen(label, scaled_buffer, zoom_level): 39 | ''' 40 | Blits the screen to a QLabel. 41 | 42 | Creates a QImage from a RGB32 formatted buffer, creates a QPixmap from the QImage 43 | and loads the pixmap into a QLabel. 44 | ''' 45 | image = QImage(scaled_buffer, 320*zoom_level, 240*zoom_level, QImage.Format_RGB32) 46 | pixmap = QPixmap.fromImage(image) 47 | 48 | label.setPixmap(pixmap) 49 | 50 | class Controllers(QLabel): 51 | ''' 52 | Controller mapping help window. 53 | ''' 54 | def __init__(self, parent=None): 55 | super(Controllers, self).__init__(parent) 56 | 57 | text = '
' 58 | text += '' 59 | text += '' 60 | for button, keys in keymap.items(): 61 | text += ''.format(button.title(), QKeySequence(keys[0]).toString(), QKeySequence(keys[1]).toString()) 62 | text += '
ButtonPlayer 1 KeyPlayer 2 Key
{0}{1}{2}
' 63 | self.setText(text) 64 | self.setWindowTitle('Controllers') 65 | 66 | class Display(QWidget): 67 | ''' 68 | The main window. 69 | 70 | Handles the menu bar, keyboard input and displays the screen. 71 | ''' 72 | def __init__(self, parent=None): 73 | super(Display, self).__init__(parent) 74 | 75 | self.parent = parent 76 | self.frames = 0 77 | self.pause_emulation = False 78 | self.zoom_level = 2 79 | self.render_filter = 'None' 80 | self.rom_fn = '' 81 | self.debug = False 82 | 83 | self.set_vdp_buffers() 84 | 85 | timer = QTimer(self) 86 | timer.timeout.connect(self.frame) 87 | timer.start(16.667) 88 | self.timer = timer 89 | self.turbo = False 90 | 91 | self.last_fps_time = QTime.currentTime() 92 | from collections import deque 93 | self.frame_times = deque([20], 1000) 94 | 95 | self.debug_label = QLabel() 96 | self.debug_label.font = QFont("Menlo", 16) 97 | self.debug_label.font.setStyleHint(QFont.TypeWriter) 98 | self.debug_label.setFont(self.debug_label.font) 99 | 100 | self.palette_debug = PaletteDebug() 101 | 102 | self.label = QLabel("Welcome to Kiwi!") 103 | self.label.setAlignment(Qt.AlignCenter) 104 | self.label.setStyleSheet("background-color: #222; color: #ddd; font-family: Verdana") 105 | self.label.show() 106 | 107 | self.qba = QByteArray() 108 | self.frame() 109 | self.setWindowTitle("Kiwi") 110 | 111 | layout = QGridLayout() 112 | layout.setRowMinimumHeight(0, 480) 113 | layout.setColumnMinimumWidth(1, 630) 114 | layout.addWidget(self.debug_label, 0, 0) 115 | layout.addWidget(self.palette_debug, 1, 0) 116 | layout.addWidget(self.label, 0, 1, 1, 2) 117 | layout.setContentsMargins(0, 0, 0, 0) 118 | self.debug_label.hide() 119 | self.palette_debug.hide() 120 | self.setLayout(layout) 121 | self.layout = layout 122 | self.create_menus() 123 | 124 | 125 | def create_menus(self): 126 | ''' 127 | Create the menu bar. 128 | ''' 129 | self.menubar = self.parent.menuBar() 130 | file_menu = self.menubar.addMenu('&File') 131 | options_menu = self.menubar.addMenu('&Options') 132 | help_menu = self.menubar.addMenu('&Help') 133 | zoom_menu = options_menu.addMenu('Video zoom') 134 | render_menu = options_menu.addMenu('Rendering filter') 135 | file_menu.addAction('Open ROM', self, SLOT('open_file()'), QKeySequence.Open) 136 | file_menu.addSeparator() 137 | file_menu.addAction('Pause emulation', self, SLOT('toggle_pause()'), QKeySequence('Ctrl+P')).setCheckable(True) 138 | file_menu.addAction('Reset emulation', self, SLOT('reset_emulation()'), QKeySequence('Ctrl+R')) 139 | file_menu.addSeparator() 140 | file_menu.addAction('Quit', self, SLOT('quit()'), QKeySequence.Quit) 141 | 142 | zoom_group = QActionGroup(self) 143 | zoom_group.triggered.connect(self.set_zoom_level) 144 | for zoom_level in ('1x', '2x', '3x', '4x'): 145 | action = QAction(zoom_level, zoom_group) 146 | action.setCheckable(True) 147 | zoom_menu.addAction(action) 148 | if zoom_level == '2x': 149 | action.setChecked(True) 150 | 151 | render_group = QActionGroup(self) 152 | render_group.triggered.connect(self.set_render_filter) 153 | for i, render_filter in enumerate(render_filters): 154 | action = QAction(render_filter, render_group) 155 | if i < 9: 156 | action.setShortcut(QKeySequence('Ctrl+{0}'.format(i+1))) 157 | action.setCheckable(True) 158 | render_menu.addAction(action) 159 | if render_filter == 'None': 160 | action.setChecked(True) 161 | 162 | options_menu.addAction('Save screenshot', self, SLOT('save_screenshot()'), QKeySequence('Ctrl+S')) 163 | options_menu.addAction('Show debug information', self, SLOT('toggle_debug()'), QKeySequence('Ctrl+D')).setCheckable(True) 164 | 165 | help_menu.addAction('Controllers', self, SLOT('show_controllers()'), QKeySequence('Ctrl+I')) 166 | 167 | @Slot() 168 | def quit(self): 169 | app.quit() 170 | 171 | @Slot() 172 | def show_controllers(self): 173 | self.controllers_window = Controllers() 174 | self.controllers_window.show() 175 | 176 | 177 | @Slot() 178 | def toggle_debug(self): 179 | self.debug = not self.debug 180 | self.debug_label.setVisible(self.debug) 181 | self.palette_debug.setVisible(self.debug) 182 | self.layout.setRowMinimumHeight(1, 100 if self.debug else 0) 183 | if self.debug: 184 | self.layout.setContentsMargins(10, 10, 10, 10) 185 | else: 186 | self.layout.setContentsMargins(0, 0, 0, 0) 187 | 188 | self.adjustSize() 189 | 190 | @Slot() 191 | def set_render_filter(self, action): 192 | render_filter = action.text() 193 | if render_filter not in render_filters: 194 | return 195 | 196 | self.render_filter = render_filter 197 | 198 | 199 | @Slot() 200 | def set_zoom_level(self, action): 201 | try: 202 | self.zoom_level = {'1x': 1, '2x': 2, '3x': 3, '4x': 4}[action.text()] 203 | self.layout.setRowMinimumHeight(0, self.zoom_level*240) 204 | self.layout.setColumnMinimumWidth(1, self.zoom_level*320-10) 205 | self.set_vdp_buffers() 206 | 207 | except KeyError: 208 | pass 209 | 210 | def set_vdp_buffers(self): 211 | ''' 212 | Allocate buffer for upscaled image and set the VDP buffers. 213 | ''' 214 | self.scaled_buffer = create_string_buffer(320*240*4*self.zoom_level*self.zoom_level) 215 | md.vdp_set_buffers(screen_buffer, self.scaled_buffer) 216 | 217 | @Slot() 218 | def open_file(self): 219 | ''' 220 | Open a ROM. 221 | ''' 222 | rom_fn, _ = QFileDialog.getOpenFileName(self, "Open ROM", os.getcwd(), "Sega Genesis ROMs (*.bin *.gen *.zip)") 223 | 224 | if not rom_fn: 225 | return 226 | 227 | self.rom_fn = rom_fn 228 | 229 | if is_zipfile(rom_fn): 230 | # if the file is a ZIP, try to open the largest file inside 231 | zipfile = ZipFile(rom_fn, 'r') 232 | contents = [(f.file_size, f.filename) for f in zipfile.infolist()] 233 | contents.sort(reverse=True) 234 | rom = zipfile.read(contents[0][1]) 235 | else: 236 | rom = open(rom_fn, 'r').read() 237 | md.set_rom(c_char_p(rom), len(rom)) 238 | self.reset_emulation() 239 | self.activateWindow() 240 | 241 | @Slot() 242 | def reset_emulation(self): 243 | md.m68k_pulse_reset() 244 | 245 | @Slot() 246 | def toggle_pause(self): 247 | self.pause_emulation = not self.pause_emulation 248 | 249 | @Slot() 250 | def save_screenshot(self, filename=None): 251 | screenshot_dir = './screenshots' 252 | if filename is None: 253 | try: 254 | os.mkdir(screenshot_dir) 255 | except OSError: 256 | pass 257 | 258 | file_list = os.listdir(screenshot_dir) 259 | max_index = 0 260 | 261 | # Find the screenshot with the highest index 262 | for fn in file_list: 263 | match = re.match('screenshot([0-9]+).png', fn) 264 | if match: 265 | index = int(match.group(1)) 266 | if index > max_index: 267 | max_index = index 268 | 269 | filename = os.path.join(screenshot_dir, 'screenshot{:04d}.png'.format(max_index+1)) 270 | 271 | buffer = self.scaled_buffer.raw 272 | image = QImage(buffer, 320*self.zoom_level, 240*self.zoom_level, QImage.Format_RGB32).copy() 273 | 274 | image.save(filename) 275 | 276 | return filename 277 | 278 | def show_fps(self): 279 | from itertools import islice 280 | values = [] 281 | for last_n in (1000, 100, 20): 282 | start = max(0, len(self.frame_times)-last_n) 283 | l = len(self.frame_times)-start 284 | q = islice(self.frame_times, start, None) 285 | values.append('{:.1f}'.format(1000.0/sum(q)*l)) 286 | return ' '.join(values) 287 | 288 | def frame(self): 289 | ''' 290 | Do a single frame. 291 | 292 | Perform a Megadrive frame, upscale the screen if necessary. 293 | Update debug information if debug is on. 294 | ''' 295 | if not self.pause_emulation and self.rom_fn: 296 | md.frame() 297 | self.frames += 1 298 | 299 | if self.debug: 300 | self.palette_debug.update() 301 | 302 | if self.rom_fn: 303 | md.scale_filter(c_char_p(self.render_filter), self.zoom_level) 304 | blit_screen(self.label, self.scaled_buffer.raw, self.zoom_level) 305 | self.adjustSize() 306 | self.parent.adjustSize() 307 | 308 | self.frame_times.append(self.last_fps_time.msecsTo(QTime.currentTime())) 309 | self.last_fps_time = QTime.currentTime() 310 | 311 | if self.debug: 312 | vdp_status = create_string_buffer(1024) 313 | md.vdp_debug_status(vdp_status) 314 | if self.frames % 2: 315 | self.debug_label.setText('Frame: {} (fps: {})\n\n{}\n\n{}'.format(self.frames, self.show_fps(), vdp_status.value, m68k_status())) 316 | 317 | 318 | def m68k_status(): 319 | ''' 320 | Shows the status of the M68K CPU. 321 | ''' 322 | registers = ['d0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 323 | 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 324 | 'pc', 'sr', 'sp', 'usp'] 325 | 326 | status = '' 327 | pc = 0 328 | for reg_i, register in enumerate(registers): 329 | if reg_i%4 == 0: 330 | status += '\n' 331 | value = md.m68k_get_reg(0, reg_i) 332 | status += '{0}={1:08x} '.format(register, value&0xffffffff) 333 | if register == 'pc': 334 | pc = value 335 | 336 | lines = [] 337 | for i in range(4): 338 | disasm = create_string_buffer(1024) 339 | old_pc = pc 340 | pc += md.m68k_disassemble(disasm, pc, 1) 341 | lines.append('{:06x}: {}'.format(old_pc, disasm.value.lower())) 342 | status = '> {}\n{}'.format('\n'.join(lines), status) 343 | 344 | return status 345 | 346 | class PaletteDebug(QWidget): 347 | ''' 348 | A widget that shows the current palette. 349 | ''' 350 | def __init__(self): 351 | super(PaletteDebug, self).__init__() 352 | self.show() 353 | 354 | def paintEvent(self, e): 355 | qp = QPainter() 356 | qp.begin(self) 357 | color = QColor(0, 0, 0, 0) 358 | qp.setPen(color) 359 | 360 | for y in range(4): 361 | for x in range(16): 362 | color = md.vdp_get_cram(y*16+x) 363 | red, green, blue = color >> 8, color >> 4, color 364 | red, green, blue = (blue&15)*16, (green&15)*16, (red&15)*16 365 | qp.setBrush(QColor(red, green, blue)) 366 | qp.drawRect(x*16, y*16, 16, 16) 367 | 368 | qp.end() 369 | 370 | 371 | class MainWindow(QMainWindow): 372 | def __init__(self, *args, **kwargs): 373 | QMainWindow.__init__(self, *args, **kwargs) 374 | self.display = Display(self) 375 | self.setCentralWidget(self.display) 376 | 377 | def keyPressEvent(self, event): 378 | try: 379 | key, pad = keymap_r[event.key()] 380 | md.pad_press_button(pad, buttons.index(key)) 381 | except KeyError: 382 | if event.key() == Qt.Key_Space: 383 | if self.display.pause_emulation: 384 | md.m68k_execute(7) 385 | else: 386 | self.display.turbo = not self.display.turbo 387 | self.display.timer.setInterval(4 if self.display.turbo else 16.67) 388 | else: 389 | super(MainWindow, self).keyPressEvent(event) 390 | 391 | def keyReleaseEvent(self, event): 392 | try: 393 | key, pad = keymap_r[event.key()] 394 | md.pad_release_button(pad, buttons.index(key)) 395 | except KeyError: 396 | super(MainWindow, self).keyReleaseEvent(event) 397 | 398 | 399 | if __name__ == '__main__': 400 | app = QApplication(sys.argv) 401 | main_window = MainWindow() 402 | main_window.show() 403 | main_window.raise_() 404 | app.exec_() 405 | -------------------------------------------------------------------------------- /m68k/history.txt: -------------------------------------------------------------------------------- 1 | The history of Musashi for anyone who might be interested: 2 | --------------------------------------------------------- 3 | 4 | Musashi was born out of sheer boredom. 5 | I needed something to code, and so having had fun with a few of the emulators 6 | around, I decided to try my hand at CPU emulation. 7 | I had owned an Amiga for many years and had done some assembly coding on it so 8 | I figured it would be the ideal chip to cut my teeth on. 9 | Had I known then how much work was involved in emulating a chip like this, I 10 | may not have even started ;-) 11 | 12 | 13 | 14 | 12-May-1998: First outline 15 | 16 | 11-Jun-1998: Early disassembler 17 | 18 | 20-Nov-1998: First prototype v0.1 19 | 20 | 04-Dec-1998: Final prototype v0.4 21 | 22 | 20-Dec-1998: Beta release of Musashi v0.5 that could run Rastan Saga under MAME 23 | (barely). 24 | 25 | 06-Jan-1999: Musashi 1.0 released 26 | 27 | 17-Mar-1999: Musashi 2.0 released 28 | - Major code overhaul. 29 | - Replaced monolithic codebase with a code generator program. 30 | - Added correct m68000 timing. 31 | - Moved timing into the opcode handlers. 32 | 33 | 25-Mar-1999: Musashi 2.1 released 34 | - Added support for m68010. 35 | - Many bugfixes. 36 | 37 | 13-May-1999: Musashi 2.2 released 38 | - Added support for m68020. 39 | - Lots of bugfixes. 40 | 41 | 05-Apr-2000: Musashi 3.0 released 42 | - Major code overhaul. 43 | - Rewrote code generator program and changed the format of 44 | m68k_in.c. 45 | - Added support for m68ec020. 46 | - Removed timing from the opcode handlers. 47 | - Added correct timing for m68000, m68010, and m68020. 48 | Note: 68020 timing is the cache timing from the manual. 49 | - Removed the m68k_peek_xxx() and m68k_poke_xxx() instructions and 50 | replaced them with m68k_get_reg() and m68k_set_reg(). 51 | - Added support for function codes. 52 | - Revamped m68kconf.h to be easier to configure and more powerful. 53 | - Added option to separate immediate and normal reads. 54 | - Added support for (undocumented) m68000 instruction prefetch. 55 | - Rewrote indexed addressing mode handling. 56 | - Rewrote interrupt handling. 57 | - Fixed a masking bug for m68k_get_reg() when requesting the PC. 58 | - Moved the instruction table sorting routine to m68kmake.c so 59 | that it is invoked at compile time rather than at runtime. 60 | - Rewrote the exception handling routines to support different 61 | stack frames (needed for m68020 emulation). 62 | - Rewrote faster status register and condition code flag handling 63 | functions / macros. 64 | - Fixed function code handling to fetch from program space when 65 | using pc-relative addressing. 66 | - Fixed initial program counter and stack pointer fetching on 67 | reset (loads from program space now). 68 | - A lot of code cleanup. 69 | - LOTS of bugfixes (especially in the m68020 code). 70 | 71 | 28-May-2000: Musashi 3.1 released 72 | - Fixed bug in m68k_get_reg() that retrieved the wrong value for 73 | the status register. 74 | - Fixed register bug in movec. 75 | - Fixed cpu type comparison problem that caused indexed 76 | addressing modes to be incorrectly interpreted when in m68ec020 77 | mode. 78 | - Added code to speed up busy waiting on some branch instructions. 79 | - Fixed some bfxxx opcode bugs. 80 | 81 | 14-Aug-2000: Musashi 3.2 released 82 | - Fixed RTE bug that killed the program counter when in m68020 83 | mode. 84 | - Minor fixes in negx and nbcd. 85 | - renamed d68k.c to m68kdasm.c and merged d68k.h into m68k.h. 86 | d68k_read_xxx() instructions have been renamed to 87 | m68k_read_xxx_disassembler(). 88 | - Rewrote exception processing and fixed 68020 stack frame 89 | problems. 90 | - FINALLY fixed the mull and divl instructions. 91 | - Added 64-bit safe code fixes. 92 | - Added 64-bit optimizations (these will only be ANSI compliant 93 | under c9x, and so to use them you must turn on M68K_USE_64_BIT 94 | in m68kconf.h). 95 | 96 | 27-Jan-2001: Musashi 3.3 released 97 | Note: This is the last release of Musashi before I separate the 98 | 68020 core. 99 | - Fixed problem when displaying negative numbers in disassembler 100 | - Fixed cpu type selector - was allowing 020 instructions to be 101 | disassembled when in 000 mode. 102 | - Fixed opcode jumptable generator (ambiguous operators in the 103 | test for f-line ops) 104 | - Fixed signed/unsigned problem in divl and mull opcodes (not 105 | sure if this was causing an error but best to be sure) 106 | - Cleaned up the naming scheme for the opcode handlers 107 | 108 | 02-Feb-2001: Musashi 3.3.1 released 109 | Note: due to the pc-relative requirement for some new drivers 110 | in MAME, I've released this small update. 111 | - Added pc-relative read modes 112 | - small optimizations to the exception handling that will help 113 | when splitting the cores 114 | - Updated the example (oops!) 115 | -------------------------------------------------------------------------------- /m68k/m68k.h: -------------------------------------------------------------------------------- 1 | #ifndef M68K__HEADER 2 | #define M68K__HEADER 3 | 4 | /* ======================================================================== */ 5 | /* ========================= LICENSING & COPYRIGHT ======================== */ 6 | /* ======================================================================== */ 7 | /* 8 | * MUSASHI 9 | * Version 3.3 10 | * 11 | * A portable Motorola M680x0 processor emulation engine. 12 | * Copyright 1998-2001 Karl Stenerud. All rights reserved. 13 | * 14 | * This code may be freely used for non-commercial purposes as long as this 15 | * copyright notice remains unaltered in the source code and any binary files 16 | * containing this code in compiled form. 17 | * 18 | * All other lisencing terms must be negotiated with the author 19 | * (Karl Stenerud). 20 | * 21 | * The latest version of this code can be obtained at: 22 | * http://kstenerud.cjb.net 23 | */ 24 | 25 | 26 | 27 | /* ======================================================================== */ 28 | /* ============================ GENERAL DEFINES =========================== */ 29 | 30 | /* ======================================================================== */ 31 | 32 | /* There are 7 levels of interrupt to the 68K. 33 | * A transition from < 7 to 7 will cause a non-maskable interrupt (NMI). 34 | */ 35 | #define M68K_IRQ_NONE 0 36 | #define M68K_IRQ_1 1 37 | #define M68K_IRQ_2 2 38 | #define M68K_IRQ_3 3 39 | #define M68K_IRQ_4 4 40 | #define M68K_IRQ_5 5 41 | #define M68K_IRQ_6 6 42 | #define M68K_IRQ_7 7 43 | 44 | 45 | /* Special interrupt acknowledge values. 46 | * Use these as special returns from the interrupt acknowledge callback 47 | * (specified later in this header). 48 | */ 49 | 50 | /* Causes an interrupt autovector (0x18 + interrupt level) to be taken. 51 | * This happens in a real 68K if VPA or AVEC is asserted during an interrupt 52 | * acknowledge cycle instead of DTACK. 53 | */ 54 | #define M68K_INT_ACK_AUTOVECTOR 0xffffffff 55 | 56 | /* Causes the spurious interrupt vector (0x18) to be taken 57 | * This happens in a real 68K if BERR is asserted during the interrupt 58 | * acknowledge cycle (i.e. no devices responded to the acknowledge). 59 | */ 60 | #define M68K_INT_ACK_SPURIOUS 0xfffffffe 61 | 62 | 63 | /* CPU types for use in m68k_set_cpu_type() */ 64 | enum 65 | { 66 | M68K_CPU_TYPE_INVALID, 67 | M68K_CPU_TYPE_68000, 68 | M68K_CPU_TYPE_68010, 69 | M68K_CPU_TYPE_68EC020, 70 | M68K_CPU_TYPE_68020, 71 | M68K_CPU_TYPE_68030, /* Supported by disassembler ONLY */ 72 | M68K_CPU_TYPE_68040 /* Supported by disassembler ONLY */ 73 | }; 74 | 75 | /* Registers used by m68k_get_reg() and m68k_set_reg() */ 76 | typedef enum 77 | { 78 | /* Real registers */ 79 | M68K_REG_D0, /* Data registers */ 80 | M68K_REG_D1, 81 | M68K_REG_D2, 82 | M68K_REG_D3, 83 | M68K_REG_D4, 84 | M68K_REG_D5, 85 | M68K_REG_D6, 86 | M68K_REG_D7, 87 | M68K_REG_A0, /* Address registers */ 88 | M68K_REG_A1, 89 | M68K_REG_A2, 90 | M68K_REG_A3, 91 | M68K_REG_A4, 92 | M68K_REG_A5, 93 | M68K_REG_A6, 94 | M68K_REG_A7, 95 | M68K_REG_PC, /* Program Counter */ 96 | M68K_REG_SR, /* Status Register */ 97 | M68K_REG_SP, /* The current Stack Pointer (located in A7) */ 98 | M68K_REG_USP, /* User Stack Pointer */ 99 | M68K_REG_ISP, /* Interrupt Stack Pointer */ 100 | M68K_REG_MSP, /* Master Stack Pointer */ 101 | M68K_REG_SFC, /* Source Function Code */ 102 | M68K_REG_DFC, /* Destination Function Code */ 103 | M68K_REG_VBR, /* Vector Base Register */ 104 | M68K_REG_CACR, /* Cache Control Register */ 105 | M68K_REG_CAAR, /* Cache Address Register */ 106 | 107 | /* Assumed registers */ 108 | /* These are cheat registers which emulate the 1-longword prefetch 109 | * present in the 68000 and 68010. 110 | */ 111 | M68K_REG_PREF_ADDR, /* Last prefetch address */ 112 | M68K_REG_PREF_DATA, /* Last prefetch data */ 113 | 114 | /* Convenience registers */ 115 | M68K_REG_PPC, /* Previous value in the program counter */ 116 | M68K_REG_IR, /* Instruction register */ 117 | M68K_REG_CPU_TYPE /* Type of CPU being run */ 118 | } m68k_register_t; 119 | 120 | /* ======================================================================== */ 121 | /* ====================== FUNCTIONS CALLED BY THE CPU ===================== */ 122 | /* ======================================================================== */ 123 | 124 | /* You will have to implement these functions */ 125 | 126 | /* read/write functions called by the CPU to access memory. 127 | * while values used are 32 bits, only the appropriate number 128 | * of bits are relevant (i.e. in write_memory_8, only the lower 8 bits 129 | * of value should be written to memory). 130 | * 131 | * NOTE: I have separated the immediate and PC-relative memory fetches 132 | * from the other memory fetches because some systems require 133 | * differentiation between PROGRAM and DATA fetches (usually 134 | * for security setups such as encryption). 135 | * This separation can either be achieved by setting 136 | * M68K_SEPARATE_READS in m68kconf.h and defining 137 | * the read functions, or by setting M68K_EMULATE_FC and 138 | * making a function code callback function. 139 | * Using the callback offers better emulation coverage 140 | * because you can also monitor whether the CPU is in SYSTEM or 141 | * USER mode, but it is also slower. 142 | */ 143 | 144 | /* Read from anywhere */ 145 | unsigned int m68k_read_memory_8(unsigned int address); 146 | unsigned int m68k_read_memory_16(unsigned int address); 147 | unsigned int m68k_read_memory_32(unsigned int address); 148 | 149 | /* Read data immediately following the PC */ 150 | unsigned int m68k_read_immediate_16(unsigned int address); 151 | unsigned int m68k_read_immediate_32(unsigned int address); 152 | 153 | /* Read data relative to the PC */ 154 | unsigned int m68k_read_pcrelative_8(unsigned int address); 155 | unsigned int m68k_read_pcrelative_16(unsigned int address); 156 | unsigned int m68k_read_pcrelative_32(unsigned int address); 157 | 158 | /* Memory access for the disassembler */ 159 | unsigned int m68k_read_disassembler_8 (unsigned int address); 160 | unsigned int m68k_read_disassembler_16 (unsigned int address); 161 | unsigned int m68k_read_disassembler_32 (unsigned int address); 162 | 163 | /* Write to anywhere */ 164 | void m68k_write_memory_8(unsigned int address, unsigned int value); 165 | void m68k_write_memory_16(unsigned int address, unsigned int value); 166 | void m68k_write_memory_32(unsigned int address, unsigned int value); 167 | 168 | 169 | 170 | /* ======================================================================== */ 171 | /* ============================== CALLBACKS =============================== */ 172 | /* ======================================================================== */ 173 | 174 | /* These functions allow you to set callbacks to the host when specific events 175 | * occur. Note that you must enable the corresponding value in m68kconf.h 176 | * in order for these to do anything useful. 177 | * Note: I have defined default callbacks which are used if you have enabled 178 | * the corresponding #define in m68kconf.h but either haven't assigned a 179 | * callback or have assigned a callback of NULL. 180 | */ 181 | 182 | /* Set the callback for an interrupt acknowledge. 183 | * You must enable M68K_EMULATE_INT_ACK in m68kconf.h. 184 | * The CPU will call the callback with the interrupt level being acknowledged. 185 | * The host program must return either a vector from 0x02-0xff, or one of the 186 | * special interrupt acknowledge values specified earlier in this header. 187 | * If this is not implemented, the CPU will always assume an autovectored 188 | * interrupt, and will automatically clear the interrupt request when it 189 | * services the interrupt. 190 | * Default behavior: return M68K_INT_ACK_AUTOVECTOR. 191 | */ 192 | void m68k_set_int_ack_callback(int (*callback)(int int_level)); 193 | 194 | 195 | /* Set the callback for a breakpoint acknowledge (68010+). 196 | * You must enable M68K_EMULATE_BKPT_ACK in m68kconf.h. 197 | * The CPU will call the callback with whatever was in the data field of the 198 | * BKPT instruction for 68020+, or 0 for 68010. 199 | * Default behavior: do nothing. 200 | */ 201 | void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data)); 202 | 203 | 204 | /* Set the callback for the RESET instruction. 205 | * You must enable M68K_EMULATE_RESET in m68kconf.h. 206 | * The CPU calls this callback every time it encounters a RESET instruction. 207 | * Default behavior: do nothing. 208 | */ 209 | void m68k_set_reset_instr_callback(void (*callback)(void)); 210 | 211 | 212 | /* Set the callback for informing of a large PC change. 213 | * You must enable M68K_MONITOR_PC in m68kconf.h. 214 | * The CPU calls this callback with the new PC value every time the PC changes 215 | * by a large value (currently set for changes by longwords). 216 | * Default behavior: do nothing. 217 | */ 218 | void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc)); 219 | 220 | 221 | /* Set the callback for CPU function code changes. 222 | * You must enable M68K_EMULATE_FC in m68kconf.h. 223 | * The CPU calls this callback with the function code before every memory 224 | * access to set the CPU's function code according to what kind of memory 225 | * access it is (supervisor/user, program/data and such). 226 | * Default behavior: do nothing. 227 | */ 228 | void m68k_set_fc_callback(void (*callback)(unsigned int new_fc)); 229 | 230 | 231 | /* Set a callback for the instruction cycle of the CPU. 232 | * You must enable M68K_INSTRUCTION_HOOK in m68kconf.h. 233 | * The CPU calls this callback just before fetching the opcode in the 234 | * instruction cycle. 235 | * Default behavior: do nothing. 236 | */ 237 | void m68k_set_instr_hook_callback(void (*callback)(void)); 238 | 239 | 240 | 241 | /* ======================================================================== */ 242 | /* ====================== FUNCTIONS TO ACCESS THE CPU ===================== */ 243 | /* ======================================================================== */ 244 | 245 | /* Use this function to set the CPU type you want to emulate. 246 | * Currently supported types are: M68K_CPU_TYPE_68000, M68K_CPU_TYPE_68010, 247 | * M68K_CPU_TYPE_EC020, and M68K_CPU_TYPE_68020. 248 | */ 249 | void m68k_set_cpu_type(unsigned int cpu_type); 250 | 251 | /* Pulse the RESET pin on the CPU. 252 | * You *MUST* reset the CPU at least once to initialize the emulation 253 | * Note: If you didn't call m68k_set_cpu_type() before resetting 254 | * the CPU for the first time, the CPU will be set to 255 | * M68K_CPU_TYPE_68000. 256 | */ 257 | void m68k_pulse_reset(void); 258 | 259 | /* execute num_cycles worth of instructions. returns number of cycles used */ 260 | int m68k_execute(int num_cycles); 261 | 262 | /* These functions let you read/write/modify the number of cycles left to run 263 | * while m68k_execute() is running. 264 | * These are useful if the 68k accesses a memory-mapped port on another device 265 | * that requires immediate processing by another CPU. 266 | */ 267 | int m68k_cycles_run(void); /* Number of cycles run so far */ 268 | int m68k_cycles_remaining(void); /* Number of cycles left */ 269 | void m68k_modify_timeslice(int cycles); /* Modify cycles left */ 270 | void m68k_end_timeslice(void); /* End timeslice now */ 271 | 272 | /* Set the IPL0-IPL2 pins on the CPU (IRQ). 273 | * A transition from < 7 to 7 will cause a non-maskable interrupt (NMI). 274 | * Setting IRQ to 0 will clear an interrupt request. 275 | */ 276 | void m68k_set_irq(unsigned int int_level); 277 | 278 | 279 | /* Halt the CPU as if you pulsed the HALT pin. */ 280 | void m68k_pulse_halt(void); 281 | 282 | 283 | /* Context switching to allow multiple CPUs */ 284 | 285 | /* Get the size of the cpu context in bytes */ 286 | unsigned int m68k_context_size(void); 287 | 288 | /* Get a cpu context */ 289 | unsigned int m68k_get_context(void* dst); 290 | 291 | /* set the current cpu context */ 292 | void m68k_set_context(void* dst); 293 | 294 | /* Save the current cpu context to disk. 295 | * You must provide a function pointer of the form: 296 | * void save_value(char* identifier, unsigned int value) 297 | */ 298 | void m68k_save_context( void (*save_value)(char* identifier, unsigned int value)); 299 | 300 | /* Load a cpu context from disk. 301 | * You must provide a function pointer of the form: 302 | * unsigned int load_value(char* identifier) 303 | */ 304 | void m68k_load_context(unsigned int (*load_value)(char* identifier)); 305 | 306 | 307 | 308 | /* Peek at the internals of a CPU context. This can either be a context 309 | * retrieved using m68k_get_context() or the currently running context. 310 | * If context is NULL, the currently running CPU context will be used. 311 | */ 312 | unsigned int m68k_get_reg(void* context, m68k_register_t reg); 313 | 314 | /* Poke values into the internals of the currently running CPU context */ 315 | void m68k_set_reg(m68k_register_t reg, unsigned int value); 316 | 317 | /* Check if an instruction is valid for the specified CPU type */ 318 | unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type); 319 | 320 | /* Disassemble 1 instruction using the epecified CPU type at pc. Stores 321 | * disassembly in str_buff and returns the size of the instruction in bytes. 322 | */ 323 | unsigned int m68k_disassemble(char* str_buff, unsigned int pc, unsigned int cpu_type); 324 | 325 | 326 | /* ======================================================================== */ 327 | /* ============================= CONFIGURATION ============================ */ 328 | /* ======================================================================== */ 329 | 330 | /* Import the configuration for this build */ 331 | #include "m68kconf.h" 332 | 333 | 334 | 335 | /* ======================================================================== */ 336 | /* ============================== END OF FILE ============================= */ 337 | /* ======================================================================== */ 338 | 339 | #endif /* M68K__HEADER */ 340 | -------------------------------------------------------------------------------- /m68k/m68kconf.h: -------------------------------------------------------------------------------- 1 | /* ======================================================================== */ 2 | /* ========================= LICENSING & COPYRIGHT ======================== */ 3 | /* ======================================================================== */ 4 | /* 5 | * MUSASHI 6 | * Version 3.3 7 | * 8 | * A portable Motorola M680x0 processor emulation engine. 9 | * Copyright 1998-2001 Karl Stenerud. All rights reserved. 10 | * 11 | * This code may be freely used for non-commercial purposes as long as this 12 | * copyright notice remains unaltered in the source code and any binary files 13 | * containing this code in compiled form. 14 | * 15 | * All other lisencing terms must be negotiated with the author 16 | * (Karl Stenerud). 17 | * 18 | * The latest version of this code can be obtained at: 19 | * http://kstenerud.cjb.net 20 | */ 21 | 22 | 23 | 24 | #ifndef M68KCONF__HEADER 25 | #define M68KCONF__HEADER 26 | 27 | 28 | /* Configuration switches. 29 | * Use OPT_SPECIFY_HANDLER for configuration options that allow callbacks. 30 | * OPT_SPECIFY_HANDLER causes the core to link directly to the function 31 | * or macro you specify, rather than using callback functions whose pointer 32 | * must be passed in using m68k_set_xxx_callback(). 33 | */ 34 | #define OPT_OFF 0 35 | #define OPT_ON 1 36 | #define OPT_SPECIFY_HANDLER 2 37 | 38 | 39 | /* ======================================================================== */ 40 | /* ============================== MAME STUFF ============================== */ 41 | /* ======================================================================== */ 42 | 43 | /* If you're compiling this for MAME, only change M68K_COMPILE_FOR_MAME 44 | * to OPT_ON and use m68kmame.h to configure the 68k core. 45 | */ 46 | #ifndef M68K_COMPILE_FOR_MAME 47 | #define M68K_COMPILE_FOR_MAME OPT_OFF 48 | #endif /* M68K_COMPILE_FOR_MAME */ 49 | 50 | #if M68K_COMPILE_FOR_MAME == OPT_ON 51 | #include "m68kmame.h" 52 | #else 53 | 54 | 55 | 56 | /* ======================================================================== */ 57 | /* ============================= CONFIGURATION ============================ */ 58 | /* ======================================================================== */ 59 | 60 | /* Turn on if you want to use the following M68K variants */ 61 | #define M68K_EMULATE_010 OPT_ON 62 | #define M68K_EMULATE_EC020 OPT_ON 63 | #define M68K_EMULATE_020 OPT_ON 64 | 65 | 66 | /* If on, the CPU will call m68k_read_immediate_xx() for immediate addressing 67 | * and m68k_read_pcrelative_xx() for PC-relative addressing. 68 | * If off, all read requests from the CPU will be redirected to m68k_read_xx() 69 | */ 70 | #define M68K_SEPARATE_READS OPT_OFF 71 | 72 | 73 | /* If on, CPU will call the interrupt acknowledge callback when it services an 74 | * interrupt. 75 | * If off, all interrupts will be autovectored and all interrupt requests will 76 | * auto-clear when the interrupt is serviced. 77 | */ 78 | #define M68K_EMULATE_INT_ACK OPT_OFF 79 | #define M68K_INT_ACK_CALLBACK(A) your_int_ack_handler_function(A) 80 | 81 | 82 | /* If on, CPU will call the breakpoint acknowledge callback when it encounters 83 | * a breakpoint instruction and it is running a 68010+. 84 | */ 85 | #define M68K_EMULATE_BKPT_ACK OPT_OFF 86 | #define M68K_BKPT_ACK_CALLBACK() your_bkpt_ack_handler_function() 87 | 88 | 89 | /* If on, the CPU will monitor the trace flags and take trace exceptions 90 | */ 91 | #define M68K_EMULATE_TRACE OPT_OFF 92 | 93 | 94 | /* If on, CPU will call the output reset callback when it encounters a reset 95 | * instruction. 96 | */ 97 | #define M68K_EMULATE_RESET OPT_OFF 98 | #define M68K_RESET_CALLBACK() your_reset_handler_function() 99 | 100 | 101 | /* If on, CPU will call the set fc callback on every memory access to 102 | * differentiate between user/supervisor, program/data access like a real 103 | * 68000 would. This should be enabled and the callback should be set if you 104 | * want to properly emulate the m68010 or higher. (moves uses function codes 105 | * to read/write data from different address spaces) 106 | */ 107 | #define M68K_EMULATE_FC OPT_OFF 108 | #define M68K_SET_FC_CALLBACK(A) your_set_fc_handler_function(A) 109 | 110 | 111 | /* If on, CPU will call the pc changed callback when it changes the PC by a 112 | * large value. This allows host programs to be nicer when it comes to 113 | * fetching immediate data and instructions on a banked memory system. 114 | */ 115 | #define M68K_MONITOR_PC OPT_OFF 116 | #define M68K_SET_PC_CALLBACK(A) your_pc_changed_handler_function(A) 117 | 118 | 119 | /* If on, CPU will call the instruction hook callback before every 120 | * instruction. 121 | */ 122 | #define M68K_INSTRUCTION_HOOK OPT_OFF 123 | #define M68K_INSTRUCTION_CALLBACK() your_instruction_hook_function() 124 | 125 | 126 | /* If on, the CPU will emulate the 4-byte prefetch queue of a real 68000 */ 127 | #define M68K_EMULATE_PREFETCH OPT_OFF 128 | 129 | 130 | /* If on, the CPU will generate address error exceptions if it tries to 131 | * access a word or longword at an odd address. 132 | * NOTE: Do not enable this! It is not working! 133 | */ 134 | #define M68K_EMULATE_ADDRESS_ERROR OPT_OFF 135 | 136 | 137 | /* Turn on to enable logging of illegal instruction calls. 138 | * M68K_LOG_FILEHANDLE must be #defined to a stdio file stream. 139 | * Turn on M68K_LOG_1010_1111 to log all 1010 and 1111 calls. 140 | */ 141 | #define M68K_LOG_ENABLE OPT_OFF 142 | #define M68K_LOG_1010_1111 OPT_OFF 143 | #define M68K_LOG_FILEHANDLE some_file_handle 144 | 145 | 146 | /* ----------------------------- COMPATIBILITY ---------------------------- */ 147 | 148 | /* The following options set optimizations that violate the current ANSI 149 | * standard, but will be compliant under the forthcoming C9X standard. 150 | */ 151 | 152 | 153 | /* If on, the enulation core will use 64-bit integers to speed up some 154 | * operations. 155 | */ 156 | #define M68K_USE_64_BIT OPT_OFF 157 | 158 | 159 | /* Set to your compiler's static inline keyword to enable it, or 160 | * set it to blank to disable it. 161 | * If you define INLINE in the makefile, it will override this value. 162 | * NOTE: not enabling inline functions will SEVERELY slow down emulation. 163 | */ 164 | #ifndef INLINE 165 | #define INLINE static __inline__ 166 | #endif /* INLINE */ 167 | 168 | 169 | /* If your environment requires special prefixes for system callback functions 170 | * such as the argument to qsort(), then set them here or in the makefile. 171 | */ 172 | #ifndef DECL_SPEC 173 | #define DECL_SPEC 174 | #endif 175 | 176 | #endif /* M68K_COMPILE_FOR_MAME */ 177 | 178 | #define m68k_read_disassembler_8 m68k_read_memory_8 179 | #define m68k_read_disassembler_16 m68k_read_memory_16 180 | #define m68k_read_disassembler_32 m68k_read_memory_32 181 | 182 | 183 | /* ======================================================================== */ 184 | /* ============================== END OF FILE ============================= */ 185 | /* ======================================================================== */ 186 | 187 | #endif /* M68KCONF__HEADER */ 188 | -------------------------------------------------------------------------------- /m68k/m68kcpu.c: -------------------------------------------------------------------------------- 1 | /* ======================================================================== */ 2 | /* ========================= LICENSING & COPYRIGHT ======================== */ 3 | /* ======================================================================== */ 4 | 5 | #if 0 6 | static const char* copyright_notice = 7 | "MUSASHI\n" 8 | "Version 3.3 (2001-01-29)\n" 9 | "A portable Motorola M680x0 processor emulation engine.\n" 10 | "Copyright 1998-2001 Karl Stenerud. All rights reserved.\n" 11 | "\n" 12 | "This code may be freely used for non-commercial purpooses as long as this\n" 13 | "copyright notice remains unaltered in the source code and any binary files\n" 14 | "containing this code in compiled form.\n" 15 | "\n" 16 | "All other lisencing terms must be negotiated with the author\n" 17 | "(Karl Stenerud).\n" 18 | "\n" 19 | "The latest version of this code can be obtained at:\n" 20 | "http://kstenerud.cjb.net\n" 21 | ; 22 | #endif 23 | 24 | 25 | /* ======================================================================== */ 26 | /* ================================= NOTES ================================ */ 27 | /* ======================================================================== */ 28 | 29 | 30 | 31 | /* ======================================================================== */ 32 | /* ================================ INCLUDES ============================== */ 33 | /* ======================================================================== */ 34 | 35 | #include "m68kops.h" 36 | #include "m68kcpu.h" 37 | 38 | /* ======================================================================== */ 39 | /* ================================= DATA ================================= */ 40 | /* ======================================================================== */ 41 | 42 | int m68ki_initial_cycles; 43 | int m68ki_remaining_cycles = 0; /* Number of clocks remaining */ 44 | uint m68ki_tracing = 0; 45 | uint m68ki_address_space; 46 | 47 | #ifdef M68K_LOG_ENABLE 48 | char* m68ki_cpu_names[9] = 49 | { 50 | "Invalid CPU", 51 | "M68000", 52 | "M68010", 53 | "Invalid CPU", 54 | "M68EC020" 55 | "Invalid CPU", 56 | "Invalid CPU", 57 | "Invalid CPU", 58 | "M68020" 59 | }; 60 | #endif /* M68K_LOG_ENABLE */ 61 | 62 | /* The CPU core */ 63 | m68ki_cpu_core m68ki_cpu = {0}; 64 | 65 | #if M68K_EMULATE_ADDRESS_ERROR 66 | jmp_buf m68ki_address_error_trap; 67 | #endif /* M68K_EMULATE_ADDRESS_ERROR */ 68 | 69 | /* Used by shift & rotate instructions */ 70 | uint8 m68ki_shift_8_table[65] = 71 | { 72 | 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 73 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 74 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 75 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 76 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 77 | 0xff, 0xff, 0xff, 0xff, 0xff 78 | }; 79 | uint16 m68ki_shift_16_table[65] = 80 | { 81 | 0x0000, 0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 82 | 0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff, 0xffff, 83 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 84 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 85 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 86 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 87 | 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 88 | 0xffff, 0xffff 89 | }; 90 | uint m68ki_shift_32_table[65] = 91 | { 92 | 0x00000000, 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000, 0xf8000000, 93 | 0xfc000000, 0xfe000000, 0xff000000, 0xff800000, 0xffc00000, 0xffe00000, 94 | 0xfff00000, 0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000, 0xffff8000, 95 | 0xffffc000, 0xffffe000, 0xfffff000, 0xfffff800, 0xfffffc00, 0xfffffe00, 96 | 0xffffff00, 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0, 0xfffffff8, 97 | 0xfffffffc, 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 98 | 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 99 | 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 100 | 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 101 | 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 102 | 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff 103 | }; 104 | 105 | 106 | /* Number of clock cycles to use for exception processing. 107 | * I used 4 for any vectors that are undocumented for processing times. 108 | */ 109 | uint8 m68ki_exception_cycle_table[3][256] = 110 | { 111 | { /* 000 */ 112 | 4, /* 0: Reset - Initial Stack Pointer */ 113 | 4, /* 1: Reset - Initial Program Counter */ 114 | 50, /* 2: Bus Error (unemulated) */ 115 | 50, /* 3: Address Error (unemulated) */ 116 | 34, /* 4: Illegal Instruction */ 117 | 38, /* 5: Divide by Zero -- ASG: changed from 42 */ 118 | 40, /* 6: CHK -- ASG: chanaged from 44 */ 119 | 34, /* 7: TRAPV */ 120 | 34, /* 8: Privilege Violation */ 121 | 34, /* 9: Trace */ 122 | 4, /* 10: 1010 */ 123 | 4, /* 11: 1111 */ 124 | 4, /* 12: RESERVED */ 125 | 4, /* 13: Coprocessor Protocol Violation (unemulated) */ 126 | 4, /* 14: Format Error */ 127 | 44, /* 15: Uninitialized Interrupt */ 128 | 4, /* 16: RESERVED */ 129 | 4, /* 17: RESERVED */ 130 | 4, /* 18: RESERVED */ 131 | 4, /* 19: RESERVED */ 132 | 4, /* 20: RESERVED */ 133 | 4, /* 21: RESERVED */ 134 | 4, /* 22: RESERVED */ 135 | 4, /* 23: RESERVED */ 136 | 44, /* 24: Spurious Interrupt */ 137 | 44, /* 25: Level 1 Interrupt Autovector */ 138 | 44, /* 26: Level 2 Interrupt Autovector */ 139 | 44, /* 27: Level 3 Interrupt Autovector */ 140 | 44, /* 28: Level 4 Interrupt Autovector */ 141 | 44, /* 29: Level 5 Interrupt Autovector */ 142 | 44, /* 30: Level 6 Interrupt Autovector */ 143 | 44, /* 31: Level 7 Interrupt Autovector */ 144 | 34, /* 32: TRAP #0 -- ASG: chanaged from 38 */ 145 | 34, /* 33: TRAP #1 */ 146 | 34, /* 34: TRAP #2 */ 147 | 34, /* 35: TRAP #3 */ 148 | 34, /* 36: TRAP #4 */ 149 | 34, /* 37: TRAP #5 */ 150 | 34, /* 38: TRAP #6 */ 151 | 34, /* 39: TRAP #7 */ 152 | 34, /* 40: TRAP #8 */ 153 | 34, /* 41: TRAP #9 */ 154 | 34, /* 42: TRAP #10 */ 155 | 34, /* 43: TRAP #11 */ 156 | 34, /* 44: TRAP #12 */ 157 | 34, /* 45: TRAP #13 */ 158 | 34, /* 46: TRAP #14 */ 159 | 34, /* 47: TRAP #15 */ 160 | 4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */ 161 | 4, /* 49: FP Inexact Result (unemulated) */ 162 | 4, /* 50: FP Divide by Zero (unemulated) */ 163 | 4, /* 51: FP Underflow (unemulated) */ 164 | 4, /* 52: FP Operand Error (unemulated) */ 165 | 4, /* 53: FP Overflow (unemulated) */ 166 | 4, /* 54: FP Signaling NAN (unemulated) */ 167 | 4, /* 55: FP Unimplemented Data Type (unemulated) */ 168 | 4, /* 56: MMU Configuration Error (unemulated) */ 169 | 4, /* 57: MMU Illegal Operation Error (unemulated) */ 170 | 4, /* 58: MMU Access Level Violation Error (unemulated) */ 171 | 4, /* 59: RESERVED */ 172 | 4, /* 60: RESERVED */ 173 | 4, /* 61: RESERVED */ 174 | 4, /* 62: RESERVED */ 175 | 4, /* 63: RESERVED */ 176 | /* 64-255: User Defined */ 177 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 178 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 179 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 180 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 181 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 182 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 183 | }, 184 | { /* 010 */ 185 | 4, /* 0: Reset - Initial Stack Pointer */ 186 | 4, /* 1: Reset - Initial Program Counter */ 187 | 126, /* 2: Bus Error (unemulated) */ 188 | 126, /* 3: Address Error (unemulated) */ 189 | 38, /* 4: Illegal Instruction */ 190 | 44, /* 5: Divide by Zero */ 191 | 44, /* 6: CHK */ 192 | 34, /* 7: TRAPV */ 193 | 38, /* 8: Privilege Violation */ 194 | 38, /* 9: Trace */ 195 | 4, /* 10: 1010 */ 196 | 4, /* 11: 1111 */ 197 | 4, /* 12: RESERVED */ 198 | 4, /* 13: Coprocessor Protocol Violation (unemulated) */ 199 | 4, /* 14: Format Error */ 200 | 44, /* 15: Uninitialized Interrupt */ 201 | 4, /* 16: RESERVED */ 202 | 4, /* 17: RESERVED */ 203 | 4, /* 18: RESERVED */ 204 | 4, /* 19: RESERVED */ 205 | 4, /* 20: RESERVED */ 206 | 4, /* 21: RESERVED */ 207 | 4, /* 22: RESERVED */ 208 | 4, /* 23: RESERVED */ 209 | 46, /* 24: Spurious Interrupt */ 210 | 46, /* 25: Level 1 Interrupt Autovector */ 211 | 46, /* 26: Level 2 Interrupt Autovector */ 212 | 46, /* 27: Level 3 Interrupt Autovector */ 213 | 46, /* 28: Level 4 Interrupt Autovector */ 214 | 46, /* 29: Level 5 Interrupt Autovector */ 215 | 46, /* 30: Level 6 Interrupt Autovector */ 216 | 46, /* 31: Level 7 Interrupt Autovector */ 217 | 38, /* 32: TRAP #0 */ 218 | 38, /* 33: TRAP #1 */ 219 | 38, /* 34: TRAP #2 */ 220 | 38, /* 35: TRAP #3 */ 221 | 38, /* 36: TRAP #4 */ 222 | 38, /* 37: TRAP #5 */ 223 | 38, /* 38: TRAP #6 */ 224 | 38, /* 39: TRAP #7 */ 225 | 38, /* 40: TRAP #8 */ 226 | 38, /* 41: TRAP #9 */ 227 | 38, /* 42: TRAP #10 */ 228 | 38, /* 43: TRAP #11 */ 229 | 38, /* 44: TRAP #12 */ 230 | 38, /* 45: TRAP #13 */ 231 | 38, /* 46: TRAP #14 */ 232 | 38, /* 47: TRAP #15 */ 233 | 4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */ 234 | 4, /* 49: FP Inexact Result (unemulated) */ 235 | 4, /* 50: FP Divide by Zero (unemulated) */ 236 | 4, /* 51: FP Underflow (unemulated) */ 237 | 4, /* 52: FP Operand Error (unemulated) */ 238 | 4, /* 53: FP Overflow (unemulated) */ 239 | 4, /* 54: FP Signaling NAN (unemulated) */ 240 | 4, /* 55: FP Unimplemented Data Type (unemulated) */ 241 | 4, /* 56: MMU Configuration Error (unemulated) */ 242 | 4, /* 57: MMU Illegal Operation Error (unemulated) */ 243 | 4, /* 58: MMU Access Level Violation Error (unemulated) */ 244 | 4, /* 59: RESERVED */ 245 | 4, /* 60: RESERVED */ 246 | 4, /* 61: RESERVED */ 247 | 4, /* 62: RESERVED */ 248 | 4, /* 63: RESERVED */ 249 | /* 64-255: User Defined */ 250 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 251 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 252 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 253 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 254 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 255 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 256 | }, 257 | { /* 020 */ 258 | 4, /* 0: Reset - Initial Stack Pointer */ 259 | 4, /* 1: Reset - Initial Program Counter */ 260 | 50, /* 2: Bus Error (unemulated) */ 261 | 50, /* 3: Address Error (unemulated) */ 262 | 20, /* 4: Illegal Instruction */ 263 | 38, /* 5: Divide by Zero */ 264 | 40, /* 6: CHK */ 265 | 20, /* 7: TRAPV */ 266 | 34, /* 8: Privilege Violation */ 267 | 25, /* 9: Trace */ 268 | 20, /* 10: 1010 */ 269 | 20, /* 11: 1111 */ 270 | 4, /* 12: RESERVED */ 271 | 4, /* 13: Coprocessor Protocol Violation (unemulated) */ 272 | 4, /* 14: Format Error */ 273 | 30, /* 15: Uninitialized Interrupt */ 274 | 4, /* 16: RESERVED */ 275 | 4, /* 17: RESERVED */ 276 | 4, /* 18: RESERVED */ 277 | 4, /* 19: RESERVED */ 278 | 4, /* 20: RESERVED */ 279 | 4, /* 21: RESERVED */ 280 | 4, /* 22: RESERVED */ 281 | 4, /* 23: RESERVED */ 282 | 30, /* 24: Spurious Interrupt */ 283 | 30, /* 25: Level 1 Interrupt Autovector */ 284 | 30, /* 26: Level 2 Interrupt Autovector */ 285 | 30, /* 27: Level 3 Interrupt Autovector */ 286 | 30, /* 28: Level 4 Interrupt Autovector */ 287 | 30, /* 29: Level 5 Interrupt Autovector */ 288 | 30, /* 30: Level 6 Interrupt Autovector */ 289 | 30, /* 31: Level 7 Interrupt Autovector */ 290 | 20, /* 32: TRAP #0 */ 291 | 20, /* 33: TRAP #1 */ 292 | 20, /* 34: TRAP #2 */ 293 | 20, /* 35: TRAP #3 */ 294 | 20, /* 36: TRAP #4 */ 295 | 20, /* 37: TRAP #5 */ 296 | 20, /* 38: TRAP #6 */ 297 | 20, /* 39: TRAP #7 */ 298 | 20, /* 40: TRAP #8 */ 299 | 20, /* 41: TRAP #9 */ 300 | 20, /* 42: TRAP #10 */ 301 | 20, /* 43: TRAP #11 */ 302 | 20, /* 44: TRAP #12 */ 303 | 20, /* 45: TRAP #13 */ 304 | 20, /* 46: TRAP #14 */ 305 | 20, /* 47: TRAP #15 */ 306 | 4, /* 48: FP Branch or Set on Unknown Condition (unemulated) */ 307 | 4, /* 49: FP Inexact Result (unemulated) */ 308 | 4, /* 50: FP Divide by Zero (unemulated) */ 309 | 4, /* 51: FP Underflow (unemulated) */ 310 | 4, /* 52: FP Operand Error (unemulated) */ 311 | 4, /* 53: FP Overflow (unemulated) */ 312 | 4, /* 54: FP Signaling NAN (unemulated) */ 313 | 4, /* 55: FP Unimplemented Data Type (unemulated) */ 314 | 4, /* 56: MMU Configuration Error (unemulated) */ 315 | 4, /* 57: MMU Illegal Operation Error (unemulated) */ 316 | 4, /* 58: MMU Access Level Violation Error (unemulated) */ 317 | 4, /* 59: RESERVED */ 318 | 4, /* 60: RESERVED */ 319 | 4, /* 61: RESERVED */ 320 | 4, /* 62: RESERVED */ 321 | 4, /* 63: RESERVED */ 322 | /* 64-255: User Defined */ 323 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 324 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 325 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 326 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 327 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 328 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 329 | } 330 | }; 331 | 332 | uint8 m68ki_ea_idx_cycle_table[64] = 333 | { 334 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 335 | 0, /* ..01.000 no memory indirect, base NULL */ 336 | 5, /* ..01..01 memory indirect, base NULL, outer NULL */ 337 | 7, /* ..01..10 memory indirect, base NULL, outer 16 */ 338 | 7, /* ..01..11 memory indirect, base NULL, outer 32 */ 339 | 0, 5, 7, 7, 0, 5, 7, 7, 0, 5, 7, 7, 340 | 2, /* ..10.000 no memory indirect, base 16 */ 341 | 7, /* ..10..01 memory indirect, base 16, outer NULL */ 342 | 9, /* ..10..10 memory indirect, base 16, outer 16 */ 343 | 9, /* ..10..11 memory indirect, base 16, outer 32 */ 344 | 0, 7, 9, 9, 0, 7, 9, 9, 0, 7, 9, 9, 345 | 6, /* ..11.000 no memory indirect, base 32 */ 346 | 11, /* ..11..01 memory indirect, base 32, outer NULL */ 347 | 13, /* ..11..10 memory indirect, base 32, outer 16 */ 348 | 13, /* ..11..11 memory indirect, base 32, outer 32 */ 349 | 0, 11, 13, 13, 0, 11, 13, 13, 0, 11, 13, 13 350 | }; 351 | 352 | 353 | 354 | /* ======================================================================== */ 355 | /* =============================== CALLBACKS ============================== */ 356 | /* ======================================================================== */ 357 | 358 | /* Default callbacks used if the callback hasn't been set yet, or if the 359 | * callback is set to NULL 360 | */ 361 | 362 | /* Interrupt acknowledge */ 363 | static int default_int_ack_callback_data; 364 | static int default_int_ack_callback(int int_level) 365 | { 366 | default_int_ack_callback_data = int_level; 367 | CPU_INT_LEVEL = 0; 368 | return M68K_INT_ACK_AUTOVECTOR; 369 | } 370 | 371 | /* Breakpoint acknowledge */ 372 | static unsigned int default_bkpt_ack_callback_data; 373 | static void default_bkpt_ack_callback(unsigned int data) 374 | { 375 | default_bkpt_ack_callback_data = data; 376 | } 377 | 378 | /* Called when a reset instruction is executed */ 379 | static void default_reset_instr_callback(void) 380 | { 381 | } 382 | 383 | /* Called when the program counter changed by a large value */ 384 | static unsigned int default_pc_changed_callback_data; 385 | static void default_pc_changed_callback(unsigned int new_pc) 386 | { 387 | default_pc_changed_callback_data = new_pc; 388 | } 389 | 390 | /* Called every time there's bus activity (read/write to/from memory */ 391 | static unsigned int default_set_fc_callback_data; 392 | static void default_set_fc_callback(unsigned int new_fc) 393 | { 394 | default_set_fc_callback_data = new_fc; 395 | } 396 | 397 | /* Called every instruction cycle prior to execution */ 398 | static void default_instr_hook_callback(void) 399 | { 400 | } 401 | 402 | 403 | 404 | /* ======================================================================== */ 405 | /* ================================= API ================================== */ 406 | /* ======================================================================== */ 407 | 408 | /* Access the internals of the CPU */ 409 | unsigned int m68k_get_reg(void* context, m68k_register_t regnum) 410 | { 411 | m68ki_cpu_core* cpu = context != NULL ?(m68ki_cpu_core*)context : &m68ki_cpu; 412 | 413 | switch(regnum) 414 | { 415 | case M68K_REG_D0: return cpu->dar[0]; 416 | case M68K_REG_D1: return cpu->dar[1]; 417 | case M68K_REG_D2: return cpu->dar[2]; 418 | case M68K_REG_D3: return cpu->dar[3]; 419 | case M68K_REG_D4: return cpu->dar[4]; 420 | case M68K_REG_D5: return cpu->dar[5]; 421 | case M68K_REG_D6: return cpu->dar[6]; 422 | case M68K_REG_D7: return cpu->dar[7]; 423 | case M68K_REG_A0: return cpu->dar[8]; 424 | case M68K_REG_A1: return cpu->dar[9]; 425 | case M68K_REG_A2: return cpu->dar[10]; 426 | case M68K_REG_A3: return cpu->dar[11]; 427 | case M68K_REG_A4: return cpu->dar[12]; 428 | case M68K_REG_A5: return cpu->dar[13]; 429 | case M68K_REG_A6: return cpu->dar[14]; 430 | case M68K_REG_A7: return cpu->dar[15]; 431 | case M68K_REG_PC: return MASK_OUT_ABOVE_32(cpu->pc); 432 | case M68K_REG_SR: return cpu->t1_flag | 433 | cpu->t0_flag | 434 | (cpu->s_flag << 11) | 435 | (cpu->m_flag << 11) | 436 | cpu->int_mask | 437 | ((cpu->x_flag & XFLAG_SET) >> 4) | 438 | ((cpu->n_flag & NFLAG_SET) >> 4) | 439 | ((!cpu->not_z_flag) << 2) | 440 | ((cpu->v_flag & VFLAG_SET) >> 6) | 441 | ((cpu->c_flag & CFLAG_SET) >> 8); 442 | case M68K_REG_SP: return cpu->dar[15]; 443 | case M68K_REG_USP: return cpu->s_flag ? cpu->sp[0] : cpu->dar[15]; 444 | case M68K_REG_ISP: return cpu->s_flag && !cpu->m_flag ? cpu->dar[15] : cpu->sp[4]; 445 | case M68K_REG_MSP: return cpu->s_flag && cpu->m_flag ? cpu->dar[15] : cpu->sp[6]; 446 | case M68K_REG_SFC: return cpu->sfc; 447 | case M68K_REG_DFC: return cpu->dfc; 448 | case M68K_REG_VBR: return cpu->vbr; 449 | case M68K_REG_CACR: return cpu->cacr; 450 | case M68K_REG_CAAR: return cpu->caar; 451 | case M68K_REG_PREF_ADDR: return cpu->pref_addr; 452 | case M68K_REG_PREF_DATA: return cpu->pref_data; 453 | case M68K_REG_PPC: return MASK_OUT_ABOVE_32(cpu->ppc); 454 | case M68K_REG_IR: return cpu->ir; 455 | case M68K_REG_CPU_TYPE: 456 | switch(cpu->cpu_type) 457 | { 458 | case CPU_TYPE_000: return (unsigned int)M68K_CPU_TYPE_68000; 459 | case CPU_TYPE_010: return (unsigned int)M68K_CPU_TYPE_68010; 460 | case CPU_TYPE_EC020: return (unsigned int)M68K_CPU_TYPE_68EC020; 461 | case CPU_TYPE_020: return (unsigned int)M68K_CPU_TYPE_68020; 462 | } 463 | return M68K_CPU_TYPE_INVALID; 464 | default: return 0; 465 | } 466 | return 0; 467 | } 468 | 469 | void m68k_set_reg(m68k_register_t regnum, unsigned int value) 470 | { 471 | switch(regnum) 472 | { 473 | case M68K_REG_D0: REG_D[0] = MASK_OUT_ABOVE_32(value); return; 474 | case M68K_REG_D1: REG_D[1] = MASK_OUT_ABOVE_32(value); return; 475 | case M68K_REG_D2: REG_D[2] = MASK_OUT_ABOVE_32(value); return; 476 | case M68K_REG_D3: REG_D[3] = MASK_OUT_ABOVE_32(value); return; 477 | case M68K_REG_D4: REG_D[4] = MASK_OUT_ABOVE_32(value); return; 478 | case M68K_REG_D5: REG_D[5] = MASK_OUT_ABOVE_32(value); return; 479 | case M68K_REG_D6: REG_D[6] = MASK_OUT_ABOVE_32(value); return; 480 | case M68K_REG_D7: REG_D[7] = MASK_OUT_ABOVE_32(value); return; 481 | case M68K_REG_A0: REG_A[0] = MASK_OUT_ABOVE_32(value); return; 482 | case M68K_REG_A1: REG_A[1] = MASK_OUT_ABOVE_32(value); return; 483 | case M68K_REG_A2: REG_A[2] = MASK_OUT_ABOVE_32(value); return; 484 | case M68K_REG_A3: REG_A[3] = MASK_OUT_ABOVE_32(value); return; 485 | case M68K_REG_A4: REG_A[4] = MASK_OUT_ABOVE_32(value); return; 486 | case M68K_REG_A5: REG_A[5] = MASK_OUT_ABOVE_32(value); return; 487 | case M68K_REG_A6: REG_A[6] = MASK_OUT_ABOVE_32(value); return; 488 | case M68K_REG_A7: REG_A[7] = MASK_OUT_ABOVE_32(value); return; 489 | case M68K_REG_PC: m68ki_jump(MASK_OUT_ABOVE_32(value)); return; 490 | case M68K_REG_SR: m68ki_set_sr(value); return; 491 | case M68K_REG_SP: REG_SP = MASK_OUT_ABOVE_32(value); return; 492 | case M68K_REG_USP: if(FLAG_S) 493 | REG_USP = MASK_OUT_ABOVE_32(value); 494 | else 495 | REG_SP = MASK_OUT_ABOVE_32(value); 496 | return; 497 | case M68K_REG_ISP: if(FLAG_S && !FLAG_M) 498 | REG_SP = MASK_OUT_ABOVE_32(value); 499 | else 500 | REG_ISP = MASK_OUT_ABOVE_32(value); 501 | return; 502 | case M68K_REG_MSP: if(FLAG_S && FLAG_M) 503 | REG_SP = MASK_OUT_ABOVE_32(value); 504 | else 505 | REG_MSP = MASK_OUT_ABOVE_32(value); 506 | return; 507 | case M68K_REG_VBR: REG_VBR = MASK_OUT_ABOVE_32(value); return; 508 | case M68K_REG_SFC: REG_SFC = value & 7; return; 509 | case M68K_REG_DFC: REG_DFC = value & 7; return; 510 | case M68K_REG_CACR: REG_CACR = MASK_OUT_ABOVE_32(value); return; 511 | case M68K_REG_CAAR: REG_CAAR = MASK_OUT_ABOVE_32(value); return; 512 | case M68K_REG_PPC: REG_PPC = MASK_OUT_ABOVE_32(value); return; 513 | case M68K_REG_IR: REG_IR = MASK_OUT_ABOVE_16(value); return; 514 | case M68K_REG_CPU_TYPE: m68k_set_cpu_type(value); return; 515 | default: return; 516 | } 517 | } 518 | 519 | /* Set the callbacks */ 520 | void m68k_set_int_ack_callback(int (*callback)(int int_level)) 521 | { 522 | CALLBACK_INT_ACK = callback ? callback : default_int_ack_callback; 523 | } 524 | 525 | void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data)) 526 | { 527 | CALLBACK_BKPT_ACK = callback ? callback : default_bkpt_ack_callback; 528 | } 529 | 530 | void m68k_set_reset_instr_callback(void (*callback)(void)) 531 | { 532 | CALLBACK_RESET_INSTR = callback ? callback : default_reset_instr_callback; 533 | } 534 | 535 | void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc)) 536 | { 537 | CALLBACK_PC_CHANGED = callback ? callback : default_pc_changed_callback; 538 | } 539 | 540 | void m68k_set_fc_callback(void (*callback)(unsigned int new_fc)) 541 | { 542 | CALLBACK_SET_FC = callback ? callback : default_set_fc_callback; 543 | } 544 | 545 | void m68k_set_instr_hook_callback(void (*callback)(void)) 546 | { 547 | CALLBACK_INSTR_HOOK = callback ? callback : default_instr_hook_callback; 548 | } 549 | 550 | #include 551 | /* Set the CPU type. */ 552 | void m68k_set_cpu_type(unsigned int cpu_type) 553 | { 554 | switch(cpu_type) 555 | { 556 | case M68K_CPU_TYPE_68000: 557 | CPU_TYPE = CPU_TYPE_000; 558 | CPU_ADDRESS_MASK = 0x00ffffff; 559 | CPU_SR_MASK = 0xa71f; /* T1 -- S -- -- I2 I1 I0 -- -- -- X N Z V C */ 560 | CYC_INSTRUCTION = m68ki_cycles[0]; 561 | CYC_EXCEPTION = m68ki_exception_cycle_table[0]; 562 | CYC_BCC_NOTAKE_B = -2; 563 | CYC_BCC_NOTAKE_W = 2; 564 | CYC_DBCC_F_NOEXP = -2; 565 | CYC_DBCC_F_EXP = 2; 566 | CYC_SCC_R_FALSE = 2; 567 | CYC_MOVEM_W = 2; 568 | CYC_MOVEM_L = 3; 569 | CYC_SHIFT = 1; 570 | CYC_RESET = 132; 571 | return; 572 | case M68K_CPU_TYPE_68010: 573 | CPU_TYPE = CPU_TYPE_010; 574 | CPU_ADDRESS_MASK = 0x00ffffff; 575 | CPU_SR_MASK = 0xa71f; /* T1 -- S -- -- I2 I1 I0 -- -- -- X N Z V C */ 576 | CYC_INSTRUCTION = m68ki_cycles[1]; 577 | CYC_EXCEPTION = m68ki_exception_cycle_table[1]; 578 | CYC_BCC_NOTAKE_B = -4; 579 | CYC_BCC_NOTAKE_W = 0; 580 | CYC_DBCC_F_NOEXP = 0; 581 | CYC_DBCC_F_EXP = 6; 582 | CYC_SCC_R_FALSE = 0; 583 | CYC_MOVEM_W = 2; 584 | CYC_MOVEM_L = 3; 585 | CYC_SHIFT = 1; 586 | CYC_RESET = 130; 587 | return; 588 | case M68K_CPU_TYPE_68EC020: 589 | CPU_TYPE = CPU_TYPE_EC020; 590 | CPU_ADDRESS_MASK = 0x00ffffff; 591 | CPU_SR_MASK = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */ 592 | CYC_INSTRUCTION = m68ki_cycles[2]; 593 | CYC_EXCEPTION = m68ki_exception_cycle_table[2]; 594 | CYC_BCC_NOTAKE_B = -2; 595 | CYC_BCC_NOTAKE_W = 0; 596 | CYC_DBCC_F_NOEXP = 0; 597 | CYC_DBCC_F_EXP = 4; 598 | CYC_SCC_R_FALSE = 0; 599 | CYC_MOVEM_W = 2; 600 | CYC_MOVEM_L = 2; 601 | CYC_SHIFT = 0; 602 | CYC_RESET = 518; 603 | return; 604 | case M68K_CPU_TYPE_68020: 605 | CPU_TYPE = CPU_TYPE_020; 606 | CPU_ADDRESS_MASK = 0xffffffff; 607 | CPU_SR_MASK = 0xf71f; /* T1 T0 S M -- I2 I1 I0 -- -- -- X N Z V C */ 608 | CYC_INSTRUCTION = m68ki_cycles[2]; 609 | CYC_EXCEPTION = m68ki_exception_cycle_table[2]; 610 | CYC_BCC_NOTAKE_B = -2; 611 | CYC_BCC_NOTAKE_W = 0; 612 | CYC_DBCC_F_NOEXP = 0; 613 | CYC_DBCC_F_EXP = 4; 614 | CYC_SCC_R_FALSE = 0; 615 | CYC_MOVEM_W = 2; 616 | CYC_MOVEM_L = 2; 617 | CYC_SHIFT = 0; 618 | CYC_RESET = 518; 619 | return; 620 | } 621 | } 622 | 623 | /* Execute some instructions until we use up num_cycles clock cycles */ 624 | /* ASG: removed per-instruction interrupt checks */ 625 | int m68k_execute(int num_cycles) 626 | { 627 | extern int cycle_counter; 628 | num_cycles /= 7; /* num_cycles is given in M cycles */ 629 | 630 | /* Make sure we're not stopped */ 631 | if(!CPU_STOPPED) 632 | { 633 | /* Set our pool of clock cycles available */ 634 | SET_CYCLES(num_cycles); 635 | m68ki_initial_cycles = num_cycles; 636 | 637 | /* ASG: update cycles */ 638 | USE_CYCLES(CPU_INT_CYCLES); 639 | CPU_INT_CYCLES = 0; 640 | 641 | /* Return point if we had an address error */ 642 | m68ki_set_address_error_trap(); /* auto-disable (see m68kcpu.h) */ 643 | 644 | /* Main loop. Keep going until we run out of clock cycles */ 645 | do 646 | { 647 | /* Set tracing accodring to T1. (T0 is done inside instruction) */ 648 | m68ki_trace_t1(); /* auto-disable (see m68kcpu.h) */ 649 | 650 | /* Set the address space for reads */ 651 | m68ki_use_data_space(); /* auto-disable (see m68kcpu.h) */ 652 | 653 | /* Call external hook to peek at CPU */ 654 | m68ki_instr_hook(); /* auto-disable (see m68kcpu.h) */ 655 | 656 | /* Record previous program counter */ 657 | REG_PPC = REG_PC; 658 | 659 | /* Read an instruction and call its handler */ 660 | REG_IR = m68ki_read_imm_16(); 661 | m68ki_instruction_jump_table[REG_IR](); 662 | USE_CYCLES(CYC_INSTRUCTION[REG_IR]); 663 | 664 | /* Trace m68k_exception, if necessary */ 665 | m68ki_exception_if_trace(); /* auto-disable (see m68kcpu.h) */ 666 | } while(GET_CYCLES() > 0); 667 | 668 | /* set previous PC to current PC for the next entry into the loop */ 669 | REG_PPC = REG_PC; 670 | 671 | /* ASG: update cycles */ 672 | USE_CYCLES(CPU_INT_CYCLES); 673 | CPU_INT_CYCLES = 0; 674 | 675 | cycle_counter += (m68ki_initial_cycles - GET_CYCLES())*7; 676 | 677 | /* return how many clocks we used */ 678 | return m68ki_initial_cycles - GET_CYCLES(); 679 | } 680 | 681 | /* We get here if the CPU is stopped or halted */ 682 | SET_CYCLES(0); 683 | CPU_INT_CYCLES = 0; 684 | 685 | return num_cycles; 686 | } 687 | 688 | 689 | int m68k_cycles_run(void) 690 | { 691 | return m68ki_initial_cycles - GET_CYCLES(); 692 | } 693 | 694 | int m68k_cycles_remaining(void) 695 | { 696 | return GET_CYCLES(); 697 | } 698 | 699 | /* Change the timeslice */ 700 | void m68k_modify_timeslice(int cycles) 701 | { 702 | m68ki_initial_cycles += cycles; 703 | ADD_CYCLES(cycles); 704 | } 705 | 706 | 707 | void m68k_end_timeslice(void) 708 | { 709 | m68ki_initial_cycles = GET_CYCLES(); 710 | SET_CYCLES(0); 711 | } 712 | 713 | 714 | /* ASG: rewrote so that the int_level is a mask of the IPL0/IPL1/IPL2 bits */ 715 | /* KS: Modified so that IPL* bits match with mask positions in the SR 716 | * and cleaned out remenants of the interrupt controller. 717 | */ 718 | void m68k_set_irq(unsigned int int_level) 719 | { 720 | uint old_level = CPU_INT_LEVEL; 721 | CPU_INT_LEVEL = int_level << 8; 722 | 723 | /* A transition from < 7 to 7 always interrupts (NMI) */ 724 | /* Note: Level 7 can also level trigger like a normal IRQ */ 725 | if(old_level != 0x0700 && CPU_INT_LEVEL == 0x0700) 726 | m68ki_exception_interrupt(7); /* Edge triggered level 7 (NMI) */ 727 | else 728 | m68ki_check_interrupts(); /* Level triggered (IRQ) */ 729 | } 730 | 731 | 732 | /* Pulse the RESET line on the CPU */ 733 | void m68k_pulse_reset(void) 734 | { 735 | static uint emulation_initialized = 0; 736 | 737 | /* The first call to this function initializes the opcode handler jump table */ 738 | if(!emulation_initialized) 739 | { 740 | m68ki_build_opcode_table(); 741 | m68k_set_int_ack_callback(NULL); 742 | m68k_set_bkpt_ack_callback(NULL); 743 | m68k_set_reset_instr_callback(NULL); 744 | m68k_set_pc_changed_callback(NULL); 745 | m68k_set_fc_callback(NULL); 746 | m68k_set_instr_hook_callback(NULL); 747 | 748 | emulation_initialized = 1; 749 | } 750 | 751 | 752 | if(CPU_TYPE == 0) /* KW 990319 */ 753 | m68k_set_cpu_type(M68K_CPU_TYPE_68000); 754 | 755 | /* Clear all stop levels and eat up all remaining cycles */ 756 | CPU_STOPPED = 0; 757 | SET_CYCLES(0); 758 | 759 | /* Turn off tracing */ 760 | FLAG_T1 = FLAG_T0 = 0; 761 | m68ki_clear_trace(); 762 | /* Interrupt mask to level 7 */ 763 | FLAG_INT_MASK = 0x0700; 764 | /* Reset VBR */ 765 | REG_VBR = 0; 766 | /* Go to supervisor mode */ 767 | m68ki_set_sm_flag(SFLAG_SET | MFLAG_CLEAR); 768 | 769 | /* Invalidate the prefetch queue */ 770 | #if M68K_EMULATE_PREFETCH 771 | /* Set to arbitrary number since our first fetch is from 0 */ 772 | CPU_PREF_ADDR = 0x1000; 773 | #endif /* M68K_EMULATE_PREFETCH */ 774 | 775 | /* Read the initial stack pointer and program counter */ 776 | m68ki_jump(0); 777 | REG_SP = m68ki_read_imm_32(); 778 | REG_PC = m68ki_read_imm_32(); 779 | m68ki_jump(REG_PC); 780 | } 781 | 782 | /* Pulse the HALT line on the CPU */ 783 | void m68k_pulse_halt(void) 784 | { 785 | CPU_STOPPED |= STOP_LEVEL_HALT; 786 | } 787 | 788 | 789 | /* Get and set the current CPU context */ 790 | /* This is to allow for multiple CPUs */ 791 | unsigned int m68k_context_size() 792 | { 793 | return sizeof(m68ki_cpu_core); 794 | } 795 | 796 | unsigned int m68k_get_context(void* dst) 797 | { 798 | if(dst) *(m68ki_cpu_core*)dst = m68ki_cpu; 799 | return sizeof(m68ki_cpu_core); 800 | } 801 | 802 | void m68k_set_context(void* src) 803 | { 804 | if(src) m68ki_cpu = *(m68ki_cpu_core*)src; 805 | } 806 | 807 | void m68k_save_context( void (*save_value)(char*, unsigned int)) 808 | { 809 | if(!save_value) 810 | return; 811 | 812 | save_value("CPU_TYPE" , m68k_get_reg(NULL, M68K_REG_CPU_TYPE)); 813 | save_value("D0" , REG_D[0]); 814 | save_value("D1" , REG_D[1]); 815 | save_value("D2" , REG_D[2]); 816 | save_value("D3" , REG_D[3]); 817 | save_value("D4" , REG_D[4]); 818 | save_value("D5" , REG_D[5]); 819 | save_value("D6" , REG_D[6]); 820 | save_value("D7" , REG_D[7]); 821 | save_value("A0" , REG_A[0]); 822 | save_value("A1" , REG_A[1]); 823 | save_value("A2" , REG_A[2]); 824 | save_value("A3" , REG_A[3]); 825 | save_value("A4" , REG_A[4]); 826 | save_value("A5" , REG_A[5]); 827 | save_value("A6" , REG_A[6]); 828 | save_value("A7" , REG_A[7]); 829 | save_value("PPC" , REG_PPC); 830 | save_value("PC" , REG_PC); 831 | save_value("USP" , REG_USP); 832 | save_value("ISP" , REG_ISP); 833 | save_value("MSP" , REG_MSP); 834 | save_value("VBR" , REG_VBR); 835 | save_value("SFC" , REG_SFC); 836 | save_value("DFC" , REG_DFC); 837 | save_value("CACR" , REG_CACR); 838 | save_value("CAAR" , REG_CAAR); 839 | save_value("SR" , m68ki_get_sr()); 840 | save_value("INT_LEVEL" , CPU_INT_LEVEL); 841 | save_value("INT_CYCLES", CPU_INT_CYCLES); 842 | save_value("STOPPED" , (CPU_STOPPED & STOP_LEVEL_STOP) != 0); 843 | save_value("HALTED" , (CPU_STOPPED & STOP_LEVEL_HALT) != 0); 844 | save_value("PREF_ADDR" , CPU_PREF_ADDR); 845 | save_value("PREF_DATA" , CPU_PREF_DATA); 846 | } 847 | 848 | void m68k_load_context(unsigned int (*load_value)(char*)) 849 | { 850 | unsigned int temp; 851 | 852 | m68k_set_cpu_type(load_value("CPU_TYPE")); 853 | REG_PPC = load_value("PPC"); 854 | REG_PC = load_value("PC"); 855 | m68ki_jump(REG_PC); 856 | CPU_INT_LEVEL = 0; 857 | m68ki_set_sr_noint(load_value("SR")); 858 | REG_D[0] = load_value("D0"); 859 | REG_D[1] = load_value("D1"); 860 | REG_D[2] = load_value("D2"); 861 | REG_D[3] = load_value("D3"); 862 | REG_D[4] = load_value("D4"); 863 | REG_D[5] = load_value("D5"); 864 | REG_D[6] = load_value("D6"); 865 | REG_D[7] = load_value("D7"); 866 | REG_A[0] = load_value("A0"); 867 | REG_A[1] = load_value("A1"); 868 | REG_A[2] = load_value("A2"); 869 | REG_A[3] = load_value("A3"); 870 | REG_A[4] = load_value("A4"); 871 | REG_A[5] = load_value("A5"); 872 | REG_A[6] = load_value("A6"); 873 | REG_A[7] = load_value("A7"); 874 | REG_USP = load_value("USP"); 875 | REG_ISP = load_value("ISP"); 876 | REG_MSP = load_value("MSP"); 877 | REG_VBR = load_value("VBR"); 878 | REG_SFC = load_value("SFC"); 879 | REG_DFC = load_value("DFC"); 880 | REG_CACR = load_value("CACR"); 881 | REG_CAAR = load_value("CAAR"); 882 | CPU_INT_LEVEL = load_value("INT_LEVEL"); 883 | CPU_INT_CYCLES = load_value("INT_CYCLES"); 884 | 885 | CPU_STOPPED = 0; 886 | temp = load_value("STOPPED"); 887 | if(temp) CPU_STOPPED |= STOP_LEVEL_STOP; 888 | temp = load_value("HALTED"); 889 | if(temp) CPU_STOPPED |= STOP_LEVEL_HALT; 890 | 891 | CPU_PREF_ADDR = load_value("PREF_ADDR"); 892 | CPU_PREF_DATA = load_value("PREF_DATA"); 893 | } 894 | 895 | 896 | 897 | /* ======================================================================== */ 898 | /* ============================== END OF FILE ============================= */ 899 | /* ======================================================================== */ 900 | -------------------------------------------------------------------------------- /m68k/m68kmake.c: -------------------------------------------------------------------------------- 1 | /* ======================================================================== */ 2 | /* ========================= LICENSING & COPYRIGHT ======================== */ 3 | /* ======================================================================== */ 4 | /* 5 | * MUSASHI 6 | * Version 3.3 7 | * 8 | * A portable Motorola M680x0 processor emulation engine. 9 | * Copyright 1998-2001 Karl Stenerud. All rights reserved. 10 | * 11 | * This code may be freely used for non-commercial purposes as long as this 12 | * copyright notice remains unaltered in the source code and any binary files 13 | * containing this code in compiled form. 14 | * 15 | * All other lisencing terms must be negotiated with the author 16 | * (Karl Stenerud). 17 | * 18 | * The latest version of this code can be obtained at: 19 | * http://kstenerud.cjb.net 20 | */ 21 | 22 | 23 | 24 | /* ======================================================================== */ 25 | /* ============================ CODE GENERATOR ============================ */ 26 | /* ======================================================================== */ 27 | /* 28 | * This is the code generator program which will generate the opcode table 29 | * and the final opcode handlers. 30 | * 31 | * It requires an input file to function (default m68k_in.c), but you can 32 | * specify your own like so: 33 | * 34 | * m68kmake 35 | * 36 | * where output path is the path where the output files should be placed, and 37 | * input file is the file to use for input. 38 | * 39 | * If you modify the input file greatly from its released form, you may have 40 | * to tweak the configuration section a bit since I'm using static allocation 41 | * to keep things simple. 42 | * 43 | * 44 | * TODO: - build a better code generator for the move instruction. 45 | * - Add callm and rtm instructions 46 | * - Fix RTE to handle other format words 47 | * - Add address error (and bus error?) handling 48 | */ 49 | 50 | 51 | char* g_version = "3.3"; 52 | 53 | /* ======================================================================== */ 54 | /* =============================== INCLUDES =============================== */ 55 | /* ======================================================================== */ 56 | 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | 64 | 65 | /* ======================================================================== */ 66 | /* ============================= CONFIGURATION ============================ */ 67 | /* ======================================================================== */ 68 | 69 | #define MAX_PATH 1024 70 | #define MAX_DIR 1024 71 | 72 | #define NUM_CPUS 3 /* 000, 010, 020 */ 73 | #define MAX_LINE_LENGTH 200 /* length of 1 line */ 74 | #define MAX_BODY_LENGTH 300 /* Number of lines in 1 function */ 75 | #define MAX_REPLACE_LENGTH 30 /* Max number of replace strings */ 76 | #define MAX_INSERT_LENGTH 5000 /* Max size of insert piece */ 77 | #define MAX_NAME_LENGTH 30 /* Max length of ophandler name */ 78 | #define MAX_SPEC_PROC_LENGTH 4 /* Max length of special processing str */ 79 | #define MAX_SPEC_EA_LENGTH 5 /* Max length of specified EA str */ 80 | #define EA_ALLOWED_LENGTH 11 /* Max length of ea allowed str */ 81 | #define MAX_OPCODE_INPUT_TABLE_LENGTH 1000 /* Max length of opcode handler tbl */ 82 | #define MAX_OPCODE_OUTPUT_TABLE_LENGTH 3000 /* Max length of opcode handler tbl */ 83 | 84 | /* Default filenames */ 85 | #define FILENAME_INPUT "m68k_in.c" 86 | #define FILENAME_PROTOTYPE "m68kops.h" 87 | #define FILENAME_TABLE "m68kops.c" 88 | #define FILENAME_OPS_AC "m68kopac.c" 89 | #define FILENAME_OPS_DM "m68kopdm.c" 90 | #define FILENAME_OPS_NZ "m68kopnz.c" 91 | 92 | 93 | /* Identifier sequences recognized by this program */ 94 | 95 | #define ID_INPUT_SEPARATOR "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" 96 | 97 | #define ID_BASE "M68KMAKE" 98 | #define ID_PROTOTYPE_HEADER ID_BASE "_PROTOTYPE_HEADER" 99 | #define ID_PROTOTYPE_FOOTER ID_BASE "_PROTOTYPE_FOOTER" 100 | #define ID_TABLE_HEADER ID_BASE "_TABLE_HEADER" 101 | #define ID_TABLE_FOOTER ID_BASE "_TABLE_FOOTER" 102 | #define ID_TABLE_BODY ID_BASE "_TABLE_BODY" 103 | #define ID_TABLE_START ID_BASE "_TABLE_START" 104 | #define ID_OPHANDLER_HEADER ID_BASE "_OPCODE_HANDLER_HEADER" 105 | #define ID_OPHANDLER_FOOTER ID_BASE "_OPCODE_HANDLER_FOOTER" 106 | #define ID_OPHANDLER_BODY ID_BASE "_OPCODE_HANDLER_BODY" 107 | #define ID_END ID_BASE "_END" 108 | 109 | #define ID_OPHANDLER_NAME ID_BASE "_OP" 110 | #define ID_OPHANDLER_EA_AY_8 ID_BASE "_GET_EA_AY_8" 111 | #define ID_OPHANDLER_EA_AY_16 ID_BASE "_GET_EA_AY_16" 112 | #define ID_OPHANDLER_EA_AY_32 ID_BASE "_GET_EA_AY_32" 113 | #define ID_OPHANDLER_OPER_AY_8 ID_BASE "_GET_OPER_AY_8" 114 | #define ID_OPHANDLER_OPER_AY_16 ID_BASE "_GET_OPER_AY_16" 115 | #define ID_OPHANDLER_OPER_AY_32 ID_BASE "_GET_OPER_AY_32" 116 | #define ID_OPHANDLER_CC ID_BASE "_CC" 117 | #define ID_OPHANDLER_NOT_CC ID_BASE "_NOT_CC" 118 | 119 | 120 | #ifndef DECL_SPEC 121 | #define DECL_SPEC 122 | #endif /* DECL_SPEC */ 123 | 124 | 125 | 126 | /* ======================================================================== */ 127 | /* ============================== PROTOTYPES ============================== */ 128 | /* ======================================================================== */ 129 | 130 | #define CPU_TYPE_000 0 131 | #define CPU_TYPE_010 1 132 | #define CPU_TYPE_020 2 133 | 134 | #define UNSPECIFIED "." 135 | #define UNSPECIFIED_CH '.' 136 | 137 | #define HAS_NO_EA_MODE(A) (strcmp(A, "..........") == 0) 138 | #define HAS_EA_AI(A) ((A)[0] == 'A') 139 | #define HAS_EA_PI(A) ((A)[1] == '+') 140 | #define HAS_EA_PD(A) ((A)[2] == '-') 141 | #define HAS_EA_DI(A) ((A)[3] == 'D') 142 | #define HAS_EA_IX(A) ((A)[4] == 'X') 143 | #define HAS_EA_AW(A) ((A)[5] == 'W') 144 | #define HAS_EA_AL(A) ((A)[6] == 'L') 145 | #define HAS_EA_PCDI(A) ((A)[7] == 'd') 146 | #define HAS_EA_PCIX(A) ((A)[8] == 'x') 147 | #define HAS_EA_I(A) ((A)[9] == 'I') 148 | 149 | enum 150 | { 151 | EA_MODE_NONE, /* No special addressing mode */ 152 | EA_MODE_AI, /* Address register indirect */ 153 | EA_MODE_PI, /* Address register indirect with postincrement */ 154 | EA_MODE_PI7, /* Address register 7 indirect with postincrement */ 155 | EA_MODE_PD, /* Address register indirect with predecrement */ 156 | EA_MODE_PD7, /* Address register 7 indirect with predecrement */ 157 | EA_MODE_DI, /* Address register indirect with displacement */ 158 | EA_MODE_IX, /* Address register indirect with index */ 159 | EA_MODE_AW, /* Absolute word */ 160 | EA_MODE_AL, /* Absolute long */ 161 | EA_MODE_PCDI, /* Program counter indirect with displacement */ 162 | EA_MODE_PCIX, /* Program counter indirect with index */ 163 | EA_MODE_I /* Immediate */ 164 | }; 165 | 166 | 167 | /* Everything we need to know about an opcode */ 168 | typedef struct 169 | { 170 | char name[MAX_NAME_LENGTH]; /* opcode handler name */ 171 | unsigned int size; /* Size of operation */ 172 | char spec_proc[MAX_SPEC_PROC_LENGTH]; /* Special processing mode */ 173 | char spec_ea[MAX_SPEC_EA_LENGTH]; /* Specified effective addressing mode */ 174 | unsigned int bits; /* Number of significant bits (used for sorting the table) */ 175 | unsigned int op_mask; /* Mask to apply for matching an opcode to a handler */ 176 | unsigned int op_match; /* Value to match after masking */ 177 | char ea_allowed[EA_ALLOWED_LENGTH]; /* Effective addressing modes allowed */ 178 | char cpu_mode[NUM_CPUS]; /* User or supervisor mode */ 179 | char cpus[NUM_CPUS+1]; /* Allowed CPUs */ 180 | unsigned int cycles[NUM_CPUS]; /* cycles for 000, 010, 020 */ 181 | } opcode_struct; 182 | 183 | 184 | /* All modifications necessary for a specific EA mode of an instruction */ 185 | typedef struct 186 | { 187 | char* fname_add; 188 | char* ea_add; 189 | unsigned int mask_add; 190 | unsigned int match_add; 191 | } ea_info_struct; 192 | 193 | 194 | /* Holds the body of a function */ 195 | typedef struct 196 | { 197 | char body[MAX_BODY_LENGTH][MAX_LINE_LENGTH+1]; 198 | int length; 199 | } body_struct; 200 | 201 | 202 | /* Holds a sequence of search / replace strings */ 203 | typedef struct 204 | { 205 | char replace[MAX_REPLACE_LENGTH][2][MAX_LINE_LENGTH+1]; 206 | int length; 207 | } replace_struct; 208 | 209 | 210 | /* Function Prototypes */ 211 | void error_exit(char* fmt, ...); 212 | void perror_exit(char* fmt, ...); 213 | int check_strsncpy(char* dst, char* src, int maxlength); 214 | int check_atoi(char* str, int *result); 215 | int skip_spaces(char* str); 216 | int num_bits(int value); 217 | int atoh(char* buff); 218 | int fgetline(char* buff, int nchars, FILE* file); 219 | int get_oper_cycles(opcode_struct* op, int ea_mode, int cpu_type); 220 | opcode_struct* find_opcode(char* name, int size, char* spec_proc, char* spec_ea); 221 | opcode_struct* find_illegal_opcode(void); 222 | int extract_opcode_info(char* src, char* name, int* size, char* spec_proc, char* spec_ea); 223 | void add_replace_string(replace_struct* replace, char* search_str, char* replace_str); 224 | void write_body(FILE* filep, body_struct* body, replace_struct* replace); 225 | void get_base_name(char* base_name, opcode_struct* op); 226 | void write_prototype(FILE* filep, char* base_name); 227 | void write_function_name(FILE* filep, char* base_name); 228 | void add_opcode_output_table_entry(opcode_struct* op, char* name); 229 | static int DECL_SPEC compare_nof_true_bits(const void* aptr, const void* bptr); 230 | void print_opcode_output_table(FILE* filep); 231 | void write_table_entry(FILE* filep, opcode_struct* op); 232 | void set_opcode_struct(opcode_struct* src, opcode_struct* dst, int ea_mode); 233 | void generate_opcode_handler(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* opinfo, int ea_mode); 234 | void generate_opcode_ea_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op); 235 | void generate_opcode_cc_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op_in, int offset); 236 | void process_opcode_handlers(void); 237 | void populate_table(void); 238 | void read_insert(char* insert); 239 | 240 | 241 | 242 | /* ======================================================================== */ 243 | /* ================================= DATA ================================= */ 244 | /* ======================================================================== */ 245 | 246 | /* Name of the input file */ 247 | char g_input_filename[MAX_PATH] = FILENAME_INPUT; 248 | 249 | /* File handles */ 250 | FILE* g_input_file = NULL; 251 | FILE* g_prototype_file = NULL; 252 | FILE* g_table_file = NULL; 253 | FILE* g_ops_ac_file = NULL; 254 | FILE* g_ops_dm_file = NULL; 255 | FILE* g_ops_nz_file = NULL; 256 | 257 | int g_num_functions = 0; /* Number of functions processed */ 258 | int g_num_primitives = 0; /* Number of function primitives read */ 259 | int g_line_number = 1; /* Current line number */ 260 | 261 | /* Opcode handler table */ 262 | opcode_struct g_opcode_input_table[MAX_OPCODE_INPUT_TABLE_LENGTH]; 263 | 264 | opcode_struct g_opcode_output_table[MAX_OPCODE_OUTPUT_TABLE_LENGTH]; 265 | int g_opcode_output_table_length = 0; 266 | 267 | ea_info_struct g_ea_info_table[13] = 268 | {/* fname ea mask match */ 269 | {"", "", 0x00, 0x00}, /* EA_MODE_NONE */ 270 | {"ai", "AY_AI", 0x38, 0x10}, /* EA_MODE_AI */ 271 | {"pi", "AY_PI", 0x38, 0x18}, /* EA_MODE_PI */ 272 | {"pi7", "A7_PI", 0x3f, 0x1f}, /* EA_MODE_PI7 */ 273 | {"pd", "AY_PD", 0x38, 0x20}, /* EA_MODE_PD */ 274 | {"pd7", "A7_PD", 0x3f, 0x27}, /* EA_MODE_PD7 */ 275 | {"di", "AY_DI", 0x38, 0x28}, /* EA_MODE_DI */ 276 | {"ix", "AY_IX", 0x38, 0x30}, /* EA_MODE_IX */ 277 | {"aw", "AW", 0x3f, 0x38}, /* EA_MODE_AW */ 278 | {"al", "AL", 0x3f, 0x39}, /* EA_MODE_AL */ 279 | {"pcdi", "PCDI", 0x3f, 0x3a}, /* EA_MODE_PCDI */ 280 | {"pcix", "PCIX", 0x3f, 0x3b}, /* EA_MODE_PCIX */ 281 | {"i", "I", 0x3f, 0x3c}, /* EA_MODE_I */ 282 | }; 283 | 284 | 285 | char* g_cc_table[16][2] = 286 | { 287 | { "t", "T"}, /* 0000 */ 288 | { "f", "F"}, /* 0001 */ 289 | {"hi", "HI"}, /* 0010 */ 290 | {"ls", "LS"}, /* 0011 */ 291 | {"cc", "CC"}, /* 0100 */ 292 | {"cs", "CS"}, /* 0101 */ 293 | {"ne", "NE"}, /* 0110 */ 294 | {"eq", "EQ"}, /* 0111 */ 295 | {"vc", "VC"}, /* 1000 */ 296 | {"vs", "VS"}, /* 1001 */ 297 | {"pl", "PL"}, /* 1010 */ 298 | {"mi", "MI"}, /* 1011 */ 299 | {"ge", "GE"}, /* 1100 */ 300 | {"lt", "LT"}, /* 1101 */ 301 | {"gt", "GT"}, /* 1110 */ 302 | {"le", "LE"}, /* 1111 */ 303 | }; 304 | 305 | /* size to index translator (0 -> 0, 8 and 16 -> 1, 32 -> 2) */ 306 | int g_size_select_table[33] = 307 | { 308 | 0, /* unsized */ 309 | 0, 0, 0, 0, 0, 0, 0, 1, /* 8 */ 310 | 0, 0, 0, 0, 0, 0, 0, 1, /* 16 */ 311 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 /* 32 */ 312 | }; 313 | 314 | /* Extra cycles required for certain EA modes */ 315 | int g_ea_cycle_table[13][NUM_CPUS][3] = 316 | {/* 000 010 020 */ 317 | {{ 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}}, /* EA_MODE_NONE */ 318 | {{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}}, /* EA_MODE_AI */ 319 | {{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}}, /* EA_MODE_PI */ 320 | {{ 0, 4, 8}, { 0, 4, 8}, { 0, 4, 4}}, /* EA_MODE_PI7 */ 321 | {{ 0, 6, 10}, { 0, 6, 10}, { 0, 5, 5}}, /* EA_MODE_PD */ 322 | {{ 0, 6, 10}, { 0, 6, 10}, { 0, 5, 5}}, /* EA_MODE_PD7 */ 323 | {{ 0, 8, 12}, { 0, 8, 12}, { 0, 5, 5}}, /* EA_MODE_DI */ 324 | {{ 0, 10, 14}, { 0, 10, 14}, { 0, 7, 7}}, /* EA_MODE_IX */ 325 | {{ 0, 8, 12}, { 0, 8, 12}, { 0, 4, 4}}, /* EA_MODE_AW */ 326 | {{ 0, 12, 16}, { 0, 12, 16}, { 0, 4, 4}}, /* EA_MODE_AL */ 327 | {{ 0, 8, 12}, { 0, 8, 12}, { 0, 5, 5}}, /* EA_MODE_PCDI */ 328 | {{ 0, 10, 14}, { 0, 10, 14}, { 0, 7, 7}}, /* EA_MODE_PCIX */ 329 | {{ 0, 4, 8}, { 0, 4, 8}, { 0, 2, 4}}, /* EA_MODE_I */ 330 | }; 331 | 332 | /* Extra cycles for JMP instruction (000, 010) */ 333 | int g_jmp_cycle_table[13] = 334 | { 335 | 0, /* EA_MODE_NONE */ 336 | 4, /* EA_MODE_AI */ 337 | 0, /* EA_MODE_PI */ 338 | 0, /* EA_MODE_PI7 */ 339 | 0, /* EA_MODE_PD */ 340 | 0, /* EA_MODE_PD7 */ 341 | 6, /* EA_MODE_DI */ 342 | 8, /* EA_MODE_IX */ 343 | 6, /* EA_MODE_AW */ 344 | 8, /* EA_MODE_AL */ 345 | 6, /* EA_MODE_PCDI */ 346 | 10, /* EA_MODE_PCIX */ 347 | 0, /* EA_MODE_I */ 348 | }; 349 | 350 | /* Extra cycles for JSR instruction (000, 010) */ 351 | int g_jsr_cycle_table[13] = 352 | { 353 | 0, /* EA_MODE_NONE */ 354 | 4, /* EA_MODE_AI */ 355 | 0, /* EA_MODE_PI */ 356 | 0, /* EA_MODE_PI7 */ 357 | 0, /* EA_MODE_PD */ 358 | 0, /* EA_MODE_PD7 */ 359 | 6, /* EA_MODE_DI */ 360 | 10, /* EA_MODE_IX */ 361 | 6, /* EA_MODE_AW */ 362 | 8, /* EA_MODE_AL */ 363 | 6, /* EA_MODE_PCDI */ 364 | 10, /* EA_MODE_PCIX */ 365 | 0, /* EA_MODE_I */ 366 | }; 367 | 368 | /* Extra cycles for LEA instruction (000, 010) */ 369 | int g_lea_cycle_table[13] = 370 | { 371 | 0, /* EA_MODE_NONE */ 372 | 4, /* EA_MODE_AI */ 373 | 0, /* EA_MODE_PI */ 374 | 0, /* EA_MODE_PI7 */ 375 | 0, /* EA_MODE_PD */ 376 | 0, /* EA_MODE_PD7 */ 377 | 8, /* EA_MODE_DI */ 378 | 12, /* EA_MODE_IX */ 379 | 8, /* EA_MODE_AW */ 380 | 12, /* EA_MODE_AL */ 381 | 8, /* EA_MODE_PCDI */ 382 | 12, /* EA_MODE_PCIX */ 383 | 0, /* EA_MODE_I */ 384 | }; 385 | 386 | /* Extra cycles for PEA instruction (000, 010) */ 387 | int g_pea_cycle_table[13] = 388 | { 389 | 0, /* EA_MODE_NONE */ 390 | 4, /* EA_MODE_AI */ 391 | 0, /* EA_MODE_PI */ 392 | 0, /* EA_MODE_PI7 */ 393 | 0, /* EA_MODE_PD */ 394 | 0, /* EA_MODE_PD7 */ 395 | 10, /* EA_MODE_DI */ 396 | 14, /* EA_MODE_IX */ 397 | 10, /* EA_MODE_AW */ 398 | 14, /* EA_MODE_AL */ 399 | 10, /* EA_MODE_PCDI */ 400 | 14, /* EA_MODE_PCIX */ 401 | 0, /* EA_MODE_I */ 402 | }; 403 | 404 | /* Extra cycles for MOVES instruction (010) */ 405 | int g_moves_cycle_table[13][3] = 406 | { 407 | { 0, 0, 0}, /* EA_MODE_NONE */ 408 | { 0, 4, 6}, /* EA_MODE_AI */ 409 | { 0, 4, 6}, /* EA_MODE_PI */ 410 | { 0, 4, 6}, /* EA_MODE_PI7 */ 411 | { 0, 6, 12}, /* EA_MODE_PD */ 412 | { 0, 6, 12}, /* EA_MODE_PD7 */ 413 | { 0, 12, 16}, /* EA_MODE_DI */ 414 | { 0, 16, 20}, /* EA_MODE_IX */ 415 | { 0, 12, 16}, /* EA_MODE_AW */ 416 | { 0, 16, 20}, /* EA_MODE_AL */ 417 | { 0, 0, 0}, /* EA_MODE_PCDI */ 418 | { 0, 0, 0}, /* EA_MODE_PCIX */ 419 | { 0, 0, 0}, /* EA_MODE_I */ 420 | }; 421 | 422 | /* Extra cycles for CLR instruction (010) */ 423 | int g_clr_cycle_table[13][3] = 424 | { 425 | { 0, 0, 0}, /* EA_MODE_NONE */ 426 | { 0, 4, 6}, /* EA_MODE_AI */ 427 | { 0, 4, 6}, /* EA_MODE_PI */ 428 | { 0, 4, 6}, /* EA_MODE_PI7 */ 429 | { 0, 6, 8}, /* EA_MODE_PD */ 430 | { 0, 6, 8}, /* EA_MODE_PD7 */ 431 | { 0, 8, 10}, /* EA_MODE_DI */ 432 | { 0, 10, 14}, /* EA_MODE_IX */ 433 | { 0, 8, 10}, /* EA_MODE_AW */ 434 | { 0, 10, 14}, /* EA_MODE_AL */ 435 | { 0, 0, 0}, /* EA_MODE_PCDI */ 436 | { 0, 0, 0}, /* EA_MODE_PCIX */ 437 | { 0, 0, 0}, /* EA_MODE_I */ 438 | }; 439 | 440 | 441 | 442 | /* ======================================================================== */ 443 | /* =========================== UTILITY FUNCTIONS ========================== */ 444 | /* ======================================================================== */ 445 | 446 | /* Print an error message and exit with status error */ 447 | void error_exit(char* fmt, ...) 448 | { 449 | va_list args; 450 | fprintf(stderr, "In %s, near or on line %d:\n\t", g_input_filename, g_line_number); 451 | va_start(args, fmt); 452 | vfprintf(stderr, fmt, args); 453 | va_end(args); 454 | fprintf(stderr, "\n"); 455 | 456 | if(g_prototype_file) fclose(g_prototype_file); 457 | if(g_table_file) fclose(g_table_file); 458 | if(g_ops_ac_file) fclose(g_ops_ac_file); 459 | if(g_ops_dm_file) fclose(g_ops_dm_file); 460 | if(g_ops_nz_file) fclose(g_ops_nz_file); 461 | if(g_input_file) fclose(g_input_file); 462 | 463 | exit(EXIT_FAILURE); 464 | } 465 | 466 | /* Print an error message, call perror(), and exit with status error */ 467 | void perror_exit(char* fmt, ...) 468 | { 469 | va_list args; 470 | va_start(args, fmt); 471 | vfprintf(stderr, fmt, args); 472 | va_end(args); 473 | perror(""); 474 | 475 | if(g_prototype_file) fclose(g_prototype_file); 476 | if(g_table_file) fclose(g_table_file); 477 | if(g_ops_ac_file) fclose(g_ops_ac_file); 478 | if(g_ops_dm_file) fclose(g_ops_dm_file); 479 | if(g_ops_nz_file) fclose(g_ops_nz_file); 480 | if(g_input_file) fclose(g_input_file); 481 | 482 | exit(EXIT_FAILURE); 483 | } 484 | 485 | 486 | /* copy until 0 or space and exit with error if we read too far */ 487 | int check_strsncpy(char* dst, char* src, int maxlength) 488 | { 489 | char* p = dst; 490 | while(*src && *src != ' ') 491 | { 492 | *p++ = *src++; 493 | if(p - dst > maxlength) 494 | error_exit("Field too long"); 495 | } 496 | *p = 0; 497 | return p - dst; 498 | } 499 | 500 | /* copy until 0 or specified character and exit with error if we read too far */ 501 | int check_strcncpy(char* dst, char* src, char delim, int maxlength) 502 | { 503 | char* p = dst; 504 | while(*src && *src != delim) 505 | { 506 | *p++ = *src++; 507 | if(p - dst > maxlength) 508 | error_exit("Field too long"); 509 | } 510 | *p = 0; 511 | return p - dst; 512 | } 513 | 514 | /* convert ascii to integer and exit with error if we find invalid data */ 515 | int check_atoi(char* str, int *result) 516 | { 517 | int accum = 0; 518 | char* p = str; 519 | while(*p >= '0' && *p <= '9') 520 | { 521 | accum *= 10; 522 | accum += *p++ - '0'; 523 | } 524 | if(*p != ' ' && *p != 0) 525 | error_exit("Malformed integer value (%c)", *p); 526 | *result = accum; 527 | return p - str; 528 | } 529 | 530 | /* Skip past spaces in a string */ 531 | int skip_spaces(char* str) 532 | { 533 | char* p = str; 534 | 535 | while(*p == ' ') 536 | p++; 537 | 538 | return p - str; 539 | } 540 | 541 | /* Count the number of set bits in a value */ 542 | int num_bits(int value) 543 | { 544 | value = ((value & 0xaaaa) >> 1) + (value & 0x5555); 545 | value = ((value & 0xcccc) >> 2) + (value & 0x3333); 546 | value = ((value & 0xf0f0) >> 4) + (value & 0x0f0f); 547 | value = ((value & 0xff00) >> 8) + (value & 0x00ff); 548 | return value; 549 | } 550 | 551 | /* Convert a hex value written in ASCII */ 552 | int atoh(char* buff) 553 | { 554 | int accum = 0; 555 | 556 | for(;;buff++) 557 | { 558 | if(*buff >= '0' && *buff <= '9') 559 | { 560 | accum <<= 4; 561 | accum += *buff - '0'; 562 | } 563 | else if(*buff >= 'a' && *buff <= 'f') 564 | { 565 | accum <<= 4; 566 | accum += *buff - 'a' + 10; 567 | } 568 | else break; 569 | } 570 | return accum; 571 | } 572 | 573 | /* Get a line of text from a file, discarding any end-of-line characters */ 574 | int fgetline(char* buff, int nchars, FILE* file) 575 | { 576 | int length; 577 | 578 | if(fgets(buff, nchars, file) == NULL) 579 | return -1; 580 | if(buff[0] == '\r') 581 | memcpy(buff, buff + 1, nchars - 1); 582 | 583 | length = strlen(buff); 584 | while(length && (buff[length-1] == '\r' || buff[length-1] == '\n')) 585 | length--; 586 | buff[length] = 0; 587 | g_line_number++; 588 | 589 | return length; 590 | } 591 | 592 | 593 | 594 | /* ======================================================================== */ 595 | /* =========================== HELPER FUNCTIONS =========================== */ 596 | /* ======================================================================== */ 597 | 598 | /* Calculate the number of cycles an opcode requires */ 599 | int get_oper_cycles(opcode_struct* op, int ea_mode, int cpu_type) 600 | { 601 | int size = g_size_select_table[op->size]; 602 | 603 | if(op->cpus[cpu_type] == '.') 604 | return 0; 605 | 606 | if(cpu_type < CPU_TYPE_020) 607 | { 608 | if(cpu_type == CPU_TYPE_010) 609 | { 610 | if(strcmp(op->name, "moves") == 0) 611 | return op->cycles[cpu_type] + g_moves_cycle_table[ea_mode][size]; 612 | if(strcmp(op->name, "clr") == 0) 613 | return op->cycles[cpu_type] + g_clr_cycle_table[ea_mode][size]; 614 | } 615 | 616 | /* ASG: added these cases -- immediate modes take 2 extra cycles here */ 617 | if(cpu_type == CPU_TYPE_000 && ea_mode == EA_MODE_I && 618 | ((strcmp(op->name, "add") == 0 && strcmp(op->spec_proc, "er") == 0) || 619 | strcmp(op->name, "adda") == 0 || 620 | (strcmp(op->name, "and") == 0 && strcmp(op->spec_proc, "er") == 0) || 621 | (strcmp(op->name, "or") == 0 && strcmp(op->spec_proc, "er") == 0) || 622 | (strcmp(op->name, "sub") == 0 && strcmp(op->spec_proc, "er") == 0) || 623 | strcmp(op->name, "suba") == 0)) 624 | return op->cycles[cpu_type] + g_ea_cycle_table[ea_mode][cpu_type][size] + 2; 625 | 626 | if(strcmp(op->name, "jmp") == 0) 627 | return op->cycles[cpu_type] + g_jmp_cycle_table[ea_mode]; 628 | if(strcmp(op->name, "jsr") == 0) 629 | return op->cycles[cpu_type] + g_jsr_cycle_table[ea_mode]; 630 | if(strcmp(op->name, "lea") == 0) 631 | return op->cycles[cpu_type] + g_lea_cycle_table[ea_mode]; 632 | if(strcmp(op->name, "pea") == 0) 633 | return op->cycles[cpu_type] + g_pea_cycle_table[ea_mode]; 634 | } 635 | return op->cycles[cpu_type] + g_ea_cycle_table[ea_mode][cpu_type][size]; 636 | } 637 | 638 | /* Find an opcode in the opcode handler list */ 639 | opcode_struct* find_opcode(char* name, int size, char* spec_proc, char* spec_ea) 640 | { 641 | opcode_struct* op; 642 | 643 | 644 | for(op = g_opcode_input_table;op->name != NULL;op++) 645 | { 646 | if( strcmp(name, op->name) == 0 && 647 | (size == (int)op->size) && 648 | strcmp(spec_proc, op->spec_proc) == 0 && 649 | strcmp(spec_ea, op->spec_ea) == 0) 650 | return op; 651 | } 652 | return NULL; 653 | } 654 | 655 | /* Specifically find the illegal opcode in the list */ 656 | opcode_struct* find_illegal_opcode(void) 657 | { 658 | opcode_struct* op; 659 | 660 | for(op = g_opcode_input_table;op->name != NULL;op++) 661 | { 662 | if(strcmp(op->name, "illegal") == 0) 663 | return op; 664 | } 665 | return NULL; 666 | } 667 | 668 | /* Parse an opcode handler name */ 669 | int extract_opcode_info(char* src, char* name, int* size, char* spec_proc, char* spec_ea) 670 | { 671 | char* ptr = strstr(src, ID_OPHANDLER_NAME); 672 | 673 | if(ptr == NULL) 674 | return 0; 675 | 676 | ptr += strlen(ID_OPHANDLER_NAME) + 1; 677 | 678 | ptr += check_strcncpy(name, ptr, ',', MAX_NAME_LENGTH); 679 | if(*ptr != ',') return 0; 680 | ptr++; 681 | ptr += skip_spaces(ptr); 682 | 683 | *size = atoi(ptr); 684 | ptr = strstr(ptr, ","); 685 | if(ptr == NULL) return 0; 686 | ptr++; 687 | ptr += skip_spaces(ptr); 688 | 689 | ptr += check_strcncpy(spec_proc, ptr, ',', MAX_SPEC_PROC_LENGTH); 690 | if(*ptr != ',') return 0; 691 | ptr++; 692 | ptr += skip_spaces(ptr); 693 | 694 | ptr += check_strcncpy(spec_ea, ptr, ')', MAX_SPEC_EA_LENGTH); 695 | if(*ptr != ')') return 0; 696 | 697 | return 1; 698 | } 699 | 700 | 701 | /* Add a search/replace pair to a replace structure */ 702 | void add_replace_string(replace_struct* replace, char* search_str, char* replace_str) 703 | { 704 | if(replace->length >= MAX_REPLACE_LENGTH) 705 | error_exit("overflow in replace structure"); 706 | 707 | strcpy(replace->replace[replace->length][0], search_str); 708 | strcpy(replace->replace[replace->length++][1], replace_str); 709 | } 710 | 711 | /* Write a function body while replacing any selected strings */ 712 | void write_body(FILE* filep, body_struct* body, replace_struct* replace) 713 | { 714 | int i; 715 | int j; 716 | char* ptr; 717 | char output[MAX_LINE_LENGTH+1]; 718 | char temp_buff[MAX_LINE_LENGTH+1]; 719 | int found; 720 | 721 | for(i=0;ilength;i++) 722 | { 723 | strcpy(output, body->body[i]); 724 | /* Check for the base directive header */ 725 | if(strstr(output, ID_BASE) != NULL) 726 | { 727 | /* Search for any text we need to replace */ 728 | found = 0; 729 | for(j=0;jlength;j++) 730 | { 731 | ptr = strstr(output, replace->replace[j][0]); 732 | if(ptr) 733 | { 734 | /* We found something to replace */ 735 | found = 1; 736 | strcpy(temp_buff, ptr+strlen(replace->replace[j][0])); 737 | strcpy(ptr, replace->replace[j][1]); 738 | strcat(ptr, temp_buff); 739 | } 740 | } 741 | /* Found a directive with no matching replace string */ 742 | if(!found) 743 | error_exit("Unknown " ID_BASE " directive"); 744 | } 745 | fprintf(filep, "%s\n", output); 746 | } 747 | fprintf(filep, "\n\n"); 748 | } 749 | 750 | /* Generate a base function name from an opcode struct */ 751 | void get_base_name(char* base_name, opcode_struct* op) 752 | { 753 | sprintf(base_name, "m68k_op_%s", op->name); 754 | if(op->size > 0) 755 | sprintf(base_name+strlen(base_name), "_%d", op->size); 756 | if(strcmp(op->spec_proc, UNSPECIFIED) != 0) 757 | sprintf(base_name+strlen(base_name), "_%s", op->spec_proc); 758 | if(strcmp(op->spec_ea, UNSPECIFIED) != 0) 759 | sprintf(base_name+strlen(base_name), "_%s", op->spec_ea); 760 | } 761 | 762 | /* Write the prototype of an opcode handler function */ 763 | void write_prototype(FILE* filep, char* base_name) 764 | { 765 | fprintf(filep, "void %s(void);\n", base_name); 766 | } 767 | 768 | /* Write the name of an opcode handler function */ 769 | void write_function_name(FILE* filep, char* base_name) 770 | { 771 | fprintf(filep, "void %s(void)\n", base_name); 772 | } 773 | 774 | void add_opcode_output_table_entry(opcode_struct* op, char* name) 775 | { 776 | opcode_struct* ptr; 777 | if(g_opcode_output_table_length > MAX_OPCODE_OUTPUT_TABLE_LENGTH) 778 | error_exit("Opcode output table overflow"); 779 | 780 | ptr = g_opcode_output_table + g_opcode_output_table_length++; 781 | 782 | *ptr = *op; 783 | strcpy(ptr->name, name); 784 | ptr->bits = num_bits(ptr->op_mask); 785 | } 786 | 787 | /* 788 | * Comparison function for qsort() 789 | * For entries with an equal number of set bits in 790 | * the mask compare the match values 791 | */ 792 | static int DECL_SPEC compare_nof_true_bits(const void* aptr, const void* bptr) 793 | { 794 | const opcode_struct *a = aptr, *b = bptr; 795 | if(a->bits != b->bits) 796 | return a->bits - b->bits; 797 | if(a->op_mask != b->op_mask) 798 | return a->op_mask - b->op_mask; 799 | return a->op_match - b->op_match; 800 | } 801 | 802 | void print_opcode_output_table(FILE* filep) 803 | { 804 | int i; 805 | qsort((void *)g_opcode_output_table, g_opcode_output_table_length, sizeof(g_opcode_output_table[0]), compare_nof_true_bits); 806 | 807 | for(i=0;iname, op->op_mask, op->op_match); 818 | 819 | for(i=0;icycles[i]); 822 | if(i < NUM_CPUS-1) 823 | fprintf(filep, ", "); 824 | } 825 | 826 | fprintf(filep, "}},\n"); 827 | } 828 | 829 | /* Fill out an opcode struct with a specific addressing mode of the source opcode struct */ 830 | void set_opcode_struct(opcode_struct* src, opcode_struct* dst, int ea_mode) 831 | { 832 | int i; 833 | 834 | *dst = *src; 835 | 836 | for(i=0;icycles[i] = get_oper_cycles(dst, ea_mode, i); 838 | if(strcmp(dst->spec_ea, UNSPECIFIED) == 0 && ea_mode != EA_MODE_NONE) 839 | sprintf(dst->spec_ea, "%s", g_ea_info_table[ea_mode].fname_add); 840 | dst->op_mask |= g_ea_info_table[ea_mode].mask_add; 841 | dst->op_match |= g_ea_info_table[ea_mode].match_add; 842 | } 843 | 844 | 845 | /* Generate a final opcode handler from the provided data */ 846 | void generate_opcode_handler(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* opinfo, int ea_mode) 847 | { 848 | char str[MAX_LINE_LENGTH+1]; 849 | opcode_struct* op = malloc(sizeof(opcode_struct)); 850 | 851 | /* Set the opcode structure and write the tables, prototypes, etc */ 852 | set_opcode_struct(opinfo, op, ea_mode); 853 | get_base_name(str, op); 854 | write_prototype(g_prototype_file, str); 855 | add_opcode_output_table_entry(op, str); 856 | write_function_name(filep, str); 857 | 858 | /* Add any replace strings needed */ 859 | if(ea_mode != EA_MODE_NONE) 860 | { 861 | sprintf(str, "EA_%s_8()", g_ea_info_table[ea_mode].ea_add); 862 | add_replace_string(replace, ID_OPHANDLER_EA_AY_8, str); 863 | sprintf(str, "EA_%s_16()", g_ea_info_table[ea_mode].ea_add); 864 | add_replace_string(replace, ID_OPHANDLER_EA_AY_16, str); 865 | sprintf(str, "EA_%s_32()", g_ea_info_table[ea_mode].ea_add); 866 | add_replace_string(replace, ID_OPHANDLER_EA_AY_32, str); 867 | sprintf(str, "OPER_%s_8()", g_ea_info_table[ea_mode].ea_add); 868 | add_replace_string(replace, ID_OPHANDLER_OPER_AY_8, str); 869 | sprintf(str, "OPER_%s_16()", g_ea_info_table[ea_mode].ea_add); 870 | add_replace_string(replace, ID_OPHANDLER_OPER_AY_16, str); 871 | sprintf(str, "OPER_%s_32()", g_ea_info_table[ea_mode].ea_add); 872 | add_replace_string(replace, ID_OPHANDLER_OPER_AY_32, str); 873 | } 874 | 875 | /* Now write the function body with the selected replace strings */ 876 | write_body(filep, body, replace); 877 | g_num_functions++; 878 | free(op); 879 | } 880 | 881 | /* Generate opcode variants based on available addressing modes */ 882 | void generate_opcode_ea_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op) 883 | { 884 | int old_length = replace->length; 885 | 886 | /* No ea modes available for this opcode */ 887 | if(HAS_NO_EA_MODE(op->ea_allowed)) 888 | { 889 | generate_opcode_handler(filep, body, replace, op, EA_MODE_NONE); 890 | return; 891 | } 892 | 893 | /* Check for and create specific opcodes for each available addressing mode */ 894 | if(HAS_EA_AI(op->ea_allowed)) 895 | generate_opcode_handler(filep, body, replace, op, EA_MODE_AI); 896 | replace->length = old_length; 897 | if(HAS_EA_PI(op->ea_allowed)) 898 | { 899 | generate_opcode_handler(filep, body, replace, op, EA_MODE_PI); 900 | replace->length = old_length; 901 | if(op->size == 8) 902 | generate_opcode_handler(filep, body, replace, op, EA_MODE_PI7); 903 | } 904 | replace->length = old_length; 905 | if(HAS_EA_PD(op->ea_allowed)) 906 | { 907 | generate_opcode_handler(filep, body, replace, op, EA_MODE_PD); 908 | replace->length = old_length; 909 | if(op->size == 8) 910 | generate_opcode_handler(filep, body, replace, op, EA_MODE_PD7); 911 | } 912 | replace->length = old_length; 913 | if(HAS_EA_DI(op->ea_allowed)) 914 | generate_opcode_handler(filep, body, replace, op, EA_MODE_DI); 915 | replace->length = old_length; 916 | if(HAS_EA_IX(op->ea_allowed)) 917 | generate_opcode_handler(filep, body, replace, op, EA_MODE_IX); 918 | replace->length = old_length; 919 | if(HAS_EA_AW(op->ea_allowed)) 920 | generate_opcode_handler(filep, body, replace, op, EA_MODE_AW); 921 | replace->length = old_length; 922 | if(HAS_EA_AL(op->ea_allowed)) 923 | generate_opcode_handler(filep, body, replace, op, EA_MODE_AL); 924 | replace->length = old_length; 925 | if(HAS_EA_PCDI(op->ea_allowed)) 926 | generate_opcode_handler(filep, body, replace, op, EA_MODE_PCDI); 927 | replace->length = old_length; 928 | if(HAS_EA_PCIX(op->ea_allowed)) 929 | generate_opcode_handler(filep, body, replace, op, EA_MODE_PCIX); 930 | replace->length = old_length; 931 | if(HAS_EA_I(op->ea_allowed)) 932 | generate_opcode_handler(filep, body, replace, op, EA_MODE_I); 933 | replace->length = old_length; 934 | } 935 | 936 | /* Generate variants of condition code opcodes */ 937 | void generate_opcode_cc_variants(FILE* filep, body_struct* body, replace_struct* replace, opcode_struct* op_in, int offset) 938 | { 939 | char repl[20]; 940 | char replnot[20]; 941 | int i; 942 | int old_length = replace->length; 943 | opcode_struct* op = malloc(sizeof(opcode_struct)); 944 | 945 | *op = *op_in; 946 | 947 | op->op_mask |= 0x0f00; 948 | 949 | /* Do all condition codes except t and f */ 950 | for(i=2;i<16;i++) 951 | { 952 | /* Add replace strings for this condition code */ 953 | sprintf(repl, "COND_%s()", g_cc_table[i][1]); 954 | sprintf(replnot, "COND_NOT_%s()", g_cc_table[i][1]); 955 | 956 | add_replace_string(replace, ID_OPHANDLER_CC, repl); 957 | add_replace_string(replace, ID_OPHANDLER_NOT_CC, replnot); 958 | 959 | /* Set the new opcode info */ 960 | strcpy(op->name+offset, g_cc_table[i][0]); 961 | 962 | op->op_match = (op->op_match & 0xf0ff) | (i<<8); 963 | 964 | /* Generate all opcode variants for this modified opcode */ 965 | generate_opcode_ea_variants(filep, body, replace, op); 966 | /* Remove the above replace strings */ 967 | replace->length = old_length; 968 | } 969 | free(op); 970 | } 971 | 972 | /* Process the opcode handlers section of the input file */ 973 | void process_opcode_handlers(void) 974 | { 975 | FILE* input_file = g_input_file; 976 | FILE* output_file; 977 | char func_name[MAX_LINE_LENGTH+1]; 978 | char oper_name[MAX_LINE_LENGTH+1]; 979 | int oper_size; 980 | char oper_spec_proc[MAX_LINE_LENGTH+1]; 981 | char oper_spec_ea[MAX_LINE_LENGTH+1]; 982 | opcode_struct* opinfo; 983 | replace_struct* replace = malloc(sizeof(replace_struct)); 984 | body_struct* body = malloc(sizeof(body_struct)); 985 | 986 | 987 | output_file = g_ops_ac_file; 988 | 989 | for(;;) 990 | { 991 | /* Find the first line of the function */ 992 | func_name[0] = 0; 993 | while(strstr(func_name, ID_OPHANDLER_NAME) == NULL) 994 | { 995 | if(strcmp(func_name, ID_INPUT_SEPARATOR) == 0) 996 | { 997 | free(replace); 998 | free(body); 999 | return; /* all done */ 1000 | } 1001 | if(fgetline(func_name, MAX_LINE_LENGTH, input_file) < 0) 1002 | error_exit("Premature end of file when getting function name"); 1003 | } 1004 | /* Get the rest of the function */ 1005 | for(body->length=0;;body->length++) 1006 | { 1007 | if(body->length > MAX_BODY_LENGTH) 1008 | error_exit("Function too long"); 1009 | 1010 | if(fgetline(body->body[body->length], MAX_LINE_LENGTH, input_file) < 0) 1011 | error_exit("Premature end of file when getting function body"); 1012 | 1013 | if(body->body[body->length][0] == '}') 1014 | { 1015 | body->length++; 1016 | break; 1017 | } 1018 | } 1019 | 1020 | g_num_primitives++; 1021 | 1022 | /* Extract the function name information */ 1023 | if(!extract_opcode_info(func_name, oper_name, &oper_size, oper_spec_proc, oper_spec_ea)) 1024 | error_exit("Invalid " ID_OPHANDLER_NAME " format"); 1025 | 1026 | /* Find the corresponding table entry */ 1027 | opinfo = find_opcode(oper_name, oper_size, oper_spec_proc, oper_spec_ea); 1028 | if(opinfo == NULL) 1029 | error_exit("Unable to find matching table entry for %s", func_name); 1030 | 1031 | /* Change output files if we pass 'c' or 'n' */ 1032 | if(output_file == g_ops_ac_file && oper_name[0] > 'c') 1033 | output_file = g_ops_dm_file; 1034 | else if(output_file == g_ops_dm_file && oper_name[0] > 'm') 1035 | output_file = g_ops_nz_file; 1036 | 1037 | replace->length = 0; 1038 | 1039 | /* Generate opcode variants */ 1040 | if(strcmp(opinfo->name, "bcc") == 0 || strcmp(opinfo->name, "scc") == 0) 1041 | generate_opcode_cc_variants(output_file, body, replace, opinfo, 1); 1042 | else if(strcmp(opinfo->name, "dbcc") == 0) 1043 | generate_opcode_cc_variants(output_file, body, replace, opinfo, 2); 1044 | else if(strcmp(opinfo->name, "trapcc") == 0) 1045 | generate_opcode_cc_variants(output_file, body, replace, opinfo, 4); 1046 | else 1047 | generate_opcode_ea_variants(output_file, body, replace, opinfo); 1048 | } 1049 | } 1050 | 1051 | 1052 | /* Populate the opcode handler table from the input file */ 1053 | void populate_table(void) 1054 | { 1055 | char* ptr; 1056 | char bitpattern[17]; 1057 | opcode_struct* op; 1058 | char buff[MAX_LINE_LENGTH]; 1059 | int i; 1060 | int temp; 1061 | 1062 | buff[0] = 0; 1063 | 1064 | /* Find the start of the table */ 1065 | while(strcmp(buff, ID_TABLE_START) != 0) 1066 | if(fgetline(buff, MAX_LINE_LENGTH, g_input_file) < 0) 1067 | error_exit("Premature EOF while reading table"); 1068 | 1069 | /* Process the entire table */ 1070 | for(op = g_opcode_input_table;;op++) 1071 | { 1072 | if(fgetline(buff, MAX_LINE_LENGTH, g_input_file) < 0) 1073 | error_exit("Premature EOF while reading table"); 1074 | if(strlen(buff) == 0) 1075 | continue; 1076 | /* We finish when we find an input separator */ 1077 | if(strcmp(buff, ID_INPUT_SEPARATOR) == 0) 1078 | break; 1079 | 1080 | /* Extract the info from the table */ 1081 | ptr = buff; 1082 | 1083 | /* Name */ 1084 | ptr += skip_spaces(ptr); 1085 | ptr += check_strsncpy(op->name, ptr, MAX_NAME_LENGTH); 1086 | 1087 | /* Size */ 1088 | ptr += skip_spaces(ptr); 1089 | ptr += check_atoi(ptr, &temp); 1090 | op->size = (unsigned char)temp; 1091 | 1092 | /* Special processing */ 1093 | ptr += skip_spaces(ptr); 1094 | ptr += check_strsncpy(op->spec_proc, ptr, MAX_SPEC_PROC_LENGTH); 1095 | 1096 | /* Specified EA Mode */ 1097 | ptr += skip_spaces(ptr); 1098 | ptr += check_strsncpy(op->spec_ea, ptr, MAX_SPEC_EA_LENGTH); 1099 | 1100 | /* Bit Pattern (more processing later) */ 1101 | ptr += skip_spaces(ptr); 1102 | ptr += check_strsncpy(bitpattern, ptr, 17); 1103 | 1104 | /* Allowed Addressing Mode List */ 1105 | ptr += skip_spaces(ptr); 1106 | ptr += check_strsncpy(op->ea_allowed, ptr, EA_ALLOWED_LENGTH); 1107 | 1108 | /* CPU operating mode (U = user or supervisor, S = supervisor only */ 1109 | ptr += skip_spaces(ptr); 1110 | for(i=0;icpu_mode[i] = *ptr++; 1113 | ptr += skip_spaces(ptr); 1114 | } 1115 | 1116 | /* Allowed CPUs for this instruction */ 1117 | for(i=0;icpus[i] = UNSPECIFIED_CH; 1123 | op->cycles[i] = 0; 1124 | ptr++; 1125 | } 1126 | else 1127 | { 1128 | op->cpus[i] = (char)('0' + i); 1129 | ptr += check_atoi(ptr, &temp); 1130 | op->cycles[i] = (unsigned char)temp; 1131 | } 1132 | } 1133 | 1134 | /* generate mask and match from bitpattern */ 1135 | op->op_mask = 0; 1136 | op->op_match = 0; 1137 | for(i=0;i<16;i++) 1138 | { 1139 | op->op_mask |= (bitpattern[i] != '.') << (15-i); 1140 | op->op_match |= (bitpattern[i] == '1') << (15-i); 1141 | } 1142 | } 1143 | /* Terminate the list */ 1144 | op->name[0] = 0; 1145 | } 1146 | 1147 | /* Read a header or footer insert from the input file */ 1148 | void read_insert(char* insert) 1149 | { 1150 | char* ptr = insert; 1151 | char* overflow = insert + MAX_INSERT_LENGTH - MAX_LINE_LENGTH; 1152 | int length; 1153 | char* first_blank = NULL; 1154 | 1155 | /* Skip any leading blank lines */ 1156 | for(length = 0;length == 0;length = fgetline(ptr, MAX_LINE_LENGTH, g_input_file)) 1157 | if(ptr >= overflow) 1158 | error_exit("Buffer overflow reading inserts"); 1159 | if(length < 0) 1160 | error_exit("Premature EOF while reading inserts"); 1161 | 1162 | /* Advance and append newline */ 1163 | ptr += length; 1164 | strcpy(ptr++, "\n"); 1165 | 1166 | /* Read until next separator */ 1167 | for(;;) 1168 | { 1169 | /* Read a new line */ 1170 | if(ptr >= overflow) 1171 | error_exit("Buffer overflow reading inserts"); 1172 | if((length = fgetline(ptr, MAX_LINE_LENGTH, g_input_file)) < 0) 1173 | error_exit("Premature EOF while reading inserts"); 1174 | 1175 | /* Stop if we read a separator */ 1176 | if(strcmp(ptr, ID_INPUT_SEPARATOR) == 0) 1177 | break; 1178 | 1179 | /* keep track in case there are trailing blanks */ 1180 | if(length == 0) 1181 | { 1182 | if(first_blank == NULL) 1183 | first_blank = ptr; 1184 | } 1185 | else 1186 | first_blank = NULL; 1187 | 1188 | /* Advance and append newline */ 1189 | ptr += length; 1190 | strcpy(ptr++, "\n"); 1191 | } 1192 | 1193 | /* kill any trailing blank lines */ 1194 | if(first_blank) 1195 | ptr = first_blank; 1196 | *ptr = 0; 1197 | } 1198 | 1199 | 1200 | 1201 | /* ======================================================================== */ 1202 | /* ============================= MAIN FUNCTION ============================ */ 1203 | /* ======================================================================== */ 1204 | 1205 | int main(int argc, char **argv) 1206 | { 1207 | /* File stuff */ 1208 | char output_path[MAX_DIR] = ""; 1209 | char filename[MAX_PATH]; 1210 | /* Section identifier */ 1211 | char section_id[MAX_LINE_LENGTH+1]; 1212 | /* Inserts */ 1213 | char temp_insert[MAX_INSERT_LENGTH+1]; 1214 | char prototype_footer_insert[MAX_INSERT_LENGTH+1]; 1215 | char table_footer_insert[MAX_INSERT_LENGTH+1]; 1216 | char ophandler_footer_insert[MAX_INSERT_LENGTH+1]; 1217 | /* Flags if we've processed certain parts already */ 1218 | int prototype_header_read = 0; 1219 | int prototype_footer_read = 0; 1220 | int table_header_read = 0; 1221 | int table_footer_read = 0; 1222 | int ophandler_header_read = 0; 1223 | int ophandler_footer_read = 0; 1224 | int table_body_read = 0; 1225 | int ophandler_body_read = 0; 1226 | 1227 | printf("\n\t\tMusashi v%s 68000, 68010, 68EC020, 68020 emulator\n", g_version); 1228 | printf("\t\tCopyright 1998-2000 Karl Stenerud (karl@mame.net)\n\n"); 1229 | 1230 | /* Check if output path and source for the input file are given */ 1231 | if(argc > 1) 1232 | { 1233 | char *ptr; 1234 | strcpy(output_path, argv[1]); 1235 | 1236 | for(ptr = strchr(output_path, '\\'); ptr; ptr = strchr(ptr, '\\')) 1237 | *ptr = '/'; 1238 | if(output_path[strlen(output_path)-1] != '/') 1239 | strcat(output_path, "/"); 1240 | if(argc > 2) 1241 | strcpy(g_input_filename, argv[2]); 1242 | } 1243 | 1244 | 1245 | /* Open the files we need */ 1246 | sprintf(filename, "%s%s", output_path, FILENAME_PROTOTYPE); 1247 | if((g_prototype_file = fopen(filename, "wt")) == NULL) 1248 | perror_exit("Unable to create prototype file (%s)\n", filename); 1249 | 1250 | sprintf(filename, "%s%s", output_path, FILENAME_TABLE); 1251 | if((g_table_file = fopen(filename, "wt")) == NULL) 1252 | perror_exit("Unable to create table file (%s)\n", filename); 1253 | 1254 | sprintf(filename, "%s%s", output_path, FILENAME_OPS_AC); 1255 | if((g_ops_ac_file = fopen(filename, "wt")) == NULL) 1256 | perror_exit("Unable to create ops ac file (%s)\n", filename); 1257 | 1258 | sprintf(filename, "%s%s", output_path, FILENAME_OPS_DM); 1259 | if((g_ops_dm_file = fopen(filename, "wt")) == NULL) 1260 | perror_exit("Unable to create ops dm file (%s)\n", filename); 1261 | 1262 | sprintf(filename, "%s%s", output_path, FILENAME_OPS_NZ); 1263 | if((g_ops_nz_file = fopen(filename, "wt")) == NULL) 1264 | perror_exit("Unable to create ops nz file (%s)\n", filename); 1265 | 1266 | if((g_input_file=fopen(g_input_filename, "rt")) == NULL) 1267 | perror_exit("can't open %s for input", g_input_filename); 1268 | 1269 | 1270 | /* Get to the first section of the input file */ 1271 | section_id[0] = 0; 1272 | while(strcmp(section_id, ID_INPUT_SEPARATOR) != 0) 1273 | if(fgetline(section_id, MAX_LINE_LENGTH, g_input_file) < 0) 1274 | error_exit("Premature EOF while reading input file"); 1275 | 1276 | /* Now process all sections */ 1277 | for(;;) 1278 | { 1279 | if(fgetline(section_id, MAX_LINE_LENGTH, g_input_file) < 0) 1280 | error_exit("Premature EOF while reading input file"); 1281 | if(strcmp(section_id, ID_PROTOTYPE_HEADER) == 0) 1282 | { 1283 | if(prototype_header_read) 1284 | error_exit("Duplicate prototype header"); 1285 | read_insert(temp_insert); 1286 | fprintf(g_prototype_file, "%s\n\n", temp_insert); 1287 | prototype_header_read = 1; 1288 | } 1289 | else if(strcmp(section_id, ID_TABLE_HEADER) == 0) 1290 | { 1291 | if(table_header_read) 1292 | error_exit("Duplicate table header"); 1293 | read_insert(temp_insert); 1294 | fprintf(g_table_file, "%s", temp_insert); 1295 | table_header_read = 1; 1296 | } 1297 | else if(strcmp(section_id, ID_OPHANDLER_HEADER) == 0) 1298 | { 1299 | if(ophandler_header_read) 1300 | error_exit("Duplicate opcode handler header"); 1301 | read_insert(temp_insert); 1302 | fprintf(g_ops_ac_file, "%s\n\n", temp_insert); 1303 | fprintf(g_ops_dm_file, "%s\n\n", temp_insert); 1304 | fprintf(g_ops_nz_file, "%s\n\n", temp_insert); 1305 | ophandler_header_read = 1; 1306 | } 1307 | else if(strcmp(section_id, ID_PROTOTYPE_FOOTER) == 0) 1308 | { 1309 | if(prototype_footer_read) 1310 | error_exit("Duplicate prototype footer"); 1311 | read_insert(prototype_footer_insert); 1312 | prototype_footer_read = 1; 1313 | } 1314 | else if(strcmp(section_id, ID_TABLE_FOOTER) == 0) 1315 | { 1316 | if(table_footer_read) 1317 | error_exit("Duplicate table footer"); 1318 | read_insert(table_footer_insert); 1319 | table_footer_read = 1; 1320 | } 1321 | else if(strcmp(section_id, ID_OPHANDLER_FOOTER) == 0) 1322 | { 1323 | if(ophandler_footer_read) 1324 | error_exit("Duplicate opcode handler footer"); 1325 | read_insert(ophandler_footer_insert); 1326 | ophandler_footer_read = 1; 1327 | } 1328 | else if(strcmp(section_id, ID_TABLE_BODY) == 0) 1329 | { 1330 | if(!prototype_header_read) 1331 | error_exit("Table body encountered before prototype header"); 1332 | if(!table_header_read) 1333 | error_exit("Table body encountered before table header"); 1334 | if(!ophandler_header_read) 1335 | error_exit("Table body encountered before opcode handler header"); 1336 | 1337 | if(table_body_read) 1338 | error_exit("Duplicate table body"); 1339 | 1340 | populate_table(); 1341 | table_body_read = 1; 1342 | } 1343 | else if(strcmp(section_id, ID_OPHANDLER_BODY) == 0) 1344 | { 1345 | if(!prototype_header_read) 1346 | error_exit("Opcode handlers encountered before prototype header"); 1347 | if(!table_header_read) 1348 | error_exit("Opcode handlers encountered before table header"); 1349 | if(!ophandler_header_read) 1350 | error_exit("Opcode handlers encountered before opcode handler header"); 1351 | if(!table_body_read) 1352 | error_exit("Opcode handlers encountered before table body"); 1353 | 1354 | if(ophandler_body_read) 1355 | error_exit("Duplicate opcode handler section"); 1356 | 1357 | process_opcode_handlers(); 1358 | 1359 | ophandler_body_read = 1; 1360 | } 1361 | else if(strcmp(section_id, ID_END) == 0) 1362 | { 1363 | /* End of input file. Do a sanity check and then write footers */ 1364 | if(!prototype_header_read) 1365 | error_exit("Missing prototype header"); 1366 | if(!prototype_footer_read) 1367 | error_exit("Missing prototype footer"); 1368 | if(!table_header_read) 1369 | error_exit("Missing table header"); 1370 | if(!table_footer_read) 1371 | error_exit("Missing table footer"); 1372 | if(!table_body_read) 1373 | error_exit("Missing table body"); 1374 | if(!ophandler_header_read) 1375 | error_exit("Missing opcode handler header"); 1376 | if(!ophandler_footer_read) 1377 | error_exit("Missing opcode handler footer"); 1378 | if(!ophandler_body_read) 1379 | error_exit("Missing opcode handler body"); 1380 | 1381 | print_opcode_output_table(g_table_file); 1382 | 1383 | fprintf(g_prototype_file, "%s\n\n", prototype_footer_insert); 1384 | fprintf(g_table_file, "%s\n\n", table_footer_insert); 1385 | fprintf(g_ops_ac_file, "%s\n\n", ophandler_footer_insert); 1386 | fprintf(g_ops_dm_file, "%s\n\n", ophandler_footer_insert); 1387 | fprintf(g_ops_nz_file, "%s\n\n", ophandler_footer_insert); 1388 | 1389 | break; 1390 | } 1391 | else 1392 | { 1393 | error_exit("Unknown section identifier: %s", section_id); 1394 | } 1395 | } 1396 | 1397 | /* Close all files and exit */ 1398 | fclose(g_prototype_file); 1399 | fclose(g_table_file); 1400 | fclose(g_ops_ac_file); 1401 | fclose(g_ops_dm_file); 1402 | fclose(g_ops_nz_file); 1403 | fclose(g_input_file); 1404 | 1405 | printf("Generated %d opcode handlers from %d primitives\n", g_num_functions, g_num_primitives); 1406 | 1407 | return 0; 1408 | } 1409 | 1410 | 1411 | 1412 | /* ======================================================================== */ 1413 | /* ============================== END OF FILE ============================= */ 1414 | /* ======================================================================== */ 1415 | -------------------------------------------------------------------------------- /m68k/readme.txt: -------------------------------------------------------------------------------- 1 | MUSASHI 2 | ======= 3 | 4 | Version 3.3 5 | 6 | A portable Motorola M680x0 processor emulation engine. 7 | Copyright 1998-2001 Karl Stenerud. All rights reserved. 8 | 9 | 10 | 11 | INTRODUCTION: 12 | ------------ 13 | 14 | Musashi is a Motorola 68000, 68010, 68EC020, and 68020 emulator written in C. 15 | This emulator was written with two goals in mind: portability and speed. 16 | 17 | The emulator is written to ANSI C specifications with the exception that I use 18 | inline functions. This is not compliant to the ANSI spec, but will be 19 | compliant to the ANSI C9X spec. 20 | 21 | It has been successfully running in the MAME project (www.mame.net) for over 2 22 | years and so has had time to mature. 23 | 24 | 25 | 26 | LICENSE AND COPYRIGHT: 27 | --------------------- 28 | 29 | The Musashi M680x0 emulator is copyright 1998-2001 Karl Stenerud. 30 | 31 | The source code included in this archive is provided AS-IS, free for any 32 | non-commercial purpose. 33 | 34 | If you build a program using this core, please give credit to the author. 35 | 36 | If you wish to use this core in a commercial environment, please contact 37 | the author to discuss commercial licensing. 38 | 39 | 40 | 41 | AVAILABILITY: 42 | ------------ 43 | The latest version of this code can be obtained at: 44 | http://kstenerud.cjb.net 45 | 46 | 47 | 48 | CONTACTING THE AUTHOR: 49 | --------------------- 50 | I can be reached at kstenerud@mame.net 51 | 52 | 53 | 54 | BASIC CONFIGURATION: 55 | ------------------- 56 | The basic configuration will give you a standard 68000 that has sufficient 57 | functionality to work in a primitive environment. 58 | 59 | This setup assumes that you only have 1 device interrupting it, that the 60 | device will always request an autovectored interrupt, and it will always clear 61 | the interrupt before the interrupt service routine finishes (but could 62 | possibly re-assert the interrupt). 63 | You will have only one address space, no tracing, and no instruction prefetch. 64 | 65 | To implement the basic configuration: 66 | 67 | - Open m68kconf.h and verify that the settings for INLINE and DECL_SPEC will 68 | work with your compiler. (They are set for gcc) 69 | 70 | - In your host program, implement the following functions: 71 | unsigned int m68k_read_memory_8(unsigned int address); 72 | unsigned int m68k_read_memory_16(unsigned int address); 73 | unsigned int m68k_read_memory_32(unsigned int address); 74 | void m68k_write_memory_8(unsigned int address, unsigned int value); 75 | void m68k_write_memory_16(unsigned int address, unsigned int value); 76 | void m68k_write_memory_32(unsigned int address, unsigned int value); 77 | 78 | - In your host program, be sure to call m68k_pulse_reset() once before calling 79 | any of the other functions as this initializes the core. 80 | 81 | - Use m68k_execute() to execute instructions and m68k_set_irq() to cause an 82 | interrupt. 83 | 84 | 85 | 86 | ADDING PROPER INTERRUPT HANDLING: 87 | -------------------------------- 88 | The interrupt handling in the basic configuration doesn't emulate the 89 | interrupt acknowledge phase of the CPU and automatically clears an interrupt 90 | request during interrupt processing. 91 | While this works for most systems, you may need more accurate interrupt 92 | handling. 93 | 94 | To add proper interrupt handling: 95 | 96 | - In m68kconf.h, set M68K_EMULATE_INT_ACK to OPT_SPECIFY_HANDLER 97 | 98 | - In m68kconf.h, set M68K_INT_ACK_CALLBACK(A) to your interrupt acknowledge 99 | routine 100 | 101 | - Your interrupt acknowledge routine must return an interrupt vector, 102 | M68K_INT_ACK_AUTOVECTOR, or M68K_INT_ACK_SPURIOUS. most m68k 103 | implementations just use autovectored interrupts. 104 | 105 | - When the interrupting device is satisfied, you must call m68k_set_irq(0) to 106 | remove the interrupt request. 107 | 108 | 109 | 110 | MULTIPLE INTERRUPTS: 111 | ------------------- 112 | The above system will work if you have only one device interrupting the CPU, 113 | but if you have more than one device, you must do a bit more. 114 | 115 | To add multiple interrupts: 116 | 117 | - You must make an interrupt arbitration device that will take the highest 118 | priority interrupt and encode it onto the IRQ pins on the CPU. 119 | 120 | - The interrupt arbitration device should use m68k_set_irq() to set the 121 | highest pending interrupt, or 0 for no interrupts pending. 122 | 123 | 124 | 125 | SEPARATE IMMEDIATE AND PC-RELATIVE READS: 126 | ---------------------------------------- 127 | You can write faster memory access functions if you know whether you are 128 | fetching from ROM or RAM. Immediate reads are always from the program space 129 | (Always in ROM unless it is running self-modifying code). 130 | This will also separate the pc-relative reads, since some systems treat 131 | PROGRAM mode reads and DATA mode reads differently (for program encryption, 132 | for instance). See the section below (ADDRESS SPACE) for an explanation of 133 | PROGRAM and DATA mode. 134 | 135 | To enable separate reads: 136 | 137 | - In m68kconf.h, turn on M68K_SEPARATE_READS. 138 | 139 | - In your host program, implement the following functions: 140 | unsigned int m68k_read_immediate_16(unsigned int address); 141 | unsigned int m68k_read_immediate_32(unsigned int address); 142 | 143 | unsigned int m68k_read_pcrelative_8(unsigned int address); 144 | unsigned int m68k_read_pcrelative_16(unsigned int address); 145 | unsigned int m68k_read_pcrelative_32(unsigned int address); 146 | 147 | - If you need to know the current PC (for banking and such), set 148 | M68K_MONITOR_PC to OPT_SPECIFY_HANDLER, and set M68K_SET_PC_CALLBACK(A) to 149 | your routine. 150 | 151 | 152 | 153 | ADDRESS SPACES: 154 | -------------- 155 | Most systems will only implement one address space, placing ROM at the lower 156 | addresses and RAM at the higher. However, there is the possibility that a 157 | system will implement ROM and RAM in the same address range, but in different 158 | address spaces, or will have different mamory types that require different 159 | handling for the program and the data. 160 | 161 | The 68k accomodates this by allowing different program spaces, the most 162 | important to us being PROGRAM and DATA space. Here is a breakdown of 163 | how information is fetched: 164 | 165 | - All immediate reads are fetched from PROGRAM space. 166 | 167 | - All PC-relative reads are fetched from PROGRAM space. 168 | 169 | - The initial stack pointer and program counter are fetched from PROGRAM space. 170 | 171 | - All other reads (except for those from the moves instruction for 68020) 172 | are fetched from DATA space. 173 | 174 | The m68k deals with this by encoding the requested address space on the 175 | function code pins: 176 | 177 | FC 178 | Address Space 210 179 | ------------------ --- 180 | USER DATA 001 181 | USER PROGRAM 010 182 | SUPERVISOR DATA 101 183 | SUPERVISOR PROGRAM 110 184 | CPU SPACE 111 <-- not emulated in this core since we emulate 185 | interrupt acknowledge in another way. 186 | 187 | Problems arise here if you need to emulate this distinction (if, for example, 188 | your ROM and RAM are at the same address range, with RAM and ROM enable 189 | wired to the function code pins). 190 | 191 | There are 2 ways to deal with this situation using Musashi: 192 | 193 | 1. If you only need the distinction between PROGRAM and DATA (the most common), 194 | you can just separate the reads (see the preceeding section). This is the 195 | faster solution. 196 | 197 | 2. You can emulate the function code pins entirely. 198 | 199 | To emulate the function code pins: 200 | 201 | - In m68kconf.h, set M68K_EMULATE_FC to OPT_SPECIFY_HANDLER and set 202 | M68K_SET_FC_CALLBACK(A) to your function code handler function. 203 | 204 | - Your function code handler should select the proper address space for 205 | subsequent calls to m68k_read_xx (and m68k_write_xx for 68010+). 206 | 207 | Note: immediate reads are always done from program space, so technically you 208 | don't need to implement the separate immediate reads, although you could 209 | gain more speed improvements leaving them in and doing some clever 210 | programming. 211 | 212 | 213 | 214 | USING DIFFERENT CPU TYPES: 215 | ------------------------- 216 | The default is to enable only the 68000 cpu type. To change this, change the 217 | settings for M68K_EMULATE_010 etc in m68kconf.h. 218 | 219 | To set the CPU type you want to use: 220 | 221 | - Make sure it is enabled in m68kconf.h. Current switches are: 222 | M68K_EMULATE_010 223 | M68K_EMULATE_EC020 224 | M68K_EMULATE_020 225 | 226 | - In your host program, call m68k_set_cpu_type() and then call 227 | m68k_pulse_reset(). Valid CPU types are: 228 | M68K_CPU_TYPE_68000, 229 | M68K_CPU_TYPE_68010, 230 | M68K_CPU_TYPE_68EC020, 231 | M68K_CPU_TYPE_68020 232 | 233 | 234 | 235 | CLOCK FREQUENCY: 236 | --------------- 237 | In order to emulate the correct clock frequency, you will have to calculate 238 | how long it takes the emulation to execute a certain number of "cycles" and 239 | vary your calls to m68k_execute() accordingly. 240 | As well, it is a good idea to take away the CPU's timeslice when it writes to 241 | a memory-mapped port in order to give the device it wrote to a chance to 242 | react. 243 | 244 | You can use the functions m68k_cycles_run(), m68k_cycles_remaining(), 245 | m68k_modify_timeslice(), and m68k_end_timeslice() to do this. 246 | Try to use large cycle values in your calls to m68k_execute() since it will 247 | increase throughput. You can always take away the timeslice later. 248 | 249 | 250 | 251 | MORE CORRECT EMULATION: 252 | ---------------------- 253 | You may need to enable these in order to properly emulate some of the more 254 | obscure functions of the m68k: 255 | 256 | - M68K_EMULATE_BKPT_ACK causes the CPU to call a breakpoint handler on a BKPT 257 | instruction 258 | 259 | - M68K_EMULATE_TRACE causes the CPU to generate trace exceptions when the 260 | trace bits are set 261 | 262 | - M68K_EMULATE_RESET causes the CPU to call a reset handler on a RESET 263 | instruction. 264 | 265 | - M68K_EMULATE_PREFETCH emulates the 4-word instruction prefetch that is part 266 | of the 68000/68010 (needed for Amiga emulation). 267 | 268 | - call m68k_pulse_halt() to emulate the HALT pin. 269 | 270 | 271 | 272 | CONVENIENCE FUNCTIONS: 273 | --------------------- 274 | These are in here for programmer convenience: 275 | 276 | - M68K_INSTRUCTION_HOOK lets you call a handler before each instruction. 277 | 278 | - M68K_LOG_ENABLE and M68K_LOG_1010_1111 lets you log illegal and A/F-line 279 | instructions. 280 | 281 | 282 | 283 | MULTIPLE CPU EMULATION: 284 | ---------------------- 285 | The default is to use only one CPU. To use more than one CPU in this core, 286 | there are some things to keep in mind: 287 | 288 | - To have different cpus call different functions, use OPT_ON instead of 289 | OPT_SPECIFY_HANDLER, and use the m68k_set_xxx_callback() functions to set 290 | your callback handlers on a per-cpu basis. 291 | 292 | - Be sure to call set_cpu_type() for each CPU you use. 293 | 294 | - Use m68k_set_context() and m68k_get_context() to switch to another CPU. 295 | 296 | 297 | 298 | LOAD AND SAVE CPU CONTEXTS FROM DISK: 299 | ------------------------------------ 300 | You can use them68k_load_context() and m68k_save_context() functions to load 301 | and save the CPU state to disk. 302 | 303 | 304 | 305 | GET/SET INFORMATION FROM THE CPU: 306 | -------------------------------- 307 | You can use m68k_get_reg() and m68k_set_reg() to gain access to the internals 308 | of the CPU. 309 | 310 | 311 | 312 | EXAMPLE: 313 | ------- 314 | 315 | I have included a file example.zip that contains a full example. 316 | -------------------------------------------------------------------------------- /macos_app.py: -------------------------------------------------------------------------------- 1 | """ 2 | macOS app bundle script 3 | 4 | Usage: 5 | python macos_app.py py2app 6 | 7 | On some systems you also need to fix @rpaths for PySide dylibs: 8 | 9 | $ otool -L dist/kiwi.app/Contents/Resources/lib/python2.7/lib-dynload/PySide/QtCore.so 10 | $ install_name_tool -change @rpath/libpyside-python2.7.1.2.dylib @executable_path/../Frameworks/libpyside-python2.7.1.2.dylib dist/kiwi.app/Contents/Resources/lib/python2.7/lib-dynload/PySide/QtCore.so 11 | $ install_name_tool -change @rpath/libshiboken-python2.7.1.2.dylib @executable_path/../Frameworks/libshiboken-python2.7.1.2.dylib dist/kiwi.app/Contents/Resources/lib/python2.7/lib-dynload/PySide/QtCore.so 12 | $ install_name_tool -change @rpath/libshiboken-python2.7.1.2.dylib @executable_path/../Frameworks/libshiboken-python2.7.1.2.dylib dist/kiwi.app/Contents/Resources/lib/python2.7/lib-dynload/PySide/QtGui.so 13 | $ install_name_tool -change @rpath/libpyside-python2.7.1.2.dylib @executable_path/../Frameworks/libpyside-python2.7.1.2.dylib dist/kiwi.app/Contents/Resources/lib/python2.7/lib-dynload/PySide/QtGui.so 14 | 15 | """ 16 | 17 | from setuptools import setup 18 | 19 | APP = ['kiwi.py'] 20 | DATA_FILES = ['megadrive.so', 'images/pad.png', 'images/pad2.png', 'images/pad_big.png'] 21 | 22 | OPTIONS = { 23 | 'argv_emulation': True, 24 | 'includes': ['PySide.QtCore', 'PySide.QtGui'], 25 | } 26 | 27 | setup( 28 | app=APP, 29 | data_files=DATA_FILES, 30 | options={'py2app': OPTIONS}, 31 | setup_requires=['py2app'], 32 | ) 33 | -------------------------------------------------------------------------------- /megadrive.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "m68k/m68k.h" 4 | #include "z80.h" 5 | #include "VDP.h" 6 | #include "input.h" 7 | 8 | /* 9 | * Megadrive memory map as well as main execution loop. 10 | */ 11 | 12 | unsigned char ROM[0x400000]; 13 | unsigned char RAM[0x10000]; 14 | unsigned char ZRAM[0x2000]; 15 | 16 | const int MCLOCK_NTSC = 53693175; 17 | const int MCYCLES_PER_LINE = 3420; 18 | 19 | int lines_per_frame = 262; /* NTSC: 262, PAL: 313 */ 20 | 21 | int cycle_counter = 0; 22 | 23 | void set_rom(unsigned char *buffer, size_t size) 24 | { 25 | memset(ROM, 0, 0x400000); 26 | memset(RAM, 0, 0x10000); 27 | memset(ZRAM, 0, 0x2000); 28 | 29 | memcpy(ROM, buffer, size); 30 | } 31 | 32 | unsigned int read_memory(unsigned int address) 33 | { 34 | unsigned int range = (address & 0xff0000) >> 16; 35 | 36 | if (range <= 0x3f) 37 | { 38 | /* ROM */ 39 | return ROM[address]; 40 | } 41 | else if (range == 0xa0) 42 | { 43 | /* Z80 space */ 44 | if (address >= 0xa00000 && address < 0xa04000) 45 | { 46 | return ZRAM[address & 0x1fff]; 47 | } 48 | return 0; 49 | } 50 | else if (range == 0xa1) 51 | { 52 | /* I/O and registers */ 53 | if (address >= 0xa10000 && address < 0xa10020) 54 | { 55 | return io_read_memory(address & 0x1f); 56 | } 57 | else if (address >= 0xa11100 && address < 0xa11300) 58 | { 59 | return z80_ctrl_read(address & 0xffff); 60 | } 61 | return 0; 62 | } 63 | else if (range >= 0xc0 && range <= 0xdf) 64 | { 65 | /* VDP */ 66 | return vdp_read(address); 67 | } 68 | else if (range >= 0xe0 && range <= 0xff) 69 | { 70 | /* RAM */ 71 | return RAM[address & 0xffff]; 72 | // RAM 73 | } 74 | printf("read(%x)\n", address); 75 | return 0; 76 | } 77 | 78 | void write_memory(unsigned int address, unsigned int value) 79 | { 80 | unsigned int range = (address & 0xff0000) >> 16; 81 | 82 | if (range <= 0x3f) 83 | { 84 | /* ROM */ 85 | ROM[address] = value; 86 | return; 87 | } 88 | else if (range == 0xa0) 89 | { 90 | /* Z80 space */ 91 | if (address >= 0xa00000 && address < 0xa04000) 92 | { 93 | ZRAM[address & 0x1fff] = value; 94 | } 95 | return; 96 | } 97 | else if (range == 0xa1) 98 | { 99 | /* I/O and registers */ 100 | if (address >= 0xa10000 && address < 0xa10020) 101 | { 102 | io_write_memory(address & 0x1f, value); 103 | return; 104 | } 105 | else if (address >= 0xa11100 && address < 0xa11300) 106 | { 107 | z80_ctrl_write(address & 0xffff, value); 108 | return; 109 | } 110 | return; 111 | } 112 | else if (range >= 0xc0 && range <= 0xdf) 113 | { 114 | /* VDP */ 115 | return; 116 | } 117 | else if (range >= 0xe0 && range <= 0xff) 118 | { 119 | /* RAM */ 120 | RAM[address & 0xffff] = value; 121 | return; 122 | } 123 | printf("write(%x, %x)\n", address, value); 124 | return; 125 | } 126 | 127 | unsigned int m68k_read_memory_8(unsigned int address) 128 | { 129 | return read_memory(address); 130 | } 131 | unsigned int m68k_read_memory_16(unsigned int address) 132 | { 133 | 134 | unsigned int range = (address & 0xff0000) >> 16; 135 | 136 | if (range >= 0xc0 && range <= 0xdf) 137 | { 138 | return vdp_read(address); 139 | } 140 | else 141 | { 142 | unsigned int word = read_memory(address)<<8 | read_memory(address+1); 143 | return word; 144 | } 145 | } 146 | unsigned int m68k_read_memory_32(unsigned int address) 147 | { 148 | unsigned int longword = read_memory(address) << 24 | 149 | read_memory(address+1) << 16 | 150 | read_memory(address+2) << 8 | 151 | read_memory(address+3); 152 | return longword; 153 | } 154 | void m68k_write_memory_8(unsigned int address, unsigned int value) 155 | { 156 | write_memory(address, value); 157 | 158 | return; 159 | } 160 | void m68k_write_memory_16(unsigned int address, unsigned int value) 161 | { 162 | unsigned int range = (address & 0xff0000) >> 16; 163 | 164 | if (range >= 0xc0 && range <= 0xdf) 165 | { 166 | vdp_write(address, value); 167 | } 168 | else 169 | { 170 | write_memory(address, (value>>8)&0xff); 171 | write_memory(address+1, (value)&0xff); 172 | } 173 | } 174 | void m68k_write_memory_32(unsigned int address, unsigned int value) 175 | { 176 | m68k_write_memory_16(address, (value>>16)&0xffff); 177 | m68k_write_memory_16(address+2, (value)&0xffff); 178 | 179 | return; 180 | } 181 | 182 | /* 183 | * The Megadrive frame, called every 1/60th second 184 | * (or 1/50th in PAL mode) 185 | */ 186 | void frame() 187 | { 188 | extern unsigned char vdp_reg[0x20], *screen; 189 | extern unsigned int vdp_status; 190 | extern int screen_width, screen_height; 191 | int hint_counter = vdp_reg[10]; 192 | int line; 193 | 194 | cycle_counter = 0; 195 | 196 | screen_width = (vdp_reg[12] & 0x01) ? 320 : 256; 197 | screen_height = (vdp_reg[1] & 0x08) ? 240 : 224; 198 | 199 | vdp_clear_vblank(); 200 | memset(screen, 0, 320*240*3); /* clear the screen before rendering */ 201 | 202 | for (line=0; line < screen_height; line++) 203 | { 204 | m68k_execute(2560+120); 205 | 206 | if (--hint_counter < 0) 207 | { 208 | hint_counter = vdp_reg[10]; 209 | if (vdp_reg[0] & 0x10) 210 | { 211 | m68k_set_irq(4); /* HInt */ 212 | //m68k_execute(7000); 213 | } 214 | } 215 | 216 | vdp_set_hblank(); 217 | m68k_execute(64+313+259); /* HBlank */ 218 | vdp_clear_hblank(); 219 | 220 | m68k_execute(104); 221 | 222 | vdp_render_line(line); /* render line */ 223 | } 224 | 225 | vdp_set_vblank(); 226 | 227 | m68k_execute(588); 228 | 229 | vdp_status |= 0x80; 230 | 231 | m68k_execute(200); 232 | 233 | if (vdp_reg[1] & 0x20) 234 | { 235 | m68k_set_irq(6); /* HInt */ 236 | } 237 | 238 | m68k_execute(3420-788); 239 | line++; 240 | 241 | for (;line < lines_per_frame; line++) 242 | { 243 | m68k_execute(3420); /**/ 244 | } 245 | 246 | } 247 | -------------------------------------------------------------------------------- /scale.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | extern unsigned int *screen, *scaled_screen; 6 | 7 | const int width = 320, height = 240; 8 | int hqx_init = 0; 9 | 10 | /* 11 | * The nearest-neightbor naive scaling algorithm. 12 | * Pixels are magnified without any additional filtering. 13 | */ 14 | void scale_nearest(unsigned int *dest, unsigned int *src, int scale) 15 | { 16 | for (int y=0; y 3 4 38 | * D 39 | * 40 | * 1=P; 2=P; 3=P; 4=P; 41 | * if C==A and C!=D and A!=B => 1=A 42 | * if A==B and A!=C and B!=D => 2=B 43 | * if D==C and D!=B and C!=A => 3=C 44 | * if B==D and B!=A and D!=C => 4=D 45 | * 46 | * 3x: 47 | * 48 | * A B C 1 2 3 49 | * D E F -> 4 5 6 50 | * G H I 7 8 9 51 | * 52 | * 1=E; 2=E; 3=E; 4=E; 5=E; 6=E; 7=E; 8=E; 9=E; 53 | * if D==B and D!=H and B!=F => 1=D 54 | * if (D==B and D!=H and B!=F and E!=C) or (B==F and B!=D and F!=H and E!=A) => 2=B 55 | * if B==F and B!=D and F!=H => 3=F 56 | * if (H==D and H!=F and D!=B and E!=A) or (D==B and D!=H and B!=F and E!=G) => 4=D 57 | * 5=E 58 | * if (B==F and B!=D and F!=H and E!=I) or (F==H and F!=B and H!=D and E!=C) => 6=F 59 | * if H==D and H!=F and D!=B => 7=D 60 | * if (F==H and F!=B and H!=D and E!=G) or (H==D and H!=F and D!=B and E!=I) => 8=H 61 | * if F==H and F!=B and H!=D => 9=F 62 | * 63 | * 4x is 2x applied twice 64 | */ 65 | #define get_pixel(dx, dy) \ 66 | (((x)+(dx) < 0 || (x)+(dx) >= w || (y)+(dy) < 0 || (y)+(dy) >= h) ? 0 : \ 67 | src[(y+dy)*w+x+dx]) 68 | 69 | #define epx_pixel(i) get_pixel(row[i].x, row[i].y) 70 | 71 | struct point { 72 | int x; 73 | int y; 74 | } epx_table_2x[4][4] = { 75 | {{0, -1}, {-1, 0}, {0, 1}, {1, 0}}, 76 | {{1, 0}, {0, -1}, {-1, 0}, {0, 1}}, 77 | {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}, 78 | {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}, 79 | }; 80 | 81 | struct point epx_table_3x[4][2] = { 82 | {{1, -1}, {-1, -1}}, 83 | {{-1, -1}, {-1, 1}}, 84 | {{1, 1}, {1, -1}}, 85 | {{-1, 1}, {1, 1}} 86 | }; 87 | 88 | int epx_rotation[4] = {1, 3, 0, 2}; 89 | int epx_corners_3x[9] = {0, -1, 1, -1, -1, -1, 2, -1, 3}; 90 | 91 | void scale_epx_hw(unsigned int *dest, unsigned int *src, int scale, int h, int w) 92 | { 93 | if (scale == 3) 94 | { 95 | for (int y=0; y>1]; 119 | struct point *row; 120 | row = epx_table_2x[k>>1]; 121 | if (epx_pixel(1) == epx_pixel(0) && 122 | epx_pixel(1) != epx_pixel(2) && 123 | epx_pixel(0) != epx_pixel(3) && 124 | get_pixel(0, 0) != get_pixel(row_3x[0].x, row_3x[0].y)) 125 | { 126 | pixel = epx_pixel(1); 127 | } 128 | 129 | row = epx_table_2x[epx_rotation[k]]; 130 | if (epx_pixel(1) == epx_pixel(0) && 131 | epx_pixel(1) != epx_pixel(2) && 132 | epx_pixel(0) != epx_pixel(3) && 133 | get_pixel(0, 0) != get_pixel(row_3x[1].x, row_3x[1].y)) 134 | { 135 | pixel = epx_pixel(0); 136 | } 137 | } 138 | dest[(y*scale+i)*w*scale+x*scale+j] = pixel; 139 | } 140 | } 141 | } 142 | } 143 | } 144 | else if (scale == 2) 145 | { 146 | for (int y=0; y 015376: move.l d1, (a1)+ 6 | 015378: dbra d7, 15376 7 | 01537c: rts 8 | 01537e: rts 9 | 10 | d0=0000000c d1=00200000 d2=00000000 d3=00000000 11 | d4=00000200 d5=0000ffff d6=00000000 d7=000000b7 12 | a0=ffffc600 a1=fffff720 a2=0005a4d3 a3=fffff284 13 | a4=fffff386 a5=ffffdfaa a6=ffffc7e0 a7=00fffdf8 14 | pc=00015376 sr=00002000 sp=00fffdf8 usp=00000000 15 | -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-1x-epx.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-1x-epx.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-1x-hqx.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-1x-hqx.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-1x-none.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-1x-none.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-2x-epx.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-2x-epx.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-2x-hqx.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-2x-hqx.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-2x-none.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-2x-none.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-3x-epx.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-3x-epx.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-3x-hqx.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-3x-hqx.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-3x-none.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-3x-none.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-4x-epx.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-4x-epx.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-4x-hqx.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-4x-hqx.bmp -------------------------------------------------------------------------------- /tests/ristar/ristar-screenshot-4x-none.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drx/kiwi/85ec7e005f9a88175b5f3236ff0d4d7268e4fefd/tests/ristar/ristar-screenshot-4x-none.bmp -------------------------------------------------------------------------------- /tests/ristar/test_ristar.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import os 3 | import tempfile 4 | 5 | from PySide.QtCore import Qt 6 | from PySide.QtGui import QAction, QFileDialog 7 | 8 | 9 | def get_bmp_sha1(filename): 10 | """ 11 | Get the sha1 hash of the pixel data in a bmp file. 12 | 13 | This is used to check if two bitmaps are identical. 14 | """ 15 | bitmap_data_offset = 0x36 # skip the BMP header (it's not standard across all platforms) 16 | 17 | with open(filename, 'rb') as f: 18 | contents = f.read() 19 | bitmap_data = contents[bitmap_data_offset:] 20 | bitmap_hash = hashlib.sha1(bitmap_data) 21 | 22 | return bitmap_hash.hexdigest() 23 | 24 | 25 | def run_frames(display, n): 26 | """ 27 | Emulate n frames. 28 | """ 29 | for i in range(n): 30 | display.frame() 31 | 32 | 33 | def test_ristar(qtbot, mock): 34 | """ 35 | Run Ristar. 36 | 37 | - Run for 7000 frames. 38 | - Select every zoom/render filter combo, run for 5 frames 39 | and check if the video output (screenshot) is correct. 40 | - Enable debug mode, press Start, check if the debug text is correct. 41 | - Disable debug mode, take a screenshot and check if it was created. 42 | 43 | """ 44 | from kiwi import MainWindow, render_filters 45 | window = MainWindow() 46 | qtbot.addWidget(window) 47 | 48 | display = window.display 49 | 50 | display.set_zoom_level(QAction('1x', window)) 51 | mock.patch.object(QFileDialog, 'getOpenFileName', return_value=('./tests/ristar/Ristar (UE) [!].zip', '*.zip')) 52 | display.open_file() 53 | 54 | run_frames(display, 7000) 55 | 56 | assert display.frames == 7000 57 | 58 | for scale_factor in ('1x', '2x', '3x', '4x'): 59 | for render_filter in render_filters: 60 | display.set_zoom_level(QAction(scale_factor, window)) 61 | display.set_render_filter(QAction(render_filter, window)) 62 | 63 | run_frames(display, 5) 64 | 65 | filename_suffix = '-{}-{}.bmp'.format(scale_factor, render_filter.lower()) 66 | 67 | _, filename = tempfile.mkstemp(suffix=filename_suffix) 68 | display.save_screenshot(filename) 69 | 70 | assert get_bmp_sha1(filename) == get_bmp_sha1('./tests/ristar/ristar-screenshot'+filename_suffix) 71 | 72 | display.set_zoom_level(QAction('1x', window)) 73 | display.toggle_debug() 74 | 75 | qtbot.keyPress(window, Qt.Key_Q) 76 | run_frames(display, 50) 77 | qtbot.keyRelease(window, Qt.Key_Q) 78 | 79 | run_frames(display, 500) 80 | 81 | debug_text = display.debug_label.text() + '\n' 82 | _, _, debug_text = debug_text.partition('\n\n') 83 | 84 | with open('./tests/ristar/debug-output.txt') as f: 85 | expected_debug_text = f.read() 86 | 87 | assert debug_text == expected_debug_text 88 | 89 | display.toggle_debug() 90 | 91 | run_frames(display, 50) 92 | 93 | filename = display.save_screenshot() 94 | 95 | assert os.path.isfile(filename) 96 | assert 'screenshot' in filename 97 | assert filename.endswith('.png') 98 | -------------------------------------------------------------------------------- /tests/test_gui.py: -------------------------------------------------------------------------------- 1 | def test_controllers(qtbot): 2 | from kiwi import MainWindow 3 | window = MainWindow() 4 | qtbot.addWidget(window) 5 | 6 | window.display.show_controllers() 7 | 8 | assert 'Start' in window.display.controllers_window.text() 9 | -------------------------------------------------------------------------------- /vdp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "m68k/m68k.h" 3 | #include "VDP.h" 4 | 5 | /* 6 | * Megadrive VDP emulation 7 | */ 8 | 9 | unsigned char VRAM[0x10000]; 10 | unsigned short CRAM[0x40]; 11 | unsigned short VSRAM[0x40]; 12 | unsigned char vdp_reg[0x20]; 13 | 14 | unsigned char *screen, *scaled_screen; 15 | 16 | int control_code = 0; 17 | unsigned int control_address = 0; 18 | int control_pending = 0; 19 | unsigned int vdp_status = 0x3400; 20 | 21 | int screen_width; 22 | int screen_height; 23 | 24 | int dma_length; 25 | unsigned int dma_source; 26 | int dma_fill = 0; 27 | 28 | /* Set a pixel on the screen using the Color RAM */ 29 | #define set_pixel(scr, x, y, index) \ 30 | do {\ 31 | int pixel = ((240-screen_height)/2+(y))*320+(x)+(320-screen_width)/2; \ 32 | scr[pixel*4+0] = (CRAM[index]>>4)&0xe0; \ 33 | scr[pixel*4+1] = (CRAM[index])&0xe0; \ 34 | scr[pixel*4+2] = (CRAM[index]<<4)&0xe0; \ 35 | } while(0); 36 | 37 | /* 38 | * Draw a single pixel of a cell 39 | */ 40 | void draw_cell_pixel(unsigned int cell, int cell_x, int cell_y, int x, int y) 41 | { 42 | unsigned char *pattern = &VRAM[0x20*(cell&0x7ff)]; 43 | 44 | int pattern_index = 0; 45 | if (cell & 0x1000) /* v flip */ 46 | pattern_index = (7-(cell_y&7))<<2; 47 | else 48 | pattern_index = (cell_y&7)<<2; 49 | 50 | if (cell & 0x800) // h flip 51 | pattern_index += (7-(cell_x&7))>>1; 52 | else 53 | pattern_index += (cell_x&7)>>1; 54 | 55 | unsigned char color_index = pattern[pattern_index]; 56 | if ((cell_x&1)^((cell>>11)&1)) color_index &= 0xf; 57 | else color_index >>= 4; 58 | 59 | if (color_index) 60 | { 61 | color_index += (cell & 0x6000)>>9; 62 | set_pixel(screen, x, y, color_index); 63 | } 64 | } 65 | 66 | /* 67 | * Render the scroll layers (plane A and B) 68 | */ 69 | void vdp_render_bg(int line, int priority) 70 | { 71 | int h_cells = 32, v_cells = 32; 72 | 73 | switch (vdp_reg[16] & 3) 74 | { 75 | case 0: h_cells = 32; break; 76 | case 1: h_cells = 64; break; 77 | case 3: h_cells = 128; break; 78 | } 79 | switch ((vdp_reg[16]>>4) & 3) 80 | { 81 | case 0: v_cells = 32; break; 82 | case 1: v_cells = 64; break; 83 | case 3: v_cells = 128; break; 84 | } 85 | 86 | int hscroll_type = vdp_reg[11]&3; 87 | unsigned char *hscroll_table = &VRAM[vdp_reg[13]<<10]; 88 | unsigned int hscroll_mask; 89 | switch (hscroll_type) 90 | { 91 | case 0x00: hscroll_mask = 0x0000; break; 92 | case 0x01: hscroll_mask = 0x0007; break; 93 | case 0x02: hscroll_mask = 0xfff8; break; 94 | case 0x03: hscroll_mask = 0xffff; break; 95 | } 96 | 97 | unsigned short vscroll_mask; 98 | if (vdp_reg[11]&4) 99 | vscroll_mask = 0xfff0; 100 | else 101 | vscroll_mask = 0x0000; 102 | 103 | for (int scroll_i = 0; scroll_i<2; scroll_i++) 104 | { 105 | unsigned char *scroll; 106 | if (scroll_i == 0) 107 | scroll = &VRAM[vdp_reg[4]<<13]; 108 | else 109 | scroll = &VRAM[vdp_reg[2]<<10]; 110 | 111 | short hscroll = (hscroll_table[((line & hscroll_mask))*4+(scroll_i^1)*2]<<8) 112 | | hscroll_table[((line & hscroll_mask))*4+(scroll_i^1)*2+1]; 113 | for (int column = 0; column < screen_width; column++) 114 | { 115 | short vscroll = VSRAM[(column & vscroll_mask)/4+(scroll_i^1)] & 0x3ff; 116 | int e_line = (line+vscroll)&(v_cells*8-1); 117 | int cell_line = e_line >> 3; 118 | int e_column = (column-hscroll)&(h_cells*8-1); 119 | int cell_column = e_column >> 3; 120 | unsigned int cell = (scroll[(cell_line*h_cells+cell_column)*2]<<8) 121 | | scroll[(cell_line*h_cells+cell_column)*2+1]; 122 | 123 | if (((cell & 0x8000) && priority) || ((cell & 0x8000) == 0 && priority == 0)) 124 | draw_cell_pixel(cell, e_column, e_line, column, line); 125 | } 126 | } 127 | } 128 | 129 | /* 130 | * Render part of a sprite on a given line. 131 | */ 132 | void vdp_render_sprite(int sprite_index, int line) 133 | { 134 | unsigned char *sprite = &VRAM[(vdp_reg[5] << 9) + sprite_index*8]; 135 | 136 | unsigned short y_pos = ((sprite[0]<<8)|sprite[1])&0x3ff; 137 | int h_size = ((sprite[2]>>2)&0x3) + 1; 138 | int v_size = (sprite[2]&0x3) + 1; 139 | unsigned int cell = (sprite[4]<<8)|sprite[5]; 140 | unsigned short x_pos = ((sprite[6]<<8)|sprite[7])&0x3ff; 141 | 142 | int y = (128-y_pos+line)&7; 143 | int cell_y = (128-y_pos+line)>>3; 144 | 145 | for (int cell_x=0; cell_x= 0 && e_x < screen_width) 163 | { 164 | draw_cell_pixel(e_cell, x, y, e_x, line); 165 | } 166 | } 167 | } 168 | 169 | } 170 | 171 | /* 172 | * Render the sprite layer. 173 | */ 174 | void vdp_render_sprites(int line, int priority) 175 | { 176 | unsigned char *sprite_table = &VRAM[vdp_reg[5] << 9]; 177 | 178 | int sprite_queue[80]; 179 | int i = 0; 180 | int cur_sprite = 0; 181 | while (1) 182 | { 183 | unsigned char *sprite = &VRAM[(vdp_reg[5] << 9) + cur_sprite*8]; 184 | unsigned short y_pos = (sprite[0]<<8)|sprite[1]; 185 | int v_size = (sprite[2]&0x3) + 1; 186 | unsigned int cell = (sprite[4]<<8)|sprite[5]; 187 | 188 | int y_min = y_pos-128; 189 | int y_max = (v_size-1)*8 + 7 + y_min; 190 | 191 | if (line >= y_min && line <= y_max) 192 | { 193 | if ((cell >> 15) == priority) 194 | sprite_queue[i++] = cur_sprite; 195 | } 196 | 197 | cur_sprite = sprite_table[cur_sprite*8+3]; 198 | if (!cur_sprite) 199 | break; 200 | 201 | if (i >= 80) 202 | break; 203 | } 204 | while (i > 0) 205 | { 206 | vdp_render_sprite(sprite_queue[--i], line); 207 | } 208 | } 209 | 210 | /* 211 | * Render a single line. 212 | */ 213 | void vdp_render_line(int line) 214 | { 215 | /* Fill the screen with the backdrop color set in register 7 */ 216 | for (int i=0; i> 8) & 0xff; 251 | VRAM[control_address+1] = (value) & 0xff; 252 | } 253 | else if (type == T_CRAM) /* CRAM write */ 254 | { 255 | CRAM[(control_address & 0x7f) >> 1] = value; 256 | } 257 | else if (type == T_VSRAM) /* VSRAM write */ 258 | { 259 | VSRAM[(control_address & 0x7f) >> 1] = value; 260 | } 261 | } 262 | 263 | void vdp_data_port_write(unsigned int value) 264 | { 265 | if (control_code & 1) /* check if write is set */ 266 | { 267 | enum ram_type type; 268 | if ((control_code & 0xe) == 0) /* VRAM write */ 269 | { 270 | type = T_VRAM; 271 | } 272 | else if ((control_code & 0xe) == 2) /* CRAM write */ 273 | { 274 | type = T_CRAM; 275 | } 276 | else if ((control_code & 0xe) == 4) /* VSRAM write */ 277 | { 278 | type = T_VSRAM; 279 | } 280 | vdp_data_write(value, type, 0); 281 | } 282 | control_address = (control_address + vdp_reg[15]) & 0xffff; 283 | control_pending = 0; 284 | 285 | /* if a DMA is scheduled, do it */ 286 | if (dma_fill) 287 | { 288 | dma_fill = 0; 289 | dma_length = vdp_reg[19] | (vdp_reg[20] << 8); 290 | while (dma_length--) 291 | { 292 | VRAM[control_address] = value >> 8; 293 | control_address += vdp_reg[15]; 294 | control_address &= 0xffff; 295 | } 296 | } 297 | } 298 | 299 | void vdp_set_reg(int reg, unsigned char value) 300 | { 301 | if (vdp_reg[1] & 4 || reg <= 10) 302 | vdp_reg[reg] = value; 303 | 304 | control_code = 0; 305 | } 306 | 307 | unsigned int vdp_get_reg(int reg) 308 | { 309 | return vdp_reg[reg]; 310 | } 311 | 312 | void vdp_control_write(unsigned int value) 313 | { 314 | if (!control_pending) 315 | { 316 | if ((value & 0xc000) == 0x8000) 317 | { 318 | int reg = (value >> 8)&0x1f; 319 | unsigned char reg_value = value & 0xff; 320 | 321 | vdp_set_reg(reg, reg_value); 322 | } 323 | else 324 | { 325 | control_code = (control_code & 0x3c) | ((value >> 14) & 3); 326 | control_address = (control_address & 0xc000) | (value & 0x3fff); 327 | control_pending = 1; 328 | } 329 | } 330 | else 331 | { 332 | control_code = (control_code & 3) | ((value >> 2) & 0x3c); 333 | control_address = (control_address & 0x3fff) | ((value & 3) << 14); 334 | control_pending = 0; 335 | 336 | if ((control_code & 0x20) && (vdp_reg[1] & 0x10)) 337 | { 338 | if ((vdp_reg[23] >> 6) == 2 && (control_code & 7) == 1) 339 | { 340 | /* DMA fill */ 341 | dma_fill = 1; 342 | } 343 | else if ((vdp_reg[23] >> 6) == 3) 344 | { 345 | /* DMA copy */ 346 | printf("DMA copy\n"); 347 | } 348 | else 349 | { 350 | /* DMA 68k -> VDP */ 351 | dma_length = vdp_reg[19] | (vdp_reg[20] << 8); 352 | dma_source = (vdp_reg[21]<<1) | (vdp_reg[22]<<9) | (vdp_reg[23]<<17); 353 | 354 | unsigned int word; 355 | enum ram_type type; 356 | if ((control_code & 0x7) == 1) 357 | { 358 | type = T_VRAM; 359 | } 360 | else if ((control_code & 0x7) == 3) 361 | { 362 | type = T_CRAM; 363 | } 364 | else if ((control_code & 0x7) == 5) 365 | { 366 | type = T_VSRAM; 367 | } 368 | 369 | while (dma_length--) 370 | { 371 | word = m68k_read_memory_16(dma_source); 372 | dma_source += 2; 373 | vdp_data_write(word, type, 1); 374 | control_address += vdp_reg[15]; 375 | control_address &= 0xffff; 376 | } 377 | } 378 | } 379 | } 380 | } 381 | 382 | void vdp_write(unsigned int address, unsigned int value) 383 | { 384 | address &= 0x1f; 385 | 386 | if (address < 0x04) 387 | { 388 | vdp_data_port_write(value); 389 | } 390 | else if (address >= 0x04 && address < 0x08) 391 | { 392 | vdp_control_write(value); 393 | } 394 | else 395 | { 396 | printf("vdp_write(%x, %x)\n", address, value); 397 | } 398 | } 399 | 400 | unsigned int vdp_read(unsigned int address) 401 | { 402 | address &= 0x1f; 403 | 404 | if (0 && address < 0x04) 405 | { 406 | } 407 | else if (address >= 0x04 && address < 0x08) 408 | { 409 | /* VDP status */ 410 | return vdp_status; 411 | } 412 | else if (address >= 0x08 && address < 0x10) 413 | { 414 | /* V/H counter */ 415 | extern int cycle_counter; 416 | extern int lines_per_frame; 417 | extern int MCYCLES_PER_LINE; 418 | 419 | int vcounter, hcounter; 420 | 421 | vcounter = cycle_counter/MCYCLES_PER_LINE-1; 422 | if (vcounter > (vdp_reg[1] & 0x08 ? 262 : 234)) 423 | { 424 | vcounter -= lines_per_frame; 425 | } 426 | 427 | if (vdp_reg[12] & 0x01) 428 | { 429 | hcounter = 0; 430 | } 431 | else 432 | { 433 | hcounter = ((cycle_counter+10)%MCYCLES_PER_LINE)/20; 434 | if (hcounter >= 12) 435 | hcounter += 0x56; 436 | hcounter += 0x85; 437 | } 438 | 439 | if (address & 1) 440 | return hcounter & 0xff; 441 | else 442 | return vcounter & 0xff; 443 | } 444 | else 445 | { 446 | printf("vdp_read(%x)\n", address); 447 | } 448 | return 0; 449 | } 450 | 451 | unsigned int vdp_get_status() 452 | { 453 | return vdp_status; 454 | } 455 | 456 | unsigned short vdp_get_cram(int index) 457 | { 458 | return CRAM[index & 0x3f]; 459 | } 460 | 461 | void vdp_set_hblank() 462 | { 463 | vdp_status |= 4; 464 | } 465 | void vdp_clear_hblank() 466 | { 467 | vdp_status &= ~4; 468 | } 469 | void vdp_set_vblank() 470 | { 471 | vdp_status |= 8; 472 | } 473 | void vdp_clear_vblank() 474 | { 475 | vdp_status &= ~8; 476 | } 477 | -------------------------------------------------------------------------------- /z80.c: -------------------------------------------------------------------------------- 1 | #include "z80.h" 2 | 3 | int bus_ack = 0; 4 | int reset = 0; 5 | 6 | void z80_ctrl_write(unsigned int address, unsigned int value) 7 | { 8 | if (address == 0x1100) // BUSREQ 9 | { 10 | if (value) 11 | { 12 | bus_ack = 1; 13 | } 14 | else 15 | { 16 | bus_ack = 0; 17 | } 18 | } 19 | else if (address == 0x1200) // RESET 20 | { 21 | if (value) 22 | { 23 | reset = 1; 24 | } 25 | else 26 | { 27 | reset = 0; 28 | } 29 | } 30 | } 31 | 32 | unsigned int z80_ctrl_read(unsigned int address) 33 | { 34 | if (address == 0x1100) 35 | { 36 | return !(reset && bus_ack); 37 | } 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /z80.h: -------------------------------------------------------------------------------- 1 | void z80_ctrl_write(unsigned int address, unsigned int value); 2 | unsigned int z80_ctrl_read(unsigned int address); 3 | --------------------------------------------------------------------------------