src/wd279x.c

Sat, 17 Nov 2012 22:26:53 +0000

author
Philip Pemberton <philpem@philpem.me.uk>
date
Sat, 17 Nov 2012 22:26:53 +0000
changeset 116
21521e62007f
parent 111
4c85846b09cd
permissions
-rw-r--r--

Add support for MSR2, partial reads from GENSTAT

* GENSTAT is sometimes read in 8bit mode. Handle this properly.

* Add support for the MSR2 register (additional HDD head select bit only at
the moment)

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 }