Converter o programa C em código de montagem
while(a!=b){
if(a > b){
a = a - b;
}
else{
b = b - a;
}
}
return a;
}
nota lateral: suponha que dois inteiros positivos a e b já estão inscritos nos registos R0 e R1.
Pode deixar comentários a explicar como o fez?
5 answers
Se estiver a utilizar gcc
, pode obter a montagem como gcc -S -o a.s a.c
se o seu código-fonte for a.c
. Se estiver a usar o Visual Studio, pode obtê-lo quando depurar, seleccionando a janela de "desmontagem". Aqui está a saída do Visual studio (eu nomeei a sub-rotina / função chamada "comum" é por isso que "comum" aparece):
while(a!=b){
003613DE mov eax,dword ptr [a]
003613E1 cmp eax,dword ptr [b]
003613E4 je common+44h (0361404h)
if(a > b){
003613E6 mov eax,dword ptr [a]
003613E9 cmp eax,dword ptr [b]
003613EC jle common+39h (03613F9h)
a = a - b;
003613EE mov eax,dword ptr [a]
003613F1 sub eax,dword ptr [b]
003613F4 mov dword ptr [a],eax
}
else{
003613F7 jmp common+42h (0361402h)
b = b - a;
003613F9 mov eax,dword ptr [b]
003613FC sub eax,dword ptr [a]
003613FF mov dword ptr [b],eax
}
}
00361402 jmp common+1Eh (03613DEh)
return a;
00361404 mov eax,dword ptr [a]
}
Aqui a variável a
é guardada na memória inicialmente e assim é b
(dword ptr [b]
).
- apenas expressões simples permitidas, ou seja
a = b + c;
é permitidoa = b + c + d;
não é permitido porque existem dois operadores lá. - apenas expressões booleanas simples são permitidas numa declaração if, ou seja
if (a < b)
é permitido, masif (( a < b) && (c < d))
não é permitido. - só se houver declarações, mais ninguém. bloco.
- não é permitido fazer ou fazer enquanto é permitido, apenas goto e label
Então, o programa acima se traduziria em;
label1:
if (a == b)
goto label2;
if (a < b)
goto label4;
a = a - b;
goto label3;
label4:
b = b - a;
label3:
goto label1;
label2:
return a;
Espero ter percebido. correct...it tem sido quase vinte anos desde que eu tive que escrever atomic-C. agora assumindo que o acima está correto, vamos começar a converter algumas das declarações atomic-C em MIPS (assumindo que é o que você está usando) montagem. A partir do link fornecido por Elliott Frisch, podemos quase imediatamente traduzir o passos de subtracção:
a = a - b becomes R0 = R0 - R1 which is: SUBU R0, R0, R1
b = b - a becomes R1 = R1 - R0 which is: SUBU R1, R1, R0
Usei subtracção sem sinal devido tanto a como b serem inteiros positivos.
As comparações podem ser feitas desta forma:
if(a == b) goto label2 becomes if(R0 == R1) goto label2 which is: beq R0, R1, L2?
O problema aqui é que o terceiro parâmetro do Código op-beq é o deslocamento que o PC move. Não saberemos esse valor até terminarmos a montagem manual aqui.
A desigualdade é mais trabalho. Se deixarmos as instruções do pseudo-Código, primeiro precisamos usar o op-codeset on less than
que coloca um no registo de destino se o primeiro registo for inferior ao segundo. Uma vez que tenhamos feito isso, podemos usar o branch on equal
como descrito acima.
if(a < b) becomes slt R2, R0, R1
goto label4 beq R2, 1, L4?
Os saltos são simples, são apenas j e depois a etiqueta para onde ir. Então,
goto label1 becomes j label1
A última coisa que temos de resolver é o regresso. O retorno é feito movendo o valor que queremos
um registro especial V0 e, em seguida, saltar para a próxima instrução após a chamada para esta função. O problema é que os MIPS não têm um registo para register move command (ou se ele faz eu esqueci - lo) assim que nos movemos de um registro para RAM e, em seguida, de volta novamente. Finalmente, usamos o registro especial R31 que contém o endereço de retorno.
return a becomes var = a which is SW R0, var
ret = var which is LW var, V0
jump RA which is JR R31
Com esta informação, o programa torna-se. E também podemos ajustar os saltos que não conhecíamos antes.
L1:
0x0100 BEQ R0, R1, 8
0x0104 SLT R2, R0, R1 ; temp = (a < b) temp = 1 if true, 0 otherwise
0x0108 LUI R3, 0x01 ; load immediate 1 into register R3
0x010C BEQ R2, 1, 2 ; goto label4
0x0110 SUBU R0, R0, R1 ; a = a - b
0x0114 J L3 ; goto label3
L4:
0x0118 SUBU R1, R1, R0 ; b = b - a;
L3:
0x011C J L1 ; goto lable1
L2:
0x0120 SW R0, ret ; move return value from register to a RAM location
0x0123 LW ret, V0 ; move return value from RAM to the return register.
0x0124 JR R31 ; return to caller
Faz quase vinte anos desde que tive que fazer coisas como esta (agora um dia, se precisar de montagem, só faço o que outros sugeriram e deixo o compilador fazer tudo pesado). Estou certo de que cometi alguns erros ao longo do caminho, e ficaria feliz por quaisquer correções ou sugestões. Eu só entrei nesta longa discussão porque eu interpretei a pergunta do OP como fazendo uma tradução manual -- algo que alguém poderia fazer como eles estavam aprendendo montagem.
Saúde.
loop:
cmp ax, bx
je .end; if A is not equal to B, then continue executing. Else, exit the loop
jg greater_than; if A is greater than B...
sub ax, bx; ... THEN subtract B from A...
jmp loop; ... and loop back to the beginning!
.greater_than:
sub bx, ax; ... ELSE, subtract A from B...
jmp loop; ... and loop back to the beginning!
.end:
push ax; return A
Eu usei {[1] } em vez de r0
e bx
em vez de r1
ORG 000H // origin
MOV DPTR,#LUT // moves starting address of LUT to DPTR
MOV P1,#00000000B // sets P1 as output port
MOV P0,#00000000B // sets P0 as output port
MAIN: MOV R6,#230D // loads register R6 with 230D
SETB P3.5 // sets P3.5 as input port
MOV TMOD,#01100001B // Sets Timer1 as Mode2 counter & Timer0 as Mode1 timer
MOV TL1,#00000000B // loads TL1 with initial value
MOV TH1,#00000000B // loads TH1 with initial value
SETB TR1 // starts timer(counter) 1
BACK: MOV TH0,#00000000B // loads initial value to TH0
MOV TL0,#00000000B // loads initial value to TL0
SETB TR0 // starts timer 0
HERE: JNB TF0,HERE // checks for Timer 0 roll over
CLR TR0 // stops Timer0
CLR TF0 // clears Timer Flag 0
DJNZ R6,BACK
CLR TR1 // stops Timer(counter)1
CLR TF0 // clears Timer Flag 0
CLR TF1 // clears Timer Flag 1
ACALL DLOOP // Calls subroutine DLOOP for displaying the count
SJMP MAIN // jumps back to the main loop
DLOOP: MOV R5,#252D
BACK1: MOV A,TL1 // loads the current count to the accumulator
MOV B,#4D // loads register B with 4D
MUL AB // Multiplies the TL1 count with 4
MOV B,#100D // loads register B with 100D
DIV AB // isolates first digit of the count
SETB P1.0 // display driver transistor Q1 ON
ACALL DISPLAY // converts 1st digit to 7seg pattern
MOV P0,A // puts the pattern to port 0
ACALL DELAY
ACALL DELAY
MOV A,B
MOV B,#10D
DIV AB // isolates the second digit of the count
CLR P1.0 // display driver transistor Q1 OFF
SETB P1.1 // display driver transistor Q2 ON
ACALL DISPLAY // converts the 2nd digit to 7seg pattern
MOV P0,A
ACALL DELAY
ACALL DELAY
MOV A,B // moves the last digit of the count to accumulator
CLR P1.1 // display driver transistor Q2 OFF
SETB P1.2 // display driver transistor Q3 ON
ACALL DISPLAY // converts 3rd digit to 7seg pattern
MOV P0,A // puts the pattern to port 0
ACALL DELAY // calls 1ms delay
ACALL DELAY
CLR P1.2
DJNZ R5,BACK1 // repeats the subroutine DLOOP 100 times
MOV P0,#11111111B
RET
DELAY: MOV R7,#250D // 1ms delay
DEL1: DJNZ R7,DEL1
RET
DISPLAY: MOVC A,@A+DPTR // gets 7seg digit drive pattern for current value in A
CPL A
RET
LUT: DB 3FH // LUT starts here
DB 06H
DB 5BH
DB 4FH
DB 66H
DB 6DH
DB 7DH
DB 07H
DB 7FH
DB 6FH
END
a
e b
antes do seu ciclo while
e você está pronto para ir.
Você pode ver como o código é compilado para montagem com uma quantidade razoável de explicação, e então você pode executar o código de montagem dentro de uma CPU hipotética.