/* * JR-IDE Project * - (c) 2017 Alan Hightower * * 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 . */ #include #include #include #include #include #include #include #include #include "jride.h" volatile uint16_t far *ide_sector; volatile uint8_t far *ide_window; volatile uint8_t far *bios_window; typedef struct _st_ata_id { uint16_t config; uint16_t cylinders; uint16_t reserved2; uint16_t heads; uint16_t obsolete4; uint16_t obsolate5; uint16_t spt; uint16_t vendor7; uint16_t vendor8; uint16_t vendor9; char serial_no[20]; uint16_t obsolete20; uint16_t obsolete21; uint16_t ecc_count; char fw_revision[8]; char model[40]; uint16_t spi; uint16_t dwio; uint16_t caps; uint16_t reserved50; uint16_t timing_pio; uint16_t timing_dma; uint16_t reserved53; uint16_t curr_cylinders; uint16_t curr_heads; uint16_t curr_spt; uint32_t curr_sectors; uint16_t curr_spi; uint32_t lba_sectors; // mostly DMA parameters follow } ata_id_t; uint16_t sector[63 * 256]; extern void _xfer_sector_in (uint16_t far *dst); #pragma aux _xfer_sector_in = \ "push ds" \ "lds si, dword ptr ide_sector" \ "mov cx, 0x100" \ "cld" \ "rep movsw" \ "pop ds" \ parm [di es] \ modify [si es di cx]; extern void _xfer_sector_out (uint16_t far *src); #pragma aux _xfer_sector_out = \ "les di, dword ptr ide_sector" \ "push ds" \ "mov ds, dx" \ "mov cx, 0x100" \ "cld" \ "rep movsw" \ "pop ds" \ parm [si dx] \ modify [si es di cx]; static void _xfer_sectors_in (uint16_t cyl, uint8_t head, uint8_t sector, uint8_t count, uint16_t far *ptr) { ide_window[IDE_REG_ADDR_CYL_L] = cyl & 0xff; ide_window[IDE_REG_ADDR_CYL_H] = cyl >> 8; ide_window[IDE_REG_ADDR_HEAD] = 0xa0 | head; ide_window[IDE_REG_SEC_COUNT] = count; ide_window[IDE_REG_ADDR_SEC] = sector; ide_window[IDE_REG_COMMAND] = 0x20; for (; count; --count) { while (!(ide_window[IDE_REG_STATUS] & 0x08)); _xfer_sector_in (ptr); ptr += 256; } return; } static void _xfer_sectors_out (uint16_t cyl, uint8_t head, uint8_t sector, uint8_t count, uint16_t far *ptr) { ide_window[IDE_REG_ADDR_CYL_L] = cyl & 0xff; ide_window[IDE_REG_ADDR_CYL_H] = cyl >> 8; ide_window[IDE_REG_ADDR_HEAD] = 0xa0 | head; ide_window[IDE_REG_SEC_COUNT] = count; ide_window[IDE_REG_ADDR_SEC] = sector; ide_window[IDE_REG_COMMAND] = 0x30; for (; count; --count) { while (!(ide_window[IDE_REG_STATUS] & 0x08)); _xfer_sector_out (ptr); ptr += 256; } return; } extern uint16_t get_ds(void); #pragma aux get_ds = \ "mov ax,ds" \ value [ax]; static char *ide_cook (const char *field, int len) { static char out[60]; char *ptr; int i; for (i = 0; i < len; i += 2) { out[i+0] = field[i+1]; out[i+1] = field[i+0]; } while (len && out[len - 1] == ' ') len--; out[len] = '\0'; ptr = out; while (*ptr == ' ') ptr++; return ptr; } #include #include #include "flash_api.h" #define LOGERR(fmt, ...) fprintf(stderr, "\nERROR: " fmt "\n\n", ## __VA_ARGS__) #ifndef MIN #define MIN(a,b) (((a) > (b)) ? (b) : (a)) #endif static int _iparse (const char *text, uint32_t *value_ptr) { int i, base, error = 0; if (strncasecmp (text, "0x", 2) == 0) { base = 16; text += 2; } else if (*text == '0') base = 8; else base = 10; *value_ptr = 0; for (i = 0; i < strlen (text); i++) { *value_ptr *= base; switch (text[i]) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': if (base != 16) { error = 1; break; } *value_ptr += 10 + text[i] - 'a'; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': if (base != 16) { error = 1; break; } *value_ptr += 10 + text[i] - 'A'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *value_ptr += text[i] - '0'; break; default: error = 1; break; } } return error; } static int _cmd_reset (int argc, char *argv[]) { #if JRIDE_ISA ide_window[JRIDE_FLASH_BASE_REG] = 0x80; ide_window[JRIDE_FLASH_BASE_REG] = 0x00; #else outp (JRIDE_POST_CTRL_PORT, 0xff); outp (JRIDE_POST_CTRL_PORT, 0x00); #endif printf ("Reset complete\n"); return 0; } static int _cmd_regs (int argc, char *argv[]) { printf ("ATA Registers (CS0):\n"); printf (" 1: Error = 0x%02x\n", ide_window[IDE_REG_ERROR]); printf (" 2: Sector Count = 0x%02x\n", ide_window[IDE_REG_SEC_COUNT]); printf (" 3: Address [S] = 0x%02x\n", ide_window[IDE_REG_ADDR_SEC]); printf (" 4: Address [C-L] = 0x%02x\n", ide_window[IDE_REG_ADDR_CYL_L]); printf (" 5: Address [C-H] = 0x%02x\n", ide_window[IDE_REG_ADDR_CYL_H]); printf (" 6: Address [H] = 0x%02x\n", ide_window[IDE_REG_ADDR_HEAD]); printf (" 7: Status = 0x%02x\n", ide_window[IDE_REG_STATUS]); printf ("\n"); printf ("ATA Registers (CS1):\n"); printf (" 0: = 0x%02x\n", ide_window[8]); printf (" 1: = 0x%02x\n", ide_window[9]); printf (" 2: = 0x%02x\n", ide_window[10]); printf (" 3: = 0x%02x\n", ide_window[11]); printf (" 4: = 0x%02x\n", ide_window[12]); printf (" 5: = 0x%02x\n", ide_window[13]); printf (" 6: = 0x%02x\n", ide_window[14]); printf (" 7: Address(ATA1) = 0x%02x\n", ide_window[IDE_REG_ADDRESS]); /* * Start-up: * Error = 0x00 * Sector Count = 0x00 * Address [S] = 0x01 * Address [C-L] = 0x00 * Address [C-H] = 0x00 * Address [H] = 0x20 * Status = 0x50 * Address(ATA1) = 0x22 */ return 0; } static void _hex_dump (uint32_t offset, void *data, uint32_t length) { uint16_t i, hspaces, cspaces, count; uint8_t *ptr = (uint8_t *) data; cspaces = offset & 0xf; hspaces = cspaces * 3; length += cspaces; offset &= ~0xf; while (length) { if (!(offset & 0xf)) printf ("0x%08lx : ", offset); count = MIN(length, 16 - cspaces); for (i = 0; i < hspaces; i++) printf (" "); hspaces = 0; for (i = 0; i < count; i++) printf ("%02x ", ptr[i]); printf (" "); for (i = 0; i < cspaces; i++) printf (" "); offset += cspaces; cspaces = 0; for (i = 0; i < count; i++) printf ("%c", (ptr[i] >= 0x20 && ptr[i] <= 0x7f) ? ptr[i] : '.'); printf ("\n"); offset += count; ptr += count; length -= count; } return; } static int _cmd_id (int argc, char *argv[]) { uint8_t tmp; ata_id_t *id; uint16_t far *ptr; do // need a breakout time { tmp = ide_window[IDE_REG_STATUS]; printf ("sts wait 0x%02x\n", tmp); } while (tmp & 0x80); ide_window[IDE_REG_ADDR_HEAD] = 0xa0; ide_window[IDE_REG_COMMAND] = 0xec; while (!(ide_window[IDE_REG_STATUS] & 0x08)); ptr = (uint16_t far *) MK_FP(get_ds(), sector); _xfer_sector_in (ptr); id = (ata_id_t *) sector; printf ("C/H/S = %u/%u/%u\n", id->cylinders, id->heads, id->spt); printf ("caps = %x\n", id->caps); printf ("config = %x\n", id->config); printf ("PIO = %x\n", id->timing_pio); printf ("LBA = %lx\n", id->lba_sectors); printf ("FW Rev = %s\n", ide_cook(id->fw_revision, 8)); printf ("Serial#= %s\n", ide_cook(id->serial_no, 20)); printf ("Model# = %s\n", ide_cook(id->model, 40)); _hex_dump (0, sector, 182); return 0; } static int _cmd_data (int argc, char *argv[]) { uint16_t far *ptr; ptr = (uint16_t far *) MK_FP(get_ds(), sector); _xfer_sector_in (ptr); _hex_dump (0, sector, 512); return 0; } static int _cmd_read (int argc, char *argv[]) { uint8_t tmp; uint32_t cyl = 0, head = 0, sec = 1; uint16_t far *ptr; if (argc > 0) _iparse (argv[0], &cyl); if (argc > 1) _iparse (argv[1], &head); if (argc > 2) _iparse (argv[2], &sec); printf ("Dumping C/H/S=%d/%d/%d\n", (int) cyl, (int) head, (int) sec); do // need a breakout time { tmp = ide_window[IDE_REG_STATUS]; printf ("sts wait 0x%02x\n", tmp); } while (tmp & 0x80); ptr = (uint16_t far *) MK_FP(get_ds(), sector); _xfer_sectors_in (cyl, head, sec, 1, ptr); _hex_dump (0, sector, 128); return 0; } static int _cmd_write (int argc, char *argv[]) { uint8_t tmp; uint16_t c; uint32_t seed = 0xdeadbeef; register uint16_t h; uint16_t far *ptr; if (argc) _iparse (argv[0], &seed); printf ("seed %lx\n", seed); do // need a breakout time { tmp = ide_window[IDE_REG_STATUS]; printf ("sts wait 0x%02x\n", tmp); } while (tmp & 0x80); ptr = (uint16_t far *) MK_FP(get_ds(), sector); for (h = 0; h < 16; h++) { for (c = 0; c < 256; c++) { if (c & 1) sector[c] = h ^ (seed & 0xffff); else sector[c] = h ^ (seed >> 16); } printf ("Writing %d\n", h); _xfer_sectors_out (0, h, 1, 1, ptr); } return 0; } static int _cmd_dout (int argc, char *argv[]) { uint8_t tmp; uint16_t far *ptr; uint8_t *bptr = (uint8_t *) sector; int i; do // need a breakout time { tmp = ide_window[IDE_REG_STATUS]; printf ("sts wait 0x%02x\n", tmp); } while (tmp & 0x80); ptr = (uint16_t far *) MK_FP(get_ds(), sector); for (i = 0; i < 16384; i++) { if (i & 0xff) bptr[i] = i & 0xff; else bptr[i] = (i >> 8); } _xfer_sectors_out (0, 0, 1, 32, ptr); return 0; } static int _cmd_verify (int argc, char *argv[]) { uint8_t tmp; uint16_t c; uint32_t seed = 0xdeadbeef; register uint16_t h; uint16_t far *ptr; if (argc) _iparse (argv[0], &seed); printf ("seed %lx\n", seed); do // need a breakout time { tmp = ide_window[IDE_REG_STATUS]; printf ("sts wait 0x%02x\n", tmp); } while (tmp & 0x80); ptr = (uint16_t far *) MK_FP(get_ds(), sector); for (h = 0; h < 16; h++) { printf ("Reading head %d\n", h); _xfer_sectors_in (0, h, 1, 1, ptr); for (c = 0; c < 256; c++) { uint16_t cmp; if (c & 1) cmp = h ^ (seed & 0xffff); else cmp = h ^ (seed >> 16); if (sector[c] != cmp) { printf ("Verify failed: H=%d C=%d %x!=%x\n", h, c, sector[c], cmp); sleep (1); break; } } if (c == 256) printf ("head %d passed\n", h); } return 0; } static int _cmd_bench (int argc, char *argv[]) { uint8_t tmp; uint16_t c; register uint16_t h; uint16_t far *ptr; do // need a breakout time { tmp = ide_window[IDE_REG_STATUS]; printf ("sts wait 0x%02x\n", tmp); } while (tmp & 0x80); ptr = (uint16_t far *) MK_FP(get_ds(), sector); for (c = 0; c < 16383; c++) { for (h = 0; h < 16; h++) { printf ("%d/%d\n", c, h); _xfer_sectors_in (c, h, 1, 63, ptr); } } return 0; } #define CMD(name, usage) { #name, usage, &_cmd_##name } const static struct _st_command { const char *name; const char *usage; int (*func_cb)(int , char **); } command[] = { CMD(reset, "- Cycle IDE reset line"), CMD(regs, "- Dump current IDE register values"), CMD(id, "- Print ATA ID sector"), CMD(data, "- Dump the data register window"), CMD(dout, "- Fill the data register window"), CMD(read, "[C] [[H] [S]] - Dump specific sector"), CMD(write, "[seed] - Write pattern to first 20 sectors"), CMD(verify, "[seed] - Verify pattern on first 20 sectors"), CMD(bench, "- Read data sequentially off disk at max rate"), }; #define commands (sizeof (command) / sizeof (command[0])) static char *progname = NULL; static void _print_usage (void) { int i; fprintf (stderr, "\nUsage: %s [options]\n\n" "Commands:\n\n", progname); for (i = 0; i < commands; i++) { if (command[i].usage) fprintf (stderr, "\t%s %s\n", command[i].name, command[i].usage); else fprintf (stderr, "\t%s\n", command[i].name); } fprintf (stderr, "\n"); return; } int main (int argc, char *argv[]) { int i, rc; uint16_t segment; progname = basename(argv[0]); if (argc < 2) { _print_usage (); return -1; } for (i = 0; i < commands; i++) { if (strcmp (argv[1], command[i].name) == 0) break; } if (i == commands) { LOGERR ("Unknown command '%s'", argv[1]); _print_usage (); return -1; } if (!(jride_flash_poll (&segment, NULL))) { fprintf (stderr, "\nERROR: Unable to locate BIOS address\n\n"); return 0; } printf ("\nJR-IDE Disk Test 1.0 - BIOS @ %04X0\n\n", segment); bios_window = (volatile uint8_t far *) MK_FP(segment, 0x0000); ide_sector = (uint16_t far *) &bios_window[JRIDE_IDE_DATA_WIN]; ide_window = (uint8_t far *) &bios_window[JRIDE_IDE_REG_BASE]; rc = command[i].func_cb (argc - 2, argv + 2); if (rc == -1) _print_usage (); return rc; }