Sat, 17 Nov 2012 19:18:29 +0000
add HDD support + fixes
Patch-Author: Andrew Warkentin <andreww591!gmail>
Patch-Message-ID: <50A772FC.8020009@gmail.com>
I have added floppy write support, full hard disk emulation, and proper handling of DMA page faults to FreeBee. I also fixed the floppy step commands, changed the "force interrupt" floppy command to generate a type 1 status, and changed the DMA address counter to reset to 3fff when a transfer completes (which is what Unix seems to expect - without it, the kernel says that the floppy isn't ready). The floppy, hard disk, and DMA page fault tests all pass. Initializing hard disks and floppies also works (the geometry for both is still fixed by the size of the image, though, and format commands only appear to succeed, but they don't modify the image). Unix still doesn't run, though (it hangs after reading some sectors from the floppy).
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@112 | 54 | long filesize; |
philpem@112 | 55 | wd2010_reset(ctx); |
philpem@112 | 56 | // Start by finding out how big the image file is |
philpem@112 | 57 | fseek(fp, 0, SEEK_END); |
philpem@112 | 58 | filesize = ftell(fp); |
philpem@112 | 59 | fseek(fp, 0, SEEK_SET); |
philpem@112 | 60 | |
philpem@112 | 61 | // Now figure out how many tracks it contains |
philpem@112 | 62 | int tracks = filesize / secsz / spt / heads; |
philpem@112 | 63 | // Confirm... |
philpem@112 | 64 | if (tracks < 1) { |
philpem@112 | 65 | return WD2010_ERR_BAD_GEOM; |
philpem@112 | 66 | } |
philpem@112 | 67 | |
philpem@112 | 68 | // Allocate enough memory to store one disc track |
philpem@112 | 69 | if (ctx->data) { |
philpem@112 | 70 | free(ctx->data); |
philpem@112 | 71 | } |
philpem@112 | 72 | ctx->data = malloc(secsz * spt); |
philpem@112 | 73 | if (!ctx->data) |
philpem@112 | 74 | return WD2010_ERR_NO_MEMORY; |
philpem@112 | 75 | |
philpem@112 | 76 | // Load the image and the geometry data |
philpem@112 | 77 | ctx->disc_image = fp; |
philpem@112 | 78 | ctx->geom_tracks = tracks; |
philpem@112 | 79 | ctx->geom_secsz = secsz; |
philpem@112 | 80 | ctx->geom_heads = heads; |
philpem@112 | 81 | ctx->geom_spt = spt; |
philpem@112 | 82 | return WD2010_ERR_OK; |
philpem@112 | 83 | |
philpem@112 | 84 | } |
philpem@112 | 85 | |
philpem@112 | 86 | void wd2010_reset(WD2010_CTX *ctx) |
philpem@112 | 87 | { |
philpem@112 | 88 | // track, head and sector unknown |
philpem@112 | 89 | ctx->track = ctx->head = ctx->sector = 0; |
philpem@112 | 90 | |
philpem@112 | 91 | // no IRQ pending |
philpem@112 | 92 | ctx->irq = false; |
philpem@112 | 93 | |
philpem@112 | 94 | // no data available |
philpem@112 | 95 | ctx->data_pos = ctx->data_len = 0; |
philpem@112 | 96 | |
philpem@112 | 97 | // Status register clear, not busy |
philpem@112 | 98 | ctx->status = 0; |
philpem@112 | 99 | |
philpem@112 | 100 | ctx->sector_count = 0; |
philpem@112 | 101 | ctx->sector_number = 0; |
philpem@112 | 102 | ctx->cylinder_low_reg = 0; |
philpem@112 | 103 | ctx->cylinder_high_reg = 0; |
philpem@112 | 104 | ctx->sdh = 0; |
philpem@112 | 105 | } |
philpem@112 | 106 | |
philpem@112 | 107 | void wd2010_done(WD2010_CTX *ctx) |
philpem@112 | 108 | { |
philpem@112 | 109 | // Reset the WD2010 |
philpem@112 | 110 | wd2010_reset(ctx); |
philpem@112 | 111 | |
philpem@112 | 112 | // Free any allocated memory |
philpem@112 | 113 | if (ctx->data) { |
philpem@112 | 114 | free(ctx->data); |
philpem@112 | 115 | ctx->data = NULL; |
philpem@112 | 116 | } |
philpem@112 | 117 | } |
philpem@112 | 118 | |
philpem@112 | 119 | |
philpem@112 | 120 | bool wd2010_get_irq(WD2010_CTX *ctx) |
philpem@112 | 121 | { |
philpem@112 | 122 | return ctx->irq; |
philpem@112 | 123 | } |
philpem@112 | 124 | |
philpem@112 | 125 | bool wd2010_get_drq(WD2010_CTX *ctx) |
philpem@112 | 126 | { |
philpem@112 | 127 | return (ctx->drq && ctx->data_pos < ctx->data_len); |
philpem@112 | 128 | } |
philpem@112 | 129 | |
philpem@112 | 130 | void wd2010_dma_miss(WD2010_CTX *ctx) |
philpem@112 | 131 | { |
philpem@112 | 132 | ctx->data_pos = ctx->data_len; |
philpem@112 | 133 | ctx->write_pos = 0; |
philpem@112 | 134 | ctx->status = SR_READY | SR_SEEK_COMPLETE; |
philpem@112 | 135 | ctx->irq = true; |
philpem@112 | 136 | } |
philpem@112 | 137 | |
philpem@112 | 138 | uint8_t wd2010_read_data(WD2010_CTX *ctx) |
philpem@112 | 139 | { |
philpem@112 | 140 | // If there's data in the buffer, return it. Otherwise return 0xFF. |
philpem@112 | 141 | if (ctx->data_pos < ctx->data_len) { |
philpem@112 | 142 | if (ctx->multi_sector && ctx->data_pos & !(ctx->data_pos & ~(ctx->geom_secsz - 1))){ |
philpem@112 | 143 | ctx->sector_count--; |
philpem@112 | 144 | ctx->sector_number++; |
philpem@112 | 145 | } |
philpem@112 | 146 | // set IRQ if this is the last data byte |
philpem@112 | 147 | if (ctx->data_pos == (ctx->data_len-1)) { |
philpem@112 | 148 | ctx->status = SR_READY | SR_SEEK_COMPLETE; |
philpem@112 | 149 | // Set IRQ |
philpem@112 | 150 | ctx->irq = true; |
philpem@112 | 151 | ctx->drq = false; |
philpem@112 | 152 | } |
philpem@112 | 153 | // return data byte and increment pointer |
philpem@112 | 154 | return ctx->data[ctx->data_pos++]; |
philpem@112 | 155 | } else { |
philpem@112 | 156 | // empty buffer (this shouldn't happen) |
philpem@112 | 157 | LOG("WD2010: attempt to read from empty data buffer"); |
philpem@112 | 158 | return 0xff; |
philpem@112 | 159 | } |
philpem@112 | 160 | } |
philpem@112 | 161 | |
philpem@112 | 162 | void wd2010_write_data(WD2010_CTX *ctx, uint8_t val) |
philpem@112 | 163 | { |
philpem@112 | 164 | // If we're processing a write command, and there's space in the |
philpem@112 | 165 | // buffer, allow the write. |
philpem@112 | 166 | if (ctx->write_pos >= 0 && ctx->data_pos < ctx->data_len) { |
philpem@112 | 167 | // store data byte and increment pointer |
philpem@112 | 168 | if (ctx->multi_sector && ctx->data_pos & !(ctx->data_pos & ~(ctx->geom_secsz - 1))){ |
philpem@112 | 169 | ctx->sector_count--; |
philpem@112 | 170 | ctx->sector_number++; |
philpem@112 | 171 | } |
philpem@112 | 172 | ctx->data[ctx->data_pos++] = val; |
philpem@112 | 173 | // set IRQ and write data if this is the last data byte |
philpem@112 | 174 | if (ctx->data_pos == ctx->data_len) { |
philpem@112 | 175 | if (!ctx->formatting){ |
philpem@112 | 176 | fseek(ctx->disc_image, ctx->write_pos, SEEK_SET); |
philpem@112 | 177 | fwrite(ctx->data, 1, ctx->data_len, ctx->disc_image); |
philpem@112 | 178 | fflush(ctx->disc_image); |
philpem@112 | 179 | } |
philpem@112 | 180 | ctx->formatting = false; |
philpem@112 | 181 | ctx->status = SR_READY | SR_SEEK_COMPLETE; |
philpem@112 | 182 | // Set IRQ and reset write pointer |
philpem@112 | 183 | ctx->irq = true; |
philpem@112 | 184 | ctx->write_pos = -1; |
philpem@112 | 185 | ctx->drq = false; |
philpem@112 | 186 | } |
philpem@112 | 187 | }else{ |
philpem@112 | 188 | LOG("WD2010: attempt to write to data buffer without a write command in progress"); |
philpem@112 | 189 | } |
philpem@112 | 190 | } |
philpem@112 | 191 | |
philpem@112 | 192 | uint32_t seek_complete(uint32_t interval, WD2010_CTX *ctx) |
philpem@112 | 193 | { |
philpem@112 | 194 | /*m68k_end_timeslice();*/ |
philpem@112 | 195 | ctx->status = SR_READY | SR_SEEK_COMPLETE; |
philpem@112 | 196 | ctx->irq = true; |
philpem@112 | 197 | return (0); |
philpem@112 | 198 | } |
philpem@112 | 199 | |
philpem@112 | 200 | uint32_t transfer_seek_complete(uint32_t interval, WD2010_CTX *ctx) |
philpem@112 | 201 | { |
philpem@112 | 202 | /*m68k_end_timeslice();*/ |
philpem@112 | 203 | ctx->drq = true; |
philpem@112 | 204 | return (0); |
philpem@112 | 205 | } |
philpem@112 | 206 | |
philpem@112 | 207 | uint8_t wd2010_read_reg(WD2010_CTX *ctx, uint8_t addr) |
philpem@112 | 208 | { |
philpem@112 | 209 | uint8_t temp = 0; |
philpem@112 | 210 | |
philpem@112 | 211 | /*cpu_log_enabled = 1;*/ |
philpem@112 | 212 | |
philpem@112 | 213 | switch (addr & 0x07) { |
philpem@112 | 214 | case WD2010_REG_ERROR: |
philpem@112 | 215 | return ctx->error_reg; |
philpem@112 | 216 | case WD2010_REG_SECTOR_COUNT: |
philpem@112 | 217 | return ctx->sector_count; |
philpem@112 | 218 | case WD2010_REG_SECTOR_NUMBER: |
philpem@112 | 219 | return ctx->sector_number; |
philpem@112 | 220 | case WD2010_REG_CYLINDER_HIGH: // High byte of cylinder |
philpem@112 | 221 | return ctx->cylinder_high_reg; |
philpem@112 | 222 | case WD2010_REG_CYLINDER_LOW: // Low byte of cylinder |
philpem@112 | 223 | return ctx->cylinder_low_reg; |
philpem@112 | 224 | case WD2010_REG_SDH: |
philpem@112 | 225 | return ctx->sdh; |
philpem@112 | 226 | case WD2010_REG_STATUS: // Status register |
philpem@112 | 227 | // Read from status register clears IRQ |
philpem@112 | 228 | ctx->irq = false; |
philpem@112 | 229 | // Get current status flags (set by last command) |
philpem@112 | 230 | // DRQ bit |
philpem@112 | 231 | if (ctx->cmd_has_drq) { |
philpem@112 | 232 | temp = ctx->status & ~(SR_BUSY & SR_DRQ); |
philpem@112 | 233 | temp |= (ctx->data_pos < ctx->data_len) ? SR_DRQ : 0; |
philpem@112 | 234 | LOG("\tWDFDC rd sr, has drq, pos=%lu len=%lu, sr=0x%02X", ctx->data_pos, ctx->data_len, temp); |
philpem@112 | 235 | } else { |
philpem@112 | 236 | temp = ctx->status & ~0x80; |
philpem@112 | 237 | } |
philpem@112 | 238 | /*XXX: where should 0x02 (command in progress) be set? should it be set here instead of 0x80 (busy)?*/ |
philpem@112 | 239 | // HDC is busy if there is still data in the buffer |
philpem@112 | 240 | 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 | 241 | // TODO: also if seek delay / read delay hasn't passed (but that's for later) |
philpem@112 | 242 | /*XXX: should anything else be set here?*/ |
philpem@112 | 243 | return temp; |
philpem@112 | 244 | default: |
philpem@112 | 245 | // shut up annoying compilers which don't recognise unreachable code when they see it |
philpem@112 | 246 | // (here's looking at you, gcc!) |
philpem@112 | 247 | return 0xff; |
philpem@112 | 248 | } |
philpem@112 | 249 | } |
philpem@112 | 250 | |
philpem@112 | 251 | |
philpem@112 | 252 | void wd2010_write_reg(WD2010_CTX *ctx, uint8_t addr, uint8_t val) |
philpem@112 | 253 | { |
philpem@112 | 254 | uint8_t cmd = val & CMD_MASK; |
philpem@112 | 255 | size_t lba; |
philpem@112 | 256 | int new_track; |
philpem@112 | 257 | int sector_count; |
philpem@112 | 258 | |
philpem@112 | 259 | m68k_end_timeslice(); |
philpem@112 | 260 | |
philpem@112 | 261 | /*cpu_log_enabled = 1;*/ |
philpem@112 | 262 | |
philpem@112 | 263 | switch (addr & 0x07) { |
philpem@112 | 264 | case WD2010_REG_WRITE_PRECOMP_CYLINDER: |
philpem@112 | 265 | break; |
philpem@112 | 266 | case WD2010_REG_SECTOR_COUNT: |
philpem@112 | 267 | ctx->sector_count = val; |
philpem@112 | 268 | break; |
philpem@112 | 269 | case WD2010_REG_SECTOR_NUMBER: |
philpem@112 | 270 | ctx->sector_number = val; |
philpem@112 | 271 | break; |
philpem@112 | 272 | case WD2010_REG_CYLINDER_HIGH: // High byte of cylinder |
philpem@112 | 273 | ctx->cylinder_high_reg = val; |
philpem@112 | 274 | break; |
philpem@112 | 275 | case WD2010_REG_CYLINDER_LOW: // Low byte of cylinder |
philpem@112 | 276 | ctx->cylinder_low_reg = val; |
philpem@112 | 277 | break; |
philpem@112 | 278 | case WD2010_REG_SDH: |
philpem@112 | 279 | /*XXX: remove this once the DMA page fault test passes (unless this is actually the correct behavior here)*/ |
philpem@112 | 280 | ctx->data_pos = ctx->data_len = 0; |
philpem@112 | 281 | ctx->sdh = val; |
philpem@112 | 282 | break; |
philpem@112 | 283 | case WD2010_REG_COMMAND: // Command register |
philpem@112 | 284 | // write to command register clears interrupt request |
philpem@112 | 285 | ctx->irq = false; |
philpem@112 | 286 | ctx->error_reg = 0; |
philpem@112 | 287 | |
philpem@112 | 288 | /*cpu_log_enabled = 1;*/ |
philpem@112 | 289 | switch (cmd) { |
philpem@112 | 290 | case CMD_RESTORE: |
philpem@112 | 291 | // Restore. Set track to 0 and throw an IRQ. |
philpem@112 | 292 | ctx->track = 0; |
philpem@112 | 293 | SDL_AddTimer(SEEK_DELAY, (SDL_NewTimerCallback)seek_complete, ctx); |
philpem@112 | 294 | break; |
philpem@112 | 295 | case CMD_SCAN_ID: |
philpem@112 | 296 | ctx->cylinder_high_reg = (ctx->track >> 8) & 0xff; |
philpem@112 | 297 | ctx->cylinder_low_reg = ctx->track & 0xff; |
philpem@112 | 298 | ctx->sector_number = ctx->sector; |
philpem@112 | 299 | ctx->sdh = (ctx->sdh & ~7) | (ctx->head & 7); |
philpem@112 | 300 | case CMD_WRITE_FORMAT: |
philpem@112 | 301 | case CMD_SEEK: |
philpem@112 | 302 | case CMD_READ_SECTOR: |
philpem@112 | 303 | case CMD_WRITE_SECTOR: |
philpem@112 | 304 | // Seek. Seek to the track specced in the cylinder |
philpem@112 | 305 | // registers. |
philpem@112 | 306 | new_track = (ctx->cylinder_high_reg << 8) | ctx->cylinder_low_reg; |
philpem@112 | 307 | if (new_track < ctx->geom_tracks) { |
philpem@112 | 308 | ctx->track = new_track; |
philpem@112 | 309 | } else { |
philpem@112 | 310 | // Seek error. :( |
philpem@112 | 311 | ctx->status = SR_ERROR; |
philpem@112 | 312 | ctx->error_reg = ER_ID_NOT_FOUND; |
philpem@112 | 313 | ctx->irq = true; |
philpem@112 | 314 | break; |
philpem@112 | 315 | } |
philpem@112 | 316 | ctx->head = ctx->sdh & 0x07; |
philpem@112 | 317 | ctx->sector = ctx->sector_number; |
philpem@112 | 318 | |
philpem@112 | 319 | ctx->formatting = cmd == CMD_WRITE_FORMAT; |
philpem@112 | 320 | switch (cmd){ |
philpem@112 | 321 | case CMD_SEEK: |
philpem@112 | 322 | SDL_AddTimer(SEEK_DELAY, (SDL_NewTimerCallback)seek_complete, ctx); |
philpem@112 | 323 | break; |
philpem@112 | 324 | case CMD_READ_SECTOR: |
philpem@112 | 325 | /*XXX: does a separate function to set the head have to be added?*/ |
philpem@112 | 326 | LOG("WD2010: READ SECTOR cmd=%02X chs=%d:%d:%d", cmd, ctx->track, ctx->head, ctx->sector); |
philpem@112 | 327 | |
philpem@112 | 328 | // Read Sector |
philpem@112 | 329 | |
philpem@112 | 330 | // Check to see if the cyl, hd and sec are valid |
philpem@112 | 331 | if ((ctx->track > (ctx->geom_tracks-1)) || (ctx->head > (ctx->geom_heads-1)) || (ctx->sector > ctx->geom_spt - 1)) { |
philpem@112 | 332 | LOG("*** WD2010 ALERT: CHS parameter limit exceeded! CHS=%d:%d:%d, maxCHS=%d:%d:%d", |
philpem@112 | 333 | ctx->track, ctx->head, ctx->sector, |
philpem@112 | 334 | ctx->geom_tracks-1, ctx->geom_heads-1, ctx->geom_spt-1); |
philpem@112 | 335 | // CHS parameters exceed limits |
philpem@112 | 336 | ctx->status = SR_ERROR; |
philpem@112 | 337 | ctx->error_reg = ER_ID_NOT_FOUND; |
philpem@112 | 338 | // Set IRQ |
philpem@112 | 339 | ctx->irq = true; |
philpem@112 | 340 | break; |
philpem@112 | 341 | } |
philpem@112 | 342 | |
philpem@112 | 343 | // reset data pointers |
philpem@112 | 344 | ctx->data_pos = ctx->data_len = 0; |
philpem@112 | 345 | |
philpem@112 | 346 | if (val & CMD_MULTI_SECTOR){ |
philpem@112 | 347 | ctx->multi_sector = 1; |
philpem@112 | 348 | sector_count = ctx->sector_count; |
philpem@112 | 349 | }else{ |
philpem@112 | 350 | ctx->multi_sector = 0; |
philpem@112 | 351 | sector_count = 1; |
philpem@112 | 352 | } |
philpem@112 | 353 | for (int i=0; i<sector_count; i++) { |
philpem@112 | 354 | // Calculate the LBA address of the required sector |
philpem@112 | 355 | // LBA = (C * nHeads * nSectors) + (H * nSectors) + S - 1 |
philpem@112 | 356 | lba = (((ctx->track * ctx->geom_heads * ctx->geom_spt) + (ctx->head * ctx->geom_spt) + ctx->sector) + i); |
philpem@112 | 357 | // convert LBA to byte address |
philpem@112 | 358 | lba *= ctx->geom_secsz; |
philpem@112 | 359 | LOG("\tREAD lba = %lu", lba); |
philpem@112 | 360 | |
philpem@112 | 361 | // Read the sector from the file |
philpem@112 | 362 | fseek(ctx->disc_image, lba, SEEK_SET); |
philpem@112 | 363 | // TODO: check fread return value! if < secsz, BAIL! (call it a crc error or secnotfound maybe? also log to stderr) |
philpem@112 | 364 | ctx->data_len += fread(&ctx->data[ctx->data_len], 1, ctx->geom_secsz, ctx->disc_image); |
philpem@112 | 365 | LOG("\tREAD len=%lu, pos=%lu, ssz=%d", ctx->data_len, ctx->data_pos, ctx->geom_secsz); |
philpem@112 | 366 | } |
philpem@112 | 367 | |
philpem@112 | 368 | ctx->status = 0; |
philpem@112 | 369 | ctx->status |= (ctx->data_pos < ctx->data_len) ? SR_DRQ | SR_COMMAND_IN_PROGRESS | SR_BUSY : 0x00; |
philpem@112 | 370 | SDL_AddTimer(SEEK_DELAY, (SDL_NewTimerCallback)transfer_seek_complete, ctx); |
philpem@112 | 371 | |
philpem@112 | 372 | break; |
philpem@112 | 373 | case CMD_WRITE_FORMAT: |
philpem@112 | 374 | ctx->sector = 0; |
philpem@112 | 375 | case CMD_WRITE_SECTOR: |
philpem@112 | 376 | LOG("WD2010: WRITE SECTOR cmd=%02X chs=%d:%d:%d", cmd, ctx->track, ctx->head, ctx->sector); |
philpem@112 | 377 | // Read Sector |
philpem@112 | 378 | |
philpem@112 | 379 | // Check to see if the cyl, hd and sec are valid |
philpem@112 | 380 | if ((ctx->track > (ctx->geom_tracks-1)) || (ctx->head > (ctx->geom_heads-1)) || (ctx->sector > ctx->geom_spt-1)) { |
philpem@112 | 381 | LOG("*** WD2010 ALERT: CHS parameter limit exceeded! CHS=%d:%d:%d, maxCHS=%d:%d:%d", |
philpem@112 | 382 | ctx->track, ctx->head, ctx->sector, |
philpem@112 | 383 | ctx->geom_tracks-1, ctx->geom_heads-1, ctx->geom_spt); |
philpem@112 | 384 | // CHS parameters exceed limits |
philpem@112 | 385 | ctx->status = SR_ERROR; |
philpem@112 | 386 | ctx->error_reg = ER_ID_NOT_FOUND; |
philpem@112 | 387 | // Set IRQ |
philpem@112 | 388 | ctx->irq = true; |
philpem@112 | 389 | break; |
philpem@112 | 390 | } |
philpem@112 | 391 | |
philpem@112 | 392 | // reset data pointers |
philpem@112 | 393 | ctx->data_pos = ctx->data_len = 0; |
philpem@112 | 394 | |
philpem@112 | 395 | if (val & CMD_MULTI_SECTOR){ |
philpem@112 | 396 | ctx->multi_sector = 1; |
philpem@112 | 397 | sector_count = ctx->sector_count; |
philpem@112 | 398 | }else{ |
philpem@112 | 399 | ctx->multi_sector = 0; |
philpem@112 | 400 | sector_count = 1; |
philpem@112 | 401 | } |
philpem@112 | 402 | ctx->data_len = ctx->geom_secsz * sector_count; |
philpem@112 | 403 | lba = (((ctx->track * ctx->geom_heads * ctx->geom_spt) + (ctx->head * ctx->geom_spt) + ctx->sector)); |
philpem@112 | 404 | // convert LBA to byte address |
philpem@112 | 405 | ctx->write_pos = lba * ctx->geom_secsz; |
philpem@112 | 406 | LOG("\tWRITE lba = %lu", lba); |
philpem@112 | 407 | |
philpem@112 | 408 | ctx->status = 0; |
philpem@112 | 409 | ctx->status |= (ctx->data_pos < ctx->data_len) ? SR_DRQ | SR_COMMAND_IN_PROGRESS | SR_BUSY : 0x00; |
philpem@112 | 410 | SDL_AddTimer(SEEK_DELAY, (SDL_NewTimerCallback)transfer_seek_complete, ctx); |
philpem@112 | 411 | |
philpem@112 | 412 | break; |
philpem@112 | 413 | default: |
philpem@112 | 414 | LOG("WD2010: invalid seeking command %x (this shouldn't happen!)\n", cmd); |
philpem@112 | 415 | break; |
philpem@112 | 416 | } |
philpem@112 | 417 | break; |
philpem@112 | 418 | case CMD_2010_EXT: /* not implemented */ |
philpem@112 | 419 | default: |
philpem@112 | 420 | LOG("WD2010: unknown command %x\n", cmd); |
philpem@112 | 421 | ctx->status = SR_ERROR; |
philpem@112 | 422 | ctx->error_reg = ER_ABORTED_COMMAND; |
philpem@112 | 423 | ctx->irq = true; |
philpem@112 | 424 | break; |
philpem@112 | 425 | } |
philpem@112 | 426 | break; |
philpem@112 | 427 | |
philpem@112 | 428 | } |
philpem@112 | 429 | } |
philpem@112 | 430 |