Compare commits

...

178 commits

Author SHA1 Message Date
Eric Anderson
cc8941dbfc Re-disable debug builds for stm32f105
GCC 13.1.0 produces smaller code than what's on gh-actions. To take
stm32f105/debug/floppy as an example (- overflow/+ headroom):

GCC  9 on Ubuntu 20.04:         -960 bytes
GCC 10 on Ubuntu 22.04:         -712 bytes
GCC 10 on Ubuntu 22.04 (-flto): -116 bytes
GCC 13 on Arch:                 +132 bytes
GCC 13 on Arch (-flto):         +824 bytes

Don't fight it for now. At least debug builds are still easyish to make
locally.
2023-07-08 20:36:26 -07:00
Eric Anderson
d285a7235b Share debug strings between IMG and DSK 2023-07-08 19:05:29 -07:00
Eric Anderson
717bc47fd8 Replace ring_io with file_cache
This allows using the extra memory available on at32f435 which was not
possible with ring_io. It also has two side-goals of reducing the
diff and I/O behavior in image handlers compared to stable-v3 and
reducing overall code size.

While readahead was a core principal of ring_io, it is more of a bolt-on
for file_cache; file_cache focuses more on batched reads and writes.
File_cache tries to look more like the normal F_read, F_write, and
volume_cache with the main difference being that operations cannot span
sectors. file_cache_{read,write} could easily support multi-sector ops,
but no callers need such a thing as multi-sector reads/writes were for
batching.

file_cache still supports the "write directly to the cache" behavior
available in ring_io, but HFE and QD are the only images that make use
of it. The other images use a write buffer to avoid the read-then-write
that was required with ring_io. Those other images now do not do
reading-during-writing when their sector sizes are 512 or larger, which
matches stable-v3 behavior.

Since file_cache doesn't require read-before-write, the readahead can be
higher or lower priority than writes.
2023-07-08 19:05:25 -07:00
Eric Anderson
eec23727ea da: Combine read and write op
This causes write op scheduling to wait on any read op. Also fixed
write_offsets overlapping write_buffer.p and alignment.
2023-07-03 11:04:34 -07:00
Eric Anderson
044d714b23 Remove sync image write_bc sizing
All images are async
2023-07-03 11:03:55 -07:00
Keir Fraser
074760853b Makefile: Fix the flash-to-device helper rules 2023-02-08 11:03:30 +00:00
Keir Fraser
72594dbc88 README.md: Banner picture 2023-02-05 16:40:14 +00:00
Keir Fraser
28d1088b45 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 09:03:21 +00:00
Keir Fraser
592cdef2fd mk_qd.py: Allow QD image lead-in to be configured on command line 2023-01-31 11:20:00 +00:00
Keir Fraser
a08b4e0590 IMG.CFG: Fix scoping of per-track sector sizes.
Refs #751
2023-01-31 11:20:00 +00:00
Keir Fraser
0baf125f45 FF.CFG: New option notify-volume= for notifying on insert/eject via speaker.
Refs #716
2023-01-17 16:01:44 +00:00
Keir Fraser
026b1dab0a 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:44:25 +00:00
Keir Fraser
d3bc831400 Main Menu: New option "MCU Info" 2023-01-15 14:00:04 +00:00
Keir Fraser
d2f1f65843 AT32F415: Run at 144MHz 2023-01-15 13:24:04 +00:00
Keir Fraser
2288a4b78a 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:57:27 +00:00
Keir Fraser
feb0b883da IMG.CFG: Add files for Ensoniq and Sequential Circuits hosts 2022-12-20 08:24:46 +00:00
Keir Fraser
fea12a55f4 IMG.CFG: Allow sector size to be specified per sector. 2022-12-20 08:11:19 +00:00
Keir Fraser
912f7ef364 FF.CFG, OLED: New display-type sub-option "hflip". 2022-12-20 08:11:15 +00:00
Keir Fraser
4b60840314 IMG.CFG: Dynacord ADS 2022-12-18 21:50:36 +00:00
Keir Fraser
7e79be8a96 Roland: Update IMG.CFG for 1.44MB sector skew observed on MT-200. 2022-12-18 21:50:36 +00:00
Keir Fraser
7a9831d78e Update to v4.7a 2022-12-04 14:19:09 +00:00
Keir Fraser
ac92c03adc AT32F435: Fix RDATA pin deassert when drive is deselected.
This was fixed for 105/415 Goteks long ago in bb7f7f0128, however
the layout of a GPIO config word is different on 435 and the O_FALSE
level specifier was at the incorrect bit offset.
2022-12-04 14:15:00 +00:00
Keir Fraser
e604563923 github: Rewrite release creation rules 2022-11-07 08:24:45 +00:00
Keir Fraser
3edd6373ed stm32f105: Temporarily eliminate logfile firmware to fit in Flash 2022-11-06 19:46:24 +00:00
Keir Fraser
ed01de5aa4 SF7: Support Sega SF-7000 *.SF7 images 2022-11-06 19:33:38 +00:00
Keir Fraser
55aa1f1984 github: Use latest versions of checkout and upload-artifact actions 2022-11-06 19:33:38 +00:00
Keir Fraser
89a245a78c github: Rewrite deprecated set-output commands 2022-11-06 19:33:38 +00:00
Keir Fraser
9de359a072 Examples: Fix Osborne IMG.CFG to use cskew instead of skew. 2022-10-20 14:13:48 +01:00
Keir Fraser
ffc26a57b8 amiga: Motor signal must be respected even while a disk is ejected.
Since the motor signal selects between ID and RDY signals on pin 34.

Refs #354
2022-10-20 11:35:26 +01:00
Keir Fraser
24e5d5550d Fix IRQs being incorrectly left disabled on exit from update_amiga_id().
Symptom: Gotek would hang when mounting a read-only image

Fixes #701
2022-10-20 11:35:26 +01:00
Keir Fraser
47e3b55168 AT32F435: Fix SD support over SPI. 2022-10-10 07:47:41 +01:00
Keir Fraser
bcdf9fc9f5 FF.CFG: New option osd-columns= to set number of OSD text columns. 2022-10-09 17:20:31 +01:00
Keir Fraser
0c15d01028 Improve code comments in update_amiga_id(). 2022-10-09 12:34:59 +01:00
Keir Fraser
da026808dd New FF.CFG option osd-display-order=, analagous to LCD/OLED display-order= 2022-10-09 12:03:16 +01:00
Keir Fraser
18f1a27673 amiga: Synchronize amiga pin 34 to /MTR when motor-delay is configured.
Output ungated open-collector /DD density signal on JC.
Refs #354
2022-10-09 12:03:12 +01:00
Keir Fraser
ab1f656dac QD: Fix debug logging of interface mode (Roland vs not-Roland).
Refs #583
2022-10-09 11:59:20 +01:00
Keir Fraser
752844b7f3 AT32F435: Fix QuickDisk window calculation.
The relationship between STK and SAMPLECLK must be included as a factor.
2022-08-02 17:46:28 +01:00
Keir Fraser
086b549dcf AT32F435: Reduce sample clock to 72MHz, same as 105/415 build.
It provides plkenty high enough resolution, while reducing risk of
unexpected tick overflows.
2022-08-02 11:28:39 +01:00
Keir Fraser
0ca4890d73 Update to v4.6a 2022-07-29 15:16:45 +01:00
Keir Fraser
8561197372 direct access: Clear the write-protect line when entering direct-access mode
Otherwise host command writes are ignored.

Fixes #596
2022-07-29 15:13:50 +01:00
Keir Fraser
2631e0b2cb Update to v4.5a 2022-07-29 09:27:19 +01:00
Keir Fraser
f9e8023327 Rename hex and dfu files to flashfloppy-at415-st105-*
This places the now more common Artery chip more prominently, and
contrasts better with flashfloppy-at435-*
2022-07-28 09:36:22 +01:00
Keir Fraser
030d66ea9c build: VPATH is not needed. 2022-07-10 10:33:21 +01:00
Eric Anderson
3db5e96e76 Fix GCC 12 infinite loop in strlen
Strlen was jumping to strlen as part of -ftree-loop-distribute-patterns.
Seems this sort of issue can be common and generally fixed with
-fno-builtin. However, some of those optimizations may be useful and I
don't think our semantics for the functions are wildly different than
the standard, so I'm fixing this surgically with
-ftree-loop-distribute-patterns to just util.c. That may be the wrong
call, so if this bites us again, let's go more aggressive. Fixes #660.

Below are some filesizes for stm32f105/prod/floppy/target.bin with
different compilers/branches that I trialed to get an idea of the impact
of these optimizations.

stable-v3:
GCC 11: 83180 baseline
GCC 11: 83180 util.c no-tree-loop-distribute-pattern
GCC 11: 83180 util.c no-builtin
GCC 11: 83200 all    no-tree-loop-distribute-pattern
GCC 11: 83336 all    no-builtin

GCC 12: 83500 baseline (broken)
GCC 12: 83512 util.c no-tree-loop-distribute-pattern
GCC 12: 83512 util.c no-builtin
GCC 12: 83524 all    no-tree-loop-distribute-pattern
GCC 12: 83544 all    no-builtin

master:
GCC 12: 89996 baseline (broken)
GCC 12: 90008 util.c no-tree-loop-distribute-pattern
GCC 12: 90020 util.c no-builtin
GCC 12: 90020 all    no-tree-loop-distribute-patterns
GCC 12: 90064 all    no-builtin
2022-07-09 19:49:16 -07:00
Eric Anderson
eea5041696 Makefile: Use VPATH to avoid rsync
Also add + to mkdir lines to fix --dry-run.
2022-07-09 15:01:18 -07:00
Keir Fraser
eb7afca29d AT32F435: Fix LDO boost (to 1.3v) and Flash divider before configuring 288MHz SYSCLK. 2022-07-04 13:04:28 +01:00
Keir Fraser
c5204442ae board_get_buttons: Remove extraneous code comment 2022-07-04 08:37:45 +01:00
Keir Fraser
1bae3e5ad8 io_test: Fix for newer board versions 2022-07-04 08:28:12 +01:00
Keir Fraser
66581698f7 board_get_buttons: Fix order of Left & Right buttons. 2022-07-04 08:28:10 +01:00
Keir Fraser
b23021407e Replace is_*pin_mcu booleans with an mcu_package enumeration. 2022-06-28 10:26:46 +01:00
Keir Fraser
1970a01cc9 Allow rotary encoder at PA13/PA14 on any LQFP48 or LQFP64 MCU 2022-06-28 10:26:44 +01:00
Keir Fraser
c2885b87ba stm32f105: Nobble the debug build. It doesn't otherwise fit in its Flash allocation.
Refs #660
2022-06-24 12:19:28 +01:00
Keir Fraser
4f96058924 build: Check that output Hex files fit within their assigned Flash space.
Refs #660
2022-06-24 12:18:10 +01:00
Keir Fraser
a4314235c3 GCC12, FatFs: Avoid uninitialised assignment error in f_open().
Refs #660
2022-06-23 10:11:37 +01:00
Keir Fraser
6c0c75f532 GCC12: Convert array comparisons to pointer-to-first-element comparisons.
These are equivalent, but the former is deprecated in C++20 and warned by GCC12.
Refs #660
2022-06-23 08:15:06 +01:00
Keir Fraser
a3b8867f8a Shorten debug banners. Squeezed for space on 105 debug build. 2022-06-17 17:33:08 +01:00
Keir Fraser
5449d569d1 Rename the HEX and DFU dist files for clarity. 2022-06-17 17:21:16 +01:00
Keir Fraser
ed2d0a19e1 FlashFloppy+: Improve banner text and 7-seg LED info text. 2022-06-17 17:21:08 +01:00
Keir Fraser
70a4da5195 AT32F435: Support the new SFRKC30.AT4.35 board
1. AT32F415 pin PF7 is PH2 on AT32F435
2. AT32F435 must set is_artery_mcu
2022-06-06 21:59:24 +01:00
Keir Fraser
6316dc919e Support new Gotek board SFRKC30.AT4.35
* Identifier: PC15 is tied to VDD
 * MOTOR is strappable to PB12 with 1k PU to +5v
 * First SFRKC30 board to support AT32F435
2022-06-06 21:59:17 +01:00
Keir Fraser
1f16192233 AT32F435: Fix speaker volume.
The pulse width is calculated as an absolute value. For consistency
across supported chips it needs to include a TIME_MHZ factor.
2022-06-06 21:59:09 +01:00
Keir Fraser
07ff1d128d Don't inline _IRQ_SELA_changed into Amiga_HD_ID.
It's unnecessary and costs ~100 bytes RAM.
2022-06-06 21:59:02 +01:00
Keir Fraser
8716314389 Optimise the drive-select ISR. It was too slow on a Vampire-accelerated Amiga.
1. Optimise the ISR functions at level -Ofast
2. Restructuire the main C routine a little
3. Provide a faster methoid for detecting dma_rd->state == DMA_active

Refs #650
2022-05-27 17:25:37 +01:00
Keir Fraser
dabcc7dd76 Reintroduce the 8x16 font, build of which was broken by AT32F435 patches. 2022-05-26 08:54:15 +01:00
Keir Fraser
0c8b911ebd GitHub release: Better release name 2022-05-23 13:19:47 +01:00
Keir Fraser
4104883bc0 Direct Access: Sanity check raw data writes to the Flash drive.
1. No writes until the LBA base is explicitly set
2. No writes outside the mounted FAT volume
3. No writes to the mounted volume boot/bpb sector
Refs #610
2022-05-23 12:55:31 +01:00
Keir Fraser
620023061d oled: New display-type option '-slow' to run I2C bus slower.
Refs #640
2022-05-06 17:10:38 +01:00
Keir Fraser
8a0cc97055 QD: Workaround for missing JC strap on SFRKC30.AT2
Refs #583
2022-05-04 10:31:58 +01:00
Keir Fraser
2280bbc135 at32f435: Extend NVIC registers to include F435's extra IRQ lines.
Fixes build error on GCC 10.
2022-04-26 12:00:37 +01:00
Keir Fraser
756eca230c dist: Include a plain-text README file. 2022-04-22 15:03:23 +01:00
Keir Fraser
1cc05cdd41 Default version is git commit hash. Use VER consistently throughout firmware. 2022-04-14 15:14:37 +01:00
Keir Fraser
0f6fc95feb Remove 'v' prefix from version number usage. 2022-04-14 14:54:20 +01:00
Keir Fraser
bb9bf6ae72 New universal UPD file format, includes firmware for both STM32F105 and AT32F435. 2022-04-14 14:20:12 +01:00
Keir Fraser
79606e074e Makefile: Get rid of dodgy use of $eval within rules. 2022-03-15 13:18:59 +00:00
Keir Fraser
de42323e96 Small fixes to new build system 2022-03-15 12:43:59 +00:00
Keir Fraser
7572434201 Makefile: Sync stm32flash build rules with stable-v3 2022-03-15 11:24:36 +00:00
Keir Fraser
c7b7610be9 Drive-select ISR:
1. Entry point must be 4-byte aligned.
2. Fix Amiga_HD_ID handler for AT32F435.
3. Slight optimisation to main C handler.
2022-03-15 08:37:54 +00:00
Keir Fraser
e41384d093 build: Clean up RPATH into new build variable SRCDIR. 2022-03-13 16:01:34 +00:00
Keir Fraser
d8b9dafa44 Fix mis-merge of f0edf11072 2022-03-13 15:40:00 +00:00
Keir Fraser
9ddb1987c2 Modify build system to place artefacts in separate out/ folder 2022-03-13 15:37:04 +00:00
Keir Fraser
72854aa7e8 AT32F435: Initial port 2022-03-13 15:31:21 +00:00
Keir Fraser
317bc9b4af Fix parallel make 2022-02-19 17:41:08 +00:00
Keir Fraser
1de6c27e95 debug: Cut USBH trace verbosity to fix the build (firmware binary too large) 2022-01-26 08:29:28 +00:00
Keir Fraser
70208c8690 KC30: New rotary pin header can be interrupt driven unless motor-delay= is specified. 2022-01-26 08:16:02 +00:00
Keir Fraser
a2d21e90ae Clean EXTI hand-off between rotary-encoder logic and MOTOR/CHGRST 2022-01-26 08:15:40 +00:00
Keir Fraser
a71f01be76 KC30AT2: WGATE pin (PB1) should not be pulled up inside the MCU. 2022-01-26 08:15:32 +00:00
Keir Fraser
ffb3f495e4 Fix RELEASE_NOTES typo 2022-01-11 10:55:37 +00:00
Keir Fraser
c7bfa47438 IMG.CFG, FF.CFG: Fix typos in new option comments. 2022-01-11 10:55:09 +00:00
Keir Fraser
c70708edc3 Update to v4.4a 2022-01-11 10:44:16 +00:00
Keir Fraser
f10f52ded8 FF.CFG: New option max-cyl=N
Refs #441
2022-01-11 10:41:31 +00:00
Keir Fraser
1eee35d0c8 IMG.CFG: New option step= to allow specifying single- vs double-step.
Refs #441
2022-01-11 10:41:10 +00:00
Keir Fraser
89aa3e21ad Repository renaming / lower-casing:
HxC_FF_File_Selector -> flashfloppy-hxc-file-selector
2021-12-23 11:03:30 +00:00
Keir Fraser
8f55f5b908 Repo names renamed to all lower case 2021-12-22 16:21:44 +00:00
Eric Anderson
9956cb22bb dsk: Shut down ring_io before direct read
Prevent accidentally reading from the wrong part of the file, if a
reader assumes the file pos hasn't changed. Both ring_io and dsk seek
before doing a read, so this was "okay" already. But there's really
nothing to gain here by having the two modules reading from the same
file simultaneously and file pos confusion could be ugly to debug.
2021-12-18 12:17:46 -06:00
Eric Anderson
26bbe822db dsk: Don't update state when waiting for sector read
Otherwise the wrong sector contents is used.

Fixes #586
2021-12-18 12:08:10 -06:00
Keir Fraser
2e079dd556 Update to v4.3a 2021-12-14 08:20:34 +00:00
Eric Anderson
caeff9a3cb Makefile: Make sudo optional for tty access
Being in the proper group is sometimes enough to avoid needing sudo to
access tty devices. If not done by the distro, a udev rule isn't too
hard to tweak to get convenient permissions on the device (using lsusb
to fill in XXXX and YYYY):
SUBSYSTEMS=="usb", ATTRS{idVendor}=="XXXX", ATTRS{idProduct}=="YYYY", GROUP="uucp"
2021-11-20 14:45:24 -08:00
Keir Fraser
fe15fc3b9a Rename the functions-in-RAM data section to ".ramfuncs". Explicitly
include this renamed section in the linker script since it no longer
falls under the ".data*" wildcard.

This avoids "setting incorrect section attributes for .data.ramfunc"
which was previously worked around by removing(!) the executable
attribute. This caused far bigger problems with binutils >= 2.36

Refs #563
2021-11-05 15:35:11 +00:00
Eric Anderson
17f2248675 First-guess image handlers as async
Image handlers may be loaded twice if the initial async value doesn't
match the actual image handler's async value. Since all the handlers are
now async, default to async and avoid the double-load.

Also now require sync method, since all handlers now implement it.
2021-10-18 21:11:15 -07:00
Eric Anderson
f8b4fe09e2 dummy: Enable async 2021-10-18 21:10:31 -07:00
Eric Anderson
d36bde69fc qd: Use ringio for buffering 2021-10-17 20:32:52 -07:00
Eric Anderson
1eeb28fa49 Fix hard sector fallout for quickdisk
991a7747 accidentally broke quickdisk, as quickdisk doesn't use the
timers involved. Just exclude the code that isn't needed for quickdisk.
2021-10-17 20:31:18 -07:00
Eric Anderson
724f005094 hfe: Handle wraparound during writing
Seems this was accidentally lost with the conversion to async. This
probably doesn't matter much, but for v3 the index pulse could be wildly
misaligned and then this could become noticable.

The lack of wraparound was observable when using a logic analyzer doing
some North Star testing, but it didn't cause issues because it just lost
the tail end of the hard sector which contains no data. I only just
realized that the wraparound logic was previously present.
2021-10-16 16:10:28 -07:00
Eric Anderson
e2fd4f9a76 dsk,img: Avoid completing writes when they overrun reads
If reading from flash is delayed, WDATA may overtake it and require
pausing WDATA processing. Previously if this happened at the end of the
write, the tail of the write would be lost. This seems unlikely, except
maybe short writes very soon after changing tracks. I was not able to
trigger this case.

ADF was already mostly correct, excepting the sec_idx!=nr_secs check.
But clearing the flag is clearer and mirrors other image formats.
2021-10-16 15:43:53 -07:00
Keir Fraser
0943eb2fdb Update to v4.2a 2021-10-13 08:56:01 +01:00
Eric Anderson
0d5298934c img,dsk: Workaround IRQ_write_dma rewriting the queue for SYNC
Code like adf is leveraging IRQ_write_dma dropping sync bytes so
aligning to 16 bits would be noticed. IRQ_write_dma could delay prod by
32 bits but it'd be overly complex. Accept that consumers have intimate
knowledge of the sync behavior as it is essentially the case already and
is a much simpler solution.
2021-10-13 00:25:21 -07:00
Eric Anderson
709d77dfd9 dsk: Stream writes to flash
This restores larger write support, given the small size of write_bc.
2021-10-13 00:25:21 -07:00
Eric Anderson
c72aa5890a dsk: Use ringio for buffering
Writes for large sectors are broken, as they full write cannot fit in
the bc buffer.
2021-10-13 00:25:21 -07:00
Keir Fraser
701ee97629 README: Remove Donations section. Donations are no longer solicited. 2021-10-10 16:56:35 +01:00
Eric Anderson
d5130f4e24 ring_io: F_sync only after ring_io_flush
There's little point to syncing before all the data has been written.

On the surface, ring_io_sync may seem a better time to set
sync_requested=TRUE. However, that function is more about blocking
waiting for writes to drain than generating I/O to be done. Also,
ring_io_sync is called when changing tracks, which means we would have
delayed a write I/O, potentially a full rotation if the host verified
the write, until the moment that we need to do reads (for the next
track). This does mean we may still do some "unnecessary" syncs, but no
more than would have been done for a sync track_handler.
2021-10-09 14:09:04 -07:00
Eric Anderson
daba5056b7 ring_io: Avoid F_sync loop
Having a partial block awaiting flush not should trigger F_sync because
the dirty bit for the block hasn't been set, so a sync is senseless.
Previously, each call to ring_io_progress could have triggered an
F_sync, even if no F_writes had been done.
2021-10-09 13:56:06 -07:00
Eric Anderson
45a0a5d27f ring_io: Always set sync_needed after setting a dirty bit
This prevents losing the last write of a track when the write+sync of
the second-to-last block completes before the final flush is issued.
2021-10-09 13:13:00 -07:00
Keir Fraser
bd1bcc586f Update to v4.1a 2021-10-02 11:54:31 +01:00
Eric Anderson
b9846a9d6e hfe: Protect HFEv3 against a broken run of 1s 2021-09-26 22:30:43 -07:00
Keir Fraser
ad529d89e9 Remove accidental debug logging change. 2021-09-22 13:01:39 +01:00
Keir Fraser
730ffa5727 QFN32: JC will in future be connected at pin PA9. Implement this. 2021-09-22 11:58:44 +01:00
Keir Fraser
f0edf11072 QFN32: Implement support for pin mapping of new board SFRKC30.AT2 using AT32F415 QFN32 package. 2021-09-22 11:58:40 +01:00
Eric Anderson
0669a4ca26 ringio: Do not lose write for unaligned wd_prod
wd_prod and rd->cons might not be aligned to block boundaries, so
flushing may need to mark two blocks as dirty.
2021-09-05 15:05:22 -07:00
Eric Anderson
68a9a676df Do not use index_suppressed if starting rd DMA is slow
Seeking to a new track may be slow because of writes that are draining
(due to async I/O). And filling dma_rd may be slow simply because of a
random slow read from flash. When index_suppressed == FALSE, it is be
better to further delay RDATA than to change the index timing.
2021-08-29 21:00:44 -07:00
Eric Anderson
767768039e mk_hfe.py: Add hard sector support using HFEv3
The 16-byte boundary's purpose was unclear to me, but it seems
unnecessary with HFEv3.
2021-08-14 17:12:27 -07:00
Keir Fraser
9f850602f3 Update to v4.0a 2021-08-10 14:27:17 +01:00
Keir Fraser
95b3c0147a IMG.CFG: New parameter gap2= to set Post-ID Gap value.
Refs #496
2021-08-10 14:22:13 +01:00
Keir Fraser
9153da4cc0 IMG, ED: GAP2 should be 41 bytes, rather than the usual 22 bytes.
Refs #496
2021-08-10 14:22:04 +01:00
Keir Fraser
3cedba05b3 Support "Enhanced Gotek" boards with Artery MCU. 2021-08-07 14:39:03 +01:00
Keir Fraser
78348d5b62 SFRKC30.AT4.7: Allow longer PF7 settle before sampling board type.
At 10us delay the board would occasionally be detected to have no KC30 header.
An attached rotary encoder then does not work.
2021-07-26 15:13:09 +01:00
Keir Fraser
432c8ea3a4 Fix button handling for "Enhanced Gotek" models. 2021-07-02 08:51:50 +01:00
Eric Anderson
af6860890e 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 and it seems best to avoid config
changes that require users to set write-drain=eot until after async
stabilizes.
2021-06-27 13:50:11 -05:00
Eric Anderson
38fd802bdd hfe: Reuse stk_per_rev from previous track for HFEv3 2021-06-27 13:46:17 -05:00
Eric Anderson
991a7747e5 Add hard sector support to infrastructure and HFE 2021-06-27 13:37:05 -05:00
Keir Fraser
ef83f012ae Support new Gotek board SFRKC30.AT4.7
* AT32F415 654-pin LQFP
 * Optional dedicated rotary header
2021-06-24 09:55:03 +01:00
Eric Anderson
139528629d 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.
2021-06-13 18:39:51 -05:00
Eric Anderson
c01e1a4b35 hfe: Process opcodes when writing 2021-06-13 18:39:51 -05:00
Eric Anderson
73fc71e3b2 hfe: Do not skip the byte following OP_rand 2021-06-13 16:21:31 -05:00
Eric Anderson
a7eac70f39 Limit maxlen passed to a strnlen call to string's actual length
It appears GCC 11.1 now notices the maxlen passed to strnlen exceeds the
actual buffer size, with its -Werror=stringop-overread
2021-06-13 16:08:18 -05:00
Eric Anderson
3932716eee img: Change trash_bc to number of bc
This aligns with the other image implementations. The added precision is
not used.
2021-05-02 23:51:46 -07:00
Eric Anderson
cc714564e8 img: Cleaner bc space calc in FM
This was missed in e60cfc84; the change was made for MFM, but not
FM. The previous code could cause read corruption once every bc index
int overflow... which takes over an hour at HD rates. So the main
benefit is it is clearer to understand and not misleading.
2021-05-02 16:02:24 -07:00
Eric Anderson
b307c256cb Destroy volume cache before entering D-A
Before 13e59f5, D-A didn't use much memory itself and happened to use
unused memory. However, with the write buffer introduced, a volume
cache initialized by another image format and D-A may end up using the
same memory which would be Very Bad.
2021-05-02 14:42:31 -07:00
Eric Anderson
b9af680328 Hard-code async write_bc.len
Previously write_bc's size was calculated to make sure writes for an HD
track in HFE could fit in memory. But that calculation is only necessary
when 1) there is enough memory to make such a thing possible (>30kB,
with min 10kB read_data) but 2) there's not enough memory to make it
"just work" (<53kB). Since 10+kB of memory is dedicated to other tasks,
this really only leaves a SoC RAM size of 48kB, yet we don't target
48kB.

Swapping to hard-code+ASSERT is simpler and avoids changing behavior
between 32 and 64 kB RAM sizes. It also avoids HFE-oriented logic in
floppy_generic.c.
2021-05-02 14:26:59 -07:00
Eric Anderson
f1fcc0c132 img: Precisely align blocks to avoid ring_io overlaps
In the worst case where tracks were not 512-byte aligned and writes
occured to both sides of a track, this could have caused data loss.
Since tracks are generally 512-byte aligned, overlapping rings were
mostly benign. But now there's a check in ring_io to detect future
accidents.
2021-04-25 23:03:24 -07:00
Eric Anderson
13e59f538b da: Convert to async I/O
This uses a custom write buffer similar to ADF. Unlike ADF though, the
read caching is preserved when writing. This is partly because the
default track size is small enough (4KB) that we can just pay the cost
all the time. But it also avoids the need to wait for a pending read to
complete to make memory available for processing written commands.
However, if the D-A controller extends the track size past 8 sectors
with CMD_SET_LBA, then the read cache becomes useless.
2021-04-24 14:48:47 -07:00
Eric Anderson
f61c9606bc fs_async: Support raw disk block I/O
Async raw disk block access will allow D-A to be converted to async.
2021-04-24 13:00:56 -07:00
Eric Anderson
62a122aeb1 adf: Add missing F_sync at end of writes 2021-04-24 12:57:58 -07:00
Eric Anderson
457454db89 adf: Remove mis-placed ring_io_seek
In a8ae61f writing no longer uses ring_io, however a ring_io_seek was
left present in the write path. It seems like that should cause havoc,
but I suspect in the basic testing either the ring was already
fully-buffered (such that there were no further reads) or that the
reader was able to keep ahead of the writer until ring io was
fully-buffered.
2021-04-24 12:51:03 -07:00
Eric Anderson
695f9202f8 adf: Fix bug that re-wrote blocks
Already-written blocks were re-written because the local 'c' variable
that mirrors cons wasn't updated. This wouldn't _create_ any corruptions
as either the data was just old, so re-write just-written blocks, or the
producer is close to wrapping-around the ring, so early-write new data.
However, if there was a CRC failure it could cause that block to be
written to disk when ordinarily it would not be written.
2021-04-24 12:38:02 -07:00
Eric Anderson
e258c7075f adf: trash_bc is actual bitcells in adf
trash_bc in IMG is number of groups of 16 bc as that's the resolution of
the offset calculation. ADF aligns to the bitcell so multiplying
trash_bc by 16 produced horribly-wrong results.
2021-04-17 18:10:52 -07:00
Keir Fraser
5c71b087fd Beta support for Artery microcontrollers
- SFRC922D using AT32F415RCT7
 - SFRC922AT3 using AT32F415CBT7
2021-04-15 11:05:14 +01:00
Eric Anderson
a8ae61f91b adf: Use custom write buffer for "open loop" writes
This reverts part of the write code from 07d5b9e and replaces it with a
custom write buffer. Reads do not _block_ on the write buffer but are
delayed until it is drained. This lets us use all async code paths while
also supporting lots of writes without the controller doing any reads or
index synchronization.
2021-04-11 13:51:39 -07:00
Eric Anderson
07d5b9eeed adf: Use ringio for buffering 2021-04-10 23:46:41 -07:00
Eric Anderson
341aa90b39 ringio: Disable write batch limit during sync
ring_io_sync blocks until writing is complete, so we should optimize
throughput during the operation.
2021-04-10 23:44:04 -07:00
Eric Anderson
835e5170c8 ringio: Advance rd->prod immediately on seek
The previous intention was that enqueue_io would advance the rd->prod
cursor. However, that didn't actually happen because enqueue_io
short-circuits if fop_cb != NULL. We could move the short-circuit after
the cursor updates, but that would cause the recalculations every call
to ring_io_progress. So instead, copy the rd->prod advancement code to
ring_io_seek.

This explains why we needed such a small batch size for 2m88 in 282c4aa,
as an I/O op actually had to complete to make any reads available. With
this bug fix, batch sizes of 2 and 4 now work again for IMG. I used 2 to
be more interleaving-friendly.
2021-04-10 17:40:34 -07:00
Eric Anderson
2399869a78 ringio: Avoid calling start_read when producer can't read
While there may be unread blocks, we only read from rd->prod so we
shouldn't try a read just because some bits are set. This would have
caused an assertion failure for interleaved sectors.
2021-04-10 17:39:03 -07:00
Eric Anderson
282c4aa787 img: Decrease chance of RDATA underrun
It now seeks to next sector sooner in the ring_io to give more time to
read the soon-to-be-needed blocks if sectors are interleaved.

For ED rates, disable the shadow ring. In addition use the same batch
size as used before ring_io (primarily to benefit ED, but applied for
all rates). It seems strange that a batch size of 1 was necessary for ED
which we'd think would benefit from larger batch sizes, but both changes
were necessary to avoid buffer underruns for the 2m88 FF_TestBed test
image.
2021-04-10 15:44:27 -07:00
Eric Anderson
dfe8d19825 ringio: Allow cached reads to be non-contiguous
This is very necessary to support interleaved blocks, as we simply can't
wait for the read producer to read up to half the track just to reach
the second physical sector.

It is also helpful for skewed sides, when the head is changed
immediately after a track step. In that case the "wrong" side is chosen
first and a skew may cause the read to go "backward".
2021-04-10 15:37:47 -07:00
Eric Anderson
47854b187c Drain ring_io I/O when changing tracks and image sync
Rapid seeking can cause a backlog of reads to be queued. This doesn't
cause too much trouble because fs_async swaps to synchronous processing
when the queue is full. However, the queued reads do have an impact on
latency once the final track is reached. This latency is noticeable, but
could be quite dramatic if the fs_async queue size were increased.

When fs_async has to block it logs, so avoiding the blocking is also
helpful to avoid polluting logs.

d2c1990 increased the fs_async queue size to 8 because a single seek
could have exceeded the 4-entry queue (4 for old track, 4 more for new
track). With the draining in place the number of I/O ops is strictly
limited to 4, so the queue size can go back to its earlier value of 4.

I/O processing after the image handler is done would have continued
writing to read_data. This was not a problem for dummy_handler and when
floppy_cancel() was next called, which were the only cases present.
Draining the I/O prevents this gotcha from biting anyone as the code
changes.
2021-03-28 15:30:09 -07:00
Eric Anderson
2e92b51e2f hfe: Flush writes eagerly for large tracks 2021-03-28 12:07:12 -07:00
Eric Anderson
9d44d153d2 Add explicit image syncing for async I/O
This avoids losing pending async writes when the user navigates,
intering D-A mode, and seeked to a non-present track.
2021-03-28 11:42:23 -07:00
Eric Anderson
a2ce484e1d img: Also properly seek on track setup with FM
e60cfc8 fixed MFM, but failed to update FM as well.
2021-03-27 16:29:23 -07:00
Eric Anderson
568f378347 img: Fix reads for sectors greater than 512 bytes
This reduces the batch size from 1024 to 256. There's no real harm to a
batch size smaller than 1024 because the batch size is no longer tied to
I/O size. 512 bytes would work, but requires half of the 2 KB read_bc to
be empty. 256 bytes ekes out 50% more of read_bc for buffering while
still being large enough for efficient buffer filling.

The problem with 1024 bytes is that bc_rdata_flux may not drain all of
read_bc. bc_rdata_flux only reads from read_bc when there are at least 4
bytes. Since odd numbers of data bytes are used for many structures, it
is trivial for 2 bc bytes to get "stuck" in read_bc and cannot drain
until further bitcells follow it. While that could be fixed, it is
better to not require the entire read_bc being empty, as that leaves a
strong risk of rdata underrun.
2021-03-27 15:08:27 -07:00
Eric Anderson
1fbcc499b6 img: Fix writes that don't include the ID record 2021-03-27 14:33:07 -07:00
Eric Anderson
e60cfc8405 img: Properly seek on track setup
The old code was horribly broken, but the bug fixed in a505765 make it
harder to notice as it would write out garbage in a correct-looking way.
Only the first sector or so would be messed up and the reader generally
isn't looking at the sector so soon after the seek anyway. With the fix
in a505765 the garbage no longer looked correct so made this bug more
apparent.

The previous code is broken in many ways, like not handling the bc
cursors properly on roll-over and having the consumer ahead of the
producer even though bc_rdata_flux doesn't support that. All of that was
supposed to be temporary to get things into a half-way working shape and
should not have been included in d8b0b2a, but was forgotten about after
resolving other issues and it not causing an obvious problem.
2021-03-27 11:35:44 -07:00
Eric Anderson
b531a0b885 img: Stream writes to flash
Previously img required the entire sector bc to be available before
decoding it. With async's reduced write_bc buffer size of 2 KB it is no
longer possible to fully-buffer sectors larger than 512 bytes along with
their crc. Decoding the writes as they arrive restores larger sector
size write support. Reads were already able to chunk a sector into
smaller pieces.
2021-03-26 19:58:51 -07:00
Eric Anderson
a505765f66 img: Don't skip bytes during slow read I/O 2021-03-26 10:02:12 -07:00
Eric Anderson
943e4820a6 fs_async: Report longer latencies in reads/writes
We'll probably need to do something better, but for the moment this
mostly avoids slowdown due to logging while still calling out times
things may glitch due to I/O latency.
2021-03-21 12:46:25 -07:00
Eric Anderson
d8b0b2aa7b img: Use ringio for buffering
This breaks sector sizes larger than 512, but that will need to be
follow-up work.
2021-03-21 12:45:48 -07:00
Eric Anderson
d2c19903b2 ringio: Add shadow ring support 2021-03-21 12:28:15 -07:00
Eric Anderson
3ee01621e3 Further fix circular buffer mask in mfm_ring_to_bin()
c4c14c58 was woefully incomplete, but it is still a one-line fix.
2021-03-21 12:25:46 -07:00
Eric Anderson
c4c14c58d4 Add missing circular buffer mask in mfm_ring_to_bin() 2021-03-20 23:09:29 -07:00
Eric Anderson
bb8d812b1b Use naked functions for cancellation and memcpy/memset asm
This is a bit cleaner code, but also allows the C compiler to generate
proper debug information. This prevents addr2line from producing strange
things like '$t at util.c:?'.
2021-03-20 23:02:59 -07:00
Eric Anderson
1447a4b761 Use __attribute__((naked)) for IRQ_SELA_changed
This allows C to be aware of the declaration which reduces the amount of
assembler necessary.
2021-03-20 22:58:35 -07:00
Eric Anderson
fef80998ee ringio: Actually check for writes past producer
Like the comment said the code did.
2021-03-13 15:46:30 -08:00
Eric Anderson
a73f4f0966 ringio: Fix writing faults caused by seeking backward
There may be pending writes past wd_prod because seeking can rewind
cursors into the recent past. Assertions were only triggering if the
track was fully buffered, but I believe that was because the writes were
just completing faster in that case and so were more likely to 'catch
up' to the rewound producer.
2021-03-07 10:22:22 -08:00
Keir Fraser
c5fa3294f9 img: Fix pc_dos layout for DMF (1.68MB)
Fixes #430
2021-03-04 09:32:45 +00:00
Eric Anderson
01e760bb9e Fix quickdisk now that image_setup_track() is void
In 1937c17 responsibility for entering and exiting D-A was removed from
image.c and moved into the floppy handling. With that change
image_setup_track no longer need a return value. Since quickdisk doesn't
support D-A, the change is as simple as removing the condition after
running image_setup_track.
2021-03-03 20:46:27 -08:00
Eric Anderson
c511bd94a8 Use async I/O in floppy and ring I/O in HFE 2021-03-03 20:35:06 -08:00
Eric Anderson
1937c174d7 Reload image when changing DA mode
This is in preparation for optimizing allocations based on image
handler. Such capability requires a full reload if the handler changes.
2021-03-03 20:35:06 -08:00
Eric Anderson
8b64c2e27a Add ring I/O API 2021-03-03 20:35:06 -08:00
Eric Anderson
53a46ebf7b Add async I/O API
SD cards were not tested.
2021-03-03 20:35:06 -08:00
Eric Anderson
fb97c5f3fe Add threading API 2021-03-03 20:35:06 -08:00
111 changed files with 7178 additions and 2036 deletions

View file

@ -5,12 +5,12 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Set environment variables - name: Set environment variables
id: vars id: vars
run: | run: |
echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Dependency packages (apt) - name: Dependency packages (apt)
run: | run: |
@ -25,7 +25,7 @@ jobs:
export VER=${{ steps.vars.outputs.sha_short }} export VER=${{ steps.vars.outputs.sha_short }}
make -j4 dist VER=$VER make -j4 dist VER=$VER
mkdir -p _cidist mkdir -p _cidist
rm flashfloppy-$VER.zip mv out/flashfloppy-$VER .
rm flashfloppy-$VER/RELEASE_NOTES rm flashfloppy-$VER/RELEASE_NOTES
git rev-parse HEAD >flashfloppy-$VER/COMMIT git rev-parse HEAD >flashfloppy-$VER/COMMIT
zip -r flashfloppy-$VER.zip flashfloppy-$VER zip -r flashfloppy-$VER.zip flashfloppy-$VER
@ -34,8 +34,8 @@ jobs:
- name: Build debug dist - name: Build debug dist
run: | run: |
export VER=${{ steps.vars.outputs.sha_short }}-debug export VER=${{ steps.vars.outputs.sha_short }}-debug
env debug=y make -j4 dist VER=$VER make -j4 dist VER=$VER level=debug
rm flashfloppy-$VER.zip mv out/flashfloppy-$VER .
rm flashfloppy-$VER/RELEASE_NOTES rm flashfloppy-$VER/RELEASE_NOTES
git rev-parse HEAD >flashfloppy-$VER/COMMIT git rev-parse HEAD >flashfloppy-$VER/COMMIT
echo debug >>flashfloppy-$VER/COMMIT echo debug >>flashfloppy-$VER/COMMIT
@ -43,7 +43,7 @@ jobs:
mv flashfloppy-$VER.zip _cidist/ mv flashfloppy-$VER.zip _cidist/
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: FlashFloppy.CI.${{ steps.vars.outputs.sha_short }} name: FlashFloppy.CI.${{ steps.vars.outputs.sha_short }}
path: _cidist path: _cidist

View file

@ -10,12 +10,12 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Set environment variables - name: Set environment variables
id: vars id: vars
run: | run: |
echo "::set-output name=ref::$(echo ${{ github.ref }} | sed -e's#.*/##')" echo "ver=$(echo ${{ github.ref }} | sed -e's#.*/v##')" >> $GITHUB_OUTPUT
- name: Dependency packages (apt) - name: Dependency packages (apt)
run: | run: |
@ -27,28 +27,19 @@ jobs:
- name: Build release - name: Build release
run: | run: |
export VER=${{ steps.vars.outputs.ref }} export VER=${{ steps.vars.outputs.ver }}
make -j4 dist VER=$VER make -j4 dist VER=$VER
mv out/flashfloppy-$VER.zip .
- name: Create Release - name: Create Release
id: create_release id: create_release
uses: actions/create-release@v1 uses: ncipollo/release-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ github.ref }} tag: ${{ github.ref }}
release_name: ${{ steps.vars.outputs.ref }} token: ${{ secrets.GITHUB_TOKEN }}
body: "[**Release Notes:**](https://github.com/keirf/FlashFloppy/blob/master/RELEASE_NOTES)" name: FlashFloppy ${{ steps.vars.outputs.ver }}
body: "[**Release Notes:**](https://github.com/keirf/flashfloppy/blob/master/RELEASE_NOTES)"
draft: false draft: false
prerelease: false prerelease: false
artifacts: flashfloppy-${{ steps.vars.outputs.ver }}.zip
- name: Upload Release Asset artifactContentType: application/zip
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: flashfloppy-${{ steps.vars.outputs.ref }}.zip
asset_name: flashfloppy-${{ steps.vars.outputs.ref }}.zip
asset_content_type: application/zip

14
.gitignore vendored
View file

@ -1,15 +1,5 @@
*.[oa]
.*.d
*~ *~
*.ld
*.elf
*.bin
*.hex
*.orig *.orig
*.rej *.rej
*.rld /out
*.upd /ext
*.dfu
/flashfloppy-*
/index.html
/HxC_Compat*

213
Makefile
View file

@ -1,113 +1,142 @@
export FW_VER := 3.23 PROJ := flashfloppy
VER := $(shell git rev-parse --short HEAD)
PROJ := FlashFloppy export FW_VER := $(VER)
VER := v$(FW_VER)
SUBDIRS += src bootloader bl_update io_test PYTHON := python3
.PHONY: all upd clean flash start serial gotek
ifneq ($(RULES_MK),y)
.DEFAULT_GOAL := gotek
export ROOT := $(CURDIR) export ROOT := $(CURDIR)
all: .PHONY: FORCE
$(MAKE) -f $(ROOT)/Rules.mk all
clean: .DEFAULT_GOAL := all
rm -f *.hex *.upd *.dfu *.html
$(MAKE) -f $(ROOT)/Rules.mk $@
gotek: all prod-%: FORCE
mv FF.dfu FF_Gotek-$(VER).dfu $(MAKE) target mcu=$* target=bootloader level=prod
mv FF.upd FF_Gotek-$(VER).upd $(MAKE) target mcu=$* target=floppy level=prod
mv FF.hex FF_Gotek-$(VER).hex $(MAKE) target mcu=$* target=quickdisk level=prod
mv BL.upd FF_Gotek-Bootloader-$(VER).upd $(MAKE) target mcu=$* target=bl_update level=prod
mv IOT.upd FF_Gotek-IO-Test-$(VER).upd $(MAKE) target mcu=$* target=io_test level=prod
HXC_FF_URL := https://www.github.com/keirf/HxC_FF_File_Selector debug-%: FORCE
$(MAKE) target mcu=$* target=bootloader level=debug
$(MAKE) target mcu=$* target=floppy level=debug
$(MAKE) target mcu=$* target=quickdisk level=debug
$(MAKE) target mcu=$* target=bl_update level=debug
$(MAKE) target mcu=$* target=io_test level=debug
logfile-%: FORCE
$(MAKE) target mcu=$* target=bootloader level=logfile
$(MAKE) target mcu=$* target=floppy level=logfile
$(MAKE) target mcu=$* target=quickdisk level=logfile
all-%: FORCE prod-% debug-% logfile-% ;
all: FORCE all-stm32f105 all-at32f435 ;
clean: FORCE
rm -rf out
mrproper: FORCE clean
rm -rf ext
out: FORCE
+mkdir -p out/$(mcu)/$(level)/$(target)
target: FORCE out
$(MAKE) -C out/$(mcu)/$(level)/$(target) -f $(ROOT)/Rules.mk target.bin target.hex target.dfu $(mcu)=y $(level)=y $(target)=y
HXC_FF_URL := https://www.github.com/keirf/flashfloppy-hxc-file-selector
HXC_FF_URL := $(HXC_FF_URL)/releases/download HXC_FF_URL := $(HXC_FF_URL)/releases/download
HXC_FF_VER := v9-FF HXC_FF_VER := v9-FF
dist: _legacy_dist: PROJ := FF_Gotek
rm -rf flashfloppy-* _legacy_dist: FORCE
mkdir -p flashfloppy-$(VER)/alt/bootloader $(PYTHON) $(ROOT)/scripts/mk_update.py old \
mkdir -p flashfloppy-$(VER)/alt/logfile $(t)/$(PROJ)-$(VER).upd \
mkdir -p flashfloppy-$(VER)/alt/io-test out/$(mcu)/$(level)/floppy/target.bin & \
mkdir -p flashfloppy-$(VER)/alt/quickdisk/logfile $(PYTHON) $(ROOT)/scripts/mk_update.py old \
$(MAKE) clean $(t)/alt/bootloader/$(PROJ)-bootloader-$(VER).upd \
$(MAKE) gotek out/$(mcu)/$(level)/bl_update/target.bin & \
cp -a FF_Gotek-$(VER).dfu flashfloppy-$(VER)/ $(PYTHON) $(ROOT)/scripts/mk_update.py old \
cp -a FF_Gotek-$(VER).upd flashfloppy-$(VER)/ $(t)/alt/io-test/$(PROJ)-io-test-$(VER).upd \
cp -a FF_Gotek-$(VER).hex flashfloppy-$(VER)/ out/$(mcu)/$(level)/io_test/target.bin & \
cp -a FF_Gotek-Bootloader-$(VER).upd flashfloppy-$(VER)/alt/bootloader/ $(PYTHON) $(ROOT)/scripts/mk_update.py old \
cp -a FF_Gotek-IO-Test-$(VER).upd flashfloppy-$(VER)/alt/io-test/ $(t)/alt/logfile/$(PROJ)-logfile-$(VER).upd \
$(MAKE) clean out/$(mcu)/logfile/floppy/target.bin & \
$(MAKE) debug=n logfile=y -f $(ROOT)/Rules.mk upd $(PYTHON) $(ROOT)/scripts/mk_update.py old \
mv FF.upd flashfloppy-$(VER)/alt/logfile/FF_Gotek-Logfile-$(VER).upd $(t)/alt/quickdisk/$(PROJ)-quickdisk-$(VER).upd \
$(MAKE) clean out/$(mcu)/$(level)/quickdisk/target.bin & \
$(MAKE) quickdisk=y -f $(ROOT)/Rules.mk upd $(PYTHON) $(ROOT)/scripts/mk_update.py old \
mv FF.upd flashfloppy-$(VER)/alt/quickdisk/FF_Gotek-QuickDisk-$(VER).upd $(t)/alt/quickdisk/logfile/$(PROJ)-quickdisk-logfile-$(VER).upd \
$(MAKE) clean out/$(mcu)/logfile/quickdisk/target.bin & \
$(MAKE) quickdisk=y debug=n logfile=y -f $(ROOT)/Rules.mk upd wait
mv FF.upd flashfloppy-$(VER)/alt/quickdisk/logfile/FF_Gotek-QuickDisk-Logfile-$(VER).upd
python3 scripts/mk_qd.py --window=6.5 flashfloppy-$(VER)/alt/quickdisk/Blank.qd
$(MAKE) clean
cp -a COPYING flashfloppy-$(VER)/
cp -a README.md flashfloppy-$(VER)/
cp -a RELEASE_NOTES flashfloppy-$(VER)/
cp -a examples flashfloppy-$(VER)/
[ -e HxC_Compat_Mode-$(HXC_FF_VER).zip ] || \
wget -q --show-progress $(HXC_FF_URL)/$(HXC_FF_VER)/HxC_Compat_Mode-$(HXC_FF_VER).zip
rm -rf index.html
unzip -q HxC_Compat_Mode-$(HXC_FF_VER).zip
mv HxC_Compat_Mode flashfloppy-$(VER)
mkdir -p flashfloppy-$(VER)/scripts
cp -a scripts/edsk* flashfloppy-$(VER)/scripts/
cp -a scripts/mk_hfe.py flashfloppy-$(VER)/scripts/
zip -r flashfloppy-$(VER).zip flashfloppy-$(VER)
mrproper: clean _dist: FORCE
rm -rf flashfloppy-* cd out/$(mcu)/$(level)/floppy; \
rm -rf HxC_Compat_Mode-$(HXC_FF_VER).zip cp -a target.dfu $(t)/dfu/$(PROJ)-$(n)-$(VER).dfu; \
cp -a target.hex $(t)/hex/$(PROJ)-$(n)-$(VER).hex
$(PYTHON) $(ROOT)/scripts/mk_update.py new \
$(t)/$(PROJ)-$(VER).upd \
out/$(mcu)/$(level)/floppy/target.bin $(mcu) & \
$(PYTHON) $(ROOT)/scripts/mk_update.py new \
$(t)/alt/bootloader/$(PROJ)-bootloader-$(VER).upd \
out/$(mcu)/$(level)/bl_update/target.bin $(mcu) & \
$(PYTHON) $(ROOT)/scripts/mk_update.py new \
$(t)/alt/io-test/$(PROJ)-io-test-$(VER).upd \
out/$(mcu)/$(level)/io_test/target.bin $(mcu) & \
$(PYTHON) $(ROOT)/scripts/mk_update.py new \
$(t)/alt/logfile/$(PROJ)-logfile-$(VER).upd \
out/$(mcu)/logfile/floppy/target.bin $(mcu) & \
$(PYTHON) $(ROOT)/scripts/mk_update.py new \
$(t)/alt/quickdisk/$(PROJ)-quickdisk-$(VER).upd \
out/$(mcu)/$(level)/quickdisk/target.bin $(mcu) & \
$(PYTHON) $(ROOT)/scripts/mk_update.py new \
$(t)/alt/quickdisk/logfile/$(PROJ)-quickdisk-logfile-$(VER).upd \
out/$(mcu)/logfile/quickdisk/target.bin $(mcu) & \
wait
else dist: level := prod
dist: t := $(ROOT)/out/$(PROJ)-$(VER)
upd: dist: FORCE all
$(MAKE) -C src -f $(ROOT)/Rules.mk $(PROJ).elf $(PROJ).bin $(PROJ).hex rm -rf out/$(PROJ)-*
$(PYTHON) ./scripts/mk_update.py src/$(PROJ).bin FF.upd mkdir -p $(t)/hex
mkdir -p $(t)/dfu
all: mkdir -p $(t)/alt/bootloader
$(MAKE) -C src -f $(ROOT)/Rules.mk $(PROJ).elf $(PROJ).bin $(PROJ).hex mkdir -p $(t)/alt/logfile
$(MAKE) bootloader=y logfile=n -C bootloader \ mkdir -p $(t)/alt/io-test
-f $(ROOT)/Rules.mk \ mkdir -p $(t)/alt/quickdisk/logfile
Bootloader.elf Bootloader.bin Bootloader.hex $(MAKE) _legacy_dist mcu=stm32f105 level=$(level) t=$(t)
$(MAKE) logfile=n -C bl_update -f $(ROOT)/Rules.mk \ $(MAKE) _dist mcu=stm32f105 n=at415-st105 level=$(level) t=$(t)
BL_Update.elf BL_Update.bin BL_Update.hex $(MAKE) _dist mcu=at32f435 n=at435 level=$(level) t=$(t)
$(MAKE) logfile=n -C io_test -f $(ROOT)/Rules.mk \ $(PYTHON) scripts/mk_qd.py --window=6.5 $(t)/alt/quickdisk/Blank.qd
IO_Test.elf IO_Test.bin IO_Test.hex cp -a COPYING $(t)/
srec_cat bootloader/Bootloader.hex -Intel src/$(PROJ).hex -Intel \ cp -a README $(t)/
-o FF.hex -Intel cp -a RELEASE_NOTES $(t)/
$(PYTHON) ./scripts/mk_update.py src/$(PROJ).bin FF.upd cp -a examples $(t)/
$(PYTHON) ./scripts/mk_update.py bl_update/BL_Update.bin BL.upd [ -e ext/HxC_Compat_Mode-$(HXC_FF_VER).zip ] || \
$(PYTHON) ./scripts/mk_update.py io_test/IO_Test.bin IOT.upd (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)
$(PYTHON) ./scripts/dfu-convert.py -i FF.hex FF.dfu (cd $(t) && unzip -q ../../ext/HxC_Compat_Mode-$(HXC_FF_VER).zip)
mkdir -p $(t)/scripts
endif cp -a scripts/edsk* $(t)/scripts/
cp -a scripts/mk_hfe.py $(t)/scripts/
cd out && zip -r $(PROJ)-$(VER).zip $(PROJ)-$(VER)
BAUD=115200 BAUD=115200
DEV=/dev/ttyUSB0 DEV=/dev/ttyUSB0
SUDO=sudo
STM32FLASH=stm32flash
T=out/$(target)/target.hex
ocd: gotek ocd: FORCE all
python3 scripts/openocd/flash.py `pwd`/FF_Gotek-$(VER).hex $(PYTHON) scripts/openocd/flash.py $(T)
flash: gotek flash: FORCE all
sudo stm32flash -b $(BAUD) -w FF_Gotek-$(VER).hex $(DEV) $(SUDO) $(STM32FLASH) -b $(BAUD) -w $(T) $(DEV)
start: start: FORCE
sudo stm32flash -b $(BAUD) -g 0 $(DEV) $(SUDO) $(STM32FLASH) -b $(BAUD) -g 0 $(DEV)
serial: serial: FORCE
sudo miniterm.py $(DEV) 3000000 $(SUDO) miniterm.py $(DEV) 3000000

75
README Normal file
View file

@ -0,0 +1,75 @@
FlashFloppy
===========
Keir Fraser <keir.xen@gmail.com>
https://github.com/keirf/flashfloppy
This distribution contains FlashFloppy custom firmware for Gotek and
Gotek-compatible hardware.
FlashFloppy documentation and downloads:
https://github.com/keirf/flashfloppy/wiki/
Supported Microcontrollers
--------------------------
FlashFloppy supports two types of STM32-like microcontroller:
STM32F105 (and the compatible AT32F415)
- 72MHz Cortex-M3, 128kB Flash, 64kB RAM (AT32F415: 32kB RAM)
AT32F435
- 288MHz Cortex-M4, 256kB Flash, 384kB RAM
If you want to know which type of MCU you have, you can open your Gotek and
read the writing on the square chip on your Gotek PCB.
Firmware Programming
--------------------
If programming a factory-fresh Gotek running factory firmware, you will need
to program a HEX or DFU firmware file as explained in the wiki, linked above.
These files are located in the hex/ and dfu/ folders respectively, and you
must use the correct file for your microcontroller:
STM32F105, AT32F415
- Use file "dfu/flashfloppy-at415-st105-<ver>.dfu" (or .hex equivalent)
AT32F435
- Use file "dfu/flashfloppy-at435-<ver>.dfu" (or .hex equivalent)
Firmware Update
---------------
Once FlashFloppy has been flashed to a device (using a HEX or DFU file),
further updates can be made via an Update file on a USB drive, as described
in the wiki.
To support older versions of the FlashFloppy bootloader, two types of
Update file are included in the distribution, distinguishable by name:
FF_Gotek-*.upd: Legacy Update file
- All STM32F105/AT32F415 devices
flashfloppy-*.upd: Universal Update file
- All AT32F435 devices
- STM32F105/AT32F415 devices running a recent FlashFloppy bootloader
Each update format will only work with a device (and FlashFloppy bootloader)
that supports it. If you are unsure which to use, it is okay to copy both
Update file types to your USB drive: the bootloader will correctly load just
one of the Update file types that it supports.
Alternatively try one Update file at a time, and switch to the other type if
you see error E01 on the Gotek display.
Alternative Firmwares
---------------------
In the alt/ folder you will find (pairs of) Update files implementing
alternative firmwares. Most users can ignore these, and use only the
Update files in the root of the distribution.
The alternative firmwares include:
* alt/bootloader: Updates your Gotek bootloader
- WARNING: Proceed with caution. Read the Firmware Update page on the wiki.
* alt/io-test: Test your Gotek I/O pins if you suspect a hardware fault
* alt/quickdisk: QuickDisk emulation (see the wiki)
- If you don't know what QuickDisk is, you don't need this firmware.
* alt/logile: Firmware with debug logging to FFLOG.TXT on the USB drive
- Useful only for fault finding. Not for general use.
Redistribution
--------------
Source code, and all binary releases, are freely redistributable in
any form. Please see the the COPYING file included in this
distribution.

View file

@ -6,6 +6,8 @@
![Downloads Badge][downloads-badge] ![Downloads Badge][downloads-badge]
![Version Badge][version-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 **FlashFloppy** is a floppy-drive emulator for the ubiquitous
[**Gotek**][Gotek-Compatibility] hardware. Connect to retro machines just [**Gotek**][Gotek-Compatibility] hardware. Connect to retro machines just
like a real floppy drive but use disk images on a modern USB stick! like a real floppy drive but use disk images on a modern USB stick!
@ -24,16 +26,7 @@ like a real floppy drive but use disk images on a modern USB stick!
- [**Download FlashFloppy**][Downloads] - [**Download FlashFloppy**][Downloads]
## Documentation ## Documentation
- [**Read the GitHub Wiki**](https://github.com/keirf/FlashFloppy/wiki) - [**Read the GitHub Wiki**](https://github.com/keirf/flashfloppy/wiki)
## Donations
FlashFloppy is a labour of love: working on it takes a **lot** of time
and effort. Although it is [Free Software](Redistribution) and
a pet project of mine, beer & coffee tokens will fuel me in the
push onwards and upwards!
For further information please see the [**Donations page**][Donations].
## Redistribution ## Redistribution
@ -51,14 +44,13 @@ file on your selling page. For example:
- FlashFloppy is free software. For more information see the - FlashFloppy is free software. For more information see the
[license](COPYING). [license](COPYING).
[Gotek-Compatibility]: https://github.com/keirf/FlashFloppy/wiki/Gotek-Compatibility [Gotek-Compatibility]: https://github.com/keirf/flashfloppy/wiki/Gotek-Compatibility
[Host-Platforms]: https://github.com/keirf/FlashFloppy/wiki/Host-Platforms [Host-Platforms]: https://github.com/keirf/flashfloppy/wiki/Host-Platforms
[Image-Formats]: https://github.com/keirf/FlashFloppy/wiki/Image-Formats [Image-Formats]: https://github.com/keirf/flashfloppy/wiki/Image-Formats
[Track-Layouts]: https://github.com/keirf/FlashFloppy/wiki/Track-Layouts [Track-Layouts]: https://github.com/keirf/flashfloppy/wiki/Track-Layouts
[FF.CFG-Configuration-File]: https://github.com/keirf/FlashFloppy/wiki/FF.CFG-Configuration-File [FF.CFG-Configuration-File]: https://github.com/keirf/flashfloppy/wiki/FF.CFG-Configuration-File
[Downloads]: https://github.com/keirf/FlashFloppy/wiki/Downloads [Downloads]: https://github.com/keirf/flashfloppy/wiki/Downloads
[Donations]: https://github.com/keirf/FlashFloppy/wiki/Donations
[ci-badge]: https://github.com/keirf/FlashFloppy/workflows/CI/badge.svg [ci-badge]: https://github.com/keirf/flashfloppy/workflows/CI/badge.svg
[downloads-badge]: https://img.shields.io/github/downloads/keirf/FlashFloppy/total [downloads-badge]: https://img.shields.io/github/downloads/keirf/flashfloppy/total
[version-badge]: https://img.shields.io/github/v/release/keirf/FlashFloppy [version-badge]: https://img.shields.io/github/v/release/keirf/flashfloppy

View file

@ -3,6 +3,43 @@
** Keir Fraser <keir.xen@gmail.com> ** Keir Fraser <keir.xen@gmail.com>
************************************ ************************************
** v4.7a - 4 December 2022
- Contains fixes up to v3.38 from v3 stable release series
** v4.6a - 29 July 2022
- AUTOBOOT / HxC Mode: Fix "Error ... LBA Change test failed!"
** v4.5a - 29 July 2022
- Contains fixes up to v3.34 from v3 stable release series
- Supports AT32F435 MCU and SFRKC430.AT4.35 PCB
** v4.4a - 11 January 2022
- DSK: Async I/O fixes
- IMG.CFG: New option step= allows to specify double-step operation
- FF.CFG: New option max-cyl= allows limiting head-step range
** v4.3a - 14 December 2021
- QuickDisk: Ported to new async I/O framework
- Various I/O bug fixes
** v4.2a - 13 October 2021
- DSK: Ported to new async I/O framework
- ringio: A few bug fixes
** v4.1a - 2 October 2021
- Contains fixes/features up to v3.29 from v3 stable release series
- Do not use index_suppressed if starting rd DMA is slow
- ringio: Do not lose write for unaligned wd_prod
- hfe: Protect HFEv3 against a broken run of 1s
** v4.0a - 10 August 2021
- Contains fixes up to v3.28 from v3 stable release series
- New asynchonous I/O handling (Eric Anderson / ejona86)
- Smarter prefetching of cylinder data
- Smarter writeback of modified tracks/sectors
- More accurate emulation
- Hard sector HFE image support (Eric Anderson / ajona86)
** v3.23 - 31 December 2020 ** v3.23 - 31 December 2020
- OLED/LCD: Fix missing folder name display row when inserting USB drive - OLED/LCD: Fix missing folder name display row when inserting USB drive
- IMG.CFG: New examples for Roland, Sinclair QL, Kaypro - IMG.CFG: New examples for Roland, Sinclair QL, Kaypro
@ -134,7 +171,7 @@
- Extra logging in FFLOG.TXT for debug purposes - Extra logging in FFLOG.TXT for debug purposes
- Amiga AutoSwap new title: Gobliiiins - Amiga AutoSwap new title: Gobliiiins
- Thanks to Arkadiusz Makarenko! - Thanks to Arkadiusz Makarenko!
- https://github.com/keirf/FF_AutoSwap/wiki/Downloads - https://github.com/keirf/flashfloppy-autoswap/wiki/Downloads
** v3.6a - 13 October 2019 ** v3.6a - 13 October 2019
- Quick Disk Initial Release - Quick Disk Initial Release
@ -194,7 +231,7 @@
- LCD/OLED: Improve power-on initialisation robustness - LCD/OLED: Improve power-on initialisation robustness
- Roland: Direct support for *.OUT images - Roland: Direct support for *.OUT images
- IO-Test: New alternative firmware to test Gotek I/O pins - IO-Test: New alternative firmware to test Gotek I/O pins
- https://github.com/keirf/FlashFloppy/wiki/Testing-IO-Pins - https://github.com/keirf/flashfloppy/wiki/Testing-IO-Pins
** v2.11a - 23 May 2019 ** v2.11a - 23 May 2019
- Simpler bootloader update process with all-in-one update file - Simpler bootloader update process with all-in-one update file
@ -298,7 +335,7 @@
- Game/demo AutoSwap-disks feature - Game/demo AutoSwap-disks feature
- No manual disk swapping, at all! - No manual disk swapping, at all!
- Requires patching of host software titles - Requires patching of host software titles
- Amiga titles so far (github:keirf/FF_AutoSwap): - Amiga titles so far (github:keirf/flashfloppy-autoswap):
- Beneath a Steel Sky - Beneath a Steel Sky
- Indiana Jones and the Fate of Atlantis - Indiana Jones and the Fate of Atlantis
- The Secret of Monkey Island - The Secret of Monkey Island

View file

@ -13,25 +13,38 @@ FLAGS = -g -Os -nostdlib -std=gnu99 -iquote $(ROOT)/inc
FLAGS += -Wall -Werror -Wno-format -Wdeclaration-after-statement FLAGS += -Wall -Werror -Wno-format -Wdeclaration-after-statement
FLAGS += -Wstrict-prototypes -Wredundant-decls -Wnested-externs FLAGS += -Wstrict-prototypes -Wredundant-decls -Wnested-externs
FLAGS += -fno-common -fno-exceptions -fno-strict-aliasing FLAGS += -fno-common -fno-exceptions -fno-strict-aliasing
FLAGS += -mlittle-endian -mthumb -mcpu=cortex-m3 -mfloat-abi=soft FLAGS += -mlittle-endian -mthumb -mfloat-abi=soft
FLAGS += -Wno-unused-value -ffunction-sections FLAGS += -Wno-unused-value -ffunction-sections
## STM32F105
ifeq ($(mcu),stm32f105)
FLAGS += -mcpu=cortex-m3 -DSTM32F105=1 -DMCU=1
ifeq ($(bootloader),y)
override logfile=n
endif
# Debug builds don't fit in available Flash. FIXME for main firmware.
override debug=n
override logfile=n
## AT32F435
else ifeq ($(mcu),at32f435)
FLAGS += -mcpu=cortex-m4 -DAT32F435=4 -DMCU=4
endif
ifneq ($(debug),y) ifneq ($(debug),y)
FLAGS += -DNDEBUG FLAGS += -DNDEBUG
endif endif
# Following options are mutually exclusive
ifeq ($(bootloader),y) ifeq ($(bootloader),y)
FLAGS += -DBOOTLOADER=1 FLAGS += -DBOOTLOADER=1
else ifeq ($(logfile),y) endif
ifeq ($(logfile),y)
FLAGS += -DLOGFILE=1 FLAGS += -DLOGFILE=1
endif endif
ifeq ($(quickdisk),y) ifeq ($(quickdisk),y)
FLAGS += -DQUICKDISK=1 FLAGS += -DQUICKDISK=1
floppy=n
else
floppy=y
endif endif
FLAGS += -MMD -MF .$(@F).d FLAGS += -MMD -MF .$(@F).d
@ -43,12 +56,11 @@ CFLAGS += $(CFLAGS-y) $(FLAGS) -include decls.h
AFLAGS += $(AFLAGS-y) $(FLAGS) -D__ASSEMBLY__ AFLAGS += $(AFLAGS-y) $(FLAGS) -D__ASSEMBLY__
LDFLAGS += $(LDFLAGS-y) $(FLAGS) -Wl,--gc-sections LDFLAGS += $(LDFLAGS-y) $(FLAGS) -Wl,--gc-sections
RULES_MK := y SRCDIR := $(shell $(PYTHON) $(ROOT)/scripts/srcdir.py $(CURDIR))
include $(SRCDIR)/Makefile
include Makefile
SUBDIRS += $(SUBDIRS-y) SUBDIRS += $(SUBDIRS-y)
OBJS += $(OBJS-y) $(patsubst %,%/build.o,$(SUBDIRS)) OBJS += $(OBJS-y) $(OBJS-^n) $(patsubst %,%/build.o,$(SUBDIRS))
# Force execution of pattern rules (for which PHONY cannot be directly used). # Force execution of pattern rules (for which PHONY cannot be directly used).
.PHONY: FORCE .PHONY: FORCE
@ -62,21 +74,14 @@ build.o: $(OBJS)
$(LD) -r -o $@ $^ $(LD) -r -o $@ $^
%/build.o: FORCE %/build.o: FORCE
+mkdir -p $*
$(MAKE) -f $(ROOT)/Rules.mk -C $* build.o $(MAKE) -f $(ROOT)/Rules.mk -C $* build.o
%.o: %.c Makefile %.ld: $(SRCDIR)/%.ld.S $(SRCDIR)/Makefile
@echo CC $@
$(CC) $(CFLAGS) -c $< -o $@
%.o: %.S Makefile
@echo AS $@
$(CC) $(AFLAGS) -c $< -o $@
%.ld: %.ld.S Makefile
@echo CPP $@ @echo CPP $@
$(CC) -P -E $(AFLAGS) $< -o $@ $(CC) -P -E $(AFLAGS) $< -o $@
%.elf: $(OBJS) %.ld Makefile %.elf: $(OBJS) %.ld $(SRCDIR)/Makefile
@echo LD $@ @echo LD $@
$(CC) $(LDFLAGS) -T$(*F).ld $(OBJS) -o $@ $(CC) $(LDFLAGS) -T$(*F).ld $(OBJS) -o $@
chmod a-x $@ chmod a-x $@
@ -85,23 +90,25 @@ build.o: $(OBJS)
@echo OBJCOPY $@ @echo OBJCOPY $@
$(OBJCOPY) -O ihex $< $@ $(OBJCOPY) -O ihex $< $@
chmod a-x $@ chmod a-x $@
$(PYTHON) $(ROOT)/scripts/check_hex.py $@ $(mcu)
ifneq ($(bootloader),y)
srec_cat ../bootloader/target.hex -Intel $@ -Intel -o $@ -Intel
endif
%.bin: %.elf %.bin: %.elf
@echo OBJCOPY $@ @echo OBJCOPY $@
$(OBJCOPY) -O binary $< $@ $(OBJCOPY) -O binary $< $@
chmod a-x $@ chmod a-x $@
%.o: $(RPATH)/%.c Makefile %.dfu: %.hex
$(PYTHON) $(ROOT)/scripts/dfu-convert.py -i $< $@
%.o: $(SRCDIR)/%.c $(SRCDIR)/Makefile
@echo CC $@ @echo CC $@
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
%.o: $(RPATH)/%.S Makefile %.o: $(SRCDIR)/%.S $(SRCDIR)/Makefile
@echo AS $@ @echo AS $@
$(CC) $(AFLAGS) -c $< -o $@ $(CC) $(AFLAGS) -c $< -o $@
clean:: $(addprefix _clean_,$(SUBDIRS) $(SUBDIRS-n) $(SUBDIRS-))
rm -f *.orig *.rej *~ *.o *.elf *.hex *.bin *.ld $(DEPS)
_clean_%: FORCE
$(MAKE) -f $(ROOT)/Rules.mk -C $* clean
-include $(DEPS) -include $(DEPS)

View file

@ -1 +0,0 @@
#include "../src/FlashFloppy.ld.S"

View file

@ -1,22 +0,0 @@
RPATH = ../src
OBJS += bl_update.o
OBJS += fpec.o
OBJS += build_info.o
OBJS += crc.o
OBJS += vectors.o
OBJS += string.o
OBJS += stm32f10x.o
OBJS += time.o
OBJS += timer.o
OBJS += util.o
OBJS += flash_cfg.o
OBJS-$(debug) += console.o
SUBDIRS += display
SUBDIRS += gotek
.PHONY: $(RPATH)/build_info.c
build_info.o: CFLAGS += -DFW_VER="\"$(FW_VER)\""

View file

@ -1,6 +0,0 @@
RPATH = ../../src/display
OBJS += display.o
OBJS += lcd.o
OBJS += oled_font_6x13.o
OBJS += led_7seg.o

View file

@ -1,3 +0,0 @@
RPATH = ../../src/gotek
OBJS += board.o

View file

@ -1,7 +0,0 @@
#define FLASH_BASE 0x08000000
#define FLASH_LEN 32K
#define RAM_BASE 0x20000000
#define RAM_LEN 64K
#include "../scripts/stm32f10x.ld.S"

View file

@ -1,29 +0,0 @@
RPATH = ../src
OBJS += fw_update.o
OBJS += fpec.o
OBJS += build_info.o
OBJS += cancellation.o
OBJS += crc.o
OBJS += vectors.o
OBJS += fs.o
OBJS += sd_spi.o
OBJS += spi.o
OBJS += string.o
OBJS += stm32f10x.o
OBJS += time.o
OBJS += timer.o
OBJS += util.o
OBJS += volume.o
OBJS += flash_cfg.o
OBJS-$(debug) += console.o
SUBDIRS += display
SUBDIRS += fatfs
SUBDIRS += gotek
SUBDIRS += usb
.PHONY: $(RPATH)/build_info.c
build_info.o: CFLAGS += -DFW_VER="\"$(FW_VER)\""

View file

@ -1,6 +0,0 @@
RPATH = ../../src/display
OBJS += display.o
OBJS += lcd.o
OBJS += oled_font_6x13.o
OBJS += led_7seg.o

View file

@ -1,4 +0,0 @@
RPATH = ../../src/fatfs
OBJS += ff.o
OBJS += ffunicode.o

View file

@ -1,3 +0,0 @@
RPATH = ../../src/gotek
OBJS += board.o

View file

@ -1,8 +0,0 @@
RPATH = ../../src/usb
OBJS += usb_bsp.o
OBJS += usbh_msc_fatfs.o
SUBDIRS += stm32_usbh_msc
usb%.o: CFLAGS += -I$(ROOT)/src/usb/stm32_usbh_msc/inc/ -include usbh_conf.h

View file

@ -1,14 +0,0 @@
RPATH = ../../../src/usb/stm32_usbh_msc
OBJS += usb_core.o
OBJS += usb_hcd.o
OBJS += usb_hcd_int.o
OBJS += usbh_core.o
OBJS += usbh_hcs.o
OBJS += usbh_ioreq.o
OBJS += usbh_msc_bot.o
OBJS += usbh_msc_core.o
OBJS += usbh_msc_scsi.o
OBJS += usbh_stdreq.o
CFLAGS += -I$(ROOT)/src/usb/stm32_usbh_msc/inc/ -include usbh_conf.h

View file

@ -59,6 +59,11 @@ pin34 = auto
# Values: yes | no # Values: yes | no
write-protect = no write-protect = no
# Maximum cylinder that can be stepped to (255 is required for access to
# Direct Access mode as used by image-selector utilities and Autoswap games).
# Values: 0 <= N <= 255
max-cyl = 255
# Filter glitches in the SIDE-select signal shorter than N microseconds # Filter glitches in the SIDE-select signal shorter than N microseconds
# Values: 0 <= N <= 255 # Values: 0 <= N <= 255
side-select-glitch-filter = 0 side-select-glitch-filter = 0
@ -194,10 +199,12 @@ indexed-prefix = "DSKA"
# lcd-CCxRR: CCxRR backlit LCD with I2C backpack (16<=CC<=40, 02<=RR<=04) # lcd-CCxRR: CCxRR backlit LCD with I2C backpack (16<=CC<=40, 02<=RR<=04)
# oled-128xNN: 128xNN I2C OLED (NN = 32 | 64) # oled-128xNN: 128xNN I2C OLED (NN = 32 | 64)
# -rotate: OLED view is rotated 180 degrees # -rotate: OLED view is rotated 180 degrees
# -hflip: OLED view is flipped horizontally
# -narrow[er]: OLED view is restricted to Gotek display cutout # -narrow[er]: OLED view is restricted to Gotek display cutout
# (-narrow: 18 chars; -narrower: 16 chars) # (-narrow: 18 chars; -narrower: 16 chars)
# -inverse: Inverse/reverse video (black text on white background) # -inverse: Inverse/reverse video (black text on white background)
# -ztech: ZHONGJY_TECH 2.23" 128x32 SSD1305 OLED display # -ztech: ZHONGJY_TECH 2.23" 128x32 SSD1305 OLED display
# -slow: Run I2C bus slower (use this if OLED regularly blanks/corrupts)
# Values: auto | lcd-CCxRR | oled-128xNN[-rotate][-narrow[er]]... # Values: auto | lcd-CCxRR | oled-128xNN[-rotate][-narrow[er]]...
display-type = auto display-type = auto
@ -212,7 +219,7 @@ oled-font = 6x13
# Values: 0 <= N <= 255 # Values: 0 <= N <= 255
oled-contrast = 143 oled-contrast = 143
# Text height and arrangement on LCD/OLED # 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). # '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] # Each list item is a digit plus optional height specifier: <content-row>[d]
# content-row: '0-3' = specified content row, '7' = blank # content-row: '0-3' = specified content row, '7' = blank
@ -224,6 +231,11 @@ oled-contrast = 143
# 'default' depends on display, eg.: oled-128x32='0,1' ; oled-128x64='3,0d,1' # 'default' depends on display, eg.: oled-128x32='0,1' ; oled-128x64='3,0d,1'
# Values: [0-7][d] | default # Values: [0-7][d] | default
display-order = default display-order = default
osd-display-order = default
# OSD text columns. This is currently respected only when no LCD/OLED is found.
# Values: 16 <= N <= 40
osd-columns = 40
# Turn an LCD or OLED display off after N seconds of inactivity # Turn an LCD or OLED display off after N seconds of inactivity
# N=0: always off; N=255: always on # N=0: always off; N=255: always on
@ -261,6 +273,14 @@ nav-scroll-pause = 300
# Values: 0 <= N <= 20 # Values: 0 <= N <= 20
step-volume = 10 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 # Report the specified version number to host software
# Values: <quoted-string> ("" means report real version) # Values: <quoted-string> ("" means report real version)
# eg. da-report-version = "v3.0.0.0" # 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=ensonize
[::450560]
cyls = 80
heads = 1
secs = 6
bps = 1024,1024,1024,1024,1024,512
id = 0
rate = 250

View file

@ -11,7 +11,7 @@ cyls = 40
heads = 1 heads = 1
secs = 10 secs = 10
bps = 256 bps = 256
skew = 2 cskew = 2
mode = fm mode = fm
iam = no iam = no
@ -35,6 +35,6 @@ cyls = 40
heads = 2 heads = 2
secs = 5 secs = 5
bps = 1024 bps = 1024
skew = 2 cskew = 2
mode = mfm mode = mfm
iam = yes iam = yes

View file

@ -37,3 +37,13 @@ tracks = 0-79 # This line can be adjusted
interleave = 2 interleave = 2
hskew = 1 hskew = 1
cskew = 2 cskew = 2
# 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

@ -34,6 +34,9 @@ cyls = 80
# Mandatory: Number of heads (1-2). # Mandatory: Number of heads (1-2).
heads = 2 heads = 2
# Number of drive-head steps between cylinders. Default is 1.
# step = 1
# Image file track layout. Default is "interleaved". # Image file track layout. Default is "interleaved".
# Comma-separated values: # Comma-separated values:
# sequential: Sequential cylinder ordering: all side 0, then side 1. # sequential: Sequential cylinder ordering: all side 0, then side 1.
@ -66,6 +69,15 @@ secs = 9
# Mandatory if @secs is non-zero. # Mandatory if @secs is non-zero.
bps = 512 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. # ID of first sector on each track (0-255). Default is 1.
# Numbers may be expressed in hexadecimal with 0x prefix (eg. 0xab). # Numbers may be expressed in hexadecimal with 0x prefix (eg. 0xab).
# id = 1 # id = 1
@ -89,6 +101,10 @@ bps = 512
# Rotational RPM. Default is 300. # Rotational RPM. Default is 300.
# rpm = 300 # rpm = 300
# Post-ID Gap (auto|0-255). Default is auto.
# auto = based on recording mode and sector size.
# gap2 = auto
# Post-Data Gap (auto|0-255). Default is auto. # Post-Data Gap (auto|0-255). Default is auto.
# auto = based on recording mode and sector size. # auto = based on recording mode and sector size.
# gap3 = auto # gap3 = auto

View file

@ -9,14 +9,30 @@
* See the file COPYING for more details, or visit <http://unlicense.org>. * See the file COPYING for more details, or visit <http://unlicense.org>.
*/ */
#if !defined(BOOTLOADER) /* Use memory range (@start,@end) to cache data items of size @item_sz.
* For non-NULL return, @entry_cnt will be set to number of entries available.
/* Use memory range (@start,@end) to cache data items of size @item_sz. */ */
struct cache *cache_init(void *start, void *end, unsigned int item_sz); struct cache *cache_init(void *start, void *end, unsigned int item_sz,
unsigned int *entry_cnt);
/* Look up item @id in the cache. Return a pointer to cached data, or NULL. */ /* Look up item @id in the cache. Return a pointer to cached data, or NULL. */
const void *cache_lookup(struct cache *c, uint32_t id); const void *cache_lookup(struct cache *c, uint32_t id);
/* Look up item @id in the cache. Return a pointer to cached data, or NULL. */
void *cache_lookup_mutable(struct cache *c, uint32_t id);
/* Returns the item id and cached data of the entry that might be evicted by
* the next cache_update. Returns NULL if no entry might be evicted. */
void *cache_lru_mutable(struct cache *c, uint32_t *id);
/* Returns the item id and cache data of the next entry that might be evicted
* after @ent. Returns NULL if @ent is the most recent entry. */
void *cache_lru_next_mutable(struct cache *c, const void* ent, uint32_t *id);
/* Returns the item id and cached data of the LRU entry, even if it is not
* nearing eviction. Returns NULL if no entries are in the cache. */
void *cache_lru_search_mutable(struct cache *c, uint32_t *id);
/* Update item @id with data @dat. Inserts the item if not present.*/ /* Update item @id with data @dat. Inserts the item if not present.*/
void cache_update(struct cache *c, uint32_t id, const void *dat); void cache_update(struct cache *c, uint32_t id, const void *dat);
@ -24,14 +40,9 @@ void cache_update(struct cache *c, uint32_t id, const void *dat);
void cache_update_N(struct cache *c, uint32_t id, void cache_update_N(struct cache *c, uint32_t id,
const void *dat, unsigned int N); const void *dat, unsigned int N);
#else /* Update item @id using returned pointer to item. Creates an uninitialized
* item if not present, and sets @created to true. */
#define cache_init(a,b,c) NULL void *cache_update_mutable(struct cache *c, uint32_t id, bool_t *created);
#define cache_lookup(a,b) NULL
#define cache_update(a,b,c) ((void)0)
#define cache_update_N(a,b,c,d) ((void)0)
#endif
/* /*
* Local variables: * Local variables:

View file

@ -101,6 +101,8 @@ struct packed ff_cfg {
#define DISPLAY_ztech (1<<4) #define DISPLAY_ztech (1<<4)
#define DISPLAY_oled_64 (1<<5) #define DISPLAY_oled_64 (1<<5)
#define DISPLAY_inverse (1<<6) #define DISPLAY_inverse (1<<6)
#define DISPLAY_slow (1<<7)
#define DISPLAY_hflip (1<<8)
/* Only if DISPLAY_lcd: */ /* Only if DISPLAY_lcd: */
#define _DISPLAY_lcd_columns 5 #define _DISPLAY_lcd_columns 5
#define DISPLAY_lcd_columns(x) ((x)<<_DISPLAY_lcd_columns) #define DISPLAY_lcd_columns(x) ((x)<<_DISPLAY_lcd_columns)
@ -162,6 +164,12 @@ struct packed ff_cfg {
#define WDRAIN_realtime 1 #define WDRAIN_realtime 1
#define WDRAIN_eot 2 #define WDRAIN_eot 2
uint8_t write_drain; uint8_t write_drain;
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; extern struct ff_cfg ff_cfg;

View file

@ -15,8 +15,17 @@
#include <stddef.h> #include <stddef.h>
#include <limits.h> #include <limits.h>
#include "stm32f10x_regs.h" #include "types.h"
#include "stm32f10x.h" #include "mcu/common_regs.h"
#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"
#include "mcu/at32f435.h"
#endif
#include "intrinsics.h" #include "intrinsics.h"
#include "time.h" #include "time.h"
@ -29,7 +38,10 @@
#include "cancellation.h" #include "cancellation.h"
#include "spi.h" #include "spi.h"
#include "timer.h" #include "timer.h"
#include "thread.h"
#include "file_cache.h"
#include "fs.h" #include "fs.h"
#include "fs_async.h"
#include "floppy.h" #include "floppy.h"
#include "volume.h" #include "volume.h"
#include "config.h" #include "config.h"

74
inc/file_cache.h Normal file
View file

@ -0,0 +1,74 @@
/*
* file_cache.h
*
* Caching I/O for a single file.
*
* Written & released by Keir Fraser <keir.xen@gmail.com> and Eric Anderson
* <ejona86@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>.
*/
/* The cache is a write-back cache and uses an I/O scheduler to schedule reads
* and writes, with preference given to reads. Cache tracking is per-sector of
* 512 bytes.
*
* The cache tries to batch reads and writes into batches of maximum size
* 'batch_secs'. The file is split into aligned groups of batch_secs, and
* batching cannot cross the boundary of groups. A read batch starts from the
* sector requested and ends at the first already-read sector or end of the
* group. Writes are not delayed to form a batch, but batches form if there are
* delays due to reads or slow writes.
*
* In addition to batch reads, readahead can be enabled via
* file_cache_readahead(). Reads and writes within the provided region will
* cause the scheduler to read additional sectors, wrapping around when getting
* to the end of the region.
*/
struct file_cache;
struct file_cache *file_cache_init(FIL *fp, uint8_t batch_secs,
void *start, void *end);
/* Waits until written data is flushed and synced to storage. */
void file_cache_sync_wait(struct file_cache *fcache);
/* Stops scheduling I/O and waits for outstanding I/O to complete. To ensure
* data is not lost, use file_cache_sync_wait() first. */
void file_cache_shutdown(struct file_cache *fcache);
/* Run the I/O scheduler without requesting new I/O. Necessary for periods
* without I/O operations for writing and readahead. */
void file_cache_progress(struct file_cache *fcache);
/* Limit I/O operation size potentially below that of batch_sec. 0 disables the
* limit. */
void file_cache_io_limit(struct file_cache *fcache, uint8_t io_max);
/* Enable readahead for I/O ops within the specified region. @prio bytes are
* higher priority than writes. */
void file_cache_readahead(
struct file_cache *fcache, FSIZE_t ofs, UINT btr, UINT prio);
/* Read within a sector. Will block until data is read. */
void file_cache_read(struct file_cache *fcache, void *buf, FSIZE_t ofs,
UINT btr);
/* Read within a sector. If FALSE, try again later. */
bool_t file_cache_try_read(struct file_cache *fcache, void *buf, FSIZE_t ofs,
UINT btr);
/* Read 512 bytes at sector-aligned @ofs. Returns NULL if read is not yet
* available. */
const void *file_cache_peek_read(struct file_cache *fcache, FSIZE_t ofs);
/* Write within a sector. May block waiting on cache space, or if a partial
* sector is being written and the sector data is not already cached. */
void file_cache_write(struct file_cache *fcache, const void *buf,
FSIZE_t ofs, UINT btw);
/* Write within a sector. If FALSE, try again later. */
bool_t file_cache_try_write(struct file_cache *fcache, const void *buf,
FSIZE_t ofs, UINT btw);
/* Read 512 bytes at sector-aligned @ofs . Returns NULL if
* the write is not yet possible. If the return is non-NULL, data written to
* the buffer is observed by the next write or file_cache_sync(). Reads are
* not permitted until the written data is observed. */
void *file_cache_peek_write(struct file_cache *fcache, FSIZE_t ofs);
/* Flush filesystem cached data for file. Does not wait for it to complete. */
void file_cache_sync(struct file_cache *fcache);

View file

@ -9,6 +9,13 @@
* See the file COPYING for more details, or visit <http://unlicense.org>. * See the file COPYING for more details, or visit <http://unlicense.org>.
*/ */
#define SAMPLECLK_MHZ 72
#define sampleclk_ns(x) (((x) * SAMPLECLK_MHZ) / 1000)
#define sampleclk_us(x) ((x) * SAMPLECLK_MHZ)
#define sampleclk_ms(x) ((x) * SAMPLECLK_MHZ * 1000)
#define sampleclk_stk(x) ((x) * (SAMPLECLK_MHZ / STK_MHZ))
#define stk_sampleclk(x) ((x) / (SAMPLECLK_MHZ / STK_MHZ))
#ifdef QUICKDISK #ifdef QUICKDISK
#define is_quickdisk TRUE #define is_quickdisk TRUE
#else #else
@ -33,45 +40,39 @@
#define verbose_image_log FALSE #define verbose_image_log FALSE
struct image_buf {
void *p;
uint32_t len;
uint32_t prod, cons;
};
struct adf_image { struct adf_image {
struct file_cache *fcache;
uint32_t trk_off; uint32_t trk_off;
uint32_t sec_idx; uint32_t sec_idx;
int32_t decode_pos; int32_t decode_pos;
uint32_t pre_idx_gap_bc; uint32_t pre_idx_gap_bc;
uint32_t nr_secs; uint32_t nr_secs;
uint32_t written_secs; uint32_t written_secs;
uint16_t trash_bc; /* Number of bitcells to throw away. */
uint8_t sec_map[2][22]; uint8_t sec_map[2][22];
}; };
struct hfe_image { struct hfe_image {
struct file_cache *fcache;
uint16_t tlut_base; uint16_t tlut_base;
uint16_t trk_off; uint16_t trk_off;
uint16_t trk_pos, trk_len; uint16_t trk_pos, trk_len;
bool_t is_v3, double_step; bool_t is_v3, double_step, fresh_seek;
uint8_t batch_secs; uint8_t next_index_pulses_pos;
struct {
uint16_t start;
bool_t wrapped;
} write;
struct {
uint16_t off, len;
bool_t dirty;
} write_batch;
}; };
struct qd_image { struct qd_image {
struct file_cache *fcache;
uint16_t tb; uint16_t tb;
uint32_t trk_off; uint32_t trk_off;
uint32_t trk_pos, trk_len; uint32_t trk_pos, trk_len;
uint32_t win_start, win_end; uint32_t win_start, win_end;
struct {
uint32_t start;
bool_t wrapped;
} write;
struct {
uint32_t off, len;
bool_t dirty;
} write_batch;
}; };
struct raw_sec { struct raw_sec {
@ -84,6 +85,7 @@ struct raw_trk {
uint16_t sec_off; uint16_t sec_off;
uint16_t data_rate; uint16_t data_rate;
uint16_t rpm; uint16_t rpm;
uint16_t img_bps; /* could squeeze this field into uint8_t or bitfield */
int16_t gap_2, gap_3, gap_4a; int16_t gap_2, gap_3, gap_4a;
uint8_t interleave, cskew, hskew; uint8_t interleave, cskew, hskew;
uint8_t has_iam:1, is_fm:1, invert_data:1; uint8_t has_iam:1, is_fm:1, invert_data:1;
@ -92,7 +94,11 @@ struct raw_trk {
}; };
struct img_image { struct img_image {
struct file_cache *fcache;
uint32_t trk_off, base_off; uint32_t trk_off, base_off;
/* Length on-disk that encompases all track data. May contain other data
* (e.g., the other side of the cylinder). */
uint32_t trk_len;
uint16_t trk_sec, rd_sec_pos; uint16_t trk_sec, rd_sec_pos;
int32_t decode_pos; int32_t decode_pos;
uint16_t decode_data_pos, crc; uint16_t decode_data_pos, crc;
@ -108,12 +114,14 @@ struct img_image {
/* Delay start of track this many bitcells past index. */ /* Delay start of track this many bitcells past index. */
uint32_t track_delay_bc; uint32_t track_delay_bc;
uint16_t gap_4; uint16_t gap_4;
uint16_t trash_bc; /* Number of bitcells to throw away. */
uint32_t idx_sz, idam_sz; uint32_t idx_sz, idam_sz;
uint16_t dam_sz_pre, dam_sz_post; uint16_t dam_sz_pre, dam_sz_post;
void *heap_bottom; void *heap_bottom;
}; };
struct dsk_image { struct dsk_image {
struct file_cache *fcache;
uint32_t trk_off; uint32_t trk_off;
uint16_t trk_pos; uint16_t trk_pos;
uint16_t rd_sec_pos; uint16_t rd_sec_pos;
@ -122,6 +130,7 @@ struct dsk_image {
bool_t extended; bool_t extended;
int8_t write_sector; int8_t write_sector;
uint16_t gap4; uint16_t gap4;
uint16_t trash_bc; /* Number of bitcells to throw away. */
uint32_t idx_sz, idam_sz; uint32_t idx_sz, idam_sz;
uint16_t dam_sz_pre, dam_sz_post; uint16_t dam_sz_pre, dam_sz_post;
uint8_t rev; uint8_t rev;
@ -132,12 +141,14 @@ struct directaccess {
int32_t decode_pos; int32_t decode_pos;
uint16_t trk_sec; uint16_t trk_sec;
uint16_t idx_sz, idam_sz, dam_sz; uint16_t idx_sz, idam_sz, dam_sz;
}; uint16_t trash_bc; /* Number of bitcells to throw away. */
uint8_t *rd_buf;
struct image_buf { FOP io_op;
void *p; struct image_buf write_buffer;
uint32_t len; LBA_t *write_offsets; /* Disk offset of each 512 byte buffer segment */
uint32_t prod, cons; uint8_t write_cnt;
uint8_t sync_state;
bool_t lba_set;
}; };
struct image_bufs { struct image_bufs {
@ -151,6 +162,8 @@ struct image_bufs {
struct image_buf read_data; struct image_buf read_data;
}; };
#define MAX_CUSTOM_PULSES 34 /* 33+1 for minor track misalignment */
struct image { struct image {
/* Handler for currently-selected type of disk image. */ /* Handler for currently-selected type of disk image. */
const struct image_handler *disk_handler; const struct image_handler *disk_handler;
@ -163,6 +176,12 @@ struct image {
/* Info about image as a whole. */ /* Info about image as a whole. */
uint8_t nr_cyls, nr_sides; uint8_t nr_cyls, nr_sides;
uint8_t step;
uint8_t index_pulses_ver; /* Changed when pulses or length changed. */
uint8_t index_pulses_len;
/* Alternative index pulse locations, in ticks. When non-empty, disables
* regular index pulse. */
uint32_t index_pulses[MAX_CUSTOM_PULSES];
/* Data buffers. */ /* Data buffers. */
struct image_bufs bufs; struct image_bufs bufs;
@ -177,7 +196,7 @@ struct image {
/* Info about current track. */ /* Info about current track. */
uint16_t cur_track; uint16_t cur_track;
uint16_t write_bc_ticks; /* Nr SYSCLK ticks per bitcell in write stream */ uint16_t write_bc_ticks; /* SAMPLECLK ticks per bitcell in write stream */
uint32_t ticks_per_cell; /* Nr 'ticks' per bitcell in read stream. */ uint32_t ticks_per_cell; /* Nr 'ticks' per bitcell in read stream. */
uint32_t tracklen_bc, cur_bc; /* Track length and cursor, in bitcells */ uint32_t tracklen_bc, cur_bc; /* Track length and cursor, in bitcells */
uint32_t tracklen_ticks; /* Timing of previous revolution, in 'ticks' */ uint32_t tracklen_ticks; /* Timing of previous revolution, in 'ticks' */
@ -212,6 +231,7 @@ struct image_handler {
bool_t (*read_track)(struct image *im); bool_t (*read_track)(struct image *im);
uint16_t (*rdata_flux)(struct image *im, uint16_t *tbuf, uint16_t nr); uint16_t (*rdata_flux)(struct image *im, uint16_t *tbuf, uint16_t nr);
bool_t (*write_track)(struct image *im); bool_t (*write_track)(struct image *im);
void (*sync)(struct image *im);
}; };
/* List of supported image types. */ /* List of supported image types. */
@ -224,19 +244,21 @@ extern const struct image_type {
bool_t image_valid(FILINFO *fp); bool_t image_valid(FILINFO *fp);
/* Open specified image file on mass storage device. */ /* Open specified image file on mass storage device. */
void image_open(struct image *im, struct slot *slot, DWORD *cltbl); void image_open(struct image *im, struct slot *slot, DWORD *cltbl,
bool_t da_mode);
/* Returns TRUE if opened in Direct Access mode. */
bool_t image_in_da_mode(struct image *im);
/* Extend a trunated image file. */ /* Extend a trunated image file. */
void image_extend(struct image *im); void image_extend(struct image *im);
/* Seek to given track and start reading track data at specified rotational /* Seek to given track and start reading track data at specified rotational
* position (specified as number of SYSCLK ticks past the index mark). * position (specified as number of SAMPLECLK ticks past the index mark).
* *
* If start_pos is NULL then the caller is in write mode and thus is not * If start_pos is NULL then the caller is in write mode and thus is not
* interested in fetching data from a particular rotational position. * interested in fetching data from a particular rotational position. */
* void image_setup_track(
* Returns TRUE if the config file needs to be re-read (exiting D-A mode). */
bool_t image_setup_track(
struct image *im, uint16_t track, uint32_t *start_pos); struct image *im, uint16_t track, uint32_t *start_pos);
/* Read track data into memory. Returns TRUE if any new data was read. */ /* Read track data into memory. Returns TRUE if any new data was read. */
@ -250,7 +272,11 @@ uint16_t bc_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr);
* was completed for the write at the tail of the pipeline. */ * was completed for the write at the tail of the pipeline. */
bool_t image_write_track(struct image *im); bool_t image_write_track(struct image *im);
/* Rotational position of last-generated flux (SYSCLK ticks past index). */ /* Sync track data from memory to mass storage. Only useful to call when normal
* processing may be interrupted, like before floppy_cancel(). */
void image_sync(struct image *im);
/* Rotational position of last-generated flux (SAMPLECLK ticks past index). */
uint32_t image_ticks_since_index(struct image *im); uint32_t image_ticks_since_index(struct image *im);
/* MFM conversion. */ /* MFM conversion. */
@ -271,6 +297,7 @@ uint16_t fm_sync(uint8_t dat, uint8_t clk);
void floppy_init(void); void floppy_init(void);
bool_t floppy_ribbon_is_reversed(void); bool_t floppy_ribbon_is_reversed(void);
void floppy_insert(unsigned int unit, struct slot *slot); void floppy_insert(unsigned int unit, struct slot *slot);
void floppy_sync(void);
void floppy_cancel(void); void floppy_cancel(void);
bool_t floppy_handle(void); /* TRUE -> re-read config file */ bool_t floppy_handle(void); /* TRUE -> re-read config file */
void floppy_set_cyl(uint8_t unit, uint8_t cyl); void floppy_set_cyl(uint8_t unit, uint8_t cyl);
@ -279,11 +306,19 @@ struct track_info {
}; };
void floppy_get_track(struct track_info *ti); void floppy_get_track(struct track_info *ti);
void floppy_set_fintf_mode(void); void floppy_set_fintf_mode(void);
void floppy_set_max_cyl(void);
static inline unsigned int im_nphys_cyls(struct image *im)
{
return min_t(unsigned int, im->nr_cyls * (im->step?:1), 255);
}
static inline bool_t in_da_mode(struct image *im, unsigned int cyl) static inline bool_t in_da_mode(struct image *im, unsigned int cyl)
{ {
return cyl >= max_t(unsigned int, DA_FIRST_CYL, im->nr_cyls); return cyl >= max_t(unsigned int, DA_FIRST_CYL, im_nphys_cyls(im));
} }
extern uint32_t motor_chgrst_exti_mask;
void motor_chgrst_setup_exti(void);
/* /*
* Local variables: * Local variables:
* mode: C * mode: C

46
inc/fs_async.h Normal file
View file

@ -0,0 +1,46 @@
/*
* fs_async.h
*
* Non-blocking error-handling wrappers around FatFS.
*
* Written & released by Keir Fraser <keir.xen@gmail.com> and Eric Anderson
* <ejona86@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>.
*/
#include "../src/fatfs/diskio.h"
typedef int FOP;
FOP F_lseek_async(FIL *fp, FSIZE_t ofs);
FOP F_read_async(FIL *fp, void *buff, UINT btr, UINT *br);
FOP F_write_async(FIL *fp, const void *buff, UINT btw, UINT *bw);
FOP F_sync_async(FIL *fp);
FOP disk_read_async(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count);
FOP disk_write_async(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count);
/* If res == NULL, then it will F_die() if result of ioctl != RES_OK. */
FOP disk_ioctl_async(BYTE pdrv, BYTE cmd, void* buff, DRESULT *res);
/* Returns TRUE if oper has completed or is cancelled. */
bool_t F_async_isdone(FOP oper);
/* Blocks until oper completes or is cancelled. */
void F_async_wait(FOP oper);
/* Requests oper be cancelled. A cancellation may not take effect immediately.
* Has no effect when called on a completed oper.
*/
void F_async_cancel(FOP oper);
/* Requests all operations to be cancelled. A cancellation may not take effect
* immediately. */
void F_async_cancel_all(void);
/* Return a completed or cancelled op. Can be used as a "fake" op that is safe
* to wait or be cancelled. */
FOP F_async_get_completed_op(void);
/* Executes async operations until none remain. */
void F_async_drain(void);

View file

@ -74,6 +74,14 @@ struct exception_frame {
* confirmed on Cortex-M3. */ * confirmed on Cortex-M3. */
#define IRQ_restore(oldpri) write_special(basepri, (oldpri)) #define IRQ_restore(oldpri) write_special(basepri, (oldpri))
#define __DEFINE_IRQ(nr, name) \
void IRQ_##nr (void) __attribute__((alias(name)))
#define _DEFINE_IRQ(nr, name) __DEFINE_IRQ(nr, name)
#define DEFINE_IRQ(nr, name) _DEFINE_IRQ(nr, name)
/* Cortex initialisation */
void cortex_init(void);
static inline uint16_t _rev16(uint16_t x) static inline uint16_t _rev16(uint16_t x)
{ {
uint16_t result; uint16_t result;

View file

@ -41,7 +41,7 @@ static inline void list_remove(struct list_head *ent)
ent->prev->next = ent->next; ent->prev->next = ent->next;
} }
static inline bool_t list_is_empty(struct list_head *head) static inline bool_t list_is_empty(const struct list_head *head)
{ {
return head->next == head; return head->next == head;
} }

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:
*/

80
inc/mcu/at32f435.h Normal file
View file

@ -0,0 +1,80 @@
/*
* at32f435.h
*
* Core and peripheral registers.
*
* 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>.
*/
/* C pointer types */
#define FLASH_BANK volatile struct flash_bank * const
#define SYSCFG volatile struct syscfg * const
#define DMAMUX volatile struct dmamux * const
/* C-accessible registers. */
static STK stk = (struct stk *)STK_BASE;
static SCB scb = (struct scb *)SCB_BASE;
static NVIC nvic = (struct nvic *)NVIC_BASE;
static DBG dbg = (struct dbg *)DBG_BASE;
static FLASH flash = (struct flash *)FLASH_BASE;
static PWR pwr = (struct pwr *)PWR_BASE;
static BKP bkp = (struct bkp *)BKP_BASE;
static RCC rcc = (struct rcc *)RCC_BASE;
static GPIO gpioa = (struct gpio *)GPIOA_BASE;
static GPIO gpiob = (struct gpio *)GPIOB_BASE;
static GPIO gpioc = (struct gpio *)GPIOC_BASE;
static GPIO gpiod = (struct gpio *)GPIOD_BASE;
static GPIO gpioe = (struct gpio *)GPIOE_BASE;
static GPIO gpiof = (struct gpio *)GPIOF_BASE;
static GPIO gpiog = (struct gpio *)GPIOG_BASE;
static GPIO gpioh = (struct gpio *)GPIOH_BASE;
static SYSCFG syscfg = (struct syscfg *)SYSCFG_BASE;
static EXTI exti = (struct exti *)EXTI_BASE;
static DMA dma1 = (struct dma *)DMA1_BASE;
static DMA dma2 = (struct dma *)DMA2_BASE;
static DMAMUX dmamux1 = (struct dmamux *)DMAMUX1_BASE;
static DMAMUX dmamux2 = (struct dmamux *)DMAMUX2_BASE;
static TIM tim1 = (struct tim *)TIM1_BASE;
static TIM tim2 = (struct tim *)TIM2_BASE;
static TIM tim3 = (struct tim *)TIM3_BASE;
static TIM tim4 = (struct tim *)TIM4_BASE;
static TIM tim5 = (struct tim *)TIM5_BASE;
static TIM tim6 = (struct tim *)TIM6_BASE;
static TIM tim7 = (struct tim *)TIM7_BASE;
static SPI spi1 = (struct spi *)SPI1_BASE;
static SPI spi2 = (struct spi *)SPI2_BASE;
static SPI spi3 = (struct spi *)SPI3_BASE;
static I2C i2c1 = (struct i2c *)I2C1_BASE;
static I2C i2c2 = (struct i2c *)I2C2_BASE;
static USART usart1 = (struct usart *)USART1_BASE;
static USART usart2 = (struct usart *)USART2_BASE;
static USART usart3 = (struct usart *)USART3_BASE;
static USB_OTG usb_otg = (struct usb_otg *)USB_OTG_BASE;
/* Clocks */
#define SYSCLK_MHZ 288
#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);
#define SOFTIRQ_0 85
#define SOFTIRQ_1 86
extern volatile uint32_t _reset_flag;
#define RESET_FLAG_BOOTLOADER 0xdeadbeefu
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

421
inc/mcu/at32f435_regs.h Normal file
View file

@ -0,0 +1,421 @@
/*
* at32f435_regs.h
*
* Core and peripheral register definitions.
*
* 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>.
*/
/* Power control */
struct pwr {
uint32_t cr; /* 00: Power control */
uint32_t csr; /* 04: Power control/status */
uint32_t rsvd[2];
uint32_t ldoov; /* 10: LDO output voltage */
};
#define PWR_LDOOV_1V3 (1u<<0)
#define PWR_BASE 0x40007000
/* Flash memory interface */
struct flash_bank {
uint32_t sr; /* +00: Flash status */
uint32_t cr; /* +04: Flash control */
uint32_t ar; /* +08: Flash address */
};
struct flash {
uint32_t psr; /* 00: Performance select */
uint32_t unlock1; /* 04: Bank 1 unlock */
uint32_t opt_unlock;/* 08: Option bytes unlock */
struct flash_bank bank1;
uint32_t rsvd; /* 18: - */
uint32_t obr; /* 1C: Option byte */
uint32_t epps0; /* 20: Erase/program protection */
uint32_t rsvd2[2]; /* 24-28: - */
uint32_t epps1; /* 2C: Erase/program protection */
uint32_t rsvd3[5]; /* 30-40: - */
uint32_t unlock2; /* 44: Bank 2 unlock */
uint32_t rsvd4; /* 48: - */
struct flash_bank bank2;
uint32_t contr; /* 58: Continue read */
uint32_t rsvd5; /* 5C: - */
uint32_t divr; /* 60: Divider */
};
#define FLASH_SR_EOP (1u<< 5)
#define FLASH_SR_WRPRTERR (1u<< 4)
#define FLASH_SR_PGERR (1u<< 2)
#define FLASH_SR_BSY (1u<< 0)
#define FLASH_CR_EOPIE (1u<<12)
#define FLASH_CR_ERRIE (1u<<10)
#define FLASH_CR_OPTWRE (1u<< 9)
#define FLASH_CR_LOCK (1u<< 7)
#define FLASH_CR_ERASE_STRT (1u<< 6)
#define FLASH_CR_OPTER (1u<< 5)
#define FLASH_CR_OPTPG (1u<< 4)
#define FLASH_CR_SEC_ER (1u<< 3)
#define FLASH_CR_BANK_ER (1u<< 2)
#define FLASH_CR_PG_ER (1u<< 1)
#define FLASH_CR_PG (1u<< 0)
#define FLASH_DIVR_DIV_2 (0u<< 0)
#define FLASH_DIVR_DIV_3 (1u<< 0)
#define FLASH_DIVR_DIV_4 (2u<< 0)
#define FLASH_BASE 0x40023c00
/* Reset and clock control */
struct rcc {
uint32_t cr; /* 00: Clock control */
uint32_t pllcfgr; /* 04: PLL configuration */
uint32_t cfgr; /* 08: Clock configuration */
uint32_t cir; /* 0C: Clock interrupt */
uint32_t ahb1rstr; /* 10: AHB1 peripheral reset */
uint32_t ahb2rstr; /* 14: AHB2 peripheral reset */
uint32_t ahb3rstr; /* 18: AHB3 peripheral reset */
uint32_t _unused0; /* 1C: - */
uint32_t apb1rstr; /* 20: APB1 peripheral reset */
uint32_t apb2rstr; /* 24: APB2 peripheral reset */
uint32_t _unused1; /* 28: - */
uint32_t _unused2; /* 2C: - */
uint32_t ahb1enr; /* 30: AHB1 peripheral clock enable */
uint32_t ahb2enr; /* 34: AHB2 peripheral clock enable */
uint32_t ahb3enr; /* 38: AHB3 peripheral clock enable */
uint32_t _unused3; /* 3C: - */
uint32_t apb1enr; /* 40: APB1 peripheral clock enable */
uint32_t apb2enr; /* 44: APB2 peripheral clock enable */
uint32_t _unused4; /* 48: - */
uint32_t _unused5; /* 4C: - */
uint32_t ahb1lpenr;/* 50: AHB1 peripheral clock enable (low-power mode) */
uint32_t ahb2lpenr;/* 54: AHB2 peripheral clock enable (low-power mode) */
uint32_t ahb3lpenr;/* 58: AHB3 peripheral clock enable (low-power mode) */
uint32_t _unused6; /* 5C: - */
uint32_t apb1lpenr;/* 60: APB1 peripheral clock enable (low-power mode) */
uint32_t apb2lpenr;/* 64: APB2 peripheral clock enable (low-power mode) */
uint32_t _unused7; /* 68: - */
uint32_t _unused8; /* 6C: - */
uint32_t bdcr; /* 70: Backup domain control */
uint32_t csr; /* 74: Clock control & status */
uint32_t _unused9[10]; /* 78-9C: - */
uint32_t misc1; /* A0: Misc 1 */
uint32_t misc2; /* A4: Misc 2 */
};
#define RCC_CR_PLLRDY (1u<<25)
#define RCC_CR_PLLON (1u<<24)
#define RCC_CR_CFDEN (1u<<19)
#define RCC_CR_HSEBYP (1u<<18)
#define RCC_CR_HSERDY (1u<<17)
#define RCC_CR_HSEON (1u<<16)
#define RCC_CR_HSIRDY (1u<<1)
#define RCC_CR_HSION (1u<<0)
#define RCC_PLLCFGR_PLLSRC_HSE (1<<22)
#define PLL_FR_2 1
#define RCC_PLLCFGR_PLL_FR(x) ((x)<<16)
#define RCC_PLLCFGR_PLL_NS(x) ((x)<< 6)
#define RCC_PLLCFGR_PLL_MS(x) ((x)<< 0)
#define RCC_CFGR_MCO2(x) ((x)<<30)
#define RCC_CFGR_MCO2PRE(x) ((x)<<27)
#define RCC_CFGR_USBPRE(x) ((x)<<24)
#define RCC_CFGR_MCO1(x) ((x)<<21)
#define RCC_CFGR_RTCPRE(x) ((x)<<16)
#define RCC_CFGR_PPRE2(x) ((x)<<13)
#define RCC_CFGR_PPRE1(x) ((x)<<10)
#define RCC_CFGR_HPRE(x) ((x)<< 4)
#define RCC_CFGR_SWS(x) ((x)<< 2)
#define RCC_CFGR_SW(x) ((x)<< 0)
#define RCC_AHB1ENR_OTGFS2EN (1u<<29)
#define RCC_AHB1ENR_DMA2EN (1u<<24)
#define RCC_AHB1ENR_DMA1EN (1u<<22)
#define RCC_AHB1ENR_EDMAEN (1u<<21)
#define RCC_AHB1ENR_CRCEN (1u<<12)
#define RCC_AHB1ENR_GPIOHEN (1u<< 7)
#define RCC_AHB1ENR_GPIOGEN (1u<< 6)
#define RCC_AHB1ENR_GPIOFEN (1u<< 5)
#define RCC_AHB1ENR_GPIOEEN (1u<< 4)
#define RCC_AHB1ENR_GPIODEN (1u<< 3)
#define RCC_AHB1ENR_GPIOCEN (1u<< 2)
#define RCC_AHB1ENR_GPIOBEN (1u<< 1)
#define RCC_AHB1ENR_GPIOAEN (1u<< 0)
#define RCC_AHB2ENR_OTGFS1EN (1u<< 7)
#define RCC_AHB2ENR_DVPEN (1u<< 0)
#define RCC_AHB3ENR_QSPI2EN (1u<<14)
#define RCC_AHB3ENR_QSPI1EN (1u<< 1)
#define RCC_AHB3ENR_XMCEN (1u<< 0)
#define RCC_APB1ENR_USART8EN (1u<<31)
#define RCC_APB1ENR_USART7EN (1u<<30)
#define RCC_APB1ENR_DACEN (1u<<29)
#define RCC_APB1ENR_PWREN (1u<<28)
#define RCC_APB1ENR_CAN1EN (1u<<25)
#define RCC_APB1ENR_I2C3EN (1u<<23)
#define RCC_APB1ENR_I2C2EN (1u<<22)
#define RCC_APB1ENR_I2C1EN (1u<<21)
#define RCC_APB1ENR_USART5EN (1u<<20)
#define RCC_APB1ENR_USART4EN (1u<<19)
#define RCC_APB1ENR_USART3EN (1u<<18)
#define RCC_APB1ENR_USART2EN (1u<<17)
#define RCC_APB1ENR_SPI3EN (1u<<15)
#define RCC_APB1ENR_SPI2EN (1u<<14)
#define RCC_APB1ENR_WWDGEN (1u<<11)
#define RCC_APB1ENR_TIM14EN (1u<< 8)
#define RCC_APB1ENR_TIM13EN (1u<< 7)
#define RCC_APB1ENR_TIM12EN (1u<< 6)
#define RCC_APB1ENR_TIM7EN (1u<< 5)
#define RCC_APB1ENR_TIM6EN (1u<< 4)
#define RCC_APB1ENR_TIM5EN (1u<< 3)
#define RCC_APB1ENR_TIM4EN (1u<< 2)
#define RCC_APB1ENR_TIM3EN (1u<< 1)
#define RCC_APB1ENR_TIM2EN (1u<< 0)
#define RCC_APB2ENR_ACCEN (1u<<29)
#define RCC_APB2ENR_TIM20EN (1u<<20)
#define RCC_APB2ENR_TIM11EN (1u<<18)
#define RCC_APB2ENR_TIM10EN (1u<<17)
#define RCC_APB2ENR_TIM9EN (1u<<16)
#define RCC_APB2ENR_SYSCFGEN (1u<<14)
#define RCC_APB2ENR_SPI4EN (1u<<13)
#define RCC_APB2ENR_SPI1EN (1u<<12)
#define RCC_APB2ENR_ADC3EN (1u<<10)
#define RCC_APB2ENR_ADC2EN (1u<< 9)
#define RCC_APB2ENR_ADC1EN (1u<< 8)
#define RCC_APB2ENR_USART6EN (1u<< 5)
#define RCC_APB2ENR_USART1EN (1u<< 4)
#define RCC_APB2ENR_TIM8EN (1u<< 1)
#define RCC_APB2ENR_TIM1EN (1u<< 0)
#define RCC_BDCR_BDRST (1u<<16)
#define RCC_BDCR_RTCEN (1u<<15)
#define RCC_BDCR_RTCSEL(x) ((x)<<8)
#define RCC_BDCR_LSEDRV(x) ((x)<<3)
#define RCC_BDCR_LSEBYP (1u<< 2)
#define RCC_BDCR_LSERDY (1u<< 1)
#define RCC_BDCR_LSEON (1u<< 0)
#define RCC_CSR_LPWRRSTF (1u<<31)
#define RCC_CSR_WWDGRSTF (1u<<30)
#define RCC_CSR_IWDGRSTF (1u<<29)
#define RCC_CSR_SFTRSTF (1u<<28)
#define RCC_CSR_PORRSTF (1u<<27)
#define RCC_CSR_PINRSTF (1u<<26)
#define RCC_CSR_BORRSTF (1u<<25)
#define RCC_CSR_RMVF (1u<<24)
#define RCC_CSR_LSIRDY (1u<< 1)
#define RCC_CSR_LSION (1u<< 0)
#define RCC_MISC2_USBDIV(x) ((x)<<12)
#define USBDIV_6 11
#define RCC_MISC2_AUTOSTEP (3u<< 4)
#define RCC_BASE 0x40023800
/* General-purpose I/O */
struct gpio {
uint32_t moder; /* 00: Port mode */
uint32_t otyper; /* 04: Port output type */
uint32_t odrvr; /* 08: Drive capability */
uint32_t pupdr; /* 0C: Port pull-up/pull-down */
uint32_t idr; /* 10: Port input data */
uint32_t odr; /* 14: Port output data */
uint32_t bsrr; /* 18: Port bit set/reset */
uint32_t lckr; /* 1C: Port configuration lock */
uint32_t afrl; /* 20: Alternate function low */
uint32_t afrh; /* 24: Alternate function high */
uint32_t brr; /* 28: Port bit reset */
uint32_t rsvd[4]; /* 2C-38 */
uint32_t hdrv; /* 3C: Huge current control */
};
/* 0-1: MODE, 2: OTYPE, 3-4:ODRV, 5-6:PUPD, 7:OUTPUT_LEVEL */
#define GPI_analog 0x3u
#define GPI(pupd) (0x0u|((pupd)<<5))
#define PUPD_none 0
#define PUPD_up 1
#define PUPD_down 2
#define GPI_floating GPI(PUPD_none)
#define GPI_pull_down GPI(PUPD_down)
#define GPI_pull_up GPI(PUPD_up)
#define GPO_pushpull(speed,level) (0x1u|((speed)<<3)|((level)<<7))
#define GPO_opendrain(speed,level) (0x5u|((speed)<<3)|((level)<<7))
#define AFI(pupd) (0x2u|((pupd)<<5))
#define AFO_pushpull(speed) (0x2u|((speed)<<3))
#define _AFO_pushpull(speed,level) (0x2u|((speed)<<3)|((level)<<7))
#define AFO_opendrain(speed) (0x6u|((speed)<<3))
#define _2MHz 0
#define _10MHz 0
#define _50MHz 0
#define LOW 0
#define HIGH 1
#define GPIOA_BASE 0x40020000
#define GPIOB_BASE 0x40020400
#define GPIOC_BASE 0x40020800
#define GPIOD_BASE 0x40020C00
#define GPIOE_BASE 0x40021000
#define GPIOF_BASE 0x40021400
#define GPIOG_BASE 0x40021800
#define GPIOH_BASE 0x40021C00
/* System configuration controller */
struct syscfg {
uint32_t cfg1; /* 00: Configuration 1 */
uint32_t cfg2; /* 04: Configuration 2 */
uint32_t exticr[4]; /* 08-14: External interrupt configuration #1-4 */
uint32_t _pad[5];
uint32_t uhdrv; /* 2C: Ultra high source/sink strength */
};
#define SYSCFG_BASE 0x40013800
/* EXTI */
#define EXTI_BASE 0x40013c00
/* DMA */
#define DMA1_CH1_IRQ 56
#define DMA1_CH2_IRQ 57
#define DMA1_CH3_IRQ 58
#define DMA1_CH4_IRQ 59
#define DMA1_CH5_IRQ 60
#define DMA1_CH6_IRQ 68
#define DMA1_CH7_IRQ 69
#define DMA1_BASE 0x40026400
#define DMA2_BASE 0x40026600
/* DMAMUX */
struct dmamux {
uint32_t sel; /* 00: Selection */
uint32_t cctrl[7]; /* 04-1C: Channel control */
uint32_t gctrl[4]; /* 20-2C: Generator control */
uint32_t sync_sts; /* 30: Channel synchronisation status */
uint32_t sync_clr; /* 34: Channel synchronisation clear */
uint32_t g_sts; /* 38: Generator interrupt status */
uint32_t g_clr; /* 3C: Generator interrupt clear */
};
#define DMAMUX_SEL_TBL_SEL (1u<< 0)
#define DMAMUX_CCTRL_REQSEL(x) ((x)<<0)
#define DMAMUX_REQ_I2C2_RX 18
#define DMAMUX_REQ_I2C2_TX 19
#define DMAMUX_REQ_TIM1_CH1 42
#define DMAMUX_REQ_TIM3_OVF 65
#define DMAMUX1_BASE (DMA1_BASE + 0x100)
#define DMAMUX2_BASE (DMA2_BASE + 0x100)
/* Timer */
#define TIM_CR1_PMEN (1u<<10)
#define TIM1_BASE 0x40010000
#define TIM2_BASE 0x40000000
#define TIM3_BASE 0x40000400
#define TIM4_BASE 0x40000800
#define TIM5_BASE 0x40000c00
#define TIM6_BASE 0x40001000
#define TIM7_BASE 0x40001400
#define TIM8_BASE 0x40010400
#define TIM9_BASE 0x40014000
#define TIM10_BASE 0x40014400
#define TIM11_BASE 0x40014800
#define TIM12_BASE 0x40001800
#define TIM13_BASE 0x40001c00
#define TIM14_BASE 0x40002000
/* I2C */
struct i2c {
uint32_t cr1; /* 00: Control 1 */
uint32_t cr2; /* 04: Control 2 */
uint32_t oar1; /* 08: Own address 1 */
uint32_t oar2; /* 0C: Own address 2 */
uint32_t timingr; /* 10: Timing */
uint32_t timeoutr;/* 14: Timeout */
uint32_t isr; /* 18: Interrupt status */
uint32_t icr; /* 1C: Interrupt clear */
uint32_t pecr; /* 20: PEC */
uint32_t rxdr; /* 24: Receive data */
uint32_t txdr; /* 28: Transmit data */
};
#define I2C_CR1_PECEN (1u<<23)
#define I2C_CR1_ALERTEN (1u<<22)
#define I2C_CR1_SMBDEN (1u<<21)
#define I2C_CR1_SMBHEN (1u<<20)
#define I2C_CR1_GCEN (1u<<19)
#define I2C_CR1_NOSTRETCH (1u<<17)
#define I2C_CR1_SBC (1u<<16)
#define I2C_CR1_RXDMAEN (1u<<15)
#define I2C_CR1_TXDMAEN (1u<<14)
#define I2C_CR1_ANFOFF (1u<<12)
#define I2C_CR1_DNF(x) ((x)<<8)
#define I2C_CR1_ERRIE (1u<< 7)
#define I2C_CR1_TCIE (1u<< 6)
#define I2C_CR1_STOPIE (1u<< 5)
#define I2C_CR1_NACKIE (1u<< 4)
#define I2C_CR1_ADDRIE (1u<< 3)
#define I2C_CR1_RXIE (1u<< 2)
#define I2C_CR1_TXIE (1u<< 1)
#define I2C_CR1_PE (1u<< 0)
#define I2C_CR2_PECBYTE (1u<<26)
#define I2C_CR2_AUTOEND (1u<<25)
#define I2C_CR2_RELOAD (1u<<24)
#define I2C_CR2_NBYTES(x) ((x)<<16)
#define I2C_CR2_NACK (1u<<15)
#define I2C_CR2_STOP (1u<<14)
#define I2C_CR2_START (1u<<13)
#define I2C_CR2_HEAD10R (1u<<12)
#define I2C_CR2_ADD10 (1u<<11)
#define I2C_CR2_RD_WRN (1u<<10)
#define I2C_CR2_SADD(x) ((x)<<0)
/* Based on 144MHz peripheral clock */
#define I2C_TIMING_100k 0x80504C4E
#define I2C_TIMING_400k 0x40301B28
#define I2C_SR_ERRORS 0x1f10
#define I2C_SR_BUSY (1u<<15)
#define I2C_SR_ALERT (1u<<13)
#define I2C_SR_TIMEOUT (1u<<12)
#define I2C_SR_PECERR (1u<<11)
#define I2C_SR_OVR (1u<<10)
#define I2C_SR_ARLO (1u<< 9)
#define I2C_SR_BERR (1u<< 8)
#define I2C_SR_TCR (1u<< 7)
#define I2C_SR_TC (1u<< 6)
#define I2C_SR_STOPF (1u<< 5)
#define I2C_SR_NACKF (1u<< 4)
#define I2C_SR_ADDR (1u<< 3)
#define I2C_SR_RXNE (1u<< 2)
#define I2C_SR_TXIS (1u<< 1)
#define I2C_SR_TXE (1u<< 0)
#define I2C1_BASE 0x40005400
#define I2C2_BASE 0x40005800
/* USART */
#define USART1_BASE 0x40011000
#define USART2_BASE 0x40004400
#define USART3_BASE 0x40004800
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

View file

@ -1,5 +1,5 @@
/* /*
* stm32f10x.h * common.h
* *
* Core and peripheral registers. * Core and peripheral registers.
* *
@ -13,12 +13,12 @@
#define STK volatile struct stk * const #define STK volatile struct stk * const
#define SCB volatile struct scb * const #define SCB volatile struct scb * const
#define NVIC volatile struct nvic * const #define NVIC volatile struct nvic * const
#define DBG volatile struct dbg * const
#define FLASH volatile struct flash * const #define FLASH volatile struct flash * const
#define PWR volatile struct pwr * const #define PWR volatile struct pwr * const
#define BKP volatile struct bkp * const #define BKP volatile struct bkp * const
#define RCC volatile struct rcc * const #define RCC volatile struct rcc * const
#define GPIO volatile struct gpio * const #define GPIO volatile struct gpio * const
#define AFIO volatile struct afio * const
#define EXTI volatile struct exti * const #define EXTI volatile struct exti * const
#define DMA volatile struct dma * const #define DMA volatile struct dma * const
#define TIM volatile struct tim * const #define TIM volatile struct tim * const
@ -27,51 +27,15 @@
#define USART volatile struct usart * const #define USART volatile struct usart * const
#define USB_OTG volatile struct usb_otg * const #define USB_OTG volatile struct usb_otg * const
/* C-accessible registers. */
static STK stk = (struct stk *)STK_BASE;
static SCB scb = (struct scb *)SCB_BASE;
static NVIC nvic = (struct nvic *)NVIC_BASE;
static FLASH flash = (struct flash *)FLASH_BASE;
static PWR pwr = (struct pwr *)PWR_BASE;
static BKP bkp = (struct bkp *)BKP_BASE;
static RCC rcc = (struct rcc *)RCC_BASE;
static GPIO gpioa = (struct gpio *)GPIOA_BASE;
static GPIO gpiob = (struct gpio *)GPIOB_BASE;
static GPIO gpioc = (struct gpio *)GPIOC_BASE;
static GPIO gpiod = (struct gpio *)GPIOD_BASE;
static GPIO gpioe = (struct gpio *)GPIOE_BASE;
static GPIO gpiof = (struct gpio *)GPIOF_BASE;
static GPIO gpiog = (struct gpio *)GPIOG_BASE;
static AFIO afio = (struct afio *)AFIO_BASE;
static EXTI exti = (struct exti *)EXTI_BASE;
static DMA dma1 = (struct dma *)DMA1_BASE;
static DMA dma2 = (struct dma *)DMA2_BASE;
static TIM tim1 = (struct tim *)TIM1_BASE;
static TIM tim2 = (struct tim *)TIM2_BASE;
static TIM tim3 = (struct tim *)TIM3_BASE;
static TIM tim4 = (struct tim *)TIM4_BASE;
static TIM tim5 = (struct tim *)TIM5_BASE;
static TIM tim6 = (struct tim *)TIM6_BASE;
static TIM tim7 = (struct tim *)TIM7_BASE;
static SPI spi1 = (struct spi *)SPI1_BASE;
static SPI spi2 = (struct spi *)SPI2_BASE;
static SPI spi3 = (struct spi *)SPI3_BASE;
static I2C i2c1 = (struct i2c *)I2C1_BASE;
static I2C i2c2 = (struct i2c *)I2C2_BASE;
static USART usart1 = (struct usart *)USART1_BASE;
static USART usart2 = (struct usart *)USART2_BASE;
static USART usart3 = (struct usart *)USART3_BASE;
static USB_OTG usb_otg = (struct usb_otg *)USB_OTG_BASE;
/* NVIC table */ /* NVIC table */
extern uint32_t vector_table[]; extern uint32_t vector_table[];
/* System */ /* System */
void stm32_init(void); void stm32_init(void);
void system_reset(void); void system_reset(void) __attribute__((noreturn));
extern bool_t is_artery_mcu;
/* Clocks */ /* Clocks */
#define SYSCLK_MHZ 72
#define SYSCLK (SYSCLK_MHZ * 1000000) #define SYSCLK (SYSCLK_MHZ * 1000000)
#define sysclk_ns(x) (((x) * SYSCLK_MHZ) / 1000) #define sysclk_ns(x) (((x) * SYSCLK_MHZ) / 1000)
#define sysclk_us(x) ((x) * SYSCLK_MHZ) #define sysclk_us(x) ((x) * SYSCLK_MHZ)
@ -113,6 +77,7 @@ typedef uint32_t stk_time_t;
#define IRQx_get_prio(x) (nvic->ipr[x] >> 4) #define IRQx_get_prio(x) (nvic->ipr[x] >> 4)
/* GPIO */ /* GPIO */
struct gpio;
void gpio_configure_pin(GPIO gpio, unsigned int pin, unsigned int mode); void gpio_configure_pin(GPIO gpio, unsigned int pin, unsigned int mode);
#define gpio_write_pin(gpio, pin, level) \ #define gpio_write_pin(gpio, pin, level) \
((gpio)->bsrr = ((level) ? 0x1u : 0x10000u) << (pin)) ((gpio)->bsrr = ((level) ? 0x1u : 0x10000u) << (pin))
@ -120,12 +85,23 @@ void gpio_configure_pin(GPIO gpio, unsigned int pin, unsigned int mode);
((gpio)->bsrr = (uint32_t)(mask) << ((level) ? 0 : 16)) ((gpio)->bsrr = (uint32_t)(mask) << ((level) ? 0 : 16))
#define gpio_read_pin(gpio, pin) (((gpio)->idr >> (pin)) & 1) #define gpio_read_pin(gpio, pin) (((gpio)->idr >> (pin)) & 1)
/* EXTI */
void _exti_route(unsigned int px, unsigned int pin);
#define exti_route_pa(pin) _exti_route(0, pin)
#define exti_route_pb(pin) _exti_route(1, pin)
#define exti_route_pc(pin) _exti_route(2, pin)
/* FPEC */ /* FPEC */
void fpec_init(void); void fpec_init(void);
void fpec_page_erase(uint32_t flash_address); void fpec_page_erase(uint32_t flash_address);
void fpec_write(const void *data, unsigned int size, uint32_t flash_address); void fpec_write(const void *data, unsigned int size, uint32_t flash_address);
#define FLASH_PAGE_SIZE 2048 #define FLASH_PAGE_SIZE 2048
extern unsigned int flash_page_size;
extern unsigned int ram_kb;
extern uint8_t mcu_package;
enum { MCU_LQFP64=0, MCU_LQFP48, MCU_QFN32 };
/* /*
* Local variables: * Local variables:

View file

@ -1,5 +1,5 @@
/* /*
* stm32f10x_regs.h * common_regs.h
* *
* Core and peripheral register definitions. * Core and peripheral register definitions.
* *
@ -97,56 +97,17 @@ struct nvic {
uint32_t ispr[32]; /* 100: Interrupt set-pending */ uint32_t ispr[32]; /* 100: Interrupt set-pending */
uint32_t icpr[32]; /* 180: Interrupt clear-pending */ uint32_t icpr[32]; /* 180: Interrupt clear-pending */
uint32_t iabr[64]; /* 200: Interrupt active */ uint32_t iabr[64]; /* 200: Interrupt active */
uint8_t ipr[80]; /* 300: Interrupt priority */ uint8_t ipr[100]; /* 300: Interrupt priority */
}; };
#define NVIC_BASE 0xe000e100 #define NVIC_BASE 0xe000e100
/* Flash memory interface */ struct dbg {
struct flash { uint32_t mcu_idcode; /* 00: MCU ID code */
uint32_t acr; /* 00: Flash access control */ uint32_t mcu_cr; /* 04: Debug MCU configuration */
uint32_t keyr; /* 04: FPEC key */
uint32_t optkeyr; /* 08: Flash OPTKEY */
uint32_t sr; /* 0C: Flash status */
uint32_t cr; /* 10: Flash control */
uint32_t ar; /* 14: Flash address */
uint32_t rsvd; /* 18: - */
uint32_t obr; /* 1C: Option byte */
uint32_t wrpr; /* 20: Write protection */
}; };
#define FLASH_ACR_PRFTBS (1u<< 5) #define DBG_BASE 0xe0042000
#define FLASH_ACR_PRFTBE (1u<< 4)
#define FLASH_ACR_HLFCYA (1u<< 3)
#define FLASH_ACR_LATENCY(w) ((w)<<0) /* wait states */
#define FLASH_SR_EOP (1u<< 5)
#define FLASH_SR_WRPRTERR (1u<< 4)
#define FLASH_SR_PGERR (1u<< 2)
#define FLASH_SR_BSY (1u<< 0)
#define FLASH_CR_EOPIE (1u<<12)
#define FLASH_CR_ERRIE (1u<<10)
#define FLASH_CR_OPTWRE (1u<< 9)
#define FLASH_CR_LOCK (1u<< 7)
#define FLASH_CR_STRT (1u<< 6)
#define FLASH_CR_OPTER (1u<< 5)
#define FLASH_CR_OPTPG (1u<< 4)
#define FLASH_CR_MER (1u<< 2)
#define FLASH_CR_PER (1u<< 1)
#define FLASH_CR_PG (1u<< 0)
#define FLASH_BASE 0x40022000
/* Power control */
struct pwr {
uint32_t cr; /* 00: Power control */
uint32_t csr; /* 04: Power control/status */
};
#define PWR_CR_DBP (1u<< 8)
#define PWR_BASE 0x40007000
/* Backup */ /* Backup */
struct bkp { struct bkp {
@ -161,156 +122,7 @@ struct bkp {
#define BKP_BASE 0x40006c00 #define BKP_BASE 0x40006c00
/* Reset and clock control */ /* EXTI */
struct rcc {
uint32_t cr; /* 00: Clock control */
uint32_t cfgr; /* 04: Clock configuration */
uint32_t cir; /* 08: Clock interrupt */
uint32_t apb2rstr; /* 0C: APB2 peripheral reset */
uint32_t apb1rstr; /* 10: APB1 peripheral reset */
uint32_t ahbenr; /* 14: AHB periphernal clock enable */
uint32_t apb2enr; /* 18: APB2 peripheral clock enable */
uint32_t apb1enr; /* 1C: APB1 peripheral clock enable */
uint32_t bdcr; /* 20: Backup domain control */
uint32_t csr; /* 24: Control/status */
uint32_t ahbstr; /* 28: AHB peripheral clock reset */
uint32_t cfgr2; /* 2C: Clock configuration 2 */
};
#define RCC_CR_PLL3RDY (1u<<29)
#define RCC_CR_PLL3ON (1u<<28)
#define RCC_CR_PLL2RDY (1u<<27)
#define RCC_CR_PLL2ON (1u<<26)
#define RCC_CR_PLLRDY (1u<<25)
#define RCC_CR_PLLON (1u<<24)
#define RCC_CR_CSSON (1u<<19)
#define RCC_CR_HSEBYP (1u<<18)
#define RCC_CR_HSERDY (1u<<17)
#define RCC_CR_HSEON (1u<<16)
#define RCC_CR_HSIRDY (1u<<1)
#define RCC_CR_HSION (1u<<0)
#define RCC_CFGR_PLLMUL(x) (((x)-2)<<18)
#define RCC_CFGR_PLLXTPRE (1u<<17)
#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_SWS_HSI (0u<<2)
#define RCC_CFGR_SWS_HSE (1u<<2)
#define RCC_CFGR_SWS_PLL (2u<<2)
#define RCC_CFGR_SWS_MASK (3u<<2)
#define RCC_CFGR_SW_HSI (0u<<0)
#define RCC_CFGR_SW_HSE (1u<<0)
#define RCC_CFGR_SW_PLL (2u<<0)
#define RCC_CFGR_SW_MASK (3u<<0)
#define RCC_AHBENR_ETHMACRXEN (1u<<16)
#define RCC_AHBENR_ETHMACTXEN (1u<<15)
#define RCC_AHBENR_ETHMACEN (1u<<14)
#define RCC_AHBENR_OTGFSEN (1u<<12)
#define RCC_AHBENR_CRCEN (1u<< 6)
#define RCC_AHBENR_FLITFEN (1u<< 4)
#define RCC_AHBENR_SRAMEN (1u<< 2)
#define RCC_AHBENR_DMA2EN (1u<< 1)
#define RCC_AHBENR_DMA1EN (1u<< 0)
#define RCC_APB1ENR_DACEN (1u<<29)
#define RCC_APB1ENR_PWREN (1u<<28)
#define RCC_APB1ENR_BKPEN (1u<<27)
#define RCC_APB1ENR_CAN2EN (1u<<26)
#define RCC_APB1ENR_CAN1EN (1u<<25)
#define RCC_APB1ENR_I2C2EN (1u<<22)
#define RCC_APB1ENR_I2C1EN (1u<<21)
#define RCC_APB1ENR_USART5EN (1u<<20)
#define RCC_APB1ENR_USART4EN (1u<<19)
#define RCC_APB1ENR_USART3EN (1u<<18)
#define RCC_APB1ENR_USART2EN (1u<<17)
#define RCC_APB1ENR_SPI3EN (1u<<15)
#define RCC_APB1ENR_SPI2EN (1u<<14)
#define RCC_APB1ENR_WWDGEN (1u<<11)
#define RCC_APB1ENR_TIM7EN (1u<< 5)
#define RCC_APB1ENR_TIM6EN (1u<< 4)
#define RCC_APB1ENR_TIM5EN (1u<< 3)
#define RCC_APB1ENR_TIM4EN (1u<< 2)
#define RCC_APB1ENR_TIM3EN (1u<< 1)
#define RCC_APB1ENR_TIM2EN (1u<< 0)
#define RCC_APB2ENR_USART1EN (1u<<14)
#define RCC_APB2ENR_SPI1EN (1u<<12)
#define RCC_APB2ENR_TIM1EN (1u<<11)
#define RCC_APB2ENR_ADC2EN (1u<<10)
#define RCC_APB2ENR_ADC1EN (1u<< 9)
#define RCC_APB2ENR_IOPEEN (1u<< 6)
#define RCC_APB2ENR_IOPDEN (1u<< 5)
#define RCC_APB2ENR_IOPCEN (1u<< 4)
#define RCC_APB2ENR_IOPBEN (1u<< 3)
#define RCC_APB2ENR_IOPAEN (1u<< 2)
#define RCC_APB2ENR_AFIOEN (1u<< 0)
#define RCC_BASE 0x40021000
/* General-purpose I/O */
struct gpio {
uint32_t crl; /* 00: Port configuration low */
uint32_t crh; /* 04: Port configuration high */
uint32_t idr; /* 08: Port input data */
uint32_t odr; /* 0C: Port output data */
uint32_t bsrr; /* 10: Port bit set/reset */
uint32_t brr; /* 14: Port bit reset */
uint32_t lckr; /* 18: Port configuration lock */
};
#define _GPI_pulled(level) (0x8u|((level)<<4))
#define GPI_analog 0x0u
#define GPI_floating 0x4u
#define GPI_pull_down _GPI_pulled(LOW)
#define GPI_pull_up _GPI_pulled(HIGH)
#define GPO_pushpull(speed,level) (0x0u|(speed)|((level)<<4))
#define GPO_opendrain(speed,level) (0x4u|(speed)|((level)<<4))
#define AFO_pushpull(speed) (0x8u|(speed))
#define AFO_opendrain(speed) (0xcu|(speed))
#define _2MHz 2
#define _10MHz 1
#define _50MHz 3
#define LOW 0
#define HIGH 1
#define GPIOA_BASE 0x40010800
#define GPIOB_BASE 0x40010c00
#define GPIOC_BASE 0x40011000
#define GPIOD_BASE 0x40011400
#define GPIOE_BASE 0x40011800
#define GPIOF_BASE 0x40011c00
#define GPIOG_BASE 0x40012000
/* Alternative-function I/O */
struct afio {
uint32_t evcr; /* 00: Event control */
uint32_t mapr; /* 04: AF remap and debug I/O configuration */
uint32_t exticr1; /* 08: External interrupt configuration #1 */
uint32_t exticr2; /* 0C: External interrupt configuration #2 */
uint32_t exticr3; /* 10: External interrupt configuration #3 */
uint32_t exticr4; /* 14: External interrupt configuration #4 */
uint32_t rsvd; /* 18: - */
uint32_t mapr2; /* 1C: AF remap and debug I/O configuration #2 */
};
#define AFIO_MAPR_SWJ_CFG_DISABLED (4u<<24)
#define AFIO_MAPR_TIM4_REMAP_FULL (1u<<12)
#define AFIO_MAPR_TIM3_REMAP_FULL (3u<<10)
#define AFIO_MAPR_TIM3_REMAP_PARTIAL (2u<<10)
#define AFIO_MAPR_TIM2_REMAP_FULL (3u<< 8)
#define AFIO_MAPR_TIM2_REMAP_PARTIAL_1 (1u<< 8)
#define AFIO_MAPR_TIM2_REMAP_PARTIAL_2 (2u<< 8)
#define AFIO_MAPR_TIM1_REMAP_FULL (3u<< 6)
#define AFIO_MAPR_TIM1_REMAP_PARTIAL (1u<< 6)
#define AFIO_MAPR_USART3_REMAP_FULL (3u<< 4)
#define AFIO_MAPR_USART3_REMAP_PARTIAL (1u<< 4)
#define AFIO_BASE 0x40010000
struct exti { struct exti {
uint32_t imr; /* 00: Interrupt mask */ uint32_t imr; /* 00: Interrupt mask */
uint32_t emr; /* 04: Event mask */ uint32_t emr; /* 04: Event mask */
@ -320,8 +132,6 @@ struct exti {
uint32_t pr; /* 14: Pending */ uint32_t pr; /* 14: Pending */
}; };
#define EXTI_BASE 0x40010400
/* DMA */ /* DMA */
struct dma_chn { struct dma_chn {
uint32_t ccr; /* +00: Configuration */ uint32_t ccr; /* +00: Configuration */
@ -333,13 +143,7 @@ struct dma_chn {
struct dma { struct dma {
uint32_t isr; /* 00: Interrupt status */ uint32_t isr; /* 00: Interrupt status */
uint32_t ifcr; /* 04: Interrupt flag clear */ uint32_t ifcr; /* 04: Interrupt flag clear */
struct dma_chn ch1; /* 08: Channel 1 */ struct dma_chn ch[7];
struct dma_chn ch2; /* 1C: Channel 2 */
struct dma_chn ch3; /* 30: Channel 3 */
struct dma_chn ch4; /* 44: Channel 4 */
struct dma_chn ch5; /* 58: Channel 5 */
struct dma_chn ch6; /* 6C: Channel 6 */
struct dma_chn ch7; /* 80: Channel 7 */
}; };
/* n=1..7 */ /* n=1..7 */
@ -375,9 +179,6 @@ struct dma {
#define DMA_CCR_TCIE (1u<< 1) #define DMA_CCR_TCIE (1u<< 1)
#define DMA_CCR_EN (1u<< 0) #define DMA_CCR_EN (1u<< 0)
#define DMA1_BASE 0x40020000
#define DMA2_BASE 0x40020400
/* Timer */ /* Timer */
struct tim { struct tim {
uint32_t cr1; /* 00: Control 1 */ uint32_t cr1; /* 00: Control 1 */
@ -509,14 +310,6 @@ struct tim {
#define TIM_BDTR_LOCK(x) ((x)<<8) #define TIM_BDTR_LOCK(x) ((x)<<8)
#define TIM_BDTR_DTG(x) ((x)<<0) #define TIM_BDTR_DTG(x) ((x)<<0)
#define TIM1_BASE 0x40012c00
#define TIM2_BASE 0x40000000
#define TIM3_BASE 0x40000400
#define TIM4_BASE 0x40000800
#define TIM5_BASE 0x40000c00
#define TIM6_BASE 0x40001000
#define TIM7_BASE 0x40001400
/* SPI/I2S */ /* SPI/I2S */
struct spi { struct spi {
uint32_t cr1; /* 00: Control 1 */ uint32_t cr1; /* 00: Control 1 */
@ -530,6 +323,17 @@ struct spi {
uint32_t i2spr; /* 20: I2S prescaler */ uint32_t i2spr; /* 20: I2S prescaler */
}; };
#define SPI_BR_DIV2 0
#define SPI_BR_DIV4 1
#define SPI_BR_DIV8 2
#define SPI_BR_DIV16 3
#define SPI_BR_DIV32 4
#define SPI_BR_DIV64 5
#define SPI_BR_DIV128 6
#define SPI_BR_DIV256 7
#define SPI_BR_DIV512 8
#define SPI_BR_DIV1024 9
#define SPI_CR1_BIDIMODE (1u<<15) #define SPI_CR1_BIDIMODE (1u<<15)
#define SPI_CR1_BIDIOE (1u<<14) #define SPI_CR1_BIDIOE (1u<<14)
#define SPI_CR1_CRCEN (1u<<13) #define SPI_CR1_CRCEN (1u<<13)
@ -540,15 +344,6 @@ struct spi {
#define SPI_CR1_SSI (1u<< 8) #define SPI_CR1_SSI (1u<< 8)
#define SPI_CR1_LSBFIRST (1u<< 7) #define SPI_CR1_LSBFIRST (1u<< 7)
#define SPI_CR1_SPE (1u<< 6) #define SPI_CR1_SPE (1u<< 6)
#define SPI_CR1_BR_DIV2 (0u<< 3)
#define SPI_CR1_BR_DIV4 (1u<< 3)
#define SPI_CR1_BR_DIV8 (2u<< 3)
#define SPI_CR1_BR_DIV16 (3u<< 3)
#define SPI_CR1_BR_DIV32 (4u<< 3)
#define SPI_CR1_BR_DIV64 (5u<< 3)
#define SPI_CR1_BR_DIV128 (6u<< 3)
#define SPI_CR1_BR_DIV256 (7u<< 3)
#define SPI_CR1_BR_MASK (7u<< 3)
#define SPI_CR1_MSTR (1u<< 2) #define SPI_CR1_MSTR (1u<< 2)
#define SPI_CR1_CPOL (1u<< 1) #define SPI_CR1_CPOL (1u<< 1)
#define SPI_CR1_CPHA (1u<< 0) #define SPI_CR1_CPHA (1u<< 0)
@ -573,74 +368,6 @@ struct spi {
#define SPI2_BASE 0x40003800 #define SPI2_BASE 0x40003800
#define SPI3_BASE 0x40003C00 #define SPI3_BASE 0x40003C00
/* I2C */
struct i2c {
uint32_t cr1; /* 00: Control 1 */
uint32_t cr2; /* 04: Control 2 */
uint32_t oar1; /* 08: Own address 1 */
uint32_t oar2; /* 0C: Own address 2 */
uint32_t dr; /* 10: Data */
uint32_t sr1; /* 14: Status 1 */
uint32_t sr2; /* 18: Status 2 */
uint32_t ccr; /* 1C: Clock control */
uint32_t trise; /* 20: Rise time */
};
#define I2C_CR1_SWRST (1u<<15)
#define I2C_CR1_ALERT (1u<<13)
#define I2C_CR1_PEC (1u<<12)
#define I2C_CR1_POS (1u<<11)
#define I2C_CR1_ACK (1u<<10)
#define I2C_CR1_STOP (1u<< 9)
#define I2C_CR1_START (1u<< 8)
#define I2C_CR1_NOSTRETCH (1u<< 7)
#define I2C_CR1_ENGC (1u<< 6)
#define I2C_CR1_ENPEC (1u<< 5)
#define I2C_CR1_ENARP (1u<< 4)
#define I2C_CR1_SMBTYPE (1u<< 3)
#define I2C_CR1_SMBUS (1u<< 1)
#define I2C_CR1_PE (1u<< 0)
#define I2C_CR2_LAST (1u<<12)
#define I2C_CR2_DMAEN (1u<<11)
#define I2C_CR2_ITBUFEN (1u<<10)
#define I2C_CR2_ITEVTEN (1u<< 9)
#define I2C_CR2_ITERREN (1u<< 8)
#define I2C_CR2_FREQ(x) (x)
#define I2C_SR1_SMBALERT (1u<<15)
#define I2C_SR1_TIMEOUT (1u<<14)
#define I2C_SR1_PECERR (1u<<12)
#define I2C_SR1_OVR (1u<<11)
#define I2C_SR1_AF (1u<<10)
#define I2C_SR1_ARLO (1u<< 9)
#define I2C_SR1_BERR (1u<< 8)
#define I2C_SR1_ERRORS 0xdf00
#define I2C_SR1_TXE (1u<< 7)
#define I2C_SR1_RXNE (1u<< 6)
#define I2C_SR1_STOPF (1u<< 4)
#define I2C_SR1_ADD10 (1u<< 3)
#define I2C_SR1_BTF (1u<< 2)
#define I2C_SR1_ADDR (1u<< 1)
#define I2C_SR1_SB (1u<< 0)
#define I2C_SR1_EVENTS 0x001f
#define I2C_SR2_PEC(x) ((x)<<15)
#define I2C_SR2_DUALF (1u<< 7)
#define I2C_SR2_SMBHOST (1u<< 6)
#define I2C_SR2_SMBDEFAULT (1u<< 5)
#define I2C_SR2_GENCALL (1u<< 4)
#define I2C_SR2_TRA (1u<< 2)
#define I2C_SR2_BUSY (1u<< 1)
#define I2C_SR2_MSL (1u<< 0)
#define I2C_CCR_FS (1u<<15)
#define I2C_CCR_DUTY (1u<<14)
#define I2C_CCR_CCR(x) (x)
#define I2C1_BASE 0x40005400
#define I2C2_BASE 0x40005800
/* USART */ /* USART */
struct usart { struct usart {
uint32_t sr; /* 00: Status */ uint32_t sr; /* 00: Status */
@ -690,10 +417,6 @@ struct usart {
#define USART_CR3_IREN (1u<< 1) #define USART_CR3_IREN (1u<< 1)
#define USART_CR3_EIE (1u<< 0) #define USART_CR3_EIE (1u<< 0)
#define USART1_BASE 0x40013800
#define USART2_BASE 0x40004400
#define USART3_BASE 0x40004800
/* USB On-The-Go Full Speed interface */ /* USB On-The-Go Full Speed interface */
struct usb_otg { struct usb_otg {
uint32_t gotctl; /* 00: Control and status */ uint32_t gotctl; /* 00: Control and status */

71
inc/mcu/stm32f105.h Normal file
View file

@ -0,0 +1,71 @@
/*
* stm32f105.h
*
* Core and peripheral registers.
*
* 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>.
*/
/* C pointer types */
#define AFIO volatile struct afio * const
/* C-accessible registers. */
static STK stk = (struct stk *)STK_BASE;
static SCB scb = (struct scb *)SCB_BASE;
static NVIC nvic = (struct nvic *)NVIC_BASE;
static DBG dbg = (struct dbg *)DBG_BASE;
static FLASH flash = (struct flash *)FLASH_BASE;
static PWR pwr = (struct pwr *)PWR_BASE;
static BKP bkp = (struct bkp *)BKP_BASE;
static RCC rcc = (struct rcc *)RCC_BASE;
static GPIO gpioa = (struct gpio *)GPIOA_BASE;
static GPIO gpiob = (struct gpio *)GPIOB_BASE;
static GPIO gpioc = (struct gpio *)GPIOC_BASE;
static GPIO gpiod = (struct gpio *)GPIOD_BASE;
static GPIO gpioe = (struct gpio *)GPIOE_BASE;
static GPIO gpiof = (struct gpio *)GPIOF_BASE;
static GPIO gpiog = (struct gpio *)GPIOG_BASE;
static AFIO afio = (struct afio *)AFIO_BASE;
static EXTI exti = (struct exti *)EXTI_BASE;
static DMA dma1 = (struct dma *)DMA1_BASE;
static DMA dma2 = (struct dma *)DMA2_BASE;
static TIM tim1 = (struct tim *)TIM1_BASE;
static TIM tim2 = (struct tim *)TIM2_BASE;
static TIM tim3 = (struct tim *)TIM3_BASE;
static TIM tim4 = (struct tim *)TIM4_BASE;
static TIM tim5 = (struct tim *)TIM5_BASE;
static TIM tim6 = (struct tim *)TIM6_BASE;
static TIM tim7 = (struct tim *)TIM7_BASE;
static SPI spi1 = (struct spi *)SPI1_BASE;
static SPI spi2 = (struct spi *)SPI2_BASE;
static SPI spi3 = (struct spi *)SPI3_BASE;
static I2C i2c1 = (struct i2c *)I2C1_BASE;
static I2C i2c2 = (struct i2c *)I2C2_BASE;
static USART usart1 = (struct usart *)USART1_BASE;
static USART usart2 = (struct usart *)USART2_BASE;
static USART usart3 = (struct usart *)USART3_BASE;
static USB_OTG usb_otg = (struct usb_otg *)USB_OTG_BASE;
/* Clocks */
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
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

313
inc/mcu/stm32f105_regs.h Normal file
View file

@ -0,0 +1,313 @@
/*
* stm32f105_regs.h
*
* Core and peripheral register definitions.
*
* 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>.
*/
/* Power control */
struct pwr {
uint32_t cr; /* 00: Power control */
uint32_t csr; /* 04: Power control/status */
};
#define PWR_CR_DBP (1u<< 8)
#define PWR_BASE 0x40007000
/* Flash memory interface */
struct flash {
uint32_t acr; /* 00: Flash access control */
uint32_t keyr; /* 04: FPEC key */
uint32_t optkeyr; /* 08: Flash OPTKEY */
uint32_t sr; /* 0C: Flash status */
uint32_t cr; /* 10: Flash control */
uint32_t ar; /* 14: Flash address */
uint32_t rsvd; /* 18: - */
uint32_t obr; /* 1C: Option byte */
uint32_t wrpr; /* 20: Write protection */
};
#define FLASH_ACR_PRFTBS (1u<< 5)
#define FLASH_ACR_PRFTBE (1u<< 4)
#define FLASH_ACR_HLFCYA (1u<< 3)
#define FLASH_ACR_LATENCY(w) ((w)<<0) /* wait states */
#define FLASH_SR_EOP (1u<< 5)
#define FLASH_SR_WRPRTERR (1u<< 4)
#define FLASH_SR_PGERR (1u<< 2)
#define FLASH_SR_BSY (1u<< 0)
#define FLASH_CR_EOPIE (1u<<12)
#define FLASH_CR_ERRIE (1u<<10)
#define FLASH_CR_OPTWRE (1u<< 9)
#define FLASH_CR_LOCK (1u<< 7)
#define FLASH_CR_STRT (1u<< 6)
#define FLASH_CR_OPTER (1u<< 5)
#define FLASH_CR_OPTPG (1u<< 4)
#define FLASH_CR_MER (1u<< 2)
#define FLASH_CR_PER (1u<< 1)
#define FLASH_CR_PG (1u<< 0)
#define FLASH_BASE 0x40022000
/* Reset and clock control */
struct rcc {
uint32_t cr; /* 00: Clock control */
uint32_t cfgr; /* 04: Clock configuration */
uint32_t cir; /* 08: Clock interrupt */
uint32_t apb2rstr; /* 0C: APB2 peripheral reset */
uint32_t apb1rstr; /* 10: APB1 peripheral reset */
uint32_t ahbenr; /* 14: AHB periphernal clock enable */
uint32_t apb2enr; /* 18: APB2 peripheral clock enable */
uint32_t apb1enr; /* 1C: APB1 peripheral clock enable */
uint32_t bdcr; /* 20: Backup domain control */
uint32_t csr; /* 24: Control/status */
uint32_t ahbstr; /* 28: AHB peripheral clock reset */
uint32_t cfgr2; /* 2C: Clock configuration 2 */
};
#define RCC_CR_PLL3RDY (1u<<29)
#define RCC_CR_PLL3ON (1u<<28)
#define RCC_CR_PLL2RDY (1u<<27)
#define RCC_CR_PLL2ON (1u<<26)
#define RCC_CR_PLLRDY (1u<<25)
#define RCC_CR_PLLON (1u<<24)
#define RCC_CR_CSSON (1u<<19)
#define RCC_CR_HSEBYP (1u<<18)
#define RCC_CR_HSERDY (1u<<17)
#define RCC_CR_HSEON (1u<<16)
#define RCC_CR_HSIRDY (1u<<1)
#define RCC_CR_HSION (1u<<0)
#define RCC_CFGR_PLLMUL(x) (((x)-2)<<18)
#define RCC_CFGR_PLLXTPRE (1u<<17)
#define RCC_CFGR_PLLSRC_HSI (0u<<16)
#define RCC_CFGR_PLLSRC_PREDIV1 (1u<<16)
#define RCC_CFGR_ADCPRE_DIV8 (3u<<14)
#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)
#define RCC_CFGR_SWS_MASK (3u<<2)
#define RCC_CFGR_SW_HSI (0u<<0)
#define RCC_CFGR_SW_HSE (1u<<0)
#define RCC_CFGR_SW_PLL (2u<<0)
#define RCC_CFGR_SW_MASK (3u<<0)
#define RCC_AHBENR_ETHMACRXEN (1u<<16)
#define RCC_AHBENR_ETHMACTXEN (1u<<15)
#define RCC_AHBENR_ETHMACEN (1u<<14)
#define RCC_AHBENR_OTGFSEN (1u<<12)
#define RCC_AHBENR_CRCEN (1u<< 6)
#define RCC_AHBENR_FLITFEN (1u<< 4)
#define RCC_AHBENR_SRAMEN (1u<< 2)
#define RCC_AHBENR_DMA2EN (1u<< 1)
#define RCC_AHBENR_DMA1EN (1u<< 0)
#define RCC_APB1ENR_DACEN (1u<<29)
#define RCC_APB1ENR_PWREN (1u<<28)
#define RCC_APB1ENR_BKPEN (1u<<27)
#define RCC_APB1ENR_CAN2EN (1u<<26)
#define RCC_APB1ENR_CAN1EN (1u<<25)
#define RCC_APB1ENR_I2C2EN (1u<<22)
#define RCC_APB1ENR_I2C1EN (1u<<21)
#define RCC_APB1ENR_USART5EN (1u<<20)
#define RCC_APB1ENR_USART4EN (1u<<19)
#define RCC_APB1ENR_USART3EN (1u<<18)
#define RCC_APB1ENR_USART2EN (1u<<17)
#define RCC_APB1ENR_SPI3EN (1u<<15)
#define RCC_APB1ENR_SPI2EN (1u<<14)
#define RCC_APB1ENR_WWDGEN (1u<<11)
#define RCC_APB1ENR_TIM7EN (1u<< 5)
#define RCC_APB1ENR_TIM6EN (1u<< 4)
#define RCC_APB1ENR_TIM5EN (1u<< 3)
#define RCC_APB1ENR_TIM4EN (1u<< 2)
#define RCC_APB1ENR_TIM3EN (1u<< 1)
#define RCC_APB1ENR_TIM2EN (1u<< 0)
#define RCC_APB2ENR_USART1EN (1u<<14)
#define RCC_APB2ENR_SPI1EN (1u<<12)
#define RCC_APB2ENR_TIM1EN (1u<<11)
#define RCC_APB2ENR_ADC2EN (1u<<10)
#define RCC_APB2ENR_ADC1EN (1u<< 9)
#define RCC_APB2ENR_IOPFEN (1u<< 7)
#define RCC_APB2ENR_IOPEEN (1u<< 6)
#define RCC_APB2ENR_IOPDEN (1u<< 5)
#define RCC_APB2ENR_IOPCEN (1u<< 4)
#define RCC_APB2ENR_IOPBEN (1u<< 3)
#define RCC_APB2ENR_IOPAEN (1u<< 2)
#define RCC_APB2ENR_AFIOEN (1u<< 0)
#define RCC_BASE 0x40021000
/* General-purpose I/O */
struct gpio {
uint32_t crl; /* 00: Port configuration low */
uint32_t crh; /* 04: Port configuration high */
uint32_t idr; /* 08: Port input data */
uint32_t odr; /* 0C: Port output data */
uint32_t bsrr; /* 10: Port bit set/reset */
uint32_t brr; /* 14: Port bit reset */
uint32_t lckr; /* 18: Port configuration lock */
};
#define _GPI_pulled(level) (0x8u|((level)<<4))
#define GPI_analog 0x0u
#define GPI_floating 0x4u
#define GPI_pull_down _GPI_pulled(LOW)
#define GPI_pull_up _GPI_pulled(HIGH)
#define GPO_pushpull(speed,level) (0x0u|(speed)|((level)<<4))
#define GPO_opendrain(speed,level) (0x4u|(speed)|((level)<<4))
#define AFO_pushpull(speed) (0x8u|(speed))
#define _AFO_pushpull(speed,level) (0x8u|(speed)|((level)<<4))
#define AFO_opendrain(speed) (0xcu|(speed))
#define _2MHz 2
#define _10MHz 1
#define _50MHz 3
#define LOW 0
#define HIGH 1
#define GPIOA_BASE 0x40010800
#define GPIOB_BASE 0x40010c00
#define GPIOC_BASE 0x40011000
#define GPIOD_BASE 0x40011400
#define GPIOE_BASE 0x40011800
#define GPIOF_BASE 0x40011c00
#define GPIOG_BASE 0x40012000
/* Alternative-function I/O */
struct afio {
uint32_t evcr; /* 00: Event control */
uint32_t mapr; /* 04: AF remap and debug I/O configuration */
uint32_t exticr[4]; /* 08-14: External interrupt configuration #1-4 */
uint32_t rsvd; /* 18: - */
uint32_t mapr2; /* 1C: AF remap and debug I/O configuration #2 */
};
#define AFIO_MAPR_SWJ_CFG_DISABLED (4u<<24)
#define AFIO_MAPR_TIM4_REMAP_FULL (1u<<12)
#define AFIO_MAPR_TIM3_REMAP_FULL (3u<<10)
#define AFIO_MAPR_TIM3_REMAP_PARTIAL (2u<<10)
#define AFIO_MAPR_TIM2_REMAP_FULL (3u<< 8)
#define AFIO_MAPR_TIM2_REMAP_PARTIAL_1 (1u<< 8)
#define AFIO_MAPR_TIM2_REMAP_PARTIAL_2 (2u<< 8)
#define AFIO_MAPR_TIM1_REMAP_FULL (3u<< 6)
#define AFIO_MAPR_TIM1_REMAP_PARTIAL (1u<< 6)
#define AFIO_MAPR_USART3_REMAP_FULL (3u<< 4)
#define AFIO_MAPR_USART3_REMAP_PARTIAL (1u<< 4)
#define AFIO_BASE 0x40010000
/* EXTI */
#define EXTI_BASE 0x40010400
/* DMA */
#define DMA1_CH1_IRQ 11
#define DMA1_CH2_IRQ 12
#define DMA1_CH3_IRQ 13
#define DMA1_CH4_IRQ 14
#define DMA1_CH5_IRQ 15
#define DMA1_CH6_IRQ 16
#define DMA1_CH7_IRQ 17
#define DMA1_BASE 0x40020000
#define DMA2_BASE 0x40020400
/* Timer */
#define TIM1_BASE 0x40012c00
#define TIM2_BASE 0x40000000
#define TIM3_BASE 0x40000400
#define TIM4_BASE 0x40000800
#define TIM5_BASE 0x40000c00
#define TIM6_BASE 0x40001000
#define TIM7_BASE 0x40001400
/* I2C */
struct i2c {
uint32_t cr1; /* 00: Control 1 */
uint32_t cr2; /* 04: Control 2 */
uint32_t oar1; /* 08: Own address 1 */
uint32_t oar2; /* 0C: Own address 2 */
uint32_t dr; /* 10: Data */
uint32_t sr1; /* 14: Status 1 */
uint32_t sr2; /* 18: Status 2 */
uint32_t ccr; /* 1C: Clock control */
uint32_t trise; /* 20: Rise time */
};
#define I2C_CR1_SWRST (1u<<15)
#define I2C_CR1_ALERT (1u<<13)
#define I2C_CR1_PEC (1u<<12)
#define I2C_CR1_POS (1u<<11)
#define I2C_CR1_ACK (1u<<10)
#define I2C_CR1_STOP (1u<< 9)
#define I2C_CR1_START (1u<< 8)
#define I2C_CR1_NOSTRETCH (1u<< 7)
#define I2C_CR1_ENGC (1u<< 6)
#define I2C_CR1_ENPEC (1u<< 5)
#define I2C_CR1_ENARP (1u<< 4)
#define I2C_CR1_SMBTYPE (1u<< 3)
#define I2C_CR1_SMBUS (1u<< 1)
#define I2C_CR1_PE (1u<< 0)
#define I2C_CR2_LAST (1u<<12)
#define I2C_CR2_DMAEN (1u<<11)
#define I2C_CR2_ITBUFEN (1u<<10)
#define I2C_CR2_ITEVTEN (1u<< 9)
#define I2C_CR2_ITERREN (1u<< 8)
#define I2C_CR2_FREQ(x) (x)
#define I2C_SR1_SMBALERT (1u<<15)
#define I2C_SR1_TIMEOUT (1u<<14)
#define I2C_SR1_PECERR (1u<<12)
#define I2C_SR1_OVR (1u<<11)
#define I2C_SR1_AF (1u<<10)
#define I2C_SR1_ARLO (1u<< 9)
#define I2C_SR1_BERR (1u<< 8)
#define I2C_SR1_ERRORS 0xdf00
#define I2C_SR1_TXE (1u<< 7)
#define I2C_SR1_RXNE (1u<< 6)
#define I2C_SR1_STOPF (1u<< 4)
#define I2C_SR1_ADD10 (1u<< 3)
#define I2C_SR1_BTF (1u<< 2)
#define I2C_SR1_ADDR (1u<< 1)
#define I2C_SR1_SB (1u<< 0)
#define I2C_SR1_EVENTS 0x001f
#define I2C_SR2_PEC(x) ((x)<<15)
#define I2C_SR2_DUALF (1u<< 7)
#define I2C_SR2_SMBHOST (1u<< 6)
#define I2C_SR2_SMBDEFAULT (1u<< 5)
#define I2C_SR2_GENCALL (1u<< 4)
#define I2C_SR2_TRA (1u<< 2)
#define I2C_SR2_BUSY (1u<< 1)
#define I2C_SR2_MSL (1u<< 0)
#define I2C_CCR_FS (1u<<15)
#define I2C_CCR_DUTY (1u<<14)
#define I2C_CCR_CCR(x) (x)
#define I2C1_BASE 0x40005400
#define I2C2_BASE 0x40005800
/* USART */
#define USART1_BASE 0x40013800
#define USART2_BASE 0x40004400
#define USART3_BASE 0x40004800
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

35
inc/thread.h Normal file
View file

@ -0,0 +1,35 @@
/*
* thread.h
*
* Cooperative multitasking.
*
* Written & released by Keir Fraser <keir.xen@gmail.com> and Eric Anderson
* <ejona86@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>.
*/
struct thread {
/* Internal bookkeeping */
bool_t exited;
};
/* Initialize a thread and queue it for execution. 'thread' must remain
* allocated for the lifetime of the thread. */
void thread_start(struct thread *thread, uint32_t *stack, void (*func)(void*), void* arg);
/* Yield execution to allow other threads to run. */
void thread_yield(void);
/* Returns true if provided thread has exited. A thread cannot be joined
* multiple times, unless it is started anew. */
bool_t thread_tryjoin(struct thread *thread);
/* Continuously yields until provided thread has exited. A thread cannot be
* joined multiple times, unless it is started anew. */
void thread_join(struct thread *thread);
/* Reinitializes threading subsystem to its initial state, throwing away all
* threads but the current. */
void thread_reset(void);

56
inc/types.h Normal file
View file

@ -0,0 +1,56 @@
/*
* types.h
*
* 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>.
*/
#ifndef NDEBUG
#define ASSERT(p) do { if (!(p)) illegal(); } while (0)
#else
#define ASSERT(p) do { if (0 && (p)) {} } while (0)
#endif
typedef char bool_t;
#define TRUE 1
#define FALSE 0
#ifndef offsetof
#define offsetof(a,b) __builtin_offsetof(a,b)
#endif
#define container_of(ptr, type, member) ({ \
typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define min(x,y) ({ \
const typeof(x) _x = (x); \
const typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })
#define max(x,y) ({ \
const typeof(x) _x = (x); \
const typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x > _y ? _x : _y; })
#define min_t(type,x,y) \
({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
#define max_t(type,x,y) \
({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
#define range_t(type,x,a,b) min_t(type, max_t(type, x, a), b)
#define m(bitnr) (1u<<(bitnr))
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

View file

@ -9,42 +9,6 @@
* See the file COPYING for more details, or visit <http://unlicense.org>. * See the file COPYING for more details, or visit <http://unlicense.org>.
*/ */
#ifndef NDEBUG
#define ASSERT(p) do { if (!(p)) illegal(); } while (0)
#else
#define ASSERT(p) do { if (0 && (p)) {} } while (0)
#endif
typedef char bool_t;
#define TRUE 1
#define FALSE 0
#ifndef offsetof
#define offsetof(a,b) __builtin_offsetof(a,b)
#endif
#define container_of(ptr, type, member) ({ \
typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define min(x,y) ({ \
const typeof(x) _x = (x); \
const typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })
#define max(x,y) ({ \
const typeof(x) _x = (x); \
const typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x > _y ? _x : _y; })
#define min_t(type,x,y) \
({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
#define max_t(type,x,y) \
({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
#define range_t(type,x,a,b) min_t(type, max_t(type, x, a), b)
struct slot { struct slot {
char name[FF_MAX_LFN+1]; char name[FF_MAX_LFN+1];
char type[7]; char type[7];
@ -55,6 +19,8 @@ struct slot {
}; };
void fatfs_from_slot(FIL *file, const struct slot *slot, BYTE mode); void fatfs_from_slot(FIL *file, const struct slot *slot, BYTE mode);
bool_t lba_within_fat_volume(uint32_t lba);
void filename_extension(const char *filename, char *extension, size_t size); void filename_extension(const char *filename, char *extension, size_t size);
/* Fast memset/memcpy: Pointers must be word-aligned, count must be a non-zero /* Fast memset/memcpy: Pointers must be word-aligned, count must be a non-zero
@ -154,6 +120,8 @@ extern uint8_t display_type;
/* Speaker. */ /* Speaker. */
void speaker_init(void); void speaker_init(void);
void speaker_pulse(void); void speaker_pulse(void);
void speaker_notify_insert(unsigned int slotnr);
void speaker_notify_eject(void);
/* Display: 3-digit 7-segment display */ /* Display: 3-digit 7-segment display */
void led_7seg_init(void); void led_7seg_init(void);
@ -192,6 +160,7 @@ void IRQ_rotary(void);
enum { DM_normal=0, DM_banner, DM_menu }; enum { DM_normal=0, DM_banner, DM_menu };
extern uint8_t display_mode; extern uint8_t display_mode;
extern uint8_t board_id; extern uint8_t board_id;
extern uint8_t has_kc30_header;
/* Gotek board revisions */ /* Gotek board revisions */
#define BRDREV_Gotek_standard 0xf #define BRDREV_Gotek_standard 0xf
@ -199,6 +168,16 @@ extern uint8_t board_id;
#define BRDREV_Gotek_sd_card 0x1 #define BRDREV_Gotek_sd_card 0x1
#define gotek_enhanced() (board_id != BRDREV_Gotek_standard) #define gotek_enhanced() (board_id != BRDREV_Gotek_standard)
extern uint32_t board_rotary_exti_mask;
void board_setup_rotary_exti(void);
unsigned int board_get_rotary(void);
unsigned int board_get_buttons(void);
#define B_LEFT 1
#define B_RIGHT 2
#define B_SELECT 4
void board_jc_set_mode(unsigned int mode);
bool_t board_jc_strapped(void);
/* Build info. */ /* Build info. */
extern const char fw_ver[]; extern const char fw_ver[];
extern const char build_date[]; extern const char build_date[];
@ -212,6 +191,7 @@ extern char _sbss[], _ebss[];
/* Stacks. */ /* Stacks. */
extern uint32_t _thread_stacktop[], _thread_stackbottom[]; extern uint32_t _thread_stacktop[], _thread_stackbottom[];
extern uint32_t _thread1_stacktop[], _thread1_stackbottom[];
extern uint32_t _irq_stacktop[], _irq_stackbottom[]; extern uint32_t _irq_stacktop[], _irq_stackbottom[];
/* Default exception handler. */ /* Default exception handler. */

View file

@ -23,6 +23,10 @@ struct volume_ops {
bool_t volume_connected(void); bool_t volume_connected(void);
bool_t volume_readonly(void); bool_t volume_readonly(void);
/* Returns TRUE if volume is in the middle of an operation that may
* thread_yield(); FALSE if no I/O in progress. When TRUE, the thread will
* yield as soon as calling this method would begin returning FALSE. */
bool_t volume_interrupt(void);
void volume_cache_init(void *start, void *end); void volume_cache_init(void *start, void *end);
void volume_cache_destroy(void); void volume_cache_destroy(void);

View file

@ -1 +0,0 @@
#include "../src/FlashFloppy.ld.S"

View file

@ -1,21 +0,0 @@
RPATH = ../src
OBJS += io_test.o
OBJS += fpec.o
OBJS += build_info.o
OBJS += crc.o
OBJS += vectors.o
OBJS += string.o
OBJS += stm32f10x.o
OBJS += time.o
OBJS += timer.o
OBJS += util.o
OBJS += flash_cfg.o
OBJS-$(debug) += console.o
SUBDIRS += display
SUBDIRS += gotek
.PHONY: $(RPATH)/build_info.c
build_info.o: CFLAGS += -DFW_VER="\"$(FW_VER)\""

View file

@ -1,6 +0,0 @@
RPATH = ../../src/display
OBJS += display.o
OBJS += lcd.o
OBJS += oled_font_6x13.o
OBJS += led_7seg.o

View file

@ -1,4 +0,0 @@
RPATH = ../../src/gotek
OBJS += board.o
OBJS += speaker.o

89
scripts/check_hex.py Normal file
View file

@ -0,0 +1,89 @@
# check_hex.py
#
# Check the base address and size of segments in an Intel Hex target file,
# based on known constraints for the specified MCU target.
#
# 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>.
import os, re, sys
from intelhex import IntelHex
def kb(n):
return n*1024
def _fatal(s):
print('*** ' + s, file=sys.stderr)
def fatal(s):
_fatal(s)
sys.exit(1)
class Target:
FLASH_BASE = 0x8000000
def __init__(self, bootloader_size, flash_size, page_size):
self._bootloader_size = bootloader_size
self._flash_size = flash_size
self._page_size = page_size
def bootloader_base(self):
return self.FLASH_BASE
def bootloader_size(self):
return self._bootloader_size
def firmware_base(self):
return self.FLASH_BASE + self._bootloader_size
def firmware_size(self):
# Allow for bootloader at start of Flash, and cached config at end
return self._flash_size - self._bootloader_size - self._page_size
targets = { 'stm32f105': Target(kb(32), kb(128), kb(2)),
'at32f435': Target(kb(48), kb(256), kb(2)) }
def usage(argv):
fatal("Usage: %s file.hex target" % argv[0])
def main(argv):
if len(argv) != 3:
usage(argv)
h = argv[1]
try:
t = targets[argv[2]]
except KeyError:
_fatal('Unknown target: ' + argv[2])
usage(argv)
# Informational prefix for log lines
prefix = h
m = re.match(r'.*/out/(.*)', os.path.abspath(h))
if m is not None:
prefix = m.group(1)
ih = IntelHex(h)
for (s,e) in ih.segments():
sz = e - s
if s == t.bootloader_base():
if sz > t.bootloader_size():
fatal('%s: Bootloader overflows by %d bytes'
% (prefix, sz - t.bootloader_size()))
print('%s: Bootloader has %d bytes headroom'
% (prefix, t.bootloader_size() - sz))
elif s == t.firmware_base():
if sz > t.firmware_size():
fatal('%s: Firmware overflows by %d bytes'
% (prefix, sz - t.firmware_size()))
print('%s: Firmware has %d bytes headroom'
% (prefix, t.firmware_size() - sz))
else:
fatal('%s: Unexpected start address %x' % (prefix, s))
if __name__ == "__main__":
main(sys.argv)

View file

@ -46,7 +46,16 @@ def main(argv):
else: else:
opts += ['DISPLAY_' + x] opts += ['DISPLAY_' + x]
val = '|'.join(opts) val = '|'.join(opts)
elif opt == "display-order": 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": if val == "default":
val = "DORD_" + val val = "DORD_" + val
else: else:

View file

@ -20,14 +20,22 @@ def main(argv):
help="number of cylinders") help="number of cylinders")
parser.add_argument("--sides", type=int, default=2, parser.add_argument("--sides", type=int, default=2,
help="number of sides") help="number of sides")
parser.add_argument("--hard-sectors", type=int, default=0,
help="number of hard sectors")
parser.add_argument("outfile", help="output filename") parser.add_argument("outfile", help="output filename")
args = parser.parse_args(argv[1:]) args = parser.parse_args(argv[1:])
v3 = args.hard_sectors != 0
bits = (args.rate * 1000 * 60) // args.rpm bits = (args.rate * 1000 * 60) // args.rpm
bits *= 2 # clock bits bits *= 2 # clock bits
bits *= 2 # 2 sides bits *= 2 # 2 sides
bytes = (bits + 7) // 8 # convert to bytes, rounded up bytes = (bits + 7) // 8 # convert to bytes, rounded up
bytes = (bytes + 15) & ~15 # round up to 16-byte boundary if not v3:
bytes = (bytes + 15) & ~15 # round up to 16-byte boundary
raw_bytes = bytes
if args.hard_sectors:
bytes += (args.hard_sectors + 1) * 2 # 2 sides
blocks = (bytes + 511) // 512 # convert to 512-byte blocks, rounded up blocks = (bytes + 511) // 512 # convert to 512-byte blocks, rounded up
print("Geometry: %u cylinders, %u sides" % (args.cyls, args.sides)) print("Geometry: %u cylinders, %u sides" % (args.cyls, args.sides))
@ -37,8 +45,9 @@ def main(argv):
# Header # Header
out_f = open(args.outfile, "wb") out_f = open(args.outfile, "wb")
sig = b'HXCHFEV3' if v3 else b"HXCPICFE"
out_f.write(struct.pack("<8s4B2H2BH", out_f.write(struct.pack("<8s4B2H2BH",
b"HXCPICFE",# signature sig, # signature
0, # revision 0, # revision
args.cyls, # nr_tracks args.cyls, # nr_tracks
args.sides, # nr_sides args.sides, # nr_sides
@ -59,7 +68,24 @@ def main(argv):
out_f.write(bytearray(b'\xff'*(tlut_blocks*512-args.cyls*4))) out_f.write(bytearray(b'\xff'*(tlut_blocks*512-args.cyls*4)))
# Data # Data
out_f.write(bytearray(b'\x88'*(blocks*512*args.cyls))) if not args.hard_sectors:
out_f.write(bytearray(b'\x88'*(blocks*512*args.cyls)))
else:
trk_side = bytearray(b'\x88')*(raw_bytes//2)
sector_len = float(raw_bytes//2) / args.hard_sectors
trk_side.insert(-int(sector_len)//2, 0x8F)
for sector in range(args.hard_sectors-1, -1, -1):
trk_side.insert(int(sector_len*sector), 0x8F)
assert len(trk_side) == bytes//2
trk_side += b'\x0F'*(256 - len(trk_side)%256)
assert len(trk_side) == blocks*256
trk = bytearray()
for pos in range(0, len(trk_side), 256):
trk.extend(trk_side[pos:pos+256])
trk.extend(trk_side[pos:pos+256])
for _ in range(args.cyls):
out_f.write(trk)
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv) main(sys.argv)

View file

@ -12,6 +12,8 @@ import sys,struct,argparse
def main(argv): def main(argv):
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter) 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, parser.add_argument("--window", type=float, default=5.5,
help="data window, seconds") help="data window, seconds")
parser.add_argument("--total", type=float, default=8.0, parser.add_argument("--total", type=float, default=8.0,
@ -22,10 +24,10 @@ def main(argv):
bit_ms = 0.004916 bit_ms = 0.004916
total_bytes = int(args.total * 1000.0 / bit_ms / 8) total_bytes = int(args.total * 1000.0 / bit_ms / 8)
window_bytes = int(args.window * 1000.0 / bit_ms / 8) window_bytes = int(args.window * 1000.0 / bit_ms / 8)
init_bytes = int(500.0 / bit_ms / 8) init_bytes = int(args.lead_in * 1000.0 / bit_ms / 8)
assert (2*init_bytes + window_bytes) < total_bytes, "Window too large" assert (2*init_bytes + window_bytes) < total_bytes, "Window too large"
print("Lead-In: %.2f sec -> %u bytes" % (0.5, init_bytes)) print("Lead-In: %.2f sec -> %u bytes" % (args.lead_in, init_bytes))
print("Window: %.2f sec -> %u bytes" % (args.window, window_bytes)) print("Window: %.2f sec -> %u bytes" % (args.window, window_bytes))
print("TOTAL: %.2f sec -> %u bytes" % (args.total, total_bytes)) print("TOTAL: %.2f sec -> %u bytes" % (args.total, total_bytes))

View file

@ -1,21 +1,156 @@
# mk_update.py # mk_update.py new <output> <firmware> <model>
# mk_update.py old <output> <firmware>
# mk_update.py verify <update_file>
# #
# Convert a raw firmware binary into an update file for our bootloader: # Convert a raw firmware binary into an update file for our bootloader.
#
# New Update Format (Little endian, unless otherwise stated):
# File Header:
# 4 bytes: 'FFUP'
# 4 bytes: <offset to catalogue>
# 4 bytes: <number of catalogue entries>
# Catalogue Entry:
# 1 byte: <hw_model>
# 3 bytes: mbz
# 4 bytes: <offset>
# 4 bytes: <length>
# Catalog Footer:
# 2 bytes: 'FZ'
# 2 bytes: CRC16-CCITT, seed 0xFFFF (big endian)
# Payload Footer:
# 2 bytes: 'FY'
# 2 bytes: CRC16-CCITT, seed 0xFFFF (big endian)
# File Footer:
# 4 bytes: CRC32 (MPEG-2, big endian)
# 4 bytes: 'FFUP'
#
# Old Update Format:
# N bytes: <raw binary data> # N bytes: <raw binary data>
# 2 bytes: 'FY' # 2 bytes: 'FY'
# 2 bytes: CRC16-CCITT, seed 0xFFFF, stored big endian # 2 bytes: CRC16-CCITT, seed 0xFFFF, stored big endian
# #
# Written & released by Keir Fraser <keir.xen@gmail.com> # Written & released by Keir Fraser <keir.xen@gmail.com>
# #
# This is free and unencumbered software released into the public domain. # This is free and unencumbered software released into the public domain.
# See the file COPYING for more details, or visit <http://unlicense.org>. # See the file COPYING for more details, or visit <http://unlicense.org>.
import crcmod.predefined import crcmod.predefined
import struct, sys import re, struct, sys, os
def main(argv): name_to_hw_model = { 'stm32f105': 1,
'at32f435': 4 }
hw_model_to_name = { 1: 'STM32F105',
4: 'AT32F435' }
class Firmware:
def __init__(self,model,binary):
self.model = model
self.binary = binary
def __str__(self):
s = hw_model_to_name[self.model] + ': '
s += '%d bytes' % len(self.binary)
return s
class Catalog:
def __init__(self, f=None):
self.catalog = []
if f is None:
return
b = f.read()
assert len(b) > 12, 'short header'
# Check the file footer
crc32 = crcmod.predefined.Crc('crc-32-mpeg')
crc32.update(b[:-4])
assert crc32.crcValue == 0, 'bad footer crc32'
assert b[-4:] == b'FFUP', 'bad footer signature'
# Check the file header
sig, off, nr = struct.unpack('<4s2I', b[:12])
assert sig == b'FFUP', 'bad header signature'
assert off == 12, 'unexpected header offset'
header_size = off + nr*12 + 4
assert len(b) >= header_size, 'short header'
# Check the header CRC16
crc16 = crcmod.predefined.Crc('crc-ccitt-false')
crc16.update(b[:header_size])
assert crc16.crcValue == 0
for i in range(nr):
# Read catalog entry and payload
m, o, l = struct.unpack('<B3x2I', b[off+i*12:off+(i+1)*12])
assert len(b) >= o+l, 'payload past end of file'
fw = b[o:o+l]
# Check the payload CRC16
crc16 = crcmod.predefined.Crc('crc-ccitt-false')
crc16.update(fw)
assert crc16.crcValue == 0
# Check the payload footer
sig, = struct.unpack('<2s', fw[-4:-2])
assert sig == b'FY', 'Footer signature must be FY'
# All good: Append to the catalog
self.append(Firmware(m, fw))
def append(self,firmware):
# Models must be uniquely represented in the catalog
for fw in self.catalog:
assert fw.model != firmware.model, 'Model already in catalog'
self.catalog.append(firmware)
def serialise(self):
# Header
b = struct.pack('<4s2I', b'FFUP', 12, len(self.catalog))
# Catalog entries
off = 12 + len(self.catalog)*12 + 4
for firmware in self.catalog:
b += struct.pack('<B3x2I', firmware.model, off,
len(firmware.binary))
off += len(firmware.binary)
# Catalog footer
b += b'FZ'
crc16 = crcmod.predefined.Crc('crc-ccitt-false')
crc16.update(b)
b += struct.pack(">H", crc16.crcValue)
# Payloads
for firmware in self.catalog:
b += firmware.binary
# File footer
crc32 = crcmod.predefined.Crc('crc-32-mpeg')
crc32.update(b)
b += struct.pack(">I4s", crc32.crcValue, b'FFUP')
return b
# New: 'flashfloppy-*.upd'
def new_upd(argv):
# Open the catalog, or else create a new one
try:
with open(argv[0], 'rb') as f:
catalog = Catalog(f)
except FileNotFoundError:
catalog = Catalog()
# Read the new firmware payload
with open(argv[1], 'rb') as f:
b = f.read()
assert (len(b) & 3) == 0, "input is not longword padded"
# Append the payload footer
b += b'FY'
crc16 = crcmod.predefined.Crc('crc-ccitt-false')
crc16.update(b)
b += struct.pack(">H", crc16.crcValue)
# Add the new firmware to the catalog
catalog.append(Firmware(name_to_hw_model[argv[2]], b))
# Rewrite the catalog
with open(argv[0], 'wb') as f:
f.write(catalog.serialise())
# Old: 'FF_Gotek*.upd"
def old_upd(argv):
in_f = open(argv[1], "rb") in_f = open(argv[1], "rb")
out_f = open(argv[2], "wb") out_f = open(argv[0], "wb")
in_dat = in_f.read() in_dat = in_f.read()
in_len = len(in_dat) in_len = len(in_dat)
assert (in_len & 3) == 0, "input is not longword padded" assert (in_len & 3) == 0, "input is not longword padded"
@ -28,5 +163,42 @@ def main(argv):
in_dat = struct.pack(">H", crc16.crcValue) in_dat = struct.pack(">H", crc16.crcValue)
out_f.write(in_dat) out_f.write(in_dat)
def verify_upd(argv):
# Read the file footer to work out type of update file, old vs new
with open(argv[0], 'rb') as f:
f.seek(-4, os.SEEK_END)
sig = f.read(2)
if sig == b'FY':
# Old
print('Old Update File:')
with open(argv[0], 'rb') as f:
b = f.read()
crc16 = crcmod.predefined.Crc('crc-ccitt-false')
crc16.update(b)
assert crc16.crcValue == 0
print(' %s: %d bytes' % (hw_model_to_name[1], len(b)))
else:
# New
print('New Update File:')
with open(argv[0], 'rb') as f:
catalog = Catalog(f)
for firmware in catalog.catalog:
print(' ' + str(firmware))
def main(argv):
if argv[1] == 'new':
dat = new_upd(argv[2:])
elif argv[1] == 'old':
dat = old_upd(argv[2:])
elif argv[1] == 'verify':
verify_upd(argv[2:])
return
else:
assert False
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv) main(sys.argv)
# Local variables:
# python-indent: 4
# End:

View file

@ -1,3 +1,5 @@
# Uncomment the following for clone chips
#set CPUTAPID 0x2ba01477
source [find interface/stlink.cfg] source [find interface/stlink.cfg]

29
scripts/srcdir.py Normal file
View file

@ -0,0 +1,29 @@
# srcdir.py
#
# Helper script to locate the relative source folder for a given object folder.
# For example:
# objdir = /path/to/out/stm32f105/prod/floppy/usb
# srcdir = ../../../../../src/usb
#
# 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>.
import sys, re
# /out/<mcu>/<level>/<target>
NR_LEVELS = 4
objdir = sys.argv[1]
# stem = /out/<mcu>/<level>/target[/<rest_of_path>]
stem = objdir[objdir.rfind('/out'):]
# stem = [/<rest_of_path>]
m = re.match('/[^/]*'*NR_LEVELS+'(/.*)?', stem)
stem = '' if m.group(1) is None else m.group(1)
# srcdir = path to sources, relative to objdir
srcdir = '../'*(NR_LEVELS+stem.count('/')) + 'src' + stem
print(srcdir)

View file

@ -5,8 +5,6 @@ MEMORY
FLASH (rx) : ORIGIN = FLASH_BASE, LENGTH = FLASH_LEN FLASH (rx) : ORIGIN = FLASH_BASE, LENGTH = FLASH_LEN
RAM (rwx) : ORIGIN = RAM_BASE, LENGTH = RAM_LEN RAM (rwx) : ORIGIN = RAM_BASE, LENGTH = RAM_LEN
} }
REGION_ALIAS("RO", FLASH);
REGION_ALIAS("RW", RAM);
SECTIONS SECTIONS
{ {
@ -24,17 +22,25 @@ SECTIONS
KEEP (*(.fini)) KEEP (*(.fini))
. = ALIGN(4); . = ALIGN(4);
_etext = .; _etext = .;
} >RO } >FLASH
#if MCU == AT32F435
.flags : {
_reset_flag = .;
. = . + 4;
} >RAM
#endif
.data : AT (_etext) { .data : AT (_etext) {
. = ALIGN(4); . = ALIGN(4);
_sdat = .; _sdat = .;
*(.data) *(.data)
*(.data*) *(.data*)
*(.ramfuncs)
. = ALIGN(4); . = ALIGN(4);
_edat = .; _edat = .;
_ldat = LOADADDR(.data); _ldat = LOADADDR(.data);
} >RW } >RAM
.bss : { .bss : {
. = ALIGN(8); . = ALIGN(8);
@ -44,12 +50,15 @@ SECTIONS
_thread_stackbottom = .; _thread_stackbottom = .;
. = . + 1024; . = . + 1024;
_thread_stacktop = .; _thread_stacktop = .;
_thread1_stackbottom = .;
. = . + 512;
_thread1_stacktop = .;
_sbss = .; _sbss = .;
*(.bss) *(.bss)
*(.bss*) *(.bss*)
. = ALIGN(4); . = ALIGN(4);
_ebss = .; _ebss = .;
} >RW } >RAM
/DISCARD/ : { /DISCARD/ : {
*(.eh_frame) *(.eh_frame)

1
src/.gitignore vendored
View file

@ -1 +0,0 @@
/ff_cfg_defaults.h

View file

@ -1,7 +0,0 @@
#define FLASH_BASE 0x08008000
#define FLASH_LEN 94K
#define RAM_BASE 0x20000000
#define RAM_LEN 64K
#include "../scripts/stm32f10x.ld.S"

View file

@ -1,27 +1,48 @@
OBJS += arena.o OBJS += arena.o
OBJS += build_info.o OBJS += build_info.o
OBJS += cache.o
OBJS += cancellation.o OBJS += cancellation.o
OBJS += config.o OBJS += config.o
OBJS += crc.o OBJS += crc.o
OBJS += flash_cfg.o OBJS += flash_cfg.o
OBJS += vectors.o OBJS += vectors.o
OBJS += fpec.o OBJS += file_cache.o
OBJS += fpec_$(mcu).o
OBJS += fs.o OBJS += fs.o
OBJS += main.o OBJS += fs_async.o
OBJS += sd_spi.o OBJS += sd_spi.o
OBJS += spi.o OBJS += spi.o
OBJS += string.o OBJS += string.o
OBJS += stm32f10x.o OBJS += thread.o
OBJS += cortex.o mcu_$(mcu).o
OBJS += time.o OBJS += time.o
OBJS += timer.o OBJS += timer.o
OBJS += util.o OBJS += util.o
OBJS += volume.o OBJS += volume.o
OBJS-$(debug) += console.o
OBJS-$(logfile) += logfile.o
ifeq ($(bl_update),y)
OBJS += bl_update.o
SUBDIRS += display gotek
else ifeq ($(io_test),y)
OBJS += io_test.o
SUBDIRS += display gotek
else ifeq ($(bootloader),y)
OBJS += fw_update.o
SUBDIRS += display fatfs gotek usb
else
OBJS += main.o
OBJS += cache.o
OBJS-$(floppy) += floppy.o OBJS-$(floppy) += floppy.o
OBJS-$(quickdisk) += quickdisk.o OBJS-$(quickdisk) += quickdisk.o
OBJS-$(debug) += console.o
OBJS-$(logfile) += logfile.o
SUBDIRS += display SUBDIRS += display
SUBDIRS += fatfs SUBDIRS += fatfs
@ -29,13 +50,17 @@ SUBDIRS += image
SUBDIRS += gotek SUBDIRS += gotek
SUBDIRS += usb SUBDIRS += usb
.PHONY: build_info.c endif
.PHONY: $(SRCDIR)/build_info.c
build_info.o: CFLAGS += -DFW_VER="\"$(FW_VER)\"" build_info.o: CFLAGS += -DFW_VER="\"$(FW_VER)\""
flash_cfg.o: ff_cfg_defaults.h # Avoid infinite loops due to GCC noticing code that can be replaced by a call
# to a standard library function... within our implementation of that function.
util.o: CFLAGS += -fno-tree-loop-distribute-patterns
ff_cfg_defaults.h: $(ROOT)/examples/FF.CFG ff_cfg_defaults.h: $(ROOT)/examples/FF.CFG
$(PYTHON) $(ROOT)/scripts/mk_config.py $< $@ $(PYTHON) $(ROOT)/scripts/mk_config.py $< $@
clean:: main.o flash_cfg.o: ff_cfg_defaults.h
rm -f ff_cfg_defaults.h main.o flash_cfg.o: CFLAGS += -iquote .

View file

@ -9,14 +9,9 @@
* See the file COPYING for more details, or visit <http://unlicense.org>. * See the file COPYING for more details, or visit <http://unlicense.org>.
*/ */
#define ram_kb 64
#define ram_bytes (ram_kb*1024)
#define heap_bot (_ebss) #define heap_bot (_ebss)
#define heap_top ((char *)0x20000000 + ram_bytes)
static char *heap_p; static char *heap_p;
static char *heap_top;
void *arena_alloc(uint32_t sz) void *arena_alloc(uint32_t sz)
{ {
@ -39,6 +34,7 @@ uint32_t arena_avail(void)
void arena_init(void) void arena_init(void)
{ {
heap_p = heap_bot; heap_p = heap_bot;
heap_top = (char *)0x20000000 + ram_kb*1024;
} }
/* /*

View file

@ -20,8 +20,13 @@
*/ */
/* Reflash the main bootloader (first 32kB). */ /* Reflash the main bootloader (first 32kB). */
#if MCU == STM32F105
#define FIRMWARE_START 0x08000000 #define FIRMWARE_START 0x08000000
#define FIRMWARE_END 0x08008000 #define FIRMWARE_END 0x08008000
#elif MCU == AT32F435
#define FIRMWARE_START 0x08000000
#define FIRMWARE_END 0x0800c000
#endif
/* The update payload. */ /* The update payload. */
extern char update_start[], update_end[]; extern char update_start[], update_end[];
@ -30,7 +35,7 @@ asm (
" .align 4\n" " .align 4\n"
" .global update_start, update_end\n" " .global update_start, update_end\n"
"update_start:\n" "update_start:\n"
" .incbin \"../bootloader/Bootloader.bin\"\n" " .incbin \"../bootloader/target.bin\"\n"
"update_end:\n" "update_end:\n"
" .previous\n" " .previous\n"
); );
@ -50,7 +55,7 @@ uint8_t board_id;
static void erase_old_firmware(void) static void erase_old_firmware(void)
{ {
uint32_t p; uint32_t p;
for (p = FIRMWARE_START; p < FIRMWARE_END; p += FLASH_PAGE_SIZE) for (p = FIRMWARE_START; p < FIRMWARE_END; p += flash_page_size)
fpec_page_erase(p); fpec_page_erase(p);
} }
@ -88,7 +93,7 @@ int main(void)
int retries = 0; int retries = 0;
/* Relocate DATA. Initialise BSS. */ /* Relocate DATA. Initialise BSS. */
if (_sdat != _ldat) if (&_sdat[0] != &_ldat[0])
memcpy(_sdat, _ldat, _edat-_sdat); memcpy(_sdat, _ldat, _edat-_sdat);
memset(_sbss, 0, _ebss-_sbss); memset(_sbss, 0, _ebss-_sbss);
@ -99,7 +104,7 @@ int main(void)
board_init(); board_init();
delay_ms(200); /* 5v settle */ delay_ms(200); /* 5v settle */
printk("\n** FF Update Firmware for Gotek\n", fw_ver); printk("\n** FF Update Firmware %s for Gotek\n", fw_ver);
printk("** Keir Fraser <keir.xen@gmail.com>\n"); printk("** Keir Fraser <keir.xen@gmail.com>\n");
printk("** https://github.com/keirf/FlashFloppy\n\n"); printk("** https://github.com/keirf/FlashFloppy\n\n");

View file

@ -26,7 +26,8 @@ struct cache {
static struct cache *cache; static struct cache *cache;
#define CACHE_HASH(_id) ((_id)&31) #define CACHE_HASH(_id) ((_id)&31)
struct cache *cache_init(void *start, void *end, unsigned int item_sz) struct cache *cache_init(void *start, void *end, unsigned int item_sz,
unsigned int *entry_cnt)
{ {
uint8_t *s, *e; uint8_t *s, *e;
int i, nitm; int i, nitm;
@ -38,10 +39,11 @@ struct cache *cache_init(void *start, void *end, unsigned int item_sz)
e = (uint8_t *)((uint32_t)end & ~3); e = (uint8_t *)((uint32_t)end & ~3);
nitm = ((e - s) - (int)sizeof(*c)) / (int)(sizeof(*cent) + item_sz); nitm = ((e - s) - (int)sizeof(*c)) / (int)(sizeof(*cent) + item_sz);
if (nitm < 8) { if (nitm < 3) {
printk("No cache: too small (%d)\n", e - s); printk("No cache: too small (%d)\n", e - s);
return NULL; return NULL;
} }
*entry_cnt = nitm;
/* Initialise the empty cache structure. */ /* Initialise the empty cache structure. */
cache = c = (struct cache *)s; cache = c = (struct cache *)s;
@ -65,6 +67,11 @@ struct cache *cache_init(void *start, void *end, unsigned int item_sz)
} }
const void *cache_lookup(struct cache *c, uint32_t id) const void *cache_lookup(struct cache *c, uint32_t id)
{
return cache_lookup_mutable(c, id);
}
void *cache_lookup_mutable(struct cache *c, uint32_t id)
{ {
struct list_head *hash, *ent; struct list_head *hash, *ent;
struct cache_ent *cent; struct cache_ent *cent;
@ -85,14 +92,57 @@ found:
return cent->dat; return cent->dat;
} }
void *cache_lru_mutable(struct cache *c, uint32_t *id)
{
struct cache_ent *cent = container_of(c->lru.prev, struct cache_ent, lru);
if (list_is_empty(&cent->hash))
return NULL;
*id = cent->id;
return cent->dat;
}
void *cache_lru_search_mutable(struct cache *c, uint32_t *id)
{
for (struct list_head *ent = c->lru.prev; ent != &c->lru; ent = ent->prev) {
struct cache_ent *cent = container_of(ent, struct cache_ent, lru);
if (list_is_empty(&cent->hash))
continue;
*id = cent->id;
return cent->dat;
}
return NULL;
}
void *cache_lru_next_mutable(struct cache *c, const void* ent, uint32_t *id)
{
const struct cache_ent *cent
= container_of(ent, const struct cache_ent, dat);
struct list_head *ent_prev = cent->lru.prev;
struct cache_ent *cent_prev;
if (ent_prev == &c->lru)
return NULL;
cent_prev = container_of(ent_prev, struct cache_ent, lru);
*id = cent_prev->id;
return cent_prev->dat;
}
void cache_update(struct cache *c, uint32_t id, const void *dat) void cache_update(struct cache *c, uint32_t id, const void *dat)
{
bool_t created;
void *p = cache_update_mutable(c, id, &created);
memcpy(p, dat, c->item_sz);
}
void *cache_update_mutable(struct cache *c, uint32_t id, bool_t *created)
{ {
struct cache_ent *cent; struct cache_ent *cent;
void *p; void *p;
/* Already in the cache? Just update the existing data. */ /* Already in the cache? Just update the existing data. */
if ((p = (void *)cache_lookup(c, id)) != NULL) if ((p = cache_lookup_mutable(c, id)) != NULL) {
goto found; *created = FALSE;
return p;
}
/* Steal the oldest cache entry from the LRU. */ /* Steal the oldest cache entry from the LRU. */
cent = container_of(c->lru.prev, struct cache_ent, lru); cent = container_of(c->lru.prev, struct cache_ent, lru);
@ -108,9 +158,8 @@ void cache_update(struct cache *c, uint32_t id, const void *dat)
list_insert_head(&c->lru, &cent->lru); list_insert_head(&c->lru, &cent->lru);
list_insert_head(&c->hash[CACHE_HASH(id)], &cent->hash); list_insert_head(&c->hash[CACHE_HASH(id)], &cent->hash);
found: *created = TRUE;
/* Finally, store away the actual item data. */ return p;
memcpy(p, dat, c->item_sz);
} }
void cache_update_N(struct cache *c, uint32_t id, void cache_update_N(struct cache *c, uint32_t id,

View file

@ -9,23 +9,27 @@
* See the file COPYING for more details, or visit <http://unlicense.org>. * See the file COPYING for more details, or visit <http://unlicense.org>.
*/ */
asm ( __attribute__((naked))
".global call_cancellable_fn\n" int call_cancellable_fn(struct cancellation *c, int (*fn)(void *), void *arg) {
".thumb_func \n" asm (
"call_cancellable_fn:\n" " stmdb.w sp!, {r0, r4, r5, r6, r7, r8, r9, r10, r11, lr}\n"
" stmdb.w sp!, {r0, r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" " str sp, [r0]\n" /* c->sp = PSP */
" str sp, [r0]\n" /* c->sp = PSP */ " mov r0, r2\n" /* r0 = arg */
" mov r0, r2\n" /* r0 = arg */ " blx r1\n" /* (*fn)(arg) */
" blx r1\n" /* (*fn)(arg) */ " ldr r2, [sp]\n"
" ldr r2, [sp]\n" " movs r1, #0\n"
" movs r1, #0\n" " str r1, [r2]\n" /* c->sp = NULL */
" str r1, [r2]\n" /* c->sp = NULL */ " b do_cancel\n"
"do_cancel:\n" );
" add sp, #4\n" }
" ldmia.w sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n"
);
void do_cancel(void); __attribute__((naked))
void do_cancel(void) {
asm (
" add sp, #4\n"
" ldmia.w sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n"
);
}
/* An exception context for cancel_call(), when initially called from Thread /* An exception context for cancel_call(), when initially called from Thread
* context. */ * context. */

View file

@ -1,7 +1,7 @@
/* /*
* console.c * console.c
* *
* printf-style interface to USART1. * printf-style interface to USART.
* *
* Written & released by Keir Fraser <keir.xen@gmail.com> * Written & released by Keir Fraser <keir.xen@gmail.com>
* *
@ -12,10 +12,27 @@
#define BAUD 3000000 /* 3Mbaud */ #define BAUD 3000000 /* 3Mbaud */
#define USART1_IRQ 37 #define USART1_IRQ 37
#define USART3_IRQ 39
#if 1
#define usart usart1
#define USART_IRQ USART1_IRQ
#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. */ /* Normally flush to serial is asynchronously executed in a low-pri IRQ. */
void IRQ_44(void) __attribute__((alias("SOFTIRQ_console"))); #define CONSOLE_SOFTIRQ SOFTIRQ_1
#define CONSOLE_SOFTIRQ 44 DEFINE_IRQ(CONSOLE_SOFTIRQ, "SOFTIRQ_console");
/* We stage serial output in a ring buffer. */ /* We stage serial output in a ring buffer. */
static char ring[2048]; static char ring[2048];
@ -32,9 +49,9 @@ static void flush_ring_to_serial(void)
barrier(); barrier();
while (c != p) { while (c != p) {
while (!(usart1->sr & USART_SR_TXE)) while (!(usart->sr & USART_SR_TXE))
cpu_relax(); cpu_relax();
usart1->dr = ring[MASK(c++)]; usart->dr = ring[MASK(c++)];
} }
barrier(); barrier();
@ -114,16 +131,27 @@ void console_sync(void)
void console_init(void) void console_init(void)
{ {
/* Turn on the clocks. */ /* Turn on the clocks. */
#if USART_IRQ == USART1_IRQ
rcc->apb2enr |= RCC_APB2ENR_USART1EN; rcc->apb2enr |= RCC_APB2ENR_USART1EN;
#else
rcc->apb1enr |= RCC_APB1ENR_USART3EN;
#endif
/* Enable TX pin (PA9) for USART output, RX pin (PA10) as input. */ /* Enable TX pin for USART output, RX pin as input. */
gpio_configure_pin(gpioa, 9, AFO_pushpull(_10MHz)); #if MCU == STM32F105
gpio_configure_pin(gpioa, 10, GPI_pull_up); gpio_configure_pin(usart_gpio, usart_tx_pin, AFO_pushpull(_10MHz));
gpio_configure_pin(usart_gpio, usart_rx_pin, GPI_pull_up);
#elif MCU == AT32F435
gpio_set_af(usart_gpio, usart_tx_pin, 7);
gpio_set_af(usart_gpio, usart_rx_pin, 7);
gpio_configure_pin(usart_gpio, usart_tx_pin, AFO_pushpull(_10MHz));
gpio_configure_pin(usart_gpio, usart_rx_pin, AFI(PUPD_up));
#endif
/* BAUD, 8n1. */ /* BAUD, 8n1. */
usart1->brr = SYSCLK / BAUD; usart->brr = PCLK / BAUD;
usart1->cr1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; usart->cr1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
usart1->cr3 = 0; usart->cr3 = 0;
IRQx_set_prio(CONSOLE_SOFTIRQ, CONSOLE_IRQ_PRI); IRQx_set_prio(CONSOLE_SOFTIRQ, CONSOLE_IRQ_PRI);
IRQx_enable(CONSOLE_SOFTIRQ); IRQx_enable(CONSOLE_SOFTIRQ);
@ -133,10 +161,16 @@ void console_init(void)
* any serial input to cause a crash dump of the stuck context. */ * any serial input to cause a crash dump of the stuck context. */
void console_crash_on_input(void) void console_crash_on_input(void)
{ {
(void)usart1->dr; /* clear UART_SR_RXNE */ if (mcu_package == MCU_QFN32) {
usart1->cr1 |= USART_CR1_RXNEIE; /* Unavailable: PA10 is reassigned from SER_RX to K4 (rotary select
IRQx_set_prio(USART1_IRQ, RESET_IRQ_PRI); * on the KC30 header). */
IRQx_enable(USART1_IRQ); return;
}
(void)usart->dr; /* clear UART_SR_RXNE */
usart->cr1 |= USART_CR1_RXNEIE;
IRQx_set_prio(USART_IRQ, RESET_IRQ_PRI);
IRQx_enable(USART_IRQ);
} }
/* /*

View file

@ -1,7 +1,7 @@
/* /*
* stm32f10x.c * cortex.c
* *
* Core and peripheral registers. * STM32 ARM Cortex management.
* *
* Written & released by Keir Fraser <keir.xen@gmail.com> * Written & released by Keir Fraser <keir.xen@gmail.com>
* *
@ -73,8 +73,12 @@ void EXC_unexpected(struct extra_exception_frame *extra)
if ((psp >= (uint32_t)_thread_stackbottom) if ((psp >= (uint32_t)_thread_stackbottom)
&& (psp < (uint32_t)_thread_stacktop)) { && (psp < (uint32_t)_thread_stacktop)) {
printk("Process Call Trace:", psp); printk("Process Call Trace (Thread %d):", 0);
show_stack(psp, (uint32_t)_thread_stacktop); show_stack(psp, (uint32_t)_thread_stacktop);
} else if ((psp >= (uint32_t)_thread1_stackbottom)
&& (psp < (uint32_t)_thread1_stacktop)) {
printk("Process Call Trace (Thread %d):", 1);
show_stack(psp, (uint32_t)_thread1_stacktop);
} }
system_reset(); system_reset();
@ -110,75 +114,18 @@ static void exception_init(void)
scb->shpr3 = 0xff<<16; scb->shpr3 = 0xff<<16;
} }
static void clock_init(void) static void sysclk_init(void)
{ {
/* Flash controller: reads require 2 wait states at 72MHz. */ /* Enable SysTick counter. */
flash->acr = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY(2);
/* Start up the external oscillator. */
rcc->cr |= RCC_CR_HSEON;
while (!(rcc->cr & RCC_CR_HSERDY))
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);
/* Enable and stabilise the PLL. */
rcc->cr |= RCC_CR_PLLON;
while (!(rcc->cr & RCC_CR_PLLRDY))
cpu_relax();
/* 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();
/* Internal oscillator no longer needed. */
rcc->cr &= ~RCC_CR_HSION;
/* Enable SysTick counter at 72/8=9MHz. */
stk->load = STK_MASK; stk->load = STK_MASK;
stk->ctrl = STK_CTRL_ENABLE; stk->ctrl = STK_CTRL_ENABLE;
} }
static void gpio_init(GPIO gpio) void cortex_init(void)
{
/* Floating Input. Reference Manual states that JTAG pins are in PU/PD
* mode at reset, so ensure all PU/PD are disabled. */
gpio->crl = gpio->crh = 0x44444444u;
}
static void peripheral_init(void)
{
/* Enable basic GPIO and AFIO clocks, all timers, and DMA. */
rcc->apb1enr = (RCC_APB1ENR_TIM2EN |
RCC_APB1ENR_TIM3EN |
RCC_APB1ENR_TIM4EN);
rcc->apb2enr = (RCC_APB2ENR_IOPAEN |
RCC_APB2ENR_IOPBEN |
RCC_APB2ENR_IOPCEN |
RCC_APB2ENR_AFIOEN |
RCC_APB2ENR_TIM1EN);
rcc->ahbenr = RCC_AHBENR_DMA1EN;
/* Turn off serial-wire JTAG and reclaim the GPIOs. */
afio->mapr = AFIO_MAPR_SWJ_CFG_DISABLED;
/* All pins in a stable state. */
gpio_init(gpioa);
gpio_init(gpiob);
gpio_init(gpioc);
}
void stm32_init(void)
{ {
exception_init(); exception_init();
clock_init();
peripheral_init();
cpu_sync(); cpu_sync();
sysclk_init();
} }
void delay_ticks(unsigned int ticks) void delay_ticks(unsigned int ticks)
@ -210,18 +157,6 @@ void delay_ms(unsigned int ms)
delay_ticks(ms * 1000u * STK_MHZ); delay_ticks(ms * 1000u * STK_MHZ);
} }
void gpio_configure_pin(GPIO gpio, unsigned int pin, unsigned int mode)
{
gpio_write_pin(gpio, pin, mode >> 4);
mode &= 0xfu;
if (pin >= 8) {
pin -= 8;
gpio->crh = (gpio->crh & ~(0xfu<<(pin<<2))) | (mode<<(pin<<2));
} else {
gpio->crl = (gpio->crl & ~(0xfu<<(pin<<2))) | (mode<<(pin<<2));
}
}
void system_reset(void) void system_reset(void)
{ {
console_sync(); console_sync();

View file

@ -1 +0,0 @@
/oled_font_*.c

View file

@ -1,13 +1,12 @@
OBJS += display.o OBJS += display.o
OBJS += lcd.o OBJS += lcd_$(mcu).o
OBJS += oled_font_6x13.o OBJS += oled_font_6x13.o
OBJS += led_7seg.o OBJS += led_7seg.o
lcd.o: CFLAGS += -Dfont_extra=1 ifneq ($(bootloader),y)
lcd_$(mcu).o: CFLAGS += -Dfont_extra=1
OBJS += oled_font_8x16.o OBJS += oled_font_8x16.o
endif
oled_font_%.c: $(ROOT)/fonts/%.bdf oled_font_%.c: $(ROOT)/fonts/%.bdf
$(PYTHON) $(ROOT)/scripts/mk_font.py $< oled_font_$* $(PYTHON) $(ROOT)/scripts/mk_font.py $< oled_font_$*
clean::
rm -f oled_font_*.c

1010
src/display/lcd_at32f435.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -43,25 +43,62 @@ struct packed i2c_osd_info {
}; };
/* STM32 I2C peripheral. */ /* STM32 I2C peripheral. */
#define i2c i2c2 static volatile struct i2c *i2c;
#define SCL 10
#define SDA 11 const static struct i2c_cfg {
uint8_t en;
uint8_t scl;
uint8_t sda;
uint8_t error_irq;
uint8_t event_irq;
uint8_t dma_tx;
uint8_t dma_rx;
} i2c1_cfg = {
.en = 21, /* RCC_APB1ENR_I2C1EN */
.scl = 6,
.sda = 7,
.error_irq = 32,
.event_irq = 31,
.dma_tx = 6,
.dma_rx = 7,
}, i2c2_cfg = {
.en = 22, /* RCC_APB1ENR_I2C2EN */
.scl = 10,
.sda = 11,
.error_irq = 34,
.event_irq = 33,
.dma_tx = 4,
.dma_rx = 5
}, *i2c_cfg;
#define SCL i2c_cfg->scl
#define SDA i2c_cfg->sda
#define PCLK_MHZ APB1_MHZ
/* I2C error ISR. */ /* I2C error ISR. */
#define I2C_ERROR_IRQ 34 #define I2C_ERROR_IRQ i2c_cfg->error_irq
void IRQ_34(void) __attribute__((alias("IRQ_i2c_error"))); void IRQ_34(void) __attribute__((alias("IRQ_i2c_error")));
void IRQ_32(void) __attribute__((alias("IRQ_i2c_error")));
/* I2C event ISR. */ /* I2C event ISR. */
#define I2C_EVENT_IRQ 33 #define I2C_EVENT_IRQ i2c_cfg->event_irq
void IRQ_33(void) __attribute__((alias("IRQ_i2c_event"))); void IRQ_33(void) __attribute__((alias("IRQ_i2c_event")));
void IRQ_31(void) __attribute__((alias("IRQ_i2c_event")));
/* DMA completion ISR. */ /* DMA Tx. */
#define DMA1_CH4_IRQ 14 #define i2c_tx_dma dma1->ch[i2c_cfg->dma_tx-1]
#define DMA_TX_CGIF DMA_IFCR_CGIF(i2c_cfg->dma_tx)
#define DMA_TX_IRQ (i2c_cfg->dma_tx + 10)
void IRQ_14(void) __attribute__((alias("IRQ_dma_tx_tc"))); void IRQ_14(void) __attribute__((alias("IRQ_dma_tx_tc")));
void IRQ_16(void) __attribute__((alias("IRQ_dma_tx_tc")));
/* DMA completion ISR. */ /* DMA Rx. */
#define DMA1_CH5_IRQ 15 #define i2c_rx_dma dma1->ch[i2c_cfg->dma_rx-1]
#define DMA_RX_CGIF DMA_IFCR_CGIF(i2c_cfg->dma_rx)
#define DMA_RX_IRQ (i2c_cfg->dma_rx + 10)
void IRQ_15(void) __attribute__((alias("IRQ_dma_rx_tc"))); void IRQ_15(void) __attribute__((alias("IRQ_dma_rx_tc")));
void IRQ_17(void) __attribute__((alias("IRQ_dma_rx_tc")));
bool_t has_osd; bool_t has_osd;
uint8_t osd_buttons_tx; uint8_t osd_buttons_tx;
@ -126,8 +163,8 @@ static void IRQ_i2c_error(void)
i2c->cr1 = I2C_CR1_SWRST; i2c->cr1 = I2C_CR1_SWRST;
/* Clear the DMA controller. */ /* Clear the DMA controller. */
dma1->ch4.ccr = dma1->ch5.ccr = 0; i2c_tx_dma.ccr = i2c_rx_dma.ccr = 0;
dma1->ifcr = DMA_IFCR_CGIF(4) | DMA_IFCR_CGIF(5); dma1->ifcr = DMA_TX_CGIF | DMA_RX_CGIF;
timer_cancel(&timeout_timer); timer_cancel(&timeout_timer);
@ -168,21 +205,21 @@ static void dma_start(unsigned int sz)
ASSERT(sz <= sizeof(buffer)); ASSERT(sz <= sizeof(buffer));
if (in_osd == OSD_read) { if (in_osd == OSD_read) {
dma1->ch5.cndtr = sz; i2c_rx_dma.cndtr = sz;
dma1->ch5.ccr = (DMA_CCR_MSIZE_8BIT | i2c_rx_dma.ccr = (DMA_CCR_MSIZE_8BIT |
DMA_CCR_PSIZE_16BIT | DMA_CCR_PSIZE_16BIT |
DMA_CCR_MINC | DMA_CCR_MINC |
DMA_CCR_DIR_P2M | DMA_CCR_DIR_P2M |
DMA_CCR_TCIE | DMA_CCR_TCIE |
DMA_CCR_EN); DMA_CCR_EN);
} else { } else {
dma1->ch4.cndtr = sz; i2c_tx_dma.cndtr = sz;
dma1->ch4.ccr = (DMA_CCR_MSIZE_8BIT | i2c_tx_dma.ccr = (DMA_CCR_MSIZE_8BIT |
DMA_CCR_PSIZE_16BIT | DMA_CCR_PSIZE_16BIT |
DMA_CCR_MINC | DMA_CCR_MINC |
DMA_CCR_DIR_M2P | DMA_CCR_DIR_M2P |
DMA_CCR_TCIE | DMA_CCR_TCIE |
DMA_CCR_EN); DMA_CCR_EN);
} }
/* Set the timeout timer in case the DMA hangs for any reason. */ /* Set the timeout timer in case the DMA hangs for any reason. */
@ -211,7 +248,8 @@ static unsigned int osd_prep_buffer(void)
uint16_t order = menu_mode ? 0x7903 : 0x7183; uint16_t order = menu_mode ? 0x7903 : 0x7183;
char *p; char *p;
uint8_t *q = buffer; uint8_t *q = buffer;
unsigned int row; unsigned int row, rows, heights;
int i;
if (++in_osd == OSD_read) { if (++in_osd == OSD_read) {
i2c->cr2 |= I2C_CR2_LAST | I2C_CR2_ITEVTEN; i2c->cr2 |= I2C_CR2_LAST | I2C_CR2_ITEVTEN;
@ -219,13 +257,31 @@ static unsigned int osd_prep_buffer(void)
return sizeof(struct i2c_osd_info); return sizeof(struct i2c_osd_info);
} }
if ((ff_cfg.osd_display_order != DORD_default)
&& (display_mode == DM_normal))
order = ff_cfg.osd_display_order;
heights = rows = 0;
for (i = 3; i >= 0; i--) {
/* Iterate over rows, bottom to top. */
row = order >> (i<<2);
/* Skip all trailing empty rows. */
if ((rows == 0) && ((row&7) == 7))
continue;
/* Count this row and check if it is double height. */
rows++;
heights <<= 1;
if (row & 8)
heights |= 1;
}
*q++ = OSD_BACKLIGHT | !!_bl; *q++ = OSD_BACKLIGHT | !!_bl;
*q++ = OSD_COLUMNS | lcd_columns; *q++ = OSD_COLUMNS | lcd_columns;
*q++ = OSD_ROWS | 3; *q++ = OSD_ROWS | rows;
*q++ = OSD_HEIGHTS | (menu_mode ? 4 : 2); *q++ = OSD_HEIGHTS | heights;
*q++ = OSD_BUTTONS | osd_buttons_tx; *q++ = OSD_BUTTONS | osd_buttons_tx;
*q++ = OSD_DATA; *q++ = OSD_DATA;
for (row = 0; row < 3; row++) { for (row = 0; row < rows; row++) {
p = text[(order >> (row * DORD_shift)) & DORD_row]; p = text[(order >> (row * DORD_shift)) & DORD_row];
memcpy(q, p, lcd_columns); memcpy(q, p, lcd_columns);
q += lcd_columns; q += lcd_columns;
@ -285,8 +341,8 @@ static unsigned int lcd_prep_buffer(void)
static void IRQ_dma_tx_tc(void) static void IRQ_dma_tx_tc(void)
{ {
/* Clear the DMA controller. */ /* Clear the DMA controller. */
dma1->ch4.ccr = 0; i2c_tx_dma.ccr = 0;
dma1->ifcr = DMA_IFCR_CGIF(4); dma1->ifcr = DMA_TX_CGIF;
/* Wait for BTF. We then get called back on dma_tx_tc_btf(). /* Wait for BTF. We then get called back on dma_tx_tc_btf().
* *
@ -319,8 +375,8 @@ static void IRQ_dma_rx_tc(void)
struct i2c_osd_info *info = (struct i2c_osd_info *)buffer; struct i2c_osd_info *info = (struct i2c_osd_info *)buffer;
/* Clear the DMA controller. */ /* Clear the DMA controller. */
dma1->ch5.ccr = 0; i2c_rx_dma.ccr = 0;
dma1->ifcr = DMA_IFCR_CGIF(5); dma1->ifcr = DMA_RX_CGIF;
/* Clean up I2C. */ /* Clean up I2C. */
i2c->cr2 &= ~I2C_CR2_LAST; i2c->cr2 &= ~I2C_CR2_LAST;
@ -371,6 +427,10 @@ static void i2c_stop(void)
i2c->cr1 |= I2C_CR1_STOP; i2c->cr1 |= I2C_CR1_STOP;
while (i2c->cr1 & I2C_CR1_STOP) while (i2c->cr1 & I2C_CR1_STOP)
continue; continue;
if (is_artery_mcu) {
i2c->cr1 = 0;
i2c->cr1 = I2C_CR1_PE;
}
} }
/* Synchronously transmit an I2C byte. */ /* Synchronously transmit an I2C byte. */
@ -473,7 +533,15 @@ bool_t lcd_init(void)
in_osd = OSD_no; in_osd = OSD_no;
osd_buttons_rx = 0; osd_buttons_rx = 0;
rcc->apb1enr |= RCC_APB1ENR_I2C2EN; if (mcu_package == MCU_QFN32) {
i2c = i2c1;
i2c_cfg = &i2c1_cfg;
} else {
i2c = i2c2;
i2c_cfg = &i2c2_cfg;
}
rcc->apb1enr |= 1<<i2c_cfg->en;
/* Check we have a clear I2C bus. Both clock and data must be high. If SDA /* Check we have a clear I2C bus. Both clock and data must be high. If SDA
* is stuck low then slave may be stuck in an ACK cycle. We can try to * is stuck low then slave may be stuck in an ACK cycle. We can try to
@ -520,9 +588,9 @@ bool_t lcd_init(void)
/* Standard Mode (100kHz) */ /* Standard Mode (100kHz) */
i2c->cr1 = 0; i2c->cr1 = 0;
i2c->cr2 = I2C_CR2_FREQ(36); i2c->cr2 = I2C_CR2_FREQ(PCLK_MHZ);
i2c->ccr = I2C_CCR_CCR(180); i2c->ccr = I2C_CCR_CCR(PCLK_MHZ*5);
i2c->trise = 37; i2c->trise = (PCLK_MHZ <= 62) ? PCLK_MHZ + 1 : 63;
i2c->cr1 = I2C_CR1_PE; i2c->cr1 = I2C_CR1_PE;
if (!reinit) { if (!reinit) {
@ -564,11 +632,7 @@ bool_t lcd_init(void)
lcd_rows = 4; lcd_rows = 4;
} else { } else {
lcd_columns = (ff_cfg.display_type >> _DISPLAY_lcd_columns) & 63; lcd_columns = (ff_cfg.display_type >> _DISPLAY_lcd_columns) & 63;
lcd_columns = max_t(uint8_t, lcd_columns, 16);
lcd_columns = min_t(uint8_t, lcd_columns, 40);
lcd_rows = (ff_cfg.display_type >> _DISPLAY_lcd_rows) & 7; lcd_rows = (ff_cfg.display_type >> _DISPLAY_lcd_rows) & 7;
lcd_rows = max_t(uint8_t, lcd_rows, 2);
lcd_rows = min_t(uint8_t, lcd_rows, 4);
} }
if (a != 0) { if (a != 0) {
@ -577,10 +641,14 @@ bool_t lcd_init(void)
i2c_addr = a; i2c_addr = a;
} else { } else {
is_oled_display = FALSE; is_oled_display = FALSE;
if (ff_cfg.display_type == DISPLAY_auto) lcd_columns = ff_cfg.osd_columns;
lcd_columns = 40;
} }
lcd_columns = max_t(uint8_t, lcd_columns, 16);
lcd_columns = min_t(uint8_t, lcd_columns, 40);
lcd_rows = max_t(uint8_t, lcd_rows, 2);
lcd_rows = min_t(uint8_t, lcd_rows, 4);
lcd_clear(); lcd_clear();
} }
@ -597,20 +665,20 @@ bool_t lcd_init(void)
i2c->cr2 |= I2C_CR2_ITERREN; i2c->cr2 |= I2C_CR2_ITERREN;
/* Initialise DMA1 channel 4 and its completion interrupt. */ /* Initialise DMA1 channel 4 and its completion interrupt. */
dma1->ch4.cmar = (uint32_t)(unsigned long)buffer; i2c_tx_dma.cmar = (uint32_t)(unsigned long)buffer;
dma1->ch4.cpar = (uint32_t)(unsigned long)&i2c->dr; i2c_tx_dma.cpar = (uint32_t)(unsigned long)&i2c->dr;
dma1->ifcr = DMA_IFCR_CGIF(4); dma1->ifcr = DMA_TX_CGIF;
IRQx_set_prio(DMA1_CH4_IRQ, I2C_IRQ_PRI); IRQx_set_prio(DMA_TX_IRQ, I2C_IRQ_PRI);
IRQx_clear_pending(DMA1_CH4_IRQ); IRQx_clear_pending(DMA_TX_IRQ);
IRQx_enable(DMA1_CH4_IRQ); IRQx_enable(DMA_TX_IRQ);
/* Initialise DMA1 channel 5 and its completion interrupt. */ /* Initialise DMA1 channel 5 and its completion interrupt. */
dma1->ch5.cmar = (uint32_t)(unsigned long)buffer; i2c_rx_dma.cmar = (uint32_t)(unsigned long)buffer;
dma1->ch5.cpar = (uint32_t)(unsigned long)&i2c->dr; i2c_rx_dma.cpar = (uint32_t)(unsigned long)&i2c->dr;
dma1->ifcr = DMA_IFCR_CGIF(5); dma1->ifcr = DMA_RX_CGIF;
IRQx_set_prio(DMA1_CH5_IRQ, I2C_IRQ_PRI); IRQx_set_prio(DMA_RX_IRQ, I2C_IRQ_PRI);
IRQx_clear_pending(DMA1_CH5_IRQ); IRQx_clear_pending(DMA_RX_IRQ);
IRQx_enable(DMA1_CH5_IRQ); IRQx_enable(DMA_RX_IRQ);
/* Timeout handler for if I2C transmission borks. */ /* Timeout handler for if I2C transmission borks. */
timer_init(&timeout_timer, timeout_fn, NULL); timer_init(&timeout_timer, timeout_fn, NULL);
@ -660,12 +728,12 @@ fail:
return FALSE; return FALSE;
IRQx_disable(I2C_EVENT_IRQ); IRQx_disable(I2C_EVENT_IRQ);
IRQx_disable(I2C_ERROR_IRQ); IRQx_disable(I2C_ERROR_IRQ);
IRQx_disable(DMA1_CH4_IRQ); IRQx_disable(DMA_TX_IRQ);
IRQx_disable(DMA1_CH5_IRQ); IRQx_disable(DMA_RX_IRQ);
i2c->cr1 &= ~I2C_CR1_PE; i2c->cr1 &= ~I2C_CR1_PE;
gpio_configure_pin(gpiob, SCL, GPI_pull_up); gpio_configure_pin(gpiob, SCL, GPI_pull_up);
gpio_configure_pin(gpiob, SDA, GPI_pull_up); gpio_configure_pin(gpiob, SDA, GPI_pull_up);
rcc->apb1enr &= ~RCC_APB1ENR_I2C2EN; rcc->apb1enr &= ~(1<<i2c_cfg->en);
return FALSE; return FALSE;
} }
@ -979,6 +1047,19 @@ fail:
return FALSE; return FALSE;
} }
static void oled_init_fast_mode(void)
{
/* Disable I2C (currently in Standard Mode). */
i2c->cr1 = 0;
/* Fast Mode (400kHz). */
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;
}
static void oled_init(void) static void oled_init(void)
{ {
static const uint8_t init_cmds[] = { static const uint8_t init_cmds[] = {
@ -998,19 +1079,11 @@ static void oled_init(void)
0xa0, /* segment mapping (default) */ 0xa0, /* segment mapping (default) */
0xc0, /* com scan direction (default) */ 0xc0, /* com scan direction (default) */
}; };
const uint8_t *cmds;
uint8_t dynamic_cmds[7], *dc; uint8_t dynamic_cmds[7], *dc;
uint8_t *p = buffer; uint8_t *p = buffer;
/* Disable I2C (currently in Standard Mode). */ if (!(ff_cfg.display_type & DISPLAY_slow))
i2c->cr1 = 0; oled_init_fast_mode();
/* Fast Mode (400kHz). */
i2c->cr2 = I2C_CR2_FREQ(36);
i2c->ccr = I2C_CCR_FS | I2C_CCR_CCR(30);
i2c->trise = 12;
i2c->cr1 = I2C_CR1_PE;
i2c->cr2 |= I2C_CR2_ITERREN;
if ((oled_model == OLED_unknown) && !oled_probe_model()) if ((oled_model == OLED_unknown) && !oled_probe_model())
goto fail; goto fail;
@ -1029,9 +1102,13 @@ static void oled_init(void)
*dc++ = (oled_height == 64) ? 0x12 : 0x02; *dc++ = (oled_height == 64) ? 0x12 : 0x02;
p += oled_queue_cmds(p, dynamic_cmds, dc - dynamic_cmds); p += oled_queue_cmds(p, dynamic_cmds, dc - dynamic_cmds);
/* Display is right-way-up, or rotated. */ /* Display orientation. */
cmds = (ff_cfg.display_type & DISPLAY_rotate) ? rot_cmds : norot_cmds; dc = dynamic_cmds;
p += oled_queue_cmds(p, cmds, sizeof(rot_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. */ /* Start off the I2C transaction. */
p += oled_start_i2c(p); p += oled_start_i2c(p);

View file

@ -21,14 +21,14 @@
#define CYCLE 8 #define CYCLE 8
/* TM1651, 74HC164: DAT = PB10, CLK = PB11 */ /* TM1651, 74HC164: DAT = PB10, CLK = PB11 */
#define DAT_PIN 10 static uint8_t DAT_PIN = 10;
#define CLK_PIN 11 static uint8_t CLK_PIN = 11;
/* TM1651, 74HC164: Alphanumeric segment arrangements. */ /* TM1651, 74HC164: Alphanumeric segment arrangements. */
static const uint8_t letters[] = { static const uint8_t letters[] = {
0x77, 0x7c, 0x58, 0x5e, 0x79, 0x71, 0x6f, 0x74, 0x04, /* a-i */ 0x77, 0x7c, 0x58, 0x5e, 0x79, 0x71, 0x6f, 0x74, 0x04, /* a-i */
0x0e, 0x08, 0x38, 0x40, 0x54, 0x5c, 0x73, 0x67, 0x50, /* j-r */ 0x0e, 0x08, 0x38, 0x40, 0x54, 0x5c, 0x73, 0x67, 0x50, /* j-r */
0x6d, 0x78, 0x1c, 0x09, 0x41, 0x76, 0x6e, 0x00 /* s-z */ 0x6d, 0x78, 0x1c, 0x09, 0x41, 0x76, 0x6e, 0x52 /* s-z */
}; };
static const uint8_t digits[] = { static const uint8_t digits[] = {
@ -261,6 +261,11 @@ void led_7seg_write_decimal(unsigned int val)
void led_7seg_init(void) void led_7seg_init(void)
{ {
if (mcu_package == MCU_QFN32) {
DAT_PIN = 6; /* PB6 */
CLK_PIN = 7; /* PB7 */
}
nr_digits = !tm1651_init() ? 3 : 2; nr_digits = !tm1651_init() ? 3 : 2;
if (nr_digits == 2) if (nr_digits == 2)
shiftreg_init(); shiftreg_init();

View file

@ -3495,6 +3495,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
/* Boundaries and Limits */ /* Boundaries and Limits */
fs->volbase = bsect; fs->volbase = bsect;
fs->volend = maxlba - 1;
fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);
fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);
if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */
@ -3564,6 +3565,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
/* Boundaries and Limits */ /* Boundaries and Limits */
fs->n_fatent = nclst + 2; /* Number of FAT entries */ fs->n_fatent = nclst + 2; /* Number of FAT entries */
fs->volbase = bsect; /* Volume start sector */ fs->volbase = bsect; /* Volume start sector */
fs->volend = bsect + tsect - 1; /* Volume end sector */
fs->fatbase = bsect + nrsv; /* FAT start sector */ fs->fatbase = bsect + nrsv; /* FAT start sector */
fs->database = bsect + sysect; /* Data start sector */ fs->database = bsect + sysect; /* Data start sector */
if (fmt == FS_FAT32) { if (fmt == FS_FAT32) {
@ -3746,6 +3748,8 @@ FRESULT f_open (
if (!fp) return FR_INVALID_OBJECT; if (!fp) return FR_INVALID_OBJECT;
dj.obj.attr = 0; /* FlashFloppy: avoid uninitialised assignment warn */
/* Get logical drive number */ /* Get logical drive number */
mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND; mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
res = mount_volume(&path, &fs, mode); res = mount_volume(&path, &fs, mode);

View file

@ -165,6 +165,7 @@ typedef struct {
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
DWORD fsize; /* Size of an FAT [sectors] */ DWORD fsize; /* Size of an FAT [sectors] */
LBA_t volbase; /* Volume base sector */ LBA_t volbase; /* Volume base sector */
LBA_t volend; /* Volume end sector */
LBA_t fatbase; /* FAT base sector */ LBA_t fatbase; /* FAT base sector */
LBA_t dirbase; /* Root directory base sector/cluster */ LBA_t dirbase; /* Root directory base sector/cluster */
LBA_t database; /* Data base sector */ LBA_t database; /* Data base sector */

512
src/file_cache.c Normal file
View file

@ -0,0 +1,512 @@
/*
* file_cache.c
*
* Caching I/O for a single file.
*
* Written & released by Keir Fraser <keir.xen@gmail.com> and Eric Anderson
* <ejona86@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 SECSZ 512
/*
Writes require the cache to have a memory buffer for I/O, since it is hard to
guarantee the image's buffer will remain around.
fs requires contiguous buffers for multi-sector reads/writes.
ADF wants to write 512 byte chunks without any reads. HFE wants to write 256
byte chunks and requires reads.
If a partial-sector write occurs, if the sector isn't already buffered, trigger
a read for the sector and reject the write.
How to handle read-ahead? Esp for HFE. Esp when track doesn't fit in memory.
- If I/O in progress, do nothing
- Write LRU if it is dirty
- Check following sectors to see if they are in the cache. updating LRU. If one
isn't in the cache, read it (evicting LRU).
- If there is a dirty sector (by scanning LRU), write it
*/
struct file_cache {
struct cache *cache;
FIL *fp;
FOP fop;
void (*fop_cb)(struct file_cache *);
FSIZE_t cur_sector;
FSIZE_t io_sector; /* Sector of 'fop' */
FSIZE_t readahead_start;
FSIZE_t readahead_end;
uint32_t dirty_key; /* Optimization to find dirty entries */
struct entry *dirty_val;
uint16_t dirty_entries; /* Number of dirty entries */
uint16_t entry_cnt;
uint16_t readahead_prio;
uint16_t readahead_sectors;
uint8_t io_max;
uint8_t subkey_bits;
uint8_t io_cnt;
bool_t sync_needed:1;
bool_t sync_requested:1;
bool_t writing:1;
bool_t readahead:1;
};
struct entry {
/* indexed by subkey */
uint8_t unread_bitfield;
uint8_t dirty_bitfield; /* Every dirty sector has unread=0. */
uint32_t _align[0];
uint8_t data[0][512];
};
static void enqueue_io(struct file_cache *fcache);
struct file_cache *file_cache_init(FIL *fp, uint8_t batch_secs,
void *start, void *end)
{
unsigned int entry_cnt;
struct file_cache *fcache = (void *)(((uint32_t)start + 3) & ~3);
start = (uint8_t *)fcache + sizeof(*fcache);
ASSERT(start < end);
memset(fcache, 0, sizeof(*fcache));
fcache->fp = fp;
ASSERT(batch_secs <= sizeof(((struct entry *)NULL)->unread_bitfield)*8);
ASSERT((batch_secs & (batch_secs-1)) == 0); /* Power of 2 */
for (uint8_t i = batch_secs; i > 1; i /= 2)
fcache->subkey_bits++;
fcache->cur_sector = UINT_MAX;
fcache->io_sector = UINT_MAX;
fcache->io_max = 0xff;
fcache->cache = cache_init(
start, end, sizeof(struct entry) + SECSZ*batch_secs, &entry_cnt);
/* We need at least one cache entry for the API to function. */
ASSERT(fcache->cache != NULL);
fcache->entry_cnt = entry_cnt;
return fcache;
}
static void init_entry(const struct file_cache *fcache, struct entry *entry)
{
entry->unread_bitfield = (1 << (1 << fcache->subkey_bits))-1;
entry->dirty_bitfield = 0;
}
static uint32_t calc_key(const struct file_cache *fcache, FSIZE_t ofs)
{
return (ofs/SECSZ)>>fcache->subkey_bits;
}
static uint32_t calc_subkey(const struct file_cache *fcache, FSIZE_t ofs)
{
return ofs/SECSZ & ((1<<fcache->subkey_bits)-1);
}
static void progress_io(struct file_cache *fcache)
{
thread_yield();
if (fcache->fop_cb != NULL && F_async_isdone(fcache->fop)) {
void (*fop_cb)(struct file_cache*) = fcache->fop_cb;
fcache->fop_cb = NULL;
fop_cb(fcache);
}
}
void file_cache_sync_wait(struct file_cache *fcache)
{
bool_t readahead = fcache->readahead;
ASSERT(!fcache->writing); /* Missing a flush? */
fcache->readahead = FALSE;
ASSERT(!fcache->sync_needed || fcache->sync_requested);
while (fcache->sync_needed) {
ASSERT(fcache->fop_cb != NULL);
F_async_wait(fcache->fop);
file_cache_progress(fcache);
}
fcache->readahead = readahead;
}
void file_cache_shutdown(struct file_cache *fcache)
{
if (fcache->fop_cb == NULL)
return;
F_async_wait(fcache->fop);
}
const void *file_cache_peek_read(struct file_cache *fcache, FSIZE_t ofs)
{
const struct entry *val;
uint32_t subkey = calc_subkey(fcache, ofs);
ASSERT(ofs % SECSZ == 0);
ASSERT(!fcache->writing); /* Missing a flush? */
fcache->cur_sector = ofs;
file_cache_progress(fcache);
val = cache_lookup(fcache->cache, calc_key(fcache, ofs));
if (!val || (val->unread_bitfield & 1<<subkey))
return NULL;
return val->data[subkey];
}
static void mark_dirty(
struct file_cache *fcache, struct entry *val, FSIZE_t ofs)
{
if (!fcache->dirty_val) {
fcache->dirty_key = calc_key(fcache, ofs);
fcache->dirty_val = val;
}
if (!val->dirty_bitfield) {
fcache->dirty_entries++;
fcache->sync_needed = TRUE;
}
val->dirty_bitfield |= 1<<calc_subkey(fcache, ofs);
}
static void flush(struct file_cache *fcache)
{
FSIZE_t ofs = fcache->cur_sector;
struct entry *val;
uint32_t subkey;
ASSERT(fcache->writing);
val = cache_lookup_mutable(fcache->cache, calc_key(fcache, ofs));
subkey = calc_subkey(fcache, ofs);
ASSERT(val && !(val->unread_bitfield & 1<<subkey));
mark_dirty(fcache, val, ofs);
fcache->writing = FALSE;
}
void *file_cache_peek_write(struct file_cache *fcache, FSIZE_t ofs)
{
struct entry *val;
uint32_t subkey;
ASSERT(ofs % SECSZ == 0);
if (fcache->writing && fcache->cur_sector != ofs)
flush(fcache);
fcache->writing = TRUE;
fcache->cur_sector = ofs;
file_cache_progress(fcache);
val = cache_lookup_mutable(fcache->cache, calc_key(fcache, ofs));
subkey = calc_subkey(fcache, ofs);
if (!val || (val->unread_bitfield & 1<<subkey))
return NULL;
return val->data[subkey];
}
void file_cache_progress(struct file_cache *fcache)
{
progress_io(fcache);
enqueue_io(fcache);
}
void file_cache_sync(struct file_cache *fcache)
{
if (fcache->writing)
flush(fcache);
if (fcache->sync_needed)
fcache->sync_requested = TRUE;
file_cache_progress(fcache);
}
bool_t file_cache_try_read(struct file_cache *fcache, void *buf, FSIZE_t ofs,
UINT btr)
{
const uint8_t *data;
ASSERT(ofs / SECSZ == (ofs+btr-1) / SECSZ);
data = file_cache_peek_read(fcache, ofs & ~(SECSZ-1));
if (!data)
return FALSE;
ofs %= SECSZ;
memcpy(buf, (uint8_t*)data + ofs, btr);
return TRUE;
}
bool_t file_cache_try_write(struct file_cache *fcache, const void *buf,
FSIZE_t ofs, UINT btw)
{
bool_t created;
const struct entry *val_lru;
struct entry *val;
uint32_t subkey, lru_id;
ASSERT(ofs / SECSZ == (ofs + btw - 1) / SECSZ);
if (btw != SECSZ) {
/* slow read+write path */
uint8_t *data = file_cache_peek_write(fcache, ofs & ~(SECSZ-1));
if (!data)
return FALSE;
ofs %= SECSZ;
memcpy((uint8_t*)data + ofs, buf, btw);
file_cache_sync(fcache);
return TRUE;
}
/* fast write-only path */
if (fcache->writing && fcache->cur_sector != ofs)
file_cache_sync(fcache);
progress_io(fcache);
val_lru = cache_lru_mutable(fcache->cache, &lru_id);
if (val_lru && (lru_id == calc_key(fcache, fcache->io_sector)
|| val_lru->dirty_bitfield)) {
enqueue_io(fcache);
return FALSE;
}
fcache->cur_sector = ofs;
val = cache_update_mutable(fcache->cache, calc_key(fcache, ofs), &created);
if (created)
init_entry(fcache, val);
subkey = calc_subkey(fcache, ofs);
if (fcache->io_sector <= ofs
&& ofs < fcache->io_sector + fcache->io_cnt*512
&& (val->unread_bitfield & 1<<subkey)) {
/* A read op is in progress for this sector. */
return FALSE;
}
val->unread_bitfield &= ~(1 << subkey);
mark_dirty(fcache, val, ofs);
ofs %= SECSZ;
memcpy(val->data[subkey] + ofs, buf, btw);
enqueue_io(fcache);
return TRUE;
}
void file_cache_read(struct file_cache *fcache, void *buf, FSIZE_t ofs,
UINT btr)
{
while (!file_cache_try_read(fcache, buf, ofs, btr))
F_async_wait(fcache->fop);
}
void file_cache_write(struct file_cache *fcache, const void *buf,
FSIZE_t ofs, UINT btw)
{
while (!file_cache_try_write(fcache, buf, ofs, btw))
F_async_wait(fcache->fop);
}
void file_cache_io_limit(struct file_cache *fcache, uint8_t io_max)
{
if (!io_max)
io_max = 0xff;
fcache->io_max = io_max;
}
void file_cache_readahead(
struct file_cache *fcache, FSIZE_t ofs, UINT btr, UINT prio)
{
int sectors, entries;
if (!btr) {
fcache->readahead = FALSE;
return;
}
fcache->readahead = TRUE;
fcache->readahead_start = ofs & ~(SECSZ-1);
fcache->readahead_end = (ofs+btr+SECSZ-1) & ~(SECSZ-1);
sectors = (fcache->readahead_end - fcache->readahead_start)/SECSZ;
entries = calc_key(fcache, ofs+btr-1) - calc_key(fcache, ofs) + 1;
if (entries > fcache->entry_cnt) {
/* Region does not fit in memory. Need to guarantee that readahead will
* not load enough entries to drop cur_sector. */
ASSERT(fcache->entry_cnt >= 3);
/* Maximum number of sectors that can be read when the region ends are
* in the readahead window. */
sectors -= (entries - fcache->entry_cnt) << fcache->subkey_bits;
/* Maximum number of sectors that can be read if cur_sector is at the
* last sector of an entry and the region ends are in the readahead
* window. */
sectors -= (1 << fcache->subkey_bits) - 1;
ASSERT(sectors >= 3);
}
sectors = min_t(int, sectors, 50); /* Avoid large linear scan. */
prio = (prio+SECSZ-1)/SECSZ;
prio = min_t(uint32_t, prio, sectors-1);
fcache->readahead_prio = prio;
fcache->readahead_sectors = sectors-1 - prio;
}
/*
* I/O Scheduler
*/
static void register_fop_whendone(
struct file_cache *fcache, FOP fop, void (*cb)(struct file_cache *))
{
ASSERT(fcache->fop_cb == NULL);
fcache->fop = fop;
fcache->fop_cb = cb;
thread_yield(); /* Give fop a chance to start. */
}
static void sync_complete(struct file_cache *fcache)
{
if (!fcache->dirty_entries) {
fcache->sync_needed = FALSE;
fcache->sync_requested = FALSE;
}
}
static void write_complete(struct file_cache *fcache)
{
fcache->io_sector = UINT_MAX;
fcache->io_cnt = 0;
}
/* Write processing should avoid changing the cache LRU. */
static void write_start(
struct file_cache *fcache, uint32_t key, struct entry *val)
{
uint32_t subkey = 0;
FSIZE_t sector;
FOP fop;
ASSERT(fcache->dirty_entries);
ASSERT(val->dirty_bitfield);
for (uint8_t d = val->dirty_bitfield; !(d & 1); d >>= 1, subkey++)
;
sector = ((key << fcache->subkey_bits) + subkey) * SECSZ;
for (fcache->io_cnt = 0;
fcache->io_cnt < fcache->io_max;
fcache->io_cnt++) {
if (!(val->dirty_bitfield & (1 << (subkey + fcache->io_cnt))))
break;
/* Clear eagerly to track new writes during the I/O. */
val->dirty_bitfield &= ~(1 << (subkey + fcache->io_cnt));
}
if (!val->dirty_bitfield) {
fcache->dirty_entries--;
/* Update dirty_key */
if (fcache->dirty_entries) {
uint32_t lru_id;
struct entry *lru_val = val;
/* Try to find a dirty entry by iterating through entries newer
* than the current write. */
while (lru_val && !lru_val->dirty_bitfield)
lru_val = cache_lru_next_mutable(fcache->cache, lru_val, &lru_id);
/* If that doesn't find an entry, iterate through all entries. */
if (!lru_val)
lru_val = cache_lru_search_mutable(fcache->cache, &lru_id);
while (lru_val && !lru_val->dirty_bitfield)
lru_val = cache_lru_next_mutable(fcache->cache, lru_val, &lru_id);
ASSERT(lru_val);
fcache->dirty_key = lru_id;
fcache->dirty_val = lru_val;
} else {
fcache->dirty_key = 0;
fcache->dirty_val = NULL;
}
}
F_lseek_async(fcache->fp, sector);
fop = F_write_async(fcache->fp, val->data[subkey],
fcache->io_cnt*SECSZ, NULL);
register_fop_whendone(fcache, fop, write_complete);
}
static void read_complete(struct file_cache *fcache)
{
uint32_t key;
uint32_t subkey;
struct entry *val;
key = calc_key(fcache, fcache->io_sector);
subkey = calc_subkey(fcache, fcache->io_sector);
val = cache_lookup_mutable(fcache->cache, key);
for (int i = 0; i < fcache->io_cnt; i++)
val->unread_bitfield &= ~(1 << (subkey + i));
fcache->io_sector = UINT_MAX;
fcache->io_cnt = 0;
}
static bool_t read_start(struct file_cache *fcache, FSIZE_t sector)
{
uint32_t lru_id;
struct entry *val;
bool_t created;
uint32_t key, subkey;
FOP fop;
/* Write LRU if it is dirty, to avoid it being lost. */
val = cache_lru_mutable(fcache->cache, &lru_id);
if (val && val->dirty_bitfield) {
write_start(fcache, lru_id, val);
return TRUE;
}
key = calc_key(fcache, sector);
subkey = calc_subkey(fcache, sector);
val = cache_update_mutable(fcache->cache, key, &created);
if (created)
init_entry(fcache, val);
if (!(val->unread_bitfield & (1<<subkey)))
return FALSE;
fcache->io_sector = sector;
for (fcache->io_cnt = 1;
fcache->io_cnt < fcache->io_max;
fcache->io_cnt++) {
if (!(val->unread_bitfield & (1 << (subkey + fcache->io_cnt))))
break;
}
F_lseek_async(fcache->fp, sector);
fop = F_read_async(fcache->fp, val->data[subkey],
fcache->io_cnt*SECSZ, NULL);
register_fop_whendone(fcache, fop, read_complete);
return TRUE;
}
static bool_t enqueue_readahead(
struct file_cache *fcache, FSIZE_t *pread_sector, uint16_t limit)
{
FSIZE_t read_sector = *pread_sector;
if (!fcache->readahead
|| fcache->readahead_start > read_sector
|| read_sector >= fcache->readahead_end)
return FALSE;
for (int i = 0; i < limit; i++) {
read_sector += SECSZ;
if (read_sector >= fcache->readahead_end)
read_sector = fcache->readahead_start;
if (read_start(fcache, read_sector))
return TRUE;
}
*pread_sector = read_sector;
return FALSE;
}
static void enqueue_io(struct file_cache *fcache)
{
FSIZE_t read_sector = fcache->cur_sector;
if (fcache->fop_cb != NULL)
return;
/* Read high-priority sectors. */
if (read_sector != UINT_MAX) {
if (read_start(fcache, read_sector))
return;
if (enqueue_readahead(fcache, &read_sector,
fcache->readahead_prio))
return;
}
/* Write. */
if (fcache->dirty_entries) {
write_start(fcache, fcache->dirty_key, fcache->dirty_val);
return;
}
if (fcache->sync_requested) {
fcache->sync_requested = FALSE;
register_fop_whendone(fcache, F_sync_async(fcache->fp), sync_complete);
return;
}
/* Read low-priority sectors. */
if (read_sector != UINT_MAX) {
if (enqueue_readahead(fcache, &read_sector, fcache->readahead_sectors))
return;
}
}

View file

@ -29,7 +29,11 @@ union cfg_slot {
uint16_t words[SLOTW_NR]; uint16_t words[SLOTW_NR];
}; };
#if MCU == STM32F105
#define SLOT_BASE (union cfg_slot *)(0x8020000 - FLASH_PAGE_SIZE) #define SLOT_BASE (union cfg_slot *)(0x8020000 - FLASH_PAGE_SIZE)
#elif MCU == AT32F435
#define SLOT_BASE (union cfg_slot *)(0x8040000 - FLASH_PAGE_SIZE)
#endif
#define SLOT_NR (FLASH_PAGE_SIZE / sizeof(union cfg_slot)) #define SLOT_NR (FLASH_PAGE_SIZE / sizeof(union cfg_slot))
#define slot_is_blank(_slot) ((_slot)->words[0] == 0xffff) #define slot_is_blank(_slot) ((_slot)->words[0] == 0xffff)
@ -87,6 +91,8 @@ void flash_ff_cfg_update(void *scratch)
} else { } else {
/* No blank slots available. Erase whole page. */ /* No blank slots available. Erase whole page. */
fpec_page_erase((uint32_t)SLOT_BASE); fpec_page_erase((uint32_t)SLOT_BASE);
if (flash_page_size < FLASH_PAGE_SIZE)
fpec_page_erase((uint32_t)SLOT_BASE + flash_page_size);
slot = SLOT_BASE; slot = SLOT_BASE;
printk("Config: Erased Whole Page\n"); printk("Config: Erased Whole Page\n");
} }

View file

@ -11,7 +11,7 @@
#define GPI_bus GPI_floating #define GPI_bus GPI_floating
#define GPO_bus GPO_pushpull(_2MHz,O_FALSE) #define GPO_bus GPO_pushpull(_2MHz,O_FALSE)
#define AFO_bus (AFO_pushpull(_2MHz) | (O_FALSE<<4)) #define AFO_bus _AFO_pushpull(_2MHz,O_FALSE)
#define GPO_rdata GPO_bus #define GPO_rdata GPO_bus
#define AFO_rdata AFO_bus #define AFO_rdata AFO_bus
@ -20,12 +20,14 @@
static void chgrst_timer(void *_drv); static void chgrst_timer(void *_drv);
static void drive_step_timer(void *_drv); static void drive_step_timer(void *_drv);
static void motor_spinup_timer(void *_drv); static void motor_spinup_timer(void *_drv);
void IRQ_43(void) __attribute__((alias("IRQ_soft")));
#define FLOPPY_SOFTIRQ 43 #define FLOPPY_SOFTIRQ SOFTIRQ_0
DEFINE_IRQ(FLOPPY_SOFTIRQ, "IRQ_soft");
/* Index-pulse timer functions. */ /* Index-pulse timer functions. */
static void index_assert(void *); /* index.timer */ static void index_assert(void *); /* index.timer */
static void index_deassert(void *); /* index.timer_deassert */ static void index_deassert(void *); /* index.timer_deassert */
static void index_custom_assert(void *); /* index.custom_timer */
static time_t sync_time, sync_pos; static time_t sync_time, sync_pos;
@ -33,6 +35,8 @@ static time_t prefetch_start_time;
static uint32_t max_prefetch_us; static uint32_t max_prefetch_us;
struct drive; struct drive;
static always_inline void drive_change_pin(
struct drive *drv, uint8_t pin, bool_t assert);
static always_inline void drive_change_output( static always_inline void drive_change_output(
struct drive *drv, uint8_t outp, bool_t assert); struct drive *drv, uint8_t outp, bool_t assert);
@ -68,7 +72,7 @@ const static struct fintf {
static always_inline void drive_change_pin( static always_inline void drive_change_pin(
struct drive *drv, uint8_t pin, bool_t assert) struct drive *drv, uint8_t pin, bool_t assert)
{ {
uint16_t pin_mask = m(pin); uint32_t pin_mask = m(pin);
/* Logically assert or deassert the pin. */ /* Logically assert or deassert the pin. */
if (assert) if (assert)
@ -77,8 +81,10 @@ static always_inline void drive_change_pin(
gpio_out_active &= ~pin_mask; gpio_out_active &= ~pin_mask;
/* Update the physical output pin, if the drive is selected. */ /* Update the physical output pin, if the drive is selected. */
if (drv->sel) if (drv->sel) {
gpio_write_pins(gpio_out, pin_mask, assert ? O_TRUE : O_FALSE); gpio_write_pins(gpiob, (uint16_t)pin_mask, assert ? O_TRUE : O_FALSE);
gpio_write_pins(gpioa, pin_mask>>16, assert ? O_TRUE : O_FALSE);
}
/* Caller expects us to re-enable interrupts. */ /* Caller expects us to re-enable interrupts. */
IRQ_global_enable(); IRQ_global_enable();
@ -127,13 +133,20 @@ static void drive_change_output(
static void update_amiga_id(struct drive *drv, bool_t amiga_hd_id) static void update_amiga_id(struct drive *drv, bool_t amiga_hd_id)
{ {
/* Only for the Amiga interface, with hacked RDY (pin 34) signal. */ /* JC and pin 34 are overridden only for the Amiga interface. */
if (fintf_mode != FINTF_AMIGA) if (fintf_mode != FINTF_AMIGA) {
drv->amiga_pin34 = FALSE;
board_jc_set_mode(GPI_pull_up);
return; return;
}
/* JC and HDEN are set according to Amiga density. */
board_jc_set_mode(GPO_opendrain(_2MHz, amiga_hd_id));
drive_change_output(drv, outp_hden, amiga_hd_id); drive_change_output(drv, outp_hden, amiga_hd_id);
if (pin34 != outp_unused) /* If pin 34 is explicitly configured, we do not mess with it. */
drv->amiga_pin34 = (pin34 == outp_unused);
if (!drv->amiga_pin34)
return; return;
IRQ_global_disable(); IRQ_global_disable();
@ -142,17 +155,20 @@ static void update_amiga_id(struct drive *drv, bool_t amiga_hd_id)
* every time the drive is selected. */ * every time the drive is selected. */
update_SELA_irq(amiga_hd_id); update_SELA_irq(amiga_hd_id);
/* DD-ID: !!HACK!! We permanently assert pin 34, even when no disk is if (ff_cfg.motor_delay == MOTOR_ignore) {
* inserted. Properly we should only do this when MTR is asserted. */ /* Best-effort pin 34 handling:
/* HD ID: !!HACK!! Without knowledge of MTR signal we cannot synchronise * DD-ID: We permanently assert pin 34, even when no disk is inserted.
* the HD-ID sequence 101010... with the host poll loop. It turns out that * Properly we should only do this when MTR is asserted.
* starting with pin 34 asserted when the HD image is mounted seems to * HD-ID: Without knowledge of MTR signal we cannot synchronise the
* generally work! */ * HD-ID sequence 101010... with the host poll loop. It turns out that
gpio_out_active |= m(pin_34); * starting with pin 34 asserted when the HD image is mounted seems to
if (drive.sel) * generally work! (But see GitHub issue #354) */
gpio_write_pins(gpio_out, m(pin_34), O_TRUE); drive_change_pin(&drive, pin_34, TRUE);
} else {
IRQ_global_enable(); /* Do nothing here. Pin 34 will be updated by IRQ_MOTOR() via
* motor_chgrst_update_status(). */
IRQ_global_enable();
}
} }
void floppy_cancel(void) void floppy_cancel(void)
@ -180,6 +196,7 @@ void floppy_cancel(void)
/* Clear soft state. */ /* Clear soft state. */
timer_cancel(&drv->chgrst_timer); timer_cancel(&drv->chgrst_timer);
timer_cancel(&index.timer); timer_cancel(&index.timer);
timer_cancel(&index.custom_timer);
barrier(); /* cancel index.timer /then/ clear dma rings */ barrier(); /* cancel index.timer /then/ clear dma rings */
dma_rd = dma_wr = NULL; dma_rd = dma_wr = NULL;
barrier(); /* /then/ clear soft state */ barrier(); /* /then/ clear soft state */
@ -189,12 +206,22 @@ void floppy_cancel(void)
index.fake_fired = FALSE; index.fake_fired = FALSE;
barrier(); /* /then/ cancel index.timer_deassert */ barrier(); /* /then/ cancel index.timer_deassert */
timer_cancel(&index.timer_deassert); timer_cancel(&index.timer_deassert);
motor_chgrst_eject(drv); motor_chgrst_update_status(drv);
/* Set outputs for empty drive. */ /* Set outputs for empty drive. */
barrier(); barrier();
drive_change_output(drv, outp_index, FALSE); drive_change_output(drv, outp_index, FALSE);
drive_change_output(drv, outp_dskchg, TRUE); drive_change_output(drv, outp_dskchg, TRUE);
/* Clean up I/O. This must avoid potential cancel_call()s while still
* getting volume communication into a consistent state. */
F_async_cancel_all();
/* cancel_call() circumvents the threading subsystem and may leave it in an
* incoherent state. Volume operations never cancel so it is safe to
* thread_yield() if a volume operation is in progress. */
while (volume_interrupt())
thread_yield();
thread_reset();
} }
void floppy_set_fintf_mode(void) void floppy_set_fintf_mode(void)
@ -221,7 +248,7 @@ void floppy_set_fintf_mode(void)
/* Jumper JC selects default floppy interface configuration: /* Jumper JC selects default floppy interface configuration:
* - No Jumper: Shugart * - No Jumper: Shugart
* - Jumpered: IBM PC */ * - Jumpered: IBM PC */
mode = gpio_read_pin(gpiob, 1) ? FINTF_SHUGART : FINTF_IBMPC; mode = board_jc_strapped() ? FINTF_IBMPC : FINTF_SHUGART;
} }
ASSERT(mode < ARRAY_SIZE(fintfs)); ASSERT(mode < ARRAY_SIZE(fintfs));
@ -247,8 +274,12 @@ void floppy_set_fintf_mode(void)
update_SELA_irq(FALSE); update_SELA_irq(FALSE);
if (drv->sel) { if (drv->sel) {
gpio_write_pins(gpio_out, old_active & ~gpio_out_active, O_FALSE); uint32_t r = old_active & ~gpio_out_active;
gpio_write_pins(gpio_out, ~old_active & gpio_out_active, O_TRUE); uint32_t s = ~old_active & gpio_out_active;
gpio_write_pins(gpioa, r>>16, O_FALSE);
gpio_write_pins(gpioa, s>>16, O_TRUE);
gpio_write_pins(gpiob, (uint16_t)r, O_FALSE);
gpio_write_pins(gpiob, (uint16_t)s, O_TRUE);
} }
IRQ_global_enable(); IRQ_global_enable();
@ -262,6 +293,24 @@ void floppy_set_fintf_mode(void)
pin34_inverted ? "not-" : "", outp_name[pin34] ?: "?"); pin34_inverted ? "not-" : "", outp_name[pin34] ?: "?");
} }
void floppy_set_max_cyl(void)
{
struct drive *drv = &drive;
IRQ_global_disable();
if (drv->cyl > ff_cfg.max_cyl)
drv->cyl = ff_cfg.max_cyl;
IRQ_global_enable();
}
static void drive_configure_output_pin(unsigned int pin)
{
if (pin >= 16) {
gpio_configure_pin(gpioa, pin-16, GPO_bus);
} else {
gpio_configure_pin(gpiob, pin, GPO_bus);
}
}
void floppy_init(void) void floppy_init(void)
{ {
struct drive *drv = &drive; struct drive *drv = &drive;
@ -274,14 +323,11 @@ void floppy_init(void)
timer_init(&drv->motor.timer, motor_spinup_timer, drv); timer_init(&drv->motor.timer, motor_spinup_timer, drv);
timer_init(&drv->chgrst_timer, chgrst_timer, drv); timer_init(&drv->chgrst_timer, chgrst_timer, drv);
gpio_configure_pin(gpio_out, pin_02, GPO_bus); drive_configure_output_pin(pin_02);
gpio_configure_pin(gpio_out, pin_08, GPO_bus); drive_configure_output_pin(pin_08);
gpio_configure_pin(gpio_out, pin_26, GPO_bus); drive_configure_output_pin(pin_26);
gpio_configure_pin(gpio_out, pin_28, GPO_bus); drive_configure_output_pin(pin_28);
gpio_configure_pin(gpio_out, pin_34, GPO_bus); drive_configure_output_pin(pin_34);
gpio_configure_pin(gpio_data, pin_wdata, GPI_bus);
gpio_configure_pin(gpio_data, pin_rdata, GPO_bus);
drive_change_output(drv, outp_dskchg, TRUE); drive_change_output(drv, outp_dskchg, TRUE);
drive_change_output(drv, outp_wrprot, TRUE); drive_change_output(drv, outp_wrprot, TRUE);
@ -294,8 +340,16 @@ void floppy_init(void)
timer_init(&index.timer, index_assert, NULL); timer_init(&index.timer, index_assert, NULL);
timer_init(&index.timer_deassert, index_deassert, NULL); timer_init(&index.timer_deassert, index_deassert, NULL);
timer_init(&index.custom_timer, index_custom_assert, NULL);
motor_chgrst_eject(drv); motor_chgrst_setup_exti();
}
static void io_thread_main(void *arg) {
while (1) {
F_async_drain();
thread_yield();
}
} }
void floppy_insert(unsigned int unit, struct slot *slot) void floppy_insert(unsigned int unit, struct slot *slot)
@ -309,10 +363,11 @@ void floppy_insert(unsigned int unit, struct slot *slot)
floppy_mount(slot); floppy_mount(slot);
im = image; im = image;
if (im->write_bc_ticks < sysclk_ns(1500)) if (im->write_bc_ticks < sampleclk_ns(1500))
drive_change_output(drv, outp_hden, TRUE); drive_change_output(drv, outp_hden, TRUE);
timer_dma_init(); timer_dma_init();
thread_start(&drv->io_thread, _thread1_stacktop, io_thread_main, NULL);
/* Drive is ready. Set output signals appropriately. */ /* Drive is ready. Set output signals appropriately. */
update_amiga_id(drv, im->stk_per_rev > stk_ms(300)); update_amiga_id(drv, im->stk_per_rev > stk_ms(300));
@ -320,7 +375,7 @@ void floppy_insert(unsigned int unit, struct slot *slot)
drive_change_output(drv, outp_wrprot, FALSE); drive_change_output(drv, outp_wrprot, FALSE);
barrier(); barrier();
drv->inserted = TRUE; drv->inserted = TRUE;
motor_chgrst_insert(drv); /* update RDY + motor state */ motor_chgrst_update_status(drv); /* update RDY + motor state */
if (ff_cfg.chgrst <= CHGRST_delay(15)) if (ff_cfg.chgrst <= CHGRST_delay(15))
timer_set(&drv->chgrst_timer, time_now() + ff_cfg.chgrst*time_ms(500)); timer_set(&drv->chgrst_timer, time_now() + ff_cfg.chgrst*time_ms(500));
} }
@ -358,23 +413,21 @@ static void floppy_sync_flux(void)
if (!drv->index_suppressed) { if (!drv->index_suppressed) {
ticks = time_diff(time_now(), sync_time) - time_us(1); ticks = time_diff(time_now(), sync_time) - time_us(1);
if (ticks > time_ms(15)) { if (ticks > time_ms(5)) {
/* Too long to wait. Immediately re-sync index timing. */
drv->index_suppressed = TRUE;
printk("Trk %u: skip %ums\n",
drv->image->cur_track, (ticks+time_us(500))/time_ms(1));
} else if (ticks > time_ms(5)) {
/* A while to wait. Go do other work. */ /* A while to wait. Go do other work. */
return; return;
} else { } else {
if (ticks > 0) if (ticks > 0)
delay_ticks(ticks); delay_ticks(ticks);
/* If we're out of sync then forcibly re-sync index timing. */ /* If we're out of sync then start over. */
ticks = time_diff(time_now(), sync_time); ticks = time_diff(time_now(), sync_time);
if (ticks < -100) { if (ticks < -100) {
drv->index_suppressed = TRUE;
printk("Trk %u: late %uus\n", printk("Trk %u: late %uus\n",
drv->image->cur_track, -ticks/time_us(1)); drv->image->cur_track, -ticks/time_us(1));
dma_rd->state = DMA_inactive;
dma_rd->prod = dma_rd->cons;
return;
} }
} }
} else if (drv->step.state) { } else if (drv->step.state) {
@ -396,11 +449,11 @@ static void floppy_sync_flux(void)
uint32_t oldpri = IRQ_save(TIMER_IRQ_PRI); uint32_t oldpri = IRQ_save(TIMER_IRQ_PRI);
timer_cancel(&index.timer); timer_cancel(&index.timer);
timer_cancel(&index.custom_timer);
/* If we crossed the index mark while filling the DMA buffer then we /* If we crossed the index mark while filling the DMA buffer then we
* need to set up the index pulse (usually done by IRQ_rdata_dma). */ * need to set up the index pulse (usually done by IRQ_rdata_dma). */
if (image_ticks_since_index(drv->image) if (image_ticks_since_index(drv->image) < sampleclk_stk(sync_pos)) {
< (sync_pos*(SYSCLK_MHZ/STK_MHZ))) {
/* Sum all flux timings in the DMA buffer. */ /* Sum all flux timings in the DMA buffer. */
const uint16_t buf_mask = ARRAY_SIZE(dma_rd->buf) - 1; const uint16_t buf_mask = ARRAY_SIZE(dma_rd->buf) - 1;
@ -412,10 +465,21 @@ static void floppy_sync_flux(void)
ticks -= image_ticks_since_index(drv->image); ticks -= image_ticks_since_index(drv->image);
/* Calculate deadline for index timer. */ /* Calculate deadline for index timer. */
ticks /= SYSCLK_MHZ/TIME_MHZ; ticks /= SAMPLECLK_MHZ/TIME_MHZ;
timer_set(&index.timer, time_now() + ticks); timer_set(&index.timer, time_now() + ticks);
} }
/* Realign custom_timer to index. */
if (index.custom_pulses_len) {
int i;
for (i = 0; i < index.custom_pulses_len; i++)
if (sync_pos < index.custom_pulses[i])
break;
if (i < index.custom_pulses_len)
timer_set(&index.custom_timer,
time_now() - sync_pos + index.custom_pulses[i]);
}
IRQ_global_disable(); IRQ_global_disable();
IRQ_restore(oldpri); IRQ_restore(oldpri);
index.prev_time = time_now() - sync_pos; index.prev_time = time_now() - sync_pos;
@ -433,6 +497,7 @@ static bool_t dma_rd_handle(struct drive *drv)
struct image *im = drv->image; struct image *im = drv->image;
time_t index_time, read_start_pos; time_t index_time, read_start_pos;
unsigned int track; unsigned int track;
time_t start_time = time_now();
/* Allow 10ms from current rotational position to load new track */ /* Allow 10ms from current rotational position to load new track */
int32_t delay = time_ms(10); int32_t delay = time_ms(10);
/* Allow extra time if heads are settling. */ /* Allow extra time if heads are settling. */
@ -454,22 +519,29 @@ static bool_t dma_rd_handle(struct drive *drv)
read_start_pos %= im->stk_per_rev; read_start_pos %= im->stk_per_rev;
/* Seek to the new track. */ /* Seek to the new track. */
track = drive_calc_track(drv); track = drive_calc_track(drv);
read_start_pos *= SYSCLK_MHZ/STK_MHZ; if (in_da_mode(im, track>>1) != image_in_da_mode(im)) {
if (in_da_mode(im, track>>1) && (drv->outp & m(outp_wrprot)) /* Changing D-A mode requires changing image handler and may need
&& !volume_readonly()) { * to re-read the config file since D-A can change it. */
/* Remove write-protect when driven into D-A mode. */
drive_change_output(drv, outp_wrprot, FALSE);
}
if (image_setup_track(im, track, &read_start_pos))
return TRUE; return TRUE;
}
read_start_pos *= SAMPLECLK_MHZ/STK_MHZ;
image_setup_track(im, track, &read_start_pos);
prefetch_start_time = time_now(); prefetch_start_time = time_now();
read_start_pos /= SYSCLK_MHZ/STK_MHZ; read_start_pos /= SAMPLECLK_MHZ/STK_MHZ;
sync_pos = read_start_pos; sync_pos = read_start_pos;
if (!drv->index_suppressed) { if (!drv->index_suppressed) {
/* Set the deadline to match existing index timing. */ /* Set the deadline to match existing index timing. */
time_t now = time_now();
sync_time = index_time + read_start_pos; sync_time = index_time + read_start_pos;
if (time_diff(time_now(), sync_time) < 0) if (time_diff(now, sync_time) < 0)
sync_time += im->stk_per_rev; sync_time += im->stk_per_rev;
if (time_diff(now, sync_time) < 0
|| time_diff(now, sync_time) > delay) {
/* image_setup_track() consumed the entire delay. Try again. */
time_t ticks = time_since(start_time) - delay;
printk("Trk %u: trk late %uus\n", track, ticks/time_us(1));
break;
}
} }
/* Change state /then/ check for race against step or side change. */ /* Change state /then/ check for race against step or side change. */
dma_rd->state = DMA_starting; dma_rd->state = DMA_starting;
@ -545,10 +617,13 @@ static void index_assert(void *dat)
{ {
struct drive *drv = &drive; struct drive *drv = &drive;
index.prev_time = index.timer.deadline; index.prev_time = index.timer.deadline;
if (drv->motor.on && !index_is_suppressed(drv)) { if (index.custom_pulses_len)
drive_change_output(drv, outp_index, TRUE); timer_set(&index.custom_timer, index.prev_time + index.custom_pulses[0]);
timer_set(&index.timer_deassert, index.prev_time + time_ms(2)); else
} if (drv->motor.on && !index_is_suppressed(drv)) {
drive_change_output(drv, outp_index, TRUE);
timer_set(&index.timer_deassert, index.prev_time + time_ms(2));
}
if (dma_rd->state != DMA_active) /* timer set from input flux stream */ if (dma_rd->state != DMA_active) /* timer set from input flux stream */
timer_set(&index.timer, index.prev_time + drv->image->stk_per_rev); timer_set(&index.timer, index.prev_time + drv->image->stk_per_rev);
} }
@ -559,6 +634,24 @@ static void index_deassert(void *dat)
drive_change_output(drv, outp_index, FALSE); drive_change_output(drv, outp_index, FALSE);
} }
static void index_custom_assert(void *dat)
{
struct drive *drv = &drive;
time_t current_pulse_pos;
int i;
if (drv->motor.on && !index_is_suppressed(drv)) {
drive_change_output(drv, outp_index, TRUE);
timer_set(&index.timer_deassert,
index.custom_timer.deadline + time_ms(2));
}
current_pulse_pos = time_since(index.prev_time);
for (i = 0; i < index.custom_pulses_len; i++)
if (current_pulse_pos < index.custom_pulses[i])
break;
if (i < index.custom_pulses_len)
timer_set(&index.custom_timer, index.prev_time + index.custom_pulses[i]);
}
static void chgrst_timer(void *_drv) static void chgrst_timer(void *_drv)
{ {
struct drive *drv = _drv; struct drive *drv = _drv;
@ -596,6 +689,10 @@ static void motor_spinup_timer(void *_drv)
struct drive *drv = _drv; struct drive *drv = _drv;
drv->motor.on = TRUE; drv->motor.on = TRUE;
if (drv->amiga_pin34) {
IRQ_global_disable();
drive_change_pin(drv, pin_34, TRUE);
}
drive_change_output(drv, outp_rdy, TRUE); drive_change_output(drv, outp_rdy, TRUE);
} }

View file

@ -10,8 +10,6 @@
* See the file COPYING for more details, or visit <http://unlicense.org>. * See the file COPYING for more details, or visit <http://unlicense.org>.
*/ */
#define m(bitnr) (1u<<(bitnr))
/* A DMA buffer for running a timer associated with a floppy-data I/O pin. */ /* A DMA buffer for running a timer associated with a floppy-data I/O pin. */
struct dma_ring { struct dma_ring {
/* Current state of DMA (RDATA): /* Current state of DMA (RDATA):
@ -53,6 +51,7 @@ static struct drive {
bool_t writing; bool_t writing;
bool_t sel; bool_t sel;
bool_t index_suppressed; /* disable IDX while writing to USB stick */ bool_t index_suppressed; /* disable IDX while writing to USB stick */
bool_t amiga_pin34;
uint8_t outp; uint8_t outp;
volatile bool_t inserted; volatile bool_t inserted;
struct timer chgrst_timer; struct timer chgrst_timer;
@ -73,16 +72,23 @@ static struct drive {
} step; } step;
uint32_t restart_pos; uint32_t restart_pos;
struct image *image; struct image *image;
struct thread io_thread;
} drive; } drive;
static struct image *image; static struct image *image;
static struct { static struct {
struct timer timer, timer_deassert; struct timer timer, timer_deassert;
struct timer custom_timer;
time_t prev_time; time_t prev_time;
bool_t fake_fired; bool_t fake_fired;
uint8_t custom_pulses_ver;
uint8_t custom_pulses_len;
/* Durations relative to track start. Must be in increasing order. */
time_t custom_pulses[MAX_CUSTOM_PULSES];
} index; } index;
static unsigned int drive_calc_track(struct drive *drv);
static void rdata_stop(void); static void rdata_stop(void);
static void wdata_start(void); static void wdata_start(void);
static void wdata_stop(void); static void wdata_stop(void);
@ -94,6 +100,7 @@ struct exti_irq {
#if defined(QUICKDISK) #if defined(QUICKDISK)
#include "gotek/quickdisk.c" #include "gotek/quickdisk.c"
#define dma_rd_set_active(x) ((void)(x))
#else #else
#include "gotek/floppy.c" #include "gotek/floppy.c"
#endif #endif
@ -182,8 +189,9 @@ static void floppy_mount(struct slot *slot)
/* ~0 avoids sync match within fewer than 32 bits of scan start. */ /* ~0 avoids sync match within fewer than 32 bits of scan start. */
im->write_bc_window = ~0; im->write_bc_window = ~0;
/* Large buffer to absorb write latencies at mass-storage layer. */ /* Size at least 4kb so a 512 byte sector (1k bc) can be fully
im->bufs.write_bc.len = 32*1024; /* 32kB, power of two. */ * encoded into the 2kb read_bc, with space to spare. */
im->bufs.write_bc.len = 4*1024; /* Power of two. */
im->bufs.write_bc.p = arena_alloc(im->bufs.write_bc.len); im->bufs.write_bc.p = arena_alloc(im->bufs.write_bc.len);
/* Read BC buffer overlaps the second half of the write BC buffer. This /* Read BC buffer overlaps the second half of the write BC buffer. This
@ -212,7 +220,24 @@ static void floppy_mount(struct slot *slot)
ASSERT(im->bufs.read_data.len >= 10*1024); ASSERT(im->bufs.read_data.len >= 10*1024);
/* Mount the image file. */ /* Mount the image file. */
image_open(im, slot, cltbl); #if defined(QUICKDISK)
image_open(im, slot, cltbl, FALSE);
#else
{
uint8_t cyl = drive_calc_track(drv)>>1;
/* Skip useless loading if D-A guaranteed. */
if (cyl != 255)
image_open(im, slot, cltbl, FALSE);
/* Now that the number of image cylinders is known, do a precise D-A
* check. */
if (in_da_mode(im, cyl)) {
volume_cache_destroy(); /* Clean up after other image format. */
image_open(im, slot, cltbl, TRUE);
/* Remove write-protect when driven into D-A mode. */
drive_change_output(drv, outp_wrprot, FALSE);
}
}
#endif
if (!im->disk_handler->write_track || volume_readonly()) if (!im->disk_handler->write_track || volume_readonly())
slot->attributes |= AM_RDO; slot->attributes |= AM_RDO;
if (slot->attributes & AM_RDO) { if (slot->attributes & AM_RDO) {
@ -238,6 +263,8 @@ static void floppy_mount(struct slot *slot)
drv->index_suppressed = FALSE; drv->index_suppressed = FALSE;
index.prev_time = time_now(); index.prev_time = time_now();
index.custom_pulses_len = 0;
index.custom_pulses_ver = 0xFF;
} }
/* Initialise timers and DMA for RDATA/WDATA. */ /* Initialise timers and DMA for RDATA/WDATA. */
@ -251,17 +278,17 @@ static void timer_dma_init(void)
IRQx_enable(dma_wdata_irq); IRQx_enable(dma_wdata_irq);
/* RDATA Timer setup: /* RDATA Timer setup:
* The counter is incremented at full SYSCLK rate. * The counter is incremented at SAMPLECLK rate.
* *
* Ch.2 (RDATA) is in PWM mode 1. It outputs O_TRUE for 400ns and then * Ch.2 (RDATA) is in PWM mode 1. It outputs O_TRUE for 400ns and then
* O_FALSE until the counter reloads. By changing the ARR via DMA we alter * O_FALSE until the counter reloads. By changing the ARR via DMA we alter
* the time between (fixed-width) O_TRUE pulses, mimicking floppy drive * the time between (fixed-width) O_TRUE pulses, mimicking floppy drive
* timings. */ * timings. */
tim_rdata->psc = 0; tim_rdata->psc = (SYSCLK_MHZ/SAMPLECLK_MHZ) - 1;
tim_rdata->ccmr1 = (TIM_CCMR1_CC2S(TIM_CCS_OUTPUT) | tim_rdata->ccmr1 = (TIM_CCMR1_CC2S(TIM_CCS_OUTPUT) |
TIM_CCMR1_OC2M(TIM_OCM_PWM1)); TIM_CCMR1_OC2M(TIM_OCM_PWM1));
tim_rdata->ccer = TIM_CCER_CC2E | ((O_TRUE==0) ? TIM_CCER_CC2P : 0); tim_rdata->ccer = TIM_CCER_CC2E | ((O_TRUE==0) ? TIM_CCER_CC2P : 0);
tim_rdata->ccr2 = sysclk_ns(400); tim_rdata->ccr2 = sampleclk_ns(400);
tim_rdata->dier = TIM_DIER_UDE; tim_rdata->dier = TIM_DIER_UDE;
tim_rdata->cr2 = 0; tim_rdata->cr2 = 0;
@ -280,13 +307,13 @@ static void timer_dma_init(void)
DMA_CCR_EN); DMA_CCR_EN);
/* WDATA Timer setup: /* WDATA Timer setup:
* The counter runs from 0x0000-0xFFFF inclusive at full SYSCLK rate. * The counter runs from 0x0000-0xFFFF inclusive at SAMPLECLK rate.
* *
* Ch.1 (WDATA) is in Input Capture mode, sampling on every clock and with * Ch.1 (WDATA) is in Input Capture mode, sampling on every clock and with
* no input prescaling or filtering. Samples are captured on the falling * no input prescaling or filtering. Samples are captured on the falling
* edge of the input (CCxP=1). DMA is used to copy the sample into a ring * edge of the input (CCxP=1). DMA is used to copy the sample into a ring
* buffer for batch processing in the DMA-completion ISR. */ * buffer for batch processing in the DMA-completion ISR. */
tim_wdata->psc = 0; tim_wdata->psc = (SYSCLK_MHZ/SAMPLECLK_MHZ) - 1;
tim_wdata->arr = 0xffff; tim_wdata->arr = 0xffff;
tim_wdata->ccmr1 = TIM_CCMR1_CC1S(TIM_CCS_INPUT_TI1); tim_wdata->ccmr1 = TIM_CCMR1_CC1S(TIM_CCS_INPUT_TI1);
tim_wdata->dier = TIM_DIER_CC1DE; tim_wdata->dier = TIM_DIER_CC1DE;
@ -364,7 +391,7 @@ static void wdata_stop(void)
image->wr_prod++; image->wr_prod++;
#if !defined(QUICKDISK) #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. */ /* Opportunistically insert an INDEX pulse ahead of writeback. */
drive_change_output(drv, outp_index, TRUE); drive_change_output(drv, outp_index, TRUE);
index.fake_fired = TRUE; index.fake_fired = TRUE;
@ -407,10 +434,10 @@ static void wdata_start(void)
tim_wdata->ccer = TIM_CCER_CC1E | TIM_CCER_CC1P; tim_wdata->ccer = TIM_CCER_CC1E | TIM_CCER_CC1P;
tim_wdata->cr1 = TIM_CR1_CEN; tim_wdata->cr1 = TIM_CR1_CEN;
/* Find rotational start position of the write, in SYSCLK ticks. */ /* Find rotational start position of the write, in SAMPLECLK ticks. */
start_pos = max_t(int32_t, 0, time_diff(index.prev_time, time_now())); start_pos = max_t(int32_t, 0, time_diff(index.prev_time, time_now()));
start_pos %= drive.image->stk_per_rev; start_pos %= drive.image->stk_per_rev;
start_pos *= SYSCLK_MHZ / STK_MHZ; start_pos *= SAMPLECLK_MHZ / STK_MHZ;
write = get_write(image, image->wr_prod); write = get_write(image, image->wr_prod);
write->start = start_pos; write->start = start_pos;
write->track = drive_calc_track(&drive); write->track = drive_calc_track(&drive);
@ -433,6 +460,7 @@ static void rdata_stop(void)
/* Ok we're now stopping DMA activity. */ /* Ok we're now stopping DMA activity. */
dma_rd->state = DMA_stopping; dma_rd->state = DMA_stopping;
dma_rd_set_active(FALSE);
/* If DMA was not yet active, don't need to touch peripherals. */ /* If DMA was not yet active, don't need to touch peripherals. */
if (prev_state != DMA_active) if (prev_state != DMA_active)
@ -461,6 +489,7 @@ static void rdata_start(void)
goto out; goto out;
dma_rd->state = DMA_active; dma_rd->state = DMA_active;
dma_rd_set_active(TRUE);
/* Start timer. */ /* Start timer. */
tim_rdata->egr = TIM_EGR_UG; tim_rdata->egr = TIM_EGR_UG;
@ -510,8 +539,7 @@ static bool_t dma_wr_handle(struct drive *drv)
} }
/* Set up the track for writing. */ /* Set up the track for writing. */
if (image_setup_track(im, write->track, NULL)) image_setup_track(im, write->track, NULL);
return TRUE;
drv->writing = TRUE; drv->writing = TRUE;
@ -530,9 +558,6 @@ static bool_t dma_wr_handle(struct drive *drv)
/* Align the bitcell consumer index for start of next write. */ /* Align the bitcell consumer index for start of next write. */
im->bufs.write_bc.cons = (write->bc_end + 31) & ~31; im->bufs.write_bc.cons = (write->bc_end + 31) & ~31;
/* Sync back to mass storage. */
F_sync(&im->fp);
IRQ_global_disable(); IRQ_global_disable();
/* Consume the write from the pipeline buffer. */ /* Consume the write from the pipeline buffer. */
im->wr_cons++; im->wr_cons++;
@ -608,6 +633,30 @@ static void IRQ_rdata_dma(void)
IRQx_set_pending(dma_rdata_irq); IRQx_set_pending(dma_rdata_irq);
} }
#if !defined(QUICKDISK)
ASSERT(drv->image->index_pulses_len < MAX_CUSTOM_PULSES);
if (drv->image->index_pulses_ver != index.custom_pulses_ver) {
time_t current_pulse_pos = time_since(index.prev_time);
uint32_t oldpri;
oldpri = IRQ_save(TIMER_IRQ_PRI);
for (i = 0; i < drv->image->index_pulses_len; i++)
index.custom_pulses[i] = (drv->image->index_pulses[i]>>4)
/ (SAMPLECLK_MHZ/TIME_MHZ);
index.custom_pulses_len = drv->image->index_pulses_len;
index.custom_pulses_ver = drv->image->index_pulses_ver;
for (i = 0; i < index.custom_pulses_len; i++)
if (current_pulse_pos <= index.custom_pulses[i])
break;
if (i < index.custom_pulses_len)
timer_set(&index.custom_timer, index.prev_time + index.custom_pulses[i]);
else
timer_cancel(&index.custom_timer);
IRQ_restore(oldpri);
}
#endif
/* Check if we have crossed the index mark. If not, we're done. */ /* Check if we have crossed the index mark. If not, we're done. */
if (image_ticks_since_index(drv->image) >= prev_ticks_since_index) if (image_ticks_since_index(drv->image) >= prev_ticks_since_index)
return; return;
@ -633,7 +682,7 @@ static void IRQ_rdata_dma(void)
/* Subtract current flux offset beyond the index. */ /* Subtract current flux offset beyond the index. */
ticks -= image_ticks_since_index(drv->image); ticks -= image_ticks_since_index(drv->image);
/* Calculate deadline for index timer. */ /* Calculate deadline for index timer. */
ticks /= SYSCLK_MHZ/TIME_MHZ; ticks /= SAMPLECLK_MHZ/TIME_MHZ;
timer_set(&index.timer, now + ticks); timer_set(&index.timer, now + ticks);
} }
@ -722,6 +771,14 @@ static void IRQ_wdata_dma(void)
dma_wr->prev_sample = prev; dma_wr->prev_sample = prev;
} }
void floppy_sync(void)
{
struct drive *drv = &drive;
struct image *im = drv->image;
image_sync(im);
}
/* /*
* Local variables: * Local variables:
* mode: C * mode: C

62
src/fpec_at32f435.c Normal file
View file

@ -0,0 +1,62 @@
/*
* fpec_at324f435.c
*
* AT32F435 Flash Memory Program/Erase Controller (FPEC).
*
* 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>.
*/
static void fpec_wait_and_clear(FLASH_BANK bank)
{
while (bank->sr & FLASH_SR_BSY)
continue;
bank->sr = FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR;
bank->cr = 0;
}
void fpec_init(void)
{
/* Unlock the FPEC. */
if (flash->bank1.cr & FLASH_CR_LOCK) {
flash->unlock1 = 0x45670123;
flash->unlock1 = 0xcdef89ab;
}
fpec_wait_and_clear(&flash->bank1);
}
void fpec_page_erase(uint32_t flash_address)
{
FLASH_BANK bank = &flash->bank1;
fpec_wait_and_clear(bank);
bank->ar = flash_address;
bank->cr |= FLASH_CR_PG_ER | FLASH_CR_ERASE_STRT;
fpec_wait_and_clear(bank);
}
void fpec_write(const void *data, unsigned int size, uint32_t flash_address)
{
FLASH_BANK bank = &flash->bank1;
uint16_t *_f = (uint16_t *)flash_address;
const uint16_t *_d = data;
fpec_wait_and_clear(bank);
for (; size != 0; size -= 2) {
bank->cr |= FLASH_CR_PG;
*_f++ = *_d++;
fpec_wait_and_clear(bank);
}
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

View file

@ -1,5 +1,5 @@
/* /*
* fpec.c * fpec_stm32f105.c
* *
* STM32F10x Flash Memory Program/Erase Controller (FPEC). * STM32F10x Flash Memory Program/Erase Controller (FPEC).
* *

205
src/fs_async.c Normal file
View file

@ -0,0 +1,205 @@
/*
* fs_async.c
*
* Non-blocking error-handling wrappers around FatFS.
*
* Written & released by Keir Fraser <keir.xen@gmail.com> and Eric Anderson
* <ejona86@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>.
*/
union op_args {
struct {
FSIZE_t ofs;
} lseek;
struct {
void *buff;
UINT btr;
UINT *br;
} read;
struct {
const void *buff;
UINT btw;
UINT *bw;
} write;
struct {
BYTE *buff;
LBA_t sector;
UINT count;
} disk_read;
struct {
const BYTE *buff;
LBA_t sector;
UINT count;
} disk_write;
struct {
BYTE cmd;
void* buff;
DRESULT *res;
} disk_ioctl;
};
struct op;
typedef void (*f_op_func)(struct op *op);
struct op {
f_op_func func;
FIL *fp;
union op_args args;
bool_t cancelled;
};
#define OPS_LEN 4 /* Power of 2. */
#define OPS_MASK(x) ((x)&(OPS_LEN-1))
static struct {
struct op ops[OPS_LEN];
int prod, cons;
} f_async_queue;
bool_t F_async_isdone(FOP oper) {
ASSERT(oper - f_async_queue.prod < 0);
return oper - f_async_queue.cons < 0;
}
void F_async_wait(FOP oper) {
while (!F_async_isdone(oper))
thread_yield();
}
void F_async_cancel(FOP oper) {
if (F_async_isdone(oper))
return;
f_async_queue.ops[OPS_MASK(oper)].cancelled = TRUE;
}
void F_async_cancel_all(void) {
for (int i = 0; i < OPS_LEN; i++)
f_async_queue.ops[i].cancelled = TRUE;
}
FOP F_async_get_completed_op(void) {
return f_async_queue.cons - 4; /* "Random" op. Chosen by fair dice roll. */
}
static FOP enqueue(f_op_func func, FIL *fp, union op_args *args) {
struct op *op;
bool_t printed = FALSE;
while (f_async_queue.prod - f_async_queue.cons >= OPS_LEN) {
if (!printed) {
printk("async queue full; blocking\n");
printed = TRUE;
}
thread_yield();
}
op = &f_async_queue.ops[OPS_MASK(f_async_queue.prod)];
op->func = func;
op->fp = fp;
op->args = *args;
op->cancelled = FALSE;
return f_async_queue.prod++;
}
void F_async_drain(void) {
while (f_async_queue.prod != f_async_queue.cons) {
struct op *op = &f_async_queue.ops[OPS_MASK(f_async_queue.cons)];
if (!op->cancelled) {
op->func(op);
}
f_async_queue.cons++;
}
}
static void do_lseek(struct op *op) {
/* Short-circuit if already appropriately positioned. The caller of
* F_lseek_async can't check fptr themselves like they could using the
* blocking API. */
if (op->args.lseek.ofs == op->fp->fptr)
return;
F_lseek(op->fp, op->args.lseek.ofs);
}
FOP F_lseek_async(FIL *fp, FSIZE_t ofs) {
union op_args args = { .lseek = {ofs} };
return enqueue(do_lseek, fp, &args);
}
static void do_read(struct op *op) {
time_t start = time_now(), duration;
F_read(op->fp, op->args.read.buff, op->args.read.btr, op->args.read.br);
duration = time_since(start);
if (duration > time_us(9000))
printk("Lengthy %s. %d bytes took %dus\n", "read", op->args.read.btr,
duration / TIME_MHZ);
}
FOP F_read_async(FIL *fp, void *buff, UINT btr, UINT *br) {
union op_args args = { .read = {buff, btr, br} };
return enqueue(do_read, fp, &args);
}
static void do_write(struct op *op) {
time_t start = time_now(), duration;
F_write(op->fp, op->args.write.buff, op->args.write.btw, op->args.write.bw);
duration = time_since(start);
if (duration > time_us(9000))
printk("Lengthy %s. %d bytes took %dus\n", "write", op->args.write.btw,
duration / TIME_MHZ);
}
FOP F_write_async(FIL *fp, const void *buff, UINT btw, UINT *bw) {
union op_args args = { .write = {buff, btw, bw} };
return enqueue(do_write, fp, &args);
}
static void do_sync(struct op *op) {
F_sync(op->fp);
}
FOP F_sync_async(FIL *fp) {
union op_args args = { 0 };
return enqueue(do_sync, fp, &args);
}
static void do_disk_read(struct op *op) {
if (disk_read((uintptr_t) op->fp, op->args.disk_read.buff,
op->args.disk_read.sector, op->args.disk_read.count) != RES_OK)
F_die(FR_DISK_ERR);
}
FOP disk_read_async(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
union op_args args = { .disk_read = {buff, sector, count} };
return enqueue(do_disk_read, (void*)(uintptr_t) pdrv, &args);
}
static void do_disk_write(struct op *op) {
time_t start = time_now();
if (disk_write((uintptr_t) op->fp, op->args.disk_write.buff,
op->args.disk_write.sector, op->args.disk_write.count) != RES_OK)
F_die(FR_DISK_ERR);
printk("Disk write %08x[%2d]: %dus\n",
op->args.disk_write.sector,
op->args.disk_write.count,
time_since(start) / TIME_MHZ);
}
FOP disk_write_async(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
union op_args args = { .disk_write = {buff, sector, count} };
return enqueue(do_disk_write, (void*)(uintptr_t) pdrv, &args);
}
static void do_disk_ioctl(struct op *op) {
DRESULT res = disk_ioctl((uintptr_t) op->fp, op->args.disk_ioctl.cmd,
op->args.disk_ioctl.buff);
if (op->args.disk_ioctl.res != NULL)
*op->args.disk_ioctl.res = res;
else if (res != RES_OK)
F_die(FR_DISK_ERR);
}
FOP disk_ioctl_async(BYTE pdrv, BYTE cmd, void* buff, DRESULT *res) {
union op_args args = { .disk_ioctl = {cmd, buff, res} };
return enqueue(do_disk_ioctl, (void*)(uintptr_t) pdrv, &args);
}

View file

@ -32,21 +32,29 @@
* See the file COPYING for more details, or visit <http://unlicense.org>. * See the file COPYING for more details, or visit <http://unlicense.org>.
*/ */
/* Main bootloader: flashes the main firmware (last 96kB of Flash). */ /* Main bootloader: flashes the main firmware. */
#if MCU == STM32F105
#define FIRMWARE_START 0x08008000 #define FIRMWARE_START 0x08008000
#define FIRMWARE_END (0x08020000 - FLASH_PAGE_SIZE) #define FIRMWARE_END (0x08020000 - FLASH_PAGE_SIZE)
#elif MCU == AT32F435
#define FIRMWARE_START 0x0800c000
#define FIRMWARE_END (0x08040000 - FLASH_PAGE_SIZE)
#endif
#define FILE_PATTERN "ff_gotek*.upd" #define FILE_PATTERN "ff_gotek*.upd"
int EXC_reset(void) __attribute__((alias("main"))); int EXC_reset(void) __attribute__((alias("main")));
static uint8_t USBH_Cfg_Rx_Buffer[512]; static uint8_t USBH_Cfg_Rx_Buffer[512];
/* File buffer. */
static uint8_t buf[2048];
/* FatFS */ /* FatFS */
static FATFS fatfs; static FATFS fatfs;
/* Shared state. regarding update progress/failure. */ /* Shared state. regarding update progress/failure. */
static bool_t old_firmware_erased; static bool_t old_firmware_erased;
static enum { static enum fail_code {
FC_no_file = 1, /* no update file */ FC_no_file = 1, /* no update file */
FC_multiple_files, /* multiple update files */ FC_multiple_files, /* multiple update files */
FC_bad_file, /* bad signature or size */ FC_bad_file, /* bad signature or size */
@ -67,10 +75,28 @@ static void canary_check(void)
ASSERT(_thread_stackbottom[0] == 0xdeadbeef); ASSERT(_thread_stackbottom[0] == 0xdeadbeef);
} }
#define MAIN_FW_KEY 0x39b5ba2c
static void reset_to_main_fw(void) __attribute__((noreturn));
static void reset_to_main_fw(void)
{
*(volatile uint32_t *)_ebss = MAIN_FW_KEY;
cpu_sync();
system_reset();
}
static bool_t main_fw_requested(void)
{
bool_t req = (*(volatile uint32_t *)_ebss == MAIN_FW_KEY);
*(volatile uint32_t *)_ebss = 0;
return req;
}
static bool_t fw_update_requested(void) static bool_t fw_update_requested(void)
{ {
bool_t requested; bool_t requested;
#if MCU == STM32F105
/* Power up the backup-register interface and allow writes. */ /* Power up the backup-register interface and allow writes. */
rcc->apb1enr |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN; rcc->apb1enr |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN;
pwr->cr |= PWR_CR_DBP; pwr->cr |= PWR_CR_DBP;
@ -82,13 +108,21 @@ static bool_t fw_update_requested(void)
bkp->dr1[0] = bkp->dr1[1] = 0; bkp->dr1[0] = bkp->dr1[1] = 0;
rcc->apb1enr = 0; rcc->apb1enr = 0;
#elif MCU == AT32F435
/* Check-and-clear a magic value poked into SRAM1 by the main firmware. */
requested = (_reset_flag == RESET_FLAG_BOOTLOADER);
_reset_flag = 0;
#endif
return requested; return requested;
} }
static void erase_old_firmware(void) static void erase_old_firmware(void)
{ {
uint32_t p; uint32_t p;
for (p = FIRMWARE_START; p < FIRMWARE_END; p += FLASH_PAGE_SIZE) for (p = FIRMWARE_START; p < FIRMWARE_END; p += flash_page_size)
fpec_page_erase(p); fpec_page_erase(p);
} }
@ -106,71 +140,69 @@ static void msg_display(const char *p)
} }
} }
int update(void *unused) static enum fail_code find_update_file(
char *file_name, size_t file_name_size, const char *file_pattern)
{ {
/* FatFS state, local to this function, but off stack. */
static FIL file;
static DIR dp; static DIR dp;
static FILINFO fno; static FILINFO fno;
static char update_fname[FF_MAX_LFN+1];
/* Our file buffer. Again, off stack. */
static uint8_t buf[2048];
uint32_t p;
uint16_t footer[2], crc;
UINT i, nr;
FIL *fp = &file;
/* Find the update file, confirming that it exists and there is no /* Find the update file, confirming that it exists and there is no
* ambiguity (ie. we don't allow multiple update files). */ * ambiguity (ie. we don't allow multiple update files). */
F_findfirst(&dp, &fno, "", FILE_PATTERN); F_findfirst(&dp, &fno, "", file_pattern);
if (*fno.fname == '\0') { if (*fno.fname == '\0') {
fail_code = FC_no_file; return FC_no_file;
goto fail;
} }
snprintf(update_fname, sizeof(update_fname), "%s", fno.fname); snprintf(file_name, file_name_size, "%s", fno.fname);
printk("Found update \"%s\"\n", update_fname); printk("Found update \"%s\"\n", file_name);
F_findnext(&dp, &fno); F_findnext(&dp, &fno);
if (*fno.fname != '\0') { if (*fno.fname != '\0') {
printk("** Error: found another file \"%s\"\n", fno.fname); printk("** Error: found another file \"%s\"\n", fno.fname);
fail_code = FC_multiple_files; return FC_multiple_files;
goto fail;
} }
F_closedir(&dp); F_closedir(&dp);
return 0;
}
/* Open and sanity-check the file. */ static uint16_t file_crc(FIL *fp, UINT off, UINT sz)
msg_display(" RD"); {
F_open(fp, update_fname, FA_READ); uint16_t crc;
/* Check size. */ UINT todo, nr;
fail_code = ((f_size(fp) < 1024)
|| (f_size(fp) > (FIRMWARE_END-FIRMWARE_START)) crc = 0xffff;
|| (f_size(fp) & 3)) F_lseek(fp, off);
? FC_bad_file : 0; for (todo = sz; todo != 0; todo -= nr) {
printk("%u bytes: %s\n", f_size(fp), fail_code ? "BAD" : "OK"); nr = min_t(UINT, sizeof(buf), todo);
if (fail_code) F_read(fp, buf, nr, NULL);
goto fail; crc = crc16_ccitt(buf, nr, crc);
/* Check signature in footer. */
F_lseek(fp, f_size(fp) - sizeof(footer));
F_read(fp, footer, sizeof(footer), NULL);
if (be16toh(footer[0]) != 0x4659/* "FY" */) {
fail_code = FC_bad_file;
goto fail;
} }
return crc;
}
static enum fail_code erase_and_program(FIL *fp, UINT off, UINT sz)
{
uint32_t p;
uint16_t footer[2];
UINT todo, nr;
/* Check size. */
fail_code = ((sz < 1024)
|| (sz > (FIRMWARE_END-FIRMWARE_START))
|| (sz & 3))
? FC_bad_file : 0;
printk("%u bytes: %s\n", sz, fail_code ? "BAD" : "OK");
if (fail_code)
return fail_code;
/* Check signature in footer. */
F_lseek(fp, off + sz - sizeof(footer));
F_read(fp, footer, sizeof(footer), NULL);
if (be16toh(footer[0]) != 0x4659/* "FY" */)
return FC_bad_file;
/* Check the CRC-CCITT. */ /* Check the CRC-CCITT. */
msg_display("CRC"); msg_display("CRC");
crc = 0xffff; if (file_crc(fp, off, sz) != 0)
F_lseek(fp, 0); return FC_bad_crc;
for (i = 0; !f_eof(fp); i++) {
nr = min_t(UINT, sizeof(buf), f_size(fp) - f_tell(fp));
F_read(&file, buf, nr, NULL);
crc = crc16_ccitt(buf, nr, crc);
}
if (crc != 0) {
fail_code = FC_bad_crc;
goto fail;
}
/* Erase the old firmware. */ /* Erase the old firmware. */
msg_display("CLR"); msg_display("CLR");
@ -180,32 +212,116 @@ int update(void *unused)
/* Program the new firmware. */ /* Program the new firmware. */
msg_display("PRG"); msg_display("PRG");
crc = 0xffff; F_lseek(fp, off);
F_lseek(fp, 0);
p = FIRMWARE_START; p = FIRMWARE_START;
for (i = 0; !f_eof(fp); i++) { for (todo = sz; todo != 0; todo -= nr) {
nr = min_t(UINT, sizeof(buf), f_size(fp) - f_tell(fp)); nr = min_t(UINT, sizeof(buf), todo);
F_read(&file, buf, nr, NULL); F_read(fp, buf, nr, NULL);
fpec_write(buf, nr, p); fpec_write(buf, nr, p);
if (memcmp((void *)p, buf, nr) != 0) { if (memcmp((void *)p, buf, nr) != 0) {
/* Byte-by-byte verify failed. */ /* Byte-by-byte verify failed. */
fail_code = FC_bad_prg; return FC_bad_prg;
goto fail;
} }
p += nr; p += nr;
} }
/* Verify the new firmware (CRC-CCITT). */ /* Verify the new firmware (CRC-CCITT). */
p = FIRMWARE_START; if (crc16_ccitt((void *)FIRMWARE_START, sz, 0xffff) != 0) {
crc = crc16_ccitt((void *)p, f_size(fp), 0xffff);
if (crc) {
/* CRC verify failed. */ /* CRC verify failed. */
fail_code = FC_bad_prg; return FC_bad_prg;
goto fail;
} }
/* All done! */ return 0;
fail_code = 0; }
static enum fail_code find_new_update_entry(FIL *fp, UINT *p_off, UINT *p_sz)
{
struct {
char sig[4];
uint32_t off;
uint32_t nr;
} header;
struct {
uint8_t model;
uint8_t pad[3];
uint32_t off;
uint32_t len;
} entry;
uint32_t i;
F_lseek(fp, 0);
F_read(fp, &header, sizeof(header), NULL);
if (strncmp(header.sig, "FFUP", 4))
return FC_bad_file;
if (file_crc(fp, 0, header.off + header.nr*12 + 4) != 0)
return FC_bad_crc;
F_lseek(fp, header.off);
for (i = 0; i < header.nr; i++) {
F_read(fp, &entry, sizeof(entry), NULL);
if (entry.model == MCU) {
*p_off = entry.off;
*p_sz = entry.len;
return 0;
}
}
return FC_bad_file;
}
static enum fail_code find_update_entry(FIL *fp, UINT *p_off, UINT *p_sz)
{
uint16_t footer[2];
*p_off = *p_sz = 0;
F_lseek(fp, f_size(fp) - sizeof(footer));
F_read(fp, footer, sizeof(footer), NULL);
switch (be16toh(footer[0])) {
#if MCU == STM32F105
case 0x4659/* "FY" */:
*p_off = 0;
*p_sz = f_size(fp);
return 0;
#endif
case 0x4646/* "FF" */:
return find_new_update_entry(fp, p_off, p_sz);
}
return FC_bad_file;
}
int update(void *unused)
{
/* FatFS state, local to this function, but off stack. */
static FIL file;
static char update_fname[FF_MAX_LFN+1];
FIL *fp = &file;
UINT off, sz;
fail_code = find_update_file(update_fname, sizeof(update_fname),
"flashfloppy-*.upd");
#if MCU == STM32F105
if (fail_code == FC_no_file)
fail_code = find_update_file(update_fname, sizeof(update_fname),
"ff_gotek*.upd");
#endif
if (fail_code)
goto fail;
/* Open and sanity-check the file. */
msg_display(" RD");
F_open(fp, update_fname, FA_READ);
fail_code = find_update_entry(fp, &off, &sz);
if (fail_code)
goto fail;
fail_code = erase_and_program(fp, off, sz);
fail: fail:
canary_check(); canary_check();
@ -225,19 +341,14 @@ static void display_setting(bool_t on)
} }
} }
#define B_LEFT 1
#define B_RIGHT 2
#define B_SELECT 4
static bool_t buttons_pressed(void) static bool_t buttons_pressed(void)
{ {
unsigned int b = board_get_buttons() | osd_buttons_rx;
return ( return (
/* Check for both LEFT and RIGHT buttons pressed. */ /* Check for both LEFT and RIGHT buttons pressed. */
(!gpio_read_pin(gpioc, 8) && !gpio_read_pin(gpioc, 7)) ((b & (B_LEFT|B_RIGHT)) == (B_LEFT|B_RIGHT))
|| ((osd_buttons_rx & (B_LEFT|B_RIGHT)) == (B_LEFT|B_RIGHT))
/* Also respond to third (SELECT) button on its own. */ /* Also respond to third (SELECT) button on its own. */
|| !gpio_read_pin(gpioc, 6) || (b & B_SELECT));
|| (osd_buttons_rx & B_SELECT));
} }
/* Wait for both buttons to be pressed (LOW) or not pressed (HIGH). Perform /* Wait for both buttons to be pressed (LOW) or not pressed (HIGH). Perform
@ -252,10 +363,8 @@ static void wait_buttons(uint8_t level)
x <<= 1; x <<= 1;
if (level) { if (level) {
/* All buttons must be released. */ /* All buttons must be released. */
x |= gpio_read_pin(gpioc, 8) unsigned int b = board_get_buttons() | osd_buttons_rx;
&& gpio_read_pin(gpioc, 7) x |= !b;
&& gpio_read_pin(gpioc, 6)
&& !osd_buttons_rx;
} else { } else {
x |= buttons_pressed(); x |= buttons_pressed();
} }
@ -266,23 +375,16 @@ int main(void)
{ {
char msg[20]; char msg[20];
FRESULT fres; FRESULT fres;
bool_t update_requested;
/* Relocate DATA. Initialise BSS. */ /* Relocate DATA. Initialise BSS. */
if (_sdat != _ldat) if (&_sdat[0] != &_ldat[0])
memcpy(_sdat, _ldat, _edat-_sdat); memcpy(_sdat, _ldat, _edat-_sdat);
memset(_sbss, 0, _ebss-_sbss); memset(_sbss, 0, _ebss-_sbss);
/* Enable GPIOC, set all pins as input with weak pull-up. */ update_requested = fw_update_requested();
rcc->apb2enr = RCC_APB2ENR_IOPCEN;
gpioc->odr = 0xffffu;
gpioc->crh = 0x88888888u;
gpioc->crl = 0x88888888u;
/* Enter update mode only if: if (main_fw_requested() && !update_requested) {
* 1. Buttons are pressed; or
* 2. It was requested via main firmware; or
* 3. No valid main firmware is found in Flash. */
if (!buttons_pressed() && !fw_update_requested()) {
/* Check for, and jump to, the main firmware. */ /* Check for, and jump to, the main firmware. */
uint32_t sp = *(uint32_t *)FIRMWARE_START; uint32_t sp = *(uint32_t *)FIRMWARE_START;
uint32_t pc = *(uint32_t *)(FIRMWARE_START + 4); uint32_t pc = *(uint32_t *)(FIRMWARE_START + 4);
@ -291,6 +393,7 @@ int main(void)
"mov sp,%0 ; blx %1" "mov sp,%0 ; blx %1"
:: "r" (sp), "r" (pc)); :: "r" (sp), "r" (pc));
} }
update_requested = TRUE;
} }
/* /*
@ -303,11 +406,15 @@ int main(void)
time_init(); time_init();
console_init(); console_init();
board_init(); board_init();
delay_ms(200); /* 5v settle */
printk("\n** FF Update Bootloader v%s for Gotek\n", fw_ver); printk("\n** FF Update Bootloader %s\n", fw_ver);
printk("** Keir Fraser <keir.xen@gmail.com>\n"); printk("** Keir Fraser <keir.xen@gmail.com>\n");
printk("** https://github.com/keirf/FlashFloppy\n\n"); printk("** github:keirf/flashfloppy\n\n");
if (!update_requested && !buttons_pressed())
reset_to_main_fw();
delay_ms(200); /* 5v settle */
flash_ff_cfg_read(); flash_ff_cfg_read();
@ -319,8 +426,7 @@ int main(void)
case DT_LCD_OLED: case DT_LCD_OLED:
snprintf(msg, sizeof(msg), "FF Update Flash"); snprintf(msg, sizeof(msg), "FF Update Flash");
lcd_write(0, 0, 0, msg); lcd_write(0, 0, 0, msg);
lcd_write(0, 1, 0, "v"); lcd_write(0, 1, 0, fw_ver);
lcd_write(1, 1, 0, fw_ver);
lcd_sync(); lcd_sync();
break; break;
} }

View file

@ -3,12 +3,63 @@
* *
* Gotek board-specific setup and management. * Gotek board-specific setup and management.
* *
* SFRC922, SFRC922C, SFRC922D et al
* Original LQFP64 designs, using STM or AT chips.
* Buttons: PC6 = Select, PC7 = Right, PC8 = Left
* Rotary: PC10, PC11
*
* SFRC922AT3
* LQFP48 design, missing rotary header.
* Alternative rotary location at PA13, PA14
* Buttons: PA5 = Select, PA4 = Right, PA3 = Left
*
* SFRKC30AT4, SFRKC30.AT4, SFRKC30.AT4.7 (KC30 Rev 1)
* LQFP64 designs with original rotary header and "KC30" rotary header.
* Buttons: PA5 = Select, PA4 = Right, PA3 = Left
* Rotary: PC10, PC11
* KC30: PF6/PH2 = Select, PA6/PA15 = Rotary
*
* SFRKC30AT3 (KC30 Rev 1)
* LQFP48 design similar to SFRC922AT3 but with the "KC30" rotary header.
* Buttons: PA5 = Select, PA4 = Right, PA3 = Left
* KC30: PF6/PH2 = Select, PA6/PA15 = Rotary
*
* SFRKC30.AT2 (KC30 Rev 1)
* QFN32 design with various pin changes and features missing. There are
* two versions; the newer version reintroduces jumper position JC.
* Missing:
* * Original rotary header
* * JC jumper position (old version)
* Relocated to new MCU pins:
* * Display header is moved to PB[7:6] using I2C1 instead of I2C2
* * KC30 header SELECT/button pin
* * Floppy output pins 2 and 26
* * Floppy WGATE input pin
* * JC jumper at PA9 (new version)
* Buttons: PA5 = Select, PA4 = Right, PA3 = Left
* KC30: PA10 = Select, PA6/PA15 = Rotary
*
* SFRKC30.AT4.35 (KC30 Rev 2)
* As SFRKC30.AT4 except PC15 is tied HIGH for identification.
* MOTOR (pin 16) is optionally jumpered to PB12 with 1k pullup to 5v.
*
* Written & released by Keir Fraser <keir.xen@gmail.com> * Written & released by Keir Fraser <keir.xen@gmail.com>
* *
* This is free and unencumbered software released into the public domain. * This is free and unencumbered software released into the public domain.
* See the file COPYING for more details, or visit <http://unlicense.org>. * See the file COPYING for more details, or visit <http://unlicense.org>.
*/ */
uint8_t mcu_package;
uint8_t has_kc30_header;
#if MCU == STM32F105
#define kc30_sel_gpio gpiof
#define kc30_sel_pin 6
#elif MCU == AT32F435
#define kc30_sel_gpio gpioh
#define kc30_sel_pin 2
#endif
/* Pull up currently unused and possibly-floating pins. */ /* Pull up currently unused and possibly-floating pins. */
static void gpio_pull_up_pins(GPIO gpio, uint16_t mask) static void gpio_pull_up_pins(GPIO gpio, uint16_t mask)
{ {
@ -20,13 +71,115 @@ static void gpio_pull_up_pins(GPIO gpio, uint16_t mask)
} }
} }
unsigned int board_get_buttons(void)
{
/* All recent Gotek revisions, regardless of MCU model or package:
* PA5 = Select, PA4 = Right, PA3 = Left.
* Note: "Enhanced Gotek" design uses these pins so must skip them here. */
unsigned int x = (board_id == BRDREV_Gotek_standard)
? gpioa->idr >> 3 : -1;
/* Earlier Gotek revisions (all of which are LQFP64):
* PC6 = Select, PC7 = Right, PC8 = Left. */
if (mcu_package == MCU_LQFP64)
x &= _rbit32(gpioc->idr) >> 23;
x = ~x & 7;
if (has_kc30_header) {
/* KC30 Select pin, Artery models only:
* PF6/PH2 = Select; except QFN32: PA10 = Select. */
unsigned int kc30 = (mcu_package == MCU_QFN32
? gpioa->idr >> (10-2) /* PA10 */
: kc30_sel_gpio->idr >> (kc30_sel_pin-2));
x |= ~kc30 & 4;
}
return x;
}
unsigned int board_get_rotary(void)
{
unsigned int x = 3;
if ((mcu_package != MCU_QFN32) && (ff_cfg.chgrst != CHGRST_pa14)) {
/* Alternative location at PA13, PA14. */
x &= gpioa->idr >> 13;
}
if (mcu_package == MCU_LQFP64) {
/* Original rotary header at PC10, PC11. */
x &= gpioc->idr >> 10;
}
if (has_kc30_header) {
/* KC30 rotary pins PA6, PA15. */
unsigned int kc30 = gpioa->idr;
kc30 = ((kc30>>6)&1) | ((kc30>>(15-1))&2);
x &= kc30;
}
return x;
}
uint32_t board_rotary_exti_mask;
void board_setup_rotary_exti(void)
{
uint32_t m = 0;
if ((mcu_package != MCU_QFN32) && (ff_cfg.chgrst != CHGRST_pa14)) {
/* Alternative location at PA13, PA14. */
exti_route_pa(13);
exti_route_pa(14);
m |= m(13) | m(14);
}
if (mcu_package == MCU_LQFP64) {
/* Original rotary header at PC10, PC11. */
exti_route_pc(10);
exti_route_pc(11);
m |= m(10) | m(11);
}
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. */
exti_route_pa(6);
exti_route_pa(15);
m |= m(6) | m(15);
}
board_rotary_exti_mask = m;
exti->rtsr |= m;
exti->ftsr |= m;
exti->imr |= m;
}
void board_jc_set_mode(unsigned int mode)
{
if (mcu_package == MCU_QFN32) {
#if !defined(NDEBUG)
/* PA9 is used for serial tx */
#else
gpio_configure_pin(gpioa, 9, mode);
#endif
} else {
gpio_configure_pin(gpiob, 1, mode);
}
}
bool_t board_jc_strapped(void)
{
if (mcu_package == MCU_QFN32) {
#if !defined(NDEBUG)
return FALSE; /* PA9 is used for serial tx */
#else
return !gpio_read_pin(gpioa, 9);
#endif
}
return !gpio_read_pin(gpiob, 1);
}
void board_init(void) void board_init(void)
{ {
uint16_t pa_skip, pb_skip; uint16_t pa_skip, pb_skip, pc_skip;
uint8_t id; uint8_t id;
/* PA0-1,8 (floppy inputs), PA2 (speaker), PA9-10 (serial console). */ /* PA0-1,8 (floppy inputs), PA2 (speaker). */
pa_skip = 0x0707; pa_skip = 0x0107;
#if !defined(NDEBUG)
/* PA9-10 (serial console). */
pa_skip |= 0x0600;
#endif
/* PB0,4,9 (floppy inputs). */ /* PB0,4,9 (floppy inputs). */
pb_skip = 0x0211; pb_skip = 0x0211;
@ -37,24 +190,95 @@ void board_init(void)
gpio_configure_pin(gpioa, 12, GPI_pull_down); gpio_configure_pin(gpioa, 12, GPI_pull_down);
/* Pull up all PCx pins. */ /* Pull up all PCx pins. */
gpio_pull_up_pins(gpioc, ~0x0000); pc_skip = 0x0000;
gpio_pull_up_pins(gpioc, ~pc_skip);
/* Wait for ID to stabilise at PC[15:12]. */ /* Wait for ID to stabilise at PC[15:12]. */
delay_us(5); delay_us(100);
id = (gpioc->idr >> 12) & 0xf; id = (gpioc->idr >> 12) & 0xf;
switch (board_id = id) { if (is_artery_mcu) {
case BRDREV_Gotek_standard: switch (dbg->mcu_idcode & 0xfff) {
break; case 0x1c6: /* AT32F415KBU7-4 */
case BRDREV_Gotek_enhanced: case 0x242: /* AT32F415KCU7-4 */
case BRDREV_Gotek_sd_card: mcu_package = MCU_QFN32;
/* PA3,15 (floppy inputs), PA4 (USBENA). */ id = 0xf;
pa_skip |= 0x8018; break;
/* PA4: /USBENA */ }
gpio_configure_pin(gpioa, 4, GPO_pushpull(_2MHz, LOW)); }
break;
default: if (is_artery_mcu && (id & 2)) {
ASSERT(0);
/* This is a factory Gotek board design, or direct clone, with an
* Artery MCU. We now check which factory design: variants exist for
* 48- and 64-pin Artery MCUs, and with various headers for buttons and
* rotary encoders. Though we have discriminated on PC13 alone, the
* only expected ID values here are 1110 (48-pin MCU) and 1111 (64-pin
* MCU). */
board_id = BRDREV_Gotek_standard;
if (mcu_package == MCU_QFN32) {
/* The sole QFN32 board is a KC30 Rev 1 design. */
has_kc30_header = 1;
pa_skip &= ~(1<<10); /* PA10 is not used as serial rx */
pb_skip |= 1<<1; /* PB1 is a floppy input (WGATE) */
} else {
/* 48-pin package has PC12 permanently LOW. */
if (!(id & 1))
mcu_package = MCU_LQFP48;
/* Check for KC30 Rev 2. */
gpio_configure_pin(gpioc, 15, GPI_pull_down);
delay_us(100);
if (gpio_read_pin(gpioc, 15) == HIGH) {
/* KC30 Rev 2. */
has_kc30_header = 2;
pb_skip |= 1<<12; /* PB12 is a floppy input (MOTOR) */
} else {
/* If PF7 is floating then we are running on a board with the
* optional rotary-encoder header (SFRKC30 Rev 1). On earlier
* boards PF6=VSS and PF7=VDD, hence we take care here. */
#if MCU == STM32F105 /* Only AT32F415 has the PF7 pin. */
rcc->apb2enr |= RCC_APB2ENR_IOPFEN;
gpio_configure_pin(gpiof, 7, GPI_pull_down);
delay_us(100);
if (gpio_read_pin(gpiof, 7) == LOW) {
/* KC30 Rev 1. */
has_kc30_header = 1;
}
gpio_configure_pin(gpiof, 7, GPI_floating);
#endif
}
}
if (has_kc30_header)
gpio_configure_pin(kc30_sel_gpio, kc30_sel_pin, GPI_pull_up);
} else {
switch (board_id = id) {
case BRDREV_Gotek_standard:
break;
case BRDREV_Gotek_enhanced:
case BRDREV_Gotek_sd_card:
/* PA3,15 (floppy inputs), PA4 (USBENA). */
pa_skip |= 0x8018;
/* PA4: /USBENA */
gpio_configure_pin(gpioa, 4, GPO_pushpull(_2MHz, LOW));
break;
default:
ASSERT(0);
}
} }
gpio_pull_up_pins(gpioa, ~pa_skip); gpio_pull_up_pins(gpioa, ~pa_skip);

View file

@ -18,50 +18,66 @@
#define pin_step 1 /* PA1 */ #define pin_step 1 /* PA1 */
#define pin_sel0 0 /* PA0 */ #define pin_sel0 0 /* PA0 */
#define pin_sel1 3 /* PA3 */ #define pin_sel1 3 /* PA3 */
#define pin_wgate 9 /* PB9 */ static uint8_t pin_wgate = 9; /* PB9 */
#define pin_side 4 /* PB4 */ #define pin_side 4 /* PB4 */
#define pin_motor 15 /* PA15 or PB15 */ static uint8_t pin_motor = 15; /* PA15 or PB15 */
#define pin_chgrst 14 /* PA14 if CHGRST_pa14 */ #define pin_chgrst 14 /* PA14 if CHGRST_pa14 */
/* Output pins. */ /* Output pins. PBx = 0-15, PAx = 16-31. */
#define gpio_out gpiob static uint8_t pin_02 = 7; /* PB7 */
#define pin_02 7 #define pin_08 8 /* PB8 */
#define pin_08 8 static uint8_t pin_26 = 6; /* PB6 */
#define pin_26 6 #define pin_28 5 /* PB5 */
#define pin_28 5 #define pin_34 3 /* PB3 */
#define pin_34 3
#define gpio_data gpioa #define gpio_data gpioa
#define pin_wdata 8 #define pin_wdata 8
#define tim_wdata (tim1) #define tim_wdata (tim1)
#define dma_wdata (dma1->ch2) #define dma_wdata (dma1->ch[2-1])
#define dma_wdata_ch 2 #define dma_wdata_ch 2
#define dma_wdata_irq 12 #define dma_wdata_irq DMA1_CH2_IRQ
void IRQ_12(void) __attribute__((alias("IRQ_wdata_dma"))); DEFINE_IRQ(dma_wdata_irq, "IRQ_wdata_dma");
#define pin_rdata 7 #define pin_rdata 7
#define tim_rdata (tim3) #define tim_rdata (tim3)
#define dma_rdata (dma1->ch3) #define dma_rdata (dma1->ch[3-1])
#define dma_rdata_ch 3 #define dma_rdata_ch 3
#define dma_rdata_irq 13 #define dma_rdata_irq DMA1_CH3_IRQ
void IRQ_13(void) __attribute__((alias("IRQ_rdata_dma"))); DEFINE_IRQ(dma_rdata_irq, "IRQ_rdata_dma");
/* EXTI IRQs. */ /* EXTI IRQs. */
/*void IRQ_6(void) __attribute__((alias("IRQ_SELA_changed")));*/ /* EXTI0 */ void IRQ_6(void) __attribute__((alias("IRQ_SELA_changed"))); /* EXTI0 */
void IRQ_7(void) __attribute__((alias("IRQ_STEP_changed"))); /* EXTI1 */ void IRQ_7(void) __attribute__((alias("IRQ_WGATE_rotary"))); /* EXTI1 */
void IRQ_10(void) __attribute__((alias("IRQ_SIDE_changed"))); /* EXTI4 */ void IRQ_10(void) __attribute__((alias("IRQ_SIDE_changed"))); /* EXTI4 */
void IRQ_23(void) __attribute__((alias("IRQ_WGATE_changed"))); /* EXTI9_5 */ void IRQ_23(void) __attribute__((alias("IRQ_WGATE_rotary"))); /* EXTI9_5 */
void IRQ_40(void) __attribute__((alias("IRQ_MOTOR_CHGRST"))); /* EXTI15_10 */ void IRQ_28(void) __attribute__((alias("IRQ_STEP_changed"))); /* TMR2 */
void IRQ_40(void) __attribute__((alias("IRQ_MOTOR_CHGRST_rotary"))); /* EXTI15_10 */
#define MOTOR_CHGRST_IRQ 40 #define MOTOR_CHGRST_IRQ 40
static const struct exti_irq exti_irqs[] = { static const struct exti_irq exti_irqs[] = {
/* SELA */ { 6, FLOPPY_IRQ_SEL_PRI, 0 }, /* SELA */ { 6, FLOPPY_IRQ_SEL_PRI, 0 },
/* STEP */ { 7, FLOPPY_IRQ_STEP_PRI, m(pin_step) }, /* STEP */ { 28, FLOPPY_IRQ_STEP_PRI, m(2) /* dummy */ },
/* WGATE */ { 7, FLOPPY_IRQ_WGATE_PRI, 0 },
/* SIDE */ { 10, TIMER_IRQ_PRI, 0 }, /* SIDE */ { 10, TIMER_IRQ_PRI, 0 },
/* WGATE */ { 23, FLOPPY_IRQ_WGATE_PRI, 0 }, /* WGATE */ { 23, FLOPPY_IRQ_WGATE_PRI, 0 },
/* MTR/CHGRST */ { 40, TIMER_IRQ_PRI, 0 } /* MTR/CHGRST */ { 40, TIMER_IRQ_PRI, 0 }
}; };
/* Subset of output pins which are active (O_TRUE). */
extern uint32_t gpio_out_active;
/* Abuse gpio_out_active:PA11 to indicate that read DMA is active. This is
* safe because PA11 is configured for USB, so GPIO level has no effect.
* This saves some memory loads in the critical SELA IRQ handler. */
#define GPIO_OUT_DMA_RD_ACTIVE (16+11)
/* GPIO register to either assert or deassert active output pins. */
extern uint32_t gpiob_setreset;
/* This bitband address is used to atomically update GPIO_OUT_DMA_RD_ACTIVE */
static volatile uint32_t *p_dma_rd_active;
#define dma_rd_set_active(x) (*p_dma_rd_active = (x))
bool_t floppy_ribbon_is_reversed(void) bool_t floppy_ribbon_is_reversed(void)
{ {
time_t t_start = time_now(); time_t t_start = time_now();
@ -79,37 +95,89 @@ bool_t floppy_ribbon_is_reversed(void)
return FALSE; return FALSE;
} }
static uint32_t *get_bitband(void *ram_addr, unsigned int bit)
{
uint32_t byte = (uint32_t)ram_addr - 0x20000000u;
return (uint32_t *)(0x22000000u + (byte * 32) + (bit * 4));
}
static void board_floppy_init(void) static void board_floppy_init(void)
{ {
uint32_t pins; p_dma_rd_active = get_bitband(&gpio_out_active, GPIO_OUT_DMA_RD_ACTIVE);
#if MCU == STM32F105
#define change_pin_mode(gpio, pin, mode) \
gpio->crl = (gpio->crl & ~(0xfu<<((pin)<<2))) \
| (((mode)&0xfu)<<((pin)<<2))
gpio_configure_pin(gpioa, pin_step, GPI_bus);
gpio_configure_pin(gpio_data, pin_wdata, GPI_bus);
gpio_configure_pin(gpio_data, pin_rdata, GPO_bus);
#elif MCU == AT32F435
#define change_pin_mode(gpio, pin, mode) \
gpio->moder = (gpio->moder & ~(0x3u<<((pin)<<1))) \
| (((mode)&0x3u)<<((pin)<<1));
#define afio syscfg
gpio_set_af(gpioa, pin_step, 1);
gpio_configure_pin(gpioa, pin_step, AFI(PUPD_none));
gpio_set_af(gpio_data, pin_wdata, 1);
gpio_configure_pin(gpio_data, pin_wdata, AFI(PUPD_none));
gpio_set_af(gpio_data, pin_rdata, 2);
gpio_configure_pin(gpio_data, pin_rdata, GPO_bus);
dmamux1->cctrl[dma_wdata_ch-1] = DMAMUX_CCTRL_REQSEL(DMAMUX_REQ_TIM1_CH1);
dmamux1->cctrl[dma_rdata_ch-1] = DMAMUX_CCTRL_REQSEL(DMAMUX_REQ_TIM3_OVF);
#endif
/* PA1 (STEP) triggers IRQ via TIM2 Channel 2, since EXTI is used for
* WGATE on PB1. */
tim2->ccmr1 = TIM_CCMR1_CC2S(TIM_CCS_INPUT_TI1);
tim2->ccer = TIM_CCER_CC2E;
tim2->dier = TIM_DIER_CC2IE;
tim2->cr1 = TIM_CR1_CEN;
if (mcu_package == MCU_QFN32) {
pin_02 = 16 + 14; /* PA14 */
pin_26 = 16 + 13; /* PA13 */
pin_wgate = 1; /* PB1 */
}
gpio_configure_pin(gpiob, pin_dir, GPI_bus); gpio_configure_pin(gpiob, pin_dir, GPI_bus);
gpio_configure_pin(gpioa, pin_step, GPI_bus);
gpio_configure_pin(gpioa, pin_sel0, GPI_bus); gpio_configure_pin(gpioa, pin_sel0, GPI_bus);
gpio_configure_pin(gpiob, pin_wgate, GPI_bus); gpio_configure_pin(gpiob, pin_wgate, GPI_bus);
gpio_configure_pin(gpiob, pin_side, GPI_bus); gpio_configure_pin(gpiob, pin_side, GPI_bus);
/* PA[15:14], PB[13:12], PC[11:10], PB[9:2], PA[1:0] */ /* PA[15:13], PB[12], PC[11:10], PB[9:1], PA[0] */
afio->exticr4 = 0x0011; afio->exticr[4-1] = 0x0001;
afio->exticr3 = 0x2211; afio->exticr[3-1] = 0x2211;
afio->exticr2 = 0x1111; afio->exticr[2-1] = 0x1111;
afio->exticr1 = 0x1100; afio->exticr[1-1] = 0x1110;
if (gotek_enhanced()) { if (gotek_enhanced()) {
gpio_configure_pin(gpioa, pin_sel1, GPI_bus); gpio_configure_pin(gpioa, pin_sel1, GPI_bus);
gpio_configure_pin(gpioa, pin_motor, GPI_bus); gpio_configure_pin(gpioa, pin_motor, GPI_bus);
} else if (has_kc30_header == 2) {
pin_motor = 12; /* PB12 */
} else { } else {
/* This gives us "motor always on" if the pin is not connected. /* This gives us "motor always on" if the pin is not connected.
* It is safe enough to pull down even if connected direct to 5v, * It is safe enough to pull down even if connected direct to 5v,
* will only sink ~0.15mA via the weak internal pulldown. */ * will only sink ~0.15mA via the weak internal pulldown. */
gpio_configure_pin(gpiob, pin_motor, GPI_pull_down); gpio_configure_pin(gpiob, pin_motor, GPI_pull_down);
afio->exticr4 = 0x1011; /* Motor = PB15 */ exti_route_pb(15); /* Motor = PB15 */
} }
pins = m(pin_wgate) | m(pin_side) | m(pin_sel0); exti->rtsr = 0xffff;
exti->rtsr = pins | m(pin_motor) | m(pin_step) | m(11) | m(10); exti->ftsr = 0xffff;
exti->ftsr = pins | m(pin_motor) | m(pin_chgrst) | m(11) | m(10); exti->imr = m(pin_wgate) | m(pin_side) | m(pin_sel0);
exti->imr = pins | m(pin_step);
gpiob_setreset = (uint32_t)&gpiob->bsrr;
} }
/* Fast speculative entry point for SELA-changed IRQ. We assume SELA has /* Fast speculative entry point for SELA-changed IRQ. We assume SELA has
@ -118,40 +186,32 @@ static void board_floppy_init(void)
* Note that the entirety of the SELA handler is in SRAM (.data) -- not only * Note that the entirety of the SELA handler is in SRAM (.data) -- not only
* is this faster to execute, but allows us to co-locate gpio_out_active for * is this faster to execute, but allows us to co-locate gpio_out_active for
* even faster access in the time-critical speculative entry point. */ * even faster access in the time-critical speculative entry point. */
void IRQ_SELA_changed(void); __attribute__((naked)) __attribute__((section(".ramfuncs"))) aligned(4)
asm ( void IRQ_SELA_changed(void) {
" .data\n" asm (
" .align 4\n" ".global gpio_out_active, gpiob_setreset\n"
" .thumb_func\n" " ldr r0, [pc, #8]\n" /* r0 = gpio_out_active */
" .type IRQ_SELA_changed,%function\n" " ldr r1, [pc, #12]\n" /* r1 = &gpiob->b[s]rr */
"IRQ_SELA_changed:\n" " uxth r2, r0\n" /* r2 = (uint16_t)gpio_out_active */
" ldr r0, [pc, #4]\n" /* r0 = gpio_out_active */ " str r2, [r1, #0]\n" /* gpiob->b[s]rr = gpio_out_active */
" ldr r1, [pc, #8]\n" /* r1 = &gpio_out->b[s]rr */ " b.n _IRQ_SELA_changed\n" /* branch to the main ISR entry point */
" str r0, [r1, #0]\n" /* gpio_out->b[s]rr = gpio_out_active */ " nop\n"
" b.n _IRQ_SELA_changed\n" /* branch to the main ISR entry point */ "gpio_out_active: .word 0\n"
"gpio_out_active: .word 0\n" "gpiob_setreset: .word 0\n" /* gpiob->b[s]rr */
"gpio_out_setreset: .word 0x40010c10\n" /* gpio_out->b[s]rr */ );
" .global IRQ_6\n" }
" .thumb_set IRQ_6,IRQ_SELA_changed\n"
" .previous\n"
);
/* Subset of output pins which are active (O_TRUE). */ static void Amiga_HD_ID(uint32_t _gpio_out_active, uint32_t _gpiob_setreset)
extern uint32_t gpio_out_active; __attribute__((used)) __attribute__((section(".ramfuncs")));
/* GPIO register to either assert or deassert active output pins. */
extern uint32_t gpio_out_setreset;
static void Amiga_HD_ID(uint32_t _gpio_out_active, uint32_t _gpio_out_setreset)
__attribute__((used)) __attribute__((section(".data@")));
static void _IRQ_SELA_changed(uint32_t _gpio_out_active) static void _IRQ_SELA_changed(uint32_t _gpio_out_active)
__attribute__((used)) __attribute__((section(".data@"))); __attribute__((used)) __attribute__((section(".ramfuncs")))
__attribute__((optimize("Ofast")));
/* Intermediate SELA-changed handler for generating the Amiga HD RDY signal. */ /* Intermediate SELA-changed handler for generating the Amiga HD RDY signal. */
static void Amiga_HD_ID(uint32_t _gpio_out_active, uint32_t _gpio_out_setreset) static void Amiga_HD_ID(uint32_t _gpio_out_active, uint32_t _gpiob_setreset)
{ {
/* If deasserting the bus, toggle pin 34 for next time we take the bus. */ /* If deasserting the bus, toggle pin 34 for next time we take the bus. */
if (!(_gpio_out_setreset & 4)) if ((uint8_t)_gpiob_setreset == (uint8_t)(uint32_t)&gpiob->bsrr)
gpio_out_active ^= m(pin_34); gpio_out_active ^= m(pin_34);
/* Continue to the main SELA-changed IRQ entry point. */ /* Continue to the main SELA-changed IRQ entry point. */
@ -163,42 +223,37 @@ static void Amiga_HD_ID(uint32_t _gpio_out_active, uint32_t _gpio_out_setreset)
* speculative entry point for the next interrupt. */ * speculative entry point for the next interrupt. */
static void _IRQ_SELA_changed(uint32_t _gpio_out_active) static void _IRQ_SELA_changed(uint32_t _gpio_out_active)
{ {
/* Clear SELA-changed flag. */ /* Latch SELA. */
exti->pr = m(pin_sel0); exti->pr = m(pin_sel0);
drive.sel = !(gpioa->idr & m(pin_sel0));
if (!(gpioa->idr & m(pin_sel0))) { if (drive.sel) {
/* SELA is asserted (this drive is selected). /* SELA is asserted (this drive is selected).
* Immediately re-enable all our asserted outputs. */ * Immediately re-enable all our asserted outputs. */
gpio_out->brr = _gpio_out_active; gpiob->brr = _gpio_out_active & 0xffff;
gpioa->brr = _gpio_out_active >> 16;
/* Set pin_rdata as timer output (AFO_bus). */ /* Set pin_rdata as timer output (AFO_bus). */
if (dma_rd && (dma_rd->state == DMA_active)) if (_gpio_out_active & m(GPIO_OUT_DMA_RD_ACTIVE))
gpio_data->crl = (gpio_data->crl & ~(0xfu<<(pin_rdata<<2))) change_pin_mode(gpio_data, pin_rdata, AFO_bus);
| ((AFO_bus&0xfu)<<(pin_rdata<<2)); /* Speculate that, on next interrupt, SELA is deasserted. */
/* Let main code know it can drive the bus until further notice. */ *(uint8_t *)&gpiob_setreset = (uint8_t)(uint32_t)&gpiob->bsrr;
drive.sel = 1;
} else { } else {
/* SELA is deasserted (this drive is not selected). /* SELA is deasserted (this drive is not selected).
* Relinquish the bus by disabling all our asserted outputs. */ * Relinquish the bus by disabling all our asserted outputs. */
gpio_out->bsrr = _gpio_out_active; gpiob->bsrr = _gpio_out_active & 0xffff;
gpioa->bsrr = _gpio_out_active >> 16;
/* Set pin_rdata as quiescent (GPO_bus). */ /* Set pin_rdata as quiescent (GPO_bus). */
if (dma_rd && (dma_rd->state == DMA_active)) change_pin_mode(gpio_data, pin_rdata, GPO_bus);
gpio_data->crl = (gpio_data->crl & ~(0xfu<<(pin_rdata<<2))) /* Speculate that, on next interrupt, SELA is asserted. */
| ((GPO_bus&0xfu)<<(pin_rdata<<2)); *(uint8_t *)&gpiob_setreset = (uint8_t)(uint32_t)&gpiob->brr;
/* Tell main code to leave the bus alone. */
drive.sel = 0;
} }
/* Set up the speculative fast path for the next interrupt. */
if (drive.sel)
gpio_out_setreset &= ~4; /* gpio_out->bsrr */
else
gpio_out_setreset |= 4; /* gpio_out->brr */
} }
/* Update the SELA handler. Used for switching in the Amiga HD-ID "magic". /* Update the SELA handler. Used for switching in the Amiga HD-ID "magic".
* Must be called with interrupts disabled. */ * Must be called with interrupts disabled. */
static void update_SELA_irq(bool_t amiga_hd_id) static void update_SELA_irq(bool_t amiga_hd_id)
{ {
#define OFF 4
uint32_t handler = amiga_hd_id ? (uint32_t)Amiga_HD_ID uint32_t handler = amiga_hd_id ? (uint32_t)Amiga_HD_ID
: (uint32_t)_IRQ_SELA_changed; : (uint32_t)_IRQ_SELA_changed;
uint32_t entry = (uint32_t)IRQ_SELA_changed; uint32_t entry = (uint32_t)IRQ_SELA_changed;
@ -209,14 +264,15 @@ static void update_SELA_irq(bool_t amiga_hd_id)
entry &= ~1; entry &= ~1;
/* Create a new tail-call instruction for the entry stub. */ /* Create a new tail-call instruction for the entry stub. */
opcode = handler - (entry + 6 + 4); opcode = handler - (entry + OFF*2 + 4);
opcode = 0xe000 | (opcode >> 1); opcode = 0xe000 | (opcode >> 1);
/* If the tail-call instruction has changed, modify the entry stub. */ /* If the tail-call instruction has changed, modify the entry stub. */
if (unlikely(((uint16_t *)entry)[3] != opcode)) { if (unlikely(((uint16_t *)entry)[OFF] != opcode)) {
((uint16_t *)entry)[3] = opcode; ((uint16_t *)entry)[OFF] = opcode;
cpu_sync(); /* synchronise self-modifying code */ cpu_sync(); /* synchronise self-modifying code */
} }
#undef OFF
} }
static bool_t drive_is_writing(void) static bool_t drive_is_writing(void)
@ -241,7 +297,7 @@ static void IRQ_STEP_changed(void)
idr_b = gpiob->idr; idr_b = gpiob->idr;
/* Clear STEP-changed flag. */ /* Clear STEP-changed flag. */
exti->pr = m(pin_step); (void)tim2->ccr2;
/* Bail if drive not selected. */ /* Bail if drive not selected. */
if (idr_a & m(pin_sel0)) if (idr_a & m(pin_sel0))
@ -259,7 +315,7 @@ static void IRQ_STEP_changed(void)
/* Latch the step direction and check bounds (0 <= cyl <= 255). */ /* Latch the step direction and check bounds (0 <= cyl <= 255). */
drv->step.inward = !(idr_b & m(pin_dir)); drv->step.inward = !(idr_b & m(pin_dir));
if (drv->cyl == (drv->step.inward ? 255 : 0)) if (drv->cyl == (drv->step.inward ? ff_cfg.max_cyl : 0))
return; return;
/* Valid step request for this drive: start the step operation. */ /* Valid step request for this drive: start the step operation. */
@ -269,7 +325,8 @@ static void IRQ_STEP_changed(void)
drive_change_output(drv, outp_trk0, FALSE); drive_change_output(drv, outp_trk0, FALSE);
if (dma_rd != NULL) { if (dma_rd != NULL) {
rdata_stop(); 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. */ /* Opportunistically insert an INDEX pulse ahead of seek op. */
drive_change_output(drv, outp_index, TRUE); drive_change_output(drv, outp_index, TRUE);
index.fake_fired = TRUE; index.fake_fired = TRUE;
@ -304,13 +361,10 @@ static void IRQ_SIDE_changed(void)
rdata_stop(); rdata_stop();
} }
static void IRQ_WGATE_changed(void) static void IRQ_WGATE(void)
{ {
struct drive *drv = &drive; struct drive *drv = &drive;
/* Clear WGATE-changed flag. */
exti->pr = m(pin_wgate);
/* If WRPROT line is asserted then we ignore WGATE. */ /* If WRPROT line is asserted then we ignore WGATE. */
if (drv->outp & m(outp_wrprot)) if (drv->outp & m(outp_wrprot))
return; return;
@ -324,9 +378,29 @@ static void IRQ_WGATE_changed(void)
} }
} }
static void IRQ_WGATE_rotary(void)
{
uint32_t rot_mask = board_rotary_exti_mask, pr = exti->pr;
/* Latch and clear PR[9:5] and PR[1]. */
exti->pr = pr & 0x03e2;
if (pr & m(pin_wgate))
IRQ_WGATE();
if (pr & rot_mask)
IRQ_rotary();
}
static void IRQ_MOTOR(struct drive *drv) static void IRQ_MOTOR(struct drive *drv)
{ {
GPIO gpio = gotek_enhanced() ? gpioa : gpiob; GPIO gpio = gotek_enhanced() ? gpioa : gpiob;
bool_t mtr_asserted = !(gpio->idr & m(pin_motor));
if (drv->amiga_pin34 && (ff_cfg.motor_delay != MOTOR_ignore)) {
IRQ_global_disable();
drive_change_pin(drv, pin_34, !mtr_asserted);
}
timer_cancel(&drv->motor.timer); timer_cancel(&drv->motor.timer);
drv->motor.on = FALSE; drv->motor.on = FALSE;
@ -338,7 +412,7 @@ static void IRQ_MOTOR(struct drive *drv)
/* Motor signal ignored -- MOTOR ON */ /* Motor signal ignored -- MOTOR ON */
drv->motor.on = TRUE; drv->motor.on = TRUE;
drive_change_output(drv, outp_rdy, TRUE); drive_change_output(drv, outp_rdy, TRUE);
} else if (gpio->idr & m(pin_motor)) { } else if (!mtr_asserted) {
/* Motor signal off -- MOTOR OFF */ /* Motor signal off -- MOTOR OFF */
drive_change_output(drv, outp_rdy, FALSE); drive_change_output(drv, outp_rdy, FALSE);
} else { } else {
@ -357,22 +431,26 @@ static void IRQ_CHGRST(struct drive *drv)
} }
} }
static void IRQ_MOTOR_CHGRST(void) static void IRQ_MOTOR_CHGRST_rotary(void)
{ {
struct drive *drv = &drive; struct drive *drv = &drive;
bool_t changed = drv->motor.changed; bool_t changed = drv->motor.changed;
uint32_t pr = exti->pr; uint32_t rot_mask = board_rotary_exti_mask, pr = exti->pr;
drv->motor.changed = FALSE; drv->motor.changed = FALSE;
exti->pr = m(pin_motor) | m(pin_chgrst) | m(11) | m(10);
if ((pr & m(pin_motor)) || changed) /* Latch and clear PR[15:10] */
exti->pr = pr & 0xfc00;
if (((pr & m(pin_motor)) && (ff_cfg.motor_delay != MOTOR_ignore))
|| changed)
IRQ_MOTOR(drv); IRQ_MOTOR(drv);
if ((pr & m(pin_chgrst)) || changed) if ((pr & m(pin_chgrst))
|| changed)
IRQ_CHGRST(drv); IRQ_CHGRST(drv);
if (pr & (m(11) | m(10))) if (pr & rot_mask)
IRQ_rotary(); IRQ_rotary();
} }
@ -383,24 +461,25 @@ static void motor_chgrst_update_status(struct drive *drv)
IRQx_set_pending(MOTOR_CHGRST_IRQ); IRQx_set_pending(MOTOR_CHGRST_IRQ);
} }
static void motor_chgrst_insert(struct drive *drv) uint32_t motor_chgrst_exti_mask;
void motor_chgrst_setup_exti(void)
{ {
uint32_t imr = exti->imr; uint32_t m = 0;
if (ff_cfg.motor_delay != MOTOR_ignore) if (ff_cfg.motor_delay != MOTOR_ignore) {
imr |= m(pin_motor); _exti_route(gotek_enhanced()?0/*PA*/:1/*PB*/, pin_motor);
m |= m(pin_motor);
}
if (ff_cfg.chgrst == CHGRST_pa14) if (ff_cfg.chgrst == CHGRST_pa14) {
imr |= m(pin_chgrst); exti_route_pa(pin_chgrst);
m |= m(pin_chgrst);
}
exti->imr = imr; motor_chgrst_exti_mask = m;
motor_chgrst_update_status(drv); exti->imr |= m;
}
static void motor_chgrst_eject(struct drive *drv) motor_chgrst_update_status(&drive);
{
exti->imr &= ~(m(pin_motor) | m(pin_chgrst));
motor_chgrst_update_status(drv);
} }
/* /*

View file

@ -15,15 +15,14 @@
/* Input pins: All are level signals. */ /* Input pins: All are level signals. */
#define pin_reset 1 /* PA1, /RS: LOW = Reset asserted */ #define pin_reset 1 /* PA1, /RS: LOW = Reset asserted */
#define pin_motor 0 /* PA0, /MO: LOW = Motor on */ #define pin_motor 0 /* PA0, /MO: LOW = Motor on */
#define pin_wgate 9 /* PB9, WG: HIGH = Write active */ static uint8_t pin_wgate = 9; /* PB9, WG: HIGH = Write active */
/* Output pins: All are level signals. */ /* Output pins: All are level signals. PBx = 0-15, PAx = 16-31. */
#define gpio_out gpiob static uint8_t pin_02 = 7; /* PB7 */
#define pin_02 7 #define pin_08 8 /* PB8 */
#define pin_08 8 static uint8_t pin_26 = 6; /* PB6 */
#define pin_26 6 #define pin_28 5 /* PB5 */
#define pin_28 5 #define pin_34 3 /* PB3 */
#define pin_34 3
#define pin_media pin_02 /* /MS: LOW = Media present */ #define pin_media pin_02 /* /MS: LOW = Media present */
#define pin_wrprot pin_28 /* /WP: LOW = Media present and writeable */ #define pin_wrprot pin_28 /* /WP: LOW = Media present and writeable */
#define pin_ready pin_34 /* /RY: LOW = Read/write window active */ #define pin_ready pin_34 /* /RY: LOW = Read/write window active */
@ -33,32 +32,32 @@
#define pin_wdata 8 /* /WD: Negative pulse signal */ #define pin_wdata 8 /* /WD: Negative pulse signal */
#define tim_wdata (tim1) #define tim_wdata (tim1)
#define dma_wdata (dma1->ch2) #define dma_wdata (dma1->ch[2-1])
#define dma_wdata_ch 2 #define dma_wdata_ch 2
#define dma_wdata_irq 12 #define dma_wdata_irq DMA1_CH2_IRQ
void IRQ_12(void) __attribute__((alias("IRQ_wdata_dma"))); DEFINE_IRQ(dma_wdata_irq, "IRQ_wdata_dma");
#define pin_rdata 7 /* RD: Positive pulse signal */ #define pin_rdata 7 /* RD: Positive pulse signal */
#define tim_rdata (tim3) #define tim_rdata (tim3)
#define dma_rdata (dma1->ch3) #define dma_rdata (dma1->ch[3-1])
#define dma_rdata_ch 3 #define dma_rdata_ch 3
#define dma_rdata_irq 13 #define dma_rdata_irq DMA1_CH3_IRQ
void IRQ_13(void) __attribute__((alias("IRQ_rdata_dma"))); DEFINE_IRQ(dma_rdata_irq, "IRQ_rdata_dma");
/* EXTI IRQs. */ /* EXTI IRQs. */
#define motor_irq 6 #define motor_irq 6
#define reset_irq 7
#define wgate_irq 23 #define wgate_irq 23
#define rotary_irq 40
void IRQ_6(void) __attribute__((alias("IRQ_MOTOR_changed"))); /* EXTI0 */ void IRQ_6(void) __attribute__((alias("IRQ_MOTOR_changed"))); /* EXTI0 */
void IRQ_7(void) __attribute__((alias("IRQ_MOTOR_changed"))); /* EXTI1 */ void IRQ_7(void) __attribute__((alias("IRQ_WGATE_rotary"))); /* EXTI1 */
void IRQ_23(void) __attribute__((alias("IRQ_WGATE_changed"))); /* EXTI9_5 */ void IRQ_23(void) __attribute__((alias("IRQ_WGATE_rotary"))); /* EXTI9_5 */
void IRQ_40(void) __attribute__((alias("_IRQ_rotary"))); /* EXTI15_10 */ void IRQ_28(void) __attribute__((alias("IRQ_RESET_changed"))); /* TMR2 */
void IRQ_40(void) __attribute__((alias("IRQ_rotary_changed"))); /* EXTI15_10 */
static const struct exti_irq exti_irqs[] = { static const struct exti_irq exti_irqs[] = {
{ motor_irq, TIMER_IRQ_PRI, 0 }, /* MOTOR */ { 6, TIMER_IRQ_PRI, 0 },
{ reset_irq, TIMER_IRQ_PRI, 0 }, /* WGATE */ { 7, FLOPPY_IRQ_WGATE_PRI, 0 },
{ wgate_irq, FLOPPY_IRQ_WGATE_PRI, 0 }, /* WGATE */ { 23, FLOPPY_IRQ_WGATE_PRI, 0 },
{ rotary_irq, TIMER_IRQ_PRI, 0 } /* RESET */ { 28, TIMER_IRQ_PRI, 0 },
/* Rotary */ { 40, TIMER_IRQ_PRI, 0 }
}; };
bool_t floppy_ribbon_is_reversed(void) bool_t floppy_ribbon_is_reversed(void)
@ -68,25 +67,59 @@ bool_t floppy_ribbon_is_reversed(void)
static void board_floppy_init(void) static void board_floppy_init(void)
{ {
uint32_t pins; #if MCU == STM32F105
/* PA[15:14], PB[13:12], PC[11:10], PB[9:2], PA[1:0] */ gpio_configure_pin(gpioa, pin_reset, GPI_bus);
afio->exticr4 = 0x0011; gpio_configure_pin(gpio_data, pin_wdata, GPI_bus);
afio->exticr3 = 0x2211; gpio_configure_pin(gpio_data, pin_rdata, GPO_rdata);
afio->exticr2 = 0x1111;
afio->exticr1 = 0x1100;
pins = m(pin_wgate) | m(pin_reset) | m(pin_motor); #elif MCU == AT32F435
exti->rtsr = pins | m(11) | m(10);
exti->ftsr = pins | m(11) | m(10); #define afio syscfg
exti->imr = pins;
gpio_set_af(gpioa, pin_reset, 1);
gpio_configure_pin(gpioa, pin_reset, AFI(PUPD_none));
gpio_set_af(gpio_data, pin_wdata, 1);
gpio_configure_pin(gpio_data, pin_wdata, AFI(PUPD_none));
gpio_set_af(gpio_data, pin_rdata, 2);
gpio_configure_pin(gpio_data, pin_rdata, GPO_rdata);
dmamux1->cctrl[dma_wdata_ch-1] = DMAMUX_CCTRL_REQSEL(DMAMUX_REQ_TIM1_CH1);
dmamux1->cctrl[dma_rdata_ch-1] = DMAMUX_CCTRL_REQSEL(DMAMUX_REQ_TIM3_OVF);
#endif
/* PA1 (RESET) triggers IRQ via TIM2 Channel 2, since EXTI is used for
* WGATE on PB1. */
tim2->ccmr1 = TIM_CCMR1_CC2S(TIM_CCS_INPUT_TI1);
tim2->ccer = TIM_CCER_CC2E;
tim2->dier = TIM_DIER_CC2IE;
tim2->cr1 = TIM_CR1_CEN;
if (mcu_package == MCU_QFN32) {
pin_02 = 16 + 14; /* PA14 */
pin_26 = 16 + 13; /* PA13 */
pin_wgate = 1; /* PB1 */
}
gpio_configure_pin(gpioa, pin_motor, GPI_bus);
gpio_configure_pin(gpiob, pin_wgate, GPI_bus);
/* PA[15:14], PB[13:12], PC[11:10], PB[9:1], PA[0] */
afio->exticr[4-1] = 0x0011;
afio->exticr[3-1] = 0x2211;
afio->exticr[2-1] = 0x1111;
afio->exticr[1-1] = 0x1110;
exti->rtsr = 0xffff;
exti->ftsr = 0xffff;
exti->imr = m(pin_wgate) | m(pin_motor);
} }
static void IRQ_WGATE_changed(void) static void IRQ_WGATE(void)
{ {
/* Clear WGATE-changed flag. */
exti->pr = m(pin_wgate);
/* If WRPROT line is asserted then we ignore WGATE. */ /* If WRPROT line is asserted then we ignore WGATE. */
if (read_pin(wrprot)) if (read_pin(wrprot))
return; return;
@ -106,16 +139,39 @@ static void IRQ_WGATE_changed(void)
} }
} }
static void IRQ_MOTOR_changed(void) static void IRQ_WGATE_rotary(void)
{ {
const uint16_t mask = m(pin_reset) | m(pin_motor); uint32_t rot_mask = board_rotary_exti_mask, pr = exti->pr;
uint8_t off;
/* Clear MOTOR- and RESET-changed flags. */ /* Latch and clear PR[9:5] and PR[1]. */
exti->pr = mask; exti->pr = pr & 0x03e2;
if (pr & m(pin_wgate))
IRQ_WGATE();
if (pr & rot_mask)
IRQ_rotary();
}
static bool_t qd_roland_mode(void)
{
if (board_jc_strapped())
return TRUE;
/* FF.CFG alternative to setting the physical JC strap. */
if (ff_cfg.interface == FINTF_IBMPC)
return TRUE;
return FALSE;
}
static void _IRQ_MOTOR_RESET_changed(unsigned int gpioa_idr)
{
const unsigned int mask = m(pin_reset) | m(pin_motor);
unsigned int off;
/* Motor is off if either /RESET low or /MOTOR high. */ /* Motor is off if either /RESET low or /MOTOR high. */
off = gpioa->idr & mask; off = gpioa_idr & mask;
off ^= m(pin_reset); off ^= m(pin_reset);
/* /RESET is forced by media removal. */ /* /RESET is forced by media removal. */
@ -137,20 +193,62 @@ static void IRQ_MOTOR_changed(void)
if (/* RESET immediately clears READY */ if (/* RESET immediately clears READY */
(off & m(pin_reset)) (off & m(pin_reset))
/* !MOTOR immediately clears READY iff Jumper JC is strapped */ /* !MOTOR immediately clears READY iff Roland mode is selected */
|| ((off & m(pin_motor)) && !gpio_read_pin(gpiob, 1))) { || ((off & m(pin_motor)) && qd_roland_mode())) {
write_pin(ready, HIGH); write_pin(ready, HIGH);
} }
} }
} }
static void _IRQ_rotary(void) static void IRQ_MOTOR_changed(void)
{ {
exti->pr = m(11) | m(10); /* Clear MOTOR-changed flag. */
exti->pr = m(pin_motor);
_IRQ_MOTOR_RESET_changed(gpioa->idr);
}
static void IRQ_RESET_changed(void)
{
unsigned int gpioa_idr, gpioa_idr2 = gpioa->idr;
do {
/* Clear RESET-changed flag. */
(void)tim2->ccr2;
/* Execute MOTOR/RESET logic based on snapshotted pin state. */
gpioa_idr = gpioa_idr2;
_IRQ_MOTOR_RESET_changed(gpioa_idr);
/* Update the timer channel's edge detector to detect the next edge
* depending on snapshotted RESET pin state. */
if (gpioa_idr & m(pin_reset)) {
tim2->ccer |= TIM_CCER_CC2P; /* Falling edge */
} else {
tim2->ccer &= ~TIM_CCER_CC2P; /* Rising edge */
}
/* Now check if we raced a RESET edge. Loop if so. */
gpioa_idr2 = gpioa->idr;
} while ((gpioa_idr ^ gpioa_idr2) & m(pin_reset));
}
static void IRQ_rotary_changed(void)
{
/* Clear PR[15:10] */
exti->pr = 0xfc00;
IRQ_rotary(); IRQ_rotary();
} }
uint32_t motor_chgrst_exti_mask;
void motor_chgrst_setup_exti(void)
{
/* Quick Disk does not have configurable MOTOR and CHGRST. */
}
/* /*
* Local variables: * Local variables:
* mode: C * mode: C

View file

@ -56,7 +56,89 @@ void speaker_pulse(void)
now = time_now(); now = time_now();
pulse.state = STATE_active; pulse.state = STATE_active;
pulse.start = now; pulse.start = now;
timer_set(&pulse.timer, now + volume*volume*3); 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();
} }
/* /*

View file

@ -14,8 +14,6 @@
#define DD_TRACKLEN_BC 101376 /* multiple of 32 */ #define DD_TRACKLEN_BC 101376 /* multiple of 32 */
#define POST_IDX_GAP_BC 1024 #define POST_IDX_GAP_BC 1024
#define MAX_WR_BATCH 11
/* Shift even/odd bits into MFM data-bit positions */ /* Shift even/odd bits into MFM data-bit positions */
#define even(x) ((x)>>1) #define even(x) ((x)>>1)
#define odd(x) (x) #define odd(x) (x)
@ -39,7 +37,8 @@ static bool_t adf_open(struct image *im)
im->nr_sides = 2; im->nr_sides = 2;
im->adf.nr_secs = 11; im->adf.nr_secs = 11;
im->tracklen_bc = DD_TRACKLEN_BC; im->tracklen_bc = DD_TRACKLEN_BC;
im->ticks_per_cell = (sysclk_stk(im->stk_per_rev) * 16u) / im->tracklen_bc; im->ticks_per_cell = ((sampleclk_stk(im->stk_per_rev) * 16u)
/ im->tracklen_bc);
im->nr_cyls = f_size(&im->fp) / (2 * 11 * 512); im->nr_cyls = f_size(&im->fp) / (2 * 11 * 512);
@ -55,8 +54,9 @@ static bool_t adf_open(struct image *im)
- im->adf.nr_secs * 544 * 16 - im->adf.nr_secs * 544 * 16
- POST_IDX_GAP_BC); - POST_IDX_GAP_BC);
volume_cache_init(im->bufs.write_data.p + MAX_WR_BATCH * 512, im->adf.fcache = file_cache_init(&im->fp, 4,
im->bufs.write_data.p + im->bufs.write_data.len); im->bufs.write_data.p + 512,
im->bufs.write_data.p + im->bufs.write_data.len);
return TRUE; return TRUE;
} }
@ -66,19 +66,20 @@ static void adf_setup_track(
{ {
struct image_buf *rd = &im->bufs.read_data; struct image_buf *rd = &im->bufs.read_data;
struct image_buf *bc = &im->bufs.read_bc; struct image_buf *bc = &im->bufs.read_bc;
uint32_t decode_off, sector, sys_ticks = start_pos ? *start_pos : 0; uint32_t decode_off, sector, start_ticks = start_pos ? *start_pos : 0;
if ((im->cur_track ^ track) & ~1) { if ((im->cur_track ^ track) & ~1) {
/* New cylinder: Refresh the sector maps (ordered by sector #). */ /* New cylinder: Refresh the sector maps (ordered by sector #). */
unsigned int sect; unsigned int sect;
for (sect = 0; sect < im->adf.nr_secs; sect++) for (sect = 0; sect < im->adf.nr_secs; sect++)
im->adf.sec_map[0][sect] = im->adf.sec_map[1][sect] = sect; im->adf.sec_map[0][sect] = im->adf.sec_map[1][sect] = sect;
file_cache_sync_wait(im->adf.fcache);
} }
im->adf.trk_off = track * im->adf.nr_secs * 512; im->adf.trk_off = track * im->adf.nr_secs * 512;
im->cur_track = track; im->cur_track = track;
im->cur_bc = (sys_ticks * 16) / im->ticks_per_cell; im->cur_bc = (start_ticks * 16) / im->ticks_per_cell;
if (im->cur_bc >= im->tracklen_bc) if (im->cur_bc >= im->tracklen_bc)
im->cur_bc = 0; im->cur_bc = 0;
im->cur_ticks = im->cur_bc * im->ticks_per_cell; im->cur_ticks = im->cur_bc * im->ticks_per_cell;
@ -102,9 +103,12 @@ static void adf_setup_track(
bc->prod = bc->cons = 0; bc->prod = bc->cons = 0;
if (start_pos) { if (start_pos) {
image_read_track(im); file_cache_readahead(im->adf.fcache,
bc->cons = decode_off; track * im->adf.nr_secs * 512, im->adf.nr_secs * 512,
6*1024);
im->adf.trash_bc = decode_off;
} else { } else {
file_cache_readahead(im->adf.fcache, 0, 0, 0);
im->adf.sec_idx = 0; im->adf.sec_idx = 0;
im->adf.written_secs = 0; im->adf.written_secs = 0;
} }
@ -123,12 +127,15 @@ static bool_t adf_read_track(struct image *im)
if (rd->prod == rd->cons) { if (rd->prod == rd->cons) {
unsigned int sector = im->adf.sec_map[hd][im->adf.sec_idx]; unsigned int sector = im->adf.sec_map[hd][im->adf.sec_idx];
F_lseek(&im->fp, im->adf.trk_off + sector * sec_sz); if (!file_cache_try_read(im->adf.fcache,
F_read(&im->fp, buf, sec_sz, NULL); buf, im->adf.trk_off + sector * sec_sz, sec_sz))
return FALSE;
rd->prod++; rd->prod++;
im->adf.sec_idx++; im->adf.sec_idx++;
if (im->adf.sec_idx >= im->adf.nr_secs) if (im->adf.sec_idx >= im->adf.nr_secs)
im->adf.sec_idx = 0; im->adf.sec_idx = 0;
} else {
file_cache_progress(im->adf.fcache);
} }
/* Generate some MFM if there is space in the raw-bitcell ring buffer. */ /* Generate some MFM if there is space in the raw-bitcell ring buffer. */
@ -210,27 +217,18 @@ static bool_t adf_read_track(struct image *im)
} }
if (im->adf.trash_bc) {
int16_t to_consume =
min_t(uint16_t, (bc_p - bc_c)*16, im->adf.trash_bc);
im->adf.trash_bc -= to_consume;
bc->cons += to_consume;
}
im->adf.decode_pos++; im->adf.decode_pos++;
bc->prod = bc_p * 32; bc->prod = bc_p * 32;
return TRUE; return TRUE;
} }
static void write_batch(struct image *im, unsigned int sect, unsigned int nr)
{
uint32_t *wrbuf = im->bufs.write_data.p;
time_t t;
if (nr == 0)
return;
t = time_now();
printk("Write %u/%u-%u... ", im->cur_track, sect, sect+nr-1);
F_lseek(&im->fp, im->adf.trk_off + sect*512);
F_write(&im->fp, wrbuf, 512*nr, NULL);
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
}
static bool_t adf_write_track(struct image *im) static bool_t adf_write_track(struct image *im)
{ {
bool_t flush; bool_t flush;
@ -241,7 +239,7 @@ static bool_t adf_write_track(struct image *im)
uint32_t *w, *wrbuf = im->bufs.write_data.p; uint32_t *w, *wrbuf = im->bufs.write_data.p;
uint32_t c = wr->cons / 32, p = wr->prod / 32; uint32_t c = wr->cons / 32, p = wr->prod / 32;
uint32_t info, dsum, csum; uint32_t info, dsum, csum;
unsigned int i, sect, batch_sect, batch, max_batch; unsigned int i, sect;
unsigned int hd = im->cur_track & 1; unsigned int hd = im->cur_track & 1;
/* If we are processing final data then use the end index, rounded up. */ /* If we are processing final data then use the end index, rounded up. */
@ -250,14 +248,7 @@ static bool_t adf_write_track(struct image *im)
if (flush) if (flush)
p = (write->bc_end + 31) / 32; p = (write->bc_end + 31) / 32;
batch = batch_sect = 0;
max_batch = min_t(unsigned int,
im->bufs.write_data.len / 512,
MAX_WR_BATCH);
w = wrbuf;
while ((int16_t)(p - c) >= (542/2)) { while ((int16_t)(p - c) >= (542/2)) {
/* Scan for sync word. */ /* Scan for sync word. */
if (be32toh(buf[c++ & bufmask]) != 0x44894489) if (be32toh(buf[c++ & bufmask]) != 0x44894489)
continue; continue;
@ -286,19 +277,12 @@ static bool_t adf_write_track(struct image *im)
continue; continue;
} }
if (batch && ((sect != batch_sect + batch) || (batch >= max_batch))) {
ASSERT(batch <= max_batch);
write_batch(im, batch_sect, batch);
batch = 0;
w = wrbuf;
}
/* Data checksum. */ /* Data checksum. */
csum = (buf[c++ & bufmask] & 0x55555555) << 1; csum = (buf[c++ & bufmask] & 0x55555555) << 1;
csum |= buf[c++ & bufmask] & 0x55555555; csum |= buf[c++ & bufmask] & 0x55555555;
/* Data area. Decode to a write buffer and keep a running checksum. */ /* Data area. Decode to a write buffer and keep a running checksum. */
dsum = 0; w = wrbuf;
for (i = dsum = 0; i < 128; i++) { for (i = dsum = 0; i < 128; i++) {
uint32_t o = buf[(c + 128) & bufmask] & 0x55555555; uint32_t o = buf[(c + 128) & bufmask] & 0x55555555;
uint32_t e = buf[c++ & bufmask] & 0x55555555; uint32_t e = buf[c++ & bufmask] & 0x55555555;
@ -314,16 +298,20 @@ static bool_t adf_write_track(struct image *im)
continue; continue;
} }
file_cache_write(im->adf.fcache, wrbuf, im->adf.trk_off + sect*512, 512);
printk("Write %u/%u...\n", im->cur_track, sect);
/* All good: add to the write-out batch. */ /* All good: add to the write-out batch. */
if (!(im->adf.written_secs & (1u<<sect))) { if (!(im->adf.written_secs & (1u<<sect))) {
im->adf.written_secs |= 1u<<sect; im->adf.written_secs |= 1u<<sect;
im->adf.sec_map[hd][im->adf.sec_idx++] = sect; im->adf.sec_map[hd][im->adf.sec_idx++] = sect;
} }
if (batch++ == 0)
batch_sect = sect;
} }
write_batch(im, batch_sect, batch); if (flush)
file_cache_sync(im->adf.fcache);
else
file_cache_progress(im->adf.fcache);
if (flush && (im->adf.sec_idx != im->adf.nr_secs)) { if (flush && (im->adf.sec_idx != im->adf.nr_secs)) {
/* End of write: If not all sectors were correctly written, /* End of write: If not all sectors were correctly written,
@ -337,12 +325,19 @@ static bool_t adf_write_track(struct image *im)
return flush; return flush;
} }
static void adf_sync(struct image *im)
{
file_cache_sync_wait(im->adf.fcache);
file_cache_shutdown(im->adf.fcache);
}
const struct image_handler adf_image_handler = { const struct image_handler adf_image_handler = {
.open = adf_open, .open = adf_open,
.setup_track = adf_setup_track, .setup_track = adf_setup_track,
.read_track = adf_read_track, .read_track = adf_read_track,
.rdata_flux = bc_rdata_flux, .rdata_flux = bc_rdata_flux,
.write_track = adf_write_track, .write_track = adf_write_track,
.sync = adf_sync,
}; };
/* /*

View file

@ -37,25 +37,81 @@ static bool_t fm_write_track(struct image *im);
static bool_t mfm_read_track(struct image *im); static bool_t mfm_read_track(struct image *im);
static bool_t mfm_write_track(struct image *im); static bool_t mfm_write_track(struct image *im);
static void process_wdata(struct image *im, unsigned int sect, uint16_t crc); #define SYNCED 0
#define SYNC_NEEDED 1
#define SYNCING 2
static void process_wdata(
struct image *im, unsigned int sect, uint16_t crc, uint16_t crc_data);
static unsigned int enc_sec_sz(struct image *im) static unsigned int enc_sec_sz(struct image *im)
{ {
return im->da.idam_sz + im->da.dam_sz; return im->da.idam_sz + im->da.dam_sz;
} }
static void da_seek_track(struct image *im, uint16_t track) static void progress_write(struct image *im)
{
struct image_buf *wb = &im->da.write_buffer;
uint16_t idx, cnt;
LBA_t off;
thread_yield();
if (!F_async_isdone(im->da.io_op)) /* Possible read op */
return;
if (im->da.write_cnt) {
wb->cons += im->da.write_cnt;
im->da.write_cnt = 0;
}
if (wb->prod == wb->cons) {
if (im->da.sync_state == SYNCING)
im->da.sync_state = SYNCED;
else if (im->da.sync_state == SYNC_NEEDED) {
im->da.io_op = disk_ioctl_async(0, CTRL_SYNC, NULL, NULL);
im->da.sync_state = SYNCING;
}
return;
}
idx = wb->cons % wb->len;
off = im->da.write_offsets[idx];
for (cnt = 1; wb->cons + cnt < wb->prod && idx + cnt < wb->len; cnt++)
if (im->da.write_offsets[idx+cnt] != off + cnt)
break;
ASSERT(off);
im->da.io_op = disk_write_async(0, wb->p + idx*512, off, cnt);
im->da.write_cnt = cnt;
im->da.sync_state = SYNC_NEEDED;
}
static bool_t da_open(struct image *im)
{ {
struct da_status_sector *dass = &im->da.dass; struct da_status_sector *dass = &im->da.dass;
void *p = im->bufs.read_data.p;
uint32_t len = im->bufs.read_data.len;
int p_used = 0;
bool_t version_override = (ff_cfg.da_report_version[0] != '\0'); bool_t version_override = (ff_cfg.da_report_version[0] != '\0');
track &= ~1; /* force side 0 */ printk("D-A Mode Entered\n");
if (im->cur_track == track) im->nr_sides = 1;
return;
im->cur_track = track;
volume_cache_init(im->bufs.write_data.p + SEC_SZ + 2, len -= -(uint32_t)p & 3;
im->bufs.write_data.p + im->bufs.write_data.len); p += -(uint32_t)p & 3;
im->da.rd_buf = p + p_used;
p_used += SEC_SZ;
/* 768 bytes for cache overhead. */
volume_cache_init(p + p_used, p + p_used + 8*SEC_SZ + 768);
p_used += 8*SEC_SZ + 768;
im->da.write_buffer.p = p + p_used;
im->da.write_buffer.len =
(len - p_used) / (512 + sizeof(*im->da.write_offsets));
p_used += im->da.write_buffer.len * sizeof(*im->da.write_offsets);
im->da.write_offsets = p + p_used;
p_used += im->da.write_buffer.len * 512;
ASSERT(p_used <= len);
ASSERT(im->da.write_buffer.len >= 8);
im->da.io_op = F_async_get_completed_op();
switch (display_type) { switch (display_type) {
case DT_LED_7SEG: case DT_LED_7SEG:
@ -66,24 +122,33 @@ static void da_seek_track(struct image *im, uint16_t track)
break; break;
} }
memset(&im->da, 0, sizeof(im->da));
snprintf(dass->sig, sizeof(dass->sig), "%s", DA_SIG); snprintf(dass->sig, sizeof(dass->sig), "%s", DA_SIG);
snprintf(dass->fw_ver, sizeof(dass->fw_ver), snprintf(dass->fw_ver, sizeof(dass->fw_ver),
version_override ? "%s" : "FF-v%s", version_override ? "%s" : "FF-%s",
version_override ? ff_cfg.da_report_version : fw_ver); version_override ? ff_cfg.da_report_version : fw_ver);
dass->current_index = get_slot_nr(); dass->current_index = get_slot_nr();
return TRUE;
}
static void da_seek_track(struct image *im, uint16_t track)
{
struct da_status_sector *dass = &im->da.dass;
track &= ~1; /* force side 0 */
if (im->cur_track == track)
return;
im->cur_track = track;
switch (im->cur_track>>1) { switch (im->cur_track>>1) {
case DA_SD_FM_CYL: case DA_SD_FM_CYL:
dass->nr_sec = 4; dass->nr_sec = 4;
im->sync = SYNC_fm; im->sync = SYNC_fm;
im->write_bc_ticks = sysclk_us(4); im->write_bc_ticks = sampleclk_us(4);
break; break;
default: default:
dass->nr_sec = 8; dass->nr_sec = 8;
im->sync = SYNC_mfm; im->sync = SYNC_mfm;
im->write_bc_ticks = sysclk_us(2); im->write_bc_ticks = sampleclk_us(2);
break; break;
} }
@ -95,7 +160,7 @@ static void da_setup_track(
{ {
struct image_buf *rd = &im->bufs.read_data; struct image_buf *rd = &im->bufs.read_data;
struct image_buf *bc = &im->bufs.read_bc; struct image_buf *bc = &im->bufs.read_bc;
uint32_t decode_off, sys_ticks = start_pos ? *start_pos : 0; uint32_t decode_off, start_ticks = start_pos ? *start_pos : 0;
unsigned int nsec; unsigned int nsec;
da_seek_track(im, track); da_seek_track(im, track);
@ -120,11 +185,11 @@ static void da_setup_track(
im->tracklen_bc += im->da.idx_sz; im->tracklen_bc += im->da.idx_sz;
im->tracklen_bc *= 16; im->tracklen_bc *= 16;
im->stk_per_rev = stk_sysclk(im->tracklen_bc * im->write_bc_ticks); im->stk_per_rev = stk_sampleclk(im->tracklen_bc * im->write_bc_ticks);
im->da.trk_sec = 0; im->da.trk_sec = 0;
im->cur_bc = (sys_ticks * 16) / im->ticks_per_cell; im->cur_bc = (start_ticks * 16) / im->ticks_per_cell;
im->cur_bc &= ~15; im->cur_bc &= ~15;
if (im->cur_bc >= im->tracklen_bc) if (im->cur_bc >= im->tracklen_bc)
im->cur_bc = 0; im->cur_bc = 0;
@ -155,9 +220,8 @@ static void da_setup_track(
bc->prod = bc->cons = 0; bc->prod = bc->cons = 0;
if (start_pos) { if (start_pos) {
image_read_track(im); im->da.trash_bc = decode_off * 16;
bc->cons = decode_off * 16; *start_pos = start_ticks;
*start_pos = sys_ticks;
} }
} }
@ -165,8 +229,11 @@ static bool_t da_read_track(struct image *im)
{ {
struct da_status_sector *dass = &im->da.dass; struct da_status_sector *dass = &im->da.dass;
struct image_buf *rd = &im->bufs.read_data; struct image_buf *rd = &im->bufs.read_data;
uint8_t *buf = rd->p; uint8_t *buf = im->da.rd_buf;
progress_write(im);
if (!F_async_isdone(im->da.io_op))
return FALSE;
if (rd->prod == rd->cons) { if (rd->prod == rd->cons) {
uint8_t sec = im->da.trk_sec; uint8_t sec = im->da.trk_sec;
if (sec == 0) { if (sec == 0) {
@ -179,13 +246,15 @@ static bool_t da_read_track(struct image *im)
if (sec == 1) if (sec == 1)
strcpy((char *)buf, im->slot->name); strcpy((char *)buf, im->slot->name);
} else { } else {
if (disk_read(0, buf, dass->lba_base+sec-1, 1) != RES_OK) im->da.io_op = disk_read_async(0, buf, dass->lba_base+sec-1, 1);
F_die(FR_DISK_ERR);
} }
rd->prod++; rd->prod++;
if (++im->da.trk_sec >= (dass->nr_sec + 1)) if (++im->da.trk_sec >= (dass->nr_sec + 1))
im->da.trk_sec = 0; im->da.trk_sec = 0;
} }
thread_yield();
if (!F_async_isdone(im->da.io_op))
return FALSE;
return (im->sync == SYNC_fm) ? fm_read_track(im) : mfm_read_track(im); return (im->sync == SYNC_fm) ? fm_read_track(im) : mfm_read_track(im);
} }
@ -195,7 +264,7 @@ static bool_t fm_read_track(struct image *im)
struct da_status_sector *dass = &im->da.dass; struct da_status_sector *dass = &im->da.dass;
struct image_buf *bc = &im->bufs.read_bc; struct image_buf *bc = &im->bufs.read_bc;
struct image_buf *rd = &im->bufs.read_data; struct image_buf *rd = &im->bufs.read_data;
uint8_t *buf = rd->p; uint8_t *buf = im->da.rd_buf;
uint16_t *bc_b = bc->p; uint16_t *bc_b = bc->p;
uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c; uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c;
uint16_t crc; uint16_t crc;
@ -255,6 +324,12 @@ static bool_t fm_read_track(struct image *im)
#undef emit_raw #undef emit_raw
#undef emit_byte #undef emit_byte
if (im->da.trash_bc) {
int16_t to_consume =
min_t(uint16_t, (bc_p - bc_c)*16, im->da.trash_bc);
im->da.trash_bc -= to_consume;
bc->cons += to_consume;
}
im->da.decode_pos++; im->da.decode_pos++;
bc->prod = bc_p * 16; bc->prod = bc_p * 16;
@ -266,7 +341,7 @@ static bool_t mfm_read_track(struct image *im)
struct da_status_sector *dass = &im->da.dass; struct da_status_sector *dass = &im->da.dass;
struct image_buf *bc = &im->bufs.read_bc; struct image_buf *bc = &im->bufs.read_bc;
struct image_buf *rd = &im->bufs.read_data; struct image_buf *rd = &im->bufs.read_data;
uint8_t *buf = rd->p; uint8_t *buf = im->da.rd_buf;
uint16_t *bc_b = bc->p; uint16_t *bc_b = bc->p;
uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c; uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c;
uint16_t pr, crc; uint16_t pr, crc;
@ -338,6 +413,12 @@ static bool_t mfm_read_track(struct image *im)
#undef emit_raw #undef emit_raw
#undef emit_byte #undef emit_byte
if (im->da.trash_bc) {
int16_t to_consume =
min_t(uint16_t, (bc_p - bc_c)*16, im->da.trash_bc);
im->da.trash_bc -= to_consume;
bc->cons += to_consume;
}
im->da.decode_pos++; im->da.decode_pos++;
bc->prod = bc_p * 16; bc->prod = bc_p * 16;
@ -356,11 +437,12 @@ static bool_t fm_write_track(struct image *im)
struct image_buf *wr = &im->bufs.write_bc; struct image_buf *wr = &im->bufs.write_bc;
uint16_t *buf = wr->p; uint16_t *buf = wr->p;
unsigned int bufmask = (wr->len / 2) - 1; unsigned int bufmask = (wr->len / 2) - 1;
uint8_t *wrbuf = im->bufs.write_data.p; struct image_buf *wb = &im->da.write_buffer;
uint8_t *wrbuf;
uint32_t c = wr->cons / 16, p = wr->prod / 16; uint32_t c = wr->cons / 16, p = wr->prod / 16;
uint32_t base = write->start / im->ticks_per_cell; /* in data bytes */ uint32_t base = write->start / im->ticks_per_cell; /* in data bytes */
unsigned int sect; unsigned int sect;
uint16_t sync; uint16_t sync, crc_data;
uint8_t x; uint8_t x;
/* If we are processing final data then use the end index, rounded up. */ /* If we are processing final data then use the end index, rounded up. */
@ -370,6 +452,7 @@ static bool_t fm_write_track(struct image *im)
p = (write->bc_end + 15) / 16; p = (write->bc_end + 15) / 16;
while ((int16_t)(p - c) >= (2 + SEC_SZ + 2)) { while ((int16_t)(p - c) >= (2 + SEC_SZ + 2)) {
uint32_t sc = c;
if (buf[c++ & bufmask] != 0xaaaa) if (buf[c++ & bufmask] != 0xaaaa)
continue; continue;
@ -382,15 +465,25 @@ static bool_t fm_write_track(struct image *im)
if (x != 0xfb) if (x != 0xfb)
continue; continue;
if (wb->prod - wb->cons >= wb->len) {
c = sc;
flush = FALSE;
break;
}
sect = (base - im->da.idx_sz - im->da.idam_sz + enc_sec_sz(im)/2) sect = (base - im->da.idx_sz - im->da.idam_sz + enc_sec_sz(im)/2)
/ enc_sec_sz(im); / enc_sec_sz(im);
mfm_ring_to_bin(buf, bufmask, c, wrbuf, SEC_SZ + 2); wrbuf = wb->p + (wb->prod % wb->len) * 512;
c += SEC_SZ + 2; mfm_ring_to_bin(buf, bufmask, c, wrbuf, SEC_SZ);
c += SEC_SZ;
mfm_ring_to_bin(buf, bufmask, c, &crc_data, 2);
c += 2;
process_wdata(im, sect, FM_DAM_CRC); process_wdata(im, sect, FM_DAM_CRC, crc_data);
} }
progress_write(im);
wr->cons = c * 16; wr->cons = c * 16;
return flush; return flush;
@ -403,11 +496,12 @@ static bool_t mfm_write_track(struct image *im)
struct image_buf *wr = &im->bufs.write_bc; struct image_buf *wr = &im->bufs.write_bc;
uint16_t *buf = wr->p; uint16_t *buf = wr->p;
unsigned int bufmask = (wr->len / 2) - 1; unsigned int bufmask = (wr->len / 2) - 1;
uint8_t *wrbuf = im->bufs.write_data.p; struct image_buf *wb = &im->da.write_buffer;
uint8_t *wrbuf;
uint32_t c = wr->cons / 16, p = wr->prod / 16; uint32_t c = wr->cons / 16, p = wr->prod / 16;
uint32_t base = write->start / im->ticks_per_cell; /* in data bytes */ uint32_t base = write->start / im->ticks_per_cell; /* in data bytes */
unsigned int sect; unsigned int sect;
uint16_t crc; uint16_t crc, crc_data;
uint8_t x; uint8_t x;
/* If we are processing final data then use the end index, rounded up. */ /* If we are processing final data then use the end index, rounded up. */
@ -452,25 +546,37 @@ static bool_t mfm_write_track(struct image *im)
goto out; goto out;
} }
mfm_ring_to_bin(buf, bufmask, c, wrbuf, SEC_SZ + 2); if (wb->prod - wb->cons >= wb->len) {
c += SEC_SZ + 2; c = sc;
flush = FALSE;
goto out;
}
process_wdata(im, sect, crc); wrbuf = wb->p + (wb->prod % wb->len) * 512;
mfm_ring_to_bin(buf, bufmask, c, wrbuf, SEC_SZ);
c += SEC_SZ;
mfm_ring_to_bin(buf, bufmask, c, &crc_data, 2);
c += 2;
process_wdata(im, sect, crc, crc_data);
} }
out: out:
progress_write(im);
wr->cons = c * 16; wr->cons = c * 16;
return flush; return flush;
} }
static void process_wdata(struct image *im, unsigned int sect, uint16_t crc) static void process_wdata(
struct image *im, unsigned int sect, uint16_t crc, uint16_t crc_data)
{ {
struct da_status_sector *dass = &im->da.dass; struct da_status_sector *dass = &im->da.dass;
uint8_t *wrbuf = im->bufs.write_data.p; struct image_buf *wb = &im->da.write_buffer;
uint8_t *wrbuf = wb->p + (wb->prod % wb->len) * 512;
unsigned int i; unsigned int i;
time_t t;
crc = crc16_ccitt(wrbuf, SEC_SZ + 2, crc); crc = crc16_ccitt(wrbuf, SEC_SZ, crc);
crc = crc16_ccitt(&crc_data, 2, crc);
if ((crc != 0) || (sect > dass->nr_sec)) { if ((crc != 0) || (sect > dass->nr_sec)) {
printk("D-A Bad Sector: CRC %04x, ID %u\n", crc, sect); printk("D-A Bad Sector: CRC %04x, ID %u\n", crc, sect);
return; return;
@ -497,6 +603,7 @@ static void process_wdata(struct image *im, unsigned int sect, uint16_t crc)
dass->nr_sec = dac->param[5] ?: (im->sync == SYNC_fm) ? 4 : 8; dass->nr_sec = dac->param[5] ?: (im->sync == SYNC_fm) ? 4 : 8;
printk("D-A LBA %08x, nr=%u\n", dass->lba_base, dass->nr_sec); printk("D-A LBA %08x, nr=%u\n", dass->lba_base, dass->nr_sec);
dass->last_cmd_status = 0; /* ok */ dass->last_cmd_status = 0; /* ok */
im->da.lba_set = TRUE;
break; break;
case CMD_SET_CYL: case CMD_SET_CYL:
printk("D-A Cyl A=%u B=%u\n", dac->param[0], dac->param[1]); printk("D-A Cyl A=%u B=%u\n", dac->param[0], dac->param[1]);
@ -528,21 +635,40 @@ static void process_wdata(struct image *im, unsigned int sect, uint16_t crc)
break; break;
} }
} else if (dass->lba_base != ~0u) { } else if (dass->lba_base != ~0u) {
uint32_t lba = dass->lba_base + sect - 1;
if (!im->da.lba_set) {
printk("Write %08x+%u... ", dass->lba_base, sect-1);
printk("before LBA set\n");
return;
}
if (!lba_within_fat_volume(lba)) {
printk("Write %08x+%u... ", dass->lba_base, sect-1);
printk("out of bounds\n");
return;
}
/* All good: write out to mass storage. */ /* All good: write out to mass storage. */
dass->write_cnt++; dass->write_cnt++;
printk("Write %08x+%u... ", dass->lba_base, sect-1); im->da.write_offsets[wb->prod % wb->len] = lba;
t = time_now(); wb->prod++;
if (disk_write(0, wrbuf, dass->lba_base+sect-1, 1) != RES_OK)
F_die(FR_DISK_ERR);
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
} }
} }
static void da_sync(struct image *im)
{
do {
F_async_wait(im->da.io_op); /* Possible read op. */
progress_write(im);
} while (im->da.sync_state);
printk("D-A Mode Exited\n");
}
const struct image_handler da_image_handler = { const struct image_handler da_image_handler = {
.open = da_open,
.setup_track = da_setup_track, .setup_track = da_setup_track,
.read_track = da_read_track, .read_track = da_read_track,
.rdata_flux = bc_rdata_flux, .rdata_flux = bc_rdata_flux,
.write_track = da_write_track, .write_track = da_write_track,
.sync = da_sync,
}; };
/* /*

View file

@ -14,6 +14,8 @@
#define GAP_4A 80 /* Post-Index */ #define GAP_4A 80 /* Post-Index */
#define GAP_SYNC 12 #define GAP_SYNC 12
#define BATCH_SIZE 256
struct dib { /* disk info */ struct dib { /* disk info */
char sig[34]; char sig[34];
char creator[14]; char creator[14];
@ -101,8 +103,10 @@ static bool_t dsk_open(struct image *im)
* length and thus the period between index pulses. */ * length and thus the period between index pulses. */
im->ticks_per_cell = im->write_bc_ticks * 16; im->ticks_per_cell = im->write_bc_ticks * 16;
volume_cache_init(im->bufs.write_data.p + 512 + 1024, im->dsk.fcache = file_cache_init(&im->fp, 2,
im->bufs.write_data.p + im->bufs.write_data.len); im->bufs.write_data.p + 512 + max_t(unsigned int, BATCH_SIZE, 512),
im->bufs.write_data.p + im->bufs.write_data.len);
im->cur_track = ~0;
return TRUE; return TRUE;
} }
@ -114,7 +118,9 @@ static void dsk_seek_track(
struct tib *tib = tib_p(im); struct tib *tib = tib_p(im);
unsigned int i, nr; unsigned int i, nr;
uint32_t tracklen; uint32_t tracklen;
uint32_t trk_len;
file_cache_sync_wait(im->dsk.fcache);
im->cur_track = track; im->cur_track = track;
if (cyl >= im->nr_cyls) { if (cyl >= im->nr_cyls) {
@ -132,13 +138,14 @@ static void dsk_seek_track(
goto unformatted; goto unformatted;
for (i = 0; i < nr; i++) for (i = 0; i < nr; i++)
im->dsk.trk_off += dib->track_szs[i] * 256; im->dsk.trk_off += dib->track_szs[i] * 256;
trk_len = dib->track_szs[nr] * 256;
} else { } else {
im->dsk.trk_off += nr * le16toh(dib->track_sz); im->dsk.trk_off += nr * le16toh(dib->track_sz);
trk_len = le16toh(dib->track_sz);
} }
/* Read the Track Info Block and Sector Info Blocks. */ /* Read the Track Info Block and Sector Info Blocks. */
F_lseek(&im->fp, im->dsk.trk_off); file_cache_read(im->dsk.fcache, tib, im->dsk.trk_off, 256);
F_read(&im->fp, tib, 256, NULL);
im->dsk.trk_off += 256; im->dsk.trk_off += 256;
if (strncmp(tib->sig, "Track-Info", 10) || !tib->nr_secs) if (strncmp(tib->sig, "Track-Info", 10) || !tib->nr_secs)
goto unformatted; goto unformatted;
@ -157,6 +164,8 @@ static void dsk_seek_track(
? le16toh(tib->sib[i].actual_length) ? le16toh(tib->sib[i].actual_length)
: 128 << min_t(unsigned, tib->sec_sz, 8); : 128 << min_t(unsigned, tib->sec_sz, 8);
file_cache_readahead(im->dsk.fcache, im->dsk.trk_off, trk_len, 6*1024);
out: out:
im->dsk.idx_sz = GAP_4A; im->dsk.idx_sz = GAP_4A;
im->dsk.idx_sz += GAP_SYNC + 4 + GAP_1; im->dsk.idx_sz += GAP_SYNC + 4 + GAP_1;
@ -183,7 +192,7 @@ out:
im->dsk.gap4 = (im->tracklen_bc - tracklen) / 16; im->dsk.gap4 = (im->tracklen_bc - tracklen) / 16;
/* Calculate ticks per revolution */ /* Calculate ticks per revolution */
im->stk_per_rev = stk_sysclk(im->tracklen_bc * im->write_bc_ticks); im->stk_per_rev = stk_sampleclk(im->tracklen_bc * im->write_bc_ticks);
} }
static uint32_t calc_start_pos(struct image *im) static uint32_t calc_start_pos(struct image *im)
@ -224,9 +233,9 @@ static uint32_t calc_start_pos(struct image *im)
im->dsk.decode_pos++; im->dsk.decode_pos++;
if (decode_off < data_sz(&tib->sib[i])) { if (decode_off < data_sz(&tib->sib[i])) {
/* Data */ /* Data */
im->dsk.rd_sec_pos = decode_off / 1024; im->dsk.rd_sec_pos = decode_off / BATCH_SIZE;
im->dsk.decode_data_pos = im->dsk.rd_sec_pos; im->dsk.decode_data_pos = im->dsk.rd_sec_pos;
decode_off %= 1024; decode_off %= BATCH_SIZE;
} else { } else {
/* Post Data */ /* Post Data */
decode_off -= data_sz(&tib->sib[i]); decode_off -= data_sz(&tib->sib[i]);
@ -238,8 +247,8 @@ static uint32_t calc_start_pos(struct image *im)
} else { } else {
/* Pre-index track gap */ /* Pre-index track gap */
im->dsk.decode_pos = tib->nr_secs * 4 + 1; im->dsk.decode_pos = tib->nr_secs * 4 + 1;
im->dsk.decode_data_pos = decode_off / 1024; im->dsk.decode_data_pos = decode_off / BATCH_SIZE;
decode_off %= 1024; decode_off %= BATCH_SIZE;
} }
} }
@ -251,7 +260,7 @@ static void dsk_setup_track(
{ {
struct image_buf *rd = &im->bufs.read_data; struct image_buf *rd = &im->bufs.read_data;
struct image_buf *bc = &im->bufs.read_bc; struct image_buf *bc = &im->bufs.read_bc;
uint32_t decode_off, sys_ticks = start_pos ? *start_pos : 0; uint32_t decode_off, start_ticks = start_pos ? *start_pos : 0;
uint8_t cyl = track/2, side = track & (im->nr_sides - 1); uint8_t cyl = track/2, side = track & (im->nr_sides - 1);
track = cyl*2 + side; track = cyl*2 + side;
@ -260,22 +269,23 @@ static void dsk_setup_track(
im->dsk.write_sector = -1; im->dsk.write_sector = -1;
im->cur_bc = (sys_ticks * 16) / im->ticks_per_cell; im->cur_bc = (start_ticks * 16) / im->ticks_per_cell;
im->cur_bc &= ~15; im->cur_bc &= ~15;
if (im->cur_bc >= im->tracklen_bc) if (im->cur_bc >= im->tracklen_bc)
im->cur_bc = 0; im->cur_bc = 0;
im->cur_ticks = im->cur_bc * im->ticks_per_cell; im->cur_ticks = im->cur_bc * im->ticks_per_cell;
im->ticks_since_flux = 0; im->ticks_since_flux = 0;
decode_off = calc_start_pos(im);
rd->prod = rd->cons = 0; rd->prod = rd->cons = 0;
bc->prod = bc->cons = 0; bc->prod = bc->cons = 0;
if (start_pos) { if (start_pos) {
image_read_track(im); decode_off = calc_start_pos(im);
bc->cons = decode_off * 16;
*start_pos = sys_ticks; im->dsk.trash_bc = decode_off * 16;
*start_pos = start_ticks;
} else {
im->dsk.decode_pos = 0;
} }
} }
@ -292,6 +302,7 @@ static bool_t dsk_read_track(struct image *im)
if (tib->nr_secs && (rd->prod == rd->cons)) { if (tib->nr_secs && (rd->prod == rd->cons)) {
uint16_t off = 0, len; uint16_t off = 0, len;
bool_t partial = FALSE;
for (i = 0; i < im->dsk.trk_pos; i++) for (i = 0; i < im->dsk.trk_pos; i++)
off += tib->sib[i].actual_length; off += tib->sib[i].actual_length;
len = data_sz(&tib->sib[i]); len = data_sz(&tib->sib[i]);
@ -299,10 +310,16 @@ static bool_t dsk_read_track(struct image *im)
/* Weak sector -- pick different data each revolution. */ /* Weak sector -- pick different data each revolution. */
off += len * (im->dsk.rev % (tib->sib[i].actual_length / len)); off += len * (im->dsk.rev % (tib->sib[i].actual_length / len));
} }
off += im->dsk.rd_sec_pos * 1024; off += im->dsk.rd_sec_pos * BATCH_SIZE;
len -= im->dsk.rd_sec_pos * 1024; len -= im->dsk.rd_sec_pos * BATCH_SIZE;
if (len > 1024) { if (len > BATCH_SIZE) {
len = 1024; len = BATCH_SIZE;
partial = TRUE;
}
if (!file_cache_try_read(im->dsk.fcache, buf, im->dsk.trk_off + off, len))
return FALSE;
if (partial) {
im->dsk.rd_sec_pos++; im->dsk.rd_sec_pos++;
} else { } else {
im->dsk.rd_sec_pos = 0; im->dsk.rd_sec_pos = 0;
@ -311,10 +328,9 @@ static bool_t dsk_read_track(struct image *im)
im->dsk.rev++; im->dsk.rev++;
} }
} }
F_lseek(&im->fp, im->dsk.trk_off + off);
F_read(&im->fp, buf, len, NULL);
rd->prod++; rd->prod++;
} }
file_cache_progress(im->dsk.fcache);
/* Generate some MFM if there is space in the raw-bitcell ring buffer. */ /* Generate some MFM if there is space in the raw-bitcell ring buffer. */
bc_p = bc->prod / 16; /* MFM words */ bc_p = bc->prod / 16; /* MFM words */
@ -346,11 +362,11 @@ static bool_t dsk_read_track(struct image *im)
emit_byte(0x4e); emit_byte(0x4e);
} else if (im->dsk.decode_pos == (tib->nr_secs * 4 + 1)) { } else if (im->dsk.decode_pos == (tib->nr_secs * 4 + 1)) {
/* Pre-index track gap */ /* Pre-index track gap */
uint16_t sz = im->dsk.gap4 - im->dsk.decode_data_pos * 1024; uint16_t sz = im->dsk.gap4 - im->dsk.decode_data_pos * BATCH_SIZE;
if (bc_space < min_t(unsigned int, sz, 1024)) if (bc_space < min_t(unsigned int, sz, BATCH_SIZE))
return FALSE; return FALSE;
if (sz > 1024) { if (sz > BATCH_SIZE) {
sz = 1024; sz = BATCH_SIZE;
im->dsk.decode_data_pos++; im->dsk.decode_data_pos++;
im->dsk.decode_pos--; im->dsk.decode_pos--;
} else { } else {
@ -402,11 +418,11 @@ static bool_t dsk_read_track(struct image *im)
} }
case 2: /* Data */ { case 2: /* Data */ {
uint16_t sec_sz = data_sz(&tib->sib[sec]); uint16_t sec_sz = data_sz(&tib->sib[sec]);
sec_sz -= im->dsk.decode_data_pos * 1024; sec_sz -= im->dsk.decode_data_pos * BATCH_SIZE;
if (bc_space < min_t(unsigned int, sec_sz, 1024)) if (bc_space < min_t(unsigned int, sec_sz, BATCH_SIZE))
return FALSE; return FALSE;
if (sec_sz > 1024) { if (sec_sz > BATCH_SIZE) {
sec_sz = 1024; sec_sz = BATCH_SIZE;
im->dsk.decode_data_pos++; im->dsk.decode_data_pos++;
im->dsk.decode_pos--; im->dsk.decode_pos--;
} else { } else {
@ -435,6 +451,11 @@ static bool_t dsk_read_track(struct image *im)
} }
} }
if (im->dsk.trash_bc) {
int16_t to_consume = min_t(uint16_t, (bc_p - bc_c)*16, im->dsk.trash_bc);
im->dsk.trash_bc -= to_consume;
bc->cons += to_consume;
}
im->dsk.decode_pos++; im->dsk.decode_pos++;
bc->prod = bc_p * 16; bc->prod = bc_p * 16;
@ -460,7 +481,7 @@ static int dsk_find_first_write_sector(
} }
if (i >= tib->nr_secs) { if (i >= tib->nr_secs) {
printk("DSK Bad Wr.Off: %d\n", base); printk("%s Bad Wr.Off: %d\n", "DSK", base);
return -2; return -2;
} }
@ -478,9 +499,7 @@ static bool_t dsk_write_track(struct image *im)
uint8_t *wrbuf = (uint8_t *)im->bufs.write_data.p + 512; /* skip DIB/TIB */ uint8_t *wrbuf = (uint8_t *)im->bufs.write_data.p + 512; /* skip DIB/TIB */
uint32_t c = wr->cons / 16, p = wr->prod / 16; uint32_t c = wr->cons / 16, p = wr->prod / 16;
unsigned int i; unsigned int i;
time_t t; uint16_t crc = im->dsk.crc;
uint16_t crc, off;
uint8_t x;
/* If we are processing final data then use the end index, rounded up. */ /* If we are processing final data then use the end index, rounded up. */
barrier(); barrier();
@ -488,28 +507,38 @@ static bool_t dsk_write_track(struct image *im)
if (flush) if (flush)
p = (write->bc_end + 15) / 16; p = (write->bc_end + 15) / 16;
while ((int16_t)(p - c) > 128) { while ((int16_t)(p - c) > 0) {
if (im->dsk.decode_pos == 0) {
uint32_t sc = c; uint8_t x;
/* When IRQ_write_dma finds the sync it will rewrite 32 bits that
if (be16toh(buf[c++ & bufmask]) != 0x4489) * may have already been observed by the consumer to align the
continue; * bitstream and throws away all but 32 bits of the sync. Give
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1) * SYNC_mfm 32 bits of margin to avoid missing the sync. */
continue; if (p - c < 2 + 2)
c++; break;
if (be16toh(buf[c++ & bufmask]) != 0x4489)
switch (x) { continue;
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
case 0xfe: /* IDAM */ continue;
c++;
if (x == 0xfe) /* IDAM */
im->dsk.decode_pos = 1;
else if (x == 0xfb) /* DAM */
im->dsk.decode_pos = 2;
} else if (im->dsk.decode_pos == 1) {
/* ID record, shy address mark */
if (p - c < 4 + 2)
break;
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
wrbuf[i] = 0xa1; wrbuf[i] = 0xa1;
wrbuf[i++] = x; wrbuf[i++] = 0xfe;
for (; i < 10; i++) for (; i < 10; i++)
wrbuf[i] = mfmtobin(buf[c++ & bufmask]); wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
crc = crc16_ccitt(wrbuf, i, 0xffff); crc = crc16_ccitt(wrbuf, i, 0xffff);
if (crc != 0) { if (crc != 0) {
printk("DSK IDAM Bad CRC: %04x, %02x\n", crc, wrbuf[6]); printk("%s IDAM Bad CRC: %04x, %02x\n", "DSK", crc, wrbuf[6]);
break; im->dsk.decode_pos = 0;
continue;
} }
/* Convert logical sector number -> rotational number. */ /* Convert logical sector number -> rotational number. */
for (i = 0; i < tib->nr_secs; i++) for (i = 0; i < tib->nr_secs; i++)
@ -517,77 +546,105 @@ static bool_t dsk_write_track(struct image *im)
break; break;
im->dsk.write_sector = i; im->dsk.write_sector = i;
if (im->dsk.write_sector >= tib->nr_secs) { if (im->dsk.write_sector >= tib->nr_secs) {
printk("DSK IDAM Bad Sector: %02x\n", wrbuf[6]); printk("%s IDAM Bad Sector: %02x\n", "DSK", wrbuf[6]);
im->dsk.write_sector = -2; im->dsk.write_sector = -2;
} }
break; im->dsk.decode_data_pos = 0;
im->dsk.decode_pos = 0;
case 0xfb: /* DAM */ { } else if (im->dsk.decode_pos == 2) {
unsigned int nr, todo, sec_sz; /* Data record, shy address mark */
unsigned int sec_sz;
int sec_nr = im->dsk.write_sector; int sec_nr = im->dsk.write_sector;
if (sec_nr < 0) { if (sec_nr < 0) {
if (sec_nr == -1) if (sec_nr == -1) {
sec_nr = dsk_find_first_write_sector(im, write, tib); sec_nr = dsk_find_first_write_sector(im, write, tib);
im->dsk.write_sector = sec_nr;
im->dsk.decode_data_pos = 0;
}
if (sec_nr < 0) { if (sec_nr < 0) {
printk("DSK DAM Unknown\n"); printk("%s DAM Unknown\n", "DSK");
goto dam_out; im->dsk.write_sector = -2;
im->dsk.decode_pos = 0;
continue;
} }
} }
sec_sz = data_sz(&tib->sib[sec_nr]); sec_sz = data_sz(&tib->sib[sec_nr]);
if ((int16_t)(p - c) < (sec_sz + 2)) {
c = sc; if (!im->dsk.decode_data_pos) {
goto out; unsigned int off;
crc = MFM_DAM_CRC;
/* Sectors less than 512 bytes require read-then-write, so
* benefit from readahead. Larger sectors don't need readahead
* and can benefit from faster write throughput. */
if (sec_sz >= 512)
file_cache_readahead(im->img.fcache, 0, 0, 0);
for (i = off = 0; i < sec_nr; i++)
off += tib->sib[i].actual_length;
im->dsk.trk_pos = off;
} }
crc = MFM_DAM_CRC; if (im->dsk.decode_data_pos < sec_sz) {
unsigned int nr;
unsigned int off = im->dsk.trk_off+im->dsk.trk_pos;
nr = sec_sz - im->dsk.decode_data_pos;
nr = min_t(unsigned int, nr, 512 - (off & 511));
if (nr >= 512 && (p - c) < 512)
break;
nr = min_t(unsigned int, nr, p - c);
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);
for (todo = sec_sz; todo != 0; todo -= nr) {
nr = min_t(unsigned int, todo, 1024);
mfm_ring_to_bin(buf, bufmask, c, wrbuf, nr); mfm_ring_to_bin(buf, bufmask, c, wrbuf, nr);
c += nr; c += nr;
crc = crc16_ccitt(wrbuf, nr, crc); crc = crc16_ccitt(wrbuf, nr, crc);
F_write(&im->fp, wrbuf, nr, NULL); file_cache_write(im->dsk.fcache, wrbuf, off, nr);
im->dsk.trk_pos += nr;
im->dsk.decode_data_pos += nr;
} }
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ); if (im->dsk.decode_data_pos < sec_sz)
continue;
if (p - c < 2)
break;
printk("Write %d[%02x]/%u\n",
sec_nr, tib->sib[sec_nr].r, tib->nr_secs);
mfm_ring_to_bin(buf, bufmask, c, wrbuf, 2); mfm_ring_to_bin(buf, bufmask, c, wrbuf, 2);
c += 2; c += 2;
crc = crc16_ccitt(wrbuf, 2, crc); crc = crc16_ccitt(wrbuf, 2, crc);
if (crc != 0) { if (crc != 0) {
printk("DSK Bad CRC: %04x, %d[%02x]\n", printk("%s Bad CRC: %04x, %d[%02x]\n",
crc, sec_nr, tib->sib[sec_nr].r); "DSK", crc, sec_nr, tib->sib[sec_nr].r);
} }
dam_out:
im->dsk.write_sector = -2; im->dsk.write_sector = -2;
break; im->dsk.decode_pos = 0;
}
} }
} }
out: if (flush)
file_cache_sync(im->dsk.fcache);
else
file_cache_progress(im->dsk.fcache);
im->dsk.crc = crc;
wr->cons = c * 16; wr->cons = c * 16;
return flush; return flush;
} }
static void dsk_sync(struct image *im)
{
file_cache_sync_wait(im->dsk.fcache);
file_cache_shutdown(im->dsk.fcache);
}
const struct image_handler dsk_image_handler = { const struct image_handler dsk_image_handler = {
.open = dsk_open, .open = dsk_open,
.setup_track = dsk_setup_track, .setup_track = dsk_setup_track,
.read_track = dsk_read_track, .read_track = dsk_read_track,
.rdata_flux = bc_rdata_flux, .rdata_flux = bc_rdata_flux,
.write_track = dsk_write_track, .write_track = dsk_write_track,
.sync = dsk_sync,
}; };
/* /*

View file

@ -21,7 +21,7 @@ static void dummy_setup_track(
{ {
im->cur_track = track; im->cur_track = track;
im->cur_ticks = (start_pos ? *start_pos : 0) * 16; im->cur_ticks = (start_pos ? *start_pos : 0) * 16;
im->tracklen_ticks = sysclk_stk(im->stk_per_rev) * 16; im->tracklen_ticks = sampleclk_stk(im->stk_per_rev) * 16;
im->ticks_since_flux = 0; im->ticks_since_flux = 0;
} }
@ -51,12 +51,17 @@ static bool_t dummy_write_track(struct image *im)
return flush; return flush;
} }
static void dummy_sync(struct image *im)
{
}
const struct image_handler dummy_image_handler = { const struct image_handler dummy_image_handler = {
.open = dummy_open, .open = dummy_open,
.setup_track = dummy_setup_track, .setup_track = dummy_setup_track,
.read_track = dummy_read_track, .read_track = dummy_read_track,
.rdata_flux = dummy_rdata_flux, .rdata_flux = dummy_rdata_flux,
.write_track = dummy_write_track, .write_track = dummy_write_track,
.sync = dummy_sync,
}; };
/* /*

View file

@ -69,12 +69,17 @@ enum {
OP_rand = 2 /* 4: flaky byte */ OP_rand = 2 /* 4: flaky byte */
}; };
static void hfe_seek_track(struct image *im, uint16_t track); #define absdiff_t(type,x,y) \
({ type __x = (x); type __y = (y); __x < __y ? __y-__x: __x-__y; })
static void hfe_seek_track(struct image *im, uint16_t track, bool_t async);
static bool_t hfe_open(struct image *im) static bool_t hfe_open(struct image *im)
{ {
struct disk_header dhdr; struct disk_header dhdr;
uint16_t bitrate; uint16_t bitrate;
/* File data is less compact since it contains data for both heads. */
uint32_t norm_buf_size = im->bufs.write_bc.len + im->bufs.read_data.len/2;
F_read(&im->fp, &dhdr, sizeof(dhdr), NULL); F_read(&im->fp, &dhdr, sizeof(dhdr), NULL);
if (!strncmp(dhdr.sig, "HXCHFEV3", sizeof(dhdr.sig))) { if (!strncmp(dhdr.sig, "HXCHFEV3", sizeof(dhdr.sig))) {
@ -100,121 +105,170 @@ static bool_t hfe_open(struct image *im)
im->hfe.double_step = !dhdr.single_step; im->hfe.double_step = !dhdr.single_step;
im->hfe.tlut_base = le16toh(dhdr.track_list_offset); im->hfe.tlut_base = le16toh(dhdr.track_list_offset);
im->nr_cyls = dhdr.nr_tracks; im->nr_cyls = dhdr.nr_tracks;
if (im->hfe.double_step) im->step = im->hfe.double_step ? 2 : 1;
im->nr_cyls = min_t(unsigned int, im->nr_cyls*2, 255);
im->nr_sides = dhdr.nr_sides; im->nr_sides = dhdr.nr_sides;
im->write_bc_ticks = sysclk_us(500) / bitrate; im->write_bc_ticks = sampleclk_us(500) / bitrate;
im->ticks_per_cell = im->write_bc_ticks * 16; im->ticks_per_cell = im->write_bc_ticks * 16;
im->sync = SYNC_none; im->sync = SYNC_none;
ASSERT(8*512 <= im->bufs.read_data.len); /* Aggressively batch our reads at HD data rate, as that can be faster
volume_cache_init(im->bufs.read_data.p + 8*512, * than some USB drives will serve up a single block.*/
im->bufs.read_data.p + im->bufs.read_data.len); im->hfe.fcache = file_cache_init(&im->fp,
volume_cache_metadata_only(&im->fp); (im->write_bc_ticks > sampleclk_ns(1500)) ? 4 : 8,
im->bufs.read_data.p,
im->bufs.read_data.p + im->bufs.read_data.len);
/* Get an initial value for ticks per revolution. */ /* Get an initial value for ticks per revolution. */
hfe_seek_track(im, 0); hfe_seek_track(im, 0, FALSE);
im->cur_track = -1;
/* Not essential, but we want to know if we are unable to fully buffer
* writes for an HD track when we'd expect there to be enough RAM to make
* it possible. */
ASSERT(ram_kb < 64 || ((200000/8 + 255) & ~255) < norm_buf_size);
return TRUE; return TRUE;
} }
static void hfe_seek_track(struct image *im, uint16_t track) static void hfe_seek_track(struct image *im, uint16_t track, bool_t async)
{ {
struct track_header thdr; struct track_header thdr;
uint16_t old_len;
F_lseek(&im->fp, im->hfe.tlut_base*512 + (track/2)*4); if (async) {
F_read(&im->fp, &thdr, sizeof(thdr), NULL); file_cache_io_limit(im->hfe.fcache, 1);
file_cache_read(im->hfe.fcache,
&thdr, im->hfe.tlut_base*512 + (track/2)*4, sizeof(thdr));
file_cache_io_limit(im->hfe.fcache, 0 /* no limit */);
} else {
F_lseek(&im->fp, im->hfe.tlut_base*512 + (track/2)*4);
F_read(&im->fp, &thdr, sizeof(thdr), NULL);
}
im->hfe.trk_off = le16toh(thdr.offset); im->hfe.trk_off = le16toh(thdr.offset);
old_len = im->hfe.trk_len;
im->hfe.trk_len = le16toh(thdr.len) / 2; im->hfe.trk_len = le16toh(thdr.len) / 2;
im->tracklen_bc = im->hfe.trk_len * 8; im->tracklen_bc = im->hfe.trk_len * 8;
im->stk_per_rev = stk_sysclk(im->tracklen_bc * im->write_bc_ticks); /* Opcodes in v3 make it difficult to predict the track's length. Keep the
* previous track's value if the track byte lengths are close. */
if (!(im->hfe.is_v3 && im->stk_per_rev
&& absdiff_t(uint16_t, old_len, im->hfe.trk_len) < 256))
im->stk_per_rev = stk_sampleclk(im->tracklen_bc * im->write_bc_ticks);
im->cur_track = track; file_cache_readahead(im->hfe.fcache,
(LBA_t)im->hfe.trk_off * 512, im->hfe.trk_len*2, 12*1024);
} }
static void hfe_setup_track( static void hfe_setup_track(
struct image *im, uint16_t track, uint32_t *start_pos) struct image *im, uint16_t track, uint32_t *start_pos)
{ {
struct image_buf *rd = &im->bufs.read_data;
struct image_buf *bc = &im->bufs.read_bc; struct image_buf *bc = &im->bufs.read_bc;
uint32_t sys_ticks; uint32_t start_ticks, opcode_adj_bc = 0;
uint8_t cyl = track >> (im->hfe.double_step ? 2 : 1); uint8_t cyl = track >> (im->hfe.double_step ? 2 : 1);
uint8_t side = track & (im->nr_sides - 1); uint8_t side = track & (im->nr_sides - 1);
int i;
track = cyl*2 + side; track = cyl*2 + side;
if (track != im->cur_track) if (track/2 != im->cur_track/2) {
hfe_seek_track(im, track); file_cache_sync_wait(im->hfe.fcache);
sys_ticks = start_pos ? *start_pos : get_write(im, im->wr_cons)->start; im->cur_track = track;
im->cur_bc = (sys_ticks * 16) / im->ticks_per_cell; hfe_seek_track(im, track, TRUE);
if (im->cur_bc >= im->tracklen_bc) } else if (track != im->cur_track) {
im->cur_track = track;
}
/* If track does not fit in memory, now is a good time to flush writes to
* reduce chances of future buffer underrun caused by a very slow write.
* However if write-drain=realtime, then any delays cut into reads so we
* just accept the buffer underrun risk. */
if ((im->hfe.trk_len*2 + 511) / 512 > im->bufs.read_data.len
&& ff_cfg.write_drain != WDRAIN_realtime)
file_cache_sync_wait(im->hfe.fcache);
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->hfe.is_v3 && im->tracklen_ticks > 0
&& im->tracklen_ticks < im->tracklen_bc * im->ticks_per_cell) {
/* If there are opcodes (other than random) in the track, seeking will
* not be precise as opcodes contribute zero bitcells and thus zero
* ticks. The HFE track data will _appear_ misaligned to the previous
* until the track is read from the beginning. Misalignment greater
* than 3 ms is possible and can shift writes backward in time.
*
* Severe misalignment is most likely caused by regular occurrences of
* OP_bitrate evenly distributed through the track. Assume opcodes
* numerous enough to become noticeable are evenly distributed in the
* track.
*/
uint32_t assumed_tracklen_ticks = im->tracklen_bc * im->ticks_per_cell;
uint32_t opcode_ticks = assumed_tracklen_ticks - im->tracklen_ticks;
uint32_t opcode_bc = opcode_ticks / im->ticks_per_cell;
opcode_adj_bc = im->cur_bc * opcode_bc / (im->tracklen_bc - opcode_bc);
}
if (im->cur_bc + opcode_adj_bc >= im->tracklen_bc) {
im->cur_bc = 0; im->cur_bc = 0;
opcode_adj_bc = 0;
}
im->cur_ticks = im->cur_bc * im->ticks_per_cell; im->cur_ticks = im->cur_bc * im->ticks_per_cell;
im->ticks_since_flux = 0; im->ticks_since_flux = 0;
sys_ticks = im->cur_ticks / 16; /* Must be careful to exclude opcode_adj_bc from tick calculations. */
im->cur_bc += opcode_adj_bc;
start_ticks = im->cur_ticks / 16;
rd->prod = rd->cons = 0;
bc->prod = bc->cons = 0; bc->prod = bc->cons = 0;
/* Aggressively batch our reads at HD data rate, as that can be faster for (i = 0; i < im->index_pulses_len; i++)
* than some USB drives will serve up a single block.*/ if (im->cur_ticks < im->index_pulses[i])
im->hfe.batch_secs = (im->write_bc_ticks > sysclk_ns(1500)) ? 2 : 8; break;
im->hfe.next_index_pulses_pos = i;
if (start_pos) { if (start_pos) {
/* Read mode. */ /* Read mode. */
im->hfe.trk_pos = (im->cur_bc/8) & ~255; im->hfe.trk_pos = (im->cur_bc/8) & ~255;
image_read_track(im); /* Consumer may be ahead of producer, but only until the first read
bc->cons = im->cur_bc & 2047; * completes. */
*start_pos = sys_ticks; bc->cons = im->cur_bc % (256*8);
*start_pos = start_ticks;
} else { } else {
/* Write mode. */ /* Write mode. */
im->hfe.trk_pos = im->cur_bc / 8; im->hfe.trk_pos = im->cur_bc / 8;
im->hfe.write.start = im->hfe.trk_pos; im->hfe.fresh_seek = TRUE;
im->hfe.write.wrapped = FALSE;
im->hfe.write_batch.len = 0;
im->hfe.write_batch.dirty = FALSE;
} }
} }
static bool_t hfe_read_track(struct image *im) static bool_t hfe_read_track(struct image *im)
{ {
struct image_buf *rd = &im->bufs.read_data;
struct image_buf *bc = &im->bufs.read_bc; struct image_buf *bc = &im->bufs.read_bc;
uint8_t *buf = rd->p;
uint8_t *bc_b = bc->p; uint8_t *bc_b = bc->p;
uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c; uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c;
unsigned int nr_sec; unsigned int nr_sec;
if (rd->prod == rd->cons) {
nr_sec = min_t(unsigned int, im->hfe.batch_secs,
(im->hfe.trk_len+255 - im->hfe.trk_pos) / 256);
F_lseek(&im->fp, im->hfe.trk_off * 512 + im->hfe.trk_pos * 2);
F_read(&im->fp, buf, nr_sec*512, NULL);
rd->cons = 0;
rd->prod = nr_sec;
im->hfe.trk_pos += nr_sec * 256;
if (im->hfe.trk_pos >= im->hfe.trk_len)
im->hfe.trk_pos = 0;
}
/* Fill the raw-bitcell ring buffer. */ /* Fill the raw-bitcell ring buffer. */
bc_p = bc->prod / 8; bc_p = bc->prod / 8;
bc_c = bc->cons / 8; bc_c = bc->cons / 8;
bc_len = bc->len; bc_len = bc->len;
bc_mask = bc_len - 1; bc_mask = bc_len - 1;
bc_space = bc_len - (uint16_t)(bc_p - bc_c); bc_space = bc_len - (int16_t)(bc_p - bc_c);
nr_sec = min_t(unsigned int, rd->prod - rd->cons, bc_space/256); nr_sec = bc_space/256;
if (nr_sec == 0) if (nr_sec == 0) {
file_cache_progress(im->hfe.fcache);
return FALSE; return FALSE;
}
while (nr_sec--) { while (nr_sec--) {
memcpy(&bc_b[bc_p & bc_mask], FSIZE_t off = im->hfe.trk_off * 512
&buf[rd->cons*512 + (im->cur_track&1)*256], + im->hfe.trk_pos * 2
256); + (im->cur_track&1)*256;
rd->cons++; if (!file_cache_try_read(im->hfe.fcache, &bc_b[bc_p & bc_mask], off, 256))
break;
im->hfe.trk_pos += 256;
if (im->hfe.trk_pos >= im->hfe.trk_len)
im->hfe.trk_pos = 0;
bc_p += 256; bc_p += 256;
} }
@ -235,14 +289,20 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
uint8_t x; uint8_t x;
bool_t is_v3 = im->hfe.is_v3; bool_t is_v3 = im->hfe.is_v3;
while ((uint32_t)(bc_p - bc_c) >= 3*8) { while ((int32_t)(bc_p - bc_c) >= 3*8) {
ASSERT(y == 8); ASSERT(y == 8);
if (im->cur_bc >= im->tracklen_bc) { if (im->cur_bc >= im->tracklen_bc) {
ASSERT(im->cur_bc == im->tracklen_bc); ASSERT(im->cur_bc == im->tracklen_bc);
im->tracklen_ticks = im->cur_ticks; im->tracklen_ticks = im->cur_ticks;
im->cur_bc = im->cur_ticks = 0; im->cur_bc = im->cur_ticks = 0;
im->stk_per_rev = stk_sampleclk(im->tracklen_ticks / 16);
/* Skip tail of current 256-byte block. */ /* Skip tail of current 256-byte block. */
bc_c = (bc_c + 256*8-1) & ~(256*8-1); bc_c = (bc_c + 256*8-1) & ~(256*8-1);
if (im->index_pulses_len != im->hfe.next_index_pulses_pos) {
im->index_pulses_len = im->hfe.next_index_pulses_pos;
im->index_pulses_ver++;
}
im->hfe.next_index_pulses_pos = 0;
continue; continue;
} }
y = bc_c % 8; y = bc_c % 8;
@ -250,8 +310,19 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
if (is_v3 && (y == 0) && ((x & 0xf) == 0xf)) { if (is_v3 && (y == 0) && ((x & 0xf) == 0xf)) {
/* V3 byte-aligned opcode processing. */ /* V3 byte-aligned opcode processing. */
switch (x >> 4) { switch (x >> 4) {
case OP_nop:
case OP_index: case OP_index:
if (im->hfe.next_index_pulses_pos < MAX_CUSTOM_PULSES
&& im->index_pulses[im->hfe.next_index_pulses_pos] != im->cur_ticks) {
im->index_pulses[im->hfe.next_index_pulses_pos]
= im->cur_ticks;
if (im->index_pulses_len < im->hfe.next_index_pulses_pos+1)
im->index_pulses_len = im->hfe.next_index_pulses_pos+1;
im->index_pulses_ver++;
}
im->hfe.next_index_pulses_pos++;
/* fallthrough */
case OP_nop:
default: default:
bc_c += 8; bc_c += 8;
im->cur_bc += 8; im->cur_bc += 8;
@ -260,7 +331,8 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
case OP_bitrate: case OP_bitrate:
x = _rbit32(bc_b[(bc_c/8+1) & bc_mask]) >> 24; x = _rbit32(bc_b[(bc_c/8+1) & bc_mask]) >> 24;
im->ticks_per_cell = ticks_per_cell = im->ticks_per_cell = ticks_per_cell =
(sysclk_us(2) * 16 * x) / 72; (sampleclk_us(2) * 16 * x) / 72;
im->write_bc_ticks = ticks_per_cell / 16;
bc_c += 2*8; bc_c += 2*8;
im->cur_bc += 2*8; im->cur_bc += 2*8;
y = 8; y = 8;
@ -273,8 +345,6 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
x = bc_b[(bc_c/8) & bc_mask] >> y; x = bc_b[(bc_c/8) & bc_mask] >> y;
break; break;
case OP_rand: case OP_rand:
bc_c += 8;
im->cur_bc += 8;
x = rand(); x = rand();
break; break;
} }
@ -305,16 +375,15 @@ out:
static bool_t hfe_write_track(struct image *im) static bool_t hfe_write_track(struct image *im)
{ {
const unsigned int batch_secs = 8;
bool_t flush; bool_t flush;
struct write *write = get_write(im, im->wr_cons); struct write *write = get_write(im, im->wr_cons);
struct image_buf *wr = &im->bufs.write_bc; struct image_buf *wr = &im->bufs.write_bc;
uint8_t *buf = wr->p; uint8_t *buf = wr->p;
uint8_t b;
unsigned int bufmask = wr->len - 1; unsigned int bufmask = wr->len - 1;
uint8_t *w, *wrbuf = im->bufs.write_data.p; uint8_t *w, *wrbuf;
uint32_t i, space, c = wr->cons / 8, p = wr->prod / 8; uint32_t i, space, c = wr->cons / 8, p = wr->prod / 8;
bool_t writeback = FALSE; bool_t is_v3 = im->hfe.is_v3;
time_t t;
/* If we are processing final data then use the end index, rounded to /* If we are processing final data then use the end index, rounded to
* nearest. */ * nearest. */
@ -323,20 +392,9 @@ static bool_t hfe_write_track(struct image *im)
if (flush) if (flush)
p = (write->bc_end + 4) / 8; p = (write->bc_end + 4) / 8;
if (im->hfe.write_batch.len == 0) {
ASSERT(!im->hfe.write_batch.dirty);
im->hfe.write_batch.off = (im->hfe.trk_pos & ~255) << 1;
im->hfe.write_batch.len = min_t(
uint32_t, batch_secs * 512,
(((im->hfe.trk_len * 2) + 511) & ~511) - im->hfe.write_batch.off);
F_lseek(&im->fp, im->hfe.trk_off * 512 + im->hfe.write_batch.off);
F_read(&im->fp, wrbuf, im->hfe.write_batch.len, NULL);
F_lseek(&im->fp, im->hfe.trk_off * 512 + im->hfe.write_batch.off);
}
for (;;) { for (;;) {
uint32_t batch_off, off = im->hfe.trk_pos; uint32_t foff, off = im->hfe.trk_pos;
UINT nr; UINT nr;
/* All bytes remaining in the raw-bitcell buffer. */ /* All bytes remaining in the raw-bitcell buffer. */
@ -350,67 +408,107 @@ static bool_t hfe_write_track(struct image *im)
if (nr == 0) if (nr == 0)
break; break;
/* Bail if required data not in the write buffer. */ foff = im->hfe.trk_off * 512 + ((off & ~255) << 1);
batch_off = (off & ~255) << 1; if ((wrbuf = file_cache_peek_write(im->hfe.fcache, foff)) == NULL) {
if ((batch_off < im->hfe.write_batch.off) flush = FALSE;
|| (batch_off >= (im->hfe.write_batch.off
+ im->hfe.write_batch.len))) {
writeback = TRUE;
break; break;
} }
/* Encode into the sector buffer for later write-out. */
w = wrbuf w = wrbuf
+ (im->cur_track & 1) * 256 + (im->cur_track & 1) * 256
+ batch_off - im->hfe.write_batch.off
+ (off & 255); + (off & 255);
for (i = 0; i < nr; i++)
*w++ = _rbit32(buf[c++ & bufmask]) >> 24;
im->hfe.write_batch.dirty = TRUE;
im->hfe.trk_pos += nr; i = 0;
if (im->hfe.trk_pos >= im->hfe.trk_len) {
ASSERT(im->hfe.trk_pos == im->hfe.trk_len); if (im->hfe.fresh_seek && is_v3 && (off & 255) >= 1) {
im->hfe.trk_pos = 0; /* Avoid writing in the middle of an opcode. */
im->hfe.write.wrapped = TRUE; char b = *(w-1);
if ((off & 255) >= 2)
if ((*(w-2) & 0xf) == 0xf && (*(w-2) >> 4) == OP_skip) {
w++;
i++;
}
if ((b & 0xf) == 0xf) {
switch (b >> 4) {
case OP_skip:
w += 2;
i += 2;
break;
case OP_bitrate:
w++;
i++;
break;
default:
break;
}
}
} }
im->hfe.fresh_seek = FALSE;
for (; i < nr; i++) {
if (is_v3 && (*w & 0xf) == 0xf) {
switch (*w >> 4) {
case OP_skip:
/* Don't bother; these bits are unlikely to matter. */
w++;
i++;
/* fallthrough */
case OP_bitrate:
/* Assume bitrate does not change for the entire track, and
* write_bc_ticks already adjusted when reading. */
w++;
i++;
/* fallthrough */
case OP_nop:
case OP_index:
default:
/* Preserve opcode. But making sure not to write past end of
* buffer. */
w++;
continue;
case OP_rand:
/* Replace with data. */
break;
}
}
b = _rbit32(buf[c++ & bufmask]) >> 24;
/* HFEv3 can't handle a run of 1s as it will appear like an opcode.
* If we encounter such a run, then either it is garbage or the
* file needs twice the bitrate. Assume garbage; a bad bitrate would
* fail rapidly. */
if (is_v3 && (b & 0xf) == 0xf)
b ^= 2;
*w++ = b;
}
im->hfe.trk_pos += i; /* i may be larger than nr due to opcodes. */
if (im->hfe.trk_pos >= im->hfe.trk_len)
im->hfe.trk_pos = 0;
} }
if (writeback) { if (flush)
/* If writeback requested then ensure we get called again. */ file_cache_sync(im->hfe.fcache);
flush = FALSE; else
} else if (flush) { file_cache_progress(im->hfe.fcache);
/* If this is the final call, we should do writeback. */
writeback = TRUE;
}
if (writeback && im->hfe.write_batch.dirty) {
t = time_now();
printk("Write %u-%u (%u)... ",
im->hfe.write_batch.off,
im->hfe.write_batch.off + im->hfe.write_batch.len - 1,
im->hfe.write_batch.len);
F_write(&im->fp, wrbuf, im->hfe.write_batch.len, NULL);
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
im->hfe.write_batch.len = 0;
im->hfe.write_batch.dirty = FALSE;
}
if (flush && im->hfe.write.wrapped
&& (im->hfe.trk_pos > im->hfe.write.start))
printk("Wrapped (%u > %u)\n", im->hfe.trk_pos, im->hfe.write.start);
wr->cons = c * 8; wr->cons = c * 8;
return flush; return flush;
} }
static void hfe_sync(struct image *im)
{
file_cache_sync_wait(im->hfe.fcache);
file_cache_shutdown(im->hfe.fcache);
}
const struct image_handler hfe_image_handler = { const struct image_handler hfe_image_handler = {
.open = hfe_open, .open = hfe_open,
.setup_track = hfe_setup_track, .setup_track = hfe_setup_track,
.read_track = hfe_read_track, .read_track = hfe_read_track,
.rdata_flux = hfe_rdata_flux, .rdata_flux = hfe_rdata_flux,
.write_track = hfe_write_track, .write_track = hfe_write_track,
.sync = hfe_sync,
}; };
/* /*

View file

@ -15,6 +15,7 @@ extern const struct image_handler adf_image_handler;
extern const struct image_handler atr_image_handler; extern const struct image_handler atr_image_handler;
extern const struct image_handler hfe_image_handler; extern const struct image_handler hfe_image_handler;
extern const struct image_handler img_image_handler; extern const struct image_handler img_image_handler;
extern const struct image_handler sf7_image_handler;
extern const struct image_handler st_image_handler; extern const struct image_handler st_image_handler;
extern const struct image_handler d81_image_handler; extern const struct image_handler d81_image_handler;
extern const struct image_handler dsk_image_handler; extern const struct image_handler dsk_image_handler;
@ -45,6 +46,7 @@ const struct image_type image_type[] = {
{ "img", &img_image_handler }, { "img", &img_image_handler },
{ "ima", &img_image_handler }, { "ima", &img_image_handler },
{ "out", &img_image_handler }, { "out", &img_image_handler },
{ "sf7", &sf7_image_handler },
{ "st", &st_image_handler }, { "st", &st_image_handler },
{ "adl", &adfs_image_handler }, { "adl", &adfs_image_handler },
{ "adm", &adfs_image_handler }, { "adm", &adfs_image_handler },
@ -116,7 +118,7 @@ static bool_t try_handler(struct image *im, struct slot *slot,
/* Sensible defaults. */ /* Sensible defaults. */
im->sync = SYNC_mfm; im->sync = SYNC_mfm;
im->write_bc_ticks = sysclk_us(2); im->write_bc_ticks = sampleclk_us(2);
im->stk_per_rev = stk_ms(200); im->stk_per_rev = stk_ms(200);
im->disk_handler = im->track_handler = handler; im->disk_handler = im->track_handler = handler;
@ -132,7 +134,8 @@ static bool_t try_handler(struct image *im, struct slot *slot,
#if !defined(QUICKDISK) #if !defined(QUICKDISK)
void image_open(struct image *im, struct slot *slot, DWORD *cltbl) void image_open(struct image *im, struct slot *slot, DWORD *cltbl,
bool_t da_mode)
{ {
static const struct image_handler * const image_handlers[] = { static const struct image_handler * const image_handlers[] = {
/* Special handler for dummy slots (empty HxC slot 0). */ /* Special handler for dummy slots (empty HxC slot 0). */
@ -147,6 +150,12 @@ void image_open(struct image *im, struct slot *slot, DWORD *cltbl)
const struct image_type *type; const struct image_type *type;
int i; int i;
if (da_mode) {
if (try_handler(im, slot, cltbl, &da_image_handler))
return;
F_die(FR_BAD_IMAGE);
}
/* Extract filename extension (if available). */ /* Extract filename extension (if available). */
memcpy(ext, slot->type, sizeof(slot->type)); memcpy(ext, slot->type, sizeof(slot->type));
ext[sizeof(slot->type)] = '\0'; ext[sizeof(slot->type)] = '\0';
@ -197,7 +206,8 @@ void image_open(struct image *im, struct slot *slot, DWORD *cltbl)
#else /* defined(QUICKDISK) */ #else /* defined(QUICKDISK) */
void image_open(struct image *im, struct slot *slot, DWORD *cltbl) void image_open(struct image *im, struct slot *slot, DWORD *cltbl,
bool_t da_mode)
{ {
if (try_handler(im, slot, cltbl, &qd_image_handler)) if (try_handler(im, slot, cltbl, &qd_image_handler))
return; return;
@ -208,6 +218,15 @@ void image_open(struct image *im, struct slot *slot, DWORD *cltbl)
#endif #endif
bool_t image_in_da_mode(struct image *im)
{
#if !defined(QUICKDISK)
return im->disk_handler == &da_image_handler;
#else
return FALSE;
#endif
}
void image_extend(struct image *im) void image_extend(struct image *im)
{ {
FSIZE_t new_sz; FSIZE_t new_sz;
@ -252,30 +271,23 @@ static void print_image_info(struct image *im)
lcd_write(0, 2, -1, msg); lcd_write(0, 2, -1, msg);
} }
bool_t image_setup_track( void image_setup_track(
struct image *im, uint16_t track, uint32_t *start_pos) struct image *im, uint16_t track, uint32_t *start_pos)
{ {
const struct image_handler *h = im->track_handler; const struct image_handler *h = im->track_handler;
#if !defined(QUICKDISK) #if !defined(QUICKDISK)
if (!in_da_mode(im, track>>1)) { if (h != &da_image_handler)
/* If we are exiting D-A mode then need to re-read the config file. */ h = ((track>>1) >= im_nphys_cyls(im)) ? &dummy_image_handler
if (h == &da_image_handler)
return TRUE;
h = ((track>>1) >= im->nr_cyls) ? &dummy_image_handler
: im->disk_handler; : im->disk_handler;
} else {
h = &da_image_handler;
im->nr_sides = 1;
}
#endif #endif
if (h != im->track_handler)
image_sync(im);
im->track_handler = h; im->track_handler = h;
h->setup_track(im, track, start_pos); h->setup_track(im, track, start_pos);
print_image_info(im); print_image_info(im);
return FALSE;
} }
bool_t image_read_track(struct image *im) bool_t image_read_track(struct image *im)
@ -293,6 +305,11 @@ bool_t image_write_track(struct image *im)
return im->track_handler->write_track(im); return im->track_handler->write_track(im);
} }
void image_sync(struct image *im)
{
im->track_handler->sync(im);
}
uint32_t image_ticks_since_index(struct image *im) uint32_t image_ticks_since_index(struct image *im)
{ {
uint32_t ticks = im->cur_ticks - im->ticks_since_flux; uint32_t ticks = im->cur_ticks - im->ticks_since_flux;

File diff suppressed because it is too large Load diff

View file

@ -74,6 +74,7 @@ void mfm_ring_to_bin(const uint16_t *ring, unsigned int mask,
unsigned int idx, void *out, unsigned int nr) unsigned int idx, void *out, unsigned int nr)
{ {
unsigned int head; unsigned int head;
idx &= mask;
head = min_t(unsigned int, nr, mask+1-idx); head = min_t(unsigned int, nr, mask+1-idx);
mfm_to_bin(&ring[idx], out, head); mfm_to_bin(&ring[idx], out, head);
if (head != nr) if (head != nr)

View file

@ -33,14 +33,13 @@ static bool_t qd_open(struct image *im)
im->qd.tb = 1; im->qd.tb = 1;
im->nr_cyls = 1; im->nr_cyls = 1;
im->nr_sides = 1; im->nr_sides = 1;
im->write_bc_ticks = sysclk_us(4) + 66; /* 4.917us */ im->write_bc_ticks = sampleclk_us(4) + 66; /* 4.917us */
im->ticks_per_cell = im->write_bc_ticks; im->ticks_per_cell = im->write_bc_ticks;
im->sync = SYNC_none; im->sync = SYNC_none;
ASSERT(8*512 <= im->bufs.read_data.len); im->qd.fcache = file_cache_init(&im->fp, 2,
volume_cache_init(im->bufs.read_data.p + 8*512, im->bufs.read_data.p,
im->bufs.read_data.p + im->bufs.read_data.len); im->bufs.read_data.p + im->bufs.read_data.len);
volume_cache_metadata_only(&im->fp);
/* There is only one track: Seek to it. */ /* There is only one track: Seek to it. */
qd_seek_track(im, 0); qd_seek_track(im, 0);
@ -60,11 +59,13 @@ static void qd_seek_track(struct image *im, uint16_t track)
im->qd.trk_len = le32toh(thdr.len); im->qd.trk_len = le32toh(thdr.len);
/* Read/write window limits in STK ticks from data start. */ /* Read/write window limits in STK ticks from data start. */
im->qd.win_start = le32toh(thdr.win_start) * im->write_bc_ticks; im->qd.win_start = (le32toh(thdr.win_start) * im->write_bc_ticks
im->qd.win_end = le32toh(thdr.win_end) * im->write_bc_ticks; * ((8 * STK_MHZ) / SAMPLECLK_MHZ));
im->qd.win_end = (le32toh(thdr.win_end) * im->write_bc_ticks
* ((8 * STK_MHZ) / SAMPLECLK_MHZ));
im->tracklen_bc = im->qd.trk_len * 8; im->tracklen_bc = im->qd.trk_len * 8;
im->stk_per_rev = stk_sysclk(im->tracklen_bc * im->write_bc_ticks); im->stk_per_rev = stk_sampleclk(im->tracklen_bc * im->write_bc_ticks);
im->cur_track = track; im->cur_track = track;
} }
@ -72,60 +73,42 @@ static void qd_seek_track(struct image *im, uint16_t track)
static void qd_setup_track( static void qd_setup_track(
struct image *im, uint16_t track, uint32_t *start_pos) struct image *im, uint16_t track, uint32_t *start_pos)
{ {
struct image_buf *rd = &im->bufs.read_data;
struct image_buf *bc = &im->bufs.read_bc; struct image_buf *bc = &im->bufs.read_bc;
uint32_t sys_ticks; uint32_t start_ticks;
sys_ticks = start_pos ? *start_pos : get_write(im, im->wr_cons)->start; start_ticks = start_pos ? *start_pos : get_write(im, im->wr_cons)->start;
im->cur_bc = sys_ticks / im->ticks_per_cell; im->cur_bc = start_ticks / im->ticks_per_cell;
if (im->cur_bc >= im->tracklen_bc) if (im->cur_bc >= im->tracklen_bc)
im->cur_bc = 0; im->cur_bc = 0;
im->cur_ticks = im->cur_bc * im->ticks_per_cell; im->cur_ticks = im->cur_bc * im->ticks_per_cell;
im->ticks_since_flux = 0; im->ticks_since_flux = 0;
sys_ticks = im->cur_ticks; start_ticks = im->cur_ticks;
rd->prod = rd->cons = 0;
bc->prod = bc->cons = 0; bc->prod = bc->cons = 0;
file_cache_readahead(im->qd.fcache,
im->qd.trk_off, im->qd.trk_len, 12*1024);
if (start_pos) { if (start_pos) {
/* Read mode. */ /* Read mode. */
im->qd.trk_pos = (im->cur_bc/8) & ~511; im->qd.trk_pos = (im->cur_bc/8) & ~511;
image_read_track(im); /* Consumer may be ahead of producer, but only until the first read
* completes. */
bc->cons = im->cur_bc & 4095; bc->cons = im->cur_bc & 4095;
*start_pos = sys_ticks; *start_pos = start_ticks;
} else { } else {
/* Write mode. */ /* Write mode. */
im->qd.trk_pos = im->cur_bc / 8; im->qd.trk_pos = im->cur_bc / 8;
im->qd.write.start = im->qd.trk_pos;
im->qd.write.wrapped = FALSE;
im->qd.write_batch.len = 0;
im->qd.write_batch.dirty = FALSE;
} }
} }
static bool_t qd_read_track(struct image *im) static bool_t qd_read_track(struct image *im)
{ {
const unsigned int batch_secs = 2;
struct image_buf *rd = &im->bufs.read_data;
struct image_buf *bc = &im->bufs.read_bc; struct image_buf *bc = &im->bufs.read_bc;
uint8_t *buf = rd->p;
uint8_t *bc_b = bc->p; uint8_t *bc_b = bc->p;
uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c; uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c;
unsigned int nr_sec; unsigned int nr_sec;
if (rd->prod == rd->cons) {
nr_sec = min_t(unsigned int, batch_secs,
(im->qd.trk_len+511 - im->qd.trk_pos) / 512);
F_lseek(&im->fp, im->qd.trk_off + im->qd.trk_pos);
F_read(&im->fp, buf, nr_sec*512, NULL);
rd->cons = 0;
rd->prod = nr_sec;
im->qd.trk_pos += nr_sec * 512;
if (im->qd.trk_pos >= im->qd.trk_len)
im->qd.trk_pos = 0;
}
/* Fill the raw-bitcell ring buffer. */ /* Fill the raw-bitcell ring buffer. */
bc_p = bc->prod / 8; bc_p = bc->prod / 8;
bc_c = bc->cons / 8; bc_c = bc->cons / 8;
@ -133,13 +116,20 @@ static bool_t qd_read_track(struct image *im)
bc_mask = bc_len - 1; bc_mask = bc_len - 1;
bc_space = bc_len - (uint16_t)(bc_p - bc_c); bc_space = bc_len - (uint16_t)(bc_p - bc_c);
nr_sec = min_t(unsigned int, rd->prod - rd->cons, bc_space/512); nr_sec = bc_space/512;
if (nr_sec == 0) if (nr_sec == 0) {
file_cache_progress(im->qd.fcache);
return FALSE; return FALSE;
}
while (nr_sec--) { while (nr_sec--) {
memcpy(&bc_b[bc_p & bc_mask], &buf[rd->cons*512], 512); if (!file_cache_try_read(im->qd.fcache,
rd->cons++; &bc_b[bc_p & bc_mask], im->qd.trk_off + im->qd.trk_pos,
512))
break;
im->qd.trk_pos += 512;
if (im->qd.trk_pos >= im->qd.trk_len)
im->qd.trk_pos = 0;
bc_p += 512; bc_p += 512;
} }
@ -197,16 +187,13 @@ out:
static bool_t qd_write_track(struct image *im) static bool_t qd_write_track(struct image *im)
{ {
const unsigned int batch_secs = 8;
bool_t flush; bool_t flush;
struct write *write = get_write(im, im->wr_cons); struct write *write = get_write(im, im->wr_cons);
struct image_buf *wr = &im->bufs.write_bc; struct image_buf *wr = &im->bufs.write_bc;
uint8_t *buf = wr->p; uint8_t *buf = wr->p;
unsigned int bufmask = wr->len - 1; unsigned int bufmask = wr->len - 1;
uint8_t *w, *wrbuf = im->bufs.write_data.p; uint8_t *w;
uint32_t i, space, c = wr->cons / 8, p = wr->prod / 8; uint32_t i, space, c = wr->cons / 8, p = wr->prod / 8;
bool_t writeback = FALSE;
time_t t;
/* If we are processing final data then use the end index, rounded to /* If we are processing final data then use the end index, rounded to
* nearest. */ * nearest. */
@ -215,17 +202,6 @@ static bool_t qd_write_track(struct image *im)
if (flush) if (flush)
p = (write->bc_end + 4) / 8; p = (write->bc_end + 4) / 8;
if (im->qd.write_batch.len == 0) {
ASSERT(!im->qd.write_batch.dirty);
im->qd.write_batch.off = im->qd.trk_pos & ~511;
im->qd.write_batch.len = min_t(
uint32_t, batch_secs * 512,
((im->qd.trk_len + 511) & ~511) - im->qd.write_batch.off);
F_lseek(&im->fp, im->qd.trk_off + im->qd.write_batch.off);
F_read(&im->fp, wrbuf, im->qd.write_batch.len, NULL);
F_lseek(&im->fp, im->qd.trk_off + im->qd.write_batch.off);
}
for (;;) { for (;;) {
uint32_t off = im->qd.trk_pos; uint32_t off = im->qd.trk_pos;
@ -242,62 +218,45 @@ static bool_t qd_write_track(struct image *im)
if (nr == 0) if (nr == 0)
break; break;
/* Bail if required data not in the write buffer. */ /* Encode into the sector buffer for later write-out. */
if ((off < im->qd.write_batch.off) if ((w = file_cache_peek_write(im->qd.fcache, off & ~511)) == NULL) {
|| (off >= (im->qd.write_batch.off + im->qd.write_batch.len))) { flush = FALSE;
writeback = TRUE;
break; break;
} }
w += off & 511;
/* Encode into the sector buffer for later write-out. */
w = &wrbuf[off - im->qd.write_batch.off];
for (i = 0; i < nr; i++) for (i = 0; i < nr; i++)
*w++ = _rbit32(buf[c++ & bufmask]) >> 24; *w++ = _rbit32(buf[c++ & bufmask]) >> 24;
im->qd.write_batch.dirty = TRUE;
im->qd.trk_pos += nr; im->qd.trk_pos += nr;
if (im->qd.trk_pos >= im->qd.trk_len) { if (im->qd.trk_pos >= im->qd.trk_len) {
ASSERT(im->qd.trk_pos == im->qd.trk_len); ASSERT(im->qd.trk_pos == im->qd.trk_len);
im->qd.trk_pos = 0; im->qd.trk_pos = 0;
im->qd.write.wrapped = TRUE;
} }
} }
if (writeback) { if (flush)
/* If writeback requested then ensure we get called again. */ file_cache_sync(im->qd.fcache);
flush = FALSE; else
} else if (flush) { file_cache_progress(im->qd.fcache);
/* If this is the final call, we should do writeback. */
writeback = TRUE;
}
if (writeback && im->qd.write_batch.dirty) {
t = time_now();
printk("Write %u-%u (%u)... ",
im->qd.write_batch.off,
im->qd.write_batch.off + im->qd.write_batch.len - 1,
im->qd.write_batch.len);
F_write(&im->fp, wrbuf, im->qd.write_batch.len, NULL);
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
im->qd.write_batch.len = 0;
im->qd.write_batch.dirty = FALSE;
}
if (flush && im->qd.write.wrapped
&& (im->qd.trk_pos > im->qd.write.start))
printk("Wrapped (%u > %u)\n", im->qd.trk_pos, im->qd.write.start);
wr->cons = c * 8; wr->cons = c * 8;
return flush; return flush;
} }
static void qd_sync(struct image *im)
{
file_cache_sync_wait(im->qd.fcache);
file_cache_shutdown(im->qd.fcache);
}
const struct image_handler qd_image_handler = { const struct image_handler qd_image_handler = {
.open = qd_open, .open = qd_open,
.setup_track = qd_setup_track, .setup_track = qd_setup_track,
.read_track = qd_read_track, .read_track = qd_read_track,
.rdata_flux = qd_rdata_flux, .rdata_flux = qd_rdata_flux,
.write_track = qd_write_track, .write_track = qd_write_track,
.sync = qd_sync,
}; };
/* /*

View file

@ -20,6 +20,8 @@ uint8_t board_id;
#define DOWN 0x40 #define DOWN 0x40
#define UP 0x00 #define UP 0x00
#define INVALID 0xff
static GPIO gpio(uint8_t x) static GPIO gpio(uint8_t x)
{ {
switch (x&0x30) { switch (x&0x30) {
@ -42,14 +44,15 @@ static uint8_t inputs[] = {
GPIOA | 8 | DOWN, /* 22: Write Data */ GPIOA | 8 | DOWN, /* 22: Write Data */
GPIOB | 9 | DOWN, /* 24: Write Gate */ GPIOB | 9 | DOWN, /* 24: Write Gate */
GPIOB | 4 | DOWN, /* 32: Side Select */ GPIOB | 4 | DOWN, /* 32: Side Select */
};
#if 0 /* Buttons and encoder are handled via board.c */
/* Buttons and encoder inputs are switch-to-ground. So we pull them up. */ /* Buttons and encoder inputs are switch-to-ground. So we pull them up. */
GPIOC | 8 | UP , /* Button: Down/Left */ GPIOC | 8 | UP , /* Button: Down/Left */
GPIOC | 7 | UP , /* Button: Up/Right */ GPIOC | 7 | UP , /* Button: Up/Right */
GPIOC | 6 | UP , /* Button: Select (Jumper JA) */ GPIOC | 6 | UP , /* Button: Select (Jumper JA) */
GPIOC | 10 | UP , /* Rotary CLK (J7-1) */ GPIOC | 10 | UP , /* Rotary CLK (J7-1) */
GPIOC | 11 | UP , /* Rotary DAT (J7-2) */ GPIOC | 11 | UP , /* Rotary DAT (J7-2) */
}; #endif
static uint8_t outputs[] = { static uint8_t outputs[] = {
GPIOB | 7 , /* 2: Disk Change/Density */ GPIOB | 7 , /* 2: Disk Change/Density */
@ -66,11 +69,23 @@ static void io_test(bool_t assert)
uint8_t d[3] = { 0 }; uint8_t d[3] = { 0 };
char p[20], *q = p; char p[20], *q = p;
int i; int i, b;
/* 0-7 */
for (i = 0; i < ARRAY_SIZE(inputs); i++) { for (i = 0; i < ARRAY_SIZE(inputs); i++) {
uint8_t x = inputs[i]; uint8_t x = inputs[i];
if (gpio_read_pin(gpio(x), x&15)) { if ((x != INVALID) && gpio_read_pin(gpio(x), x&15)) {
*q++ = char_map[i];
d[i/7] |= 1 << (i%7);
} else {
*q++ = ' ';
}
}
/* 8-12 */
b = (~board_get_buttons() & 7) | (board_get_rotary() << 3);
for (; i < 13; i++) {
if (b & (1<<(i-8))) {
*q++ = char_map[i]; *q++ = char_map[i];
d[i/7] |= 1 << (i%7); d[i/7] |= 1 << (i%7);
} else { } else {
@ -114,7 +129,7 @@ int main(void)
int i; int i;
/* Relocate DATA. Initialise BSS. */ /* Relocate DATA. Initialise BSS. */
if (_sdat != _ldat) if (&_sdat[0] != &_ldat[0])
memcpy(_sdat, _ldat, _edat-_sdat); memcpy(_sdat, _ldat, _edat-_sdat);
memset(_sbss, 0, _ebss-_sbss); memset(_sbss, 0, _ebss-_sbss);
@ -125,7 +140,7 @@ int main(void)
board_init(); board_init();
delay_ms(200); /* 5v settle */ delay_ms(200); /* 5v settle */
printk("\n** FF I/O Test for Gotek\n", fw_ver); printk("\n** FF I/O Test %s for Gotek\n", fw_ver);
printk("** Keir Fraser <keir.xen@gmail.com>\n"); printk("** Keir Fraser <keir.xen@gmail.com>\n");
printk("** https://github.com/keirf/FlashFloppy\n\n"); printk("** https://github.com/keirf/FlashFloppy\n\n");
@ -134,12 +149,26 @@ int main(void)
display_init(); display_init();
display_setting(TRUE); display_setting(TRUE);
/* Standard Gotek: optional motor signal is PB15. */ if (mcu_package == MCU_QFN32) {
if (!gotek_enhanced()) inputs[6] = GPIOB | 1 | DOWN; /* wgate */
inputs[2] |= GPIOB; outputs[0] = GPIOA | 14; /* pin_02, dskchg/den */
outputs[2] = GPIOA | 13; /* pin_26, trk00 */
}
if (!gotek_enhanced()) {
inputs[1] = INVALID; /* no SELB */
if (has_kc30_header == 2) {
inputs[2] = GPIOB | 12 | DOWN;
} else {
/* Standard Gotek: optional motor signal is PB15. */
inputs[2] = GPIOB | 15 | DOWN;
}
}
for (i = 0; i < ARRAY_SIZE(inputs); i++) { for (i = 0; i < ARRAY_SIZE(inputs); i++) {
uint8_t x = inputs[i]; uint8_t x = inputs[i];
if (x == INVALID)
continue;
gpio_configure_pin(gpio(x), x&15, gpio_configure_pin(gpio(x), x&15,
(x&DOWN) ? GPI_pull_down : GPI_pull_up); (x&DOWN) ? GPI_pull_down : GPI_pull_up);
} }

View file

@ -83,6 +83,12 @@ void flashfloppy_fill_fileinfo(FIL *fp);
} while(0) } while(0)
#endif #endif
bool_t lba_within_fat_volume(uint32_t lba)
{
/* Also disallows access to the boot/bpb sector of the mounted volume. */
return (lba > fatfs.volbase) && (lba <= fatfs.volend);
}
static bool_t slot_valid(unsigned int i) static bool_t slot_valid(unsigned int i)
{ {
if (i > cfg.max_slot_nr) if (i > cfg.max_slot_nr)
@ -472,26 +478,21 @@ static uint8_t read_rotary(uint8_t rotary)
static struct timer button_timer; static struct timer button_timer;
static volatile uint8_t buttons, velocity; static volatile uint8_t buttons, velocity;
static uint8_t rotary, rb; static uint8_t rotary, rb;
#define B_LEFT 1
#define B_RIGHT 2
#define B_SELECT 4
void IRQ_rotary(void) void IRQ_rotary(void)
{ {
if ((ff_cfg.rotary & ROT_typemask) != ROT_full) if ((ff_cfg.rotary & ROT_typemask) != ROT_full)
return; return;
rotary = ((rotary << 2) | ((gpioc->idr >> 10) & 3)) & 15; rotary = ((rotary << 2) | board_get_rotary()) & 15;
rb = read_rotary(rotary) ?: rb; rb = read_rotary(rotary) ?: rb;
} }
static void set_rotary_exti(void) static void set_rotary_exti(void)
{ {
uint32_t imr; exti->imr &= ~board_rotary_exti_mask;
board_rotary_exti_mask = 0;
imr = exti->imr & ~0x0c00;
if ((ff_cfg.rotary & ROT_typemask) == ROT_full) if ((ff_cfg.rotary & ROT_typemask) == ROT_full)
imr |= 0x0c00; board_setup_rotary_exti();
exti->imr = imr;
} }
static void button_timer_fn(void *unused) static void button_timer_fn(void *unused)
@ -502,7 +503,7 @@ static void button_timer_fn(void *unused)
static uint16_t cur_time, prev_time; static uint16_t cur_time, prev_time;
static uint32_t _b[3]; /* 0 = left, 1 = right, 2 = select */ static uint32_t _b[3]; /* 0 = left, 1 = right, 2 = select */
uint8_t b = osd_buttons_rx; uint8_t x, b = osd_buttons_rx;
bool_t twobutton_rotary = bool_t twobutton_rotary =
(ff_cfg.twobutton_action & TWOBUTTON_mask) == TWOBUTTON_rotary; (ff_cfg.twobutton_action & TWOBUTTON_mask) == TWOBUTTON_rotary;
int i, twobutton_reverse = !!(ff_cfg.twobutton_action & TWOBUTTON_reverse); int i, twobutton_reverse = !!(ff_cfg.twobutton_action & TWOBUTTON_reverse);
@ -521,9 +522,11 @@ static void button_timer_fn(void *unused)
/* We debounce the switches by waiting for them to be pressed continuously /* We debounce the switches by waiting for them to be pressed continuously
* for 32 consecutive sample periods (32 * 2ms == 64ms) */ * for 32 consecutive sample periods (32 * 2ms == 64ms) */
x = ~board_get_buttons();
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
_b[i] <<= 1; _b[i] <<= 1;
_b[i] |= gpio_read_pin(gpioc, 8-i); _b[i] |= x & 1;
x >>= 1;
} }
if (_b[twobutton_reverse] == 0) if (_b[twobutton_reverse] == 0)
@ -535,7 +538,7 @@ static void button_timer_fn(void *unused)
if (_b[2] == 0) if (_b[2] == 0)
b |= B_SELECT; b |= B_SELECT;
rotary = ((rotary << 2) | ((gpioc->idr >> 10) & 3)) & 15; rotary = ((rotary << 2) | board_get_rotary()) & 15;
switch (ff_cfg.rotary & ROT_typemask) { switch (ff_cfg.rotary & ROT_typemask) {
case ROT_trackball: { case ROT_trackball: {
@ -606,13 +609,15 @@ static void button_timer_fn(void *unused)
static void canary_init(void) static void canary_init(void)
{ {
_irq_stackbottom[0] = _thread_stackbottom[0] = 0xdeadbeef; _irq_stackbottom[0] = _thread_stackbottom[0] = _thread1_stackbottom[0]
= 0xdeadbeef;
} }
static void canary_check(void) static void canary_check(void)
{ {
ASSERT(_irq_stackbottom[0] == 0xdeadbeef); ASSERT(_irq_stackbottom[0] == 0xdeadbeef);
ASSERT(_thread_stackbottom[0] == 0xdeadbeef); ASSERT(_thread_stackbottom[0] == 0xdeadbeef);
ASSERT(_thread1_stackbottom[0] == 0xdeadbeef);
} }
static void fix_hxc_short_slot(struct short_slot *short_slot) static void fix_hxc_short_slot(struct short_slot *short_slot)
@ -912,6 +917,30 @@ static uint8_t parse_pin_str(const char *s)
return pin; return pin;
} }
static uint16_t parse_display_order(const char *p)
{
int sh = 0;
uint16_t order = 0;
if (!strcmp(p, "default"))
return DORD_default;
while (p != NULL) {
order |= ((p[0]-'0')&7) << sh;
if (p[1] == 'd')
order |= DORD_double << sh;
sh += DORD_shift;
if ((p = strchr(p, ',')) == NULL)
break;
p++;
}
if (sh < 16)
order |= 0x7777 << sh;
return order;
}
static void read_ff_cfg(void) static void read_ff_cfg(void)
{ {
enum { enum {
@ -992,6 +1021,10 @@ static void read_ff_cfg(void)
ff_cfg.write_protect = !strcmp(opts.arg, "yes"); ff_cfg.write_protect = !strcmp(opts.arg, "yes");
break; break;
case FFCFG_max_cyl:
ff_cfg.max_cyl = strtol(opts.arg, NULL, 10);
break;
case FFCFG_side_select_glitch_filter: case FFCFG_side_select_glitch_filter:
ff_cfg.side_select_glitch_filter = strtol(opts.arg, NULL, 10); ff_cfg.side_select_glitch_filter = strtol(opts.arg, NULL, 10);
break; break;
@ -1175,6 +1208,10 @@ static void read_ff_cfg(void)
ff_cfg.display_type |= DISPLAY_inverse; ff_cfg.display_type |= DISPLAY_inverse;
} else if (!strcmp(p, "ztech")) { } else if (!strcmp(p, "ztech")) {
ff_cfg.display_type |= DISPLAY_ztech; 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;
} }
} }
} }
@ -1191,26 +1228,17 @@ static void read_ff_cfg(void)
ff_cfg.oled_contrast = strtol(opts.arg, NULL, 10); ff_cfg.oled_contrast = strtol(opts.arg, NULL, 10);
break; break;
case FFCFG_display_order: { case FFCFG_display_order:
char *p = opts.arg; ff_cfg.display_order = parse_display_order(opts.arg);
int sh = 0; break;
ff_cfg.display_order = DORD_default;
if (!strcmp(p, "default")) case FFCFG_osd_display_order:
break; ff_cfg.osd_display_order = parse_display_order(opts.arg);
ff_cfg.display_order = 0; break;
while (p != NULL) {
ff_cfg.display_order |= ((p[0]-'0')&7) << sh; case FFCFG_osd_columns:
if (p[1] == 'd') ff_cfg.osd_columns = strtol(opts.arg, NULL, 10);
ff_cfg.display_order |= DORD_double << sh;
sh += DORD_shift;
if ((p = strchr(p, ',')) == NULL)
break;
p++;
}
if (sh < 16)
ff_cfg.display_order |= 0x7777 << sh;
break; break;
}
case FFCFG_display_off_secs: case FFCFG_display_off_secs:
ff_cfg.display_off_secs = strtol(opts.arg, NULL, 10); ff_cfg.display_off_secs = strtol(opts.arg, NULL, 10);
@ -1254,6 +1282,25 @@ static void read_ff_cfg(void)
break; 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: case FFCFG_da_report_version:
memset(ff_cfg.da_report_version, 0, memset(ff_cfg.da_report_version, 0,
sizeof(ff_cfg.da_report_version)); sizeof(ff_cfg.da_report_version));
@ -1276,14 +1323,31 @@ static void read_ff_cfg(void)
static void process_ff_cfg_opts(const struct ff_cfg *old) static void process_ff_cfg_opts(const struct ff_cfg *old)
{ {
if (ff_cfg.rotary != old->rotary) /* chgrst, motor-delay: Reset EXTI handlers. */
set_rotary_exti(); if ((ff_cfg.motor_delay != old->motor_delay)
|| (ff_cfg.chgrst != old->chgrst)) {
exti->imr &= ~motor_chgrst_exti_mask;
motor_chgrst_exti_mask = 0;
}
/* interface, pin02, pin34: Inform the floppy subsystem. */ /* rotary, chgrst, motor-delay: Inform the rotary-encoder subsystem.
* It is harmless to reset rotary EXTI handlers unconditionally. */
set_rotary_exti();
/* interface, pin02, pin34, chgrst, motor-delay: Inform the floppy
* subsystem. */
if ((ff_cfg.interface != old->interface) if ((ff_cfg.interface != old->interface)
|| (ff_cfg.pin02 != old->pin02) || (ff_cfg.pin02 != old->pin02)
|| (ff_cfg.pin34 != old->pin34)) || (ff_cfg.pin34 != old->pin34)
|| (ff_cfg.motor_delay != old->motor_delay)
|| (ff_cfg.chgrst != old->chgrst)) {
floppy_set_fintf_mode(); floppy_set_fintf_mode();
motor_chgrst_setup_exti();
}
/* max-cyl: Inform the floppy subsystem. */
if (ff_cfg.max_cyl != old->max_cyl)
floppy_set_max_cyl();
/* ejected-on-startup: Set the ejected state appropriately. */ /* ejected-on-startup: Set the ejected state appropriately. */
if (ff_cfg.ejected_on_startup) if (ff_cfg.ejected_on_startup)
@ -1825,7 +1889,8 @@ indexed_mode:
fs->fp.fname[0] != '\0'; fs->fp.fname[0] != '\0';
F_findnext(&fs->dp, &fs->fp)) { F_findnext(&fs->dp, &fs->fp)) {
const char *p = fs->fp.fname const char *p = fs->fp.fname
+ strnlen(ff_cfg.indexed_prefix, 256); + strnlen(ff_cfg.indexed_prefix,
sizeof(ff_cfg.indexed_prefix));
unsigned int idx = 0; unsigned int idx = 0;
/* Skip directories. */ /* Skip directories. */
if (fs->fp.fattrib & AM_DIR) if (fs->fp.fattrib & AM_DIR)
@ -2079,6 +2144,8 @@ static int run_floppy(void *_b)
t_prev = t_now; t_prev = t_now;
} }
floppy_sync();
if (display_type == DT_LED_7SEG) if (display_type == DT_LED_7SEG)
display_state = LED_NORMAL; display_state = LED_NORMAL;
@ -2645,8 +2712,10 @@ static int floppy_main(void *unused)
b = B_SELECT; b = B_SELECT;
} else { } else {
floppy_arena_teardown(); floppy_arena_teardown();
speaker_notify_insert(cfg.slot_nr);
fres = F_call_cancellable(run_floppy, &b); fres = F_call_cancellable(run_floppy, &b);
floppy_cancel(); floppy_cancel();
speaker_notify_eject();
assert_volume_connected(); assert_volume_connected();
if ((b != 0) && (display_type == DT_LCD_OLED)) { if ((b != 0) && (display_type == DT_LCD_OLED)) {
/* Immediate visual indication of button press. */ /* Immediate visual indication of button press. */
@ -2775,6 +2844,24 @@ static bool_t main_menu_confirm(const char *op)
return wait_twobutton_press(b) == B_SELECT; 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) static void factory_reset(void)
{ {
/* Inform user that factory reset is about to occur. */ /* Inform user that factory reset is about to occur. */
@ -2807,6 +2894,8 @@ static void factory_reset(void)
static void update_firmware(void) static void update_firmware(void)
{ {
#if MCU == STM32F105
/* Power up the backup-register interface and allow writes. */ /* Power up the backup-register interface and allow writes. */
rcc->apb1enr |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN; rcc->apb1enr |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN;
pwr->cr |= PWR_CR_DBP; pwr->cr |= PWR_CR_DBP;
@ -2815,7 +2904,12 @@ static void update_firmware(void)
bkp->dr1[0] = 0xdead; bkp->dr1[0] = 0xdead;
bkp->dr1[1] = 0xbeef; bkp->dr1[1] = 0xbeef;
/* Reset everything (except backup registers). */ #elif MCU == AT32F435
_reset_flag = RESET_FLAG_BOOTLOADER;
#endif
system_reset(); system_reset();
} }
@ -2852,7 +2946,7 @@ static void ff_osd_configure(void)
static void main_menu(void) static void main_menu(void)
{ {
const static char *menu[] = { const static char *menu[] = {
"**Main Menu**", "MCU Info",
"Factory Reset", "Factory Reset",
"Update Firmware", "Update Firmware",
"Configure FF OSD", "Configure FF OSD",
@ -2875,6 +2969,7 @@ static void main_menu(void)
if (sel >= ARRAY_SIZE(menu)) if (sel >= ARRAY_SIZE(menu))
sel -= ARRAY_SIZE(menu); sel -= ARRAY_SIZE(menu);
lcd_write(0, 0, -1, "**Main Menu**");
lcd_write(0, 1, -1, menu[sel]); lcd_write(0, 1, -1, menu[sel]);
lcd_on(); lcd_on();
@ -2897,6 +2992,9 @@ static void main_menu(void)
while (buttons) while (buttons)
continue; continue;
switch (sel) { switch (sel) {
case 0: /* MCU Info */
mcu_info();
break;
case 1: /* Factory Reset */ case 1: /* Factory Reset */
if (main_menu_confirm("Reset")) if (main_menu_confirm("Reset"))
factory_reset(); factory_reset();
@ -2908,7 +3006,7 @@ static void main_menu(void)
case 3: /* Configure FF OSD */ case 3: /* Configure FF OSD */
ff_osd_configure(); ff_osd_configure();
break; break;
case 0: case 4: /* Exit */ case 4: /* Exit */
goto out; goto out;
} }
} }
@ -2919,33 +3017,52 @@ out:
display_mode = DM_normal; display_mode = DM_normal;
} }
static void banner(void) static void noinline banner(void)
{ {
char msg[2][25];
switch (display_type) { switch (display_type) {
case DT_LED_7SEG: case DT_LED_7SEG:
#if MCU == STM32F105
#define sep_ch "-"
#elif MCU == AT32F435
#define sep_ch "z"
#endif
led_7seg_write_string( led_7seg_write_string(
#if defined(LOGFILE) #if defined(LOGFILE)
"LOG" "LOG"
#elif defined(QUICKDISK) #elif defined(QUICKDISK)
(led_7seg_nr_digits() == 3) ? "Q-D" : "QD" (led_7seg_nr_digits() == 3) ? "Q"sep_ch"D" : "QD"
#else #else
(led_7seg_nr_digits() == 3) ? "F-F" : "FF" (led_7seg_nr_digits() == 3) ? "F"sep_ch"F" : "FF"
#endif #endif
); );
#undef sep_ch
break; break;
case DT_LCD_OLED: case DT_LCD_OLED:
lcd_clear(); lcd_clear();
display_mode = DM_banner; /* double height row 0 */ display_mode = DM_banner; /* double height row 0 */
#if MCU == STM32F105
lcd_write(0, 0, 0, "FlashFloppy"); lcd_write(0, 0, 0, "FlashFloppy");
lcd_write(0, 1, 0, "v"); #elif MCU == AT32F435
lcd_write(1, 1, 0, fw_ver); lcd_write(0, 0, 0, "FlashFloppy+");
#if defined(LOGFILE)
lcd_write(10, 1, 0, "[Log]");
#elif defined(QUICKDISK)
lcd_write(10, 1, 0, "[QD]");
#endif #endif
snprintf(msg[0], sizeof(msg[0]), "%s%s", fw_ver,
#if defined(LOGFILE)
" Log"
#elif defined(QUICKDISK)
" QD"
#else
""
#endif
);
snprintf(msg[1], sizeof(msg[1]), "%9s %dkB", msg[0], ram_kb);
lcd_write(0, 1, 0, msg[1]);
lcd_on(); lcd_on();
break; break;
} }
} }
@ -2989,6 +3106,21 @@ static void maybe_show_version(void)
if (nb) if (nb)
return; 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);
led_7seg_write_decimal(ram_kb);
delay_ms(1000);
led_7seg_write_string("UER");
delay_ms(1000);
/* Iterate through the dotted sections of the version number. */ /* Iterate through the dotted sections of the version number. */
for (p = fw_ver; p != NULL; p = np ? np+1 : NULL) { for (p = fw_ver; p != NULL; p = np ? np+1 : NULL) {
np = strchr(p, '.'); np = strchr(p, '.');
@ -3058,7 +3190,7 @@ int main(void)
FRESULT fres; FRESULT fres;
/* Relocate DATA. Initialise BSS. */ /* Relocate DATA. Initialise BSS. */
if (_sdat != _ldat) if (&_sdat[0] != &_ldat[0])
memcpy(_sdat, _ldat, _edat-_sdat); memcpy(_sdat, _ldat, _edat-_sdat);
memset(_sbss, 0, _ebss-_sbss); memset(_sbss, 0, _ebss-_sbss);
@ -3066,13 +3198,13 @@ int main(void)
stm32_init(); stm32_init();
time_init(); time_init();
console_init(); console_init();
console_crash_on_input();
board_init(); board_init();
console_crash_on_input();
delay_ms(200); /* 5v settle */ delay_ms(200); /* 5v settle */
printk("\n** FlashFloppy v%s for Gotek\n", fw_ver); printk("\n** FlashFloppy %s\n", fw_ver);
printk("** Keir Fraser <keir.xen@gmail.com>\n"); printk("** Keir Fraser <keir.xen@gmail.com>\n");
printk("** https://github.com/keirf/FlashFloppy\n\n"); printk("** github:keirf/flashfloppy\n\n");
printk("Build: %s %s\n", build_date, build_time); printk("Build: %s %s\n", build_date, build_time);
printk("Board: %s\n", board_name[board_id]); printk("Board: %s\n", board_name[board_id]);
@ -3101,7 +3233,7 @@ int main(void)
usbh_msc_init(); usbh_msc_init();
rotary = (gpioc->idr >> 10) & 3; rotary = board_get_rotary();
set_rotary_exti(); set_rotary_exti();
timer_init(&button_timer, button_timer_fn, NULL); timer_init(&button_timer, button_timer_fn, NULL);
timer_set(&button_timer, time_now()); timer_set(&button_timer, time_now());

140
src/mcu_at32f435.c Normal file
View file

@ -0,0 +1,140 @@
/*
* mcu_at342f435.c
*
* Core and peripheral registers.
*
* 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>.
*/
bool_t is_artery_mcu = TRUE;
unsigned int flash_page_size = FLASH_PAGE_SIZE;
unsigned int ram_kb = 384;
static void clock_init(void)
{
/* Enable PWR interface so we can set the LDO boost. */
rcc->apb1enr |= RCC_APB1ENR_PWREN;
/* Bootloader leaves MISC1 set up for USB clocked from HICK.
* Clear MISC1 register to its reset value. */
rcc->misc1 = 0;
/* 288MHz requires LDO voltage boost. */
pwr->ldoov = PWR_LDOOV_1V3;
flash->divr = FLASH_DIVR_DIV_3;
/* Start up the external oscillator. */
rcc->cr |= RCC_CR_HSEON;
while (!(rcc->cr & RCC_CR_HSERDY))
cpu_relax();
/* Enable auto-step. */
rcc->misc2 |= RCC_MISC2_AUTOSTEP;
/* Configure PLL for 8MHz input, 288MHz output. */
rcc->pllcfgr = (RCC_PLLCFGR_PLLSRC_HSE | /* PLLSrc = HSE = 8MHz */
RCC_PLLCFGR_PLL_MS(1) | /* PLL In = HSE/1 = 8MHz */
RCC_PLLCFGR_PLL_NS(72) | /* PLLVCO = 8MHz*72 = 576MHz */
RCC_PLLCFGR_PLL_FR(PLL_FR_2)); /* PLL Out = 576MHz/2 */
/* Bus divisors. */
rcc->cfgr = (RCC_CFGR_PPRE2(4) | /* APB2 = 288MHz/2 = 144MHz */
RCC_CFGR_PPRE1(4) | /* APB1 = 288MHz/2 = 144MHz */
RCC_CFGR_HPRE(0)); /* AHB = 288MHz/1 = 288MHz */
/* Enable and stabilise the PLL. */
rcc->cr |= RCC_CR_PLLON;
while (!(rcc->cr & RCC_CR_PLLRDY))
cpu_relax();
/* Switch to the externally-driven PLL for system clock. */
rcc->cfgr |= RCC_CFGR_SW(2);
while ((rcc->cfgr & RCC_CFGR_SWS(3)) != RCC_CFGR_SWS(2))
cpu_relax();
/* Internal oscillator no longer needed. */
rcc->cr &= ~RCC_CR_HSION;
/* Disable auto-step. */
rcc->misc2 &= ~RCC_MISC2_AUTOSTEP;
}
static void peripheral_init(void)
{
/* Enable basic GPIO clocks, DTCM RAM, DMA, and EXTICR. */
rcc->ahb1enr |= (RCC_AHB1ENR_DMA1EN |
RCC_AHB1ENR_GPIOHEN |
RCC_AHB1ENR_GPIOCEN |
RCC_AHB1ENR_GPIOBEN |
RCC_AHB1ENR_GPIOAEN);
rcc->apb1enr |= (RCC_APB1ENR_TIM2EN |
RCC_APB1ENR_TIM3EN |
RCC_APB1ENR_TIM4EN |
RCC_APB1ENR_TIM5EN);
rcc->apb2enr |= (RCC_APB2ENR_SYSCFGEN |
RCC_APB2ENR_TIM1EN);
/* Flexible DMA request mappings. */
dmamux1->sel = DMAMUX_SEL_TBL_SEL;
dmamux2->sel = DMAMUX_SEL_TBL_SEL;
/* Release JTAG pins. */
gpio_configure_pin(gpioa, 15, GPI_floating);
gpio_configure_pin(gpiob, 3, GPI_floating);
gpio_configure_pin(gpiob, 4, GPI_floating);
}
void stm32_init(void)
{
cortex_init();
clock_init();
peripheral_init();
cpu_sync();
}
void gpio_configure_pin(GPIO gpio, unsigned int pin, unsigned int mode)
{
gpio_write_pin(gpio, pin, mode >> 7);
gpio->moder = (gpio->moder & ~(3<<(pin<<1))) | ((mode&3)<<(pin<<1));
mode >>= 2;
gpio->otyper = (gpio->otyper & ~(1<<pin)) | ((mode&1)<<pin);
mode >>= 1;
gpio->odrvr = (gpio->odrvr & ~(3<<(pin<<1))) | ((mode&3)<<(pin<<1));
mode >>= 2;
gpio->pupdr = (gpio->pupdr & ~(3<<(pin<<1))) | ((mode&3)<<(pin<<1));
}
void gpio_set_af(GPIO gpio, unsigned int pin, unsigned int af)
{
if (pin < 8) {
gpio->afrl = (gpio->afrl & ~(15<<(pin<<2))) | (af<<(pin<<2));
} else {
pin -= 8;
gpio->afrh = (gpio->afrh & ~(15<<(pin<<2))) | (af<<(pin<<2));
}
}
void _exti_route(unsigned int px, unsigned int pin)
{
unsigned int n = pin >> 2;
unsigned int s = (pin & 3) << 2;
uint32_t exticr = syscfg->exticr[n];
ASSERT(!in_exception()); /* no races please */
exticr &= ~(0xf << s);
exticr |= px << s;
syscfg->exticr[n] = exticr;
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

161
src/mcu_stm32f105.c Normal file
View file

@ -0,0 +1,161 @@
/*
* mcu_stm32f105.c
*
* Core and peripheral registers.
*
* 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>.
*/
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;
static void identify_mcu(void)
{
/* DBGMCU_IDCODE (E0042000):
* STM32F105RB: 10016418 (device id: 418)
* AT32F415CBT7: 700301c5 (device id: 1c5)
* AT32F415RCT7: 70030240 (device id: 240)
* However the AT32 IDCODE values are undocumented so we cannot rely
* on them (for example, what will be the ID for chips with differing
* amounts of Flash, or numbers of pins?) */
/* We detect an Artery MCU by presence of Cortex-M4 CPUID.
* Cortex-M4: 41xfc24x ; Cortex-M3: 41xfc23x */
is_artery_mcu = ((scb->cpuid >> 4) & 0xf) == 4;
if (is_artery_mcu) {
unsigned int flash_kb = *(uint16_t *)0x1ffff7e0;
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(sysclk_mhz/32);
/* Start up the external oscillator. */
rcc->cr |= RCC_CR_HSEON;
while (!(rcc->cr & RCC_CR_HSERDY))
cpu_relax();
/* PLLs, scalers, muxes. */
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;
}
static void gpio_init(GPIO gpio)
{
/* Floating Input. Reference Manual states that JTAG pins are in PU/PD
* mode at reset, so ensure all PU/PD are disabled. */
gpio->crl = gpio->crh = 0x44444444u;
}
static void peripheral_init(void)
{
/* Enable basic GPIO and AFIO clocks, all timers, and DMA. */
rcc->apb1enr = (RCC_APB1ENR_TIM2EN |
RCC_APB1ENR_TIM3EN |
RCC_APB1ENR_TIM4EN);
rcc->apb2enr = (RCC_APB2ENR_IOPAEN |
RCC_APB2ENR_IOPBEN |
RCC_APB2ENR_IOPCEN |
RCC_APB2ENR_AFIOEN |
RCC_APB2ENR_TIM1EN);
rcc->ahbenr = RCC_AHBENR_DMA1EN;
/* Turn off serial-wire JTAG and reclaim the GPIOs. */
afio->mapr = AFIO_MAPR_SWJ_CFG_DISABLED;
/* All pins in a stable state. */
gpio_init(gpioa);
gpio_init(gpiob);
gpio_init(gpioc);
}
void stm32_init(void)
{
cortex_init();
identify_mcu();
clock_init();
peripheral_init();
cpu_sync();
}
void gpio_configure_pin(GPIO gpio, unsigned int pin, unsigned int mode)
{
gpio_write_pin(gpio, pin, mode >> 4);
mode &= 0xfu;
if (pin >= 8) {
pin -= 8;
gpio->crh = (gpio->crh & ~(0xfu<<(pin<<2))) | (mode<<(pin<<2));
} else {
gpio->crl = (gpio->crl & ~(0xfu<<(pin<<2))) | (mode<<(pin<<2));
}
}
void _exti_route(unsigned int px, unsigned int pin)
{
unsigned int n = pin >> 2;
unsigned int s = (pin & 3) << 2;
uint32_t exticr = afio->exticr[n];
ASSERT(!in_exception()); /* no races please */
exticr &= ~(0xf << s);
exticr |= px << s;
afio->exticr[n] = exticr;
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

View file

@ -12,7 +12,7 @@
#define GPI_bus GPI_floating #define GPI_bus GPI_floating
#define GPO_bus GPO_pushpull(_2MHz, HIGH) #define GPO_bus GPO_pushpull(_2MHz, HIGH)
#define GPO_rdata GPO_pushpull(_2MHz, LOW) #define GPO_rdata GPO_pushpull(_2MHz, LOW)
#define AFO_rdata (AFO_pushpull(_2MHz) | (LOW<<4)) #define AFO_rdata _AFO_pushpull(_2MHz,LOW)
/* READY-window state machine and timer handling. */ /* READY-window state machine and timer handling. */
static struct window { static struct window {
@ -51,7 +51,11 @@ static volatile struct {
} pins; } pins;
#define read_pin(pin) pins.pin #define read_pin(pin) pins.pin
#define write_pin(pin, level) ({ \ #define write_pin(pin, level) ({ \
gpio_write_pin(gpio_out, pin_##pin, level); \ unsigned int __pin = pin_##pin; \
if (__pin >= 16) \
gpio_write_pin(gpioa, __pin-16, level); \
else \
gpio_write_pin(gpiob, __pin, level); \
pins.pin = level; }) pins.pin = level; })
#include "floppy_generic.c" #include "floppy_generic.c"
@ -96,6 +100,20 @@ void floppy_set_fintf_mode(void)
/* Quick Disk interface is static. */ /* Quick Disk interface is static. */
} }
void floppy_set_max_cyl(void)
{
/* Quick Disk has no STEP signal. */
}
static void drive_configure_output_pin(unsigned int pin)
{
if (pin >= 16) {
gpio_configure_pin(gpioa, pin-16, GPO_bus);
} else {
gpio_configure_pin(gpiob, pin, GPO_bus);
}
}
void floppy_init(void) void floppy_init(void)
{ {
struct drive *drv = &drive; struct drive *drv = &drive;
@ -103,21 +121,18 @@ void floppy_init(void)
floppy_set_fintf_mode(); floppy_set_fintf_mode();
printk("Interface: QuickDisk, JC=%s\n", printk("Interface: QuickDisk, JC=%s\n",
!gpio_read_pin(gpiob, 1) ? "On (Roland)" : "Off"); qd_roland_mode() ? "On (Roland)" : "Off");
board_floppy_init(); board_floppy_init();
timer_init(&motor.timer, motor_timer, drv); timer_init(&motor.timer, motor_timer, drv);
timer_init(&window.timer, window_timer, drv); timer_init(&window.timer, window_timer, drv);
gpio_configure_pin(gpio_out, pin_02, GPO_bus); drive_configure_output_pin(pin_02);
gpio_configure_pin(gpio_out, pin_08, GPO_bus); drive_configure_output_pin(pin_08);
gpio_configure_pin(gpio_out, pin_26, GPO_bus); drive_configure_output_pin(pin_26);
gpio_configure_pin(gpio_out, pin_28, GPO_bus); drive_configure_output_pin(pin_28);
gpio_configure_pin(gpio_out, pin_34, GPO_bus); drive_configure_output_pin(pin_34);
gpio_configure_pin(gpio_data, pin_wdata, GPI_bus);
gpio_configure_pin(gpio_data, pin_rdata, GPO_rdata);
write_pin(media, HIGH); write_pin(media, HIGH);
write_pin(wrprot, HIGH); write_pin(wrprot, HIGH);
@ -128,12 +143,20 @@ void floppy_init(void)
timer_init(&index.timer, index_assert, NULL); timer_init(&index.timer, index_assert, NULL);
} }
static void io_thread_main(void *arg) {
while (1) {
F_async_drain();
thread_yield();
}
}
void floppy_insert(unsigned int unit, struct slot *slot) void floppy_insert(unsigned int unit, struct slot *slot)
{ {
floppy_mount(slot); floppy_mount(slot);
timer_dma_init(); timer_dma_init();
tim_rdata->ccr2 = sysclk_ns(1500); /* RD: 1.5us positive pulses */ thread_start(&drive.io_thread, _thread1_stacktop, io_thread_main, NULL);
tim_rdata->ccr2 = sampleclk_ns(1500); /* RD: 1.5us positive pulses */
/* Drive is ready. Set output signals appropriately. */ /* Drive is ready. Set output signals appropriately. */
write_pin(media, LOW); write_pin(media, LOW);
@ -233,9 +256,8 @@ static bool_t dma_rd_handle(struct drive *drv)
case DMA_inactive: { case DMA_inactive: {
time_t read_start_pos = window.paused ? window.pause_pos : 0; time_t read_start_pos = window.paused ? window.pause_pos : 0;
read_start_pos %= drv->image->stk_per_rev; read_start_pos %= drv->image->stk_per_rev;
read_start_pos *= SYSCLK_MHZ/STK_MHZ; read_start_pos *= SAMPLECLK_MHZ/STK_MHZ;
if (image_setup_track(drv->image, 0, &read_start_pos)) image_setup_track(drv->image, 0, &read_start_pos);
return TRUE;
/* Change state /then/ check for race against step or side change. */ /* Change state /then/ check for race against step or side change. */
dma_rd->state = DMA_starting; dma_rd->state = DMA_starting;
barrier(); barrier();

View file

@ -9,16 +9,10 @@
* See the file COPYING for more details, or visit <http://unlicense.org>. * See the file COPYING for more details, or visit <http://unlicense.org>.
*/ */
#if 1 #define PCLK_MHZ APB1_MHZ
/* We can now switch to Default Speed (25MHz). Closest we can get is 36Mhz/2 = #define INIT_SPEED_KHZ 400
* 18MHz. */ #define DEFAULT_SPEED_KHZ 25000
#define DEFAULT_SPEED_DIV SPI_CR1_BR_DIV2 /* 18MHz */
#define SPI_PIN_SPEED _50MHz #define SPI_PIN_SPEED _50MHz
#else
/* Best speed I can reliably achieve right now is 9Mbit/s. */
#define DEFAULT_SPEED_DIV SPI_CR1_BR_DIV4 /* 9MHz */
#define SPI_PIN_SPEED _10MHz
#endif
#if 0 #if 0
#define TRC(f, a...) printk("SD: " f, ## a) #define TRC(f, a...) printk("SD: " f, ## a)
@ -66,13 +60,15 @@ static void spi_release(void)
static uint8_t wait_ready(void) static uint8_t wait_ready(void)
{ {
stk_time_t start = stk_now(); time_t start = time_now();
uint8_t res; uint8_t res = 0xff;
/* Wait 500ms for card to be ready. */ /* Wait 500ms for card to be ready. */
do { do {
if (res != 0xff)
thread_yield();
res = spi_recv8(spi); res = spi_recv8(spi);
} while ((res != 0xff) && (stk_timesince(start) < stk_ms(500))); } while ((res != 0xff) && (time_since(start) < time_ms(500)));
return res; return res;
} }
@ -171,14 +167,16 @@ static uint8_t send_cmd(uint8_t cmd, uint32_t arg)
static bool_t datablock_recv(BYTE *buff, uint16_t bytes) static bool_t datablock_recv(BYTE *buff, uint16_t bytes)
{ {
uint8_t token, _crc[2]; uint8_t token = 0, _crc[2];
uint32_t start = stk_now();
uint16_t todo, w, crc; uint16_t todo, w, crc;
time_t start = time_now();
/* Wait 100ms for data to be ready. */ /* Wait 100ms for data to be ready. */
do { do {
if (token == 0xff)
thread_yield();
token = spi_recv8(spi); token = spi_recv8(spi);
} while ((token == 0xff) && (stk_timesince(start) < stk_ms(100))); } while ((token == 0xff) && (time_since(start) < time_ms(100)));
if (token != 0xfe) /* valid data token? */ if (token != 0xfe) /* valid data token? */
return FALSE; return FALSE;
@ -270,9 +268,26 @@ static bool_t sd_inserted(void)
return gpio_read_pin(gpioc, 9); return gpio_read_pin(gpioc, 9);
} }
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] */
spi->cr1 = cr1 | ((br & 7) << 3); /* CR1[5:4] := MDIV[2:0] */
}
static DSTATUS sd_disk_initialize(BYTE pdrv) static DSTATUS sd_disk_initialize(BYTE pdrv)
{ {
uint32_t start, cr1; time_t start;
uint32_t cr1;
uint16_t rcv; uint16_t rcv;
uint8_t i; uint8_t i;
@ -288,16 +303,24 @@ static DSTATUS sd_disk_initialize(BYTE pdrv)
/* Enable external I/O pins. */ /* Enable external I/O pins. */
gpio_configure_pin(gpiob, PIN_CS, GPO_pushpull(SPI_PIN_SPEED, HIGH)); gpio_configure_pin(gpiob, PIN_CS, GPO_pushpull(SPI_PIN_SPEED, HIGH));
#if MCU == STM32F105
gpio_configure_pin(gpiob, 13, AFO_pushpull(SPI_PIN_SPEED)); /* CK */ gpio_configure_pin(gpiob, 13, AFO_pushpull(SPI_PIN_SPEED)); /* CK */
gpio_configure_pin(gpiob, 14, GPI_pull_up); /* MISO */ gpio_configure_pin(gpiob, 14, GPI_pull_up); /* MISO */
gpio_configure_pin(gpiob, 15, AFO_pushpull(SPI_PIN_SPEED)); /* MOSI */ gpio_configure_pin(gpiob, 15, AFO_pushpull(SPI_PIN_SPEED)); /* MOSI */
#elif MCU == AT32F435
gpio_set_af(gpiob, 13, 5);
gpio_configure_pin(gpiob, 13, AFO_pushpull(SPI_PIN_SPEED)); /* CK */
gpio_set_af(gpiob, 14, 5);
gpio_configure_pin(gpiob, 14, AFI(PUPD_up)); /* MISO */
gpio_set_af(gpiob, 15, 5);
gpio_configure_pin(gpiob, 15, AFO_pushpull(SPI_PIN_SPEED)); /* MOSI */
#endif
/* Configure SPI: 8-bit mode, MSB first, CPOL Low, CPHA Leading Edge. */ /* Configure SPI: 8-bit mode, MSB first, CPOL Low, CPHA Leading Edge. */
spi->cr2 = 0;
cr1 = (SPI_CR1_MSTR | /* master */ cr1 = (SPI_CR1_MSTR | /* master */
SPI_CR1_SSM | SPI_CR1_SSI | /* software NSS */ SPI_CR1_SSM | SPI_CR1_SSI | /* software NSS */
SPI_CR1_SPE); SPI_CR1_SPE);
spi->cr1 = cr1 | SPI_CR1_BR_DIV128; /* ~281kHz (<400kHz) */ sd_spi_set_cr(cr1, INIT_SPEED_KHZ);
/* Drain SPI I/O. */ /* Drain SPI I/O. */
spi_quiesce(spi); spi_quiesce(spi);
@ -328,9 +351,9 @@ static DSTATUS sd_disk_initialize(BYTE pdrv)
} }
/* Request SDHC/SDXC and start card initialisation. */ /* Request SDHC/SDXC and start card initialisation. */
start = stk_now(); start = time_now();
while (send_cmd(ACMD(41), 1u<<30)) { while (send_cmd(ACMD(41), 1u<<30)) {
if (stk_timesince(start) >= stk_ms(1000)) if (time_since(start) >= time_ms(1000))
goto out; /* initialisation timeout */ goto out; /* initialisation timeout */
} }
@ -359,9 +382,9 @@ static DSTATUS sd_disk_initialize(BYTE pdrv)
} }
/* Wait for card initialisation. */ /* Wait for card initialisation. */
start = stk_now(); start = time_now();
while (send_cmd(cmd, 0)) { while (send_cmd(cmd, 0)) {
if (stk_timesince(start) >= stk_ms(1000)) if (time_since(start) >= time_ms(1000))
goto out; /* initialisation timeout */ goto out; /* initialisation timeout */
} }
@ -379,7 +402,7 @@ out:
if (!(status & STA_NOINIT)) { if (!(status & STA_NOINIT)) {
delay_us(10); /* XXX small delay here stops SPI getting stuck?? */ delay_us(10); /* XXX small delay here stops SPI getting stuck?? */
spi->cr1 = cr1 | DEFAULT_SPEED_DIV; sd_spi_set_cr(cr1, DEFAULT_SPEED_KHZ);
printk("SD Card configured\n"); printk("SD Card configured\n");
dump_cid_info(); dump_cid_info();
} else { } else {

Some files were not shown because too many files have changed in this diff Show more