Compare commits
41 commits
memory-tes
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0351c481c0 | ||
|
|
ca7144b454 | ||
|
|
e829d827da | ||
|
|
c6310b803b | ||
|
|
3946e7f7a6 | ||
|
|
18ecd15be9 | ||
|
|
ae4245018d | ||
|
|
cbbac4e3c2 | ||
|
|
69e2c545a3 | ||
|
|
959fd9a4a0 | ||
|
|
da6188ae7e | ||
|
|
9ac66c313e | ||
|
|
25742f6ecf | ||
|
|
0c423fb9de | ||
|
|
548c791cc2 | ||
|
|
11a2ea9e2c | ||
|
|
e2f010c648 | ||
|
|
b34cb95543 | ||
|
|
cc67ce5903 | ||
|
|
dea7a6ae4d | ||
|
|
78f93ea431 | ||
|
|
9308d30b2e | ||
|
|
5e3e3a227f | ||
|
|
fb3c7c03d8 | ||
|
|
5067e622ad | ||
|
|
7fd32fe437 | ||
|
|
fb58d39502 | ||
|
|
8a150d8750 | ||
|
|
47dbbdc1ec | ||
|
|
2935de98de | ||
|
|
fb49e98256 | ||
|
|
9521a79ad9 | ||
|
|
82aaae8bf5 | ||
|
|
484af134e6 | ||
|
|
faf9137bdb | ||
|
|
38aa3c9000 | ||
|
|
007de2c8d6 | ||
|
|
410a7203cc | ||
|
|
3e219840d9 | ||
|
|
38cf13cf23 | ||
|
|
c885f2cb49 |
27 changed files with 629 additions and 319 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
|
@ -2,10 +2,10 @@ name: CI
|
|||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set environment variables
|
||||
id: vars
|
||||
|
|
@ -43,7 +43,7 @@ jobs:
|
|||
mv flashfloppy-$VER.zip _cidist/
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: FlashFloppy.CI.${{ steps.vars.outputs.sha_short }}
|
||||
path: _cidist
|
||||
|
|
|
|||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
|
|
@ -7,10 +7,10 @@ name: Release
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set environment variables
|
||||
id: vars
|
||||
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
tag: ${{ github.ref }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: FlashFloppy ${{ steps.vars.outputs.ver }}
|
||||
body: "[**Release Notes:**](https://github.com/keirf/flashfloppy/blob/stable-v3/RELEASE_NOTES)"
|
||||
body: "[**Release Notes:**](https://github.com/keirf/flashfloppy/blob/master/RELEASE_NOTES)"
|
||||
draft: false
|
||||
prerelease: false
|
||||
artifacts: flashfloppy-${{ steps.vars.outputs.ver }}.zip
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -115,6 +115,8 @@ dist: FORCE all
|
|||
cp -a README $(t)/
|
||||
cp -a RELEASE_NOTES $(t)/
|
||||
cp -a examples $(t)/
|
||||
# Clive Drive is particularly fussy about QD timings.
|
||||
$(PYTHON) scripts/mk_qd.py --window=6.4 --total=7.5 --round $(t)/examples/Host/Sinclair_ZX_Spectrum/Clive_Drive/CliveDrive_Blank.qd
|
||||
[ -e ext/HxC_Compat_Mode-$(HXC_FF_VER).zip ] || \
|
||||
(mkdir -p ext ; cd ext ; wget -q --show-progress $(HXC_FF_URL)/$(HXC_FF_VER)/HxC_Compat_Mode-$(HXC_FF_VER).zip ; rm -rf index.html)
|
||||
(cd $(t) && unzip -q ../../ext/HxC_Compat_Mode-$(HXC_FF_VER).zip)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,23 @@
|
|||
** Keir Fraser <keir.xen@gmail.com>
|
||||
************************************
|
||||
|
||||
** v3.42 - 11 January 2024
|
||||
- HFEv3: Various read/write improvements
|
||||
- WDATA: Merge short write pulses, and apply de-jitter/precomp
|
||||
- IMG, EDSK: Stream large sector writes to flash
|
||||
|
||||
** v3.41 - 14 July 2023
|
||||
- AT32F415: Fix timer handling since clock speed increase (v3.39).
|
||||
- LCD: Faster power-on initialisation. Don't wait for display to clear.
|
||||
|
||||
** v3.40 - 27 June 2023
|
||||
- XDF: Fix writes to side 1 of XDF disk images
|
||||
- HFE: Better handling of long No Flux Areas
|
||||
- QuickDisk: Reduce motor spinup time to 1 second (previously 2 seconds)
|
||||
- LCD: Completely blank display when backlight is off
|
||||
|
||||
** v3.39 - 1 March 2023
|
||||
- AT32F415: Run this MCU at 144Hz (previously 72MHz).
|
||||
- AT32F415: Run this MCU at 144MHz (previously 72MHz).
|
||||
- HFE: Fix HFEv3 support. Support Dungeon Master & Chaos Strikes Back.
|
||||
- IMG.CFG: Support mixed sector sizes per track (Ensoniq Mirage etc).
|
||||
- IMG.CFG: New option img_bps= allows padding of short sectors in IMG files.
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@
|
|||
# jc: Specified by jumper JC (open: shugart, closed: ibmpc)
|
||||
# shugart: P2=DSKCHG, P34=RDY (Amiga, Atari ST, many others)
|
||||
# ibmpc: P2=unused, P34=DSKCHG (IBM PC interface)
|
||||
# ibmpc-hdout: P2=HD_OUT, P34=DSKCHG (not generally needed: prefer 'ibmpc')
|
||||
# ibmpc-hdout: P2=HD_OUT, P34=DSKCHG (not generally needed: prefer ibmpc)
|
||||
# jppc: P2=unused, P34=RDY (Japanese PC standard)
|
||||
# jppc-hdout: P2=HD_OUT, P34=RDY (Japanese PC alternate: prefer 'jppc')
|
||||
# akai-s950: Legacy alias of 'jppc-hdout', previously used for Akai S950
|
||||
# amiga: P2=DSKCHG, P34=DRIVE_ID (not generally needed: prefer 'shugart')
|
||||
# jppc-hdout: P2=HD_OUT, P34=RDY (Japanese PC alternate: prefer jppc)
|
||||
# akai-s950: Legacy alias of jppc-hdout, previously used for Akai S950
|
||||
# amiga: P2=DSKCHG, P34=DRIVE_ID (not generally needed: prefer shugart)
|
||||
interface = jc
|
||||
|
||||
# Host platform: Improves image-format detection for generic types such as IMG
|
||||
|
|
@ -172,9 +172,9 @@ twobutton-action = zero
|
|||
# Input sensor type at the rotary-encoder inputs (pins PC10 and PC11):
|
||||
# [full | half | quarter]:
|
||||
# Rotary encoder, identified by fraction of a Gray-code cycle performed
|
||||
# per detent/click. If default value ('full') requires multiple
|
||||
# clicks/detents to move position then change to 'half' (if 2 clicks
|
||||
# per move) or 'quarter' (if 4 clicks).
|
||||
# per detent/click. If default value (full) requires multiple
|
||||
# clicks/detents to move position then change to half (if 2 clicks
|
||||
# per move) or quarter (if 4 clicks).
|
||||
# [trackball]:
|
||||
# Blackberry-style trackball (eg. using Hall-effect sensors).
|
||||
# [buttons]:
|
||||
|
|
@ -220,15 +220,15 @@ oled-font = 6x13
|
|||
oled-contrast = 143
|
||||
|
||||
# Text height and arrangement on LCD/OLED and on OSD, respectively.
|
||||
# 'default', or a comma-separated list (one entry per LCD/OLED row, top down).
|
||||
# Each list item is a digit plus optional height specifier: <content-row>[d]
|
||||
# content-row: '0-3' = specified content row, '7' = blank
|
||||
# Comma-separated list, one entry per display row, top down.
|
||||
# Each list item is a digit plus optional height specifier: [0-7][d]
|
||||
# content-row: 0-3 = specified content, 7 = blank
|
||||
# 0: Current image name
|
||||
# 1: Status
|
||||
# 2: Image/Volume info
|
||||
# 3: Current subfolder name
|
||||
# height specifier: 'd' = double height (32px, OLED only; ignored for LCD)
|
||||
# 'default' depends on display, eg.: oled-128x32='0,1' ; oled-128x64='3,0d,1'
|
||||
# height-specifier: d = double height (32px, OLED only; ignored for LCD)
|
||||
# Default depends on display, eg.: oled-128x32 -> 0,1 ; oled-128x64 -> 3,0d,1
|
||||
# Values: [0-7][d] | default
|
||||
display-order = default
|
||||
osd-display-order = default
|
||||
|
|
|
|||
17
examples/Host/GRiD/IMG.CFG
Normal file
17
examples/Host/GRiD/IMG.CFG
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
## IMG.CFG for machines produced by GRiD Systems Corp.
|
||||
|
||||
# NOTE: The tags match on filesize alone. If you wish to define an explicit
|
||||
# tagname match, you can for example add 'flex' to the square-bracketed tags
|
||||
# to limit matches to filenames of the form *.flex.{img,ima,dsk}
|
||||
|
||||
# 360k 40-cylinder DS/DD format used by GRiD Compass
|
||||
[::368640]
|
||||
cyls = 40
|
||||
heads = 2
|
||||
bps = 512
|
||||
secs = 9
|
||||
mode = mfm
|
||||
interleave = 5
|
||||
id = 1
|
||||
tracks = 0-39.1
|
||||
id = 10
|
||||
|
|
@ -38,6 +38,17 @@ tracks = 0-79 # This line can be adjusted
|
|||
hskew = 1
|
||||
cskew = 2
|
||||
|
||||
# Matches 720kB images named *.mc.{img,ima,dsk}.
|
||||
# This higher level of interleave is found on all disks for the
|
||||
# Roland MC-300, MC-500 and MC-50.
|
||||
[mc::737280]
|
||||
cyls = 80
|
||||
heads = 2
|
||||
secs = 9
|
||||
bps = 512
|
||||
tracks = 0-79
|
||||
interleave = 4
|
||||
|
||||
# Roland 1.44MB format may apply sector skew.
|
||||
# This is as seen on a Roland MT-200.
|
||||
[::1474560]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
The Clive Drive controller has a 16-pin drive interface. Pins 3-12
|
||||
inclusive correspond to the usual 10-pin QD interface. For reference,
|
||||
see the Clive Drive schematic at:
|
||||
https://speccy4ever.speccy.org/rom/clive/Clive%20Drive.pdf
|
||||
|
||||
Clive Controller QDD Gotek
|
||||
|
||||
1---------NC
|
||||
2---------NC
|
||||
3---------WRITE PROTECT (WRPR) 1 28
|
||||
4---------WRITE DATA (WRDT) 2 22
|
||||
5---------WRITE GATE 1 (WRGT1) 3 24
|
||||
6---------MOTOR ON 1 (MTON1) 4 16
|
||||
7---------READ DATA (RDDT) 5 30
|
||||
8---------READY 6 34
|
||||
9---------MEDIA SENSE (MDST) 7 2
|
||||
10--------QD RESET (QDDRST) 8 20
|
||||
11--------VCC +5V 9 +5v
|
||||
12--------GND 10 GND
|
||||
13--------MOTOR ON 2 (MTON2) NC NC
|
||||
14--------WRITE GATE 2 (WRGT2) NC NC
|
||||
15--------SEL IN (SELIN) NC NC
|
||||
16--------NC
|
||||
45
examples/Host/Sinclair_ZX_Spectrum/Sandy_FDD2/IMG.CFG
Normal file
45
examples/Host/Sinclair_ZX_Spectrum/Sandy_FDD2/IMG.CFG
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
## IMG.CFG for the Sandy FDD2 interface.
|
||||
|
||||
# Sandy FDD2 is a clone of the FDC-1 by Technology Research Ltd.
|
||||
# Using a 1771 controller chip, it supports single-density (FM) recording
|
||||
# only, unlike the later Beta Disk interfaces.
|
||||
|
||||
# *.ss40.img: Single-sided 40 cylinders.
|
||||
[ss40::102400]
|
||||
cyls = 40
|
||||
heads = 1
|
||||
secs = 10
|
||||
bps = 256
|
||||
mode = fm
|
||||
interleave = 2
|
||||
|
||||
# *.ss80.img: Single-sided 80 cylinders.
|
||||
[ss80::204800]
|
||||
cyls = 80
|
||||
heads = 1
|
||||
secs = 10
|
||||
bps = 256
|
||||
mode = fm
|
||||
interleave = 2
|
||||
|
||||
# *.ds40.img: Double-sided 40 cylinders.
|
||||
[ds40::204800]
|
||||
cyls = 40
|
||||
heads = 2
|
||||
secs = 10
|
||||
bps = 256
|
||||
mode = fm
|
||||
interleave = 2
|
||||
tracks = 0-39.1
|
||||
h = 0
|
||||
|
||||
# *.ds80.img: Double-sided 80 cylinders.
|
||||
[ds80::409600]
|
||||
cyls = 80
|
||||
heads = 2
|
||||
secs = 10
|
||||
bps = 256
|
||||
mode = fm
|
||||
interleave = 2
|
||||
tracks = 0-79.1
|
||||
h = 0
|
||||
|
|
@ -14,6 +14,7 @@ typedef uint32_t time_t;
|
|||
#define TIME_MHZ STK_MHZ
|
||||
#define time_us(x) stk_us(x)
|
||||
#define time_ms(x) stk_ms(x)
|
||||
#define time_stk(x) (x)
|
||||
#define time_sysclk(x) stk_sysclk(x)
|
||||
#define sysclk_time(x) sysclk_stk(x)
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ int snprintf(char *str, size_t size, const char *format, ...)
|
|||
#define htobe16(x) _rev16(x)
|
||||
#define htobe32(x) _rev32(x)
|
||||
|
||||
uint32_t udiv64(uint64_t dividend, uint32_t divisor);
|
||||
|
||||
/* Arena-based memory allocation */
|
||||
void *arena_alloc(uint32_t sz);
|
||||
uint32_t arena_total(void);
|
||||
|
|
@ -89,6 +91,8 @@ static inline int vprintk(const char *format, va_list ap) { return 0; }
|
|||
static inline int printk(const char *format, ...) { return 0; }
|
||||
#endif
|
||||
|
||||
#define log(f, a...) printk("%s: " f, LOG_PREFIX, ## a)
|
||||
|
||||
#if defined(LOGFILE)
|
||||
/* Logfile management */
|
||||
void logfile_flush(FIL *file);
|
||||
|
|
|
|||
|
|
@ -9,41 +9,59 @@
|
|||
|
||||
import sys,struct,argparse
|
||||
|
||||
def round_up(x, y):
|
||||
return (x + y - 1) & ~(y - 1)
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument("--lead-in", type=float, default=0.5,
|
||||
help="lead-in, seconds")
|
||||
parser.add_argument("--window", type=float, default=5.5,
|
||||
help="data window, seconds")
|
||||
parser.add_argument("--total", type=float, default=8.0,
|
||||
help="total length, seconds")
|
||||
parser.add_argument("outfile", help="output filename")
|
||||
args = parser.parse_args(argv[1:])
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument("--lead-in", type=float, default=0.5,
|
||||
help="lead-in, seconds")
|
||||
parser.add_argument("--window", type=float, default=5.5,
|
||||
help="data window, seconds")
|
||||
parser.add_argument("--total", type=float, default=8.0,
|
||||
help="total length, seconds")
|
||||
parser.add_argument("--round", action="store_true",
|
||||
help="round values up to 512-byte block size")
|
||||
parser.add_argument("outfile", help="output filename")
|
||||
args = parser.parse_args(argv[1:])
|
||||
|
||||
bit_ms = 0.004916
|
||||
total_bytes = int(args.total * 1000.0 / bit_ms / 8)
|
||||
window_bytes = int(args.window * 1000.0 / bit_ms / 8)
|
||||
init_bytes = int(args.lead_in * 1000.0 / bit_ms / 8)
|
||||
lead_in, window, total = args.lead_in, args.window, args.total
|
||||
|
||||
assert (2*init_bytes + window_bytes) < total_bytes, "Window too large"
|
||||
print("Lead-In: %.2f sec -> %u bytes" % (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))
|
||||
assert lead_in >= 0.1, "Insufficient lead-in"
|
||||
assert total - window - lead_in >= 0.1, "Insufficient lead-out"
|
||||
|
||||
# Header
|
||||
out_f = open(args.outfile, "wb")
|
||||
out_f.write(struct.pack("<3x2s3x", b"QD"))
|
||||
out_f.write(bytearray(b'\x00'*(512-8)))
|
||||
bit_ms = 0.004916
|
||||
total_bytes = int(total * 1000.0 / bit_ms / 8)
|
||||
window_bytes = int(window * 1000.0 / bit_ms / 8)
|
||||
init_bytes = int(lead_in * 1000.0 / bit_ms / 8)
|
||||
|
||||
# Track
|
||||
out_f.write(struct.pack("<4I", 1024, total_bytes, init_bytes,
|
||||
init_bytes + window_bytes))
|
||||
out_f.write(bytearray(b'\x00'*(512-16)))
|
||||
if args.round:
|
||||
total_bytes = round_up(total_bytes, 512)
|
||||
window_bytes = round_up(window_bytes, 512)
|
||||
init_bytes = round_up(init_bytes, 512)
|
||||
|
||||
# Data
|
||||
blocks = (total_bytes + 511) // 512
|
||||
out_f.write(bytearray(b'\x11'*(blocks*512)))
|
||||
print("Lead-In: %.2f sec -> %u bytes" % (lead_in, init_bytes))
|
||||
print("Window: %.2f sec -> %u bytes" % (window, window_bytes))
|
||||
print("TOTAL: %.2f sec -> %u bytes" % (total, total_bytes))
|
||||
|
||||
# Header
|
||||
out_f = open(args.outfile, "wb")
|
||||
out_f.write(struct.pack("<3x2s3x", b"QD"))
|
||||
out_f.write(bytearray(b'\x00'*(512-8)))
|
||||
|
||||
# Track
|
||||
out_f.write(struct.pack("<4I", 1024, total_bytes, init_bytes,
|
||||
init_bytes + window_bytes))
|
||||
out_f.write(bytearray(b'\x00'*(512-16)))
|
||||
|
||||
# Data
|
||||
blocks = (total_bytes + 511) // 512
|
||||
out_f.write(bytearray(b'\x11'*(blocks*512)))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
|
||||
# Local variables:
|
||||
# python-indent: 4
|
||||
# End:
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ static unsigned int lcd_prep_buffer(void)
|
|||
order = ff_cfg.display_order;
|
||||
|
||||
row = (order >> (i2c_row * DORD_shift)) & DORD_row;
|
||||
p = (row < ARRAY_SIZE(text)) ? text[row] : NULL;
|
||||
p = (_bl && row < ARRAY_SIZE(text)) ? text[row] : NULL;
|
||||
|
||||
emit8(&q, CMD_SETDDRADDR | row_offs[i2c_row], 0);
|
||||
for (i = 0; i < lcd_columns; i++)
|
||||
|
|
@ -650,11 +650,8 @@ bool_t lcd_init(void)
|
|||
emit8(&p, CMD_DISPLAYCTL | 4, 0); /* display on */
|
||||
dma_start(p - buffer);
|
||||
|
||||
/* Wait for DMA engine to initialise RAM, then turn on backlight. */
|
||||
if (!reinit) {
|
||||
lcd_sync();
|
||||
if (!reinit)
|
||||
lcd_backlight(TRUE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ static unsigned int lcd_prep_buffer(void)
|
|||
order = ff_cfg.display_order;
|
||||
|
||||
row = (order >> (i2c_row * DORD_shift)) & DORD_row;
|
||||
p = (row < ARRAY_SIZE(text)) ? text[row] : NULL;
|
||||
p = (_bl && row < ARRAY_SIZE(text)) ? text[row] : NULL;
|
||||
|
||||
emit8(&q, CMD_SETDDRADDR | row_offs[i2c_row], 0);
|
||||
for (i = 0; i < lcd_columns; i++)
|
||||
|
|
@ -715,11 +715,8 @@ bool_t lcd_init(void)
|
|||
i2c->cr2 |= I2C_CR2_DMAEN;
|
||||
dma_start(p - buffer);
|
||||
|
||||
/* Wait for DMA engine to initialise RAM, then turn on backlight. */
|
||||
if (!reinit) {
|
||||
lcd_sync();
|
||||
if (!reinit)
|
||||
lcd_backlight(TRUE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@
|
|||
static uint8_t DAT_PIN = 10;
|
||||
static uint8_t CLK_PIN = 11;
|
||||
|
||||
/* TM1651, 74HC164: Alphanumeric segment arrangements. */
|
||||
/* TM1651, 74HC164: Alphanumeric segment arrangements.
|
||||
* Bit positions 0-6 correspond to conventional segment labels A-G resp. */
|
||||
static const uint8_t letters[] = {
|
||||
0x77, 0x7c, 0x58, 0x5e, 0x79, 0x71, 0x6f, 0x74, 0x04, /* a-i */
|
||||
0x0e, 0x08, 0x38, 0x40, 0x54, 0x5c, 0x73, 0x67, 0x50, /* j-r */
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ static void wdata_stop(void)
|
|||
image->wr_prod++;
|
||||
|
||||
#if !defined(QUICKDISK)
|
||||
if (!ff_cfg.index_suppression) {
|
||||
if (!ff_cfg.index_suppression && ff_cfg.write_drain != WDRAIN_realtime) {
|
||||
/* Opportunistically insert an INDEX pulse ahead of writeback. */
|
||||
drive_change_output(drv, outp_index, TRUE);
|
||||
index.fake_fired = TRUE;
|
||||
|
|
@ -643,16 +643,14 @@ static void IRQ_rdata_dma(void)
|
|||
static void IRQ_wdata_dma(void)
|
||||
{
|
||||
const uint16_t buf_mask = ARRAY_SIZE(dma_rd->buf) - 1;
|
||||
uint16_t cons, prod, prev, curr, next;
|
||||
uint16_t cell = image->write_bc_ticks, window;
|
||||
uint16_t cons, prod, prev, next;
|
||||
uint32_t bc_dat = 0, bc_prod;
|
||||
uint32_t *bc_buf = image->bufs.write_bc.p;
|
||||
unsigned int sync = image->sync;
|
||||
unsigned int bc_bufmask = (image->bufs.write_bc.len / 4) - 1;
|
||||
int curr, cell = image->write_bc_ticks;
|
||||
struct write *write = NULL;
|
||||
|
||||
window = cell + (cell >> 1);
|
||||
|
||||
/* Clear DMA peripheral interrupts. */
|
||||
dma1->ifcr = DMA_IFCR_CGIF(dma_wdata_ch);
|
||||
|
||||
|
|
@ -676,15 +674,20 @@ static void IRQ_wdata_dma(void)
|
|||
bc_dat = image->write_bc_window;
|
||||
for (cons = dma_wr->cons; cons != prod; cons = (cons+1) & buf_mask) {
|
||||
next = dma_wr->buf[cons];
|
||||
curr = next - prev;
|
||||
curr = (int16_t)(next - prev) - (cell >> 1);
|
||||
if (unlikely(curr < 0)) {
|
||||
/* Runt flux, much shorter than bitcell clock. Merge it forward. */
|
||||
continue;
|
||||
}
|
||||
prev = next;
|
||||
while (curr > window) {
|
||||
curr -= cell;
|
||||
while ((curr -= cell) > 0) {
|
||||
bc_dat <<= 1;
|
||||
bc_prod++;
|
||||
if (!(bc_prod&31))
|
||||
bc_buf[((bc_prod-1) / 32) & bc_bufmask] = htobe32(bc_dat);
|
||||
}
|
||||
curr += cell >> 1; /* remove the 1/2-cell bias */
|
||||
prev -= curr >> 2; /* de-jitter/precomp: carry 1/4 of phase error */
|
||||
bc_dat = (bc_dat << 1) | 1;
|
||||
bc_prod++;
|
||||
switch (sync) {
|
||||
|
|
|
|||
|
|
@ -325,7 +325,8 @@ static void IRQ_STEP_changed(void)
|
|||
drive_change_output(drv, outp_trk0, FALSE);
|
||||
if (dma_rd != NULL) {
|
||||
rdata_stop();
|
||||
if (!ff_cfg.index_suppression) {
|
||||
if (!ff_cfg.index_suppression
|
||||
&& ff_cfg.track_change != TRKCHG_realtime) {
|
||||
/* Opportunistically insert an INDEX pulse ahead of seek op. */
|
||||
drive_change_output(drv, outp_index, TRUE);
|
||||
index.fake_fired = TRUE;
|
||||
|
|
|
|||
|
|
@ -183,8 +183,8 @@ static void _IRQ_MOTOR_RESET_changed(unsigned int gpioa_idr)
|
|||
|
||||
if (!off) {
|
||||
|
||||
/* 2 seconds to spin up the motor. */
|
||||
timer_set(&motor.timer, time_now() + time_ms(2000));
|
||||
/* 1 second to spin up the motor. */
|
||||
timer_set(&motor.timer, time_now() + time_ms(1000));
|
||||
|
||||
} else {
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ static bool_t adf_open(struct image *im)
|
|||
im->tracklen_bc = DD_TRACKLEN_BC;
|
||||
im->ticks_per_cell = ((sampleclk_stk(im->stk_per_rev) * 16u)
|
||||
/ im->tracklen_bc);
|
||||
im->write_bc_ticks = im->ticks_per_cell / 16u;
|
||||
|
||||
im->nr_cyls = f_size(&im->fp) / (2 * 11 * 512);
|
||||
|
||||
|
|
|
|||
180
src/image/dsk.c
180
src/image/dsk.c
|
|
@ -9,11 +9,15 @@
|
|||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
#define LOG_PREFIX "DSK"
|
||||
|
||||
#define GAP_1 50 /* Post-IAM */
|
||||
#define GAP_2 22 /* Post-IDAM */
|
||||
#define GAP_4A 80 /* Post-Index */
|
||||
#define GAP_SYNC 12
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
struct dib { /* disk info */
|
||||
char sig[34];
|
||||
char creator[14];
|
||||
|
|
@ -94,14 +98,14 @@ static bool_t dsk_open(struct image *im)
|
|||
|
||||
im->nr_cyls = dib->nr_tracks;
|
||||
im->nr_sides = dib->nr_sides;
|
||||
printk("DSK: %u cyls, %u sides\n", im->nr_cyls, im->nr_sides);
|
||||
log("%u cyls, %u sides\n", im->nr_cyls, im->nr_sides);
|
||||
|
||||
/* DSK data rate is fixed at 2us bitcell. Where the specified track layout
|
||||
* will not fit in regular 100k-bitcell track we simply extend the track
|
||||
* length and thus the period between index pulses. */
|
||||
im->ticks_per_cell = im->write_bc_ticks * 16;
|
||||
|
||||
volume_cache_init(im->bufs.write_data.p + 512 + 1024,
|
||||
volume_cache_init(im->bufs.write_data.p + 512 + CHUNK_SIZE,
|
||||
im->bufs.write_data.p + im->bufs.write_data.len);
|
||||
|
||||
return TRUE;
|
||||
|
|
@ -224,9 +228,9 @@ static uint32_t calc_start_pos(struct image *im)
|
|||
im->dsk.decode_pos++;
|
||||
if (decode_off < data_sz(&tib->sib[i])) {
|
||||
/* Data */
|
||||
im->dsk.rd_sec_pos = decode_off / 1024;
|
||||
im->dsk.rd_sec_pos = decode_off / CHUNK_SIZE;
|
||||
im->dsk.decode_data_pos = im->dsk.rd_sec_pos;
|
||||
decode_off %= 1024;
|
||||
decode_off %= CHUNK_SIZE;
|
||||
} else {
|
||||
/* Post Data */
|
||||
decode_off -= data_sz(&tib->sib[i]);
|
||||
|
|
@ -238,8 +242,8 @@ static uint32_t calc_start_pos(struct image *im)
|
|||
} else {
|
||||
/* Pre-index track gap */
|
||||
im->dsk.decode_pos = tib->nr_secs * 4 + 1;
|
||||
im->dsk.decode_data_pos = decode_off / 1024;
|
||||
decode_off %= 1024;
|
||||
im->dsk.decode_data_pos = decode_off / CHUNK_SIZE;
|
||||
decode_off %= CHUNK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -267,15 +271,17 @@ static void dsk_setup_track(
|
|||
im->cur_ticks = im->cur_bc * im->ticks_per_cell;
|
||||
im->ticks_since_flux = 0;
|
||||
|
||||
decode_off = calc_start_pos(im);
|
||||
|
||||
rd->prod = rd->cons = 0;
|
||||
bc->prod = bc->cons = 0;
|
||||
|
||||
if (start_pos) {
|
||||
decode_off = calc_start_pos(im);
|
||||
|
||||
image_read_track(im);
|
||||
bc->cons = decode_off * 16;
|
||||
*start_pos = start_ticks;
|
||||
} else {
|
||||
im->dsk.decode_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -299,10 +305,10 @@ static bool_t dsk_read_track(struct image *im)
|
|||
/* Weak sector -- pick different data each revolution. */
|
||||
off += len * (im->dsk.rev % (tib->sib[i].actual_length / len));
|
||||
}
|
||||
off += im->dsk.rd_sec_pos * 1024;
|
||||
len -= im->dsk.rd_sec_pos * 1024;
|
||||
if (len > 1024) {
|
||||
len = 1024;
|
||||
off += im->dsk.rd_sec_pos * CHUNK_SIZE;
|
||||
len -= im->dsk.rd_sec_pos * CHUNK_SIZE;
|
||||
if (len > CHUNK_SIZE) {
|
||||
len = CHUNK_SIZE;
|
||||
im->dsk.rd_sec_pos++;
|
||||
} else {
|
||||
im->dsk.rd_sec_pos = 0;
|
||||
|
|
@ -346,11 +352,11 @@ static bool_t dsk_read_track(struct image *im)
|
|||
emit_byte(0x4e);
|
||||
} else if (im->dsk.decode_pos == (tib->nr_secs * 4 + 1)) {
|
||||
/* Pre-index track gap */
|
||||
uint16_t sz = im->dsk.gap4 - im->dsk.decode_data_pos * 1024;
|
||||
if (bc_space < min_t(unsigned int, sz, 1024))
|
||||
uint16_t sz = im->dsk.gap4 - im->dsk.decode_data_pos * CHUNK_SIZE;
|
||||
if (bc_space < min_t(unsigned int, sz, CHUNK_SIZE))
|
||||
return FALSE;
|
||||
if (sz > 1024) {
|
||||
sz = 1024;
|
||||
if (sz > CHUNK_SIZE) {
|
||||
sz = CHUNK_SIZE;
|
||||
im->dsk.decode_data_pos++;
|
||||
im->dsk.decode_pos--;
|
||||
} else {
|
||||
|
|
@ -402,11 +408,11 @@ static bool_t dsk_read_track(struct image *im)
|
|||
}
|
||||
case 2: /* Data */ {
|
||||
uint16_t sec_sz = data_sz(&tib->sib[sec]);
|
||||
sec_sz -= im->dsk.decode_data_pos * 1024;
|
||||
if (bc_space < min_t(unsigned int, sec_sz, 1024))
|
||||
sec_sz -= im->dsk.decode_data_pos * CHUNK_SIZE;
|
||||
if (bc_space < min_t(unsigned int, sec_sz, CHUNK_SIZE))
|
||||
return FALSE;
|
||||
if (sec_sz > 1024) {
|
||||
sec_sz = 1024;
|
||||
if (sec_sz > CHUNK_SIZE) {
|
||||
sec_sz = CHUNK_SIZE;
|
||||
im->dsk.decode_data_pos++;
|
||||
im->dsk.decode_pos--;
|
||||
} else {
|
||||
|
|
@ -460,7 +466,7 @@ static int dsk_find_first_write_sector(
|
|||
}
|
||||
|
||||
if (i >= tib->nr_secs) {
|
||||
printk("DSK Bad Wr.Off: %d\n", base);
|
||||
log("Bad Wr.Off: %d\n", base);
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
|
@ -477,10 +483,9 @@ static bool_t dsk_write_track(struct image *im)
|
|||
unsigned int bufmask = (wr->len / 2) - 1;
|
||||
uint8_t *wrbuf = (uint8_t *)im->bufs.write_data.p + 512; /* skip DIB/TIB */
|
||||
uint32_t c = wr->cons / 16, p = wr->prod / 16;
|
||||
unsigned int i;
|
||||
unsigned int i, off;
|
||||
time_t t;
|
||||
uint16_t crc, off;
|
||||
uint8_t x;
|
||||
uint16_t crc = im->dsk.crc;
|
||||
|
||||
/* If we are processing final data then use the end index, rounded up. */
|
||||
barrier();
|
||||
|
|
@ -490,94 +495,119 @@ static bool_t dsk_write_track(struct image *im)
|
|||
|
||||
while ((int16_t)(p - c) > 128) {
|
||||
|
||||
uint32_t sc = c;
|
||||
if (im->dsk.decode_pos == 0) {
|
||||
|
||||
if (be16toh(buf[c++ & bufmask]) != 0x4489)
|
||||
continue;
|
||||
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
|
||||
continue;
|
||||
c++;
|
||||
uint8_t x;
|
||||
|
||||
switch (x) {
|
||||
if (be16toh(buf[c++ & bufmask]) != 0x4489)
|
||||
continue;
|
||||
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
|
||||
continue;
|
||||
c++;
|
||||
|
||||
case 0xfe: /* IDAM */
|
||||
for (i = 0; i < 3; i++)
|
||||
wrbuf[i] = 0xa1;
|
||||
wrbuf[i++] = x;
|
||||
for (; i < 10; i++)
|
||||
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
|
||||
crc = crc16_ccitt(wrbuf, i, 0xffff);
|
||||
if (crc != 0) {
|
||||
printk("DSK IDAM Bad CRC: %04x, %02x\n", crc, wrbuf[6]);
|
||||
switch (x) {
|
||||
|
||||
case 0xfe: /* IDAM */
|
||||
for (i = 0; i < 3; i++)
|
||||
wrbuf[i] = 0xa1;
|
||||
wrbuf[i++] = x;
|
||||
for (; i < 10; i++)
|
||||
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
|
||||
crc = crc16_ccitt(wrbuf, i, 0xffff);
|
||||
if (crc != 0) {
|
||||
log("IDAM Bad CRC: %04x, %02x\n", crc, wrbuf[6]);
|
||||
break;
|
||||
}
|
||||
/* Convert logical sector number -> rotational number. */
|
||||
for (i = 0; i < tib->nr_secs; i++)
|
||||
if (wrbuf[6] == tib->sib[i].r)
|
||||
break;
|
||||
im->dsk.write_sector = i;
|
||||
if (im->dsk.write_sector >= tib->nr_secs) {
|
||||
log("IDAM Bad Sector: %02x\n", wrbuf[6]);
|
||||
im->dsk.write_sector = -2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xfb: /* DAM */
|
||||
im->dsk.decode_pos = 1;
|
||||
im->dsk.decode_data_pos = 0;
|
||||
break;
|
||||
}
|
||||
/* Convert logical sector number -> rotational number. */
|
||||
for (i = 0; i < tib->nr_secs; i++)
|
||||
if (wrbuf[6] == tib->sib[i].r)
|
||||
break;
|
||||
im->dsk.write_sector = i;
|
||||
if (im->dsk.write_sector >= tib->nr_secs) {
|
||||
printk("DSK IDAM Bad Sector: %02x\n", wrbuf[6]);
|
||||
im->dsk.write_sector = -2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xfb: /* DAM */ {
|
||||
unsigned int nr, todo, sec_sz;
|
||||
} else {
|
||||
|
||||
/* Data record, shy address mark */
|
||||
unsigned int sec_sz;
|
||||
int sec_nr = im->dsk.write_sector;
|
||||
|
||||
ASSERT(im->dsk.decode_pos == 1);
|
||||
|
||||
if (sec_nr < 0) {
|
||||
if (sec_nr == -1)
|
||||
if (sec_nr == -1) {
|
||||
sec_nr = dsk_find_first_write_sector(im, write, tib);
|
||||
im->dsk.write_sector = sec_nr;
|
||||
}
|
||||
if (sec_nr < 0) {
|
||||
printk("DSK DAM Unknown\n");
|
||||
goto dam_out;
|
||||
log("DAM Unknown\n");
|
||||
goto data_complete;
|
||||
}
|
||||
}
|
||||
|
||||
sec_sz = data_sz(&tib->sib[sec_nr]);
|
||||
if ((int16_t)(p - c) < (sec_sz + 2)) {
|
||||
c = sc;
|
||||
goto out;
|
||||
}
|
||||
|
||||
crc = MFM_DAM_CRC;
|
||||
|
||||
printk("Write %d[%02x]/%u... ",
|
||||
sec_nr, tib->sib[sec_nr].r, tib->nr_secs);
|
||||
t = time_now();
|
||||
|
||||
for (i = off = 0; i < sec_nr; i++)
|
||||
off += tib->sib[i].actual_length;
|
||||
F_lseek(&im->fp, im->dsk.trk_off + off);
|
||||
off += im->dsk.trk_off;
|
||||
off += im->dsk.decode_data_pos;
|
||||
|
||||
for (todo = sec_sz; todo != 0; todo -= nr) {
|
||||
nr = min_t(unsigned int, todo, 1024);
|
||||
if (im->dsk.decode_data_pos < sec_sz) {
|
||||
unsigned int nr = sec_sz - im->dsk.decode_data_pos;
|
||||
nr = min_t(unsigned int, nr, CHUNK_SIZE - (off & 511));
|
||||
if ((int16_t)(p - c) < nr)
|
||||
break;
|
||||
|
||||
if (!im->dsk.decode_data_pos) {
|
||||
crc = MFM_DAM_CRC;
|
||||
log("Write %d[%02x]/%u...",
|
||||
sec_nr, tib->sib[sec_nr].r, tib->nr_secs);
|
||||
F_lseek(&im->fp, off);
|
||||
}
|
||||
|
||||
t = time_now();
|
||||
mfm_ring_to_bin(buf, bufmask, c, wrbuf, nr);
|
||||
c += nr;
|
||||
crc = crc16_ccitt(wrbuf, nr, crc);
|
||||
F_write(&im->fp, wrbuf, nr, NULL);
|
||||
printk(" %u us", time_diff(t, time_now()) / TIME_MHZ);
|
||||
im->dsk.decode_data_pos += nr;
|
||||
if (im->dsk.decode_data_pos < sec_sz)
|
||||
printk("...");
|
||||
else
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
|
||||
if (im->dsk.decode_data_pos < sec_sz)
|
||||
continue;
|
||||
|
||||
if ((int16_t)(p - c) < 2)
|
||||
break;
|
||||
mfm_ring_to_bin(buf, bufmask, c, wrbuf, 2);
|
||||
c += 2;
|
||||
crc = crc16_ccitt(wrbuf, 2, crc);
|
||||
if (crc != 0) {
|
||||
printk("DSK Bad CRC: %04x, %d[%02x]\n",
|
||||
crc, sec_nr, tib->sib[sec_nr].r);
|
||||
log("Bad CRC: %04x, %d[%02x]\n",
|
||||
crc, sec_nr, tib->sib[sec_nr].r);
|
||||
}
|
||||
|
||||
dam_out:
|
||||
data_complete:
|
||||
im->dsk.write_sector = -2;
|
||||
break;
|
||||
}
|
||||
im->dsk.decode_pos = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
im->dsk.crc = crc;
|
||||
wr->cons = c * 16;
|
||||
return flush;
|
||||
}
|
||||
|
|
|
|||
137
src/image/hfe.c
137
src/image/hfe.c
|
|
@ -128,7 +128,13 @@ static void hfe_seek_track(struct image *im, uint16_t track)
|
|||
im->hfe.trk_off = le16toh(thdr.offset);
|
||||
im->hfe.trk_len = le16toh(thdr.len) / 2;
|
||||
im->tracklen_bc = im->hfe.trk_len * 8;
|
||||
im->stk_per_rev = stk_sampleclk(im->tracklen_bc * im->write_bc_ticks);
|
||||
if (im->hfe.is_v3 && im->tracklen_ticks) {
|
||||
/* Opcodes in v3 make it difficult to predict the track's length. Keep
|
||||
* the previous track's value since this isn't the first seek. */
|
||||
} else {
|
||||
im->tracklen_ticks = im->tracklen_bc * im->ticks_per_cell;
|
||||
im->stk_per_rev = stk_sampleclk(im->tracklen_ticks / 16);
|
||||
}
|
||||
|
||||
im->cur_track = track;
|
||||
}
|
||||
|
|
@ -147,13 +153,16 @@ static void hfe_setup_track(
|
|||
hfe_seek_track(im, track);
|
||||
|
||||
start_ticks = start_pos ? *start_pos : get_write(im, im->wr_cons)->start;
|
||||
im->cur_bc = (start_ticks * 16) / im->ticks_per_cell;
|
||||
if (im->cur_bc >= im->tracklen_bc)
|
||||
im->cur_bc = 0;
|
||||
im->cur_ticks = im->cur_bc * im->ticks_per_cell;
|
||||
im->ticks_since_flux = 0;
|
||||
|
||||
start_ticks = im->cur_ticks / 16;
|
||||
im->cur_ticks = start_ticks * 16;
|
||||
im->cur_bc = udiv64((uint64_t)im->cur_ticks * im->tracklen_bc,
|
||||
im->tracklen_ticks);
|
||||
if ((im->cur_ticks >= im->tracklen_ticks) ||
|
||||
(im->cur_bc >= im->tracklen_bc)) {
|
||||
im->cur_ticks = 0;
|
||||
im->cur_bc = 0;
|
||||
}
|
||||
im->ticks_since_flux = 0;
|
||||
|
||||
rd->prod = rd->cons = 0;
|
||||
bc->prod = bc->cons = 0;
|
||||
|
|
@ -167,10 +176,16 @@ static void hfe_setup_track(
|
|||
im->hfe.trk_pos = (im->cur_bc/8) & ~255;
|
||||
image_read_track(im);
|
||||
bc->cons = im->cur_bc & 2047;
|
||||
*start_pos = start_ticks;
|
||||
} else {
|
||||
/* Write mode. */
|
||||
im->hfe.trk_pos = im->cur_bc / 8;
|
||||
if (im->hfe.is_v3) {
|
||||
/* Provide context to the write to avoid corrupting an opcode. */
|
||||
if ((im->hfe.trk_pos & 255) == 0 && im->hfe.trk_pos != 0)
|
||||
im->hfe.trk_pos--;
|
||||
else if ((im->hfe.trk_pos & 255) == 1)
|
||||
im->hfe.trk_pos = (im->hfe.trk_pos+1) % im->hfe.trk_len;
|
||||
}
|
||||
im->hfe.write.start = im->hfe.trk_pos;
|
||||
im->hfe.write.wrapped = FALSE;
|
||||
im->hfe.write_batch.len = 0;
|
||||
|
|
@ -231,30 +246,28 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
|
|||
uint32_t bc_c = bc->cons, bc_p = bc->prod, bc_mask = bc->len - 1;
|
||||
uint32_t ticks = im->ticks_since_flux;
|
||||
uint32_t ticks_per_cell = im->ticks_per_cell;
|
||||
uint32_t y, todo = nr;
|
||||
uint32_t bit_off, todo = nr;
|
||||
uint8_t x;
|
||||
bool_t is_v3 = im->hfe.is_v3;
|
||||
|
||||
for (;;) {
|
||||
|
||||
if ((uint32_t)(bc_p - bc_c) < 3*8) {
|
||||
y = 8;
|
||||
goto out;
|
||||
}
|
||||
while ((uint32_t)(bc_p - bc_c) >= 3*8) {
|
||||
|
||||
if (im->cur_bc >= im->tracklen_bc) {
|
||||
/* Malformed HFE v3 file can trigger this assertion. Requires a
|
||||
* multi-byte opcode which extends beyond reported track length. */
|
||||
ASSERT(im->cur_bc == im->tracklen_bc);
|
||||
im->tracklen_ticks = im->cur_ticks;
|
||||
im->stk_per_rev = stk_sampleclk(im->tracklen_ticks / 16);
|
||||
im->cur_bc = im->cur_ticks = 0;
|
||||
/* Skip tail of current 256-byte block. */
|
||||
bc_c = (bc_c + 256*8-1) & ~(256*8-1);
|
||||
continue;
|
||||
}
|
||||
|
||||
y = bc_c % 8;
|
||||
bit_off = bc_c % 8;
|
||||
x = bc_b[(bc_c/8) & bc_mask];
|
||||
bc_c += 8 - y;
|
||||
im->cur_bc += 8 - y;
|
||||
bc_c += 8 - bit_off;
|
||||
im->cur_bc += 8 - bit_off;
|
||||
|
||||
if (is_v3 && ((x & 0xf) == 0xf)) {
|
||||
/* V3 byte-aligned opcode processing. */
|
||||
|
|
@ -267,6 +280,7 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
|
|||
x = _rbit32(bc_b[(bc_c/8) & bc_mask]) >> 24;
|
||||
im->ticks_per_cell = ticks_per_cell =
|
||||
(sampleclk_us(2) * 16 * x) / 72;
|
||||
im->write_bc_ticks = ticks_per_cell / 16;
|
||||
bc_c += 8;
|
||||
im->cur_bc += 8;
|
||||
continue;
|
||||
|
|
@ -281,25 +295,38 @@ static uint16_t hfe_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
|
|||
}
|
||||
}
|
||||
|
||||
x >>= y;
|
||||
im->cur_ticks += (8 - y) * ticks_per_cell;
|
||||
while (y < 8) {
|
||||
y++;
|
||||
x >>= bit_off;
|
||||
im->cur_ticks += (8 - bit_off) * ticks_per_cell;
|
||||
while (bit_off < 8) {
|
||||
bit_off++;
|
||||
ticks += ticks_per_cell;
|
||||
if (x & 1) {
|
||||
*tbuf++ = (ticks >> 4) - 1;
|
||||
ticks &= 15;
|
||||
if (!--todo)
|
||||
if (!--todo) {
|
||||
bc_c -= 8 - bit_off;
|
||||
im->cur_bc -= 8 - bit_off;
|
||||
im->cur_ticks -= (8 - bit_off) * ticks_per_cell;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
x >>= 1;
|
||||
}
|
||||
|
||||
/* Subdivide a long flux gap to avoid overflowing the 16-bit timer.
|
||||
* This mishandles long No Flux Areas slightly, by regularly emitting
|
||||
* a flux-reversal pulse every 2^14 sampleclk ticks. */
|
||||
if (unlikely((ticks >> (15+4)) != 0)) {
|
||||
*tbuf++ = (1u << 14) - 1;
|
||||
ticks -= 1u << (14+4);
|
||||
if (!--todo)
|
||||
goto out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
bc->cons = bc_c - (8 - y);
|
||||
im->cur_bc -= 8 - y;
|
||||
im->cur_ticks -= (8 - y) * ticks_per_cell;
|
||||
bc->cons = bc_c;
|
||||
im->ticks_since_flux = ticks;
|
||||
return nr - todo;
|
||||
}
|
||||
|
|
@ -314,6 +341,7 @@ static bool_t hfe_write_track(struct image *im)
|
|||
unsigned int bufmask = wr->len - 1;
|
||||
uint8_t *w, *wrbuf = im->bufs.write_data.p;
|
||||
uint32_t i, space, c = wr->cons / 8, p = wr->prod / 8;
|
||||
bool_t is_v3 = im->hfe.is_v3;
|
||||
bool_t writeback = FALSE;
|
||||
time_t t;
|
||||
|
||||
|
|
@ -365,11 +393,62 @@ static bool_t hfe_write_track(struct image *im)
|
|||
+ (im->cur_track & 1) * 256
|
||||
+ batch_off - im->hfe.write_batch.off
|
||||
+ (off & 255);
|
||||
for (i = 0; i < nr; i++)
|
||||
*w++ = _rbit32(buf[c++ & bufmask]) >> 24;
|
||||
|
||||
i = 0;
|
||||
|
||||
if (is_v3 && off == im->hfe.write.start && off != 0) {
|
||||
/* Avoid starting write in the middle of an opcode. */
|
||||
if (w[-2] == OP_SkipBits) {
|
||||
i++;
|
||||
} else {
|
||||
switch (w[-1]) {
|
||||
case OP_SkipBits:
|
||||
i += 2;
|
||||
break;
|
||||
case OP_Bitrate:
|
||||
i++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (i < nr) {
|
||||
if (is_v3 && (w[i] & 0xf) == 0xf) {
|
||||
switch (w[i]) {
|
||||
case OP_SkipBits:
|
||||
/* Keep the write byte-aligned. This changes the length of
|
||||
* the track by 8+skip bitcells, but overwriting OP_SkipBits
|
||||
* should be rare. */
|
||||
w[i++] = OP_Nop;
|
||||
continue;
|
||||
|
||||
case OP_Bitrate:
|
||||
/* Assume bitrate does not change significantly for the
|
||||
* entire track, and write_bc_ticks already adjusted when
|
||||
* reading. */
|
||||
i += 2;
|
||||
continue;
|
||||
|
||||
case OP_Nop:
|
||||
case OP_Index:
|
||||
default:
|
||||
/* Preserve opcode. But making sure not to write past end of
|
||||
* buffer. */
|
||||
i++;
|
||||
continue;
|
||||
|
||||
case OP_Rand:
|
||||
/* Replace with data. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
w[i++] = _rbit32(buf[c++ & bufmask]) >> 24;
|
||||
}
|
||||
im->hfe.write_batch.dirty = TRUE;
|
||||
|
||||
im->hfe.trk_pos += nr;
|
||||
im->hfe.trk_pos += i; /* i may be larger than nr due to opcodes. */
|
||||
if (im->hfe.trk_pos >= im->hfe.trk_len) {
|
||||
ASSERT(im->hfe.trk_pos == im->hfe.trk_len);
|
||||
im->hfe.trk_pos = 0;
|
||||
|
|
|
|||
255
src/image/img.c
255
src/image/img.c
|
|
@ -9,6 +9,8 @@
|
|||
* See the file COPYING for more details, or visit <http://unlicense.org>.
|
||||
*/
|
||||
|
||||
#define LOG_PREFIX "IMG"
|
||||
|
||||
static FSIZE_t raw_extend(struct image *im);
|
||||
static void raw_setup_track(
|
||||
struct image *im, uint16_t track, uint32_t *start_pos);
|
||||
|
|
@ -64,6 +66,8 @@ static bool_t xdf_check(const struct bpb *bpb);
|
|||
#define LAYOUT_sides_swapped (1u<<1)
|
||||
#define LAYOUT_reverse_side(x) (1u<<(2+(x)))
|
||||
|
||||
#define CHUNK_SIZE 1024
|
||||
|
||||
#define sec_sz(n) (128u << (n))
|
||||
|
||||
#define _IAM 1 /* IAM */
|
||||
|
|
@ -1367,13 +1371,13 @@ static bool_t xdf_open(struct image *im)
|
|||
{ /* 3.5 HD */
|
||||
/* Cyl 0, head 0:
|
||||
* 1-8,129-139 (secs=19, interleave=2)
|
||||
* Sectors 1-8 (Aux FAT): Offsets 0x1800-0x2600
|
||||
* Sectors 129-139 (Main FAT, Pt.1): Offsets 0x0000-0x1400 */
|
||||
* Sectors 1-8 (Aux FAT): Offsets 0x1800-0x27ff
|
||||
* Sectors 129-139 (Main FAT, Pt.1): Offsets 0x0000-0x15ff */
|
||||
/* Cyl 0, head 1:
|
||||
* 129-147 (secs=19, interleave=2)
|
||||
* Sector 129 (Main FAT, Pt.2): Offset 0x1600
|
||||
* Sectors 130-143 (RootDir): Offsets 0x2e00-0x4800
|
||||
* Sectors 144-147 (Data): Offsets 0x5400-0x5a00 */
|
||||
* Sector 129 (Main FAT, Pt.2): Offset 0x1600-0x17ff
|
||||
* Sectors 130-143 (RootDir): Offsets 0x2e00-0x49ff
|
||||
* Sectors 144-147 (Data): Offsets 0x5400-0x5bff */
|
||||
/* Cyl N, head 0:
|
||||
* 131(1k), 130(.5k), 132(2k), 134(8k)
|
||||
* Cyl N, head 1: Track slip of ~10k bitcells relative to head 0
|
||||
|
|
@ -1828,9 +1832,9 @@ static uint32_t calc_start_pos(struct image *im)
|
|||
im->img.decode_pos++;
|
||||
if (decode_off < sec_sz(sec->n)) {
|
||||
/* Data */
|
||||
im->img.rd_sec_pos = decode_off / 1024;
|
||||
im->img.rd_sec_pos = decode_off / CHUNK_SIZE;
|
||||
im->img.decode_data_pos = im->img.rd_sec_pos;
|
||||
decode_off %= 1024;
|
||||
decode_off %= CHUNK_SIZE;
|
||||
} else {
|
||||
/* Post Data */
|
||||
decode_off -= sec_sz(sec->n);
|
||||
|
|
@ -1843,8 +1847,8 @@ static uint32_t calc_start_pos(struct image *im)
|
|||
} else {
|
||||
/* Pre-index track gap */
|
||||
im->img.decode_pos = trk->nr_sectors * 4 + 1;
|
||||
im->img.decode_data_pos = decode_off / 1024;
|
||||
decode_off %= 1024;
|
||||
im->img.decode_data_pos = decode_off / CHUNK_SIZE;
|
||||
decode_off %= CHUNK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1872,15 +1876,17 @@ static void raw_setup_track(
|
|||
im->cur_ticks = im->cur_bc * im->ticks_per_cell;
|
||||
im->ticks_since_flux = 0;
|
||||
|
||||
decode_off = calc_start_pos(im);
|
||||
|
||||
rd->prod = rd->cons = 0;
|
||||
bc->prod = bc->cons = 0;
|
||||
|
||||
if (start_pos) {
|
||||
decode_off = calc_start_pos(im);
|
||||
|
||||
image_read_track(im);
|
||||
bc->cons = decode_off * 16;
|
||||
*start_pos = start_ticks;
|
||||
} else {
|
||||
im->img.decode_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1889,7 +1895,7 @@ static bool_t raw_open(struct image *im)
|
|||
if (im->step == 0)
|
||||
im->step = 1;
|
||||
|
||||
volume_cache_init(im->bufs.write_data.p + 1024,
|
||||
volume_cache_init(im->bufs.write_data.p + CHUNK_SIZE,
|
||||
im->img.heap_bottom);
|
||||
|
||||
/* Initialise write_bc_ticks (used by floppy_insert to set outp_hden). */
|
||||
|
|
@ -1925,9 +1931,9 @@ static int raw_find_first_write_sector(
|
|||
int32_t base;
|
||||
|
||||
base = write->start / im->ticks_per_cell; /* in data bytes */
|
||||
base -= im->img.track_delay_bc;
|
||||
base -= im->img.track_delay_bc / 16;
|
||||
if (base < 0)
|
||||
base += im->tracklen_bc;
|
||||
base += im->tracklen_bc / 16;
|
||||
|
||||
/* Convert write offset to sector number (in rotational order). */
|
||||
base -= im->img.idx_sz + im->img.idam_sz;
|
||||
|
|
@ -1940,7 +1946,7 @@ static int raw_find_first_write_sector(
|
|||
|
||||
/* Convert rotational order to logical order. */
|
||||
if (i >= trk->nr_sectors) {
|
||||
printk("IMG Bad Wr.Off: %d\n", base);
|
||||
log("Bad Wr.Off: %d\n", base);
|
||||
return -2;
|
||||
}
|
||||
return *sec_map;
|
||||
|
|
@ -1959,8 +1965,8 @@ static bool_t raw_write_track(struct image *im)
|
|||
struct raw_sec *sec;
|
||||
unsigned int i, off;
|
||||
time_t t;
|
||||
uint16_t crc;
|
||||
uint8_t idam_r, x;
|
||||
uint16_t crc = im->img.crc;
|
||||
uint8_t idam_r;
|
||||
|
||||
/* If we are processing final data then use the end index, rounded up. */
|
||||
barrier();
|
||||
|
|
@ -1970,127 +1976,152 @@ static bool_t raw_write_track(struct image *im)
|
|||
|
||||
while ((int16_t)(p - c) > 128) {
|
||||
|
||||
uint32_t sc = c;
|
||||
if (im->img.decode_pos == 0) {
|
||||
|
||||
if (im->sync == SYNC_fm) {
|
||||
uint8_t x;
|
||||
|
||||
uint16_t sync;
|
||||
if (buf[c++ & bufmask] != 0xaaaa)
|
||||
continue;
|
||||
sync = buf[c & bufmask];
|
||||
if (mfmtobin(sync >> 1) != FM_SYNC_CLK)
|
||||
continue;
|
||||
x = mfmtobin(sync);
|
||||
c++;
|
||||
|
||||
} else { /* MFM */
|
||||
|
||||
if (be16toh(buf[c++ & bufmask]) != 0x4489)
|
||||
continue;
|
||||
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
|
||||
continue;
|
||||
c++;
|
||||
|
||||
}
|
||||
|
||||
switch (x) {
|
||||
|
||||
case 0xfe: /* IDAM */
|
||||
if (im->sync == SYNC_fm) {
|
||||
wrbuf[0] = x;
|
||||
for (i = 1; i < 7; i++)
|
||||
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
|
||||
idam_r = wrbuf[3];
|
||||
|
||||
uint16_t sync;
|
||||
if (buf[c++ & bufmask] != 0xaaaa)
|
||||
continue;
|
||||
sync = buf[c & bufmask];
|
||||
if (mfmtobin(sync >> 1) != FM_SYNC_CLK)
|
||||
continue;
|
||||
x = mfmtobin(sync);
|
||||
c++;
|
||||
|
||||
} else { /* MFM */
|
||||
for (i = 0; i < 3; i++)
|
||||
wrbuf[i] = 0xa1;
|
||||
wrbuf[i++] = x;
|
||||
for (; i < 10; i++)
|
||||
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
|
||||
idam_r = wrbuf[6];
|
||||
|
||||
if (be16toh(buf[c++ & bufmask]) != 0x4489)
|
||||
continue;
|
||||
if ((x = mfmtobin(buf[c & bufmask])) == 0xa1)
|
||||
continue;
|
||||
c++;
|
||||
|
||||
}
|
||||
crc = crc16_ccitt(wrbuf, i, 0xffff);
|
||||
if (crc != 0) {
|
||||
printk("IMG IDAM Bad CRC: %04x, %u\n", crc, idam_r);
|
||||
|
||||
switch (x) {
|
||||
|
||||
case 0xfe: /* IDAM */
|
||||
if (im->sync == SYNC_fm) {
|
||||
wrbuf[0] = x;
|
||||
for (i = 1; i < 7; i++)
|
||||
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
|
||||
idam_r = wrbuf[3];
|
||||
} else { /* MFM */
|
||||
for (i = 0; i < 3; i++)
|
||||
wrbuf[i] = 0xa1;
|
||||
wrbuf[i++] = x;
|
||||
for (; i < 10; i++)
|
||||
wrbuf[i] = mfmtobin(buf[c++ & bufmask]);
|
||||
idam_r = wrbuf[6];
|
||||
}
|
||||
crc = crc16_ccitt(wrbuf, i, 0xffff);
|
||||
if (crc != 0) {
|
||||
log("IDAM Bad CRC: %04x, %u\n", crc, idam_r);
|
||||
break;
|
||||
}
|
||||
/* Search by sector id for this sector's logical order. */
|
||||
for (i = 0, sec = im->img.sec_info;
|
||||
(i < trk->nr_sectors) && (sec->r != idam_r);
|
||||
i++, sec++)
|
||||
continue;
|
||||
im->img.write_sector = i;
|
||||
if (i >= trk->nr_sectors) {
|
||||
log("IDAM Bad Sector: %02x\n", idam_r);
|
||||
im->img.write_sector = -2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xfb: /* DAM */
|
||||
im->img.decode_pos = 1;
|
||||
im->img.decode_data_pos = 0;
|
||||
break;
|
||||
}
|
||||
/* Search by sector id for this sector's logical order. */
|
||||
for (i = 0, sec = im->img.sec_info;
|
||||
(i < trk->nr_sectors) && (sec->r != idam_r);
|
||||
i++, sec++)
|
||||
continue;
|
||||
im->img.write_sector = i;
|
||||
if (i >= trk->nr_sectors) {
|
||||
printk("IMG IDAM Bad Sector: %02x\n", idam_r);
|
||||
im->img.write_sector = -2;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xfb: /* DAM */ {
|
||||
unsigned int nr, todo, sec_sz;
|
||||
} else {
|
||||
|
||||
/* Data record, shy address mark */
|
||||
unsigned int sec_sz;
|
||||
int sec_nr = im->img.write_sector;
|
||||
|
||||
ASSERT(im->img.decode_pos == 1);
|
||||
|
||||
if (sec_nr < 0) {
|
||||
if (sec_nr == -1)
|
||||
if (sec_nr == -1) {
|
||||
sec_nr = raw_find_first_write_sector(im, write, trk);
|
||||
im->img.write_sector = sec_nr;
|
||||
}
|
||||
if (sec_nr < 0) {
|
||||
printk("IMG DAM Unknown\n");
|
||||
goto dam_out;
|
||||
log("DAM Unknown\n");
|
||||
goto data_complete;
|
||||
}
|
||||
}
|
||||
|
||||
sec_sz = sec_sz(im->img.sec_info[sec_nr].n);
|
||||
if ((int16_t)(p - c) < (sec_sz + 2)) {
|
||||
c = sc;
|
||||
goto out;
|
||||
}
|
||||
|
||||
crc = (im->sync == SYNC_fm) ? FM_DAM_CRC : MFM_DAM_CRC;
|
||||
|
||||
sec = &im->img.sec_info[sec_nr];
|
||||
printk("Write %u[%02x]/%u... ", sec_nr, sec->r, trk->nr_sectors);
|
||||
t = time_now();
|
||||
|
||||
if (im->img.file_sec_offsets) {
|
||||
off = im->img.file_sec_offsets[sec_nr];
|
||||
} else if (trk->img_bps != 0) {
|
||||
off = sec_nr * trk->img_bps;
|
||||
} else {
|
||||
off = 0;
|
||||
sec = im->img.sec_info;
|
||||
for (i = 0; i < sec_nr; i++)
|
||||
for (i = off = 0; i < sec_nr; i++)
|
||||
off += sec_sz(sec++->n);
|
||||
}
|
||||
F_lseek(&im->fp, im->img.trk_off + off);
|
||||
off += im->img.trk_off;
|
||||
off += im->img.decode_data_pos;
|
||||
|
||||
for (todo = sec_sz; todo != 0; todo -= nr) {
|
||||
nr = min_t(unsigned int, todo, 1024);
|
||||
if (im->img.decode_data_pos < sec_sz) {
|
||||
unsigned int nr = sec_sz - im->img.decode_data_pos;
|
||||
nr = min_t(unsigned int, nr, CHUNK_SIZE - (off & 511));
|
||||
if ((int16_t)(p - c) < nr)
|
||||
break;
|
||||
|
||||
if (!im->img.decode_data_pos) {
|
||||
crc = (im->sync == SYNC_fm) ? FM_DAM_CRC : MFM_DAM_CRC;
|
||||
log("Write %u[%02x]/%u...",
|
||||
sec_nr, sec->r, trk->nr_sectors);
|
||||
F_lseek(&im->fp, off);
|
||||
}
|
||||
|
||||
t = time_now();
|
||||
mfm_ring_to_bin(buf, bufmask, c, wrbuf, nr);
|
||||
c += nr;
|
||||
crc = crc16_ccitt(wrbuf, nr, crc);
|
||||
process_data(im, wrbuf, nr);
|
||||
F_write(&im->fp, wrbuf, nr, NULL);
|
||||
printk(" %u us", time_diff(t, time_now()) / TIME_MHZ);
|
||||
im->img.decode_data_pos += nr;
|
||||
if (im->img.decode_data_pos < sec_sz)
|
||||
printk("...");
|
||||
else
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
printk("%u us\n", time_diff(t, time_now()) / TIME_MHZ);
|
||||
if (im->img.decode_data_pos < sec_sz)
|
||||
continue;
|
||||
|
||||
if ((int16_t)(p - c) < 2)
|
||||
break;
|
||||
mfm_ring_to_bin(buf, bufmask, c, wrbuf, 2);
|
||||
c += 2;
|
||||
crc = crc16_ccitt(wrbuf, 2, crc);
|
||||
if (crc != 0) {
|
||||
printk("IMG Bad CRC: %04x, %u[%02x]\n",
|
||||
crc, sec_nr, sec->r);
|
||||
log("Bad CRC: %04x, %u[%02x]\n", crc, sec_nr, sec->r);
|
||||
}
|
||||
|
||||
dam_out:
|
||||
data_complete:
|
||||
im->img.write_sector = -2;
|
||||
break;
|
||||
}
|
||||
im->img.decode_pos = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
im->img.crc = crc;
|
||||
wr->cons = c * 16;
|
||||
return flush;
|
||||
}
|
||||
|
|
@ -2153,11 +2184,11 @@ static void img_fetch_data(struct image *im)
|
|||
|
||||
len = sec_sz(sec->n);
|
||||
|
||||
off += im->img.rd_sec_pos * 1024;
|
||||
len -= im->img.rd_sec_pos * 1024;
|
||||
off += im->img.rd_sec_pos * CHUNK_SIZE;
|
||||
len -= im->img.rd_sec_pos * CHUNK_SIZE;
|
||||
|
||||
if (len > 1024) {
|
||||
len = 1024;
|
||||
if (len > CHUNK_SIZE) {
|
||||
len = CHUNK_SIZE;
|
||||
im->img.rd_sec_pos++;
|
||||
} else {
|
||||
im->img.rd_sec_pos = 0;
|
||||
|
|
@ -2180,14 +2211,14 @@ static void *align_p(void *p)
|
|||
static void check_p(void *p, struct image *im)
|
||||
{
|
||||
uint8_t *a = p, *b = (uint8_t *)im->bufs.read_data.p;
|
||||
if ((int32_t)(a-b) < 1024)
|
||||
if ((int32_t)(a-b) < CHUNK_SIZE)
|
||||
F_die(FR_BAD_IMAGE);
|
||||
im->img.heap_bottom = p;
|
||||
}
|
||||
|
||||
/* Initialise track/sector-info structures at the top of the heap.
|
||||
* In ascending address order:
|
||||
* {read,write}_data (truncated to 1024 bytes)
|
||||
* {read,write}_data (truncated to CHUNK_SIZE bytes)
|
||||
* ... [volume cache]
|
||||
* im->img.trk_info (trk_map[] points into here)
|
||||
* im->img.sec_info_base (trk_info[] + sec_map[] point into here)
|
||||
|
|
@ -2454,11 +2485,11 @@ static bool_t mfm_read_track(struct image *im)
|
|||
}
|
||||
} else if (im->img.decode_pos == (trk->nr_sectors * 4 + 1)) {
|
||||
/* Pre-index track gap */
|
||||
uint16_t sz = im->img.gap_4 - im->img.decode_data_pos * 1024;
|
||||
if (bc_space < min_t(unsigned int, sz, 1024))
|
||||
uint16_t sz = im->img.gap_4 - im->img.decode_data_pos * CHUNK_SIZE;
|
||||
if (bc_space < min_t(unsigned int, sz, CHUNK_SIZE))
|
||||
return FALSE;
|
||||
if (sz > 1024) {
|
||||
sz = 1024;
|
||||
if (sz > CHUNK_SIZE) {
|
||||
sz = CHUNK_SIZE;
|
||||
im->img.decode_data_pos++;
|
||||
im->img.decode_pos--;
|
||||
} else {
|
||||
|
|
@ -2506,11 +2537,11 @@ static bool_t mfm_read_track(struct image *im)
|
|||
}
|
||||
case 2: /* Data */ {
|
||||
uint16_t sec_sz = sec_sz(sec->n);
|
||||
sec_sz -= im->img.decode_data_pos * 1024;
|
||||
if (bc_space < min_t(unsigned int, sec_sz, 1024))
|
||||
sec_sz -= im->img.decode_data_pos * CHUNK_SIZE;
|
||||
if (bc_space < min_t(unsigned int, sec_sz, CHUNK_SIZE))
|
||||
return FALSE;
|
||||
if (sec_sz > 1024) {
|
||||
sec_sz = 1024;
|
||||
if (sec_sz > CHUNK_SIZE) {
|
||||
sec_sz = CHUNK_SIZE;
|
||||
im->img.decode_data_pos++;
|
||||
im->img.decode_pos--;
|
||||
} else {
|
||||
|
|
@ -2667,11 +2698,11 @@ static bool_t fm_read_track(struct image *im)
|
|||
}
|
||||
} else if (im->img.decode_pos == (trk->nr_sectors * 4 + 1)) {
|
||||
/* Pre-index track gap */
|
||||
uint16_t sz = im->img.gap_4 - im->img.decode_data_pos * 1024;
|
||||
if (bc_space < min_t(unsigned int, sz, 1024))
|
||||
uint16_t sz = im->img.gap_4 - im->img.decode_data_pos * CHUNK_SIZE;
|
||||
if (bc_space < min_t(unsigned int, sz, CHUNK_SIZE))
|
||||
return FALSE;
|
||||
if (sz > 1024) {
|
||||
sz = 1024;
|
||||
if (sz > CHUNK_SIZE) {
|
||||
sz = CHUNK_SIZE;
|
||||
im->img.decode_data_pos++;
|
||||
im->img.decode_pos--;
|
||||
} else {
|
||||
|
|
@ -2713,11 +2744,11 @@ static bool_t fm_read_track(struct image *im)
|
|||
}
|
||||
case 2: /* Data */ {
|
||||
uint16_t sec_sz = sec_sz(sec->n);
|
||||
sec_sz -= im->img.decode_data_pos * 1024;
|
||||
if (bc_space < min_t(unsigned int, sec_sz, 1024))
|
||||
sec_sz -= im->img.decode_data_pos * CHUNK_SIZE;
|
||||
if (bc_space < min_t(unsigned int, sec_sz, CHUNK_SIZE))
|
||||
return FALSE;
|
||||
if (sec_sz > 1024) {
|
||||
sec_sz = 1024;
|
||||
if (sec_sz > CHUNK_SIZE) {
|
||||
sec_sz = CHUNK_SIZE;
|
||||
im->img.decode_data_pos++;
|
||||
im->img.decode_pos--;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ static bool_t qd_open(struct image *im)
|
|||
im->qd.tb = 1;
|
||||
im->nr_cyls = 1;
|
||||
im->nr_sides = 1;
|
||||
im->write_bc_ticks = sampleclk_us(4) + 66; /* 4.917us */
|
||||
im->write_bc_ticks = sampleclk_ns(4917); /* 4.917us */
|
||||
im->ticks_per_cell = im->write_bc_ticks;
|
||||
im->sync = SYNC_none;
|
||||
|
||||
|
|
@ -159,11 +159,11 @@ static uint16_t qd_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
|
|||
uint32_t bc_c = bc->cons, bc_p = bc->prod, bc_mask = bc->len - 1;
|
||||
uint32_t ticks = im->ticks_since_flux;
|
||||
uint32_t ticks_per_cell = im->ticks_per_cell;
|
||||
uint32_t y = 8, todo = nr;
|
||||
uint32_t bit_off, todo = nr;
|
||||
uint8_t x;
|
||||
|
||||
while ((uint32_t)(bc_p - bc_c) >= 8) {
|
||||
ASSERT(y == 8);
|
||||
|
||||
if (im->cur_bc >= im->tracklen_bc) {
|
||||
ASSERT(im->cur_bc == im->tracklen_bc);
|
||||
im->tracklen_ticks = im->cur_ticks;
|
||||
|
|
@ -172,28 +172,43 @@ static uint16_t qd_rdata_flux(struct image *im, uint16_t *tbuf, uint16_t nr)
|
|||
bc_c = (bc_c + 512*8-1) & ~(512*8-1);
|
||||
continue;
|
||||
}
|
||||
y = bc_c % 8;
|
||||
x = bc_b[(bc_c/8) & bc_mask] >> y;
|
||||
bc_c += 8 - y;
|
||||
im->cur_bc += 8 - y;
|
||||
im->cur_ticks += (8 - y) * ticks_per_cell;
|
||||
while (y < 8) {
|
||||
y++;
|
||||
|
||||
bit_off = bc_c % 8;
|
||||
x = bc_b[(bc_c/8) & bc_mask] >> bit_off;
|
||||
bc_c += 8 - bit_off;
|
||||
im->cur_bc += 8 - bit_off;
|
||||
im->cur_ticks += (8 - bit_off) * ticks_per_cell;
|
||||
|
||||
while (bit_off < 8) {
|
||||
bit_off++;
|
||||
ticks += ticks_per_cell;
|
||||
if (x & 1) {
|
||||
*tbuf++ = ticks - 1;
|
||||
ticks = 0;
|
||||
if (!--todo)
|
||||
if (!--todo) {
|
||||
bc_c -= 8 - bit_off;
|
||||
im->cur_bc -= 8 - bit_off;
|
||||
im->cur_ticks -= (8 - bit_off) * ticks_per_cell;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
x >>= 1;
|
||||
}
|
||||
|
||||
/* Subdivide a long flux gap to avoid overflowing the 16-bit timer.
|
||||
* This mishandles long No Flux Areas slightly, by regularly emitting
|
||||
* a flux-reversal pulse every 2^14 sampleclk ticks. */
|
||||
if (unlikely((ticks >> 15) != 0)) {
|
||||
*tbuf++ = (1u << 14) - 1;
|
||||
ticks -= 1u << 14;
|
||||
if (!--todo)
|
||||
goto out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
bc->cons = bc_c - (8 - y);
|
||||
im->cur_bc -= 8 - y;
|
||||
im->cur_ticks -= (8 - y) * ticks_per_cell;
|
||||
bc->cons = bc_c;
|
||||
im->ticks_since_flux = ticks;
|
||||
return nr - todo;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
static volatile time_t time_stamp;
|
||||
static struct timer time_stamp_timer;
|
||||
|
||||
/* Timestamp needs to be updated ever 2^24 systicks. We aim to update
|
||||
/* Hardware systick timer overflows every 2^24 ticks. We aim to update
|
||||
* the timestamp at twice that rate (2^23 systicks). */
|
||||
#define TIME_UPDATE_MS ((1u<<23)/(STK_MHZ*1000))
|
||||
#define TIME_UPDATE_PERIOD time_stk(1u<<23)
|
||||
|
||||
void delay_from(time_t t, unsigned int ticks)
|
||||
{
|
||||
|
|
@ -27,7 +27,7 @@ static void time_stamp_update(void *unused)
|
|||
{
|
||||
time_t now = time_now();
|
||||
time_stamp = ~now;
|
||||
timer_set(&time_stamp_timer, now + time_ms(TIME_UPDATE_MS));
|
||||
timer_set(&time_stamp_timer, now + TIME_UPDATE_PERIOD);
|
||||
}
|
||||
|
||||
time_t time_now(void)
|
||||
|
|
@ -45,7 +45,7 @@ void time_init(void)
|
|||
timers_init();
|
||||
time_stamp = stk_now();
|
||||
timer_init(&time_stamp_timer, time_stamp_update, NULL);
|
||||
timer_set(&time_stamp_timer, time_now() + time_ms(TIME_UPDATE_MS));
|
||||
timer_set(&time_stamp_timer, time_now() + TIME_UPDATE_PERIOD);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ static void reprogram_timer(int32_t delta)
|
|||
* fine-grained deadline. */
|
||||
tim->psc = sysclk_us(100)-1;
|
||||
tim->arr = min_t(uint32_t, 0xffffu,
|
||||
delta/time_us(100)-50); /* 5ms early */
|
||||
delta/time_us(100)-10); /* 1ms early */
|
||||
}
|
||||
tim->egr = TIM_EGR_UG; /* update CNT, PSC, ARR */
|
||||
tim->sr = 0; /* dummy write, gives hardware time to process EGR.UG=1 */
|
||||
|
|
|
|||
|
|
@ -17,41 +17,47 @@ static bool_t msc_device_connected;
|
|||
extern USB_OTG_CORE_HANDLE USB_OTG_Core;
|
||||
USBH_HOST USB_Host;
|
||||
|
||||
#if 1
|
||||
#define TRC_FUNC() printk("> %s\n", __FUNCTION__)
|
||||
#else
|
||||
#define TRC_FUNC() ((void)0)
|
||||
#endif
|
||||
|
||||
static void USBH_USR_Init(void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
}
|
||||
|
||||
static void USBH_USR_DeInit(void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
msc_device_connected = FALSE;
|
||||
}
|
||||
|
||||
static void USBH_USR_DeviceAttached(void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
}
|
||||
|
||||
static void USBH_USR_ResetDevice(void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
}
|
||||
|
||||
static void USBH_USR_DeviceDisconnected (void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
msc_device_connected = FALSE;
|
||||
}
|
||||
|
||||
static void USBH_USR_OverCurrentDetected (void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
}
|
||||
|
||||
static void USBH_USR_DeviceSpeedDetected(uint8_t DeviceSpeed)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
printk("> Device speed: %s\n",
|
||||
(DeviceSpeed == HPRT0_PRTSPD_HIGH_SPEED) ? "High" :
|
||||
(DeviceSpeed == HPRT0_PRTSPD_FULL_SPEED) ? "Full" :
|
||||
|
|
@ -61,14 +67,14 @@ static void USBH_USR_DeviceSpeedDetected(uint8_t DeviceSpeed)
|
|||
static void USBH_USR_DeviceDescAvailable(void *DeviceDesc)
|
||||
{
|
||||
USBH_DevDesc_TypeDef *hs = DeviceDesc;
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
printk(" VID : %04X\n", hs->idVendor);
|
||||
printk(" PID : %04X\n", hs->idProduct);
|
||||
}
|
||||
|
||||
static void USBH_USR_DeviceAddressAssigned(void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
}
|
||||
|
||||
static void USBH_USR_ConfigurationDescAvailable(
|
||||
|
|
@ -78,7 +84,7 @@ static void USBH_USR_ConfigurationDescAvailable(
|
|||
{
|
||||
USBH_InterfaceDesc_TypeDef *id = itfDesc;
|
||||
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
printk("> Class connected: %02x (%s)\n",
|
||||
id->bInterfaceClass,
|
||||
(id->bInterfaceClass == 0x08) ? "MSC" :
|
||||
|
|
@ -102,12 +108,12 @@ static void USBH_USR_SerialNumString(void *SerialNumString)
|
|||
|
||||
static void USBH_USR_EnumerationDone(void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
}
|
||||
|
||||
static USBH_USR_Status USBH_USR_UserInput(void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
return USBH_USR_RESP_OK;
|
||||
}
|
||||
|
||||
|
|
@ -120,12 +126,12 @@ static int USBH_USR_UserApplication(void)
|
|||
|
||||
static void USBH_USR_DeviceNotSupported(void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
}
|
||||
|
||||
static void USBH_USR_UnrecoveredError (void)
|
||||
{
|
||||
printk("> %s\n", __FUNCTION__);
|
||||
TRC_FUNC();
|
||||
msc_device_connected = FALSE;
|
||||
}
|
||||
|
||||
|
|
|
|||
12
src/util.c
12
src/util.c
|
|
@ -335,6 +335,18 @@ unsigned int popcount(uint32_t x)
|
|||
return (((x + (x >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24;
|
||||
}
|
||||
|
||||
|
||||
/* 64:32->32q division requiring 32:32->64 multiply. Cortex M3+ */
|
||||
uint32_t udiv64(uint64_t dividend, uint32_t divisor)
|
||||
{
|
||||
uint32_t x, q = 0;
|
||||
for (x = 1u<<31; x != 0; x >>= 1) {
|
||||
if (((uint64_t)(q|x)*divisor) <= dividend)
|
||||
q |= x;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
|
|
|
|||
Loading…
Reference in a new issue