src/wd279x.c

Sat, 17 Nov 2012 19:18:29 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Sat, 17 Nov 2012 19:18:29 +0000
changeset 112
a392eb8f9806
parent 111
4c85846b09cd
permissions
-rw-r--r--

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