sd-8516_assembly_language
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| sd-8516_assembly_language [2026/01/20 03:24] – appledog | sd-8516_assembly_language [2026/01/29 12:09] (current) – appledog | ||
|---|---|---|---|
| Line 4: | Line 4: | ||
| Welcome to the SD-8516 Assembly Language Reference Manual! Inside here you will find detailed information and discussion about programming your new SD-8516. | Welcome to the SD-8516 Assembly Language Reference Manual! Inside here you will find detailed information and discussion about programming your new SD-8516. | ||
| - | == A Guide to Registers | + | == Lesson 1 : Memory, Registers and Flags |
| + | * Lesson 1: " | ||
| + | * Time: 5 min | ||
| + | * Learn: | ||
| + | ** Registers: | ||
| + | ** Flags: N Z C V | ||
| + | ** Memory: Flat memory model | ||
| + | |||
| + | In general there are some things you should know and consider first when learning SD-8516 assembly language programming. | ||
| + | |||
| + | One, it's a flat memory model, which goes from $0 to $03FFFF. That's 4 banks of 64k. | ||
| + | |||
| + | Two, word registers are 16 bits. To access them as high byte or low byte use H and L. For example X is 16 bits, but XH is the high byte, and XL is the low byte. Similarly for every register, such as Y, YL is the low byte and YH is the high byte. For A, AH and AL, and so on. | ||
| + | |||
| + | Third, because memory requires three bytes to address, you can only address bank 0 if you use a 16 bit register. To address upper memory, include a bank byte. Like this: BLX, ELM, or GLD, etc. This means BL+X (BL is the bank byte and X is the address within that bank). Or if you use ELM it EL + M, and so on. Always remember that these are not independent registers, but if you modify EL or M it will modify ELM, and if you modify ELM it will modify EL and M as well. | ||
| + | |||
| + | Finally, the concept of flags. Flags are one bit status registers. Some operations modify flags. For example, if you load a zero, the zero flag will be set. if you ADD two numbers which don't fit in a word, the overflow or carry flags might be set. The flags will be explained more in detail in the lessons teaching the individual instructions and how they manage flags. For now know that there are four primary flags that are set via operations: | ||
| + | |||
| + | * N -- Negative. If an operation produces a number that //looks like// a signed negative number, this flag will be set. Most of the time you can just ignore this. | ||
| + | * C -- Carry flag. If you add two numbers and it doesn' | ||
| + | * V -- Overflow flag. In general if there is more overflow than in carry, this is set | ||
| + | * Z -- Zero flag. If an operation produces or sees a zero, this is set. | ||
| + | |||
| + | Why flags? | ||
| + | |||
| + | During decision making, you can use flags to control the program flow. This will be discussed in lesson 5: Program flow. | ||
| + | |||
| + | == Lesson 2 : Load-Store Architecture | ||
| + | * Lesson 2: " | ||
| + | * Time: 5 min | ||
| + | * Learn: | ||
| + | ** Registers: A and B | ||
| + | ** Opcodes: LDA, LDB, STA, STB. | ||
| + | ** Addressing modes: Immediate mode (numbers). Memory mode (memory reference). | ||
| + | |||
| + | Let's dive in to the basic idea behind SD-8516 assembly language programming! If you've ever programmed before, it's similar but different to a high level language. It is similar because there are functions and commands that take operands, and it is different because the functions are very simple building blocks, and there are only a limited number of integer variables that you can use. | ||
| + | |||
| + | The first concept is " | ||
| + | |||
| + | The commands to load and store are LD and ST (load and store) followed by the register and an operand. For example, | ||
| + | |||
| + | ; * LDA means "load into A," | ||
| + | ; * LDB means "load into B". | ||
| + | |||
| + | LDA #56 ; Load the decimal number 56 into the variable A. | ||
| + | LDA [#56] ; Load the value in memory location #56 into the variable A. | ||
| + | |||
| + | That's it for load operations. You can load a variable with a number either from memory or from a number directly. You cannot load a variable from another variable. This is invalid: | ||
| + | |||
| + | LDA B ; this doesn' | ||
| + | |||
| + | Next let's look at store operations. Store operations only write to memory. You can't write to a number, that's impossible, and you can't write to another variable. That would violate the " | ||
| + | |||
| + | STA [$1000] | ||
| + | |||
| + | Hex 1000 in decimal is 4,096. You can use decimal numbers via the '#' | ||
| + | |||
| + | Now you know how to load and store information from memory to the variables! | ||
| + | |||
| + | == Lesson 3: Operations | ||
| + | * Lesson 3: Operations | ||
| + | * Time: 5 min | ||
| + | * Learn: | ||
| + | ** Registers: C and D | ||
| + | ** Opcodes: ADD, SUB | ||
| + | |||
| + | Now, once you have access to information in the computer' | ||
| + | |||
| + | ADD A, B ; Adds A and B and stores the result in A. | ||
| + | ADD B, C ; Adds B and C and stores the result in B | ||
| + | |||
| + | As you can see, the ADD command has a source register and a destination register. The destination is first and the source is second. So ADD A, D means D will be added to A, and A will hold the result. All of the registers such as A, B, C, D can be used. However by convention we like to use A and B for simple math. | ||
| + | |||
| + | Anyways, you can also do these things: | ||
| + | |||
| + | SUB A, B ; Subtract A - B and store the result in A. | ||
| + | |||
| + | == Lesson 4: Advanced Operations | ||
| + | * Lesson 4: Advanced Operations | ||
| + | * Time: 5 min | ||
| + | * Learn: | ||
| + | ** 32-bit Register Pairing | ||
| + | ** MUL and DIV | ||
| + | |||
| + | Some processors such as the venerable 6502 (6510, etc.) stop with ADD and SUB, but we have a more advanced 8516, so we can also MUL and DIV. However, MUL and DIV are special operations; observe: | ||
| + | |||
| + | MUL A, B ; multiply A and B and store the result in AB. | ||
| + | |||
| + | Storing the result in AB? What's that? The SD-8516 has a special 32 bit extended operation for multiplication. The result is stored in A, but if the result would not fit in a word, the extra information is in B and the overflow flag is set. For example. what is $FFFF times $FFFF? It obviously cannot fit in one word. However, the result ($FFFE0001) does fit into two words. So in this case, A would be $FFFE and B would be $0001. This kind of overflow allows muliplication of larger numbers. Before anyone says "Why not just check overflow", | ||
| + | |||
| + | MUL AB, CD ; Multiply AB by CD and store in ABCD. | ||
| + | |||
| + | Now, there is no way to operate on a 64 bit number (ex. ABCD) however, the result will be stored there, for you to interpret. That's the power of the SD-8516, it can multiply quite nicely! If you wanted, you could extend 64 bit operations via software. It would be slow, but workable. " | ||
| + | |||
| + | DIV A, B ; Divide A by B and store the answer in A and the remainder in B | ||
| + | |||
| + | The special properties of DIV allow you to perform modulus for free, or, in a modulus operation you can get the DIV for free. You can also do things like: | ||
| + | |||
| + | DIV AB, CD ; Divide AB by CD and store in AB and modulus (remainder) in CD. | ||
| + | |||
| + | You can also divide 32 bit paired registers. The powerful MUL and DIV capabilities of the SD-8516 set it apart from other CPUs of the era. | ||
| + | |||
| + | == Lesson 5: Flow Control (Branching) | ||
| + | * Lesson 5: Flow Control (Branching) | ||
| + | * Time: 10 min | ||
| + | * Learn: Assembler Labels, CMP, JZ, RET | ||
| + | |||
| + | Tying everything together, what do you think this program does? | ||
| + | |||
| + | LDA [$00] | ||
| + | LDB [$02] | ||
| + | CMP A, B | ||
| + | JZ @equal | ||
| + | |||
| + | not_equal: | ||
| + | LDC $01 ; error code #1 | ||
| + | RET | ||
| + | |||
| + | equal: | ||
| + | LDC $00 ; no error | ||
| + | RET | ||
| + | |||
| + | The program loads the word (two bytes) at $00 ($00 and $01) into A, and the word at $02 ($02 and $03) into B. Then it compares them. If they are equal, the zero flag is set. Depending on this we set our return code, which here by convention is C. But it could be anything. We have thus demonstrated the ability to compare registers and make a decision on program contorl flow based on that comparison. This has applications everywhere, from making sure a cursor is within the limits of the screen, to testing if a character is uppercase or lowercase, and many, so many applications that we cannot list them here. | ||
| + | |||
| + | CMP is the fundamental flow control operation. Compare two registers and JZ if equal. Fall-through is the not-equal case. You could also use JNZ instead and fall-through the "is equal" case. Now you know how to control the flow of your programs! | ||
| + | |||
| + | === How CMP affects flags | ||
| + | CMP works by doing a simple test: | ||
| + | |||
| + | CMP A, B ; We are doing A - B! | ||
| + | |||
| + | Yes that's right, it's doing A - B, but it isn't doing it to store the value in A. It's testing if the result is 0 or not. If the result is zero, it sets the zero flag; ZF = 1. If it's not equal, then it is either ABOVE or BELOW zero. Imagine CMP 5,5 versus CMP 5,10 versus CMP 10,5: | ||
| + | |||
| + | CMP 5, 5 ; | ||
| + | CMP 5, 10 ; 5 - 10 = -5. No zero. ZF = 0 | ||
| + | CMP 10, 5 ; 10 - 5 = 5. No zero. ZF = 0 | ||
| + | |||
| + | So because it's equal, it produces a zero. Seeing the zero, the CPU sets the zero flag. Then you can control program flow by JZ (jump-if-zero) and JNZ (jump-if-not-zero). | ||
| + | |||
| + | But there is more! As you see above, there are actually three situations that can occurr. It can be equal, or it can be less than zero, or above zero. You will notice that if A is less than B, the number is negative -- or, "less than". And, if the number in A is greater than B, then A-B produces a positive number, which is " | ||
| + | |||
| + | CMP 5, 5 ; | ||
| + | CMP 5, 10 ; 5 - 10 = -5. Yes borrow --> carry is NOT set: CF = 0 | ||
| + | CMP 10, 5 ; 10 - 5 = 5. No borrow --> carry is set: CF = 1 | ||
| + | |||
| + | Therefore, if carry is set, we know that A is less than B. | ||
| + | |||
| + | But wait! There' | ||
| + | |||
| + | CMP 5, 5 ; | ||
| + | CMP 5, 10 ; 5 - 10 = -5. Yes negative. N flag set! | ||
| + | CMP 10, 5 ; 10 - 5 = 5. Not negative. N flag NOT set! | ||
| + | |||
| + | So you can also use the N flag. So here is the situation: | ||
| + | |||
| + | * If ZF=1 then A and B are equal. | ||
| + | * If ZF = 0, then look at CF or NF | ||
| + | ** If CF is set, A is greater than B. | ||
| + | ** If NF is set, A is less than B. | ||
| + | |||
| + | There you go! You can do this now, to branch on each condition: | ||
| + | |||
| + | * JZ @A_equals_B | ||
| + | * JC @A_greater_than_B | ||
| + | * JN @A_less_than_B | ||
| + | |||
| + | This is the foundation of how an IF statement works, or the ternary operator in C. | ||
| + | |||
| + | |||
| + | == Lesson 6: The Boring Lesson | ||
| + | * Lesson 6: The Boring Lesson | ||
| + | * Time: 5-10 min | ||
| + | * Learn: AND, OR, XOR, NOT | ||
| + | |||
| + | The problem with computer science is that sometimes you have to learn some very boring things and you might not understand why they are important until later. Please understand that this is lesson #6, a fundamental lesson, and even if you find it boring, it will all work out for the best -- //trust me bro.// | ||
| + | |||
| + | === AND | ||
| + | AND is a classic logic gate. When two signals are 1, it shows result 1. I.E. 1 and 1 is 1. If one of the signals is down (like, an actual electrical signal in a wire) then the result is zero. This is OFTEN but not always an analogy for a light switch. There is always power in your house (A is 1) but only when the switch is ON (=1) is the light on. So you need 1 power and 1 switch and when they are both ON, then the light is ON. If they are both off, then what happens? Nothing! Absolutely nothing! Watch: | ||
| + | |||
| + | LDA 1 | ||
| + | LDB 1 | ||
| + | AND A, B ; | ||
| + | |||
| + | LDC 1 | ||
| + | LDD 0 | ||
| + | AND C, D ; | ||
| + | |||
| + | LDE 0 | ||
| + | LDF 1 | ||
| + | AND E, F ; | ||
| + | |||
| + | LDG 0 | ||
| + | LDI 0 | ||
| + | AND G, I ; | ||
| + | |||
| + | And is often displayed as an easy to read table: | ||
| + | | | ||
| + | | | 0 | 1 | | ||
| + | | 0 | 0 | 0 | | ||
| + | | 1 | 0 | 1 | | ||
| + | |||
| + | The AND means " | ||
| + | |||
| + | === Binary | ||
| + | Now wrap your head around the power of binary! | ||
| + | |||
| + | LDA 0b01000111 | ||
| + | LDD 0b00010110 | ||
| + | AND A, D ; A is now what? 0b00000110 | ||
| + | |||
| + | The bits in A that were also set in D remain. The bits that weren' | ||
| + | |||
| + | LDA 0b01000111 | ||
| + | LDD 0b00000100 | ||
| + | AND A, D ; A is now 0b00000100 | ||
| + | JNZ @bit_3_is_set | ||
| + | JZ @bit_3_is_not_set | ||
| + | |||
| + | Since if bit 3 is not set, AND A, D produces a zero, you can branch flow control based on bits. So for example, if your CPU has a " | ||
| + | |||
| + | Other commands that work in a similar way are OR, XOR, and NOT. | ||
| + | |||
| + | === OR | ||
| + | OR works by saying "Set the bit if either A or B is set." So it will be 1 unless both are zero. Thats useful for detecting thieves. If any one of the laser traps detect a thief, the alarm has to go off. Not all of them at once, but any one, anywhere, and the alarms go off! That's how OR works. | ||
| + | |||
| + | === XOR | ||
| + | XOR is " | ||
| + | |||
| + | * 0b00010001 | ||
| + | * 0b00010010 | ||
| + | * XOR | ||
| + | * 0b00000011 | ||
| + | |||
| + | The bits that were the same are 0, the bits that are diffrent are 1. Please don't ask me why this is useful, i'm sure i'll remember why later. Ha. | ||
| + | |||
| + | === NOT | ||
| + | Finally, NOT. Not inverts a number. | ||
| + | * 0b00000001 ; This is a 1. | ||
| + | * NOT | ||
| + | * 0b11111110; This is 254 in decimal or FE in hex. Commonly written as #254 or $FE in assembler convention. Or 0xFE. Or FEh. | ||
| + | |||
| + | Why is NOT useful? NOT gives you the negative version minus one. So to make a number negative. NOT it and add one. In the case of 1, this is FF. This means you had a zero, subtracted one, and it //rolled over// to FF. So FF is negative one! We will explain negative numbers later. For now, FF is 255. Not -1. But, well, that's what NOT is for. | ||
| + | |||
| + | === The End of the Boring Lesson | ||
| + | If this lesson was confusing I'm sorry. The fact is you're not going to understand binary logic until later when you see it in action and see how it actually is used. For now, just try to remember the basic ideas. Or, failing that, just remember that there is an AND, and OR, an XOR, and a NOT. Everything else is based on those. | ||
| + | |||
| + | == Appendix I: Registers | ||
| There are sixteen general purpose registers available for use> Here they are, with a short comment on name and purpose. Of course, since they' | There are sixteen general purpose registers available for use> Here they are, with a short comment on name and purpose. Of course, since they' | ||
| Line 53: | Line 299: | ||
| This is because during MUL operations overflow moves into the high byte, otherwise it stays in the original register. EX. MUL A, B moves into A but overflow goes into B. | This is because during MUL operations overflow moves into the high byte, otherwise it stays in the original register. EX. MUL A, B moves into A but overflow goes into B. | ||
| - | == No Borrow Carry | + | == Appendix II: Flags |
| + | |||
| + | === Carry Flag: No Borrow Carry | ||
| The SD-8516 follows in the grand tradition of no borrow carry. Here's how to understand it: | The SD-8516 follows in the grand tradition of no borrow carry. Here's how to understand it: | ||
| Line 92: | Line 340: | ||
| CARRY = 0 if A < B | CARRY = 0 if A < B | ||
| - | == Memory Trick | + | ==== CAM/ABC mnemonic |
| Just remember C = A ≥ M. You can also say it as ABC; remember your ABC's: C = A ≥ B or A ≥ B = C. The sign points in the direction you read the letters, i.e. >= so it is easy to remember. " | Just remember C = A ≥ M. You can also say it as ABC; remember your ABC's: C = A ≥ B or A ≥ B = C. The sign points in the direction you read the letters, i.e. >= so it is easy to remember. " | ||
sd-8516_assembly_language.1768879462.txt.gz · Last modified: by appledog
