mirror of
https://github.com/tebl/RC6502-Apple-1-Replica.git
synced 2024-11-25 17:44:22 +00:00
245 lines
11 KiB
NASM
245 lines
11 KiB
NASM
.CR 6502
|
||
.TF wozaci.hex,HEX,8
|
||
.LF wozaci.list
|
||
;-------------------------------------------------------------------------
|
||
;
|
||
; The WOZ Apple Cassette Interface for the Apple 1
|
||
; Written by Steve Wozniak somewhere around 1976
|
||
;
|
||
;-------------------------------------------------------------------------
|
||
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Memory declaration
|
||
;-------------------------------------------------------------------------
|
||
|
||
HEX1L .EQ $24 End address of dump block
|
||
HEX1H .EQ $25
|
||
HEX2L .EQ $26 Begin address of dump block
|
||
HEX2H .EQ $27
|
||
SAVEINDEX .EQ $28 Save index in input buffer
|
||
LASTSTATE .EQ $29 Last input state
|
||
|
||
IN .EQ $0200 Input buffer
|
||
FLIP .EQ $C000 Output flip-flop
|
||
TAPEIN .EQ $C081 Tape input
|
||
KBD .EQ $D010 PIA.A keyboard input
|
||
KBDCR .EQ $D011 PIA.A keyboard control register
|
||
ESCAPE .EQ $FF1A Escape back to monitor
|
||
ECHO .EQ $FFEF Echo character to terminal
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Constants
|
||
;-------------------------------------------------------------------------
|
||
|
||
CR .EQ $8D Carriage Return
|
||
ESC .EQ $9B ASCII ESC
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Let's get started
|
||
;-------------------------------------------------------------------------
|
||
.OR $C100
|
||
WOZACI LDA #"*" Print the Tape prompt
|
||
JSR ECHO
|
||
LDA #CR And drop the cursor one line
|
||
JSR ECHO
|
||
|
||
LDY #-1 Reset the input buffer index
|
||
NEXTCHAR INY
|
||
KBDWAIT LDA KBDCR Wait for a key
|
||
BPL KBDWAIT Still no key!
|
||
|
||
LDA KBD Read key from keyboard
|
||
STA IN,Y Save it into buffer
|
||
JSR ECHO And type it on the screen
|
||
CMP #ESC
|
||
BEQ WOZACI Start from scratch if ESC!
|
||
CMP #CR
|
||
BNE NEXTCHAR Read keys until CR
|
||
|
||
LDX #-1 Initialize parse buffer pointer
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Start parsing first or a new tape command
|
||
;-------------------------------------------------------------------------
|
||
|
||
NEXTCMD LDA #0 Clear begin and end values
|
||
STA HEX1L
|
||
STA HEX1H
|
||
STA HEX2L
|
||
STA HEX2H
|
||
|
||
NEXTCHR INX Increment input pointer
|
||
LDA IN,X Get next char from input line
|
||
CMP #"R" Read command?
|
||
BEQ READ Yes!
|
||
CMP #"W" Write command?
|
||
BEQ WRITE Yes! (note: CY=1)
|
||
CMP #"." Separator?
|
||
BEQ SEP Yes!
|
||
CMP #CR End of line?
|
||
BEQ GOESC Escape to monitor! We're done
|
||
CMP #" " Ignore spaces
|
||
BEQ NEXTCHR
|
||
EOR #"0" Map digits to 0-9
|
||
CMP #9+1 Is it a decimal digit?
|
||
BCC DIG Yes!
|
||
ADC #$88 Map letter "A"-"F" to $FA-$FF
|
||
CMP #$FA Hex letter?
|
||
BCC WOZACI No! Character not hex!
|
||
|
||
DIG ASL Hex digit to MSD of A
|
||
ASL
|
||
ASL
|
||
ASL
|
||
|
||
LDY #4 Shift count
|
||
HEXSHIFT ASL Hex digit left, MSB to carry
|
||
ROL HEX1L Rotate into LSD
|
||
ROL HEX1H Rotate into MSD
|
||
DEY Done 4 shifts?
|
||
BNE HEXSHIFT No! Loop
|
||
BEQ NEXTCHR Handle next character
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Return to monitor, prints \ first
|
||
;-------------------------------------------------------------------------
|
||
|
||
GOESC JMP ESCAPE Escape back to monitor
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Separating . found. Copy HEX1 to Hex2. Doesn't clear HEX1!!!
|
||
;-------------------------------------------------------------------------
|
||
|
||
SEP LDA HEX1L Copy hex value 1 to hex value 2
|
||
STA HEX2L
|
||
LDA HEX1H
|
||
STA HEX2H
|
||
BCS NEXTCHR Always taken!
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Write a block of memory to tape
|
||
;-------------------------------------------------------------------------
|
||
|
||
WRITE LDA #64 Write 10 second header
|
||
JSR WHEADER
|
||
|
||
WRNEXT DEY Compensate timing for extra work
|
||
LDX #0 Get next byte to write
|
||
LDA (HEX2L,X)
|
||
|
||
LDX #8*2 Shift 8 bits (decremented twice)
|
||
WBITLOOP ASL Shift MSB to carry
|
||
JSR WRITEBIT Write this bit
|
||
BNE WBITLOOP Do all 8 bits!
|
||
|
||
JSR INCADDR Increment address
|
||
LDY #30 Compensate timer for extra work
|
||
BCC WRNEXT Not done yet! Write next byte
|
||
|
||
RESTIDX LDX SAVEINDEX Restore index in input line
|
||
BCS NEXTCMD Always taken!
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Read from tape
|
||
;-------------------------------------------------------------------------
|
||
|
||
READ JSR FULLCYCLE Wait until full cycle is detected
|
||
LDA #22 Introduce some delay to allow
|
||
JSR WHEADER the tape speed to stabilize
|
||
JSR FULLCYCLE Synchronize with full cycle
|
||
|
||
NOTSTART LDY #31 Try to detect the much shorter
|
||
JSR CMPLEVEL start bit
|
||
BCS NOTSTART Start bit not detected yet!
|
||
|
||
JSR CMPLEVEL Wait for 2nd phase of start bit
|
||
|
||
LDY #58 Set threshold value in middle
|
||
RDBYTE LDX #8 Receiver 8 bits
|
||
RDBIT PHA
|
||
JSR FULLCYCLE Detect a full cycle
|
||
PLA
|
||
ROL Roll new bit into result
|
||
LDY #57 Set threshold value in middle
|
||
DEX Decrement bit counter
|
||
BNE RDBIT Read next bit!
|
||
STA (HEX2L,X) Save new byte
|
||
|
||
JSR INCADDR Increment address
|
||
LDY #53 Compensate threshold with workload
|
||
BCC RDBYTE Do next byte if not done yet!
|
||
BCS RESTIDX Always taken! Restore parse index
|
||
|
||
FULLCYCLE JSR CMPLEVEL Wait for two level changes
|
||
CMPLEVEL DEY Decrement time counter
|
||
LDA TAPEIN Get Tape In data
|
||
CMP LASTSTATE Same as before?
|
||
BEQ CMPLEVEL Yes!
|
||
STA LASTSTATE Save new data
|
||
|
||
CPY #128 Compare threshold
|
||
RTS
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Write header to tape
|
||
;
|
||
; The header consists of an asymmetric cycle, starting with one phase of
|
||
; approximately (66+47)x5=565us, followed by a second phase of
|
||
; approximately (44+47)x5=455us.
|
||
; Total cycle duration is approximately 1020us ~ 1kHz. The actual
|
||
; frequencywill be a bit lower because of the additional workload between
|
||
; the twoloops.
|
||
; The header ends with a short phase of (30+47)x5=385us and a normal
|
||
; phase of (44+47)x5=455us. This start bit must be detected by the read
|
||
; routine to trigger the reading of the actual data.
|
||
;-------------------------------------------------------------------------
|
||
|
||
WHEADER STX SAVEINDEX Save index in input line
|
||
HCOUNT LDY #66 Extra long delay
|
||
JSR WDELAY CY is constantly 1, writing a 1
|
||
BNE HCOUNT Do this 64 * 256 time!
|
||
ADC #-2 Decrement A (CY=1 all the time)
|
||
BCS HCOUNT Not all done!
|
||
LDY #30 Write a final short bit (start)
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Write a full bit cycle
|
||
;
|
||
; Upon entry Y contains a compensated value for the first phase of 0
|
||
; bit length. All subsequent loops don't have to be time compensated.
|
||
;-------------------------------------------------------------------------
|
||
|
||
WRITEBIT JSR WDELAY Do two equal phases
|
||
LDY #44 Load 250us counter - compensation
|
||
|
||
WDELAY DEY Delay 250us (one phase of 2kHz)
|
||
BNE WDELAY
|
||
BCC WRITE1 Write a '1' (2kHz)
|
||
|
||
LDY #47 Additional delay for '0' (1kHz)
|
||
WDELAY0 DEY (delay 250us)
|
||
BNE WDELAY0
|
||
|
||
WRITE1 LDY FLIP,X Flip the output bit
|
||
LDY #41 Reload 250us cntr (compensation)
|
||
DEX Decrement bit counter
|
||
RTS
|
||
|
||
;-------------------------------------------------------------------------
|
||
; Increment current address and compare with last address
|
||
;-------------------------------------------------------------------------
|
||
|
||
INCADDR LDA HEX2L Compare current address with
|
||
CMP HEX1L end address
|
||
LDA HEX2H
|
||
SBC HEX1H
|
||
INC HEX2L And increment current address
|
||
BNE NOCARRY No carry to MSB!
|
||
INC HEX2H
|
||
NOCARRY RTS
|
||
|
||
;-------------------------------------------------------------------------
|
||
|
||
.LI OFF
|
||
|