/*
* 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;
}