Category:68000 Assembly: Difference between revisions

m
 
(18 intermediate revisions by the same user not shown)
Line 1:
{{stub}}{{language}}
{{merge language | M680x0 }}
{{language}}
68000 assembly is the assembly language used for the Motorola 68000, or commonly known as the 68K. It should not be confused with the 6800 (which predates it). The Motorola 68000 is a big-endian processor with full 32-bit capabilities (despite most systems that use it being considered 16-bit.) It was used in many computers such as the Amiga or the Canon Cat, as well as game consoles such as the Sega Genesis and Neo Geo.
 
Line 22 ⟶ 24:
 
This difference isn't usually relevant in the majority of situations, so don't concern yourself too much. It's much more important when doing [[6502 Assembly]] where registers are smaller than the address space.
 
===Notation Conventions===
How you write the source code depends on your assembler and the syntax it uses. This page is written using Motorola syntax but there is also Milo syntax which has different conventions.
 
How you go about defining numbers or text in your code varies wildly between assemblers. I'm using VASM and these are the rules I have to follow, but your assembler may be different.
 
* A number with a # in front represents a constant, literal value. For example, the 3 in <code>MOVE.B #3,D0</code> represents the number 3.
 
* A number without a # in front represents a memory location. For example, the 3 in <code>MOVE.B 3,D0</code> represents <i>the byte stored at memory address <code>$00000003</code></i>.
 
* A number that doesn't have a $ or % prefix is a decimal (base 10) value.
 
* A number that begins with a $ is a hexadecimal value.
 
* A number that begins with a % is a binary value.
 
* Single and double quotes can be used for ASCII values. When you type <code>MOVE.L "EGGS",D0</code> for example, this is equivalent to <code>MOVE.L #$69717183,D0</code>.
 
* The operand of <code>BTST</code>,<code>BSET</code>,<code>BCLR</code>, or <code>BCHG</code> represents a bit position, starting at the rightmost binary digit as 0 and counting up from right to left. For example, <code>BCLR #3,D0</code> performs the same bitwise operation that you would get from doing <code>AND.B #%11110111,D0</code>.
 
* The operand before the comma is the "source", and the operand after is the "destination." For example, <code>MOVE.L D3,D2</code> takes the value in D3 and stores it into D2, not the other way around. This is the opposite of x86 and ARM, which have the source on the right and the destination on the left.
 
 
Data blocks, on the other hand, begin with <code>DC.B</code>, <code>DC.W</code>, or <code>DC.L</code> and each represents a constant numeric value. Strangely, you do NOT prefix these with # to signify them as constants (doing so will cause an error on most assemblers). However, you can use the $ or % modifiers to denote hexadecimal or binary.
 
Keep in mind that there is no requirement to use hexadecimal, decimal, or binary in your source code. It all gets converted to binary anyway. However, it is recommended to use the notation that is appropriate for how your data is meant to be interpreted, for readability purposes.
 
===Data Registers===
Line 40 ⟶ 68:
MOVE.L D2,(A5) ;store the contents of D2 into the memory address pointed to by A5.</lang>
 
 
Note that it's also possible to transfer values to/from memory directly, without involving address registers at all. For constant memory locations, this is fine. However, the real strength of the address registers is in their pre-decrement and post-increment modes, which constant memory locations cannot use.
 
<lang 68000devpac>MOVE.L ($00FF0000),D0
MOVE.W D1,($00FFFFFE)
MOVE.W ($00FF0000),($00FF1000)</lang>
 
The use of parentheses is not required on most assemblers, but can be used as a reminder to someone reading your code that these represent the values stored at the specified memory locations rather than literal numbers.
 
====Post-Increment====
Line 78 ⟶ 114:
 
====The Stack====
The 68000's stack is commonly referred to as <code>SP</code> but it is also address register <code>A7</code>. This register is handled differently than the other address registers when pushing bytes onto the stack. A byte value pushed onto the stack will be padded to the <b>right</b> with zeroes. The stack needs to pad byte-length data so that it can stay word-aligned at all times. Otherwise the CPU would crash as soon as you tried to use the stack for anything other than a byte! If a byte is popped, the zeroes are placed on the left side of the lower word of the data register, and the actual byte goes in the right side.
 
<lang 68000devpac>MOVE.B #$FF,-(SP) ;push #$FF then #$00 onto the stack, in that order.
MOVE.B (SP)+,D0 ;The values are popped in the order #$00 #$FF.</lang>
 
You can abuse this property of the stack to quickly swap bytes around. Suppose you had a number like <code>#$11223344</code> stored in <code>D0</code> and you wanted to change it to <code>#$11224433</code>:
 
<lang 68000devpac>MOVE.W D0,-(SP) ;push #$3344 onto the stack
MOVE.B (SP)+,D0 ;pop them in the order #$44 #$33.</lang>
 
===Length===
Line 107 ⟶ 135:
MOVE.L #0,D3 ;D3 = #$00000000</lang>
 
Loading immediate values into address registers is different. You can only move words or longer into address registers, and if you move a word, the value is sign-extended. This means that if the top nibble of the word is 8 or greater, the value gets padded to the left with Fs, and is padded with zeroes if the top nibble is 7 or less. ItIf you're adding a constant value less than 7FFF to an address, it's bestusually safe to alwaysuse movethe longsword intolength addressoperation, registers.which Thattakes wayless youbytes knowto whatencode you'rethan the long length gettingversion.
 
<lang 68000devpac>MOVEA.W #$8000,A4 ;A4 = #$FFFF8000. Remember the top byte is ignored so this is the same as #$00FF8000.
Line 124 ⟶ 152:
 
* X: The eXtend flag is bit 4 of the CCR, and is similar to the carry flag. It gets set and cleared often for the same reasons and is used with the <code>ADDX</code>, <code>SUBX</code>, <code>NEGX</code>, <code>ROXL</code>, and <code>ROXR</code> commands. Why the 68000 has both this and the carry flag, I still don't know.
 
 
* N: The negative flag is bit 3 of the CCR, and is set when the last operation resulted in a "negative" value. What constitutes a negative value depends on the size of the last operation - for <code>.B</code> instructions, $80-$FF. For <code>.W</code> instructions, $8000-$FFFF, and for <code>.L</code> instructions, $80000000-$FFFFFFFF.
 
 
* Z: The zero flag is bit 2 of the CCR and works like you would expect - it's set whenever an operation results in zero. Unlike x86 Assembly, this also includes moving 0 directly into a register, clearing a register <b>or memory</b> with <code>CLR</code>, etc.
 
* V: The overflow flag is set whenever a math operation results in a value crossing the $7F-$80 boundary. (Wraparound from 00 to FF doesn't count as overflow, but it does set the carry flag.)
 
* V: The overflow flag is bit 1 of the CCR. It is set whenever a math operation results in a value crossing the $7F-$80 boundary. (Wraparound from 00 to FF doesn't count as overflow, but it does set the carry flag.)
* C: The carry flag is set when a math operation results in a carry or borrow. Rolling over from FF to 00, or a 1 getting "pushed out" via a bit shift or rotate, set the carry flag. When using <code>CMP</code>, the carry flag determines the unsigned magnitude comparison. Carry set is less than, carry clear is greater than or equal.
 
 
* C: The carry flag is bit 0 of the CCR. It is set when a math operation results in a carry or borrow. Rolling over from FF to 00, or a 1 getting "pushed out" via a bit shift or rotate, set the carry flag. When using <code>CMP</code>, the carry flag determines the unsigned magnitude comparison. Carry set is less than, carry clear is greater than or equal.
 
 
 
Line 138 ⟶ 171:
 
==The Vector Table==
Certain memory locations have special meanings to the 68000 CPU. Most of these are used for system calls or exception handling. Sixteen of them can be accessed with the <code>TRAP #?</code> command, which takes an immediate constant ? ranging from 0 to 15 as an operand. This effectively equates to a <code>JSR</code> to the address stored at <code>$00000080 + (4*?)</code>. For the most part, what each <code>TRAP</code> does depends on the system's firmware and so you'll need to read the documentation. ThisSome featuremachines isallow notthe usableuser onto everydefine system;their onown theTRAPs Segaat Genesis$00000080 for- example$000000BF, theyothers allare becomehardcoded nullin. pointersEven regardlessothers ofwill whatlikely take a "middle ground" and have the hard-coded trap read a function pointer from some other address that the user is allowed to write to, youand actuallyjump putto therethat.
 
The 68000's vector table contains more than this, but to keep things simple there will be a few things omitted.
Line 145 ⟶ 178:
* The next 8 longwords are the hardware traps. Each represents the memory location of a function or procedure meant for a hardware error (the 68000 doesn't have segmentation faults but it's a similar concept. Among them include handlers for division by zero, signed overflow, etc.)
* Interrupt requests and user-defined traps go here as well.
 
 
Programming your own trap is a lot like programming a typical subroutine. However, there are a few differences:
* The statement to return back to your regular program must be <code>RTE</code> rather than <code>RTS</code>. Otherwise, you'll most likely crash the CPU.
 
* It's best to push all registers (D0 thru D7 and A0-A6) onto the stack at the start and pop them off at the end. This is not required for the traps you call with the <code>TRAP #?</code> command, but for the others it's absolutely necessary, since you can't know in advance when they'll happen. You can leave out A7 when doing this since that's the stack pointer itself.
 
==Interrupts==
The 68000 supports 7 different interrupts, often called IRQs or Interrupt Requests. Enabling interrupts is often a twofold process: first, the interrupt source must be enabled, which is usually an implementation-defined process that involves interacting with memory-mapped ports. Second, the status register must be set accordingly to allow the interrupt to occur, which can be achieved with <code>MOVE #$2x00,SR</code> where X is the desired interrupt level. X can range from 0 to 7, and for an interrupt to occur, its interrupt level (which is determined by the hardware implementation and the placement of the desired address in the vector table) must be greater than X or it will not happen (even if the source is enabled.)
 
==Alignment==
Line 185 ⟶ 227:
;if we did MOVE.W (A0)+,(A1)+ now we'd crash. First we need to adjust the pointers.
MOVE.B (A0)+,D7 ;dummy read to D7. Now A0 is word aligned.
MOVE.B (A1)+,D7 ;dummy read to D7. Now A0A1 is word aligned.
MOVE.W (A0)+,(A1)+ ;copy $0345 to a new memory location</lang>
 
Line 196 ⟶ 238:
#[[https://www.chibiakumas.com/68000/ ChibiAkumas Motorola 68000 Tutorial]]
#[[http://www.easy68k.com/paulrsm/doc/trick68k.htm 68000 Tricks and Traps]]
 
{{merge language | M680x0 }}
1,489

edits