* Optimizations for size etc
* Make endskip actually skip to the end
* Save last ATA Error
* Remove need for divide by using right shifts
This commit is contained in:
Matt Harlum 2023-03-28 11:52:13 +00:00
parent 2679269fc6
commit 45db8c4b21
9 changed files with 223 additions and 137 deletions

View File

@ -1,7 +1,16 @@
PROJECT=liv2ride
CC=m68k-amigaos-gcc
DEBUG=0
CFLAGS=-nostartfiles -nostdlib -noixemul -mcpu=68000 -Wall -Wno-multichar -Wno-pointer-sign -DDEBUG=$(DEBUG) -O3 -fomit-frame-pointer -Wno-unused-value -fno-delete-null-pointer-checks
CFLAGS=-nostartfiles -nostdlib -noixemul -mcpu=68000 -Wall -Wno-multichar -Wno-pointer-sign -Wno-unused-value -Og -fomit-frame-pointer -fno-delete-null-pointer-checks -msmall-code -s
LDFLAGS=-lamiga -lgcc -lc
AS=m68k-amigaos-as
ifdef DEBUG
CFLAGS+= -DDEBUG=$(DEBUG)
LDFLAGS+= -ldebug
.PHONY: $(PROJECT)
endif
LDFLAGS+= -lnix13
.PHONY: clean all
all: $(PROJECT)
@ -11,10 +20,13 @@ OBJ = driver.o \
idetask.o \
mounter.o
SRCS = $(OBJ:%.o=%.c)
ASMOBJ = endskip.o
liv2ride: $(SRCS)
${CC} -o $@ $(CFLAGS) $(SRCS) -lamiga -lgcc -ldebug -lnix13
SRCS = $(OBJ:%.o=%.c)
SRCS += $(ASMOBJ:%.o=%.S)
$(PROJECT): $(SRCS)
${CC} -o $@ $(CFLAGS) $(SRCS) $(LDFLAGS)
clean:
-rm $(PROJECT)

263
ata.c
View File

@ -25,7 +25,7 @@
*
* returns false on timeout
*/
bool ata_wait_not_busy(struct IDEUnit *unit) {
static bool ata_wait_not_busy(struct IDEUnit *unit) {
#ifndef NOTIMER
struct Device *TimerBase = unit->TimeReq->tr_node.io_Device;
struct timeval now, then;
@ -39,7 +39,7 @@ bool ata_wait_not_busy(struct IDEUnit *unit) {
#endif
while (1) {
if ((*(volatile BYTE *)unit->drive->status_command & ata_busy) == 0) return true;
if ((*(volatile BYTE *)unit->drive->status_command & ata_flag_busy) == 0) return true;
#ifndef NOTIMER
GetSysTime(&now);
@ -61,7 +61,7 @@ bool ata_wait_not_busy(struct IDEUnit *unit) {
*
* returns false on timeout
*/
bool ata_wait_ready(struct IDEUnit *unit) {
static bool ata_wait_ready(struct IDEUnit *unit) {
#ifndef NOTIMER
struct Device *TimerBase = unit->TimeReq->tr_node.io_Device;
struct timeval now, then;
@ -74,7 +74,7 @@ bool ata_wait_ready(struct IDEUnit *unit) {
KPrintF("wait_ready_enter\n");
#endif
while (1) {
if (*(volatile BYTE *)unit->drive->status_command & ata_ready) return true;
if (*(volatile BYTE *)unit->drive->status_command & ata_flag_ready) return true;
#ifndef NOTIMER
GetSysTime(&now);
@ -95,7 +95,7 @@ bool ata_wait_ready(struct IDEUnit *unit) {
*
* returns false on timeout
*/
bool ata_wait_drq(struct IDEUnit *unit) {
static bool ata_wait_drq(struct IDEUnit *unit) {
#ifndef NOTIMER
struct Device *TimerBase = unit->TimeReq->tr_node.io_Device;
struct timeval now, then;
@ -110,7 +110,7 @@ bool ata_wait_drq(struct IDEUnit *unit) {
#endif
while (1) {
if (*(volatile BYTE *)unit->drive->status_command& ata_drq) return true;
if (*(volatile BYTE *)unit->drive->status_command& ata_flag_drq) return true;
#ifndef NOTIMER
GetSysTime(&now);
@ -149,11 +149,11 @@ bool ata_identify(struct IDEUnit *unit, UWORD *buffer)
volatile UBYTE *status = unit->drive->status_command;
#if DEBUG >= 2
KPrintF("Drive Status: %04x%04x %04x%04x\n",status, *status);
KPrintF("Drive Status: %08lx %08lx\n",status, *status);
#endif
if (*status == 0) return false; // No drive there?
while ((*status & (ata_drq | ata_error)) == 0) {
while ((*status & (ata_flag_drq | ata_flag_error)) == 0) {
// Wait until ready to transfer or error condition
#ifndef NOTIMER
GetSysTime(&now);
@ -161,10 +161,16 @@ bool ata_identify(struct IDEUnit *unit, UWORD *buffer)
#endif
}
if (*status & ata_error) {
if (*status & ata_flag_error) {
#if DEBUG >= 1
KPrintF("IDENTIFY Status: Error\n");
KPrintF("IDENTIFY Status: Error\n");
KPrintF("last_error: %08lx\n",&unit->last_error[0]);
#endif
unit->last_error[0] = *unit->drive->error_features;
unit->last_error[1] = *unit->drive->lbaHigh;
unit->last_error[2] = *unit->drive->lbaMid;
unit->last_error[3] = *unit->drive->lbaLow;
unit->last_error[4] = *unit->drive->status_command;
return false;
}
@ -212,7 +218,7 @@ bool ata_init_unit(struct IDEUnit *unit) {
dev_found = true;
#if DEBUG >= 1
KPrintF("Something there?\n");
KPrintF("Unit base: %04x%04x; Drive base %04x%04x\n",unit, unit->drive);
KPrintF("Unit base: %08lx; Drive base %08lx\n",unit, unit->drive);
#endif
break;
}
@ -241,12 +247,150 @@ bool ata_init_unit(struct IDEUnit *unit) {
unit->heads = *((UWORD *)buf + ata_identify_heads);
unit->sectorsPerTrack = *((UWORD *)buf + ata_identify_sectors);
unit->blockSize = *((UWORD *)buf + ata_identify_sectorsize);
unit->blockShift = 0;
// It's faster to shift than divide
// Figure out how many shifts are needed for the equivalent divide
if (unit->blockSize == 0) {
#if DEBUG >= 1
KPrintF("Error! blockSize is 0\n");
#endif
if (buf) FreeMem(buf,512);
return false;
}
while ((unit->blockSize >> unit->blockShift) > 1) {
unit->blockShift++;
}
unit->present = true;
if (buf) FreeMem(buf,512);
return true;
}
/**
* ata_transfer
*
* Read/write a block from/to the unit
* @param buffer Source buffer
* @param lba LBA Address
* @param count Number of blocks to transfer
* @param actual Pointer to the io requests io_Actual
* @param unit Pointer to the unit structure
* @param direction READ/WRITE
*/
BYTE ata_transfer(void *buffer, ULONG lba, ULONG count, ULONG *actual, struct IDEUnit *unit, enum xfer_dir direction) {
#if DEBUG >= 2
if (direction == READ) {
KPrintF("ata_read");
} else {
KPrintF("ata_write");
}
#endif
ULONG subcount = 0;
ULONG offset = 0;
*actual = 0;
if (count == 0) return TDERR_TooFewSecs;
BYTE drvSel = (unit->primary) ? 0xE0 : 0xF0;
*unit->drive->devHead = (UBYTE)(drvSel | ((lba >> 24) & 0x0F));
#if DEBUG >= 3
KPrintF("Request sector count: %ld\n",count);
#endif
// None of this max_transfer 1FE00 nonsense!
while (count > 0) {
if (count >= MAX_TRANSFER_SECTORS) { // Transfer 256 Sectors at a time
subcount = MAX_TRANSFER_SECTORS;
} else {
subcount = count; // Get any remainders
}
count -= subcount;
#if DEBUG >= 3
KPrintF("XFER Count: %ld, Subcount: %ld\n",count,subcount);
#endif
if (!ata_wait_not_busy(unit))
return HFERR_BadStatus;
*unit->drive->sectorCount = subcount; // Count value of 0 indicates to transfer 256 sectors
*unit->drive->lbaLow = (UBYTE)(lba);
*unit->drive->lbaMid = (UBYTE)(lba >> 8);
*unit->drive->lbaHigh = (UBYTE)(lba >> 16);
*unit->drive->error_features = 0;
*unit->drive->status_command = (direction == READ) ? ATA_CMD_READ : ATA_CMD_WRITE;
for (int block = 0; block < subcount; block++) {
if (!ata_wait_drq(unit))
return HFERR_BadStatus;
if (direction == READ) {
//for (int i=0; i<(unit->blockSize / 2); i++) {
// ((UWORD *)buffer)[offset] = *(UWORD *)unit->drive->data;
// offset++;
//}
read_fast((void *)(unit->drive->error_features - 48),(buffer + offset));
offset += 512;
} else {
//for (int i=0; i<(unit->blockSize / 2); i++) {
// *(UWORD *)unit->drive->data = ((UWORD *)buffer)[offset];
// offset++;
//}
write_fast((buffer + offset),(void *)(unit->drive->error_features - 48));
offset += 512;
}
*actual += unit->blockSize;
}
if (*unit->drive->error_features & ata_flag_error) {
unit->last_error[0] = unit->drive->error_features[0];
unit->last_error[1] = unit->drive->lbaHigh[0];
unit->last_error[2] = unit->drive->lbaMid[0];
unit->last_error[3] = unit->drive->lbaLow[0];
unit->last_error[4] = unit->drive->status_command[0];
#if DEBUG
KPrintF("ATA ERROR!!!");
KPrintF("last_error: %08lx\n",unit->last_error);
KPrintF("LBA: %ld, LastLBA: %ld\n",lba,(unit->sectorsPerTrack * unit->cylinders * unit->heads));
#endif
return TDERR_NotSpecified;
}
lba += subcount;
}
return 0;
}
#pragma GCC optimize ("-fomit-frame-pointer")
/**
* read_fast
*
* Fast copy of a 512-byte sector using movem
* Adapted from the open source at_apollo_device by Frédéric REQUIN
* https://github.com/fredrequin/at_apollo_device
*
* NOTE! source needs to be 48 bytes before the end of the data port!
* The 68000 does an extra memory access at the end of a movem instruction!
* Source: https://github.com/prb28/m68k-instructions-documentation/blob/master/instructions/movem.md
*
* With the src of end-48 the error reg will be harmlessly read instead.
*
* @param source Pointer to drive data port
* @param destination Pointer to source buffer
*/
void read_fast (void *source, void *destinaton) {
asm volatile ("moveq #48,d7\n\t"
@ -298,6 +442,16 @@ void read_fast (void *source, void *destinaton) {
);
}
/**
* write_fast
*
* Fast copy of a 512-byte sector using movem
* Adapted from the open source at_apollo_device by Frédéric REQUIN
* https://github.com/fredrequin/at_apollo_device
*
* @param source Pointer to source buffer
* @param destination Pointer to drive data port
*/
void write_fast (void *source, void *destinaton) {
asm volatile (
"movem.l (%0)+,d0-d6/a1-a4/a6\n\t"
@ -338,89 +492,4 @@ void write_fast (void *source, void *destinaton) {
);
}
/**
* ata_transfer
*
* Read/write a block from/to the unit
* @param buffer Source buffer
* @param lba LBA Address
* @param count Number of blocks to transfer
* @param actual Pointer to the io requests io_Actual
* @param unit Pointer to the unit structure
* @param direction READ/WRITE
*/
BYTE ata_transfer(void *buffer, ULONG lba, ULONG count, ULONG *actual, struct IDEUnit *unit, enum xfer_dir direction) {
#if DEBUG >= 2
if (direction == READ) {
KPrintF("ata_read");
} else {
KPrintF("ata_write");
}
#endif
ULONG subcount;
ULONG offset = 0;
*actual = 0;
if (count == 0) return TDERR_TooFewSecs;
BYTE drvSel = (unit->primary) ? 0xE0 : 0xF0;
*unit->drive->devHead = (UBYTE)(drvSel | ((lba >> 24) & 0x0F));
// None of this max_transfer 1FE00 nonsense!
while (count > 0) {
if (count >= MAX_TRANSFER_SECTORS) { // Transfer 256 Sectors at a time
subcount = MAX_TRANSFER_SECTORS;
} else {
subcount = count; // Get any remainders
}
if (!ata_wait_not_busy(unit))
return HFERR_BadStatus;
*unit->drive->sectorCount = (subcount & 0xFF); // Count value of 0 indicates to transfer 256 sectors
*unit->drive->lbaLow = (UBYTE)(lba);
*unit->drive->lbaMid = (UBYTE)(lba >> 8);
*unit->drive->lbaHigh = (UBYTE)(lba >> 16);
*unit->drive->error_features = 0;
*unit->drive->status_command = (direction == READ) ? ATA_CMD_READ : ATA_CMD_WRITE;
for (int block = 0; block < subcount; block++) {
if (!ata_wait_drq(unit))
return HFERR_BadStatus;
if (*unit->drive->error_features && ata_error) {
#if DEBUG >= 1
KPrintF("ATA ERROR!!!");
#endif
return TDERR_NotSpecified;
}
if (direction == READ) {
// for (int i=0; i<(unit->blockSize / 2); i++) {
// ((UWORD *)buffer)[offset] = *(UWORD *)unit->drive->data;
// offset++;
// }
read_fast((void *)(unit->drive->error_features - 48),(buffer + offset));
offset += 512;
} else {
//for (int i=0; i<(unit->blockSize / 2); i++) {
// *(UWORD *)unit->drive->data = ((UWORD *)buffer)[offset];
// offset++;
//}
write_fast((buffer + offset),(void *)(unit->drive->error_features - 48));
offset += 512;
}
*actual += unit->blockSize;
}
count -= subcount;
}
return 0;
}
#pragma GCC reset_options

14
ata.h
View File

@ -27,10 +27,10 @@
#define drv_sel_secondary (1<<4)
#define ata_busy (1<<7)
#define ata_ready (1<<6)
#define ata_drq (1<<3)
#define ata_error (1<<0)
#define ata_flag_busy (1<<7)
#define ata_flag_ready (1<<6)
#define ata_flag_drq (1<<3)
#define ata_flag_error (1<<0)
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_CMD_READ 0x20
@ -56,13 +56,13 @@ enum xfer_dir {
WRITE
};
bool ata_wait_busy(struct IDEUnit *);
bool ata_wait_ready(struct IDEUnit *);
bool ata_wait_drq(struct IDEUnit *);
bool ata_init_unit(struct IDEUnit *);
bool ata_identify(struct IDEUnit *, UWORD *);
BYTE ata_transfer(void *buffer, ULONG lba, ULONG count, ULONG *actual, struct IDEUnit *unit, enum xfer_dir);
void read_fast (void *, void *);
void write_fast (void *, void *);
#endif

View File

@ -33,11 +33,13 @@ struct IDEUnit {
BOOL present;
UBYTE channel;
UBYTE device_type;
ULONG change_count;
UWORD cylinders;
UWORD heads;
UWORD sectorsPerTrack;
UWORD blockSize;
ULONG change_count;
UWORD blockShift;
UBYTE last_error[5];
};
struct DeviceBase {

View File

@ -37,20 +37,17 @@ int __attribute__((no_reorder)) _start()
asm("romtag: \n"
" dc.w "XSTR(RTC_MATCHWORD)" \n"
" dc.l romtag \n"
" dc.l endcode \n"
" dc.l _endskip \n"
" dc.b "XSTR(RTF_COLDSTART)" \n"
" dc.b "XSTR(DEVICE_VERSION)" \n"
" dc.b "XSTR(NT_DEVICE)" \n"
" dc.b "XSTR(DEVICE_PRIORITY)" \n"
" dc.l _device_name+4 \n"
" dc.l _device_id_string \n"
" dc.l _init \n"
"endcode: \n");
" dc.l _init \n");
char device_name[] = DEVICE_NAME;
const char device_id_string[] = DEVICE_ID_STRING;
const char task_name[] = TASK_NAME;
char const device_id_string[] = DEVICE_ID_STRING;
/**
* set_dev_name
@ -87,7 +84,7 @@ char * set_dev_name(struct DeviceBase *dev) {
*
* Free used resources back to the system
*/
void Cleanup(struct DeviceBase *dev) {
static void Cleanup(struct DeviceBase *dev) {
#if DEBUG > 0
KPrintF("Cleaning up...\n");
#endif
@ -115,14 +112,13 @@ struct Library __attribute__((used)) * init_device(struct ExecBase *SysBase asm(
{
dev->SysBase = SysBase;
#if DEBUG >= 1
KPrintF("Init dev, base: %08x\n",dev);
KPrintF("Init dev, base: %08lx\n",dev);
#endif
struct Library *ExpansionBase = NULL;
char *devName;
if (!(devName = set_dev_name(dev))) return NULL;
/* save pointer to our loaded code (the SegList) */
dev->saved_seg_list = seg_list;
dev->lib.lib_Node.ln_Type = NT_DEVICE;
@ -143,7 +139,7 @@ struct Library __attribute__((used)) * init_device(struct ExecBase *SysBase asm(
if ((dev->units = AllocMem(sizeof(struct IDEUnit)*MAX_UNITS, (MEMF_ANY|MEMF_CLEAR))) == NULL)
return NULL;
#if DEBUG >=2
KPrintF("Dev->Units: %x%x\n",(ULONG)dev->units);
KPrintF("Dev->Units: %08lx\n",(ULONG)dev->units);
#endif
if (!(ExpansionBase = (struct Library *)OpenLibrary("expansion.library",0))) {
Cleanup(dev);
@ -182,7 +178,7 @@ struct Library __attribute__((used)) * init_device(struct ExecBase *SysBase asm(
cd->cd_Flags &= ~(CDF_CONFIGME); // Claim the board
dev->num_boards++;
#if DEBUG >= 2
KPrintF("Claiming board %04x%04x\n",(ULONG)cd->cd_BoardAddr);
KPrintF("Claiming board %08lx\n",(ULONG)cd->cd_BoardAddr);
#endif
for (BYTE i=0; i<2; i++) {
dev->units[i].SysBase = SysBase;
@ -192,8 +188,9 @@ struct Library __attribute__((used)) * init_device(struct ExecBase *SysBase asm(
dev->units[i].channel = ((i%4) < 2) ? 0 : 1;
dev->units[i].change_count = 1;
dev->units[i].device_type = DG_DIRECT_ACCESS;
dev->units[i].present = false;
#if DEBUG >= 2
KPrintF("testing unit %x%x\n",i);
KPrintF("testing unit %08lx\n",i);
#endif
if (ata_init_unit(&dev->units[i])) {
dev->num_units++;
@ -204,7 +201,7 @@ struct Library __attribute__((used)) * init_device(struct ExecBase *SysBase asm(
}
#if DEBUG >= 1
KPrintF("Detected %x%x drives, %x%x boards\n",dev->num_units, dev->num_boards);
KPrintF("Detected %ld drives, %ld boards\n",dev->num_units, dev->num_boards);
#endif
if (dev->num_units > 0) {
#if DEBUG >= 1
@ -262,7 +259,7 @@ static BPTR __attribute__((used)) expunge(struct DeviceBase *dev asm("a6"))
return 0;
}
if (dev->Task != NULL) {
if (dev->Task != NULL && dev->TaskActive == true) {
// Shut down ide_task
struct MsgPort *mp = NULL;
@ -298,12 +295,13 @@ will execute your Open at a time. */
static void __attribute__((used)) open(struct DeviceBase *dev asm("a6"), struct IORequest *ioreq asm("a1"), ULONG unitnum asm("d0"), ULONG flags asm("d1"))
{
#if DEBUG >= 2
KPrintF((CONST_STRPTR) "running open() for unitnum %x%x\n",unitnum);
KPrintF((CONST_STRPTR) "running open() for unitnum %ld\n",unitnum);
#endif
ioreq->io_Error = IOERR_OPENFAIL;
if (dev->Task == NULL) {
if (dev->Task == NULL || dev->TaskActive == false) {
ioreq->io_Error = IOERR_OPENFAIL;
return;
}
@ -372,6 +370,7 @@ static UWORD supported_commands[] =
TD_CHANGESTATE,
TD_GETDRIVETYPE,
TD_GETGEOMETRY,
TD_MOTOR,
TD_PROTSTATUS,
TD_READ64,
TD_WRITE64,
@ -396,7 +395,7 @@ static void __attribute__((used)) begin_io(struct DeviceBase *dev asm("a6"), str
#endif
ioreq->io_Error = TDERR_NotSpecified;
if (dev->Task == NULL) {
if (dev->Task == NULL || dev->TaskActive == false) {
ioreq->io_Error = IOERR_OPENFAIL;
}
@ -405,6 +404,7 @@ static void __attribute__((used)) begin_io(struct DeviceBase *dev asm("a6"), str
switch (ioreq->io_Command) {
case CMD_CLEAR:
case CMD_UPDATE:
case TD_MOTOR:
ioreq->io_Actual = 0;
ioreq->io_Error = 0;
break;
@ -473,12 +473,12 @@ static void __attribute__((used)) begin_io(struct DeviceBase *dev asm("a6"), str
default:
#if DEBUG >= 2
KPrintF("Unknown command %x%x\n", ioreq->io_Command);
KPrintF("Unknown command %d\n", ioreq->io_Command);
#endif
ioreq->io_Error = IOERR_NOCMD;
}
if (!ioreq->io_Flags & IOF_QUICK) {
ReplyMsg(&ioreq->io_Message);
if (ioreq && !(ioreq->io_Flags & IOF_QUICK)) {
ReplyMsg(&ioreq->io_Message);
}
}

2
endskip.S Normal file
View File

@ -0,0 +1,2 @@
.globl _endskip
_endskip:

View File

@ -35,7 +35,7 @@ static void handle_scsi_command(struct IOStdReq *ioreq) {
enum xfer_dir direction = WRITE;
#if DEBUG >= 2
KPrintF("Command %04x%04x\n",*scsi_command->scsi_Command);
KPrintF("Command %ld\n",*scsi_command->scsi_Command);
#endif
switch (scsi_command->scsi_Command[0]) {
case SCSI_CMD_TEST_UNIT_READY:
@ -178,13 +178,13 @@ do_scsi_transfer:
* This is a task to complete IO Requests for all units
* Requests are sent here from begin_io via the dev->TaskMP Message port
*/
void ide_task () {
void __attribute__((noreturn)) ide_task () {
struct ExecBase *SysBase = *(struct ExecBase **)4UL;
struct Task volatile *task = FindTask(NULL);
struct MsgPort *mp;
struct IOStdReq *ioreq;
struct IDEUnit *unit;
UWORD blocksize;
UWORD blockShift;
ULONG lba;
ULONG count;
enum xfer_dir direction = WRITE;
@ -230,9 +230,9 @@ void ide_task () {
case TD_FORMAT64:
case NSCMD_TD_WRITE64:
case NSCMD_TD_FORMAT64:
blocksize = ((struct IDEUnit *)ioreq->io_Unit)->blockSize;
lba = (((long long)ioreq->io_Actual << 32 | ioreq->io_Offset) / (UWORD)blocksize);
count = (ioreq->io_Length/blocksize);
blockShift = ((struct IDEUnit *)ioreq->io_Unit)->blockShift;
lba = (((long long)ioreq->io_Actual << 32 | ioreq->io_Offset) >> blockShift);
count = (ioreq->io_Length >> blockShift);
ioreq->io_Error = ata_transfer(ioreq->io_Data, lba, count, &ioreq->io_Actual, unit, direction);
break;
@ -249,6 +249,7 @@ void ide_task () {
DeletePort(mp);
dev->TaskMP = NULL;
dev->Task = NULL;
dev->TaskActive = false;
ReplyMsg(&ioreq->io_Message);
RemTask(NULL);
Wait(0);

BIN
liv2ride

Binary file not shown.

View File

@ -1033,7 +1033,7 @@ int mount_drives(struct ConfigDev *cd, char *devName)
dbg("Mounter:\n");
ms.deviceName = devName;
ULONG units[] = {2,0,1};
ULONG units[] = {1,0};
ms.unitNum = &units;
ms.creatorName = NULL;
ms.configDev = cd;