From b24388e9e54bf46a15d6902e975cdd9824f0c416 Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 16 Apr 2026 20:43:00 +0200 Subject: [PATCH 1/9] Load static binary assets from PREFIX tree Instead of loading assets from a relative directory which depends on the current working directory, use the PREFIXed path like every good Unix software. --- src/bios.c | 3 ++- src/main.c | 1 + src/video.c | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/bios.c b/src/bios.c index 841cacf..50b9fb7 100644 --- a/src/bios.c +++ b/src/bios.c @@ -10,9 +10,10 @@ #include #include +#define LOG_LEVEL LOG_LVL_DEBUG #include "log.h" -#define ROM_BIOS_PATH "rom/V1.01_ROM.bin" +#define ROM_BIOS_PATH CEDA_PREFIX "/share/ceda/v1.01_rom.bin" #define ROM_BIOS_SIZE (ceda_size_t)(4 * KiB) static uint8_t bios[ROM_BIOS_SIZE] = {0}; diff --git a/src/main.c b/src/main.c index f6dbf34..6889d0e 100644 --- a/src/main.c +++ b/src/main.c @@ -7,6 +7,7 @@ int main(int argc, char *argv[]) { int ret = 0; LOG_INFO("CEDA Emulator\n"); + LOG_INFO("prefix = %s\n", CEDA_PREFIX); (void)argc; (void)argv; diff --git a/src/video.c b/src/video.c index a7d9c19..cf26042 100644 --- a/src/video.c +++ b/src/video.c @@ -32,9 +32,9 @@ #define CRT_PIXEL_WIDTH 640 #define CRT_PIXEL_HEIGHT 400 -#define CHAR_ROM_PATH "rom/CGV7.2_ROM.bin" +#define CHAR_ROM_PATH CEDA_PREFIX "/share/ceda/cgv7.2_rom.bin" #define CHAR_ROM_SIZE (ceda_size_t)(4 * KiB) -#define CGE_ROM_PATH "rom/CGE.bin" +#define CGE_ROM_PATH CEDA_PREFIX "/share/ceda/cge2412.bin" #define CGE_ROM_SIZE (ceda_size_t)(4 * KiB) #define UPDATE_INTERVAL 20000 // [us] 20 ms => 50 Hz From 0f7a5813bda142d7ddc7dafcfafc1dde9d626927 Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 16 Apr 2026 21:07:11 +0200 Subject: [PATCH 2/9] Add .template suffix for ceda-cemu.ini example configuration file This action has the purpose of making it more clear that this is only a template file which is not directly used by the emulator, unless the user edits it as their liking and puts it in the proper location. --- conf/{ceda-cemu.ini => ceda-cemu.ini.template} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename conf/{ceda-cemu.ini => ceda-cemu.ini.template} (100%) diff --git a/conf/ceda-cemu.ini b/conf/ceda-cemu.ini.template similarity index 100% rename from conf/ceda-cemu.ini rename to conf/ceda-cemu.ini.template From 8a408b3e2e0efcd03c0187855af22ce62730541a Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 16 Apr 2026 20:43:48 +0200 Subject: [PATCH 3/9] CMakeLists.txt: define PREFIX and install assets in PREFIX dir --- CMakeLists.txt | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 377a308..0c40666 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,8 @@ set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}" "--use-color" "--extra-arg=-Wno-unkno set_source_files_properties(src/3rd/disassembler.c PROPERTIES COMPILE_FLAGS -Wno-discarded-qualifiers) +add_compile_definitions(CEDA_PREFIX="${CMAKE_INSTALL_PREFIX}") + # Automatically add targets, with same properties function(add_ceda_target target) @@ -121,3 +123,56 @@ target_sources(ceda-test ${TEST_SRCS} ) +# External binary blobs / assets (eg. ROM and operating system disks) +# ------------------------------------------------------------------- +include(ExternalProject) +ExternalProject_Add(asset_rom + URL https://github.com/GLGPrograms/ceda-home/raw/refs/heads/main/assets/v1.01_rom.bin + URL_HASH SHA256=e6c83290a92268a01a97469b568e770fc154ee435503c0c3b313bed61a5f820e + DOWNLOAD_NO_EXTRACT True + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/asset_rom-prefix/src/v1.01_rom.bin + ${CMAKE_BINARY_DIR}/asset_rom-prefix/v1.01_rom.bin +) +ExternalProject_Add(asset_char + URL https://github.com/GLGPrograms/ceda-home/raw/refs/heads/main/assets/cgv7.2_rom.bin + URL_HASH SHA256=4d52b5f43ab5ade0f37e6d2aca0d8cdfae6fcdf4598640a727dd6cf3e1c450b4 + DOWNLOAD_NO_EXTRACT True + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/asset_char-prefix/src/cgv7.2_rom.bin + ${CMAKE_BINARY_DIR}/asset_char-prefix/cgv7.2_rom.bin +) +ExternalProject_Add(asset_disk + URL https://github.com/GLGPrograms/ceda-cpm/releases/download/cpm22-vers1.7/SANCO-CPM22_us.bin + URL_HASH SHA256=db9e29115690a158c9a18088eb3ee348c28eadad3f533623af9e7733949adb9f + DOWNLOAD_NO_EXTRACT True + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/asset_disk-prefix/src/SANCO-CPM22_us.bin + ${CMAKE_BINARY_DIR}/asset_disk-prefix/SANCO-CPM22_us.bin +) + +# Installation +# ------------ +install(TARGETS ceda + RUNTIME + DESTINATION bin + COMPONENT application +) +install(FILES ${CMAKE_SOURCE_DIR}/conf/ceda-cemu.ini.template DESTINATION share/ceda) +install(FILES ${CMAKE_BINARY_DIR}/asset_rom-prefix/v1.01_rom.bin DESTINATION share/ceda) +install(FILES ${CMAKE_BINARY_DIR}/asset_char-prefix/cgv7.2_rom.bin DESTINATION share/ceda) +install(FILES ${CMAKE_BINARY_DIR}/asset_disk-prefix/SANCO-CPM22_us.bin DESTINATION share/ceda) + +# Packaging +# --------- +set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) +set(CPACK_COMPONENTS_ALL application) +include(CPack) + + From 74e05a8a81022b7e42dada3e698ff4dce98b821d Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 16 Apr 2026 21:34:41 +0200 Subject: [PATCH 4/9] Update README.md --- README.md | 61 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 8225081..483e5ef 100644 --- a/README.md +++ b/README.md @@ -8,43 +8,60 @@ This software is released under the terms of the GNU GPLv3 license. ![sample screenshot](img/ceda.gif) ## Build + +Make sure to specify an user-writable prefix path to install the software and the binary blobs. +Note: this command will also download the needed binary blobs. + ``` git submodule init git submodule update -script/docker script/build +cmake -B build -DCMAKE_INSTALL_PREFIX=$(pwd)/root +make -C build install ``` -## Run -First, you need a copy of the ROMs inside the `rom/` directory: -- [BIOS ROM](https://github.com/GLGPrograms/ceda-home/blob/main/README.md#rom) -- [character ROM](https://github.com/GLGPrograms/ceda-home/blob/main/README.md#rom) +## Quick start +``` +root/bin/ceda # run the emulator +``` +Press the `BOOT` key to boot. +Note: since your keyboard probably does not have a `BOOT` key, use `INS` instead :smile: -Optionally (but strongly suggested) you also need: -- [CP/M disk](https://github.com/GLGPrograms/ceda-cpm) (.bin) +### Also useful -Then: +You can connect to the emulator command line interface via: ``` -build/release/ceda # run the emulator -telnet 127.0.0.1 52954 # connect to command line interface +telnet 127.0.0.1 52954 # (52954 is 0xCEDA) ``` -Lore: *52954* is just the decimal version of `0xCEDA`. +- The provided command line allows the user to thinker with the emulated software + - mount/umount disk images + - start/stop emulation, pause, add/delete exec/mem read/mem write/io in/io out breakpoints, step, disassemble + - read/write memory, load/save chunks to disk + - emulate read/write in I/O space, trigger interrupts manually +- Enter `help` in the command line to get a full list of available commands -Emulation can be started/stopped/resumed via the provided command line debugger accessible via the telnet session. +## Binary blobs +These blobs are needed if you want the emulator to do something useful out of the box. +They are now automatically downloaded and installed as part of the CMake build. -In the command line, type: -- `mount ` to mount the CP/M floppy image (optional); -- `continue` to start the execution; -- `help` to get a full list of all supported commands; - -To emulate the `BOOT` key of the original keyboard, press `INS`. +- [BIOS ROM](https://github.com/GLGPrograms/ceda-home/blob/main/README.md#rom) +- [character ROM](https://github.com/GLGPrograms/ceda-home/blob/main/README.md#rom) +- [CP/M disk](https://github.com/GLGPrograms/ceda-cpm/releases) (choose your keymap) ## Development -The `script/` directory contains some useful script for development, mainly `script/build` and `script/test`. -It is suggested to run them in the docker container by prefixing them with `script/docker` in order to use the correct version of the dev tools. +The `script/` directory contains some useful script for development, use them as quick shortcuts. +You can run them in the official build container in order to use the right version of the dev tools. + +Example: `script/docker script/build` + +- `script/build`: build everything, both release and debug mode +- `script/coverage`: generate test coverage report +- `script/docker`: run command in the build container +- `script/format`: format source code +- `script/test`: run tests +- `script/valgrind`: check for memory leaks / hunt bugs / sanitize memory access -- `format`: clang-format sources -- `valgrind`: check for memory leaks +YMMV: using these scripts directly might be a pleasant or rough experience, depending on your distribution. ## About This emulator is part of a documentation effort by [Retrofficina GLG Programs](https://retrofficina.glgprograms.it/). From f2b61f8e62a88146004715657a9b0eeff5d5e2f7 Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 16 Apr 2026 21:06:55 +0200 Subject: [PATCH 5/9] Disable configuration loading from current working directory. We do not want the software to load stuff depending on the current working directory anymore, to better embed it in the standard file system tree. --- src/conf.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/conf.c b/src/conf.c index 588f8d2..ccbb3b1 100644 --- a/src/conf.c +++ b/src/conf.c @@ -14,7 +14,6 @@ #include #include -static const char *CONF_PATH_CWD = "./ceda-cemu.ini"; static const char *CONF_PATH_HOME = "/.config/it.glgprograms.retrofficina/ceda-cemu.ini"; @@ -132,18 +131,12 @@ void conf_init(void) { const char *loaded_path = NULL; char path[CONF_PATH_SIZE]; - // load ini from local working directory - if (ini_parse(CONF_PATH_CWD, conf_handler, conf_tuples) >= 0) - loaded_path = CONF_PATH_CWD; - - // load ini from user home, if not in local directory - if (loaded_path == NULL) { - const char *home = getenv("HOME"); - if (home) { - (void)snprintf(path, CONF_PATH_SIZE, "%s/%s", home, CONF_PATH_HOME); - if (ini_parse(path, conf_handler, conf_tuples) >= 0) - loaded_path = path; - } + // load ini from user home + const char *home = getenv("HOME"); + if (home) { + (void)snprintf(path, CONF_PATH_SIZE, "%s/%s", home, CONF_PATH_HOME); + if (ini_parse(path, conf_handler, conf_tuples) >= 0) + loaded_path = path; } if (loaded_path) From 911db421a560f1a0165fd29f3648b2903343d8c8 Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 16 Apr 2026 21:21:45 +0200 Subject: [PATCH 6/9] CMakeLists.txt: exclude dependency output from install targets When adding the Z80 dependency by using add_subdirectory(), it also brings its install targets in scope. Thus, when using `make install` on the ceda-cemu project, this also installs the library's output in the target root directory, which we do not want. This commit fixes this behaviour by normally excluding the Z80 library's targets if not strictly needed. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c40666..8918fcf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,7 @@ include_directories(vendor/inih) include_directories(vendor/Z80/API) set(Z80_SHARED_LIBS NO CACHE BOOL "") -add_subdirectory(vendor/Z80) +add_subdirectory(vendor/Z80 EXCLUDE_FROM_ALL) # Global properties and settings set(CMAKE_C_STANDARD 17) From b11a60aee5f6449c402df6edf3f0f0c5cf700bfa Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 16 Apr 2026 21:59:05 +0200 Subject: [PATCH 7/9] Emulate KB_SEL/JP3 to echo back characters sent to keyboard --- src/keyboard.c | 10 ++++++++++ src/keyboard.h | 2 ++ src/sio2.c | 1 + 3 files changed, 13 insertions(+) diff --git a/src/keyboard.c b/src/keyboard.c index b64da53..fadca78 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -249,3 +249,13 @@ bool keyboard_getChar(uint8_t *c) { *c = FIFO_POP(&keyboard_serial_fifo); return true; } + +// Emulates JP3 which shorts together KBD_RX and KBD_TX +// and is normally closed on the main board +bool keyboard_putChar(uint8_t c) { + if (FIFO_ISFULL(&keyboard_serial_fifo)) + return false; + + FIFO_PUSH(&keyboard_serial_fifo, c); + return true; +} diff --git a/src/keyboard.h b/src/keyboard.h index ccace2b..3fa33ea 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -10,4 +10,6 @@ void keyboard_handleEvent(const SDL_KeyboardEvent *event); bool keyboard_getChar(uint8_t *c); +bool keyboard_putChar(uint8_t c); + #endif // CEDA_KEYBOARD_H diff --git a/src/sio2.c b/src/sio2.c index f937e64..0090517 100644 --- a/src/sio2.c +++ b/src/sio2.c @@ -438,6 +438,7 @@ static bool sio2_restart(void) { // attach keyboard to channel B channels[SIO_CHANNEL_B].getc = keyboard_getChar; + channels[SIO_CHANNEL_B].putc = keyboard_putChar; return true; } From be3b6d5bf6541222c3d73322d7a4a9eebd40ecdb Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 16 Apr 2026 22:14:36 +0200 Subject: [PATCH 8/9] Log floppy mount for better user experience --- src/floppy.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/floppy.c b/src/floppy.c index 2549ec0..be798c5 100644 --- a/src/floppy.c +++ b/src/floppy.c @@ -10,6 +10,9 @@ #include "fdc.h" #include "macro.h" +#define LOG_LEVEL LOG_LVL_INFO +#include "log.h" + #define CFF_MAXIMUM_TRACKS (80U) #define CFF_SECTOR_SIZE (1024U) #define CFF_MAX_SECTORS (5U) @@ -57,13 +60,16 @@ ssize_t floppy_load_image(const char *filename, unsigned int unit_number) { // TODO(giuliof): if extension is ..., then image format is ... FILE *fd = fopen(filename, "rb+"); - if (fd == NULL) + if (fd == NULL) { + LOG_WARN("floppy: unable to mount %s\n", filename); return -1; + } floppy_units[unit_number].fd = fd; fdc_kickDiskImage(floppy_read_buffer, floppy_write_buffer); + LOG_INFO("floppy: mounted %s\n", filename); return 0; } From 71d893c1a25038d8585873c1e4d002bc253ffbf1 Mon Sep 17 00:00:00 2001 From: giomba Date: Thu, 16 Apr 2026 22:15:15 +0200 Subject: [PATCH 9/9] Automatically load the standard operating system disk image from PREFIX --- src/ceda.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ceda.c b/src/ceda.c index 123829d..c04d181 100644 --- a/src/ceda.c +++ b/src/ceda.c @@ -7,6 +7,7 @@ #include "conf.h" #include "cpu.h" #include "fdc.h" +#include "floppy.h" #include "gui.h" #include "int.h" #include "limits.h" @@ -154,6 +155,9 @@ int ceda_run(void) { goto err; } + // mount operating system disk (if any), and ignore errors + floppy_load_image(CEDA_PREFIX "/share/ceda/SANCO-CPM22_us.bin", 0); + cpu_pause(false); // main loop