Compare commits

...

2 Commits

Author SHA1 Message Date
David Given
98a28225d6 Add support for the 83kB 5.25" Lanier format. 2025-04-23 23:25:09 +02:00
David Given
d041b538bb Add boilerplate for the aeslanier5 format. 2025-04-23 21:17:10 +02:00
12 changed files with 190 additions and 24 deletions

View File

@@ -1,9 +1,19 @@
#ifndef AESLANIER_H #ifndef AESLANIER_H
#define AESLANIER_H #define AESLANIER_H
/* MFM:
*
* Raw bits:
* 5 5 5 5 5 1 2 2
* 0101 0101 0101 0101 0101 0001 0010 0010
* 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1
* 0 0 0 5
* Decoded bits.
*/
#define AESLANIER_RECORD_SEPARATOR 0x55555122 #define AESLANIER_RECORD_SEPARATOR 0x55555122
#define AESLANIER_SECTOR_LENGTH 256 #define AESLANIER_SECTOR_LENGTH 256
#define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 5) #define AESLANIER_RECORD_SIZE (AESLANIER_SECTOR_LENGTH + 4)
extern std::unique_ptr<Decoder> createAesLanierDecoder( extern std::unique_ptr<Decoder> createAesLanierDecoder(
const DecoderProto& config); const DecoderProto& config);

View File

@@ -1,4 +1,4 @@
syntax = "proto2"; syntax = "proto2";
message AesLanierDecoderProto {} message AesLanier5DecoderProto {}

View File

@@ -0,0 +1,21 @@
#ifndef AESLANIER5_H
#define AESLANIER5_H
/* Format is FM:
*
* a a a a f b e f
* 1010 1010 1010 1010 1111 1011 1110 1111
* 0 0 0 0 0 0 0 0 1 1 0 1 1 0 1 1
* 0 0 d b
*
* However, note that this pattern is _not_ reversed...
*/
#define AESLANIER5_RECORD_SEPARATOR 0xaaaaaaaaaaaafbefLL
#define AESLANIER5_SECTOR_LENGTH 151
#define AESLANIER5_RECORD_SIZE (AESLANIER5_SECTOR_LENGTH + 3)
extern std::unique_ptr<Decoder> createAesLanier5Decoder(
const DecoderProto& config);
#endif

View File

@@ -0,0 +1,4 @@
syntax = "proto2";
message AesLanierDecoderProto {}

View File

@@ -0,0 +1,78 @@
#include "lib/core/globals.h"
#include "lib/decoders/decoders.h"
#include "aeslanier5.h"
#include "lib/core/crc.h"
#include "lib/data/fluxmap.h"
#include "lib/data/fluxmapreader.h"
#include "lib/data/fluxpattern.h"
#include "lib/data/sector.h"
#include "lib/core/bytes.h"
#include "fmt/format.h"
#include <string.h>
static const FluxPattern SECTOR_PATTERN(32, AESLANIER5_RECORD_SEPARATOR);
/* This is actually FM, rather than MFM, but it our MFM/FM decoder copes fine
* with it. */
class AesLanier5Decoder : public Decoder
{
public:
AesLanier5Decoder(const DecoderProto& config): Decoder(config) {}
nanoseconds_t advanceToNextRecord() override
{
return seekToPattern(SECTOR_PATTERN);
}
void decodeSectorRecord() override
{
/* Skip ID mark (we know it's a AESLANIER5_RECORD_SEPARATOR). */
readRawBits(SECTOR_PATTERN.length());
const auto& rawbits = readRawBits(AESLANIER5_RECORD_SIZE * 16);
const auto& bytes =
decodeFmMfm(rawbits).slice(0, AESLANIER5_RECORD_SIZE);
const auto& reversed = bytes.reverseBits();
uint8_t encodedTrack = reversed[0];
uint8_t encodedSector = reversed[1];
_sector->logicalTrack = encodedTrack >> 1;
_sector->logicalSide = 0;
_sector->logicalSector = encodedSector;
/* Check header 'checksum' (which seems far too simple to mean much). */
{
uint8_t wanted = reversed[2];
uint8_t got = reversed[0] + reversed[1];
if (wanted != got)
return;
}
/* Check data checksum, which also includes the header and is
* significantly better. */
_sector->data = reversed.slice(3, AESLANIER5_SECTOR_LENGTH);
uint8_t wanted, got;
ByteReader br(_sector->data);
if ((encodedSector == 0) || (encodedSector == 8))
{
wanted = br.seek(17).read_8() + br.seek(150).read_8();
got = sumBytes(_sector->data.slice(0, 17)) + sumBytes(_sector->data.slice(18, 132));
}
else
{
wanted = br.seek(150).read_8();
got = sumBytes(_sector->data.slice(0, AESLANIER5_SECTOR_LENGTH-1));
}
_sector->status = (wanted == got) ? Sector::OK : Sector::BAD_CHECKSUM;
}
};
std::unique_ptr<Decoder> createAesLanier5Decoder(const DecoderProto& config)
{
return std::unique_ptr<Decoder>(new AesLanier5Decoder(config));
}

View File

@@ -4,6 +4,7 @@
#include "lib/config/config.h" #include "lib/config/config.h"
#include "arch/agat/agat.h" #include "arch/agat/agat.h"
#include "arch/aeslanier/aeslanier.h" #include "arch/aeslanier/aeslanier.h"
#include "arch/aeslanier5/aeslanier5.h"
#include "arch/amiga/amiga.h" #include "arch/amiga/amiga.h"
#include "arch/apple2/apple2.h" #include "arch/apple2/apple2.h"
#include "arch/brother/brother.h" #include "arch/brother/brother.h"
@@ -70,6 +71,7 @@ std::unique_ptr<Decoder> Arch::createDecoder(const DecoderProto& config)
decoders = { decoders = {
{DecoderProto::kAgat, createAgatDecoder }, {DecoderProto::kAgat, createAgatDecoder },
{DecoderProto::kAeslanier, createAesLanierDecoder }, {DecoderProto::kAeslanier, createAesLanierDecoder },
{DecoderProto::kAeslanier5, createAesLanier5Decoder },
{DecoderProto::kAmiga, createAmigaDecoder }, {DecoderProto::kAmiga, createAmigaDecoder },
{DecoderProto::kApple2, createApple2Decoder }, {DecoderProto::kApple2, createApple2Decoder },
{DecoderProto::kBrother, createBrotherDecoder }, {DecoderProto::kBrother, createBrotherDecoder },

View File

@@ -5,6 +5,7 @@ proto(
name="proto", name="proto",
srcs=[ srcs=[
"./aeslanier/aeslanier.proto", "./aeslanier/aeslanier.proto",
"./aeslanier5/aeslanier5.proto",
"./agat/agat.proto", "./agat/agat.proto",
"./amiga/amiga.proto", "./amiga/amiga.proto",
"./apple2/apple2.proto", "./apple2/apple2.proto",
@@ -36,6 +37,7 @@ cxxlibrary(
srcs=[ srcs=[
"./arch.cc", "./arch.cc",
"./aeslanier/decoder.cc", "./aeslanier/decoder.cc",
"./aeslanier5/decoder.cc",
"./agat/agat.cc", "./agat/agat.cc",
"./agat/decoder.cc", "./agat/decoder.cc",
"./agat/encoder.cc", "./agat/encoder.cc",
@@ -83,6 +85,7 @@ cxxlibrary(
"arch/f85/f85.h": "./f85/f85.h", "arch/f85/f85.h": "./f85/f85.h",
"arch/mx/mx.h": "./mx/mx.h", "arch/mx/mx.h": "./mx/mx.h",
"arch/aeslanier/aeslanier.h": "./aeslanier/aeslanier.h", "arch/aeslanier/aeslanier.h": "./aeslanier/aeslanier.h",
"arch/aeslanier5/aeslanier5.h": "./aeslanier5/aeslanier5.h",
"arch/northstar/northstar.h": "./northstar/northstar.h", "arch/northstar/northstar.h": "./northstar/northstar.h",
"arch/brother/data_gcr.h": "./brother/data_gcr.h", "arch/brother/data_gcr.h": "./brother/data_gcr.h",
"arch/brother/brother.h": "./brother/brother.h", "arch/brother/brother.h": "./brother/brother.h",

View File

@@ -31,13 +31,16 @@ based on what looks right. If anyone knows _anything_ about these disks,
## Options ## Options
(no options) - Format variants:
- `8`: use the format found on 8" disks
- `5`: use the format found on 5.25" disks
## Examples ## Examples
To read: To read:
- `fluxengine read aeslanier -s drive:0 -o aeslanier.img` - `fluxengine read aeslanier --8 -s drive:0 -o aeslanier.img`
- `fluxengine read aeslanier --5 -s drive:0 -o aeslanier.img`
## References ## References

View File

@@ -33,6 +33,11 @@ public:
bool matches(const unsigned* intervals, FluxMatch& match) const override; bool matches(const unsigned* intervals, FluxMatch& match) const override;
unsigned length() const
{
return _bits;
}
unsigned intervals() const override unsigned intervals() const override
{ {
return _intervals.size(); return _intervals.size();

View File

@@ -2,6 +2,7 @@ syntax = "proto2";
import "arch/agat/agat.proto"; import "arch/agat/agat.proto";
import "arch/aeslanier/aeslanier.proto"; import "arch/aeslanier/aeslanier.proto";
import "arch/aeslanier5/aeslanier5.proto";
import "arch/amiga/amiga.proto"; import "arch/amiga/amiga.proto";
import "arch/apple2/apple2.proto"; import "arch/apple2/apple2.proto";
import "arch/brother/brother.proto"; import "arch/brother/brother.proto";
@@ -22,7 +23,7 @@ import "arch/zilogmcz/zilogmcz.proto";
import "lib/fluxsink/fluxsink.proto"; import "lib/fluxsink/fluxsink.proto";
import "lib/config/common.proto"; import "lib/config/common.proto";
//NEXT: 33 //NEXT: 34
message DecoderProto { message DecoderProto {
optional double pulse_debounce_threshold = 1 [default = 0.30, optional double pulse_debounce_threshold = 1 [default = 0.30,
(help) = "ignore pulses with intervals shorter than this, in fractions of a clock"]; (help) = "ignore pulses with intervals shorter than this, in fractions of a clock"];
@@ -37,6 +38,7 @@ message DecoderProto {
oneof format { oneof format {
AesLanierDecoderProto aeslanier = 7; AesLanierDecoderProto aeslanier = 7;
AesLanier5DecoderProto aeslanier5 = 33;
AgatDecoderProto agat = 28; AgatDecoderProto agat = 28;
AmigaDecoderProto amiga = 8; AmigaDecoderProto amiga = 8;
Apple2DecoderProto apple2 = 13; Apple2DecoderProto apple2 = 13;

View File

@@ -14,6 +14,8 @@ of nearly £50,000 in 2018!).
processing software off twin 5.25" drive units, but apparently other software processing software off twin 5.25" drive units, but apparently other software
was available. was available.
**Note:** the following is wrong and needs to be updated.
The disk format is exceptionally weird. They used 77 track, 32 sector, single-sided The disk format is exceptionally weird. They used 77 track, 32 sector, single-sided
_hard_ sectored disks, where there were multiple index holes, _hard_ sectored disks, where there were multiple index holes,
indicating to the hardware where the sectors start. The encoding scheme indicating to the hardware where the sectors start. The encoding scheme
@@ -46,21 +48,56 @@ image_writer {
type: IMAGETYPE_IMG type: IMAGETYPE_IMG
} }
decoder { option_group {
aeslanier {} comment: "$formats"
}
layout { option {
format_type: FORMATTYPE_80TRACK name: '8'
tracks: 77 comment: 'use the format found on 8" disks'
sides: 1 set_by_default: true
layoutdata {
sector_size: 256 config {
physical { decoder {
start_sector: 0 aeslanier {}
count: 32 }
layout {
format_type: FORMATTYPE_80TRACK
tracks: 77
sides: 1
layoutdata {
sector_size: 256
physical {
start_sector: 0
count: 32
}
}
}
}
}
option {
name: '83'
comment: '83kB 5.25" 35-track 16-sector SSSD'
config {
decoder {
aeslanier5 {}
}
layout {
format_type: FORMATTYPE_40TRACK
tracks: 35
sides: 1
layoutdata {
sector_size: 150
physical {
start_sector: 0
count: 16
skew: 3
}
}
}
} }
} }
} }

View File

@@ -246,12 +246,13 @@ void FluxViewerControl::OnPaint(wxPaintEvent&)
dc.SetTextForeground(*wxBLACK); dc.SetTextForeground(*wxBLACK);
for (auto& sector : trackdata->sectors) for (auto& sector : trackdata->sectors)
{ {
nanoseconds_t sr = sector->dataEndTime; nanoseconds_t tl = sector->headerStartTime;
if (!sr) nanoseconds_t tr = sector->dataEndTime;
sr = sector->headerEndTime; if (!tl && !sector->headerEndTime)
tl = sector->dataStartTime;
int sp = sector->headerStartTime / _nanosecondsPerPixel; int sp = tl / _nanosecondsPerPixel;
int sw = (sr - sector->headerStartTime) / _nanosecondsPerPixel; int sw = (tr - tl) / _nanosecondsPerPixel;
wxRect rect = {x + sp, t1y - ch2, sw, ch}; wxRect rect = {x + sp, t1y - ch2, sw, ch};
bool hovered = rect.Contains(_mouseX, _mouseY); bool hovered = rect.Contains(_mouseX, _mouseY);