Compare commits

...

58 commits

Author SHA1 Message Date
Keir Fraser
0351c481c0 FF.CFG: Get rid of single quotes around example config values
Single quotes are not accepted by the parser, so the extra punctuation
is unnecessary and confusing verbiage.

Refs #865
2024-03-19 10:31:56 +00:00
Keir Fraser
ca7144b454 Add an example IMG.CFG for the ZX Spectrum FDC-1 / Sandy FDD2 interfaces 2024-02-29 14:47:39 +00:00
Keir Fraser
e829d827da github: Update workflow actions to latest versions 2024-02-05 15:08:48 +00:00
Keir Fraser
c6310b803b Update to v3.42 2024-01-11 13:53:15 +00:00
Keir Fraser
3946e7f7a6 adf: More accurate value for write_bc_ticks 2024-01-11 13:26:58 +00:00
Keir Fraser
18ecd15be9 qd: Use sampleclk_ns() rather than hardcoding a sample count value 2024-01-11 13:26:12 +00:00
Keir Fraser
ae4245018d floppy: Gently de-jitter/precomp WDATA line
This can be enough to handle heavily-precompensated marginal write
signals.

Fixes #861
2024-01-11 13:16:30 +00:00
Keir Fraser
cbbac4e3c2 Add an example IMG.CFG configuration for GRiD Compass
Refs #862
2024-01-11 09:38:08 +00:00
Eric Anderson
69e2c545a3 hfe: Process HFEv3 opcodes when writing 2023-10-29 08:10:35 +00:00
Eric Anderson
959fd9a4a0 img, dsk: Stream writes to flash
This starts writes for larger sector sizes earlier, allowing large
sectors to be supported independent of the write_bc size.
It also allows some write latencies to be hidden by the time it takes to
receive the data.

This also makes better use of write_bc for buffering. Waiting for a full
sector means that space in write_bc is _not_ used for buffering
processing delays of the sector. Draining the sector data earlier frees
space earlier from the buffer.

[keirf: Minor tweaks and refactoring.]
2023-09-17 22:16:53 +01:00
Keir Fraser
da6188ae7e hfe: Clarify HFEv3 tracklen "caching" in hfe_seek_track(). 2023-09-17 22:04:16 +01:00
Keir Fraser
9ac66c313e hfe: On track setup, start_bc = start_ticks * max_bc / max_ticks
In essence, we take the rotational position from index as a fraction
(start_ticks/max_ticks) and start the bitstream at the same position.
This is especially important for HFEv3 where the track data may be
elongated by opcodes.

Original commit message by ejona86:

hfe: Compensate for opcodes during seeking

This reduces the worst-case observed skew from 3 ms to 50 us. There is a
risk that in the future an opcode is added that is numerous and not
evenly distributed, but that seems unlikely and we'd need to swap to a
more complicated/advanced scheme.
2023-09-17 22:03:09 +01:00
Keir Fraser
25742f6ecf util: New function udiv64(): 64/32 -> 32q 2023-09-17 08:31:59 +01:00
Eric Anderson
0c423fb9de dsk: Use constant CHUNK_SIZE
[keirf: Renamed BATCH_SIZE to CHUNK_SIZE.]
2023-09-16 21:35:43 +01:00
Eric Anderson
548c791cc2 img: Use constant CHUNK_SIZE
[keirf: renamed BATCH_SIZE to CHUNK_SIZE.]
2023-09-16 21:34:50 +01:00
Keir Fraser
11a2ea9e2c debug: Cut USBH trace verbosity to fix the build (firmware binary too large)
[keirf: Leave the trace messages intact in debug builds for now.]
2023-09-16 21:30:23 +01:00
Keir Fraser
e2f010c648 New logging macro log() prepends a LOG_PREFIX to log output 2023-09-16 21:27:30 +01:00
Eric Anderson
b34cb95543 Share debug strings between IMG and DSK 2023-09-16 21:15:38 +01:00
Keir Fraser
cc67ce5903 Merge very short fluxes when processing WDATA
Fluxes shorter than half a bitcell are definitely invalid and it
is reasonable to assume they are bogus. Instead of pushing a 1
bit for very short flux, instead merge the flux into the next.

Refs #827
2023-09-16 21:09:51 +01:00
Eric Anderson
dea7a6ae4d HFEv3: Reuse tracklen_ticks and stk_per_rev from previous track
This is a better guess than trying to account for, or totally
ignoring, opcode overhead every time we seek to a new track.

Note that this commit also initialises tracklen_ticks earlier, even
for regular HFE image files.

[keirf: commits squashed and commit messages amended]
2023-09-16 18:12:23 +01:00
Eric Anderson
78f93ea431 Avoid fake_fired for realtime seek/write
fake_fired inserts an index pulse to avoid host-side timeouts. However,
the realtime options don't carry any risk of host-side timeouts because
the index pulses occur at the normal rate.

index-suppression=no causes behavior like write-drain=eot, since when
fake_fired is set during writing it also changes restart_pos. This
commit disables that behavior for write-drain=realtime, but not for
write-drain=instant which still carries timeout risk. Some users may be
depending on the implicit behavior.
2023-09-10 18:06:16 +01:00
Eric Anderson
9308d30b2e Upgrade gh-actions to Ubuntu 22.04
The newer GCC does a better job of reducing code size.
2023-07-17 18:18:49 +01:00
Keir Fraser
5e3e3a227f HFE, QD: Clarify respective rdata_flux() functions 2023-07-17 18:13:37 +01:00
Keir Fraser
fb3c7c03d8 Update to v3.41 2023-07-14 13:58:47 +01:00
Keir Fraser
5067e622ad time: Simplify calculation of 32-bit timebase update period
There is no need to convert to and from milliseconds.
2023-07-14 08:04:52 +01:00
Keir Fraser
7fd32fe437 LCD: Don't bother waiting for RAM to clear before enabling backlight
It's pointless since the display is so slow to update anyway. It
just delays full initialisation of the firmware.
2023-07-14 07:53:12 +01:00
Keir Fraser
fb58d39502 AT32F415: Fix timer logic for AT32F415 running at STK_MHZ=18
This MCU runs timers on a 16-bit hardware timer. Since the next
timer deadline may be longer than 65535 ticks, we set a coarser
deadline to fire before the actual event: When this triggers,
we expect to set a fine deadline to run the actual timer handler.

The problem is that this coarse deadline is set 5ms early, and this
*also* overflows 65535 ticks at 18MHz. An assumption in the timer
arithmetic gets broken and we end up setting a coarse-grained timer
deadline way in the future. This breaks the 32-bit timebase (which
itself relies on somewhat timely timer to extrapolate from the 24-bit
systick hardware timer). And all random hell breaks loose.

The solution is simple: Set the coarse deadline only 1ms early.

Refs #808
2023-07-13 18:30:38 +01:00
Keir Fraser
8a150d8750 Typo in release notes 2023-06-27 12:54:58 +01:00
Keir Fraser
47dbbdc1ec Update to v3.40 2023-06-27 12:48:15 +01:00
Keir Fraser
2935de98de xdf: Fix writes to side 1 (with bitcell-granularity track skew).
Fixes #802
2023-06-25 08:37:09 +01:00
Keir Fraser
fb49e98256 HFE, QD: Subdivide NFA regions at a smaller granularity
1. This makes the DMA ring less lumpy and potentially quicker to fill
   since the worst-case time values are reduced.
2. The tail value at the end of a sub-divided chain is guaranteed
   to be greater than 2^14 sampleclk ticks. This should make it
   easier to interrupt during the tail period to re-enable RDATA
   output.
3. By detecting overflow at 2^15 ticks we prevent a hard overflow
   at 2^16 ticks earlier in the {hfe,qd}_rdata_flux processing loop.

The motivation for this NFA work is the game Repton Thru Time for
the BBC Micro. An HFE dump of the original game disk has a long NFA
(>100ms) on Track 1.
2023-06-20 16:29:51 +01:00
Keir Fraser
9521a79ad9 HFE, QD: Subdivide long NFA regions so that the 16-bit timer does not overflow 2023-05-31 17:32:24 +01:00
Keir Fraser
82aaae8bf5 led_7seg: Add a code comment for the mapping of bit positions to segments. 2023-04-19 12:55:33 +01:00
Keir Fraser
484af134e6 QD: Add documentation for interfacing to ZX Spectrum Clive Drive 2023-04-03 08:35:55 +01:00
Keir Fraser
faf9137bdb QD: Re-indent mk_qd script and add new parameter to round up byte values to block size. 2023-03-31 14:50:19 +01:00
Keir Fraser
38aa3c9000 Fix typo in release notes. 2023-03-28 08:19:41 +01:00
Keir Fraser
007de2c8d6 LCD: Completely blank display when backlight is off.
Refs #774
2023-03-24 10:15:05 +00:00
Keir Fraser
410a7203cc QD: Reduce motor spinup to 1 second (previously 2 seconds) 2023-03-22 16:48:32 +00:00
Keir Fraser
3e219840d9 IMG.CFG: Update interleave for all disks for Roland MC-x line.
Confirmed by Chris Poacher.
2023-03-14 21:26:50 +00:00
Keir Fraser
38cf13cf23 IMG.CFG: Roland MC-500 2023-03-12 17:55:05 +00:00
Keir Fraser
c885f2cb49 release.yml: Fix RELEASE_NOTES link 2023-03-01 12:39:12 +00:00
Keir Fraser
e20b845c31 Update to v3.39 2023-03-01 12:32:48 +00:00
Keir Fraser
47c657dc34 HFEv3: Fix and simplify opcode handling
This reworking also naturally allows OP_Rand to follow OP_SkipBits,
as required for non-byte-sized weak regions.
2023-02-09 20:24:16 +00:00
Keir Fraser
e8c0942594 Makefile: Fix the flash-to-device helper rules 2023-02-08 11:01:50 +00:00
Keir Fraser
d572d26552 README.md: Banner picture 2023-02-05 16:42:37 +00:00
Keir Fraser
5be461fe26 IMG.CFG: New per-track parameter "img_bps"
Specifies bytes occupied per sector in the IMG image file.

Allows padding of short sectors for uniformly-indexed fiels.

Refs #751
2023-02-01 08:58:30 +00:00
Keir Fraser
13d743d9b5 mk_qd.py: Allow QD image lead-in to be configured on command line 2023-01-30 18:01:25 +00:00
Keir Fraser
a9b8f8d45b IMG.CFG: Fix scoping of per-track sector sizes.
Refs #751
2023-01-30 17:59:52 +00:00
Keir Fraser
d44a5e72ee FF.CFG: New option notify-volume= for notifying on insert/eject via speaker.
Refs #716
2023-01-17 16:01:26 +00:00
Keir Fraser
8b203cd0ee i2c: Remove unnecessary static initialiser for i2c peripheral.
Avoids build error on older versions of GCC:
"initializer element is not constant"
2023-01-16 14:43:08 +00:00
Keir Fraser
9c73940f89 Main Menu: New option "MCU Info" 2023-01-15 13:58:14 +00:00
Keir Fraser
75877cb8eb AT32F415: Run at 144MHz 2023-01-15 13:23:37 +00:00
Keir Fraser
c7a3c761df SFRKC30.AT4.35: Rotary input pins are always routed to EXTI.
There is never a conflict with the Motor input pin at PB12.
2023-01-12 12:54:50 +00:00
Keir Fraser
6ada2a88a7 IMG.CFG: Add files for Ensoniq and Sequential Circuits hosts 2023-01-03 10:55:10 +00:00
Keir Fraser
4db76683d7 IMG.CFG: Allow sector size to be specified per sector. 2022-12-20 08:10:05 +00:00
Keir Fraser
96aeecc0bb FF.CFG, OLED: New display-type sub-option "hflip". 2022-12-20 08:09:45 +00:00
Keir Fraser
39f15211de IMG.CFG: Dynacord ADS 2022-12-17 17:41:53 +00:00
Keir Fraser
e787a59862 Roland: Update IMG.CFG for 1.44MB sector skew observed on MT-200. 2022-12-15 17:37:07 +00:00
46 changed files with 1120 additions and 438 deletions

View file

@ -2,10 +2,10 @@ name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set environment variables
id: vars
@ -43,7 +43,7 @@ jobs:
mv flashfloppy-$VER.zip _cidist/
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: FlashFloppy.CI.${{ steps.vars.outputs.sha_short }}
path: _cidist

View file

@ -7,10 +7,10 @@ name: Release
jobs:
build:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set environment variables
id: vars
@ -38,7 +38,7 @@ jobs:
tag: ${{ github.ref }}
token: ${{ secrets.GITHUB_TOKEN }}
name: FlashFloppy ${{ steps.vars.outputs.ver }}
body: "[**Release Notes:**](https://github.com/keirf/flashfloppy/blob/stable-v3/RELEASE_NOTES)"
body: "[**Release Notes:**](https://github.com/keirf/flashfloppy/blob/master/RELEASE_NOTES)"
draft: false
prerelease: false
artifacts: flashfloppy-${{ steps.vars.outputs.ver }}.zip

View file

@ -115,6 +115,8 @@ dist: FORCE all
cp -a README $(t)/
cp -a RELEASE_NOTES $(t)/
cp -a examples $(t)/
# Clive Drive is particularly fussy about QD timings.
$(PYTHON) scripts/mk_qd.py --window=6.4 --total=7.5 --round $(t)/examples/Host/Sinclair_ZX_Spectrum/Clive_Drive/CliveDrive_Blank.qd
[ -e ext/HxC_Compat_Mode-$(HXC_FF_VER).zip ] || \
(mkdir -p ext ; cd ext ; wget -q --show-progress $(HXC_FF_URL)/$(HXC_FF_VER)/HxC_Compat_Mode-$(HXC_FF_VER).zip ; rm -rf index.html)
(cd $(t) && unzip -q ../../ext/HxC_Compat_Mode-$(HXC_FF_VER).zip)
@ -127,12 +129,13 @@ BAUD=115200
DEV=/dev/ttyUSB0
SUDO=sudo
STM32FLASH=stm32flash
T=out/$(target)/target.hex
ocd: FORCE all
$(PYTHON) scripts/openocd/flash.py $(target)/target.hex
$(PYTHON) scripts/openocd/flash.py $(T)
flash: FORCE all
$(SUDO) $(STM32FLASH) -b $(BAUD) -w $(target)/target.hex $(DEV)
$(SUDO) $(STM32FLASH) -b $(BAUD) -w $(T) $(DEV)
start: FORCE
$(SUDO) $(STM32FLASH) -b $(BAUD) -g 0 $(DEV)

View file

@ -6,6 +6,8 @@
![Downloads Badge][downloads-badge]
![Version Badge][version-badge]
<img src="https://raw.githubusercontent.com/wiki/keirf/flashfloppy/assets/banner.jpg">
**FlashFloppy** is a floppy-drive emulator for the ubiquitous
[**Gotek**][Gotek-Compatibility] hardware. Connect to retro machines just
like a real floppy drive but use disk images on a modern USB stick!

View file

@ -3,6 +3,30 @@
** Keir Fraser <keir.xen@gmail.com>
************************************
** v3.42 - 11 January 2024
- HFEv3: Various read/write improvements
- WDATA: Merge short write pulses, and apply de-jitter/precomp
- IMG, EDSK: Stream large sector writes to flash
** v3.41 - 14 July 2023
- AT32F415: Fix timer handling since clock speed increase (v3.39).
- LCD: Faster power-on initialisation. Don't wait for display to clear.
** v3.40 - 27 June 2023
- XDF: Fix writes to side 1 of XDF disk images
- HFE: Better handling of long No Flux Areas
- QuickDisk: Reduce motor spinup time to 1 second (previously 2 seconds)
- LCD: Completely blank display when backlight is off
** v3.39 - 1 March 2023
- AT32F415: Run this MCU at 144MHz (previously 72MHz).
- HFE: Fix HFEv3 support. Support Dungeon Master & Chaos Strikes Back.
- IMG.CFG: Support mixed sector sizes per track (Ensoniq Mirage etc).
- IMG.CFG: New option img_bps= allows padding of short sectors in IMG files.
- FF.CFG: New option notify-volume= for notifying on insert/eject events.
- FF.CFG: New OLED display sub-option 'hflip' horizontally flips display.
- Various other small fixes.
** v3.38 - 4 December 2022
- AT32F435: Fix RDATA release on drive deassert
- SF7: Support Sega SF-7000 *.SF7 images

View file

@ -13,11 +13,11 @@
# jc: Specified by jumper JC (open: shugart, closed: ibmpc)
# shugart: P2=DSKCHG, P34=RDY (Amiga, Atari ST, many others)
# ibmpc: P2=unused, P34=DSKCHG (IBM PC interface)
# ibmpc-hdout: P2=HD_OUT, P34=DSKCHG (not generally needed: prefer 'ibmpc')
# ibmpc-hdout: P2=HD_OUT, P34=DSKCHG (not generally needed: prefer ibmpc)
# jppc: P2=unused, P34=RDY (Japanese PC standard)
# jppc-hdout: P2=HD_OUT, P34=RDY (Japanese PC alternate: prefer 'jppc')
# akai-s950: Legacy alias of 'jppc-hdout', previously used for Akai S950
# amiga: P2=DSKCHG, P34=DRIVE_ID (not generally needed: prefer 'shugart')
# jppc-hdout: P2=HD_OUT, P34=RDY (Japanese PC alternate: prefer jppc)
# akai-s950: Legacy alias of jppc-hdout, previously used for Akai S950
# amiga: P2=DSKCHG, P34=DRIVE_ID (not generally needed: prefer shugart)
interface = jc
# Host platform: Improves image-format detection for generic types such as IMG
@ -172,9 +172,9 @@ twobutton-action = zero
# Input sensor type at the rotary-encoder inputs (pins PC10 and PC11):
# [full | half | quarter]:
# Rotary encoder, identified by fraction of a Gray-code cycle performed
# per detent/click. If default value ('full') requires multiple
# clicks/detents to move position then change to 'half' (if 2 clicks
# per move) or 'quarter' (if 4 clicks).
# per detent/click. If default value (full) requires multiple
# clicks/detents to move position then change to half (if 2 clicks
# per move) or quarter (if 4 clicks).
# [trackball]:
# Blackberry-style trackball (eg. using Hall-effect sensors).
# [buttons]:
@ -199,6 +199,7 @@ indexed-prefix = "DSKA"
# lcd-CCxRR: CCxRR backlit LCD with I2C backpack (16<=CC<=40, 02<=RR<=04)
# oled-128xNN: 128xNN I2C OLED (NN = 32 | 64)
# -rotate: OLED view is rotated 180 degrees
# -hflip: OLED view is flipped horizontally
# -narrow[er]: OLED view is restricted to Gotek display cutout
# (-narrow: 18 chars; -narrower: 16 chars)
# -inverse: Inverse/reverse video (black text on white background)
@ -219,15 +220,15 @@ oled-font = 6x13
oled-contrast = 143
# Text height and arrangement on LCD/OLED and on OSD, respectively.
# 'default', or a comma-separated list (one entry per LCD/OLED row, top down).
# Each list item is a digit plus optional height specifier: <content-row>[d]
# content-row: '0-3' = specified content row, '7' = blank
# Comma-separated list, one entry per display row, top down.
# Each list item is a digit plus optional height specifier: [0-7][d]
# content-row: 0-3 = specified content, 7 = blank
# 0: Current image name
# 1: Status
# 2: Image/Volume info
# 3: Current subfolder name
# height specifier: 'd' = double height (32px, OLED only; ignored for LCD)
# 'default' depends on display, eg.: oled-128x32='0,1' ; oled-128x64='3,0d,1'
# height-specifier: d = double height (32px, OLED only; ignored for LCD)
# Default depends on display, eg.: oled-128x32 -> 0,1 ; oled-128x64 -> 3,0d,1
# Values: [0-7][d] | default
display-order = default
osd-display-order = default
@ -272,6 +273,14 @@ nav-scroll-pause = 300
# Values: 0 <= N <= 20
step-volume = 10
# Speaker volume for insert, eject, and slot-number notifications.
# Slot number is indicated by a sequence of beeps when an image is mounted
# iff "slotnr" is specified. The slot number is then notified by a sequence of
# long beeps (each counting +5), followed by a sequence of short beeps
# (each counting +1).
# Values: N[,slotnr] (0 <= N <= 15)
notify-volume = 0
# Report the specified version number to host software
# Values: <quoted-string> ("" means report real version)
# eg. da-report-version = "v3.0.0.0"

View file

@ -0,0 +1,16 @@
## IMG.CFG for Dynacord ADS hosts.
# NOTE: The tags match on filesize alone. If you wish to define an explicit
# tagname match, you can for example add 'dyna' to the square-bracketed tags
# to limit matches to filenames of the form *.dyna.{img,ima,dsk}
[::1638400]
cyls = 80
heads = 2
bps = 512
secs = 20
interleave = 2
tracks = 0-79.0 ## Head 0
id = 1
tracks = 0-79.1 ## Head 1
id = 21 # Follows on from head 0 (1..20 -> 21..40)

View file

@ -0,0 +1,39 @@
# IMG.CFG for Ensoniq hosts
# 800kB and 1.6MB image types are also handled by FF.CFG: host=ensoniq
# NOTE: The tags match on filesize alone. If you wish to define an explicit
# tagname match, you can for example add 'ensoniq' to the square-bracketed tags
# to limit matches to filenames of the form *.ensoniq.{img,ima,dsk}
# Ensoniq 800kB DSDD
# Also handled by FF.CFG: host=ensoniq
[::819200]
cyls = 80
heads = 2
secs = 10
bps = 512
gap3 = 30
id = 0
rate = 250
# Ensoniq 1.6MB DSHD
# Also handled by FF.CFG: host=ensoniq
[::1638400]
cyls = 80
heads = 2
secs = 20
bps = 512
gap3 = 40
id = 0
rate = 500
# Ensoniq Mirage 440kB SSDD
# Mixed sector sizes *NOT* handled by FF.CFG: host=ensoniq
[::450560]
cyls = 80
heads = 1
secs = 6
bps = 1024,1024,1024,1024,1024,512
id = 0
rate = 250

View file

@ -0,0 +1,17 @@
## IMG.CFG for machines produced by GRiD Systems Corp.
# NOTE: The tags match on filesize alone. If you wish to define an explicit
# tagname match, you can for example add 'flex' to the square-bracketed tags
# to limit matches to filenames of the form *.flex.{img,ima,dsk}
# 360k 40-cylinder DS/DD format used by GRiD Compass
[::368640]
cyls = 40
heads = 2
bps = 512
secs = 9
mode = mfm
interleave = 5
id = 1
tracks = 0-39.1
id = 10

View file

@ -37,3 +37,24 @@ tracks = 0-79 # This line can be adjusted
interleave = 2
hskew = 1
cskew = 2
# Matches 720kB images named *.mc.{img,ima,dsk}.
# This higher level of interleave is found on all disks for the
# Roland MC-300, MC-500 and MC-50.
[mc::737280]
cyls = 80
heads = 2
secs = 9
bps = 512
tracks = 0-79
interleave = 4
# Roland 1.44MB format may apply sector skew.
# This is as seen on a Roland MT-200.
[::1474560]
cyls = 80
heads = 2
secs = 18
bps = 512
hskew = 3
cskew = 6

View file

@ -0,0 +1,14 @@
# IMG.CFG for Sequential Circuits hosts
# NOTE: The tags match on filesize alone. If you wish to define an explicit
# tagname match, you can for example add 'sci' to the square-bracketed tags
# to limit matches to filenames of the form *.sci.{img,ima,dsk}
# Sequential Circuits Prophet 840kB DSDD
[::860160]
cyls = 80
heads = 2
secs = 6
bps = 1024,1024,1024,1024,1024,256
id = 0
rate = 250

View file

@ -0,0 +1,24 @@
The Clive Drive controller has a 16-pin drive interface. Pins 3-12
inclusive correspond to the usual 10-pin QD interface. For reference,
see the Clive Drive schematic at:
https://speccy4ever.speccy.org/rom/clive/Clive%20Drive.pdf
Clive Controller QDD Gotek
1---------NC
2---------NC
3---------WRITE PROTECT (WRPR) 1 28
4---------WRITE DATA (WRDT) 2 22
5---------WRITE GATE 1 (WRGT1) 3 24
6---------MOTOR ON 1 (MTON1) 4 16
7---------READ DATA (RDDT) 5 30
8---------READY 6 34
9---------MEDIA SENSE (MDST) 7 2
10--------QD RESET (QDDRST) 8 20
11--------VCC +5V 9 +5v
12--------GND 10 GND
13--------MOTOR ON 2 (MTON2) NC NC
14--------WRITE GATE 2 (WRGT2) NC NC
15--------SEL IN (SELIN) NC NC
16--------NC

View file

@ -0,0 +1,45 @@
## IMG.CFG for the Sandy FDD2 interface.
# Sandy FDD2 is a clone of the FDC-1 by Technology Research Ltd.
# Using a 1771 controller chip, it supports single-density (FM) recording
# only, unlike the later Beta Disk interfaces.
# *.ss40.img: Single-sided 40 cylinders.
[ss40::102400]
cyls = 40
heads = 1
secs = 10
bps = 256
mode = fm
interleave = 2
# *.ss80.img: Single-sided 80 cylinders.
[ss80::204800]
cyls = 80
heads = 1
secs = 10
bps = 256
mode = fm
interleave = 2
# *.ds40.img: Double-sided 40 cylinders.
[ds40::204800]
cyls = 40
heads = 2
secs = 10
bps = 256
mode = fm
interleave = 2
tracks = 0-39.1
h = 0
# *.ds80.img: Double-sided 80 cylinders.
[ds80::409600]
cyls = 80
heads = 2
secs = 10
bps = 256
mode = fm
interleave = 2
tracks = 0-79.1
h = 0

View file

@ -69,6 +69,15 @@ secs = 9
# Mandatory if @secs is non-zero.
bps = 512
# Alternative form in which bytes per sector is specified per sector.
# The list is comma separated; no white space allowed.
# bps = 512,512,512,512,512,512,512,512,256
# Bytes per sector within the IMG file. Smaller sectors will be padded.
# The default is 0: Sectors occupy precisely their data size; No padding.
# Supported values: 0, 128, 256, 512, 1024, 2048, 4096, 8192.
# img_bps = 0
# ID of first sector on each track (0-255). Default is 1.
# Numbers may be expressed in hexadecimal with 0x prefix (eg. 0xab).
# id = 1

View file

@ -102,6 +102,7 @@ struct packed ff_cfg {
#define DISPLAY_oled_64 (1<<5)
#define DISPLAY_inverse (1<<6)
#define DISPLAY_slow (1<<7)
#define DISPLAY_hflip (1<<8)
/* Only if DISPLAY_lcd: */
#define _DISPLAY_lcd_columns 5
#define DISPLAY_lcd_columns(x) ((x)<<_DISPLAY_lcd_columns)
@ -166,6 +167,9 @@ struct packed ff_cfg {
uint8_t max_cyl;
uint16_t osd_display_order;
uint8_t osd_columns;
#define NOTIFY_volume_mask 15
#define NOTIFY_slotnr (1<<4)
uint8_t notify_volume;
};
extern struct ff_cfg ff_cfg;

View file

@ -20,6 +20,7 @@
#include "mcu/common.h"
#if MCU == STM32F105
#include "mcu/stm32f105_regs.h"
#include "mcu/at32f415_regs.h"
#include "mcu/stm32f105.h"
#elif MCU == AT32F435
#include "mcu/at32f435_regs.h"

View file

@ -91,6 +91,7 @@ struct raw_trk {
uint16_t sec_off;
uint16_t data_rate;
uint16_t rpm;
uint16_t img_bps; /* could squeeze this field into uint8_t or bitfield */
int16_t gap_2, gap_3, gap_4a;
uint8_t interleave, cskew, hskew;
uint8_t has_iam:1, is_fm:1, invert_data:1;

34
inc/mcu/at32f415_regs.h Normal file
View file

@ -0,0 +1,34 @@
/*
* at32f415_regs.h
*
* Extra registers and features of the AT32F415 that we use, over and above
* the baseline features of the STM32F105.
*
* Written & released by Keir Fraser <keir.xen@gmail.com>
*
* This is free and unencumbered software released into the public domain.
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
#define RCC_CFGR_PLLMUL_18 ((uint32_t)0x20040000)
#define RCC_CFGR_USBPSC_3 ((uint32_t)0x08400000)
#define RCC_PLL (&rcc->cfgr2)
#define RCC_PLL_PLLCFGEN (1u<<31)
#define RCC_PLL_FREF_MASK (7u<<24)
#define RCC_PLL_FREF_8M (2u<<24)
static volatile uint32_t * const RCC_MISC2 = (uint32_t *)(RCC_BASE + 0x54);
#define RCC_MISC2_AUTOSTEP_EN (3u<< 4)
#define TIM_CR1_PMEN (1u<<10)
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

View file

@ -56,9 +56,9 @@ static USB_OTG usb_otg = (struct usb_otg *)USB_OTG_BASE;
/* Clocks */
#define SYSCLK_MHZ 288
#define AHB_MHZ (SYSCLK_MHZ / 1) /* 288MHz */
#define APB1_MHZ (SYSCLK_MHZ / 2) /* 144MHz */
#define APB2_MHZ (SYSCLK_MHZ / 2) /* 144MHz */
#define AHB_MHZ 288
#define APB1_MHZ 144
#define APB2_MHZ 144
/* GPIO */
void gpio_set_af(GPIO gpio, unsigned int pin, unsigned int af);

View file

@ -50,10 +50,12 @@ static USART usart3 = (struct usart *)USART3_BASE;
static USB_OTG usb_otg = (struct usb_otg *)USB_OTG_BASE;
/* Clocks */
#define SYSCLK_MHZ 72
#define AHB_MHZ (SYSCLK_MHZ / 1) /* 72MHz */
#define APB1_MHZ (SYSCLK_MHZ / 2) /* 36MHz */
#define APB2_MHZ (SYSCLK_MHZ / 1) /* 72MHz */
extern unsigned int sysclk_mhz;
extern unsigned int apb1_mhz;
#define SYSCLK_MHZ sysclk_mhz
#define AHB_MHZ sysclk_mhz
#define APB1_MHZ apb1_mhz
#define APB2_MHZ 72
#define SOFTIRQ_0 43
#define SOFTIRQ_1 44

View file

@ -89,7 +89,8 @@ struct rcc {
#define RCC_CFGR_PLLSRC_HSI (0u<<16)
#define RCC_CFGR_PLLSRC_PREDIV1 (1u<<16)
#define RCC_CFGR_ADCPRE_DIV8 (3u<<14)
#define RCC_CFGR_PPRE1_DIV2 (4u<<8)
#define RCC_CFGR_APB2PSC_2 (4u<<11)
#define RCC_CFGR_APB1PSC_2 (4u<< 8)
#define RCC_CFGR_SWS_HSI (0u<<2)
#define RCC_CFGR_SWS_HSE (1u<<2)
#define RCC_CFGR_SWS_PLL (2u<<2)

View file

@ -14,6 +14,7 @@ typedef uint32_t time_t;
#define TIME_MHZ STK_MHZ
#define time_us(x) stk_us(x)
#define time_ms(x) stk_ms(x)
#define time_stk(x) (x)
#define time_sysclk(x) stk_sysclk(x)
#define sysclk_time(x) sysclk_stk(x)

View file

@ -69,6 +69,8 @@ int snprintf(char *str, size_t size, const char *format, ...)
#define htobe16(x) _rev16(x)
#define htobe32(x) _rev32(x)
uint32_t udiv64(uint64_t dividend, uint32_t divisor);
/* Arena-based memory allocation */
void *arena_alloc(uint32_t sz);
uint32_t arena_total(void);
@ -89,6 +91,8 @@ static inline int vprintk(const char *format, va_list ap) { return 0; }
static inline int printk(const char *format, ...) { return 0; }
#endif
#define log(f, a...) printk("%s: " f, LOG_PREFIX, ## a)
#if defined(LOGFILE)
/* Logfile management */
void logfile_flush(FIL *file);
@ -120,6 +124,8 @@ extern uint8_t display_type;
/* Speaker. */
void speaker_init(void);
void speaker_pulse(void);
void speaker_notify_insert(unsigned int slotnr);
void speaker_notify_eject(void);
/* Display: 3-digit 7-segment display */
void led_7seg_init(void);

View file

@ -46,6 +46,15 @@ def main(argv):
else:
opts += ['DISPLAY_' + x]
val = '|'.join(opts)
elif opt == "notify-volume":
opts = []
for x in val.split(","):
vol = re.match("([0-9]+)", x)
if vol:
opts.append(vol.group(1))
else:
opts.append('NOTIFY_' + x)
val = '|'.join(opts)
elif opt == "display-order" or opt == "osd-display-order":
if val == "default":
val = "DORD_" + val

View file

@ -9,39 +9,59 @@
import sys,struct,argparse
def round_up(x, y):
return (x + y - 1) & ~(y - 1)
def main(argv):
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--window", type=float, default=5.5,
help="data window, seconds")
parser.add_argument("--total", type=float, default=8.0,
help="total length, seconds")
parser.add_argument("outfile", help="output filename")
args = parser.parse_args(argv[1:])
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--lead-in", type=float, default=0.5,
help="lead-in, seconds")
parser.add_argument("--window", type=float, default=5.5,
help="data window, seconds")
parser.add_argument("--total", type=float, default=8.0,
help="total length, seconds")
parser.add_argument("--round", action="store_true",
help="round values up to 512-byte block size")
parser.add_argument("outfile", help="output filename")
args = parser.parse_args(argv[1:])
bit_ms = 0.004916
total_bytes = int(args.total * 1000.0 / bit_ms / 8)
window_bytes = int(args.window * 1000.0 / bit_ms / 8)
init_bytes = int(500.0 / bit_ms / 8)
lead_in, window, total = args.lead_in, args.window, args.total
assert (2*init_bytes + window_bytes) < total_bytes, "Window too large"
print("Lead-In: %.2f sec -> %u bytes" % (0.5, init_bytes))
print("Window: %.2f sec -> %u bytes" % (args.window, window_bytes))
print("TOTAL: %.2f sec -> %u bytes" % (args.total, total_bytes))
assert lead_in >= 0.1, "Insufficient lead-in"
assert total - window - lead_in >= 0.1, "Insufficient lead-out"
# Header
out_f = open(args.outfile, "wb")
out_f.write(struct.pack("<3x2s3x", b"QD"))
out_f.write(bytearray(b'\x00'*(512-8)))
bit_ms = 0.004916
total_bytes = int(total * 1000.0 / bit_ms / 8)
window_bytes = int(window * 1000.0 / bit_ms / 8)
init_bytes = int(lead_in * 1000.0 / bit_ms / 8)
# Track
out_f.write(struct.pack("<4I", 1024, total_bytes, init_bytes,
init_bytes + window_bytes))
out_f.write(bytearray(b'\x00'*(512-16)))
if args.round:
total_bytes = round_up(total_bytes, 512)
window_bytes = round_up(window_bytes, 512)
init_bytes = round_up(init_bytes, 512)
# Data
blocks = (total_bytes + 511) // 512
out_f.write(bytearray(b'\x11'*(blocks*512)))
print("Lead-In: %.2f sec -> %u bytes" % (lead_in, init_bytes))
print("Window: %.2f sec -> %u bytes" % (window, window_bytes))
print("TOTAL: %.2f sec -> %u bytes" % (total, total_bytes))
# Header
out_f = open(args.outfile, "wb")
out_f.write(struct.pack("<3x2s3x", b"QD"))
out_f.write(bytearray(b'\x00'*(512-8)))
# Track
out_f.write(struct.pack("<4I", 1024, total_bytes, init_bytes,
init_bytes + window_bytes))
out_f.write(bytearray(b'\x00'*(512-16)))
# Data
blocks = (total_bytes + 511) // 512
out_f.write(bytearray(b'\x11'*(blocks*512)))
if __name__ == "__main__":
main(sys.argv)
# Local variables:
# python-indent: 4
# End:

View file

@ -20,12 +20,14 @@
#define usart_gpio gpioa
#define usart_tx_pin 9
#define usart_rx_pin 10
#define PCLK (APB2_MHZ * 1000000)
#else
#define usart usart3
#define USART_IRQ USART3_IRQ
#define usart_gpio gpioc
#define usart_tx_pin 10
#define usart_rx_pin 11
#define PCLK (APB1_MHZ * 1000000)
#endif
/* Normally flush to serial is asynchronously executed in a low-pri IRQ. */
@ -147,7 +149,7 @@ void console_init(void)
#endif
/* BAUD, 8n1. */
usart->brr = (APB2_MHZ * 1000000) / BAUD;
usart->brr = PCLK / BAUD;
usart->cr1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
usart->cr3 = 0;

View file

@ -290,7 +290,7 @@ static unsigned int lcd_prep_buffer(void)
order = ff_cfg.display_order;
row = (order >> (i2c_row * DORD_shift)) & DORD_row;
p = (row < ARRAY_SIZE(text)) ? text[row] : NULL;
p = (_bl && row < ARRAY_SIZE(text)) ? text[row] : NULL;
emit8(&q, CMD_SETDDRADDR | row_offs[i2c_row], 0);
for (i = 0; i < lcd_columns; i++)
@ -650,11 +650,8 @@ bool_t lcd_init(void)
emit8(&p, CMD_DISPLAYCTL | 4, 0); /* display on */
dma_start(p - buffer);
/* Wait for DMA engine to initialise RAM, then turn on backlight. */
if (!reinit) {
lcd_sync();
if (!reinit)
lcd_backlight(TRUE);
}
return TRUE;
@ -957,7 +954,6 @@ static void oled_init(void)
0xa0, /* segment mapping (default) */
0xc0, /* com scan direction (default) */
};
const uint8_t *cmds;
uint8_t dynamic_cmds[7], *dc;
uint8_t *p = buffer;
@ -981,9 +977,13 @@ static void oled_init(void)
*dc++ = (oled_height == 64) ? 0x12 : 0x02;
p += oled_queue_cmds(p, dynamic_cmds, dc - dynamic_cmds);
/* Display is right-way-up, or rotated. */
cmds = (ff_cfg.display_type & DISPLAY_rotate) ? rot_cmds : norot_cmds;
p += oled_queue_cmds(p, cmds, sizeof(rot_cmds));
/* Display orientation. */
dc = dynamic_cmds;
memcpy(dc, (ff_cfg.display_type & DISPLAY_rotate) ? rot_cmds : norot_cmds,
2);
if (ff_cfg.display_type & DISPLAY_hflip)
dc[0] ^= 1;
p += oled_queue_cmds(p, dc, 2);
/* Start off the I2C transaction. */
p += oled_start_i2c(p);

View file

@ -43,7 +43,7 @@ struct packed i2c_osd_info {
};
/* STM32 I2C peripheral. */
static volatile struct i2c *i2c = i2c2;
static volatile struct i2c *i2c;
const static struct i2c_cfg {
uint8_t en;
@ -74,6 +74,8 @@ const static struct i2c_cfg {
#define SCL i2c_cfg->scl
#define SDA i2c_cfg->sda
#define PCLK_MHZ APB1_MHZ
/* I2C error ISR. */
#define I2C_ERROR_IRQ i2c_cfg->error_irq
void IRQ_34(void) __attribute__((alias("IRQ_i2c_error")));
@ -325,7 +327,7 @@ static unsigned int lcd_prep_buffer(void)
order = ff_cfg.display_order;
row = (order >> (i2c_row * DORD_shift)) & DORD_row;
p = (row < ARRAY_SIZE(text)) ? text[row] : NULL;
p = (_bl && row < ARRAY_SIZE(text)) ? text[row] : NULL;
emit8(&q, CMD_SETDDRADDR | row_offs[i2c_row], 0);
for (i = 0; i < lcd_columns; i++)
@ -586,9 +588,9 @@ bool_t lcd_init(void)
/* Standard Mode (100kHz) */
i2c->cr1 = 0;
i2c->cr2 = I2C_CR2_FREQ(36);
i2c->ccr = I2C_CCR_CCR(180);
i2c->trise = 37;
i2c->cr2 = I2C_CR2_FREQ(PCLK_MHZ);
i2c->ccr = I2C_CCR_CCR(PCLK_MHZ*5);
i2c->trise = (PCLK_MHZ <= 62) ? PCLK_MHZ + 1 : 63;
i2c->cr1 = I2C_CR1_PE;
if (!reinit) {
@ -713,11 +715,8 @@ bool_t lcd_init(void)
i2c->cr2 |= I2C_CR2_DMAEN;
dma_start(p - buffer);
/* Wait for DMA engine to initialise RAM, then turn on backlight. */
if (!reinit) {
lcd_sync();
if (!reinit)
lcd_backlight(TRUE);
}
return TRUE;
@ -1051,9 +1050,9 @@ static void oled_init_fast_mode(void)
i2c->cr1 = 0;
/* Fast Mode (400kHz). */
i2c->cr2 = I2C_CR2_FREQ(36);
i2c->ccr = I2C_CCR_FS | I2C_CCR_CCR(30);
i2c->trise = 12;
i2c->cr2 = I2C_CR2_FREQ(PCLK_MHZ);
i2c->ccr = I2C_CCR_FS | I2C_CCR_CCR(PCLK_MHZ*5/6);
i2c->trise = PCLK_MHZ*3/10 + 1;
i2c->cr1 = I2C_CR1_PE;
i2c->cr2 |= I2C_CR2_ITERREN;
}
@ -1077,7 +1076,6 @@ static void oled_init(void)
0xa0, /* segment mapping (default) */
0xc0, /* com scan direction (default) */
};
const uint8_t *cmds;
uint8_t dynamic_cmds[7], *dc;
uint8_t *p = buffer;
@ -1101,9 +1099,13 @@ static void oled_init(void)
*dc++ = (oled_height == 64) ? 0x12 : 0x02;
p += oled_queue_cmds(p, dynamic_cmds, dc - dynamic_cmds);
/* Display is right-way-up, or rotated. */
cmds = (ff_cfg.display_type & DISPLAY_rotate) ? rot_cmds : norot_cmds;
p += oled_queue_cmds(p, cmds, sizeof(rot_cmds));
/* Display orientation. */
dc = dynamic_cmds;
memcpy(dc, (ff_cfg.display_type & DISPLAY_rotate) ? rot_cmds : norot_cmds,
2);
if (ff_cfg.display_type & DISPLAY_hflip)
dc[0] ^= 1;
p += oled_queue_cmds(p, dc, 2);
/* Start off the I2C transaction. */
p += oled_start_i2c(p);

View file

@ -24,7 +24,8 @@
static uint8_t DAT_PIN = 10;
static uint8_t CLK_PIN = 11;
/* TM1651, 74HC164: Alphanumeric segment arrangements. */
/* TM1651, 74HC164: Alphanumeric segment arrangements.
* Bit positions 0-6 correspond to conventional segment labels A-G resp. */
static const uint8_t letters[] = {
0x77, 0x7c, 0x58, 0x5e, 0x79, 0x71, 0x6f, 0x74, 0x04, /* a-i */
0x0e, 0x08, 0x38, 0x40, 0x54, 0x5c, 0x73, 0x67, 0x50, /* j-r */

View file

@ -365,7 +365,7 @@ static void wdata_stop(void)
image->wr_prod++;
#if !defined(QUICKDISK)
if (!ff_cfg.index_suppression) {
if (!ff_cfg.index_suppression && ff_cfg.write_drain != WDRAIN_realtime) {
/* Opportunistically insert an INDEX pulse ahead of writeback. */
drive_change_output(drv, outp_index, TRUE);
index.fake_fired = TRUE;
@ -643,16 +643,14 @@ static void IRQ_rdata_dma(void)
static void IRQ_wdata_dma(void)
{
const uint16_t buf_mask = ARRAY_SIZE(dma_rd->buf) - 1;
uint16_t cons, prod, prev, curr, next;
uint16_t cell = image->write_bc_ticks, window;
uint16_t cons, prod, prev, next;
uint32_t bc_dat = 0, bc_prod;
uint32_t *bc_buf = image->bufs.write_bc.p;
unsigned int sync = image->sync;
unsigned int bc_bufmask = (image->bufs.write_bc.len / 4) - 1;
int curr, cell = image->write_bc_ticks;
struct write *write = NULL;
window = cell + (cell >> 1);
/* Clear DMA peripheral interrupts. */
dma1->ifcr = DMA_IFCR_CGIF(dma_wdata_ch);
@ -676,15 +674,20 @@ static void IRQ_wdata_dma(void)
bc_dat = image->write_bc_window;
for (cons = dma_wr->cons; cons != prod; cons = (cons+1) & buf_mask) {
next = dma_wr->buf[cons];
curr = next - prev;
curr = (int16_t)(next - prev) - (cell >> 1);
if (unlikely(curr < 0)) {
/* Runt flux, much shorter than bitcell clock. Merge it forward. */
continue;
}
prev = next;
while (curr > window) {
curr -= cell;
while ((curr -= cell) > 0) {
bc_dat <<= 1;
bc_prod++;
if (!(bc_prod&31))
bc_buf[((bc_prod-1) / 32) & bc_bufmask] = htobe32(bc_dat);
}
curr += cell >> 1; /* remove the 1/2-cell bias */
prev -= curr >> 2; /* de-jitter/precomp: carry 1/4 of phase error */
bc_dat = (bc_dat << 1) | 1;
bc_prod++;
switch (sync) {

View file

@ -130,13 +130,12 @@ void board_setup_rotary_exti(void)
exti_route_pc(11);
m |= m(10) | m(11);
}
if (has_kc30_header) {
if (((has_kc30_header == 1) && (ff_cfg.motor_delay == MOTOR_ignore))
|| (has_kc30_header == 2) /* No conflict with motor on PB12 */) {
/* KC30 rotary pins PA6, PA15. */
if (ff_cfg.motor_delay == MOTOR_ignore) {
exti_route_pa(6);
exti_route_pa(15);
m |= m(6) | m(15);
}
exti_route_pa(6);
exti_route_pa(15);
m |= m(6) | m(15);
}
board_rotary_exti_mask = m;
exti->rtsr |= m;

View file

@ -325,7 +325,8 @@ static void IRQ_STEP_changed(void)
drive_change_output(drv, outp_trk0, FALSE);
if (dma_rd != NULL) {
rdata_stop();
if (!ff_cfg.index_suppression) {
if (!ff_cfg.index_suppression
&& ff_cfg.track_change != TRKCHG_realtime) {
/* Opportunistically insert an INDEX pulse ahead of seek op. */
drive_change_output(drv, outp_index, TRUE);
index.fake_fired = TRUE;

View file

@ -183,8 +183,8 @@ static void _IRQ_MOTOR_RESET_changed(unsigned int gpioa_idr)
if (!off) {
/* 2 seconds to spin up the motor. */
timer_set(&motor.timer, time_now() + time_ms(2000));
/* 1 second to spin up the motor. */
timer_set(&motor.timer, time_now() + time_ms(1000));
} else {

View file

@ -59,6 +59,88 @@ void speaker_pulse(void)
timer_set(&pulse.timer, now + volume*volume*(TIME_MHZ/3));
}
static void speaker_hz(unsigned int hz, unsigned int ms)
{
unsigned int vol = (ff_cfg.notify_volume & NOTIFY_volume_mask) + 1;
unsigned int period = STK_MHZ * 1000000 / hz;
unsigned int period_on = period * vol * vol / (2*400);
unsigned int nr = hz * ms / 1000;
while (nr--) {
gpio_write_pin(gpio_spk, pin_spk, TRUE);
delay_ticks(period_on);
gpio_write_pin(gpio_spk, pin_spk, FALSE);
delay_ticks(period - period_on);
}
}
static void speaker_lock(void)
{
uint32_t oldpri;
oldpri = IRQ_save(TIMER_IRQ_PRI);
timer_cancel(&pulse.timer);
pulse.state = STATE_masked;
IRQ_restore(oldpri);
}
static void speaker_unlock(void)
{
pulse.state = STATE_idle;
}
static void speaker_notify_slot(unsigned int nr)
{
while (nr >= 5) {
speaker_hz(1500, 100);
nr -= 5;
if (nr != 0)
delay_ms(120);
}
while (nr != 0) {
speaker_hz(1500, 40);
nr -= 1;
if (nr != 0)
delay_ms(120);
}
}
void speaker_notify_insert(unsigned int slotnr)
{
if ((ff_cfg.notify_volume & NOTIFY_volume_mask) == 0)
return;
speaker_lock();
speaker_hz(880, 40); /* a5 */
delay_ms(20);
speaker_hz(784, 40); /* g5 */
delay_ms(20);
speaker_hz(1046, 60); /* c6 */
if (ff_cfg.notify_volume & NOTIFY_slotnr) {
delay_ms(300);
speaker_notify_slot(slotnr);
}
speaker_unlock();
}
void speaker_notify_eject(void)
{
if ((ff_cfg.notify_volume & NOTIFY_volume_mask) == 0)
return;
speaker_lock();
speaker_hz(932, 40); /* a#5 */
delay_ms(20);
speaker_hz(831, 40); /* g#5 */
delay_ms(20);
speaker_hz(659, 60); /* e5 */
speaker_unlock();
}
/*
* Local variables:
* mode: C

View file

@ -41,6 +41,7 @@ static bool_t adf_open(struct image *im)
im->tracklen_bc = DD_TRACKLEN_BC;
im->ticks_per_cell = ((sampleclk_stk(im->stk_per_rev) * 16u)
/ im->tracklen_bc);
im->write_bc_ticks = im->ticks_per_cell / 16u;
im->nr_cyls = f_size(&im->fp) / (2 * 11 * 512);

View file

@ -9,11 +9,15 @@
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
#define LOG_PREFIX "DSK"
#define GAP_1 50 /* Post-IAM */
#define GAP_2 22 /* Post-IDAM */
#define GAP_4A 80 /* Post-Index */
#define GAP_SYNC 12
#define CHUNK_SIZE 1024
struct dib { /* disk info */
char sig[34];
char creator[14];
@ -94,14 +98,14 @@ static bool_t dsk_open(struct image *im)
im->nr_cyls = dib->nr_tracks;
im->nr_sides = dib->nr_sides;
printk("DSK: %u cyls, %u sides\n", im->nr_cyls, im->nr_sides);
log("%u cyls, %u sides\n", im->nr_cyls, im->nr_sides);
/* DSK data rate is fixed at 2us bitcell. Where the specified track layout
* will not fit in regular 100k-bitcell track we simply extend the track
* length and thus the period between index pulses. */
im->ticks_per_cell = im->write_bc_ticks * 16;
volume_cache_init(im->bufs.write_data.p + 512 + 1024,
volume_cache_init(im->bufs.write_data.p + 512 + CHUNK_SIZE,
im->bufs.write_data.p + im->bufs.write_data.len);
return TRUE;
@ -224,9 +228,9 @@ static uint32_t calc_start_pos(struct image *im)
im->dsk.decode_pos++;
if (decode_off < data_sz(&tib->sib[i])) {
/* Data */
im->dsk.rd_sec_pos = decode_off / 1024;
im->dsk.rd_sec_pos = decode_off / CHUNK_SIZE;
im->dsk.decode_data_pos = im->dsk.rd_sec_pos;
decode_off %= 1024;
decode_off %= CHUNK_SIZE;
} else {
/* Post Data */
decode_off -= data_sz(&tib->sib[i]);
@ -238,8 +242,8 @@ static uint32_t calc_start_pos(struct image *im)
} else {
/* Pre-index track gap */
im->dsk.decode_pos = tib->nr_secs * 4 + 1;
im->dsk.decode_data_pos = decode_off / 1024;
decode_off %= 1024;
im->dsk.decode_data_pos = decode_off / CHUNK_SIZE;
decode_off %= CHUNK_SIZE;
}
}
@ -267,15 +271,17 @@ static void dsk_setup_track(
im->cur_ticks = im->cur_bc * im->ticks_per_cell;
im->ticks_since_flux = 0;
decode_off = calc_start_pos(im);
rd->prod = rd->cons = 0;
bc->prod = bc->cons = 0;
if (start_pos) {
decode_off = calc_start_pos(im);
image_read_track(im);
bc->cons = decode_off * 16;
*start_pos = start_ticks;
} else {
im->dsk.decode_pos = 0;
}
}
@ -299,10 +305,10 @@ static bool_t dsk_read_track(struct image *im)
/* Weak sector -- pick different data each revolution. */
off += len * (im->dsk.rev % (tib->sib[i].actual_length / len));
}
off += im->dsk.rd_sec_pos * 1024;
len -= im->dsk.rd_sec_pos * 1024;
if (len > 1024) {
len = 1024;
off += im->dsk.rd_sec_pos * CHUNK_SIZE;
len -= im->dsk.rd_sec_pos * CHUNK_SIZE;
if (len > CHUNK_SIZE) {
len = CHUNK_SIZE;
im->dsk.rd_sec_pos++;
} else {
im->dsk.rd_sec_pos = 0;
@ -346,11 +352,11 @@ static bool_t dsk_read_track(struct image *im)
emit_byte(0x4e);
} else if (im->dsk.decode_pos == (tib->nr_secs * 4 + 1)) {
/* Pre-index track gap */
uint16_t sz = im->dsk.gap4 - im->dsk.decode_data_pos * 1024;
if (bc_space < min_t(unsigned int, sz, 1024))
uint16_t sz = im->dsk.gap4 - im->dsk.decode_data_pos * CHUNK_SIZE;
if (bc_space < min_t(unsigned int, sz, CHUNK_SIZE))
return FALSE;
if (sz > 1024) {
sz = 1024;
if (sz > CHUNK_SIZE) {
sz = CHUNK_SIZE;
im->dsk.decode_data_pos++;
im->dsk.decode_pos--;
} else {
@ -402,11 +408,11 @@ static bool_t dsk_read_track(struct image *im)
}
case 2: /* Data */ {
uint16_t sec_sz = data_sz(&tib->sib[sec]);
sec_sz -= im->dsk.decode_data_pos * 1024;
if (bc_space < min_t(unsigned int, sec_sz, 1024))
sec_sz -= im->dsk.decode_data_pos * CHUNK_SIZE;
if (bc_space < min_t(unsigned int, sec_sz, CHUNK_SIZE))
return FALSE;
if (sec_sz > 1024) {
sec_sz = 1024;
if (sec_sz > CHUNK_SIZE) {
sec_sz = CHUNK_SIZE;
im->dsk.decode_data_pos++;
im->dsk.decode_pos--;
} else {
@ -460,7 +466,7 @@ static int dsk_find_first_write_sector(
}
if (i >= tib->nr_secs) {
printk("DSK Bad Wr.Off: %d\n", base);
log("Bad Wr.Off: %d\n", base);
return -2;
}
@ -477,10 +483,9 @@ static bool_t dsk_write_track(struct image *im)
unsigned int bufmask = (wr->len / 2) - 1;
uint8_t *wrbuf = (uint8_t *)im->bufs.write_data.p + 512; /* skip DIB/TIB */
uint32_t c = wr->cons / 16, p = wr->prod / 16;
unsigned int i;
unsigned int i, off;
time_t t;
uint16_t crc, off;
uint8_t x;
uint16_t crc = im->dsk.crc;
/* If we are processing final data then use the end index, rounded up. */
barrier();
@ -490,94 +495,119 @@ static bool_t dsk_write_track(struct image *im)
while ((int16_t)(p - c) > 128) {
uint32_t sc = c;
if (im->dsk.decode_pos == 0) {
if (be16toh(buf[c++ & bufmask]) != 0x4489)
continue;
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
continue;
c++;
uint8_t x;
switch (x) {
if (be16toh(buf[c++ & bufmask]) != 0x4489)
continue;
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
continue;
c++;
case 0xfe: /* IDAM */
for (i = 0; i < 3; i++)
wrbuf[i] = 0xa1;
wrbuf[i++] = x;
for (; i < 10; i++)
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
crc = crc16_ccitt(wrbuf, i, 0xffff);
if (crc != 0) {
printk("DSK IDAM Bad CRC: %04x, %02x\n", crc, wrbuf[6]);
switch (x) {
case 0xfe: /* IDAM */
for (i = 0; i < 3; i++)
wrbuf[i] = 0xa1;
wrbuf[i++] = x;
for (; i < 10; i++)
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
crc = crc16_ccitt(wrbuf, i, 0xffff);
if (crc != 0) {
log("IDAM Bad CRC: %04x, %02x\n", crc, wrbuf[6]);
break;
}
/* Convert logical sector number -> rotational number. */
for (i = 0; i < tib->nr_secs; i++)
if (wrbuf[6] == tib->sib[i].r)
break;
im->dsk.write_sector = i;
if (im->dsk.write_sector >= tib->nr_secs) {
log("IDAM Bad Sector: %02x\n", wrbuf[6]);
im->dsk.write_sector = -2;
}
break;
case 0xfb: /* DAM */
im->dsk.decode_pos = 1;
im->dsk.decode_data_pos = 0;
break;
}
/* Convert logical sector number -> rotational number. */
for (i = 0; i < tib->nr_secs; i++)
if (wrbuf[6] == tib->sib[i].r)
break;
im->dsk.write_sector = i;
if (im->dsk.write_sector >= tib->nr_secs) {
printk("DSK IDAM Bad Sector: %02x\n", wrbuf[6]);
im->dsk.write_sector = -2;
}
break;
case 0xfb: /* DAM */ {
unsigned int nr, todo, sec_sz;
} else {
/* Data record, shy address mark */
unsigned int sec_sz;
int sec_nr = im->dsk.write_sector;
ASSERT(im->dsk.decode_pos == 1);
if (sec_nr < 0) {
if (sec_nr == -1)
if (sec_nr == -1) {
sec_nr = dsk_find_first_write_sector(im, write, tib);
im->dsk.write_sector = sec_nr;
}
if (sec_nr < 0) {
printk("DSK DAM Unknown\n");
goto dam_out;
log("DAM Unknown\n");
goto data_complete;
}
}
sec_sz = data_sz(&tib->sib[sec_nr]);
if ((int16_t)(p - c) < (sec_sz + 2)) {
c = sc;
goto out;
}
crc = MFM_DAM_CRC;
printk("Write %d[%02x]/%u... ",
sec_nr, tib->sib[sec_nr].r, tib->nr_secs);
t = time_now();
for (i = off = 0; i < sec_nr; i++)
off += tib->sib[i].actual_length;
F_lseek(&im->fp, im->dsk.trk_off + off);
off += im->dsk.trk_off;
off += im->dsk.decode_data_pos;
for (todo = sec_sz; todo != 0; todo -= nr) {
nr = min_t(unsigned int, todo, 1024);
if (im->dsk.decode_data_pos < sec_sz) {
unsigned int nr = sec_sz - im->dsk.decode_data_pos;
nr = min_t(unsigned int, nr, CHUNK_SIZE - (off & 511));
if ((int16_t)(p - c) < nr)
break;
if (!im->dsk.decode_data_pos) {
crc = MFM_DAM_CRC;
log("Write %d[%02x]/%u...",
sec_nr, tib->sib[sec_nr].r, tib->nr_secs);
F_lseek(&im->fp, off);
}
t = time_now();
mfm_ring_to_bin(buf, bufmask, c, wrbuf, nr);
c += nr;
crc = crc16_ccitt(wrbuf, nr, crc);
F_write(&im->fp, wrbuf, nr, NULL);
printk(" %u us", time_diff(t, time_now()) / TIME_MHZ);
im->dsk.decode_data_pos += nr;
if (im->dsk.decode_data_pos < sec_sz)
printk("...");
else
printk("\n");
}
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
if (im->dsk.decode_data_pos < sec_sz)
continue;
if ((int16_t)(p - c) < 2)
break;
mfm_ring_to_bin(buf, bufmask, c, wrbuf, 2);
c += 2;
crc = crc16_ccitt(wrbuf, 2, crc);
if (crc != 0) {
printk("DSK Bad CRC: %04x, %d[%02x]\n",
crc, sec_nr, tib->sib[sec_nr].r);
log("Bad CRC: %04x, %d[%02x]\n",
crc, sec_nr, tib->sib[sec_nr].r);
}
dam_out:
data_complete:
im->dsk.write_sector = -2;
break;
}
im->dsk.decode_pos = 0;
}
}
out:
im->dsk.crc = crc;
wr->cons = c * 16;
return flush;
}

View file

@ -60,13 +60,13 @@ struct track_header {
uint16_t len;
};
/* HFEv3 opcodes. The 4-bit codes have their bit ordering reversed. */
/* HFEv3 opcodes. Bit order is reversed to match raw HFE bit order. */
enum {
OP_nop = 0, /* 0: no effect */
OP_index = 8, /* 1: index mark */
OP_bitrate = 4, /* 2: +1byte: new bitrate */
OP_skip = 12, /* 3: +1byte: skip 0-8 bits in next byte */
OP_rand = 2 /* 4: flaky byte */
OP_Nop = 0x0f, /* Nop */
OP_Index = 0x8f, /* Index mark */
OP_Bitrate = 0x4f, /* +1 byte: new bitrate */
OP_SkipBits = 0xcf, /* +1 byte: skip 1-7 bits in following byte */
OP_Rand = 0x2f /* Random byte (or bits, if following OP_skip) */
};
static void hfe_seek_track(struct image *im, uint16_t track);
@ -128,7 +128,13 @@ static void hfe_seek_track(struct image *im, uint16_t track)
im->hfe.trk_off = le16toh(thdr.offset);
im->hfe.trk_len = le16toh(thdr.len) / 2;
im->tracklen_bc = im->hfe.trk_len * 8;
im->stk_per_rev = stk_sampleclk(im->tracklen_bc * im->write_bc_ticks);
if (im->hfe.is_v3 && im->tracklen_ticks) {
/* Opcodes in v3 make it difficult to predict the track's length. Keep
* the previous track's value since this isn't the first seek. */
} else {
im->tracklen_ticks = im->tracklen_bc * im->ticks_per_cell;
im->stk_per_rev = stk_sampleclk(im->tracklen_ticks / 16);
}
im->cur_track = track;
}
@ -147,13 +153,16 @@ static void hfe_setup_track(
hfe_seek_track(im, track);
start_ticks = start_pos ? *start_pos : get_write(im, im->wr_cons)->start;
im->cur_bc = (start_ticks * 16) / im->ticks_per_cell;
if (im->cur_bc >= im->tracklen_bc)
im->cur_bc = 0;
im->cur_ticks = im->cur_bc * im->ticks_per_cell;
im->ticks_since_flux = 0;
start_ticks = im->cur_ticks / 16;
im->cur_ticks = start_ticks * 16;
im->cur_bc = udiv64((uint64_t)im->cur_ticks * im->tracklen_bc,
im->tracklen_ticks);
if ((im->cur_ticks >= im->tracklen_ticks) ||
(im->cur_bc >= im->tracklen_bc)) {
im->cur_ticks = 0;
im->cur_bc = 0;
}
im->ticks_since_flux = 0;
rd->prod = rd->cons = 0;
bc->prod = bc->cons = 0;
@ -167,10 +176,16 @@ static void hfe_setup_track(
im->hfe.trk_pos = (im->cur_bc/8) & ~255;
image_read_track(im);
bc->cons = im->cur_bc & 2047;
*start_pos = start_ticks;
} else {
/* Write mode. */
im->hfe.trk_pos = im->cur_bc / 8;
if (im->hfe.is_v3) {
/* Provide context to the write to avoid corrupting an opcode. */
if ((im->hfe.trk_pos & 255) == 0 && im->hfe.trk_pos != 0)
im->hfe.trk_pos--;
else if ((im->hfe.trk_pos & 255) == 1)
im->hfe.trk_pos = (im->hfe.trk_pos+1) % im->hfe.trk_len;
}
im->hfe.write.start = im->hfe.trk_pos;
im->hfe.write.wrapped = FALSE;
im->hfe.write_batch.len = 0;
@ -231,74 +246,87 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
uint32_t bc_c = bc->cons, bc_p = bc->prod, bc_mask = bc->len - 1;
uint32_t ticks = im->ticks_since_flux;
uint32_t ticks_per_cell = im->ticks_per_cell;
uint32_t y = 8, todo = nr;
uint32_t bit_off, todo = nr;
uint8_t x;
bool_t is_v3 = im->hfe.is_v3;
while ((uint32_t)(bc_p - bc_c) >= 3*8) {
ASSERT(y == 8);
if (im->cur_bc >= im->tracklen_bc) {
/* Malformed HFE v3 file can trigger this assertion. Requires a
* multi-byte opcode which extends beyond reported track length. */
ASSERT(im->cur_bc == im->tracklen_bc);
im->tracklen_ticks = im->cur_ticks;
im->stk_per_rev = stk_sampleclk(im->tracklen_ticks / 16);
im->cur_bc = im->cur_ticks = 0;
/* Skip tail of current 256-byte block. */
bc_c = (bc_c + 256*8-1) & ~(256*8-1);
continue;
}
y = bc_c % 8;
x = bc_b[(bc_c/8) & bc_mask] >> y;
if (is_v3 && (y == 0) && ((x & 0xf) == 0xf)) {
bit_off = bc_c % 8;
x = bc_b[(bc_c/8) & bc_mask];
bc_c += 8 - bit_off;
im->cur_bc += 8 - bit_off;
if (is_v3 && ((x & 0xf) == 0xf)) {
/* V3 byte-aligned opcode processing. */
switch (x >> 4) {
case OP_nop:
case OP_index:
switch (x) {
case OP_Nop:
case OP_Index:
default:
bc_c += 8;
im->cur_bc += 8;
y = 8;
continue;
case OP_bitrate:
x = _rbit32(bc_b[(bc_c/8+1) & bc_mask]) >> 24;
case OP_Bitrate:
x = _rbit32(bc_b[(bc_c/8) & bc_mask]) >> 24;
im->ticks_per_cell = ticks_per_cell =
(sampleclk_us(2) * 16 * x) / 72;
bc_c += 2*8;
im->cur_bc += 2*8;
y = 8;
continue;
case OP_skip:
x = (_rbit32(bc_b[(bc_c/8+1) & bc_mask]) >> 24) & 7;
bc_c += 2*8 + x;
im->cur_bc += 2*8 + x;
y = x;
x = bc_b[(bc_c/8) & bc_mask] >> y;
break;
case OP_rand:
im->write_bc_ticks = ticks_per_cell / 16;
bc_c += 8;
im->cur_bc += 8;
continue;
case OP_SkipBits:
x = (_rbit32(bc_b[(bc_c/8) & bc_mask]) >> 24) & 7;
bc_c += 8 + x;
im->cur_bc += 8 + x;
continue;
case OP_Rand:
x = rand();
break;
}
}
bc_c += 8 - y;
im->cur_bc += 8 - y;
im->cur_ticks += (8 - y) * ticks_per_cell;
while (y < 8) {
y++;
x >>= bit_off;
im->cur_ticks += (8 - bit_off) * ticks_per_cell;
while (bit_off < 8) {
bit_off++;
ticks += ticks_per_cell;
if (x & 1) {
*tbuf++ = (ticks >> 4) - 1;
ticks &= 15;
if (!--todo)
if (!--todo) {
bc_c -= 8 - bit_off;
im->cur_bc -= 8 - bit_off;
im->cur_ticks -= (8 - bit_off) * ticks_per_cell;
goto out;
}
}
x >>= 1;
}
/* Subdivide a long flux gap to avoid overflowing the 16-bit timer.
* This mishandles long No Flux Areas slightly, by regularly emitting
* a flux-reversal pulse every 2^14 sampleclk ticks. */
if (unlikely((ticks >> (15+4)) != 0)) {
*tbuf++ = (1u << 14) - 1;
ticks -= 1u << (14+4);
if (!--todo)
goto out;
}
}
out:
bc->cons = bc_c - (8 - y);
im->cur_bc -= 8 - y;
im->cur_ticks -= (8 - y) * ticks_per_cell;
bc->cons = bc_c;
im->ticks_since_flux = ticks;
return nr - todo;
}
@ -313,6 +341,7 @@ static bool_t hfe_write_track(struct image *im)
unsigned int bufmask = wr->len - 1;
uint8_t *w, *wrbuf = im->bufs.write_data.p;
uint32_t i, space, c = wr->cons / 8, p = wr->prod / 8;
bool_t is_v3 = im->hfe.is_v3;
bool_t writeback = FALSE;
time_t t;
@ -364,11 +393,62 @@ static bool_t hfe_write_track(struct image *im)
+ (im->cur_track & 1) * 256
+ batch_off - im->hfe.write_batch.off
+ (off & 255);
for (i = 0; i < nr; i++)
*w++ = _rbit32(buf[c++ & bufmask]) >> 24;
i = 0;
if (is_v3 && off == im->hfe.write.start && off != 0) {
/* Avoid starting write in the middle of an opcode. */
if (w[-2] == OP_SkipBits) {
i++;
} else {
switch (w[-1]) {
case OP_SkipBits:
i += 2;
break;
case OP_Bitrate:
i++;
break;
default:
break;
}
}
}
while (i < nr) {
if (is_v3 && (w[i] & 0xf) == 0xf) {
switch (w[i]) {
case OP_SkipBits:
/* Keep the write byte-aligned. This changes the length of
* the track by 8+skip bitcells, but overwriting OP_SkipBits
* should be rare. */
w[i++] = OP_Nop;
continue;
case OP_Bitrate:
/* Assume bitrate does not change significantly for the
* entire track, and write_bc_ticks already adjusted when
* reading. */
i += 2;
continue;
case OP_Nop:
case OP_Index:
default:
/* Preserve opcode. But making sure not to write past end of
* buffer. */
i++;
continue;
case OP_Rand:
/* Replace with data. */
break;
}
}
w[i++] = _rbit32(buf[c++ & bufmask]) >> 24;
}
im->hfe.write_batch.dirty = TRUE;
im->hfe.trk_pos += nr;
im->hfe.trk_pos += i; /* i may be larger than nr due to opcodes. */
if (im->hfe.trk_pos >= im->hfe.trk_len) {
ASSERT(im->hfe.trk_pos == im->hfe.trk_len);
im->hfe.trk_pos = 0;

View file

@ -9,6 +9,8 @@
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
#define LOG_PREFIX "IMG"
static FSIZE_t raw_extend(struct image *im);
static void raw_setup_track(
struct image *im, uint16_t track, uint32_t *start_pos);
@ -64,6 +66,8 @@ static bool_t xdf_check(const struct bpb *bpb);
#define LAYOUT_sides_swapped (1u<<1)
#define LAYOUT_reverse_side(x) (1u<<(2+(x)))
#define CHUNK_SIZE 1024
#define sec_sz(n) (128u << (n))
#define _IAM 1 /* IAM */
@ -235,8 +239,14 @@ found:
return raw_open(im);
}
struct tag_layout {
struct simple_layout s;
uint16_t img_bps;
uint8_t no[256];
};
static void tag_add_layout(
struct image *im, const struct simple_layout *layout, unsigned int trk_idx)
struct image *im, const struct tag_layout *layout, unsigned int trk_idx)
{
struct raw_sec *sec;
struct raw_trk *trk;
@ -245,23 +255,24 @@ static void tag_add_layout(
if (trk_idx == 0)
init_track_map(im);
trk = add_track_layout(im, layout->nr_sectors, trk_idx);
trk->is_fm = layout->is_fm;
trk->rpm = layout->rpm;
trk->has_iam = layout->has_iam;
trk->gap_2 = layout->gap2;
trk->gap_3 = layout->gap3;
trk->gap_4a = layout->gap4a;
trk->data_rate = layout->data_rate;
trk->interleave = layout->interleave;
trk->cskew = layout->cskew;
trk->hskew = layout->hskew;
trk->head = layout->head;
trk = add_track_layout(im, layout->s.nr_sectors, trk_idx);
trk->is_fm = layout->s.is_fm;
trk->rpm = layout->s.rpm;
trk->has_iam = layout->s.has_iam;
trk->gap_2 = layout->s.gap2;
trk->gap_3 = layout->s.gap3;
trk->gap_4a = layout->s.gap4a;
trk->data_rate = layout->s.data_rate;
trk->interleave = layout->s.interleave;
trk->cskew = layout->s.cskew;
trk->hskew = layout->s.hskew;
trk->head = layout->s.head;
trk->img_bps = layout->img_bps;
sec = &im->img.sec_info_base[trk->sec_off];
for (i = 0; i < layout->nr_sectors; i++) {
sec->r = i + layout->base[0];
sec->n = layout->no;
for (i = 0; i < layout->s.nr_sectors; i++) {
sec->r = i + layout->s.base[0];
sec->n = layout->no[i];
sec++;
}
}
@ -275,6 +286,7 @@ static bool_t tag_open(struct image *im, char *tag)
IMGCFG_step,
IMGCFG_secs,
IMGCFG_bps,
IMGCFG_img_bps,
IMGCFG_id,
IMGCFG_h,
IMGCFG_mode,
@ -298,6 +310,7 @@ static bool_t tag_open(struct image *im, char *tag)
[IMGCFG_step] = { "step" },
[IMGCFG_secs] = { "secs" },
[IMGCFG_bps] = { "bps" },
[IMGCFG_img_bps] = { "img_bps" },
[IMGCFG_id] = { "id" },
[IMGCFG_h] = { "h" },
[IMGCFG_mode] = { "mode" },
@ -314,12 +327,13 @@ static bool_t tag_open(struct image *im, char *tag)
};
int match, active, option, nr_t = 0;
struct simple_layout t_layout, d_layout;
struct {
FIL file;
struct slot slot;
char buf[512];
} *heap = (void *)im->bufs.read_data.p;
struct tag_layout t_layout;
struct tag_layout d_layout;
} *heap = (void *)im->bufs.write_bc.p;
struct opts opts = {
.file = &heap->file,
.opts = img_cfg_opts,
@ -327,6 +341,8 @@ static bool_t tag_open(struct image *im, char *tag)
.argmax = sizeof(heap->buf)-1
};
ASSERT(sizeof(*heap) <= im->bufs.write_bc.len);
if (!get_img_cfg(&heap->slot))
return FALSE;
@ -340,7 +356,7 @@ static bool_t tag_open(struct image *im, char *tag)
char *p, *q;
/* New section: Finalise any currently-active section. */
if (active) {
tag_add_layout(im, &t_layout, nr_t);
tag_add_layout(im, &heap->t_layout, nr_t);
finalise_track_map(im);
active = 0;
}
@ -371,7 +387,10 @@ static bool_t tag_open(struct image *im, char *tag)
/* Best score so far: Process the section. */
match = active;
reset_all_params(im);
d_layout = t_layout = dfl_simple_layout;
heap->d_layout.s = dfl_simple_layout;
heap->d_layout.img_bps = 0;
memset(heap->d_layout.no, ~0, sizeof(heap->d_layout.no));
heap->t_layout = heap->d_layout;
nr_t = 0;
} else {
/* Mark ourselves inactive for this section. */
@ -387,10 +406,10 @@ static bool_t tag_open(struct image *im, char *tag)
case IMGCFG_tracks: {
char *p = opts.arg;
int c_s, c_e, h_s, h_e, c, h;
tag_add_layout(im, &t_layout, nr_t);
tag_add_layout(im, &heap->t_layout, nr_t);
if (nr_t++ == 0)
d_layout = t_layout;
t_layout = d_layout;
heap->d_layout = heap->t_layout;
heap->t_layout = heap->d_layout;
do {
/* <cylinder>[-<cylinder>] */
c_s = strtol(p, &p, 10);
@ -418,58 +437,71 @@ static bool_t tag_open(struct image *im, char *tag)
im->nr_sides = strtol(opts.arg, NULL, 10);
break;
case IMGCFG_secs:
t_layout.nr_sectors = strtol(opts.arg, NULL, 10);
heap->t_layout.s.nr_sectors = strtol(opts.arg, NULL, 10);
break;
case IMGCFG_step:
im->step = strtol(opts.arg, NULL, 10);
break;
case IMGCFG_bps: {
int no, sz = strtol(opts.arg, NULL, 10);
for (no = 0; no < 8; no++)
if ((128u<<no) == sz)
break;
t_layout.no = no;
case IMGCFG_bps: {
char *p, *q;
int no = ~0, i = 0;
for (p = opts.arg; *p != '\0'; p = q) {
int sz;
for (q = p; *q && *q != ','; q++)
continue;
if (*q == ',')
*q++ = '\0';
sz = strtol(p, NULL, 10);
for (no = 0; no < 8; no++)
if ((128u<<no) == sz)
break;
heap->t_layout.no[i++] = no;
}
memset(&heap->t_layout.no[i], no, sizeof(heap->t_layout.no)-i);
break;
}
case IMGCFG_img_bps:
heap->t_layout.img_bps = strtol(opts.arg, NULL, 0);
break;
}
case IMGCFG_id:
t_layout.base[0] = strtol(opts.arg, NULL, 0);
heap->t_layout.s.base[0] = strtol(opts.arg, NULL, 0);
break;
case IMGCFG_h:
t_layout.head = (*opts.arg == 'a') ? 0
heap->t_layout.s.head = (*opts.arg == 'a') ? 0
: (strtol(opts.arg, NULL, 10) & 1) + 1;
break;
case IMGCFG_mode:
t_layout.is_fm = !strcmp(opts.arg, "fm");
heap->t_layout.s.is_fm = !strcmp(opts.arg, "fm");
break;
case IMGCFG_interleave:
t_layout.interleave = strtol(opts.arg, NULL, 10);
heap->t_layout.s.interleave = strtol(opts.arg, NULL, 10);
break;
case IMGCFG_cskew:
t_layout.cskew = strtol(opts.arg, NULL, 10);
heap->t_layout.s.cskew = strtol(opts.arg, NULL, 10);
break;
case IMGCFG_hskew:
t_layout.hskew = strtol(opts.arg, NULL, 10);
heap->t_layout.s.hskew = strtol(opts.arg, NULL, 10);
break;
case IMGCFG_rpm:
t_layout.rpm = strtol(opts.arg, NULL, 10);
heap->t_layout.s.rpm = strtol(opts.arg, NULL, 10);
break;
case IMGCFG_gap2:
t_layout.gap2 = (*opts.arg == 'a') ? -1
heap->t_layout.s.gap2 = (*opts.arg == 'a') ? -1
: (uint8_t)strtol(opts.arg, NULL, 10);
break;
case IMGCFG_gap3:
t_layout.gap3 = (*opts.arg == 'a') ? -1
heap->t_layout.s.gap3 = (*opts.arg == 'a') ? -1
: (uint8_t)strtol(opts.arg, NULL, 10);
break;
case IMGCFG_gap4a:
t_layout.gap4a = (*opts.arg == 'a') ? -1
heap->t_layout.s.gap4a = (*opts.arg == 'a') ? -1
: (uint8_t)strtol(opts.arg, NULL, 10);
break;
case IMGCFG_iam:
t_layout.has_iam = !strcmp(opts.arg, "yes");
heap->t_layout.s.has_iam = !strcmp(opts.arg, "yes");
break;
case IMGCFG_rate:
t_layout.data_rate = strtol(opts.arg, NULL, 10);
heap->t_layout.s.data_rate = strtol(opts.arg, NULL, 10);
break;
case IMGCFG_file_layout: {
char *p, *q;
@ -494,7 +526,7 @@ static bool_t tag_open(struct image *im, char *tag)
}
if (active) {
tag_add_layout(im, &t_layout, nr_t);
tag_add_layout(im, &heap->t_layout, nr_t);
finalise_track_map(im);
}
@ -1339,13 +1371,13 @@ static bool_t xdf_open(struct image *im)
{ /* 3.5 HD */
/* Cyl 0, head 0:
* 1-8,129-139 (secs=19, interleave=2)
* Sectors 1-8 (Aux FAT): Offsets 0x1800-0x2600
* Sectors 129-139 (Main FAT, Pt.1): Offsets 0x0000-0x1400 */
* Sectors 1-8 (Aux FAT): Offsets 0x1800-0x27ff
* Sectors 129-139 (Main FAT, Pt.1): Offsets 0x0000-0x15ff */
/* Cyl 0, head 1:
* 129-147 (secs=19, interleave=2)
* Sector 129 (Main FAT, Pt.2): Offset 0x1600
* Sectors 130-143 (RootDir): Offsets 0x2e00-0x4800
* Sectors 144-147 (Data): Offsets 0x5400-0x5a00 */
* Sector 129 (Main FAT, Pt.2): Offset 0x1600-0x17ff
* Sectors 130-143 (RootDir): Offsets 0x2e00-0x49ff
* Sectors 144-147 (Data): Offsets 0x5400-0x5bff */
/* Cyl N, head 0:
* 131(1k), 130(.5k), 132(2k), 134(8k)
* Cyl N, head 1: Track slip of ~10k bitcells relative to head 0
@ -1743,10 +1775,14 @@ static void raw_seek_track(
if (file_idx(im, i, j) >= idx)
continue;
trk = &im->img.trk_info[im->img.trk_map[i*im->nr_sides + j]];
sec = &im->img.sec_info_base[trk->sec_off];
for (k = 0; k < trk->nr_sectors; k++) {
off += sec_sz(sec->n);
sec++;
if (trk->img_bps != 0) {
off += trk->nr_sectors * trk->img_bps;
} else {
sec = &im->img.sec_info_base[trk->sec_off];
for (k = 0; k < trk->nr_sectors; k++) {
off += sec_sz(sec->n);
sec++;
}
}
}
}
@ -1796,9 +1832,9 @@ static uint32_t calc_start_pos(struct image *im)
im->img.decode_pos++;
if (decode_off < sec_sz(sec->n)) {
/* Data */
im->img.rd_sec_pos = decode_off / 1024;
im->img.rd_sec_pos = decode_off / CHUNK_SIZE;
im->img.decode_data_pos = im->img.rd_sec_pos;
decode_off %= 1024;
decode_off %= CHUNK_SIZE;
} else {
/* Post Data */
decode_off -= sec_sz(sec->n);
@ -1811,8 +1847,8 @@ static uint32_t calc_start_pos(struct image *im)
} else {
/* Pre-index track gap */
im->img.decode_pos = trk->nr_sectors * 4 + 1;
im->img.decode_data_pos = decode_off / 1024;
decode_off %= 1024;
im->img.decode_data_pos = decode_off / CHUNK_SIZE;
decode_off %= CHUNK_SIZE;
}
}
@ -1840,15 +1876,17 @@ static void raw_setup_track(
im->cur_ticks = im->cur_bc * im->ticks_per_cell;
im->ticks_since_flux = 0;
decode_off = calc_start_pos(im);
rd->prod = rd->cons = 0;
bc->prod = bc->cons = 0;
if (start_pos) {
decode_off = calc_start_pos(im);
image_read_track(im);
bc->cons = decode_off * 16;
*start_pos = start_ticks;
} else {
im->img.decode_pos = 0;
}
}
@ -1857,7 +1895,7 @@ static bool_t raw_open(struct image *im)
if (im->step == 0)
im->step = 1;
volume_cache_init(im->bufs.write_data.p + 1024,
volume_cache_init(im->bufs.write_data.p + CHUNK_SIZE,
im->img.heap_bottom);
/* Initialise write_bc_ticks (used by floppy_insert to set outp_hden). */
@ -1893,9 +1931,9 @@ static int raw_find_first_write_sector(
int32_t base;
base = write->start / im->ticks_per_cell; /* in data bytes */
base -= im->img.track_delay_bc;
base -= im->img.track_delay_bc / 16;
if (base < 0)
base += im->tracklen_bc;
base += im->tracklen_bc / 16;
/* Convert write offset to sector number (in rotational order). */
base -= im->img.idx_sz + im->img.idam_sz;
@ -1908,7 +1946,7 @@ static int raw_find_first_write_sector(
/* Convert rotational order to logical order. */
if (i >= trk->nr_sectors) {
printk("IMG Bad Wr.Off: %d\n", base);
log("Bad Wr.Off: %d\n", base);
return -2;
}
return *sec_map;
@ -1927,8 +1965,8 @@ static bool_t raw_write_track(struct image *im)
struct raw_sec *sec;
unsigned int i, off;
time_t t;
uint16_t crc;
uint8_t idam_r, x;
uint16_t crc = im->img.crc;
uint8_t idam_r;
/* If we are processing final data then use the end index, rounded up. */
barrier();
@ -1938,124 +1976,152 @@ static bool_t raw_write_track(struct image *im)
while ((int16_t)(p - c) > 128) {
uint32_t sc = c;
if (im->img.decode_pos == 0) {
if (im->sync == SYNC_fm) {
uint8_t x;
uint16_t sync;
if (buf[c++ & bufmask] != 0xaaaa)
continue;
sync = buf[c & bufmask];
if (mfmtobin(sync >> 1) != FM_SYNC_CLK)
continue;
x = mfmtobin(sync);
c++;
} else { /* MFM */
if (be16toh(buf[c++ & bufmask]) != 0x4489)
continue;
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
continue;
c++;
}
switch (x) {
case 0xfe: /* IDAM */
if (im->sync == SYNC_fm) {
wrbuf[0] = x;
for (i = 1; i < 7; i++)
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
idam_r = wrbuf[3];
uint16_t sync;
if (buf[c++ & bufmask] != 0xaaaa)
continue;
sync = buf[c & bufmask];
if (mfmtobin(sync >> 1) != FM_SYNC_CLK)
continue;
x = mfmtobin(sync);
c++;
} else { /* MFM */
for (i = 0; i < 3; i++)
wrbuf[i] = 0xa1;
wrbuf[i++] = x;
for (; i < 10; i++)
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
idam_r = wrbuf[6];
if (be16toh(buf[c++ & bufmask]) != 0x4489)
continue;
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
continue;
c++;
}
crc = crc16_ccitt(wrbuf, i, 0xffff);
if (crc != 0) {
printk("IMG IDAM Bad CRC: %04x, %u\n", crc, idam_r);
switch (x) {
case 0xfe: /* IDAM */
if (im->sync == SYNC_fm) {
wrbuf[0] = x;
for (i = 1; i < 7; i++)
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
idam_r = wrbuf[3];
} else { /* MFM */
for (i = 0; i < 3; i++)
wrbuf[i] = 0xa1;
wrbuf[i++] = x;
for (; i < 10; i++)
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
idam_r = wrbuf[6];
}
crc = crc16_ccitt(wrbuf, i, 0xffff);
if (crc != 0) {
log("IDAM Bad CRC: %04x, %u\n", crc, idam_r);
break;
}
/* Search by sector id for this sector's logical order. */
for (i = 0, sec = im->img.sec_info;
(i < trk->nr_sectors) && (sec->r != idam_r);
i++, sec++)
continue;
im->img.write_sector = i;
if (i >= trk->nr_sectors) {
log("IDAM Bad Sector: %02x\n", idam_r);
im->img.write_sector = -2;
}
break;
case 0xfb: /* DAM */
im->img.decode_pos = 1;
im->img.decode_data_pos = 0;
break;
}
/* Search by sector id for this sector's logical order. */
for (i = 0, sec = im->img.sec_info;
(i < trk->nr_sectors) && (sec->r != idam_r);
i++, sec++)
continue;
im->img.write_sector = i;
if (i >= trk->nr_sectors) {
printk("IMG IDAM Bad Sector: %02x\n", idam_r);
im->img.write_sector = -2;
}
break;
case 0xfb: /* DAM */ {
unsigned int nr, todo, sec_sz;
} else {
/* Data record, shy address mark */
unsigned int sec_sz;
int sec_nr = im->img.write_sector;
ASSERT(im->img.decode_pos == 1);
if (sec_nr < 0) {
if (sec_nr == -1)
if (sec_nr == -1) {
sec_nr = raw_find_first_write_sector(im, write, trk);
im->img.write_sector = sec_nr;
}
if (sec_nr < 0) {
printk("IMG DAM Unknown\n");
goto dam_out;
log("DAM Unknown\n");
goto data_complete;
}
}
sec_sz = sec_sz(im->img.sec_info[sec_nr].n);
if ((int16_t)(p - c) < (sec_sz + 2)) {
c = sc;
goto out;
}
crc = (im->sync == SYNC_fm) ? FM_DAM_CRC : MFM_DAM_CRC;
sec = &im->img.sec_info[sec_nr];
printk("Write %u[%02x]/%u... ", sec_nr, sec->r, trk->nr_sectors);
t = time_now();
if (im->img.file_sec_offsets) {
off = im->img.file_sec_offsets[sec_nr];
} else if (trk->img_bps != 0) {
off = sec_nr * trk->img_bps;
} else {
sec = im->img.sec_info;
for (i = off = 0; i < sec_nr; i++)
off += sec_sz(sec++->n);
}
F_lseek(&im->fp, im->img.trk_off + off);
off += im->img.trk_off;
off += im->img.decode_data_pos;
for (todo = sec_sz; todo != 0; todo -= nr) {
nr = min_t(unsigned int, todo, 1024);
if (im->img.decode_data_pos < sec_sz) {
unsigned int nr = sec_sz - im->img.decode_data_pos;
nr = min_t(unsigned int, nr, CHUNK_SIZE - (off & 511));
if ((int16_t)(p - c) < nr)
break;
if (!im->img.decode_data_pos) {
crc = (im->sync == SYNC_fm) ? FM_DAM_CRC : MFM_DAM_CRC;
log("Write %u[%02x]/%u...",
sec_nr, sec->r, trk->nr_sectors);
F_lseek(&im->fp, off);
}
t = time_now();
mfm_ring_to_bin(buf, bufmask, c, wrbuf, nr);
c += nr;
crc = crc16_ccitt(wrbuf, nr, crc);
process_data(im, wrbuf, nr);
F_write(&im->fp, wrbuf, nr, NULL);
printk(" %u us", time_diff(t, time_now()) / TIME_MHZ);
im->img.decode_data_pos += nr;
if (im->img.decode_data_pos < sec_sz)
printk("...");
else
printk("\n");
}
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
if (im->img.decode_data_pos < sec_sz)
continue;
if ((int16_t)(p - c) < 2)
break;
mfm_ring_to_bin(buf, bufmask, c, wrbuf, 2);
c += 2;
crc = crc16_ccitt(wrbuf, 2, crc);
if (crc != 0) {
printk("IMG Bad CRC: %04x, %u[%02x]\n",
crc, sec_nr, sec->r);
log("Bad CRC: %04x, %u[%02x]\n", crc, sec_nr, sec->r);
}
dam_out:
data_complete:
im->img.write_sector = -2;
break;
}
im->img.decode_pos = 0;
}
}
out:
im->img.crc = crc;
wr->cons = c * 16;
return flush;
}
@ -2081,7 +2147,7 @@ static void raw_dump_info(struct image *im)
im->ticks_per_cell, im->write_bc_ticks, trk->has_iam);
printk(" interleave: %u, cskew %u, hskew %u\n ",
trk->interleave, trk->cskew, trk->hskew);
printk(" file-layout: %x\n", im->img.layout);
printk(" file_layout: %x, img_bps: %u\n", im->img.layout, trk->img_bps);
for (i = 0; i < trk->nr_sectors; i++) {
struct raw_sec *sec = &im->img.sec_info[im->img.sec_map[i]];
int hd = trk->head ? trk->head-1 : im->cur_track&1;
@ -2094,12 +2160,13 @@ static void raw_dump_info(struct image *im)
static void img_fetch_data(struct image *im)
{
struct image_buf *rd = &im->bufs.read_data;
struct raw_trk *trk = im->img.trk;
uint8_t *buf = rd->p;
struct raw_sec *sec, *s;
uint8_t sec_i;
uint16_t off, len;
if ((im->img.trk->nr_sectors == 0) || (rd->prod != rd->cons))
if ((trk->nr_sectors == 0) || (rd->prod != rd->cons))
return;
sec_i = im->img.sec_map[im->img.trk_sec];
@ -2107,6 +2174,8 @@ static void img_fetch_data(struct image *im)
if (im->img.file_sec_offsets) {
off = im->img.file_sec_offsets[sec_i];
} else if (trk->img_bps != 0) {
off = sec_i * trk->img_bps;
} else {
off = 0;
for (s = im->img.sec_info; s != sec; s++)
@ -2115,15 +2184,15 @@ static void img_fetch_data(struct image *im)
len = sec_sz(sec->n);
off += im->img.rd_sec_pos * 1024;
len -= im->img.rd_sec_pos * 1024;
off += im->img.rd_sec_pos * CHUNK_SIZE;
len -= im->img.rd_sec_pos * CHUNK_SIZE;
if (len > 1024) {
len = 1024;
if (len > CHUNK_SIZE) {
len = CHUNK_SIZE;
im->img.rd_sec_pos++;
} else {
im->img.rd_sec_pos = 0;
if (++im->img.trk_sec >= im->img.trk->nr_sectors)
if (++im->img.trk_sec >= trk->nr_sectors)
im->img.trk_sec = 0;
}
@ -2142,14 +2211,14 @@ static void *align_p(void *p)
static void check_p(void *p, struct image *im)
{
uint8_t *a = p, *b = (uint8_t *)im->bufs.read_data.p;
if ((int32_t)(a-b) < 1024)
if ((int32_t)(a-b) < CHUNK_SIZE)
F_die(FR_BAD_IMAGE);
im->img.heap_bottom = p;
}
/* Initialise track/sector-info structures at the top of the heap.
* In ascending address order:
* {read,write}_data (truncated to 1024 bytes)
* {read,write}_data (truncated to CHUNK_SIZE bytes)
* ... [volume cache]
* im->img.trk_info (trk_map[] points into here)
* im->img.sec_info_base (trk_info[] + sec_map[] point into here)
@ -2416,11 +2485,11 @@ static bool_t mfm_read_track(struct image *im)
}
} else if (im->img.decode_pos == (trk->nr_sectors * 4 + 1)) {
/* Pre-index track gap */
uint16_t sz = im->img.gap_4 - im->img.decode_data_pos * 1024;
if (bc_space < min_t(unsigned int, sz, 1024))
uint16_t sz = im->img.gap_4 - im->img.decode_data_pos * CHUNK_SIZE;
if (bc_space < min_t(unsigned int, sz, CHUNK_SIZE))
return FALSE;
if (sz > 1024) {
sz = 1024;
if (sz > CHUNK_SIZE) {
sz = CHUNK_SIZE;
im->img.decode_data_pos++;
im->img.decode_pos--;
} else {
@ -2468,11 +2537,11 @@ static bool_t mfm_read_track(struct image *im)
}
case 2: /* Data */ {
uint16_t sec_sz = sec_sz(sec->n);
sec_sz -= im->img.decode_data_pos * 1024;
if (bc_space < min_t(unsigned int, sec_sz, 1024))
sec_sz -= im->img.decode_data_pos * CHUNK_SIZE;
if (bc_space < min_t(unsigned int, sec_sz, CHUNK_SIZE))
return FALSE;
if (sec_sz > 1024) {
sec_sz = 1024;
if (sec_sz > CHUNK_SIZE) {
sec_sz = CHUNK_SIZE;
im->img.decode_data_pos++;
im->img.decode_pos--;
} else {
@ -2629,11 +2698,11 @@ static bool_t fm_read_track(struct image *im)
}
} else if (im->img.decode_pos == (trk->nr_sectors * 4 + 1)) {
/* Pre-index track gap */
uint16_t sz = im->img.gap_4 - im->img.decode_data_pos * 1024;
if (bc_space < min_t(unsigned int, sz, 1024))
uint16_t sz = im->img.gap_4 - im->img.decode_data_pos * CHUNK_SIZE;
if (bc_space < min_t(unsigned int, sz, CHUNK_SIZE))
return FALSE;
if (sz > 1024) {
sz = 1024;
if (sz > CHUNK_SIZE) {
sz = CHUNK_SIZE;
im->img.decode_data_pos++;
im->img.decode_pos--;
} else {
@ -2675,11 +2744,11 @@ static bool_t fm_read_track(struct image *im)
}
case 2: /* Data */ {
uint16_t sec_sz = sec_sz(sec->n);
sec_sz -= im->img.decode_data_pos * 1024;
if (bc_space < min_t(unsigned int, sec_sz, 1024))
sec_sz -= im->img.decode_data_pos * CHUNK_SIZE;
if (bc_space < min_t(unsigned int, sec_sz, CHUNK_SIZE))
return FALSE;
if (sec_sz > 1024) {
sec_sz = 1024;
if (sec_sz > CHUNK_SIZE) {
sec_sz = CHUNK_SIZE;
im->img.decode_data_pos++;
im->img.decode_pos--;
} else {

View file

@ -33,7 +33,7 @@ static bool_t qd_open(struct image *im)
im->qd.tb = 1;
im->nr_cyls = 1;
im->nr_sides = 1;
im->write_bc_ticks = sampleclk_us(4) + 66; /* 4.917us */
im->write_bc_ticks = sampleclk_ns(4917); /* 4.917us */
im->ticks_per_cell = im->write_bc_ticks;
im->sync = SYNC_none;
@ -159,11 +159,11 @@ static uint16_t qd_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
uint32_t bc_c = bc->cons, bc_p = bc->prod, bc_mask = bc->len - 1;
uint32_t ticks = im->ticks_since_flux;
uint32_t ticks_per_cell = im->ticks_per_cell;
uint32_t y = 8, todo = nr;
uint32_t bit_off, todo = nr;
uint8_t x;
while ((uint32_t)(bc_p - bc_c) >= 8) {
ASSERT(y == 8);
if (im->cur_bc >= im->tracklen_bc) {
ASSERT(im->cur_bc == im->tracklen_bc);
im->tracklen_ticks = im->cur_ticks;
@ -172,28 +172,43 @@ static uint16_t qd_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
bc_c = (bc_c + 512*8-1) & ~(512*8-1);
continue;
}
y = bc_c % 8;
x = bc_b[(bc_c/8) & bc_mask] >> y;
bc_c += 8 - y;
im->cur_bc += 8 - y;
im->cur_ticks += (8 - y) * ticks_per_cell;
while (y < 8) {
y++;
bit_off = bc_c % 8;
x = bc_b[(bc_c/8) & bc_mask] >> bit_off;
bc_c += 8 - bit_off;
im->cur_bc += 8 - bit_off;
im->cur_ticks += (8 - bit_off) * ticks_per_cell;
while (bit_off < 8) {
bit_off++;
ticks += ticks_per_cell;
if (x & 1) {
*tbuf++ = ticks - 1;
ticks = 0;
if (!--todo)
if (!--todo) {
bc_c -= 8 - bit_off;
im->cur_bc -= 8 - bit_off;
im->cur_ticks -= (8 - bit_off) * ticks_per_cell;
goto out;
}
}
x >>= 1;
}
/* Subdivide a long flux gap to avoid overflowing the 16-bit timer.
* This mishandles long No Flux Areas slightly, by regularly emitting
* a flux-reversal pulse every 2^14 sampleclk ticks. */
if (unlikely((ticks >> 15) != 0)) {
*tbuf++ = (1u << 14) - 1;
ticks -= 1u << 14;
if (!--todo)
goto out;
}
}
out:
bc->cons = bc_c - (8 - y);
im->cur_bc -= 8 - y;
im->cur_ticks -= (8 - y) * ticks_per_cell;
bc->cons = bc_c;
im->ticks_since_flux = ticks;
return nr - todo;
}

View file

@ -1208,6 +1208,8 @@ static void read_ff_cfg(void)
ff_cfg.display_type |= DISPLAY_ztech;
} else if (!strcmp(p, "slow")) {
ff_cfg.display_type |= DISPLAY_slow;
} else if (!strcmp(p, "hflip")) {
ff_cfg.display_type |= DISPLAY_hflip;
}
}
}
@ -1278,6 +1280,25 @@ static void read_ff_cfg(void)
break;
}
case FFCFG_notify_volume: {
char *p, *q;
ff_cfg.notify_volume = 0;
for (p = opts.arg; *p != '\0'; p = q) {
for (q = p; *q && *q != ','; q++)
continue;
if (*q == ',')
*q++ = '\0';
if (!strcmp(p, "slotnr")) {
ff_cfg.notify_volume |= NOTIFY_slotnr;
} else {
ff_cfg.notify_volume &= ~NOTIFY_volume_mask;
ff_cfg.notify_volume |= strtol(p, NULL, 10)
& NOTIFY_volume_mask;
}
}
break;
}
case FFCFG_da_report_version:
memset(ff_cfg.da_report_version, 0,
sizeof(ff_cfg.da_report_version));
@ -2687,8 +2708,10 @@ static int floppy_main(void *unused)
b = B_SELECT;
} else {
floppy_arena_teardown();
speaker_notify_insert(cfg.slot_nr);
fres = F_call_cancellable(run_floppy, &b);
floppy_cancel();
speaker_notify_eject();
assert_volume_connected();
if ((b != 0) && (display_type == DT_LCD_OLED)) {
/* Immediate visual indication of button press. */
@ -2817,6 +2840,24 @@ static bool_t main_menu_confirm(const char *op)
return wait_twobutton_press(b) == B_SELECT;
}
static void noinline mcu_info(void)
{
#if MCU == STM32F105
static const char * const mcus[] = {
"STM32F105", "AT32F415"
};
const char * const mcu = mcus[!!is_artery_mcu];
#elif MCU == AT32F435
const static char mcu[] = "AT32F435";
#endif
char msg[20];
snprintf(msg, sizeof(msg), "%uMHz, %ukB", SYSCLK_MHZ, ram_kb);
lcd_write(0, 0, -1, mcu);
lcd_write(0, 1, -1, msg);
while (!buttons)
continue;
}
static void factory_reset(void)
{
/* Inform user that factory reset is about to occur. */
@ -2901,7 +2942,7 @@ static void ff_osd_configure(void)
static void main_menu(void)
{
const static char *menu[] = {
"**Main Menu**",
"MCU Info",
"Factory Reset",
"Update Firmware",
"Configure FF OSD",
@ -2924,6 +2965,7 @@ static void main_menu(void)
if (sel >= ARRAY_SIZE(menu))
sel -= ARRAY_SIZE(menu);
lcd_write(0, 0, -1, "**Main Menu**");
lcd_write(0, 1, -1, menu[sel]);
lcd_on();
@ -2946,6 +2988,9 @@ static void main_menu(void)
while (buttons)
continue;
switch (sel) {
case 0: /* MCU Info */
mcu_info();
break;
case 1: /* Factory Reset */
if (main_menu_confirm("Reset"))
factory_reset();
@ -2957,7 +3002,7 @@ static void main_menu(void)
case 3: /* Configure FF OSD */
ff_osd_configure();
break;
case 0: case 4: /* Exit */
case 4: /* Exit */
goto out;
}
}
@ -3057,6 +3102,12 @@ static void maybe_show_version(void)
if (nb)
return;
led_7seg_write_string("CPU");
delay_ms(1000);
led_7seg_write_decimal(SYSCLK_MHZ);
delay_ms(1000);
led_7seg_write_string("RAN");
delay_ms(1000);

View file

@ -9,6 +9,9 @@
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
unsigned int sysclk_mhz = 72;
unsigned int apb1_mhz = 36;
bool_t is_artery_mcu;
unsigned int flash_page_size = FLASH_PAGE_SIZE;
unsigned int ram_kb = 64;
@ -32,13 +35,15 @@ static void identify_mcu(void)
ram_kb = 32;
if (flash_kb == 128)
flash_page_size = 1024;
sysclk_mhz = 144;
apb1_mhz = 72;
}
}
static void clock_init(void)
{
/* Flash controller: reads require 2 wait states at 72MHz. */
flash->acr = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY(2);
flash->acr = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY(sysclk_mhz/32);
/* Start up the external oscillator. */
rcc->cr |= RCC_CR_HSEON;
@ -46,21 +51,40 @@ static void clock_init(void)
cpu_relax();
/* PLLs, scalers, muxes. */
rcc->cfgr = (RCC_CFGR_PLLMUL(9) | /* PLL = 9*8MHz = 72MHz */
RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_ADCPRE_DIV8 |
RCC_CFGR_PPRE1_DIV2);
if (is_artery_mcu) {
uint32_t rcc_pll = *RCC_PLL;
rcc_pll &= ~(RCC_PLL_PLLCFGEN | RCC_PLL_FREF_MASK);
rcc_pll |= RCC_PLL_FREF_8M;
*RCC_PLL = rcc_pll;
rcc->cfgr = (RCC_CFGR_PLLMUL_18 | /* PLL = 18*8MHz = 144MHz */
RCC_CFGR_USBPSC_3 | /* USB = SYSCLK/3 = 48MHz */
RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_ADCPRE_DIV8 |
RCC_CFGR_APB2PSC_2 | /* APB2 = SYSCLK/2 = 72MHz */
RCC_CFGR_APB1PSC_2); /* APB1 = SYSCLK/2 = 72MHz */
} else {
rcc->cfgr = (RCC_CFGR_PLLMUL(9) | /* PLL = 9*8MHz = 72MHz */
RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_ADCPRE_DIV8 |
RCC_CFGR_APB1PSC_2); /* APB1 = SYSCLK/2 = 36MHz */
}
/* Enable and stabilise the PLL. */
rcc->cr |= RCC_CR_PLLON;
while (!(rcc->cr & RCC_CR_PLLRDY))
cpu_relax();
if (is_artery_mcu)
*RCC_MISC2 |= RCC_MISC2_AUTOSTEP_EN;
/* Switch to the externally-driven PLL for system clock. */
rcc->cfgr |= RCC_CFGR_SW_PLL;
while ((rcc->cfgr & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_PLL)
cpu_relax();
if (is_artery_mcu)
*RCC_MISC2 &= ~RCC_MISC2_AUTOSTEP_EN;
/* Internal oscillator no longer needed. */
rcc->cr &= ~RCC_CR_HSION;
}

View file

@ -9,15 +9,9 @@
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
#if MCU == STM32F105
/* Default Speed (25MHz). Closest we can get is 36Mhz/2 = 18MHz. */
#define INIT_SPEED_DIV SPI_BR_DIV128 /* ~281kHz (<400kHz) */
#define DEFAULT_SPEED_DIV SPI_BR_DIV2 /* 18MHz */
#elif MCU == AT32F435
/* Default Speed (25MHz). Closest we can get is 144Mhz/8 = 18MHz. */
#define INIT_SPEED_DIV SPI_BR_DIV512 /* ~281kHz (<400kHz) */
#define DEFAULT_SPEED_DIV SPI_BR_DIV8 /* 18MHz */
#endif
#define PCLK_MHZ APB1_MHZ
#define INIT_SPEED_KHZ 400
#define DEFAULT_SPEED_KHZ 25000
#define SPI_PIN_SPEED _50MHz
#if 0
@ -270,8 +264,16 @@ static bool_t sd_inserted(void)
return gpio_read_pin(gpioc, 9);
}
static void sd_spi_set_cr(uint32_t cr1, uint32_t br)
static void sd_spi_set_cr(uint32_t cr1, unsigned int max_khz)
{
/* Set the divisor to satisfy maximum communications frequency. */
uint32_t br = SPI_BR_DIV2;
unsigned int khz = (PCLK_MHZ * 1000) >> 1;
while (khz > max_khz) {
khz >>= 1;
br++;
}
/* BR is called MDIV in AT32F435 documentation, and is extended by one
* bit which resides in CR2. */
spi->cr2 = (br & 8) << (8 - 3); /* CR2[8] := MDIV[3] */
@ -314,7 +316,7 @@ static DSTATUS sd_disk_initialize(BYTE pdrv)
cr1 = (SPI_CR1_MSTR | /* master */
SPI_CR1_SSM | SPI_CR1_SSI | /* software NSS */
SPI_CR1_SPE);
sd_spi_set_cr(cr1, INIT_SPEED_DIV);
sd_spi_set_cr(cr1, INIT_SPEED_KHZ);
/* Drain SPI I/O. */
spi_quiesce(spi);
@ -396,7 +398,7 @@ out:
if (!(status & STA_NOINIT)) {
delay_us(10); /* XXX small delay here stops SPI getting stuck?? */
sd_spi_set_cr(cr1, DEFAULT_SPEED_DIV);
sd_spi_set_cr(cr1, DEFAULT_SPEED_KHZ);
printk("SD Card configured\n");
dump_cid_info();
} else {

View file

@ -12,9 +12,9 @@
static volatile time_t time_stamp;
static struct timer time_stamp_timer;
/* Timestamp needs to be updated ever 2^24 systicks. We aim to update
/* Hardware systick timer overflows every 2^24 ticks. We aim to update
* the timestamp at twice that rate (2^23 systicks). */
#define TIME_UPDATE_MS ((1u<<23)/(STK_MHZ*1000))
#define TIME_UPDATE_PERIOD time_stk(1u<<23)
void delay_from(time_t t, unsigned int ticks)
{
@ -27,7 +27,7 @@ static void time_stamp_update(void *unused)
{
time_t now = time_now();
time_stamp = ~now;
timer_set(&time_stamp_timer, now + time_ms(TIME_UPDATE_MS));
timer_set(&time_stamp_timer, now + TIME_UPDATE_PERIOD);
}
time_t time_now(void)
@ -45,7 +45,7 @@ void time_init(void)
timers_init();
time_stamp = stk_now();
timer_init(&time_stamp_timer, time_stamp_update, NULL);
timer_set(&time_stamp_timer, time_now() + time_ms(TIME_UPDATE_MS));
timer_set(&time_stamp_timer, time_now() + TIME_UPDATE_PERIOD);
}

View file

@ -47,7 +47,7 @@ static void reprogram_timer(int32_t delta)
* fine-grained deadline. */
tim->psc = sysclk_us(100)-1;
tim->arr = min_t(uint32_t, 0xffffu,
delta/time_us(100)-50); /* 5ms early */
delta/time_us(100)-10); /* 1ms early */
}
tim->egr = TIM_EGR_UG; /* update CNT, PSC, ARR */
tim->sr = 0; /* dummy write, gives hardware time to process EGR.UG=1 */

View file

@ -17,41 +17,47 @@ static bool_t msc_device_connected;
extern USB_OTG_CORE_HANDLE USB_OTG_Core;
USBH_HOST USB_Host;
#if 1
#define TRC_FUNC() printk("> %s\n", __FUNCTION__)
#else
#define TRC_FUNC() ((void)0)
#endif
static void USBH_USR_Init(void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
}
static void USBH_USR_DeInit(void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
msc_device_connected = FALSE;
}
static void USBH_USR_DeviceAttached(void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
}
static void USBH_USR_ResetDevice(void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
}
static void USBH_USR_DeviceDisconnected (void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
msc_device_connected = FALSE;
}
static void USBH_USR_OverCurrentDetected (void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
}
static void USBH_USR_DeviceSpeedDetected(uint8_t DeviceSpeed)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
printk("> Device speed: %s\n",
(DeviceSpeed == HPRT0_PRTSPD_HIGH_SPEED) ? "High" :
(DeviceSpeed == HPRT0_PRTSPD_FULL_SPEED) ? "Full" :
@ -61,14 +67,14 @@ static void USBH_USR_DeviceSpeedDetected(uint8_t DeviceSpeed)
static void USBH_USR_DeviceDescAvailable(void *DeviceDesc)
{
USBH_DevDesc_TypeDef *hs = DeviceDesc;
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
printk(" VID : %04X\n", hs->idVendor);
printk(" PID : %04X\n", hs->idProduct);
}
static void USBH_USR_DeviceAddressAssigned(void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
}
static void USBH_USR_ConfigurationDescAvailable(
@ -78,7 +84,7 @@ static void USBH_USR_ConfigurationDescAvailable(
{
USBH_InterfaceDesc_TypeDef *id = itfDesc;
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
printk("> Class connected: %02x (%s)\n",
id->bInterfaceClass,
(id->bInterfaceClass == 0x08) ? "MSC" :
@ -102,12 +108,12 @@ static void USBH_USR_SerialNumString(void *SerialNumString)
static void USBH_USR_EnumerationDone(void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
}
static USBH_USR_Status USBH_USR_UserInput(void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
return USBH_USR_RESP_OK;
}
@ -120,12 +126,12 @@ static int USBH_USR_UserApplication(void)
static void USBH_USR_DeviceNotSupported(void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
}
static void USBH_USR_UnrecoveredError (void)
{
printk("> %s\n", __FUNCTION__);
TRC_FUNC();
msc_device_connected = FALSE;
}

View file

@ -335,6 +335,18 @@ unsigned int popcount(uint32_t x)
return (((x + (x >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24;
}
/* 64:32->32q division requiring 32:32->64 multiply. Cortex M3+ */
uint32_t udiv64(uint64_t dividend, uint32_t divisor)
{
uint32_t x, q = 0;
for (x = 1u<<31; x != 0; x >>= 1) {
if (((uint64_t)(q|x)*divisor) <= dividend)
q |= x;
}
return q;
}
/*
* Local variables:
* mode: C