Writing a while loop in assembly
Asked Answered
Y

4

9

I'm trying to write a while loop in assembly with a 6502 processor and I cannot figure out how to write the hexadecimal code. I've seen examples written using the shorthand where there is a label for where the loop should begin and end but I do not see anything for the actual hex code.

The two codes I see being useful are:

  1. Comparing a byte in memory to the X reg (shorthand: CPX, hex: EC). This sets the Z flag to zero if equal and
  2. Branch X bytes if Z flag = 0 (shorthand: BNE, hex: D0)
Yvor answered 16/5, 2011 at 17:14 Comment(6)
Why do you want hex codes instead of using an assembler?Kayleen
@Yvor "Hexdecimal code"? Which IDE, assembler or compiler are you using? What experience with 6502 assembly do you have already?Korea
I hope that you're not telling us that you are writing a 6502 program without using an assembler. What are you using for a development system? An Apple ][ ? Or what?Impervious
Trying to write a compiler that generates hex code so it can be run on a simulated 6502 microprocessor.Yvor
He may be using a microprocessor trainer (e.g. innovision-group.net/catalog/images/…). These are simple computers that you program directly in machine code. You can still use an assembler to generate the machine code, but you still have to manually enter the machine code into the system.Cypripedium
Yes, except instead of a separate machine it is simulated in a webpage. The code prints out the hexcode that is copied into the system.Yvor
I
6

Here's a place for you to start. The page features a cross-assembler that you can run on your PC. That could be a good dev platform for you.

Before doing anything, you have to understand the theory of operation of the 6502. Then you have to understand the software-development process that includes:

-- preparing a "source file," so called, of symbolic instructions that you call "shorthand"
-- using an assembler, translating that source file into machine instructions that the 6502 understands
-- loading the translation into the 6502
-- telling the 6502 to execute the translated machine instructions

Your example program tries to copy LEN memory bytes from SRC to DST.

You format it like this:

      LDX #0    ; Start with the first byte 
_LOOP LDA SRC,X ; load a byte from SRC into the A register 
      STA DST,X ; store that byte into DST
      INX       ; bump the index register to point to the next SRC and DST locations 
      CPX #LEN  ; have we moved LEN characters?  
      BNE _LOOP ; if not, go move the next one

After you have added more statement lines (like END, for example); and after you have defined SRC, DST, and LEN, you save the whole thing in a file called, let's say, cploop.txt.

Then you tell the assembler to translate it. The assembler comes out with a file of binary 6502 machine code that cam be represented as the hex bytes you're talking about.

You feed that file of machine code to the simulated 6502. Then you somehow tell the 6502 to execute the operations that the machine code embodies.

Impervious answered 16/5, 2011 at 17:27 Comment(5)
Thanks, that helps but I am still unclear on how something like this example from that website: " LDX #0 ;Start with the first byte _LOOP LDA SRC,X ;Move it STA DST,X INX ;Then bump the index ... CPX #LEN ;... until we reach the limit BNE _LOOP " can be represented in hex. The _loop label is important in the shorthand there but I do not see how to have something like that in actual machine code. Sorry, I have no idea how to format that code.Yvor
@Yvor You really need to split your problem into "how can I write this code in 6502 machine code" and "how can I then convert that code to hexadecimal notation". As it stands, it's hard to help you.Korea
@Yvor -- There are two steps: (1) write your program in assembly language, what you call "shorthand" (a term I've never heard, btw); (2) give that file of assembly language to the assembler, which then spits out the hex codes you want.Impervious
Incidentally, decremental loops are more efficient - they omit the need for the compare instruction, and as well as saving those two bytes are also anywhere from 2-6 clocks faster per iteration (depending on the compare and addressing mode): LDX #LEN ; Start with the last byte _LOOP LDA SRC,X ; load a byte from SRC into the A register STA DST,X ; store that byte into DST DEX ; bump the index register to point to the next SRC and DST locations BNE _LOOP ; if not zero, go move the next oneFiredrake
@Yvor - one thing you seem to be stuck on is the existance of thhe "_LOOP" label in the assembly code. This is just an assembler directive that represents the address at the location that instruction starts. It isn't assembled into anything in the machine code that's output at the point it's defined. When it's used as part of an operand (i.e., the arguments to the assembly instructions) it is replaced with the address of the instruction it's at when it's defined. On the 6502 it's written out least significant byte first - address $0300 is written out "00 03" in machine code.Alicealicea
A
3

Here's an example showing the correspondence between assembly (what you call "shorthand") and machine code. First, here's the assembly code for the algorithm, with some parameters abstracted away:

* = 4000          ; This is the address where our program will be stored

      LDX #len
loop  LDA src,X 
      STA dest,X 
      DEX       
      BNE loop

Of course, you can't turn that directly into machine code. You also need to fill in the values of len, src and dest:

src = $1234
dest = $5678
len = 10

The thing to understand about the loop name is that just like src is assigned the value $1234, loop will be assigned the address of the instruction after it. So in this case, since LDX #len takes up 2 bytes (as I'll show you shortly), loop is set to $4000 + 2 = $4002. This is done automatically by the assembler, but of course you could do all this on paper as well.

So what is the 6502 machine code for the above assembly program?

A2 0A
BD 34 12
9D 78 56
CA
D0 F7

How do I know this? Well, I've just pasted the above program into the online 6502 assembler at http://www.masswerk.at/6502/assembler.html. It even shows you the detailed mapping between assembly and machine code:

4000        LDX #LEN        A2 0A
4002 LOOP   LDA SRC,X       BD 34 12
4005        STA DEST,X      9D 78 56
4008        DEX             CA
4009        BNE LOOP        D0 F7
400B

Note how the actual value of LOOP is not even used to compute the machine code for BNE LOOP, only its relative address compared to the BNE instruction itself: F7 is -9, and the difference between $400B and $4002 is -9!

So if you were to do this by hand, you'd just translate everything else into machine code, then when you get to a jump, you compute the difference between the next instruction's starting address and the jump destination's address. It should be negative for backwards jumps and positive for forward jumps.

Aminaamine answered 12/6, 2014 at 3:8 Comment(0)
P
2

The branch instructions take a single-byte signed relative address operand, which is added to the address of the next instruction to yield the branch target. Since the branch instruction always occupies 2 bytes, the target address is the address of the branch instruction plus the (sign-extended) operand minus 2.

Examples:
$D0 $00: no-op: the branch goes to the next instruction regardless of the condition
$D0 $FE: branch points back to itself, creating an infinite loop if Z=0.

Pyretotherapy answered 18/8, 2013 at 18:11 Comment(1)
Upvote for decoding and answering the badly worded question. ; - )Richmal
U
1

A while statement really means:

  1. test a condition
  2. if the condition is false, go to 5
  3. do something
  4. go back to 1 (a simple JMP or branch)
  5. rest of program

With 6502, none of this is going to be extremely simple unless you can make a lot of assumptions. If the condition you are testing is always going to be a register, the compare instructions (cmp, cpx, cpy) and branch instructions are obviously what you need for 1.

If it's going to be a single byte stored in memory, then you need to load that byte, and then compare it.

If it's a 16-bit value stored in two bytes, you need to load and test each value of the byte.

Dealing with floats? If you have written or have available to you a floating point package (such as the Commodore 64 ROM BASIC floating-point routines) you'll need to use them.

You can see why high-level languages have data types.

So really, it depends on the type of data you are dealing with, but any implementation of while in 6502 should pretty much follow the above.

The specific case you identify in your question is OK if you know the data you will compare will always be in X and that your destination will always be +127/-128 bytes away (range limit of Bxx instructions).

Ulna answered 18/8, 2013 at 14:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.