RC6502-Apple-1-Replica/software/firmware/WozMon/wozaci.asm

245 lines
11 KiB
NASM
Raw Normal View History

2020-01-25 16:07:42 +00:00
.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