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