From e1b7d906357d04973b22a6021ff0db8d73d577b9 Mon Sep 17 00:00:00 2001 From: giomba Date: Wed, 7 May 2025 20:21:12 +0200 Subject: [PATCH 1/5] Add mock CharMon (character-based monitor) peripheral. This fake peripheral can be used to print to the emulator's stdout from the emulated software. --- CMakeLists.txt | 1 + src/charmon.c | 26 ++++++++++++++++++++++++++ src/charmon.h | 10 ++++++++++ src/conf.c | 2 ++ 4 files changed, 39 insertions(+) create mode 100644 src/charmon.c create mode 100644 src/charmon.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a9e075..0c63e66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ set(CORE_SRCS src/int.c src/keyboard.c src/main.c + src/charmon.c src/serial.c src/sio2.c src/speaker.c diff --git a/src/charmon.c b/src/charmon.c new file mode 100644 index 0000000..fcb4a4c --- /dev/null +++ b/src/charmon.c @@ -0,0 +1,26 @@ +#include "charmon.h" + +#include "conf.h" +#include "ubus.h" + +#include +#include + +#define CHARMON_BASE (0xF0) + +void charmon_out(ceda_ioaddr_t address, uint8_t value) { + (void)address; + (void)putc(value, stdout); +} + +void charmon_init(CEDAModule *mod) { + memset(mod, 0, sizeof(*mod)); + + bool *conf_installed = conf_getBool("mod", "charmon_installed"); + bool installed = conf_installed ? *conf_installed : false; + + if (!installed) + return; + + ubus_register(CHARMON_BASE, CHARMON_BASE + 1, NULL, charmon_out); +} diff --git a/src/charmon.h b/src/charmon.h new file mode 100644 index 0000000..a899731 --- /dev/null +++ b/src/charmon.h @@ -0,0 +1,10 @@ +#ifndef CEDA_CHAR_MONITOR_H +#define CEDA_CHAR_MONITOR_H + +#include "module.h" +#include "type.h" + +void charmon_init(CEDAModule *mod); +void charmon_out(ceda_ioaddr_t address, uint8_t value); + +#endif // CEDA_CHAR_MONITOR_H diff --git a/src/conf.c b/src/conf.c index 7be4288..1127e0c 100644 --- a/src/conf.c +++ b/src/conf.c @@ -23,6 +23,7 @@ static const char *CONF_PATH_HOME = // Emulator dynamic configuration static struct { bool cge_installed; + bool charmon_installed; ceda_string_t *bios_rom_path; ceda_string_t *char_rom_path; ceda_string_t *cge_rom_path; @@ -46,6 +47,7 @@ typedef struct conf_tuple_t { static conf_tuple_t conf_tuples[] = { {"mod", "cge_installed", CONF_BOOL, &conf.cge_installed}, + {"mod", "charmon_installed", CONF_BOOL, &conf.charmon_installed}, {"path", "bios_rom", CONF_STR, &conf.bios_rom_path}, {"path", "char_rom", CONF_STR, &conf.char_rom_path}, {"path", "cge_rom", CONF_STR, &conf.cge_rom_path}, From 2b0d56b95c93ba386a335fbf7874aeeffb71701b Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 21 Aug 2025 22:07:49 +0200 Subject: [PATCH 2/5] Add "User Bus" module. This new module allows the connection of custom peripherals to the computer bus. This module can be used to implement user's mods. --- CMakeLists.txt | 1 + src/ubus.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/ubus.h | 37 +++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 src/ubus.c create mode 100644 src/ubus.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c63e66..953500c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ set(CORE_SRCS src/time.c src/timer.c src/tokenizer.c + src/ubus.c src/upd8255.c src/video.c src/ram/auxram.c diff --git a/src/ubus.c b/src/ubus.c new file mode 100644 index 0000000..8e5bcf0 --- /dev/null +++ b/src/ubus.c @@ -0,0 +1,88 @@ +#include "ubus.h" + +#include "conf.h" + +#include +#include + +#define UBUS_MAX_PERIPHERALS (4) + +#define LOG_LEVEL LOG_LVL_INFO +#include "log.h" + +struct ubus_io_slot { + ceda_ioaddr_t base; + uint32_t top; + ubus_io_read_t in; + ubus_io_write_t out; +}; + +static struct ubus_io_slot ubus_slots[UBUS_MAX_PERIPHERALS]; +static size_t ubus_used = 0; + +void ubus_init(CEDAModule *mod) { + memset(mod, 0, sizeof(*mod)); + + mod->init = ubus_init; +} + +bool ubus_register(ceda_ioaddr_t base, uint32_t top, ubus_io_read_t read, + ubus_io_write_t write) { + if (!read && !write) + return false; + + if (top > 0x100) + return false; + + if (top <= base) + return false; + + if (ubus_used == UBUS_MAX_PERIPHERALS) { + LOG_WARN("Too many peripherals registered\n"); + return false; + } + + // check if peripherals are overlapping + for (size_t i = 0; i < ubus_used; ++i) { + struct ubus_io_slot *slot = &ubus_slots[i]; + if (slot->base >= base && slot->base < top) + return false; + if (slot->top <= top && slot->top > base) + return false; + if (slot->base < base && slot->top > top) + return false; + if (slot->base > base && slot->top < top) + return false; + } + + ubus_slots[ubus_used].base = base; + ubus_slots[ubus_used].top = top; + ubus_slots[ubus_used].in = read; + ubus_slots[ubus_used].out = write; + ubus_used += 1; + + LOG_INFO("Registered peripheral at %02x\n", (unsigned int)base); + + return true; +} + +zuint8 ubus_io_in(ceda_ioaddr_t address) { + for (size_t i = 0; i < ubus_used; ++i) { + struct ubus_io_slot *slot = &ubus_slots[i]; + if (address >= slot->base && address < slot->top) + if (slot->in) + return slot->in(address - slot->base); + } + return 0; +} + +void ubus_io_out(ceda_ioaddr_t address, uint8_t value) { + for (size_t i = 0; i < ubus_used; ++i) { + struct ubus_io_slot *slot = &ubus_slots[i]; + if (address >= slot->base && address < slot->top) + if (slot->out) { + slot->out(address - slot->base, value); + return; + } + } +} diff --git a/src/ubus.h b/src/ubus.h new file mode 100644 index 0000000..7b89014 --- /dev/null +++ b/src/ubus.h @@ -0,0 +1,37 @@ +#ifndef CEDA_USER_BUS_H +#define CEDA_USER_BUS_H + +#include "module.h" +#include "type.h" + +#include + +#include + +typedef uint8_t (*ubus_io_read_t)(ceda_ioaddr_t address); +typedef void (*ubus_io_write_t)(ceda_ioaddr_t address, uint8_t value); + +/** + * @brief Initialize the User Bus module. + * + * This module allows users to connect custom peripherals to the computer bus. + */ +void ubus_init(CEDAModule *mod); + +/** + * @brief Register a peripheral on the bus. + * + * @param base Peripheral base address. + * @param top Peripheral top address + 1 (eg. the first unused address) + * @param read IO input callback. + * @param write IO output callback. + * + * @return true in case of success, false otherwise. + */ +bool ubus_register(ceda_ioaddr_t base, uint32_t top, ubus_io_read_t read, + ubus_io_write_t write); + +zuint8 ubus_io_in(ceda_ioaddr_t address); +void ubus_io_out(ceda_ioaddr_t address, uint8_t value); + +#endif // CEDA_USER_BUS_H From 3e1a3a064e51d039f01990b816ed0386c201e99e Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 21 Aug 2025 23:02:11 +0200 Subject: [PATCH 3/5] Add support for custom peripherals bus (UserBus) --- src/bus.c | 5 ++++- src/ceda.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/bus.c b/src/bus.c index 9715ab3..b554b64 100644 --- a/src/bus.c +++ b/src/bus.c @@ -10,6 +10,7 @@ #include "sio2.h" #include "speaker.h" #include "timer.h" +#include "ubus.h" #include "upd8255.h" #include "video.h" @@ -120,7 +121,7 @@ uint8_t bus_io_in(ceda_ioaddr_t address) { } } - return 0; + return ubus_io_in(address); } void bus_io_out(ceda_ioaddr_t _address, uint8_t value) { @@ -136,6 +137,8 @@ void bus_io_out(ceda_ioaddr_t _address, uint8_t value) { } } } + + ubus_io_out(address, value); } void bus_init(CEDAModule *mod) { diff --git a/src/ceda.c b/src/ceda.c index 1a0d3e7..c6f68ad 100644 --- a/src/ceda.c +++ b/src/ceda.c @@ -1,5 +1,6 @@ #include "ceda.h" +// computer core #include "bios.h" #include "bus.h" #include "cli.h" @@ -14,9 +15,13 @@ #include "serial.h" #include "sio2.h" #include "speaker.h" +#include "ubus.h" #include "upd8255.h" #include "video.h" +// user peripherals +#include "charmon.h" + #include #include @@ -32,10 +37,12 @@ static CEDAModule mod_speaker; static CEDAModule mod_sio2; static CEDAModule mod_int; static CEDAModule mod_serial; +static CEDAModule mod_ubus; +static CEDAModule mod_charmon; static CEDAModule *modules[] = { - &mod_bios, &mod_cli, &mod_gui, &mod_bus, &mod_cpu, - &mod_video, &mod_speaker, &mod_int, &mod_serial, &mod_sio2, + &mod_bios, &mod_cli, &mod_gui, &mod_bus, &mod_cpu, &mod_video, + &mod_speaker, &mod_int, &mod_serial, &mod_sio2, &mod_ubus, &mod_charmon, }; void ceda_init(void) { @@ -48,6 +55,8 @@ void ceda_init(void) { rom_bios_init(&mod_bios); video_init(&mod_video); speaker_init(&mod_speaker); + ubus_init(&mod_ubus); + charmon_init(&mod_charmon); bus_init(&mod_bus); cpu_init(&mod_cpu); int_init(&mod_int); From 5acad2c64873803a162703f7a0f2e72e7fb66ee5 Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 21 Aug 2025 23:17:16 +0200 Subject: [PATCH 4/5] Add example configuration to enable the CharMon device. --- conf/ceda-cemu.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conf/ceda-cemu.ini b/conf/ceda-cemu.ini index aa9769f..9923f86 100644 --- a/conf/ceda-cemu.ini +++ b/conf/ceda-cemu.ini @@ -15,6 +15,10 @@ # See: https://github.com/GLGPrograms/ceda-img cge_installed = true +# Character Device Monitor, write-only +# Virtual peripheral to log via stdout on the emulator +charmon_installed = false + [path] # Custom path for BIOS ROM, else default is used From 3206f2ce76eb9efd681a22dc2855588357aebe53 Mon Sep 17 00:00:00 2001 From: giomba Date: Mon, 25 Aug 2025 20:59:33 +0200 Subject: [PATCH 5/5] Reverse logic for bus peripherals overlapping check. --- src/ubus.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ubus.c b/src/ubus.c index 8e5bcf0..faef5d9 100644 --- a/src/ubus.c +++ b/src/ubus.c @@ -45,13 +45,8 @@ bool ubus_register(ceda_ioaddr_t base, uint32_t top, ubus_io_read_t read, // check if peripherals are overlapping for (size_t i = 0; i < ubus_used; ++i) { struct ubus_io_slot *slot = &ubus_slots[i]; - if (slot->base >= base && slot->base < top) - return false; - if (slot->top <= top && slot->top > base) - return false; - if (slot->base < base && slot->top > top) - return false; - if (slot->base > base && slot->top < top) + bool ok = (top <= slot->base) || (base >= slot->top); + if (!ok) return false; }