Compare commits
178 commits
master
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc8941dbfc | ||
|
|
d285a7235b | ||
|
|
717bc47fd8 | ||
|
|
eec23727ea | ||
|
|
044d714b23 | ||
|
|
074760853b | ||
|
|
72594dbc88 | ||
|
|
28d1088b45 | ||
|
|
592cdef2fd | ||
|
|
a08b4e0590 | ||
|
|
0baf125f45 | ||
|
|
026b1dab0a | ||
|
|
d3bc831400 | ||
|
|
d2f1f65843 | ||
|
|
2288a4b78a | ||
|
|
feb0b883da | ||
|
|
fea12a55f4 | ||
|
|
912f7ef364 | ||
|
|
4b60840314 | ||
|
|
7e79be8a96 | ||
|
|
7a9831d78e | ||
|
|
ac92c03adc | ||
|
|
e604563923 | ||
|
|
3edd6373ed | ||
|
|
ed01de5aa4 | ||
|
|
55aa1f1984 | ||
|
|
89a245a78c | ||
|
|
9de359a072 | ||
|
|
ffc26a57b8 | ||
|
|
24e5d5550d | ||
|
|
47e3b55168 | ||
|
|
bcdf9fc9f5 | ||
|
|
0c15d01028 | ||
|
|
da026808dd | ||
|
|
18f1a27673 | ||
|
|
ab1f656dac | ||
|
|
752844b7f3 | ||
|
|
086b549dcf | ||
|
|
0ca4890d73 | ||
|
|
8561197372 | ||
|
|
2631e0b2cb | ||
|
|
f9e8023327 | ||
|
|
030d66ea9c | ||
|
|
3db5e96e76 | ||
|
|
eea5041696 | ||
|
|
eb7afca29d | ||
|
|
c5204442ae | ||
|
|
1bae3e5ad8 | ||
|
|
66581698f7 | ||
|
|
b23021407e | ||
|
|
1970a01cc9 | ||
|
|
c2885b87ba | ||
|
|
4f96058924 | ||
|
|
a4314235c3 | ||
|
|
6c0c75f532 | ||
|
|
a3b8867f8a | ||
|
|
5449d569d1 | ||
|
|
ed2d0a19e1 | ||
|
|
70a4da5195 | ||
|
|
6316dc919e | ||
|
|
1f16192233 | ||
|
|
07ff1d128d | ||
|
|
8716314389 | ||
|
|
dabcc7dd76 | ||
|
|
0c8b911ebd | ||
|
|
4104883bc0 | ||
|
|
620023061d | ||
|
|
8a0cc97055 | ||
|
|
2280bbc135 | ||
|
|
756eca230c | ||
|
|
1cc05cdd41 | ||
|
|
0f6fc95feb | ||
|
|
bb9bf6ae72 | ||
|
|
79606e074e | ||
|
|
de42323e96 | ||
|
|
7572434201 | ||
|
|
c7b7610be9 | ||
|
|
e41384d093 | ||
|
|
d8b9dafa44 | ||
|
|
9ddb1987c2 | ||
|
|
72854aa7e8 | ||
|
|
317bc9b4af | ||
|
|
1de6c27e95 | ||
|
|
70208c8690 | ||
|
|
a2d21e90ae | ||
|
|
a71f01be76 | ||
|
|
ffb3f495e4 | ||
|
|
c7bfa47438 | ||
|
|
c70708edc3 | ||
|
|
f10f52ded8 | ||
|
|
1eee35d0c8 | ||
|
|
89aa3e21ad | ||
|
|
8f55f5b908 | ||
|
|
9956cb22bb | ||
|
|
26bbe822db | ||
|
|
2e079dd556 | ||
|
|
caeff9a3cb | ||
|
|
fe15fc3b9a | ||
|
|
17f2248675 | ||
|
|
f8b4fe09e2 | ||
|
|
d36bde69fc | ||
|
|
1eeb28fa49 | ||
|
|
724f005094 | ||
|
|
e2fd4f9a76 | ||
|
|
0943eb2fdb | ||
|
|
0d5298934c | ||
|
|
709d77dfd9 | ||
|
|
c72aa5890a | ||
|
|
701ee97629 | ||
|
|
d5130f4e24 | ||
|
|
daba5056b7 | ||
|
|
45a0a5d27f | ||
|
|
bd1bcc586f | ||
|
|
b9846a9d6e | ||
|
|
ad529d89e9 | ||
|
|
730ffa5727 | ||
|
|
f0edf11072 | ||
|
|
0669a4ca26 | ||
|
|
68a9a676df | ||
|
|
767768039e | ||
|
|
9f850602f3 | ||
|
|
95b3c0147a | ||
|
|
9153da4cc0 | ||
|
|
3cedba05b3 | ||
|
|
78348d5b62 | ||
|
|
432c8ea3a4 | ||
|
|
af6860890e | ||
|
|
38fd802bdd | ||
|
|
991a7747e5 | ||
|
|
ef83f012ae | ||
|
|
139528629d | ||
|
|
c01e1a4b35 | ||
|
|
73fc71e3b2 | ||
|
|
a7eac70f39 | ||
|
|
3932716eee | ||
|
|
cc714564e8 | ||
|
|
b307c256cb | ||
|
|
b9af680328 | ||
|
|
f1fcc0c132 | ||
|
|
13e59f538b | ||
|
|
f61c9606bc | ||
|
|
62a122aeb1 | ||
|
|
457454db89 | ||
|
|
695f9202f8 | ||
|
|
e258c7075f | ||
|
|
5c71b087fd | ||
|
|
a8ae61f91b | ||
|
|
07d5b9eeed | ||
|
|
341aa90b39 | ||
|
|
835e5170c8 | ||
|
|
2399869a78 | ||
|
|
282c4aa787 | ||
|
|
dfe8d19825 | ||
|
|
47854b187c | ||
|
|
2e92b51e2f | ||
|
|
9d44d153d2 | ||
|
|
a2ce484e1d | ||
|
|
568f378347 | ||
|
|
1fbcc499b6 | ||
|
|
e60cfc8405 | ||
|
|
b531a0b885 | ||
|
|
a505765f66 | ||
|
|
943e4820a6 | ||
|
|
d8b0b2aa7b | ||
|
|
d2c19903b2 | ||
|
|
3ee01621e3 | ||
|
|
c4c14c58d4 | ||
|
|
bb8d812b1b | ||
|
|
1447a4b761 | ||
|
|
fef80998ee | ||
|
|
a73f4f0966 | ||
|
|
c5fa3294f9 | ||
|
|
01e760bb9e | ||
|
|
c511bd94a8 | ||
|
|
1937c174d7 | ||
|
|
8b64c2e27a | ||
|
|
53a46ebf7b | ||
|
|
fb97c5f3fe |
111 changed files with 7178 additions and 2036 deletions
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
|
|
@ -5,12 +5,12 @@ jobs:
|
|||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set environment variables
|
||||
id: vars
|
||||
run: |
|
||||
echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
|
||||
echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Dependency packages (apt)
|
||||
run: |
|
||||
|
|
@ -25,7 +25,7 @@ jobs:
|
|||
export VER=${{ steps.vars.outputs.sha_short }}
|
||||
make -j4 dist VER=$VER
|
||||
mkdir -p _cidist
|
||||
rm flashfloppy-$VER.zip
|
||||
mv out/flashfloppy-$VER .
|
||||
rm flashfloppy-$VER/RELEASE_NOTES
|
||||
git rev-parse HEAD >flashfloppy-$VER/COMMIT
|
||||
zip -r flashfloppy-$VER.zip flashfloppy-$VER
|
||||
|
|
@ -34,8 +34,8 @@ jobs:
|
|||
- name: Build debug dist
|
||||
run: |
|
||||
export VER=${{ steps.vars.outputs.sha_short }}-debug
|
||||
env debug=y make -j4 dist VER=$VER
|
||||
rm flashfloppy-$VER.zip
|
||||
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
|
||||
|
|
@ -43,7 +43,7 @@ jobs:
|
|||
mv flashfloppy-$VER.zip _cidist/
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: FlashFloppy.CI.${{ steps.vars.outputs.sha_short }}
|
||||
path: _cidist
|
||||
|
|
|
|||
31
.github/workflows/release.yml
vendored
31
.github/workflows/release.yml
vendored
|
|
@ -10,12 +10,12 @@ jobs:
|
|||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set environment variables
|
||||
id: vars
|
||||
run: |
|
||||
echo "::set-output name=ref::$(echo ${{ github.ref }} | sed -e's#.*/##')"
|
||||
echo "ver=$(echo ${{ github.ref }} | sed -e's#.*/v##')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Dependency packages (apt)
|
||||
run: |
|
||||
|
|
@ -27,28 +27,19 @@ jobs:
|
|||
|
||||
- name: Build release
|
||||
run: |
|
||||
export VER=${{ steps.vars.outputs.ref }}
|
||||
export VER=${{ steps.vars.outputs.ver }}
|
||||
make -j4 dist VER=$VER
|
||||
mv out/flashfloppy-$VER.zip .
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ steps.vars.outputs.ref }}
|
||||
body: "[**Release Notes:**](https://github.com/keirf/FlashFloppy/blob/master/RELEASE_NOTES)"
|
||||
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
|
||||
|
||||
- name: Upload Release Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: flashfloppy-${{ steps.vars.outputs.ref }}.zip
|
||||
asset_name: flashfloppy-${{ steps.vars.outputs.ref }}.zip
|
||||
asset_content_type: application/zip
|
||||
artifacts: flashfloppy-${{ steps.vars.outputs.ver }}.zip
|
||||
artifactContentType: application/zip
|
||||
|
|
|
|||
14
.gitignore
vendored
14
.gitignore
vendored
|
|
@ -1,15 +1,5 @@
|
|||
*.[oa]
|
||||
.*.d
|
||||
*~
|
||||
*.ld
|
||||
*.elf
|
||||
*.bin
|
||||
*.hex
|
||||
*.orig
|
||||
*.rej
|
||||
*.rld
|
||||
*.upd
|
||||
*.dfu
|
||||
/flashfloppy-*
|
||||
/index.html
|
||||
/HxC_Compat*
|
||||
/out
|
||||
/ext
|
||||
|
|
|
|||
213
Makefile
213
Makefile
|
|
@ -1,113 +1,142 @@
|
|||
|
||||
export FW_VER := 3.23
|
||||
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 := 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
|
||||
mkdir -p flashfloppy-$(VER)/alt/quickdisk/logfile
|
||||
$(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
|
||||
$(MAKE) debug=n logfile=y -f $(ROOT)/Rules.mk upd
|
||||
mv FF.upd flashfloppy-$(VER)/alt/logfile/FF_Gotek-Logfile-$(VER).upd
|
||||
$(MAKE) clean
|
||||
$(MAKE) quickdisk=y -f $(ROOT)/Rules.mk upd
|
||||
mv FF.upd flashfloppy-$(VER)/alt/quickdisk/FF_Gotek-QuickDisk-$(VER).upd
|
||||
$(MAKE) clean
|
||||
$(MAKE) quickdisk=y debug=n logfile=y -f $(ROOT)/Rules.mk upd
|
||||
mv FF.upd flashfloppy-$(VER)/alt/quickdisk/logfile/FF_Gotek-QuickDisk-Logfile-$(VER).upd
|
||||
python3 scripts/mk_qd.py --window=6.5 flashfloppy-$(VER)/alt/quickdisk/Blank.qd
|
||||
$(MAKE) clean
|
||||
cp -a COPYING flashfloppy-$(VER)/
|
||||
cp -a README.md flashfloppy-$(VER)/
|
||||
cp -a RELEASE_NOTES flashfloppy-$(VER)/
|
||||
cp -a examples flashfloppy-$(VER)/
|
||||
[ -e HxC_Compat_Mode-$(HXC_FF_VER).zip ] || \
|
||||
wget -q --show-progress $(HXC_FF_URL)/$(HXC_FF_VER)/HxC_Compat_Mode-$(HXC_FF_VER).zip
|
||||
rm -rf index.html
|
||||
unzip -q HxC_Compat_Mode-$(HXC_FF_VER).zip
|
||||
mv HxC_Compat_Mode flashfloppy-$(VER)
|
||||
mkdir -p flashfloppy-$(VER)/scripts
|
||||
cp -a scripts/edsk* flashfloppy-$(VER)/scripts/
|
||||
cp -a scripts/mk_hfe.py flashfloppy-$(VER)/scripts/
|
||||
zip -r flashfloppy-$(VER).zip flashfloppy-$(VER)
|
||||
_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
|
||||
$(MAKE) bootloader=y logfile=n -C bootloader \
|
||||
-f $(ROOT)/Rules.mk \
|
||||
Bootloader.elf Bootloader.bin Bootloader.hex
|
||||
$(MAKE) logfile=n -C bl_update -f $(ROOT)/Rules.mk \
|
||||
BL_Update.elf BL_Update.bin BL_Update.hex
|
||||
$(MAKE) logfile=n -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)/
|
||||
[ -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
|
||||
|
||||
ocd: gotek
|
||||
python3 scripts/openocd/flash.py `pwd`/FF_Gotek-$(VER).hex
|
||||
ocd: FORCE all
|
||||
$(PYTHON) scripts/openocd/flash.py $(T)
|
||||
|
||||
flash: gotek
|
||||
sudo stm32flash -b $(BAUD) -w FF_Gotek-$(VER).hex $(DEV)
|
||||
flash: FORCE all
|
||||
$(SUDO) $(STM32FLASH) -b $(BAUD) -w $(T) $(DEV)
|
||||
|
||||
start:
|
||||
sudo stm32flash -b $(BAUD) -g 0 $(DEV)
|
||||
start: FORCE
|
||||
$(SUDO) $(STM32FLASH) -b $(BAUD) -g 0 $(DEV)
|
||||
|
||||
serial:
|
||||
sudo miniterm.py $(DEV) 3000000
|
||||
serial: FORCE
|
||||
$(SUDO) miniterm.py $(DEV) 3000000
|
||||
|
|
|
|||
75
README
Normal file
75
README
Normal 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.
|
||||
32
README.md
32
README.md
|
|
@ -6,6 +6,8 @@
|
|||
![Downloads Badge][downloads-badge]
|
||||
![Version Badge][version-badge]
|
||||
|
||||
<img src="https://raw.githubusercontent.com/wiki/keirf/flashfloppy/assets/banner.jpg">
|
||||
|
||||
**FlashFloppy** is a floppy-drive emulator for the ubiquitous
|
||||
[**Gotek**][Gotek-Compatibility] hardware. Connect to retro machines just
|
||||
like a real floppy drive but use disk images on a modern USB stick!
|
||||
|
|
@ -24,16 +26,7 @@ like a real floppy drive but use disk images on a modern USB stick!
|
|||
- [**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. Although it is [Free Software](Redistribution) and
|
||||
a pet project of mine, beer & coffee tokens will fuel me in the
|
||||
push onwards and upwards!
|
||||
|
||||
For further information please see the [**Donations page**][Donations].
|
||||
- [**Read the GitHub Wiki**](https://github.com/keirf/flashfloppy/wiki)
|
||||
|
||||
## Redistribution
|
||||
|
||||
|
|
@ -51,14 +44,13 @@ file on your selling page. For example:
|
|||
- FlashFloppy is free software. For more information see the
|
||||
[license](COPYING).
|
||||
|
||||
[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
|
||||
[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
|
||||
[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
|
||||
|
|
|
|||
|
|
@ -3,6 +3,43 @@
|
|||
** Keir Fraser <keir.xen@gmail.com>
|
||||
************************************
|
||||
|
||||
** v4.7a - 4 December 2022
|
||||
- Contains fixes up to v3.38 from v3 stable release series
|
||||
|
||||
** v4.6a - 29 July 2022
|
||||
- AUTOBOOT / HxC Mode: Fix "Error ... LBA Change test failed!"
|
||||
|
||||
** v4.5a - 29 July 2022
|
||||
- Contains fixes up to v3.34 from v3 stable release series
|
||||
- Supports AT32F435 MCU and SFRKC430.AT4.35 PCB
|
||||
|
||||
** v4.4a - 11 January 2022
|
||||
- DSK: Async I/O fixes
|
||||
- IMG.CFG: New option step= allows to specify double-step operation
|
||||
- FF.CFG: New option max-cyl= allows limiting head-step range
|
||||
|
||||
** v4.3a - 14 December 2021
|
||||
- QuickDisk: Ported to new async I/O framework
|
||||
- Various I/O bug fixes
|
||||
|
||||
** v4.2a - 13 October 2021
|
||||
- DSK: Ported to new async I/O framework
|
||||
- ringio: A few bug fixes
|
||||
|
||||
** v4.1a - 2 October 2021
|
||||
- Contains fixes/features up to v3.29 from v3 stable release series
|
||||
- Do not use index_suppressed if starting rd DMA is slow
|
||||
- ringio: Do not lose write for unaligned wd_prod
|
||||
- hfe: Protect HFEv3 against a broken run of 1s
|
||||
|
||||
** v4.0a - 10 August 2021
|
||||
- Contains fixes up to v3.28 from v3 stable release series
|
||||
- New asynchonous I/O handling (Eric Anderson / ejona86)
|
||||
- Smarter prefetching of cylinder data
|
||||
- Smarter writeback of modified tracks/sectors
|
||||
- More accurate emulation
|
||||
- Hard sector HFE image support (Eric Anderson / ajona86)
|
||||
|
||||
** v3.23 - 31 December 2020
|
||||
- OLED/LCD: Fix missing folder name display row when inserting USB drive
|
||||
- IMG.CFG: New examples for Roland, Sinclair QL, Kaypro
|
||||
|
|
@ -134,7 +171,7 @@
|
|||
- Extra logging in FFLOG.TXT for debug purposes
|
||||
- Amiga AutoSwap new title: Gobliiiins
|
||||
- Thanks to Arkadiusz Makarenko!
|
||||
- https://github.com/keirf/FF_AutoSwap/wiki/Downloads
|
||||
- https://github.com/keirf/flashfloppy-autoswap/wiki/Downloads
|
||||
|
||||
** v3.6a - 13 October 2019
|
||||
- Quick Disk Initial Release
|
||||
|
|
@ -194,7 +231,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
|
||||
|
|
@ -298,7 +335,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
|
||||
|
|
|
|||
61
Rules.mk
61
Rules.mk
|
|
@ -13,25 +13,38 @@ 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 += -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)
|
||||
override logfile=n
|
||||
endif
|
||||
# Debug builds don't fit in available Flash. FIXME for main firmware.
|
||||
override debug=n
|
||||
override logfile=n
|
||||
|
||||
## AT32F435
|
||||
else ifeq ($(mcu),at32f435)
|
||||
FLAGS += -mcpu=cortex-m4 -DAT32F435=4 -DMCU=4
|
||||
endif
|
||||
|
||||
ifneq ($(debug),y)
|
||||
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
|
||||
floppy=n
|
||||
else
|
||||
floppy=y
|
||||
endif
|
||||
|
||||
FLAGS += -MMD -MF .$(@F).d
|
||||
|
|
@ -43,12 +56,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
|
||||
|
|
@ -62,21 +74,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 $@
|
||||
|
|
@ -85,23 +90,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)
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
#include "../src/FlashFloppy.ld.S"
|
||||
|
|
@ -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)\""
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
RPATH = ../../src/display
|
||||
|
||||
OBJS += display.o
|
||||
OBJS += lcd.o
|
||||
OBJS += oled_font_6x13.o
|
||||
OBJS += led_7seg.o
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
RPATH = ../../src/gotek
|
||||
|
||||
OBJS += board.o
|
||||
|
|
@ -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"
|
||||
|
|
@ -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)\""
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
RPATH = ../../src/display
|
||||
|
||||
OBJS += display.o
|
||||
OBJS += lcd.o
|
||||
OBJS += oled_font_6x13.o
|
||||
OBJS += led_7seg.o
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
RPATH = ../../src/fatfs
|
||||
|
||||
OBJS += ff.o
|
||||
OBJS += ffunicode.o
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
RPATH = ../../src/gotek
|
||||
|
||||
OBJS += board.o
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -59,6 +59,11 @@ 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
|
||||
|
|
@ -194,10 +199,12 @@ indexed-prefix = "DSKA"
|
|||
# lcd-CCxRR: CCxRR backlit LCD with I2C backpack (16<=CC<=40, 02<=RR<=04)
|
||||
# oled-128xNN: 128xNN I2C OLED (NN = 32 | 64)
|
||||
# -rotate: OLED view is rotated 180 degrees
|
||||
# -hflip: OLED view is flipped horizontally
|
||||
# -narrow[er]: OLED view is restricted to Gotek display cutout
|
||||
# (-narrow: 18 chars; -narrower: 16 chars)
|
||||
# -inverse: Inverse/reverse video (black text on white background)
|
||||
# -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
|
||||
|
||||
|
|
@ -212,7 +219,7 @@ oled-font = 6x13
|
|||
# Values: 0 <= N <= 255
|
||||
oled-contrast = 143
|
||||
|
||||
# Text height and arrangement on LCD/OLED
|
||||
# Text height and arrangement on LCD/OLED and on OSD, respectively.
|
||||
# 'default', or a comma-separated list (one entry per LCD/OLED row, top down).
|
||||
# Each list item is a digit plus optional height specifier: <content-row>[d]
|
||||
# content-row: '0-3' = specified content row, '7' = blank
|
||||
|
|
@ -224,6 +231,11 @@ oled-contrast = 143
|
|||
# 'default' depends on display, eg.: oled-128x32='0,1' ; oled-128x64='3,0d,1'
|
||||
# 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
|
||||
|
|
@ -261,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"
|
||||
|
|
|
|||
16
examples/Host/Dynacord/IMG.CFG
Normal file
16
examples/Host/Dynacord/IMG.CFG
Normal 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)
|
||||
39
examples/Host/Ensoniq/IMG.CFG
Normal file
39
examples/Host/Ensoniq/IMG.CFG
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# IMG.CFG for Ensoniq hosts
|
||||
|
||||
# 800kB and 1.6MB image types are also handled by FF.CFG: host=ensoniq
|
||||
|
||||
# NOTE: The tags match on filesize alone. If you wish to define an explicit
|
||||
# tagname match, you can for example add 'ensoniq' to the square-bracketed tags
|
||||
# to limit matches to filenames of the form *.ensoniq.{img,ima,dsk}
|
||||
|
||||
# Ensoniq 800kB DSDD
|
||||
# Also handled by FF.CFG: host=ensoniq
|
||||
[::819200]
|
||||
cyls = 80
|
||||
heads = 2
|
||||
secs = 10
|
||||
bps = 512
|
||||
gap3 = 30
|
||||
id = 0
|
||||
rate = 250
|
||||
|
||||
# Ensoniq 1.6MB DSHD
|
||||
# Also handled by FF.CFG: host=ensoniq
|
||||
[::1638400]
|
||||
cyls = 80
|
||||
heads = 2
|
||||
secs = 20
|
||||
bps = 512
|
||||
gap3 = 40
|
||||
id = 0
|
||||
rate = 500
|
||||
|
||||
# Ensoniq Mirage 440kB SSDD
|
||||
# Mixed sector sizes *NOT* handled by FF.CFG: host=ensonize
|
||||
[::450560]
|
||||
cyls = 80
|
||||
heads = 1
|
||||
secs = 6
|
||||
bps = 1024,1024,1024,1024,1024,512
|
||||
id = 0
|
||||
rate = 250
|
||||
|
|
@ -11,7 +11,7 @@ cyls = 40
|
|||
heads = 1
|
||||
secs = 10
|
||||
bps = 256
|
||||
skew = 2
|
||||
cskew = 2
|
||||
mode = fm
|
||||
iam = no
|
||||
|
||||
|
|
@ -35,6 +35,6 @@ cyls = 40
|
|||
heads = 2
|
||||
secs = 5
|
||||
bps = 1024
|
||||
skew = 2
|
||||
cskew = 2
|
||||
mode = mfm
|
||||
iam = yes
|
||||
|
|
|
|||
|
|
@ -37,3 +37,13 @@ tracks = 0-79 # This line can be adjusted
|
|||
interleave = 2
|
||||
hskew = 1
|
||||
cskew = 2
|
||||
|
||||
# Roland 1.44MB format may apply sector skew.
|
||||
# This is as seen on a Roland MT-200.
|
||||
[::1474560]
|
||||
cyls = 80
|
||||
heads = 2
|
||||
secs = 18
|
||||
bps = 512
|
||||
hskew = 3
|
||||
cskew = 6
|
||||
|
|
|
|||
14
examples/Host/Sequential_Circuits/IMG.CFG
Normal file
14
examples/Host/Sequential_Circuits/IMG.CFG
Normal 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
|
||||
|
|
@ -34,6 +34,9 @@ cyls = 80
|
|||
# Mandatory: Number of heads (1-2).
|
||||
heads = 2
|
||||
|
||||
# Number of drive-head steps between cylinders. Default is 1.
|
||||
# step = 1
|
||||
|
||||
# Image file track layout. Default is "interleaved".
|
||||
# Comma-separated values:
|
||||
# sequential: Sequential cylinder ordering: all side 0, then side 1.
|
||||
|
|
@ -66,6 +69,15 @@ secs = 9
|
|||
# Mandatory if @secs is non-zero.
|
||||
bps = 512
|
||||
|
||||
# Alternative form in which bytes per sector is specified per sector.
|
||||
# The list is comma separated; no white space allowed.
|
||||
# bps = 512,512,512,512,512,512,512,512,256
|
||||
|
||||
# Bytes per sector within the IMG file. Smaller sectors will be padded.
|
||||
# The default is 0: Sectors occupy precisely their data size; No padding.
|
||||
# Supported values: 0, 128, 256, 512, 1024, 2048, 4096, 8192.
|
||||
# img_bps = 0
|
||||
|
||||
# ID of first sector on each track (0-255). Default is 1.
|
||||
# Numbers may be expressed in hexadecimal with 0x prefix (eg. 0xab).
|
||||
# id = 1
|
||||
|
|
@ -89,6 +101,10 @@ bps = 512
|
|||
# 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
|
||||
|
|
|
|||
35
inc/cache.h
35
inc/cache.h
|
|
@ -9,14 +9,30 @@
|
|||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
#if !defined(BOOTLOADER)
|
||||
|
||||
/* Use memory range (@start,@end) to cache data items of size @item_sz. */
|
||||
struct cache *cache_init(void *start, void *end, unsigned int item_sz);
|
||||
/* Use memory range (@start,@end) to cache data items of size @item_sz.
|
||||
* For non-NULL return, @entry_cnt will be set to number of entries available.
|
||||
*/
|
||||
struct cache *cache_init(void *start, void *end, unsigned int item_sz,
|
||||
unsigned int *entry_cnt);
|
||||
|
||||
/* Look up item @id in the cache. Return a pointer to cached data, or NULL. */
|
||||
const void *cache_lookup(struct cache *c, uint32_t id);
|
||||
|
||||
/* Look up item @id in the cache. Return a pointer to cached data, or NULL. */
|
||||
void *cache_lookup_mutable(struct cache *c, uint32_t id);
|
||||
|
||||
/* Returns the item id and cached data of the entry that might be evicted by
|
||||
* the next cache_update. Returns NULL if no entry might be evicted. */
|
||||
void *cache_lru_mutable(struct cache *c, uint32_t *id);
|
||||
|
||||
/* Returns the item id and cache data of the next entry that might be evicted
|
||||
* after @ent. Returns NULL if @ent is the most recent entry. */
|
||||
void *cache_lru_next_mutable(struct cache *c, const void* ent, uint32_t *id);
|
||||
|
||||
/* Returns the item id and cached data of the LRU entry, even if it is not
|
||||
* nearing eviction. Returns NULL if no entries are in the cache. */
|
||||
void *cache_lru_search_mutable(struct cache *c, uint32_t *id);
|
||||
|
||||
/* Update item @id with data @dat. Inserts the item if not present.*/
|
||||
void cache_update(struct cache *c, uint32_t id, const void *dat);
|
||||
|
||||
|
|
@ -24,14 +40,9 @@ void cache_update(struct cache *c, uint32_t id, const void *dat);
|
|||
void cache_update_N(struct cache *c, uint32_t id,
|
||||
const void *dat, unsigned int N);
|
||||
|
||||
#else
|
||||
|
||||
#define cache_init(a,b,c) NULL
|
||||
#define cache_lookup(a,b) NULL
|
||||
#define cache_update(a,b,c) ((void)0)
|
||||
#define cache_update_N(a,b,c,d) ((void)0)
|
||||
|
||||
#endif
|
||||
/* Update item @id using returned pointer to item. Creates an uninitialized
|
||||
* item if not present, and sets @created to true. */
|
||||
void *cache_update_mutable(struct cache *c, uint32_t id, bool_t *created);
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ struct packed ff_cfg {
|
|||
#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)
|
||||
|
|
@ -162,6 +164,12 @@ struct packed ff_cfg {
|
|||
#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;
|
||||
|
|
|
|||
16
inc/decls.h
16
inc/decls.h
|
|
@ -15,8 +15,17 @@
|
|||
#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"
|
||||
|
|
@ -29,7 +38,10 @@
|
|||
#include "cancellation.h"
|
||||
#include "spi.h"
|
||||
#include "timer.h"
|
||||
#include "thread.h"
|
||||
#include "file_cache.h"
|
||||
#include "fs.h"
|
||||
#include "fs_async.h"
|
||||
#include "floppy.h"
|
||||
#include "volume.h"
|
||||
#include "config.h"
|
||||
|
|
|
|||
74
inc/file_cache.h
Normal file
74
inc/file_cache.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* file_cache.h
|
||||
*
|
||||
* Caching I/O for a single file.
|
||||
*
|
||||
* Written & released by Keir Fraser <keir.xen@gmail.com> and Eric Anderson
|
||||
* <ejona86@gmail.com>
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
/* The cache is a write-back cache and uses an I/O scheduler to schedule reads
|
||||
* and writes, with preference given to reads. Cache tracking is per-sector of
|
||||
* 512 bytes.
|
||||
*
|
||||
* The cache tries to batch reads and writes into batches of maximum size
|
||||
* 'batch_secs'. The file is split into aligned groups of batch_secs, and
|
||||
* batching cannot cross the boundary of groups. A read batch starts from the
|
||||
* sector requested and ends at the first already-read sector or end of the
|
||||
* group. Writes are not delayed to form a batch, but batches form if there are
|
||||
* delays due to reads or slow writes.
|
||||
*
|
||||
* In addition to batch reads, readahead can be enabled via
|
||||
* file_cache_readahead(). Reads and writes within the provided region will
|
||||
* cause the scheduler to read additional sectors, wrapping around when getting
|
||||
* to the end of the region.
|
||||
*/
|
||||
|
||||
struct file_cache;
|
||||
|
||||
struct file_cache *file_cache_init(FIL *fp, uint8_t batch_secs,
|
||||
void *start, void *end);
|
||||
/* Waits until written data is flushed and synced to storage. */
|
||||
void file_cache_sync_wait(struct file_cache *fcache);
|
||||
/* Stops scheduling I/O and waits for outstanding I/O to complete. To ensure
|
||||
* data is not lost, use file_cache_sync_wait() first. */
|
||||
void file_cache_shutdown(struct file_cache *fcache);
|
||||
/* Run the I/O scheduler without requesting new I/O. Necessary for periods
|
||||
* without I/O operations for writing and readahead. */
|
||||
void file_cache_progress(struct file_cache *fcache);
|
||||
/* Limit I/O operation size potentially below that of batch_sec. 0 disables the
|
||||
* limit. */
|
||||
void file_cache_io_limit(struct file_cache *fcache, uint8_t io_max);
|
||||
/* Enable readahead for I/O ops within the specified region. @prio bytes are
|
||||
* higher priority than writes. */
|
||||
void file_cache_readahead(
|
||||
struct file_cache *fcache, FSIZE_t ofs, UINT btr, UINT prio);
|
||||
|
||||
/* Read within a sector. Will block until data is read. */
|
||||
void file_cache_read(struct file_cache *fcache, void *buf, FSIZE_t ofs,
|
||||
UINT btr);
|
||||
/* Read within a sector. If FALSE, try again later. */
|
||||
bool_t file_cache_try_read(struct file_cache *fcache, void *buf, FSIZE_t ofs,
|
||||
UINT btr);
|
||||
/* Read 512 bytes at sector-aligned @ofs. Returns NULL if read is not yet
|
||||
* available. */
|
||||
const void *file_cache_peek_read(struct file_cache *fcache, FSIZE_t ofs);
|
||||
|
||||
/* Write within a sector. May block waiting on cache space, or if a partial
|
||||
* sector is being written and the sector data is not already cached. */
|
||||
void file_cache_write(struct file_cache *fcache, const void *buf,
|
||||
FSIZE_t ofs, UINT btw);
|
||||
/* Write within a sector. If FALSE, try again later. */
|
||||
bool_t file_cache_try_write(struct file_cache *fcache, const void *buf,
|
||||
FSIZE_t ofs, UINT btw);
|
||||
/* Read 512 bytes at sector-aligned @ofs . Returns NULL if
|
||||
* the write is not yet possible. If the return is non-NULL, data written to
|
||||
* the buffer is observed by the next write or file_cache_sync(). Reads are
|
||||
* not permitted until the written data is observed. */
|
||||
void *file_cache_peek_write(struct file_cache *fcache, FSIZE_t ofs);
|
||||
|
||||
/* Flush filesystem cached data for file. Does not wait for it to complete. */
|
||||
void file_cache_sync(struct file_cache *fcache);
|
||||
101
inc/floppy.h
101
inc/floppy.h
|
|
@ -9,6 +9,13 @@
|
|||
* 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
|
||||
|
|
@ -33,45 +40,39 @@
|
|||
|
||||
#define verbose_image_log FALSE
|
||||
|
||||
struct image_buf {
|
||||
void *p;
|
||||
uint32_t len;
|
||||
uint32_t prod, cons;
|
||||
};
|
||||
|
||||
struct adf_image {
|
||||
struct file_cache *fcache;
|
||||
uint32_t trk_off;
|
||||
uint32_t sec_idx;
|
||||
int32_t decode_pos;
|
||||
uint32_t pre_idx_gap_bc;
|
||||
uint32_t nr_secs;
|
||||
uint32_t written_secs;
|
||||
uint16_t trash_bc; /* Number of bitcells to throw away. */
|
||||
uint8_t sec_map[2][22];
|
||||
};
|
||||
|
||||
struct hfe_image {
|
||||
struct file_cache *fcache;
|
||||
uint16_t tlut_base;
|
||||
uint16_t trk_off;
|
||||
uint16_t trk_pos, trk_len;
|
||||
bool_t is_v3, double_step;
|
||||
uint8_t batch_secs;
|
||||
struct {
|
||||
uint16_t start;
|
||||
bool_t wrapped;
|
||||
} write;
|
||||
struct {
|
||||
uint16_t off, len;
|
||||
bool_t dirty;
|
||||
} write_batch;
|
||||
bool_t is_v3, double_step, fresh_seek;
|
||||
uint8_t next_index_pulses_pos;
|
||||
};
|
||||
|
||||
struct qd_image {
|
||||
struct file_cache *fcache;
|
||||
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 {
|
||||
|
|
@ -84,6 +85,7 @@ struct raw_trk {
|
|||
uint16_t sec_off;
|
||||
uint16_t data_rate;
|
||||
uint16_t rpm;
|
||||
uint16_t img_bps; /* could squeeze this field into uint8_t or bitfield */
|
||||
int16_t gap_2, gap_3, gap_4a;
|
||||
uint8_t interleave, cskew, hskew;
|
||||
uint8_t has_iam:1, is_fm:1, invert_data:1;
|
||||
|
|
@ -92,7 +94,11 @@ struct raw_trk {
|
|||
};
|
||||
|
||||
struct img_image {
|
||||
struct file_cache *fcache;
|
||||
uint32_t trk_off, base_off;
|
||||
/* Length on-disk that encompases all track data. May contain other data
|
||||
* (e.g., the other side of the cylinder). */
|
||||
uint32_t trk_len;
|
||||
uint16_t trk_sec, rd_sec_pos;
|
||||
int32_t decode_pos;
|
||||
uint16_t decode_data_pos, crc;
|
||||
|
|
@ -108,12 +114,14 @@ struct img_image {
|
|||
/* Delay start of track this many bitcells past index. */
|
||||
uint32_t track_delay_bc;
|
||||
uint16_t gap_4;
|
||||
uint16_t trash_bc; /* Number of bitcells to throw away. */
|
||||
uint32_t idx_sz, idam_sz;
|
||||
uint16_t dam_sz_pre, dam_sz_post;
|
||||
void *heap_bottom;
|
||||
};
|
||||
|
||||
struct dsk_image {
|
||||
struct file_cache *fcache;
|
||||
uint32_t trk_off;
|
||||
uint16_t trk_pos;
|
||||
uint16_t rd_sec_pos;
|
||||
|
|
@ -122,6 +130,7 @@ struct dsk_image {
|
|||
bool_t extended;
|
||||
int8_t write_sector;
|
||||
uint16_t gap4;
|
||||
uint16_t trash_bc; /* Number of bitcells to throw away. */
|
||||
uint32_t idx_sz, idam_sz;
|
||||
uint16_t dam_sz_pre, dam_sz_post;
|
||||
uint8_t rev;
|
||||
|
|
@ -132,12 +141,14 @@ struct directaccess {
|
|||
int32_t decode_pos;
|
||||
uint16_t trk_sec;
|
||||
uint16_t idx_sz, idam_sz, dam_sz;
|
||||
};
|
||||
|
||||
struct image_buf {
|
||||
void *p;
|
||||
uint32_t len;
|
||||
uint32_t prod, cons;
|
||||
uint16_t trash_bc; /* Number of bitcells to throw away. */
|
||||
uint8_t *rd_buf;
|
||||
FOP io_op;
|
||||
struct image_buf write_buffer;
|
||||
LBA_t *write_offsets; /* Disk offset of each 512 byte buffer segment */
|
||||
uint8_t write_cnt;
|
||||
uint8_t sync_state;
|
||||
bool_t lba_set;
|
||||
};
|
||||
|
||||
struct image_bufs {
|
||||
|
|
@ -151,6 +162,8 @@ struct image_bufs {
|
|||
struct image_buf read_data;
|
||||
};
|
||||
|
||||
#define MAX_CUSTOM_PULSES 34 /* 33+1 for minor track misalignment */
|
||||
|
||||
struct image {
|
||||
/* Handler for currently-selected type of disk image. */
|
||||
const struct image_handler *disk_handler;
|
||||
|
|
@ -163,6 +176,12 @@ struct image {
|
|||
|
||||
/* Info about image as a whole. */
|
||||
uint8_t nr_cyls, nr_sides;
|
||||
uint8_t step;
|
||||
uint8_t index_pulses_ver; /* Changed when pulses or length changed. */
|
||||
uint8_t index_pulses_len;
|
||||
/* Alternative index pulse locations, in ticks. When non-empty, disables
|
||||
* regular index pulse. */
|
||||
uint32_t index_pulses[MAX_CUSTOM_PULSES];
|
||||
|
||||
/* Data buffers. */
|
||||
struct image_bufs bufs;
|
||||
|
|
@ -177,7 +196,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' */
|
||||
|
|
@ -212,6 +231,7 @@ struct image_handler {
|
|||
bool_t (*read_track)(struct image *im);
|
||||
uint16_t (*rdata_flux)(struct image *im, uint16_t *tbuf, uint16_t nr);
|
||||
bool_t (*write_track)(struct image *im);
|
||||
void (*sync)(struct image *im);
|
||||
};
|
||||
|
||||
/* List of supported image types. */
|
||||
|
|
@ -224,19 +244,21 @@ extern const struct image_type {
|
|||
bool_t image_valid(FILINFO *fp);
|
||||
|
||||
/* Open specified image file on mass storage device. */
|
||||
void image_open(struct image *im, struct slot *slot, DWORD *cltbl);
|
||||
void image_open(struct image *im, struct slot *slot, DWORD *cltbl,
|
||||
bool_t da_mode);
|
||||
|
||||
/* Returns TRUE if opened in Direct Access mode. */
|
||||
bool_t image_in_da_mode(struct image *im);
|
||||
|
||||
/* Extend a trunated image file. */
|
||||
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.
|
||||
*
|
||||
* Returns TRUE if the config file needs to be re-read (exiting D-A mode). */
|
||||
bool_t image_setup_track(
|
||||
* interested in fetching data from a particular rotational position. */
|
||||
void image_setup_track(
|
||||
struct image *im, uint16_t track, uint32_t *start_pos);
|
||||
|
||||
/* Read track data into memory. Returns TRUE if any new data was read. */
|
||||
|
|
@ -250,7 +272,11 @@ uint16_t bc_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr);
|
|||
* was completed for the write at the tail of the pipeline. */
|
||||
bool_t image_write_track(struct image *im);
|
||||
|
||||
/* Rotational position of last-generated flux (SYSCLK ticks past index). */
|
||||
/* Sync track data from memory to mass storage. Only useful to call when normal
|
||||
* processing may be interrupted, like before floppy_cancel(). */
|
||||
void image_sync(struct image *im);
|
||||
|
||||
/* Rotational position of last-generated flux (SAMPLECLK ticks past index). */
|
||||
uint32_t image_ticks_since_index(struct image *im);
|
||||
|
||||
/* MFM conversion. */
|
||||
|
|
@ -271,6 +297,7 @@ uint16_t fm_sync(uint8_t dat, uint8_t clk);
|
|||
void floppy_init(void);
|
||||
bool_t floppy_ribbon_is_reversed(void);
|
||||
void floppy_insert(unsigned int unit, struct slot *slot);
|
||||
void floppy_sync(void);
|
||||
void floppy_cancel(void);
|
||||
bool_t floppy_handle(void); /* TRUE -> re-read config file */
|
||||
void floppy_set_cyl(uint8_t unit, uint8_t cyl);
|
||||
|
|
@ -279,11 +306,19 @@ struct track_info {
|
|||
};
|
||||
void floppy_get_track(struct track_info *ti);
|
||||
void floppy_set_fintf_mode(void);
|
||||
void floppy_set_max_cyl(void);
|
||||
static inline unsigned int im_nphys_cyls(struct image *im)
|
||||
{
|
||||
return min_t(unsigned int, im->nr_cyls * (im->step?:1), 255);
|
||||
}
|
||||
static inline bool_t in_da_mode(struct image *im, unsigned int cyl)
|
||||
{
|
||||
return cyl >= max_t(unsigned int, DA_FIRST_CYL, im->nr_cyls);
|
||||
return cyl >= max_t(unsigned int, DA_FIRST_CYL, im_nphys_cyls(im));
|
||||
}
|
||||
|
||||
extern uint32_t motor_chgrst_exti_mask;
|
||||
void motor_chgrst_setup_exti(void);
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
|
|
|
|||
46
inc/fs_async.h
Normal file
46
inc/fs_async.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* fs_async.h
|
||||
*
|
||||
* Non-blocking error-handling wrappers around FatFS.
|
||||
*
|
||||
* Written & released by Keir Fraser <keir.xen@gmail.com> and Eric Anderson
|
||||
* <ejona86@gmail.com>
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
#include "../src/fatfs/diskio.h"
|
||||
|
||||
typedef int FOP;
|
||||
|
||||
FOP F_lseek_async(FIL *fp, FSIZE_t ofs);
|
||||
FOP F_read_async(FIL *fp, void *buff, UINT btr, UINT *br);
|
||||
FOP F_write_async(FIL *fp, const void *buff, UINT btw, UINT *bw);
|
||||
FOP F_sync_async(FIL *fp);
|
||||
FOP disk_read_async(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count);
|
||||
FOP disk_write_async(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count);
|
||||
/* If res == NULL, then it will F_die() if result of ioctl != RES_OK. */
|
||||
FOP disk_ioctl_async(BYTE pdrv, BYTE cmd, void* buff, DRESULT *res);
|
||||
|
||||
/* Returns TRUE if oper has completed or is cancelled. */
|
||||
bool_t F_async_isdone(FOP oper);
|
||||
|
||||
/* Blocks until oper completes or is cancelled. */
|
||||
void F_async_wait(FOP oper);
|
||||
|
||||
/* Requests oper be cancelled. A cancellation may not take effect immediately.
|
||||
* Has no effect when called on a completed oper.
|
||||
*/
|
||||
void F_async_cancel(FOP oper);
|
||||
|
||||
/* Requests all operations to be cancelled. A cancellation may not take effect
|
||||
* immediately. */
|
||||
void F_async_cancel_all(void);
|
||||
|
||||
/* Return a completed or cancelled op. Can be used as a "fake" op that is safe
|
||||
* to wait or be cancelled. */
|
||||
FOP F_async_get_completed_op(void);
|
||||
|
||||
/* Executes async operations until none remain. */
|
||||
void F_async_drain(void);
|
||||
|
|
@ -74,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;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ static inline void list_remove(struct list_head *ent)
|
|||
ent->prev->next = ent->next;
|
||||
}
|
||||
|
||||
static inline bool_t list_is_empty(struct list_head *head)
|
||||
static inline bool_t list_is_empty(const struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
|
|
|||
34
inc/mcu/at32f415_regs.h
Normal file
34
inc/mcu/at32f415_regs.h
Normal 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
80
inc/mcu/at32f435.h
Normal 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
421
inc/mcu/at32f435_regs.h
Normal 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:
|
||||
*/
|
||||
|
|
@ -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:
|
||||
|
|
@ -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
71
inc/mcu/stm32f105.h
Normal 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
313
inc/mcu/stm32f105_regs.h
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* stm32f105_regs.h
|
||||
*
|
||||
* Core and peripheral register definitions.
|
||||
*
|
||||
* Written & released by Keir Fraser <keir.xen@gmail.com>
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
/* Power control */
|
||||
struct pwr {
|
||||
uint32_t cr; /* 00: Power control */
|
||||
uint32_t csr; /* 04: Power control/status */
|
||||
};
|
||||
|
||||
#define PWR_CR_DBP (1u<< 8)
|
||||
|
||||
#define PWR_BASE 0x40007000
|
||||
|
||||
/* Flash memory interface */
|
||||
struct flash {
|
||||
uint32_t acr; /* 00: Flash access control */
|
||||
uint32_t keyr; /* 04: FPEC key */
|
||||
uint32_t optkeyr; /* 08: Flash OPTKEY */
|
||||
uint32_t sr; /* 0C: Flash status */
|
||||
uint32_t cr; /* 10: Flash control */
|
||||
uint32_t ar; /* 14: Flash address */
|
||||
uint32_t rsvd; /* 18: - */
|
||||
uint32_t obr; /* 1C: Option byte */
|
||||
uint32_t wrpr; /* 20: Write protection */
|
||||
};
|
||||
|
||||
#define FLASH_ACR_PRFTBS (1u<< 5)
|
||||
#define FLASH_ACR_PRFTBE (1u<< 4)
|
||||
#define FLASH_ACR_HLFCYA (1u<< 3)
|
||||
#define FLASH_ACR_LATENCY(w) ((w)<<0) /* wait states */
|
||||
|
||||
#define FLASH_SR_EOP (1u<< 5)
|
||||
#define FLASH_SR_WRPRTERR (1u<< 4)
|
||||
#define FLASH_SR_PGERR (1u<< 2)
|
||||
#define FLASH_SR_BSY (1u<< 0)
|
||||
|
||||
#define FLASH_CR_EOPIE (1u<<12)
|
||||
#define FLASH_CR_ERRIE (1u<<10)
|
||||
#define FLASH_CR_OPTWRE (1u<< 9)
|
||||
#define FLASH_CR_LOCK (1u<< 7)
|
||||
#define FLASH_CR_STRT (1u<< 6)
|
||||
#define FLASH_CR_OPTER (1u<< 5)
|
||||
#define FLASH_CR_OPTPG (1u<< 4)
|
||||
#define FLASH_CR_MER (1u<< 2)
|
||||
#define FLASH_CR_PER (1u<< 1)
|
||||
#define FLASH_CR_PG (1u<< 0)
|
||||
|
||||
#define FLASH_BASE 0x40022000
|
||||
|
||||
/* Reset and clock control */
|
||||
struct rcc {
|
||||
uint32_t cr; /* 00: Clock control */
|
||||
uint32_t cfgr; /* 04: Clock configuration */
|
||||
uint32_t cir; /* 08: Clock interrupt */
|
||||
uint32_t apb2rstr; /* 0C: APB2 peripheral reset */
|
||||
uint32_t apb1rstr; /* 10: APB1 peripheral reset */
|
||||
uint32_t ahbenr; /* 14: AHB periphernal clock enable */
|
||||
uint32_t apb2enr; /* 18: APB2 peripheral clock enable */
|
||||
uint32_t apb1enr; /* 1C: APB1 peripheral clock enable */
|
||||
uint32_t bdcr; /* 20: Backup domain control */
|
||||
uint32_t csr; /* 24: Control/status */
|
||||
uint32_t ahbstr; /* 28: AHB peripheral clock reset */
|
||||
uint32_t cfgr2; /* 2C: Clock configuration 2 */
|
||||
};
|
||||
|
||||
#define RCC_CR_PLL3RDY (1u<<29)
|
||||
#define RCC_CR_PLL3ON (1u<<28)
|
||||
#define RCC_CR_PLL2RDY (1u<<27)
|
||||
#define RCC_CR_PLL2ON (1u<<26)
|
||||
#define RCC_CR_PLLRDY (1u<<25)
|
||||
#define RCC_CR_PLLON (1u<<24)
|
||||
#define RCC_CR_CSSON (1u<<19)
|
||||
#define RCC_CR_HSEBYP (1u<<18)
|
||||
#define RCC_CR_HSERDY (1u<<17)
|
||||
#define RCC_CR_HSEON (1u<<16)
|
||||
#define RCC_CR_HSIRDY (1u<<1)
|
||||
#define RCC_CR_HSION (1u<<0)
|
||||
|
||||
#define RCC_CFGR_PLLMUL(x) (((x)-2)<<18)
|
||||
#define RCC_CFGR_PLLXTPRE (1u<<17)
|
||||
#define RCC_CFGR_PLLSRC_HSI (0u<<16)
|
||||
#define RCC_CFGR_PLLSRC_PREDIV1 (1u<<16)
|
||||
#define RCC_CFGR_ADCPRE_DIV8 (3u<<14)
|
||||
#define RCC_CFGR_APB2PSC_2 (4u<<11)
|
||||
#define RCC_CFGR_APB1PSC_2 (4u<< 8)
|
||||
#define RCC_CFGR_SWS_HSI (0u<<2)
|
||||
#define RCC_CFGR_SWS_HSE (1u<<2)
|
||||
#define RCC_CFGR_SWS_PLL (2u<<2)
|
||||
#define RCC_CFGR_SWS_MASK (3u<<2)
|
||||
#define RCC_CFGR_SW_HSI (0u<<0)
|
||||
#define RCC_CFGR_SW_HSE (1u<<0)
|
||||
#define RCC_CFGR_SW_PLL (2u<<0)
|
||||
#define RCC_CFGR_SW_MASK (3u<<0)
|
||||
|
||||
#define RCC_AHBENR_ETHMACRXEN (1u<<16)
|
||||
#define RCC_AHBENR_ETHMACTXEN (1u<<15)
|
||||
#define RCC_AHBENR_ETHMACEN (1u<<14)
|
||||
#define RCC_AHBENR_OTGFSEN (1u<<12)
|
||||
#define RCC_AHBENR_CRCEN (1u<< 6)
|
||||
#define RCC_AHBENR_FLITFEN (1u<< 4)
|
||||
#define RCC_AHBENR_SRAMEN (1u<< 2)
|
||||
#define RCC_AHBENR_DMA2EN (1u<< 1)
|
||||
#define RCC_AHBENR_DMA1EN (1u<< 0)
|
||||
|
||||
#define RCC_APB1ENR_DACEN (1u<<29)
|
||||
#define RCC_APB1ENR_PWREN (1u<<28)
|
||||
#define RCC_APB1ENR_BKPEN (1u<<27)
|
||||
#define RCC_APB1ENR_CAN2EN (1u<<26)
|
||||
#define RCC_APB1ENR_CAN1EN (1u<<25)
|
||||
#define RCC_APB1ENR_I2C2EN (1u<<22)
|
||||
#define RCC_APB1ENR_I2C1EN (1u<<21)
|
||||
#define RCC_APB1ENR_USART5EN (1u<<20)
|
||||
#define RCC_APB1ENR_USART4EN (1u<<19)
|
||||
#define RCC_APB1ENR_USART3EN (1u<<18)
|
||||
#define RCC_APB1ENR_USART2EN (1u<<17)
|
||||
#define RCC_APB1ENR_SPI3EN (1u<<15)
|
||||
#define RCC_APB1ENR_SPI2EN (1u<<14)
|
||||
#define RCC_APB1ENR_WWDGEN (1u<<11)
|
||||
#define RCC_APB1ENR_TIM7EN (1u<< 5)
|
||||
#define RCC_APB1ENR_TIM6EN (1u<< 4)
|
||||
#define RCC_APB1ENR_TIM5EN (1u<< 3)
|
||||
#define RCC_APB1ENR_TIM4EN (1u<< 2)
|
||||
#define RCC_APB1ENR_TIM3EN (1u<< 1)
|
||||
#define RCC_APB1ENR_TIM2EN (1u<< 0)
|
||||
|
||||
#define RCC_APB2ENR_USART1EN (1u<<14)
|
||||
#define RCC_APB2ENR_SPI1EN (1u<<12)
|
||||
#define RCC_APB2ENR_TIM1EN (1u<<11)
|
||||
#define RCC_APB2ENR_ADC2EN (1u<<10)
|
||||
#define RCC_APB2ENR_ADC1EN (1u<< 9)
|
||||
#define RCC_APB2ENR_IOPFEN (1u<< 7)
|
||||
#define RCC_APB2ENR_IOPEEN (1u<< 6)
|
||||
#define RCC_APB2ENR_IOPDEN (1u<< 5)
|
||||
#define RCC_APB2ENR_IOPCEN (1u<< 4)
|
||||
#define RCC_APB2ENR_IOPBEN (1u<< 3)
|
||||
#define RCC_APB2ENR_IOPAEN (1u<< 2)
|
||||
#define RCC_APB2ENR_AFIOEN (1u<< 0)
|
||||
|
||||
#define RCC_BASE 0x40021000
|
||||
|
||||
/* General-purpose I/O */
|
||||
struct gpio {
|
||||
uint32_t crl; /* 00: Port configuration low */
|
||||
uint32_t crh; /* 04: Port configuration high */
|
||||
uint32_t idr; /* 08: Port input data */
|
||||
uint32_t odr; /* 0C: Port output data */
|
||||
uint32_t bsrr; /* 10: Port bit set/reset */
|
||||
uint32_t brr; /* 14: Port bit reset */
|
||||
uint32_t lckr; /* 18: Port configuration lock */
|
||||
};
|
||||
|
||||
#define _GPI_pulled(level) (0x8u|((level)<<4))
|
||||
#define GPI_analog 0x0u
|
||||
#define GPI_floating 0x4u
|
||||
#define GPI_pull_down _GPI_pulled(LOW)
|
||||
#define GPI_pull_up _GPI_pulled(HIGH)
|
||||
|
||||
#define GPO_pushpull(speed,level) (0x0u|(speed)|((level)<<4))
|
||||
#define GPO_opendrain(speed,level) (0x4u|(speed)|((level)<<4))
|
||||
#define AFO_pushpull(speed) (0x8u|(speed))
|
||||
#define _AFO_pushpull(speed,level) (0x8u|(speed)|((level)<<4))
|
||||
#define AFO_opendrain(speed) (0xcu|(speed))
|
||||
#define _2MHz 2
|
||||
#define _10MHz 1
|
||||
#define _50MHz 3
|
||||
#define LOW 0
|
||||
#define HIGH 1
|
||||
|
||||
#define GPIOA_BASE 0x40010800
|
||||
#define GPIOB_BASE 0x40010c00
|
||||
#define GPIOC_BASE 0x40011000
|
||||
#define GPIOD_BASE 0x40011400
|
||||
#define GPIOE_BASE 0x40011800
|
||||
#define GPIOF_BASE 0x40011c00
|
||||
#define GPIOG_BASE 0x40012000
|
||||
|
||||
/* Alternative-function I/O */
|
||||
struct afio {
|
||||
uint32_t evcr; /* 00: Event control */
|
||||
uint32_t mapr; /* 04: AF remap and debug I/O configuration */
|
||||
uint32_t exticr[4]; /* 08-14: External interrupt configuration #1-4 */
|
||||
uint32_t rsvd; /* 18: - */
|
||||
uint32_t mapr2; /* 1C: AF remap and debug I/O configuration #2 */
|
||||
};
|
||||
|
||||
#define AFIO_MAPR_SWJ_CFG_DISABLED (4u<<24)
|
||||
#define AFIO_MAPR_TIM4_REMAP_FULL (1u<<12)
|
||||
#define AFIO_MAPR_TIM3_REMAP_FULL (3u<<10)
|
||||
#define AFIO_MAPR_TIM3_REMAP_PARTIAL (2u<<10)
|
||||
#define AFIO_MAPR_TIM2_REMAP_FULL (3u<< 8)
|
||||
#define AFIO_MAPR_TIM2_REMAP_PARTIAL_1 (1u<< 8)
|
||||
#define AFIO_MAPR_TIM2_REMAP_PARTIAL_2 (2u<< 8)
|
||||
#define AFIO_MAPR_TIM1_REMAP_FULL (3u<< 6)
|
||||
#define AFIO_MAPR_TIM1_REMAP_PARTIAL (1u<< 6)
|
||||
#define AFIO_MAPR_USART3_REMAP_FULL (3u<< 4)
|
||||
#define AFIO_MAPR_USART3_REMAP_PARTIAL (1u<< 4)
|
||||
|
||||
#define AFIO_BASE 0x40010000
|
||||
|
||||
/* EXTI */
|
||||
#define EXTI_BASE 0x40010400
|
||||
|
||||
/* DMA */
|
||||
#define DMA1_CH1_IRQ 11
|
||||
#define DMA1_CH2_IRQ 12
|
||||
#define DMA1_CH3_IRQ 13
|
||||
#define DMA1_CH4_IRQ 14
|
||||
#define DMA1_CH5_IRQ 15
|
||||
#define DMA1_CH6_IRQ 16
|
||||
#define DMA1_CH7_IRQ 17
|
||||
|
||||
#define DMA1_BASE 0x40020000
|
||||
#define DMA2_BASE 0x40020400
|
||||
|
||||
/* Timer */
|
||||
#define TIM1_BASE 0x40012c00
|
||||
#define TIM2_BASE 0x40000000
|
||||
#define TIM3_BASE 0x40000400
|
||||
#define TIM4_BASE 0x40000800
|
||||
#define TIM5_BASE 0x40000c00
|
||||
#define TIM6_BASE 0x40001000
|
||||
#define TIM7_BASE 0x40001400
|
||||
|
||||
/* I2C */
|
||||
struct i2c {
|
||||
uint32_t cr1; /* 00: Control 1 */
|
||||
uint32_t cr2; /* 04: Control 2 */
|
||||
uint32_t oar1; /* 08: Own address 1 */
|
||||
uint32_t oar2; /* 0C: Own address 2 */
|
||||
uint32_t dr; /* 10: Data */
|
||||
uint32_t sr1; /* 14: Status 1 */
|
||||
uint32_t sr2; /* 18: Status 2 */
|
||||
uint32_t ccr; /* 1C: Clock control */
|
||||
uint32_t trise; /* 20: Rise time */
|
||||
};
|
||||
|
||||
#define I2C_CR1_SWRST (1u<<15)
|
||||
#define I2C_CR1_ALERT (1u<<13)
|
||||
#define I2C_CR1_PEC (1u<<12)
|
||||
#define I2C_CR1_POS (1u<<11)
|
||||
#define I2C_CR1_ACK (1u<<10)
|
||||
#define I2C_CR1_STOP (1u<< 9)
|
||||
#define I2C_CR1_START (1u<< 8)
|
||||
#define I2C_CR1_NOSTRETCH (1u<< 7)
|
||||
#define I2C_CR1_ENGC (1u<< 6)
|
||||
#define I2C_CR1_ENPEC (1u<< 5)
|
||||
#define I2C_CR1_ENARP (1u<< 4)
|
||||
#define I2C_CR1_SMBTYPE (1u<< 3)
|
||||
#define I2C_CR1_SMBUS (1u<< 1)
|
||||
#define I2C_CR1_PE (1u<< 0)
|
||||
|
||||
#define I2C_CR2_LAST (1u<<12)
|
||||
#define I2C_CR2_DMAEN (1u<<11)
|
||||
#define I2C_CR2_ITBUFEN (1u<<10)
|
||||
#define I2C_CR2_ITEVTEN (1u<< 9)
|
||||
#define I2C_CR2_ITERREN (1u<< 8)
|
||||
#define I2C_CR2_FREQ(x) (x)
|
||||
|
||||
#define I2C_SR1_SMBALERT (1u<<15)
|
||||
#define I2C_SR1_TIMEOUT (1u<<14)
|
||||
#define I2C_SR1_PECERR (1u<<12)
|
||||
#define I2C_SR1_OVR (1u<<11)
|
||||
#define I2C_SR1_AF (1u<<10)
|
||||
#define I2C_SR1_ARLO (1u<< 9)
|
||||
#define I2C_SR1_BERR (1u<< 8)
|
||||
#define I2C_SR1_ERRORS 0xdf00
|
||||
#define I2C_SR1_TXE (1u<< 7)
|
||||
#define I2C_SR1_RXNE (1u<< 6)
|
||||
#define I2C_SR1_STOPF (1u<< 4)
|
||||
#define I2C_SR1_ADD10 (1u<< 3)
|
||||
#define I2C_SR1_BTF (1u<< 2)
|
||||
#define I2C_SR1_ADDR (1u<< 1)
|
||||
#define I2C_SR1_SB (1u<< 0)
|
||||
#define I2C_SR1_EVENTS 0x001f
|
||||
|
||||
#define I2C_SR2_PEC(x) ((x)<<15)
|
||||
#define I2C_SR2_DUALF (1u<< 7)
|
||||
#define I2C_SR2_SMBHOST (1u<< 6)
|
||||
#define I2C_SR2_SMBDEFAULT (1u<< 5)
|
||||
#define I2C_SR2_GENCALL (1u<< 4)
|
||||
#define I2C_SR2_TRA (1u<< 2)
|
||||
#define I2C_SR2_BUSY (1u<< 1)
|
||||
#define I2C_SR2_MSL (1u<< 0)
|
||||
|
||||
#define I2C_CCR_FS (1u<<15)
|
||||
#define I2C_CCR_DUTY (1u<<14)
|
||||
#define I2C_CCR_CCR(x) (x)
|
||||
|
||||
#define I2C1_BASE 0x40005400
|
||||
#define I2C2_BASE 0x40005800
|
||||
|
||||
/* USART */
|
||||
#define USART1_BASE 0x40013800
|
||||
#define USART2_BASE 0x40004400
|
||||
#define USART3_BASE 0x40004800
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "Linux"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
35
inc/thread.h
Normal file
35
inc/thread.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* thread.h
|
||||
*
|
||||
* Cooperative multitasking.
|
||||
*
|
||||
* Written & released by Keir Fraser <keir.xen@gmail.com> and Eric Anderson
|
||||
* <ejona86@gmail.com>
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
struct thread {
|
||||
/* Internal bookkeeping */
|
||||
bool_t exited;
|
||||
};
|
||||
|
||||
/* Initialize a thread and queue it for execution. 'thread' must remain
|
||||
* allocated for the lifetime of the thread. */
|
||||
void thread_start(struct thread *thread, uint32_t *stack, void (*func)(void*), void* arg);
|
||||
|
||||
/* Yield execution to allow other threads to run. */
|
||||
void thread_yield(void);
|
||||
|
||||
/* Returns true if provided thread has exited. A thread cannot be joined
|
||||
* multiple times, unless it is started anew. */
|
||||
bool_t thread_tryjoin(struct thread *thread);
|
||||
|
||||
/* Continuously yields until provided thread has exited. A thread cannot be
|
||||
* joined multiple times, unless it is started anew. */
|
||||
void thread_join(struct thread *thread);
|
||||
|
||||
/* Reinitializes threading subsystem to its initial state, throwing away all
|
||||
* threads but the current. */
|
||||
void thread_reset(void);
|
||||
56
inc/types.h
Normal file
56
inc/types.h
Normal 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:
|
||||
*/
|
||||
52
inc/util.h
52
inc/util.h
|
|
@ -9,42 +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
|
||||
|
||||
#ifndef offsetof
|
||||
#define offsetof(a,b) __builtin_offsetof(a,b)
|
||||
#endif
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
#define min(x,y) ({ \
|
||||
const typeof(x) _x = (x); \
|
||||
const typeof(y) _y = (y); \
|
||||
(void) (&_x == &_y); \
|
||||
_x < _y ? _x : _y; })
|
||||
|
||||
#define max(x,y) ({ \
|
||||
const typeof(x) _x = (x); \
|
||||
const typeof(y) _y = (y); \
|
||||
(void) (&_x == &_y); \
|
||||
_x > _y ? _x : _y; })
|
||||
|
||||
#define min_t(type,x,y) \
|
||||
({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
|
||||
#define max_t(type,x,y) \
|
||||
({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
|
||||
#define range_t(type,x,a,b) min_t(type, max_t(type, x, a), b)
|
||||
|
||||
struct slot {
|
||||
char name[FF_MAX_LFN+1];
|
||||
char type[7];
|
||||
|
|
@ -55,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
|
||||
|
|
@ -154,6 +120,8 @@ extern uint8_t display_type;
|
|||
/* Speaker. */
|
||||
void speaker_init(void);
|
||||
void speaker_pulse(void);
|
||||
void speaker_notify_insert(unsigned int slotnr);
|
||||
void speaker_notify_eject(void);
|
||||
|
||||
/* Display: 3-digit 7-segment display */
|
||||
void led_7seg_init(void);
|
||||
|
|
@ -192,6 +160,7 @@ 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
|
||||
|
|
@ -199,6 +168,16 @@ 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[];
|
||||
|
|
@ -212,6 +191,7 @@ extern char _sbss[], _ebss[];
|
|||
|
||||
/* Stacks. */
|
||||
extern uint32_t _thread_stacktop[], _thread_stackbottom[];
|
||||
extern uint32_t _thread1_stacktop[], _thread1_stackbottom[];
|
||||
extern uint32_t _irq_stacktop[], _irq_stackbottom[];
|
||||
|
||||
/* Default exception handler. */
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ struct volume_ops {
|
|||
|
||||
bool_t volume_connected(void);
|
||||
bool_t volume_readonly(void);
|
||||
/* Returns TRUE if volume is in the middle of an operation that may
|
||||
* thread_yield(); FALSE if no I/O in progress. When TRUE, the thread will
|
||||
* yield as soon as calling this method would begin returning FALSE. */
|
||||
bool_t volume_interrupt(void);
|
||||
|
||||
void volume_cache_init(void *start, void *end);
|
||||
void volume_cache_destroy(void);
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
#include "../src/FlashFloppy.ld.S"
|
||||
|
|
@ -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)\""
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
RPATH = ../../src/display
|
||||
|
||||
OBJS += display.o
|
||||
OBJS += lcd.o
|
||||
OBJS += oled_font_6x13.o
|
||||
OBJS += led_7seg.o
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
RPATH = ../../src/gotek
|
||||
|
||||
OBJS += board.o
|
||||
OBJS += speaker.o
|
||||
89
scripts/check_hex.py
Normal file
89
scripts/check_hex.py
Normal 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)
|
||||
|
|
@ -46,7 +46,16 @@ def main(argv):
|
|||
else:
|
||||
opts += ['DISPLAY_' + x]
|
||||
val = '|'.join(opts)
|
||||
elif opt == "display-order":
|
||||
elif opt == "notify-volume":
|
||||
opts = []
|
||||
for x in val.split(","):
|
||||
vol = re.match("([0-9]+)", x)
|
||||
if vol:
|
||||
opts.append(vol.group(1))
|
||||
else:
|
||||
opts.append('NOTIFY_' + x)
|
||||
val = '|'.join(opts)
|
||||
elif opt == "display-order" or opt == "osd-display-order":
|
||||
if val == "default":
|
||||
val = "DORD_" + val
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -20,14 +20,22 @@ def main(argv):
|
|||
help="number of cylinders")
|
||||
parser.add_argument("--sides", type=int, default=2,
|
||||
help="number of sides")
|
||||
parser.add_argument("--hard-sectors", type=int, default=0,
|
||||
help="number of hard sectors")
|
||||
parser.add_argument("outfile", help="output filename")
|
||||
args = parser.parse_args(argv[1:])
|
||||
|
||||
v3 = args.hard_sectors != 0
|
||||
|
||||
bits = (args.rate * 1000 * 60) // args.rpm
|
||||
bits *= 2 # clock bits
|
||||
bits *= 2 # 2 sides
|
||||
bytes = (bits + 7) // 8 # convert to bytes, rounded up
|
||||
bytes = (bytes + 15) & ~15 # round up to 16-byte boundary
|
||||
bytes = (bits + 7) // 8 # convert to bytes, rounded up
|
||||
if not v3:
|
||||
bytes = (bytes + 15) & ~15 # round up to 16-byte boundary
|
||||
raw_bytes = bytes
|
||||
if args.hard_sectors:
|
||||
bytes += (args.hard_sectors + 1) * 2 # 2 sides
|
||||
blocks = (bytes + 511) // 512 # convert to 512-byte blocks, rounded up
|
||||
|
||||
print("Geometry: %u cylinders, %u sides" % (args.cyls, args.sides))
|
||||
|
|
@ -37,8 +45,9 @@ def main(argv):
|
|||
|
||||
# Header
|
||||
out_f = open(args.outfile, "wb")
|
||||
sig = b'HXCHFEV3' if v3 else b"HXCPICFE"
|
||||
out_f.write(struct.pack("<8s4B2H2BH",
|
||||
b"HXCPICFE",# signature
|
||||
sig, # signature
|
||||
0, # revision
|
||||
args.cyls, # nr_tracks
|
||||
args.sides, # nr_sides
|
||||
|
|
@ -59,7 +68,24 @@ def main(argv):
|
|||
out_f.write(bytearray(b'\xff'*(tlut_blocks*512-args.cyls*4)))
|
||||
|
||||
# Data
|
||||
out_f.write(bytearray(b'\x88'*(blocks*512*args.cyls)))
|
||||
if not args.hard_sectors:
|
||||
out_f.write(bytearray(b'\x88'*(blocks*512*args.cyls)))
|
||||
else:
|
||||
trk_side = bytearray(b'\x88')*(raw_bytes//2)
|
||||
sector_len = float(raw_bytes//2) / args.hard_sectors
|
||||
trk_side.insert(-int(sector_len)//2, 0x8F)
|
||||
for sector in range(args.hard_sectors-1, -1, -1):
|
||||
trk_side.insert(int(sector_len*sector), 0x8F)
|
||||
assert len(trk_side) == bytes//2
|
||||
trk_side += b'\x0F'*(256 - len(trk_side)%256)
|
||||
assert len(trk_side) == blocks*256
|
||||
|
||||
trk = bytearray()
|
||||
for pos in range(0, len(trk_side), 256):
|
||||
trk.extend(trk_side[pos:pos+256])
|
||||
trk.extend(trk_side[pos:pos+256])
|
||||
for _ in range(args.cyls):
|
||||
out_f.write(trk)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import sys,struct,argparse
|
|||
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,
|
||||
|
|
@ -22,10 +24,10 @@ def main(argv):
|
|||
bit_ms = 0.004916
|
||||
total_bytes = int(args.total * 1000.0 / bit_ms / 8)
|
||||
window_bytes = int(args.window * 1000.0 / bit_ms / 8)
|
||||
init_bytes = int(500.0 / bit_ms / 8)
|
||||
init_bytes = int(args.lead_in * 1000.0 / bit_ms / 8)
|
||||
|
||||
assert (2*init_bytes + window_bytes) < total_bytes, "Window too large"
|
||||
print("Lead-In: %.2f sec -> %u bytes" % (0.5, init_bytes))
|
||||
print("Lead-In: %.2f sec -> %u bytes" % (args.lead_in, init_bytes))
|
||||
print("Window: %.2f sec -> %u bytes" % (args.window, window_bytes))
|
||||
print("TOTAL: %.2f sec -> %u bytes" % (args.total, total_bytes))
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# Uncomment the following for clone chips
|
||||
#set CPUTAPID 0x2ba01477
|
||||
|
||||
source [find interface/stlink.cfg]
|
||||
|
||||
|
|
|
|||
29
scripts/srcdir.py
Normal file
29
scripts/srcdir.py
Normal 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)
|
||||
|
|
@ -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);
|
||||
|
|
@ -44,12 +50,15 @@ SECTIONS
|
|||
_thread_stackbottom = .;
|
||||
. = . + 1024;
|
||||
_thread_stacktop = .;
|
||||
_thread1_stackbottom = .;
|
||||
. = . + 512;
|
||||
_thread1_stacktop = .;
|
||||
_sbss = .;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
} >RW
|
||||
} >RAM
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame)
|
||||
1
src/.gitignore
vendored
1
src/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/ff_cfg_defaults.h
|
||||
|
|
@ -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"
|
||||
45
src/Makefile
45
src/Makefile
|
|
@ -1,27 +1,48 @@
|
|||
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 += fpec.o
|
||||
OBJS += file_cache.o
|
||||
OBJS += fpec_$(mcu).o
|
||||
OBJS += fs.o
|
||||
OBJS += main.o
|
||||
OBJS += fs_async.o
|
||||
OBJS += sd_spi.o
|
||||
OBJS += spi.o
|
||||
OBJS += string.o
|
||||
OBJS += stm32f10x.o
|
||||
OBJS += thread.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
|
||||
OBJS-$(debug) += console.o
|
||||
OBJS-$(logfile) += logfile.o
|
||||
|
||||
SUBDIRS += display
|
||||
SUBDIRS += fatfs
|
||||
|
|
@ -29,13 +50,17 @@ SUBDIRS += image
|
|||
SUBDIRS += gotek
|
||||
SUBDIRS += usb
|
||||
|
||||
.PHONY: build_info.c
|
||||
endif
|
||||
|
||||
.PHONY: $(SRCDIR)/build_info.c
|
||||
build_info.o: CFLAGS += -DFW_VER="\"$(FW_VER)\""
|
||||
|
||||
flash_cfg.o: ff_cfg_defaults.h
|
||||
# Avoid infinite loops due to GCC noticing code that can be replaced by a call
|
||||
# to a standard library function... within our implementation of that function.
|
||||
util.o: CFLAGS += -fno-tree-loop-distribute-patterns
|
||||
|
||||
ff_cfg_defaults.h: $(ROOT)/examples/FF.CFG
|
||||
$(PYTHON) $(ROOT)/scripts/mk_config.py $< $@
|
||||
|
||||
clean::
|
||||
rm -f ff_cfg_defaults.h
|
||||
main.o flash_cfg.o: ff_cfg_defaults.h
|
||||
main.o flash_cfg.o: CFLAGS += -iquote .
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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,7 +55,7 @@ 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);
|
||||
}
|
||||
|
||||
|
|
@ -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,7 +104,7 @@ 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");
|
||||
|
||||
|
|
|
|||
63
src/cache.c
63
src/cache.c
|
|
@ -26,7 +26,8 @@ struct cache {
|
|||
static struct cache *cache;
|
||||
#define CACHE_HASH(_id) ((_id)&31)
|
||||
|
||||
struct cache *cache_init(void *start, void *end, unsigned int item_sz)
|
||||
struct cache *cache_init(void *start, void *end, unsigned int item_sz,
|
||||
unsigned int *entry_cnt)
|
||||
{
|
||||
uint8_t *s, *e;
|
||||
int i, nitm;
|
||||
|
|
@ -38,10 +39,11 @@ struct cache *cache_init(void *start, void *end, unsigned int item_sz)
|
|||
e = (uint8_t *)((uint32_t)end & ~3);
|
||||
|
||||
nitm = ((e - s) - (int)sizeof(*c)) / (int)(sizeof(*cent) + item_sz);
|
||||
if (nitm < 8) {
|
||||
if (nitm < 3) {
|
||||
printk("No cache: too small (%d)\n", e - s);
|
||||
return NULL;
|
||||
}
|
||||
*entry_cnt = nitm;
|
||||
|
||||
/* Initialise the empty cache structure. */
|
||||
cache = c = (struct cache *)s;
|
||||
|
|
@ -65,6 +67,11 @@ struct cache *cache_init(void *start, void *end, unsigned int item_sz)
|
|||
}
|
||||
|
||||
const void *cache_lookup(struct cache *c, uint32_t id)
|
||||
{
|
||||
return cache_lookup_mutable(c, id);
|
||||
}
|
||||
|
||||
void *cache_lookup_mutable(struct cache *c, uint32_t id)
|
||||
{
|
||||
struct list_head *hash, *ent;
|
||||
struct cache_ent *cent;
|
||||
|
|
@ -85,14 +92,57 @@ found:
|
|||
return cent->dat;
|
||||
}
|
||||
|
||||
void *cache_lru_mutable(struct cache *c, uint32_t *id)
|
||||
{
|
||||
struct cache_ent *cent = container_of(c->lru.prev, struct cache_ent, lru);
|
||||
if (list_is_empty(¢->hash))
|
||||
return NULL;
|
||||
*id = cent->id;
|
||||
return cent->dat;
|
||||
}
|
||||
|
||||
void *cache_lru_search_mutable(struct cache *c, uint32_t *id)
|
||||
{
|
||||
for (struct list_head *ent = c->lru.prev; ent != &c->lru; ent = ent->prev) {
|
||||
struct cache_ent *cent = container_of(ent, struct cache_ent, lru);
|
||||
if (list_is_empty(¢->hash))
|
||||
continue;
|
||||
*id = cent->id;
|
||||
return cent->dat;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *cache_lru_next_mutable(struct cache *c, const void* ent, uint32_t *id)
|
||||
{
|
||||
const struct cache_ent *cent
|
||||
= container_of(ent, const struct cache_ent, dat);
|
||||
struct list_head *ent_prev = cent->lru.prev;
|
||||
struct cache_ent *cent_prev;
|
||||
if (ent_prev == &c->lru)
|
||||
return NULL;
|
||||
cent_prev = container_of(ent_prev, struct cache_ent, lru);
|
||||
*id = cent_prev->id;
|
||||
return cent_prev->dat;
|
||||
}
|
||||
|
||||
void cache_update(struct cache *c, uint32_t id, const void *dat)
|
||||
{
|
||||
bool_t created;
|
||||
void *p = cache_update_mutable(c, id, &created);
|
||||
memcpy(p, dat, c->item_sz);
|
||||
}
|
||||
|
||||
void *cache_update_mutable(struct cache *c, uint32_t id, bool_t *created)
|
||||
{
|
||||
struct cache_ent *cent;
|
||||
void *p;
|
||||
|
||||
/* Already in the cache? Just update the existing data. */
|
||||
if ((p = (void *)cache_lookup(c, id)) != NULL)
|
||||
goto found;
|
||||
if ((p = cache_lookup_mutable(c, id)) != NULL) {
|
||||
*created = FALSE;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Steal the oldest cache entry from the LRU. */
|
||||
cent = container_of(c->lru.prev, struct cache_ent, lru);
|
||||
|
|
@ -108,9 +158,8 @@ void cache_update(struct cache *c, uint32_t id, const void *dat)
|
|||
list_insert_head(&c->lru, ¢->lru);
|
||||
list_insert_head(&c->hash[CACHE_HASH(id)], ¢->hash);
|
||||
|
||||
found:
|
||||
/* Finally, store away the actual item data. */
|
||||
memcpy(p, dat, c->item_sz);
|
||||
*created = TRUE;
|
||||
return p;
|
||||
}
|
||||
|
||||
void cache_update_N(struct cache *c, uint32_t id,
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
*
|
||||
|
|
@ -73,8 +73,12 @@ void EXC_unexpected(struct extra_exception_frame *extra)
|
|||
|
||||
if ((psp >= (uint32_t)_thread_stackbottom)
|
||||
&& (psp < (uint32_t)_thread_stacktop)) {
|
||||
printk("Process Call Trace:", psp);
|
||||
printk("Process Call Trace (Thread %d):", 0);
|
||||
show_stack(psp, (uint32_t)_thread_stacktop);
|
||||
} else if ((psp >= (uint32_t)_thread1_stackbottom)
|
||||
&& (psp < (uint32_t)_thread1_stacktop)) {
|
||||
printk("Process Call Trace (Thread %d):", 1);
|
||||
show_stack(psp, (uint32_t)_thread1_stacktop);
|
||||
}
|
||||
|
||||
system_reset();
|
||||
|
|
@ -110,75 +114,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 +157,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();
|
||||
1
src/display/.gitignore
vendored
1
src/display/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/oled_font_*.c
|
||||
|
|
@ -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
|
||||
|
|
|
|||
1010
src/display/lcd_at32f435.c
Normal file
1010
src/display/lcd_at32f435.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -43,25 +43,62 @@ struct packed i2c_osd_info {
|
|||
};
|
||||
|
||||
/* STM32 I2C peripheral. */
|
||||
#define i2c i2c2
|
||||
#define SCL 10
|
||||
#define SDA 11
|
||||
static volatile struct i2c *i2c;
|
||||
|
||||
const static struct i2c_cfg {
|
||||
uint8_t en;
|
||||
uint8_t scl;
|
||||
uint8_t sda;
|
||||
uint8_t error_irq;
|
||||
uint8_t event_irq;
|
||||
uint8_t dma_tx;
|
||||
uint8_t dma_rx;
|
||||
} i2c1_cfg = {
|
||||
.en = 21, /* RCC_APB1ENR_I2C1EN */
|
||||
.scl = 6,
|
||||
.sda = 7,
|
||||
.error_irq = 32,
|
||||
.event_irq = 31,
|
||||
.dma_tx = 6,
|
||||
.dma_rx = 7,
|
||||
}, i2c2_cfg = {
|
||||
.en = 22, /* RCC_APB1ENR_I2C2EN */
|
||||
.scl = 10,
|
||||
.sda = 11,
|
||||
.error_irq = 34,
|
||||
.event_irq = 33,
|
||||
.dma_tx = 4,
|
||||
.dma_rx = 5
|
||||
}, *i2c_cfg;
|
||||
|
||||
#define SCL i2c_cfg->scl
|
||||
#define SDA i2c_cfg->sda
|
||||
|
||||
#define PCLK_MHZ APB1_MHZ
|
||||
|
||||
/* I2C error ISR. */
|
||||
#define I2C_ERROR_IRQ 34
|
||||
#define I2C_ERROR_IRQ i2c_cfg->error_irq
|
||||
void IRQ_34(void) __attribute__((alias("IRQ_i2c_error")));
|
||||
void IRQ_32(void) __attribute__((alias("IRQ_i2c_error")));
|
||||
|
||||
/* I2C event ISR. */
|
||||
#define I2C_EVENT_IRQ 33
|
||||
#define I2C_EVENT_IRQ i2c_cfg->event_irq
|
||||
void IRQ_33(void) __attribute__((alias("IRQ_i2c_event")));
|
||||
void IRQ_31(void) __attribute__((alias("IRQ_i2c_event")));
|
||||
|
||||
/* DMA completion ISR. */
|
||||
#define DMA1_CH4_IRQ 14
|
||||
/* DMA Tx. */
|
||||
#define i2c_tx_dma dma1->ch[i2c_cfg->dma_tx-1]
|
||||
#define DMA_TX_CGIF DMA_IFCR_CGIF(i2c_cfg->dma_tx)
|
||||
#define DMA_TX_IRQ (i2c_cfg->dma_tx + 10)
|
||||
void IRQ_14(void) __attribute__((alias("IRQ_dma_tx_tc")));
|
||||
void IRQ_16(void) __attribute__((alias("IRQ_dma_tx_tc")));
|
||||
|
||||
/* DMA completion ISR. */
|
||||
#define DMA1_CH5_IRQ 15
|
||||
/* DMA Rx. */
|
||||
#define i2c_rx_dma dma1->ch[i2c_cfg->dma_rx-1]
|
||||
#define DMA_RX_CGIF DMA_IFCR_CGIF(i2c_cfg->dma_rx)
|
||||
#define DMA_RX_IRQ (i2c_cfg->dma_rx + 10)
|
||||
void IRQ_15(void) __attribute__((alias("IRQ_dma_rx_tc")));
|
||||
void IRQ_17(void) __attribute__((alias("IRQ_dma_rx_tc")));
|
||||
|
||||
bool_t has_osd;
|
||||
uint8_t osd_buttons_tx;
|
||||
|
|
@ -126,8 +163,8 @@ static void IRQ_i2c_error(void)
|
|||
i2c->cr1 = I2C_CR1_SWRST;
|
||||
|
||||
/* Clear the DMA controller. */
|
||||
dma1->ch4.ccr = dma1->ch5.ccr = 0;
|
||||
dma1->ifcr = DMA_IFCR_CGIF(4) | DMA_IFCR_CGIF(5);
|
||||
i2c_tx_dma.ccr = i2c_rx_dma.ccr = 0;
|
||||
dma1->ifcr = DMA_TX_CGIF | DMA_RX_CGIF;
|
||||
|
||||
timer_cancel(&timeout_timer);
|
||||
|
||||
|
|
@ -168,21 +205,21 @@ static void dma_start(unsigned int sz)
|
|||
ASSERT(sz <= sizeof(buffer));
|
||||
|
||||
if (in_osd == OSD_read) {
|
||||
dma1->ch5.cndtr = sz;
|
||||
dma1->ch5.ccr = (DMA_CCR_MSIZE_8BIT |
|
||||
DMA_CCR_PSIZE_16BIT |
|
||||
DMA_CCR_MINC |
|
||||
DMA_CCR_DIR_P2M |
|
||||
DMA_CCR_TCIE |
|
||||
DMA_CCR_EN);
|
||||
i2c_rx_dma.cndtr = sz;
|
||||
i2c_rx_dma.ccr = (DMA_CCR_MSIZE_8BIT |
|
||||
DMA_CCR_PSIZE_16BIT |
|
||||
DMA_CCR_MINC |
|
||||
DMA_CCR_DIR_P2M |
|
||||
DMA_CCR_TCIE |
|
||||
DMA_CCR_EN);
|
||||
} else {
|
||||
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);
|
||||
i2c_tx_dma.cndtr = sz;
|
||||
i2c_tx_dma.ccr = (DMA_CCR_MSIZE_8BIT |
|
||||
DMA_CCR_PSIZE_16BIT |
|
||||
DMA_CCR_MINC |
|
||||
DMA_CCR_DIR_M2P |
|
||||
DMA_CCR_TCIE |
|
||||
DMA_CCR_EN);
|
||||
}
|
||||
|
||||
/* Set the timeout timer in case the DMA hangs for any reason. */
|
||||
|
|
@ -211,7 +248,8 @@ static unsigned int osd_prep_buffer(void)
|
|||
uint16_t order = menu_mode ? 0x7903 : 0x7183;
|
||||
char *p;
|
||||
uint8_t *q = buffer;
|
||||
unsigned int row;
|
||||
unsigned int row, rows, heights;
|
||||
int i;
|
||||
|
||||
if (++in_osd == OSD_read) {
|
||||
i2c->cr2 |= I2C_CR2_LAST | I2C_CR2_ITEVTEN;
|
||||
|
|
@ -219,13 +257,31 @@ static unsigned int osd_prep_buffer(void)
|
|||
return sizeof(struct i2c_osd_info);
|
||||
}
|
||||
|
||||
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 | 3;
|
||||
*q++ = OSD_HEIGHTS | (menu_mode ? 4 : 2);
|
||||
*q++ = OSD_ROWS | rows;
|
||||
*q++ = OSD_HEIGHTS | heights;
|
||||
*q++ = OSD_BUTTONS | osd_buttons_tx;
|
||||
*q++ = OSD_DATA;
|
||||
for (row = 0; row < 3; row++) {
|
||||
for (row = 0; row < rows; row++) {
|
||||
p = text[(order >> (row * DORD_shift)) & DORD_row];
|
||||
memcpy(q, p, lcd_columns);
|
||||
q += lcd_columns;
|
||||
|
|
@ -285,8 +341,8 @@ static unsigned int lcd_prep_buffer(void)
|
|||
static void IRQ_dma_tx_tc(void)
|
||||
{
|
||||
/* Clear the DMA controller. */
|
||||
dma1->ch4.ccr = 0;
|
||||
dma1->ifcr = DMA_IFCR_CGIF(4);
|
||||
i2c_tx_dma.ccr = 0;
|
||||
dma1->ifcr = DMA_TX_CGIF;
|
||||
|
||||
/* Wait for BTF. We then get called back on dma_tx_tc_btf().
|
||||
*
|
||||
|
|
@ -319,8 +375,8 @@ static void IRQ_dma_rx_tc(void)
|
|||
struct i2c_osd_info *info = (struct i2c_osd_info *)buffer;
|
||||
|
||||
/* Clear the DMA controller. */
|
||||
dma1->ch5.ccr = 0;
|
||||
dma1->ifcr = DMA_IFCR_CGIF(5);
|
||||
i2c_rx_dma.ccr = 0;
|
||||
dma1->ifcr = DMA_RX_CGIF;
|
||||
|
||||
/* Clean up I2C. */
|
||||
i2c->cr2 &= ~I2C_CR2_LAST;
|
||||
|
|
@ -371,6 +427,10 @@ static void i2c_stop(void)
|
|||
i2c->cr1 |= I2C_CR1_STOP;
|
||||
while (i2c->cr1 & I2C_CR1_STOP)
|
||||
continue;
|
||||
if (is_artery_mcu) {
|
||||
i2c->cr1 = 0;
|
||||
i2c->cr1 = I2C_CR1_PE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Synchronously transmit an I2C byte. */
|
||||
|
|
@ -473,7 +533,15 @@ bool_t lcd_init(void)
|
|||
in_osd = OSD_no;
|
||||
osd_buttons_rx = 0;
|
||||
|
||||
rcc->apb1enr |= RCC_APB1ENR_I2C2EN;
|
||||
if (mcu_package == MCU_QFN32) {
|
||||
i2c = i2c1;
|
||||
i2c_cfg = &i2c1_cfg;
|
||||
} else {
|
||||
i2c = i2c2;
|
||||
i2c_cfg = &i2c2_cfg;
|
||||
}
|
||||
|
||||
rcc->apb1enr |= 1<<i2c_cfg->en;
|
||||
|
||||
/* Check we have a clear I2C bus. Both clock and data must be high. If SDA
|
||||
* is stuck low then slave may be stuck in an ACK cycle. We can try to
|
||||
|
|
@ -520,9 +588,9 @@ bool_t lcd_init(void)
|
|||
|
||||
/* Standard Mode (100kHz) */
|
||||
i2c->cr1 = 0;
|
||||
i2c->cr2 = I2C_CR2_FREQ(36);
|
||||
i2c->ccr = I2C_CCR_CCR(180);
|
||||
i2c->trise = 37;
|
||||
i2c->cr2 = I2C_CR2_FREQ(PCLK_MHZ);
|
||||
i2c->ccr = I2C_CCR_CCR(PCLK_MHZ*5);
|
||||
i2c->trise = (PCLK_MHZ <= 62) ? PCLK_MHZ + 1 : 63;
|
||||
i2c->cr1 = I2C_CR1_PE;
|
||||
|
||||
if (!reinit) {
|
||||
|
|
@ -564,11 +632,7 @@ bool_t lcd_init(void)
|
|||
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;
|
||||
lcd_rows = max_t(uint8_t, lcd_rows, 2);
|
||||
lcd_rows = min_t(uint8_t, lcd_rows, 4);
|
||||
}
|
||||
|
||||
if (a != 0) {
|
||||
|
|
@ -577,10 +641,14 @@ bool_t lcd_init(void)
|
|||
i2c_addr = a;
|
||||
} else {
|
||||
is_oled_display = FALSE;
|
||||
if (ff_cfg.display_type == DISPLAY_auto)
|
||||
lcd_columns = 40;
|
||||
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();
|
||||
|
||||
}
|
||||
|
|
@ -597,20 +665,20 @@ bool_t lcd_init(void)
|
|||
i2c->cr2 |= I2C_CR2_ITERREN;
|
||||
|
||||
/* Initialise DMA1 channel 4 and its completion interrupt. */
|
||||
dma1->ch4.cmar = (uint32_t)(unsigned long)buffer;
|
||||
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->dr;
|
||||
dma1->ifcr = DMA_TX_CGIF;
|
||||
IRQx_set_prio(DMA_TX_IRQ, I2C_IRQ_PRI);
|
||||
IRQx_clear_pending(DMA_TX_IRQ);
|
||||
IRQx_enable(DMA_TX_IRQ);
|
||||
|
||||
/* Initialise DMA1 channel 5 and its completion interrupt. */
|
||||
dma1->ch5.cmar = (uint32_t)(unsigned long)buffer;
|
||||
dma1->ch5.cpar = (uint32_t)(unsigned long)&i2c->dr;
|
||||
dma1->ifcr = DMA_IFCR_CGIF(5);
|
||||
IRQx_set_prio(DMA1_CH5_IRQ, I2C_IRQ_PRI);
|
||||
IRQx_clear_pending(DMA1_CH5_IRQ);
|
||||
IRQx_enable(DMA1_CH5_IRQ);
|
||||
i2c_rx_dma.cmar = (uint32_t)(unsigned long)buffer;
|
||||
i2c_rx_dma.cpar = (uint32_t)(unsigned long)&i2c->dr;
|
||||
dma1->ifcr = DMA_RX_CGIF;
|
||||
IRQx_set_prio(DMA_RX_IRQ, I2C_IRQ_PRI);
|
||||
IRQx_clear_pending(DMA_RX_IRQ);
|
||||
IRQx_enable(DMA_RX_IRQ);
|
||||
|
||||
/* Timeout handler for if I2C transmission borks. */
|
||||
timer_init(&timeout_timer, timeout_fn, NULL);
|
||||
|
|
@ -660,12 +728,12 @@ fail:
|
|||
return FALSE;
|
||||
IRQx_disable(I2C_EVENT_IRQ);
|
||||
IRQx_disable(I2C_ERROR_IRQ);
|
||||
IRQx_disable(DMA1_CH4_IRQ);
|
||||
IRQx_disable(DMA1_CH5_IRQ);
|
||||
IRQx_disable(DMA_TX_IRQ);
|
||||
IRQx_disable(DMA_RX_IRQ);
|
||||
i2c->cr1 &= ~I2C_CR1_PE;
|
||||
gpio_configure_pin(gpiob, SCL, GPI_pull_up);
|
||||
gpio_configure_pin(gpiob, SDA, GPI_pull_up);
|
||||
rcc->apb1enr &= ~RCC_APB1ENR_I2C2EN;
|
||||
rcc->apb1enr &= ~(1<<i2c_cfg->en);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -979,6 +1047,19 @@ fail:
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static void oled_init_fast_mode(void)
|
||||
{
|
||||
/* Disable I2C (currently in Standard Mode). */
|
||||
i2c->cr1 = 0;
|
||||
|
||||
/* Fast Mode (400kHz). */
|
||||
i2c->cr2 = I2C_CR2_FREQ(PCLK_MHZ);
|
||||
i2c->ccr = I2C_CCR_FS | I2C_CCR_CCR(PCLK_MHZ*5/6);
|
||||
i2c->trise = PCLK_MHZ*3/10 + 1;
|
||||
i2c->cr1 = I2C_CR1_PE;
|
||||
i2c->cr2 |= I2C_CR2_ITERREN;
|
||||
}
|
||||
|
||||
static void oled_init(void)
|
||||
{
|
||||
static const uint8_t init_cmds[] = {
|
||||
|
|
@ -998,19 +1079,11 @@ static void oled_init(void)
|
|||
0xa0, /* segment mapping (default) */
|
||||
0xc0, /* com scan direction (default) */
|
||||
};
|
||||
const uint8_t *cmds;
|
||||
uint8_t dynamic_cmds[7], *dc;
|
||||
uint8_t *p = buffer;
|
||||
|
||||
/* 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;
|
||||
|
|
@ -1029,9 +1102,13 @@ static void oled_init(void)
|
|||
*dc++ = (oled_height == 64) ? 0x12 : 0x02;
|
||||
p += oled_queue_cmds(p, dynamic_cmds, dc - dynamic_cmds);
|
||||
|
||||
/* Display is right-way-up, or rotated. */
|
||||
cmds = (ff_cfg.display_type & DISPLAY_rotate) ? rot_cmds : norot_cmds;
|
||||
p += oled_queue_cmds(p, cmds, sizeof(rot_cmds));
|
||||
/* Display orientation. */
|
||||
dc = dynamic_cmds;
|
||||
memcpy(dc, (ff_cfg.display_type & DISPLAY_rotate) ? rot_cmds : norot_cmds,
|
||||
2);
|
||||
if (ff_cfg.display_type & DISPLAY_hflip)
|
||||
dc[0] ^= 1;
|
||||
p += oled_queue_cmds(p, dc, 2);
|
||||
|
||||
/* Start off the I2C transaction. */
|
||||
p += oled_start_i2c(p);
|
||||
|
|
@ -21,14 +21,14 @@
|
|||
#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. */
|
||||
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 +261,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();
|
||||
|
|
|
|||
|
|
@ -3495,6 +3495,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
|
|||
|
||||
/* Boundaries and Limits */
|
||||
fs->volbase = bsect;
|
||||
fs->volend = maxlba - 1;
|
||||
fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);
|
||||
fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);
|
||||
if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */
|
||||
|
|
@ -3564,6 +3565,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
|
|||
/* Boundaries and Limits */
|
||||
fs->n_fatent = nclst + 2; /* Number of FAT entries */
|
||||
fs->volbase = bsect; /* Volume start sector */
|
||||
fs->volend = bsect + tsect - 1; /* Volume end sector */
|
||||
fs->fatbase = bsect + nrsv; /* FAT start sector */
|
||||
fs->database = bsect + sysect; /* Data start sector */
|
||||
if (fmt == FS_FAT32) {
|
||||
|
|
@ -3746,6 +3748,8 @@ FRESULT f_open (
|
|||
|
||||
if (!fp) return FR_INVALID_OBJECT;
|
||||
|
||||
dj.obj.attr = 0; /* FlashFloppy: avoid uninitialised assignment warn */
|
||||
|
||||
/* Get logical drive number */
|
||||
mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
|
||||
res = mount_volume(&path, &fs, mode);
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ typedef struct {
|
|||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||
DWORD fsize; /* Size of an FAT [sectors] */
|
||||
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 */
|
||||
|
|
|
|||
512
src/file_cache.c
Normal file
512
src/file_cache.c
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* file_cache.c
|
||||
*
|
||||
* Caching I/O for a single file.
|
||||
*
|
||||
* Written & released by Keir Fraser <keir.xen@gmail.com> and Eric Anderson
|
||||
* <ejona86@gmail.com>
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
#define SECSZ 512
|
||||
|
||||
|
||||
/*
|
||||
Writes require the cache to have a memory buffer for I/O, since it is hard to
|
||||
guarantee the image's buffer will remain around.
|
||||
|
||||
fs requires contiguous buffers for multi-sector reads/writes.
|
||||
|
||||
ADF wants to write 512 byte chunks without any reads. HFE wants to write 256
|
||||
byte chunks and requires reads.
|
||||
|
||||
If a partial-sector write occurs, if the sector isn't already buffered, trigger
|
||||
a read for the sector and reject the write.
|
||||
|
||||
How to handle read-ahead? Esp for HFE. Esp when track doesn't fit in memory.
|
||||
- If I/O in progress, do nothing
|
||||
- Write LRU if it is dirty
|
||||
- Check following sectors to see if they are in the cache. updating LRU. If one
|
||||
isn't in the cache, read it (evicting LRU).
|
||||
- If there is a dirty sector (by scanning LRU), write it
|
||||
*/
|
||||
|
||||
struct file_cache {
|
||||
struct cache *cache;
|
||||
FIL *fp;
|
||||
FOP fop;
|
||||
void (*fop_cb)(struct file_cache *);
|
||||
FSIZE_t cur_sector;
|
||||
FSIZE_t io_sector; /* Sector of 'fop' */
|
||||
FSIZE_t readahead_start;
|
||||
FSIZE_t readahead_end;
|
||||
uint32_t dirty_key; /* Optimization to find dirty entries */
|
||||
struct entry *dirty_val;
|
||||
uint16_t dirty_entries; /* Number of dirty entries */
|
||||
uint16_t entry_cnt;
|
||||
uint16_t readahead_prio;
|
||||
uint16_t readahead_sectors;
|
||||
uint8_t io_max;
|
||||
uint8_t subkey_bits;
|
||||
uint8_t io_cnt;
|
||||
bool_t sync_needed:1;
|
||||
bool_t sync_requested:1;
|
||||
bool_t writing:1;
|
||||
bool_t readahead:1;
|
||||
};
|
||||
|
||||
struct entry {
|
||||
/* indexed by subkey */
|
||||
uint8_t unread_bitfield;
|
||||
uint8_t dirty_bitfield; /* Every dirty sector has unread=0. */
|
||||
uint32_t _align[0];
|
||||
uint8_t data[0][512];
|
||||
};
|
||||
|
||||
static void enqueue_io(struct file_cache *fcache);
|
||||
|
||||
struct file_cache *file_cache_init(FIL *fp, uint8_t batch_secs,
|
||||
void *start, void *end)
|
||||
{
|
||||
unsigned int entry_cnt;
|
||||
struct file_cache *fcache = (void *)(((uint32_t)start + 3) & ~3);
|
||||
start = (uint8_t *)fcache + sizeof(*fcache);
|
||||
ASSERT(start < end);
|
||||
|
||||
memset(fcache, 0, sizeof(*fcache));
|
||||
fcache->fp = fp;
|
||||
ASSERT(batch_secs <= sizeof(((struct entry *)NULL)->unread_bitfield)*8);
|
||||
ASSERT((batch_secs & (batch_secs-1)) == 0); /* Power of 2 */
|
||||
|
||||
for (uint8_t i = batch_secs; i > 1; i /= 2)
|
||||
fcache->subkey_bits++;
|
||||
fcache->cur_sector = UINT_MAX;
|
||||
fcache->io_sector = UINT_MAX;
|
||||
fcache->io_max = 0xff;
|
||||
fcache->cache = cache_init(
|
||||
start, end, sizeof(struct entry) + SECSZ*batch_secs, &entry_cnt);
|
||||
/* We need at least one cache entry for the API to function. */
|
||||
ASSERT(fcache->cache != NULL);
|
||||
fcache->entry_cnt = entry_cnt;
|
||||
return fcache;
|
||||
}
|
||||
|
||||
static void init_entry(const struct file_cache *fcache, struct entry *entry)
|
||||
{
|
||||
entry->unread_bitfield = (1 << (1 << fcache->subkey_bits))-1;
|
||||
entry->dirty_bitfield = 0;
|
||||
}
|
||||
|
||||
static uint32_t calc_key(const struct file_cache *fcache, FSIZE_t ofs)
|
||||
{
|
||||
return (ofs/SECSZ)>>fcache->subkey_bits;
|
||||
}
|
||||
|
||||
static uint32_t calc_subkey(const struct file_cache *fcache, FSIZE_t ofs)
|
||||
{
|
||||
return ofs/SECSZ & ((1<<fcache->subkey_bits)-1);
|
||||
}
|
||||
|
||||
static void progress_io(struct file_cache *fcache)
|
||||
{
|
||||
thread_yield();
|
||||
if (fcache->fop_cb != NULL && F_async_isdone(fcache->fop)) {
|
||||
void (*fop_cb)(struct file_cache*) = fcache->fop_cb;
|
||||
fcache->fop_cb = NULL;
|
||||
fop_cb(fcache);
|
||||
}
|
||||
}
|
||||
|
||||
void file_cache_sync_wait(struct file_cache *fcache)
|
||||
{
|
||||
bool_t readahead = fcache->readahead;
|
||||
ASSERT(!fcache->writing); /* Missing a flush? */
|
||||
fcache->readahead = FALSE;
|
||||
ASSERT(!fcache->sync_needed || fcache->sync_requested);
|
||||
while (fcache->sync_needed) {
|
||||
ASSERT(fcache->fop_cb != NULL);
|
||||
F_async_wait(fcache->fop);
|
||||
file_cache_progress(fcache);
|
||||
}
|
||||
fcache->readahead = readahead;
|
||||
}
|
||||
|
||||
void file_cache_shutdown(struct file_cache *fcache)
|
||||
{
|
||||
if (fcache->fop_cb == NULL)
|
||||
return;
|
||||
F_async_wait(fcache->fop);
|
||||
}
|
||||
|
||||
const void *file_cache_peek_read(struct file_cache *fcache, FSIZE_t ofs)
|
||||
{
|
||||
const struct entry *val;
|
||||
uint32_t subkey = calc_subkey(fcache, ofs);
|
||||
ASSERT(ofs % SECSZ == 0);
|
||||
ASSERT(!fcache->writing); /* Missing a flush? */
|
||||
fcache->cur_sector = ofs;
|
||||
file_cache_progress(fcache);
|
||||
val = cache_lookup(fcache->cache, calc_key(fcache, ofs));
|
||||
if (!val || (val->unread_bitfield & 1<<subkey))
|
||||
return NULL;
|
||||
return val->data[subkey];
|
||||
}
|
||||
|
||||
static void mark_dirty(
|
||||
struct file_cache *fcache, struct entry *val, FSIZE_t ofs)
|
||||
{
|
||||
if (!fcache->dirty_val) {
|
||||
fcache->dirty_key = calc_key(fcache, ofs);
|
||||
fcache->dirty_val = val;
|
||||
}
|
||||
if (!val->dirty_bitfield) {
|
||||
fcache->dirty_entries++;
|
||||
fcache->sync_needed = TRUE;
|
||||
}
|
||||
val->dirty_bitfield |= 1<<calc_subkey(fcache, ofs);
|
||||
}
|
||||
|
||||
static void flush(struct file_cache *fcache)
|
||||
{
|
||||
FSIZE_t ofs = fcache->cur_sector;
|
||||
struct entry *val;
|
||||
uint32_t subkey;
|
||||
ASSERT(fcache->writing);
|
||||
val = cache_lookup_mutable(fcache->cache, calc_key(fcache, ofs));
|
||||
subkey = calc_subkey(fcache, ofs);
|
||||
ASSERT(val && !(val->unread_bitfield & 1<<subkey));
|
||||
mark_dirty(fcache, val, ofs);
|
||||
fcache->writing = FALSE;
|
||||
}
|
||||
|
||||
void *file_cache_peek_write(struct file_cache *fcache, FSIZE_t ofs)
|
||||
{
|
||||
struct entry *val;
|
||||
uint32_t subkey;
|
||||
ASSERT(ofs % SECSZ == 0);
|
||||
if (fcache->writing && fcache->cur_sector != ofs)
|
||||
flush(fcache);
|
||||
fcache->writing = TRUE;
|
||||
fcache->cur_sector = ofs;
|
||||
file_cache_progress(fcache);
|
||||
val = cache_lookup_mutable(fcache->cache, calc_key(fcache, ofs));
|
||||
subkey = calc_subkey(fcache, ofs);
|
||||
if (!val || (val->unread_bitfield & 1<<subkey))
|
||||
return NULL;
|
||||
return val->data[subkey];
|
||||
}
|
||||
|
||||
void file_cache_progress(struct file_cache *fcache)
|
||||
{
|
||||
progress_io(fcache);
|
||||
enqueue_io(fcache);
|
||||
}
|
||||
|
||||
void file_cache_sync(struct file_cache *fcache)
|
||||
{
|
||||
if (fcache->writing)
|
||||
flush(fcache);
|
||||
if (fcache->sync_needed)
|
||||
fcache->sync_requested = TRUE;
|
||||
file_cache_progress(fcache);
|
||||
}
|
||||
|
||||
bool_t file_cache_try_read(struct file_cache *fcache, void *buf, FSIZE_t ofs,
|
||||
UINT btr)
|
||||
{
|
||||
const uint8_t *data;
|
||||
ASSERT(ofs / SECSZ == (ofs+btr-1) / SECSZ);
|
||||
data = file_cache_peek_read(fcache, ofs & ~(SECSZ-1));
|
||||
if (!data)
|
||||
return FALSE;
|
||||
ofs %= SECSZ;
|
||||
memcpy(buf, (uint8_t*)data + ofs, btr);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool_t file_cache_try_write(struct file_cache *fcache, const void *buf,
|
||||
FSIZE_t ofs, UINT btw)
|
||||
{
|
||||
bool_t created;
|
||||
const struct entry *val_lru;
|
||||
struct entry *val;
|
||||
uint32_t subkey, lru_id;
|
||||
ASSERT(ofs / SECSZ == (ofs + btw - 1) / SECSZ);
|
||||
if (btw != SECSZ) {
|
||||
/* slow read+write path */
|
||||
uint8_t *data = file_cache_peek_write(fcache, ofs & ~(SECSZ-1));
|
||||
if (!data)
|
||||
return FALSE;
|
||||
ofs %= SECSZ;
|
||||
memcpy((uint8_t*)data + ofs, buf, btw);
|
||||
file_cache_sync(fcache);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* fast write-only path */
|
||||
if (fcache->writing && fcache->cur_sector != ofs)
|
||||
file_cache_sync(fcache);
|
||||
progress_io(fcache);
|
||||
val_lru = cache_lru_mutable(fcache->cache, &lru_id);
|
||||
if (val_lru && (lru_id == calc_key(fcache, fcache->io_sector)
|
||||
|| val_lru->dirty_bitfield)) {
|
||||
enqueue_io(fcache);
|
||||
return FALSE;
|
||||
}
|
||||
fcache->cur_sector = ofs;
|
||||
val = cache_update_mutable(fcache->cache, calc_key(fcache, ofs), &created);
|
||||
if (created)
|
||||
init_entry(fcache, val);
|
||||
subkey = calc_subkey(fcache, ofs);
|
||||
if (fcache->io_sector <= ofs
|
||||
&& ofs < fcache->io_sector + fcache->io_cnt*512
|
||||
&& (val->unread_bitfield & 1<<subkey)) {
|
||||
/* A read op is in progress for this sector. */
|
||||
return FALSE;
|
||||
}
|
||||
val->unread_bitfield &= ~(1 << subkey);
|
||||
mark_dirty(fcache, val, ofs);
|
||||
ofs %= SECSZ;
|
||||
memcpy(val->data[subkey] + ofs, buf, btw);
|
||||
enqueue_io(fcache);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void file_cache_read(struct file_cache *fcache, void *buf, FSIZE_t ofs,
|
||||
UINT btr)
|
||||
{
|
||||
while (!file_cache_try_read(fcache, buf, ofs, btr))
|
||||
F_async_wait(fcache->fop);
|
||||
}
|
||||
|
||||
void file_cache_write(struct file_cache *fcache, const void *buf,
|
||||
FSIZE_t ofs, UINT btw)
|
||||
{
|
||||
while (!file_cache_try_write(fcache, buf, ofs, btw))
|
||||
F_async_wait(fcache->fop);
|
||||
}
|
||||
|
||||
void file_cache_io_limit(struct file_cache *fcache, uint8_t io_max)
|
||||
{
|
||||
if (!io_max)
|
||||
io_max = 0xff;
|
||||
fcache->io_max = io_max;
|
||||
}
|
||||
|
||||
void file_cache_readahead(
|
||||
struct file_cache *fcache, FSIZE_t ofs, UINT btr, UINT prio)
|
||||
{
|
||||
int sectors, entries;
|
||||
if (!btr) {
|
||||
fcache->readahead = FALSE;
|
||||
return;
|
||||
}
|
||||
fcache->readahead = TRUE;
|
||||
fcache->readahead_start = ofs & ~(SECSZ-1);
|
||||
fcache->readahead_end = (ofs+btr+SECSZ-1) & ~(SECSZ-1);
|
||||
|
||||
sectors = (fcache->readahead_end - fcache->readahead_start)/SECSZ;
|
||||
entries = calc_key(fcache, ofs+btr-1) - calc_key(fcache, ofs) + 1;
|
||||
if (entries > fcache->entry_cnt) {
|
||||
/* Region does not fit in memory. Need to guarantee that readahead will
|
||||
* not load enough entries to drop cur_sector. */
|
||||
ASSERT(fcache->entry_cnt >= 3);
|
||||
/* Maximum number of sectors that can be read when the region ends are
|
||||
* in the readahead window. */
|
||||
sectors -= (entries - fcache->entry_cnt) << fcache->subkey_bits;
|
||||
/* Maximum number of sectors that can be read if cur_sector is at the
|
||||
* last sector of an entry and the region ends are in the readahead
|
||||
* window. */
|
||||
sectors -= (1 << fcache->subkey_bits) - 1;
|
||||
ASSERT(sectors >= 3);
|
||||
}
|
||||
sectors = min_t(int, sectors, 50); /* Avoid large linear scan. */
|
||||
|
||||
prio = (prio+SECSZ-1)/SECSZ;
|
||||
prio = min_t(uint32_t, prio, sectors-1);
|
||||
fcache->readahead_prio = prio;
|
||||
fcache->readahead_sectors = sectors-1 - prio;
|
||||
}
|
||||
|
||||
/*
|
||||
* I/O Scheduler
|
||||
*/
|
||||
|
||||
static void register_fop_whendone(
|
||||
struct file_cache *fcache, FOP fop, void (*cb)(struct file_cache *))
|
||||
{
|
||||
ASSERT(fcache->fop_cb == NULL);
|
||||
fcache->fop = fop;
|
||||
fcache->fop_cb = cb;
|
||||
thread_yield(); /* Give fop a chance to start. */
|
||||
}
|
||||
|
||||
static void sync_complete(struct file_cache *fcache)
|
||||
{
|
||||
if (!fcache->dirty_entries) {
|
||||
fcache->sync_needed = FALSE;
|
||||
fcache->sync_requested = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_complete(struct file_cache *fcache)
|
||||
{
|
||||
fcache->io_sector = UINT_MAX;
|
||||
fcache->io_cnt = 0;
|
||||
}
|
||||
|
||||
/* Write processing should avoid changing the cache LRU. */
|
||||
static void write_start(
|
||||
struct file_cache *fcache, uint32_t key, struct entry *val)
|
||||
{
|
||||
uint32_t subkey = 0;
|
||||
FSIZE_t sector;
|
||||
FOP fop;
|
||||
ASSERT(fcache->dirty_entries);
|
||||
ASSERT(val->dirty_bitfield);
|
||||
|
||||
for (uint8_t d = val->dirty_bitfield; !(d & 1); d >>= 1, subkey++)
|
||||
;
|
||||
sector = ((key << fcache->subkey_bits) + subkey) * SECSZ;
|
||||
for (fcache->io_cnt = 0;
|
||||
fcache->io_cnt < fcache->io_max;
|
||||
fcache->io_cnt++) {
|
||||
if (!(val->dirty_bitfield & (1 << (subkey + fcache->io_cnt))))
|
||||
break;
|
||||
/* Clear eagerly to track new writes during the I/O. */
|
||||
val->dirty_bitfield &= ~(1 << (subkey + fcache->io_cnt));
|
||||
}
|
||||
if (!val->dirty_bitfield) {
|
||||
fcache->dirty_entries--;
|
||||
/* Update dirty_key */
|
||||
if (fcache->dirty_entries) {
|
||||
uint32_t lru_id;
|
||||
struct entry *lru_val = val;
|
||||
/* Try to find a dirty entry by iterating through entries newer
|
||||
* than the current write. */
|
||||
while (lru_val && !lru_val->dirty_bitfield)
|
||||
lru_val = cache_lru_next_mutable(fcache->cache, lru_val, &lru_id);
|
||||
/* If that doesn't find an entry, iterate through all entries. */
|
||||
if (!lru_val)
|
||||
lru_val = cache_lru_search_mutable(fcache->cache, &lru_id);
|
||||
while (lru_val && !lru_val->dirty_bitfield)
|
||||
lru_val = cache_lru_next_mutable(fcache->cache, lru_val, &lru_id);
|
||||
ASSERT(lru_val);
|
||||
fcache->dirty_key = lru_id;
|
||||
fcache->dirty_val = lru_val;
|
||||
} else {
|
||||
fcache->dirty_key = 0;
|
||||
fcache->dirty_val = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
F_lseek_async(fcache->fp, sector);
|
||||
fop = F_write_async(fcache->fp, val->data[subkey],
|
||||
fcache->io_cnt*SECSZ, NULL);
|
||||
register_fop_whendone(fcache, fop, write_complete);
|
||||
}
|
||||
|
||||
static void read_complete(struct file_cache *fcache)
|
||||
{
|
||||
uint32_t key;
|
||||
uint32_t subkey;
|
||||
struct entry *val;
|
||||
key = calc_key(fcache, fcache->io_sector);
|
||||
subkey = calc_subkey(fcache, fcache->io_sector);
|
||||
val = cache_lookup_mutable(fcache->cache, key);
|
||||
for (int i = 0; i < fcache->io_cnt; i++)
|
||||
val->unread_bitfield &= ~(1 << (subkey + i));
|
||||
fcache->io_sector = UINT_MAX;
|
||||
fcache->io_cnt = 0;
|
||||
}
|
||||
|
||||
static bool_t read_start(struct file_cache *fcache, FSIZE_t sector)
|
||||
{
|
||||
uint32_t lru_id;
|
||||
struct entry *val;
|
||||
bool_t created;
|
||||
uint32_t key, subkey;
|
||||
FOP fop;
|
||||
|
||||
/* Write LRU if it is dirty, to avoid it being lost. */
|
||||
val = cache_lru_mutable(fcache->cache, &lru_id);
|
||||
if (val && val->dirty_bitfield) {
|
||||
write_start(fcache, lru_id, val);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
key = calc_key(fcache, sector);
|
||||
subkey = calc_subkey(fcache, sector);
|
||||
val = cache_update_mutable(fcache->cache, key, &created);
|
||||
if (created)
|
||||
init_entry(fcache, val);
|
||||
if (!(val->unread_bitfield & (1<<subkey)))
|
||||
return FALSE;
|
||||
|
||||
fcache->io_sector = sector;
|
||||
for (fcache->io_cnt = 1;
|
||||
fcache->io_cnt < fcache->io_max;
|
||||
fcache->io_cnt++) {
|
||||
if (!(val->unread_bitfield & (1 << (subkey + fcache->io_cnt))))
|
||||
break;
|
||||
}
|
||||
F_lseek_async(fcache->fp, sector);
|
||||
fop = F_read_async(fcache->fp, val->data[subkey],
|
||||
fcache->io_cnt*SECSZ, NULL);
|
||||
register_fop_whendone(fcache, fop, read_complete);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool_t enqueue_readahead(
|
||||
struct file_cache *fcache, FSIZE_t *pread_sector, uint16_t limit)
|
||||
{
|
||||
FSIZE_t read_sector = *pread_sector;
|
||||
if (!fcache->readahead
|
||||
|| fcache->readahead_start > read_sector
|
||||
|| read_sector >= fcache->readahead_end)
|
||||
return FALSE;
|
||||
|
||||
for (int i = 0; i < limit; i++) {
|
||||
read_sector += SECSZ;
|
||||
if (read_sector >= fcache->readahead_end)
|
||||
read_sector = fcache->readahead_start;
|
||||
if (read_start(fcache, read_sector))
|
||||
return TRUE;
|
||||
}
|
||||
*pread_sector = read_sector;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void enqueue_io(struct file_cache *fcache)
|
||||
{
|
||||
FSIZE_t read_sector = fcache->cur_sector;
|
||||
|
||||
if (fcache->fop_cb != NULL)
|
||||
return;
|
||||
|
||||
/* Read high-priority sectors. */
|
||||
if (read_sector != UINT_MAX) {
|
||||
if (read_start(fcache, read_sector))
|
||||
return;
|
||||
if (enqueue_readahead(fcache, &read_sector,
|
||||
fcache->readahead_prio))
|
||||
return;
|
||||
}
|
||||
/* Write. */
|
||||
if (fcache->dirty_entries) {
|
||||
write_start(fcache, fcache->dirty_key, fcache->dirty_val);
|
||||
return;
|
||||
}
|
||||
if (fcache->sync_requested) {
|
||||
fcache->sync_requested = FALSE;
|
||||
register_fop_whendone(fcache, F_sync_async(fcache->fp), sync_complete);
|
||||
return;
|
||||
}
|
||||
/* Read low-priority sectors. */
|
||||
if (read_sector != UINT_MAX) {
|
||||
if (enqueue_readahead(fcache, &read_sector, fcache->readahead_sectors))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
215
src/floppy.c
215
src/floppy.c
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#define GPI_bus GPI_floating
|
||||
#define GPO_bus GPO_pushpull(_2MHz,O_FALSE)
|
||||
#define AFO_bus (AFO_pushpull(_2MHz) | (O_FALSE<<4))
|
||||
#define AFO_bus _AFO_pushpull(_2MHz,O_FALSE)
|
||||
|
||||
#define GPO_rdata GPO_bus
|
||||
#define AFO_rdata AFO_bus
|
||||
|
|
@ -20,12 +20,14 @@
|
|||
static void chgrst_timer(void *_drv);
|
||||
static void drive_step_timer(void *_drv);
|
||||
static void motor_spinup_timer(void *_drv);
|
||||
void IRQ_43(void) __attribute__((alias("IRQ_soft")));
|
||||
#define FLOPPY_SOFTIRQ 43
|
||||
|
||||
#define FLOPPY_SOFTIRQ SOFTIRQ_0
|
||||
DEFINE_IRQ(FLOPPY_SOFTIRQ, "IRQ_soft");
|
||||
|
||||
/* Index-pulse timer functions. */
|
||||
static void index_assert(void *); /* index.timer */
|
||||
static void index_deassert(void *); /* index.timer_deassert */
|
||||
static void index_custom_assert(void *); /* index.custom_timer */
|
||||
|
||||
static time_t sync_time, sync_pos;
|
||||
|
||||
|
|
@ -33,6 +35,8 @@ static time_t prefetch_start_time;
|
|||
static uint32_t max_prefetch_us;
|
||||
|
||||
struct drive;
|
||||
static always_inline void drive_change_pin(
|
||||
struct drive *drv, uint8_t pin, bool_t assert);
|
||||
static always_inline void drive_change_output(
|
||||
struct drive *drv, uint8_t outp, bool_t assert);
|
||||
|
||||
|
|
@ -68,7 +72,7 @@ const static struct fintf {
|
|||
static always_inline void drive_change_pin(
|
||||
struct drive *drv, uint8_t pin, bool_t assert)
|
||||
{
|
||||
uint16_t pin_mask = m(pin);
|
||||
uint32_t pin_mask = m(pin);
|
||||
|
||||
/* Logically assert or deassert the pin. */
|
||||
if (assert)
|
||||
|
|
@ -77,8 +81,10 @@ static always_inline void drive_change_pin(
|
|||
gpio_out_active &= ~pin_mask;
|
||||
|
||||
/* Update the physical output pin, if the drive is selected. */
|
||||
if (drv->sel)
|
||||
gpio_write_pins(gpio_out, pin_mask, assert ? O_TRUE : O_FALSE);
|
||||
if (drv->sel) {
|
||||
gpio_write_pins(gpiob, (uint16_t)pin_mask, assert ? O_TRUE : O_FALSE);
|
||||
gpio_write_pins(gpioa, pin_mask>>16, assert ? O_TRUE : O_FALSE);
|
||||
}
|
||||
|
||||
/* Caller expects us to re-enable interrupts. */
|
||||
IRQ_global_enable();
|
||||
|
|
@ -127,13 +133,20 @@ static void drive_change_output(
|
|||
|
||||
static void update_amiga_id(struct drive *drv, bool_t amiga_hd_id)
|
||||
{
|
||||
/* Only for the Amiga interface, with hacked RDY (pin 34) signal. */
|
||||
if (fintf_mode != FINTF_AMIGA)
|
||||
/* JC and pin 34 are overridden only for the Amiga interface. */
|
||||
if (fintf_mode != FINTF_AMIGA) {
|
||||
drv->amiga_pin34 = FALSE;
|
||||
board_jc_set_mode(GPI_pull_up);
|
||||
return;
|
||||
}
|
||||
|
||||
/* JC and HDEN are set according to Amiga density. */
|
||||
board_jc_set_mode(GPO_opendrain(_2MHz, amiga_hd_id));
|
||||
drive_change_output(drv, outp_hden, amiga_hd_id);
|
||||
|
||||
if (pin34 != outp_unused)
|
||||
/* If pin 34 is explicitly configured, we do not mess with it. */
|
||||
drv->amiga_pin34 = (pin34 == outp_unused);
|
||||
if (!drv->amiga_pin34)
|
||||
return;
|
||||
|
||||
IRQ_global_disable();
|
||||
|
|
@ -142,17 +155,20 @@ static void update_amiga_id(struct drive *drv, bool_t amiga_hd_id)
|
|||
* every time the drive is selected. */
|
||||
update_SELA_irq(amiga_hd_id);
|
||||
|
||||
/* DD-ID: !!HACK!! We permanently assert pin 34, even when no disk is
|
||||
* inserted. Properly we should only do this when MTR is asserted. */
|
||||
/* HD ID: !!HACK!! Without knowledge of MTR signal we cannot synchronise
|
||||
* the HD-ID sequence 101010... with the host poll loop. It turns out that
|
||||
* starting with pin 34 asserted when the HD image is mounted seems to
|
||||
* generally work! */
|
||||
gpio_out_active |= m(pin_34);
|
||||
if (drive.sel)
|
||||
gpio_write_pins(gpio_out, m(pin_34), O_TRUE);
|
||||
|
||||
IRQ_global_enable();
|
||||
if (ff_cfg.motor_delay == MOTOR_ignore) {
|
||||
/* Best-effort pin 34 handling:
|
||||
* DD-ID: We permanently assert pin 34, even when no disk is inserted.
|
||||
* Properly we should only do this when MTR is asserted.
|
||||
* HD-ID: Without knowledge of MTR signal we cannot synchronise the
|
||||
* HD-ID sequence 101010... with the host poll loop. It turns out that
|
||||
* starting with pin 34 asserted when the HD image is mounted seems to
|
||||
* generally work! (But see GitHub issue #354) */
|
||||
drive_change_pin(&drive, pin_34, TRUE);
|
||||
} else {
|
||||
/* Do nothing here. Pin 34 will be updated by IRQ_MOTOR() via
|
||||
* motor_chgrst_update_status(). */
|
||||
IRQ_global_enable();
|
||||
}
|
||||
}
|
||||
|
||||
void floppy_cancel(void)
|
||||
|
|
@ -180,6 +196,7 @@ void floppy_cancel(void)
|
|||
/* Clear soft state. */
|
||||
timer_cancel(&drv->chgrst_timer);
|
||||
timer_cancel(&index.timer);
|
||||
timer_cancel(&index.custom_timer);
|
||||
barrier(); /* cancel index.timer /then/ clear dma rings */
|
||||
dma_rd = dma_wr = NULL;
|
||||
barrier(); /* /then/ clear soft state */
|
||||
|
|
@ -189,12 +206,22 @@ void floppy_cancel(void)
|
|||
index.fake_fired = FALSE;
|
||||
barrier(); /* /then/ cancel index.timer_deassert */
|
||||
timer_cancel(&index.timer_deassert);
|
||||
motor_chgrst_eject(drv);
|
||||
motor_chgrst_update_status(drv);
|
||||
|
||||
/* Set outputs for empty drive. */
|
||||
barrier();
|
||||
drive_change_output(drv, outp_index, FALSE);
|
||||
drive_change_output(drv, outp_dskchg, TRUE);
|
||||
|
||||
/* Clean up I/O. This must avoid potential cancel_call()s while still
|
||||
* getting volume communication into a consistent state. */
|
||||
F_async_cancel_all();
|
||||
/* cancel_call() circumvents the threading subsystem and may leave it in an
|
||||
* incoherent state. Volume operations never cancel so it is safe to
|
||||
* thread_yield() if a volume operation is in progress. */
|
||||
while (volume_interrupt())
|
||||
thread_yield();
|
||||
thread_reset();
|
||||
}
|
||||
|
||||
void floppy_set_fintf_mode(void)
|
||||
|
|
@ -221,7 +248,7 @@ void floppy_set_fintf_mode(void)
|
|||
/* Jumper JC selects default floppy interface configuration:
|
||||
* - No Jumper: Shugart
|
||||
* - Jumpered: IBM PC */
|
||||
mode = gpio_read_pin(gpiob, 1) ? FINTF_SHUGART : FINTF_IBMPC;
|
||||
mode = board_jc_strapped() ? FINTF_IBMPC : FINTF_SHUGART;
|
||||
}
|
||||
|
||||
ASSERT(mode < ARRAY_SIZE(fintfs));
|
||||
|
|
@ -247,8 +274,12 @@ void floppy_set_fintf_mode(void)
|
|||
update_SELA_irq(FALSE);
|
||||
|
||||
if (drv->sel) {
|
||||
gpio_write_pins(gpio_out, old_active & ~gpio_out_active, O_FALSE);
|
||||
gpio_write_pins(gpio_out, ~old_active & gpio_out_active, O_TRUE);
|
||||
uint32_t r = old_active & ~gpio_out_active;
|
||||
uint32_t s = ~old_active & gpio_out_active;
|
||||
gpio_write_pins(gpioa, r>>16, O_FALSE);
|
||||
gpio_write_pins(gpioa, s>>16, O_TRUE);
|
||||
gpio_write_pins(gpiob, (uint16_t)r, O_FALSE);
|
||||
gpio_write_pins(gpiob, (uint16_t)s, O_TRUE);
|
||||
}
|
||||
|
||||
IRQ_global_enable();
|
||||
|
|
@ -262,6 +293,24 @@ void floppy_set_fintf_mode(void)
|
|||
pin34_inverted ? "not-" : "", outp_name[pin34] ?: "?");
|
||||
}
|
||||
|
||||
void floppy_set_max_cyl(void)
|
||||
{
|
||||
struct drive *drv = &drive;
|
||||
IRQ_global_disable();
|
||||
if (drv->cyl > ff_cfg.max_cyl)
|
||||
drv->cyl = ff_cfg.max_cyl;
|
||||
IRQ_global_enable();
|
||||
}
|
||||
|
||||
static void drive_configure_output_pin(unsigned int pin)
|
||||
{
|
||||
if (pin >= 16) {
|
||||
gpio_configure_pin(gpioa, pin-16, GPO_bus);
|
||||
} else {
|
||||
gpio_configure_pin(gpiob, pin, GPO_bus);
|
||||
}
|
||||
}
|
||||
|
||||
void floppy_init(void)
|
||||
{
|
||||
struct drive *drv = &drive;
|
||||
|
|
@ -274,14 +323,11 @@ void floppy_init(void)
|
|||
timer_init(&drv->motor.timer, motor_spinup_timer, drv);
|
||||
timer_init(&drv->chgrst_timer, chgrst_timer, drv);
|
||||
|
||||
gpio_configure_pin(gpio_out, pin_02, GPO_bus);
|
||||
gpio_configure_pin(gpio_out, pin_08, GPO_bus);
|
||||
gpio_configure_pin(gpio_out, pin_26, GPO_bus);
|
||||
gpio_configure_pin(gpio_out, pin_28, GPO_bus);
|
||||
gpio_configure_pin(gpio_out, pin_34, GPO_bus);
|
||||
|
||||
gpio_configure_pin(gpio_data, pin_wdata, GPI_bus);
|
||||
gpio_configure_pin(gpio_data, pin_rdata, GPO_bus);
|
||||
drive_configure_output_pin(pin_02);
|
||||
drive_configure_output_pin(pin_08);
|
||||
drive_configure_output_pin(pin_26);
|
||||
drive_configure_output_pin(pin_28);
|
||||
drive_configure_output_pin(pin_34);
|
||||
|
||||
drive_change_output(drv, outp_dskchg, TRUE);
|
||||
drive_change_output(drv, outp_wrprot, TRUE);
|
||||
|
|
@ -294,8 +340,16 @@ void floppy_init(void)
|
|||
|
||||
timer_init(&index.timer, index_assert, NULL);
|
||||
timer_init(&index.timer_deassert, index_deassert, NULL);
|
||||
timer_init(&index.custom_timer, index_custom_assert, NULL);
|
||||
|
||||
motor_chgrst_eject(drv);
|
||||
motor_chgrst_setup_exti();
|
||||
}
|
||||
|
||||
static void io_thread_main(void *arg) {
|
||||
while (1) {
|
||||
F_async_drain();
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
|
||||
void floppy_insert(unsigned int unit, struct slot *slot)
|
||||
|
|
@ -309,10 +363,11 @@ void floppy_insert(unsigned int unit, struct slot *slot)
|
|||
floppy_mount(slot);
|
||||
im = image;
|
||||
|
||||
if (im->write_bc_ticks < sysclk_ns(1500))
|
||||
if (im->write_bc_ticks < sampleclk_ns(1500))
|
||||
drive_change_output(drv, outp_hden, TRUE);
|
||||
|
||||
timer_dma_init();
|
||||
thread_start(&drv->io_thread, _thread1_stacktop, io_thread_main, NULL);
|
||||
|
||||
/* Drive is ready. Set output signals appropriately. */
|
||||
update_amiga_id(drv, im->stk_per_rev > stk_ms(300));
|
||||
|
|
@ -320,7 +375,7 @@ void floppy_insert(unsigned int unit, struct slot *slot)
|
|||
drive_change_output(drv, outp_wrprot, FALSE);
|
||||
barrier();
|
||||
drv->inserted = TRUE;
|
||||
motor_chgrst_insert(drv); /* update RDY + motor state */
|
||||
motor_chgrst_update_status(drv); /* update RDY + motor state */
|
||||
if (ff_cfg.chgrst <= CHGRST_delay(15))
|
||||
timer_set(&drv->chgrst_timer, time_now() + ff_cfg.chgrst*time_ms(500));
|
||||
}
|
||||
|
|
@ -358,23 +413,21 @@ static void floppy_sync_flux(void)
|
|||
|
||||
if (!drv->index_suppressed) {
|
||||
ticks = time_diff(time_now(), sync_time) - time_us(1);
|
||||
if (ticks > time_ms(15)) {
|
||||
/* Too long to wait. Immediately re-sync index timing. */
|
||||
drv->index_suppressed = TRUE;
|
||||
printk("Trk %u: skip %ums\n",
|
||||
drv->image->cur_track, (ticks+time_us(500))/time_ms(1));
|
||||
} else if (ticks > time_ms(5)) {
|
||||
if (ticks > time_ms(5)) {
|
||||
/* A while to wait. Go do other work. */
|
||||
return;
|
||||
} else {
|
||||
if (ticks > 0)
|
||||
delay_ticks(ticks);
|
||||
/* If we're out of sync then forcibly re-sync index timing. */
|
||||
/* If we're out of sync then start over. */
|
||||
ticks = time_diff(time_now(), sync_time);
|
||||
if (ticks < -100) {
|
||||
drv->index_suppressed = TRUE;
|
||||
printk("Trk %u: late %uus\n",
|
||||
drv->image->cur_track, -ticks/time_us(1));
|
||||
|
||||
dma_rd->state = DMA_inactive;
|
||||
dma_rd->prod = dma_rd->cons;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (drv->step.state) {
|
||||
|
|
@ -396,11 +449,11 @@ static void floppy_sync_flux(void)
|
|||
uint32_t oldpri = IRQ_save(TIMER_IRQ_PRI);
|
||||
|
||||
timer_cancel(&index.timer);
|
||||
timer_cancel(&index.custom_timer);
|
||||
|
||||
/* If we crossed the index mark while filling the DMA buffer then we
|
||||
* need to set up the index pulse (usually done by IRQ_rdata_dma). */
|
||||
if (image_ticks_since_index(drv->image)
|
||||
< (sync_pos*(SYSCLK_MHZ/STK_MHZ))) {
|
||||
if (image_ticks_since_index(drv->image) < sampleclk_stk(sync_pos)) {
|
||||
|
||||
/* Sum all flux timings in the DMA buffer. */
|
||||
const uint16_t buf_mask = ARRAY_SIZE(dma_rd->buf) - 1;
|
||||
|
|
@ -412,10 +465,21 @@ static void floppy_sync_flux(void)
|
|||
ticks -= image_ticks_since_index(drv->image);
|
||||
|
||||
/* Calculate deadline for index timer. */
|
||||
ticks /= SYSCLK_MHZ/TIME_MHZ;
|
||||
ticks /= SAMPLECLK_MHZ/TIME_MHZ;
|
||||
timer_set(&index.timer, time_now() + ticks);
|
||||
}
|
||||
|
||||
/* Realign custom_timer to index. */
|
||||
if (index.custom_pulses_len) {
|
||||
int i;
|
||||
for (i = 0; i < index.custom_pulses_len; i++)
|
||||
if (sync_pos < index.custom_pulses[i])
|
||||
break;
|
||||
if (i < index.custom_pulses_len)
|
||||
timer_set(&index.custom_timer,
|
||||
time_now() - sync_pos + index.custom_pulses[i]);
|
||||
}
|
||||
|
||||
IRQ_global_disable();
|
||||
IRQ_restore(oldpri);
|
||||
index.prev_time = time_now() - sync_pos;
|
||||
|
|
@ -433,6 +497,7 @@ static bool_t dma_rd_handle(struct drive *drv)
|
|||
struct image *im = drv->image;
|
||||
time_t index_time, read_start_pos;
|
||||
unsigned int track;
|
||||
time_t start_time = time_now();
|
||||
/* Allow 10ms from current rotational position to load new track */
|
||||
int32_t delay = time_ms(10);
|
||||
/* Allow extra time if heads are settling. */
|
||||
|
|
@ -454,22 +519,29 @@ static bool_t dma_rd_handle(struct drive *drv)
|
|||
read_start_pos %= im->stk_per_rev;
|
||||
/* Seek to the new track. */
|
||||
track = drive_calc_track(drv);
|
||||
read_start_pos *= SYSCLK_MHZ/STK_MHZ;
|
||||
if (in_da_mode(im, track>>1) && (drv->outp & m(outp_wrprot))
|
||||
&& !volume_readonly()) {
|
||||
/* Remove write-protect when driven into D-A mode. */
|
||||
drive_change_output(drv, outp_wrprot, FALSE);
|
||||
}
|
||||
if (image_setup_track(im, track, &read_start_pos))
|
||||
if (in_da_mode(im, track>>1) != image_in_da_mode(im)) {
|
||||
/* Changing D-A mode requires changing image handler and may need
|
||||
* to re-read the config file since D-A can change it. */
|
||||
return TRUE;
|
||||
}
|
||||
read_start_pos *= SAMPLECLK_MHZ/STK_MHZ;
|
||||
image_setup_track(im, track, &read_start_pos);
|
||||
prefetch_start_time = time_now();
|
||||
read_start_pos /= SYSCLK_MHZ/STK_MHZ;
|
||||
read_start_pos /= SAMPLECLK_MHZ/STK_MHZ;
|
||||
sync_pos = read_start_pos;
|
||||
if (!drv->index_suppressed) {
|
||||
/* Set the deadline to match existing index timing. */
|
||||
time_t now = time_now();
|
||||
sync_time = index_time + read_start_pos;
|
||||
if (time_diff(time_now(), sync_time) < 0)
|
||||
if (time_diff(now, sync_time) < 0)
|
||||
sync_time += im->stk_per_rev;
|
||||
if (time_diff(now, sync_time) < 0
|
||||
|| time_diff(now, sync_time) > delay) {
|
||||
/* image_setup_track() consumed the entire delay. Try again. */
|
||||
time_t ticks = time_since(start_time) - delay;
|
||||
printk("Trk %u: trk late %uus\n", track, ticks/time_us(1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Change state /then/ check for race against step or side change. */
|
||||
dma_rd->state = DMA_starting;
|
||||
|
|
@ -545,10 +617,13 @@ static void index_assert(void *dat)
|
|||
{
|
||||
struct drive *drv = &drive;
|
||||
index.prev_time = index.timer.deadline;
|
||||
if (drv->motor.on && !index_is_suppressed(drv)) {
|
||||
drive_change_output(drv, outp_index, TRUE);
|
||||
timer_set(&index.timer_deassert, index.prev_time + time_ms(2));
|
||||
}
|
||||
if (index.custom_pulses_len)
|
||||
timer_set(&index.custom_timer, index.prev_time + index.custom_pulses[0]);
|
||||
else
|
||||
if (drv->motor.on && !index_is_suppressed(drv)) {
|
||||
drive_change_output(drv, outp_index, TRUE);
|
||||
timer_set(&index.timer_deassert, index.prev_time + time_ms(2));
|
||||
}
|
||||
if (dma_rd->state != DMA_active) /* timer set from input flux stream */
|
||||
timer_set(&index.timer, index.prev_time + drv->image->stk_per_rev);
|
||||
}
|
||||
|
|
@ -559,6 +634,24 @@ static void index_deassert(void *dat)
|
|||
drive_change_output(drv, outp_index, FALSE);
|
||||
}
|
||||
|
||||
static void index_custom_assert(void *dat)
|
||||
{
|
||||
struct drive *drv = &drive;
|
||||
time_t current_pulse_pos;
|
||||
int i;
|
||||
if (drv->motor.on && !index_is_suppressed(drv)) {
|
||||
drive_change_output(drv, outp_index, TRUE);
|
||||
timer_set(&index.timer_deassert,
|
||||
index.custom_timer.deadline + time_ms(2));
|
||||
}
|
||||
current_pulse_pos = time_since(index.prev_time);
|
||||
for (i = 0; i < index.custom_pulses_len; i++)
|
||||
if (current_pulse_pos < index.custom_pulses[i])
|
||||
break;
|
||||
if (i < index.custom_pulses_len)
|
||||
timer_set(&index.custom_timer, index.prev_time + index.custom_pulses[i]);
|
||||
}
|
||||
|
||||
static void chgrst_timer(void *_drv)
|
||||
{
|
||||
struct drive *drv = _drv;
|
||||
|
|
@ -596,6 +689,10 @@ static void motor_spinup_timer(void *_drv)
|
|||
struct drive *drv = _drv;
|
||||
|
||||
drv->motor.on = TRUE;
|
||||
if (drv->amiga_pin34) {
|
||||
IRQ_global_disable();
|
||||
drive_change_pin(drv, pin_34, TRUE);
|
||||
}
|
||||
drive_change_output(drv, outp_rdy, TRUE);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@
|
|||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
#define m(bitnr) (1u<<(bitnr))
|
||||
|
||||
/* A DMA buffer for running a timer associated with a floppy-data I/O pin. */
|
||||
struct dma_ring {
|
||||
/* Current state of DMA (RDATA):
|
||||
|
|
@ -53,6 +51,7 @@ static struct drive {
|
|||
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;
|
||||
|
|
@ -73,16 +72,23 @@ static struct drive {
|
|||
} step;
|
||||
uint32_t restart_pos;
|
||||
struct image *image;
|
||||
struct thread io_thread;
|
||||
} drive;
|
||||
|
||||
static struct image *image;
|
||||
|
||||
static struct {
|
||||
struct timer timer, timer_deassert;
|
||||
struct timer custom_timer;
|
||||
time_t prev_time;
|
||||
bool_t fake_fired;
|
||||
uint8_t custom_pulses_ver;
|
||||
uint8_t custom_pulses_len;
|
||||
/* Durations relative to track start. Must be in increasing order. */
|
||||
time_t custom_pulses[MAX_CUSTOM_PULSES];
|
||||
} index;
|
||||
|
||||
static unsigned int drive_calc_track(struct drive *drv);
|
||||
static void rdata_stop(void);
|
||||
static void wdata_start(void);
|
||||
static void wdata_stop(void);
|
||||
|
|
@ -94,6 +100,7 @@ struct exti_irq {
|
|||
|
||||
#if defined(QUICKDISK)
|
||||
#include "gotek/quickdisk.c"
|
||||
#define dma_rd_set_active(x) ((void)(x))
|
||||
#else
|
||||
#include "gotek/floppy.c"
|
||||
#endif
|
||||
|
|
@ -182,8 +189,9 @@ static void floppy_mount(struct slot *slot)
|
|||
/* ~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 = 32*1024; /* 32kB, power of two. */
|
||||
/* Size at least 4kb so a 512 byte sector (1k bc) can be fully
|
||||
* encoded into the 2kb read_bc, with space to spare. */
|
||||
im->bufs.write_bc.len = 4*1024; /* Power of two. */
|
||||
im->bufs.write_bc.p = arena_alloc(im->bufs.write_bc.len);
|
||||
|
||||
/* Read BC buffer overlaps the second half of the write BC buffer. This
|
||||
|
|
@ -212,7 +220,24 @@ static void floppy_mount(struct slot *slot)
|
|||
ASSERT(im->bufs.read_data.len >= 10*1024);
|
||||
|
||||
/* Mount the image file. */
|
||||
image_open(im, slot, cltbl);
|
||||
#if defined(QUICKDISK)
|
||||
image_open(im, slot, cltbl, FALSE);
|
||||
#else
|
||||
{
|
||||
uint8_t cyl = drive_calc_track(drv)>>1;
|
||||
/* Skip useless loading if D-A guaranteed. */
|
||||
if (cyl != 255)
|
||||
image_open(im, slot, cltbl, FALSE);
|
||||
/* Now that the number of image cylinders is known, do a precise D-A
|
||||
* check. */
|
||||
if (in_da_mode(im, cyl)) {
|
||||
volume_cache_destroy(); /* Clean up after other image format. */
|
||||
image_open(im, slot, cltbl, TRUE);
|
||||
/* Remove write-protect when driven into D-A mode. */
|
||||
drive_change_output(drv, outp_wrprot, FALSE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!im->disk_handler->write_track || volume_readonly())
|
||||
slot->attributes |= AM_RDO;
|
||||
if (slot->attributes & AM_RDO) {
|
||||
|
|
@ -238,6 +263,8 @@ static void floppy_mount(struct slot *slot)
|
|||
|
||||
drv->index_suppressed = FALSE;
|
||||
index.prev_time = time_now();
|
||||
index.custom_pulses_len = 0;
|
||||
index.custom_pulses_ver = 0xFF;
|
||||
}
|
||||
|
||||
/* Initialise timers and DMA for RDATA/WDATA. */
|
||||
|
|
@ -251,17 +278,17 @@ static void timer_dma_init(void)
|
|||
IRQx_enable(dma_wdata_irq);
|
||||
|
||||
/* RDATA Timer setup:
|
||||
* The counter is incremented at full SYSCLK rate.
|
||||
* The counter is incremented at SAMPLECLK rate.
|
||||
*
|
||||
* Ch.2 (RDATA) is in PWM mode 1. It outputs O_TRUE for 400ns and then
|
||||
* 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 = 0;
|
||||
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 = sysclk_ns(400);
|
||||
tim_rdata->ccr2 = sampleclk_ns(400);
|
||||
tim_rdata->dier = TIM_DIER_UDE;
|
||||
tim_rdata->cr2 = 0;
|
||||
|
||||
|
|
@ -280,13 +307,13 @@ static void timer_dma_init(void)
|
|||
DMA_CCR_EN);
|
||||
|
||||
/* WDATA Timer setup:
|
||||
* The counter runs from 0x0000-0xFFFF inclusive at full SYSCLK rate.
|
||||
* The counter runs from 0x0000-0xFFFF inclusive at SAMPLECLK rate.
|
||||
*
|
||||
* Ch.1 (WDATA) is in Input Capture mode, sampling on every clock and with
|
||||
* 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 = 0;
|
||||
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;
|
||||
|
|
@ -364,7 +391,7 @@ static void wdata_stop(void)
|
|||
image->wr_prod++;
|
||||
|
||||
#if !defined(QUICKDISK)
|
||||
if (!ff_cfg.index_suppression) {
|
||||
if (!ff_cfg.index_suppression && ff_cfg.write_drain != WDRAIN_realtime) {
|
||||
/* Opportunistically insert an INDEX pulse ahead of writeback. */
|
||||
drive_change_output(drv, outp_index, TRUE);
|
||||
index.fake_fired = TRUE;
|
||||
|
|
@ -407,10 +434,10 @@ static void wdata_start(void)
|
|||
tim_wdata->ccer = TIM_CCER_CC1E | TIM_CCER_CC1P;
|
||||
tim_wdata->cr1 = TIM_CR1_CEN;
|
||||
|
||||
/* Find rotational start position of the write, in SYSCLK ticks. */
|
||||
/* Find rotational start position of the write, in SAMPLECLK ticks. */
|
||||
start_pos = max_t(int32_t, 0, time_diff(index.prev_time, time_now()));
|
||||
start_pos %= drive.image->stk_per_rev;
|
||||
start_pos *= SYSCLK_MHZ / STK_MHZ;
|
||||
start_pos *= SAMPLECLK_MHZ / STK_MHZ;
|
||||
write = get_write(image, image->wr_prod);
|
||||
write->start = start_pos;
|
||||
write->track = drive_calc_track(&drive);
|
||||
|
|
@ -433,6 +460,7 @@ static void rdata_stop(void)
|
|||
|
||||
/* 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)
|
||||
|
|
@ -461,6 +489,7 @@ static void rdata_start(void)
|
|||
goto out;
|
||||
|
||||
dma_rd->state = DMA_active;
|
||||
dma_rd_set_active(TRUE);
|
||||
|
||||
/* Start timer. */
|
||||
tim_rdata->egr = TIM_EGR_UG;
|
||||
|
|
@ -510,8 +539,7 @@ static bool_t dma_wr_handle(struct drive *drv)
|
|||
}
|
||||
|
||||
/* Set up the track for writing. */
|
||||
if (image_setup_track(im, write->track, NULL))
|
||||
return TRUE;
|
||||
image_setup_track(im, write->track, NULL);
|
||||
|
||||
drv->writing = TRUE;
|
||||
|
||||
|
|
@ -530,9 +558,6 @@ static bool_t dma_wr_handle(struct drive *drv)
|
|||
/* Align the bitcell consumer index for start of next write. */
|
||||
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++;
|
||||
|
|
@ -608,6 +633,30 @@ static void IRQ_rdata_dma(void)
|
|||
IRQx_set_pending(dma_rdata_irq);
|
||||
}
|
||||
|
||||
#if !defined(QUICKDISK)
|
||||
ASSERT(drv->image->index_pulses_len < MAX_CUSTOM_PULSES);
|
||||
if (drv->image->index_pulses_ver != index.custom_pulses_ver) {
|
||||
time_t current_pulse_pos = time_since(index.prev_time);
|
||||
uint32_t oldpri;
|
||||
|
||||
oldpri = IRQ_save(TIMER_IRQ_PRI);
|
||||
for (i = 0; i < drv->image->index_pulses_len; i++)
|
||||
index.custom_pulses[i] = (drv->image->index_pulses[i]>>4)
|
||||
/ (SAMPLECLK_MHZ/TIME_MHZ);
|
||||
index.custom_pulses_len = drv->image->index_pulses_len;
|
||||
index.custom_pulses_ver = drv->image->index_pulses_ver;
|
||||
|
||||
for (i = 0; i < index.custom_pulses_len; i++)
|
||||
if (current_pulse_pos <= index.custom_pulses[i])
|
||||
break;
|
||||
if (i < index.custom_pulses_len)
|
||||
timer_set(&index.custom_timer, index.prev_time + index.custom_pulses[i]);
|
||||
else
|
||||
timer_cancel(&index.custom_timer);
|
||||
IRQ_restore(oldpri);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check if we have crossed the index mark. If not, we're done. */
|
||||
if (image_ticks_since_index(drv->image) >= prev_ticks_since_index)
|
||||
return;
|
||||
|
|
@ -633,7 +682,7 @@ static void IRQ_rdata_dma(void)
|
|||
/* Subtract current flux offset beyond the index. */
|
||||
ticks -= image_ticks_since_index(drv->image);
|
||||
/* Calculate deadline for index timer. */
|
||||
ticks /= SYSCLK_MHZ/TIME_MHZ;
|
||||
ticks /= SAMPLECLK_MHZ/TIME_MHZ;
|
||||
timer_set(&index.timer, now + ticks);
|
||||
}
|
||||
|
||||
|
|
@ -722,6 +771,14 @@ static void IRQ_wdata_dma(void)
|
|||
dma_wr->prev_sample = prev;
|
||||
}
|
||||
|
||||
void floppy_sync(void)
|
||||
{
|
||||
struct drive *drv = &drive;
|
||||
struct image *im = drv->image;
|
||||
|
||||
image_sync(im);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
|
|
|
|||
62
src/fpec_at32f435.c
Normal file
62
src/fpec_at32f435.c
Normal 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:
|
||||
*/
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* fpec.c
|
||||
* fpec_stm32f105.c
|
||||
*
|
||||
* STM32F10x Flash Memory Program/Erase Controller (FPEC).
|
||||
*
|
||||
205
src/fs_async.c
Normal file
205
src/fs_async.c
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* fs_async.c
|
||||
*
|
||||
* Non-blocking error-handling wrappers around FatFS.
|
||||
*
|
||||
* Written & released by Keir Fraser <keir.xen@gmail.com> and Eric Anderson
|
||||
* <ejona86@gmail.com>
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
union op_args {
|
||||
struct {
|
||||
FSIZE_t ofs;
|
||||
} lseek;
|
||||
struct {
|
||||
void *buff;
|
||||
UINT btr;
|
||||
UINT *br;
|
||||
} read;
|
||||
struct {
|
||||
const void *buff;
|
||||
UINT btw;
|
||||
UINT *bw;
|
||||
} write;
|
||||
struct {
|
||||
BYTE *buff;
|
||||
LBA_t sector;
|
||||
UINT count;
|
||||
} disk_read;
|
||||
struct {
|
||||
const BYTE *buff;
|
||||
LBA_t sector;
|
||||
UINT count;
|
||||
} disk_write;
|
||||
struct {
|
||||
BYTE cmd;
|
||||
void* buff;
|
||||
DRESULT *res;
|
||||
} disk_ioctl;
|
||||
};
|
||||
|
||||
struct op;
|
||||
|
||||
typedef void (*f_op_func)(struct op *op);
|
||||
|
||||
struct op {
|
||||
f_op_func func;
|
||||
FIL *fp;
|
||||
union op_args args;
|
||||
bool_t cancelled;
|
||||
};
|
||||
|
||||
#define OPS_LEN 4 /* Power of 2. */
|
||||
#define OPS_MASK(x) ((x)&(OPS_LEN-1))
|
||||
static struct {
|
||||
struct op ops[OPS_LEN];
|
||||
int prod, cons;
|
||||
} f_async_queue;
|
||||
|
||||
bool_t F_async_isdone(FOP oper) {
|
||||
ASSERT(oper - f_async_queue.prod < 0);
|
||||
return oper - f_async_queue.cons < 0;
|
||||
}
|
||||
|
||||
void F_async_wait(FOP oper) {
|
||||
while (!F_async_isdone(oper))
|
||||
thread_yield();
|
||||
}
|
||||
|
||||
void F_async_cancel(FOP oper) {
|
||||
if (F_async_isdone(oper))
|
||||
return;
|
||||
f_async_queue.ops[OPS_MASK(oper)].cancelled = TRUE;
|
||||
}
|
||||
|
||||
void F_async_cancel_all(void) {
|
||||
for (int i = 0; i < OPS_LEN; i++)
|
||||
f_async_queue.ops[i].cancelled = TRUE;
|
||||
}
|
||||
|
||||
FOP F_async_get_completed_op(void) {
|
||||
return f_async_queue.cons - 4; /* "Random" op. Chosen by fair dice roll. */
|
||||
}
|
||||
|
||||
static FOP enqueue(f_op_func func, FIL *fp, union op_args *args) {
|
||||
struct op *op;
|
||||
bool_t printed = FALSE;
|
||||
while (f_async_queue.prod - f_async_queue.cons >= OPS_LEN) {
|
||||
if (!printed) {
|
||||
printk("async queue full; blocking\n");
|
||||
printed = TRUE;
|
||||
}
|
||||
thread_yield();
|
||||
}
|
||||
op = &f_async_queue.ops[OPS_MASK(f_async_queue.prod)];
|
||||
op->func = func;
|
||||
op->fp = fp;
|
||||
op->args = *args;
|
||||
op->cancelled = FALSE;
|
||||
return f_async_queue.prod++;
|
||||
}
|
||||
|
||||
void F_async_drain(void) {
|
||||
while (f_async_queue.prod != f_async_queue.cons) {
|
||||
struct op *op = &f_async_queue.ops[OPS_MASK(f_async_queue.cons)];
|
||||
if (!op->cancelled) {
|
||||
op->func(op);
|
||||
}
|
||||
f_async_queue.cons++;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_lseek(struct op *op) {
|
||||
/* Short-circuit if already appropriately positioned. The caller of
|
||||
* F_lseek_async can't check fptr themselves like they could using the
|
||||
* blocking API. */
|
||||
if (op->args.lseek.ofs == op->fp->fptr)
|
||||
return;
|
||||
F_lseek(op->fp, op->args.lseek.ofs);
|
||||
}
|
||||
|
||||
FOP F_lseek_async(FIL *fp, FSIZE_t ofs) {
|
||||
union op_args args = { .lseek = {ofs} };
|
||||
return enqueue(do_lseek, fp, &args);
|
||||
}
|
||||
|
||||
static void do_read(struct op *op) {
|
||||
time_t start = time_now(), duration;
|
||||
F_read(op->fp, op->args.read.buff, op->args.read.btr, op->args.read.br);
|
||||
duration = time_since(start);
|
||||
if (duration > time_us(9000))
|
||||
printk("Lengthy %s. %d bytes took %dus\n", "read", op->args.read.btr,
|
||||
duration / TIME_MHZ);
|
||||
}
|
||||
|
||||
FOP F_read_async(FIL *fp, void *buff, UINT btr, UINT *br) {
|
||||
union op_args args = { .read = {buff, btr, br} };
|
||||
return enqueue(do_read, fp, &args);
|
||||
}
|
||||
|
||||
static void do_write(struct op *op) {
|
||||
time_t start = time_now(), duration;
|
||||
F_write(op->fp, op->args.write.buff, op->args.write.btw, op->args.write.bw);
|
||||
duration = time_since(start);
|
||||
if (duration > time_us(9000))
|
||||
printk("Lengthy %s. %d bytes took %dus\n", "write", op->args.write.btw,
|
||||
duration / TIME_MHZ);
|
||||
}
|
||||
|
||||
FOP F_write_async(FIL *fp, const void *buff, UINT btw, UINT *bw) {
|
||||
union op_args args = { .write = {buff, btw, bw} };
|
||||
return enqueue(do_write, fp, &args);
|
||||
}
|
||||
|
||||
static void do_sync(struct op *op) {
|
||||
F_sync(op->fp);
|
||||
}
|
||||
|
||||
FOP F_sync_async(FIL *fp) {
|
||||
union op_args args = { 0 };
|
||||
return enqueue(do_sync, fp, &args);
|
||||
}
|
||||
|
||||
static void do_disk_read(struct op *op) {
|
||||
if (disk_read((uintptr_t) op->fp, op->args.disk_read.buff,
|
||||
op->args.disk_read.sector, op->args.disk_read.count) != RES_OK)
|
||||
F_die(FR_DISK_ERR);
|
||||
}
|
||||
|
||||
FOP disk_read_async(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
|
||||
union op_args args = { .disk_read = {buff, sector, count} };
|
||||
return enqueue(do_disk_read, (void*)(uintptr_t) pdrv, &args);
|
||||
}
|
||||
|
||||
static void do_disk_write(struct op *op) {
|
||||
time_t start = time_now();
|
||||
if (disk_write((uintptr_t) op->fp, op->args.disk_write.buff,
|
||||
op->args.disk_write.sector, op->args.disk_write.count) != RES_OK)
|
||||
F_die(FR_DISK_ERR);
|
||||
printk("Disk write %08x[%2d]: %dus\n",
|
||||
op->args.disk_write.sector,
|
||||
op->args.disk_write.count,
|
||||
time_since(start) / TIME_MHZ);
|
||||
}
|
||||
|
||||
FOP disk_write_async(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
|
||||
union op_args args = { .disk_write = {buff, sector, count} };
|
||||
return enqueue(do_disk_write, (void*)(uintptr_t) pdrv, &args);
|
||||
}
|
||||
|
||||
static void do_disk_ioctl(struct op *op) {
|
||||
DRESULT res = disk_ioctl((uintptr_t) op->fp, op->args.disk_ioctl.cmd,
|
||||
op->args.disk_ioctl.buff);
|
||||
if (op->args.disk_ioctl.res != NULL)
|
||||
*op->args.disk_ioctl.res = res;
|
||||
else if (res != RES_OK)
|
||||
F_die(FR_DISK_ERR);
|
||||
}
|
||||
|
||||
FOP disk_ioctl_async(BYTE pdrv, BYTE cmd, void* buff, DRESULT *res) {
|
||||
union op_args args = { .disk_ioctl = {cmd, buff, res} };
|
||||
return enqueue(do_disk_ioctl, (void*)(uintptr_t) pdrv, &args);
|
||||
}
|
||||
292
src/fw_update.c
292
src/fw_update.c
|
|
@ -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,10 +75,28 @@ 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;
|
||||
|
|
@ -82,13 +108,21 @@ static bool_t fw_update_requested(void)
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
@ -106,71 +140,69 @@ static void msg_display(const char *p)
|
|||
}
|
||||
}
|
||||
|
||||
int update(void *unused)
|
||||
static enum fail_code find_update_file(
|
||||
char *file_name, size_t file_name_size, const char *file_pattern)
|
||||
{
|
||||
/* FatFS state, local to this function, but off stack. */
|
||||
static FIL file;
|
||||
static DIR dp;
|
||||
static 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");
|
||||
|
|
@ -180,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();
|
||||
|
|
@ -225,19 +341,14 @@ static void display_setting(bool_t on)
|
|||
}
|
||||
}
|
||||
|
||||
#define B_LEFT 1
|
||||
#define B_RIGHT 2
|
||||
#define B_SELECT 4
|
||||
|
||||
static bool_t buttons_pressed(void)
|
||||
{
|
||||
unsigned int b = board_get_buttons() | osd_buttons_rx;
|
||||
return (
|
||||
/* Check for both LEFT and RIGHT buttons pressed. */
|
||||
(!gpio_read_pin(gpioc, 8) && !gpio_read_pin(gpioc, 7))
|
||||
|| ((osd_buttons_rx & (B_LEFT|B_RIGHT)) == (B_LEFT|B_RIGHT))
|
||||
((b & (B_LEFT|B_RIGHT)) == (B_LEFT|B_RIGHT))
|
||||
/* Also respond to third (SELECT) button on its own. */
|
||||
|| !gpio_read_pin(gpioc, 6)
|
||||
|| (osd_buttons_rx & B_SELECT));
|
||||
|| (b & B_SELECT));
|
||||
}
|
||||
|
||||
/* Wait for both buttons to be pressed (LOW) or not pressed (HIGH). Perform
|
||||
|
|
@ -252,10 +363,8 @@ static void wait_buttons(uint8_t level)
|
|||
x <<= 1;
|
||||
if (level) {
|
||||
/* All buttons must be released. */
|
||||
x |= gpio_read_pin(gpioc, 8)
|
||||
&& gpio_read_pin(gpioc, 7)
|
||||
&& gpio_read_pin(gpioc, 6)
|
||||
&& !osd_buttons_rx;
|
||||
unsigned int b = board_get_buttons() | osd_buttons_rx;
|
||||
x |= !b;
|
||||
} else {
|
||||
x |= buttons_pressed();
|
||||
}
|
||||
|
|
@ -266,23 +375,16 @@ 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:
|
||||
* 1. Buttons are pressed; or
|
||||
* 2. It was requested via main firmware; or
|
||||
* 3. No valid main firmware is found in Flash. */
|
||||
if (!buttons_pressed() && !fw_update_requested()) {
|
||||
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);
|
||||
|
|
@ -291,6 +393,7 @@ int main(void)
|
|||
"mov sp,%0 ; blx %1"
|
||||
:: "r" (sp), "r" (pc));
|
||||
}
|
||||
update_requested = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -303,11 +406,15 @@ 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();
|
||||
|
||||
|
|
@ -319,8 +426,7 @@ int main(void)
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -18,50 +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_CHGRST"))); /* EXTI15_10 */
|
||||
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[] = {
|
||||
/* SELA */ { 6, FLOPPY_IRQ_SEL_PRI, 0 },
|
||||
/* STEP */ { 7, FLOPPY_IRQ_STEP_PRI, m(pin_step) },
|
||||
/* STEP */ { 28, FLOPPY_IRQ_STEP_PRI, m(2) /* dummy */ },
|
||||
/* WGATE */ { 7, FLOPPY_IRQ_WGATE_PRI, 0 },
|
||||
/* SIDE */ { 10, TIMER_IRQ_PRI, 0 },
|
||||
/* 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();
|
||||
|
|
@ -79,37 +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)
|
||||
{
|
||||
uint32_t pins;
|
||||
p_dma_rd_active = get_bitband(&gpio_out_active, GPIO_OUT_DMA_RD_ACTIVE);
|
||||
|
||||
#if MCU == STM32F105
|
||||
|
||||
#define change_pin_mode(gpio, pin, mode) \
|
||||
gpio->crl = (gpio->crl & ~(0xfu<<((pin)<<2))) \
|
||||
| (((mode)&0xfu)<<((pin)<<2))
|
||||
|
||||
gpio_configure_pin(gpioa, pin_step, GPI_bus);
|
||||
gpio_configure_pin(gpio_data, pin_wdata, GPI_bus);
|
||||
gpio_configure_pin(gpio_data, pin_rdata, GPO_bus);
|
||||
|
||||
#elif MCU == AT32F435
|
||||
|
||||
#define change_pin_mode(gpio, pin, mode) \
|
||||
gpio->moder = (gpio->moder & ~(0x3u<<((pin)<<1))) \
|
||||
| (((mode)&0x3u)<<((pin)<<1));
|
||||
#define afio syscfg
|
||||
|
||||
gpio_set_af(gpioa, pin_step, 1);
|
||||
gpio_configure_pin(gpioa, pin_step, AFI(PUPD_none));
|
||||
|
||||
gpio_set_af(gpio_data, pin_wdata, 1);
|
||||
gpio_configure_pin(gpio_data, pin_wdata, AFI(PUPD_none));
|
||||
|
||||
gpio_set_af(gpio_data, pin_rdata, 2);
|
||||
gpio_configure_pin(gpio_data, pin_rdata, GPO_bus);
|
||||
|
||||
dmamux1->cctrl[dma_wdata_ch-1] = DMAMUX_CCTRL_REQSEL(DMAMUX_REQ_TIM1_CH1);
|
||||
dmamux1->cctrl[dma_rdata_ch-1] = DMAMUX_CCTRL_REQSEL(DMAMUX_REQ_TIM3_OVF);
|
||||
|
||||
#endif
|
||||
|
||||
/* PA1 (STEP) triggers IRQ via TIM2 Channel 2, since EXTI is used for
|
||||
* WGATE on PB1. */
|
||||
tim2->ccmr1 = TIM_CCMR1_CC2S(TIM_CCS_INPUT_TI1);
|
||||
tim2->ccer = TIM_CCER_CC2E;
|
||||
tim2->dier = TIM_DIER_CC2IE;
|
||||
tim2->cr1 = TIM_CR1_CEN;
|
||||
|
||||
if (mcu_package == MCU_QFN32) {
|
||||
pin_02 = 16 + 14; /* PA14 */
|
||||
pin_26 = 16 + 13; /* PA13 */
|
||||
pin_wgate = 1; /* PB1 */
|
||||
}
|
||||
|
||||
gpio_configure_pin(gpiob, pin_dir, GPI_bus);
|
||||
gpio_configure_pin(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);
|
||||
|
||||
/* PA[15:14], PB[13:12], PC[11:10], PB[9:2], PA[1:0] */
|
||||
afio->exticr4 = 0x0011;
|
||||
afio->exticr3 = 0x2211;
|
||||
afio->exticr2 = 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);
|
||||
} 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);
|
||||
afio->exticr4 = 0x1011; /* Motor = PB15 */
|
||||
exti_route_pb(15); /* Motor = PB15 */
|
||||
}
|
||||
|
||||
pins = m(pin_wgate) | m(pin_side) | m(pin_sel0);
|
||||
exti->rtsr = pins | m(pin_motor) | m(pin_step) | m(11) | m(10);
|
||||
exti->ftsr = pins | m(pin_motor) | m(pin_chgrst) | m(11) | m(10);
|
||||
exti->imr = pins | m(pin_step);
|
||||
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
|
||||
|
|
@ -118,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. */
|
||||
|
|
@ -163,42 +223,37 @@ static void Amiga_HD_ID(uint32_t _gpio_out_active, uint32_t _gpio_out_setreset)
|
|||
* speculative entry point for the next interrupt. */
|
||||
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;
|
||||
|
|
@ -209,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)
|
||||
|
|
@ -241,7 +297,7 @@ static void IRQ_STEP_changed(void)
|
|||
idr_b = gpiob->idr;
|
||||
|
||||
/* Clear STEP-changed flag. */
|
||||
exti->pr = m(pin_step);
|
||||
(void)tim2->ccr2;
|
||||
|
||||
/* Bail if drive not selected. */
|
||||
if (idr_a & m(pin_sel0))
|
||||
|
|
@ -259,7 +315,7 @@ static void IRQ_STEP_changed(void)
|
|||
|
||||
/* Latch the step direction and check bounds (0 <= cyl <= 255). */
|
||||
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. */
|
||||
|
|
@ -269,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;
|
||||
|
|
@ -304,13 +361,10 @@ static void IRQ_SIDE_changed(void)
|
|||
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;
|
||||
|
|
@ -324,9 +378,29 @@ static void IRQ_WGATE_changed(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void IRQ_WGATE_rotary(void)
|
||||
{
|
||||
uint32_t rot_mask = board_rotary_exti_mask, pr = exti->pr;
|
||||
|
||||
/* Latch and clear PR[9:5] and PR[1]. */
|
||||
exti->pr = pr & 0x03e2;
|
||||
|
||||
if (pr & m(pin_wgate))
|
||||
IRQ_WGATE();
|
||||
|
||||
if (pr & rot_mask)
|
||||
IRQ_rotary();
|
||||
}
|
||||
|
||||
static void IRQ_MOTOR(struct drive *drv)
|
||||
{
|
||||
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.on = FALSE;
|
||||
|
|
@ -338,7 +412,7 @@ static void IRQ_MOTOR(struct drive *drv)
|
|||
/* Motor signal ignored -- 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 {
|
||||
|
|
@ -357,22 +431,26 @@ static void IRQ_CHGRST(struct drive *drv)
|
|||
}
|
||||
}
|
||||
|
||||
static void IRQ_MOTOR_CHGRST(void)
|
||||
static void IRQ_MOTOR_CHGRST_rotary(void)
|
||||
{
|
||||
struct drive *drv = &drive;
|
||||
bool_t changed = drv->motor.changed;
|
||||
uint32_t pr = exti->pr;
|
||||
uint32_t rot_mask = board_rotary_exti_mask, pr = exti->pr;
|
||||
|
||||
drv->motor.changed = FALSE;
|
||||
exti->pr = m(pin_motor) | m(pin_chgrst) | m(11) | m(10);
|
||||
|
||||
if ((pr & m(pin_motor)) || changed)
|
||||
/* Latch and clear PR[15:10] */
|
||||
exti->pr = pr & 0xfc00;
|
||||
|
||||
if (((pr & m(pin_motor)) && (ff_cfg.motor_delay != MOTOR_ignore))
|
||||
|| changed)
|
||||
IRQ_MOTOR(drv);
|
||||
|
||||
if ((pr & m(pin_chgrst)) || changed)
|
||||
if ((pr & m(pin_chgrst))
|
||||
|| changed)
|
||||
IRQ_CHGRST(drv);
|
||||
|
||||
if (pr & (m(11) | m(10)))
|
||||
if (pr & rot_mask)
|
||||
IRQ_rotary();
|
||||
}
|
||||
|
||||
|
|
@ -383,24 +461,25 @@ static void motor_chgrst_update_status(struct drive *drv)
|
|||
IRQx_set_pending(MOTOR_CHGRST_IRQ);
|
||||
}
|
||||
|
||||
static void motor_chgrst_insert(struct drive *drv)
|
||||
uint32_t motor_chgrst_exti_mask;
|
||||
void motor_chgrst_setup_exti(void)
|
||||
{
|
||||
uint32_t imr = exti->imr;
|
||||
uint32_t m = 0;
|
||||
|
||||
if (ff_cfg.motor_delay != MOTOR_ignore)
|
||||
imr |= m(pin_motor);
|
||||
if (ff_cfg.motor_delay != MOTOR_ignore) {
|
||||
_exti_route(gotek_enhanced()?0/*PA*/:1/*PB*/, pin_motor);
|
||||
m |= m(pin_motor);
|
||||
}
|
||||
|
||||
if (ff_cfg.chgrst == CHGRST_pa14)
|
||||
imr |= m(pin_chgrst);
|
||||
if (ff_cfg.chgrst == CHGRST_pa14) {
|
||||
exti_route_pa(pin_chgrst);
|
||||
m |= m(pin_chgrst);
|
||||
}
|
||||
|
||||
exti->imr = imr;
|
||||
motor_chgrst_update_status(drv);
|
||||
}
|
||||
motor_chgrst_exti_mask = m;
|
||||
exti->imr |= m;
|
||||
|
||||
static void motor_chgrst_eject(struct drive *drv)
|
||||
{
|
||||
exti->imr &= ~(m(pin_motor) | m(pin_chgrst));
|
||||
motor_chgrst_update_status(drv);
|
||||
motor_chgrst_update_status(&drive);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -15,15 +15,14 @@
|
|||
/* Input pins: All are level signals. */
|
||||
#define pin_reset 1 /* PA1, /RS: LOW = Reset asserted */
|
||||
#define pin_motor 0 /* PA0, /MO: LOW = Motor on */
|
||||
#define pin_wgate 9 /* PB9, WG: HIGH = Write active */
|
||||
static uint8_t pin_wgate = 9; /* PB9, WG: HIGH = Write active */
|
||||
|
||||
/* Output pins: All are level signals. */
|
||||
#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: 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 */
|
||||
|
|
@ -33,32 +32,32 @@
|
|||
|
||||
#define pin_wdata 8 /* /WD: Negative pulse signal */
|
||||
#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 /* RD: Positive pulse signal */
|
||||
#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. */
|
||||
#define motor_irq 6
|
||||
#define reset_irq 7
|
||||
#define wgate_irq 23
|
||||
#define rotary_irq 40
|
||||
void IRQ_6(void) __attribute__((alias("IRQ_MOTOR_changed"))); /* EXTI0 */
|
||||
void IRQ_7(void) __attribute__((alias("IRQ_MOTOR_changed"))); /* EXTI1 */
|
||||
void IRQ_23(void) __attribute__((alias("IRQ_WGATE_changed"))); /* EXTI9_5 */
|
||||
void IRQ_40(void) __attribute__((alias("_IRQ_rotary"))); /* EXTI15_10 */
|
||||
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_irq, TIMER_IRQ_PRI, 0 },
|
||||
{ reset_irq, TIMER_IRQ_PRI, 0 },
|
||||
{ wgate_irq, FLOPPY_IRQ_WGATE_PRI, 0 },
|
||||
{ rotary_irq, TIMER_IRQ_PRI, 0 }
|
||||
/* 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)
|
||||
|
|
@ -68,25 +67,59 @@ bool_t floppy_ribbon_is_reversed(void)
|
|||
|
||||
static void board_floppy_init(void)
|
||||
{
|
||||
uint32_t pins;
|
||||
#if MCU == STM32F105
|
||||
|
||||
/* PA[15:14], PB[13:12], PC[11:10], PB[9:2], PA[1:0] */
|
||||
afio->exticr4 = 0x0011;
|
||||
afio->exticr3 = 0x2211;
|
||||
afio->exticr2 = 0x1111;
|
||||
afio->exticr1 = 0x1100;
|
||||
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);
|
||||
|
||||
pins = m(pin_wgate) | m(pin_reset) | m(pin_motor);
|
||||
exti->rtsr = pins | m(11) | m(10);
|
||||
exti->ftsr = pins | m(11) | m(10);
|
||||
exti->imr = pins;
|
||||
#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_changed(void)
|
||||
static void IRQ_WGATE(void)
|
||||
{
|
||||
/* Clear WGATE-changed flag. */
|
||||
exti->pr = m(pin_wgate);
|
||||
|
||||
/* If WRPROT line is asserted then we ignore WGATE. */
|
||||
if (read_pin(wrprot))
|
||||
return;
|
||||
|
|
@ -106,16 +139,39 @@ static void IRQ_WGATE_changed(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void IRQ_MOTOR_changed(void)
|
||||
static void IRQ_WGATE_rotary(void)
|
||||
{
|
||||
const uint16_t mask = m(pin_reset) | m(pin_motor);
|
||||
uint8_t off;
|
||||
uint32_t rot_mask = board_rotary_exti_mask, pr = exti->pr;
|
||||
|
||||
/* Clear MOTOR- and RESET-changed flags. */
|
||||
exti->pr = mask;
|
||||
/* 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 = gpioa_idr & mask;
|
||||
off ^= m(pin_reset);
|
||||
|
||||
/* /RESET is forced by media removal. */
|
||||
|
|
@ -137,20 +193,62 @@ static void IRQ_MOTOR_changed(void)
|
|||
|
||||
if (/* RESET immediately clears READY */
|
||||
(off & m(pin_reset))
|
||||
/* !MOTOR immediately clears READY iff Jumper JC is strapped */
|
||||
|| ((off & m(pin_motor)) && !gpio_read_pin(gpiob, 1))) {
|
||||
/* !MOTOR immediately clears READY iff Roland mode is selected */
|
||||
|| ((off & m(pin_motor)) && qd_roland_mode())) {
|
||||
write_pin(ready, HIGH);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void _IRQ_rotary(void)
|
||||
static void IRQ_MOTOR_changed(void)
|
||||
{
|
||||
exti->pr = m(11) | m(10);
|
||||
/* Clear MOTOR-changed flag. */
|
||||
exti->pr = m(pin_motor);
|
||||
|
||||
_IRQ_MOTOR_RESET_changed(gpioa->idr);
|
||||
}
|
||||
|
||||
static void IRQ_RESET_changed(void)
|
||||
{
|
||||
unsigned int gpioa_idr, gpioa_idr2 = gpioa->idr;
|
||||
|
||||
do {
|
||||
|
||||
/* Clear RESET-changed flag. */
|
||||
(void)tim2->ccr2;
|
||||
|
||||
/* Execute MOTOR/RESET logic based on snapshotted pin state. */
|
||||
gpioa_idr = gpioa_idr2;
|
||||
_IRQ_MOTOR_RESET_changed(gpioa_idr);
|
||||
|
||||
/* Update the timer channel's edge detector to detect the next edge
|
||||
* depending on snapshotted RESET pin state. */
|
||||
if (gpioa_idr & m(pin_reset)) {
|
||||
tim2->ccer |= TIM_CCER_CC2P; /* Falling edge */
|
||||
} else {
|
||||
tim2->ccer &= ~TIM_CCER_CC2P; /* Rising edge */
|
||||
}
|
||||
|
||||
/* Now check if we raced a RESET edge. Loop if so. */
|
||||
gpioa_idr2 = gpioa->idr;
|
||||
} while ((gpioa_idr ^ gpioa_idr2) & m(pin_reset));
|
||||
}
|
||||
|
||||
static void IRQ_rotary_changed(void)
|
||||
{
|
||||
/* Clear PR[15:10] */
|
||||
exti->pr = 0xfc00;
|
||||
|
||||
IRQ_rotary();
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -56,7 +56,89 @@ void speaker_pulse(void)
|
|||
now = time_now();
|
||||
pulse.state = STATE_active;
|
||||
pulse.start = now;
|
||||
timer_set(&pulse.timer, now + volume*volume*3);
|
||||
timer_set(&pulse.timer, now + volume*volume*(TIME_MHZ/3));
|
||||
}
|
||||
|
||||
static void speaker_hz(unsigned int hz, unsigned int ms)
|
||||
{
|
||||
unsigned int vol = (ff_cfg.notify_volume & NOTIFY_volume_mask) + 1;
|
||||
unsigned int period = STK_MHZ * 1000000 / hz;
|
||||
unsigned int period_on = period * vol * vol / (2*400);
|
||||
unsigned int nr = hz * ms / 1000;
|
||||
while (nr--) {
|
||||
gpio_write_pin(gpio_spk, pin_spk, TRUE);
|
||||
delay_ticks(period_on);
|
||||
gpio_write_pin(gpio_spk, pin_spk, FALSE);
|
||||
delay_ticks(period - period_on);
|
||||
}
|
||||
}
|
||||
|
||||
static void speaker_lock(void)
|
||||
{
|
||||
uint32_t oldpri;
|
||||
oldpri = IRQ_save(TIMER_IRQ_PRI);
|
||||
timer_cancel(&pulse.timer);
|
||||
pulse.state = STATE_masked;
|
||||
IRQ_restore(oldpri);
|
||||
}
|
||||
|
||||
static void speaker_unlock(void)
|
||||
{
|
||||
pulse.state = STATE_idle;
|
||||
}
|
||||
|
||||
static void speaker_notify_slot(unsigned int nr)
|
||||
{
|
||||
while (nr >= 5) {
|
||||
speaker_hz(1500, 100);
|
||||
nr -= 5;
|
||||
if (nr != 0)
|
||||
delay_ms(120);
|
||||
}
|
||||
|
||||
while (nr != 0) {
|
||||
speaker_hz(1500, 40);
|
||||
nr -= 1;
|
||||
if (nr != 0)
|
||||
delay_ms(120);
|
||||
}
|
||||
}
|
||||
|
||||
void speaker_notify_insert(unsigned int slotnr)
|
||||
{
|
||||
if ((ff_cfg.notify_volume & NOTIFY_volume_mask) == 0)
|
||||
return;
|
||||
|
||||
speaker_lock();
|
||||
|
||||
speaker_hz(880, 40); /* a5 */
|
||||
delay_ms(20);
|
||||
speaker_hz(784, 40); /* g5 */
|
||||
delay_ms(20);
|
||||
speaker_hz(1046, 60); /* c6 */
|
||||
|
||||
if (ff_cfg.notify_volume & NOTIFY_slotnr) {
|
||||
delay_ms(300);
|
||||
speaker_notify_slot(slotnr);
|
||||
}
|
||||
|
||||
speaker_unlock();
|
||||
}
|
||||
|
||||
void speaker_notify_eject(void)
|
||||
{
|
||||
if ((ff_cfg.notify_volume & NOTIFY_volume_mask) == 0)
|
||||
return;
|
||||
|
||||
speaker_lock();
|
||||
|
||||
speaker_hz(932, 40); /* a#5 */
|
||||
delay_ms(20);
|
||||
speaker_hz(831, 40); /* g#5 */
|
||||
delay_ms(20);
|
||||
speaker_hz(659, 60); /* e5 */
|
||||
|
||||
speaker_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@
|
|||
#define DD_TRACKLEN_BC 101376 /* multiple of 32 */
|
||||
#define POST_IDX_GAP_BC 1024
|
||||
|
||||
#define MAX_WR_BATCH 11
|
||||
|
||||
/* Shift even/odd bits into MFM data-bit positions */
|
||||
#define even(x) ((x)>>1)
|
||||
#define odd(x) (x)
|
||||
|
|
@ -39,7 +37,8 @@ static bool_t adf_open(struct image *im)
|
|||
im->nr_sides = 2;
|
||||
im->adf.nr_secs = 11;
|
||||
im->tracklen_bc = DD_TRACKLEN_BC;
|
||||
im->ticks_per_cell = (sysclk_stk(im->stk_per_rev) * 16u) / im->tracklen_bc;
|
||||
im->ticks_per_cell = ((sampleclk_stk(im->stk_per_rev) * 16u)
|
||||
/ im->tracklen_bc);
|
||||
|
||||
im->nr_cyls = f_size(&im->fp) / (2 * 11 * 512);
|
||||
|
||||
|
|
@ -55,8 +54,9 @@ static bool_t adf_open(struct image *im)
|
|||
- im->adf.nr_secs * 544 * 16
|
||||
- POST_IDX_GAP_BC);
|
||||
|
||||
volume_cache_init(im->bufs.write_data.p + MAX_WR_BATCH * 512,
|
||||
im->bufs.write_data.p + im->bufs.write_data.len);
|
||||
im->adf.fcache = file_cache_init(&im->fp, 4,
|
||||
im->bufs.write_data.p + 512,
|
||||
im->bufs.write_data.p + im->bufs.write_data.len);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -66,19 +66,20 @@ static void adf_setup_track(
|
|||
{
|
||||
struct image_buf *rd = &im->bufs.read_data;
|
||||
struct image_buf *bc = &im->bufs.read_bc;
|
||||
uint32_t decode_off, sector, sys_ticks = start_pos ? *start_pos : 0;
|
||||
uint32_t decode_off, sector, start_ticks = start_pos ? *start_pos : 0;
|
||||
|
||||
if ((im->cur_track ^ track) & ~1) {
|
||||
/* New cylinder: Refresh the sector maps (ordered by sector #). */
|
||||
unsigned int sect;
|
||||
for (sect = 0; sect < im->adf.nr_secs; sect++)
|
||||
im->adf.sec_map[0][sect] = im->adf.sec_map[1][sect] = sect;
|
||||
file_cache_sync_wait(im->adf.fcache);
|
||||
}
|
||||
|
||||
im->adf.trk_off = track * im->adf.nr_secs * 512;
|
||||
im->cur_track = track;
|
||||
|
||||
im->cur_bc = (sys_ticks * 16) / im->ticks_per_cell;
|
||||
im->cur_bc = (start_ticks * 16) / im->ticks_per_cell;
|
||||
if (im->cur_bc >= im->tracklen_bc)
|
||||
im->cur_bc = 0;
|
||||
im->cur_ticks = im->cur_bc * im->ticks_per_cell;
|
||||
|
|
@ -102,9 +103,12 @@ static void adf_setup_track(
|
|||
bc->prod = bc->cons = 0;
|
||||
|
||||
if (start_pos) {
|
||||
image_read_track(im);
|
||||
bc->cons = decode_off;
|
||||
file_cache_readahead(im->adf.fcache,
|
||||
track * im->adf.nr_secs * 512, im->adf.nr_secs * 512,
|
||||
6*1024);
|
||||
im->adf.trash_bc = decode_off;
|
||||
} else {
|
||||
file_cache_readahead(im->adf.fcache, 0, 0, 0);
|
||||
im->adf.sec_idx = 0;
|
||||
im->adf.written_secs = 0;
|
||||
}
|
||||
|
|
@ -123,12 +127,15 @@ static bool_t adf_read_track(struct image *im)
|
|||
|
||||
if (rd->prod == rd->cons) {
|
||||
unsigned int sector = im->adf.sec_map[hd][im->adf.sec_idx];
|
||||
F_lseek(&im->fp, im->adf.trk_off + sector * sec_sz);
|
||||
F_read(&im->fp, buf, sec_sz, NULL);
|
||||
if (!file_cache_try_read(im->adf.fcache,
|
||||
buf, im->adf.trk_off + sector * sec_sz, sec_sz))
|
||||
return FALSE;
|
||||
rd->prod++;
|
||||
im->adf.sec_idx++;
|
||||
if (im->adf.sec_idx >= im->adf.nr_secs)
|
||||
im->adf.sec_idx = 0;
|
||||
} else {
|
||||
file_cache_progress(im->adf.fcache);
|
||||
}
|
||||
|
||||
/* Generate some MFM if there is space in the raw-bitcell ring buffer. */
|
||||
|
|
@ -210,27 +217,18 @@ static bool_t adf_read_track(struct image *im)
|
|||
|
||||
}
|
||||
|
||||
if (im->adf.trash_bc) {
|
||||
int16_t to_consume =
|
||||
min_t(uint16_t, (bc_p - bc_c)*16, im->adf.trash_bc);
|
||||
im->adf.trash_bc -= to_consume;
|
||||
bc->cons += to_consume;
|
||||
}
|
||||
im->adf.decode_pos++;
|
||||
bc->prod = bc_p * 32;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void write_batch(struct image *im, unsigned int sect, unsigned int nr)
|
||||
{
|
||||
uint32_t *wrbuf = im->bufs.write_data.p;
|
||||
time_t t;
|
||||
|
||||
if (nr == 0)
|
||||
return;
|
||||
|
||||
t = time_now();
|
||||
printk("Write %u/%u-%u... ", im->cur_track, sect, sect+nr-1);
|
||||
F_lseek(&im->fp, im->adf.trk_off + sect*512);
|
||||
F_write(&im->fp, wrbuf, 512*nr, NULL);
|
||||
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
|
||||
}
|
||||
|
||||
static bool_t adf_write_track(struct image *im)
|
||||
{
|
||||
bool_t flush;
|
||||
|
|
@ -241,7 +239,7 @@ static bool_t adf_write_track(struct image *im)
|
|||
uint32_t *w, *wrbuf = im->bufs.write_data.p;
|
||||
uint32_t c = wr->cons / 32, p = wr->prod / 32;
|
||||
uint32_t info, dsum, csum;
|
||||
unsigned int i, sect, batch_sect, batch, max_batch;
|
||||
unsigned int i, sect;
|
||||
unsigned int hd = im->cur_track & 1;
|
||||
|
||||
/* If we are processing final data then use the end index, rounded up. */
|
||||
|
|
@ -250,14 +248,7 @@ static bool_t adf_write_track(struct image *im)
|
|||
if (flush)
|
||||
p = (write->bc_end + 31) / 32;
|
||||
|
||||
batch = batch_sect = 0;
|
||||
max_batch = min_t(unsigned int,
|
||||
im->bufs.write_data.len / 512,
|
||||
MAX_WR_BATCH);
|
||||
w = wrbuf;
|
||||
|
||||
while ((int16_t)(p - c) >= (542/2)) {
|
||||
|
||||
/* Scan for sync word. */
|
||||
if (be32toh(buf[c++ & bufmask]) != 0x44894489)
|
||||
continue;
|
||||
|
|
@ -286,19 +277,12 @@ static bool_t adf_write_track(struct image *im)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (batch && ((sect != batch_sect + batch) || (batch >= max_batch))) {
|
||||
ASSERT(batch <= max_batch);
|
||||
write_batch(im, batch_sect, batch);
|
||||
batch = 0;
|
||||
w = wrbuf;
|
||||
}
|
||||
|
||||
/* Data checksum. */
|
||||
csum = (buf[c++ & bufmask] & 0x55555555) << 1;
|
||||
csum |= buf[c++ & bufmask] & 0x55555555;
|
||||
|
||||
/* Data area. Decode to a write buffer and keep a running checksum. */
|
||||
dsum = 0;
|
||||
w = wrbuf;
|
||||
for (i = dsum = 0; i < 128; i++) {
|
||||
uint32_t o = buf[(c + 128) & bufmask] & 0x55555555;
|
||||
uint32_t e = buf[c++ & bufmask] & 0x55555555;
|
||||
|
|
@ -314,16 +298,20 @@ static bool_t adf_write_track(struct image *im)
|
|||
continue;
|
||||
}
|
||||
|
||||
file_cache_write(im->adf.fcache, wrbuf, im->adf.trk_off + sect*512, 512);
|
||||
printk("Write %u/%u...\n", im->cur_track, sect);
|
||||
|
||||
/* All good: add to the write-out batch. */
|
||||
if (!(im->adf.written_secs & (1u<<sect))) {
|
||||
im->adf.written_secs |= 1u<<sect;
|
||||
im->adf.sec_map[hd][im->adf.sec_idx++] = sect;
|
||||
}
|
||||
if (batch++ == 0)
|
||||
batch_sect = sect;
|
||||
}
|
||||
|
||||
write_batch(im, batch_sect, batch);
|
||||
if (flush)
|
||||
file_cache_sync(im->adf.fcache);
|
||||
else
|
||||
file_cache_progress(im->adf.fcache);
|
||||
|
||||
if (flush && (im->adf.sec_idx != im->adf.nr_secs)) {
|
||||
/* End of write: If not all sectors were correctly written,
|
||||
|
|
@ -337,12 +325,19 @@ static bool_t adf_write_track(struct image *im)
|
|||
return flush;
|
||||
}
|
||||
|
||||
static void adf_sync(struct image *im)
|
||||
{
|
||||
file_cache_sync_wait(im->adf.fcache);
|
||||
file_cache_shutdown(im->adf.fcache);
|
||||
}
|
||||
|
||||
const struct image_handler adf_image_handler = {
|
||||
.open = adf_open,
|
||||
.setup_track = adf_setup_track,
|
||||
.read_track = adf_read_track,
|
||||
.rdata_flux = bc_rdata_flux,
|
||||
.write_track = adf_write_track,
|
||||
.sync = adf_sync,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
212
src/image/da.c
212
src/image/da.c
|
|
@ -37,25 +37,81 @@ static bool_t fm_write_track(struct image *im);
|
|||
static bool_t mfm_read_track(struct image *im);
|
||||
static bool_t mfm_write_track(struct image *im);
|
||||
|
||||
static void process_wdata(struct image *im, unsigned int sect, uint16_t crc);
|
||||
#define SYNCED 0
|
||||
#define SYNC_NEEDED 1
|
||||
#define SYNCING 2
|
||||
|
||||
static void process_wdata(
|
||||
struct image *im, unsigned int sect, uint16_t crc, uint16_t crc_data);
|
||||
|
||||
static unsigned int enc_sec_sz(struct image *im)
|
||||
{
|
||||
return im->da.idam_sz + im->da.dam_sz;
|
||||
}
|
||||
|
||||
static void da_seek_track(struct image *im, uint16_t track)
|
||||
static void progress_write(struct image *im)
|
||||
{
|
||||
struct image_buf *wb = &im->da.write_buffer;
|
||||
uint16_t idx, cnt;
|
||||
LBA_t off;
|
||||
|
||||
thread_yield();
|
||||
if (!F_async_isdone(im->da.io_op)) /* Possible read op */
|
||||
return;
|
||||
if (im->da.write_cnt) {
|
||||
wb->cons += im->da.write_cnt;
|
||||
im->da.write_cnt = 0;
|
||||
}
|
||||
if (wb->prod == wb->cons) {
|
||||
if (im->da.sync_state == SYNCING)
|
||||
im->da.sync_state = SYNCED;
|
||||
else if (im->da.sync_state == SYNC_NEEDED) {
|
||||
im->da.io_op = disk_ioctl_async(0, CTRL_SYNC, NULL, NULL);
|
||||
im->da.sync_state = SYNCING;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
idx = wb->cons % wb->len;
|
||||
off = im->da.write_offsets[idx];
|
||||
for (cnt = 1; wb->cons + cnt < wb->prod && idx + cnt < wb->len; cnt++)
|
||||
if (im->da.write_offsets[idx+cnt] != off + cnt)
|
||||
break;
|
||||
ASSERT(off);
|
||||
im->da.io_op = disk_write_async(0, wb->p + idx*512, off, cnt);
|
||||
im->da.write_cnt = cnt;
|
||||
im->da.sync_state = SYNC_NEEDED;
|
||||
}
|
||||
|
||||
static bool_t da_open(struct image *im)
|
||||
{
|
||||
struct da_status_sector *dass = &im->da.dass;
|
||||
void *p = im->bufs.read_data.p;
|
||||
uint32_t len = im->bufs.read_data.len;
|
||||
int p_used = 0;
|
||||
bool_t version_override = (ff_cfg.da_report_version[0] != '\0');
|
||||
|
||||
track &= ~1; /* force side 0 */
|
||||
if (im->cur_track == track)
|
||||
return;
|
||||
im->cur_track = track;
|
||||
printk("D-A Mode Entered\n");
|
||||
im->nr_sides = 1;
|
||||
|
||||
volume_cache_init(im->bufs.write_data.p + SEC_SZ + 2,
|
||||
im->bufs.write_data.p + im->bufs.write_data.len);
|
||||
len -= -(uint32_t)p & 3;
|
||||
p += -(uint32_t)p & 3;
|
||||
|
||||
im->da.rd_buf = p + p_used;
|
||||
p_used += SEC_SZ;
|
||||
/* 768 bytes for cache overhead. */
|
||||
volume_cache_init(p + p_used, p + p_used + 8*SEC_SZ + 768);
|
||||
p_used += 8*SEC_SZ + 768;
|
||||
im->da.write_buffer.p = p + p_used;
|
||||
im->da.write_buffer.len =
|
||||
(len - p_used) / (512 + sizeof(*im->da.write_offsets));
|
||||
p_used += im->da.write_buffer.len * sizeof(*im->da.write_offsets);
|
||||
im->da.write_offsets = p + p_used;
|
||||
p_used += im->da.write_buffer.len * 512;
|
||||
ASSERT(p_used <= len);
|
||||
ASSERT(im->da.write_buffer.len >= 8);
|
||||
|
||||
im->da.io_op = F_async_get_completed_op();
|
||||
|
||||
switch (display_type) {
|
||||
case DT_LED_7SEG:
|
||||
|
|
@ -66,24 +122,33 @@ static void da_seek_track(struct image *im, uint16_t track)
|
|||
break;
|
||||
}
|
||||
|
||||
memset(&im->da, 0, sizeof(im->da));
|
||||
|
||||
snprintf(dass->sig, sizeof(dass->sig), "%s", DA_SIG);
|
||||
snprintf(dass->fw_ver, sizeof(dass->fw_ver),
|
||||
version_override ? "%s" : "FF-v%s",
|
||||
version_override ? "%s" : "FF-%s",
|
||||
version_override ? ff_cfg.da_report_version : fw_ver);
|
||||
dass->current_index = get_slot_nr();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void da_seek_track(struct image *im, uint16_t track)
|
||||
{
|
||||
struct da_status_sector *dass = &im->da.dass;
|
||||
|
||||
track &= ~1; /* force side 0 */
|
||||
if (im->cur_track == track)
|
||||
return;
|
||||
im->cur_track = track;
|
||||
|
||||
switch (im->cur_track>>1) {
|
||||
case DA_SD_FM_CYL:
|
||||
dass->nr_sec = 4;
|
||||
im->sync = SYNC_fm;
|
||||
im->write_bc_ticks = sysclk_us(4);
|
||||
im->write_bc_ticks = sampleclk_us(4);
|
||||
break;
|
||||
default:
|
||||
dass->nr_sec = 8;
|
||||
im->sync = SYNC_mfm;
|
||||
im->write_bc_ticks = sysclk_us(2);
|
||||
im->write_bc_ticks = sampleclk_us(2);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +160,7 @@ static void da_setup_track(
|
|||
{
|
||||
struct image_buf *rd = &im->bufs.read_data;
|
||||
struct image_buf *bc = &im->bufs.read_bc;
|
||||
uint32_t decode_off, sys_ticks = start_pos ? *start_pos : 0;
|
||||
uint32_t decode_off, start_ticks = start_pos ? *start_pos : 0;
|
||||
unsigned int nsec;
|
||||
|
||||
da_seek_track(im, track);
|
||||
|
|
@ -120,11 +185,11 @@ static void da_setup_track(
|
|||
im->tracklen_bc += im->da.idx_sz;
|
||||
im->tracklen_bc *= 16;
|
||||
|
||||
im->stk_per_rev = stk_sysclk(im->tracklen_bc * im->write_bc_ticks);
|
||||
im->stk_per_rev = stk_sampleclk(im->tracklen_bc * im->write_bc_ticks);
|
||||
|
||||
im->da.trk_sec = 0;
|
||||
|
||||
im->cur_bc = (sys_ticks * 16) / im->ticks_per_cell;
|
||||
im->cur_bc = (start_ticks * 16) / im->ticks_per_cell;
|
||||
im->cur_bc &= ~15;
|
||||
if (im->cur_bc >= im->tracklen_bc)
|
||||
im->cur_bc = 0;
|
||||
|
|
@ -155,9 +220,8 @@ static void da_setup_track(
|
|||
bc->prod = bc->cons = 0;
|
||||
|
||||
if (start_pos) {
|
||||
image_read_track(im);
|
||||
bc->cons = decode_off * 16;
|
||||
*start_pos = sys_ticks;
|
||||
im->da.trash_bc = decode_off * 16;
|
||||
*start_pos = start_ticks;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,8 +229,11 @@ static bool_t da_read_track(struct image *im)
|
|||
{
|
||||
struct da_status_sector *dass = &im->da.dass;
|
||||
struct image_buf *rd = &im->bufs.read_data;
|
||||
uint8_t *buf = rd->p;
|
||||
uint8_t *buf = im->da.rd_buf;
|
||||
|
||||
progress_write(im);
|
||||
if (!F_async_isdone(im->da.io_op))
|
||||
return FALSE;
|
||||
if (rd->prod == rd->cons) {
|
||||
uint8_t sec = im->da.trk_sec;
|
||||
if (sec == 0) {
|
||||
|
|
@ -179,13 +246,15 @@ static bool_t da_read_track(struct image *im)
|
|||
if (sec == 1)
|
||||
strcpy((char *)buf, im->slot->name);
|
||||
} else {
|
||||
if (disk_read(0, buf, dass->lba_base+sec-1, 1) != RES_OK)
|
||||
F_die(FR_DISK_ERR);
|
||||
im->da.io_op = disk_read_async(0, buf, dass->lba_base+sec-1, 1);
|
||||
}
|
||||
rd->prod++;
|
||||
if (++im->da.trk_sec >= (dass->nr_sec + 1))
|
||||
im->da.trk_sec = 0;
|
||||
}
|
||||
thread_yield();
|
||||
if (!F_async_isdone(im->da.io_op))
|
||||
return FALSE;
|
||||
|
||||
return (im->sync == SYNC_fm) ? fm_read_track(im) : mfm_read_track(im);
|
||||
}
|
||||
|
|
@ -195,7 +264,7 @@ static bool_t fm_read_track(struct image *im)
|
|||
struct da_status_sector *dass = &im->da.dass;
|
||||
struct image_buf *bc = &im->bufs.read_bc;
|
||||
struct image_buf *rd = &im->bufs.read_data;
|
||||
uint8_t *buf = rd->p;
|
||||
uint8_t *buf = im->da.rd_buf;
|
||||
uint16_t *bc_b = bc->p;
|
||||
uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c;
|
||||
uint16_t crc;
|
||||
|
|
@ -255,6 +324,12 @@ static bool_t fm_read_track(struct image *im)
|
|||
#undef emit_raw
|
||||
#undef emit_byte
|
||||
|
||||
if (im->da.trash_bc) {
|
||||
int16_t to_consume =
|
||||
min_t(uint16_t, (bc_p - bc_c)*16, im->da.trash_bc);
|
||||
im->da.trash_bc -= to_consume;
|
||||
bc->cons += to_consume;
|
||||
}
|
||||
im->da.decode_pos++;
|
||||
bc->prod = bc_p * 16;
|
||||
|
||||
|
|
@ -266,7 +341,7 @@ static bool_t mfm_read_track(struct image *im)
|
|||
struct da_status_sector *dass = &im->da.dass;
|
||||
struct image_buf *bc = &im->bufs.read_bc;
|
||||
struct image_buf *rd = &im->bufs.read_data;
|
||||
uint8_t *buf = rd->p;
|
||||
uint8_t *buf = im->da.rd_buf;
|
||||
uint16_t *bc_b = bc->p;
|
||||
uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c;
|
||||
uint16_t pr, crc;
|
||||
|
|
@ -338,6 +413,12 @@ static bool_t mfm_read_track(struct image *im)
|
|||
#undef emit_raw
|
||||
#undef emit_byte
|
||||
|
||||
if (im->da.trash_bc) {
|
||||
int16_t to_consume =
|
||||
min_t(uint16_t, (bc_p - bc_c)*16, im->da.trash_bc);
|
||||
im->da.trash_bc -= to_consume;
|
||||
bc->cons += to_consume;
|
||||
}
|
||||
im->da.decode_pos++;
|
||||
bc->prod = bc_p * 16;
|
||||
|
||||
|
|
@ -356,11 +437,12 @@ static bool_t fm_write_track(struct image *im)
|
|||
struct image_buf *wr = &im->bufs.write_bc;
|
||||
uint16_t *buf = wr->p;
|
||||
unsigned int bufmask = (wr->len / 2) - 1;
|
||||
uint8_t *wrbuf = im->bufs.write_data.p;
|
||||
struct image_buf *wb = &im->da.write_buffer;
|
||||
uint8_t *wrbuf;
|
||||
uint32_t c = wr->cons / 16, p = wr->prod / 16;
|
||||
uint32_t base = write->start / im->ticks_per_cell; /* in data bytes */
|
||||
unsigned int sect;
|
||||
uint16_t sync;
|
||||
uint16_t sync, crc_data;
|
||||
uint8_t x;
|
||||
|
||||
/* If we are processing final data then use the end index, rounded up. */
|
||||
|
|
@ -370,6 +452,7 @@ static bool_t fm_write_track(struct image *im)
|
|||
p = (write->bc_end + 15) / 16;
|
||||
|
||||
while ((int16_t)(p - c) >= (2 + SEC_SZ + 2)) {
|
||||
uint32_t sc = c;
|
||||
|
||||
if (buf[c++ & bufmask] != 0xaaaa)
|
||||
continue;
|
||||
|
|
@ -382,15 +465,25 @@ static bool_t fm_write_track(struct image *im)
|
|||
if (x != 0xfb)
|
||||
continue;
|
||||
|
||||
if (wb->prod - wb->cons >= wb->len) {
|
||||
c = sc;
|
||||
flush = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
sect = (base - im->da.idx_sz - im->da.idam_sz + enc_sec_sz(im)/2)
|
||||
/ enc_sec_sz(im);
|
||||
|
||||
mfm_ring_to_bin(buf, bufmask, c, wrbuf, SEC_SZ + 2);
|
||||
c += SEC_SZ + 2;
|
||||
wrbuf = wb->p + (wb->prod % wb->len) * 512;
|
||||
mfm_ring_to_bin(buf, bufmask, c, wrbuf, SEC_SZ);
|
||||
c += SEC_SZ;
|
||||
mfm_ring_to_bin(buf, bufmask, c, &crc_data, 2);
|
||||
c += 2;
|
||||
|
||||
process_wdata(im, sect, FM_DAM_CRC);
|
||||
process_wdata(im, sect, FM_DAM_CRC, crc_data);
|
||||
}
|
||||
|
||||
progress_write(im);
|
||||
wr->cons = c * 16;
|
||||
|
||||
return flush;
|
||||
|
|
@ -403,11 +496,12 @@ static bool_t mfm_write_track(struct image *im)
|
|||
struct image_buf *wr = &im->bufs.write_bc;
|
||||
uint16_t *buf = wr->p;
|
||||
unsigned int bufmask = (wr->len / 2) - 1;
|
||||
uint8_t *wrbuf = im->bufs.write_data.p;
|
||||
struct image_buf *wb = &im->da.write_buffer;
|
||||
uint8_t *wrbuf;
|
||||
uint32_t c = wr->cons / 16, p = wr->prod / 16;
|
||||
uint32_t base = write->start / im->ticks_per_cell; /* in data bytes */
|
||||
unsigned int sect;
|
||||
uint16_t crc;
|
||||
uint16_t crc, crc_data;
|
||||
uint8_t x;
|
||||
|
||||
/* If we are processing final data then use the end index, rounded up. */
|
||||
|
|
@ -452,25 +546,37 @@ static bool_t mfm_write_track(struct image *im)
|
|||
goto out;
|
||||
}
|
||||
|
||||
mfm_ring_to_bin(buf, bufmask, c, wrbuf, SEC_SZ + 2);
|
||||
c += SEC_SZ + 2;
|
||||
if (wb->prod - wb->cons >= wb->len) {
|
||||
c = sc;
|
||||
flush = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
process_wdata(im, sect, crc);
|
||||
wrbuf = wb->p + (wb->prod % wb->len) * 512;
|
||||
mfm_ring_to_bin(buf, bufmask, c, wrbuf, SEC_SZ);
|
||||
c += SEC_SZ;
|
||||
mfm_ring_to_bin(buf, bufmask, c, &crc_data, 2);
|
||||
c += 2;
|
||||
|
||||
process_wdata(im, sect, crc, crc_data);
|
||||
}
|
||||
|
||||
out:
|
||||
progress_write(im);
|
||||
wr->cons = c * 16;
|
||||
return flush;
|
||||
}
|
||||
|
||||
static void process_wdata(struct image *im, unsigned int sect, uint16_t crc)
|
||||
static void process_wdata(
|
||||
struct image *im, unsigned int sect, uint16_t crc, uint16_t crc_data)
|
||||
{
|
||||
struct da_status_sector *dass = &im->da.dass;
|
||||
uint8_t *wrbuf = im->bufs.write_data.p;
|
||||
struct image_buf *wb = &im->da.write_buffer;
|
||||
uint8_t *wrbuf = wb->p + (wb->prod % wb->len) * 512;
|
||||
unsigned int i;
|
||||
time_t t;
|
||||
|
||||
crc = crc16_ccitt(wrbuf, SEC_SZ + 2, crc);
|
||||
crc = crc16_ccitt(wrbuf, SEC_SZ, crc);
|
||||
crc = crc16_ccitt(&crc_data, 2, crc);
|
||||
if ((crc != 0) || (sect > dass->nr_sec)) {
|
||||
printk("D-A Bad Sector: CRC %04x, ID %u\n", crc, sect);
|
||||
return;
|
||||
|
|
@ -497,6 +603,7 @@ static void process_wdata(struct image *im, unsigned int sect, uint16_t crc)
|
|||
dass->nr_sec = dac->param[5] ?: (im->sync == SYNC_fm) ? 4 : 8;
|
||||
printk("D-A LBA %08x, nr=%u\n", dass->lba_base, dass->nr_sec);
|
||||
dass->last_cmd_status = 0; /* ok */
|
||||
im->da.lba_set = TRUE;
|
||||
break;
|
||||
case CMD_SET_CYL:
|
||||
printk("D-A Cyl A=%u B=%u\n", dac->param[0], dac->param[1]);
|
||||
|
|
@ -528,21 +635,40 @@ static void process_wdata(struct image *im, unsigned int sect, uint16_t crc)
|
|||
break;
|
||||
}
|
||||
} else if (dass->lba_base != ~0u) {
|
||||
uint32_t lba = dass->lba_base + sect - 1;
|
||||
if (!im->da.lba_set) {
|
||||
printk("Write %08x+%u... ", dass->lba_base, sect-1);
|
||||
printk("before LBA set\n");
|
||||
return;
|
||||
}
|
||||
if (!lba_within_fat_volume(lba)) {
|
||||
printk("Write %08x+%u... ", dass->lba_base, sect-1);
|
||||
printk("out of bounds\n");
|
||||
return;
|
||||
}
|
||||
/* All good: write out to mass storage. */
|
||||
dass->write_cnt++;
|
||||
printk("Write %08x+%u... ", dass->lba_base, sect-1);
|
||||
t = time_now();
|
||||
if (disk_write(0, wrbuf, dass->lba_base+sect-1, 1) != RES_OK)
|
||||
F_die(FR_DISK_ERR);
|
||||
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
|
||||
im->da.write_offsets[wb->prod % wb->len] = lba;
|
||||
wb->prod++;
|
||||
}
|
||||
}
|
||||
|
||||
static void da_sync(struct image *im)
|
||||
{
|
||||
do {
|
||||
F_async_wait(im->da.io_op); /* Possible read op. */
|
||||
progress_write(im);
|
||||
} while (im->da.sync_state);
|
||||
printk("D-A Mode Exited\n");
|
||||
}
|
||||
|
||||
const struct image_handler da_image_handler = {
|
||||
.open = da_open,
|
||||
.setup_track = da_setup_track,
|
||||
.read_track = da_read_track,
|
||||
.rdata_flux = bc_rdata_flux,
|
||||
.write_track = da_write_track,
|
||||
.sync = da_sync,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
221
src/image/dsk.c
221
src/image/dsk.c
|
|
@ -14,6 +14,8 @@
|
|||
#define GAP_4A 80 /* Post-Index */
|
||||
#define GAP_SYNC 12
|
||||
|
||||
#define BATCH_SIZE 256
|
||||
|
||||
struct dib { /* disk info */
|
||||
char sig[34];
|
||||
char creator[14];
|
||||
|
|
@ -101,8 +103,10 @@ static bool_t dsk_open(struct image *im)
|
|||
* length and thus the period between index pulses. */
|
||||
im->ticks_per_cell = im->write_bc_ticks * 16;
|
||||
|
||||
volume_cache_init(im->bufs.write_data.p + 512 + 1024,
|
||||
im->bufs.write_data.p + im->bufs.write_data.len);
|
||||
im->dsk.fcache = file_cache_init(&im->fp, 2,
|
||||
im->bufs.write_data.p + 512 + max_t(unsigned int, BATCH_SIZE, 512),
|
||||
im->bufs.write_data.p + im->bufs.write_data.len);
|
||||
im->cur_track = ~0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -114,7 +118,9 @@ static void dsk_seek_track(
|
|||
struct tib *tib = tib_p(im);
|
||||
unsigned int i, nr;
|
||||
uint32_t tracklen;
|
||||
uint32_t trk_len;
|
||||
|
||||
file_cache_sync_wait(im->dsk.fcache);
|
||||
im->cur_track = track;
|
||||
|
||||
if (cyl >= im->nr_cyls) {
|
||||
|
|
@ -132,13 +138,14 @@ static void dsk_seek_track(
|
|||
goto unformatted;
|
||||
for (i = 0; i < nr; i++)
|
||||
im->dsk.trk_off += dib->track_szs[i] * 256;
|
||||
trk_len = dib->track_szs[nr] * 256;
|
||||
} else {
|
||||
im->dsk.trk_off += nr * le16toh(dib->track_sz);
|
||||
trk_len = le16toh(dib->track_sz);
|
||||
}
|
||||
|
||||
/* Read the Track Info Block and Sector Info Blocks. */
|
||||
F_lseek(&im->fp, im->dsk.trk_off);
|
||||
F_read(&im->fp, tib, 256, NULL);
|
||||
file_cache_read(im->dsk.fcache, tib, im->dsk.trk_off, 256);
|
||||
im->dsk.trk_off += 256;
|
||||
if (strncmp(tib->sig, "Track-Info", 10) || !tib->nr_secs)
|
||||
goto unformatted;
|
||||
|
|
@ -157,6 +164,8 @@ static void dsk_seek_track(
|
|||
? le16toh(tib->sib[i].actual_length)
|
||||
: 128 << min_t(unsigned, tib->sec_sz, 8);
|
||||
|
||||
file_cache_readahead(im->dsk.fcache, im->dsk.trk_off, trk_len, 6*1024);
|
||||
|
||||
out:
|
||||
im->dsk.idx_sz = GAP_4A;
|
||||
im->dsk.idx_sz += GAP_SYNC + 4 + GAP_1;
|
||||
|
|
@ -183,7 +192,7 @@ out:
|
|||
im->dsk.gap4 = (im->tracklen_bc - tracklen) / 16;
|
||||
|
||||
/* Calculate ticks per revolution */
|
||||
im->stk_per_rev = stk_sysclk(im->tracklen_bc * im->write_bc_ticks);
|
||||
im->stk_per_rev = stk_sampleclk(im->tracklen_bc * im->write_bc_ticks);
|
||||
}
|
||||
|
||||
static uint32_t calc_start_pos(struct image *im)
|
||||
|
|
@ -224,9 +233,9 @@ static uint32_t calc_start_pos(struct image *im)
|
|||
im->dsk.decode_pos++;
|
||||
if (decode_off < data_sz(&tib->sib[i])) {
|
||||
/* Data */
|
||||
im->dsk.rd_sec_pos = decode_off / 1024;
|
||||
im->dsk.rd_sec_pos = decode_off / BATCH_SIZE;
|
||||
im->dsk.decode_data_pos = im->dsk.rd_sec_pos;
|
||||
decode_off %= 1024;
|
||||
decode_off %= BATCH_SIZE;
|
||||
} else {
|
||||
/* Post Data */
|
||||
decode_off -= data_sz(&tib->sib[i]);
|
||||
|
|
@ -238,8 +247,8 @@ static uint32_t calc_start_pos(struct image *im)
|
|||
} else {
|
||||
/* Pre-index track gap */
|
||||
im->dsk.decode_pos = tib->nr_secs * 4 + 1;
|
||||
im->dsk.decode_data_pos = decode_off / 1024;
|
||||
decode_off %= 1024;
|
||||
im->dsk.decode_data_pos = decode_off / BATCH_SIZE;
|
||||
decode_off %= BATCH_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -251,7 +260,7 @@ static void dsk_setup_track(
|
|||
{
|
||||
struct image_buf *rd = &im->bufs.read_data;
|
||||
struct image_buf *bc = &im->bufs.read_bc;
|
||||
uint32_t decode_off, sys_ticks = start_pos ? *start_pos : 0;
|
||||
uint32_t decode_off, start_ticks = start_pos ? *start_pos : 0;
|
||||
uint8_t cyl = track/2, side = track & (im->nr_sides - 1);
|
||||
|
||||
track = cyl*2 + side;
|
||||
|
|
@ -260,22 +269,23 @@ static void dsk_setup_track(
|
|||
|
||||
im->dsk.write_sector = -1;
|
||||
|
||||
im->cur_bc = (sys_ticks * 16) / im->ticks_per_cell;
|
||||
im->cur_bc = (start_ticks * 16) / im->ticks_per_cell;
|
||||
im->cur_bc &= ~15;
|
||||
if (im->cur_bc >= im->tracklen_bc)
|
||||
im->cur_bc = 0;
|
||||
im->cur_ticks = im->cur_bc * im->ticks_per_cell;
|
||||
im->ticks_since_flux = 0;
|
||||
|
||||
decode_off = calc_start_pos(im);
|
||||
|
||||
rd->prod = rd->cons = 0;
|
||||
bc->prod = bc->cons = 0;
|
||||
|
||||
if (start_pos) {
|
||||
image_read_track(im);
|
||||
bc->cons = decode_off * 16;
|
||||
*start_pos = sys_ticks;
|
||||
decode_off = calc_start_pos(im);
|
||||
|
||||
im->dsk.trash_bc = decode_off * 16;
|
||||
*start_pos = start_ticks;
|
||||
} else {
|
||||
im->dsk.decode_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -292,6 +302,7 @@ static bool_t dsk_read_track(struct image *im)
|
|||
|
||||
if (tib->nr_secs && (rd->prod == rd->cons)) {
|
||||
uint16_t off = 0, len;
|
||||
bool_t partial = FALSE;
|
||||
for (i = 0; i < im->dsk.trk_pos; i++)
|
||||
off += tib->sib[i].actual_length;
|
||||
len = data_sz(&tib->sib[i]);
|
||||
|
|
@ -299,10 +310,16 @@ static bool_t dsk_read_track(struct image *im)
|
|||
/* Weak sector -- pick different data each revolution. */
|
||||
off += len * (im->dsk.rev % (tib->sib[i].actual_length / len));
|
||||
}
|
||||
off += im->dsk.rd_sec_pos * 1024;
|
||||
len -= im->dsk.rd_sec_pos * 1024;
|
||||
if (len > 1024) {
|
||||
len = 1024;
|
||||
off += im->dsk.rd_sec_pos * BATCH_SIZE;
|
||||
len -= im->dsk.rd_sec_pos * BATCH_SIZE;
|
||||
if (len > BATCH_SIZE) {
|
||||
len = BATCH_SIZE;
|
||||
partial = TRUE;
|
||||
}
|
||||
if (!file_cache_try_read(im->dsk.fcache, buf, im->dsk.trk_off + off, len))
|
||||
return FALSE;
|
||||
|
||||
if (partial) {
|
||||
im->dsk.rd_sec_pos++;
|
||||
} else {
|
||||
im->dsk.rd_sec_pos = 0;
|
||||
|
|
@ -311,10 +328,9 @@ static bool_t dsk_read_track(struct image *im)
|
|||
im->dsk.rev++;
|
||||
}
|
||||
}
|
||||
F_lseek(&im->fp, im->dsk.trk_off + off);
|
||||
F_read(&im->fp, buf, len, NULL);
|
||||
rd->prod++;
|
||||
}
|
||||
file_cache_progress(im->dsk.fcache);
|
||||
|
||||
/* Generate some MFM if there is space in the raw-bitcell ring buffer. */
|
||||
bc_p = bc->prod / 16; /* MFM words */
|
||||
|
|
@ -346,11 +362,11 @@ static bool_t dsk_read_track(struct image *im)
|
|||
emit_byte(0x4e);
|
||||
} else if (im->dsk.decode_pos == (tib->nr_secs * 4 + 1)) {
|
||||
/* Pre-index track gap */
|
||||
uint16_t sz = im->dsk.gap4 - im->dsk.decode_data_pos * 1024;
|
||||
if (bc_space < min_t(unsigned int, sz, 1024))
|
||||
uint16_t sz = im->dsk.gap4 - im->dsk.decode_data_pos * BATCH_SIZE;
|
||||
if (bc_space < min_t(unsigned int, sz, BATCH_SIZE))
|
||||
return FALSE;
|
||||
if (sz > 1024) {
|
||||
sz = 1024;
|
||||
if (sz > BATCH_SIZE) {
|
||||
sz = BATCH_SIZE;
|
||||
im->dsk.decode_data_pos++;
|
||||
im->dsk.decode_pos--;
|
||||
} else {
|
||||
|
|
@ -402,11 +418,11 @@ static bool_t dsk_read_track(struct image *im)
|
|||
}
|
||||
case 2: /* Data */ {
|
||||
uint16_t sec_sz = data_sz(&tib->sib[sec]);
|
||||
sec_sz -= im->dsk.decode_data_pos * 1024;
|
||||
if (bc_space < min_t(unsigned int, sec_sz, 1024))
|
||||
sec_sz -= im->dsk.decode_data_pos * BATCH_SIZE;
|
||||
if (bc_space < min_t(unsigned int, sec_sz, BATCH_SIZE))
|
||||
return FALSE;
|
||||
if (sec_sz > 1024) {
|
||||
sec_sz = 1024;
|
||||
if (sec_sz > BATCH_SIZE) {
|
||||
sec_sz = BATCH_SIZE;
|
||||
im->dsk.decode_data_pos++;
|
||||
im->dsk.decode_pos--;
|
||||
} else {
|
||||
|
|
@ -435,6 +451,11 @@ static bool_t dsk_read_track(struct image *im)
|
|||
}
|
||||
}
|
||||
|
||||
if (im->dsk.trash_bc) {
|
||||
int16_t to_consume = min_t(uint16_t, (bc_p - bc_c)*16, im->dsk.trash_bc);
|
||||
im->dsk.trash_bc -= to_consume;
|
||||
bc->cons += to_consume;
|
||||
}
|
||||
im->dsk.decode_pos++;
|
||||
bc->prod = bc_p * 16;
|
||||
|
||||
|
|
@ -460,7 +481,7 @@ static int dsk_find_first_write_sector(
|
|||
}
|
||||
|
||||
if (i >= tib->nr_secs) {
|
||||
printk("DSK Bad Wr.Off: %d\n", base);
|
||||
printk("%s Bad Wr.Off: %d\n", "DSK", base);
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
|
@ -478,9 +499,7 @@ static bool_t dsk_write_track(struct image *im)
|
|||
uint8_t *wrbuf = (uint8_t *)im->bufs.write_data.p + 512; /* skip DIB/TIB */
|
||||
uint32_t c = wr->cons / 16, p = wr->prod / 16;
|
||||
unsigned int i;
|
||||
time_t t;
|
||||
uint16_t crc, off;
|
||||
uint8_t x;
|
||||
uint16_t crc = im->dsk.crc;
|
||||
|
||||
/* If we are processing final data then use the end index, rounded up. */
|
||||
barrier();
|
||||
|
|
@ -488,28 +507,38 @@ static bool_t dsk_write_track(struct image *im)
|
|||
if (flush)
|
||||
p = (write->bc_end + 15) / 16;
|
||||
|
||||
while ((int16_t)(p - c) > 128) {
|
||||
|
||||
uint32_t sc = c;
|
||||
|
||||
if (be16toh(buf[c++ & bufmask]) != 0x4489)
|
||||
continue;
|
||||
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
|
||||
continue;
|
||||
c++;
|
||||
|
||||
switch (x) {
|
||||
|
||||
case 0xfe: /* IDAM */
|
||||
while ((int16_t)(p - c) > 0) {
|
||||
if (im->dsk.decode_pos == 0) {
|
||||
uint8_t x;
|
||||
/* When IRQ_write_dma finds the sync it will rewrite 32 bits that
|
||||
* may have already been observed by the consumer to align the
|
||||
* bitstream and throws away all but 32 bits of the sync. Give
|
||||
* SYNC_mfm 32 bits of margin to avoid missing the sync. */
|
||||
if (p - c < 2 + 2)
|
||||
break;
|
||||
if (be16toh(buf[c++ & bufmask]) != 0x4489)
|
||||
continue;
|
||||
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
|
||||
continue;
|
||||
c++;
|
||||
if (x == 0xfe) /* IDAM */
|
||||
im->dsk.decode_pos = 1;
|
||||
else if (x == 0xfb) /* DAM */
|
||||
im->dsk.decode_pos = 2;
|
||||
} else if (im->dsk.decode_pos == 1) {
|
||||
/* ID record, shy address mark */
|
||||
if (p - c < 4 + 2)
|
||||
break;
|
||||
for (i = 0; i < 3; i++)
|
||||
wrbuf[i] = 0xa1;
|
||||
wrbuf[i++] = x;
|
||||
wrbuf[i++] = 0xfe;
|
||||
for (; i < 10; i++)
|
||||
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
|
||||
crc = crc16_ccitt(wrbuf, i, 0xffff);
|
||||
if (crc != 0) {
|
||||
printk("DSK IDAM Bad CRC: %04x, %02x\n", crc, wrbuf[6]);
|
||||
break;
|
||||
printk("%s IDAM Bad CRC: %04x, %02x\n", "DSK", crc, wrbuf[6]);
|
||||
im->dsk.decode_pos = 0;
|
||||
continue;
|
||||
}
|
||||
/* Convert logical sector number -> rotational number. */
|
||||
for (i = 0; i < tib->nr_secs; i++)
|
||||
|
|
@ -517,77 +546,105 @@ static bool_t dsk_write_track(struct image *im)
|
|||
break;
|
||||
im->dsk.write_sector = i;
|
||||
if (im->dsk.write_sector >= tib->nr_secs) {
|
||||
printk("DSK IDAM Bad Sector: %02x\n", wrbuf[6]);
|
||||
printk("%s IDAM Bad Sector: %02x\n", "DSK", wrbuf[6]);
|
||||
im->dsk.write_sector = -2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xfb: /* DAM */ {
|
||||
unsigned int nr, todo, sec_sz;
|
||||
im->dsk.decode_data_pos = 0;
|
||||
im->dsk.decode_pos = 0;
|
||||
} else if (im->dsk.decode_pos == 2) {
|
||||
/* Data record, shy address mark */
|
||||
unsigned int sec_sz;
|
||||
int sec_nr = im->dsk.write_sector;
|
||||
|
||||
if (sec_nr < 0) {
|
||||
if (sec_nr == -1)
|
||||
if (sec_nr == -1) {
|
||||
sec_nr = dsk_find_first_write_sector(im, write, tib);
|
||||
im->dsk.write_sector = sec_nr;
|
||||
im->dsk.decode_data_pos = 0;
|
||||
}
|
||||
if (sec_nr < 0) {
|
||||
printk("DSK DAM Unknown\n");
|
||||
goto dam_out;
|
||||
printk("%s DAM Unknown\n", "DSK");
|
||||
im->dsk.write_sector = -2;
|
||||
im->dsk.decode_pos = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
sec_sz = data_sz(&tib->sib[sec_nr]);
|
||||
if ((int16_t)(p - c) < (sec_sz + 2)) {
|
||||
c = sc;
|
||||
goto out;
|
||||
|
||||
if (!im->dsk.decode_data_pos) {
|
||||
unsigned int off;
|
||||
crc = MFM_DAM_CRC;
|
||||
|
||||
/* Sectors less than 512 bytes require read-then-write, so
|
||||
* benefit from readahead. Larger sectors don't need readahead
|
||||
* and can benefit from faster write throughput. */
|
||||
if (sec_sz >= 512)
|
||||
file_cache_readahead(im->img.fcache, 0, 0, 0);
|
||||
|
||||
for (i = off = 0; i < sec_nr; i++)
|
||||
off += tib->sib[i].actual_length;
|
||||
im->dsk.trk_pos = off;
|
||||
}
|
||||
|
||||
crc = MFM_DAM_CRC;
|
||||
if (im->dsk.decode_data_pos < sec_sz) {
|
||||
unsigned int nr;
|
||||
unsigned int off = im->dsk.trk_off+im->dsk.trk_pos;
|
||||
nr = sec_sz - im->dsk.decode_data_pos;
|
||||
nr = min_t(unsigned int, nr, 512 - (off & 511));
|
||||
if (nr >= 512 && (p - c) < 512)
|
||||
break;
|
||||
nr = min_t(unsigned int, nr, p - c);
|
||||
|
||||
printk("Write %d[%02x]/%u... ",
|
||||
sec_nr, tib->sib[sec_nr].r, tib->nr_secs);
|
||||
t = time_now();
|
||||
|
||||
for (i = off = 0; i < sec_nr; i++)
|
||||
off += tib->sib[i].actual_length;
|
||||
F_lseek(&im->fp, im->dsk.trk_off + off);
|
||||
|
||||
for (todo = sec_sz; todo != 0; todo -= nr) {
|
||||
nr = min_t(unsigned int, todo, 1024);
|
||||
mfm_ring_to_bin(buf, bufmask, c, wrbuf, nr);
|
||||
c += nr;
|
||||
crc = crc16_ccitt(wrbuf, nr, crc);
|
||||
F_write(&im->fp, wrbuf, nr, NULL);
|
||||
file_cache_write(im->dsk.fcache, wrbuf, off, nr);
|
||||
im->dsk.trk_pos += nr;
|
||||
im->dsk.decode_data_pos += nr;
|
||||
}
|
||||
|
||||
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
|
||||
if (im->dsk.decode_data_pos < sec_sz)
|
||||
continue;
|
||||
|
||||
if (p - c < 2)
|
||||
break;
|
||||
printk("Write %d[%02x]/%u\n",
|
||||
sec_nr, tib->sib[sec_nr].r, tib->nr_secs);
|
||||
mfm_ring_to_bin(buf, bufmask, c, wrbuf, 2);
|
||||
c += 2;
|
||||
crc = crc16_ccitt(wrbuf, 2, crc);
|
||||
if (crc != 0) {
|
||||
printk("DSK Bad CRC: %04x, %d[%02x]\n",
|
||||
crc, sec_nr, tib->sib[sec_nr].r);
|
||||
printk("%s Bad CRC: %04x, %d[%02x]\n",
|
||||
"DSK", crc, sec_nr, tib->sib[sec_nr].r);
|
||||
}
|
||||
|
||||
dam_out:
|
||||
im->dsk.write_sector = -2;
|
||||
break;
|
||||
}
|
||||
|
||||
im->dsk.decode_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (flush)
|
||||
file_cache_sync(im->dsk.fcache);
|
||||
else
|
||||
file_cache_progress(im->dsk.fcache);
|
||||
im->dsk.crc = crc;
|
||||
wr->cons = c * 16;
|
||||
return flush;
|
||||
}
|
||||
|
||||
static void dsk_sync(struct image *im)
|
||||
{
|
||||
file_cache_sync_wait(im->dsk.fcache);
|
||||
file_cache_shutdown(im->dsk.fcache);
|
||||
}
|
||||
|
||||
const struct image_handler dsk_image_handler = {
|
||||
.open = dsk_open,
|
||||
.setup_track = dsk_setup_track,
|
||||
.read_track = dsk_read_track,
|
||||
.rdata_flux = bc_rdata_flux,
|
||||
.write_track = dsk_write_track,
|
||||
.sync = dsk_sync,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ static void dummy_setup_track(
|
|||
{
|
||||
im->cur_track = track;
|
||||
im->cur_ticks = (start_pos ? *start_pos : 0) * 16;
|
||||
im->tracklen_ticks = sysclk_stk(im->stk_per_rev) * 16;
|
||||
im->tracklen_ticks = sampleclk_stk(im->stk_per_rev) * 16;
|
||||
im->ticks_since_flux = 0;
|
||||
}
|
||||
|
||||
|
|
@ -51,12 +51,17 @@ static bool_t dummy_write_track(struct image *im)
|
|||
return flush;
|
||||
}
|
||||
|
||||
static void dummy_sync(struct image *im)
|
||||
{
|
||||
}
|
||||
|
||||
const struct image_handler dummy_image_handler = {
|
||||
.open = dummy_open,
|
||||
.setup_track = dummy_setup_track,
|
||||
.read_track = dummy_read_track,
|
||||
.rdata_flux = dummy_rdata_flux,
|
||||
.write_track = dummy_write_track,
|
||||
.sync = dummy_sync,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
328
src/image/hfe.c
328
src/image/hfe.c
|
|
@ -69,12 +69,17 @@ enum {
|
|||
OP_rand = 2 /* 4: flaky byte */
|
||||
};
|
||||
|
||||
static void hfe_seek_track(struct image *im, uint16_t track);
|
||||
#define absdiff_t(type,x,y) \
|
||||
({ type __x = (x); type __y = (y); __x < __y ? __y-__x: __x-__y; })
|
||||
|
||||
static void hfe_seek_track(struct image *im, uint16_t track, bool_t async);
|
||||
|
||||
static bool_t hfe_open(struct image *im)
|
||||
{
|
||||
struct disk_header dhdr;
|
||||
uint16_t bitrate;
|
||||
/* File data is less compact since it contains data for both heads. */
|
||||
uint32_t norm_buf_size = im->bufs.write_bc.len + im->bufs.read_data.len/2;
|
||||
|
||||
F_read(&im->fp, &dhdr, sizeof(dhdr), NULL);
|
||||
if (!strncmp(dhdr.sig, "HXCHFEV3", sizeof(dhdr.sig))) {
|
||||
|
|
@ -100,121 +105,170 @@ static bool_t hfe_open(struct image *im)
|
|||
im->hfe.double_step = !dhdr.single_step;
|
||||
im->hfe.tlut_base = le16toh(dhdr.track_list_offset);
|
||||
im->nr_cyls = dhdr.nr_tracks;
|
||||
if (im->hfe.double_step)
|
||||
im->nr_cyls = min_t(unsigned int, im->nr_cyls*2, 255);
|
||||
im->step = im->hfe.double_step ? 2 : 1;
|
||||
im->nr_sides = dhdr.nr_sides;
|
||||
im->write_bc_ticks = sysclk_us(500) / bitrate;
|
||||
im->write_bc_ticks = sampleclk_us(500) / bitrate;
|
||||
im->ticks_per_cell = im->write_bc_ticks * 16;
|
||||
im->sync = SYNC_none;
|
||||
|
||||
ASSERT(8*512 <= im->bufs.read_data.len);
|
||||
volume_cache_init(im->bufs.read_data.p + 8*512,
|
||||
im->bufs.read_data.p + im->bufs.read_data.len);
|
||||
volume_cache_metadata_only(&im->fp);
|
||||
/* Aggressively batch our reads at HD data rate, as that can be faster
|
||||
* than some USB drives will serve up a single block.*/
|
||||
im->hfe.fcache = file_cache_init(&im->fp,
|
||||
(im->write_bc_ticks > sampleclk_ns(1500)) ? 4 : 8,
|
||||
im->bufs.read_data.p,
|
||||
im->bufs.read_data.p + im->bufs.read_data.len);
|
||||
|
||||
/* Get an initial value for ticks per revolution. */
|
||||
hfe_seek_track(im, 0);
|
||||
hfe_seek_track(im, 0, FALSE);
|
||||
im->cur_track = -1;
|
||||
|
||||
/* Not essential, but we want to know if we are unable to fully buffer
|
||||
* writes for an HD track when we'd expect there to be enough RAM to make
|
||||
* it possible. */
|
||||
ASSERT(ram_kb < 64 || ((200000/8 + 255) & ~255) < norm_buf_size);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void hfe_seek_track(struct image *im, uint16_t track)
|
||||
static void hfe_seek_track(struct image *im, uint16_t track, bool_t async)
|
||||
{
|
||||
struct track_header thdr;
|
||||
uint16_t old_len;
|
||||
|
||||
F_lseek(&im->fp, im->hfe.tlut_base*512 + (track/2)*4);
|
||||
F_read(&im->fp, &thdr, sizeof(thdr), NULL);
|
||||
if (async) {
|
||||
file_cache_io_limit(im->hfe.fcache, 1);
|
||||
file_cache_read(im->hfe.fcache,
|
||||
&thdr, im->hfe.tlut_base*512 + (track/2)*4, sizeof(thdr));
|
||||
file_cache_io_limit(im->hfe.fcache, 0 /* no limit */);
|
||||
} else {
|
||||
F_lseek(&im->fp, im->hfe.tlut_base*512 + (track/2)*4);
|
||||
F_read(&im->fp, &thdr, sizeof(thdr), NULL);
|
||||
}
|
||||
|
||||
im->hfe.trk_off = le16toh(thdr.offset);
|
||||
old_len = im->hfe.trk_len;
|
||||
im->hfe.trk_len = le16toh(thdr.len) / 2;
|
||||
im->tracklen_bc = im->hfe.trk_len * 8;
|
||||
im->stk_per_rev = stk_sysclk(im->tracklen_bc * im->write_bc_ticks);
|
||||
/* Opcodes in v3 make it difficult to predict the track's length. Keep the
|
||||
* previous track's value if the track byte lengths are close. */
|
||||
if (!(im->hfe.is_v3 && im->stk_per_rev
|
||||
&& absdiff_t(uint16_t, old_len, im->hfe.trk_len) < 256))
|
||||
im->stk_per_rev = stk_sampleclk(im->tracklen_bc * im->write_bc_ticks);
|
||||
|
||||
im->cur_track = track;
|
||||
file_cache_readahead(im->hfe.fcache,
|
||||
(LBA_t)im->hfe.trk_off * 512, im->hfe.trk_len*2, 12*1024);
|
||||
}
|
||||
|
||||
static void hfe_setup_track(
|
||||
struct image *im, uint16_t track, uint32_t *start_pos)
|
||||
{
|
||||
struct image_buf *rd = &im->bufs.read_data;
|
||||
struct image_buf *bc = &im->bufs.read_bc;
|
||||
uint32_t sys_ticks;
|
||||
uint32_t start_ticks, opcode_adj_bc = 0;
|
||||
uint8_t cyl = track >> (im->hfe.double_step ? 2 : 1);
|
||||
uint8_t side = track & (im->nr_sides - 1);
|
||||
int i;
|
||||
|
||||
track = cyl*2 + side;
|
||||
if (track != im->cur_track)
|
||||
hfe_seek_track(im, track);
|
||||
if (track/2 != im->cur_track/2) {
|
||||
file_cache_sync_wait(im->hfe.fcache);
|
||||
|
||||
sys_ticks = start_pos ? *start_pos : get_write(im, im->wr_cons)->start;
|
||||
im->cur_bc = (sys_ticks * 16) / im->ticks_per_cell;
|
||||
if (im->cur_bc >= im->tracklen_bc)
|
||||
im->cur_track = track;
|
||||
hfe_seek_track(im, track, TRUE);
|
||||
} else if (track != im->cur_track) {
|
||||
im->cur_track = track;
|
||||
}
|
||||
|
||||
/* If track does not fit in memory, now is a good time to flush writes to
|
||||
* reduce chances of future buffer underrun caused by a very slow write.
|
||||
* However if write-drain=realtime, then any delays cut into reads so we
|
||||
* just accept the buffer underrun risk. */
|
||||
if ((im->hfe.trk_len*2 + 511) / 512 > im->bufs.read_data.len
|
||||
&& ff_cfg.write_drain != WDRAIN_realtime)
|
||||
file_cache_sync_wait(im->hfe.fcache);
|
||||
|
||||
start_ticks = start_pos ? *start_pos : get_write(im, im->wr_cons)->start;
|
||||
im->cur_bc = (start_ticks * 16) / im->ticks_per_cell;
|
||||
if (im->hfe.is_v3 && im->tracklen_ticks > 0
|
||||
&& im->tracklen_ticks < im->tracklen_bc * im->ticks_per_cell) {
|
||||
|
||||
/* If there are opcodes (other than random) in the track, seeking will
|
||||
* not be precise as opcodes contribute zero bitcells and thus zero
|
||||
* ticks. The HFE track data will _appear_ misaligned to the previous
|
||||
* until the track is read from the beginning. Misalignment greater
|
||||
* than 3 ms is possible and can shift writes backward in time.
|
||||
*
|
||||
* Severe misalignment is most likely caused by regular occurrences of
|
||||
* OP_bitrate evenly distributed through the track. Assume opcodes
|
||||
* numerous enough to become noticeable are evenly distributed in the
|
||||
* track.
|
||||
*/
|
||||
uint32_t assumed_tracklen_ticks = im->tracklen_bc * im->ticks_per_cell;
|
||||
uint32_t opcode_ticks = assumed_tracklen_ticks - im->tracklen_ticks;
|
||||
uint32_t opcode_bc = opcode_ticks / im->ticks_per_cell;
|
||||
opcode_adj_bc = im->cur_bc * opcode_bc / (im->tracklen_bc - opcode_bc);
|
||||
}
|
||||
if (im->cur_bc + opcode_adj_bc >= im->tracklen_bc) {
|
||||
im->cur_bc = 0;
|
||||
opcode_adj_bc = 0;
|
||||
}
|
||||
im->cur_ticks = im->cur_bc * im->ticks_per_cell;
|
||||
im->ticks_since_flux = 0;
|
||||
|
||||
sys_ticks = im->cur_ticks / 16;
|
||||
/* Must be careful to exclude opcode_adj_bc from tick calculations. */
|
||||
im->cur_bc += opcode_adj_bc;
|
||||
|
||||
start_ticks = im->cur_ticks / 16;
|
||||
|
||||
rd->prod = rd->cons = 0;
|
||||
bc->prod = bc->cons = 0;
|
||||
|
||||
/* Aggressively batch our reads at HD data rate, as that can be faster
|
||||
* than some USB drives will serve up a single block.*/
|
||||
im->hfe.batch_secs = (im->write_bc_ticks > sysclk_ns(1500)) ? 2 : 8;
|
||||
for (i = 0; i < im->index_pulses_len; i++)
|
||||
if (im->cur_ticks < im->index_pulses[i])
|
||||
break;
|
||||
im->hfe.next_index_pulses_pos = i;
|
||||
|
||||
if (start_pos) {
|
||||
/* Read mode. */
|
||||
im->hfe.trk_pos = (im->cur_bc/8) & ~255;
|
||||
image_read_track(im);
|
||||
bc->cons = im->cur_bc & 2047;
|
||||
*start_pos = sys_ticks;
|
||||
/* Consumer may be ahead of producer, but only until the first read
|
||||
* completes. */
|
||||
bc->cons = im->cur_bc % (256*8);
|
||||
*start_pos = start_ticks;
|
||||
} else {
|
||||
/* Write mode. */
|
||||
im->hfe.trk_pos = im->cur_bc / 8;
|
||||
im->hfe.write.start = im->hfe.trk_pos;
|
||||
im->hfe.write.wrapped = FALSE;
|
||||
im->hfe.write_batch.len = 0;
|
||||
im->hfe.write_batch.dirty = FALSE;
|
||||
im->hfe.fresh_seek = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static bool_t hfe_read_track(struct image *im)
|
||||
{
|
||||
struct image_buf *rd = &im->bufs.read_data;
|
||||
struct image_buf *bc = &im->bufs.read_bc;
|
||||
uint8_t *buf = rd->p;
|
||||
uint8_t *bc_b = bc->p;
|
||||
uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c;
|
||||
unsigned int nr_sec;
|
||||
|
||||
if (rd->prod == rd->cons) {
|
||||
nr_sec = min_t(unsigned int, im->hfe.batch_secs,
|
||||
(im->hfe.trk_len+255 - im->hfe.trk_pos) / 256);
|
||||
F_lseek(&im->fp, im->hfe.trk_off * 512 + im->hfe.trk_pos * 2);
|
||||
F_read(&im->fp, buf, nr_sec*512, NULL);
|
||||
rd->cons = 0;
|
||||
rd->prod = nr_sec;
|
||||
im->hfe.trk_pos += nr_sec * 256;
|
||||
if (im->hfe.trk_pos >= im->hfe.trk_len)
|
||||
im->hfe.trk_pos = 0;
|
||||
}
|
||||
|
||||
/* Fill the raw-bitcell ring buffer. */
|
||||
bc_p = bc->prod / 8;
|
||||
bc_c = bc->cons / 8;
|
||||
bc_len = bc->len;
|
||||
bc_mask = bc_len - 1;
|
||||
bc_space = bc_len - (uint16_t)(bc_p - bc_c);
|
||||
bc_space = bc_len - (int16_t)(bc_p - bc_c);
|
||||
|
||||
nr_sec = min_t(unsigned int, rd->prod - rd->cons, bc_space/256);
|
||||
if (nr_sec == 0)
|
||||
nr_sec = bc_space/256;
|
||||
if (nr_sec == 0) {
|
||||
file_cache_progress(im->hfe.fcache);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (nr_sec--) {
|
||||
memcpy(&bc_b[bc_p & bc_mask],
|
||||
&buf[rd->cons*512 + (im->cur_track&1)*256],
|
||||
256);
|
||||
rd->cons++;
|
||||
FSIZE_t off = im->hfe.trk_off * 512
|
||||
+ im->hfe.trk_pos * 2
|
||||
+ (im->cur_track&1)*256;
|
||||
if (!file_cache_try_read(im->hfe.fcache, &bc_b[bc_p & bc_mask], off, 256))
|
||||
break;
|
||||
im->hfe.trk_pos += 256;
|
||||
if (im->hfe.trk_pos >= im->hfe.trk_len)
|
||||
im->hfe.trk_pos = 0;
|
||||
bc_p += 256;
|
||||
}
|
||||
|
||||
|
|
@ -235,14 +289,20 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
|
|||
uint8_t x;
|
||||
bool_t is_v3 = im->hfe.is_v3;
|
||||
|
||||
while ((uint32_t)(bc_p - bc_c) >= 3*8) {
|
||||
while ((int32_t)(bc_p - bc_c) >= 3*8) {
|
||||
ASSERT(y == 8);
|
||||
if (im->cur_bc >= im->tracklen_bc) {
|
||||
ASSERT(im->cur_bc == im->tracklen_bc);
|
||||
im->tracklen_ticks = im->cur_ticks;
|
||||
im->cur_bc = im->cur_ticks = 0;
|
||||
im->stk_per_rev = stk_sampleclk(im->tracklen_ticks / 16);
|
||||
/* Skip tail of current 256-byte block. */
|
||||
bc_c = (bc_c + 256*8-1) & ~(256*8-1);
|
||||
if (im->index_pulses_len != im->hfe.next_index_pulses_pos) {
|
||||
im->index_pulses_len = im->hfe.next_index_pulses_pos;
|
||||
im->index_pulses_ver++;
|
||||
}
|
||||
im->hfe.next_index_pulses_pos = 0;
|
||||
continue;
|
||||
}
|
||||
y = bc_c % 8;
|
||||
|
|
@ -250,8 +310,19 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
|
|||
if (is_v3 && (y == 0) && ((x & 0xf) == 0xf)) {
|
||||
/* V3 byte-aligned opcode processing. */
|
||||
switch (x >> 4) {
|
||||
case OP_nop:
|
||||
case OP_index:
|
||||
if (im->hfe.next_index_pulses_pos < MAX_CUSTOM_PULSES
|
||||
&& im->index_pulses[im->hfe.next_index_pulses_pos] != im->cur_ticks) {
|
||||
|
||||
im->index_pulses[im->hfe.next_index_pulses_pos]
|
||||
= im->cur_ticks;
|
||||
if (im->index_pulses_len < im->hfe.next_index_pulses_pos+1)
|
||||
im->index_pulses_len = im->hfe.next_index_pulses_pos+1;
|
||||
im->index_pulses_ver++;
|
||||
}
|
||||
im->hfe.next_index_pulses_pos++;
|
||||
/* fallthrough */
|
||||
case OP_nop:
|
||||
default:
|
||||
bc_c += 8;
|
||||
im->cur_bc += 8;
|
||||
|
|
@ -260,7 +331,8 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
|
|||
case OP_bitrate:
|
||||
x = _rbit32(bc_b[(bc_c/8+1) & bc_mask]) >> 24;
|
||||
im->ticks_per_cell = ticks_per_cell =
|
||||
(sysclk_us(2) * 16 * x) / 72;
|
||||
(sampleclk_us(2) * 16 * x) / 72;
|
||||
im->write_bc_ticks = ticks_per_cell / 16;
|
||||
bc_c += 2*8;
|
||||
im->cur_bc += 2*8;
|
||||
y = 8;
|
||||
|
|
@ -273,8 +345,6 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
|
|||
x = bc_b[(bc_c/8) & bc_mask] >> y;
|
||||
break;
|
||||
case OP_rand:
|
||||
bc_c += 8;
|
||||
im->cur_bc += 8;
|
||||
x = rand();
|
||||
break;
|
||||
}
|
||||
|
|
@ -305,16 +375,15 @@ out:
|
|||
|
||||
static bool_t hfe_write_track(struct image *im)
|
||||
{
|
||||
const unsigned int batch_secs = 8;
|
||||
bool_t flush;
|
||||
struct write *write = get_write(im, im->wr_cons);
|
||||
struct image_buf *wr = &im->bufs.write_bc;
|
||||
uint8_t *buf = wr->p;
|
||||
uint8_t b;
|
||||
unsigned int bufmask = wr->len - 1;
|
||||
uint8_t *w, *wrbuf = im->bufs.write_data.p;
|
||||
uint8_t *w, *wrbuf;
|
||||
uint32_t i, space, c = wr->cons / 8, p = wr->prod / 8;
|
||||
bool_t writeback = FALSE;
|
||||
time_t t;
|
||||
bool_t is_v3 = im->hfe.is_v3;
|
||||
|
||||
/* If we are processing final data then use the end index, rounded to
|
||||
* nearest. */
|
||||
|
|
@ -323,20 +392,9 @@ static bool_t hfe_write_track(struct image *im)
|
|||
if (flush)
|
||||
p = (write->bc_end + 4) / 8;
|
||||
|
||||
if (im->hfe.write_batch.len == 0) {
|
||||
ASSERT(!im->hfe.write_batch.dirty);
|
||||
im->hfe.write_batch.off = (im->hfe.trk_pos & ~255) << 1;
|
||||
im->hfe.write_batch.len = min_t(
|
||||
uint32_t, batch_secs * 512,
|
||||
(((im->hfe.trk_len * 2) + 511) & ~511) - im->hfe.write_batch.off);
|
||||
F_lseek(&im->fp, im->hfe.trk_off * 512 + im->hfe.write_batch.off);
|
||||
F_read(&im->fp, wrbuf, im->hfe.write_batch.len, NULL);
|
||||
F_lseek(&im->fp, im->hfe.trk_off * 512 + im->hfe.write_batch.off);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
|
||||
uint32_t batch_off, off = im->hfe.trk_pos;
|
||||
uint32_t foff, off = im->hfe.trk_pos;
|
||||
UINT nr;
|
||||
|
||||
/* All bytes remaining in the raw-bitcell buffer. */
|
||||
|
|
@ -350,67 +408,107 @@ static bool_t hfe_write_track(struct image *im)
|
|||
if (nr == 0)
|
||||
break;
|
||||
|
||||
/* Bail if required data not in the write buffer. */
|
||||
batch_off = (off & ~255) << 1;
|
||||
if ((batch_off < im->hfe.write_batch.off)
|
||||
|| (batch_off >= (im->hfe.write_batch.off
|
||||
+ im->hfe.write_batch.len))) {
|
||||
writeback = TRUE;
|
||||
foff = im->hfe.trk_off * 512 + ((off & ~255) << 1);
|
||||
if ((wrbuf = file_cache_peek_write(im->hfe.fcache, foff)) == NULL) {
|
||||
flush = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Encode into the sector buffer for later write-out. */
|
||||
w = wrbuf
|
||||
+ (im->cur_track & 1) * 256
|
||||
+ batch_off - im->hfe.write_batch.off
|
||||
+ (off & 255);
|
||||
for (i = 0; i < nr; i++)
|
||||
*w++ = _rbit32(buf[c++ & bufmask]) >> 24;
|
||||
im->hfe.write_batch.dirty = TRUE;
|
||||
|
||||
im->hfe.trk_pos += nr;
|
||||
if (im->hfe.trk_pos >= im->hfe.trk_len) {
|
||||
ASSERT(im->hfe.trk_pos == im->hfe.trk_len);
|
||||
im->hfe.trk_pos = 0;
|
||||
im->hfe.write.wrapped = TRUE;
|
||||
i = 0;
|
||||
|
||||
if (im->hfe.fresh_seek && is_v3 && (off & 255) >= 1) {
|
||||
/* Avoid writing in the middle of an opcode. */
|
||||
char b = *(w-1);
|
||||
if ((off & 255) >= 2)
|
||||
if ((*(w-2) & 0xf) == 0xf && (*(w-2) >> 4) == OP_skip) {
|
||||
w++;
|
||||
i++;
|
||||
}
|
||||
if ((b & 0xf) == 0xf) {
|
||||
switch (b >> 4) {
|
||||
case OP_skip:
|
||||
w += 2;
|
||||
i += 2;
|
||||
break;
|
||||
case OP_bitrate:
|
||||
w++;
|
||||
i++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
im->hfe.fresh_seek = FALSE;
|
||||
|
||||
for (; i < nr; i++) {
|
||||
if (is_v3 && (*w & 0xf) == 0xf) {
|
||||
switch (*w >> 4) {
|
||||
case OP_skip:
|
||||
/* Don't bother; these bits are unlikely to matter. */
|
||||
w++;
|
||||
i++;
|
||||
/* fallthrough */
|
||||
case OP_bitrate:
|
||||
/* Assume bitrate does not change for the entire track, and
|
||||
* write_bc_ticks already adjusted when reading. */
|
||||
w++;
|
||||
i++;
|
||||
/* fallthrough */
|
||||
case OP_nop:
|
||||
case OP_index:
|
||||
default:
|
||||
/* Preserve opcode. But making sure not to write past end of
|
||||
* buffer. */
|
||||
w++;
|
||||
continue;
|
||||
|
||||
case OP_rand:
|
||||
/* Replace with data. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
b = _rbit32(buf[c++ & bufmask]) >> 24;
|
||||
/* HFEv3 can't handle a run of 1s as it will appear like an opcode.
|
||||
* If we encounter such a run, then either it is garbage or the
|
||||
* file needs twice the bitrate. Assume garbage; a bad bitrate would
|
||||
* fail rapidly. */
|
||||
if (is_v3 && (b & 0xf) == 0xf)
|
||||
b ^= 2;
|
||||
*w++ = b;
|
||||
}
|
||||
|
||||
im->hfe.trk_pos += i; /* i may be larger than nr due to opcodes. */
|
||||
if (im->hfe.trk_pos >= im->hfe.trk_len)
|
||||
im->hfe.trk_pos = 0;
|
||||
}
|
||||
|
||||
if (writeback) {
|
||||
/* If writeback requested then ensure we get called again. */
|
||||
flush = FALSE;
|
||||
} else if (flush) {
|
||||
/* If this is the final call, we should do writeback. */
|
||||
writeback = TRUE;
|
||||
}
|
||||
|
||||
if (writeback && im->hfe.write_batch.dirty) {
|
||||
t = time_now();
|
||||
printk("Write %u-%u (%u)... ",
|
||||
im->hfe.write_batch.off,
|
||||
im->hfe.write_batch.off + im->hfe.write_batch.len - 1,
|
||||
im->hfe.write_batch.len);
|
||||
F_write(&im->fp, wrbuf, im->hfe.write_batch.len, NULL);
|
||||
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
|
||||
im->hfe.write_batch.len = 0;
|
||||
im->hfe.write_batch.dirty = FALSE;
|
||||
}
|
||||
|
||||
if (flush && im->hfe.write.wrapped
|
||||
&& (im->hfe.trk_pos > im->hfe.write.start))
|
||||
printk("Wrapped (%u > %u)\n", im->hfe.trk_pos, im->hfe.write.start);
|
||||
if (flush)
|
||||
file_cache_sync(im->hfe.fcache);
|
||||
else
|
||||
file_cache_progress(im->hfe.fcache);
|
||||
|
||||
wr->cons = c * 8;
|
||||
|
||||
return flush;
|
||||
}
|
||||
|
||||
static void hfe_sync(struct image *im)
|
||||
{
|
||||
file_cache_sync_wait(im->hfe.fcache);
|
||||
file_cache_shutdown(im->hfe.fcache);
|
||||
}
|
||||
|
||||
const struct image_handler hfe_image_handler = {
|
||||
.open = hfe_open,
|
||||
.setup_track = hfe_setup_track,
|
||||
.read_track = hfe_read_track,
|
||||
.rdata_flux = hfe_rdata_flux,
|
||||
.write_track = hfe_write_track,
|
||||
.sync = hfe_sync,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ extern const struct image_handler adf_image_handler;
|
|||
extern const struct image_handler atr_image_handler;
|
||||
extern const struct image_handler hfe_image_handler;
|
||||
extern const struct image_handler img_image_handler;
|
||||
extern const struct image_handler sf7_image_handler;
|
||||
extern const struct image_handler st_image_handler;
|
||||
extern const struct image_handler d81_image_handler;
|
||||
extern const struct image_handler dsk_image_handler;
|
||||
|
|
@ -45,6 +46,7 @@ const struct image_type image_type[] = {
|
|||
{ "img", &img_image_handler },
|
||||
{ "ima", &img_image_handler },
|
||||
{ "out", &img_image_handler },
|
||||
{ "sf7", &sf7_image_handler },
|
||||
{ "st", &st_image_handler },
|
||||
{ "adl", &adfs_image_handler },
|
||||
{ "adm", &adfs_image_handler },
|
||||
|
|
@ -116,7 +118,7 @@ static bool_t try_handler(struct image *im, struct slot *slot,
|
|||
|
||||
/* Sensible defaults. */
|
||||
im->sync = SYNC_mfm;
|
||||
im->write_bc_ticks = sysclk_us(2);
|
||||
im->write_bc_ticks = sampleclk_us(2);
|
||||
im->stk_per_rev = stk_ms(200);
|
||||
|
||||
im->disk_handler = im->track_handler = handler;
|
||||
|
|
@ -132,7 +134,8 @@ static bool_t try_handler(struct image *im, struct slot *slot,
|
|||
|
||||
#if !defined(QUICKDISK)
|
||||
|
||||
void image_open(struct image *im, struct slot *slot, DWORD *cltbl)
|
||||
void image_open(struct image *im, struct slot *slot, DWORD *cltbl,
|
||||
bool_t da_mode)
|
||||
{
|
||||
static const struct image_handler * const image_handlers[] = {
|
||||
/* Special handler for dummy slots (empty HxC slot 0). */
|
||||
|
|
@ -147,6 +150,12 @@ void image_open(struct image *im, struct slot *slot, DWORD *cltbl)
|
|||
const struct image_type *type;
|
||||
int i;
|
||||
|
||||
if (da_mode) {
|
||||
if (try_handler(im, slot, cltbl, &da_image_handler))
|
||||
return;
|
||||
F_die(FR_BAD_IMAGE);
|
||||
}
|
||||
|
||||
/* Extract filename extension (if available). */
|
||||
memcpy(ext, slot->type, sizeof(slot->type));
|
||||
ext[sizeof(slot->type)] = '\0';
|
||||
|
|
@ -197,7 +206,8 @@ void image_open(struct image *im, struct slot *slot, DWORD *cltbl)
|
|||
|
||||
#else /* defined(QUICKDISK) */
|
||||
|
||||
void image_open(struct image *im, struct slot *slot, DWORD *cltbl)
|
||||
void image_open(struct image *im, struct slot *slot, DWORD *cltbl,
|
||||
bool_t da_mode)
|
||||
{
|
||||
if (try_handler(im, slot, cltbl, &qd_image_handler))
|
||||
return;
|
||||
|
|
@ -208,6 +218,15 @@ void image_open(struct image *im, struct slot *slot, DWORD *cltbl)
|
|||
|
||||
#endif
|
||||
|
||||
bool_t image_in_da_mode(struct image *im)
|
||||
{
|
||||
#if !defined(QUICKDISK)
|
||||
return im->disk_handler == &da_image_handler;
|
||||
#else
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void image_extend(struct image *im)
|
||||
{
|
||||
FSIZE_t new_sz;
|
||||
|
|
@ -252,30 +271,23 @@ static void print_image_info(struct image *im)
|
|||
lcd_write(0, 2, -1, msg);
|
||||
}
|
||||
|
||||
bool_t image_setup_track(
|
||||
void image_setup_track(
|
||||
struct image *im, uint16_t track, uint32_t *start_pos)
|
||||
{
|
||||
const struct image_handler *h = im->track_handler;
|
||||
|
||||
#if !defined(QUICKDISK)
|
||||
if (!in_da_mode(im, track>>1)) {
|
||||
/* If we are exiting D-A mode then need to re-read the config file. */
|
||||
if (h == &da_image_handler)
|
||||
return TRUE;
|
||||
h = ((track>>1) >= im->nr_cyls) ? &dummy_image_handler
|
||||
if (h != &da_image_handler)
|
||||
h = ((track>>1) >= im_nphys_cyls(im)) ? &dummy_image_handler
|
||||
: im->disk_handler;
|
||||
} else {
|
||||
h = &da_image_handler;
|
||||
im->nr_sides = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (h != im->track_handler)
|
||||
image_sync(im);
|
||||
im->track_handler = h;
|
||||
h->setup_track(im, track, start_pos);
|
||||
|
||||
print_image_info(im);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool_t image_read_track(struct image *im)
|
||||
|
|
@ -293,6 +305,11 @@ bool_t image_write_track(struct image *im)
|
|||
return im->track_handler->write_track(im);
|
||||
}
|
||||
|
||||
void image_sync(struct image *im)
|
||||
{
|
||||
im->track_handler->sync(im);
|
||||
}
|
||||
|
||||
uint32_t image_ticks_since_index(struct image *im)
|
||||
{
|
||||
uint32_t ticks = im->cur_ticks - im->ticks_since_flux;
|
||||
|
|
|
|||
514
src/image/img.c
514
src/image/img.c
File diff suppressed because it is too large
Load diff
|
|
@ -74,6 +74,7 @@ void mfm_ring_to_bin(const uint16_t *ring, unsigned int mask,
|
|||
unsigned int idx, void *out, unsigned int nr)
|
||||
{
|
||||
unsigned int head;
|
||||
idx &= mask;
|
||||
head = min_t(unsigned int, nr, mask+1-idx);
|
||||
mfm_to_bin(&ring[idx], out, head);
|
||||
if (head != nr)
|
||||
|
|
|
|||
131
src/image/qd.c
131
src/image/qd.c
|
|
@ -33,14 +33,13 @@ static bool_t qd_open(struct image *im)
|
|||
im->qd.tb = 1;
|
||||
im->nr_cyls = 1;
|
||||
im->nr_sides = 1;
|
||||
im->write_bc_ticks = sysclk_us(4) + 66; /* 4.917us */
|
||||
im->write_bc_ticks = sampleclk_us(4) + 66; /* 4.917us */
|
||||
im->ticks_per_cell = im->write_bc_ticks;
|
||||
im->sync = SYNC_none;
|
||||
|
||||
ASSERT(8*512 <= im->bufs.read_data.len);
|
||||
volume_cache_init(im->bufs.read_data.p + 8*512,
|
||||
im->bufs.read_data.p + im->bufs.read_data.len);
|
||||
volume_cache_metadata_only(&im->fp);
|
||||
im->qd.fcache = file_cache_init(&im->fp, 2,
|
||||
im->bufs.read_data.p,
|
||||
im->bufs.read_data.p + im->bufs.read_data.len);
|
||||
|
||||
/* There is only one track: Seek to it. */
|
||||
qd_seek_track(im, 0);
|
||||
|
|
@ -60,11 +59,13 @@ static void qd_seek_track(struct image *im, uint16_t track)
|
|||
im->qd.trk_len = le32toh(thdr.len);
|
||||
|
||||
/* Read/write window limits in STK ticks from data start. */
|
||||
im->qd.win_start = le32toh(thdr.win_start) * im->write_bc_ticks;
|
||||
im->qd.win_end = le32toh(thdr.win_end) * im->write_bc_ticks;
|
||||
im->qd.win_start = (le32toh(thdr.win_start) * im->write_bc_ticks
|
||||
* ((8 * STK_MHZ) / SAMPLECLK_MHZ));
|
||||
im->qd.win_end = (le32toh(thdr.win_end) * im->write_bc_ticks
|
||||
* ((8 * STK_MHZ) / SAMPLECLK_MHZ));
|
||||
|
||||
im->tracklen_bc = im->qd.trk_len * 8;
|
||||
im->stk_per_rev = stk_sysclk(im->tracklen_bc * im->write_bc_ticks);
|
||||
im->stk_per_rev = stk_sampleclk(im->tracklen_bc * im->write_bc_ticks);
|
||||
|
||||
im->cur_track = track;
|
||||
}
|
||||
|
|
@ -72,60 +73,42 @@ static void qd_seek_track(struct image *im, uint16_t track)
|
|||
static void qd_setup_track(
|
||||
struct image *im, uint16_t track, uint32_t *start_pos)
|
||||
{
|
||||
struct image_buf *rd = &im->bufs.read_data;
|
||||
struct image_buf *bc = &im->bufs.read_bc;
|
||||
uint32_t sys_ticks;
|
||||
uint32_t start_ticks;
|
||||
|
||||
sys_ticks = start_pos ? *start_pos : get_write(im, im->wr_cons)->start;
|
||||
im->cur_bc = sys_ticks / im->ticks_per_cell;
|
||||
start_ticks = start_pos ? *start_pos : get_write(im, im->wr_cons)->start;
|
||||
im->cur_bc = start_ticks / im->ticks_per_cell;
|
||||
if (im->cur_bc >= im->tracklen_bc)
|
||||
im->cur_bc = 0;
|
||||
im->cur_ticks = im->cur_bc * im->ticks_per_cell;
|
||||
im->ticks_since_flux = 0;
|
||||
|
||||
sys_ticks = im->cur_ticks;
|
||||
start_ticks = im->cur_ticks;
|
||||
|
||||
rd->prod = rd->cons = 0;
|
||||
bc->prod = bc->cons = 0;
|
||||
|
||||
file_cache_readahead(im->qd.fcache,
|
||||
im->qd.trk_off, im->qd.trk_len, 12*1024);
|
||||
if (start_pos) {
|
||||
/* Read mode. */
|
||||
im->qd.trk_pos = (im->cur_bc/8) & ~511;
|
||||
image_read_track(im);
|
||||
/* Consumer may be ahead of producer, but only until the first read
|
||||
* completes. */
|
||||
bc->cons = im->cur_bc & 4095;
|
||||
*start_pos = sys_ticks;
|
||||
*start_pos = start_ticks;
|
||||
} else {
|
||||
/* Write mode. */
|
||||
im->qd.trk_pos = im->cur_bc / 8;
|
||||
im->qd.write.start = im->qd.trk_pos;
|
||||
im->qd.write.wrapped = FALSE;
|
||||
im->qd.write_batch.len = 0;
|
||||
im->qd.write_batch.dirty = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static bool_t qd_read_track(struct image *im)
|
||||
{
|
||||
const unsigned int batch_secs = 2;
|
||||
struct image_buf *rd = &im->bufs.read_data;
|
||||
struct image_buf *bc = &im->bufs.read_bc;
|
||||
uint8_t *buf = rd->p;
|
||||
uint8_t *bc_b = bc->p;
|
||||
uint32_t bc_len, bc_mask, bc_space, bc_p, bc_c;
|
||||
unsigned int nr_sec;
|
||||
|
||||
if (rd->prod == rd->cons) {
|
||||
nr_sec = min_t(unsigned int, batch_secs,
|
||||
(im->qd.trk_len+511 - im->qd.trk_pos) / 512);
|
||||
F_lseek(&im->fp, im->qd.trk_off + im->qd.trk_pos);
|
||||
F_read(&im->fp, buf, nr_sec*512, NULL);
|
||||
rd->cons = 0;
|
||||
rd->prod = nr_sec;
|
||||
im->qd.trk_pos += nr_sec * 512;
|
||||
if (im->qd.trk_pos >= im->qd.trk_len)
|
||||
im->qd.trk_pos = 0;
|
||||
}
|
||||
|
||||
/* Fill the raw-bitcell ring buffer. */
|
||||
bc_p = bc->prod / 8;
|
||||
bc_c = bc->cons / 8;
|
||||
|
|
@ -133,13 +116,20 @@ static bool_t qd_read_track(struct image *im)
|
|||
bc_mask = bc_len - 1;
|
||||
bc_space = bc_len - (uint16_t)(bc_p - bc_c);
|
||||
|
||||
nr_sec = min_t(unsigned int, rd->prod - rd->cons, bc_space/512);
|
||||
if (nr_sec == 0)
|
||||
nr_sec = bc_space/512;
|
||||
if (nr_sec == 0) {
|
||||
file_cache_progress(im->qd.fcache);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (nr_sec--) {
|
||||
memcpy(&bc_b[bc_p & bc_mask], &buf[rd->cons*512], 512);
|
||||
rd->cons++;
|
||||
if (!file_cache_try_read(im->qd.fcache,
|
||||
&bc_b[bc_p & bc_mask], im->qd.trk_off + im->qd.trk_pos,
|
||||
512))
|
||||
break;
|
||||
im->qd.trk_pos += 512;
|
||||
if (im->qd.trk_pos >= im->qd.trk_len)
|
||||
im->qd.trk_pos = 0;
|
||||
bc_p += 512;
|
||||
}
|
||||
|
||||
|
|
@ -197,16 +187,13 @@ out:
|
|||
|
||||
static bool_t qd_write_track(struct image *im)
|
||||
{
|
||||
const unsigned int batch_secs = 8;
|
||||
bool_t flush;
|
||||
struct write *write = get_write(im, im->wr_cons);
|
||||
struct image_buf *wr = &im->bufs.write_bc;
|
||||
uint8_t *buf = wr->p;
|
||||
unsigned int bufmask = wr->len - 1;
|
||||
uint8_t *w, *wrbuf = im->bufs.write_data.p;
|
||||
uint8_t *w;
|
||||
uint32_t i, space, c = wr->cons / 8, p = wr->prod / 8;
|
||||
bool_t writeback = FALSE;
|
||||
time_t t;
|
||||
|
||||
/* If we are processing final data then use the end index, rounded to
|
||||
* nearest. */
|
||||
|
|
@ -215,17 +202,6 @@ static bool_t qd_write_track(struct image *im)
|
|||
if (flush)
|
||||
p = (write->bc_end + 4) / 8;
|
||||
|
||||
if (im->qd.write_batch.len == 0) {
|
||||
ASSERT(!im->qd.write_batch.dirty);
|
||||
im->qd.write_batch.off = im->qd.trk_pos & ~511;
|
||||
im->qd.write_batch.len = min_t(
|
||||
uint32_t, batch_secs * 512,
|
||||
((im->qd.trk_len + 511) & ~511) - im->qd.write_batch.off);
|
||||
F_lseek(&im->fp, im->qd.trk_off + im->qd.write_batch.off);
|
||||
F_read(&im->fp, wrbuf, im->qd.write_batch.len, NULL);
|
||||
F_lseek(&im->fp, im->qd.trk_off + im->qd.write_batch.off);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
|
||||
uint32_t off = im->qd.trk_pos;
|
||||
|
|
@ -242,62 +218,45 @@ static bool_t qd_write_track(struct image *im)
|
|||
if (nr == 0)
|
||||
break;
|
||||
|
||||
/* Bail if required data not in the write buffer. */
|
||||
if ((off < im->qd.write_batch.off)
|
||||
|| (off >= (im->qd.write_batch.off + im->qd.write_batch.len))) {
|
||||
writeback = TRUE;
|
||||
/* Encode into the sector buffer for later write-out. */
|
||||
if ((w = file_cache_peek_write(im->qd.fcache, off & ~511)) == NULL) {
|
||||
flush = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Encode into the sector buffer for later write-out. */
|
||||
w = &wrbuf[off - im->qd.write_batch.off];
|
||||
w += off & 511;
|
||||
for (i = 0; i < nr; i++)
|
||||
*w++ = _rbit32(buf[c++ & bufmask]) >> 24;
|
||||
im->qd.write_batch.dirty = TRUE;
|
||||
|
||||
im->qd.trk_pos += nr;
|
||||
if (im->qd.trk_pos >= im->qd.trk_len) {
|
||||
ASSERT(im->qd.trk_pos == im->qd.trk_len);
|
||||
im->qd.trk_pos = 0;
|
||||
im->qd.write.wrapped = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (writeback) {
|
||||
/* If writeback requested then ensure we get called again. */
|
||||
flush = FALSE;
|
||||
} else if (flush) {
|
||||
/* If this is the final call, we should do writeback. */
|
||||
writeback = TRUE;
|
||||
}
|
||||
|
||||
if (writeback && im->qd.write_batch.dirty) {
|
||||
t = time_now();
|
||||
printk("Write %u-%u (%u)... ",
|
||||
im->qd.write_batch.off,
|
||||
im->qd.write_batch.off + im->qd.write_batch.len - 1,
|
||||
im->qd.write_batch.len);
|
||||
F_write(&im->fp, wrbuf, im->qd.write_batch.len, NULL);
|
||||
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
|
||||
im->qd.write_batch.len = 0;
|
||||
im->qd.write_batch.dirty = FALSE;
|
||||
}
|
||||
|
||||
if (flush && im->qd.write.wrapped
|
||||
&& (im->qd.trk_pos > im->qd.write.start))
|
||||
printk("Wrapped (%u > %u)\n", im->qd.trk_pos, im->qd.write.start);
|
||||
if (flush)
|
||||
file_cache_sync(im->qd.fcache);
|
||||
else
|
||||
file_cache_progress(im->qd.fcache);
|
||||
|
||||
wr->cons = c * 8;
|
||||
|
||||
return flush;
|
||||
}
|
||||
|
||||
static void qd_sync(struct image *im)
|
||||
{
|
||||
file_cache_sync_wait(im->qd.fcache);
|
||||
file_cache_shutdown(im->qd.fcache);
|
||||
}
|
||||
|
||||
const struct image_handler qd_image_handler = {
|
||||
.open = qd_open,
|
||||
.setup_track = qd_setup_track,
|
||||
.read_track = qd_read_track,
|
||||
.rdata_flux = qd_rdata_flux,
|
||||
.write_track = qd_write_track,
|
||||
.sync = qd_sync,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ uint8_t board_id;
|
|||
#define DOWN 0x40
|
||||
#define UP 0x00
|
||||
|
||||
#define INVALID 0xff
|
||||
|
||||
static GPIO gpio(uint8_t x)
|
||||
{
|
||||
switch (x&0x30) {
|
||||
|
|
@ -42,14 +44,15 @@ static uint8_t inputs[] = {
|
|||
GPIOA | 8 | DOWN, /* 22: Write Data */
|
||||
GPIOB | 9 | DOWN, /* 24: Write Gate */
|
||||
GPIOB | 4 | DOWN, /* 32: Side Select */
|
||||
|
||||
};
|
||||
#if 0 /* Buttons and encoder are handled via board.c */
|
||||
/* Buttons and encoder inputs are switch-to-ground. So we pull them up. */
|
||||
GPIOC | 8 | UP , /* Button: Down/Left */
|
||||
GPIOC | 7 | UP , /* Button: Up/Right */
|
||||
GPIOC | 6 | UP , /* Button: Select (Jumper JA) */
|
||||
GPIOC | 10 | UP , /* Rotary CLK (J7-1) */
|
||||
GPIOC | 11 | UP , /* Rotary DAT (J7-2) */
|
||||
};
|
||||
#endif
|
||||
|
||||
static uint8_t outputs[] = {
|
||||
GPIOB | 7 , /* 2: Disk Change/Density */
|
||||
|
|
@ -66,11 +69,23 @@ static void io_test(bool_t assert)
|
|||
|
||||
uint8_t d[3] = { 0 };
|
||||
char p[20], *q = p;
|
||||
int i;
|
||||
int i, b;
|
||||
|
||||
/* 0-7 */
|
||||
for (i = 0; i < ARRAY_SIZE(inputs); i++) {
|
||||
uint8_t x = inputs[i];
|
||||
if (gpio_read_pin(gpio(x), x&15)) {
|
||||
if ((x != INVALID) && gpio_read_pin(gpio(x), x&15)) {
|
||||
*q++ = char_map[i];
|
||||
d[i/7] |= 1 << (i%7);
|
||||
} else {
|
||||
*q++ = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
/* 8-12 */
|
||||
b = (~board_get_buttons() & 7) | (board_get_rotary() << 3);
|
||||
for (; i < 13; i++) {
|
||||
if (b & (1<<(i-8))) {
|
||||
*q++ = char_map[i];
|
||||
d[i/7] |= 1 << (i%7);
|
||||
} else {
|
||||
|
|
@ -114,7 +129,7 @@ int main(void)
|
|||
int i;
|
||||
|
||||
/* Relocate DATA. Initialise BSS. */
|
||||
if (_sdat != _ldat)
|
||||
if (&_sdat[0] != &_ldat[0])
|
||||
memcpy(_sdat, _ldat, _edat-_sdat);
|
||||
memset(_sbss, 0, _ebss-_sbss);
|
||||
|
||||
|
|
@ -125,7 +140,7 @@ int main(void)
|
|||
board_init();
|
||||
delay_ms(200); /* 5v settle */
|
||||
|
||||
printk("\n** FF I/O Test for Gotek\n", fw_ver);
|
||||
printk("\n** FF I/O Test %s for Gotek\n", fw_ver);
|
||||
printk("** Keir Fraser <keir.xen@gmail.com>\n");
|
||||
printk("** https://github.com/keirf/FlashFloppy\n\n");
|
||||
|
||||
|
|
@ -134,12 +149,26 @@ int main(void)
|
|||
display_init();
|
||||
display_setting(TRUE);
|
||||
|
||||
/* Standard Gotek: optional motor signal is PB15. */
|
||||
if (!gotek_enhanced())
|
||||
inputs[2] |= GPIOB;
|
||||
if (mcu_package == MCU_QFN32) {
|
||||
inputs[6] = GPIOB | 1 | DOWN; /* wgate */
|
||||
outputs[0] = GPIOA | 14; /* pin_02, dskchg/den */
|
||||
outputs[2] = GPIOA | 13; /* pin_26, trk00 */
|
||||
}
|
||||
|
||||
if (!gotek_enhanced()) {
|
||||
inputs[1] = INVALID; /* no SELB */
|
||||
if (has_kc30_header == 2) {
|
||||
inputs[2] = GPIOB | 12 | DOWN;
|
||||
} else {
|
||||
/* Standard Gotek: optional motor signal is PB15. */
|
||||
inputs[2] = GPIOB | 15 | DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(inputs); i++) {
|
||||
uint8_t x = inputs[i];
|
||||
if (x == INVALID)
|
||||
continue;
|
||||
gpio_configure_pin(gpio(x), x&15,
|
||||
(x&DOWN) ? GPI_pull_down : GPI_pull_up);
|
||||
}
|
||||
|
|
|
|||
240
src/main.c
240
src/main.c
|
|
@ -83,6 +83,12 @@ void flashfloppy_fill_fileinfo(FIL *fp);
|
|||
} while(0)
|
||||
#endif
|
||||
|
||||
bool_t lba_within_fat_volume(uint32_t lba)
|
||||
{
|
||||
/* Also disallows access to the boot/bpb sector of the mounted volume. */
|
||||
return (lba > fatfs.volbase) && (lba <= fatfs.volend);
|
||||
}
|
||||
|
||||
static bool_t slot_valid(unsigned int i)
|
||||
{
|
||||
if (i > cfg.max_slot_nr)
|
||||
|
|
@ -472,26 +478,21 @@ static uint8_t read_rotary(uint8_t rotary)
|
|||
static struct timer button_timer;
|
||||
static volatile uint8_t buttons, velocity;
|
||||
static uint8_t rotary, rb;
|
||||
#define B_LEFT 1
|
||||
#define B_RIGHT 2
|
||||
#define B_SELECT 4
|
||||
|
||||
void IRQ_rotary(void)
|
||||
{
|
||||
if ((ff_cfg.rotary & ROT_typemask) != ROT_full)
|
||||
return;
|
||||
rotary = ((rotary << 2) | ((gpioc->idr >> 10) & 3)) & 15;
|
||||
rotary = ((rotary << 2) | board_get_rotary()) & 15;
|
||||
rb = read_rotary(rotary) ?: rb;
|
||||
}
|
||||
|
||||
static void set_rotary_exti(void)
|
||||
{
|
||||
uint32_t imr;
|
||||
|
||||
imr = exti->imr & ~0x0c00;
|
||||
exti->imr &= ~board_rotary_exti_mask;
|
||||
board_rotary_exti_mask = 0;
|
||||
if ((ff_cfg.rotary & ROT_typemask) == ROT_full)
|
||||
imr |= 0x0c00;
|
||||
exti->imr = imr;
|
||||
board_setup_rotary_exti();
|
||||
}
|
||||
|
||||
static void button_timer_fn(void *unused)
|
||||
|
|
@ -502,7 +503,7 @@ static void button_timer_fn(void *unused)
|
|||
|
||||
static uint16_t cur_time, prev_time;
|
||||
static uint32_t _b[3]; /* 0 = left, 1 = right, 2 = select */
|
||||
uint8_t b = osd_buttons_rx;
|
||||
uint8_t x, b = osd_buttons_rx;
|
||||
bool_t twobutton_rotary =
|
||||
(ff_cfg.twobutton_action & TWOBUTTON_mask) == TWOBUTTON_rotary;
|
||||
int i, twobutton_reverse = !!(ff_cfg.twobutton_action & TWOBUTTON_reverse);
|
||||
|
|
@ -521,9 +522,11 @@ static void button_timer_fn(void *unused)
|
|||
|
||||
/* We debounce the switches by waiting for them to be pressed continuously
|
||||
* for 32 consecutive sample periods (32 * 2ms == 64ms) */
|
||||
x = ~board_get_buttons();
|
||||
for (i = 0; i < 3; i++) {
|
||||
_b[i] <<= 1;
|
||||
_b[i] |= gpio_read_pin(gpioc, 8-i);
|
||||
_b[i] |= x & 1;
|
||||
x >>= 1;
|
||||
}
|
||||
|
||||
if (_b[twobutton_reverse] == 0)
|
||||
|
|
@ -535,7 +538,7 @@ static void button_timer_fn(void *unused)
|
|||
if (_b[2] == 0)
|
||||
b |= B_SELECT;
|
||||
|
||||
rotary = ((rotary << 2) | ((gpioc->idr >> 10) & 3)) & 15;
|
||||
rotary = ((rotary << 2) | board_get_rotary()) & 15;
|
||||
switch (ff_cfg.rotary & ROT_typemask) {
|
||||
|
||||
case ROT_trackball: {
|
||||
|
|
@ -606,13 +609,15 @@ static void button_timer_fn(void *unused)
|
|||
|
||||
static void canary_init(void)
|
||||
{
|
||||
_irq_stackbottom[0] = _thread_stackbottom[0] = 0xdeadbeef;
|
||||
_irq_stackbottom[0] = _thread_stackbottom[0] = _thread1_stackbottom[0]
|
||||
= 0xdeadbeef;
|
||||
}
|
||||
|
||||
static void canary_check(void)
|
||||
{
|
||||
ASSERT(_irq_stackbottom[0] == 0xdeadbeef);
|
||||
ASSERT(_thread_stackbottom[0] == 0xdeadbeef);
|
||||
ASSERT(_thread1_stackbottom[0] == 0xdeadbeef);
|
||||
}
|
||||
|
||||
static void fix_hxc_short_slot(struct short_slot *short_slot)
|
||||
|
|
@ -912,6 +917,30 @@ static uint8_t parse_pin_str(const char *s)
|
|||
return pin;
|
||||
}
|
||||
|
||||
static uint16_t parse_display_order(const char *p)
|
||||
{
|
||||
int sh = 0;
|
||||
uint16_t order = 0;
|
||||
|
||||
if (!strcmp(p, "default"))
|
||||
return DORD_default;
|
||||
|
||||
while (p != NULL) {
|
||||
order |= ((p[0]-'0')&7) << sh;
|
||||
if (p[1] == 'd')
|
||||
order |= DORD_double << sh;
|
||||
sh += DORD_shift;
|
||||
if ((p = strchr(p, ',')) == NULL)
|
||||
break;
|
||||
p++;
|
||||
}
|
||||
|
||||
if (sh < 16)
|
||||
order |= 0x7777 << sh;
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
static void read_ff_cfg(void)
|
||||
{
|
||||
enum {
|
||||
|
|
@ -992,6 +1021,10 @@ static void read_ff_cfg(void)
|
|||
ff_cfg.write_protect = !strcmp(opts.arg, "yes");
|
||||
break;
|
||||
|
||||
case FFCFG_max_cyl:
|
||||
ff_cfg.max_cyl = strtol(opts.arg, NULL, 10);
|
||||
break;
|
||||
|
||||
case FFCFG_side_select_glitch_filter:
|
||||
ff_cfg.side_select_glitch_filter = strtol(opts.arg, NULL, 10);
|
||||
break;
|
||||
|
|
@ -1175,6 +1208,10 @@ static void read_ff_cfg(void)
|
|||
ff_cfg.display_type |= DISPLAY_inverse;
|
||||
} else if (!strcmp(p, "ztech")) {
|
||||
ff_cfg.display_type |= DISPLAY_ztech;
|
||||
} else if (!strcmp(p, "slow")) {
|
||||
ff_cfg.display_type |= DISPLAY_slow;
|
||||
} else if (!strcmp(p, "hflip")) {
|
||||
ff_cfg.display_type |= DISPLAY_hflip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1191,26 +1228,17 @@ static void read_ff_cfg(void)
|
|||
ff_cfg.oled_contrast = strtol(opts.arg, NULL, 10);
|
||||
break;
|
||||
|
||||
case FFCFG_display_order: {
|
||||
char *p = opts.arg;
|
||||
int sh = 0;
|
||||
ff_cfg.display_order = DORD_default;
|
||||
if (!strcmp(p, "default"))
|
||||
break;
|
||||
ff_cfg.display_order = 0;
|
||||
while (p != NULL) {
|
||||
ff_cfg.display_order |= ((p[0]-'0')&7) << sh;
|
||||
if (p[1] == 'd')
|
||||
ff_cfg.display_order |= DORD_double << sh;
|
||||
sh += DORD_shift;
|
||||
if ((p = strchr(p, ',')) == NULL)
|
||||
break;
|
||||
p++;
|
||||
}
|
||||
if (sh < 16)
|
||||
ff_cfg.display_order |= 0x7777 << sh;
|
||||
case FFCFG_display_order:
|
||||
ff_cfg.display_order = parse_display_order(opts.arg);
|
||||
break;
|
||||
|
||||
case FFCFG_osd_display_order:
|
||||
ff_cfg.osd_display_order = parse_display_order(opts.arg);
|
||||
break;
|
||||
|
||||
case FFCFG_osd_columns:
|
||||
ff_cfg.osd_columns = strtol(opts.arg, NULL, 10);
|
||||
break;
|
||||
}
|
||||
|
||||
case FFCFG_display_off_secs:
|
||||
ff_cfg.display_off_secs = strtol(opts.arg, NULL, 10);
|
||||
|
|
@ -1254,6 +1282,25 @@ static void read_ff_cfg(void)
|
|||
break;
|
||||
}
|
||||
|
||||
case FFCFG_notify_volume: {
|
||||
char *p, *q;
|
||||
ff_cfg.notify_volume = 0;
|
||||
for (p = opts.arg; *p != '\0'; p = q) {
|
||||
for (q = p; *q && *q != ','; q++)
|
||||
continue;
|
||||
if (*q == ',')
|
||||
*q++ = '\0';
|
||||
if (!strcmp(p, "slotnr")) {
|
||||
ff_cfg.notify_volume |= NOTIFY_slotnr;
|
||||
} else {
|
||||
ff_cfg.notify_volume &= ~NOTIFY_volume_mask;
|
||||
ff_cfg.notify_volume |= strtol(p, NULL, 10)
|
||||
& NOTIFY_volume_mask;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FFCFG_da_report_version:
|
||||
memset(ff_cfg.da_report_version, 0,
|
||||
sizeof(ff_cfg.da_report_version));
|
||||
|
|
@ -1276,14 +1323,31 @@ static void read_ff_cfg(void)
|
|||
|
||||
static void process_ff_cfg_opts(const struct ff_cfg *old)
|
||||
{
|
||||
if (ff_cfg.rotary != old->rotary)
|
||||
set_rotary_exti();
|
||||
/* chgrst, motor-delay: Reset EXTI handlers. */
|
||||
if ((ff_cfg.motor_delay != old->motor_delay)
|
||||
|| (ff_cfg.chgrst != old->chgrst)) {
|
||||
exti->imr &= ~motor_chgrst_exti_mask;
|
||||
motor_chgrst_exti_mask = 0;
|
||||
}
|
||||
|
||||
/* interface, pin02, pin34: Inform the floppy subsystem. */
|
||||
/* rotary, chgrst, motor-delay: Inform the rotary-encoder subsystem.
|
||||
* It is harmless to reset rotary EXTI handlers unconditionally. */
|
||||
set_rotary_exti();
|
||||
|
||||
/* interface, pin02, pin34, chgrst, motor-delay: Inform the floppy
|
||||
* subsystem. */
|
||||
if ((ff_cfg.interface != old->interface)
|
||||
|| (ff_cfg.pin02 != old->pin02)
|
||||
|| (ff_cfg.pin34 != old->pin34))
|
||||
|| (ff_cfg.pin34 != old->pin34)
|
||||
|| (ff_cfg.motor_delay != old->motor_delay)
|
||||
|| (ff_cfg.chgrst != old->chgrst)) {
|
||||
floppy_set_fintf_mode();
|
||||
motor_chgrst_setup_exti();
|
||||
}
|
||||
|
||||
/* max-cyl: Inform the floppy subsystem. */
|
||||
if (ff_cfg.max_cyl != old->max_cyl)
|
||||
floppy_set_max_cyl();
|
||||
|
||||
/* ejected-on-startup: Set the ejected state appropriately. */
|
||||
if (ff_cfg.ejected_on_startup)
|
||||
|
|
@ -1825,7 +1889,8 @@ indexed_mode:
|
|||
fs->fp.fname[0] != '\0';
|
||||
F_findnext(&fs->dp, &fs->fp)) {
|
||||
const char *p = fs->fp.fname
|
||||
+ strnlen(ff_cfg.indexed_prefix, 256);
|
||||
+ strnlen(ff_cfg.indexed_prefix,
|
||||
sizeof(ff_cfg.indexed_prefix));
|
||||
unsigned int idx = 0;
|
||||
/* Skip directories. */
|
||||
if (fs->fp.fattrib & AM_DIR)
|
||||
|
|
@ -2079,6 +2144,8 @@ static int run_floppy(void *_b)
|
|||
t_prev = t_now;
|
||||
}
|
||||
|
||||
floppy_sync();
|
||||
|
||||
if (display_type == DT_LED_7SEG)
|
||||
display_state = LED_NORMAL;
|
||||
|
||||
|
|
@ -2645,8 +2712,10 @@ static int floppy_main(void *unused)
|
|||
b = B_SELECT;
|
||||
} else {
|
||||
floppy_arena_teardown();
|
||||
speaker_notify_insert(cfg.slot_nr);
|
||||
fres = F_call_cancellable(run_floppy, &b);
|
||||
floppy_cancel();
|
||||
speaker_notify_eject();
|
||||
assert_volume_connected();
|
||||
if ((b != 0) && (display_type == DT_LCD_OLED)) {
|
||||
/* Immediate visual indication of button press. */
|
||||
|
|
@ -2775,6 +2844,24 @@ static bool_t main_menu_confirm(const char *op)
|
|||
return wait_twobutton_press(b) == B_SELECT;
|
||||
}
|
||||
|
||||
static void noinline mcu_info(void)
|
||||
{
|
||||
#if MCU == STM32F105
|
||||
static const char * const mcus[] = {
|
||||
"STM32F105", "AT32F415"
|
||||
};
|
||||
const char * const mcu = mcus[!!is_artery_mcu];
|
||||
#elif MCU == AT32F435
|
||||
const static char mcu[] = "AT32F435";
|
||||
#endif
|
||||
char msg[20];
|
||||
snprintf(msg, sizeof(msg), "%uMHz, %ukB", SYSCLK_MHZ, ram_kb);
|
||||
lcd_write(0, 0, -1, mcu);
|
||||
lcd_write(0, 1, -1, msg);
|
||||
while (!buttons)
|
||||
continue;
|
||||
}
|
||||
|
||||
static void factory_reset(void)
|
||||
{
|
||||
/* Inform user that factory reset is about to occur. */
|
||||
|
|
@ -2807,6 +2894,8 @@ static void factory_reset(void)
|
|||
|
||||
static void update_firmware(void)
|
||||
{
|
||||
#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;
|
||||
|
|
@ -2815,7 +2904,12 @@ static void update_firmware(void)
|
|||
bkp->dr1[0] = 0xdead;
|
||||
bkp->dr1[1] = 0xbeef;
|
||||
|
||||
/* Reset everything (except backup registers). */
|
||||
#elif MCU == AT32F435
|
||||
|
||||
_reset_flag = RESET_FLAG_BOOTLOADER;
|
||||
|
||||
#endif
|
||||
|
||||
system_reset();
|
||||
}
|
||||
|
||||
|
|
@ -2852,7 +2946,7 @@ static void ff_osd_configure(void)
|
|||
static void main_menu(void)
|
||||
{
|
||||
const static char *menu[] = {
|
||||
"**Main Menu**",
|
||||
"MCU Info",
|
||||
"Factory Reset",
|
||||
"Update Firmware",
|
||||
"Configure FF OSD",
|
||||
|
|
@ -2875,6 +2969,7 @@ static void main_menu(void)
|
|||
if (sel >= ARRAY_SIZE(menu))
|
||||
sel -= ARRAY_SIZE(menu);
|
||||
|
||||
lcd_write(0, 0, -1, "**Main Menu**");
|
||||
lcd_write(0, 1, -1, menu[sel]);
|
||||
lcd_on();
|
||||
|
||||
|
|
@ -2897,6 +2992,9 @@ static void main_menu(void)
|
|||
while (buttons)
|
||||
continue;
|
||||
switch (sel) {
|
||||
case 0: /* MCU Info */
|
||||
mcu_info();
|
||||
break;
|
||||
case 1: /* Factory Reset */
|
||||
if (main_menu_confirm("Reset"))
|
||||
factory_reset();
|
||||
|
|
@ -2908,7 +3006,7 @@ static void main_menu(void)
|
|||
case 3: /* Configure FF OSD */
|
||||
ff_osd_configure();
|
||||
break;
|
||||
case 0: case 4: /* Exit */
|
||||
case 4: /* Exit */
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
|
@ -2919,33 +3017,52 @@ out:
|
|||
display_mode = DM_normal;
|
||||
}
|
||||
|
||||
static void banner(void)
|
||||
static void noinline banner(void)
|
||||
{
|
||||
char msg[2][25];
|
||||
|
||||
switch (display_type) {
|
||||
|
||||
case DT_LED_7SEG:
|
||||
#if MCU == STM32F105
|
||||
#define sep_ch "-"
|
||||
#elif MCU == AT32F435
|
||||
#define sep_ch "z"
|
||||
#endif
|
||||
led_7seg_write_string(
|
||||
#if defined(LOGFILE)
|
||||
"LOG"
|
||||
#elif defined(QUICKDISK)
|
||||
(led_7seg_nr_digits() == 3) ? "Q-D" : "QD"
|
||||
(led_7seg_nr_digits() == 3) ? "Q"sep_ch"D" : "QD"
|
||||
#else
|
||||
(led_7seg_nr_digits() == 3) ? "F-F" : "FF"
|
||||
(led_7seg_nr_digits() == 3) ? "F"sep_ch"F" : "FF"
|
||||
#endif
|
||||
);
|
||||
#undef sep_ch
|
||||
break;
|
||||
|
||||
case DT_LCD_OLED:
|
||||
lcd_clear();
|
||||
display_mode = DM_banner; /* double height row 0 */
|
||||
#if MCU == STM32F105
|
||||
lcd_write(0, 0, 0, "FlashFloppy");
|
||||
lcd_write(0, 1, 0, "v");
|
||||
lcd_write(1, 1, 0, fw_ver);
|
||||
#if defined(LOGFILE)
|
||||
lcd_write(10, 1, 0, "[Log]");
|
||||
#elif defined(QUICKDISK)
|
||||
lcd_write(10, 1, 0, "[QD]");
|
||||
#elif MCU == AT32F435
|
||||
lcd_write(0, 0, 0, "FlashFloppy+");
|
||||
#endif
|
||||
snprintf(msg[0], sizeof(msg[0]), "%s%s", fw_ver,
|
||||
#if defined(LOGFILE)
|
||||
" Log"
|
||||
#elif defined(QUICKDISK)
|
||||
" QD"
|
||||
#else
|
||||
""
|
||||
#endif
|
||||
);
|
||||
snprintf(msg[1], sizeof(msg[1]), "%9s %dkB", msg[0], ram_kb);
|
||||
lcd_write(0, 1, 0, msg[1]);
|
||||
lcd_on();
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2989,6 +3106,21 @@ static void maybe_show_version(void)
|
|||
if (nb)
|
||||
return;
|
||||
|
||||
led_7seg_write_string("CPU");
|
||||
delay_ms(1000);
|
||||
|
||||
led_7seg_write_decimal(SYSCLK_MHZ);
|
||||
delay_ms(1000);
|
||||
|
||||
led_7seg_write_string("RAN");
|
||||
delay_ms(1000);
|
||||
|
||||
led_7seg_write_decimal(ram_kb);
|
||||
delay_ms(1000);
|
||||
|
||||
led_7seg_write_string("UER");
|
||||
delay_ms(1000);
|
||||
|
||||
/* Iterate through the dotted sections of the version number. */
|
||||
for (p = fw_ver; p != NULL; p = np ? np+1 : NULL) {
|
||||
np = strchr(p, '.');
|
||||
|
|
@ -3058,7 +3190,7 @@ int main(void)
|
|||
FRESULT fres;
|
||||
|
||||
/* Relocate DATA. Initialise BSS. */
|
||||
if (_sdat != _ldat)
|
||||
if (&_sdat[0] != &_ldat[0])
|
||||
memcpy(_sdat, _ldat, _edat-_sdat);
|
||||
memset(_sbss, 0, _ebss-_sbss);
|
||||
|
||||
|
|
@ -3066,13 +3198,13 @@ int main(void)
|
|||
stm32_init();
|
||||
time_init();
|
||||
console_init();
|
||||
console_crash_on_input();
|
||||
board_init();
|
||||
console_crash_on_input();
|
||||
delay_ms(200); /* 5v settle */
|
||||
|
||||
printk("\n** FlashFloppy v%s for Gotek\n", fw_ver);
|
||||
printk("\n** FlashFloppy %s\n", fw_ver);
|
||||
printk("** Keir Fraser <keir.xen@gmail.com>\n");
|
||||
printk("** https://github.com/keirf/FlashFloppy\n\n");
|
||||
printk("** github:keirf/flashfloppy\n\n");
|
||||
|
||||
printk("Build: %s %s\n", build_date, build_time);
|
||||
printk("Board: %s\n", board_name[board_id]);
|
||||
|
|
@ -3101,7 +3233,7 @@ int main(void)
|
|||
|
||||
usbh_msc_init();
|
||||
|
||||
rotary = (gpioc->idr >> 10) & 3;
|
||||
rotary = board_get_rotary();
|
||||
set_rotary_exti();
|
||||
timer_init(&button_timer, button_timer_fn, NULL);
|
||||
timer_set(&button_timer, time_now());
|
||||
|
|
|
|||
140
src/mcu_at32f435.c
Normal file
140
src/mcu_at32f435.c
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* mcu_at342f435.c
|
||||
*
|
||||
* Core and peripheral registers.
|
||||
*
|
||||
* Written & released by Keir Fraser <keir.xen@gmail.com>
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
bool_t is_artery_mcu = TRUE;
|
||||
unsigned int flash_page_size = FLASH_PAGE_SIZE;
|
||||
unsigned int ram_kb = 384;
|
||||
|
||||
static void clock_init(void)
|
||||
{
|
||||
/* Enable PWR interface so we can set the LDO boost. */
|
||||
rcc->apb1enr |= RCC_APB1ENR_PWREN;
|
||||
|
||||
/* Bootloader leaves MISC1 set up for USB clocked from HICK.
|
||||
* Clear MISC1 register to its reset value. */
|
||||
rcc->misc1 = 0;
|
||||
|
||||
/* 288MHz requires LDO voltage boost. */
|
||||
pwr->ldoov = PWR_LDOOV_1V3;
|
||||
|
||||
flash->divr = FLASH_DIVR_DIV_3;
|
||||
|
||||
/* Start up the external oscillator. */
|
||||
rcc->cr |= RCC_CR_HSEON;
|
||||
while (!(rcc->cr & RCC_CR_HSERDY))
|
||||
cpu_relax();
|
||||
|
||||
/* Enable auto-step. */
|
||||
rcc->misc2 |= RCC_MISC2_AUTOSTEP;
|
||||
|
||||
/* Configure PLL for 8MHz input, 288MHz output. */
|
||||
rcc->pllcfgr = (RCC_PLLCFGR_PLLSRC_HSE | /* PLLSrc = HSE = 8MHz */
|
||||
RCC_PLLCFGR_PLL_MS(1) | /* PLL In = HSE/1 = 8MHz */
|
||||
RCC_PLLCFGR_PLL_NS(72) | /* PLLVCO = 8MHz*72 = 576MHz */
|
||||
RCC_PLLCFGR_PLL_FR(PLL_FR_2)); /* PLL Out = 576MHz/2 */
|
||||
|
||||
/* Bus divisors. */
|
||||
rcc->cfgr = (RCC_CFGR_PPRE2(4) | /* APB2 = 288MHz/2 = 144MHz */
|
||||
RCC_CFGR_PPRE1(4) | /* APB1 = 288MHz/2 = 144MHz */
|
||||
RCC_CFGR_HPRE(0)); /* AHB = 288MHz/1 = 288MHz */
|
||||
|
||||
/* Enable and stabilise the PLL. */
|
||||
rcc->cr |= RCC_CR_PLLON;
|
||||
while (!(rcc->cr & RCC_CR_PLLRDY))
|
||||
cpu_relax();
|
||||
|
||||
/* Switch to the externally-driven PLL for system clock. */
|
||||
rcc->cfgr |= RCC_CFGR_SW(2);
|
||||
while ((rcc->cfgr & RCC_CFGR_SWS(3)) != RCC_CFGR_SWS(2))
|
||||
cpu_relax();
|
||||
|
||||
/* Internal oscillator no longer needed. */
|
||||
rcc->cr &= ~RCC_CR_HSION;
|
||||
|
||||
/* Disable auto-step. */
|
||||
rcc->misc2 &= ~RCC_MISC2_AUTOSTEP;
|
||||
}
|
||||
|
||||
static void peripheral_init(void)
|
||||
{
|
||||
/* Enable basic GPIO clocks, DTCM RAM, DMA, and EXTICR. */
|
||||
rcc->ahb1enr |= (RCC_AHB1ENR_DMA1EN |
|
||||
RCC_AHB1ENR_GPIOHEN |
|
||||
RCC_AHB1ENR_GPIOCEN |
|
||||
RCC_AHB1ENR_GPIOBEN |
|
||||
RCC_AHB1ENR_GPIOAEN);
|
||||
rcc->apb1enr |= (RCC_APB1ENR_TIM2EN |
|
||||
RCC_APB1ENR_TIM3EN |
|
||||
RCC_APB1ENR_TIM4EN |
|
||||
RCC_APB1ENR_TIM5EN);
|
||||
rcc->apb2enr |= (RCC_APB2ENR_SYSCFGEN |
|
||||
RCC_APB2ENR_TIM1EN);
|
||||
|
||||
/* Flexible DMA request mappings. */
|
||||
dmamux1->sel = DMAMUX_SEL_TBL_SEL;
|
||||
dmamux2->sel = DMAMUX_SEL_TBL_SEL;
|
||||
|
||||
/* Release JTAG pins. */
|
||||
gpio_configure_pin(gpioa, 15, GPI_floating);
|
||||
gpio_configure_pin(gpiob, 3, GPI_floating);
|
||||
gpio_configure_pin(gpiob, 4, GPI_floating);
|
||||
}
|
||||
|
||||
void stm32_init(void)
|
||||
{
|
||||
cortex_init();
|
||||
clock_init();
|
||||
peripheral_init();
|
||||
cpu_sync();
|
||||
}
|
||||
|
||||
void gpio_configure_pin(GPIO gpio, unsigned int pin, unsigned int mode)
|
||||
{
|
||||
gpio_write_pin(gpio, pin, mode >> 7);
|
||||
gpio->moder = (gpio->moder & ~(3<<(pin<<1))) | ((mode&3)<<(pin<<1));
|
||||
mode >>= 2;
|
||||
gpio->otyper = (gpio->otyper & ~(1<<pin)) | ((mode&1)<<pin);
|
||||
mode >>= 1;
|
||||
gpio->odrvr = (gpio->odrvr & ~(3<<(pin<<1))) | ((mode&3)<<(pin<<1));
|
||||
mode >>= 2;
|
||||
gpio->pupdr = (gpio->pupdr & ~(3<<(pin<<1))) | ((mode&3)<<(pin<<1));
|
||||
}
|
||||
|
||||
void gpio_set_af(GPIO gpio, unsigned int pin, unsigned int af)
|
||||
{
|
||||
if (pin < 8) {
|
||||
gpio->afrl = (gpio->afrl & ~(15<<(pin<<2))) | (af<<(pin<<2));
|
||||
} else {
|
||||
pin -= 8;
|
||||
gpio->afrh = (gpio->afrh & ~(15<<(pin<<2))) | (af<<(pin<<2));
|
||||
}
|
||||
}
|
||||
|
||||
void _exti_route(unsigned int px, unsigned int pin)
|
||||
{
|
||||
unsigned int n = pin >> 2;
|
||||
unsigned int s = (pin & 3) << 2;
|
||||
uint32_t exticr = syscfg->exticr[n];
|
||||
ASSERT(!in_exception()); /* no races please */
|
||||
exticr &= ~(0xf << s);
|
||||
exticr |= px << s;
|
||||
syscfg->exticr[n] = exticr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "Linux"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
161
src/mcu_stm32f105.c
Normal file
161
src/mcu_stm32f105.c
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* mcu_stm32f105.c
|
||||
*
|
||||
* Core and peripheral registers.
|
||||
*
|
||||
* Written & released by Keir Fraser <keir.xen@gmail.com>
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
unsigned int sysclk_mhz = 72;
|
||||
unsigned int apb1_mhz = 36;
|
||||
|
||||
bool_t is_artery_mcu;
|
||||
unsigned int flash_page_size = FLASH_PAGE_SIZE;
|
||||
unsigned int ram_kb = 64;
|
||||
|
||||
static void identify_mcu(void)
|
||||
{
|
||||
/* DBGMCU_IDCODE (E0042000):
|
||||
* STM32F105RB: 10016418 (device id: 418)
|
||||
* AT32F415CBT7: 700301c5 (device id: 1c5)
|
||||
* AT32F415RCT7: 70030240 (device id: 240)
|
||||
* However the AT32 IDCODE values are undocumented so we cannot rely
|
||||
* on them (for example, what will be the ID for chips with differing
|
||||
* amounts of Flash, or numbers of pins?) */
|
||||
|
||||
/* We detect an Artery MCU by presence of Cortex-M4 CPUID.
|
||||
* Cortex-M4: 41xfc24x ; Cortex-M3: 41xfc23x */
|
||||
is_artery_mcu = ((scb->cpuid >> 4) & 0xf) == 4;
|
||||
|
||||
if (is_artery_mcu) {
|
||||
unsigned int flash_kb = *(uint16_t *)0x1ffff7e0;
|
||||
ram_kb = 32;
|
||||
if (flash_kb == 128)
|
||||
flash_page_size = 1024;
|
||||
sysclk_mhz = 144;
|
||||
apb1_mhz = 72;
|
||||
}
|
||||
}
|
||||
|
||||
static void clock_init(void)
|
||||
{
|
||||
/* Flash controller: reads require 2 wait states at 72MHz. */
|
||||
flash->acr = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY(sysclk_mhz/32);
|
||||
|
||||
/* Start up the external oscillator. */
|
||||
rcc->cr |= RCC_CR_HSEON;
|
||||
while (!(rcc->cr & RCC_CR_HSERDY))
|
||||
cpu_relax();
|
||||
|
||||
/* PLLs, scalers, muxes. */
|
||||
if (is_artery_mcu) {
|
||||
uint32_t rcc_pll = *RCC_PLL;
|
||||
rcc_pll &= ~(RCC_PLL_PLLCFGEN | RCC_PLL_FREF_MASK);
|
||||
rcc_pll |= RCC_PLL_FREF_8M;
|
||||
*RCC_PLL = rcc_pll;
|
||||
rcc->cfgr = (RCC_CFGR_PLLMUL_18 | /* PLL = 18*8MHz = 144MHz */
|
||||
RCC_CFGR_USBPSC_3 | /* USB = SYSCLK/3 = 48MHz */
|
||||
RCC_CFGR_PLLSRC_PREDIV1 |
|
||||
RCC_CFGR_ADCPRE_DIV8 |
|
||||
RCC_CFGR_APB2PSC_2 | /* APB2 = SYSCLK/2 = 72MHz */
|
||||
RCC_CFGR_APB1PSC_2); /* APB1 = SYSCLK/2 = 72MHz */
|
||||
} else {
|
||||
rcc->cfgr = (RCC_CFGR_PLLMUL(9) | /* PLL = 9*8MHz = 72MHz */
|
||||
RCC_CFGR_PLLSRC_PREDIV1 |
|
||||
RCC_CFGR_ADCPRE_DIV8 |
|
||||
RCC_CFGR_APB1PSC_2); /* APB1 = SYSCLK/2 = 36MHz */
|
||||
}
|
||||
|
||||
/* Enable and stabilise the PLL. */
|
||||
rcc->cr |= RCC_CR_PLLON;
|
||||
while (!(rcc->cr & RCC_CR_PLLRDY))
|
||||
cpu_relax();
|
||||
|
||||
if (is_artery_mcu)
|
||||
*RCC_MISC2 |= RCC_MISC2_AUTOSTEP_EN;
|
||||
|
||||
/* Switch to the externally-driven PLL for system clock. */
|
||||
rcc->cfgr |= RCC_CFGR_SW_PLL;
|
||||
while ((rcc->cfgr & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_PLL)
|
||||
cpu_relax();
|
||||
|
||||
if (is_artery_mcu)
|
||||
*RCC_MISC2 &= ~RCC_MISC2_AUTOSTEP_EN;
|
||||
|
||||
/* Internal oscillator no longer needed. */
|
||||
rcc->cr &= ~RCC_CR_HSION;
|
||||
}
|
||||
|
||||
static void gpio_init(GPIO gpio)
|
||||
{
|
||||
/* Floating Input. Reference Manual states that JTAG pins are in PU/PD
|
||||
* mode at reset, so ensure all PU/PD are disabled. */
|
||||
gpio->crl = gpio->crh = 0x44444444u;
|
||||
}
|
||||
|
||||
static void peripheral_init(void)
|
||||
{
|
||||
/* Enable basic GPIO and AFIO clocks, all timers, and DMA. */
|
||||
rcc->apb1enr = (RCC_APB1ENR_TIM2EN |
|
||||
RCC_APB1ENR_TIM3EN |
|
||||
RCC_APB1ENR_TIM4EN);
|
||||
rcc->apb2enr = (RCC_APB2ENR_IOPAEN |
|
||||
RCC_APB2ENR_IOPBEN |
|
||||
RCC_APB2ENR_IOPCEN |
|
||||
RCC_APB2ENR_AFIOEN |
|
||||
RCC_APB2ENR_TIM1EN);
|
||||
rcc->ahbenr = RCC_AHBENR_DMA1EN;
|
||||
|
||||
/* Turn off serial-wire JTAG and reclaim the GPIOs. */
|
||||
afio->mapr = AFIO_MAPR_SWJ_CFG_DISABLED;
|
||||
|
||||
/* All pins in a stable state. */
|
||||
gpio_init(gpioa);
|
||||
gpio_init(gpiob);
|
||||
gpio_init(gpioc);
|
||||
}
|
||||
|
||||
void stm32_init(void)
|
||||
{
|
||||
cortex_init();
|
||||
identify_mcu();
|
||||
clock_init();
|
||||
peripheral_init();
|
||||
cpu_sync();
|
||||
}
|
||||
|
||||
void gpio_configure_pin(GPIO gpio, unsigned int pin, unsigned int mode)
|
||||
{
|
||||
gpio_write_pin(gpio, pin, mode >> 4);
|
||||
mode &= 0xfu;
|
||||
if (pin >= 8) {
|
||||
pin -= 8;
|
||||
gpio->crh = (gpio->crh & ~(0xfu<<(pin<<2))) | (mode<<(pin<<2));
|
||||
} else {
|
||||
gpio->crl = (gpio->crl & ~(0xfu<<(pin<<2))) | (mode<<(pin<<2));
|
||||
}
|
||||
}
|
||||
|
||||
void _exti_route(unsigned int px, unsigned int pin)
|
||||
{
|
||||
unsigned int n = pin >> 2;
|
||||
unsigned int s = (pin & 3) << 2;
|
||||
uint32_t exticr = afio->exticr[n];
|
||||
ASSERT(!in_exception()); /* no races please */
|
||||
exticr &= ~(0xf << s);
|
||||
exticr |= px << s;
|
||||
afio->exticr[n] = exticr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "Linux"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
#define GPI_bus GPI_floating
|
||||
#define GPO_bus GPO_pushpull(_2MHz, HIGH)
|
||||
#define GPO_rdata GPO_pushpull(_2MHz, LOW)
|
||||
#define AFO_rdata (AFO_pushpull(_2MHz) | (LOW<<4))
|
||||
#define AFO_rdata _AFO_pushpull(_2MHz,LOW)
|
||||
|
||||
/* READY-window state machine and timer handling. */
|
||||
static struct window {
|
||||
|
|
@ -51,7 +51,11 @@ static volatile struct {
|
|||
} pins;
|
||||
#define read_pin(pin) pins.pin
|
||||
#define write_pin(pin, level) ({ \
|
||||
gpio_write_pin(gpio_out, pin_##pin, level); \
|
||||
unsigned int __pin = pin_##pin; \
|
||||
if (__pin >= 16) \
|
||||
gpio_write_pin(gpioa, __pin-16, level); \
|
||||
else \
|
||||
gpio_write_pin(gpiob, __pin, level); \
|
||||
pins.pin = level; })
|
||||
|
||||
#include "floppy_generic.c"
|
||||
|
|
@ -96,6 +100,20 @@ void floppy_set_fintf_mode(void)
|
|||
/* Quick Disk interface is static. */
|
||||
}
|
||||
|
||||
void floppy_set_max_cyl(void)
|
||||
{
|
||||
/* Quick Disk has no STEP signal. */
|
||||
}
|
||||
|
||||
static void drive_configure_output_pin(unsigned int pin)
|
||||
{
|
||||
if (pin >= 16) {
|
||||
gpio_configure_pin(gpioa, pin-16, GPO_bus);
|
||||
} else {
|
||||
gpio_configure_pin(gpiob, pin, GPO_bus);
|
||||
}
|
||||
}
|
||||
|
||||
void floppy_init(void)
|
||||
{
|
||||
struct drive *drv = &drive;
|
||||
|
|
@ -103,21 +121,18 @@ void floppy_init(void)
|
|||
floppy_set_fintf_mode();
|
||||
|
||||
printk("Interface: QuickDisk, JC=%s\n",
|
||||
!gpio_read_pin(gpiob, 1) ? "On (Roland)" : "Off");
|
||||
qd_roland_mode() ? "On (Roland)" : "Off");
|
||||
|
||||
board_floppy_init();
|
||||
|
||||
timer_init(&motor.timer, motor_timer, drv);
|
||||
timer_init(&window.timer, window_timer, drv);
|
||||
|
||||
gpio_configure_pin(gpio_out, pin_02, GPO_bus);
|
||||
gpio_configure_pin(gpio_out, pin_08, GPO_bus);
|
||||
gpio_configure_pin(gpio_out, pin_26, GPO_bus);
|
||||
gpio_configure_pin(gpio_out, pin_28, GPO_bus);
|
||||
gpio_configure_pin(gpio_out, pin_34, GPO_bus);
|
||||
|
||||
gpio_configure_pin(gpio_data, pin_wdata, GPI_bus);
|
||||
gpio_configure_pin(gpio_data, pin_rdata, GPO_rdata);
|
||||
drive_configure_output_pin(pin_02);
|
||||
drive_configure_output_pin(pin_08);
|
||||
drive_configure_output_pin(pin_26);
|
||||
drive_configure_output_pin(pin_28);
|
||||
drive_configure_output_pin(pin_34);
|
||||
|
||||
write_pin(media, HIGH);
|
||||
write_pin(wrprot, HIGH);
|
||||
|
|
@ -128,12 +143,20 @@ void floppy_init(void)
|
|||
timer_init(&index.timer, index_assert, NULL);
|
||||
}
|
||||
|
||||
static void io_thread_main(void *arg) {
|
||||
while (1) {
|
||||
F_async_drain();
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
|
||||
void floppy_insert(unsigned int unit, struct slot *slot)
|
||||
{
|
||||
floppy_mount(slot);
|
||||
|
||||
timer_dma_init();
|
||||
tim_rdata->ccr2 = sysclk_ns(1500); /* RD: 1.5us positive pulses */
|
||||
thread_start(&drive.io_thread, _thread1_stacktop, io_thread_main, NULL);
|
||||
tim_rdata->ccr2 = sampleclk_ns(1500); /* RD: 1.5us positive pulses */
|
||||
|
||||
/* Drive is ready. Set output signals appropriately. */
|
||||
write_pin(media, LOW);
|
||||
|
|
@ -233,9 +256,8 @@ static bool_t dma_rd_handle(struct drive *drv)
|
|||
case DMA_inactive: {
|
||||
time_t read_start_pos = window.paused ? window.pause_pos : 0;
|
||||
read_start_pos %= drv->image->stk_per_rev;
|
||||
read_start_pos *= SYSCLK_MHZ/STK_MHZ;
|
||||
if (image_setup_track(drv->image, 0, &read_start_pos))
|
||||
return TRUE;
|
||||
read_start_pos *= SAMPLECLK_MHZ/STK_MHZ;
|
||||
image_setup_track(drv->image, 0, &read_start_pos);
|
||||
/* Change state /then/ check for race against step or side change. */
|
||||
dma_rd->state = DMA_starting;
|
||||
barrier();
|
||||
|
|
|
|||
69
src/sd_spi.c
69
src/sd_spi.c
|
|
@ -9,16 +9,10 @@
|
|||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
#if 1
|
||||
/* We can now switch to Default Speed (25MHz). Closest we can get is 36Mhz/2 =
|
||||
* 18MHz. */
|
||||
#define DEFAULT_SPEED_DIV SPI_CR1_BR_DIV2 /* 18MHz */
|
||||
#define PCLK_MHZ APB1_MHZ
|
||||
#define INIT_SPEED_KHZ 400
|
||||
#define DEFAULT_SPEED_KHZ 25000
|
||||
#define SPI_PIN_SPEED _50MHz
|
||||
#else
|
||||
/* Best speed I can reliably achieve right now is 9Mbit/s. */
|
||||
#define DEFAULT_SPEED_DIV SPI_CR1_BR_DIV4 /* 9MHz */
|
||||
#define SPI_PIN_SPEED _10MHz
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define TRC(f, a...) printk("SD: " f, ## a)
|
||||
|
|
@ -66,13 +60,15 @@ static void spi_release(void)
|
|||
|
||||
static uint8_t wait_ready(void)
|
||||
{
|
||||
stk_time_t start = stk_now();
|
||||
uint8_t res;
|
||||
time_t start = time_now();
|
||||
uint8_t res = 0xff;
|
||||
|
||||
/* Wait 500ms for card to be ready. */
|
||||
do {
|
||||
if (res != 0xff)
|
||||
thread_yield();
|
||||
res = spi_recv8(spi);
|
||||
} while ((res != 0xff) && (stk_timesince(start) < stk_ms(500)));
|
||||
} while ((res != 0xff) && (time_since(start) < time_ms(500)));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
@ -171,14 +167,16 @@ static uint8_t send_cmd(uint8_t cmd, uint32_t arg)
|
|||
|
||||
static bool_t datablock_recv(BYTE *buff, uint16_t bytes)
|
||||
{
|
||||
uint8_t token, _crc[2];
|
||||
uint32_t start = stk_now();
|
||||
uint8_t token = 0, _crc[2];
|
||||
uint16_t todo, w, crc;
|
||||
time_t start = time_now();
|
||||
|
||||
/* Wait 100ms for data to be ready. */
|
||||
do {
|
||||
if (token == 0xff)
|
||||
thread_yield();
|
||||
token = spi_recv8(spi);
|
||||
} while ((token == 0xff) && (stk_timesince(start) < stk_ms(100)));
|
||||
} while ((token == 0xff) && (time_since(start) < time_ms(100)));
|
||||
if (token != 0xfe) /* valid data token? */
|
||||
return FALSE;
|
||||
|
||||
|
|
@ -270,9 +268,26 @@ static bool_t sd_inserted(void)
|
|||
return gpio_read_pin(gpioc, 9);
|
||||
}
|
||||
|
||||
static void sd_spi_set_cr(uint32_t cr1, unsigned int max_khz)
|
||||
{
|
||||
/* Set the divisor to satisfy maximum communications frequency. */
|
||||
uint32_t br = SPI_BR_DIV2;
|
||||
unsigned int khz = (PCLK_MHZ * 1000) >> 1;
|
||||
while (khz > max_khz) {
|
||||
khz >>= 1;
|
||||
br++;
|
||||
}
|
||||
|
||||
/* BR is called MDIV in AT32F435 documentation, and is extended by one
|
||||
* bit which resides in CR2. */
|
||||
spi->cr2 = (br & 8) << (8 - 3); /* CR2[8] := MDIV[3] */
|
||||
spi->cr1 = cr1 | ((br & 7) << 3); /* CR1[5:4] := MDIV[2:0] */
|
||||
}
|
||||
|
||||
static DSTATUS sd_disk_initialize(BYTE pdrv)
|
||||
{
|
||||
uint32_t start, cr1;
|
||||
time_t start;
|
||||
uint32_t cr1;
|
||||
uint16_t rcv;
|
||||
uint8_t i;
|
||||
|
||||
|
|
@ -288,16 +303,24 @@ static DSTATUS sd_disk_initialize(BYTE pdrv)
|
|||
|
||||
/* Enable external I/O pins. */
|
||||
gpio_configure_pin(gpiob, PIN_CS, GPO_pushpull(SPI_PIN_SPEED, HIGH));
|
||||
#if MCU == STM32F105
|
||||
gpio_configure_pin(gpiob, 13, AFO_pushpull(SPI_PIN_SPEED)); /* CK */
|
||||
gpio_configure_pin(gpiob, 14, GPI_pull_up); /* MISO */
|
||||
gpio_configure_pin(gpiob, 15, AFO_pushpull(SPI_PIN_SPEED)); /* MOSI */
|
||||
#elif MCU == AT32F435
|
||||
gpio_set_af(gpiob, 13, 5);
|
||||
gpio_configure_pin(gpiob, 13, AFO_pushpull(SPI_PIN_SPEED)); /* CK */
|
||||
gpio_set_af(gpiob, 14, 5);
|
||||
gpio_configure_pin(gpiob, 14, AFI(PUPD_up)); /* MISO */
|
||||
gpio_set_af(gpiob, 15, 5);
|
||||
gpio_configure_pin(gpiob, 15, AFO_pushpull(SPI_PIN_SPEED)); /* MOSI */
|
||||
#endif
|
||||
|
||||
/* Configure SPI: 8-bit mode, MSB first, CPOL Low, CPHA Leading Edge. */
|
||||
spi->cr2 = 0;
|
||||
cr1 = (SPI_CR1_MSTR | /* master */
|
||||
SPI_CR1_SSM | SPI_CR1_SSI | /* software NSS */
|
||||
SPI_CR1_SPE);
|
||||
spi->cr1 = cr1 | SPI_CR1_BR_DIV128; /* ~281kHz (<400kHz) */
|
||||
sd_spi_set_cr(cr1, INIT_SPEED_KHZ);
|
||||
|
||||
/* Drain SPI I/O. */
|
||||
spi_quiesce(spi);
|
||||
|
|
@ -328,9 +351,9 @@ static DSTATUS sd_disk_initialize(BYTE pdrv)
|
|||
}
|
||||
|
||||
/* Request SDHC/SDXC and start card initialisation. */
|
||||
start = stk_now();
|
||||
start = time_now();
|
||||
while (send_cmd(ACMD(41), 1u<<30)) {
|
||||
if (stk_timesince(start) >= stk_ms(1000))
|
||||
if (time_since(start) >= time_ms(1000))
|
||||
goto out; /* initialisation timeout */
|
||||
}
|
||||
|
||||
|
|
@ -359,9 +382,9 @@ static DSTATUS sd_disk_initialize(BYTE pdrv)
|
|||
}
|
||||
|
||||
/* Wait for card initialisation. */
|
||||
start = stk_now();
|
||||
start = time_now();
|
||||
while (send_cmd(cmd, 0)) {
|
||||
if (stk_timesince(start) >= stk_ms(1000))
|
||||
if (time_since(start) >= time_ms(1000))
|
||||
goto out; /* initialisation timeout */
|
||||
}
|
||||
|
||||
|
|
@ -379,7 +402,7 @@ out:
|
|||
|
||||
if (!(status & STA_NOINIT)) {
|
||||
delay_us(10); /* XXX small delay here stops SPI getting stuck?? */
|
||||
spi->cr1 = cr1 | DEFAULT_SPEED_DIV;
|
||||
sd_spi_set_cr(cr1, DEFAULT_SPEED_KHZ);
|
||||
printk("SD Card configured\n");
|
||||
dump_cid_info();
|
||||
} else {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue