mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98a28225d6 | ||
|
|
d041b538bb |
@@ -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);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
syntax = "proto2";
|
syntax = "proto2";
|
||||||
|
|
||||||
message AesLanierDecoderProto {}
|
message AesLanier5DecoderProto {}
|
||||||
|
|
||||||
|
|||||||
21
arch/aeslanier5/aeslanier5.h
Normal file
21
arch/aeslanier5/aeslanier5.h
Normal 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
|
||||||
4
arch/aeslanier5/aeslanier5.proto
Normal file
4
arch/aeslanier5/aeslanier5.proto
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
message AesLanierDecoderProto {}
|
||||||
|
|
||||||
78
arch/aeslanier5/decoder.cc
Normal file
78
arch/aeslanier5/decoder.cc
Normal 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));
|
||||||
|
}
|
||||||
@@ -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 },
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user