Page 1 of 1

First real foray into 6502 programming....

Posted: Mon May 16, 2005 4:22 am
by Gurft
So I've been learning 6502 assembly, picking it up pretty quickly now that I'm writing my own code.

I wrote this "bouncing ball" program that the next step is to turn it into some kind of pong game, it runs in MK's 6502 simulator quite well, am looking for comments, critic, and suggestions.

http://www.gurft.net/6502/BouncingBall.asm

Please let me know different ways I could have done things. I'm sure there's a MUCH more efficient way of doing the MOVEBALL routine, or a way to put the wall collision detection into a subroutine, but hey, it's my first pass at this (Suggestions on how to do this is also more then welcome)

Thanks

Posted: Mon May 16, 2005 4:51 am
by Memblers
Looks nice.

Here's one thing I found:

Code: Select all

DIR2:
	INX
	CPX	width
	BEQ	CD3
	DEY
	CPY	#$00
	BEQ	CD1
	LDA	#$02
	STA	curdir
	JMP 	BACK
I've rarely (if ever) needed to compare to #0. The DEY opcode before that will affect the zero-flag, so you can safely remove the CPY #$00 and get the same result.

With collision detection, I'd do something like greater/less-than checks. Compare the ball's position to the wall's position and check the carry flag (BCS/BCC). And do it on 2 or all sides of the ball.

It need not be a subroutine really, if you only call it from one place then the JSR/RTS is superfluous.

Posted: Mon May 16, 2005 5:03 am
by Memblers
Also, if you move the ball just by inc'ing an dec'ing the position you're likely to end up with movement that's either too fast or too slow.

You could use fixed-point addition. 16-bits for the position and at least 8 bits for the movement speed. It would use the upper 8 bits for the displayed position, and the lower 8 bits only with the movement speed.

Posted: Mon May 16, 2005 7:56 am
by GARTHWILSON
Congratulations on a good start. I see certain good traits in your code that beginners often violate and end up with code that's very difficult to figure out later to fix or modify. You have for example blank lines that make it easier to mentally separate groups of lines that serve as function entities, and use variable and constant names instead of unnecessarily mixing raw number in with the code. [Edit-- Oops, I see now that you said you have a ton of software-development experience, just not in 6502. I won't re-write the following-- Just take it for whatever it's worth.]

You're apparently looking for constructive criticism. Without knowing more about your end goal, I can only give minor criticisms to make the code more time- and memory-efficient. Tight code is easier to manage.

You have for example in subroutine PAUSE, PHA and PLA; but you don't use A, so there's no reason to save it and then restore it again.

As Memblers said, there's never any need to use CPY #0 immediately after LDY, INY, DEY, TAY, PLY, etc.. It's redundant. The CPY #0 already happens automatically as part of these other instructions.

Your NOP NOP NOP NOP which you're using for 8 clocks' delay could be shortened to PHY PLY with only two lines and two bytes if 7 clocks is ok. I know-- picky, picky. If it came up at least two or three times, it would be worth making a macro called something like NOP4, so it would only take one line per occurrence and eliminate some distraction from viewing the goal of the routine.

DRAWBALL only pushes and pulls A, but also uses X and Y. If you want to keep X and Y unaffected, you could do the same thing you're doing, using the same number of lines, bytes, and clocks, by only using A. If, OTOH, you don't need to keep X or Y unaffected, you could use one of them instead of A and then omit the PHA and PLA.

There seem to only be four possible values for curdir. If you have a 65c02 and if you can make curdir go in even numbers, the CMP-BEQ sequence in MOVEBALL could be replaced with a jump table (although it would make more difference when you have more comparisons). You could replace those 9 lines with

Code: Select all

        LDX curdir
        JMP (table-2, X)
and the table before or after the routine,

Code: Select all

        MB_JMP:    .WORD DIR1, DIR2, DIR3, DIR4
(The "-2" above is because you're not using a "0" value .)

Again if you have a 65c02, you can replace the short JMPs with the BRA instruction, saving a byte on each. If you only have an NMOS 65c02, in many of your cases the value of a flag will be known from an LDX or other recent instrucion, so you could BPL or something like that and know that the branch will always be taken. (This would be more important when memory gets tight. If it comes down to that, be sure the comments tell why the BPL, BVC, or whatever is there. BRA on the other hand usually needs no explanation.)

If you have 65c02, your LDX #00, STX io_posy can be replaced with the single instruction STZ io_posy. You have a similar situation in DODRAW2, even though there's an STY in between.

In DRAWSIDES, the LDA #1, LDY #$01 could save a byte by changing the LDY #$01 to TAY since the 1 is already in A.

In DODRAW2, there are two occurrences of STA io_putc with nothing changing the value of A in between, and no labels in between to jump to; so the second one will always store the same value in io_putc that it already had... unless that location is not a variable but rather an I/O device that outputs another byte every time you store to that address.

I personally like to indent loops. This could be considered a matter of personal style, but I think it makes it easier to visually factor them at a glance.

This is just a quick look that I saw some minor areas for improvement in. Some people have the attitude that they don't matter; but to me is seems to be part of a mentality of keeping the code as concise and well documented as possible, which pays off big time when you get into programs that are thousands or tens of thousands of lines, where maintenance becomes hopeless if you haven't been careful.

At my last place of work, I was working on a project with another man in the late 1980's. He typed incessantly in his programming work. For a long time, I wondered what he could possibly be typing, going non-stop like that. After he left the company and the boss asked me to change how one of his routines worked, I looked through the code and found it. It was four pages of spaghetti. The first attack I made was like reducing an equation to lowest terms. "Ok, we already have a subroutine to do these three parts... This part and this part really should be a macro... Here are some redundant instructions..." etc.. By the time I was done, it was down to only half a page, took far less memory and ran faster, was much easier to read, and I finally could see what needed modifying. Little by little, I ended up having to re-write much of his code.

Posted: Mon May 16, 2005 11:26 am
by Gurft
Thanks guys! This is exactly what I was looking for. I'm very used to writing code so that others can read and understand what I'm doing (and not end up like the gentleman Garth discusses at the end of his post)

The reason I asked for you guys to look over my code is that there are standard conventions that I'm not used to. For example, doing the CMP Y, #$00 Being my first 6502 program I specified it to keep track of what *I* was doing, but can see now that it can come out.

I'm basing all my code of the NMOS 6502, as it's what I have in my posession, and I don't ever expect my code to end up in the wild in some production application. I'm building this computer for my personal edification (and to replace my more expensive VW hoppy since we bought a house ;))

Thanks for the tips, I'll work on it more tonight and post a second version to see how well things shore up.