diff --git a/data/webui/device_specific/BUILD_APPLE/autorun.po b/data/webui/device_specific/BUILD_APPLE/autorun.po index dda46a0db..7694c7b12 100644 Binary files a/data/webui/device_specific/BUILD_APPLE/autorun.po and b/data/webui/device_specific/BUILD_APPLE/autorun.po differ diff --git a/lib/bus/iwm/iwm.cpp b/lib/bus/iwm/iwm.cpp index 5bcb2eba3..cee573742 100644 --- a/lib/bus/iwm/iwm.cpp +++ b/lib/bus/iwm/iwm.cpp @@ -441,6 +441,8 @@ void IRAM_ATTR iwmBus::service() // process smartport before diskII if (!serviceSmartPort()) serviceDiskII(); + + serviceDiskIIWrite(); } // Returns true if SmartPort was handled @@ -568,7 +570,8 @@ bool IRAM_ATTR iwmBus::serviceDiskII() if (iwm_drive_enabled() == iwm_enable_state_t::on) { IWM_ACTIVE_DISK2->change_track(0); // copy current track in for this drive - diskii_xface.start(diskii_xface.iwm_enable_states() - 1); // start it up + diskii_xface.start(diskii_xface.iwm_enable_states() - 1, + IWM_ACTIVE_DISK2->readonly); // start it up } } // make a call to start the RMT stream else @@ -601,6 +604,93 @@ bool IRAM_ATTR iwmBus::serviceDiskII() return true; } +// Returns true if a Disk II write was received +bool IRAM_ATTR iwmBus::serviceDiskIIWrite() +{ + iwm_write_data item; + int sector_num; + uint8_t *decoded; + size_t decode_len; + size_t sector_start, sector_end; + bool found_start, found_end; + size_t bitlen, used; + + + if (!xQueueReceive(diskii_xface.iwm_write_queue, &item, 0)) + return false; + + Debug_printf("\r\nDisk II iwm queue receive %u %u %u %u", + item.length, item.track_begin, item.track_end, item.track_numbits); + // gap 1 = 16 * 10 + // sector header = 10 * 8 [D5 AA 96] + 4 + [DE AA EB] + // gap 2 = 7 * 10 + // sector data = (6 + 343) * 8 [D5 AA AD] + 343 + [DE AA EB] + // gap 3 = 16 * 10 + // per sector bits = 3102 + + // Take advantage of fixed sector positions of mediaTypeDSK serialise_track() + // (as listed above) + sector_num = (item.track_begin - 16 * 10) / 3102; + + bitlen = (item.track_end + item.track_numbits - item.track_begin) % item.track_numbits; + Debug_printf("\r\nDisk II write Qtrack/sector: %i/%i bit_len: %i", + item.quarter_track, sector_num, bitlen); + decoded = (uint8_t *) malloc(item.length); + decode_len = diskii_xface.iwm_decode_buffer(item.buffer, item.length, + smartport.f_spirx, 100, decoded, &used); + Debug_printf("\r\nDisk II used: %u", used); + + // Find start of sector: D5 AA AD + for (sector_start = 0; sector_start <= decode_len - 349; sector_start++) + if (decoded[sector_start] == 0xD5 + && decoded[sector_start+1] == 0xAA + && decoded[sector_start+2] == 0xAD) + break; + found_start = sector_start <= decode_len - 349; + + // Find end of sector too: DE AA EB + for (sector_end = 0; sector_end <= decode_len - 3; sector_end++) + if (decoded[sector_end] == 0xDE + && decoded[sector_end+1] == 0xAA + && decoded[sector_end+2] == 0xEB) + break; + found_end = sector_end <= decode_len - 3; + + if (!found_start && found_end) { + Debug_printf("\r\nDisk II no prologue found"); +#if 0 + sector_start = sector_end - 346; + found_start = true; +#endif + } + + if (found_start && found_end && sector_end - sector_start == 346) { + uint8_t sector_data[343]; // Need enough room to demap and de-xor + uint16_t checksum; + const int phys2log[] = {0, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15}; + + + Debug_printf("\r\nDisk II sector data: %i", sector_start + 3); + checksum = decode_6_and_2(sector_data, &decoded[sector_start + 3]); + if ((checksum >> 8) != (checksum & 0xff)) + Debug_printf("\r\nDisk II checksum mismatch: %04x", checksum); + + iwmDisk2 *disk_dev = IWM_ACTIVE_DISK2; + disk_dev->write_sector(item.quarter_track, phys2log[sector_num], sector_data); + disk_dev->change_track(0); + } + else { + Debug_printf("\r\nDisk II sector not found"); + } + + // FIXME - is there another sector to decode? + + free(decoded); + free(item.buffer); + + return true; +} + #ifndef DEV_RELAY_SLIP iwm_enable_state_t IRAM_ATTR iwmBus::iwm_drive_enabled() { diff --git a/lib/bus/iwm/iwm.h b/lib/bus/iwm/iwm.h index 947c0724e..97ff9771f 100644 --- a/lib/bus/iwm/iwm.h +++ b/lib/bus/iwm/iwm.h @@ -322,6 +322,7 @@ class iwmBus void service(); bool serviceSmartPort(); bool serviceDiskII(); + bool serviceDiskIIWrite(); void shutdown(); int numDevices(); diff --git a/lib/bus/iwm/iwm_ll.cpp b/lib/bus/iwm/iwm_ll.cpp index c0ffb8bf5..e37b01ecf 100644 --- a/lib/bus/iwm/iwm_ll.cpp +++ b/lib/bus/iwm/iwm_ll.cpp @@ -4,8 +4,8 @@ #include -#include "esp_rom_gpio.h" -#include "soc/spi_periph.h" +#include +#include #include "iwm_ll.h" #include "iwm.h" @@ -15,6 +15,7 @@ #include "fnHardwareTimer.h" #include "../../include/debug.h" #include "led.h" +#include "spi_continuous.h" #define MHZ (1000*1000) #define CELL_US 4 // microseconds @@ -22,6 +23,13 @@ #define IWM_BITLEN_FOR_BYTES(bytecount, freq, buffer) ({ \ (bytecount) * 8 * IWM_SAMPLES_PER_CELL(freq); \ }) +#define IWM_NUMBYTES_FOR_BITS(bitcount, buffer) ({ \ + size_t bufbits = sizeof(*(buffer)) * 8; \ + size_t blen = (bitcount) * \ + IWM_SAMPLES_PER_CELL(smartport.f_spirx); \ + blen = (blen + bufbits - 1) / bufbits; \ + blen; \ + }) volatile uint8_t _phases = 0; volatile sp_cmd_state_t sp_command_mode = sp_cmd_state_t::standby; @@ -297,6 +305,21 @@ uint8_t iwm_ll::iwm_decode_byte(uint8_t *src, size_t src_size, unsigned int samp return byte; } +size_t iwm_ll::iwm_decode_buffer(uint8_t *src, size_t src_size, unsigned int sample_frequency, + int timeout, uint8_t *dest, size_t *used) +{ + bool more_data = true; + size_t offset = 0; + uint8_t *output; + + + for (output = dest, offset = 0; more_data; output++) + *output = iwm_decode_byte(src, src_size, sample_frequency, timeout, &offset, &more_data); + + *used = (offset + 7) / 8; + return output - dest; +} + int IRAM_ATTR iwm_sp_ll::iwm_read_packet_spi(int packet_len) { return iwm_read_packet_spi(packet_buffer, packet_len); @@ -525,7 +548,7 @@ void iwm_sp_ll::setup_spi() spirx_mosi_pin = SP_RDDATA; // SPI for receiving packets - sprirx - bus_cfg = { + spi_bus_config_t bus_cfg = { .mosi_io_num = spirx_mosi_pin, .miso_io_num = SP_WRDATA, .sclk_io_num = -1, @@ -625,7 +648,10 @@ void iwm_ll::setup_gpio() fnSystem.set_pin_mode(SP_PHI2, gpio_mode_t::GPIO_MODE_INPUT, SystemManager::pull_updown_t::PULL_NONE, gpio_int_type_t::GPIO_INTR_ANYEDGE); fnSystem.set_pin_mode(SP_PHI3, gpio_mode_t::GPIO_MODE_INPUT, SystemManager::pull_updown_t::PULL_NONE, gpio_int_type_t::GPIO_INTR_ANYEDGE); - fnSystem.set_pin_mode(SP_WREQ, gpio_mode_t::GPIO_MODE_INPUT); + fnSystem.set_pin_mode(SP_WREQ, gpio_mode_t::GPIO_MODE_INPUT, + SystemManager::pull_updown_t::PULL_UP, + gpio_int_type_t::GPIO_INTR_ANYEDGE); + fnSystem.set_pin_mode(SP_DRIVE1, gpio_mode_t::GPIO_MODE_INPUT); fnSystem.set_pin_mode(SP_DRIVE2, gpio_mode_t::GPIO_MODE_INPUT, SystemManager::pull_updown_t::PULL_UP); fnSystem.set_pin_mode(SP_EN35, gpio_mode_t::GPIO_MODE_INPUT); @@ -823,8 +849,86 @@ void iwm_diskii_ll::set_output_to_low() #define RMT_TX_CHANNEL rmt_channel_t::RMT_CHANNEL_0 #define RMT_USEC (APB_CLK_FREQ / MHZ) -void iwm_diskii_ll::start(uint8_t drive) +// enable/disable capturing write signal from Disk II +void IRAM_ATTR diskii_write_handler_forwarder(void *arg) +{ + iwm_diskii_ll *d2i = (iwm_diskii_ll *) arg; + d2i->diskii_write_handler(); + return; +} + +void IRAM_ATTR iwm_diskii_ll::diskii_write_handler() +{ + bool doCapture = !IWM_BIT(SP_WREQ); + + + //Debug_printf("\r\nDisk II write state: %i", doCapture); + + if (doCapture) { + d2w_begin = track_location; + d2w_position = cspi_current_pos(smartport.spirx); + d2w_writing = true; + } + else if (d2w_writing) { + BaseType_t woken; + iwm_write_data item = { + .quarter_track = IWM_ACTIVE_DISK2->get_track_pos(), + .track_begin = d2w_begin, + .track_end = track_location, + .track_numbits = track_numbits, + .buffer = NULL, + .length = 0, + }; + + { + size_t offset; + + // Rewind pointer to make sure to get sync bytes + d2w_position = (d2w_position + d2w_buflen - D2W_CHUNK_SIZE * 2) % d2w_buflen; + + offset = cspi_current_pos(smartport.spirx); + item.length = (offset + d2w_buflen - d2w_position) % d2w_buflen; + } + + item.buffer = (decltype(item.buffer)) heap_caps_malloc(item.length, MALLOC_CAP_8BIT); + if (!item.buffer) + Debug_printf("\r\nDisk II unable to allocate buffer! %u %u %u", + item.length, item.track_begin, item.track_end); + else { + size_t end1, end2; + + + end1 = d2w_position + item.length; + end2 = 0; + if (end1 >= d2w_buflen) { + end2 = end1 - d2w_buflen; + end1 = d2w_buflen; + } + + end1 -= d2w_position; + memcpy(item.buffer, &d2w_buffer[d2w_position], end1); + if (end2) + memcpy(&item.buffer[end1], d2w_buffer, end2); + xQueueSendFromISR(iwm_write_queue, &item, &woken); + } + d2w_writing = false; + } + + return; +} + +void iwm_diskii_ll::start(uint8_t drive, bool write_protect) { + if (write_protect) + smartport.iwm_ack_set(); + else { + smartport.iwm_ack_clr(); + gpio_isr_handler_add(SP_WREQ, diskii_write_handler_forwarder, (void *) this); + + cspi_begin_continuous(smartport.spirx, d2w_desc); + d2w_started = true; + } + diskii_xface.set_output_to_rmt(); diskii_xface.enable_output(); ESP_ERROR_CHECK(fnRMT.rmt_write_bitstream(RMT_TX_CHANNEL, track_buffer, track_numbits, track_bit_period)); @@ -836,6 +940,12 @@ void iwm_diskii_ll::stop() { fnRMT.rmt_tx_stop(RMT_TX_CHANNEL); diskii_xface.disable_output(); + if (d2w_started) { + cspi_end_continuous(smartport.spirx); + d2w_started = false; + } + smartport.iwm_ack_set(); + gpio_isr_handler_remove(SP_WREQ); fnLedManager.set(LED_BUS, false); Debug_printf("\nstop diskII"); } @@ -978,11 +1088,15 @@ void IRAM_ATTR encode_rmt_bitstream(const void* src, rmt_item32_t* dest, size_t /* - * Initialize the RMT Tx channel + * Initialize the RMT Tx channel and SPI Rx channel */ void iwm_diskii_ll::setup_rmt() { -#define RMT_TX_CHANNEL rmt_channel_t::RMT_CHANNEL_0 + // SPI continuous + d2w_buflen = cspi_alloc_continuous(IWM_NUMBYTES_FOR_BITS(TRACK_LEN * 8, d2w_buffer), + D2W_CHUNK_SIZE, &d2w_buffer, &d2w_desc); + iwm_write_queue = xQueueCreate(10, sizeof(iwm_write_data)); + track_buffer = (uint8_t *)heap_caps_malloc(TRACK_LEN, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); if (track_buffer == NULL) Debug_println("could not allocate track buffer"); diff --git a/lib/bus/iwm/iwm_ll.h b/lib/bus/iwm/iwm_ll.h index 27282fa25..71a4226a6 100644 --- a/lib/bus/iwm/iwm_ll.h +++ b/lib/bus/iwm/iwm_ll.h @@ -5,6 +5,7 @@ #include // #include #include +#include #include #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) #include @@ -185,6 +186,9 @@ extern volatile sp_cmd_state_t sp_command_mode; class iwm_ll { protected: + // SPI receiver + spi_transaction_t rxtrans; + // low level bit-banging i/o functions bool iwm_req_val() { return (IWM_BIT(SP_REQ)); }; void iwm_extra_set(); @@ -195,7 +199,9 @@ class iwm_ll public: void setup_gpio(); uint8_t iwm_decode_byte(uint8_t *src, size_t src_size, unsigned int sample_frequency, - int timeout, size_t *bit_offset, bool *more_avail); + int timeout, size_t *bit_offset, bool *more_avail); + size_t iwm_decode_buffer(uint8_t *src, size_t src_size, unsigned int sample_frequency, + int timeout, uint8_t *dest, size_t *used); }; class iwm_sp_ll : public iwm_ll @@ -205,11 +211,9 @@ class iwm_sp_ll : public iwm_ll // SPI data handling uint8_t *spi_buffer = nullptr; //[8 * (BLOCK_PACKET_LEN+2)]; //smartport packet buffer - spi_bus_config_t bus_cfg; spi_device_handle_t spi; - // SPI receiver - spi_transaction_t rxtrans; - spi_device_handle_t spirx; + +public: /** SPI data clock * N Clock MHz /8 Bit rate (kHz) Bit/Byte period (us) * 39 2.051282051 256.4102564 3.9 31.2 256410 is only 0.3% faster than 255682 @@ -218,10 +222,7 @@ class iwm_sp_ll : public iwm_ll **/ // const int f_spirx = APB_CLK_FREQ / 39; // 2051282 Hz or 2052kHz or 2.052 MHz - works for NTSC but ... const int f_spirx = APB_CLK_FREQ / 40; // 2 MHz - need slower rate for PAL - - // SPI receiver data stream counters - int spirx_byte_ctr = 0; - int spirx_bit_ctr = 0; + spi_device_handle_t spirx; //uint8_t packet_buffer[BLOCK_PACKET_LEN]; //smartport packet buffer uint16_t packet_len = 0; @@ -283,14 +284,24 @@ class iwm_diskii_ll : public iwm_ll bool enabledD2 = true; + // write state + bool d2w_writing = false, d2w_started = false; + uint8_t *d2w_buffer; + lldesc_t *d2w_desc; + size_t d2w_buflen, d2w_begin; + size_t d2w_position; + public: + QueueHandle_t iwm_write_queue; + // Phase lines and ACK handshaking uint8_t iwm_phase_vector() { return (uint8_t)(GPIO.in1.val & (uint32_t)0b1111); }; uint8_t iwm_enable_states(); // Disk II handling by RMT peripheral void setup_rmt(); // install the RMT device - void start(uint8_t drive); + void diskii_write_handler(); + void start(uint8_t drive, bool write_protect); void stop(); // need a function to remove the RMT device? @@ -308,5 +319,14 @@ class iwm_diskii_ll : public iwm_ll extern iwm_sp_ll smartport; extern iwm_diskii_ll diskii_xface; +typedef struct { + int quarter_track; + size_t track_begin, track_end, track_numbits; + uint8_t *buffer; + size_t length; +} iwm_write_data; + +#define D2W_CHUNK_SIZE 128 + #endif // IWM_LL_H #endif // BUILD_APPLE diff --git a/lib/bus/iwm/spi_continuous.c b/lib/bus/iwm/spi_continuous.c new file mode 100644 index 000000000..b7dcf6e28 --- /dev/null +++ b/lib/bus/iwm/spi_continuous.c @@ -0,0 +1,174 @@ +#include "spi_continuous.h" + +#ifndef CONFIG_IDF_TARGET_ESP32S3 + +#include +#include +#include +#include + +// FIXME - structs that had to be copied from ESPIDF components/driver/spi_master.c +// these structs are needed in order to tear down a +// spi_device_handle_t to access the selected SPI peripheral +// registers + +// spi_device_t and spi_host_t reference each other +typedef struct spi_device_t spi_device_t; +//spi_device_handle_t is a pointer to spi_device_t + +typedef struct { + spi_transaction_t *trans; + const uint32_t *buffer_to_send; + uint32_t *buffer_to_rcv; +} spi_trans_priv_t; + +typedef struct { + int id; + spi_device_t *device[DEV_NUM_MAX]; + intr_handle_t intr; + spi_hal_context_t hal; + spi_trans_priv_t cur_trans_buf; + int cur_cs; + const spi_bus_attr_t *bus_attr; + spi_device_t *device_acquiring_lock; + + //debug information + bool polling; //in process of a polling, avoid of queue new transactions into ISR +} spi_host_t; + +struct spi_device_t { + int id; + QueueHandle_t trans_queue; + QueueHandle_t ret_queue; + spi_device_interface_config_t cfg; + spi_hal_dev_config_t hal_dev; + spi_host_t *host; + spi_bus_lock_dev_handle_t dev_lock; +}; + +// END spi_master.c structs + +/* Allocates a DMA buffer that is at least length long but is an even + multiple of chunk_size. Sets up a circular linked list with each + element pointing to a successive section of buffer that is only + chunk_size long. The last element in the list points back to the + first and no element has eof set. + + Returns length of allocated buffer or 0 on error. +*/ +size_t cspi_alloc_continuous(size_t length, size_t chunk_size, + uint8_t **buffer, lldesc_t **desc) +{ + uint32_t num_chunks, idx; + lldesc_t *llfirst, *llcur; + uint8_t *newbuf; + + + *buffer = NULL; + *desc = NULL; + + /* Make sure it's an even multiple of chunk_size */ + num_chunks = (length + chunk_size - 1) / chunk_size; + length = num_chunks * chunk_size; + newbuf = (uint8_t *) heap_caps_malloc(length, MALLOC_CAP_DMA); + if (!newbuf) { + ESP_LOGE("SPI_DMA", "Failed to allocate DMA descriptor"); + return 0; + } + *buffer = newbuf; + + llfirst = (lldesc_t *) heap_caps_malloc(sizeof(lldesc_t) * num_chunks, MALLOC_CAP_DMA); + if (!llfirst) { + ESP_LOGE("SPI_DMA", "Failed to allocate DMA descriptor"); + return 0; + } + *desc = llfirst; + + memset(llfirst, 0, sizeof(lldesc_t) * num_chunks); + for (idx = 0; idx < num_chunks; idx++) { + llcur = &llfirst[idx]; + llcur->size = chunk_size; + llcur->owner = 1; // Owned by DMA hardware + llcur->buf = &newbuf[idx * chunk_size]; + llcur->qe.stqe_next = &llfirst[(idx + 1) % num_chunks]; + } + + return length; +} + +/* Put SPI peripheral into continuous capture mode */ +void cspi_begin_continuous(spi_device_handle_t handle, lldesc_t *desc) +{ + spi_transaction_t rxtrans; + spi_host_t *host = handle->host; + spi_dev_t *hw = host->hal.hw; + + + /* Do a quick poll transaction with a single bit to get the SPI + peripheral configured. Must be less than 8 bits otherwise + continuous doesn't work. */ + memset(&rxtrans, 0, sizeof(spi_transaction_t)); + rxtrans.rxlength = 1; + rxtrans.rx_buffer = (uint8_t *) desc->buf; + + ESP_ERROR_CHECK(spi_device_polling_start(handle, &rxtrans, portMAX_DELAY)); + spi_device_polling_end(handle, portMAX_DELAY); + + /* Setup SPI peripheral for continuous using passed linked list */ + hw->dma_in_link.addr = (uint32_t) desc; + hw->dma_inlink_dscr = hw->dma_in_link.addr; + hw->dma_conf.dma_continue = 1; + hw->dma_in_link.start = 1; + + // Start SPI receive + hw->cmd.usr = 1; + + return; +} + +void cspi_end_continuous(spi_device_handle_t handle) +{ + spi_transaction_t rxtrans; + spi_host_t *host = handle->host; + spi_dev_t *hw = host->hal.hw; + + + hw->dma_conf.dma_rx_stop = 1; + hw->dma_conf.dma_tx_stop = 1; + + /* Wait for transaction to come to a full and complete stop */ + while (hw->ext2.st) + ; + + hw->dma_conf.dma_continue = 0; + + /* Do a poll transaction with 8 bits to get the SPI peripheral back + into a state that the ESPIDF driver expects */ + memset(&rxtrans, 0, sizeof(spi_transaction_t)); + rxtrans.rxlength = 8; + rxtrans.rx_buffer = (uint8_t *) heap_caps_malloc(8, MALLOC_CAP_DMA); + ESP_ERROR_CHECK(spi_device_polling_start(handle, &rxtrans, portMAX_DELAY)); + spi_device_polling_end(handle, portMAX_DELAY); + heap_caps_free(rxtrans.rx_buffer); + + return; +} + +// Get current SPI position +size_t cspi_current_pos(spi_device_handle_t handle) +{ + uint8_t *buf, *cur; + lldesc_t *current_desc; + spi_host_t *host = handle->host; + spi_dev_t *hw = host->hal.hw; + + + // Access the current descriptor being used by DMA + current_desc = (typeof(current_desc)) hw->dma_inlink_dscr; + cur = (typeof(cur)) current_desc->buf; + buf = (typeof(buf)) host->cur_trans_buf.buffer_to_rcv; + + return cur - buf; +} + +#endif /* CONFIG_IDF_TARGET_ESP32S3 */ diff --git a/lib/bus/iwm/spi_continuous.h b/lib/bus/iwm/spi_continuous.h new file mode 100644 index 000000000..a81274c82 --- /dev/null +++ b/lib/bus/iwm/spi_continuous.h @@ -0,0 +1,22 @@ +#include "sdkconfig.h" + +#ifndef CONFIG_IDF_TARGET_ESP32S3 + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + size_t cspi_alloc_continuous(size_t length, size_t chunk_size, + uint8_t **buffer, lldesc_t **desc); + void cspi_begin_continuous(spi_device_handle_t handle, lldesc_t *desc); + void cspi_end_continuous(spi_device_handle_t handle); + size_t cspi_current_pos(spi_device_handle_t handle); + +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_IDF_TARGET_ESP32S3 */ diff --git a/lib/device/iwm/disk2.cpp b/lib/device/iwm/disk2.cpp index 859502f27..ed3d6cdc8 100644 --- a/lib/device/iwm/disk2.cpp +++ b/lib/device/iwm/disk2.cpp @@ -161,5 +161,10 @@ void IRAM_ATTR iwmDisk2::change_track(int indicator) // Since the empty track has no data, and therefore no length, using a fake length of 51,200 bits (6400 bytes) works very well. } +bool iwmDisk2::write_sector(int track, int sector, uint8_t* buffer) +{ + return _disk->write_sector(track, sector, buffer); +} + #endif /* !SLIP */ #endif /* BUILD_APPLE */ diff --git a/lib/device/iwm/disk2.h b/lib/device/iwm/disk2.h index fb41d6478..267eddb0a 100644 --- a/lib/device/iwm/disk2.h +++ b/lib/device/iwm/disk2.h @@ -53,6 +53,8 @@ class iwmDisk2 : public iwmDisk // void set_disk_number(char c) { disk_num = c; } // char get_disk_number() { return disk_num; }; + bool write_sector(int track, int sector, uint8_t* buffer); + ~iwmDisk2(); }; diff --git a/lib/media/apple/mediaType.h b/lib/media/apple/mediaType.h index 26f19d8c3..261c05ce5 100644 --- a/lib/media/apple/mediaType.h +++ b/lib/media/apple/mediaType.h @@ -81,6 +81,7 @@ class MediaType virtual bool read(uint32_t blockNum, uint16_t *count, uint8_t* buffer) = 0; // Returns TRUE if an error condition occurred virtual bool write(uint32_t blockNum, uint16_t *count, uint8_t* buffer) = 0; + virtual bool write_sector(int track, int sector, uint8_t *buffer) = 0; // virtual uint16_t sector_size(uint16_t sectornum); diff --git a/lib/media/apple/mediaTypeDSK.cpp b/lib/media/apple/mediaTypeDSK.cpp index a60856b58..12b3d25ed 100644 --- a/lib/media/apple/mediaTypeDSK.cpp +++ b/lib/media/apple/mediaTypeDSK.cpp @@ -15,6 +15,7 @@ // #include #define BYTES_PER_TRACK 4096 +#define BYTES_PER_SECTOR 256 // routines to convert DSK to WOZ stolen from DSK2WOZ by Tom Harte // https://github.com/TomHarte/dsk2woz @@ -22,6 +23,47 @@ // forward reference static void serialise_track(uint8_t *dest, const uint8_t *src, uint8_t track_number, bool is_prodos); +bool MediaTypeDSK::write_sector(int qtrack, int sector, uint8_t *buffer) +{ + size_t offset, size; + size_t sectors_per_track = 16; // FIXME - what about 13 sector disks? + uint8_t *trackbuf; + int track = tmap[qtrack]; + + + Debug_printf("\r\nDSK writing track %i sector %i ", track, sector); + + offset = (track * sectors_per_track + sector) * BYTES_PER_SECTOR; + if (fnio::fseek(_media_fileh, offset, SEEK_SET) != 0) + return true; + size = fnio::fwrite(buffer, 1, BYTES_PER_SECTOR, _media_fileh); + if (size != BYTES_PER_SECTOR) + return true; + + offset = track * sectors_per_track * BYTES_PER_SECTOR; + if (fnio::fseek(_media_fileh, offset, SEEK_SET) != 0) + return true; + + size = sectors_per_track * BYTES_PER_SECTOR; + trackbuf = (uint8_t *) malloc(size); + if (!trackbuf) + return true; + + offset = fnio::fread(trackbuf, 1, size, _media_fileh); + if (offset != size) + return true; + + offset = sector * BYTES_PER_SECTOR; + memcpy(&trackbuf[offset], buffer, BYTES_PER_SECTOR); + + memset(trk_ptrs[track], 0, WOZ1_NUM_BLKS * 512); + serialise_track(trk_ptrs[track], trackbuf, track, _mediatype == MEDIATYPE_PO); + + free(trackbuf); + + return false; +} + mediatype_t MediaTypeDSK::mount(fnFile *f, uint32_t disksize) { switch (disksize) { @@ -401,6 +443,48 @@ static void encode_6_and_2(uint8_t *dest, const uint8_t *src) { } } +uint16_t decode_6_and_2(uint8_t *dest, const uint8_t *src) +{ + int idx, step; + uint8_t bits; + uint16_t checksum; + const uint8_t bit_reverse[] = {0, 2, 1, 3}; + const uint8_t six_and_two_unmapping[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x02, 0x03, 0x00, 0x04, 0x05, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, + 0x00, 0x00, 0x00, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + 0x00, 0x00, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x00, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1b, 0x00, 0x1c, 0x1d, 0x1e, + 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x20, 0x21, + 0x00, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x2a, 0x2b, + 0x00, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, + 0x00, 0x00, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x00, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + }; + + + for (idx = 0; idx < 343; idx++) + dest[idx] = six_and_two_unmapping[src[idx] - 144]; + for (idx = 0; idx < 341; idx++) + dest[idx + 1] ^= dest[idx]; + + checksum = (dest[341] << 8) | dest[342]; + + for (idx = 85; idx >= 0; idx--) { + bits = dest[idx]; + for (step = 0; step < 3; step++) { + dest[idx + step * 86] = (dest[idx + (step + 1) * 86] << 2) + | bit_reverse[(bits >> step * 2) & 0x3]; + } + } + + return checksum; +} + /*! Converts a DSK-style track to a WOZ-style track. diff --git a/lib/media/apple/mediaTypeDSK.h b/lib/media/apple/mediaTypeDSK.h index a45bc1631..c68369a99 100644 --- a/lib/media/apple/mediaTypeDSK.h +++ b/lib/media/apple/mediaTypeDSK.h @@ -14,6 +14,7 @@ // uint32_t bit_count; // }; +extern uint16_t decode_6_and_2(uint8_t *dest, const uint8_t *src); class MediaTypeDSK : public MediaTypeWOZ { @@ -28,6 +29,7 @@ class MediaTypeDSK : public MediaTypeWOZ virtual mediatype_t mount(fnFile *f, uint32_t disksize) override; // virtual void unmount() override; + virtual bool write_sector(int track, int sector, uint8_t *buffer) override; // static bool create(FILE *f, uint32_t numBlock); }; diff --git a/lib/media/apple/mediaTypePO.cpp b/lib/media/apple/mediaTypePO.cpp index 1609528b9..45ca14537 100644 --- a/lib/media/apple/mediaTypePO.cpp +++ b/lib/media/apple/mediaTypePO.cpp @@ -68,6 +68,12 @@ bool MediaTypePO::write(uint32_t blockNum, uint16_t *count, uint8_t* buffer) return false; } +bool MediaTypePO::write_sector(int track, int sector, uint8_t *buffer) +{ + Debug_printf("\r\nProDOS disk needs to write sector!"); + return false; +} + bool MediaTypePO::format(uint16_t *responsesize) { return false; diff --git a/lib/media/apple/mediaTypePO.h b/lib/media/apple/mediaTypePO.h index b33cd0c43..441c8e8d6 100644 --- a/lib/media/apple/mediaTypePO.h +++ b/lib/media/apple/mediaTypePO.h @@ -13,6 +13,7 @@ class MediaTypePO : public MediaType public: virtual bool read(uint32_t blockNum, uint16_t *count, uint8_t* buffer) override; virtual bool write(uint32_t blockNum, uint16_t *count, uint8_t* buffer) override; + virtual bool write_sector(int track, int sector, uint8_t *buffer) override; virtual bool format(uint16_t *responsesize) override; diff --git a/lib/media/apple/mediaTypeWOZ.cpp b/lib/media/apple/mediaTypeWOZ.cpp index fa80e8d88..f2f6e79cf 100644 --- a/lib/media/apple/mediaTypeWOZ.cpp +++ b/lib/media/apple/mediaTypeWOZ.cpp @@ -10,6 +10,12 @@ #define WOZ1 '1' #define WOZ2 '2' +bool MediaTypeWOZ::write_sector(int track, int sector, uint8_t *buffer) +{ + Debug_printf("\r\nWOZ disk needs to write sector!"); + return false; +} + mediatype_t MediaTypeWOZ::mount(fnFile *f, uint32_t disksize) { _media_fileh = f; @@ -252,4 +258,4 @@ bool MediaTypeWOZ::woz2_read_tracks() return false; } -#endif // BUILD_APPLE \ No newline at end of file +#endif // BUILD_APPLE diff --git a/lib/media/apple/mediaTypeWOZ.h b/lib/media/apple/mediaTypeWOZ.h index cab124995..2700a0ff1 100644 --- a/lib/media/apple/mediaTypeWOZ.h +++ b/lib/media/apple/mediaTypeWOZ.h @@ -36,6 +36,7 @@ class MediaTypeWOZ : public MediaType public: virtual bool read(uint32_t blockNum, uint16_t *count, uint8_t* buffer) override { return false; }; virtual bool write(uint32_t blockNum, uint16_t *count, uint8_t* buffer) override { return false; }; + virtual bool write_sector(int track, int sector, uint8_t *buffer) override; virtual bool format(uint16_t *responsesize) override { return false; };