Yet another version of the 100 door problem; this time, let's extend Commodore BASIC to add PRINT @, shall we?

Yet another version of the 100 door problem; this time, let's extend Commodore BASIC to add PRINT @, shall we?

I stumbled across a ZX Spectrum version of the 100 Door problem that got me thinking. Wouldn't it be nice to have PRINT AT in Commodore BASIC?

We do this in Assembly all the time.

We already know from several previous articles that moving the cursor is pretty straightforward using a Kernal routine:

Load the row number in X, the column number to Y, clear carry, and jump to $fff0. Simple.

ldx #08 ;row
ldy #08 ;column
clc
jsr $fff0 

We can do this easily with pokes, of course. The X register is at 781, the Y register is at 782, and we need to clear carry, which we can do by POKING the status register with a 0 at 783.


10 POKE 781,8:POKE 782,8:POKE 783,0:SYS 65520:PRINT"HOWDY LINE 8 COL 8"
0:00
/

Easy enough. And for many applications, probably fast enough. But we probably want to do this in Machine Language/Assembly for some speed, right?

There are many examples I've run across that outline how to get parameters into the SYS command. The Tool Kit BASIC book on p.9-11 gives a complete example of this, including syntax checking for a comma and storing the next token... hmm.

A few pages later, the same book gives an example of reading USR() parameters.  

That's interesting. Let's look at Mapping the C64/VIC for $B79E...

Huh. $B79B does look like what we want here. So what's $B79E?

Okay, what's $73?

If we go back to Tool Kit BASIC p.377 to look at how SYS works.

So if it leaves the text pointer at the comma, does that mean we can use GETBYTC to get the next parameters? Let's try it:

		*=828
	
    	jsr $B79B ; or $D79B on VIC-20
    	txa		  ; save this for later
    	pha
    	jsr $B79B ; or $D79B on VIC-20
        pla
        tax
        ldy $65   ; Y should now be in the low byte of FAC1
        clc
        jsr $fff0
        brk
    
    

This is the third or fourth version of this I tried. I stored things in zero page a couple of times, and then I saw in the Monitor that Y ended up in the FAC1 low byte and I could save stashing things in different places. I suspect there are about 100 ways to do this.

Let's load this into BASIC and try it out:

VIC-20 Version


0 DATA 32,155,215,138,72,32,155,215,104,170,164,101,24,76,240,255
10 FORI=828TO843:READX:POKEI,X:NEXT

C64 Version


0 DATA 32,155,183,138,72,32,155,183,104,170,164,101,24,76,240,255
10 FORI=828TO843:READX:POKEI,X:NEXT
0:00
/

100 Doors with PRINT @

We've done this at length before on a bunch of systems, but let's use our new "PRINT AT" to do another fun visual.


0 REM 100 DOOR USING PRINT @ C64
5 DATA 32,155,183,138,72,32,155,183,104,170,164,101,24,76,240,255
10 FORI=828TO843:READX:POKEI,X:NEXT
20 DIM D(100)
22 PRINTCHR$(147)
25 GOSUB 170
30 FOR A=1 TO 100
35 SYS 828,0,0:PRINT"STEP:"MID$(STR$(A),2);
40 FOR B=A TO 100 STEP A
45 SYS 828,0,10:PRINT"DOOR:"MID$(STR$(B),2,3)" "
50 D(B)=NOT D(B)
55 GOSUB 150
60 NEXT B
70 NEXT A
80 GOSUB 170
85 SYS 828,22,0:PRINT"OPEN:"MID$(STR$(OP),2)" ";
90 END 
150 REM PRINT DOOR STATUS
151 P=(B-1)/10
152 Q=B-INT(B/10)*10:IFQ=0THENQ=10
153 P=INT(P)
154 OP=OP+(D(B)=1)-(D(B)=0)
156 SYS 828,2*P+2,2*Q:PRINTMID$(STR$(D(B)),2);
160 RETURN 
165 REM PRINT STEP STATUS
170 OP=0
175 FOR P=0 TO 9
180 FOR Q=1 TO 10
185 SYS 828,2*P+2,2*Q:PRINTMID$(STR$(D(P*10+Q)),2,1);
188 OP=OP+D(P*10+Q)
190 NEXT Q
200 NEXT P
210 RETURN

On a VIC-20, replace line 5 with:


5 DATA 32,155,215,138,72,32,155,215,104,170,164,101,24,76,240,255
0:00
/
PAL VIC20 vs NTSC Commodore 64

PET Version

This will actually work on all of the BASIC versions, it's just slower. I couldn't quite figure out the ROM routine for entry like I found on the VIC-20 and C64. If you know how do to this, please let me know.


0 REM 100 DOOR USING PRINT @ PET
20 DIM D(100)
22 PRINTCHR$(147)
25 GOSUB 170
30 FOR A=1 TO 100
35 V=0:H=0:GOSUB1000:PRINT"STEP:"MID$(STR$(A),2);
40 FOR B=A TO 100 STEP A
45 V=0:H=10:GOSUB1000:PRINT"DOOR:"MID$(STR$(B),2,3)" "
50 D(B)=NOT D(B)
55 GOSUB 150
60 NEXT B
70 NEXT A
80 GOSUB 170
85 V=22:H=0:GOSUB1000:PRINT"OPEN:"MID$(STR$(OP),2)" ";
90 END 
150 REM PRINT DOOR STATUS
151 P=(B-1)/10
152 Q=B-INT(B/10)*10:IFQ=0THENQ=10
153 P=INT(P)
154 OP=OP+(D(B)=1)-(D(B)=0)
156 V=2*P+2:H=2*Q:GOSUB1000:PRINTMID$(STR$(D(B)),2);
160 RETURN 
165 REM PRINT STEP STATUS
170 OP=0
175 FOR P=0 TO 9
180 FOR Q=1 TO 10
185 V=2*P+2:H=2*Q:GOSUB1000:PRINTMID$(STR$(D(P*10+Q)),2,1);
188 OP=OP+D(P*10+Q)
190 NEXT Q
200 NEXT P
210 RETURN
1000 PRINT "[HOME]";:FORJ=0TOH:PRINT"[RIGHT]";:NEXT:FORJ=0TOV:PRINT"[DOWN]";:NEXT:RETURN
0:00
/

Have fun!

UPDATE 5/4/2022, an hour after publishing: It turns out that this EXACT Machine Language implementation is in Programming the C64 by Raeto Collin West in the Extending BASIC chapter. It could have saved me some time, but I'm thrilled that I came up with the same answer as him on my own.