See Also: part 1 and part 2 (coming soon).
Updates:
- 2026-05-03 – Corrected hex value of 64 (thanks MiaM).
Today I was tagged in a Facebook post by MC-10 (well, and CoCo) programmer, Jim Gerrie. He shared a snipped of code he was trying to get working. The concept was to have stuff on the 32 column text screen get copied into a normal string and then be able to PRINT it back.
Jim’s post (with the code that wasn’t working) with code for the MC-10 was this:
10 CLEAR1200:DIMJ,K,A$,B$:GOSUB100
20 A$=””:K=VARPTR(A$):POKEK,255:POKEK+1,0:POKEK+2,64:B$=A$
50 CLS:PRINTB$;
60 GOTO60
100 CLS1:PRINT”THIS IS LINE ONE”
110 PRINT”THIS IS LINE TWO”
112 PRINT”THIS IS LINE THREE”
113 PRINT”THIS IS LINE FOUR”
114 PRINT”THIS IS LINE FIVE”
115 PRINT”THIS IS LINE SIX”
116 PRINT”THIS IS LINE SEVEN”
117 PRINT”THIS IS LINE EIGHT”:RETURNCan someone explain why this program doesn’t work on my TRS-80 MC-10?! It should reassign the memory pointer of string variable A$ to the beginning of screen memory (highbyte 64 lowbyte 0) so that I can then just assign A$ to B$, which will allow me to “capture” the first 255 bytes of screen mem.
It works on the TRS-80 MODEL I/III (using its screen start at highbyte 60 lowbyte 0)!
Any help greatly appreciated.
– Jim Gerrie in the Facebook TRS-80 MC-10 Group.
I could immediately see what the program was attempting to do, and it was something that never occurred to me to try. The concept is “simple” now that I see it:
- Stuff is placed on the screen (CLS, PRINT, etc.)
- A string (A$) is declared (line 20) and then VARPTR is used to get the memory location of the 5-byte string descriptor for that string. At this point, A$ is zero bytes long, but it will point to somewhere inside the program memory just after the first quote in A=”” because that is where the string begins (even if it is zero bytes long).
- The string descriptor is modified using POKE to change the length of the string to 255 bytes, then the start location of the string from that location inside program memory to be the start of the text screen. That was the first bug. The location being POKEd was off by one and was not modifying the string start address properly.
- After this, A$ is copied into a normal string, thus saving the contents of the first string (screen memory) into the new string (in normal reserved string memory). This is where the second bug was.
Before continuing, If you need a refreshed on VARPTR, start with this article. It will show how the five byte string descriptor is used. Here is a refresh:
STRING DESCRIPTOR (5 BYTES)
0 - LENGTH OF STRING
1 - NOT USED FOR STRINGS
2 - MSB OF ADDR OF STRING IN MEMORY
3 - LSB OF ADDR OF STRING IN MEMORY
5 - ALWAYS 0 FOR A STRING
The first POKE at the address returned by VARTPR (K) was fine, setting the length to 255. But the next two pokes were at K+1 and K+2. For Color BASIC, they should have been at K+2 and K+3. Also, the values being poked were 0 and 64, which is backwards. From a quick search, the MC-10s text screen starts at the 16K mark, $4000 (16384). To verify this, I went to the online MC-10 emulator here:
…and then did POKE 16384,42. That indeed placed an inverted “*” in the top left of the text screen.
The MC-10 is a big endian processor, so the memory location should be MSB ($40) then LSB ($00). $40 in decimal is 64 in decimal (no HEX support on the MC-10, I don’t think). So the actual pokes to make a string start at the top left corner of the text screen should have been 64 and 0 rather than 0 and 64. (That is 64*256+0 to make 16384.)
Adjusting those POKEs to be at the proper spot in VARPTR and swapping the values to MSB/LSB was the first fix.
At that point, I still wasn’t getting it working. This was due to the initial string being a “hard coded” string in BASIC. When A$=”” was declared in BASIC, it made a string that pointed into the program space. I have not looked into why, but forcing the string to be in RAM by doing A$=””+”” was all I needed to change to make this work. (NOTE TO SELF: Explore this and understand what was different about the program-space string.)
Jim posted a corrected version for the MC-10:
0 CLEAR1200:DIMC1,M$,I$,AA$,BB$:GOSUB100:GOTO20
8 M$="":C1=VARPTR(M$):POKEC1,255:POKEC1+2,64:POKEC1+3,0:AA$=M$+""
9 M$="":C1=VARPTR(M$):POKEC1,255:POKEC1+2,65:POKEC1+3,0:BB$=M$+"":RETURN
20 GOSUB8:CLS:PRINTAA$" "BB$;
60 GOTO60
100 CLS8:PRINT"THIS IS LINE ONE"
110 PRINT"THIS IS LINE TWO"
112 PRINT"THIS IS LINE THREE"
113 PRINT"THIS IS LINE FOUR"
114 PRINT"THIS IS LINE FIVE"
115 PRINT"THIS IS LINE SIX"
116 PRINT"THIS IS LINE SEVEN"
117 PRINT"THIS IS LINE EIGHT"
118 PRINT"THIS IS LINE NINE"
119 PRINT"THIS IS LINE TEN"
120 PRINT"THIS IS LINE ELEVEN"
121 PRINT"THIS IS LINE TWELVE"
122 PRINT"THIS IS LINE THIRTEEN"
123 PRINT"THIS IS LINE FOURTEEN"
124 PRINT"THIS IS LINE FIFTEEN"
125 PRINT"THIS IS LINE SIXTEEN";:RETURN
Meanwhile, I had come up with a silly program that would create some kind of image on the CoCo screen, then capture it in two strings so it could be quickly restored later. Well, almost the entire screen — a string is limited to 255 bytes so two strings captures 510 bytes of the 32×16 screen. If one were to use this trick, it could be adjusted to capture just the number of lines on the screen needed (like, the first 5 lines, or lines 10-20, etc.).
My example looked like this:
0 'SAVESCR1.BAS
10 CLEAR511:DIMS1$,S2$
11 ' 0 = LEN OF STRING
12 ' 1 = NOT USED FOR STRING
13 ' 2 = MSB OF ADDRESS
14 ' 3 = LSB OF ADDRESS
15 ' 4 = ALWAYS 0
16 ' 1024 = 4*256+0
17 ' 1279 = 4*256+255
20 REM DRAW SCREEN
25 CLS0:C=0:FOR I=0 TO 29 STEP 2
30 SET(I*2,0,C):SET(63-I*2,30,C)
35 SET(0,30-I,C):SET(63,I,C)
40 C=C+1:IF C>7 THEN C=0
50 NEXT
55 PRINT@266,"THIS";CHR$(128)"IS";CHR$(128);"COOL";
60 'SAVE SCREEN
65 GOSUB 1000
70 'WAIT FOR KEY
75 GOSUB 5000
80 'CLEAR SCREEN
85 CLS 5
90 'WAIT FOR KEY
95 GOSUB 5000
100 'RESTORE SCREEN
105 GOSUB 2000
110 GOTO 70
999 GOTO 999
1000 ' SAVE SCREEN
1005 Z$="":K=VARPTR(Z$):POKEK,255:POKEK+2,4:POKEK+3,0:S1$=Z$+""
1010 Z$="":K=VARPTR(Z$):POKEK,255:POKEK+2,4:POKEK+3,255:S2$=Z$+""
1015 RETURN
2000 ' RESTORE SCREEN
2005 PRINT@0,S1$;S2$;:RETURN
5000 ' WAIT FOR KEY
5005 IF INKEY$="" THEN 5005
5010 RETURN
I made a “save screen” subroutine at line 1000. My program starts and makes a simple splash screen (colored pixels set around the screen and a text message in the center), then calls the save screen routine which makes a temporary Z$ then modifies it to point to the start of the text screen (1024 – so values 4*256+0) and be 255 bytes long. It then copies that string to S1$. It repeats the process for the next part of the screen, which begins at 1024+255 (1279, so 4*256+255). That is saved in S2$.
Then the main program waits for a keypress, then does a CLS 5 to erase the screen to white, then after another keypress, it calls the “restore screen” subroutine which just prints the two saved strings starting at position 0 on the top left corner of the screen.
AND IT WORKS!
Now blasting bytes to the screen can be as fast as printing a string. I can think of some interesting uses for this, such as “drawing” various levels slowly during initialization, and capturing them to strings so they can be displayed later very fast.
And that gives me an idea for an old project I was playing with some years ago.
But that will have to wait for the next installment…

When you do
A$="A STRING"the BASIC interpreter sets the pointer to the string to the literal string in the source code. It does this to save space in the string pool (the bit of memory set aside when you doCLEAR 200). You can also point a string to bits of ROM as well with no ill effect). The string pool is used when strings are constructed via operators like+or functions likeLEFT$orMID$. Also, the string pool is the only area of memory that is subject to garbage collection. It’s a simple method, but it is not fast.You could also save the entire screen in four variables, each 128 characters long.
I did some checking so I could understand the second bug where +”” was needed. Now that I have done this, “it is obvious.” I love those :)
10 A=”HELLO”:B$=A$
Both will point to the same 5 bytes in the program space. That makes perfect sense, since that data does not live in reserved string space.
But if created in string space and cloned:
A$=”HELLO:B$=A$
…B$ becomes its own second copy of those five bytes. “It is obvious.”
Now I am curious and want to look up: What method do the ROMs use to make sure they skip these program-allocated strings? All variables are in the same variable table (assumption) regardless of where the data lives. There must be a check against the start of string space (ignore anything lower) or something else simple like that.
I love an excuse to dig into the ROM disassemblies…
The ROMs do check if the string is in the string pool during garbage collection. In fact, the algorithm for the GC is: go through all variables, and for each string variable, find the one that exists highest in the string pool, and then move it to the top of the string pool. Then go through all the variables and find the next string variable that is highest in the string pool, then move that. Repeat until all strings in the string pool have been moved. If there is still no space, then trigger an error.
Easy, but not fast at all.
And in there is a check for the start or strong space?
Interesting!
Re string in program space v.s. variable space: I would think that a string in program space is treated as a constant so when copying it to another string, the second string also ends up just being a pointer to the same constant.
I.E. instead of having to do A$=””+”” you could probably had done B$=A$+”” (or something similar). Otherwise B$ would probably just also point to the screen. It kind of “feels safer” to do it this way as it seems like a good idea to have A$ act like a constant as you’d never risk changing the screen content by modifying the content of A$.
Btw, a english-as-a-second-language-reader question: “The MC-10 is a big endian processor” – should this perhaps rather either say “has a big endian processor” or “is a big endian computer”?
Also right after I think you made two typos right after each other: “$C0 in decimal is 64 in decimal”. $40 rather than $C0, and hex rather than dec.
I wonder how end-of-string detection and whatnot would work? I’ve never looked into how string lengths are handled in Microsoft Basic, but I’ve noticed that ASC(“”) returns the same 0 as ASC of an actual null character. Thus when using GET you kind of need to also check the LEN of the string. The only actual real life case where I think this really needs to be checked is when using GET for serial communications, to determine if a ascii 0 character has been received or no character at all has been received. Not sure if/how the CoCo (and/or MC10) does this, but on the Commodore 8-bit computers starting with the VIC 20 there is software UART emulation that can be used from Basic (device #2).
Ooops, Sean beat me to it while I was typing!
You are correct. I should have said “The MC-10 is a big endian machine” or “The MC-10 has a big endian processor” or similar. What is your primary language? I would not have guessed it was not English.
A$=”” and then B$=A$+”” makes sense. I did that in my sample:
1000 ‘ SAVE SCREEN
1005 Z$=””:K=VARPTR(Z$):POKEK,255:POKEK+2,4:POKEK+3,0:S1$=Z$+””
1010 Z$=””:K=VARPTR(Z$):POKEK,255:POKEK+2,4:POKEK+3,255:S2$=Z$+””
1015 RETURN
I will correct my mistake on the hex values! Thanks to both of you.
Also, ASC(“”) returning 0 does not surprise me. I guess -1 would be a better “error” code. But INSTR has a similar behavior. If no match is found, it returns 0. But then it returns 1 (!!!) if you try to match an empty string.
PRINT INSTR(“ABC”,”B”) – returns 2
PRINT INSTR(“ABC”,”X”) – returns 0
PRINT INSTR(“ABC”,”A”) – returns 1
PRINT INSTR(“ABC”,””) – also returns 1
Grrr. When I ran into this — trying to pass INKEY$ directly into it (years ago, here) — folks told me that this even happened with Visual BASIC and such. I guess it is intentional. But why?
My native language is Swedish. I.E. one of the other Germanic languages (and also where subtitles are used rather than dubbing for TV shows and movies (except for things for kids), I.E. every Germanic language country except Germany).
I’ve also rented out a room for a few months about a decade ago to a couple where one of them had moved from an English speaking country. It only took a few weeks before I unintentionally started thinking in English rather than in Swedish. :)
Are you in Sweden? (As I show my ignorance of where languages are spoken.)