diff --git a/CMakeLists.txt b/CMakeLists.txt index 377a308..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) @@ -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) + + 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/). 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 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/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 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) 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; } 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/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/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; } 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