]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
Keyboard MCU emulation
authorToni Wilen <twilen@winuae.net>
Sat, 4 Jan 2025 11:07:59 +0000 (13:07 +0200)
committerToni Wilen <twilen@winuae.net>
Sat, 4 Jan 2025 11:07:59 +0000 (13:07 +0200)
33 files changed:
cfgfile.cpp
cia.cpp
devices.cpp
expansion.cpp
include/gui.h
include/keyboard_mcu.h [new file with mode: 0644]
include/mos6502.h [new file with mode: 0644]
include/options.h
include/savestate.h
inputdevice.cpp
kbmcu/8048/co8048.cpp [new file with mode: 0644]
kbmcu/8048/co8048.h [new file with mode: 0644]
kbmcu/keyboard_mcu_6500_1.cpp [new file with mode: 0644]
kbmcu/keyboard_mcu_6805.cpp [new file with mode: 0644]
kbmcu/keyboard_mcu_d8039hlc .cpp [new file with mode: 0644]
kbmcu/m6805/m68_internal.h [new file with mode: 0644]
kbmcu/m6805/m68_ops.cpp [new file with mode: 0644]
kbmcu/m6805/m68_optab_hc05.h [new file with mode: 0644]
kbmcu/m6805/m68emu.cpp [new file with mode: 0644]
kbmcu/m6805/m68emu.h [new file with mode: 0644]
keybuf.cpp
mos6502.cpp [new file with mode: 0644]
od-win32/keyboard_win32.cpp
od-win32/resources/resource.h
od-win32/resources/winuae.rc
od-win32/win32.cpp
od-win32/win32gfx.cpp
od-win32/win32gui.cpp
od-win32/winuae_msvc15/winuae_msvc.vcxproj
od-win32/winuae_msvc15/winuae_msvc.vcxproj.filters
rommgr.cpp
savestate.cpp
statusline.cpp

index 1098a5ca6a4debed2f540ecd93654fc8b5daf7ea..9e0813b5043b7f74b6427c88be2a9f644c9692ac 100644 (file)
@@ -254,6 +254,18 @@ static const TCHAR *eclocksync[] = { _T("default"), _T("68000"), _T("Gayle"), _T
 static const TCHAR *agnusmodel[] = { _T("default"), _T("velvet"), _T("a1000"), _T("ocs"), _T("ecs"), _T("aga"), 0 };
 static const TCHAR *agnussize[] = { _T("default"), _T("512k"), _T("1m"), _T("2m"), 0 };
 static const TCHAR *denisemodel[] = { _T("default"), _T("velvet"), _T("a1000_noehb"), _T("a1000"), _T("ocs"), _T("ecs"), _T("aga"), 0 };
+static const TCHAR *kbtype[] = {
+       _T("disconnected"),
+       _T("UAE"),
+       _T("a500_6570-036"),
+       _T("a600_6570-036"),
+       _T("a1000_6500-1"),
+       _T("a1000_6570-036"),
+       _T("a1200_6805"),
+       _T("a2000_8039"),
+       _T("ax000_6570-036"),
+       NULL
+};
 
 struct hdcontrollerconfig
 {
@@ -2984,7 +2996,9 @@ void cfgfile_save_options (struct zfile *f, struct uae_prefs *p, int type)
        cfgfile_write_bool(f, _T("fm801_pci"), p->obs_sound_fm801);
 #endif
 
-       cfgfile_dwrite_bool(f, _T("keyboard_connected"), p->keyboard_connected);
+       cfgfile_dwrite_bool(f, _T("keyboard_connected"), p->keyboard_mode >= 0);
+       cfgfile_dwrite_bool(f, _T("keyboard_nkro"), p->keyboard_nkro);
+       cfgfile_dwrite_strarr(f, _T("keyboard_type"), kbtype, p->keyboard_mode + 1);
        cfgfile_write_str (f, _T("kbd_lang"), (p->keyboard_lang == KBD_LANG_DE ? _T("de")
                : p->keyboard_lang == KBD_LANG_DK ? _T("dk")
                : p->keyboard_lang == KBD_LANG_ES ? _T("es")
@@ -5868,6 +5882,14 @@ static int cfgfile_parse_hardware (struct uae_prefs *p, const TCHAR *option, TCH
                }
                return 1;
        }
+       if (cfgfile_yesno(option, value, _T("keyboard_connected"), &dummybool)) {
+               p->keyboard_mode = dummybool ? 0 : -1;
+               return 1;
+       }
+       if (cfgfile_strval(option, value, _T("keyboard_type"), &p->keyboard_mode, kbtype, 0)) {
+               p->keyboard_mode--;
+               return 1;
+       }
 
        if (cfgfile_yesno(option, value, _T("immediate_blits"), &p->immediate_blits)
                || cfgfile_yesno(option, value, _T("fpu_no_unimplemented"), &p->fpu_no_unimplemented)
@@ -5898,7 +5920,6 @@ static int cfgfile_parse_hardware (struct uae_prefs *p, const TCHAR *option, TCH
                || cfgfile_yesno(option, value, _T("gfxcard_dacswitch"), &p->rtg_dacswitch)
                || cfgfile_yesno(option, value, _T("gfxcard_multithread"), &p->rtg_multithread)
                || cfgfile_yesno(option, value, _T("synchronize_clock"), &p->tod_hack)
-               || cfgfile_yesno(option, value, _T("keyboard_connected"), &p->keyboard_connected)
                || cfgfile_coords(option, value, _T("lightpen_offset"), &p->lightpen_offset[0], &p->lightpen_offset[1])
                || cfgfile_yesno(option, value, _T("lightpen_crosshair"), &p->lightpen_crosshair)
 
@@ -5936,6 +5957,7 @@ static int cfgfile_parse_hardware (struct uae_prefs *p, const TCHAR *option, TCH
                || cfgfile_yesno(option, value, _T("harddrive_write_protect"), &p->harddrive_read_only)
                || cfgfile_yesno(option, value, _T("uae_hide_autoconfig"), &p->uae_hide_autoconfig)
                || cfgfile_yesno(option, value, _T("board_custom_order"), &p->autoconfig_custom_sort)
+               || cfgfile_yesno(option, value, _T("keyboard_nkro"), &p->keyboard_nkro)
                || cfgfile_yesno(option, value, _T("uaeserial"), &p->uaeserial))
                return 1;
 
@@ -8397,7 +8419,8 @@ void default_prefs (struct uae_prefs *p, bool reset, int type)
                inputdevice_joyport_config_store(p, _T("kbd1"), 1, -1, -1, 0, 0);
        }
        p->keyboard_lang = KBD_LANG_US;
-       p->keyboard_connected = true;
+       p->keyboard_mode = 0;
+       p->keyboard_nkro = true;
 
        p->produce_sound = 3;
        p->sound_stereo = SND_STEREO;
@@ -9507,7 +9530,7 @@ static int bip_casablanca(struct uae_prefs *p, int config, int compa, int romche
        p->immediate_blits = 0;
        p->produce_sound = 2;
        p->nr_floppies = 0;
-       p->keyboard_connected = false;
+       p->keyboard_mode = -1;
        p->floppyslots[0].dfxtype = DRV_NONE;
        p->floppyslots[1].dfxtype = DRV_NONE;
        p->floppyslots[2].dfxtype = DRV_PC_35_ONLY_80;
@@ -9532,7 +9555,7 @@ static int bip_draco(struct uae_prefs *p, int config, int compa, int romcheck)
        p->immediate_blits = 0;
        p->produce_sound = 2;
        p->nr_floppies = 0;
-       p->keyboard_connected = false;
+       p->keyboard_mode = -1;
        p->cpuboard_settings |= 0x10;
        p->floppyslots[0].dfxtype = DRV_NONE;
        p->floppyslots[1].dfxtype = DRV_NONE;
diff --git a/cia.cpp b/cia.cpp
index 4afc532480f77ee068bd8c8eb24eb42a90520d34..2576ae83752cb15a836c022880811f2028512b0c 100644 (file)
--- a/cia.cpp
+++ b/cia.cpp
@@ -47,6 +47,7 @@
 #include "scsi.h"
 #include "rtc.h"
 #include "devices.h"
+#include "keyboard_mcu.h"
 
 #define CIAA_DEBUG_R 0
 #define CIAA_DEBUG_W 0
@@ -969,7 +970,7 @@ static void sendrw(void)
 
 int resetwarning_do(int canreset)
 {
-       if (!currprefs.keyboard_connected)
+       if (currprefs.keyboard_mode < 0)
                return 0;
        if (resetwarning_phase || regs.halted > 0) {
                /* just force reset if second reset happens during resetwarning */
@@ -1025,13 +1026,15 @@ void CIA_hsync_prehandler (void)
 {
 }
 
-static void keyreq (void)
+void cia_keyreq(uae_u8 code)
 {
 #if KB_DEBUG
        write_log(_T("code=%02x (%02x)\n"), kbcode, (uae_u8)(~((kbcode >> 1) | (kbcode << 7))));
 #endif
-       cia[0].sdr = kbcode;
-       kblostsynccnt = 8 * maxvpos * 8; // 8 frames * 8 bits.
+       cia[0].sdr = code;
+       if (currprefs.keyboard_mode == 0) {
+               kblostsynccnt = 8 * maxvpos * 8; // 8 frames * 8 bits.
+       }
        CIA_sync_interrupt(0, ICR_SP);
 }
 
@@ -1151,6 +1154,9 @@ void keyboard_connected(bool connect)
 {
        if (connect) {
                write_log(_T("Keyboard connected\n"));
+               if (currprefs.keyboard_mode > 0) {
+                       keymcu_reset();
+               }
        } else {
                write_log(_T("Keyboard disconnected\n"));
        }
@@ -1159,9 +1165,53 @@ void keyboard_connected(bool connect)
        resetwarning_phase = 0;
 }
 
+static bool keymcu_execute(void)
+{
+       bool handshake = (cia[0].t[0].cr & 0x40) != 0 && (cia[0].sdr_buf & 0x80) == 0;
+
+#if 1
+       extern int blop;
+       if (blop & 1) {
+               handshake = true;
+       }
+#endif
+
+       bool cyclemode = false;
+       if (currprefs.keyboard_mode == KB_A500_6570 ||
+               currprefs.keyboard_mode == KB_A600_6570 ||
+               currprefs.keyboard_mode == KB_A1000_6570 ||
+               currprefs.keyboard_mode == KB_Ax000_6570)
+       {
+               cyclemode = keymcu_run(handshake);
+       }
+       if (currprefs.keyboard_mode == KB_A1200_6805) {
+               cyclemode = keymcu2_run(handshake);
+       }
+       if (currprefs.keyboard_mode == KB_A2000_8039) {
+               cyclemode = keymcu3_run(handshake);
+       }
+       return cyclemode;
+}
+
+static void keymcu_event(uae_u32 v)
+{
+       bool cyclemode = keymcu_execute();
+       if (cyclemode) {
+               // execute few times / scanline, does not need to be accurate
+               // because keyboard MCU has separate not that accurate clock crystal.
+               event2_newevent_x_remove(keymcu_event);
+               event2_newevent_xx(-1, 27 * CYCLE_UNIT, 0, keymcu_event);
+       }
+}
+
+static void keymcu_do(void)
+{
+       keymcu_event(0);
+}
+
 static void check_keyboard(void)
 {
-       if (currprefs.keyboard_connected) {
+       if (currprefs.keyboard_mode >= 0) {
                if ((keys_available() || kbstate < 3) && !kblostsynccnt ) {
                        switch (kbstate)
                        {
@@ -1181,7 +1231,7 @@ static void check_keyboard(void)
                                        kbcode = ~get_next_key();
                                        break;
                        }
-                       keyreq();
+                       cia_keyreq(kbcode);
                }
        } else {
                while (keys_available()) {
@@ -1213,29 +1263,34 @@ void CIA_hsync_posthandler(bool ciahsync, bool dotod)
                if (currprefs.tod_hack && cia[0].todon) {
                        do_tod_hack(dotod);
                }
-       } else if (currprefs.keyboard_connected) {
-               // custom hsync
-               if (resetwarning_phase) {
-                       resetwarning_check();
-                       while (keys_available()) {
-                               get_next_key();
-                       }
+       } else {
+               if (currprefs.keyboard_mode > 0) {
+                       keymcu_do();
                } else {
-                       if ((hsync_counter & 15) == 0) {
-                               check_keyboard();
+                       if (currprefs.keyboard_mode == 0) {
+                               // custom hsync
+                               if (resetwarning_phase) {
+                                       resetwarning_check();
+                                       while (keys_available()) {
+                                               get_next_key();
+                                       }
+                               } else {
+                                       if ((hsync_counter & 15) == 0) {
+                                               check_keyboard();
+                                       }
+                               }
+                       } else {
+                               while (keys_available()) {
+                                       get_next_key();
+                               }
                        }
                }
-       } else {
-               while (keys_available()) {
-                       get_next_key();
-               }
        }
 
        if (!ciahsync) {
                // Increase CIA-A TOD if delayed from previous line
                cia_delayed_tod(0);
        }
-
 }
 
 static void calc_led(int old_led)
@@ -1310,7 +1365,7 @@ void CIA_vsync_prehandler(void)
                if (kblostsynccnt <= 0) {
                        kblostsynccnt = 0;
                        kbcode = 0;
-                       keyreq();
+                       cia_keyreq(kbcode);
 #if KB_DEBUG
                        write_log(_T("lostsync\n"));
 #endif
@@ -2004,41 +2059,52 @@ static void WriteCIAA(uae_u16 addr, uae_u8 val, uae_u32 *flags)
        case 13:
        case 15:
                CIA_update();
+               if (currprefs.keyboard_mode > 0 && reg == 12) {
+                       keymcu_do();
+               }
                WriteCIAReg(0, reg, val);
                CIA_calctimers();
                break;
        case 14:
-               CIA_update();
-               // keyboard handshake handling
-               if (currprefs.cpuboard_type != 0 && (val & 0x40) != (c->t[0].cr & 0x40)) {
-                       /* bleh, Phase5 CPU timed early boot key check fix.. */
-                       if (m68k_getpc() >= 0xf00000 && m68k_getpc() < 0xf80000)
-                               check_keyboard();
-               }
-               if ((val & CR_INMODE1) != 0 && (c->t[0].cr & CR_INMODE1) == 0) {
-                       // handshake start
-                       if (kblostsynccnt > 0 && currprefs.cs_kbhandshake) {
-                               kbhandshakestart = get_cycles();
-                       }
-#if KB_DEBUG
-                       write_log(_T("KB_ACK_START %02x->%02x %08x\n"), c->t[0].cr, val, M68K_GETPC);
-#endif
-               } else if ((val & CR_INMODE1) == 0 && (c->t[0].cr & CR_INMODE1) != 0) {
-                       // handshake end
-                       /* todo: check if low to high or high to low only */
-                       if (kblostsynccnt > 0 && currprefs.cs_kbhandshake) {
-                               evt_t len = get_cycles() - kbhandshakestart;
-                               if (len < currprefs.cs_kbhandshake * CYCLE_UNIT) {
-                                       write_log(_T("Keyboard handshake pulse length %d < %d (CCKs)\n"), len / CYCLE_UNIT, currprefs.cs_kbhandshake);
+               {
+                       CIA_update();
+                       bool handshake = (val & CR_INMODE1) != (c->t[0].cr & CR_INMODE1);
+                       if (currprefs.keyboard_mode == 0) {
+                               // keyboard handshake handling
+                               if (currprefs.cpuboard_type != 0 && handshake) {
+                                       /* bleh, Phase5 CPU timed early boot key check fix.. */
+                                       if (m68k_getpc() >= 0xf00000 && m68k_getpc() < 0xf80000)
+                                               check_keyboard();
                                }
-                       }
-                       kblostsynccnt = 0;
+                               if ((val & CR_INMODE1) != 0 && (c->t[0].cr & CR_INMODE1) == 0) {
+                                       // handshake start
+                                       if (kblostsynccnt > 0 && currprefs.cs_kbhandshake) {
+                                               kbhandshakestart = get_cycles();
+                                       }
+#if KB_DEBUG
+                                       write_log(_T("KB_ACK_START %02x->%02x %08x\n"), c->t[0].cr, val, M68K_GETPC);
+#endif
+                               } else if ((val & CR_INMODE1) == 0 && (c->t[0].cr & CR_INMODE1) != 0) {
+                                       // handshake end
+                                       /* todo: check if low to high or high to low only */
+                                       if (kblostsynccnt > 0 && currprefs.cs_kbhandshake) {
+                                               evt_t len = get_cycles() - kbhandshakestart;
+                                               if (len < currprefs.cs_kbhandshake * CYCLE_UNIT) {
+                                                       write_log(_T("Keyboard handshake pulse length %d < %d (CCKs)\n"), len / CYCLE_UNIT, currprefs.cs_kbhandshake);
+                                               }
+                                       }
+                                       kblostsynccnt = 0;
 #if KB_DEBUG
-                       write_log(_T("KB_ACK_END %02x->%02x %08x\n"), c->t[0].cr, val, M68K_GETPC);
+                                       write_log(_T("KB_ACK_END %02x->%02x %08x\n"), c->t[0].cr, val, M68K_GETPC);
 #endif
+                               }
+                       }
+                       WriteCIAReg(0, reg, val);
+                       CIA_calctimers();
+                       if (currprefs.keyboard_mode > 0 && handshake) {
+                               keymcu_do();
+                       }
                }
-               WriteCIAReg(0, reg, val);
-               CIA_calctimers();
                break;
        }
 }
index 73376174ede23c03178cc7774df2965b6a8be1ca..dd166408a9cc700d803b4e5f9b3faba17f9450e3 100644 (file)
@@ -65,6 +65,7 @@
 #endif
 #include "draco.h"
 #include "dsp3210/dsp_glue.h"
+#include "keyboard_mcu.h"
 
 #define MAX_DEVICE_ITEMS 64
 
@@ -243,6 +244,9 @@ void devices_reset(int hardreset)
 #ifdef RETROPLATFORM
        rp_reset();
 #endif
+       keymcu_reset();
+       keymcu2_reset();
+       keymcu3_reset();
        uae_int_requested = 0;
 }
 
@@ -357,6 +361,9 @@ void virtualdevice_free(void)
 #ifdef WITH_DRACO
        draco_free();
 #endif
+       keymcu_free();
+       keymcu2_free();
+       keymcu3_free();
        execute_device_items(device_leaves, device_leave_cnt);
 }
 
@@ -418,6 +425,9 @@ void virtualdevice_init (void)
 #ifdef WITH_DRACO
        draco_init();
 #endif
+       keymcu_init();
+       keymcu2_init();
+       keymcu3_init();
 }
 
 void devices_restore_start(void)
index a71f6da97c1864cc565cda4ced462c6756388733..39feba7235c0ad172701804e4ce135cf41e44414 100644 (file)
@@ -4946,6 +4946,21 @@ static struct expansionboardsettings ethernet_settings[] = {
                NULL
        }
 };
+static const struct expansionboardsettings keyboard_settings[] = {
+       {
+               _T("Options\0") _T("-\0") _T("RAM fault\0") _T("ROM fault\0") _T("Watchdog fault\0"),
+               _T("kbmcuopt\0") _T("-\0") _T("ramf\0") _T("romf\0") _T("wdf\0"),
+               true
+       },
+       {
+               _T("Special feature enable"),
+               _T("sfe")
+       },
+       {
+               NULL
+       }
+};
+
 
 static struct expansionboardsettings *netsettings[] = {
        ethernet_settings,
@@ -6307,6 +6322,14 @@ const struct expansionromtype expansionroms[] = {
 
        // misc
 
+       {
+               _T("keyboard"), _T("Keyboard"), NULL,
+               NULL, NULL, NULL, NULL, ROMTYPE_KBMCU| ROMTYPE_NONE, 0, 0, BOARD_IGNORE, true,
+               NULL, 0,
+               false, EXPANSIONTYPE_INTERNAL,
+               0, 0, 0, false, NULL,
+               false, 0, keyboard_settings
+       },
        {
                _T("pcmciasram"), _T("PCMCIA SRAM"), NULL,
                NULL, gayle_init_board_common_pcmcia, NULL, NULL, ROMTYPE_PCMCIASRAM | ROMTYPE_NOT, 0, 0, BOARD_NONAUTOCONFIG_BEFORE, true,
index ee1c2b04ed25fcf3720b3f0b5ccd8750200d21cf..dc3c074ff8b63bca8b809ef083eb657119416209 100644 (file)
@@ -19,6 +19,7 @@ extern void gui_handle_events (void);
 extern void gui_filename (int, const TCHAR *);
 extern void gui_fps (int fps, int idle, int color);
 extern void gui_changesettings (void);
+extern void gui_fps (int fps, int lines, bool lace, int idle, int color);
 extern void gui_lock (void);
 extern void gui_unlock (void);
 extern void gui_flicker_led (int, int, int);
@@ -43,11 +44,13 @@ extern bool no_gui, quit_to_gui;
 #define LED_HD 5
 #define LED_CD 6
 #define LED_FPS 7
-#define LED_CPU 8
-#define LED_SND 9
-#define LED_MD 10
-#define LED_NET 11
-#define LED_MAX 12
+#define LED_LINES 8
+#define LED_CPU 9
+#define LED_SND 10
+#define LED_MD 11
+#define LED_NET 12
+#define LED_CAPS 13
+#define LED_MAX 14
 
 struct gui_info_drive {
        bool drive_motor;               /* motor on off */
@@ -62,17 +65,18 @@ struct gui_info_drive {
 
 struct gui_info
 {
-    bool powerled;                             /* state of power led */
-    uae_u8 powerled_brightness;        /* 0 to 255 */
-    uae_s8 drive_side;                 /* floppy side */
-    uae_s8 hd;                                 /* harddrive */
-    uae_s8 cd;                                 /* CD */
+       bool powerled;                          /* state of power led */
+       uae_u8 powerled_brightness;     /* 0 to 255 */
+       bool capslock;                          /* caps lock state if KB MCU mode */
+       uae_s8 drive_side;                      /* floppy side */
+       uae_s8 hd;                                      /* harddrive */
+       uae_s8 cd;                                      /* CD */
        uae_s8 md;                                      /* CD32 or CDTV internal storage */
        uae_s8 net;                                     /* network */
-    int cpu_halted;
-       int fps, idle;
+       int cpu_halted;
+       int fps, lines, lace, idle;
        int fps_color;
-    int sndbuf, sndbuf_status;
+       int sndbuf, sndbuf_status;
        bool sndbuf_avail;
        struct gui_info_drive drives[4];
 };
diff --git a/include/keyboard_mcu.h b/include/keyboard_mcu.h
new file mode 100644 (file)
index 0000000..1a9d3b1
--- /dev/null
@@ -0,0 +1,17 @@
+
+void keymcu_reset(void);
+void keymcu_init(void);
+void keymcu_free(void);
+bool keymcu_run(bool);
+
+void keymcu2_reset(void);
+void keymcu2_init(void);
+void keymcu2_free(void);
+bool keymcu2_run(bool);
+
+void keymcu3_reset(void);
+void keymcu3_init(void);
+void keymcu3_free(void);
+bool keymcu3_run(bool);
+
+void cia_keyreq(uae_u8);
\ No newline at end of file
diff --git a/include/mos6502.h b/include/mos6502.h
new file mode 100644 (file)
index 0000000..c30400f
--- /dev/null
@@ -0,0 +1,192 @@
+//============================================================================
+// Name        : mos6502
+// Author      : Gianluca Ghettini
+// Version     : 1.0
+// Copyright   :
+// Description : A MOS 6502 CPU emulator written in C++
+//============================================================================
+
+#pragma once
+#include <stdint.h>
+
+class mos6502
+{
+private:
+    // register reset values
+    uint8_t reset_A;
+    uint8_t reset_X;
+    uint8_t reset_Y;
+    uint8_t reset_sp;
+    uint8_t reset_status;
+
+       // registers
+       uint8_t A; // accumulator
+       uint8_t X; // X-index
+       uint8_t Y; // Y-index
+
+       // stack pointer
+       uint8_t sp;
+
+       // program counter
+       uint16_t pc;
+
+       // status register
+       uint8_t status;
+
+       typedef void (mos6502::*CodeExec)(uint16_t);
+       typedef uint16_t (mos6502::*AddrExec)();
+
+       struct Instr
+       {
+               AddrExec addr;
+               CodeExec code;
+               uint8_t cycles;
+       };
+
+       static Instr InstrTable[256];
+
+       void Exec(Instr i);
+
+       bool illegalOpcode;
+
+       // addressing modes
+       uint16_t Addr_ACC(); // ACCUMULATOR
+       uint16_t Addr_IMM(); // IMMEDIATE
+       uint16_t Addr_ABS(); // ABSOLUTE
+       uint16_t Addr_ZER(); // ZERO PAGE
+       uint16_t Addr_ZEX(); // INDEXED-X ZERO PAGE
+       uint16_t Addr_ZEY(); // INDEXED-Y ZERO PAGE
+       uint16_t Addr_ABX(); // INDEXED-X ABSOLUTE
+       uint16_t Addr_ABY(); // INDEXED-Y ABSOLUTE
+       uint16_t Addr_IMP(); // IMPLIED
+       uint16_t Addr_REL(); // RELATIVE
+       uint16_t Addr_INX(); // INDEXED-X INDIRECT
+       uint16_t Addr_INY(); // INDEXED-Y INDIRECT
+       uint16_t Addr_ABI(); // ABSOLUTE INDIRECT
+
+       // opcodes (grouped as per datasheet)
+       void Op_ADC(uint16_t src);
+       void Op_AND(uint16_t src);
+       void Op_ASL(uint16_t src);      void Op_ASL_ACC(uint16_t src);
+       void Op_BCC(uint16_t src);
+       void Op_BCS(uint16_t src);
+
+       void Op_BEQ(uint16_t src);
+       void Op_BIT(uint16_t src);
+       void Op_BMI(uint16_t src);
+       void Op_BNE(uint16_t src);
+       void Op_BPL(uint16_t src);
+
+       void Op_BRK(uint16_t src);
+       void Op_BVC(uint16_t src);
+       void Op_BVS(uint16_t src);
+       void Op_CLC(uint16_t src);
+       void Op_CLD(uint16_t src);
+
+       void Op_CLI(uint16_t src);
+       void Op_CLV(uint16_t src);
+       void Op_CMP(uint16_t src);
+       void Op_CPX(uint16_t src);
+       void Op_CPY(uint16_t src);
+
+       void Op_DEC(uint16_t src);
+       void Op_DEX(uint16_t src);
+       void Op_DEY(uint16_t src);
+       void Op_EOR(uint16_t src);
+       void Op_INC(uint16_t src);
+
+       void Op_INX(uint16_t src);
+       void Op_INY(uint16_t src);
+       void Op_JMP(uint16_t src);
+       void Op_JSR(uint16_t src);
+       void Op_LDA(uint16_t src);
+
+       void Op_LDX(uint16_t src);
+       void Op_LDY(uint16_t src);
+       void Op_LSR(uint16_t src);      void Op_LSR_ACC(uint16_t src);
+       void Op_NOP(uint16_t src);
+       void Op_ORA(uint16_t src);
+
+       void Op_PHA(uint16_t src);
+       void Op_PHP(uint16_t src);
+       void Op_PLA(uint16_t src);
+       void Op_PLP(uint16_t src);
+       void Op_ROL(uint16_t src);      void Op_ROL_ACC(uint16_t src);
+
+       void Op_ROR(uint16_t src);      void Op_ROR_ACC(uint16_t src);
+       void Op_RTI(uint16_t src);
+       void Op_RTS(uint16_t src);
+       void Op_SBC(uint16_t src);
+       void Op_SEC(uint16_t src);
+       void Op_SED(uint16_t src);
+
+       void Op_SEI(uint16_t src);
+       void Op_STA(uint16_t src);
+       void Op_STX(uint16_t src);
+       void Op_STY(uint16_t src);
+       void Op_TAX(uint16_t src);
+
+       void Op_TAY(uint16_t src);
+       void Op_TSX(uint16_t src);
+       void Op_TXA(uint16_t src);
+       void Op_TXS(uint16_t src);
+       void Op_TYA(uint16_t src);
+
+       void Op_ILLEGAL(uint16_t src);
+
+       // IRQ, reset, NMI vectors
+       static const uint16_t irqVectorH = 0xFFFF;
+       static const uint16_t irqVectorL = 0xFFFE;
+       static const uint16_t rstVectorH = 0xFFFD;
+       static const uint16_t rstVectorL = 0xFFFC;
+       static const uint16_t nmiVectorH = 0xFFFB;
+       static const uint16_t nmiVectorL = 0xFFFA;
+
+       // read/write/clock-cycle callbacks
+       typedef void (*BusWrite)(uint16_t, uint8_t);
+       typedef uint8_t (*BusRead)(uint16_t);
+       typedef void (*ClockCycle)(mos6502*);
+       BusRead Read;
+       BusWrite Write;
+       ClockCycle Cycle;
+
+       // stack operations
+       inline void StackPush(uint8_t byte);
+       inline uint8_t StackPop();
+
+public:
+       enum CycleMethod {
+               INST_COUNT,
+               CYCLE_COUNT,
+       };
+       mos6502(BusRead r, BusWrite w, ClockCycle c = nullptr);
+       void NMI();
+       void IRQ();
+       void Reset();
+       void Run(
+               int32_t cycles,
+               uint64_t& cycleCount,
+               CycleMethod cycleMethod = CYCLE_COUNT);
+       void RunEternally(); // until it encounters a illegal opcode
+                                                // useful when running e.g. WOZ Monitor
+                                                // no need to worry about cycle exhaus-
+                                                // tion
+    uint16_t GetPC();
+    uint8_t GetS();
+    uint8_t GetP();
+    uint8_t GetA();
+    uint8_t GetX();
+    uint8_t GetY();
+    void SetResetS(uint8_t value);
+    void SetResetP(uint8_t value);
+    void SetResetA(uint8_t value);
+    void SetResetX(uint8_t value);
+    void SetResetY(uint8_t value);
+       void SetPC(uint16_t value);
+       void SetP(uint8_t value);
+    uint8_t GetResetS();
+    uint8_t GetResetP();
+    uint8_t GetResetA();
+    uint8_t GetResetX();
+    uint8_t GetResetY();
+};
index d79301460bc14d5ac73c23b1f4c22bb91930160f..8c8b1c7495da86a6b88b5bb75f132395e61e4b5f 100644 (file)
@@ -495,6 +495,16 @@ struct monconfig
        struct wh gfx_size_fs_xtra[GFX_SIZE_EXTRA_NUM];
 };
 
+#define KB_DISCONNECTED -1
+#define KB_UAE 0
+#define KB_A500_6570 1
+#define KB_A600_6570 2
+#define KB_A1000_6500 3
+#define KB_A1000_6570 4
+#define KB_A1200_6805 5
+#define KB_A2000_8039 6
+#define KB_Ax000_6570 7
+
 struct uae_prefs {
 
        struct strlist *all_lines;
@@ -621,7 +631,9 @@ struct uae_prefs {
        float blitter_speed_throttle;
        unsigned int chipset_mask;
        bool chipset_hr;
-       bool keyboard_connected;
+       bool display_calibration;
+       int keyboard_mode;
+       bool keyboard_nkro;
        bool ntscmode;
        bool genlock;
        int genlock_image;
index 6d4d7c2a0f6453f4b73bae475e881bde6ce29faa..7bcb68c0b4879a677eac51acf466cd15a256ba8f 100644 (file)
@@ -141,6 +141,13 @@ extern void restore_p96_finish(void);
 extern uae_u8 *restore_keyboard(uae_u8 *);
 extern uae_u8 *save_keyboard(size_t *,uae_u8*);
 
+extern uae_u8 *restore_kbmcu(uae_u8 *);
+extern uae_u8 *save_kbmcu(size_t *,uae_u8*);
+extern uae_u8 *restore_kbmcu2(uae_u8 *);
+extern uae_u8 *save_kbmcu2(size_t *,uae_u8*);
+extern uae_u8 *restore_kbmcu3(uae_u8 *);
+extern uae_u8 *save_kbmcu3(size_t *,uae_u8*);
+
 extern uae_u8 *restore_akiko(uae_u8 *src);
 extern uae_u8 *save_akiko(size_t *len, uae_u8*);
 extern void restore_akiko_finish(void);
index 3720581e62fad3113ced988f3d29247f43b0e0ec..10d3a1a335014cf42a0a963238e37a580b4eb57d 100644 (file)
@@ -4623,7 +4623,22 @@ void inputdevice_do_keyboard(int code, int state)
                        return;
                }
 
-               if (key == AK_RESETWARNING) {
+               if (currprefs.keyboard_mode > 0) {
+                       if (!currprefs.cs_resetwarning) {
+                               if (keyboardresetkeys() || key == AK_RESETWARNING) {
+                                       int r = keybuf[AK_LALT] | keybuf[AK_RALT];
+                                       if (r) {
+                                               keyboard_reset_seq_mode = 2;
+                                               custom_reset(true, true);
+                                               cpu_inreset();
+                                       } else {
+                                               custom_reset(false, true);
+                                               cpu_inreset();
+                                               keyboard_reset_seq_mode = 1;
+                                       }
+                               }
+                       }
+               } else if (key == AK_RESETWARNING) {
                        if (resetwarning_do(0)) {
                                keyboard_reset_seq_mode = 3;
                        }
@@ -4667,6 +4682,8 @@ void inputdevice_do_kb_reset(void)
                } else {
                        keyboard_reset_seq_mode = 4;
                }
+       } else {
+               uae_reset(0, 1);
        }
 }
 
@@ -9163,6 +9180,7 @@ void inputdevice_swap_compa_ports (struct uae_prefs *prefs, int portswap)
        memcpy (&tmp, &prefs->jports[portswap], sizeof (struct jport));
        memcpy (&prefs->jports[portswap], &prefs->jports[portswap + 1], sizeof (struct jport));
        memcpy (&prefs->jports[portswap + 1], &tmp, sizeof (struct jport));
+
        inputdevice_updateconfig(NULL, prefs);
 }
 
diff --git a/kbmcu/8048/co8048.cpp b/kbmcu/8048/co8048.cpp
new file mode 100644 (file)
index 0000000..565d680
--- /dev/null
@@ -0,0 +1,923 @@
+//     Altirra - Atari 800/800XL/5200 emulator
+//     Coprocessor library - 65802 emulator
+//     Copyright (C) 2009-2015 Avery Lee
+//
+//     This program is free software; you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation; either version 2 of the License, or
+//     (at your option) any later version.
+//
+//     This program is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+//
+//     You should have received a copy of the GNU General Public License along
+//     with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+//     As a special exception, this library can also be redistributed and/or
+//     modified under an alternate license. See COPYING.RMT in the same source
+//     archive for details.
+
+#include <string>
+#include "co8048.h"
+
+ATCoProc8048::ATCoProc8048() {
+}
+
+void ATCoProc8048::SetProgramBanks(const void *p0, const void *p1) {
+       mpProgramBanks[0] = (const uint8 *)p0;
+       mpProgramBanks[1] = (const uint8 *)p1;
+
+       mpProgramBank = mpProgramBanks[mbPBK];
+}
+
+uint8 ATCoProc8048::ReadByte(uint8 addr) const {
+       return mRAM[addr];
+}
+
+void ATCoProc8048::WriteByte(uint8 addr, uint8 val) {
+       mRAM[addr] = val;
+}
+
+void ATCoProc8048::ColdReset() {
+       memset(mRAM, 0, sizeof mRAM);
+       mA = 0;
+       mT = 0;
+
+       WarmReset();
+}
+
+void ATCoProc8048::WarmReset() {
+       mPSW = 0x08;
+       mPC = 0;
+       mbF1 = false;
+       mbTF = false;
+       mbIF = false;
+       mbPBK = false;
+       mbDBF = false;
+       mbIrqEnabled = true;
+       mpRegBank = &mRAM[0];
+       mpProgramBank = mpProgramBanks[0];
+
+       mbTimerActive = false;
+
+       mP1 = 0xFF;
+       mP2 = 0xFF;
+       mpFnWritePort(0, 0xFF);
+       mpFnWritePort(1, 0xFF);
+
+       mTStatesLeft = 4;
+}
+
+void ATCoProc8048::AssertIrq() {
+       mbIrqPending = true;
+
+       if (mbIF)
+               mbIrqAttention = true;
+}
+
+void ATCoProc8048::NegateIrq() {
+       mbIrqPending = false;
+}
+
+void ATCoProc8048::Run() {
+       static const uint8 kInsnBytes[256]={
+//             0 1 2 3 4 5 6 7 8 9 A B C D E F
+               1,1,1,2,2,1,1,1,1,1,1,1,1,1,1,1,        // 1x
+               1,1,2,2,2,1,2,1,1,1,1,1,1,1,1,1,        // 1x
+               1,1,1,2,2,1,2,1,1,1,1,1,1,1,1,1,        // 2x
+               1,1,2,1,2,1,2,1,1,1,1,1,1,1,1,1,        // 3x
+               1,1,1,2,2,1,2,1,1,1,1,1,1,1,1,1,        // 4x
+               1,1,2,2,2,1,2,1,1,1,1,1,1,1,1,1,        // 5x
+               1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,        // 6x
+               1,1,2,1,2,1,2,1,1,1,1,1,1,1,1,1,        // 7x
+               1,1,2,1,2,1,2,1,2,2,2,1,1,1,1,1,        // 8x
+               1,1,2,1,2,1,2,1,2,2,2,1,1,1,1,1,        // 9x
+               1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,        // Ax
+               2,2,2,1,2,1,2,1,2,2,2,2,2,2,2,2,        // Bx
+               1,1,2,1,2,1,2,1,1,1,1,1,1,1,1,1,        // Cx
+               1,1,2,2,2,1,1,1,1,1,1,1,1,1,1,1,        // Dx
+               1,1,2,1,2,1,2,1,2,2,2,2,2,2,2,2,        // Ex
+               1,1,2,1,2,1,2,1,1,1,1,1,1,1,1,1,        // Fx
+       };
+
+       static const uint8 kInsnCycles[256]={
+//             0 1 2 3 4 5 6 7 8 9 A B C D E F
+               1,1,2,2,2,1,1,1,2,2,2,1,1,1,1,1,        // 0x
+               1,1,2,2,2,1,2,1,1,1,1,1,1,1,1,1,        // 1x
+               1,1,1,2,2,1,2,1,1,1,1,1,1,1,1,1,        // 2x
+               1,1,2,1,2,1,2,1,2,2,2,1,1,1,1,1,        // 3x
+               1,1,1,2,2,1,2,1,1,1,1,1,1,1,1,1,        // 4x
+               1,1,2,2,2,1,2,1,1,1,1,1,1,1,1,1,        // 5x
+               1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,        // 6x
+               1,1,2,1,2,1,2,1,1,1,1,1,1,1,1,1,        // 7x
+               2,2,2,2,2,1,2,1,2,2,2,1,1,1,1,1,        // 8x
+               2,2,2,2,2,1,2,1,2,2,2,1,2,2,2,1,        // 9x
+               2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,        // Ax
+               2,2,2,2,2,1,2,1,2,2,2,2,2,2,2,2,        // Bx
+               1,1,2,1,2,1,2,1,1,1,1,1,1,1,1,1,        // Cx
+               1,1,2,2,2,1,1,1,1,1,1,1,1,1,1,1,        // Dx
+               1,1,2,2,2,1,2,1,2,2,2,2,2,2,2,2,        // Ex
+               1,1,2,1,2,1,2,1,1,1,1,1,1,1,1,1,        // Fx
+       };
+
+       mCyclesLeft += mCyclesSaved;
+       mCyclesSaved = 0;
+
+       while(mCyclesLeft > 0) {
+               if (mbIrqAttention) {
+                       mbIrqAttention = false;
+
+                       if (mbIrqEnabled) {
+                               if (mbIrqPending && mbIF)
+                                       DispatchIrq();
+                       }
+               }
+
+               if (mbTIF && !mbTF && mbTimerActive) {
+                       UpdateTimer();
+                       if (mbTimerIrqPending && mbIrqEnabled)
+                               DispatchIrq();
+               }
+
+               const uint8 opcode = mpProgramBank[mPC];
+               const uint8 insnCycles = kInsnCycles[opcode];
+
+               mTStatesLeft = insnCycles;
+
+               if (mCyclesLeft < mTStatesLeft) {
+                       mCyclesSaved = mCyclesLeft;
+                       mCyclesLeft = 0;
+                       break;
+               }
+
+               const uint8 operand = mpProgramBank[mPC + 1];
+
+               mCyclesLeft -= insnCycles;
+
+               mPC = (mPC + kInsnBytes[opcode]) & 0x7FF;
+
+               switch(opcode) {
+                       case 0x68:              // ADD A,Rr
+                       case 0x69:
+                       case 0x6A:
+                       case 0x6B:
+                       case 0x6C:
+                       case 0x6D:
+                       case 0x6E:
+                       case 0x6F:
+                               {
+                                       const uint8 r = mpRegBank[opcode - 0x68];
+                                       uint32 result = (uint32)mA + r;
+
+                                       mPSW &= 0x3F;
+                                       mPSW |= (result >> 1) & 0x80;
+
+                                       if ((mA ^ r ^ result) & 0x10)
+                                               mPSW |= 0x40;
+
+                                       mA = (uint8)result;
+                               }
+                               break;
+
+                       case 0x60:              // ADD A,@Rr
+                       case 0x61:
+                               {
+                                       const uint8 r = mRAM[mpRegBank[opcode - 0x60]];
+                                       uint32 result = (uint32)mA + r;
+
+                                       mPSW &= 0x3F;
+                                       mPSW |= (result >> 1) & 0x80;
+
+                                       if ((mA ^ r ^ result) & 0x10)
+                                               mPSW |= 0x40;
+
+                                       mA = (uint8)result;
+                               }
+                               break;
+
+                       case 0x03:              // ADD A,#imm
+                               {
+                                       const uint8 r = operand;
+                                       uint32 result = (uint32)mA + r;
+
+                                       mPSW &= 0x3F;
+                                       mPSW |= (result >> 1) & 0x80;
+
+                                       if ((mA ^ r ^ result) & 0x10)
+                                               mPSW |= 0x40;
+
+                                       mA = (uint8)result;
+                               }
+                               break;
+
+                       case 0x78:              // ADDC A,Rr
+                       case 0x79:
+                       case 0x7A:
+                       case 0x7B:
+                       case 0x7C:
+                       case 0x7D:
+                       case 0x7E:
+                       case 0x7F:
+                               {
+                                       const uint8 r = mpRegBank[opcode - 0x78];
+                                       uint32 result = (uint32)mA + r + (mPSW >> 7);
+
+                                       mPSW &= 0x3F;
+                                       mPSW |= (result >> 1) & 0x80;
+
+                                       if ((mA ^ r ^ result) & 0x10)
+                                               mPSW |= 0x40;
+
+                                       mA = (uint8)result;
+                               }
+                               break;
+
+                       case 0x70:              // ADDC A,@Rr
+                       case 0x71:
+                               {
+                                       const uint8 r = mRAM[mpRegBank[opcode - 0x70]];
+                                       uint32 result = (uint32)mA + r + (mPSW >> 7);
+
+                                       mPSW &= 0x3F;
+                                       mPSW |= (result >> 1) & 0x80;
+
+                                       if ((mA ^ r ^ result) & 0x10)
+                                               mPSW |= 0x40;
+
+                                       mA = (uint8)result;
+                               }
+                               break;
+
+                       case 0x13:              // ADDC A,#imm
+                               {
+                                       const uint8 r = operand;
+                                       uint32 result = (uint32)mA + r + (mPSW >> 7);
+
+                                       mPSW &= 0x3F;
+                                       mPSW |= (result >> 1) & 0x80;
+
+                                       if ((mA ^ r ^ result) & 0x10)
+                                               mPSW |= 0x40;
+
+                                       mA = (uint8)result;
+                               }
+                               break;
+
+                       case 0x58:              // ANL A,Rr
+                       case 0x59:
+                       case 0x5A:
+                       case 0x5B:
+                       case 0x5C:
+                       case 0x5D:
+                       case 0x5E:
+                       case 0x5F:
+                               mA &= mpRegBank[opcode - 0x58];
+                               break;
+
+                       case 0x50:              // ANL A,@Rr
+                       case 0x51:
+                               mA &= mRAM[mpRegBank[opcode - 0x50]];
+                               break;
+
+                       case 0x53:              // ANL A,#data
+                               mA &= operand;
+                               break;
+
+       //              case 0x98:              // ANL BUS,#data
+
+                       case 0x99:              // ANL P1,#data
+                               {
+                                       const uint8 r = mP1 & operand;
+
+                                       if (mP1 != r) {
+                                               mP1 = r;
+
+                                               mpFnWritePort(0, r);
+                                       }
+                               }
+                               break;
+
+                       case 0x9A:              // ANL P2,#data
+                               {
+                                       const uint8 r = mP2 & operand;
+
+                                       if (mP2 != r) {
+                                               mP2 = r;
+
+                                               mpFnWritePort(1, r);
+                                       }
+                               }
+                               break;
+
+                       case 0x14:              // CALL address
+                       case 0x34:
+                       case 0x54:
+                       case 0x74:
+                       case 0x94:
+                       case 0xB4:
+                       case 0xD4:
+                       case 0xF4:
+                               {
+                                       uint8 *stackEntry = &mRAM[0x08 + (mPSW & 7) * 2];
+
+                                       stackEntry[0] = (uint8)mPC;
+                                       stackEntry[1] = (uint8)((mPC >> 8) + (mPSW & 0xF0) + (mbPBK ? 0x08 : 0x00));
+
+                                       mPSW = (mPSW - 0x07) | 0x08;
+
+                                       mbPBK = mbDBF;
+                                       mPC = ((uint32)(opcode & 0xE0) << 3) + operand;
+                                       mpProgramBank = mpProgramBanks[mbPBK];
+                               }
+                               break;
+
+                       case 0x27:              // CLR A
+                               mA = 0;
+                               break;
+
+                       case 0x97:              // CLR C
+                               mPSW &= 0x7F;
+                               break;
+
+                       case 0xA5:              // CLR F1
+                               mbF1 = false;
+                               break;
+
+                       case 0x85:              // CLR F0
+                               mPSW &= 0xDF;
+                               break;
+
+                       case 0x37:              // CPL A
+                               mA = ~mA;
+                               break;
+
+                       case 0xA7:              // CPL C
+                               mPSW ^= 0x80;
+                               break;
+
+                       case 0x95:              // CPL F0
+                               mPSW ^= 0x20;
+                               break;
+
+                       case 0xB5:              // CPL F1
+                               mbF1 = !mbF1;
+                               break;
+
+                       case 0x57:              // DA A
+                               {
+                                       uint32 r = mA;
+
+                                       if ((r & 0x0F) >= 0x0A || (mPSW & 0x40))
+                                               r += 0x06;
+
+                                       if (r >= 0xA0 || (mPSW & 0x80))
+                                               r += 0x60;
+
+                                       mPSW &= 0x7F;
+                                       mPSW |= (r >> 1) & 0x80;
+                               }
+                               break;
+
+                       case 0x07:              // DEC A
+                               --mA;
+                               break;
+
+                       case 0xC8:              // DEC Rr
+                       case 0xC9:
+                       case 0xCA:
+                       case 0xCB:
+                       case 0xCC:
+                       case 0xCD:
+                       case 0xCE:
+                       case 0xCF:
+                               --mpRegBank[opcode - 0xC8];
+                               break;
+
+                       case 0x15:              // DIS I
+                               mbIF = false;
+                               break;
+
+                       case 0x35:              // DIS TCNTI
+                               mbTIF = false;
+                               mbTimerIrqPending = false;
+                               break;
+
+                       case 0xE8:              // DJNZ Rr,address
+                       case 0xE9:
+                       case 0xEA:
+                       case 0xEB:
+                       case 0xEC:
+                       case 0xED:
+                       case 0xEE:
+                       case 0xEF:
+                               if (--mpRegBank[opcode - 0xE8])
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0x05:              // EN I
+                               mbIF = true;
+                               mbIrqAttention |= mbIrqPending;
+                               break;
+
+                       case 0x25:              // EN TCNTI
+                               mbTIF = true;
+                               mbIrqAttention |= mbTF;
+
+                               DispatchIrq();
+                               break;
+
+                       case 0x08:              // IN A,BUS
+                               mA = mpFnReadBus();
+                               break;
+
+                       case 0x09:              // IN A,P1
+                               mA = mpFnReadPort(0, mP1);
+                               break;
+
+                       case 0x0A:              // IN A,P2
+                               mA = mpFnReadPort(1, mP2);
+                               break;
+
+                       case 0x17:              // INC A
+                               ++mA;
+                               break;
+
+                       case 0x18:              // INC Rr
+                       case 0x19:
+                       case 0x1A:
+                       case 0x1B:
+                       case 0x1C:
+                       case 0x1D:
+                       case 0x1E:
+                       case 0x1F:
+                               ++mpRegBank[opcode - 0x18];
+                               break;
+
+                       case 0x10:              // INC @Rr
+                       case 0x11:              // INC @Rr
+                               ++mRAM[mpRegBank[opcode - 0x10]];
+                               break;
+
+                       case 0x12:              // JBb address
+                       case 0x32:
+                       case 0x52:
+                       case 0x72:
+                       case 0x92:
+                       case 0xB2:
+                       case 0xD2:
+                       case 0xF2:
+                               if (mA & (1 << ((opcode >> 5) & 7)))
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0xF6:              // JC address
+                               if (mPSW & 0x80)
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0xB6:              // JF0 address
+                               if (mPSW & 0x20)
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0x76:              // JF1 address
+                               if (mbF1)
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0x04:              // JMP address
+                       case 0x24:
+                       case 0x44:
+                       case 0x64:
+                       case 0x84:
+                       case 0xA4:
+                       case 0xC4:
+                       case 0xE4:
+                               mbPBK = mbDBF;
+                               mPC = ((uint32)(opcode & 0xE0) << 3) + operand;
+                               mpProgramBank = mpProgramBanks[mbPBK];
+                               break;
+
+                       case 0xB3:              // JMPP @A
+                               {
+                                       const uint32 pageBase = ((mPC - 1) & 0x700);
+                                       mPC = pageBase + mpProgramBank[pageBase + mA];
+                               }
+                               break;
+
+                       case 0xE6:              // JNC address
+                               if (!(mPSW & 0x80))
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0x86:              // JNI address
+                               if (mbIrqPending)
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0x26:              // JNT0 address
+                               if (!mpFnReadT0())
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0x46:              // JNT1 address
+                               if (!mpFnReadT1())
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0x96:              // JNZ address
+                               if (mA)
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0x16:              // JTF address
+                               if (!mbTF && mbTimerActive)
+                                       UpdateTimer();
+
+                               if (mbTF) {
+                                       mbTF = false;
+                                       UpdateTimer();
+                                       UpdateTimerDeadline();
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               }
+                               break;
+
+                       case 0x36:              // JT0 address
+                               if (mpFnReadT0())
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0x56:              // JT1 address
+                               if (mpFnReadT1())
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0xC6:              // JZ address
+                               if (!mA)
+                                       mPC = ((mPC - 1) & 0x700) + operand;
+                               break;
+
+                       case 0x23:              // MOV A,#data
+                               mA = operand;
+                               break;
+
+                       case 0xC7:              // MOV A,PSW
+                               mA = mPSW;
+                               break;
+
+                       case 0xF8:              // MOV A,Rr
+                       case 0xF9:
+                       case 0xFA:
+                       case 0xFB:
+                       case 0xFC:
+                       case 0xFD:
+                       case 0xFE:
+                       case 0xFF:
+                               mA = mpRegBank[opcode - 0xF8];
+                               break;
+
+                       case 0xF0:              // MOV A,@Rr
+                       case 0xF1:
+                               mA = mRAM[mpRegBank[opcode - 0xF0]];
+                               break;
+
+                       case 0x42:              // MOV A,T
+                               if (mbTimerActive)
+                                       UpdateTimer();
+
+                               mA = mT;
+                               break;
+
+                       case 0xD7:              // MOV PSW,A
+                               mPSW = mA | 0x08;
+                               mpRegBank = mPSW & 0x10 ? &mRAM[0x18] : mRAM;
+                               break;
+
+                       case 0xA8:              // MOV Rr,A
+                       case 0xA9:
+                       case 0xAA:
+                       case 0xAB:
+                       case 0xAC:
+                       case 0xAD:
+                       case 0xAE:
+                       case 0xAF:
+                               mpRegBank[opcode - 0xA8] = mA;
+                               break;
+
+                       case 0xB8:              // MOV Rr,#data
+                       case 0xB9:
+                       case 0xBA:
+                       case 0xBB:
+                       case 0xBC:
+                       case 0xBD:
+                       case 0xBE:
+                       case 0xBF:
+                               mpRegBank[opcode - 0xB8] = operand;
+                               break;
+
+                       case 0xA0:              // MOV @Rr,A
+                       case 0xA1:
+                               mRAM[mpRegBank[opcode - 0xA0]] = mA;
+                               break;
+
+                       case 0xB0:              // MOV @Rr,#data
+                       case 0xB1:
+                               mRAM[mpRegBank[opcode - 0xB0]] = operand;
+                               break;
+
+                       case 0x62:              // MOV T,A
+                               mT = mA;
+                               if (mbTimerActive)
+                                       UpdateTimerDeadline();
+                               break;
+
+                       case 0xA3:              // MOVP A,@A
+                               mA = mpProgramBank[((mPC - 1) & 0x700) + mA];
+                               break;
+
+                       case 0xE3:              // MOVP3 A,@A
+                               // MOVP3 is documented as always setting A8-11 to 0011
+                               // regardless of the current state of DBF or PC[11], so we
+                               // need to force bank 0.
+                               mA = mpProgramBanks[0][0x300 + mA];
+                               break;
+
+                       case 0x80:              // MOVX A,@Rr
+                       case 0x81:
+                               mA = mpRegBank[opcode - 0x80];
+                               break;
+
+                       case 0x90:              // MOVX @Rr,A
+                       case 0x91:
+                               mpRegBank[opcode - 0x90], mA;
+                               break;
+
+                       case 0x00:              // NOP
+                               break;
+
+                       case 0x48:              // ORL A,Rr
+                       case 0x49:
+                       case 0x4A:
+                       case 0x4B:
+                       case 0x4C:
+                       case 0x4D:
+                       case 0x4E:
+                       case 0x4F:
+                               mA |= mpRegBank[opcode - 0x48];
+                               break;
+
+                       case 0x40:              // ORL A,@Rr
+                       case 0x41:
+                               mA |= mRAM[mpRegBank[opcode - 0x40]];
+                               break;
+
+                       case 0x43:              // ORL A,#data
+                               mA |= operand;
+                               break;
+
+       //              case 0x88:              // ORL BUS,#data
+                       case 0x89:              // ORL P1,#data
+                               {
+                                       const uint8 r = mP1 | operand;
+
+                                       if (mP1 != r) {
+                                               mP1 = r;
+
+                                               mpFnWritePort(0, r);
+                                       }
+                               }
+                               break;
+
+                       case 0x8A:              // ORL P2,#data
+                               {
+                                       const uint8 r = mP2 | operand;
+
+                                       if (mP2 != r) {
+                                               mP2 = r;
+
+                                               mpFnWritePort(1, r);
+                                       }
+                               }
+                               break;
+
+       //              case 0x38:              // OUTL BUS,A
+
+                       case 0x39:              // OUTL P1,A
+                               if (mP1 != mA) {
+                                       mP1 = mA;
+                                       mpFnWritePort(0, mA);
+                               }
+                               break;
+
+                       case 0x3A:              // OUTL P2,A
+                               if (mP2 != mA) {
+                                       mP2 = mA;
+                                       mpFnWritePort(1, mA);
+                               }
+                               break;
+
+                       case 0x83:              // RET
+                               {
+                                       mPSW = (mPSW - 1) | 0x08;
+
+                                       const uint8 *stackEntry = &mRAM[0x08 + (mPSW & 7)*2];
+
+                                       mbPBK = (stackEntry[1] & 0x08) != 0;
+                                       mpProgramBank = mpProgramBanks[mbPBK];
+
+                                       mPC = (uint32)stackEntry[0] + (((uint32)stackEntry[1] & 0x07) << 8);
+                               }
+                               break;
+
+                       case 0x93:              // RETR
+                               {
+                                       mPSW = (mPSW - 1) | 0x08;
+
+                                       const uint8 *stackEntry = &mRAM[0x08 + (mPSW & 7)*2];
+
+                                       mbPBK = (stackEntry[1] & 0x08) != 0;
+                                       mpProgramBank = mpProgramBanks[mbPBK];
+
+                                       mPC = (uint32)stackEntry[0] + (((uint32)stackEntry[1] & 0x07) << 8);
+                                       mPSW &= stackEntry[1] | 0x0F;
+                                       mpRegBank = (mPSW & 0x10) ? &mRAM[0x18] : mRAM;
+
+                                       mbIrqEnabled = true;
+
+                                       DispatchIrq();
+                               }
+                               break;
+
+                       case 0xE7:              // RL A
+                               mA = (mA + mA) + (mA >> 7);
+                               break;
+
+                       case 0xF7:              // RLC A
+                               {
+                                       uint32 r = (uint32)mA + mA;
+
+                                       mA = (uint8)(r + (mPSW >> 7));
+                                       mPSW = (mPSW & 0x7F) + ((r >> 1) & 0x80);
+                               }
+                               break;
+
+                       case 0x77:              // RR A
+                               mA = (mA >> 1) + (mA << 7);
+                               break;
+
+                       case 0x67:              // RRC A
+                               {
+                                       uint8 r = mA;
+
+                                       mA = (uint8)((mA >> 1) + (mPSW & 0x80));
+                                       mPSW = (mPSW & 0x7F) + (r << 7);
+                               }
+                               break;
+
+                       case 0xE5:              // SEL MB0
+                               mbDBF = false;
+                               break;
+
+                       case 0xF5:              // SEL MB1
+                               mbDBF = true;
+                               break;
+
+                       case 0xC5:              // SEL RB0
+                               mPSW &= 0xEF;
+                               mpRegBank = mRAM;
+                               break;
+
+                       case 0xD5:              // SEL RB1
+                               mPSW |= 0x10;
+                               mpRegBank = &mRAM[0x18];
+                               break;
+
+                       case 0x65:              // STOP TCNT
+                               if (mbTimerActive) {
+                                       mbTimerActive = false;
+
+                                       UpdateTimer();
+                               }
+                               break;
+
+       //              case 0x45:              // STRT TCNT
+
+                       case 0x55:              // STRT T
+                               mbTimerActive = true;
+                               UpdateTimerDeadline();
+                               break;
+
+                       case 0x47:              // SWAP A
+                               mA = (mA << 4) + (mA >> 4);
+                               break;
+
+                       case 0x28:              // XCH A,Rr
+                       case 0x29:
+                       case 0x2A:
+                       case 0x2B:
+                       case 0x2C:
+                       case 0x2D:
+                       case 0x2E:
+                       case 0x2F:
+                               {
+                                       uint8 *r = &mpRegBank[opcode - 0x28];
+                                       uint8 t = mA;
+                                       mA = *r;
+                                       *r = t;
+                               }
+                               break;
+
+                       case 0x20:              // XCH A,@Rr
+                       case 0x21:
+                               {
+                                       uint8 *r = &mRAM[mpRegBank[opcode - 0x20]];
+                                       uint8 t = mA;
+                                       mA = *r;
+                                       *r = t;
+                               }
+                               break;
+
+                       case 0xD8:              // XRL A,Rr
+                       case 0xD9:
+                       case 0xDA:
+                       case 0xDB:
+                       case 0xDC:
+                       case 0xDD:
+                       case 0xDE:
+                       case 0xDF:
+                               mA ^= mpRegBank[opcode - 0xD8];
+                               break;
+
+                       case 0xD0:              // XRL A,@Rr
+                       case 0xD1:
+                               mA ^= mRAM[mpRegBank[opcode - 0xD0]];
+                               break;
+
+                       case 0xD3:              // XRL A,#data
+                               mA ^= operand;
+                               break;
+
+                       default:
+                               mCyclesLeft = 0;
+                               mPC = (mPC - 1) & 0x7FF;
+                               break;
+               }
+       }
+
+force_exit:
+       ;
+}
+
+void ATCoProc8048::DispatchIrq() {
+       if (!mbIrqEnabled)
+               return;
+
+       if (mbIF && mbIrqPending) {
+               mbIrqEnabled = false;
+
+               uint8 *stackEntry = &mRAM[0x08 + (mPSW & 7) * 2];
+
+               stackEntry[0] = (uint8)mPC;
+               stackEntry[1] = (uint8)((mPC >> 8) + (mPSW & 0xF0) + (mbPBK ? 0x08 : 0x00));
+
+               mPSW = (mPSW - 0x07) | 0x08;
+
+               mbPBK = false;
+               mpProgramBank = mpProgramBanks[false];
+               mPC = 3;
+       } else if (mbTIF && mbTimerIrqPending) {
+               mbIrqEnabled = false;
+               mbTimerIrqPending = false;
+
+               uint8 *stackEntry = &mRAM[0x08 + (mPSW & 7) * 2];
+
+               stackEntry[0] = (uint8)mPC;
+               stackEntry[1] = (uint8)((mPC >> 8) + (mPSW & 0xF0) + (mbPBK ? 0x08 : 0x00));
+
+               mPSW = (mPSW - 0x07) | 0x08;
+
+               mbPBK = false;
+               mpProgramBank = mpProgramBanks[false];
+               mPC = 7;
+       }
+}
+
+void ATCoProc8048::UpdateTimer() {
+       const uint32 delta = (mCyclesBase - mCyclesLeft) - mTimerDeadline;
+       mT = (uint8)(delta >> 5);
+
+       if (!mbTF && !(delta & (UINT32_C(1) << 31))) {
+               mbTF = true;
+
+               if (mbTIF)
+                       mbTimerIrqPending = true;
+
+               mTimerDeadline += 32 * 256;
+       }
+}
+
+void ATCoProc8048::UpdateTimerDeadline() {
+       mTimerDeadline = ((mCyclesBase - mCyclesLeft) & ~UINT32_C(31)) + ((0x100 - (uint32)mT) << 5);
+}
diff --git a/kbmcu/8048/co8048.h b/kbmcu/8048/co8048.h
new file mode 100644 (file)
index 0000000..002320d
--- /dev/null
@@ -0,0 +1,118 @@
+//     Altirra - Atari 800/800XL/5200 aemulator
+//     Coprocessor library - 65802 emulator
+//     Copyright (C) 2009-2015 Avery Lee
+//
+//     This program is free software; you can redistribute it and/or modify
+//     it under the terms of the GNU General Public License as published by
+//     the Free Software Foundation; either version 2 of the License, or
+//     (at your option) any later version.
+//
+//     This program is distributed in the hope that it will be useful,
+//     but WITHOUT ANY WARRANTY; without even the implied warranty of
+//     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//     GNU General Public License for more details.
+//
+//     You should have received a copy of the GNU General Public License along
+//     with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+//     As a special exception, this library can also be redistributed and/or
+//     modified under an alternate license. See COPYING.RMT in the same source
+//     archive for details.
+
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef uint8_t uint8;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef int32_t sint32;
+
+#ifndef f_ATCOPROC_CO8048_H
+#define f_ATCOPROC_CO8048_H
+
+//#include <vd2/system/function.h>
+
+class ATCoProc8048 {
+public:
+       ATCoProc8048();
+
+       void SetProgramBanks(const void *p0, const void *p1);
+
+       uint16 GetPC() const { return mPC; }
+
+       uint8 GetSP() const { return mPSW & 7; }
+       uint32 GetStepStackLevel() const { return (uint32)mPSW << 29; }
+       uint8 GetPort1Output() const { return mP1; }
+       uint8 GetPort2Output() const { return mP2; }
+
+       uint32 GetTime() const { return mCyclesBase - mCyclesLeft; }
+       uint32 GetTimeBase() const { return mCyclesBase; }
+
+       const uint8 *GetInternalRAM() const { return mRAM; }
+       uint8 *GetInternalRAM() { return mRAM; }
+       uint8 ReadByte(uint8 addr) const;
+       void WriteByte(uint8 addr, uint8 val);
+
+       void ColdReset();
+       void WarmReset();
+
+       void AssertIrq();
+       void NegateIrq();
+
+       uint32 GetTStatesPending() const { return mTStatesLeft; }
+       uint32 GetCyclesLeft() const { return mCyclesLeft; }
+       void AddCycles(sint32 cycles) { mCyclesBase += cycles;  mCyclesLeft += cycles; }
+       void Run();
+
+       void DispatchIrq();
+       void UpdateTimer();
+       void UpdateTimerDeadline();
+
+       int             mTStatesLeft = 0;
+
+       uint8   mA;
+       uint8   mPSW;
+       uint8   *mpRegBank;
+       const uint8     *mpProgramBanks[2];
+       const uint8     *mpProgramBank;
+
+       uint8   mT = 0;
+       uint8   mP1 = 0xFF;
+       uint8   mP2 = 0xFF;
+       bool    mbDBF = false;
+       bool    mbPBK = false;
+       bool    mbIF = false;
+       bool    mbTIF = false;
+       bool    mbTF = false;
+       bool    mbF1 = false;
+       bool    mbIrqEnabled = false;
+       bool    mbIrqPending = false;
+       bool    mbIrqAttention = false;
+       bool    mbTimerIrqPending = false;
+
+       uint16          mPC = 0;
+       sint32          mCyclesLeft = 0;
+       uint32          mCyclesBase = 0;
+       uint32          mCyclesSaved = 0;
+
+       bool    mbTimerActive = false;
+       uint32          mTimerDeadline = 0;
+
+       const uint8     *mpNextState = nullptr;
+       uint8           mReadOpcodeState = 0;
+
+       alignas(2) uint8        mRAM[256];
+
+       static const uint8 kInitialState;
+       static const uint8 kInitialStateNoBreak;
+       static const uint8 kIrqSequence[];
+};
+
+void mpFnWritePort(int, uint8);
+uint8 mpFnReadBus(void);
+void mpFnWriteBus(uint8);
+uint8 mpFnReadPort(int, uint8);
+uint8 mpFnReadT0(void);
+uint8 mpFnReadT1(void);
+
+#endif // f_ATCOPROC_CO8048_H
diff --git a/kbmcu/keyboard_mcu_6500_1.cpp b/kbmcu/keyboard_mcu_6500_1.cpp
new file mode 100644 (file)
index 0000000..a3558b9
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+* UAE - The Un*x Amiga Emulator
+*
+* Keyboard MCU emulation
+* 6500-1 (early A1000 keyboards. ROM not yet dumped)
+* CSG 6570-036 is same MCU as 6500-1 with ROM update that fixes key ghosting.
+*
+* Copyright 2024 Toni Wilen
+*/
+
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include "options.h"
+#include "rommgr.h"
+#include "zfile.h"
+#include "events.h"
+#include "keyboard_mcu.h"
+#include "keybuf.h"
+#include "mos6502.h"
+#include "inputdevice.h"
+#include "gui.h"
+#include "savestate.h"
+
+int kb_mcu_log = 0;
+
+static uae_u8 mcu_rom[2048];
+static uae_u8 mcu_ram[0x40];
+static uae_u8 mcu_io[0x10];
+static uae_u8 matrix[128];
+static uae_u32 mcu_wd;
+static bool mcu_caps_led;
+static mos6502 *mcu;
+static evt_t lastcycle, kclk_reset_start;
+static uae_u8 kbdata, kbdatacnt;
+static bool kbhandshake;
+static bool rom_loaded;
+static int fakemode;
+static bool state_loaded;
+static int mcu_flags;
+
+// 15*6 matrix
+static const uae_u8 matrixkeys[] = {
+       0x5f,0x4c,0x4f,0x4e,0x4d,0x4a,
+       0x59,0x0d,0x44,0x46,0x41,0x0f,
+       0x58,0x0c,0x1b,0x2b,0x40,0x1d,
+       0x57,0x0b,0x1a,0x2a,0x3b,0x2d,
+       0x56,0x0a,0x19,0x29,0x3a,0x3d,
+       0x5c,0x09,0x18,0x28,0x39,0x43,
+       0x55,0x08,0x17,0x27,0x38,0x1e,
+       0x5b,0x07,0x16,0x26,0x37,0x2e,
+       0x54,0x06,0x15,0x25,0x36,0x3e,
+       0x53,0x05,0x14,0x24,0x35,0x3c,
+       0x52,0x04,0x13,0x23,0x34,0x1f,
+       0x51,0x03,0x12,0x22,0x33,0x2f,
+       0x50,0x02,0x11,0x21,0x32,0x3f,
+       0x5a,0x01,0x10,0x20,0x31,0x5e,
+       0x45,0x00,0x42,0x62,0x30,0x5d,
+       // not connected in matrix but included in ROM matrix table
+       0x49,0x48,0x47,0x2c,0x1c,0x0e
+};
+
+static void key_to_matrix(uae_u8 code)
+{
+       uae_u8 c = code >> 1;
+       if (code & 1) {
+               matrix[c] = 0;
+               if (kb_mcu_log & 1) {
+                       write_log("release %02x\n", c);
+               }
+       } else {
+               matrix[c] = 1;
+               if (kb_mcu_log & 1) {
+                       write_log("press %02x\n", c);
+               }
+       }
+}
+
+static void keymcu_irq(void)
+{
+       uae_u8 c = mcu_io[15];
+       if  ((c & (0x80 | 0x10)) == (0x80 | 0x10) ||
+               (c & (0x40 | 0x08)) == (0x40 | 0x08) ||
+               (c & (0x20 | 0x04)) == (0x20 | 0x04)) {
+               if (kb_mcu_log & 4) {
+                       write_log("%04x %04x IRQ %02x\n", mcu_wd, mcu->GetPC(), c);
+               }
+               mcu->IRQ();
+       }
+}
+
+// This should be in CIA emulation but it is simpler to have it here.
+static void get_kbit(bool v)
+{
+       if (kbhandshake && !fakemode) {
+               return;
+       }
+       kbdata <<= 1;
+       kbdata |= v ? 1 : 0;
+       kbdatacnt++;
+       if (kbdatacnt >= 8) {
+               kbdatacnt = 0;
+               if (kb_mcu_log & 1) {
+                       uae_u8 code = (kbdata >> 1) | (kbdata << 7);
+                       write_log("got keycode %02x\n", code ^ 0xff);
+               }
+               if (fakemode) {
+                       fakemode = 10000;
+               } else {
+                       cia_keyreq(kbdata);
+               }
+       }
+}
+
+static uae_u8 mcu_io_read(int addr)
+{
+       uint8_t v = mcu_io[addr];
+
+       if (kb_mcu_log & 2) {
+               write_log("%04x %04x IOR %d = %02x\n", mcu_wd, mcu->GetPC(), addr, v);
+       }
+
+       switch (addr)
+       {
+               case 0: // PORTA: KDAT=0,KCLK=1,2-7 = Matrix column read
+               {
+                       if (kbhandshake || fakemode > 1) {
+                               v &= ~1;
+                       }
+
+                       int row = mcu_io[2] | ((mcu_io[3] & 0x7f) << 8);
+
+                       if (!currprefs.keyboard_nkro) {
+                               // matrix without diodes
+                               for (int i = 0; i < 15; i++) {
+                                       if (!(row & (1 << i))) {
+                                               const uae_u8 *mr = matrixkeys + i * 6;
+                                               for (int j = 0; j < 6; j++) {
+                                                       if (matrix[mr[j]]) {
+                                                               for(int ii = 0; ii < 15; ii++) {
+                                                                       if ((row & (1 << ii)) && ii != i) {
+                                                                               const uae_u8 *mr2 = matrixkeys + ii * 6;
+                                                                               if (matrix[mr2[j]]) {
+                                                                                       row &= ~(1 << ii);
+                                                                                       i = -1;
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       for (int i = 0; i < 15; i++) {
+                               if (!(row & (1 << i))) {
+                                       const uae_u8 *mr = matrixkeys + i * 6;
+                                       for (int j = 0; j < 6; j++) {
+                                               if (matrix[mr[j]]) {
+                                                       v &= ~(4 << j);
+                                               }
+                                       }
+                               }
+                       }
+               }
+               break;
+               case 1: // PORTB: RSHIFT=0,RALT=1,RAMIGA=2,CTRL=3,LSHIFT=4,LALT=5,LAMIGA=6,CAPSLED=7
+               {
+                       if (matrix[0x61]) {
+                               v &= ~0x01;
+                       }
+                       if (matrix[0x65]) {
+                               v &= ~0x02;
+                       }
+                       if (matrix[0x67]) {
+                               v &= ~0x04;
+                       }
+                       if (matrix[0x63]) {
+                               v &= ~0x08;
+                       }
+                       if (matrix[0x60]) {
+                               v &= ~0x10;
+                       }
+                       if (matrix[0x64]) {
+                               v &= ~0x20;
+                       }
+                       if (matrix[0x66]) {
+                               v &= ~0x40;
+                       }
+               }
+               break;
+               case 7:
+               mcu_io[15] &= ~0x80;
+               break;
+       }
+       return v;
+}
+
+static void mcu_io_write(int addr, uae_u8 v)
+{
+       if (kb_mcu_log & 2) {
+               write_log("%04x %04X IOW %d = %02x\n", mcu_wd, mcu->GetPC(), addr, v);
+       }
+
+       switch (addr)
+       {
+               case 0xf: // CONTROL
+               mcu_io[addr] = v;
+               break;
+               case 0x8: // UPPER LATCH AND TRANSFER LATCH TO COUNTER
+               mcu_io[4] = v;
+               mcu_io[7] = mcu_io[5];
+               mcu_io[6] = mcu_io[4];
+               mcu_io[15] &= ~0x80;
+               break;
+               case 9: // CLEAR PA0 POS EDGE DETECT
+               mcu_io[15] &= ~0x40;
+               break;
+               case 10: // CLEAR PA1 NEG EDGE DETECT
+               mcu_io[15] &= ~0x20;
+               break;
+               case 0x03: // PORTD
+               // bit7 = watchdog reset
+               if (!(mcu_io[3] & 0x80) && (v & 0x80)) {
+                       mcu_wd = 0;
+                       if (kb_mcu_log & 2) {
+                               write_log("KBMCU WD clear PC=%04x\n", mcu->GetPC());
+                       }
+               }
+               mcu_io[addr] = v;
+               break;
+               case 2: // PORTC
+               mcu_io[addr] = v;
+               break;
+               case 1: // PORTB
+               // bit7 = CAPS LED
+               if (mcu_caps_led != ((v & 0x80) != 0)) {
+                       mcu_caps_led = (v & 0x80) != 0;
+                       gui_data.capslock = mcu_caps_led;
+                       if (!fakemode) {
+                               gui_led(LED_CAPS, gui_data.capslock, -1);
+                       }
+                       if (kb_mcu_log & 1) {
+                               write_log("KBMCU: CAPS = %d\n", mcu_caps_led);
+                       }
+               }
+               mcu_io[addr] = v;
+               break;
+               case 0: // PORTA
+               {
+                       uae_u8 vv = v;
+                       if (kbhandshake || fakemode > 1) {
+                               vv &= ~1;
+                       }
+                       // PA0 (KDAT)
+                       if ((mcu_io[0] & 0x01) != (vv & 0x01)) {
+                               if (kb_mcu_log & 1) {
+                                       write_log("KDAT=%d KCLK=%d HS=%d CNT=%d PC=%04x\n", (vv & 0x01) != 0, (vv & 0x02) != 0, kbhandshake, kbdatacnt, mcu->GetPC());
+                               }
+                               if ((vv & 0x01)) {
+                                       mcu_io[15] |= 0x40;
+                                       keymcu_irq();
+                               }
+                       }
+                       // PA1 (KCLK)
+                       if ((mcu_io[0] & 0x02) != (vv & 0x02)) {
+                               if (kb_mcu_log & 1) {
+                                       write_log("KCLK=%d KDAT=%d HS=%d CNT=%d PC=%04x\n", (vv & 0x02) != 0, (vv & 0x01) != 0, kbhandshake, kbdatacnt, mcu->GetPC());
+                               }
+                               if (!(vv & 0x02)) {
+                                       mcu_io[15] |= 0x20;
+                                       keymcu_irq();
+                                       if (currprefs.cs_resetwarning) {
+                                               kclk_reset_start = get_cycles() + CYCLE_UNIT * 1500000;
+                                       }
+                               } else {
+                                       kclk_reset_start = 0;
+                               }
+                               if (vv & 0x02) {
+                                       get_kbit((vv & 0x01) != 0);
+                               }
+                       }
+                       mcu_io[addr] = v;
+               }
+               break;
+               default:
+               mcu_io[addr] = v;
+               break;
+       }
+}
+
+static void keymcu_res(void)
+{
+       mcu_io[3] = mcu_io[2] = mcu_io[1] = mcu_io[0] = 0xff;
+       mcu_wd = 0;
+       mcu_io[15] = 0;
+       kclk_reset_start = 0;
+       mcu->Reset();
+}
+
+static void dec_counter(void)
+{
+       mcu_io[7]--;
+       if (mcu_io[7] == 0xff) {
+               mcu_io[6]--;
+               if (mcu_io[6] == 0xff) {
+                       mcu_io[15] |= 0x80;
+                       mcu_io[7] = mcu_io[5];
+                       mcu_io[6] = mcu_io[4];
+                       keymcu_irq();
+               }
+       }
+       mcu_wd++;
+       // WD TIMER fault
+       if ((mcu_flags & 3) == 3) {
+               mcu_wd = 0;
+       }
+       // Watchdog is about 40ms
+       if (mcu_wd >= 60000) {
+               if (kb_mcu_log & 1) {
+                       write_log("KBMCU WD reset\n");
+               }
+               keymcu_res();
+       }
+}
+
+static uint8_t MemoryRead(uint16_t address)
+{
+       uint8_t v = 0;
+       if (address >= 0x80 && address < 0x90) {
+               v = mcu_io_read(address - 0x80);
+       } else if (address >= 0x800) {
+               address &= 0x7ff;
+               v = mcu_rom[address];
+               // ROM fault
+               if ((mcu_flags & 3) == 1) {
+                       if (address == 0) {
+                               v ^= 0x55;
+                       }
+               }
+       } else {
+               address &= 0x3f;
+               v = mcu_ram[address];
+       }
+       return v;
+}
+static void MemoryWrite(uint16_t address, uint8_t value)
+{
+       if (address >= 0x80 && address < 0x90) {
+               mcu_io_write(address - 0x80, value);
+       } else if (address < 0x800) {
+               address &= 0x3f;
+               // RAM fault
+               if ((mcu_flags & 3) == 2) {
+                       if (address == 0x3f) {
+                               value = 0xff;
+                       }
+               }
+               mcu_ram[address] = value;
+       }
+}
+
+static bool ismcu(void)
+{
+       return currprefs.keyboard_mode == KB_A500_6570 ||
+               currprefs.keyboard_mode == KB_A600_6570 ||
+               currprefs.keyboard_mode == KB_A1000_6570 ||
+               currprefs.keyboard_mode == KB_Ax000_6570;
+}
+
+void keymcu_reset(void)
+{
+       if (ismcu()) {
+               if (!state_loaded) {
+                       memset(mcu_ram, 0, sizeof mcu_ram);
+                       memset(mcu_io, 0, sizeof mcu_io);
+                       memset(matrix, 0, sizeof matrix);
+                       kbhandshake = false;
+               }
+               rom_loaded = false;
+
+               memset(mcu_rom, 0, sizeof mcu_rom);
+               struct zfile *zf = NULL;
+               const TCHAR *path = NULL;
+               struct boardromconfig *brc = get_device_rom_new(&currprefs, ROMTYPE_KBMCU, 0, NULL);
+               if (brc) {
+                       mcu_flags = brc->roms[0].device_settings;
+                       if (brc->roms[0].romfile[0]) {
+                               path = brc->roms[0].romfile;
+                               zf = zfile_fopen(path, _T("rb"));
+                       }
+               }
+               if (!zf) {
+                       int ids[] = { 321, -1 };
+                       struct romlist *rl = getromlistbyids(ids, NULL);
+                       if (rl) {
+                               path = rl->path;
+                               zf = read_rom(rl->rd);
+                       }
+               }
+               if (zf) {
+                       zfile_fread(mcu_rom, sizeof(mcu_rom), 1, zf);
+                       zfile_fclose(zf);
+                       write_log(_T("Loaded 6500-1/6570-036 KB MCU ROM '%s'\n"), path);
+                       rom_loaded = true;
+               } else {
+                       write_log(_T("Failed to load 6500-1/6570-036 KB MCU ROM\n"));
+               }
+               if (state_loaded) {
+                       uint16_t pc = mcu->GetPC();
+                       uint8_t status = mcu->GetP();
+                       mcu->Reset();
+                       mcu->SetPC(pc);
+                       mcu->SetP(status);
+                       gui_data.capslock = mcu_caps_led;
+               } else {
+                       keymcu_res();
+                       if (isrestore()) {
+                               // if loading statefile without keyboard state: execute MCU code until init phase is done
+                               fakemode = 1;
+                               uint64_t cycleCount = 0;
+                               for (int i = 0; i < 1000000; i++) {
+                                       mcu->Run(1, cycleCount, mos6502::CYCLE_COUNT);
+                                       if (fakemode > 1) {
+                                               fakemode--;
+                                               if (fakemode == 1) {
+                                                       mcu_io[15] |= 0x40;
+                                                       keymcu_irq();
+                                               }
+                                       }
+                               }
+                               fakemode = 0;
+                       }
+                       lastcycle = get_cycles();
+               }
+       }
+       state_loaded = false;
+}
+
+void keymcu_free(void)
+{
+       if (mcu) {
+               delete mcu;
+               mcu = NULL;
+       }
+       state_loaded = false;
+}
+
+static void ClockCycle(mos6502 *m)
+{
+       dec_counter();
+       keymcu_irq();
+}
+
+void keymcu_init(void)
+{
+       mcu = new mos6502(MemoryRead, MemoryWrite, ClockCycle);
+       mcu->Reset();
+       state_loaded = false;
+}
+
+bool keymcu_run(bool handshake)
+{
+       if (ismcu() && rom_loaded) {
+               bool hs = kbhandshake;
+               kbhandshake = handshake;
+               if (!handshake && hs) {
+                       if (kb_mcu_log & 1) {
+                               write_log("handshake off\n");
+                       }
+                       mcu_io[15] |= 0x40;
+                       keymcu_irq();
+                       kbdatacnt = 0;
+               } else if (handshake && !hs) {
+                       if (kb_mcu_log & 1) {
+                               write_log("handshake on\n");
+                       }
+                       kbdatacnt = 0;
+               }
+               evt_t c = get_cycles();
+               int m = CYCLE_UNIT * (currprefs.ntscmode ? 3579545 : 3546895) / 1500000;
+               int cycles = (c - lastcycle) / m;
+               if (cycles > 0) {
+                       uint64_t cycleCount = 0;
+                       if (mcu) {
+                               mcu->Run(cycles, cycleCount, mos6502::CYCLE_COUNT);
+                       }
+                       lastcycle += cycles * m;
+               }
+               if (kclk_reset_start > 0 && kclk_reset_start < c) {
+                       write_log("Keyboard MCU generated system reset\n");
+                       inputdevice_do_kb_reset();
+               }
+       }
+       while (keys_available()) {
+               uae_u8 kbcode = get_next_key();
+               key_to_matrix(kbcode);
+       }
+       return kbdatacnt >= 3;
+}
+
+#ifdef SAVESTATE
+
+uae_u8 *save_kbmcu(size_t *len, uae_u8 *dstptr)
+{
+       if (!mcu || !ismcu()) {
+               return NULL;
+       }
+
+       uae_u8 *dstbak, *dst;
+       if (dstptr) {
+               dstbak = dst = dstptr;
+       } else {
+               dstbak = dst = xmalloc(uae_u8, 1000 + sizeof(mcu_ram) + sizeof(mcu_io) + sizeof(matrix));
+       }
+
+       save_u32(1);
+       save_u32((kbhandshake ? 1 : 0) | (mcu_caps_led ? 2 : 0));
+       save_u32(mcu_wd);
+       save_u8(kbdata);
+       save_u8(kbdatacnt);
+       save_u64(lastcycle);
+       save_u64(kclk_reset_start);
+       for (int i = 0; i < sizeof(mcu_ram); i++) {
+               save_u8(mcu_ram[i]);
+       }
+       for (int i = 0; i < sizeof(mcu_io); i++) {
+               save_u8(mcu_io[i]);
+       }
+       for (int i = 0; i < sizeof(matrix); i++) {
+               save_u8(matrix[i]);
+       }
+       save_u8(mcu->GetA());
+       save_u8(mcu->GetX());
+       save_u8(mcu->GetY());
+       save_u8(mcu->GetP());
+       save_u8(mcu->GetS());
+       save_u16(mcu->GetPC());
+
+       *len = dst - dstbak;
+       return dstbak;
+}
+
+uae_u8* restore_kbmcu(uae_u8 *src)
+{
+       if (!mcu || !ismcu()) {
+               return NULL;
+       }
+
+       int v = restore_u32();
+       if (!(v & 1)) {
+               return NULL;
+       }
+       uae_u32 flags = restore_u32();
+       kbhandshake = flags != 0;
+       mcu_caps_led = flags & 2;
+       mcu_wd = restore_u32();
+       kbdata = restore_u8();
+       kbdatacnt = restore_u8();
+       lastcycle = restore_u64();
+       kclk_reset_start = restore_u64();
+       for (int i = 0; i < sizeof(mcu_ram); i++) {
+               mcu_ram[i] = restore_u8();
+       }
+       for (int i = 0; i < sizeof(mcu_io); i++) {
+               mcu_io[i] = restore_u8();
+       }
+       for (int i = 0; i < sizeof(matrix); i++) {
+               matrix[i] = restore_u8();
+       }
+       mcu->SetResetA(restore_u8());
+       mcu->SetResetX(restore_u8());
+       mcu->SetResetY(restore_u8());
+       mcu->SetResetP(restore_u8());
+       mcu->SetResetS(restore_u8());
+       mcu->SetPC(restore_u16());
+
+       state_loaded = true;
+
+       return src;
+}
+
+#endif
\ No newline at end of file
diff --git a/kbmcu/keyboard_mcu_6805.cpp b/kbmcu/keyboard_mcu_6805.cpp
new file mode 100644 (file)
index 0000000..83fa92d
--- /dev/null
@@ -0,0 +1,750 @@
+/*
+* UAE - The Un*x Amiga Emulator
+*
+* Keyboard MCU emulation (A1200 M68HC05)
+*
+* Copyright 2024 Toni Wilen
+*/
+
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include "options.h"
+#include "rommgr.h"
+#include "zfile.h"
+#include "events.h"
+#include "keyboard_mcu.h"
+#include "keybuf.h"
+#include "inputdevice.h"
+#include "gui.h"
+#include "savestate.h"
+#include "m6805/m68emu.h"
+
+static struct M68_CTX ctx;
+
+extern int kb_mcu_log;
+
+static uae_u8 mcu_rom[8192];
+static uae_u8 mcu_ram[320];
+static uae_u8 mcu_io[32];
+static uae_u8 mcu_option;
+static uae_u8 matrix[128];
+static uae_u8 mcu_status_read;
+static uae_u8 mcu_dec_ps;
+static bool state_loaded;
+static bool mcu_caps_led;
+static bool rom_loaded;
+static uae_u8 mcu_timer_latch_low;
+static bool mcu_timer_latched;
+static bool mcu_input_capture_latch, mcu_output_compare_latch;
+static evt_t lastcycle;
+static bool kbhandshake;
+static bool mcu_ram0, mcu_ram1;
+static int fakemode;
+static uae_u8 kbdata, kbdatacnt;
+static uae_s32 mcu_wd, mcu_res;
+static int mcu_flags;
+
+// 15*6 matrix
+static const uae_u8 matrixkeys[] = {
+       0x45,0x00,0x42,0x62,0x30,0x5d,
+       0x5a,0x01,0x10,0x20,0x31,0x5e,
+       0x50,0x02,0x11,0x21,0x32,0x3f,
+       0x51,0x03,0x12,0x22,0x33,0x2f,
+       0x52,0x04,0x13,0x23,0x34,0x1f,
+       0x53,0x05,0x14,0x24,0x35,0x3c,
+       0x54,0x06,0x15,0x25,0x36,0x3e,
+       0x5b,0x07,0x16,0x26,0x37,0x2e,
+       0x55,0x08,0x17,0x27,0x38,0x1e,
+       0x5c,0x09,0x18,0x28,0x39,0x43,
+       0x56,0x0a,0x19,0x29,0x3a,0x3d,
+       0x57,0x0b,0x1a,0x2a,0x3b,0x2d,
+       0x58,0x0c,0x1b,0x2b,0x40,0x1d,
+       0x59,0x0d,0x44,0x46,0x41,0x0f,
+       0x5f,0x4c,0x4f,0x4e,0x4d,0x4a
+};
+
+static void key_to_matrix(uae_u8 code)
+{
+       uae_u8 c = code >> 1;
+       if (code & 1) {
+               matrix[c] = 0;
+               if (kb_mcu_log & 1) {
+                       write_log("release %02x\n", c);
+               }
+       } else {
+               matrix[c] = 1;
+               if (kb_mcu_log & 1) {
+                       write_log("press %02x\n", c);
+               }
+       }
+}
+
+// This should be in CIA emulation but it is simpler to have it here.
+static void get_kbit(bool v)
+{
+       if (kbhandshake && !fakemode) {
+               return;
+       }
+       kbdata <<= 1;
+       kbdata |= (v ? 1 : 0);
+       kbdatacnt++;
+       if (kbdatacnt >= 8) {
+               kbdatacnt = 0;
+               if (kb_mcu_log & 1) {
+                       uae_u8 code = (kbdata >> 1) | (kbdata << 7);
+                       write_log("got keycode %02x\n", code ^ 0xff);
+               }
+               if (fakemode) {
+                       fakemode = 10000;
+               } else {
+                       cia_keyreq(kbdata);
+               }
+       }
+}
+
+static void keymcu_irq(void)
+{
+       if (mcu_io[0x12] & mcu_io[0x13] & (0x80 | 0x40 | 0x20)) {
+               if (kb_mcu_log & 2) {
+                       write_log("%04x %04x IRQ %02x\n", mcu_wd, ctx.reg_pc, mcu_io[0x12] & mcu_io[0x13]);
+               }
+               m68_set_interrupt_line(&ctx, M68_INT_TIMER1);
+       }
+}
+
+static void check_input_capture(uae_u8 v)
+{
+       if ((v & 0x01) == ((mcu_io[0x12] >> 1) & 1) && !mcu_input_capture_latch) {
+               mcu_io[0x14] = mcu_io[0x18];
+               mcu_io[0x15] = mcu_io[0x19];
+               mcu_io[0x13] |= 1 << 7; // INPUT CAPTURE
+               keymcu_irq();
+       }
+}
+
+static uae_u8 mcu_io_read(int addr)
+{
+       uint8_t v = mcu_io[addr];
+
+       if (kb_mcu_log & 2) {
+               write_log("%04x IOR %04x = %02x\n", ctx.reg_pc, addr, v);
+       }
+
+       switch (addr)
+       {
+               case 0x13: // Read STATUS
+               mcu_status_read |= v;
+               break;
+               case 0x14: // INPUT CAPTURE HIGH
+               mcu_input_capture_latch = true;
+               break;
+               case 0x15: // Reset INPUT CAPTURE LOW
+               mcu_input_capture_latch = false;
+               if (mcu_status_read & (1 << 7)) {
+                       mcu_status_read &= ~(1 << 7);
+                       mcu_io[0x13] &= ~(1 << 7);
+               }
+               break;
+               case 0x17: // Reset OUTPUT COMPARE LOW
+               if (mcu_status_read & (1 << 6)) {
+                       mcu_status_read &= ~(1 << 6);
+                       mcu_io[0x13] &= ~(1 << 6);
+               }
+               break;
+               case 0x18: // TIMER HIGH
+               mcu_timer_latch_low = mcu_io[0x19];
+               mcu_timer_latched = true;
+               break;
+               case 0x19: // Reset TIMER LOW
+               if (mcu_timer_latched) {
+                       mcu_timer_latched = false;
+                       v = mcu_timer_latch_low;
+               }
+               if (mcu_status_read & (1 << 5)) {
+                       mcu_status_read &= ~(1 << 5);
+                       mcu_io[0x13] &= ~(1 << 5);
+               }
+               break;
+               case 0x1a: // ALT TIMER HIGH
+               mcu_timer_latch_low = mcu_io[0x19];
+               mcu_timer_latched = true;
+               v = mcu_io[0x18];
+               break;
+               case 0x1b: // ALT TIMER LOW
+               if (mcu_timer_latched) {
+                       mcu_timer_latched = false;
+                       v = mcu_timer_latch_low;
+               } else {
+                       v = mcu_io[0x19];
+               }
+               break;
+
+               case 1: // PORTB: KDAT=0,KCLK=1,2-7 = Matrix column read
+               {
+                       v &= ~3;
+                       if (kbhandshake || fakemode > 1) {
+                               v |= 2;
+                       } else {
+                               v |= 3;
+                       }
+                       // Pullups in key lines
+                       v |= 0x80 | 0x40 | 0x20 | 0x10 | 0x08 | 0x04;
+
+                       // row is PORTA + PORTC
+                       int row = mcu_io[0] | ((mcu_io[2] & 0x3f) << 8) | ((mcu_io[2] & 0x80) << 7);
+
+                       if (!currprefs.keyboard_nkro) {
+                               // matrix without diodes
+                               for (int i = 0; i < 15; i++) {
+                                       if (!(row & (1 << i))) {
+                                               const uae_u8 *mr = matrixkeys + i * 6;
+                                               for (int j = 0; j < 6; j++) {
+                                                       if (matrix[mr[j]]) {
+                                                               for(int ii = 0; ii < 15; ii++) {
+                                                                       if ((row & (1 << ii)) && ii != i) {
+                                                                               const uae_u8 *mr2 = matrixkeys + ii * 6;
+                                                                               if (matrix[mr2[j]]) {
+                                                                                       row &= ~(1 << ii);
+                                                                                       i = -1;
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       for (int i = 0; i < 15; i++) {
+                               if (!(row & (1 << i))) {
+                                       const uae_u8 *mr = matrixkeys + i * 6;
+                                       for (int j = 0; j < 6; j++) {
+                                               if (matrix[mr[j]]) {
+                                                       v &= ~(4 << j);
+                                               }
+                                       }
+                               }
+                       }
+
+                       v &= ~mcu_io[addr + 4];
+                       v |= mcu_io[addr] & mcu_io[addr + 4];
+               }
+               break;
+               case 3: // PORTD: RSHIFT=0,RALT=1,RAMIGA=2,CTRL=3,LSHIFT=4,LALT=5,LAMIGA=7
+               {
+                       v |= ~0x40; // Pullups in all key lines
+                       if (matrix[0x61]) {
+                               v &= ~0x01;
+                       }
+                       if (matrix[0x65]) {
+                               v &= ~0x02;
+                       }
+                       if (matrix[0x67]) {
+                               v &= ~0x04;
+                       }
+                       if (matrix[0x63]) {
+                               v &= ~0x08;
+                       }
+                       if (matrix[0x60]) {
+                               v &= ~0x10;
+                       }
+                       if (matrix[0x64]) {
+                               v &= ~0x20;
+                       }
+                       if (matrix[0x66]) {
+                               v &= ~0x80;
+                       }
+               }
+               break;
+               case 0: // PORTA
+               case 2: // PORTC
+               v &= ~mcu_io[addr + 4];
+               v |= mcu_io[addr] & mcu_io[addr + 4];
+               break;
+       }
+
+       return v;
+}
+
+static void check_porta(uint16_t addr, uae_u8 old, uae_u8 vv)
+{
+       // PA0 (KDAT)
+       if ((old & 0x01) != (vv & 0x01)) {
+               if (kb_mcu_log & 1) {
+                       write_log("KDAT=%d KCLK=%d HS=%d CNT=%d PC=%04x\n", (vv & 0x01) != 0, (vv & 0x02) != 0, kbhandshake, kbdatacnt, ctx.reg_pc);
+               }
+               check_input_capture(vv);
+       }
+       // PA1 (KCLK)
+       if ((old & 0x02) != (vv & 0x02)) {
+               if (kb_mcu_log & 1) {
+                       write_log("KCLK=%d KDAT=%d HS=%d CNT=%d PC=%04x\n", (vv & 0x02) != 0, (vv & 0x01) != 0, kbhandshake, kbdatacnt, ctx.reg_pc);
+               }
+               if ((vv & 0x02) && (mcu_io[addr + 4] & 2)) {
+                       get_kbit((vv & 0x01) != 0);
+               }
+       }
+}
+
+static void mcu_io_write(uint16_t addr, uae_u8 v)
+{
+
+       if (kb_mcu_log & 2) {
+               write_log("%04x IOW %04x = %02x\n", ctx.reg_pc, addr, v);
+       }
+
+       switch (addr)
+       {
+               case 0x16: // OUTPUT COMPARE HIGH
+               mcu_output_compare_latch = true;
+               mcu_io[addr] = v;
+               break;
+               case 0x17: // Reset OUTPUT COMPARE LOW
+               mcu_output_compare_latch = false;
+               if (mcu_status_read & (1 << 6)) {
+                       mcu_status_read &= ~(1 << 6);
+                       mcu_io[0x13] &= ~(1 << 6);
+               }
+               mcu_io[addr] = v;
+               break;
+               case 0x12: // TIMER CONTROL
+               v &= (0x80 | 0x40 | 0x20 | 0x02 | 0x01);
+               mcu_io[addr] = v;
+               keymcu_irq();
+               break;
+
+               case 3: // PORTD
+               mcu_io[addr] = v;
+               break;
+               case 2: // PORTC
+               // bit7 = CAPS LED
+               if (mcu_caps_led != ((v & 0x80) != 0)) {
+                       mcu_caps_led = (v & 0x80) != 0;
+                       gui_data.capslock = mcu_caps_led == 0;
+                       if (!fakemode) {
+                               gui_led(LED_CAPS, gui_data.capslock, -1);
+                       }
+                       if (kb_mcu_log & 1) {
+                               write_log("KBMCU: CAPS = %d\n", mcu_caps_led);
+                       }
+               }
+               mcu_io[addr] = v;
+               break;
+               case 0: // PORTA
+               mcu_io[addr] = v;
+               break;
+               case 1: // PORTB
+               {
+                       uae_u8 vv = v;
+                       if (kbhandshake || fakemode > 1) {
+                               vv &= ~1;
+                       }
+                       // if input mode: external pullup -> 1
+                       if (!(mcu_io[addr + 4] & 1) && !(vv & 1)) {
+                               vv |= 1;
+                       }
+                       if (!(mcu_io[addr + 4] & 2) && !(vv & 2)) {
+                               vv |= 2;
+                       }
+                       check_porta(addr, mcu_io[addr], vv);
+                       mcu_io[addr] = v;
+               }
+               break;
+               case 5: // PORTB data direction
+               {
+                       uae_u8 dv = mcu_io[addr - 4];
+                       uae_u8 olddc = mcu_io[addr];
+                       // if input mode: external pullup -> 1
+                       if (!(v & 1) && !(dv & 1)) {
+                               dv |= 1;
+                       }
+                       if (!(v & 2) && !(dv & 2)) {
+                               dv |= 2;
+                       }
+                       check_porta(addr, mcu_io[addr - 4], dv);
+                       mcu_io[addr] = v;
+               }
+               break;
+               // read only registers
+               case 0x13:
+               case 0x14:
+               case 0x15:
+               case 0x18:
+               case 0x19:
+               case 0x1a:
+               case 0x1b:
+               break;
+               default:
+               mcu_io[addr] = v;
+               break;
+       }}
+
+static uint8_t readfunc(struct M68_CTX* ctx, const uint16_t addr)
+{
+       uint8_t v = 0;
+       uint16_t a = addr & 0x1fff;
+       if (a < 0x20) {
+               v = mcu_io_read(addr);
+       } else if (a >= 0x20 && a < 0x30 && mcu_ram0) {
+               v = 0xff;
+       } else if (a >= 0x50 && a < 0x100) {
+               v = mcu_ram[a - 0x30];
+               // RAM fault
+               if ((mcu_flags & 3) == 2) {
+                       if (a == 0x80) {
+                               v ^= 0x55;
+                       }
+               }
+       } else if (a >= 0x30 && a < 0x50 && mcu_ram0) {
+               v = mcu_ram[a - 0x30];
+       } else if (a >= 0x20 && a < 0x30 && !mcu_ram0) {
+               v = mcu_rom[a];
+       } else if (a >= 0x40 && a < 0x50) {
+               v = mcu_rom[a];
+       } else if (a >= 0x100 && a < 0x160 && mcu_ram1) {
+               v = mcu_ram[a - 0x30];
+       } else if (a == 0x1fdf) {
+               v = mcu_option;
+       } else if (a >= 0x100) {
+               v = mcu_rom[a];
+               // ROM fault
+               if ((mcu_flags & 3) == 1) {
+                       if (a == 0x100) {
+                               v ^= 0x55;
+                       }
+               }
+       }
+       return v;
+}
+
+void writefunc(struct M68_CTX *ctx, const uint16_t addr, const uint8_t data)
+{
+       uint16_t a = addr & 0x1fff;
+       if (a < 0x20) {
+               mcu_io_write(addr, data);
+       } else if (a == 0x1fdf) {
+               mcu_option = data;
+               mcu_ram0 = (data & 0x80) != 0;
+               mcu_ram1 = (data & 0x40) != 0;
+       } else if (a == 0x1ff0) {
+               if (!(data & 1)) {
+                       mcu_wd = 0;
+               }
+       } else if (a >= 0x30 && a < 0x50 && mcu_ram0) {
+               mcu_ram[a - 0x30] = data;
+       } else if (a >= 0x50 && a < 0x100) {
+               mcu_ram[a - 0x30] = data;
+       } else if (a >= 0x100 && a < 0x160 && mcu_ram1) {
+               mcu_ram[a - 0x30] = data;
+       }
+}
+
+static void keymcu_res(void)
+{
+       m68_reset(&ctx);
+       ctx.irq = (mcu_flags & 4) == 0;
+       mcu_wd = 0;
+       mcu_res = 0;
+       mcu_input_capture_latch = false;
+       mcu_output_compare_latch = false;
+       mcu_io[0] = 0;
+       mcu_io[1] = 0;
+       mcu_io[2] = 0;
+       mcu_io[3] = 0;
+       mcu_io[4] = 0;
+       mcu_io[5] = 0;
+       mcu_io[6] = 0;
+       mcu_option = (0x02 | 0x08);
+       mcu_io[0x0a] = 0;
+       mcu_io[0x0b] = 0;
+       mcu_io[0x12] = 0;
+       mcu_io[0x13] = 0;
+       mcu_io[0x18] = 0xff;
+       mcu_io[0x19] = 0xfc;
+}
+
+static void dec_counter(void)
+{
+       mcu_dec_ps++;
+       if ((mcu_dec_ps & 3) == 0) {
+               mcu_wd++;
+               // WD TIMER fault
+               if ((mcu_flags & 3) == 3) {
+                       mcu_wd = 0;
+               }
+               if (mcu_wd >= 32768) {
+                       bool res = mcu_res < 0;
+                       keymcu_res();
+                       if (kb_mcu_log & 1) {
+                               write_log("KBMCU WD reset\n");
+                       }
+                       if (res) {
+                               mcu_res = 1;
+                       }
+                       return;
+               }
+               if (mcu_res > 0) {
+                       mcu_res++;
+                       if (mcu_res >= 128) {
+                               write_log("Keyboard MCU generated system reset\n");
+                               inputdevice_do_kb_reset();
+                               keymcu_res();
+                               return;
+                       }
+               }
+               mcu_io[0x19]++;
+               if (mcu_io[0x19] == 0x00) {
+                       mcu_io[0x18]++;
+                       if (mcu_io[0x18] == 0x00) {
+                               mcu_io[0x13] |= 1 << 5; // TIMER OVERFLOW
+                               keymcu_irq();
+                       }
+               }
+               if (mcu_io[0x19] == mcu_io[0x17] && mcu_io[0x18] == mcu_io[0x16] && !mcu_output_compare_latch) {
+                       mcu_io[0x13] |= 1 << 6; // OUTPUT COMPARE
+                       keymcu_irq();
+                       if ((mcu_io[0x12] & 1) == 0) {
+                               if (mcu_res < 0) {
+                                       // KB_RESET active
+                                       mcu_res = 1;
+                                       if (kb_mcu_log & 1) {
+                                               write_log("KBMCU KB_RESET ACTIVE\n");
+                                       }       
+                               }
+                       } else {
+                               if (mcu_res > 0) {
+                                       if (kb_mcu_log & 1) {
+                                               write_log("KBMCU KB_RESET INACTIVE\n");
+                                       }       
+                               }
+                               mcu_res = -1;
+                       }
+               }
+       }
+}
+
+void keymcu2_reset(void)
+{
+       if (currprefs.keyboard_mode == KB_A1200_6805) {
+               if (!state_loaded) {
+                       memset(mcu_ram, 0, sizeof mcu_ram);
+                       memset(mcu_io, 0, sizeof mcu_io);
+                       memset(matrix, 0, sizeof matrix);
+                       kbhandshake = false;
+                       mcu_option = 0;
+                       mcu_wd = 0;
+                       mcu_res = 0;
+               }
+               rom_loaded = false;
+
+               memset(mcu_rom, 0, sizeof mcu_rom);
+               struct zfile *zf = NULL;
+               const TCHAR *path = NULL;
+               struct boardromconfig *brc = get_device_rom_new(&currprefs, ROMTYPE_KBMCU, 0, NULL);
+               if (brc) {
+                       mcu_flags = brc->roms[0].device_settings;
+                       if (brc->roms[0].romfile[0]) {
+                               path = brc->roms[0].romfile;
+                               zf = zfile_fopen(path, _T("rb"));
+                       }
+               }
+               if (!zf) {
+                       int ids[] = { 322, -1 };
+                       struct romlist *rl = getromlistbyids(ids, NULL);
+                       if (rl) {
+                               path = rl->path;
+                               zf = read_rom(rl->rd);
+                       }
+               }
+               if (zf) {
+                       zfile_fread(mcu_rom, sizeof(mcu_rom), 1, zf);
+                       zfile_fclose(zf);
+                       write_log(_T("Loaded 6805 KB MCU ROM '%s'\n"), path);
+                       rom_loaded = true;
+               } else {
+                       write_log(_T("Failed to load 6805 KB MCU ROM\n"));
+               }
+               if (state_loaded) {
+                       gui_data.capslock = mcu_caps_led == 0;
+               } else {
+                       keymcu_res();
+                       if (isrestore()) {
+                               // if loading statefile without keyboard state: execute MCU code until init phase is done
+                               fakemode = 1;
+                               uint64_t cycleCount = 0;
+                               for (int i = 0; i < 1000000; i++) {
+                                       uint64_t cyc = m68_exec_cycle(&ctx);
+                                       while (cyc > 0) {
+                                               dec_counter();
+                                               cyc--;
+                                       }
+                                       if (fakemode > 1) {
+                                               fakemode--;
+                                               if (fakemode == 1) {
+                                                       check_input_capture(0);
+                                                       check_input_capture(1);
+                                               }
+                                       }
+                               }
+                               fakemode = 0;
+                       }
+                       lastcycle = get_cycles();
+               }
+       }
+       state_loaded = false;
+}
+
+void keymcu2_free(void)
+{
+       state_loaded = false;
+}
+
+void keymcu2_init(void)
+{
+       ctx.read_mem  = &readfunc;
+       ctx.write_mem = &writefunc;
+       ctx.opdecode  = NULL;
+       m68_init(&ctx, M68_CPU_HC05C4);
+       keymcu_reset();
+       state_loaded = false;
+}
+
+bool keymcu2_run(bool handshake)
+{
+       if (currprefs.keyboard_mode == KB_A1200_6805 && rom_loaded) {
+               bool hs = kbhandshake;
+               kbhandshake = handshake;
+               if (!handshake && hs) {
+                       if (kb_mcu_log & 1) {
+                               write_log("handshake off\n");
+                       }
+                       check_input_capture(1);
+                       kbdatacnt = 0;
+               } else if (handshake && !hs) {
+                       if (kb_mcu_log & 1) {
+                               write_log("handshake on\n");
+                       }
+                       check_input_capture(0);
+                       kbdatacnt = 0;
+               }
+               evt_t c = get_cycles();
+               int m = CYCLE_UNIT * (currprefs.ntscmode ? 3579545 : 3546895) / 1500000;
+               int cycles = (c - lastcycle) / m;
+               lastcycle += cycles * m;
+               while (cycles > 0) {
+                       uint64_t cyc = m68_exec_cycle(&ctx);
+                       cycles -= cyc;
+                       while (cyc > 0) {
+                               dec_counter();
+                               cyc--;
+                       }
+               }
+       }
+       while (keys_available()) {
+               uae_u8 kbcode = get_next_key();
+               key_to_matrix(kbcode);
+       }
+       return kbdatacnt >= 3;
+}
+
+#ifdef SAVESTATE
+
+uae_u8 *save_kbmcu2(size_t *len, uae_u8 *dstptr)
+{
+       if (currprefs.keyboard_mode != KB_A1200_6805) {
+               return NULL;
+       }
+
+       uae_u8 *dstbak, *dst;
+       if (dstptr) {
+               dstbak = dst = dstptr;
+       } else {
+               dstbak = dst = xmalloc(uae_u8, 1000 + sizeof(mcu_ram) + sizeof(mcu_io) + sizeof(matrix));
+       }
+
+       save_u32(1);
+       save_u32((kbhandshake ? 1 : 0) | (ctx.is_stopped ? 2 : 0) | (ctx.is_waiting ? 4 : 0) |
+               (mcu_timer_latched ? 8 : 0) | (mcu_input_capture_latch ? 0x10 : 0) | (mcu_output_compare_latch ? 0x20 : 0) |
+               (mcu_caps_led ? 0x40 : 0));
+       save_u32(mcu_wd);
+       save_u8(kbdata);
+       save_u8(kbdatacnt);
+       save_u64(lastcycle);
+       save_u32(mcu_res);
+       save_u8(mcu_option);
+       save_u8(mcu_status_read);
+       save_u8(mcu_dec_ps);
+       save_u8(mcu_timer_latch_low);
+       for (int i = 0; i < sizeof(mcu_ram); i++) {
+               save_u8(mcu_ram[i]);
+       }
+       for (int i = 0; i < sizeof(mcu_io); i++) {
+               save_u8(mcu_io[i]);
+       }
+       for (int i = 0; i < sizeof(matrix); i++) {
+               save_u8(matrix[i]);
+       }
+       save_u8(ctx.reg_acc);
+       save_u8(ctx.reg_x);
+       save_u16(ctx.reg_pc);
+       save_u16(ctx.pc_next);
+       save_u16(ctx.reg_sp);
+       save_u8(ctx.reg_ccr);
+       save_u8(ctx.pending_interrupts);
+
+       *len = dst - dstbak;
+       return dstbak;
+}
+
+uae_u8* restore_kbmcu2(uae_u8 *src)
+{
+       if (currprefs.keyboard_mode != KB_A1200_6805) {
+               return NULL;
+       }
+
+       int v = restore_u32();
+       if (!(v & 1)) {
+               return NULL;
+       }
+
+       uae_u32 flags = restore_u32();
+       kbhandshake = flags != 0;
+       ctx.is_stopped = (flags & 2) != 0;
+       ctx.is_waiting = (flags & 4) != 0;
+       mcu_timer_latched = (flags & 8) != 0;
+       mcu_input_capture_latch = (flags & 0x10) != 0;
+       mcu_output_compare_latch = (flags & 0x20) != 0;
+       mcu_caps_led = (flags & 0x40) != 0;
+       mcu_wd = restore_u32();
+       kbdata = restore_u8();
+       kbdatacnt = restore_u8();
+       lastcycle = restore_u64();
+       mcu_res = restore_u32();
+       mcu_option = restore_u8();
+       mcu_status_read = restore_u8();
+       mcu_dec_ps = restore_u8();
+       mcu_timer_latch_low = restore_u8();
+       for (int i = 0; i < sizeof(mcu_ram); i++) {
+               mcu_ram[i] = restore_u8();
+       }
+       for (int i = 0; i < sizeof(mcu_io); i++) {
+               mcu_io[i] = restore_u8();
+       }
+       for (int i = 0; i < sizeof(matrix); i++) {
+               matrix[i] = restore_u8();
+       }
+       ctx.reg_acc = restore_u8();
+       ctx.reg_x = restore_u8();
+       ctx.reg_pc = restore_u16();
+       ctx.pc_next = restore_u16();
+       ctx.reg_sp = restore_u16();
+       ctx.reg_ccr = restore_u8();
+       ctx.pending_interrupts = restore_u8();
+
+       state_loaded = true;
+
+       return src;
+}
+
+#endif
\ No newline at end of file
diff --git a/kbmcu/keyboard_mcu_d8039hlc .cpp b/kbmcu/keyboard_mcu_d8039hlc .cpp
new file mode 100644 (file)
index 0000000..90f6d01
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+* UAE - The Un*x Amiga Emulator
+*
+* Keyboard MCU emulation (D8039HLC, A2000 Cherry)
+*
+* Copyright 2024 Toni Wilen
+*/
+
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include "options.h"
+#include "rommgr.h"
+#include "zfile.h"
+#include "events.h"
+#include "keyboard_mcu.h"
+#include "keybuf.h"
+#include "inputdevice.h"
+#include "gui.h"
+#include "savestate.h"
+#include "8048/co8048.h"
+
+extern int kb_mcu_log;
+
+static uae_u8 mcu_rom[2048];
+static uae_u8 mcu_io[2];
+static uae_u8 matrix[128];
+static ATCoProc8048 *mcu;
+static bool state_loaded;
+static bool mcu_caps_led;
+static bool rom_loaded;
+static bool kbhandshake;
+static uae_u8 kbdata, kbdatacnt;
+static evt_t lastcycle;
+static evt_t kclk_reset_start;
+static int fakemode;
+static uae_u32 mcu_wd;
+static bool mcu_t0;
+static int mcu_flags;
+
+// 13x8 matrix
+static const uae_u8 matrixkeys[] = {
+       0x55,0x59,0x0A,0x06,0x36,0x25,0x19,0x15,
+       0x52,0x41,0xFF,0x05,0x35,0x24,0x44,0x14,
+       0x51,0xFF,0xFF,0x02,0x32,0x21,0xFF,0x11,
+       0x45,0xFF,0x30,0x01,0x31,0x20,0x62,0x10,
+       0x54,0xFF,0x40,0x03,0x33,0x22,0x65,0x12,
+       0x5D,0x5C,0x3F,0x4A,0x43,0x1F,0x5E,0x2F,
+       0x5A,0x5B,0x3E,0x3D,0x1E,0x1D,0x2E,0x2D,
+       0x50,0xFF,0x64,0x00,0x60,0x63,0x66,0x42,
+       0x5F,0x46,0x4C,0x4E,0x3C,0x0F,0x4F,0x4D,
+       0x57,0xFF,0x3A,0x07,0x37,0x26,0x29,0x16,
+       0x58,0x0B,0x1A,0x09,0x39,0x28,0x2A,0x18,
+       0x56,0x0C,0x1B,0x08,0x38,0x27,0x2B,0x17,
+       0x53,0x0D,0x67,0x04,0x34,0x23,0x61,0x13
+};
+
+static void key_to_matrix(uae_u8 code)
+{
+       uae_u8 c = code >> 1;
+       if (code & 1) {
+               matrix[c] = 0;
+               if (kb_mcu_log & 1) {
+                       write_log("release %02x\n", c);
+               }
+       } else {
+               matrix[c] = 1;
+               if (kb_mcu_log & 1) {
+                       write_log("press %02x\n", c);
+               }
+       }
+}
+
+// This should be in CIA emulation but it is simpler to have it here.
+static void get_kbit(bool v)
+{
+       if (kbhandshake && !fakemode) {
+               return;
+       }
+       kbdata <<= 1;
+       kbdata |= (v ? 1 : 0);
+       kbdatacnt++;
+       if (kbdatacnt >= 8) {
+               kbdatacnt = 0;
+               if (kb_mcu_log & 1) {
+                       uae_u8 code = (kbdata >> 1) | (kbdata << 7);
+                       write_log("got keycode %02x\n", code ^ 0xff);
+               }
+               if (fakemode) {
+                       mcu->AssertIrq();
+                       fakemode = 10000;
+               } else {
+                       cia_keyreq(kbdata);
+               }
+       }
+}
+
+void mpFnWritePort(int p, uint8 v)
+{
+       if (kb_mcu_log & 2) {
+               write_log("%04x write port %d = %02x\n", mcu->GetPC(), p, v);
+       }
+
+       uae_u8 o = mcu_io[p];
+       if (p == 1) {
+
+               if (mcu_caps_led != ((v & 0x20) != 0)) {
+                       mcu_caps_led = (v & 0x20) != 0;
+                       gui_data.capslock = mcu_caps_led == 0;
+                       if (!fakemode) {
+                               gui_led(LED_CAPS, gui_data.capslock, -1);
+                       }
+
+                       if (kb_mcu_log & 1) {
+                               write_log("KBMCU: CAPS = %d\n", mcu_caps_led);
+                       }
+               }
+
+               if ((o ^ v) & 0x80) {
+                       if (v & 0x80) {
+                               get_kbit(((o & 0x40) ? 1 : 0) != 0);
+                               kclk_reset_start = 0;
+                       } else {
+                               if (currprefs.cs_resetwarning) {
+                                       kclk_reset_start = get_cycles() + CYCLE_UNIT * 1800000;
+                               }
+                       }
+
+                       if (kb_mcu_log & 1) {
+                               write_log("KCLK = %d\n", (v & 0x80) != 0);
+                       }
+               }
+               if ((o ^ v) & 0x40) {
+                       if (kb_mcu_log & 1) {
+                               write_log("KDAT = %d\n", (v & 0x40) != 0);
+                       }
+               }
+       }
+
+       //write_log("%04x write port %02x %02x\n", mcu->GetPC(), mcu_io[0], mcu_io[1]);
+
+       mcu_io[p] = v;
+}
+void mpFnWriteBus(uint8 v)
+{
+       write_log("%04x write bus = %02x\n", mcu->GetPC(), v);
+}
+uint8 mpFnReadBus(void)
+{
+       uint8 v = 0;
+
+       uint8 io2 = mcu_io[1];
+       int row = mcu_io[0];
+       if (io2 & 1) {
+               row |= 0x1000;
+       }
+       if (io2 & 2) {
+               row |= 0x0800;
+       }
+       if (io2 & 4) {
+               row |= 0x0400;
+       }
+       if (io2 & 8) {
+               row |= 0x0200;
+       }
+       if (io2 & 0x10) {
+               row |= 0x0100;
+       }
+
+       for (int i = 0; i < 13; i++) {
+               if ((row & (1 << i))) {
+                       const uae_u8 *mr = matrixkeys + i * 8;
+                       for (int j = 0; j < 8; j++) {
+                               if (mr[j] < 0x80) {
+                                       if (matrix[mr[j]]) {
+                                               v |= 1 << j;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if (kb_mcu_log & 2) {
+               write_log("%04x read bus = %02x\n", mcu->GetPC(), v);
+       }
+
+       return v;
+}
+uint8 mpFnReadPort(int p, uint8 vv)
+{
+       uint8 v = mcu_io[p];
+
+       if (kb_mcu_log & 2) {
+               write_log("%04x read port %d = %02x\n", mcu->GetPC(), p, v);
+       }
+       return v;
+}
+uint8 mpFnReadT0(void)
+{
+       uint8 v = 0;
+       if (kb_mcu_log & 2) {
+               write_log("%04x read T0 = %02x\n", mcu->GetPC(), v);
+       }
+       return v;
+}
+uint8 mpFnReadT1(void)
+{
+       uint8 v = 0;
+       if (kb_mcu_log & 2) {
+               write_log("%04x read T1 = %02x\n", mcu->GetPC(), v);
+       }
+       return v;
+}
+
+static void keymcu_res(void)
+{
+       mcu->ColdReset();
+       mcu->NegateIrq();
+       kclk_reset_start = 0;
+}
+
+void keymcu3_reset(void)
+{
+       if (currprefs.keyboard_mode == KB_A2000_8039) {
+               if (!state_loaded) {
+                       memset(mcu_io, 0xff, sizeof mcu_io);
+                       memset(matrix, 0, sizeof matrix);
+                       kbhandshake = false;
+               }
+               rom_loaded = false;
+
+               memset(mcu_rom, 0, sizeof mcu_rom);
+               struct zfile *zf = NULL;
+               const TCHAR *path = NULL;
+               struct boardromconfig *brc = get_device_rom_new(&currprefs, ROMTYPE_KBMCU, 0, NULL);
+               if (brc) {
+                       mcu_flags = brc->roms[0].device_settings;
+                       if (brc->roms[0].romfile[0]) {
+                               path = brc->roms[0].romfile;
+                               zf = zfile_fopen(path, _T("rb"));
+                       }
+               }
+               if(!zf) {
+                       int ids[] = { 323, -1 };
+                       struct romlist *rl = getromlistbyids(ids, NULL);
+                       if (rl) {
+                               path = rl->path;
+                               zf = read_rom(rl->rd);
+                       }
+               }
+               if (zf) {
+                       zfile_fread(mcu_rom, sizeof(mcu_rom), 1, zf);
+                       zfile_fclose(zf);
+                       write_log(_T("Loaded 8039 KB MCU ROM '%s'\n"), path);
+                       rom_loaded = true;
+               } else {
+                       write_log(_T("Failed to load 8039 KB MCU ROM\n"));
+               }
+               if (state_loaded) {
+                       gui_data.capslock = mcu_caps_led == 0;
+               } else {
+                       keymcu_res();
+                       if (isrestore()) {
+                               // if loading statefile without keyboard state: execute MCU code until init phase is done
+                               fakemode = 1;
+                               for (int i = 0; i < 3000000; i++) {
+                                       mcu->AddCycles(1);
+                                       mcu->Run();
+                                       if (fakemode > 1) {
+                                               fakemode--;
+                                               if (fakemode == 1) {
+                                                       mcu->NegateIrq();
+                                               }
+                                       }
+                               }
+                               fakemode = 0;
+                       }
+               }
+               lastcycle = get_cycles();
+       }
+       state_loaded = false;
+}
+
+void keymcu3_free(void)
+{
+       delete mcu;
+       mcu = NULL;
+       state_loaded = false;
+}
+
+void keymcu3_init(void)
+{
+       mcu = new ATCoProc8048();
+       mcu->ColdReset();
+       mcu->SetProgramBanks(mcu_rom, mcu_rom);
+       keymcu_reset();
+       state_loaded = false;
+}
+
+bool keymcu3_run(bool handshake)
+{
+       if (currprefs.keyboard_mode == KB_A2000_8039 && rom_loaded) {
+               bool hs = kbhandshake;
+               kbhandshake = handshake;
+               if (!handshake && hs) {
+                       if (kb_mcu_log & 1) {
+                               write_log("handshake off\n");
+                       }
+                       kbdatacnt = 0;
+                       mcu->NegateIrq();
+               } else if (handshake && !hs) {
+                       if (kb_mcu_log & 1) {
+                               write_log("handshake on\n");
+                       }
+                       kbdatacnt = 0;
+                       mcu->AssertIrq();
+               }
+               evt_t c = get_cycles();
+               int m = CYCLE_UNIT * (currprefs.ntscmode ? 3579545 : 3546895) / (6000000 / (5 * 3));
+               int cycles = (c - lastcycle) / m;
+               lastcycle += cycles * m;
+               mcu->AddCycles(cycles);
+               mcu->Run();
+               if (kclk_reset_start > 0 && kclk_reset_start < c) {
+                       write_log("Keyboard MCU system reset\n");
+                       inputdevice_do_kb_reset();
+               }
+       }
+       while (keys_available()) {
+               uae_u8 kbcode = get_next_key();
+               key_to_matrix(kbcode);
+       }
+       return kbdatacnt >= 3;
+}
+
+#ifdef SAVESTATE
+
+uae_u8 *save_kbmcu3(size_t *len, uae_u8 *dstptr)
+{
+       if (currprefs.keyboard_mode != KB_A2000_8039) {
+               return NULL;
+       }
+
+       uae_u8 *dstbak, *dst;
+       if (dstptr) {
+               dstbak = dst = dstptr;
+       } else {
+               dstbak = dst = xmalloc(uae_u8, 1000 + 128 + sizeof(mcu_io) + sizeof(matrix));
+       }
+
+       save_u32(1);
+       save_u32((kbhandshake ? 1 : 0) | (mcu->mbTimerActive ? 2 : 0) | (mcu->mbIrqAttention ? 4 : 0) |
+               (mcu->mbIrqEnabled ? 8 : 0) | (mcu->mbF1 ? 16 : 0) | (mcu->mbTF ? 32 : 0) |
+               (mcu->mbIF ? 64 : 0) | (mcu->mbPBK ? 128 : 0) | (mcu->mbDBF ? 256 : 0) |
+               (mcu->mbIrqPending ? 512 : 0) | (mcu->mbTimerIrqPending ? 1024 : 0));
+       save_u32(mcu_wd);
+       save_u8(kbdata);
+       save_u8(kbdatacnt);
+       save_u64(lastcycle);
+       save_u64(kclk_reset_start);
+       for (int i = 0; i < 128; i++) {
+               save_u8(mcu->mRAM[i]);
+       }
+       for (int i = 0; i < sizeof(mcu_io); i++) {
+               save_u8(mcu_io[i]);
+       }
+       for (int i = 0; i < sizeof(matrix); i++) {
+               save_u8(matrix[i]);
+       }
+       save_u8(mcu->mA);
+       save_u8(mcu->mT);
+       save_u8(mcu->mPSW);
+       save_u16(mcu->mPC);
+       save_u8(mcu->mP1);
+       save_u8(mcu->mP2);
+       save_u8(mcu->mTStatesLeft);
+
+       *len = dst - dstbak;
+       return dstbak;
+}
+
+uae_u8 *restore_kbmcu3(uae_u8* src)
+{
+       if (currprefs.keyboard_mode != KB_A2000_8039) {
+               return NULL;
+       }
+
+       uae_u32 v = restore_u32();
+       if (!(v & 1)) {
+               return src;
+       }
+       v = restore_u32();
+       kbhandshake = v & 1;
+       mcu->mbTimerActive = (v & 2) != 0;
+       mcu->mbIrqAttention = (v & 4) != 0;
+       mcu->mbIrqEnabled = (v & 8) != 0;
+       mcu->mbF1 = (v & 16) != 0;
+       mcu->mbTF = (v & 32) != 0;
+       mcu->mbIF = (v & 64) != 0;
+       mcu->mbPBK = (v & 128) != 0;
+       mcu->mbDBF = (v & 256) != 0;
+       mcu->mbIrqPending = (v & 512) != 0;
+       mcu->mbTimerIrqPending = (v & 1024) != 0;
+       mcu_wd = restore_u32();
+       kbdata = restore_u8();
+       kbdatacnt = restore_u8();
+       lastcycle = restore_u64();
+       kclk_reset_start = restore_u64();
+       for (int i = 0; i < 128; i++) {
+               mcu->mRAM[i] = restore_u8();
+       }
+       for (int i = 0; i < sizeof(mcu_io); i++) {
+               mcu_io[i] = restore_u8();
+       }
+       for (int i = 0; i < sizeof(matrix); i++) {
+               matrix[i] = restore_u8();
+       }
+       mcu->mA = restore_u8();
+       mcu->mT = restore_u8();
+       mcu->mPSW = restore_u8();
+       mcu->mPC = restore_u16();
+       mcu->mP1 = restore_u8();
+       mcu->mP2 = restore_u8();
+       mcu->mTStatesLeft = restore_u8();
+
+       state_loaded = true;
+       return src;
+}
+
+#endif
\ No newline at end of file
diff --git a/kbmcu/m6805/m68_internal.h b/kbmcu/m6805/m68_internal.h
new file mode 100644 (file)
index 0000000..0d56fed
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef M68_INTERNAL_H
+#define M68_INTERNAL_H
+
+#include <stdint.h>
+
+//! Addressing modes
+typedef enum {
+       AMODE_DIRECT,
+       AMODE_DIRECT_REL,
+       AMODE_DIRECT_JUMP,
+       AMODE_EXTENDED,
+       AMODE_EXTENDED_JUMP,
+       AMODE_IMMEDIATE,
+       AMODE_INDEXED0,
+       AMODE_INDEXED0_JUMP,
+       AMODE_INDEXED1,
+       AMODE_INDEXED1_JUMP,
+       AMODE_INDEXED2,
+       AMODE_INDEXED2_JUMP,
+       AMODE_INHERENT,
+       AMODE_INHERENT_A,
+       AMODE_INHERENT_X,
+       AMODE_RELATIVE,
+       AMODE_ILLEGAL,
+       AMODE_MAX
+} M68_AMODE;
+
+
+typedef struct M68_OPTABLE_ENT {
+       char *                  mnem;           /* instruction mnemonic */
+       M68_AMODE               amode;          /* addressing mode */
+       uint8_t                 cycles;         /* number of cycles to execute */
+       bool                    write_only;     /* is write-only?  only supported for direct, extended and indexed */
+       bool (*opfunc)(M68_CTX *ctx, const uint8_t opcode, uint8_t *param);     /* opcode exec function */
+} M68_OPTABLE_ENT;
+
+
+extern M68_OPTABLE_ENT m68hc05_optable[256];
+
+// M68HC vector addresses.
+static const uint16_t _M68_RESET_VECTOR = 0xFFFE;
+static const uint16_t _M68_SWI_VECTOR   = 0xFFFC;
+static const uint16_t _M68_INT_VECTOR   = 0xFFFA;
+static const uint16_t _M68_TMR1_VECTOR  = 0xFFF8;
+
+// TODO - need to have an m68_int function which allows vector address to be passed at runtime
+
+void jump_to_vector(M68_CTX *ctx,uint16_t addr);
+
+
+
+/**
+ * Force one or more flag bits to a given state
+ *
+ * @param      ctx                     Emulation context
+ * @param      ccr_bits        CCR bit map (OR of M68_CCR_x constants)
+ * @param      state           State to set -- true for set, false for clear
+ */
+static inline void force_flags(M68_CTX *ctx, const uint8_t ccr_bits, const bool state)
+{
+       if (state) {
+               ctx->reg_ccr |= ccr_bits;
+       } else {
+               ctx->reg_ccr &= ~ccr_bits;
+       }
+
+       // Force CCR top 3 bits to 1
+       ctx->reg_ccr |= 0xE0;
+}
+
+/**
+ * Get the state of a CCR flag bit
+ *
+ * @param      ctx                     Emulation context
+ * @param      ccr_bits        CCR bit (M68_CCR_x constant)
+ */
+static inline bool get_flag(M68_CTX *ctx, const uint8_t ccr_bit)
+{
+       return (ctx->reg_ccr & ccr_bit) ? true : false;
+}
+
+/**
+ * Push a byte onto the stack
+ *
+ * @param      ctx                     Emulation context
+ * @param      value           Byte to PUSH
+ */
+static inline void push_byte(M68_CTX *ctx, const uint8_t value)
+{
+       ctx->write_mem(ctx, ctx->reg_sp, value);
+       ctx->reg_sp = ((ctx->reg_sp - 1) & ctx->sp_and) | ctx->sp_or;
+}
+
+/**
+ * Pop a byte off of the stack
+ *
+ * @param      ctx                     Emulation context
+ * @return     Byte popped off of the stack
+ */
+static inline uint8_t pop_byte(M68_CTX *ctx)
+{
+       ctx->reg_sp = ((ctx->reg_sp + 1) & ctx->sp_and) | ctx->sp_or;
+       return ctx->read_mem(ctx, ctx->reg_sp);
+}
+
+
+
+#endif // M68_INTERNAL_H
diff --git a/kbmcu/m6805/m68_ops.cpp b/kbmcu/m6805/m68_ops.cpp
new file mode 100644 (file)
index 0000000..5db97f6
--- /dev/null
@@ -0,0 +1,759 @@
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include "m68emu.h"
+#include "m68_internal.h"
+
+
+/****************************************************************************
+ * HELPER FUNCTIONS
+ ****************************************************************************/
+
+/**
+ * Carry flag calculation method selection.
+ *
+ * Instructions calculate the carry flag state in different ways:
+ *
+ *   * ADC, ADD: (A7 && M7) || (M7 && !R7) || (!R7 && A7)
+ *   * ASL, LSL, ROL: b7
+ *   * ASR, LSR, ROR: b0
+ *   * BRCLR, BRSET: Mn
+ *   * CLC, MUL: 0
+ *   * NEG: R != 0
+ *   * CMP, CPX*, SBC: (!A7 && M7) || (M7 && R7) || (R7 && !A7)
+ *     (* CPX: substitute X7 in place of A7)
+ *
+ * The shift and negation instructions handle carry themselves.
+ * This means update_flags() only needs to implement Add and Subtract carry
+ * logic.
+ *
+ * CARRY_ADD: Addition carry
+ * CARRY_SUB: Subtraction carry
+ * CARRY_UNDEFINED: Raise an assertion failure if this is ever encountered
+ *
+ * We could in theory have CARRY_CLEAR and CARRY_SET in here, but I think it's
+ * preferable to have an explicit force_flags() call for the carry instead of
+ * hiding carry set/clear inside this call.
+ */
+typedef enum {
+       CARRY_ADD,
+       CARRY_SUB,
+       CARRY_UNDEFINED
+} M68_CARRY_TYPE;
+
+/**
+ * Update the CCR flag bits after an arithmetic operation.
+ *
+ * @param      ctx                     Emulation context
+ * @param      ccr_bits        CCR bit map (OR of M68_CCR_x constants)
+ * @param      a                       Accumulator initial value (or X-register for CPX)
+ * @param      m                       Operation parameter
+ * @param      r                       Operation result
+ */
+static inline void update_flags(M68_CTX *ctx, const uint8_t ccr_bits, const uint8_t a, const uint8_t m, const uint8_t r, const M68_CARRY_TYPE carryMode)
+{
+       // Half Carry
+       if (ccr_bits & M68_CCR_H) {
+               if (( (a & 0x08) &&  (m & 0x08)) ||
+                               ( (m & 0x08) && !(r & 0x08)) ||
+                               (!(r & 0x08) &&  (a & 0x08))) {
+                       ctx->reg_ccr |= M68_CCR_H;
+               } else {
+                       ctx->reg_ccr &= ~M68_CCR_H;
+               }
+       }
+
+       // Negative
+       if (ccr_bits & M68_CCR_N) {
+               if (r & 0x80) {
+                       ctx->reg_ccr |= M68_CCR_N;
+               } else {
+                       ctx->reg_ccr &= ~M68_CCR_N;
+               }
+       }
+
+       // Zero
+       if (ccr_bits & M68_CCR_Z) {
+               if (r == 0) {
+                       ctx->reg_ccr |= M68_CCR_Z;
+               } else {
+                       ctx->reg_ccr &= ~M68_CCR_Z;
+               }
+       }
+
+       // Carry
+       // TODO:  Carry is calculated in different ways depending on instruction. Implement the different modes.
+       if (ccr_bits & M68_CCR_C) {
+               bool newCarry;
+               switch (carryMode) {
+                       case CARRY_ADD:
+                               newCarry =
+                                       (( (a & 0x80) &&  (m & 0x80)) ||
+                                        ( (m & 0x80) && !(r & 0x80)) ||
+                                        (!(r & 0x80) &&  (a & 0x80)));
+                               break;
+
+                       case CARRY_SUB:
+                               newCarry =
+                                       ((!(a & 0x80) &&  (m & 0x80)) ||
+                                        ( (m & 0x80) &&  (r & 0x80)) ||
+                                        ( (r & 0x80) && !(a & 0x80)));
+                               break;
+
+                       default:
+                               assert(0);
+               }
+
+               if (newCarry) {
+                       ctx->reg_ccr |= M68_CCR_C;
+               } else {
+                       ctx->reg_ccr &= ~M68_CCR_C;
+               }
+       }
+
+       // Force CCR top 3 bits to 1
+       ctx->reg_ccr |= 0xE0;
+}
+
+
+/****************************************************************************
+ * OPCODE IMPLEMENTATION
+ ****************************************************************************/
+
+/* **
+ * Opcode implementation rules:
+ *
+ *   - Branch and jump opcodes (AMODE_RELATIVE, AMODE_*_JUMP)
+ *     - Return true to take the branch.
+ *     - Return false to continue execution from the next instruction.
+ *   - General opcodes
+ *     - Return 'true' to write the new value of 'param' back to the source.
+ *
+ * When the opfunc is entered, the PC will point to the next instruction. This
+ * is to make sure the opfunc doesn't need to worry about correcting pushed
+ * return addresses -- which would require a case for every opcode.
+ */
+
+
+/// ADC: Add with carry
+static bool m68op_ADC(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = ctx->reg_acc + *param + (ctx->reg_ccr & M68_CCR_C ? 1 : 0);
+
+       update_flags(ctx, M68_CCR_H | M68_CCR_N | M68_CCR_Z | M68_CCR_C, ctx->reg_acc, *param, result, CARRY_ADD);
+       ctx->reg_acc = result;
+
+       // Don't write back, affects ACC and flags only
+       return false;
+}
+
+/// ADD: Add
+static bool m68op_ADD(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = ctx->reg_acc + *param;
+
+       update_flags(ctx, M68_CCR_H | M68_CCR_N | M68_CCR_Z | M68_CCR_C, ctx->reg_acc, *param, result, CARRY_ADD);
+       ctx->reg_acc = result;
+
+       // Don't write back, affects ACC and flags only
+       return false;
+}
+
+/// AND: Logical AND
+static bool m68op_AND(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = ctx->reg_acc & *param;
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+       ctx->reg_acc = result;
+
+       // Don't write back, affects ACC and flags only
+       return false;
+}
+
+/// ASL: Arithmetic shift left (same as LSL)
+static bool m68op_ASL(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = (*param << 1) & 0xFE;
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+       force_flags(ctx, M68_CCR_C, *param & 0x80);
+
+       // Result is written back to the source (in-place)
+       *param = result;
+       return true;
+}
+
+/// ASR: Arithmetic shift right
+static bool m68op_ASR(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = (*param >> 1) | (*param & 0x80);
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+       force_flags(ctx, M68_CCR_C, *param & 1);
+
+       // Result is written back to the source (in-place)
+       *param = result;
+       return true;
+}
+
+/// BCC: Branch carry clear (same as BHS)
+static bool m68op_BCC(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (!get_flag(ctx, M68_CCR_C));
+}
+
+/// BCLR: Clear bit in memory
+static bool m68op_BCLR(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // Extract the bit number from the opcode
+       uint8_t bitnum = (opcode & 0x0F) >> 1;
+
+       *param &= ~(1 << bitnum);
+
+       // Result is written back to the source (in-place)
+       return true;
+}
+
+/// BCS: Branch carry set
+static bool m68op_BCS(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (get_flag(ctx, M68_CCR_C));
+}
+
+/// BEQ: Branch if equal
+static bool m68op_BEQ(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (get_flag(ctx, M68_CCR_Z));
+}
+
+/// BHCC: Branch if half-carry clear
+static bool m68op_BHCC(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (!get_flag(ctx, M68_CCR_H));
+}
+
+/// BHCS: Branch if half-carry set
+static bool m68op_BHCS(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (get_flag(ctx, M68_CCR_H));
+}
+
+/// BHI: Branch if higher
+static bool m68op_BHI(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (!get_flag(ctx, M68_CCR_C) && !get_flag(ctx, M68_CCR_Z));
+}
+
+// BHS: see BCC
+
+/// BIH: Branch if /IRQ high
+static bool m68op_BIH(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (ctx->irq);
+}
+
+/// BIL: Branch if /IRQ low
+static bool m68op_BIL(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (!ctx->irq);
+}
+
+/// BIT: Bit test memory with accumulator
+static bool m68op_BIT(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = ctx->reg_acc & *param;
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+
+       // Don't change the memory or the accumulator
+       return *param;
+}
+
+// BLO: see BCS
+
+/// BLS: Branch if lower or same
+static bool m68op_BLS(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (get_flag(ctx, M68_CCR_C) || get_flag(ctx, M68_CCR_Z));
+}
+
+/// BNC: Branch if interrupt mask is clear
+static bool m68op_BMC(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (!get_flag(ctx, M68_CCR_I));
+}
+
+/// BMI: Branch if minus
+static bool m68op_BMI(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (get_flag(ctx, M68_CCR_N));
+}
+
+/// BMS: Branch if interrupt mask is set
+static bool m68op_BMS(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (get_flag(ctx, M68_CCR_I));
+}
+
+/// BNE: Branch if not equal
+static bool m68op_BNE(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (!get_flag(ctx, M68_CCR_Z));
+}
+
+/// BPL: Branch if plus
+static bool m68op_BPL(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return (!get_flag(ctx, M68_CCR_N));
+}
+
+/// BRA: Branch always
+static bool m68op_BRA(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // Always take the branch
+       return true;
+}
+
+/// BRCLR: Branch if bit clear
+static bool m68op_BRCLR(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // Extract the bit number from the opcode
+       uint8_t bitnum = (opcode & 0x0F) >> 1;
+
+       // Carry flag is set to the bit state
+       force_flags(ctx, M68_CCR_C, *param & (1 << bitnum) ? 1 : 0);
+
+       return !(*param & (1 << bitnum));
+}
+
+/// BRN: Branch never
+static bool m68op_BRN(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // Don't take the branch
+       return false;
+}
+
+/// BRSET: Branch if bit set
+static bool m68op_BRSET(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // Extract the bit number from the opcode
+       uint8_t bitnum = (opcode & 0x0F) >> 1;
+
+       // Carry flag is set to the bit state
+       force_flags(ctx, M68_CCR_C, *param & (1 << bitnum) ? 1 : 0);
+
+       return (*param & (1 << bitnum));
+}
+
+/// BSET: Bit set
+static bool m68op_BSET(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // Extract the bit number from the opcode
+       uint8_t bitnum = (opcode & 0x0F) >> 1;
+
+       *param |= (1 << bitnum);
+
+       // Result is written back to the source (in-place)
+       return true;
+}
+
+/// BSR: Branch to subroutine
+static bool m68op_BSR(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // push return address onto stack
+       uint16_t ra = ctx->pc_next;
+       push_byte(ctx, ra & 0xff);
+       push_byte(ctx, (ra >> 8) & 0xff);
+
+       // take the branch
+       return true;
+}
+
+/// CLC: Clear carry
+static bool m68op_CLC(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       force_flags(ctx, M68_CCR_C, 0);
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// CLI: Clear interrupt mask
+static bool m68op_CLI(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       force_flags(ctx, M68_CCR_I, 0);
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// CLR: Clear accumulator, X or register
+static bool m68op_CLR(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       force_flags(ctx, M68_CCR_N, 0);
+       force_flags(ctx, M68_CCR_Z, 1);
+
+       // Result is written back to the source (in-place)
+       *param = 0;
+       return true;
+}
+
+/// CMP: Compare accumulator with memory
+static bool m68op_CMP(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint8_t result = ctx->reg_acc - *param;
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z | M68_CCR_C, ctx->reg_acc, *param, result, CARRY_SUB);
+
+       // CMP affects flags only, not the parameter or accumulator
+       return false;
+}
+
+/// COM: Complement
+static bool m68op_COM(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint8_t result = ~*param;
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+       force_flags(ctx, M68_CCR_C, 1);
+
+       // Result is written back to the source (in-place)
+       *param = result;
+       return true;
+}
+
+/// CPX: Compare index register with memory
+static bool m68op_CPX(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint8_t result = ctx->reg_x - *param;
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z | M68_CCR_C, ctx->reg_x, *param, result, CARRY_SUB);
+
+       // CPX affects flags only, not the parameter or accumulator
+       return false;
+}
+
+/// DEC: Decrement
+static bool m68op_DEC(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint8_t result = *param - 1;
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+
+       // Result is written back to the source (in-place)
+       *param = result;
+       return true;
+}
+
+/// EOR: Exclusive-OR accumulator with memory
+static bool m68op_EOR(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint8_t result = ctx->reg_acc ^ *param;
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+       ctx->reg_acc = result;
+
+       // Don't write back, affects ACC and flags only
+       return false;
+}
+
+/// INC: Increment
+static bool m68op_INC(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint8_t result = *param + 1;
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+
+       // Result is written back to the source (in-place)
+       *param = result;
+       return true;
+}
+
+/// JMP: Long jump
+static bool m68op_JMP(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       return true;
+}
+
+/// JSR: Jump subroutine
+static bool m68op_JSR(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // push return address onto stack
+       uint16_t ra = ctx->pc_next;
+       push_byte(ctx, ra & 0xff);
+       push_byte(ctx, (ra >> 8) & 0xff);
+
+       // take the branch
+       return true;
+}
+
+/// LDA: Load accumulator
+static bool m68op_LDA(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       ctx->reg_acc = *param;
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, *param, CARRY_UNDEFINED);
+
+       // Don't write back, affects ACC and flags only
+       return false;
+}
+
+/// LDX: Load index
+static bool m68op_LDX(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       ctx->reg_x = *param;
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, *param, CARRY_UNDEFINED);
+
+       // Don't write back, affects X and flags only
+       return false;
+}
+
+/// LSR: Logical shift right
+static bool m68op_LSR(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = (*param >> 1);
+
+       update_flags(ctx, M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+       force_flags(ctx, M68_CCR_N, 0);
+       force_flags(ctx, M68_CCR_C, *param & 1);
+
+       // Result is written back to the source (in-place)
+       *param = result;
+       return true;
+}
+
+/// MUL: Multiply
+static bool m68op_MUL(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = (uint16_t)ctx->reg_x * (uint16_t)ctx->reg_acc;
+
+       ctx->reg_x = (result >> 8) & 0xff;
+       ctx->reg_acc = result & 0xff;
+
+       force_flags(ctx, M68_CCR_H | M68_CCR_C, 0);
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// NEG: Negate (2's complement)
+static bool m68op_NEG(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = 0 - *param;
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+       force_flags(ctx, M68_CCR_C, result != 0);
+       ctx->reg_acc = result;
+
+       // Don't write back, affects ACC and flags only
+       return false;
+}
+
+/// NOP: No operation
+static bool m68op_NOP(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // Do nothing
+       return false;
+}
+
+/// ORA: Inclusive-OR
+static bool m68op_ORA(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = ctx->reg_acc | *param;
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+       ctx->reg_acc = result;
+
+       // Don't write back, affects ACC and flags only
+       return false;
+}
+
+/// ROL: Rotate left
+static bool m68op_ROL(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = ((*param << 1) | (get_flag(ctx, M68_CCR_C) ? 1 : 0)) & 0xFF;
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+       force_flags(ctx, M68_CCR_C, *param & 0x80);
+
+       // Result is written back to the source (in-place)
+       *param = result;
+       return true;
+}
+
+/// ROR: Rotate right
+static bool m68op_ROR(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = ((*param >> 1) | (get_flag(ctx, M68_CCR_C) ? 0x80 : 0)) & 0xFF;
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, result, CARRY_UNDEFINED);
+       force_flags(ctx, M68_CCR_C, *param & 1);
+
+       // Result is written back to the source (in-place)
+       *param = result;
+       return true;
+}
+
+/// RSP: Reset stack pointer
+static bool m68op_RSP(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       ctx->reg_sp = 0xFF;     // TODO INITIAL_SP constant?
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// RTI: Return from interrupt
+static bool m68op_RTI(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // pop CCR, ACCA, X
+       ctx->reg_ccr = pop_byte(ctx);
+       ctx->reg_acc = pop_byte(ctx);
+       ctx->reg_x   = pop_byte(ctx);
+
+       // pop PCH:PCL
+       // This is split up to force instruction sequencing (ref C99, sequence points!)
+       uint16_t new_pc;
+       new_pc = (uint16_t)pop_byte(ctx) << 8;
+       new_pc |= pop_byte(ctx);
+       ctx->pc_next = new_pc & ctx->pc_and;
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// RTS: Return from subroutine
+static bool m68op_RTS(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // pop PCH:PCL
+       // This is split up to force instruction sequencing (ref C99, sequence points!)
+       uint16_t new_pc;
+       new_pc = (uint16_t)pop_byte(ctx) << 8;
+       new_pc |= pop_byte(ctx);
+       ctx->pc_next = new_pc & ctx->pc_and;
+
+       // Inherent operation, nothing to write back
+       return false;}
+
+/// SBC: Subtract with carry
+static bool m68op_SBC(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = ctx->reg_acc - *param - (ctx->reg_ccr & M68_CCR_C ? 1 : 0);
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z | M68_CCR_C, ctx->reg_acc, *param, result, CARRY_SUB);
+       ctx->reg_acc = result;
+
+       // Don't write back, affects ACC and flags only
+       return false;
+}
+
+/// SEC: Set carry flag
+static bool m68op_SEC(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       force_flags(ctx, M68_CCR_C, 1);
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// SEI: Set interrupt mask flag (disable interrupts)
+static bool m68op_SEI(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       force_flags(ctx, M68_CCR_I, 1);
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// STA: Store accumulator
+static bool m68op_STA(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_acc, *param, ctx->reg_acc, CARRY_UNDEFINED);
+
+       // Accumulator is written to memory
+       *param = ctx->reg_acc;
+       return true;
+}
+
+/// STOP: Enable IRQs, stop oscillator
+static bool m68op_STOP(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       force_flags(ctx, M68_CCR_I, 0);
+       ctx->is_stopped = true;
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// STX: Store X register
+static bool m68op_STX(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, ctx->reg_x, *param, ctx->reg_x, CARRY_UNDEFINED);
+
+       // X register is written to memory
+       *param = ctx->reg_x;
+       return true;
+}
+
+/// SUB: Subtract
+static bool m68op_SUB(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       uint16_t result = ctx->reg_acc - *param;
+
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z | M68_CCR_C, ctx->reg_acc, *param, result, CARRY_SUB);
+       ctx->reg_acc = result;
+
+       // Don't write back, affects ACC and flags only
+       return false;
+}
+
+/// SWI: Software Interrupt
+static bool m68op_SWI(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       // PC will already have been advanced by the emulation loop
+
+        jump_to_vector(ctx,_M68_SWI_VECTOR);
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// TAX: Transfer A to X
+static bool m68op_TAX(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       ctx->reg_x = ctx->reg_acc;
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// TST: Test if negative or zero
+static bool m68op_TST(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       update_flags(ctx, M68_CCR_N | M68_CCR_Z, 0, 0, *param, CARRY_UNDEFINED);
+
+       // Contents of the tested register or memory location are left unaltered.
+       return false;
+}
+
+/// TXA: Transfer X to ACC
+static bool m68op_TXA(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       ctx->reg_acc = ctx->reg_x;
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+/// WAIT: Wait for interrupt. Like STOP, but leaves peripherals running.
+static bool m68op_WAIT(M68_CTX *ctx, const uint8_t opcode, uint8_t *param)
+{
+       force_flags(ctx, M68_CCR_I, 0);
+       ctx->is_waiting = true;
+
+       // Inherent operation, nothing to write back
+       return false;
+}
+
+
+/****************************************************************************
+ * OPCODE TABLES
+ ****************************************************************************/
+
+#include "m68_optab_hc05.h"
+
diff --git a/kbmcu/m6805/m68_optab_hc05.h b/kbmcu/m6805/m68_optab_hc05.h
new file mode 100644 (file)
index 0000000..ad99c8d
--- /dev/null
@@ -0,0 +1,258 @@
+M68_OPTABLE_ENT m68hc05_optable[256] = {
+       { "BRSET", AMODE_DIRECT_REL, 5, false, &m68op_BRSET },
+       { "BRCLR", AMODE_DIRECT_REL, 5, false, &m68op_BRCLR },
+       { "BRSET", AMODE_DIRECT_REL, 5, false, &m68op_BRSET },
+       { "BRCLR", AMODE_DIRECT_REL, 5, false, &m68op_BRCLR },
+       { "BRSET", AMODE_DIRECT_REL, 5, false, &m68op_BRSET },
+       { "BRCLR", AMODE_DIRECT_REL, 5, false, &m68op_BRCLR },
+       { "BRSET", AMODE_DIRECT_REL, 5, false, &m68op_BRSET },
+       { "BRCLR", AMODE_DIRECT_REL, 5, false, &m68op_BRCLR },
+       { "BRSET", AMODE_DIRECT_REL, 5, false, &m68op_BRSET },
+       { "BRCLR", AMODE_DIRECT_REL, 5, false, &m68op_BRCLR },
+       { "BRSET", AMODE_DIRECT_REL, 5, false, &m68op_BRSET },
+       { "BRCLR", AMODE_DIRECT_REL, 5, false, &m68op_BRCLR },
+       { "BRSET", AMODE_DIRECT_REL, 5, false, &m68op_BRSET },
+       { "BRCLR", AMODE_DIRECT_REL, 5, false, &m68op_BRCLR },
+       { "BRSET", AMODE_DIRECT_REL, 5, false, &m68op_BRSET },
+       { "BRCLR", AMODE_DIRECT_REL, 5, false, &m68op_BRCLR },
+       { "BSET", AMODE_DIRECT, 5, false, &m68op_BSET },
+       { "BCLR", AMODE_DIRECT, 5, false, &m68op_BCLR },
+       { "BSET", AMODE_DIRECT, 5, false, &m68op_BSET },
+       { "BCLR", AMODE_DIRECT, 5, false, &m68op_BCLR },
+       { "BSET", AMODE_DIRECT, 5, false, &m68op_BSET },
+       { "BCLR", AMODE_DIRECT, 5, false, &m68op_BCLR },
+       { "BSET", AMODE_DIRECT, 5, false, &m68op_BSET },
+       { "BCLR", AMODE_DIRECT, 5, false, &m68op_BCLR },
+       { "BSET", AMODE_DIRECT, 5, false, &m68op_BSET },
+       { "BCLR", AMODE_DIRECT, 5, false, &m68op_BCLR },
+       { "BSET", AMODE_DIRECT, 5, false, &m68op_BSET },
+       { "BCLR", AMODE_DIRECT, 5, false, &m68op_BCLR },
+       { "BSET", AMODE_DIRECT, 5, false, &m68op_BSET },
+       { "BCLR", AMODE_DIRECT, 5, false, &m68op_BCLR },
+       { "BSET", AMODE_DIRECT, 5, false, &m68op_BSET },
+       { "BCLR", AMODE_DIRECT, 5, false, &m68op_BCLR },
+       { "BRA", AMODE_RELATIVE, 3, false, &m68op_BRA },
+       { "BRN", AMODE_RELATIVE, 3, false, &m68op_BRN },
+       { "BHI", AMODE_RELATIVE, 3, false, &m68op_BHI },
+       { "BLS", AMODE_RELATIVE, 3, false, &m68op_BLS },
+       { "BCC", AMODE_RELATIVE, 3, false, &m68op_BCC },
+       { "BCS", AMODE_RELATIVE, 3, false, &m68op_BCS },
+       { "BNE", AMODE_RELATIVE, 3, false, &m68op_BNE },
+       { "BEQ", AMODE_RELATIVE, 3, false, &m68op_BEQ },
+       { "BHCC", AMODE_RELATIVE, 3, false, &m68op_BHCC },
+       { "BHCS", AMODE_RELATIVE, 3, false, &m68op_BHCS },
+       { "BPL", AMODE_RELATIVE, 3, false, &m68op_BPL },
+       { "BMI", AMODE_RELATIVE, 3, false, &m68op_BMI },
+       { "BMC", AMODE_RELATIVE, 3, false, &m68op_BMC },
+       { "BMS", AMODE_RELATIVE, 3, false, &m68op_BMS },
+       { "BIL", AMODE_RELATIVE, 3, false, &m68op_BIL },
+       { "BIH", AMODE_RELATIVE, 3, false, &m68op_BIH },
+       { "NEG", AMODE_DIRECT, 5, false, &m68op_NEG },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "COM", AMODE_DIRECT, 5, false, &m68op_COM },
+       { "LSR", AMODE_DIRECT, 5, false, &m68op_LSR },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ROR", AMODE_DIRECT, 5, false, &m68op_ROR },
+       { "ASR", AMODE_DIRECT, 5, false, &m68op_ASR },
+       { "ASL", AMODE_DIRECT, 5, false, &m68op_ASL },
+       { "ROL", AMODE_DIRECT, 5, false, &m68op_ROL },
+       { "DEC", AMODE_DIRECT, 5, false, &m68op_DEC },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "INC", AMODE_DIRECT, 5, false, &m68op_INC },
+       { "TST", AMODE_DIRECT, 4, false, &m68op_TST },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "CLR", AMODE_DIRECT, 5, true, &m68op_CLR },
+       { "NEGA", AMODE_INHERENT_A, 3, false, &m68op_NEG },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "MUL", AMODE_INHERENT, 11, false, &m68op_MUL },
+       { "COMA", AMODE_INHERENT_A, 3, false, &m68op_COM },
+       { "LSRA", AMODE_INHERENT_A, 3, false, &m68op_LSR },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "RORA", AMODE_INHERENT_A, 3, false, &m68op_ROR },
+       { "ASRA", AMODE_INHERENT_A, 3, false, &m68op_ASR },
+       { "ASLA", AMODE_INHERENT_A, 3, false, &m68op_ASL },
+       { "ROLA", AMODE_INHERENT_A, 3, false, &m68op_ROL },
+       { "DECA", AMODE_INHERENT_A, 3, false, &m68op_DEC },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "INCA", AMODE_INHERENT_A, 3, false, &m68op_INC },
+       { "TSTA", AMODE_INHERENT_A, 3, false, &m68op_TST },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "CLRA", AMODE_INHERENT_A, 3, true, &m68op_CLR },
+       { "NEGX", AMODE_INHERENT_X, 3, false, &m68op_NEG },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "COMX", AMODE_INHERENT_X, 3, false, &m68op_COM },
+       { "LSRX", AMODE_INHERENT_X, 3, false, &m68op_LSR },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "RORX", AMODE_INHERENT_X, 3, false, &m68op_ROR },
+       { "ASRX", AMODE_INHERENT_X, 3, false, &m68op_ASR },
+       { "ASLX", AMODE_INHERENT_A, 3, false, &m68op_ASL },
+       { "ROLX", AMODE_INHERENT_X, 3, false, &m68op_ROL },
+       { "DECX", AMODE_INHERENT_X, 3, false, &m68op_DEC },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "INCX", AMODE_INHERENT_X, 3, false, &m68op_INC },
+       { "TSTX", AMODE_INHERENT_X, 3, false, &m68op_TST },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "CLRX", AMODE_INHERENT_X, 3, true, &m68op_CLR },
+       { "NEG", AMODE_INDEXED1, 6, false, &m68op_NEG },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "COM", AMODE_INDEXED1, 6, false, &m68op_COM },
+       { "LSR", AMODE_INDEXED1, 6, false, &m68op_LSR },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ROR", AMODE_INDEXED1, 6, false, &m68op_ROR },
+       { "ASR", AMODE_INDEXED1, 6, false, &m68op_ASR },
+       { "ASL", AMODE_INDEXED1, 6, false, &m68op_ASL },
+       { "ROL", AMODE_INDEXED1, 6, false, &m68op_ROL },
+       { "DEC", AMODE_INDEXED1, 6, false, &m68op_DEC },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "INC", AMODE_INDEXED1, 6, false, &m68op_INC },
+       { "TST", AMODE_INDEXED1, 5, false, &m68op_TST },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "CLR", AMODE_INDEXED1, 6, true, &m68op_CLR },
+       { "NEG", AMODE_INDEXED0, 5, false, &m68op_NEG },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "COM", AMODE_INDEXED0, 5, false, &m68op_COM },
+       { "LSR", AMODE_INDEXED0, 5, false, &m68op_LSR },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ROR", AMODE_INDEXED0, 5, false, &m68op_ROR },
+       { "ASR", AMODE_INDEXED0, 5, false, &m68op_ASR },
+       { "ASL", AMODE_INDEXED0, 5, false, &m68op_ASL },
+       { "ROL", AMODE_INDEXED0, 5, false, &m68op_ROL },
+       { "DEC", AMODE_INDEXED0, 5, false, &m68op_DEC },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "INC", AMODE_INDEXED0, 5, false, &m68op_INC },
+       { "TST", AMODE_INDEXED0, 4, false, &m68op_TST },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "CLR", AMODE_INDEXED0, 5, true, &m68op_CLR },
+       { "RTI", AMODE_INHERENT, 9, false, &m68op_RTI },
+       { "RTS", AMODE_INHERENT, 6, false, &m68op_RTS },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "SWI", AMODE_INHERENT, 10, false, &m68op_SWI },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "STOP", AMODE_INHERENT, 2, false, &m68op_STOP },
+       { "WAIT", AMODE_INHERENT, 2, false, &m68op_WAIT },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "TAX", AMODE_INHERENT, 2, false, &m68op_TAX },
+       { "CLC", AMODE_INHERENT, 2, false, &m68op_CLC },
+       { "SEC", AMODE_INHERENT, 2, false, &m68op_SEC },
+       { "CLI", AMODE_INHERENT, 2, false, &m68op_CLI },
+       { "SEI", AMODE_INHERENT, 2, false, &m68op_SEI },
+       { "RSP", AMODE_INHERENT, 2, false, &m68op_RSP },
+       { "NOP", AMODE_INHERENT, 2, false, &m68op_NOP },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "TXA", AMODE_INHERENT, 2, false, &m68op_TXA },
+       { "SUB", AMODE_IMMEDIATE, 2, false, &m68op_SUB },
+       { "CMP", AMODE_IMMEDIATE, 2, false, &m68op_CMP },
+       { "SBC", AMODE_IMMEDIATE, 2, false, &m68op_SBC },
+       { "CPX", AMODE_IMMEDIATE, 2, false, &m68op_CPX },
+       { "AND", AMODE_IMMEDIATE, 2, false, &m68op_AND },
+       { "BIT", AMODE_IMMEDIATE, 2, false, &m68op_BIT },
+       { "LDA", AMODE_IMMEDIATE, 2, false, &m68op_LDA },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "EOR", AMODE_IMMEDIATE, 2, false, &m68op_EOR },
+       { "ADC", AMODE_IMMEDIATE, 2, false, &m68op_ADC },
+       { "ORA", AMODE_IMMEDIATE, 2, false, &m68op_ORA },
+       { "ADD", AMODE_IMMEDIATE, 2, false, &m68op_ADD },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "BSR", AMODE_RELATIVE, 6, false, &m68op_BSR },
+       { "LDX", AMODE_IMMEDIATE, 2, false, &m68op_LDX },
+       { "ILLEGAL", AMODE_ILLEGAL, 0, 0, NULL },
+       { "SUB", AMODE_DIRECT, 3, false, &m68op_SUB },
+       { "CMP", AMODE_DIRECT, 3, false, &m68op_CMP },
+       { "SBC", AMODE_DIRECT, 3, false, &m68op_SBC },
+       { "CPX", AMODE_DIRECT, 3, false, &m68op_CPX },
+       { "AND", AMODE_DIRECT, 3, false, &m68op_AND },
+       { "BIT", AMODE_DIRECT, 3, false, &m68op_BIT },
+       { "LDA", AMODE_DIRECT, 3, false, &m68op_LDA },
+       { "STA", AMODE_DIRECT, 4, true, &m68op_STA },
+       { "EOR", AMODE_DIRECT, 3, false, &m68op_EOR },
+       { "ADC", AMODE_DIRECT, 3, false, &m68op_ADC },
+       { "ORA", AMODE_DIRECT, 3, false, &m68op_ORA },
+       { "ADD", AMODE_DIRECT, 3, false, &m68op_ADD },
+       { "JMP", AMODE_DIRECT_JUMP, 2, false, &m68op_JMP },
+       { "JSR", AMODE_DIRECT_JUMP, 5, false, &m68op_JSR },
+       { "LDX", AMODE_DIRECT, 3, false, &m68op_LDX },
+       { "STX", AMODE_DIRECT, 4, true, &m68op_STX },
+       { "SUB", AMODE_EXTENDED, 4, false, &m68op_SUB },
+       { "CMP", AMODE_EXTENDED, 4, false, &m68op_CMP },
+       { "SBC", AMODE_EXTENDED, 4, false, &m68op_SBC },
+       { "CPX", AMODE_EXTENDED, 4, false, &m68op_CPX },
+       { "AND", AMODE_EXTENDED, 4, false, &m68op_AND },
+       { "BIT", AMODE_EXTENDED, 4, false, &m68op_BIT },
+       { "LDA", AMODE_EXTENDED, 4, false, &m68op_LDA },
+       { "STA", AMODE_EXTENDED, 5, true, &m68op_STA },
+       { "EOR", AMODE_EXTENDED, 4, false, &m68op_EOR },
+       { "ADC", AMODE_EXTENDED, 4, false, &m68op_ADC },
+       { "ORA", AMODE_EXTENDED, 4, false, &m68op_ORA },
+       { "ADD", AMODE_EXTENDED, 4, false, &m68op_ADD },
+       { "JMP", AMODE_EXTENDED_JUMP, 3, false, &m68op_JMP },
+       { "JSR", AMODE_EXTENDED_JUMP, 6, false, &m68op_JSR },
+       { "LDX", AMODE_EXTENDED, 4, false, &m68op_LDX },
+       { "STX", AMODE_EXTENDED, 5, true, &m68op_STX },
+       { "SUB", AMODE_INDEXED2, 5, false, &m68op_SUB },
+       { "CMP", AMODE_INDEXED2, 5, false, &m68op_CMP },
+       { "SBC", AMODE_INDEXED2, 5, false, &m68op_SBC },
+       { "CPX", AMODE_INDEXED2, 5, false, &m68op_CPX },
+       { "AND", AMODE_INDEXED2, 5, false, &m68op_AND },
+       { "BIT", AMODE_INDEXED2, 5, false, &m68op_BIT },
+       { "LDA", AMODE_INDEXED2, 5, false, &m68op_LDA },
+       { "STA", AMODE_INDEXED2, 6, true, &m68op_STA },
+       { "EOR", AMODE_INDEXED2, 5, false, &m68op_EOR },
+       { "ADC", AMODE_INDEXED2, 5, false, &m68op_ADC },
+       { "ORA", AMODE_INDEXED2, 5, false, &m68op_ORA },
+       { "ADD", AMODE_INDEXED2, 5, false, &m68op_ADD },
+       { "JMP", AMODE_INDEXED2_JUMP, 4, false, &m68op_JMP },
+       { "JSR", AMODE_INDEXED2_JUMP, 7, false, &m68op_JSR },
+       { "LDX", AMODE_INDEXED2, 5, false, &m68op_LDX },
+       { "STX", AMODE_INDEXED2, 6, true, &m68op_STX },
+       { "SUB", AMODE_INDEXED1, 4, false, &m68op_SUB },
+       { "CMP", AMODE_INDEXED1, 4, false, &m68op_CMP },
+       { "SBC", AMODE_INDEXED1, 4, false, &m68op_SBC },
+       { "CPX", AMODE_INDEXED1, 4, false, &m68op_CPX },
+       { "AND", AMODE_INDEXED1, 4, false, &m68op_AND },
+       { "BIT", AMODE_INDEXED1, 4, false, &m68op_BIT },
+       { "LDA", AMODE_INDEXED1, 4, false, &m68op_LDA },
+       { "STA", AMODE_INDEXED1, 5, true, &m68op_STA },
+       { "EOR", AMODE_INDEXED1, 4, false, &m68op_EOR },
+       { "ADC", AMODE_INDEXED1, 4, false, &m68op_ADC },
+       { "ORA", AMODE_INDEXED1, 4, false, &m68op_ORA },
+       { "ADD", AMODE_INDEXED1, 4, false, &m68op_ADD },
+       { "JMP", AMODE_INDEXED1_JUMP, 3, false, &m68op_JMP },
+       { "JSR", AMODE_INDEXED1_JUMP, 6, false, &m68op_JSR },
+       { "LDX", AMODE_INDEXED1, 4, false, &m68op_LDX },
+       { "STX", AMODE_INDEXED1, 5, true, &m68op_STX },
+       { "SUB", AMODE_INDEXED0, 3, false, &m68op_SUB },
+       { "CMP", AMODE_INDEXED0, 3, false, &m68op_CMP },
+       { "SBC", AMODE_INDEXED0, 3, false, &m68op_SBC },
+       { "CPX", AMODE_INDEXED0, 3, false, &m68op_CPX },
+       { "AND", AMODE_INDEXED0, 3, false, &m68op_AND },
+       { "BIT", AMODE_INDEXED0, 3, false, &m68op_BIT },
+       { "LDA", AMODE_INDEXED0, 3, false, &m68op_LDA },
+       { "STA", AMODE_INDEXED0, 4, true, &m68op_STA },
+       { "EOR", AMODE_INDEXED0, 3, false, &m68op_EOR },
+       { "ADC", AMODE_INDEXED0, 3, false, &m68op_ADC },
+       { "ORA", AMODE_INDEXED0, 3, false, &m68op_ORA },
+       { "ADD", AMODE_INDEXED0, 3, false, &m68op_ADD },
+       { "JMP", AMODE_INDEXED0_JUMP, 2, false, &m68op_JMP },
+       { "JSR", AMODE_INDEXED0_JUMP, 5, false, &m68op_JSR },
+       { "LDX", AMODE_INDEXED0, 3, false, &m68op_LDX },
+       { "STX", AMODE_INDEXED0, 4, true, &m68op_STX },
+};
diff --git a/kbmcu/m6805/m68emu.cpp b/kbmcu/m6805/m68emu.cpp
new file mode 100644 (file)
index 0000000..97920e7
--- /dev/null
@@ -0,0 +1,330 @@
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include "m68emu.h"
+#include "m68_internal.h"
+
+void m68_init(M68_CTX *ctx, const M68_CPUTYPE cpuType)
+{
+       switch (cpuType) {
+               case M68_CPU_HC05C4:
+                       // 68HC05SC21 is based on the 68HC05C4 core.
+                       // SP is 13 bits.
+                       //   7 MSBs are permanently set to 0000011
+                       //   6 LSBs are passed through
+                       // Address range: 0xC0 to 0xFF
+                       ctx->sp_and = 0x003F;
+                       ctx->sp_or  = 0x00C0;
+
+                       // PC is 13 bits too
+                       ctx->pc_and = 0x1FFF;
+                       break;
+               case M68_CPU_HD6805V1:
+                       ctx->pc_and=0x0FFF;
+                       // Address range: 0xC0 to 0xFF ?
+                       ctx->sp_and = 0x003F;
+                       ctx->sp_or  = 0x00C0;
+                       break;
+               default:
+                       assert(0);
+       }
+
+       ctx->cpuType = cpuType;
+       ctx->trace = false;
+       ctx->pending_interrupts = 0;
+       m68_reset(ctx);
+}
+
+void m68_reset(M68_CTX *ctx)
+{
+       // Read the reset vector
+       ctx->reg_pc = _M68_RESET_VECTOR & ctx->pc_and;
+       uint16_t rstvec = (uint16_t)ctx->read_mem(ctx, ctx->reg_pc) << 8;
+       rstvec |= ctx->read_mem(ctx, ctx->reg_pc+1);
+
+       // Set PC to the reset vector
+       ctx->reg_pc = rstvec & ctx->pc_and;
+       ctx->pc_next = ctx->reg_pc;
+
+       // Reset stack pointer to 0xFF
+       ctx->reg_sp = 0xFF;
+
+       // Set the I bit in the CCR to 1 (mask off interrupts)
+       ctx->reg_ccr |= M68_CCR_I;
+
+       // Clear STOP and WAIT latches
+       ctx->is_stopped = ctx->is_waiting = 0;
+
+       // Clear external interrupt latch
+       ctx->irq = 0;
+}
+
+
+void m68_set_interrupt_line(M68_CTX * ctx,M68_INTERRUPT i){
+       if (i == M68_INT_IRQ){
+               ctx->irq=true;
+       }
+       ctx->pending_interrupts |= (1 << i);
+}
+
+void jump_to_vector(M68_CTX *ctx,uint16_t addr)
+{
+       push_byte(ctx, ctx->pc_next & 0xFF);
+       push_byte(ctx, ctx->pc_next >> 8);
+       push_byte(ctx, ctx->reg_x);
+       push_byte(ctx, ctx->reg_acc);
+       push_byte(ctx, ctx->reg_ccr);
+
+       // Mask further interrupts
+       force_flags(ctx, M68_CCR_I, 1);
+
+       // Vector fetch
+       uint16_t vector;
+       vector = (uint16_t)ctx->read_mem(ctx, addr & ctx->pc_and) << 8;
+       vector |= ctx->read_mem(ctx, (addr+1) & ctx->pc_and);
+       ctx->pc_next = vector;
+}
+
+uint64_t m68_exec_cycle(M68_CTX *ctx)
+{
+       uint8_t opval;
+       M68_OPTABLE_ENT *opcode;
+
+       // Save current program counter
+       ctx->reg_pc = ctx->pc_next;
+
+       if (ctx->pending_interrupts && get_flag(ctx,M68_CCR_I)==0){
+               if (ctx->pending_interrupts & (1<<M68_INT_IRQ)) {
+                       ctx->pending_interrupts &= ~(1<<M68_INT_IRQ);
+                       jump_to_vector(ctx,_M68_INT_VECTOR);
+               } else if (ctx->pending_interrupts& (1<<M68_INT_TIMER1)) {
+                       ctx->pending_interrupts &= ~(1<<M68_INT_TIMER1);
+                       jump_to_vector(ctx,_M68_TMR1_VECTOR);
+               }
+               return 11;
+       }
+
+       // Fetch and decode opcode
+       opval = ctx->read_mem(ctx, ctx->pc_next++);
+       if (ctx->opdecode != NULL) {
+               opval = ctx->opdecode(ctx, opval);
+       }
+       switch (ctx->cpuType) {
+               case M68_CPU_HC05C4:
+               case M68_CPU_HD6805V1:
+                       opcode = &m68hc05_optable[opval];
+                       break;
+               default:
+                       assert(0);
+       }
+
+       if (ctx->trace) {
+               printf("M68 EXEC: pc %04X sp %02X opval %02X mnem '%s' amode %d cycles %d\n",
+                               ctx->reg_pc, ctx->reg_sp, opval, opcode->mnem, opcode->amode, opcode->cycles);
+       }
+
+       // Read the opcode parameter bytes, if any
+       uint8_t opParam;                // parameter
+       uint16_t dirPtr;                // direct pointer
+       uint16_t opNextPC;              // next PC (if branch or jump)
+       bool opResult;
+
+       switch(opcode->amode) {
+               case AMODE_DIRECT:
+                       // Direct addressing: parameter is an address in zero page
+                       dirPtr = ctx->read_mem(ctx, ctx->pc_next++);
+                       if (!opcode->write_only) {
+                               opParam = ctx->read_mem(ctx, dirPtr);
+                       }
+                       break;
+
+               case AMODE_DIRECT_JUMP:
+                       // Direct addressing, jump
+                       opNextPC = ctx->read_mem(ctx, ctx->pc_next++);
+                       opParam = -1;
+                       break;
+
+               case AMODE_DIRECT_REL:
+                       // Direct + relative addressing: parameter is an address in zero page
+                       //   followed by a relative jump address.
+                       // Direct
+                       dirPtr = ctx->read_mem(ctx, ctx->pc_next++);
+                       opParam = ctx->read_mem(ctx, dirPtr);
+                       // Relative
+                       opNextPC = ctx->pc_next + 1;
+                       opNextPC += (int8_t)ctx->read_mem(ctx, ctx->pc_next++);
+                       break;
+
+               case AMODE_EXTENDED:
+                       // Extended addressing: parameter is a 16-bit address
+                       dirPtr = (uint16_t)ctx->read_mem(ctx, ctx->pc_next++) << 8;
+                       dirPtr |= ctx->read_mem(ctx, ctx->pc_next++);
+                       if (!opcode->write_only) {
+                               opParam = ctx->read_mem(ctx, dirPtr);
+                       }
+                       break;
+
+               case AMODE_EXTENDED_JUMP:
+                       // Extended addressing, jump
+                       opNextPC = (uint16_t)ctx->read_mem(ctx, ctx->pc_next++) << 8;
+                       opNextPC |= ctx->read_mem(ctx, ctx->pc_next++);
+                       opParam = -1;
+                       break;
+
+               case AMODE_IMMEDIATE:
+                       // Immediate addressing: parameter is an immediate value following the opcode
+                       opParam = ctx->read_mem(ctx, ctx->pc_next++);
+                       break;
+
+               case AMODE_INDEXED0:
+                       // Indexed with no offset. Take the X register as an address.
+                       dirPtr = ctx->reg_x;
+                       if (!opcode->write_only) {
+                               opParam = ctx->read_mem(ctx, dirPtr);
+                       }
+                       break;
+
+               case AMODE_INDEXED0_JUMP:
+                       // Indexed jump with no offset. Take the X register as an address.
+                       opNextPC = ctx->reg_x;
+                       opParam = -1;
+                       break;
+
+               case AMODE_INDEXED1:
+                       // Indexed with 1-byte offset. Add X and offset.
+                       dirPtr = (uint16_t)ctx->read_mem(ctx, ctx->pc_next++) + ctx->reg_x;
+                       if (!opcode->write_only) {
+                               opParam = ctx->read_mem(ctx, dirPtr);
+                       }
+                       break;
+
+               case AMODE_INDEXED1_JUMP:
+                       // Indexed jump with 1-byte offset. Take the X register as an address.
+                       opNextPC = (uint16_t)ctx->read_mem(ctx, ctx->pc_next++) + ctx->reg_x;
+                       opParam = -1;
+                       break;
+
+               case AMODE_INDEXED2:
+                       // Indexed with 2-byte offset. Add X and offset.
+                       dirPtr = (uint16_t)ctx->read_mem(ctx, ctx->pc_next++) << 8;
+                       dirPtr |= ctx->read_mem(ctx, ctx->pc_next++);
+                       dirPtr += ctx->reg_x;
+                       if (!opcode->write_only) {
+                               opParam = ctx->read_mem(ctx, dirPtr);
+                       }
+                       break;
+
+               case AMODE_INDEXED2_JUMP:
+                       // Indexed jump with 2-byte offset. Add X and offset.
+                       opNextPC = (uint16_t)ctx->read_mem(ctx, ctx->pc_next++) << 8;
+                       opNextPC |= ctx->read_mem(ctx, ctx->pc_next++);
+                       opNextPC += ctx->reg_x;
+                       opParam = -1;
+                       break;
+
+               case AMODE_INHERENT:
+                       // Inherent addressing, affects nothing.
+                       opParam = -1;
+                       break;
+
+               case AMODE_INHERENT_A:
+                       // Inherent addressing, affects Accumulator.
+                       opParam = ctx->reg_acc;
+                       break;
+
+               case AMODE_INHERENT_X:
+                       // Inherent addressing, affects X register.
+                       opParam = ctx->reg_x;
+                       break;
+
+               case AMODE_RELATIVE:
+                       // Relative addressing: signed relative branch or jump.
+                       opNextPC = ctx->pc_next + 1;
+                       opNextPC += (int8_t)ctx->read_mem(ctx, ctx->pc_next++);
+                       break;
+
+               case AMODE_ILLEGAL:
+               case AMODE_MAX:
+                       printf("ILLEGAL M68 EXEC: pc %04X sp %02X opval %02X mnem '%s' amode %d cycles %d\n",
+                                       ctx->reg_pc, ctx->reg_sp, opval, opcode->mnem, opcode->amode, opcode->cycles);
+
+                       // Illegal instruction
+                       assert(1==2);
+                       break;
+       }
+
+       // Execute opcode
+       opResult = opcode->opfunc(ctx, opval, &opParam);
+       if (ctx->trace) {
+               if (opResult) {
+                       printf("\t-> %3d (0x%02X)\n", opParam, opParam);
+               }
+       }
+
+       // Write back result (param)
+       switch(opcode->amode) {
+               case AMODE_DIRECT:
+               case AMODE_EXTENDED:
+               case AMODE_INDEXED0:
+               case AMODE_INDEXED1:
+               case AMODE_INDEXED2:
+                       // Direct addressing: parameter is an address in zero page
+                       // Extended addressing: parameter is a 16-bit address
+                       // Indexed with no offset. Take the X register as an address.
+                       // Indexed with 1-byte offset. Add X and offset.
+                       // Indexed with 2-byte offset. Add X and offset.
+                       if (opResult) {
+                               ctx->write_mem(ctx, dirPtr, opParam);
+                       }
+                       break;
+
+               case AMODE_DIRECT_JUMP:
+               case AMODE_EXTENDED_JUMP:
+               case AMODE_INDEXED0_JUMP:
+               case AMODE_INDEXED1_JUMP:
+               case AMODE_INDEXED2_JUMP:
+               case AMODE_DIRECT_REL:
+               case AMODE_RELATIVE:
+                       // Direct + relative addressing: parameter is an address in zero page
+                       //   followed by a signed relative jump address.
+                       // Relative addressing: signed relative branch or jump.
+                       //
+                       // If the opfunc returned true, take the jump.
+                       if (opResult) {
+                               ctx->pc_next = opNextPC & ctx->pc_and;
+                       }
+                       break;
+
+               case AMODE_IMMEDIATE:
+                       // Immediate addressing: parameter is an immediate value and cannot be written back.
+                       break;
+
+               case AMODE_INHERENT:
+                       // Inherent addressing, affects nothing.
+                       break;
+
+               case AMODE_INHERENT_A:
+                       // Inherent addressing, affects Accumulator.
+                       if (opResult) {
+                               ctx->reg_acc = opParam;
+                       }
+                       break;
+
+               case AMODE_INHERENT_X:
+                       // Inherent addressing, affects X register.
+                       if (opResult) {
+                               ctx->reg_x = opParam;
+                       }
+                       break;
+
+               case AMODE_ILLEGAL:
+               case AMODE_MAX:
+                       // Illegal instruction
+                       assert(1==2);
+                       break;
+       }
+
+       // Return number of cycles executed
+       return opcode->cycles;
+}
+
diff --git a/kbmcu/m6805/m68emu.h b/kbmcu/m6805/m68emu.h
new file mode 100644 (file)
index 0000000..1b8efbb
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef M68EMU_H
+#define M68EMU_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef enum {
+       M68_CPU_HC05C4,
+       M68_CPU_HD6805V1
+} M68_CPUTYPE;
+
+typedef enum{
+  M68_INT_IRQ,
+  M68_INT_TIMER1
+} M68_INTERRUPT;
+
+
+struct M68_CTX;
+
+typedef uint8_t (*M68_READMEM_F)  (struct M68_CTX *ctx, const uint16_t addr);
+typedef void    (*M68_WRITEMEM_F) (struct M68_CTX *ctx, const uint16_t addr, const uint8_t data);
+typedef uint8_t (*M68_OPDECODE_F) (struct M68_CTX *ctx, const uint8_t value);
+
+
+/**
+ * Emulation context structure
+ */
+typedef struct M68_CTX {
+       uint8_t                 reg_acc;                                ///< Accumulator register
+       uint8_t                 reg_x;                                  ///< X-index register
+       uint16_t                reg_sp;                                 ///< Stack pointer
+       uint16_t                reg_pc;                                 ///< Program counter for current instruction
+       uint16_t                pc_next;                                ///< Program counter for next instruction
+       uint8_t                 reg_ccr;                                ///< Condition code register
+       M68_CPUTYPE             cpuType;                                ///< CPU type
+       bool                    irq;                                    ///< IRQ input state
+        uint8_t                 pending_interrupts;
+       uint16_t                sp_and, sp_or;                  ///< Stack pointer AND/OR masks
+       uint16_t                pc_and;                                 ///< Program counter AND mask
+       bool                    is_stopped;                             ///< True if processor is stopped
+       bool                    is_waiting;                             ///< True if processor is WAITing
+       M68_READMEM_F   read_mem;                               ///< Memory read callback
+       M68_WRITEMEM_F  write_mem;                              ///< Memory write callback
+       M68_OPDECODE_F  opdecode;                               ///< Opcode decode function, or NULL
+       bool                    trace;
+} M68_CTX;
+
+
+/* CCR bits */
+#define                M68_CCR_H       0x10            /* Half carry */
+#define                M68_CCR_I       0x08            /* Interrupt mask */
+#define                M68_CCR_N       0x04            /* Negative */
+#define                M68_CCR_C       0x02            /* Carry/borrow */
+#define                M68_CCR_Z       0x01            /* Zero */
+
+
+void m68_init(M68_CTX *ctx, const M68_CPUTYPE cpuType);
+void m68_reset(M68_CTX *ctx);
+uint64_t m68_exec_cycle(M68_CTX *ctx);
+void m68_set_interrupt_line(M68_CTX * ctx,M68_INTERRUPT i);
+
+
+#endif // M68EMU_H
index 9560ed4b71d64d98bcd2d62712f4617f1a8360a3..456bcbe910e6376a646f759508db8d81f97b9d52 100644 (file)
@@ -299,7 +299,7 @@ int record_key_direct(int kc, bool direct)
                if (currprefs.cpuboard_settings & 0x10) {
                        inputdevice_draco_key(kc);
                }
-               if (!currprefs.keyboard_connected) {
+               if (currprefs.keyboard_mode < 0) {
                        return 1;
                }
        }
diff --git a/mos6502.cpp b/mos6502.cpp
new file mode 100644 (file)
index 0000000..3db1c62
--- /dev/null
@@ -0,0 +1,1635 @@
+#include "mos6502.h"
+
+#define NEGATIVE  0x80
+#define OVERFLOW  0x40
+#define CONSTANT  0x20
+#define BREAK     0x10
+#define DECIMAL   0x08
+#define INTERRUPT 0x04
+#define ZERO      0x02
+#define CARRY     0x01
+
+#define SET_NEGATIVE(x) (x ? (status |= NEGATIVE) : (status &= (~NEGATIVE)) )
+#define SET_OVERFLOW(x) (x ? (status |= OVERFLOW) : (status &= (~OVERFLOW)) )
+//#define SET_CONSTANT(x) (x ? (status |= CONSTANT) : (status &= (~CONSTANT)) )
+//#define SET_BREAK(x) (x ? (status |= BREAK) : (status &= (~BREAK)) )
+#define SET_DECIMAL(x) (x ? (status |= DECIMAL) : (status &= (~DECIMAL)) )
+#define SET_INTERRUPT(x) (x ? (status |= INTERRUPT) : (status &= (~INTERRUPT)) )
+#define SET_ZERO(x) (x ? (status |= ZERO) : (status &= (~ZERO)) )
+#define SET_CARRY(x) (x ? (status |= CARRY) : (status &= (~CARRY)) )
+
+#define IF_NEGATIVE() ((status & NEGATIVE) ? true : false)
+#define IF_OVERFLOW() ((status & OVERFLOW) ? true : false)
+#define IF_CONSTANT() ((status & CONSTANT) ? true : false)
+#define IF_BREAK() ((status & BREAK) ? true : false)
+#define IF_DECIMAL() ((status & DECIMAL) ? true : false)
+#define IF_INTERRUPT() ((status & INTERRUPT) ? true : false)
+#define IF_ZERO() ((status & ZERO) ? true : false)
+#define IF_CARRY() ((status & CARRY) ? true : false)
+
+mos6502::Instr mos6502::InstrTable[256];
+
+mos6502::mos6502(BusRead r, BusWrite w, ClockCycle c)
+       : reset_A(0x00)
+    , reset_X(0x00)
+    , reset_Y(0x00)
+    , reset_sp(0xFD)
+    , reset_status(CONSTANT)
+{
+       Write = (BusWrite)w;
+       Read = (BusRead)r;
+       Cycle = (ClockCycle)c;
+
+       static bool initialized = false;
+       if (initialized) return;
+       initialized = true;
+
+       Instr instr;
+       // fill jump table with ILLEGALs
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_ILLEGAL;
+       for(int i = 0; i < 256; i++)
+       {
+               InstrTable[i] = instr;
+       }
+
+       // insert opcodes
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_ADC;
+       instr.cycles = 2;
+       InstrTable[0x69] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_ADC;
+       instr.cycles = 4;
+       InstrTable[0x6D] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_ADC;
+       instr.cycles = 3;
+       InstrTable[0x65] = instr;
+       instr.addr = &mos6502::Addr_INX;
+       instr.code = &mos6502::Op_ADC;
+       instr.cycles = 6;
+       InstrTable[0x61] = instr;
+       instr.addr = &mos6502::Addr_INY;
+       instr.code = &mos6502::Op_ADC;
+       instr.cycles = 6;
+       InstrTable[0x71] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_ADC;
+       instr.cycles = 4;
+       InstrTable[0x75] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_ADC;
+       instr.cycles = 4;
+       InstrTable[0x7D] = instr;
+       instr.addr = &mos6502::Addr_ABY;
+       instr.code = &mos6502::Op_ADC;
+       instr.cycles = 4;
+       InstrTable[0x79] = instr;
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_AND;
+       instr.cycles = 2;
+       InstrTable[0x29] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_AND;
+       instr.cycles = 4;
+       InstrTable[0x2D] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_AND;
+       instr.cycles = 3;
+       InstrTable[0x25] = instr;
+       instr.addr = &mos6502::Addr_INX;
+       instr.code = &mos6502::Op_AND;
+       instr.cycles = 6;
+       InstrTable[0x21] = instr;
+       instr.addr = &mos6502::Addr_INY;
+       instr.code = &mos6502::Op_AND;
+       instr.cycles = 5;
+       InstrTable[0x31] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_AND;
+       instr.cycles = 4;
+       InstrTable[0x35] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_AND;
+       instr.cycles = 4;
+       InstrTable[0x3D] = instr;
+       instr.addr = &mos6502::Addr_ABY;
+       instr.code = &mos6502::Op_AND;
+       instr.cycles = 4;
+       InstrTable[0x39] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_ASL;
+       instr.cycles = 6;
+       InstrTable[0x0E] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_ASL;
+       instr.cycles = 5;
+       InstrTable[0x06] = instr;
+       instr.addr = &mos6502::Addr_ACC;
+       instr.code = &mos6502::Op_ASL_ACC;
+       instr.cycles = 2;
+       InstrTable[0x0A] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_ASL;
+       instr.cycles = 6;
+       InstrTable[0x16] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_ASL;
+       instr.cycles = 7;
+       InstrTable[0x1E] = instr;
+
+       instr.addr = &mos6502::Addr_REL;
+       instr.code = &mos6502::Op_BCC;
+       instr.cycles = 2;
+       InstrTable[0x90] = instr;
+
+       instr.addr = &mos6502::Addr_REL;
+       instr.code = &mos6502::Op_BCS;
+       instr.cycles = 2;
+       InstrTable[0xB0] = instr;
+
+       instr.addr = &mos6502::Addr_REL;
+       instr.code = &mos6502::Op_BEQ;
+       instr.cycles = 2;
+       InstrTable[0xF0] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_BIT;
+       instr.cycles = 4;
+       InstrTable[0x2C] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_BIT;
+       instr.cycles = 3;
+       InstrTable[0x24] = instr;
+
+       instr.addr = &mos6502::Addr_REL;
+       instr.code = &mos6502::Op_BMI;
+       instr.cycles = 2;
+       InstrTable[0x30] = instr;
+
+       instr.addr = &mos6502::Addr_REL;
+       instr.code = &mos6502::Op_BNE;
+       instr.cycles = 2;
+       InstrTable[0xD0] = instr;
+
+       instr.addr = &mos6502::Addr_REL;
+       instr.code = &mos6502::Op_BPL;
+       instr.cycles = 2;
+       InstrTable[0x10] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_BRK;
+       instr.cycles = 7;
+       InstrTable[0x00] = instr;
+
+       instr.addr = &mos6502::Addr_REL;
+       instr.code = &mos6502::Op_BVC;
+       instr.cycles = 2;
+       InstrTable[0x50] = instr;
+
+       instr.addr = &mos6502::Addr_REL;
+       instr.code = &mos6502::Op_BVS;
+       instr.cycles = 2;
+       InstrTable[0x70] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_CLC;
+       instr.cycles = 2;
+       InstrTable[0x18] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_CLD;
+       instr.cycles = 2;
+       InstrTable[0xD8] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_CLI;
+       instr.cycles = 2;
+       InstrTable[0x58] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_CLV;
+       instr.cycles = 2;
+       InstrTable[0xB8] = instr;
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_CMP;
+       instr.cycles = 2;
+       InstrTable[0xC9] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_CMP;
+       instr.cycles = 4;
+       InstrTable[0xCD] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_CMP;
+       instr.cycles = 3;
+       InstrTable[0xC5] = instr;
+       instr.addr = &mos6502::Addr_INX;
+       instr.code = &mos6502::Op_CMP;
+       instr.cycles = 6;
+       InstrTable[0xC1] = instr;
+       instr.addr = &mos6502::Addr_INY;
+       instr.code = &mos6502::Op_CMP;
+       instr.cycles = 3;
+       InstrTable[0xD1] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_CMP;
+       instr.cycles = 4;
+       InstrTable[0xD5] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_CMP;
+       instr.cycles = 4;
+       InstrTable[0xDD] = instr;
+       instr.addr = &mos6502::Addr_ABY;
+       instr.code = &mos6502::Op_CMP;
+       instr.cycles = 4;
+       InstrTable[0xD9] = instr;
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_CPX;
+       instr.cycles = 2;
+       InstrTable[0xE0] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_CPX;
+       instr.cycles = 4;
+       InstrTable[0xEC] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_CPX;
+       instr.cycles = 3;
+       InstrTable[0xE4] = instr;
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_CPY;
+       instr.cycles = 2;
+       InstrTable[0xC0] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_CPY;
+       instr.cycles = 4;
+       InstrTable[0xCC] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_CPY;
+       instr.cycles = 3;
+       InstrTable[0xC4] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_DEC;
+       instr.cycles = 6;
+       InstrTable[0xCE] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_DEC;
+       instr.cycles = 5;
+       InstrTable[0xC6] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_DEC;
+       instr.cycles = 6;
+       InstrTable[0xD6] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_DEC;
+       instr.cycles = 7;
+       InstrTable[0xDE] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_DEX;
+       instr.cycles = 2;
+       InstrTable[0xCA] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_DEY;
+       instr.cycles = 2;
+       InstrTable[0x88] = instr;
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_EOR;
+       instr.cycles = 2;
+       InstrTable[0x49] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_EOR;
+       instr.cycles = 4;
+       InstrTable[0x4D] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_EOR;
+       instr.cycles = 3;
+       InstrTable[0x45] = instr;
+       instr.addr = &mos6502::Addr_INX;
+       instr.code = &mos6502::Op_EOR;
+       instr.cycles = 6;
+       InstrTable[0x41] = instr;
+       instr.addr = &mos6502::Addr_INY;
+       instr.code = &mos6502::Op_EOR;
+       instr.cycles = 5;
+       InstrTable[0x51] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_EOR;
+       instr.cycles = 4;
+       InstrTable[0x55] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_EOR;
+       instr.cycles = 4;
+       InstrTable[0x5D] = instr;
+       instr.addr = &mos6502::Addr_ABY;
+       instr.code = &mos6502::Op_EOR;
+       instr.cycles = 4;
+       InstrTable[0x59] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_INC;
+       instr.cycles = 6;
+       InstrTable[0xEE] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_INC;
+       instr.cycles = 5;
+       InstrTable[0xE6] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_INC;
+       instr.cycles = 6;
+       InstrTable[0xF6] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_INC;
+       instr.cycles = 7;
+       InstrTable[0xFE] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_INX;
+       instr.cycles = 2;
+       InstrTable[0xE8] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_INY;
+       instr.cycles = 2;
+       InstrTable[0xC8] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_JMP;
+       instr.cycles = 3;
+       InstrTable[0x4C] = instr;
+       instr.addr = &mos6502::Addr_ABI;
+       instr.code = &mos6502::Op_JMP;
+       instr.cycles = 5;
+       InstrTable[0x6C] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_JSR;
+       instr.cycles = 6;
+       InstrTable[0x20] = instr;
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_LDA;
+       instr.cycles = 2;
+       InstrTable[0xA9] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_LDA;
+       instr.cycles = 4;
+       InstrTable[0xAD] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_LDA;
+       instr.cycles = 3;
+       InstrTable[0xA5] = instr;
+       instr.addr = &mos6502::Addr_INX;
+       instr.code = &mos6502::Op_LDA;
+       instr.cycles = 6;
+       InstrTable[0xA1] = instr;
+       instr.addr = &mos6502::Addr_INY;
+       instr.code = &mos6502::Op_LDA;
+       instr.cycles = 5;
+       InstrTable[0xB1] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_LDA;
+       instr.cycles = 4;
+       InstrTable[0xB5] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_LDA;
+       instr.cycles = 4;
+       InstrTable[0xBD] = instr;
+       instr.addr = &mos6502::Addr_ABY;
+       instr.code = &mos6502::Op_LDA;
+       instr.cycles = 4;
+       InstrTable[0xB9] = instr;
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_LDX;
+       instr.cycles = 2;
+       InstrTable[0xA2] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_LDX;
+       instr.cycles = 4;
+       InstrTable[0xAE] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_LDX;
+       instr.cycles = 3;
+       InstrTable[0xA6] = instr;
+       instr.addr = &mos6502::Addr_ABY;
+       instr.code = &mos6502::Op_LDX;
+       instr.cycles = 4;
+       InstrTable[0xBE] = instr;
+       instr.addr = &mos6502::Addr_ZEY;
+       instr.code = &mos6502::Op_LDX;
+       instr.cycles = 4;
+       InstrTable[0xB6] = instr;
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_LDY;
+       instr.cycles = 2;
+       InstrTable[0xA0] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_LDY;
+       instr.cycles = 4;
+       InstrTable[0xAC] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_LDY;
+       instr.cycles = 3;
+       InstrTable[0xA4] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_LDY;
+       instr.cycles = 4;
+       InstrTable[0xB4] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_LDY;
+       instr.cycles = 4;
+       InstrTable[0xBC] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_LSR;
+       instr.cycles = 6;
+       InstrTable[0x4E] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_LSR;
+       instr.cycles = 5;
+       InstrTable[0x46] = instr;
+       instr.addr = &mos6502::Addr_ACC;
+       instr.code = &mos6502::Op_LSR_ACC;
+       instr.cycles = 2;
+       InstrTable[0x4A] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_LSR;
+       instr.cycles = 6;
+       InstrTable[0x56] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_LSR;
+       instr.cycles = 7;
+       InstrTable[0x5E] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_NOP;
+       instr.cycles = 2;
+       InstrTable[0xEA] = instr;
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_ORA;
+       instr.cycles = 2;
+       InstrTable[0x09] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_ORA;
+       instr.cycles = 4;
+       InstrTable[0x0D] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_ORA;
+       instr.cycles = 3;
+       InstrTable[0x05] = instr;
+       instr.addr = &mos6502::Addr_INX;
+       instr.code = &mos6502::Op_ORA;
+       instr.cycles = 6;
+       InstrTable[0x01] = instr;
+       instr.addr = &mos6502::Addr_INY;
+       instr.code = &mos6502::Op_ORA;
+       instr.cycles = 5;
+       InstrTable[0x11] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_ORA;
+       instr.cycles = 4;
+       InstrTable[0x15] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_ORA;
+       instr.cycles = 4;
+       InstrTable[0x1D] = instr;
+       instr.addr = &mos6502::Addr_ABY;
+       instr.code = &mos6502::Op_ORA;
+       instr.cycles = 4;
+       InstrTable[0x19] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_PHA;
+       instr.cycles = 3;
+       InstrTable[0x48] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_PHP;
+       instr.cycles = 3;
+       InstrTable[0x08] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_PLA;
+       instr.cycles = 4;
+       InstrTable[0x68] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_PLP;
+       instr.cycles = 4;
+       InstrTable[0x28] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_ROL;
+       instr.cycles = 6;
+       InstrTable[0x2E] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_ROL;
+       instr.cycles = 5;
+       InstrTable[0x26] = instr;
+       instr.addr = &mos6502::Addr_ACC;
+       instr.code = &mos6502::Op_ROL_ACC;
+       instr.cycles = 2;
+       InstrTable[0x2A] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_ROL;
+       instr.cycles = 6;
+       InstrTable[0x36] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_ROL;
+       instr.cycles = 7;
+       InstrTable[0x3E] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_ROR;
+       instr.cycles = 6;
+       InstrTable[0x6E] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_ROR;
+       instr.cycles = 5;
+       InstrTable[0x66] = instr;
+       instr.addr = &mos6502::Addr_ACC;
+       instr.code = &mos6502::Op_ROR_ACC;
+       instr.cycles = 2;
+       InstrTable[0x6A] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_ROR;
+       instr.cycles = 6;
+       InstrTable[0x76] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_ROR;
+       instr.cycles = 7;
+       InstrTable[0x7E] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_RTI;
+       instr.cycles = 6;
+       InstrTable[0x40] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_RTS;
+       instr.cycles = 6;
+       InstrTable[0x60] = instr;
+
+       instr.addr = &mos6502::Addr_IMM;
+       instr.code = &mos6502::Op_SBC;
+       instr.cycles = 2;
+       InstrTable[0xE9] = instr;
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_SBC;
+       instr.cycles = 4;
+       InstrTable[0xED] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_SBC;
+       instr.cycles = 3;
+       InstrTable[0xE5] = instr;
+       instr.addr = &mos6502::Addr_INX;
+       instr.code = &mos6502::Op_SBC;
+       instr.cycles = 6;
+       InstrTable[0xE1] = instr;
+       instr.addr = &mos6502::Addr_INY;
+       instr.code = &mos6502::Op_SBC;
+       instr.cycles = 5;
+       InstrTable[0xF1] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_SBC;
+       instr.cycles = 4;
+       InstrTable[0xF5] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_SBC;
+       instr.cycles = 4;
+       InstrTable[0xFD] = instr;
+       instr.addr = &mos6502::Addr_ABY;
+       instr.code = &mos6502::Op_SBC;
+       instr.cycles = 4;
+       InstrTable[0xF9] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_SEC;
+       instr.cycles = 2;
+       InstrTable[0x38] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_SED;
+       instr.cycles = 2;
+       InstrTable[0xF8] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_SEI;
+       instr.cycles = 2;
+       InstrTable[0x78] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_STA;
+       instr.cycles = 4;
+       InstrTable[0x8D] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_STA;
+       instr.cycles = 3;
+       InstrTable[0x85] = instr;
+       instr.addr = &mos6502::Addr_INX;
+       instr.code = &mos6502::Op_STA;
+       instr.cycles = 6;
+       InstrTable[0x81] = instr;
+       instr.addr = &mos6502::Addr_INY;
+       instr.code = &mos6502::Op_STA;
+       instr.cycles = 6;
+       InstrTable[0x91] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_STA;
+       instr.cycles = 4;
+       InstrTable[0x95] = instr;
+       instr.addr = &mos6502::Addr_ABX;
+       instr.code = &mos6502::Op_STA;
+       instr.cycles = 5;
+       InstrTable[0x9D] = instr;
+       instr.addr = &mos6502::Addr_ABY;
+       instr.code = &mos6502::Op_STA;
+       instr.cycles = 5;
+       InstrTable[0x99] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_STX;
+       instr.cycles = 4;
+       InstrTable[0x8E] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_STX;
+       instr.cycles = 3;
+       InstrTable[0x86] = instr;
+       instr.addr = &mos6502::Addr_ZEY;
+       instr.code = &mos6502::Op_STX;
+       instr.cycles = 4;
+       InstrTable[0x96] = instr;
+
+       instr.addr = &mos6502::Addr_ABS;
+       instr.code = &mos6502::Op_STY;
+       instr.cycles = 4;
+       InstrTable[0x8C] = instr;
+       instr.addr = &mos6502::Addr_ZER;
+       instr.code = &mos6502::Op_STY;
+       instr.cycles = 3;
+       InstrTable[0x84] = instr;
+       instr.addr = &mos6502::Addr_ZEX;
+       instr.code = &mos6502::Op_STY;
+       instr.cycles = 4;
+       InstrTable[0x94] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_TAX;
+       instr.cycles = 2;
+       InstrTable[0xAA] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_TAY;
+       instr.cycles = 2;
+       InstrTable[0xA8] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_TSX;
+       instr.cycles = 2;
+       InstrTable[0xBA] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_TXA;
+       instr.cycles = 2;
+       InstrTable[0x8A] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_TXS;
+       instr.cycles = 2;
+       InstrTable[0x9A] = instr;
+
+       instr.addr = &mos6502::Addr_IMP;
+       instr.code = &mos6502::Op_TYA;
+       instr.cycles = 2;
+       InstrTable[0x98] = instr;
+
+       return;
+}
+
+uint16_t mos6502::Addr_ACC()
+{
+       return 0; // not used
+}
+
+uint16_t mos6502::Addr_IMM()
+{
+       return pc++;
+}
+
+uint16_t mos6502::Addr_ABS()
+{
+       uint16_t addrL;
+       uint16_t addrH;
+       uint16_t addr;
+
+       addrL = Read(pc++);
+       addrH = Read(pc++);
+
+       addr = addrL + (addrH << 8);
+
+       return addr;
+}
+
+uint16_t mos6502::Addr_ZER()
+{
+       return Read(pc++);
+}
+
+uint16_t mos6502::Addr_IMP()
+{
+       return 0; // not used
+}
+
+uint16_t mos6502::Addr_REL()
+{
+       uint16_t offset;
+       uint16_t addr;
+
+       offset = (uint16_t)Read(pc++);
+       if (offset & 0x80) offset |= 0xFF00;
+       addr = pc + (int16_t)offset;
+       return addr;
+}
+
+uint16_t mos6502::Addr_ABI()
+{
+       uint16_t addrL;
+       uint16_t addrH;
+       uint16_t effL;
+       uint16_t effH;
+       uint16_t abs;
+       uint16_t addr;
+
+       addrL = Read(pc++);
+       addrH = Read(pc++);
+
+       abs = (addrH << 8) | addrL;
+
+       effL = Read(abs);
+
+#ifndef CMOS_INDIRECT_JMP_FIX
+       effH = Read((abs & 0xFF00) + ((abs + 1) & 0x00FF) );
+#else
+       effH = Read(abs + 1);
+#endif
+
+       addr = effL + 0x100 * effH;
+
+       return addr;
+}
+
+uint16_t mos6502::Addr_ZEX()
+{
+       uint16_t addr = (Read(pc++) + X) & 0xFF;
+       return addr;
+}
+
+uint16_t mos6502::Addr_ZEY()
+{
+       uint16_t addr = (Read(pc++) + Y) & 0xFF;
+       return addr;
+}
+
+uint16_t mos6502::Addr_ABX()
+{
+       uint16_t addr;
+       uint16_t addrL;
+       uint16_t addrH;
+
+       addrL = Read(pc++);
+       addrH = Read(pc++);
+
+       addr = addrL + (addrH << 8) + X;
+       return addr;
+}
+
+uint16_t mos6502::Addr_ABY()
+{
+       uint16_t addr;
+       uint16_t addrL;
+       uint16_t addrH;
+
+       addrL = Read(pc++);
+       addrH = Read(pc++);
+
+       addr = addrL + (addrH << 8) + Y;
+       return addr;
+}
+
+
+uint16_t mos6502::Addr_INX()
+{
+       uint16_t zeroL;
+       uint16_t zeroH;
+       uint16_t addr;
+
+       zeroL = (Read(pc++) + X) & 0xFF;
+       zeroH = (zeroL + 1) & 0xFF;
+       addr = Read(zeroL) + (Read(zeroH) << 8);
+
+       return addr;
+}
+
+uint16_t mos6502::Addr_INY()
+{
+       uint16_t zeroL;
+       uint16_t zeroH;
+       uint16_t addr;
+
+       zeroL = Read(pc++);
+       zeroH = (zeroL + 1) & 0xFF;
+       addr = Read(zeroL) + (Read(zeroH) << 8) + Y;
+
+       return addr;
+}
+
+void mos6502::SetPC(uint16_t value)
+{
+       pc = value;
+}
+
+void mos6502::Reset()
+{
+       A = reset_A;
+       Y = reset_Y;
+       X = reset_X;
+
+       // load PC from reset vector
+       uint8_t pcl = Read(rstVectorL);
+       uint8_t pch = Read(rstVectorH);
+       pc = (pch << 8) + pcl;
+
+       sp = reset_sp;
+
+       status = reset_status | CONSTANT | BREAK;
+
+       illegalOpcode = false;
+
+       return;
+}
+
+void mos6502::StackPush(uint8_t byte)
+{
+       Write(0x0100 + sp, byte);
+       if(sp == 0x00) sp = 0xFF;
+       else sp--;
+}
+
+uint8_t mos6502::StackPop()
+{
+       if(sp == 0xFF) sp = 0x00;
+       else sp++;
+       return Read(0x0100 + sp);
+}
+
+void mos6502::IRQ()
+{
+       if(!IF_INTERRUPT())
+       {
+               //SET_BREAK(0);
+               StackPush((pc >> 8) & 0xFF);
+               StackPush(pc & 0xFF);
+               StackPush((status & ~BREAK) | CONSTANT);
+               SET_INTERRUPT(1);
+
+               // load PC from interrupt request vector
+               uint8_t pcl = Read(irqVectorL);
+               uint8_t pch = Read(irqVectorH);
+               pc = (pch << 8) + pcl;
+       }
+       return;
+}
+
+void mos6502::NMI()
+{
+       //SET_BREAK(0);
+       StackPush((pc >> 8) & 0xFF);
+       StackPush(pc & 0xFF);
+       StackPush((status & ~BREAK) | CONSTANT);
+       SET_INTERRUPT(1);
+
+       // load PC from non-maskable interrupt vector
+       uint8_t pcl = Read(nmiVectorL);
+       uint8_t pch = Read(nmiVectorH);
+       pc = (pch << 8) + pcl;
+       return;
+}
+
+void mos6502::Run(
+       int32_t cyclesRemaining,
+       uint64_t& cycleCount,
+       CycleMethod cycleMethod
+) {
+       uint8_t opcode;
+       Instr instr;
+
+       while(cyclesRemaining > 0 && !illegalOpcode)
+       {
+               // fetch
+               opcode = Read(pc++);
+
+               // decode
+               instr = InstrTable[opcode];
+
+               // execute
+               Exec(instr);
+               cycleCount += instr.cycles;
+               cyclesRemaining -=
+                       cycleMethod == CYCLE_COUNT        ? instr.cycles
+                       /* cycleMethod == INST_COUNT */   : 1;
+
+               // run clock cycle callback
+               if (Cycle)
+                       for(int i = 0; i < instr.cycles; i++)
+                               Cycle(this);
+       }
+}
+
+void mos6502::RunEternally()
+{
+       uint8_t opcode;
+       Instr instr;
+
+       while(!illegalOpcode)
+       {
+               // fetch
+               opcode = Read(pc++);
+
+               // decode
+               instr = InstrTable[opcode];
+
+               // execute
+               Exec(instr);
+
+               // run clock cycle callback
+               if (Cycle)
+                       for(int i = 0; i < instr.cycles; i++)
+                               Cycle(this);
+       }
+}
+
+void mos6502::Exec(Instr i)
+{
+       uint16_t src = (this->*i.addr)();
+       (this->*i.code)(src);
+}
+
+uint16_t mos6502::GetPC()
+{
+    return pc;
+}
+
+uint8_t mos6502::GetS()
+{
+    return sp;
+}
+
+uint8_t mos6502::GetP()
+{
+    return status;
+}
+
+uint8_t mos6502::GetA()
+{
+    return A;
+}
+
+uint8_t mos6502::GetX()
+{
+    return X;
+}
+
+uint8_t mos6502::GetY()
+{
+    return Y;
+}
+
+void mos6502::SetResetS(uint8_t value)
+{
+    reset_sp = value;
+}
+
+void mos6502::SetResetP(uint8_t value)
+{
+    reset_status = value | CONSTANT | BREAK;
+}
+
+void mos6502::SetResetA(uint8_t value)
+{
+    reset_A = value;
+}
+
+void mos6502::SetResetX(uint8_t value)
+{
+    reset_X = value;
+}
+
+void mos6502::SetResetY(uint8_t value)
+{
+    reset_Y = value;
+}
+
+uint8_t mos6502::GetResetS()
+{
+    return reset_sp;
+}
+
+uint8_t mos6502::GetResetP()
+{
+    return reset_status;
+}
+
+uint8_t mos6502::GetResetA()
+{
+    return reset_A;
+}
+
+uint8_t mos6502::GetResetX()
+{
+    return reset_X;
+}
+
+uint8_t mos6502::GetResetY()
+{
+    return reset_Y;
+}
+
+void mos6502::SetP(uint8_t value)
+{
+       status = value;
+}
+
+void mos6502::Op_ILLEGAL(uint16_t src)
+{
+       illegalOpcode = true;
+}
+
+
+void mos6502::Op_ADC(uint16_t src)
+{
+       uint8_t m = Read(src);
+       unsigned int tmp = m + A + (IF_CARRY() ? 1 : 0);
+       SET_ZERO(!(tmp & 0xFF));
+       if (IF_DECIMAL())
+       {
+               if (((A & 0xF) + (m & 0xF) + (IF_CARRY() ? 1 : 0)) > 9) tmp += 6;
+               SET_NEGATIVE(tmp & 0x80);
+               SET_OVERFLOW(!((A ^ m) & 0x80) && ((A ^ tmp) & 0x80));
+               if (tmp > 0x99)
+               {
+                       tmp += 96;
+               }
+               SET_CARRY(tmp > 0x99);
+       }
+       else
+       {
+               SET_NEGATIVE(tmp & 0x80);
+               SET_OVERFLOW(!((A ^ m) & 0x80) && ((A ^ tmp) & 0x80));
+               SET_CARRY(tmp > 0xFF);
+       }
+
+       A = tmp & 0xFF;
+       return;
+}
+
+
+
+void mos6502::Op_AND(uint16_t src)
+{
+       uint8_t m = Read(src);
+       uint8_t res = m & A;
+       SET_NEGATIVE(res & 0x80);
+       SET_ZERO(!res);
+       A = res;
+       return;
+}
+
+
+void mos6502::Op_ASL(uint16_t src)
+{
+       uint8_t m = Read(src);
+       SET_CARRY(m & 0x80);
+       m <<= 1;
+       m &= 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       Write(src, m);
+       return;
+}
+
+void mos6502::Op_ASL_ACC(uint16_t src)
+{
+       uint8_t m = A;
+       SET_CARRY(m & 0x80);
+       m <<= 1;
+       m &= 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       A = m;
+       return;
+}
+
+void mos6502::Op_BCC(uint16_t src)
+{
+       if (!IF_CARRY())
+       {
+               pc = src;
+       }
+       return;
+}
+
+
+void mos6502::Op_BCS(uint16_t src)
+{
+       if (IF_CARRY())
+       {
+               pc = src;
+       }
+       return;
+}
+
+void mos6502::Op_BEQ(uint16_t src)
+{
+       if (IF_ZERO())
+       {
+               pc = src;
+       }
+       return;
+}
+
+void mos6502::Op_BIT(uint16_t src)
+{
+       uint8_t m = Read(src);
+       uint8_t res = m & A;
+       SET_NEGATIVE(res & 0x80);
+       status = (status & 0x3F) | (uint8_t)(m & 0xC0) | CONSTANT | BREAK;
+       SET_ZERO(!res);
+       return;
+}
+
+void mos6502::Op_BMI(uint16_t src)
+{
+       if (IF_NEGATIVE())
+       {
+               pc = src;
+       }
+       return;
+}
+
+void mos6502::Op_BNE(uint16_t src)
+{
+       if (!IF_ZERO())
+       {
+               pc = src;
+       }
+       return;
+}
+
+void mos6502::Op_BPL(uint16_t src)
+{
+       if (!IF_NEGATIVE())
+       {
+               pc = src;
+       }
+       return;
+}
+
+void mos6502::Op_BRK(uint16_t src)
+{
+       pc++;
+       StackPush((pc >> 8) & 0xFF);
+       StackPush(pc & 0xFF);
+       StackPush(status | CONSTANT | BREAK);
+       SET_INTERRUPT(1);
+       pc = (Read(irqVectorH) << 8) + Read(irqVectorL);
+       return;
+}
+
+void mos6502::Op_BVC(uint16_t src)
+{
+       if (!IF_OVERFLOW())
+       {
+               pc = src;
+       }
+       return;
+}
+
+void mos6502::Op_BVS(uint16_t src)
+{
+       if (IF_OVERFLOW())
+       {
+               pc = src;
+       }
+       return;
+}
+
+void mos6502::Op_CLC(uint16_t src)
+{
+       SET_CARRY(0);
+       return;
+}
+
+void mos6502::Op_CLD(uint16_t src)
+{
+       SET_DECIMAL(0);
+       return;
+}
+
+void mos6502::Op_CLI(uint16_t src)
+{
+       SET_INTERRUPT(0);
+       return;
+}
+
+void mos6502::Op_CLV(uint16_t src)
+{
+       SET_OVERFLOW(0);
+       return;
+}
+
+void mos6502::Op_CMP(uint16_t src)
+{
+       unsigned int tmp = A - Read(src);
+       SET_CARRY(tmp < 0x100);
+       SET_NEGATIVE(tmp & 0x80);
+       SET_ZERO(!(tmp & 0xFF));
+       return;
+}
+
+void mos6502::Op_CPX(uint16_t src)
+{
+       unsigned int tmp = X - Read(src);
+       SET_CARRY(tmp < 0x100);
+       SET_NEGATIVE(tmp & 0x80);
+       SET_ZERO(!(tmp & 0xFF));
+       return;
+}
+
+void mos6502::Op_CPY(uint16_t src)
+{
+       unsigned int tmp = Y - Read(src);
+       SET_CARRY(tmp < 0x100);
+       SET_NEGATIVE(tmp & 0x80);
+       SET_ZERO(!(tmp & 0xFF));
+       return;
+}
+
+void mos6502::Op_DEC(uint16_t src)
+{
+       uint8_t m = Read(src);
+       m = (m - 1) & 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       Write(src, m);
+       return;
+}
+
+void mos6502::Op_DEX(uint16_t src)
+{
+       uint8_t m = X;
+       m = (m - 1) & 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       X = m;
+       return;
+}
+
+void mos6502::Op_DEY(uint16_t src)
+{
+       uint8_t m = Y;
+       m = (m - 1) & 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       Y = m;
+       return;
+}
+
+void mos6502::Op_EOR(uint16_t src)
+{
+       uint8_t m = Read(src);
+       m = A ^ m;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       A = m;
+}
+
+void mos6502::Op_INC(uint16_t src)
+{
+       uint8_t m = Read(src);
+       m = (m + 1) & 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       Write(src, m);
+}
+
+void mos6502::Op_INX(uint16_t src)
+{
+       uint8_t m = X;
+       m = (m + 1) & 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       X = m;
+}
+
+void mos6502::Op_INY(uint16_t src)
+{
+       uint8_t m = Y;
+       m = (m + 1) & 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       Y = m;
+}
+
+void mos6502::Op_JMP(uint16_t src)
+{
+       pc = src;
+}
+
+void mos6502::Op_JSR(uint16_t src)
+{
+       pc--;
+       StackPush((pc >> 8) & 0xFF);
+       StackPush(pc & 0xFF);
+       pc = src;
+}
+
+void mos6502::Op_LDA(uint16_t src)
+{
+       uint8_t m = Read(src);
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       A = m;
+}
+
+void mos6502::Op_LDX(uint16_t src)
+{
+       uint8_t m = Read(src);
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       X = m;
+}
+
+void mos6502::Op_LDY(uint16_t src)
+{
+       uint8_t m = Read(src);
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       Y = m;
+}
+
+void mos6502::Op_LSR(uint16_t src)
+{
+       uint8_t m = Read(src);
+       SET_CARRY(m & 0x01);
+       m >>= 1;
+       SET_NEGATIVE(0);
+       SET_ZERO(!m);
+       Write(src, m);
+}
+
+void mos6502::Op_LSR_ACC(uint16_t src)
+{
+       uint8_t m = A;
+       SET_CARRY(m & 0x01);
+       m >>= 1;
+       SET_NEGATIVE(0);
+       SET_ZERO(!m);
+       A = m;
+}
+
+void mos6502::Op_NOP(uint16_t src)
+{
+       return;
+}
+
+void mos6502::Op_ORA(uint16_t src)
+{
+       uint8_t m = Read(src);
+       m = A | m;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       A = m;
+}
+
+void mos6502::Op_PHA(uint16_t src)
+{
+       StackPush(A);
+       return;
+}
+
+void mos6502::Op_PHP(uint16_t src)
+{
+       StackPush(status | CONSTANT | BREAK);
+       return;
+}
+
+void mos6502::Op_PLA(uint16_t src)
+{
+       A = StackPop();
+       SET_NEGATIVE(A & 0x80);
+       SET_ZERO(!A);
+       return;
+}
+
+void mos6502::Op_PLP(uint16_t src)
+{
+       status = StackPop() | CONSTANT | BREAK;
+       //SET_CONSTANT(1);
+       return;
+}
+
+void mos6502::Op_ROL(uint16_t src)
+{
+       uint16_t m = Read(src);
+       m <<= 1;
+       if (IF_CARRY()) m |= 0x01;
+       SET_CARRY(m > 0xFF);
+       m &= 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       Write(src, m);
+       return;
+}
+
+void mos6502::Op_ROL_ACC(uint16_t src)
+{
+       uint16_t m = A;
+       m <<= 1;
+       if (IF_CARRY()) m |= 0x01;
+       SET_CARRY(m > 0xFF);
+       m &= 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       A = m;
+       return;
+}
+
+void mos6502::Op_ROR(uint16_t src)
+{
+       uint16_t m = Read(src);
+       if (IF_CARRY()) m |= 0x100;
+       SET_CARRY(m & 0x01);
+       m >>= 1;
+       m &= 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       Write(src, m);
+       return;
+}
+
+void mos6502::Op_ROR_ACC(uint16_t src)
+{
+       uint16_t m = A;
+       if (IF_CARRY()) m |= 0x100;
+       SET_CARRY(m & 0x01);
+       m >>= 1;
+       m &= 0xFF;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       A = m;
+       return;
+}
+
+void mos6502::Op_RTI(uint16_t src)
+{
+       uint8_t lo, hi;
+
+       status = StackPop() | CONSTANT | BREAK;
+
+       lo = StackPop();
+       hi = StackPop();
+
+       pc = (hi << 8) | lo;
+       return;
+}
+
+void mos6502::Op_RTS(uint16_t src)
+{
+       uint8_t lo, hi;
+
+       lo = StackPop();
+       hi = StackPop();
+
+       pc = ((hi << 8) | lo) + 1;
+       return;
+}
+
+void mos6502::Op_SBC(uint16_t src)
+{
+       uint8_t m = Read(src);
+       unsigned int tmp = A - m - (IF_CARRY() ? 0 : 1);
+       SET_NEGATIVE(tmp & 0x80);
+       SET_ZERO(!(tmp & 0xFF));
+       SET_OVERFLOW(((A ^ tmp) & 0x80) && ((A ^ m) & 0x80));
+
+       if (IF_DECIMAL())
+       {
+               if ( ((A & 0x0F) - (IF_CARRY() ? 0 : 1)) < (m & 0x0F)) tmp -= 6;
+               if (tmp > 0x99)
+               {
+                       tmp -= 0x60;
+               }
+       }
+       SET_CARRY(tmp < 0x100);
+       A = (tmp & 0xFF);
+       return;
+}
+
+void mos6502::Op_SEC(uint16_t src)
+{
+       SET_CARRY(1);
+       return;
+}
+
+void mos6502::Op_SED(uint16_t src)
+{
+       SET_DECIMAL(1);
+       return;
+}
+
+void mos6502::Op_SEI(uint16_t src)
+{
+       SET_INTERRUPT(1);
+       return;
+}
+
+void mos6502::Op_STA(uint16_t src)
+{
+       Write(src, A);
+       return;
+}
+
+void mos6502::Op_STX(uint16_t src)
+{
+       Write(src, X);
+       return;
+}
+
+void mos6502::Op_STY(uint16_t src)
+{
+       Write(src, Y);
+       return;
+}
+
+void mos6502::Op_TAX(uint16_t src)
+{
+       uint8_t m = A;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       X = m;
+       return;
+}
+
+void mos6502::Op_TAY(uint16_t src)
+{
+       uint8_t m = A;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       Y = m;
+       return;
+}
+
+void mos6502::Op_TSX(uint16_t src)
+{
+       uint8_t m = sp;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       X = m;
+       return;
+}
+
+void mos6502::Op_TXA(uint16_t src)
+{
+       uint8_t m = X;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       A = m;
+       return;
+}
+
+void mos6502::Op_TXS(uint16_t src)
+{
+       sp = X;
+       return;
+}
+
+void mos6502::Op_TYA(uint16_t src)
+{
+       uint8_t m = Y;
+       SET_NEGATIVE(m & 0x80);
+       SET_ZERO(!m);
+       A = m;
+       return;
+}
index 36ccb22d503016e069713399c2e718fc11ffea2c..7da9ece04ecb774c41fbf2a0d89856019fa969a0 100644 (file)
@@ -363,6 +363,10 @@ int getcapslock (void)
        capstable[5] = host_scrolllockstate;
        capstable[6] = 0;
        capslockstate = inputdevice_synccapslock (capslockstate, capstable);
+       if (currprefs.keyboard_mode == 0) {
+               gui_data.capslock = host_capslockstate;
+               gui_led(LED_CAPS, gui_data.capslock, -1);
+       }
        return capslockstate;
 }
 
@@ -585,15 +589,27 @@ void keyboard_settrans (void)
 
 int target_checkcapslock (int scancode, int *state)
 {
-       if (scancode != DIK_CAPITAL && scancode != DIK_NUMLOCK && scancode != DIK_SCROLL)
+       if (scancode != DIK_CAPITAL && scancode != DIK_NUMLOCK && scancode != DIK_SCROLL) {
                return 0;
-       if (*state == 0)
+       }
+       if (currprefs.keyboard_mode > 0) {
+               return 1;
+       }
+       if (*state == 0) {
                return -1;
-       if (scancode == DIK_CAPITAL)
+       }
+       if (scancode == DIK_CAPITAL) {
                *state = host_capslockstate;
-       if (scancode == DIK_NUMLOCK)
+               if (gui_data.capslock != (host_capslockstate != 0)) {
+                       gui_data.capslock = host_capslockstate;
+                       gui_led(LED_CAPS, gui_data.capslock, -1);
+               }
+       }
+       if (scancode == DIK_NUMLOCK) {
                *state = host_numlockstate;
-       if (scancode == DIK_SCROLL)
+       }
+       if (scancode == DIK_SCROLL) {
                *state = host_scrolllockstate;
+       }
        return 1;
 }
index 8ef450d369e707b5df0cf739579604a10e4b4dd1..0c21814dd381ce1d7983088c40efc81c8ae98929 100644 (file)
 #define IDC_BLITIMM                     1174
 #define IDC_BLITWAIT                    1175
 #define IDC_LORES                       1176
-#define IDC_KEYBOARD_CONNECTED          1176
+#define IDC_KEYBOARDNKRO                1176
 #define IDC_RATE2BOX                    1177
 #define IDC_SUBPIXEL                    1177
 #define IDC_AUTORESOLUTIONSELECT        1178
 #define IDC_SOUND_OPENAL                1716
 #define IDC_MONITOREMU_MON              1716
 #define IDC_CS_CD32CD                   1717
+#define IDC_KEYBOARDMODE                1717
 #define IDC_CS_CD32C2P                  1718
 #define IDC_SOUND_PORTAUDIO             1718
 #define IDC_CS_CD32NVRAM                1719
index c2d8728ede3c1125c1c4646cc7dff075b47fd315..c5de39291b31a78d2e954e284d0b4eef24183957 100644 (file)
@@ -800,6 +800,10 @@ BEGIN
     CONTROL         "Keep aspect ratio",IDC_GENLOCK_KEEP_ASPECT,"Button",BS_AUTOCHECKBOX | BS_LEFT | WS_GROUP | WS_TABSTOP,13,262,214,10
     COMBOBOX        IDC_GENLOCKFILE,12,278,356,75,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
     PUSHBUTTON      "...",IDC_GENLOCKFILESELECT,375,277,10,15
+    COMBOBOX        IDC_KEYBOARDMODE,12,142,191,65,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "Keyboard N-key rollover [] Always emulate n-key rollover (no key ghosting or jamming when pressing multiple keys at the same time) even if selected emulated keyboard model does not support it.",IDC_KEYBOARDNKRO,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,162,150,10
+    GROUPBOX        "Keyboard",IDC_STATIC,0,125,215,53
 END
 
 IDD_CHIPSET2 DIALOGEX 0, 0, 396, 316
index 5ead2b9c37acb48fa76242a3317f5e7b7b8e9966..74ac7629b19ae02923c0a3090e0b3073940ad80d 100644 (file)
@@ -3349,7 +3349,7 @@ bool handle_events (void)
                        setpaused (pause_emulation);
                        was_paused = pause_emulation;
                        mon->manual_painting_needed++;
-                       gui_fps (0, 0, 0);
+                       gui_fps (0, 0, 0, 0, 0);
                        gui_led (LED_SND, 0, -1);
                        // we got just paused, report it to caller.
                        return 1;
@@ -7073,6 +7073,10 @@ static int parseargs(const TCHAR *argx, const TCHAR *np, const TCHAR *np2)
                log_ld = getval (np);
                return 2;
        }
+       if (!_tcscmp (arg, _T("kbmculog"))) {
+               kb_mcu_log = getval (np);
+               return 2;
+       }
        if (!_tcscmp (arg, _T("midiinbuffer"))) {
                midi_inbuflen = getval (np);
                if (midi_inbuflen < 16000)
index a95e80b39601cfb00ae62e8181efdb1ff64d3373..f8f7333f8495b154c56c46efd3db43d84855d97d 100644 (file)
@@ -2052,7 +2052,7 @@ static int open_windows(struct AmigaMonitor *mon, bool mousecapture, bool starte
                for (int i = 0; i < NUM_LEDS; i++)
                        gui_flicker_led(i, -1, -1);
                gui_led(LED_POWER, gui_data.powerled, gui_data.powerled_brightness);
-               gui_fps(0, 0, 0);
+               gui_fps(0, 0, 0, 0, 0);
                if (gui_data.md >= 0)
                        gui_led(LED_MD, 0, -1);
                for (int i = 0; i < 4; i++) {
@@ -3217,10 +3217,10 @@ static void createstatuswindow(struct AmigaMonitor *mon)
        RECT rc;
        HLOCAL hloc;
        LPINT lpParts;
-       int drive_width, hd_width, cd_width, power_width;
-       int fps_width, idle_width, snd_width, joy_width, net_width;
+       int drive_width, hd_width, cd_width, power_width, caps_width;
+       int fps_width, lines_width, idle_width, snd_width, joy_width, net_width;
        int joys = currprefs.win32_statusbar > 1 ? 2 : 0;
-       int num_parts = 12 + joys + 1;
+       int num_parts = LED_MAX + joys + 1;
        float scale = 1.0;
        WINDOWINFO wi;
        int extra;
@@ -3257,6 +3257,8 @@ static void createstatuswindow(struct AmigaMonitor *mon)
        hd_width = (int)(24 * scale);
        cd_width = (int)(24 * scale);
        power_width = (int)(42 * scale);
+       caps_width = (int)(42 * scale);
+       lines_width = (int)(42 * scale);
        fps_width = (int)(64 * scale);
        idle_width = (int)(64 * scale);
        net_width = (int)(24 * scale);
@@ -3278,7 +3280,7 @@ static void createstatuswindow(struct AmigaMonitor *mon)
                        i++;
                        window_led_msg_start = i;
                        /* Calculate the right edge coordinate for each part, and copy the coords to the array.  */
-                       int startx = rc.right - (drive_width * 4) - power_width - idle_width - fps_width - cd_width - hd_width - snd_width - net_width - joys * joy_width - extra;
+                       int startx = rc.right - (drive_width * 4) - power_width - caps_width - idle_width - fps_width - lines_width - cd_width - hd_width - snd_width - net_width - joys * joy_width - extra;
                        for (int j = 0; j < joys; j++) {
                                lpParts[i] = startx;
                                i++;
@@ -3296,12 +3298,18 @@ static void createstatuswindow(struct AmigaMonitor *mon)
                        // fps
                        lpParts[i] = lpParts[i - 1] + idle_width;
                        i++;
-                       // power
+                       // lines
                        lpParts[i] = lpParts[i - 1] + fps_width;
                        i++;
+                       // power
+                       lpParts[i] = lpParts[i - 1] + lines_width;
+                       i++;
+                       // caps
+                       lpParts[i] = lpParts[i - 1] + power_width;
+                       i++;
                        i1 = i;
                        // hd
-                       lpParts[i] = lpParts[i - 1] + power_width;
+                       lpParts[i] = lpParts[i - 1] + caps_width;
                        i++;
                        // cd
                        lpParts[i] = lpParts[i - 1] + hd_width;
index 038ad316aa91031b85358851893434f7de55a81b..179777ca16777a7053b488fb4754928acbbb49b3 100644 (file)
@@ -8193,6 +8193,17 @@ static void enable_for_chipsetdlg (HWND hDlg)
        ew(hDlg, IDC_GENLOCKFILESELECT, genlock && (workprefs.genlock_image >= 6 || (workprefs.genlock_image >= 3 && workprefs.genlock_image < 5)) ? TRUE : FALSE);
 
        ew(hDlg, IDC_MONITOREMU_MON, workprefs.monitoremu != 0);
+
+       if (workprefs.keyboard_mode == KB_UAE || workprefs.keyboard_mode == KB_A2000_8039) {
+               if (!workprefs.keyboard_nkro) {
+                       workprefs.keyboard_nkro = true;
+                       CheckDlgButton(hDlg, IDC_KEYBOARDNKRO, TRUE);
+               }
+               ew(hDlg, IDC_KEYBOARDNKRO, FALSE);
+       } else {
+               ew(hDlg, IDC_KEYBOARDNKRO, TRUE);
+       }
+
 }
 
 static const int fakerefreshrates[] = { 50, 60, 100, 120, 0 };
@@ -9234,7 +9245,8 @@ static void values_to_chipsetdlg (HWND hDlg)
        CheckDlgButton(hDlg, IDC_GENLOCK, workprefs.genlock);
        CheckDlgButton(hDlg, IDC_BLITIMM, workprefs.immediate_blits);
        CheckDlgButton(hDlg, IDC_BLITWAIT, workprefs.waiting_blits);
-       CheckDlgButton(hDlg, IDC_KEYBOARD_CONNECTED, workprefs.keyboard_connected);
+       CheckDlgButton(hDlg, IDC_KEYBOARDNKRO, workprefs.keyboard_nkro);
+       xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_SETCURSEL, workprefs.keyboard_mode + 1, 0);
        CheckDlgButton(hDlg, IDC_SUBPIXEL, workprefs.chipset_hr);
        xSendDlgItemMessage(hDlg, IDC_CS_HVCSYNC, CB_SETCURSEL, workprefs.cs_hvcsync, 0);
 
@@ -9266,7 +9278,11 @@ static void values_from_chipsetdlg (HWND hDlg, UINT msg, WPARAM wParam, LPARAM l
        workprefs.immediate_blits = ischecked (hDlg, IDC_BLITIMM);
        workprefs.waiting_blits = ischecked (hDlg, IDC_BLITWAIT) ? 1 : 0;
        workprefs.chipset_hr = ischecked(hDlg, IDC_SUBPIXEL);
-       workprefs.keyboard_connected = ischecked(hDlg, IDC_KEYBOARD_CONNECTED) ? 1 : 0;
+       workprefs.keyboard_nkro = ischecked(hDlg, IDC_KEYBOARDNKRO);
+       nn = xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_GETCURSEL, 0, 0);
+       if (nn != CB_ERR) {
+               workprefs.keyboard_mode = nn - 1;
+       }
        int val = xSendDlgItemMessage(hDlg, IDC_CS_HVCSYNC, CB_GETCURSEL, 0, 0L);
        if (val != CB_ERR)
                workprefs.cs_hvcsync = val;
@@ -9367,6 +9383,13 @@ static void setgenlock(HWND hDlg)
        }
 }
 
+static void appendkbmcurom(TCHAR *s, bool hasrom)
+{
+       if (!hasrom) {
+               _tcscat(s, _T(" [ROM not found]"));
+       }
+}
+
 static INT_PTR CALLBACK ChipsetDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
 {
        static int recursive = 0;
@@ -9383,6 +9406,38 @@ static INT_PTR CALLBACK ChipsetDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPAR
        {
                pages[CHIPSET_ID] = hDlg;
                currentpage = CHIPSET_ID;
+               int ids1[] = { 321, -1 };
+               int ids2[] = { 322, -1 };
+               int ids3[] = { 323, -1 };
+               struct romlist *has65001 = NULL;
+               struct romlist *has657036 = getromlistbyids(ids1, NULL);
+               struct romlist *has6805 = getromlistbyids(ids2, NULL);
+               struct romlist *has8039 = getromlistbyids(ids3, NULL);
+
+               xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_RESETCONTENT, 0, 0);
+               xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_ADDSTRING, 0, (LPARAM)_T("Keyboard disconnected"));
+               xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_ADDSTRING, 0, (LPARAM)_T("UAE High level emulation"));
+               _tcscpy(tmp, _T("A500 / A500 + (6570 - 036 MCU)"));
+               appendkbmcurom(tmp, has657036);
+               xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_ADDSTRING, 0, tmp);
+               _tcscpy(tmp, _T("A600(6570 - 036 MCU)"));
+               appendkbmcurom(tmp, has657036);
+               xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_ADDSTRING, 0, tmp);
+               _tcscpy(tmp, _T("A1000 (6500-1 MCU. ROM not yet dumped)"));
+               appendkbmcurom(tmp, has65001);
+               xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_ADDSTRING, 0, tmp);
+               _tcscpy(tmp, _T("A1000 (6570-036 MCU)"));
+               appendkbmcurom(tmp, has657036);
+               xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_ADDSTRING, 0, tmp);
+               _tcscpy(tmp, _T("A1200 (68HC05C MCU)"));
+               appendkbmcurom(tmp, has6805);
+               xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_ADDSTRING, 0, tmp);
+               _tcscpy(tmp, _T("A2000 (Cherry, 8039 MCU)"));
+               appendkbmcurom(tmp, has8039);
+               xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_ADDSTRING, 0, tmp);
+               _tcscpy(tmp, _T("A2000/A3000/A4000 (6570-036 MCU)"));
+               appendkbmcurom(tmp, has657036);
+               xSendDlgItemMessage(hDlg, IDC_KEYBOARDMODE, CB_ADDSTRING, 0, tmp);
 
                xSendDlgItemMessage(hDlg, IDC_CS_EXT, CB_RESETCONTENT, 0, 0);
                xSendDlgItemMessage(hDlg, IDC_CS_EXT, CB_ADDSTRING, 0, (LPARAM)_T("Custom"));
@@ -23870,14 +23925,17 @@ void gui_flicker_led (int led, int unitnum, int status)
        }
 }
 
-void gui_fps (int fps, int idle, int color)
+void gui_fps (int fps, int lines, bool lace, int idle, int color)
 {
        gui_data.fps = fps;
+       gui_data.lines = lines;
+       gui_data.lace = lace;
        gui_data.idle = idle;
        gui_data.fps_color = color;
-       gui_led (LED_FPS, 0, -1);
-       gui_led (LED_CPU, 0, -1);
-       gui_led (LED_SND, (gui_data.sndbuf_status > 1 || gui_data.sndbuf_status < 0) ? 0 : 1, -1);
+       gui_led(LED_FPS, 0, -1);
+       gui_led(LED_LINES, 0, -1);
+       gui_led(LED_CPU, 0, -1);
+       gui_led(LED_SND, (gui_data.sndbuf_status > 1 || gui_data.sndbuf_status < 0) ? 0 : 1, -1);
 }
 
 #define LED_STRING_WIDTH 40
@@ -23912,7 +23970,7 @@ void gui_led (int led, int on, int brightness)
                return;
        tt = NULL;
        if (led >= LED_DF0 && led <= LED_DF3) {
-               pos = 7 + (led - LED_DF0);
+               pos = 9 + (led - LED_DF0);
                ptr = drive_text + pos * LED_STRING_WIDTH;
                if (gui_data.drives[led - 1].drive_disabled)
                        _tcscpy (ptr, _T(""));
@@ -23937,17 +23995,21 @@ void gui_led (int led, int on, int brightness)
                if (gui_data.drives[led - 1].floppy_protected)
                        writeprotected = 1;
        } else if (led == LED_POWER) {
-               pos = 3;
+               pos = 4;
                ptr = _tcscpy(drive_text + pos * LED_STRING_WIDTH, _T("Power"));
                center = 1;
+       } else if (led == LED_CAPS) {
+               pos = 5;
+               ptr = _tcscpy(drive_text + pos * LED_STRING_WIDTH, _T("Caps"));
+               center = 1;
        } else if (led == LED_HD) {
-               pos = 4;
+               pos = 7;
                ptr = _tcscpy(drive_text + pos * LED_STRING_WIDTH, _T("HD"));
                center = 1;
                if (on > 1)
                        writing = 1;
        } else if (led == LED_CD) {
-               pos = 5;
+               pos = 6;
                ptr = _tcscpy(drive_text + pos * LED_STRING_WIDTH, _T("CD"));
                center = 1;
                if (on >= 0) {
@@ -23958,11 +24020,16 @@ void gui_led (int led, int on, int brightness)
                        on &= 1;
                }
        } else if (led == LED_NET) {
-               pos = 6;
+               pos = 8;
                ptr = _tcscpy(drive_text + pos * LED_STRING_WIDTH, _T("N"));
                center = 1;
                if (on > 1)
                        writing = 1;
+       } else if (led == LED_LINES) {
+               pos = 3;
+               ptr = drive_text + pos * LED_STRING_WIDTH;
+               _stprintf(ptr, _T("%d%c"), gui_data.lines, gui_data.lace ? 'i' : 'p');
+               on = 1;
        } else if (led == LED_FPS) {
                float fps = gui_data.fps / 10.0f;
                extern float p96vblank;
@@ -24052,7 +24119,7 @@ void gui_led (int led, int on, int brightness)
                        on = 0;
                }
        } else if (led == LED_MD) {
-               pos = 7 + 3;
+               pos = 9 + 3;
                ptr = _tcscpy(drive_text + pos * LED_STRING_WIDTH, _T("NV"));
        }
 
index 1d0b9fc950eebd95f345aab7b3dbe41eec8556bf..b9dba3a538f1e23d634128cf827b5d9804c1d129 100644 (file)
     <ClCompile Include="..\..\ini.cpp" />
     <ClCompile Include="..\..\inputrecord.cpp" />
     <ClCompile Include="..\..\isofs.cpp" />
+    <ClCompile Include="..\..\kbmcu\8048\co8048.cpp" />
+    <ClCompile Include="..\..\kbmcu\keyboard_mcu_6500_1.cpp" />
+    <ClCompile Include="..\..\kbmcu\keyboard_mcu_6805.cpp" />
+    <ClCompile Include="..\..\kbmcu\keyboard_mcu_d8039hlc .cpp" />
+    <ClCompile Include="..\..\kbmcu\m6805\m68emu.cpp" />
+    <ClCompile Include="..\..\kbmcu\m6805\m68_ops.cpp" />
     <ClCompile Include="..\..\logging.cpp" />
     <ClCompile Include="..\..\luascript.cpp" />
     <ClCompile Include="..\..\mame\a2410.cpp" />
     <ClCompile Include="..\..\mame\tm34010\tms34010.cpp" />
     <ClCompile Include="..\..\midiemu.cpp" />
+    <ClCompile Include="..\..\mos6502.cpp" />
     <ClCompile Include="..\..\ncr9x_scsi.cpp" />
     <ClCompile Include="..\..\newcpu_common.cpp" />
     <ClCompile Include="..\..\pcem\386.cpp" />
index 5869048a717b92b246a1108f71b6fce4c0936d26..839ce97f3a5a66450dbd2b223d7829fa3dbd4173 100644 (file)
@@ -77,6 +77,9 @@
     <Filter Include="dsp3210">
       <UniqueIdentifier>{6f046035-4e6d-4038-a65f-d789a89ee702}</UniqueIdentifier>
     </Filter>
+    <Filter Include="kbmcu">
+      <UniqueIdentifier>{b60c3d87-d969-4982-8508-e9fdfc29a312}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\ahidsound_dsonly.cpp">
     <ClCompile Include="..\..\pcem\vid_bt482_ramdac.cpp">
       <Filter>pcem</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\kbmcu\keyboard_mcu_6500_1.cpp">
+      <Filter>kbmcu</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\kbmcu\keyboard_mcu_6805.cpp">
+      <Filter>kbmcu</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\kbmcu\keyboard_mcu_d8039hlc .cpp">
+      <Filter>kbmcu</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\kbmcu\m6805\m68_ops.cpp">
+      <Filter>kbmcu</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\kbmcu\m6805\m68emu.cpp">
+      <Filter>kbmcu</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\kbmcu\8048\co8048.cpp">
+      <Filter>kbmcu</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\mos6502.cpp">
+      <Filter>common</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\resources\35floppy.ico">
index 63d597a3ed2e0c831b285fe7e95005e49e9f8758..2a81d4760c479d64700b9480607507e753c70750 100644 (file)
@@ -81,7 +81,6 @@ void romlist_add (const TCHAR *path, struct romdata *rd)
        }
 }
 
-
 struct romdata *getromdatabypath (const TCHAR *path)
 {
        int i;
@@ -97,7 +96,7 @@ struct romdata *getromdatabypath (const TCHAR *path)
        return NULL;
 }
 
-#define NEXT_ROM_ID 322
+#define NEXT_ROM_ID 324
 
 #if NEXT_ROM_ID >= MAX_ROMMGR_ROMS
 #error Increase MAX_ROMMGR_ROMS!
@@ -123,8 +122,12 @@ static struct romdata roms[] = {
        { _T("Cloanto Amiga Forever 2010 ROM key"), 0, 0, 0, 0, 0, 1544, 73, 0, 1, ROMTYPE_KEY, 0, 0, NULL,
        0x8c4dd05c, 0x05034f62,0x0b5bb7b2,0x86954ea9,0x164fdb90,0xfb2897a4 },
 
-       { _T("6500/1 Keyboard MCU ROM"), 0, 0, 0, 0, _T("KBDMCU\0"), 2048, 321, 0, 0, ROMTYPE_KBMCU, 0, 0, NULL,
+       { _T("6570-036 Keyboard MCU ROM"), 0, 0, 0, 0, _T("KBDMCU\0"), 2048, 321, 0, 0, ROMTYPE_KBMCU, 0, 0, NULL,
        0x4a3fc332, 0x83b21d0c, 0x8b93fc9b, 0x9b3b287f, 0xde4ec8f3, 0xbadac5a2 },
+       { _T("68HC05 Keyboard MCU ROM"), 0, 0, 0, 0, _T("KBDMCU\0"), 8192, 322, 0, 0, ROMTYPE_KBMCU, 0, 0, NULL,
+       0x2a77eec4, 0x301ec6a6, 0x9404457d, 0x912c89e3, 0xfc54095e, 0xda9f0e93 },
+       { _T("D8039HLC Keyboard MCU ROM"), 0, 0, 0, 0, _T("KBDMCU\0"), 2048, 323, 0, 0, ROMTYPE_KBMCU, 0, 0, NULL,
+       0xc23d280e, 0xb5c4b8c3, 0x8b9e4a86, 0x4abc871d, 0x048bd1c3, 0xa02c8432 },
 
        { _T("KS ROM v23.93 (Velvet)"), 23, 93, 23, 93, _T("VELVET\0"), 131072, 125, 0, 0, ROMTYPE_KICK, 0, 0, NULL,
        0xadcb44c9, 0x7c36b2ba,0x298da3da,0xce60d0ba,0x8511d470,0x76a40d5c, NULL, NULL, 4 },
index fc7aa23247180efbbf33b18bd321a94513fa0080..7ad304e880a567267cc0626b4e2297debb08544e 100644 (file)
@@ -743,6 +743,12 @@ void restore_state (const TCHAR *filename)
                        end = restore_disk2 (3, chunk);
                else if (!_tcscmp (name, _T("KEYB")))
                        end = restore_keyboard (chunk);
+               else if (!_tcscmp (name, _T("KBM1")))
+                       end = restore_kbmcu(chunk);
+               else if (!_tcscmp (name, _T("KBM2")))
+                       end = restore_kbmcu2(chunk);
+               else if (!_tcscmp (name, _T("KBM3")))
+                       end = restore_kbmcu3(chunk);
 #ifdef AUTOCONFIG
                else if (!_tcscmp (name, _T("EXPA")))
                        end = restore_expansion (chunk);
@@ -1078,6 +1084,16 @@ static int save_state_internal (struct zfile *f, const TCHAR *description, int c
        save_chunk (f, dst, len, _T("KEYB"), 0);
        xfree (dst);
 
+       dst = save_kbmcu(&len, NULL);
+       save_chunk(f, dst, len, _T("KBM1"), 0);
+       xfree (dst);
+       dst = save_kbmcu2(&len, NULL);
+       save_chunk(f, dst, len, _T("KBM2"), 0);
+       xfree (dst);
+       dst = save_kbmcu3(&len, NULL);
+       save_chunk(f, dst, len, _T("KBM3"), 0);
+       xfree (dst);
+
 #ifdef AUTOCONFIG
        // new
        i = 0;
index 6f119e459e6bd8adc7e6a24e07edb092f2148b91..c174dfae81baa10c62a12b229c569787cfac5d8f 100644 (file)
@@ -195,7 +195,7 @@ void draw_status_line_single(int monid, uae_u8 *buf, int bpp, int y, int totalwi
                        struct floppyslot *fs = &currprefs.floppyslots[pled];
                        struct gui_info_drive *gid = &gui_data.drives[pled];
                        int track = gid->drive_track;
-                       pos = 7 + pled;
+                       pos = 8 + pled;
                        on_rgb = 0x00cc00;
                        if (!gid->drive_disabled) {
                                num1 = -1;
@@ -216,13 +216,18 @@ void draw_status_line_single(int monid, uae_u8 *buf, int bpp, int y, int totalwi
                        on_rgb &= 0xffffff;
                        off_rgb = rgbmuldiv(on_rgb, 2, 4);
                        on_rgb2 = rgbmuldiv(on_rgb, 2, 3);
+               } else if (led == LED_CAPS) {
+                       pos = 4;
+                       on_rgb = 0xcc9900;
+                       on = gui_data.capslock;
+                       off_rgb = (on_rgb & 0xfefefe) >> 1;
                } else if (led == LED_POWER) {
                        pos = 3;
                        on_rgb = ((gui_data.powerled_brightness * 10 / 16) + 0x33) << 16;
                        on = 1;
                        off_rgb = 0x330000;
                } else if (led == LED_CD) {
-                       pos = 5;
+                       pos = 6;
                        if (gui_data.cd >= 0) {
                                on = gui_data.cd & (LED_CD_AUDIO | LED_CD_ACTIVE);
                                on_rgb = (on & LED_CD_AUDIO) ? 0x00cc00 : 0x0000cc;
@@ -236,7 +241,7 @@ void draw_status_line_single(int monid, uae_u8 *buf, int bpp, int y, int totalwi
                                num3 = 12;
                        }
                } else if (led == LED_HD) {
-                       pos = 4;
+                       pos = 5;
                        if (gui_data.hd >= 0) {
                                on = gui_data.hd;
                                on_rgb = on == 2 ? 0xcc0000 : 0x0000cc;
@@ -341,7 +346,7 @@ void draw_status_line_single(int monid, uae_u8 *buf, int bpp, int y, int totalwi
                } else if (led == LED_MD) {
                        // DF3 reused as internal non-volatile ram led (cd32/cdtv)
                        if (gui_data.drives[3].drive_disabled && gui_data.md >= 0) {
-                               pos = 7 + 3;
+                               pos = 8 + 3;
                                if (gui_data.md >= 0) {
                                        on = gui_data.md;
                                        on_rgb = on == 2 ? 0xcc0000 : 0x00cc00;
@@ -354,7 +359,7 @@ void draw_status_line_single(int monid, uae_u8 *buf, int bpp, int y, int totalwi
                                continue;
                        }
                } else if (led == LED_NET) {
-                       pos = 6;
+                       pos = 7;
                        if (gui_data.net >= 0) {
                                on = gui_data.net;
                                on_rgb = 0;