How Did They Do That? Smooth Scrolling in Family BASIC
So I typed up Star Dust II from the February 1986 edition of Micom BASIC magazine and noticed that it had some fancy background scrolling as your space ship flies around.
The background that you type in is fairly simple, just some random stars and a square vortex thing, but as you move in the game it scrolls around smoothly. This isn't a feature of BASIC, so how are they doing this? Let's take a look at the listing.
10 B=1:C=2:D=3:SPRITEON:O=50:A$="NINTENDO
20 CLS:CGENC:CGSETB,B:LOCATE19,7:PRINT"{K2|K2}":LOCATE8,8:PRINT"STAR DUST {K6|K6}":LOCATE19,9:PRINT"{K1|K1}
25 LOCATE6,15:PRINT"PUSH START BUTTON":GOSUB260:GOSUB370:GOSUB390:FORI=6TO22:COLORI,15,B:NEXT:E=A:F=D
30 PALETB B,A,RND(52),A,A:IFSTRIG(A)<>B GOTO30
40 W=A:GOSUB390:VIEW:DEFSPRITEA,(A,B,A,A,A)="ピプペポ":POKE&H684,&HD5:DEFSPRITEC,(A,B,A,A,A)="XYZ「
50 X=120:Y=A:K=A:L=87:SPRITEA,X,150:SPRITEC,K,L:G=A:H=-12:GOSUB270:V=A:POKE&H326,X:POKE&H32B,Y:CALL&H320:PAUSE99
60 S=STICK(A):IFS ANDB ANDG<12G=G+C
70 IFS ANDC ANDG>-12G=G-C
80 IFX AND4 ANDH<-6H=H+C
90 IFS AND8 ANDH>-12H=H-C
100 X=X+G:Y=Y+H:M=M-G:N=N-H:K=K-G:L=L-H:IFV=B R=R-G:IFR>255ORR<A V=A:SPRITEB
110 IFX>255X=X-255
120 IFX<A X=X+255
130 IFY<A Y=Y+239
140 IFK>255K=K-255
150 IFK<A K=K+255
160 IFL>239L=L-239
170 POKE&H326,X:POKE&H32B,Y:CALL&H320:P=P+(SGN(120-M))*C:Q=Q+(SGN(150-N))*C:M=M+P:N=N+Q:IFM>255ORM<A ORN>239ORN<A GOSUB270
180 SPRITEC,M,N:IFABS(120-M)<12ANDABS(150-N)<12ORABS(120-K)<32ANDABS(150-L)<32POKE&H4000,&HF,&HF4,&HFF,&HB:F=F-B:GOSUB270:IFF<A GOTO280
190 S=STRIG(A):IFS=A GOTO210
200 IFV=A V=B:R=124:U=160:IFS=4 V=C
210 IFV=A GOTO60
220 U=U-30:IFU<A U=A:V=A:SPRITEB:GOTO240
230 SPRITEB,R,U:IFABS(R-M)<12ANDABS(U-N)<20POKE&H4000,&H4F,&HCF,&HFF,&H8:SPRITEB:E=E+B:W=W+B:V=A:IFW<5GOSUB270
240 IFW>9GOTO380
250 GOTO60
260 POKE&H4015,&H17:POKE&H320,&HA9,&H90,&H8D,A,&H20,&HA9,&H78,&H8D,&H5,&H20,&HA9,A,&H8D,&H5,&H20,&H60:RETURN
270 M=K:N=L:P=A:Q=A:RETURN
280 FORI=A TO4:SPRITEI:DEFMOVE(I)=SPRITE(10,I*C,B,25):POSITIONI,120,150:MOVEI:NEXT:PAUSE150:GOSUB390:X=240
290 CLS:CGEND:POKE&H680,&H47,&H41,&H4F,&H56,A,A,A,A,&H4D,&H45,&H45,&H52:GOSUB370
300 X=X-B:SPRITEA,112,X:SPRITEC,128,X:PALETS A,14,X,X,X:IFX>80GOTO300
310 PAUSE99:IFE>O O=E:GOSUB320
315 SPRITEA:SPRITEC:GOTO20
320 LOCATE8,12:PRINT"HIGH SCORE !!":X=12:Y=96:A$="
325 S=STICK(A):Y=Y+(S=C)-(S=B):X=X-(STRIG(A)=4)*(X>12):IFY>180Y=32
330 IFY<32Y=180
340 LOCATEX,15:PRINTCHR$(Y)" ":PAUSE7:IFSTRIG(A)<>8GOTO325
350 X=X+B:BEEP:IFX<22GOTO325
360 FORI=12TO22:A$=A$+SCR$(I,15):NEXT:RETURN
370 LOCATE13,A:PRINT"HIGH "RIGHT$("0000"+MID$(STR$(O),C,4),4)"0 ":LOCATEA,A:PRINT"SCORE "RIGHT$("0000"+MID$(STR$(E),C,4),4)"0":LOCATE13,B:PRINT"NAME "A$:RETURN
380 FORI=A TO4:SPRITEI+B:DEFMOVE(I)=SPRITE(10,I*C,B,25):POSITIONI,M,N:MOVEI:NEXT:E=E+F*10:LOCATE7,14:PRINT"CLEAR BONUS"F*100:GOSUB370:PAUSE300:F=F+B:GOTO40
390 ERAA,B,C,D,4:RETURN
To get an effect like that, the author had to take things to a lower level and write their own assembly routine. You can accomplish this in BASIC by using POKE to write your assembly routine to memory and then using the CALL method to invoke it.
We can unravel what the author has done here by working backwards. We can see the CALL command being used on lines 50 and 170, calling out to a routine at &H320. And if we look for &H320, we can see a POKE to that address on line 260. That must be their assembly routine!
260 POKE&H4015,&H17:POKE&H320,&HA9,&H90,&H8D,A,&H20,&HA9,&H78,&H8D,&H5,&H20,&HA9,A,&H8D,&H5,&H20,&H60:RETURN
Let's break down what the routine is doing. If we write it in a simpler form, the routine in hex looks like this:
a9 90 8d 00 20 a9 78 8d 05 20 a9 00 8d 05 20 60
This is just taking all of their &H values and writing them more plainly. The first argument to POKE is the address you want to start writing to, and any other arguments are the bytes you want to write.
Where Did 'A' Go?
You might notice that we POKE the variable A in two places, but I wrote down 00 instead. Why? The variable A in this listing is never assigned a value, and an unassigned variable will evaluate to 0. Why not just use 0? In Family BASIC v2, using 0 will take up three bytes of memory (one to indicate that it is a base-10 number rather than base-16, then two bytes to store the 16-bit value), whereas the unassigned variable takes up only one byte. Memory is tight, and that saves two bytes each time!
Correction: it was pointed out to me that while you do save two bytes of memory used by the BASIC listing each time you do this, the undefined variable does still get allocated in memory once you run the program, so you start out by losing three bytes. However, if you use this trick three or more times in the listing, it becomes a net memory savings.
Now that we have the hex for our assembly routine, we need to convert it back to the 6502 instructions to see what it does. I used the virtual 6502 Disassembler website to do this.
So paste the hex into the first box, labeled code. It will format it nicely for you after you've pasted it. Enter in 320 for the start address (hex) field (remember, this is the address we passed to the POKE command to write these values). Finally, press the Disassemble button to get our output disassembly.
* = $0320
0320 A9 90 LDA #$90
0322 8D 00 20 STA $2000
0325 A9 78 LDA #$78
0327 8D 05 20 STA $2005
032A A9 00 LDA #$00
032C 8D 05 20 STA $2005
032F 60 RTS
.END
That's not a lot of code, is it? What the heck is it doing? Let me break it down a few different ways. Here it is with some rudimentary comments.
0320 A9 90 LDA #$90 ; A = 0x90
0322 8D 00 20 STA $2000 ; Store A into address $2000
0325 A9 78 LDA #$78 ; A = 0x78
0327 8D 05 20 STA $2005 ; Store A into address $2005
032A A9 00 LDA #$00 ; A = 0x00
032C 8D 05 20 STA $2005 ; Store A into address $2005
032F 60 RTS ; Return
You might be wondering, "What is the point of writing a value to 2005 and then immediately replacing it?". The answer is that we are not storing a value into memory at all. Rather, we are providing instructions to the Famicom hardware by writing to a hardware register. Specifically, we are writing values to the Famicom PPU's PPUSCROLL register. Here's an updated set of comments.
0320 A9 90 LDA #$90 ; A = 0x90
0322 8D 00 20 STA $2000 ; Init PPUCTRL
0325 A9 78 LDA #$78 ; A = 0x78
0327 8D 05 20 STA $2005 ; Update PPUSCROLL X
032A A9 00 LDA #$00 ; A = 0x00
032C 8D 05 20 STA $2005 ; Update PPUSCROLL Y
032F 60 RTS ; Return
So we see that the author's routine is a tiny bit of code that updates the scroll register. This is what is allowing the background to scroll around as we fly about in our space ship.
Why 0x78 and 0x00 for coordinates, though? Those are just defaults. You can see on the start of line 170 that the values of X and Y in the listing get set onto &H326 and &H32B using POKE. This is dynamically updating the assembly routine so that those LDA lines make use of the current X and Y values.
So there you have it. A fancy little assembly routine in a BASIC game, letting it extend beyond the limits of what BASIC provides.
But if you are like me, you might be wondering, "Wait, isn't this from 1986? How did the author even know about the PPUSCROLL register?" Indeed, there was no widespread Famicom hardware manual, and certainly no NESdev Wiki to reference. How on Earth did they know?
I believe I have the answer...but that is a story for another time. Stay tuned!

