aboutsummaryrefslogtreecommitdiff
path: root/chudinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'chudinit.c')
-rw-r--r--chudinit.c221
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;
+}