Saving and Loading Memory on the VIC-20

Saving and loading memory is quite easy on the VIC-20 once you know how. However, it isn't obvious how to do this and therefore this article will present a few simple ways of doing it from BASIC and Assembler.

Saving/Loading from BASIC a Byte at a Time

A straight forward way to save and load memory from BASIC is to use the OPEN, PRINT# and GET# commands to write or read a byte at a time.

Saving

In the following file we are saving memory at 673-692 to 'FLASH.PRG' on device 8. If we wanted to save it to a .SEQ file then we would use FLASH,S,W. We use CHR$ to convert the byte to PETSCII which the PRINT# command will then convert back to its byte representation when writing to the file. One interesting thing to note is that even though this is saving to a .PRG file it doesn't save the load address. If we want this included in the file then we need to start it with two bytes representing the LSB/MSB of the load address.

10 OPEN 8,8,1,"FLASH,W"
20 FOR I=673 TO 692
30 B=PEEK(I)
40 PRINT#8, CHR$(B);
50 NEXT I
60 CLOSE 8

Loading

The following program will load 'FLASH.PRG' into locations 673-692 from device 8.

10 OPEN 8,8,0,"FLASH"
20 FOR I=673 TO 692
30 GET#8, B$
40 POKE I,ASC(B$)
50 NEXT I
60 CLOSE 8

If the file contains the origin address as the first two bytes then we can use that if desired. We have to be careful that the origin address isn't where our tokenized basic program is being stored. This allows us to load a memory block without corrupting a running BASIC program. In the following ST is the status after certain I/O operations and is used here to detect the end of file by checking if it is equal to 64.

10 OPEN 8,8,0,"FLASH,R"
20 GET#8, O1$
30 GET#8, O2$
40 O=ASC(O1$)+256*ASC(O2$)
50 PRINT "ORIGIN: ";O
60 GET#8, B$
70 IF ST<>0 AND ST<>64 GOTO 200
80 POKE O,ASC(B$)
90 O=O+1
100 IF ST<>64 GOTO 60
110 CLOSE 8
120 END
200 PRINT "ERROR STATUS: ";ST
210 CLOSE 8

Using the SAVE/LOAD Commands in BASIC

BASIC has built-in commands to save and load programs which we can use to save and load our block of memory.

Saving

We can use the SAVE command in BASIC to save a block of memory by telling the Vic that the tokenized BASIC area is located at the same block of memory that we want to save.

The following example saves a block of memory at 673-692 to 'FLASH.PRG' on cassette (device 1). The start address is stored at 43/44 ($2B/2C) which indicates the start of tokenized BASIC in LSB/MSB order. The end address is stored at 45/56 ($2D/$2E) which indicates the end of tokenized BASIC in LSB/MSB order.

POKE 44,673/256:POKE 43,673-256*PEEK(44)
POKE 46,692/256:POKE 45,692-256*PEEK(46)
SAVE "FLASH",1,1

This works well but because it alters the BASIC start and end points it can't be used from within a BASIC program. We should reset the Vic or restore these locations to their original values once finished. It should also be noted that this method saves the load/origin address to the first two bytes of the file because the final ,1 is at the end of the SAVE command. On cassette this prevents the relocate function of the LOAD command but doesn't apply to disk.

Loading

If we have saved a block of memory that had a start address and is a self-contained runnable program then we can simply use:

LOAD "FLASH",8,1

SYS to BASIC/KERNEL ROM from BASIC

We can call BASIC/KERNEL ROM routines from BASIC using SYS to save and load blocks of memory in a way which will work within a BASIC program. This method uses the BASIC ROM routine PARSL at 57809 ($E1D1) which sets the LOAD, VERIFY and SAVE parameters and therefore is designed to process the rest of the parameters after it is called. In this case (N$),<device>. Locations 780, 781 and 782 are used to access the 6502 registers: A, X, Y.

From: ROM Calls and Other Tricks on the Denial Forum

Saving

The following method saves memory to the filename in N$. end must be one past the address of the last byte to save. This routine uses the KERNEL ROM routine SAVE at 65496 ($FFD8) to perform the save. It uses register A (location 780) to set the starting address store to 193.

N$="FLASH"
SYS57809(N$),<device>:POKE193,<start_lo>:POKE194,<start_hi>
POKE780,193:POKE781,<end_lo>:POKE782,<end_hi>:SYS65496

If we wanted to save the memory at 673-692 ($02A1-$02B4) to 'FLASH.PRG' on device '8' and store the load address using ,1 it would look like the following. The final ,1 after the device isn't actually used by disk but we may want to use it if saving to cassette (device 1). We can also replace (NS) with the quoted filename:

SYS57809"FLASH",8,1:POKE193,161:POKE194,2
POKE780,193:POKE781,181:POKE782,2:SYS65496

Loading

Loading is equally easy and in this case uses the KERNEL ROM routine LOAD at 65493 ($FFD5) to perform the load. If ,1 after device then it is forced to load at original address and pokes for start_lo and start_hi aren't needed. Register A (location 780) is used to specify a LOAD (0) operation rather than a VERIFY (1) operation.

N$="FLASH"
SYS57809(N$),<device>:POKE780,0:POKE781,<start_lo>:POKE782,<start_hi>:SYS65493

Therefore if we want to load our 'FLASH.PRG' program, saved previously, we can use the following to load it from device 8 into its original memory location:

SYS57809"FLASH",8,1:POKE780,0:SYS65493

Assembly Routines for Saving and Loading

We can use some of the same routines used by the SYS calls above to load and save memory from assembly. We first have to set up the device, file number and secondary address using the SETLFS routine at $FFBA (65466) and then use the SETNAM routine at $FFBD (65469) to set the filename. Finally we call either the LOAD or SAVE routine after setting the start and end addresses appropriately of the memory block to load or save.

Saving

The following routine would save block-blockend from memory to the file 'FLASH.PRG' on drive 8. The secondary address has no effect when being saved to disk but has the same effect as the BASIC SAVE command if saving to cassette. It uses the same SAVE routine at $FFD8 (65496) used above.

; KERNEL/BASIC ROM Routines
SAVE        = $FFD8
SETLFS      = $FFBA
SETNAM      = $FFBD

            LDA  #$08            ; Logical file number
            LDX  #$08            ; Device number
            LDY  #$01            ; Secondary address
            JSR  SETLFS          ; Set above parameters

            LDA  #$05            ; Length of file name
            LDX  #<filename      ; Low byte of file name location
            LDY  #>filename      ; High byte of file name location
            JSR  SETNAM          ; Set the name

            LDA  #<block         ; Low byte of start of memory block
            STA  $C1
            LDA  #>block         ; High byte of start of memory block
            STA  $C2

            LDA  #$C1            ; Pointer to location of start address
            LDX  #<(blockend+1)  ; Low byte of (end of memory block + 1)
            LDY  #>(blockend+1)  ; High byte of (end of memory block + 1)
            JSR  SAVE            ; Perform save
            BCS  error           ; Jump to error handler if error

            RTS

error       ; Handle error in A
            RTS

filename    .asc "FLASH"

            ; The two byte memory block to save
block       .byt $05
blockend    .byt $06

Loading

The following routine would load the file 'FLASH.PRG' from drive 8 to memory at location 673 ($02A1). It uses the same LOAD routine at $FFD5 (65493) used above.

block       = $02A1              ; Load address

; KERNEL/BASIC ROM Routines
LOAD        = $FFD5
SETLFS      = $FFBA
SETNAM      = $FFBD

main        LDA  #$08            ; Logical file number
            LDX  #$08            ; Device number
            LDY  #$00            ; Secondary address
                                 ; $00 Load using specifed address
                                 ; $01 Load using original address from file
            JSR  SETLFS          ; Set above parameters

            LDA  #$05            ; Length of file name
            LDX  #<filename      ; Low byte of file name location
            LDY  #>filename      ; High byte of file name location
            JSR  SETNAM          ; Set the name

            LDA  #$00            ; Load = 0, Verify = 1
            LDX  #<block         ; Low byte of start address
            LDY  #>block         ; High byte of start address
            JSR  LOAD            ; Perform load
            BCS  error           ; Jump to error handler if error

            RTS

error       ; Handle error in A
            RTS

filename    .asc "FLASH"

Machine Language Monitors

To finish this off the last method I want to mention is loading and saving via a machine language monitor such as VICMON.

Saving

We can save in VICMON using the S command. The following example saves the memory at 673-692 ($02A1-$02B4) to 'FLASH.PRG' on device '8'. Note that we are using 02B5 as the end address because the end address for this command has to be one past the last byte that we want to save. The .PRG file will be stored with its origin address.

.S "FLASH",08,02A1,02B5

Loading

To load a file we use the L command which takes an optional device number. If the device number isn't specified then it defaults to 01 - the cassette. In the following example we are loading 'FLASH.PRG' from device 8 into the same memory location that it was saved from.

.L "FLASH",08

Video

The following video shows these methods of saving and loading memory in action.

Creative Commons License
Saving and Loading Memory on the VIC-20 by Lawrence Woodman is licensed under a Creative Commons Attribution 4.0 International License.

Share This Post

Feedback/Discuss

Related Articles

Getting the Address of BASIC Variables on the VIC-20

Getting the address of a BASIC variable can be useful if you want to pass data to a machine code routine or want to access the bytes of a variable directly to improve speed and reduce garbage collectio...   Read More

Storing Machine Code in REM Statements on the VIC-20

BASIC programs often contain machine code routines but they take up quite a lot of space in BASIC. An interesting way to reduce the amount of space that they take is to store the machine code in REM s...   Read More

Adding Basic Stubs to Assembly Language on the Commodore VIC-20

To make machine language programs more friendly it's nice to add a Basic stub which contains a line with a SYS statement to start the code. This is easy to do on the VIC-20 and the process gives you a...   Read More

Basic Line Storage on the VIC-20

BASIC programs are stored in memory using a simple structure that we can investigate and manipulate. This article will show how they are stored and contains a BASIC program to go through each line of ...   Read More

Programming in Assembly with VICMON on the VIC-20

VICMON is a machine language monitor released by Commodore in 1982 and is great for programming the VIC-20. Its interactive nature means that it can often be quicker to develop via this rather than us...   Read More