Qwikload/Save For VIC And 64
Richard L. Witkover
Here is a BASIC program which can drastically reduce your waiting time when loading or saving large blocks of data.
Have you ever sat staring at your television set while saving or loading large blocks of data? If you use the GET# or INPUT# commands, chances are you have. It may have taken only a few minutes, but it seems like forever. You can do it ten times faster by using the Kernal routines built into your Commodore computer.
First, you must tell the Kernal routine where to load the data, or what section of memory to save. The Kernal looks for this information in the microprocessor's internal registers. These registers can be accessed from BASIC via memory locations 780–782. The SYS command transfers the contents of these locations into the registers before it jumps to the machine language routine. Location 780 corresponds to the accumulator, 781 to the X register, and 782 to the Y register. So, all we need to do is POKE the information into these locations and SYS to the Kernal routine. "Qwikload/Save" uses this technique to access the Kernal routines SETLFS, SETNAM, LOAD, and SAVE.
Qwikload/Save allows you to save any section of memory in the 64 and up to location 32766 ($7FFE) in the VIC. Any files saved by Qwikload/Save can also be loaded by the program into any area in RAM. Either tape or disk may be used. Just type in the program, SAVE it, and RUN it.
How It Works
Lines 100–110 ask the user whether to use tape or disk and store the answer in B$. Lines 120–130 ask whether to save or load, storing the answer in A$. Line 140 INPUTs the filename and stores it in F$. Line 144 INPUTs the starting address of the block to be saved or loaded. Then the high byte (AH) and low byte (AL) of the starting address are calculated. Line 147 branches to line 500 if disk was chosen. Line 150 branches to line 300 if load was chosen.
Lines 160–200 save a block of memory to tape. Line 160 INPUTs the end address of the block and calculates the high byte (BH) and low byte (BL). Line 190 jumps to the subroutine at line 400 to open a file. Then the high byte and low byte of the starting address are POKEd into zero page. Line 200 POKEs location 780 (accumulator) with the zero-page address of the low byte used in line 190. This creates a pointer which tells the computer where to find the starting address. The low byte of the end address is POKEd into location 781 (X register) and the high byte into location 782 (Y register). Then the block is saved by SYSing to the SAVE routine in the Kernal. The file is then closed by jumping to line 330.
Lines 300–330 load a file from tape. Line 300 opens the file. Line 310 specifies a LOAD by POKEing 0 into location 780 (0 = LOAD; 1 = VERIFY). The low byte and high byte of the starting address are POKEd into locations 781 and 782, respectively. Then the file is loaded by SYSing to the LOAD routine. Line 320 checks bits 4 and 5 of the STATUS variable. If either bit is set, the file was not loaded correctly and the message ?LOAD ERROR is printed. Line 330 closes the file and ends the program.
Lines 400–440 comprise a subroutine which opens a file to the cassette recorder similar to the BASIC command OPEN 1,1,0,F$. Line 400 POKEs the length of the filename into location 183. The end-of-arrays pointer is calculated and stored in S. Line 410 POKEs the filename into the free RAM area just above the arrays. Line 420 sets up the logical file by POKEing the file number into location 780, POKEing the device number into location 781, POKEing the secondary address into location 782, and SYSing to the SETLFS routine. Line 430 sets up the filename by POKEing the filename length into location 780, POKEing the low byte of the end-of-arrays pointer into location 781, POKEing the high byte into location 782, and SYSing to the SETNAM routine. Line 440 turns on the tape messages (SEARCHING, FOUND, etc.) by setting bit 7 of location 157.
Line 500 branches to line 700 if a disk load is chosen. Lines 530–660 save a block of memory to disk. Line 530 INPUTs the end address, adds 1 to it, and calculates the low byte and high byte. It is necessary to add 1 to the end address in order to save the last byte of the block. Line 540 OPENs the disk error channel. Line 550 OPENs a program file for writing. Line 560 checks for errors by reading the error channel. Line 570 branches to line 650 if no error occurs. Line 580 prints the error information and jumps to line 760 to end the program if the error number is not 63 (FILE EXISTS ERROR).
Lines 590–610 ask whether the user wants to replace the file on disk with the new file. If not, the program ends by jumping to line 760. Otherwise, the file is replaced by scratching the file on disk and saving the new file. Line 620 scratches the old file. Line 630 returns to line 540 to save the new file. Lines 650–660 save the file by POKEing the starting and ending addresses and SYSing to the SAVE routine.
Lines 700–760 load a file from disk. Line 700 OPENs the error channel. Line 710 OPENs the program file for reading. Line 720 reads the error channel. If any error occurs, line 730 prints the error information and ends the program. Line 750 enables a relocatable load by POKEing a 0 into location 185. Then the file is loaded by POKEing the necessary information and SYSing to the LOAD routine as described for tape. Line 760 closes the files and ends the program.
Qwikload/Save For VIC And 64
Refer to the "Automatic Proofreader" article before typing this program in.
100 PRINT "{CLR}TAPE OR DISK (T/D)?"; :rem 125 105 GETB$ : IFB$ = ""THEN105 :rem 81 110 IFB$<> "T"ANDB$<> "D"THEN105 :rem 153 120 PRINTB$ : PRINT"{DOWN} SAVE OR LOAD (S/L)?"; :rem 43 125 GETA$ : IFA$ = "" THEN125 :rem 83 130 IFA$ <> "S" ANDA$ <> "L"THEN 125 :rem 162 140 PRINTA$ : INPUT"{DOWN}FILENAME";F$ :rem 140 144 INPUT"{DOWN} STARTING ADDRESS" ;X : AH = INT(X/256) : AL = X - AH * 256 :rem l88 147 IFB$ = "D"THEN500 :rem 26 150 IFA$ = "L" THEN300 :rem 25 159 REM TAPE SAVE :rem 220 160 INPUT"{DOWN} END ADDRESS" ;X : X = X + 1 : BH = INT(X/256) : BL = X - BH * 256 :rem l71 190 GOSUB400 : POKE251, AL : POKE252, AH :rem 31 200 POKE780, 251 : POKE781, BL : POKE782, BH : SYS 65496 : GOTO330 :rem 247 299 REM TAPE LOAD :rem 210 300 GOSUB400 :rem 167 310 POKE 780, 0 : POKE781, AL : POKE782, AH : SYS65493 :rem 131 320 IF(ST AND 48) THEN PRINT"{DOWN}? LOAD" : PRINT"ERROR" :rem 96 330 CLOSE1 : END :rem 78 399 REM OPEN TAPE CHANNEL :rem 222 400 L = LEN(F$) : POKE183, L : S = 256 * PEEK(50) + PEEK(49) :rem 174 410 FORX = 1TOL : POKES + X - 1, ASC(MID$(F$, X, 1)): NEXT :rem 53 420 POKE780, 1 : POKE781, 1 : POKE782, 0 : SYS65466 :rem 209 430 POKE780, L : POKE781, PEEK(49) : POKE782, PEEK(50) : SYS65469 :rem 77 440 POKE157, 128 :RETURN :rem 69 500 IF A$ = "L"THEN 700 :rem 28 529 REM DISK SAVE :rem 222 530 INPUT "{DOWN} END ADDRESS" ; X : X = X + 1 : BH = INT(X/256) : BL = X - BH * 256 :rem 172 540 OPEN15, 8, 15, "I0" :rem 16 550 OPEN 3, 8, 1, "0:" + F$ + ", P, W" :rem 157 560 INPUT # 15, EN, EM$, ET, ES :rem 222 570 IFEN = 0 THEN650 :rem 245 580 IFEN <> 63 THENPRINTEN ; EM$ ; ET ; ES : GOTO 760 :rem 153 590 PRINT "FILE EXISTS. {2 SPACES} REPLACE (Y/N)?" ; :rem 58 600 GETA$ : IFA$ = "" THEN600 :rem 79 610 PRINTA$ : IFA$ <> "Y" THEN760 :rem 154 620 PRINT#15, "S0 : " + F$ + ", P, W" :rem 222 630 CLOSE15 : CLOSE 3 : GOTO540 :rem 100 650 POKE157, 128 : POKE 251, AL : POKE252, AH :rem 159 660 POKE780, 251 : POKE781, BL : POKE 782, BH : SYS 65496 : GOTO760 :rem 8 699 REM DISK LOAD :rem 215 700 OPEN15, 8, 15, "I0" :rem 14 710 OPEN3, 8, 0, "0:" + F$ + ", P, R" :rem 149 720 INPUT# 15, EN, EM$, ET, ES :rem 220 730 IFENTHENPRINTEN ; EM$ ; ET ; ES : GOTO760 :rem 179 750 POKE157, 128 : POKE 185, 0 : POKE 780, 0 : POKE 7 81, AL : POKE782, AH : SYS65493 :rem 187 760 CLOSE3 : CLOSE15 : END :rem 109