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