diff options
Diffstat (limited to 'avr/cmd_loadcpm3.c')
-rw-r--r-- | avr/cmd_loadcpm3.c | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/avr/cmd_loadcpm3.c b/avr/cmd_loadcpm3.c new file mode 100644 index 0000000..425d1fd --- /dev/null +++ b/avr/cmd_loadcpm3.c @@ -0,0 +1,195 @@ +/* + * (C) Copyright 2015,2016,2018 Leo C. <erbl259-lmu@yahoo.de> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +/* + * See CP/M 3 System Manual, Appendix D: CPM3.SYS File Format + */ + +#include "cmd_loadcpm3.h" + +#include "env.h" +#include "ff.h" +#include "eval_arg.h" +#include "con-utils.h" +#include "z80-if.h" +#include "debug.h" + + +#define RS 128 /* CP/M record size */ + +#define FSIZE_t DWORD + +static FRESULT read_record(FIL *fp, uint8_t *buffer) +{ + unsigned int br; /* bytes read */ + + FRESULT res = f_read(fp, buffer, RS, &br); + if (br != RS) + return EEOF; + return res; +} + +/* + * Load Routine + * + * Input: addr = Page Address of load top + * len = Length in pages of module to read + * + */ +ERRNUM load(FIL *File, uint32_t addr, uint8_t len) +{ + uint8_t buffer[RS]; + + len *= 2; /* length in records of module */ + //debug("## load: addr: 0x%.4X, records: 0x%.4X, (%u)\n", addr, len, len); + + for (; len; len--) { + addr -= RS; + FRESULT res = read_record(File, buffer); + if (res) + return res; + + if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) + return EBUSTO; + z80_write_block(buffer, addr, RS); + z80_bus_cmd(Release); + //debug("## written: 0x%.4X\n", addr); + } + + return ESUCCESS; +} + +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + +command_ret_t do_loadcpm3(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[]) +{ + uint16_t mem_top; + uint8_t res_len; + uint16_t bank_top; + uint8_t bank_len; + uint16_t osentry_addr = 0; + uint32_t common_base = 0; + uint32_t banked_base; + char *fname; + FIL File; + char default_fname[strlen_P(PSTR(CONFIG_CPM3_SYSFILE)) + 1]; + uint8_t buffer[RS]; + FRESULT res; + + + //common_base = getenv_ulong(PSTR(ENV_CPM3_COMMON_BASE), 16, + // CONFIG_CPM3_COMMON_BASE); + banked_base = getenv_ulong(PSTR(ENV_CPM3_BANKED_BASE), 16, + CONFIG_CPM3_BANKED_BASE); + + if (argc > 3) + banked_base = eval_arg(argv[3], NULL); + if (argc > 2) + common_base = eval_arg(argv[2], NULL); + + fname = getenv_str(PSTR(ENV_CPM3_SYSFILE)); + if (fname == NULL || *fname == '\0') { + strcpy_P(default_fname, PSTR(CONFIG_CPM3_SYSFILE)); + fname = default_fname; + } + if (argc > 1) { + fname = argv[1]; + } + + res = f_open(&File, fname, FA_READ ); + if (res) + goto out; + + printf_P(PSTR("Loading: '%s'...\n"), fname); + + /* read the load record */ + res = read_record(&File, buffer); + if (res) + goto out; + + mem_top = buffer[0] << 8; + res_len = buffer[1]; + bank_top = buffer[2] << 8; + bank_len = buffer[3]; + osentry_addr = buffer[4] + (buffer[5] << 8); + + /* read display info */ + res = read_record(&File, buffer); + if (res) + goto out; + + /* print the info */ + buffer[RS-1] = '$'; + uint8_t *p = memchr(buffer, '$', RS); + *p = '\0'; + my_puts((char *)buffer); + + if (common_base == 0) { + /* read common base + * http://www.seasip.info/Cpm/scb.html + */ + FSIZE_t common_base_ofs = ((res_len - 6) << 8) + 2*RS + RS-7; + FSIZE_t cur_pos = f_tell(&File); + unsigned int br; /* bytes read */ + if ((res = f_lseek(&File, common_base_ofs)) || + (res = f_read(&File, buffer, 2, &br)) || + (res = f_lseek(&File, cur_pos))) + goto out; + if (br != 2) { + res = EEOF; + goto out; + } + common_base = (uint16_t) buffer[0] + (buffer[1] << 8); + setenv_hex(PSTR(ENV_CPM3_COMMON_BASE), common_base); + } + + setenv_hex(PSTR(ENV_CPM3_SCB), mem_top - ((res_len - (6 - 1)) << 8) + common_base); + + /* Main System Load */ + + /* Load Common Portion of System */ + if ((res = load(&File, common_base + mem_top, res_len)) != 0) + goto out; + + /* Load Banked Portion of System */ + res = load(&File, banked_base + bank_top, bank_len); + +out: + f_close(&File); + + if (res) + cmd_error(CMD_RET_FAILURE, res, PSTR("%s"), fname); + + if (res_len != 0) { + if (osentry_addr + common_base > 0xffff) { + z80_bus_cmd(Request); + if (z80_read(osentry_addr + common_base) == 0xc3) { + osentry_addr = z80_read(osentry_addr+common_base+1) + + (z80_read(osentry_addr + common_base+2) << 8); + } + z80_bus_cmd(Release); + if (banked_base + osentry_addr > 0xffff) + osentry_addr = 0; + } + setenv_hex(PSTR(ENV_STARTADDRESS), osentry_addr); + } + printf_P(PSTR("Loaded: Resident: ")); + if (res_len != 0) + printf_P(PSTR("%.5lX-%.5lX, "), + (common_base + mem_top) - res_len*256, + (common_base + mem_top) - 1); + else + printf_P(PSTR(" - ")); + printf_P(PSTR("Banked: ")); + if (bank_len != 0) + printf_P(PSTR("%.5lX-%.5lX\n"), + (banked_base + bank_top) - bank_len*256, + (banked_base + bank_top) - 1); + else + printf_P(PSTR(" - \n")); + + return CMD_RET_SUCCESS; +} |