Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 56 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

Expand Down Expand Up @@ -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)


61 changes: 39 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <image.bin>` 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/).
Expand Down
File renamed without changes.
3 changes: 2 additions & 1 deletion src/bios.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
#include <stdlib.h>
#include <string.h>

#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};
Expand Down
4 changes: 4 additions & 0 deletions src/ceda.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
19 changes: 6 additions & 13 deletions src/conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include <stdlib.h>
#include <string.h>

static const char *CONF_PATH_CWD = "./ceda-cemu.ini";
static const char *CONF_PATH_HOME =
"/.config/it.glgprograms.retrofficina/ceda-cemu.ini";

Expand Down Expand Up @@ -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)
Expand Down
8 changes: 7 additions & 1 deletion src/floppy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}

Expand Down
10 changes: 10 additions & 0 deletions src/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions src/keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/sio2.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions src/video.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading