6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Sep 29, 2024 9:29 am

All times are UTC




Post new topic Reply to topic  [ 25 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Mon Nov 12, 2018 3:10 pm 
Offline

Joined: Mon May 21, 2018 8:39 am
Posts: 41
Branches can also be done in the reverse direction. Here is a rather inefficient,
but illustrative example:
1 ********************************
2 * AL05-LOOP PROGRAM 2A *
3 ********************************
4 *
5 * OBJ $300
6 ORG $300
7 HOME EQU $FC58
8 *
9 START JSR HOME
10 JMP SETX
11 END RTS
12 *
10 SETX LDX #$FF
11 LOOP STX $700
12 DEX
13 BEQ END
14 JMP LOOP

The Monitor listing for this would be:
*300L
0300- 20 58 FC JSR $FC58
0303- 4C 07 03 JMP $0307
0306- 60 RTS
0307- A2 FF LDX #$FF
0309- 8E 00 07 STX $0700
030C- CA DEX
030D- F0 F7 BEQ $0306
030F- 4C 09 03 JMP $0309

In this example, the branch, if taken, will cause the program to move back
up through the listing. To indicate this branch in the opposite direction, the high
bit is set. This is the same technique that is often used to show negative numbers
in assembly-language programs. Please note that it is not just a matter of setting
the high bit. If that were the case, the value following the BEQ command might be
expected to be$89. (The address of the next instruction ($30C) minus where we
want to go to ($303) equals $09
. Then with the high bit on, we have $89.)
This is almost correct. The actual value is arrived at by subtracting the
branch distance from$100. Thus $100 minus $09 equals $F7.


This is taken from page 29 of the Assembly Lines: The complete book [pdf] by Roger Wagner.


My question is: Why to subtract $030C from $0303 (as mr. Wagner says which I really don't understand why he mentions that addresses) and not $030F (the address of the next instruction-after BEQ) from $0306 (the address of the instruction where we REALLY want to go!)

Normally the Program Counter will point to the next instruction, when branch is executed. And since it is a branch, Program Counter will be pushed into the Stack. So, the offset is calculated from the subtraction of the $030F minus the address of the instruction where the Branch instruction points (i.e. $0306).

Am I missing something?


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 12, 2018 4:08 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 9:02 pm
Posts: 1738
Location: Sacramento, CA
As the 6502 is decoding the instruction and opcode, it auto increments the program counter after the byte is read. So after it reads the branch offset, it increments the program counter to point at the opcode of the next instruction. At this point, it will decide if the branch should be taken or not. If it does, then it adds the offset to the program counter (no use of the stack happens for branch instructions) and fetches the opcode from the new address. That is why the math works for calculating the offset. Also, if a branch is taken, 1 additional clock cycle is used to perform the addition. AND, if a page boundary gets crossed (carry of the lower 8 bits of the addition), then 1 more clock cycle is needed to adjust the upper program counter byte.

I am not sure why Mr. Wagner stated to subtract $030C. Perhaps it was a simple error.

I hope this helps.

Daryl

_________________
Please visit my website -> https://sbc.rictor.org/


Top
 Profile  
Reply with quote  
PostPosted: Mon Nov 12, 2018 5:51 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
It certainly looks like an error to be looking at 303 and 30C - the instruction before the destination and the instruction before the branch could each be 1, 2 or 3 bytes, so the subtraction could cover quite a range of values, only one of which would be correct!

It's really not a good description, in my view. If you have prepared the reader to understand a little of binary arithmetic, then a negative value will be understood.

Here's the book online:
https://archive.org/details/AssemblyLin ... r/page/n47


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 13, 2018 2:53 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
I'll just leave this here:
Code:
   uint16_t rel()  { int8_t off = read(PC++); carryCycle(PC+off,PC); return PC + off; }


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 13, 2018 4:05 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
This is my version:
Code:
    public int addrRelative() {
        int value = fetchByte(pc++);
        value = signed(value);
        int addr = pc + value;
        addr &= 0xffff;

        return addr;
    }

What does carryCycle do?


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 13, 2018 8:29 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
It just counts the extra clock cycle if the upper half of the address changes. If cycle counting is disabled, it's a no-op.


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 13, 2018 8:51 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
What C is hiding here, of course, is the sign-extension of the single byte and the application of the addition to the two-byte PC. And hiding that is a good service to the person programming in C.

At the level of explanation in the original article, you've got a two-byte PC and a one-byte offset, and somehow you have to explain how these are combined. If you wanted (or needed) to explain the extra cycle of a page-crossing branch, then explaining the calculation as one or two operations each on a single byte would help.

It's common enough for emulators - especially first attempts at emulators - to distinguish the forward and backward case, and even to use an addition or a subtraction accordingly. And you can certainly make that work. But it misses something, something which explains how the 6502 was such a simple and low-cost implementation, and also explains the page-crossing cycle.

The original explanation is at that level, I think, of a first attempt at understanding, probably without the benefit that we have today, of a peer group who can comment on an offering, and prior efforts which we can refer to. Back in the days of Apple Assembly Lines, I think community and communication were more limited.


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 13, 2018 1:13 pm 
Offline

Joined: Mon May 21, 2018 8:39 am
Posts: 41
Thank you all so much! :)


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 13, 2018 5:40 pm 
Offline

Joined: Mon May 21, 2018 8:39 am
Posts: 41
By the way, which book do you consider as the best for self-teaching 6502 assembly language?


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 13, 2018 8:54 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8521
Location: Southern California
Kris1978 wrote:
By the way, which book do you consider as the best for self-teaching 6502 assembly language?

No 65xx enthusiast should be without the Eyes & Lichty manual: https://wdc65xx.com/Programming-Manual/ The description there does not do it justice.

There are tons of 1970's 6502 programming books that may have been good for their time, but I think you will really be doing yourself a disservice if you limit yourself to just the original NMOS 6502 instructions. The CMOS 65c02 added new instructions and addressing modes and got rid of the bugs. I compiled a list of the differences at http://wilsonminesco.com/NMOS-CMOSdif/ . Unfortunately most of those often-recommended books came out before the CMOS version of the processor was out.

_________________
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?


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 13, 2018 9:01 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10940
Location: England
Zaks' Programming the 6502 was my start, and I'd recommend it. Leventhal is also well thought of.

These days, you could try running through the easy6502 tutorial online. As different people have different learning styles, you need to find what works for you.


Top
 Profile  
Reply with quote  
PostPosted: Tue Nov 13, 2018 10:15 pm 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
I'd be remiss if I didn't add this bit of code as well.

This is from my assembler, so, perhaps, more relevant.
Code:
int result = branchInst.getOperand() - (branchInst.getAddress() + 2);

Let's consider this simple use case:
Code:
0200         BNE LABEL
0202         NOP
0203         NOP
0204 LABEL:  LDA #0

When my assembler first assembles the BNE line, it stores in the Operand value of the instruction, that actual address of the destination. This is no different than if I were assembling, say, a JMP or an absolute LDA.

The Address value is the address of the instruction.

So, in this case, the values for Address is 0200, and the value for Operand is 0204

From the math, we get: result = 0204 - (0200 + 2), which equals 0204 - 0202, which equals 2.

And that's right!
Code:
0000 0200               .ORIGIN $0200
0001 0200
0002 0200 d0 02         BNE LABEL
0003 0202 ea            NOP
0004 0203 ea            NOP
0005 0204 a9 00 LABEL   LDA #0
0006 0206

Let's try it in reverse.
Code:
0200 LABEL   LDA #0
0202         NOP
0203         NOP
0204         BNE LABEL

Here, Operand = 0200, and Address = 0204.

So, result = 0200 - (0204 + 2), which equals 0200 - 0206, which equals -6.

-6 is FA in two's complement.
To convert positive 6 to -6, in two's complement, you negate the value and add 1.
Code:
6      = 0000 0110
Negate = 1111 1001
Add 1  = 1111 1010
FA     = 1111 1010

Thus:
Code:
0000 0200               .ORIGIN $0200
0001 0200
0002 0200 a9 00 LABEL   LDA #0
0003 0202 ea            NOP
0004 0203 ea            NOP
0005 0204 d0 fa         BNE LABEL

So, this code checks out.

This code is a little different from what the simulators are doing at the instruction level, so this little snippet may be more germane.


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 14, 2018 1:03 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
One quibble on terminology: I would say "to negate a two's complement value, you invert all the bits, then add 1." This clearly distinguishes the arithmetic operation from the logical one.

For the same reason, I like the way Lua uses a dedicated concatenation operator (..) instead of overloading addition (+).


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 14, 2018 8:54 am 
Offline

Joined: Mon May 21, 2018 8:39 am
Posts: 41
BigEd wrote:
Zaks' Programming the 6502 was my start, and I'd recommend it. Leventhal is also well thought of.

These days, you could try running through the easy6502 tutorial online. As different people have different learning styles, you need to find what works for you.

I have Zak's book (3rd edition) and I have to say that after some chapters of reading, it really confused me rather than helping me in order to learn. :D

I DO want to include the CMOS instruction set! :) 65c02 is the best 8-bit microprocessor IMHO and enhanced Apple IIe Platinum, the best 8-bit machine.

Well, my concern is that most of the books from the late 70's and early 80's "teaching" about 6502 assembly is that they have either a) lots of mistakes or b) the way of teaching is not the ideal for an amateur (at least this is how I feel)

Lance Leventhal's book looks like a very "serious/professional" job (600+ pages) and I may try it.....

And after GARTHWILSON's suggestion, I may as well try Eyes & Lichty manual......

Cheers!


Top
 Profile  
Reply with quote  
PostPosted: Wed Nov 14, 2018 9:10 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
From my point of view, the CPU itself is pretty simple and easy to learn. There are three basic areas to master: the operations of the ALU, the addressing modes, and the ways to influence control flow.

What's not so simple is learning to use it effectively, because its simplicity means it has some pretty severe limitations; working around those limitations introduces complexity in your code that isn't inherent in the algorithm for solving your underlying problem. That's why I often sketch out my solutions in C or pseudo-C before translating them into assembly.

With more modern CPU architectures like ARM, the limitations of the CPU itself are much less severe, so your code often looks much more like the algorithm required.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 25 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 9 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: