Page 1 of 4
Confused by Example ASM Program
Posted: Wed Apr 01, 2020 12:31 am
by load81
Okay, so I'm working my way through
C64 Machine Language for Beginners by Danny Davis, Melbourne House (1984) as a 6502 primer. On the whole I really like the book. As a rule it's not moving too fast for and it pretty darn through about explaining itself the vast majority of the time.
I'm working through this text in parallel with my efforts to dissassemble a
C64 Cartridge and convert it to working fully commented code. Doing these two things at the same time has been very helpful. As I'm disassembling entirely by hand (no automatic disassembly) I'm only about 10% done. My progress in the book has been quite solid, I'm about 60% done. The only "hiccup" up to this point was 2's compliment arithmetic when performing branching instructions. From what I understand, that confuses everyone the first time around.
Unfortunately, I've hit one of those places where the sample program isn't fully intelligible and the book's explanation... isn't what it could be and I'm just not 100% following. Googling around hasn't helped much. I did check my favorite
6502 Instruction Set Reference site before posting.
In summary: the point of the program is to look for the value
$a9 in memory and (exit?) when it encounters the 4th occurrence. I think it just exits if it wraps around at
$ff. Here is the code, with my perhaps questionable comments after the semi-colon. This is on page 59:
Code: Select all
ldx #$00 ; X is a counter, it increments upon encountering $a9
ldy #$00 ; Y is an index into memory a memory location.
lda #$a9 ; A has the value to be compared.
L40 cmp $f000,y ; Compare somewhere in RAM mapped to KERNAL ROM? Y indexed memory.
beq L90 ; Why am I checking Z and jumping forward? Is this spaghetti code?
L60 iny ; Y++ and Y indexed memory is offset by the new Y value.
bne L40 ; Z!=0? What's setting Z? Is this effectively a greater than statement? If so, why?
stx $0334 ; X is stored here -- as incremented when $a9 is encountered.
rts ; Exit to BASIC?
L90 inx ; X++
cpx #$04 ; X != 4?
bne L60 ; If not, branch to L60 and continue.
stx $0334 ; X is stored here, the same as 5 lines up. Why is this here twice?
rts ; Exit to BASIC?
Any help more completely grasping what I'm seeing would be helpful. I don't want to move on to chapter 8 until I get this.
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 1:04 am
by Chromatix
Okay, I think I have this. In logical order to aid understanding:
Compare somewhere in RAM mapped to KERNAL ROM?
I think it's using KERNAL as a source of example data. There should be plenty of $A9 bytes in there, as that's the LDA immediate opcode.
Why am I checking Z and jumping forward?
The Z and N flags are set by most instructions that change a register value or perform read-modify-write on memory. In this case CMP-BEQ is a common idiom meaning "if(A == M) then goto …"
The INY did. If Z is set, then it just wrapped around from 255 to 0. If that didn't happen, then we loop back to test the next byte.
I think this is a bug. It should be STY, not STX, so that the location where the fourth $A9 was found is stored. Since the routine is supposed to be looking for the fourth $A9, it would be logical to store that. And if you get zero back (after INY wrapped around), you know that there weren't as many as four of them in that page.
Strictly speaking, this returns to whatever called this subroutine. It might be used by a larger machine-language program, rather than being a toy example called directly from BASIC.
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 2:32 am
by BillG
Code: Select all
L60 iny ; Y++ and Y indexed memory is offset by the new Y value.
bne L40 ; Z!=0? What's setting Z? Is this effectively a greater than statement? If so, why?
The INY instruction sets the Z flag if incrementing register Y results in a zero value, otherwise Z is cleared.
I found the book and the program is looking for $A9 within 255 bytes of the given address.
Their program actually contains a bug - it actually looks for $A9 within
256 bytes of $F000!
I am not a C64 expert and will have to research the meaning of storing a value at $0334. It is probably a way to get a result back to a BASIC program.
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 2:38 am
by BillG
I think this is a bug. It should be STY, not STX, so that the location where the fourth $A9 was found is stored. Since the routine is supposed to be looking for the fourth $A9, it would be logical to store that. And if you get zero back (after INY wrapped around), you know that there weren't as many as four of them in that page.
No, it is not a bug.
There are two ways to return from that subroutine: one when $A9 is found four times and one when Y wraps around. In both places, X contains the number of times $A9 has been found.
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 2:45 am
by load81
I am not a C64 expert and will have to research the meaning of storing a value at $0334. It is probably a way to get a result back to a BASIC program.
If my memory isn't playing tricks on me, I think that's somewhere in the cassette tape buffer. In which case, it's just a convenient place to store data that isn't likely to be overwritten while using their ML input program. I don't think BASIC has any special access into this area of memory.
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 2:48 am
by BillG
If you will look on page 147, location $0334 is unused, so it is a safe place to pass a value back without crashing BASIC.
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 2:50 am
by Chromatix
The book happens to be online at archive.org.
I found the relevant program on page 57, not 59. There is a comment in the text that changing STX to STY does indeed store the address of the fourth $A9, rather than the count. They're using a WATCH command to monitor the contents of that address, rather than attempting to examine the 6502 registers themselves.
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 2:52 am
by GARTHWILSON
There are lots of simple programming tricks, with links to more, on the 6502 primer's programming tips page, at http://wilsonminesco.com/6502primer/PgmTips.html . These may answer many of the questions you come up with regarding why various things were done the way they were like your INY, BNE.
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 2:56 am
by barrym95838
In my own experience, non-trivial assembly language examples have a natural tendency to resemble spaghetti code, unless some more advanced assembler features like macros and local labels are brought into play, and even then it's probably not going to look much like PASCAL. That being said, it looks like this particular example is a bit more "spaghettiish" than necessary ... I'll abstain from any further unprompted criticism, because doing so would have questionable educational value. Have fun and keep learning!

Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 2:59 am
by load81
Their program actually contains a bug - it actually looks for $A9 within 256 bytes of $F000!
Wait... are you saying the BNE and the INY should be swapped to eliminate an off-by-one error? If so, can you spell it out for me as to why this is true?
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 3:20 am
by Chromatix
Let me be more precise. The program checks a range of 256 bytes, at offsets 0 to 255 inclusive from the base address, $F000. So in fact it *does* cover "within 255 bytes" of $F000. There is no bug in that respect.
You may find it helpful to work through the CPU's operation by hand, step by step, and pay attention to what happens when.
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 3:37 am
by barrym95838
Reiterating what Chromatix said, a significant percentage of 65xx instructions have condition flag side-effects, perhaps a higher percentage than any other microprocessor I can recall. Getting these side-effects to work for you rather than against you is one of the keys to writing (and understanding) efficient code, and requires some practice and careful thought.
Re: Confused by Example ASM Program
Posted: Wed Apr 01, 2020 6:31 am
by cjs
Just a few addenda to all the other good comments:
First, until you fully memorize which instructions affect which flags (which will come naturally over time), carefully check a reference sheet for that information for the instruction preceeding a branch and further instructions back if necessary. That information is on the Masswerk sheet you linked, but you may find the
Obelisk Instruction Reference easier to read, at least at first.
So when we look at code like this:
Code: Select all
lda #$a9 ; A has the value to be compared.
L40 cmp $f000,y ; Compare somewhere in RAM mapped to KERNAL ROM? Y indexed memory.
beq L90 ; Why am I checking Z and jumping forward? Is this spaghetti code?
we can analyze it as follows:
- Looking up BEQ, we see that it branches if the Z (zero) flag is set and continues forward if the Z flag is clear. (Essentially, it's asking if some result of the last instruction to set it was equal to zero.)
- Looking backwards at CMP, we see that it indeed sets/clears the zero flag based on its result. CMP subtracts memory from the accumulator, in this case A - ($F000+Y), reading the parens as "the contents of." Since if A is $A9 and the contents of the memory location is $A9, and $A9-$A9 = $00, that would set the zero flag and we now see why the branch instruction is named BEQ. The negative and carry flags will be set as appropriate as well, but the result will be thrown away, rather than being stored somewhere.
- But what number are we comparing? If I hadn't inserted the spoiler above, you'd look back at the previous instruction yet and see that it's a LDA #$A9, so now we know what was in the A register when the CMP instruction was executed.
As for spaghetti code, well, the standard in 6502 assembler for that is pretty high: remember, all we have is conditional GOTO, so the only result of a test is either to continue forward or jump to somewhere else. But in this case, we have a loop with an exception condition in it that we jump out to from the middle, and when done with that jump back into the middle of the loop, so it is a bit confusing. But having to do this is not infrequent in assembler.
Re: Confused by Example ASM Program
Posted: Thu Apr 02, 2020 8:00 pm
by teamtempest
If you prefer, the example program can be re-written in any number of ways. Here's one:
Code: Select all
ldx #0 ; we're going to count to four
ldy #0 ; we're going to index off an absolute address
lda #a9 ; this is the byte we're looking for
L1 cmp $f000, y ; subtract the value at $f000+y from the accumulator, throw the result away but do set the processor flags
bne L2 ; branch if no match
inx ; found another one
cpx #4 ; subtract the value 4 from the x-register, throw the result away but do set the processor flags
beq L3 ; branch if we found all we're looking for
L2 iny ; index to the next memory location and set the z-flag if we've wrapped the full range of the y-register
bne L1 ; branch if we haven't looked at the full page yet ($F000 - $F0FF)
L3 stx $0334 ; save however many we actually found
rts ; return to caller
Re: Confused by Example ASM Program
Posted: Thu Apr 02, 2020 8:39 pm
by BitWise
It can even be structured...
Code: Select all
00:0300 A200 : ldx #0 ; we're going to count to four
00:0302 A000 : ldy #0 ; we're going to index off an absolute address
00:0304 A9A9 : lda #$a9 ; this is the byte we're looking for
repeat
00:0306 D900F0 : cmp $f000,y ; subtract the value at $f000+y from the accumulator, throw the result away but do set the processor flags
00:0309 D005 : if eq
00:030B E8 : inx ; found another one
00:030C E004 : cpx #4 ; subtract the value 4 from the x-register, throw the result away but do set the processor flags
00:030E F003 : break eq ; branch if we found all we're looking for
endif
00:0310 C8 : iny ; index to the next memory location and set the z-flag if we've wrapped the full range of the y-register
00:0311 D0F3 : until eq ; branch if we haven't looked at the full page yet ($F000 - $F0FF)
00:0313 8E3403 : stx $0334 ; save however many we actually found
00:0316 60 : rts ; return to caller
.. which removes the need for labels.