├── requirements.txt ├── .vscode ├── .cortex-debug.peripherals.state.json ├── .cortex-debug.registers.state.json ├── launch.json └── settings.json ├── .gitignore ├── images ├── dd1.webp ├── dd2.webp ├── dd3.webp ├── dd4.webp ├── 6809.webp ├── monitor_main.png ├── monitor_step.png ├── display-board.webp └── monitor_confirm_data.png ├── scripts ├── d32.rom ├── deploy.sh └── loader.py ├── .gitattributes ├── e6809.xcworkspace ├── xcshareddata │ ├── WorkspaceSettings.xcsettings │ └── IDEWorkspaceChecks.plist └── contents.xcworkspacedata ├── source ├── cpu_tests.h ├── environment.h ├── keypad.h ├── pia.h ├── main.h ├── monitor.h ├── ht16k33.h ├── keypad.c ├── ht16k33.c ├── pia.c ├── cpu.h ├── main.c ├── ops.h ├── monitor.c └── cpu_tests.c ├── e6809.code-workspace ├── LICENSE.md ├── docs └── e6809_sys.md ├── CMakeLists.txt ├── pico_sdk_import.cmake ├── deploy.sh └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | pyserial==3.5 2 | -------------------------------------------------------------------------------- /.vscode/.cortex-debug.peripherals.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /.vscode/.cortex-debug.registers.state.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | *.xcworkspace/xcuserdata/* 3 | .python/* 4 | -------------------------------------------------------------------------------- /images/dd1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smittytone/e6809/HEAD/images/dd1.webp -------------------------------------------------------------------------------- /images/dd2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smittytone/e6809/HEAD/images/dd2.webp -------------------------------------------------------------------------------- /images/dd3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smittytone/e6809/HEAD/images/dd3.webp -------------------------------------------------------------------------------- /images/dd4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smittytone/e6809/HEAD/images/dd4.webp -------------------------------------------------------------------------------- /scripts/d32.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smittytone/e6809/HEAD/scripts/d32.rom -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /images/6809.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smittytone/e6809/HEAD/images/6809.webp -------------------------------------------------------------------------------- /images/monitor_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smittytone/e6809/HEAD/images/monitor_main.png -------------------------------------------------------------------------------- /images/monitor_step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smittytone/e6809/HEAD/images/monitor_step.png -------------------------------------------------------------------------------- /images/display-board.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smittytone/e6809/HEAD/images/display-board.webp -------------------------------------------------------------------------------- /images/monitor_confirm_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smittytone/e6809/HEAD/images/monitor_confirm_data.png -------------------------------------------------------------------------------- /e6809.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /e6809.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /source/cpu_tests.h: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * 4 | * @version 0.0.2 5 | * @author smittytone 6 | * @copyright 2025 7 | * @licence MIT 8 | * 9 | */ 10 | #ifndef _CPU_TESTS_HEADER_ 11 | #define _CPU_TESTS_HEADER_ 12 | 13 | 14 | /* 15 | * PROTOTYPES 16 | */ 17 | void test_main(void); 18 | 19 | 20 | #endif // _CPU_TESTS_HEADER_ 21 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { "name": "Pico Debug", 5 | "device": "RP2040", 6 | "gdbPath": "arm-none-eabi-gdb", 7 | "cwd": "${workspaceRoot}", 8 | "executable": "${command:cmake.launchTargetPath}", 9 | "request": "launch", 10 | "type": "cortex-debug", 11 | "servertype": "openocd", 12 | "configFiles": [ 13 | "/interface/picoprobe.cfg", 14 | "/target/rp2040.cfg" 15 | ], 16 | "svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd", 17 | "runToMain": true, 18 | "postRestartCommands": [ 19 | "continue" 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /source/environment.h: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * 4 | * @version 0.0.2 5 | * @author smittytone 6 | * @copyright 2025 7 | * @licence MIT 8 | * 9 | */ 10 | 11 | 12 | enum Memory_Map_Band_Type { 13 | Ram_User = 0, 14 | Ram_System, 15 | Rom_System, 16 | Rom_Cartridge, 17 | None = 99 18 | } 19 | 20 | 21 | typedef struct { 22 | uint16_t start_address; 23 | uint16_t size; 24 | } Rom_File; 25 | 26 | 27 | typedef struct { 28 | uint16_t start_address; 29 | Memory_Map_Band_Type type; 30 | } Memory_Map_Band; 31 | 32 | 33 | typedef struct { 34 | Rom_File rom; 35 | bool has_rom; 36 | bool has_68221; 37 | uint16_t ram_size; 38 | Memory_Map_Band[24]; 39 | } Environment; 40 | 41 | 42 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.environment": { 3 | "PICO_SDK_PATH": "/Users/smitty/GitHub/pico-sdk" 4 | }, 5 | "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", 6 | "files.associations": { 7 | "random": "c", 8 | "array": "c", 9 | "string": "c", 10 | "string_view": "c", 11 | "ranges": "c", 12 | "algorithm": "c", 13 | "cstddef": "c", 14 | "phantoms.h": "c", 15 | "time.h": "c", 16 | "sstream": "c", 17 | "optional": "c", 18 | "istream": "c", 19 | "ostream": "c", 20 | "system_error": "c", 21 | "functional": "c", 22 | "tuple": "c", 23 | "type_traits": "c", 24 | "utility": "c", 25 | "memory_resource": "c", 26 | "deque": "c", 27 | "unordered_map": "c", 28 | "vector": "c", 29 | "initializer_list": "c" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /e6809.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.associations": { 9 | "random": "c", 10 | "array": "c", 11 | "string": "c", 12 | "string_view": "c", 13 | "ranges": "c", 14 | "algorithm": "c", 15 | "cstddef": "c", 16 | "phantoms.h": "c", 17 | "time.h": "c", 18 | "sstream": "c", 19 | "optional": "c", 20 | "istream": "c", 21 | "ostream": "c", 22 | "system_error": "c", 23 | "functional": "c", 24 | "tuple": "c", 25 | "type_traits": "c", 26 | "utility": "c", 27 | "memory_resource": "c", 28 | "deque": "c", 29 | "unordered_map": "c", 30 | "vector": "c", 31 | "initializer_list": "c" 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | ### Copyright © 2025 smittytone 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /docs/e6809_sys.md: -------------------------------------------------------------------------------- 1 | # Possible Pinouts \[TEMP] 2 | 3 | 1. SYS sets up the following RS2040 pins, mapping in config: 4 | * GP27 - 6809 BA - OUT 5 | * GP26 - 6809 BS - OUT 6 | * GP22 - NMI - IN 7 | * GP21 - FIRQ - IN 8 | * GP20 - IRQ - IN 9 | * PIA 1 (A only) 10 | * GP6-9: PA0-3 11 | * GP10-13: PA4-7 12 | 1. KEYPAD uses these pins: 13 | * GP3 - KEYPAD INT 14 | * GP4 - KEYPAD I2C SDA 15 | * GP5 - KEYPAD I2C SCL 16 | * GP19 - KEYPAD SPI TX 17 | * GP18 - KEYPAD SPI SCLK 18 | * GP17 - KEYPAD SPI CS 19 | * GP16 - KEYPAD SPI RX 20 | 1. SYS sets memory map by setting. Governs instantiation of components, eg. PIA, SAM 21 | 1. MEMORY_MAP_BASE - Mostly RAM + PIA 22 | 1. MEMORY_MAP_DG32 - As per Dragon 32 23 | 1. MEMORY_MAP_FLAT - 64KB pure Ram 24 | 1. SYS initialises memory as per above 25 | 1. SYS initalises CPU - primarily registers 26 | 1. SYS starts CPU 27 | 1. SYS calls 'CPU::process_next_instruction()' 28 | 1. SYS checks IR pins 29 | * SYS fakes NMI? when monitor running 30 | 1. SYS balances timing for clock accuracy 31 | 1. SYS loops -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | # Set project data 4 | set(PROJECT_NAME "e6809") 5 | set(VERSION_NUMBER "0.0.2") 6 | set(BUILD_NUMBER "2") 7 | 8 | # Make project data accessible to compiler 9 | add_compile_definitions(APP_NAME="${PROJECT_NAME}") 10 | add_compile_definitions(APP_VERSION="${VERSION_NUMBER}") 11 | add_compile_definitions(BUILD_NUM=${BUILD_NUMBER}) 12 | add_compile_definitions(DEBUG=1) 13 | 14 | include(pico_sdk_import.cmake) 15 | 16 | project(${PROJECT_NAME} 17 | LANGUAGES C CXX ASM 18 | VERSION 0.0.2 19 | DESCRIPTION "Motorola 6809e simulator") 20 | 21 | add_executable(${PROJECT_NAME} 22 | source/main.c 23 | source/cpu.c 24 | source/cpu_tests.c 25 | source/ht16k33.c 26 | source/keypad.c 27 | source/monitor.c 28 | source/pia.c 29 | ) 30 | 31 | pico_sdk_init() 32 | 33 | target_link_libraries(${PROJECT_NAME} 34 | pico_stdlib 35 | hardware_gpio 36 | hardware_i2c 37 | hardware_spi 38 | hardware_adc 39 | hardware_flash 40 | hardware_sync 41 | ) 42 | 43 | pico_enable_stdio_usb(${PROJECT_NAME} 1) 44 | pico_enable_stdio_uart(${PROJECT_NAME} 0) 45 | pico_add_extra_outputs(${PROJECT_NAME}) 46 | -------------------------------------------------------------------------------- /source/keypad.h: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * Keypad driver 4 | * 5 | * @version 0.0.2 6 | * @author Pimoroni, smittytone 7 | * @copyright 2025 8 | * @licence MIT 9 | * 10 | */ 11 | #ifndef _KEYPAD_HEADER_ 12 | #define _KEYPAD_HEADER_ 13 | 14 | 15 | /* 16 | * INCLUDES 17 | */ 18 | #include 19 | 20 | 21 | /* 22 | * CONSTANTS 23 | */ 24 | #define DEFAULT_BRIGHTNESS 0.5f 25 | #define KEYPAD_WIDTH 4 26 | #define KEYPAD_HEIGHT 4 27 | #define NUM_KEYS 16 28 | 29 | #define KEYPAD_I2C_ADDRESS 0x20 30 | #define KEYPAD_PIN_KEYS_SDA 4 31 | #define KEYPAD_PIN_KEYS_SCL 5 32 | #define KEYPAD_PIN_LEDS_CS 17 33 | #define KEYPAD_PIN_LEDS_SCK 18 34 | #define KEYPAD_PIN_LEDS_TX 19 35 | 36 | 37 | /* 38 | * PROTOTYPES 39 | */ 40 | bool keypad_init(void); 41 | void keypad_update_leds(void); 42 | void keypad_set_brightness(float brightness); 43 | void keypad_set_led(uint8_t i, uint8_t r, uint8_t g, uint8_t b); 44 | void keypad_set_all(uint8_t r, uint8_t g, uint8_t b); 45 | void keypad_clear(void); 46 | uint16_t keypad_get_button_states(void); 47 | 48 | 49 | #endif // _KEYPAD_HEADER_ 50 | -------------------------------------------------------------------------------- /source/pia.h: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * Peripheral Interface Adapter (PIA) 4 | * 5 | * @version 0.0.2 6 | * @author smittytone 7 | * @copyright 2025 8 | * @licence MIT 9 | * 10 | */ 11 | #ifndef _PIA_HEADER_ 12 | #define _PIA_HEADER_ 13 | 14 | 15 | /* 16 | * INCLUDES 17 | */ 18 | #include 19 | 20 | 21 | /* 22 | * CONSTANTS 23 | */ 24 | #define INPUT 1 25 | #define OUTPUT 0 26 | 27 | 28 | /* 29 | * STRUCTS 30 | */ 31 | typedef struct { 32 | uint8_t* pa_pins; 33 | uint8_t* ca_pins; 34 | uint8_t* reg_control_a; 35 | uint8_t* reg_data_a; 36 | uint8_t reg_output_a; 37 | uint8_t reg_direction_a; 38 | bool emit_output; 39 | bool enabled; 40 | bool ca_1_can_interrupt; 41 | bool ca_1_is_set_on_up; 42 | bool ca_2_can_interrupt; 43 | bool ca_2_is_set_on_up; 44 | bool ca_2_is_output; 45 | } MC6821; 46 | 47 | 48 | /* 49 | * PROTOTYPES 50 | */ 51 | void pia_init(MC6821* pia); 52 | void pia_reset(MC6821* pia); 53 | 54 | void pia_set_gpio_direction(MC6821* pia, uint8_t pin); 55 | uint8_t pia_get_gpio_direction(MC6821* pia, uint8_t pin); 56 | void pia_set_gpio_output_state(MC6821* pia, uint8_t pin); 57 | void pia_get_gpio_input_state(MC6821* pia, uint8_t pin); 58 | 59 | void pia_update(MC6821* pia); 60 | void pia_update_flags(MC6821* pia); 61 | 62 | void pia_set_pia_ca(MC6821* pia); 63 | void pia_check_irqs(void); 64 | 65 | 66 | #endif // _PIA_HEADER_ 67 | -------------------------------------------------------------------------------- /source/main.h: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * 4 | * @version 0.0.2 5 | * @author smittytone 6 | * @copyright 2025 7 | * @licence MIT 8 | * 9 | */ 10 | #ifndef _E6809_HEADER_ 11 | #define _E6809_HEADER_ 12 | 13 | 14 | /* 15 | * INCLUDES 16 | */ 17 | // C 18 | #include 19 | 20 | 21 | /* 22 | * CONSTANTS 23 | */ 24 | // NOTE These are RP2040 GPIO pin numbers 25 | #define PIN_6809_NMI 0 26 | #define PIN_6809_IRQ 1 27 | #define PIN_6809_FIRQ 2 28 | #define PIN_6809_RESET 3 29 | 30 | #define PIN_PICO_LED 25 31 | 32 | #define PIN_6821_PA0 6 33 | #define PIN_6821_PA1 7 34 | #define PIN_6821_PA2 8 35 | #define PIN_6821_PA3 9 36 | #define PIN_6821_PA4 10 37 | #define PIN_6821_PA5 11 38 | #define PIN_6821_PA6 12 39 | #define PIN_6821_PA7 13 40 | #define PIN_6821_CA1 14 41 | #define PIN_6821_CA2 15 42 | 43 | #define RP2040_IRQ_GPIO_COUNT 4 44 | #define RP2040_PIA_GPIO_COUNT 10 45 | 46 | #define RP2040_FLASH_DATA_START 1048576 47 | #define RP2040_FLASH_DATA_SIZE 65536 48 | 49 | 50 | /* 51 | * STRUCTS 52 | */ 53 | typedef struct { 54 | bool has_led; 55 | bool has_mc6821; 56 | uint8_t irq_gpio[RP2040_IRQ_GPIO_COUNT]; 57 | uint8_t pia_gpio[RP2040_PIA_GPIO_COUNT]; 58 | } STATE_RP2040; 59 | 60 | 61 | /* 62 | * PROTOTYPES 63 | */ 64 | uint8_t sample_interrupts(void); 65 | void flash_led(uint8_t count); 66 | 67 | 68 | #endif // _E6809_HEADER_ 69 | -------------------------------------------------------------------------------- /source/monitor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * Monitor code 4 | * 5 | * @version 0.0.2 6 | * @author smittytone 7 | * @copyright 2025 8 | * @licence MIT 9 | * 10 | */ 11 | #ifndef _MONITOR_HEADER_ 12 | #define _MONITOR_HEADER_ 13 | 14 | 15 | /* 16 | * CONSTANTS 17 | */ 18 | #define DEBOUNCE_TIME_US 5000 // 5ms 19 | #define UPLOAD_TIMEOUT_US 20000000 // 20s 20 | 21 | #define DISPLAY_LEFT 0 22 | #define DISPLAY_RIGHT 1 23 | 24 | #define MENU_MODE_MAIN 0 25 | #define MENU_MAIN_ADDR 1 26 | #define MENU_MAIN_BYTE 2 27 | #define MENU_MAIN_RUN_STEP 3 28 | #define MENU_MAIN_RUN 4 29 | #define INPUT_MAIN_ADDR 0x8000 30 | #define INPUT_MAIN_BYTE 0x4000 31 | #define INPUT_MAIN_RUN_STEP 0x2000 32 | #define INPUT_MAIN_RUN 0x1000 33 | #define INPUT_MAIN_MEM_UP 0x0008 34 | #define INPUT_MAIN_MEM_DOWN 0x0001 35 | #define INPUT_MAIN_LOAD 0x0800 36 | #define INPUT_MAIN_MASK 0xF809 37 | 38 | #define MENU_MODE_STEP 10 39 | #define INPUT_STEP_NEXT 0x8000 40 | #define INPUT_STEP_SHOW_CC 0x4000 41 | #define INPUT_STEP_SHOW_AD 0x2000 42 | #define INPUT_STEP_EXIT 0x1000 43 | #define INPUT_STEP_MEM_UP 0x0008 44 | #define INPUT_STEP_MEM_DOWN 0x0001 45 | #define INPUT_STEP_MASK 0xF009 46 | 47 | #define MENU_MODE_CONFIRM 20 48 | #define MENU_CONF_OK 21 49 | #define MENU_CONF_CANCEL 22 50 | #define MENU_CONF_CONTINUE 23 51 | #define INPUT_CONF_OK 0x8000 52 | #define INPUT_CONF_CANCEL 0x1000 53 | #define INPUT_CONF_CONTINUE 0x4000 54 | #define INPUT_CONF_MASK_ADDR 0x9000 55 | #define INPUT_CONF_MASK_BYTE 0xD000 56 | 57 | #define MENU_MODE_RUN 30 58 | #define MENU_MODE_RUN_DONE 31 59 | #define INPUT_RUN_MASK 0xFFFF 60 | 61 | 62 | /* 63 | * PROTOTYPES 64 | */ 65 | bool init_board(void); 66 | void monitor_event_loop(void); 67 | 68 | 69 | #endif // _MONITOR_HEADER_ 70 | -------------------------------------------------------------------------------- /e6809.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 21 | 22 | 24 | 25 | 27 | 28 | 30 | 31 | 33 | 34 | 36 | 37 | 39 | 40 | 42 | 43 | 45 | 46 | 48 | 49 | 51 | 52 | 54 | 55 | 56 | 59 | 61 | 62 | 64 | 65 | 67 | 68 | 69 | 72 | 74 | 75 | 76 | 79 | 81 | 82 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /source/ht16k33.h: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * Display driver 4 | * 5 | * @version 0.0.2 6 | * @author smittytone 7 | * @copyright 2025 8 | * @licence MIT 9 | * 10 | */ 11 | #ifndef _HT16K33_HEADER_ 12 | #define _HT16K33_HEADER_ 13 | 14 | 15 | #include 16 | #include 17 | 18 | 19 | /* 20 | * Constants 21 | */ 22 | #define I2C_PORT i2c0 23 | #define I2C_FREQUENCY 400000 24 | #define ON 1 25 | #define OFF 0 26 | #define SDA_GPIO 20 27 | #define SCL_GPIO 21 28 | 29 | #define HT16K33_GENERIC_DISPLAY_ON 0x81 30 | #define HT16K33_GENERIC_DISPLAY_OFF 0x80 31 | #define HT16K33_GENERIC_SYSTEM_ON 0x21 32 | #define HT16K33_GENERIC_SYSTEM_OFF 0x20 33 | #define HT16K33_GENERIC_DISPLAY_ADDRESS 0x00 34 | #define HT16K33_GENERIC_CMD_BRIGHTNESS 0xE0 35 | #define HT16K33_GENERIC_CMD_BLINK 0x81 36 | #define HT16K33_SEGMENT_COLON_ROW 0x04 37 | #define HT16K33_SEGMENT_MINUS_CHAR 0x10 38 | #define HT16K33_SEGMENT_DEGREE_CHAR 0x11 39 | #define HT16K33_SEGMENT_SPACE_CHAR 0x00 40 | #define HT16K33_SEGMENT_COLON_ROW 0x04 41 | 42 | 43 | /* 44 | * PROTOTYPES 45 | */ 46 | // I2C Functions 47 | void i2c_write_byte(uint8_t address, uint8_t byte); 48 | void i2c_write_block(uint8_t address, uint8_t *data, uint8_t count); 49 | 50 | // Display Functions 51 | void ht16k33_init(uint8_t address, uint8_t *buffer); 52 | void ht16k33_brightness(uint8_t address, uint8_t brightness); 53 | void ht16k33_clear(uint8_t address, uint8_t *buffer); 54 | void ht16k33_draw(uint8_t address, uint8_t *buffer); 55 | void ht16k33_set_number(uint8_t address, uint8_t *buffer, uint16_t number, uint8_t digit, bool has_dot); 56 | void ht16k33_set_alpha(uint8_t address, uint8_t *buffer, char chr, uint8_t digit, bool has_dot); 57 | void ht16k33_set_glyph(uint8_t address, uint8_t *buffer, uint8_t glyph, uint8_t digit, bool has_dot); 58 | void ht16k33_show_colon(uint8_t address, uint8_t *buffer, bool show); 59 | 60 | 61 | #endif // _HT16K33_HEADER_ 62 | -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Deploy compiled firmware to RP2040-based board 4 | # 5 | # NOTE Requires USB IO enabled via Pico SDK call `stdio_usb_init()` 6 | # on currently running RP2040 code. 7 | # 8 | # Usage: 9 | # ./deploy.sh {path/to/device} {path/to/uf2} 10 | # 11 | # Examples: 12 | # macOS: ./deploy.sh /dev/cu.usbmodem1.1 /build/App-IRQs/IRQS_DEMO.uf2 13 | # Linux RPiOS: ./deploy.sh /dev/ttyACMO /build/App-IRQs/IRQS_DEMO.uf2 14 | 15 | show_error_and_exit() { 16 | echo "[ERROR] $1" 17 | exit 1 18 | } 19 | 20 | if [[ -z ${1} ]]; then 21 | echo "Usage: deploy.sh {path/to/device} {path/to/uf2}" 22 | exit 0 23 | fi 24 | 25 | if [[ -z ${2} || ${2##*.} != "uf2" ]]; then 26 | echo "[ERROR] No .uf2 file specified" 27 | exit 1 28 | fi 29 | 30 | if [[ ! -f ${2} ]]; then 31 | echo "[ERROR] ${2} cannot be found" 32 | exit 1 33 | fi 34 | 35 | # Put the Pico onto BOOTSEL mode 36 | platform=$(uname) 37 | if [[ ${platform} = Darwin ]]; then 38 | # macOS mount path 39 | pico_path=/Volumes/RPI-RP2 40 | stty -f ${1} 1200 || show_error_and_exit "Could not connect to device ${1}" 41 | else 42 | # NOTE This is for Raspberry Pi -- you may need to change it 43 | # depending on how you or your OS locate USB drive mount points 44 | pico_path="/media/$USER/RPI-RP2" 45 | stty -F ${1} 1200 || show_error_and_exit "Could not connect to device ${1}" 46 | 47 | # Allow for command line usage -- ie. not in a GUI terminal 48 | # Command line is SHLVL 1, so script is SHLVL 2 (under the GUI we'd be a SHLVL 3) 49 | if [[ $SHLVL -eq 2 ]]; then 50 | # Mount the disk, but allow time for it to appear (not immediate on RPi) 51 | sleep 5 52 | rp2_disk=$(sudo fdisk -l | grep FAT16 | cut -f 1 -d ' ') 53 | if [[ -z ${rp2_disk} ]]; then 54 | show_error_and_exit "Could not see device ${1}" 55 | fi 56 | 57 | sudo mkdir ${pico_path} || show_error_and_exit "Could not make mount point ${pico_path}" 58 | sudo mount ${rp2_disk} ${pico_path} -o rw || show_error_and_exit "Could not mount device ${1}" 59 | fi 60 | fi 61 | 62 | echo "Waiting for Pico to mount..." 63 | count=0 64 | while [ ! -d ${pico_path} ]; do 65 | sleep 0.1 66 | ((count+=1)) 67 | [[ ${count} -eq 200 ]] && show_error_and_exit "Pico mount timed out" 68 | done 69 | sleep 0.5 70 | 71 | # Copy the target file 72 | echo "Copying ${2} to ${1}..." 73 | if [[ ${platform} = Darwin ]]; then 74 | cp ${2} ${pico_path} 75 | else 76 | sudo cp ${2} ${pico_path} 77 | if [[ $SHLVL -eq 2 ]]; then 78 | # We're at the command line, so unmount (RPi GUI does this automatically) 79 | sudo umount ${rp2_disk} && echo "Pico unmounted" && sudo rm -rf ${pico_path} && echo "Mountpoint removed" 80 | fi 81 | fi 82 | echo Done 83 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Deploy compiled firmware to an RP2040-based board operating 4 | # as a Depot USB-to-I2C hardware bridge 5 | # 6 | # Version 1.2.3 7 | # 8 | # NOTE For this to work, the RP2040 must be running code that uses 9 | # calls `stdio_usb_init()` (as Depot firmware does). Once that is 10 | # the case, the RP2040 board will appear under macOS and Linux as 11 | # a device in `/dev`, eg. `/dev/cu.usbmodem.1` under macOS. 12 | # 13 | # Usage: 14 | # ./deploy.sh {path/to/device} {path/to/uf2} 15 | # 16 | # Examples: 17 | # macOS: ./deploy.sh /dev/cu.usbmodem1.1 /build/firmware/pico/firmware_pico.uf2 18 | # Linux RPiOS: ./deploy.sh /dev/ttyACMO /build/firmware/pico/firmware_pico.uf2 19 | 20 | 21 | show_error_and_exit() { 22 | echo "[ERROR] $1" 23 | exit 1 24 | } 25 | 26 | if [[ -z ${1} ]]; then 27 | echo "Usage: deploy.sh {path/to/device} {path/to/uf2}" 28 | exit 0 29 | fi 30 | 31 | if [[ -z ${2} || ${2##*.} != "uf2" ]]; then 32 | echo "[ERROR] No .uf2 file specified" 33 | exit 1 34 | fi 35 | 36 | if [[ ! -f ${2} ]]; then 37 | echo "[ERROR] ${2} cannot be found" 38 | exit 1 39 | fi 40 | 41 | # Put the Pico onto BOOTSEL mode 42 | platform=$(uname) 43 | if [[ ${platform} = Darwin ]]; then 44 | # macOS mount path 45 | pico_path=/Volumes/RPI-RP2 46 | stty -f ${1} 1200 || show_error_and_exit "Could not connect to device ${1}" 47 | else 48 | # NOTE This is for Raspberry Pi -- you may need to change it 49 | # depending on how you or your OS locate USB drive mount points 50 | pico_path="/media/$USER/RPI-RP2" 51 | stty -F ${1} 1200 || show_error_and_exit "Could not connect to device ${1}" 52 | 53 | # Allow for command line usage -- ie. not in a GUI terminal 54 | # Command line is SHLVL 1, so script is SHLVL 2 (under the GUI we'd be a SHLVL 3) 55 | if [[ $SHLVL -eq 2 ]]; then 56 | # Mount the disk, but allow time for it to appear (not immediate on RPi) 57 | sleep 5 58 | rp2_disk=$(sudo fdisk -l | grep FAT16 | cut -f 1 -d ' ') 59 | if [[ -z ${rp2_disk} ]]; then 60 | show_error_and_exit "Could not see device ${1}" 61 | fi 62 | 63 | sudo mkdir ${pico_path} || show_error_and_exit "Could not make mount point ${pico_path}" 64 | sudo mount ${rp2_disk} ${pico_path} -o rw || show_error_and_exit "Could not mount device ${1}" 65 | fi 66 | fi 67 | 68 | echo "Waiting for Pico to mount..." 69 | count=0 70 | while [ ! -d ${pico_path} ]; do 71 | sleep 0.1 72 | ((count+=1)) 73 | [[ ${count} -eq 200 ]] && show_error_and_exit "Pico mount timed out" 74 | done 75 | sleep 0.5 76 | 77 | # Copy the target file 78 | echo "Copying ${2} to ${1}..." 79 | if [[ ${platform} = Darwin ]]; then 80 | cp ${2} ${pico_path} 81 | else 82 | sudo cp ${2} ${pico_path} 83 | if [[ $SHLVL -eq 2 ]]; then 84 | # We're at the command line, so unmount (RPi GUI does this automatically) 85 | sudo umount ${rp2_disk} && echo "Pico unmounted" && sudo rm -rf ${pico_path} && echo "Mountpoint removed" 86 | fi 87 | fi 88 | echo Done 89 | -------------------------------------------------------------------------------- /source/keypad.c: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * Keypad driver 4 | * 5 | * @version 0.0.2 6 | * @author Pimoroni, smittytone 7 | * @copyright 2025 8 | * @licence MIT 9 | * 10 | */ 11 | 12 | #include 13 | // Pico 14 | #include "pico/stdlib.h" 15 | #include "hardware/gpio.h" 16 | #include "hardware/i2c.h" 17 | #include "hardware/spi.h" 18 | // App 19 | #include "ht16k33.h" 20 | #include "keypad.h" 21 | 22 | 23 | /* 24 | * STATICS 25 | */ 26 | static bool check_board_presence(void); 27 | static void keypad_set_led_at(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b); 28 | static void keypad_set_led_data(uint16_t o, uint8_t r, uint8_t g, uint8_t b); 29 | 30 | 31 | /* 32 | * GLOBALS 33 | */ 34 | uint8_t led_buffer[72]; // Dimension = H x W x 4 + 8 35 | uint8_t *led_data = led_buffer + 4; 36 | 37 | 38 | /** 39 | * @brief Initialise the keypad, setting the default brightness and 40 | * bringing up the I2C0 and SPI0 peripherals. All key pixels 41 | * initially set to white. 42 | * 43 | * @retval `true` if the board is detected, otherwise `false`. 44 | */ 45 | bool keypad_init(void) { 46 | 47 | for (uint32_t i = 0 ; i < sizeof(led_buffer) ; ++i) { 48 | led_buffer[i] = 0x00; 49 | } 50 | 51 | // Must be called to init each LED frame 52 | keypad_set_brightness(DEFAULT_BRIGHTNESS); 53 | 54 | // Set up I2C to read buttons 55 | i2c_init(i2c0, 400000); 56 | gpio_set_function(KEYPAD_PIN_KEYS_SDA, GPIO_FUNC_I2C); 57 | gpio_set_function(KEYPAD_PIN_KEYS_SCL, GPIO_FUNC_I2C); 58 | gpio_pull_up(KEYPAD_PIN_KEYS_SDA); 59 | gpio_pull_up(KEYPAD_PIN_KEYS_SCL); 60 | 61 | // Check a board is connected 62 | if (!check_board_presence()) { 63 | i2c_deinit(i2c0); 64 | return false; 65 | } 66 | 67 | // Set up SPI to set LEDs 68 | spi_init(spi0, 4194304); 69 | gpio_set_function(KEYPAD_PIN_LEDS_CS, GPIO_FUNC_SIO); 70 | gpio_set_dir(KEYPAD_PIN_LEDS_CS, GPIO_OUT); 71 | gpio_put(KEYPAD_PIN_LEDS_CS, 1); 72 | gpio_set_function(KEYPAD_PIN_LEDS_SCK, GPIO_FUNC_SPI); 73 | gpio_set_function(KEYPAD_PIN_LEDS_TX, GPIO_FUNC_SPI); 74 | 75 | // Set the LEDs 76 | keypad_set_all(0x20, 0x20, 0x20); 77 | keypad_update_leds(); 78 | return true; 79 | } 80 | 81 | 82 | /** 83 | * @brief Write the pixel colour data out to SPI. 84 | */ 85 | void keypad_update_leds(void) { 86 | 87 | gpio_put(KEYPAD_PIN_LEDS_CS, 0); 88 | spi_write_blocking(spi0, led_buffer, sizeof(led_buffer)); 89 | gpio_put(KEYPAD_PIN_LEDS_CS, 1); 90 | } 91 | 92 | 93 | /** 94 | * @brief Set the brightness for all pixels by writing the brightness bits 95 | * to the LED data array for each pixel. 96 | * 97 | * @param brightness: The pixels brightness, 0.0-1.0 exclusive. 98 | */ 99 | void keypad_set_brightness(float brightness) { 100 | 101 | if (brightness < 0.0 || brightness > 1.0) return; 102 | for (uint16_t i = 0 ; i < NUM_KEYS ; ++i) { 103 | led_data[i * 4] = 0b11100000 | (uint8_t)(brightness * (float)0b11111); 104 | } 105 | } 106 | 107 | 108 | /** 109 | * @brief Set the data for a single key's pixel, using the specified colour, 110 | * and its key value. 111 | * 112 | * @param i: The pixel by its key value, 0x00-0F. 113 | * @param r: The red colour component, 0x00-FF. 114 | * @param g: The green colour component, 0x00-FF. 115 | * @param b: The blue colour component, 0x00-FF. 116 | */ 117 | void keypad_set_led(uint8_t i, uint8_t r, uint8_t g, uint8_t b) { 118 | 119 | if (i < 0 || i >= NUM_KEYS) return; 120 | keypad_set_led_data(i * 4, r, g, b); 121 | } 122 | 123 | 124 | /** 125 | * @brief Turn on every key's pixel to the specified colour. 126 | * 127 | * @param r: The red colour component, 0x00-FF. 128 | * @param g: The green colour component, 0x00-FF. 129 | * @param b: The blue colour component, 0x00-FF. 130 | */ 131 | void keypad_set_all(uint8_t r, uint8_t g, uint8_t b) { 132 | 133 | for (uint16_t i = 0 ; i < NUM_KEYS ; ++i) { 134 | keypad_set_led(i, r, g, b); 135 | } 136 | } 137 | 138 | 139 | /** 140 | * @brief Turn off every key's pixel. 141 | */ 142 | void keypad_clear(void) { 143 | 144 | for (uint16_t i = 0 ; i < NUM_KEYS ; ++i) { 145 | keypad_set_led(i, 0, 0, 0); 146 | } 147 | } 148 | 149 | 150 | /** 151 | * @brief Read the TCA9555 IO expander to determine key states. 152 | * 153 | * @retval A 16-bit value in which each represents that key's state, 154 | * eg. bit 0 is key `0`, bit 15 is key `F`. 155 | */ 156 | uint16_t keypad_get_button_states(void) { 157 | 158 | uint8_t input_buffer[2]; 159 | uint8_t tca9555_reg = 0; 160 | i2c_write_blocking(i2c0, KEYPAD_I2C_ADDRESS, &tca9555_reg, 1, true); 161 | i2c_read_blocking(i2c0, KEYPAD_I2C_ADDRESS, input_buffer, 2, false); 162 | 163 | // Read value is 0 = pressed, 1 = not pressed, so invert the return value 164 | return ~((input_buffer[0]) | (input_buffer[1] << 8)); 165 | } 166 | 167 | 168 | /** 169 | * @brief Set the data for a single key's pixel, using the specified colour, 170 | * and its co-ordinates on the key grid. 171 | * 172 | * @param x: The pixel by its x coordinate, 0-3. 173 | * @param y: The pixel by its y coordinate, 0-3. 174 | * @param r: The red colour component, 0x00-FF. 175 | * @param g: The green colour component, 0x00-FF. 176 | * @param b: The blue colour component, 0x00-FF. 177 | */ 178 | static void keypad_set_led_at(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b) { 179 | 180 | if (x < 0 || x >= KEYPAD_WIDTH || y < 0 || y >= KEYPAD_HEIGHT) return; 181 | keypad_set_led_data((x + (y * KEYPAD_WIDTH)) * 4, r, g, b); 182 | } 183 | 184 | 185 | /** 186 | * @brief Set the data for a single key's pixel, using the specified colour 187 | * and its index in the data array. 188 | * 189 | * @param o: The pixel by its index in the data array. 190 | * @param r: The red colour component, 0x00-FF. 191 | * @param g: The green colour component, 0x00-FF. 192 | * @param b: The blue colour component, 0x00-FF. 193 | */ 194 | static void keypad_set_led_data(uint16_t o, uint8_t r, uint8_t g, uint8_t b) { 195 | 196 | led_data[o + 0] = 0xFF; 197 | led_data[o + 1] = b; 198 | led_data[o + 2] = g; 199 | led_data[o + 3] = r; 200 | } 201 | 202 | 203 | /** 204 | * @brief Check for the presence of the TCA9555 IO expander at 0x20. 205 | * Will detect *any thing* at 0x20, however. 206 | * 207 | * @retval `true` if the board is detected, otherwise `false`. 208 | */ 209 | static bool check_board_presence(void) { 210 | 211 | uint8_t rxdata; 212 | int ret = i2c_read_blocking(i2c0, KEYPAD_I2C_ADDRESS, &rxdata, 1, false); 213 | return !(rxdata < 0); 214 | } 215 | -------------------------------------------------------------------------------- /source/ht16k33.c: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * Display driver 4 | * 5 | * @version 0.0.2 6 | * @author smittytone 7 | * @copyright 2025 8 | * @licence MIT 9 | * 10 | */ 11 | 12 | // Pico 13 | #include "hardware/i2c.h" 14 | // App 15 | #include "ht16k33.h" 16 | 17 | 18 | /* 19 | * STATICS 20 | */ 21 | static void ht16k33_power(uint8_t address, uint8_t on); 22 | 23 | 24 | /* 25 | * GLOBALS 26 | */ 27 | const uint8_t CHARSET[18] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 28 | 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x63}; 29 | const uint8_t POS[4] = {0, 2, 6, 8}; 30 | // 0x5F, 0x7C, 0x58, 0x5E, 0x7B, 0x71 31 | 32 | 33 | /* 34 | * I2C FUNCTIONS 35 | */ 36 | 37 | /** 38 | * @brief Convenience function to write a single byte to the matrix. 39 | */ 40 | void i2c_write_byte(uint8_t address, uint8_t byte) { 41 | 42 | i2c_write_blocking(I2C_PORT, address, &byte, 1, false); 43 | } 44 | 45 | /** 46 | * @brief Convenience function to write a 'count' bytes to the matrix 47 | */ 48 | void i2c_write_block(uint8_t address, uint8_t *data, uint8_t count) { 49 | 50 | i2c_write_blocking(I2C_PORT, address, data, count, false); 51 | } 52 | 53 | 54 | /* 55 | * HT16K33 SEGMENT LED FUNCTIONS 56 | */ 57 | 58 | /** 59 | * @brief Initialise the display. 60 | * NOTE Assumes the display is on I2C0. 61 | * @param address: The display's I2C address. 62 | * @param buffer: Pointer to the display code's data buffer. 63 | */ 64 | void ht16k33_init(uint8_t address, uint8_t *buffer) { 65 | 66 | ht16k33_power(address, 1); 67 | ht16k33_brightness(address, 6); 68 | ht16k33_clear(address, buffer); 69 | ht16k33_draw(address, buffer); 70 | } 71 | 72 | /** 73 | * @brief Power the display on or off. 74 | * 75 | * @Param address: The display's I2C address. 76 | * @Param on: Whether to power up the display (`true`) or turn it off (`false`). 77 | */ 78 | static void ht16k33_power(uint8_t address, uint8_t on) { 79 | 80 | i2c_write_byte(address, on == ON ? HT16K33_GENERIC_SYSTEM_ON : HT16K33_GENERIC_DISPLAY_OFF); 81 | i2c_write_byte(address, on == ON ? HT16K33_GENERIC_DISPLAY_ON : HT16K33_GENERIC_SYSTEM_OFF); 82 | } 83 | 84 | /** 85 | * @brief Power the display on or off. 86 | * 87 | * @param address: The display's I2C address. 88 | * @param brightness: The brightness value, 1-15. 89 | */ 90 | void ht16k33_brightness(uint8_t address, uint8_t brightness) { 91 | 92 | // Set the LED brightness 93 | if (brightness < 0 || brightness > 15) brightness = 15; 94 | i2c_write_byte(address, HT16K33_GENERIC_CMD_BRIGHTNESS | brightness); 95 | } 96 | 97 | /** 98 | * @brief Clear the display. 99 | * Writes to the buffer, but not the device: call `draw()` after. 100 | * 101 | * @param address: The display's I2C address. 102 | * @param buffer: Pointer to the display code's data buffer. 103 | */ 104 | void ht16k33_clear(uint8_t address, uint8_t *buffer) { 105 | 106 | // Clear the display buffer and then write it out 107 | for (uint8_t i = 0 ; i < 16 ; ++i) buffer[i] = 0; 108 | } 109 | 110 | /** 111 | * @brief Writes the buffer to the device. 112 | * 113 | * @param address: The display's I2C address. 114 | * @param buffer: Pointer to the display code's data buffer. 115 | */ 116 | void ht16k33_draw(uint8_t address, uint8_t *buffer) { 117 | 118 | // Set up the buffer holding the data to be 119 | // transmitted to the LED 120 | uint8_t tx_buffer[17]; 121 | for (uint8_t i = 0 ; i < 17 ; ++i) tx_buffer[i] = 0; 122 | 123 | // Span the 8 bytes of the graphics buffer 124 | // across the 16 bytes of the LED's buffer 125 | for (uint8_t i = 0 ; i < 16 ; ++i) { 126 | tx_buffer[i + 1] = buffer[i]; 127 | } 128 | 129 | // Write out the transmit buffer 130 | i2c_write_block(address, tx_buffer, sizeof(tx_buffer)); 131 | } 132 | 133 | /** 134 | * @brief Set the specified digit to a hex number. 135 | * 136 | * @param address: The display's I2C address. 137 | * @param buffer: Pointer to the display code's data buffer. 138 | * @param number: The value to show, 0x00-0x0F. 139 | * @param digit: The target display digit, 0-3. 140 | * @param has_dot: Illuminate the decimal point (`true`) or not (`false`). 141 | */ 142 | void ht16k33_set_number(uint8_t address, uint8_t *buffer, uint16_t number, uint8_t digit, bool has_dot) { 143 | 144 | if (digit > 3) return; 145 | if (number > 15) return; 146 | if (number < 10) ht16k33_set_alpha(address, buffer, '0' + number, digit, has_dot); 147 | if (number > 9) ht16k33_set_alpha(address, buffer, 'a' + (number - 10), digit, has_dot); 148 | } 149 | 150 | /** 151 | * @brief Set the specified digit to a character from the driver's 152 | * character set -- see `CHARSET`, above. 153 | * 154 | * @param address: The display's I2C address. 155 | * @param buffer: Pointer to the display code's data buffer. 156 | * @param chr: The character to show, from the CHARSET. 157 | * @param digit: The target display digit, 0-3. 158 | * @param has_dot: Illuminate the decimal point (`true`) or not (`false`). 159 | */ 160 | void ht16k33_set_alpha(uint8_t address, uint8_t *buffer, char chr, uint8_t digit, bool has_dot) { 161 | 162 | if (digit > 3) return; 163 | 164 | uint8_t char_val = 0xFF; 165 | if (chr == ' ') { 166 | char_val = HT16K33_SEGMENT_SPACE_CHAR; 167 | } else if (chr == '-') { 168 | char_val = HT16K33_SEGMENT_MINUS_CHAR; 169 | } else if (chr == 'o') { 170 | char_val = HT16K33_SEGMENT_DEGREE_CHAR; 171 | } else if (chr >= 'a' && chr <= 'f') { 172 | char_val = (uint8_t)chr - 87; 173 | } else if (chr >= '0' && chr <= '9') { 174 | char_val = (uint8_t)chr - 48; 175 | } 176 | 177 | if (char_val == 0xFF) return; 178 | buffer[POS[digit]] = CHARSET[char_val]; 179 | if (has_dot) buffer[POS[digit]] |= 0x80; 180 | } 181 | 182 | /** 183 | * @brief Set the specified digit to an arbitrary glyph, one bit per segment. 184 | * 185 | * @param address: The display's I2C address. 186 | * @param buffer: Pointer to the display code's data buffer. 187 | * @param glyph: The glyph value, 0x00-0x7F. 188 | * @param digit: The target display digit, 0-3. 189 | * @param has_dot: Illuminate the decimal point (`true`) or not (`false`). 190 | */ 191 | void ht16k33_set_glyph(uint8_t address, uint8_t *buffer, uint8_t glyph, uint8_t digit, bool has_dot) { 192 | 193 | if (glyph > 0x7F) return; 194 | buffer[POS[digit]] = glyph; 195 | if (has_dot) buffer[POS[digit]] |= 0x80; 196 | } 197 | 198 | /** 199 | * @brief Show or hide the display's colon symbol. 200 | * 201 | * @param address: The display's I2C address. 202 | * @param buffer: Pointer to the display code's data buffer. 203 | * @param how: Show (`true`) or hide (`false`) the colon. 204 | */ 205 | void ht16k33_show_colon(uint8_t address, uint8_t *buffer, bool show) { 206 | 207 | buffer[HT16K33_SEGMENT_COLON_ROW] = (show ? 0x02 : 0x00); 208 | } 209 | -------------------------------------------------------------------------------- /source/pia.c: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * Peripheral Interface Adapter (PIA) 4 | * 5 | * @version 0.0.2 6 | * @author smittytone 7 | * @copyright 2025 8 | * @licence MIT 9 | * 10 | */ 11 | 12 | #include 13 | // Pico 14 | #include "pico/stdlib.h" 15 | #include "hardware/gpio.h" 16 | // App 17 | #include "main.h" 18 | #include "cpu.h" 19 | #include "pia.h" 20 | 21 | 22 | /* 23 | * STATICS 24 | */ 25 | static void pia_set_data_byte(MC6821* pia); 26 | 27 | 28 | /* 29 | * GLOBALS 30 | */ 31 | extern REG_6809 reg; 32 | extern uint8_t mem[KB64]; 33 | extern STATE_RP2040 pico_state; 34 | 35 | 36 | /** 37 | * @brief Initialise a PIA. 38 | * 39 | * @param pia: Pointee to an MC6821 struc 40 | */ 41 | void pia_init(MC6821* pia) { 42 | 43 | // Perform a reset 44 | pia_reset(pia); 45 | } 46 | 47 | 48 | void pia_reset(MC6821* pia) { 49 | 50 | pia->enabled = true; 51 | 52 | // All output, all low 53 | // NOTE This is a local store, for reference 54 | pia->reg_output_a = 0; 55 | pia->reg_direction_a = 0; 56 | 57 | // CA interrupts disabled, CA2 is input 58 | pia->ca_1_can_interrupt = false; 59 | pia->ca_1_is_set_on_up = false; 60 | pia->ca_2_can_interrupt = false; 61 | pia->ca_2_is_set_on_up = false; 62 | pia->ca_2_is_output = false; 63 | 64 | // Update GPIO directions (set all to input with pullup) 65 | // See MC6821 Datasheet p.8 66 | for (uint8_t i = 0 ; i < 8 ; i++) { 67 | gpio_set_dir(*(pia->pa_pins + i), GPIO_IN); 68 | gpio_pull_up(*(pia->pa_pins + i)); 69 | } 70 | 71 | // Set the CA pins (inputs) 72 | gpio_set_dir(*(pia->ca_pins), GPIO_IN); 73 | gpio_set_dir(*(pia->ca_pins + 1), GPIO_IN); 74 | 75 | // Update the Control Register 76 | 77 | } 78 | 79 | 80 | /** 81 | * @brief Update the PIA status based on the current value of 82 | * the Control Register. 83 | */ 84 | void pia_process_control(MC6821* pia) { 85 | 86 | uint16_t reg_value = (uint16_t)*(pia->reg_control_a); 87 | 88 | pia->ca_1_can_interrupt = is_bit_set(reg_value, 0); 89 | pia->ca_1_is_set_on_up = is_bit_set(reg_value, 1); 90 | 91 | pia->ca_2_can_interrupt = is_bit_set(reg_value, 5); 92 | 93 | if (pia->ca_2_can_interrupt) { 94 | pia->ca_2_is_output = true; 95 | if (is_bit_set(reg_value, 4)) { 96 | gpio_put(*(pia->ca_pins + 1), is_bit_set(reg_value, 3)); 97 | } else { 98 | // See MCP6821 Datasheet p.10 99 | } 100 | } else { 101 | pia->ca_2_is_output = false; 102 | pia->ca_2_is_set_on_up = is_bit_set(reg_value, 4); 103 | } 104 | 105 | // Data direction or Output register? 106 | pia->emit_output = is_bit_set(reg_value, 2); 107 | pia_set_data_byte(pia); 108 | } 109 | 110 | 111 | static void pia_set_data_byte(MC6821* pia) { 112 | 113 | if (pia->emit_output) { 114 | *(pia->reg_data_a) = pia->reg_output_a; 115 | } else { 116 | *(pia->reg_data_a) = pia->reg_direction_a; 117 | } 118 | } 119 | 120 | 121 | /* 122 | Set the PA direction based on the DDR 123 | */ 124 | void pia_set_gpio_direction(MC6821* pia, uint8_t pin) { 125 | 126 | // Pico GPIO directions: false is input, true is output 127 | uint8_t value = ((*pia->reg_data_a & (1 << pin)) >> pin); 128 | gpio_set_dir(*(pia->pa_pins + pin), (value == OUTPUT)); 129 | 130 | // If the pin is an output, set its pin level according 131 | // to the output register value 132 | if (value == OUTPUT) pia_set_gpio_output_state(pia, pin); 133 | } 134 | 135 | 136 | /* 137 | Get the RP2040 pin direction. 138 | 139 | - Returns: 1 for output, 0 for input 140 | */ 141 | uint8_t pia_get_gpio_direction(MC6821* pia, uint8_t pin) { 142 | 143 | return ((*pia->reg_data_a & (1 << pin)) >> pin); 144 | } 145 | 146 | 147 | /** 148 | * @brief Set the PA output value based on the OR. 149 | * 150 | * @note Assumes we have already checked that the pin 151 | * *is* an output. 152 | */ 153 | void pia_set_gpio_output_state(MC6821* pia, uint8_t pin) { 154 | 155 | uint8_t value = ((pia->reg_output_a & (1 << pin)) >> pin); 156 | gpio_put(*(pia->pa_pins + pin), (value == 1)); 157 | } 158 | 159 | /** 160 | * @brief Read a specific input and set its output register bit 161 | * accordingly. 162 | */ 163 | void pia_get_gpio_input_state(MC6821* pia, uint8_t pin) { 164 | 165 | if (gpio_get(*(pia->pa_pins + pin))) { 166 | pia->reg_output_a |= (1 << pin); 167 | } else { 168 | pia->reg_output_a &= !(1 << pin); 169 | } 170 | } 171 | 172 | 173 | /** 174 | * @brief Update the memory for the specified PIA. 175 | */ 176 | void pia_update(MC6821* pia) { 177 | 178 | if (!pia->enabled) return; 179 | 180 | // Update CR 181 | // Get bits 0-5 from memory; retain bits 6 and 7 182 | //pia->reg_control_a = (pia->reg_control_a & 0xC0) | (mem[*pia->reg_control_a] & 0x3F); 183 | pia_set_pia_ca(pia); 184 | pia_update_flags(pia); 185 | 186 | // Check the DDR Access bit 187 | uint8_t new = *pia->reg_data_a; 188 | if (*pia->reg_control_a & 0x04) { 189 | // Read in the Output Register from memory (set by CPU) 190 | // and set the pin state -- if its an output 191 | if (new != pia->reg_output_a) { 192 | pia->reg_output_a = new; 193 | for (uint8_t i = 0 ; i < 8 ; i++) { 194 | // Update all output pins states 195 | if (pia_get_gpio_direction(pia, i) == OUTPUT) pia_set_gpio_output_state(pia, i); 196 | } 197 | } 198 | } else { 199 | // Read in the DDR and set pin direction 200 | // NOTE This uses OR for state on output 201 | if (new != *pia->reg_data_a) { 202 | *pia->reg_data_a = new; 203 | for (uint8_t i = 0 ; i < 8 ; i++) { 204 | pia_set_gpio_direction(pia, i); 205 | } 206 | } 207 | } 208 | 209 | // Read inputs 210 | for (uint8_t i = 0 ; i < 8 ; i++) { 211 | // Update all input pins states: affects OR 212 | if (pia_get_gpio_direction(pia, i) == INPUT) pia_get_gpio_input_state(pia, i); 213 | } 214 | 215 | // Handle IRQs 216 | pia_check_irqs(); 217 | } 218 | 219 | 220 | void pia_update_flags(MC6821* pia) { 221 | 222 | pia->ca_1_can_interrupt = ((*pia->reg_control_a & 0x01) > 0); 223 | 224 | // Bit is 1, IRQ trigged on LOW to HIGH (up), else 225 | // on HIGH to LOW (down) 226 | pia->ca_1_is_set_on_up = ((*pia->reg_control_a & 0x02) > 0); 227 | 228 | bool is_output = ((*pia->reg_control_a & 0x20) > 0); 229 | if (pia->ca_2_is_output != is_output) { 230 | // Settings changed 231 | pia->ca_2_is_output = is_output; 232 | 233 | if (!pia->ca_2_is_output) { 234 | pia->ca_2_can_interrupt = ((*pia->reg_control_a & 0x08) > 0); 235 | 236 | // Bit is 1, IRQ trigged on LOW to HIGH (up), else 237 | // on HIGH to LOW (down) 238 | pia->ca_2_is_set_on_up = ((*pia->reg_control_a & 0x10) > 0); 239 | } else { 240 | 241 | } 242 | } 243 | } 244 | 245 | 246 | void pia_check_irqs(void) { 247 | 248 | // TODO 249 | } 250 | 251 | 252 | /* 253 | Set CA2 -- if it's an output -- to value of CR bit 3 254 | */ 255 | void pia_set_pia_ca(MC6821* pia) { 256 | 257 | if (pia->ca_2_is_output & (*pia->reg_control_a & 0x10) > 0) { 258 | gpio_put(*(pia->ca_pins + 1), (*pia->reg_control_a & 0x08 > 0)); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /source/cpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * 4 | * @version 0.0.2 5 | * @author smittytone 6 | * @copyright 2025 7 | * @licence MIT 8 | * 9 | */ 10 | #ifndef _CPU_HEADER_ 11 | #define _CPU_HEADER_ 12 | 13 | 14 | /* 15 | * INCLUDES 16 | */ 17 | #include 18 | 19 | 20 | /* 21 | * CONSTANTS 22 | */ 23 | #define KB64 65536 24 | #define BRANCH_MARKER 0x20 25 | 26 | #define MODE_UNKNOWN 0 27 | #define MODE_IMMEDIATE 1 28 | #define MODE_DIRECT 2 29 | #define MODE_INDEXED 3 30 | #define MODE_EXTENDED 4 31 | #define MODE_INHERENT 5 32 | 33 | #define OPCODE_EXTENDED_1 0x10 34 | #define OPCODE_EXTENDED_2 0x11 35 | 36 | #define CC_C_BIT 0 37 | #define CC_V_BIT 1 38 | #define CC_Z_BIT 2 39 | #define CC_N_BIT 3 40 | #define CC_I_BIT 4 41 | #define CC_H_BIT 5 42 | #define CC_F_BIT 6 43 | #define CC_E_BIT 7 44 | 45 | #define REG_X 0 46 | #define REG_Y 1 47 | #define REG_U 2 48 | #define REG_S 3 49 | 50 | #define NMI_BIT 0 51 | #define IRQ_BIT 1 52 | #define FIRQ_BIT 2 53 | #define RESET_BIT 3 54 | 55 | #define SIGN_BIT_8 7 56 | #define SIGN_BIT_16 15 57 | 58 | #define MASK_ZC 0xFA 59 | #define MASK_NZ 0xF3 60 | #define MASK_NZC 0xF2 61 | #define MASK_NZV 0xF1 62 | #define MASK_NZVC 0xF0 63 | 64 | #define PUSH_PULL_CC_REG 0x01 65 | #define PUSH_PULL_ALL_REGS 0xFE 66 | #define PUSH_PULL_EVERY_REG 0xFF 67 | #define PUSH_PULL_PC_REG 0x80 68 | 69 | #define PUSH_TO_HARD_STACK true 70 | 71 | #define START_VECTORS 0xFFF0 72 | #define SWI3_VECTOR 0xFFF2 73 | #define SWI2_VECTOR 0xFFF4 74 | #define FIRQ_VECTOR 0xFFF6 75 | #define IRQ_VECTOR 0xFFF8 76 | #define SWI1_VECTOR 0xFFFA 77 | #define NMI_VECTOR 0xFFFC 78 | #define RESET_VECTOR 0xFFFE 79 | 80 | #define BUS_STATE_RUN_BA 0 81 | #define BUS_STATE_INT_BA 1 82 | #define BUS_STATE_SYN_BA 2 83 | #define BUS_STATE_HLT_BA 3 84 | #define BUS_STATE_RUN_BS 4 85 | #define BUS_STATE_INT_BS 5 86 | #define BUS_STATE_SYN_BS 6 87 | #define BUS_STATE_HLT_BS 7 88 | 89 | #define BREAK_TO_MONITOR 0xFF 90 | 91 | #define IRQ_STATE_ASSERTED 1 92 | #define IRQ_STATE_HANDLED 2 93 | 94 | #define IS_8_BIT false 95 | #define IS_16_BIT true 96 | 97 | #define DAA_CONVERSION_FACTOR 6 98 | 99 | 100 | /* 101 | * STRUCTURES 102 | */ 103 | typedef struct { 104 | uint8_t a; 105 | uint8_t b; 106 | uint16_t d; // Only used for TFR/EXG ops which require a pointer to a unit16_t 107 | uint16_t x; 108 | uint16_t y; 109 | uint16_t s; 110 | uint16_t u; 111 | uint16_t pc; 112 | uint8_t cc; 113 | uint8_t dp; 114 | } REG_6809; 115 | 116 | typedef struct { 117 | bool wait_for_interrupt; 118 | bool is_sync; 119 | bool nmi_disarmed; 120 | uint8_t interrupts; 121 | // May drop these below 122 | uint8_t bus_state_pins; 123 | uint8_t interrupt_state; 124 | } STATE_6809; 125 | 126 | 127 | /* 128 | * PROTOTYPES 129 | */ 130 | uint32_t process_next_instruction(void); 131 | // Op Primary Functions 132 | void abx(void); 133 | void adc(uint8_t op, uint8_t mode); 134 | void add(uint8_t op, uint8_t mode); 135 | void add_16(uint8_t op, uint8_t mode); 136 | void and(uint8_t op, uint8_t mode); 137 | void andcc(uint8_t value); 138 | void asl(uint8_t op, uint8_t mode); 139 | void asr(uint8_t op, uint8_t mode); 140 | void bit(uint8_t op, uint8_t mode); 141 | void clr(uint8_t op, uint8_t mode); 142 | void cmp(uint8_t op, uint8_t mode); 143 | void cmp_16(uint8_t op, uint8_t mode, uint8_t ex_op); 144 | void com(uint8_t op, uint8_t mode); 145 | void cwai(void); 146 | void daa(void); 147 | void dec(uint8_t op, uint8_t mode); 148 | void eor(uint8_t op, uint8_t mode); 149 | void inc(uint8_t op, uint8_t mode); 150 | void jmp(uint8_t mode); 151 | void jsr(uint8_t mode); 152 | void ld(uint8_t op, uint8_t mode); 153 | void ld_16(uint8_t op, uint8_t mode, uint8_t ex_op); 154 | void lea(uint8_t op); 155 | void lsr(uint8_t op, uint8_t mode); 156 | void mul(void); 157 | void neg(uint8_t op, uint8_t mode); 158 | void orr(uint8_t op, uint8_t mode); 159 | void orcc(uint8_t value); 160 | void rol(uint8_t op, uint8_t mode); 161 | void ror(uint8_t op, uint8_t mode); 162 | void rti(void); 163 | void rts(void); 164 | void sbc(uint8_t op, uint8_t mode); 165 | void sex(void); 166 | void st(uint8_t op, uint8_t mode); 167 | void st_16(uint8_t op, uint8_t mode, uint8_t ex_op); 168 | void sub(uint8_t op, uint8_t mode); 169 | void sub_16(uint8_t op, uint8_t mode, uint8_t ex_op); 170 | void swi(uint8_t number); 171 | void sync(void); 172 | void tst(uint8_t op, uint8_t mode); 173 | // Op Helper Functions 174 | uint8_t alu(uint8_t value_1, uint8_t value_2, bool use_carry); 175 | uint16_t alu_16(uint16_t value_1, uint16_t value_2, bool use_carry); 176 | uint8_t add_no_carry(uint8_t value, uint8_t amount); 177 | uint8_t add_with_carry(uint8_t value, uint8_t amount); 178 | uint8_t subtract(uint8_t value, uint8_t amount); 179 | uint16_t subtract_16(uint16_t value_1, uint16_t value_2); 180 | uint8_t sub_with_carry(uint8_t value, uint8_t amount); 181 | uint8_t base_sub(uint8_t value, uint8_t amount, bool use_carry); 182 | uint8_t do_and(uint8_t value, uint8_t amount); 183 | uint8_t do_or(uint8_t value, uint8_t amount); 184 | uint8_t do_xor(uint8_t value, uint8_t amount); 185 | uint8_t arith_shift_right(uint8_t value); 186 | uint8_t logic_shift_left(uint8_t value); 187 | uint8_t logic_shift_right(uint8_t value); 188 | uint8_t partial_shift_right(uint8_t value); 189 | uint8_t rotate_left(uint8_t value); 190 | uint8_t rotate_right(uint8_t value); 191 | void compare(uint8_t value, uint8_t amount); 192 | uint8_t negate(uint8_t value, bool ignore); 193 | uint8_t ones_complement(uint8_t value); 194 | uint8_t twos_complement(uint8_t value); 195 | uint8_t complement(uint8_t value); 196 | uint8_t decrement(uint8_t value); 197 | uint8_t increment(uint8_t value); 198 | uint8_t *set_reg_ptr(uint8_t reg_code); 199 | uint16_t *set_reg_16_ptr(uint8_t reg_code); 200 | void transfer_decode(uint8_t reg_code, bool is_swap); 201 | void transfer_decode(uint8_t reg_code, bool is_swap); 202 | uint8_t exchange(uint8_t value, uint8_t reg_code); 203 | uint16_t exchange_16(uint16_t value, uint8_t reg_code); 204 | void load_effective(uint16_t amount, uint8_t reg_code); 205 | void push(bool to_hardware, uint8_t post_byte); 206 | void pull(bool from_hardware, uint8_t post_byte); 207 | void test(uint8_t value); 208 | void do_branch(uint8_t bop, bool is_long); 209 | void process_interrupt(uint8_t irq); 210 | // Addressing Functions 211 | uint16_t address_from_mode(uint8_t mode); 212 | // Misc 213 | void init_cpu(void); 214 | void init_vectors(uint16_t* vectors); 215 | void reset_registers(void); 216 | bool is_bit_set(uint16_t value, uint8_t bit); 217 | 218 | #endif // _CPU_HEADER_ 219 | -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * 4 | * @version 0.0.2 5 | * @author smittytone 6 | * @copyright 2025 7 | * @licence MIT 8 | * 9 | */ 10 | 11 | // C 12 | #include 13 | #include 14 | #include 15 | #include 16 | // Pico 17 | #include "pico/stdlib.h" 18 | #include "hardware/flash.h" 19 | #include "hardware/sync.h" 20 | // App 21 | #include "ops.h" 22 | #include "cpu.h" 23 | #include "cpu_tests.h" 24 | #include "monitor.h" 25 | #include "pia.h" 26 | #include "main.h" 27 | 28 | 29 | /* 30 | * STATICS 31 | */ 32 | static void boot_cpu(void); 33 | static void init_rp2040_gpio(void); 34 | static void prepare_environment(void); 35 | // EXPERIMENTAL 36 | static void read_into_ram(void); 37 | static void save_ram(void); 38 | 39 | 40 | /* 41 | * GLOBALS 42 | */ 43 | STATE_RP2040 pico_state; 44 | 45 | extern REG_6809 reg; 46 | extern STATE_6809 state; 47 | extern uint8_t mem[KB64]; 48 | 49 | MC6821 pia01; 50 | 51 | 52 | /* 53 | * ENTRY POINT 54 | */ 55 | int main() { 56 | // Enable STDIO 57 | stdio_usb_init(); 58 | #ifdef DEBUG 59 | // Pause to allow the USB path to initialize 60 | sleep_ms(2000); 61 | #endif 62 | 63 | // Basic RP2040 config 64 | pico_state.has_led = true; 65 | pico_state.has_mc6821 = false; 66 | 67 | // Prepare the board 68 | bool is_using_monitor = init_board(); 69 | 70 | // Set up the host MCU 71 | init_rp2040_gpio(); 72 | 73 | // Boot the CPU 74 | boot_cpu(); 75 | 76 | // Boot the PIA 77 | if (pico_state.has_mc6821) { 78 | pia01.pa_pins = &pico_state.pia_gpio[0]; 79 | pia01.ca_pins = &pico_state.pia_gpio[8]; 80 | pia01.reg_control_a = &mem[0xFF00]; 81 | pia01.reg_data_a = &mem[0xFF01]; 82 | pia_init(&pia01); 83 | } 84 | 85 | // Branch according to whether the Pico is connected to a 86 | // monitor board or not (in which case run tests for now) 87 | if (is_using_monitor) { 88 | // Enter the monitor UI 89 | #ifdef DEBUG 90 | printf("Using monitor board\n"); 91 | #endif 92 | monitor_event_loop(); 93 | } else { 94 | // Follow standard boot sequence 95 | #ifdef DEBUG 96 | printf("Booted\n"); 97 | #endif 98 | prepare_environment(); 99 | } 100 | 101 | return 0; 102 | } 103 | 104 | 105 | /** 106 | * @brief Bring up the virtual 6809e and 64KB of memory. 107 | * In future, this will offer alternative memory layouts. 108 | */ 109 | static void boot_cpu(void) { 110 | 111 | #ifdef DEBUG 112 | printf("Resetting the registers\n"); 113 | #endif 114 | 115 | // Interrupt Vectors: 116 | uint16_t vectors[] = {0x8000, // RESET 117 | 0x0500, // NMI 118 | 0x0500, // SWI 119 | 0x0500, // IRQ 120 | 0x0500, // FIRQ 121 | 0x0500, // SWI2 122 | 0x0500, // SWI3 123 | 0x0000}; // Reserved 124 | init_vectors(vectors); 125 | init_cpu(); 126 | 127 | #ifdef DEBUG 128 | printf("Initializing memory\n"); 129 | #endif 130 | // Clear the RAM below the IRQ vector table 131 | memset(mem, 0x00, START_VECTORS); 132 | 133 | #ifdef DEBUG 134 | printf("Entering sample program at 0x4000\n"); 135 | 136 | uint16_t start = 0x4000; 137 | uint8_t prog[] = {0x86,0xFF,0x8E,0x80,0x00,0xA7,0x80,0x8C,0x80,0x09,0x2D,0xF9,0x8E,0x40,0x00,0x6E,0x84}; 138 | memcpy(&mem[start], prog, sizeof(prog)); 139 | 140 | reg.pc = start; 141 | reg.cc = 0x6B; // WHY THIS SETTING? 142 | 143 | /* 144 | * TEST PROGS 145 | */ 146 | 147 | /* 148 | {0x34,0x46,0x33,0x64,0xA6,0x42,0xAE,0x43,0xE6,0x80,0x34,0x04,0x34,0x04,0x4A,0x27,0x13,0xE6,0x80,0xE1,0xE4,0x2E,0x08,0xE1,0x61,0x2E,0x06,0xE7,0x61,0x20,0x02,0xE7,0xE4,0x4A,0x26,0xED,0xA6,0xE0,0xA7,0x45,0xA6,0xE0,0xA7,0x46,0x35,0xC6,0x32,0x7E,0x8E,0x80,0x42,0x34,0x10,0xB6,0x80,0x41,0x34,0x02,0x8D,0xC4,0xA6,0x63,0xE6,0x64,0x39,0x0A,0x01,0x02,0x03,0x04,0x00,0x06,0x07,0x09,0x08,0x0B}; 149 | {0x86, 0x41, 0x8E, 0x04, 0x00, 0xA7, 0x80, 0x8C, 0x06, 0x00, 0x26, 0xF9, 0x1A, 0x0F, 0x3B}; 150 | */ 151 | #endif 152 | } 153 | 154 | 155 | /** 156 | * @brief Configure the RP2040’s GPIO pins. 157 | * 158 | * @note Assumes the use of the Pico board. 159 | */ 160 | static void init_rp2040_gpio(void) { 161 | 162 | #ifdef DEBUG 163 | printf("Setting RP2040 GPIO pins\n"); 164 | #endif 165 | // Configuration 166 | pico_state.irq_gpio[0] = PIN_6809_NMI; 167 | pico_state.irq_gpio[1] = PIN_6809_IRQ; 168 | pico_state.irq_gpio[2] = PIN_6809_FIRQ; 169 | pico_state.irq_gpio[3] = PIN_6809_RESET; 170 | 171 | pico_state.pia_gpio[0] = PIN_6821_PA0; 172 | pico_state.pia_gpio[1] = PIN_6821_PA1, 173 | pico_state.pia_gpio[2] = PIN_6821_PA2, 174 | pico_state.pia_gpio[3] = PIN_6821_PA3, 175 | pico_state.pia_gpio[4] = PIN_6821_PA4, 176 | pico_state.pia_gpio[5] = PIN_6821_PA5, 177 | pico_state.pia_gpio[6] = PIN_6821_PA6, 178 | pico_state.pia_gpio[7] = PIN_6821_PA7, 179 | pico_state.pia_gpio[8] = PIN_6821_CA1, 180 | pico_state.pia_gpio[9] = PIN_6821_CA2; 181 | 182 | // Set up the IRQ pins 183 | for (uint8_t i = 0 ; i < RP2040_IRQ_GPIO_COUNT ; ++i) { 184 | gpio_init(pico_state.irq_gpio[i]); 185 | gpio_set_dir(pico_state.irq_gpio[i], GPIO_IN); 186 | gpio_pull_down(pico_state.irq_gpio[i]); 187 | } 188 | 189 | // Initialize PIA pins if PIA is present 190 | // TODO Sync with pia.c 191 | if (pico_state.has_mc6821) { 192 | for (uint8_t i = 0 ; i < RP2040_PIA_GPIO_COUNT ; ++i) { 193 | // On RESET, set PA0-7, CA1, CA2 to inputs 194 | // See MC6821 Data Sheet p6 195 | gpio_init(pico_state.pia_gpio[i]); 196 | gpio_set_dir(pico_state.pia_gpio[i], GPIO_IN); 197 | gpio_pull_down(pico_state.pia_gpio[i]); 198 | } 199 | } 200 | 201 | // Set up the Pico LED 202 | gpio_init(PIN_PICO_LED); 203 | gpio_set_dir(PIN_PICO_LED, GPIO_OUT); 204 | gpio_put(PIN_PICO_LED, false); 205 | } 206 | 207 | 208 | /** 209 | * @brief Sample the interrupt pins and return a bitfield. 210 | * This will be called by the CPU code, initially on a per-cycle 211 | * basis but later as true interrupts. 212 | */ 213 | uint8_t sample_interrupts(void) { 214 | 215 | uint8_t irqs = 0; 216 | for (uint8_t i = 0 ; i < 3 ; ++i) { 217 | if (gpio_get(pico_state.irq_gpio[i])) irqs |= (1 << i); 218 | } 219 | return irqs; 220 | } 221 | 222 | 223 | /** 224 | * @brief Flash the Pico LED. 225 | * 226 | * @param count: The number of blinks in the sequence. 227 | */ 228 | void flash_led(uint8_t count) { 229 | 230 | if (pico_state.has_led) { 231 | while (count > 0) { 232 | gpio_put(PIN_PICO_LED, true); 233 | sleep_ms(250); 234 | gpio_put(PIN_PICO_LED, false); 235 | sleep_ms(250); 236 | count--; 237 | } 238 | } 239 | } 240 | 241 | 242 | /* 243 | * EXPERIMENTAL 244 | */ 245 | static void read_into_ram(void) { 246 | 247 | // See https://kevinboone.me/picoflash.html?i=1 248 | // 2MB Flash = 2,097,152 249 | // Allow 1MB for app code, so start at 250 | // XIP_BASE + 1,048,576 251 | // RAM SIZE = 64KB = 65,536 252 | char *p = (char *)XIP_BASE; 253 | p += 1048576; 254 | 255 | // Read 64KB from Flash into RAM 256 | for (uint16_t i = 0 ; i < 65536 ; ++i) { 257 | mem[i] = (uint8_t)(*p); 258 | } 259 | } 260 | 261 | 262 | static void save_ram(void) { 263 | 264 | // See https://kevinboone.me/picoflash.html?i=1 265 | uint32_t irqs = save_and_disable_interrupts(); 266 | flash_range_erase (RP2040_FLASH_DATA_START, RP2040_FLASH_DATA_SIZE); 267 | flash_range_program (RP2040_FLASH_DATA_START, mem, RP2040_FLASH_DATA_SIZE); 268 | restore_interrupts (irqs); 269 | } 270 | 271 | 272 | static void prepare_environment(void) { 273 | 274 | } 275 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # e6809 0.0.2 2 | 3 | An emulation of the Motorola 6809e 8-bit microprocessor designed to run on the RP2040 microcontroller board, such as the Raspberry Pi Pico. 4 | 5 | It is intended for standalone usage — though not as a drop in replacement for an original 6808e — and within a microprocessor kit design called the Monitor Board. 6 | 7 | There are two versions: 8 | 9 | 1. Raspberry Pi Pico - [branch `main`](https://github.com/smittytone/e6809/tree/main) 10 | 1. Mac — [branch `mac`](https://github.com/smittytone/e6809/tree/mac) 11 | 12 | Current development activity is centred on the `main` branch. The `pico` branch will soon be removed. The `mac` branch is out of data and will be removed. 13 | 14 | **This is a work in progress which I tackle occasionally. Expect errors, breakage and a slow response to bug reports!** 15 | 16 | ## 6809 Merch 17 | 18 | [![6809 T-shirt. Shirt, logo, photo (c) 2025 Tony Smith](./images/6809.webp)](https://www.redbubble.com/i/t-shirt/6809-microprocessor-by-squinter-mac/85380308.IJ6L0) 19 | 20 | Show your support: [buy a 6089 T-shirt here](https://www.redbubble.com/i/t-shirt/6809-microprocessor-by-squinter-mac/85380308.IJ6L0)! 21 | 22 | And for Dragon fans in particular: 23 | 24 | [![Dragon Data Logo T-shirt. photo (c) 2025 Tony Smith](./images/dd1.webp)](https://www.redbubble.com/i/t-shirt/Dragon-Data-Logo-by-squinter-mac/83509782.IJ6L0) [![Dragon Data Logo T-shirt. photo (c) 2025 Tony Smith](./images/dd2.webp)](https://www.redbubble.com/i/t-shirt/Dragon-Data-Logo-Slim-by-squinter-mac/83509973.IJ6L0) [![Dragon Data boot T-shirt. photo (c) 2025 Tony Smith](./images/dd3.webp)](https://www.redbubble.com/i/t-shirt/Dragon-Boot-Message-Reverse-Video-by-squinter-mac/46847689.IJ6L0) [![Dragon Data boot T-shirt. photo (c) 2025 Tony Smith](./images/dd4.webp)](https://www.redbubble.com/i/t-shirt/Dragon-Boot-Message-by-squinter-mac/46847619.1482B) 25 | 26 | ## The CPU 27 | 28 | The CPU is booted and run on power up. The code checks for the presence of the [Monitor Board](#the-monitor-board) and will switch to that if it is available. If not, it currently runs the CPU test suite, but this will change as I near ‘release’. 29 | 30 | ### Interrupts 31 | 32 | The 6809e’s `NMI`, `IRQ` and `FIRQ` interrupts are broken out to the RP2040’s GPIO pins 22, 20 and 21, respectively. Other control pins, such as `HALT` and `RESET`, will be added. If the pin reads a HIGH signal, the interrupt is triggered. 33 | 34 | ## The Monitor Board 35 | 36 | The Monitor Board is based on [Pimoroni’s RGB Keyboard Base](https://shop.pimoroni.com/products/pico-rgb-keypad-base) add-on for the Raspberry Pi Pico. It also uses a custom display board based on two HT16K33-driven four-digit, seven-segment LED displays. 37 | 38 | For the display board mount the two [Adafruit 0.56in 4-digit 7-segment](https://www.adafruit.com/product/880) side by side on a breadboard. Make sure the two displays have different I²C addresses [as described here](https://learn.adafruit.com/adafruit-led-backpack/0-dot-56-seven-segment-backpack-assembly). Connect each of the two displays’ SDA, SCL, GND and 3V3 pins to four common connection points, and these four to the Keyboard Base’s 3V3, GND, GPIO 4 (SDA) and GPIO 5 (SCL) headers. 39 | 40 | The board typically shows an 8-bit value (left four digits) and a 16-bit address (right four digits) both in hexadecimal. 41 | 42 | ![Monitor main menu](./images/display-board.webp) 43 | 44 | The Monitor code is menu driven. It provides code entry, program execution and memory inspection functions. 45 | 46 | ### Main Menu 47 | 48 | ![Monitor main menu](./images/monitor_main.png) 49 | 50 | * `0` — Step through memory, down. 51 | * `3` — Step through memory, up. 52 | * `B` — Load code via USB. 53 | * `C` — Run code. 54 | * `D` — Run code in single-step mode. 55 | * `E` — Enter a byte at the current address. 56 | * `F` — Enter the current address. 57 | 58 | When you run code, whether without breaks or in single-step mode, the Program Counter is set to the current address. This can be set by entering it directly (hit the yellow key) or by stepping to the required address using the upper blue keys. 59 | 60 | ### Confirm Menu 61 | 62 | ![Monitor confirm menu](./images/monitor_confirm_data.png) 63 | 64 | * `C` — Exit to main menu. 65 | * `E` — Accept the current byte and continue in data-entry mode. 66 | * `E` — Flip between address/value and register views. 67 | * `F` — Accept the current byte or address and return to previous menu. 68 | 69 | The orange button (shown above) is only illuminated when you have entered a byte value. Tap it to store the byte and continue in byte-entry mode, or hit green to store the byte and return to the main menu. Hitting red ignores the entered byte value. 70 | 71 | The magenta button (not shown above; also key `E`) is only illuminated when you have paused running code. Tap it to change the display mode (see next section). 72 | 73 | ### Single-step Menu 74 | 75 | ![Monitor code-step menu](./images/monitor_step.png) 76 | 77 | * `0` — Step through memory, down. 78 | * `3` — Step through memory, up. 79 | * `D` — Reset display to current address. 80 | * `E` — MOve between address/value and register views. 81 | * `F` — Process instruction at current address. 82 | 83 | Using the memory step keys can take you away from the address referenced by the 6809e’s Program Counter register. Hit the orange button to align the display with the PC. 84 | 85 | Pressing the magenta button cycles the display through the following outputs: 86 | 87 | * Current address (PC register) and that memory location’s contents. 88 | * The Condition Code register bits (E, F, H, I, N, Z, V and C) as a binary value. 89 | * The A, B and Direct Page registers in that order. 90 | * The X and Y registers. 91 | * The S and U registers. 92 | 93 | ### The Run Menu 94 | 95 | When you are running code without automatically pausing between instructions, the keypad will glow white. Tap any key to halt the code. The keys will cease to glow and the [Confirm Menu](#confirm-menu) will be shown. If the keys cease to glow without a key press, then the code has returned. 96 | 97 | ### Loading Code 98 | 99 | Use the `loader.py` utility in `/scripts` to send binary program data in the form of `.rom` files to the monitor. To upload a file: 100 | 101 | 1. Press `B` (magenta) at the main menu to prepare the board for loading. 102 | 2. On your computer, run `python loader.py -s -d `. 103 | 104 | `-s` specifies the 16-bit address at which the program will be stored. `-d` specifies the monitor board’s device file under macOS or Linux. For example: 105 | 106 | ```shell 107 | python loader.py -s 0x8000 -d /dev/cu.usbmodem1414301 d32.rom 108 | ``` 109 | 110 | The Pico LED will flash five times to signal a load error, if one occurred. There is a 30s timeout after which the loading will stop and the main menu will be accessible again. 111 | 112 | You can use [Spasm](https://github.com/smittytone/Spasm) to generate assembled `.rom` files: 113 | 114 | ```shell 115 | spasm.py -o test.rom test.asm 116 | ``` 117 | 118 | ## RP2040 Pinout (Provisional!) 119 | 120 | ``` 121 | NMI• 0 - -------- - VBUS 122 | IRQ• 1 - | | - VSYS 123 | G - | | - G 124 | FIRQ• 2 - | | - 3V3EN 125 | RESET• 3 - | R | - 3V3 3V3* 126 | SDA* 4 - | P | - 127 | SCL* 5 - | 2 | - 28 128 | G - | 0 | - G 129 | PA0† 6 - | 4 | - 27 130 | PA1† 7 - | 0 | - 26 131 | PA2† 8 - | | - RUN 132 | PA3† 9 - | | - 22 133 | G - | | - G 134 | PA4† 10 - | | - 21 135 | PA5† 11 - | | - 20 136 | PA6† 12 - | | - 19 TX* 137 | PA7† 13 - | | - 18 SCK* 138 | G - | | - G 139 | CA1† 14 - | | - 17 CS* 140 | CA2† 15 - -------- - 16 141 | ``` 142 | 143 | * Keypad Monitor board
144 | • 6809E
145 | † MC6821 PIA 146 | 147 | **SUBJECT TO CHANGE** 148 | 149 | ## To Do 150 | 151 | In no particular order... 152 | 153 | * Add Motorola PIA chip support. 154 | * Add 6809e start-up sequence when Monitor Board not present. 155 | * Clock-precise (1MHz) processing. 156 | * Support alternative memory maps, not just a flat 64KB space. 157 | * Support 64KB memory pages. 158 | * Add downloading of RAM contents via USB. 159 | 160 | ## Copyright 161 | 162 | Emulation software copyright © 2025 Tony Smith (@smittytone) 163 | 164 | 6809e ISA copyright © 1977 Motorola — and, by sequential ownership, NXP Semiconductor. 165 | -------------------------------------------------------------------------------- /scripts/loader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ''' 4 | Loader -- code loader for e6809 on Pico 5 | 6 | Version: 7 | 1.0.0 8 | 9 | Copyright: 10 | 2021, Tony Smith (@smittytone) 11 | 12 | License: 13 | MIT (terms attached to this repo) 14 | ''' 15 | 16 | ''' 17 | IMPORTS 18 | ''' 19 | from os import path 20 | from sys import exit, argv 21 | from time import time_ns 22 | 23 | 24 | ''' 25 | GLOBALS 26 | ''' 27 | verbose = True 28 | 29 | 30 | ''' 31 | FUNCTIONS 32 | ''' 33 | 34 | ''' 35 | Load a .rom file into memory. 36 | 37 | Args: 38 | file (String): The .rom's path and filename. 39 | 40 | Returns: 41 | Bytearray: The loaded byte data. 42 | ''' 43 | def get_file(file): 44 | ba = bytearray() 45 | with open(file, "rb") as f: 46 | while (b := f.read(1)): 47 | ba += b 48 | return ba 49 | 50 | 51 | ''' 52 | Trigger a check for an ACK from the Monitor Board, or 53 | close the UART and exit. 54 | 55 | Args: 56 | uart (Serial): The chosen serial port. 57 | ''' 58 | def await_ack_or_exit(uart): 59 | r = await_ack(uart) 60 | if r == False: 61 | uart.close() 62 | print("[ERROR] No ACK") 63 | exit(1) 64 | 65 | 66 | ''' 67 | Sample the UART for an ACK response. 68 | 69 | Args: 70 | uart (Serial): The chosen serial port. 71 | timeout (Int): The sampling timeout. Default: 2s. 72 | 73 | Returns: 74 | int: True if the last block as ACK’d, False otherwise. 75 | ''' 76 | def await_ack(uart, timeout=2000): 77 | buffer = bytes() 78 | now = (time_ns() // 1000000) 79 | while ((time_ns() // 1000000) - now) < timeout: 80 | if uart.in_waiting > 0: 81 | buffer += uart.read(uart.in_waiting) 82 | if "\n" in buffer.decode(): 83 | show_verbose("RX: " + buffer[:-1].decode()) 84 | return True 85 | # Error -- No Ack received 86 | return False 87 | 88 | 89 | ''' 90 | Send a single address block. This is the code's 91 | 16-bit entry point. 92 | 93 | Args: 94 | uart (Serial): The chosen serial port. 95 | address (Int): The 16-bit address. 96 | ''' 97 | def send_addr_block(uart, address): 98 | out = bytearray(8) 99 | out[0] = 0x55 # Head 100 | out[1] = 0x3C # Sync 101 | out[2] = 0x00 # Block Type 102 | out[3] = 2 # Data length 103 | out[4] = (address >> 8) & 0xFF 104 | out[5] = address & 0xFF 105 | 106 | # Compute checksum 107 | cs = 0 108 | for i in range (2, 6): 109 | cs += out[i] 110 | 111 | out[6] = cs & 0xFF # Checksum 112 | out[7] = 0x55 # Trailer 113 | r = uart.write(out) 114 | 115 | 116 | ''' 117 | Send a single data block. 118 | 119 | Args: 120 | uart (Serial): The chosen serial port. 121 | bytes (Bytearray): The data store. 122 | counter (Int): The index of the first byte to send. 123 | 124 | Returns: 125 | Int: The updated byte index. 126 | ''' 127 | def send_data_block(uart, bytes, counter): 128 | length = len(bytes) - counter 129 | if length > 255: length = 255 130 | out = bytearray(length + 6) 131 | out[0] = 0x55 # Head 132 | out[1] = 0x3C # Sync 133 | out[2] = 0x01 # Block Type 134 | out[3] = length # Data length 135 | 136 | # Set the data 137 | for i in range(0, length): 138 | out[i + 4] = bytes[counter + i] 139 | 140 | # Compute checksum 141 | cs = 0 142 | for i in range (2, length + 6 - 2): 143 | cs += out[i] 144 | cs &= 0xFF 145 | out[length + 6 - 2] = cs # Checksum 146 | out[length + 6 - 1] = 0x55 # Trailer 147 | counter += length 148 | r = uart.write(out) 149 | return counter 150 | 151 | 152 | ''' 153 | Send a single end-of-file block. 154 | 155 | Args: 156 | uart (Serial): The chosen serial port. 157 | ''' 158 | def send_trailer_block(uart): 159 | out = b'\x55\x3C\xFF\x00\x00\x55' 160 | r = uart.write(out) 161 | 162 | 163 | ''' 164 | Display a message if verbose mode is enabled. 165 | 166 | Args: 167 | messsage (str): The text to print. 168 | ''' 169 | def show_verbose(message): 170 | if verbose is True: print(message) 171 | 172 | 173 | ''' 174 | Pass on all supplied '.asm' files on for assembly, '.6809' or '.rom' files for disassembly. 175 | 176 | Args: 177 | the_files (list): The .asm, .rom or .6809 files. 178 | 179 | Returns: 180 | int: The numerical value 181 | ''' 182 | def str_to_int(num_str): 183 | num_base = 10 184 | if num_str[0] == "$": num_str = "0x" + num_str[1:] 185 | if num_str[:2] == "0x": num_base = 16 186 | try: 187 | return int(num_str, num_base) 188 | except ValueError: 189 | return False 190 | 191 | 192 | ''' 193 | Show the utility help 194 | ''' 195 | def show_help(): 196 | show_version() 197 | print("\nTransfer binary data to the 6809e Monitor Board.\n") 198 | print("Usage:\n\n loader.py [-s] [-d] [-q] [-h] \n") 199 | print("Options:\n") 200 | print(" -s / --start Code 16-bit start address. Default: 0x0000.") 201 | print(" -d / --device The Monitor Board USB-serial device file.") 202 | print(" -q / --quiet Quiet output -- no messages other than errors.") 203 | print(" -h / --help This help information.") 204 | print() 205 | 206 | 207 | ''' 208 | Show the utility version info 209 | ''' 210 | def show_version(): 211 | print("Loader 1.0.0 copyright (c) 2021 Tony Smith (@smittytone)") 212 | 213 | 214 | ''' 215 | RUNTIME START 216 | ''' 217 | if __name__ == '__main__': 218 | 219 | arg_flag = False 220 | start_address = 0x0000 221 | rom_file = None 222 | device = None 223 | 224 | if len(argv) > 1: 225 | for index, item in enumerate(argv): 226 | file_ext = path.splitext(item)[1] 227 | if arg_flag is True: 228 | arg_flag = False 229 | elif item in ("-h", "--help"): 230 | show_help() 231 | exit(0) 232 | elif item in ("-q", "--quiet"): 233 | verbose = False 234 | elif item in ("-s", "--startaddress"): 235 | if index + 1 >= len(argv): 236 | print("[ERROR] -s / --startaddress must be followed by an address") 237 | exit(1) 238 | an_address = str_to_int(argv[index + 1]) 239 | if an_address is False or an_address < 0 or an_address > 0xFFFF: 240 | print("[ERROR] -s / --startaddress must be followed by a valid address") 241 | exit(1) 242 | start_address = an_address 243 | arg_flag = True 244 | elif item in ("-d", "--device"): 245 | if index + 1 >= len(argv): 246 | print("[ERROR] -d / --device must be followed by a device file") 247 | exit(1) 248 | device = argv[index + 1] 249 | arg_flag = True 250 | else: 251 | if item[0] == "-": 252 | print("[ERROR] unknown option: " + item) 253 | exit(1) 254 | elif index != 0 and arg_flag is False: 255 | # Handle any included .rom files 256 | if not path.exists(item): 257 | print("[ERROR] File " + item + " does not exist") 258 | exit(1) 259 | _, arg_file_ext = path.splitext(item) 260 | if arg_file_ext in (".rom"): 261 | rom_file = item 262 | else: 263 | print("[ERROR] File " + item + " is not a .rom file") 264 | 265 | if rom_file is None: 266 | print("[ERROR] No .rom file specified") 267 | exit(1) 268 | 269 | if device is None: 270 | print("[ERROR] No e6809 device file specified") 271 | exit(1) 272 | 273 | # Set the port or fail 274 | port = None 275 | try: 276 | import serial 277 | port = serial.Serial(port=device, baudrate=115200) 278 | except: 279 | print("[ERROR] An invalid e6809 device file was specified:",device) 280 | exit(1) 281 | 282 | # Load the data 283 | data_bytes = get_file(rom_file) 284 | length = len(data_bytes) 285 | show_verbose("Code start adddress set to 0x{:04X}".format(start_address)) 286 | show_verbose(str(length) + " bytes to send") 287 | 288 | # Send the header 289 | send_addr_block(port, start_address) 290 | await_ack_or_exit(port) 291 | 292 | # Send the data out block by block 293 | c = 0 294 | while True: 295 | c = send_data_block(port, data_bytes, c) 296 | await_ack_or_exit(port) 297 | if length - c <= 0: break 298 | 299 | # Send the trailer 300 | send_trailer_block(port) 301 | await_ack_or_exit(port) 302 | 303 | # Close the port 304 | port.close() 305 | -------------------------------------------------------------------------------- /source/ops.h: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * Opcode constant definitions 4 | * 5 | * @version 0.0.2 6 | * @author smittytone 7 | * @copyright 2025 8 | * @licence MIT 9 | * 10 | */ 11 | #ifndef _OPS_HEADER_ 12 | #define _OPS_HEADER_ 13 | 14 | // Opcodes 15 | 16 | #define NEG_direct 0x00 // 17 | #define COM_direct 0x03 // 18 | #define LSR_direct 0x04 // 19 | #define ROR_direct 0x06 // 20 | #define ASR_direct 0x07 // 21 | #define ASL_direct 0x08 // 22 | #define ROL_direct 0x09 // 23 | #define DEC_direct 0x0A // 24 | #define INC_direct 0x0C // 25 | #define TST_direct 0x0D // 26 | 27 | #define JMP_direct 0x0E // UNTESTED 28 | #define CLR_direct 0x0F // 29 | 30 | #define NOP 0x12 // 31 | #define SYNC 0x13 32 | #define LBRA 0x16 // 33 | #define LBSR 0x17 // 34 | #define DAA 0x19 // BUG 35 | #define ORCC_immed 0x1A // 36 | #define ANDCC_immed 0x1C // 37 | #define SEX 0x1D // UNTESTED 38 | #define EXG_immed 0x1E // 39 | #define TFR_immed 0x1F // 40 | 41 | #define BRA 0x20 42 | #define BRN 0x21 43 | #define BHI 0x22 44 | #define BLS 0x23 45 | #define BCC 0x24 46 | #define BHS 0x24 47 | #define BCS 0x25 48 | #define BLO 0x25 49 | #define BNE 0x26 50 | #define BEQ 0x27 51 | #define BVC 0x28 52 | #define BVS 0x29 53 | #define BPL 0x2A 54 | #define BMI 0x2B 55 | #define BGE 0x2C 56 | #define BLT 0x2D 57 | #define BGT 0x2E 58 | #define BLE 0x2F 59 | 60 | #define LEAX_indexed 0x30 // 61 | #define LEAY_indexed 0x31 // 62 | #define LEAS_indexed 0x32 // 63 | #define LEAU_indexed 0x33 // 64 | 65 | #define PSHS_immed 0x34 // 66 | #define PULS_immed 0x35 // 67 | #define PSHU_immed 0x36 // 68 | #define PULU_immed 0x37 // 69 | 70 | #define RTS 0x39 // 71 | #define ABX 0x3A // 72 | #define RTI 0x3B // 73 | #define CWAI_immed 0x3C // 74 | #define MUL 0x3D // 75 | #define SWI 0x3F // UNTESTED 76 | 77 | #define NEGA 0x40 // Note: Immediate addressing 78 | #define COMA 0x43 // 79 | #define LSRA 0x44 // 80 | #define RORA 0x46 // 81 | #define ASRA 0x47 // 82 | #define ASLA 0x48 // 83 | #define ROLA 0x49 // 84 | #define DECA 0x4A // 85 | #define INCA 0x4C // 86 | #define TSTA 0x4D // 87 | #define CLRA 0x4F // 88 | 89 | #define NEGB 0x50 // Note: Immediate addressing 90 | #define COMB 0x53 // 91 | #define LSRB 0x54 // 92 | #define RORB 0x56 // 93 | #define ASRB 0x57 // 94 | #define ASLB 0x58 // 95 | #define ROLB 0x59 // 96 | #define DECB 0x5A // 97 | #define INCB 0x5C // 98 | #define TSTB 0x5D // 99 | #define CLRB 0x5F // 100 | 101 | #define NEG_indexed 0x60 // 102 | #define COM_indexed 0x63 // 103 | #define LSR_indexed 0x64 // 104 | #define ROR_indexed 0x66 // 105 | #define ASR_indexed 0x67 // 106 | #define ASL_indexed 0x68 // 107 | #define ROL_indexed 0x69 // 108 | #define DEC_indexed 0x6A // 109 | #define INC_indexed 0x6C // 110 | #define TST_indexed 0x6D // 111 | #define JMP_indexed 0x6E // 112 | #define CLR_indexed 0x6F // 113 | 114 | #define NEG_extended 0x70 115 | #define COM_extended 0x73 116 | #define LSR_extended 0x74 117 | #define ROR_extended 0x76 118 | #define ASR_extended 0x77 119 | #define ASL_extended 0x78 120 | #define ROL_extended 0x79 121 | #define DEC_extended 0x7A 122 | #define INC_extended 0x7C 123 | #define TST_extended 0x7D 124 | #define JMP_extended 0x7E 125 | #define CLR_extended 0x7F 126 | 127 | #define SUBA_immed 0x80 128 | #define CMPA_immed 0x81 129 | #define SBCA_immed 0x82 130 | #define SUBD_immed 0x83 131 | #define ANDA_immed 0x84 132 | #define BITA_immed 0x85 133 | #define LDA_immed 0x86 134 | #define EORA_immed 0x88 135 | #define ADCA_immed 0x89 136 | #define ORA_immed 0x8A 137 | #define ADDA_immed 0x8B 138 | #define CMPX_immed 0x8C 139 | #define BSR 0x8D 140 | #define LDX_immed 0x8E 141 | 142 | #define SUBA_direct 0x90 143 | #define CMPA_direct 0x91 144 | #define SBCA_direct 0x92 145 | #define SUBD_direct 0x93 146 | #define ANDA_direct 0x94 147 | #define BITA_direct 0x95 148 | #define LDA_direct 0x96 149 | #define STA_direct 0x97 150 | #define EORA_direct 0x98 151 | #define ADCA_direct 0x99 152 | #define ORA_direct 0x9A 153 | #define ADDA_direct 0x9B 154 | #define CMPX_direct 0x9C 155 | #define JSR_direct 0x9D 156 | #define LDX_direct 0x9E 157 | #define STX_direct 0x9F 158 | 159 | #define SUBA_indexed 0xA0 160 | #define CMPA_indexed 0xA1 161 | #define SBCA_indexed 0xA2 162 | #define SUBD_indexed 0xA3 163 | #define ANDA_indexed 0xA4 164 | #define BITA_indexed 0xA5 165 | #define LDA_indexed 0xA6 166 | #define STA_indexed 0xA7 167 | #define EORA_indexed 0xA8 168 | #define ADCA_indexed 0xA9 169 | #define ORA_indexed 0xAA 170 | #define ADDA_indexed 0xAB 171 | #define CMPX_indexed 0xAC 172 | #define JSR_indexed 0xAD 173 | #define LDX_indexed 0xAE 174 | #define STX_indexed 0xAF 175 | 176 | #define SUBA_extended 0xB0 177 | #define CMPA_extended 0xB1 178 | #define SBCA_extended 0xB2 179 | #define SUBD_extended 0xB3 180 | #define ANDA_extended 0xB4 181 | #define BITA_extended 0xB5 182 | #define LDA_extended 0xB6 183 | #define STA_extended 0xB7 184 | #define EORA_extended 0xB8 185 | #define ADCA_extended 0xB9 186 | #define ORA_extended 0xBA 187 | #define ADDA_extended 0xBB 188 | #define CMPX_extended 0xBC 189 | #define JSR_extended 0xBD 190 | #define LDX_extended 0xBE 191 | #define STX_extended 0xBF 192 | 193 | #define SUBB_immed 0xC0 194 | #define CMPB_immed 0xC1 195 | #define SBCB_immed 0xC2 196 | #define ADDD_immed 0xC3 197 | #define ANDB_immed 0xC4 198 | #define BITB_immed 0xC5 199 | #define LDB_immed 0xC6 200 | #define EORB_immed 0xC8 201 | #define ADCB_immed 0xC9 202 | #define ORB_immed 0xCA 203 | #define ADDB_immed 0xCB 204 | #define LDD_immed 0xCC 205 | #define LDU_immed 0xCE 206 | 207 | #define SUBB_direct 0xD0 208 | #define CMPB_direct 0xD1 209 | #define SBCB_direct 0xD2 210 | #define ADDD_direct 0xD3 211 | #define ANDB_direct 0xD4 212 | #define BITB_direct 0xD5 213 | #define LDB_direct 0xD6 214 | #define STB_direct 0xD7 215 | #define EORB_direct 0xD8 216 | #define ADCB_direct 0xD9 217 | #define ORB_direct 0xDA 218 | #define ADDB_direct 0xDB 219 | #define LDD_direct 0xDC 220 | #define STD_direct 0xDD 221 | #define LDU_direct 0xDE 222 | #define STU_direct 0xDF 223 | 224 | #define SUBB_indexed 0xE0 225 | #define CMPB_indexed 0xE1 226 | #define SBCB_indexed 0xE2 227 | #define ADDD_indexed 0xE3 228 | #define ANDB_indexed 0xE4 229 | #define BITB_indexed 0xE5 230 | #define LDB_indexed 0xE6 231 | #define STB_indexed 0xE7 232 | #define EORB_indexed 0xE8 233 | #define ADCB_indexed 0xE9 234 | #define ORB_indexed 0xEA 235 | #define ADDB_indexed 0xEB 236 | #define LDD_indexed 0xEC 237 | #define STD_indexed 0xED 238 | #define LDU_indexed 0xEE 239 | #define STU_indexed 0xEF 240 | 241 | #define SUBB_extended 0xF0 242 | #define CMPB_extended 0xF1 243 | #define SBCB_extended 0xF2 244 | #define ADDD_extended 0xF3 245 | #define ANDB_extended 0xF4 246 | #define BITB_extended 0xF5 247 | #define LDB_extended 0xF6 248 | #define STB_extended 0xF7 249 | #define EORB_extended 0xF8 250 | #define ADCB_extended 0xF9 251 | #define ORB_extended 0xFA 252 | #define ADDB_extended 0xFB 253 | #define LDD_extended 0xFC 254 | #define STD_extended 0xFD 255 | #define LDU_extended 0xFE 256 | #define STU_extended 0xFF 257 | 258 | // Extended opcode start here 259 | 260 | #define LBRN 0x1021 // UNTESTED 261 | #define LBHI 0x1022 // UNTESTED 262 | #define LBLS 0x1023 // UNTESTED 263 | #define LBHS 0x1024 // UNTESTED 264 | #define LBLO 0x1025 // UNTESTED 265 | #define LBNE 0x1026 // UNTESTED 266 | #define LBEQ 0x1027 // UNTESTED 267 | #define LBVC 0x1028 // UNTESTED 268 | #define LBVS 0x1029 // UNTESTED 269 | #define LBPL 0x102A // UNTESTED 270 | #define LBMI 0x102B // UNTESTED 271 | #define LBGE 0x102C // UNTESTED 272 | #define LBLT 0x102D // UNTESTED 273 | #define LBGT 0x102E // UNTESTED 274 | #define LBLE 0x102F // UNTESTED 275 | 276 | #define SWI2 0x103F 277 | #define CMPD_immed 0x1083 278 | #define CMPY_immed 0x108C 279 | #define LDY_immed 0x108E 280 | #define CMPD_direct 0x1093 281 | #define CMPY_direct 0x109C 282 | #define LDY_direct 0x109E 283 | #define STY_direct 0x109F 284 | #define CMPD_indexed 0x10A3 285 | #define CMPY_indexed 0x10AC 286 | #define LDY_indexed 0x10AE 287 | #define STY_indexed 0x10AF 288 | #define CMPD_extended 0x10B3 289 | #define CMPY_extended 0x10BC 290 | #define LDY_extended 0x10BE 291 | #define STY_extended 0x10BF 292 | 293 | #define LDS_immed 0x10CE 294 | #define LDS_direct 0x10DE 295 | #define STS_direct 0x10DF 296 | #define LDS_indexed 0x10EE 297 | #define STS_indexed 0x10EF 298 | #define LDS_extended 0x10FE 299 | #define STS_extended 0x10FF 300 | 301 | // Second extended opcodes 302 | 303 | #define SWI3 0x113F 304 | #define CMPU_immed 0x1183 305 | #define CMPS_immed 0x118C 306 | #define CMPU_direct 0x1193 307 | #define CMPS_direct 0x119C 308 | #define CMPU_indexed 0x11A3 309 | #define CMPS_indexed 0x11AC 310 | #define CMPU_extended 0x11B3 311 | #define CMPS_extended 0x11BC 312 | 313 | #endif // _OPS_HEADER_ 314 | -------------------------------------------------------------------------------- /source/monitor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * Monitor code 4 | * 5 | * @version 0.0.2 6 | * @author smittytone 7 | * @copyright 2025 8 | * @licence MIT 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | // Pico 15 | #include "pico/stdlib.h" 16 | #include "hardware/gpio.h" 17 | // App 18 | #include "main.h" 19 | #include "cpu.h" 20 | #include "cpu_tests.h" 21 | #include "ht16k33.h" 22 | #include "keypad.h" 23 | #include "monitor.h" 24 | 25 | 26 | /* 27 | * STATICS 28 | */ 29 | static void process_key(uint16_t); 30 | static void set_keys(void); 31 | static uint8_t keypress_to_value(uint16_t input); 32 | static void update_display(void); 33 | static void display_cc(void); 34 | static void display_ab_dp(void); 35 | static void display_left(uint16_t value); 36 | static void display_right(uint16_t value); 37 | static void display_value(uint16_t value, uint8_t index, bool is_16_bit, bool show_colon); 38 | static bool load_code(void); 39 | static uint16_t get_block(uint8_t *buff); 40 | 41 | 42 | /* 43 | * GLOBALS 44 | */ 45 | uint8_t display_mode = 0; 46 | uint8_t input_count = 0; 47 | uint16_t input_value = 0; 48 | uint16_t input_mask = 0; 49 | uint16_t mode = 0; 50 | uint16_t previous_mode = 0; 51 | uint16_t current_address = 0x0000; 52 | uint16_t start_address = 0x0000; 53 | bool mode_changed = false; 54 | bool do_display_pc = true; 55 | bool is_running_steps = false; 56 | bool is_running_full = false; 57 | bool led_state = false; 58 | uint8_t buffer[32]; 59 | uint8_t *display_buffer[2] = {buffer, buffer + 16}; 60 | uint8_t display_address[2] = {0x71, 0x70}; 61 | 62 | extern REG_6809 reg; 63 | extern uint8_t mem[KB64]; 64 | extern STATE_6809 state; 65 | 66 | 67 | /** 68 | * @brief Bring up the monitor board if it is present. 69 | * 70 | * @retval `true` if the board is present and enabled, otherwise `false`. 71 | */ 72 | bool init_board(void) { 73 | 74 | #ifdef DEBUG 75 | printf("Configuring the monitor board\n"); 76 | #endif 77 | 78 | // Set up the keypad -- this sets up I2C0 @ 400,000bps 79 | if (!keypad_init()) { 80 | #ifdef DEBUG 81 | printf("No monitor board found\n"); 82 | #endif 83 | return false; 84 | } 85 | 86 | // Turn down the key LED glare 87 | keypad_set_brightness(0.2); 88 | 89 | // Set up the displays 90 | ht16k33_init(display_address[0], display_buffer[0]); 91 | ht16k33_init(display_address[1], display_buffer[1]); 92 | return true; 93 | } 94 | 95 | 96 | /** 97 | * @brief Initialise and run the main event loop. 98 | * 99 | * This primarily continually reads the keypad, allowing for 100 | * debounces on press and release actions. 101 | * Buttons are triggered only on release. 102 | */ 103 | void monitor_event_loop(void) { 104 | 105 | #ifdef DEBUG 106 | printf("Entering UI at main menu\n"); 107 | #endif 108 | 109 | bool is_key_pressed = false; 110 | bool can_key_release = false; 111 | 112 | uint32_t debounce_count_press = 0; 113 | uint32_t debounce_count_release = 0; 114 | uint32_t cpu_cycle_complete = 0; 115 | 116 | uint16_t the_key = 0; 117 | 118 | uint8_t input_buffer[10]; 119 | uint8_t* buffer_ptr = input_buffer; 120 | uint8_t buffer_index = 0; 121 | const char test_text[] = "HAIL"; 122 | 123 | // Set the button colours and the display 124 | set_keys(); 125 | update_display(); 126 | 127 | // Run the button press loop 128 | while (true) { 129 | // Read the keypad 130 | uint32_t now = time_us_32(); 131 | uint16_t any_key = keypad_get_button_states(); 132 | is_key_pressed = (any_key != 0); 133 | state.interrupts = sample_interrupts(); 134 | 135 | if (now - cpu_cycle_complete > 250000) { 136 | cpu_cycle_complete = now; 137 | led_state = !led_state; 138 | } 139 | 140 | if (is_running_full) { 141 | // Execute the next instruction 142 | gpio_put(PIN_PICO_LED, led_state); 143 | uint32_t result = process_next_instruction(); 144 | 145 | // Update the display 146 | update_display(); 147 | 148 | if (result == BREAK_TO_MONITOR) { 149 | // Code hit RTI -- show we're not running 150 | // NOTE A key press then will take the 151 | // user to the main menu 152 | mode = MENU_MODE_RUN_DONE; 153 | mode_changed = true; 154 | is_running_full = false; 155 | set_keys(); 156 | } 157 | } 158 | 159 | if (is_key_pressed) { 160 | // Allow a debounce period, eg. 20ms 161 | if (debounce_count_press == 0) { 162 | debounce_count_press = now; 163 | } else if (now - debounce_count_press > DEBOUNCE_TIME_US) { 164 | // Key still pressed -- start to check for its release 165 | debounce_count_press == 0; 166 | can_key_release = true; 167 | if (the_key == 0) the_key = any_key; 168 | } 169 | } else if (can_key_release) { 170 | // Allow a debounce period, eg. 20ms 171 | if (debounce_count_press == 0) { 172 | debounce_count_press = now; 173 | } else if (now - debounce_count_press > DEBOUNCE_TIME_US) { 174 | // Key released -- check and action it 175 | can_key_release = false; 176 | process_key(the_key); 177 | the_key = 0; 178 | } 179 | } else { 180 | // pause to allow room for interrupts 181 | // TODO 182 | } 183 | } 184 | } 185 | 186 | 187 | /** 188 | * @brief Process a key press to determine if it is valid - a lit button was pressed - 189 | * and to then trigger the action the key represents. 190 | * 191 | * @param input: The key press value read from the keypad. 192 | */ 193 | void process_key(uint16_t input) { 194 | 195 | // Make sure the key pressed was a valid one 196 | input &= input_mask; 197 | if (input != 0) { 198 | // Check the key's action according to the menu mode 199 | bool show_on_completion = false; 200 | switch (mode) { 201 | case MENU_MAIN_ADDR: 202 | case MENU_MAIN_BYTE: 203 | // 8- and 16-bit value input -- determined by 'input_count' 204 | input_value = (input_value << 4) | keypress_to_value(input); 205 | input_count -= 1; 206 | 207 | if (mode == MENU_MAIN_BYTE) { 208 | display_left(current_address); 209 | display_right(input_value); 210 | } else { 211 | display_left(input_value); 212 | } 213 | 214 | if (input_count == 0) { 215 | // Got 2 or 4 presses -- jump to confirm menu 216 | previous_mode = mode; 217 | mode = MENU_MODE_CONFIRM; 218 | mode_changed = true; 219 | } 220 | 221 | break; 222 | case MENU_MODE_STEP: 223 | // Single-step run menu 224 | // 0 -- Memory stop down -- BLUE 225 | // 3 -- Memory step up -- BLUE 226 | // C -- Run next instruction -- GREEN 227 | // D -- Toggle the display between address and CC register -- MAGENTA 228 | // E -- Track the PC register on the display -- ORANGE 229 | // F -- Exit to main menu -- RED 230 | if (input == INPUT_STEP_NEXT && is_running_steps) { 231 | // Run next instruction 232 | uint32_t result = process_next_instruction(); 233 | 234 | if (result == BREAK_TO_MONITOR) { 235 | // Code hit RTI -- jump back to the main menu 236 | mode = MENU_MODE_MAIN; 237 | mode_changed = true; 238 | is_running_steps = false; 239 | current_address = start_address; 240 | } else { 241 | if (do_display_pc) current_address = reg.pc; 242 | } 243 | 244 | if (result == 99) show_on_completion = true; 245 | } 246 | 247 | if (input == INPUT_STEP_SHOW_CC) { 248 | // Toggle the display between modes: 249 | // 0 - Address : value 250 | // 1 - CC register (binary) 251 | // 2 - DP : A, B (D) registers 252 | // 3 - X : Y registers 253 | // 4 - S : U registers 254 | display_mode++; 255 | if (display_mode > 4) display_mode = 0; 256 | } 257 | 258 | if (input == INPUT_STEP_SHOW_AD) { 259 | // Jump to current value of PC register 260 | do_display_pc = true; 261 | current_address = reg.pc; 262 | } 263 | 264 | if (input == INPUT_STEP_MEM_UP || input == INPUT_STEP_MEM_DOWN) { 265 | // Step up and down from the current memory value 266 | // NOTE This will 'fix' the memory displayed -- hit the Blue 267 | // key in the menu to continue tracking the PC register 268 | current_address += (input == INPUT_STEP_MEM_UP ? 1 : -1); 269 | do_display_pc = false; 270 | } 271 | 272 | if (input == INPUT_STEP_EXIT) { 273 | // User hit Cancel -- go back to the main menu 274 | mode = MENU_MODE_MAIN; 275 | mode_changed = true; 276 | is_running_steps = false; 277 | current_address = start_address; 278 | } 279 | 280 | update_display(); 281 | if (show_on_completion) { 282 | // If the code completed, add a long minus sign to the right display 283 | ht16k33_set_glyph(display_address[1], display_buffer[1], 0x40, 0, false); 284 | ht16k33_set_glyph(display_address[1], display_buffer[1], 0x40, 1, false); 285 | ht16k33_draw(display_address[1], display_buffer[1]); 286 | } 287 | break; 288 | case MENU_MODE_CONFIRM: 289 | // Confirm value entry menu 290 | // C -- Reject value and return to main menu -- RED 291 | // E -- Accept value and return to data entry -- ORANGE 292 | // E -- Switch display -- MAGENTA 293 | // F -- Accept value and return to main menu -- GREEN 294 | 295 | // There's always a mode change 296 | mode = MENU_MODE_MAIN; 297 | mode_changed = true; 298 | 299 | if (input == INPUT_CONF_OK) { 300 | // User hit OK -- results depend on which menu mode 301 | // they came from 302 | if (previous_mode == MENU_MAIN_ADDR) { 303 | current_address = input_value; 304 | } 305 | 306 | if (previous_mode == MENU_MAIN_BYTE) { 307 | mem[current_address] = input_value; 308 | } 309 | 310 | if (previous_mode == MENU_MODE_RUN) { 311 | // Continue running 312 | mode = previous_mode; 313 | is_running_full = true; 314 | } 315 | } 316 | 317 | if (input == INPUT_CONF_CONTINUE) { 318 | if (previous_mode == MENU_MODE_RUN) { 319 | display_mode++; 320 | if (display_mode > 4) display_mode = 0; 321 | mode = MENU_MODE_CONFIRM; 322 | } else { 323 | // Accept input and jump back to data-entry 324 | // NOTE Continue button only shown after byte entry 325 | mem[current_address] = input_value; 326 | current_address++; 327 | mode = previous_mode; 328 | mode_changed = false; 329 | } 330 | } 331 | 332 | if (input == INPUT_CONF_CANCEL && previous_mode == MENU_MODE_RUN) { 333 | led_state = false; 334 | gpio_put(PIN_PICO_LED, false); 335 | } 336 | 337 | // Show the current values 338 | update_display(); 339 | break; 340 | case MENU_MODE_RUN: 341 | // Key press during a run -- treat this as a pause 342 | // so show the Confirm Menu to continue or cancel 343 | previous_mode = mode; 344 | mode = MENU_MODE_CONFIRM; 345 | mode_changed = true; 346 | is_running_full = false; 347 | break; 348 | case MENU_MODE_RUN_DONE: 349 | // Key press after run returned, so go back to 350 | // the Main Menu 351 | mode = MENU_MODE_MAIN; 352 | mode_changed = true; 353 | is_running_full = false; 354 | current_address = start_address; 355 | break; 356 | default: 357 | // Main menu 358 | // 0 -- Memory stop down -- BLUE 359 | // 3 -- Memory step up -- BLUE 360 | // C -- Run code at current memory -- WHITE 361 | // D -- Run code at current memory, single stepping -- GREEN 362 | // E -- Input 8-bit value to current memory location -- CYAN 363 | // F -- Input 16-bit value to set current memory location -- YELLOW 364 | if (input == INPUT_MAIN_ADDR) { 365 | mode = MENU_MAIN_ADDR; 366 | mode_changed = true; 367 | } 368 | 369 | if (input == INPUT_MAIN_BYTE) { 370 | mode = MENU_MAIN_BYTE; 371 | mode_changed = true; 372 | } 373 | 374 | if (input == INPUT_MAIN_RUN_STEP) { 375 | mode = MENU_MODE_STEP; 376 | mode_changed = true; 377 | is_running_steps = true; 378 | start_address = current_address; 379 | reg.pc = current_address; 380 | do_display_pc = true; 381 | } 382 | 383 | if (input == INPUT_MAIN_RUN) { 384 | mode = MENU_MODE_RUN; 385 | mode_changed = true; 386 | is_running_full = true; 387 | start_address = current_address; 388 | reg.pc = current_address; 389 | } 390 | 391 | if (input == INPUT_MAIN_MEM_UP || input == INPUT_MAIN_MEM_DOWN) { 392 | current_address += (input == INPUT_MAIN_MEM_UP ? 1 : -1); 393 | display_left(current_address); 394 | display_right(mem[current_address]); 395 | } 396 | 397 | if (input == INPUT_MAIN_LOAD) { 398 | load_code(); 399 | display_left(current_address); 400 | display_right(mem[current_address]); 401 | } 402 | 403 | if (input == 0x0002) { 404 | printf("****** RUNNING CPU TESTS *****\n"); 405 | test_main(); 406 | } 407 | } 408 | } 409 | 410 | if (mode_changed) set_keys(); 411 | } 412 | 413 | 414 | /** 415 | * @brief Prime the keypad for the current menu mode, setting key colours, 416 | * masking the keys that can be pressed, and, for data-entry screens, 417 | * the number of digits that can be entered. 418 | */ 419 | void set_keys(void) { 420 | 421 | // Clear the keyboard 422 | keypad_clear(); 423 | keypad_update_leds(); 424 | 425 | // Illuminate available keys according to menu mode 426 | switch (mode) { 427 | case MENU_MAIN_ADDR: 428 | // Enter 16-bit address: all keys yellow 429 | keypad_set_all(0x10, 0x10, 0x00); 430 | input_count = 4; 431 | input_mask = 0xFFFF; 432 | input_value = 0; 433 | break; 434 | case MENU_MAIN_BYTE: 435 | // Enter 8-bit data: all keys cyan 436 | keypad_set_all(0x00, 0x10, 0x10); 437 | input_count = 2; 438 | input_mask = 0xFFFF; 439 | input_value = 0; 440 | break; 441 | case MENU_MODE_STEP: 442 | // Run code in single-step mode 443 | keypad_set_led(15, 0x00, 0x10, 0x00); 444 | keypad_set_led(14, 0x10, 0x00, 0x10); 445 | keypad_set_led(13, 0x20, 0x10, 0x00); 446 | keypad_set_led(12, 0x10, 0x00, 0x00); 447 | keypad_set_led(3, 0x00, 0x00, 0x40); 448 | keypad_set_led(0, 0x00, 0x00, 0x40); 449 | input_count = 1; 450 | input_mask = INPUT_STEP_MASK; 451 | break; 452 | case MENU_MODE_CONFIRM: 453 | // Enter or cancel input 454 | keypad_set_led(15, 0x00, 0x10, 0x00); 455 | keypad_set_led(12, 0x10, 0x00, 0x00); 456 | input_count = 1; 457 | input_mask = INPUT_CONF_MASK_ADDR; 458 | 459 | // Show continue for program entry 460 | if (previous_mode == MENU_MAIN_BYTE) { 461 | keypad_set_led(14, 0x20, 0x10, 0x00); 462 | input_mask = INPUT_CONF_MASK_BYTE; 463 | } 464 | 465 | // Show display change for run pause 466 | if (previous_mode == MENU_MODE_RUN) { 467 | keypad_set_led(14, 0x10, 0x00, 0x10); 468 | input_mask = INPUT_CONF_MASK_BYTE; 469 | } 470 | 471 | break; 472 | case MENU_MODE_RUN: 473 | keypad_set_all(0x10, 0x20, 0x20); 474 | input_count = 1; 475 | input_mask = INPUT_RUN_MASK; 476 | break; 477 | case MENU_MODE_RUN_DONE: 478 | keypad_set_all(0x03, 0x06, 0x06); 479 | input_count = 1; 480 | input_mask = INPUT_RUN_MASK; 481 | break; 482 | default: 483 | // Main menu: enter an action 484 | keypad_set_led(15, 0x10, 0x10, 0x00); 485 | keypad_set_led(14, 0x00, 0x10, 0x10); 486 | keypad_set_led(13, 0x00, 0x10, 0x00); 487 | keypad_set_led(12, 0x10, 0x20, 0x20); 488 | keypad_set_led(3, 0x00, 0x00, 0x40); 489 | keypad_set_led(0, 0x00, 0x00, 0x40); 490 | keypad_set_led(11, 0x20, 0x00, 0x20); 491 | input_count = 1; 492 | input_mask = INPUT_MAIN_MASK | 0x02; 493 | break; 494 | } 495 | 496 | keypad_update_leds(); 497 | mode_changed = false; 498 | } 499 | 500 | 501 | /** 502 | * @brief Convert the key press value into an actual value. 503 | * 504 | * @param input: The key press value read from the keypad. 505 | * 506 | * @retval The actual value represented by the pressed key. 507 | */ 508 | uint8_t keypress_to_value(uint16_t input) { 509 | 510 | // NOTE Assumes one key press and exits at the first 511 | // low bit that is set 512 | for (uint32_t i = 0 ; i < 16 ; ++i) { 513 | if ((input & (1 << i)) != 0) return i; 514 | } 515 | 516 | return 0; 517 | } 518 | 519 | 520 | /** 521 | * @brief Update the display by mode. 522 | */ 523 | void update_display(void) { 524 | 525 | uint16_t left = 0; 526 | uint16_t right = 0; 527 | uint16_t address = 0; 528 | uint16_t val = 0; 529 | switch(display_mode) { 530 | case 0: 531 | address = is_running_full ? reg.pc : current_address; 532 | val = is_running_full ? (uint16_t)mem[reg.pc] : (uint16_t)mem[current_address]; 533 | display_left(address); 534 | display_right(val); 535 | printf("0x%04X -> 0x%02X\n", address, val); 536 | return; 537 | case 1: 538 | display_cc(); 539 | return; 540 | case 2: 541 | display_ab_dp(); 542 | return; 543 | case 3: 544 | left = reg.x; 545 | right = reg.y; 546 | break; 547 | default: 548 | left = reg.s; 549 | right = reg.u; 550 | } 551 | 552 | display_value(left, DISPLAY_LEFT, true, false); 553 | display_value(right, DISPLAY_RIGHT, true, false); 554 | } 555 | 556 | /** 557 | * @brief Display the CC register as eight binary digits. 558 | */ 559 | void display_cc(void) { 560 | 561 | ht16k33_clear(display_address[DISPLAY_LEFT], display_buffer[DISPLAY_LEFT]); 562 | ht16k33_clear(display_address[DISPLAY_RIGHT], display_buffer[DISPLAY_RIGHT]); 563 | 564 | for (uint8_t i = 0 ; i < 4 ; ++i) { 565 | ht16k33_set_number(display_address[DISPLAY_RIGHT], display_buffer[DISPLAY_RIGHT], ((reg.cc >> i) & 0x01), 3 - i, false); 566 | } 567 | 568 | for (uint8_t i = 4 ; i < 8 ; ++i) { 569 | ht16k33_set_number(display_address[DISPLAY_LEFT], display_buffer[DISPLAY_LEFT], ((reg.cc >> i) & 0x01), 7 - i, false); 570 | } 571 | 572 | ht16k33_draw(display_address[DISPLAY_LEFT], display_buffer[DISPLAY_LEFT]); 573 | ht16k33_draw(display_address[DISPLAY_RIGHT], display_buffer[DISPLAY_RIGHT]); 574 | } 575 | 576 | /** 577 | * @brief Display the A, B and DP registers evenly spaced on the display. 578 | */ 579 | void display_ab_dp(void) { 580 | 581 | ht16k33_clear(display_address[DISPLAY_LEFT], display_buffer[DISPLAY_LEFT]); 582 | ht16k33_clear(display_address[DISPLAY_RIGHT], display_buffer[DISPLAY_RIGHT]); 583 | 584 | // A register 585 | ht16k33_set_number(display_address[DISPLAY_LEFT], display_buffer[DISPLAY_LEFT], ((reg.a >> 4) & 0x0F), 0, false); 586 | ht16k33_set_number(display_address[DISPLAY_LEFT], display_buffer[DISPLAY_LEFT], (reg.a & 0x0F), 1, false); 587 | 588 | // B register 589 | ht16k33_set_number(display_address[DISPLAY_LEFT], display_buffer[DISPLAY_LEFT], ((reg.b >> 4) & 0x0F), 3, false); 590 | ht16k33_set_number(display_address[DISPLAY_RIGHT], display_buffer[DISPLAY_RIGHT], (reg.b & 0x0F), 0, false); 591 | 592 | // DP register 593 | ht16k33_set_number(display_address[DISPLAY_RIGHT], display_buffer[DISPLAY_RIGHT], ((reg.dp >> 4) & 0x0F), 2, false); 594 | ht16k33_set_number(display_address[DISPLAY_RIGHT], display_buffer[DISPLAY_RIGHT], (reg.dp & 0x0F), 3, false); 595 | 596 | ht16k33_draw(display_address[DISPLAY_LEFT], display_buffer[DISPLAY_LEFT]); 597 | ht16k33_draw(display_address[DISPLAY_RIGHT], display_buffer[DISPLAY_RIGHT]); 598 | } 599 | 600 | 601 | /** 602 | * @brief Display a 16-bit value on the left display. 603 | * 604 | * @param value: The 16-bit value to display. 605 | */ 606 | void display_left(uint16_t value) { 607 | 608 | display_value(value, DISPLAY_LEFT, true, false); 609 | } 610 | 611 | 612 | /** 613 | * @brief Display an 8-bit value on the right display. 614 | * 615 | * @param value: The 8-bit value to display. 616 | */ 617 | void display_right(uint16_t value) { 618 | 619 | display_value(value, DISPLAY_RIGHT, false, false); 620 | } 621 | 622 | 623 | /** 624 | * @brief Display an 8- or 16-bit value on a display, specified by index: 625 | * 0 = left, 1 = right. 626 | * 627 | * @param value: The value to display. 628 | * @param index: The display's index, 0 or 1. 629 | * @param is_16_bit: Whether the value is 16-bit (`true`) or 8-bit (`false`). 630 | */ 631 | void display_value(uint16_t value, uint8_t index, bool is_16_bit, bool show_colon) { 632 | 633 | if (value > 0xFFFF) value = 0x0000; 634 | if (!is_16_bit) value &= 0xFF; 635 | ht16k33_clear(display_address[index], display_buffer[index]); 636 | 637 | if (is_16_bit) { 638 | ht16k33_set_number(display_address[index], display_buffer[index], (value >> 12) & 0x0F, 0, false); 639 | ht16k33_set_number(display_address[index], display_buffer[index], (value >> 8) & 0x0F, 1, false); 640 | } 641 | 642 | ht16k33_set_number(display_address[index], display_buffer[index], (value >> 4) & 0x0F, 2, false); 643 | ht16k33_set_number(display_address[index], display_buffer[index], value & 0x0F, 3, false); 644 | 645 | if (show_colon) ht16k33_show_colon(display_address[index], display_buffer[index], true); 646 | ht16k33_draw(display_address[index], display_buffer[index]); 647 | } 648 | 649 | 650 | /** 651 | * @brief Load code via STDIO on USB and store in memory. The incoming data structure 652 | * indicates at what address this will start. 653 | * 654 | * @retval `true` on a successful load, otherwise `false`. 655 | */ 656 | bool load_code(void) { 657 | 658 | uint8_t load_buffer[262]; 659 | uint16_t bytes_read = 0; 660 | uint16_t block_count = 0; 661 | uint16_t address = 0; 662 | uint16_t start_addr = 0; 663 | uint32_t start = time_us_32(); 664 | 665 | 666 | // Light the LED to show we're clear to receive 667 | gpio_put(PIN_PICO_LED, true); 668 | 669 | while(true) { 670 | bytes_read = get_block(load_buffer); 671 | if (bytes_read > 0) { 672 | block_count++; 673 | if (load_buffer[0] == 0x55 && load_buffer[1] == 0x3C) { 674 | // Got a valid block header 675 | uint8_t block_type = load_buffer[2]; 676 | uint16_t block_len = load_buffer[3]; 677 | 678 | if (block_type == 0xFF) { 679 | // Done -- signal the sender back and 680 | // turn off the LED 681 | printf("OK END\n"); 682 | gpio_put(PIN_PICO_LED, false); 683 | current_address = start_addr; 684 | return true; 685 | } 686 | 687 | // Get the checksum on all blocks but the last 688 | uint32_t checksum = 0; 689 | for (uint16_t i = 2 ; i < 4 + block_len ; i++) { 690 | checksum += load_buffer[i]; 691 | } 692 | 693 | // Break to error if the checksum is bad 694 | if ((checksum & 0xFF) != load_buffer[4 + block_len]) break; 695 | 696 | // Look for the address block 697 | if (block_type == 0x00) { 698 | if (load_buffer[3] != 0x02) break; 699 | address = (load_buffer[4] << 8) | load_buffer[5]; 700 | start_addr = address; 701 | 702 | // ACK the block 703 | printf("OK ADDRESS BLOCK: %i\n", start_addr); 704 | } 705 | 706 | // Look for a data block 707 | if (block_type == 0x01) { 708 | for (uint16_t i = 0 ; i < block_len ; ++i) { 709 | mem[address++] = load_buffer[4 + i]; 710 | } 711 | 712 | // ACK the block 713 | printf("OK DATA BYTES: %i,%i\n", block_count, block_len); 714 | } 715 | } 716 | 717 | bytes_read = 0; 718 | } 719 | 720 | // Check for a timeout: break on fail 721 | if (time_us_32() - start > UPLOAD_TIMEOUT_US) break; 722 | } 723 | 724 | // Signal an error and turn off the LED 725 | flash_led(5); 726 | gpio_put(PIN_PICO_LED, false); 727 | return false; 728 | } 729 | 730 | 731 | /** 732 | * @brief Read in a single transmitted block (up to 262 bytes). 733 | * 734 | * @param buff: A pointer to the byte store buffer, 735 | * 736 | * @retval The index of the last read byte in the buffer. 737 | */ 738 | uint16_t get_block(uint8_t *buff) { 739 | 740 | uint16_t buff_ptr = 0; 741 | while (true) { 742 | int c = getchar_timeout_us(100); 743 | if (c != PICO_ERROR_TIMEOUT && buff_ptr < 262) { 744 | buff[buff_ptr++] = (c & 0xFF); 745 | display_left(buff_ptr); 746 | } else { 747 | break; 748 | } 749 | } 750 | 751 | sleep_ms(10); 752 | return buff_ptr; 753 | } 754 | -------------------------------------------------------------------------------- /source/cpu_tests.c: -------------------------------------------------------------------------------- 1 | /* 2 | * e6809 for Raspberry Pi Pico 3 | * 4 | * @version 0.0.2 5 | * @author smittytone 6 | * @copyright 2025 7 | * @licence MIT 8 | * 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "ops.h" 16 | #include "cpu.h" 17 | #include "cpu_tests.h" 18 | 19 | 20 | /* 21 | * STATICS 22 | */ 23 | static void test_addressing(void); 24 | static void test_alu(void); 25 | static void test_index(void); 26 | static void test_logic(void); 27 | static void test_reg(void); 28 | static void test_branch(void); 29 | static void test_setup(void); 30 | static void test_irqs(void); 31 | static void test_report(uint16_t code, uint32_t err_count); 32 | static void expected(uint16_t wanted, uint16_t got); 33 | 34 | 35 | /* 36 | * GLOBALS 37 | */ 38 | uint32_t errors = 0; 39 | uint32_t passes = 0; 40 | uint32_t tests = 0; 41 | 42 | extern REG_6809 reg; 43 | extern uint8_t mem[KB64]; 44 | extern STATE_6809 state; 45 | 46 | 47 | void test_main(void) { 48 | 49 | tests = 0; 50 | errors = 0; 51 | passes = 0; 52 | 53 | test_addressing(); 54 | test_alu(); 55 | test_index(); 56 | test_logic(); 57 | test_reg(); 58 | test_branch(); 59 | test_irqs(); 60 | 61 | printf("Tests: %i\n", tests); 62 | printf("Passes: %i\n", passes); 63 | printf("Fails: %i\n", errors); 64 | printf("--------------------------------------------------\n"); 65 | } 66 | 67 | 68 | static void test_addressing(void) { 69 | 70 | uint16_t result; 71 | uint32_t current_errors = errors; 72 | 73 | // Immediate 74 | test_setup(); 75 | reg.pc = 0x00FF; 76 | mem[0x00FF] = 0x0A; 77 | result = address_from_mode(MODE_IMMEDIATE); 78 | if (mem[result] == 0x0A) { 79 | passes++; 80 | } else { 81 | errors++; 82 | } 83 | 84 | // Direct 85 | test_setup(); 86 | reg.pc = 0x00FF; 87 | reg.dp = 0x20; 88 | mem[0x00FF] = 0x0A; 89 | mem[0x200A] = 0x0C; 90 | result = address_from_mode(MODE_DIRECT); 91 | if (mem[result] == 0x0C) { 92 | passes++; 93 | } else { 94 | errors++; 95 | } 96 | 97 | // Extended 98 | test_setup(); 99 | reg.pc = 0x00FF; 100 | reg.dp = 0x20; 101 | mem[0x00FF] = 0x0A; 102 | mem[0x0100] = 0x0C; 103 | mem[0x0A0C] = 0x42; 104 | result = address_from_mode(MODE_EXTENDED); 105 | if (mem[result] == 0x42) { 106 | passes++; 107 | } else { 108 | errors++; 109 | } 110 | 111 | test_report(0, errors - current_errors); 112 | } 113 | 114 | 115 | static void test_alu(void) { 116 | 117 | uint8_t result; 118 | uint32_t current_errors = errors; 119 | 120 | // ADC 121 | // Zaks p.122 122 | test_setup(); 123 | reg.cc = 0x0B; 124 | result = add_with_carry(0x14, 0x22); 125 | if (result == 0x37 && reg.cc == 0x00) { 126 | passes++; 127 | } else { 128 | errors++; 129 | expected(0x3700, (uint16_t)((result << 8) | reg.cc)); 130 | } 131 | 132 | // Leventhal p.22-3 133 | test_setup(); 134 | reg.cc = 0x01; 135 | result = add_with_carry(0x3A, 0x7C); 136 | if (result == 0xB7 && reg.cc == 0x2A) { 137 | passes++; 138 | } else { 139 | errors++; 140 | expected(0xB72A, (uint16_t)((result << 8) | reg.cc)); 141 | } 142 | 143 | // ADD 8-bit 144 | // Zaks p.123 145 | test_setup(); 146 | reg.cc = 0x13; 147 | result = add_no_carry(0xF2, 0x39); 148 | if (result == 0x2B && reg.cc == 0x11) { 149 | passes++; 150 | } else { 151 | errors++; 152 | expected(0x2B11, (uint16_t)((result << 8) | reg.cc)); 153 | } 154 | 155 | // Leventhal p.22-3 156 | test_setup(); 157 | result = add_no_carry(0x24, 0x8B); 158 | if (result == 0xAF && reg.cc == 0x08) { 159 | passes++; 160 | } else { 161 | errors++; 162 | expected(0xAF08, (uint16_t)((result << 8) | reg.cc)); 163 | } 164 | 165 | // ADD 16-bit 166 | // Zaks p.124 167 | test_setup(); 168 | reg.a = 0x00; 169 | reg.b = 0x0F; 170 | reg.pc = 0x00; 171 | mem[0] = 0x03; 172 | mem[1] = 0x22; 173 | add_16(0x00, MODE_IMMEDIATE); // First arg, op, is not used 174 | if (reg.a == 0x03 && reg.b == 0x31 && reg.cc == 0x00) { 175 | passes++; 176 | } else { 177 | errors++; 178 | expected(0x0331, (uint16_t)((reg.a << 8) | reg.b)); 179 | expected(0x00, (uint16_t)reg.cc); 180 | } 181 | 182 | // Leventhal p.22-6 183 | test_setup(); 184 | reg.a = 0x10; 185 | reg.b = 0x55; 186 | reg.pc = 0x00; 187 | mem[0] = 0x10; 188 | mem[1] = 0x11; 189 | add_16(0x00, MODE_IMMEDIATE); // First arg, op, is not used 190 | if (reg.a == 0x20 && reg.b == 0x66 && reg.cc == 0x00) { 191 | passes++; 192 | } else { 193 | errors++; 194 | expected(0x2066, (uint16_t)((reg.a << 8) | reg.b)); 195 | expected(0x00, (uint16_t)reg.cc); 196 | } 197 | 198 | // CMP 8-bit 199 | // Leventhal p.22-28 200 | test_setup(); 201 | compare(0xF6, 0x18); 202 | if (reg.cc == 0x08) { 203 | passes++; 204 | } else { 205 | errors++; 206 | expected(0x08, (uint16_t)reg.cc); 207 | } 208 | 209 | // Zaks p.150 210 | test_setup(); 211 | reg.cc = 0x52; 212 | compare(0x05, 0x06); 213 | if (reg.cc == 0x59) { 214 | passes++; 215 | } else { 216 | errors++; 217 | expected(0x59, (uint16_t)reg.cc); 218 | } 219 | 220 | // CMP 16-bit 221 | // Zaks p.151 222 | test_setup(); 223 | reg.cc = 0x23; 224 | reg.x = 0x5410; 225 | reg.pc = 0x00FF; 226 | mem[0x00FF] = 0x3B; 227 | mem[0x0100] = 0x33; 228 | mem[0x3B33] = 0x54; 229 | mem[0x3B34] = 0x10; 230 | cmp_16(CMPX_immed, MODE_EXTENDED, 0x00); 231 | if (reg.cc == 0x24 && reg.x == 0x5410) { 232 | passes++; 233 | } else { 234 | errors++; 235 | expected(0x24, (uint16_t)reg.cc); 236 | expected(0x5410, reg.x); 237 | } 238 | 239 | // Leventhal p.22-29 240 | test_setup(); 241 | reg.x = 0x1AB0; 242 | reg.pc = 0x00FF; 243 | mem[0x00FF] = 0xA4; 244 | mem[0x0100] = 0xF1; 245 | mem[0xA4F1] = 0x1B; 246 | mem[0xA4F2] = 0xB0; 247 | cmp_16(CMPX_immed, MODE_EXTENDED, 0x00); 248 | if ((reg.cc & 0x0F) == 0x09 && reg.x == 0x1AB0) { 249 | passes++; 250 | } else { 251 | errors++; 252 | expected(0x09, (uint16_t)(reg.cc & 0x0F)); 253 | expected(0x1AB0, reg.x); 254 | } 255 | 256 | // COM 257 | // Leventhal p.22-30 258 | test_setup(); 259 | reg.cc = 0x00; 260 | result = complement(0x23); 261 | if (result == 0xDC && reg.cc == 0x09) { 262 | passes++; 263 | } else { 264 | errors++; 265 | expected(0xDC09, (uint16_t)((result << 8) | reg.cc)); 266 | } 267 | 268 | // Zaks p.152 269 | test_setup(); 270 | reg.cc = 0x04; 271 | result = complement(0x9B); 272 | if (result == 0x64 && reg.cc == 0x01) { 273 | passes++; 274 | } else { 275 | errors++; 276 | expected(0x6401, (uint16_t)((result << 8) | reg.cc)); 277 | } 278 | 279 | // DAA 280 | // Leventhal p.22-32 281 | test_setup(); 282 | reg.a = add_no_carry(0x39, 0x47); 283 | daa(); 284 | if (reg.a == 0x86) { 285 | passes++; 286 | } else { 287 | errors++; 288 | expected(0x86, (uint16_t)reg.a); 289 | } 290 | 291 | // Atkinson p.56 292 | test_setup(); 293 | reg.cc = 0x00; 294 | reg.a = add_no_carry(0x64, 0x27); 295 | daa(); 296 | if (reg.a == 0x91) { 297 | passes++; 298 | } else { 299 | errors++; 300 | expected(0x91, (uint16_t)reg.a); 301 | } 302 | 303 | // Zaks p.154 304 | test_setup(); 305 | reg.a = 0x7F; 306 | daa(); 307 | if (reg.a == 0x85) { 308 | passes++; 309 | } else { 310 | errors++; 311 | expected(0x85, (uint16_t)reg.a); 312 | } 313 | 314 | // DEC 315 | // Zaks p.155 316 | test_setup(); 317 | reg.cc = 0x35; 318 | result = decrement(0x32); 319 | if (result == 0x31 && reg.cc == 0x31) { 320 | passes++; 321 | } else { 322 | errors++; 323 | expected(0x3131, (uint16_t)((result << 8) | reg.cc)); 324 | } 325 | 326 | // Leventhal p.22-33 327 | test_setup(); 328 | reg.cc = 0xFF; 329 | result = decrement(0x3A); 330 | if (result == 0x39 && reg.cc == 0xF1) { 331 | passes++; 332 | } else { 333 | errors++; 334 | expected(0x39F1, (uint16_t)((result << 8) | reg.cc)); 335 | } 336 | 337 | test_setup(); 338 | reg.cc = 0x09; 339 | result = decrement(0x80); 340 | if (result == 0x7F && reg.cc == 0x03) { 341 | passes++; 342 | } else { 343 | errors++; 344 | expected(0x7F03, (uint16_t)((result << 8) | reg.cc)); 345 | } 346 | 347 | // INC 348 | // Zaks p.158 349 | test_setup(); 350 | reg.cc = 0x00; 351 | result = increment(0x35); 352 | if (result == 0x36 && reg.cc == 0x00) { 353 | passes++; 354 | } else { 355 | errors++; 356 | expected(0x3600, (uint16_t)((result << 8) | reg.cc)); 357 | } 358 | 359 | test_setup(); 360 | reg.cc = 0x00; 361 | result = increment(0x7F); 362 | if (result == 0x80 && reg.cc == 0x0A) { 363 | passes++; 364 | } else { 365 | errors++; 366 | expected(0x800A, (uint16_t)((result << 8) | reg.cc)); 367 | } 368 | 369 | // Leventhal p.22-38 370 | test_setup(); 371 | reg.cc = 0x01; 372 | result = increment(0xC0); 373 | if (result == 0xC1 && reg.cc == 0x09) { 374 | passes++; 375 | } else { 376 | errors++; 377 | expected(0xC109, (uint16_t)((result << 8) | reg.cc)); 378 | } 379 | 380 | // MUL 381 | // Zaks p.166 382 | test_setup(); 383 | reg.a = 0x0C; 384 | reg.b = 0x64; 385 | mul(); 386 | if (reg.a == 0x04 && reg.b == 0xB0 && (reg.cc & 0x01) == 0x01) { 387 | passes++; 388 | } else { 389 | errors++; 390 | expected(0x04B0, (uint16_t)((reg.a << 8) | reg.b)); 391 | expected(0x01, (uint16_t)reg.cc); 392 | } 393 | 394 | // Levenathal p.22-52 395 | test_setup(); 396 | reg.a = 0x6F; 397 | reg.b = 0x61; 398 | reg.cc = 0x0F; 399 | mul(); 400 | if (reg.a == 0x2A && reg.b == 0x0F && reg.cc == 0x0A) { 401 | passes++; 402 | } else { 403 | errors++; 404 | expected(0x2A0F, (uint16_t)((reg.a << 8) | reg.b)); 405 | expected(0x0A, (uint16_t)reg.cc); 406 | } 407 | 408 | // NEG 409 | // Zaks p.167 410 | test_setup(); 411 | reg.cc = 34; 412 | result = negate(0xF3, false); 413 | // NOTE Zaks CC values weird and wrong 414 | if (result == 0x0D && reg.cc == 0x21) { 415 | passes++; 416 | } else { 417 | errors++; 418 | expected(0x0D21, (uint16_t)((result << 8) | reg.cc)); 419 | } 420 | 421 | // Leventhal p.22-53 422 | test_setup(); 423 | result = negate(0x3A, false); 424 | // NOTE Zaks CC values weird and wrong 425 | if (result == 0xC6 && (reg.cc & 0x0F) == 0x09) { 426 | passes++; 427 | } else { 428 | errors++; 429 | expected(0xC60F, (uint16_t)((result << 8) | (reg.cc & 0x0F))); 430 | } 431 | 432 | // SBC 433 | // Zaks p.179 434 | test_setup(); 435 | reg.cc = 0x01; 436 | result = sub_with_carry(0x35, 0x03); 437 | if (result == 0x31 && reg.cc == 0x20) { 438 | passes++; 439 | } else { 440 | errors++; 441 | expected(0x3120, (uint16_t)((result << 8) | reg.cc)); 442 | } 443 | 444 | // Leventhal p.22-64 445 | test_setup(); 446 | reg.cc = 0x01; 447 | reg.b = 0x14; 448 | reg.y = 0x105A; 449 | reg.pc = 0x0000; 450 | mem[0x0000] = 0xA8; 451 | mem[0x0001] = 0x3E; 452 | mem[reg.y + mem[0x0001]] = 0x34; 453 | sbc(SBCB_indexed, MODE_INDEXED); 454 | if (reg.b == 0xDF && (reg.cc & 0x0F) == 0x08) { 455 | passes++; 456 | } else { 457 | errors++; 458 | expected(0xDF08, (uint16_t)((reg.b << 8) | (reg.cc & 0x0F))); 459 | } 460 | 461 | // SUB 8-bit 462 | // Zaks p.183 463 | test_setup(); 464 | reg.cc = 0x44; 465 | result = subtract(0x03, 0x21); 466 | if (result == 0xE2 && reg.cc == 0x69) { 467 | passes++; 468 | } else { 469 | errors++; 470 | expected(0xE269, (uint16_t)((result << 8) | reg.cc)); 471 | } 472 | 473 | // Leventhal p.183 474 | test_setup(); 475 | result = subtract(0xE3, 0xA0); 476 | if (result == 0x43 && (reg.cc & 0x0F) == 0x00) { 477 | passes++; 478 | } else { 479 | errors++; 480 | expected(0x4300, (uint16_t)((result << 8) | (reg.cc & 0x0F))); 481 | } 482 | 483 | /* 484 | // SUB 16-bit 485 | // Zaks p.184 486 | test_setup(); 487 | reg.cc = 0x59; 488 | reg.a = 0x6B; 489 | reg.b = 0x90; 490 | mem[0] = 0x02; 491 | mem[1] = 0x0F; 492 | sub_16(SUBB_immed, MODE_IMMEDIATE, 0x00); 493 | if (reg.a == 0x69 && reg.b == 0x81 && reg.cc == 0x50) { 494 | passes++; 495 | } else { 496 | errors++; // FAILS ON CC (C BIT) 497 | } 498 | */ 499 | 500 | test_report(1, errors - current_errors); 501 | } 502 | 503 | 504 | static void test_index(void) { 505 | 506 | uint32_t current_errors = errors; 507 | 508 | // ABX 509 | // Zaks p.121 510 | test_setup(); 511 | reg.x = 0x8006; 512 | reg.b = 0xCE; 513 | abx(); 514 | if (reg.x == 0x80D4) { 515 | passes++; 516 | } else { 517 | errors++; 518 | } 519 | 520 | // Leventhal p.22-2 521 | test_setup(); 522 | reg.x = 0x1097; 523 | reg.b = 0x84; 524 | abx(); 525 | if (reg.x == 0x111B) { 526 | passes++; 527 | } else { 528 | errors++; 529 | } 530 | 531 | // Atkinsom p.4 532 | test_setup(); 533 | reg.x = 0x301B; 534 | reg.b = 0xFF; 535 | abx(); 536 | if (reg.x == 0x311A) { 537 | passes++; 538 | } else { 539 | errors++; 540 | } 541 | 542 | // LEA 543 | // Zaks p.163 544 | test_setup(); 545 | reg.pc = 0x00; 546 | mem[0x0000] = 0x4A; 547 | reg.u = 0x0455; 548 | lea(LEAU_indexed); 549 | if (reg.u == 0x045F) { 550 | passes++; 551 | } else { 552 | errors++; 553 | } 554 | 555 | // Leventhal p.22-50 556 | test_setup(); 557 | uint16_t mm = 0xE386; 558 | reg.pc = mm; 559 | mem[mm] = 0x9D; 560 | mem[mm + 1] = 0x02; 561 | mem[mm + 2] = 0x3C; 562 | mem[mm + 3 + 0x023C] = 0xDE; 563 | mem[mm + 3 + 0x023D] = 0x2F; 564 | reg.x = 0xFFFF; 565 | reg.cc = 0xFF; 566 | lea(LEAX_indexed); 567 | if (reg.x == 0xDE2F && reg.cc == 0xFB) { 568 | passes++; 569 | } else { 570 | errors++; 571 | } 572 | 573 | // Atkinson p.4 574 | test_setup(); 575 | reg.pc = 0x00; 576 | mem[0x0000] = 0x85; 577 | reg.x = 0x301B; 578 | reg.b = 0xFF; 579 | lea(LEAX_indexed); 580 | if (reg.x == 0x301A) { 581 | passes++; 582 | } else { 583 | errors++; 584 | } 585 | 586 | 587 | 588 | test_report(2, errors - current_errors); 589 | } 590 | 591 | 592 | static void test_logic(void) { 593 | 594 | uint8_t result; 595 | uint32_t current_errors = errors; 596 | 597 | // AND 598 | // Zaks p.125 599 | test_setup(); 600 | reg.cc = 0x32; 601 | result = do_and(0x8B, 0x0F); 602 | if (result == 0x0B && reg.cc == 0x30) { 603 | passes++; 604 | } else { 605 | errors++; 606 | expected(0x0B30, (uint16_t)((result << 8) | reg.cc)); 607 | } 608 | 609 | // Leventhal p.22-7 610 | test_setup(); 611 | reg.cc = 0x0F; 612 | result = do_and(0xFC, 0x13); 613 | if (result == 0x10 && reg.cc == 0x01) { 614 | passes++; 615 | } else { 616 | errors++; 617 | expected(0x1001, (uint16_t)((result << 8) | reg.cc)); 618 | } 619 | 620 | // ANDCC 621 | // Zaks p.126 622 | test_setup(); 623 | reg.cc = 0x79; 624 | andcc(0xAF); 625 | if (reg.cc == 0x29) { 626 | passes++; 627 | } else { 628 | errors++; 629 | expected(0x29, (uint16_t)reg.cc); 630 | } 631 | 632 | // Leventhal p.22-8 633 | test_setup(); 634 | reg.cc = 0xD4; 635 | andcc(0xBF); 636 | if (reg.cc == 0x94) { 637 | passes++; 638 | } else { 639 | errors++; 640 | } 641 | 642 | // ASL/LSL 643 | // Zaks p.127 644 | test_setup(); 645 | reg.cc = 0x04; 646 | result = logic_shift_left(0xA5); 647 | if (result == 0x4A && reg.cc == 0x03) { 648 | passes++; 649 | } else { 650 | errors++; 651 | expected(0x4A03, (uint16_t)((result << 8) | reg.cc)); 652 | } 653 | 654 | // Zaks p.164 655 | test_setup(); 656 | result = logic_shift_left(0xB8); 657 | if (result == 0x70 && reg.cc == 0x03) { 658 | passes++; 659 | } else { 660 | errors++; 661 | expected(0x7003, (uint16_t)((result << 8) | reg.cc)); 662 | } 663 | 664 | // Leventhal p.22-9 665 | test_setup(); 666 | result = logic_shift_left(0x7A); 667 | if (result == 0xF4 && reg.cc == 0x0A) { 668 | passes++; 669 | } else { 670 | errors++; 671 | expected(0xF40A, (uint16_t)((result << 8) | reg.cc)); 672 | } 673 | 674 | // ASR 675 | // Zaks p.128 676 | test_setup(); 677 | reg.cc = 0x00; 678 | result = arith_shift_right(0xE5); 679 | if (result == 0xF2 && reg.cc == 0x09) { 680 | passes++; 681 | } else { 682 | errors++; 683 | expected(0xF209, (uint16_t)((result << 8) | reg.cc)); 684 | } 685 | 686 | // Leventhal p22-10 687 | test_setup(); 688 | reg.cc = 0x00; 689 | result = arith_shift_right(0xCB); 690 | if (result == 0xE5 && reg.cc == 0x09) { 691 | passes++; 692 | } else { 693 | errors++; 694 | expected(0xE509, (uint16_t)((result << 8) | reg.cc)); 695 | } 696 | 697 | // Me 698 | test_setup(); 699 | reg.cc = 0x0F; 700 | result = partial_shift_right(0xA9); 701 | if (result == 0xD4 && is_bit_set(reg.cc, CC_C_BIT) && !is_bit_set(reg.cc, CC_Z_BIT) && !is_bit_set(reg.cc, CC_N_BIT)) { 702 | passes++; 703 | } else { 704 | errors++; 705 | expected(0xD403, (uint16_t)((result << 8) | reg.cc)); 706 | } 707 | 708 | // BIT 709 | // Leventhal p.22-16 710 | test_setup(); 711 | reg.cc = 0xFF; 712 | reg.a = 0xA6; 713 | reg.pc = 0x0000; 714 | mem[0] = 0xE0; 715 | bit(BITA_immed, MODE_IMMEDIATE); 716 | if (reg.a == 0xA6 && reg.cc == 0xF9) { 717 | passes++; 718 | } else { 719 | errors++; 720 | expected(0xA6F9, (uint16_t)((reg.a << 8) | reg.cc)); 721 | } 722 | 723 | // CLR 724 | // Zaks p.149 725 | test_setup(); 726 | reg.a = 0xE2; 727 | reg.cc = 0x00; 728 | clr(CLRA, MODE_INHERENT); 729 | if (reg.a == 0x00 && reg.cc == 0x04) { 730 | passes++; 731 | } else { 732 | errors++; 733 | expected(0x0004, (uint16_t)((reg.a << 8) | reg.cc)); 734 | } 735 | 736 | // Leventhal p.22-27 737 | test_setup(); 738 | reg.a = 0x43; 739 | reg.cc = 0x00; 740 | clr(CLRA, MODE_INHERENT); 741 | if (reg.a == 0x00 && reg.cc == 0x04) { 742 | passes++; 743 | } else { 744 | errors++; 745 | expected(0x0004, (uint16_t)((reg.a << 8) | reg.cc)); 746 | } 747 | 748 | // EOR 749 | // Zaks p.156 750 | test_setup(); 751 | reg.cc = 0x03; 752 | result = do_xor(0xF2, 0x98); 753 | if (result == 0x6A && reg.cc == 0x01) { 754 | passes++; 755 | } else { 756 | errors++; 757 | expected(0x6A01, (uint16_t)((result << 8) | reg.cc)); 758 | } 759 | 760 | // Leventhal p.22-35 761 | test_setup(); 762 | reg.cc = 0xFF; 763 | result = do_xor(0xE3, 0xA0); 764 | if (result == 0x43 && reg.cc == 0xF1) { 765 | passes++; 766 | } else { 767 | errors++; 768 | expected(0x43F1, (uint16_t)((result << 8) | reg.cc)); 769 | } 770 | 771 | // LSR 772 | // Zaks p.165 773 | test_setup(); 774 | reg.cc = 0x0F; 775 | result = logic_shift_right(0x3E); 776 | // NOTE Error in Zaks: CC is 0x02 not 0x00 777 | if (result == 0x1F && reg.cc == 0x02) { 778 | passes++; 779 | } else { 780 | errors++; 781 | expected(0x1F02, (uint16_t)((result << 8) | reg.cc)); 782 | } 783 | 784 | // OR 785 | // Zaks p.169 786 | test_setup(); 787 | reg.cc = 0x43; 788 | result = do_or(0xDA, 0x0F); 789 | if (result == 0xDF && reg.cc == 0x49) { 790 | passes++; 791 | } else { 792 | errors++; 793 | expected(0xDF49, (uint16_t)((result << 8) | reg.cc)); 794 | } 795 | 796 | // Leventhal p.169 797 | test_setup(); 798 | reg.cc = 0x0F; 799 | result = do_or(0xE3, 0xAB); 800 | if (result == 0xEB && reg.cc == 0x09) { 801 | passes++; 802 | } else { 803 | errors++; 804 | expected(0xEB09, (uint16_t)((result << 8) | reg.cc)); 805 | } 806 | 807 | // ORCC 808 | // Zaks p.170 809 | test_setup(); 810 | reg.cc = 0x13; 811 | orcc(0x50); 812 | if (reg.cc == 0x53) { 813 | passes++; 814 | } else { 815 | errors++; 816 | expected(0x53, (uint16_t)reg.cc); 817 | } 818 | 819 | test_setup(); 820 | reg.cc = 0x13; 821 | orcc(0xC0); 822 | if (reg.cc == 0xD3) { 823 | passes++; 824 | } else { 825 | errors++; 826 | expected(0xD3, (uint16_t)reg.cc); 827 | } 828 | 829 | // ROL 830 | // Zaks p.175 831 | test_setup(); 832 | reg.cc = 0x09; 833 | result = rotate_left(0x89); 834 | // NOTE Error in Zaks: CC is 0x02 not 0x00 835 | if (result == 0x13 && reg.cc == 0x03) { 836 | passes++; 837 | } else { 838 | errors++; 839 | expected(0x1303, (uint16_t)((result << 8) | reg.cc)); 840 | } 841 | 842 | // Leventhal p.22-60 843 | test_setup(); 844 | reg.cc = 0x0E; 845 | reg.pc = 0x00; 846 | mem[0x0000] = 0xA4; 847 | reg.y = 0x1403; 848 | mem[reg.y] = 0x2E; 849 | rol(0x69, MODE_INDEXED); 850 | reg.a = mem[reg.y]; 851 | // NOTE Error in Zaks: CC is 0x02 not 0x00 852 | if (reg.a == 0x5C && reg.cc == 0x00) { 853 | passes++; 854 | } else { 855 | errors++; 856 | expected(0x5C00, (uint16_t)((reg.a << 8) | reg.cc)); 857 | } 858 | 859 | // ROR 860 | // Zaks p.176 861 | test_setup(); 862 | reg.cc = 0x09; 863 | result = rotate_right(0x89); 864 | // NOTE Error in Zaks: CC is 0x02 not 0x00 865 | if (result == 0xC4 && reg.cc == 0x09) { 866 | passes++; 867 | } else { 868 | errors++; 869 | expected(0xC409, (uint16_t)((result << 8) | reg.cc)); 870 | } 871 | 872 | test_report(3, errors - current_errors); 873 | } 874 | 875 | 876 | static void test_reg(void) { 877 | 878 | uint32_t current_errors = errors; 879 | 880 | // CWAI 881 | // TODO 882 | 883 | // EXG 884 | // Zaks p.157 885 | test_setup(); 886 | reg.a = 0x42; 887 | reg.dp = 0x00; 888 | transfer_decode(0x8B, true); 889 | if (reg.a == 0x00 && reg.dp == 0x42) { 890 | passes++; 891 | } else { 892 | errors++; 893 | } 894 | 895 | // Leventhal p.22-36 896 | test_setup(); 897 | reg.a = 0x7E; 898 | reg.b = 0xA5; 899 | transfer_decode(0x89, true); 900 | if (reg.a == 0xA5 && reg.b == 0x7E) { 901 | passes++; 902 | } else { 903 | errors++; 904 | } 905 | 906 | test_setup(); 907 | reg.a = 0x7E; 908 | reg.x = 0x0011; 909 | transfer_decode(0x81, true); 910 | if (reg.a == 0x7E && reg.x == 0x0011) { 911 | passes++; 912 | } else { 913 | errors++; 914 | expected(0x0011, reg.x); 915 | } 916 | 917 | // LD 8-bit 918 | // Zaks p.161 919 | test_setup(); 920 | reg.cc = 0x13; 921 | reg.pc = 0x00; 922 | mem[0x0000] = 0xee; 923 | mem[0x0001] = 0x01; 924 | mem[0xee01] = 0xF2; 925 | ld(LDA_extended, MODE_EXTENDED); 926 | if (reg.a == 0xF2 && reg.cc == 0x19 && ((reg.cc & 0x02) == 0x00)) { 927 | passes++; 928 | } else { 929 | errors++; 930 | } 931 | 932 | // Leventhal p.22-48 933 | test_setup(); 934 | reg.x = 0x13E1; 935 | reg.a = 0x20; 936 | reg.b = 0xB8; 937 | reg.pc = 0x00; 938 | reg.cc = 0x03; 939 | mem[0x0000] = 0x9B; 940 | mem[0x3499] = 0xA4; 941 | mem[0x349A] = 0x7D; 942 | mem[0xA47D] = 0xAA; 943 | ld(LDB_indexed, MODE_INDEXED); 944 | if (reg.b == 0xAA && reg.cc == 0x09) { 945 | passes++; 946 | } else { 947 | errors++; 948 | } 949 | 950 | // LD 16-bit 951 | // Zaks p.162 952 | test_setup(); 953 | reg.cc = 0x54; 954 | reg.pc = 0x00; 955 | mem[0x0000] = 0x14; 956 | mem[0x0001] = 0xA2; 957 | reg.a = 0x03; 958 | reg.b = 0x30; 959 | ld_16(LDD_immed, MODE_IMMEDIATE, 0); 960 | if (reg.a == 0x14 && reg.b == 0xA2 && reg.cc == 0x50) { 961 | passes++; 962 | } else { 963 | errors++; 964 | } 965 | 966 | // Leventhal p.22-49 967 | test_setup(); 968 | reg.y = 0x8E05; 969 | reg.a = 0x20; 970 | reg.b = 0xB8; 971 | reg.pc = 0x00; 972 | reg.cc = 0x03; 973 | mem[0x0000] = 0xB1; 974 | mem[0x8E05] = 0xB3; 975 | mem[0x8E06] = 0x94; 976 | mem[0xB394] = 0x07; 977 | mem[0xB395] = 0xF2; 978 | ld_16(LDD_indexed, MODE_INDEXED, 0); 979 | if (reg.a == 0x07 && reg.b == 0xF2 && reg.y == 0x8e07 && reg.cc == 0x01) { 980 | passes++; 981 | } else { 982 | errors++; 983 | } 984 | 985 | // PSHS 986 | // Zak p.171 987 | test_setup(); 988 | reg.s = 0xFFFF; 989 | reg.a = 1; 990 | reg.b = 2; 991 | reg.cc = 0xFF; 992 | reg.dp = 4; 993 | reg.x = 0x8008; 994 | reg.y = 0x8010; 995 | reg.u = 0x8020; 996 | uint8_t smem[13] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; 997 | push(true, 0xFF); 998 | for (uint16_t i = reg.s ; i < 65535 ; i++) { 999 | smem[i - reg.s] = mem[i]; 1000 | } 1001 | 1002 | if (reg.s == 65523 && smem[3] == 0x04) { 1003 | passes++; 1004 | } else { 1005 | errors++; 1006 | } 1007 | 1008 | // PSHU 1009 | // Zak p.172 1010 | test_setup(); 1011 | reg.u = 0xFFFF; 1012 | reg.a = 1; 1013 | reg.b = 2; 1014 | reg.cc = 0xFF; 1015 | reg.dp = 4; 1016 | reg.x = 0x8008; 1017 | reg.y = 0x8010; 1018 | reg.s = 0x8021; 1019 | uint8_t umem[13] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; 1020 | push(false, 0xFF); 1021 | for (uint16_t i = reg.u ; i < 65535 ; i++) { 1022 | umem[i - reg.u] = mem[i]; 1023 | } 1024 | 1025 | if (reg.u == 65523 && umem[8] == 0x80 && umem[9] == 0x21) { 1026 | passes++; 1027 | } else { 1028 | errors++; 1029 | } 1030 | 1031 | // SEX 1032 | // Zaks p.180 1033 | test_setup(); 1034 | reg.b = 0xE6; 1035 | sex(); 1036 | if (reg.a == 0xFF && reg.b == 0xE6) { 1037 | passes++; 1038 | } else { 1039 | errors++; 1040 | } 1041 | 1042 | // ST 8-bit 1043 | // Zaks p.181 1044 | test_setup(); 1045 | reg.cc = 0x0F; 1046 | reg.b = 0xE5; 1047 | reg.x = 0x556A; 1048 | mem[0x0000] = 0xE7; 1049 | mem[0x0001] = 0x98; 1050 | mem[0x0002] = 0x0F; 1051 | mem[0x5579] = 0x03; 1052 | mem[0x557A] = 0xBB; 1053 | mem[0x03BB] = 0x02; 1054 | process_next_instruction(); 1055 | reg.a = mem[0x03BB]; 1056 | if (reg.a == 0xE5 && reg.cc == 0x09) { 1057 | passes++; 1058 | } else { 1059 | errors++; 1060 | } 1061 | 1062 | // Leventhal p.22-67 1063 | test_setup(); 1064 | reg.cc = 0x0F; 1065 | reg.b = 0x63; 1066 | reg.y = 0x0238; 1067 | mem[0x0000] = 0xE7; 1068 | mem[0x0001] = 0xA9; 1069 | mem[0x0002] = 0x03; 1070 | mem[0x0003] = 0x02; 1071 | mem[0x053A] = 0xFF; 1072 | process_next_instruction(); 1073 | reg.a = mem[0x053A]; 1074 | if (reg.a == 0x63 && reg.cc == 0x01) { 1075 | passes++; 1076 | } else { 1077 | errors++; 1078 | } 1079 | 1080 | // ST 16-bit 1081 | // Zaks p.182 1082 | test_setup(); 1083 | reg.x = 0x660C; 1084 | reg.cc = 0x0F; 1085 | mem[0x0000] = 0x12; 1086 | mem[0x0001] = 0xB0; 1087 | mem[0x12B0] = 0x37; 1088 | mem[0x12B1] = 0xBF; 1089 | st_16(STX_extended, MODE_EXTENDED, 0x00); 1090 | reg.y = (mem[0x12B0] << 8) | mem[0x12B1]; 1091 | if (reg.x == reg.y && reg.cc == 0x01) { 1092 | passes++; 1093 | } else { 1094 | errors++; 1095 | } 1096 | 1097 | // Leventhal p.22-67 1098 | test_setup(); 1099 | reg.y = 0x1430; 1100 | reg.x = reg.y + 2; 1101 | reg.a = 0xC1; 1102 | reg.b = 0x9A; 1103 | reg.cc = 0x0F; 1104 | mem[0x0000] = 0xED; 1105 | mem[0x0001] = 0xA1; 1106 | mem[0x1430] = 0xFF; 1107 | mem[0x1431] = 0xFF; 1108 | process_next_instruction(); 1109 | if (reg.a == mem[0x1430] && reg.b == mem[0x1431] && reg.cc == 0x09 && reg.y == reg.x) { 1110 | passes++; 1111 | } else { 1112 | errors++; 1113 | } 1114 | 1115 | test_report(4, errors - current_errors); 1116 | } 1117 | 1118 | 1119 | static void test_branch(void) { 1120 | 1121 | uint32_t current_errors = errors; 1122 | 1123 | // JMP 1124 | test_setup(); 1125 | reg.pc = 0x00FF; 1126 | mem[0x00FF] = 0xAA; 1127 | mem[0x0100] = 0xAA; 1128 | jmp(MODE_EXTENDED); 1129 | if (reg.pc == 0xAAAA) { 1130 | passes++; 1131 | } else { 1132 | errors++; 1133 | expected(0xAAAA, reg.pc); 1134 | } 1135 | 1136 | // Zaks p.159 1137 | test_setup(); 1138 | reg.pc = 0x3041; 1139 | reg.x = 0xB290; 1140 | mem[0x3041] = 0x84; 1141 | jmp(MODE_INDEXED); 1142 | if (reg.pc == 0xB290) { 1143 | passes++; 1144 | } else { 1145 | errors++; 1146 | expected(0xB290, reg.pc); 1147 | } 1148 | 1149 | // Leventhal p.22-40 1150 | test_setup(); 1151 | reg.pc = 0x00FF; 1152 | mem[0x00FF] = 0x3A; 1153 | mem[0x0100] = 0x05; 1154 | jmp(MODE_EXTENDED); 1155 | if (reg.pc == 0x3A05) { 1156 | passes++; 1157 | } else { 1158 | errors++; 1159 | expected(0x3A05, reg.pc); 1160 | } 1161 | 1162 | test_setup(); 1163 | reg.pc = 0x00FF; 1164 | mem[0x00FF] = 0x40; 1165 | mem[0x0100] = 0x00; 1166 | jmp(MODE_EXTENDED); 1167 | if (reg.pc == 0x4000) { 1168 | passes++; 1169 | } else { 1170 | errors++; 1171 | expected(0x4000, reg.pc); 1172 | } 1173 | 1174 | test_setup(); 1175 | reg.pc = 0x00FF; 1176 | mem[0x00FF] = 0x9F; 1177 | mem[0x0100] = 0x40; 1178 | mem[0x0101] = 0x00; 1179 | mem[0x4000] = 0xFF; 1180 | mem[0x4001] = 0x00; 1181 | jmp(MODE_INDEXED); 1182 | if (reg.pc == 0xFF00) { 1183 | passes++; 1184 | } else { 1185 | errors++; 1186 | expected(0xFF00, reg.pc); 1187 | } 1188 | 1189 | test_setup(); 1190 | reg.pc = 0x00FF; 1191 | mem[0x00FF] = 0x84; 1192 | reg.x = 0x4000; 1193 | jmp(MODE_INDEXED); 1194 | if (reg.pc == 0x4000) { 1195 | passes++; 1196 | } else { 1197 | errors++; 1198 | expected(0x4000, reg.pc); 1199 | } 1200 | 1201 | test_setup(); 1202 | reg.pc = 0x00FF; 1203 | mem[0x00FF] = 0x85; 1204 | reg.x = 0x4000; 1205 | reg.b = 0x7F; 1206 | jmp(MODE_INDEXED); 1207 | if (reg.pc == 0x407F) { 1208 | passes++; 1209 | } else { 1210 | errors++; 1211 | expected(0x407F, reg.pc); 1212 | } 1213 | 1214 | test_setup(); 1215 | reg.pc = 0x00FF; 1216 | mem[0x00FF] = 0x95; 1217 | reg.x = 0x4000; 1218 | reg.b = 0x7F; 1219 | mem[0x407F] = 0xAA; 1220 | mem[0x4080] = 0xBB; 1221 | jmp(MODE_INDEXED); 1222 | if (reg.pc == 0xAABB) { 1223 | passes++; 1224 | } else { 1225 | errors++; 1226 | expected(0xAABB, reg.pc); 1227 | } 1228 | 1229 | test_setup(); 1230 | reg.pc = 0x00FF; 1231 | reg.dp = 0xFF; 1232 | mem[0x00FF] = 0x40; 1233 | jmp(MODE_DIRECT); 1234 | if (reg.pc == 0xFF40) { 1235 | passes++; 1236 | } else { 1237 | errors++; 1238 | expected(0xFF40, reg.pc); 1239 | } 1240 | 1241 | // JSR 1242 | // Zaks p.160 1243 | test_setup(); 1244 | reg.s = 0x03F2; 1245 | reg.pc = 0x10CB; 1246 | mem[0x10CB] = 0x32; 1247 | mem[0x10CC] = 0x0D; 1248 | mem[0x03F0] = 0x03; 1249 | mem[0x03F1] = 0x4B; 1250 | jsr(MODE_EXTENDED); 1251 | reg.a = mem[0x03F0]; // MSB 1252 | reg.b = mem[0x03F1]; // LSB 1253 | if (reg.pc == 0x320D && reg.s == 0x03F0 && reg.a == 0x10 && reg.b == 0xCD) { 1254 | passes++; 1255 | } else { 1256 | errors++; 1257 | expected(0x320D, reg.pc); 1258 | expected(0x03F0, reg.s); 1259 | expected(0x10CD, (uint16_t)((reg.a << 8) | reg.b)); 1260 | } 1261 | 1262 | // RTS 1263 | // Zaks p.178 1264 | tests++; 1265 | rts(); 1266 | if (reg.pc == 0x10CD && reg.s == 0x03F2) { 1267 | passes++; 1268 | } else { 1269 | errors++; 1270 | expected(0x10CD, reg.pc); 1271 | expected(0x03F2, reg.s); 1272 | } 1273 | 1274 | // JSR 1275 | // Leventhal p.22-14 1276 | test_setup(); 1277 | reg.s = 0x08A0; 1278 | reg.pc = 0xE56C; 1279 | mem[0xE56C] = 0xE1; 1280 | mem[0xE56D] = 0xA3; 1281 | mem[0x089E] = 0xFF; 1282 | mem[0x089F] = 0xFF; 1283 | jsr(MODE_EXTENDED); 1284 | reg.a = mem[0x089E]; // MSB 1285 | reg.b = mem[0x089F]; // LSB 1286 | if (reg.pc == 0xE1A3 && reg.s == 0x089E && reg.a == 0xE5 && reg.b == 0x6E) { 1287 | passes++; 1288 | } else { 1289 | errors++; 1290 | expected(0xE1A3, reg.pc); 1291 | expected(0x089E, reg.s); 1292 | expected(0xE56E, (uint16_t)((reg.a << 8) | reg.b)); 1293 | } 1294 | 1295 | // RTS 1296 | // Leventhal p.22-62 1297 | tests++; 1298 | rts(); 1299 | if (reg.pc == 0xE56E && reg.s == 0x08A0) { 1300 | passes++; 1301 | } else { 1302 | errors++; 1303 | expected(0xE56E, reg.pc); 1304 | expected(0x08A0, reg.s); 1305 | } 1306 | 1307 | // RTI 1308 | // TODO 1309 | // Zaks p.177 1310 | // Leventhal p.22-61 1311 | 1312 | // SWI 1313 | // Zaks p.185-7 1314 | test_setup(); 1315 | mem[SWI1_VECTOR] = 0x20; 1316 | mem[SWI1_VECTOR + 1] = 0x01; 1317 | reg.cc = 0x00; 1318 | reg.s = 0xF000; 1319 | reg.x = (mem[SWI1_VECTOR] << 8) | mem[SWI1_VECTOR + 1]; 1320 | swi(1); 1321 | if (reg.pc == reg.x && reg.cc == 0xD0) { 1322 | passes++; 1323 | } else { 1324 | errors++; 1325 | expected(reg.x, reg.pc); 1326 | expected(0xD0, (uint16_t)reg.cc); 1327 | } 1328 | 1329 | test_setup(); 1330 | mem[SWI2_VECTOR] = 0x20; 1331 | mem[SWI2_VECTOR + 1] = 0x01; 1332 | reg.cc = 0x00; 1333 | reg.s = 0xF000; 1334 | reg.x = (mem[SWI2_VECTOR] << 8) | mem[SWI2_VECTOR + 1]; 1335 | swi(2); 1336 | if (reg.pc == reg.x && reg.cc == 0x80) { 1337 | passes++; 1338 | } else { 1339 | errors++; 1340 | expected(reg.x, reg.pc); 1341 | expected(0x80, (uint16_t)reg.cc); 1342 | } 1343 | 1344 | test_setup(); 1345 | mem[SWI3_VECTOR] = 0x20; 1346 | mem[SWI3_VECTOR + 1] = 0x01; 1347 | reg.s = 0xF000; 1348 | reg.x = (mem[SWI3_VECTOR] << 8) | mem[SWI3_VECTOR + 1]; 1349 | swi(3); 1350 | if (reg.pc == reg.x && reg.cc == 0x80) { 1351 | passes++; 1352 | } else { 1353 | errors++; 1354 | expected(reg.x, reg.pc); 1355 | expected(0x80, (uint16_t)reg.cc); 1356 | } 1357 | 1358 | // TFR 1359 | test_setup(); 1360 | reg.a = 0xFF; 1361 | transfer_decode(0x8A, false); 1362 | if (reg.cc == reg.a) { 1363 | passes++; 1364 | } else { 1365 | errors++; 1366 | expected(reg.a, (uint16_t)reg.cc); 1367 | } 1368 | 1369 | // BCC 1370 | // Branch back 1371 | test_setup(); 1372 | reg.cc = 0x00; 1373 | reg.pc = 0xFFF; 1374 | mem[0x0FFF] = 0xFF; 1375 | do_branch(BCC, false); 1376 | if (reg.pc == 0x0FFE) { 1377 | passes++; 1378 | } else { 1379 | errors++; 1380 | expected(0x0FFE, reg.pc); 1381 | } 1382 | 1383 | // Branch forward 1384 | test_setup(); 1385 | reg.cc = 0x00; 1386 | reg.pc = 0x0FFF; 1387 | mem[0x0FFF] = 0x01; 1388 | do_branch(BCC, false); 1389 | if (reg.pc == 0x1000) { 1390 | passes++; 1391 | } else { 1392 | errors++; 1393 | expected(0x1000, reg.pc); 1394 | } 1395 | 1396 | // No branch 1397 | test_setup(); 1398 | reg.cc = 0x01; 1399 | reg.pc = 0x0FFF; 1400 | mem[0x0FFF] = 0x01; 1401 | do_branch(BCC, false); 1402 | if (reg.pc == 0x0FFF) { 1403 | passes++; 1404 | } else { 1405 | errors++; 1406 | expected(0x0FFF, reg.pc); 1407 | } 1408 | 1409 | // BCS 1410 | // Branch back 1411 | test_setup(); 1412 | reg.cc = 0x01; 1413 | reg.pc = 0x0FFF; 1414 | mem[0x0FFF] = 0xFF; 1415 | do_branch(BCS, false); 1416 | if (reg.pc == 0x0FFE) { 1417 | passes++; 1418 | } else { 1419 | errors++; 1420 | expected(0x0FFE, reg.pc); 1421 | } 1422 | 1423 | // Branch forward 1424 | test_setup(); 1425 | reg.cc = 0x01; 1426 | reg.pc = 0x0FFF; 1427 | mem[0x0FFF] = 0x01; 1428 | do_branch(BCS, false); 1429 | if (reg.pc == 0x1000) { 1430 | passes++; 1431 | } else { 1432 | errors++; 1433 | expected(0x1000, reg.pc); 1434 | } 1435 | 1436 | // No branch 1437 | test_setup(); 1438 | reg.cc = 0x00; 1439 | reg.pc = 0x0FFF; 1440 | mem[0x0FFF] = 0x01; 1441 | do_branch(BCS, false); 1442 | if (reg.pc == 0x0FFF) { 1443 | passes++; 1444 | } else { 1445 | errors++; 1446 | expected(0x0FFF, reg.pc); 1447 | } 1448 | 1449 | // BEQ 1450 | // Branch back 1451 | test_setup(); 1452 | reg.cc = 0x04; 1453 | reg.pc = 0x0FFF; 1454 | mem[0x0FFF] = 0xFF; 1455 | do_branch(BEQ, false); 1456 | if (reg.pc == 0x0FFE) { 1457 | passes++; 1458 | } else { 1459 | errors++; 1460 | expected(0x0FFE, reg.pc); 1461 | } 1462 | 1463 | // Branch forward 1464 | test_setup(); 1465 | reg.cc = 0x04; 1466 | reg.pc = 0x0FFF; 1467 | mem[0x0FFF] = 0x01; 1468 | do_branch(BEQ, false); 1469 | if (reg.pc == 0x1000) { 1470 | passes++; 1471 | } else { 1472 | errors++; 1473 | expected(0x1000, reg.pc); 1474 | } 1475 | 1476 | // No branch 1477 | test_setup(); 1478 | reg.cc = 0x00; 1479 | reg.pc = 0x0FFF; 1480 | mem[0x0FFF] = 0x01; 1481 | do_branch(BEQ, false); 1482 | if (reg.pc == 0x0FFF) { 1483 | passes++; 1484 | } else { 1485 | errors++; 1486 | expected(0x0FFF, reg.pc); 1487 | } 1488 | 1489 | // BGE 1490 | // Branch back 1491 | test_setup(); 1492 | reg.cc = 0x0A; 1493 | reg.pc = 0x0FFF; 1494 | mem[0x0FFF] = 0xFF; 1495 | do_branch(BGE, false); 1496 | if (reg.pc == 0x0FFE) { 1497 | passes++; 1498 | } else { 1499 | errors++; 1500 | expected(0x0FFE, reg.pc); 1501 | } 1502 | 1503 | // Branch forward 1504 | test_setup(); 1505 | reg.cc = 0x0A; 1506 | reg.pc = 0x0FFF; 1507 | mem[0x0FFF] = 0x01; 1508 | do_branch(BGE, false); 1509 | if (reg.pc == 0x1000) { 1510 | passes++; 1511 | } else { 1512 | errors++; 1513 | expected(0x1000, reg.pc); 1514 | } 1515 | 1516 | // No branch 1517 | test_setup(); 1518 | reg.cc = 0x08; 1519 | reg.pc = 0x0FFF; 1520 | mem[0x0FFF] = 0x01; 1521 | do_branch(BGE, false); 1522 | if (reg.pc == 0x0FFF) { 1523 | passes++; 1524 | } else { 1525 | errors++; 1526 | expected(0x0FFF, reg.pc); 1527 | } 1528 | 1529 | // BGT 1530 | // Branch back 1531 | test_setup(); 1532 | reg.cc = 0x0A; 1533 | reg.pc = 0x0FFF; 1534 | mem[0x0FFF] = 0xFF; 1535 | do_branch(BGT, false); 1536 | if (reg.pc == 0x0FFE) { 1537 | passes++; 1538 | } else { 1539 | errors++; 1540 | expected(0x0FFE, reg.pc); 1541 | } 1542 | 1543 | // Branch forward 1544 | test_setup(); 1545 | reg.cc = 0x0A; 1546 | reg.pc = 0x0FFF; 1547 | mem[0x0FFF] = 0x01; 1548 | do_branch(BGT, false); 1549 | if (reg.pc == 0x1000) { 1550 | passes++; 1551 | } else { 1552 | errors++; 1553 | expected(0x1000, reg.pc); 1554 | } 1555 | 1556 | // No branch 1557 | test_setup(); 1558 | reg.cc = 0x0E; 1559 | reg.pc = 0x0FFF; 1560 | mem[0x0FFF] = 0x01; 1561 | do_branch(BGT, false); 1562 | if (reg.pc == 0x0FFF) { 1563 | passes++; 1564 | } else { 1565 | errors++; 1566 | expected(0x0FFF, reg.pc); 1567 | } 1568 | 1569 | // BHI 1570 | // Branch back 1571 | test_setup(); 1572 | reg.cc = 0x0A; 1573 | reg.pc = 0x0FFF; 1574 | mem[0x0FFF] = 0xFF; 1575 | do_branch(BHI, false); 1576 | if (reg.pc == 0x0FFE) { 1577 | passes++; 1578 | } else { 1579 | errors++; 1580 | expected(0x0FFE, reg.pc); 1581 | } 1582 | 1583 | // Branch forward 1584 | test_setup(); 1585 | reg.cc = 0x0A; 1586 | reg.pc = 0x0FFF; 1587 | mem[0x0FFF] = 0x01; 1588 | do_branch(BHI, false); 1589 | if (reg.pc == 0x1000) { 1590 | passes++; 1591 | } else { 1592 | errors++; 1593 | expected(0x1000, reg.pc); 1594 | } 1595 | 1596 | // No branch - C = 1 1597 | test_setup(); 1598 | reg.cc = 0x0B; 1599 | reg.pc = 0x0FFF; 1600 | mem[0x0FFF] = 0x01; 1601 | do_branch(BHI, false); 1602 | if (reg.pc == 0x0FFF) { 1603 | passes++; 1604 | } else { 1605 | errors++; 1606 | expected(0x0FFF, reg.pc); 1607 | } 1608 | 1609 | // No branch - Z = 1 1610 | test_setup(); 1611 | reg.cc = 0x0E; 1612 | reg.pc = 0x0FFF; 1613 | mem[0x0FFF] = 0x01; 1614 | do_branch(BHI, false); 1615 | if (reg.pc == 0x0FFF) { 1616 | passes++; 1617 | } else { 1618 | errors++; 1619 | expected(0x0FFF, reg.pc); 1620 | } 1621 | 1622 | // No branch - Z = C = 1 1623 | test_setup(); 1624 | reg.cc = 0x0F; 1625 | reg.pc = 0x0FFF; 1626 | mem[0x0FFF] = 0x01; 1627 | do_branch(BHI, false); 1628 | if (reg.pc == 0x0FFF) { 1629 | passes++; 1630 | } else { 1631 | errors++; 1632 | expected(0x0FFF, reg.pc); 1633 | } 1634 | 1635 | // BHS 1636 | // Branch back 1637 | test_setup(); 1638 | reg.cc = 0x00; 1639 | reg.pc = 0x0FFF; 1640 | mem[0x0FFF] = 0xFF; 1641 | do_branch(BHS, false); 1642 | if (reg.pc == 0x0FFE) { 1643 | passes++; 1644 | } else { 1645 | errors++; 1646 | expected(0x0FFE, reg.pc); 1647 | } 1648 | 1649 | // Branch forward 1650 | test_setup(); 1651 | reg.cc = 0x00; 1652 | reg.pc = 0x0FFF; 1653 | mem[0x0FFF] = 0x01; 1654 | do_branch(BHS, false); 1655 | if (reg.pc == 0x1000) { 1656 | passes++; 1657 | } else { 1658 | errors++; 1659 | expected(0x1000, reg.pc); 1660 | } 1661 | 1662 | // No branch 1663 | test_setup(); 1664 | reg.cc = 0x01; 1665 | reg.pc = 0x0FFF; 1666 | mem[0x0FFF] = 0x01; 1667 | do_branch(BHS, false); 1668 | if (reg.pc == 0x0FFF) { 1669 | passes++; 1670 | } else { 1671 | errors++; 1672 | expected(0x0FFF, reg.pc); 1673 | } 1674 | 1675 | // BLE 1676 | // Branch back 1677 | test_setup(); 1678 | reg.cc = 0x0E; // z = 1, c = v 1679 | reg.pc = 0x0FFF; 1680 | mem[0x0FFF] = 0xFF; 1681 | do_branch(BLE, false); 1682 | if (reg.pc == 0x0FFE) { 1683 | passes++; 1684 | } else { 1685 | errors++; 1686 | expected(0x0FFE, reg.pc); 1687 | } 1688 | 1689 | // Branch forward 1690 | test_setup(); 1691 | reg.cc = 0x0C; // z = 0, c != v 1692 | reg.pc = 0x0FFF; 1693 | mem[0x0FFF] = 0x01; 1694 | do_branch(BLE, false); 1695 | if (reg.pc == 0x1000) { 1696 | passes++; 1697 | } else { 1698 | errors++; 1699 | expected(0x1000, reg.pc); 1700 | } 1701 | 1702 | // No branch 1703 | test_setup(); 1704 | reg.cc = 0x0A; // z = 0, c = v 1705 | reg.cc = 0x0A; 1706 | reg.pc = 0x0FFF; 1707 | mem[0x0FFF] = 0x01; 1708 | do_branch(BLE, false); 1709 | if (reg.pc == 0x0FFF) { 1710 | passes++; 1711 | } else { 1712 | errors++; 1713 | expected(0x0FFF, reg.pc); 1714 | } 1715 | 1716 | // BLO 1717 | // Branch back 1718 | test_setup(); 1719 | reg.cc = 0x01; 1720 | reg.pc = 0x0FFF; 1721 | mem[0x0FFF] = 0xFF; 1722 | do_branch(BLO, false); 1723 | if (reg.pc == 0x0FFE) { 1724 | passes++; 1725 | } else { 1726 | errors++; 1727 | expected(0x0FFE, reg.pc); 1728 | } 1729 | 1730 | // Branch forward 1731 | test_setup(); 1732 | reg.cc = 0x01; 1733 | reg.pc = 0x0FFF; 1734 | mem[0x0FFF] = 0x01; 1735 | do_branch(BLO, false); 1736 | if (reg.pc == 0x1000) { 1737 | passes++; 1738 | } else { 1739 | errors++; 1740 | expected(0x1000, reg.pc); 1741 | } 1742 | 1743 | // No branch 1744 | test_setup(); 1745 | reg.cc = 0x00; 1746 | reg.pc = 0x0FFF; 1747 | mem[0x0FFF] = 0x01; 1748 | do_branch(BLO, false); 1749 | if (reg.pc == 0x0FFF) { 1750 | passes++; 1751 | } else { 1752 | errors++; 1753 | expected(0x0FFF, reg.pc); 1754 | } 1755 | 1756 | // BLS 1757 | // Branch back 1758 | test_setup(); 1759 | reg.cc = 0x04; // z = 1, c = 0 1760 | reg.pc = 0x0FFF; 1761 | mem[0x0FFF] = 0xFF; 1762 | do_branch(BLS, false); 1763 | if (reg.pc == 0x0FFE) { 1764 | passes++; 1765 | } else { 1766 | errors++; 1767 | expected(0x0FFE, reg.pc); 1768 | } 1769 | 1770 | // Branch forward 1771 | test_setup(); 1772 | reg.cc = 0x01; // z = 0, c = 1 1773 | reg.pc = 0x0FFF; 1774 | mem[0x0FFF] = 0x01; 1775 | do_branch(BLS, false); 1776 | if (reg.pc == 0x1000) { 1777 | passes++; 1778 | } else { 1779 | errors++; 1780 | expected(0x1000, reg.pc); 1781 | } 1782 | 1783 | // No branch 1784 | test_setup(); 1785 | reg.cc = 0x00; // z = 0, c = 0 1786 | reg.pc = 0x0FFF; 1787 | mem[0x0FFF] = 0x01; 1788 | do_branch(BLS, false); 1789 | if (reg.pc == 0x0FFF) { 1790 | passes++; 1791 | } else { 1792 | errors++; 1793 | expected(0x0FFF, reg.pc); 1794 | } 1795 | 1796 | // BLT 1797 | // Branch back 1798 | test_setup(); 1799 | reg.cc = 0x08; // n = 1, v = 0 1800 | reg.pc = 0x0FFF; 1801 | mem[0x0FFF] = 0xFF; 1802 | do_branch(BLT, false); 1803 | if (reg.pc == 0x0FFE) { 1804 | passes++; 1805 | } else { 1806 | errors++; 1807 | expected(0x0FFE, reg.pc); 1808 | } 1809 | 1810 | // Branch forward 1811 | test_setup(); 1812 | reg.cc = 0x02; // n = 0, v = 1 1813 | reg.pc = 0x0FFF; 1814 | mem[0x0FFF] = 0x01; 1815 | do_branch(BLT, false); 1816 | if (reg.pc == 0x1000) { 1817 | passes++; 1818 | } else { 1819 | errors++; 1820 | expected(0x1000, reg.pc); 1821 | } 1822 | 1823 | // No branch 1824 | test_setup(); 1825 | reg.cc = 0x00; // n = 0, v = 0 1826 | reg.pc = 0x0FFF; 1827 | mem[0x0FFF] = 0x01; 1828 | do_branch(BLT, false); 1829 | if (reg.pc == 0x0FFF) { 1830 | passes++; 1831 | } else { 1832 | errors++; 1833 | expected(0x0FFF, reg.pc); 1834 | } 1835 | 1836 | // BMI 1837 | // Branch back 1838 | test_setup(); 1839 | reg.cc = 0x08; 1840 | reg.pc = 0x0FFF; 1841 | mem[0x0FFF] = 0xFF; 1842 | do_branch(BMI, false); 1843 | if (reg.pc == 0x0FFE) { 1844 | passes++; 1845 | } else { 1846 | errors++; 1847 | expected(0x0FFE, reg.pc); 1848 | } 1849 | 1850 | // Branch forward 1851 | test_setup(); 1852 | reg.cc = 0x08; 1853 | reg.pc = 0x0FFF; 1854 | mem[0x0FFF] = 0x01; 1855 | do_branch(BMI, false); 1856 | if (reg.pc == 0x1000) { 1857 | passes++; 1858 | } else { 1859 | errors++; 1860 | expected(0x1000, reg.pc); 1861 | } 1862 | 1863 | // No branch 1864 | test_setup(); 1865 | reg.cc = 0x00; 1866 | reg.pc = 0x0FFF; 1867 | mem[0x0FFF] = 0x01; 1868 | do_branch(BMI, false); 1869 | if (reg.pc == 0x0FFF) { 1870 | passes++; 1871 | } else { 1872 | errors++; 1873 | expected(0x0FFF, reg.pc); 1874 | } 1875 | 1876 | // BNE 1877 | // Branch back 1878 | test_setup(); 1879 | reg.cc = 0x0B; 1880 | reg.pc = 0x0FFF; 1881 | mem[0x0FFF] = 0xFF; 1882 | do_branch(BNE, false); 1883 | if (reg.pc == 0x0FFE) { 1884 | passes++; 1885 | } else { 1886 | errors++; 1887 | expected(0x0FFE, reg.pc); 1888 | } 1889 | 1890 | // Branch forward 1891 | test_setup(); 1892 | reg.cc = 0x00; 1893 | reg.pc = 0x0FFF; 1894 | mem[0x0FFF] = 0x01; 1895 | do_branch(BNE, false); 1896 | if (reg.pc == 0x1000) { 1897 | passes++; 1898 | } else { 1899 | errors++; 1900 | expected(0x1000, reg.pc); 1901 | } 1902 | 1903 | // No branch 1904 | test_setup(); 1905 | reg.cc = 0x0F; 1906 | reg.pc = 0x0FFF; 1907 | mem[0x0FFF] = 0x01; 1908 | do_branch(BNE, false); 1909 | if (reg.pc == 0x0FFF) { 1910 | passes++; 1911 | } else { 1912 | errors++; 1913 | expected(0x0FFF, reg.pc); 1914 | } 1915 | 1916 | // BPL 1917 | // Branch back 1918 | test_setup(); 1919 | reg.cc = 0x07; 1920 | reg.pc = 0x0FFF; 1921 | mem[0x0FFF] = 0xFF; 1922 | do_branch(BPL, false); 1923 | if (reg.pc == 0x0FFE) { 1924 | passes++; 1925 | } else { 1926 | errors++; 1927 | expected(0x0FFE, reg.pc); 1928 | } 1929 | 1930 | // Branch forward 1931 | test_setup(); 1932 | reg.cc = 0x00; 1933 | reg.pc = 0x0FFF; 1934 | mem[0x0FFF] = 0x01; 1935 | do_branch(BPL, false); 1936 | if (reg.pc == 0x1000) { 1937 | passes++; 1938 | } else { 1939 | errors++; 1940 | expected(0x1000, reg.pc); 1941 | } 1942 | 1943 | // No branch 1944 | test_setup(); 1945 | reg.cc = 0x0F; 1946 | reg.pc = 0x0FFF; 1947 | mem[0x0FFF] = 0x01; 1948 | do_branch(BPL, false); 1949 | if (reg.pc == 0x0FFF) { 1950 | passes++; 1951 | } else { 1952 | errors++; 1953 | expected(0x0FFF, reg.pc); 1954 | } 1955 | 1956 | // BRA 1957 | // Branch back 1958 | test_setup(); 1959 | reg.cc = 0xFF; 1960 | reg.pc = 0x0FFF; 1961 | mem[0x0FFF] = 0xFF; 1962 | do_branch(BRA, false); 1963 | if (reg.pc == 0x0FFE) { 1964 | passes++; 1965 | } else { 1966 | errors++; 1967 | expected(0x0FFE, reg.pc); 1968 | } 1969 | 1970 | // Branch forward 1971 | test_setup(); 1972 | reg.cc = 0x00; 1973 | reg.pc = 0x0FFF; 1974 | mem[0x0FFF] = 0x01; 1975 | do_branch(BRA, false); 1976 | if (reg.pc == 0x1000) { 1977 | passes++; 1978 | } else { 1979 | errors++; 1980 | expected(0x1000, reg.pc); 1981 | } 1982 | 1983 | // BRN 1984 | // Branch back 1985 | test_setup(); 1986 | reg.cc = 0xFF; 1987 | reg.pc = 0x0FFF; 1988 | mem[0x0FFF] = 0xFF; 1989 | do_branch(BRN, false); 1990 | if (reg.pc == 0x0FFF) { 1991 | passes++; 1992 | } else { 1993 | errors++; 1994 | expected(0x0FFF, reg.pc); 1995 | } 1996 | 1997 | // Branch forward 1998 | test_setup(); 1999 | reg.cc = 0x00; 2000 | reg.pc = 0x0FFF; 2001 | mem[0x0FFF] = 0x01; 2002 | do_branch(BRN, false); 2003 | if (reg.pc == 0x0FFF) { 2004 | passes++; 2005 | } else { 2006 | errors++; 2007 | expected(0x0FFF, reg.pc); 2008 | } 2009 | 2010 | // BVC 2011 | // Branch back 2012 | test_setup(); 2013 | reg.cc = 0x0D; 2014 | reg.pc = 0x0FFF; 2015 | mem[0x0FFF] = 0xFF; 2016 | do_branch(BVC, false); 2017 | if (reg.pc == 0x0FFE) { 2018 | passes++; 2019 | } else { 2020 | errors++; 2021 | expected(0x0FFE, reg.pc); 2022 | } 2023 | 2024 | // Branch forward 2025 | test_setup(); 2026 | reg.cc = 0x00; 2027 | reg.pc = 0x0FFF; 2028 | mem[0x0FFF] = 0x01; 2029 | do_branch(BVC, false); 2030 | if (reg.pc == 0x1000) { 2031 | passes++; 2032 | } else { 2033 | errors++; 2034 | expected(0x1000, reg.pc); 2035 | } 2036 | 2037 | // No branch 2038 | test_setup(); 2039 | reg.cc = 0x0F; 2040 | reg.pc = 0x0FFF; 2041 | mem[0x0FFF] = 0x01; 2042 | do_branch(BVC, false); 2043 | if (reg.pc == 0x0FFF) { 2044 | passes++; 2045 | } else { 2046 | errors++; 2047 | expected(0x0FFF, reg.pc); 2048 | } 2049 | 2050 | // BVS 2051 | // Branch back 2052 | test_setup(); 2053 | reg.cc = 0x0F; 2054 | reg.pc = 0x0FFF; 2055 | mem[0x0FFF] = 0xFF; 2056 | do_branch(BVS, false); 2057 | if (reg.pc == 0x0FFE) { 2058 | passes++; 2059 | } else { 2060 | errors++; 2061 | expected(0x0FFE, reg.pc); 2062 | } 2063 | 2064 | // Branch forward 2065 | test_setup(); 2066 | reg.cc = 0x02; 2067 | reg.pc = 0x0FFF; 2068 | mem[0x0FFF] = 0x01; 2069 | do_branch(BVS, false); 2070 | if (reg.pc == 0x1000) { 2071 | passes++; 2072 | } else { 2073 | errors++; 2074 | expected(0x1000, reg.pc); 2075 | } 2076 | 2077 | // No branch 2078 | test_setup(); 2079 | reg.cc = 0x0D; 2080 | reg.pc = 0x0FFF; 2081 | mem[0x0FFF] = 0x01; 2082 | do_branch(BVS, false); 2083 | if (reg.pc == 0x0FFF) { 2084 | passes++; 2085 | } else { 2086 | errors++; 2087 | expected(0x0FFF, reg.pc); 2088 | } 2089 | 2090 | test_report(5, errors - current_errors); 2091 | } 2092 | 2093 | 2094 | static void test_irqs(void) { 2095 | 2096 | uint16_t result; 2097 | uint32_t current_errors = errors; 2098 | 2099 | // FIRQ 2100 | test_setup(); 2101 | reg.pc = 0x00FF; 2102 | reg.cc = 0x80; 2103 | reg.s = 0x0FFF; 2104 | mem[0x00FF] = 0x0A; 2105 | process_interrupt(FIRQ_BIT); 2106 | if (reg.pc == (mem[FIRQ_VECTOR] << 8) | mem[FIRQ_VECTOR + 1] 2107 | && !is_bit_set(reg.cc, CC_E_BIT) 2108 | && reg.s == 0x0FFF - 3 2109 | && is_bit_set(reg.cc, CC_F_BIT) 2110 | && is_bit_set(reg.cc, CC_I_BIT)) { 2111 | passes++; 2112 | } else { 2113 | errors++; 2114 | } 2115 | 2116 | // IRQ 2117 | test_setup(); 2118 | reg.pc = 0x00FF; 2119 | reg.cc = 0x80; 2120 | reg.s = 24; 2121 | mem[0x00FF] = 0x0A; 2122 | process_interrupt(IRQ_BIT); 2123 | if (reg.pc == (mem[IRQ_VECTOR] << 8) | mem[IRQ_VECTOR + 1] 2124 | && is_bit_set(reg.cc, CC_E_BIT) 2125 | && reg.s == 12 2126 | && !is_bit_set(reg.cc, CC_F_BIT) 2127 | && is_bit_set(reg.cc, CC_I_BIT)) { 2128 | passes++; 2129 | } else { 2130 | errors++; 2131 | expected(12, reg.s); 2132 | } 2133 | 2134 | // NMI 2135 | test_setup(); 2136 | reg.pc = 0x00FF; 2137 | reg.cc = 0x80; 2138 | reg.s = 24; 2139 | mem[0x00FF] = 0x0A; 2140 | process_interrupt(NMI_BIT); 2141 | if (reg.pc == (mem[NMI_VECTOR] << 8) | mem[NMI_VECTOR + 1] 2142 | && is_bit_set(reg.cc, CC_E_BIT) 2143 | && reg.s == 12 2144 | && is_bit_set(reg.cc, CC_F_BIT) 2145 | && is_bit_set(reg.cc, CC_I_BIT)) { 2146 | passes++; 2147 | } else { 2148 | errors++; 2149 | expected(12, reg.s); 2150 | } 2151 | 2152 | test_report(6, errors - current_errors); 2153 | } 2154 | 2155 | 2156 | 2157 | static void test_setup(void) { 2158 | 2159 | tests++; 2160 | reset_registers(); 2161 | } 2162 | 2163 | 2164 | static void test_report(uint16_t code, uint32_t err_count) { 2165 | 2166 | uint32_t test_count = 7; 2167 | const char *names[test_count]; 2168 | names[0] = "Addressing"; 2169 | names[1] = "ALU"; 2170 | names[2] = "Index"; 2171 | names[3] = "Logic"; 2172 | names[4] = "Registers"; 2173 | names[5] = "Branch Ops"; 2174 | names[6] = "IRQs"; 2175 | 2176 | if (code < test_count) { 2177 | printf("%02d errors in %s (see above). Test count: %03d\n", err_count, names[code], tests); 2178 | } 2179 | } 2180 | 2181 | 2182 | static void expected(uint16_t wanted, uint16_t got) { 2183 | 2184 | printf(" %02d. Expected %04X got %04X\n", tests, wanted, got); 2185 | } 2186 | 2187 | 2188 | --------------------------------------------------------------------------------