uc65, a Mid-Level Language Compiler READY FOR USE!

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
infiniteneslives
Posts: 2100
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by infiniteneslives » Sun Dec 08, 2013 9:15 pm

qbradq wrote:Also, there is no end clause for loop.
So loop is basically an infinite loop then? Same as:

Code: Select all

while 1==1
...
end while
If that's the case, I'd wonder if it's even worth having loop since the while loop is easily adapted to give the same effect.
Subroutine parameters are copy-on-call. If you want them in zero-page, use the fast modifier for the subroutine. If you want the subroutine's variables in zero page, use the fast modifier on the variables. uc65 only uses the stack to store register values during interrupt handlers.

rom and ram apply to everything that emits code or variables following those statements until the next such statement.

Again when I am feeling better I will try to make the documentation more clear.
That makes sense based on the limitation that recursion isn't allowed as the second call will override the variables of the first. So basically every subroutine's variables will ALWAYS occupy a location in ram. A subroutines variables aren't temporary. So if one is trying to conserve ram use try to limit the number of variables used. Using globals for a subroutine to modify themselves, vice using temporary storage variables within a subroutine would be best. Or perhaps a handful of global temp variables which all routines can use as scratch. These things make sense when you understand the underlying assembly going on. :) One can take the easy route and create those temporary variables without trouble, but then when it comes to optimizing it helps to have a good grasp of assembly.

Regarding my discussion of having banked ram, it would sound prudent to put all globals in system ram so they truely are global (regardless of banking). To allow subroutines to use banked ram for variable use would require some specific rules. Let me know if this sounds right:

subroutine parameters: when are variables copied over? I assume it's just before the parent routine jumps to the child subroutine. So by ensuring the child's ram bank is active prior to calling the subroutine, you ensure the subroutine will get the copy over of input parameters.

variables within the subroutine: The subroutine needs to ensure it's ram bank is active before using the variables.

There is no harm in the parent setting up ram banking for the child's variables. But as a design choice you could make the child responsible for setting up banking for it's own variables. Parents MUST initialize child's ram banking when parameters are passed in though to ensure proper copy over. Since ram/rom segments can't be used within subroutines, that means the parameters and variables of a subroutine need to be in the same bank/segment. Starts to get tricky the more I think about this... Having banked SRAM would require a lot of delicate care especially if the parent and child's ram wasn't visible at the same time.

Really my only desire is to put game/graphics data swappable with RAM in FME-7 style. So operating with the assumption that the WRAM was always there except for the routines which swapped in ROM for game/graphics data, then you'd be okay.

The best way to handle funky situations like that were you had to handshake between parent and child ram banking would be to make use of the stack and/or A/X/Y registers for passing/returning variables. Using the stack for passing in parameters could be done with the language as-is by creating a helper 'handshake' function to utilize the stack so you'd pass the parameters into the helper vice the subroutine itself.

So I'm having some confusion on how to best utilize return variables. I was writing a subroutine to read controllers:

Code: Select all

;; Read controller ports
;;
;; @param padnum The controller pad desired to be read
;; return button output A,B,sel,srt,U,D,L,R
export sub nesReadPad byte padnum as byte

	;strobe latch signal
	controller1 = $10
	controller1 = $00

	if padnum == 1
		asm
			LDX #$08
		joy1_loop:
			LDA $4016
			LSR A			; bit0 -> Carry
			ROL nesReadPad_return	; bit0 <- Carry
			DEX	
			BNE joy1_loop
		end asm

	else ;if padnum == 2
		asm
			LDX #$08
		joy2_loop:
			LDA $4017
			LSR A			; bit0 -> Carry
			ROL nesReadPad_return	; bit0 <- Carry
			DEX	
			BNE joy2_loop
		end asm
	end if

	return nesReadPad_return

end sub
But I'm not sure I'm doing it right... See I don't want to create a new variable, and use that for ROL'ing the controller data into, because there is already a return variable automatically created. So can I just use the sub_return variable without instantiating it (outside of asm portions too) because I know it will be created by the assembler? Do I need to use the return statement since I know I'm modifying the return variable? Or could just leave out the return statement and assume it's implied? Basically I don't know how to use the return variable within the subroutine. Perhaps I should instantiate a variable which conflicts with the return variable's name intentionally? Normally I wouldn't have thought about doing this except the comment in the documentation regarding asm portions brought it up so now I want to give it a try as it conserves on ram use. I suppose once return variables via A/X/Y registers is implemented then this isn't of much use aside from large return variables I suppose.

Is there any way to utilize macros with uc65?
If you're gonna play the Game Boy, you gotta learn to play it right. -Kenny Rogers

slobu
Posts: 275
Joined: Tue Jul 12, 2011 10:58 am

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by slobu » Mon Dec 09, 2013 7:11 am

Or perhaps a handful of global temp variables which all routines can use as scratch.
This sounds good. When a function/routine returns a value it would default to the first temp variable (temp1) and go up from there. Also having them available as plain old temp vars is a plus.

User avatar
qbradq
Posts: 951
Joined: Wed Oct 15, 2008 11:50 am

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by qbradq » Mon Dec 09, 2013 7:42 am

loop is an explicit infinite loop. "while 1=1" will still execute a comparison. There's no compile-time optimization to remove this comparison.

Banking of both RAM and ROM is left to the user to implement and not mess up. Also, to a previous point you had, the OMA page $0200 is already set up in the memory configs.

The caller of the subroutine copies values into the subroutine's parameter variables before the jsr.

Return values are a syntax convenience. If you want to eliminate the automatic variable created by the subroutine, you can place the value in a global temp variable and not use return values. The the caller will have to copy the value manually.

The thing to remember here is that uc65 will not generate 100% efficient assembly code. There's a trade off between ease of development and resource utilization. There's always the option of using assembly :)

Personally I tend to write subroutines like controller polling in assembly because they are small and easy to write in assembly. In the larger main program I write things in uc65 and call the subroutines I've written because it's easier to maintain and develop.

I am concerned that RAM utilization will be a key limiting factor in the size and complexity of a uc65 program though. We'll have to see how it plays out.

User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by cpow » Mon Dec 09, 2013 1:50 pm

Just FYI I've created a NESICIDE template project using the hello world that INL put together. It works, but there's still the issue of the error in the debug info. That, unfortunately, prevents being able to do source stepping, setting breakpoints, etc. Any luck on that front yet?

So in order to get going in UC65 all one would have to do is create a new project based on the 'NROM Hello World - UC' template. It builds/runs out of the box. I'm going to add uc65.jar directly into the CC65 bin folder that's delivered with the installer, I think...

User avatar
infiniteneslives
Posts: 2100
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by infiniteneslives » Mon Dec 09, 2013 4:52 pm

qbradq wrote:loop is an explicit infinite loop. "while 1=1" will still execute a comparison. There's no compile-time optimization to remove this comparison.
Gotcha. I'd agree the way you've set things up make sense then. People will probably request things like loop N times, but I can see why you wouldn't want to implement that. Better off to force the user to create a while with their own variable for that use.
Banking of both RAM and ROM is left to the user to implement and not mess up. Also, to a previous point you had, the OMA page $0200 is already set up in the memory configs.
Yeah I understand that, I was trying to figure out how to not mess it up. ;) I noticed the OMA defn shortly after posting of course...
Return values are a syntax convenience. If you want to eliminate the automatic variable created by the subroutine, you can place the value in a global temp variable and not use return values. The the caller will have to copy the value manually.
I get that, my question is how do I utilize that variable within the subroutine that's about to return it. Perhaps the best answer is just not to try and make use of the return variable before the return statement, but your mention of it's name being 'sub_return' made me want to make use of it. Do I need to instantiate it before making use of it? Or can I just assume it exists with the 'sub_return' name since the subroutine is defined as having a return type?

The thing to remember here is that uc65 will not generate 100% efficient assembly code. There's a trade off between ease of development and resource utilization. There's always the option of using assembly :)

I am concerned that RAM utilization will be a key limiting factor in the size and complexity of a uc65 program though. We'll have to see how it plays out.
While I get that we're trading conveince for effeciency when choosing uc65 over assembly, I share your concern about ram utilization. So understanding how the language makes use of and allocates ram aids in trying to write uc65 code that conserves on ram use. From what I'm seeing thus far you have to think about ram use as if you're writing in assembly. I think doing that will go a long way to keeping ram from becoming scarce with uc65.
cpow wrote:So in order to get going in UC65 all one would have to do is create a new project based on the 'NROM Hello World - UC' template. It builds/runs out of the box. I'm going to add uc65.jar directly into the CC65 bin folder that's delivered with the installer, I think...
Good stuff! Looking forward to playing around with NESICIDE once things are finished up! ;)
If you're gonna play the Game Boy, you gotta learn to play it right. -Kenny Rogers

User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by cpow » Mon Dec 09, 2013 9:04 pm

I'm trying to figure out how to rebuild uc65.jar...it's not obvious to me. I'd like to try the following changes to CodeGenerator.java:

Code: Select all

				".DBG SYM, \"%s\", \"00\", %s, \"%s\"\n",

Code: Select all

				".DBG FUNC, \"%s\", \"00\", %s, \"%s\"\n",
I looked at CC65 source and it emits "00" not "90" for those.

Hopefully that'll solve the debuginfo parsing problem...but I can't be sure. Can you provide directions for how to rebuild uc65.jar?

EDIT: I manually changed the 90's to 00's in the generated debuginfo file and source stepping works again, so likely the change proposed to uc65.jar will correct the problem.

User avatar
qbradq
Posts: 951
Joined: Wed Oct 15, 2008 11:50 am

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by qbradq » Tue Dec 10, 2013 12:59 pm

Thanks for working that out. The 90 thing was based on reading the source code. Like I said, documentation is no good for this.

I have attached a new zip file, rc4, to the first post. This contains a new JAR file with the 00 fix, the stderr fix and the percision / precision spelling correction. I am over my flu today but still sleeping a lot trying to get my strength back. Should be back in the swing of things tomorrow :)

Edit: Also, if you put uc65.jar into the NESCISIDE bundle, please include the file COPYING (or in some other way note that uc65 is licensed under the GPLv3 or later, and provide the text of the GPLv3) and a link to the project page. This will satisfy the terms of the license as long as you don't make source code changes.

User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by cpow » Tue Dec 10, 2013 3:22 pm

qbradq wrote:Thanks for working that out. The 90 thing was based on reading the source code. Like I said, documentation is no good for this.
Works here. I've released a NESICIDE update with a template project for uc65. Eventually I'd like to put a lexer for the language into the editor, and support symbol tool-tips. Actually...if you treat your symbols like C symbols [prepend an underscore in the generated ASM output], what's there for C support might "just work". But anyway, symbol watching appears to work, breakpointing, stepping source, also.

User avatar
infiniteneslives
Posts: 2100
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by infiniteneslives » Tue Dec 17, 2013 1:22 pm

Not sure if I was supposed to be able to derive this issue from the doc or not, but quoting the doc:
IMPORTS/EXPORTS section: Note that the compiler does not compile imported source files into assembly files, nor does it automatically link these assembly files. The user must take care in their build automation steps to separately compile, assemble and link each source file needed to construct the entire program. Failure to do so will result in unresolved symbols at link time. See the section "Build Automation" for more information.
I have 3 files, global.uc, sprite.uc, and main.uc

I want sprite.uc and main.uc to import global.uc, and I want main.uc to import sprite.uc.

The ordering in which the files are imported into main.uc is critical, else errors plague you.


main.uc broke:

Code: Select all

import "sprite.uc"
import "global.uc"
main.uc works:

Code: Select all

import "global.uc"
import "sprite.uc"
I also verified that sprites.uc wasn't bringing global.uc along for the ride when sprites.uc is imported to main.uc, so the issue isn't global.uc getting doubly imported. I'm not used to the order things are imported making much difference.

Not saying it should necessarily needs to be changed. But it threw me for a loop for awhile, perhaps explaining that in the manual (more clearly?) would be helpful. Or maybe the upcoming "build automation" doc will help explain? Just thought it was worth sharing at a minimum.
If you're gonna play the Game Boy, you gotta learn to play it right. -Kenny Rogers

User avatar
qbradq
Posts: 951
Joined: Wed Oct 15, 2008 11:50 am

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by qbradq » Wed Dec 18, 2013 7:11 am

Can you help me understand how the first scenario "broke"? Does sprite.uc import global.uc? I think I may have an issue with the way I am preventing multiple imports, let me look into this.

Also, I have updated the documentation regarding the loop statement to be correct.

Thank you all for your patience with this project as I got my day job situation taken care of!

User avatar
infiniteneslives
Posts: 2100
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by infiniteneslives » Wed Dec 18, 2013 11:57 am

qbradq wrote:Can you help me understand how the first scenario "broke"? Does sprite.uc import global.uc? I think I may have an issue with the way I am preventing multiple imports, let me look into this.
Yes, both main.uc and sprite.uc import global.uc, and main.uc imports sprite.uc

sprite.uc does not import anything besides global.uc, and global.uc imports nothing.

no other files import global.uc

sprite.uc

Code: Select all

import "global.uc"
main.uc

Code: Select all

import "nes.uc"
import "data.uc"
import "sprite.uc" 
import "global.uc" ;must come before sprites.uc which also imports global.uc
errors:

Code: Select all

C:\Dropbox\nesdev\dig_deeper2>java -jar ..\uc65.jar source\global.uc

C:\Dropbox\nesdev\dig_deeper2>java -jar ..\uc65.jar source\nes.uc
source\nes.uc(165): Warning: Possible loss of precision

C:\Dropbox\nesdev\dig_deeper2>java -jar ..\uc65.jar source\sprite.uc

C:\Dropbox\nesdev\dig_deeper2>java -jar ..\uc65.jar source\main.uc
source\main.uc(30): Error: Unresolved symbol gameState
source\main.uc(62): Error: Unresolved symbol buttons1
source\main.uc(63): Error: Unresolved symbol buttons2
source\main.uc(69): Error: Unresolved symbol buttons1
source\main.uc(72): Error: Unresolved symbol buttons1
source\main.uc(75): Error: Unresolved symbol buttons1
source\main.uc(78): Error: Unresolved symbol buttons1
Compilation failed
global.uc:

Code: Select all

; along with this program.  If not, see <http://www.gnu.org/licenses/>.

export fast byte gameState
export fast byte buttons1
export fast byte buttons2

;;buttons
export constant	NOTHING		= %00000000

;;gameState
export constant	TITLE		= $00
export constant LEVEL1 		= $01
export constant LEVEL2 		= $02
export constant GAMEOVER 	= $FF


;;metaSprite.state
export constant INACTIVE	= $00
export constant NEWBORN		= $01
export constant NORMAL 		= $02
export constant DYING		= $FF
Make this one change to main.uc and errors go away:

Code: Select all

import "nes.uc"
import "data.uc"
import "global.uc" ;must come before sprites.uc which also imports global.uc
import "sprite.uc" 

Thank you all for your patience with this project as I got my day job situation taken care of!
No problem, thanks for working on things as time permits. :)
If you're gonna play the Game Boy, you gotta learn to play it right. -Kenny Rogers

User avatar
qbradq
Posts: 951
Joined: Wed Oct 15, 2008 11:50 am

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by qbradq » Thu Dec 19, 2013 8:02 am

Thanks for the bug report INL! I have raised Issue 4 in the bug tracker to track the status of this issue. The issue has been reproduced and a failing regression test added to source control. I am working on the fix now. I'll also update this thread when I fix it.

User avatar
infiniteneslives
Posts: 2100
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by infiniteneslives » Fri Dec 20, 2013 6:27 pm

I'm confused as to how break and continue statements are supposed to work especially with providing number of loops to advance.

Since there are no defined iterators (like a typical for loop would have), how does the language/compiler know what iteration to advance to? I've created the same effect of a for loop using the while statement, but have to create my own iterator. It doesn't seem that the language would support "continue [n]" and know which variable is my iterator. Based on how the while and loop statements work, it would only make sense for the user to have to manually advance the iterator prior to the continue call.

I tried to use continue, and I'm pretty sure it's broken, although due to my confusion above and lack of trust in my surrouding logic I'm not yet certain of the issue. I still need to look through the asm output though too. I wanted your input and improve my understanding before I start trying to debug further.

This is the current definition:
Continue and Break

The continue and break statements advance to the next iteration of, or end condition of a containing while or loop statement block respectively. An optional number of nested loops to advance through may be provided.

continue [n]
break [n]

Where n is the number of nested loops to advance through. If omitted, this defaults to one. N must be greater than zero and less than or equal to the nested loop depth.
I'm guessing it's just a carry over issue similar to the loop conditional, so if I were a betting man I'm thinking with the current while and loop definitions the following would be more accurate:
Continue and Break

continue within a while block: jump out of the while loop and evaluate the while conditional to determine whether to re-enter the while statement block or to exit the while block.

continue withing a loop statement block: jump out of current iteration of the loop and restart from the loop statement.

break within a while statement block: exit the while block all together.

break within a loop statement block: exit the loop, a means to end the infinite loop.

There is no such thing as: continue [n], but you can create the same effect by manually advancing your own iterator prior to the continue statement. In fact it's 'default' is always "continue 0" because you have to manually advance the iterator, failing to do so would repeat the current iteration from the beginning.

Doesn't make sense: break [n] where n>1, because you can only exit a while or loop statement once. It's as if the 'default' is always 1.
Thoughts? Am I all mixed up here?
If you're gonna play the Game Boy, you gotta learn to play it right. -Kenny Rogers

User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by cpow » Sun Dec 22, 2013 6:45 pm

infiniteneslives wrote: Doesn't make sense: break [n] where n>1, because you can only exit a while or loop statement once. It's as if the 'default' is always 1.
Doesn't the break [n] where n>1 really mean "break out of n nest levels". In other words, if I'm nested three deep and I do a break 2 I'll break not only out of the innermost loop, but the mid-level loop also and find myself back in the outer loop.

User avatar
infiniteneslives
Posts: 2100
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Re: uc65, a Mid-Level Language Compiler READY FOR USE!

Post by infiniteneslives » Mon Dec 23, 2013 12:14 am

Ahh gotcha, that makes sense. I think the combining of the explanation of break and continue at the same time confused me a little in the doc.

So the only thing I'm confused on now is the continue [n] statement without an explicit iterator.
If you're gonna play the Game Boy, you gotta learn to play it right. -Kenny Rogers

Post Reply