Compare commits

...

428 commits

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

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

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

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

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

Original commit message by ejona86:

hfe: Compensate for opcodes during seeking

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

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

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

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

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

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

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

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

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

Allows padding of short sectors for uniformly-indexed fiels.

Refs #751
2023-02-01 08:58:30 +00:00
Keir Fraser
13d743d9b5 mk_qd.py: Allow QD image lead-in to be configured on command line 2023-01-30 18:01:25 +00:00
Keir Fraser
a9b8f8d45b IMG.CFG: Fix scoping of per-track sector sizes.
Refs #751
2023-01-30 17:59:52 +00:00
Keir Fraser
d44a5e72ee FF.CFG: New option notify-volume= for notifying on insert/eject via speaker.
Refs #716
2023-01-17 16:01:26 +00:00
Keir Fraser
8b203cd0ee i2c: Remove unnecessary static initialiser for i2c peripheral.
Avoids build error on older versions of GCC:
"initializer element is not constant"
2023-01-16 14:43:08 +00:00
Keir Fraser
9c73940f89 Main Menu: New option "MCU Info" 2023-01-15 13:58:14 +00:00
Keir Fraser
75877cb8eb AT32F415: Run at 144MHz 2023-01-15 13:23:37 +00:00
Keir Fraser
c7a3c761df SFRKC30.AT4.35: Rotary input pins are always routed to EXTI.
There is never a conflict with the Motor input pin at PB12.
2023-01-12 12:54:50 +00:00
Keir Fraser
6ada2a88a7 IMG.CFG: Add files for Ensoniq and Sequential Circuits hosts 2023-01-03 10:55:10 +00:00
Keir Fraser
4db76683d7 IMG.CFG: Allow sector size to be specified per sector. 2022-12-20 08:10:05 +00:00
Keir Fraser
96aeecc0bb FF.CFG, OLED: New display-type sub-option "hflip". 2022-12-20 08:09:45 +00:00
Keir Fraser
39f15211de IMG.CFG: Dynacord ADS 2022-12-17 17:41:53 +00:00
Keir Fraser
e787a59862 Roland: Update IMG.CFG for 1.44MB sector skew observed on MT-200. 2022-12-15 17:37:07 +00:00
Keir Fraser
bc5e2c44ca Update to v3.38 2022-12-04 14:16:48 +00:00
Keir Fraser
027d19ee10 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:14:35 +00:00
Keir Fraser
e912ef4cf0 github: Rewrite release creation rules 2022-11-07 08:23:26 +00:00
Keir Fraser
dbe9480d5f SF7: Support Sega SF-7000 *.SF7 images 2022-11-06 19:32:48 +00:00
Keir Fraser
df0d5e5a89 github: Use latest versions of checkout and upload-artifact actions 2022-11-06 19:12:19 +00:00
Keir Fraser
8e8238c8ab github: Rewrite deprecated set-output commands 2022-11-06 19:00:14 +00:00
Keir Fraser
50462f5a6d Examples: Fix Osborne IMG.CFG to use cskew instead of skew. 2022-10-20 14:13:18 +01:00
Keir Fraser
b348ae1687 Update to v3.37 2022-10-20 11:40:32 +01:00
Keir Fraser
6de7cc7151 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:33:32 +01:00
Keir Fraser
fc161f4e05 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-18 15:52:11 +01:00
Keir Fraser
d6731dd1dc Update to v3.36 2022-10-10 14:51:25 +01:00
Keir Fraser
c1f1240c45 AT32F435: Fix SD support over SPI. 2022-10-10 07:44:35 +01:00
Keir Fraser
080063e891 FF.CFG: New option osd-columns= to set number of OSD text columns. 2022-10-09 17:12:06 +01:00
Keir Fraser
9deb3b5f0c Improve code comments in update_amiga_id(). 2022-10-09 12:34:22 +01:00
Keir Fraser
8637c47cbf New FF.CFG option osd-display-order=, analagous to LCD/OLED display-order= 2022-10-09 12:01:22 +01:00
Keir Fraser
e137eaccf9 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:01:07 +01:00
Keir Fraser
620ee339dd QD: Fix debug logging of interface mode (Roland vs not-Roland).
Refs #583
2022-10-02 10:50:59 +01:00
Keir Fraser
32116708cb Update to v3.35 2022-08-04 13:54:22 +01:00
Keir Fraser
2d31c042f8 AT32F435: Fix QuickDisk window calculation.
The relationship between STK and SAMPLECLK must be included as a factor.
2022-08-02 17:44:19 +01:00
Keir Fraser
19042f251f 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:17:53 +01:00
Keir Fraser
fdf1c936eb 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:34:55 +01:00
Keir Fraser
ad688c18a4 build: VPATH is not needed. 2022-07-10 10:30:01 +01:00
Eric Anderson
d5b4ec76cb 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:48:34 -07:00
Eric Anderson
317b34715d Makefile: Use VPATH to avoid rsync
Also add + to mkdir lines to fix --dry-run.
2022-07-09 15:59:18 -07:00
Keir Fraser
d59265c05c Update to v3.34 2022-07-04 14:55:23 +01:00
Keir Fraser
7278acc522 AT32F435: Cache HFE and QD image data. There is plenty of RAM. 2022-07-04 14:34:50 +01:00
Keir Fraser
edb2686d57 AT32F435: Fix LDO boost (to 1.3v) and Flash divider before configuring 288MHz SYSCLK. 2022-07-04 13:03:46 +01:00
Keir Fraser
4ef0e2a59e board_get_buttons: Remove extraneous code comment 2022-07-04 08:38:11 +01:00
Keir Fraser
b6605d0a1e io_test: Fix for newer board versions 2022-07-04 08:27:41 +01:00
Keir Fraser
2fc8b68786 board_get_buttons: Fix order of Left & Right buttons. 2022-07-04 08:26:48 +01:00
Keir Fraser
8ad53eb84c Replace is_*pin_mcu booleans with an mcu_package enumeration. 2022-06-27 14:02:31 +01:00
Keir Fraser
31751c77cc Allow rotary encoder at PA13/PA14 on any LQFP48 or LQFP64 MCU 2022-06-27 13:12:57 +01:00
Keir Fraser
a259e23bc6 build: Check that output Hex files fit within their assigned Flash space.
Refs #660
2022-06-24 12:20:24 +01:00
Keir Fraser
66bd55b483 GCC12, FatFs: Avoid uninitialised assignment error in f_open().
Refs #660
2022-06-23 10:12:45 +01:00
Keir Fraser
f4ccf03d20 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:14:24 +01:00
Keir Fraser
98a0d92d32 Update to v3.33 2022-06-20 11:48:41 +01:00
Keir Fraser
b4ae0d895d Shorten debug banners. Squeezed for space on 105 debug build. 2022-06-17 17:33:36 +01:00
Keir Fraser
f39540e1d2 Rename the HEX and DFU dist files for clarity. 2022-06-17 17:20:38 +01:00
Keir Fraser
b66e26ffe0 Revert "Temporarily disable AT32F435 artifacts in dist tarball."
This reverts commit ffaac201ee.
2022-06-17 17:10:53 +01:00
Keir Fraser
b5ee30b518 FlashFloppy+: Improve banner text and 7-seg LED info text. 2022-06-17 17:10:00 +01:00
Keir Fraser
286ab03196 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:57:19 +01:00
Keir Fraser
ddff7d882a 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:57:17 +01:00
Keir Fraser
b14bd8baec 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:32:14 +01:00
Keir Fraser
370bf83618 Don't inline _IRQ_SELA_changed into Amiga_HD_ID.
It's unnecessary and costs ~100 bytes RAM.
2022-06-06 15:31:13 +01:00
Keir Fraser
4adeb4a877 Update to v3.32 2022-05-27 22:06:52 +01:00
Keir Fraser
328f642d16 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:22:47 +01:00
Keir Fraser
ea44c1d600 Reintroduce the 8x16 font, build of which was broken by AT32F435 patches. 2022-05-25 16:52:37 +01:00
Keir Fraser
d593c6297d GitHub release: Better release name, and fix RELEASE_NOTES link 2022-05-23 13:18:54 +01:00
Keir Fraser
983a684435 Update to v3.31 2022-05-23 13:05:27 +01:00
Keir Fraser
ffaac201ee Temporarily disable AT32F435 artifacts in dist tarball. 2022-05-23 13:00:42 +01:00
Keir Fraser
5854c43240 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:50:50 +01:00
Keir Fraser
6db4ae58ac oled: New display-type option '-slow' to run I2C bus slower.
Refs #640
2022-05-06 17:09:37 +01:00
Keir Fraser
27e94efddf QD: Workaround for missing JC strap on SFRKC30.AT2
Refs #583
2022-05-04 10:32:46 +01:00
Keir Fraser
f3fae16582 at32f435: Extend NVIC registers to include F435's extra IRQ lines.
Fixes build error on GCC 10.
2022-04-26 11:57:52 +01:00
Keir Fraser
5489db3dbf dist: Include a plain-text README file. 2022-04-22 15:02:34 +01:00
Keir Fraser
34e067b41d Default version is git commit hash. Use VER consistently throughout firmware. 2022-04-14 15:04:13 +01:00
Keir Fraser
d7d3897a47 Remove 'v' prefix from version number usage. 2022-04-14 14:53:33 +01:00
Keir Fraser
2e3a130ac7 New universal UPD file format, includes firmware for both STM32F105 and AT32F435. 2022-04-14 14:20:32 +01:00
Keir Fraser
6947d82c7b Makefile: Get rid of dodgy use of $eval within rules. 2022-03-15 13:18:20 +00:00
Keir Fraser
b0803794d1 Small fixes to new build system 2022-03-15 12:43:24 +00:00
Keir Fraser
673a0afe0c Makefile: Define makefile variable for sudo ($(SUDO)) 2022-03-15 11:22:35 +00:00
Keir Fraser
3567abee63 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:36:07 +00:00
Keir Fraser
0c174ec927 build: Clean up RPATH into new build variable SRCDIR. 2022-03-13 16:00:56 +00:00
Keir Fraser
773d173717 Modify build system to place artefacts in separate out/ folder 2022-03-13 15:27:12 +00:00
Keir Fraser
7f5642abe0 AT32F435: Initial port 2022-03-10 16:05:53 +00:00
Keir Fraser
71d33fbd57 Fix parallel make 2022-02-19 17:41:52 +00:00
Keir Fraser
6d6e498cba Update to v3.30 2022-01-26 08:19:41 +00:00
Keir Fraser
8e63881d93 KC30: New rotary pin header can be interrupt driven unless motor-delay= is specified. 2022-01-25 08:15:00 +00:00
Keir Fraser
44e65ea7fd Clean EXTI hand-off between rotary-encoder logic and MOTOR/CHGRST 2022-01-24 08:48:26 +00:00
Keir Fraser
cf8551bbd4 KC30AT2: WGATE pin (PB1) should not be pulled up inside the MCU. 2022-01-24 08:17:24 +00:00
Keir Fraser
fd87bce0c9 IMG.CFG, FF.CFG: Fix typos in new option comments. 2022-01-11 10:54:36 +00:00
Keir Fraser
b8dc88f69b FF.CFG: New option max-cyl=N
Refs #441
2022-01-11 10:35:22 +00:00
Keir Fraser
e4f2033f16 IMG.CFG: New option step= to allow specifying single- vs double-step.
Refs #441
2022-01-10 13:18:46 +00:00
Keir Fraser
51c529d9db Repository renaming / lower-casing:
HxC_FF_File_Selector -> flashfloppy-hxc-file-selector
2021-12-23 11:04:47 +00:00
Keir Fraser
74c4808577 Repo names renamed to all lower case 2021-12-22 16:22:05 +00:00
Keir Fraser
3633e9c124 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-06 17:21:45 +00:00
Keir Fraser
914510fd67 README: Remove Donations section. Donations are no longer solicited. 2021-10-10 16:57:23 +01:00
Eric Anderson
3a0734992a 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-10-07 17:09:33 +01:00
Keir Fraser
abe7a064c6 Update to v3.29 2021-10-02 11:50:26 +01:00
Keir Fraser
016e032708 Remove accidental debug logging change. 2021-09-22 13:01:06 +01:00
Eric Anderson
36e8e63a8d Use __attribute__((naked)) for IRQ_SELA_changed
This allows C to be aware of the declaration which reduces the amount of
assembler necessary.
2021-09-22 12:09:22 +01:00
Eric Anderson
29700fa0b4 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-09-22 12:00:41 +01:00
Keir Fraser
0d1b586fb6 QFN32: JC will in future be connected at pin PA9. Implement this. 2021-09-22 10:03:47 +01:00
Keir Fraser
6de6d16668 QFN32: Implement support for pin mapping of new board SFRKC30.AT2 using AT32F415 QFN32 package. 2021-09-22 10:03:17 +01:00
Keir Fraser
5e085a4217 IMG.CFG: New parameter gap2= to set Post-ID Gap value.
Refs #496
2021-08-10 14:19:48 +01:00
Keir Fraser
f8de8ec413 IMG, ED: GAP2 should be 41 bytes, rather than the usual 22 bytes.
Refs #496
2021-08-10 14:07:09 +01:00
Keir Fraser
2180f96866 Support "Enhanced Gotek" boards with Artery MCU. 2021-08-07 14:38:19 +01:00
Keir Fraser
d45a03dab0 Update to v3.28 2021-07-26 15:21:30 +01:00
Keir Fraser
01f437ae66 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:11:24 +01:00
Keir Fraser
babe837723 Update to v3.27 2021-07-02 08:50:44 +01:00
Keir Fraser
c0251223b9 Fix button handling for "Enhanced Gotek" models. 2021-06-30 17:01:34 +01:00
Keir Fraser
6f12c9dc81 Update to v3.26 2021-06-24 09:59:04 +01:00
Keir Fraser
90310e9f00 Support new Gotek board SFRKC30.AT4.7
* AT32F415 654-pin LQFP
 * Optional dedicated rotary header
2021-06-23 08:59:15 +01:00
Keir Fraser
f8a71a4bd9 Update to v3.25 2021-04-25 16:05:01 +01:00
Keir Fraser
f1c328f349 mfm: Fix ring-index handling in mfm_ring_to_bin().
From master:
 c4c14c58d4
 3ee01621e3
2021-04-25 15:55:38 +01:00
Keir Fraser
4225a9828c Update to v3.24 2021-04-15 10:32:11 +01:00
Keir Fraser
c84bbe9c2b Beta support for Artery microcontrollers
- SFRC922D using AT32F415RCT7
 - SFRC922AT3 using AT32F415CBT7
2021-04-15 10:30:28 +01:00
Keir Fraser
963ed184c4 img: Fix pc_dos layout for DMF (1.68MB)
Fixes #430
2021-03-04 09:33:34 +00:00
Keir Fraser
32e2fd08f4
Merge pull request #439 from ejona86/lsync-fsize
Align F_lseek definition with declaration
2021-03-02 07:27:54 +00:00
Eric Anderson
a8598bb347 Align F_lseek definition with declaration
Doesn't hurt much because the two types have the same size, but was a
bit confusing.
2021-03-01 17:24:56 -08:00
Keir Fraser
4bd6286495 build: Remove -fdata-sections. It makes the binaries larger!
All the benefit is actually from -ffunction-sections.

For an explanation of why -fdata-sections makes the binary larger,
by inflating the literal pools, see a detailed stackoverflow answer:
https://stackoverflow.com/questions/4274804/query-on-ffunction-section-fdata-sections-options-of-gcc
2021-02-22 10:58:46 +00:00
Keir Fraser
d6e2539651 build: Fix handling command-line options passed on command line rather than in environment. 2021-02-22 10:40:05 +00:00
Keir Fraser
61afa784d1
Merge pull request #432 from ejona86/better-gc-sections
Improve gc-sections binary reduction with finer-grained sections
2021-02-22 10:27:23 +00:00
Keir Fraser
0b1ed450ae Example IMG.CFG file for Osborne CP/M systems. Thanks to Dan Doore.
Closes #423
2021-02-15 07:46:22 +00:00
Eric Anderson
94404c298c Improve gc-sections binary reduction with finer-grained sections
Saves about 1k for a normal build. Allows building bootloader in debug
mode.
2021-01-30 22:23:27 -08:00
Keir Fraser
457af659c5 Update to v3.23 2020-12-31 09:19:48 +00:00
Keir Fraser
0e7169d257 IMG.CFG: Up to 255 cylinders is now permitted. 2020-12-31 08:15:25 +00:00
Keir Fraser
012f191e50 img: Fix FM data rate for 8-inch disk images 2020-12-30 17:52:03 +00:00
Keir Fraser
f83211f5cf IMG.CFG: Parse the IMG.CFG file even in HxC and indexed modes.
Refs #410
2020-12-30 14:37:25 +00:00
Keir Fraser
87264a6dbc Remove fast seek outward from tracks >= 84.
Refs #405
2020-12-23 12:35:21 +00:00
Keir Fraser
8ceac4d17e Do not enter DA mode if within cylinder range of mounted image.
Refs #405
2020-12-23 12:35:02 +00:00
Keir Fraser
78cb485361 Remove Kaypro from FF.CFG and provide an IMG.CFG instead.
Refs #398
2020-12-10 08:14:23 +00:00
Keir Fraser
c2a5c48439 Remove RELEASE_NOTES from CI artifacts. Remove RELEASE from Release zip. 2020-12-08 12:55:46 +00:00
Keir Fraser
73eddd3d5f Release workflow 2020-12-08 12:36:22 +00:00
Keir Fraser
81c24a3025 Put rev/sha commit id in CI artifacts 2020-12-08 11:39:01 +00:00
Keir Fraser
06e01b9d2b Version badge 2020-12-07 18:31:20 +00:00
Keir Fraser
c04ddc13cb Rename CI uploaded artifact 2020-12-07 12:47:37 +00:00
Keir Fraser
ad3ac0fafd Downloads badge 2020-12-06 14:18:04 +00:00
Keir Fraser
af635d06a4 CI badge 2020-12-06 11:32:38 +00:00
Keir Fraser
b8cd33e1a2 Consistently use python3. Update CI workflow to reflect this and use Ubuntu 20.04. 2020-12-06 11:12:06 +00:00
Keir Fraser
4556f86c75
Merge pull request #394 from rfinnie/ci
Add Github CI workflow
2020-12-06 10:07:51 +00:00
Ryan Finnie
3028de3cbd
Add Github CI workflow 2020-12-05 11:14:54 -08:00
Keir Fraser
44c45debf7 Sinclair QL: Add ED IMG.CFG example
Refs #277
2020-12-05 08:03:55 +00:00
Keir Fraser
f229b3baa4 Roland: Example IMG.CFG for Roland OS & System disks 2020-12-05 07:57:14 +00:00
Keir Fraser
94c9e8c483 Clear LCD/OLED before cfg_init(), as this may write to LCD/OLED buffer.
Fixes missing folder-name row when Flash drive is first mounted.
2020-12-02 10:16:46 +00:00
Keir Fraser
b862362640 Update to v3.22 2020-11-17 14:50:54 +00:00
Keir Fraser
5fd121e591 Fix crashes when mounting a Flash drive with non-existent IMAGE_A.CFG path.
1. Was crashing if the specified sub-folder is now empty
2. Was crashing if IMAGE_A.CFG blown away due to invalid cfg.sorted[] value
2020-11-17 14:46:13 +00:00
Keir Fraser
05695ef384 FF.CFG: Fix display-type= -inverse description text 2020-11-17 13:28:48 +00:00
Keir Fraser
bdbbdc2e8e Rename native_read_dir() to native_read_and_sort_dir() and give it a -1 error return.
The previous error return, zero, is also a legitimate return value for an empty dir.
This fixes a subsequent crash in IMAGE_A.CFG processing on an empty root dir.
2020-11-17 13:09:04 +00:00
Keir Fraser
9dfc22fbd8 lcd,oled: display-order= is ignored in "banner" and "menu" modes. 2020-11-13 11:31:19 +00:00
Keir Fraser
24ca89f134 Rename display_mode to display_type. 2020-11-13 09:32:07 +00:00
Keir Fraser
afe4ef7f4a fatfs: Mount FAT16/FAT32 volumes with FATs which don't cover the
total sectors of the volume. The tail end of the volume is simply
unusable.
2020-11-13 09:19:49 +00:00
Keir Fraser
a6cf9eb168 FF.CFG: Fix display-type= parsing to recognise OLED options only if
OLED has been explicitly specified.
2020-11-08 10:14:13 +00:00
Keir Fraser
80c05ce92d oled: New FF.CFG display-type option: -inverse 2020-11-02 21:00:22 +00:00
Keir Fraser
c3e0d2b4a0 Update to v3.21 2020-10-26 09:04:06 +00:00
Keir Fraser
b36c926b4f fatfs: Enable GPT partition handling 2020-10-26 08:49:24 +00:00
Keir Fraser
6fe58c5863 fatfs: Use correct LBA_t/FSIZE_t types in our interfaces to FatFS 2020-10-25 19:35:48 +00:00
Keir Fraser
b0c0d0b104
Merge pull request #383 from hharte/master
Update FatFS to R0.140-patch2
2020-10-25 19:08:19 +00:00
Howard M. Harte
960f9d87a1 Add example image configuration for CompuPro 8-16 8" CP/M disks.
Tested on a CompuPro 8-16 System using the 50-to-34 pin adapter
board available here:

https://gitlab.com/NF6X_Retrocomputing/fd50to34

Make sure to set the jumpers on the fd50to34:
    * 2SIDE - Install if emulated floppy disk is double-sided.
    * MTRON - Install

In FF.CFG, set interface=shugart

Supports disk images:
    * Standard CP/M 8" SS/DD 128-byte sectors.
    * CompuPro 8" SS/DD  128-byte sectors.
    * CompuPro 8" DS/SD  128-byte sectors.
    * CompuPro 8" DS/DD 1024-byte sectors.
    * CompuPro 8" DS/DD  512-byte sectors.
    * CompuPro 8" DS/DD  256-byte sectors.
2020-10-25 11:32:26 -07:00
Howard M. Harte
57b53d11aa Update fatfs to R0.140-patch2 2020-10-24 13:11:26 -07:00
Howard M. Harte
67766c57c3 Update fatfs to R0.140-patch1 2020-10-24 11:56:32 -07:00
Howard M. Harte
e3603ec0c0 Update fatfs to R0.14 2020-10-24 11:44:56 -07:00
Howard M. Harte
b8f022f02f Update fatfs to R0.13c 2020-10-24 09:29:14 -07:00
Keir Fraser
838ae074b0 Update to v3.20 2020-10-15 12:58:16 +01:00
Keir Fraser
732ed49603 Eject Menu: Dynamically construct it based on which options make sense.
Folder Menu: Enter on "..".
2020-10-15 12:46:57 +01:00
Keir Fraser
a9596cfabc Eject Menu: Replace Clone with Copy/Paste. Allows cloning to a different folder.
Also implement Paste to Folder when a Folder is selected and the select button
is held for 1500ms.
2020-10-15 11:02:05 +01:00
Keir Fraser
acc2022535 img: Rename raw_sec fields to use R and N (IDAM byte IDs). 2020-10-15 09:34:03 +01:00
Keir Fraser
ed41a52996 IMG.CFG: Allow head-id to be forced via new "h=0|1" option.
Use this in the TSC_Flex format descriptors.
Refs #378
2020-10-15 08:11:10 +01:00
Keir Fraser
dee6ece5c3 Fix the build after removing "rotary=gray" config option. 2020-10-15 08:09:34 +01:00
Keir Fraser
3824571cc3 Host/Bung_MGD2: Tweak GAP3 for 1756kB "Ultra Format" to massively
improve read speed. Reduced from 3m30s to 24s for a 8Mbit image!

Fixes #134
2020-10-14 11:32:19 +01:00
Keir Fraser
446e020a03 IMG.CFG: Clean up example file to better match the updated wiki docs. 2020-10-11 12:32:30 +01:00
Keir Fraser
a8757c82a6 examples/FF.CFG: Fix twobutton-action=htu description. Buttons were swapped. 2020-10-11 12:03:54 +01:00
Keir Fraser
2341619c0b FF.CFG: New option "rotary=v2" uses v2.x rotary encoder logic.
This is more permissive than v3.x logic and may work better with
some cheap encoders which skip Gray-code states altogether.

Fixes #357
2020-10-11 11:49:33 +01:00
Keir Fraser
fea9e270f9 FF.CFG: Remove obsolete "rotary=gray" option.
Clean up type subfield of ff_cfg.rotary by introducing an explicit typemask.
2020-10-11 11:44:09 +01:00
Keir Fraser
db2ab822ea FF.CFG: New 'twobutton-action = htu'. Original Gotek Hundreds/Tens/Units
button behaviour during image navigation:
Both:  Hundred
Left:  Tens
Right: Units

Hold both for 1 second to reset to 000.

Refs #379
2020-10-11 11:31:44 +01:00
Keir Fraser
893c5cb1ce Host/Bung_MGD2: IMG.CFG for 1756kB "Ultra Format"
Refs #134
2020-10-11 11:29:36 +01:00
Keir Fraser
9e97833423 TSC_Flex: Update IAM & GAP values based on actual HFE attached to ticket 378.
Refs #378
2020-10-10 18:48:05 +01:00
Keir Fraser
d9fb2364a2 img: Fix some insane raw_type values and simplify mfm_prep_track().
raw_type fixes:
1. MBD DD (not really seen in practice) is obviously 5*1kB. Somehow
   the interleave and size fields had got swapped.
2. PC98 DD (again not seen in practice) 720kB format is obviously
   the usual 300RPM spin rate. It just doesn't fit at 360RPM.
3. PC98 DD 8spt format needs a smaller gap3. It's certainly not the
   same as PC98 HD GAP3 as that uses 1kB sectors. Shrink it enough
   that the track fits comfortably within 100,000 bitcells.
4. Shrink the 1.68M (DMF) GAP3 so that the track fits comfortably
   within 200,000 bitcells.

mfm_prep_track() simplifications:
1. Set initial auto GAP3 value to 0 instead of 12. The only thing
   it could affect is auto-density selection, and we don't want to
   bump DD->HD->ED simply based on a pretend GAP3 value. This
2. Don't muck with GAP4A, it doesn't buy us much, it would only have
   triggered with the Atari ST 11spt formats (in tandem with the
   already removed sync-gap shrinkage). We should just be able to
   live with the slightly longer track. If not, the ST handler should
   be forcing GAP4A.
2020-10-10 18:14:57 +01:00
Keir Fraser
5fac120259 img: Remove redundant (and broken) track-layout default assigns from IBM 3174 handler. 2020-10-10 15:31:14 +01:00
Keir Fraser
6df9f0bc86 img: Rename MFM GAP macros with MFM_ prefix to match FM equivalents. 2020-10-10 15:06:30 +01:00
Keir Fraser
bf911c343f img: Allow GAP values of 0 to be specified. Auto-GAP is now special value -1.
Allow GAP4A to be specified in IMG.CFG
2020-10-10 15:00:42 +01:00
Keir Fraser
aa08307e6f img: Get rid of bogo shrinkage of IDAM pre-sync gap when GAP3 is short. 2020-10-10 13:59:24 +01:00
Keir Fraser
5df0b89426 TSC Flex: Add example IMG.CFG 2020-10-10 11:40:42 +01:00
Keir Fraser
4ecd2df6b6 IMG.CFG: Allow filesize to be specified in tags 2020-10-10 11:18:32 +01:00
Keir Fraser
6a2a1cf8b8 IMG.CFG: Tags are case insensitive 2020-10-10 10:22:52 +01:00
Keir Fraser
4754acc666 IMG.CFG: Allow track sub-sections to be specified. 2020-10-10 10:15:45 +01:00
Keir Fraser
99246acad7 img: Make cskew and hskew per-track parameters, and included in simple_layout structure. 2020-10-09 10:00:13 +01:00
Keir Fraser
dd10f79afc img: Make interleave a per-track parameter. Define it in simple_layout along with data_rate. 2020-10-09 09:47:13 +01:00
Keir Fraser
9b4d461c12 img: Allocate trk_map[] first and allocate {trk,sec}_info[] below it. 2020-10-09 09:23:04 +01:00
Keir Fraser
b0ae16cd8f img: Clean up XDF handling. Do not depend on sec_map being at bottom
of heap, but use explicit heap_bottom pointer instead.
2020-10-06 18:55:59 +01:00
Keir Fraser
6ace3b9c50 Update to v3.19 2020-09-28 15:16:50 +01:00
Keir Fraser
3875e17547 FF.CFG: New interface options 'jppc' and 'jppc-hdout'.
These correspond to standard interface types in Japanese PCs.
2020-09-28 15:12:30 +01:00
Keir Fraser
e23c893f8e adf: Remember sector write order for current cylinder if it is modified.
Although we forget as soon as the heads are stepped or disk ejected, this
is sufficient to satisfy X-Copy in verify mode when copying a source disk
with out-of-order sectors (seems rare but does happen).

Fixes #365
2020-09-28 09:23:20 +01:00
Keir Fraser
fbc2c4e649 RELEASE_NOTES: Typo 2020-09-23 12:35:13 +01:00
Keir Fraser
40149d980d Update to v3.18 2020-09-23 12:32:17 +01:00
Keir Fraser
72d2338d05 img, dsk: Improved log messages 2020-09-23 12:28:19 +01:00
Keir Fraser
35428b469f img: Set raw_sec pointer correctly in MFM Data write path.
This merely fixes the "Bad CRC" log message, to indicate the correct sector id value.
2020-09-22 16:55:41 +01:00
Keir Fraser
9fcebff12a img, dsk, da: A faster method for detecting 4489 MFM sync 2020-09-22 13:49:08 +01:00
Keir Fraser
4baa4d1927 img, dsk, da: Replace some CRC computations with pre-calculated versions 2020-09-22 13:36:24 +01:00
Keir Fraser
5dc275d02b img, dsk, da: On writes, rearrange sync-search and sector-size calculations. 2020-09-22 13:13:26 +01:00
Keir Fraser
9154327a60 img, dsk: Pull first write_sector calculation into separate functions. 2020-09-22 12:41:48 +01:00
Keir Fraser
bac051fa02 i2c: Revert 07953affa5 and implement correct fix.
The last byte of a multi-DMA i2c transfer could be lost, not because
of an error in the i2c display controller, but because of a bug in
FlashFloppy's I2C DMA handling.

When the last byte of a transmit is acknowledged by the receiver,
I2C sets the BTF flag in SR1. This is not cleared until SR1 is read
and a further byte is transmitted. Unfortunately this means that if
we are slow to start a new DMA after the previous completes, BTF
gets set. And it won't clear because we don't read SR1. Then at the
end of the final DMA, we wait for BTF and then issue STOP. But
because BTF is already set, we issue STOP too early and the final
byte is not transmitted.

The fix is to always wait for BTF at the end of a DMA transfer.
Because this requires us to wait for the final two bytes (one in the
data register, and one in the shift register) the delay can be 50us
(Fast I2C) up to 200us (Standard I2C). We avoid spin-waiting by
enabling the I2C Event IRQ and calling back into the DMA pipeline
when we are interrupted on BTF.
2020-09-20 16:43:04 +01:00
Keir Fraser
ae1354135e logfile: Write FFLOG.TXT to root or FF/ subfolder. 2020-09-20 08:19:07 +01:00
Keir Fraser
07953affa5 ssd1309: Fix corruption in last column of 2.42" 128x64 displays.
Seems to be due to last data byte in a transaction being dropped.
One fix was to introduce 100us delay between BTF and STOP in
i2c_btf_stop(). The workaround here is simply to clear that column
during oled init. With 6x13 font it is always blank anyway.
2020-09-20 08:08:14 +01:00
Keir Fraser
e22bb5709d Update to v3.17 2020-09-17 13:24:16 +01:00
Keir Fraser
ed96ecd08d quickdisk: Restart read stream at the end of a write.
Fixes #364
2020-09-16 21:12:01 +01:00
Keir Fraser
2fc792caa6 dsk: Avoid infinite image-open loop for unrecognised .DSK files.
Refs #369
2020-09-15 08:49:02 +01:00
Keir Fraser
89b9faa727 IMG.CFG: Remove [default] section from example IMG.CFG.
Many users blindly copy the example configs to their USB drive, and
this will defeat FlashFloppy's default IMG geometry detection.

Refs #363
2020-09-01 12:16:29 +01:00
Keir Fraser
6c3d0d0fea Update to v3.16 2020-08-22 14:25:43 +01:00
Keir Fraser
c43c285b62 openocd: Add 'make ocd' target 2020-08-21 09:02:19 +01:00
Keir Fraser
9a5a29a579 rotary: Implement a velocity curve for image navigation 2020-08-20 14:58:02 +01:00
Keir Fraser
3048d177e9 rotary: ROT_full only accept a click on entry to state 0b11.
Avoids false double clicks on some cheap encoders (Chris Morley).
2020-08-20 10:17:08 +01:00
Keir Fraser
5830c77f98 rotary: Rotary encoder inputs are now IRQ-driven rather than polled. 2020-08-20 10:16:00 +01:00
Keir Fraser
d244c30aaf Remove file-type extension from HXCSDFE.CFG filenames. Matches direct nav behaviour.
Fixes #355
2020-08-19 03:54:04 +01:00
Keir Fraser
58614c27d5 hfe: Fix double-step cylinder limit
Fixes #353
2020-08-17 16:46:26 +01:00
Keir Fraser
32eeaabc68 lcd/oled: Fix filename display for displays wider than 24 chars 2020-08-17 16:45:15 +01:00
Keir Fraser
02fb24bc2d Update to v3.15 2020-07-23 11:46:17 +01:00
Keir Fraser
133c02c01d Sample rotary-encoder inputs at 4x frequency of regular buttons (ie 2kHz rather than 500Hz).
Fixes missed detents on some cheap rotary encoders.

Seems to have no bad effect on high-rate images (eg. HD HFE, ED IMG, as in FF_TestBed).
2020-07-22 16:38:18 +01:00
Keir Fraser
8f5ac9a142 Update to v3.14a 2020-07-15 14:17:47 +01:00
Keir Fraser
dd69afef7e floppy: Get rid of duplicate 'nr_sides' field in drive structure.
Use the image structure's field exclusively.
2020-07-15 14:07:12 +01:00
Keir Fraser
b5e70de77b image: Properly implement empty/unformatted tracks beyond end of image-file track count.
These tracks read as garbage, and ignore writes.

This fixes a bug where writing beyond an image-file limit would trash the last
cylinder's track data. This was particularly an issue for HFE image files.

Refs #309
2020-07-15 13:19:41 +01:00
Keir Fraser
cfd3c51062 HxC_Compat: Update to v9-FF
1. Fix Atari ST key layout (QWERTY instead of AZERTY)
 2. Remove unused menu settings
 3. Fix the search filter (Esc and Backspace keys)
2020-07-12 12:06:02 +01:00
Keir Fraser
0551750868 speaker: Rate-limit speaker pulses to one per 2.7ms. Avoids annoying
high-frequency chirp when entering D-A mode or in Gotek-aware high-speed loaders.
2020-07-10 14:56:12 +01:00
Keir Fraser
bf0506145d akai: Add skew to the Akai DD and HD layouts.
This makes access faster on many Akai models, and fixes
access on Oberheim DPX.
2020-06-16 11:45:05 +01:00
Keir Fraser
ce4c874b21 Update to v3.13a 2020-03-13 08:19:40 +00:00
Keir Fraser
222f57a1e2 hfe: Respect double-step header flag
Refs #318
2020-03-13 07:54:02 +00:00
Keir Fraser
bff87bbfde FF.CFG: write-drain = instant | realtime | eot
'eot' fixes Not Ready errors on large writes and disk copies to
Gotek on Amstrad PPC640/512.

Fixes #320
2020-03-13 07:14:42 +00:00
Keir Fraser
0e9554daac index-suppression: Suppress index while RDATA and WGATE inactive. 2020-03-12 07:57:48 +00:00
Keir Fraser
c82596eb40 FF.CFG: display-on-activity=sel will trigger OLED/LCD when drive is selected.
Refs #321
2020-03-03 10:30:50 +00:00
Keir Fraser
fc181422e6 edsk: Fix double-step script to support Extended CPC DSK images.
Refs #318
2020-02-16 22:57:39 +00:00
Keir Fraser
0f6b448348 edsk: Provide a script to double-up EDSK cylinders to emulate "double stepping"
Refs #318
2020-02-16 19:55:33 +00:00
Keir Fraser
ca3fdccc1f Update to v3.12a 2020-02-07 08:55:16 +00:00
Keir Fraser
7184c7ae90 IBM 3174 tweaks. 2020-02-07 07:41:43 +00:00
Matt Burke
fba2689d5a Added new RAW image format for IBM 3174 2020-02-07 07:37:54 +00:00
Keir Fraser
b1bc37709c osd,oled: Wait for slow OLED to initialise, even if we find FF OSD meanwhile.
But only if display-type is suitably set!
2020-02-05 10:08:27 +00:00
Keir Fraser
eb36d402b8 img,raw: Make RPM a per-track parameter
Refs #314
2020-02-05 08:57:19 +00:00
Keir Fraser
701e88b339 floppy: Avoid FAT02 error on mounting the dummy "(Empty)" file.
This is used when HXCSDFE.CFG is present with no AUTOBOOT.HFE
2020-02-03 07:58:18 +00:00
Keir Fraser
74a989b6c8 README: Update intro text 2020-01-17 11:53:11 +00:00
Keir Fraser
9ff7dbbadb Update to v3.11a 2019-12-30 13:29:43 +00:00
Keir Fraser
4a4efe01b3 oled: Fix subfolder name when moving to a parent folder (..)
Refs #305
2019-12-30 13:13:21 +00:00
Keir Fraser
c69ba81fdb Update to v3.10a 2019-12-28 05:27:07 +00:00
Keir Fraser
be975c4990 fw_update: Wait for buttons to be pressed before proceeding.
This avoids an infinite loop when reprogramming the bootloader.

Broken since v3.5a
2019-12-28 05:22:21 +00:00
Keir Fraser
0eb3b4ba7b IMG.CFG, FF.CFG: Clarify that must reside in FF/, if FF/ exists. 2019-12-27 15:04:57 +00:00
Keir Fraser
f766cf8213 Update to v3.9a 2019-12-27 13:11:14 +00:00
Keir Fraser
a3aebcdb51 img: Fix outp_hden for high-density images.
write_bc_ticks has not been initialised early enough since v2.2a
Refs #290
Refs #301
2019-12-27 07:38:18 +00:00
Keir Fraser
c94a4ababf rotary: A better debounce algorithm. Count contiguous step-wise
sequences of gray-code transitions, CW and CCW. Register a left/right
movement when a counter reaches the relevant threshold.

This is reported to work better than both the existing algorithm, and
the reference found online.
2019-12-17 11:59:03 +00:00
Keir Fraser
a4d3db8ccf examples/FF.CFG: Fix init=static file to be INIT_A.CFG 2019-12-15 17:16:00 +00:00
Keir Fraser
49b9b6479e rotary encoder: Debounce by checking number of transitions seen
between detents.

Scan the rotary encoder faster (500Hz) to avoid missing transitions.
2019-12-15 10:52:55 +00:00
Keir Fraser
2cd1b74192 Update to v3.8a 2019-12-10 16:06:01 +00:00
Keir Fraser
5cba7a650a amiga: Fixes for 'interface = amiga'.
1. Set the HDEN output according to RPM. The bitcell is always 2us on Amiga.
2. Do not futz with pin34 unless it is otherwise unused.

This fixes FlashFloppy for use with an esoteric disk module for A4000T.
Fixes #293.
2019-12-10 15:52:55 +00:00
Keir Fraser
b2d27a6b9a Fix strcmp() build on gcc-9.2.0 2019-12-10 15:06:42 +00:00
Keir Fraser
5538be6686 Remove double underscore prefix from __packed and __aligned() definitions.
It is polluting the GCC namespace and breaks the build with GCC v9.
2019-12-10 15:04:55 +00:00
Keir Fraser
1148715937 usb: Fix buffer overflow when parsing USB string descriptors.
This was triggered by a very-long-indeed SanDisk Ultra Fit serial nr.
2019-12-10 14:24:21 +00:00
Keir Fraser
e9271b14c4 QD: Larger read/write "ready" window in default QD image file.
Refs #282
2019-12-02 11:33:52 +00:00
Keir Fraser
fd0532a4ce HxC_Compat: Update to v8-FF
Fixes startup crash on Atari ST: Clears BSS region
2019-12-02 11:29:45 +00:00
Keir Fraser
af86cd1026 trd: Fix geometry detection
Fixes #260
2019-11-26 08:17:28 +00:00
Keir Fraser
d9c9ca606d Update to v3.7a 2019-11-11 09:32:21 +00:00
Keir Fraser
1e233734bf Debug: Log build date/time, and QuickDisk JC setting 2019-11-06 20:57:15 +00:00
Keir Fraser
44c01c3db7 hfe, qd: Fix bogus premature truncation to uint16_t in min_t() comparison.
Refs #271
2019-11-06 19:15:06 +00:00
Keir Fraser
1883c14d7f quickdisk: Minor tweaks
- Log that this is QuickDisk build in debug-log banner text
 - Filter ADF images out: We support only QD images
2019-11-05 16:07:49 +00:00
Keir Fraser
4ab57f39b0 quickdisk: Fix offset of writes on first read revolution.
Refs #271
2019-11-05 13:20:26 +00:00
Keir Fraser
3b51c09408 Update to v3.6a 2019-10-13 10:21:31 +01:00
Keir Fraser
8446bdbe0b Update to File Selector v7-FF 2019-10-13 10:18:47 +01:00
Keir Fraser
a06d641182 QD: Simplify QD write handler slightly. 2019-10-10 22:10:13 +01:00
Keir Fraser
a5ae5aefe6 Quickdisk: Distribute logfile version of QD firmware 2019-10-10 21:46:29 +01:00
Keir Fraser
a1e9ecbf39 QD: Further tweaks 2019-10-09 16:26:47 +01:00
Keir Fraser
de919ae023 Quickdisk: Initial support 2019-10-09 11:29:27 +01:00
Keir Fraser
f467cce28e Update for v3.5a 2019-10-09 11:15:46 +01:00
Keir Fraser
1dfa46f676 New Main Menu. Allows Firmware Update by request, and FF OSD Config. 2019-10-09 09:51:29 +01:00
Keir Fraser
7b5a77007f OSD: Bidirectional comms, to receive button/hotkey state. 2019-10-09 08:25:35 +01:00
Keir Fraser
91c0fbcf70 OSD: Default to 40-column if no other display installed and no
explicit display-type= specified.
2019-10-01 08:32:55 +01:00
Keir Fraser
f75f344595 TR-DOS: Fix geometry detection based on TR-DOS header.
Fixes #260
2019-09-28 11:52:09 +01:00
Keir Fraser
a90f658267 Update for v3.4a 2019-09-25 14:29:31 +01:00
Keir Fraser
b58070e5c1 FF OSD: Support Configuration via Gotek 2019-09-25 12:36:39 +01:00
Keir Fraser
9e1c264397 LCD/OLED: Support bus sharing with FF OSD 2019-09-25 12:36:28 +01:00
Keir Fraser
2c1dda06c5 bootloader: Shrink size (primarily by disabling debug printk) 2019-09-23 13:56:16 +01:00
Keir Fraser
f661196314 README: Tone down the Donations section. It's all about the beer & coffee. :) 2019-07-22 11:25:25 +01:00
Keir Fraser
8a5b45ece7 RELEASE_NOTES: Note that v3.3a changes the config filename for static image name to INIT_A.CFG 2019-07-08 09:48:21 +01:00
Keir Fraser
0b9a212ff8 Update to v3.3a 2019-07-08 09:34:52 +01:00
Keir Fraser
d55fcefe3c OLED/LCD: Fix cur-folder display row for non-default image-on-startup=
This has resulted in renaming static IMAGE_A.CFG to INIT_A.CFG
2019-07-08 09:30:04 +01:00
Keir Fraser
3d9329c44a CHGRST: Implement chgrst=delay-N to automatically reset DSKCHG some
multiple of 0.5s after a disk is inserted.
2019-07-08 05:41:10 +01:00
Keir Fraser
fbcdcb93a6 STEP IRQ triggers only on rising edges. 2019-07-08 05:40:45 +01:00
Keir Fraser
2de218406a Update to v3.2a 2019-06-25 09:24:16 +01:00
Keir Fraser
71a0b70efa FF.CFG: Rename oled-text= to display-order=. It now controls both LCD and OLED.
Remove the 's' single-height specifier, this is teh default height anyway.
Double-height is now an optional 'd' suffix to each row's content line nr.
2019-06-25 09:16:45 +01:00
Keir Fraser
c0413c111e OLED: Change default 128x64 layout when in eject menu. 2019-06-25 08:42:34 +01:00
Keir Fraser
1d51f36d00 LCD: DMA LCD data row-by-row. Avoids buffer overflow. 2019-06-24 07:44:24 +01:00
Keir Fraser
d2021a8def LCD: Support 4-row LCDs (specifically the common 2004 form factor). 2019-06-21 15:42:18 +01:00
Keir Fraser
217f005c74 oled: New optional row 3: Subfolder name 2019-06-21 13:00:23 +01:00
Keir Fraser
52b7a12b2d Improvements to trackball and menu system:
1. Add inertia to trackball so it is less jittery
2. Require confirmation of image ops (Clone, Delete)
3. Refresh display after delete
2019-06-21 10:48:29 +01:00
Keir Fraser
fdbeef3449 Update for v3.1a 2019-06-13 09:03:06 +01:00
Keir Fraser
26e04973ad Implement Delete & Clone 2019-06-13 08:23:10 +01:00
Keir Fraser
01c955c19f LCD/OLED: Eject Menu 2019-06-12 16:23:43 +01:00
Keir Fraser
83186755a6 FF.CFG: New option oled-text=...
Default 128x64 display is now 3 rows, only 1st row double height.

The sizing and row ordering is configurable.
2019-06-12 13:12:17 +01:00
Keir Fraser
ad0ffa73bd Update to v3.0a 2019-06-10 13:18:56 +01:00
Keir Fraser
e41e9460a8 FF.CFG: New display-type=oled-128x32-ztech
For ZHONGJY_TECH displays based on SSD1305 controller.
2019-06-10 11:41:55 +01:00
Keir Fraser
3f31b68f77 FF.CFG: New option 'chgrst = step | pa14'
Allows emulation of old drives with "Disk Change Reset" line.
2019-06-09 18:51:12 +01:00
Keir Fraser
769f8e287f Update to v2.13 2019-06-07 07:57:47 +01:00
Keir Fraser
57d32f5544 Update to HxC_FF v6 2019-06-07 07:55:28 +01:00
Keir Fraser
14dd9e9c26 floppy: Enable signal capture *after* resetting timer counter.
Doesn't seem to really matter, but the new ordering is more
obviously safe.
2019-06-07 07:24:14 +01:00
Keir Fraser
4f16968dc4 floppy.c: Change max_read_us into a quiter max-prefetch-time diagnostic.
Also assert during prefetch that DMA doesn't fire, as it will screw up
the ring indexes.
2019-06-04 16:48:01 +01:00
Keir Fraser
953fb30ab3 console: Avoid polling the USART with interrupts globally disabled.
Instead defer that work to a very low-priority IRQ.
2019-06-04 15:20:25 +01:00
Keir Fraser
88d4692961 hfe: Fix read-ring handling. 2019-06-03 14:28:52 +01:00
Keir Fraser
6f51cb70d5 Call traces in crash handler 2019-06-03 14:28:28 +01:00
Keir Fraser
fe28df8e1c Update for v2.12 -- new major stable release 2019-06-02 10:06:42 +01:00
Keir Fraser
5228488efa OUT: Support Roland *.OUT images 2019-06-02 09:42:23 +01:00
Keir Fraser
c2a2e0bdf4 io-test: New alternative firmware to test Gotek I/Os 2019-06-01 18:55:46 +01:00
Keir Fraser
403fefe668 Rename LCD display type from LCD_1602 to the more accurate LCD_OLED.
Also improve the start-of-day info logging
2019-06-01 09:57:46 +01:00
Keir Fraser
c526c155e3 hfev3: Implement flaky-byte opcode 2019-06-01 09:54:32 +01:00
Keir Fraser
9cdbce17e4 lcd/oled: Clear i2c_dead when re-init'ing LCD/OLED subsystem 2019-06-01 09:39:12 +01:00
Keir Fraser
176b66126f Add update-via-SD to release notes. 2019-05-23 22:29:53 +01:00
Keir Fraser
4ae922929f Update for v2.11a 2019-05-23 17:02:37 +01:00
Keir Fraser
892d5e0669 FF.CFG: New option sort-priority=folders|files|none
Default sort priority is folders first, then files.

Also fix bug with folders >1000 items.
2019-05-23 16:56:52 +01:00
Keir Fraser
756333a04d Replace the Reloader mess with an all-in-one Bootloader update file.
Also allow the update file to be supplied on SD Card (Enhanced Gotek only).

Fixes #229
2019-05-23 14:26:33 +01:00
Keir Fraser
772c7d3143 Update for v2.10a 2019-05-20 13:12:40 +01:00
Keir Fraser
0a6bcca6bb Fix host=acorn *.ADF support, and host=tandy-coco *.DSK support.
Both have been broken since v2.2a

Fixes #228
2019-05-20 13:12:40 +01:00
Keir Fraser
a679306c6a Update for v2.9a 2019-05-17 21:33:06 +01:00
Keir Fraser
bf36c6c027 FF.CFG: New option 'motor-delay = ignore | 0-1000'
Allows to enable processing of MOTOR input signal, and modify
READY and INDEX output behaviours accordingly.

RDATA does not respect MOTOR: Probably noone cares.
2019-05-17 21:02:42 +01:00
Keir Fraser
6f8016e92d IMG/DSK: Reduce write-staging-buffer size to 1024 by chunking large-sector writes.
Gives more room for block cache.
2019-05-16 18:31:45 +01:00
Keir Fraser
fa6cadce09 Faster MFM-to-bin conversion 2019-05-16 14:29:20 +01:00
Keir Fraser
57421fd8a6 floppy: Correctly handle index signal when we pass it while pre-filling read DMA ring. 2019-05-16 14:28:57 +01:00
Keir Fraser
351e858490 Avoid logging on every tack seek in DSK and IMG handlers.
But make this configurable in inc/floppy.h
2019-05-15 10:41:11 +01:00
Keir Fraser
7fbae7b5be hfe: Log when a whole-track write overwrites the start of itself. 2019-05-14 10:25:05 +01:00
Keir Fraser
e162b7c7ff logfile: Add a version of fw which dumps logs to USB stick 2019-05-14 10:24:57 +01:00
Keir Fraser
63aaa148e3 Update for v2.8a 2019-05-10 13:22:26 +01:00
Keir Fraser
dd893f7b49 img: Reduce verbosity of debug logging. 2019-05-10 13:22:06 +01:00
Keir Fraser
ec3467b894 DSK, IMG: Fix MFM clock pulse immediately after DAM byte (0xFB) when next data bit is 0.
Clock should be 0, but was erroneously generated as 1 because
last bit of DAM byte was forgotten.
2019-05-10 13:12:41 +01:00
Keir Fraser
840eab2f79 floppy: Increase write buffer size to 32kB, big enough for a full HD track.
Place read buffer inside write buffer, at far end, to conserve RAM.
This is safe if we reset write ring indexes when all writes are drained
as we get plenty of time to kill the read buffer before write data
catches up.
2019-05-03 08:51:21 +01:00
Keir Fraser
c35f981193 hfe: Make effective use of the read-data and -bitcell ring buffers. 2019-05-02 13:53:04 +01:00
Keir Fraser
49dca96a58 Belts and braces: Always initialise cfg.ejected on entry to floppy_main().
I don't think this actually changes behaviour, but it's easier to reason about.
2019-04-30 09:06:54 +01:00
Keir Fraser
463e58610c RELEASE_NOTES: Typo 2019-04-23 17:53:11 +01:00
Keir Fraser
e4e2716a37 Update for v2.7a 2019-04-23 17:40:02 +01:00
Keir Fraser
b210e8cdc0 Sort folder entries in direct navigation mode.
New FF.CFG option folder-sort=always,never,small

Allows new behaviour to be disabled always, or only for large folders
(which by default get truncated).
2019-04-23 17:36:46 +01:00
Keir Fraser
3deed943da Reduce stack usage of the most stack-hogging functions. 2019-04-23 13:46:37 +01:00
Keir Fraser
6d12292bb2 cache: Defeat C signed->unsigned conversion rules with explicit casts. 2019-04-22 11:43:36 +01:00
Keir Fraser
d9689d87d0 ATR: A couple of fixes:
1. Sectors are interleaved because SIO is a bottleneck;
2. Data rate is increased 4%. Tracks are long since original
drives spin slow; while on later interfaces the bitcells are
clocked faster. We match the latter "overclock".
2019-04-20 12:17:50 +01:00
Keir Fraser
18ce89d083 scripts: mk_hfe.py: Make a blank HFE image with specified geometry and rates 2019-04-20 12:17:04 +01:00
Keir Fraser
db2451b205 Update to v2.6a 2019-04-14 18:31:12 +01:00
Keir Fraser
542577893d Update HxC_Compat to v5-FF 2019-04-14 18:28:49 +01:00
Keir Fraser
cf10dadef3 Re-calculate fast-seek cluster table when image_extend() does work.
Fixes bug introduced in v2.5a
2019-04-14 16:06:46 +01:00
Keir Fraser
61f8264cba Remove bogus NULLing of filesystem data. It is done on correct path elsewhere now.
Fixes bug introduced in v2.5a
2019-04-14 15:49:34 +01:00
Keir Fraser
b8fe4b231f cache: Simplify code a little and add some more comments. 2019-04-12 15:28:22 +01:00
Keir Fraser
941c0e4e88 Update for v2.5a 2019-04-12 12:03:24 +01:00
Keir Fraser
d3a0c8421b FF.CFG: Allow rotary encopder inputs to be used for trackball or buttons. 2019-04-12 11:49:34 +01:00
Keir Fraser
55e184e060 Pre-fetch file fragment locations when mounting an image.
Makes seeks faster.
2019-04-12 11:19:02 +01:00
Keir Fraser
f5d7d0d7fd HFE: Cache metadata only -- track data simply never fits, so don't
bother trying.
2019-04-12 09:50:33 +01:00
Keir Fraser
ec703e4d54 cache: Interleave cache-entry headers and cached data.
Makes it easier to find a cache entry's data area.
2019-04-11 08:22:15 +01:00
Keir Fraser
c8a01cbcc6 Implement block cache at mass-storage volume layer. 2019-04-10 17:05:08 +01:00
Keir Fraser
a13909e9ca Update for v2.4a 2019-04-09 09:51:33 +01:00
Keir Fraser
d032e02357 IMG: Various fixes for raw sector images.
1. FM and IAM flags not plumbed through for most handlers.
   Means that many FM formats were broken (SSD,DSD,VDK,...)

2. Data fetch for read path did not load sectors in the correct order
   for interleaved/skewed tracks.
   Also affected lots of formats (SSD,DSD,ST,OPD,...)

Thanks to John Gray for reporting and testing!
2019-04-09 09:45:08 +01:00
Keir Fraser
92102cae67 Update for v2.3a 2019-04-08 10:58:52 +01:00
Keir Fraser
23dfa40656 ATR: Invert data as it appears that the standard Atari floppy-SIO
bridge interface does this.
Otherwise an Atari host cannot read downloaded ATR images; only
ones that it formatted/wrote (hence inverted during write, and
inverted again [==uninverted] during read).
2019-04-08 10:48:49 +01:00
Keir Fraser
feb5d7b83c OLED: Auto-detect SSD1306 vs SH1106.
Remove '-sh1106' from FF.CFG as it's no longer needed.
2019-04-08 10:47:48 +01:00
Keir Fraser
2f565f55d5 Update for v2.2a 2019-04-05 12:43:03 +01:00
Keir Fraser
3daf9e615d IMG.CFG: Rename sskew -> hskew (head skew) 2019-04-05 12:33:30 +01:00
Keir Fraser
fdc663ab32 XDF: Fall back from IMG/IMA to XDF handler.
While here, remove header-less formats from the fallback image-detect
list. They cause confusing fallbacks to occur.
2019-04-05 12:25:22 +01:00
Keir Fraser
d3dcbfa99b OLED: Correct horizontal offet for SH1106 128x32 displays. 2019-04-05 11:16:20 +01:00
Keir Fraser
712c0c93ec XDF: Make the format handling more generic. Use the physical cylinder
layouts from fdutils/xdfcopy.
2019-04-05 11:12:58 +01:00
Keir Fraser
78a596cbb1 IMG: Support for *.XDF images (eXtended Disk Format). 2019-04-05 08:15:26 +01:00
Keir Fraser
577d57def1 IMG: Recording mode (FM/MFM) is per-track.
Also rename lots of things IMG -> RAW.
2019-04-03 10:30:24 +01:00
Keir Fraser
6001466210 IMG: Make has_iam/gap_2/gap_4a per-track values 2019-04-03 09:50:06 +01:00
Keir Fraser
8a7af7c168 IMG: Add support for 8-bit Atari (*.ATR) images. 2019-04-03 09:06:43 +01:00
Keir Fraser
737c53423b img: Support multiple track layouts in a single image. 2019-04-03 06:56:52 +01:00
Keir Fraser
c614f88e03 IMG: Allow to specify mixed-size sectors in the read/write engine. 2019-04-02 08:46:08 +01:00
Keir Fraser
acddd92f53 led-trk: Remove configuration option: new mode is always enabled 2019-04-01 09:07:50 +01:00
Keir Fraser
ed0cdd2ae1 led-trk: Use an LED segment to indicate write. 2019-04-01 08:57:39 +01:00
Keir Fraser
839f017c65 led-trk: Improved track display, revert to image # during inactivity. 2019-04-01 08:47:27 +01:00
Keir Fraser
7226b00ed4 IMG.CFG: Max cyls is 254 2019-03-28 15:40:26 +00:00
Keir Fraser
928f58a0b6 Update to v2.1a 2019-03-26 13:35:28 +00:00
Keir Fraser
8d12d0277e Makefile: "make dist" includes EDSK fixup scripts 2019-03-26 11:29:24 +00:00
Keir Fraser
bf7b644b35 edsk.py: Interpret non-extended DSK images correctly. 2019-03-26 11:26:44 +00:00
Keir Fraser
a406fc1aac IMG.CFG: file-layout=reverse-sideN (N=0,1) 2019-03-26 10:49:03 +00:00
Keir Fraser
b6dfe6a0ef IMG: nr_sectors field must be 16 bits. 2019-03-26 09:51:07 +00:00
Keir Fraser
2921e91ecc IMG.CFG: Allow up to 256 sectors per track 2019-03-26 09:37:03 +00:00
Keir Fraser
406ce1c5e3 Update to v2.1pre (preparing for v2.1a) 2019-03-26 08:46:54 +00:00
Keir Fraser
845a789ad1 FF.CFG: Better matching on display-type=led-trk 2019-03-26 08:39:43 +00:00
Keir Fraser
1b543c5767 LCD/OLED: Show 3-digit cylinder #s in full on >16-column display 2019-03-26 08:36:45 +00:00
Keir Fraser
df02509547 FF.CFG: "display-type=led-trk" display track# on 7-seg LED. 2019-03-25 17:52:09 +00:00
Keir Fraser
cdacf6547b IMG.CFG: Allow file layout to be specified 2019-03-25 09:22:39 +00:00
Keir Fraser
7a1d848ae4 IMG.CFG: Allow per-side and per-cyl skew to be specified. 2019-03-25 08:37:41 +00:00
Keir Fraser
d4f4123c8a Streamline the README. 2019-03-19 09:42:53 +00:00
Keir Fraser
c2cd55de7b Update for v2.0a (first development release) 2019-03-14 12:56:27 +00:00
Keir Fraser
96e0906745 indexed mode: Allow a proper name after the DSKA0000 prefix.
eg. DSKA0000_myimage.adf
Also allow the "DSKA" prefix to be changed in FF.CFG.
2019-03-14 12:47:09 +00:00
Keir Fraser
52661c9d30 img: Auto calculation of GAP3. 2019-03-14 11:48:44 +00:00
Keir Fraser
8cafc0b1db IMG.CFG: New option 'rate=' for data rate in kHz 2019-03-13 21:09:07 +00:00
Keir Fraser
6b62ec58d6 img: Support FM mode with IAM 2019-03-13 20:18:50 +00:00
Keir Fraser
f16c6d2cf4 IMG.CFG: Return Err#31 if a tag is badly defined.
Make 'id' an optional parameter, default value 1.
2019-03-13 18:30:17 +00:00
Keir Fraser
7b5703de05 config.c: Allow more punctuation in valid param and opt strings 2019-03-13 09:04:23 +00:00
Keir Fraser
c5679701da IMG.CFG: Allow specify ID start for cyl 0 vs cyls 1+ 2019-03-13 09:03:58 +00:00
Keir Fraser
57ef23aac9 IMG.CFG: Allow a default tag to be defined. 2019-03-13 07:56:20 +00:00
Keir Fraser
723d4806c5 IMG.CFG: New configuration file for specifying IMG/IMA geometry 2019-03-13 07:48:46 +00:00
134 changed files with 15878 additions and 5085 deletions

49
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,49 @@
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set environment variables
id: vars
run: |
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Dependency packages (apt)
run: |
sudo apt update
sudo apt -y install git gcc-arm-none-eabi python3-pip srecord stm32flash zip unzip wget
- name: Dependency packages (pip)
run: python3 -m pip install --user crcmod intelhex
- name: Build dist
run: |
export VER=${{ steps.vars.outputs.sha_short }}
make -j4 dist VER=$VER
mkdir -p _cidist
mv out/flashfloppy-$VER .
rm flashfloppy-$VER/RELEASE_NOTES
git rev-parse HEAD >flashfloppy-$VER/COMMIT
zip -r flashfloppy-$VER.zip flashfloppy-$VER
mv flashfloppy-$VER.zip _cidist/
- name: Build debug dist
run: |
export VER=${{ steps.vars.outputs.sha_short }}-debug
make -j4 dist VER=$VER level=debug
mv out/flashfloppy-$VER .
rm flashfloppy-$VER/RELEASE_NOTES
git rev-parse HEAD >flashfloppy-$VER/COMMIT
echo debug >>flashfloppy-$VER/COMMIT
zip -r flashfloppy-$VER.zip flashfloppy-$VER
mv flashfloppy-$VER.zip _cidist/
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: FlashFloppy.CI.${{ steps.vars.outputs.sha_short }}
path: _cidist

45
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,45 @@
on:
push:
tags:
- 'v*.*'
name: Release
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set environment variables
id: vars
run: |
echo "ver=$(echo ${{ github.ref }} | sed -e's#.*/v##')" >> $GITHUB_OUTPUT
- name: Dependency packages (apt)
run: |
sudo apt update
sudo apt -y install git gcc-arm-none-eabi python3-pip srecord stm32flash zip unzip wget
- name: Dependency packages (pip)
run: python3 -m pip install --user crcmod intelhex
- name: Build release
run: |
export VER=${{ steps.vars.outputs.ver }}
make -j4 dist VER=$VER
mv out/flashfloppy-$VER.zip .
- name: Create Release
id: create_release
uses: ncipollo/release-action@v1
with:
tag: ${{ github.ref }}
token: ${{ secrets.GITHUB_TOKEN }}
name: FlashFloppy ${{ steps.vars.outputs.ver }}
body: "[**Release Notes:**](https://github.com/keirf/flashfloppy/blob/master/RELEASE_NOTES)"
draft: false
prerelease: false
artifacts: flashfloppy-${{ steps.vars.outputs.ver }}.zip
artifactContentType: application/zip

14
.gitignore vendored
View file

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

195
Makefile
View file

@ -1,87 +1,144 @@
export FW_VER := 1.0
PROJ := flashfloppy
VER := $(shell git rev-parse --short HEAD)
PROJ := FlashFloppy
VER := v$(FW_VER)
export FW_VER := $(VER)
SUBDIRS += src bootloader reloader
PYTHON := python3
.PHONY: all clean flash start serial gotek
ifneq ($(RULES_MK),y)
.DEFAULT_GOAL := gotek
export ROOT := $(CURDIR)
all:
$(MAKE) -f $(ROOT)/Rules.mk all
.PHONY: FORCE
clean:
rm -f *.hex *.upd *.rld *.dfu *.html
$(MAKE) -f $(ROOT)/Rules.mk $@
.DEFAULT_GOAL := all
gotek: export gotek=y
gotek: all
mv FF.dfu FF_Gotek-$(VER).dfu
mv FF.upd FF_Gotek-$(VER).upd
mv FF.hex FF_Gotek-$(VER).hex
mv BL.rld FF_Gotek-Bootloader-$(VER).rld
mv RL.upd FF_Gotek-Reloader-$(VER).upd
prod-%: FORCE
$(MAKE) target mcu=$* target=bootloader level=prod
$(MAKE) target mcu=$* target=floppy level=prod
$(MAKE) target mcu=$* target=quickdisk level=prod
$(MAKE) target mcu=$* target=bl_update level=prod
$(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_VER := v1.75-ff
HXC_FF_VER := v9-FF
dist:
rm -rf flashfloppy-*
mkdir -p flashfloppy-$(VER)/reloader
$(MAKE) clean
$(MAKE) gotek
cp -a FF_Gotek-$(VER).dfu flashfloppy-$(VER)/
cp -a FF_Gotek-$(VER).upd flashfloppy-$(VER)/
cp -a FF_Gotek-$(VER).hex flashfloppy-$(VER)/
cp -a FF_Gotek-Reloader-$(VER).upd flashfloppy-$(VER)/reloader/
cp -a FF_Gotek-Bootloader-$(VER).rld flashfloppy-$(VER)/reloader/
$(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)
zip -r flashfloppy-$(VER).zip flashfloppy-$(VER)
_legacy_dist: PROJ := FF_Gotek
_legacy_dist: FORCE
$(PYTHON) $(ROOT)/scripts/mk_update.py old \
$(t)/$(PROJ)-$(VER).upd \
out/$(mcu)/$(level)/floppy/target.bin & \
$(PYTHON) $(ROOT)/scripts/mk_update.py old \
$(t)/alt/bootloader/$(PROJ)-bootloader-$(VER).upd \
out/$(mcu)/$(level)/bl_update/target.bin & \
$(PYTHON) $(ROOT)/scripts/mk_update.py old \
$(t)/alt/io-test/$(PROJ)-io-test-$(VER).upd \
out/$(mcu)/$(level)/io_test/target.bin & \
$(PYTHON) $(ROOT)/scripts/mk_update.py old \
$(t)/alt/logfile/$(PROJ)-logfile-$(VER).upd \
out/$(mcu)/logfile/floppy/target.bin & \
$(PYTHON) $(ROOT)/scripts/mk_update.py old \
$(t)/alt/quickdisk/$(PROJ)-quickdisk-$(VER).upd \
out/$(mcu)/$(level)/quickdisk/target.bin & \
$(PYTHON) $(ROOT)/scripts/mk_update.py old \
$(t)/alt/quickdisk/logfile/$(PROJ)-quickdisk-logfile-$(VER).upd \
out/$(mcu)/logfile/quickdisk/target.bin & \
wait
mrproper: clean
rm -rf flashfloppy-*
rm -rf HxC_Compat_Mode-$(HXC_FF_VER).zip
_dist: FORCE
cd out/$(mcu)/$(level)/floppy; \
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
all:
$(MAKE) -C src -f $(ROOT)/Rules.mk $(PROJ).elf $(PROJ).bin $(PROJ).hex
bootloader=y $(MAKE) -C bootloader -f $(ROOT)/Rules.mk \
Bootloader.elf Bootloader.bin Bootloader.hex
reloader=y $(MAKE) -C reloader -f $(ROOT)/Rules.mk \
Reloader.elf Reloader.bin Reloader.hex
srec_cat bootloader/Bootloader.hex -Intel src/$(PROJ).hex -Intel \
-o FF.hex -Intel
$(PYTHON) ./scripts/mk_update.py src/$(PROJ).bin FF.upd
$(PYTHON) ./scripts/mk_update.py bootloader/Bootloader.bin BL.rld
$(PYTHON) ./scripts/mk_update.py reloader/Reloader.bin RL.upd
$(PYTHON) ./scripts/dfu-convert.py -i FF.hex FF.dfu
endif
dist: level := prod
dist: t := $(ROOT)/out/$(PROJ)-$(VER)
dist: FORCE all
rm -rf out/$(PROJ)-*
mkdir -p $(t)/hex
mkdir -p $(t)/dfu
mkdir -p $(t)/alt/bootloader
mkdir -p $(t)/alt/logfile
mkdir -p $(t)/alt/io-test
mkdir -p $(t)/alt/quickdisk/logfile
$(MAKE) _legacy_dist mcu=stm32f105 level=$(level) t=$(t)
$(MAKE) _dist mcu=stm32f105 n=at415-st105 level=$(level) t=$(t)
$(MAKE) _dist mcu=at32f435 n=at435 level=$(level) t=$(t)
$(PYTHON) scripts/mk_qd.py --window=6.5 $(t)/alt/quickdisk/Blank.qd
cp -a COPYING $(t)/
cp -a README $(t)/
cp -a RELEASE_NOTES $(t)/
cp -a examples $(t)/
# Clive Drive is particularly fussy about QD timings.
$(PYTHON) scripts/mk_qd.py --window=6.4 --total=7.5 --round $(t)/examples/Host/Sinclair_ZX_Spectrum/Clive_Drive/CliveDrive_Blank.qd
[ -e ext/HxC_Compat_Mode-$(HXC_FF_VER).zip ] || \
(mkdir -p ext ; cd ext ; wget -q --show-progress $(HXC_FF_URL)/$(HXC_FF_VER)/HxC_Compat_Mode-$(HXC_FF_VER).zip ; rm -rf index.html)
(cd $(t) && unzip -q ../../ext/HxC_Compat_Mode-$(HXC_FF_VER).zip)
mkdir -p $(t)/scripts
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
DEV=/dev/ttyUSB0
SUDO=sudo
STM32FLASH=stm32flash
T=out/$(target)/target.hex
flash:
sudo stm32flash -b $(BAUD) -w FF_Gotek-$(VER).hex /dev/ttyUSB0
ocd: FORCE all
$(PYTHON) scripts/openocd/flash.py $(T)
start:
sudo stm32flash -b $(BAUD) -g 0 /dev/ttyUSB0
flash: FORCE all
$(SUDO) $(STM32FLASH) -b $(BAUD) -w $(T) $(DEV)
serial:
sudo miniterm.py /dev/ttyUSB0 3000000
start: FORCE
$(SUDO) $(STM32FLASH) -b $(BAUD) -g 0 $(DEV)
serial: FORCE
$(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

@ -1,43 +1,32 @@
# FlashFloppy
A retro floppy emulator for the ubiquitous Gotek hardware.
- Directly supports a wide range of image formats:
- ADF (Commodore Amiga)
- ADM, ADL, ADF, DSD, SSD (Acorn DFS and ADFS)
- D81 (Commodore 64 1581)
- DSK (Amstrad CPC, Spectrum +3, Sam Coupe, Microbee)
- FDI, HDM (NEC PC-98)
- IMG, IMA, DSK (IBM MFM Raw Sector)
- JVC, DSK (Tandy Color Computer 'CoCo')
- MBD (Spectrum MB02)
- MGT (Spectrum DISCiPLE/+D)
- OPD (Spectrum Opus Discovery)
- SDU (SAB Diskette Utility)
- ST (Atari ST)
- TRD (Spectrum TR-DOS)
- V9T9, DSK (TI-99/4A)
- VDK (Dragon)
- HFE, HFEv3 (Universal)
- Pre-configured track layouts for Raw Sector Images:
- Akai (S01, S20, S950), Korg, SC (Prophet 3000)
- Casio (FZ-1)
- DEC (RX33, RX50)
- Ensoniq (ASR, TS, etc)
- Fluke (9100)
- General Music (S2, S3, S2R)
- Kaypro
- Memotech
- MSX
- Nascom (1, 2)
- NEC PC-98
- UKNC, DVK (Soviet PDP-11)
- IBM PC and many others
*The #1 floppy emulator.*
![CI Badge][ci-badge]
![Downloads Badge][downloads-badge]
![Version Badge][version-badge]
<img src="https://raw.githubusercontent.com/wiki/keirf/flashfloppy/assets/banner.jpg">
**FlashFloppy** is a floppy-drive emulator for the ubiquitous
[**Gotek**][Gotek-Compatibility] hardware. Connect to retro machines just
like a real floppy drive but use disk images on a modern USB stick!
- Say goodbye to old and unreliable floppy disks
- Download and play from the disk-image archives for your retro machines
**FlashFloppy** is the **#1 floppy emulator**:
- Supports a [massive range][Host-Platforms] of retro computers, synths, and machinery
- Directly reads and writes [many image formats][Image-Formats]
- [Flexible track layout][Track-Layouts] for Raw Sector Images
- [Extremely configurable][FF.CFG-Configuration-File]
**FlashFloppy** is **Free and Open-Source Software**.
## Download
- [**Download FlashFloppy**](https://github.com/keirf/FlashFloppy/wiki/Downloads)
- [**Download FlashFloppy**][Downloads]
## Documentation
- [**Read the GitHub Wiki**](https://github.com/keirf/FlashFloppy/wiki)
- [**Read the GitHub Wiki**](https://github.com/keirf/flashfloppy/wiki)
## Redistribution
@ -55,16 +44,13 @@ file on your selling page. For example:
- FlashFloppy is free software. For more information see the
[license](COPYING).
## Donations
[Gotek-Compatibility]: https://github.com/keirf/flashfloppy/wiki/Gotek-Compatibility
[Host-Platforms]: https://github.com/keirf/flashfloppy/wiki/Host-Platforms
[Image-Formats]: https://github.com/keirf/flashfloppy/wiki/Image-Formats
[Track-Layouts]: https://github.com/keirf/flashfloppy/wiki/Track-Layouts
[FF.CFG-Configuration-File]: https://github.com/keirf/flashfloppy/wiki/FF.CFG-Configuration-File
[Downloads]: https://github.com/keirf/flashfloppy/wiki/Downloads
Although FlashFloppy is Free Software, some people have expressed a
wish to make a donation for using and distributing it. If this
includes you please feel free to make that donation to a charity of your
choice. Money sent directly to me is donated to
[Macmillan Cancer Support](https://www.macmillan.org.uk/donate)
in the UK.
A couple of redistributors have chosen to donate 1GBP/1EUR to charity
for each unit they sell. I appreciate this lovely gesture but as with
end-user donations it is entirely at their discretion. FlashFloppy
will always be Free.
[ci-badge]: https://github.com/keirf/flashfloppy/workflows/CI/badge.svg
[downloads-badge]: https://img.shields.io/github/downloads/keirf/flashfloppy/total
[version-badge]: https://img.shields.io/github/v/release/keirf/flashfloppy

View file

@ -3,11 +3,401 @@
** Keir Fraser <keir.xen@gmail.com>
************************************
** v3.42 - 11 January 2024
- HFEv3: Various read/write improvements
- WDATA: Merge short write pulses, and apply de-jitter/precomp
- IMG, EDSK: Stream large sector writes to flash
** v3.41 - 14 July 2023
- AT32F415: Fix timer handling since clock speed increase (v3.39).
- LCD: Faster power-on initialisation. Don't wait for display to clear.
** v3.40 - 27 June 2023
- XDF: Fix writes to side 1 of XDF disk images
- HFE: Better handling of long No Flux Areas
- QuickDisk: Reduce motor spinup time to 1 second (previously 2 seconds)
- LCD: Completely blank display when backlight is off
** v3.39 - 1 March 2023
- AT32F415: Run this MCU at 144MHz (previously 72MHz).
- HFE: Fix HFEv3 support. Support Dungeon Master & Chaos Strikes Back.
- IMG.CFG: Support mixed sector sizes per track (Ensoniq Mirage etc).
- IMG.CFG: New option img_bps= allows padding of short sectors in IMG files.
- FF.CFG: New option notify-volume= for notifying on insert/eject events.
- FF.CFG: New OLED display sub-option 'hflip' horizontally flips display.
- Various other small fixes.
** v3.38 - 4 December 2022
- AT32F435: Fix RDATA release on drive deassert
- SF7: Support Sega SF-7000 *.SF7 images
** v3.37 - 20 October 2022
- Amiga: Fix firmware crash when mounting read-only disk image
- Since v3.36 when interface=amiga and motor-delay= is configured
- Amiga: Respect MOTOR when motor-delay= is configured and disk ejected
- Previously the MOTOR signal was ignored when no disk inserted
** v3.36 - 10 October 2022
- AT32F435: Fix SD card handling on boards which support it
- FF.CFG, OSD: New options osd-columns= and osd-display-order=
- Amiga: Improved pin 34 ID/RDY emulation
- Requires AT32F435 MCU and MOR jumper strap
- Requires FF.CFG options: interface=amiga, motor-delay=500
** v3.35 - 4 August 2022
- AT32F435: Fix Quick Disk firmware
** v3.34 - 4 July 2022
- AT32F435: Fix startup to MCU spec (LDO voltage, Flash clock divisor)
- AT32F435: Cache HFE and QD image data, since there is plenty of RAM
- Allow rotary encoder on pins PA13/PA14 on any board except QFN32 MCU
- io-test: Fix io-test alt firmware for modern Gotek boards
** v3.33 - 20 June 2022
- Support new board SFRKC30.AT4.35
- Support new chip AT32F435 (288MHz M4, 256kB+ Flash, 384kB SRAM)
- New UPD file format and explanatory README: Please read!
** v3.32 - 28 May 2022
- Fix 'oled-font=8x16' option (broken in release 3.31)
- Improve drive-select ISR performance
- Fixes issues with fast Amiga accelerators
** v3.31 - 23 May 2022
- Fix Gotek drive detection with Amiga Kickstart ROM v3.2.1
- QD: FF.CFG alternative to JC jumper (no JC on SFRKC30.AT2 model)
- Place "interface=ibmpc" in FF.CFG
- OLED: New "-slow" modifier to slow down I2C bus for glitchy displays
- AUTOBOOT: Disallow writes to MBR, which can trash the USB drive
** v3.30 - 26 January 2022
- IMG.CFG: New option step= allows to specify double-step operation
- FF.CFG: New option max-cyl= allows limiting head-step range
- SFRKC30 Gotek Models: Improved rotary support on new "KC30" header
** v3.29 - 2 October 2021
- Support new Gotek board SFRKC30.AT2 using AT32F415KBU7-4 (QFN32)
- Support LQFP64 AT32F415RxT7 chips on existing "Enhanced Gotek" boards
- IMG: Fix default GAP2 for ED (eg. 2.88M) images
- IMG.CFG: New parameter gap2= to set Post-ID Gap value
** v3.28 - 2 July 2021
- Gotek model SFR1M44-U100LQD: Fix occasional rotary encoder issue
- This model uses SFRKC40.AT4.7 PCB with encoder on new pin header
- Rotary encoder would occasionally not be detected at power on
** v3.27 - 2 July 2021
- Fix v3.26 regression in button handling for GOEX hardware
** v3.26 - 24 June 2021
- Support new Gotek board SFRKC30.AT4.7
** v3.25 - 25 April 2021
- IMG, DSK: Fix track formatting on Artery microcontrollers
- Firmware would crash during format operations
** v3.24 - 15 April 2021
- Beta support for new Artery microcontrollers used on latest Gotek models
- See the wiki for more details
- https://github.com/keirf/FlashFloppy/wiki/Gotek-Compatibility
- IMG: Fix skew/interleave on MSDMF (1.68M) images
** v3.23 - 31 December 2020
- OLED/LCD: Fix missing folder name display row when inserting USB drive
- IMG.CFG: New examples for Roland, Sinclair QL, Kaypro
- IMG.CFG: Now supported in HxC-compat and indexed navigation modes
- IMG: Fix default 8-inch single-density data rate
- IMG, HFE: Support 255-cylinder image files
** v3.22 - 17 November 2020
- Fix various crashes when mounting a Flash drive with stale IMAGE_A.CFG
- FAT FS: Support filesystems for which cluster table is "too small"
- Windows and Linux will mount these volumes, thus so should we
- FF.CFG: New OLED display-type option: -inverse
- Reverse-video effect (black text on white background)
- FF.CFG: display-order= option affects normal display modes only
- Ignored in all banner and menu modes, to avoid jumbled display
- FF.CFG: Fix display-type parsing for OLED-specific options
- Ignore them unless an OLED display is explicitly configured
** v3.21 - 26 October 2020
- Support GUID Partition Table (GPT)
- Now support: GPT, MBR, and no partition table
- FatFS: Updated to R0.14, patchlevel 2
** v3.20 - 15 October 2020
- Eject Menu: Replace Clone with Copy/Paste
- Allows an image to be copied to a different folder
- Navigation mode: Copy to selected folder by holding Select for 1.5s
- FF.CFG: New option "rotary=v2" to use v2.x encoder logic
- Fixes a very few encoders which don't work with stricter v3.x logic
- FF.CFG: New option "twobutton-action=htu"
- Implements hundreds/tens/units button actions of the factory firmware
- IMG.CFG: New parameters:
- tracks: Track-scoped parameter lists (geometry can vary by track)
- h: Override default IDAM Head field
- gap4a: Override default post-index track gap
- IMG.CFG: Tag names are now case insensitive
- IMG.CFG: Tag by file size and/or name
- IMG.CFG: Implement some example configurations under examples/Host/
- IMG: Fix some of the more bizarre default track geometries
** v3.19 - 28 September 2020
- Amiga, ADF: Fix X-Copy verified writes with out-of-order sectors
- Remember the latest written sector order for current cylinder
- Forgotten after head step or disk eject but sufficient for X-Copy
- FF.CFG: New interface= options 'jppc' and 'jppc-hdout'
- jppc: pin2=nc, pin34=RDY, Japanese PC standard
- jppc-hdout: As above but pin2=HD_OUT, similar to ibmpc-hdout
- akai-s950 is retained as a legacy alias for jppc-hdout
** v3.18 - 23 September 2020
- OLED/LCD: Fix potential minor display corruption
- Only seen with 128x64 OLED displays, bottom right corner
- alt/logfile: Write FFLOG.TXT to correct folder (root or FF/)
- IMG, DSK: Various cleanups to sector-write handling
** v3.17 - 17 September 2020
- DSK: Fix infinite loop when trying to open a bad DSK image file
- Quick Disk: Restart read stream immediately after a write
- QD support now works on Sharp MZ-800
- IMG.CFG: Remove [default] stanza from example config
- Users copying the example file will no longer break their IMG files
** v3.16 - 22 August 2020
- Rotary encoder: Velocity curve during image navigation
- Fast spin of the encoder skips multiple entries per click
- LCD/OLED/OSD: Fix image name when display is wider than 24 characters
- HFE: Fix double-step HFE images (broken since v3.14a)
- HxC Compat: Strip filename extension from image name display row
** v3.15 - 23 July 2020
- New stable release series
- Improve rotary encoder robustness by sampling at 4x higher rate
** v3.14a - 15 July 2020
- Properly implement non-existent tracks as empty / unformatted
- In particular this avoids writing tracks beyond end of disk image
- See issue #309: Could corrupt HFE images
- Akai IMG: Implement track skew (faster access and fixes Oberheim DPX)
- Speaker: Rate-limit step pulses at the speaker
- Avoids high-freq chirp in Direct Access mode and Gotek-aware fast loaders
- HxC Compat, v9-FF:
- Atari ST keymap is now QWERTY
- Remove unused settings from drive setup menu
- Fix the search/filter box (Esc and Backspace keys)
** v3.13a - 13 March 2020
- FF.CFG: New option 'write-drain=eot'
- Fixes writes to Gotek on Amstrad PPC series (#320)
- FF.CFG: New option 'display-on-activity=sel'
- Turns on display whenever the Gotek drive is selected by host
- HFE: Respect double-step header flag
- Fixes 180k disk images on Amstrad PCW (#318)
** v3.12a - 7 February 2020
- IMG: Support IBM 3174 1.2M and 2.4M formats (host=ibm-3174)
- HxC: Fix HxC mode with no file selector (AUTOBOOT.HFE)
- FF OSD: Fix dual-display operation with slower OLED
- Wait for OLED to initialise when display-type=oled...
** v3.11a - 30 December 2019
- LCD/OLED: Fix subfolder name, as shown in 3- & 4-row displays
- Subfolder wasn't updated correctly when moving to parent (..) folder
** v3.10a - 28 December 2019
- Bootloader: Wait for buttons to be pressed then released...
- ...before starting firmware update process
- Avoids an infinite loop when reprogramming Bootloader itself
** v3.9a - 27 December 2019
- IMG: Fix density-select pin output for HD images
- Fixes 'pin02=dens' and 'interface=ibmpc-hdout'
- Bug has existed since v2.2a
- Rotary Encoder: Improve tracking
- Higher frequency, and better debounce algorithm
** v3.8a - 10 December 2019
- USB: Fix buffer overflow when parsing string descriptors
- Fixes crash with recent SanDisk Ultra Fit drives
- HxC Compat, v8-FF: Fixes startup crash on Atari ST
- Amiga: Fixes for "interface = amiga" setting
- TR-DOS (TRD): Fix geometry when TR-DOS header is incomplete
- Quick Disk: Larger read/write window in default blank image
- GCC9 build fixes
** v3.7a - 11 November 2019
- Quick Disk Bug Fixes
- Fix write offsets and lengths within QD track
- Filter ADF images from navigator: only QD images supported
- Extra logging in FFLOG.TXT for debug purposes
- Amiga AutoSwap new title: Gobliiiins
- Thanks to Arkadiusz Makarenko!
- https://github.com/keirf/flashfloppy-autoswap/wiki/Downloads
** v3.6a - 13 October 2019
- Quick Disk Initial Release
- New firmware and blank QD image in alt/quickdisk
- Documentation in the Wiki
- Update HxC Compat to v7-FF
- Fix failure when booted on unmodded A1200 ESCOM boards
** v3.5a - 9 October 2019
- New Main Menu: Press Prev/Next or Select with no Flash drive inserted
- Factory Reset, Firmware Update, OSD Config
- OSD: Must be updated to latest version: v1.7 or later!
- OSD: Receive Gotek button commands via I2C
- OSD: Default to 40 columns if no other display is connected
- TR-DOS (TRD): Fix geometry detection based on TR-DOS header
** v3.4a - 25 September 2019
- FF OSD support
- Bus sharing with existing LCD/OLED display
- Gotek buttons can be remoted to FF OSD for OSD configuration
** v3.3a - 8 July 2019
- FF.CFG: New Disk-Change Reset option: chgrst=delay-N
- Automatically clears the DSKCHG signal Nx0.5s after disk insertion
- chgrst=delay-3 fixes Disk Change on Ensoniq EPS synths
- Change head-step signal handling to reduce interrupt rate
- OLED/LCD: Fix current-folder display line for image-on-startup=init|static
- image-on-startup=static: The static image is now specified in INIT_A.CFG
** v3.2a - 25 June 2019
- OLED: New default layout for 128x64 displays
- LCD: Support 20x4 character displays
- LCD: Row ordering is configurable
- FF.CFG: Rename oled-text= to display-order= to reflect this
- Eject menu: Confirmation required for Delete/Clone operations
- Trackball: Reduce jitteriness by adding simple inertia
** v3.1a - 13 June 2019
- OLED: Text height and content can be configured (FF.CFG:oled-text=)
- New default for 128x64 displays presents a new third text row
- LCD/OLED: Present an action menu when an image is ejected
- Includes Clone and Delete Image operations
** v3.0a - 10 June 2019
- Disk Change Reset: Emulate explicit Reset signal of certain vintage drives
- Requires a hardware modification and FF.CFG: chgrst=pa14
- Support ZHONGJY_TECH 2.23" 128x32 displays based on SSD1305 controller
- FF.CFG: display-type=oled-128x32-ztech
** v2.13 - 7 June 2019
- HFE: Fix read buffering error
- Update HxC Compat to v6-FF
- More robust error checking on writes
** v2.12 - 2 June 2019
- HFEv3: Support flaky/weak bytes
- LCD/OLED: Improve power-on initialisation robustness
- Roland: Direct support for *.OUT images
- IO-Test: New alternative firmware to test Gotek I/O pins
- https://github.com/keirf/flashfloppy/wiki/Testing-IO-Pins
** v2.11a - 23 May 2019
- Simpler bootloader update process with all-in-one update file
- Update firmware via SD card (enhanced Goteks with SD card slot)
- FF.CFG: sort-priority= to prioritise folders vs files during navigation
- Default: Folders are listed before files
** v2.10a - 20 May 2019
- Fix breakages since v2.2a:
- host=acorn: *.ADF handling
- host=tandy-coco: *.DSK handling
** v2.9a - 17 May 2019
- FF.CFG: New option motor-delay= to provide emulation of motor behaviour
- Requires modification of a standard Gotek PCB (see Wiki: Hardware Mods)
- Debug logging: An alternative firmware is provided to log to FFLOG.TXT
- Find the alternative update file in alt/logfile/
- Use this for personal interest or as directed by me for debugging
- Not for general use!
- IMG/DSK: Better RAM handling (more space for mass-storage block cache)
- Code optimisation (MFM decode speeded up 4x)
- Small fix to generate INDEX signal when starting read near end of track
** v2.8a - 10 May 2019
- DSK, IMG: Fix small error in generated MFM data
- Increase write buffer to 32kB, enough for a full high-density track
- More reliable whole-track writes, especially to HFE images
** v2.7a - 23 April 2019
- Sort folders into alphabetical order during direct navigation
- FF.CFG: folder-sort= to modify this new default behaviour
- Fix memory corruption issues (stack overflow)
- ATR: Interleave sectors for best performance over slow SIO interface
** v2.6a - 14 April 2019
- Fix feature breakages in v2.5a
- SSD/DSD/TRD auto-extend feature
- FF.CFG: eject-on-startup=yes feature
- Update HxC Compat to v5-FF
** v2.5a - 12 April 2019
- Fragment-offsets cache implemented at the FAT filesystem layer
- Block cache implemented at the mass-storage volume layer
- Faster image selection/navigation in large folders
- Improved reliability in image handling (reduced access latencies)
- USB stick will usually stop flashing when drive is not being accessed!
- FF.CFG: New rotary input options
- 'trackball': Blackberry-style trackball
- 'buttons': Push-to-ground Prev/Next buttons
** v2.4a - 9 April 2019
- This release fixes many raw track formats (SSD, DSD, OPD, VDK, ...)
- Thanks to John Gray for bug report, and testing!
- RAW: Fix FM track generation
- FM and IAM flags not plumbed through from front-end image handlers
- RAW: Fix interleaved/skewed track generation
- Data fetch not loading sectors from image file in the right order
** v2.3a - 8 April 2019
- OLED: Auto-detect SSD1306 vs SH1106 display controller
- FF.CFG: 'display-type = ...-sh1106' is now redundant and ignored
- ATR: Fix readability of downloaded ATR images
- Invert data as it is read/written, mirroring the standard XF551 interface
- Thanks to Piotr for this report (using his XF551 clone)
** v2.2a - 5 April 2019
- New image types supported:
- XDF: 3.5" HD eXtended Disk Format (1840kB)
- ATR: Atari 8-bit
- 7-Seg LED: Improved track display.
- Remove option 'led-trk' from FF.CFG. Track display mode is always on.
- OLED: Fix horizontal offset on SH1106 128x32 displays
- IMG.CFG: 'id' parameter simplified; 'sskew' renamed 'hskew' (head skew)
** v2.1a - 26 March 2019
- IMG.CFG: Allow up to 256 sectors per track
- IMG.CFG: Split skew into per-cylinder and per-side values (cskew/sskew)
- IMG.CFG: Introduce image-file layout option (file-layout=<csv-list>):
- sequential,interleaved: Cylinder-first or side-first track ordering
- reverse-sideN: Side-N cylinders in reverse order (high to low) (N=0,1)
- sides-swapped: Ordering of disk sides is swapped in the image file
- 7-Segment LED: Display track number when an image is mounted
- FF.CFG: "display-type = led-trk"
- Displays slot number as usual during image selection/navigation
- First button press reverts to slot display; Next press begins navigation
- LCD/OLED: Display full cylinder number on displays wider than 16 columns
- Previously truncated at cylinder 99
** v2.0a - 14 March 2019
- IMG: Allow geometry to be manually configured in new file IMG.CFG
- See examples/IMG.CFG for documentation
- Multiple formats can be defined; distinguished by image name tags
- IMG: Support FM mode with Index Address Mark (IAM)
- IMG: Support auto-configuration of GAP3 (post data gap) when using IMG.CFG
- FF.CFG: Fix parsing of comma-separated values
- Indexed Mode: Allow arbitrary image name after "DSKAnnnn" prefix
- eg. DSKA0000_myimage.img, DSKA0002_another.adf
- Indexed Mode: Allow the "DSKA" prefix to be changed (FF.CFG:indexed-prefix=)
** v1.0 - 4 February 2019
- Game/demo AutoSwap-disks feature
- No manual disk swapping, at all!
- 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
- Indiana Jones and the Fate of Atlantis
- The Secret of Monkey Island

View file

@ -3,7 +3,7 @@ CC = $(TOOL_PREFIX)gcc
OBJCOPY = $(TOOL_PREFIX)objcopy
LD = $(TOOL_PREFIX)ld
PYTHON = python
PYTHON = python3
ifneq ($(VERBOSE),1)
TOOL_PREFIX := @$(TOOL_PREFIX)
@ -13,21 +13,39 @@ FLAGS = -g -Os -nostdlib -std=gnu99 -iquote $(ROOT)/inc
FLAGS += -Wall -Werror -Wno-format -Wdeclaration-after-statement
FLAGS += -Wstrict-prototypes -Wredundant-decls -Wnested-externs
FLAGS += -fno-common -fno-exceptions -fno-strict-aliasing
FLAGS += -mlittle-endian -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
FLAGS += -Wno-unused-value
FLAGS += -mlittle-endian -mthumb -mfloat-abi=soft
FLAGS += -Wno-unused-value -ffunction-sections
## STM32F105
ifeq ($(mcu),stm32f105)
FLAGS += -mcpu=cortex-m3 -DSTM32F105=1 -DMCU=1
ifeq ($(bootloader),y)
# Debug bootloader doesn't fit in 32kB
override debug=n
override logfile=n
endif
## AT32F435
else ifeq ($(mcu),at32f435)
FLAGS += -mcpu=cortex-m4 -DAT32F435=4 -DMCU=4
endif
ifneq ($(debug),y)
FLAGS += -DNDEBUG
endif
ifeq ($(reloader),y)
FLAGS += -DRELOADER=1
endif
ifeq ($(bootloader),y)
FLAGS += -DBOOTLOADER=1
endif
ifeq ($(logfile),y)
FLAGS += -DLOGFILE=1
endif
ifeq ($(quickdisk),y)
FLAGS += -DQUICKDISK=1
endif
FLAGS += -MMD -MF .$(@F).d
DEPS = .*.d
@ -37,12 +55,11 @@ CFLAGS += $(CFLAGS-y) $(FLAGS) -include decls.h
AFLAGS += $(AFLAGS-y) $(FLAGS) -D__ASSEMBLY__
LDFLAGS += $(LDFLAGS-y) $(FLAGS) -Wl,--gc-sections
RULES_MK := y
include Makefile
SRCDIR := $(shell $(PYTHON) $(ROOT)/scripts/srcdir.py $(CURDIR))
include $(SRCDIR)/Makefile
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).
.PHONY: FORCE
@ -56,21 +73,14 @@ build.o: $(OBJS)
$(LD) -r -o $@ $^
%/build.o: FORCE
+mkdir -p $*
$(MAKE) -f $(ROOT)/Rules.mk -C $* build.o
%.o: %.c Makefile
@echo CC $@
$(CC) $(CFLAGS) -c $< -o $@
%.o: %.S Makefile
@echo AS $@
$(CC) $(AFLAGS) -c $< -o $@
%.ld: %.ld.S Makefile
%.ld: $(SRCDIR)/%.ld.S $(SRCDIR)/Makefile
@echo CPP $@
$(CC) -P -E $(AFLAGS) $< -o $@
%.elf: $(OBJS) %.ld Makefile
%.elf: $(OBJS) %.ld $(SRCDIR)/Makefile
@echo LD $@
$(CC) $(LDFLAGS) -T$(*F).ld $(OBJS) -o $@
chmod a-x $@
@ -79,23 +89,25 @@ build.o: $(OBJS)
@echo OBJCOPY $@
$(OBJCOPY) -O ihex $< $@
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
@echo OBJCOPY $@
$(OBJCOPY) -O binary $< $@
chmod a-x $@
%.o: $(RPATH)/%.c Makefile
%.dfu: %.hex
$(PYTHON) $(ROOT)/scripts/dfu-convert.py -i $< $@
%.o: $(SRCDIR)/%.c $(SRCDIR)/Makefile
@echo CC $@
$(CC) $(CFLAGS) -c $< -o $@
%.o: $(RPATH)/%.S Makefile
%.o: $(SRCDIR)/%.S $(SRCDIR)/Makefile
@echo AS $@
$(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)

View file

@ -8,6 +8,7 @@ GPIn: (all must be 5v tolerant, *=not)
PB9 WGATE
PB4 SIDE
PA15 MOTOR [Enhanced Boards only]
PB15 MOTOR [Standard board mod: pin 5 of SPI Flash footprint]
GPOut:
PB7 CHNG
PB8 INDEX

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,28 +0,0 @@
RPATH = ../src
OBJS += update.o
OBJS += fpec.o
OBJS += build_info.o
OBJS += cancellation.o
OBJS += crc.o
OBJS += vectors.o
OBJS += fs.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

@ -1,19 +1,23 @@
## FF.CFG: Example FlashFloppy Configuration File
# Place in the root folder or FF/ subfolder of your USB drive.
# NOTE: If FF/ exists, IMG.CFG must reside there, not the root folder.
# Uncommented lines below are the default settings.
# Uncommented options cannot be overridden by settings in other config files.
##
## DRIVE EMULATION
# Floppy-drive interface mode
# shugart: P2=DSKCHG, P34=RDY
# ibmpc: P2=unused, P34=DSKCHG
# ibmpc-hdout: P2=HD_OUT, P34=DSKCHG (not generally needed: prefer 'ibmpc')
# akai-s950: P2=HD_OUT, P34=RDY (Akai S950)
# amiga: P2=DSKCHG, P34=DRIVE_ID (not generally needed: prefer 'shugart')
# jc: JC closed: ibmpc, JC open: shugart
# Floppy-drive interface mode (interface pins 2 and 34)
# jc: Specified by jumper JC (open: shugart, closed: ibmpc)
# shugart: P2=DSKCHG, P34=RDY (Amiga, Atari ST, many others)
# ibmpc: P2=unused, P34=DSKCHG (IBM PC interface)
# ibmpc-hdout: P2=HD_OUT, P34=DSKCHG (not generally needed: prefer ibmpc)
# jppc: P2=unused, P34=RDY (Japanese PC standard)
# jppc-hdout: P2=HD_OUT, P34=RDY (Japanese PC alternate: prefer jppc)
# akai-s950: Legacy alias of jppc-hdout, previously used for Akai S950
# amiga: P2=DSKCHG, P34=DRIVE_ID (not generally needed: prefer shugart)
interface = jc
# Host platform: Improves image-format detection for generic types such as IMG
@ -24,7 +28,7 @@ interface = jc
# ensoniq: Ensoniq (ASR, TS, etc)
# fluke: Fluke 9100
# gem: General Music (S2, S3)
# kaypro: Kaypro
# ibm-3174: IBM 3174 Establishment Controller
# memotech: Memotech
# msx: MSX
# nascom: Nascom
@ -55,16 +59,28 @@ pin34 = auto
# Values: yes | 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
# Values: 0 <= N <= 255
side-select-glitch-filter = 0
# Rotational offset of data after a track change
# Rotational offset of disk after a track change
# instant: No rotation during track change
# realtime: Emulate rotation of disk while track is changing
# Values: instant | realtime
track-change = instant
# Rotational offset of disk after draining a write to Flash
# instant: No rotation
# realtime: Disk rotates in real time during drain
# eot: Disk rotates to (near) end of track
# Values: instant | realtime | eot
write-drain = instant
# Index pulses suppressed when RDATA and WDATA inactive?
# Values: yes | no
index-suppression = yes
@ -73,6 +89,19 @@ index-suppression = yes
# Values: 0 <= N <= 255
head-settle-ms = 12
# Milliseconds delay from motor-on to drive ready.
# On a standard unmodified Gotek the motor signal is not connected and a
# non-default value here will have no effect. Most systems and software do
# not care about correct motor behaviour, and default (ignore) works fine.
# Values: ignore | 0 <= N <= 1000
motor-delay = ignore
# What causes the disk-change (chg) signal to reset after disk insertion?
# step: Step command received
# pa14: CHGRST (pin 1 on old Sony drives), connected to PA14 (JTCK/SWCLK)
# delay-N: Automatically after N*0.5sec (0 <= N <= 15)
chgrst = step
##
## STARTUP / INITIALISATION
@ -82,7 +111,7 @@ ejected-on-startup = no
# Which image (or folder) is selected at startup?
# last: Last-selected item at power-off (recorded in IMAGE_A.CFG)
# static: Static path specified in IMAGE_A.CFG
# static: Static path specified in INIT_A.CFG
# init: First item in root folder
# Values: last | static | init
image-on-startup = last
@ -105,6 +134,20 @@ autoselect-file-secs = 2
# Values: 0 <= N <= 255
autoselect-folder-secs = 2
# Sorting of folder entries in native navigation mode.
# always: Always sort folder entries. Large folders may be truncated.
# never: Never sort folder entries, instead presenting them in FAT order.
# small: Only sort folders which are small enough to sort in full.
# Values: always | never | small
folder-sort = always
# Priority of files vs subfolders when sorting folder entries:
# folders: Folders listed before files
# files: Files listed before folders
# none: Files and folders are not differentiated
# Values: folders | files | none
sort-priority = folders
# Navigation mode for selecting images/slots
# native: Navigate through all valid images/dirs
# indexed: Navigate through DSKA0000, DSKA0001, ...
@ -119,33 +162,50 @@ nav-loop = yes
# B1 | B2 | Both
# zero: Prev | Next | Slot 0
# eject: Prev | Next | Eject/Insert
# htu: +10 | +1 | +100
# rotary: Up-dir | Select/Eject/Insert | -
# rotary-fast: Prev | Next | Up-dir [Prev/Next are accelerated]
# reverse: Reverse sense of B1 and B2
# Multiple values can be separated by commas, eg twobutton-action=eject,reverse
twobutton-action = zero
# Type of rotary encoder connected to pins PC10 and PC11, identified by
# fraction of a Gray-code cycle performed per detent/click.
# If default value ('full') requires multiple clicks/detents to move position
# then change to 'half' (if 2 clicks per move) or 'quarter' (if 4 clicks).
# If the encoder works in reverse, use option 'reverse' to swap directions.
# Input sensor type at the rotary-encoder inputs (pins PC10 and PC11):
# [full | half | quarter]:
# Rotary encoder, identified by fraction of a Gray-code cycle performed
# per detent/click. If default value (full) requires multiple
# clicks/detents to move position then change to half (if 2 clicks
# per move) or quarter (if 4 clicks).
# [trackball]:
# Blackberry-style trackball (eg. using Hall-effect sensors).
# [buttons]:
# Push-to-ground Prev/Next buttons.
# [reverse]:
# If the input is working in reverse, use this option to swap directions.
# [v2]:
# Use the rotary encoder logic from FlashFloppy v2.x. Use this if the
# v3 logic is too strict and results in no, or missing, movements.
# Multiple values can be separated by commas, eg rotary=quarter,reverse
# Values: none | quarter | half | full | reverse
# Values: none | quarter | half | full | trackball | buttons | reverse | v2
rotary = full
# Prefix for image names in indexed navigation mode. String can be empty ("").
indexed-prefix = "DSKA"
##
## DISPLAY
# Display Type: <type>[-rotate][-narrow][-sh1106]
# Display Type.
# auto: Auto-detect (7-seg LED, LCD, OLED)
# lcd-NNx02: NNx2 backlit LCD with I2C backpack (16 <= NN <= 40)
# lcd-CCxRR: CCxRR backlit LCD with I2C backpack (16<=CC<=40, 02<=RR<=04)
# oled-128xNN: 128xNN I2C OLED (NN = 32 | 64)
# -rotate: OLED view is rotated 180 degrees
# -hflip: OLED view is flipped horizontally
# -narrow[er]: OLED view is restricted to Gotek display cutout
# (-narrow: 18 chars; -narrower: 16 chars)
# -sh1106: OLED controller is SH1106, not SSD1306
# Values: auto | lcd-NNx02 | oled-128xNN[-rotate][-narrow[er]][-sh1106]
# -inverse: Inverse/reverse video (black text on white background)
# -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]]...
display-type = auto
# OLED Font. Narrow and wide options.
@ -159,13 +219,34 @@ oled-font = 6x13
# Values: 0 <= N <= 255
oled-contrast = 143
# Text height and arrangement on LCD/OLED and on OSD, respectively.
# Comma-separated list, one entry per display row, top down.
# Each list item is a digit plus optional height specifier: [0-7][d]
# content-row: 0-3 = specified content, 7 = blank
# 0: Current image name
# 1: Status
# 2: Image/Volume info
# 3: Current subfolder name
# height-specifier: d = double height (32px, OLED only; ignored for LCD)
# Default depends on display, eg.: oled-128x32 -> 0,1 ; oled-128x64 -> 3,0d,1
# Values: [0-7][d] | 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
# N=0: always off; N=255: always on
# Values: 0 <= N <= 255
display-off-secs = 60
# Automatically switch LCD/OLED display on when there is drive activity?
# Values: yes | no
# Switch on LCD/OLED display when there is drive activity?
# yes: Trigger on track changes and disk writes
# sel: Trigger on drive select
# no: No automatic trigger
# Values: yes | sel | no
display-on-activity = yes
# LCD/OLED long filename scroll rate in milliseconds per update
@ -192,6 +273,14 @@ nav-scroll-pause = 300
# Values: 0 <= N <= 20
step-volume = 10
# Speaker volume for insert, eject, and slot-number notifications.
# Slot number is indicated by a sequence of beeps when an image is mounted
# iff "slotnr" is specified. The slot number is then notified by a sequence of
# long beeps (each counting +5), followed by a sequence of short beeps
# (each counting +1).
# Values: N[,slotnr] (0 <= N <= 15)
notify-volume = 0
# Report the specified version number to host software
# Values: <quoted-string> ("" means report real version)
# eg. da-report-version = "v3.0.0.0"

View file

@ -0,0 +1,18 @@
## IMG.CFG for Bung Multi Game Doctor (MGD) 2.
# NOTE: The tags match on filesize alone. If you wish to define an explicit
# tagname match, you can for example add 'mgd2' to the square-bracketed tags
# to limit matches to filenames of the form *.mgd2.{img,ima,dsk}
# Images are 1756kB exactly.
[::1798144]
cyls = 80
heads = 2
tracks = 0
secs = 18
bps = 512
tracks = 1-79
secs = 11
bps = 1024
gap3 = 30

View file

@ -0,0 +1,170 @@
## IMG.CFG: FlashFloppy Geometry Configuration File for Raw IMG Images
## For CompuPro 8-16 8" Floppy disks.
# Tested on a CompuPro 8-16 System using the 50-to-34 pin adapter
# board available here: https://gitlab.com/NF6X_Retrocomputing/fd50to34
#
# Make sure to set the jumpers on the fd50to34:
# * 2SIDE - Install if emulated floppy disk is double-sided.
# * MTRON - Install
#
# In FF.CFG, set interface=shugart
#
# Supports tagged IMG/IMA raw image files with self-identifying geometry.
# If you wish to use this feature, your IMG.CFG must be placed in the
# root folder or FF/ subfolder of your USB drive.
# NOTE: If FF/ exists, IMG.CFG must reside there, not the root folder.
# A badly-defined section (eg. undefined mandatory parameters) may result in
# error 31 (Bad Image).
####################################################################
## TAG in square brackets.
## Format: [<tagname>][::<filesize>]
## Matching Rules:
## 1. <tagname> matches images of the form *.<tagname>.{img,ima,dsk}
## 2. Missing (empty) tagname matches any *.{img,ima,dsk} which is untagged
## or does not match any other defined tag.
## 3. Tagnames and their matches are NOT case sensitive.
## 4. <filesize>, if specified, must exactly match the image filesize.
## 5. If an image matches no tag, FlashFloppy uses normal geometry
## auto-detection based on the host= setting in FF.CFG.
####################################################################
# Definition for standard CP/M 8" SS/DD.
#
# CP/M 8" SS/SD format is single-density (FM) with 26 128-byte
# sectors on all cylinders.
[::256256]
cyls = 77
heads = 1
rpm = 360
gap3 = 42
interleave = 1
bps = 128 ## 128-byte sectors
secs = 26
mode = fm
id = 1
####################################################################
# Definition for CompuPro 8" SS/DD.
#
# CP/M 8" SS/DD format is double-density but with a single-density
# (FM) cylinder 0.
[::625920]
cyls = 77
heads = 1
interleave = 1
rpm = 360
gap3 = 42
tracks = 0 ## Boot cylinder, head 0
bps = 128 ## Cylinder 0 has 128-byte sectors
secs = 26
mode = fm
id = 1
tracks = 1-76 ## All other cylinders
bps = 1024
secs = 8
mode = mfm
id = 1
####################################################################
# Definition for CompuPro 8" DS/SD.
#
# CP/M 8" DS/SD format is single density, 128-byte sectors.
[::512512]
cyls = 77
heads = 2
interleave = 1
rpm = 360
gap3 = 42
bps = 128
secs = 26
mode = fm
id = 1
####################################################################
# Definition for CompuPro 8" DS/DD 1024-byte sectors.
#
# CP/M 8" DS/DD format is double-density but with a single-density
# (FM) track 0.
[::1256704]
cyls = 77
heads = 2
interleave = 1
rpm = 360
gap3 = 42
tracks = 0.0 ## Boot cylinder, head 0
bps = 128 ## Cylinder 0, head 0 has 128-byte sectors
secs = 26
mode = fm
id = 1
tracks = 0.1 ## Boot cylinder, head 1
bps = 1024 ## Cylinder 0, head 1 has 1024-byte sectors
secs = 8
mode = mfm
id = 1
tracks = 1-76 ## All other cylinders
bps = 1024
secs = 8
mode = mfm
id = 1
####################################################################
# Definition for CompuPro 8" DS/DD 512-byte sectors.
#
# CP/M 8" DS/DD format is double-density but with a single-density
# (FM) track 0.
[::1178368]
cyls = 77
heads = 2
interleave = 1
rpm = 360
gap3 = 42
tracks = 0.0 ## Boot cylinder, head 0
bps = 128 ## Cylinder 0, head 0 has 128-byte sectors
secs = 26
mode = fm
id = 1
tracks = 0.1 ## Boot cylinder, head 1
bps = 512 ## Cylinder 0, head 1 has 512-byte sectors
secs = 15
mode = mfm
id = 1
tracks = 1-76 ## All other cylinders
bps = 512
secs = 15
mode = mfm
id = 1
####################################################################
# Definition for CompuPro 8" DS/DD 256-byte sectors.
#
# CP/M 8" DS/DD format is double-density but with a single-density
# (FM) track 0.
[::1021696]
cyls = 77
heads = 2
interleave = 1
rpm = 360
gap3 = 42
secs = 26
tracks = 0.0 ## Boot cylinder, head 0
bps = 128 ## Cylinder 0, head 0 has 128-byte sectors
mode = fm
id = 1
tracks = 0.1 ## Boot cylinder, head 1
bps = 256 ## Cylinder 0, head 1 has 256-byte sectors
secs = 26
mode = mfm
id = 1
tracks = 1-76 ## All other cylinders
bps = 256
secs = 26
mode = mfm
id = 1

View file

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

View file

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

View file

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

View file

@ -0,0 +1,40 @@
## IMG.CFG for Kaypro systems.
# NOTE: The tags match on filesize alone. If you wish to define an explicit
# tagname match, you can for example add 'kp' to the square-bracketed tags
# to limit matches to filenames of the form *.kp.{img,ima,dsk}
# 200kB: SSDD, 40 cyl
[::204800]
cyls = 40
heads = 1
secs = 10
bps = 512
interleave = 3
id = 0
# 400kB: DSDD, 40 cyl
[::409600]
cyls = 40
heads = 2
secs = 10
bps = 512
interleave = 3
h = 0
tracks = 0-39.0
id = 0
tracks = 0-39.1
id = 10
# 800kB: DSDD, 80 cyl
[::819200]
cyls = 80
heads = 2
secs = 10
bps = 512
interleave = 3
h = 0
tracks = 0-79.0
id = 0
tracks = 0-79.1
id = 10

View file

@ -0,0 +1,40 @@
## IMG.CFG for Osborne systems.
# NOTE: The tags match on filesize alone. If you wish to define an explicit
# tagname match, you can for example add 'occ1' to the square-bracketed tags
# to limit matches to filenames of the form *.occ1.{img,ima,dsk}
# Osborne 1 OCC1/1A (for units without double density upgrade fitted)
# SSSD 100Kb
[::102400]
cyls = 40
heads = 1
secs = 10
bps = 256
cskew = 2
mode = fm
iam = no
# Osborne Executive OCC2 (also OCC1/1A with double density upgrade fitted)
# SSDD 200Kb
[::204800]
cyls = 40
heads = 1
secs = 5
bps = 1024
mode = mfm
iam = yes
# Osborne Vixen OCC4 (also Executive OCC2 with double sided upgrade fitted)
# DSDD 400Kb
# Note: Multiple sizes for OCC4. Covered here with a default rule but could
# instead explicitly handle the following sizes if the square-bracket syntax
# is ever extended to support it: 409600, 409728, 409856, 419840
[]
cyls = 40
heads = 2
secs = 5
bps = 1024
cskew = 2
mode = mfm
iam = yes

View file

@ -0,0 +1,60 @@
## IMG.CFG for faster loading of Roland OS and System disks.
## Some Roland disks have 'sector interleave' and if this is missing (as in
## default 720kB track configuration) the OS and System Utilities can load
## approximately 5x slower than expected. The configurations below fix this.
# NOTES:
#
# 1. OS and System images must be named as described below.
#
# 2. By default 'os' and 'sys' tags apply sector interleave to all tracks.
# Some disks (eg S-330 OS disk) contain sample data with no interleave:
# They will load 2x slower than expected. If this concerns you, adjust the
# 'track=' line to cover only the tracks requiring interleave (eg. '0-8').
#
# 3. You can copy and paste the sections below to create further tags as
# needed for your specific set of OS and System disks.
# Matches 720kB images named *.os.{img,ima,dsk}.
[os::737280]
cyls = 80
heads = 2
secs = 9
bps = 512
tracks = 0-79 # This line can be adjusted
interleave = 2
hskew = 1
cskew = 2
# Matches 720kB images named *.sys.{img,ima,dsk}.
[sys::737280]
cyls = 80
heads = 2
secs = 9
bps = 512
tracks = 0-79 # This line can be adjusted
interleave = 2
hskew = 1
cskew = 2
# Matches 720kB images named *.mc.{img,ima,dsk}.
# This higher level of interleave is found on all disks for the
# Roland MC-300, MC-500 and MC-50.
[mc::737280]
cyls = 80
heads = 2
secs = 9
bps = 512
tracks = 0-79
interleave = 4
# Roland 1.44MB format may apply sector skew.
# This is as seen on a Roland MT-200.
[::1474560]
cyls = 80
heads = 2
secs = 18
bps = 512
hskew = 3
cskew = 6

View file

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

View file

@ -0,0 +1,13 @@
## IMG.CFG for Sinclair QL ED Disks (3.2MB)
# NOTE: The tags match on filesize alone. If you wish to define an explicit
# tagname match, you can for example add 'ql' to the square-bracketed tags
# to limit matches to filenames of the form *.ql.{img,ima,dsk}
# Images are 3200kB exactly.
[::3276800]
cyls = 80
heads = 2
secs = 10
bps = 2048

View file

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

View file

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

View file

@ -0,0 +1,68 @@
## IMG.CFG for TSC Flex hosts.
# NOTE: The tags match on filesize alone. If you wish to define an explicit
# tagname match, you can for example add 'flex' to the square-bracketed tags
# to limit matches to filenames of the form *.flex.{img,ima,dsk}
# DS/DD with SD (FM) cylinder 0.
# Sector numbering and interleave is continuous across drive heads. Since
# there is no direct way to flag this, we implement it here by explicitly
# specifying head 1 track formats: with sector @id following on from head 0,
# and with @hskew shifting the first sector of side 1 the correct amount to
# simulate cross-track interleave.
[::733184]
cyls = 80
heads = 2
bps = 256 # All tracks have 256-byte sectors. Number of sectors varies.
iam = no
gap3 = 16
gap4a = 16
tracks = 0.0 ## Boot cylinder, head 0
secs = 10
mode = fm
interleave = 4
id = 1
h = 0
tracks = 0.1 ## Boot cylinder, head 1
secs = 10
mode = fm
interleave = 4
hskew = 1 # Simulates correct inter-track interleave
id = 11 # Follows on from head 0 (1..10 -> 11..20)
h = 0
tracks = 1-79.0 ## All other cylinders, head 0
secs = 18
mode = mfm
interleave = 6
id = 1
h = 1
tracks = 1-79.1 ## All other cylinders, head 1
secs = 18
mode = mfm
interleave = 6
hskew = 5 # Simulates correct inter-track interleave
id = 19 # Follows on from head 0 (1..18 -> 19..36)
h = 1
# SS/DD with SD (FM) cylinder 0.
# Definition is a simplified version of the DS/DD format.
[::366592]
cyls = 80
heads = 1
bps = 256
iam = no
gap3 = 16
gap4a = 16
id = 1
tracks = 0.0
secs = 10
mode = fm
interleave = 4
h = 0
tracks = 1-79.0
secs = 18
mode = mfm
interleave = 6
h = 1

172
examples/IMG.CFG Normal file
View file

@ -0,0 +1,172 @@
## IMG.CFG: Example FlashFloppy Geometry Configuration File for Raw IMG Images
# Supports tagged IMG/IMA raw image files with self-identifying geometry.
# If you wish to use this feature, your IMG.CFG must be placed in the
# root folder or FF/ subfolder of your USB drive.
# NOTE: If FF/ exists, IMG.CFG must reside there, not the root folder.
# A badly-defined section (eg. undefined mandatory parameters) may result in
# error 31 (Bad Image).
####################################################################
## TAG in square brackets.
## Format: [<tagname>][::<filesize>]
## Matching Rules:
## 1. <tagname> matches images of the form *.<tagname>.{img,ima,dsk}
## 2. Missing (empty) tagname matches any *.{img,ima,dsk} which is untagged
## or does not match any other defined tag.
## 3. Tagnames and their matches are NOT case sensitive.
## 4. <filesize>, if specified, must exactly match the image filesize.
## 5. If an image matches no tag, FlashFloppy uses normal geometry
## auto-detection based on the host= setting in FF.CFG.
# [dsdd80::737280] matches images with names of the form *.dsdd80.{img,ima,dsk}
# and with size exactly 720kB (737280 bytes).
[dsdd80::737280]
## DISK-SCOPE PARAMETERS
# Apply to the whole disk image. Cannot appear in a @tracks sub-section.
# Mandatory: Number of cylinders (1-255).
cyls = 80
# Mandatory: Number of heads (1-2).
heads = 2
# Number of drive-head steps between cylinders. Default is 1.
# step = 1
# Image file track layout. Default is "interleaved".
# Comma-separated values:
# sequential: Sequential cylinder ordering: all side 0, then side 1.
# interleaved: Interleaved cylinder ordering: c0s0, c0s1, c1s0, c1s1, ...
# reverse-sideN: Side-N cylinders are ordered from high to low (N=0,1).
# sides-swapped: Sides 0 and 1 ordering is swapped in the image file.
# eg. "sequential,reverse-side1"
# file-layout = interleaved
## TRACK-SCOPE PARAMETERS
# These can vary across tracks. By default they apply to all tracks unless
# preceded by a @tracks declaration.
# tracks = <track-list>
# Specify the tracks that the following track-scope parameters applies to.
# Format:
# track-list ::= <track-range>[,<track-list>]
# track-range ::= <cylinder>[.<head>] | <cylinder>-<cylinder>[.<head>]
# cylinder ::= [0-9][0-9]*
# head ::= 0|1
# If no head is specified in a track-range, then all heads are assumed.
# Examples:
# "0-23.1,45.0" specifies head 1 of cyls 0-23 and head 0 of cyl 45.
# "27-33" specifies all heads of cyls 27-33.
# Number of sectors per track (0-256). Default is 0.
secs = 9
# Bytes per sector (128, 256, 512, 1024, 2048, 4096, 8192).
# Mandatory if @secs is non-zero.
bps = 512
# Alternative form in which bytes per sector is specified per sector.
# The list is comma separated; no white space allowed.
# bps = 512,512,512,512,512,512,512,512,256
# Bytes per sector within the IMG file. Smaller sectors will be padded.
# The default is 0: Sectors occupy precisely their data size; No padding.
# Supported values: 0, 128, 256, 512, 1024, 2048, 4096, 8192.
# img_bps = 0
# ID of first sector on each track (0-255). Default is 1.
# Numbers may be expressed in hexadecimal with 0x prefix (eg. 0xab).
# id = 1
# Head number for all sectors on this track (auto|0|1). Default is auto.
# auto = use physical drive head number
# h = auto
# Recording mode (fm | mfm). Default is mfm.
# mode = mfm
# Sector interleave. Default is 1:1 (no interleave).
# interleave = 1
# Sector skew per cylinders. Default is 0 (no skew).
# cskew = 0
# Sector skew per head. Default is 0 (no skew).
# hskew = 0
# Rotational RPM. Default is 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.
# auto = based on recording mode and sector size.
# gap3 = auto
# Post-Index Gap (auto|0-255). Default is auto.
# auto = based on recording mode and sector size.
# gap4a = auto
# Index Address Mark (yes | no). Default is yes.
# iam = yes
# Data rate in kHz (kbit/s) (eg. MFM DD = 250). Default is 0.
# 0 = based on recording mode and size of track.
# rate = 0
####################################################################
# An example definition for a Kaypro DS/DD 40-track image.
[kaypro-dsdd40]
cyls = 40
heads = 2
interleave = 3
secs = 10
bps = 512
tracks = 0-39.0
id = 0 # Side 0, sector IDs 0..9
tracks = 0-39.1
id = 10 # Side 1, sector IDs 10..19
####################################################################
# An example definition for TSC Flex.
#
# Flex format is double-density but with single-density (FM) cylinder 0.
# Sector numbering and interleave is continuous across drive heads. Since
# there is no direct way to flag this, we implement it here by explicitly
# specifying head 1 track formats: with sector @id following on from head 0,
# and with @hskew shifting the first sector of side 1 the correct amount to
# simulate cross-track interleave.
[flex::733184]
cyls = 80
heads = 2
bps = 256 # All tracks have 256-byte sectors. Number of sectors varies.
tracks = 0.0 ## Boot cylinder, head 0
secs = 10
mode = fm
interleave = 4
id = 1
tracks = 0.1 ## Boot cylinder, head 1
secs = 10
mode = fm
interleave = 4
hskew = 1 # Simulates correct inter-track interleave
id = 11 # Follows on from head 0 (1..10)
tracks = 1-79.0 ## All other cylinders, head 0
secs = 18
mode = mfm
interleave = 6
id = 1
tracks = 1-79.1 ## All other cylinders, head 1
secs = 18
mode = mfm
interleave = 6
hskew = 5 # Simulates correct inter-track interleave
id = 19 # Follows on from head 0 (1..18)

44
inc/cache.h Normal file
View file

@ -0,0 +1,44 @@
/*
* cache.h
*
* In-memory data cache.
*
* 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>.
*/
#if !defined(BOOTLOADER)
/* 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);
/* 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);
/* Update item @id with data @dat. Inserts the item if not present.*/
void cache_update(struct cache *c, uint32_t id, const void *dat);
/* Update @N items (@id..@id+@N-1) with data @dat. Calls cache_update(). */
void cache_update_N(struct cache *c, uint32_t id,
const void *dat, unsigned int N);
#else
#define cache_init(a,b,c) NULL
#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:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

View file

@ -21,9 +21,11 @@ struct opts {
};
int get_next_opt(struct opts *opts);
#define OPT_eof -1
#define OPT_section -2
/* FF.CFG options structure. */
struct __packed ff_cfg {
struct packed ff_cfg {
/* Bump version for every incompatible change to structure layout.
* No need to bump for new fields appended to this structure. */
#define FFCFG_VERSION 2
@ -39,7 +41,10 @@ struct __packed ff_cfg {
uint8_t autoselect_folder_secs;
bool_t nav_loop; /* Wrap slot number at 0 and max? */
uint8_t display_off_secs;
bool_t display_on_activity; /* Display on when there is drive activity? */
#define DISPON_no 0
#define DISPON_yes 1
#define DISPON_sel 2
uint8_t display_on_activity;
uint16_t display_scroll_rate;
#define FONT_6x13 7
#define FONT_8x16 8
@ -56,7 +61,8 @@ struct __packed ff_cfg {
#define TWOBUTTON_eject 1
#define TWOBUTTON_rotary 2
#define TWOBUTTON_rotary_fast 3
#define TWOBUTTON_mask 3
#define TWOBUTTON_htu 4
#define TWOBUTTON_mask 7
#define TWOBUTTON_reverse (1u<<7)
uint8_t twobutton_action;
#define NAVMODE_default 0
@ -80,26 +86,37 @@ struct __packed ff_cfg {
#define HOST_dec 11
#define HOST_tandy_coco 12
#define HOST_fluke 13
#define HOST_kaypro 14
#define HOST_nascom 15
#define HOST_casio 16
#define HOST_ibm_3174 17
uint8_t host;
/* Bitfields within display_type field. */
#define DISPLAY_auto 0
#define DISPLAY_lcd (1<<0)
#define DISPLAY_oled (1<<1)
#define DISPLAY_narrower (1<<0) /* only if DISPLAY_oled */
#define DISPLAY_rotate (1<<2) /* only if DISPLAY_oled */
#define DISPLAY_narrow (1<<3) /* only if DISPLAY_oled */
#define DISPLAY_sh1106 (1<<4) /* only if DISPLAY_oled */
#define DISPLAY_oled_64 (1<<5) /* only if DISPLAY_oled */
#define _DISPLAY_lcd_columns 5 /* only if DISPLAY_lcd */
/* Only if DISPLAY_oled: */
#define DISPLAY_narrower (1<<0)
#define DISPLAY_rotate (1<<2)
#define DISPLAY_narrow (1<<3)
#define DISPLAY_ztech (1<<4)
#define DISPLAY_oled_64 (1<<5)
#define DISPLAY_inverse (1<<6)
#define DISPLAY_slow (1<<7)
#define DISPLAY_hflip (1<<8)
/* Only if DISPLAY_lcd: */
#define _DISPLAY_lcd_columns 5
#define DISPLAY_lcd_columns(x) ((x)<<_DISPLAY_lcd_columns)
#define _DISPLAY_lcd_rows 11
#define DISPLAY_lcd_rows(x) ((x)<<_DISPLAY_lcd_rows)
uint16_t display_type;
#define ROT_none 0
#define ROT_full 1
#define ROT_half 3
#define ROT_quarter 2
#define ROT_none 0
#define ROT_full 1
#define ROT_half 3
#define ROT_quarter 2
#define ROT_trackball 4
#define ROT_buttons 5
#define ROT_typemask 15
#define ROT_v2 (1u<<6)
#define ROT_reverse (1u<<7)
uint8_t rotary;
bool_t write_protect;
@ -122,12 +139,43 @@ struct __packed ff_cfg {
uint8_t pin02, pin34;
uint8_t head_settle_ms;
uint8_t oled_contrast;
char indexed_prefix[8];
uint8_t _unused; /* never been used */
#define SORT_never 0
#define SORT_always 1
#define SORT_small 2
uint8_t folder_sort;
#define MOTOR_ignore 0xff
uint8_t motor_delay; /* / 10ms */
#define SORTPRI_folders 0
#define SORTPRI_files 1
#define SORTPRI_none 2
uint8_t sort_priority;
#define CHGRST_step 0xff
#define CHGRST_pa14 0x8e
#define CHGRST_delay(x) (x)
uint8_t chgrst;
#define DORD_default 0xffff
#define DORD_shift 4
#define DORD_row 7
#define DORD_double 8
uint16_t display_order;
#define WDRAIN_instant 0
#define WDRAIN_realtime 1
#define WDRAIN_eot 2
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 const struct ff_cfg dfl_ff_cfg;
void flash_ff_cfg_update(void);
void flash_ff_cfg_update(void *scratch);
void flash_ff_cfg_erase(void);
void flash_ff_cfg_read(void);

View file

@ -14,7 +14,7 @@
#define DA_DD_MFM_CYL (DA_FIRST_CYL + 1)
/* Direct-Access Mode: Returned in sector 0 of direct-access track. */
struct __packed da_status_sector {
struct packed da_status_sector {
char sig[8];
char fw_ver[12];
uint32_t lba_base;
@ -32,7 +32,7 @@ struct __packed da_status_sector {
};
/* Direct-Access Mode: Sent to us in sector 0 of direct-access track. */
struct __packed da_cmd_sector {
struct packed da_cmd_sector {
char sig[8];
uint8_t cmd;
uint8_t param[8];

View file

@ -13,16 +13,28 @@
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#include <limits.h>
#include "stm32f10x_regs.h"
#include "stm32f10x.h"
#include "types.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 "time.h"
#include "../src/fatfs/ff.h"
#include "util.h"
#include "list.h"
#include "cache.h"
#include "da.h"
#include "hxc.h"
#include "util.h"
#include "cancellation.h"
#include "spi.h"
#include "timer.h"

View file

@ -9,11 +9,25 @@
* 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
#define is_quickdisk TRUE
#else
#define is_quickdisk FALSE
#endif
#define FINTF_SHUGART 0
#define FINTF_IBMPC 1
#define FINTF_IBMPC_HDOUT 2
#define FINTF_AKAI_S950 3
#define FINTF_JPPC_HDOUT 3
#define FINTF_AMIGA 4
#define FINTF_JPPC 5
#define outp_dskchg 0
#define outp_index 1
@ -24,44 +38,87 @@
#define outp_nr 6
#define outp_unused outp_nr
#define verbose_image_log FALSE
struct adf_image {
uint32_t trk_off;
uint16_t trk_pos, trk_len;
uint32_t sec_idx;
int32_t decode_pos;
uint32_t pre_idx_gap_bc;
uint32_t nr_secs;
uint32_t written_secs;
uint8_t sec_map[2][22];
};
struct hfe_image {
uint16_t tlut_base;
uint16_t trk_off;
uint16_t trk_pos, trk_len;
bool_t is_v3;
bool_t is_v3, double_step;
uint8_t batch_secs;
struct {
uint16_t start;
bool_t wrapped;
} write;
struct {
uint16_t off, len;
bool_t dirty;
} write_batch;
};
struct qd_image {
uint16_t tb;
uint32_t trk_off;
uint32_t trk_pos, trk_len;
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 {
uint8_t r;
uint8_t n; /* 3 bits */
};
struct raw_trk {
uint16_t nr_sectors;
uint16_t sec_off;
uint16_t data_rate;
uint16_t rpm;
uint16_t img_bps; /* could squeeze this field into uint8_t or bitfield */
int16_t gap_2, gap_3, gap_4a;
uint8_t interleave, cskew, hskew;
uint8_t has_iam:1, is_fm:1, invert_data:1;
#define RAW_TRK_HEAD(h) ((h)+1)
uint8_t head:2;
};
struct img_image {
uint32_t trk_off, base_off;
uint16_t trk_sec, rd_sec_pos;
uint16_t rpm;
int32_t decode_pos;
uint16_t decode_data_pos, crc;
uint8_t layout; /* LAYOUT_* */
bool_t has_iam;
uint8_t gap_2, gap_3, gap_4a;
uint8_t post_crc_syncs;
int8_t write_sector;
uint8_t sec_base[2], sec_map[64];
uint8_t nr_sectors, sec_no;
uint8_t interleave:4, skew:4;
bool_t skew_cyls_only;
uint16_t data_rate, gap_4;
int16_t write_sector;
uint8_t *sec_map, *trk_map;
struct raw_trk *trk, *trk_info;
struct raw_sec *sec_info, *sec_info_base;
/* If not NULL, replaces the default method for finding sector data.
* Sector data is at trk_off + file_sec_offsets[i]. */
uint32_t *file_sec_offsets;
/* Delay start of track this many bitcells past index. */
uint32_t track_delay_bc;
uint16_t gap_4;
uint32_t idx_sz, idam_sz;
uint16_t dam_sz_pre, dam_sz_post;
void *heap_bottom;
};
struct dsk_image {
@ -83,6 +140,7 @@ struct directaccess {
int32_t decode_pos;
uint16_t trk_sec;
uint16_t idx_sz, idam_sz, dam_sz;
bool_t lba_set;
};
struct image_buf {
@ -103,13 +161,18 @@ struct image_bufs {
};
struct image {
const struct image_handler *handler;
/* Handler for currently-selected type of disk image. */
const struct image_handler *disk_handler;
/* Handler for current track. May differ from the primary disk handler. */
const struct image_handler *track_handler;
/* FatFS. */
FIL fp;
/* Info about image as a whole. */
uint8_t nr_cyls, nr_sides;
uint8_t step;
/* Data buffers. */
struct image_bufs bufs;
@ -124,7 +187,7 @@ struct image {
/* Info about current 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 tracklen_bc, cur_bc; /* Track length and cursor, in bitcells */
uint32_t tracklen_ticks; /* Timing of previous revolution, in 'ticks' */
@ -137,12 +200,13 @@ struct image {
union {
struct adf_image adf;
struct hfe_image hfe;
struct qd_image qd;
struct img_image img;
struct dsk_image dsk;
struct directaccess da;
};
const struct slot *slot;
struct slot *slot;
};
static inline struct write *get_write(struct image *im, uint16_t idx)
@ -152,7 +216,7 @@ static inline struct write *get_write(struct image *im, uint16_t idx)
struct image_handler {
bool_t (*open)(struct image *im);
void (*extend)(struct image *im);
FSIZE_t (*extend)(struct image *im);
void (*setup_track)(
struct image *im, uint16_t track, uint32_t *start_pos);
bool_t (*read_track)(struct image *im);
@ -170,13 +234,13 @@ extern const struct image_type {
bool_t image_valid(FILINFO *fp);
/* Open specified image file on mass storage device. */
void image_open(struct image *im, const struct slot *slot);
void image_open(struct image *im, struct slot *slot, DWORD *cltbl);
/* Extend a trunated image file. */
void image_extend(struct image *im);
/* 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
* interested in fetching data from a particular rotational position.
@ -196,13 +260,18 @@ 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. */
bool_t image_write_track(struct image *im);
/* Rotational position of last-generated flux (SYSCLK ticks past index). */
/* Rotational position of last-generated flux (SAMPLECLK ticks past index). */
uint32_t image_ticks_since_index(struct image *im);
/* MFM conversion. */
extern const uint16_t mfmtab[];
static inline uint16_t bintomfm(uint8_t x) { return mfmtab[x]; }
uint8_t mfmtobin(uint16_t x);
void mfm_to_bin(const void *in, void *out, unsigned int nr);
void mfm_ring_to_bin(const uint16_t *ring, unsigned int mask,
unsigned int idx, void *out, unsigned int nr);
#define MFM_DAM_CRC 0xe295 /* 0xa1, 0xa1, 0xa1, 0xfb */
#define FM_DAM_CRC 0xbf84 /* 0xfb */
/* FM conversion. */
#define FM_SYNC_CLK 0xc7
@ -215,9 +284,23 @@ void floppy_insert(unsigned int unit, struct slot *slot);
void floppy_cancel(void);
bool_t floppy_handle(void); /* TRUE -> re-read config file */
void floppy_set_cyl(uint8_t unit, uint8_t cyl);
void floppy_get_track(uint8_t *p_cyl, uint8_t *p_side, uint8_t *p_sel,
uint8_t *p_writing);
struct track_info {
uint8_t cyl, side:1, sel:1, writing:1, in_da_mode:1;
};
void floppy_get_track(struct track_info *ti);
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)
{
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:

View file

@ -19,7 +19,7 @@ void F_close(FIL *fp);
void F_read(FIL *fp, void *buff, UINT btr, UINT *br);
void F_write(FIL *fp, const void *buff, UINT btw, UINT *bw);
void F_sync(FIL *fp);
void F_lseek(FIL *fp, DWORD ofs);
void F_lseek(FIL *fp, FSIZE_t ofs);
void F_truncate(FIL *fp);
void F_opendir(DIR *dp, const TCHAR *path);
void F_closedir(DIR *dp);

View file

@ -10,7 +10,7 @@
*/
/* HXCSDFE.CFG file header. */
struct __packed hxcsdfe_cfg {
struct packed hxcsdfe_cfg {
char signature[16]; /* "HXCFECFGVx.y" */
uint8_t step_sound;
uint8_t ihm_sound;
@ -27,7 +27,7 @@ struct __packed hxcsdfe_cfg {
uint8_t startup_mode;
uint8_t enable_drive_b;
uint8_t index_mode;
struct __packed {
struct packed {
uint8_t cfg_from_cfg;
uint8_t interfacemode;
uint8_t pin02_cfg;
@ -47,7 +47,7 @@ struct __packed hxcsdfe_cfg {
#define HXCSTARTUP_ejected 0x10
/* HXCFECFGV1.x slots start at offset 0x400: */
struct __packed v1_slot {
struct packed v1_slot {
char name[12];
uint8_t attributes;
uint32_t firstCluster;
@ -56,7 +56,7 @@ struct __packed v1_slot {
};
/* HXCFECFGV2.x slots start at sector offset 'slots_position': */
struct __packed v2_slot {
struct packed v2_slot {
char type[3];
uint8_t attributes;
uint32_t firstCluster;

View file

@ -13,9 +13,16 @@ struct exception_frame {
uint32_t r0, r1, r2, r3, r12, lr, pc, psr;
};
#define __aligned(x) __attribute__((aligned(x)))
#define __packed __attribute((packed))
#define _STR(x) #x
#define STR(x) _STR(x)
/* Force a compilation error if condition is true */
#define BUILD_BUG_ON(cond) ({ _Static_assert(!(cond), "!(" #cond ")"); })
#define aligned(x) __attribute__((aligned(x)))
#define packed __attribute((packed))
#define always_inline __inline__ __attribute__((always_inline))
#define noinline __attribute__((noinline))
#define likely(x) __builtin_expect(!!(x),1)
#define unlikely(x) __builtin_expect(!!(x),0)
@ -67,6 +74,14 @@ struct exception_frame {
* confirmed on Cortex-M3. */
#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)
{
uint16_t result;

57
inc/list.h Normal file
View file

@ -0,0 +1,57 @@
/*
* list.h
*
* Doubly linked list.
*
* 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>.
*/
struct list_head {
struct list_head *prev, *next;
};
static inline void list_init(struct list_head *head)
{
head->prev = head;
head->next = head;
}
static inline void list_insert_head(
struct list_head *head, struct list_head *ent)
{
ent->next = head->next;
ent->prev = head;
ent->next->prev = head->next = ent;
}
static inline void list_insert_tail(
struct list_head *head, struct list_head *ent)
{
ent->prev = head->prev;
ent->next = head;
ent->prev->next = head->prev = ent;
}
static inline void list_remove(struct list_head *ent)
{
ent->next->prev = ent->prev;
ent->prev->next = ent->next;
}
static inline bool_t list_is_empty(struct list_head *head)
{
return head->next == head;
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

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.
*
@ -13,12 +13,12 @@
#define STK volatile struct stk * const
#define SCB volatile struct scb * const
#define NVIC volatile struct nvic * const
#define DBG volatile struct dbg * const
#define FLASH volatile struct flash * const
#define PWR volatile struct pwr * const
#define BKP volatile struct bkp * const
#define RCC volatile struct rcc * const
#define GPIO volatile struct gpio * const
#define AFIO volatile struct afio * const
#define EXTI volatile struct exti * const
#define DMA volatile struct dma * const
#define TIM volatile struct tim * const
@ -27,51 +27,15 @@
#define USART volatile struct usart * 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 */
extern uint32_t vector_table[];
/* System */
void stm32_init(void);
void system_reset(void);
void system_reset(void) __attribute__((noreturn));
extern bool_t is_artery_mcu;
/* Clocks */
#define SYSCLK_MHZ 72
#define SYSCLK (SYSCLK_MHZ * 1000000)
#define sysclk_ns(x) (((x) * SYSCLK_MHZ) / 1000)
#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)
/* GPIO */
struct gpio;
void gpio_configure_pin(GPIO gpio, unsigned int pin, unsigned int mode);
#define gpio_write_pin(gpio, pin, level) \
((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))
#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 */
void fpec_init(void);
void fpec_page_erase(uint32_t flash_address);
void fpec_write(const void *data, unsigned int size, uint32_t flash_address);
#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:

View file

@ -1,5 +1,5 @@
/*
* stm32f10x_regs.h
* common_regs.h
*
* Core and peripheral register definitions.
*
@ -97,56 +97,17 @@ struct nvic {
uint32_t ispr[32]; /* 100: Interrupt set-pending */
uint32_t icpr[32]; /* 180: Interrupt clear-pending */
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
/* 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 */
struct dbg {
uint32_t mcu_idcode; /* 00: MCU ID code */
uint32_t mcu_cr; /* 04: Debug MCU configuration */
};
#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
/* 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
#define DBG_BASE 0xe0042000
/* Backup */
struct bkp {
@ -161,156 +122,7 @@ struct bkp {
#define BKP_BASE 0x40006c00
/* 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_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
/* EXTI */
struct exti {
uint32_t imr; /* 00: Interrupt mask */
uint32_t emr; /* 04: Event mask */
@ -320,8 +132,6 @@ struct exti {
uint32_t pr; /* 14: Pending */
};
#define EXTI_BASE 0x40010400
/* DMA */
struct dma_chn {
uint32_t ccr; /* +00: Configuration */
@ -333,13 +143,7 @@ struct dma_chn {
struct dma {
uint32_t isr; /* 00: Interrupt status */
uint32_t ifcr; /* 04: Interrupt flag clear */
struct dma_chn ch1; /* 08: Channel 1 */
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 */
struct dma_chn ch[7];
};
/* n=1..7 */
@ -375,9 +179,6 @@ struct dma {
#define DMA_CCR_TCIE (1u<< 1)
#define DMA_CCR_EN (1u<< 0)
#define DMA1_BASE 0x40020000
#define DMA2_BASE 0x40020400
/* Timer */
struct tim {
uint32_t cr1; /* 00: Control 1 */
@ -509,14 +310,6 @@ struct tim {
#define TIM_BDTR_LOCK(x) ((x)<<8)
#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 */
struct spi {
uint32_t cr1; /* 00: Control 1 */
@ -530,6 +323,17 @@ struct spi {
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_BIDIOE (1u<<14)
#define SPI_CR1_CRCEN (1u<<13)
@ -540,15 +344,6 @@ struct spi {
#define SPI_CR1_SSI (1u<< 8)
#define SPI_CR1_LSBFIRST (1u<< 7)
#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_CPOL (1u<< 1)
#define SPI_CR1_CPHA (1u<< 0)
@ -573,74 +368,6 @@ struct spi {
#define SPI2_BASE 0x40003800
#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 */
struct usart {
uint32_t sr; /* 00: Status */
@ -690,10 +417,6 @@ struct usart {
#define USART_CR3_IREN (1u<< 1)
#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 */
struct usb_otg {
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:
*/

View file

@ -14,9 +14,11 @@ typedef uint32_t time_t;
#define TIME_MHZ STK_MHZ
#define time_us(x) stk_us(x)
#define time_ms(x) stk_ms(x)
#define time_stk(x) (x)
#define time_sysclk(x) stk_sysclk(x)
#define sysclk_time(x) sysclk_stk(x)
void delay_from(time_t t, unsigned int ticks);
time_t time_now(void);
#define time_diff(x,y) ((int32_t)((y)-(x))) /* d = y - x */

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,44 +9,6 @@
* 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
#define LONG_MAX ((long int)((~0UL)>>1))
#define LONG_MIN ((long int)~LONG_MAX)
#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; })
struct slot {
char name[FF_MAX_LFN+1];
char type[7];
@ -57,6 +19,8 @@ struct slot {
};
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);
/* Fast memset/memcpy: Pointers must be word-aligned, count must be a non-zero
@ -69,7 +33,9 @@ void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
size_t strlen(const char *s);
size_t strnlen(const char *s, size_t maxlen);
int strcmp_ci(const char *s1, const char *s2); /* case insensitive */
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
char *strcpy(char *dest, const char *src);
@ -81,6 +47,13 @@ int isspace(int c);
long int strtol(const char *nptr, char **endptr, int base);
void qsort_p(void *base, unsigned int nr,
int (*compar)(const void *, const void *));
uint32_t rand(void);
unsigned int popcount(uint32_t x);
int vsnprintf(char *str, size_t size, const char *format, va_list ap)
__attribute__ ((format (printf, 3, 0)));
@ -96,6 +69,8 @@ int snprintf(char *str, size_t size, const char *format, ...)
#define htobe16(x) _rev16(x)
#define htobe32(x) _rev32(x)
uint32_t udiv64(uint64_t dividend, uint32_t divisor);
/* Arena-based memory allocation */
void *arena_alloc(uint32_t sz);
uint32_t arena_total(void);
@ -105,27 +80,35 @@ void arena_init(void);
/* Board-specific callouts */
void board_init(void);
#ifndef NDEBUG
/* Serial console control */
void console_init(void);
void console_sync(void);
void console_crash_on_input(void);
/* Serial console output */
#if !defined(NDEBUG) || defined(LOGFILE)
/* Log output, to serial console or logfile. */
int vprintk(const char *format, va_list ap)
__attribute__ ((format (printf, 1, 0)));
int printk(const char *format, ...)
__attribute__ ((format (printf, 1, 2)));
#else /* NDEBUG && !LOGFILE */
static inline int vprintk(const char *format, va_list ap) { return 0; }
static inline int printk(const char *format, ...) { return 0; }
#endif
#define log(f, a...) printk("%s: " f, LOG_PREFIX, ## a)
#if defined(LOGFILE)
/* Logfile management */
void logfile_flush(FIL *file);
#else /* !LOGFILE */
#define logfile_flush(f) ((void)0)
#endif
#if !defined(NDEBUG)
/* Serial console control */
void console_init(void);
void console_sync(void);
void console_crash_on_input(void);
#else /* NDEBUG */
#define console_init() ((void)0)
#define console_sync() IRQ_global_disable()
#define console_crash_on_input() ((void)0)
static inline int vprintk(const char *format, va_list ap) { return 0; }
static inline int printk(const char *format, ...) { return 0; }
#endif
/* CRC-CCITT */
@ -133,17 +116,20 @@ uint16_t crc16_ccitt(const void *buf, size_t len, uint16_t crc);
/* Display setup and identification. */
void display_init(void);
extern uint8_t display_mode;
#define DM_NONE 0
#define DM_LCD_1602 1
#define DM_LED_7SEG 2
extern uint8_t display_type;
#define DT_NONE 0
#define DT_LCD_OLED 1
#define DT_LED_7SEG 2
/* Speaker. */
void speaker_init(void);
void speaker_pulse(void);
void speaker_notify_insert(unsigned int slotnr);
void speaker_notify_eject(void);
/* Display: 3-digit 7-segment display */
void led_7seg_init(void);
void led_7seg_write_raw(const uint8_t *d);
void led_7seg_write_string(const char *p);
void led_7seg_write_decimal(unsigned int val);
void led_7seg_display_setting(bool_t enable);
@ -157,6 +143,11 @@ void lcd_backlight(bool_t on);
void lcd_sync(void);
extern uint8_t lcd_columns, lcd_rows;
/* FF OSD (On Screen Display) */
extern bool_t has_osd;
extern uint8_t osd_buttons_tx; /* Gotek -> FF_OSD */
extern uint8_t osd_buttons_rx; /* FF_OSD -> Gotek */
/* USB stack processing */
void usbh_msc_init(void);
void usbh_msc_buffer_set(uint8_t *buf);
@ -166,9 +157,14 @@ bool_t usbh_msc_inserted(void);
/* Navigation/UI frontend */
uint16_t get_slot_nr(void);
bool_t set_slot_nr(uint16_t slot_nr);
int set_slot_by_name(const char *name, void *scratch);
void set_slot_name(const char *name);
bool_t get_img_cfg(struct slot *slot);
void IRQ_rotary(void);
enum { DM_normal=0, DM_banner, DM_menu };
extern uint8_t display_mode;
extern uint8_t board_id;
extern uint8_t has_kc30_header;
/* Gotek board revisions */
#define BRDREV_Gotek_standard 0xf
@ -176,11 +172,24 @@ extern uint8_t board_id;
#define BRDREV_Gotek_sd_card 0x1
#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. */
extern const char fw_ver[];
extern const char build_date[];
extern const char build_time[];
/* Text/data/BSS address ranges. */
extern char _stext[], _etext[];
extern char _smaintext[], _emaintext[];
extern char _sdat[], _edat[], _ldat[];
extern char _sbss[], _ebss[];
@ -196,14 +205,13 @@ void EXC_unused(void);
#define FLOPPY_IRQ_SEL_PRI 1
#define FLOPPY_IRQ_WGATE_PRI 2
#define FLOPPY_IRQ_STEP_PRI 3
#define FLOPPY_IRQ_SIDE_PRI 4
#define FLOPPY_IRQ_HI_PRI 3
#define TIMER_IRQ_PRI 4
#define WDATA_IRQ_PRI 7
#define RDATA_IRQ_PRI 8
#define FLOPPY_SOFTIRQ_PRI 9
#define I2C_IRQ_PRI 13
#define USB_IRQ_PRI 14
#define CONSOLE_IRQ_PRI 15
/*
* Local variables:

View file

@ -14,8 +14,8 @@
struct volume_ops {
DSTATUS (*initialize)(BYTE);
DSTATUS (*status)(BYTE);
DRESULT (*read)(BYTE, BYTE *, DWORD, UINT);
DRESULT (*write)(BYTE, const BYTE *, DWORD, UINT);
DRESULT (*read)(BYTE, BYTE *, LBA_t, UINT);
DRESULT (*write)(BYTE, const BYTE *, LBA_t, UINT);
DRESULT (*ioctl)(BYTE, BYTE, void *);
bool_t (*connected)(void);
bool_t (*readonly)(void);
@ -24,6 +24,10 @@ struct volume_ops {
bool_t volume_connected(void);
bool_t volume_readonly(void);
void volume_cache_init(void *start, void *end);
void volume_cache_destroy(void);
void volume_cache_metadata_only(FIL *fp);
/*
* Local variables:
* mode: C

View file

@ -1,28 +0,0 @@
RPATH = ../src
OBJS += update.o
OBJS += fpec.o
OBJS += build_info.o
OBJS += cancellation.o
OBJS += crc.o
OBJS += vectors.o
OBJS += fs.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 +0,0 @@
#include "../src/FlashFloppy.ld.S"

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

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

@ -18,6 +18,13 @@ def main(argv):
sides = int(x[3])
tsz = in_dat[52:256]
populated = 0
ext = False
if x[0].startswith(b"EXTENDED CPC DSK File\r\nDisk-Info\r\n"):
print("Extended DSK")
ext = True
else:
assert x[0].startswith(b"MV - CPCEMU")
print("Standard DSK")
while tsz:
x = struct.unpack("B", tsz[:1])
if int(x[0]) != 0:
@ -42,6 +49,8 @@ def main(argv):
tot_dlen = 0
while sinfo and nr != 0:
(c,h,r,n,s1,s2,alen) = struct.unpack("<BBBBBBH", sinfo[:8])
if not ext:
alen = 128<<n
sdat = in_dat[:alen]
in_dat = in_dat[alen:]
tot_dlen += alen

View file

@ -0,0 +1,56 @@
# edsk_double_step.py
#
# Create a double-step EDSK image by doubling up cylinders.
#
# 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 struct, sys, random
def main(argv):
if len(argv) != 3:
print("%s <input_file> <output_file>" % argv[0])
return
in_f = open(argv[1], "rb")
in_dat = in_f.read()
out = bytearray(in_dat[:256])
# Check image size
if len(in_dat) < 2048:
print("Not a valid EDSK image - Too short")
return
# Check image header
sig, _, tracks, sides, tsz = struct.unpack("<34s14sBBH", in_dat[:52])
out[48] = tracks * 2 # double up on number of cyls
tszs = in_dat[52:256]
in_dat = in_dat[256:]
if sig.startswith(b"MV - CPCEMU"):
for i in range(tracks):
out += in_dat[:tsz*sides]
for j in range(sides):
out[16-tsz*(j+1)] = i*2 # fix cyl#
out += in_dat[:tsz*sides]
for j in range(sides):
out[16-tsz*(j+1)] = i*2+1 # fix cyl#
in_dat = in_dat[tsz*sides:]
elif sig.startswith(b"EXTENDED CPC DSK File\r\nDisk-Info\r\n"):
for i in range(tracks):
for j in range(2):
off = 0
for k in range(sides):
tsz = tszs[k]*256
out += in_dat[off:off+tsz]
out[16-tsz] = i*2+j # fix cyl#
out[52+(i*2+j)*sides+k] = tszs[k] # fix track size
off += tsz
tszs = tszs[sides:]
in_dat = in_dat[off:]
else:
print("Not a valid EDSK image")
return
with open(argv[2], "wb") as f:
f.write(out)
if __name__ == "__main__":
main(sys.argv)

View file

@ -15,7 +15,7 @@ def main(argv):
out_f.write("/* Autogenerated by " + argv[0] + " */\n")
for line in in_f:
match = re.match("[ \t]*([A-Za-z0-9-]+)[ \t]*=[ \t]*"
"([A-Za-z0-9-]+|\".*\")", line)
"([A-Za-z0-9-,]+|\".*\")", line)
if match:
opt = match.group(1)
val = match.group(2)
@ -25,6 +25,8 @@ def main(argv):
val = "PIN_" + val
elif opt == "track-change":
val = "TRKCHG_" + val
elif opt == "write-drain":
val = "WDRAIN_" + val
elif opt == "host":
val = "HOST_" + val
elif opt == "oled-font":
@ -38,11 +40,37 @@ def main(argv):
h = int(size.group(2))
if w == 128 and h == 64:
opts += ['DISPLAY_oled_64']
elif h == 2 and w >= 16 and w <= 40:
elif h >= 2 and h <= 4 and w >= 16 and w <= 40:
opts += ['DISPLAY_lcd_columns(%d)' % w]
opts += ['DISPLAY_lcd_rows(%d)' % h]
else:
opts += ['DISPLAY_' + x]
val = '|'.join(opts)
elif opt == "notify-volume":
opts = []
for x in val.split(","):
vol = re.match("([0-9]+)", x)
if vol:
opts.append(vol.group(1))
else:
opts.append('NOTIFY_' + x)
val = '|'.join(opts)
elif opt == "display-order" or opt == "osd-display-order":
if val == "default":
val = "DORD_" + val
else:
v = 0
sh = 0
for x in val.split(","):
o = re.match("([0-9])(d?)", x)
v |= int(o.group(1)) << sh
if o.group(2) == "d":
v |= 8 << sh
sh += 4
while sh < 16:
v |= 7 << sh
sh += 4
val = str(v)
elif opt == "image-on-startup":
val = "IMGS_" + val
elif opt == "rotary":
@ -57,6 +85,22 @@ def main(argv):
val = '|'.join(opts)
elif opt == "nav-mode":
val = "NAVMODE_" + val
elif opt == "folder-sort":
val = "SORT_" + val
elif opt == "sort-priority":
val = "SORTPRI_" + val
elif opt == "display-on-activity":
val = "DISPON_" + val
elif opt == "motor-delay":
if val == 'ignore':
val = "MOTOR_" + val
else:
val = (int(val) + 9) // 10
elif opt == "chgrst":
delay = re.match("delay-([0-9]+)", val)
if delay:
val = 'delay(%d)' % int(delay.group(1))
val = "CHGRST_" + val
else:
val = {
'no': 'FALSE',

View file

@ -29,7 +29,7 @@ def main(argv):
gap = 16 - height
tgap = gap // 2
bgap = gap - tgap
out_f.write("const uint8_t %s[] __aligned(4) = {\n" % argv[2])
out_f.write("const uint8_t %s[] aligned(4) = {\n" % argv[2])
for line in in_f:
# Look for a new character encoding
match = re.match("^ENCODING ([0-9]+)", line)

65
scripts/mk_hfe.py Normal file
View file

@ -0,0 +1,65 @@
# mk_hfe.py
#
# Make a blank HFE image.
#
# 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,struct,argparse
def main(argv):
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--rate", type=int, default=250,
help="data rate, kbit/s")
parser.add_argument("--rpm", type=int, default=300,
help="rotational rate, rpm")
parser.add_argument("--cyls", type=int, default=80,
help="number of cylinders")
parser.add_argument("--sides", type=int, default=2,
help="number of sides")
parser.add_argument("outfile", help="output filename")
args = parser.parse_args(argv[1:])
bits = (args.rate * 1000 * 60) // args.rpm
bits *= 2 # clock bits
bits *= 2 # 2 sides
bytes = (bits + 7) // 8 # convert to bytes, rounded up
bytes = (bytes + 15) & ~15 # round up to 16-byte boundary
blocks = (bytes + 511) // 512 # convert to 512-byte blocks, rounded up
print("Geometry: %u cylinders, %u sides" % (args.cyls, args.sides))
print("%ukbit/s @ %uRPM -> %u Encoded Bits"
% (args.rate, args.rpm, bits/2))
print("Data per HFE Track: %u bytes, %u blocks" % (bytes, blocks))
# Header
out_f = open(args.outfile, "wb")
out_f.write(struct.pack("<8s4B2H2BH",
b"HXCPICFE",# signature
0, # revision
args.cyls, # nr_tracks
args.sides, # nr_sides
0xff, # track_encoding
args.rate, # bitrate
0, # rpm
0xfe, # interface_mode
1, # rsvd
1)) # track_list_offset
out_f.write(bytearray(b'\xff'*(512-20)))
# TLUT
tlut_blocks = (args.cyls*4 + 511) // 512
base = 1 + tlut_blocks
for i in range(args.cyls):
out_f.write(struct.pack("<2H", base, bytes))
base += blocks
out_f.write(bytearray(b'\xff'*(tlut_blocks*512-args.cyls*4)))
# Data
out_f.write(bytearray(b'\x88'*(blocks*512*args.cyls)))
if __name__ == "__main__":
main(sys.argv)

67
scripts/mk_qd.py Normal file
View file

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

View file

@ -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>
# 2 bytes: 'FY'
# 2 bytes: CRC16-CCITT, seed 0xFFFF, stored big endian
#
#
# 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 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")
out_f = open(argv[2], "wb")
out_f = open(argv[0], "wb")
in_dat = in_f.read()
in_len = len(in_dat)
assert (in_len & 3) == 0, "input is not longword padded"
@ -28,5 +163,42 @@ def main(argv):
in_dat = struct.pack(">H", crc16.crcValue)
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__":
main(sys.argv)
# Local variables:
# python-indent: 4
# End:

10
scripts/openocd/f1.cfg Normal file
View file

@ -0,0 +1,10 @@
# Uncomment the following for clone chips
#set CPUTAPID 0x2ba01477
source [find interface/stlink.cfg]
transport select hla_swd
source [find target/stm32f1x.cfg]
reset_config srst_only connect_assert_srst

22
scripts/openocd/flash.py Normal file
View file

@ -0,0 +1,22 @@
# flash.py <hex_filename>
import os, sys, time, telnetlib
cmd = 'reset init ; flash write_image erase %s ; reset\n' % sys.argv[1]
# Start the OpenOCD daemon in the background and connect via telnet
def open_ocd():
os.system('openocd -f scripts/openocd/f1.cfg &')
while True:
time.sleep(0.5)
try:
t = telnetlib.Telnet('localhost', 4444)
except:
pass
else:
return t
with open_ocd() as t:
t.write(cmd.encode('utf-8'))
t.write('shutdown\n'.encode('utf-8'))
t.read_all() # Waits for EOF (telnet session shutdown)

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,33 +5,42 @@ MEMORY
FLASH (rx) : ORIGIN = FLASH_BASE, LENGTH = FLASH_LEN
RAM (rwx) : ORIGIN = RAM_BASE, LENGTH = RAM_LEN
}
REGION_ALIAS("RO", FLASH);
REGION_ALIAS("RW", RAM);
SECTIONS
{
.text : {
_stext = .;
*(.vector_table)
*(.vector_table*)
_smaintext = .;
*(.text)
*(.text*)
_emaintext = .;
*(.rodata)
*(.rodata*)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .;
} >RO
} >FLASH
#if MCU == AT32F435
.flags : {
_reset_flag = .;
. = . + 4;
} >RAM
#endif
.data : AT (_etext) {
. = ALIGN(4);
_sdat = .;
*(.data)
*(.data*)
*(.ramfuncs)
. = ALIGN(4);
_edat = .;
_ldat = LOADADDR(.data);
} >RW
} >RAM
.bss : {
. = ALIGN(8);
@ -46,7 +55,7 @@ SECTIONS
*(.bss*)
. = ALIGN(4);
_ebss = .;
} >RW
} >RAM
/DISCARD/ : {
*(.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 96K
#define RAM_BASE 0x20000000
#define RAM_LEN 64K
#include "../scripts/stm32f10x.ld.S"

View file

@ -5,20 +5,41 @@ OBJS += config.o
OBJS += crc.o
OBJS += flash_cfg.o
OBJS += vectors.o
OBJS += floppy.o
OBJS += fpec.o
OBJS += fpec_$(mcu).o
OBJS += fs.o
OBJS += main.o
OBJS += sd_spi.o
OBJS += spi.o
OBJS += string.o
OBJS += stm32f10x.o
OBJS += cortex.o mcu_$(mcu).o
OBJS += time.o
OBJS += timer.o
OBJS += util.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-$(quickdisk) += quickdisk.o
SUBDIRS += display
SUBDIRS += fatfs
@ -26,13 +47,17 @@ SUBDIRS += image
SUBDIRS += gotek
SUBDIRS += usb
.PHONY: build_info.c
endif
.PHONY: $(SRCDIR)/build_info.c
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
$(PYTHON) $(ROOT)/scripts/mk_config.py $< $@
clean::
rm -f ff_cfg_defaults.h
main.o flash_cfg.o: 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>.
*/
#define ram_kb 64
#define ram_bytes (ram_kb*1024)
#define heap_bot (_ebss)
#define heap_top ((char *)0x20000000 + ram_bytes)
static char *heap_p;
static char *heap_top;
void *arena_alloc(uint32_t sz)
{
@ -39,6 +34,7 @@ uint32_t arena_avail(void)
void arena_init(void)
{
heap_p = heap_bot;
heap_top = (char *)0x20000000 + ram_kb*1024;
}
/*

176
src/bl_update.c Normal file
View file

@ -0,0 +1,176 @@
/*
* bl_update.c
*
* Main firmware containing a payload of an updated bootloader.
*
* Procedure:
* - Place this *.UPD file on your USB stick and follow usual update process.
*
* Status messages:
* CLr -> Erasing flash
* Prg -> Programming flash
*
* Error messages:
* E05 -> Flash error (bad CRC on verify)
*
* 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>.
*/
/* Reflash the main bootloader (first 32kB). */
#if MCU == STM32F105
#define FIRMWARE_START 0x08000000
#define FIRMWARE_END 0x08008000
#elif MCU == AT32F435
#define FIRMWARE_START 0x08000000
#define FIRMWARE_END 0x0800c000
#endif
/* The update payload. */
extern char update_start[], update_end[];
asm (
" .section .rodata\n"
" .align 4\n"
" .global update_start, update_end\n"
"update_start:\n"
" .incbin \"../bootloader/target.bin\"\n"
"update_end:\n"
" .previous\n"
);
/* Only the vector table in low 2kB, as we erase first page of firmware,
* and we mustn't erase the code we're executing. */
asm (
" .section .vector_table.padding\n"
" .balign "STR(FLASH_PAGE_SIZE)"\n"
" .previous\n"
);
int EXC_reset(void) __attribute__((alias("main")));
uint8_t board_id;
static void erase_old_firmware(void)
{
uint32_t p;
for (p = FIRMWARE_START; p < FIRMWARE_END; p += flash_page_size)
fpec_page_erase(p);
}
static void msg_display(const char *p)
{
printk("[%s]\n", p);
switch (display_type) {
case DT_LED_7SEG:
led_7seg_write_string(p);
break;
case DT_LCD_OLED:
lcd_write(6, 1, 0, p);
lcd_sync();
break;
}
}
static void display_setting(bool_t on)
{
switch (display_type) {
case DT_LED_7SEG:
led_7seg_display_setting(on);
break;
case DT_LCD_OLED:
lcd_backlight(on);
lcd_sync();
break;
}
}
int main(void)
{
void *buf = update_start;
unsigned int nr = update_end - update_start;
int retries = 0;
/* Relocate DATA. Initialise BSS. */
if (&_sdat[0] != &_ldat[0])
memcpy(_sdat, _ldat, _edat-_sdat);
memset(_sbss, 0, _ebss-_sbss);
/* Initialise the world. */
stm32_init();
time_init();
console_init();
board_init();
delay_ms(200); /* 5v settle */
printk("\n** FF Update Firmware %s for Gotek\n", fw_ver);
printk("** Keir Fraser <keir.xen@gmail.com>\n");
printk("** https://github.com/keirf/FlashFloppy\n\n");
flash_ff_cfg_read();
display_init();
switch (display_type) {
case DT_LED_7SEG:
msg_display("BLD");
break;
case DT_LCD_OLED:
lcd_write(0, 0, 0, "New Bootloader..");
lcd_write(0, 1, 0, " [ ]");
lcd_sync();
break;
}
display_setting(TRUE);
for (retries = 0; retries < 5; retries++) {
/* Erase the old firmware. */
msg_display("CLR");
fpec_init();
erase_old_firmware();
/* Program the new firmware. */
msg_display("PRG");
fpec_write(buf, nr, FIRMWARE_START);
if (!memcmp((void *)FIRMWARE_START, buf, nr))
goto success;
}
/* An error occurred. Report it on the display. */
msg_display("ERR");
/* Erase the bootloader. It's now damaged. */
erase_old_firmware();
/* Spin forever. We're toast. */
for (;;)
continue;
success:
/* No errors. */
printk("Success!\n");
/* Clear the display. */
display_setting(FALSE);
/* All done. Erase ourself and reset. */
IRQ_global_disable();
fpec_page_erase((uint32_t)_stext);
system_reset();
return 0;
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

View file

@ -8,6 +8,8 @@
*/
const char fw_ver[] = FW_VER;
const char build_date[] = __DATE__;
const char build_time[] = __TIME__;
/*
* Local variables:

135
src/cache.c Normal file
View file

@ -0,0 +1,135 @@
/*
* cache.c
*
* In-memory data cache.
*
* 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>.
*/
struct cache_ent {
uint32_t id;
struct list_head lru;
struct list_head hash;
uint8_t dat[0];
};
struct cache {
uint32_t item_sz;
struct list_head lru;
struct list_head hash[32];
struct cache_ent ents[0];
};
static struct cache *cache;
#define CACHE_HASH(_id) ((_id)&31)
struct cache *cache_init(void *start, void *end, unsigned int item_sz)
{
uint8_t *s, *e;
int i, nitm;
struct cache *c;
struct cache_ent *cent;
/* Cache boundaries are four-byte aligned. */
s = (uint8_t *)(((uint32_t)start + 3) & ~3);
e = (uint8_t *)((uint32_t)end & ~3);
nitm = ((e - s) - (int)sizeof(*c)) / (int)(sizeof(*cent) + item_sz);
if (nitm < 8) {
printk("No cache: too small (%d)\n", e - s);
return NULL;
}
/* Initialise the empty cache structure. */
cache = c = (struct cache *)s;
c->item_sz = item_sz;
list_init(&c->lru);
for (i = 0; i < ARRAY_SIZE(c->hash); i++)
list_init(&c->hash[i]);
/* Insert all the cache entries into the LRU list. They are not present
* in any hash chain as none of the cache entries are yet in use. */
cent = c->ents;
for (i = 0; i < nitm; i++) {
list_insert_tail(&c->lru, &cent->lru);
list_init(&cent->hash);
cent = (struct cache_ent *)((uint32_t)cent + sizeof(*cent) + item_sz);
}
printk("Cache %u items\n", nitm);
return c;
}
const void *cache_lookup(struct cache *c, uint32_t id)
{
struct list_head *hash, *ent;
struct cache_ent *cent;
/* Look up the item in the appropriate hash chain. */
hash = &c->hash[CACHE_HASH(id)];
for (ent = hash->next; ent != hash; ent = ent->next) {
cent = container_of(ent, struct cache_ent, hash);
if (cent->id == id)
goto found;
}
return NULL;
found:
/* Item is cached. Move it to head of LRU and return the data. */
list_remove(&cent->lru);
list_insert_head(&c->lru, &cent->lru);
return cent->dat;
}
void cache_update(struct cache *c, uint32_t id, const void *dat)
{
struct cache_ent *cent;
void *p;
/* Already in the cache? Just update the existing data. */
if ((p = (void *)cache_lookup(c, id)) != NULL)
goto found;
/* Steal the oldest cache entry from the LRU. */
cent = container_of(c->lru.prev, struct cache_ent, lru);
p = cent->dat;
/* Remove the selected cache entry from the cache. */
list_remove(&cent->lru);
if (!list_is_empty(&cent->hash))
list_remove(&cent->hash);
/* Reinsert the cache entry in the correct hash chain, and head of LRU. */
cent->id = id;
list_insert_head(&c->lru, &cent->lru);
list_insert_head(&c->hash[CACHE_HASH(id)], &cent->hash);
found:
/* Finally, store away the actual item data. */
memcpy(p, dat, c->item_sz);
}
void cache_update_N(struct cache *c, uint32_t id,
const void *dat, unsigned int N)
{
const uint8_t *p = dat;
while (N--) {
cache_update(c, id, p);
id++;
p += c->item_sz;
}
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

View file

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

View file

@ -9,34 +9,46 @@
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
/* Alphanumeric plus ",-._:" */
static int isvalid(char c)
{
return (((c >= 'A') && (c <= 'Z'))
|| ((c >= 'a') && (c <= 'z'))
|| ((c >= '0') && (c <= '9'))
|| ((c == '-')));
const static char map[] = {
/* , (2c) - (2d) . (2e) 0-9 (30-39) : (3a)
* A-Z (41-5a) _ (5f) a-z (61-7a) */
0x00, 0x00, 0x00, 0x00, /* 0x00-0x1f */
0x00, 0x0e, 0xff, 0xe0, /* 0x20-0x3f */
0x7f, 0xff, 0xff, 0xe1, /* 0x40-0x5f */
0x7f, 0xff, 0xff, 0xe0 /* 0x60-0x7f */
};
return ((c/8) < sizeof(map)) ? (int8_t)(map[c/8] << (c&7)) < 0 : FALSE;
}
int get_next_opt(struct opts *opts)
{
char *p, c;
const struct opt *opt;
bool_t section;
F_read(opts->file, &c, 1, NULL);
next_line:
if (c == '\0')
return -1; /* eof */
return OPT_eof;
/* Skip leading whitespace. */
while (isspace(c))
F_read(opts->file, &c, 1, NULL);
/* Option name parsing. */
section = (c == '['); /* "[section]" */
if (section)
F_read(opts->file, &c, 1, NULL);
p = opts->arg;
while (isvalid(c) && ((p-opts->arg) < (opts->argmax-1))) {
*p++ = c;
F_read(opts->file, &c, 1, NULL);
}
*p = '\0';
if (section)
return OPT_section;
/* Look for a match in the accepted options list. */
for (opt = opts->opts; opt->name; opt++)
if (!strcmp(opt->name, opts->arg))
@ -63,7 +75,7 @@ next_line:
F_read(opts->file, &c, 1, NULL);
}
} else {
/* Non-quoted value: pretty much alphanumeric only is accepted. */
/* Non-quoted value: restricted character set. */
while (isvalid(c) && ((p-opts->arg) < (opts->argmax-1))) {
*p++ = c;
F_read(opts->file, &c, 1, NULL);

View file

@ -1,7 +1,7 @@
/*
* console.c
*
* printf-style interface to USART1.
* printf-style interface to USART.
*
* Written & released by Keir Fraser <keir.xen@gmail.com>
*
@ -12,12 +12,64 @@
#define BAUD 3000000 /* 3Mbaud */
#define USART1_IRQ 37
#define USART3_IRQ 39
static void emit_char(uint8_t c)
#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. */
#define CONSOLE_SOFTIRQ SOFTIRQ_1
DEFINE_IRQ(CONSOLE_SOFTIRQ, "SOFTIRQ_console");
/* We stage serial output in a ring buffer. */
static char ring[2048];
#define MASK(x) ((x)&(sizeof(ring)-1))
static unsigned int cons, prod;
/* The console can be set into synchronous mode in which case IRQ is disabled
* and the transmit-empty flag is polled manually for each byte. */
static bool_t sync_console;
static void flush_ring_to_serial(void)
{
while (!(usart1->sr & USART_SR_TXE))
cpu_relax();
usart1->dr = c;
unsigned int c = cons, p = prod;
barrier();
while (c != p) {
while (!(usart->sr & USART_SR_TXE))
cpu_relax();
usart->dr = ring[MASK(c++)];
}
barrier();
cons = c;
}
static void SOFTIRQ_console(void)
{
flush_ring_to_serial();
}
static void kick_tx(void)
{
if (sync_console) {
flush_ring_to_serial();
} else if (cons != prod) {
IRQx_set_pending(CONSOLE_SOFTIRQ);
}
}
int vprintk(const char *format, va_list ap)
@ -31,20 +83,23 @@ int vprintk(const char *format, va_list ap)
n = vsnprintf(str, sizeof(str), format, ap);
p = str;
while ((c = *p++) != '\0') {
while (((c = *p++) != '\0') && ((prod-cons) != (sizeof(ring) - 1))) {
switch (c) {
case '\r': /* CR: ignore as we generate our own CR/LF */
break;
case '\n': /* LF: convert to CR/LF (usual terminal behaviour) */
emit_char('\r');
ring[MASK(prod++)] = '\r';
/* fall through */
default:
emit_char(c);
ring[MASK(prod++)] = c;
break;
}
}
IRQ_global_enable();
kick_tx();
if (!sync_console)
IRQ_global_enable();
return n;
}
@ -63,33 +118,59 @@ int printk(const char *format, ...)
void console_sync(void)
{
if (sync_console)
return;
IRQ_global_disable();
sync_console = TRUE;
kick_tx();
/* Leave IRQs globally disabled. */
}
void console_init(void)
{
/* Turn on the clocks. */
#if USART_IRQ == USART1_IRQ
rcc->apb2enr |= RCC_APB2ENR_USART1EN;
#else
rcc->apb1enr |= RCC_APB1ENR_USART3EN;
#endif
/* Enable TX pin (PA9) for USART output, RX pin (PA10) as input. */
gpio_configure_pin(gpioa, 9, AFO_pushpull(_10MHz));
gpio_configure_pin(gpioa, 10, GPI_pull_up);
/* Enable TX pin for USART output, RX pin as input. */
#if MCU == STM32F105
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. */
usart1->brr = SYSCLK / BAUD;
usart1->cr1 = (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE);
usart1->cr3 = 0;
usart->brr = PCLK / BAUD;
usart->cr1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
usart->cr3 = 0;
IRQx_set_prio(CONSOLE_SOFTIRQ, CONSOLE_IRQ_PRI);
IRQx_enable(CONSOLE_SOFTIRQ);
}
/* Debug helper: if we get stuck somewhere, calling this beforehand will cause
* any serial input to cause a crash dump of the stuck context. */
void console_crash_on_input(void)
{
(void)usart1->dr; /* clear UART_SR_RXNE */
usart1->cr1 |= USART_CR1_RXNEIE;
IRQx_set_prio(USART1_IRQ, RESET_IRQ_PRI);
IRQx_enable(USART1_IRQ);
if (mcu_package == MCU_QFN32) {
/* Unavailable: PA10 is reassigned from SER_RX to K4 (rotary select
* on the KC30 header). */
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>
*
@ -13,6 +13,27 @@ struct extra_exception_frame {
uint32_t r4, r5, r6, r7, r8, r9, r10, r11, lr;
};
static inline bool_t in_text(uint32_t p)
{
return (p >= (uint32_t)_smaintext) && (p < (uint32_t)_emaintext);
}
static void show_stack(uint32_t sp, uint32_t top)
{
unsigned int i = 0;
sp &= ~3;
while (sp < top) {
uint32_t v = *(uint32_t *)sp & ~1;
sp += 4;
if (!in_text(v))
continue;
if ((i++&7) == 0)
printk("\n ", sp);
printk("%x ", v);
}
printk("\n");
}
void EXC_unexpected(struct extra_exception_frame *extra)
{
struct exception_frame *frame;
@ -43,6 +64,18 @@ void EXC_unexpected(struct extra_exception_frame *extra)
frame->r12, (extra->lr & 4) ? psp : msp, frame->lr, frame->pc);
printk(" msp: %08x psp: %08x psr: %08x\n",
msp, psp, frame->psr);
if ((msp >= (uint32_t)_irq_stackbottom)
&& (msp < (uint32_t)_irq_stacktop)) {
printk("IRQ Call Trace:");
show_stack(msp, (uint32_t)_irq_stacktop);
}
if ((psp >= (uint32_t)_thread_stackbottom)
&& (psp < (uint32_t)_thread_stacktop)) {
printk("Process Call Trace:", psp);
show_stack(psp, (uint32_t)_thread_stacktop);
}
system_reset();
}
@ -77,75 +110,18 @@ static void exception_init(void)
scb->shpr3 = 0xff<<16;
}
static void clock_init(void)
static void sysclk_init(void)
{
/* Flash controller: reads require 2 wait states at 72MHz. */
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. */
/* Enable SysTick counter. */
stk->load = STK_MASK;
stk->ctrl = STK_CTRL_ENABLE;
}
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)
void cortex_init(void)
{
exception_init();
clock_init();
peripheral_init();
cpu_sync();
sysclk_init();
}
void delay_ticks(unsigned int ticks)
@ -177,18 +153,6 @@ void delay_ms(unsigned int ms)
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)
{
console_sync();

View file

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

View file

@ -1,13 +1,12 @@
OBJS += display.o
OBJS += lcd.o
OBJS += lcd_$(mcu).o
OBJS += oled_font_6x13.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
endif
oled_font_%.c: $(ROOT)/fonts/%.bdf
$(PYTHON) $(ROOT)/scripts/mk_font.py $< oled_font_$*
clean::
rm -f oled_font_*.c

View file

@ -9,14 +9,14 @@
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
uint8_t display_mode;
uint8_t display_type;
void display_init(void)
{
char name[20];
int probe_ms = ff_cfg.display_probe_ms;
display_mode = DM_NONE;
display_type = DT_NONE;
snprintf(name, sizeof(name), "None");
for (;;) {
@ -24,15 +24,15 @@ void display_init(void)
stk_time_t t = stk_now();
if (lcd_init()) {
display_mode = DM_LCD_1602;
snprintf(name, sizeof(name), "1602 LCD");
display_type = DT_LCD_OLED;
snprintf(name, sizeof(name), "LCD/OLED");
break; /* positive identification */
}
if (ff_cfg.display_type == DISPLAY_auto) {
if ((ff_cfg.display_type & 3) == DISPLAY_auto) {
led_7seg_init();
display_mode = DM_LED_7SEG;
snprintf(name, sizeof(name), "%u-Digit 7-Seg LED",
display_type = DT_LED_7SEG;
snprintf(name, sizeof(name), "%u-Digit LED",
led_7seg_nr_digits());
if (led_7seg_nr_digits() == 3)
break; /* positive identification */

View file

@ -29,8 +29,22 @@
#define CMD_SETDDRADDR 0x80
#define FS_2LINE 0x08
/* FF OSD command set */
#define OSD_BACKLIGHT 0x00 /* [0] = backlight on */
#define OSD_DATA 0x02 /* next columns*rows bytes are text data */
#define OSD_ROWS 0x10 /* [3:0] = #rows */
#define OSD_HEIGHTS 0x20 /* [3:0] = 1 iff row is 2x height */
#define OSD_BUTTONS 0x30 /* [3:0] = button mask */
#define OSD_COLUMNS 0x40 /* [6:0] = #columns */
struct packed i2c_osd_info {
uint8_t protocol_ver;
uint8_t fw_major, fw_minor;
uint8_t buttons;
};
/* STM32 I2C peripheral. */
#define i2c i2c2
#define SCL 10
#define SDA 11
@ -42,32 +56,59 @@ void IRQ_34(void) __attribute__((alias("IRQ_i2c_error")));
#define I2C_EVENT_IRQ 33
void IRQ_33(void) __attribute__((alias("IRQ_i2c_event")));
/* DMA completion ISR. */
#define DMA1_CH4_IRQ 14
void IRQ_14(void) __attribute__((alias("IRQ_dma1_ch4_tc")));
/* DMA Tx. */
#define DMA_TX_CH 4
#define i2c_tx_dma dma1->ch[DMA_TX_CH-1]
/* DMA Rx. */
#define DMA_RX_CH 5
#define i2c_rx_dma dma1->ch[DMA_RX_CH-1]
bool_t has_osd;
uint8_t osd_buttons_tx;
uint8_t osd_buttons_rx;
#define OSD_no 0
#define OSD_read 1
#define OSD_write 2
static uint8_t in_osd, osd_ver;
#define OSD_I2C_ADDR 0x10
static uint8_t _bl;
static uint8_t i2c_addr;
static uint8_t i2c_dead;
static uint8_t i2c_row;
static bool_t is_oled_display;
static uint8_t oled_height;
#define OLED_ADDR 0x3c
enum { OLED_unknown, OLED_ssd1306, OLED_sh1106 };
static uint8_t oled_model;
static void oled_init(void);
static unsigned int oled_prep_buffer(void);
#define I2C_RD TRUE
#define I2C_WR FALSE
static void i2c_start(uint8_t a, unsigned int nr, bool_t rd);
static void i2c_tx_tc(void);
static void i2c_rx_tc(void);
/* Count of display-refresh completions. For synchronisation/flush. */
static volatile uint8_t refresh_count;
/* I2C data buffer. Data is DMAed to the I2C peripheral. */
static uint8_t buffer[256] __aligned(4);
static uint8_t buffer[256] aligned(4);
/* Text buffer, rendered into I2C data and placed into buffer[]. */
static char text[2][40];
static char text[4][40];
/* Columns and rows of text. */
uint8_t lcd_columns, lcd_rows;
/* Current display mode: Affects row ordering and sizing. */
uint8_t display_mode = DM_banner;
#define menu_mode (display_mode == DM_menu)
/* Occasionally the I2C/DMA engine seems to get stuck. Detect this with
* a timeout timer and unwedge it by calling the I2C error handler. */
#define DMA_TIMEOUT time_ms(200)
@ -81,16 +122,15 @@ static void timeout_fn(void *unused)
static void IRQ_i2c_error(void)
{
/* Dump and clear I2C errors. */
printk("I2C: Error (%04x)\n", (uint16_t)(i2c->sr1 & I2C_SR1_ERRORS));
i2c->sr1 &= ~I2C_SR1_ERRORS;
printk("I2C: Error (%04x)\n", (uint16_t)(i2c->isr & I2C_SR_ERRORS));
i2c->icr = I2C_SR_ERRORS;
/* Clear the I2C peripheral. */
i2c->cr1 = 0;
i2c->cr1 = I2C_CR1_SWRST;
i2c->cr1 = I2C_CR1_PE;
/* Clear the DMA controller. */
dma1->ch4.ccr = 0;
dma1->ifcr = DMA_IFCR_CGIF(4);
i2c_tx_dma.ccr = i2c_rx_dma.ccr = 0;
timer_cancel(&timeout_timer);
@ -99,34 +139,59 @@ static void IRQ_i2c_error(void)
static void IRQ_i2c_event(void)
{
uint16_t sr1 = i2c->sr1;
uint16_t sr = i2c->isr;
if (sr1 & I2C_SR1_SB) {
/* Send address. Clears SR1_SB. */
i2c->dr = i2c_addr << 1;
}
if (sr1 & I2C_SR1_ADDR) {
/* Read SR2 clears SR1_ADDR. */
(void)i2c->sr2;
/* No more events: data phase is driven by DMA. */
i2c->cr2 &= ~I2C_CR2_ITEVTEN;
if (sr & I2C_SR_STOPF) {
i2c->icr = I2C_SR_STOPF;
if (sr & I2C_SR_NACKF) {
/* I2C automatically STOPs on NACK. But it's an error as far
* as we're concerned, so punt it down that path. */
i2c->icr = I2C_SR_NACKF;
IRQx_set_pending(I2C_ERROR_IRQ);
} else if (i2c->cr2 & I2C_CR2_RD_WRN) {
i2c_rx_tc();
} else {
i2c_tx_tc();
}
}
}
/* Start an I2C DMA sequence. */
static void dma_start(unsigned int sz)
{
unsigned int addr = in_osd ? OSD_I2C_ADDR : i2c_addr;
ASSERT(sz <= sizeof(buffer));
dma1->ch4.cmar = (uint32_t)(unsigned long)buffer;
dma1->ch4.cndtr = sz;
dma1->ch4.ccr = (DMA_CCR_MSIZE_8BIT |
DMA_CCR_PSIZE_16BIT |
DMA_CCR_MINC |
DMA_CCR_DIR_M2P |
DMA_CCR_TCIE |
DMA_CCR_EN);
if (in_osd == OSD_read) {
i2c_rx_dma.ccr = 0;
i2c->cr1 = (I2C_CR1_RXDMAEN | I2C_CR1_ERRIE | I2C_CR1_STOPIE);
i2c_rx_dma.cndtr = sz;
i2c_rx_dma.ccr = (DMA_CCR_MSIZE_8BIT |
DMA_CCR_PSIZE_32BIT |
DMA_CCR_MINC |
DMA_CCR_DIR_P2M |
DMA_CCR_EN);
i2c_start(addr, sz, I2C_RD);
} else {
i2c_tx_dma.ccr = 0;
i2c->cr1 = (I2C_CR1_TXDMAEN | I2C_CR1_ERRIE | I2C_CR1_STOPIE);
i2c_tx_dma.cndtr = sz;
i2c_tx_dma.ccr = (DMA_CCR_MSIZE_8BIT |
DMA_CCR_PSIZE_32BIT |
DMA_CCR_MINC |
DMA_CCR_DIR_M2P |
DMA_CCR_EN);
i2c_start(addr, sz, I2C_WR);
}
/* Set the timeout timer in case the DMA hangs for any reason. */
timer_set(&timeout_timer, time_now() + DMA_TIMEOUT);
@ -149,43 +214,124 @@ static void emit8(uint8_t **p, uint8_t val, uint8_t signals)
}
/* Snapshot text buffer into the command buffer. */
static unsigned int lcd_prep_buffer(void)
static unsigned int osd_prep_buffer(void)
{
uint16_t order = menu_mode ? 0x7903 : 0x7183;
char *p;
uint8_t *q = buffer;
unsigned int i, j;
unsigned int row, rows, heights;
int i;
/* We transmit complete display on every DMA. */
refresh_count++;
for (i = 0; i < lcd_rows; i++) {
emit8(&q, CMD_SETDDRADDR | (i*64), 0);
for (j = 0; j < lcd_columns; j++)
emit8(&q, text[i][j], _RS);
if (++in_osd == OSD_read) {
memset(buffer, 0x11, 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_COLUMNS | lcd_columns;
*q++ = OSD_ROWS | rows;
*q++ = OSD_HEIGHTS | heights;
*q++ = OSD_BUTTONS | osd_buttons_tx;
*q++ = OSD_DATA;
for (row = 0; row < rows; row++) {
p = text[(order >> (row * DORD_shift)) & DORD_row];
memcpy(q, p, lcd_columns);
q += lcd_columns;
}
if (i2c_addr == 0)
refresh_count++;
in_osd = OSD_write;
return q - buffer;
}
static void IRQ_dma1_ch4_tc(void)
/* Snapshot text buffer into the command buffer. */
static unsigned int lcd_prep_buffer(void)
{
const static uint8_t row_offs[] = { 0x00, 0x40, 0x14, 0x54 };
uint16_t order;
char *p;
uint8_t *q = buffer;
unsigned int i, row;
if (i2c_row == lcd_rows) {
i2c_row++;
if (has_osd)
return osd_prep_buffer();
}
if (i2c_row > lcd_rows) {
i2c_row = 0;
refresh_count++;
}
order = (lcd_rows == 2) ? 0x7710 : 0x2103;
if ((ff_cfg.display_order != DORD_default) && (display_mode == DM_normal))
order = ff_cfg.display_order;
row = (order >> (i2c_row * DORD_shift)) & DORD_row;
p = (_bl && row < ARRAY_SIZE(text)) ? text[row] : NULL;
emit8(&q, CMD_SETDDRADDR | row_offs[i2c_row], 0);
for (i = 0; i < lcd_columns; i++)
emit8(&q, p ? *p++ : ' ', _RS);
i2c_row++;
return q - buffer;
}
static void i2c_tx_tc(void)
{
unsigned int dma_sz;
/* Clear the DMA controller. */
dma1->ch4.ccr = 0;
dma1->ifcr = DMA_IFCR_CGIF(4);
/* Prepare the DMA buffer and start the next DMA sequence. */
dma_sz = is_oled_display ? oled_prep_buffer() : lcd_prep_buffer();
in_osd = OSD_no;
if (i2c_addr == 0) {
dma_sz = osd_prep_buffer();
} else {
dma_sz = is_oled_display ? oled_prep_buffer() : lcd_prep_buffer();
}
dma_start(dma_sz);
}
static void i2c_rx_tc(void)
{
struct i2c_osd_info *info = (struct i2c_osd_info *)buffer;
osd_buttons_rx = info->buttons;
/* Now do the OSD write. */
dma_start(osd_prep_buffer());
}
/* Wait for given status condition @s while also checking for errors. */
static bool_t i2c_wait(uint8_t s)
{
stk_time_t t = stk_now();
while ((i2c->sr1 & s) != s) {
if (i2c->sr1 & I2C_SR1_ERRORS) {
i2c->sr1 &= ~I2C_SR1_ERRORS;
while ((i2c->isr & s) != s) {
if (i2c->isr & I2C_SR_ERRORS) {
i2c->icr = I2C_SR_ERRORS;
return FALSE;
}
if (stk_diff(t, stk_now()) > stk_ms(10)) {
@ -197,43 +343,90 @@ static bool_t i2c_wait(uint8_t s)
return TRUE;
}
/* Synchronously transmit the I2C START sequence. */
static bool_t i2c_start(uint8_t a)
static void i2c_start(uint8_t a, unsigned int nr, bool_t rd)
{
i2c->cr1 |= I2C_CR1_START;
if (!i2c_wait(I2C_SR1_SB))
uint32_t cr2 = 0;
ASSERT(nr <= 255);
i2c->cr1 &= ~I2C_CR1_PE;
i2c->cr1 |= I2C_CR1_PE;
if (rd)
cr2 |= I2C_CR2_RD_WRN;
cr2 |= I2C_CR2_NBYTES(nr) | I2C_CR2_SADD(a<<1) | I2C_CR2_AUTOEND;
i2c->cr2 = cr2;
i2c->cr2 |= I2C_CR2_START;
}
/* Synchronously transmit the I2C STOP sequence. */
static bool_t i2c_stop(void)
{
if (!i2c_wait(I2C_SR_STOPF))
return FALSE;
i2c->dr = a << 1;
if (!i2c_wait(I2C_SR1_ADDR))
return FALSE;
(void)i2c->sr2;
i2c->icr = I2C_SR_STOPF;
return TRUE;
}
/* Synchronously transmit an I2C command. */
static bool_t i2c_cmd(uint8_t cmd)
/* Synchronously transmit an I2C byte. */
static bool_t i2c_sync_write(uint8_t b)
{
i2c->dr = cmd;
return i2c_wait(I2C_SR1_BTF);
if (!i2c_wait(I2C_SR_TXIS))
return FALSE;
i2c->txdr = b;
return TRUE;
}
/* Synchronously transmitreceive an I2C byte. */
static bool_t i2c_sync_read(uint8_t *pb)
{
if (!i2c_wait(I2C_SR_RXNE))
return FALSE;
*pb = i2c->rxdr;
return TRUE;
}
static bool_t i2c_sync_write_txn(uint8_t addr, uint8_t *cmds, unsigned int nr)
{
unsigned int i;
i2c_start(addr, nr, I2C_WR);
for (i = 0; i < nr; i++)
if (!i2c_sync_write(*cmds++))
return FALSE;
return i2c_stop();
}
static bool_t i2c_sync_read_txn(uint8_t addr, uint8_t *rsp, unsigned int nr)
{
unsigned int i;
i2c_start(addr, nr, I2C_RD);
for (i = 0; i < nr; i++)
if (!i2c_sync_read(rsp+i))
return FALSE;
return i2c_stop();
}
/* Write a 4-bit nibble over D7-D4 (4-bit bus). */
static void write4(uint8_t val)
{
i2c_cmd(val);
i2c_cmd(val | _EN);
i2c_cmd(val);
i2c_sync_write(val);
i2c_sync_write(val | _EN);
i2c_sync_write(val);
}
/* Check whether an I2C device is responding at given address. */
static bool_t i2c_probe(uint8_t a)
{
if (!i2c_start(a) || !i2c_cmd(0))
i2c_start(a, 1, I2C_WR);
if (!i2c_sync_write(0))
return FALSE;
i2c->cr1 |= I2C_CR1_STOP;
while (i2c->cr1 & I2C_CR1_STOP)
continue;
return TRUE;
return i2c_stop();
}
/* Check given inclusive range of addresses for a responding I2C device. */
@ -248,8 +441,7 @@ static uint8_t i2c_probe_range(uint8_t s, uint8_t e)
void lcd_clear(void)
{
lcd_write(0, 0, -1, "");
lcd_write(0, 1, -1, "");
memset(text, ' ', sizeof(text));
}
void lcd_write(int col, int row, int min, const char *str)
@ -257,10 +449,6 @@ void lcd_write(int col, int row, int min, const char *str)
char c, *p;
uint32_t oldpri;
if (row < 0)
row += lcd_rows;
if (col < 0)
col += lcd_columns;
if (min < 0)
min = lcd_columns;
@ -295,7 +483,12 @@ void lcd_sync(void)
bool_t lcd_init(void)
{
uint8_t a, *p;
bool_t reinit = (i2c_addr != 0);
bool_t reinit = (i2c_addr != 0) || has_osd;
i2c_dead = FALSE;
i2c_row = 0;
in_osd = OSD_no;
osd_buttons_rx = 0;
rcc->apb1enr |= RCC_APB1ENR_I2C2EN;
@ -327,56 +520,82 @@ bool_t lcd_init(void)
/* Check the bus is not floating (or still stuck!). We shouldn't be able to
* pull the lines low with our internal weak pull-downs (min. 30kohm). */
if (!reinit) {
bool_t scl, sda;
gpio_configure_pin(gpiob, SCL, GPI_pull_down);
gpio_configure_pin(gpiob, SDA, GPI_pull_down);
delay_us(10);
if (!gpio_read_pin(gpiob, SCL) || !gpio_read_pin(gpiob, SDA)) {
printk("I2C: Invalid bus\n");
scl = gpio_read_pin(gpiob, SCL);
sda = gpio_read_pin(gpiob, SDA);
if (!scl || !sda) {
printk("I2C: Invalid bus SCL=%u SDA=%u\n", scl, sda);
goto fail;
}
}
gpio_set_af(gpiob, SCL, 4);
gpio_set_af(gpiob, SDA, 4);
gpio_configure_pin(gpiob, SCL, AFO_opendrain(_2MHz));
gpio_configure_pin(gpiob, SDA, AFO_opendrain(_2MHz));
/* Standard Mode (100kHz) */
i2c->cr1 = 0;
i2c->cr2 = I2C_CR2_FREQ(36);
i2c->ccr = I2C_CCR_CCR(180);
i2c->trise = 37;
i2c->timingr = I2C_TIMING_100k;
i2c->cr1 = I2C_CR1_PE;
if (!reinit) {
/* Probe the bus for an I2C device. */
/* First probe after I2C re-initialisation seems to fail, and so we
* fail to detect FF OSD. So issue a dummy probe first. */
(void)i2c_probe(0);
/* Probe the bus for I2C devices: We support a single LCD/OLED plus
* an FF OSD device. */
has_osd = i2c_probe(OSD_I2C_ADDR);
a = i2c_probe_range(0x20, 0x27) ?: i2c_probe_range(0x38, 0x3f);
if (a == 0) {
if ((a == 0) && (i2c_dead || !has_osd
|| ((ff_cfg.display_type & 3) != DISPLAY_auto))) {
printk("I2C: %s\n",
i2c_dead ? "Bus locked up?" : "No device found");
has_osd = FALSE;
goto fail;
}
/* Probe the FF OSD device if we found one. */
if (has_osd) {
(void)i2c_sync_read_txn(OSD_I2C_ADDR, &osd_ver, 1);
printk("I2C: FF OSD found (ver %x)\n", osd_ver);
}
is_oled_display = (ff_cfg.display_type & DISPLAY_oled) ? TRUE
: (ff_cfg.display_type & DISPLAY_lcd) ? FALSE
: ((a&~1) == OLED_ADDR);
lcd_rows = 2;
if (is_oled_display) {
oled_height = (ff_cfg.display_type & DISPLAY_oled_64) ? 64 : 32;
lcd_columns = (ff_cfg.oled_font == FONT_8x16) ? 16
: (ff_cfg.display_type & DISPLAY_narrower) ? 16
: (ff_cfg.display_type & DISPLAY_narrow) ? 18 : 21;
lcd_rows = 4;
} else {
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;
}
printk("I2C: %s found at 0x%02x\n",
is_oled_display ? "OLED" : "LCD", a);
i2c_addr = a;
if (a != 0) {
printk("I2C: %s found at 0x%02x\n",
is_oled_display ? "OLED" : "LCD", a);
i2c_addr = a;
} else {
is_oled_display = FALSE;
lcd_columns = ff_cfg.osd_columns;
}
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();
}
/* Enable the Event IRQ. */
@ -388,14 +607,17 @@ bool_t lcd_init(void)
IRQx_set_prio(I2C_ERROR_IRQ, I2C_IRQ_PRI);
IRQx_clear_pending(I2C_ERROR_IRQ);
IRQx_enable(I2C_ERROR_IRQ);
i2c->cr2 |= I2C_CR2_ITERREN;
dmamux1->cctrl[DMA_TX_CH-1] = DMAMUX_CCTRL_REQSEL(DMAMUX_REQ_I2C2_TX);
dmamux1->cctrl[DMA_RX_CH-1] = DMAMUX_CCTRL_REQSEL(DMAMUX_REQ_I2C2_RX);
/* Initialise DMA1 channel 4 and its completion interrupt. */
dma1->ch4.cpar = (uint32_t)(unsigned long)&i2c->dr;
dma1->ifcr = DMA_IFCR_CGIF(4);
IRQx_set_prio(DMA1_CH4_IRQ, I2C_IRQ_PRI);
IRQx_clear_pending(DMA1_CH4_IRQ);
IRQx_enable(DMA1_CH4_IRQ);
i2c_tx_dma.cmar = (uint32_t)(unsigned long)buffer;
i2c_tx_dma.cpar = (uint32_t)(unsigned long)&i2c->txdr;
/* Initialise DMA1 channel 5 and its completion interrupt. */
i2c_rx_dma.cmar = (uint32_t)(unsigned long)buffer;
i2c_rx_dma.cpar = (uint32_t)(unsigned long)&i2c->rxdr;
/* Timeout handler for if I2C transmission borks. */
timer_init(&timeout_timer, timeout_fn, NULL);
@ -404,19 +626,21 @@ bool_t lcd_init(void)
if (is_oled_display) {
oled_init();
return TRUE;
} else if (i2c_addr == 0) {
dma_start(osd_prep_buffer());
return TRUE;
}
if (!i2c_start(i2c_addr))
goto fail;
/* Initialise 4-bit interface, as in the datasheet. Do this synchronously
* and with the required delays. */
i2c_start(i2c_addr, 4*3, I2C_WR);
write4(3 << 4);
delay_us(4100);
write4(3 << 4);
delay_us(100);
write4(3 << 4);
write4(2 << 4);
i2c_stop();
/* More initialisation from the datasheet. Send by DMA. */
p = buffer;
@ -424,23 +648,19 @@ bool_t lcd_init(void)
emit8(&p, CMD_DISPLAYCTL, 0);
emit8(&p, CMD_ENTRYMODE | 2, 0);
emit8(&p, CMD_DISPLAYCTL | 4, 0); /* display on */
i2c->cr2 |= I2C_CR2_DMAEN;
dma_start(p - buffer);
/* Wait for DMA engine to initialise RAM, then turn on backlight. */
if (!reinit) {
lcd_sync();
if (!reinit)
lcd_backlight(TRUE);
}
return TRUE;
fail:
if (reinit)
return FALSE;
IRQx_disable(I2C_EVENT_IRQ);
IRQx_disable(I2C_ERROR_IRQ);
IRQx_disable(DMA1_CH4_IRQ);
i2c->cr1 &= ~I2C_CR1_PE;
i2c->cr1 = 0;
gpio_configure_pin(gpiob, SCL, GPI_pull_up);
gpio_configure_pin(gpiob, SDA, GPI_pull_up);
rcc->apb1enr &= ~RCC_APB1ENR_I2C2EN;
@ -502,8 +722,6 @@ static void oled_convert_text_row(char *pc)
oled_convert_text_row_6x13(pc);
}
static uint8_t oled_row;
static unsigned int oled_queue_cmds(
uint8_t *buf, const uint8_t *cmds, unsigned int nr)
{
@ -561,23 +779,29 @@ static unsigned int oled_start_i2c(uint8_t *buf)
static const uint8_t ssd1306_addr_cmds[] = {
0x20, 0, /* horizontal addressing mode */
0x21, 0, 127, /* column address range: 0-127 */
0x22, 0, /*3*//* page address range: 0-3 */
0x22, /* page address range: ?-? */
}, ztech_addr_cmds[] = {
0xda, 0x12, /* alternate com pins config */
0x21, 4, 131, /* column address range: 4-131 */
}, sh1106_addr_cmds[] = {
0x02, 0x10, /* column address: 2 */
0x10 /* column address high nibble is zero */
};
uint8_t dynamic_cmds[4], *dc = dynamic_cmds;
uint8_t *p = buf;
/* Set up the display address range. */
if (ff_cfg.display_type & DISPLAY_sh1106) {
if (oled_model == OLED_sh1106) {
p += oled_queue_cmds(p, sh1106_addr_cmds, sizeof(sh1106_addr_cmds));
/* Page address: according to oled_row. */
*dc++ = 0xb0 + oled_row;
/* Column address: 0 or 2 (seems 128x64 displays are shifted by 2). */
*dc++ = (oled_height == 64) ? 0x02 : 0x00;
/* Page address: according to i2c_row. */
*dc++ = 0xb0 + i2c_row;
} else {
p += oled_queue_cmds(p, ssd1306_addr_cmds, sizeof(ssd1306_addr_cmds));
/* Page address max: depends on display height */
*dc++ = (oled_height / 8) - 1;
/* Page address: according to i2c_row. */
*dc++ = i2c_row;
*dc++ = 7;
}
/* Display on/off according to backlight setting. */
@ -585,98 +809,130 @@ static unsigned int oled_start_i2c(uint8_t *buf)
p += oled_queue_cmds(p, dynamic_cmds, dc - dynamic_cmds);
/* ZHONGJY_TECH 2.23" 128x32 display based on SSD1305 controller.
* It has alternate COM pin mapping and is offset horizontally. */
if (ff_cfg.display_type & DISPLAY_ztech)
p += oled_queue_cmds(p, ztech_addr_cmds, sizeof(ztech_addr_cmds));
/* All subsequent bytes are data bytes. */
*p++ = 0x40;
/* Start the I2C transaction. */
i2c->cr2 |= I2C_CR2_ITEVTEN;
i2c->cr1 |= I2C_CR1_START;
return p - buf;
}
static unsigned int ssd1306_prep_buffer(void)
static int oled_to_lcd_row(int in_row)
{
/* If we have completed a complete fill of the OLED display, start a new
* I2C transaction. The OLED display seems to occasionally silently lose
* a byte and then we lose sync with the display address. */
if (oled_row == (oled_height / 16)) {
/* Wait for BTF. */
while (!(i2c->sr1 & I2C_SR1_BTF)) {
/* Any errors: bail and leave it to the Error ISR. */
if (i2c->sr1 & I2C_SR1_ERRORS)
return 0;
}
/* Send STOP. Clears SR1_TXE and SR1_BTF. */
i2c->cr1 |= I2C_CR1_STOP;
while (i2c->cr1 & I2C_CR1_STOP)
continue;
/* Kick off new I2C transaction. */
oled_row = 0;
refresh_count++;
return oled_start_i2c(buffer);
uint16_t order;
int i = 0, row;
bool_t large = FALSE;
order = (oled_height == 32) ? 0x7710 : menu_mode ? 0x7903 : 0x7183;
if ((ff_cfg.display_order != DORD_default) && (display_mode == DM_normal))
order = ff_cfg.display_order;
for (;;) {
large = !!(order & DORD_double);
i += large ? 2 : 1;
if (i > in_row)
break;
order >>= DORD_shift;
}
/* Convert one row of text[] into buffer[] writes. */
if (oled_height == 64) {
oled_convert_text_row(text[oled_row/2]);
oled_double_height(buffer, &buffer[(oled_row & 1) ? 128 : 0], 0x3);
/* Remap the row */
row = order & DORD_row;
if (row < lcd_rows) {
oled_convert_text_row(text[row]);
} else {
oled_convert_text_row(text[oled_row]);
memset(buffer, 0, 256);
}
oled_row++;
return 256;
return large ? i - in_row : 0;
}
static unsigned int sh1106_prep_buffer(void)
/* Snapshot text buffer into the bitmap buffer. */
static unsigned int oled_prep_buffer(void)
{
int size;
uint8_t *p = buffer;
if (i2c_row == (oled_height / 8)) {
i2c_row++;
if (has_osd)
return osd_prep_buffer();
}
if (i2c_row > (oled_height / 8)) {
i2c_row = 0;
refresh_count++;
}
/* Convert one row of text[] into buffer[] writes. */
if (oled_height == 64) {
oled_convert_text_row(text[oled_row/4]);
oled_double_height(&buffer[128], &buffer[(oled_row & 2) ? 128 : 0],
(oled_row & 1) + 1);
size = oled_to_lcd_row(i2c_row/2);
if (size != 0) {
oled_double_height(&buffer[128], &buffer[(size == 1) ? 128 : 0],
(i2c_row & 1) + 1);
} else {
oled_convert_text_row(text[oled_row/2]);
if (!(oled_row & 1))
if (!(i2c_row & 1))
memcpy(&buffer[128], &buffer[0], 128);
}
/* Wait for BTF. */
while (!(i2c->sr1 & I2C_SR1_BTF)) {
/* Any errors: bail and leave it to the Error ISR. */
if (i2c->sr1 & I2C_SR1_ERRORS)
return 0;
}
/* Send STOP. Clears SR1_TXE and SR1_BTF. */
i2c->cr1 |= I2C_CR1_STOP;
while (i2c->cr1 & I2C_CR1_STOP)
continue;
/* Every 8 rows needs a new page address and hence new I2C transaction. */
/* New I2C transaction. */
p += oled_start_i2c(p);
/* Patch the data bytes onto the end of the address setup sequence. */
memcpy(p, &buffer[128], 128);
p += 128;
if (++oled_row == (oled_height / 8)) {
oled_row = 0;
refresh_count++;
}
i2c_row++;
return p - buffer;
}
/* Snapshot text buffer into the bitmap buffer. */
static unsigned int oled_prep_buffer(void)
static bool_t oled_probe_model(void)
{
return (ff_cfg.display_type & DISPLAY_sh1106)
? sh1106_prep_buffer()
: ssd1306_prep_buffer();
uint8_t cmd1[] = { 0x80, 0x00, /* Column 0 */
0xc0 }; /* Read one data */
uint8_t cmd2[] = { 0x80, 0x00, /* Column 0 */
0xc0, 0x00 }; /* Write one data */
uint8_t rsp[2];
int i;
uint8_t x, px = 0;
uint8_t *rand = (uint8_t *)emit8;
for (i = 0; i < 3; i++) {
/* 1st Write stage. */
if (!i2c_sync_write_txn(i2c_addr, cmd1, sizeof(cmd1)))
goto fail;
/* Read stage. */
if (!i2c_sync_read_txn(i2c_addr, rsp, sizeof(rsp)))
goto fail;
x = rsp[1];
/* 2nd Write stage. */
cmd2[3] = x ^ rand[i]; /* XOR the write with "randomness" */
if (!i2c_sync_write_txn(i2c_addr, cmd2, sizeof(cmd2)))
goto fail;
/* Check we read what we wrote on previous iteration. */
if (i && (x != px))
break;
/* Remember what we wrote, for next iteration. */
px = cmd2[3];
}
oled_model = (i == 3) ? OLED_sh1106 : OLED_ssd1306;
printk("OLED: %s\n", (oled_model == OLED_sh1106) ? "SH1106" : "SSD1306");
return TRUE;
fail:
return FALSE;
}
static void oled_init_fast_mode(void)
{
/* Disable I2C (currently in Standard Mode). */
i2c->cr1 = 0;
/* Fast Mode (400kHz). */
i2c->timingr = I2C_TIMING_400k;
}
static void oled_init(void)
@ -690,7 +946,6 @@ static void oled_init(void)
0xd9, 0xf1, /* pre-charge period */
0xdb, 0x20, /* vcomh detect (default) */
0xa4, /* output follows ram contents */
0xa6, /* normal display output (inverse=off) */
0x2e, /* deactivate scroll */
}, norot_cmds[] = {
0xa1, /* segment mapping (reverse) */
@ -699,25 +954,21 @@ static void oled_init(void)
0xa0, /* segment mapping (default) */
0xc0, /* com scan direction (default) */
};
const uint8_t *cmds;
uint8_t dynamic_cmds[6], *dc;
uint8_t dynamic_cmds[7], *dc;
uint8_t *p = buffer;
/* Disable I2C (currently in Standard Mode). */
i2c->cr1 = 0;
if (!(ff_cfg.display_type & DISPLAY_slow))
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())
goto fail;
/* Initialisation sequence for SSD1306/SH1106. */
p += oled_queue_cmds(p, init_cmds, sizeof(init_cmds));
/* Dynamically-generated initialisation commands. */
dc = dynamic_cmds;
*dc++ = (ff_cfg.display_type & DISPLAY_inverse) ? 0xa7 : 0xa6; /* Video */
*dc++ = 0x81; /* Display Contrast */
*dc++ = ff_cfg.oled_contrast;
*dc++ = 0xa8; /* Multiplex ratio (lcd height - 1) */
@ -726,17 +977,23 @@ static void oled_init(void)
*dc++ = (oled_height == 64) ? 0x12 : 0x02;
p += oled_queue_cmds(p, dynamic_cmds, dc - dynamic_cmds);
/* Display is right-way-up, or rotated. */
cmds = (ff_cfg.display_type & DISPLAY_rotate) ? rot_cmds : norot_cmds;
p += oled_queue_cmds(p, cmds, sizeof(rot_cmds));
/* Display orientation. */
dc = dynamic_cmds;
memcpy(dc, (ff_cfg.display_type & DISPLAY_rotate) ? rot_cmds : norot_cmds,
2);
if (ff_cfg.display_type & DISPLAY_hflip)
dc[0] ^= 1;
p += oled_queue_cmds(p, dc, 2);
/* Start off the I2C transaction. */
oled_row = 0;
p += oled_start_i2c(p);
/* Send the initialisation command sequence by DMA. */
i2c->cr2 |= I2C_CR2_DMAEN;
dma_start(p - buffer);
return;
fail:
IRQx_set_pending(I2C_ERROR_IRQ);
}
/*

1130
src/display/lcd_stm32f105.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -21,14 +21,15 @@
#define CYCLE 8
/* TM1651, 74HC164: DAT = PB10, CLK = PB11 */
#define DAT_PIN 10
#define CLK_PIN 11
static uint8_t DAT_PIN = 10;
static uint8_t CLK_PIN = 11;
/* TM1651, 74HC164: Alphanumeric segment arrangements. */
/* TM1651, 74HC164: Alphanumeric segment arrangements.
* Bit positions 0-6 correspond to conventional segment labels A-G resp. */
static const uint8_t letters[] = {
0x77, 0x7c, 0x58, 0x5e, 0x79, 0x71, 0x6f, 0x74, 0x04, /* a-i */
0x0e, 0x00, 0x38, 0x00, 0x54, 0x5c, 0x73, 0x67, 0x50, /* j-r */
0x6d, 0x78, 0x1c, 0x00, 0x00, 0x76, 0x6e, 0x00 /* s-z */
0x0e, 0x08, 0x38, 0x40, 0x54, 0x5c, 0x73, 0x67, 0x50, /* j-r */
0x6d, 0x78, 0x1c, 0x09, 0x41, 0x76, 0x6e, 0x52 /* s-z */
};
static const uint8_t digits[] = {
@ -130,7 +131,7 @@ static bool_t tm1651_send_cmd(uint8_t cmd)
return fail;
}
static void tm1651_update_display(uint8_t *d)
static void tm1651_update_display(const uint8_t *d)
{
bool_t fail = TRUE;
int retry;
@ -184,7 +185,7 @@ static void shiftreg_update_display_u16(uint16_t x)
static uint16_t shiftreg_curval;
static void shiftreg_update_display(uint8_t *d)
static void shiftreg_update_display(const uint8_t *d)
{
uint16_t x = ((uint16_t)d[0] << 8) | d[1];
shiftreg_curval = x;
@ -220,6 +221,14 @@ void led_7seg_display_setting(bool_t enable)
shiftreg_display_setting(enable);
}
void led_7seg_write_raw(const uint8_t *d)
{
if (nr_digits == 3)
tm1651_update_display(d);
else
shiftreg_update_display(d);
}
void led_7seg_write_string(const char *p)
{
uint8_t d[3] = { 0 }, c;
@ -239,10 +248,7 @@ void led_7seg_write_string(const char *p)
}
}
if (nr_digits == 3)
tm1651_update_display(d);
else
shiftreg_update_display(d);
led_7seg_write_raw(d);
}
void led_7seg_write_decimal(unsigned int val)
@ -256,6 +262,11 @@ void led_7seg_write_decimal(unsigned int val)
void led_7seg_init(void)
{
if (mcu_package == MCU_QFN32) {
DAT_PIN = 6; /* PB6 */
CLK_PIN = 7; /* PB7 */
}
nr_digits = !tm1651_init() ? 3 : 2;
if (nr_digits == 2)
shiftreg_init();

View file

@ -1,5 +1,5 @@
/*-----------------------------------------------------------------------/
/ Low level disk interface modlue include file (C)ChaN, 2014 /
/ Low level disk interface modlue include file (C)ChaN, 2019 /
/-----------------------------------------------------------------------*/
#ifndef _DISKIO_DEFINED
@ -9,9 +9,6 @@
extern "C" {
#endif
#include "integer.h"
/* Status of Disk Functions */
typedef BYTE DSTATUS;
@ -31,8 +28,8 @@ typedef enum {
DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
@ -46,11 +43,11 @@ DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
/* Command code for disk_ioctrl fucntion */
/* Generic command (Used by FatFs) */
#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */
#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
/* Generic command (Not used by FatFs) */
#define CTRL_POWER 5 /* Get/Set power status */

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem module R0.13 /
/ FatFs - Generic FAT Filesystem module R0.14 /
/-----------------------------------------------------------------------------/
/
/ Copyright (C) 2017, ChaN, all right reserved.
/ Copyright (C) 2019, ChaN, all right reserved.
/
/ FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided
@ -20,13 +20,12 @@
#ifndef FF_DEFINED
#define FF_DEFINED 87030 /* Revision ID */
#define FF_DEFINED 86606 /* Revision ID */
#ifdef __cplusplus
extern "C" {
#endif
#include "integer.h" /* Basic integer types */
#include "ffconf.h" /* FatFs configuration options */
#if FF_DEFINED != FFCONF_DEF
@ -34,6 +33,30 @@ extern "C" {
#endif
/* Integer types used for FatFs API */
#if defined(_WIN32) /* Main development platform */
#define FF_INTDEF 2
#include <windows.h>
typedef unsigned __int64 QWORD;
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
#define FF_INTDEF 2
#include <stdint.h>
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */
typedef uint16_t WORD; /* 16-bit unsigned integer */
typedef uint32_t DWORD; /* 32-bit unsigned integer */
typedef uint64_t QWORD; /* 64-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#else /* Earlier than C99 */
#define FF_INTDEF 1
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */
typedef unsigned short WORD; /* 16-bit unsigned integer */
typedef unsigned long DWORD; /* 32-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#endif
/* Definitions of volume management */
@ -42,40 +65,64 @@ typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
#endif
#if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
#endif
#endif
/* Type of path name strings on FatFs API */
#if FF_LFN_UNICODE && FF_USE_LFN /* Unicode (UTF-16) string */
#ifndef _INC_TCHAR
#define _INC_TCHAR
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
typedef WCHAR TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#define _INC_TCHAR
#endif
#else /* ANSI/OEM string */
#ifndef _INC_TCHAR
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
typedef char TCHAR;
#define _T(x) u8 ## x
#define _TEXT(x) u8 ## x
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
typedef DWORD TCHAR;
#define _T(x) U ## x
#define _TEXT(x) U ## x
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
#error Wrong FF_LFN_UNICODE setting
#else /* ANSI/OEM code in SBCS/DBCS */
typedef char TCHAR;
#define _T(x) x
#define _TEXT(x) x
#define _INC_TCHAR
#endif
#endif
/* Type of file size variables */
/* Type of file size and LBA variables */
#if FF_FS_EXFAT
#if !FF_USE_LFN
#error LFN must be enabled when enable exFAT
#if FF_INTDEF != 2
#error exFAT feature wants C99 or later
#endif
typedef QWORD FSIZE_t;
#if FF_LBA64
typedef QWORD LBA_t;
#else
typedef DWORD LBA_t;
#endif
#else
#if FF_LBA64
#error exFAT needs to be enabled when enable 64-bit LBA
#endif
typedef DWORD FSIZE_t;
typedef DWORD LBA_t;
#endif
@ -83,8 +130,8 @@ typedef DWORD FSIZE_t;
/* Filesystem object structure (FATFS) */
typedef struct {
BYTE fs_type; /* Filesystem type (0:N/A) */
BYTE pdrv; /* Physical drive number */
BYTE fs_type; /* Filesystem type (0:not mounted) */
BYTE pdrv; /* Associated physical drive */
BYTE n_fats; /* Number of FATs (1 or 2) */
BYTE wflag; /* win[] flag (b0:dirty) */
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
@ -117,11 +164,15 @@ typedef struct {
#endif
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
DWORD fsize; /* Size of an FAT [sectors] */
DWORD volbase; /* Volume base sector */
DWORD fatbase; /* FAT base sector */
DWORD dirbase; /* Root directory base sector/cluster */
DWORD database; /* Data base sector */
DWORD winsect; /* Current sector appearing in the win[] */
LBA_t volbase; /* Volume base sector */
LBA_t volend; /* Volume end sector */
LBA_t fatbase; /* FAT base sector */
LBA_t dirbase; /* Root directory base sector/cluster */
LBA_t database; /* Data base sector */
#if FF_FS_EXFAT
LBA_t bitbase; /* Allocation bitmap base sector */
#endif
LBA_t winsect; /* Current sector appearing in the win[] */
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
} FATFS;
@ -133,7 +184,7 @@ typedef struct {
FATFS* fs; /* Pointer to the hosting volume of this object */
WORD id; /* Hosting volume mount ID */
BYTE attr; /* Object attribute */
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:flagmented in this session, b2:sub-directory stretched) */
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
#if FF_FS_EXFAT
@ -158,9 +209,9 @@ typedef struct {
BYTE err; /* Abort flag (error code) */
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
#if !FF_FS_READONLY
DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
#endif
#if FF_USE_FASTSEEK
@ -179,7 +230,7 @@ typedef struct {
FFOBJID obj; /* Object identifier */
DWORD dptr; /* Current read/write offset */
DWORD clust; /* Current cluster */
DWORD sect; /* Current sector (0:Read operation has terminated) */
LBA_t sect; /* Current sector (0:Read operation has terminated) */
BYTE* dir; /* Pointer to the directory item in the win[] */
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
#if FF_USE_LFN
@ -198,17 +249,31 @@ typedef struct {
FSIZE_t fsize; /* File size */
WORD fdate; /* Modified date */
WORD ftime; /* Modified time */
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
BYTE fattrib; /* File attribute */
#if FF_USE_LFN
TCHAR altname[13]; /* Altenative file name */
TCHAR fname[FF_MAX_LFN + 1]; /* Primary file name */
TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
#else
TCHAR fname[13]; /* File name */
TCHAR fname[12 + 1]; /* File name */
#endif
} FILINFO;
/* Format parameter structure (MKFS_PARM) */
typedef struct {
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
BYTE n_fat; /* Number of FATs */
UINT align; /* Data area alignment (sector) */
UINT n_root; /* Number of root directory entries */
DWORD au_size; /* Cluster size (byte) */
} MKFS_PARM;
/* File function return code (FRESULT) */
typedef enum {
@ -232,13 +297,13 @@ typedef enum {
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
FR_INVALID_PARAMETER, /* (19) Given parameter is invalid */
/* FlashFloppy error codes: */
FR_DISK_FULL = 30, /* (30) Disk full during write */
FR_BAD_IMAGE, /* (31) Bad disk image file */
FR_BAD_HXCSDFE, /* (32) Bad HXCSDFE.CFG file */
FR_BAD_IMAGECFG, /* (33) Bad IMAGE_A.CFG file */
FR_NO_DIRENTS, /* (34) No valid directory entries */
FR_PATH_TOO_DEEP, /* (35) Folders nested too deeply */
/* FlashFloppy error codes: */
FR_DISK_FULL = 30, /* (30) Disk full during write */
FR_BAD_IMAGE, /* (31) Bad disk image file */
FR_BAD_HXCSDFE, /* (32) Bad HXCSDFE.CFG file */
FR_BAD_IMAGECFG, /* (33) Bad IMAGE_A.CFG file */
FR_NO_DIRENTS, /* (34) No valid directory entries */
FR_PATH_TOO_DEEP, /* (35) Folders nested too deeply */
} FRESULT;
@ -271,10 +336,10 @@ FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get numbe
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
FRESULT f_setcp (WORD cp); /* Set current code page */
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
@ -306,10 +371,10 @@ DWORD get_fattime (void);
#endif
/* LFN support functions */
#if FF_USE_LFN /* Code conversion (defined in unicode.c) */
#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
WCHAR ff_uni2oem (WCHAR uni, WORD cp); /* Unicode to OEM code conversion */
WCHAR ff_wtoupper (WCHAR uni); /* Unicode upper-case conversion */
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
#endif
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
void* ff_memalloc (UINT msize); /* Allocate memory block */

View file

@ -1,14 +1,14 @@
/*---------------------------------------------------------------------------/
/ FatFs - Configuration file
/ FatFs Functional Configurations
/---------------------------------------------------------------------------*/
#define FFCONF_DEF 87030 /* Revision ID */
#define FFCONF_DEF 86606 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/
#if defined(BOOTLOADER) || defined(RELOADER)
#if defined(BOOTLOADER)
#define FF_FS_READONLY 1
#else
#define FF_FS_READONLY 0
@ -19,14 +19,14 @@
/ and optional writing functions as well. */
#if defined(BOOTLOADER) || defined(RELOADER)
#if defined(BOOTLOADER)
#define FF_FS_MINIMIZE 1
#else
#define FF_FS_MINIMIZE 0
#endif
/* This option defines minimization level to remove some basic API functions.
/
/ 0: All basic functions are enabled.
/ 0: Basic functions are fully enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/ are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
@ -50,7 +50,11 @@
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#if defined(BOOTLOADER)
#define FF_USE_FASTSEEK 0
#else
#define FF_USE_FASTSEEK 1
#endif
/* This option switches fast seek function. (0:Disable or 1:Enable) */
@ -110,40 +114,59 @@
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/ 0: Disable LFN. FF_MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added
/ to the project. The working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/ additional 608 bytes at exFAT enabled. FF_MAX_LFN can be in range from 12 to 255.
/ It should be set 255 to support full featured LFN operations.
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
/ specification.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree(), must be added to the project. */
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
#define FF_LFN_UNICODE 0
/* This option switches character encoding on the API, 0:ANSI/OEM or 1:UTF-16,
/ when LFN is enabled. Also behavior of string I/O functions will be affected by
/ this option. When LFN is not enabled, this option has no effect.
*/
/* This option switches the character encoding on the API when LFN is enabled.
/
/ 0: ANSI/OEM in current CP (TCHAR = char)
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
/ 2: Unicode in UTF-8 (TCHAR = char)
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
/
/ Also behavior of string I/O functions will be affected by this option.
/ When LFN is not enabled, this option has no effect. */
#define FF_LFN_BUF 255
#define FF_SFN_BUF 12
/* This set of options defines size of file name members in the FILINFO structure
/ which is used to read out directory items. These values should be suffcient for
/ the file names to read. The maximum possible length of the read file name depends
/ on character encoding. When LFN is not enabled, these options have no effect. */
#define FF_STRF_ENCODE 3
/* When FF_LFN_UNICODE = 1 with LFN enabled, string I/O functions, f_gets(),
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
/ This option selects assumption of character encoding ON THE FILE to be
/ read/written via those functions.
/
/ 0: ANSI/OEM
/ 1: UTF-16LE
/ 2: UTF-16BE
/ 3: UTF-8
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
#if defined(BOOTLOADER)
#define FF_FS_RPATH 0
#else
#define FF_FS_RPATH 1
#endif
/* This option configures support for relative path.
/
/ 0: Disable relative path and remove related functions.
@ -162,11 +185,16 @@
#define FF_STR_VOLUME_ID 0
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* FF_STR_VOLUME_ID switches string support for volume ID.
/ When FF_STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the drive ID strings for each
/ logical drives. Number of items must be equal to FF_VOLUMES. Valid characters for
/ the drive ID strings are: A-Z and 0-9. */
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table needs to be defined as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/
#define FF_MULTI_PARTITION 0
@ -188,24 +216,27 @@
/ GET_SECTOR_SIZE command. */
/* FLASHFLOPPY: Allow GPT partitions to be found. Does not require LBA64,
* nor exFAT. This option alone does not support GPT in f_mkfs. */
#define FF_GPT 1
#define FF_LBA64 0
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
#define FF_MIN_GPT 0x100000000
/* Minimum number of sectors to switch GPT format to create partition in f_mkfs and
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
#define FF_USE_TRIM 0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
/ disk_ioctl() function. */
#define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
/*---------------------------------------------------------------------------/
/ System Configurations
@ -220,22 +251,34 @@
#define FF_FS_EXFAT 0
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ When enable exFAT, also LFN needs to be enabled.
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
#define FF_FS_NORTC 1
#define FF_NORTC_MON 5
#define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2017
#define FF_NORTC_YEAR 2019
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
/ the timestamp function. All objects modified by FatFs will have a fixed timestamp
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
#define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
#define FF_FS_LOCK 0
@ -250,6 +293,7 @@
/ lock control is independent of re-entrancy. */
/* #include <somertos.h> // O/S definitions */
#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT 1000
#define FF_SYNC_t HANDLE
@ -270,8 +314,6 @@
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.h. */
/* #include <windows.h> // O/S definitions */
/*--- End of configuration options ---*/

View file

@ -7,7 +7,7 @@
/ that function to avoid silly memory consumption. /
/-------------------------------------------------------------------------*/
/*
/ Copyright (C) 2017, ChaN, all right reserved.
/ Copyright (C) 2014, ChaN, all right reserved.
/
/ FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided
@ -25,7 +25,7 @@
#include "ff.h"
#if FF_USE_LFN
#if FF_USE_LFN /* This module will be blanked if non-LFN configuration */
#define MERGE2(a, b) a ## b
#define CVTBL(tbl, cp) MERGE2(tbl, cp)
@ -36,8 +36,7 @@
/*------------------------------------------------------------------------*/
#if FF_CODE_PAGE == 932 || FF_CODE_PAGE == 0 /* Japanese */
static
const WCHAR uni2oem932[] = { /* Unicode --> Shift_JIS pairs */
static const WCHAR uni2oem932[] = { /* Unicode --> Shift_JIS pairs */
0x00A7, 0x8198, 0x00A8, 0x814E, 0x00B0, 0x818B, 0x00B1, 0x817D, 0x00B4, 0x814C, 0x00B6, 0x81F7, 0x00D7, 0x817E, 0x00F7, 0x8180,
0x0391, 0x839F, 0x0392, 0x83A0, 0x0393, 0x83A1, 0x0394, 0x83A2, 0x0395, 0x83A3, 0x0396, 0x83A4, 0x0397, 0x83A5, 0x0398, 0x83A6,
0x0399, 0x83A7, 0x039A, 0x83A8, 0x039B, 0x83A9, 0x039C, 0x83AA, 0x039D, 0x83AB, 0x039E, 0x83AC, 0x039F, 0x83AD, 0x03A0, 0x83AE,
@ -964,8 +963,7 @@ const WCHAR uni2oem932[] = { /* Unicode --> Shift_JIS pairs */
0xFFE1, 0x8192, 0xFFE2, 0x81CA, 0xFFE3, 0x8150, 0xFFE4, 0xFA55, 0xFFE5, 0x818F, 0, 0
};
static
const WCHAR oem2uni932[] = { /* Shift_JIS --> Unicode pairs */
static const WCHAR oem2uni932[] = { /* Shift_JIS --> Unicode pairs */
0x00A1, 0xFF61, 0x00A2, 0xFF62, 0x00A3, 0xFF63, 0x00A4, 0xFF64, 0x00A5, 0xFF65, 0x00A6, 0xFF66, 0x00A7, 0xFF67, 0x00A8, 0xFF68,
0x00A9, 0xFF69, 0x00AA, 0xFF6A, 0x00AB, 0xFF6B, 0x00AC, 0xFF6C, 0x00AD, 0xFF6D, 0x00AE, 0xFF6E, 0x00AF, 0xFF6F, 0x00B0, 0xFF70,
0x00B1, 0xFF71, 0x00B2, 0xFF72, 0x00B3, 0xFF73, 0x00B4, 0xFF74, 0x00B5, 0xFF75, 0x00B6, 0xFF76, 0x00B7, 0xFF77, 0x00B8, 0xFF78,
@ -1894,8 +1892,7 @@ const WCHAR oem2uni932[] = { /* Shift_JIS --> Unicode pairs */
#endif
#if FF_CODE_PAGE == 936 || FF_CODE_PAGE == 0 /* Simplified Chinese */
static
const WCHAR uni2oem936[] = { /* Unicode --> GBK pairs */
static const WCHAR uni2oem936[] = { /* Unicode --> GBK pairs */
0x00A4, 0xA1E8, 0x00A7, 0xA1EC, 0x00A8, 0xA1A7, 0x00B0, 0xA1E3, 0x00B1, 0xA1C0, 0x00B7, 0xA1A4, 0x00D7, 0xA1C1, 0x00E0, 0xA8A4,
0x00E1, 0xA8A2, 0x00E8, 0xA8A8, 0x00E9, 0xA8A6, 0x00EA, 0xA8BA, 0x00EC, 0xA8AC, 0x00ED, 0xA8AA, 0x00F2, 0xA8B0, 0x00F3, 0xA8AE,
0x00F7, 0xA1C2, 0x00F9, 0xA8B4, 0x00FA, 0xA8B2, 0x00FC, 0xA8B9, 0x0101, 0xA8A1, 0x0113, 0xA8A5, 0x011B, 0xA8A7, 0x012B, 0xA8A9,
@ -4623,8 +4620,7 @@ const WCHAR uni2oem936[] = { /* Unicode --> GBK pairs */
0, 0
};
static
const WCHAR oem2uni936[] = { /* GBK --> Unicode pairs */
static const WCHAR oem2uni936[] = { /* GBK --> Unicode pairs */
0x0080, 0x20AC, 0x8140, 0x4E02, 0x8141, 0x4E04, 0x8142, 0x4E05, 0x8143, 0x4E06, 0x8144, 0x4E0F, 0x8145, 0x4E12, 0x8146, 0x4E17,
0x8147, 0x4E1F, 0x8148, 0x4E20, 0x8149, 0x4E21, 0x814A, 0x4E23, 0x814B, 0x4E26, 0x814C, 0x4E29, 0x814D, 0x4E2E, 0x814E, 0x4E2F,
0x814F, 0x4E31, 0x8150, 0x4E33, 0x8151, 0x4E35, 0x8152, 0x4E37, 0x8153, 0x4E3C, 0x8154, 0x4E40, 0x8155, 0x4E41, 0x8156, 0x4E42,
@ -7354,8 +7350,7 @@ const WCHAR oem2uni936[] = { /* GBK --> Unicode pairs */
#endif
#if FF_CODE_PAGE == 949 || FF_CODE_PAGE == 0 /* Korean */
static
const WCHAR uni2oem949[] = { /* Unicode --> Korean pairs */
static const WCHAR uni2oem949[] = { /* Unicode --> Korean pairs */
0x00A1, 0xA2AE, 0x00A4, 0xA2B4, 0x00A7, 0xA1D7, 0x00A8, 0xA1A7, 0x00AA, 0xA8A3, 0x00AD, 0xA1A9, 0x00AE, 0xA2E7, 0x00B0, 0xA1C6,
0x00B1, 0xA1BE, 0x00B2, 0xA9F7, 0x00B3, 0xA9F8, 0x00B4, 0xA2A5, 0x00B6, 0xA2D2, 0x00B7, 0xA1A4, 0x00B8, 0xA2AC, 0x00B9, 0xA9F6,
0x00BA, 0xA8AC, 0x00BC, 0xA8F9, 0x00BD, 0xA8F6, 0x00BE, 0xA8FA, 0x00BF, 0xA2AF, 0x00C6, 0xA8A1, 0x00D0, 0xA8A2, 0x00D7, 0xA1BF,
@ -9490,8 +9485,7 @@ const WCHAR uni2oem949[] = { /* Unicode --> Korean pairs */
0, 0
};
static
const WCHAR oem2uni949[] = { /* Korean --> Unicode pairs */
static const WCHAR oem2uni949[] = { /* Korean --> Unicode pairs */
0x8141, 0xAC02, 0x8142, 0xAC03, 0x8143, 0xAC05, 0x8144, 0xAC06, 0x8145, 0xAC0B, 0x8146, 0xAC0C, 0x8147, 0xAC0D, 0x8148, 0xAC0E,
0x8149, 0xAC0F, 0x814A, 0xAC18, 0x814B, 0xAC1E, 0x814C, 0xAC1F, 0x814D, 0xAC21, 0x814E, 0xAC22, 0x814F, 0xAC23, 0x8150, 0xAC25,
0x8151, 0xAC26, 0x8152, 0xAC27, 0x8153, 0xAC28, 0x8154, 0xAC29, 0x8155, 0xAC2A, 0x8156, 0xAC2B, 0x8157, 0xAC2E, 0x8158, 0xAC32,
@ -11628,8 +11622,7 @@ const WCHAR oem2uni949[] = { /* Korean --> Unicode pairs */
#endif
#if FF_CODE_PAGE == 950 || FF_CODE_PAGE == 0 /* Traditional Chinese */
static
const WCHAR uni2oem950[] = { /* Unicode --> Big5 pairs */
static const WCHAR uni2oem950[] = { /* Unicode --> Big5 pairs */
0x00A7, 0xA1B1, 0x00AF, 0xA1C2, 0x00B0, 0xA258, 0x00B1, 0xA1D3, 0x00B7, 0xA150, 0x00D7, 0xA1D1, 0x00F7, 0xA1D2, 0x02C7, 0xA3BE,
0x02C9, 0xA3BC, 0x02CA, 0xA3BD, 0x02CB, 0xA3BF, 0x02CD, 0xA1C5, 0x02D9, 0xA3BB, 0x0391, 0xA344, 0x0392, 0xA345, 0x0393, 0xA346,
0x0394, 0xA347, 0x0395, 0xA348, 0x0396, 0xA349, 0x0397, 0xA34A, 0x0398, 0xA34B, 0x0399, 0xA34C, 0x039A, 0xA34D, 0x039B, 0xA34E,
@ -13320,8 +13313,7 @@ const WCHAR uni2oem950[] = { /* Unicode --> Big5 pairs */
0xFF5C, 0xA155, 0xFF5D, 0xA162, 0xFF5E, 0xA1E3, 0xFFE0, 0xA246, 0xFFE1, 0xA247, 0xFFE3, 0xA1C3, 0xFFE5, 0xA244, 0, 0
};
static
const WCHAR oem2uni950[] = { /* Big5 --> Unicode pairs */
static const WCHAR oem2uni950[] = { /* Big5 --> Unicode pairs */
0xA140, 0x3000, 0xA141, 0xFF0C, 0xA142, 0x3001, 0xA143, 0x3002, 0xA144, 0xFF0E, 0xA145, 0x2027, 0xA146, 0xFF1B, 0xA147, 0xFF1A,
0xA148, 0xFF1F, 0xA149, 0xFF01, 0xA14A, 0xFE30, 0xA14B, 0x2026, 0xA14C, 0x2025, 0xA14D, 0xFE50, 0xA14E, 0xFE51, 0xA14F, 0xFE52,
0xA150, 0x00B7, 0xA151, 0xFE54, 0xA152, 0xFE55, 0xA153, 0xFE56, 0xA154, 0xFE57, 0xA155, 0xFF5C, 0xA156, 0x2013, 0xA157, 0xFE31,
@ -15014,8 +15006,7 @@ const WCHAR oem2uni950[] = { /* Big5 --> Unicode pairs */
#endif
#if FF_CODE_PAGE == 437 || FF_CODE_PAGE == 0
static
const WCHAR uc437[] = { /* CP437(U.S.) to Unicode conversion table */
static const WCHAR uc437[] = { /* CP437(U.S.) to Unicode conversion table */
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
@ -15027,8 +15018,7 @@ const WCHAR uc437[] = { /* CP437(U.S.) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 720 || FF_CODE_PAGE == 0
static
const WCHAR uc720[] = { /* CP720(Arabic) to Unicode conversion table */
static const WCHAR uc720[] = { /* CP720(Arabic) to Unicode conversion table */
0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
@ -15040,8 +15030,7 @@ const WCHAR uc720[] = { /* CP720(Arabic) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 737 || FF_CODE_PAGE == 0
static
const WCHAR uc737[] = { /* CP737(Greek) to Unicode conversion table */
static const WCHAR uc737[] = { /* CP737(Greek) to Unicode conversion table */
0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
@ -15053,8 +15042,7 @@ const WCHAR uc737[] = { /* CP737(Greek) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 771 || FF_CODE_PAGE == 0
static
const WCHAR uc771[] = { /* CP771(KBL) to Unicode conversion table */
static const WCHAR uc771[] = { /* CP771(KBL) to Unicode conversion table */
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
@ -15066,8 +15054,7 @@ const WCHAR uc771[] = { /* CP771(KBL) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 775 || FF_CODE_PAGE == 0
static
const WCHAR uc775[] = { /* CP775(Baltic) to Unicode conversion table */
static const WCHAR uc775[] = { /* CP775(Baltic) to Unicode conversion table */
0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
@ -15079,8 +15066,7 @@ const WCHAR uc775[] = { /* CP775(Baltic) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 850 || FF_CODE_PAGE == 0
static
const WCHAR uc850[] = { /* CP850(Latin 1) to Unicode conversion table */
static const WCHAR uc850[] = { /* CP850(Latin 1) to Unicode conversion table */
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
@ -15092,8 +15078,7 @@ const WCHAR uc850[] = { /* CP850(Latin 1) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 852 || FF_CODE_PAGE == 0
static
const WCHAR uc852[] = { /* CP852(Latin 2) to Unicode conversion table */
static const WCHAR uc852[] = { /* CP852(Latin 2) to Unicode conversion table */
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
@ -15105,8 +15090,7 @@ const WCHAR uc852[] = { /* CP852(Latin 2) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 855 || FF_CODE_PAGE == 0
static
const WCHAR uc855[] = { /* CP855(Cyrillic) to Unicode conversion table */
static const WCHAR uc855[] = { /* CP855(Cyrillic) to Unicode conversion table */
0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
@ -15118,8 +15102,7 @@ const WCHAR uc855[] = { /* CP855(Cyrillic) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 857 || FF_CODE_PAGE == 0
static
const WCHAR uc857[] = { /* CP857(Turkish) to Unicode conversion table */
static const WCHAR uc857[] = { /* CP857(Turkish) to Unicode conversion table */
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
@ -15131,8 +15114,7 @@ const WCHAR uc857[] = { /* CP857(Turkish) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 860 || FF_CODE_PAGE == 0
static
const WCHAR uc860[] = { /* CP860(Portuguese) to Unicode conversion table */
static const WCHAR uc860[] = { /* CP860(Portuguese) to Unicode conversion table */
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2,
0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3,
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
@ -15144,8 +15126,7 @@ const WCHAR uc860[] = { /* CP860(Portuguese) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 861 || FF_CODE_PAGE == 0
static
const WCHAR uc861[] = { /* CP861(Icelandic) to Unicode conversion table */
static const WCHAR uc861[] = { /* CP861(Icelandic) to Unicode conversion table */
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5,
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
@ -15157,8 +15138,7 @@ const WCHAR uc861[] = { /* CP861(Icelandic) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 862 || FF_CODE_PAGE == 0
static
const WCHAR uc862[] = { /* CP862(Hebrew) to Unicode conversion table */
static const WCHAR uc862[] = { /* CP862(Hebrew) to Unicode conversion table */
0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
@ -15170,8 +15150,7 @@ const WCHAR uc862[] = { /* CP862(Hebrew) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 863 || FF_CODE_PAGE == 0
static
const WCHAR uc863[] = { /* CP863(Canadian French) to Unicode conversion table */
static const WCHAR uc863[] = { /* CP863(Canadian French) to Unicode conversion table */
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0,
0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192,
0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 0x00AB, 0x00BB,
@ -15183,8 +15162,7 @@ const WCHAR uc863[] = { /* CP863(Canadian French) to Unicode conversion table *
};
#endif
#if FF_CODE_PAGE == 864 || FF_CODE_PAGE == 0
static
const WCHAR uc864[] = { /* CP864(Arabic) to Unicode conversion table */
static const WCHAR uc864[] = { /* CP864(Arabic) to Unicode conversion table */
0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518,
0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000,
0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5,
@ -15196,8 +15174,7 @@ const WCHAR uc864[] = { /* CP864(Arabic) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 865 || FF_CODE_PAGE == 0
static
const WCHAR uc865[] = { /* CP865(Nordic) to Unicode conversion table */
static const WCHAR uc865[] = { /* CP865(Nordic) to Unicode conversion table */
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192,
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4,
@ -15209,8 +15186,7 @@ const WCHAR uc865[] = { /* CP865(Nordic) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 866 || FF_CODE_PAGE == 0
static
const WCHAR uc866[] = { /* CP866(Russian) to Unicode conversion table */
static const WCHAR uc866[] = { /* CP866(Russian) to Unicode conversion table */
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
@ -15222,8 +15198,7 @@ const WCHAR uc866[] = { /* CP866(Russian) to Unicode conversion table */
};
#endif
#if FF_CODE_PAGE == 869 || FF_CODE_PAGE == 0
static
const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */
static const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */
0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389,
0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF,
0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB,
@ -15245,7 +15220,7 @@ const WCHAR uc869[] = { /* CP869(Greek 2) to Unicode conversion table */
#if FF_CODE_PAGE != 0 && FF_CODE_PAGE < 900
WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
WCHAR uni, /* Unicode character to be converted */
DWORD uni, /* UTF-16 encoded character to be converted */
WORD cp /* Code page for the conversion */
)
{
@ -15253,19 +15228,20 @@ WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
const WCHAR *p = CVTBL(uc, FF_CODE_PAGE);
if (uni < 0x80) { /* ASCII char */
c = uni;
if (uni < 0x80) { /* ASCII? */
c = (WCHAR)uni;
} else { /* Non-ASCII char */
if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */
} else { /* Non-ASCII */
if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it in BMP and valid code page? */
for (c = 0; c < 0x80 && uni != p[c]; c++) ;
c = (c + 0x80) & 0xFF;
}
}
return c;
}
WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
WCHAR oem, /* OEM code to be converted */
WORD cp /* Code page for the conversion */
)
@ -15274,7 +15250,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
const WCHAR *p = CVTBL(uc, FF_CODE_PAGE);
if (oem < 0x80) { /* ASCII char */
if (oem < 0x80) { /* ASCII? */
c = oem;
} else { /* Extended char */
@ -15282,6 +15258,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
if (oem < 0x100) c = p[oem - 0x80];
}
}
return c;
}
@ -15294,29 +15271,30 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
/* DBCS fixed code page */
/*------------------------------------------------------------------------*/
#if FF_CODE_PAGE != 0 && FF_CODE_PAGE >= 900
#if FF_CODE_PAGE >= 900
WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
WCHAR uni, /* Unicode character to be converted */
DWORD uni, /* UTF-16 encoded character to be converted */
WORD cp /* Code page for the conversion */
)
{
const WCHAR *p;
WCHAR c = 0;
UINT i, n, li, hi;
WCHAR c = 0, uc;
UINT i = 0, n, li, hi;
if (uni < 0x80) { /* ASCII char */
c = uni;
if (uni < 0x80) { /* ASCII? */
c = (WCHAR)uni;
} else { /* Non-ASCII char */
if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */
} else { /* Non-ASCII */
if (uni < 0x10000 && cp == FF_CODE_PAGE) { /* Is it in BMP and valid code page? */
uc = (WCHAR)uni;
p = CVTBL(uni2oem, FF_CODE_PAGE);
hi = sizeof CVTBL(uni2oem, FF_CODE_PAGE) / 4 - 1;
li = 0;
for (n = 16; n; n--) {
i = li + (hi - li) / 2;
if (uni == p[i * 2]) break;
if (uni > p[i * 2]) {
if (uc == p[i * 2]) break;
if (uc > p[i * 2]) {
li = i;
} else {
hi = i;
@ -15325,25 +15303,26 @@ WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
if (n != 0) c = p[i * 2 + 1];
}
}
return c;
}
WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
WCHAR oem, /* OEM code to be converted */
WORD cp /* Code page for the conversion */
)
{
const WCHAR *p;
WCHAR c = 0;
UINT i, n, li, hi;
UINT i = 0, n, li, hi;
if (oem < 0x80) { /* ASCII char */
if (oem < 0x80) { /* ASCII? */
c = oem;
} else { /* Extended char */
if (cp == FF_CODE_PAGE) { /* Is it a valid code page? */
if (cp == FF_CODE_PAGE) { /* Is it valid code page? */
p = CVTBL(oem2uni, FF_CODE_PAGE);
hi = sizeof CVTBL(oem2uni, FF_CODE_PAGE) / 4 - 1;
li = 0;
@ -15359,6 +15338,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
if (n != 0) c = p[i * 2 + 1];
}
}
return c;
}
#endif
@ -15372,59 +15352,63 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
#if FF_CODE_PAGE == 0
static const WORD cp_code[] = { 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 0};
static const WCHAR *const cp_table[] = {uc437, uc720, uc737, uc771, uc775, uc850, uc852, uc855, uc857, uc860, uc861, uc862, uc863, uc864, uc865, uc866, uc869, 0};
static const WCHAR* const cp_table[] = {uc437, uc720, uc737, uc771, uc775, uc850, uc852, uc855, uc857, uc860, uc861, uc862, uc863, uc864, uc865, uc866, uc869, 0};
WCHAR ff_uni2oem ( /* Returns OEM code character, zero on error */
WCHAR uni, /* Unicode character to be converted */
DWORD uni, /* UTF-16 encoded character to be converted */
WORD cp /* Code page for the conversion */
)
{
const WCHAR *p;
WCHAR c = 0;
WCHAR c = 0, uc;
UINT i, n, li, hi;
if (uni < 0x80) { /* ASCII char */
c = uni;
if (uni < 0x80) { /* ASCII? */
c = (WCHAR)uni;
} else { /* Non-ASCII char */
p = 0;
if (cp < 900) { /* SBCS */
for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get table */
p = cp_table[i];
if (p) { /* Is it a valid CP ? */
for (c = 0; c < 0x80 && uni != p[c]; c++) ; /* Find OEM code in the table */
c = (c + 0x80) & 0xFF;
}
} else { /* DBCS */
switch (cp) {
case 932 : p = uni2oem932; hi = sizeof uni2oem932 / 4 - 1; break;
case 936 : p = uni2oem936; hi = sizeof uni2oem936 / 4 - 1; break;
case 949 : p = uni2oem949; hi = sizeof uni2oem949 / 4 - 1; break;
case 950 : p = uni2oem950; hi = sizeof uni2oem950 / 4 - 1; break;
}
if (p) { /* Is it a valid code page? */
li = 0;
for (n = 16; n; n--) { /* Find OEM code */
i = li + (hi - li) / 2;
if (uni == p[i * 2]) break;
if (uni > p[i * 2]) {
li = i;
} else {
hi = i;
}
} else { /* Non-ASCII */
if (uni < 0x10000) { /* Is it in BMP? */
uc = (WCHAR)uni;
p = 0;
if (cp < 900) { /* SBCS */
for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ; /* Get conversion table */
p = cp_table[i];
if (p) { /* Is it valid code page ? */
for (c = 0; c < 0x80 && uc != p[c]; c++) ; /* Find OEM code in the table */
c = (c + 0x80) & 0xFF;
}
} else { /* DBCS */
switch (cp) { /* Get conversion table */
case 932 : p = uni2oem932; hi = sizeof uni2oem932 / 4 - 1; break;
case 936 : p = uni2oem936; hi = sizeof uni2oem936 / 4 - 1; break;
case 949 : p = uni2oem949; hi = sizeof uni2oem949 / 4 - 1; break;
case 950 : p = uni2oem950; hi = sizeof uni2oem950 / 4 - 1; break;
}
if (p) { /* Is it valid code page? */
li = 0;
for (n = 16; n; n--) { /* Find OEM code */
i = li + (hi - li) / 2;
if (uc == p[i * 2]) break;
if (uc > p[i * 2]) {
li = i;
} else {
hi = i;
}
}
if (n != 0) c = p[i * 2 + 1];
}
if (n != 0) c = p[i * 2 + 1];
}
}
}
return c;
}
WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
WCHAR oem, /* OEM code to be converted */
WCHAR ff_oem2uni ( /* Returns Unicode character in UTF-16, zero on error */
WCHAR oem, /* OEM code to be converted (DBC if >=0x100) */
WORD cp /* Code page for the conversion */
)
{
@ -15433,7 +15417,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
UINT i, n, li, hi;
if (oem < 0x80) { /* ASCII char */
if (oem < 0x80) { /* ASCII? */
c = oem;
} else { /* Extended char */
@ -15466,6 +15450,7 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
}
}
}
return c;
}
#endif
@ -15476,54 +15461,94 @@ WCHAR ff_oem2uni ( /* Returns Unicode character, zero on error */
/* Unicode up-case conversion */
/*------------------------------------------------------------------------*/
WCHAR ff_wtoupper ( /* Returns up-converted character */
WCHAR uni /* Unicode character to be upper converted (BMP only) */
DWORD ff_wtoupper ( /* Returns up-converted code point */
DWORD uni /* Unicode code point to be up-converted */
)
{
/* Compressed upper conversion table */
static const WCHAR cvt1[] = { /* U+0000 - U+0FFF */
const WORD *p;
WORD uc, bc, nc, cmd;
static const WORD cvt1[] = { /* Compressed up conversion table for U+0000 - U+0FFF */
/* Basic Latin */
0x0061,0x031A,
/* Latin-1 Supplement */
0x00E0,0x0317, 0x00F8,0x0307, 0x00FF,0x0001,0x0178,
0x00E0,0x0317,
0x00F8,0x0307,
0x00FF,0x0001,0x0178,
/* Latin Extended-A */
0x0100,0x0130, 0x0132,0x0106, 0x0139,0x0110, 0x014A,0x012E, 0x0179,0x0106,
0x0100,0x0130,
0x0132,0x0106,
0x0139,0x0110,
0x014A,0x012E,
0x0179,0x0106,
/* Latin Extended-B */
0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,
0x01CD,0x0110, 0x01DD,0x0001,0x018E, 0x01DE,0x0112, 0x01F3,0x0003,0x01F1,0x01F4,0x01F4, 0x01F8,0x0128,
0x0222,0x0112, 0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241, 0x0246,0x010A,
0x01CD,0x0110,
0x01DD,0x0001,0x018E,
0x01DE,0x0112,
0x01F3,0x0003,0x01F1,0x01F4,0x01F4,
0x01F8,0x0128,
0x0222,0x0112,
0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241,
0x0246,0x010A,
/* IPA Extensions */
0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,
/* Greek, Coptic */
0x037B,0x0003,0x03FD,0x03FE,0x03FF, 0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A, 0x03B1,0x0311,
0x03C2,0x0002,0x03A3,0x03A3, 0x03C4,0x0308, 0x03CC,0x0003,0x038C,0x038E,0x038F, 0x03D8,0x0118,
0x037B,0x0003,0x03FD,0x03FE,0x03FF,
0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A,
0x03B1,0x0311,
0x03C2,0x0002,0x03A3,0x03A3,
0x03C4,0x0308,
0x03CC,0x0003,0x038C,0x038E,0x038F,
0x03D8,0x0118,
0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,
/* Cyrillic */
0x0430,0x0320, 0x0450,0x0710, 0x0460,0x0122, 0x048A,0x0136, 0x04C1,0x010E, 0x04CF,0x0001,0x04C0, 0x04D0,0x0144,
0x0430,0x0320,
0x0450,0x0710,
0x0460,0x0122,
0x048A,0x0136,
0x04C1,0x010E,
0x04CF,0x0001,0x04C0,
0x04D0,0x0144,
/* Armenian */
0x0561,0x0426,
0x0000
0x0000 /* EOT */
};
static const WCHAR cvt2[] = { /* U+1000 - U+FFFF */
static const WORD cvt2[] = { /* Compressed up conversion table for U+1000 - U+FFFF */
/* Phonetic Extensions */
0x1D7D,0x0001,0x2C63,
/* Latin Extended Additional */
0x1E00,0x0196, 0x1EA0,0x015A,
0x1E00,0x0196,
0x1EA0,0x015A,
/* Greek Extended */
0x1F00,0x0608, 0x1F10,0x0606, 0x1F20,0x0608, 0x1F30,0x0608, 0x1F40,0x0606,
0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F, 0x1F60,0x0608,
0x1F00,0x0608,
0x1F10,0x0606,
0x1F20,0x0608,
0x1F30,0x0608,
0x1F40,0x0606,
0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F,
0x1F60,0x0608,
0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,
0x1F80,0x0608, 0x1F90,0x0608, 0x1FA0,0x0608, 0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,
0x1FCC,0x0001,0x1FC3, 0x1FD0,0x0602, 0x1FE0,0x0602, 0x1FE5,0x0001,0x1FEC, 0x1FF3,0x0001,0x1FFC,
0x1F80,0x0608,
0x1F90,0x0608,
0x1FA0,0x0608,
0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,
0x1FCC,0x0001,0x1FC3,
0x1FD0,0x0602,
0x1FE0,0x0602,
0x1FE5,0x0001,0x1FEC,
0x1FF3,0x0001,0x1FFC,
/* Letterlike Symbols */
0x214E,0x0001,0x2132,
/* Number forms */
0x2170,0x0210, 0x2184,0x0001,0x2183,
0x2170,0x0210,
0x2184,0x0001,0x2183,
/* Enclosed Alphanumerics */
0x24D0,0x051A, 0x2C30,0x042F,
0x24D0,0x051A,
0x2C30,0x042F,
/* Latin Extended-C */
0x2C60,0x0102, 0x2C67,0x0106, 0x2C75,0x0102,
0x2C60,0x0102,
0x2C67,0x0106, 0x2C75,0x0102,
/* Coptic */
0x2C80,0x0164,
/* Georgian Supplement */
@ -15531,36 +15556,38 @@ WCHAR ff_wtoupper ( /* Returns up-converted character */
/* Full-width */
0xFF41,0x031A,
0x0000
0x0000 /* EOT */
};
const WCHAR *p;
WCHAR bc, nc, cmd;
p = uni < 0x1000 ? cvt1 : cvt2;
for (;;) {
bc = *p++; /* Get block base */
if (!bc || uni < bc) break;
nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */
if (uni < bc + nc) { /* In the block? */
switch (cmd) {
case 0: uni = p[uni - bc]; break; /* Table conversion */
case 1: uni -= (uni - bc) & 1; break; /* Case pairs */
case 2: uni -= 16; break; /* Shift -16 */
case 3: uni -= 32; break; /* Shift -32 */
case 4: uni -= 48; break; /* Shift -48 */
case 5: uni -= 26; break; /* Shift -26 */
case 6: uni += 8; break; /* Shift +8 */
case 7: uni -= 80; break; /* Shift -80 */
case 8: uni -= 0x1C60; break; /* Shift -0x1C60 */
if (uni < 0x10000) { /* Is it in BMP? */
uc = (WORD)uni;
p = uc < 0x1000 ? cvt1 : cvt2;
for (;;) {
bc = *p++; /* Get the block base */
if (bc == 0 || uc < bc) break; /* Not matched? */
nc = *p++; cmd = nc >> 8; nc &= 0xFF; /* Get processing command and block size */
if (uc < bc + nc) { /* In the block? */
switch (cmd) {
case 0: uc = p[uc - bc]; break; /* Table conversion */
case 1: uc -= (uc - bc) & 1; break; /* Case pairs */
case 2: uc -= 16; break; /* Shift -16 */
case 3: uc -= 32; break; /* Shift -32 */
case 4: uc -= 48; break; /* Shift -48 */
case 5: uc -= 26; break; /* Shift -26 */
case 6: uc += 8; break; /* Shift +8 */
case 7: uc -= 80; break; /* Shift -80 */
case 8: uc -= 0x1C60; break; /* Shift -0x1C60 */
}
break;
}
break;
if (cmd == 0) p += nc; /* Skip table if needed */
}
if (!cmd) p += nc;
uni = uc;
}
return uni;
}
#endif /* #if _USE_LFN */
#endif /* #if FF_USE_LFN */

View file

@ -1,38 +0,0 @@
/*-------------------------------------------*/
/* Integer type definitions for FatFs module */
/*-------------------------------------------*/
#ifndef FF_INTEGER
#define FF_INTEGER
#ifdef _WIN32 /* FatFs development platform */
#include <windows.h>
#include <tchar.h>
typedef unsigned __int64 QWORD;
#else /* Embedded platform */
/* These types MUST be 16-bit or 32-bit */
typedef int INT;
typedef unsigned int UINT;
/* This type MUST be 8-bit */
typedef unsigned char BYTE;
/* These types MUST be 16-bit */
typedef short SHORT;
typedef unsigned short WORD;
typedef unsigned short WCHAR;
/* These types MUST be 32-bit */
typedef long LONG;
typedef unsigned long DWORD;
/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */
typedef unsigned long long QWORD;
#endif
#endif

View file

@ -29,7 +29,11 @@ union cfg_slot {
uint16_t words[SLOTW_NR];
};
#if MCU == STM32F105
#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_is_blank(_slot) ((_slot)->words[0] == 0xffff)
@ -66,9 +70,9 @@ static union cfg_slot *cfg_slot_find(void)
return NULL;
}
void flash_ff_cfg_update(void)
void flash_ff_cfg_update(void *scratch)
{
union cfg_slot new_slot, *slot = cfg_slot_find();
union cfg_slot *new_slot = scratch, *slot = cfg_slot_find();
uint16_t crc;
/* Nothing to do if Flashed configuration is valid and up to date. */
@ -87,16 +91,18 @@ void flash_ff_cfg_update(void)
} else {
/* No blank slots available. Erase whole page. */
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;
printk("Config: Erased Whole Page\n");
}
memset(&new_slot, 0, sizeof(new_slot));
memcpy(&new_slot.ff_cfg, &ff_cfg, sizeof(ff_cfg));
new_slot.words[SLOTW_DEAD] = 0xffff;
crc = htobe16(crc16_ccitt(&new_slot, sizeof(new_slot)-2, 0xffff));
memset(new_slot, 0, sizeof(*new_slot));
memcpy(&new_slot->ff_cfg, &ff_cfg, sizeof(ff_cfg));
new_slot->words[SLOTW_DEAD] = 0xffff;
crc = htobe16(crc16_ccitt(new_slot, sizeof(*new_slot)-2, 0xffff));
/* Write up to but excluding SLOTW_DEAD. */
fpec_write(&new_slot, sizeof(new_slot)-4, (uint32_t)slot);
fpec_write(new_slot, sizeof(*new_slot)-4, (uint32_t)slot);
/* Write SLOTW_CRC. */
fpec_write(&crc, 2, (uint32_t)&slot->words[SLOTW_CRC]);
printk("Config: Written to Flash Slot %u\n", slot - SLOT_BASE);
@ -114,6 +120,8 @@ void flash_ff_cfg_read(void)
union cfg_slot *slot = cfg_slot_find();
bool_t found = slot_is_valid(slot);
BUILD_BUG_ON(sizeof(*slot) != sizeof(slot->words));
ff_cfg = dfl_ff_cfg;
printk("Config: ");
if (found) {

File diff suppressed because it is too large Load diff

739
src/floppy_generic.c Normal file
View file

@ -0,0 +1,739 @@
/*
* floppy_generic.c
*
* Generic floppy drive low-level support routines.
* Mainly dealing with IRQs, timers and DMA.
*
* 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>.
*/
/* A DMA buffer for running a timer associated with a floppy-data I/O pin. */
struct dma_ring {
/* Current state of DMA (RDATA):
* DMA_inactive: No activity, buffer is empty.
* DMA_starting: Buffer is filling, DMA+timer not yet active.
* DMA_active: DMA is active, timer is operational.
* DMA_stopping: DMA+timer halted, buffer waiting to be cleared.
* Current state of DMA (WDATA):
* DMA_inactive: No activity, flux ring and bitcell buffer are empty.
* DMA_starting: Flux ring and bitcell buffer are filling.
* DMA_active: Writeback processing is active (to mass storage).
* DMA_stopping: Timer halted, buffers waiting to be cleared. */
#define DMA_inactive 0 /* -> {starting, active} */
#define DMA_starting 1 /* -> {active, stopping} */
#define DMA_active 2 /* -> {stopping} */
#define DMA_stopping 3 /* -> {inactive} */
volatile uint8_t state;
/* IRQ handler sets this if the read buffer runs dry. */
volatile uint8_t kick_dma_irq;
/* Indexes into the buf[] ring buffer. */
uint16_t cons;
union {
uint16_t prod; /* dma_rd: our producer index for flux samples */
uint16_t prev_sample; /* dma_wr: previous CCRx sample value */
};
/* DMA ring buffer of timer values (ARR or CCRx). */
uint16_t buf[1024];
};
/* DMA buffers are permanently allocated while a disk image is loaded, allowing
* independent and concurrent management of the RDATA/WDATA pins. */
static struct dma_ring *dma_rd; /* RDATA DMA buffer */
static struct dma_ring *dma_wr; /* WDATA DMA buffer */
/* Statically-allocated floppy drive state. Tracks head movements and
* side changes at all times, even when the drive is empty. */
static struct drive {
uint8_t cyl, head;
bool_t writing;
bool_t sel;
bool_t index_suppressed; /* disable IDX while writing to USB stick */
bool_t amiga_pin34;
uint8_t outp;
volatile bool_t inserted;
struct timer chgrst_timer;
struct {
struct timer timer;
bool_t on;
bool_t changed;
} motor;
struct {
#define STEP_started 1 /* started by hi-pri IRQ */
#define STEP_latched 2 /* latched by lo-pri IRQ */
#define STEP_active (STEP_started | STEP_latched)
#define STEP_settling 4 /* handled by step.timer */
uint8_t state;
bool_t inward;
time_t start;
struct timer timer;
} step;
uint32_t restart_pos;
struct image *image;
} drive;
static struct image *image;
static struct {
struct timer timer, timer_deassert;
time_t prev_time;
bool_t fake_fired;
} index;
static void rdata_stop(void);
static void wdata_start(void);
static void wdata_stop(void);
struct exti_irq {
uint8_t irq, pri;
uint16_t pr_mask; /* != 0: irq- and exti-pending flags are cleared */
};
#if defined(QUICKDISK)
#include "gotek/quickdisk.c"
#define dma_rd_set_active(x) ((void)(x))
#else
#include "gotek/floppy.c"
#endif
/* Initialise IRQs according to statically-defined exti_irqs[]. */
static void floppy_init_irqs(void)
{
const struct exti_irq *e;
unsigned int i;
/* Configure physical interface interrupts. */
for (i = 0, e = exti_irqs; i < ARRAY_SIZE(exti_irqs); i++, e++) {
IRQx_set_prio(e->irq, e->pri);
if (e->pr_mask != 0) {
/* Do not trigger an initial interrupt on this line. Clear EXTI_PR
* before IRQ-pending, otherwise IRQ-pending is immediately
* reasserted. */
exti->pr = e->pr_mask;
IRQx_clear_pending(e->irq);
} else {
/* Common case: we deliberately trigger the first interrupt to
* prime the ISR's state. */
IRQx_set_pending(e->irq);
}
}
/* Enable physical interface interrupts. */
for (i = 0, e = exti_irqs; i < ARRAY_SIZE(exti_irqs); i++, e++) {
IRQx_enable(e->irq);
}
}
/* Allocate and initialise a DMA ring. */
static struct dma_ring *dma_ring_alloc(void)
{
struct dma_ring *dma = arena_alloc(sizeof(*dma));
memset(dma, 0, offsetof(struct dma_ring, buf));
return dma;
}
/* Allocate floppy resources and mount the given image.
* On return: dma_rd, dma_wr, image and index are all valid. */
static void floppy_mount(struct slot *slot)
{
struct image *im;
struct dma_ring *_dma_rd, *_dma_wr;
struct drive *drv = &drive;
FSIZE_t fastseek_sz;
DWORD *cltbl;
FRESULT fr;
int max_ring_kb = (ram_kb >= 128) ? 64 : (ram_kb >= 64) ? 32 : 8;
do {
arena_init();
_dma_rd = dma_ring_alloc();
_dma_wr = dma_ring_alloc();
im = arena_alloc(sizeof(*im));
memset(im, 0, sizeof(*im));
/* Create a fast-seek cluster table for the image. */
#define MAX_FILE_FRAGS 511 /* up to a 4kB cluster table */
cltbl = arena_alloc(0);
*cltbl = (MAX_FILE_FRAGS + 1) * 2;
fatfs_from_slot(&im->fp, slot, FA_READ);
fastseek_sz = f_size(&im->fp);
if (fastseek_sz == 0) {
/* Empty or dummy file. */
cltbl = NULL;
} else {
im->fp.cltbl = cltbl;
fr = f_lseek(&im->fp, CREATE_LINKMAP);
printk("Fast Seek: %u frags\n", (*cltbl / 2) - 1);
if (fr == FR_OK) {
DWORD *_cltbl = arena_alloc(*cltbl * 4);
ASSERT(_cltbl == cltbl);
} else if (fr == FR_NOT_ENOUGH_CORE) {
printk("Fast Seek: FAILED\n");
cltbl = NULL;
} else {
F_die(fr);
}
}
/* ~0 avoids sync match within fewer than 32 bits of scan start. */
im->write_bc_window = ~0;
/* Large buffer to absorb write latencies at mass-storage layer. */
im->bufs.write_bc.len = max_ring_kb*1024; /* power of two */
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
* is because:
* (a) The read BC buffer does not need to absorb such large latencies
* (reads are much more predictable than writes to mass storage).
* (b) By dedicating the first half of the write buffer to writes, we
* can safely start processing write flux while read-data is still
* processing (eg. in-flight mass storage io). At say 10kB of
* dedicated write buffer, this is good for >80ms before colliding
* with read buffers, even at HD data rate (1us/bitcell).
* This is more than enough time for read
* processing to complete. */
im->bufs.read_bc.len = im->bufs.write_bc.len / 2;
im->bufs.read_bc.p = (char *)im->bufs.write_bc.p
+ im->bufs.read_bc.len;
/* Any remaining space is used for staging I/O to mass storage, shared
* between read and write paths (Change of use of this memory space is
* fully serialised). */
im->bufs.write_data.len = arena_avail();
im->bufs.write_data.p = arena_alloc(im->bufs.write_data.len);
im->bufs.read_data = im->bufs.write_data;
/* Minimum allowable buffer space. */
ASSERT(im->bufs.read_data.len >= 10*1024);
/* Mount the image file. */
image_open(im, slot, cltbl);
if (!im->disk_handler->write_track || volume_readonly())
slot->attributes |= AM_RDO;
if (slot->attributes & AM_RDO) {
printk("Image is R/O\n");
} else {
image_extend(im);
}
} while (f_size(&im->fp) != fastseek_sz);
/* After image is extended at mount time, we permit no further changes
* to the file metadata. Clear the dirent info to ensure this. */
im->fp.dir_ptr = NULL;
im->fp.dir_sect = 0;
_dma_rd->state = DMA_stopping;
/* Make allocated state globally visible now. */
drv->image = image = im;
barrier(); /* image ptr /then/ dma rings */
dma_rd = _dma_rd;
dma_wr = _dma_wr;
drv->index_suppressed = FALSE;
index.prev_time = time_now();
}
/* Initialise timers and DMA for RDATA/WDATA. */
static void timer_dma_init(void)
{
/* Enable DMA interrupts. */
dma1->ifcr = DMA_IFCR_CGIF(dma_rdata_ch) | DMA_IFCR_CGIF(dma_wdata_ch);
IRQx_set_prio(dma_rdata_irq, RDATA_IRQ_PRI);
IRQx_set_prio(dma_wdata_irq, WDATA_IRQ_PRI);
IRQx_enable(dma_rdata_irq);
IRQx_enable(dma_wdata_irq);
/* RDATA Timer setup:
* The counter is incremented at SAMPLECLK rate.
*
* 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
* the time between (fixed-width) O_TRUE pulses, mimicking floppy drive
* timings. */
tim_rdata->psc = (SYSCLK_MHZ/SAMPLECLK_MHZ) - 1;
tim_rdata->ccmr1 = (TIM_CCMR1_CC2S(TIM_CCS_OUTPUT) |
TIM_CCMR1_OC2M(TIM_OCM_PWM1));
tim_rdata->ccer = TIM_CCER_CC2E | ((O_TRUE==0) ? TIM_CCER_CC2P : 0);
tim_rdata->ccr2 = sampleclk_ns(400);
tim_rdata->dier = TIM_DIER_UDE;
tim_rdata->cr2 = 0;
/* DMA setup: From a circular buffer into the RDATA Timer's ARR. */
dma_rdata.cpar = (uint32_t)(unsigned long)&tim_rdata->arr;
dma_rdata.cmar = (uint32_t)(unsigned long)dma_rd->buf;
dma_rdata.cndtr = ARRAY_SIZE(dma_rd->buf);
dma_rdata.ccr = (DMA_CCR_PL_HIGH |
DMA_CCR_MSIZE_16BIT |
DMA_CCR_PSIZE_16BIT |
DMA_CCR_MINC |
DMA_CCR_CIRC |
DMA_CCR_DIR_M2P |
DMA_CCR_HTIE |
DMA_CCR_TCIE |
DMA_CCR_EN);
/* WDATA Timer setup:
* The counter runs from 0x0000-0xFFFF inclusive at SAMPLECLK rate.
*
* 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
* 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. */
tim_wdata->psc = (SYSCLK_MHZ/SAMPLECLK_MHZ) - 1;
tim_wdata->arr = 0xffff;
tim_wdata->ccmr1 = TIM_CCMR1_CC1S(TIM_CCS_INPUT_TI1);
tim_wdata->dier = TIM_DIER_CC1DE;
tim_wdata->cr2 = 0;
/* DMA setup: From the WDATA Timer's CCRx into a circular buffer. */
dma_wdata.cpar = (uint32_t)(unsigned long)&tim_wdata->ccr1;
dma_wdata.cmar = (uint32_t)(unsigned long)dma_wr->buf;
dma_wdata.cndtr = ARRAY_SIZE(dma_wr->buf);
dma_wdata.ccr = (DMA_CCR_PL_HIGH |
DMA_CCR_MSIZE_16BIT |
DMA_CCR_PSIZE_16BIT |
DMA_CCR_MINC |
DMA_CCR_CIRC |
DMA_CCR_DIR_P2M |
DMA_CCR_HTIE |
DMA_CCR_TCIE |
DMA_CCR_EN);
}
static unsigned int drive_calc_track(struct drive *drv)
{
return drv->cyl*2 + (drv->head & (drv->image->nr_sides - 1));
}
/* Find current rotational position for read-stream restart. */
static void drive_set_restart_pos(struct drive *drv)
{
uint32_t pos = max_t(int32_t, 0, time_diff(index.prev_time, time_now()));
pos %= drv->image->stk_per_rev;
drv->restart_pos = pos;
drv->index_suppressed = TRUE;
}
/* Called from IRQ context to stop the write stream. */
static void wdata_stop(void)
{
struct write *write;
struct drive *drv = &drive;
uint8_t prev_state = dma_wr->state;
/* Already inactive? Nothing to do. */
if ((prev_state == DMA_inactive) || (prev_state == DMA_stopping))
return;
/* Ok we're now stopping DMA activity. */
dma_wr->state = DMA_stopping;
/* Turn off timer. */
tim_wdata->ccer = 0;
tim_wdata->cr1 = 0;
/* Drain out the DMA buffer. */
IRQx_set_pending(dma_wdata_irq);
switch (ff_cfg.write_drain) {
case WDRAIN_instant:
/* Restart read exactly where write ended. No more IDX pulses until
* write-out is complete. */
drive_set_restart_pos(drv);
break;
case WDRAIN_realtime:
/* Nothing to do. */
break;
case WDRAIN_eot:
/* Position so that an INDEX pulse is quickly triggered. */
drv->restart_pos = drv->image->stk_per_rev - stk_ms(20);
drv->index_suppressed = TRUE;
break;
}
/* Remember where this write's DMA stream ended. */
write = get_write(image, image->wr_prod);
write->dma_end = ARRAY_SIZE(dma_wr->buf) - dma_wdata.cndtr;
image->wr_prod++;
#if !defined(QUICKDISK)
if (!ff_cfg.index_suppression && ff_cfg.write_drain != WDRAIN_realtime) {
/* Opportunistically insert an INDEX pulse ahead of writeback. */
drive_change_output(drv, outp_index, TRUE);
index.fake_fired = TRUE;
IRQx_set_pending(FLOPPY_SOFTIRQ);
/* Position read head so it quickly triggers an INDEX pulse. */
drv->restart_pos = drv->image->stk_per_rev - stk_ms(20);
drv->index_suppressed = TRUE;
}
#endif
}
static void wdata_start(void)
{
struct write *write;
uint32_t start_pos;
switch (dma_wr->state) {
case DMA_starting:
case DMA_active:
/* Already active: ignore WGATE glitch. */
printk("*** WGATE glitch\n");
return;
case DMA_stopping:
if ((image->wr_prod - image->wr_cons) >= ARRAY_SIZE(image->write)) {
/* The write pipeline is full. Complain to the log. */
printk("*** Missed write\n");
return;
}
break;
case DMA_inactive:
/* The write path is quiescent and ready to process this new write. */
break;
}
dma_wr->state = DMA_starting;
/* Start timer. */
tim_wdata->egr = TIM_EGR_UG;
tim_wdata->sr = 0; /* dummy write, gives h/w time to process EGR.UG=1 */
tim_wdata->ccer = TIM_CCER_CC1E | TIM_CCER_CC1P;
tim_wdata->cr1 = TIM_CR1_CEN;
/* 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 %= drive.image->stk_per_rev;
start_pos *= SAMPLECLK_MHZ / STK_MHZ;
write = get_write(image, image->wr_prod);
write->start = start_pos;
write->track = drive_calc_track(&drive);
/* Allow IDX pulses while handling a write. */
drive.index_suppressed = FALSE;
/* Exit head-settling state. Ungates INDEX signal. */
cmpxchg(&drive.step.state, STEP_settling, 0);
}
/* Called from IRQ context to stop the read stream. */
static void rdata_stop(void)
{
uint8_t prev_state = dma_rd->state;
/* Already inactive? Nothing to do. */
if (prev_state == DMA_inactive)
return;
/* Ok we're now stopping DMA activity. */
dma_rd->state = DMA_stopping;
dma_rd_set_active(FALSE);
/* If DMA was not yet active, don't need to touch peripherals. */
if (prev_state != DMA_active)
return;
/* Turn off the output pin */
gpio_configure_pin(gpio_data, pin_rdata, GPO_rdata);
/* Turn off timer. */
tim_rdata->cr1 = 0;
/* track-change = instant: Restart read stream where we left off. */
if ((ff_cfg.track_change == TRKCHG_instant)
&& !drive.index_suppressed
&& ff_cfg.index_suppression)
drive_set_restart_pos(&drive);
}
/* Called from user context to start the read stream. */
static void rdata_start(void)
{
IRQ_global_disable();
/* Did we race rdata_stop()? Then bail. */
if (dma_rd->state == DMA_stopping)
goto out;
dma_rd->state = DMA_active;
dma_rd_set_active(TRUE);
/* Start timer. */
tim_rdata->egr = TIM_EGR_UG;
tim_rdata->sr = 0; /* dummy write, gives h/w time to process EGR.UG=1 */
tim_rdata->cr1 = TIM_CR1_CEN;
/* Enable output. */
if (drive.sel)
gpio_configure_pin(gpio_data, pin_rdata, AFO_rdata);
/* Exit head-settling state. Ungates INDEX signal. */
cmpxchg(&drive.step.state, STEP_settling, 0);
out:
IRQ_global_enable();
}
static void floppy_read_data(struct drive *drv)
{
/* Read some track data if there is buffer space. */
if (image_read_track(drv->image) && dma_rd->kick_dma_irq) {
/* We buffered some more data and the DMA handler requested a kick. */
dma_rd->kick_dma_irq = FALSE;
IRQx_set_pending(dma_rdata_irq);
}
}
static bool_t dma_rd_handle(struct drive *drv);
static bool_t dma_wr_handle(struct drive *drv)
{
struct image *im = drv->image;
struct write *write = get_write(im, im->wr_cons);
bool_t completed;
ASSERT((dma_wr->state == DMA_starting) || (dma_wr->state == DMA_stopping));
/* Start a write. */
if (!drv->writing) {
/* Bail out of read mode. */
if (dma_rd->state != DMA_inactive) {
ASSERT(dma_rd->state == DMA_stopping);
if (dma_rd_handle(drv))
return TRUE;
ASSERT(dma_rd->state == DMA_inactive);
}
/* Set up the track for writing. */
if (image_setup_track(im, write->track, NULL))
return TRUE;
drv->writing = TRUE;
}
/* Continue a write. */
completed = image_write_track(im);
/* Is this write now completely processed? */
if (completed) {
/* Clear the staging buffer. */
im->bufs.write_data.cons = 0;
im->bufs.write_data.prod = 0;
/* Align the bitcell consumer index for start of next write. */
im->bufs.write_bc.cons = (write->bc_end + 31) & ~31;
/* Sync back to mass storage. */
F_sync(&im->fp);
IRQ_global_disable();
/* Consume the write from the pipeline buffer. */
im->wr_cons++;
/* If the buffer is empty then reset the write-bitcell ring and return
* to read operation. */
if ((im->wr_cons == im->wr_prod) && (dma_wr->state != DMA_starting)) {
im->bufs.write_bc.cons = im->bufs.write_bc.prod = 0;
dma_wr->state = DMA_inactive;
}
IRQ_global_enable();
/* This particular write is completed. */
drv->writing = FALSE;
}
return FALSE;
}
bool_t floppy_handle(void)
{
struct drive *drv = &drive;
return ((dma_wr->state == DMA_inactive)
? dma_rd_handle : dma_wr_handle)(drv);
}
static void IRQ_rdata_dma(void)
{
const uint16_t buf_mask = ARRAY_SIZE(dma_rd->buf) - 1;
uint32_t prev_ticks_since_index, ticks, i;
uint16_t nr_to_wrap, nr_to_cons, nr, dmacons, done;
time_t now;
struct drive *drv = &drive;
/* Clear DMA peripheral interrupts. */
dma1->ifcr = DMA_IFCR_CGIF(dma_rdata_ch);
/* If we happen to be called in the wrong state, just bail. */
if (dma_rd->state != DMA_active)
return;
/* Find out where the DMA engine's consumer index has got to. */
dmacons = ARRAY_SIZE(dma_rd->buf) - dma_rdata.cndtr;
/* Check for DMA catching up with the producer index (underrun). */
if (((dmacons < dma_rd->cons)
? (dma_rd->prod >= dma_rd->cons) || (dma_rd->prod < dmacons)
: (dma_rd->prod >= dma_rd->cons) && (dma_rd->prod < dmacons))
&& (dmacons != dma_rd->cons))
printk("RDATA underrun! %x-%x-%x\n",
dma_rd->cons, dma_rd->prod, dmacons);
dma_rd->cons = dmacons;
/* Find largest contiguous stretch of ring buffer we can fill. */
nr_to_wrap = ARRAY_SIZE(dma_rd->buf) - dma_rd->prod;
nr_to_cons = (dmacons - dma_rd->prod - 1) & buf_mask;
nr = min(nr_to_wrap, nr_to_cons);
if (nr == 0) /* Buffer already full? Then bail. */
return;
/* Now attempt to fill the contiguous stretch with flux data calculated
* from buffered image data. */
prev_ticks_since_index = image_ticks_since_index(drv->image);
dma_rd->prod += done = image_rdata_flux(
drv->image, &dma_rd->buf[dma_rd->prod], nr);
dma_rd->prod &= buf_mask;
if (done != nr) {
/* Read buffer ran dry: kick us when more data is available. */
dma_rd->kick_dma_irq = TRUE;
} else if (nr != nr_to_cons) {
/* We didn't fill the ring: re-enter this ISR to do more work. */
IRQx_set_pending(dma_rdata_irq);
}
/* Check if we have crossed the index mark. If not, we're done. */
if (image_ticks_since_index(drv->image) >= prev_ticks_since_index)
return;
/* We crossed the index mark: Synchronise index pulse to the bitstream. */
for (;;) {
/* Snapshot current position in flux stream, including progress through
* current timer sample. */
now = time_now();
/* Ticks left in current sample. */
ticks = tim_rdata->arr - tim_rdata->cnt;
/* Index of next sample. */
dmacons = ARRAY_SIZE(dma_rd->buf) - dma_rdata.cndtr;
/* If another sample was loaded meanwhile, try again for a consistent
* snapshot. */
if (dmacons == dma_rd->cons)
break;
dma_rd->cons = dmacons;
}
/* Sum all flux timings in the DMA buffer. */
for (i = dmacons; i != dma_rd->prod; i = (i+1) & buf_mask)
ticks += dma_rd->buf[i] + 1;
/* Subtract current flux offset beyond the index. */
ticks -= image_ticks_since_index(drv->image);
/* Calculate deadline for index timer. */
ticks /= SAMPLECLK_MHZ/TIME_MHZ;
timer_set(&index.timer, now + ticks);
}
static void IRQ_wdata_dma(void)
{
const uint16_t buf_mask = ARRAY_SIZE(dma_rd->buf) - 1;
uint16_t cons, prod, prev, next;
uint32_t bc_dat = 0, bc_prod;
uint32_t *bc_buf = image->bufs.write_bc.p;
unsigned int sync = image->sync;
unsigned int bc_bufmask = (image->bufs.write_bc.len / 4) - 1;
int curr, cell = image->write_bc_ticks;
struct write *write = NULL;
/* Clear DMA peripheral interrupts. */
dma1->ifcr = DMA_IFCR_CGIF(dma_wdata_ch);
/* If we happen to be called in the wrong state, just bail. */
if (dma_wr->state == DMA_inactive)
return;
/* Find out where the DMA engine's producer index has got to. */
prod = ARRAY_SIZE(dma_wr->buf) - dma_wdata.cndtr;
/* Check if we are processing the tail end of a write. */
barrier(); /* interrogate peripheral /then/ check for write-end. */
if (image->wr_bc != image->wr_prod) {
write = get_write(image, image->wr_bc);
prod = write->dma_end;
}
/* Process the flux timings into the raw bitcell buffer. */
prev = dma_wr->prev_sample;
bc_prod = image->bufs.write_bc.prod;
bc_dat = image->write_bc_window;
for (cons = dma_wr->cons; cons != prod; cons = (cons+1) & buf_mask) {
next = dma_wr->buf[cons];
curr = (int16_t)(next - prev) - (cell >> 1);
if (unlikely(curr < 0)) {
/* Runt flux, much shorter than bitcell clock. Merge it forward. */
continue;
}
prev = next;
while ((curr -= cell) > 0) {
bc_dat <<= 1;
bc_prod++;
if (!(bc_prod&31))
bc_buf[((bc_prod-1) / 32) & bc_bufmask] = htobe32(bc_dat);
}
curr += cell >> 1; /* remove the 1/2-cell bias */
prev -= curr >> 2; /* de-jitter/precomp: carry 1/4 of phase error */
bc_dat = (bc_dat << 1) | 1;
bc_prod++;
switch (sync) {
case SYNC_fm:
/* FM clock sync clock byte is 0xc7. Check for:
* 1010 1010 1010 1010 1x1x 0x0x 0x1x 1x1x */
if ((bc_dat & 0xffffd555) == 0x55555015)
bc_prod = (bc_prod - 31) | 31;
break;
case SYNC_mfm:
if (bc_dat == 0x44894489)
bc_prod &= ~31;
break;
}
if (!(bc_prod&31))
bc_buf[((bc_prod-1) / 32) & bc_bufmask] = htobe32(bc_dat);
}
if (bc_prod & 31)
bc_buf[(bc_prod / 32) & bc_bufmask] = htobe32(bc_dat << (-bc_prod&31));
/* Processing the tail end of a write? */
if (write != NULL) {
/* Remember where this write's bitcell data ends. */
write->bc_end = bc_prod;
if (++image->wr_bc != image->wr_prod)
IRQx_set_pending(dma_wdata_irq);
/* Initialise decoder state for the start of the next write. */
bc_prod = (bc_prod + 31) & ~31;
bc_dat = ~0;
prev = 0;
}
/* Save our progress for next time. */
image->write_bc_window = bc_dat;
image->bufs.write_bc.prod = bc_prod;
dma_wr->cons = cons;
dma_wr->prev_sample = prev;
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

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).
*

View file

@ -124,7 +124,7 @@ void F_truncate(FIL *fp)
#endif /* !FF_FS_READONLY */
void F_lseek(FIL *fp, DWORD ofs)
void F_lseek(FIL *fp, FSIZE_t ofs)
{
FRESULT fr;
#if !FF_FS_READONLY
@ -168,11 +168,13 @@ void F_findnext(DIR *dp, FILINFO *fno)
handle_fr(fr);
}
#if !defined(BOOTLOADER)
void F_chdir(const TCHAR *path)
{
FRESULT fr = f_chdir(path);
handle_fr(fr);
}
#endif
/*
* Local variables:

View file

@ -1,5 +1,5 @@
/*
* update.c
* fw_update.c
*
* USB-flash update bootloader for main firmware.
*
@ -32,30 +32,29 @@
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
#ifndef RELOADER
/* 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_END (0x08020000 - FLASH_PAGE_SIZE)
#define FILE_PATTERN "ff_gotek*.upd"
#define is_reloader FALSE
#else
/* "Reloader": reflashes the main bootloader (first 32kB). */
#define FIRMWARE_START 0x08000000
#define FIRMWARE_END 0x08008000
#define FILE_PATTERN "ff_gotek*.rld"
#define is_reloader TRUE
#elif MCU == AT32F435
#define FIRMWARE_START 0x0800c000
#define FIRMWARE_END (0x08040000 - FLASH_PAGE_SIZE)
#endif
#define FILE_PATTERN "ff_gotek*.upd"
int EXC_reset(void) __attribute__((alias("main")));
static uint8_t USBH_Cfg_Rx_Buffer[512];
/* File buffer. */
static uint8_t buf[2048];
/* FatFS */
static FATFS fatfs;
/* Shared state. regarding update progress/failure. */
static bool_t old_firmware_erased;
static enum {
static enum fail_code {
FC_no_file = 1, /* no update file */
FC_multiple_files, /* multiple update files */
FC_bad_file, /* bad signature or size */
@ -76,92 +75,134 @@ static void canary_check(void)
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)
{
bool_t requested;
#if MCU == STM32F105
/* Power up the backup-register interface and allow writes. */
rcc->apb1enr |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN;
pwr->cr |= PWR_CR_DBP;
/* Has bootloader been requested via magic numbers in the backup regs? */
requested = ((bkp->dr1[0] == 0xdead) && (bkp->dr1[1] == 0xbeef));
/* Clean up backup registers and peripheral clocks. */
bkp->dr1[0] = bkp->dr1[1] = 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;
}
static void erase_old_firmware(void)
{
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);
}
static void msg_display(const char *p)
{
printk("[%s]\n", p);
switch (display_mode) {
case DM_LED_7SEG:
switch (display_type) {
case DT_LED_7SEG:
led_7seg_write_string(p);
break;
case DM_LCD_1602:
case DT_LCD_OLED:
lcd_write(6, 1, 0, p);
lcd_sync();
break;
}
}
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 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
* 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') {
fail_code = FC_no_file;
goto fail;
return FC_no_file;
}
snprintf(update_fname, sizeof(update_fname), "%s", fno.fname);
printk("Found update \"%s\"\n", update_fname);
snprintf(file_name, file_name_size, "%s", fno.fname);
printk("Found update \"%s\"\n", file_name);
F_findnext(&dp, &fno);
if (*fno.fname != '\0') {
printk("** Error: found another file \"%s\"\n", fno.fname);
fail_code = FC_multiple_files;
goto fail;
return FC_multiple_files;
}
F_closedir(&dp);
return 0;
}
/* Open and sanity-check the file. */
msg_display(" RD");
F_open(fp, update_fname, FA_READ);
/* Check size. */
fail_code = ((f_size(fp) < 1024)
|| (f_size(fp) > (FIRMWARE_END-FIRMWARE_START))
|| (f_size(fp) & 3))
? FC_bad_file : 0;
printk("%u bytes: %s\n", f_size(fp), fail_code ? "BAD" : "OK");
if (fail_code)
goto fail;
/* 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;
static uint16_t file_crc(FIL *fp, UINT off, UINT sz)
{
uint16_t crc;
UINT todo, nr;
crc = 0xffff;
F_lseek(fp, off);
for (todo = sz; todo != 0; todo -= nr) {
nr = min_t(UINT, sizeof(buf), todo);
F_read(fp, buf, nr, NULL);
crc = crc16_ccitt(buf, nr, crc);
}
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. */
msg_display("CRC");
crc = 0xffff;
F_lseek(fp, 0);
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;
}
if (file_crc(fp, off, sz) != 0)
return FC_bad_crc;
/* Erase the old firmware. */
msg_display("CLR");
@ -171,32 +212,116 @@ int update(void *unused)
/* Program the new firmware. */
msg_display("PRG");
crc = 0xffff;
F_lseek(fp, 0);
F_lseek(fp, off);
p = FIRMWARE_START;
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);
for (todo = sz; todo != 0; todo -= nr) {
nr = min_t(UINT, sizeof(buf), todo);
F_read(fp, buf, nr, NULL);
fpec_write(buf, nr, p);
if (memcmp((void *)p, buf, nr) != 0) {
/* Byte-by-byte verify failed. */
fail_code = FC_bad_prg;
goto fail;
return FC_bad_prg;
}
p += nr;
}
/* Verify the new firmware (CRC-CCITT). */
p = FIRMWARE_START;
crc = crc16_ccitt((void *)p, f_size(fp), 0xffff);
if (crc) {
if (crc16_ccitt((void *)FIRMWARE_START, sz, 0xffff) != 0) {
/* CRC verify failed. */
fail_code = FC_bad_prg;
goto fail;
return FC_bad_prg;
}
/* All done! */
fail_code = 0;
return 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:
canary_check();
@ -205,11 +330,11 @@ fail:
static void display_setting(bool_t on)
{
switch (display_mode) {
case DM_LED_7SEG:
switch (display_type) {
case DT_LED_7SEG:
led_7seg_display_setting(on);
break;
case DM_LCD_1602:
case DT_LCD_OLED:
lcd_backlight(on);
lcd_sync();
break;
@ -218,10 +343,12 @@ static void display_setting(bool_t on)
static bool_t buttons_pressed(void)
{
/* Check for both LEFT and RIGHT buttons pressed. */
return ((!gpio_read_pin(gpioc, 8) && !gpio_read_pin(gpioc, 7))
/* Also respond to third (SELECT) button on its own. */
|| !gpio_read_pin(gpioc, 6));
unsigned int b = board_get_buttons() | osd_buttons_rx;
return (
/* Check for both LEFT and RIGHT buttons pressed. */
((b & (B_LEFT|B_RIGHT)) == (B_LEFT|B_RIGHT))
/* Also respond to third (SELECT) button on its own. */
|| (b & B_SELECT));
}
/* Wait for both buttons to be pressed (LOW) or not pressed (HIGH). Perform
@ -236,9 +363,8 @@ static void wait_buttons(uint8_t level)
x <<= 1;
if (level) {
/* All buttons must be released. */
x |= gpio_read_pin(gpioc, 8)
&& gpio_read_pin(gpioc, 7)
&& gpio_read_pin(gpioc, 6);
unsigned int b = board_get_buttons() | osd_buttons_rx;
x |= !b;
} else {
x |= buttons_pressed();
}
@ -249,22 +375,17 @@ int main(void)
{
char msg[20];
FRESULT fres;
bool_t update_requested;
/* Relocate DATA. Initialise BSS. */
if (_sdat != _ldat)
if (&_sdat[0] != &_ldat[0])
memcpy(_sdat, _ldat, _edat-_sdat);
memset(_sbss, 0, _ebss-_sbss);
#ifndef RELOADER
/* Enable GPIOC, set all pins as input with weak pull-up. */
rcc->apb2enr = RCC_APB2ENR_IOPCEN;
gpioc->odr = 0xffffu;
gpioc->crh = 0x88888888u;
gpioc->crl = 0x88888888u;
update_requested = fw_update_requested();
/* Enter update mode only if buttons are pressed. */
if (!buttons_pressed()) {
/* Nope, so jump straight at the main firmware. */
if (main_fw_requested() && !update_requested) {
/* Check for, and jump to, the main firmware. */
uint32_t sp = *(uint32_t *)FIRMWARE_START;
uint32_t pc = *(uint32_t *)(FIRMWARE_START + 4);
if (sp != ~0u) { /* only if firmware is apparently not erased */
@ -272,8 +393,8 @@ int main(void)
"mov sp,%0 ; blx %1"
:: "r" (sp), "r" (pc));
}
update_requested = TRUE;
}
#endif
/*
* UPDATE MODE
@ -285,27 +406,27 @@ int main(void)
time_init();
console_init();
board_init();
delay_ms(200); /* 5v settle */
printk("\n** FF %s v%s for Gotek\n",
is_reloader ? "Reloader" : "Update Bootloader",
fw_ver);
printk("\n** FF Update Bootloader %s\n", fw_ver);
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();
display_init();
switch (display_mode) {
case DM_LED_7SEG:
msg_display(is_reloader ? "RLD" : "UPD");
switch (display_type) {
case DT_LED_7SEG:
msg_display("UPD");
break;
case DM_LCD_1602:
snprintf(msg, sizeof(msg), "FF %s",
is_reloader ? "Reloader" : "Update Flash");
case DT_LCD_OLED:
snprintf(msg, sizeof(msg), "FF Update Flash");
lcd_write(0, 0, 0, msg);
lcd_write(0, 1, 0, "v");
lcd_write(1, 1, 0, fw_ver);
lcd_write(0, 1, 0, fw_ver);
lcd_sync();
break;
}
@ -321,7 +442,7 @@ int main(void)
/* Wait for buttons to be released. */
wait_buttons(HIGH);
if (display_mode == DM_LCD_1602)
if (display_type == DT_LCD_OLED)
lcd_write(0, 1, -1, " [ ]");
/* Wait for a filesystem. */

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