Hypertape DUMP

Original article on hypertape by Jim Butterfield.

HYPERTAPE
Jim Butterfield

How long does it take you to load a full 1K of KIM-1
memory? Over two minutes? And if you’re going for
memory expansion, how long will it take you to load
your 8K? Twenty minutes?

Hold onto your hats. Program HYPERTAPE! will write
fully compatible tapes in a fraction of the time.
You can load a full 1K in 21 seconds.

Fully compatible means this: once you’ve written
a tape using HYPERTAPE! you can read it back in using
the normal KIM-1 program (starting at 1873 as usual).
And the utilities and diagnostic programs work on this
super-compressed data (e.g., DIRECTORY and VUTAPE).

You’ll need some memory space for the program, of course.
If you have memory expansion, there’ll be no problem
finding space, of course. But if you’re on the basic
KIM-1, as I am, you’ll have to “squeeze in” HYPERTAPE!
along with the programs you’re dumping to tape. I try
to leave page 1 alone usually (the stack can overwrite
your program due to bugs), so I stage HYPERTAPE! in
that area. For the convenience of relocation, the
listing underlines those addresses that will need
changing. There are also four values needed in page zero which
you may change to any convenient location.

For those Interested in the theory of the thing, I
should mention: HYPERTAPE! is not the limit. If you
wished to abandon KIM-1 monitor compatibility, you
could continue to speed up tape by a factor of 4 or 5
times more. Can you imagine reading 1K in four seconds?
For the moment, however, HYPERTAPE! is plenty fast for me.

Thanks go to Julien Dubé for his help in staging early
versions off HYPERTAPE.

        TIC     = $F1
        COUNT   = $F2
        TRIB    = $F3
        GANG    = $F5

        SBD     = $1742
        PBDD    = $1743
        CLK1T   = $1744
        CLKRDI  = $1747
        CHKL    = $17E7
        CHKH    = $17E8
        SAL     = $17F5
        SAH     = $17F6
        EAL     = $17F7
        EAH     = $17F8
        ID      = $17F9
        VEB     = $17EC
        DISPZ   = $185C
        INTVEB  = $1932        
        CHKT    = $194C
        INCVEB  = $19EA

        .ORG    $0100

; this program also included in Super-dupe

DUMP:   LDA    #$AD
        STA    VEB
        JSR    INTVEB           ; set up sub
        LDA    #$27
        STA    GANG             ; flag for SBD
        LDA    #$BF
        STA    PBDD
        LDX    #$64
        LDA    #$16
        JSR     HIC
        LDA     #$2A
        JSR     OUTCHT
        LDA     ID
        JSR     OUTBT
        LDA     SAL
        JSR     OUTBTC
        LDA     SAH
        JSR     OUTBTC
DUMPT4: JSR     VEB
        JSR     OUTBTC
        JSR     INCVEB
        LDA     VEB+1
        CMP     EAL
        LDA     VEB+2
        SBC     EAH
        BCC     DUMPT4
        LDA     #$2F
        JSR     OUTCHT
        LDA     CHKL
        JSR     OUTBT
        LDA     CHKH
EXIT:   JSR     OUTBT
        LDX     #$02
        LDA     #$04
        JSR     HIC
        JMP     DISPZ

; subroutines

HIC:    STX     TIC
HIC1:   PHA
        JSR     OUTCHT
        PLA
        DEC     TIC
        BNE     HIC1
        RTS
OUTBTC: JSR     CHKT
OUTBT:  PHA
        LSR     A
        LSR     A
        LSR     A
        LSR     A
        JSR     HEXOUT
        PLA
        JSR     HEXOUT
        RTS
;
HEXOUT: AND     #$0F
        CMP     #$0A
        CLC
        BMI     HEX1
        ADC     #$07
HEX1:   ADC     #$30
OUTCHT: LDY     #$07
        STY     COUNT
TRY:    LDY     #$02
        STY     TRIB
ZON:    LDX     NPUL,Y
        PHA
ZON1:   BIT     CLKRDI
        BPL     ZON1
        LDA     TIMG,Y
        STA     CLK1T
        LDA     GANG
        EOR     #$80
        STA     SBD
        STA     GANG
        DEX
        BNE     ZON1
        PLA
        DEC     TRIB
        BEQ     SETZ
        BMI     ROUT
        LSR     A
        BCC     ZON
SETZ:   LDY     #0
        BEQ     ZON
ROUT:   DEC     COUNT
        BPL     TRY
        RTS

; frequency/density controls

NPUL:   .BYTE   $02
TIMG:   .BYTE   $C3, $03, $7E

Hypertape as used in MICRO ADE

2645   2EA9             ;
2646   2EA9             ; ****************************************
2647   2EA9             ; * KIM Cassette Read and Write routines *
2648   2EA9             ; ****************************************
2649   2EA9 20 CD 2D    ERID      JSR HEXOUT
2650   2EAC 20 EE 2D              JSR OUTSP
2651   2EAF             ;
2652   2EAF             ; Cassette Read Subroutine
2653   2EAF             ;
2654   2EAF             ; Very similar to the read routine in the KIM-1          
2655   2EAF             ; The LED display of incoming data adapted
2656   2EAF             ; from VUtape, a program by Jim Butterfield
2657   2EAF             ; from the KIM-1 User Notes.
2658   2EAF             ; 
2659   2EAF AD 02 17    CREAD     LDA SBD2              ; turn on cassette #1 by
2660   2EB2 29 FB                 AND #$FB              ; changing bit 2 to 0 in PIA port B
2661   2EB4 8D 02 17              STA SBD2
2662   2EB7 A9 7F                 LDA #$7F              ; turn on the KIM LED display             
2663   2EB9 8D 41 17              STA PADD              ; by setting the DD register                
2664   2EBC             ;
2665   2EBC D8                    CLD                   ; just to be sure
2666   2EBD             ;
2667   2EBD A9 8D                 LDA #$8D              ; set up VEB to save data
2668   2EBF 8D EC 17              STA VEB
2669   2EC2 20 32 19              JSR INTVEB            ; in KIM ROM
2670   2EC5             ;
2671   2EC5 A9 13                 LDA #$13              ; turn on input port from cassette
2672   2EC7 8D 42 17              STA SBD               ; 
2673   2ECA             ;
2674   2ECA 20 41 1A    SYNC      JSR RDBIT             ; start reading a bit at a time
2675   2ECD             ;
2676   2ECD 46 F3                 LSR TMP               ; shift into TMP
2677   2ECF 05 F3                 ORA TMP               ; and save it
2678   2ED1 85 F3                 STA TMP               ; and save it
2679   2ED3 8D 40 17              STA SAD               ; place it on the LED
2680   2ED6             ;
2681   2ED6 C9 16       TST       CMP #$16              ; is it a sync character?
2682   2ED8 D0 F0                 BNE SYNC              ; if not keep trying
2683   2EDA             ;
2684   2EDA 20 24 1A              JSR RDCHT             ; in sync, read a character
2685   2EDD 8D 40 17              STA SAD               ; display it on LED
2686   2EE0 C9 2A                 CMP #$2A              ; is it the start of data?
2687   2EE2 D0 F2                 BNE TST               ; if not loop again
2688   2EE4             ;
2689   2EE4 20 F3 19              JSR RDBYT             ; read the tape ID
2690   2EE7 C5 62                 CMP ID                ; is this the right tape ID?
2691   2EE9 D0 BE                 BNE ERID              ; print it if wrong
2692   2EEB             ;
2693   2EEB 20 F3 19              JSR RDBYT             ; read the start address
2694   2EEE 20 4C 19              JSR CHKT              ; include it in checksum
2695   2EF1 8D ED 17              STA VEB + 1           ; and save it in VEB
2696   2EF4 20 F3 19              JSR RDBYT             ; Read the hi part of the address
2697   2EF7 20 4C 19              JSR CHKT              ; include in checksum
2698   2EFA 8D EE 17              STA VEB + 2           ; set it up in VEB
2699   2EFD             ;
2700   2EFD A2 02       LOADIT    LDX #$02              ; start to laod data as 
2701   2EFF 20 24 1A    READIT    JSR RDCHT             ; ASCII characters
2702   2F02 C9 2F                 CMP #$2F              ; end of data symbol
2703   2F04 F0 14                 BEQ ENDRD             ; so wind it up
2704   2F06             ;
2705   2F06 20 94 2E              JSR PACKT             ; pack the ASCII into hex
2706   2F09 D0 BF                 BNE SYNC              ; error in character read 
2707   2F0B CA                    DEX                   ; count to two
2708   2F0C D0 F1                 BNE READIT            ; read second half
2709   2F0E             ;
2710   2F0E 20 4C 19              JSR CHKT              ; include in checksum
2711   2F11 20 EC 17              JSR VEB               ; RETPC via VEB
2712   2F14 20 EA 19              JSR INCVEB            ; increment RETPC address
2713   2F17 4C FD 2E              JMP LOADIT            ; and load next byte
2714   2F1A             ;
2715   2F1A 20 F3 19    ENDRD     JSR RDBYT             ; read checksum from tape
2716   2F1D CD E7 17              CMP CHKL              ; compare to calculated
2717   2F20 D0 A8                 BNE SYNC              ; and start over if wrong
2718   2F22 20 F3 19              JSR RDBYT             ; get second half of sum
2719   2F25 CD E8 17              CMP CHKH              ; and do the same with it
2720   2F28 D0 A0                 BNE SYNC
2721   2F2A             
2722   2F2A AD 02 17              LDA SBD2              ; turn off cassette bit 2
2723   2F2D 09 04                 ORA #$04              ; by setting bit 2 of PIA port
2724   2F2F 8D 02 17              STA SBD2
2725   2F32 4C 8C 1E              JMP INIT              ; return via INIT (reset all ports)
2726   2F35             ;
2727   2F35             ; *** KIM Cassette Write Subroutine ***
2728   2F35             ;
2729   2F35             ; adapted from Supertape by Jim Butterfield
2730   2F35             ; as published by in KIM-1 User Notes (V T, N 2)
2731   2F35             ;          
2732   2F35 AD 02 17    CWRITE    LDA SBD2              ; turn on cassette #2
2733   2F38 29 F7                 AND #$F7              ; by setting bit 3 to 0
2734   2F3A 8D 02 17              STA SBD2              ; in PIA port B
2735   2F3D             ;
2736   2F3D A9 AD                 LDA #$AD              ; setup VEB for save
2737   2F3F 8D EC 17              STA VEB               ;
2738   2F42 20 32 19              JSR INTVEB            ; 
2739   2F45             ;
2740   2F45 A9 27                 LDA #$27              ; set flag for SBD later
2741   2F47 85 F0                 STA GANG              ; 
2742   2F49             ;
2743   2F49 A9 BF                 LDA #$BF              ; turn on output to cassette
2744   2F4B 8D 43 17              STA PBDD              ; 
2745   2F4E             ;
2746   2F4E A2 F0                 LDX #$F0              ; send 240 sync pulses (optimal recorder start/stop time)
2747   2F50             ;
2748   2F50 A9 16                 LDA #$16              ; sync character
2749   2F52 20 A3 2F              JSR HIC               ; output X times
2750   2F55             ;
2751   2F55 A9 2A                 LDA #$2A              ; send start of data character
2752   2F57 20 C6 2F              JSR OUTCHT            ; 
2753   2F5A             ;
2754   2F5A A5 62                 LDA ID                ; get ID
2755   2F5C 20 B2 2F              JSR OUTBT             ; and send as byte
2756   2F5F             ;
2757   2F5F A5 60                 LDA SALX              ; send execution address
2758   2F61 20 AF 2F              JSR OUTBTC            ; with checksum calculation
2759   2F64 A5 61                 LDA SAHX              ; hi part too
2760   2F66 20 AF 2F              JSR OUTBTC
2761   2F69             ;
2762   2F69 20 EC 17    DUMPTA    JSR VEB               ; get a byte of memory
2763   2F6C 20 AF 2F              JSR OUTBTC            ; send and checksum it
2764   2F6F 20 EA 19              JSR INCVEB            ; point to next byte
2765   2F72 AD ED 17              LDA VEB + 1           ; check for end  against EAL
2766   2F75 CD F7 17              CMP EAL
2767   2F78 AD EE 17              LDA VEB + 2           ; and EAH
2768   2F7B ED F8 17              SBC EAH
2769   2F7E 90 E9                 BCC DUMPTA            ; again if not end
2770   2F80             ;
2771   2F80 A9 2F                 LDA #$2F              ; end end of data character
2772   2F82 20 C6 2F              JSR OUTCHT            ; as char
2773   2F85             ;
2774   2F85 AD E7 17              LDA CHKL              ; send checksum lo and hi
2775   2F88 20 B2 2F              JSR OUTBT
2776   2F8B AD E8 17              LDA CHKH
2777   2F8E 20 B2 2F              JSR OUTBT
2778   2F91 A2 02                 LDX #$02              ; send 2 EOT characters
2779   2F93 A9 04                 LDA #$04
2780   2F95 20 A3 2F              JSR HIC
2781   2F98             ;
2782   2F98 AD 02 17              LDA SBD2              ; turn off cassette 
2783   2F9B 09 08                 ORA #$08              ; by setting bit 3
2784   2F9D 8D 02 17              STA SBD2              ; of the control port
2785   2FA0 4C 8C 1E              JMP INIT              ; reset all ports
2786   2FA3             ;
2787   2FA3             ; subroutine to send X characters to tape
2788   2FA3             ;  
2789   2FA3 86 F1       HIC       STX TIC         
2790   2FA5 48          HICA      PHA
2791   2FA6 20 C6 2F              JSR OUTCHT            ; send the character
2792   2FA9 68                    PLA                   ; and get it back
2793   2FAA C6 F1                 DEC TIC               ; to send again
2794   2FAC D0 F7                 BNE HICA              ; until count = 0
2795   2FAE 60                    RTS
2796   2FAF             ; 
2797   2FAF 20 4C 19    OUTBTC    JSR CHKT
2798   2FB2             ; 
2799   2FB2             ; subroutine to send byte as two ASCII chars
2800   2FB2             ;
2801   2FB2 48          OUTBT     PHA                   ; save byte
2802   2FB3 4A                    LSR A                 ; get upper nybble
2803   2FB4 4A                    LSR A
2804   2FB5 4A                    LSR A
2805   2FB6 4A                    LSR A
2806   2FB7 20 BB 2F              JSR HEXT              ; and send it
2807   2FBA 68                    PLA                   ; return byte and sent lower nybble
2808   2FBB             ;
2809   2FBB             ;  subroutine to send one HEX char as ASCII
2810   2FBB             ;          
2811   2FBB 29 0F       HEXT      AND #$0F              ; cleanup data (lower nybble)
2812   2FBD C9 0A                 CMP #$0A              ; change to ASCII
2813   2FBF 18                    CLC                   
2814   2FC0 30 02                 BMI HEXAT
2815   2FC2 69 07                 ADC #$07              ; 37 to A..F
2816   2FC4 69 30       HEXAT     ADC #$30              ; 30 to 0..9
2817   2FC6             ;
2818   2FC6             ; subroutine to send one 8 bit byte
2819   2FC6             ;
2820   2FC6 A0 08       OUTCHT    LDY #$08              ; eight bit count
2821   2FC8 84 F2                 STY COUNT             
2822   2FCA A0 02       TRY       LDY #$02              ; start at 3600 Hz
2823   2FCC 84 FE                 STY TRIB              ;
2824   2FCE BE FC 2F    ZON       LDX NPUL,Y            ; number if half cycles
2825   2FD1 48                    PHA                   ; save char
2826   2FD2 2C 47 17    ZONA      BIT TIMER             ; wait for end of cycle            
2827   2FD5 10 FB                 BPL ZONA              ; in tight loop
2828   2FD7             ;
2829   2FD7 B9 FD 2F              LDA $TIMG,Y           ; setup timer
2830   2FDA 8D 44 17              STA TIMERD            ; change state of output port
2831   2FDD             ;
2832   2FDD A5 F0                 LDA GANG              ; 
2833   2FDF 49 80                 EOR #$80
2834   2FE1 8D 42 17              STA SBD
2835   2FE4             ;
2836   2FE4 85 F0                 STA GANG              ; save state
2837   2FE6 CA                    DEX                   ; done all cycles?
2838   2FE7 D0 E9                 BNE ZONA
2839   2FE9             ;
2840   2FE9 68                    PLA                   ; retrieve byte
2841   2FEA C6 FE                 DEC TRIB              ; one more gone
2842   2FEC F0 05                 BEQ SETZ              ; the last one too
2843   2FEE 30 07                 BMI ROUT              ; even the last one went
2844   2FF0             ;
2845   2FF0 4A                    LSR A                 ; another bit to the carry
2846   2FF1 90 DB                 BCC ZON               ; if it is not set
2847   2FF3 A0 00       SETZ      LDY #$00              ; switch to 2400 Hz
2848   2FF5 F0 D7                 BEQ ZON               ; always
2849   2FF7             ;
2850   2FF7 C6 F2       ROUT      DEC COUNT             ; one bit sent
2851   2FF9 D0 CF                 BNE TRY               ; but more to go
2852   2FFB 60                    RTS                   ; All over, go home
2853   2FFC             ;
2854   2FFC             ; Timing table
2855   2FFC             ;          
2856   2FFC 02          NPUL      .BYTE $02 ;00000010
2857   2FFD C3          TIMG      .BYTE $C3 ;11000011
2858   2FFE 03                    .BYTE $03 ;00000011
2859   2FFF 7E                    .BYTE $7E
2860   3000             ; If your recorder cannot handle this speed
2861   3000             ; you can slow down by changing NPUL and NPUL2
2862   3000             ; to one of the following        03       04
2863   3000             ;                                06       09
2864   3000             ; This is the KIM ROM speed      0C       12