From daf51ce25640da642027f9854e8df4133fba8eab Mon Sep 17 00:00:00 2001 From: Toni Wilen Date: Sat, 4 Jan 2025 13:07:59 +0200 Subject: [PATCH] Keyboard MCU emulation --- cfgfile.cpp | 33 +- cia.cpp | 162 +- devices.cpp | 10 + expansion.cpp | 23 + include/gui.h | 30 +- include/keyboard_mcu.h | 17 + include/mos6502.h | 192 ++ include/options.h | 14 +- include/savestate.h | 7 + inputdevice.cpp | 20 +- kbmcu/8048/co8048.cpp | 923 ++++++++++ kbmcu/8048/co8048.h | 118 ++ kbmcu/keyboard_mcu_6500_1.cpp | 583 ++++++ kbmcu/keyboard_mcu_6805.cpp | 750 ++++++++ kbmcu/keyboard_mcu_d8039hlc .cpp | 430 +++++ kbmcu/m6805/m68_internal.h | 108 ++ kbmcu/m6805/m68_ops.cpp | 759 ++++++++ kbmcu/m6805/m68_optab_hc05.h | 258 +++ kbmcu/m6805/m68emu.cpp | 330 ++++ kbmcu/m6805/m68emu.h | 63 + keybuf.cpp | 2 +- mos6502.cpp | 1635 +++++++++++++++++ od-win32/keyboard_win32.cpp | 26 +- od-win32/resources/resource.h | 3 +- od-win32/resources/winuae.rc | 4 + od-win32/win32.cpp | 6 +- od-win32/win32gfx.cpp | 22 +- od-win32/win32gui.cpp | 91 +- od-win32/winuae_msvc15/winuae_msvc.vcxproj | 7 + .../winuae_msvc15/winuae_msvc.vcxproj.filters | 24 + rommgr.cpp | 9 +- savestate.cpp | 16 + statusline.cpp | 15 +- 33 files changed, 6587 insertions(+), 103 deletions(-) create mode 100644 include/keyboard_mcu.h create mode 100644 include/mos6502.h create mode 100644 kbmcu/8048/co8048.cpp create mode 100644 kbmcu/8048/co8048.h create mode 100644 kbmcu/keyboard_mcu_6500_1.cpp create mode 100644 kbmcu/keyboard_mcu_6805.cpp create mode 100644 kbmcu/keyboard_mcu_d8039hlc .cpp create mode 100644 kbmcu/m6805/m68_internal.h create mode 100644 kbmcu/m6805/m68_ops.cpp create mode 100644 kbmcu/m6805/m68_optab_hc05.h create mode 100644 kbmcu/m6805/m68emu.cpp create mode 100644 kbmcu/m6805/m68emu.h create mode 100644 mos6502.cpp diff --git a/cfgfile.cpp b/cfgfile.cpp index 1098a5ca..9e0813b5 100644 --- a/cfgfile.cpp +++ b/cfgfile.cpp @@ -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 4afc5324..2576ae83 100644 --- 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; } } diff --git a/devices.cpp b/devices.cpp index 73376174..dd166408 100644 --- a/devices.cpp +++ b/devices.cpp @@ -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) diff --git a/expansion.cpp b/expansion.cpp index a71f6da9..39feba72 100644 --- a/expansion.cpp +++ b/expansion.cpp @@ -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, diff --git a/include/gui.h b/include/gui.h index ee1c2b04..dc3c074f 100644 --- a/include/gui.h +++ b/include/gui.h @@ -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 index 00000000..1a9d3b17 --- /dev/null +++ b/include/keyboard_mcu.h @@ -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 index 00000000..c30400fa --- /dev/null +++ b/include/mos6502.h @@ -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 + +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(); +}; diff --git a/include/options.h b/include/options.h index d7930146..8c8b1c74 100644 --- a/include/options.h +++ b/include/options.h @@ -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; diff --git a/include/savestate.h b/include/savestate.h index 6d4d7c2a..7bcb68c0 100644 --- a/include/savestate.h +++ b/include/savestate.h @@ -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); diff --git a/inputdevice.cpp b/inputdevice.cpp index 3720581e..10d3a1a3 100644 --- a/inputdevice.cpp +++ b/inputdevice.cpp @@ -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 index 00000000..565d6803 --- /dev/null +++ b/kbmcu/8048/co8048.cpp @@ -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 . +// +// 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 +#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 index 00000000..002320da --- /dev/null +++ b/kbmcu/8048/co8048.h @@ -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 . +// +// 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 +#include + +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 + +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 index 00000000..a3558b95 --- /dev/null +++ b/kbmcu/keyboard_mcu_6500_1.cpp @@ -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 index 00000000..83fa92dd --- /dev/null +++ b/kbmcu/keyboard_mcu_6805.cpp @@ -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 index 00000000..90f6d01e --- /dev/null +++ b/kbmcu/keyboard_mcu_d8039hlc .cpp @@ -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 index 00000000..0d56feda --- /dev/null +++ b/kbmcu/m6805/m68_internal.h @@ -0,0 +1,108 @@ +#ifndef M68_INTERNAL_H +#define M68_INTERNAL_H + +#include + +//! 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 index 00000000..5db97f6a --- /dev/null +++ b/kbmcu/m6805/m68_ops.cpp @@ -0,0 +1,759 @@ +#include +#include +#include +#include +#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 index 00000000..ad99c8d9 --- /dev/null +++ b/kbmcu/m6805/m68_optab_hc05.h @@ -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 index 00000000..97920e70 --- /dev/null +++ b/kbmcu/m6805/m68emu.cpp @@ -0,0 +1,330 @@ +#include +#include +#include +#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<pending_interrupts &= ~(1<pending_interrupts& (1<pending_interrupts &= ~(1<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 index 00000000..1b8efbb9 --- /dev/null +++ b/kbmcu/m6805/m68emu.h @@ -0,0 +1,63 @@ +#ifndef M68EMU_H +#define M68EMU_H + +#include +#include + +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 diff --git a/keybuf.cpp b/keybuf.cpp index 9560ed4b..456bcbe9 100644 --- a/keybuf.cpp +++ b/keybuf.cpp @@ -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 index 00000000..3db1c624 --- /dev/null +++ b/mos6502.cpp @@ -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; +} diff --git a/od-win32/keyboard_win32.cpp b/od-win32/keyboard_win32.cpp index 36ccb22d..7da9ece0 100644 --- a/od-win32/keyboard_win32.cpp +++ b/od-win32/keyboard_win32.cpp @@ -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; } diff --git a/od-win32/resources/resource.h b/od-win32/resources/resource.h index 8ef450d3..0c21814d 100644 --- a/od-win32/resources/resource.h +++ b/od-win32/resources/resource.h @@ -528,7 +528,7 @@ #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 @@ -1127,6 +1127,7 @@ #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 diff --git a/od-win32/resources/winuae.rc b/od-win32/resources/winuae.rc index c2d8728e..c5de3929 100644 --- a/od-win32/resources/winuae.rc +++ b/od-win32/resources/winuae.rc @@ -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 diff --git a/od-win32/win32.cpp b/od-win32/win32.cpp index 5ead2b9c..74ac7629 100644 --- a/od-win32/win32.cpp +++ b/od-win32/win32.cpp @@ -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) diff --git a/od-win32/win32gfx.cpp b/od-win32/win32gfx.cpp index a95e80b3..f8f7333f 100644 --- a/od-win32/win32gfx.cpp +++ b/od-win32/win32gfx.cpp @@ -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; diff --git a/od-win32/win32gui.cpp b/od-win32/win32gui.cpp index 038ad316..179777ca 100644 --- a/od-win32/win32gui.cpp +++ b/od-win32/win32gui.cpp @@ -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")); } diff --git a/od-win32/winuae_msvc15/winuae_msvc.vcxproj b/od-win32/winuae_msvc15/winuae_msvc.vcxproj index 1d0b9fc9..b9dba3a5 100644 --- a/od-win32/winuae_msvc15/winuae_msvc.vcxproj +++ b/od-win32/winuae_msvc15/winuae_msvc.vcxproj @@ -1343,11 +1343,18 @@ + + + + + + + diff --git a/od-win32/winuae_msvc15/winuae_msvc.vcxproj.filters b/od-win32/winuae_msvc15/winuae_msvc.vcxproj.filters index 5869048a..839ce97f 100644 --- a/od-win32/winuae_msvc15/winuae_msvc.vcxproj.filters +++ b/od-win32/winuae_msvc15/winuae_msvc.vcxproj.filters @@ -77,6 +77,9 @@ {6f046035-4e6d-4038-a65f-d789a89ee702} + + {b60c3d87-d969-4982-8508-e9fdfc29a312} + @@ -1024,6 +1027,27 @@ pcem + + kbmcu + + + kbmcu + + + kbmcu + + + kbmcu + + + kbmcu + + + kbmcu + + + common + diff --git a/rommgr.cpp b/rommgr.cpp index 63d597a3..2a81d476 100644 --- a/rommgr.cpp +++ b/rommgr.cpp @@ -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 }, diff --git a/savestate.cpp b/savestate.cpp index fc7aa232..7ad304e8 100644 --- a/savestate.cpp +++ b/savestate.cpp @@ -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; diff --git a/statusline.cpp b/statusline.cpp index 6f119e45..c174dfae 100644 --- a/statusline.cpp +++ b/statusline.cpp @@ -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; -- 2.47.3