Confused by Example ASM Program

Programming the 6502 microprocessor and its relatives in assembly and other languages.
load81
Posts: 71
Joined: 16 Nov 2018

Confused by Example ASM Program

Post 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.
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: Confused by Example ASM Program

Post by Chromatix »

Okay, I think I have this. In logical order to aid understanding:
Quote:
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.
Quote:
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 …"
Quote:
Z!=0? What's setting Z?
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.
Quote:
stx $0334 (twice)
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.
Quote:
Exit to BASIC?
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.
BillG
Posts: 710
Joined: 12 Mar 2020
Location: North Tejas

Re: Confused by Example ASM Program

Post by BillG »

load81 wrote:

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.
BillG
Posts: 710
Joined: 12 Mar 2020
Location: North Tejas

Re: Confused by Example ASM Program

Post by BillG »

Chromatix wrote:
Quote:
stx $0334 (twice)
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.
load81
Posts: 71
Joined: 16 Nov 2018

Re: Confused by Example ASM Program

Post by load81 »

BillG wrote:
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.
BillG
Posts: 710
Joined: 12 Mar 2020
Location: North Tejas

Re: Confused by Example ASM Program

Post 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.
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: Confused by Example ASM Program

Post 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.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Confused by Example ASM Program

Post 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.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Confused by Example ASM Program

Post 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! :)
Last edited by barrym95838 on Wed Apr 01, 2020 3:03 am, edited 1 time in total.
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)
load81
Posts: 71
Joined: 16 Nov 2018

Re: Confused by Example ASM Program

Post by load81 »

BillG wrote:
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?
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: Confused by Example ASM Program

Post 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.
User avatar
barrym95838
Posts: 2056
Joined: 30 Jun 2013
Location: Sacramento, CA, USA

Re: Confused by Example ASM Program

Post 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.
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)
User avatar
cjs
Posts: 759
Joined: 01 Dec 2018
Location: Tokyo, Japan
Contact:

Re: Confused by Example ASM Program

Post 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.
Curt J. Sampson - github.com/0cjs
teamtempest
Posts: 443
Joined: 08 Nov 2009
Location: Minnesota
Contact:

Re: Confused by Example ASM Program

Post 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
User avatar
BitWise
In Memoriam
Posts: 996
Joined: 02 Mar 2004
Location: Berkshire, UK
Contact:

Re: Confused by Example ASM Program

Post 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.
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs
Post Reply