Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

확장된 어셈블러

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_1.html

기초적인 어셈블러는 기계어로부터 한단계 위에서 프로세서를 보는 관점이다. 기계동작과 피연산자와 주소로 비트패턴을 사용하는 대신에 어셈블러에서는 상징을 사용할 수 있다. 하지만 기초적인 어셈블러에서는 어셈블러 명령들은 기계명령에 직접적으로 상응한다.

연장된 어셈블러는 보다 고차원적인 관점이다. 실질적인 MIPS 하드웨어 보다도 더풍부한 명령을 갖추고 있는 추상적인 기계를 구현한다.확장된 어셈블러가 받아들이는 명령들의 대부분이 여러가지 기계명령에 해당한다.

장의 주제:

  • 레지스터 축약코드(mnemonic) 이름
  • 의사명령(Pseudoinstructions)과 의사컴퓨터(pseudocomputer)
  • move 의사명령.
  • li 의사명령.
  • la 의사명령.
  • lw 의사명령.
  • sw 의사명령.
  • nop 의사명령.
스핌은 기초적인 어셈블러로 작동할 수 있고 확장된 어셈블러로도 작동할 수 있다. 이전 장에서는 실제 하드웨어가 어떻게 작동하는 가를 볼 수 있도록 기초적인 어셈블러를 설명하였다. 이 장에서는 확장된 어셈블러의 기능을 적극적으로 활용한다.

질문: 범용 또는 일반목적의 레지스터란 무엇입니까?

레지스터 축약코드

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_2.html

답: 레지스터는 비트패턴을 담고있는 전자 장치이다.일반목적의 레지스터는 어셈블리언어에서 직접적으로 사용할 수 있는 레지스터이다. 부동소수점 레지스터도 어셈블리어에서 직접적으로 접근할 수 있지만 일반목적 레지스터로 간주하지는 않는다.

레지스터가 일반적인 목적의 레지스터라고 할지라도 소프트웨어에서 관례적으로 각각의 레지스터를 특정한 목적에 쓰는것이 관례이다. 기계적으로 그렇게 써야만 하는 이유는 없다. 관례를 세움으로써 프로그래머가 혼동되지 않도록 히고자 하는 것이다. 확장된 어셈블러는 레지스터의 용도를 나타내는 축약된 이름을 레지스터를 지칭하는데 사용한다.

예를 들자면, 레지스터 $8부터 $15까지 관례적으로 임시 값을 담는다. 다음은 레지스터 $0부터 $7 까지의 축약된 이름이다.

레지스터 번호 축약된 이름 관례적으로 사용하는 예
$0 zero 항상 0을 담믄다
|| $1 || $at || 어셈블러의 임시값 Assembler Temporary || || $2, $3 || $v0, $v1 || 서브루틴에서 돌아오는 값 Value returned by a subroutine || || $4-$7 || $a0-$a3 || 서브루틴에 입력값 Subroutine Arguments ||
$8-$15 $t0-$t7 임시값 Temporary
$16-$23 $s0-$s7 저장되는 레지스터들 Saved registers
$24, $25 $t8, $t9 임시값 Temporary
$26, $27 $k0, $k1 커널 Kernel
$28 $gp 전역 포인터 Global Pointer
$29 $sp 스텍 포인터 Stack Pointer
$30 $fp 프레임 포인터 Frame Pointer
|| $31 || $ra || 리턴 주소 Return Address ||

하드웨어적으로 $0 과 $31 레지스터만이 나머지 레지스터와 다르다.( $0는 전부 0을 담고 있고 $31레지스터는 서브루틴의 연결명령에서 돌아오는 주소값을 담는데 자동적으로 사용된다.) 나머지 레지스터는 전자적으로 동일하다. 소프트웨어의 관례에 따라 용도에 맞추어 레지스터들을 사용한다.

질문: 확장된 어셈블러의 결과물(output)과 기초적인 어셈블러가 산출란 결과물을 비교할 때 다른 기계명령들을 가지고 있을까요?

확장된 어셈블러

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_3.html

답: 다르지 않습니다. 축약된 이름은 32개의 일반 목적 레지스터를 지칭하는 또다른 이름일 뿐입니다. 레지스터를 지정하는 기계코드는 전과같이 똑같은 비트 패턴으로 지정합니다.

확장된 어셈블러에서는 기계적인 레지스터 번호를 대신해 축약된 레지스터의 이름을 사용할 수 있다. 어셈블러가 축약된 레지스터 이름을 레지스터 번호로 바꾸어준다. 물론 어셈블리 명령은 궁극적으로 기계어로 번역된다.

확장된 어셈블러를 스핌에서 사용하는 경우 스핌은 MIPS 기계에서는 제공하지 않는 많은 기능들을 구현한다. 확장된 어셈블러는 기초적인 어셈블리어에 확장되는 기능을 사용하는 소스코드를 허용한다. 소스 코드에 있는 확장된 기능들은 기초적인 어셈블리어로 바꿔진후 기계코드로 번역된다.

그림은 확장된 어셈블리 코드가 확장된 어셈블러에 의해 어떤 방식으로 기초적인 어셈블리 코드로 해석 되는가를 보여준다. 기초적인 어셈블리 코드로 해선되어진후 기초 어셈블러에 의해 기계어로 번역되어진다. 그림은 개념적으로는 정확할 수 있지만 실제 구현에 있어서 중간 단계들이 통합되어지고 확장된 어셈블러가 확장된 어셈블리 코드를 직접 기계어로 해석하기도 한다.

attachment:extendAss.gif

확장된 어셈블러를 사용하여 프로그래머는 더편리한 언어로 프로그램을 할 수 있다. 하지만 작동하는 기계는 변하지 않는다. 물론 고차원적인 언어에서는 이보다 더욱 확장된 기능을 사용할 수 있다. 주어진 기계에서 어셈블리어를 확장하기 보다는 전혀 다른 종류의 프로그램어를 기계코드로 해석할 수 도 있다.

질문: 앞 장에서 프로그램은 레지스터 $8번부터 $15번가지 사용하였다. 이 레지스터들의 축약된 이름은 무엇입니까? 레지스터표를 참조해 봅시다.

임시값을 위한 레지스터들

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_4.html

답: $8번부터 $15까지 == $t0부터 $t7까지

대부분의 고차원의 프로그램언어에서처럼 어셈블리 프로그램도 서브루틴의 집합체이다. 메인 프로그램은 작업을 수행하기 위해 일련의 서브루틴들을 호출한다. 물론 서브루틴 자첸도 다른 서브루틴을 호출할 수도 있다.(자바에서는 소프트웨어는 오브젝트로 묶여져있다. 서브루틴은 메소드에 해당한다고 볼 수 있다) 서브루틴들은 지역 변수를 사용한다. 지역변수는 서프루틴안에서만 사용되고 다른 곳에서는 사용되지 않는다. 지역변수(또는 어셈블리어에서 그에해당하는 개념)를 사용할 때는 임시 레지스터로는 $t0-$t7,$t8과 $t9 이 사용되고 저장되는 레지스터로는 $s0-$7을 사용한다.

프로그램을 작성할때 임시 레지스터와 저장 레지스터는 마음대로 쓰고싶은대로 사용할 수 있다. 하지만 소프트웨어 관례에 따라 임시 레지스터는 호출하는 서브루틴에 의해서 변화가 가능하다. 저장레지스터는 서브루틴 호출에 의해서도 변화하지 않는다. 다음의 예를 보자.


        ori  $t0,$0,32    #  32를 임시 레지스터에 올린다.
        ori  $s0,$0,13    # 13을 저장 레지스터에 올린다. 

        jal  subrout      # 서브루틴을 호출한다(지금은 세부사항을 무시해도 된다)
        sll  $0,$0,0      # 지연 슬롯

back:   addu $s0,$s0,$t0  # 서브루틴에서 돌아 오는 값
                          # 임시 레지스터 $t0의 값은 변할 수 도 있다.
                          # 저장 레지스터 $s0에는 변화가 없다.

back에 선언문에는 에러가 있다. 임시레지스터 $t0값은 서브루틴 subrout에 의해서 바뀌었을 수 도 있다.

질문: 메인 프로그램도 일종의 서브루틴으로 간주할 수 있습니까?

그외의 레지스터들

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_5.html

답: 볼 수 있다. 운영체제에 있어서 메인은 일종의 서브루틴입니다.

레지스터들을 서브루틴에서 변수와 인수(argument)로 사용되는 것들 ($0, $v0-$v1, $a0-$a3, $t0-$t9, $s0-$s7)과 그 이외 다른 목적으로 사용되는 것들로 묶어볼 수 있다. 서브루틴은 앞으로 다룰거이고 그 이외의 것들도 주제에 맞게 논의 될것이다. 운영체제에서 가장 근원적인 부분은 커널이다. 운영체제의 커널이외 나머지 부분은 커널이 제공하는 함수를 사용하여 대부분 구현한다. 레지스터 $k0와 $k1은 커널을 위해 예비되어 있다. 응용프로그램과 대부분의 운영체제 루틴은 이 레지스터들은 다루지 말아야한다.

스핌은 운영체제가 아니다. 하지만 요청에 따라 레지스터 $k0와 $k1을 사용하는 트랩 핸들러를 로드한다.

레지스터 $gp와 $sp와 $fp는 메모리의 여러부분을 접근하기 위한 베이스 레지스터로 사용된다.

질문: 다음 어셈블리 명령은 무엇을 합니까?
addu $t5,zero,$t7

It ________ the contents of register ________ into register ________.

의사명령(Pseudoinstruction)

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_6.html

답: 다음 어셈블리 명령은 무엇을 합니까?
addu $t5,zero,$t7

It copies the contents of register $t7 into register $t5.
레지스터 $7의 내용을 레지스터 $5으로 복사합니다.

위와 같이 사용된 addu명령은 소스레지스터로부터 비트패턴을 목적 레지스터로 복사한다. 비록 소스레지스터에는 변화가 없지만, 이것을 보통 move 작동이라고 부른다. 어떤 레지스터로부터 다른 레지스터로 편리하게 값을 이동시키다.

move작동을 하려고 할때 add동작이라고 한다면 어색할 수 도 있다. 확장된 어셈블러에서는 addu 명령을 대신해 축약된 move명령을 사용할 수 있다. 이와같이 축약된 명령은 새로운 기계명령이 아니다. 의미에 적합한 축약된 코드를 사용하여 같은 명령을 수행하는 것이다. 어셈블러는 이런 의사명령을 적절한 기초 어셈블리 명령으로 번역시킨다.

확장된 어셈블러는 의사명령을 하나 또는 그 이상의 기초적인 어셈블리 명령으로 대체한다.

확장된 어셈블러는 많은 수의 의사명령을 구현한다. 때로는 어떤 의사명령은 단순히 기본적인 어셈블리 명령을 편리하게 다시 고쳐부르는 것일 수 도 있다. 어떤 경우에는 의사명령은 기본적인 어셈블리 명령들을 작고 편리하게 나열한 것일 수 도 있다. 다음은 move 의사 명령이다.

move d,s      #소스레지스터 s의 내용을 목적레지스터 d로 복사하는 의사 명령  

레지스터 d와 s는 $t5 또는 zero와 같이 축약된 레지스터 이름으로 지정하거나 또는 $13이나 $0같은 레지스터 번호를 사용하여 지정할 수 있다. 확장된 어셈블러의 모든 명령들에서 이와같이 레지스터를 사용할 수 있다.

질문: 레지스터 $t3의 내용을 레지스터 $s1으로 복사하는 명령을 작성해보자.

_____   ____ , ____

대입문(Assignment Statement)

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_7.html

답:
move $s1,$t3
피연산자의 순서를 혼동하기 쉽다. move명령은 고급언어의 대입문처럼 읽을 수 있다.

j = val;
val의 내용을 변수 i 로 복사한다.

move $s1,$t3

$t3의 내용을 레지스터 $s1로 복사한다.

질문: ori $t5,$0,74 명령다음에 레지스터 $5는 무슨 값을 가지고 있습니까?

Load Immediate

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_8.html

답:ori $t5,$0,74는 74<10>의 이진수로 표현하여 레지스터 $t5에 집어넣는다.

위에 사용한 것처럼 ori 명령은 명령에 있는 비트패턴을 목적 레지스터로 복사한다.(16비트 크기의 직접피연산자가 32비트로 0확장된다는 것을 기억하자) 이러한 작동을 일반적으로 load immediate 작동이라고 한다. 이 명령은 메모리에 접근할 필요없이 즉각적으로 이용할 수 있는 값을 레지스터에 로드한다. load immediate 동작에 대한 축약코드가 있으면 편리할지도 모른다. 사실 축약코드가 있다. 확장된 어셈블러는 li 축약명령을 포함한다. 어셈블러는 이러한 의사명령을 적합한 기초적인 명령으로 번역해 준다.

li   d,value      # 레지스터 $d에 양의 정수 또는 음의 정수 값을 올린다. Value 값은 16비트나 32비트의 정수가 될 수 있다. 

질문: 다음의 의사 명령을 그에 해당하는 기초 어셈블리 명령으로 번역해 보자 (레지스터의 축약된 이름을 사용하자)

li $v0,12 == ori ____,____,____

여러가지로 번역하기

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_9.html 답:
li $v0,12 == ori $v0,zero,12

load immediate 의사명령은 레지스터에 정수값을 올릴때 유용하다. 다음은 또다른 예이다.
li  $t2,-156

위의 명령은 2의 보수로 표현된 정수값 -156을 레지스터 $t2에 집어넣는다. 명령의 immediate field 값이 16비트 크기의 음수와양수를 구분하지 않는 정수값으로 실행시 32비트 크기로 0 확장된다. 그렇기 때분에 ori 명령으로는 -156을 집어넣을 수 없다. li 명령의 즉시값이 음수 이기 때문에 이 명령은 다르게 번역된다.

li $v2,-156   ==   addiu $v2,$0,-156

덧셈을 수행하기 전에 addiu는 즉시 피연산자(immediate operand)를 부호연장 한다는 것을 기억하자. 또한 축약코드의 u는 오버플로우시 트랩을 발생시키지 않는 다는 것을 의미한다. addiu 명령에서 음의 정수를 사용할 수 있다. 확장된 어셈블러는 li명령은 즉시값(immediate value)의 부호에 따라 각기 다른 기초 명령으로 번역한다.

질문: ori 또는 addiu 같은 기초 명령에서 16비트 보다 더큰 크기의 즉시값(immediate value)을 사용할 수 있습니까?

두개의 기본적인 명령

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_10.html

답: 사용할 수 없다. 기계명령은 즉시값으로 16비트 크기의 field르 가지고 있다.

기계명령은 고정된 크기의 필드 값을 가지고 있다. 즉시 피연산자(mmediate operands)는 항상 16비트 크기이다. 하지만 두개의 기계명령을 사용하여 32비트 크기의 레지스터에 32비트 정수의 앞쪽반과 두쪽반 값을 올릴 수 있다.

li $v2,0x12345678 	    ==    	lui $v2,0x1234
  	  	ori $v2,$v2,0x5678

다음li 명령을 두가지 기초 명령으로 번역할 수 있다. 첫번째 명령은 $v2에 위쪽 반값을 올린다. 두번째 명령은 ori작동을 사용하여 아래쪽 반의 값을 올린다. 즉시 피연산자 보다 더 큰 비트 값을 요구하는 어떤 정수도(음수나 양수나) 이런식으로 번역할 수 있다. 이와같이 확장된 어셈블리어는 프로그래머에게 유용한 명령들을 제공한다.

li   d,value        # 레지스터 $d에 음수나 양수의 정수값을 올린다. 정수값은 32비트 크기의 어떤 정수라도 가능하다.(의사명령) 

확장된 어셈블러는 자동적으로 이러한 의사명령을 가장 효율적이고 실질적인 명령들로 해석하여 준다.

질문: 다음 의사명령을 그에 해당하는 기초적인 어셈블리 명령으로 해석하여 보자.
li $t7,0xFF001234

예제 프로그램

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_11.html 답:
li $t7,0xFF001234 	  ==   	lui $t7,0xFF00
  	  	ori $t7,$t7,0x1234

위의 의사 명령에서 즉시 피연산자는(immediate operand) 음의 정수를 표현한다.(부호 비트가 설정되어 있다는 것을 주목해서 보자). li 명령을 사용할 경우 꼭 16진수(hex)를 사용할 필요는 없다. li $t7,-16772555로 명령을 표현할 수 있다.

다음은 load immediate 명령을 사용하는 짧은 프로그램이다. 몇 페이지에 걸쳐서 좀더 실질적인 예제프로그램을 살펴볼 것이다.

## liEg.asm
##
        .text
        .globl  main

main:
        li    $t0,43        #  첫번째 값
        li    $t1,-96       #  두번째 값
        li    $t7,-16772555 #  세번째 값
        addu  $t0,$t0,$t1   #  값을 더한다.
        addu  $t0,$t0,$t7   #  결과를 $t0에 남겨둔다.

## end of liEg.asm

각각의 li 명령은 각기 다른 기본 명령으로 번역 되어진다. 스핌에서 프로그램을 실행하기 위해 메뉴의 세팅을 먼저 살펴보자. 의사명령을 허용한다는 옵션에 표시를 하고 기초 머신 옵션에는 표시를 제거하자.

질문: 심볼 주소(symbolic address)는 무엇입니까?

모르는 주소(Unknown Addresses)

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_12.html

답: 심볼주소는 메모리의 위치를 나타낼 때 이름을 사용한다.

심볼주소는 특정한 데이터나 명령이 결과적으로 위치해 있을 장소를 나타낸다. 일반적으로 이러한 주소는 미리 알 수 없다. 자주 프로그램은 여러가지 목적 모듈(object module)과 어셈블러 소스 파일을 사용하여 작성된다.(분리 어셈블리에 대해서는 1장을 복습해 보자) 다양한 모듈은 심볼 주소를 사용하여 서로서로 주소를 지정한다. 실제 주소는 모든 모듈이 링크되고 메모리에 올려질때 시스템 소프트웨어에 의해 결정된다. 프로그래머가 이렇게 다양한 부분들의 주소가 무엇이라고 미리 결정하는 것은 매우 힘든 작업이고 그렇게 할 필요도 없다.

어셈블러에서는 심볼을 사용하여 프로그래머가 실행물을 작성한 이후의 과정에서나 알 수 있는 주소를 지정할 수 있다. 어셈블러나 링커나 로더같은 이 후에 시스템 소프트웨어는 심볼 주소가 재현하는 주소가 이후에 결정될 때까지 심볼 주소를 추적한다. 실행에 앞서 모든것이 결정된다. 기계어 프로그램과 데이타는 메모리에 올려지고 실행된다.

attachment:memPicture.gif

질문: 그림은 프로그램이 실행중일 때 실행시 메모리의 한단면이다. 그림에서 주소 0x1000000C에 word는 무슨 데이터 입니까? 0x00000002를 담고 있는 word의 주소는 무엇입니까?

주소와 주소가 담고 있는 내용

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_13.html 답: 그림에서 주소 0x1000000C에 word는 무슨 데이터 입니까? 0x00000003 0x00000002를 담고 있는 word의 주소는 무엇입니까?0x10000008

attachment:memPicture_1.gif

워드 단위의 메모리에 어떠한 32비트 패턴도 올 수 있다. 메모리의 워드 주소 또한 32비트 패턴 형태이다. 두 형태의 데이터 모두 32비트 레지스터에 꼭맞는다.때때로 메모리가 담고 있는 워드의 내용이 레지스터에 올리고자 할 때가 있다. 예를들어 0x00000002라는 갓을 올리고자 한다고 가정해 보자. lw 명령을 사용하여 올릴 수 있다. 그림을 살펴보자. 올리고자 하는 값의 메모리 주소는 0x10000008이다.

어떨 경우에는 메모리에 있는 워드의 주소를 올리고자 할 수 있다. 예를 들어 주소 0x10000008 를 올리고자 할 수 있다. la명령으로 올릴 수 있다.

la   d,exp          #  레지스터 $d에 exp가 가리키는 주소를 load한다.  
                    #  "exp" 는 심볼 주소이다.
                    #  (pseudoinstruction)
질문: 전에 했던 프로그램으로 레지스터에 메모리 주소를 집어넣을 수 있습니까?

레지스터에 주소담기

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_14.html 답:예, 베이스레지터는 주소를 담고 있습니다. 또한 배열이나 문자열에 사용하는 포인터 레지스터도 주소를 담고 있습니다.

전 장에서 주소를 베이스 레지스터에 올려보았다. 다음과 같은 코드로 주소를 레지스터에 올릴 수 있다.

main:                        #  Val2값의 주소를 베이스레지스터에 올리기. 
        lui   $10,0x1000     #  상위 반
        ori   $10,$10,0x0008 #  하위 반
        lw    $11,0($10)     #  주소에 있는 메모리의 내용을 올린다. 

        . . . 

        .data
val0:   .word   0 
val1:   .word   1 
val2:   .word   2           # 베이스 레지스터 $10은 "2"를 가리킨다.
val3:   .word   3 
val4:   .word   4 
val5:   .word   5 

질문: 위의 코드에 좀어색한 부분이 있습니까?

Load Address 의사명령

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_15.html

답: 예, 프로그래머가 주소를 미리 알아야만 합니다.

항상 미리 주소를 알고 있을 수 는 없다. 심볼주소와 시스템 소프트웨어를 사용한다면 주소를 미리알고 있을 필요가 없고 편리하다. 그런 과정에서 la와같은 축약코드(mnemonic)또는 의사명령을 사용한다. 축약코드는 일대일로 기계명령에 상응하지 않는다. 어셈블러는 의사명령을 여러개의 기계명령으로 번역한다.

la   d,exp          #  레지스터 $d에 exp가 가리키는 주소를 load한다.  
                    #  "exp" 는 심볼 주소이다.
                    #  (pseudoinstruction)
이러한 의사명령에서 exp는 어떤 메모리의 주소를 읽어서 평가 한다는 의미를 표현한 것이다. exp로 여러가지 형태가 올 수 있고 자주 쓰이는 것이 심볼 주소이다. la 의사명령은 exp에 따라서 여러가지 다른형태의 기계명령으로 번역될 수 있다.

질문: la명령으로 메모리를 읽을 수 있습니까?

LA 예

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_16.html

답: 아니오

실행시 la 명령은 요구되는 주소와 함께 지정된 레지스터로 올려지게될 하나 또는 그 이상의 여러 기계명령으로 번역된다. 이과정에서 어떤 메모리 접근도 일어나지 않는다. 그러므로 명령이후에 지연슬롯이 필요하지 않다.

다음은 la 명령을 사용한 예이다.

main:                        #  Load memory address:
        la    $10,val2       #  32-bit 주소를 레지스터 $10에 올린다.
        lw    $11,0($10)     # 그 주소의 메모리 내용을 $11에 올린다.  

        . . . 

        .data
val0:   .word   0 
val1:   .word   1 
val2:   .word   2            # 베이스 레지스터 $10은  "2"를 가리킨다.
val3:   .word   3 
val4:   .word   4 
val5:   .word   5

질문: 이 la 명령은 2개의 기계 명령으로 번역됩니다. 2개의 명령은 무엇일까요?

SPIM 예제

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_17.html

답: la명령은 이전장에서도 공부했던것처럼 lui명령과 ori명령으로 번역된다.

다음은 la 명령을 사용하는 SPIM 프로그램의 예이다. 각 명령마다 F10을 눌러서 단계적으로 실행해 보자. 레지스터 창을 살펴보고 $t0에 val2의 값을 주소 값으로 가져오는 과정을 주목해 보자. 이제 $t0 를 베이스레즈스터로 이용하여 메모리에 있는 word 값을 $t1과 $t2에 올릴 수 있다.

## addEm.asm
## 두정수를 더하는 프로그램
##
        .text
        .globl  main

main:
        la    $t0,val2     #  32비트 주소를 $t0에 집어넣는다. 
        lw    $t1,0($t0)   #  첫번째 값을 올린다 , 2
        lw    $t2,4($t0)   #  두번째 값을 올린다, 3
        sll   $0,$0,0      #   로드 지연 슬롯
        addu  $t1,$t1,$t2  #  calc. sum

        .data
val0:   .word   0 
val1:   .word   1 
val2:   .word   2 
val3:   .word   3 
val4:   .word   4 
val5:   .word   5 

## end of addEm.asm

질문: 프로그램을 실행하는데 F10을 몇번이나 눌러야 할까요?

SPIM 실행

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_18.html

답: 다섯번. la 명령은 2개의 기계명령으로 본역된다. 각 명령마다 F10을 한번씩 눌러줘야한다.

다음은 프로그램의 실행이다. 아래의 스핌은 la 의사명령을 사용했을 때 실행되는 상황에서 어셈블러가 사용한 2개의 기계명령을 보여준다 (다른 상황에서는 어셈블러는 la 명령을 다른 기계명령으로 해석할 수 도 있다)

attachment:SPIMla.gif

SPIM에서 la 명령을 사용할 때 해석된 2개의 명령을 주의깊게 살펴보자. 두 명령다 $1을 사용한다. 질문: 레지스터 테이블에서 $1번의 축약코드(mnemonic name)는 무엇입니까?

임시 레지스터(Assembler Temporary Register)

19 임시 레지스터(Assembler Temporary Register) http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_19.html

답: 어셈블러 임시 레지스터 $at

어셈블러 임시레지스터는 의사명령을 해석한 기계명령들이 사용하도록 제한 되있다. 종종 의사명령은 $at를 사용하는 짧은 기계명령들로 구현된다. 다음 예는 la명령이 어떻게 해석 되었는가를 보여준다.
la $t0,val5   ==   lui $1,4097
                   ori $8,$1,8

때때로 의사명령과 기초 어셈블러 명령의 축약코드(mnemonic)가 동일하기도 하다. 예를 들어 lw (load word) 명령은 기초 어셈블러 명령이다. 다음과 같이 사용된다.
lw    $t1,8($t0)   #  주소 $t0+8에 있는 워드를 올려라.

이 명령은 $t0와 같은 베이스 레지스터에 8이라는 차이값을 더하여 주소를 지정한다.이 명령은 정확히 하나의 기계명령에 해당한다. 기초명령에서 다른 형태는 없다. 하지만 확장된 어셈블러에서는 다음과 같은 의사명령으로 사용될 수 도 있다.
lw    $t1,exp    #  주소 exp의 값을 올려라.

기계명령 lw는 베이스레지스터를 사용하여 주소를 만들어낸다. 의사명령 lw도 베이스레지스터를 사용하기 위해 (MIPS명령은 이런식으로만 메모리를 주소화한다) 기초명령들로 해석된다.

질문: 어떤 베이스레지스터를 사용합니까?

Load Word(의사명령)

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_20.html

답: 어셈블러 임시 레지스터 $at ($1이기도 하다)

lw 의사명령은 워드 데이터를 메모리로부터 레지스터로 한번에 복사하는것처럼 보인다.

lw    d,exp      # 주소 exp에 있는 값을 $d에 올린다.    
                 #  exp 주소에 있는 값은 어떠한 형태도 올 수 가 잇다. 
                 #  (의사 명령) 

다음은 의사명령 lw의 가능한 한가지 해석이다. 주소 0x10000004가 심볼 데이터라고 가정해보자.
lw $t0,data   ==   lui $1,0x1000
                   lw $8,4($1)
attachment:pseudocomputer.gif

확장된 어셈블러와 의사명령을 이용하여 프로그램 하다보면 마치 실제 하드웨어보다 기능이 많고 편리한 의사컴퓨터에 프로그램하는 것처럼 보일 수 도 있다. (종종 이것을 가상 컴퓨터(virtual computer)라고 부르기도 한다. 하지만 여기서는 혼동을 피하기위해 의사명령은 의사컴퓨터에서 실행된다라고 표현한다) 또다른 기계위에 가상기계를 구현할 수 있다는 생각은 컴퓨터과학에서 아주 중요한 개념이다.

기계위에 구현된 의사컴퓨터는 추상적 차원에서 기계를 바라보는 것이다. 확장된 어셈블러와 의사명령을 사용하여 프로그램을 작성할 경우 의사컴퓨터와 그 컴퓨터의 능력의 차원에서 프로그램을 작성한다. 의사컴퓨터가 실제 프로그램을 직접실행한다고 가정할 수 있다.

질문: lw의사 명령뒤에 로드지연슬롯이 뒤따를까요? (힌트:의사 명령을 번역한 것을 보자)

Store Word (의사명령)

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_21.html 답:예, 해석된 어셈블리에 두번째 명령은 로드값을 사용하기전에 로드지연이 필요한 일반적인 로드 명령이다.

nop(무작동) 의사명령은 sll $0,$0,0로 해석된다. 이것은 여태까지 사용했던 무작동 명령이다.

nop              #  무작동.
                 #  한기계 사이클동안 아무것도 하지 않는다. 
                 #  (의사명령) 
sw 축약코드는 기본어셈블러와 확장된 어셈블러 모두에서 사용된다. 의사명령의 경우 한번의 명령으로 레지스터를 메모리에 저장시킬 수 있다.

sw    d,exp      #  주소 exp의 $d 레지스터의 내용을 저장한다.       
                 #  (pseudoinstruction) 

물론 이 의사명령은 여러개의 기본 명령으로 구현되었다.

질문:

의사 컴퓨터를 작동해보자: sw명령을 확장되지 않은 명령으로 해석해보자.
sw   $t0,someWord   ==    lui  _____,0x1000

                          sw   _____,12( ____ )

예제 프로그램

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_22.html

답:
sw   $t0,someWord   ==    lui  $1,0x1000

                          sw   $8,12( $1 )
$t8을 $t0로 $1을 $at로 표현하여도 무방하다.

다음은 lw와 sw명령을 사용하는 예제이다. 프로그램은 데이터를 여러 레지스터에 올리는 것으로 시작한다. 로드명령의 로드지연슬롯 자리가 유용한 명령들로 채워졌다는 것을 주목해보자. 이렇게 할 수 있는 이유는 데이터가 올려진후 데이터가 몇번의 명령 이후에서나 사용되기 때문이다.예를들어 x의 값은 4번의 명령이후에나 사용된다. 그렇기 때문에 lw 명령 다음에 로드지연슬롯에 유용한 명령들로 채워져있다.

mflo또는 mfhi 명령 이후에 두번의 명령 전까지 mult명령을 하지 말아야 한다는 규칙에 따라 nop (무작동)명령이 사용되었다. (SPIM에서는 MIPS의 그런 제한을 따르지는 않기때문에 SPIM프로그램에서는 일반적으로 이러한 규칙을 지키지는 않는다)

## pseudoPoly.asm
## evaluate the polynomial ax2 + bx + c
##
        .text
        .globl  main

main:
        lw   $t3,x          # x값을 가져온다
        lw   $t0,a          # a 값을 가져온다.
        lw   $t1,bb         #  bb값을 가져온다.
        lw   $t2,c          # c 값을 가져온다.

        mult $t3,$t3        # x2
        mflo $t4            # $t4 = x2
        nop
        nop
        mult $t4,$t0        # low  = ax2
        mflo $t4            # $t4  = ax2
        nop
        nop

        mult $t1,$t3        # low  = bx
        mflo $t5            # $t5  = bx
        addu $t5,$t4,$t5    # $t5  = ax2 + bx

        addu $t5,$t5,$t2    # $t5 = ax2 + bx + c
        sw   $t5,value      # value = polynomial

        .data
x:      .word   4 
value:  .word   1 
a:      .word  20
bb:     .word  -2           # 스핌어셈블러에서는 레벨로 "b"를 사용할 수 없다.
c:      .word   5
 
## end of pseudoPoly.asm
이 프로그램은 다항식을 계산하는 것이다. Horner의 기법을 사용하여 무작동 명령을 재치있게 채울 수 있다면 프로그램의 성능을 향상시킬 수 있다.

질문: 이 프로그램을 작성할때 어떤 조건을 미리 가정하였을까요?

21장 끝

http://chortle.ccsu.edu/AssemblyTutorial/Chapter-21/ass21_23.html

답: 프로그램은 모든 계산이 32비트 범위이내라고 가정하고 있다.

논의된 주제.

  • 레지스터의 축약된 이름.
  • 어셈블러 임시 레지스터.
  • OS의 커널(kernel).
  • 시스템 소프트웨어와 심볼 주소.
  • 의사명령(Pseudoinstructions)과 의사컴퓨터(pseudocomputer)
  • move 의사명령.
  • la 의사명령.
  • lw 의사명령.
  • sw 의사명령.
  • nop 의사명령.
장 끝의 퀴즈 과제를 꼭 풀어봅시다.