This program demonstrates Color BASIC’s string garbage collection. As strings are manipulated, string memory used will continue to grow until BASIC decides to clean up the string space and move things around a bit.
The program creates a string array and fills each entry up with string data. Using code that checks how much string space is remaining, it will delete old strings if string memory is low.
Watching it run reveals some curious things…
On an Extended or Disk BASIC system, you will need to do a PCLEAR 1 to get enough memory to run it.
- Line 5 – We CLEAR enough string space to hold 100 lines at their largest size of 255 characters. Note that when you use DIM to set an array size, it is base-0. Doing a DIM A$(99) gives you A$(0) to A$(99) — 100 elements. Thus, the CLEAR uses 100, but the DIM in the next line uses 99.
- Line 10 – MX represents the maximum number of lines in the array.
- Line 20 – DIM A$(MX) creates an array of 0-99 entries (100). AL is set to 0, and will be the line (array entry) we will ADD next. DL will be used to track the next line (array entry) we need to delete. DL is set to -1 for “nothing to delete yet.”
- Main Loop
- Line 30 – SZ is set to the length of the string we want to add. It has an unused RND at the start, but then SZ is hard-coded to 255. The program was designed to test random lengths of strings, but for this demo, we will be overriding that and making every string the maximum size.
- Line 40 – GOSUB 1000 goes to a routine that will ADD a line. Then we GOTO 30 to create a new line and do it again.
- Add New String subroutine
- Line 1010 checks to see if the ADD line has caught up to the DELETE line. If it has, we GOSUB 2000 to handle deleting the delete line.
- Line 1020 – GOSUB 3000 will return the amount of free string space in Z. If Z is less than 104, GOSUB 2000 is called to delete a string.
- Line 1030 – It prints how long of a string is being added to which line, followed by the current free string space before the add.
- Line 1040 – The string is added to the A$ array at position AL.
- Line 1050 – AL (add line) is incremented by 1, moving it to the next line of the array. If AL is larger than the MX (max array entries), it will wrap around to entry 0.
- Line 1070 – Returns.
- Delete Old String subroutine
- Line 2010 – Print which line is about to be deleted (set to “”).
- Line 2020 – The A$ entry at position DL is set to “”, releasing the string memory it was using.
- Line 2030 – DL (delete line) is incremented by one. If DL is larger than MX (max array entries), it will wrap around to entry 0.
- Line 2040 – GOSUB 5000 is a pause routine so we can see what just happened.
- Get Free String Space routine
- Line 3010 – Z is calculated as the difference between the FRETOP (start of string storage) memory location and the STRTAB (start of string variables) in the reserved string area. This gives us how many bytes of unused string memory is available.
- Line 2030 – Returns.
- Pause subroutine
- Line 510 – Print the message “[PAUSE]” with no carriage return at the end.
- Line 5020 – If INKEY$ returns nothing (no key pressed), keep checking.
- Line 5030 – Prints a string of 7 CHR$(8)s (backspace character) which will erase over the “[PAUSE]” prompt.
Based on the intent, one might think that running this would fill strings up to around entry 100, and then it would start deleting the old string
But is that what it will do?
Let’s run it and find out.
5 CLEAR 100*255 10 MX=99 20 DIM A$(MX):AL=0:DL=-1 30 SZ=RND(255):SZ=255 40 A$=STRING$(SZ,"X") 50 GOSUB 1000:GOTO 30 999 END 1000 REM 1001 REM ADD NEW STRING 1002 REM 1005 IF AL=DL THEN GOSUB 2000 1006 GOSUB 3000:IF Z<1024 THEN GOSUB 2000 1010 PRINT "ADDING";LEN(A$)"BYTES AT";AL;Z 1020 A$(AL)=A$ 1030 AL=AL+1:IF AL>MX THEN AL=0 1040 IF DL=-1 THEN DL=0 1050 RETURN 2000 REM 2001 REM DELETE OLD STRING 2002 REM 2010 PRINT ,"DELETING";DL 2020 A$(DL)="" 2030 DL=DL+1:IF DL>MX THEN DL=0 2035 GOSUB5000 2040 RETURN 3000 REM 3001 REM GET FREE STRING SPACE 3002 REM 3010 Z=(PEEK(&H23)*256+PEEK(&H24))-(PEEK(&H21)*256+PEEK(&H22)):RETURN 5000 REM 5001 REM PAUSE 5002 REM 5010 PRINT"[PAUSE]"; 5020 IF INKEY$="" THEN 5020 5030 PRINT STRING$(7,8);:RETURN
Running the Program
After a PCLEAR 1 and RUN, the program starts filling lines and we can see string memory going down. When it gets to line 47, there is only 1275 bytes of string space left.
And if we check those values, the difference between each one isn’t the 255 we might have expected. We clearly added a new 255 byte string, but memory went from 7905 to 7395 (510 bytes less) and continued down to 1785 to 1275 (510 bytes less). It appears each time we add a 255 byte string, it takes 510 bytes of string memory.
As we get to line 47, we must have had less than 1024 bytes of string memory left and the DELETE LINE subroutine is called. It deletes the oldest line, which is currently line 0. That should free up 255 bytes of string memory.
After pressing a key, we see the next few entries:
After deleting 0, memory has gone from 1275 to 756 (5120 bytes), so DELETE is called again. This time it deletes line 1.
We press a key and let it scroll a bit more until the next DELETE:
Here we see that, at some point, some string cleanup was done and our free string space grew larger. The trend of reducing by 510 bytes for each ned 255 byte string added continues…
And the process repeats, until eventually we roll over at line 99.
From here on, things get a bit more predictable, with a DELETE happening almost every line — though sometimes every two lines.
Eventually things settle in to a pattern of basically DELETING for every line ADDED.
Is it broken? Is it just poorly implemented? Or is it behaving exactly like it is supposed to?
Tune in next time for the exciting conclusion…