#mips
#mips
Вопрос:
Я должен перевести эту функцию C в MIPS.
void updatePoint(int *arg)
{
int distance;
distance = findDistance(arg[0],arg[1],0,-1);
if ((distance < 1) amp;amp; (arg[2] < 0))
arg[2] = - arg[2];
distance = findDistance(arg[0],arg[1],10,-1);
if ((distance < 1) amp;amp; (arg[2] > 0))
arg[2] = - arg[2];
distance = findDistance(arg[0],arg[1],-1,0);
if ((distance < 1) amp;amp; (arg[3] < 0))
arg[3] = - arg[3];
distance = findDistance(arg[0],arg[1],-1,10);
if ((distance < 1) amp;amp; (arg[3] > 0))
arg[3] = - arg[3];
arg[0] = arg[0] arg[2];
arg[1] = arg[1] arg[3];
return;
}
Вот код MIPS, ведущий к вызову функции.
.data
redData: .word 0:4
greenData: .word 0:4
prmpt1: .asciiz "Enter x-coordinate for red particle (0 to 10):"
prmpt2: .asciiz "Enter y-coordinate for red particle (0 to 10):"
prmpt3: .asciiz "Enter x-coordinate for green particle (0 to 10):"
prmpt4: .asciiz "Enter y-coordinate for green particle (0 to 10):"
prmpt5: .asciiz "cycle "
prmpt6: .asciiz "red particle (x,y,xVel,yVel): "
prmpt7: .asciiz "green particle (x,y,xVel,yVel): "
prmpt8: .asciiz "Collison: oops, end of simulation!n"
space: .asciiz " "
endl: .asciiz "n"
# i $s0
# cycle $s1 = 0
# dist $s2
.text
main: li $s1,0
la $s3,redData # redData[2] = 1 ;
li $s4,1
sw $s4,8($s3)
sw $s4,12($s3) # redData[3] = 1 ;
la $s3,greenData # greenData[2] = -1 ;
li $s4,-1
sw $s4,8($s3)
sw $s4,12($s3) # greenData[3] = -1 ;
la $a0,prmpt1 # cout << prmpt1 ;
li $v0,4
syscall
la $s3,redData
li $v0,5 # cin >> redData[0] ;
syscall
sw $v0,($s3)
la $a0,prmpt2 # cout << prmpt2 ;
li $v0,4
syscall
li $v0,5 # cin >> redData[1] ;
syscall
sw $v0,4($s3)
la $a0,prmpt3 # cout << prmpt3 ;
li $v0,4
syscall
la $s3,greenData # cin >> greenData[0] ;
li $v0,5
syscall
sw $v0,($s3)
la $a0,prmpt4 # cout << prmpt4 ;
li $v0,4
syscall
li $v0,5 # cin >> greenData[1] ;
syscall
sw $v0,4($s3)
loop: # do {
la $a0,prmpt5 # cout << "cycle " << cycle << endl ;
li $v0,4
syscall
move $a0,$s1
li $v0,1
syscall
la $a0,endl
li $v0,4
syscall
la $a0,prmpt6 # cout << "red particle (x,y,xVel,yVel): "
li $v0,4
syscall
la $s3, redData
lw $a0,($s3) # << redData[0]
li $v0,1
syscall
la $a0,space # << " "
li $v0,4
syscall
lw $a0,4($s3) # << redData[1]
li $v0,1
syscall
la $a0,space # << " "
li $v0,4
syscall
lw $a0,8($s3) # << redData[2]
li $v0,1
syscall
la $a0,space # << " "
li $v0,4
syscall
lw $a0,12($s3) # << redData[3]
li $v0,1
syscall
la $a0,endl # << endl ;
li $v0,4
syscall
la $a0,prmpt7 # cout << "green particle (x,y,xVel,yVel): "
li $v0,4
syscall
la $s3, greenData
lw $a0,($s3) # << greenData[0]
li $v0,1
syscall
la $a0,space # << " "
li $v0,4
syscall
lw $a0,4($s3) # << greenData[1]
li $v0,1
syscall
la $a0,space # << " "
li $v0,4
syscall
lw $a0,8($s3) # << greenData[2]
li $v0,1
syscall
la $a0,space # << " "
li $v0,4
syscall
lw $a0,12($s3) # << greenData[3]
li $v0,1
syscall
la $a0,endl # << endl ;
li $v0,4
syscall
la $a0,endl # << endl ;
li $v0,4
syscall
la $a0,redData # updatePoint(redData) ;
jal updatePoint
Для точки обновления я должен сохранять значения arg[0] — arg[2] в стек? Для этого я должен сделать
lw $s0, 0($a0)
lw $s1, 4($a0)
lw $s2, 8($a0)
lw $s3, 12($a0)
addi $sp, $sp, -20
lw $s0, 0($sp)
lw $s1, 4($sp)
lw $s2, 8($sp)
lw $s3, 12($sp)
lw $ra, 16($sp)
Ответ №1:
Для точки обновления я должен сохранять значения arg[0] — arg[2] в стек?
Нет, arg
это массив. Он передается по ссылке (т. Е. Указатель / адрес arg
находится внутри $a0
). arg
Ожидается, что при возврате значения в будут изменены на updatePoint
. Он изменит arg[0]
и arg[1]
(т. Е. См. Последние две строки updatePoint
) и, условно, может изменять arg[2]
и arg[3]
по пути.
На каждом шаге updatePoint
он должен использовать обновленные значения, arg
и по возвращении вызывающий объект updatePoint
должен получать обратно arg
измененные значения.
Ничто в updatePoint
не позволит arg
изменять, findDistance
потому что все аргументы findDistance
передаются по значению.
Итак, вам нужно сохранить [значение указателя в] $a0
updatePoint
, чтобы предотвратить потерю этого значения указателя arg
при выполнении вызовов findDistance
.
Вы могли бы сохранить его во фрейме стека для updatePoint
, но вам пришлось бы перезагружать его после каждого вызова findDistance
. Лучше сохранить его в сохраненном вызываемом регистре (например $s0
), чтобы это findDistance
не мешало ему.
Вот пример, который выполняет первый вызов findDistance
. Вам должно быть легко заполнить остальные, а также последние две строки C . Обратите внимание, что больше ничего не нужно сохранять в updatePoint
[это сильный намек]. Вы можете использовать столько других временных правил, сколько захотите.
# void
# updatePoint(int *arg)
# {
# int distance;
#
# distance = findDistance(arg[0], arg[1], 0, -1);
# if ((distance < 1) amp;amp; (arg[2] < 0))
# arg[2] = -arg[2];
#
# distance = findDistance(arg[0], arg[1], 10, -1);
# if ((distance < 1) amp;amp; (arg[2] > 0))
# arg[2] = -arg[2];
#
# distance = findDistance(arg[0], arg[1], -1, 0);
# if ((distance < 1) amp;amp; (arg[3] < 0))
# arg[3] = -arg[3];
#
# distance = findDistance(arg[0], arg[1], -1, 10);
# if ((distance < 1) amp;amp; (arg[3] > 0))
# arg[3] = -arg[3];
#
# arg[0] = arg[0] arg[2];
# arg[1] = arg[1] arg[3];
# }
updatePoint:
subu $sp,$sp,8
sw $ra,4($sp)
sw $s0,0($sp)
# NOTE: now that we've preserved $s0 of our caller, we are free to use it
# for whatever we want -- we use it to preserve the address of 'arg'
move $s0,$a0 # preserve address of 'arg'
# distance = findDistance(arg[0], arg[1], 0, -1);
lw $a0,0($s0) # get arg[0]
lw $a1,4($s0) # get arg[1]
li $a2,0 # set 0
li $a3,-1 # set -1
jal findDistance
# if ((distance < 1) amp;amp; (arg[2] < 0))
bge $v0,1,noset1 # distance < 1? if no, fly
lw $v0,8($s0) # get arg[2]
bgez $v0,noset1 # is arg[2] < 0? if no, fly
# arg[2] = -arg[2];
neg $v0,$v0 # get -arg[2]
sw $v0,8($s0) # set arg[2] = -arg[2]
noset1:
# with adjusted parameters repeat the above for all three remaining calls
# perform last two lines of function ...
lw $ra,4($sp)
lw $s0,0($sp)
addu $sp,$sp,8
jr $ra
findDistance:
Обратите внимание, что вызываемая функция должна только сохраняться $s0-$s7
, должна оставаться $sp
такой, какой она ее нашла, и должна сохраняться $ra
, чтобы она могла выполнять jr $ra
возврат.
Например $ra
, функция может сохранить его в (например) $t3
и позже сделать jr $t3
. Вызывающему абоненту все равно, $ra
сохранен он или нет (т. Е. Выполняйте jr
с любым reg, который вы пожелаете, идиоматическим $ra
). ). Просто убедитесь, что функция возвращается в правильное место.
В приведенном выше коде обратите внимание, что после вызова to findDistance
проверяется возвращаемое значение [in $v0
] . После этого возвращаемое значение не требуется, и код использует $v0
в качестве временного (против (например) $t0
)).
Аналогично, вызываемая функция может использовать регистры аргументов $a0-$a3
для всего, что пожелает (т. Е. Они могут использоваться в качестве временных регистров во время вычислений)
Вот ваша функция c , перекодированная в то, что я называю «простым C». Это гораздо больше похоже на прямой перевод в asm:
void
updatePoint(int *arg)
{
int distance;
distance = findDistance(arg[0], arg[1], 0, -1);
if (distance >= 1)
goto noset1;
if (arg[2] >= 0)
goto noset1;
arg[2] = -arg[2];
noset1:
distance = findDistance(arg[0], arg[1], 10, -1);
if (distance >= 1)
goto noset2;
if (arg[2] <= 0)
goto noset2;
arg[2] = -arg[2];
noset2:
distance = findDistance(arg[0], arg[1], -1, 0);
if (distance >= 1)
goto noset3;
if (arg[3] >= 0)
goto noset3;
arg[3] = -arg[3];
noset3:
distance = findDistance(arg[0], arg[1], -1, 10);
if (distance >= 1)
goto noset4;
if (arg[3] <= 0)
goto noset4;
arg[3] = -arg[3];
noset4:
arg[0] = arg[0] arg[2];
arg[1] = arg[1] arg[3];
}