c2t-96h minor updates, doc updates, article added/updated, windows 96h binary
This commit is contained in:
parent
dc09517573
commit
c96332b204
9
Makefile
9
Makefile
|
@ -1,13 +1,16 @@
|
||||||
|
|
||||||
|
|
||||||
all: c2t
|
all: c2t c2t-96h
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm c2t.h c2t
|
rm c2t.h c2t c2t-96h
|
||||||
cd asm; make clean
|
cd asm; make clean
|
||||||
|
|
||||||
c2t: c2t.c c2t.h
|
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
|
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
|
./makeheader
|
||||||
|
|
19
README.md
19
README.md
|
@ -28,7 +28,8 @@ You clearly do not understand the awesomeness of the Apple II, move along.
|
||||||
|
|
||||||
## Version
|
## Version
|
||||||
|
|
||||||
0.996 (Nov 29 2014)
|
c2t 0.996 (Nov 29 2014)
|
||||||
|
c2t-96h 0.997 (Dec 31 2015)
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -63,6 +64,22 @@ gcc -Wall -O3 -static -o c2t c2t.c
|
||||||
```
|
```
|
||||||
> Use the `miniz.h` in the `windows` directory.
|
> Use the `miniz.h` in the `windows` directory.
|
||||||
|
|
||||||
|
To cross build for Windows from OS/X, first install <http://crossgcc.rts-software.org/doku.php?id=compiling_for_win32>, 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:
|
## Tested Configurations:
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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
|
|
@ -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
|
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
|
@ -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')
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
From: http://support.apple.com/kb/TA40730
|
||||||
|
|
||||||
|
This note is about the cassette interface built into the Apple II and
|
||||||
|
Apple II+, subroutines. An assumption made here is that the cassette
|
||||||
|
recorder is in the proper mode, play or record, when the read and write
|
||||||
|
routines are executed. Note also that the timing is approximate and may
|
||||||
|
vary from one Apple to another.
|
||||||
|
|
||||||
|
A record is a block of binary data. This data may be a BASIC or APPLESOFT
|
||||||
|
program, a machine language program, or just binary data. Records representing
|
||||||
|
BASIC or APPLESOFT programs are really two records, the length of the program
|
||||||
|
and the actual program. A record consists of a header, synchronous bit, the
|
||||||
|
actual data, and a checksum byte for error detection.
|
||||||
|
|
||||||
|
Monitor record format
|
||||||
|
|
||||||
|
+--------+-+-----------------------+-+
|
||||||
|
| HEADER |S| DATA |C|
|
||||||
|
+--------+-+-----------------------+-+
|
||||||
|
|
||||||
|
BASIC program record format
|
||||||
|
|
||||||
|
+--------+-+----+-+--------+-+----------------+-+
|
||||||
|
| HEADER |S| LB |C| HEADER |S| PROGRAM |C|
|
||||||
|
+--------+-+----+-+--------+-+----------------+-+
|
||||||
|
|
||||||
|
Key: S = SYNC bit
|
||||||
|
C = CHECKSUM byte
|
||||||
|
LB = BASIC program length
|
||||||
|
|
||||||
|
The header consists of 10 seconds of 770 Hz tone, (1 cycle equals 1300
|
||||||
|
microseconds). This gives enough time for the cassette motor to attain speed
|
||||||
|
and the plastic tape leader to go by. A subroutine called HEADR generates a
|
||||||
|
shortened header between the BASIC length bytes and the BASIC program itself.
|
||||||
|
The length of the header tone is controlled by the value of the accumulator on
|
||||||
|
entry to the subroutine. This can vary from 0.2 seconds to 40 seconds. On
|
||||||
|
entry the X register should be 0 and the carry flag should be set. HEADR also
|
||||||
|
generates a synchronous bit at the end of the tone. HEADR resides at
|
||||||
|
hexadecimal address $FCC9, or decimal address -882.
|
||||||
|
|
||||||
|
The last cycle of header tone and SYNC bit
|
||||||
|
|
||||||
|
---+ +-------------+ +-----+
|
||||||
|
| | | | |
|
||||||
|
+-------------+ +----+ |
|
||||||
|
|
||||||
|
| 1300 microseconds | 200 | 250 | header tone | synchronous bit |
|
||||||
|
|
||||||
|
The synchronous bit, generated by HEADR, is one half cycle of 2500 Hz, (200
|
||||||
|
microseconds) and one half cycle of 2000 Hz, (250 microseconds). It is used to
|
||||||
|
signal the end of the header tone and the start of the data.
|
||||||
|
|
||||||
|
The data is recorded on the tape with a low starting address and a high ending
|
||||||
|
address. Each byte of data is shifted out most significant bit first, least
|
||||||
|
significant bit last. A zero bit is made up of one cycle of 2 kHz, (250
|
||||||
|
microseconds per half cycle) and a one bit is one cycle of 1 kHz, (500
|
||||||
|
microseconds per half cycle). This works out to 2000 baud for zeros only and
|
||||||
|
1000 baud for ones, or an average of 1500 baud.
|
||||||
|
|
||||||
|
A zero bit and a one bit
|
||||||
|
|
||||||
|
+-----+ +----------+ +
|
||||||
|
| | | | |
|
||||||
|
+ +-----+ +----------+
|
||||||
|
|
||||||
|
| 500 usec | 1000 usec |
|
||||||
|
|
||||||
|
The checksum byte is written on the tape at the end of the data block. All
|
||||||
|
during reading or writing each data byte is EXCLUSIVE OR-ed with the checksum
|
||||||
|
byte. If the checksum computed during a read agrees with the checksum that was
|
||||||
|
written out, then the data is probably good. This method will detect an odd
|
||||||
|
number of errors for any of the eight bits of the byte.
|
||||||
|
|
||||||
|
In writing data, the cassette output uses quite simple circuitry, a flip-flop
|
||||||
|
connected through a voltage divider to the jack on the back panel of the
|
||||||
|
Apple. Any time the address $C020 is accessed this flip-flop changes state.
|
||||||
|
Accessing the flip-flop once every 500 microseconds generates a 1000 Hz
|
||||||
|
tone.
|
|
@ -0,0 +1,65 @@
|
||||||
|
From: http://support.apple.com/kb/TA40737
|
||||||
|
|
||||||
|
For reading data, the cassette recorder uses a more complicated input circuit
|
||||||
|
consisting of a 741 operational amplifier configured as a zero crossing
|
||||||
|
detector. Zero crossing detection means that whenever the voltage at the input
|
||||||
|
jack goes from positive to negative (or negative to positive) the output of
|
||||||
|
the amplifier switches from a 1 to a 0 (or 0 to 1). The detector is accessed
|
||||||
|
by any read to address $C060. The sign bit (most significant bit) of the byte
|
||||||
|
read reflects the detector status. The read routines continually EXCLUSIVE
|
||||||
|
ORs this bit with the value most recently read to detect a change in state.
|
||||||
|
The amount of time required to change state indicates the incoming frequency
|
||||||
|
which then is used to determine if a one or a zero has been received. After
|
||||||
|
detecting the first zero crossing at the start of the header, the read routine
|
||||||
|
uses HEADR to generate a 3.5 delay, and then the read routine waits for the
|
||||||
|
sync bit. After HEADR generates the synchronous bit, the read routine reads
|
||||||
|
the data and puts it in the specified memory range.
|
||||||
|
|
||||||
|
In using the cassette interface to either read or write, all you need do is
|
||||||
|
specify an address range and execute the read or write subroutine. The
|
||||||
|
address range is stored in four bytes, two for the first address to be saved
|
||||||
|
and two for the last to be saved. In both cases the least significant byte is
|
||||||
|
first.
|
||||||
|
|
||||||
|
Commanding the cassette interface:
|
||||||
|
|
||||||
|
1. from the monitor:
|
||||||
|
If the start is $800 and the end is $9FF, then
|
||||||
|
800.9FFW will write the data to the cassette and
|
||||||
|
800.9FFR will retrieve it.
|
||||||
|
|
||||||
|
2. from machine language:
|
||||||
|
Again, if the start is $800 and the end is $9FF then store the address
|
||||||
|
range,
|
||||||
|
|
||||||
|
LDA #$00
|
||||||
|
STA $3C starting address low
|
||||||
|
LDA #$08
|
||||||
|
STA $3D starting address high
|
||||||
|
LDA #$FF
|
||||||
|
STA $3E ending address low
|
||||||
|
LDA #$09
|
||||||
|
STA $3F ending address high
|
||||||
|
JSR $FEDC write to block to tape
|
||||||
|
|
||||||
|
The JSR $FEDC will write to the cassette; JSR $FEFD will read from the
|
||||||
|
cassette.
|
||||||
|
|
||||||
|
3. from BASIC:
|
||||||
|
First set up the address range. If S = the start and E = the end then from
|
||||||
|
integer BASIC,
|
||||||
|
|
||||||
|
POKE 60,S MOD 256
|
||||||
|
POKE 61,S / 256
|
||||||
|
POKE 62,E MOD 256
|
||||||
|
POKE 63,E / 256
|
||||||
|
|
||||||
|
4. from APPLESOFT,
|
||||||
|
|
||||||
|
POKE 60,S - INT(S / 256) * 256
|
||||||
|
POKE 61,S / 256
|
||||||
|
POKE 62,E - INT(E / 256) * 256
|
||||||
|
POKE 63,E / 256
|
||||||
|
|
||||||
|
Then, to write out to cassette, use CALL -307; to read in from the cassette,
|
||||||
|
use CALL -259.
|
Binary file not shown.
Loading…
Reference in New Issue