/ c2t.c
c2t.c
1 /* 2 3 c2t, Code to Tape|Text, Version 0.997, Wed Sep 27 15:27:56 GMT 2017 4 5 Parts copyright (c) 2011-2017 All Rights Reserved, Egan Ford (egan@sense.net) 6 7 THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 8 KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 9 IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 10 PARTICULAR PURPOSE. 11 12 Built on work by: 13 * Mike Willegal (http://www.willegal.net/appleii/toaiff.c) 14 * Paul Bourke (http://paulbourke.net/dataformats/audio/, AIFF and WAVE output code) 15 * Malcolm Slaney and Ken Turkowski (Integer to IEEE 80-bit float code) 16 * Lance Leventhal and Winthrop Saville (6502 Assembly Language Subroutines, CRC 6502 code) 17 * Piotr Fusik (http://atariarea.krap.pl/x-asm/inflate.html, inflate 6502 code) 18 * Rich Geldreich (http://code.google.com/p/miniz/, deflate C code) 19 * Mike Chambers (http://rubbermallet.org/fake6502.c, 6502 simulator) 20 21 License: 22 * Do what you like, remember to credit all sources when using. 23 24 Description: 25 This small utility will read Apple I/II binary and 26 monitor text files and output Apple I or II AIFF and WAV 27 audio files for use with the Apple I and II cassette 28 interface. 29 30 Features: 31 * Apple I, II, II+, IIe support. 32 * Big and little-endian machine support. 33 o Little-endian tested. 34 * AIFF and WAVE output (both tested). 35 * Platforms tested: 36 o 32-bit/64-bit x86 OS/X. 37 o 32-bit/64-bit x86 Linux. 38 o 32-bit x86 Windows/Cygwin. 39 o 32-bit x86 Windows/MinGW. 40 * Multi-segment tapes. 41 42 Compile: 43 OS/X: 44 gcc -Wall -O -o c2t c2t.c 45 Linux: 46 gcc -Wall -O -o c2t c2t.c -lm 47 Windows/Cygwin: 48 gcc -Wall -O -o c2t c2t.c 49 Windows/MinGW: 50 PATH=C:\MinGW\bin;%PATH% 51 gcc -Wall -O -static -o c2t c2t.c 52 53 Notes: 54 * Virtual ][ only supports .aif (or .cass) 55 * Dropbox only supports .wav and .aiff (do not use .wave or .aif) 56 57 Not yet done: 58 * Test big-endian. 59 * gnuindent 60 * Redo malloc code in appendtone 61 62 Thinking about: 63 * Check for existing file and abort, or warn, or prompt. 64 * -q quiet option for Makefiles 65 * autoload support for basic programs 66 67 Bugs: 68 * Probably 69 70 */ 71 72 #if defined(_WIN32) 73 #include "miniz_win32.h" 74 #else 75 #include "miniz.h" 76 #endif 77 78 #include <fake6502.h> 79 #include <stdio.h> 80 #include <stdlib.h> 81 #include <ctype.h> 82 #include <unistd.h> 83 #include <string.h> 84 #include <math.h> 85 #include <c2t.h> 86 87 #define ABS(x) (((x) < 0) ? -(x) : (x)) 88 89 #define VERSION "Version 0.997" 90 #define OUTFILE argv[argc-1] 91 #define BINARY 0 92 #define MONITOR 1 93 #define AIFF 2 94 #define WAVE 3 95 #define DSK 4 96 97 #define WRITEBYTE(x) { \ 98 unsigned char wb_j, wb_temp=(x); \ 99 for(wb_j=0;wb_j<8;wb_j++) { \ 100 if(wb_temp & 0x80) \ 101 appendtone(&output,&outputlength,freq1,rate,0,1,&offset); \ 102 else \ 103 appendtone(&output,&outputlength,freq0,rate,0,1,&offset); \ 104 wb_temp<<=1; \ 105 } \ 106 } 107 108 void usage(); 109 char *getext(char *filename); 110 void appendtone(double **sound, long *length, int freq, int rate, double time, double cycles, int *offset); 111 void Write_AIFF(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp); 112 void Write_WAVE(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp); 113 void ConvertToIeeeExtended(double num, unsigned char *bytes); 114 uint8_t read6502(uint16_t address); 115 void write6502(uint16_t address, uint8_t value); 116 117 unsigned char ram[65536]; 118 int square = 0; 119 120 typedef struct seg { 121 int start; 122 int length; 123 int codelength; 124 unsigned char *data; 125 char filename[256]; 126 } segment; 127 128 int main(int argc, char **argv) 129 { 130 FILE *ofp; 131 double *output = NULL, amp=0.75; 132 long outputlength=0; 133 int i, c, model=0, outputtype, offset=0, fileoutput=1, warm=0, dsk=0, noformat=0, k8=0, qr=0; 134 int autoload=0, basicload=0, compress=0, fast=0, cd=0, tape=0, endpage=0, longmon=0, rate=11025, bits=8, freq0=2000, freq1=1000, freq_pre=770, freq_end=770; 135 char *filetypes[] = {"binary","monitor","aiff","wave","disk"}; 136 char *modeltypes[] = {"\b","I","II"}; 137 char *ext; 138 unsigned int numseg = 0; 139 segment *segments = NULL; 140 141 opterr = 1; 142 while((c = getopt(argc, argv, "12vabcftdpn8meh?lqr:")) != -1) 143 switch(c) { 144 case '1': // apple 1 145 rate = 8000; 146 model = 1; 147 break; 148 case '2': // apple 2 149 model = 2; 150 break; 151 case 'v': // version 152 fprintf(stderr,"\n%s\n\n",VERSION); 153 return 1; 154 break; 155 case 'a': // assembly autoloader 156 model = 2; 157 autoload = 1; 158 break; 159 case 'b': // basic autoloader 160 model = 2; 161 basicload = autoload = 1; 162 break; 163 case 'c': // compression 164 model = 2; 165 autoload = compress = 1; 166 break; 167 case 'f': // hifreq 168 rate = 48000; 169 model = 2; 170 autoload = fast = 1; 171 cd = k8 = 0; 172 break; 173 case 'd': // hifreq CD 174 rate = 44100; 175 bits = 16; 176 amp = 1.0; 177 model = 2; 178 cd = autoload = 1; 179 fast = k8 = 0; 180 break; 181 case 't': // 10 sec leader 182 tape = 6; 183 amp = 1.0; 184 break; 185 case 'm': // drop to monitor after load 186 warm = 1; 187 break; 188 case 'e': // end on page boundary 189 endpage = 1; 190 break; 191 case 'p': // stdout 192 fileoutput = 0; 193 break; 194 case 'n': 195 noformat = 1; 196 break; 197 case '8': // 8k 198 rate = 48000; 199 model = 2; 200 autoload = k8 = 1; 201 fast = cd = 0; 202 break; 203 case 'h': // help 204 case '?': 205 usage(); 206 return 1; 207 case 'q': // qr code support 208 rate = 48000; 209 model = 2; 210 autoload = k8 = qr = 1; 211 fast = cd = 0; 212 break; 213 case 'l': // long mon lines 214 longmon = 1; 215 break; 216 case 'r': // override rate for -1/-2 only 217 rate = atoi(optarg); 218 autoload = basicload = k8 = qr = fast = cd = 0; 219 break; 220 } 221 222 if(argc - optind < 1 + fileoutput) { 223 usage(); 224 return 1; 225 } 226 227 // read intput files 228 229 fprintf(stderr,"\n"); 230 for(i=optind;i<argc-fileoutput;i++) { 231 char start[5]; 232 unsigned char b, *data; 233 int j, k, inputtype=BINARY; 234 segment *tmp; 235 FILE *ifp; 236 237 if((tmp = realloc(segments, (numseg+1) * sizeof(segment))) == NULL) { 238 fprintf(stderr,"could not allocate segment %d\n",numseg+1); 239 abort(); 240 } 241 segments = tmp; 242 243 k=0; 244 for(j=0;j<strlen(argv[i]);j++) { 245 if(argv[i][j] == ',') 246 break; 247 segments[numseg].filename[k++]=argv[i][j]; 248 } 249 segments[numseg].filename[k] = '\0'; 250 // TODO: store as basename, check for MINGW compat 251 252 k=0;j++; 253 for(;j<strlen(argv[i]);j++) 254 start[k++]=argv[i][j]; 255 start[k] = '\0'; 256 if(k == 0) 257 segments[numseg].start = -1; 258 else 259 segments[numseg].start = (int)strtol(start, (char **)NULL, 16); 260 261 if((ext = getext(segments[numseg].filename)) != NULL) 262 if(strcmp(ext,"mon") == 0) 263 inputtype = MONITOR; 264 265 if((ext = getext(segments[numseg].filename)) != NULL) 266 if(strcmp(ext,"dsk") == 0) 267 inputtype = DSK; 268 269 //TODO: Windows needs "rb", check UNIX/Linux 270 271 if ((ifp = fopen(segments[numseg].filename, "rb")) == NULL) { 272 fprintf(stderr,"Cannot read: %s\n\n",segments[numseg].filename); 273 return 1; 274 } 275 276 fprintf(stderr,"Reading %s, type %s, segment %d, start: ",segments[numseg].filename,filetypes[inputtype],numseg+1); 277 278 //hack to support dumping disks for testing, should be 48, not 140 (really should be dynamic) 279 280 if((data = malloc(140*1024*sizeof(char))) == NULL) { 281 fprintf(stderr,"could not allocate 140K data\n"); 282 abort(); 283 } 284 285 if(inputtype == DSK) { 286 dsk = 1; 287 segments[numseg].length = 0; 288 for(i=0;i<5;i++) { 289 //segments[numseg].start=i*(140 * 1024 / 5); 290 segments[numseg].start=0x1000; 291 292 while(fread(&b, 1, 1, ifp) == 1 && segments[numseg].length < (140 * 1024 / 5)) 293 data[segments[numseg].length++]=b; 294 295 segments[numseg].data = data; 296 fprintf(stderr,"0x%04X, length: %d\n",segments[numseg].start,segments[numseg].length); 297 298 if(segments[numseg].length != (140 * 1024 / 5)) { 299 fprintf(stderr,"\n%s segment too short (< %d) for file type DISK\n\n",segments[numseg].filename,140*1024/5); 300 return 1; 301 } 302 303 if(i==4) 304 break; 305 306 numseg++; 307 if((tmp = realloc(segments, (numseg+1) * sizeof(segment))) == NULL) { 308 fprintf(stderr,"could not allocate segment %d\n",numseg+1); 309 abort(); 310 } 311 segments = tmp; 312 strcpy(segments[numseg].filename,segments[numseg-1].filename); 313 segments[numseg].length = 0; 314 if((data = malloc(48*1024*sizeof(char))) == NULL) { 315 fprintf(stderr,"could not allocate 48K data\n"); 316 abort(); 317 } 318 data[segments[numseg].length++]=b; 319 320 fprintf(stderr,"Reading %s, type %s, segment %d, start: ",segments[numseg].filename,filetypes[inputtype],numseg+1); 321 } 322 } 323 324 if(inputtype == BINARY) { 325 if(segments[numseg].start == -1) { 326 fread(&b, 1, 1, ifp); 327 segments[numseg].start = b; 328 fread(&b, 1, 1, ifp); 329 segments[numseg].start |= b << 8; 330 fread(&b, 1, 1, ifp); 331 segments[numseg].length = b; 332 fread(&b, 1, 1, ifp); 333 segments[numseg].length |= b << 8; 334 } 335 336 segments[numseg].length=0; 337 while(fread(&b, 1, 1, ifp) == 1) 338 data[segments[numseg].length++]=b; 339 340 segments[numseg].data = data; 341 fprintf(stderr,"0x%04X, length: %d\n",segments[numseg].start,segments[numseg].length); 342 } 343 344 if(inputtype == MONITOR) { 345 int byte, naddr; 346 char addrs[8], s; 347 348 segments[numseg].start = -1; 349 segments[numseg].length = 0; 350 351 while(fscanf(ifp,"%s ",addrs) != EOF) { 352 naddr = (int)strtol(addrs, (char **)NULL, 16); 353 if(segments[numseg].start == -1) 354 segments[numseg].start = naddr; 355 356 if(naddr != segments[numseg].start + segments[numseg].length) { // multi segment 357 segments[numseg].data = data; 358 fprintf(stderr,"0x%04X, length: %d\n",segments[numseg].start,segments[numseg].length); 359 numseg++; 360 if((tmp = realloc(segments, (numseg+1) * sizeof(segment))) == NULL) { 361 fprintf(stderr,"could not allocate segment %d\n",numseg+1); 362 abort(); 363 } 364 segments = tmp; 365 if((data = malloc(48*1024*sizeof(char))) == NULL) { 366 fprintf(stderr,"could not allocate 48K data\n"); 367 abort(); 368 } 369 segments[numseg].start = naddr; 370 segments[numseg].length = 0; 371 strcpy(segments[numseg].filename,segments[numseg-1].filename); 372 fprintf(stderr,"Reading %s, type %s, segment %d, start: ",segments[numseg].filename,filetypes[inputtype],numseg+1); 373 } 374 375 while (fscanf(ifp, "%x%c", &byte, &s) != EOF) { 376 data[segments[numseg].length++]=byte; 377 if (s == '\n' || s == '\r') 378 break; 379 } 380 } 381 segments[numseg].data = data; 382 fprintf(stderr,"0x%04X, length: %d\n",segments[numseg].start,segments[numseg].length); 383 } 384 385 fclose(ifp); 386 numseg++; 387 } 388 fprintf(stderr,"\n"); 389 390 if(dsk) { 391 fast=autoload=cd=tape=0; 392 model=2; 393 394 if(numseg != 5) { 395 fprintf(stderr,"Number of segments != 5 and/or not of length %d\n\n",140*1024/5); 396 return 1; 397 } 398 else { 399 for(i=0;i<5;i++) { 400 if(segments[i].length != 140*1024/5) { 401 fprintf(stderr,"Number of segments != 5 and/or not of length %d\n\n",140*1024/5); 402 return 1; 403 } 404 } 405 } 406 } 407 408 if(endpage) 409 for(i=0;i<numseg;i++) { 410 int pad = (0xFF - ((segments[i].length + segments[i].start - 1) & 0xFF)); 411 412 segments[i].length += pad; 413 while(pad--) 414 segments[i].data[segments[i].length - pad - 1] = 0; 415 } 416 417 if(numseg > 1 || model == 1) { 418 if(autoload) 419 fprintf(stderr,"WARNING: number of segments > 1 or model = 1: autoload and fast disabled.\n\n"); 420 autoload = fast = 0; 421 } 422 423 if(fileoutput) { 424 if((ext = getext(OUTFILE)) == NULL) { 425 usage(); 426 return 1; 427 } 428 else { 429 if(strcmp(ext,"aiff") == 0 || strcmp(ext,"aif") == 0) 430 outputtype = AIFF; 431 else if(strcmp(ext,"wave") == 0 || strcmp(ext,"wav") == 0) 432 outputtype = WAVE; 433 else if(strcmp(ext,"mon") == 0) 434 outputtype = MONITOR; 435 else { 436 usage(); 437 return 1; 438 } 439 } 440 } 441 else { 442 /* 443 if(!model) 444 outputtype = MONITOR; 445 else 446 outputtype = AIFF; 447 */ 448 outputtype = MONITOR; 449 } 450 451 if(outputtype != MONITOR && !model) { 452 fprintf(stderr,"\nYou must specify -1 or -2 for Apple I or II tape format, exiting.\n\n"); 453 return 1; 454 } 455 456 // TODO: check for existing file and abort, or warn, or prompt 457 458 ofp=stdout; 459 if(fileoutput) { 460 if ((ofp = fopen(OUTFILE, "w")) == NULL) { 461 fprintf(stderr,"\nCannot write: %s\n\n",OUTFILE); 462 return 1; 463 } 464 fprintf(stderr,"Writing %s as Apple %s formatted %s.\n\n",OUTFILE,modeltypes[model],filetypes[outputtype]); 465 } 466 else 467 fprintf(stderr,"Writing %s as Apple %s formatted %s.\n\n","STDOUT",modeltypes[model],filetypes[outputtype]); 468 469 if(outputtype == MONITOR) { 470 int i, j, saddr; 471 // unsigned long cmp_len; 472 size_t cmp_len; 473 unsigned char *cmp_data; 474 475 for(i=0;i<numseg;i++) { 476 if(compress) { 477 cmp_data = tdefl_compress_mem_to_heap(segments[i].data, segments[i].length, &cmp_len, TDEFL_MAX_PROBES_MASK); 478 free(segments[i].data); 479 segments[i].data = cmp_data; 480 segments[i].length = cmp_len; 481 } 482 saddr = segments[i].start; 483 fprintf(ofp,"%04X:", saddr); 484 for(j=0;j<segments[i].length;j++) { 485 fprintf(ofp," %02X", segments[i].data[j]); 486 if(++saddr % (8+(24*longmon)) == 0 && j < segments[i].length - 1) 487 fprintf(ofp,"\n%04X:",saddr); 488 } 489 fprintf(ofp,"\n"); 490 } 491 492 fclose(ofp); 493 return 0; 494 } 495 496 // write out code 497 if(!autoload && !dsk) { 498 int i, j; 499 //unsigned long cmp_len; 500 size_t cmp_len; 501 unsigned char *cmp_data; 502 char checksum; 503 504 for(i=0;i<numseg;i++) { 505 // header 506 if(model == 1) { 507 appendtone(&output,&outputlength,1000,rate,4.0+tape,0,&offset); 508 appendtone(&output,&outputlength,2000,rate,0,1,&offset); 509 } 510 else { 511 appendtone(&output,&outputlength,770,rate,4.0+tape,0,&offset); 512 appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); 513 appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); 514 } 515 checksum = 0xff; 516 517 if(compress) { 518 cmp_data = tdefl_compress_mem_to_heap(segments[i].data, segments[i].length, &cmp_len, TDEFL_MAX_PROBES_MASK); 519 free(segments[i].data); 520 segments[i].data = cmp_data; 521 segments[i].length = cmp_len; 522 } 523 for(j=0;j<segments[i].length;j++) { 524 WRITEBYTE(segments[i].data[j]); 525 checksum ^= segments[i].data[j]; 526 } 527 528 // checksum/endbits 529 if(model == 2) 530 WRITEBYTE(checksum); 531 appendtone(&output,&outputlength,1000,rate,0,1,&offset); 532 } 533 534 // friendly help 535 fprintf(stderr,"To load up and run on your Apple %s, type:\n\n",modeltypes[model]); 536 if(model == 1) 537 fprintf(stderr,"\tC100R\n\t"); 538 else 539 fprintf(stderr,"\tCALL -151\n\t"); 540 541 for(i=0;i<numseg;i++) 542 fprintf(stderr,"%X.%XR ",segments[i].start,segments[i].start+segments[i].length-1); 543 fprintf(stderr,"\n"); 544 545 if(numseg == 1) { 546 if(model == 1) 547 fprintf(stderr,"\t%XR\n",segments[0].start); 548 else 549 fprintf(stderr,"\t%XG\n",segments[0].start); 550 } 551 fprintf(stderr,"\n"); 552 } 553 554 if(autoload) { 555 char eta[40], loading[]=" LOADING "; 556 unsigned char byte, checksum, *cmp_data, table[12]; 557 unsigned long ones=0, zeros=0; 558 size_t cmp_len; 559 unsigned int length, move_len; 560 int i, j; 561 562 appendtone(&output,&outputlength,770,rate,4.0+tape,0,&offset); 563 appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); 564 appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); 565 566 // compute uncompressed ETA 567 for(j=0;j<segments[0].length;j++) { 568 byte=segments[0].data[j]; 569 for(i=0;i<8;i++) { 570 if(byte & 0x80) 571 ones++; 572 else 573 zeros++; 574 byte <<= 1; 575 } 576 } 577 578 if(fast) { 579 freq0 = 12000; 580 freq1 = 8000; 581 freq_pre = 6000; 582 freq_end = 2000; 583 } 584 if(k8) { 585 freq0 = 12000; 586 freq1 = 6000; 587 freq_pre = 2000; 588 freq_end = 770; 589 } 590 if(cd) { 591 freq0 = 11025; 592 freq1 = 7350; 593 freq_pre = 5512; 594 freq_end = 2000; 595 } 596 597 if(compress) { 598 unsigned long cmp_ones=0, cmp_zeros=0; 599 double inflate_time = 0; 600 unsigned int endj; 601 602 cmp_data = tdefl_compress_mem_to_heap(segments[0].data, segments[0].length, &cmp_len, TDEFL_MAX_PROBES_MASK); 603 604 for(j=0;j<cmp_len;j++) { 605 byte=cmp_data[j]; 606 for(i=0;i<8;i++) { 607 if(byte & 0x80) 608 cmp_ones++; 609 else 610 cmp_zeros++; 611 byte <<= 1; 612 } 613 } 614 615 // we need to append inflate/decompress code to end of data 616 for(j=0;j<sizeof(inflatecode)/sizeof(char);j++) { 617 byte=inflatecode[j]; 618 for(i=0;i<8;i++) { 619 if(byte & 0x80) 620 cmp_ones++; 621 else 622 cmp_zeros++; 623 byte <<= 1; 624 } 625 } 626 627 //compute inflate time 628 629 //load up inflate data 630 checksum = 0xff; 631 for(j=0;j<cmp_len;j++) { 632 ram[0xBA00 - cmp_len + j] = cmp_data[j]; 633 checksum ^= cmp_data[j]; 634 } 635 //load up inflate code 636 for(j=0;j<sizeof(inflatecode)/sizeof(char);j++) { 637 ram[0xBA00 + j] = inflatecode[j]; 638 checksum ^= inflatecode[j]; 639 } 640 ram[0xBA00 + j] = checksum; 641 endj = 0xBA00 + j + 1; 642 643 if(k8) { 644 for(j=(0x823 - 0x80C);j<sizeof(fastload8000)/sizeof(char);j++) 645 ram[0xBE80 - (0x823 - 0x80C) + j] = fastload8000[j]; 646 ram[0xBE80 - (0x823 - 0x80C) + j++] = (0xBA00 - cmp_len) & 0xFF; 647 ram[0xBE80 - (0x823 - 0x80C) + j++] = (0xBA00 - cmp_len) >> 8; 648 ram[0xBE80 - (0x823 - 0x80C) + j++] = endj & 0xFF; 649 ram[0xBE80 - (0x823 - 0x80C) + j++] = endj >> 8; 650 ram[0x00] = 0xFF; 651 ram[0xBF09] = 0x00; //BRK 652 653 reset6502(); 654 exec6502(0xBEE3); 655 656 if(ram[0x00] != 0) 657 fprintf(stderr,"WARNING: simulated checksum failed: %02X\n",ram[0x00]); 658 659 inflate_time += clockticks6502/1023000.0; 660 } 661 662 //zero page src 663 ram[0x0] = (0xBA00 - cmp_len) & 0xFF; 664 ram[0x1] = (0xBA00 - cmp_len) >> 8; 665 //zero page dst 666 ram[0x2] = (segments[0].start) & 0xFF; 667 ram[0x3] = (segments[0].start) >> 8; 668 //setup JSR 669 ram[0xBF00] = 0x20; // JSR $9B00 670 ram[0xBF01] = 0x00; 671 ram[0xBF02] = 0xBA; 672 ram[0xBF03] = 0x00; //BRK to stop simulation 673 //run it 674 reset6502(); 675 exec6502(0xBF00); 676 //compare (just to be safe) 677 for(j=0;j<segments[0].length;j++) 678 if(ram[segments[0].start + j] != segments[0].data[j]) { 679 fprintf(stderr,"WARNING: simulated inflate failed at %04X\n",j+0x1000); 680 break; 681 } 682 inflate_time += clockticks6502/1023000.0; 683 684 fprintf(stderr,"start: 0x%04X, length: %5d, deflated: %.02f%%, data time:%.02f, inflate time:%.02f\n",(unsigned int)(0xB9FF - cmp_len),(unsigned int)cmp_len,100.0*(1-cmp_len/(float)segments[0].length),cmp_ones/(float)freq1 + cmp_zeros/(float)freq0,inflate_time); 685 686 if((ones/(float)freq1 + zeros/(float)freq0) < inflate_time + (cmp_ones/(float)freq1 + cmp_zeros/(float)freq0)) { 687 fprintf(stderr,"WARNING: compression disabled: no significant gain (%.02f)\n",ones/(float)freq1 + zeros/(float)freq0); 688 compress = 0; 689 } 690 else { 691 free(segments[0].data); 692 segments[0].data = cmp_data; 693 segments[0].codelength = segments[0].length; 694 segments[0].length = cmp_len; 695 ones=cmp_ones; 696 zeros=cmp_zeros; 697 } 698 fprintf(stderr,"\n"); 699 } 700 701 sprintf(eta,", ETA %d SEC. ",(int) (ones/(float)freq1 + zeros/(float)freq0 + 0.5 + 0.25 + (3.75 * ((k8|cd|fast) == 0))) ); 702 703 length = sizeof(basic)/sizeof(char) + sizeof(table)/sizeof(char) + strlen(loading) + strlen(segments[0].filename) + strlen(eta) + 1; 704 705 move_len = (0x823 - 0x80C); 706 if(fast) 707 length += sizeof(fastload9600)/sizeof(char); 708 else 709 if(k8) 710 length += sizeof(fastload8000)/sizeof(char); 711 else 712 if(cd) 713 length += sizeof(fastloadcd)/sizeof(char); 714 else { 715 length += sizeof(autoloadcode)/sizeof(char); 716 move_len = (0x81A - 0x80C); 717 } 718 719 if(fast | k8 | cd) { 720 if(length - sizeof(basic)/sizeof(char) - move_len > 384) { 721 segments[0].filename[strlen(segments[0].filename) - (length - sizeof(basic)/sizeof(char) - move_len - 384)] = '\0'; 722 fprintf(stderr,"WARNING: BF00 page overflow: truncating display filename to %s\n\n",segments[0].filename); 723 length = 384 + sizeof(basic)/sizeof(char) + move_len; 724 } 725 } 726 else { 727 if(length - sizeof(basic)/sizeof(char) - move_len > 256) { 728 segments[0].filename[strlen(segments[0].filename) - (length - sizeof(basic)/sizeof(char) - move_len - 256)] = '\0'; 729 fprintf(stderr,"WARNING: BF00 page overflow: truncating display filename to %s\n\n",segments[0].filename); 730 length = 256 + sizeof(basic)/sizeof(char) + move_len; 731 } 732 } 733 734 freq0 = 2000; 735 freq1 = 1000; 736 checksum = 0xff; 737 738 if(basicload) { // write basic stub 739 header[0] = length & 0xFF; 740 header[1] = length >> 8; 741 for(i=0;i<3;i++) { 742 WRITEBYTE(header[i]); 743 checksum ^= header[i]; 744 } 745 WRITEBYTE(checksum); 746 747 appendtone(&output,&outputlength,1000,rate,0,1,&offset); 748 appendtone(&output,&outputlength,770,rate,4.0,0,&offset); 749 appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); 750 appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); 751 752 // write out basic program 753 checksum = 0xff; 754 for(i=0;i<sizeof(basic)/sizeof(char);i++) { 755 WRITEBYTE(basic[i]); 756 checksum ^= basic[i]; 757 } 758 } 759 else { // write out JMP 80C NOP NOP ... 760 unsigned char patch[] = {0x4C,0x0C,0x08,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA}; 761 for(i=0;i<sizeof(patch)/sizeof(char);i++) { 762 WRITEBYTE(patch[i]); 763 checksum ^= patch[i]; 764 } 765 } 766 767 // write out move and load code 768 if(compress) { 769 unsigned int cmp_start = 0xBA00 - segments[0].length; 770 771 //load start 772 table[0] = cmp_start & 0xff; 773 table[1] = cmp_start >> 8; 774 775 //load end 776 table[2] = (cmp_start + segments[0].length + sizeof(inflatecode)/sizeof(char) + 1) & 0xff; 777 table[3] = (cmp_start + segments[0].length + sizeof(inflatecode)/sizeof(char) + 1) >> 8; 778 779 //inflate src 780 table[4] = cmp_start & 0xff; 781 table[5] = cmp_start >> 8; 782 783 //inflate end 784 table[8] = (segments[0].start + segments[0].codelength) & 0xff; 785 table[9] = (segments[0].start + segments[0].codelength) >> 8; 786 } 787 else { 788 //load start 789 table[0] = segments[0].start & 0xff; 790 table[1] = segments[0].start >> 8; 791 792 //load end 793 table[2] = (segments[0].start + segments[0].length + 1) & 0xff; 794 table[3] = (segments[0].start + segments[0].length + 1) >> 8; 795 } 796 //JMP to code, inflate dst 797 table[6] = segments[0].start & 0xff; 798 table[7] = segments[0].start >> 8; 799 table[10] = compress; 800 table[11] = warm; 801 802 if(fast) 803 for(i=0;i<sizeof(fastload9600)/sizeof(char);i++) { 804 WRITEBYTE(fastload9600[i]); 805 checksum ^= fastload9600[i]; 806 } 807 else 808 if(k8) 809 for(i=0;i<sizeof(fastload8000)/sizeof(char);i++) { 810 WRITEBYTE(fastload8000[i]); 811 checksum ^= fastload8000[i]; 812 } 813 else 814 if(cd) 815 for(i=0;i<sizeof(fastloadcd)/sizeof(char);i++) { 816 WRITEBYTE(fastloadcd[i]); 817 checksum ^= fastloadcd[i]; 818 } 819 else 820 for(i=0;i<sizeof(autoloadcode)/sizeof(char);i++) { 821 WRITEBYTE(autoloadcode[i]); 822 checksum ^= autoloadcode[i]; 823 } 824 825 // append table 826 for(i=0;i<sizeof(table)/sizeof(char);i++) { 827 WRITEBYTE(table[i]); 828 checksum ^= table[i]; 829 } 830 831 // append LOADING... 832 loading[0] = 0x0D; 833 for(i=0;i<strlen(loading);i++) { 834 byte = toupper(loading[i]) + 0x80; 835 if(loading[i] == '_') 836 byte = toupper(' ') + 0x80; 837 WRITEBYTE(byte); 838 checksum ^= byte; 839 } 840 841 // append to loader the name of the file 842 for(i=0;i<strlen(segments[0].filename);i++) { 843 byte = toupper(segments[0].filename[i]) + 0x80; 844 if(segments[0].filename[i] == '_') 845 byte = toupper(' ') + 0x80; 846 WRITEBYTE(byte); 847 checksum ^= byte; 848 } 849 850 // append to loader the ETA 851 for(i=0;i<strlen(eta);i++) { 852 byte = toupper(eta[i]) + 0x80; 853 WRITEBYTE(byte); 854 checksum ^= byte; 855 } 856 857 // append to NULL to LOADING string 858 WRITEBYTE(0x00); 859 checksum ^= 0x00; 860 861 // it's a wrap! 862 WRITEBYTE(0xff); 863 checksum ^= 0xff; 864 865 if(!basicload) { 866 int pad = (0xFF - (length & 0xFF)); 867 868 if(!(fast|cd|k8)) 869 pad += 0x100; 870 871 length += pad; 872 while(pad--) 873 WRITEBYTE(0x00); 874 } 875 876 WRITEBYTE(checksum); 877 878 appendtone(&output,&outputlength,1000,rate,0,1,&offset); 879 if(fast || cd || k8) 880 appendtone(&output,&outputlength,freq_pre,rate,0.25,0,&offset); 881 else { 882 appendtone(&output,&outputlength,770,rate,4.0,0,&offset); 883 appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); 884 appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); 885 } 886 887 // now the code 888 if(fast) { 889 freq0 = 12000; 890 freq1 = 8000; 891 } 892 if(cd) { 893 freq0 = 11025; 894 freq1 = 7350; 895 } 896 if(k8) { 897 freq0 = 12000; 898 freq1 = 6000; 899 } 900 901 if(qr) { 902 char loading[]="LOADING "; 903 outputlength = 0; 904 905 // 0.25 sec 906 appendtone(&output,&outputlength,freq_pre,rate,0.25,0,&offset); 907 908 checksum = 0xff; 909 910 // parameters, 12 bytes 911 for(i=0;i<sizeof(table)/sizeof(char);i++) { 912 WRITEBYTE(table[i]); 913 checksum ^= table[i]; 914 } 915 916 // LOADING 917 for(i=0;i<strlen(loading);i++) { 918 byte = loading[i] + 0x80; 919 WRITEBYTE(byte); 920 checksum ^= byte; 921 } 922 923 // append to loader the name of the file 924 for(i=0;i<strlen(segments[0].filename);i++) { 925 byte = toupper(segments[0].filename[i]) + 0x80; 926 if(segments[0].filename[i] == '_') 927 byte = toupper(' ') + 0x80; 928 WRITEBYTE(byte); 929 checksum ^= byte; 930 } 931 932 // append to loader the ETA 933 for(i=0;i<strlen(eta);i++) { 934 byte = toupper(eta[i]) + 0x80; 935 WRITEBYTE(byte); 936 checksum ^= byte; 937 } 938 939 for(i=0;i<60-strlen(segments[0].filename)-strlen(eta)-strlen(loading);i++) { 940 WRITEBYTE(0x00); 941 checksum ^= 0x00; 942 } 943 944 WRITEBYTE(checksum); 945 946 // end of parameters 947 appendtone(&output,&outputlength,freq_end,rate,0,2,&offset); 948 949 // time to processes 950 appendtone(&output,&outputlength,freq_pre,rate,0.25,0,&offset); 951 } 952 953 checksum = 0xff; 954 for(j=0;j<segments[0].length;j++) { 955 WRITEBYTE(segments[0].data[j]); 956 checksum ^= segments[0].data[j]; 957 } 958 959 if(compress) { 960 for(j=0;j<sizeof(inflatecode)/sizeof(char);j++) { 961 WRITEBYTE(inflatecode[j]); 962 checksum ^= inflatecode[j]; 963 } 964 } 965 966 if(fast + cd + k8 == 0) { // hack so that standard method matches others 967 WRITEBYTE(0x00); 968 WRITEBYTE(0x00); 969 } 970 971 WRITEBYTE(checksum); 972 973 if(fast || cd || k8) 974 //appendtone(&output,&outputlength,freq_end,rate,0,1,&offset); 975 appendtone(&output,&outputlength,freq_end,rate,0,10,&offset); 976 else 977 //appendtone(&output,&outputlength,1000,rate,0,1,&offset); 978 appendtone(&output,&outputlength,1000,rate,0,10,&offset); 979 980 if(!qr) { 981 if(basicload) { 982 fprintf(stderr,"To load up and run on your Apple %s, type:\n\n\tLOAD\n",modeltypes[model]); 983 if(warm) 984 fprintf(stderr,"\t%XG\n",segments[0].start); 985 } 986 else { 987 fprintf(stderr,"To load up and run on your Apple %s, type:\n\n\t800.%XR 800G\n",modeltypes[model],0x800 + length + 1); 988 } 989 } 990 else { 991 fprintf(stderr,"To load up and run on your Apple %s, use the client disk.\n",modeltypes[model]); 992 } 993 fprintf(stderr,"\n"); 994 } 995 996 if(dsk) { 997 char eta[40]; 998 unsigned char byte, checksum=0xff, *cmp_data, start_table[21], *diskloadcode; 999 unsigned long ones=0, zeros=0, diskloadcode_len; 1000 size_t cmp_len; 1001 unsigned int length, start_table_len = 0; 1002 int i, j; 1003 double inflate_times[5]; 1004 1005 if(k8) { 1006 diskloadcode = diskload8000; 1007 diskloadcode_len = sizeof(diskload8000)/sizeof(char); 1008 } 1009 else { 1010 diskloadcode = diskload9600; 1011 diskloadcode_len = sizeof(diskload9600)/sizeof(char); 1012 } 1013 1014 rate = 48000; 1015 appendtone(&output,&outputlength,770,rate,4.0+tape,0,&offset); 1016 appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); 1017 appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); 1018 1019 for(j=0;j<sizeof(diskloadcode2)/sizeof(char);j++) { 1020 byte=diskloadcode2[j]; 1021 for(i=0;i<8;i++) { 1022 if(byte & 0x80) 1023 ones++; 1024 else 1025 zeros++; 1026 byte <<= 1; 1027 } 1028 } 1029 1030 // compute pad length, assuming 4 pages max for code 1031 zeros += 8*(4 * 256 - sizeof(diskloadcode2)/sizeof(char)); 1032 1033 for(j=0;j<sizeof(diskloadcode3)/sizeof(char);j++) { 1034 byte=diskloadcode3[j]; 1035 for(i=0;i<8;i++) { 1036 if(byte & 0x80) 1037 ones++; 1038 else 1039 zeros++; 1040 byte <<= 1; 1041 } 1042 } 1043 1044 for(j=0;j<sizeof(dosboot1)/sizeof(char);j++) { 1045 byte=dosboot1[j]; 1046 for(i=0;i<8;i++) { 1047 if(byte & 0x80) 1048 ones++; 1049 else 1050 zeros++; 1051 byte <<= 1; 1052 } 1053 } 1054 1055 for(j=0;j<sizeof(dosboot2)/sizeof(char);j++) { 1056 byte=dosboot2[j]; 1057 for(i=0;i<8;i++) { 1058 if(byte & 0x80) 1059 ones++; 1060 else 1061 zeros++; 1062 byte <<= 1; 1063 } 1064 } 1065 1066 freq0 = 12000; 1067 freq1 = 8000; 1068 if(k8) 1069 freq1 = 6000; 1070 sprintf(eta,"%d SEC. ",(int) (ones/(float)freq1 + zeros/(float)freq0 + 0.5 + 0.25)); 1071 1072 //length = sizeof(basic)/sizeof(char) + sizeof(diskloadcode)/sizeof(char); 1073 length = sizeof(basic)/sizeof(char) + diskloadcode_len; 1074 header[0] = length & 0xFF; 1075 header[1] = length >> 8; 1076 1077 freq0 = 2000; 1078 freq1 = 1000; 1079 for(i=0;i<3;i++) { 1080 WRITEBYTE(header[i]); 1081 checksum ^= header[i]; 1082 } 1083 WRITEBYTE(checksum); 1084 1085 appendtone(&output,&outputlength,1000,rate,0,1,&offset); 1086 appendtone(&output,&outputlength,770,rate,4.0,0,&offset); 1087 appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); 1088 appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); 1089 1090 // write out basic program 1091 checksum = 0xff; 1092 for(i=0;i<sizeof(basic)/sizeof(char);i++) { 1093 WRITEBYTE(basic[i]); 1094 checksum ^= basic[i]; 1095 } 1096 1097 // patch in ETA 1098 for(i=0;i<strlen(eta);i++) 1099 diskloadcode[0x84F - 0x80C + i] = eta[i] + 0x80; 1100 1101 // write out move and load code 1102 //for(i=0;i<sizeof(diskloadcode)/sizeof(char);i++) { 1103 for(i=0;i<diskloadcode_len;i++) { 1104 WRITEBYTE(diskloadcode[i]); 1105 checksum ^= diskloadcode[i]; 1106 } 1107 1108 // end of basic and diskloadcode 1109 WRITEBYTE(0xff); 1110 checksum ^= 0xff; 1111 1112 WRITEBYTE(checksum); 1113 1114 appendtone(&output,&outputlength,1000,rate,0,1,&offset); 1115 square=0; 1116 freq0 = 12000; 1117 if(k8) { 1118 freq1 = 6000; 1119 appendtone(&output,&outputlength,2000,rate,0.25,0,&offset); 1120 } 1121 else { 1122 freq1 = 8000; 1123 appendtone(&output,&outputlength,6000,rate,0.25,0,&offset); 1124 } 1125 1126 checksum = 0xff; 1127 for(i=0;i<sizeof(dosboot1)/sizeof(char);i++) { 1128 WRITEBYTE(dosboot1[i]); 1129 checksum ^= dosboot1[i]; 1130 } 1131 1132 // time to compress and compute start location and length 1133 // patch loadcode2 with start locations and ETA 1134 for(i=0;i<numseg;i++) { 1135 int k, err; 1136 double orig_len; 1137 unsigned char checksum=0xff; 1138 1139 inflate_times[i] = 0; 1140 1141 cmp_data = tdefl_compress_mem_to_heap(segments[i].data, segments[i].length, &cmp_len, TDEFL_MAX_PROBES_MASK); 1142 1143 //compute inflate time 1144 //load up inflate code 1145 for(j=0;j<sizeof(diskloadcode3)/sizeof(char);j++) 1146 ram[0x9B00 + j] = diskloadcode3[j]; 1147 //load up inflate data 1148 for(j=0;j<cmp_len;j++) { 1149 ram[0x8FFF - cmp_len + j] = cmp_data[j]; 1150 checksum ^= cmp_data[j]; 1151 } 1152 ram[0x8FFF] = checksum; 1153 1154 //compute chksum time 1155 if(k8) { 1156 for(j=(0x859 - 0x80C);j<diskloadcode_len;j++) 1157 ram[0x9000 - (0x859 - 0x80C) + j] = diskloadcode[j]; 1158 ram[0x00] = (0x8FFF - cmp_len) & 0xFF; 1159 ram[0x01] = (0x8FFF - cmp_len) >> 8; 1160 ram[0x02] = 0x00; 1161 ram[0x03] = 0x90; 1162 ram[0x04] = 0xFF; 1163 ram[0x9089] = 0x85; //STA 1164 ram[0x908A] = 0x04; //zero page $04 1165 ram[0x908B] = 0x00; //BRK 1166 1167 reset6502(); 1168 exec6502(0x9065); 1169 1170 if(ram[0x04] != 0) 1171 fprintf(stderr,"WARNING: simulated checksum failed: %02X\n",ram[0x04]); 1172 1173 inflate_times[i] += clockticks6502/1023000.0; 1174 } 1175 1176 //zero page src 1177 ram[0x10] = (0x8FFF - cmp_len) & 0xFF; 1178 ram[0x11] = (0x8FFF - cmp_len) >> 8; 1179 //zero page dst 1180 ram[0x12] = 0x00; 1181 ram[0x13] = 0x10; 1182 //setup JSR 1183 ram[0x9000] = 0x20; // JSR $9B00 1184 ram[0x9001] = 0x00; 1185 ram[0x9002] = 0x9B; 1186 ram[0x9003] = 0x00; //BRK to stop simulation 1187 //run it 1188 reset6502(); 1189 exec6502(0x9000); 1190 //compare (just to be safe) 1191 err=0; 1192 for(j=0;j<7 * 4096;j++) 1193 if(ram[0x1000 + j] != segments[i].data[j]) { 1194 err = 1; 1195 break; 1196 } 1197 if(err) 1198 fprintf(stderr,"WARNING: simulated inflate failed at %04X\n",j+0x1000); 1199 inflate_times[i] += clockticks6502/1023000.0; 1200 1201 free(segments[i].data); 1202 segments[i].data = cmp_data; 1203 orig_len = segments[i].length; 1204 segments[i].length = cmp_len; 1205 segments[i].start = 0x8FFF - segments[i].length; 1206 1207 // compress ? 1208 // need to see what is faster, defaulting to compress for now 1209 // if not compressed do not set start location, change asm code to check for 0,0 1210 // and not use inflate code 1211 1212 // where to load data 1213 start_table[start_table_len++] = segments[i].start & 0xFF; 1214 start_table[start_table_len++] = segments[i].start >> 8; 1215 1216 ones = zeros = 0; 1217 for(j=0;j<segments[i].length;j++) { 1218 byte=segments[i].data[j]; 1219 for(k=0;k<8;k++) { 1220 if(byte & 0x80) 1221 ones++; 1222 else 1223 zeros++; 1224 byte <<= 1; 1225 } 1226 } 1227 sprintf(eta,"%d",(int) (ones/(float)freq1 + zeros/(float)freq0 + 0.5 + 0.25)); 1228 1229 // ETA 1230 start_table[start_table_len++] = eta[0] + 0x80; 1231 if(eta[1] != 0) 1232 start_table[start_table_len++] = eta[1] + 0x80; 1233 else 1234 start_table[start_table_len++] = 0; 1235 1236 fprintf(stderr,"Segment: %d, start: 0x%04X, length: %5d, deflated: %.02f%%, data time:%s, inflate time:%.02f\n",i,segments[i].start,segments[i].length,100.0*(1-segments[i].length/orig_len),eta,inflate_times[i]); 1237 } 1238 fprintf(stderr,"\n"); 1239 1240 for(i=0;i<sizeof(diskloadcode2)/sizeof(char);i++) { 1241 WRITEBYTE(diskloadcode2[i]); 1242 checksum ^= diskloadcode2[i]; 1243 } 1244 1245 start_table[start_table_len++] = noformat; 1246 1247 for(i=0;i<start_table_len;i++) { 1248 WRITEBYTE(start_table[i]); 1249 checksum ^= start_table[i]; 1250 } 1251 1252 for(i=0;i<4*256 - sizeof(diskloadcode2)/sizeof(char) - start_table_len;i++) { 1253 WRITEBYTE(0x00); 1254 checksum ^= 0x00; 1255 } 1256 1257 for(i=0;i<sizeof(diskloadcode3)/sizeof(char);i++) { 1258 WRITEBYTE(diskloadcode3[i]); 1259 checksum ^= diskloadcode3[i]; 1260 } 1261 1262 for(i=0;i<sizeof(dosboot2)/sizeof(char);i++) { 1263 WRITEBYTE(dosboot2[i]); 1264 checksum ^= dosboot2[i]; 1265 } 1266 1267 WRITEBYTE(checksum); 1268 if(k8) { 1269 appendtone(&output,&outputlength,770,rate,0,2,&offset); 1270 appendtone(&output,&outputlength,2000,rate,0.3,0,&offset); 1271 } 1272 else { 1273 appendtone(&output,&outputlength,2000,rate,0,1,&offset); 1274 appendtone(&output,&outputlength,6000,rate,0.1,0,&offset); 1275 } 1276 1277 for(i=0;i<numseg;i++) { 1278 //appendtone(&output,&outputlength,6000,rate,1,0,&offset); 1279 1280 //timing 1281 if(i==0) { 1282 if(!noformat) 1283 j=28; 1284 else 1285 j=0; 1286 } 1287 else { 1288 //j = 6 + ceil(inflate_times[i-1]); // 6 = write track time, may need to make it 7 1289 // disk ][ verified (format and no-format) 1290 // Virtual ][ emulator verified (format and no-format, 8K only) 1291 // CFFA3000 3.1 failed, needs more time 1292 1293 j = ceil(6.5 + inflate_times[i-1]); // 6 = write track time, may need to make it 7 1294 // disk ][ verified (format and no-format) 1295 // Apple duodisk verified (format and no-format) 1296 // CFFA3000 3.1 verified with USB stick (no-format only) 1297 // CFFA3000 3.1 failed with IBM 4GB Microdrive (too slow) 1298 // Nishida Radio SDISK // (no-format only) 1299 } 1300 if(i==1) // seek time for track 0, just in case 1301 j+=2; 1302 1303 /* count down code 1304 for(;j>=0;j--) { 1305 checksum = 0xff; 1306 WRITEBYTE(j/10 + 48 + 0x80); 1307 checksum ^= (j/10 + 48 + 0x80); 1308 WRITEBYTE(j%10 + 48 + 0x80); 1309 checksum ^= (j%10 + 48 + 0x80); 1310 WRITEBYTE(0x00); 1311 checksum ^= 0x00; 1312 WRITEBYTE(checksum); 1313 appendtone(&output,&outputlength,2000,rate,0,1,&offset); 1314 appendtone(&output,&outputlength,6000,rate,1,0,&offset); 1315 } 1316 */ 1317 1318 if(k8) 1319 appendtone(&output,&outputlength,2000,rate,j,0,&offset); 1320 else 1321 appendtone(&output,&outputlength,6000,rate,j,0,&offset); 1322 1323 checksum = 0xff; 1324 for(j=0;j<segments[i].length;j++) { 1325 WRITEBYTE(segments[i].data[j]); 1326 checksum ^= segments[i].data[j]; 1327 } 1328 WRITEBYTE(checksum); 1329 if(k8) 1330 //appendtone(&output,&outputlength,770,rate,0,2,&offset); 1331 appendtone(&output,&outputlength,770,rate,0,10,&offset); 1332 else 1333 //appendtone(&output,&outputlength,2000,rate,0,1,&offset); 1334 appendtone(&output,&outputlength,2000,rate,0,10,&offset); 1335 } 1336 1337 fprintf(stderr,"To load up and run on your Apple %s, type:\n\n\tLOAD\n\n",modeltypes[model]); 1338 } 1339 1340 // append zero to zero out last wave 1341 appendtone(&output,&outputlength,0,rate,0,1,&offset); 1342 1343 // 0.1 sec quiet to help some emulators 1344 appendtone(&output,&outputlength,0,rate,0.1,0,&offset); 1345 1346 // 0.4 sec quiet to help some IIs 1347 // appendtone(&output,&outputlength,0,rate,0.4,0,&offset); 1348 1349 // write it 1350 if(outputtype == AIFF) 1351 Write_AIFF(ofp,output,outputlength,rate,bits,amp); 1352 else if(outputtype == WAVE) 1353 Write_WAVE(ofp,output,outputlength,rate,bits,amp); 1354 1355 fclose(ofp); 1356 return 0; 1357 } 1358 1359 void appendtone(double **sound, long *length, int freq, int rate, double time, double cycles, int *offset) 1360 { 1361 long i, n=time*rate; 1362 static long grow = 0; 1363 double *tmp = NULL; 1364 1365 if(freq && cycles) 1366 n=cycles*rate/freq; 1367 1368 if(n == 0) 1369 n=cycles; 1370 1371 /* 1372 if((tmp = (double *)realloc(*sound, (*length + n) * sizeof(double))) == NULL) 1373 abort(); 1374 *sound = tmp; 1375 */ 1376 1377 // new code for speed up Windows realloc 1378 if(*length + n > grow) { 1379 grow = *length + n + 10000000; 1380 if((tmp = (double *)realloc(*sound, (grow) * sizeof(double))) == NULL) 1381 abort(); 1382 *sound = tmp; 1383 } 1384 1385 //tmp -> (*sound) 1386 if(square) { 1387 int j; 1388 1389 if(freq) 1390 for (i = 0; i < n; i++) { 1391 for(j = 0;j < rate / freq / 2;j++) 1392 (*sound)[*length + i++] = 1; 1393 for(j = 0;j < rate / freq / 2;j++) 1394 (*sound)[*length + i++] = -1; 1395 i--; 1396 } 1397 else 1398 for (i = 0; i < n; i++) 1399 (*sound)[*length + i] = 0; 1400 } 1401 else 1402 for(i=0;i<n;i++) 1403 (*sound)[*length+i] = sin(2*M_PI*i*freq/rate + *offset*M_PI); 1404 1405 if(cycles - (int)cycles == 0.5) 1406 *offset = (*offset == 0); 1407 1408 *length += n; 1409 } 1410 1411 char *getext(char *filename) 1412 { 1413 char stack[256], *rval; 1414 int i, sp = 0; 1415 1416 for(i=strlen(filename)-1;i>=0;i--) { 1417 if(filename[i] == '.') 1418 break; 1419 stack[sp++] = filename[i]; 1420 } 1421 stack[sp] = '\0'; 1422 1423 if(sp == strlen(filename) || sp == 0) 1424 return(NULL); 1425 1426 if((rval = (char *)malloc(sp * sizeof(char))) == NULL) 1427 ; //do error code 1428 1429 rval[sp] = '\0'; 1430 for(i=0;i<sp+i;i++) 1431 rval[i] = stack[--sp]; 1432 1433 return(rval); 1434 } 1435 1436 void usage() 1437 { 1438 fprintf(stderr,"%s",usagetext); 1439 } 1440 1441 // Code below from http://paulbourke.net/dataformats/audio/ 1442 /* 1443 Write an AIFF sound file 1444 Only do one channel, only support 16 bit. 1445 Supports sample frequencies of 11, 22, 44KHz (default). 1446 Little/big endian independent! 1447 */ 1448 1449 // egan: changed code to support any Hz and 8 bit. 1450 1451 void Write_AIFF(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp) 1452 { 1453 unsigned short v; 1454 int i; 1455 unsigned long totalsize; 1456 double themin, themax, scale, themid; 1457 unsigned char bit80[10]; 1458 1459 // Write the form chunk 1460 fprintf(fptr, "FORM"); 1461 totalsize = 4 + 8 + 18 + 8 + (bits / 8) * nsamples + 8; 1462 fputc((totalsize & 0xff000000) >> 24, fptr); 1463 fputc((totalsize & 0x00ff0000) >> 16, fptr); 1464 fputc((totalsize & 0x0000ff00) >> 8, fptr); 1465 fputc((totalsize & 0x000000ff), fptr); 1466 fprintf(fptr, "AIFF"); 1467 1468 // Write the common chunk 1469 fprintf(fptr, "COMM"); 1470 fputc(0, fptr); // Size 1471 fputc(0, fptr); 1472 fputc(0, fptr); 1473 fputc(18, fptr); 1474 fputc(0, fptr); // Channels = 1 1475 fputc(1, fptr); 1476 fputc((nsamples & 0xff000000) >> 24, fptr); // Samples 1477 fputc((nsamples & 0x00ff0000) >> 16, fptr); 1478 fputc((nsamples & 0x0000ff00) >> 8, fptr); 1479 fputc((nsamples & 0x000000ff), fptr); 1480 fputc(0, fptr); // Size = 16 1481 fputc(bits, fptr); 1482 1483 ConvertToIeeeExtended(nfreq, bit80); 1484 for (i = 0; i < 10; i++) 1485 fputc(bit80[i], fptr); 1486 1487 // Write the sound data chunk 1488 fprintf(fptr, "SSND"); 1489 fputc((((bits / 8) * nsamples + 8) & 0xff000000) >> 24, fptr); // Size 1490 fputc((((bits / 8) * nsamples + 8) & 0x00ff0000) >> 16, fptr); 1491 fputc((((bits / 8) * nsamples + 8) & 0x0000ff00) >> 8, fptr); 1492 fputc((((bits / 8) * nsamples + 8) & 0x000000ff), fptr); 1493 fputc(0, fptr); // Offset 1494 fputc(0, fptr); 1495 fputc(0, fptr); 1496 fputc(0, fptr); 1497 fputc(0, fptr); // Block 1498 fputc(0, fptr); 1499 fputc(0, fptr); 1500 fputc(0, fptr); 1501 1502 // Find the range 1503 themin = samples[0]; 1504 themax = themin; 1505 for (i = 1; i < nsamples; i++) { 1506 if (samples[i] > themax) 1507 themax = samples[i]; 1508 if (samples[i] < themin) 1509 themin = samples[i]; 1510 } 1511 if (themin >= themax) { 1512 themin -= 1; 1513 themax += 1; 1514 } 1515 themid = (themin + themax) / 2; 1516 themin -= themid; 1517 themax -= themid; 1518 if (ABS(themin) > ABS(themax)) 1519 themax = ABS(themin); 1520 // scale = amp * 32760 / (themax); 1521 scale = amp * ((bits == 16) ? 32760 : 124) / (themax); 1522 1523 // Write the data 1524 for (i = 0; i < nsamples; i++) { 1525 if (bits == 16) { 1526 v = (unsigned short) (scale * (samples[i] - themid)); 1527 fputc((v & 0xff00) >> 8, fptr); 1528 fputc((v & 0x00ff), fptr); 1529 } else { 1530 v = (unsigned char) (scale * (samples[i] - themid)); 1531 fputc(v, fptr); 1532 } 1533 } 1534 } 1535 1536 /* 1537 Write an WAVE sound file 1538 Only do one channel, only support 16 bit. 1539 Supports any (reasonable) sample frequency 1540 Little/big endian independent! 1541 */ 1542 1543 // egan: changed code to support 8 bit. 1544 1545 void Write_WAVE(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp) 1546 { 1547 unsigned short v; 1548 int i; 1549 unsigned long totalsize, bytespersec; 1550 double themin, themax, scale, themid; 1551 1552 // Write the form chunk 1553 fprintf(fptr, "RIFF"); 1554 totalsize = (bits / 8) * nsamples + 36; 1555 fputc((totalsize & 0x000000ff), fptr); // File size 1556 fputc((totalsize & 0x0000ff00) >> 8, fptr); 1557 fputc((totalsize & 0x00ff0000) >> 16, fptr); 1558 fputc((totalsize & 0xff000000) >> 24, fptr); 1559 fprintf(fptr, "WAVE"); 1560 fprintf(fptr, "fmt "); // fmt_ chunk 1561 fputc(16, fptr); // Chunk size 1562 fputc(0, fptr); 1563 fputc(0, fptr); 1564 fputc(0, fptr); 1565 fputc(1, fptr); // Format tag - uncompressed 1566 fputc(0, fptr); 1567 fputc(1, fptr); // Channels 1568 fputc(0, fptr); 1569 fputc((nfreq & 0x000000ff), fptr); // Sample frequency (Hz) 1570 fputc((nfreq & 0x0000ff00) >> 8, fptr); 1571 fputc((nfreq & 0x00ff0000) >> 16, fptr); 1572 fputc((nfreq & 0xff000000) >> 24, fptr); 1573 bytespersec = (bits / 8) * nfreq; 1574 fputc((bytespersec & 0x000000ff), fptr); // Average bytes per second 1575 fputc((bytespersec & 0x0000ff00) >> 8, fptr); 1576 fputc((bytespersec & 0x00ff0000) >> 16, fptr); 1577 fputc((bytespersec & 0xff000000) >> 24, fptr); 1578 fputc((bits / 8), fptr); // Block alignment 1579 fputc(0, fptr); 1580 fputc(bits, fptr); // Bits per sample 1581 fputc(0, fptr); 1582 fprintf(fptr, "data"); 1583 totalsize = (bits / 8) * nsamples; 1584 fputc((totalsize & 0x000000ff), fptr); // Data size 1585 fputc((totalsize & 0x0000ff00) >> 8, fptr); 1586 fputc((totalsize & 0x00ff0000) >> 16, fptr); 1587 fputc((totalsize & 0xff000000) >> 24, fptr); 1588 1589 // Find the range 1590 themin = samples[0]; 1591 themax = themin; 1592 for (i = 1; i < nsamples; i++) { 1593 if (samples[i] > themax) 1594 themax = samples[i]; 1595 if (samples[i] < themin) 1596 themin = samples[i]; 1597 } 1598 if (themin >= themax) { 1599 themin -= 1; 1600 themax += 1; 1601 } 1602 themid = (themin + themax) / 2; 1603 themin -= themid; 1604 themax -= themid; 1605 if (ABS(themin) > ABS(themax)) 1606 themax = ABS(themin); 1607 // scale = amp * 32760 / (themax); 1608 scale = amp * ((bits == 16) ? 32760 : 124) / (themax); 1609 1610 // Write the data 1611 for (i = 0; i < nsamples; i++) { 1612 if (bits == 16) { 1613 v = (unsigned short) (scale * (samples[i] - themid)); 1614 fputc((v & 0x00ff), fptr); 1615 fputc((v & 0xff00) >> 8, fptr); 1616 } else { 1617 v = (unsigned char) (scale * (samples[i] - themid)); 1618 fputc(v + 0x80, fptr); 1619 } 1620 } 1621 } 1622 1623 1624 /* 1625 * C O N V E R T T O I E E E E X T E N D E D 1626 */ 1627 1628 /* Copyright (C) 1988-1991 Apple Computer, Inc. 1629 * All rights reserved. 1630 * 1631 * Machine-independent I/O routines for IEEE floating-point numbers. 1632 * 1633 * NaN's and infinities are converted to HUGE_VAL or HUGE, which 1634 * happens to be infinity on IEEE machines. Unfortunately, it is 1635 * impossible to preserve NaN's in a machine-independent way. 1636 * Infinities are, however, preserved on IEEE machines. 1637 * 1638 * These routines have been tested on the following machines: 1639 * Apple Macintosh, MPW 3.1 C compiler 1640 * Apple Macintosh, THINK C compiler 1641 * Silicon Graphics IRIS, MIPS compiler 1642 * Cray X/MP and Y/MP 1643 * Digital Equipment VAX 1644 * 1645 * 1646 * Implemented by Malcolm Slaney and Ken Turkowski. 1647 * 1648 * Malcolm Slaney contributions during 1988-1990 include big- and little- 1649 * endian file I/O, conversion to and from Motorola's extended 80-bit 1650 * floating-point format, and conversions to and from IEEE single- 1651 * precision floating-point format. 1652 * 1653 * In 1991, Ken Turkowski implemented the conversions to and from 1654 * IEEE double-precision format, added more precision to the extended 1655 * conversions, and accommodated conversions involving +/- infinity, 1656 * NaN's, and denormalized numbers. 1657 */ 1658 1659 #ifndef HUGE_VAL 1660 #define HUGE_VAL HUGE 1661 #endif /*HUGE_VAL */ 1662 1663 #define FloatToUnsigned(f) ((unsigned long)(((long)(f - 2147483648.0)) + 2147483647L) + 1) 1664 1665 void ConvertToIeeeExtended(double num, unsigned char *bytes) 1666 { 1667 int sign; 1668 int expon; 1669 double fMant, fsMant; 1670 unsigned long hiMant, loMant; 1671 1672 if (num < 0) { 1673 sign = 0x8000; 1674 num *= -1; 1675 } else { 1676 sign = 0; 1677 } 1678 1679 if (num == 0) { 1680 expon = 0; 1681 hiMant = 0; 1682 loMant = 0; 1683 } else { 1684 fMant = frexp(num, &expon); 1685 if ((expon > 16384) || !(fMant < 1)) { /* Infinity or NaN */ 1686 expon = sign | 0x7FFF; 1687 hiMant = 0; 1688 loMant = 0; /* infinity */ 1689 } else { /* Finite */ 1690 expon += 16382; 1691 if (expon < 0) { /* denormalized */ 1692 fMant = ldexp(fMant, expon); 1693 expon = 0; 1694 } 1695 expon |= sign; 1696 fMant = ldexp(fMant, 32); 1697 fsMant = floor(fMant); 1698 hiMant = FloatToUnsigned(fsMant); 1699 fMant = ldexp(fMant - fsMant, 32); 1700 fsMant = floor(fMant); 1701 loMant = FloatToUnsigned(fsMant); 1702 } 1703 } 1704 1705 bytes[0] = expon >> 8; 1706 bytes[1] = expon; 1707 bytes[2] = hiMant >> 24; 1708 bytes[3] = hiMant >> 16; 1709 bytes[4] = hiMant >> 8; 1710 bytes[5] = hiMant; 1711 bytes[6] = loMant >> 24; 1712 bytes[7] = loMant >> 16; 1713 bytes[8] = loMant >> 8; 1714 bytes[9] = loMant; 1715 } 1716 1717 uint8_t read6502(uint16_t address) 1718 { 1719 return ram[address]; 1720 } 1721 1722 void write6502(uint16_t address, uint8_t value) 1723 { 1724 ram[address] = value; 1725 }