#include #include #include "datastream.h" #include #include CLIClient *console; #define SLICE4 1 #define SDIO_CLK 16 #define SDIO_CMD 17 #define SDIO_DAT0 18 #define SDIO_DAT1 19 #define SDIO_DAT2 20 #define SDIO_DAT3 21 #define SECTOR_128 0 #define SECTOR_256 1 #define SECTOR_512 2 #define SECTOR_1024 3 #define INDEX 12 #define DOUT 0 #define STEP 7 #define DIR 8 #define SEEK_DONE 9 #define TRACK0 10 #define HSEL0 2 #define HSEL1 3 #define HSEL2 4 #define HSEL3 5 static PIOProgram datastreamPgm(&datastream_program); PIO pio; int sm; int offset; int dma; const uint8_t rd54_vs2k_uit[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x36,0x00,0x00,0x00,0xc9,0x00, 0x00,0x00,0xd8,0xbf,0x04,0x00,0x61,0x02,0x00,0x00,0x11,0x00,0x0f,0x00,0xc9,0x04, 0xc9,0x04,0xc9,0x04,0x00,0x00,0x01,0x00,0x07,0x00,0x08,0x00,0x36,0x40,0x64,0x25, 0x01,0x00,0x07,0x00,0x0e,0x00,0x10,0x00,0x10,0x00,0x05,0x00,0x28,0x00,0x0d,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x15,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe6,0x70, }; uint32_t loadtime; volatile uint32_t current_head = 0; volatile uint32_t current_cyl = 0; volatile uint32_t current_sector = 0; volatile uint32_t target_cyl = 0; #define POLY16 0x1021 #define POLY32 0xa00805 #define NUM_SECTORS 17 #define TRACK_SIZE (512 * NUM_SECTORS) #define OPT_HEADER_CRC16 0x00000001 #define OPT_HEADER_CRC32 0x00000002 #define OPT_HEADER_CRC_MASK 0xFFFFFFFC #define OPT_DATA_CRC16 0x00000004 #define OPT_DATA_CRC32 0x00000008 #define OPT_DATA_CRC_MASK 0xFFFFFFF3 SdFs sd; uint8_t *track_buffer; volatile bool run_mfm_sm = false; mutex mfm_sm_running; struct disk_format { char *name; char *comment; char *uit_file; const uint8_t *uit_blob; uint32_t uit_bloblen; uint32_t uit_start; uint32_t uit_cyls; uint32_t uit_repeats; uint32_t image_start; uint16_t cyls; uint8_t heads; uint8_t sectors; uint8_t sector_size; uint32_t index_width; uint32_t track_pregap; uint32_t track_postgap; uint32_t header_postgap; uint32_t data_postgap; uint32_t sync_count; float data_rate; uint32_t flags; uint32_t header_poly; uint32_t data_poly; bool auto_return; // Calculated data - not to be filled. uint32_t tlen; uint32_t slen; uint32_t idx_ts; uint32_t idx_period; float clock_div; }; class more { public: constexpr bool operator()(uint32_t lhs, uint32_t rhs) const { return lhs > rhs; // assumes that the implementation handles pointer total order } }; class slice { public: virtual void load_track(uint32_t track, uint8_t *buffer, size_t track_len) = 0; }; class blob_slice : public slice { private: const uint8_t *_ptr; uint32_t _len; uint8_t _repeats; public: blob_slice(const uint8_t *ptr, uint32_t len, uint8_t repeats) { _ptr = ptr; _len = len; _repeats = repeats; } void load_track(uint32_t track, uint8_t *buffer, size_t track_len) { memset(buffer, 0, track_len); for (int i = 0; i < _repeats; i++) { memcpy(buffer + _len * i, _ptr, _len); } } }; class file_slice : public slice { private: FsFile _file; uint32_t _repeats; public: file_slice(const char *filename, uint32_t repeats) { _file.open(filename); _repeats = repeats; } ~file_slice() { _file.close(); } // TODO void load_track(uint32_t track, uint8_t *buffer, size_t track_len) { memset(buffer, 0, track_len); size_t nr_read = 0; for (int i = 0; (i < _repeats) && (nr_read < track_len); i++) { _file.seek(track * track_len); nr_read += _file.read(buffer + nr_read, track_len - nr_read); } } }; struct slice_list { uint32_t start; slice *slice_ptr; struct slice_list *next; }; class image { private: struct slice_list *slices; uint32_t _bps, _spt, _tpc; slice *find_slice(uint32_t track, uint32_t *offset) { slice *sl = NULL; for (struct slice_list *scan = slices; scan; scan = scan->next) { if (scan->start <= track) { sl = scan->slice_ptr; if (offset != NULL) { *offset = scan->start; } } else { break; } } return sl; } void purge_slices() { struct slice_list *ptr; struct slice_list *scan = slices; while (scan) { Serial.println("Deleted a slice"); delay(10); ptr = scan->next; delete(scan); scan = ptr; } } public: image(uint32_t bytes_per_sector, uint32_t sectors_per_track, uint32_t tracks_per_cyl) { _bps = bytes_per_sector; _spt = sectors_per_track; _tpc = tracks_per_cyl; slices = NULL; } ~image() { purge_slices(); } void load_track(uint32_t track, uint8_t *buffer) { uint32_t offset; slice *image = find_slice(track, &offset); if (image != NULL) { image->load_track(track - offset, buffer, _bps * _spt * _tpc); } } void add_slice(uint32_t start, slice *slice_ptr) { struct slice_list *newslice = (struct slice_list *)malloc(sizeof(struct slice_list)); newslice->start = start; newslice->slice_ptr = slice_ptr; newslice->next = NULL; if (slices == NULL) { slices = newslice; return; } for (struct slice_list *scan = slices; scan; scan = scan->next) { if (scan->next == NULL) { scan->next = newslice; break; } } } }; image *current_image; struct format_list { struct disk_format *fmt; struct format_list *next; }; struct format_list *formats = NULL; struct encoded_sector { uint16_t header[10]; uint16_t data[520]; }; struct raw_sector { uint16_t header_crc; uint32_t data_crc; uint8_t *data; }; struct head { struct raw_sector *sector; }; struct cylinder { uint32_t heads; uint32_t sectors; struct head *head; }; struct cylinder cyl_data; struct disk_format *format; bool _sd_is_open = false; bool open_sd_if_needed() { if (_sd_is_open) return true; if (!sd.begin(SdioConfig(SDIO_CLK, SDIO_CMD, SDIO_DAT0, 1.5))) { sd.initErrorPrint(); return false;; } _sd_is_open = true; return true; } void bindump(uint16_t v) { for (int i = 0; i < 16; i++) { v <<= 1; } } uint16_t crc16(uint8_t val, uint16_t crc, uint16_t poly) { uint16_t xval = val; int j; crc = crc ^ (xval << 8); for (j = 1; j <= 8; j++) { if (crc & 0x8000) { crc = (crc << 1) ^ poly; } else { crc = crc << 1; } } return crc; } #if !defined(SLICE4) static uint32_t CRCTable[256]; static void CRC32_init(uint32_t poly) { uint32_t mask = 0xFFFFFFFF; uint32_t topbit = 0x80000000; for (int i = 0; i < 256; i++) { uint32_t c = (i << 24); for (int j = 0; j < 8; j++) { if (c & topbit) { c = ((c << 1) ^ poly) & mask; } else { c = (c << 1) & mask; } } CRCTable[i] = c; } } static inline uint32_t crc32(uint8_t val, uint32_t crc, uint32_t poly) { const uint8_t idx = ((crc >> 24) ^ val) & 0xFF; crc = ((crc << 8) ^ CRCTable[idx]); return crc; } #else static uint32_t CRCTable[4][256]; void CRC32_init(uint32_t poly) { const uint32_t topbit = 0x80000000u; for (int i = 0; i < 256; i++) { uint32_t c = (uint32_t)i << 24; for (int j = 0; j < 8; j++) { if (c & topbit) c = (c << 1) ^ poly; else c <<= 1; } CRCTable[0][i] = c; } // Generate slicing tables 1–3 for (int t = 1; t < 4; t++) { for (int i = 0; i < 256; i++) { uint32_t c = CRCTable[t - 1][i]; CRCTable[t][i] = CRCTable[0][(c >> 24) & 0xFF] ^ (c << 8); } } } __attribute__((section(".time_critical"))) uint32_t crc32_fast(const uint8_t *data, size_t len, uint32_t crc) { while (len >= 4) { uint32_t word = ((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) | ((uint32_t)data[2] << 8) | ((uint32_t)data[3]); crc ^= word; crc = CRCTable[3][(crc >> 24) & 0xFF] ^ CRCTable[2][(crc >> 16) & 0xFF] ^ CRCTable[1][(crc >> 8) & 0xFF] ^ CRCTable[0][ crc & 0xFF]; data += 4; len -= 4; } // Remaining bytes while (len--) { uint8_t idx = (crc >> 24) ^ *data++; crc = (crc << 8) ^ CRCTable[0][idx]; } return crc; } #endif uint8_t last_bit = 0; uint16_t mfm_encode_bit(uint8_t b) { if (b == 0x80) { last_bit = 1; return 0b01; } if (last_bit == 0) { return 0b10; } else { last_bit = 0; return 0b00; } } uint16_t mfm_encode(uint8_t b, bool reset = false) { if (reset) { last_bit = 0; } uint16_t out = 0; for (int i = 0; i < 8; i++) { out <<= 2; out |= mfm_encode_bit(b & 0x80); b <<= 1; } return out; } enum { PH_INDEX_START, PH_INDEX_END, PH_TRACK_PREGAP, PH_SEND_HEADER, PH_HEADER_POSTGAP, PH_SEND_DATA, PH_DATA_POSTGAP, PH_TRACK_POSTGAP }; enum { LOAD_IDLE, LOAD_HEADER, LOAD_DATA, LOAD_DATA_RUN, LOAD_HEADER_CS, LOAD_DATA_CS, LOAD_HEADER_MFM, LOAD_DATA_MFM, LOAD_SYNC }; void second_cpu_thread() { dma = dma_claim_unused_channel(true); dma_channel_config config = dma_channel_get_default_config(dma); channel_config_set_read_increment(&config, true); channel_config_set_write_increment(&config, false); channel_config_set_dreq(&config, pio_get_dreq(pio, sm, true)); channel_config_set_transfer_data_size(&config, DMA_SIZE_16); uint32_t ts = micros(); int phase = 0; int load_sm = LOAD_IDLE; int load_iter = 0; uint16_t load_hcs = 0; uint32_t load_dcs = 0; int next_sector = 1; current_sector = 0; struct encoded_sector sectorA; struct encoded_sector sectorB; struct encoded_sector *load = §orA; struct encoded_sector *send = §orB; struct encoded_sector *swap; pinMode(HSEL0, INPUT); pinMode(HSEL1, INPUT); pinMode(HSEL2, INPUT); pinMode(HSEL3, INPUT); uint32_t cs = current_cyl; bool running = false; while (1) { if (!run_mfm_sm) { phase = 0; load_sm = LOAD_IDLE; if (running) { dma_channel_wait_for_finish_blocking(dma); mutex_exit(&mfm_sm_running); } running = false; continue; } if (!running) { running = true; mutex_enter_blocking(&mfm_sm_running); } uint16_t hp = format->header_poly; current_head = digitalRead(HSEL0) | (digitalRead(HSEL1) << 1) | (digitalRead(HSEL2) << 2) | (digitalRead(HSEL3) << 3); switch (phase) { case PH_INDEX_START: // Start index pulse cs = current_cyl; ts = micros(); format->idx_period = micros() - format->idx_ts; format->idx_ts = micros(); digitalWrite(INDEX, LOW); phase++; break; case PH_INDEX_END: // Index pulse timing if ((micros() - ts) >= format->index_width) { ts = micros(); digitalWrite(INDEX, HIGH); phase++; } break; case PH_TRACK_PREGAP: // Track pre-gap if ((micros() - ts) >= format->track_pregap) { ts = micros(); phase = PH_SEND_HEADER; } break; case PH_SEND_HEADER: ts = micros(); swap = load; load = send; send = swap; dma_channel_configure(dma, &config, &pio->txf[sm], send->header, 10, true ); next_sector = (current_sector + 1) % format->sectors; load_sm = LOAD_HEADER; phase = PH_HEADER_POSTGAP; break; case PH_HEADER_POSTGAP: if ((micros() - ts) >= format->header_postgap) { ts = micros(); phase = PH_SEND_DATA; } break; case PH_SEND_DATA: ts = micros(); dma_channel_configure(dma, &config, &pio->txf[sm], send->data, 520, true ); if (current_sector < format->sectors - 1) { phase = PH_DATA_POSTGAP; } else { phase = PH_TRACK_POSTGAP; } break; case PH_DATA_POSTGAP: if ((micros() - ts) > format->data_postgap) { ts = micros(); current_sector++; phase = PH_SEND_HEADER; } break; case PH_TRACK_POSTGAP: if ((micros() - ts) > format->track_postgap) { ts = micros(); current_sector = 0; phase = PH_INDEX_START; } break; default: Serial.println("ERROR: BAD STATE MACHINE STATE"); phase = PH_INDEX_START; break; } // Sector loading state machine switch (load_sm) { case LOAD_IDLE: // Waiting for instruction break; case LOAD_HEADER: //digitalWrite(INDEX, LOW); //digitalWrite(INDEX, HIGH); load->header[0] = 0; load->header[1] = 0xA1; load->header[2] = 0xFE; load->header[3] = (cs & 0xFF); load->header[4] = ((cs & 0xF00) >> 4) | (current_head & 0x0F); load->header[5] = next_sector; load->header[6] = format->sector_size; load->header[7] = 0x00; load->header[8] = 0x00; load->header[9] = 0; load_sm = LOAD_DATA; break; case LOAD_DATA: load->data[0] = 0; load->data[1] = 0xA1; load->data[2] = 0xFB; load->data[515] = ((cyl_data.head[current_head].sector[next_sector].data_crc >> 24) & 0xFF); load->data[516] = ((cyl_data.head[current_head].sector[next_sector].data_crc >> 16) & 0xFF); load->data[517] = ((cyl_data.head[current_head].sector[next_sector].data_crc >> 8) & 0xFF); load->data[518] = (cyl_data.head[current_head].sector[next_sector].data_crc & 0xFF); load->data[519] = 0; load_iter = 0; load_sm = LOAD_HEADER_CS; load_iter = 1; load_hcs = 0xFFFF; break; case LOAD_HEADER_CS: load_hcs = crc16(load->header[load_iter], load_hcs, hp); load_iter++; if (load_iter == 7) { load->header[7] = (load_hcs >> 8) & 0xFF; load->header[8] = load_hcs & 0xFF; load_iter = 1; load_sm = LOAD_HEADER_MFM; } break; case LOAD_HEADER_MFM: load->header[0] = mfm_encode(load->header[0], true); load->header[1] = mfm_encode(load->header[1], false) & 0b1111111111011111; load->header[2] = mfm_encode(load->header[2], false); load->header[3] = mfm_encode(load->header[3], false); load->header[4] = mfm_encode(load->header[4], false); load->header[5] = mfm_encode(load->header[5], false); load->header[6] = mfm_encode(load->header[6], false); load->header[7] = mfm_encode(load->header[7], false); load->header[8] = mfm_encode(load->header[8], false); load->header[9] = mfm_encode(load->header[9], false); load_iter = 0; load_sm = LOAD_DATA_MFM; break; case LOAD_DATA_MFM: if ((load_iter >= 3) && (load_iter < 515)) { load->data[load_iter] = mfm_encode(cyl_data.head[current_head].sector[next_sector].data[load_iter - 3]); } else { load->data[load_iter] = mfm_encode(load->data[load_iter]); } load_iter++; if ((load_iter >= 3) && (load_iter < 515)) { load->data[load_iter] = mfm_encode(cyl_data.head[current_head].sector[next_sector].data[load_iter - 3]); } else { load->data[load_iter] = mfm_encode(load->data[load_iter]); } load_iter++; if ((load_iter >= 3) && (load_iter < 515)) { load->data[load_iter] = mfm_encode(cyl_data.head[current_head].sector[next_sector].data[load_iter - 3]); } else { load->data[load_iter] = mfm_encode(load->data[load_iter]); } load_iter++; if ((load_iter >= 3) && (load_iter < 515)) { load->data[load_iter] = mfm_encode(cyl_data.head[current_head].sector[next_sector].data[load_iter - 3]); } else { load->data[load_iter] = mfm_encode(load->data[load_iter]); } load_iter++; if ((load_iter >= 3) && (load_iter < 515)) { load->data[load_iter] = mfm_encode(cyl_data.head[current_head].sector[next_sector].data[load_iter - 3]); } else { load->data[load_iter] = mfm_encode(load->data[load_iter]); } load_iter++; if (load_iter >= 520) { load->data[1] &= 0b1111111111011111; //digitalWrite(INDEX, LOW); //digitalWrite(INDEX, HIGH); load_sm = LOAD_IDLE; } break; case LOAD_SYNC: load->header[1] &= 0b1111111111011111; load->data[1] &= 0b1111111111011111; load_sm = LOAD_IDLE; break; default: Serial.println("ERROR: BAD LOADING SM STATUS"); load_sm = LOAD_IDLE; break; } } } void create_track_store() { if (cyl_data.head) { for (int i = 0; i < cyl_data.heads; i++) { if (cyl_data.head[i].sector) { free(cyl_data.head[i].sector); } } free(cyl_data.head); } cyl_data.head = (struct head *)malloc(sizeof(struct head) * format->heads); for (int i = 0; i < format->heads; i++) { cyl_data.head[i].sector = (struct raw_sector *)malloc(sizeof(struct raw_sector) * format->sectors); } } void calculate_sector_crc(int head, int sector, int sectorsize) { uint32_t crc = 0xFFFFFFFF; uint32_t poly = format->data_poly; #if defined(SLICE4) uint8_t hdr[2] = { 0xA1, 0xFB }; crc = crc32_fast(hdr, 2, crc); uint8_t *data = cyl_data.head[head].sector[sector].data; crc = crc32_fast(data, (0x80 << sectorsize), crc); /* uint8_t *buf = (uint8_t *)alloca((0x80 << sectorsize) + 2); buf[0] = 0xA1; buf[1] = 0xFB; uint8_t *data = cyl_data.head[head].sector[sector].data; memcpy(buf + 2, data, 0x80 << sectorsize); crc = crc32_fast(buf, (0x80 << sectorsize) + 2, crc); */ #else crc = crc32(0xA1, crc, poly); crc = crc32(0xFB, crc, poly); uint8_t *data = cyl_data.head[head].sector[sector].data; for (int i = 0; i < (0x80 << sectorsize); i++) { crc = crc32(data[i], crc, poly); } #endif cyl_data.head[head].sector[sector].data_crc = crc; } void load_cyl(uint32_t cyl, uint32_t heads, uint32_t sectors, uint32_t sectorsize) { uint32_t ts = micros(); current_image->load_track(cyl, track_buffer); uint32_t rts = micros() - ts; for (int head = 0; head < format->heads; head++) { for (int sector = 0; sector < format->sectors; sector++) { calculate_sector_crc(head, sector, sectorsize); } } uint32_t cts = micros() - ts; Serial.printf("T: %d R: %u c: %u %f tps\r\n", cyl, rts, cts - rts, 1 / (cts / 1000000.0)); } char *trim(char *str) { char *end; // Trim leading space while(isspace((unsigned char)*str)) str++; if(*str == 0) // All spaces? return str; // Trim trailing space end = str + strlen(str) - 1; while(end > str && isspace((unsigned char)*end)) end--; // Write new null terminator character end[1] = '\0'; return str; } int execute_file(const char *filename, CLIClient *client) { FsFile file; if (!open_sd_if_needed()) { errno = ENOENT; return 10; } if (!sd.exists(filename)) { errno = ENOENT; return 10; } file.open(filename); char line[1024]; while (file.fgets(line, 1024)) { char *tline = trim(line); if (strlen(tline) == 0) continue; if (tline[0] == '#') continue; client->print(filename); client->print(": "); client->println(tline); client->parseCommand(tline); } file.close(); return 0; } CLI_COMMAND(cli_source) { if (argc != 2) { dev->println("Usage: source "); return 10; } if (execute_file(argv[1], dev) == 0) { return 0; } dev->println("File not found"); return 10; } CLI_COMMAND(cli_status) { uint32_t sector_bytes = 8 + format->slen + 6 + format->header_postgap + format->data_postgap + 4; uint32_t total_clocks = ((sector_bytes * format->sectors) + format->track_pregap + format->track_postgap + 1) * 8; if ((argc == 2) && (strcmp(argv[1], "ini") == 0)) { dev->print("cyls="); dev->println(format->cyls); dev->print("heads="); dev->println(format->heads); dev->print("sectors="); dev->println(format->sectors); dev->print("sector_size="); dev->println(0x80 << format->sector_size); dev->print("track_pregap="); dev->println(format->track_pregap); dev->print("track_postgap="); dev->println(format->track_postgap); dev->print("header_postgap="); dev->println(format->header_postgap); dev->print("data_postgap="); dev->println(format->data_postgap); dev->print("data_rate="); dev->println(format->data_rate); dev->print("header_crc="); dev->println((format->flags & OPT_HEADER_CRC16) ? "16" : (format->flags & OPT_HEADER_CRC32) ? "32" : "ERROR"); dev->print("data_crc="); dev->println((format->flags & OPT_DATA_CRC16) ? "16" : (format->flags & OPT_DATA_CRC32) ? "32" : "ERROR"); dev->print("header_poly="); dev->println(format->header_poly, HEX); dev->print("data_poly="); dev->println(format->data_poly, HEX); return 0; } float rpm = (format->data_rate / total_clocks) * 60.0; dev->print("Cylinders: "); dev->println(format->cyls); dev->print("Heads: "); dev->println(format->heads); dev->print("Sectors: "); dev->println(format->sectors); dev->print("Sector Size: "); dev->print(0x80 << format->sector_size); dev->println(" bytes"); dev->println(); dev->print("Header CRC Bits: "); dev->println((format->flags & OPT_HEADER_CRC16) ? "16" : (format->flags & OPT_HEADER_CRC32) ? "32" : "ERROR"); dev->print("Data CRC Bits: "); dev->println((format->flags & OPT_DATA_CRC16) ? "16" : (format->flags & OPT_DATA_CRC32) ? "32" : "ERROR"); dev->print("Header CRC Polynomial: "); dev->println(format->header_poly, HEX); dev->print("Data CRC Polynomial: "); dev->println(format->data_poly, HEX); dev->println(); dev->print("Track Pregap: "); dev->print(format->track_pregap); dev->println(" uS"); dev->print("Track Postgap: "); dev->print(format->track_postgap); dev->println(" uS"); dev->print("Header Postgap: "); dev->print(format->header_postgap); dev->println(" uS"); dev->print("Data Postgap: "); dev->print(format->data_postgap); dev->println(" uS"); dev->println(); dev->print("Actual RPM: "); float p = format->idx_period / 1000000.0; float f = 1.0 / p; float r = f * 60; dev->println(r); dev->print("Requested Data Rate: "); dev->print(format->data_rate); dev->println("Hz"); dev->print("Actual Data Rate: "); dev->print(F_CPU / format->clock_div / 20.0); dev->println("Hz"); dev->print("Clock divider: "); dev->println(format->clock_div); dev->println(); dev->print("PIO: "); dev->println(PIO_NUM(pio)); dev->print("State Machine: "); dev->println(sm); dev->print("Offset: "); dev->println(offset); dev->println(); dev->print("Load time: "); dev->print(loadtime); dev->println("uS"); dev->println(); dev->print("Current C/H/S: "); dev->print(current_cyl); dev->print("/"); dev->print(current_head); dev->print("/"); dev->println(current_sector); dev->print("MFM State Machine Run: "); dev->println(run_mfm_sm ? "Yes" : "No"); dev->println(); dev->print("Known formats: "); for (struct format_list *scan = formats; scan; scan = scan->next) { dev->print(scan->fmt->name); dev->print(" "); } dev->println(); return 0; } CLI_COMMAND(cli_set) { if (argc != 3) { dev->println("Usage: set "); dev->println("Possible items:"); dev->println(" track_pregap"); dev->println(" track_posthap"); dev->println(" header_postgap"); dev->println(" data_postgap"); dev->println(" data_rate"); dev->println(" header_crc"); dev->println(" data_crc"); dev->println(" header_poly"); dev->println(" data_poly"); dev->println(" auto_return"); return 10; } if (strcmp(argv[1], "track_pregap") == 0) { format->track_pregap = strtoul(argv[2], NULL, 10); return 0; } if (strcmp(argv[1], "track_postgap") == 0) { format->track_postgap = strtoul(argv[2], NULL, 10); return 0; } if (strcmp(argv[1], "header_postgap") == 0) { format->header_postgap = strtoul(argv[2], NULL, 10); return 0; } if (strcmp(argv[1], "data_postgap") == 0) { format->data_postgap = strtoul(argv[2], NULL, 10); return 0; } if (strcmp(argv[1], "data_rate") == 0) { float r = strtof(argv[2], NULL); float cd = F_CPU / r / 20.0; if (cd < 1) { dev->println("Data rate too high for the CPU clock"); return 10; } format->data_rate = r; format->clock_div = cd; pio_sm_set_clkdiv(pio, sm, format->clock_div); return 0; } if (strcmp(argv[1], "header_crc") == 0) { int c = strtol(argv[2], NULL, 10); if (c == 16) { format->flags &= OPT_HEADER_CRC_MASK; format->flags |= OPT_HEADER_CRC16; return 0; } if (c == 32) { format->flags &= OPT_HEADER_CRC_MASK; format->flags |= OPT_HEADER_CRC32; return 0; } dev->println("Error: header_crc must be 16 or 32"); return 10; } if (strcmp(argv[1], "data_crc") == 0) { int c = strtol(argv[2], NULL, 10); if (c == 16) { format->flags &= OPT_DATA_CRC_MASK; format->flags |= OPT_DATA_CRC16; return 0; } if (c == 32) { format->flags &= OPT_DATA_CRC_MASK; format->flags |= OPT_DATA_CRC32; return 0; } dev->println("Error: data_crc must be 16 or 32"); return 10; } if (strcmp(argv[1], "header_poly") == 0) { format->header_poly = strtoul(argv[2], NULL, 16); return 0; } if (strcmp(argv[1], "data_poly") == 0) { format->data_poly = strtoul(argv[2], NULL, 16); return 0; } if (strcmp(argv[1], "auto_return") == 0) { format->auto_return = strncasecmp(argv[2], "Y", 1) == 0; return 0; } dev->println("Possible items:"); dev->println(" track_pregap"); dev->println(" track_posthap"); dev->println(" header_postgap"); dev->println(" data_postgap"); return 10; } CLI_COMMAND(cli_ls) { if (!open_sd_if_needed()) { return 10; } sd.ls(LS_A | LS_DATE | LS_SIZE); return 0; } CLI_COMMAND(cli_create) { if (argc != 3) { dev->println("Usage: create "); dev->println("Create a new image file using the specified disk format"); return 10; } if (!open_sd_if_needed()) { return 10; } struct disk_format *fmt = find_format_by_name(argv[2]); if (fmt == NULL) { dev->println("Unknown format"); return 10; } if (sd.exists(argv[1])) { dev->println("File already exists"); return 10; } uint32_t disk_size = 0x80 << fmt->sector_size; disk_size *= fmt->sectors; disk_size *= fmt->heads; disk_size *= fmt->cyls; dev->println("Creating new file, please wait..."); FsFile newfile; newfile.open(argv[1], O_RDWR | O_CREAT); newfile.preAllocate(disk_size); newfile.close(); return 0; } CLI_COMMAND(cli_mount) { if (argc != 3) { dev->println("Usage: mount "); dev->println("Mount an image file as the virtual disk"); return 10; } if (!open_sd_if_needed()) { return 10; } if (!sd.exists(argv[1])) { dev->println("File not found"); return 10; } run_mfm_sm = false; if (!mutex_enter_timeout_ms(&mfm_sm_running, 1000)) { dev->println("Timeout waiting for disk idle"); return 10; } //while (mfm_sm_running); if (current_image) { delete(current_image); current_image = NULL; } format = find_format_by_name(argv[2]); if (format == NULL) { dev->println("Format not known"); return 10; } create_track_store(); current_cyl = 0; current_head = 0; uint32_t bps = 0x80 << format->sector_size; uint32_t bufsz = format->heads * format->sectors * bps; dev->printf("Track buffer size: %d\n", bufsz); track_buffer = (uint8_t *)malloc(bufsz); if (!track_buffer) { dev->println("Unable to allocate track buffer"); return 10; } for (int head = 0; head < format->heads; head++) { int hoff = head * format->sectors; for (int sector = 0; sector < format->sectors; sector++) { uint8_t *doff = track_buffer + ((hoff + sector) * bps); cyl_data.head[head].sector[sector].data = doff; } } CRC32_init(format->data_poly); dev->println("Creating image"); delay(10); current_image = new image(0x80 << format->sector_size, format->sectors, format->heads); dev->println("Adding UIT"); delay(10); if (format->uit_blob) { dev->println(" ... blob"); delay(10); blob_slice *blob = new blob_slice(format->uit_blob, format->uit_bloblen, format->uit_repeats); current_image->add_slice(format->uit_start, blob); dev->println("ok"); delay(10); } else if (format->uit_file) { dev->println(" ... file"); delay(10); file_slice *uit_file = new file_slice(format->uit_file, format->uit_repeats); current_image->add_slice(format->uit_start, uit_file); dev->println("ok"); delay(10); } dev->println("Adding main image slice"); delay(10); file_slice *file = new file_slice(argv[1], 1); current_image->add_slice(format->image_start, file); dev->println("Loading first track"); delay(10); load_cyl(current_cyl, format->heads, format->sectors, format->sector_size); dev->println("Completed"); delay(10); mutex_exit(&mfm_sm_running); run_mfm_sm = true; return 0; } void do_step() { if (format == NULL) return; //if(!mfm_sm_running) return; digitalWrite(SEEK_DONE, LOW); int dir = digitalRead(DIR); if (dir == 1 && current_cyl == 0) { // No can do. digitalWrite(SEEK_DONE, HIGH); return; } if (dir == 1) { target_cyl --; } else { target_cyl ++; } if (target_cyl >= format->cyls) { if (format->auto_return) { target_cyl = 0; } else { target_cyl = format->cyls - 1; } } } struct disk_format *find_format_by_name(const char *filename) { for (struct format_list *scan = formats; scan; scan = scan->next) { if (strcasecmp(filename, scan->fmt->name) == 0) { return scan->fmt; } } return NULL; } void append_format(struct disk_format *fmt) { if (formats == NULL) { formats = (struct format_list *)malloc(sizeof(struct format_list)); formats->fmt = fmt; formats->next = NULL; return; } struct format_list *nf = (struct format_list *)malloc(sizeof(struct format_list)); nf->fmt = fmt; nf->next = NULL; for (struct format_list *scan = formats; scan; scan = scan->next) { if (scan->next == NULL) { scan->next = nf; return; } } } struct disk_format *load_format(const char *filename) { FsFile file; file.open(filename); if (!file) { return NULL; } struct disk_format *fmt = (struct disk_format *)malloc(sizeof(struct disk_format)); memset(fmt, 0, sizeof(struct disk_format)); char l[1024]; while (file.fgets(l, 1024)) { char *tl = trim(l); if (strlen(tl) == 0) continue; if (tl[0] == '#') continue; if (tl[0] == ';') continue; if (tl[0] == '!') continue; char *key = strtok(tl, "="); char *val = strtok(NULL, "="); key = trim(key); val = trim(val); if (strcasecmp(key, "name") == 0) { fmt->name = strdup(val); continue; } if (strcasecmp(key, "comment") == 0) { fmt->comment = strdup(val); continue; } if (strcasecmp(key, "cyls") == 0) { fmt->cyls = strtoul(val, NULL, 0); continue; } if (strcasecmp(key, "heads") == 0) { fmt->heads = strtoul(val, NULL, 0); continue; } if (strcasecmp(key, "sectors") == 0) { fmt->sectors = strtoul(val, NULL, 0); continue; } if (strcasecmp(key, "sector_size") == 0) { int ss = strtoul(val, NULL, 0); switch (ss) { case 128: fmt->sector_size = SECTOR_128; break; case 256: fmt->sector_size = SECTOR_256; break; case 512: fmt->sector_size = SECTOR_512; break; case 1024: fmt->sector_size = SECTOR_1024; break; } continue; } if (strcasecmp(key, "index_width") == 0) { fmt->index_width = strtoul(val, NULL, 0); continue; } if (strcasecmp(key, "track_pregap") == 0) { fmt->track_pregap = strtoul(val, NULL, 0); continue; } if (strcasecmp(key, "track_postgap") == 0) { fmt->track_postgap = strtoul(val, NULL, 0); continue; } if (strcasecmp(key, "header_postgap") == 0) { fmt->header_postgap = strtoul(val, NULL, 0); continue; } if (strcasecmp(key, "data_postgap") == 0) { fmt->data_postgap = strtoul(val, NULL, 0); continue; } if (strcasecmp(key, "data_rate") == 0) { fmt->data_rate = strtoul(val, NULL, 0); continue; } if (strcasecmp(key, "header_crc") == 0) { int ss = strtoul(val, NULL, 0); switch (ss) { case 16: fmt->flags |= OPT_HEADER_CRC16; break; case 32: fmt->flags |= OPT_HEADER_CRC32; break; } continue; } if (strcasecmp(key, "data_crc") == 0) { int ss = strtoul(val, NULL, 0); switch (ss) { case 16: fmt->flags |= OPT_DATA_CRC16; break; case 32: fmt->flags |= OPT_DATA_CRC32; break; } continue; } if (strcasecmp(key, "header_poly") == 0) { fmt->header_poly = strtoul(val, NULL, 0); continue; } if (strcasecmp(key, "data_poly") == 0) { fmt->data_poly = strtoul(val, NULL, 0); continue; } } file.close(); return fmt; } void load_formats(CLIClient *dev) { char name[1024]; FsFile f; FsFile dir; if (open_sd_if_needed()) { dir.open("/", O_RDONLY); dir.rewind(); while (f.openNext(&dir, O_RDONLY)) { f.getName(name, 1024); f.close(); int s = strlen(name); if (s > 4) { if ((name[s - 4] == '.') && (name[s - 3] == 'f') && (name[s - 2] == 'm') && (name[s - 1] == 't')) { dev->println(name); struct disk_format *fmt = load_format(name); if (fmt == NULL) { dev->println("Error loading format"); } else { append_format(fmt); } } } } dir.close(); } } void factory_formats() { struct disk_format *rd54 = (struct disk_format *)malloc(sizeof(struct disk_format)); rd54->name = strdup("RD54-VS2K"); rd54->comment = strdup("DEC RD54 Hard Drive on VAXstation 2000"); rd54->uit_file = NULL; rd54->uit_blob = rd54_vs2k_uit; rd54->uit_bloblen = 512; rd54->uit_start = 0; rd54->uit_cyls = 1; rd54->uit_repeats = 3; rd54->image_start = 1; rd54->cyls=1225; rd54->heads=15; rd54->sectors=17; rd54->sector_size=SECTOR_512; rd54->index_width=10; rd54->track_pregap=290; rd54->track_postgap=1100; rd54->header_postgap=41; rd54->data_postgap=911; rd54->sync_count=13; rd54->data_rate=5000000; rd54->flags = OPT_HEADER_CRC16 | OPT_DATA_CRC32; rd54->header_poly=0x1021; rd54->data_poly=0xa00805; rd54->auto_return = true; append_format(rd54); } CLI_COMMAND(cli_eject) { if (current_image) { delete(current_image); } sd.end(); _sd_is_open = false; dev->println("It is now safe to remove the SD card."); return 0; } CLI_COMMAND(cli_load_formats) { load_formats(dev); return 0; } CLI_COMMAND(cli_seek) { if (argc != 2) { dev->println("Usage: seek "); return 10; } int track = strtol(argv[1], NULL, 0); if ((track < 0) || (track >= format->cyls)) { dev->println("Track out of range"); return 10; } target_cyl = track; return 0; } void setup() { mutex_init(&mfm_sm_running); datastreamPgm.prepare(&pio, &sm, &offset); datastream_program_init(pio, sm, offset, DOUT); pio_sm_set_enabled(pio, sm, true); pio->txf[sm] = mfm_encode(0x00); pinMode(INDEX, OUTPUT); digitalWrite(INDEX, HIGH); Serial.begin(115200); //format = &RD54; factory_formats(); format = formats->fmt; load_formats(console); create_track_store(); format->slen = 0x80 << format->sector_size; format->tlen = format->slen * format->sectors; format->clock_div = F_CPU / format->data_rate / 20.0; pio_sm_set_clkdiv(pio, sm, format->clock_div); multicore_launch_core1(second_cpu_thread); CLI.setDefaultPrompt("RTmFM> "); console = CLI.addClient(Serial); CLI.addCommand("status", cli_status); CLI.addCommand("set", cli_set); CLI.addCommand("ls", cli_ls); CLI.addCommand("dir", cli_ls); CLI.addCommand("create", cli_create); CLI.addCommand("mount", cli_mount); CLI.addCommand("source", cli_source); CLI.addCommand("eject", cli_eject); CLI.addCommand("lf", cli_load_formats); CLI.addCommand("seek", cli_seek); pinMode(STEP, INPUT); pinMode(DIR, INPUT); pinMode(SEEK_DONE, OUTPUT); digitalWrite(SEEK_DONE, HIGH); pinMode(TRACK0, OUTPUT); digitalWrite(TRACK0, current_cyl == 0); attachInterrupt(STEP, do_step, FALLING); // if (open_sd_if_needed()) { // if (sd.exists("autoexec.bat")) { // execute_file("autoexec.bat", console); // } // } } void loop() { CLI.process(); cli(); uint32_t tmp_target = target_cyl; sei(); if (current_image) { if (tmp_target != current_cyl) { while (tmp_target != current_cyl) { current_cyl = tmp_target; // run_mfm_sm = false; // if (!mutex_enter_timeout_ms(&mfm_sm_running, 1000)) { // run_mfm_sm = true; // Serial.println("Timeout waiting for disk idle"); // } else { load_cyl(current_cyl, format->heads, format->sectors, format->sector_size); digitalWrite(TRACK0, current_cyl == 0); cli(); tmp_target = target_cyl; sei(); // mutex_exit(&mfm_sm_running); // run_mfm_sm = true; // } } digitalWrite(SEEK_DONE, HIGH); } } }