mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-24 11:11:02 -07:00
Compare commits
229 Commits
ab
...
dcae381973
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcae381973 | ||
|
|
2142bc7cce | ||
|
|
ae3f82264a | ||
|
|
710e83c098 | ||
|
|
4f46fff3be | ||
|
|
58ea21a9a2 | ||
|
|
0fd1aa82a6 | ||
|
|
5b7f9d84f9 | ||
|
|
4b7e8e74a7 | ||
|
|
5375c72d02 | ||
|
|
5c257be164 | ||
|
|
7fa17322dc | ||
|
|
ed3640d945 | ||
|
|
87ce3ad61d | ||
|
|
6d75feb0ce | ||
|
|
168b8b6f6c | ||
|
|
3d063e932a | ||
|
|
157ec569b2 | ||
|
|
f63c8dadf1 | ||
|
|
d17f6116f0 | ||
|
|
2d6cb22e3a | ||
|
|
2de8b52e56 | ||
|
|
171576e538 | ||
|
|
2db9f65e8b | ||
|
|
2572b64bd1 | ||
|
|
533aaf85f2 | ||
|
|
f67ddc1f77 | ||
|
|
b1d64f3683 | ||
|
|
7e8840e03f | ||
|
|
b003297b22 | ||
|
|
7341cec2c4 | ||
|
|
a98b7f72fd | ||
|
|
2e97579394 | ||
|
|
f960c7efd0 | ||
|
|
c2e7f32cba | ||
|
|
137528fc53 | ||
|
|
cbf4cc35fb | ||
|
|
cd7b3de1b3 | ||
|
|
fddc2270e5 | ||
|
|
2a96d9bd78 | ||
|
|
fd554f0808 | ||
|
|
6776c51b23 | ||
|
|
ef58295304 | ||
|
|
2e2c3e3e34 | ||
|
|
e87bb44a2d | ||
|
|
0ba0a9cce5 | ||
|
|
97bb563ba0 | ||
|
|
8f047f842e | ||
|
|
9d596ef530 | ||
|
|
580ffa8cf7 | ||
|
|
341e0a320d | ||
|
|
cff0a9703c | ||
|
|
38618532c4 | ||
|
|
6026dcd86d | ||
|
|
3949971546 | ||
|
|
6146f442fb | ||
|
|
7090c1bfdf | ||
|
|
563babc969 | ||
|
|
b649c2b9af | ||
|
|
f7f887789c | ||
|
|
a8fcdcc528 | ||
|
|
a988578cc7 | ||
|
|
ee585b24f0 | ||
|
|
3d6e980990 | ||
|
|
f5d19416a9 | ||
|
|
4187fa5a09 | ||
|
|
eb7613c03f | ||
|
|
7910429037 | ||
|
|
cd1cc736a7 | ||
|
|
e6d6805f25 | ||
|
|
9733879360 | ||
|
|
725712f796 | ||
|
|
2122cea5c4 | ||
|
|
5466e716a9 | ||
|
|
0dc0e3d9a1 | ||
|
|
4bb12b2caa | ||
|
|
0d9c5f5150 | ||
|
|
4030031a2c | ||
|
|
3143c87f1c | ||
|
|
f16f02c4c7 | ||
|
|
3e13b2461d | ||
|
|
5fd0d1589e | ||
|
|
23e6d234d0 | ||
|
|
cf2a97f8aa | ||
|
|
5a815e0cd6 | ||
|
|
06a3af2a1d | ||
|
|
0558d95fa3 | ||
|
|
81f9246ab8 | ||
|
|
6979567429 | ||
|
|
348de4165d | ||
|
|
0755d420dd | ||
|
|
dead21bce5 | ||
|
|
4cf451ce60 | ||
|
|
72298ac805 | ||
|
|
3d1ad81652 | ||
|
|
88c79169b6 | ||
|
|
d9747b9021 | ||
|
|
256976a5a1 | ||
|
|
0ba4b82e10 | ||
|
|
ffd9e28b42 | ||
|
|
9c919c786d | ||
|
|
47a9a56959 | ||
|
|
6e03bc604a | ||
|
|
feea6a027a | ||
|
|
08fa06b7fe | ||
|
|
8a976edef9 | ||
|
|
c71d8d6c74 | ||
|
|
e809af7426 | ||
|
|
ab05db9040 | ||
|
|
04f916741e | ||
|
|
f6224f3718 | ||
|
|
10185bb7a1 | ||
|
|
d565960c70 | ||
|
|
c21073294f | ||
|
|
3cd95de434 | ||
|
|
6552dba9aa | ||
|
|
c8ebe55aa9 | ||
|
|
1eefa2d604 | ||
|
|
a359394eea | ||
|
|
9f13026bec | ||
|
|
8fcc99b2a1 | ||
|
|
125a0536ff | ||
|
|
4115947d80 | ||
|
|
2f1dcd7c9a | ||
|
|
5e00ffca13 | ||
|
|
ac27095493 | ||
|
|
e27ca5cd4c | ||
|
|
cc72ac6327 | ||
|
|
5443aa6501 | ||
|
|
902bf32169 | ||
|
|
d200633747 | ||
|
|
a48b749c2e | ||
|
|
46fab84b95 | ||
|
|
b0290f858c | ||
|
|
fe09c12cd6 | ||
|
|
b5ae5a1cea | ||
|
|
113cb85512 | ||
|
|
da276bcb3b | ||
|
|
9a78d0f38c | ||
|
|
ec2e1666e7 | ||
|
|
478df40d4b | ||
|
|
a8b9d79cb1 | ||
|
|
23865d1a10 | ||
|
|
458b3f24fe | ||
|
|
86fa23e6fa | ||
|
|
dd9d5aaed5 | ||
|
|
b22df17bb5 | ||
|
|
b81e609e66 | ||
|
|
d41e57cba6 | ||
|
|
da7e83e257 | ||
|
|
83be12fcf1 | ||
|
|
a999e2d6c9 | ||
|
|
6d6251e757 | ||
|
|
be8b26ef94 | ||
|
|
c6b8bce5d6 | ||
|
|
d8b3452c07 | ||
|
|
eddbd43cd9 | ||
|
|
168189180d | ||
|
|
9e092bab6a | ||
|
|
2c35126b3a | ||
|
|
7dc0e4ca31 | ||
|
|
96257f89d5 | ||
|
|
09919343b4 | ||
|
|
b070c1068c | ||
|
|
5628a576db | ||
|
|
073c78e25f | ||
|
|
6a826d6eb5 | ||
|
|
11a6143d4c | ||
|
|
6127c9a46d | ||
|
|
98f7febef7 | ||
|
|
85afadacf0 | ||
|
|
01cd812162 | ||
|
|
39329acc77 | ||
|
|
bdc96038ef | ||
|
|
93760d989a | ||
|
|
b306c7063b | ||
|
|
e3d7fa69d8 | ||
|
|
f6c0e5405a | ||
|
|
fc12a2662c | ||
|
|
ab5b16488c | ||
|
|
4d5900268b | ||
|
|
b5c5a4335d | ||
|
|
e76235541a | ||
|
|
e75e1a6e27 | ||
|
|
aa220ecbcb | ||
|
|
edc8d74418 | ||
|
|
2831aa09ae | ||
|
|
e1b4b0d3a3 | ||
|
|
e5df6ca33b | ||
|
|
68c3cbb020 | ||
|
|
ca3c37d20a | ||
|
|
6fcd9233ea | ||
|
|
3761c4b1e2 | ||
|
|
c89c53b1c7 | ||
|
|
be0f63a133 | ||
|
|
a8216995ad | ||
|
|
995359ef45 | ||
|
|
bc84e3c8a0 | ||
|
|
af12a25a9d | ||
|
|
f6b2821221 | ||
|
|
458601a139 | ||
|
|
a89130edbd | ||
|
|
c95cd8a4da | ||
|
|
4d313a8495 | ||
|
|
263eef3442 | ||
|
|
2e97996211 | ||
|
|
7035b9c3c2 | ||
|
|
5628d2ca06 | ||
|
|
61cf7fbccf | ||
|
|
ce347c6326 | ||
|
|
94119b19fe | ||
|
|
9c7be1268f | ||
|
|
a9d59f67ba | ||
|
|
8d2a72228f | ||
|
|
60b95dd3f3 | ||
|
|
b1094f40dc | ||
|
|
e40ea80e34 | ||
|
|
9e1222d38a | ||
|
|
4446785729 | ||
|
|
790f015d72 | ||
|
|
ccb0dcea3c | ||
|
|
15a0632af0 | ||
|
|
3c0da28947 | ||
|
|
95227f32ca | ||
|
|
edf75b5cda | ||
|
|
af87c48451 | ||
|
|
7cde8e3aa6 | ||
|
|
46b90d9c36 | ||
|
|
7ee67082aa |
57
.github/workflows/ccpp.yml
vendored
57
.github/workflows/ccpp.yml
vendored
@@ -8,19 +8,21 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine'
|
||||
path: 'fluxengine'
|
||||
submodules: 'true'
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine-testdata'
|
||||
path: 'fluxengine-testdata'
|
||||
- name: apt
|
||||
run: |
|
||||
sudo apt install libudev-dev libsqlite3-dev protobuf-compiler libwxgtk3.0-gtk3-dev libfmt-dev libprotobuf-dev
|
||||
sudo apt update
|
||||
sudo apt install libudev-dev libsqlite3-dev protobuf-compiler libwxgtk3.2-dev libfmt-dev libprotobuf-dev libmagic-dev libmbedtls-dev libcurl4-openssl-dev libmagic-dev nlohmann-json3-dev libdbus-1-dev libglfw3-dev libmd4c-dev libfreetype-dev libcli11-dev libboost-regex-dev
|
||||
- name: make
|
||||
run: CXXFLAGS="-Wp,-D_GLIBCXX_ASSERTIONS" make -j`nproc` -C fluxengine
|
||||
|
||||
@@ -50,20 +52,22 @@ jobs:
|
||||
build-macos-current:
|
||||
strategy:
|
||||
matrix:
|
||||
runs-on: [macos-13, macos-latest]
|
||||
runs-on: [macos-15, macos-15-intel]
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine'
|
||||
path: 'fluxengine'
|
||||
submodules: 'true'
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine-testdata'
|
||||
path: 'fluxengine-testdata'
|
||||
- name: brew
|
||||
run: |
|
||||
brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg
|
||||
brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg libmagic nlohmann-json cli11 boost glfw3 md4c ninja python freetype2 mbedtls
|
||||
brew upgrade
|
||||
- name: make
|
||||
run: gmake -C fluxengine
|
||||
- name: Upload build artifacts
|
||||
@@ -76,29 +80,33 @@ jobs:
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
||||
steps:
|
||||
- name: setup WSL
|
||||
run: |
|
||||
curl -L https://github.com/WhitewaterFoundry/Fedora-Remix-for-WSL/releases/download/41.0.0/Fedora-Remix-for-WSL-SL_41.0.0.0_x64_arm64.msixbundle -o fedora.msixbundle
|
||||
unzip fedora.msixbundle Fedora-Remix-for-WSL-SL_41.0.0.0_x64.msix
|
||||
unzip Fedora-Remix-for-WSL-SL_41.0.0.0_x64.msix install.tar.gz
|
||||
wsl --update
|
||||
wsl --set-default-version 1
|
||||
wsl --import fedora fedora install.tar.gz
|
||||
wsl --set-default fedora
|
||||
wsl sh -c 'dnf -y install https://github.com/rpmsphere/noarch/raw/master/r/rpmsphere-release-40-1.noarch.rpm'
|
||||
wsl sh -c 'dnf -y install gcc gcc-c++ protobuf-c-compiler protobuf-devel fmt-devel systemd-devel sqlite-devel wxGTK-devel mingw32-gcc mingw32-gcc-c++ mingw32-zlib-static mingw32-protobuf-static mingw32-sqlite-static mingw32-wxWidgets3-static mingw32-libpng-static mingw32-libjpeg-static mingw32-libtiff-static mingw32-nsis png2ico ninja-build'
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: mingw64
|
||||
update: true
|
||||
install: |
|
||||
python diffutils ninja make zip
|
||||
pacboy: |
|
||||
protobuf:p pkgconf:p curl-winssl:p file:p glfw:p mbedtls:p
|
||||
sqlite:p freetype:p boost:p gcc:p binutils:p nsis:p abseil-cpp:p
|
||||
|
||||
- name: fix line endings
|
||||
- name: debug
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
|
||||
pacboy -Q --info protobuf:p
|
||||
cat /mingw64/lib/pkgconfig/protobuf.pc
|
||||
/mingw64/bin/pkg-config.exe protobuf --cflags
|
||||
/mingw64/bin/pkg-config.exe protobuf --cflags --static
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'davidgiven/fluxengine'
|
||||
path: 'fluxengine'
|
||||
submodules: 'true'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -107,17 +115,18 @@ jobs:
|
||||
|
||||
- name: run
|
||||
run: |
|
||||
wsl sh -c 'make -C fluxengine BUILDTYPE=windows -j$(nproc)'
|
||||
make -C fluxengine BUILDTYPE=windows
|
||||
|
||||
- name: nsis
|
||||
run: |
|
||||
wsl sh -c 'cd fluxengine && strip fluxengine.exe -o fluxengine-stripped.exe'
|
||||
wsl sh -c 'cd fluxengine && strip fluxengine-gui.exe -o fluxengine-gui-stripped.exe'
|
||||
wsl sh -c 'cd fluxengine && makensis -v2 -nocd -dOUTFILE=fluxengine-installer.exe extras/windows-installer.nsi'
|
||||
cd fluxengine
|
||||
strip fluxengine.exe -o fluxengine-stripped.exe
|
||||
strip fluxengine-gui.exe -o fluxengine-gui-stripped.exe
|
||||
makensis -v2 -nocd -dOUTFILE=fluxengine-installer.exe extras/windows-installer.nsi
|
||||
|
||||
- name: zip
|
||||
run: |
|
||||
wsl sh -c 'cd fluxengine && zip -9 fluxengine-windows.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex fluxengine-installer.exe'
|
||||
cd fluxengine && zip -9 fluxengine-windows.zip fluxengine.exe fluxengine-gui.exe upgrade-flux-file.exe brother120tool.exe brother240tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex fluxengine-installer.exe
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
36
.gitmodules
vendored
Normal file
36
.gitmodules
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
[submodule "dep/imhex"]
|
||||
path = dep/imhex
|
||||
url = git@github.com:davidgiven/ImHex.git
|
||||
[submodule "dep/libwolv"]
|
||||
path = dep/libwolv
|
||||
url = https://github.com/WerWolv/libwolv.git
|
||||
[submodule "dep/imgui"]
|
||||
path = dep/imgui
|
||||
url = https://github.com/ocornut/imgui.git
|
||||
[submodule "dep/pattern-language"]
|
||||
path = dep/pattern-language
|
||||
url = https://github.com/WerWolv/PatternLanguage.git
|
||||
[submodule "dep/native-file-dialog"]
|
||||
path = dep/native-file-dialog
|
||||
url = https://github.com/btzy/nativefiledialog-extended.git
|
||||
[submodule "dep/xdgpp"]
|
||||
path = dep/xdgpp
|
||||
url = https://github.com/WerWolv/xdgpp.git
|
||||
[submodule "dep/libromfs"]
|
||||
path = dep/libromfs
|
||||
url = https://github.com/WerWolv/libromfs.git
|
||||
[submodule "dep/throwing_ptr"]
|
||||
path = dep/throwing_ptr
|
||||
url = https://github.com/rockdreamer/throwing_ptr.git
|
||||
[submodule "dep/lunasvg"]
|
||||
path = dep/lunasvg
|
||||
url = https://github.com/sammycage/lunasvg.git
|
||||
[submodule "dep/md4c"]
|
||||
path = dep/md4c
|
||||
url = https://github.com/mity/md4c
|
||||
[submodule "dep/nlohmann_json"]
|
||||
path = dep/nlohmann_json
|
||||
url = https://github.com/nlohmann/json
|
||||
[submodule "dep/cli11"]
|
||||
path = dep/cli11
|
||||
url = https://github.com/CLIUtils/CLI11
|
||||
90
Makefile
90
Makefile
@@ -8,30 +8,52 @@ ifeq ($(BUILDTYPE),)
|
||||
endif
|
||||
export BUILDTYPE
|
||||
|
||||
OPTFLAGS = -g -O3
|
||||
|
||||
ifeq ($(BUILDTYPE),windows)
|
||||
MINGW = i686-w64-mingw32-
|
||||
MINGW = x86_64-w64-mingw32-
|
||||
CC = $(MINGW)gcc
|
||||
CXX = $(MINGW)g++ -std=c++20
|
||||
CFLAGS += -g -O3 \
|
||||
-Wno-unknown-warning-option \
|
||||
CXX = $(MINGW)g++
|
||||
CFLAGS += \
|
||||
$(OPTFLAGS) \
|
||||
-ffunction-sections \
|
||||
-fdata-sections
|
||||
-fdata-sections \
|
||||
-Wno-attributes \
|
||||
-Wa,-mbig-obj \
|
||||
-static
|
||||
CXXFLAGS += \
|
||||
-fext-numeric-literals \
|
||||
$(OPTFLAGS) \
|
||||
-std=c++23 \
|
||||
-Wno-deprecated-enum-float-conversion \
|
||||
-Wno-deprecated-enum-enum-conversion
|
||||
LDFLAGS += -static -Wl,--gc-sections
|
||||
AR = $(MINGW)ar
|
||||
PKG_CONFIG = $(MINGW)pkg-config -static
|
||||
-Wno-deprecated-enum-enum-conversion \
|
||||
-Wno-attributes \
|
||||
-Wa,-mbig-obj \
|
||||
-static
|
||||
LDFLAGS += -Wl,--gc-sections -static
|
||||
AR = $(MINGW)gcc-ar
|
||||
PKG_CONFIG = $(MINGW)pkg-config --static
|
||||
WINDRES = $(MINGW)windres
|
||||
WX_CONFIG = /usr/i686-w64-mingw32/sys-root/mingw/bin/wx-config-3.0 --static=yes
|
||||
NINJA = /bin/ninja
|
||||
PROTOC = /mingw64/bin/protoc
|
||||
PROTOC_SEPARATOR = ;
|
||||
EXT = .exe
|
||||
|
||||
AB_SANDBOX = no
|
||||
else
|
||||
CC = gcc
|
||||
CXX = g++ -std=c++20
|
||||
CFLAGS = -g -O3 \
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
CFLAGS = \
|
||||
$(OPTFLAGS) \
|
||||
-I/opt/homebrew/include -I/usr/local/include \
|
||||
-Wno-unknown-warning-option
|
||||
CXXFLAGS += \
|
||||
CXXFLAGS = \
|
||||
$(OPTFLAGS) \
|
||||
-std=c++23 \
|
||||
-fexperimental-library \
|
||||
-I/opt/homebrew/include -I/usr/local/include \
|
||||
-Wformat \
|
||||
-Wformat-security \
|
||||
-Wno-deprecated-enum-float-conversion \
|
||||
-Wno-deprecated-enum-enum-conversion
|
||||
LDFLAGS =
|
||||
@@ -56,31 +78,33 @@ BINDIR ?= $(PREFIX)/bin
|
||||
|
||||
# Special Windows settings.
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
EXT ?= .exe
|
||||
MINGWBIN = /mingw32/bin
|
||||
CCPREFIX = $(MINGWBIN)/
|
||||
PKG_CONFIG = $(MINGWBIN)/pkg-config
|
||||
WX_CONFIG = /usr/bin/sh $(MINGWBIN)/wx-config --static=yes
|
||||
PROTOC = $(MINGWBIN)/protoc
|
||||
WINDRES = windres
|
||||
LDFLAGS += \
|
||||
-static
|
||||
CXXFLAGS += \
|
||||
-fext-numeric-literals \
|
||||
-Wno-deprecated-enum-float-conversion \
|
||||
-Wno-deprecated-enum-enum-conversion
|
||||
|
||||
# Required to get the gcc run - time libraries on the path.
|
||||
export PATH := $(PATH):$(MINGWBIN)
|
||||
endif
|
||||
#ifeq ($(OS), Windows_NT)
|
||||
# EXT ?= .exe
|
||||
# MINGWBIN = /mingw32/bin
|
||||
# CCPREFIX = $(MINGWBIN)/
|
||||
# PKG_CONFIG = $(MINGWBIN)/pkg-config
|
||||
# WX_CONFIG = /usr/bin/sh $(MINGWBIN)/wx-config --static=yes
|
||||
# PROTOC = $(MINGWBIN)/protoc
|
||||
# WINDRES = windres
|
||||
# LDFLAGS += \
|
||||
# -static
|
||||
# CXXFLAGS += \
|
||||
# -fext-numeric-literals \
|
||||
# -Wno-deprecated-enum-float-conversion \
|
||||
# -Wno-deprecated-enum-enum-conversion
|
||||
#
|
||||
# # Required to get the gcc run - time libraries on the path.
|
||||
# export PATH := $(PATH):$(MINGWBIN)
|
||||
#endif
|
||||
|
||||
# Special OSX settings.
|
||||
|
||||
ifeq ($(shell uname),Darwin)
|
||||
LDFLAGS += \
|
||||
-framework IOKit \
|
||||
-framework Foundation
|
||||
-framework AppKit \
|
||||
-framework UniformTypeIdentifiers \
|
||||
-framework UserNotifications
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
|
||||
@@ -36,8 +36,8 @@ public:
|
||||
decodeFmMfm(rawbits).slice(0, AESLANIER_RECORD_SIZE);
|
||||
const auto& reversed = bytes.reverseBits();
|
||||
|
||||
_sector->logicalTrack = reversed[1];
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalCylinder = reversed[1];
|
||||
_sector->logicalHead = 0;
|
||||
_sector->logicalSector = reversed[2];
|
||||
|
||||
/* Check header 'checksum' (which seems far too simple to mean much). */
|
||||
|
||||
@@ -59,9 +59,9 @@ public:
|
||||
if (bytes[3] != 0x5a)
|
||||
return;
|
||||
|
||||
_sector->logicalTrack = bytes[1] >> 1;
|
||||
_sector->logicalCylinder = bytes[1] >> 1;
|
||||
_sector->logicalSector = bytes[2];
|
||||
_sector->logicalSide = bytes[1] & 1;
|
||||
_sector->logicalHead = bytes[1] & 1;
|
||||
_sector->status = Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
}
|
||||
|
||||
|
||||
@@ -58,13 +58,10 @@ private:
|
||||
};
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
auto trackLayout = Layout::getLayoutOfTrack(
|
||||
trackInfo->logicalTrack, trackInfo->logicalSide);
|
||||
|
||||
double clockRateUs = _config.target_clock_period_us() / 2.0;
|
||||
int bitsPerRevolution =
|
||||
(_config.target_rotational_period_ms() * 1000.0) / clockRateUs;
|
||||
@@ -80,7 +77,7 @@ public:
|
||||
writeFillerRawBytes(_config.pre_sector_gap_bytes(), 0xaaaa);
|
||||
writeRawBits(SECTOR_ID, 64);
|
||||
writeByte(0x5a);
|
||||
writeByte((sector->logicalTrack << 1) | sector->logicalSide);
|
||||
writeByte((sector->logicalCylinder << 1) | sector->logicalHead);
|
||||
writeByte(sector->logicalSector);
|
||||
writeByte(0x5a);
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ public:
|
||||
Bytes header = amigaDeinterleave(ptr, 4);
|
||||
Bytes recoveryinfo = amigaDeinterleave(ptr, 16);
|
||||
|
||||
_sector->logicalTrack = header[1] >> 1;
|
||||
_sector->logicalSide = header[1] & 1;
|
||||
_sector->logicalCylinder = header[1] >> 1;
|
||||
_sector->logicalHead = header[1] & 1;
|
||||
_sector->logicalSector = header[2];
|
||||
|
||||
uint32_t wantedheaderchecksum =
|
||||
|
||||
@@ -84,7 +84,7 @@ static void write_sector(std::vector<bool>& bits,
|
||||
|
||||
checksum = 0;
|
||||
Bytes header = {0xff, /* Amiga 1.0 format byte */
|
||||
(uint8_t)((sector->logicalTrack << 1) | sector->logicalSide),
|
||||
(uint8_t)((sector->logicalCylinder << 1) | sector->logicalHead),
|
||||
(uint8_t)sector->logicalSector,
|
||||
(uint8_t)(AMIGA_SECTORS_PER_TRACK - sector->logicalSector)};
|
||||
write_interleaved_bytes(header);
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "protocol.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/apple2/apple2.h"
|
||||
#include "arch/apple2/apple2.pb.h"
|
||||
#include "lib/decoders/decoders.pb.h"
|
||||
@@ -93,24 +94,25 @@ public:
|
||||
ByteReader br(header);
|
||||
|
||||
uint8_t volume = combine(br.read_be16());
|
||||
_sector->logicalTrack = combine(br.read_be16());
|
||||
_sector->logicalSide = _sector->physicalSide;
|
||||
_sector->logicalCylinder = combine(br.read_be16());
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalSector = combine(br.read_be16());
|
||||
uint8_t checksum = combine(br.read_be16());
|
||||
|
||||
// If the checksum is correct, upgrade the sector from MISSING
|
||||
// to DATA_MISSING in anticipation of its data record
|
||||
if (checksum ==
|
||||
(volume ^ _sector->logicalTrack ^ _sector->logicalSector))
|
||||
(volume ^ _sector->logicalCylinder ^ _sector->logicalSector))
|
||||
_sector->status =
|
||||
Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
|
||||
if (_sector->logicalSide == 1)
|
||||
_sector->logicalTrack -= _config.apple2().side_one_track_offset();
|
||||
if (_sector->logicalHead == 1)
|
||||
_sector->logicalCylinder -=
|
||||
_config.apple2().side_one_track_offset();
|
||||
|
||||
/* Sanity check. */
|
||||
|
||||
if (_sector->logicalTrack > 100)
|
||||
if (_sector->logicalCylinder > 100)
|
||||
{
|
||||
_sector->status = Sector::MISSING;
|
||||
return;
|
||||
|
||||
@@ -36,7 +36,7 @@ private:
|
||||
const Apple2EncoderProto& _config;
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
@@ -129,8 +129,8 @@ private:
|
||||
// extra padding.
|
||||
write_ff40(sector.logicalSector == 0 ? 32 : 8);
|
||||
|
||||
int track = sector.logicalTrack;
|
||||
if (sector.logicalSide == 1)
|
||||
int track = sector.logicalCylinder;
|
||||
if (sector.logicalHead == 1)
|
||||
track += _config.side_one_track_offset();
|
||||
|
||||
// Write address field: APPLE2_SECTOR_RECORD + sector identifier +
|
||||
|
||||
@@ -75,14 +75,14 @@ public:
|
||||
const auto& bytes = toBytes(rawbits).slice(0, 4);
|
||||
|
||||
ByteReader br(bytes);
|
||||
_sector->logicalTrack = decode_header_gcr(br.read_be16());
|
||||
_sector->logicalCylinder = decode_header_gcr(br.read_be16());
|
||||
_sector->logicalSector = decode_header_gcr(br.read_be16());
|
||||
|
||||
/* Sanity check the values read; there's no header checksum and
|
||||
* occasionally we get garbage due to bit errors. */
|
||||
if (_sector->logicalSector > 11)
|
||||
return;
|
||||
if (_sector->logicalTrack > 79)
|
||||
if (_sector->logicalCylinder > 79)
|
||||
return;
|
||||
|
||||
_sector->status = Sector::DATA_MISSING;
|
||||
|
||||
@@ -107,7 +107,7 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
fillBitmapTo(bits, cursor, headerCursor, {true, false});
|
||||
write_sector_header(bits,
|
||||
cursor,
|
||||
sectorData->logicalTrack,
|
||||
sectorData->logicalCylinder,
|
||||
sectorData->logicalSector);
|
||||
fillBitmapTo(bits, cursor, dataCursor, {true, false});
|
||||
write_sector_data(bits, cursor, sectorData->data);
|
||||
|
||||
@@ -74,8 +74,8 @@ public:
|
||||
|
||||
uint8_t checksum = bytes[0];
|
||||
_sector->logicalSector = bytes[1];
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalTrack = bytes[2] - 1;
|
||||
_sector->logicalHead = 0;
|
||||
_sector->logicalCylinder = bytes[2] - 1;
|
||||
if (checksum == xorBytes(bytes.slice(1, 4)))
|
||||
_sector->status =
|
||||
Sector::DATA_MISSING; /* unintuitive but correct */
|
||||
|
||||
@@ -155,7 +155,7 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
@@ -178,7 +178,7 @@ public:
|
||||
else
|
||||
_formatByte1 = _formatByte2 = 0;
|
||||
|
||||
double clockRateUs = clockPeriodForC64Track(trackInfo->logicalTrack);
|
||||
double clockRateUs = clockPeriodForC64Track(ltl.logicalCylinder);
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
@@ -245,7 +245,7 @@ private:
|
||||
* 06-07 - $0F ("off" bytes)
|
||||
*/
|
||||
uint8_t encodedTrack =
|
||||
((sector->logicalTrack) +
|
||||
((sector->logicalCylinder) +
|
||||
1); // C64 track numbering starts with 1. Fluxengine with 0.
|
||||
uint8_t encodedSector = sector->logicalSector;
|
||||
// uint8_t formatByte1 = C64_FORMAT_ID_BYTE1;
|
||||
|
||||
@@ -76,8 +76,8 @@ public:
|
||||
const auto& bytes = decode(readRawBits(6 * 10));
|
||||
|
||||
_sector->logicalSector = bytes[2];
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalTrack = bytes[0];
|
||||
_sector->logicalHead = 0;
|
||||
_sector->logicalCylinder = bytes[0];
|
||||
|
||||
uint16_t wantChecksum = bytes.reader().seek(4).read_be16();
|
||||
uint16_t gotChecksum = crc16(CCITT_POLY, 0xef21, bytes.slice(0, 4));
|
||||
|
||||
@@ -126,8 +126,8 @@ public:
|
||||
return;
|
||||
|
||||
uint8_t abssector = id[2];
|
||||
_sector->logicalTrack = abssector >> 1;
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalCylinder = abssector >> 1;
|
||||
_sector->logicalHead = 0;
|
||||
_sector->logicalSector = abssector & 1;
|
||||
_sector->data.writer().append(id.slice(5, 12)).append(payload);
|
||||
|
||||
|
||||
@@ -141,11 +141,10 @@ public:
|
||||
bw += decodeFmMfm(bits).slice(0, IBM_IDAM_LEN);
|
||||
|
||||
IbmDecoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(
|
||||
trackdata, _sector->physicalTrack, _sector->physicalSide);
|
||||
getTrackFormat(trackdata, _ltl->logicalCylinder, _ltl->logicalHead);
|
||||
|
||||
_sector->logicalTrack = br.read_8();
|
||||
_sector->logicalSide = br.read_8();
|
||||
_sector->logicalCylinder = br.read_8();
|
||||
_sector->logicalHead = br.read_8();
|
||||
_sector->logicalSector = br.read_8();
|
||||
_currentSectorSize = 1 << (br.read_8() + 7);
|
||||
|
||||
@@ -156,11 +155,10 @@ public:
|
||||
Sector::DATA_MISSING; /* correct but unintuitive */
|
||||
|
||||
if (trackdata.ignore_side_byte())
|
||||
_sector->logicalSide =
|
||||
Layout::remapSidePhysicalToLogical(_sector->physicalSide);
|
||||
_sector->logicalSide ^= trackdata.invert_side_byte();
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalHead ^= trackdata.invert_side_byte();
|
||||
if (trackdata.ignore_track_byte())
|
||||
_sector->logicalTrack = _sector->physicalTrack;
|
||||
_sector->logicalCylinder = _ltl->logicalCylinder;
|
||||
|
||||
for (int sector : trackdata.ignore_sector())
|
||||
if (_sector->logicalSector == sector)
|
||||
@@ -209,16 +207,14 @@ public:
|
||||
_sector->status =
|
||||
(wantCrc == gotCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
|
||||
|
||||
auto layout = Layout::getLayoutOfTrack(
|
||||
_sector->logicalTrack, _sector->logicalSide);
|
||||
if (_currentSectorSize != layout->sectorSize)
|
||||
if (_currentSectorSize != _ltl->sectorSize)
|
||||
std::cerr << fmt::format(
|
||||
"Warning: configured sector size for t{}.h{}.s{} is {} bytes "
|
||||
"but that seen on disk is {} bytes\n",
|
||||
_sector->logicalTrack,
|
||||
_sector->logicalSide,
|
||||
_sector->logicalCylinder,
|
||||
_sector->logicalHead,
|
||||
_sector->logicalSector,
|
||||
layout->sectorSize,
|
||||
_ltl->sectorSize,
|
||||
_currentSectorSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -107,16 +107,12 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
IbmEncoderProto::TrackdataProto trackdata;
|
||||
getEncoderTrackData(
|
||||
trackdata, trackInfo->logicalTrack, trackInfo->logicalSide);
|
||||
|
||||
auto trackLayout = Layout::getLayoutOfTrack(
|
||||
trackInfo->logicalTrack, trackInfo->logicalSide);
|
||||
getEncoderTrackData(trackdata, ltl.logicalCylinder, ltl.logicalHead);
|
||||
|
||||
auto writeBytes = [&](const Bytes& bytes)
|
||||
{
|
||||
@@ -152,7 +148,7 @@ public:
|
||||
|
||||
uint8_t sectorSize = 0;
|
||||
{
|
||||
int s = trackLayout->sectorSize >> 7;
|
||||
int s = ltl.sectorSize >> 7;
|
||||
while (s > 1)
|
||||
{
|
||||
s >>= 1;
|
||||
@@ -202,9 +198,9 @@ public:
|
||||
bw.write_8(MFM_RECORD_SEPARATOR_BYTE);
|
||||
}
|
||||
bw.write_8(idamUnencoded);
|
||||
bw.write_8(sectorData->logicalTrack);
|
||||
bw.write_8(sectorData->logicalCylinder);
|
||||
bw.write_8(
|
||||
sectorData->logicalSide ^ trackdata.invert_side_byte());
|
||||
sectorData->logicalHead ^ trackdata.invert_side_byte());
|
||||
bw.write_8(sectorData->logicalSector);
|
||||
bw.write_8(sectorSize);
|
||||
uint16_t crc = crc16(CCITT_POLY, header);
|
||||
@@ -237,8 +233,7 @@ public:
|
||||
}
|
||||
bw.write_8(damUnencoded);
|
||||
|
||||
Bytes truncatedData =
|
||||
sectorData->data.slice(0, trackLayout->sectorSize);
|
||||
Bytes truncatedData = sectorData->data.slice(0, ltl.sectorSize);
|
||||
bw += truncatedData;
|
||||
uint16_t crc = crc16(CCITT_POLY, data);
|
||||
bw.write_be16(crc);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "protocol.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/macintosh/macintosh.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
@@ -146,7 +147,7 @@ public:
|
||||
auto header = toBytes(readRawBits(7 * 8)).slice(0, 7);
|
||||
|
||||
uint8_t encodedTrack = decode_data_gcr(header[0]);
|
||||
if (encodedTrack != (_sector->physicalTrack & 0x3f))
|
||||
if (encodedTrack != (_ltl->logicalCylinder & 0x3f))
|
||||
return;
|
||||
|
||||
uint8_t encodedSector = decode_data_gcr(header[1]);
|
||||
@@ -157,8 +158,8 @@ public:
|
||||
if (encodedSector > 11)
|
||||
return;
|
||||
|
||||
_sector->logicalTrack = _sector->physicalTrack;
|
||||
_sector->logicalSide = decode_side(encodedSide);
|
||||
_sector->logicalCylinder = _ltl->logicalCylinder;
|
||||
_sector->logicalHead = decode_side(encodedSide);
|
||||
_sector->logicalSector = encodedSector;
|
||||
uint8_t gotsum =
|
||||
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
|
||||
@@ -181,10 +181,10 @@ static void write_sector(std::vector<bool>& bits,
|
||||
write_bits(bits, cursor, 0xff3fcff3fcffLL, 6 * 8); /* sync */
|
||||
write_bits(bits, cursor, MAC_SECTOR_RECORD, 3 * 8);
|
||||
|
||||
uint8_t encodedTrack = sector->logicalTrack & 0x3f;
|
||||
uint8_t encodedTrack = sector->logicalCylinder & 0x3f;
|
||||
uint8_t encodedSector = sector->logicalSector;
|
||||
uint8_t encodedSide =
|
||||
encode_side(sector->logicalTrack, sector->logicalSide);
|
||||
encode_side(sector->logicalCylinder, sector->logicalHead);
|
||||
uint8_t formatByte = MAC_FORMAT_BYTE;
|
||||
uint8_t headerChecksum =
|
||||
(encodedTrack ^ encodedSector ^ encodedSide ^ formatByte) & 0x3f;
|
||||
@@ -220,11 +220,11 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
double clockRateUs = clockRateUsForTrack(trackInfo->logicalTrack);
|
||||
double clockRateUs = clockRateUsForTrack(ltl.logicalCylinder);
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/micropolis/micropolis.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "fmt/format.h"
|
||||
@@ -222,14 +223,14 @@ public:
|
||||
if (syncByte != 0xFF)
|
||||
return;
|
||||
|
||||
_sector->logicalTrack = br.read_8();
|
||||
_sector->logicalSide = _sector->physicalSide;
|
||||
_sector->logicalCylinder = br.read_8();
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalSector = br.read_8();
|
||||
if (_sector->logicalSector > 15)
|
||||
return;
|
||||
if (_sector->logicalTrack > 76)
|
||||
if (_sector->logicalCylinder > 76)
|
||||
return;
|
||||
if (_sector->logicalTrack != _sector->physicalTrack)
|
||||
if (_sector->logicalCylinder != _ltl->logicalCylinder)
|
||||
return;
|
||||
|
||||
br.read(10); /* OS data or padding */
|
||||
|
||||
@@ -40,7 +40,7 @@ static void write_sector(std::vector<bool>& bits,
|
||||
{
|
||||
ByteWriter writer(sectorData);
|
||||
writer.write_8(0xff); /* Sync */
|
||||
writer.write_8(sector->logicalTrack);
|
||||
writer.write_8(sector->logicalCylinder);
|
||||
writer.write_8(sector->logicalSector);
|
||||
for (int i = 0; i < 10; i++)
|
||||
writer.write_8(0); /* Padding */
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include <string.h>
|
||||
|
||||
const int SECTOR_SIZE = 256;
|
||||
@@ -64,8 +65,8 @@ public:
|
||||
gotChecksum += br.read_be16();
|
||||
uint16_t wantChecksum = br.read_be16();
|
||||
|
||||
_sector->logicalTrack = _sector->physicalTrack;
|
||||
_sector->logicalSide = _sector->physicalSide;
|
||||
_sector->logicalCylinder = _ltl->logicalCylinder;
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalSector = _currentSector;
|
||||
_sector->data = bytes.slice(0, SECTOR_SIZE).swab();
|
||||
_sector->status =
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "lib/data/fluxpattern.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/northstar/northstar.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "lib/decoders/decoders.pb.h"
|
||||
@@ -159,9 +160,9 @@ public:
|
||||
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
|
||||
ByteReader br(bytes);
|
||||
|
||||
_sector->logicalSide = _sector->physicalSide;
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalSector = _hardSectorId;
|
||||
_sector->logicalTrack = _sector->physicalTrack;
|
||||
_sector->logicalCylinder = _ltl->logicalCylinder;
|
||||
|
||||
if (headerSize == NORTHSTAR_HEADER_SIZE_DD)
|
||||
{
|
||||
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "protocol.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "arch/smaky6/smaky6.h"
|
||||
#include "lib/core/bytes.h"
|
||||
#include "lib/core/crc.h"
|
||||
@@ -129,11 +130,11 @@ public:
|
||||
uint8_t wantedChecksum = br.read_8();
|
||||
uint8_t gotChecksum = sumBytes(data) & 0xff;
|
||||
|
||||
if (track != _sector->physicalTrack)
|
||||
if (track != _ltl->logicalCylinder)
|
||||
return;
|
||||
|
||||
_sector->logicalTrack = _sector->physicalTrack;
|
||||
_sector->logicalSide = _sector->physicalSide;
|
||||
_sector->logicalCylinder = _ltl->physicalCylinder;
|
||||
_sector->logicalHead = _ltl->logicalHead;
|
||||
_sector->logicalSector = _sectorId;
|
||||
|
||||
_sector->data = data;
|
||||
|
||||
@@ -43,8 +43,8 @@ public:
|
||||
|
||||
ByteReader br(bytes);
|
||||
uint8_t track = br.read_8();
|
||||
_sector->logicalTrack = track >> 1;
|
||||
_sector->logicalSide = track & 1;
|
||||
_sector->logicalCylinder = track >> 1;
|
||||
_sector->logicalHead = track & 1;
|
||||
br.skip(1); /* seems always to be 1 */
|
||||
_sector->logicalSector = br.read_8();
|
||||
uint8_t wantChecksum = br.read_8();
|
||||
|
||||
@@ -17,7 +17,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
@@ -83,7 +83,7 @@ private:
|
||||
Bytes bytes;
|
||||
ByteWriter bw(bytes);
|
||||
bw.write_8(
|
||||
(sectorData->logicalTrack << 1) | sectorData->logicalSide);
|
||||
(sectorData->logicalCylinder << 1) | sectorData->logicalHead);
|
||||
bw.write_8(1);
|
||||
bw.write_8(sectorData->logicalSector);
|
||||
bw.write_8(~sumBytes(bytes.slice(0, 3)));
|
||||
|
||||
@@ -64,8 +64,8 @@ public:
|
||||
uint16_t gotChecksum =
|
||||
crc16(CCITT_POLY, bytes.slice(1, TIDS990_SECTOR_RECORD_SIZE - 3));
|
||||
|
||||
_sector->logicalSide = br.read_8() >> 3;
|
||||
_sector->logicalTrack = br.read_8();
|
||||
_sector->logicalHead = br.read_8() >> 3;
|
||||
_sector->logicalCylinder = br.read_8();
|
||||
br.read_8(); /* number of sectors per track */
|
||||
_sector->logicalSector = br.read_8();
|
||||
br.read_be16(); /* sector size */
|
||||
|
||||
@@ -59,7 +59,7 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
@@ -95,8 +95,8 @@ public:
|
||||
|
||||
writeBytes(12, 0x55);
|
||||
bw.write_8(am1Unencoded);
|
||||
bw.write_8(sectorData->logicalSide << 3);
|
||||
bw.write_8(sectorData->logicalTrack);
|
||||
bw.write_8(sectorData->logicalHead << 3);
|
||||
bw.write_8(sectorData->logicalCylinder);
|
||||
bw.write_8(_config.sector_count());
|
||||
bw.write_8(sectorData->logicalSector);
|
||||
bw.write_be16(sectorData->data.size());
|
||||
|
||||
@@ -80,11 +80,11 @@ public:
|
||||
_sector->logicalSector = bytes[1];
|
||||
uint8_t gotChecksum = bytes[2];
|
||||
|
||||
_sector->logicalTrack = rawTrack & 0x7f;
|
||||
_sector->logicalSide = rawTrack >> 7;
|
||||
_sector->logicalCylinder = rawTrack & 0x7f;
|
||||
_sector->logicalHead = rawTrack >> 7;
|
||||
uint8_t wantChecksum = bytes[0] + bytes[1];
|
||||
if ((_sector->logicalSector > 20) || (_sector->logicalTrack > 85) ||
|
||||
(_sector->logicalSide > 1))
|
||||
if ((_sector->logicalSector > 20) || (_sector->logicalCylinder > 85) ||
|
||||
(_sector->logicalHead > 1))
|
||||
return;
|
||||
|
||||
if (wantChecksum == gotChecksum)
|
||||
|
||||
@@ -112,7 +112,7 @@ static void write_sector(std::vector<bool>& bits,
|
||||
write_one_bits(bits, cursor, trackdata.pre_header_sync_bits());
|
||||
write_bits(bits, cursor, VICTOR9K_SECTOR_RECORD, 10);
|
||||
|
||||
uint8_t encodedTrack = sector.logicalTrack | (sector.logicalSide << 7);
|
||||
uint8_t encodedTrack = sector.logicalCylinder | (sector.logicalHead << 7);
|
||||
uint8_t encodedSector = sector.logicalSector;
|
||||
write_bytes(bits,
|
||||
cursor,
|
||||
@@ -164,13 +164,12 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> encode(std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
Victor9kEncoderProto::TrackdataProto trackdata;
|
||||
getTrackFormat(
|
||||
trackdata, trackInfo->logicalTrack, trackInfo->logicalSide);
|
||||
getTrackFormat(trackdata, ltl.logicalCylinder, ltl.logicalHead);
|
||||
|
||||
unsigned bitsPerRevolution = (trackdata.rotational_period_ms() * 1e3) /
|
||||
trackdata.clock_period_us();
|
||||
|
||||
@@ -34,11 +34,11 @@ public:
|
||||
ByteReader br(bytes);
|
||||
|
||||
_sector->logicalSector = br.read_8() & 0x1f;
|
||||
_sector->logicalSide = 0;
|
||||
_sector->logicalTrack = br.read_8() & 0x7f;
|
||||
_sector->logicalHead = 0;
|
||||
_sector->logicalCylinder = br.read_8() & 0x7f;
|
||||
if (_sector->logicalSector > 31)
|
||||
return;
|
||||
if (_sector->logicalTrack > 80)
|
||||
if (_sector->logicalCylinder > 80)
|
||||
return;
|
||||
|
||||
_sector->data = br.read(132);
|
||||
|
||||
6
build.py
6
build.py
@@ -104,15 +104,15 @@ export(
|
||||
name="all",
|
||||
items={
|
||||
"fluxengine$(EXT)": "src+fluxengine",
|
||||
"fluxengine-gui$(EXT)": "src/gui",
|
||||
"fluxengine-gui$(EXT)": "src/gui2",
|
||||
"brother120tool$(EXT)": "tools+brother120tool",
|
||||
"brother240tool$(EXT)": "tools+brother240tool",
|
||||
"upgrade-flux-file$(EXT)": "tools+upgrade-flux-file",
|
||||
}
|
||||
| (
|
||||
{
|
||||
"FluxEngine.pkg": "src/gui+fluxengine_pkg",
|
||||
"FluxEngine.app.zip": "src/gui+fluxengine_app_zip",
|
||||
"FluxEngine.pkg": "src/gui2+fluxengine_pkg",
|
||||
"FluxEngine.app.zip": "src/gui2+fluxengine_app_zip",
|
||||
}
|
||||
if config.osx
|
||||
else {}
|
||||
|
||||
14
build/ab.mk
14
build/ab.mk
@@ -70,7 +70,7 @@ define newline
|
||||
endef
|
||||
|
||||
define check_for_command
|
||||
$(shell command -v $1 >/dev/null || (echo "Required command '$1' missing" >/dev/stderr && kill $$PPID))
|
||||
$(shell command -v $1 >/dev/null || (echo "Required command '$1' missing" >&2 && kill $$PPID))
|
||||
endef
|
||||
|
||||
$(call check_for_command,ninja)
|
||||
@@ -84,12 +84,15 @@ build-file-timestamps = $(shell ls -l $(build-files) | md5sum)
|
||||
# Wipe the build file (forcing a regeneration) if the make environment is different.
|
||||
# (Conveniently, this includes the pkg-config hash calculated above.)
|
||||
|
||||
ignored-variables = MAKE_RESTARTS .VARIABLES MAKECMDGOALS MAKEFLAGS MFLAGS
|
||||
ignored-variables = MAKE_RESTARTS .VARIABLES MAKECMDGOALS MAKEFLAGS MFLAGS PAGER _ \
|
||||
DESKTOP_STARTUP_ID XAUTHORITY ICEAUTHORITY SSH_AUTH_SOCK SESSION_MANAGER \
|
||||
INVOCATION_ID SYSTEMD_EXEC_PID MANAGER_PID SSH_AGENT_PID JOURNAL_STREAM \
|
||||
GPG_TTY WINDOWID MANAGERPID MAKE_TERMOUT MAKE_TERMERR OLDPWD
|
||||
$(shell mkdir -p $(OBJ))
|
||||
$(file >$(OBJ)/newvars.txt,$(foreach v,$(filter-out $(ignored-variables),$(.VARIABLES)),$(v)=$($(v))$(newline)))
|
||||
$(shell touch $(OBJ)/vars.txt)
|
||||
#$(shell diff -u $(OBJ)/vars.txt $(OBJ)/newvars.txt > /dev/stderr)
|
||||
$(shell cmp -s $(OBJ)/newvars.txt $(OBJ)/vars.txt || (rm -f $(OBJ)/build.ninja && echo "Environment changed --- regenerating" > /dev/stderr))
|
||||
#$(shell diff -u $(OBJ)/vars.txt $(OBJ)/newvars.txt >&2)
|
||||
$(shell cmp -s $(OBJ)/newvars.txt $(OBJ)/vars.txt || (rm -f $(OBJ)/build.ninja && echo "Environment changed --- regenerating" >&2))
|
||||
$(shell mv $(OBJ)/newvars.txt $(OBJ)/vars.txt)
|
||||
|
||||
.PHONY: update-ab
|
||||
@@ -104,6 +107,9 @@ clean::
|
||||
@echo CLEAN
|
||||
$(hide) rm -rf $(OBJ)
|
||||
|
||||
compile_commands.json: $(OBJ)/build.ninja
|
||||
+$(hide) $(NINJA) -f $(OBJ)/build.ninja -t compdb > $@
|
||||
|
||||
export PYTHONHASHSEED = 1
|
||||
$(OBJ)/build.ninja $(OBJ)/build.targets &:
|
||||
@echo "AB"
|
||||
|
||||
33
build/ab.py
33
build/ab.py
@@ -558,19 +558,23 @@ def emit_rule(self, ins, outs, cmds=[], label=None):
|
||||
os.makedirs(self.dir, exist_ok=True)
|
||||
rule = []
|
||||
|
||||
sandbox = join(self.dir, "sandbox")
|
||||
emit(f"rm -rf {sandbox}", into=rule)
|
||||
emit(
|
||||
f"{G.PYTHON} build/_sandbox.py --link -s", sandbox, *fins, into=rule
|
||||
)
|
||||
for c in cmds:
|
||||
emit(f"(cd {sandbox} &&", c, ")", into=rule)
|
||||
emit(
|
||||
f"{G.PYTHON} build/_sandbox.py --export -s",
|
||||
sandbox,
|
||||
*fouts,
|
||||
into=rule,
|
||||
)
|
||||
if G.AB_SANDBOX == "yes":
|
||||
sandbox = join(self.dir, "sandbox")
|
||||
emit(f"rm -rf {sandbox}", into=rule)
|
||||
emit(
|
||||
f"{G.PYTHON} build/_sandbox.py --link -s", sandbox, *fins, into=rule
|
||||
)
|
||||
for c in cmds:
|
||||
emit(f"(cd {sandbox} &&", c, ")", into=rule)
|
||||
emit(
|
||||
f"{G.PYTHON} build/_sandbox.py --export -s",
|
||||
sandbox,
|
||||
*fouts,
|
||||
into=rule,
|
||||
)
|
||||
else:
|
||||
for c in cmds:
|
||||
emit(c, into=rule)
|
||||
|
||||
ruletext = "".join(rule)
|
||||
if len(ruletext) > 7000:
|
||||
@@ -581,7 +585,7 @@ def emit_rule(self, ins, outs, cmds=[], label=None):
|
||||
fp.write("set -e\n")
|
||||
fp.write(ruletext)
|
||||
|
||||
emit("build", *fouts, ":rule", *fins, rulef)
|
||||
emit("build", *fouts, ":rule", *fins)
|
||||
emit(" command=sh", rulef)
|
||||
else:
|
||||
emit("build", *fouts, ":rule", *fins)
|
||||
@@ -696,6 +700,7 @@ def main():
|
||||
if "=" in line:
|
||||
name, value = line.split("=", 1)
|
||||
G.setdefault(name.strip(), value.strip())
|
||||
G.setdefault("AB_SANDBOX", "yes")
|
||||
|
||||
global ninjaFp, shellFp, outputdir
|
||||
outputdir = args.outputdir
|
||||
|
||||
@@ -4,11 +4,13 @@ from os.path import join, abspath, dirname, relpath
|
||||
from build.pkg import has_package
|
||||
|
||||
G.setdefault("PROTOC", "protoc")
|
||||
G.setdefault("PROTOC_SEPARATOR", ":")
|
||||
G.setdefault("HOSTPROTOC", "hostprotoc")
|
||||
|
||||
assert has_package("protobuf"), "required package 'protobuf' not installed"
|
||||
|
||||
|
||||
|
||||
def _getprotodeps(deps):
|
||||
r = set()
|
||||
for d in deps:
|
||||
@@ -19,7 +21,7 @@ def _getprotodeps(deps):
|
||||
@Rule
|
||||
def proto(self, name, srcs: Targets = [], deps: Targets = []):
|
||||
protodeps = _getprotodeps(deps)
|
||||
descriptorlist = ":".join(
|
||||
descriptorlist = (G.PROTOC_SEPARATOR).join(
|
||||
[
|
||||
relpath(f, start=self.dir)
|
||||
for f in filenamesmatchingof(protodeps, "*.descriptor")
|
||||
@@ -46,7 +48,7 @@ def proto(self, name, srcs: Targets = [], deps: Targets = []):
|
||||
f"--descriptor_set_out={self.localname}.descriptor",
|
||||
]
|
||||
+ (
|
||||
[f"--descriptor_set_in={descriptorlist}"]
|
||||
[f"--descriptor_set_in='{descriptorlist}'"]
|
||||
if descriptorlist
|
||||
else []
|
||||
)
|
||||
@@ -89,7 +91,7 @@ def protocc(self, name, srcs: Targets = [], deps: Targets = []):
|
||||
outs += ["=" + cc, "=" + h]
|
||||
|
||||
protodeps = _getprotodeps(deps + srcs)
|
||||
descriptorlist = ":".join(
|
||||
descriptorlist = G.PROTOC_SEPARATOR.join(
|
||||
[
|
||||
relpath(f, start=self.dir)
|
||||
for f in filenamesmatchingof(protodeps, "*.descriptor")
|
||||
@@ -110,7 +112,7 @@ def protocc(self, name, srcs: Targets = [], deps: Targets = []):
|
||||
"$(PROTOC)",
|
||||
"--proto_path=.",
|
||||
"--cpp_out=.",
|
||||
f"--descriptor_set_in={descriptorlist}",
|
||||
f"--descriptor_set_in='{descriptorlist}'",
|
||||
]
|
||||
+ protos
|
||||
)
|
||||
|
||||
1
dep/cli11
Submodule
1
dep/cli11
Submodule
Submodule dep/cli11 added at 89dc726939
1
dep/imgui
Submodule
1
dep/imgui
Submodule
Submodule dep/imgui added at 4d216d4510
1
dep/imhex
Submodule
1
dep/imhex
Submodule
Submodule dep/imhex added at a76eae2c11
1
dep/libromfs
Submodule
1
dep/libromfs
Submodule
Submodule dep/libromfs added at fa444f2995
1
dep/libwolv
Submodule
1
dep/libwolv
Submodule
Submodule dep/libwolv added at 56f77945fe
1
dep/lunasvg
Submodule
1
dep/lunasvg
Submodule
Submodule dep/lunasvg added at 83c58df810
1
dep/md4c
Submodule
1
dep/md4c
Submodule
Submodule dep/md4c added at 481fbfbdf7
1
dep/native-file-dialog
Submodule
1
dep/native-file-dialog
Submodule
Submodule dep/native-file-dialog added at 6db343ad34
1
dep/nlohmann_json
Submodule
1
dep/nlohmann_json
Submodule
Submodule dep/nlohmann_json added at 44bee1b138
1
dep/pattern-language
Submodule
1
dep/pattern-language
Submodule
Submodule dep/pattern-language added at f97999d4da
1
dep/throwing_ptr
Submodule
1
dep/throwing_ptr
Submodule
Submodule dep/throwing_ptr added at cd28490ebf
1
dep/xdgpp
Submodule
1
dep/xdgpp
Submodule
Submodule dep/xdgpp added at f01f810714
47
doc/disk-juku.md
Normal file
47
doc/disk-juku.md
Normal file
@@ -0,0 +1,47 @@
|
||||
juku
|
||||
====
|
||||
## CP/M
|
||||
<!-- This file is automatically generated. Do not edit. -->
|
||||
|
||||
Juku E5104 is an Estonian school computer from late 1980s and
|
||||
early 1990s. It was designed by EKTA in 1985, and starting
|
||||
from 1988 produced in Narva "Baltijets" factory. Arguably
|
||||
the school computer was technically outdated already when
|
||||
released, but still occupies a precious spot in the memories
|
||||
of a whole generation of Estonian IT professionals.
|
||||
|
||||
The system uses dual 5.25 inch 2ce9
|
||||
diskette drive with regular MFM encoded DSDD. The disks have
|
||||
a sector skew factor 2 and tracks are written on one side of
|
||||
the floppy until it is full and then continued on the other
|
||||
side, starting from the outside of the disk again. This differs
|
||||
from the most common alternating sides method and somewhat
|
||||
complicates reading CP/M filesystem content with common tools.
|
||||
|
||||
Mostly 800kB (786kB) DSDD disks were used, but there are also
|
||||
400kB (386kB) SSDD floppies in circulation.
|
||||
|
||||
## References (all in Estonian)
|
||||
|
||||
- [How to read/write Juku disk images?](https://j3k.infoaed.ee/kettad/)
|
||||
- [List of recovered Juku software](https://j3k.infoaed.ee/tarkvara-kataloog/)
|
||||
- [System disks for E5104](https://elektroonikamuuseum.ee/juku_arvuti_tarkvara.html)
|
||||
|
||||
## Options
|
||||
|
||||
- Format variants:
|
||||
- `800`: 800kB 80-track 10-sector DSDD
|
||||
- `400`: 400kB 80-track 10-sector SSDD
|
||||
|
||||
## Examples
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read -c juku --800 -s drive:0 -o image.juk`
|
||||
- `fluxengine read -c juku --400 -s drive:0 -o image.juk`
|
||||
|
||||
To write:
|
||||
|
||||
- `fluxengine write -c juku --800 -d drive:0 -i image.juk`
|
||||
- `fluxengine write -c juku --400 -d drive:0 -i image.juk`
|
||||
|
||||
@@ -52,7 +52,7 @@ need to apply extra options to change the format if desired.
|
||||
|
||||
## Options
|
||||
|
||||
- :
|
||||
- $format:
|
||||
- `143`: 143kB 5.25" SSDD hard-sectored; Micropolis MetaFloppy Mod I
|
||||
- `287`: 287kB 5.25" DSDD hard-sectored; Micropolis MetaFloppy Mod I
|
||||
- `315`: 315kB 5.25" SSDD hard-sectored; Micropolis MetaFloppy Mod II
|
||||
|
||||
26
doc/disk-ti99.md
Normal file
26
doc/disk-ti99.md
Normal file
@@ -0,0 +1,26 @@
|
||||
ti99
|
||||
====
|
||||
## 90kB 35-track SSSD
|
||||
<!-- This file is automatically generated. Do not edit. -->
|
||||
|
||||
The TI-99 was a deeply weird microcomputer from 1981, whose main claim to fame
|
||||
was being built around a 16-bit TMS9900 CPU --- and also having only 256 bytes
|
||||
of system RAM, with an additional 16kB of video RAM, requiring the BASIC to
|
||||
store the user's program in video RAM.
|
||||
|
||||
It had an optional rack-mount expansion system with an optional disk drive. This
|
||||
was controlled by a standard FD1771 or FD179x chip, meaning a relatively normal
|
||||
IBM-scheme disk format of 35 tracks containing nine 256-byte sectors.
|
||||
|
||||
FluxEngine can read these.
|
||||
|
||||
## Options
|
||||
|
||||
(no options)
|
||||
|
||||
## Examples
|
||||
|
||||
To read:
|
||||
|
||||
- `fluxengine read -c ti99 -s drive:0 -o ti99.img`
|
||||
|
||||
@@ -385,9 +385,8 @@ disks, and have different magnetic properties. 3.5" drives can usually
|
||||
autodetect what kind of medium is inserted into the drive based on the hole in
|
||||
the disk casing, but 5.25" drives can't. As a result, you need to explicitly
|
||||
tell FluxEngine on the command line whether you're using a high density disk or
|
||||
not with the `--drive.high_density` configuration setting.
|
||||
**If you don't do this, your disks may not read correctly and will _certainly_
|
||||
fail to write correctly.**
|
||||
not with the `--hd` configuration setting. **If you don't do this, your disks
|
||||
may not read correctly and will _certainly_ fail to write correctly.**
|
||||
|
||||
You can distinguish high density 5.25" floppies from the presence of a
|
||||
traction ring around the hole in the middle of the disk; if the ring is not
|
||||
|
||||
@@ -84,16 +84,12 @@ void renderLogMessage(
|
||||
void renderLogMessage(
|
||||
LogRenderer& r, std::shared_ptr<const TrackReadLogMessage> m)
|
||||
{
|
||||
const auto& track = *m->track;
|
||||
|
||||
std::set<std::shared_ptr<const Sector>> rawSectors;
|
||||
std::set<std::shared_ptr<const Record>> rawRecords;
|
||||
for (const auto& trackDataFlux : track.trackDatas)
|
||||
for (const auto& track : m->tracks)
|
||||
{
|
||||
rawSectors.insert(
|
||||
trackDataFlux->sectors.begin(), trackDataFlux->sectors.end());
|
||||
rawRecords.insert(
|
||||
trackDataFlux->records.begin(), trackDataFlux->records.end());
|
||||
rawSectors.insert(track->allSectors.begin(), track->allSectors.end());
|
||||
rawRecords.insert(track->records.begin(), track->records.end());
|
||||
}
|
||||
|
||||
nanoseconds_t clock = 0;
|
||||
@@ -114,22 +110,22 @@ void renderLogMessage(
|
||||
r.newline().add("sectors:");
|
||||
|
||||
std::vector<std::shared_ptr<const Sector>> sectors(
|
||||
track.sectors.begin(), track.sectors.end());
|
||||
m->sectors.begin(), m->sectors.end());
|
||||
std::sort(sectors.begin(), sectors.end(), sectorPointerSortPredicate);
|
||||
|
||||
for (const auto& sector : sectors)
|
||||
for (const auto& sector : rawSectors)
|
||||
r.add(fmt::format("{}.{}.{}{}",
|
||||
sector->logicalTrack,
|
||||
sector->logicalSide,
|
||||
sector->logicalCylinder,
|
||||
sector->logicalHead,
|
||||
sector->logicalSector,
|
||||
Sector::statusToChar(sector->status)));
|
||||
|
||||
int size = 0;
|
||||
std::set<std::pair<int, int>> track_ids;
|
||||
for (const auto& sector : m->track->sectors)
|
||||
for (const auto& sector : m->sectors)
|
||||
{
|
||||
track_ids.insert(
|
||||
std::make_pair(sector->logicalTrack, sector->logicalSide));
|
||||
std::make_pair(sector->logicalCylinder, sector->logicalHead));
|
||||
size += sector->data.size();
|
||||
}
|
||||
|
||||
@@ -184,12 +180,16 @@ private:
|
||||
_cache;
|
||||
};
|
||||
|
||||
void measureDiskRotation()
|
||||
static nanoseconds_t getRotationalPeriodFromConfig()
|
||||
{
|
||||
return globalConfig()->drive().rotational_period_ms() * 1e6;
|
||||
}
|
||||
|
||||
static nanoseconds_t measureDiskRotation()
|
||||
{
|
||||
log(BeginSpeedOperationLogMessage());
|
||||
|
||||
nanoseconds_t oneRevolution =
|
||||
globalConfig()->drive().rotational_period_ms() * 1e6;
|
||||
nanoseconds_t oneRevolution = getRotationalPeriodFromConfig();
|
||||
if (oneRevolution == 0)
|
||||
{
|
||||
usbSetDrive(globalConfig()->drive().drive(),
|
||||
@@ -224,22 +224,24 @@ void measureDiskRotation()
|
||||
error("Failed\nIs a disk in the drive?");
|
||||
|
||||
log(EndSpeedOperationLogMessage{oneRevolution});
|
||||
return oneRevolution;
|
||||
}
|
||||
|
||||
/* Given a set of sectors, deduplicates them sensibly (e.g. if there is a good
|
||||
* and bad version of the same sector, the bad version is dropped). */
|
||||
|
||||
static std::set<std::shared_ptr<const Sector>> collectSectors(
|
||||
std::set<std::shared_ptr<const Sector>>& track_sectors,
|
||||
static std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
std::vector<std::shared_ptr<const Sector>>& trackSectors,
|
||||
bool collapse_conflicts = true)
|
||||
{
|
||||
typedef std::tuple<unsigned, unsigned, unsigned> key_t;
|
||||
std::multimap<key_t, std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
for (const auto& sector : track_sectors)
|
||||
for (const auto& sector : trackSectors)
|
||||
{
|
||||
key_t sectorid = {
|
||||
sector->logicalTrack, sector->logicalSide, sector->logicalSector};
|
||||
key_t sectorid = {sector->logicalCylinder,
|
||||
sector->logicalHead,
|
||||
sector->logicalSector};
|
||||
sectors.insert({sectorid, sector});
|
||||
}
|
||||
|
||||
@@ -281,42 +283,52 @@ static std::set<std::shared_ptr<const Sector>> collectSectors(
|
||||
sector_set.insert(new_sector);
|
||||
it = ub;
|
||||
}
|
||||
return sector_set;
|
||||
return sector_set | std::ranges::to<std::vector>();
|
||||
}
|
||||
|
||||
BadSectorsState combineRecordAndSectors(TrackFlux& trackFlux,
|
||||
Decoder& decoder,
|
||||
std::shared_ptr<const TrackInfo>& trackLayout)
|
||||
struct CombinationResult
|
||||
{
|
||||
std::set<std::shared_ptr<const Sector>> track_sectors;
|
||||
BadSectorsState result;
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
};
|
||||
|
||||
static CombinationResult combineRecordAndSectors(
|
||||
std::vector<std::shared_ptr<const Track>>& tracks,
|
||||
Decoder& decoder,
|
||||
const std::shared_ptr<const LogicalTrackLayout>& ltl)
|
||||
{
|
||||
CombinationResult cr = {HAS_NO_BAD_SECTORS};
|
||||
std::vector<std::shared_ptr<const Sector>> track_sectors;
|
||||
|
||||
/* Add the sectors which were there. */
|
||||
|
||||
for (auto& trackdataflux : trackFlux.trackDatas)
|
||||
track_sectors.insert(
|
||||
trackdataflux->sectors.begin(), trackdataflux->sectors.end());
|
||||
for (auto& track : tracks)
|
||||
for (auto& sector : track->allSectors)
|
||||
track_sectors.push_back(sector);
|
||||
|
||||
/* Add the sectors which should be there. */
|
||||
|
||||
for (unsigned sectorId : trackLayout->naturalSectorOrder)
|
||||
for (unsigned sectorId : ltl->diskSectorOrder)
|
||||
{
|
||||
auto sector = std::make_shared<Sector>(LogicalLocation{
|
||||
trackLayout->logicalTrack, trackLayout->logicalSide, sectorId});
|
||||
auto sector = std::make_shared<Sector>(
|
||||
LogicalLocation{ltl->logicalCylinder, ltl->logicalHead, sectorId});
|
||||
|
||||
sector->status = Sector::MISSING;
|
||||
track_sectors.insert(sector);
|
||||
sector->physicalLocation = std::make_optional(
|
||||
CylinderHead(ltl->physicalCylinder, ltl->physicalHead));
|
||||
track_sectors.push_back(sector);
|
||||
}
|
||||
|
||||
/* Deduplicate. */
|
||||
|
||||
trackFlux.sectors = collectSectors(track_sectors);
|
||||
if (trackFlux.sectors.empty())
|
||||
return HAS_BAD_SECTORS;
|
||||
for (const auto& sector : trackFlux.sectors)
|
||||
cr.sectors = collectSectors(track_sectors);
|
||||
if (cr.sectors.empty())
|
||||
cr.result = HAS_BAD_SECTORS;
|
||||
for (const auto& sector : cr.sectors)
|
||||
if (sector->status != Sector::OK)
|
||||
return HAS_BAD_SECTORS;
|
||||
cr.result = HAS_BAD_SECTORS;
|
||||
|
||||
return HAS_NO_BAD_SECTORS;
|
||||
return cr;
|
||||
}
|
||||
|
||||
static void adjustTrackOnError(FluxSource& fluxSource, int baseTrack)
|
||||
@@ -339,179 +351,226 @@ static void adjustTrackOnError(FluxSource& fluxSource, int baseTrack)
|
||||
}
|
||||
}
|
||||
|
||||
ReadResult readGroup(FluxSourceIteratorHolder& fluxSourceIteratorHolder,
|
||||
std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
TrackFlux& trackFlux,
|
||||
struct ReadGroupResult
|
||||
{
|
||||
ReadResult result;
|
||||
std::vector<std::shared_ptr<const Sector>> combinedSectors;
|
||||
};
|
||||
|
||||
static ReadGroupResult readGroup(const DiskLayout& diskLayout,
|
||||
FluxSourceIteratorHolder& fluxSourceIteratorHolder,
|
||||
const std::shared_ptr<const LogicalTrackLayout>& ltl,
|
||||
std::vector<std::shared_ptr<const Track>>& tracks,
|
||||
Decoder& decoder)
|
||||
{
|
||||
ReadResult result = BAD_AND_CAN_NOT_RETRY;
|
||||
ReadGroupResult rgr = {BAD_AND_CAN_NOT_RETRY};
|
||||
|
||||
/* Before doing the read, look to see if we already have the necessary
|
||||
* sectors. */
|
||||
|
||||
for (unsigned offset = 0; offset < trackInfo->groupSize;
|
||||
offset += Layout::getHeadWidth())
|
||||
{
|
||||
log(BeginReadOperationLogMessage{
|
||||
trackInfo->physicalTrack + offset, trackInfo->physicalSide});
|
||||
auto [result, sectors] = combineRecordAndSectors(tracks, decoder, ltl);
|
||||
rgr.combinedSectors = sectors;
|
||||
if (result == HAS_NO_BAD_SECTORS)
|
||||
{
|
||||
/* We have all necessary sectors, so can stop here. */
|
||||
rgr.result = GOOD_READ;
|
||||
if (globalConfig()->decoder().skip_unnecessary_tracks())
|
||||
return rgr;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned offset = 0; offset < ltl->groupSize;
|
||||
offset += diskLayout.headWidth)
|
||||
{
|
||||
unsigned physicalCylinder = ltl->physicalCylinder + offset;
|
||||
unsigned physicalHead = ltl->physicalHead;
|
||||
auto& ptl = diskLayout.layoutByPhysicalLocation.at(
|
||||
{physicalCylinder, physicalHead});
|
||||
|
||||
/* Do the physical read. */
|
||||
|
||||
log(BeginReadOperationLogMessage{physicalCylinder, physicalHead});
|
||||
|
||||
auto& fluxSourceIterator = fluxSourceIteratorHolder.getIterator(
|
||||
trackInfo->physicalTrack + offset, trackInfo->physicalSide);
|
||||
physicalCylinder, physicalHead);
|
||||
if (!fluxSourceIterator.hasNext())
|
||||
continue;
|
||||
|
||||
std::shared_ptr<const Fluxmap> fluxmap = fluxSourceIterator.next();
|
||||
// ->rescale(
|
||||
// 1.0 / globalConfig()->flux_source().rescale());
|
||||
auto fluxmap = fluxSourceIterator.next();
|
||||
log(EndReadOperationLogMessage());
|
||||
log("{0} ms in {1} bytes",
|
||||
(int)(fluxmap->duration() / 1e6),
|
||||
fluxmap->bytes());
|
||||
|
||||
auto trackdataflux = decoder.decodeToSectors(fluxmap, trackInfo);
|
||||
trackFlux.trackDatas.push_back(trackdataflux);
|
||||
if (combineRecordAndSectors(trackFlux, decoder, trackInfo) ==
|
||||
HAS_NO_BAD_SECTORS)
|
||||
auto flux = decoder.decodeToSectors(std::move(fluxmap), ptl);
|
||||
flux->normalisedSectors = collectSectors(flux->allSectors);
|
||||
tracks.push_back(flux);
|
||||
|
||||
/* Decode what we've got so far. */
|
||||
|
||||
auto [result, sectors] = combineRecordAndSectors(tracks, decoder, ltl);
|
||||
rgr.combinedSectors = sectors;
|
||||
if (result == HAS_NO_BAD_SECTORS)
|
||||
{
|
||||
result = GOOD_READ;
|
||||
/* We have all necessary sectors, so can stop here. */
|
||||
rgr.result = GOOD_READ;
|
||||
if (globalConfig()->decoder().skip_unnecessary_tracks())
|
||||
return result;
|
||||
break;
|
||||
}
|
||||
else if (fluxSourceIterator.hasNext())
|
||||
result = BAD_AND_CAN_RETRY;
|
||||
{
|
||||
/* The flux source claims it can do more reads, so mark this
|
||||
* group as being retryable. */
|
||||
rgr.result = BAD_AND_CAN_RETRY;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return rgr;
|
||||
}
|
||||
|
||||
void writeTracks(FluxSink& fluxSink,
|
||||
void writeTracks(const DiskLayout& diskLayout,
|
||||
|
||||
FluxSinkFactory& fluxSinkFactory,
|
||||
std::function<std::unique_ptr<const Fluxmap>(
|
||||
std::shared_ptr<const TrackInfo>& trackInfo)> producer,
|
||||
std::function<bool(std::shared_ptr<const TrackInfo>& trackInfo)> verifier,
|
||||
std::vector<std::shared_ptr<const TrackInfo>>& trackInfos)
|
||||
const std::shared_ptr<const LogicalTrackLayout>&)> producer,
|
||||
std::function<bool(const std::shared_ptr<const LogicalTrackLayout>&)>
|
||||
verifier,
|
||||
const std::vector<CylinderHead>& logicalLocations)
|
||||
{
|
||||
log(BeginOperationLogMessage{"Encoding and writing to disk"});
|
||||
|
||||
if (fluxSink.isHardware())
|
||||
if (fluxSinkFactory.isHardware())
|
||||
measureDiskRotation();
|
||||
int index = 0;
|
||||
for (auto& trackInfo : trackInfos)
|
||||
{
|
||||
log(OperationProgressLogMessage{
|
||||
index * 100 / (unsigned)trackInfos.size()});
|
||||
index++;
|
||||
|
||||
testForEmergencyStop();
|
||||
|
||||
int retriesRemaining = globalConfig()->decoder().retries();
|
||||
for (;;)
|
||||
auto fluxSink = fluxSinkFactory.create();
|
||||
int index = 0;
|
||||
for (auto& ch : logicalLocations)
|
||||
{
|
||||
for (int offset = 0; offset < trackInfo->groupSize;
|
||||
offset += Layout::getHeadWidth())
|
||||
log(OperationProgressLogMessage{
|
||||
index * 100 / (unsigned)logicalLocations.size()});
|
||||
index++;
|
||||
|
||||
testForEmergencyStop();
|
||||
|
||||
const auto& ltl = diskLayout.layoutByLogicalLocation.at(ch);
|
||||
int retriesRemaining = globalConfig()->decoder().retries();
|
||||
for (;;)
|
||||
{
|
||||
unsigned physicalTrack = trackInfo->physicalTrack + offset;
|
||||
|
||||
log(BeginWriteOperationLogMessage{
|
||||
physicalTrack, trackInfo->physicalSide});
|
||||
|
||||
if (offset == globalConfig()->drive().group_offset())
|
||||
for (int offset = 0; offset < ltl->groupSize;
|
||||
offset += diskLayout.headWidth)
|
||||
{
|
||||
auto fluxmap = producer(trackInfo);
|
||||
if (!fluxmap)
|
||||
goto erase;
|
||||
unsigned physicalCylinder = ltl->physicalCylinder + offset;
|
||||
unsigned physicalHead = ltl->physicalHead;
|
||||
|
||||
fluxSink.writeFlux(
|
||||
physicalTrack, trackInfo->physicalSide, *fluxmap);
|
||||
log("writing {0} ms in {1} bytes",
|
||||
int(fluxmap->duration() / 1e6),
|
||||
fluxmap->bytes());
|
||||
}
|
||||
else
|
||||
{
|
||||
erase:
|
||||
/* Erase this track rather than writing. */
|
||||
log(BeginWriteOperationLogMessage{
|
||||
physicalCylinder, ltl->physicalHead});
|
||||
|
||||
Fluxmap blank;
|
||||
fluxSink.writeFlux(
|
||||
physicalTrack, trackInfo->physicalSide, blank);
|
||||
log("erased");
|
||||
if (offset == globalConfig()->drive().group_offset())
|
||||
{
|
||||
auto fluxmap = producer(ltl);
|
||||
if (!fluxmap)
|
||||
goto erase;
|
||||
|
||||
fluxSink->addFlux(
|
||||
physicalCylinder, physicalHead, *fluxmap);
|
||||
log("writing {0} ms in {1} bytes",
|
||||
int(fluxmap->duration() / 1e6),
|
||||
fluxmap->bytes());
|
||||
}
|
||||
else
|
||||
{
|
||||
erase:
|
||||
/* Erase this track rather than writing. */
|
||||
|
||||
Fluxmap blank;
|
||||
fluxSink->addFlux(
|
||||
physicalCylinder, physicalHead, blank);
|
||||
log("erased");
|
||||
}
|
||||
|
||||
log(EndWriteOperationLogMessage());
|
||||
}
|
||||
|
||||
log(EndWriteOperationLogMessage());
|
||||
if (verifier(ltl))
|
||||
break;
|
||||
|
||||
if (retriesRemaining == 0)
|
||||
error("fatal error on write");
|
||||
|
||||
log("retrying; {} retries remaining", retriesRemaining);
|
||||
retriesRemaining--;
|
||||
}
|
||||
|
||||
if (verifier(trackInfo))
|
||||
break;
|
||||
|
||||
if (retriesRemaining == 0)
|
||||
error("fatal error on write");
|
||||
|
||||
log("retrying; {} retries remaining", retriesRemaining);
|
||||
retriesRemaining--;
|
||||
}
|
||||
}
|
||||
|
||||
log(EndOperationLogMessage{"Write complete"});
|
||||
}
|
||||
|
||||
void writeTracks(FluxSink& fluxSink,
|
||||
void writeTracks(const DiskLayout& diskLayout,
|
||||
FluxSinkFactory& fluxSinkFactory,
|
||||
Encoder& encoder,
|
||||
const Image& image,
|
||||
std::vector<std::shared_ptr<const TrackInfo>>& trackInfos)
|
||||
const std::vector<CylinderHead>& chs)
|
||||
{
|
||||
writeTracks(
|
||||
fluxSink,
|
||||
[&](std::shared_ptr<const TrackInfo>& trackInfo)
|
||||
diskLayout,
|
||||
fluxSinkFactory,
|
||||
[&](const std::shared_ptr<const LogicalTrackLayout>& ltl)
|
||||
{
|
||||
auto sectors = encoder.collectSectors(trackInfo, image);
|
||||
return encoder.encode(trackInfo, sectors, image);
|
||||
auto sectors = encoder.collectSectors(*ltl, image);
|
||||
return encoder.encode(*ltl, sectors, image);
|
||||
},
|
||||
[](const auto&)
|
||||
{
|
||||
return true;
|
||||
},
|
||||
trackInfos);
|
||||
chs);
|
||||
}
|
||||
|
||||
void writeTracksAndVerify(FluxSink& fluxSink,
|
||||
void writeTracksAndVerify(const DiskLayout& diskLayout,
|
||||
FluxSinkFactory& fluxSinkFactory,
|
||||
Encoder& encoder,
|
||||
FluxSource& fluxSource,
|
||||
Decoder& decoder,
|
||||
const Image& image,
|
||||
std::vector<std::shared_ptr<const TrackInfo>>& trackInfos)
|
||||
const std::vector<CylinderHead>& chs)
|
||||
{
|
||||
writeTracks(
|
||||
fluxSink,
|
||||
[&](std::shared_ptr<const TrackInfo>& trackInfo)
|
||||
diskLayout,
|
||||
fluxSinkFactory,
|
||||
[&](const std::shared_ptr<const LogicalTrackLayout>& ltl)
|
||||
{
|
||||
auto sectors = encoder.collectSectors(trackInfo, image);
|
||||
return encoder.encode(trackInfo, sectors, image);
|
||||
auto sectors = encoder.collectSectors(*ltl, image);
|
||||
return encoder.encode(*ltl, sectors, image);
|
||||
},
|
||||
[&](std::shared_ptr<const TrackInfo>& trackInfo)
|
||||
[&](const std::shared_ptr<const LogicalTrackLayout>& ltl)
|
||||
{
|
||||
auto trackFlux = std::make_shared<TrackFlux>();
|
||||
trackFlux->trackInfo = trackInfo;
|
||||
FluxSourceIteratorHolder fluxSourceIteratorHolder(fluxSource);
|
||||
auto result = readGroup(
|
||||
fluxSourceIteratorHolder, trackInfo, *trackFlux, decoder);
|
||||
log(TrackReadLogMessage{trackFlux});
|
||||
std::vector<std::shared_ptr<const Track>> tracks;
|
||||
auto [result, sectors] = readGroup(
|
||||
diskLayout, fluxSourceIteratorHolder, ltl, tracks, decoder);
|
||||
log(TrackReadLogMessage{tracks, sectors});
|
||||
|
||||
if (result != GOOD_READ)
|
||||
{
|
||||
adjustTrackOnError(fluxSource, trackInfo->physicalTrack);
|
||||
adjustTrackOnError(fluxSource, ltl->physicalCylinder);
|
||||
log("bad read");
|
||||
return false;
|
||||
}
|
||||
|
||||
Image wanted;
|
||||
for (const auto& sector : encoder.collectSectors(trackInfo, image))
|
||||
for (const auto& sector : encoder.collectSectors(*ltl, image))
|
||||
wanted
|
||||
.put(sector->logicalTrack,
|
||||
sector->logicalSide,
|
||||
.put(sector->logicalCylinder,
|
||||
sector->logicalHead,
|
||||
sector->logicalSector)
|
||||
->data = sector->data;
|
||||
|
||||
for (const auto& sector : trackFlux->sectors)
|
||||
for (const auto& sector : sectors)
|
||||
{
|
||||
const auto s = wanted.get(sector->logicalTrack,
|
||||
sector->logicalSide,
|
||||
const auto s = wanted.get(sector->logicalCylinder,
|
||||
sector->logicalHead,
|
||||
sector->logicalSector);
|
||||
if (!s)
|
||||
{
|
||||
@@ -523,8 +582,8 @@ void writeTracksAndVerify(FluxSink& fluxSink,
|
||||
log("data mismatch on verify");
|
||||
return false;
|
||||
}
|
||||
wanted.erase(sector->logicalTrack,
|
||||
sector->logicalSide,
|
||||
wanted.erase(sector->logicalCylinder,
|
||||
sector->logicalHead,
|
||||
sector->logicalSector);
|
||||
}
|
||||
if (!wanted.empty())
|
||||
@@ -534,60 +593,75 @@ void writeTracksAndVerify(FluxSink& fluxSink,
|
||||
}
|
||||
return true;
|
||||
},
|
||||
trackInfos);
|
||||
chs);
|
||||
}
|
||||
|
||||
void writeDiskCommand(const Image& image,
|
||||
void writeDiskCommand(const DiskLayout& diskLayout,
|
||||
const Image& image,
|
||||
Encoder& encoder,
|
||||
FluxSink& fluxSink,
|
||||
FluxSinkFactory& fluxSinkFactory,
|
||||
Decoder* decoder,
|
||||
FluxSource* fluxSource,
|
||||
const std::vector<CylinderHead>& physicalLocations)
|
||||
{
|
||||
auto trackinfos = Layout::getLayoutOfTracksPhysical(physicalLocations);
|
||||
auto chs = std::ranges::views::keys(diskLayout.layoutByLogicalLocation) |
|
||||
std::ranges::to<std::vector>();
|
||||
if (fluxSource && decoder)
|
||||
writeTracksAndVerify(
|
||||
fluxSink, encoder, *fluxSource, *decoder, image, trackinfos);
|
||||
writeTracksAndVerify(diskLayout,
|
||||
fluxSinkFactory,
|
||||
encoder,
|
||||
*fluxSource,
|
||||
*decoder,
|
||||
image,
|
||||
chs);
|
||||
else
|
||||
writeTracks(fluxSink, encoder, image, trackinfos);
|
||||
writeTracks(diskLayout, fluxSinkFactory, encoder, image, chs);
|
||||
}
|
||||
|
||||
void writeDiskCommand(const Image& image,
|
||||
void writeDiskCommand(const DiskLayout& diskLayout,
|
||||
const Image& image,
|
||||
Encoder& encoder,
|
||||
FluxSink& fluxSink,
|
||||
FluxSinkFactory& fluxSinkFactory,
|
||||
Decoder* decoder,
|
||||
FluxSource* fluxSource)
|
||||
{
|
||||
auto locations = Layout::computePhysicalLocations();
|
||||
writeDiskCommand(image, encoder, fluxSink, decoder, fluxSource, locations);
|
||||
writeDiskCommand(diskLayout,
|
||||
image,
|
||||
encoder,
|
||||
fluxSinkFactory,
|
||||
decoder,
|
||||
fluxSource,
|
||||
std::ranges::views::keys(diskLayout.layoutByLogicalLocation) |
|
||||
std::ranges::to<std::vector>());
|
||||
}
|
||||
|
||||
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
|
||||
void writeRawDiskCommand(const DiskLayout& diskLayout,
|
||||
FluxSource& fluxSource,
|
||||
FluxSinkFactory& fluxSinkFactory)
|
||||
{
|
||||
auto physicalLocations = Layout::computePhysicalLocations();
|
||||
auto trackinfos = Layout::getLayoutOfTracksPhysical(physicalLocations);
|
||||
writeTracks(
|
||||
fluxSink,
|
||||
[&](std::shared_ptr<const TrackInfo>& trackInfo)
|
||||
diskLayout,
|
||||
fluxSinkFactory,
|
||||
[&](const std::shared_ptr<const LogicalTrackLayout>& ltl)
|
||||
{
|
||||
return fluxSource
|
||||
.readFlux(trackInfo->physicalTrack, trackInfo->physicalSide)
|
||||
.readFlux(ltl->physicalCylinder, ltl->physicalHead)
|
||||
->next();
|
||||
},
|
||||
[](const auto&)
|
||||
{
|
||||
return true;
|
||||
},
|
||||
trackinfos);
|
||||
diskLayout.logicalLocations);
|
||||
}
|
||||
|
||||
std::shared_ptr<TrackFlux> readAndDecodeTrack(FluxSource& fluxSource,
|
||||
void readAndDecodeTrack(const DiskLayout& diskLayout,
|
||||
FluxSource& fluxSource,
|
||||
Decoder& decoder,
|
||||
std::shared_ptr<const TrackInfo>& trackInfo)
|
||||
const std::shared_ptr<const LogicalTrackLayout>& ltl,
|
||||
std::vector<std::shared_ptr<const Track>>& tracks,
|
||||
std::vector<std::shared_ptr<const Sector>>& combinedSectors)
|
||||
{
|
||||
auto trackFlux = std::make_shared<TrackFlux>();
|
||||
trackFlux->trackInfo = trackInfo;
|
||||
|
||||
if (fluxSource.isHardware())
|
||||
measureDiskRotation();
|
||||
|
||||
@@ -595,8 +669,9 @@ std::shared_ptr<TrackFlux> readAndDecodeTrack(FluxSource& fluxSource,
|
||||
int retriesRemaining = globalConfig()->decoder().retries();
|
||||
for (;;)
|
||||
{
|
||||
auto result =
|
||||
readGroup(fluxSourceIteratorHolder, trackInfo, *trackFlux, decoder);
|
||||
auto [result, sectors] = readGroup(
|
||||
diskLayout, fluxSourceIteratorHolder, ltl, tracks, decoder);
|
||||
combinedSectors = sectors;
|
||||
if (result == GOOD_READ)
|
||||
break;
|
||||
if (result == BAD_AND_CAN_NOT_RETRY)
|
||||
@@ -613,131 +688,172 @@ std::shared_ptr<TrackFlux> readAndDecodeTrack(FluxSource& fluxSource,
|
||||
|
||||
if (fluxSource.isHardware())
|
||||
{
|
||||
adjustTrackOnError(fluxSource, trackInfo->physicalTrack);
|
||||
adjustTrackOnError(fluxSource, ltl->physicalCylinder);
|
||||
log("retrying; {} retries remaining", retriesRemaining);
|
||||
retriesRemaining--;
|
||||
}
|
||||
}
|
||||
|
||||
return trackFlux;
|
||||
}
|
||||
|
||||
std::shared_ptr<const DiskFlux> readDiskCommand(
|
||||
FluxSource& fluxSource, Decoder& decoder)
|
||||
void readDiskCommand(const DiskLayout& diskLayout,
|
||||
FluxSource& fluxSource,
|
||||
Decoder& decoder,
|
||||
Disk& disk)
|
||||
{
|
||||
std::unique_ptr<FluxSink> outputFluxSink;
|
||||
std::unique_ptr<FluxSinkFactory> outputFluxSinkFactory;
|
||||
if (globalConfig()->decoder().has_copy_flux_to())
|
||||
outputFluxSink =
|
||||
FluxSink::create(globalConfig()->decoder().copy_flux_to());
|
||||
outputFluxSinkFactory =
|
||||
FluxSinkFactory::create(globalConfig()->decoder().copy_flux_to());
|
||||
|
||||
auto diskflux = std::make_shared<DiskFlux>();
|
||||
std::map<CylinderHead, std::vector<std::shared_ptr<const Track>>>
|
||||
tracksByLogicalLocation;
|
||||
for (auto& [ch, track] : disk.tracksByPhysicalLocation)
|
||||
tracksByLogicalLocation[CylinderHead(track->ltl->logicalCylinder,
|
||||
track->ltl->logicalHead)]
|
||||
.push_back(track);
|
||||
|
||||
log(BeginOperationLogMessage{"Reading and decoding disk"});
|
||||
auto physicalLocations = Layout::computePhysicalLocations();
|
||||
unsigned index = 0;
|
||||
for (auto& physicalLocation : physicalLocations)
|
||||
|
||||
if (fluxSource.isHardware())
|
||||
disk.rotationalPeriod = measureDiskRotation();
|
||||
else
|
||||
disk.rotationalPeriod = getRotationalPeriodFromConfig();
|
||||
|
||||
{
|
||||
auto trackInfo = Layout::getLayoutOfTrackPhysical(
|
||||
physicalLocation.cylinder, physicalLocation.head);
|
||||
|
||||
log(OperationProgressLogMessage{
|
||||
index * 100 / (unsigned)physicalLocations.size()});
|
||||
index++;
|
||||
|
||||
testForEmergencyStop();
|
||||
|
||||
auto trackFlux = readAndDecodeTrack(fluxSource, decoder, trackInfo);
|
||||
diskflux->tracks.push_back(trackFlux);
|
||||
|
||||
if (outputFluxSink)
|
||||
std::unique_ptr<FluxSink> outputFluxSink;
|
||||
if (outputFluxSinkFactory)
|
||||
outputFluxSink = outputFluxSinkFactory->create();
|
||||
unsigned index = 0;
|
||||
for (auto& [logicalLocation, ltl] : diskLayout.layoutByLogicalLocation)
|
||||
{
|
||||
for (const auto& data : trackFlux->trackDatas)
|
||||
outputFluxSink->writeFlux(trackInfo->physicalTrack,
|
||||
trackInfo->physicalSide,
|
||||
*data->fluxmap);
|
||||
}
|
||||
log(OperationProgressLogMessage{
|
||||
index * 100 /
|
||||
(unsigned)diskLayout.layoutByLogicalLocation.size()});
|
||||
index++;
|
||||
|
||||
if (globalConfig()->decoder().dump_records())
|
||||
{
|
||||
std::vector<std::shared_ptr<const Record>> sorted_records;
|
||||
testForEmergencyStop();
|
||||
|
||||
for (const auto& data : trackFlux->trackDatas)
|
||||
sorted_records.insert(sorted_records.end(),
|
||||
data->records.begin(),
|
||||
data->records.end());
|
||||
auto& trackFluxes = tracksByLogicalLocation[logicalLocation];
|
||||
std::vector<std::shared_ptr<const Sector>> trackSectors;
|
||||
readAndDecodeTrack(diskLayout,
|
||||
fluxSource,
|
||||
decoder,
|
||||
ltl,
|
||||
trackFluxes,
|
||||
trackSectors);
|
||||
|
||||
std::sort(sorted_records.begin(),
|
||||
sorted_records.end(),
|
||||
[](const auto& o1, const auto& o2)
|
||||
{
|
||||
return o1->startTime < o2->startTime;
|
||||
});
|
||||
/* Replace all tracks on the disk by the new combined set. */
|
||||
|
||||
std::cout << "\nRaw (undecoded) records follow:\n\n";
|
||||
for (const auto& record : sorted_records)
|
||||
for (const auto& flux : trackFluxes)
|
||||
disk.tracksByPhysicalLocation.erase(CylinderHead{
|
||||
flux->ptl->physicalCylinder, flux->ptl->physicalHead});
|
||||
for (const auto& flux : trackFluxes)
|
||||
disk.tracksByPhysicalLocation.emplace(
|
||||
CylinderHead{
|
||||
flux->ptl->physicalCylinder, flux->ptl->physicalHead},
|
||||
flux);
|
||||
|
||||
/* Likewise for sectors. */
|
||||
|
||||
for (const auto& sector : trackSectors)
|
||||
disk.sectorsByPhysicalLocation.erase(
|
||||
sector->physicalLocation.value());
|
||||
for (const auto& sector : trackSectors)
|
||||
disk.sectorsByPhysicalLocation.emplace(
|
||||
sector->physicalLocation.value(), sector);
|
||||
|
||||
if (outputFluxSink)
|
||||
{
|
||||
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
|
||||
record->startTime / 1000.0,
|
||||
record->clock / 1000.0);
|
||||
hexdump(std::cout, record->rawData);
|
||||
std::cout << std::endl;
|
||||
for (const auto& data : trackFluxes)
|
||||
outputFluxSink->addFlux(data->ptl->physicalCylinder,
|
||||
data->ptl->physicalHead,
|
||||
*data->fluxmap);
|
||||
}
|
||||
}
|
||||
|
||||
if (globalConfig()->decoder().dump_sectors())
|
||||
{
|
||||
auto collected_sectors = collectSectors(trackFlux->sectors, false);
|
||||
std::vector<std::shared_ptr<const Sector>> sorted_sectors(
|
||||
collected_sectors.begin(), collected_sectors.end());
|
||||
std::sort(sorted_sectors.begin(),
|
||||
sorted_sectors.end(),
|
||||
[](const auto& o1, const auto& o2)
|
||||
{
|
||||
return *o1 < *o2;
|
||||
});
|
||||
|
||||
std::cout << "\nDecoded sectors follow:\n\n";
|
||||
for (const auto& sector : sorted_sectors)
|
||||
if (globalConfig()->decoder().dump_records())
|
||||
{
|
||||
std::cout << fmt::format(
|
||||
"{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: "
|
||||
"status {}\n",
|
||||
sector->logicalTrack,
|
||||
sector->logicalSide,
|
||||
sector->logicalSector,
|
||||
sector->headerStartTime / 1000.0,
|
||||
sector->clock / 1000.0,
|
||||
Sector::statusToString(sector->status));
|
||||
hexdump(std::cout, sector->data);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
std::vector<std::shared_ptr<const Record>> sorted_records;
|
||||
|
||||
/* track can't be modified below this point. */
|
||||
log(TrackReadLogMessage{trackFlux});
|
||||
for (const auto& data : trackFluxes)
|
||||
sorted_records.insert(sorted_records.end(),
|
||||
data->records.begin(),
|
||||
data->records.end());
|
||||
|
||||
std::sort(sorted_records.begin(),
|
||||
sorted_records.end(),
|
||||
[](const auto& o1, const auto& o2)
|
||||
{
|
||||
return o1->startTime < o2->startTime;
|
||||
});
|
||||
|
||||
std::cout << "\nRaw (undecoded) records follow:\n\n";
|
||||
for (const auto& record : sorted_records)
|
||||
{
|
||||
std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
|
||||
record->startTime / 1000.0,
|
||||
record->clock / 1000.0);
|
||||
hexdump(std::cout, record->rawData);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (globalConfig()->decoder().dump_sectors())
|
||||
{
|
||||
auto sectors = collectSectors(trackSectors, false);
|
||||
std::ranges::sort(sectors,
|
||||
[](const auto& o1, const auto& o2)
|
||||
{
|
||||
return *o1 < *o2;
|
||||
});
|
||||
|
||||
std::cout << "\nDecoded sectors follow:\n\n";
|
||||
for (const auto& sector : sectors)
|
||||
{
|
||||
std::cout << fmt::format(
|
||||
"{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: "
|
||||
"status {}\n",
|
||||
sector->logicalCylinder,
|
||||
sector->logicalHead,
|
||||
sector->logicalSector,
|
||||
sector->headerStartTime / 1000.0,
|
||||
sector->clock / 1000.0,
|
||||
Sector::statusToString(sector->status));
|
||||
hexdump(std::cout, sector->data);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
/* track can't be modified below this point. */
|
||||
log(TrackReadLogMessage{trackFluxes, trackSectors});
|
||||
|
||||
std::vector<std::shared_ptr<const Sector>> all_sectors;
|
||||
for (auto& [ch, sector] : disk.sectorsByPhysicalLocation)
|
||||
all_sectors.push_back(sector);
|
||||
all_sectors = collectSectors(all_sectors);
|
||||
disk.image = std::make_shared<Image>(all_sectors);
|
||||
|
||||
/* Log a _copy_ of the disk structure so that the logger
|
||||
* doesn't see the disk get mutated in subsequent reads. */
|
||||
log(DiskReadLogMessage{std::make_shared<Disk>(disk)});
|
||||
}
|
||||
}
|
||||
|
||||
std::set<std::shared_ptr<const Sector>> all_sectors;
|
||||
for (auto& track : diskflux->tracks)
|
||||
for (auto& sector : track->sectors)
|
||||
all_sectors.insert(sector);
|
||||
all_sectors = collectSectors(all_sectors);
|
||||
diskflux->image = std::make_shared<Image>(all_sectors);
|
||||
if (!disk.image)
|
||||
disk.image = std::make_shared<Image>();
|
||||
|
||||
/* diskflux can't be modified below this point. */
|
||||
log(DiskReadLogMessage{diskflux});
|
||||
log(EndOperationLogMessage{"Read complete"});
|
||||
return diskflux;
|
||||
}
|
||||
|
||||
void readDiskCommand(
|
||||
FluxSource& fluxsource, Decoder& decoder, ImageWriter& writer)
|
||||
void readDiskCommand(const DiskLayout& diskLayout,
|
||||
FluxSource& fluxSource,
|
||||
Decoder& decoder,
|
||||
ImageWriter& writer)
|
||||
{
|
||||
auto diskflux = readDiskCommand(fluxsource, decoder);
|
||||
Disk disk;
|
||||
readDiskCommand(diskLayout, fluxSource, decoder, disk);
|
||||
|
||||
writer.printMap(*diskflux->image);
|
||||
writer.printMap(*disk.image);
|
||||
if (globalConfig()->decoder().has_write_csv_to())
|
||||
writer.writeCsv(
|
||||
*diskflux->image, globalConfig()->decoder().write_csv_to());
|
||||
writer.writeImage(*diskflux->image);
|
||||
writer.writeCsv(*disk.image, globalConfig()->decoder().write_csv_to());
|
||||
writer.writeImage(*disk.image);
|
||||
}
|
||||
|
||||
@@ -3,19 +3,20 @@
|
||||
|
||||
#include "lib/data/locations.h"
|
||||
|
||||
class Disk;
|
||||
class Track;
|
||||
class Decoder;
|
||||
class DiskLayout;
|
||||
class Encoder;
|
||||
class DiskFlux;
|
||||
class FluxSink;
|
||||
class FluxSinkFactory;
|
||||
class FluxSource;
|
||||
class FluxSourceIteratorHolder;
|
||||
class Fluxmap;
|
||||
class Image;
|
||||
class ImageReader;
|
||||
class ImageWriter;
|
||||
class TrackInfo;
|
||||
class TrackFlux;
|
||||
class TrackDataFlux;
|
||||
class LogicalTrackLayout;
|
||||
class PhysicalTrackLayout;
|
||||
class Sector;
|
||||
|
||||
struct BeginSpeedOperationLogMessage
|
||||
@@ -29,12 +30,13 @@ struct EndSpeedOperationLogMessage
|
||||
|
||||
struct TrackReadLogMessage
|
||||
{
|
||||
std::shared_ptr<const TrackFlux> track;
|
||||
std::vector<std::shared_ptr<const Track>> tracks;
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
};
|
||||
|
||||
struct DiskReadLogMessage
|
||||
{
|
||||
std::shared_ptr<const DiskFlux> disk;
|
||||
std::shared_ptr<const Disk> disk;
|
||||
};
|
||||
|
||||
struct BeginReadOperationLogMessage
|
||||
@@ -45,7 +47,7 @@ struct BeginReadOperationLogMessage
|
||||
|
||||
struct EndReadOperationLogMessage
|
||||
{
|
||||
std::shared_ptr<const TrackDataFlux> trackDataFlux;
|
||||
std::shared_ptr<const Track> trackDataFlux;
|
||||
std::set<std::shared_ptr<const Sector>> sectors;
|
||||
};
|
||||
|
||||
@@ -74,42 +76,56 @@ struct OperationProgressLogMessage
|
||||
unsigned progress;
|
||||
};
|
||||
|
||||
extern void measureDiskRotation();
|
||||
|
||||
extern void writeTracks(FluxSink& fluxSink,
|
||||
extern void writeTracks(const DiskLayout& diskLayout,
|
||||
FluxSinkFactory& fluxSinkFactory,
|
||||
const std::function<std::unique_ptr<const Fluxmap>(
|
||||
std::shared_ptr<const TrackInfo>& layout)> producer,
|
||||
std::vector<std::shared_ptr<const TrackInfo>>& locations);
|
||||
const LogicalTrackLayout& ltl)> producer,
|
||||
const std::vector<CylinderHead>& locations);
|
||||
|
||||
extern void writeTracksAndVerify(FluxSink& fluxSink,
|
||||
extern void writeTracksAndVerify(const DiskLayout& diskLayout,
|
||||
FluxSinkFactory& fluxSinkFactory,
|
||||
Encoder& encoder,
|
||||
FluxSource& fluxSource,
|
||||
Decoder& decoder,
|
||||
const Image& image,
|
||||
std::vector<std::shared_ptr<const TrackInfo>>& locations);
|
||||
const std::vector<CylinderHead>& locations);
|
||||
|
||||
extern void writeDiskCommand(const Image& image,
|
||||
extern void writeDiskCommand(const DiskLayout& diskLayout,
|
||||
const Image& image,
|
||||
Encoder& encoder,
|
||||
FluxSink& fluxSink,
|
||||
FluxSinkFactory& fluxSinkFactory,
|
||||
Decoder* decoder,
|
||||
FluxSource* fluxSource,
|
||||
const std::vector<CylinderHead>& locations);
|
||||
|
||||
extern void writeDiskCommand(const Image& image,
|
||||
extern void writeDiskCommand(const DiskLayout& diskLayout,
|
||||
const Image& image,
|
||||
Encoder& encoder,
|
||||
FluxSink& fluxSink,
|
||||
FluxSinkFactory& fluxSinkFactory,
|
||||
Decoder* decoder = nullptr,
|
||||
FluxSource* fluxSource = nullptr);
|
||||
|
||||
extern void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink);
|
||||
extern void writeRawDiskCommand(const DiskLayout& diskLayout,
|
||||
FluxSource& fluxSource,
|
||||
FluxSinkFactory& fluxSinkFactory);
|
||||
|
||||
extern std::shared_ptr<TrackFlux> readAndDecodeTrack(FluxSource& fluxSource,
|
||||
/* Reads a single group of tracks. tracks and combinedSectors are populated.
|
||||
* tracks may contain preexisting data which will be taken into account. */
|
||||
|
||||
extern void readAndDecodeTrack(const DiskLayout& diskLayout,
|
||||
FluxSource& fluxSource,
|
||||
Decoder& decoder,
|
||||
std::shared_ptr<const TrackInfo>& layout);
|
||||
const std::shared_ptr<const LogicalTrackLayout>& ltl,
|
||||
std::vector<std::shared_ptr<const Track>>& tracks,
|
||||
std::vector<std::shared_ptr<const Sector>>& combinedSectors);
|
||||
|
||||
extern std::shared_ptr<const DiskFlux> readDiskCommand(
|
||||
FluxSource& fluxsource, Decoder& decoder);
|
||||
extern void readDiskCommand(
|
||||
FluxSource& source, Decoder& decoder, ImageWriter& writer);
|
||||
extern void readDiskCommand(const DiskLayout& diskLayout,
|
||||
FluxSource& fluxSource,
|
||||
Decoder& decoder,
|
||||
Disk& disk);
|
||||
extern void readDiskCommand(const DiskLayout& diskLayout,
|
||||
FluxSource& source,
|
||||
Decoder& decoder,
|
||||
ImageWriter& writer);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -211,6 +211,13 @@ void Config::clear()
|
||||
_appliedOptions.clear();
|
||||
}
|
||||
|
||||
static std::string getValidValues(const OptionGroupProto& group)
|
||||
{
|
||||
return fmt::format("{}",
|
||||
fmt::join(
|
||||
std::views::transform(group.option(), &OptionProto::name), ", "));
|
||||
}
|
||||
|
||||
std::vector<std::string> Config::validate()
|
||||
{
|
||||
std::vector<std::string> results;
|
||||
@@ -218,7 +225,7 @@ std::vector<std::string> Config::validate()
|
||||
/* Ensure that only one item in each group is set. */
|
||||
|
||||
std::map<const OptionGroupProto*, const OptionProto*> optionsByGroup;
|
||||
for (auto [group, option, hasArgument] : _appliedOptions)
|
||||
for (auto& [group, option, hasArgument] : _appliedOptions)
|
||||
if (group)
|
||||
{
|
||||
auto& o = optionsByGroup[group];
|
||||
@@ -227,12 +234,23 @@ std::vector<std::string> Config::validate()
|
||||
fmt::format("multiple mutually exclusive values set for "
|
||||
"group '{}': valid values are: {}",
|
||||
group->comment(),
|
||||
fmt::join(std::views::transform(
|
||||
group->option(), &OptionProto::name),
|
||||
", ")));
|
||||
getValidValues(*group)));
|
||||
o = option;
|
||||
}
|
||||
|
||||
/* Ensure that every group has an option set. */
|
||||
|
||||
for (const auto& group : base()->option_group())
|
||||
{
|
||||
if (!optionsByGroup.contains(&group))
|
||||
{
|
||||
results.push_back(
|
||||
fmt::format("no value set for group '{}': valid values are: {}",
|
||||
group.comment(),
|
||||
getValidValues(group)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Check option requirements. */
|
||||
|
||||
for (auto [group, option, hasArgument] : _appliedOptions)
|
||||
@@ -357,7 +375,7 @@ Config::OptionInfo Config::findOption(
|
||||
{
|
||||
if (optionGroup.name().empty())
|
||||
if (searchOptionList(optionGroup.option(), name))
|
||||
return {nullptr, found, false};
|
||||
return {&optionGroup, found, false};
|
||||
}
|
||||
|
||||
throw OptionNotFoundException(fmt::format("option {} not found", name));
|
||||
@@ -395,8 +413,7 @@ void Config::checkOptionValid(const OptionProto& option)
|
||||
ss << ']';
|
||||
|
||||
throw InapplicableOptionException(
|
||||
fmt::format("option '{}' is inapplicable to this "
|
||||
"configuration "
|
||||
fmt::format("option '{}' is inapplicable to this configuration "
|
||||
"because {}={} could not be met",
|
||||
option.name(),
|
||||
req.key(),
|
||||
@@ -434,6 +451,52 @@ bool Config::applyOption(const std::string& name, const std::string value)
|
||||
return optionInfo.usesValue;
|
||||
}
|
||||
|
||||
void Config::applyOptionsFile(const std::string& data)
|
||||
{
|
||||
if (!data.empty())
|
||||
{
|
||||
for (auto setting : split(data, '\n'))
|
||||
{
|
||||
setting = trimWhitespace(setting);
|
||||
if (setting.size() == 0)
|
||||
continue;
|
||||
if (setting[0] == '#')
|
||||
continue;
|
||||
|
||||
auto equals = setting.find('=');
|
||||
if (equals == std::string::npos)
|
||||
error("Malformed setting line '{}'", setting);
|
||||
|
||||
auto key = setting.substr(0, equals);
|
||||
auto value = setting.substr(equals + 1);
|
||||
globalConfig().set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Config::applyDefaultOptions()
|
||||
{
|
||||
std::set<const OptionGroupProto*> appliedOptionGroups;
|
||||
for (auto& [group, option, hasArgument] : _appliedOptions)
|
||||
if (group)
|
||||
appliedOptionGroups.insert(group);
|
||||
|
||||
/* For every group which doesn't have an option set, find the default and
|
||||
* set it. */
|
||||
|
||||
for (const auto& group : base()->option_group())
|
||||
{
|
||||
if (!appliedOptionGroups.contains(&group))
|
||||
{
|
||||
for (const auto& option : group.option())
|
||||
{
|
||||
if (option.set_by_default())
|
||||
applyOption({&group, &option, false});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Config::clearOptions()
|
||||
{
|
||||
_appliedOptions.clear();
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
class ConfigProto;
|
||||
class OptionProto;
|
||||
class FluxSource;
|
||||
class FluxSink;
|
||||
class FluxSinkFactory;
|
||||
class ImageReader;
|
||||
class ImageWriter;
|
||||
class Encoder;
|
||||
@@ -142,6 +142,8 @@ public:
|
||||
bool isOptionValid(const OptionProto& option);
|
||||
void applyOption(const OptionInfo& optionInfo);
|
||||
bool applyOption(const std::string& name, const std::string value = "");
|
||||
void applyOptionsFile(const std::string& data);
|
||||
void applyDefaultOptions();
|
||||
void clearOptions();
|
||||
|
||||
/* Adjust overall inputs and outputs. */
|
||||
|
||||
@@ -55,7 +55,16 @@ message OptionPrerequisiteProto
|
||||
repeated string value = 2 [(help) = "list of required values"];
|
||||
}
|
||||
|
||||
// NEXT_TAG: 8
|
||||
enum OptionApplicabilityHint
|
||||
{
|
||||
FORMAT = 0;
|
||||
ANY_SOURCESINK = 1;
|
||||
HARDWARE_SOURCESINK = 2;
|
||||
MANUAL_SOURCESINK = 3;
|
||||
FLUXFILE_SOURCESINK = 4;
|
||||
}
|
||||
|
||||
// NEXT_TAG: 9
|
||||
message OptionProto
|
||||
{
|
||||
optional string name = 1 [(help) = "option name"];
|
||||
@@ -68,11 +77,14 @@ message OptionProto
|
||||
7 [(help) = "prerequisites for this option"];
|
||||
|
||||
optional ConfigProto config = 4 [(help) = "option data"];
|
||||
repeated OptionApplicabilityHint applicability = 8;
|
||||
}
|
||||
|
||||
// NEXT_TAG: 5
|
||||
message OptionGroupProto
|
||||
{
|
||||
optional string comment = 1 [(help) = "help text for option group"];
|
||||
optional string name = 2 [(help) = "option group name"];
|
||||
repeated OptionProto option = 3;
|
||||
repeated OptionApplicabilityHint applicability = 4;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ message DriveProto
|
||||
[ default = 0, (help) = "index pulses longer than this interval are "
|
||||
"considered sector markers; shorter indicates an true index marker" ];
|
||||
optional bool high_density = 5
|
||||
[ default = true, (help) = "set if this is a high density disk" ];
|
||||
[ default = false, (help) = "set if this is a high density disk" ];
|
||||
optional bool sync_with_index = 6
|
||||
[ default = false, (help) = "start reading at index mark" ];
|
||||
optional double revolutions = 7
|
||||
[ default = 1.2, (help) = "number of revolutions to read" ];
|
||||
[ default = 2.5, (help) = "number of revolutions to read" ];
|
||||
|
||||
optional string tracks = 8
|
||||
[ default = "c0-80h0-1", (help) = "Tracks supported by drive" ];
|
||||
|
||||
@@ -174,6 +174,7 @@ std::vector<std::string> FlagGroup::parseFlagsWithFilenames(int argc,
|
||||
index++;
|
||||
}
|
||||
|
||||
globalConfig().applyDefaultOptions();
|
||||
globalConfig().validateAndThrow();
|
||||
return filenames;
|
||||
}
|
||||
|
||||
@@ -117,38 +117,28 @@ static ProtoField resolveProtoPath(
|
||||
"config field '{}' in '{}' is not a message", item, path));
|
||||
|
||||
const auto* reflection = message->GetReflection();
|
||||
if ((field->label() !=
|
||||
google::protobuf::FieldDescriptor::LABEL_REPEATED) &&
|
||||
(index != -1))
|
||||
if (!field->is_repeated() && (index != -1))
|
||||
throw ProtoPathNotFoundException(fmt::format(
|
||||
"config field '{}[{}]' is indexed, but not repeated",
|
||||
item,
|
||||
index));
|
||||
|
||||
switch (field->label())
|
||||
if (field->is_repeated())
|
||||
{
|
||||
case google::protobuf::FieldDescriptor::LABEL_OPTIONAL:
|
||||
case google::protobuf::FieldDescriptor::LABEL_REQUIRED:
|
||||
if (!create && !reflection->HasField(*message, field))
|
||||
throw ProtoPathNotFoundException(fmt::format(
|
||||
"could not find config field '{}'", field->name()));
|
||||
message = reflection->MutableMessage(message, field);
|
||||
break;
|
||||
if (index == -1)
|
||||
throw ProtoPathNotFoundException(fmt::format(
|
||||
"config field '{}' is repeated and must be indexed", item));
|
||||
while (reflection->FieldSize(*message, field) <= index)
|
||||
reflection->AddMessage(message, field);
|
||||
|
||||
case google::protobuf::FieldDescriptor::LABEL_REPEATED:
|
||||
if (index == -1)
|
||||
throw ProtoPathNotFoundException(fmt::format(
|
||||
"config field '{}' is repeated and must be indexed",
|
||||
item));
|
||||
while (reflection->FieldSize(*message, field) <= index)
|
||||
reflection->AddMessage(message, field);
|
||||
|
||||
message =
|
||||
reflection->MutableRepeatedMessage(message, field, index);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("bad proto label for field '{}' in '{}'", item, path);
|
||||
message = reflection->MutableRepeatedMessage(message, field, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!create && !reflection->HasField(*message, field))
|
||||
throw ProtoPathNotFoundException(fmt::format(
|
||||
"could not find config field '{}'", field->name()));
|
||||
message = reflection->MutableMessage(message, field);
|
||||
}
|
||||
|
||||
descriptor = message->GetDescriptor();
|
||||
@@ -215,7 +205,7 @@ static void updateRepeatedField(
|
||||
void ProtoField::set(const std::string& value)
|
||||
{
|
||||
const auto* reflection = _message->GetReflection();
|
||||
if (_field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
if (_field->is_repeated())
|
||||
{
|
||||
if (_index == -1)
|
||||
error("field '{}' is repeated but no index is provided");
|
||||
@@ -359,7 +349,7 @@ void ProtoField::set(const std::string& value)
|
||||
std::string ProtoField::get() const
|
||||
{
|
||||
const auto* reflection = _message->GetReflection();
|
||||
if (_field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
if (_field->is_repeated())
|
||||
{
|
||||
if (_index == -1)
|
||||
error("field '{}' is repeated but no index is provided",
|
||||
@@ -456,7 +446,7 @@ std::string ProtoField::get() const
|
||||
google::protobuf::Message* ProtoField::getMessage() const
|
||||
{
|
||||
const auto* reflection = _message->GetReflection();
|
||||
if (_field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
if (_field->is_repeated())
|
||||
{
|
||||
if (_index == -1)
|
||||
error("field '{}' is repeated but no index is provided",
|
||||
@@ -477,7 +467,7 @@ google::protobuf::Message* ProtoField::getMessage() const
|
||||
std::string ProtoField::getBytes() const
|
||||
{
|
||||
const auto* reflection = _message->GetReflection();
|
||||
if (_field->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
if (_field->is_repeated())
|
||||
{
|
||||
if (_index == -1)
|
||||
error("field '{}' is repeated but no index is provided",
|
||||
@@ -536,7 +526,7 @@ findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor)
|
||||
const google::protobuf::FieldDescriptor* f = d->field(i);
|
||||
std::string n = s + (std::string)f->name();
|
||||
|
||||
if (f->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
if (f->is_repeated())
|
||||
n += "[]";
|
||||
|
||||
if (shouldRecurse(f))
|
||||
@@ -550,12 +540,13 @@ findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor)
|
||||
return fields;
|
||||
}
|
||||
|
||||
std::vector<ProtoField> findAllProtoFields(google::protobuf::Message* message)
|
||||
std::vector<ProtoField> findAllProtoFields(
|
||||
const google::protobuf::Message* message)
|
||||
{
|
||||
std::vector<ProtoField> allFields;
|
||||
|
||||
std::function<void(google::protobuf::Message*, const std::string&)>
|
||||
recurse = [&](auto* message, const auto& name)
|
||||
std::function<void(const google::protobuf::Message*, const std::string&)>
|
||||
recurse = [&](const auto* message, const auto& name)
|
||||
{
|
||||
const auto* reflection = message->GetReflection();
|
||||
std::vector<const google::protobuf::FieldDescriptor*> fields;
|
||||
@@ -568,25 +559,26 @@ std::vector<ProtoField> findAllProtoFields(google::protobuf::Message* message)
|
||||
basename += '.';
|
||||
basename += f->name();
|
||||
|
||||
if (f->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
|
||||
if (f->is_repeated())
|
||||
{
|
||||
for (int i = 0; i < reflection->FieldSize(*message, f); i++)
|
||||
{
|
||||
const auto n = fmt::format("{}[{}]", basename, i);
|
||||
if (shouldRecurse(f))
|
||||
recurse(
|
||||
reflection->MutableRepeatedMessage(message, f, i),
|
||||
n);
|
||||
&reflection->GetRepeatedMessage(*message, f, i), n);
|
||||
else
|
||||
allFields.push_back(ProtoField(n, message, f, i));
|
||||
allFields.push_back(ProtoField(
|
||||
n, (google::protobuf::Message*)message, f, i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shouldRecurse(f))
|
||||
recurse(reflection->MutableMessage(message, f), basename);
|
||||
recurse(&reflection->GetMessage(*message, f), basename);
|
||||
else
|
||||
allFields.push_back(ProtoField(basename, message, f));
|
||||
allFields.push_back(ProtoField(
|
||||
basename, (google::protobuf::Message*)message, f));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -594,3 +586,12 @@ std::vector<ProtoField> findAllProtoFields(google::protobuf::Message* message)
|
||||
recurse(message, "");
|
||||
return allFields;
|
||||
}
|
||||
|
||||
std::string renderProtoAsConfig(const google::protobuf::Message* message)
|
||||
{
|
||||
auto allFields = findAllProtoFields(message);
|
||||
std::stringstream ss;
|
||||
for (const auto& field : allFields)
|
||||
ss << field.path() << "=" << field.get() << "\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@@ -72,7 +72,9 @@ extern std::map<std::string, const google::protobuf::FieldDescriptor*>
|
||||
findAllPossibleProtoFields(const google::protobuf::Descriptor* descriptor);
|
||||
|
||||
extern std::vector<ProtoField> findAllProtoFields(
|
||||
google::protobuf::Message* message);
|
||||
const google::protobuf::Message* message);
|
||||
extern std::string renderProtoAsConfig(
|
||||
const google::protobuf::Message* message);
|
||||
|
||||
template <class T>
|
||||
static inline const T parseProtoBytes(const std::string_view& bytes)
|
||||
|
||||
@@ -23,5 +23,6 @@ cxxlibrary(
|
||||
"dep/agg",
|
||||
"dep/stb",
|
||||
"+fmt_lib",
|
||||
"+z_lib",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -27,28 +27,27 @@
|
||||
#define STRINGIFY(a) XSTRINGIFY(a)
|
||||
#define XSTRINGIFY(a) #a
|
||||
|
||||
template <class T>
|
||||
static inline std::vector<T> vector_of(T item)
|
||||
{
|
||||
return std::vector<T>{item};
|
||||
}
|
||||
|
||||
typedef double nanoseconds_t;
|
||||
class Bytes;
|
||||
|
||||
extern double getCurrentTime();
|
||||
extern void hexdump(std::ostream& stream, const Bytes& bytes);
|
||||
extern void hexdumpForSrp16(std::ostream& stream, const Bytes& bytes);
|
||||
|
||||
struct ErrorException
|
||||
struct ErrorException : public std::exception
|
||||
{
|
||||
ErrorException(const std::string& message): message(message) {}
|
||||
|
||||
const std::string message;
|
||||
|
||||
const char* what() const throw() override;
|
||||
void print() const;
|
||||
};
|
||||
|
||||
struct OutOfRangeException : public ErrorException
|
||||
{
|
||||
OutOfRangeException(const std::string& message): ErrorException(message) {}
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
[[noreturn]] inline void error(fmt::string_view fstr, const Args&... args)
|
||||
{
|
||||
@@ -71,4 +70,24 @@ struct overloaded : Ts...
|
||||
template <class... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
template <typename K, typename V>
|
||||
inline const V& findOrDefault(
|
||||
const std::map<K, V>& map, const K& key, const V& defaultValue = V())
|
||||
{
|
||||
auto it = map.find(key);
|
||||
if (it == map.end())
|
||||
return defaultValue;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
inline const std::optional<V> findOptionally(
|
||||
const std::map<K, V>& map, const K& key, const V& defaultValue = V())
|
||||
{
|
||||
auto it = map.find(key);
|
||||
if (it == map.end())
|
||||
return std::nullopt;
|
||||
return std::make_optional(it->second);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -32,10 +32,3 @@ void hexdump(std::ostream& stream, const Bytes& buffer)
|
||||
pos += 16;
|
||||
}
|
||||
}
|
||||
|
||||
void hexdumpForSrp16(std::ostream& stream, const Bytes& buffer)
|
||||
{
|
||||
for (uint8_t byte : buffer)
|
||||
stream << fmt::format("{:02x}", byte);
|
||||
stream << std::endl;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
#include "fmt/format.h"
|
||||
#include <type_traits>
|
||||
|
||||
class DiskFlux;
|
||||
class TrackDataFlux;
|
||||
class TrackFlux;
|
||||
class Disk;
|
||||
class Track;
|
||||
class Sector;
|
||||
class LogRenderer;
|
||||
|
||||
|
||||
@@ -17,6 +17,11 @@ void ErrorException::print() const
|
||||
std::cerr << message << '\n';
|
||||
}
|
||||
|
||||
const char* ErrorException::what() const throw()
|
||||
{
|
||||
return message.c_str();
|
||||
}
|
||||
|
||||
std::string join(
|
||||
const std::vector<std::string>& values, const std::string& separator)
|
||||
{
|
||||
|
||||
@@ -37,6 +37,15 @@ std::map<V, K> reverseMap(const std::map<K, V>& map)
|
||||
return reverse;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
std::map<K, std::vector<V>> multimapToMapOfVectors(const std::multimap<K, V>& multimap)
|
||||
{
|
||||
std::map<K, std::vector<V>> results;
|
||||
for (auto& [k, v] : multimap)
|
||||
results[k].push_back(v);
|
||||
return results;
|
||||
}
|
||||
|
||||
/* If set, any running job will terminate as soon as possible (with an error).
|
||||
*/
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from build.c import cxxlibrary
|
||||
cxxlibrary(
|
||||
name="data",
|
||||
srcs=[
|
||||
"./disk.cc",
|
||||
"./fluxmap.cc",
|
||||
"./fluxmapreader.cc",
|
||||
"./fluxpattern.cc",
|
||||
@@ -12,7 +13,7 @@ cxxlibrary(
|
||||
"./sector.cc",
|
||||
],
|
||||
hdrs={
|
||||
"lib/data/flux.h": "./flux.h",
|
||||
"lib/data/disk.h": "./disk.h",
|
||||
"lib/data/fluxmap.h": "./fluxmap.h",
|
||||
"lib/data/sector.h": "./sector.h",
|
||||
"lib/data/layout.h": "./layout.h",
|
||||
|
||||
56
lib/data/disk.cc
Normal file
56
lib/data/disk.cc
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/disk.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "protocol.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct pair_to_range_t
|
||||
{
|
||||
template <typename I>
|
||||
friend constexpr auto operator|(
|
||||
std::pair<I, I> const& pr, pair_to_range_t)
|
||||
{
|
||||
return std::ranges::subrange(pr.first, pr.second);
|
||||
}
|
||||
};
|
||||
inline constexpr pair_to_range_t pair_to_range{};
|
||||
}
|
||||
|
||||
Disk::Disk() {}
|
||||
|
||||
Disk::Disk(
|
||||
const std::shared_ptr<const Image>& image, const DiskLayout& diskLayout):
|
||||
image(image)
|
||||
{
|
||||
std::multimap<CylinderHead, std::shared_ptr<const Sector>>
|
||||
sectorsGroupedByTrack;
|
||||
for (const auto& sector : *image)
|
||||
sectorsGroupedByTrack.insert(
|
||||
std::make_pair(sector->physicalLocation.value(), sector));
|
||||
|
||||
for (const auto& physicalLocation :
|
||||
std::views::keys(sectorsGroupedByTrack) | std::ranges::to<std::set>())
|
||||
{
|
||||
const auto& ptl =
|
||||
diskLayout.layoutByPhysicalLocation.at(physicalLocation);
|
||||
const auto& ltl = ptl->logicalTrackLayout;
|
||||
|
||||
auto decodedTrack = std::make_shared<Track>();
|
||||
decodedTrack->ltl = ltl;
|
||||
decodedTrack->ptl = ptl;
|
||||
tracksByPhysicalLocation.insert(
|
||||
std::make_pair(physicalLocation, decodedTrack));
|
||||
|
||||
for (auto& [ch, sector] :
|
||||
sectorsGroupedByTrack.equal_range(physicalLocation) | pair_to_range)
|
||||
{
|
||||
decodedTrack->allSectors.push_back(sector);
|
||||
decodedTrack->normalisedSectors.push_back(sector);
|
||||
sectorsByPhysicalLocation.insert(
|
||||
std::make_pair(physicalLocation, sector));
|
||||
}
|
||||
}
|
||||
}
|
||||
62
lib/data/disk.h
Normal file
62
lib/data/disk.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef FLUX_H
|
||||
#define FLUX_H
|
||||
|
||||
#include "lib/core/bytes.h"
|
||||
#include "lib/data/locations.h"
|
||||
|
||||
class DiskLayout;
|
||||
class Fluxmap;
|
||||
class Image;
|
||||
class LogicalTrackLayout;
|
||||
class PhysicalTrackLayout;
|
||||
class Sector;
|
||||
|
||||
struct Record
|
||||
{
|
||||
nanoseconds_t clock = 0;
|
||||
nanoseconds_t startTime = 0;
|
||||
nanoseconds_t endTime = 0;
|
||||
uint32_t position = 0;
|
||||
Bytes rawData;
|
||||
};
|
||||
|
||||
struct Track
|
||||
{
|
||||
std::shared_ptr<const LogicalTrackLayout> ltl;
|
||||
std::shared_ptr<const PhysicalTrackLayout> ptl;
|
||||
std::shared_ptr<const Fluxmap> fluxmap;
|
||||
std::vector<std::shared_ptr<const Record>> records;
|
||||
|
||||
/* All sectors, valid or not, including duplicates. */
|
||||
|
||||
std::vector<std::shared_ptr<const Sector>> allSectors;
|
||||
|
||||
/* Zero or one sector for each ID, preferring good ones. */
|
||||
|
||||
std::vector<std::shared_ptr<const Sector>> normalisedSectors;
|
||||
};
|
||||
|
||||
struct Disk
|
||||
{
|
||||
Disk();
|
||||
|
||||
/* Creates a Disk from an Image, populating the tracks and sectors maps
|
||||
* based on the supplied disk layout. */
|
||||
|
||||
Disk(const std::shared_ptr<const Image>& image,
|
||||
const DiskLayout& diskLayout);
|
||||
|
||||
Disk& operator=(const Disk& other) = default;
|
||||
|
||||
std::multimap<CylinderHead, std::shared_ptr<const Track>>
|
||||
tracksByPhysicalLocation;
|
||||
std::multimap<CylinderHead, std::shared_ptr<const Sector>>
|
||||
sectorsByPhysicalLocation;
|
||||
std::shared_ptr<const Image> image;
|
||||
|
||||
/* 0 if the period is unknown (e.g. if this Disk was made from an image). */
|
||||
|
||||
nanoseconds_t rotationalPeriod = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,41 +0,0 @@
|
||||
#ifndef FLUX_H
|
||||
#define FLUX_H
|
||||
|
||||
#include "lib/core/bytes.h"
|
||||
|
||||
class Fluxmap;
|
||||
class Sector;
|
||||
class Image;
|
||||
class TrackInfo;
|
||||
|
||||
struct Record
|
||||
{
|
||||
nanoseconds_t clock = 0;
|
||||
nanoseconds_t startTime = 0;
|
||||
nanoseconds_t endTime = 0;
|
||||
uint32_t position = 0;
|
||||
Bytes rawData;
|
||||
};
|
||||
|
||||
struct TrackDataFlux
|
||||
{
|
||||
std::shared_ptr<const TrackInfo> trackInfo;
|
||||
std::shared_ptr<const Fluxmap> fluxmap;
|
||||
std::vector<std::shared_ptr<const Record>> records;
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
};
|
||||
|
||||
struct TrackFlux
|
||||
{
|
||||
std::shared_ptr<const TrackInfo> trackInfo;
|
||||
std::vector<std::shared_ptr<TrackDataFlux>> trackDatas;
|
||||
std::set<std::shared_ptr<const Sector>> sectors;
|
||||
};
|
||||
|
||||
struct DiskFlux
|
||||
{
|
||||
std::vector<std::shared_ptr<TrackFlux>> tracks;
|
||||
std::shared_ptr<const Image> image;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "protocol.h"
|
||||
#include <mutex>
|
||||
|
||||
Fluxmap& Fluxmap::appendBytes(const Bytes& bytes)
|
||||
{
|
||||
@@ -12,6 +14,8 @@ Fluxmap& Fluxmap::appendBytes(const Bytes& bytes)
|
||||
|
||||
Fluxmap& Fluxmap::appendBytes(const uint8_t* ptr, size_t len)
|
||||
{
|
||||
flushIndexMarks();
|
||||
|
||||
ByteWriter bw(_bytes);
|
||||
bw.seekToEnd();
|
||||
|
||||
@@ -52,6 +56,7 @@ Fluxmap& Fluxmap::appendPulse()
|
||||
|
||||
Fluxmap& Fluxmap::appendIndex()
|
||||
{
|
||||
flushIndexMarks();
|
||||
findLastByte() |= 0x40;
|
||||
return *this;
|
||||
}
|
||||
@@ -75,3 +80,33 @@ std::vector<std::unique_ptr<const Fluxmap>> Fluxmap::split() const
|
||||
|
||||
return maps;
|
||||
}
|
||||
|
||||
const std::vector<nanoseconds_t>& Fluxmap::getIndexMarks() const
|
||||
{
|
||||
std::scoped_lock lock(_mutationMutex);
|
||||
if (!_indexMarks.has_value())
|
||||
{
|
||||
_indexMarks = std::make_optional<std::vector<nanoseconds_t>>();
|
||||
FluxmapReader fmr(*this);
|
||||
nanoseconds_t oldt = -1;
|
||||
for (;;)
|
||||
{
|
||||
unsigned ticks;
|
||||
if (!fmr.findEvent(F_BIT_INDEX, ticks))
|
||||
break;
|
||||
|
||||
/* Debounce. */
|
||||
nanoseconds_t t = fmr.tell().ns();
|
||||
if (t != oldt)
|
||||
_indexMarks->push_back(t);
|
||||
oldt = t;
|
||||
}
|
||||
}
|
||||
return *_indexMarks;
|
||||
}
|
||||
|
||||
void Fluxmap::flushIndexMarks()
|
||||
{
|
||||
std::scoped_lock lock(_mutationMutex);
|
||||
_indexMarks = {};
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lib/core/bytes.h"
|
||||
#include "protocol.h"
|
||||
#include "fmt/format.h"
|
||||
#include <mutex>
|
||||
|
||||
class RawBits;
|
||||
|
||||
@@ -82,14 +83,18 @@ public:
|
||||
std::unique_ptr<const Fluxmap> precompensate(
|
||||
int threshold_ticks, int amount_ticks);
|
||||
std::vector<std::unique_ptr<const Fluxmap>> split() const;
|
||||
const std::vector<nanoseconds_t>& getIndexMarks() const;
|
||||
|
||||
private:
|
||||
uint8_t& findLastByte();
|
||||
void flushIndexMarks();
|
||||
|
||||
private:
|
||||
nanoseconds_t _duration = 0;
|
||||
int _ticks = 0;
|
||||
Bytes _bytes;
|
||||
mutable std::mutex _mutationMutex;
|
||||
mutable std::optional<std::vector<nanoseconds_t>> _indexMarks;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -44,6 +44,13 @@ void FluxmapReader::skipToEvent(int event)
|
||||
findEvent(event, ticks);
|
||||
}
|
||||
|
||||
int FluxmapReader::getCurrentEvent()
|
||||
{
|
||||
if (eof())
|
||||
return F_EOF;
|
||||
return _bytes[_pos.bytes] & 0xc0;
|
||||
}
|
||||
|
||||
bool FluxmapReader::findEvent(int event, unsigned& ticks)
|
||||
{
|
||||
ticks = 0;
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
return (_fluxmap.duration());
|
||||
}
|
||||
|
||||
int getCurrentEvent();
|
||||
void getNextEvent(int& event, unsigned& ticks);
|
||||
void skipToEvent(int event);
|
||||
bool findEvent(int event, unsigned& ticks);
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
|
||||
Image::Image() {}
|
||||
|
||||
Image::Image(std::set<std::shared_ptr<const Sector>>& sectors)
|
||||
Image::Image(const std::vector<std::shared_ptr<const Sector>>& sectors)
|
||||
{
|
||||
for (auto& sector : sectors)
|
||||
{
|
||||
key_t key = std::make_tuple(
|
||||
sector->logicalTrack, sector->logicalSide, sector->logicalSector);
|
||||
_sectors[key] = sector;
|
||||
}
|
||||
_sectors[{sector->logicalCylinder,
|
||||
sector->logicalHead,
|
||||
sector->logicalSector}] = sector;
|
||||
|
||||
calculateSize();
|
||||
}
|
||||
|
||||
@@ -23,72 +22,53 @@ void Image::clear()
|
||||
_geometry = {0, 0, 0};
|
||||
}
|
||||
|
||||
void Image::createBlankImage()
|
||||
{
|
||||
clear();
|
||||
for (const auto& trackAndHead : Layout::getTrackOrdering(
|
||||
globalConfig()->layout().filesystem_track_order()))
|
||||
{
|
||||
unsigned track = trackAndHead.first;
|
||||
unsigned side = trackAndHead.second;
|
||||
auto trackLayout = Layout::getLayoutOfTrack(track, side);
|
||||
Bytes blank(trackLayout->sectorSize);
|
||||
for (unsigned sectorId : trackLayout->naturalSectorOrder)
|
||||
put(track, side, sectorId)->data = blank;
|
||||
}
|
||||
}
|
||||
|
||||
bool Image::empty() const
|
||||
{
|
||||
return _sectors.empty();
|
||||
}
|
||||
|
||||
bool Image::contains(unsigned track, unsigned side, unsigned sectorid) const
|
||||
bool Image::contains(const LogicalLocation& location) const
|
||||
{
|
||||
key_t key = std::make_tuple(track, side, sectorid);
|
||||
return _sectors.find(key) != _sectors.end();
|
||||
return _sectors.find(location) != _sectors.end();
|
||||
}
|
||||
|
||||
std::shared_ptr<const Sector> Image::get(
|
||||
unsigned track, unsigned side, unsigned sectorid) const
|
||||
std::shared_ptr<const Sector> Image::get(const LogicalLocation& location) const
|
||||
{
|
||||
static std::shared_ptr<const Sector> NONE;
|
||||
|
||||
key_t key = std::make_tuple(track, side, sectorid);
|
||||
auto i = _sectors.find(key);
|
||||
auto i = _sectors.find(location);
|
||||
if (i == _sectors.end())
|
||||
return NONE;
|
||||
return nullptr;
|
||||
return i->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<Sector> Image::put(
|
||||
unsigned track, unsigned side, unsigned sectorid)
|
||||
std::shared_ptr<Sector> Image::put(const LogicalLocation& location)
|
||||
{
|
||||
auto trackLayout = Layout::getLayoutOfTrack(track, side);
|
||||
key_t key = std::make_tuple(track, side, sectorid);
|
||||
std::shared_ptr<Sector> sector = std::make_shared<Sector>();
|
||||
sector->logicalTrack = track;
|
||||
sector->logicalSide = side;
|
||||
sector->logicalSector = sectorid;
|
||||
sector->physicalTrack = Layout::remapTrackLogicalToPhysical(track);
|
||||
sector->physicalSide = side;
|
||||
_sectors[key] = sector;
|
||||
auto sector = std::make_shared<Sector>(location);
|
||||
_sectors[location] = sector;
|
||||
return sector;
|
||||
}
|
||||
|
||||
void Image::erase(unsigned track, unsigned side, unsigned sectorid)
|
||||
void Image::erase(const LogicalLocation& location)
|
||||
{
|
||||
key_t key = std::make_tuple(track, side, sectorid);
|
||||
_sectors.erase(key);
|
||||
_sectors.erase(location);
|
||||
}
|
||||
|
||||
std::set<std::pair<unsigned, unsigned>> Image::tracks() const
|
||||
void Image::addMissingSectors(const DiskLayout& diskLayout, bool populated)
|
||||
{
|
||||
std::set<std::pair<unsigned, unsigned>> result;
|
||||
for (const auto& e : _sectors)
|
||||
result.insert(
|
||||
std::make_pair(std::get<0>(e.first), std::get<1>(e.first)));
|
||||
return result;
|
||||
for (auto& location : diskLayout.logicalSectorLocationsInFilesystemOrder)
|
||||
if (!_sectors.contains(location))
|
||||
{
|
||||
auto& ltl = diskLayout.layoutByLogicalLocation.at(
|
||||
{location.logicalCylinder, location.logicalHead});
|
||||
auto sector = std::make_shared<Sector>(location);
|
||||
|
||||
if (populated)
|
||||
sector->data = Bytes(ltl->sectorSize);
|
||||
else
|
||||
sector->status = Sector::MISSING;
|
||||
|
||||
_sectors[location] = sector;
|
||||
}
|
||||
calculateSize();
|
||||
}
|
||||
|
||||
void Image::calculateSize()
|
||||
@@ -100,16 +80,39 @@ void Image::calculateSize()
|
||||
const auto& sector = i.second;
|
||||
if (sector)
|
||||
{
|
||||
_geometry.numTracks = std::max(
|
||||
_geometry.numTracks, (unsigned)sector->logicalTrack + 1);
|
||||
_geometry.numSides =
|
||||
std::max(_geometry.numSides, (unsigned)sector->logicalSide + 1);
|
||||
_geometry.numCylinders = std::max(
|
||||
_geometry.numCylinders, (unsigned)sector->logicalCylinder + 1);
|
||||
_geometry.numHeads =
|
||||
std::max(_geometry.numHeads, (unsigned)sector->logicalHead + 1);
|
||||
_geometry.firstSector = std::min(
|
||||
_geometry.firstSector, (unsigned)sector->logicalSector);
|
||||
maxSector = std::max(maxSector, (unsigned)sector->logicalSector);
|
||||
_geometry.sectorSize =
|
||||
std::max(_geometry.sectorSize, (unsigned)sector->data.size());
|
||||
std::max(_geometry.sectorSize, sector->data.size());
|
||||
_geometry.totalBytes += _geometry.sectorSize;
|
||||
}
|
||||
}
|
||||
_geometry.numSectors = maxSector - _geometry.firstSector + 1;
|
||||
}
|
||||
|
||||
void Image::populateSectorPhysicalLocationsFromLogicalLocations(
|
||||
const DiskLayout& diskLayout)
|
||||
{
|
||||
Image tempImage;
|
||||
for (const auto& sector : *this)
|
||||
{
|
||||
const auto& ltl = diskLayout.layoutByLogicalLocation.at(
|
||||
{sector->logicalCylinder, sector->logicalHead});
|
||||
auto newSector = tempImage.put(sector->logicalCylinder,
|
||||
sector->logicalHead,
|
||||
sector->logicalSector);
|
||||
*newSector = *sector;
|
||||
newSector->physicalLocation = std::make_optional<CylinderHead>(
|
||||
ltl->physicalCylinder, ltl->physicalHead);
|
||||
}
|
||||
|
||||
for (const auto& sector : tempImage)
|
||||
_sectors[{sector->logicalCylinder,
|
||||
sector->logicalHead,
|
||||
sector->logicalSector}] = sector;
|
||||
}
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
#ifndef IMAGE_H
|
||||
#define IMAGE_H
|
||||
|
||||
#include "lib/data/locations.h"
|
||||
|
||||
class Sector;
|
||||
class DiskLayout;
|
||||
|
||||
struct Geometry
|
||||
{
|
||||
unsigned numTracks = 0;
|
||||
unsigned numSides = 0;
|
||||
unsigned numCylinders = 0;
|
||||
unsigned numHeads = 0;
|
||||
unsigned firstSector = UINT_MAX;
|
||||
unsigned numSectors = 0;
|
||||
unsigned sectorSize = 0;
|
||||
bool irregular = false;
|
||||
unsigned totalBytes = 0;
|
||||
};
|
||||
|
||||
class Image
|
||||
{
|
||||
private:
|
||||
typedef std::tuple<unsigned, unsigned, unsigned> key_t;
|
||||
|
||||
public:
|
||||
Image();
|
||||
Image(std::set<std::shared_ptr<const Sector>>& sectors);
|
||||
Image(const std::vector<std::shared_ptr<const Sector>>& sectors);
|
||||
|
||||
public:
|
||||
class const_iterator
|
||||
{
|
||||
typedef std::map<key_t, std::shared_ptr<const Sector>>::const_iterator
|
||||
wrapped_iterator_t;
|
||||
typedef std::map<LogicalLocation,
|
||||
std::shared_ptr<const Sector>>::const_iterator wrapped_iterator_t;
|
||||
|
||||
public:
|
||||
const_iterator(const wrapped_iterator_t& it): _it(it) {}
|
||||
@@ -60,14 +61,53 @@ public:
|
||||
void clear();
|
||||
void createBlankImage();
|
||||
bool empty() const;
|
||||
bool contains(unsigned track, unsigned side, unsigned sectorId) const;
|
||||
std::shared_ptr<const Sector> get(
|
||||
unsigned track, unsigned side, unsigned sectorId) const;
|
||||
std::shared_ptr<Sector> put(
|
||||
unsigned track, unsigned side, unsigned sectorId);
|
||||
void erase(unsigned track, unsigned side, unsigned sectorId);
|
||||
|
||||
std::set<std::pair<unsigned, unsigned>> tracks() const;
|
||||
bool contains(const LogicalLocation& location) const;
|
||||
std::shared_ptr<const Sector> get(const LogicalLocation& location) const;
|
||||
std::shared_ptr<Sector> put(const LogicalLocation& location);
|
||||
void erase(const LogicalLocation& location);
|
||||
|
||||
bool contains(const CylinderHead& ch, unsigned sector) const
|
||||
{
|
||||
return contains({ch.cylinder, ch.head, sector});
|
||||
}
|
||||
|
||||
bool contains(unsigned cylinder, unsigned head, unsigned sector) const
|
||||
{
|
||||
return contains({cylinder, head, sector});
|
||||
}
|
||||
|
||||
std::shared_ptr<const Sector> get(
|
||||
const CylinderHead& ch, unsigned sector) const
|
||||
{
|
||||
return get({ch.cylinder, ch.head, sector});
|
||||
}
|
||||
|
||||
std::shared_ptr<const Sector> get(
|
||||
unsigned cylinder, unsigned head, unsigned sector) const
|
||||
{
|
||||
return get({cylinder, head, sector});
|
||||
}
|
||||
|
||||
std::shared_ptr<Sector> put(const CylinderHead& ch, unsigned sector)
|
||||
{
|
||||
return put({ch.cylinder, ch.head, sector});
|
||||
}
|
||||
|
||||
std::shared_ptr<Sector> put(
|
||||
unsigned cylinder, unsigned head, unsigned sector)
|
||||
{
|
||||
return put({cylinder, head, sector});
|
||||
}
|
||||
|
||||
void erase(unsigned cylinder, unsigned head, unsigned sector)
|
||||
{
|
||||
erase({cylinder, head, sector});
|
||||
}
|
||||
|
||||
void addMissingSectors(const DiskLayout& layout, bool populated = false);
|
||||
void populateSectorPhysicalLocationsFromLogicalLocations(
|
||||
const DiskLayout& diskLayout);
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
@@ -89,7 +129,7 @@ public:
|
||||
|
||||
private:
|
||||
Geometry _geometry = {0, 0, 0};
|
||||
std::map<key_t, std::shared_ptr<const Sector>> _sectors;
|
||||
std::map<LogicalLocation, std::shared_ptr<const Sector>> _sectors;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
#include "lib/config/proto.h"
|
||||
#include "lib/core/logger.h"
|
||||
|
||||
static unsigned getTrackStep()
|
||||
static unsigned getTrackStep(const ConfigProto& config = globalConfig())
|
||||
{
|
||||
auto format_type = globalConfig()->layout().format_type();
|
||||
auto drive_type = globalConfig()->drive().drive_type();
|
||||
auto format_type = config.layout().format_type();
|
||||
auto drive_type = config.drive().drive_type();
|
||||
|
||||
switch (format_type)
|
||||
{
|
||||
@@ -22,6 +22,9 @@ static unsigned getTrackStep()
|
||||
|
||||
case DRIVETYPE_APPLE2:
|
||||
return 4;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
case FORMATTYPE_80TRACK:
|
||||
@@ -30,8 +33,7 @@ static unsigned getTrackStep()
|
||||
case DRIVETYPE_40TRACK:
|
||||
error(
|
||||
"you can't read/write an 80 track image from/to a 40 "
|
||||
"track "
|
||||
"drive");
|
||||
"track drive");
|
||||
|
||||
case DRIVETYPE_80TRACK:
|
||||
return 1;
|
||||
@@ -39,111 +41,55 @@ static unsigned getTrackStep()
|
||||
case DRIVETYPE_APPLE2:
|
||||
error(
|
||||
"you can't read/write an 80 track image from/to an "
|
||||
"Apple II "
|
||||
"drive");
|
||||
"Apple II drive");
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned Layout::remapTrackPhysicalToLogical(unsigned ptrack)
|
||||
static std::vector<CylinderHead> getTrackOrdering(
|
||||
LayoutProto::Order ordering, unsigned tracks, unsigned sides)
|
||||
{
|
||||
return (ptrack - globalConfig()->drive().head_bias()) / getTrackStep();
|
||||
}
|
||||
|
||||
unsigned Layout::remapTrackLogicalToPhysical(unsigned ltrack)
|
||||
{
|
||||
return globalConfig()->drive().head_bias() + ltrack * getTrackStep();
|
||||
}
|
||||
|
||||
unsigned Layout::remapSidePhysicalToLogical(unsigned pside)
|
||||
{
|
||||
return pside ^ globalConfig()->layout().swap_sides();
|
||||
}
|
||||
|
||||
unsigned Layout::remapSideLogicalToPhysical(unsigned lside)
|
||||
{
|
||||
return lside ^ globalConfig()->layout().swap_sides();
|
||||
}
|
||||
|
||||
std::vector<CylinderHead> Layout::computePhysicalLocations()
|
||||
{
|
||||
if (globalConfig()->has_tracks())
|
||||
return parseCylinderHeadsString(globalConfig()->tracks());
|
||||
|
||||
std::set<unsigned> tracks = iterate(0, globalConfig()->layout().tracks());
|
||||
std::set<unsigned> heads = iterate(0, globalConfig()->layout().sides());
|
||||
|
||||
std::vector<CylinderHead> locations;
|
||||
for (unsigned logicalTrack : tracks)
|
||||
for (unsigned logicalHead : heads)
|
||||
locations.push_back(
|
||||
CylinderHead{remapTrackLogicalToPhysical(logicalTrack),
|
||||
remapSideLogicalToPhysical(logicalHead)});
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
Layout::LayoutBounds Layout::getBounds(
|
||||
const std::vector<CylinderHead>& locations)
|
||||
{
|
||||
LayoutBounds r{.minTrack = INT_MAX,
|
||||
.maxTrack = INT_MIN,
|
||||
.minSide = INT_MAX,
|
||||
.maxSide = INT_MIN};
|
||||
|
||||
for (const auto& ti : locations)
|
||||
{
|
||||
r.minTrack = std::min<int>(r.minTrack, ti.cylinder);
|
||||
r.maxTrack = std::max<int>(r.maxTrack, ti.cylinder);
|
||||
r.minSide = std::min<int>(r.minSide, ti.head);
|
||||
r.maxSide = std::max<int>(r.maxSide, ti.head);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> Layout::getTrackOrdering(
|
||||
LayoutProto::Order ordering, unsigned guessedTracks, unsigned guessedSides)
|
||||
{
|
||||
auto layout = globalConfig()->layout();
|
||||
int tracks = layout.has_tracks() ? layout.tracks() : guessedTracks;
|
||||
int sides = layout.has_sides() ? layout.sides() : guessedSides;
|
||||
|
||||
std::vector<std::pair<int, int>> trackList;
|
||||
std::vector<CylinderHead> trackList;
|
||||
switch (ordering)
|
||||
{
|
||||
case LayoutProto::CHS:
|
||||
{
|
||||
for (int track = 0; track < tracks; track++)
|
||||
for (unsigned track = 0; track < tracks; track++)
|
||||
{
|
||||
for (int side = 0; side < sides; side++)
|
||||
trackList.push_back(std::make_pair(track, side));
|
||||
for (unsigned side = 0; side < sides; side++)
|
||||
trackList.push_back({track, side});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LayoutProto::HCS:
|
||||
{
|
||||
for (int side = 0; side < sides; side++)
|
||||
for (unsigned side = 0; side < sides; side++)
|
||||
{
|
||||
for (int track = 0; track < tracks; track++)
|
||||
trackList.push_back(std::make_pair(track, side));
|
||||
for (unsigned track = 0; track < tracks; track++)
|
||||
trackList.push_back({track, side});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LayoutProto::HCS_RH1:
|
||||
{
|
||||
for (int side = 0; side < sides; side++)
|
||||
for (unsigned side = 0; side < sides; side++)
|
||||
{
|
||||
if (side == 0)
|
||||
for (int track = 0; track < tracks; track++)
|
||||
trackList.push_back(std::make_pair(track, side));
|
||||
for (unsigned track = 0; track < tracks; track++)
|
||||
trackList.push_back({track, side});
|
||||
if (side == 1)
|
||||
for (int track = tracks; track >= 0; track--)
|
||||
trackList.push_back(std::make_pair(track - 1, side));
|
||||
for (unsigned track = tracks; track >= 0; track--)
|
||||
trackList.push_back({track - 1, side});
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -155,7 +101,7 @@ std::vector<std::pair<int, int>> Layout::getTrackOrdering(
|
||||
return trackList;
|
||||
}
|
||||
|
||||
std::vector<unsigned> Layout::expandSectorList(
|
||||
static std::vector<unsigned> expandSectorList(
|
||||
const SectorListProto& sectorsProto)
|
||||
{
|
||||
std::vector<unsigned> sectors;
|
||||
@@ -197,95 +143,200 @@ std::vector<unsigned> Layout::expandSectorList(
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::shared_ptr<const TrackInfo> Layout::getLayoutOfTrack(
|
||||
unsigned logicalTrack, unsigned logicalSide)
|
||||
static const LayoutProto::LayoutdataProto getLayoutData(
|
||||
unsigned logicalCylinder, unsigned logicalHead, const ConfigProto& config)
|
||||
{
|
||||
auto trackInfo = std::make_shared<TrackInfo>();
|
||||
|
||||
LayoutProto::LayoutdataProto layoutdata;
|
||||
for (const auto& f : globalConfig()->layout().layoutdata())
|
||||
LayoutProto::LayoutdataProto layoutData;
|
||||
for (const auto& f : config.layout().layoutdata())
|
||||
{
|
||||
if (f.has_track() && f.has_up_to_track() &&
|
||||
((logicalTrack < f.track()) || (logicalTrack > f.up_to_track())))
|
||||
((logicalCylinder < f.track()) ||
|
||||
(logicalCylinder > f.up_to_track())))
|
||||
continue;
|
||||
if (f.has_track() && !f.has_up_to_track() &&
|
||||
(logicalTrack != f.track()))
|
||||
(logicalCylinder != f.track()))
|
||||
continue;
|
||||
if (f.has_side() && (f.side() != logicalSide))
|
||||
if (f.has_side() && (f.side() != logicalHead))
|
||||
continue;
|
||||
|
||||
layoutdata.MergeFrom(f);
|
||||
layoutData.MergeFrom(f);
|
||||
}
|
||||
|
||||
trackInfo->numTracks = globalConfig()->layout().tracks();
|
||||
trackInfo->numSides = globalConfig()->layout().sides();
|
||||
trackInfo->sectorSize = layoutdata.sector_size();
|
||||
trackInfo->logicalTrack = logicalTrack;
|
||||
trackInfo->logicalSide = logicalSide;
|
||||
trackInfo->physicalTrack = remapTrackLogicalToPhysical(logicalTrack);
|
||||
trackInfo->physicalSide =
|
||||
logicalSide ^ globalConfig()->layout().swap_sides();
|
||||
trackInfo->groupSize = getTrackStep();
|
||||
trackInfo->diskSectorOrder = expandSectorList(layoutdata.physical());
|
||||
trackInfo->naturalSectorOrder = trackInfo->diskSectorOrder;
|
||||
std::sort(trackInfo->naturalSectorOrder.begin(),
|
||||
trackInfo->naturalSectorOrder.end());
|
||||
trackInfo->numSectors = trackInfo->naturalSectorOrder.size();
|
||||
|
||||
if (layoutdata.has_filesystem())
|
||||
{
|
||||
trackInfo->filesystemSectorOrder =
|
||||
expandSectorList(layoutdata.filesystem());
|
||||
if (trackInfo->filesystemSectorOrder.size() != trackInfo->numSectors)
|
||||
error(
|
||||
"filesystem sector order list doesn't contain the right "
|
||||
"number of sectors");
|
||||
}
|
||||
else
|
||||
trackInfo->filesystemSectorOrder = trackInfo->naturalSectorOrder;
|
||||
|
||||
for (int i = 0; i < trackInfo->numSectors; i++)
|
||||
{
|
||||
unsigned fid = trackInfo->naturalSectorOrder[i];
|
||||
unsigned lid = trackInfo->filesystemSectorOrder[i];
|
||||
trackInfo->filesystemToNaturalSectorMap[fid] = lid;
|
||||
trackInfo->naturalToFilesystemSectorMap[lid] = fid;
|
||||
}
|
||||
|
||||
return trackInfo;
|
||||
return layoutData;
|
||||
}
|
||||
|
||||
std::shared_ptr<const TrackInfo> Layout::getLayoutOfTrackPhysical(
|
||||
unsigned physicalTrack, unsigned physicalSide)
|
||||
DiskLayout::DiskLayout(const ConfigProto& config)
|
||||
{
|
||||
return getLayoutOfTrack(remapTrackPhysicalToLogical(physicalTrack),
|
||||
remapSidePhysicalToLogical(physicalSide));
|
||||
}
|
||||
minPhysicalCylinder = minPhysicalHead = UINT_MAX;
|
||||
maxPhysicalCylinder = maxPhysicalHead = 0;
|
||||
|
||||
std::shared_ptr<const TrackInfo> Layout::getLayoutOfTrackPhysical(
|
||||
const CylinderHead& physicalLocation)
|
||||
{
|
||||
return getLayoutOfTrackPhysical(
|
||||
physicalLocation.cylinder, physicalLocation.head);
|
||||
}
|
||||
numLogicalCylinders = config.layout().tracks();
|
||||
numLogicalHeads = config.layout().sides();
|
||||
|
||||
std::vector<std::shared_ptr<const TrackInfo>> Layout::getLayoutOfTracksPhysical(
|
||||
const std::vector<CylinderHead>& physicalLocations)
|
||||
{
|
||||
std::vector<std::shared_ptr<const TrackInfo>> results;
|
||||
for (const auto& physicalLocation : physicalLocations)
|
||||
results.push_back(getLayoutOfTrackPhysical(physicalLocation));
|
||||
return results;
|
||||
}
|
||||
groupSize = getTrackStep(config);
|
||||
headBias = config.drive().head_bias();
|
||||
swapSides = config.layout().swap_sides();
|
||||
|
||||
int Layout::getHeadWidth()
|
||||
{
|
||||
switch (globalConfig()->drive().drive_type())
|
||||
switch (config.drive().drive_type())
|
||||
{
|
||||
case DRIVETYPE_APPLE2:
|
||||
return 4;
|
||||
headWidth = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
headWidth = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
for (unsigned logicalCylinder = 0; logicalCylinder < numLogicalCylinders;
|
||||
logicalCylinder++)
|
||||
for (unsigned logicalHead = 0; logicalHead < numLogicalHeads;
|
||||
logicalHead++)
|
||||
{
|
||||
auto ltl = std::make_shared<LogicalTrackLayout>();
|
||||
CylinderHead ch(logicalCylinder, logicalHead);
|
||||
layoutByLogicalLocation[ch] = ltl;
|
||||
logicalLocations.push_back(ch);
|
||||
|
||||
ltl->logicalCylinder = logicalCylinder;
|
||||
ltl->logicalHead = logicalHead;
|
||||
ltl->groupSize = groupSize;
|
||||
ltl->physicalCylinder =
|
||||
remapCylinderLogicalToPhysical(logicalCylinder);
|
||||
ltl->physicalHead = remapHeadLogicalToPhysical(logicalHead);
|
||||
|
||||
minPhysicalCylinder =
|
||||
std::min(minPhysicalCylinder, ltl->physicalCylinder);
|
||||
maxPhysicalCylinder = std::max(maxPhysicalCylinder,
|
||||
ltl->physicalCylinder + ltl->groupSize - 1);
|
||||
minPhysicalHead = std::min(minPhysicalHead, ltl->physicalHead);
|
||||
maxPhysicalHead = std::max(maxPhysicalHead, ltl->physicalHead);
|
||||
|
||||
auto layoutdata =
|
||||
getLayoutData(logicalCylinder, logicalHead, config);
|
||||
ltl->sectorSize = layoutdata.sector_size();
|
||||
ltl->diskSectorOrder = expandSectorList(layoutdata.physical());
|
||||
ltl->naturalSectorOrder = ltl->diskSectorOrder;
|
||||
std::sort(
|
||||
ltl->naturalSectorOrder.begin(), ltl->naturalSectorOrder.end());
|
||||
ltl->numSectors = ltl->naturalSectorOrder.size();
|
||||
|
||||
if (layoutdata.has_filesystem())
|
||||
{
|
||||
ltl->filesystemSectorOrder =
|
||||
expandSectorList(layoutdata.filesystem());
|
||||
if (ltl->filesystemSectorOrder.size() != ltl->numSectors)
|
||||
error(
|
||||
"filesystem sector order list doesn't contain the "
|
||||
"right number of sectors");
|
||||
}
|
||||
else
|
||||
ltl->filesystemSectorOrder = ltl->naturalSectorOrder;
|
||||
|
||||
for (int i = 0; i < ltl->numSectors; i++)
|
||||
{
|
||||
unsigned fid = ltl->naturalSectorOrder[i];
|
||||
unsigned lid = ltl->filesystemSectorOrder[i];
|
||||
ltl->sectorIdToNaturalOrdering[i] = fid;
|
||||
ltl->sectorIdToFilesystemOrdering[i] = fid;
|
||||
}
|
||||
};
|
||||
|
||||
for (unsigned physicalCylinder = minPhysicalCylinder;
|
||||
physicalCylinder <= maxPhysicalCylinder;
|
||||
physicalCylinder++)
|
||||
for (unsigned physicalHead = minPhysicalHead;
|
||||
physicalHead <= maxPhysicalHead;
|
||||
physicalHead++)
|
||||
{
|
||||
auto ptl = std::make_shared<PhysicalTrackLayout>();
|
||||
CylinderHead ch(physicalCylinder, physicalHead);
|
||||
layoutByPhysicalLocation[ch] = ptl;
|
||||
physicalLocations.push_back(ch);
|
||||
|
||||
ptl->physicalCylinder = physicalCylinder;
|
||||
ptl->physicalHead = physicalHead;
|
||||
ptl->groupOffset = (physicalCylinder - headBias) % groupSize;
|
||||
|
||||
unsigned logicalCylinder =
|
||||
remapCylinderPhysicalToLogical(physicalCylinder);
|
||||
unsigned logicalHead = remapHeadPhysicalToLogical(physicalHead);
|
||||
ptl->logicalTrackLayout = findOrDefault(
|
||||
layoutByLogicalLocation, {logicalCylinder, logicalHead});
|
||||
}
|
||||
|
||||
unsigned sectorOffset = 0;
|
||||
unsigned blockId = 0;
|
||||
for (auto& ch : getTrackOrdering(config.layout().filesystem_track_order(),
|
||||
numLogicalCylinders,
|
||||
numLogicalHeads))
|
||||
{
|
||||
const auto& ltl = layoutByLogicalLocation[ch];
|
||||
logicalLocationsInFilesystemOrder.push_back(ch);
|
||||
|
||||
for (unsigned lid : ltl->filesystemSectorOrder)
|
||||
{
|
||||
LogicalLocation logicalLocation = {ch.cylinder, ch.head, lid};
|
||||
logicalSectorLocationBySectorOffset[sectorOffset] = logicalLocation;
|
||||
sectorOffsetByLogicalSectorLocation[logicalLocation] = sectorOffset;
|
||||
logicalSectorLocationsInFilesystemOrder.push_back(logicalLocation);
|
||||
sectorOffset += ltl->sectorSize;
|
||||
|
||||
blockIdByLogicalSectorLocation[logicalLocation] = blockId;
|
||||
blockId++;
|
||||
}
|
||||
}
|
||||
|
||||
totalBytes = sectorOffset;
|
||||
}
|
||||
|
||||
static ConfigProto createTestConfig(unsigned numCylinders,
|
||||
unsigned numHeads,
|
||||
unsigned numSectors,
|
||||
unsigned sectorSize)
|
||||
{
|
||||
ConfigProto config;
|
||||
auto* layout = config.mutable_layout();
|
||||
layout->set_tracks(numCylinders);
|
||||
layout->set_sides(numHeads);
|
||||
auto* layoutData = layout->add_layoutdata();
|
||||
layoutData->set_sector_size(sectorSize);
|
||||
layoutData->mutable_physical()->set_count(numSectors);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
DiskLayout::DiskLayout(unsigned numCylinders,
|
||||
unsigned numHeads,
|
||||
unsigned numSectors,
|
||||
unsigned sectorSize):
|
||||
DiskLayout(createTestConfig(numCylinders, numHeads, numSectors, sectorSize))
|
||||
{
|
||||
}
|
||||
|
||||
static DiskLayout::LayoutBounds getBounds(std::ranges::view auto keys)
|
||||
{
|
||||
DiskLayout::LayoutBounds r{.minCylinder = INT_MAX,
|
||||
.maxCylinder = INT_MIN,
|
||||
.minHead = INT_MAX,
|
||||
.maxHead = INT_MIN};
|
||||
|
||||
for (const auto& ch : keys)
|
||||
{
|
||||
r.minCylinder = std::min<int>(r.minCylinder, ch.cylinder);
|
||||
r.maxCylinder = std::max<int>(r.maxCylinder, ch.cylinder);
|
||||
r.minHead = std::min<int>(r.minHead, ch.head);
|
||||
r.maxHead = std::max<int>(r.maxHead, ch.head);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
DiskLayout::LayoutBounds DiskLayout::getPhysicalBounds() const
|
||||
{
|
||||
return getBounds(std::views::keys(layoutByPhysicalLocation));
|
||||
}
|
||||
|
||||
DiskLayout::LayoutBounds DiskLayout::getLogicalBounds() const
|
||||
{
|
||||
return getBounds(std::views::keys(layoutByLogicalLocation));
|
||||
}
|
||||
|
||||
@@ -1,73 +1,163 @@
|
||||
#ifndef LAYOUT_H
|
||||
#define LAYOUT_H
|
||||
|
||||
#include "lib/data/flux.h"
|
||||
#include "lib/data/disk.h"
|
||||
#include "lib/config/layout.pb.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/data/locations.h"
|
||||
|
||||
class SectorListProto;
|
||||
class TrackInfo;
|
||||
class ConfigProto;
|
||||
|
||||
class Layout
|
||||
struct LogicalTrackLayout
|
||||
{
|
||||
/* Physical cylinder of the first element of the group. */
|
||||
unsigned physicalCylinder;
|
||||
|
||||
/* Physical head of the first element of the group. */
|
||||
unsigned physicalHead;
|
||||
|
||||
/* Size of this group. */
|
||||
unsigned groupSize;
|
||||
|
||||
/* Logical cylinder of this track. */
|
||||
unsigned logicalCylinder = 0;
|
||||
|
||||
/* Logical side of this track. */
|
||||
unsigned logicalHead = 0;
|
||||
|
||||
/* The number of sectors in this track. */
|
||||
unsigned numSectors = 0;
|
||||
|
||||
/* Number of bytes in a sector. */
|
||||
unsigned sectorSize = 0;
|
||||
|
||||
/* Sector IDs in sector ID order. This is the order in which the appear in
|
||||
* disk images. */
|
||||
std::vector<unsigned> naturalSectorOrder;
|
||||
|
||||
/* Sector IDs in disk order. This is the order they are written to the disk.
|
||||
*/
|
||||
std::vector<unsigned> diskSectorOrder;
|
||||
|
||||
/* Sector IDs in filesystem order. This is the order in which the filesystem
|
||||
* uses them. */
|
||||
std::vector<unsigned> filesystemSectorOrder;
|
||||
|
||||
/* Mapping of sector ID to filesystem ordering. */
|
||||
std::map<unsigned, unsigned> sectorIdToFilesystemOrdering;
|
||||
|
||||
/* Mapping of sector ID to natural ordering. */
|
||||
std::map<unsigned, unsigned> sectorIdToNaturalOrdering;
|
||||
};
|
||||
|
||||
struct PhysicalTrackLayout
|
||||
{
|
||||
/* Physical location of this track. */
|
||||
unsigned physicalCylinder;
|
||||
|
||||
/* Physical side of this track. */
|
||||
unsigned physicalHead;
|
||||
|
||||
/* Which member of the group this is. */
|
||||
unsigned groupOffset;
|
||||
|
||||
/* The logical track that this track is part of. */
|
||||
std::shared_ptr<const LogicalTrackLayout> logicalTrackLayout;
|
||||
};
|
||||
|
||||
class DiskLayout
|
||||
{
|
||||
public:
|
||||
/* Translates logical track numbering (the numbers actually written in the
|
||||
* sector headers) to the track numbering on the actual drive, taking into
|
||||
* account tpi settings.
|
||||
*/
|
||||
static unsigned remapTrackPhysicalToLogical(unsigned physicalTrack);
|
||||
static unsigned remapTrackLogicalToPhysical(unsigned logicalTrack);
|
||||
DiskLayout(const ConfigProto& config = globalConfig());
|
||||
|
||||
/* Translates logical side numbering (the numbers actually written in the
|
||||
* sector headers) to the sides used on the actual drive.
|
||||
*/
|
||||
static unsigned remapSidePhysicalToLogical(unsigned physicalSide);
|
||||
static unsigned remapSideLogicalToPhysical(unsigned logicalSide);
|
||||
/* Makes a simplified layout for testing. */
|
||||
|
||||
/* Uses the layout and current track and heads settings to determine
|
||||
* which physical tracks are going to be read from or written to.
|
||||
*/
|
||||
static std::vector<CylinderHead> computePhysicalLocations();
|
||||
DiskLayout(unsigned numCylinders,
|
||||
unsigned numHeads,
|
||||
unsigned numSectors,
|
||||
unsigned sectorSize);
|
||||
|
||||
public:
|
||||
/* Logical size. */
|
||||
|
||||
unsigned numLogicalCylinders;
|
||||
unsigned numLogicalHeads;
|
||||
|
||||
/* Physical size and properties. */
|
||||
|
||||
unsigned minPhysicalCylinder, maxPhysicalCylinder;
|
||||
unsigned minPhysicalHead, maxPhysicalHead;
|
||||
unsigned groupSize; /* Number of physical cylinders per logical cylinder */
|
||||
unsigned headBias; /* Physical cylinder offset */
|
||||
unsigned headWidth; /* Width of the physical head */
|
||||
bool swapSides; /* Whether sides need to be swapped */
|
||||
unsigned totalBytes; /* Total number of bytes on the disk. */
|
||||
|
||||
/* Physical and logical layouts by location. */
|
||||
|
||||
std::map<CylinderHead, std::shared_ptr<const PhysicalTrackLayout>>
|
||||
layoutByPhysicalLocation;
|
||||
std::map<CylinderHead, std::shared_ptr<const LogicalTrackLayout>>
|
||||
layoutByLogicalLocation;
|
||||
|
||||
/* Ordered lists of physical and logical locations. */
|
||||
|
||||
std::vector<CylinderHead> logicalLocations;
|
||||
std::vector<CylinderHead> logicalLocationsInFilesystemOrder;
|
||||
std::vector<CylinderHead> physicalLocations;
|
||||
|
||||
/* Ordered lists of sector locations, plus the reverse mapping. */
|
||||
|
||||
std::vector<LogicalLocation> logicalSectorLocationsInFilesystemOrder;
|
||||
std::map<LogicalLocation, unsigned> blockIdByLogicalSectorLocation;
|
||||
std::vector<CylinderHeadSector> physicalSectorLocationsInFilesystemOrder;
|
||||
|
||||
/* Mapping from logical location to sector offset and back again. */
|
||||
|
||||
std::map<unsigned, LogicalLocation> logicalSectorLocationBySectorOffset;
|
||||
std::map<LogicalLocation, unsigned> sectorOffsetByLogicalSectorLocation;
|
||||
|
||||
public:
|
||||
unsigned remapCylinderPhysicalToLogical(unsigned physicalCylinder) const
|
||||
{
|
||||
return (physicalCylinder - headBias) / groupSize;
|
||||
}
|
||||
|
||||
unsigned remapCylinderLogicalToPhysical(unsigned logicalCylinder) const
|
||||
{
|
||||
return headBias + logicalCylinder * groupSize;
|
||||
}
|
||||
|
||||
unsigned remapHeadPhysicalToLogical(unsigned physicalHead) const
|
||||
{
|
||||
return physicalHead ^ swapSides;
|
||||
}
|
||||
|
||||
unsigned remapHeadLogicalToPhysical(unsigned logicalHead) const
|
||||
{
|
||||
return logicalHead ^ swapSides;
|
||||
}
|
||||
|
||||
/* Given a list of CylinderHead locations, determines the minimum and
|
||||
* maximum track and side settings. */
|
||||
struct LayoutBounds
|
||||
{
|
||||
int minTrack, maxTrack, minSide, maxSide;
|
||||
std::strong_ordering operator<=>(
|
||||
const LayoutBounds& other) const = default;
|
||||
|
||||
int minCylinder, maxCylinder, minHead, maxHead;
|
||||
};
|
||||
static LayoutBounds getBounds(const std::vector<CylinderHead>& locations);
|
||||
|
||||
/* Returns a series of <track, side> pairs representing the filesystem
|
||||
* ordering of the disk, in logical numbers. */
|
||||
static std::vector<std::pair<int, int>> getTrackOrdering(
|
||||
LayoutProto::Order ordering,
|
||||
unsigned guessedTracks = 0,
|
||||
unsigned guessedSides = 0);
|
||||
|
||||
/* Returns the layout of a given track. */
|
||||
static std::shared_ptr<const TrackInfo> getLayoutOfTrack(
|
||||
unsigned logicalTrack, unsigned logicalHead);
|
||||
|
||||
/* Returns the layout of a given track via physical location. */
|
||||
static std::shared_ptr<const TrackInfo> getLayoutOfTrackPhysical(
|
||||
unsigned physicalTrack, unsigned physicalSide);
|
||||
|
||||
/* Returns the layout of a given track via physical location. */
|
||||
static std::shared_ptr<const TrackInfo> getLayoutOfTrackPhysical(
|
||||
const CylinderHead& physicalLocation);
|
||||
|
||||
/* Returns the layouts of a multiple tracks via physical location. */
|
||||
static std::vector<std::shared_ptr<const TrackInfo>>
|
||||
getLayoutOfTracksPhysical(const std::vector<CylinderHead>& locations);
|
||||
|
||||
/* Expand a SectorList into the actual sector IDs. */
|
||||
static std::vector<unsigned> expandSectorList(
|
||||
const SectorListProto& sectorsProto);
|
||||
|
||||
/* Return the head width of the current drive. */
|
||||
static int getHeadWidth();
|
||||
LayoutBounds getPhysicalBounds() const;
|
||||
LayoutBounds getLogicalBounds() const;
|
||||
};
|
||||
|
||||
static std::shared_ptr<DiskLayout> createDiskLayout(
|
||||
const ConfigProto& config = globalConfig())
|
||||
{
|
||||
return std::make_shared<DiskLayout>(config);
|
||||
}
|
||||
|
||||
class TrackInfo
|
||||
{
|
||||
public:
|
||||
@@ -79,23 +169,23 @@ private:
|
||||
TrackInfo& operator=(const TrackInfo&);
|
||||
|
||||
public:
|
||||
unsigned numTracks = 0;
|
||||
unsigned numSides = 0;
|
||||
unsigned numCylinders = 0;
|
||||
unsigned numHeads = 0;
|
||||
|
||||
/* The number of sectors in this track. */
|
||||
unsigned numSectors = 0;
|
||||
|
||||
/* Physical location of this track. */
|
||||
unsigned physicalTrack = 0;
|
||||
unsigned physicalCylinder = 0;
|
||||
|
||||
/* Physical side of this track. */
|
||||
unsigned physicalSide = 0;
|
||||
unsigned physicalHead = 0;
|
||||
|
||||
/* Logical location of this track. */
|
||||
unsigned logicalTrack = 0;
|
||||
unsigned logicalCylinder = 0;
|
||||
|
||||
/* Logical side of this track. */
|
||||
unsigned logicalSide = 0;
|
||||
unsigned logicalHead = 0;
|
||||
|
||||
/* The number of physical tracks which need to be written for one logical
|
||||
* track. */
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <lexy_ext/report_error.hpp>
|
||||
#include "fmt/ranges.h"
|
||||
#include <ranges>
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@@ -8,6 +8,41 @@ struct CylinderHead
|
||||
unsigned cylinder, head;
|
||||
};
|
||||
|
||||
struct CylinderHeadSector
|
||||
{
|
||||
bool operator==(const CylinderHeadSector&) const = default;
|
||||
std::strong_ordering operator<=>(const CylinderHeadSector&) const = default;
|
||||
|
||||
unsigned cylinder, head, sector;
|
||||
};
|
||||
|
||||
struct LogicalLocation
|
||||
{
|
||||
bool operator==(const LogicalLocation&) const = default;
|
||||
std::strong_ordering operator<=>(const LogicalLocation&) const = default;
|
||||
|
||||
unsigned logicalCylinder;
|
||||
unsigned logicalHead;
|
||||
unsigned logicalSector;
|
||||
|
||||
operator std::string() const
|
||||
{
|
||||
return fmt::format(
|
||||
"c{}h{}s{}", logicalCylinder, logicalHead, logicalSector);
|
||||
}
|
||||
|
||||
CylinderHead trackLocation() const
|
||||
{
|
||||
return {logicalCylinder, logicalHead};
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& stream, LogicalLocation location)
|
||||
{
|
||||
stream << (std::string)location;
|
||||
return stream;
|
||||
}
|
||||
|
||||
extern std::vector<CylinderHead> parseCylinderHeadsString(const std::string& s);
|
||||
extern std::string convertCylinderHeadsToString(
|
||||
const std::vector<CylinderHead>& chs);
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/data/flux.h"
|
||||
#include "lib/data/disk.h"
|
||||
#include "lib/data/sector.h"
|
||||
#include "lib/data/layout.h"
|
||||
|
||||
Sector::Sector(const LogicalLocation& location):
|
||||
LogicalLocation(location),
|
||||
physicalTrack(Layout::remapTrackLogicalToPhysical(location.logicalTrack)),
|
||||
physicalSide(Layout::remapSideLogicalToPhysical(location.logicalSide))
|
||||
{
|
||||
}
|
||||
|
||||
Sector::Sector(std::shared_ptr<const TrackInfo>& layout, unsigned sectorId):
|
||||
LogicalLocation({layout->logicalTrack, layout->logicalSide, sectorId}),
|
||||
physicalTrack(layout->physicalTrack),
|
||||
physicalSide(layout->physicalSide)
|
||||
{
|
||||
}
|
||||
Sector::Sector(const LogicalLocation& location): LogicalLocation(location) {}
|
||||
|
||||
std::string Sector::statusToString(Status status)
|
||||
{
|
||||
@@ -32,7 +20,7 @@ std::string Sector::statusToString(Status status)
|
||||
case Status::CONFLICT:
|
||||
return "conflicting data";
|
||||
default:
|
||||
return fmt::format("unknown error {}", status);
|
||||
return fmt::format("unknown error {}", (int)status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,36 +3,10 @@
|
||||
|
||||
#include "lib/core/bytes.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/data/locations.h"
|
||||
|
||||
class Record;
|
||||
class TrackInfo;
|
||||
|
||||
struct LogicalLocation
|
||||
{
|
||||
unsigned logicalTrack;
|
||||
unsigned logicalSide;
|
||||
unsigned logicalSector;
|
||||
|
||||
std::tuple<int, int, int> key() const
|
||||
{
|
||||
return std::make_tuple(logicalTrack, logicalSide, logicalSector);
|
||||
}
|
||||
|
||||
bool operator==(const LogicalLocation& rhs) const
|
||||
{
|
||||
return key() == rhs.key();
|
||||
}
|
||||
|
||||
bool operator!=(const LogicalLocation& rhs) const
|
||||
{
|
||||
return key() != rhs.key();
|
||||
}
|
||||
|
||||
bool operator<(const LogicalLocation& rhs) const
|
||||
{
|
||||
return key() < rhs.key();
|
||||
}
|
||||
};
|
||||
class LogicalTrackLayout;
|
||||
|
||||
struct Sector : public LogicalLocation
|
||||
{
|
||||
@@ -57,36 +31,24 @@ struct Sector : public LogicalLocation
|
||||
nanoseconds_t headerEndTime = 0;
|
||||
nanoseconds_t dataStartTime = 0;
|
||||
nanoseconds_t dataEndTime = 0;
|
||||
unsigned physicalTrack = 0;
|
||||
unsigned physicalSide = 0;
|
||||
std::optional<CylinderHead> physicalLocation = {};
|
||||
Bytes data;
|
||||
std::vector<std::shared_ptr<Record>> records;
|
||||
|
||||
Sector() {}
|
||||
|
||||
Sector(std::shared_ptr<const TrackInfo>& layout, unsigned sectorId = 0);
|
||||
Sector(const Sector& other) = default;
|
||||
Sector& operator=(const Sector& other) = default;
|
||||
|
||||
Sector(const LogicalLocation& location);
|
||||
|
||||
std::tuple<int, int, int, Status> key() const
|
||||
{
|
||||
return std::make_tuple(
|
||||
logicalTrack, logicalSide, logicalSector, status);
|
||||
logicalCylinder, logicalHead, logicalSector, status);
|
||||
}
|
||||
|
||||
bool operator==(const Sector& rhs) const
|
||||
std::strong_ordering operator<=>(const Sector& rhs) const
|
||||
{
|
||||
return key() == rhs.key();
|
||||
}
|
||||
|
||||
bool operator!=(const Sector& rhs) const
|
||||
{
|
||||
return key() != rhs.key();
|
||||
}
|
||||
|
||||
bool operator<(const Sector& rhs) const
|
||||
{
|
||||
return key() < rhs.key();
|
||||
return key() <=> rhs.key();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,7 +57,7 @@ struct fmt::formatter<Sector::Status> : formatter<string_view>
|
||||
{
|
||||
auto format(Sector::Status status, format_context& ctx) const
|
||||
{
|
||||
return format_to(ctx.out(), "{}", Sector::statusToString(status));
|
||||
return fmt::format_to(ctx.out(), "{}", Sector::statusToString(status));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/decoders/decoders.h"
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/data/flux.h"
|
||||
#include "lib/data/disk.h"
|
||||
#include "protocol.h"
|
||||
#include "lib/decoders/rawbits.h"
|
||||
#include "lib/data/sector.h"
|
||||
@@ -13,20 +13,25 @@
|
||||
#include "lib/data/layout.h"
|
||||
#include <numeric>
|
||||
|
||||
std::shared_ptr<TrackDataFlux> Decoder::decodeToSectors(
|
||||
std::shared_ptr<Track> Decoder::decodeToSectors(
|
||||
std::shared_ptr<const Fluxmap> fluxmap,
|
||||
std::shared_ptr<const TrackInfo>& trackInfo)
|
||||
const std::shared_ptr<const PhysicalTrackLayout>& ptl)
|
||||
{
|
||||
_trackdata = std::make_shared<TrackDataFlux>();
|
||||
_ltl = ptl->logicalTrackLayout;
|
||||
|
||||
_trackdata = std::make_shared<Track>();
|
||||
_trackdata->fluxmap = fluxmap;
|
||||
_trackdata->trackInfo = trackInfo;
|
||||
_trackdata->ptl = ptl;
|
||||
_trackdata->ltl = ptl->logicalTrackLayout;
|
||||
|
||||
FluxmapReader fmr(*fluxmap);
|
||||
_fmr = &fmr;
|
||||
|
||||
auto newSector = [&]
|
||||
{
|
||||
_sector = std::make_shared<Sector>(trackInfo, 0);
|
||||
_sector = std::make_shared<Sector>(LogicalLocation{0, 0, 0});
|
||||
_sector->physicalLocation = std::make_optional<CylinderHead>(
|
||||
ptl->physicalCylinder, ptl->physicalHead);
|
||||
_sector->status = Sector::MISSING;
|
||||
};
|
||||
|
||||
@@ -67,6 +72,7 @@ std::shared_ptr<TrackDataFlux> Decoder::decodeToSectors(
|
||||
|
||||
before = fmr.tell();
|
||||
decodeDataRecord();
|
||||
_sector->data = _sector->data.slice(0, _ltl->sectorSize);
|
||||
after = fmr.tell();
|
||||
|
||||
if (_sector->status != Sector::DATA_MISSING)
|
||||
@@ -84,11 +90,7 @@ std::shared_ptr<TrackDataFlux> Decoder::decodeToSectors(
|
||||
}
|
||||
|
||||
if (_sector->status != Sector::MISSING)
|
||||
{
|
||||
auto trackLayout = Layout::getLayoutOfTrack(
|
||||
_sector->logicalTrack, _sector->logicalSide);
|
||||
_trackdata->sectors.push_back(_sector);
|
||||
}
|
||||
_trackdata->allSectors.push_back(_sector);
|
||||
}
|
||||
|
||||
return _trackdata;
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
#include "lib/data/fluxmapreader.h"
|
||||
#include "lib/decoders/fluxdecoder.h"
|
||||
|
||||
class Sector;
|
||||
class Fluxmap;
|
||||
class FluxMatcher;
|
||||
class FluxmapReader;
|
||||
class RawBits;
|
||||
class DecoderProto;
|
||||
class Config;
|
||||
class DecoderProto;
|
||||
class FluxMatcher;
|
||||
class Fluxmap;
|
||||
class FluxmapReader;
|
||||
class PhysicalTrackLayout;
|
||||
class RawBits;
|
||||
class Sector;
|
||||
|
||||
#include "lib/data/flux.h"
|
||||
#include "lib/data/disk.h"
|
||||
|
||||
extern void setDecoderManualClockRate(double clockrate_us);
|
||||
|
||||
@@ -52,9 +53,9 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
std::shared_ptr<TrackDataFlux> decodeToSectors(
|
||||
std::shared_ptr<Track> decodeToSectors(
|
||||
std::shared_ptr<const Fluxmap> fluxmap,
|
||||
std::shared_ptr<const TrackInfo>& trackInfo);
|
||||
const std::shared_ptr<const PhysicalTrackLayout>& ptl);
|
||||
|
||||
void pushRecord(
|
||||
const Fluxmap::Position& start, const Fluxmap::Position& end);
|
||||
@@ -104,7 +105,8 @@ protected:
|
||||
virtual void decodeDataRecord() {};
|
||||
|
||||
const DecoderProto& _config;
|
||||
std::shared_ptr<TrackDataFlux> _trackdata;
|
||||
std::shared_ptr<const LogicalTrackLayout> _ltl;
|
||||
std::shared_ptr<Track> _trackdata;
|
||||
std::shared_ptr<Sector> _sector;
|
||||
std::unique_ptr<FluxDecoder> _decoder;
|
||||
std::vector<bool> _recordBits;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "lib/encoders/encoders.pb.h"
|
||||
#include "lib/config/proto.h"
|
||||
#include "lib/data/layout.h"
|
||||
#include "lib/data/locations.h"
|
||||
#include "lib/data/image.h"
|
||||
#include "protocol.h"
|
||||
|
||||
@@ -23,25 +24,24 @@ nanoseconds_t Encoder::calculatePhysicalClockPeriod(
|
||||
}
|
||||
|
||||
std::shared_ptr<const Sector> Encoder::getSector(
|
||||
std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
const Image& image,
|
||||
unsigned sectorId)
|
||||
const CylinderHead& ch, const Image& image, unsigned sectorId)
|
||||
{
|
||||
return image.get(trackInfo->logicalTrack, trackInfo->logicalSide, sectorId);
|
||||
return image.get(ch.cylinder, ch.head, sectorId);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<const Sector>> Encoder::collectSectors(
|
||||
std::shared_ptr<const TrackInfo>& trackLayout, const Image& image)
|
||||
const LogicalTrackLayout& ltl, const Image& image)
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
|
||||
for (unsigned sectorId : trackLayout->diskSectorOrder)
|
||||
for (unsigned sectorId : ltl.diskSectorOrder)
|
||||
{
|
||||
const auto& sector = getSector(trackLayout, image, sectorId);
|
||||
const auto& sector =
|
||||
getSector({ltl.logicalCylinder, ltl.logicalHead}, image, sectorId);
|
||||
if (!sector)
|
||||
error("sector {}.{}.{} is missing from the image",
|
||||
trackLayout->logicalTrack,
|
||||
trackLayout->logicalSide,
|
||||
ltl.logicalCylinder,
|
||||
ltl.logicalHead,
|
||||
sectorId);
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#ifndef ENCODERS_H
|
||||
#define ENCODERS_H
|
||||
|
||||
class Config;
|
||||
class CylinderHead;
|
||||
class EncoderProto;
|
||||
class Fluxmap;
|
||||
class Image;
|
||||
class Layout;
|
||||
class LogicalTrackLayout;
|
||||
class Sector;
|
||||
class TrackInfo;
|
||||
class Config;
|
||||
|
||||
class Encoder
|
||||
{
|
||||
@@ -19,15 +20,12 @@ public:
|
||||
|
||||
public:
|
||||
virtual std::shared_ptr<const Sector> getSector(
|
||||
std::shared_ptr<const TrackInfo>&,
|
||||
const Image& image,
|
||||
unsigned sectorId);
|
||||
const CylinderHead& ch, const Image& image, unsigned sectorId);
|
||||
|
||||
virtual std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
std::shared_ptr<const TrackInfo>&, const Image& image);
|
||||
const LogicalTrackLayout& ltl, const Image& image);
|
||||
|
||||
virtual std::unique_ptr<Fluxmap> encode(
|
||||
std::shared_ptr<const TrackInfo>& trackInfo,
|
||||
virtual std::unique_ptr<Fluxmap> encode(const LogicalTrackLayout& ltl,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) = 0;
|
||||
|
||||
|
||||
8
lib/external/kryoflux.cc
vendored
8
lib/external/kryoflux.cc
vendored
@@ -28,14 +28,6 @@ std::unique_ptr<Fluxmap> readStream(
|
||||
{
|
||||
std::string suffix = fmt::format("{:02}.{}.raw", track, side);
|
||||
|
||||
FILE* fp = fopen(dir.c_str(), "r");
|
||||
if (fp)
|
||||
{
|
||||
fclose(fp);
|
||||
int i = dir.find_last_of("/\\");
|
||||
dir = dir.substr(0, i);
|
||||
}
|
||||
|
||||
DIR* dirp = opendir(dir.c_str());
|
||||
if (!dirp)
|
||||
error("cannot access path '{}'", dir);
|
||||
|
||||
@@ -17,228 +17,246 @@
|
||||
#include <sys/types.h>
|
||||
#include "fmt/chrono.h"
|
||||
|
||||
namespace
|
||||
static uint32_t ticks_to_a2r(uint32_t ticks)
|
||||
{
|
||||
uint32_t ticks_to_a2r(uint32_t ticks)
|
||||
return ticks * NS_PER_TICK / A2R_NS_PER_TICK;
|
||||
}
|
||||
|
||||
class A2RSink : public FluxSink
|
||||
{
|
||||
public:
|
||||
A2RSink(const std::string& filename):
|
||||
_filename(filename),
|
||||
_bytes{},
|
||||
_writer(_bytes.writer())
|
||||
{
|
||||
return ticks * NS_PER_TICK / A2R_NS_PER_TICK;
|
||||
time_t now{std::time(nullptr)};
|
||||
auto t = gmtime(&now);
|
||||
_metadata["image_date"] = fmt::format("{:%FT%TZ}", *t);
|
||||
}
|
||||
|
||||
class A2RFluxSink : public FluxSink
|
||||
~A2RSink()
|
||||
{
|
||||
public:
|
||||
A2RFluxSink(const A2RFluxSinkProto& lconfig):
|
||||
_config(lconfig),
|
||||
_bytes{},
|
||||
_writer{_bytes.writer()}
|
||||
// FIXME: should use a passed-in DiskLayout object.
|
||||
auto diskLayout = createDiskLayout();
|
||||
auto [minCylinder, maxCylinder, minHead, maxHead] =
|
||||
diskLayout->getPhysicalBounds();
|
||||
|
||||
_minCylinder = minCylinder;
|
||||
_maxCylinder = maxCylinder;
|
||||
_minHead = minHead;
|
||||
_maxHead = maxHead;
|
||||
|
||||
log("A2R: writing A2R {} file containing {} tracks...",
|
||||
(_minHead == _maxHead) ? "single sided" : "double sided",
|
||||
_maxCylinder - _minCylinder + 1);
|
||||
|
||||
writeHeader();
|
||||
writeInfo();
|
||||
writeStream();
|
||||
writeMeta();
|
||||
|
||||
std::ofstream of(_filename, std::ios::out | std::ios::binary);
|
||||
if (!of.is_open())
|
||||
error("cannot open output file");
|
||||
_bytes.writeTo(of);
|
||||
of.close();
|
||||
}
|
||||
|
||||
private:
|
||||
void writeChunkAndData(uint32_t chunk_id, const Bytes& data)
|
||||
{
|
||||
_writer.write_le32(chunk_id);
|
||||
_writer.write_le32(data.size());
|
||||
_writer += data;
|
||||
}
|
||||
|
||||
void writeHeader()
|
||||
{
|
||||
static const uint8_t a2r2_fileheader[] = {
|
||||
'A', '2', 'R', '2', 0xff, 0x0a, 0x0d, 0x0a};
|
||||
_writer += Bytes(a2r2_fileheader, sizeof(a2r2_fileheader));
|
||||
}
|
||||
|
||||
void writeInfo()
|
||||
{
|
||||
Bytes info;
|
||||
auto writer = info.writer();
|
||||
writer.write_8(A2R_INFO_CHUNK_VERSION);
|
||||
auto version_str_padded = fmt::format("{: <32}", "FluxEngine");
|
||||
assert(version_str_padded.size() == 32);
|
||||
writer.append(version_str_padded);
|
||||
|
||||
writer.write_8(
|
||||
(globalConfig()->drive().drive_type() == DRIVETYPE_APPLE2)
|
||||
? A2R_DISK_525
|
||||
: A2R_DISK_35);
|
||||
|
||||
writer.write_8(1); // write protected
|
||||
writer.write_8(1); // synchronized
|
||||
writeChunkAndData(A2R_CHUNK_INFO, info);
|
||||
}
|
||||
|
||||
void writeMeta()
|
||||
{
|
||||
Bytes meta;
|
||||
auto writer = meta.writer();
|
||||
for (auto& i : _metadata)
|
||||
{
|
||||
time_t now{std::time(nullptr)};
|
||||
auto t = gmtime(&now);
|
||||
_metadata["image_date"] = fmt::format("{:%FT%TZ}", *t);
|
||||
writer.append(i.first);
|
||||
writer.write_8('\t');
|
||||
writer.append(i.second);
|
||||
writer.write_8('\n');
|
||||
}
|
||||
writeChunkAndData(A2R_CHUNK_META, meta);
|
||||
}
|
||||
|
||||
void writeStream()
|
||||
{
|
||||
// A STRM always ends with a 255, even though this could ALSO
|
||||
// indicate the first byte of a multi-byte sequence
|
||||
_strmWriter.write_8(255);
|
||||
|
||||
writeChunkAndData(A2R_CHUNK_STRM, _strmBytes);
|
||||
}
|
||||
|
||||
public:
|
||||
void addFlux(int cylinder, int head, const Fluxmap& fluxmap) override
|
||||
{
|
||||
if (!fluxmap.bytes())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
~A2RFluxSink()
|
||||
// Writing from an image (as opposed to from a floppy) will
|
||||
// contain exactly one revolution and no index events.
|
||||
auto is_image = [](auto& fluxmap)
|
||||
{
|
||||
auto physicalLocations = Layout::computePhysicalLocations();
|
||||
auto [minTrack, maxTrack, minSide, maxSide] =
|
||||
Layout::getBounds(physicalLocations);
|
||||
_minTrack = minTrack;
|
||||
_maxTrack = maxTrack;
|
||||
_minSide = minSide;
|
||||
_maxSide = maxSide;
|
||||
|
||||
log("A2R: writing A2R {} file containing {} tracks...",
|
||||
(_minSide == _maxSide) ? "single sided" : "double sided",
|
||||
_maxTrack - _minTrack + 1);
|
||||
|
||||
writeHeader();
|
||||
writeInfo();
|
||||
writeStream();
|
||||
writeMeta();
|
||||
|
||||
std::ofstream of(
|
||||
_config.filename(), std::ios::out | std::ios::binary);
|
||||
if (!of.is_open())
|
||||
error("cannot open output file");
|
||||
_bytes.writeTo(of);
|
||||
of.close();
|
||||
}
|
||||
|
||||
private:
|
||||
void writeChunkAndData(uint32_t chunk_id, const Bytes& data)
|
||||
{
|
||||
_writer.write_le32(chunk_id);
|
||||
_writer.write_le32(data.size());
|
||||
_writer += data;
|
||||
}
|
||||
|
||||
void writeHeader()
|
||||
{
|
||||
static const uint8_t a2r2_fileheader[] = {
|
||||
'A', '2', 'R', '2', 0xff, 0x0a, 0x0d, 0x0a};
|
||||
_writer += Bytes(a2r2_fileheader, sizeof(a2r2_fileheader));
|
||||
}
|
||||
|
||||
void writeInfo()
|
||||
{
|
||||
Bytes info;
|
||||
auto writer = info.writer();
|
||||
writer.write_8(A2R_INFO_CHUNK_VERSION);
|
||||
auto version_str_padded = fmt::format("{: <32}", "FluxEngine");
|
||||
assert(version_str_padded.size() == 32);
|
||||
writer.append(version_str_padded);
|
||||
|
||||
writer.write_8(
|
||||
(globalConfig()->drive().drive_type() == DRIVETYPE_APPLE2)
|
||||
? A2R_DISK_525
|
||||
: A2R_DISK_35);
|
||||
|
||||
writer.write_8(1); // write protected
|
||||
writer.write_8(1); // synchronized
|
||||
writeChunkAndData(A2R_CHUNK_INFO, info);
|
||||
}
|
||||
|
||||
void writeMeta()
|
||||
{
|
||||
Bytes meta;
|
||||
auto writer = meta.writer();
|
||||
for (auto& i : _metadata)
|
||||
{
|
||||
writer.append(i.first);
|
||||
writer.write_8('\t');
|
||||
writer.append(i.second);
|
||||
writer.write_8('\n');
|
||||
}
|
||||
writeChunkAndData(A2R_CHUNK_META, meta);
|
||||
}
|
||||
|
||||
void writeStream()
|
||||
{
|
||||
// A STRM always ends with a 255, even though this could ALSO
|
||||
// indicate the first byte of a multi-byte sequence
|
||||
_strmWriter.write_8(255);
|
||||
|
||||
writeChunkAndData(A2R_CHUNK_STRM, _strmBytes);
|
||||
}
|
||||
|
||||
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
|
||||
{
|
||||
if (!fluxmap.bytes())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Writing from an image (as opposed to from a floppy) will contain
|
||||
// exactly one revolution and no index events.
|
||||
auto is_image = [](auto& fluxmap)
|
||||
{
|
||||
FluxmapReader fmr(fluxmap);
|
||||
fmr.skipToEvent(F_BIT_INDEX);
|
||||
// but maybe there is no index, if we're writing from an image
|
||||
// to an a2r
|
||||
return fmr.eof();
|
||||
};
|
||||
|
||||
// Write the flux data into its own Bytes
|
||||
Bytes trackBytes;
|
||||
auto trackWriter = trackBytes.writer();
|
||||
|
||||
auto write_one_flux = [&](unsigned ticks)
|
||||
{
|
||||
auto value = ticks_to_a2r(ticks);
|
||||
while (value > 254)
|
||||
{
|
||||
trackWriter.write_8(255);
|
||||
value -= 255;
|
||||
}
|
||||
trackWriter.write_8(value);
|
||||
};
|
||||
|
||||
int revolution = 0;
|
||||
uint32_t loopPoint = 0;
|
||||
uint32_t totalTicks = 0;
|
||||
FluxmapReader fmr(fluxmap);
|
||||
fmr.skipToEvent(F_BIT_INDEX);
|
||||
// but maybe there is no index, if we're writing from an
|
||||
// image to an a2r
|
||||
return fmr.eof();
|
||||
};
|
||||
|
||||
auto write_flux = [&](unsigned maxTicks = ~0u)
|
||||
{
|
||||
unsigned ticksSinceLastPulse = 0;
|
||||
// Write the flux data into its own Bytes
|
||||
Bytes trackBytes;
|
||||
auto trackWriter = trackBytes.writer();
|
||||
|
||||
while (!fmr.eof() && totalTicks < maxTicks)
|
||||
{
|
||||
unsigned ticks;
|
||||
int event;
|
||||
fmr.getNextEvent(event, ticks);
|
||||
|
||||
ticksSinceLastPulse += ticks;
|
||||
totalTicks += ticks;
|
||||
|
||||
if (event & F_BIT_PULSE)
|
||||
{
|
||||
write_one_flux(ticksSinceLastPulse);
|
||||
ticksSinceLastPulse = 0;
|
||||
}
|
||||
|
||||
if (event & F_BIT_INDEX && revolution == 0)
|
||||
{
|
||||
loopPoint = totalTicks;
|
||||
revolution += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (is_image(fluxmap))
|
||||
{
|
||||
// A timing stream with no index represents exactly one
|
||||
// revolution with no index. However, a2r nominally contains 450
|
||||
// degress of rotation, 250ms at 300rpm.
|
||||
write_flux();
|
||||
loopPoint = totalTicks;
|
||||
fmr.rewind();
|
||||
revolution += 1;
|
||||
write_flux(totalTicks * 5 / 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have an index, so this is a real read from a floppy and
|
||||
// should be "one revolution plus a bit"
|
||||
fmr.skipToEvent(F_BIT_INDEX);
|
||||
write_flux();
|
||||
}
|
||||
|
||||
uint32_t chunk_size = 10 + trackBytes.size();
|
||||
|
||||
if (globalConfig()->drive().drive_type() == DRIVETYPE_APPLE2)
|
||||
_strmWriter.write_8(cylinder);
|
||||
else
|
||||
_strmWriter.write_8((cylinder << 1) | head);
|
||||
|
||||
_strmWriter.write_8(A2R_TIMING);
|
||||
_strmWriter.write_le32(trackBytes.size());
|
||||
_strmWriter.write_le32(ticks_to_a2r(loopPoint));
|
||||
_strmWriter += trackBytes;
|
||||
}
|
||||
|
||||
operator std::string() const override
|
||||
auto write_one_flux = [&](unsigned ticks)
|
||||
{
|
||||
return fmt::format("a2r({})", _config.filename());
|
||||
auto value = ticks_to_a2r(ticks);
|
||||
while (value > 254)
|
||||
{
|
||||
trackWriter.write_8(255);
|
||||
value -= 255;
|
||||
}
|
||||
trackWriter.write_8(value);
|
||||
};
|
||||
|
||||
int revolution = 0;
|
||||
uint32_t loopPoint = 0;
|
||||
uint32_t totalTicks = 0;
|
||||
FluxmapReader fmr(fluxmap);
|
||||
|
||||
auto write_flux = [&](unsigned maxTicks = ~0u)
|
||||
{
|
||||
unsigned ticksSinceLastPulse = 0;
|
||||
|
||||
while (!fmr.eof() && totalTicks < maxTicks)
|
||||
{
|
||||
unsigned ticks;
|
||||
int event;
|
||||
fmr.getNextEvent(event, ticks);
|
||||
|
||||
ticksSinceLastPulse += ticks;
|
||||
totalTicks += ticks;
|
||||
|
||||
if (event & F_BIT_PULSE)
|
||||
{
|
||||
write_one_flux(ticksSinceLastPulse);
|
||||
ticksSinceLastPulse = 0;
|
||||
}
|
||||
|
||||
if (event & F_BIT_INDEX && revolution == 0)
|
||||
{
|
||||
loopPoint = totalTicks;
|
||||
revolution += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (is_image(fluxmap))
|
||||
{
|
||||
// A timing stream with no index represents exactly one
|
||||
// revolution with no index. However, a2r nominally contains
|
||||
// 450 degress of rotation, 250ms at 300rpm.
|
||||
write_flux();
|
||||
loopPoint = totalTicks;
|
||||
fmr.rewind();
|
||||
revolution += 1;
|
||||
write_flux(totalTicks * 5 / 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have an index, so this is a real read from a floppy
|
||||
// and should be "one revolution plus a bit"
|
||||
fmr.skipToEvent(F_BIT_INDEX);
|
||||
write_flux();
|
||||
}
|
||||
|
||||
private:
|
||||
const A2RFluxSinkProto& _config;
|
||||
Bytes _bytes;
|
||||
ByteWriter _writer;
|
||||
Bytes _strmBytes;
|
||||
ByteWriter _strmWriter{_strmBytes.writer()};
|
||||
std::map<std::string, std::string> _metadata;
|
||||
int _minSide;
|
||||
int _maxSide;
|
||||
int _minTrack;
|
||||
int _maxTrack;
|
||||
};
|
||||
} // namespace
|
||||
uint32_t chunk_size = 10 + trackBytes.size();
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::createA2RFluxSink(
|
||||
if (globalConfig()->drive().drive_type() == DRIVETYPE_APPLE2)
|
||||
_strmWriter.write_8(cylinder);
|
||||
else
|
||||
_strmWriter.write_8((cylinder << 1) | head);
|
||||
|
||||
_strmWriter.write_8(A2R_TIMING);
|
||||
_strmWriter.write_le32(trackBytes.size());
|
||||
_strmWriter.write_le32(ticks_to_a2r(loopPoint));
|
||||
_strmWriter += trackBytes;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _filename;
|
||||
Bytes _bytes;
|
||||
ByteWriter _writer;
|
||||
Bytes _strmBytes;
|
||||
ByteWriter _strmWriter{_strmBytes.writer()};
|
||||
std::map<std::string, std::string> _metadata;
|
||||
int _minHead;
|
||||
int _maxHead;
|
||||
int _minCylinder;
|
||||
int _maxCylinder;
|
||||
};
|
||||
|
||||
class A2RFluxSinkFactory : public FluxSinkFactory
|
||||
{
|
||||
public:
|
||||
A2RFluxSinkFactory(const A2RFluxSinkProto& lconfig): _config(lconfig) {}
|
||||
|
||||
std::unique_ptr<FluxSink> create() override
|
||||
{
|
||||
return std::make_unique<A2RSink>(_config.filename());
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> getPath() const override
|
||||
{
|
||||
return std::make_optional(_config.filename());
|
||||
}
|
||||
|
||||
operator std::string() const override
|
||||
{
|
||||
return fmt::format("a2r({})", _config.filename());
|
||||
}
|
||||
|
||||
private:
|
||||
const A2RFluxSinkProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSinkFactory> FluxSinkFactory::createA2RFluxSinkFactory(
|
||||
const A2RFluxSinkProto& config)
|
||||
{
|
||||
return std::unique_ptr<FluxSink>(new A2RFluxSink(config));
|
||||
return std::unique_ptr<FluxSinkFactory>(new A2RFluxSinkFactory(config));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lib/core/globals.h"
|
||||
#include "lib/core/logger.h"
|
||||
#include "lib/config/flags.h"
|
||||
#include "lib/data/fluxmap.h"
|
||||
#include "lib/core/bytes.h"
|
||||
@@ -11,27 +12,29 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
class AuFluxSink : public FluxSink
|
||||
class AuSink : public FluxSink
|
||||
{
|
||||
public:
|
||||
AuFluxSink(const AuFluxSinkProto& config): _config(config) {}
|
||||
|
||||
~AuFluxSink()
|
||||
AuSink(const std::string& directory, bool indexMarkers):
|
||||
_directory(directory),
|
||||
_indexMarkers(indexMarkers)
|
||||
{
|
||||
std::cerr << "Warning: do not play these files, or you will break your "
|
||||
"speakers and/or ears!\n";
|
||||
}
|
||||
|
||||
public:
|
||||
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
|
||||
~AuSink()
|
||||
{
|
||||
log("Warning: do not play these files, or you will break your "
|
||||
"speakers and/or ears!");
|
||||
}
|
||||
|
||||
void addFlux(int track, int head, const Fluxmap& fluxmap) override
|
||||
{
|
||||
unsigned totalTicks = fluxmap.ticks() + 2;
|
||||
unsigned channels = _config.index_markers() ? 2 : 1;
|
||||
unsigned channels = _indexMarkers ? 2 : 1;
|
||||
|
||||
mkdir(_config.directory().c_str(), 0744);
|
||||
mkdir(_directory.c_str(), 0744);
|
||||
std::ofstream of(
|
||||
fmt::format(
|
||||
"{}/c{:02d}.h{:01d}.au", _config.directory(), track, head),
|
||||
fmt::format("{}/c{:02d}.h{:01d}.au", _directory, track, head),
|
||||
std::ios::out | std::ios::binary);
|
||||
if (!of.is_open())
|
||||
error("cannot open output file");
|
||||
@@ -73,7 +76,7 @@ public:
|
||||
|
||||
if (event & F_BIT_PULSE)
|
||||
data[timestamp * channels + 0] = 0x7f;
|
||||
if (_config.index_markers() && (event & F_BIT_INDEX))
|
||||
if (_indexMarkers && (event & F_BIT_INDEX))
|
||||
data[timestamp * channels + 1] = 0x7f;
|
||||
}
|
||||
|
||||
@@ -81,6 +84,27 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _directory;
|
||||
bool _indexMarkers;
|
||||
};
|
||||
|
||||
class AuFluxSinkFactory : public FluxSinkFactory
|
||||
{
|
||||
public:
|
||||
AuFluxSinkFactory(const AuFluxSinkProto& config): _config(config) {}
|
||||
|
||||
std::unique_ptr<FluxSink> create() override
|
||||
{
|
||||
return std::make_unique<AuSink>(
|
||||
_config.directory(), _config.index_markers());
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> getPath() const override
|
||||
{
|
||||
return std::make_optional(_config.directory());
|
||||
}
|
||||
|
||||
operator std::string() const override
|
||||
{
|
||||
return fmt::format("au({})", _config.directory());
|
||||
@@ -90,8 +114,8 @@ private:
|
||||
const AuFluxSinkProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::createAuFluxSink(
|
||||
std::unique_ptr<FluxSinkFactory> FluxSinkFactory::createAuFluxSinkFactory(
|
||||
const AuFluxSinkProto& config)
|
||||
{
|
||||
return std::unique_ptr<FluxSink>(new AuFluxSink(config));
|
||||
return std::unique_ptr<FluxSinkFactory>(new AuFluxSinkFactory(config));
|
||||
}
|
||||
|
||||
@@ -16,15 +16,10 @@
|
||||
#include <sys/types.h>
|
||||
#include <filesystem>
|
||||
|
||||
class Fl2FluxSink : public FluxSink
|
||||
class Fl2Sink : public FluxSink
|
||||
{
|
||||
public:
|
||||
Fl2FluxSink(const Fl2FluxSinkProto& lconfig):
|
||||
Fl2FluxSink(lconfig.filename())
|
||||
{
|
||||
}
|
||||
|
||||
Fl2FluxSink(const std::string& filename): _filename(filename)
|
||||
Fl2Sink(const std::string& filename): _filename(filename)
|
||||
{
|
||||
std::ofstream of(filename);
|
||||
if (!of.is_open())
|
||||
@@ -33,7 +28,7 @@ public:
|
||||
std::filesystem::remove(filename);
|
||||
}
|
||||
|
||||
~Fl2FluxSink()
|
||||
~Fl2Sink()
|
||||
{
|
||||
log("FL2: writing {}", _filename);
|
||||
|
||||
@@ -54,31 +49,51 @@ public:
|
||||
saveFl2File(_filename, proto);
|
||||
}
|
||||
|
||||
public:
|
||||
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
|
||||
void addFlux(int track, int head, const Fluxmap& fluxmap) override
|
||||
{
|
||||
auto& vector = _data[std::make_pair(track, head)];
|
||||
vector.push_back(fluxmap.rawBytes());
|
||||
}
|
||||
|
||||
operator std::string() const override
|
||||
{
|
||||
return fmt::format("fl2({})", _filename);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _filename;
|
||||
std::map<std::pair<unsigned, unsigned>, std::vector<Bytes>> _data;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(
|
||||
class Fl2FluxSinkFactory : public FluxSinkFactory
|
||||
{
|
||||
public:
|
||||
Fl2FluxSinkFactory(const std::string& filename): _filename(filename) {}
|
||||
|
||||
std::unique_ptr<FluxSink> create() override
|
||||
{
|
||||
return std::make_unique<Fl2Sink>(_filename);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> getPath() const override
|
||||
{
|
||||
return std::make_optional(_filename);
|
||||
}
|
||||
|
||||
public:
|
||||
operator std::string() const override
|
||||
{
|
||||
return fmt::format("fl2({})", _filename);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string _filename;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSinkFactory> FluxSinkFactory::createFl2FluxSinkFactory(
|
||||
const Fl2FluxSinkProto& config)
|
||||
{
|
||||
return std::unique_ptr<FluxSink>(new Fl2FluxSink(config));
|
||||
return std::unique_ptr<FluxSinkFactory>(
|
||||
new Fl2FluxSinkFactory(config.filename()));
|
||||
}
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(
|
||||
std::unique_ptr<FluxSinkFactory> FluxSinkFactory::createFl2FluxSinkFactory(
|
||||
const std::string& filename)
|
||||
{
|
||||
return std::unique_ptr<FluxSink>(new Fl2FluxSink(filename));
|
||||
return std::unique_ptr<FluxSinkFactory>(new Fl2FluxSinkFactory(filename));
|
||||
}
|
||||
|
||||
@@ -7,36 +7,37 @@
|
||||
#include "lib/core/utils.h"
|
||||
#include <regex>
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::create(Config& config)
|
||||
std::unique_ptr<FluxSinkFactory> FluxSinkFactory::create(Config& config)
|
||||
{
|
||||
if (!config.hasFluxSink())
|
||||
error("no flux sink configured");
|
||||
return create(config->flux_sink());
|
||||
}
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::create(const FluxSinkProto& config)
|
||||
std::unique_ptr<FluxSinkFactory> FluxSinkFactory::create(
|
||||
const FluxSinkProto& config)
|
||||
{
|
||||
switch (config.type())
|
||||
{
|
||||
case FLUXTYPE_DRIVE:
|
||||
return createHardwareFluxSink(config.drive());
|
||||
return createHardwareFluxSinkFactory(config.drive());
|
||||
|
||||
case FLUXTYPE_A2R:
|
||||
return createA2RFluxSink(config.a2r());
|
||||
return createA2RFluxSinkFactory(config.a2r());
|
||||
|
||||
case FLUXTYPE_AU:
|
||||
return createAuFluxSink(config.au());
|
||||
return createAuFluxSinkFactory(config.au());
|
||||
|
||||
case FLUXTYPE_VCD:
|
||||
return createVcdFluxSink(config.vcd());
|
||||
return createVcdFluxSinkFactory(config.vcd());
|
||||
|
||||
case FLUXTYPE_SCP:
|
||||
return createScpFluxSink(config.scp());
|
||||
return createScpFluxSinkFactory(config.scp());
|
||||
|
||||
case FLUXTYPE_FLUX:
|
||||
return createFl2FluxSink(config.fl2());
|
||||
return createFl2FluxSinkFactory(config.fl2());
|
||||
|
||||
default:
|
||||
return std::unique_ptr<FluxSink>();
|
||||
return std::unique_ptr<FluxSinkFactory>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lib/config/flags.h"
|
||||
#include "lib/data/locations.h"
|
||||
#include <ostream>
|
||||
#include <filesystem>
|
||||
|
||||
class Fluxmap;
|
||||
class FluxSinkProto;
|
||||
@@ -14,39 +15,52 @@ class VcdFluxSinkProto;
|
||||
class ScpFluxSinkProto;
|
||||
class Fl2FluxSinkProto;
|
||||
class Config;
|
||||
class Disk;
|
||||
|
||||
class FluxSink
|
||||
{
|
||||
public:
|
||||
FluxSink() {}
|
||||
virtual ~FluxSink() {}
|
||||
|
||||
static std::unique_ptr<FluxSink> createHardwareFluxSink(
|
||||
const HardwareFluxSinkProto& config);
|
||||
static std::unique_ptr<FluxSink> createAuFluxSink(
|
||||
const AuFluxSinkProto& config);
|
||||
static std::unique_ptr<FluxSink> createA2RFluxSink(
|
||||
const A2RFluxSinkProto& config);
|
||||
static std::unique_ptr<FluxSink> createVcdFluxSink(
|
||||
const VcdFluxSinkProto& config);
|
||||
static std::unique_ptr<FluxSink> createScpFluxSink(
|
||||
const ScpFluxSinkProto& config);
|
||||
static std::unique_ptr<FluxSink> createFl2FluxSink(
|
||||
const Fl2FluxSinkProto& config);
|
||||
|
||||
static std::unique_ptr<FluxSink> createFl2FluxSink(
|
||||
const std::string& filename);
|
||||
|
||||
static std::unique_ptr<FluxSink> create(Config& config);
|
||||
static std::unique_ptr<FluxSink> create(const FluxSinkProto& config);
|
||||
|
||||
public:
|
||||
/* Writes a fluxmap to a track and side. */
|
||||
|
||||
virtual void writeFlux(int track, int side, const Fluxmap& fluxmap) = 0;
|
||||
void writeFlux(const CylinderHead& location, const Fluxmap& fluxmap)
|
||||
virtual void addFlux(int track, int side, const Fluxmap& fluxmap) = 0;
|
||||
void addFlux(const CylinderHead& location, const Fluxmap& fluxmap)
|
||||
{
|
||||
writeFlux(location.cylinder, location.head, fluxmap);
|
||||
addFlux(location.cylinder, location.head, fluxmap);
|
||||
}
|
||||
};
|
||||
|
||||
class FluxSinkFactory
|
||||
{
|
||||
public:
|
||||
virtual ~FluxSinkFactory() {}
|
||||
|
||||
static std::unique_ptr<FluxSinkFactory> createHardwareFluxSinkFactory(
|
||||
const HardwareFluxSinkProto& config);
|
||||
static std::unique_ptr<FluxSinkFactory> createAuFluxSinkFactory(
|
||||
const AuFluxSinkProto& config);
|
||||
static std::unique_ptr<FluxSinkFactory> createA2RFluxSinkFactory(
|
||||
const A2RFluxSinkProto& config);
|
||||
static std::unique_ptr<FluxSinkFactory> createVcdFluxSinkFactory(
|
||||
const VcdFluxSinkProto& config);
|
||||
static std::unique_ptr<FluxSinkFactory> createScpFluxSinkFactory(
|
||||
const ScpFluxSinkProto& config);
|
||||
static std::unique_ptr<FluxSinkFactory> createFl2FluxSinkFactory(
|
||||
const Fl2FluxSinkProto& config);
|
||||
|
||||
static std::unique_ptr<FluxSinkFactory> createFl2FluxSinkFactory(
|
||||
const std::string& filename);
|
||||
|
||||
static std::unique_ptr<FluxSinkFactory> create(Config& config);
|
||||
static std::unique_ptr<FluxSinkFactory> create(const FluxSinkProto& config);
|
||||
|
||||
public:
|
||||
/* Creates a writer object. */
|
||||
|
||||
virtual std::unique_ptr<FluxSink> create() = 0;
|
||||
|
||||
/* Returns whether this is writing to real hardware or not. */
|
||||
|
||||
@@ -55,10 +69,19 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns the path (filename or directory) being written to, if there is
|
||||
* one. */
|
||||
|
||||
virtual std::optional<std::filesystem::path> getPath() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual operator std::string() const = 0;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& stream, FluxSink& flushSink)
|
||||
inline std::ostream& operator<<(
|
||||
std::ostream& stream, FluxSinkFactory& flushSink)
|
||||
{
|
||||
stream << (std::string)flushSink;
|
||||
return stream;
|
||||
|
||||
@@ -8,15 +8,9 @@
|
||||
#include "lib/fluxsink/fluxsink.h"
|
||||
#include "lib/fluxsink/fluxsink.pb.h"
|
||||
|
||||
class HardwareFluxSink : public FluxSink
|
||||
class HardwareSink : public FluxSink
|
||||
{
|
||||
public:
|
||||
HardwareFluxSink(const HardwareFluxSinkProto& conf): _config(conf) {}
|
||||
|
||||
~HardwareFluxSink() {}
|
||||
|
||||
public:
|
||||
void writeFlux(int track, int side, const Fluxmap& fluxmap) override
|
||||
void addFlux(int track, int side, const Fluxmap& fluxmap) override
|
||||
{
|
||||
auto& drive = globalConfig()->drive();
|
||||
usbSetDrive(drive.drive(), drive.high_density(), drive.index_mode());
|
||||
@@ -25,6 +19,15 @@ public:
|
||||
return usbWrite(
|
||||
side, fluxmap.rawBytes(), drive.hard_sector_threshold_ns());
|
||||
}
|
||||
};
|
||||
|
||||
class HardwareFluxSinkFactory : public FluxSinkFactory
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<FluxSink> create() override
|
||||
{
|
||||
return std::make_unique<HardwareSink>();
|
||||
}
|
||||
|
||||
bool isHardware() const override
|
||||
{
|
||||
@@ -33,15 +36,12 @@ public:
|
||||
|
||||
operator std::string() const override
|
||||
{
|
||||
return fmt::format("drive {}", globalConfig()->drive().drive());
|
||||
return "hardware {}";
|
||||
}
|
||||
|
||||
private:
|
||||
const HardwareFluxSinkProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::createHardwareFluxSink(
|
||||
std::unique_ptr<FluxSinkFactory> FluxSinkFactory::createHardwareFluxSinkFactory(
|
||||
const HardwareFluxSinkProto& config)
|
||||
{
|
||||
return std::unique_ptr<FluxSink>(new HardwareFluxSink(config));
|
||||
return std::make_unique<HardwareFluxSinkFactory>();
|
||||
}
|
||||
|
||||
@@ -36,41 +36,46 @@ static void appendChecksum(uint32_t& checksum, const Bytes& bytes)
|
||||
checksum += br.read_8();
|
||||
}
|
||||
|
||||
class ScpFluxSink : public FluxSink
|
||||
class ScpSink : public FluxSink
|
||||
{
|
||||
public:
|
||||
ScpFluxSink(const ScpFluxSinkProto& lconfig): _config(lconfig)
|
||||
ScpSink(const std::string& filename, uint8_t typeByte, bool alignWithIndex):
|
||||
_filename(filename),
|
||||
_typeByte(typeByte),
|
||||
_alignWithIndex(alignWithIndex)
|
||||
{
|
||||
auto [minTrack, maxTrack, minSide, maxSide] =
|
||||
Layout::getBounds(Layout::computePhysicalLocations());
|
||||
// FIXME: should use a passed-in DiskLayout object.
|
||||
auto diskLayout = createDiskLayout();
|
||||
auto [minCylinder, maxCylinder, minHead, maxHead] =
|
||||
diskLayout->getPhysicalBounds();
|
||||
|
||||
_fileheader.file_id[0] = 'S';
|
||||
_fileheader.file_id[1] = 'C';
|
||||
_fileheader.file_id[2] = 'P';
|
||||
_fileheader.version = 0x18; /* Version 1.8 of the spec */
|
||||
_fileheader.type = _config.type_byte();
|
||||
_fileheader.start_track = strackno(minTrack, minSide);
|
||||
_fileheader.end_track = strackno(maxTrack, maxSide);
|
||||
_fileheader.type = _typeByte;
|
||||
_fileheader.start_track = strackno(minCylinder, minHead);
|
||||
_fileheader.end_track = strackno(maxCylinder, maxHead);
|
||||
_fileheader.flags = SCP_FLAG_INDEXED;
|
||||
if (globalConfig()->drive().drive_type() == DRIVETYPE_APPLE2)
|
||||
error("you can't write Apple II flux images to SCP files yet");
|
||||
if (globalConfig()->drive().drive_type() != DRIVETYPE_40TRACK)
|
||||
_fileheader.flags |= SCP_FLAG_96TPI;
|
||||
_fileheader.cell_width = 0;
|
||||
if ((minSide == 0) && (maxSide == 0))
|
||||
if ((minHead == 0) && (maxHead == 0))
|
||||
_fileheader.heads = 1;
|
||||
else if ((minSide == 1) && (maxSide == 1))
|
||||
else if ((minHead == 1) && (maxHead == 1))
|
||||
_fileheader.heads = 2;
|
||||
else
|
||||
_fileheader.heads = 0;
|
||||
|
||||
log("SCP: writing {} tpi {} file containing {} tracks",
|
||||
(_fileheader.flags & SCP_FLAG_96TPI) ? 96 : 48,
|
||||
(minSide == maxSide) ? "single sided" : "double sided",
|
||||
(minHead == maxHead) ? "single sided" : "double sided",
|
||||
_fileheader.end_track - _fileheader.start_track + 1);
|
||||
}
|
||||
|
||||
~ScpFluxSink()
|
||||
~ScpSink()
|
||||
{
|
||||
uint32_t checksum = 0;
|
||||
appendChecksum(checksum,
|
||||
@@ -80,7 +85,7 @@ public:
|
||||
write_le32(_fileheader.checksum, checksum);
|
||||
|
||||
log("SCP: writing output file");
|
||||
std::ofstream of(_config.filename(), std::ios::out | std::ios::binary);
|
||||
std::ofstream of(_filename, std::ios::out | std::ios::binary);
|
||||
if (!of.is_open())
|
||||
error("cannot open output file");
|
||||
of.write((const char*)&_fileheader, sizeof(_fileheader));
|
||||
@@ -88,8 +93,7 @@ public:
|
||||
of.close();
|
||||
}
|
||||
|
||||
public:
|
||||
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
|
||||
void addFlux(int track, int head, const Fluxmap& fluxmap) override
|
||||
{
|
||||
ByteWriter trackdataWriter(_trackdata);
|
||||
trackdataWriter.seekToEnd();
|
||||
@@ -97,7 +101,8 @@ public:
|
||||
|
||||
if (strack >= std::size(_fileheader.track))
|
||||
{
|
||||
log("SCP: cannot write track {} head {}, there are not not enough "
|
||||
log("SCP: cannot write track {} head {}, there are not not "
|
||||
"enough "
|
||||
"Track Data Headers.",
|
||||
track,
|
||||
head);
|
||||
@@ -115,7 +120,7 @@ public:
|
||||
|
||||
int revolution =
|
||||
-1; // -1 indicates that we are before the first index pulse
|
||||
if (_config.align_with_index())
|
||||
if (_alignWithIndex)
|
||||
{
|
||||
fmr.skipToEvent(F_BIT_INDEX);
|
||||
revolution = 0;
|
||||
@@ -134,9 +139,9 @@ public:
|
||||
totalTicks += ticks;
|
||||
revTicks += ticks;
|
||||
|
||||
// if we haven't output any revolutions yet by the end of the track,
|
||||
// assume that the whole track is one rev
|
||||
// also discard any duplicate index pulses
|
||||
// if we haven't output any revolutions yet by the end of the
|
||||
// track, assume that the whole track is one rev also discard
|
||||
// any duplicate index pulses
|
||||
if (((fmr.eof() && revolution <= 0) ||
|
||||
((event & F_BIT_INDEX)) && revTicks > 0))
|
||||
{
|
||||
@@ -179,6 +184,32 @@ public:
|
||||
trackdataWriter += fluxdata;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _filename;
|
||||
uint8_t _typeByte;
|
||||
bool _alignWithIndex;
|
||||
ScpHeader _fileheader = {0};
|
||||
Bytes _trackdata;
|
||||
};
|
||||
|
||||
class ScpFluxSinkFactory : public FluxSinkFactory
|
||||
{
|
||||
public:
|
||||
ScpFluxSinkFactory(const ScpFluxSinkProto& lconfig): _config(lconfig) {}
|
||||
|
||||
std::unique_ptr<FluxSink> create() override
|
||||
{
|
||||
return std::make_unique<ScpSink>(_config.filename(),
|
||||
_config.type_byte(),
|
||||
_config.align_with_index());
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> getPath() const override
|
||||
{
|
||||
return std::make_optional(_config.filename());
|
||||
}
|
||||
|
||||
public:
|
||||
operator std::string() const override
|
||||
{
|
||||
return fmt::format("scp({})", _config.filename());
|
||||
@@ -186,12 +217,10 @@ public:
|
||||
|
||||
private:
|
||||
const ScpFluxSinkProto& _config;
|
||||
ScpHeader _fileheader = {0};
|
||||
Bytes _trackdata;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::createScpFluxSink(
|
||||
std::unique_ptr<FluxSinkFactory> FluxSinkFactory::createScpFluxSinkFactory(
|
||||
const ScpFluxSinkProto& config)
|
||||
{
|
||||
return std::unique_ptr<FluxSink>(new ScpFluxSink(config));
|
||||
return std::unique_ptr<FluxSinkFactory>(new ScpFluxSinkFactory(config));
|
||||
}
|
||||
|
||||
@@ -11,18 +11,16 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
class VcdFluxSink : public FluxSink
|
||||
class VcdSink : public FluxSink
|
||||
{
|
||||
public:
|
||||
VcdFluxSink(const VcdFluxSinkProto& config): _config(config) {}
|
||||
VcdSink(const std::string& directory): _directory(directory) {}
|
||||
|
||||
public:
|
||||
void writeFlux(int track, int head, const Fluxmap& fluxmap) override
|
||||
void addFlux(int track, int head, const Fluxmap& fluxmap) override
|
||||
{
|
||||
mkdir(_config.directory().c_str(), 0744);
|
||||
mkdir(_directory.c_str(), 0744);
|
||||
std::ofstream of(
|
||||
fmt::format(
|
||||
"{}/c{:02d}.h{:01d}.vcd", _config.directory(), track, head),
|
||||
fmt::format("{}/c{:02d}.h{:01d}.vcd", _directory, track, head),
|
||||
std::ios::out | std::ios::binary);
|
||||
if (!of.is_open())
|
||||
error("cannot open output file");
|
||||
@@ -64,6 +62,26 @@ public:
|
||||
of << "\n";
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string _directory;
|
||||
};
|
||||
|
||||
class VcdFluxSinkFactory : public FluxSinkFactory
|
||||
{
|
||||
public:
|
||||
VcdFluxSinkFactory(const VcdFluxSinkProto& config): _config(config) {}
|
||||
|
||||
std::unique_ptr<FluxSink> create() override
|
||||
{
|
||||
return std::make_unique<VcdSink>(_config.directory());
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> getPath() const override
|
||||
{
|
||||
return std::make_optional(_config.directory());
|
||||
}
|
||||
|
||||
public:
|
||||
operator std::string() const override
|
||||
{
|
||||
return fmt::format("vcd({})", _config.directory());
|
||||
@@ -73,8 +91,8 @@ private:
|
||||
const VcdFluxSinkProto& _config;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::createVcdFluxSink(
|
||||
std::unique_ptr<FluxSinkFactory> FluxSinkFactory::createVcdFluxSinkFactory(
|
||||
const VcdFluxSinkProto& config)
|
||||
{
|
||||
return std::unique_ptr<FluxSink>(new VcdFluxSink(config));
|
||||
return std::unique_ptr<FluxSinkFactory>(new VcdFluxSinkFactory(config));
|
||||
}
|
||||
|
||||
@@ -119,8 +119,14 @@ public:
|
||||
|
||||
auto keys = std::views::keys(_v2data);
|
||||
std::vector<CylinderHead> chs{keys.begin(), keys.end()};
|
||||
auto [minCylinder, maxCylinder, minHead, maxHead] =
|
||||
Layout::getBounds(chs);
|
||||
unsigned minCylinder = std::ranges::min(
|
||||
chs | std::views::transform(&CylinderHead::cylinder));
|
||||
unsigned maxCylinder = std::ranges::min(
|
||||
chs | std::views::transform(&CylinderHead::cylinder));
|
||||
unsigned minHead = std::ranges::min(
|
||||
chs | std::views::transform(&CylinderHead::head));
|
||||
unsigned maxHead = std::ranges::min(
|
||||
chs | std::views::transform(&CylinderHead::head));
|
||||
log("A2R: reading A2R {} file with {} cylinders and {} head{}",
|
||||
(disktype == 1) ? "Apple II"
|
||||
: (disktype == 2) ? "normal"
|
||||
|
||||
@@ -11,9 +11,7 @@ std::unique_ptr<FluxSource> FluxSource::create(Config& config)
|
||||
{
|
||||
if (!config.hasFluxSource())
|
||||
error("no flux source configured");
|
||||
auto fluxSource = create(config->flux_source());
|
||||
globalConfig().base()->MergeFrom(fluxSource->getExtraConfig());
|
||||
return fluxSource;
|
||||
return create(config->flux_source());
|
||||
}
|
||||
|
||||
std::unique_ptr<FluxSource> FluxSource::create(const FluxSourceProto& config)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
class A2rFluxSourceProto;
|
||||
class CwfFluxSourceProto;
|
||||
class DiskFlux;
|
||||
class Disk;
|
||||
class EraseFluxSourceProto;
|
||||
class Fl2FluxSourceProto;
|
||||
class FluxSourceProto;
|
||||
@@ -58,7 +58,7 @@ private:
|
||||
|
||||
public:
|
||||
static std::unique_ptr<FluxSource> createMemoryFluxSource(
|
||||
const DiskFlux& flux);
|
||||
const Disk& flux);
|
||||
|
||||
static std::unique_ptr<FluxSource> create(Config& config);
|
||||
static std::unique_ptr<FluxSource> create(const FluxSourceProto& spec);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user