1학년 / 1학기 / 전산학개론 / 7강

7강 시스템 소프트웨어와 어셈블리어

이번 강의에서 배울 것

  • 교재 명령어 집합으로 대입·조건·반복 의사코드를 어셈블리어로 번역할 수 있다.
  • COMPARE의 비교 방향과 JUMPGT·JUMPEQ·JUMPLT의 의미를 정확히 쓸 수 있다.
  • 2-pass 어셈블러가 symbol table을 만드는 과정을 추적하고 기계어를 해석할 수 있다.

7강 시스템 소프트웨어와 어셈블리어

학습 흐름

naked machine(2진수로만 대화) → system software가 만든 virtual machine
        ↓
assembly language: 기계어와 1:1, symbolic op code · symbolic address · pseudo-op
        ↓
명령어 집합(Figure 6.5) → 대입 · 조건 · 반복 번역 → 완성 프로그램(최댓값·최솟값)
        ↓
assembler: op code 표 검색 · pass 1(symbol table) · pass 2(object file) → loader
        ↓
실제 x86 어셈블리와 C 코드, 메모리 속 2진수

1. naked machine과 virtual machine

Naked machine (맨 하드웨어)Virtual machine (시스템 소프트웨어 위)
1 프로그램을 2진수로 작성1 텍스트 편집기로 고급 언어 작성
2 명령을 한 칸씩 메모리에 넣음2 폴더에 저장
3 시작 주소를 넣고 "go" 버튼3 번역기로 2진수 변환
4 결과를 메모리에서 한 칸씩 2진수로 읽음4 scheduler가 적재·실행, 5 I/O 시스템이 결과 출력
구성 요소하는 일
Operating system중심 시스템 소프트웨어. 사용자와 대화하고 다른 프로그램을 시작
GUI ("gooey")OS 등에 대한 시각적 인터페이스
I/O systems여러 장치와 통신
Language services고급 언어 지원(assembler, compiler, interpreter)
Memory managers프로그램에 메모리 할당
Information managers대용량 저장 장치(파일) 조직
Scheduler실행을 기다리는 프로그램 관리
Utilities도구, 프로그램 라이브러리

2. 어셈블리어 (assembly language)

source program ─(assembler)→ object program ─(loader)→ memory ─→ hardware 실행
 (어셈블리어)                    (기계어)

한 줄의 형식:

label:    op code   address field   -- comment
NEXTSTEP: LOAD      X               -- Put X into register R
  • label: 이 명령 위치의 이름(선택)
  • comment: -- 뒤. 어셈블러가 무시

3. 명령어 집합 (Figure 6.5) — 시험지에 주어짐

레지스터는 R 하나, 조건 코드는 GT, EQ, LT. CON(X) = 메모리 X의 내용.

op code명령의미
0000LOAD XCON(X) → R
0001STORE XR → CON(X)
0010CLEAR X0 → CON(X)
0011ADD XR + CON(X) → R
0100INCREMENT XCON(X) + 1 → CON(X)
0101SUBTRACT XR − CON(X) → R
0110DECREMENT XCON(X) − 1 → CON(X)
0111COMPARE XCON(X) > R 이면 GT = 1, = 이면 EQ = 1, < 이면 LT = 1
1000JUMP X다음 명령을 X에서 가져옴
1001JUMPGT XGT = 1이면 X로
1010JUMPEQ XEQ = 1이면 X로
1011JUMPLT XLT = 1이면 X로
1100JUMPNEQ XEQ = 0이면 X로
1101IN X정수를 입력받아 X에 저장
1110OUT XX의 값을 10진수로 출력
1111HALT실행 중지

프로그램 구조 (Figure 6.6)

.BEGIN          -- 첫 줄
   …            -- 명령어들
   HALT         -- 실행 종료
   …            -- .DATA 들은 HALT 뒤에
.END            -- 마지막 줄
COUNTER:  .DATA  0       -- 이름이 COUNTER 인 칸에 0
X:        .DATA  12

4. 기본 번역 패턴

대입: x = y + 3 (슬라이드 예제)

        LOAD    Y          -- R ← y
        ADD     THREE      -- R ← y + 3
        STORE   X          -- x ← R
        …
X:      .DATA   0
Y:      .DATA   5
THREE:  .DATA   3          -- 상수 3 도 메모리에 만든다

"Add 3 to the value of K"를 어셈블리어로 번역하시오. 필요한 .DATA를 모두 쓰시오. (Sample Q3)

정답 확인
        LOAD    K
        ADD     THREE
        STORE   K
        HALT
K:      .DATA   0
THREE:  .DATA   3

ADD 3이라고 쓰면 숫자 3이 아니라 메모리 3번지의 내용을 더한다. 이 명령어 집합에는 즉시값(immediate)이 없으므로 상수도 .DATA로 만들어야 한다. INCREMENT K를 세 번 써도 같은 결과다.

다음 두 코드는 무엇이 다른가?

(1) LOAD X        (2) INCREMENT X
    ADD  TWO          INCREMENT X
정답 확인

(1)은 R = X + 2가 되지만 X 자체는 그대로다(STORE가 없음). (2)는 메모리의 X가 X + 2가 되고 R은 바뀌지 않는다.

조건문: If K > 10 then output K

        LOAD    TEN        -- R ← 10
        COMPARE K          -- K 와 10 비교: K > 10 이면 GT
        JUMPGT  PRINTK
        JUMP    ENDIF
PRINTK: OUT     K
ENDIF:  HALT
K:      .DATA   0
TEN:    .DATA   10

If/Else: If K > L then (output K, K++) else (output L, L++)

        LOAD    L          -- R ← L
        COMPARE K          -- K > L 이면 GT
        JUMPGT  THEN
        OUT     L          -- else 부분
        INCREMENT L
        JUMP    ENDIF      -- then 부분을 건너뜀
THEN:   OUT     K
        INCREMENT K
ENDIF:  HALT
K:      .DATA   0
L:      .DATA   0

반복: Set K to 1, 반복해서 K를 출력하고 K++, K > 100이면 끝

        LOAD    ONE
        STORE   K          -- K ← 1
LOOP:   LOAD    HUNDRED    -- R ← 100
        COMPARE K          -- K > 100 이면 GT
        JUMPGT  DONE
        OUT     K
        INCREMENT K
        JUMP    LOOP
DONE:   HALT
K:      .DATA   0
ONE:    .DATA   1
HUNDRED: .DATA  100

반복: while a > b do (print a, a = a − 2)

        IN      A
        IN      B
LOOP:   LOAD    A          -- R ← a
        COMPARE B          -- b 와 a 비교
        JUMPGT  ENDLOOP    -- b > a 이면 끝
        JUMPEQ  ENDLOOP    -- b = a 이면 끝   (즉 a ≤ b 이면 끝)
        OUT     A
        LOAD    A
        SUBTRACT TWO
        STORE   A
        JUMP    LOOP
ENDLOOP: HALT
A:      .DATA   0
B:      .DATA   0
TWO:    .DATA   2

입력 10, 3 → 출력 10 8 6 4

Set K to (L + 1) − (M + N) 을 번역하시오.

정답 확인
        LOAD    M
        ADD     N          -- R ← M + N
        STORE   TEMP       -- 잠시 보관
        LOAD    L
        ADD     ONE        -- R ← L + 1
        SUBTRACT TEMP      -- R ← (L + 1) − (M + N)
        STORE   K
        HALT
K:      .DATA   0
L:      .DATA   0
M:      .DATA   0
N:      .DATA   0
TEMP:   .DATA   0
ONE:    .DATA   1

레지스터가 R 하나뿐이라 중간 결과를 임시 메모리(TEMP) 에 저장해야 한다. 5장 연습 17 "x = y + 19가 왜 번거로운가"의 답도 같다: 상수 19를 담은 칸을 따로 만들어야 하기 때문.

5. 족보 유형: 완성 프로그램 쓰기

두 수 중 큰 값 출력 (중간 족보 문제 12)

         IN      A
         IN      B
         LOAD    A          -- R ← A
         COMPARE B          -- B > A 이면 GT
         JUMPGT  PRINTB
         OUT     A          -- 아니면 A 출력
         JUMP    DONE
PRINTB:  OUT     B
DONE:    HALT
A:       .DATA   0
B:       .DATA   0

100개 정수의 최댓값과 최솟값 (Sample Q4 = 교재 연습 13)

정수 100개를 하나씩 입력받아 가장 큰 값과 가장 작은 값을 출력하는 완전한 어셈블리 프로그램을 쓰시오(필요한 pseudo-op 모두 포함).

힌트 1 보기

첫 번째 수로 MAX와 MIN을 모두 초기화하고, 나머지 99개를 루프에서 처리합니다. 개수를 세는 COUNT와 상수 100, 1이 필요합니다.

힌트 2 보기

STORE는 R을 바꾸지 않습니다. MAX를 갱신한 뒤에도 R에는 방금 읽은 수가 남아 있으니 바로 COMPARE MIN을 할 수 있습니다.

정답 확인
        .BEGIN
        IN      NUM            -- 첫 번째 수
        LOAD    NUM
        STORE   MAX            -- MAX ← 첫 수
        STORE   MIN            -- MIN ← 첫 수
        LOAD    ONE
        STORE   COUNT          -- 읽은 개수 = 1
LOOP:   LOAD    COUNT
        COMPARE HUNDRED        -- 100 과 COUNT 비교
        JUMPEQ  DONE           -- 100개 다 읽었으면 끝
        IN      NUM
        INCREMENT COUNT
        LOAD    NUM            -- R ← NUM
        COMPARE MAX            -- MAX > NUM 이면 GT
        JUMPGT  CHKMIN         -- 최댓값 갱신 불필요
        STORE   MAX            -- MAX ← NUM
CHKMIN: COMPARE MIN            -- MIN < NUM 이면 LT  (R 은 여전히 NUM)
        JUMPLT  LOOP           -- 최솟값 갱신 불필요
        STORE   MIN            -- MIN ← NUM
        JUMP    LOOP
DONE:   OUT     MAX
        OUT     MIN
        HALT
NUM:    .DATA   0
MAX:    .DATA   0
MIN:    .DATA   0
COUNT:  .DATA   0
ONE:    .DATA   1
HUNDRED: .DATA  100
        .END

체크 포인트

  • MAX·MIN을 0으로 시작하면 모든 입력이 음수(또는 양수)일 때 틀린다 → 첫 입력으로 초기화
  • 루프는 99번 돈다(COUNT 1 → 100). 처음에 COUNT = 0으로 두고 100번 읽으면 첫 수를 중복 처리하게 된다.
  • 무작위 정수 100개로 200번 시뮬레이션해 결과가 실제 max·min과 같음을 확인한 코드다.

음이 아닌 수의 합 (Figure 6.8)과 그 변형

        .BEGIN
        CLEAR   SUM            -- (0) sum ← 0
        IN      N              -- (1) 첫 입력
AGAIN:  LOAD    ZERO           -- (2) R ← 0
        COMPARE N              -- (3) N < 0 이면 LT
        JUMPLT  NEG            -- (4)
        LOAD    SUM            -- (5)
        ADD     N              -- (6)
        STORE   SUM            -- (7) sum ← sum + N
        IN      N              -- (8) 다음 입력
        JUMP    AGAIN          -- (9)
NEG:    OUT     SUM            -- (10) 음수(sentinel)를 만나면 출력
        HALT                   -- (11)
SUM:    .DATA   0              -- (12)
N:      .DATA   0              -- (13)
ZERO:   .DATA   0              -- (14)
        .END

(1) Figure 6.8의 CLEAR SUM과 LOAD ZERO는 꼭 필요한가?

(2) 이 프로그램을 0에서 멈추고, 양수의 합과 음수의 합을 따로 출력하도록 고치시오. (입력 12, −2, 14, 1, −7, 0 → 27, −9)

정답 확인

(1) CLEAR SUM: SUM: .DATA 0이 이미 0으로 초기화하므로 한 번만 실행할 때는 없어도 된다. 하지만 메모리를 다시 적재하지 않고 재실행하면 이전 합이 남으므로 두는 것이 안전하다. LOAD ZERO: COMPARE는 R과 비교하므로 매 회차 R을 0으로 만들어야 한다. 루프 안에서 R이 SUM으로 바뀌므로 반드시 필요하다.

(2)

        CLEAR   POS
        CLEAR   NEGS
LOOP:   IN      N
        LOAD    ZERO
        COMPARE N              -- N 과 0 비교
        JUMPEQ  DONE           -- N = 0 → 끝
        JUMPLT  ISNEG          -- N < 0
        LOAD    POS
        ADD     N
        STORE   POS
        JUMP    LOOP
ISNEG:  LOAD    NEGS
        ADD     N
        STORE   NEGS
        JUMP    LOOP
DONE:   OUT     POS
        OUT     NEGS
        HALT
POS:    .DATA   0
NEGS:   .DATA   0
N:      .DATA   0
ZERO:   .DATA   0

6. 명령 하나하나의 효과

레지스터 R = 13, 메모리 60 = 472, 61 = −1일 때, 각 명령을 처음 상태에서 하나씩 실행한 결과를 쓰시오.

(1) LOAD 60 (2) STORE 60 (3) ADD 60 (4) COMPARE 61 (5) IN 61 (사용자가 50 입력) (6) OUT 61

정답 확인
명령R6061비고
LOAD 60472472−1
STORE 601313−1
ADD 60485472−1
COMPARE 6113472−1−1 < 13 → LT = 1, GT = EQ = 0
IN 611347250
OUT 6113472−1화면에 −1 출력

메모리 79에 +6이 있고 기호 Z가 79번지를 뜻한다. 각 명령 뒤 R의 값은? (1) LOAD 79 (2) LOAD 6 (3) LOAD Z (4) LOAD Z + 1

정답 확인

(1) 6 (2) 숫자 6이 아니라 메모리 6번지의 내용 (3) 6 (Z = 79) (4) 80번지의 내용

주소 필드는 항상 주소다. LOAD 6은 6을 넣는 명령이 아니다.

7. 어셈블러: 번역과 적재

어셈블러가 하는 일

  1. symbolic op code → 2진 op code — op code 표를 찾아 바꿈. 표를 정렬해 두고 이진 탐색(3장)으로 찾음
  2. symbolic label → 메모리 주소
  3. pseudo-op 처리 (.DATA → 2진 값)
  4. 기계어를 담은 object file 작성

Loader는 object file의 명령을 메모리에 놓고 하드웨어가 실행하게 합니다.

왜 두 번 읽는가 (two passes)

JUMPGT DONE을 만났을 때 DONE은 아직 아래에 나오지 않아 주소를 모릅니다(forward reference). 그래서

Pass하는 일
Pass 1location counter로 각 줄이 몇 번지인지 세며, label을 만나면 (label, 주소)를 symbol table에 추가
Pass 2op code를 2진수로 바꾸고, 주소 필드의 label을 symbol table에서 찾아 바꿈. .DATA 값을 2진수로. object file에 기록. 표에 없는 op code·label은 오류

프로그램이 0번지부터 적재될 때 symbol table에 들어가는 AGAIN, ANS, X, ONE의 값은?

        .BEGIN
        IN       X
        LOAD     X
AGAIN:  ADD      ANS
        SUBTRACT ONE
        STORE    ANS
        OUT      ANS
        JUMP     AGAIN
ANS:    .DATA    0
X:      .DATA    0
ONE:    .DATA    1
        .END
정답 확인

.BEGIN은 pseudo-op이라 칸을 차지하지 않는다. IN(0), LOAD(1), AGAIN = 2, SUBTRACT(3), STORE(4), OUT(5), JUMP(6), ANS = 7, X = 8, ONE = 9.

(참고: 이 프로그램에는 HALT가 없어 무한히 반복한다.)

기계어 해석과 데이터를 실행하는 사고

(1) 0101001100001100 과 0011000000000111 을 어셈블리어로 바꾸시오.

(2) 실수로 .DATA 16387이 명령으로 실행되면 무슨 일이 일어나는가?

정답 확인

(1) 0101 | 001100001100 → SUBTRACT 780, 0011 | 000000000111 → ADD 7

(2) 16387 = 16384 + 3 = 0100 000000000011 → INCREMENT 3. 메모리 3번지의 값(아마 어떤 명령)이 1 증가해 프로그램이 망가지고, 실행은 다음 칸으로 계속된다. 폰 노이만 구조에서는 명령과 데이터가 같은 메모리에 같은 2진수로 있어 구별되지 않기 때문이다(stored program). 그래서 .DATA는 HALT 뒤에 둔다.

op code가 16개이고 프로그램이 100줄일 때, 1초에 50,000번 비교하는 어셈블러가 op code를 찾는 데 쓰는 최대 시간을 (1) 순차 탐색 (2) 이진 탐색으로 구하시오.

정답 확인

(1) 줄당 최악 16번 × 100 = 1,600번 → 0.032초 (2) 줄당 최악 ⌊lg 16⌋ + 1 = 5번 × 100 = 500번 → 0.01초

op code가 300개, 프로그램이 50,000줄이면 차이가 훨씬 커진다(순차 300 vs 이진 9번/줄). 3장의 Θ(n) vs Θ(lg n)이 실제 시스템 소프트웨어에서 쓰이는 예.

8. 실제 어셈블리와 메모리 속 2진수 (슬라이드 부록)

C 코드와 x86 어셈블리의 대응(슬라이드 28쪽)도 같은 틀입니다.

Cx86교재 어셈블리로 보면
while (i < 10) { … }cmpl $10, %ecx / jge endwhile / … / jmp whileloopCOMPARE + 조건 점프로 탈출 + JUMP로 복귀
if (i == 2) {…} else {…}cmpl $2, %ecx / jne elsetrue / … / jmp endif조건 점프 + 무조건 점프
i++incl %eaxINCREMENT

자료형마다 메모리 크기가 정해져 있습니다: bool 1, char 1, int 4, float 4, long 8, double 8바이트(string은 길이에 따라 다름).

int score1 = 72;   // 00000000 00000000 00000000 01001000
int score2 = 73;   // 00000000 00000000 00000000 01001001
int score3 = 33;   // 00000000 00000000 00000000 00100001

hello, %s 같은 C 프로그램도 컴파일러 → 어셈블리 → 기계어(0과 1)로 내려가고, 반대로 기계어에서 원래 의미를 추측하는 것이 reverse engineering입니다.


정리 카드

system software가 하는 두 가지 일은?

컴퓨터 자원을 관리하고, 사용자와 하드웨어 사이의 중개자로서 virtual machine을 만든다.

어셈블리어가 기계어보다 나은 점 세 가지는?

symbolic op code, symbolic address(label), pseudo-op으로 데이터를 사람 형태로 표현 → 명확성·가독성·유지보수성, 재배치 가능.

LOAD A 후 COMPARE B에서 GT = 1이 되는 조건은?

B > A. COMPARE X는 CON(X)를 R과 비교한다.

어셈블러가 두 번(two passes) 읽는 이유는?

label이 정의되기 전에 쓰일 수 있어서(forward reference). pass 1에서 symbol table을 만들고 pass 2에서 번역한다.

.DATA를 HALT 뒤에 두는 이유는?

명령과 데이터가 같은 메모리에 같은 2진수로 저장되어, 앞에 두면 데이터가 명령으로 실행되기 때문이다.

source program, object program, assembler, loader의 관계는?

assembler가 source(어셈블리어)를 object(기계어)로 번역하고, loader가 object를 메모리에 적재해 실행을 시작시킨다.

오늘도 한 걸음 앞으로.

내용을 이해했다면 학습 완료를 체크해 주세요.

학습 기록은 현재 브라우저에 저장됩니다.
HJS / STUDY한지석