├── Makefile ├── cpu.c ├── cpu.h ├── readme.txt └── siddump.c /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CXX=g++ 3 | CFLAGS+=-O3 -Wall 4 | CXXFLAGS=$(CFLAGS) 5 | 6 | siddump.exe: siddump.o cpu.o 7 | gcc -o $@ $^ -lm 8 | strip $@ 9 | -------------------------------------------------------------------------------- /cpu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define FN 0x80 5 | #define FV 0x40 6 | #define FB 0x10 7 | #define FD 0x08 8 | #define FI 0x04 9 | #define FZ 0x02 10 | #define FC 0x01 11 | 12 | #define MEM(address) (mem[address]) 13 | #define LO() (MEM(pc)) 14 | #define HI() (MEM(pc+1)) 15 | #define FETCH() (MEM(pc++)) 16 | #define SETPC(newpc) (pc = (newpc)) 17 | #define PUSH(data) (MEM(0x100 + (sp--)) = (data)) 18 | #define POP() (MEM(0x100 + (++sp))) 19 | 20 | #define IMMEDIATE() (LO()) 21 | #define ABSOLUTE() (LO() | (HI() << 8)) 22 | #define ABSOLUTEX() (((LO() | (HI() << 8)) + x) & 0xffff) 23 | #define ABSOLUTEY() (((LO() | (HI() << 8)) + y) & 0xffff) 24 | #define ZEROPAGE() (LO() & 0xff) 25 | #define ZEROPAGEX() ((LO() + x) & 0xff) 26 | #define ZEROPAGEY() ((LO() + y) & 0xff) 27 | #define INDIRECTX() (MEM((LO() + x) & 0xff) | (MEM((LO() + x + 1) & 0xff) << 8)) 28 | #define INDIRECTY() (((MEM(LO()) | (MEM((LO() + 1) & 0xff) << 8)) + y) & 0xffff) 29 | #define INDIRECTZP() (((MEM(LO()) | (MEM((LO() + 1) & 0xff) << 8)) + 0) & 0xffff) 30 | 31 | #define WRITE(address) \ 32 | { \ 33 | /* cpuwritemap[(address) >> 6] = 1; */ \ 34 | } 35 | 36 | #define EVALPAGECROSSING(baseaddr, realaddr) ((((baseaddr) ^ (realaddr)) & 0xff00) ? 1 : 0) 37 | #define EVALPAGECROSSING_ABSOLUTEX() (EVALPAGECROSSING(ABSOLUTE(), ABSOLUTEX())) 38 | #define EVALPAGECROSSING_ABSOLUTEY() (EVALPAGECROSSING(ABSOLUTE(), ABSOLUTEY())) 39 | #define EVALPAGECROSSING_INDIRECTY() (EVALPAGECROSSING(INDIRECTZP(), INDIRECTY())) 40 | 41 | #define BRANCH() \ 42 | { \ 43 | ++cpucycles; \ 44 | temp = FETCH(); \ 45 | if (temp < 0x80) \ 46 | { \ 47 | cpucycles += EVALPAGECROSSING(pc, pc + temp); \ 48 | SETPC(pc + temp); \ 49 | } \ 50 | else \ 51 | { \ 52 | cpucycles += EVALPAGECROSSING(pc, pc + temp - 0x100); \ 53 | SETPC(pc + temp - 0x100); \ 54 | } \ 55 | } 56 | 57 | #define SETFLAGS(data) \ 58 | { \ 59 | if (!(data)) \ 60 | flags = (flags & ~FN) | FZ; \ 61 | else \ 62 | flags = (flags & ~(FN|FZ)) | \ 63 | ((data) & FN); \ 64 | } 65 | 66 | #define ASSIGNSETFLAGS(dest, data) \ 67 | { \ 68 | dest = data; \ 69 | if (!dest) \ 70 | flags = (flags & ~FN) | FZ; \ 71 | else \ 72 | flags = (flags & ~(FN|FZ)) | \ 73 | (dest & FN); \ 74 | } 75 | 76 | #define ADC(data) \ 77 | { \ 78 | unsigned tempval = data; \ 79 | \ 80 | if (flags & FD) \ 81 | { \ 82 | temp = (a & 0xf) + (tempval & 0xf) + (flags & FC); \ 83 | if (temp > 0x9) \ 84 | temp += 0x6; \ 85 | if (temp <= 0x0f) \ 86 | temp = (temp & 0xf) + (a & 0xf0) + (tempval & 0xf0); \ 87 | else \ 88 | temp = (temp & 0xf) + (a & 0xf0) + (tempval & 0xf0) + 0x10; \ 89 | if (!((a + tempval + (flags & FC)) & 0xff)) \ 90 | flags |= FZ; \ 91 | else \ 92 | flags &= ~FZ; \ 93 | if (temp & 0x80) \ 94 | flags |= FN; \ 95 | else \ 96 | flags &= ~FN; \ 97 | if (((a ^ temp) & 0x80) && !((a ^ tempval) & 0x80)) \ 98 | flags |= FV; \ 99 | else \ 100 | flags &= ~FV; \ 101 | if ((temp & 0x1f0) > 0x90) temp += 0x60; \ 102 | if ((temp & 0xff0) > 0xf0) \ 103 | flags |= FC; \ 104 | else \ 105 | flags &= ~FC; \ 106 | } \ 107 | else \ 108 | { \ 109 | temp = tempval + a + (flags & FC); \ 110 | SETFLAGS(temp & 0xff); \ 111 | if (!((a ^ tempval) & 0x80) && ((a ^ temp) & 0x80)) \ 112 | flags |= FV; \ 113 | else \ 114 | flags &= ~FV; \ 115 | if (temp > 0xff) \ 116 | flags |= FC; \ 117 | else \ 118 | flags &= ~FC; \ 119 | } \ 120 | a = temp; \ 121 | } 122 | 123 | #define SBC(data) \ 124 | { \ 125 | unsigned tempval = data; \ 126 | temp = a - tempval - ((flags & FC) ^ FC); \ 127 | \ 128 | if (flags & FD) \ 129 | { \ 130 | unsigned tempval2; \ 131 | tempval2 = (a & 0xf) - (tempval & 0xf) - ((flags & FC) ^ FC); \ 132 | if (tempval2 & 0x10) \ 133 | tempval2 = ((tempval2 - 6) & 0xf) | ((a & 0xf0) - (tempval \ 134 | & 0xf0) - 0x10); \ 135 | else \ 136 | tempval2 = (tempval2 & 0xf) | ((a & 0xf0) - (tempval \ 137 | & 0xf0)); \ 138 | if (tempval2 & 0x100) \ 139 | tempval2 -= 0x60; \ 140 | if (temp < 0x100) \ 141 | flags |= FC; \ 142 | else \ 143 | flags &= ~FC; \ 144 | SETFLAGS(temp & 0xff); \ 145 | if (((a ^ temp) & 0x80) && ((a ^ tempval) & 0x80)) \ 146 | flags |= FV; \ 147 | else \ 148 | flags &= ~FV; \ 149 | a = tempval2; \ 150 | } \ 151 | else \ 152 | { \ 153 | SETFLAGS(temp & 0xff); \ 154 | if (temp < 0x100) \ 155 | flags |= FC; \ 156 | else \ 157 | flags &= ~FC; \ 158 | if (((a ^ temp) & 0x80) && ((a ^ tempval) & 0x80)) \ 159 | flags |= FV; \ 160 | else \ 161 | flags &= ~FV; \ 162 | a = temp; \ 163 | } \ 164 | } 165 | 166 | #define CMP(src, data) \ 167 | { \ 168 | temp = (src - data) & 0xff; \ 169 | \ 170 | flags = (flags & ~(FC|FN|FZ)) | \ 171 | (temp & FN); \ 172 | \ 173 | if (!temp) flags |= FZ; \ 174 | if (src >= data) flags |= FC; \ 175 | } 176 | 177 | #define ASL(data) \ 178 | { \ 179 | temp = data; \ 180 | temp <<= 1; \ 181 | if (temp & 0x100) flags |= FC; \ 182 | else flags &= ~FC; \ 183 | ASSIGNSETFLAGS(data, temp); \ 184 | } 185 | 186 | #define LSR(data) \ 187 | { \ 188 | temp = data; \ 189 | if (temp & 1) flags |= FC; \ 190 | else flags &= ~FC; \ 191 | temp >>= 1; \ 192 | ASSIGNSETFLAGS(data, temp); \ 193 | } 194 | 195 | #define ROL(data) \ 196 | { \ 197 | temp = data; \ 198 | temp <<= 1; \ 199 | if (flags & FC) temp |= 1; \ 200 | if (temp & 0x100) flags |= FC; \ 201 | else flags &= ~FC; \ 202 | ASSIGNSETFLAGS(data, temp); \ 203 | } 204 | 205 | #define ROR(data) \ 206 | { \ 207 | temp = data; \ 208 | if (flags & FC) temp |= 0x100; \ 209 | if (temp & 1) flags |= FC; \ 210 | else flags &= ~FC; \ 211 | temp >>= 1; \ 212 | ASSIGNSETFLAGS(data, temp); \ 213 | } 214 | 215 | #define DEC(data) \ 216 | { \ 217 | temp = data - 1; \ 218 | ASSIGNSETFLAGS(data, temp); \ 219 | } 220 | 221 | #define INC(data) \ 222 | { \ 223 | temp = data + 1; \ 224 | ASSIGNSETFLAGS(data, temp); \ 225 | } 226 | 227 | #define EOR(data) \ 228 | { \ 229 | a ^= data; \ 230 | SETFLAGS(a); \ 231 | } 232 | 233 | #define ORA(data) \ 234 | { \ 235 | a |= data; \ 236 | SETFLAGS(a); \ 237 | } 238 | 239 | #define AND(data) \ 240 | { \ 241 | a &= data; \ 242 | SETFLAGS(a) \ 243 | } 244 | 245 | #define BIT(data) \ 246 | { \ 247 | flags = (flags & ~(FN|FV)) | \ 248 | (data & (FN|FV)); \ 249 | if (!(data & a)) flags |= FZ; \ 250 | else flags &= ~FZ; \ 251 | } 252 | 253 | void initcpu(unsigned short newpc, unsigned char newa, unsigned char newx, unsigned char newy); 254 | int runcpu(void); 255 | void setpc(unsigned short newpc); 256 | 257 | unsigned short pc; 258 | unsigned char a; 259 | unsigned char x; 260 | unsigned char y; 261 | unsigned char flags; 262 | unsigned char sp; 263 | unsigned char mem[0x10000]; 264 | unsigned int cpucycles; 265 | 266 | static const int cpucycles_table[] = 267 | { 268 | 7, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, 269 | 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 270 | 6, 6, 0, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, 271 | 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 272 | 6, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, 273 | 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 274 | 6, 6, 0, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, 275 | 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 276 | 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 277 | 2, 6, 0, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, 278 | 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 279 | 2, 5, 0, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, 280 | 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, 281 | 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 282 | 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, 283 | 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 284 | }; 285 | 286 | void initcpu(unsigned short newpc, unsigned char newa, unsigned char newx, unsigned char newy) 287 | { 288 | pc = newpc; 289 | a = newa; 290 | x = newx; 291 | y = newy; 292 | flags = 0; 293 | sp = 0xff; 294 | cpucycles = 0; 295 | } 296 | 297 | int runcpu(void) 298 | { 299 | unsigned temp; 300 | 301 | unsigned char op = FETCH(); 302 | /* printf("PC: %04x OP: %02x A:%02x X:%02x Y:%02x\n", pc-1, op, a, x, y); */ 303 | cpucycles += cpucycles_table[op]; 304 | switch(op) 305 | { 306 | case 0xa7: 307 | ASSIGNSETFLAGS(a, MEM(ZEROPAGE())); 308 | x = a; 309 | pc++; 310 | break; 311 | 312 | case 0xb7: 313 | ASSIGNSETFLAGS(a, MEM(ZEROPAGEY())); 314 | x = a; 315 | pc++; 316 | break; 317 | 318 | case 0xaf: 319 | ASSIGNSETFLAGS(a, MEM(ABSOLUTE())); 320 | x = a; 321 | pc += 2; 322 | break; 323 | 324 | case 0xa3: 325 | ASSIGNSETFLAGS(a, MEM(INDIRECTX())); 326 | x = a; 327 | pc++; 328 | break; 329 | 330 | case 0xb3: 331 | cpucycles += EVALPAGECROSSING_INDIRECTY(); 332 | ASSIGNSETFLAGS(a, MEM(INDIRECTY())); 333 | x = a; 334 | pc++; 335 | break; 336 | 337 | case 0x1a: 338 | case 0x3a: 339 | case 0x5a: 340 | case 0x7a: 341 | case 0xda: 342 | case 0xfa: 343 | break; 344 | 345 | case 0x80: 346 | case 0x82: 347 | case 0x89: 348 | case 0xc2: 349 | case 0xe2: 350 | case 0x04: 351 | case 0x44: 352 | case 0x64: 353 | case 0x14: 354 | case 0x34: 355 | case 0x54: 356 | case 0x74: 357 | case 0xd4: 358 | case 0xf4: 359 | pc++; 360 | break; 361 | 362 | case 0x0c: 363 | case 0x1c: 364 | case 0x3c: 365 | case 0x5c: 366 | case 0x7c: 367 | case 0xdc: 368 | case 0xfc: 369 | cpucycles += EVALPAGECROSSING_ABSOLUTEX(); 370 | pc += 2; 371 | break; 372 | 373 | case 0x69: 374 | ADC(IMMEDIATE()); 375 | pc++; 376 | break; 377 | 378 | case 0x65: 379 | ADC(MEM(ZEROPAGE())); 380 | pc++; 381 | break; 382 | 383 | case 0x75: 384 | ADC(MEM(ZEROPAGEX())); 385 | pc++; 386 | break; 387 | 388 | case 0x6d: 389 | ADC(MEM(ABSOLUTE())); 390 | pc += 2; 391 | break; 392 | 393 | case 0x7d: 394 | cpucycles += EVALPAGECROSSING_ABSOLUTEX(); 395 | ADC(MEM(ABSOLUTEX())); 396 | pc += 2; 397 | break; 398 | 399 | case 0x79: 400 | cpucycles += EVALPAGECROSSING_ABSOLUTEY(); 401 | ADC(MEM(ABSOLUTEY())); 402 | pc += 2; 403 | break; 404 | 405 | case 0x61: 406 | ADC(MEM(INDIRECTX())); 407 | pc++; 408 | break; 409 | 410 | case 0x71: 411 | cpucycles += EVALPAGECROSSING_INDIRECTY(); 412 | ADC(MEM(INDIRECTY())); 413 | pc++; 414 | break; 415 | 416 | case 0x29: 417 | AND(IMMEDIATE()); 418 | pc++; 419 | break; 420 | 421 | case 0x25: 422 | AND(MEM(ZEROPAGE())); 423 | pc++; 424 | break; 425 | 426 | case 0x35: 427 | AND(MEM(ZEROPAGEX())); 428 | pc++; 429 | break; 430 | 431 | case 0x2d: 432 | AND(MEM(ABSOLUTE())); 433 | pc += 2; 434 | break; 435 | 436 | case 0x3d: 437 | cpucycles += EVALPAGECROSSING_ABSOLUTEX(); 438 | AND(MEM(ABSOLUTEX())); 439 | pc += 2; 440 | break; 441 | 442 | case 0x39: 443 | cpucycles += EVALPAGECROSSING_ABSOLUTEY(); 444 | AND(MEM(ABSOLUTEY())); 445 | pc += 2; 446 | break; 447 | 448 | case 0x21: 449 | AND(MEM(INDIRECTX())); 450 | pc++; 451 | break; 452 | 453 | case 0x31: 454 | cpucycles += EVALPAGECROSSING_INDIRECTY(); 455 | AND(MEM(INDIRECTY())); 456 | pc++; 457 | break; 458 | 459 | case 0x0a: 460 | ASL(a); 461 | break; 462 | 463 | case 0x06: 464 | ASL(MEM(ZEROPAGE())); 465 | pc++; 466 | break; 467 | 468 | case 0x16: 469 | ASL(MEM(ZEROPAGEX())); 470 | pc++; 471 | break; 472 | 473 | case 0x0e: 474 | ASL(MEM(ABSOLUTE())); 475 | pc += 2; 476 | break; 477 | 478 | case 0x1e: 479 | ASL(MEM(ABSOLUTEX())); 480 | pc += 2; 481 | break; 482 | 483 | case 0x90: 484 | if (!(flags & FC)) BRANCH() 485 | else pc++; 486 | break; 487 | 488 | case 0xb0: 489 | if (flags & FC) BRANCH() 490 | else pc++; 491 | break; 492 | 493 | case 0xf0: 494 | if (flags & FZ) BRANCH() 495 | else pc++; 496 | break; 497 | 498 | case 0x24: 499 | BIT(MEM(ZEROPAGE())); 500 | pc++; 501 | break; 502 | 503 | case 0x2c: 504 | BIT(MEM(ABSOLUTE())); 505 | pc += 2; 506 | break; 507 | 508 | case 0x30: 509 | if (flags & FN) BRANCH() 510 | else pc++; 511 | break; 512 | 513 | case 0xd0: 514 | if (!(flags & FZ)) BRANCH() 515 | else pc++; 516 | break; 517 | 518 | case 0x10: 519 | if (!(flags & FN)) BRANCH() 520 | else pc++; 521 | break; 522 | 523 | case 0x50: 524 | if (!(flags & FV)) BRANCH() 525 | else pc++; 526 | break; 527 | 528 | case 0x70: 529 | if (flags & FV) BRANCH() 530 | else pc++; 531 | break; 532 | 533 | case 0x18: 534 | flags &= ~FC; 535 | break; 536 | 537 | case 0xd8: 538 | flags &= ~FD; 539 | break; 540 | 541 | case 0x58: 542 | flags &= ~FI; 543 | break; 544 | 545 | case 0xb8: 546 | flags &= ~FV; 547 | break; 548 | 549 | case 0xc9: 550 | CMP(a, IMMEDIATE()); 551 | pc++; 552 | break; 553 | 554 | case 0xc5: 555 | CMP(a, MEM(ZEROPAGE())); 556 | pc++; 557 | break; 558 | 559 | case 0xd5: 560 | CMP(a, MEM(ZEROPAGEX())); 561 | pc++; 562 | break; 563 | 564 | case 0xcd: 565 | CMP(a, MEM(ABSOLUTE())); 566 | pc += 2; 567 | break; 568 | 569 | case 0xdd: 570 | cpucycles += EVALPAGECROSSING_ABSOLUTEX(); 571 | CMP(a, MEM(ABSOLUTEX())); 572 | pc += 2; 573 | break; 574 | 575 | case 0xd9: 576 | cpucycles += EVALPAGECROSSING_ABSOLUTEY(); 577 | CMP(a, MEM(ABSOLUTEY())); 578 | pc += 2; 579 | break; 580 | 581 | case 0xc1: 582 | CMP(a, MEM(INDIRECTX())); 583 | pc++; 584 | break; 585 | 586 | case 0xd1: 587 | cpucycles += EVALPAGECROSSING_INDIRECTY(); 588 | CMP(a, MEM(INDIRECTY())); 589 | pc++; 590 | break; 591 | 592 | case 0xe0: 593 | CMP(x, IMMEDIATE()); 594 | pc++; 595 | break; 596 | 597 | case 0xe4: 598 | CMP(x, MEM(ZEROPAGE())); 599 | pc++; 600 | break; 601 | 602 | case 0xec: 603 | CMP(x, MEM(ABSOLUTE())); 604 | pc += 2; 605 | break; 606 | 607 | case 0xc0: 608 | CMP(y, IMMEDIATE()); 609 | pc++; 610 | break; 611 | 612 | case 0xc4: 613 | CMP(y, MEM(ZEROPAGE())); 614 | pc++; 615 | break; 616 | 617 | case 0xcc: 618 | CMP(y, MEM(ABSOLUTE())); 619 | pc += 2; 620 | break; 621 | 622 | case 0xc6: 623 | DEC(MEM(ZEROPAGE())); 624 | WRITE(ZEROPAGE()); 625 | pc++; 626 | break; 627 | 628 | case 0xd6: 629 | DEC(MEM(ZEROPAGEX())); 630 | WRITE(ZEROPAGEX()); 631 | pc++; 632 | break; 633 | 634 | case 0xce: 635 | DEC(MEM(ABSOLUTE())); 636 | WRITE(ABSOLUTE()); 637 | pc += 2; 638 | break; 639 | 640 | case 0xde: 641 | DEC(MEM(ABSOLUTEX())); 642 | WRITE(ABSOLUTEX()); 643 | pc += 2; 644 | break; 645 | 646 | case 0xca: 647 | x--; 648 | SETFLAGS(x); 649 | break; 650 | 651 | case 0x88: 652 | y--; 653 | SETFLAGS(y); 654 | break; 655 | 656 | case 0x49: 657 | EOR(IMMEDIATE()); 658 | pc++; 659 | break; 660 | 661 | case 0x45: 662 | EOR(MEM(ZEROPAGE())); 663 | pc++; 664 | break; 665 | 666 | case 0x55: 667 | EOR(MEM(ZEROPAGEX())); 668 | pc++; 669 | break; 670 | 671 | case 0x4d: 672 | EOR(MEM(ABSOLUTE())); 673 | pc += 2; 674 | break; 675 | 676 | case 0x5d: 677 | cpucycles += EVALPAGECROSSING_ABSOLUTEX(); 678 | EOR(MEM(ABSOLUTEX())); 679 | pc += 2; 680 | break; 681 | 682 | case 0x59: 683 | cpucycles += EVALPAGECROSSING_ABSOLUTEY(); 684 | EOR(MEM(ABSOLUTEY())); 685 | pc += 2; 686 | break; 687 | 688 | case 0x41: 689 | EOR(MEM(INDIRECTX())); 690 | pc++; 691 | break; 692 | 693 | case 0x51: 694 | cpucycles += EVALPAGECROSSING_INDIRECTY(); 695 | EOR(MEM(INDIRECTY())); 696 | pc++; 697 | break; 698 | 699 | case 0xe6: 700 | INC(MEM(ZEROPAGE())); 701 | WRITE(ZEROPAGE()); 702 | pc++; 703 | break; 704 | 705 | case 0xf6: 706 | INC(MEM(ZEROPAGEX())); 707 | WRITE(ZEROPAGEX()); 708 | pc++; 709 | break; 710 | 711 | case 0xee: 712 | INC(MEM(ABSOLUTE())); 713 | WRITE(ABSOLUTE()); 714 | pc += 2; 715 | break; 716 | 717 | case 0xfe: 718 | INC(MEM(ABSOLUTEX())); 719 | WRITE(ABSOLUTEX()); 720 | pc += 2; 721 | break; 722 | 723 | case 0xe8: 724 | x++; 725 | SETFLAGS(x); 726 | break; 727 | 728 | case 0xc8: 729 | y++; 730 | SETFLAGS(y); 731 | break; 732 | 733 | case 0x20: 734 | PUSH((pc+1) >> 8); 735 | PUSH((pc+1) & 0xff); 736 | pc = ABSOLUTE(); 737 | break; 738 | 739 | case 0x4c: 740 | pc = ABSOLUTE(); 741 | break; 742 | 743 | case 0x6c: 744 | { 745 | unsigned short adr = ABSOLUTE(); 746 | pc = (MEM(adr) | (MEM(((adr + 1) & 0xff) | (adr & 0xff00)) << 8)); 747 | } 748 | break; 749 | 750 | case 0xa9: 751 | ASSIGNSETFLAGS(a, IMMEDIATE()); 752 | pc++; 753 | break; 754 | 755 | case 0xa5: 756 | ASSIGNSETFLAGS(a, MEM(ZEROPAGE())); 757 | pc++; 758 | break; 759 | 760 | case 0xb5: 761 | ASSIGNSETFLAGS(a, MEM(ZEROPAGEX())); 762 | pc++; 763 | break; 764 | 765 | case 0xad: 766 | ASSIGNSETFLAGS(a, MEM(ABSOLUTE())); 767 | pc += 2; 768 | break; 769 | 770 | case 0xbd: 771 | cpucycles += EVALPAGECROSSING_ABSOLUTEX(); 772 | ASSIGNSETFLAGS(a, MEM(ABSOLUTEX())); 773 | pc += 2; 774 | break; 775 | 776 | case 0xb9: 777 | cpucycles += EVALPAGECROSSING_ABSOLUTEY(); 778 | ASSIGNSETFLAGS(a, MEM(ABSOLUTEY())); 779 | pc += 2; 780 | break; 781 | 782 | case 0xa1: 783 | ASSIGNSETFLAGS(a, MEM(INDIRECTX())); 784 | pc++; 785 | break; 786 | 787 | case 0xb1: 788 | cpucycles += EVALPAGECROSSING_INDIRECTY(); 789 | ASSIGNSETFLAGS(a, MEM(INDIRECTY())); 790 | pc++; 791 | break; 792 | 793 | case 0xa2: 794 | ASSIGNSETFLAGS(x, IMMEDIATE()); 795 | pc++; 796 | break; 797 | 798 | case 0xa6: 799 | ASSIGNSETFLAGS(x, MEM(ZEROPAGE())); 800 | pc++; 801 | break; 802 | 803 | case 0xb6: 804 | ASSIGNSETFLAGS(x, MEM(ZEROPAGEY())); 805 | pc++; 806 | break; 807 | 808 | case 0xae: 809 | ASSIGNSETFLAGS(x, MEM(ABSOLUTE())); 810 | pc += 2; 811 | break; 812 | 813 | case 0xbe: 814 | cpucycles += EVALPAGECROSSING_ABSOLUTEY(); 815 | ASSIGNSETFLAGS(x, MEM(ABSOLUTEY())); 816 | pc += 2; 817 | break; 818 | 819 | case 0xa0: 820 | ASSIGNSETFLAGS(y, IMMEDIATE()); 821 | pc++; 822 | break; 823 | 824 | case 0xa4: 825 | ASSIGNSETFLAGS(y, MEM(ZEROPAGE())); 826 | pc++; 827 | break; 828 | 829 | case 0xb4: 830 | ASSIGNSETFLAGS(y, MEM(ZEROPAGEX())); 831 | pc++; 832 | break; 833 | 834 | case 0xac: 835 | ASSIGNSETFLAGS(y, MEM(ABSOLUTE())); 836 | pc += 2; 837 | break; 838 | 839 | case 0xbc: 840 | cpucycles += EVALPAGECROSSING_ABSOLUTEX(); 841 | ASSIGNSETFLAGS(y, MEM(ABSOLUTEX())); 842 | pc += 2; 843 | break; 844 | 845 | case 0x4a: 846 | LSR(a); 847 | break; 848 | 849 | case 0x46: 850 | LSR(MEM(ZEROPAGE())); 851 | WRITE(ZEROPAGE()); 852 | pc++; 853 | break; 854 | 855 | case 0x56: 856 | LSR(MEM(ZEROPAGEX())); 857 | WRITE(ZEROPAGEX()); 858 | pc++; 859 | break; 860 | 861 | case 0x4e: 862 | LSR(MEM(ABSOLUTE())); 863 | WRITE(ABSOLUTE()); 864 | pc += 2; 865 | break; 866 | 867 | case 0x5e: 868 | LSR(MEM(ABSOLUTEX())); 869 | WRITE(ABSOLUTEX()); 870 | pc += 2; 871 | break; 872 | 873 | case 0xea: 874 | break; 875 | 876 | case 0x09: 877 | ORA(IMMEDIATE()); 878 | pc++; 879 | break; 880 | 881 | case 0x05: 882 | ORA(MEM(ZEROPAGE())); 883 | pc++; 884 | break; 885 | 886 | case 0x15: 887 | ORA(MEM(ZEROPAGEX())); 888 | pc++; 889 | break; 890 | 891 | case 0x0d: 892 | ORA(MEM(ABSOLUTE())); 893 | pc += 2; 894 | break; 895 | 896 | case 0x1d: 897 | cpucycles += EVALPAGECROSSING_ABSOLUTEX(); 898 | ORA(MEM(ABSOLUTEX())); 899 | pc += 2; 900 | break; 901 | 902 | case 0x19: 903 | cpucycles += EVALPAGECROSSING_ABSOLUTEY(); 904 | ORA(MEM(ABSOLUTEY())); 905 | pc += 2; 906 | break; 907 | 908 | case 0x01: 909 | ORA(MEM(INDIRECTX())); 910 | pc++; 911 | break; 912 | 913 | case 0x11: 914 | cpucycles += EVALPAGECROSSING_INDIRECTY(); 915 | ORA(MEM(INDIRECTY())); 916 | pc++; 917 | break; 918 | 919 | case 0x48: 920 | PUSH(a); 921 | break; 922 | 923 | case 0x08: 924 | PUSH(flags | 0x30); 925 | break; 926 | 927 | case 0x68: 928 | ASSIGNSETFLAGS(a, POP()); 929 | break; 930 | 931 | case 0x28: 932 | flags = POP(); 933 | break; 934 | 935 | case 0x2a: 936 | ROL(a); 937 | break; 938 | 939 | case 0x26: 940 | ROL(MEM(ZEROPAGE())); 941 | WRITE(ZEROPAGE()); 942 | pc++; 943 | break; 944 | 945 | case 0x36: 946 | ROL(MEM(ZEROPAGEX())); 947 | WRITE(ZEROPAGEX()); 948 | pc++; 949 | break; 950 | 951 | case 0x2e: 952 | ROL(MEM(ABSOLUTE())); 953 | WRITE(ABSOLUTE()); 954 | pc += 2; 955 | break; 956 | 957 | case 0x3e: 958 | ROL(MEM(ABSOLUTEX())); 959 | WRITE(ABSOLUTEX()); 960 | pc += 2; 961 | break; 962 | 963 | case 0x6a: 964 | ROR(a); 965 | break; 966 | 967 | case 0x66: 968 | ROR(MEM(ZEROPAGE())); 969 | WRITE(ZEROPAGE()); 970 | pc++; 971 | break; 972 | 973 | case 0x76: 974 | ROR(MEM(ZEROPAGEX())); 975 | WRITE(ZEROPAGEX()); 976 | pc++; 977 | break; 978 | 979 | case 0x6e: 980 | ROR(MEM(ABSOLUTE())); 981 | WRITE(ABSOLUTE()); 982 | pc += 2; 983 | break; 984 | 985 | case 0x7e: 986 | ROR(MEM(ABSOLUTEX())); 987 | WRITE(ABSOLUTEX()); 988 | pc += 2; 989 | break; 990 | 991 | case 0x40: 992 | if (sp == 0xff) return 0; 993 | flags = POP(); 994 | pc = POP(); 995 | pc |= POP() << 8; 996 | break; 997 | 998 | case 0x60: 999 | if (sp == 0xff) return 0; 1000 | pc = POP(); 1001 | pc |= POP() << 8; 1002 | pc++; 1003 | break; 1004 | 1005 | case 0xe9: 1006 | case 0xeb: 1007 | SBC(IMMEDIATE()); 1008 | pc++; 1009 | break; 1010 | 1011 | case 0xe5: 1012 | SBC(MEM(ZEROPAGE())); 1013 | pc++; 1014 | break; 1015 | 1016 | case 0xf5: 1017 | SBC(MEM(ZEROPAGEX())); 1018 | pc++; 1019 | break; 1020 | 1021 | case 0xed: 1022 | SBC(MEM(ABSOLUTE())); 1023 | pc += 2; 1024 | break; 1025 | 1026 | case 0xfd: 1027 | cpucycles += EVALPAGECROSSING_ABSOLUTEX(); 1028 | SBC(MEM(ABSOLUTEX())); 1029 | pc += 2; 1030 | break; 1031 | 1032 | case 0xf9: 1033 | cpucycles += EVALPAGECROSSING_ABSOLUTEY(); 1034 | SBC(MEM(ABSOLUTEY())); 1035 | pc += 2; 1036 | break; 1037 | 1038 | case 0xe1: 1039 | SBC(MEM(INDIRECTX())); 1040 | pc++; 1041 | break; 1042 | 1043 | case 0xf1: 1044 | cpucycles += EVALPAGECROSSING_INDIRECTY(); 1045 | SBC(MEM(INDIRECTY())); 1046 | pc++; 1047 | break; 1048 | 1049 | case 0x38: 1050 | flags |= FC; 1051 | break; 1052 | 1053 | case 0xf8: 1054 | flags |= FD; 1055 | break; 1056 | 1057 | case 0x78: 1058 | flags |= FI; 1059 | break; 1060 | 1061 | case 0x85: 1062 | MEM(ZEROPAGE()) = a; 1063 | WRITE(ZEROPAGE()); 1064 | pc++; 1065 | break; 1066 | 1067 | case 0x95: 1068 | MEM(ZEROPAGEX()) = a; 1069 | WRITE(ZEROPAGEX()); 1070 | pc++; 1071 | break; 1072 | 1073 | case 0x8d: 1074 | MEM(ABSOLUTE()) = a; 1075 | WRITE(ABSOLUTE()); 1076 | pc += 2; 1077 | break; 1078 | 1079 | case 0x9d: 1080 | MEM(ABSOLUTEX()) = a; 1081 | WRITE(ABSOLUTEX()); 1082 | pc += 2; 1083 | break; 1084 | 1085 | case 0x99: 1086 | MEM(ABSOLUTEY()) = a; 1087 | WRITE(ABSOLUTEY()); 1088 | pc += 2; 1089 | break; 1090 | 1091 | case 0x81: 1092 | MEM(INDIRECTX()) = a; 1093 | WRITE(INDIRECTX()); 1094 | pc++; 1095 | break; 1096 | 1097 | case 0x91: 1098 | MEM(INDIRECTY()) = a; 1099 | WRITE(INDIRECTY()); 1100 | pc++; 1101 | break; 1102 | 1103 | case 0x86: 1104 | MEM(ZEROPAGE()) = x; 1105 | WRITE(ZEROPAGE()); 1106 | pc++; 1107 | break; 1108 | 1109 | case 0x96: 1110 | MEM(ZEROPAGEY()) = x; 1111 | WRITE(ZEROPAGEY()); 1112 | pc++; 1113 | break; 1114 | 1115 | case 0x8e: 1116 | MEM(ABSOLUTE()) = x; 1117 | WRITE(ABSOLUTE()); 1118 | pc += 2; 1119 | break; 1120 | 1121 | case 0x84: 1122 | MEM(ZEROPAGE()) = y; 1123 | WRITE(ZEROPAGE()); 1124 | pc++; 1125 | break; 1126 | 1127 | case 0x94: 1128 | MEM(ZEROPAGEX()) = y; 1129 | WRITE(ZEROPAGEX()); 1130 | pc++; 1131 | break; 1132 | 1133 | case 0x8c: 1134 | MEM(ABSOLUTE()) = y; 1135 | WRITE(ABSOLUTE()); 1136 | pc += 2; 1137 | break; 1138 | 1139 | case 0xaa: 1140 | ASSIGNSETFLAGS(x, a); 1141 | break; 1142 | 1143 | case 0xba: 1144 | ASSIGNSETFLAGS(x, sp); 1145 | break; 1146 | 1147 | case 0x8a: 1148 | ASSIGNSETFLAGS(a, x); 1149 | break; 1150 | 1151 | case 0x9a: 1152 | sp = x; 1153 | break; 1154 | 1155 | case 0x98: 1156 | ASSIGNSETFLAGS(a, y); 1157 | break; 1158 | 1159 | case 0xa8: 1160 | ASSIGNSETFLAGS(y, a); 1161 | break; 1162 | 1163 | case 0x00: 1164 | return 0; 1165 | 1166 | case 0x02: 1167 | printf("Error: CPU halt at %04X\n", pc-1); 1168 | exit(1); 1169 | break; 1170 | 1171 | default: 1172 | printf("Error: Unknown opcode $%02X at $%04X\n", op, pc-1); 1173 | exit(1); 1174 | break; 1175 | } 1176 | return 1; 1177 | } 1178 | 1179 | void setpc(unsigned short newpc) 1180 | { 1181 | pc = newpc; 1182 | } 1183 | 1184 | -------------------------------------------------------------------------------- /cpu.h: -------------------------------------------------------------------------------- 1 | extern unsigned char mem[]; 2 | extern unsigned int cpucycles; 3 | extern unsigned short pc; 4 | void initcpu(unsigned short newpc, unsigned char newa, unsigned char newx, unsigned char newy); 5 | int runcpu(void); 6 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | SIDDump V1.09 2 | by Lasse Oorni (loorni@gmail.com) and Stein Pedersen 3 | 4 | Version history: 5 | 6 | V1.0 - Original 7 | V1.01 - Fixed BIT instruction 8 | V1.02 - Added incomplete illegal opcode support, enough for John Player 9 | V1.03 - Some CPU bugs fixed 10 | V1.04 - Improved command line handling, added illegal NOP instructions, fixed 11 | illegal LAX to work again 12 | V1.05 - Partial support for multispeed tunes 13 | V1.06 - Added CPU cycle profiling functionality by Stein Pedersen 14 | V1.07 - Support rudimentary line counting for SID detection routines 15 | V1.08 - CPU bugfixes 16 | V1.09 - Add alternating (funktempo) note spacing 17 | 18 | Copyright (C) 2005-2024 by the authors. All rights reserved. 19 | 20 | Redistribution and use in source and binary forms, with or without 21 | modification, are permitted provided that the following conditions are met: 22 | 23 | 1. Redistributions of source code must retain the above copyright notice, 24 | this list of conditions and the following disclaimer. 25 | 2. Redistributions in binary form must reproduce the above copyright notice, 26 | this list of conditions and the following disclaimer in the 27 | documentation and/or other materials provided with the distribution. 28 | 3. The name of the author may not be used to endorse or promote products 29 | derived from this software without specific prior written permission. 30 | 31 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED 32 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 33 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 34 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 35 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 36 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 37 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 38 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 39 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 40 | POSSIBILITY OF SUCH DAMAGE. 41 | -------------------------------------------------------------------------------- /siddump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "cpu.h" 7 | 8 | #define MAX_INSTR 0x100000 9 | 10 | typedef struct 11 | { 12 | unsigned short freq; 13 | unsigned short pulse; 14 | unsigned short adsr; 15 | unsigned char wave; 16 | int note; 17 | } CHANNEL; 18 | 19 | typedef struct 20 | { 21 | unsigned short cutoff; 22 | unsigned char ctrl; 23 | unsigned char type; 24 | } FILTER; 25 | 26 | int main(int argc, char **argv); 27 | unsigned char readbyte(FILE *f); 28 | unsigned short readword(FILE *f); 29 | 30 | CHANNEL chn[3]; 31 | CHANNEL prevchn[3]; 32 | CHANNEL prevchn2[3]; 33 | FILTER filt; 34 | FILTER prevfilt; 35 | 36 | extern unsigned short pc; 37 | 38 | const char *notename[] = 39 | {"C-0", "C#0", "D-0", "D#0", "E-0", "F-0", "F#0", "G-0", "G#0", "A-0", "A#0", "B-0", 40 | "C-1", "C#1", "D-1", "D#1", "E-1", "F-1", "F#1", "G-1", "G#1", "A-1", "A#1", "B-1", 41 | "C-2", "C#2", "D-2", "D#2", "E-2", "F-2", "F#2", "G-2", "G#2", "A-2", "A#2", "B-2", 42 | "C-3", "C#3", "D-3", "D#3", "E-3", "F-3", "F#3", "G-3", "G#3", "A-3", "A#3", "B-3", 43 | "C-4", "C#4", "D-4", "D#4", "E-4", "F-4", "F#4", "G-4", "G#4", "A-4", "A#4", "B-4", 44 | "C-5", "C#5", "D-5", "D#5", "E-5", "F-5", "F#5", "G-5", "G#5", "A-5", "A#5", "B-5", 45 | "C-6", "C#6", "D-6", "D#6", "E-6", "F-6", "F#6", "G-6", "G#6", "A-6", "A#6", "B-6", 46 | "C-7", "C#7", "D-7", "D#7", "E-7", "F-7", "F#7", "G-7", "G#7", "A-7", "A#7", "B-7"}; 47 | 48 | const char *filtername[] = 49 | {"Off", "Low", "Bnd", "L+B", "Hi ", "L+H", "B+H", "LBH"}; 50 | 51 | unsigned char freqtbllo[] = { 52 | 0x17,0x27,0x39,0x4b,0x5f,0x74,0x8a,0xa1,0xba,0xd4,0xf0,0x0e, 53 | 0x2d,0x4e,0x71,0x96,0xbe,0xe8,0x14,0x43,0x74,0xa9,0xe1,0x1c, 54 | 0x5a,0x9c,0xe2,0x2d,0x7c,0xcf,0x28,0x85,0xe8,0x52,0xc1,0x37, 55 | 0xb4,0x39,0xc5,0x5a,0xf7,0x9e,0x4f,0x0a,0xd1,0xa3,0x82,0x6e, 56 | 0x68,0x71,0x8a,0xb3,0xee,0x3c,0x9e,0x15,0xa2,0x46,0x04,0xdc, 57 | 0xd0,0xe2,0x14,0x67,0xdd,0x79,0x3c,0x29,0x44,0x8d,0x08,0xb8, 58 | 0xa1,0xc5,0x28,0xcd,0xba,0xf1,0x78,0x53,0x87,0x1a,0x10,0x71, 59 | 0x42,0x89,0x4f,0x9b,0x74,0xe2,0xf0,0xa6,0x0e,0x33,0x20,0xff}; 60 | 61 | unsigned char freqtblhi[] = { 62 | 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x02, 63 | 0x02,0x02,0x02,0x02,0x02,0x02,0x03,0x03,0x03,0x03,0x03,0x04, 64 | 0x04,0x04,0x04,0x05,0x05,0x05,0x06,0x06,0x06,0x07,0x07,0x08, 65 | 0x08,0x09,0x09,0x0a,0x0a,0x0b,0x0c,0x0d,0x0d,0x0e,0x0f,0x10, 66 | 0x11,0x12,0x13,0x14,0x15,0x17,0x18,0x1a,0x1b,0x1d,0x1f,0x20, 67 | 0x22,0x24,0x27,0x29,0x2b,0x2e,0x31,0x34,0x37,0x3a,0x3e,0x41, 68 | 0x45,0x49,0x4e,0x52,0x57,0x5c,0x62,0x68,0x6e,0x75,0x7c,0x83, 69 | 0x8b,0x93,0x9c,0xa5,0xaf,0xb9,0xc4,0xd0,0xdd,0xea,0xf8,0xff}; 70 | 71 | int main(int argc, char **argv) 72 | { 73 | int subtune = 0; 74 | int seconds = 60; 75 | int instr = 0; 76 | int frames = 0; 77 | int spacing[2]; 78 | int pattspacing = 0; 79 | int firstframe = 0; 80 | int counter = 0; 81 | int basefreq = 0; 82 | int basenote = 0xb0; 83 | int lowres = 0; 84 | int rows = 0; 85 | int oldnotefactor = 1; 86 | int timeseconds = 0; 87 | int usage = 0; 88 | int profiling = 0; 89 | int tempoindex = 0; 90 | unsigned loadend; 91 | unsigned loadpos; 92 | unsigned loadsize; 93 | unsigned loadaddress; 94 | unsigned initaddress; 95 | unsigned playaddress; 96 | unsigned dataoffset; 97 | FILE *in; 98 | char *sidname = 0; 99 | int c; 100 | 101 | // Scan arguments 102 | for (c = 1; c < argc; c++) 103 | { 104 | if (argv[c][0] == '-') 105 | { 106 | switch(toupper(argv[c][1])) 107 | { 108 | case '?': 109 | usage = 1; 110 | break; 111 | 112 | case 'A': 113 | sscanf(&argv[c][2], "%u", &subtune); 114 | break; 115 | 116 | case 'C': 117 | sscanf(&argv[c][2], "%x", &basefreq); 118 | break; 119 | 120 | case 'D': 121 | sscanf(&argv[c][2], "%x", &basenote); 122 | break; 123 | 124 | case 'F': 125 | sscanf(&argv[c][2], "%u", &firstframe); 126 | break; 127 | 128 | case 'L': 129 | lowres = 1; 130 | break; 131 | 132 | case 'N': 133 | // Funktempo 134 | if (strchr(argv[c], ',')) 135 | sscanf(&argv[c][2], "%u,%u", &spacing[0], &spacing[1]); 136 | else 137 | { 138 | sscanf(&argv[c][2], "%u", &spacing[0]); 139 | spacing[1] = spacing[0]; 140 | } 141 | break; 142 | 143 | case 'O': 144 | sscanf(&argv[c][2], "%u", &oldnotefactor); 145 | if (oldnotefactor < 1) oldnotefactor = 1; 146 | break; 147 | 148 | case 'P': 149 | sscanf(&argv[c][2], "%u", &pattspacing); 150 | break; 151 | 152 | case 'S': 153 | timeseconds = 1; 154 | break; 155 | 156 | case 'T': 157 | sscanf(&argv[c][2], "%u", &seconds); 158 | break; 159 | 160 | case 'Z': 161 | profiling = 1; 162 | break; 163 | } 164 | } 165 | else 166 | { 167 | if (!sidname) sidname = argv[c]; 168 | } 169 | } 170 | 171 | // Usage display 172 | if ((argc < 2) || (usage)) 173 | { 174 | printf("Usage: SIDDUMP [options]\n" 175 | "Warning: CPU emulation may be buggy/inaccurate, illegals support very limited\n\n" 176 | "Options:\n" 177 | "-a Accumulator value on init (subtune number) default = 0\n" 178 | "-c Frequency recalibration. Give note frequency in hex\n" 179 | "-d Select calibration note (abs.notation 80-DF). Default middle-C (B0)\n" 180 | "-f First frame to display, default 0\n" 181 | "-l Low-resolution mode (only display 1 row per note)\n" 182 | "-n Note spacing, default 0 (none). Use , to specify a funktempo\n" 183 | "-o ""Oldnote-sticky"" factor. Default 1, increase for better vibrato display\n" 184 | " (when increased, requires well calibrated frequencies)\n" 185 | "-p Pattern spacing, default 0 (none)\n" 186 | "-s Display time in minutes:seconds:frame format\n" 187 | "-t Playback time in seconds, default 60\n" 188 | "-z Include CPU cycles+rastertime (PAL)+rastertime, badline corrected\n"); 189 | return 1; 190 | } 191 | 192 | // Recalibrate frequencytable 193 | if (basefreq) 194 | { 195 | basenote &= 0x7f; 196 | if ((basenote < 0) || (basenote > 96)) 197 | { 198 | printf("Warning: Calibration note out of range. Aborting recalibration.\n"); 199 | } 200 | else 201 | { 202 | for (c = 0; c < 96; c++) 203 | { 204 | double note = c - basenote; 205 | double freq = (double)basefreq * pow(2.0, note/12.0); 206 | int f = freq; 207 | if (freq > 0xffff) freq = 0xffff; 208 | freqtbllo[c] = f & 0xff; 209 | freqtblhi[c] = f >> 8; 210 | } 211 | } 212 | } 213 | 214 | // Check other parameters for correctness 215 | if ((lowres) && (!spacing[0])) lowres = 0; 216 | 217 | // Open SID file 218 | if (!sidname) 219 | { 220 | printf("Error: no SID file specified.\n"); 221 | return 1; 222 | } 223 | 224 | in = fopen(sidname, "rb"); 225 | if (!in) 226 | { 227 | printf("Error: couldn't open SID file.\n"); 228 | return 1; 229 | } 230 | 231 | // Read interesting parts of the SID header 232 | fseek(in, 6, SEEK_SET); 233 | dataoffset = readword(in); 234 | loadaddress = readword(in); 235 | initaddress = readword(in); 236 | playaddress = readword(in); 237 | fseek(in, dataoffset, SEEK_SET); 238 | if (loadaddress == 0) 239 | loadaddress = readbyte(in) | (readbyte(in) << 8); 240 | 241 | // Load the C64 data 242 | loadpos = ftell(in); 243 | fseek(in, 0, SEEK_END); 244 | loadend = ftell(in); 245 | fseek(in, loadpos, SEEK_SET); 246 | loadsize = loadend - loadpos; 247 | if (loadsize + loadaddress >= 0x10000) 248 | { 249 | printf("Error: SID data continues past end of C64 memory.\n"); 250 | fclose(in); 251 | return 1; 252 | } 253 | fread(&mem[loadaddress], loadsize, 1, in); 254 | fclose(in); 255 | 256 | // Print info & run initroutine 257 | printf("Load address: $%04X Init address: $%04X Play address: $%04X\n", loadaddress, initaddress, playaddress); 258 | printf("Calling initroutine with subtune %d\n", subtune); 259 | mem[0x01] = 0x37; 260 | initcpu(initaddress, subtune, 0, 0); 261 | instr = 0; 262 | while (runcpu()) 263 | { 264 | // Allow SID model detection (including $d011 wait) to eventually terminate 265 | ++mem[0xd012]; 266 | if (!mem[0xd012] || ((mem[0xd011] & 0x80) && mem[0xd012] >= 0x38)) 267 | { 268 | mem[0xd011] ^= 0x80; 269 | mem[0xd012] = 0x00; 270 | } 271 | instr++; 272 | if (instr > MAX_INSTR) 273 | { 274 | printf("Warning: CPU executed a high number of instructions in init, breaking\n"); 275 | break; 276 | } 277 | } 278 | 279 | if (playaddress == 0) 280 | { 281 | printf("Warning: SID has play address 0, reading from interrupt vector instead\n"); 282 | if ((mem[0x01] & 0x07) == 0x5) 283 | playaddress = mem[0xfffe] | (mem[0xffff] << 8); 284 | else 285 | playaddress = mem[0x314] | (mem[0x315] << 8); 286 | printf("New play address is $%04X\n", playaddress); 287 | } 288 | 289 | // Clear channelstructures in preparation & print first time info 290 | memset(&chn, 0, sizeof chn); 291 | memset(&filt, 0, sizeof filt); 292 | memset(&prevchn, 0, sizeof prevchn); 293 | memset(&prevchn2, 0, sizeof prevchn2); 294 | memset(&prevfilt, 0, sizeof prevfilt); 295 | printf("Calling playroutine for %d frames, starting from frame %d\n", seconds*50, firstframe); 296 | printf("Middle C frequency is $%04X\n\n", freqtbllo[48] | (freqtblhi[48] << 8)); 297 | printf("| Frame | Freq Note/Abs WF ADSR Pul | Freq Note/Abs WF ADSR Pul | Freq Note/Abs WF ADSR Pul | FCut RC Typ V |"); 298 | if (profiling) 299 | { // CPU cycles, Raster lines, Raster lines with badlines on every 8th line, first line included 300 | printf(" Cycl RL RB |"); 301 | } 302 | printf("\n"); 303 | printf("+-------+---------------------------+---------------------------+---------------------------+---------------+"); 304 | if (profiling) 305 | { 306 | printf("------------+"); 307 | } 308 | printf("\n"); 309 | 310 | // Data collection & display loop 311 | while (frames < firstframe + seconds*50) 312 | { 313 | int c; 314 | 315 | // Run the playroutine 316 | instr = 0; 317 | initcpu(playaddress, 0, 0, 0); 318 | while (runcpu()) 319 | { 320 | instr++; 321 | if (instr > MAX_INSTR) 322 | { 323 | printf("Error: CPU executed abnormally high amount of instructions in playroutine, exiting\n"); 324 | return 1; 325 | } 326 | // Test for jump into Kernal interrupt handler exit 327 | if ((mem[0x01] & 0x07) != 0x5 && (pc == 0xea31 || pc == 0xea81)) 328 | break; 329 | } 330 | 331 | // Get SID parameters from each channel and the filter 332 | for (c = 0; c < 3; c++) 333 | { 334 | chn[c].freq = mem[0xd400 + 7*c] | (mem[0xd401 + 7*c] << 8); 335 | chn[c].pulse = (mem[0xd402 + 7*c] | (mem[0xd403 + 7*c] << 8)) & 0xfff; 336 | chn[c].wave = mem[0xd404 + 7*c]; 337 | chn[c].adsr = mem[0xd406 + 7*c] | (mem[0xd405 + 7*c] << 8); 338 | } 339 | filt.cutoff = (mem[0xd415] << 5) | (mem[0xd416] << 8); 340 | filt.ctrl = mem[0xd417]; 341 | filt.type = mem[0xd418]; 342 | 343 | // Frame display 344 | if (frames >= firstframe) 345 | { 346 | char output[512]; 347 | int time = frames - firstframe; 348 | output[0] = 0; 349 | 350 | if (!timeseconds) 351 | sprintf(&output[strlen(output)], "| %5d | ", time); 352 | else 353 | sprintf(&output[strlen(output)], "|%01d:%02d.%02d| ", time/3000, (time/50)%60, time%50); 354 | 355 | // Loop for each channel 356 | for (c = 0; c < 3; c++) 357 | { 358 | int newnote = 0; 359 | 360 | // Keyoff-keyon sequence detection 361 | if (chn[c].wave >= 0x10) 362 | { 363 | if ((chn[c].wave & 1) && ((!(prevchn2[c].wave & 1)) || (prevchn2[c].wave < 0x10))) 364 | prevchn[c].note = -1; 365 | } 366 | 367 | // Frequency 368 | if ((frames == firstframe) || (prevchn[c].note == -1) || (chn[c].freq != prevchn[c].freq)) 369 | { 370 | int d; 371 | int dist = 0x7fffffff; 372 | int delta = ((int)chn[c].freq) - ((int)prevchn2[c].freq); 373 | 374 | sprintf(&output[strlen(output)], "%04X ", chn[c].freq); 375 | 376 | if (chn[c].wave >= 0x10) 377 | { 378 | // Get new note number 379 | for (d = 0; d < 96; d++) 380 | { 381 | int cmpfreq = freqtbllo[d] | (freqtblhi[d] << 8); 382 | int freq = chn[c].freq; 383 | 384 | if (abs(freq - cmpfreq) < dist) 385 | { 386 | dist = abs(freq - cmpfreq); 387 | // Favor the old note 388 | if (d == prevchn[c].note) dist /= oldnotefactor; 389 | chn[c].note = d; 390 | } 391 | } 392 | 393 | // Print new note 394 | if (chn[c].note != prevchn[c].note) 395 | { 396 | if (prevchn[c].note == -1) 397 | { 398 | if (lowres) newnote = 1; 399 | sprintf(&output[strlen(output)], " %s %02X ", notename[chn[c].note], chn[c].note | 0x80); 400 | } 401 | else 402 | sprintf(&output[strlen(output)], "(%s %02X) ", notename[chn[c].note], chn[c].note | 0x80); 403 | } 404 | else 405 | { 406 | // If same note, print frequency change (slide/vibrato) 407 | if (delta) 408 | { 409 | if (delta > 0) 410 | sprintf(&output[strlen(output)], "(+ %04X) ", delta); 411 | else 412 | sprintf(&output[strlen(output)], "(- %04X) ", -delta); 413 | } 414 | else sprintf(&output[strlen(output)], " ... .. "); 415 | } 416 | } 417 | else sprintf(&output[strlen(output)], " ... .. "); 418 | } 419 | else sprintf(&output[strlen(output)], ".... ... .. "); 420 | 421 | // Waveform 422 | if ((frames == firstframe) || (newnote) || (chn[c].wave != prevchn[c].wave)) 423 | sprintf(&output[strlen(output)], "%02X ", chn[c].wave); 424 | else sprintf(&output[strlen(output)], ".. "); 425 | 426 | // ADSR 427 | if ((frames == firstframe) || (newnote) || (chn[c].adsr != prevchn[c].adsr)) sprintf(&output[strlen(output)], "%04X ", chn[c].adsr); 428 | else sprintf(&output[strlen(output)], ".... "); 429 | 430 | // Pulse 431 | if ((frames == firstframe) || (newnote) || (chn[c].pulse != prevchn[c].pulse)) sprintf(&output[strlen(output)], "%03X ", chn[c].pulse); 432 | else sprintf(&output[strlen(output)], "... "); 433 | 434 | sprintf(&output[strlen(output)], "| "); 435 | } 436 | 437 | // Filter cutoff 438 | if ((frames == firstframe) || (filt.cutoff != prevfilt.cutoff)) sprintf(&output[strlen(output)], "%04X ", filt.cutoff); 439 | else sprintf(&output[strlen(output)], ".... "); 440 | 441 | // Filter control 442 | if ((frames == firstframe) || (filt.ctrl != prevfilt.ctrl)) 443 | sprintf(&output[strlen(output)], "%02X ", filt.ctrl); 444 | else sprintf(&output[strlen(output)], ".. "); 445 | 446 | // Filter passband 447 | if ((frames == firstframe) || ((filt.type & 0x70) != (prevfilt.type & 0x70))) 448 | sprintf(&output[strlen(output)], "%s ", filtername[(filt.type >> 4) & 0x7]); 449 | else sprintf(&output[strlen(output)], "... "); 450 | 451 | // Mastervolume 452 | if ((frames == firstframe) || ((filt.type & 0xf) != (prevfilt.type & 0xf))) sprintf(&output[strlen(output)], "%01X ", filt.type & 0xf); 453 | else sprintf(&output[strlen(output)], ". "); 454 | 455 | // Rasterlines / cycle count 456 | if (profiling) 457 | { 458 | int cycles = cpucycles; 459 | int rasterlines = (cycles + 62) / 63; 460 | int badlines = ((cycles + 503) / 504); 461 | int rasterlinesbad = (badlines * 40 + cycles + 62) / 63; 462 | sprintf(&output[strlen(output)], "| %4d %02X %02X ", cycles, rasterlines, rasterlinesbad); 463 | } 464 | 465 | // End of frame display, print info so far and copy SID registers to old registers 466 | sprintf(&output[strlen(output)], "|\n"); 467 | if ((!lowres) || (!((frames - firstframe) % spacing[tempoindex]))) 468 | { 469 | printf("%s", output); 470 | for (c = 0; c < 3; c++) 471 | { 472 | prevchn[c] = chn[c]; 473 | } 474 | prevfilt = filt; 475 | } 476 | for (c = 0; c < 3; c++) prevchn2[c] = chn[c]; 477 | 478 | // Print note/pattern separators 479 | if (spacing[tempoindex]) 480 | { 481 | counter++; 482 | if (counter >= spacing[tempoindex]) 483 | { 484 | tempoindex ^= 1; 485 | counter = 0; 486 | if (pattspacing) 487 | { 488 | rows++; 489 | if (rows >= pattspacing) 490 | { 491 | rows = 0; 492 | printf("+=======+===========================+===========================+===========================+===============+\n"); 493 | } 494 | else 495 | if (!lowres) printf("+-------+---------------------------+---------------------------+---------------------------+---------------+\n"); 496 | } 497 | else 498 | if (!lowres) printf("+-------+---------------------------+---------------------------+---------------------------+---------------+\n"); 499 | } 500 | } 501 | } 502 | 503 | // Advance to next frame 504 | frames++; 505 | } 506 | return 0; 507 | } 508 | 509 | unsigned char readbyte(FILE *f) 510 | { 511 | unsigned char res; 512 | 513 | fread(&res, 1, 1, f); 514 | return res; 515 | } 516 | 517 | unsigned short readword(FILE *f) 518 | { 519 | unsigned char res[2]; 520 | 521 | fread(&res, 2, 1, f); 522 | return (res[0] << 8) | res[1]; 523 | } 524 | 525 | 526 | 527 | 528 | --------------------------------------------------------------------------------