It started with a video by 8-Bit Show And Tell showing 10 rarely used Commodore 64 BASIC features. As the title suggests, the author goes through commands that don’t get much attention in Commodore BASIC. The one that I’ve never used before was the USR() function.

It was a command that I never really understood and the Commodore 64 Programmers reference manual didn’t really shed much light on the subject. I also never found myself in a position where I couldn’t do what I needed to do that would warrent using USR(). BASIC gave me a set of tools and I worked within the limitations of those tools. I definitely wished there were some added tools available, but never made the connection that I could make my own tools using USR().

After I saw the video, I started playing with the command a little bit and found some helpful BASIC machine language routines to help the program utilize them. The first is located at $B1AA which converts a floating point number currently in BASIC’s FAC1 to a signed 16bit integer with the high byte put into the accumulator and the low byte put into the y register. Further reading into this routine indicates that it was built specifically to be used for passing data from the USR() fuction to the programmers machine language routine. It is never called from within BASIC.

Likewise there is another BASIC routine at $B391 that converts a signed 16bit integer to a floating point number and stores it in FAC1 for returning to BASIC. When you have a command like:

A=USR(10)

JSR $B1AA will pull the 10 into your machine language program and JMP $B391 will return a value to A while returning control back to BASIC.

When looking in the book “Mapping the Commodore 64” from Compute! books, it talks about not going directly to routines written in BASIC because BASIC may change, breaking you USR() program (an issue I don’t think concerns any of us these days). In antisipation of that, BASIC has set up two vectors on zero page that point to these two routines. Address 3 and 4 point to $B1AA and 5 and 6 point to $B391. Since that isn’t much of a worry these days, it frees up 5 bytes of zero page space (2 is already unused) for use by the programmer.

Here is a simple utilization of the USR() function. It will take the number you pass to it and change the screen and border color to that color. It will then return the number to A.

// Simple USR() to change screen and border color together

/* When using $b1aa, BASIC will grab the float out of fac1
and turn it into a signed integer with the hi byte stored in
the Accumulator and the lo byte stored in the Y register.*/

* = $c000 "USR Function"

Start:					// USR()  data starts as float in fac1
	jsr $b1aa			// grab float from fac1 convert to int
	sty $d020			// stor lo byte if interger to border color 
	sty $d021 			// and screen color
	jmp $b391			// return int to fac1 and return to BASIC

The only thing left to do is set the pointer at memory locations 785 and 786 to point to our machine language program at 49152 and run the command when needed. The simple BASIC program below loads the program into memory, changes the pointer for the USR() function and then goes through changing the screen/border color through all sixteen colors.

100 rem --simple usr()--
110 forn=49152to49163
120 reada:poken,a
130 nextn
140 rem --set usr pointer--
145 rem lo/hi byte of 49152
150 poke785,0:poke786,192
200 rem -- use usr --
205 print"hit any key to advance"
210 forn=0to15
220 a=usr(n)
230 print"color"a
240 poke198,0:wait198,1:geta$
250 nextn
260 end
500 data 32,170,177,140,32,208,140
502 data 33,208,76,145,179

So after using the command for a while and playing with different things I could do (some of which will end up in future programs), I wondered if there was a way to pass multiple variable across into my machine language routines, at least two. After some more research, I discovered there is!

Two Variable Pass in USR().

It actually looks like this:

A=USR(1)2

I admit, it’s a little wierd to look at, but if it bothers you, you can always go with:

A=USR(1)(2)

I came across it while researching USR() online. I found an article by Peter Karlsson that was published in Go64/Commodore World in December of 1999. In the article Peter talks about a previous article where the author mentioned a lack of a modulo operator in Commodore BASIC which calculates the remainder of an integer division.

Peter talks about how it could be done in BASIC, but the solutions are slow. The solution to the speed issue would be to write a program in assemble but the hurdle is how to pass the arguments and how to get the result. This is where USR() comes in. Normally, you can only pass one argument in the USR() function, but Peter describes his solution to this:

“To get the second argument (the denominator) we first call the MOV2F routine to move the input argument out to memory, in the BASIC temporary storage area, addresses 87-91 (hex 57-5B). We then call the FRMNUM routine, which loads the next numeric argument in the BASIC source code and puts it in FAC1.”

What he’s saying basically is that when you run a USR() function, the argument in the parentheses is stored in BASIC’s fac1 location (memory 97-102) as a floating point number. He relocates the data to a different memory location that BASIC uses as temporary storage (87-91) using the MOV2f routine which is a BASIC routine located at $BBC7. He then runs another basic routine called FRMNUM which is located at $AD8A. FRMNUM will grab the next argument, make sure it’s the right type (number/string) and place the argument in BASIC’s fac1 which he then uses for his calculations.

In Peter’s routine, he is using the floating point math routines in BASIC so it makes sense to keep the data in floating point format. My first version of using this method used all floats as well but I changed it to my own integer math shortly afterwards.

For my routine I started by using the BASIC routine for creating an integer from FAC1 then running the FRMNUM routine described by Peter to get the second argument and turned that into another signed integer to use in my own program. Unlike my last example, we use a short machine language routine to set the pointer up at 785 and 786 ($0311, $0312) and use the SYS statement at the beginning of the BASIC program to set it up.

//  USR(x)y to return screen poke memory location
//  pulling two parameters for the function
//  Michael Cassera 2020
//  Kick Assembler


.label frmnum= $ad8a	//get and evaluate data for type mismatch
.label usradd= $0311	//pointer for USR funtion for ml
.label fac2ya= $b1aa	//fac to int in y(lo) a(hi)
.label givayf= $b391	//int y(lo) a(hi) to fac
.label err   = $a437	//BASIC error handling routine

* = $c000 "USR Function"

Setup:
	lda #<Start			// get the start address of our USR routine
	ldy #>Start			// lo and hi
	sta usradd			// put the address in location 785 & 786
	sty usradd+1		        // so BASIC knows what to do with USR
	rts				// and return to BASIC
	
Start:					// USR()  data starts as float in fac1
	jsr fac2ya			// grab float from fac1 and
	sty xpos			// convert to 16bit signed integer with 
	sta xpos+1			// lo in .y and hi in .a. Store in memory
	
	jsr frmnum			// grab data after USR()  and put in fac1
	
	jsr fac2ya			// grab float from fac1 and
	sty ypos			// convert to 16bit signed integer with 
	sty ypos+2			// lo in .y and hi in .a. Store in memory
	sta ypos+1			
	sta ypos+3			// two sets of y data because of math after	
					// error check
	
ErrorCheck:
	lda xpos			//checks to make sure 0<=x<40
	cmp #$28		
	bcs Error			
	lda ypos			// check to make sure 0<=y<25
	cmp #$19
	bcs Error	
	
Math:					//y*40 breaks down to (y*32)+(y*8)	
	ldx #$05			
	clc
l1:					//l1 does left shift 5 times
	asl ypos			//to multiply y*32
	rol ypos+1
	dex
	bne l1
	
	ldx #$03
	clc
l2:					//l2 does left shift 3 times
	asl ypos+2			//to multiply y*8
	rol ypos+3
	dex
	bne l2
	
	clc
	lda ypos			//add results of two loops
	adc ypos+2			//(y*32)+(y*8)
	sta ypos
	lda ypos+1
	adc ypos+3
	sta ypos+1	
	clc 
	lda xpos			//add x to result to get single number
	adc ypos
	sta ypos
	lda ypos+1			//add 1024 for start of screen as well/
	adc #$04			//as any carry from adding x.
	ldy ypos			//load lo byte of result in .y (.a has hi)
	
	jmp givayf			//send int to fac1 and return to BASIC
	
Error:
	ldx #$0e			//sets error message to illegal quantity
	jmp err				//jumps to BASIC error handling routine
	

xpos:
	.byte $00,$00
ypos:
	.byte $00,$00,$00,$00

	
	
	
	

My routine takes X and Y arguments and returns the memory location to poke a character on the screen. It include error checking so 0<=X<40 and 0<=Y<25. Having a value outside that range for either will return an illegal quantity error. Since USR() is a function, you can make it part of the poke command to put characters on the screen. This is a valid BASIC statement using this routine.

POKE USR(X)Y,81

10 poke53280,0:poke53281,0
20 poke646,1:printchr$(147)
25 gosub5000:rem  load usr routine
30 sys49152 :rem setup usr routine
40 pi=3.1415
100 forn=pito-pistep-pi/56
110 x=19+16*sin(n)
120 y=12+12*cos(n)
130 poke usr(x)y,81
140 nextn
150 poke198,0:wait198,1:geta$
160 printchr$(147):end
5000 rem -- usr() routine --
5010 forn=49152to49278
5020 reada:poken,a
5030 nextn
5040 return
49152 data 169,11,160,192,141,17,3
49159 data 140,18,3,96,32,170,177,140
49167 data 121,192,141,122,192,32,138
49174 data 173,32,170,177,140,123,192
49181 data 140,125,192,141,124,192
49187 data 141,126,192,173,121,192
49193 data 201,40,176,71,173,123,192
49200 data 201,25,176,64,162,5,24,14
49208 data 123,192,46,124,192,202,208
49215 data 247,162,3,24,14,125,192
49222 data 46,126,192,202,208,247,24
49229 data 173,123,192,109,125,192
49235 data 141,123,192,173,124,192
49241 data 109,126,192,141,124,192
49247 data 24,173,121,192,109,123,192
49254 data 141,123,192,173,124,192
49260 data 105,4,172,123,192,76,145
49267 data 179,162,14,76,55,164,0,0
49275 data 0,0,0,0

So after playing around with passing two arguments to USR() and getting a better understanding of what is going on in machine language, I though, “Would this work with three or four arguments?” So I added more FRMNUM routines followed by FAC2YA routines and discovered that you can pass any number of arguments into your machine language routine using this method.

So I modified my ML program to pass 4 arguments and put the character on the screen using x and y coordinates as well as the character to be poked and the color I want it to be. In BASIC the command would look like this:

10 a=usr(x)(y)(char)(color)

I’ve added parentheses to the last argument because I’ve also added the ability to test what character is in the location instead of putting a character in it. So making the color argument -1 (or anything over 255) would return the current character at location x, y instead of the byte that x, y generates. A negative number outside the parentheses will generate an error because -1 needs to be evaluated first before it FRMNUM can process it.

Here’s the assembly:

/*
 USR() function that takes x,y,char,color parameters and plots to
 the screen, or reads the character at the x,y location.
 2020 - Michael Cassera  	WWW.DEFIANCESTUDIOS.COM

 Usage:	A=USR(X)(Y)(CHAR)(COLOR)
			X must be between 0 and 39
			Y must be between 0 and 24
				if not, an illegal quantity error will result
			CHAR is between 0 & 255 but all 16 bit signed integers are 
				allowed. The high byte is ignored.
			COLOR is between 0 and 15. All 16 bit signed integers are
				allowed. 0 to 15 will color the cell at x,y.

			USR() will return the screen memory location and plot the
				character set by CHAR.  
			*** If COLOR is set to a negative value or a value over
					255 then USR() will return the value of the 
					character currently on the screen at x,y. No 
					data will be written to the screen.
*/


// pulling arguments for the function


.label frmnum = $ad8a	// get and evaluate data for type mismatch
.label usradd = $0311	// pointer for USR funtion for ml
.label fac2ya = $b1aa	// fac1 to int in y(lo) a(hi)
.label givayf = $b391	// int y(lo) a(hi) to fac1
.label err    = $a437	// BASIC error handling routine

* = $c000 "USR Function"

Setup:
	lda #<Start			// get the start address of our USR routine
	ldy #>Start			// lo and hi
	sta usradd			// put the address in location 785 & 786
	sty usradd+1		        // so BASIC knows what to do with USR
	rts				// and return to BASIC
	
Start:					// USR()  data starts as float in fac1

	// grab x
	jsr fac2ya			// grab float from fac1 and
	sty xpos			// convert to 16bit signed integer with 
	sta xpos+1			// lo in .y and hi in .a. Store in memory
	
	//grab y
	jsr frmnum			// grab data after USR()  and put in fac1	
	jsr fac2ya			// grab float from fac1 and
	sty ypos			// convert to 16bit signed integer with 
	sty ypos+2			// lo in .y and hi in .a. Store in memory
	sta ypos+1			
	sta ypos+3			// two sets of y data because of math after	
					// error check

	//grab character
	jsr frmnum			// Next argument for character
	jsr fac2ya			// Get the signed integer
	sty char			// store in memory	
	sta char+1
	
	//grab color
	jsr frmnum			// get next argument color / byte check
	jsr fac2ya			// get the signed integer
	sty color			// store in memory
	sta color+1
	
ErrorCheck:
	lda xpos			// checks to make sure 0<=x<40
	cmp #$28		
	bcs Error			
	lda ypos			// check to make sure 0<=y<25
	cmp #$19
	bcs Error	
	
Math:					// y*40 breaks down to (y*32)+(y*8)	
	ldx #$05			
	clc
l1:					// l1 does left shift 5 times
	asl ypos			// to multiply y*32
	rol ypos+1
	dex
	bne l1
	
	ldx #$03
	clc
l2:					// l2 does left shift 3 times
	asl ypos+2			// to multiply y*8
	rol ypos+3
	dex
	bne l2
	
	clc
	lda ypos			// add results of two loops
	adc ypos+2			// (y*32)+(y*8)
	sta ypos
	lda ypos+1
	adc ypos+3
	sta ypos+1	
	clc 
	lda xpos			// add x to result to get single number
	adc ypos
	sta ypos
	lda ypos+1			// add 1024 for start of screen
	adc #$04			// and any carry from adding x.
	ldy ypos			// load lo byte of result in .y (.a has hi)
	sty $fb
	sta $fc
	
Plot:
	lda color+1			// load the hi byte of color to see if this
	bne Inquire			// is a query of the character (not zero
					// is a request for char at x,y)
	lda $fc				// no query then plot the char at x,y
	jsr givayf			// send int to fac1
	lda char			// get char from memory into A
	ldy #$00			// set Y register to 0 for indexing
	sta ($fb),y			// store A in screen memory
	clc				// clear carry for addition
	lda $fc				// get the screen loc hi byte
	adc #$d4			// add $d4 to locate color memory
	sta $fc				// and store for indirect index
	lda color			// load A with color choice from mem
	sta ($fb),y			// Store color in color mem cell
	rts				// return to BASIC
	
Inquire:
	ldy #$00			// reset Y register for indirect index
	lda ($fb),y			// get the current charact at X,Y loc
	tay				// transfer byte to Y for return float routine
	lda #$00			// Set A to 0 for return float routine
	jmp givayf			// Return integer to float in fac1 for BASIC
					// USR() function and return to BASIC
	
Error:
	ldx #$0e			// sets error message to illegal quantity
	jmp err				// jumps to BASIC error handling routine
	

xpos:
	.byte $00,$00
ypos:
	.byte $00,$00,$00,$00
char:
	.byte $00,$00
color:
	.byte $00,$00

	
	
	
	

And here it is being used in a BASIC program:

100 rem --usr() utility--
102 rem usage:
104 rem a=usr(x)(y)(char)(color)
106 rem a negative number in color
108 rem will return char at loc x,y
110 rem otherwise will return location
112 rem and put char at loc x,y with
114 rem color
115 gosub45000:rem install ml
120 sys49152:  rem activate ml
130 rem -------------------------------
132 poke53280,0:poke53281,0
135 printchr$(147)
137 ch=81
140 a=rnd(-ti)
150 x=rnd(1)*40
160 a=usr(x)(0)(0)(-1)
170 ifa<>32then290
175 cl=int(rnd(1)*16)
176 ifcl=0then175
180 a=usr(x)(0)(ch)(cl)
190 y=1
200 a=usr(x)(y)(0)(-1)
210 ifa<>32then150
220 a=usr(x)(y)(ch)(cl)
230 a=usr(x)(y-1)(32)(cl)
240 y=y+1
250 ify<25then200
260 goto150
290 c2=87
300 forn=0to1
310 fory=0to24
320 a=usr(x)(y)(c2)(1)
330 nexty
340 c2=32
350 nextn
360 goto150
45000 rem --install ml--
45010 forn=49152to49346
45020 reada:poken,a
45030 nextn
45040 return
45999 end
49152 data 169,11,160,192,141,17,3
49159 data 140,18,3,96,32,170,177,140
49167 data 186,192,141,187,192,32,138
49174 data 173,32,170,177,140,188,192
49181 data 140,190,192,141,189,192
49187 data 141,191,192,32,138,173,32
49194 data 170,177,140,192,192,141
49200 data 193,192,32,138,173,32,170
49207 data 177,140,194,192,141,195
49213 data 192,173,186,192,201,40,176
49220 data 112,173,188,192,201,25,176
49227 data 105,162,5,24,14,188,192
49234 data 46,189,192,202,208,247,162
49241 data 3,24,14,190,192,46,191,192
49249 data 202,208,247,24,173,188,192
49256 data 109,190,192,141,188,192
49262 data 173,189,192,109,191,192
49268 data 141,189,192,24,173,186,192
49275 data 109,188,192,141,188,192
49281 data 173,189,192,105,4,172,188
49288 data 192,132,251,133,252,173
49294 data 195,192,208,25,165,252,32
49301 data 145,179,173,192,192,160
49307 data 0,145,251,24,165,252,105
49314 data 212,133,252,173,194,192
49320 data 145,251,96,160,0,177,251
49327 data 168,169,0,76,145,179,162
49334 data 14,76,55,164,0,0,0,0,0,0
49344 data 0,0,0,0

Having the ability to pass multiple arguments to my machine language routines through USR() has given me lots of ideas on how I could use this. Certainly one use that comes to mind is to write a routine to manage sprites where the first argument could be the sprint number, followed by x, y coordinates and then its color.

Now that I’ve played with this command for a little while now, I wish it was something I tried to figure out all those years ago.