Compare commits

...

334 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
129 changed files with 12743 additions and 4664 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

207
Makefile
View file

@ -1,101 +1,144 @@
export FW_VER := 2.13
PROJ := flashfloppy
VER := $(shell git rev-parse --short HEAD)
PROJ := FlashFloppy
VER := v$(FW_VER)
export FW_VER := $(VER)
SUBDIRS += src bootloader bl_update io_test
PYTHON := python3
.PHONY: all upd clean flash start serial gotek
ifneq ($(RULES_MK),y)
.DEFAULT_GOAL := gotek
export ROOT := $(CURDIR)
all:
$(MAKE) -f $(ROOT)/Rules.mk all
.PHONY: FORCE
clean:
rm -f *.hex *.upd *.dfu *.html
$(MAKE) -f $(ROOT)/Rules.mk $@
.DEFAULT_GOAL := all
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.upd FF_Gotek-Bootloader-$(VER).upd
mv IOT.upd FF_Gotek-IO-Test-$(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 := v6-FF
HXC_FF_VER := v9-FF
dist:
rm -rf flashfloppy-*
mkdir -p flashfloppy-$(VER)/alt/bootloader
mkdir -p flashfloppy-$(VER)/alt/logfile
mkdir -p flashfloppy-$(VER)/alt/io-test
$(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-Bootloader-$(VER).upd flashfloppy-$(VER)/alt/bootloader/
cp -a FF_Gotek-IO-Test-$(VER).upd flashfloppy-$(VER)/alt/io-test/
$(MAKE) clean
debug=n logfile=y $(MAKE) -f $(ROOT)/Rules.mk upd
mv FF.upd flashfloppy-$(VER)/alt/logfile/FF_Gotek-Logfile-$(VER).upd
$(MAKE) clean
cp -a COPYING flashfloppy-$(VER)/
cp -a README.md flashfloppy-$(VER)/
cp -a RELEASE_NOTES flashfloppy-$(VER)/
cp -a examples flashfloppy-$(VER)/
[ -e HxC_Compat_Mode-$(HXC_FF_VER).zip ] || \
wget -q --show-progress $(HXC_FF_URL)/$(HXC_FF_VER)/HxC_Compat_Mode-$(HXC_FF_VER).zip
rm -rf index.html
unzip -q HxC_Compat_Mode-$(HXC_FF_VER).zip
mv HxC_Compat_Mode flashfloppy-$(VER)
mkdir -p flashfloppy-$(VER)/scripts
cp -a scripts/edsk* flashfloppy-$(VER)/scripts/
cp -a scripts/mk_hfe.py flashfloppy-$(VER)/scripts/
zip -r flashfloppy-$(VER).zip flashfloppy-$(VER)
_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
upd:
$(MAKE) -C src -f $(ROOT)/Rules.mk $(PROJ).elf $(PROJ).bin $(PROJ).hex
$(PYTHON) ./scripts/mk_update.py src/$(PROJ).bin FF.upd
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
logfile=n $(MAKE) -C bl_update -f $(ROOT)/Rules.mk \
BL_Update.elf BL_Update.bin BL_Update.hex
logfile=n $(MAKE) -C io_test -f $(ROOT)/Rules.mk \
IO_Test.elf IO_Test.bin IO_Test.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 bl_update/BL_Update.bin BL.upd
$(PYTHON) ./scripts/mk_update.py io_test/IO_Test.bin IOT.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)
ocd: FORCE all
$(PYTHON) scripts/openocd/flash.py $(T)
start:
sudo stm32flash -b $(BAUD) -g 0 $(DEV)
flash: FORCE all
$(SUDO) $(STM32FLASH) -b $(BAUD) -w $(T) $(DEV)
serial:
sudo miniterm.py $(DEV) 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,26 +1,32 @@
# FlashFloppy
A retro floppy emulator for the ubiquitous Gotek hardware.
- Directly supports a [wide range of image formats][Image-Formats]
*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**][Downloads]
## Documentation
- [**Read the GitHub Wiki**](https://github.com/keirf/FlashFloppy/wiki)
## Donations
FlashFloppy is a labour of love: working on it takes a **lot** of time
and effort. The development of new features, plus maintenance, bug
fixing, testing, release preparations and so on is a huge undertaking;
and that ignores non-development tasks such as writing documentation
and community management.
If you would like to **support FlashFloppy's development**, please see
the [**Donations page**][Donations].
- [**Read the GitHub Wiki**](https://github.com/keirf/flashfloppy/wiki)
## Redistribution
@ -38,8 +44,13 @@ file on your selling page. For example:
- FlashFloppy is free software. For more information see the
[license](COPYING).
[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
[Donations]: https://github.com/keirf/FlashFloppy/wiki/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
[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,6 +3,286 @@
** 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
@ -13,7 +293,7 @@
- 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
- https://github.com/keirf/flashfloppy/wiki/Testing-IO-Pins
** v2.11a - 23 May 2019
- Simpler bootloader update process with all-in-one update file
@ -117,7 +397,7 @@
- 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,20 +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
# Following options are mutually exclusive
ifeq ($(bootloader),y)
FLAGS += -DBOOTLOADER=1
else ifeq ($(logfile),y)
endif
ifeq ($(logfile),y)
FLAGS += -DLOGFILE=1
endif
ifeq ($(quickdisk),y)
FLAGS += -DQUICKDISK=1
endif
FLAGS += -MMD -MF .$(@F).d
DEPS = .*.d
@ -36,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
@ -55,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 $@
@ -78,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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
@ -80,6 +96,12 @@ head-settle-ms = 12
# 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
@ -89,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
@ -140,6 +162,7 @@ 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
@ -149,17 +172,20 @@ twobutton-action = zero
# Input sensor type at the rotary-encoder inputs (pins PC10 and PC11):
# [full | half | quarter]:
# Rotary encoder, identified by fraction of a Gray-code cycle performed
# per detent/click. If default value ('full') requires multiple
# clicks/detents to move position then change to 'half' (if 2 clicks
# per move) or 'quarter' (if 4 clicks).
# per detent/click. If default value (full) requires multiple
# clicks/detents to move position then change to half (if 2 clicks
# per move) or quarter (if 4 clicks).
# [trackball]:
# Blackberry-style trackball (eg. using Hall-effect sensors).
# [buttons]:
# 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 | trackball | buttons | reverse
# Values: none | quarter | half | full | trackball | buttons | reverse | v2
rotary = full
# Prefix for image names in indexed navigation mode. String can be empty ("").
@ -168,14 +194,18 @@ indexed-prefix = "DSKA"
##
## DISPLAY
# Display Type: <type>[-rotate][-narrow]
# 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)
# Values: auto | lcd-NNx02 | oled-128xNN[-rotate][-narrow[er]]
# -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.
@ -189,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
@ -222,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

View file

@ -4,71 +4,40 @@
# 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.
# Matching Rules:
# 1. If tag 'tagname' is defined, this describes the geometry of
# all IMG/IMA files of the form *.tagname.img and *.tagname.ima
# 2. If tag 'default' is defined, this describes the geometry of
# all IMG/IMA files that do not match any other defined tag.
# This includes untagged files (ie. *.img and *.ima).
# 3. If a file does not match any tag, and no default tag is defined,
# FlashFloppy uses normal geometry auto-detection based on the
# host= setting in FF.CFG.
# A badly-defined tag (eg. undefined mandatory parameters) will result in
# A badly-defined section (eg. undefined mandatory parameters) may result in
# error 31 (Bad Image).
# Tag name in square brackets.
# 'default' matches any *.img or *.ima which are untagged, or do not
# match any other defined tag.
[default]
####################################################################
## 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.
# Mandatory: Number of cylinders (1-254).
# [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
# Mandatory: Number of sectors per track (1-256).
secs = 9
# Number of drive-head steps between cylinders. Default is 1.
# step = 1
# Mandatory: Bytes per sector (128, 256, 512, 1024, 2048, 4096, 8192).
bps = 512
# Optional: ID of first sector on each track (0-255[:0-255])
# Default is 1.
# Numbers may be expressed in hexadecimal with 0x prefix (eg. 0xab).
# Format x:y allows specifying different ID (x, y) for each head/disk-side.
# Format x specifies same ID (x) for both heads/sides of a double-sided disk.
# id = 1
# Optional: Recording mode (fm | mfm). Default is mfm.
# mode = mfm
# Optional: Sector interleave. Default is 1:1 (no interleave).
# interleave = 1
# Optional: Sector skew per cylinders. Default is 0 (no skew).
# cskew = 0
# Optional: Sector skew per head. Default is 0 (no skew).
# hskew = 0
# Optional: Rotational RPM. Default is 300.
# rpm = 300
# Optional: Post-Data Gap (0-255). Default is 0.
# 0 = based on recording mode and sector size.
# gap3 = 0
# Optional: Index Address Mark (yes | no). Default is yes.
# iam = yes
# Optional: Data rate in kHz (kbit/s) (eg. MFM DD = 250). Default is 0.
# 0 = based on recording mode and size of track.
# rate = 0
# Optional: Image file track layout. Default is "interleaved".
# 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, ...
@ -77,11 +46,127 @@ bps = 512
# eg. "sequential,reverse-side1"
# file-layout = interleaved
# 'sssd40' matches images of the form *.sssd40.img and *.sssd40.ima
[sssd40]
cyls = 40
heads = 1
secs = 9
bps = 256
mode = fm
iam = no
## 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)

View file

@ -25,7 +25,7 @@ int get_next_opt(struct opts *opts);
#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
@ -41,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
@ -58,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
@ -82,9 +86,9 @@ 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
@ -94,11 +98,16 @@ struct __packed ff_cfg {
#define DISPLAY_narrower (1<<0)
#define DISPLAY_rotate (1<<2)
#define DISPLAY_narrow (1<<3)
/*#define DISPLAY_sh1106 (1<<4) legacy unused */
#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
@ -106,6 +115,8 @@ struct __packed ff_cfg {
#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;
@ -129,7 +140,7 @@ struct __packed ff_cfg {
uint8_t head_settle_ms;
uint8_t oled_contrast;
char indexed_prefix[8];
uint8_t display_mode;
uint8_t _unused; /* never been used */
#define SORT_never 0
#define SORT_always 1
#define SORT_small 2
@ -140,6 +151,25 @@ struct __packed ff_cfg {
#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;

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,9 +13,19 @@
#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"

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
@ -28,17 +42,19 @@
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;
@ -50,23 +66,42 @@ struct hfe_image {
} 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 id;
uint8_t no; /* 3 bits */
uint8_t r;
uint8_t n; /* 3 bits */
};
struct raw_trk {
uint16_t nr_sectors;
uint16_t sec_off;
uint16_t data_rate;
uint8_t gap_2, gap_3, gap_4a;
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_* */
@ -80,7 +115,6 @@ struct img_image {
uint32_t *file_sec_offsets;
/* Delay start of track this many bitcells past index. */
uint32_t track_delay_bc;
uint8_t interleave, cskew, hskew;
uint16_t gap_4;
uint32_t idx_sz, idam_sz;
uint16_t dam_sz_pre, dam_sz_post;
@ -106,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 {
@ -126,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;
@ -147,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' */
@ -160,6 +200,7 @@ 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;
@ -199,7 +240,7 @@ void image_open(struct image *im, struct slot *slot, DWORD *cltbl);
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.
@ -219,7 +260,7 @@ 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. */
@ -229,6 +270,8 @@ 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
@ -242,11 +285,22 @@ void floppy_cancel(void);
bool_t floppy_handle(void); /* TRUE -> re-read config file */
void floppy_set_cyl(uint8_t unit, uint8_t cyl);
struct track_info {
uint8_t cyl, side, sel, writing;
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_motor_delay(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

@ -19,9 +19,10 @@ struct exception_frame {
/* 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 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)
@ -73,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;

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
@ -71,6 +35,7 @@ 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);
@ -87,6 +52,8 @@ void qsort_p(void *base, unsigned int nr,
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)));
@ -102,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);
@ -122,6 +91,8 @@ static inline int vprintk(const char *format, va_list ap) { return 0; }
static inline int printk(const char *format, ...) { return 0; }
#endif
#define log(f, a...) printk("%s: " f, LOG_PREFIX, ## a)
#if defined(LOGFILE)
/* Logfile management */
void logfile_flush(FIL *file);
@ -145,14 +116,16 @@ 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_OLED 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);
@ -170,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);
@ -181,8 +159,12 @@ uint16_t get_slot_nr(void);
bool_t set_slot_nr(uint16_t slot_nr);
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
@ -190,8 +172,20 @@ 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[];
@ -211,8 +205,6 @@ 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

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);

View file

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

View file

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

View file

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

View file

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

89
scripts/check_hex.py Normal file
View file

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

View file

@ -19,11 +19,11 @@ def main(argv):
tsz = in_dat[52:256]
populated = 0
ext = False
if x[0].startswith("EXTENDED CPC DSK File\r\nDisk-Info\r\n"):
if x[0].startswith(b"EXTENDED CPC DSK File\r\nDisk-Info\r\n"):
print("Extended DSK")
ext = True
else:
assert x[0].startswith("MV - CPCEMU")
assert x[0].startswith(b"MV - CPCEMU")
print("Standard DSK")
while tsz:
x = struct.unpack("B", tsz[:1])

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

@ -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":
@ -61,11 +89,18 @@ def main(argv):
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)

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,8 +5,6 @@ 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
{
@ -24,17 +22,25 @@ SECTIONS
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);
@ -49,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 94K
#define RAM_BASE 0x20000000
#define RAM_LEN 64K
#include "../scripts/stm32f10x.ld.S"

View file

@ -1,40 +1,63 @@
OBJS += arena.o
OBJS += build_info.o
OBJS += cache.o
OBJS += cancellation.o
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
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;
}
/*

View file

@ -20,8 +20,13 @@
*/
/* 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[];
@ -30,7 +35,7 @@ asm (
" .align 4\n"
" .global update_start, update_end\n"
"update_start:\n"
" .incbin \"../bootloader/Bootloader.bin\"\n"
" .incbin \"../bootloader/target.bin\"\n"
"update_end:\n"
" .previous\n"
);
@ -50,18 +55,18 @@ uint8_t board_id;
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_OLED:
case DT_LCD_OLED:
lcd_write(6, 1, 0, p);
lcd_sync();
break;
@ -70,11 +75,11 @@ static void msg_display(const char *p)
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_OLED:
case DT_LCD_OLED:
lcd_backlight(on);
lcd_sync();
break;
@ -88,7 +93,7 @@ int main(void)
int retries = 0;
/* Relocate DATA. Initialise BSS. */
if (_sdat != _ldat)
if (&_sdat[0] != &_ldat[0])
memcpy(_sdat, _ldat, _edat-_sdat);
memset(_sbss, 0, _ebss-_sbss);
@ -99,18 +104,18 @@ int main(void)
board_init();
delay_ms(200); /* 5v settle */
printk("\n** FF Update Firmware for Gotek\n", fw_ver);
printk("\n** FF Update Firmware %s for Gotek\n", fw_ver);
printk("** Keir Fraser <keir.xen@gmail.com>\n");
printk("** https://github.com/keirf/FlashFloppy\n\n");
flash_ff_cfg_read();
display_init();
switch (display_mode) {
case DM_LED_7SEG:
switch (display_type) {
case DT_LED_7SEG:
msg_display("BLD");
break;
case DM_LCD_OLED:
case DT_LCD_OLED:
lcd_write(0, 0, 0, "New Bootloader..");
lcd_write(0, 1, 0, " [ ]");
lcd_sync();

View file

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

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,15 +9,16 @@
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
/* Alphanumeric plus ",-_:" */
/* Alphanumeric plus ",-._:" */
static int isvalid(char c)
{
const static char map[] = {
/* , (2c) - (2d) 0-9 (30-39) : (3a) A-Z (41-5a) _ (5f) a-z (61-7a) */
/* , (2c) - (2d) . (2e) 0-9 (30-39) : (3a)
* A-Z (41-5a) _ (5f) a-z (61-7a) */
0x00, 0x00, 0x00, 0x00, /* 0x00-0x1f */
0x00, 0x0c, 0xff, 0xe0, /* 0x20-0x2f */
0x7f, 0xff, 0xff, 0xe1, /* 0x30-0x3f */
0x7f, 0xff, 0xff, 0xe0 /* 0x40-0x4f */
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;
}

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,10 +12,27 @@
#define BAUD 3000000 /* 3Mbaud */
#define USART1_IRQ 37
#define USART3_IRQ 39
#if 1
#define usart usart1
#define USART_IRQ USART1_IRQ
#define usart_gpio gpioa
#define usart_tx_pin 9
#define usart_rx_pin 10
#define PCLK (APB2_MHZ * 1000000)
#else
#define usart usart3
#define USART_IRQ USART3_IRQ
#define usart_gpio gpioc
#define usart_tx_pin 10
#define usart_rx_pin 11
#define PCLK (APB1_MHZ * 1000000)
#endif
/* Normally flush to serial is asynchronously executed in a low-pri IRQ. */
void IRQ_44(void) __attribute__((alias("SOFTIRQ_console")));
#define CONSOLE_SOFTIRQ 44
#define CONSOLE_SOFTIRQ SOFTIRQ_1
DEFINE_IRQ(CONSOLE_SOFTIRQ, "SOFTIRQ_console");
/* We stage serial output in a ring buffer. */
static char ring[2048];
@ -32,9 +49,9 @@ static void flush_ring_to_serial(void)
barrier();
while (c != p) {
while (!(usart1->sr & USART_SR_TXE))
while (!(usart->sr & USART_SR_TXE))
cpu_relax();
usart1->dr = ring[MASK(c++)];
usart->dr = ring[MASK(c++)];
}
barrier();
@ -114,16 +131,27 @@ void console_sync(void)
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);
@ -133,10 +161,16 @@ void console_init(void)
* 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>
*
@ -110,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)
@ -210,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,14 +24,14 @@ void display_init(void)
stk_time_t t = stk_now();
if (lcd_init()) {
display_mode = DM_LCD_OLED;
display_type = DT_LCD_OLED;
snprintf(name, sizeof(name), "LCD/OLED");
break; /* positive identification */
}
if ((ff_cfg.display_type & 3) == DISPLAY_auto) {
led_7seg_init();
display_mode = DM_LED_7SEG;
display_type = DT_LED_7SEG;
snprintf(name, sizeof(name), "%u-Digit LED",
led_7seg_nr_digits());
if (led_7seg_nr_digits() == 3)

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,13 +56,27 @@ 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;
@ -58,18 +86,29 @@ 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)
@ -83,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);
@ -101,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);
@ -151,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)) {
@ -199,48 +343,73 @@ static bool_t i2c_wait(uint8_t s)
return TRUE;
}
/* Synchronously transmit the I2C START sequence.
* Caller must already have asserted I2C_CR1_START. */
#define I2C_RD TRUE
#define I2C_WR FALSE
static bool_t i2c_start(uint8_t a, bool_t rd)
static void i2c_start(uint8_t a, unsigned int nr, bool_t rd)
{
if (!i2c_wait(I2C_SR1_SB))
return FALSE;
i2c->dr = (a << 1) | rd;
if (!i2c_wait(I2C_SR1_ADDR))
return FALSE;
(void)i2c->sr2;
return TRUE;
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 void i2c_stop(void)
static bool_t i2c_stop(void)
{
i2c->cr1 |= I2C_CR1_STOP;
while (i2c->cr1 & I2C_CR1_STOP)
continue;
if (!i2c_wait(I2C_SR_STOPF))
return FALSE;
i2c->icr = I2C_SR_STOPF;
return TRUE;
}
/* Synchronously transmit an I2C byte. */
static bool_t i2c_sync_write(uint8_t b)
{
i2c->dr = b;
return i2c_wait(I2C_SR1_BTF);
if (!i2c_wait(I2C_SR_TXIS))
return FALSE;
i2c->txdr = b;
return TRUE;
}
static bool_t i2c_sync_write_txn(uint8_t *cmds, unsigned int nr)
/* 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;
if (!i2c_start(i2c_addr, I2C_WR))
return FALSE;
i2c_start(addr, nr, I2C_WR);
for (i = 0; i < nr; i++)
if (!i2c_sync_write(*cmds++))
return FALSE;
return TRUE;
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). */
@ -254,11 +423,10 @@ static void write4(uint8_t val)
/* Check whether an I2C device is responding at given address. */
static bool_t i2c_probe(uint8_t a)
{
i2c->cr1 |= I2C_CR1_START;
if (!i2c_start(a, I2C_WR) || !i2c_sync_write(0))
i2c_start(a, 1, I2C_WR);
if (!i2c_sync_write(0))
return FALSE;
i2c_stop();
return TRUE;
return i2c_stop();
}
/* Check given inclusive range of addresses for a responding I2C device. */
@ -273,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)
@ -282,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;
@ -320,9 +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;
@ -354,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. */
@ -415,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);
@ -431,20 +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;
}
i2c->cr1 |= I2C_CR1_START;
if (!i2c_start(i2c_addr, I2C_WR))
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;
@ -452,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;
@ -530,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)
{
@ -589,7 +779,10 @@ 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, /*?*//* page address range: 0-? */
0x22, /* page address range: ?-? */
}, ztech_addr_cmds[] = {
0xda, 0x12, /* alternate com pins config */
0x21, 4, 131, /* column address range: 4-131 */
}, sh1106_addr_cmds[] = {
0x10 /* column address high nibble is zero */
};
@ -602,12 +795,13 @@ static unsigned int oled_start_i2c(uint8_t *buf)
p += oled_queue_cmds(p, sh1106_addr_cmds, sizeof(sh1106_addr_cmds));
/* Column address: 0 or 2 (seems 128x64 displays are shifted by 2). */
*dc++ = (oled_height == 64) ? 0x02 : 0x00;
/* Page address: according to oled_row. */
*dc++ = 0xb0 + oled_row;
/* 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. */
@ -615,125 +809,107 @@ 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_stop();
/* 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_stop();
/* 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)
{
return (oled_model == OLED_sh1106)
? sh1106_prep_buffer()
: ssd1306_prep_buffer();
}
static bool_t oled_probe_model(void)
{
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. */
i2c->cr1 |= I2C_CR1_START;
if (!i2c_sync_write_txn(cmd1, sizeof(cmd1)))
if (!i2c_sync_write_txn(i2c_addr, cmd1, sizeof(cmd1)))
goto fail;
/* Read stage. */
i2c->cr1 |= I2C_CR1_START | I2C_CR1_ACK;
if (!i2c_start(i2c_addr, I2C_RD) || !i2c_wait(I2C_SR1_RXNE))
if (!i2c_sync_read_txn(i2c_addr, rsp, sizeof(rsp)))
goto fail;
i2c->cr1 &= ~I2C_CR1_ACK; /* NACK and Restart after next byte */
i2c->cr1 |= I2C_CR1_START;
(void)i2c->dr; /* 1st read: Dummy */
if (!i2c_wait(I2C_SR1_RXNE))
goto fail;
x = i2c->dr; /* 2nd read: Data */
x = rsp[1];
/* 2nd Write stage. */
cmd2[3] = x ^ rand[i]; /* XOR the write with "randomness" */
if (!i2c_sync_write_txn(cmd2, sizeof(cmd2)))
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))
@ -741,7 +917,6 @@ static bool_t oled_probe_model(void)
/* Remember what we wrote, for next iteration. */
px = cmd2[3];
}
i2c_stop();
oled_model = (i == 3) ? OLED_sh1106 : OLED_ssd1306;
printk("OLED: %s\n", (oled_model == OLED_sh1106) ? "SH1106" : "SSD1306");
@ -751,6 +926,15 @@ 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)
{
static const uint8_t init_cmds[] = {
@ -762,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) */
@ -771,19 +954,11 @@ 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;
/* 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 (!(ff_cfg.display_type & DISPLAY_slow))
oled_init_fast_mode();
if ((oled_model == OLED_unknown) && !oled_probe_model())
goto fail;
@ -793,6 +968,7 @@ static void oled_init(void)
/* 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) */
@ -801,16 +977,18 @@ 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;

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, 0x08, 0x38, 0x40, 0x54, 0x5c, 0x73, 0x67, 0x50, /* j-r */
0x6d, 0x78, 0x1c, 0x09, 0x41, 0x76, 0x6e, 0x00 /* s-z */
0x6d, 0x78, 0x1c, 0x09, 0x41, 0x76, 0x6e, 0x52 /* s-z */
};
static const uint8_t digits[] = {
@ -261,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,19 +249,31 @@ typedef struct {
FSIZE_t fsize; /* File size */
WORD fdate; /* Modified date */
WORD ftime; /* Modified time */
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) */
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 {
@ -234,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;
@ -273,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 */
@ -308,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,8 +1,8 @@
/*---------------------------------------------------------------------------/
/ FatFs - Configuration file
/ FatFs Functional Configurations
/---------------------------------------------------------------------------*/
#define FFCONF_DEF 87030 /* Revision ID */
#define FFCONF_DEF 86606 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
@ -26,7 +26,7 @@
#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.
@ -114,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.
@ -166,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
@ -192,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
@ -224,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
@ -254,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
@ -274,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)
@ -87,6 +91,8 @@ void flash_ff_cfg_update(void *scratch)
} 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");
}

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

@ -32,21 +32,29 @@
* See the file COPYING for more details, or visit <http://unlicense.org>.
*/
/* Main bootloader: flashes the main firmware (last 96kB of Flash). */
/* Main bootloader: flashes the main firmware. */
#if MCU == STM32F105
#define FIRMWARE_START 0x08008000
#define FIRMWARE_END (0x08020000 - FLASH_PAGE_SIZE)
#elif MCU == AT32F435
#define FIRMWARE_START 0x0800c000
#define FIRMWARE_END (0x08040000 - FLASH_PAGE_SIZE)
#endif
#define FILE_PATTERN "ff_gotek*.upd"
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 */
@ -67,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_OLED:
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");
@ -162,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();
@ -196,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_OLED:
case DT_LCD_OLED:
lcd_backlight(on);
lcd_sync();
break;
@ -209,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
@ -227,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();
}
@ -240,21 +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);
/* 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 */
@ -262,6 +393,7 @@ int main(void)
"mov sp,%0 ; blx %1"
:: "r" (sp), "r" (pc));
}
update_requested = TRUE;
}
/*
@ -274,24 +406,27 @@ int main(void)
time_init();
console_init();
board_init();
delay_ms(200); /* 5v settle */
printk("\n** FF Update Bootloader v%s for Gotek\n", fw_ver);
printk("\n** FF Update Bootloader %s\n", fw_ver);
printk("** Keir Fraser <keir.xen@gmail.com>\n");
printk("** 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:
switch (display_type) {
case DT_LED_7SEG:
msg_display("UPD");
break;
case DM_LCD_OLED:
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;
}
@ -307,7 +442,7 @@ int main(void)
/* Wait for buttons to be released. */
wait_buttons(HIGH);
if (display_mode == DM_LCD_OLED)
if (display_type == DT_LCD_OLED)
lcd_write(0, 1, -1, " [ ]");
/* Wait for a filesystem. */

View file

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

View file

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

260
src/gotek/quickdisk.c Normal file
View file

@ -0,0 +1,260 @@
/*
* gotek/quickdisk.c
*
* Gotek-specific QD-interface setup.
*
* 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>.
*/
/* Used by floppy_generic.c to set up correct RDATA polarity. */
#define O_TRUE 1
/* Input pins: All are level signals. */
#define pin_reset 1 /* PA1, /RS: LOW = Reset asserted */
#define pin_motor 0 /* PA0, /MO: LOW = Motor on */
static uint8_t pin_wgate = 9; /* PB9, WG: HIGH = Write active */
/* Output pins: All are level signals. PBx = 0-15, PAx = 16-31. */
static uint8_t pin_02 = 7; /* PB7 */
#define pin_08 8 /* PB8 */
static uint8_t pin_26 = 6; /* PB6 */
#define pin_28 5 /* PB5 */
#define pin_34 3 /* PB3 */
#define pin_media pin_02 /* /MS: LOW = Media present */
#define pin_wrprot pin_28 /* /WP: LOW = Media present and writeable */
#define pin_ready pin_34 /* /RY: LOW = Read/write window active */
/* RDATA and /WDATA */
#define gpio_data gpioa
#define pin_wdata 8 /* /WD: Negative pulse signal */
#define tim_wdata (tim1)
#define dma_wdata (dma1->ch[2-1])
#define dma_wdata_ch 2
#define dma_wdata_irq DMA1_CH2_IRQ
DEFINE_IRQ(dma_wdata_irq, "IRQ_wdata_dma");
#define pin_rdata 7 /* RD: Positive pulse signal */
#define tim_rdata (tim3)
#define dma_rdata (dma1->ch[3-1])
#define dma_rdata_ch 3
#define dma_rdata_irq DMA1_CH3_IRQ
DEFINE_IRQ(dma_rdata_irq, "IRQ_rdata_dma");
/* EXTI IRQs. */
#define motor_irq 6
#define wgate_irq 23
void IRQ_6(void) __attribute__((alias("IRQ_MOTOR_changed"))); /* EXTI0 */
void IRQ_7(void) __attribute__((alias("IRQ_WGATE_rotary"))); /* EXTI1 */
void IRQ_23(void) __attribute__((alias("IRQ_WGATE_rotary"))); /* EXTI9_5 */
void IRQ_28(void) __attribute__((alias("IRQ_RESET_changed"))); /* TMR2 */
void IRQ_40(void) __attribute__((alias("IRQ_rotary_changed"))); /* EXTI15_10 */
static const struct exti_irq exti_irqs[] = {
/* MOTOR */ { 6, TIMER_IRQ_PRI, 0 },
/* WGATE */ { 7, FLOPPY_IRQ_WGATE_PRI, 0 },
/* WGATE */ { 23, FLOPPY_IRQ_WGATE_PRI, 0 },
/* RESET */ { 28, TIMER_IRQ_PRI, 0 },
/* Rotary */ { 40, TIMER_IRQ_PRI, 0 }
};
bool_t floppy_ribbon_is_reversed(void)
{
return FALSE;
}
static void board_floppy_init(void)
{
#if MCU == STM32F105
gpio_configure_pin(gpioa, pin_reset, GPI_bus);
gpio_configure_pin(gpio_data, pin_wdata, GPI_bus);
gpio_configure_pin(gpio_data, pin_rdata, GPO_rdata);
#elif MCU == AT32F435
#define afio syscfg
gpio_set_af(gpioa, pin_reset, 1);
gpio_configure_pin(gpioa, pin_reset, AFI(PUPD_none));
gpio_set_af(gpio_data, pin_wdata, 1);
gpio_configure_pin(gpio_data, pin_wdata, AFI(PUPD_none));
gpio_set_af(gpio_data, pin_rdata, 2);
gpio_configure_pin(gpio_data, pin_rdata, GPO_rdata);
dmamux1->cctrl[dma_wdata_ch-1] = DMAMUX_CCTRL_REQSEL(DMAMUX_REQ_TIM1_CH1);
dmamux1->cctrl[dma_rdata_ch-1] = DMAMUX_CCTRL_REQSEL(DMAMUX_REQ_TIM3_OVF);
#endif
/* PA1 (RESET) triggers IRQ via TIM2 Channel 2, since EXTI is used for
* WGATE on PB1. */
tim2->ccmr1 = TIM_CCMR1_CC2S(TIM_CCS_INPUT_TI1);
tim2->ccer = TIM_CCER_CC2E;
tim2->dier = TIM_DIER_CC2IE;
tim2->cr1 = TIM_CR1_CEN;
if (mcu_package == MCU_QFN32) {
pin_02 = 16 + 14; /* PA14 */
pin_26 = 16 + 13; /* PA13 */
pin_wgate = 1; /* PB1 */
}
gpio_configure_pin(gpioa, pin_motor, GPI_bus);
gpio_configure_pin(gpiob, pin_wgate, GPI_bus);
/* PA[15:14], PB[13:12], PC[11:10], PB[9:1], PA[0] */
afio->exticr[4-1] = 0x0011;
afio->exticr[3-1] = 0x2211;
afio->exticr[2-1] = 0x1111;
afio->exticr[1-1] = 0x1110;
exti->rtsr = 0xffff;
exti->ftsr = 0xffff;
exti->imr = m(pin_wgate) | m(pin_motor);
}
static void IRQ_WGATE(void)
{
/* If WRPROT line is asserted then we ignore WGATE. */
if (read_pin(wrprot))
return;
if (!(gpiob->idr & m(pin_wgate)) || read_pin(ready)) {
/* !WG || !/RY */
drive.index_suppressed = FALSE;
wdata_stop();
if (drive.index_suppressed && (window.state <= WIN_rdata_off)) {
window.paused = TRUE;
window.pause_pos = drive.restart_pos;
}
} else {
/* WG && /RY */
rdata_stop();
wdata_start();
}
}
static void IRQ_WGATE_rotary(void)
{
uint32_t rot_mask = board_rotary_exti_mask, pr = exti->pr;
/* Latch and clear PR[9:5] and PR[1]. */
exti->pr = pr & 0x03e2;
if (pr & m(pin_wgate))
IRQ_WGATE();
if (pr & rot_mask)
IRQ_rotary();
}
static bool_t qd_roland_mode(void)
{
if (board_jc_strapped())
return TRUE;
/* FF.CFG alternative to setting the physical JC strap. */
if (ff_cfg.interface == FINTF_IBMPC)
return TRUE;
return FALSE;
}
static void _IRQ_MOTOR_RESET_changed(unsigned int gpioa_idr)
{
const unsigned int mask = m(pin_reset) | m(pin_motor);
unsigned int off;
/* Motor is off if either /RESET low or /MOTOR high. */
off = gpioa_idr & mask;
off ^= m(pin_reset);
/* /RESET is forced by media removal. */
if (read_pin(media))
off |= m(pin_reset);
/* Some signal changed, so we lose the spun-up state immediately. */
motor.on = FALSE;
if (!off) {
/* 1 second to spin up the motor. */
timer_set(&motor.timer, time_now() + time_ms(1000));
} else {
/* Motor is spinning down, or off: Cancel the spin-up timer. */
timer_cancel(&motor.timer);
if (/* RESET immediately clears READY */
(off & m(pin_reset))
/* !MOTOR immediately clears READY iff Roland mode is selected */
|| ((off & m(pin_motor)) && qd_roland_mode())) {
write_pin(ready, HIGH);
}
}
}
static void IRQ_MOTOR_changed(void)
{
/* Clear MOTOR-changed flag. */
exti->pr = m(pin_motor);
_IRQ_MOTOR_RESET_changed(gpioa->idr);
}
static void IRQ_RESET_changed(void)
{
unsigned int gpioa_idr, gpioa_idr2 = gpioa->idr;
do {
/* Clear RESET-changed flag. */
(void)tim2->ccr2;
/* Execute MOTOR/RESET logic based on snapshotted pin state. */
gpioa_idr = gpioa_idr2;
_IRQ_MOTOR_RESET_changed(gpioa_idr);
/* Update the timer channel's edge detector to detect the next edge
* depending on snapshotted RESET pin state. */
if (gpioa_idr & m(pin_reset)) {
tim2->ccer |= TIM_CCER_CC2P; /* Falling edge */
} else {
tim2->ccer &= ~TIM_CCER_CC2P; /* Rising edge */
}
/* Now check if we raced a RESET edge. Loop if so. */
gpioa_idr2 = gpioa->idr;
} while ((gpioa_idr ^ gpioa_idr2) & m(pin_reset));
}
static void IRQ_rotary_changed(void)
{
/* Clear PR[15:10] */
exti->pr = 0xfc00;
IRQ_rotary();
}
uint32_t motor_chgrst_exti_mask;
void motor_chgrst_setup_exti(void)
{
/* Quick Disk does not have configurable MOTOR and CHGRST. */
}
/*
* Local variables:
* mode: C
* c-file-style: "Linux"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

View file

@ -13,25 +13,132 @@
#define gpio_spk gpioa
#define pin_spk 2
static struct timer pulse_timer;
static struct {
volatile enum { STATE_idle, STATE_active, STATE_masked } state;
time_t start;
struct timer timer;
} pulse;
static void pulse_timer_fn(void *unused)
{
gpio_write_pin(gpio_spk, pin_spk, FALSE);
switch (pulse.state) {
case STATE_idle:
break;
case STATE_active:
gpio_write_pin(gpio_spk, pin_spk, FALSE);
pulse.state = STATE_masked;
/* Mask to typical minimum floppy step cycle (3ms) less 10% */
timer_set(&pulse.timer, pulse.start + time_us(2700));
break;
case STATE_masked:
pulse.state = STATE_idle;
break;
}
}
void speaker_init(void)
{
pulse.state = STATE_idle;
gpio_configure_pin(gpio_spk, pin_spk, GPO_pushpull(_2MHz, FALSE));
timer_init(&pulse_timer, pulse_timer_fn, NULL);
timer_init(&pulse.timer, pulse_timer_fn, NULL);
}
void speaker_pulse(void)
{
unsigned int volume = ff_cfg.step_volume;
if (!volume)
time_t now;
if (!volume || (pulse.state != STATE_idle))
return;
gpio_write_pin(gpio_spk, pin_spk, TRUE);
timer_set(&pulse_timer, time_now() + volume*volume*3);
now = time_now();
pulse.state = STATE_active;
pulse.start = now;
timer_set(&pulse.timer, now + volume*volume*(TIME_MHZ/3));
}
static void speaker_hz(unsigned int hz, unsigned int ms)
{
unsigned int vol = (ff_cfg.notify_volume & NOTIFY_volume_mask) + 1;
unsigned int period = STK_MHZ * 1000000 / hz;
unsigned int period_on = period * vol * vol / (2*400);
unsigned int nr = hz * ms / 1000;
while (nr--) {
gpio_write_pin(gpio_spk, pin_spk, TRUE);
delay_ticks(period_on);
gpio_write_pin(gpio_spk, pin_spk, FALSE);
delay_ticks(period - period_on);
}
}
static void speaker_lock(void)
{
uint32_t oldpri;
oldpri = IRQ_save(TIMER_IRQ_PRI);
timer_cancel(&pulse.timer);
pulse.state = STATE_masked;
IRQ_restore(oldpri);
}
static void speaker_unlock(void)
{
pulse.state = STATE_idle;
}
static void speaker_notify_slot(unsigned int nr)
{
while (nr >= 5) {
speaker_hz(1500, 100);
nr -= 5;
if (nr != 0)
delay_ms(120);
}
while (nr != 0) {
speaker_hz(1500, 40);
nr -= 1;
if (nr != 0)
delay_ms(120);
}
}
void speaker_notify_insert(unsigned int slotnr)
{
if ((ff_cfg.notify_volume & NOTIFY_volume_mask) == 0)
return;
speaker_lock();
speaker_hz(880, 40); /* a5 */
delay_ms(20);
speaker_hz(784, 40); /* g5 */
delay_ms(20);
speaker_hz(1046, 60); /* c6 */
if (ff_cfg.notify_volume & NOTIFY_slotnr) {
delay_ms(300);
speaker_notify_slot(slotnr);
}
speaker_unlock();
}
void speaker_notify_eject(void)
{
if ((ff_cfg.notify_volume & NOTIFY_volume_mask) == 0)
return;
speaker_lock();
speaker_hz(932, 40); /* a#5 */
delay_ms(20);
speaker_hz(831, 40); /* g#5 */
delay_ms(20);
speaker_hz(659, 60); /* e5 */
speaker_unlock();
}
/*

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