User Tools

Site Tools


aslfaqs

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
aslfaqs [2025/09/09 13:28] – [What is a stack and how do I (safely) use it?] reggieaslfaqs [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't matter which. For a character or small numberic variable pick an accumulator, A or B, again it doesn't matter which. If you need 16-bit values use D, which is the two accumulators combined (A high, B low).+For an address variable pick an index register, X, Y or even (user stack) U, it doesn't matter which. For a character or small numeric variable pick an accumulator, A or B, again it doesn't matter which. If you need 16-bit values use D, which is the two accumulators combined (A high, B low).
  
 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, except for ADD and SUB which are available to 16-bit D. Index registers uniquely have the LEAX operation, and the stacks (U and S) have PSH and PUL. 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, except for ADD and SUB which are available to 16-bit D. Index registers uniquely have the LEAX operation, and the stacks (U and S) have PSH and PUL.
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 "big-endian"; the Intel 8080, like many early micros, is "little-endian". This refers to the way 16-bit values are stored in memory - big byte first, or little byte first.
 +
 +It may seem the less sensible concept (which gets sillier as the design decision persists to 64-bithood), but it's simpler to design an 8-bit microprocessor as little-endian. For example, to add a 16-bit constant 8 bits at a time, storing the low byte first makes it easier to deal with the carry. If there was no ADDD instruction for the 6809 we might do this:
 +
 +    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 'effective affress', in the context of a LDA instruction for example, is simply the address from which the register A is loaded:
 +
 +    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",0
 +    leax helloStr,pcr      ; point X to string
 +    lda ,x+                ; A contains 72 (ASCII 'H')
 +
 +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, onto the system stack (S register), then loading the PC with the destination address. The next RTS instruction should simply PULS the PC's value from the stack. Note that this won't work if the stack has been modified and not correctly restored.+Subroutines work by pushing the PC (Program Counter), which always contains the address of the next instruction, onto the system stack (S register), then loading the PC with the destination address. The next RTS instruction should simply PULS the PC's value from the stack. Note that this won't work if the stack has been modified and not correctly restored.
  
 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,y,pc    ; restore registers and return      puls x,y,pc    ; restore registers and return 
 +
 +==== 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 "stack") to make room. Then the phone rings and you need to get out your appointment book, so the photos go onto the "stack". And then there's someone at the door... this could go on indefinitely, though there is ultimately a physical limit to the size of the stack. 
 +
 +The point is that you can easily imagine retrieving your papers from the "stack" and smoothly carrying on with the last job after every interruption. It only needs a little order in your working methods. And if you have a whole program written down beside you, and the current line number is just another detail that can be jotted down and pushed on the stack, nothing could possibly go wrong.
 +
 +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't usually set it). Pushing a byte means first decrementing S by 1, then storing the byte into address S. Doing this twice pushes a 16-bit word. To pull a byte, read its value from address S then increment S by 1. Note that this is identical to using the 6809's autoincrement/decrement modes.
 +
 +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,b,x,y,u,pc
 +
 +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 "modify A" part might have included PSHS X,Y followed by PULS X,Y, and we would not notice. But we must do our stacking in matching pairs with no overlap; PSHS A then PSHS X,Y then PULS A will not restore A.
 +
 +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   DP    X                        PC 
 +  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's nothing to stop it expanding to overwrite programs and data, but this shouldn't happen in practice. 
 +  * 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 branch at the end:+This is very similar to REPEAT, 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 test at the end:
  
   WHILE left<=right DO   WHILE left<=right DO
Line 147: Line 265:
     bls plotLoop     ; WHILE     bls plotLoop     ; WHILE
     ldb bottom       ; continue     ldb bottom       ; continue
- 
-==== 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 'effective affress', in the context of a LDA instruction for example, is simply the address from which the register A is loaded: 
- 
-    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",0 
-    leax helloStr,pcr      ; point X to string 
-    lda ,x+                ; A contains 72 (ASCII 'H') 
- 
-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 with address S
     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's no reason why an assembly language subroutine shouldn't call itself. But the same caveats apply as in any other language. You must use local variables for instance, and not alter global memory in unexpected ways.
 +
 +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 analogies. For eampleyou're at your desk scribbling your memoirs when you need to check through those photos you took on holiday in Ibiza. So you put your papers aside (onto the "stack") to make room. Then the phone rings and you need to get out your appointment bookso the photos go onto the "stack". And then there's someone at the door... this could go on indefinitely, though there is ultimately physical limit to the size of the stack+An interrupt is a physical signal that can occur at any timetriggering the CPU into stacking its registers, executing a subroutine, then retrieving its registers to continue as normalThis is a way of providing a software response to a hardware event. It may be as simple as updating a counter in memory in sync with an external timer.
  
-The point is that you can easily imagine retrieving you papers from the "stack" and smoothly carrying on with the last job after every interruption. It only needs little order in your working methods. And if you have a whole program written down beside youand the current line number is just another detail that can be jotted down and pushed on the stack, everything is bound to be fine.+The details of interrupts are intricate and a relatively advanced topic, but most home micros have a simple use for them: vertical blanking. On an old-fashioned CRTevery time the display of a graphics frame is complete the circuitry issues an interrupt. Syncing with this allows for smooth animation in games.
  
-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't usually set it). Pushing a byte means first decrementing S by 1then storing the byte at address S. Doing this twice pushes a 16-bit word. To pull a byte, read its value from address S then increment S by 1. Note that this is identical to using the 6809's autoincrement/decrement modes.+  gameLoop: 
 +    sync                  ; wait for interrupt 
 +    lbsr eraseSprite 
 +    lbsr moveSprite 
 +    lbsr drawSprite       ; most of the time is spent with the sprite drawnnot erased 
 +    bra gameLoop
  
-There are two stacks on the 6809, U (User) and (system); normally we don't bother much with U. Any combination of registers can be pushed and pulled in single instructionWith one exception: we can't stack a stack register onto itselfwhich would have little point anyway.+The 6809 has four hardware interrupts (IRQ, FIRQ, NMI, and RESET)and three software interrupts (SWI, SWI2, SWI3), each with its own vector pointing to service routineSoftware interrputs are machine language instructions rather than hardware eventsand typically are used for debugging breakpoints or calling operating system routines.
  
-    pshs a,b,x,y +An interrupt service routine must end with the instruction RTI (Return from Interrupt) so that the registers are unstacked correctlyThis first pulls the CC and checks the E (Entire) bitIf set it then pulls all the registers from the stack, if clear it just pulls the PCThe fast interrupt (FIRQ) doesn't stack the working registersand the E bit is the way of flagging the difference.
-    ldd #loopCount +
-  loop:   +
-    pshs d +
-    lda ,x+ +
-    cmpa ,y+ +
-    ... +
-    puls d +
-    subd #1 +
-    bne loop   +
-    puls a,b,x,y,pc+
  
-If we PSHS Amodify A, then PULS A we get the same value back regardless of what was done to A previouslyEqually, our "modify A" might have included PSHS X,Y followed by PULS X,Y+Two other CC bits are involved with interrupts. Setting I disables (or masks) the IRQ responseand setting F disables the FIRQ response.
  
 +==== 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's no reason why a short routine shouldn't be reentrant. Simply allocate variables on the stack and never use globals. And avoid eccentricities like self-modifying code. For a larger program things become complex. Fresh memory might be allocated from a heap by the operating system for example. It's an advanced topic, but still doable on the 6809.  
 + 
 +==== What is indirection and how do I use it? ====
  
 ==== 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

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki