Feature #73 » atx.patch
atx.c 2018-09-01 14:35:20.121479335 +0200 | ||
---|---|---|
//
|
||
//*****************************************************************************
|
||
#include <stdlib.h>
|
||
#include <avr/io.h>
|
||
#include <util/delay.h>
|
||
#include "avrlibtypes.h"
|
||
#include "atx_eclaire.h"
|
||
#include "atx.h"
|
||
#include "fat.h"
|
||
// number of angular units in a full disk rotation
|
||
#define AU_FULL_ROTATION 26042
|
||
... | ... | |
u16 gBytesPerSector; // number of bytes per sector
|
||
u08 gSectorsPerTrack; // number of sectors in each track
|
||
struct atxTrackInfo gTrackInfo[2][40]; // pre-calculated info for each track and drive
|
||
// support slot D1 and D2 only because of insufficient RAM!
|
||
u16 gLastAngle = 0;
|
||
u08 gCurrentHeadTrack = 1;
|
||
struct atxTrackInfo gTrackInfo[40]; // pre-calculated info for each track
|
||
u16 gLastAngle;
|
||
u08 gCurrentHeadTrack;
|
||
#define FILE_ACCESS_READ 0
|
||
void longbyteswap(u32 * x);
|
||
void byteswap(u16 * x);
|
||
void byteswapatxfileheader(struct atxFileHeader * header)
|
||
{
|
||
// only swap the used entries
|
||
byteswap(&header->version);
|
||
byteswap(&header->minVersion);
|
||
longbyteswap(&header->startData);
|
||
}
|
||
void byteswapatxtrackheader(struct atxTrackHeader * header)
|
||
{
|
||
// only swap the used entries
|
||
longbyteswap(&header->size); // used
|
||
byteswap(&header->sectorCount); // used
|
||
longbyteswap(&header->headerSize); // used
|
||
}
|
||
void byteswapatxsectorlistheader(struct atxSectorListHeader * header)
|
||
{
|
||
longbyteswap(&header->next);
|
||
}
|
||
void byteswapatxsectorheader(struct atxSectorHeader * header)
|
||
{
|
||
byteswap(&header->timev);
|
||
longbyteswap(&header->data);
|
||
}
|
||
u16 loadAtxFile(u08 drive) {
|
||
void byteswapatxtrackchunk(struct atxTrackChunk *header)
|
||
{
|
||
byteswap(&header->data);
|
||
longbyteswap(&header->size);
|
||
}
|
||
u16 loadAtxFile() {
|
||
struct atxFileHeader *fileHeader;
|
||
struct atxTrackHeader *trackHeader;
|
||
// read the file header
|
||
faccess_offset(FILE_ACCESS_READ, 0, sizeof(struct atxFileHeader));
|
||
byteswapatxfileheader((struct atxFileHeader *) atari_sector_buffer);
|
||
// validate the ATX file header
|
||
fileHeader = (struct atxFileHeader *) atari_sector_buffer;
|
||
... | ... | |
fileHeader->signature[3] != 'X' ||
|
||
fileHeader->version != ATX_VERSION ||
|
||
fileHeader->minVersion != ATX_VERSION) {
|
||
return 0;
|
||
}
|
||
... | ... | |
break;
|
||
}
|
||
trackHeader = (struct atxTrackHeader *) atari_sector_buffer;
|
||
gTrackInfo[drive][trackHeader->trackNumber].offset = startOffset;
|
||
byteswapatxtrackheader(trackHeader);
|
||
gTrackInfo[trackHeader->trackNumber].offset = startOffset;
|
||
startOffset += trackHeader->size;
|
||
}
|
||
return gBytesPerSector;
|
||
}
|
||
u16 loadAtxSector(u08 drive, u16 num, unsigned short *sectorSize, u08 *status) {
|
||
u16 loadAtxSector(u16 num, unsigned short *sectorSize, u08 *status) {
|
||
struct atxTrackHeader *trackHeader;
|
||
struct atxSectorListHeader *slHeader;
|
||
struct atxSectorHeader *sectorHeader;
|
||
... | ... | |
u16 headPosition = getCurrentHeadPosition();
|
||
// read the track header
|
||
u32 currentFileOffset = gTrackInfo[drive][tgtTrackNumber - 1].offset;
|
||
u32 currentFileOffset = gTrackInfo[tgtTrackNumber - 1].offset;
|
||
faccess_offset(FILE_ACCESS_READ, currentFileOffset, sizeof(struct atxTrackHeader));
|
||
trackHeader = (struct atxTrackHeader *) atari_sector_buffer;
|
||
byteswapatxtrackheader(trackHeader);
|
||
u16 sectorCount = trackHeader->sectorCount;
|
||
// if there are no sectors in this track or the track number doesn't match, return error
|
||
... | ... | |
currentFileOffset += trackHeader->headerSize;
|
||
faccess_offset(FILE_ACCESS_READ, currentFileOffset, sizeof(struct atxSectorListHeader));
|
||
slHeader = (struct atxSectorListHeader *) atari_sector_buffer;
|
||
byteswapatxsectorlistheader(slHeader);
|
||
// sector list header is variable length, so skip any extra header bytes that may be present
|
||
currentFileOffset += slHeader->next - sectorCount * sizeof(struct atxSectorHeader);
|
||
... | ... | |
for (i=0; i < sectorCount; i++) {
|
||
if (faccess_offset(FILE_ACCESS_READ, currentFileOffset, sizeof(struct atxSectorHeader))) {
|
||
sectorHeader = (struct atxSectorHeader *) atari_sector_buffer;
|
||
byteswapatxsectorheader(sectorHeader);
|
||
// if the sector number matches the one we're looking for...
|
||
if (sectorHeader->number == tgtSectorNumber) {
|
||
// check if it's the next sector that the head would encounter angularly...
|
||
... | ... | |
// if an extended data record exists for this track, iterate through all track chunks to search
|
||
// for those records (note that we stop looking for chunks when we hit the 8-byte terminator; length == 0)
|
||
if (extendedDataRecords > 0) {
|
||
currentFileOffset = gTrackInfo[drive][tgtTrackNumber - 1].offset + trackHeader->headerSize;
|
||
currentFileOffset = gTrackInfo[tgtTrackNumber - 1].offset + trackHeader->headerSize;
|
||
do {
|
||
faccess_offset(FILE_ACCESS_READ, currentFileOffset, sizeof(struct atxTrackChunk));
|
||
extSectorData = (struct atxTrackChunk *) atari_sector_buffer;
|
||
byteswapatxtrackchunk(extSectorData);
|
||
if (extSectorData->size > 0) {
|
||
// if the target sector has a weak data flag, grab the start weak offset within the sector data
|
||
if (extSectorData->sectorIndex == tgtSectorIndex && extSectorData->type == 0x10) {
|
||
... | ... | |
*sectorSize = gBytesPerSector;
|
||
// read the data (re-using tgtSectorIndex variable here to reduce stack consumption)
|
||
tgtSectorIndex = tgtSectorOffset ? (u16) faccess_offset(FILE_ACCESS_READ, gTrackInfo[drive][tgtTrackNumber - 1].offset + tgtSectorOffset, gBytesPerSector) : (u16) 0;
|
||
tgtSectorIndex = tgtSectorOffset ? (u16) faccess_offset(FILE_ACCESS_READ, gTrackInfo[tgtTrackNumber - 1].offset + tgtSectorOffset, gBytesPerSector) : (u16) 0;
|
||
tgtSectorIndex = hasError ? (u16) 0 : tgtSectorIndex;
|
||
// if a weak offset is defined, randomize the appropriate data
|
||
... | ... | |
return ret;
|
||
}
|
||
void waitForAngularPosition(u16 pos) {
|
||
// if the position is less than the current timer, we need to wait for a rollover
|
||
// to occur
|
||
if (pos < TCNT1 / 2) {
|
||
TIFR1 |= _BV(OCF1A);
|
||
while (!(TIFR1 & _BV(OCF1A)));
|
||
}
|
||
// wait for the timer to reach the target position
|
||
while (TCNT1 / 2 < pos);
|
||
}
|
||
u16 getCurrentHeadPosition() {
|
||
// TCNT1 is a variable driven by an Atmel timer that ticks every 4 microseconds. A full
|
||
// rotation of the disk is represented in an ATX file by an angular positional value
|
||
// between 1-26042 (or 8 microseconds based on 288 rpms). So, TCNT1 / 2 always gives the
|
||
// current angular position of the drive head on the track any given time assuming the
|
||
// disk is spinning continously.
|
||
return TCNT1 / 2;
|
||
}
|