Module 6Módulo 6

Assembly and CEnsamblador y C

The same machine, two languages. How C compiles down to the instructions you already know. La misma máquina, dos lenguajes. Cómo C se compila a las instrucciones que ya conoces.

Santi Scagliusi, PhD

▶Simulator: M6 · Demo: La convención de llamada (R12, R13 -> R12)Simulador: M6 · Demo: La convención de llamada (R12, R13 -> R12)

maximo(25, -3) in assemblymaximo(25, -3) en ensamblador

       mov.w #25, R12     ; a
       mov.w #-3, R13     ; b
       call  #maximo      ; R12 = ?

maximo: cmp.w R13, R12
        jge   maxFin
        mov.w R13, R12
maxFin: ret
Pause and predictPausa y predice

What will R12 hold on return?¿Qué valor tendrá R12 al volver?

And with a = -7, b = -2?¿Y con a = -7, b = -2?

Two languages, one instruction setDos lenguajes, un solo juego de instrucciones

The compiler does not invent new operations. It emits the same MSP430 instructions you write by hand.El compilador no inventa operaciones nuevas. Emite las mismas instrucciones MSP430 que escribes a mano.

C sourceFuente en C
int add(int a, int b) { return a + b; }

A C compiler reads this and produces an assembly file.Un compilador de C lee esto y produce un archivo en ensamblador.

Assembly emittedEnsamblador emitido
ADD.W R13, R12 RET

Args in R12, R13. Result in R12.Argumentos en R12, R13. Resultado en R12.

file.c → compilercompilador → file.asm → assemblerensamblador → machine code in FRAMcódigo máquina en FRAM

C data types on a 16-bit coreTipos de datos de C en un núcleo de 16 bits

One word is 16 bits. A type that fits in a word is cheap. Anything wider costs extra instructions and memory accesses.Una palabra son 16 bits. Un tipo que cabe en una palabra es barato. Lo más ancho cuesta instrucciones y accesos a memoria extra.

Fits in a word or lessCabe en una palabra o menos
char1 B
short2 B
int2 B
pointerpuntero2 B

Small data model: a pointer is a 16-bit address. One register.Modelo de datos pequeño: un puntero es una dirección de 16 bits. Un registro.

Wider than a wordMás ancho que una palabra
long4 B
long long8 B
float4 B
double8 B

No floating-point hardware: float and double run as software library calls.Sin hardware de coma flotante: float y double se ejecutan con llamadas a librería.

Pick the smallest type that holds your value. The width you choose becomes the number of instructions.Elige el tipo más pequeño que contenga tu valor. El ancho que elijas se convierte en número de instrucciones.

The program skeletonEl esqueleto del programa

Every assembly program starts the same way: a code section, an entry label, stop the watchdog, set the stack, then run.Todo programa en ensamblador empieza igual: una sección de código, una etiqueta de entrada, parar el watchdog, fijar la pila y ejecutar.

.text ; code -> FRAM (0x4400) main: mov.w #WDTPW|WDTHOLD, &WDTCTL ; stop watchdog mov.w #0x2400, SP ; init stack pointer call #setup ; configure peripherals loop: ; ... main work ... jmp loop .intvec RESET_VECTOR, main ; 0xFFFE -> main

.text lands the code in FRAM at 0x4400..text coloca el código en FRAM en 0x4400.

Stop the watchdog first, or it resets the chip mid-run.Para el watchdog primero, o reinicia el chip a mitad.

.intvec writes main into 0xFFFE: where the CPU starts..intvec escribe main en 0xFFFE: donde arranca la CPU.

CALL pushes the return addressCALL apila la dirección de retorno

CALL saves the address of the next instruction on the stack, then jumps. RET pops it back into PC.CALL guarda en la pila la dirección de la siguiente instrucción y salta. RET la recupera en el PC.

CallerLlamador
0x4410 call #add
0x4414 mov.w R12, sum ; return here
PC
Subroutine + stackSubrutina + pila
add: add.w R13, R12
ret
0x23FE 0x4414 return addrdir. retorno

RET is just MOV @SP+, PC. The saved address goes straight back into the program counter.RET es simplemente MOV @SP+, PC. La dirección guardada vuelve directa al contador de programa.

▶Simulator: M6 · Demo: CALL apila la dirección de retornoSimulador: M6 · Demo: CALL apila la dirección de retorno

Watch the stack during CALLMira la pila durante el CALL

       mov.w #7, R12
       call  #doble       ; [SP] = ?
       mov.w R12, R13     ; aquí vuelve RET

doble: rla.w R12          ; R12 = 2 * R12
       ret
Pause and predictPausa y predice

Which address sits on top of the stack?¿Qué dirección queda en la cima de la pila?

Hint: the instruction after the CALL.Pista: la instrucción que sigue al CALL.

The stack frame, balancedEl marco de pila, equilibrado

A subroutine that uses R6 must save and restore it. Every PUSH needs a matching POP before RET.Una subrutina que use R6 debe guardarlo y restaurarlo. Cada PUSH necesita su POP antes de RET.

work: push.w R6 ; save R6 mov.w #0, R6 ; use it ; ... body ... pop.w R6 ; restore R6 ret ; SP balanced

When RET runs, the top of the stack must hold the return address, not your saved R6. PUSH and POP keep SP balanced.Cuando se ejecuta RET, la cima de la pila debe contener la dirección de retorno, no tu R6 guardado. PUSH y POP mantienen el SP equilibrado.

Inside work, after PUSHDentro de work, tras PUSH grows upcrece hacia arriba ↑
0x2400 -- -- callerllamador
0x23FE 0x4414 return addrdir. retorno
0x23FC old R6 ← SP

Low address at the bottom, near SP. CALL wrote 0x4414; PUSH put old R6 below it.Dirección baja abajo, junto a SP. CALL escribió 0x4414; PUSH puso el R6 antiguo debajo.

The calling conventionLa convención de llamada

A shared contract so C and assembly can call each other. The TI and GCC compilers both follow the MSP430 EABI.Un contrato común para que C y ensamblador se llamen entre sí. Los compiladores de TI y GCC siguen el EABI del MSP430.

R12 arg 1arg 1 + return+ retorno
R13 arg 2arg 2
R14 arg 3arg 3
R15 arg 4arg 4
arg 5, 6, …arg 5, 6, … → pushed on the stackse apilan en la pila

Args fill R12→R15 in order. The result comes back in R12: same register, dual role. Los argumentos llenan R12→R15 en orden. El resultado vuelve en R12: el mismo registro, doble función.

; C: result = add(x, y); mov.w x, R12 ; arg 1 -> R12 mov.w y, R13 ; arg 2 -> R13 call #add ; run the subroutine mov.w R12, result ; result <- R12

Who saves which registerQuién guarda cada registro

The convention splits the working registers in two, so neither side saves more than it must.La convención divide los registros de trabajo en dos, para que ningún lado guarde más de lo necesario.

R0
R1
R2
R3
R4
R5
R6
R7
R8
R9
R10
R11
R12
R13
R14
R15
specialespeciales
callee-saved · the subroutine restoressalva la subrutina · ella los restaura
caller-savedsalva el llamador
Caller protects R11–R15El llamador protege R11–R15
push.w R14 ; before the call call #sub pop.w R14 ; after, R14 back
Callee protects R4–R10La subrutina protege R4–R10
sub: push.w R6 ; save on entry ; ... use R6 ... pop.w R6 ; restore ret
▶Simulator: M6 · Demo: Quién guarda cada registro (R4-R10 son tuyos)Simulador: M6 · Demo: Quién guarda cada registro (R4-R10 son tuyos)

The callee keeps R10 intactLa función deja R10 intacto

       mov.w #0x00AA, R10
       call  #suma3       ; R12 = 1+2+3

suma3: push.w R10         ; guardar
       mov.w  R12, R10
       add.w  R13, R10
       add.w  R14, R10
       mov.w  R10, R12
       pop.w  R10         ; restaurar
       ret
Pause and predictPausa y predice

Without PUSH/POP, what is R10?Sin PUSH/POP, ¿cuánto vale R10?

Comment them out and run it again.Coméntalos y vuelve a ejecutar.

What the compiler emitsLo que emite el compilador

A tiny C function and the assembly a compiler produces from it. Read it line by line, it is instructions you know.Una función pequeña en C y el ensamblador que un compilador genera de ella. Léela línea a línea, son instrucciones que conoces.

C functionFunción en C
int max(int a, int b) { if (a > b) return a; return b; }
AssemblyEnsamblador
max: cmp.w R13, R12 ; a - b jge done ; a >= b: keep R12 mov.w R13, R12 ; else b done: ret ; result in R12

Arguments a and b were already in R12 and R13. The compiler only needed a compare, a jump, and a return.Los argumentos a y b ya estaban en R12 y R13. El compilador solo necesitó una comparación, un salto y un retorno.

Control flow is compare plus jumpEl flujo de control es comparar y saltar

Every C control structure becomes the same two-instruction idea: set the flags, then a conditional jump.Toda estructura de control de C se vuelve la misma idea de dos instrucciones: fija los flags y luego salta condicionalmente.

while
wloop: cmp.b R12, R13 jeq wend ; exit when equal ; ... body ... jmp wloop wend:
for (i = 0; i < n; i++)
clr.b R12 ; i = 0 floop: cmp.b #n, R12 jhs fend ; exit when i >= n ; ... body ... inc.b R12 jmp floop fend:

if-else, do-while, every loop: the same shape. Only the condition and the jump change.if-else, do-while, cualquier bucle: la misma forma. Solo cambian la condición y el salto.

Count down, not upCuenta hacia abajo, no hacia arriba

Counting toward zero drops the compare entirely. DEC already sets the Zero flag.Contar hacia cero elimina la comparación por completo. DEC ya fija el flag Zero.

Count upContar hacia arriba CMP each passCMP en cada vuelta
clr.b R12 up: cmp.b #4, R12 ; extra compare jhs done ; ... body ... inc.b R12 jmp up
Count downContar hacia abajo no CMPsin CMP
mov.b #4, R12 dn: ; ... body ... dec.b R12 ; sets Z jnz dn ; loop if not 0

DEC updates the flags as a side effect, so JNZ reads them directly.DEC actualiza los flags como efecto secundario, así que JNZ los lee directamente.

Mixing C and assemblyMezclar C y ensamblador

C calls your assembly like any C function. .global plus the convention is the whole contract.C llama a tu ensamblador como a cualquier función. .global más la convención es todo el contrato.

C sideLado C
extern int max(int,int); int t = max(a, b);
a→R12 · b→R13
→
R12→t
conventionconvención
Assembly sideLado ensamblador
.global max ; visible to C .text max: cmp.w R13,R12 jge done mov.w R13,R12 done: ret

Course setting: small code and data model, so call/ret and 16-bit pointers. The large model uses calla/reta.En el curso: modelo de código y datos pequeño, así que call/ret y punteros de 16 bits. El modelo grande usa calla/reta.

A pointer is indirect addressingUn puntero es direccionamiento indirecto

Dereferencing a C pointer and the indirect register mode are the same mechanism: a register holds the address.Desreferenciar un puntero de C y el modo registro indirecto son el mismo mecanismo: un registro guarda la dirección.

C with a pointerC con un puntero
int *p = &value; // an address int x = *p; // read via p *p = 0; // write via p
Assembly with @RnEnsamblador con @Rn
mov.w #value, R5 ; an address mov.w @R5, R6 ; read via R5 clr.w @R5 ; write via R5

Arrays and tables in linear memoryArrays y tablas en memoria lineal

A 2-D table is stored row by row. The element address is a small offset calculation.Una tabla 2-D se guarda fila por fila. La dirección del elemento es un cálculo de desplazamiento pequeño.

offset = (i × M + j) × element_sizedesplazamiento = (i × M + j) × tamaño_elemento
; table[1][2], M = 4 cols, int = 2 B mov.w #1, R12 ; i mov.w #2, R13 ; j rla.w R12 ; i * 2 rla.w R12 ; i * 4 (= i * M) add.w R13, R12 ; i*M + j rla.w R12 ; * 2 bytes -> 12 mov.w table(R12), R14 ; load element
byte offset of each celldesplazamiento en bytes de cada celda
+0
+2
+4
+6
+8
+10
+12
+14
+16
+18
+20
+22

Row 1, column 2 lands at byte +12: exactly what the arithmetic gives.Fila 1, columna 2 cae en el byte +12: justo lo que da la aritmética.

Three ideas to take with youTres ideas para llevarte

C and assembly share one machine and one contract.C y ensamblador comparten una máquina y un contrato.

C → ASM

Same instruction setEl mismo juego de instrucciones

The compiler emits MOV, ADD, CMP, jumps. Nothing you have not seen.El compilador emite MOV, ADD, CMP, saltos. Nada que no hayas visto.

R12–R15

Arguments and returnArgumentos y retorno

The calling convention lets C and assembly call each other safely.La convención de llamada permite que C y ensamblador se llamen con seguridad.

@Rn

Pointers are addressesLos punteros son direcciones

A C pointer is a register holding an address: indirect addressing.Un puntero de C es un registro con una dirección: direccionamiento indirecto.