Tue, 15 Nov 2011 10:12:37 +0000
[musashi] Fix handling of bus errors
Patch-Author: Andrew Warkentin <andreww591!gmail>
Patch-MessageID: <4EC200CE.2020304@gmail.com>
I have fixed the first page fault test failure in FreeBee (the page fault test now hangs rather than errors out, because it is trying to read from the hard drive to test DMA page faults).
There were actually two bugs (the first bug was masking the second one).
First, the ancient version of Musashi that you used is unable to properly resume from bus errors that happen in the middle of certain instructions (some instructions are fetched in stages, with the PC being advanced to each part of the instruction, so basically what happens is the CPU core attempts to read the memory location referenced by the first operand, the bus error occurs, causing the PC to jump to the exception vector, but the faulting instruction is still in the middle of being fetched, so the PC is then advanced past the beginning of the exception handler). I fixed this by delaying the jump to the bus error vector until after the faulting instruction finishes.
The second bug is simpler - you had the UDS and LDS bits in BSR0 inverted (they are supposed to be active low).
philpem@48 | 1 | #include <stdint.h> |
philpem@48 | 2 | #include <stdbool.h> |
philpem@49 | 3 | #include <malloc.h> |
philpem@53 | 4 | #include "musashi/m68k.h" |
philpem@48 | 5 | #include "wd279x.h" |
philpem@48 | 6 | |
philpem@73 | 7 | #ifndef WD279X_DEBUG |
philpem@71 | 8 | #define NDEBUG |
philpem@73 | 9 | #endif |
philpem@71 | 10 | #include "utils.h" |
philpem@71 | 11 | |
philpem@48 | 12 | /// WD2797 command constants |
philpem@48 | 13 | enum { |
philpem@48 | 14 | CMD_MASK = 0xF0, ///< Bit mask to detect command bits |
philpem@48 | 15 | CMD_RESTORE = 0x00, ///< Restore (recalibrate, seek to track 0) |
philpem@48 | 16 | CMD_SEEK = 0x10, ///< Seek to given track |
philpem@48 | 17 | CMD_STEP = 0x20, ///< Step |
philpem@48 | 18 | CMD_STEP_TU = 0x30, ///< Step and update track register |
philpem@48 | 19 | CMD_STEPIN = 0x40, ///< Step In |
philpem@48 | 20 | CMD_STEPIN_TU = 0x50, ///< Step In and update track register |
philpem@48 | 21 | CMD_STEPOUT = 0x60, ///< Step Out |
philpem@48 | 22 | CMD_STEPOUT_TU = 0x70, ///< Step Out and update track register |
philpem@48 | 23 | CMD_READ_SECTOR = 0x80, ///< Read Sector |
philpem@48 | 24 | CMD_READ_SECTOR_MULTI = 0x90, ///< Read Multiple Sectors |
philpem@48 | 25 | CMD_WRITE_SECTOR = 0xA0, ///< Write Sector |
philpem@48 | 26 | CMD_WRITE_SECTOR_MULTI = 0xB0, ///< Write Multiple Sectors |
philpem@48 | 27 | CMD_READ_ADDRESS = 0xC0, ///< Read Address (IDAM contents) |
philpem@48 | 28 | CMD_FORCE_INTERRUPT = 0xD0, ///< Force Interrupt |
philpem@48 | 29 | CMD_READ_TRACK = 0xE0, ///< Read Track |
philpem@48 | 30 | CMD_FORMAT_TRACK = 0xF0 ///< Format Track |
philpem@48 | 31 | }; |
philpem@48 | 32 | |
philpem@48 | 33 | |
philpem@49 | 34 | void wd2797_init(WD2797_CTX *ctx) |
philpem@48 | 35 | { |
philpem@48 | 36 | // track, head and sector unknown |
philpem@49 | 37 | ctx->track = ctx->head = ctx->sector = 0; |
philpem@48 | 38 | |
philpem@49 | 39 | // no IRQ pending |
philpem@76 | 40 | ctx->irq = false; |
philpem@48 | 41 | |
philpem@48 | 42 | // no data available |
philpem@49 | 43 | ctx->data_pos = ctx->data_len = 0; |
philpem@49 | 44 | ctx->data = NULL; |
philpem@49 | 45 | |
philpem@76 | 46 | // Status register clear, not busy; type1 command |
philpem@49 | 47 | ctx->status = 0; |
philpem@76 | 48 | ctx->cmd_has_drq = false; |
philpem@49 | 49 | |
philpem@49 | 50 | // Clear data register |
philpem@49 | 51 | ctx->data_reg = 0; |
philpem@49 | 52 | |
philpem@76 | 53 | // Last step direction = "towards zero" |
philpem@49 | 54 | ctx->last_step_dir = -1; |
philpem@48 | 55 | |
philpem@48 | 56 | // No disc image loaded |
philpem@48 | 57 | ctx->disc_image = NULL; |
philpem@49 | 58 | ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = ctx->geom_tracks = 0; |
philpem@48 | 59 | } |
philpem@48 | 60 | |
philpem@48 | 61 | |
philpem@49 | 62 | void wd2797_reset(WD2797_CTX *ctx) |
philpem@49 | 63 | { |
philpem@49 | 64 | // track, head and sector unknown |
philpem@49 | 65 | ctx->track = ctx->head = ctx->sector = 0; |
philpem@49 | 66 | |
philpem@49 | 67 | // no IRQ pending |
philpem@76 | 68 | ctx->irq = false; |
philpem@49 | 69 | |
philpem@49 | 70 | // no data available |
philpem@49 | 71 | ctx->data_pos = ctx->data_len = 0; |
philpem@49 | 72 | |
philpem@49 | 73 | // Status register clear, not busy |
philpem@49 | 74 | ctx->status = 0; |
philpem@49 | 75 | |
philpem@49 | 76 | // Clear data register |
philpem@49 | 77 | ctx->data_reg = 0; |
philpem@49 | 78 | |
philpem@49 | 79 | // Last step direction |
philpem@49 | 80 | ctx->last_step_dir = -1; |
philpem@49 | 81 | } |
philpem@49 | 82 | |
philpem@49 | 83 | |
philpem@49 | 84 | void wd2797_done(WD2797_CTX *ctx) |
philpem@49 | 85 | { |
philpem@49 | 86 | // Reset the WD2797 |
philpem@49 | 87 | wd2797_reset(ctx); |
philpem@49 | 88 | |
philpem@49 | 89 | // Free any allocated memory |
philpem@49 | 90 | if (ctx->data) { |
philpem@49 | 91 | free(ctx->data); |
philpem@49 | 92 | ctx->data = NULL; |
philpem@49 | 93 | } |
philpem@49 | 94 | } |
philpem@49 | 95 | |
philpem@49 | 96 | |
philpem@49 | 97 | bool wd2797_get_irq(WD2797_CTX *ctx) |
philpem@48 | 98 | { |
philpem@76 | 99 | return ctx->irq; |
philpem@48 | 100 | } |
philpem@48 | 101 | |
philpem@48 | 102 | |
philpem@49 | 103 | bool wd2797_get_drq(WD2797_CTX *ctx) |
philpem@48 | 104 | { |
philpem@48 | 105 | return (ctx->data_pos < ctx->data_len); |
philpem@48 | 106 | } |
philpem@48 | 107 | |
philpem@48 | 108 | |
philpem@49 | 109 | WD2797_ERR wd2797_load(WD2797_CTX *ctx, FILE *fp, int secsz, int spt, int heads) |
philpem@49 | 110 | { |
philpem@49 | 111 | size_t filesize; |
philpem@49 | 112 | |
philpem@49 | 113 | // Start by finding out how big the image file is |
philpem@49 | 114 | fseek(fp, 0, SEEK_END); |
philpem@49 | 115 | filesize = ftell(fp); |
philpem@49 | 116 | fseek(fp, 0, SEEK_SET); |
philpem@49 | 117 | |
philpem@49 | 118 | // Now figure out how many tracks it contains |
philpem@49 | 119 | int tracks = filesize / secsz / spt / heads; |
philpem@49 | 120 | // Confirm... |
philpem@49 | 121 | if (tracks < 1) { |
philpem@49 | 122 | return WD2797_ERR_BAD_GEOM; |
philpem@49 | 123 | } |
philpem@49 | 124 | |
philpem@49 | 125 | // Allocate enough memory to store one disc track |
philpem@49 | 126 | if (ctx->data) { |
philpem@49 | 127 | free(ctx->data); |
philpem@49 | 128 | } |
philpem@49 | 129 | ctx->data = malloc(secsz * spt); |
philpem@49 | 130 | if (!ctx->data) |
philpem@49 | 131 | return WD2797_ERR_NO_MEMORY; |
philpem@49 | 132 | |
philpem@49 | 133 | // Load the image and the geometry data |
philpem@49 | 134 | ctx->disc_image = fp; |
philpem@49 | 135 | ctx->geom_tracks = tracks; |
philpem@49 | 136 | ctx->geom_secsz = secsz; |
philpem@49 | 137 | ctx->geom_heads = heads; |
philpem@49 | 138 | ctx->geom_spt = spt; |
philpem@49 | 139 | |
philpem@49 | 140 | return WD2797_ERR_OK; |
philpem@49 | 141 | } |
philpem@49 | 142 | |
philpem@49 | 143 | |
philpem@49 | 144 | void wd2797_unload(WD2797_CTX *ctx) |
philpem@49 | 145 | { |
philpem@49 | 146 | // Free memory buffer |
philpem@49 | 147 | if (ctx->data) { |
philpem@49 | 148 | free(ctx->data); |
philpem@49 | 149 | ctx->data = NULL; |
philpem@49 | 150 | } |
philpem@49 | 151 | |
philpem@49 | 152 | // Clear file pointer |
philpem@49 | 153 | ctx->disc_image = NULL; |
philpem@49 | 154 | |
philpem@49 | 155 | // Clear the disc geometry |
philpem@49 | 156 | ctx->geom_tracks = ctx->geom_secsz = ctx->geom_spt = ctx->geom_heads = 0; |
philpem@49 | 157 | } |
philpem@49 | 158 | |
philpem@49 | 159 | |
philpem@49 | 160 | uint8_t wd2797_read_reg(WD2797_CTX *ctx, uint8_t addr) |
philpem@48 | 161 | { |
philpem@48 | 162 | uint8_t temp = 0; |
philpem@48 | 163 | |
philpem@48 | 164 | switch (addr & 0x03) { |
philpem@49 | 165 | case WD2797_REG_STATUS: // Status register |
philpem@48 | 166 | // Read from status register clears IRQ |
philpem@76 | 167 | ctx->irq = false; |
philpem@48 | 168 | |
philpem@48 | 169 | // Get current status flags (set by last command) |
philpem@48 | 170 | // DRQ bit |
philpem@53 | 171 | if (ctx->cmd_has_drq) { |
philpem@53 | 172 | temp = ctx->status & ~0x03; |
philpem@48 | 173 | temp |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00; |
philpem@71 | 174 | LOG("\tWDFDC rd sr, has drq, pos=%lu len=%lu, sr=0x%02X", ctx->data_pos, ctx->data_len, temp); |
philpem@53 | 175 | } else { |
philpem@53 | 176 | temp = ctx->status & ~0x01; |
philpem@53 | 177 | } |
philpem@48 | 178 | // FDC is busy if there is still data in the buffer |
philpem@48 | 179 | temp |= (ctx->data_pos < ctx->data_len) ? 0x01 : 0x00; // if data in buffer, then DMA hasn't copied it yet, and we're still busy! |
philpem@49 | 180 | // TODO: also if seek delay / read delay hasn't passed (but that's for later) |
philpem@48 | 181 | return temp; |
philpem@48 | 182 | |
philpem@49 | 183 | case WD2797_REG_TRACK: // Track register |
philpem@48 | 184 | return ctx->track; |
philpem@48 | 185 | |
philpem@49 | 186 | case WD2797_REG_SECTOR: // Sector register |
philpem@48 | 187 | return ctx->sector; |
philpem@48 | 188 | |
philpem@49 | 189 | case WD2797_REG_DATA: // Data register |
philpem@48 | 190 | // If there's data in the buffer, return it. Otherwise return 0xFF. |
philpem@48 | 191 | if (ctx->data_pos < ctx->data_len) { |
philpem@53 | 192 | // set IRQ if this is the last data byte |
philpem@53 | 193 | if (ctx->data_pos == (ctx->data_len-1)) { |
philpem@76 | 194 | // Set IRQ |
philpem@76 | 195 | ctx->irq = true; |
philpem@53 | 196 | } |
philpem@48 | 197 | // return data byte and increment pointer |
philpem@48 | 198 | return ctx->data[ctx->data_pos++]; |
philpem@48 | 199 | } else { |
philpem@53 | 200 | // command finished |
philpem@48 | 201 | return 0xff; |
philpem@48 | 202 | } |
philpem@48 | 203 | |
philpem@48 | 204 | default: |
philpem@48 | 205 | // shut up annoying compilers which don't recognise unreachable code when they see it |
philpem@48 | 206 | // (here's looking at you, gcc!) |
philpem@48 | 207 | return 0xff; |
philpem@48 | 208 | } |
philpem@48 | 209 | } |
philpem@48 | 210 | |
philpem@48 | 211 | |
philpem@49 | 212 | void wd2797_write_reg(WD2797_CTX *ctx, uint8_t addr, uint8_t val) |
philpem@48 | 213 | { |
philpem@48 | 214 | uint8_t cmd = val & CMD_MASK; |
philpem@48 | 215 | size_t lba; |
philpem@48 | 216 | bool is_type1 = false; |
philpem@49 | 217 | int temp; |
philpem@48 | 218 | |
philpem@53 | 219 | m68k_end_timeslice(); |
philpem@53 | 220 | |
philpem@48 | 221 | switch (addr) { |
philpem@49 | 222 | case WD2797_REG_COMMAND: // Command register |
philpem@48 | 223 | // write to command register clears interrupt request |
philpem@76 | 224 | ctx->irq = false; |
philpem@48 | 225 | |
philpem@48 | 226 | // Is the drive ready? |
philpem@48 | 227 | if (ctx->disc_image == NULL) { |
philpem@48 | 228 | // No disc image, thus the drive is busy. |
philpem@48 | 229 | ctx->status = 0x80; |
philpem@48 | 230 | return; |
philpem@48 | 231 | } |
philpem@48 | 232 | |
philpem@48 | 233 | // Handle Type 1 commands |
philpem@48 | 234 | switch (cmd) { |
philpem@48 | 235 | case CMD_RESTORE: |
philpem@48 | 236 | // Restore. Set track to 0 and throw an IRQ. |
philpem@48 | 237 | is_type1 = true; |
philpem@48 | 238 | ctx->track = 0; |
philpem@48 | 239 | break; |
philpem@48 | 240 | |
philpem@48 | 241 | case CMD_SEEK: |
philpem@48 | 242 | // Seek. Seek to the track specced in the Data Register. |
philpem@48 | 243 | is_type1 = true; |
philpem@48 | 244 | if (ctx->data_reg < ctx->geom_tracks) { |
philpem@48 | 245 | ctx->track = ctx->data_reg; |
philpem@48 | 246 | } else { |
philpem@48 | 247 | // Seek error. :( |
philpem@48 | 248 | ctx->status = 0x10; |
philpem@48 | 249 | } |
philpem@48 | 250 | |
philpem@48 | 251 | case CMD_STEP: |
philpem@48 | 252 | // TODO! deal with trk0! |
philpem@48 | 253 | // Need to keep a copy of the track register; when it hits 0, set the TRK0 flag. |
philpem@48 | 254 | is_type1 = true; |
philpem@48 | 255 | break; |
philpem@48 | 256 | |
philpem@48 | 257 | case CMD_STEPIN: |
philpem@48 | 258 | case CMD_STEPOUT: |
philpem@48 | 259 | // TODO! deal with trk0! |
philpem@48 | 260 | // Need to keep a copy of the track register; when it hits 0, set the TRK0 flag. |
philpem@48 | 261 | if (cmd == CMD_STEPIN) { |
philpem@48 | 262 | ctx->last_step_dir = 1; |
philpem@48 | 263 | } else { |
philpem@48 | 264 | ctx->last_step_dir = -1; |
philpem@48 | 265 | } |
philpem@48 | 266 | is_type1 = true; |
philpem@48 | 267 | break; |
philpem@48 | 268 | |
philpem@48 | 269 | case CMD_STEP_TU: |
philpem@48 | 270 | case CMD_STEPIN_TU: |
philpem@48 | 271 | case CMD_STEPOUT_TU: |
philpem@48 | 272 | // if this is a Step In or Step Out cmd, set the step-direction |
philpem@48 | 273 | if (cmd == CMD_STEPIN_TU) { |
philpem@48 | 274 | ctx->last_step_dir = 1; |
philpem@48 | 275 | } else if (cmd == CMD_STEPOUT_TU) { |
philpem@48 | 276 | ctx->last_step_dir = -1; |
philpem@48 | 277 | } |
philpem@48 | 278 | |
philpem@48 | 279 | // Seek one step in the last direction used. |
philpem@48 | 280 | ctx->track += ctx->last_step_dir; |
philpem@48 | 281 | if (ctx->track < 0) ctx->track = 0; |
philpem@48 | 282 | if (ctx->track >= ctx->geom_tracks) { |
philpem@48 | 283 | // Seek past end of disc... that'll be a Seek Error then. |
philpem@48 | 284 | ctx->status = 0x10; |
philpem@48 | 285 | ctx->track = ctx->geom_tracks - 1; |
philpem@48 | 286 | } |
philpem@48 | 287 | is_type1 = true; |
philpem@48 | 288 | break; |
philpem@48 | 289 | |
philpem@48 | 290 | default: |
philpem@48 | 291 | break; |
philpem@48 | 292 | } |
philpem@48 | 293 | |
philpem@48 | 294 | if (is_type1) { |
philpem@48 | 295 | // Terminate any sector reads or writes |
philpem@48 | 296 | ctx->data_len = ctx->data_pos = 0; |
philpem@48 | 297 | |
philpem@48 | 298 | // No DRQ bit for these commands. |
philpem@48 | 299 | ctx->cmd_has_drq = false; |
philpem@48 | 300 | |
philpem@48 | 301 | // Type1 status byte... |
philpem@48 | 302 | ctx->status = 0; |
philpem@48 | 303 | // S7 = Not Ready. Command executed, therefore the drive was ready... :) |
philpem@48 | 304 | // S6 = Write Protect. TODO: add this |
philpem@48 | 305 | // S5 = Head Loaded. For certain emulation-related reasons, the heads are always loaded... |
philpem@48 | 306 | ctx->status |= 0x20; |
philpem@48 | 307 | // S4 = Seek Error. Not bloody likely if we got down here...! |
philpem@48 | 308 | // S3 = CRC Error. Not gonna happen on a disc image! |
philpem@48 | 309 | // S2 = Track 0 |
philpem@48 | 310 | ctx->status |= (ctx->track == 0) ? 0x04 : 0x00; |
philpem@48 | 311 | // S1 = Index Pulse. TODO -- need periodics to emulate this |
philpem@48 | 312 | // S0 = Busy. We just exec'd the command, thus we're not busy. |
philpem@48 | 313 | // TODO: Set a timer for seeks, and ONLY clear BUSY when that timer expires. Need periodics for that. |
philpem@48 | 314 | |
philpem@76 | 315 | // Set IRQ |
philpem@76 | 316 | ctx->irq = true; |
philpem@48 | 317 | return; |
philpem@48 | 318 | } |
philpem@48 | 319 | |
philpem@48 | 320 | // That's the Type 1 (seek) commands sorted. Now for the others. |
philpem@48 | 321 | |
philpem@53 | 322 | // All these commands return the DRQ bit... |
philpem@53 | 323 | ctx->cmd_has_drq = true; |
philpem@53 | 324 | |
philpem@48 | 325 | // If drive isn't ready, then set status B7 and exit |
philpem@48 | 326 | if (ctx->disc_image == NULL) { |
philpem@48 | 327 | ctx->status = 0x80; |
philpem@48 | 328 | return; |
philpem@48 | 329 | } |
philpem@48 | 330 | |
philpem@48 | 331 | // If this is a Write command, check write protect status too |
philpem@48 | 332 | // TODO! |
philpem@48 | 333 | if (false) { |
philpem@48 | 334 | // Write protected disc... |
philpem@48 | 335 | if ((cmd == CMD_WRITE_SECTOR) || (cmd == CMD_WRITE_SECTOR_MULTI) || (cmd == CMD_FORMAT_TRACK)) { |
philpem@48 | 336 | // Set Write Protect bit and bail. |
philpem@48 | 337 | ctx->status = 0x40; |
philpem@48 | 338 | |
philpem@76 | 339 | // Set IRQ |
philpem@76 | 340 | ctx->irq = true; |
philpem@48 | 341 | |
philpem@48 | 342 | return; |
philpem@48 | 343 | } |
philpem@48 | 344 | } |
philpem@48 | 345 | |
philpem@48 | 346 | // Disc is ready to go. Parse the command word. |
philpem@48 | 347 | switch (cmd) { |
philpem@48 | 348 | case CMD_READ_ADDRESS: |
philpem@48 | 349 | // Read Address |
philpem@54 | 350 | ctx->head = (val & 0x02) ? 1 : 0; |
philpem@48 | 351 | |
philpem@48 | 352 | // reset data pointers |
philpem@48 | 353 | ctx->data_pos = ctx->data_len = 0; |
philpem@48 | 354 | |
philpem@48 | 355 | // load data buffer |
philpem@48 | 356 | ctx->data[ctx->data_len++] = ctx->track; |
philpem@48 | 357 | ctx->data[ctx->data_len++] = ctx->head; |
philpem@48 | 358 | ctx->data[ctx->data_len++] = ctx->sector; |
philpem@48 | 359 | switch (ctx->geom_secsz) { |
philpem@48 | 360 | case 128: ctx->data[ctx->data_len++] = 0; break; |
philpem@48 | 361 | case 256: ctx->data[ctx->data_len++] = 1; break; |
philpem@48 | 362 | case 512: ctx->data[ctx->data_len++] = 2; break; |
philpem@48 | 363 | case 1024: ctx->data[ctx->data_len++] = 3; break; |
philpem@48 | 364 | default: ctx->data[ctx->data_len++] = 0xFF; break; // TODO: deal with invalid values better |
philpem@48 | 365 | } |
philpem@48 | 366 | ctx->data[ctx->data_len++] = 0; // TODO: IDAM CRC! |
philpem@48 | 367 | ctx->data[ctx->data_len++] = 0; |
philpem@48 | 368 | |
philpem@53 | 369 | ctx->status = 0; |
philpem@48 | 370 | // B6, B5 = 0 |
philpem@48 | 371 | // B4 = Record Not Found. We're not going to see this... FIXME-not emulated |
philpem@48 | 372 | // B3 = CRC Error. Not possible. |
philpem@48 | 373 | // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated |
philpem@48 | 374 | // B1 = DRQ. Data request. |
philpem@48 | 375 | ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00; |
philpem@48 | 376 | break; |
philpem@48 | 377 | |
philpem@48 | 378 | case CMD_READ_SECTOR: |
philpem@48 | 379 | case CMD_READ_SECTOR_MULTI: |
philpem@54 | 380 | ctx->head = (val & 0x02) ? 1 : 0; |
philpem@71 | 381 | LOG("WD279X: READ SECTOR cmd=%02X chs=%d:%d:%d", cmd, ctx->track, ctx->head, ctx->sector); |
philpem@48 | 382 | // Read Sector or Read Sector Multiple |
philpem@57 | 383 | |
philpem@57 | 384 | // Check to see if the cyl, hd and sec are valid |
philpem@57 | 385 | if ((ctx->track > (ctx->geom_tracks-1)) || (ctx->head > (ctx->geom_heads-1)) || (ctx->sector > ctx->geom_spt) || (ctx->sector == 0)) { |
philpem@71 | 386 | LOG("*** WD2797 ALERT: CHS parameter limit exceeded! CHS=%d:%d:%d, maxCHS=%d:%d:%d", |
philpem@57 | 387 | ctx->track, ctx->head, ctx->sector, |
philpem@57 | 388 | ctx->geom_tracks-1, ctx->geom_heads-1, ctx->geom_spt); |
philpem@57 | 389 | // CHS parameters exceed limits |
philpem@57 | 390 | ctx->status = 0x10; // Record Not Found |
philpem@57 | 391 | break; |
philpem@78 | 392 | // Set IRQ |
philpem@78 | 393 | ctx->irq = true; |
philpem@57 | 394 | } |
philpem@57 | 395 | |
philpem@48 | 396 | // reset data pointers |
philpem@48 | 397 | ctx->data_pos = ctx->data_len = 0; |
philpem@48 | 398 | |
philpem@49 | 399 | // Calculate number of sectors to read from disc |
philpem@49 | 400 | if (cmd == CMD_READ_SECTOR_MULTI) |
philpem@49 | 401 | temp = ctx->geom_spt; |
philpem@49 | 402 | else |
philpem@49 | 403 | temp = 1; |
philpem@48 | 404 | |
philpem@49 | 405 | for (int i=0; i<temp; i++) { |
philpem@49 | 406 | // Calculate the LBA address of the required sector |
philpem@57 | 407 | // LBA = (C * nHeads * nSectors) + (H * nSectors) + S - 1 |
philpem@57 | 408 | lba = (((ctx->track * ctx->geom_heads * ctx->geom_spt) + (ctx->head * ctx->geom_spt) + ctx->sector) + i) - 1; |
philpem@57 | 409 | // convert LBA to byte address |
philpem@57 | 410 | lba *= ctx->geom_secsz; |
philpem@71 | 411 | LOG("\tREAD lba = %lu", lba); |
philpem@49 | 412 | |
philpem@49 | 413 | // Read the sector from the file |
philpem@49 | 414 | fseek(ctx->disc_image, lba, SEEK_SET); |
philpem@71 | 415 | // TODO: check fread return value! if < secsz, BAIL! (call it a crc error or secnotfound maybe? also log to stderr) |
philpem@49 | 416 | ctx->data_len += fread(&ctx->data[ctx->data_len], 1, ctx->geom_secsz, ctx->disc_image); |
philpem@71 | 417 | LOG("\tREAD len=%lu, pos=%lu, ssz=%d", ctx->data_len, ctx->data_pos, ctx->geom_secsz); |
philpem@49 | 418 | } |
philpem@48 | 419 | |
philpem@53 | 420 | ctx->status = 0; |
philpem@48 | 421 | // B6 = 0 |
philpem@48 | 422 | // B5 = Record Type -- 1 = deleted, 0 = normal. We can't emulate anything but normal data blocks. |
philpem@57 | 423 | // B4 = Record Not Found. Basically, the CHS parameters are bullcrap. |
philpem@48 | 424 | // B3 = CRC Error. Not possible. |
philpem@48 | 425 | // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated |
philpem@48 | 426 | // B1 = DRQ. Data request. |
philpem@48 | 427 | ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00; |
philpem@48 | 428 | break; |
philpem@48 | 429 | |
philpem@48 | 430 | case CMD_READ_TRACK: |
philpem@48 | 431 | // Read Track |
philpem@49 | 432 | // TODO! implement this |
philpem@54 | 433 | ctx->head = (val & 0x02) ? 1 : 0; |
philpem@53 | 434 | ctx->status = 0; |
philpem@48 | 435 | // B6, B5, B4, B3 = 0 |
philpem@48 | 436 | // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated |
philpem@48 | 437 | // B1 = DRQ. Data request. |
philpem@48 | 438 | ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00; |
philpem@48 | 439 | break; |
philpem@48 | 440 | |
philpem@48 | 441 | case CMD_WRITE_SECTOR: |
philpem@48 | 442 | case CMD_WRITE_SECTOR_MULTI: |
philpem@48 | 443 | // Write Sector or Write Sector Multiple |
philpem@48 | 444 | |
philpem@54 | 445 | ctx->head = (val & 0x02) ? 1 : 0; |
philpem@48 | 446 | // reset data pointers |
philpem@48 | 447 | ctx->data_pos = ctx->data_len = 0; |
philpem@48 | 448 | |
philpem@48 | 449 | // TODO: set "write pending" flag, and write LBA, and go from there. |
philpem@48 | 450 | |
philpem@53 | 451 | ctx->status = 0; |
philpem@48 | 452 | // B6 = Write Protect. FIXME -- emulate this! |
philpem@48 | 453 | // B5 = 0 |
philpem@48 | 454 | // B4 = Record Not Found. We're not going to see this... FIXME-not emulated |
philpem@48 | 455 | // B3 = CRC Error. Not possible. |
philpem@48 | 456 | // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated |
philpem@48 | 457 | // B1 = DRQ. Data request. |
philpem@48 | 458 | ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00; |
philpem@48 | 459 | break; |
philpem@48 | 460 | |
philpem@48 | 461 | case CMD_FORMAT_TRACK: |
philpem@48 | 462 | // Write Track (aka Format Track) |
philpem@54 | 463 | ctx->head = (val & 0x02) ? 1 : 0; |
philpem@53 | 464 | ctx->status = 0; |
philpem@48 | 465 | // B6 = Write Protect. FIXME -- emulate this! |
philpem@48 | 466 | // B5, B4, B3 = 0 |
philpem@48 | 467 | // B2 = Lost Data. Caused if DRQ isn't serviced in time. FIXME-not emulated |
philpem@48 | 468 | // B1 = DRQ. Data request. |
philpem@48 | 469 | ctx->status |= (ctx->data_pos < ctx->data_len) ? 0x02 : 0x00; |
philpem@48 | 470 | break; |
philpem@48 | 471 | |
philpem@48 | 472 | case CMD_FORCE_INTERRUPT: |
philpem@48 | 473 | // Force Interrupt... |
philpem@49 | 474 | // Terminates current operation and sends an interrupt |
philpem@49 | 475 | // TODO! |
philpem@53 | 476 | ctx->status = 0; |
philpem@49 | 477 | ctx->data_pos = ctx->data_len = 0; |
philpem@76 | 478 | // Set IRQ |
philpem@76 | 479 | ctx->irq = true; |
philpem@48 | 480 | break; |
philpem@48 | 481 | } |
philpem@48 | 482 | break; |
philpem@48 | 483 | |
philpem@49 | 484 | case WD2797_REG_TRACK: // Track register |
philpem@48 | 485 | ctx->track = val; |
philpem@48 | 486 | break; |
philpem@48 | 487 | |
philpem@49 | 488 | case WD2797_REG_SECTOR: // Sector register |
philpem@48 | 489 | ctx->sector = val; |
philpem@48 | 490 | break; |
philpem@48 | 491 | |
philpem@49 | 492 | case WD2797_REG_DATA: // Data register |
philpem@48 | 493 | // Save the value written into the data register |
philpem@48 | 494 | ctx->data_reg = val; |
philpem@48 | 495 | |
philpem@48 | 496 | // If we're processing a write command, and there's space in the |
philpem@48 | 497 | // buffer, allow the write. |
philpem@48 | 498 | if (ctx->data_pos < ctx->data_len) { |
philpem@53 | 499 | // set IRQ if this is the last data byte |
philpem@53 | 500 | if (ctx->data_pos == (ctx->data_len-1)) { |
philpem@76 | 501 | // Set IRQ |
philpem@76 | 502 | ctx->irq = true; |
philpem@53 | 503 | } |
philpem@53 | 504 | |
philpem@48 | 505 | // store data byte and increment pointer |
philpem@48 | 506 | ctx->data[ctx->data_pos++] = val; |
philpem@48 | 507 | } |
philpem@48 | 508 | break; |
philpem@48 | 509 | } |
philpem@48 | 510 | } |
philpem@48 | 511 |