|
28 | 28 | #include "shared-bindings/digitalio/DigitalInOut.h" |
29 | 29 | #include "common-hal/floppyio/__init__.h" |
30 | 30 |
|
| 31 | +#include <math.h> |
31 | 32 | #include <stdint.h> |
32 | 33 |
|
33 | 34 | #include "py/binary.h" |
|
52 | 53 | //| ... |
53 | 54 | //| |
54 | 55 | STATIC mp_obj_t floppyio_flux_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { |
55 | | - enum { ARG_buffer, ARG_data, ARG_index }; |
| 56 | + enum { ARG_buffer, ARG_data, ARG_index, ARG_index_wait }; |
56 | 57 | static const mp_arg_t allowed_args[] = { |
57 | 58 | { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
58 | 59 | { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
59 | 60 | { MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
| 61 | + { MP_QSTR_index_wait, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
60 | 62 | }; |
61 | 63 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; |
62 | 64 | mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); |
63 | 65 |
|
64 | 66 | mp_buffer_info_t bufinfo; |
65 | 67 | mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); |
| 68 | + |
66 | 69 | digitalio_digitalinout_obj_t *data = assert_digitalinout(args[ARG_data].u_obj); |
67 | 70 | digitalio_digitalinout_obj_t *index = assert_digitalinout(args[ARG_index].u_obj); |
68 | 71 |
|
69 | | - return MP_OBJ_NEW_SMALL_INT(common_hal_floppyio_flux_readinto(bufinfo.buf, bufinfo.len, data, index)); |
| 72 | + mp_int_t index_wait_ms = args[ARG_index_wait].u_obj ? |
| 73 | + MICROPY_FLOAT_C_FUN(round)(mp_arg_validate_type_float(args[ARG_index_wait].u_obj, MP_QSTR_index_wait) * 1000) : |
| 74 | + MICROPY_FLOAT_CONST(.220); |
| 75 | + |
| 76 | + return MP_OBJ_NEW_SMALL_INT(common_hal_floppyio_flux_readinto(bufinfo.buf, bufinfo.len, data, index, index_wait_ms)); |
70 | 77 | } |
71 | 78 | MP_DEFINE_CONST_FUN_OBJ_KW(floppyio_flux_readinto_obj, 0, floppyio_flux_readinto); |
72 | 79 |
|
73 | 80 | //| def mfm_readinto( |
74 | | -//| buffer: WriteableBuffer, data: digitalio.DigitalInOut, index: digitalio.DigitalInOut |
| 81 | +//| buffer: WriteableBuffer, |
| 82 | +//| flux: ReadableBuffer, |
| 83 | +//| flux_t1_nominal: int, |
| 84 | +//| validity: bytearray | None = None, |
| 85 | +//| clear_validity: bool = True, |
75 | 86 | //| ) -> int: |
76 | | -//| """Read mfm blocks into the buffer. |
| 87 | +//| """Decode MFM flux into the buffer |
77 | 88 | //| |
78 | 89 | //| The track is assumed to consist of 512-byte sectors. |
79 | 90 | //| |
80 | | -//| The function returns when all sectors have been successfully read, or |
81 | | -//| a number of index pulses have occurred. Due to technical limitations, this |
82 | | -//| process may not be interruptible by KeyboardInterrupt. |
| 91 | +//| The function returns the number of sectors successfully read. In addition, |
| 92 | +//| it updates the ``validity`` buffer with information about which sectors |
| 93 | +//| were read. |
83 | 94 | //| |
84 | | -//| :param buffer: Read data into this buffer. Must be a multiple of 512. |
85 | | -//| :param data: Pin on which the mfm data appears |
86 | | -//| :param index: Pin on which the index pulse appears |
| 95 | +//| MFM encoding uses pulses of 3 widths, "T2", "T3" and "T4". |
| 96 | +//| A 1440KiB disk in standard MFM format has "T2" pulses of 2000ns, "T3" pulses of 3000ns, |
| 97 | +//| and "T4" pulses of 4000ns. |
| 98 | +//| |
| 99 | +//| Parameters ``t2_max`` and ``t3_max`` are used to distinguish these pulses. |
| 100 | +//| A pulse with ``p <= t2_max`` is a "T2" pulse, |
| 101 | +//| a pulse with ``t2_max < p <= t3_max`` is a "T3" pulse, |
| 102 | +//| and a pulse with ``t3_max < p`` is a "T4" pulse. |
| 103 | +//| |
| 104 | +//| The following code can convert a number in nanoseconds to a number of samples |
| 105 | +//| for a given sample rate: |
| 106 | +//| |
| 107 | +//| . code-block:: py |
| 108 | +//| |
| 109 | +//| def ns_to_count(ns, samplerate): |
| 110 | +//| return round(ns * samplerate * 1e-9) |
| 111 | +//| |
| 112 | +//| This means the following typical values are a good starting place for a 1.44MB floppy: |
| 113 | +//| |
| 114 | +//| . code-block:: py |
| 115 | +//| |
| 116 | +//| t2_max = ns_to_count(2500, samplerate) # Mid way between T2 and T3 length |
| 117 | +//| t3_max = ns_to_count(3500, samplerate) # Mid way between T2 and T3 length |
| 118 | +//| |
| 119 | +//| :param buffer: Read data into this buffer. Byte length must be a multiple of 512. |
| 120 | +//| :param flux: Flux data from a previous `flux_readinto` call |
| 121 | +//| :param t2_max: Maximum time of a flux cell in counts. |
| 122 | +//| :param t3_max: Nominal time of a flux cell in counts. |
| 123 | +//| :param validity: Optional bytearray. For each sector successfully read, the corresponding validity entry is set to ``1`` and previously valid sectors are not decoded. |
| 124 | +//| :param clear_validity: If `True`, clear the validity information before decoding and attempt to decode all sectors. |
87 | 125 | //| :return: The actual number of sectors read |
88 | 126 | //| """ |
89 | 127 | //| ... |
90 | 128 | //| |
91 | 129 | STATIC mp_obj_t floppyio_mfm_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { |
92 | | - enum { ARG_buffer, ARG_data, ARG_index }; |
| 130 | + enum { ARG_buffer, ARG_flux, ARG_t2_max, ARG_t3_max, ARG_validity, ARG_clear_validity }; |
93 | 131 | static const mp_arg_t allowed_args[] = { |
94 | 132 | { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
95 | | - { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
96 | | - { MP_QSTR_index, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
| 133 | + { MP_QSTR_flux, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
| 134 | + { MP_QSTR_flux_t2_max, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, |
| 135 | + { MP_QSTR_flux_t3_max, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, |
| 136 | + { MP_QSTR_validity, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, |
| 137 | + { MP_QSTR_clear_validity, MP_ARG_BOOL, {.u_bool = true } }, |
97 | 138 | }; |
98 | 139 | mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; |
99 | 140 | mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); |
100 | 141 |
|
101 | 142 | mp_buffer_info_t bufinfo; |
102 | 143 | mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); |
103 | | - digitalio_digitalinout_obj_t *data = assert_digitalinout(args[ARG_data].u_obj); |
104 | | - digitalio_digitalinout_obj_t *index = assert_digitalinout(args[ARG_index].u_obj); |
105 | | - |
106 | 144 | if (bufinfo.len % 512 != 0) { |
107 | 145 | mp_raise_ValueError(MP_ERROR_TEXT("Buffer must be a multiple of 512 bytes")); |
108 | 146 | } |
109 | 147 | size_t n_sectors = bufinfo.len / 512; |
110 | | - return MP_OBJ_NEW_SMALL_INT(common_hal_floppyio_mfm_readinto(bufinfo.buf, n_sectors, data, index)); |
| 148 | + |
| 149 | + mp_buffer_info_t bufinfo_flux; |
| 150 | + mp_get_buffer_raise(args[ARG_flux].u_obj, &bufinfo_flux, MP_BUFFER_READ); |
| 151 | + |
| 152 | + mp_buffer_info_t bufinfo_validity; |
| 153 | + uint8_t validity_buf[n_sectors]; |
| 154 | + if (args[ARG_validity].u_obj) { |
| 155 | + mp_get_buffer_raise(args[ARG_validity].u_obj, &bufinfo_validity, MP_BUFFER_READ); |
| 156 | + mp_arg_validate_length_min(bufinfo_validity.len, n_sectors, MP_QSTR_validity); |
| 157 | + if (args[ARG_clear_validity].u_bool) { |
| 158 | + memset(validity_buf, 0, sizeof(validity_buf)); |
| 159 | + } |
| 160 | + } else { |
| 161 | + bufinfo_validity.buf = &validity_buf; |
| 162 | + bufinfo_validity.len = n_sectors; |
| 163 | + memset(validity_buf, 0, sizeof(validity_buf)); |
| 164 | + } |
| 165 | + |
| 166 | + 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)); |
111 | 167 | } |
112 | 168 | MP_DEFINE_CONST_FUN_OBJ_KW(floppyio_mfm_readinto_obj, 0, floppyio_mfm_readinto); |
113 | 169 |
|
|
0 commit comments