Thu, 17 Apr 2014 01:58:05 -0600
disabled seek delay on WD2010 (Unix seems to work fine without it)
philpem@112 | 1 | #include <stdint.h> |
philpem@112 | 2 | #include <stdbool.h> |
philpem@112 | 3 | #include <malloc.h> |
philpem@112 | 4 | #include "SDL.h" |
philpem@112 | 5 | #include "musashi/m68k.h" |
philpem@112 | 6 | #include "wd2010.h" |
philpem@112 | 7 | |
philpem@112 | 8 | #define WD2010_DEBUG |
philpem@112 | 9 | |
philpem@112 | 10 | #ifndef WD2010_DEBUG |
philpem@112 | 11 | #define NDEBUG |
philpem@112 | 12 | #endif |
philpem@112 | 13 | #include "utils.h" |
philpem@112 | 14 | |
philpem@126 | 15 | #ifndef WD2010_SEEK_DELAY |
philpem@126 | 16 | #define WD2010_SEEK_DELAY 30 |
philpem@126 | 17 | #endif |
philpem@112 | 18 | |
philpem@112 | 19 | #define CMD_ENABLE_RETRY 0x01 |
philpem@112 | 20 | #define CMD_LONG_MODE 0x02 |
philpem@112 | 21 | #define CMD_MULTI_SECTOR 0x04 |
philpem@112 | 22 | #define CMD_INTRQ_WHEN_COMPLETE 0x08 |
philpem@112 | 23 | |
philpem@112 | 24 | #define ER_BAD_BLOCK 0x80 |
philpem@112 | 25 | #define ER_CRC 0x40 |
philpem@112 | 26 | #define ER_ID_NOT_FOUND 0x10 |
philpem@112 | 27 | #define ER_ABORTED_COMMAND 0x04 |
philpem@112 | 28 | #define ER_NO_TK0 0x02 |
philpem@112 | 29 | #define ER_NO_ADDRESS_MARK 0x01 |
philpem@112 | 30 | |
philpem@112 | 31 | #define SR_BUSY 0x80 |
philpem@112 | 32 | #define SR_READY 0x40 |
philpem@112 | 33 | #define SR_WRITE_FAULT 0x20 |
philpem@112 | 34 | #define SR_SEEK_COMPLETE 0x10 |
philpem@112 | 35 | #define SR_DRQ 0x08 |
philpem@112 | 36 | #define SR_CORRECTED 0x04 |
philpem@112 | 37 | #define SR_COMMAND_IN_PROGRESS 0x02 |
philpem@112 | 38 | #define SR_ERROR 0x01 |
philpem@112 | 39 | |
philpem@112 | 40 | extern int cpu_log_enabled; |
philpem@112 | 41 | |
philpem@112 | 42 | /// WD2010 command constants |
philpem@112 | 43 | enum { |
philpem@112 | 44 | CMD_MASK = 0xF0, ///< Bit mask to detect command bits |
philpem@112 | 45 | CMD_2010_EXT = 0x00, ///< WD2010 extended commands (compute correction, set parameter) |
philpem@112 | 46 | CMD_RESTORE = 0x10, ///< Restore (recalibrate, seek to track 0) |
philpem@112 | 47 | CMD_READ_SECTOR = 0x20, ///< Read sector |
philpem@112 | 48 | CMD_WRITE_SECTOR = 0x30, ///< Write sector |
philpem@112 | 49 | CMD_SCAN_ID = 0x40, ///< Scan ID |
philpem@112 | 50 | CMD_WRITE_FORMAT = 0x50, ///< Write format |
philpem@112 | 51 | CMD_SEEK = 0x70, ///< Seek to given track |
philpem@112 | 52 | }; |
philpem@112 | 53 | |
philpem@112 | 54 | int wd2010_init(WD2010_CTX *ctx, FILE *fp, int secsz, int spt, int heads) |
philpem@112 | 55 | { |
philpem@122 | 56 | size_t filesize; |
philpem@122 | 57 | |
philpem@112 | 58 | wd2010_reset(ctx); |
philpem@122 | 59 | |
philpem@112 | 60 | // Start by finding out how big the image file is |
philpem@112 | 61 | fseek(fp, 0, SEEK_END); |
philpem@112 | 62 | filesize = ftell(fp); |
philpem@112 | 63 | fseek(fp, 0, SEEK_SET); |
philpem@112 | 64 | |
philpem@112 | 65 | // Now figure out how many tracks it contains |
philpem@122 | 66 | unsigned int tracks = filesize / secsz / spt / heads; |
philpem@112 | 67 | // Confirm... |
philpem@112 | 68 | if (tracks < 1) { |
philpem@112 | 69 | return WD2010_ERR_BAD_GEOM; |
philpem@112 | 70 | } |
philpem@112 | 71 | |
philpem@122 | 72 | LOG("WD2010 initialised, %d cylinders, %d heads, %d sectors per track", tracks, heads, spt); |
philpem@122 | 73 | |
philpem@112 | 74 | // Allocate enough memory to store one disc track |
philpem@112 | 75 | if (ctx->data) { |
philpem@112 | 76 | free(ctx->data); |
philpem@112 | 77 | } |
philpem@112 | 78 | ctx->data = malloc(secsz * spt); |
philpem@112 | 79 | if (!ctx->data) |
philpem@112 | 80 | return WD2010_ERR_NO_MEMORY; |
philpem@112 | 81 | |
philpem@112 | 82 | // Load the image and the geometry data |
philpem@112 | 83 | ctx->disc_image = fp; |
philpem@112 | 84 | ctx->geom_tracks = tracks; |
philpem@112 | 85 | ctx->geom_secsz = secsz; |
philpem@112 | 86 | ctx->geom_heads = heads; |
philpem@112 | 87 | ctx->geom_spt = spt; |
philpem@122 | 88 | |
philpem@112 | 89 | return WD2010_ERR_OK; |
philpem@112 | 90 | } |
philpem@112 | 91 | |
philpem@112 | 92 | void wd2010_reset(WD2010_CTX *ctx) |
philpem@112 | 93 | { |
philpem@112 | 94 | // track, head and sector unknown |
philpem@112 | 95 | ctx->track = ctx->head = ctx->sector = 0; |
philpem@112 | 96 | |
philpem@112 | 97 | // no IRQ pending |
philpem@112 | 98 | ctx->irq = false; |
philpem@112 | 99 | |
philpem@112 | 100 | // no data available |
philpem@112 | 101 | ctx->data_pos = ctx->data_len = 0; |
philpem@112 | 102 | |
philpem@112 | 103 | // Status register clear, not busy |
philpem@112 | 104 | ctx->status = 0; |
philpem@112 | 105 | |
philpem@112 | 106 | ctx->sector_count = 0; |
philpem@112 | 107 | ctx->sector_number = 0; |
philpem@112 | 108 | ctx->cylinder_low_reg = 0; |
philpem@112 | 109 | ctx->cylinder_high_reg = 0; |
philpem@112 | 110 | ctx->sdh = 0; |
philpem@116 | 111 | ctx->mcr2_hdsel3 = 0; |
philpem@116 | 112 | ctx->mcr2_ddrive1 = 0; |
philpem@112 | 113 | } |
philpem@112 | 114 | |
philpem@112 | 115 | void wd2010_done(WD2010_CTX *ctx) |
philpem@112 | 116 | { |
philpem@112 | 117 | // Reset the WD2010 |
philpem@112 | 118 | wd2010_reset(ctx); |
philpem@112 | 119 | |
philpem@112 | 120 | // Free any allocated memory |
philpem@112 | 121 | if (ctx->data) { |
philpem@112 | 122 | free(ctx->data); |
philpem@112 | 123 | ctx->data = NULL; |
philpem@112 | 124 | } |
philpem@112 | 125 | } |
philpem@112 | 126 | |
philpem@112 | 127 | |
philpem@112 | 128 | bool wd2010_get_irq(WD2010_CTX *ctx) |
philpem@112 | 129 | { |
philpem@112 | 130 | return ctx->irq; |
philpem@112 | 131 | } |
philpem@112 | 132 | |
philpem@112 | 133 | bool wd2010_get_drq(WD2010_CTX *ctx) |
philpem@112 | 134 | { |
philpem@112 | 135 | return (ctx->drq && ctx->data_pos < ctx->data_len); |
philpem@112 | 136 | } |
philpem@112 | 137 | |
philpem@112 | 138 | void wd2010_dma_miss(WD2010_CTX *ctx) |
philpem@112 | 139 | { |
philpem@112 | 140 | ctx->data_pos = ctx->data_len; |
philpem@112 | 141 | ctx->write_pos = 0; |
philpem@112 | 142 | ctx->status = SR_READY | SR_SEEK_COMPLETE; |
philpem@112 | 143 | ctx->irq = true; |
philpem@112 | 144 | } |
philpem@112 | 145 | |
philpem@112 | 146 | uint8_t wd2010_read_data(WD2010_CTX *ctx) |
philpem@112 | 147 | { |
philpem@112 | 148 | // If there's data in the buffer, return it. Otherwise return 0xFF. |
philpem@112 | 149 | if (ctx->data_pos < ctx->data_len) { |
philpem@123 | 150 | if (ctx->multi_sector && (ctx->data_pos > 0) && ((ctx->data_pos % ctx->geom_secsz) == 0)){ |
philpem@112 | 151 | ctx->sector_count--; |
philpem@112 | 152 | ctx->sector_number++; |
philpem@112 | 153 | } |
philpem@112 | 154 | // set IRQ if this is the last data byte |
philpem@112 | 155 | if (ctx->data_pos == (ctx->data_len-1)) { |
philpem@112 | 156 | ctx->status = SR_READY | SR_SEEK_COMPLETE; |
philpem@112 | 157 | // Set IRQ |
philpem@112 | 158 | ctx->irq = true; |
philpem@112 | 159 | ctx->drq = false; |
philpem@112 | 160 | } |
philpem@112 | 161 | // return data byte and increment pointer |
philpem@112 | 162 | return ctx->data[ctx->data_pos++]; |
philpem@112 | 163 | } else { |
philpem@112 | 164 | // empty buffer (this shouldn't happen) |
philpem@115 | 165 | LOGS("WD2010: attempt to read from empty data buffer"); |
philpem@112 | 166 | return 0xff; |
philpem@112 | 167 | } |
philpem@112 | 168 | } |
philpem@112 | 169 | |
philpem@112 | 170 | void wd2010_write_data(WD2010_CTX *ctx, uint8_t val) |
philpem@112 | 171 | { |
philpem@112 | 172 | // If we're processing a write command, and there's space in the |
philpem@112 | 173 | // buffer, allow the write. |
philpem@112 | 174 | if (ctx->write_pos >= 0 && ctx->data_pos < ctx->data_len) { |
philpem@112 | 175 | // store data byte and increment pointer |
philpem@123 | 176 | if (ctx->multi_sector && (ctx->data_pos > 0) && ((ctx->data_pos % ctx->geom_secsz) == 0)){ |
philpem@112 | 177 | ctx->sector_count--; |
philpem@112 | 178 | ctx->sector_number++; |
philpem@112 | 179 | } |
philpem@112 | 180 | ctx->data[ctx->data_pos++] = val; |
philpem@112 | 181 | // set IRQ and write data if this is the last data byte |
philpem@112 | 182 | if (ctx->data_pos == ctx->data_len) { |
philpem@112 | 183 | if (!ctx->formatting){ |
philpem@112 | 184 | fseek(ctx->disc_image, ctx->write_pos, SEEK_SET); |
philpem@112 | 185 | fwrite(ctx->data, 1, ctx->data_len, ctx->disc_image); |
philpem@112 | 186 | fflush(ctx->disc_image); |
philpem@112 | 187 | } |
philpem@112 | 188 | ctx->formatting = false; |
philpem@112 | 189 | ctx->status = SR_READY | SR_SEEK_COMPLETE; |
philpem@112 | 190 | // Set IRQ and reset write pointer |
philpem@112 | 191 | ctx->irq = true; |
philpem@112 | 192 | ctx->write_pos = -1; |
philpem@112 | 193 | ctx->drq = false; |
philpem@112 | 194 | } |
philpem@112 | 195 | }else{ |
philpem@115 | 196 | LOGS("WD2010: attempt to write to data buffer without a write command in progress"); |
philpem@112 | 197 | } |
philpem@112 | 198 | } |
philpem@112 | 199 | |
philpem@112 | 200 | uint32_t seek_complete(uint32_t interval, WD2010_CTX *ctx) |
philpem@112 | 201 | { |
philpem@112 | 202 | /*m68k_end_timeslice();*/ |
philpem@112 | 203 | ctx->status = SR_READY | SR_SEEK_COMPLETE; |
philpem@112 | 204 | ctx->irq = true; |
philpem@112 | 205 | return (0); |
philpem@112 | 206 | } |
philpem@112 | 207 | |
philpem@112 | 208 | uint32_t transfer_seek_complete(uint32_t interval, WD2010_CTX *ctx) |
philpem@112 | 209 | { |
philpem@112 | 210 | /*m68k_end_timeslice();*/ |
philpem@112 | 211 | ctx->drq = true; |
philpem@112 | 212 | return (0); |
philpem@112 | 213 | } |
philpem@112 | 214 | |
philpem@112 | 215 | uint8_t wd2010_read_reg(WD2010_CTX *ctx, uint8_t addr) |
philpem@112 | 216 | { |
philpem@112 | 217 | uint8_t temp = 0; |
philpem@112 | 218 | |
philpem@112 | 219 | /*cpu_log_enabled = 1;*/ |
philpem@112 | 220 | |
philpem@112 | 221 | switch (addr & 0x07) { |
philpem@112 | 222 | case WD2010_REG_ERROR: |
philpem@112 | 223 | return ctx->error_reg; |
philpem@112 | 224 | case WD2010_REG_SECTOR_COUNT: |
philpem@112 | 225 | return ctx->sector_count; |
philpem@112 | 226 | case WD2010_REG_SECTOR_NUMBER: |
philpem@112 | 227 | return ctx->sector_number; |
philpem@112 | 228 | case WD2010_REG_CYLINDER_HIGH: // High byte of cylinder |
philpem@112 | 229 | return ctx->cylinder_high_reg; |
philpem@112 | 230 | case WD2010_REG_CYLINDER_LOW: // Low byte of cylinder |
philpem@112 | 231 | return ctx->cylinder_low_reg; |
philpem@112 | 232 | case WD2010_REG_SDH: |
philpem@112 | 233 | return ctx->sdh; |
philpem@112 | 234 | case WD2010_REG_STATUS: // Status register |
philpem@112 | 235 | // Read from status register clears IRQ |
philpem@112 | 236 | ctx->irq = false; |
philpem@112 | 237 | // Get current status flags (set by last command) |
philpem@112 | 238 | // DRQ bit |
philpem@112 | 239 | if (ctx->cmd_has_drq) { |
philpem@112 | 240 | temp = ctx->status & ~(SR_BUSY & SR_DRQ); |
philpem@112 | 241 | temp |= (ctx->data_pos < ctx->data_len) ? SR_DRQ : 0; |
philpem@136 | 242 | LOG("\tWDFDC rd sr, has drq, pos=%zu len=%zu, sr=0x%02X", ctx->data_pos, ctx->data_len, temp); |
philpem@112 | 243 | } else { |
philpem@112 | 244 | temp = ctx->status & ~0x80; |
philpem@112 | 245 | } |
philpem@112 | 246 | /*XXX: where should 0x02 (command in progress) be set? should it be set here instead of 0x80 (busy)?*/ |
philpem@112 | 247 | // HDC is busy if there is still data in the buffer |
philpem@112 | 248 | temp |= (ctx->data_pos < ctx->data_len) ? SR_BUSY : 0; // if data in buffer, then DMA hasn't copied it yet, and we're still busy! |
philpem@112 | 249 | // TODO: also if seek delay / read delay hasn't passed (but that's for later) |
philpem@112 | 250 | /*XXX: should anything else be set here?*/ |
philpem@112 | 251 | return temp; |
philpem@112 | 252 | default: |
philpem@112 | 253 | // shut up annoying compilers which don't recognise unreachable code when they see it |
philpem@112 | 254 | // (here's looking at you, gcc!) |
philpem@112 | 255 | return 0xff; |
philpem@112 | 256 | } |
philpem@112 | 257 | } |
philpem@112 | 258 | |
philpem@112 | 259 | |
philpem@112 | 260 | void wd2010_write_reg(WD2010_CTX *ctx, uint8_t addr, uint8_t val) |
philpem@112 | 261 | { |
philpem@112 | 262 | uint8_t cmd = val & CMD_MASK; |
philpem@112 | 263 | size_t lba; |
philpem@112 | 264 | int new_track; |
philpem@112 | 265 | int sector_count; |
philpem@112 | 266 | |
philpem@112 | 267 | m68k_end_timeslice(); |
philpem@112 | 268 | |
philpem@112 | 269 | /*cpu_log_enabled = 1;*/ |
philpem@112 | 270 | |
philpem@116 | 271 | if (addr == UNIXPC_REG_MCR2) { |
philpem@116 | 272 | // The UNIX PC has an "MCR2" register with the following format: |
philpem@116 | 273 | // [ 7..2 ][1][0] |
philpem@116 | 274 | // Bits 7..2: Not used |
philpem@116 | 275 | // Bit 1: DDRIVE1 (hard disk drive 1 select - not used?) |
philpem@116 | 276 | // Bit 0: HDSEL3 (head-select bit 3) |
philpem@116 | 277 | ctx->mcr2_hdsel3 = ((val & 1) == 1); |
philpem@116 | 278 | ctx->mcr2_ddrive1 = ((val & 2) == 2); |
philpem@116 | 279 | return; |
philpem@116 | 280 | } |
philpem@116 | 281 | |
philpem@112 | 282 | switch (addr & 0x07) { |
philpem@112 | 283 | case WD2010_REG_WRITE_PRECOMP_CYLINDER: |
philpem@112 | 284 | break; |
philpem@112 | 285 | case WD2010_REG_SECTOR_COUNT: |
philpem@112 | 286 | ctx->sector_count = val; |
philpem@112 | 287 | break; |
philpem@112 | 288 | case WD2010_REG_SECTOR_NUMBER: |
philpem@112 | 289 | ctx->sector_number = val; |
philpem@112 | 290 | break; |
philpem@112 | 291 | case WD2010_REG_CYLINDER_HIGH: // High byte of cylinder |
philpem@112 | 292 | ctx->cylinder_high_reg = val; |
philpem@112 | 293 | break; |
philpem@112 | 294 | case WD2010_REG_CYLINDER_LOW: // Low byte of cylinder |
philpem@112 | 295 | ctx->cylinder_low_reg = val; |
philpem@112 | 296 | break; |
philpem@112 | 297 | case WD2010_REG_SDH: |
philpem@112 | 298 | /*XXX: remove this once the DMA page fault test passes (unless this is actually the correct behavior here)*/ |
philpem@130 | 299 | //ctx->data_pos = ctx->data_len = 0; |
philpem@112 | 300 | ctx->sdh = val; |
philpem@112 | 301 | break; |
philpem@112 | 302 | case WD2010_REG_COMMAND: // Command register |
philpem@112 | 303 | // write to command register clears interrupt request |
philpem@112 | 304 | ctx->irq = false; |
philpem@112 | 305 | ctx->error_reg = 0; |
philpem@112 | 306 | |
philpem@112 | 307 | /*cpu_log_enabled = 1;*/ |
philpem@112 | 308 | switch (cmd) { |
philpem@112 | 309 | case CMD_RESTORE: |
philpem@112 | 310 | // Restore. Set track to 0 and throw an IRQ. |
philpem@112 | 311 | ctx->track = 0; |
philpem@126 | 312 | SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_NewTimerCallback)seek_complete, ctx); |
philpem@112 | 313 | break; |
philpem@112 | 314 | case CMD_SCAN_ID: |
philpem@112 | 315 | ctx->cylinder_high_reg = (ctx->track >> 8) & 0xff; |
philpem@112 | 316 | ctx->cylinder_low_reg = ctx->track & 0xff; |
philpem@112 | 317 | ctx->sector_number = ctx->sector; |
philpem@112 | 318 | ctx->sdh = (ctx->sdh & ~7) | (ctx->head & 7); |
philpem@112 | 319 | case CMD_WRITE_FORMAT: |
philpem@112 | 320 | case CMD_SEEK: |
philpem@112 | 321 | case CMD_READ_SECTOR: |
philpem@112 | 322 | case CMD_WRITE_SECTOR: |
philpem@112 | 323 | // Seek. Seek to the track specced in the cylinder |
philpem@112 | 324 | // registers. |
philpem@112 | 325 | new_track = (ctx->cylinder_high_reg << 8) | ctx->cylinder_low_reg; |
philpem@112 | 326 | if (new_track < ctx->geom_tracks) { |
philpem@112 | 327 | ctx->track = new_track; |
philpem@112 | 328 | } else { |
philpem@112 | 329 | // Seek error. :( |
philpem@134 | 330 | LOG("WD2010 ALERT: track %d out of range", new_track); |
philpem@112 | 331 | ctx->status = SR_ERROR; |
philpem@112 | 332 | ctx->error_reg = ER_ID_NOT_FOUND; |
philpem@112 | 333 | ctx->irq = true; |
philpem@112 | 334 | break; |
philpem@112 | 335 | } |
philpem@116 | 336 | // The SDH register provides 3 head select bits; the 4th comes from MCR2. |
philpem@116 | 337 | ctx->head = (ctx->sdh & 0x07) + (ctx->mcr2_hdsel3 ? 8 : 0); |
philpem@112 | 338 | ctx->sector = ctx->sector_number; |
philpem@112 | 339 | |
philpem@112 | 340 | ctx->formatting = cmd == CMD_WRITE_FORMAT; |
philpem@112 | 341 | switch (cmd){ |
philpem@112 | 342 | case CMD_SEEK: |
philpem@126 | 343 | SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_NewTimerCallback)seek_complete, ctx); |
philpem@112 | 344 | break; |
philpem@112 | 345 | case CMD_READ_SECTOR: |
philpem@112 | 346 | /*XXX: does a separate function to set the head have to be added?*/ |
philpem@124 | 347 | LOG("WD2010: READ SECTOR cmd=%02X chs=%d:%d:%d nsectors=%d", cmd, ctx->track, ctx->head, ctx->sector, ctx->sector_count); |
philpem@112 | 348 | |
philpem@112 | 349 | // Read Sector |
philpem@112 | 350 | |
philpem@112 | 351 | // Check to see if the cyl, hd and sec are valid |
andrew@148 | 352 | if (cmd != CMD_WRITE_FORMAT && ((ctx->track > (ctx->geom_tracks-1)) || (ctx->head > (ctx->geom_heads-1)) || ((ctx->sector + ctx->sector_count - 1) > ctx->geom_spt-1))) { |
philpem@125 | 353 | LOG("*** WD2010 ALERT: CHS parameter limit exceeded! CHS=%d:%d:%d, nSecs=%d, endSec=%d maxCHS=%d:%d:%d", |
philpem@112 | 354 | ctx->track, ctx->head, ctx->sector, |
philpem@125 | 355 | ctx->sector_count, |
philpem@125 | 356 | ctx->sector + ctx->sector_count - 1, |
philpem@125 | 357 | ctx->geom_tracks-1, ctx->geom_heads-1, ctx->geom_spt); |
philpem@112 | 358 | // CHS parameters exceed limits |
philpem@112 | 359 | ctx->status = SR_ERROR; |
philpem@112 | 360 | ctx->error_reg = ER_ID_NOT_FOUND; |
philpem@112 | 361 | // Set IRQ |
philpem@112 | 362 | ctx->irq = true; |
philpem@112 | 363 | break; |
philpem@112 | 364 | } |
philpem@112 | 365 | |
philpem@112 | 366 | // reset data pointers |
philpem@112 | 367 | ctx->data_pos = ctx->data_len = 0; |
philpem@112 | 368 | |
philpem@112 | 369 | if (val & CMD_MULTI_SECTOR){ |
philpem@112 | 370 | ctx->multi_sector = 1; |
philpem@112 | 371 | sector_count = ctx->sector_count; |
philpem@112 | 372 | }else{ |
philpem@112 | 373 | ctx->multi_sector = 0; |
philpem@112 | 374 | sector_count = 1; |
philpem@112 | 375 | } |
philpem@112 | 376 | for (int i=0; i<sector_count; i++) { |
philpem@112 | 377 | // Calculate the LBA address of the required sector |
philpem@112 | 378 | // LBA = (C * nHeads * nSectors) + (H * nSectors) + S - 1 |
philpem@112 | 379 | lba = (((ctx->track * ctx->geom_heads * ctx->geom_spt) + (ctx->head * ctx->geom_spt) + ctx->sector) + i); |
philpem@112 | 380 | // convert LBA to byte address |
philpem@112 | 381 | lba *= ctx->geom_secsz; |
philpem@136 | 382 | LOG("\tREAD lba = %zu", lba); |
philpem@112 | 383 | |
philpem@112 | 384 | // Read the sector from the file |
philpem@112 | 385 | fseek(ctx->disc_image, lba, SEEK_SET); |
philpem@112 | 386 | // TODO: check fread return value! if < secsz, BAIL! (call it a crc error or secnotfound maybe? also log to stderr) |
philpem@112 | 387 | ctx->data_len += fread(&ctx->data[ctx->data_len], 1, ctx->geom_secsz, ctx->disc_image); |
philpem@136 | 388 | LOG("\tREAD len=%zu, pos=%zu, ssz=%d", ctx->data_len, ctx->data_pos, ctx->geom_secsz); |
philpem@112 | 389 | } |
philpem@112 | 390 | |
philpem@112 | 391 | ctx->status = 0; |
philpem@112 | 392 | ctx->status |= (ctx->data_pos < ctx->data_len) ? SR_DRQ | SR_COMMAND_IN_PROGRESS | SR_BUSY : 0x00; |
andrew@149 | 393 | /*SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_NewTimerCallback)transfer_seek_complete, ctx);*/ |
andrew@149 | 394 | ctx->drq = true; |
philpem@112 | 395 | |
philpem@112 | 396 | break; |
philpem@112 | 397 | case CMD_WRITE_FORMAT: |
philpem@112 | 398 | ctx->sector = 0; |
philpem@112 | 399 | case CMD_WRITE_SECTOR: |
philpem@124 | 400 | LOG("WD2010: WRITE SECTOR cmd=%02X chs=%d:%d:%d nsectors=%d", cmd, ctx->track, ctx->head, ctx->sector, ctx->sector_count); |
philpem@124 | 401 | // Write Sector |
philpem@112 | 402 | |
philpem@112 | 403 | // Check to see if the cyl, hd and sec are valid |
philpem@134 | 404 | if (cmd != CMD_WRITE_FORMAT && ((ctx->track > (ctx->geom_tracks-1)) || (ctx->head > (ctx->geom_heads-1)) || ((ctx->sector + ctx->sector_count - 1) > ctx->geom_spt-1))) { |
philpem@125 | 405 | LOG("*** WD2010 ALERT: CHS parameter limit exceeded! CHS=%d:%d:%d, nSecs=%d, endSec=%d maxCHS=%d:%d:%d", |
philpem@112 | 406 | ctx->track, ctx->head, ctx->sector, |
philpem@125 | 407 | ctx->sector_count, |
philpem@125 | 408 | ctx->sector + ctx->sector_count - 1, |
philpem@112 | 409 | ctx->geom_tracks-1, ctx->geom_heads-1, ctx->geom_spt); |
philpem@112 | 410 | // CHS parameters exceed limits |
philpem@112 | 411 | ctx->status = SR_ERROR; |
philpem@112 | 412 | ctx->error_reg = ER_ID_NOT_FOUND; |
philpem@112 | 413 | // Set IRQ |
philpem@112 | 414 | ctx->irq = true; |
philpem@112 | 415 | break; |
philpem@112 | 416 | } |
philpem@112 | 417 | |
philpem@112 | 418 | // reset data pointers |
philpem@112 | 419 | ctx->data_pos = ctx->data_len = 0; |
philpem@112 | 420 | |
philpem@112 | 421 | if (val & CMD_MULTI_SECTOR){ |
philpem@112 | 422 | ctx->multi_sector = 1; |
philpem@112 | 423 | sector_count = ctx->sector_count; |
philpem@112 | 424 | }else{ |
philpem@112 | 425 | ctx->multi_sector = 0; |
philpem@112 | 426 | sector_count = 1; |
philpem@112 | 427 | } |
philpem@112 | 428 | ctx->data_len = ctx->geom_secsz * sector_count; |
philpem@112 | 429 | lba = (((ctx->track * ctx->geom_heads * ctx->geom_spt) + (ctx->head * ctx->geom_spt) + ctx->sector)); |
philpem@112 | 430 | // convert LBA to byte address |
philpem@124 | 431 | ctx->write_pos = (lba *= ctx->geom_secsz); |
philpem@124 | 432 | LOG("\tWRITE lba = %zu", lba); |
philpem@112 | 433 | |
philpem@112 | 434 | ctx->status = 0; |
philpem@112 | 435 | ctx->status |= (ctx->data_pos < ctx->data_len) ? SR_DRQ | SR_COMMAND_IN_PROGRESS | SR_BUSY : 0x00; |
andrew@149 | 436 | /*SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_NewTimerCallback)transfer_seek_complete, ctx);*/ |
andrew@149 | 437 | ctx->drq = true; |
philpem@112 | 438 | |
philpem@112 | 439 | break; |
philpem@112 | 440 | default: |
philpem@112 | 441 | LOG("WD2010: invalid seeking command %x (this shouldn't happen!)\n", cmd); |
philpem@112 | 442 | break; |
philpem@112 | 443 | } |
philpem@112 | 444 | break; |
philpem@112 | 445 | case CMD_2010_EXT: /* not implemented */ |
philpem@112 | 446 | default: |
philpem@112 | 447 | LOG("WD2010: unknown command %x\n", cmd); |
philpem@112 | 448 | ctx->status = SR_ERROR; |
philpem@112 | 449 | ctx->error_reg = ER_ABORTED_COMMAND; |
philpem@112 | 450 | ctx->irq = true; |
philpem@112 | 451 | break; |
philpem@112 | 452 | } |
philpem@112 | 453 | break; |
philpem@112 | 454 | |
philpem@112 | 455 | } |
philpem@112 | 456 | } |
philpem@112 | 457 |