Skip to content

Commit

Permalink
Add LBA48 support
Browse files Browse the repository at this point in the history
  • Loading branch information
LIV2 committed Dec 16, 2023
1 parent d47b737 commit aea9061
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 13 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
## Features
* Autoboot
* Works with Kickstart 1.3 and up
* Supports up to 127GB drives
* Supports up to 2TB drives
* Supports ATAPI Devices (CD/DVD-ROM, Zip disk etc)
* Boot from ZIP/LS-120 etc
* [Boot from CD-ROM*](#boot-from-cdrom)
Expand Down
66 changes: 62 additions & 4 deletions ata.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "wait.h"

static BYTE write_taskfile_lba(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount);
static BYTE write_taskfile_lba48(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount);
static BYTE write_taskfile_chs(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount);

/**
Expand Down Expand Up @@ -309,9 +310,25 @@ bool ata_init_unit(struct IDEUnit *unit) {
unit->multiple_count = 1;
}

if (unit->lba == true) {
// Support LBA-48 but only up to 2TB
if (((*((UWORD *)buf + ata_identify_features) & ata_feature_lba48) != 0) && unit->logicalSectors >= 0xFFFFFFF) {
if (*((UWORD *)buf + ata_identify_lba48_sectors + 2) > 0 ||
*((UWORD *)buf + ata_identify_lba48_sectors + 3) > 0) {
Info("INIT: Rejecting drive larger than 2TB\n");
return false;
}

unit->lba48 = true;
Info("INIT: Drive supports LBA48 mode \n");
unit->logicalSectors = (*((UWORD *)buf + ata_identify_lba48_sectors + 1) << 16 | *((UWORD *)buf + ata_identify_lba48_sectors));
unit->write_taskfile = &write_taskfile_lba48;

} else if (unit->lba == true) {
// LBA-28 up to 127GB
unit->write_taskfile = &write_taskfile_lba;

} else {
// CHS Mode
Warn("INIT: Drive doesn't support LBA mode\n");
unit->write_taskfile = &write_taskfile_chs;
unit->logicalSectors = (unit->cylinders * unit->heads * unit->sectorsPerTrack);
Expand All @@ -321,7 +338,12 @@ bool ata_init_unit(struct IDEUnit *unit) {

if (unit->logicalSectors == 0 || unit->heads == 0 || unit->cylinders == 0) goto ident_failed;

if (unit->logicalSectors >= 16514064) {
if (unit->logicalSectors >= 267382800) {
// For drives larger than 127GB fudge the geometry
unit->heads = 63;
unit->sectorsPerTrack = 255;
unit->cylinders = (unit->logicalSectors / (63*255));
} else if (unit->logicalSectors >= 16514064) {
// If a drive is larger than 8GB then the drive will report a geometry of 16383/16/63 (CHS)
// In this case generate a new Cylinders value
unit->heads = 16;
Expand Down Expand Up @@ -422,7 +444,13 @@ BYTE ata_read(void *buffer, ULONG lba, ULONG count, ULONG *actual, struct IDEUni
UBYTE error = 0;
ULONG txn_count; // Amount of sectors to transfer in the current READ/WRITE command

UBYTE command = (unit->xfer_multiple) ? ATA_CMD_READ_MULTIPLE : ATA_CMD_READ;
UBYTE command;

if (unit->lba48) {
command = ATA_CMD_READ_MULTIPLE_EXT;
} else {
command = (unit->xfer_multiple) ? ATA_CMD_READ_MULTIPLE : ATA_CMD_READ;
}

if (count == 0) return TDERR_TooFewSecs;

Expand Down Expand Up @@ -517,7 +545,13 @@ BYTE ata_write(void *buffer, ULONG lba, ULONG count, ULONG *actual, struct IDEUn

ULONG txn_count; // Amount of sectors to transfer in the current READ/WRITE command

UBYTE command = (unit->xfer_multiple) ? ATA_CMD_WRITE_MULTIPLE : ATA_CMD_WRITE;
UBYTE command;

if (unit->lba48) {
command = ATA_CMD_WRITE_MULTIPLE_EXT;
} else {
command = (unit->xfer_multiple) ? ATA_CMD_WRITE_MULTIPLE : ATA_CMD_WRITE;
}

if (count == 0) return TDERR_TooFewSecs;

Expand Down Expand Up @@ -680,3 +714,27 @@ static BYTE write_taskfile_lba(struct IDEUnit *unit, UBYTE command, ULONG lba, U

return 0;
}

/**
* write_taskfile_lba48
*
* @param unit Pointer to an IDEUnit struct
* @param lba Pointer to the LBA variable
*/
static BYTE write_taskfile_lba48(struct IDEUnit *unit, UBYTE command, ULONG lba, UBYTE sectorCount) {

if (!ata_wait_ready(unit,ATA_RDY_WAIT_COUNT))
return HFERR_SelTimeout;

*unit->drive->sectorCount = (sectorCount == 0) ? 1 : 0;
*unit->drive->lbaHigh = 0;
*unit->drive->lbaMid = 0;
*unit->drive->lbaLow = (UBYTE)(lba >> 24);
*unit->drive->sectorCount = sectorCount; // Count value of 0 indicates to transfer 256 sectors
*unit->drive->lbaHigh = (UBYTE)(lba >> 16);
*unit->drive->lbaMid = (UBYTE)(lba >> 8);
*unit->drive->lbaLow = (UBYTE)(lba);
*unit->drive->status_command = command;

return 0;
}
20 changes: 12 additions & 8 deletions ata.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@

#define ata_err_flag_aborted (1<<2)

#define ATA_CMD_DEVICE_RESET 0x08
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_CMD_READ 0x20
#define ATA_CMD_READ_MULTIPLE 0xC4
#define ATA_CMD_WRITE 0x30
#define ATA_CMD_WRITE_MULTIPLE 0xC5
#define ATA_CMD_SET_MULTIPLE 0xC6
#define ATA_CMD_DEVICE_RESET 0x08
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_CMD_READ 0x20
#define ATA_CMD_READ_MULTIPLE 0xC4
#define ATA_CMD_READ_MULTIPLE_EXT 0x29
#define ATA_CMD_WRITE 0x30
#define ATA_CMD_WRITE_MULTIPLE 0xC5
#define ATA_CMD_WRITE_MULTIPLE_EXT 0x39
#define ATA_CMD_SET_MULTIPLE 0xC6

// Identify data word offsets
#define ata_identify_cylinders 1
Expand All @@ -60,11 +62,13 @@
#define ata_identify_capabilities 49
#define ata_identify_logical_sectors 60
#define ata_identify_pio_modes 64

#define ata_identify_features 83
#define ata_identify_lba48_sectors 100
#define ataf_multiple (1<<8)

#define ata_capability_lba (1<<9)
#define ata_capability_dma (1<<8)
#define ata_feature_lba48 (1<<10)

enum xfer_dir {
READ,
Expand Down
1 change: 1 addition & 0 deletions device.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ struct IDEUnit {
BOOL mediumPresentPrev;
BOOL xfer_multiple;
BOOL lba;
BOOL lba48;
UWORD open_count;
UWORD change_count;
UWORD cylinders;
Expand Down

0 comments on commit aea9061

Please sign in to comment.