src/wd2010.c

Fri, 12 Apr 2013 16:26:25 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Fri, 12 Apr 2013 16:26:25 +0100
branch
experimental_memory_mapper_v2
changeset 144
609707511166
parent 135
159f937af10d
child 145
2d6de28c6e6c
permissions
-rw-r--r--

Don't set PS1 if there is a level-7 interrupt or bus error

PS1 should only be set if the page was originally present (PS1 or PS0 set). If
PS0 and PS1 are clear (page not present) then do NOT set PS1.

Once again the TRM is blatantly and spectacularly wrong...

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