CS225,  Fall 2006,  LC2 architecture

Unfortunately, the LC2 simulator will not run on the Windows XP machines (the editor and assembler sort of will, but the simulator just doesn't).  As far as I know it was never available for Macintosh...  It might be amusing to simulate the simulator and read/guess what it looks like anyhow, but not as much fun as writing programs that actually run!.
LC2, a Load-Store, general-purpose register architecture.  
Handout 1
Get handout 2, formats of instructions, from me.  All instructions are in appendix A of Patt&Patel.

Direct addressing:  LD DR,  Label      ST SR,  Label  
Just like PEP, acts on contents of memory at location Label.

Immediate mode:  Only in ADD and AND; actual numbers, signed binary, 5 bits.  (Sign extended to 16 bits in computation)  ADD R2, R3, #7        R2<-- R3+7.
 What do these do?   AND R2, R2, #0     ADD R2, R2, # -1   
   AND R2, R2, #0                   AND R2, R2, #0
   ADD R2, R2, R5                   ADD R2, R2, #5

and, the only Immediate memory operation
Load Effective Address  LEA DR, Label    LEA R4, FOO:  R4 <-- address which FOO represents
       Equivalent to LOADB Label, i

Indexed addressing is a little different here:  "Base + Offset"
LDR DR, BaseR, index6                           STR SR, BaseR, index6
LDR R4, R2, #10:   R4<-- mem[R2 +10]      STR R4, R2, #10:    mem[R2 +10] <--R4
        R2 is the Base and 10 is the offset. 
   But this instruction has a fixed offset, so can't be used like the PEP Indexed,
            where LdA Base,x :  A<--mem[Base+X]  Base stays fixed and X changes like the index of  an array.
Most commonly used in this form: 
                 LEA R2, ArrayBase
Loop         LDR R0, R2, #0         ; Get element from memory at address in R2
                 ;output (or whatever ) R0
                 ADD R2, R2, #1        ; Move pointer R2 to next element in array
                 BRxx   Loop               ; Loop to get another one
(So the offset is usually 0 for traversing an array, and the "base" register changes.)
When might you use the fixed offset?  How about in a stack frame for locating internal variables, relative to stack pointer.

Example:
; = = = = = = = = = = == = = = = = = = = = = = = = = =
;asctobin.asm
; Gets a digit from the keyboard and changes it to binary.
; Stores it in array.  Repeats for 5 digits
.orig x3000
          LEA    R3,Binary   ; Initialize to first location
          LD     R6,ASCII    ; Template to strip ascii
          LD     R1,COUNT    ; Initialize to 5
AGAIN     IN                 ; Get keyboard input
          ADD    R0,R0,R6    ; Strip ASCII template
          STR    R0,R3,#0    ; Store binary digit
          ADD    R3,R3,#1    ; Increment array pointer
          ADD    R1,R1,#-1   ; Decrement COUNT.
          BRp    AGAIN       ; More characters?
          halt              
ASCII     .FILL  xFFD0       ; Negative of x0030.
COUNT     .FILL  #5        ;
Binary    .BLKW  #5         ; Array space for 5 digits.
.end
= = = = = = = = = = = = = = = = = = = = = = = = = = =
;outputstring.asm
; Outputs a string, stored in C form in array String.
;Follows with Linefeed
.orig x3000
             LEA     R1, String        ; Initialize to first location
Loop         LDR     R0, R1, #0      ; Retrieve the character(s)
             BRz     Nomore          ; If it is 0, done
             OUT                     ; Write the character
             ADD     R1, R1, #1      ; Increment pointer
             BR      Loop            ; Do it all over again
Nomore       LD      R0, Linefeed
             OUT
          Halt
;            
String         .Stringz  "Hello world"
Linefeed     .Fill    x0A
.end
= = = = = = = = = = = = = = = = = = = = = = = = = = = =
All Registers are interchangeable EXCEPT 
 R0 is used for I/0
 R7 is used by Traps and Subroutines to store Return address.  Watch after IN or OUT
(R6 is used by  other Interrupts to store Return address)
How to protect?  Save and restore in a convenient memory location, call it SaveR7  

;asctobinwrong.asm
; Gets a digit from the keyboard and changes it to binary.
; Stores it in array.  Repeats for 5 digits
.orig x3000
          LEA    R3,Binary   ; Initialize to first location
          LD     R6,ASCII    ; Template for stripping ASCII
          LD     R7,COUNT    ; Initialize to 5
AGAIN     IN                 ; Get keyboard input
          ADD    R0,R0,R6    ; Strip ASCII template
          STR    R0,R3,#0    ; Store binary digit
          ADD    R3,R3,#1    ; Increment array pointer
          ADD    R7,R7,#-1   ; Decrement COUNT.
          BRp    AGAIN       ; More characters?
          halt              
ASCII     .FILL  xFFD0       ; Negative of x0030.
COUNT     .FILL  #5        ;
Binary    .BLKW  #5         ; Array space for 5 digits.
.end
- - - - - - - - - -
This doesn't work. 
Insert these lines in the 3 appropriate places to make it work.
(Careful!!)
SaveR7    .BLKW #1  ; space to store R7
        ST R7, SaveR7   ; contents of R7 to memory
        LD R7, SaveR7   ; restore contents of R7 from memory
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Procedures:  How do they return, how do we pass/protect things?
JSR label  
As you're used to.  Puts return address into R7.
RET  Return from subroutine.  Copies R7 to PC.
How do we: Pass by reference: value in R3.  Just use R3 in the subroutine.
How do we: Pass by value.  Leave other registers untouched by subroutine.  Have local variables.
    At beginning of  subroutine, save all the registers not passing by reference to local memory; restore before exiting subroutine.  Example next time.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
HW:
A) What are the largest negative and the largest positive numbers you can use in immediate addressing with ADD & AND?

B) a) Run asctobin.  b) Change it so that it just stores the 5 characters in memory  (Doesn't strip the ASCII off).  Hand in the .asm listing.

C) a) Run outputstring.  b) Check the format of the ascii string in memory, making sure it really is null-terminated.  Note that this is a wasteful way to store a string, taking up a whole 16 bits per character.
c) Modify the program so it prints out every other character! (  Hl..etc.  ) There could be a problem with the end of string, so add a space filled with 0 at the end.   Hand in the .asm listing,  and write what the output is.  (I don't see a way to print the console window.)
  
D) Write a short program which will take a number and find its 2's complement negative.  Get the number from a memory location, and store the negative back to another memory location.  Run it with 3 numbers, hand in the .asm file and the results for your 3 numbers ( hex and dec for the number and for the negative.  You can get the dec off the register values as you step the program.)

E)  Run asctobinwrong.  Note that R7 is the problem; it has the wrong value and doesn't decrement, because IN overwrites it.  (To halt a running program, use Execute>Stop)

Subroutines:
PC is passed in R7.   Pass  parameters in other registers. 
Example:
 ;subsimple.asm
; Main Calls subroutine S.  Character is in R1. 
; S adds 1 to Character (Parameter passed by reference).
; Main outputs the new Character
    .orig x3000
main         LD     R1, String        ; Loads "M" into R1
        JSR S
            AND    R0, R0, #0       ; Move new character to
            Add    R0, R0, R1       ; R0
            OUT                     ; Output new Character
          Halt
;- - - - - S- - - - - - - - - - -
S            ADD     R1, R1, #1      ; Increment character
             RET
    
;- - - - - - - - - - - - - - -
; Main variable  
String         .Stringz  "M"
.end
= = = = = = = = = ==== = = = = = = = = = = = =
Protection:  Specify a memory location, in main SaveR7, in procedure SaveR0, R1, etc.  Use ST and LD to save and restore these values.
Example:   Outputstring as a subroutine.  Address of first entry of string passed in R0.  We can pass the address by value.  But we aren't passing the string by value.
= = = = = = = = =
;stringassubrou.asm
; Main Calls subroutine outstring.  Pointer to string is in R1. 
; outstring Outputs a string, stored in C form.
; Main follows with Linefeed
    .orig x3000
main         LEA     R1, String        ; R1 points to string
                ST     R7, SaveR7
                JSR Stringout
                LD         R7, SaveR7       ;Does this work?
LD           R0, Linefeed
                OUT
                Halt
;
;- - - - - Stringout- - - - - - - - - - -
Stringout    ST         R0, SaveR0         ; Save registers used by
                 ST         R1, SaveR1      ;    this procedure,
                 ST      R7, SaveR7      ;INCLUDING .R7
Loop         LDR     R0, R1, #0      ; Retrieve the character(s)
                 BRz     Nomore          ; If it is 0, done
                OUT                     ; Write the character
                 ADD     R1, R1, #1      ; Increment pointer
                 BR      Loop            ; Do it all over again
Nomore       LD         R7, SaveR7       ; Restore registers used by
                 LD         R1, SaveR1        
                 LD         R0, SaveR0      ;    this procedure
                 RET
; Outstring variables
SaveR0       .blkw  1   
SaveR1     .blkw  1
SaveR7       .blkw  1   
;- - - - - - - - - - - - - - -
; Main variables
  
String         .Stringz  "Hi"
Linefeed     .Fill    x0A
.end
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

Passing parameters in registers (
vs.Passing parameters purely on the stack )
--Simple to program.
--Much faster, especially if you don't have to protect too much.
 (Register windowing:  "New" chips have many many registers (say 100). Some are organized into smaller "windows"--each contains private registers, registers for passing out and passing in.  Hardware has a way of hooking up the passing registers).
Registers:   m m m m m m m m s s s s s s s s t t t t t t t t
m m m m m m m m                 Main's window
          s s s s s s s s          Subroutine1's window (what it feels like)
                    t t t t t t t t   Subroutine of Sub1's window (what it feels like)
--Save/restore in local memory:  Can't do recursion.  Usually we save and restore registers to a stack
   If you have too many parameters to pass in registers, rest go on stack.

Calling conventions:
--Who should preserve registers
(A compiler wants to have register use clear up front:  Optimally will minimize load-store, maximize register use.)
      Scratch registers:  expect it to be trashed.  Use locally only.  All PEP's registers.
      Caller-saved registers  
      Callee-saved registers 
      Forbidden registers (like PC, SP)
Agreement between caller and subroutine(s).  Sometimes above is a formal assigment by op. sys. or ISA.
         Caller saves:  Caller's responsibility to save all registers it wants to still be there (maybe all--overkill?)
         Callee saves:  Subroutine's responsibility to save all registers that it uses, (that the caller might want.)
                     IT MUST GET DONE!
  Who decides:  Compiler writer, subject to op.sys or ISA req'ts. 
         (Appleworks Pascal and Metrowerks Pascal for old Macs--incompatible codes--different calling conventions)
      Assembly level:  Your specs better make it clear!
How you pass:
   On the stack:  
Caller always pushes arguments onto stack.  Caller or callee pops them when done. (Which? Pep: caller)
             Return value allocated in caller's stack space?  Or callee's stack space? 
                            (PEP: caller; along with arguments)
   In register(s):   Parameters--Often first few (3) pass in registers, rest on stack.  Function Return value--a specific register?
         Which registers?  Conventional to use first few registers (e.g. R0 for I /O)
 Return address--usually on stack.  LC2--you'd need to put it on a stack yourself for potential deeper subs.)
     
= = = = = = = = = = = = = = = = = = = = = = = = = = = = =
HW:  A)  Run asctobinwrong (above).  Note that R7 is the problem. Fix it, with the code given at the bottom.
B) Rewrite subsimple.asm so it gets a character from the keyboard (instead of memory), passes it to the subroutine, and outputs the next character.
C)  Fix the error in stringassubrou that means R7 is not properly restored in the main program.

, To Sievers Home Page
CS225-Fall06/LC2/LC2.htm 
10:00pm, 11/28/06
This page belongs to Sally Sievers who is solely responsible for its content. Please see our statement of responsibility.