├── 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 | [](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 | [](https://www.redbubble.com/i/t-shirt/Dragon-Data-Logo-by-squinter-mac/83509782.IJ6L0) [](https://www.redbubble.com/i/t-shirt/Dragon-Data-Logo-Slim-by-squinter-mac/83509973.IJ6L0) [](https://www.redbubble.com/i/t-shirt/Dragon-Boot-Message-Reverse-Video-by-squinter-mac/46847689.IJ6L0) [](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 | 
43 |
44 | The Monitor code is menu driven. It provides code entry, program execution and memory inspection functions.
45 |
46 | ### Main Menu
47 |
48 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------