Skip navigation
Published Friday, January 18, 2013 at 12:26 AM

Getting Closer

Good progress tonight! I think the code to WRITE data to tape is fully complete. I'm not particularly impressed with my own 6502 assembly, but by golly it works.

Note that there is no checksum yet. I'm calling this “Optional” for now. I may regret that later.

The next step is the opposite process: READING THE DATA! I'll have more on that when I'm a little less tired.

.alias TAPEST   $40
.alias A1L      $41             ; Start address low byte
.alias A1H      $42             ; Start address high byte
.alias A2L      $43             ; End address low byte
.alias A2H      $44             ; End address high byte

.alias IOB      $8000
.alias IOA      $8001
.alias DDRB     $8002
.alias DDRA     $8003

.org $0300

INIT:   LDA     #$FF            ; Make all Port-A IO lines outputs
        STA     DDRA
        LDA     #$00            ; Initialize tape state scratch space
        STA     TAPEST
        STA     IOA

;*******************************************************************************
; Write Memory to Tape
;   Start Address: A1L,A1H
;   End Address: A2L,A2H
;*******************************************************************************
WRITE:  JSR     HEADR
        JSR     WRSYNC
        JSR     WRDATA

        ; END
        BRK
        ; END

;*******************************************************************************
; Write the Tape Header.
; This outputs 10 seconds of 750 Hz square wave.
;
; NB: Measured values for cycle widths at 1MHz PHI2:
;
;  $79 = 776.4Hz  1.288ms
;  $7A = 766.9Hz  1.304ms
;  $7B = 757.6Hz  1.320ms
;*******************************************************************************
HEADR:  LDX     #$3C            ; 60 times thru inner loop
HD0:    LDY     #$FF            ;  (60 * 255 = 15,300 half cycles)
HD1:    JSR     TOGTAP
        LDA     #$7A            ; (122 * 5 uS) + overhead = 650 uS width
HD3:    SBC     #$01            ; Delay (using A, because X and Y are busy)
        BNE     HD3
        DEY
        BNE     HD1             ; Inner Loop
        DEX
        BNE     HD0             ; Outer Loop
        RTS

;*******************************************************************************
; Write a single Sync Bit.
;*******************************************************************************

WRSYNC: LDX     #$24
        JSR     WS0             ; Recursive call to toggle bit twice
WS0:    JSR     TOGTAP
WS1:    DEX
        BNE     WS1
        LDX     #$28
        RTS

;*******************************************************************************
; Read the memory between A1L,A1H and A2L,A2H, one byte at a
; time, and send it to tape.
;
; Values #$34 was chosen as a perfect match for a 250uS
; delay. #$2A is the same delay, but compensated.
;*******************************************************************************

WRDATA: LDX     #$00
        LDA     (A1L,X)         ; Get first byte to send to tape.
        LDY     #$08            ; 8 bits to do
WRBIT:  ROL                     ; Shift leftmost bit into C
        PHA                     ; Save the accumulator.
WD0:    JSR     TOGTAP          ; Toggle output state
        BCC     ZERDLY          ; Is C a '0'? Skip '1' delay
        LDX     #$34
ONEDLY: DEX                     ; Delay for 260 clock cycles.
        BNE     ONEDLY
ZERDLY: LDX     #$2A
ZD0:    DEX                     ; Delay for 210 clock cycles.
        BNE     ZD0
        ; Bit test does not affect 'Carry' flag, so it will
        ; still be set from the ROL
        BIT     TAPEST          ; Check to see if the cycle is done.
        BNE     WD0             ; If not, write second half.
        PLA                     ; If so, restore the input byte
        DEY                     ; If not done with bits, do
        BNE     WRBIT           ;   next bit...
        JSR     INCADR          ; Increment read address,
        BCC     WRDATA          ;   and get the next byte
        ; Done. At this point, the entire block of memory to
        ; dump to tape has been sent. A1L,A1H have been
        ; incremented to be > A2L,A2H
        RTS

;*******************************************************************************
; Toggle the current tape output state. Modifies Accumulator.
;*******************************************************************************
TOGTAP: LDA     TAPEST          ; Load current tape state
        EOR     #$01            ; Toggle it
        STA     TAPEST          ; Store back into tape state,
        STA     IOA             ;    and out to tape.
        RTS

;*******************************************************************************
; Increment the tape memory buffer pointer.
;
; Carry will be left set iff A1L == A2L and A1H == A2H
; Blatently stolen from Woz's Apple 1 Monitor
;*******************************************************************************
INCADR: LDA     A1L             ; Compare current addr with end addr.
        CMP     A2L             ; Sets carry if A1L >= A2L.
        LDA     A1H
        SBC     A2H             ; Clears carry if A2H < A1H.
        INC     A1L
        BNE     NOCARY          ; If A1L is now 00, inc A1H as well.
        INC     A1H
NOCARY: RTS