diff --git a/lib/adafruit_floppy b/lib/adafruit_floppy index e36a6127b957a..a20190fb04d14 160000 --- a/lib/adafruit_floppy +++ b/lib/adafruit_floppy @@ -1 +1 @@ -Subproject commit e36a6127b957ab2f602e031ba3583de9c571582e +Subproject commit a20190fb04d14197dcc2b578d54ce0ba9223f525 diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 17e7e1f36655d..cb370d0ede9cc 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -508,6 +508,7 @@ msgstr "" msgid "All event channels in use" msgstr "" +#: ports/raspberrypi/common-hal/floppyio/__init__.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c @@ -4029,6 +4030,15 @@ msgstr "" msgid "timeout must be < 655.35 secs" msgstr "" +#: ports/raspberrypi/common-hal/floppyio/__init__.c +msgid "timeout waiting for flux" +msgstr "" + +#: ports/raspberrypi/common-hal/floppyio/__init__.c +#: shared-module/floppyio/__init__.c +msgid "timeout waiting for index pulse" +msgstr "" + #: shared-module/sdcardio/SDCard.c msgid "timeout waiting for v1 card" msgstr "" diff --git a/ports/atmel-samd/common-hal/floppyio/__init__.c b/ports/atmel-samd/common-hal/floppyio/__init__.c new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/raspberrypi/boards/adafruit_floppsy_rp2040/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_floppsy_rp2040/mpconfigboard.mk index 2cd4e0bb8bb5f..b2264a0e58962 100644 --- a/ports/raspberrypi/boards/adafruit_floppsy_rp2040/mpconfigboard.mk +++ b/ports/raspberrypi/boards/adafruit_floppsy_rp2040/mpconfigboard.mk @@ -7,3 +7,6 @@ CHIP_VARIANT = RP2040 CHIP_FAMILY = rp2 EXTERNAL_FLASH_DEVICES = "GD25Q64C,W25Q64JVxQ,W25Q128JV" + +CIRCUITPY_USB_HOST = 0 +CIRCUITPY_PICODVI = 0 diff --git a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c index d30225f017c27..179717144532b 100644 --- a/ports/raspberrypi/common-hal/audiobusio/I2SOut.c +++ b/ports/raspberrypi/common-hal/audiobusio/I2SOut.c @@ -175,10 +175,10 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, sideset_pin = bit_clock; if (left_justified) { - program_len = sizeof(i2s_program_left_justified) / sizeof(i2s_program_left_justified[0]); + program_len = MP_ARRAY_SIZE(i2s_program_left_justified); program = i2s_program_left_justified; } else { - program_len = sizeof(i2s_program) / sizeof(i2s_program[0]); + program_len = MP_ARRAY_SIZE(i2s_program); program = i2s_program; } @@ -186,10 +186,10 @@ void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, sideset_pin = word_select; if (left_justified) { - program_len = sizeof(i2s_program_left_justified_swap) / sizeof(i2s_program_left_justified_swap[0]); + program_len = MP_ARRAY_SIZE(i2s_program_left_justified_swap); program = i2s_program_left_justified_swap; } else { - program_len = sizeof(i2s_program_swap) / sizeof(i2s_program_swap[0]); + program_len = MP_ARRAY_SIZE(i2s_program_swap); program = i2s_program_swap; } diff --git a/ports/raspberrypi/common-hal/floppyio/__init__.c b/ports/raspberrypi/common-hal/floppyio/__init__.c new file mode 100644 index 0000000000000..9a117d9329672 --- /dev/null +++ b/ports/raspberrypi/common-hal/floppyio/__init__.c @@ -0,0 +1,195 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "bindings/rp2pio/StateMachine.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/floppyio/__init__.h" +#include "common-hal/floppyio/__init__.h" +#include "shared-bindings/time/__init__.h" +#include "supervisor/shared/tick.h" + +static const uint16_t fluxread_program[] = { + // ; Count flux pulses and watch for index pin + // ; flux input is the 'jmp pin'. index is "pin zero". + // ; Counts are in units 3 / F_pio, so e.g., at 30MHz 1 count = 0.1us + // ; Count down while waiting for the counter to go HIGH + // ; The only counting is down, so C code will just have to negate the + // count! + // ; Each 'wait one' loop takes 3 instruction-times + // wait_one: + 0x0041, // jmp x--, wait_one_next ; acts as a non-conditional decrement + // of x + // wait_one_next: + 0x00c3, // jmp pin wait_zero + 0x0000, // jmp wait_one + // ; Each 'wait zero' loop takes 3 instruction-times, needing one + // instruction delay + // ; (it has to match the 'wait one' timing exactly) + // wait_zero: + 0x0044, // jmp x--, wait_zero_next ; acts as a non-conditional decrement + // of x + // wait_zero_next: + 0x01c3, // jmp pin wait_zero [1] + // ; Top bit is index status, bottom 15 bits are inverse of counts + // ; Combined FIFO gives 16 entries (8 32-bit entries) so with the + // ; smallest plausible pulse of 2us there are 250 CPU cycles available + // @125MHz + 0x4001, // in pins, 1 + 0x402f, // in x, 15 + // ; Three cycles for the end of loop, so we need to decrement x to make + // everything + // ; come out right. This has constant timing whether we actually jump back + // vs wrapping. + 0x0040, // jmp x--, wait_one +}; + +typedef struct { + PIO pio; + uint8_t sm; + bool word_available; + uint16_t half; +} floppy_reader; + +static bool data_available(floppy_reader *reader) { + return reader->word_available || !pio_sm_is_rx_fifo_empty(reader->pio, reader->sm); +} + +static uint16_t read_fifo(floppy_reader *reader) { + if (reader->word_available) { + reader->word_available = false; + return reader->half; + } + uint32_t value = pio_sm_get_blocking(reader->pio, reader->sm); + reader->half = value >> 16; + reader->word_available = true; + return value & 0xffff; +} + + +int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index, mp_int_t index_wait_ms) { +#define READ_INDEX() (!!(*index_port & index_mask)) + uint32_t index_mask; + volatile uint32_t *index_port = common_hal_digitalio_digitalinout_get_reg(index, DIGITALINOUT_REG_READ, &index_mask); + + memset(buf, 0, len); + + uint32_t pins_we_use = 1 << data->pin->number; + + rp2pio_statemachine_obj_t state_machine; + bool ok = rp2pio_statemachine_construct(&state_machine, + fluxread_program, MP_ARRAY_SIZE(fluxread_program), + FLOPPYIO_SAMPLERATE * 3, // 3 PIO cycles per sample count + NULL, 0, // init program + NULL, 0, // out + index->pin, 1, // in + 1, 0, // in pull up/down + NULL, 0, // set + NULL, 0, // sideset + 0, 0, // initial pin state + data->pin, // jump pin + pins_we_use, false, true, + true, 32, false, // TX setting we don't use + true, // Wait for txstall. If we don't, then we'll deinit too quickly. + true, 32, true, // move 32 bits at a time + false, // claim pins + false, // Not user-interruptible. + false, // No sideset enable + 0, -1, // wrap + PIO_ANY_OFFSET // offset + ); + if (!ok) { + mp_raise_RuntimeError(MP_ERROR_TEXT("All state machines in use")); + } + + floppy_reader reader = { .pio = state_machine.pio, .sm = state_machine.state_machine, }; + + uint8_t *ptr = buf, *end = ptr + len; + + uint64_t index_deadline_us = time_us_64() + index_wait_ms * 1000; + + common_hal_mcu_disable_interrupts(); + + // check if flux is arriving + uint64_t flux_deadline_us = time_us_64() + 20; + while (pio_sm_is_rx_fifo_empty(reader.pio, reader.sm)) { + if (time_us_64() > flux_deadline_us) { + common_hal_mcu_enable_interrupts(); + common_hal_rp2pio_statemachine_deinit(&state_machine); + mp_raise_RuntimeError(MP_ERROR_TEXT("timeout waiting for flux")); + } + } + + // wait for index pulse low + while (READ_INDEX()) { + if (time_us_64() > index_deadline_us) { + common_hal_mcu_enable_interrupts(); + common_hal_rp2pio_statemachine_deinit(&state_machine); + mp_raise_RuntimeError(MP_ERROR_TEXT("timeout waiting for index pulse")); + } + } + + pio_sm_clear_fifos(reader.pio, reader.sm); + + // if another index doesn't show up ... + index_deadline_us = time_us_64() + index_wait_ms * 1000; + + int last = read_fifo(&reader); + bool last_index = READ_INDEX(); + while (ptr != end) { + + /* Handle index */ + bool now_index = READ_INDEX(); + + if (!now_index && last_index) { + break; + } + last_index = now_index; + + if (!data_available(&reader)) { + // no flux is arriving? is ANY flux arriving or has a full revoulution gone by? + if (time_us_64() > index_deadline_us) { + break; + } + continue; + } + + int timestamp = read_fifo(&reader); + int delta = last - timestamp; + if (delta < 0) { + delta += 65536; + } + delta /= 2; + + last = timestamp; + *ptr++ = delta > 255 ? 255 : delta; + } + + common_hal_mcu_enable_interrupts(); + common_hal_rp2pio_statemachine_deinit(&state_machine); + + return ptr - (uint8_t *)buf; +} diff --git a/ports/raspberrypi/common-hal/neopixel_write/__init__.c b/ports/raspberrypi/common-hal/neopixel_write/__init__.c index c4a2282bdf7f8..6a179270435e4 100644 --- a/ports/raspberrypi/common-hal/neopixel_write/__init__.c +++ b/ports/raspberrypi/common-hal/neopixel_write/__init__.c @@ -62,7 +62,7 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout, // change the pins then though. uint32_t pins_we_use = 1 << digitalinout->pin->number; bool ok = rp2pio_statemachine_construct(&state_machine, - neopixel_program, sizeof(neopixel_program) / sizeof(neopixel_program[0]), + neopixel_program, MP_ARRAY_SIZE(neopixel_program), 12800000, // 12.8MHz, to get appropriate sub-bit times in PIO program. NULL, 0, // init program NULL, 1, // out diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index ee8a395e87ac9..4e002867ccd80 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -40,6 +40,7 @@ SRC_BITMAP := \ shared-bindings/displayio/Bitmap.c \ shared-bindings/displayio/ColorConverter.c \ shared-bindings/displayio/Palette.c \ + shared-bindings/floppyio/__init__.c \ shared-bindings/jpegio/__init__.c \ shared-bindings/jpegio/JpegDecoder.c \ shared-bindings/locale/__init__.c \ @@ -69,6 +70,7 @@ SRC_BITMAP := \ shared-module/displayio/Bitmap.c \ shared-module/displayio/ColorConverter.c \ shared-module/displayio/Palette.c \ + shared-module/floppyio/__init__.c \ shared-module/jpegio/__init__.c \ shared-module/jpegio/JpegDecoder.c \ shared-module/os/getenv.c \ @@ -94,6 +96,7 @@ CFLAGS += \ -DCIRCUITPY_BITMAPTOOLS=1 \ -DCIRCUITPY_CODEOP=1 \ -DCIRCUITPY_DISPLAYIO_UNIX=1 \ + -DCIRCUITPY_FLOPPYIO=1 \ -DCIRCUITPY_FUTURE=1 \ -DCIRCUITPY_GIFIO=1 \ -DCIRCUITPY_JPEGIO=1 \ diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index e83baae68f5bd..70003d4fe54ce 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -478,6 +478,7 @@ SRC_COMMON_HAL_ALL = \ dotclockframebuffer/DotClockFramebuffer.c \ dotclockframebuffer/__init__.c \ dualbank/__init__.c \ + floppyio/__init__.c \ frequencyio/FrequencyIn.c \ frequencyio/__init__.c \ imagecapture/ParallelImageCapture.c \ diff --git a/shared-bindings/floppyio/__init__.c b/shared-bindings/floppyio/__init__.c index fa45cd53a8a1f..4338fbf6158a5 100644 --- a/shared-bindings/floppyio/__init__.c +++ b/shared-bindings/floppyio/__init__.c @@ -25,9 +25,14 @@ */ #include "shared-bindings/floppyio/__init__.h" +#if CIRCUITPY_DIGITALIO #include "shared-bindings/digitalio/DigitalInOut.h" #include "common-hal/floppyio/__init__.h" +#else +#define FLOPPYIO_SAMPLERATE (24000000) +#endif +#include #include #include "py/binary.h" @@ -35,7 +40,10 @@ #include "py/runtime.h" //| def flux_readinto( -//| buffer: WriteableBuffer, data: digitalio.DigitalInOut, index: digitalio.DigitalInOut +//| buffer: WriteableBuffer, +//| data: digitalio.DigitalInOut, +//| index: digitalio.DigitalInOut, +//| index_wait=0.220, //| ) -> int: //| """Read flux transition information into the buffer. //| @@ -47,67 +55,128 @@ //| :param buffer: Read data into this buffer. Each element represents the time between successive zero-to-one transitions. //| :param data: Pin on which the flux data appears //| :param index: Pin on which the index pulse appears +//| :param index_wait: Time to wait, in seconds, for the index pulse //| :return: The actual number of bytes of read //| """ //| ... //| STATIC mp_obj_t floppyio_flux_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_buffer, ARG_data, ARG_index }; + #if CIRCUITPY_DIGITALIO + enum { ARG_buffer, ARG_data, ARG_index, ARG_index_wait }; static const mp_arg_t allowed_args[] = { { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_index_wait, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); + digitalio_digitalinout_obj_t *data = assert_digitalinout(args[ARG_data].u_obj); digitalio_digitalinout_obj_t *index = assert_digitalinout(args[ARG_index].u_obj); - return MP_OBJ_NEW_SMALL_INT(common_hal_floppyio_flux_readinto(bufinfo.buf, bufinfo.len, data, index)); + mp_int_t index_wait_ms = args[ARG_index_wait].u_obj ? + MICROPY_FLOAT_C_FUN(round)(mp_arg_validate_type_float(args[ARG_index_wait].u_obj, MP_QSTR_index_wait) * 1000) : + 220; + + return MP_OBJ_NEW_SMALL_INT(common_hal_floppyio_flux_readinto(bufinfo.buf, bufinfo.len, data, index, index_wait_ms)); + #else + mp_raise_NotImplementedError(NULL); + #endif } MP_DEFINE_CONST_FUN_OBJ_KW(floppyio_flux_readinto_obj, 0, floppyio_flux_readinto); //| def mfm_readinto( -//| buffer: WriteableBuffer, data: digitalio.DigitalInOut, index: digitalio.DigitalInOut +//| buffer: WriteableBuffer, +//| flux: ReadableBuffer, +//| flux_t2_max: int, +//| flux_t3_max: int, +//| validity: bytearray | None = None, +//| clear_validity: bool = True, //| ) -> int: -//| """Read mfm blocks into the buffer. +//| """Decode MFM flux into the buffer //| //| The track is assumed to consist of 512-byte sectors. //| -//| The function returns when all sectors have been successfully read, or -//| a number of index pulses have occurred. Due to technical limitations, this -//| process may not be interruptible by KeyboardInterrupt. +//| The function returns the number of sectors successfully read. In addition, +//| it updates the ``validity`` buffer with information about which sectors +//| were read. //| -//| :param buffer: Read data into this buffer. Must be a multiple of 512. -//| :param data: Pin on which the mfm data appears -//| :param index: Pin on which the index pulse appears +//| MFM encoding uses pulses of 3 widths, "T2", "T3" and "T4". +//| A 1440KiB disk in standard MFM format has "T2" pulses of 2000ns, "T3" pulses of 3000ns, +//| and "T4" pulses of 4000ns. +//| +//| Parameters ``t2_max`` and ``t3_max`` are used to distinguish these pulses. +//| A pulse with ``p <= t2_max`` is a "T2" pulse, +//| a pulse with ``t2_max < p <= t3_max`` is a "T3" pulse, +//| and a pulse with ``t3_max < p`` is a "T4" pulse. +//| +//| The following code can convert a number in nanoseconds to a number of samples +//| for a given sample rate: +//| +//| .. code-block:: py +//| +//| def ns_to_count(ns, samplerate): +//| return round(ns * samplerate * 1e-9) +//| +//| This means the following typical values are a good starting place for a 1.44MB floppy: +//| +//| .. code-block:: py +//| +//| t2_max = ns_to_count(2500, samplerate) # Mid way between T2 and T3 length +//| t3_max = ns_to_count(3500, samplerate) # Mid way between T2 and T3 length +//| +//| :param buffer: Read data into this buffer. Byte length must be a multiple of 512. +//| :param flux: Flux data from a previous `flux_readinto` call +//| :param t2_max: Maximum time of a flux cell in counts. +//| :param t3_max: Nominal time of a flux cell in counts. +//| :param validity: Optional bytearray. For each sector successfully read, the corresponding validity entry is set to ``1`` and previously valid sectors are not decoded. +//| :param clear_validity: If `True`, clear the validity information before decoding and attempt to decode all sectors. //| :return: The actual number of sectors read //| """ //| ... //| STATIC mp_obj_t floppyio_mfm_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_buffer, ARG_data, ARG_index }; + enum { ARG_buffer, ARG_flux, ARG_t2_max, ARG_t3_max, ARG_validity, ARG_clear_validity }; static const mp_arg_t allowed_args[] = { { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_flux, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_flux_t2_max, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_flux_t3_max, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_validity, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_clear_validity, MP_ARG_BOOL, {.u_bool = true } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); - digitalio_digitalinout_obj_t *data = assert_digitalinout(args[ARG_data].u_obj); - digitalio_digitalinout_obj_t *index = assert_digitalinout(args[ARG_index].u_obj); - if (bufinfo.len % 512 != 0) { mp_raise_ValueError(MP_ERROR_TEXT("Buffer must be a multiple of 512 bytes")); } size_t n_sectors = bufinfo.len / 512; - return MP_OBJ_NEW_SMALL_INT(common_hal_floppyio_mfm_readinto(bufinfo.buf, n_sectors, data, index)); + + mp_buffer_info_t bufinfo_flux; + mp_get_buffer_raise(args[ARG_flux].u_obj, &bufinfo_flux, MP_BUFFER_READ); + + mp_buffer_info_t bufinfo_validity; + uint8_t validity_buf[n_sectors]; + if (args[ARG_validity].u_obj) { + mp_get_buffer_raise(args[ARG_validity].u_obj, &bufinfo_validity, MP_BUFFER_READ); + mp_arg_validate_length_min(bufinfo_validity.len, n_sectors, MP_QSTR_validity); + if (args[ARG_clear_validity].u_bool) { + memset(validity_buf, 0, sizeof(validity_buf)); + } + } else { + bufinfo_validity.buf = &validity_buf; + bufinfo_validity.len = n_sectors; + memset(validity_buf, 0, sizeof(validity_buf)); + } + + return MP_OBJ_NEW_SMALL_INT(common_hal_floppyio_mfm_readinto(&bufinfo, &bufinfo_flux, bufinfo_validity.buf, args[ARG_t2_max].u_int, args[ARG_t3_max].u_int)); } MP_DEFINE_CONST_FUN_OBJ_KW(floppyio_mfm_readinto_obj, 0, floppyio_mfm_readinto); diff --git a/shared-bindings/floppyio/__init__.h b/shared-bindings/floppyio/__init__.h index 322bbe7d430cd..54079ee025f66 100644 --- a/shared-bindings/floppyio/__init__.h +++ b/shared-bindings/floppyio/__init__.h @@ -26,7 +26,11 @@ #pragma once +#include "py/obj.h" + +#if CIRCUITPY_DIGITALIO #include "common-hal/digitalio/DigitalInOut.h" +int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index, mp_int_t index_wait_ms); +#endif -int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index); -int common_hal_floppyio_mfm_readinto(void *buf, size_t n_sector, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index); +int common_hal_floppyio_mfm_readinto(const mp_buffer_info_t *buf, const mp_buffer_info_t *flux_buf, uint8_t *validity, size_t t2_max, size_t t3_max); diff --git a/shared-bindings/time/__init__.h b/shared-bindings/time/__init__.h index dba8229f26d0f..432736a1ec8e0 100644 --- a/shared-bindings/time/__init__.h +++ b/shared-bindings/time/__init__.h @@ -30,6 +30,7 @@ #include #include +#include "py/obj.h" #include "shared/timeutils/timeutils.h" extern mp_obj_t struct_time_from_tm(timeutils_struct_time_t *tm); diff --git a/shared-module/floppyio/__init__.c b/shared-module/floppyio/__init__.c index 6700c48078a0c..cb41dba0c3939 100644 --- a/shared-module/floppyio/__init__.c +++ b/shared-module/floppyio/__init__.c @@ -25,30 +25,29 @@ */ #include "py/runtime.h" +#include "py/mphal.h" #include "shared-bindings/time/__init__.h" #include "shared-bindings/floppyio/__init__.h" +#if CIRCUITPY_DIGITALIO #include "common-hal/floppyio/__init__.h" #include "shared-bindings/digitalio/DigitalInOut.h" - -#ifndef T2_5 -#define T2_5 (FLOPPYIO_SAMPLERATE * 5 / 2 / 1000000) -#endif -#ifndef T3_5 -#define T3_5 (FLOPPYIO_SAMPLERATE * 7 / 2 / 1000000) #endif -#define MFM_IO_MMIO (1) #include "lib/adafruit_floppy/src/mfm_impl.h" +#if CIRCUITPY_DIGITALIO +MP_WEAK __attribute__((optimize("O3"))) -int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index) { +int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index, mp_int_t index_wait_ms) { + mp_printf(&mp_plat_print, "common_hal_floppyio_flux_readinto in %s\n", __FILE__); uint32_t index_mask; volatile uint32_t *index_port = common_hal_digitalio_digitalinout_get_reg(index, DIGITALINOUT_REG_READ, &index_mask); uint32_t data_mask; volatile uint32_t *data_port = common_hal_digitalio_digitalinout_get_reg(data, DIGITALINOUT_REG_READ, &data_mask); + uint32_t index_deadline_ms = supervisor_ticks_ms32() + index_wait_ms; #undef READ_INDEX #undef READ_DATA #define READ_INDEX() (!!(*index_port & index_mask)) @@ -62,6 +61,11 @@ int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalin // wait for index pulse low while (READ_INDEX()) { /* NOTHING */ + if (supervisor_ticks_ms32() > index_deadline_ms) { + common_hal_mcu_enable_interrupts(); + mp_raise_RuntimeError(MP_ERROR_TEXT("timeout waiting for index pulse")); + return 0; + } } @@ -95,16 +99,19 @@ int common_hal_floppyio_flux_readinto(void *buf, size_t len, digitalio_digitalin return pulses_ptr - pulses; } +#endif -int common_hal_floppyio_mfm_readinto(void *buf, size_t n_sectors, digitalio_digitalinout_obj_t *data, digitalio_digitalinout_obj_t *index) { - mfm_io_t io; - io.index_port = common_hal_digitalio_digitalinout_get_reg(index, DIGITALINOUT_REG_READ, &io.index_mask); - io.data_port = common_hal_digitalio_digitalinout_get_reg(data, DIGITALINOUT_REG_READ, &io.data_mask); - - common_hal_mcu_disable_interrupts(); - uint8_t validity[n_sectors]; - int result = read_track(io, n_sectors, buf, validity); - common_hal_mcu_enable_interrupts(); +int common_hal_floppyio_mfm_readinto(const mp_buffer_info_t *buf, const mp_buffer_info_t *flux_buf, uint8_t *validity, size_t t2_max, size_t t3_max) { + mfm_io_t io = { + .T2_max = t2_max, + .T3_max = t3_max, + .pulses = flux_buf->buf, + .n_pulses = flux_buf->len, + .sectors = buf->buf, + .sector_validity = validity, + .n_sectors = buf->len / 512, + }; + int result = decode_track_mfm(&io); return result; } diff --git a/shared/timeutils/timeutils.h b/shared/timeutils/timeutils.h index 9f4b500caa7b6..13ec3a01f2b22 100644 --- a/shared/timeutils/timeutils.h +++ b/shared/timeutils/timeutils.h @@ -27,6 +27,8 @@ #ifndef MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H #define MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H +#include "mpconfigport.h" // CIRCUITPY-CHANGE for MICROPY_EPOCH_IS_1970 + // The number of seconds between 1970/1/1 and 2000/1/1 is calculated using: // time.mktime((2000,1,1,0,0,0,0,0,0)) - time.mktime((1970,1,1,0,0,0,0,0,0)) #define TIMEUTILS_SECONDS_1970_TO_2000 (946684800ULL) diff --git a/tests/circuitpython/mfm.py b/tests/circuitpython/mfm.py new file mode 100644 index 0000000000000..9839b5c756073 --- /dev/null +++ b/tests/circuitpython/mfm.py @@ -0,0 +1,79 @@ +import floppyio + +mfm_content = ( + b'HHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHH' + b'H00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH0' + b'0HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00H' + b'HHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHH' + b'H00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH0' + b'0HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00H' + b'HHH00HHHH00HHHH00HHHH00HHHH00HHHH00H00000000000000000000000000' + b'00000000000000000000000000000000000000000000000000000000000000' + b'0000000H0H`H`0H`H`0H`H`00000H0HHH00HHHH00HHHH00HHHH00HHHH00HHH' + b'H00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH0' + b'0HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00H' + b'HHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHH' + b'H00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH0' + b'0HHHH00HHHH00HHHH00H000000000000000000000000000000000000000000' + b'00000000000000000000000000000000000000000000000000000H`H`H0`H`' + b'H0`H`H0000000H0000000000000000000000HH0000H`0HH`HH0`000`HH00HH' + b'HH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH' + b'00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00HHHH00' + b'H0000000000000000000000000000000000000000000000000000000000000' + b'0000000000000000000000000000000000H`H`H0`H`H0`H`H00000`0`0H00H' + b'`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0' + b'HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HH' + b'H0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0H' + b'H0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0' + b'H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H0' + b'0H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H' + b'`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0' + b'HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH' + b'0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0' + b'H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0' + b'H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H0' + b'0H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H' + b'`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0' + b'HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HH' + b'H0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0H' + b'H0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0' + b'H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H0' + b'0H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H' + b'`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0' + b'HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH' + b'0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0' + b'H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0' + b'H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H0' + b'0H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H' + b'`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0' + b'HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HH' + b'H0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0H' + b'H0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0' + b'H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H0' + b'0H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H' + b'`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0' + b'HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH' + b'0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0' + b'H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0' + b'H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H0' + b'0H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H' + b'`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0' + b'HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HH' + b'H0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0H' + b'H0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0' + b'H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H0' + b'0H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H' + b'`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0' + b'HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH' + b'0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0' + b'H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0' + b'H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H0' + b'0H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H' + b'`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0HH0HH0H00H`0HHH0H0H00H`0' + b'HH0HH0H00H`0HHH0H0H00H`0HH0``HHHH0`0000hhhhhhhhhhhhhhhhhhhhhhh' +) + +b = bytearray(512) +r = floppyio.mfm_readinto(b, mfm_content, 60, 84) +print(r) +print(b) diff --git a/tests/circuitpython/mfm.py.exp b/tests/circuitpython/mfm.py.exp new file mode 100644 index 0000000000000..aa8f5d5f3f517 --- /dev/null +++ b/tests/circuitpython/mfm.py.exp @@ -0,0 +1,2 @@ +1 +bytearray(b'adafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadafadaf') diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 46fd6ddcf2c42..10bf6c4e49049 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -55,12 +55,12 @@ _thread aesio array audiocore audiomixer binascii bitmapfilter bitmaptools cexample cmath codeop collections cppexample displayio errno example_package -gc hashlib heapq io -jpegio json locale math -os platform qrio rainbowio -random re select struct -synthio sys time traceback -uctypes ulab zlib +floppyio gc hashlib heapq +io jpegio json locale +math os platform qrio +rainbowio random re select +struct synthio sys time +traceback uctypes ulab zlib me rainbowio random