diff --git a/src/bus.c b/src/bus.c index b554b64..3a147b3 100644 --- a/src/bus.c +++ b/src/bus.c @@ -141,7 +141,7 @@ void bus_io_out(ceda_ioaddr_t _address, uint8_t value) { ubus_io_out(address, value); } -void bus_init(CEDAModule *mod) { +static void bus_prepareFirstAccess(void) { // when starting, BIOS ROM is mounted at 0x0, // until the first I/O access is performed, // but we'll just emulate this behaviour with an equivalent @@ -150,8 +150,19 @@ void bus_init(CEDAModule *mod) { for (uint8_t address = 0; address < (uint8_t)ARRAY_SIZE(jmp); ++address) { dyn_ram_write(address, jmp[address]); } +} + +static bool bus_restart(void) { + is_mem_switched = false; + bus_prepareFirstAccess(); + return true; +} +void bus_init(CEDAModule *mod) { memset(mod, 0, sizeof(*mod)); + mod->restart = bus_restart; + + bus_prepareFirstAccess(); } void bus_memSwitch(bool switched) { diff --git a/src/ceda.c b/src/ceda.c index c6f68ad..84b57d4 100644 --- a/src/ceda.c +++ b/src/ceda.c @@ -15,6 +15,7 @@ #include "serial.h" #include "sio2.h" #include "speaker.h" +#include "timer.h" #include "ubus.h" #include "upd8255.h" #include "video.h" @@ -30,19 +31,23 @@ static CEDAModule mod_bios; static CEDAModule mod_bus; static CEDAModule mod_cpu; +static CEDAModule mod_charmon; static CEDAModule mod_cli; +static CEDAModule mod_fdc; static CEDAModule mod_gui; -static CEDAModule mod_video; -static CEDAModule mod_speaker; -static CEDAModule mod_sio2; static CEDAModule mod_int; static CEDAModule mod_serial; +static CEDAModule mod_speaker; +static CEDAModule mod_sio2; +static CEDAModule mod_timer; static CEDAModule mod_ubus; -static CEDAModule mod_charmon; +static CEDAModule mod_upd8255; +static CEDAModule mod_video; static CEDAModule *modules[] = { - &mod_bios, &mod_cli, &mod_gui, &mod_bus, &mod_cpu, &mod_video, - &mod_speaker, &mod_int, &mod_serial, &mod_sio2, &mod_ubus, &mod_charmon, + &mod_bios, &mod_cli, &mod_gui, &mod_bus, &mod_cpu, + &mod_video, &mod_speaker, &mod_int, &mod_timer, &mod_serial, + &mod_sio2, &mod_upd8255, &mod_fdc, &mod_ubus, &mod_charmon, }; void ceda_init(void) { @@ -50,8 +55,8 @@ void ceda_init(void) { cli_init(&mod_cli); gui_init(&mod_gui); - fdc_init(); - upd8255_init(); + fdc_init(&mod_fdc); + upd8255_init(&mod_upd8255); rom_bios_init(&mod_bios); video_init(&mod_video); speaker_init(&mod_speaker); @@ -60,6 +65,7 @@ void ceda_init(void) { bus_init(&mod_bus); cpu_init(&mod_cpu); int_init(&mod_int); + timer_init(&mod_timer); serial_init(&mod_serial); sio2_init(&mod_sio2); } @@ -112,6 +118,21 @@ static void ceda_performance(void) { } } +static bool ceda_restart(void) { + bool ret = false; + for (unsigned int i = 0; i < ARRAY_SIZE(modules); ++i) { + bool (*restart)(void) = modules[i]->restart; + if (!restart) + continue; + ret = restart(); + if (!ret) { + LOG_ERR("module %u: restart error\n", i); + break; + } + } + return ret; +} + static void ceda_cleanup(void) { for (int i = ARRAY_SIZE(modules) - 1; i >= 0; --i) { void (*cleanup)(void) = modules[i]->cleanup; @@ -140,6 +161,10 @@ int ceda_run(void) { if (gui_isQuit() || cli_isQuit()) { break; } + if (cli_checkRestart()) { + if (!ceda_restart()) + break; + } // check for how long each module can sleep, and yield host cpu ceda_remaining(); diff --git a/src/cli.c b/src/cli.c index afcdd8d..330cd9f 100644 --- a/src/cli.c +++ b/src/cli.c @@ -37,6 +37,7 @@ enum { BLOCK_BUFFER_SIZE = 4096 }; // big page-like stuff static bool initialized = false; static bool quit = false; +static bool restart = false; static const us_interval_t UPDATE_INTERVAL = 20000; // [us] 20 ms => 50 Hz static us_time_t last_update = 0; // last poll() call @@ -50,6 +51,12 @@ bool cli_isQuit(void) { return quit; } +bool cli_checkRestart(void) { + bool prev = restart; + restart = false; + return prev; +} + /** * @brief Send a string to the connected client. * @@ -696,6 +703,12 @@ static ceda_string_t *cli_int(const char *arg) { return NULL; } +static ceda_string_t *cli_reset(const char *arg) { + (void)arg; + restart = true; + return NULL; +} + static ceda_string_t *cli_in(const char *arg) { char word[LINE_BUFFER_SIZE]; ceda_string_t *msg = ceda_string_new(0); @@ -815,6 +828,7 @@ static const cli_command cli_commands[] = { {"step", "step one instruction", cli_step}, {"goto", "override cpu program counter", cli_goto}, {"int", "trigger interrupt request", cli_int}, + {"reset", "perform an hard reset", cli_reset}, {"read", "read from memory", cli_read}, {"write", "write to memory", cli_write}, {"in", "read from io", cli_in}, diff --git a/src/cli.h b/src/cli.h index 5710adb..f03f743 100644 --- a/src/cli.h +++ b/src/cli.h @@ -9,4 +9,13 @@ void cli_init(CEDAModule *mod); bool cli_isQuit(void); +/** + * @brief Check if the computer must be restarted (cold hard reset). + * + * The restart value is automatically reset upon read. + * + * @return true if must be restarted, false otherwise. + */ +bool cli_checkRestart(void); + #endif // CEDA_CLI_H diff --git a/src/cpu.c b/src/cpu.c index c30ae2d..a36c288 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -207,6 +207,16 @@ static uint8_t cpu_int_read(void *context, zuint16 address) { return int_pop(); } +static void cpu_cleanup(void) { + z80_power(&cpu, false); +} + +static bool cpu_restart(void) { + cpu_cleanup(); + z80_power(&cpu, true); + return true; +} + void cpu_init(CEDAModule *mod) { // init mod struct memset(mod, 0, sizeof(*mod)); @@ -214,7 +224,8 @@ void cpu_init(CEDAModule *mod) { mod->start = NULL; mod->poll = cpu_poll; mod->remaining = cpu_remaining; - mod->cleanup = NULL; + mod->cleanup = cpu_cleanup; + mod->restart = cpu_restart; mod->performance = cpu_performance; // init cpu diff --git a/src/crtc.c b/src/crtc.c index bab5ee9..9e5e0f0 100644 --- a/src/crtc.c +++ b/src/crtc.c @@ -2,6 +2,7 @@ #include #include +#include #include "video.h" @@ -27,14 +28,10 @@ static uint8_t regs[CRTC_REGISTER_COUNT]; #define REG_CURSOR_L 15 #define REG_LIGHT_PEN_H 16 #define REG_LIGHT_PEN_L 17 -unsigned int rselect = 0; // current register selected +static unsigned int rselect = 0; // current register selected #define CRTC_NOT_IMPLEMENTED_STR "not implemented\n" -void crtc_init(void) { - // TODO(giomba): missing initialization -} - uint8_t crtc_in(ceda_ioaddr_t address) { (void)address; @@ -139,3 +136,15 @@ uint16_t crtc_startAddress(void) { return start_address; } + +static bool crtc_restart(void) { + memset(®s, 0, sizeof(regs)); + rselect = 0; + return true; +} + +void crtc_init(CEDAModule *mod) { + memset(mod, 0, sizeof(*mod)); + mod->init = crtc_init; + mod->restart = crtc_restart; +} diff --git a/src/crtc.h b/src/crtc.h index e8a6175..a26a02b 100644 --- a/src/crtc.h +++ b/src/crtc.h @@ -1,6 +1,7 @@ #ifndef CEDA_CRTC_H #define CEDA_CRTC_H +#include "module.h" #include "type.h" #include @@ -12,7 +13,7 @@ typedef enum CRTCCursorBlink { CRTC_CURSOR_BLINK_FAST, } CRTCCursorBlink; -void crtc_init(void); +void crtc_init(CEDAModule *mod); uint8_t crtc_in(ceda_ioaddr_t address); void crtc_out(ceda_ioaddr_t address, uint8_t value); diff --git a/src/fdc.c b/src/fdc.c index 4edeb4a..ce738be 100644 --- a/src/fdc.c +++ b/src/fdc.c @@ -911,9 +911,7 @@ static bool fdc_commit_write(void) { return false; } -/* * * * * * * * * * * * * * * Public routines * * * * * * * * * * * * * * */ - -void fdc_init(void) { +static bool fdc_restart(void) { // Reset current command status fdc_status = CMD; fdc_currop = NULL; @@ -931,9 +929,17 @@ void fdc_init(void) { // Reset track positions memset(track, 0, sizeof(track)); - // Detach any read/write callback - read_buffer_cb = NULL; - write_buffer_cb = NULL; + return true; +} + +/* * * * * * * * * * * * * * * Public routines * * * * * * * * * * * * * * */ + +void fdc_init(CEDAModule *mod) { + memset(mod, 0, sizeof(*mod)); + mod->init = fdc_init; + mod->restart = fdc_restart; + + fdc_restart(); } uint8_t fdc_in(ceda_ioaddr_t address) { diff --git a/src/fdc.h b/src/fdc.h index 5183bdb..6a3bdf8 100644 --- a/src/fdc.h +++ b/src/fdc.h @@ -1,6 +1,7 @@ #ifndef CEDA_FDC_H #define CEDA_FDC_H +#include "module.h" #include "type.h" #include @@ -30,7 +31,7 @@ typedef int (*fdc_read_write_t)(uint8_t *buffer, uint8_t unit_number, * @brief Initialize the Floppy Disk Controller system * */ -void fdc_init(void); +void fdc_init(CEDAModule *mod); /** * @brief Read data from the Floppy Disk Controller using its bus diff --git a/src/module.h b/src/module.h index 2d2d1cd..e012383 100644 --- a/src/module.h +++ b/src/module.h @@ -35,6 +35,20 @@ typedef struct CEDAModule { */ bool (*start)(void); + /** + * @brief Restart a module. + * + * This routine is used to implement the "reset" functionality of a module. + * + * This routine should perform a suitable module cleanup (eg. release + * dynamically acquired resources), and then the subsequent proper restart + * (eg. reinitialize variables, reacquire dynamic resources, etc...). + * + * Return true in case of success, false otherwise. + * + */ + bool (*restart)(void); + /** * @brief Advance the internal status of the module. * diff --git a/src/sio2.c b/src/sio2.c index ec83845..cebdb3a 100644 --- a/src/sio2.c +++ b/src/sio2.c @@ -426,16 +426,23 @@ void sio2_detachPeripheral(sio_channel_idx_t channel) { channels[channel].putc = NULL; } +static bool sio2_restart(void) { + for (size_t i = 0; i < ARRAY_SIZE(channels); ++i) + sio_channel_init(&channels[i]); + + // attach keyboard to channel B + channels[SIO_CHANNEL_B].getc = keyboard_getChar; + + return true; +} + void sio2_init(CEDAModule *mod) { mod->init = sio2_init; mod->start = sio2_start; + mod->restart = sio2_restart; mod->poll = sio2_poll; mod->remaining = sio2_remaining; mod->cleanup = sio2_cleanup; - for (size_t i = 0; i < ARRAY_SIZE(channels); ++i) - sio_channel_init(&channels[i]); - - // attach keyboard to channel B - channels[SIO_CHANNEL_B].getc = keyboard_getChar; + sio2_restart(); } diff --git a/src/tests/test_fdc.c b/src/tests/test_fdc.c index 92eb9c8..2d9e54f 100644 --- a/src/tests/test_fdc.c +++ b/src/tests/test_fdc.c @@ -6,6 +6,7 @@ // TODO(giuliof) source path is src! #include "../fdc.h" #include "../fdc_registers.h" +#include "../module.h" static void assert_fdc_sr(uint8_t expected_sr); static int fake_read(uint8_t *buffer, uint8_t unit_number, bool phy_head, @@ -77,14 +78,16 @@ static int fake_read_check_track(uint8_t *buffer, uint8_t unit_number, } Test(ceda_fdc, mainStatusRegisterWhenIdle) { - fdc_init(); + CEDAModule mod; + fdc_init(&mod); // Try to read status register and check that it is idle assert_fdc_sr(FDC_ST_RQM); } Test(ceda_fdc, specifyCommand) { - fdc_init(); + CEDAModule mod; + fdc_init(&mod); // Try to read status register and check that it is idle fdc_out(FDC_ADDR_DATA_REGISTER, FDC_SPECIFY); @@ -101,7 +104,8 @@ Test(ceda_fdc, specifyCommand) { } Test(ceda_fdc, seekCommand) { - fdc_init(); + CEDAModule mod; + fdc_init(&mod); uint8_t data; @@ -150,7 +154,8 @@ Test(ceda_fdc, seekCommand) { * be an Invalid Command" (see invalidCommand test). */ Test(ceda_fdc, invalidSeekSequence) { - fdc_init(); + CEDAModule mod; + fdc_init(&mod); uint8_t data; @@ -222,7 +227,8 @@ Test(ceda_fdc, readCommandNoMedium) { 4, // DTL }; - fdc_init(); + CEDAModule mod; + fdc_init(&mod); fdc_out(FDC_ADDR_DATA_REGISTER, FDC_READ_DATA); @@ -269,7 +275,8 @@ Test(ceda_fdc, readCommandInvalidParams) { uint8_t result[sizeof(expected_result)]; - fdc_init(); + CEDAModule mod; + fdc_init(&mod); // Link a fake reading function fdc_kickDiskImage(fake_wrong_rw, NULL); @@ -317,7 +324,8 @@ Test(ceda_fdc, readCommandOverEot) { uint8_t result[sizeof(expected_result)]; - fdc_init(); + CEDAModule mod; + fdc_init(&mod); // Link a fake reading function fdc_kickDiskImage(fake_read_check_track, NULL); @@ -562,7 +570,8 @@ ParameterizedTestParameters(ceda_fdc, readCommand0) { ParameterizedTest(struct rw_test_params_t *param, ceda_fdc, readCommand0) { uint8_t result[sizeof(param->result)]; - fdc_init(); + CEDAModule mod; + fdc_init(&mod); // Link a fake reading function fdc_kickDiskImage(fake_read, NULL); @@ -629,7 +638,8 @@ ParameterizedTestParameters(ceda_fdc, writeCommand0) { ParameterizedTest(struct rw_test_params_t *param, ceda_fdc, writeCommand0) { uint8_t result[sizeof(param->result)]; - fdc_init(); + CEDAModule mod; + fdc_init(&mod); // Link a fake reading function fdc_kickDiskImage(NULL, fake_write); @@ -696,7 +706,8 @@ Test(ceda_fdc, writeCommandInvalidParams) { uint8_t result[sizeof(expected_result)]; - fdc_init(); + CEDAModule mod; + fdc_init(&mod); // Link a fake reading function fdc_kickDiskImage(NULL, fake_wrong_rw); @@ -739,7 +750,8 @@ Test(ceda_fdc, formatCommand) { uint8_t result[7]; - fdc_init(); + CEDAModule mod; + fdc_init(&mod); // Link a fake reading function fdc_kickDiskImage(NULL, fake_write); @@ -799,7 +811,8 @@ Test(ceda_fdc, formatCommandInvalidParams) { uint8_t result[7]; - fdc_init(); + CEDAModule mod; + fdc_init(&mod); // Link a fake reading function fdc_kickDiskImage(NULL, fake_wrong_rw); @@ -839,7 +852,8 @@ Test(ceda_fdc, formatCommandInvalidParams) { Test(ceda_fdc, invalidCommand) { uint8_t st0; - fdc_init(); + CEDAModule mod; + fdc_init(&mod); // Force an invalid command fdc_out(FDC_ADDR_DATA_REGISTER, 0x00); diff --git a/src/timer.c b/src/timer.c index 6986ef6..d37d906 100644 --- a/src/timer.c +++ b/src/timer.c @@ -1,8 +1,6 @@ #include "timer.h" -void timer_init() { - // TODO(giomba) -} +#include uint8_t timer_in(ceda_ioaddr_t address) { // TODO(giomba) @@ -15,4 +13,16 @@ void timer_out(ceda_ioaddr_t address, uint8_t value) { // TODO(giomba) (void)address; (void)value; -} \ No newline at end of file +} + +static bool timer_restart(void) { + // TODO(giomba): implement me + return true; +} + +void timer_init(CEDAModule *mod) { + memset(mod, 0, sizeof(*mod)); + mod->init = timer_init; + mod->restart = timer_restart; + // TODO(giomba) +} diff --git a/src/timer.h b/src/timer.h index 3912597..e2e8ef4 100644 --- a/src/timer.h +++ b/src/timer.h @@ -1,11 +1,12 @@ #ifndef CEDA_TIMER_H #define CEDA_TIMER_H +#include "module.h" #include "type.h" #include -void timer_init(); +void timer_init(CEDAModule *mod); uint8_t timer_in(ceda_ioaddr_t address); void timer_out(ceda_ioaddr_t address, uint8_t value); diff --git a/src/upd8255.c b/src/upd8255.c index 823614d..b2b9d35 100644 --- a/src/upd8255.c +++ b/src/upd8255.c @@ -6,6 +6,7 @@ #include #include +#include #include "log.h" @@ -14,15 +15,11 @@ #define UPD8255_PORTC_REG 2 #define UPD8255_PORTS_COUNT 3 -uint8_t port[UPD8255_PORTS_COUNT]; +static uint8_t port[UPD8255_PORTS_COUNT]; #define UPD8255_CONTROL_REG 3 #define UPD8255_REG_COUNT 4 -void upd8255_init(void) { - ; -} - uint8_t upd8255_in(ceda_ioaddr_t address) { assert(address < UPD8255_REG_COUNT); @@ -76,3 +73,14 @@ void upd8255_out(ceda_ioaddr_t address, uint8_t value) { assert(0); } + +static bool upd8255_restart(void) { + memset(&port, 0, sizeof(port)); + return true; +} + +void upd8255_init(CEDAModule *mod) { + memset(mod, 0, sizeof(*mod)); + mod->init = upd8255_init; + mod->restart = upd8255_restart; +} diff --git a/src/upd8255.h b/src/upd8255.h index 4e7c3aa..4397dd6 100644 --- a/src/upd8255.h +++ b/src/upd8255.h @@ -1,11 +1,12 @@ #ifndef CEDA_UPD8255_H #define CEDA_UPD8255_H +#include "module.h" #include "type.h" #include -void upd8255_init(void); +void upd8255_init(CEDAModule *mod); uint8_t upd8255_in(ceda_ioaddr_t address); void upd8255_out(ceda_ioaddr_t address, uint8_t value); diff --git a/src/video.c b/src/video.c index e21158f..8f62878 100644 --- a/src/video.c +++ b/src/video.c @@ -379,18 +379,24 @@ static us_time_t video_remaining(void) { return diff; } +static bool video_restart(void) { + // default to character memory + mem = mem_char; + return true; +} + void video_init(CEDAModule *mod) { // mod init memset(mod, 0, sizeof(*mod)); mod->init = video_init; mod->start = video_start; + mod->restart = video_restart; mod->poll = video_poll; mod->remaining = video_remaining; mod->cleanup = NULL; mod->performance = video_performance; - // default to character memory - mem = mem_char; + video_restart(); } zuint8 video_ram_read(ceda_address_t address) {