Skip to content

Add support for the AES Lanier 83kB format. #807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion arch/aeslanier/aeslanier.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
#ifndef 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_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(
const DecoderProto& config);
Expand Down
2 changes: 1 addition & 1 deletion arch/aeslanier/aeslanier.proto
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
syntax = "proto2";

message AesLanierDecoderProto {}
message AesLanier5DecoderProto {}

21 changes: 21 additions & 0 deletions arch/aeslanier5/aeslanier5.h
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions arch/aeslanier5/aeslanier5.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
syntax = "proto2";

message AesLanierDecoderProto {}

78 changes: 78 additions & 0 deletions arch/aeslanier5/decoder.cc
Original file line number Diff line number Diff line change
@@ -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));
}
2 changes: 2 additions & 0 deletions arch/arch.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "lib/config/config.h"
#include "arch/agat/agat.h"
#include "arch/aeslanier/aeslanier.h"
#include "arch/aeslanier5/aeslanier5.h"
#include "arch/amiga/amiga.h"
#include "arch/apple2/apple2.h"
#include "arch/brother/brother.h"
Expand Down Expand Up @@ -70,6 +71,7 @@ std::unique_ptr<Decoder> Arch::createDecoder(const DecoderProto& config)
decoders = {
{DecoderProto::kAgat, createAgatDecoder },
{DecoderProto::kAeslanier, createAesLanierDecoder },
{DecoderProto::kAeslanier5, createAesLanier5Decoder },
{DecoderProto::kAmiga, createAmigaDecoder },
{DecoderProto::kApple2, createApple2Decoder },
{DecoderProto::kBrother, createBrotherDecoder },
Expand Down
3 changes: 3 additions & 0 deletions arch/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
name="proto",
srcs=[
"./aeslanier/aeslanier.proto",
"./aeslanier5/aeslanier5.proto",
"./agat/agat.proto",
"./amiga/amiga.proto",
"./apple2/apple2.proto",
Expand Down Expand Up @@ -36,6 +37,7 @@
srcs=[
"./arch.cc",
"./aeslanier/decoder.cc",
"./aeslanier5/decoder.cc",
"./agat/agat.cc",
"./agat/decoder.cc",
"./agat/encoder.cc",
Expand Down Expand Up @@ -83,6 +85,7 @@
"arch/f85/f85.h": "./f85/f85.h",
"arch/mx/mx.h": "./mx/mx.h",
"arch/aeslanier/aeslanier.h": "./aeslanier/aeslanier.h",
"arch/aeslanier5/aeslanier5.h": "./aeslanier5/aeslanier5.h",
"arch/northstar/northstar.h": "./northstar/northstar.h",
"arch/brother/data_gcr.h": "./brother/data_gcr.h",
"arch/brother/brother.h": "./brother/brother.h",
Expand Down
7 changes: 5 additions & 2 deletions doc/disk-aeslanier.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,16 @@ based on what looks right. If anyone knows _anything_ about these disks,

## Options

(no options)
- Format variants:
- `8`: use the format found on 8" disks
- `5`: use the format found on 5.25" disks

## Examples

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

Expand Down
5 changes: 5 additions & 0 deletions lib/data/fluxpattern.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class FluxPattern : public FluxMatcher

bool matches(const unsigned* intervals, FluxMatch& match) const override;

unsigned length() const
{
return _bits;
}

unsigned intervals() const override
{
return _intervals.size();
Expand Down
4 changes: 3 additions & 1 deletion lib/decoders/decoders.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ syntax = "proto2";

import "arch/agat/agat.proto";
import "arch/aeslanier/aeslanier.proto";
import "arch/aeslanier5/aeslanier5.proto";
import "arch/amiga/amiga.proto";
import "arch/apple2/apple2.proto";
import "arch/brother/brother.proto";
Expand All @@ -22,7 +23,7 @@ import "arch/zilogmcz/zilogmcz.proto";
import "lib/fluxsink/fluxsink.proto";
import "lib/config/common.proto";

//NEXT: 33
//NEXT: 34
message DecoderProto {
optional double pulse_debounce_threshold = 1 [default = 0.30,
(help) = "ignore pulses with intervals shorter than this, in fractions of a clock"];
Expand All @@ -37,6 +38,7 @@ message DecoderProto {

oneof format {
AesLanierDecoderProto aeslanier = 7;
AesLanier5DecoderProto aeslanier5 = 33;
AgatDecoderProto agat = 28;
AmigaDecoderProto amiga = 8;
Apple2DecoderProto apple2 = 13;
Expand Down
63 changes: 50 additions & 13 deletions src/formats/aeslanier.textpb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ of nearly £50,000 in 2018!).
processing software off twin 5.25" drive units, but apparently other software
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
_hard_ sectored disks, where there were multiple index holes,
indicating to the hardware where the sectors start. The encoding scheme
Expand Down Expand Up @@ -46,21 +48,56 @@ image_writer {
type: IMAGETYPE_IMG
}

decoder {
aeslanier {}
}
option_group {
comment: "$formats"

option {
name: '8'
comment: 'use the format found on 8" disks'
set_by_default: true

layout {
format_type: FORMATTYPE_80TRACK
tracks: 77
sides: 1
layoutdata {
sector_size: 256
physical {
start_sector: 0
count: 32
config {
decoder {
aeslanier {}
}

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
}
}
}
}
}
}
11 changes: 6 additions & 5 deletions src/gui/fluxviewercontrol.cc
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,13 @@ void FluxViewerControl::OnPaint(wxPaintEvent&)
dc.SetTextForeground(*wxBLACK);
for (auto& sector : trackdata->sectors)
{
nanoseconds_t sr = sector->dataEndTime;
if (!sr)
sr = sector->headerEndTime;
nanoseconds_t tl = sector->headerStartTime;
nanoseconds_t tr = sector->dataEndTime;
if (!tl && !sector->headerEndTime)
tl = sector->dataStartTime;

int sp = sector->headerStartTime / _nanosecondsPerPixel;
int sw = (sr - sector->headerStartTime) / _nanosecondsPerPixel;
int sp = tl / _nanosecondsPerPixel;
int sw = (tr - tl) / _nanosecondsPerPixel;

wxRect rect = {x + sp, t1y - ch2, sw, ch};
bool hovered = rect.Contains(_mouseX, _mouseY);
Expand Down
Loading