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 ;----