aslfaqs
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
aslfaqs [2025/09/09 13:28] – [What is a stack and how do I (safely) use it?] reggie | aslfaqs [2025/09/09 19:26] (current) – [Which registers do I use?] reggie | ||
---|---|---|---|
Line 20: | Line 20: | ||
==== Which registers do I use? ==== | ==== Which registers do I use? ==== | ||
- | For an address variable pick an index register, X, Y or even (user stack) U, it doesn' | + | For an address variable pick an index register, X, Y or even (user stack) U, it doesn' |
Choice between index or accumulator severely limits the operations you can perform. Only LD, ST, and CMP are common between all user registers. General arithmetic operations (NEG, AND etc.) are limited to the 8-bit accumulators, | Choice between index or accumulator severely limits the operations you can perform. Only LD, ST, and CMP are common between all user registers. General arithmetic operations (NEG, AND etc.) are limited to the 8-bit accumulators, | ||
Line 55: | Line 55: | ||
| | ||
A variable can be initialised at assembly time with the FCB (format constant byte) and FDB (format double byte) directives. This isn't a good idea except for constant data as the initialisation happens only when the program is first loaded, and not when it is run a second time. | A variable can be initialised at assembly time with the FCB (format constant byte) and FDB (format double byte) directives. This isn't a good idea except for constant data as the initialisation happens only when the program is first loaded, and not when it is run a second time. | ||
+ | |||
+ | ==== What is a bigendian? ==== | ||
+ | |||
+ | The 6809 is described by the Swiftian term " | ||
+ | |||
+ | It may seem the less sensible concept (which gets sillier as the design decision persists to 64-bithood), | ||
+ | |||
+ | addb ,x+ ; add low byte | ||
+ | adca ,x+ ; add high byte, using the carry | ||
+ | |||
+ | The assembler will take care of the byte ordering for us when we specify 16-bit constants, but it's something to bear in mind when converting programs between micros. | ||
+ | |||
+ | ==== What does the LEA instruction do? ==== | ||
+ | |||
+ | The LEA (Load Effective Address) instruction is used to initialise and modify index registers, ie. address variables. Only indexed addressing modes are available. | ||
+ | |||
+ | The jargon term ' | ||
+ | |||
+ | ldx #$0400 | ||
+ | ldb #$10 | ||
+ | lda $20,x ; Effective Address is X+$20, ie. $0420 | ||
+ | lda b,x ; Effective Address is X+B, ie. $0410 | ||
+ | |||
+ | Therefore a LEA instruction is simply performing addition: | ||
+ | |||
+ | leax $20,x ; X:=X+32 | ||
+ | leax b,x ; X:=X+B | ||
+ | |||
+ | Registers can be mixed as needed: | ||
+ | |||
+ | leay ,x ; Y:=X | ||
+ | leau 4,s ; U:=S+4 | ||
+ | Any indexed mode can be used, so index registers can be initialised using PC relative mode: | ||
+ | |||
+ | helloStr fcb "Hello World", | ||
+ | leax helloStr, | ||
+ | lda ,x+ ; A contains 72 (ASCII ' | ||
+ | |||
+ | Don't use the autoincrement modes, stick to constant offsets. Only the Zero flag of the condition codes is modified, and only for assignments to X and Y. This is so the stacks can be adjusted at the end of a subroutine without affecting the zero flag which might be checked by the caller. | ||
+ | |||
+ | leax ,x++ ; does nothing | ||
+ | leay ,-y ; not recommended | ||
+ | leay -1,y ; approved loop counter | ||
+ | bne loop | ||
==== How do I call a subroutine? ==== | ==== How do I call a subroutine? ==== | ||
Line 81: | Line 125: | ||
rts | rts | ||
- | Subroutines work by pushing the PC, which always contains the address of the next instruction, | + | Subroutines work by pushing the PC (Program Counter), which always contains the address of the next instruction, |
It's often a good idea to save and restore the values or register that are modified during the subroutine. If the last instruction before the RTS is a PULS we can optimise by combining the two: | It's often a good idea to save and restore the values or register that are modified during the subroutine. If the last instruction before the RTS is a PULS we can optimise by combining the two: | ||
Line 90: | Line 134: | ||
... | ... | ||
puls x, | puls x, | ||
+ | |||
+ | ==== What is a stack and how do I (safely) use it? ==== | ||
+ | |||
+ | A stack is a crucial concept of machine language programming. Basically it's a way of storing information and retrieving it later without too much worry about the fine details. You can PuSH registers onto the stack and PulL (sometimes known as POP) them off later. As long as pushing and pulling is done in matching order (like matching the brackets in an arithmetical expression) all we need to know is the name of the register (S or U in the case of the 6809). | ||
+ | |||
+ | People like to use physical analogies. For example, you're at your desk scribbling your memoirs when you need to check a detail in those photos you took on holiday in Ibiza. So you put your papers aside (onto the " | ||
+ | |||
+ | The point is that you can easily imagine retrieving your papers from the " | ||
+ | |||
+ | The system stack in a 6809 system is pointed to by the register S. Typically it will be initialised by the operating system at the highest address in RAM (the user program doesn' | ||
+ | |||
+ | There are two stacks on the 6809, U (User) and S (system); normally we don't bother much with U. Any combination of registers can be pushed and pulled in a single instruction. With one exception: we can't stack a stack register onto itself, which would have little point anyway. | ||
+ | |||
+ | pshs a,b,x,y,u | ||
+ | ldd #loopCount | ||
+ | loop: | ||
+ | pshs d | ||
+ | lda ,x+ | ||
+ | cmpa ,y+ | ||
+ | ... | ||
+ | puls d | ||
+ | subd #1 | ||
+ | bne loop | ||
+ | puls a, | ||
+ | |||
+ | If we PSHS A, modify A, then PULS A we get the same value back, and we don't have to care what was done to the stack before the push. Equally, our " | ||
+ | |||
+ | Calling subroutines relies on the system stack. A JSR pushes the Program Counter (PC) onto the stack, and a RTS pulls it. Thus the program can continue where it left off when the subroutine is over. Instead of using RTS, a subroutine might add the PC to a list of pulls at the end of a subroutine. | ||
+ | |||
+ | Stacking order is always the same, regardless of how the assembly language instruction is written. For example PSHS Y,B,A,X will be retrieved correctly by PULS D,Y,X. When all the registers are stacked on S it will look like this: | ||
+ | |||
+ | CC A B | ||
+ | S+ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ||
+ | | ||
+ | The S register can be used as an index register just like any other to access these values individually. But don't use negative offsets as you can't trust anything below the stack; the system may overwrite these bytes at any time, literally between instructions. | ||
+ | | ||
+ | You can use the LEA instruction to reserve space on the stack; LEAS -12,S is like a push, LEAS 12,S a pull. As before, you must do this in matching pairs. | ||
+ | |||
+ | pshs d,x,y,u | ||
+ | leas -4,s ; reserve 4 bytes to do what we like with | ||
+ | clr 2,s | ||
+ | ... | ||
+ | lda #4 | ||
+ | pshs a | ||
+ | lda 3,s ; the same address we cleared above | ||
+ | ... | ||
+ | puls a ; A equals 4 | ||
+ | leas 4,s | ||
+ | puls d,x,y,u,pc | ||
+ | |||
+ | The dangers of using the stack: | ||
+ | * There' | ||
+ | * Using the S register for our own purposes when a system interrupt may occur, overwriting our data seemingly at random. | ||
+ | * Generally not doing pushing and pulling in matching pairs and mixing up the data, with particular reference to return addresses. | ||
+ | |||
+ | ==== How do I do something a hundred times over? ==== | ||
+ | |||
+ | In a high-level language, typically you would set a variable to zero, then count up to a hundred: | ||
+ | |||
+ | n:=0; | ||
+ | REPEAT | ||
+ | writeln; | ||
+ | n:=n+1 | ||
+ | UNTIL n=100 | ||
+ | |||
+ | Assembly language prefers an idiom where you start at a hundred and count down to zero: | ||
+ | |||
+ | ldb #100 | ||
+ | repeatLoop: | ||
+ | lbsr writeln | ||
+ | decb | ||
+ | bne repeatLoop | ||
+ | |||
+ | Assuming the value of the loop counter isn't used anywhere, it's a little simpler this way. | ||
==== How do I write an IF / THEN statement? ==== | ==== How do I write an IF / THEN statement? ==== | ||
Line 130: | Line 248: | ||
==== How do I write a WHILE loop? ==== | ==== How do I write a WHILE loop? ==== | ||
- | This is very similar, except the comparison has to be made at the start of the loop. This is done simply by preceding the loop with a branch to the conditional | + | This is very similar |
WHILE left< | WHILE left< | ||
Line 147: | Line 265: | ||
bls plotLoop | bls plotLoop | ||
ldb bottom | ldb bottom | ||
- | |||
- | ==== What does the LEA instruction do? ==== | ||
- | |||
- | The LEA (Load Effective Address) instruction is used to initialise and modify index registers, ie. address variables. Only indexed addressing modes are available. | ||
- | |||
- | The jargon term ' | ||
- | |||
- | ldx #$0400 | ||
- | ldb #$10 | ||
- | lda $20,x ; Effective Address is X+$20, ie. $0420 | ||
- | lda b,x ; Effective Address is X+B, ie. $0410 | ||
- | |||
- | Therefore a LEA instruction is simply performing addition: | ||
- | |||
- | leax $20,x ; X:=X+32 | ||
- | leax b,x ; X:=X+B | ||
- | |||
- | Registers can be mixed as needed: | ||
- | |||
- | leay ,x ; Y:=X | ||
- | leau 4,s ; U:=S+4 | ||
- | Any indexed mode can be used, so index registers can be initialised using PC relative mode: | ||
- | |||
- | helloStr fcb "Hello World", | ||
- | leax helloStr, | ||
- | lda ,x+ ; A contains 72 (ASCII ' | ||
- | |||
- | Don't use the autoincrement modes, stick to constant offsets. Only the Zero flag of the condition codes is modified, and only for assignments to X and Y. This is so the stacks can be adjusted at the end of a subroutine without affecting the zero flag which might be checked by the caller. | ||
- | |||
- | leax ,x++ ; does nothing | ||
- | leay ,-y ; not recommended | ||
- | leay -1,y ; approved loop counter | ||
- | bne loop | ||
==== How do I access an array? ==== | ==== How do I access an array? ==== | ||
Line 289: | Line 374: | ||
; shift register A right B times | ; shift register A right B times | ||
; return result in D | ; return result in D | ||
- | pshs b ; B is now a local variable | + | pshs b ; B is now a local variable |
clrb ; destroy B to hold result | clrb ; destroy B to hold result | ||
tst ,s ; was B zero? | tst ,s ; was B zero? | ||
Line 352: | Line 437: | ||
leas 4,s ; release reserved space | leas 4,s ; release reserved space | ||
puls u,pc ; restore U and return | puls u,pc ; restore U and return | ||
+ | |||
+ | ==== Can I do recursion? ==== | ||
+ | |||
+ | Yes, there' | ||
+ | |||
+ | Beware of very deep recursion; a pixel flood-fill routine might call itself thousands of times, overwriting more than the available RAM with the system stack. | ||
==== What is position independence and when do I use it? ==== | ==== What is position independence and when do I use it? ==== | ||
Line 415: | Line 506: | ||
In general, you should always use position independent code for programs intended to have any sort of longevity. The exceptions are special cases such as embedded systems, ROM cartridges, and plain quick-and-dirty mash-ups. | In general, you should always use position independent code for programs intended to have any sort of longevity. The exceptions are special cases such as embedded systems, ROM cartridges, and plain quick-and-dirty mash-ups. | ||
+ | |||
| | ||
- | | ||
- | | ||
- | |||
- | | ||
- | ==== What is a stack and how do I (safely) use it? ==== | ||
- | A stack is a crucial concept of machine language programming. Basically it's a way of storing information and retrieving it later without too much worry about the fine details. You can PuSH registers onto the stack and PulL (sometimes known as POP) them off later. As long as pushing and pulling is done in matching order (like matching the brackets in an arithmetical expression) all we need to know is the name of the register (S or U in the case of the 6809). | + | ==== What are interrupts? ==== |
- | People like to use physical | + | An interrupt is a physical |
- | The point is that you can easily imagine retrieving you papers from the " | + | The details of interrupts are intricate |
- | The system stack in a 6809 system | + | gameLoop: |
+ | sync ; wait for interrupt | ||
+ | lbsr eraseSprite | ||
+ | lbsr moveSprite | ||
+ | lbsr drawSprite | ||
+ | bra gameLoop | ||
- | There are two stacks on the 6809, U (User) and S (system); normally we don't bother much with U. Any combination of registers can be pushed and pulled in a single instruction. With one exception: we can't stack a stack register onto itself, which would have little point anyway. | + | The 6809 has four hardware interrupts |
- | pshs a,b,x,y | + | An interrupt service routine must end with the instruction RTI (Return from Interrupt) so that the registers are unstacked correctly. This first pulls the CC and checks the E (Entire) bit. If set it then pulls all the registers from the stack, if clear it just pulls the PC. The fast interrupt (FIRQ) doesn' |
- | ldd # | + | |
- | loop: | + | |
- | pshs d | + | |
- | lda ,x+ | + | |
- | cmpa ,y+ | + | |
- | | + | |
- | puls d | + | |
- | subd #1 | + | |
- | bne loop | + | |
- | puls a,b,x,y,pc | + | |
- | If we PSHS A, modify A, then PULS A we get the same value back regardless of what was done to A previously. Equally, our " | + | Two other CC bits are involved with interrupts. Setting I disables (or masks) the IRQ response, and setting F disables |
+ | ==== What is reentrant code and how do I write it? ==== | ||
+ | A reentrant machine language program can be very roughly treated; you can stop it (saving its registers), restart it from the beginning, stop it again, and still have it continue correctly where it left off (restoring its registers of course). This is exactly what is needed for a multi-tasking operating system: the same code used by multiple processes. | ||
- | ==== Can I do recursion? ==== | + | The concept is related to interrupts and recursion. Whereas a recursive routine secures its variable so it's safe to call itself from just one point, a reentrant routine must try harder. Basically, it must allocate itself a new set of variables whenever it runs. |
- | ==== What is reentrant code and how do I write it? ==== | + | There' |
+ | |||
+ | ==== What is indirection | ||
==== How do I use virtual methods to create polymorphic objects? ==== | ==== How do I use virtual methods to create polymorphic objects? ==== |
aslfaqs.1757424507.txt.gz · Last modified: 2025/09/09 13:28 by reggie