lcc-C-conventions.html

lcc has a version of stdio.h. Included is code for, printf.asm scanf.asm stdio.h getchar.asm putchar.asm stdio.asm Use these in the usual way in C code.

The C-to-LC3 compiler, lcc, produces LC3 assembly code from C source code. Specifically, it produces code compatible with the lc3as assembler. There is no stand-alone linker available; consequently, if we wish to mix assembly code and C code, we must do the linking by hand. To do that, we must first understand the layout and meaning of the assembly code lcc produces. Note: C code can be separately compiled and linked in the usual way using lcc.

SUMMARY:

We start with the simplest example code we can think of.
Here is a C source file, myC.c:

int main(void) {
    return(0);
}

The lcc compiler produces LC3 assembly (see below). Here
is the result in shorthand. NB-- "=" means assign a value;
"<==" means points to; "==" means has same value.

;;;-------------------------------

PREAMBLE:
    ;;;--- set up stack pointers (push decrements SP)
    STACKBOTTOM <== SP <== BP

   ;;;--- set up global data pointer
   GlobalDataArea <==  GDP

   ;;;--- Call main, on return
   ;;;--- jump to OS.
   R7 <== (*GDP).entryPoint
   JSRR R7
   HALT

ENTER:
    ;;;--- sets stack call frame. Result stack
    ;;;--- looks like this, (big address at top):
    STACKBOTTOM         
    frame1.retVal     
    frame1.retAddr    == address to use to when exiting.
    frame1.oldBP      == prior BP value
    frame1.end        <== SP <== BP

BODY:
  frame1.retVal = (*GDP).const

EXIT:
    ;;;--- resets stack call frame. Result stack
    ;;;--- looks like this, (big address at top):
    STACKBOTTOM     <== BP
    frame1.retVal   = (*GDP).const <== SP
    frame1.retAddr  == R7 

  RET

;;;--------------------------------------

DETAILS:

 
Here is the C source file again, myC.c:

int main(void) {
    return(0);
}

Here is the result of running "lcc f.c", which gave 
us "a.asm". (Usually, we need -s flag to get .asm)
My added comments are preceded by ";----".

======(a.asm starts here) ============================

  ;------- PREAMBLE-init-user-SP-BP ---------------
  ;---- Sets up the user's initial stack, SP and BP.    
  ;---- lcc assumes this is a user program.
  ;---- Sets SP and BP to large end of memory.
  ;---- Recall, xF000-xFFFF are I/O registers.
  ;---- So, this sets SP == BP == xEFFF.
  ;---- BP is R5.
.Orig x3000
INIT_CODE
LEA SP, #-1             ;----     SP  == PC-1  == x3000
ADD BP, SP, #0          ;----     BP  == SP    == x3000
ADD SP, SP, SP          ;----     SP  == 2*SP  == x6000
ADD SP, SP, SP          ;----     SP  == 2*SP  == xC000
ADD SP, SP, BP          ;----     SP  == SP+BP == xF000
ADD SP, SP, #-1         ;----     SP  == SP--  == xEFFF
ADD BP, BP, BP          ;----        [ignored]
ADD BP, SP, #0          ;----     BP  == SP    == xEFFF

  ;--------- PREAMBLE-init-main ---------------------
  ;---- Inits a register, R4, aka GDP, with a pointer
  ;---- we can then use to access our data area
  ;---- from anywhere in memory. Also moves
  ;---- a pointer into R7, to jump to main(), which
  ;---- allows for an arbitrary jump.
  ;---- These pointers were created by lcc's linker.

LD GDP, GLOBAL_DATA_POINTER      ;---- GDP == (address of data area).
LD R7, GLOBAL_MAIN_POINTER       ;---- R7  == (address of main()).
jsrr R7                          ;---- Jump to main().
HALT                             ;---- When main() exits, jump to OS
                                 ;---- via the Trap Vector Table.
GLOBAL_DATA_POINTER .FILL GLOBAL_DATA_START
GLOBAL_MAIN_POINTER .FILL main

;;;;;;;;;;;;;;;;;;;;;;;;;;;;main;;;;;;;;;;;;;;;;;;;;;;;;;;;;

  ;---------- We jumped here from PREAMBLE-init-main.
  ;---------- ENTER (standard code to enter a procedure.)
main                   ;----
                       ;---- SP========>  frame0.end     (at addr xEFFF)
ADD SP, SP, #-2        ;---- SP-- =====>  frame1.retVal  (at addr xEFFE)
STR R7, SP, #0         ;---- PUSH R7 ==   frame1.retAddr (at addr xEFFD)
ADD SP, SP, #-1        ;---- PUSH BP ==   frame1.oldBP   (at addr xEFFC)
STR BP, SP, #0         ;---- 
ADD BP, SP, #-1        ;---- BP =======>  frame1.end     (at addr xEFFB)
ADD SP, SP, #-1        ;---- SP == BP


  ;---------- BODY (the instructions in the body of the procedure.)
  ;---------- In this case, nothing to do but prepare the return value
  ;---------- by fetching a constant value from the global data area.
ADD R7, GDP, #1        ;---- R7 ==  &GD.L2_myC
LDR R7, R7, #0         ;---- R7 ==   GD.L2_myC == 0


  ;---------- LEAVE (standard code to exit a procedure.)
  ;---------- Puts return value in frame, resets BP as it was before,
  ;---------- and resets SP to point to return value in frame,
  ;---------- ready to be popped by code that called this procedure.
lc3_L1_myC             ;----
STR R7, BP, #3         ;---- BP+3 =======> frame1.retval == R7 (at xEFFE)
ADD SP, BP, #1         ;---- SP == BP+1 => frame1.oldBP        (at xEFFC)
LDR BP, SP, #0         ;---- POP BP =====> frame0.end          (at xEFFF)
ADD SP, SP, #1         ;---- SP =========> frame1.retAddr      (at xEFFD)
LDR R7, SP, #0         ;---- POP R7 ==     frame1.retAddr (addr in xEFFD)
ADD SP, SP, #1         ;---- SP =========> frame1.retVal       (at xEFFE)
RET                    ;---- return via R7

  ;---------- GLOBAL DATA, STATIC, CONSTANTS AREA.
  ;---------- Pointers to jump locations, and data items.
  ;---------- AKA "GD" in the above.

GLOBAL_DATA_START              ;----
L1_myC .FILL lc3_L1_myC        ;---- GD.LEAVE_location
L2_myC .FILL #0                ;---- GD.constant
.END                           ;----