diff --git a/Makefile b/Makefile index b25460d..284c37a 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,16 @@ -all: c2t +all: c2t c2t-96h clean: - rm c2t.h c2t + rm c2t.h c2t c2t-96h cd asm; make clean c2t: c2t.c c2t.h - gcc -Wall -O3 -o c2t c2t.c + gcc -Wall -Wno-unused-value -Wno-unused-function -O3 -o c2t c2t.c + +c2t-96h: c2t-96h.c c2t.h + gcc -Wall -Wno-unused-value -Wno-unused-function -O3 -o c2t-96h c2t-96h.c c2t.h: mon/dos33.boot1.mon mon/dos33.boot2.mon asm/autoload.s asm/diskload2.s asm/diskload3.s asm/diskload8000.s asm/diskload9600.s asm/fastload8000.s asm/fastload9600.s asm/fastloadcd.s asm/inflate.s ./makeheader diff --git a/README.md b/README.md index 98e15e0..9ef2b87 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ You clearly do not understand the awesomeness of the Apple II, move along. ## Version -0.996 (Nov 29 2014) +c2t 0.996 (Nov 29 2014) +c2t-96h 0.997 (Dec 31 2015) ## Installation @@ -63,6 +64,22 @@ gcc -Wall -O3 -static -o c2t c2t.c ``` > Use the `miniz.h` in the `windows` directory. +To cross build for Windows from OS/X, first install , then type: +``` +cd windows +cp ../c2t.* +cp ../fake6502.h +/usr/local/gcc-4.8.0-qt-4.8.4-for-mingw32/win32-gcc/bin/i586-mingw32-gcc -Wall -Wno-unused-value -Wno-unused-function -O3 -static -o c2t.exe c2t.c +``` + + +## c2t-96h Version + +`c2t-96h` is a hacked up version of `c2t` that fixes a few bugs (e.g. `.po` files) and adds working 9600 BPS code. +Both `-8` and `-f` activate this new 9600 BPS code. + +`c2t-96h` will eventually replace `c2t`. IOW, use `c2t-96h` for now. + ## Tested Configurations: diff --git a/article/article.docx b/article/article.docx new file mode 100644 index 0000000..7c434a7 Binary files /dev/null and b/article/article.docx differ diff --git a/article/article.pdf b/article/article.pdf new file mode 100644 index 0000000..61a9405 Binary files /dev/null and b/article/article.pdf differ diff --git a/article/article.pptx b/article/article.pptx new file mode 100644 index 0000000..cccd34e Binary files /dev/null and b/article/article.pptx differ diff --git a/article/c12k.txt b/article/c12k.txt new file mode 100644 index 0000000..5e08191 --- /dev/null +++ b/article/c12k.txt @@ -0,0 +1,34 @@ +12000 +12000 +-2 +2 main: ldy #0 +4 psync: bit tapein +2 bmi psync +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +2 bpl ploop +2 cpy #$40 +2 bpl endcode +2 cpy #$15 +2 bpl main +2 cpy #$07 +7 store: rol nnnn,x +2 asl +2 bne main +2 lda #1 +2 inx +2 bne main +6 inc store+2 +3 jmp main diff --git a/article/c6k.txt b/article/c6k.txt new file mode 100644 index 0000000..968c8e1 --- /dev/null +++ b/article/c6k.txt @@ -0,0 +1,49 @@ +6000 +6000 +-2 +2 main: ldy #0 +4 psync: bit tapein +2 bmi psync +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +3 bpl ploop +2 ploop: iny +4 bit tapein +2 bpl ploop +2 cpy #$40 +2 bpl endcode +2 cpy #$15 +2 bpl main +2 cpy #$07 +7 store: rol nnnn,x +2 asl +3 bne main +2 lda #1 +2 inx +3 bne main +6 inc store+2 +3 jmp main diff --git a/article/helloworldbasicheader.png b/article/helloworldbasicheader.png new file mode 100644 index 0000000..b2da11a Binary files /dev/null and b/article/helloworldbasicheader.png differ diff --git a/article/plot.py b/article/plot.py new file mode 100755 index 0000000..8be0fe2 --- /dev/null +++ b/article/plot.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +import sys, math, os + +try: + cycles_file = sys.argv[1] +except: + print "usage: %s cycles.txt" % sys.argv[0] + sys.exit(1) + +f = open(cycles_file,'r') +freq0 = f.readline().strip() +freq1 = f.readline().strip() +start = int(f.readline().strip()) + +a2freq = 1020484.4497 # src: openemulator, src: https://discussions.apple.com/thread/559102 +#rot = True +rot = False + +xr = a2freq / float(freq0) +xcross = xr / float(2.0) +xc2 = int(xcross * 100) / float(100) +ycross = 0 +xdiv = xcross / float(math.pi) +yb = -0.25 + +g = open('plot.gnuplot','w') +if rot: + g.write('set title " %d/%d KHz Cycle" offset 0,-2\n' % (int(freq0)/1000,int(freq1)/1000)) +else: + g.write('set ylabel "%d/%d KHz Cycle"\n' % (int(freq0)/1000,int(freq1)/1000)) +g.write('set term postscript \n') +g.write('set size ratio 0.5\n') +g.write('set output "plot.ps"\n') +g.write('set key off\n') +g.write('set grid xtics lt 0 lw 1 lc rgb "#000000"\n') +g.write('set grid ytics lt 0 lw 1 lc rgb "#000000"\n') +g.write('set xrange [%d:%f]\n' % (start,xr)) +g.write('set yrange [%f:1]\n' % yb) +g.write('set xtics 1 font "courier,10"\n') +g.write('set x2tics 1 font "courier,10"\n') +g.write('set ytics 1 font "courier,10"\n') +g.write('set arrow from %d,0 to %f,0 nohead lw 1 lc rgb "black"\n' % (start,xr)) +g.write('set arrow from %f,%f to %f,1 nohead lw 1 lc rgb "black"\n' % (xcross,yb,xcross)) +if start < 0: + g.write('set arrow from %f,%f to %f,1 nohead lw 1 lc rgb "black"\n' % (0,yb,0)) +g.write('set xtic rotate by 90 right \n') +g.write('set x2tic rotate by 90 left \n') + +x2tics = {} +xtics = {} + +base = start +ll = 0 +for i in f.readlines(): + cycles = int(i.split(' ')[0]) + text = i.split(' ',1)[1].rstrip().upper() + if len(text) > ll: + ll = len(text) + xtics[str(base)] = text + x2tics[str(base)] = str(base) + base += cycles + +x2tics[str(xc2)] = ' ZC' +if start < 0: + x2tics['0'] = '0 ZC' + +g.write('set x2tics (') +for k, v in x2tics.iteritems(): + g.write('"%s" %f, ' % (v,float(k))) +g.write(')\n') + +g.write('set xtics (') +for k, v in xtics.iteritems(): + g.write('"%s" %d, ' % (v + ' '*(ll-len(v)),int(k))) +g.write(')\n') + +f.close() + +g.write('set ytics ("0" 0, "+" 1, "-" %f)\n' % yb) +g.write('plot sin(x/%f)\n' % xdiv) +g.write('quit\n') + +g.close() +os.system('gnuplot plot.gnuplot') +os.system('pstopdf plot.ps -o plot.pdf') +if rot: + os.system('pdf90 plot.pdf') + diff --git a/article/timeline.py b/article/timeline.py new file mode 100755 index 0000000..1249793 --- /dev/null +++ b/article/timeline.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +import sys, math, os + +try: + timeline_file = sys.argv[1] +except: + print "usage: %s timeline.txt" % sys.argv[0] + sys.exit(1) + +f = open(timeline_file,'r') +title = f.readline().strip() + " Audio Timeline" +start = 0 +rot = True + +g = open('timeline.gnuplot','w') +if rot: + g.write('set x2label "%s"\n' % title); +#else: +# g.write('set ylabel "%d/%d KHz Cycle"\n' % (int(freq0)/1000,int(freq1)/1000)) +g.write('set term postscript \n') +#g.write('set size ratio 0.5\n') +g.write('set output "timeline.ps"\n') +g.write('set key off\n') +g.write('set grid xtics lt 0 lw 1 lc rgb "#000000"\n') +g.write('set grid ytics lt 0 lw 1 lc rgb "#000000"\n') +g.write('set yrange [0:1]\n') +g.write('set xtics 1 font "courier,9"\n') +g.write('set x2tics 1 font "courier,9"\n') +g.write('set xtic rotate by 90 right \n') +g.write('set x2tic rotate by 90 left \n') +g.write('unset ytics\n') +g.write('unset y2tics\n') + +x2tics = {} +xtics = {} + +ll = 0 +for i in f.readlines(): + if i[0] == '#': + continue + timestamp = float(i.split(',')[0]) + label = i.split(',')[1].rstrip() + if len(label) > ll: + ll = len(label) + xtics[str(timestamp)] = label + x2tics[str(timestamp)] = str(timestamp) + g.write('set arrow from %f,0 to %f,1 nohead lw 1 lc rgb "black"\n' % (timestamp,timestamp)) + xr = timestamp + +g.write('set x2tics (') +for k, v in x2tics.iteritems(): + g.write('"%s" %f, ' % (v,float(k))) +g.write(')\n') + +g.write('set xtics (') +for k, v in xtics.iteritems(): + #g.write('"%s" %f, ' % (v + ' '*(ll-len(v)),float(k))) + g.write('"%s" %f, ' % (v,float(k))) +g.write(')\n') + +f.close() + +#g.write('set ytics ("0" 0, "+" 1, "-" %f)\n' % yb) +g.write('set xrange [%d:%f]\n' % (start,xr)) +g.write('plot 0\n') +g.write('quit\n') + +g.close() +os.system('gnuplot timeline.gnuplot') +os.system('pstopdf timeline.ps -o timeline.pdf') +if rot: + os.system('pdf90 timeline.pdf') + diff --git a/c2t b/c2t index 7823931..b94ef8c 100755 Binary files a/c2t and b/c2t differ diff --git a/c2t-96h b/c2t-96h new file mode 100755 index 0000000..7d58651 Binary files /dev/null and b/c2t-96h differ diff --git a/c2t-96h.c b/c2t-96h.c new file mode 100644 index 0000000..f64a13a --- /dev/null +++ b/c2t-96h.c @@ -0,0 +1,1855 @@ +/* + +c2t, Code to Tape|Text, Version 0.995, Tue May 22 22:11:12 GMT 2012 + +Parts copyright (c) 2011, 2012 All Rights Reserved, Egan Ford (egan@sense.net) + +THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +PARTICULAR PURPOSE. + +Built on work by: + * Mike Willegal (http://www.willegal.net/appleii/toaiff.c) + * Paul Bourke (http://paulbourke.net/dataformats/audio/, AIFF and WAVE output code) + * Malcolm Slaney and Ken Turkowski (Integer to IEEE 80-bit float code) + * Lance Leventhal and Winthrop Saville (6502 Assembly Language Subroutines, CRC 6502 code) + * Piotr Fusik (http://atariarea.krap.pl/x-asm/inflate.html, inflate 6502 code) + * Rich Geldreich (http://code.google.com/p/miniz/, deflate C code) + * Mike Chambers (http://rubbermallet.org/fake6502.c, 6502 simulator) + +License: + * Do what you like, remember to credit all sources when using. + +Description: + This small utility will read Apple I/II binary and + monitor text files and output Apple I or II AIFF and WAV + audio files for use with the Apple I and II cassette + interface. + +Features: + * Apple I, II, II+, IIe support. + * Big and little-endian machine support. + o Little-endian tested. + * AIFF and WAVE output (both tested). + * Platforms tested: + o 32-bit/64-bit x86 OS/X. + o 32-bit/64-bit x86 Linux. + o 32-bit x86 Windows/Cygwin. + o 32-bit x86 Windows/MinGW. + * Multi-segment tapes. + +Compile: + OS/X: + gcc -Wall -O -o c2t c2t.c + Linux: + gcc -Wall -O -o c2t c2t.c -lm + Windows/Cygwin: + gcc -Wall -O -o c2t c2t.c + Windows/MinGW: + PATH=C:\MinGW\bin;%PATH% + gcc -Wall -O -static -o c2t c2t.c + +Notes: + * Virtual ][ only supports .aif (or .cass) + * Dropbox only supports .wav and .aiff (do not use .wave or .aif) + +Not yet done: + * Test big-endian. + * gnuindent + * Redo malloc code in appendtone + +Thinking about: + * Check for existing file and abort, or warn, or prompt. + * -q quiet option for Makefiles + * autoload support for basic programs + +Bugs: + * Probably + +*/ + +#include "miniz.h" +#include "fake6502.h" +#include +#include +#include +#include +#include +#include +#include "c2t.h" + +#define ABS(x) (((x) < 0) ? -(x) : (x)) + +#define VERSION "Version 0.997" +#define OUTFILE argv[argc-1] +#define BINARY 0 +#define MONITOR 1 +#define AIFF 2 +#define WAVE 3 +#define DSK 4 + +#define WRITEBYTE(x) { \ + unsigned char wb_j, wb_temp=(x); \ + for(wb_j=0;wb_j<8;wb_j++) { \ + if(wb_temp & 0x80) { \ + if(freq1 == 6000) { \ + appendtone(&output,&outputlength,freq1,rate,0,0.5,&offset); \ + appendtone(&output,&outputlength,freq0,rate,0,0.5,&offset); \ + } \ + else { \ + appendtone(&output,&outputlength,freq1,rate,0,1,&offset); \ + } \ + } \ + else { \ + appendtone(&output,&outputlength,freq0,rate,0,1,&offset); \ + } \ + wb_temp<<=1; \ + } \ +} + +void usage(); +char *getext(char *filename); +void appendtone(double **sound, long *length, int freq, int rate, double time, double cycles, int *offset); +void Write_AIFF(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp); +void Write_WAVE(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp); +void ConvertToIeeeExtended(double num, unsigned char *bytes); +uint8_t read6502(uint16_t address); +void write6502(uint16_t address, uint8_t value); + +unsigned char ram[65536]; + +typedef struct seg { + int start; + int length; + int codelength; + unsigned char *data; + char filename[256]; +} segment; + +#define MAXEVENTS 100 + +typedef struct event { + unsigned long int timestamp; + char label[64]; +} event; + +unsigned int eventnumber = 0; + +void registerevent(event *events, unsigned long int timestamp, char *label); +void printevents(event *events, int rate); + +typedef struct s { + unsigned char bytes[256]; +} sector; + +typedef struct t { + sector sectors[16]; +} track; + +typedef struct d { + track tracks[35]; +} disk; + +int square = 1; + +int main(int argc, char **argv) +{ + FILE *ofp; + double *output = NULL, amp=0.75; + long outputlength=0; + int i, c, model=0, outputtype, offset=0, fileoutput=1, warm=0, dsk=0, noformat=0, k8=0, qr=0; + 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; + char *filetypes[] = {"binary","monitor","aiff","wave","disk"}; + char *modeltypes[] = {"\b","I","II"}; + char *ext; + unsigned int numseg = 0; + segment *segments = NULL; + event events[MAXEVENTS]; + + opterr = 1; + while((c = getopt(argc, argv, "12vabcftdpn8meh?lqr:")) != -1) + switch(c) { + case '1': // apple 1 + rate = 8000; + model = 1; + break; + case '2': // apple 2 + model = 2; + break; + case 'v': // version + fprintf(stderr,"\n%s\n\n",VERSION); + return 1; + break; + case 'a': // assembly autoloader + model = 2; + autoload = 1; + break; + case 'b': // basic autoloader + model = 2; + basicload = autoload = 1; + break; + case 'c': // compression + model = 2; + autoload = compress = 1; + break; + case 'f': // hifreq + rate = 48000; + model = 2; + // autoload = fast = 1; + // cd = k8 = 0; + autoload = k8 = 1; + fast = cd = 0; + break; + case 'd': // hifreq CD + rate = 44100; + bits = 16; + amp = 1.0; + model = 2; + cd = autoload = 1; + fast = k8 = 0; + break; + case 't': // 10 sec leader + tape = 6; + amp = 1.0; + break; + case 'm': // drop to monitor after load + warm = 1; + break; + case 'e': // end on page boundary + endpage = 1; + break; + case 'p': // stdout + fileoutput = 0; + break; + case 'n': + noformat = 1; + break; + case '8': // 8k + rate = 48000; + model = 2; + autoload = k8 = 1; + fast = cd = 0; + break; + case 'h': // help + case '?': + usage(); + return 1; + case 'q': // qr code support + rate = 48000; + model = 2; + autoload = k8 = qr = 1; + fast = cd = 0; + break; + case 'l': // long mon lines + longmon = 1; + break; + case 'r': // override rate for -1/-2 only + rate = atoi(optarg); + autoload = basicload = k8 = qr = fast = cd = 0; + break; + } + + if(argc - optind < 1 + fileoutput) { + usage(); + return 1; + } + + // read intput files + + fprintf(stderr,"\n"); + for(i=optind;i 1 || model == 1) { + if(autoload) + fprintf(stderr,"WARNING: number of segments > 1 or model = 1: autoload and fast disabled.\n\n"); + autoload = fast = 0; + } + + if(fileoutput) { + if((ext = getext(OUTFILE)) == NULL) { + usage(); + return 1; + } + else { + if(strcmp(ext,"aiff") == 0 || strcmp(ext,"aif") == 0) + outputtype = AIFF; + else if(strcmp(ext,"wave") == 0 || strcmp(ext,"wav") == 0) + outputtype = WAVE; + else if(strcmp(ext,"mon") == 0) + outputtype = MONITOR; + else { + usage(); + return 1; + } + } + } + else { +/* + if(!model) + outputtype = MONITOR; + else + outputtype = AIFF; +*/ + outputtype = MONITOR; + } + + if(outputtype != MONITOR && !model) { + fprintf(stderr,"\nYou must specify -1 or -2 for Apple I or II tape format, exiting.\n\n"); + return 1; + } + + // TODO: check for existing file and abort, or warn, or prompt + + ofp=stdout; + if(fileoutput) { + if ((ofp = fopen(OUTFILE, "w")) == NULL) { + fprintf(stderr,"\nCannot write: %s\n\n",OUTFILE); + return 1; + } + fprintf(stderr,"Writing %s as Apple %s formatted %s.\n\n",OUTFILE,modeltypes[model],filetypes[outputtype]); + } + else + fprintf(stderr,"Writing %s as Apple %s formatted %s.\n\n","STDOUT",modeltypes[model],filetypes[outputtype]); + + if(outputtype == MONITOR) { + int i, j, saddr; + //unsigned long cmp_len; + size_t cmp_len; + unsigned char *cmp_data; + + for(i=0;i> 8; + ram[0xBE80 - (0x823 - 0x80C) + j++] = endj & 0xFF; + ram[0xBE80 - (0x823 - 0x80C) + j++] = endj >> 8; + ram[0x00] = 0xFF; + ram[0xBF09] = 0x00; //BRK + + reset6502(); + exec6502(0xBEE3); + + if(ram[0x00] != 0) + fprintf(stderr,"WARNING: simulated checksum failed: %02X\n",ram[0x00]); + + inflate_time += clockticks6502/1023000.0; + } + + //zero page src + ram[0x0] = (0xBA00 - cmp_len) & 0xFF; + ram[0x1] = (0xBA00 - cmp_len) >> 8; + //zero page dst + ram[0x2] = (segments[0].start) & 0xFF; + ram[0x3] = (segments[0].start) >> 8; + //setup JSR + ram[0xBF00] = 0x20; // JSR $9B00 + ram[0xBF01] = 0x00; + ram[0xBF02] = 0xBA; + ram[0xBF03] = 0x00; //BRK to stop simulation + //run it + reset6502(); + exec6502(0xBF00); + //compare (just to be safe) + for(j=0;j 384) { + segments[0].filename[strlen(segments[0].filename) - (length - sizeof(basic)/sizeof(char) - move_len - 384)] = '\0'; + fprintf(stderr,"WARNING: BF00 page overflow: truncating display filename to %s\n\n",segments[0].filename); + length = 384 + sizeof(basic)/sizeof(char) + move_len; + } + } + else { + if(length - sizeof(basic)/sizeof(char) - move_len > 256) { + segments[0].filename[strlen(segments[0].filename) - (length - sizeof(basic)/sizeof(char) - move_len - 256)] = '\0'; + fprintf(stderr,"WARNING: BF00 page overflow: truncating display filename to %s\n\n",segments[0].filename); + length = 256 + sizeof(basic)/sizeof(char) + move_len; + } + } + + freq0 = 2000; + freq1 = 1000; + checksum = 0xff; + + if(basicload) { // write basic stub + header[0] = length & 0xFF; + header[1] = length >> 8; + for(i=0;i<3;i++) { + WRITEBYTE(header[i]); + checksum ^= header[i]; + } + WRITEBYTE(checksum); + + appendtone(&output,&outputlength,1000,rate,0,1,&offset); + appendtone(&output,&outputlength,770,rate,4.0,0,&offset); + appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); + appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); + + // write out basic program + checksum = 0xff; + for(i=0;i> 8; + + //load end + table[2] = (cmp_start + segments[0].length + sizeof(inflatecode)/sizeof(char) + 1) & 0xff; + table[3] = (cmp_start + segments[0].length + sizeof(inflatecode)/sizeof(char) + 1) >> 8; + + //inflate src + table[4] = cmp_start & 0xff; + table[5] = cmp_start >> 8; + + //inflate end + table[8] = (segments[0].start + segments[0].codelength) & 0xff; + table[9] = (segments[0].start + segments[0].codelength) >> 8; + } + else { + //load start + table[0] = segments[0].start & 0xff; + table[1] = segments[0].start >> 8; + + //load end + table[2] = (segments[0].start + segments[0].length + 1) & 0xff; + table[3] = (segments[0].start + segments[0].length + 1) >> 8; + } + //JMP to code, inflate dst + table[6] = segments[0].start & 0xff; + table[7] = segments[0].start >> 8; + table[10] = compress; + table[11] = warm; + + if(fast) + for(i=0;i> 8; + + freq0 = 2000; + freq1 = 1000; + for(i=0;i<3;i++) { + WRITEBYTE(header[i]); + checksum ^= header[i]; + } + WRITEBYTE(checksum); + + registerevent(events,outputlength,"BASIC Header + 770Hz Preamble"); + + appendtone(&output,&outputlength,1000,rate,0,1,&offset); + appendtone(&output,&outputlength,770,rate,4.0,0,&offset); + appendtone(&output,&outputlength,2500,rate,0,0.5,&offset); + appendtone(&output,&outputlength,2000,rate,0,0.5,&offset); + + registerevent(events,outputlength,"BASIC Stub/Assembly 9600 BPS Code @ 1333 BPS"); + + // write out basic program + checksum = 0xff; + for(i=0;i> 8; + ram[0x02] = 0x00; + ram[0x03] = 0x90; + ram[0x04] = 0xFF; + ram[0x9089] = 0x85; //STA + ram[0x908A] = 0x04; //zero page $04 + ram[0x908B] = 0x00; //BRK + + reset6502(); + exec6502(0x9065); + + if(ram[0x04] != 0) + fprintf(stderr,"WARNING: simulated checksum failed: %02X\n",ram[0x04]); + + inflate_times[i] += clockticks6502/1023000.0; + } + + //zero page src + ram[0x10] = (0x8FFF - cmp_len) & 0xFF; + ram[0x11] = (0x8FFF - cmp_len) >> 8; + //zero page dst + ram[0x12] = 0x00; + ram[0x13] = 0x10; + //setup JSR + ram[0x9000] = 0x20; // JSR $9B00 + ram[0x9001] = 0x00; + ram[0x9002] = 0x9B; + ram[0x9003] = 0x00; //BRK to stop simulation + //run it + reset6502(); + exec6502(0x9000); + //compare (just to be safe) + err=0; + for(j=0;j<7 * 4096;j++) + if(ram[0x1000 + j] != segments[i].data[j]) { + err = 1; + break; + } + if(err) + fprintf(stderr,"WARNING: simulated inflate failed at %04X\n",j+0x1000); + inflate_times[i] += clockticks6502/1023000.0; + + free(segments[i].data); + segments[i].data = cmp_data; + orig_len = segments[i].length; + segments[i].length = cmp_len; + segments[i].start = 0x8FFF - segments[i].length; + + // compress ? + // need to see what is faster, defaulting to compress for now + // if not compressed do not set start location, change asm code to check for 0,0 + // and not use inflate code + + // where to load data + start_table[start_table_len++] = segments[i].start & 0xFF; + start_table[start_table_len++] = segments[i].start >> 8; + + ones = zeros = 0; + for(j=0;j=0;j--) { + checksum = 0xff; + WRITEBYTE(j/10 + 48 + 0x80); + checksum ^= (j/10 + 48 + 0x80); + WRITEBYTE(j%10 + 48 + 0x80); + checksum ^= (j%10 + 48 + 0x80); + WRITEBYTE(0x00); + checksum ^= 0x00; + WRITEBYTE(checksum); + appendtone(&output,&outputlength,2000,rate,0,1,&offset); + appendtone(&output,&outputlength,6000,rate,1,0,&offset); + } +*/ + if(k8) + appendtone(&output,&outputlength,2000,rate,j,0,&offset); + else + appendtone(&output,&outputlength,6000,rate,j,0,&offset); + + registerevent(events,outputlength,"Load Segment @ 9600 BPS"); + + checksum = 0xff; + for(j=0;j grow) { + grow = *length + n + 1000000; + if((tmp = (double *)realloc(*sound, (grow) * sizeof(double))) == NULL) + abort(); + *sound = tmp; + } + +//tmp -> (*sound) + /* + better square code someday, theory here is to use sinewave then square it. + to address sin() == 0, i have to keep track of the last value to determine + direction + + this method was written to better address cycles that do not divide the sample rate + */ + if(square) { + double last = -1; + + if(*offset) + last = 1; + + if(freq) + for(i=0;i 0) - (last < 0)) : ((a > 0) - (a < 0)); + } + else + for (i = 0; i < n; i++) + (*sound)[*length + i] = 0; + } + else + for(i=0;i=0;i--) { + if(filename[i] == '.') + break; + stack[sp++] = filename[i]; + } + stack[sp] = '\0'; + + if(sp == strlen(filename) || sp == 0) + return(NULL); + + if((rval = (char *)malloc(sp * sizeof(char))) == NULL) + ; //do error code + + rval[sp] = '\0'; + for(i=0;i> 24, fptr); + fputc((totalsize & 0x00ff0000) >> 16, fptr); + fputc((totalsize & 0x0000ff00) >> 8, fptr); + fputc((totalsize & 0x000000ff), fptr); + fprintf(fptr, "AIFF"); + + // Write the common chunk + fprintf(fptr, "COMM"); + fputc(0, fptr); // Size + fputc(0, fptr); + fputc(0, fptr); + fputc(18, fptr); + fputc(0, fptr); // Channels = 1 + fputc(1, fptr); + fputc((nsamples & 0xff000000) >> 24, fptr); // Samples + fputc((nsamples & 0x00ff0000) >> 16, fptr); + fputc((nsamples & 0x0000ff00) >> 8, fptr); + fputc((nsamples & 0x000000ff), fptr); + fputc(0, fptr); // Size = 16 + fputc(bits, fptr); + + ConvertToIeeeExtended(nfreq, bit80); + for (i = 0; i < 10; i++) + fputc(bit80[i], fptr); + + // Write the sound data chunk + fprintf(fptr, "SSND"); + fputc((((bits / 8) * nsamples + 8) & 0xff000000) >> 24, fptr); // Size + fputc((((bits / 8) * nsamples + 8) & 0x00ff0000) >> 16, fptr); + fputc((((bits / 8) * nsamples + 8) & 0x0000ff00) >> 8, fptr); + fputc((((bits / 8) * nsamples + 8) & 0x000000ff), fptr); + fputc(0, fptr); // Offset + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); // Block + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); + + // Find the range + themin = samples[0]; + themax = themin; + for (i = 1; i < nsamples; i++) { + if (samples[i] > themax) + themax = samples[i]; + if (samples[i] < themin) + themin = samples[i]; + } + if (themin >= themax) { + themin -= 1; + themax += 1; + } + themid = (themin + themax) / 2; + themin -= themid; + themax -= themid; + if (ABS(themin) > ABS(themax)) + themax = ABS(themin); +// scale = amp * 32760 / (themax); + scale = amp * ((bits == 16) ? 32760 : 124) / (themax); + + // Write the data + for (i = 0; i < nsamples; i++) { + if (bits == 16) { + v = (unsigned short) (scale * (samples[i] - themid)); + fputc((v & 0xff00) >> 8, fptr); + fputc((v & 0x00ff), fptr); + } else { + v = (unsigned char) (scale * (samples[i] - themid)); + fputc(v, fptr); + } + } +} + +/* + Write an WAVE sound file + Only do one channel, only support 16 bit. + Supports any (reasonable) sample frequency + Little/big endian independent! +*/ + +// egan: changed code to support 8 bit. + +void Write_WAVE(FILE * fptr, double *samples, long nsamples, int nfreq, int bits, double amp) +{ + unsigned short v; + int i; + unsigned long totalsize, bytespersec; + double themin, themax, scale, themid; + + // Write the form chunk + fprintf(fptr, "RIFF"); + totalsize = (bits / 8) * nsamples + 36; + fputc((totalsize & 0x000000ff), fptr); // File size + fputc((totalsize & 0x0000ff00) >> 8, fptr); + fputc((totalsize & 0x00ff0000) >> 16, fptr); + fputc((totalsize & 0xff000000) >> 24, fptr); + fprintf(fptr, "WAVE"); + fprintf(fptr, "fmt "); // fmt_ chunk + fputc(16, fptr); // Chunk size + fputc(0, fptr); + fputc(0, fptr); + fputc(0, fptr); + fputc(1, fptr); // Format tag - uncompressed + fputc(0, fptr); + fputc(1, fptr); // Channels + fputc(0, fptr); + fputc((nfreq & 0x000000ff), fptr); // Sample frequency (Hz) + fputc((nfreq & 0x0000ff00) >> 8, fptr); + fputc((nfreq & 0x00ff0000) >> 16, fptr); + fputc((nfreq & 0xff000000) >> 24, fptr); + bytespersec = (bits / 8) * nfreq; + fputc((bytespersec & 0x000000ff), fptr); // Average bytes per second + fputc((bytespersec & 0x0000ff00) >> 8, fptr); + fputc((bytespersec & 0x00ff0000) >> 16, fptr); + fputc((bytespersec & 0xff000000) >> 24, fptr); + fputc((bits / 8), fptr); // Block alignment + fputc(0, fptr); + fputc(bits, fptr); // Bits per sample + fputc(0, fptr); + fprintf(fptr, "data"); + totalsize = (bits / 8) * nsamples; + fputc((totalsize & 0x000000ff), fptr); // Data size + fputc((totalsize & 0x0000ff00) >> 8, fptr); + fputc((totalsize & 0x00ff0000) >> 16, fptr); + fputc((totalsize & 0xff000000) >> 24, fptr); + + // Find the range + themin = samples[0]; + themax = themin; + for (i = 1; i < nsamples; i++) { + if (samples[i] > themax) + themax = samples[i]; + if (samples[i] < themin) + themin = samples[i]; + } + if (themin >= themax) { + themin -= 1; + themax += 1; + } + themid = (themin + themax) / 2; + themin -= themid; + themax -= themid; + if (ABS(themin) > ABS(themax)) + themax = ABS(themin); +// scale = amp * 32760 / (themax); + scale = amp * ((bits == 16) ? 32760 : 124) / (themax); + + // Write the data + for (i = 0; i < nsamples; i++) { + if (bits == 16) { + v = (unsigned short) (scale * (samples[i] - themid)); + fputc((v & 0x00ff), fptr); + fputc((v & 0xff00) >> 8, fptr); + } else { + v = (unsigned char) (scale * (samples[i] - themid)); + fputc(v + 0x80, fptr); + } + } +} + + +/* + * C O N V E R T T O I E E E E X T E N D E D + */ + +/* Copyright (C) 1988-1991 Apple Computer, Inc. + * All rights reserved. + * + * Machine-independent I/O routines for IEEE floating-point numbers. + * + * NaN's and infinities are converted to HUGE_VAL or HUGE, which + * happens to be infinity on IEEE machines. Unfortunately, it is + * impossible to preserve NaN's in a machine-independent way. + * Infinities are, however, preserved on IEEE machines. + * + * These routines have been tested on the following machines: + * Apple Macintosh, MPW 3.1 C compiler + * Apple Macintosh, THINK C compiler + * Silicon Graphics IRIS, MIPS compiler + * Cray X/MP and Y/MP + * Digital Equipment VAX + * + * + * Implemented by Malcolm Slaney and Ken Turkowski. + * + * Malcolm Slaney contributions during 1988-1990 include big- and little- + * endian file I/O, conversion to and from Motorola's extended 80-bit + * floating-point format, and conversions to and from IEEE single- + * precision floating-point format. + * + * In 1991, Ken Turkowski implemented the conversions to and from + * IEEE double-precision format, added more precision to the extended + * conversions, and accommodated conversions involving +/- infinity, + * NaN's, and denormalized numbers. + */ + +#ifndef HUGE_VAL +#define HUGE_VAL HUGE +#endif /*HUGE_VAL */ + +#define FloatToUnsigned(f) ((unsigned long)(((long)(f - 2147483648.0)) + 2147483647L) + 1) + +void ConvertToIeeeExtended(double num, unsigned char *bytes) +{ + int sign; + int expon; + double fMant, fsMant; + unsigned long hiMant, loMant; + + if (num < 0) { + sign = 0x8000; + num *= -1; + } else { + sign = 0; + } + + if (num == 0) { + expon = 0; + hiMant = 0; + loMant = 0; + } else { + fMant = frexp(num, &expon); + if ((expon > 16384) || !(fMant < 1)) { /* Infinity or NaN */ + expon = sign | 0x7FFF; + hiMant = 0; + loMant = 0; /* infinity */ + } else { /* Finite */ + expon += 16382; + if (expon < 0) { /* denormalized */ + fMant = ldexp(fMant, expon); + expon = 0; + } + expon |= sign; + fMant = ldexp(fMant, 32); + fsMant = floor(fMant); + hiMant = FloatToUnsigned(fsMant); + fMant = ldexp(fMant - fsMant, 32); + fsMant = floor(fMant); + loMant = FloatToUnsigned(fsMant); + } + } + + bytes[0] = expon >> 8; + bytes[1] = expon; + bytes[2] = hiMant >> 24; + bytes[3] = hiMant >> 16; + bytes[4] = hiMant >> 8; + bytes[5] = hiMant; + bytes[6] = loMant >> 24; + bytes[7] = loMant >> 16; + bytes[8] = loMant >> 8; + bytes[9] = loMant; +} + +uint8_t read6502(uint16_t address) +{ + return ram[address]; +} + +void write6502(uint16_t address, uint8_t value) +{ + ram[address] = value; +} + +void registerevent(event *events, unsigned long int timestamp, char *label) +{ + assert(eventnumber < MAXEVENTS); + + events[eventnumber].timestamp = timestamp; + strcpy(events[eventnumber].label,label); + + eventnumber++; +} + +void printevents(event *events, int rate) +{ + int i; + + printf("Play List:\n\n"); + for(i=0;i