diff options
Diffstat (limited to 'chudinit.c')
| -rw-r--r-- | chudinit.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/chudinit.c b/chudinit.c new file mode 100644 index 0000000..1919100 --- /dev/null +++ b/chudinit.c @@ -0,0 +1,221 @@ +/* + * CHU - driver for CH375 in disk mode + * Copyright (C) 2024 Javier S. Pedro + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + + +#include <stdbool.h> +#include <string.h> + +#include "inc/dosapi.h" +#include "inc/dosint.h" +#include "inc/bda.h" + +#include "chudisk.h" +#include "ch375.h" +#include "dlog.h" +#include "cio.h" +#include "utils.h" + +static int parse_int(const char far * far *s) +{ + int i = 0; + while (**s >= '0' && **s <= '9') { + i = (i * 10) + (**s - '0'); + (*s)++; + } + if (**s == ' ' || **s == '\r' || **s == '\n') { + return i; + } else { + return -1; + } +} + +static int parse_hex(const char far * far *s) +{ + int i = 0; + while ((**s >= '0' && **s <= '9') || (**s >= 'A' && **s <= 'F') || (**s >= 'a' && **s <= 'f')) { + if (**s >= '0' && **s <= '9') i = (i * 16) + (**s - '0'); + else if (**s >= 'A' && **s <= 'F') i = (i * 16) + (**s - 'A'); + else if (**s >= 'a' && **s <= 'f') i = (i * 16) + (**s - 'a'); + (*s)++; + } + if (**s == ' ' || **s == '\r' || **s == '\n') { + return i; + } else { + return -1; + } +} + +static bool parse_cmdline(const char far *cmdline) +{ + // Source command line string + // E.g. DEVICE.SYS /N3\r\n + const char far *s = cmdline; + + // Skip filename part (search for first sep) + while (*s != ' ' && *s != '\r' && *s != '\n') { s++; } + if (*s == '\r' || *s == '\n') return true; // No arguments + // Now skip whitespace + while (*s == ' ') { s++; } + + // Now handle options like /N3 /X4 + while (*s != '\r' && *s != '\n') { + char c = *s++, opt; + int num; + switch (c) { + case '/': + opt = *s++; + switch (opt) { + case 'N': + case 'n': // number of units + num = parse_int(&s); + if (num > 0 && num <= MAX_UNITS) { + num_units = num; + } else { + c_printf("invalid value '%d' for option '%c'\n", num, opt); + return false; + } + break; + case 'P': + case 'p': // IO port + num = parse_hex(&s); + if (num > 0) { + ch375_port = num; + } else { + c_printf("invalid value '%d' for option '%c'\n", num, opt); + return false; + } + break; + case 'R': + case 'r': // Read only + read_only = true; + break; + case '3': // Show FAT32 partitions + show_fat32 = true; + break; + default: + c_printf("invalid option '%c'\n", opt); + return false; + } + break; + case ' ': + break; + default: + c_printf("invalid syntax '%c'\n", c); + return false; + } + } + + dprintf("Configured num drives: %hu\n", num_units); + + return true; +} + +static void fill_initial_bpb(DOS_BPB *bpb) +{ + // Dummy, only used during driver init + // Most likely these values can be anything you want, + // as long as it remotely looks like a valid FAT16 BPB + bpb->bytesPerSector = 512; + bpb->sectorsPerCluster = 1; + bpb->reservedSectors = 0; + bpb->numberOfFATs = 2; + bpb->rootDirEntries = 16; + bpb->numberOfSectors = 512; + bpb->mediaType = DOS_BPB_MEDIA_HDD; + bpb->sectorsPerFAT = 1; + bpb->sectorsPerTrack = 63; + bpb->numberOfHeads = 255; + bpb->hiddenSectors = 0; + bpb->numberOfSectorsLarge = 0; + memcpy(bpb->fat16.volumeLabel, "NO NAME ", 11); +} + +void driver_init(DOS_DRS __far *drs) +{ + unsigned i; + uint16_t dos_version; + uint8_t dos_major, dos_minor; + uint8_t hw_version; + + dlog_init(); + + // Defaults + ch375_port = 0x260; + num_units = 1; + read_only = false; + show_fat32 = false; + + // If DOS > 7.1, enable FAT32 by default + dos_version = dos_get_version(); + dos_major = dos_version & 0xFF; + dos_minor = (dos_version >> 8) & 0xFF; + dprintf("DOS version %d.%d\n", dos_major, dos_minor); + if (dos_major > 7 || (dos_major == 7 && dos_minor >= 10)) { + show_fat32 = true; + } + + // Parse command line arguments + if (!parse_cmdline(drs->cmd.init.in.cmdline)) { + c_puts("CHUDISK: Syntax error in cmdline arguments"); + goto fail; + } + + // Try to initialize CH375 + dputs("CH375 reset..."); + ch375_reset(); + bda_wait_ticks(3); // wait 100-150ms + dputs("CH375 reset wait done"); + + if (ch375_check_exists(0xA5) != (uint8_t)~0xA5) { + c_printf("CHUDISK: CH375 not detected at @%x", ch375_port); + goto fail; + } + + hw_version = ch375_get_version(); + + // Do not really set up the chip until it is used + ch375_set_usb_mode(CH375_USB_MODE_HOST_DISABLED); + ch375_enter_sleep(); + + data.ready = false; + data.sectors = 0; + data.bytesPerSector = 0; + + // Build the template BPBs + drs->cmd.init.out.number_units = num_units; + for (i = 0; i < num_units; i++) { + fill_initial_bpb(&data.unit[i].bpb); + data.bpb_ptrs[i] = &data.unit[i].bpb; + } + drs->cmd.init.out.bpbs = data.bpb_ptrs; + drs->cmd.init.out.break_addr = MK_FP(get_cs(), FP_OFF(&resident_end)); + drs->status = DOS_DD_STATUS_DONE; + + c_printf("CHUDISK v.0 (HW v.%x), drives", hw_version); + for (i = 0; i < num_units; i++) { + c_printf(" %c:", drive_index_to_letter(drs->cmd.init.in.first_drive_number + i)); + } + c_putc('\n'); + + return; +fail: + drs->status = DOS_DD_STATUS_DONE | DOS_DD_STATUS_ERROR | DOS_DD_ERR_GENERAL_FAILURE; + drs->cmd.init.out.number_units = 0; + drs->cmd.init.out.break_addr = MK_FP(get_cs(), 0); // Deallocate everything + return; +} |
