시소프 5,6,7일차

OneTwoThree·2022년 12월 30일

주소지정방식

n i x b p e

  • n , i
    즉시주소지정 n=0 i=1
    간접주소지정 n=1 i=0
    simple addressing n=1 i=1 또는 n=0 i=0
  • x
    피연산자에 x (인덱스) 를 사용하면 x=1
    (LDCH ALPHA,X 처럼)
  • b , p
    PC 기준 간접주소면 b=0 p=1
    BASE 기준 간접주소면 b=1 p=0
  • e
    확장주소지정방식이면 e=1 (포맷4)

위 : 포맷 3
아래 : 포맷 4 (확장형)
포맷 3의 disp 3byte 로 주소를 나타낼 수 없는 경우 포맷 4의 address 4byte를 사용한다.
op는 연산의 종류 (OPCODE)
n i x b p e는 어떤 주소지정 방식을 사용하는지 알려준다.
포맷 3는 disp : 변이값 (시작 주소부터 떨어진 정도)
포맷 4는 address : 주소값
포맷 4를 사용할 경우는 명령어 앞에 +를 붙인다

BASE RELATIVE는 TA = B + DISP
PC RELATIVE는 TA = PC + DISP
SIMPLE ADDRESSING은 TA = DISP

IMMEDIATE ADDRESSING은 TA 자체가 피연산자 값으로 사용됨
INDIRECT ADDRESSING은 주소값 자체가 TA임


Base Relative 간접주소지정
b=1 p=0
Base 레지스터 사용
Base 레지스터는 값을 바꿀 수 있다.
TA = 피연산자주소값 - BASE 레지스터 주소값

Program-counter relative 간접주소지정
b=0 p=1
PC 레지스터 사용
PC 레지스터는 값을 바꿀 수 없다.
TA = 피연산자주소값 - PC 주소값

Immediate addressing 즉시주소지정 #
i=1 n=0
명령어 앞에 #를 붙임
피연산자는 이미 명령어의 일부분임 (메모리 검색이 필요없다)
예시)
LDB #LENGTH <- 라벨명일 경우 LENGTH의 주소값
LDA #3 <- 피연산자가 명령어의 일부분(메모리 참조 X)
라벨명과 값 모두 사용 가능하다.

Indirect adressing 간접주소지정 @
i=0 n=1 address:TA
추가적인 명령어 사용을 피할 수 있다.

즉, 간접주소지정 방식이 Base 레지스터와 PC 레지스터 두개로 나뉜다.
n i x b p e
1 0 0 1 0 0 base 레지스터
1 0 0 0 1 0 pc 레지스터

Simple addressing
i=0 n=0 TA=disp
또는 i=1,n=1도 가능

뒤에 있는 주소값이 상대주소가 아닌 실제 주소값이면 SIMPLE ADDRESSING임


즉시주소지정방식 (#)

LDA OPCODE 0000
#이 붙은 즉시주소지정 방식이므로 i=1, n=0
피연산자 값 3
포맷에 맞게 2진수로 작성 후 16진수로 바꿔주면 (4비트가 한자리) OPCODE 완성


확장된 포맷 4를 사용해서 앞에 +를 붙인다. 따라서 e=1이다.
즉시주소지정을 사용해서 피연산자 앞에 #를 붙였고 i=0이다.
확장주소지정방식을 사용한 이유는 4096은 2진수로 16진수로 표현하면 1000이다. 따라서 2진수로 표현하면 포맷 3의 12자리 안에 표현할 수가 없다. 따라서 포맷 4로 표현한다.

RDREC이 저장되어 있는 주소가 1000번지 넘어감. 주소는 16진수니까 1000번지 넘어가면 마찬가지로 12bit안에 표현할 수 가 없어서 포맷 4를 사용했음. 따라서 명령어 앞에 +가 있다.
그리고 RDREC의 실제 주소값을 사용하는 Simple address 방식임
따라서 i=1, n=1이다. 확장 방식이니까 e=1이다.

상대주소지정방식

PC 레지스터를 사용하는 상대주소지정방식이다. 따라서 p=1이다.
변이(disp)를 사용하는데 피연산자의 주소에서 PC값을 뺀다


뺐는데 음수가 나올 경우 2의 보수를 취해서 음수표현을 해주면 된다.(반전시키고 1더함)

PC 상대주소일 경우
피연산자 주소 - PC값을 해줘서 disp에 사용하면 된다.

그리고 분명히 PC 상대주소 방식인데 ni xbpe 에서 n=1,i=1이다. 이건 simple address 방식일 때 인데 simple address는 뒤의 값이 실제 주소일 때다. 따라서 맞지 않는다.
교수님은 이걸 여러가지 주소지정 방식을 사용할 수 있어서 이렇게 된 거라고 하시는데 정확히는 이해가 안간다..


Base 레지스터를 이용한 방식이다.
PC 레지스터 방식을 사용하면 disp 값을 포맷 3의 3byte 내에 넣을 수가 없다.
따라서 Base 상대 주소 방식을 사용한다.
Buffer 위치에서 Base 값을 빼주면 된다.
이렇게 하기 위해 Buffer 앞에 있는 LENGTH라는 상수의 주소를 BASE 레지스터에 미리 넣어놓고 한 것이다.

헷갈려서 요약

ni xbpe

  • 즉시주소지정
    n=0 i=1
    피연산자 앞에 #이 붙어서 바로 값을 나타냄

  • 간접주소지정
    n=1 i=0
    피연산자 앞에 @ 사용
    뒤에 있는 주소값이 TA임

  • 상대주소지정
    b=1 p=0 베이스 레지스터 기준
    b=0 p=1 PC 레지스터 기준
    피연산자 - 레지스터 주소 로 disp 구함

  • simple addressing
    i=1 n=1 이거나 i=0 n=0
    변이값이 TA

  • n=1,i=1 simple addressing이 아닌데 왜이런거임?
    그냥 여러개 쓸수 있대 나도몰라

Relocation 프로그램 재배치

프로그램을 꼭 정해진 주소에서만 사용하는 것이 아닌 빈 메모리에 프로그램을 load 해서 사용한다.
상대 주소 방식은 상관이 없는데 절대 주소 방식은 재배치를 해줘야 한다. (앞에 +가 붙는 포멧4)

  • 어셈블러
    시작을 0번지로 놓고 SYMTAB 만들면서 LOCCTR(위치카운터)를 이용해 주소를 전부 부여한다
  • LOADER
    실행될 때 실제 0으로 시작한 주소값에 실제 시작 주소를 더해주면 된다.

구체적인 수정 방식은 다음과 같다.
+JSUB RDREC은 앞에 +가 붙은 포맷 4 방식이라 주소 수정이 필요하다.
m000007 05 명령어를 통해 수정한다.
명령어의 의미는 이렇다.
000007은 수정할 주소를 의미한다.
왜 7번지부터냐면, JSUB RDREC 자체는 6번지부터 시작한다. 하지만 앞의 4BJ는 JSUB에 해당한다. 즉 주소에 관련된 부분이 아니다.
뒤의 1/2바이트 5개가 피연산자인 RDREC의 주소에 해당한다.
따라서 7에서 뒤의 1/2바이트 5개인 부분을 수정하라는 의미의 명령어이다.


Machine-Independent Assember

지금까지는 기기 종속적인 어셈블러
이제부터 기기 독립적인 어셈블러

Literals 상수

리터럴 앞에는 접두사 =를 붙인다.
어딘가에 상수값을 정의하고, 라벨을 부여한다.
피연산자로 상수를 사용할 수 있다.

=c'EOF'라는 상수를 피연산자로 사용한다.
이렇게 하지 않으면 라벨을 정의하고 그 라벨을 피연산자로 사용해야 해서 메모리를 참조해서 비효율적이다.

LTORG는 메모리의 해당 위치에 상수값을 저장하는 코드이다.
프로그램 처음부터 LTROG를 만난 위치까지의 상수가 모두 LTROG 위치에 저장된다.
만약 LTORG가 없다면 리터럴 값은 프로그램 끝단에 모두 저장된다.

*는 LOCCTR의 값을 의미한다.
즉 * =C'EOF' 코드는 현재 LOCCTR을 불러와 그 위치에 상수값을 저장, 즉 현재 위치에 저장하는 것을 의미한다.


즉시주소지정방식과 리터럴의 차이점
즉시주소지정방식(#) 은 기계 명령어 상에 상수값을 넣는다.
리터럴(=)은 어셈블러가 상수값을 어딘가에 저장하고, 그 주소값을 명령어에 넣는 것이다.

Literal Pool

literal 피연산자들은 하나 이상의 literal pool에 모여있다.

  • LTORG가 없을 경우 END 문장 뒤에
  • LOTRG가 있을 경우 해당 위치에 literal pool이 생성된다.

리터럴은 중복되더라도 한번만 저장한다.
리터럴이 중복되는지 확인하는 방법은 2가지가 있다.

  • 문자만 비교
    문자만 비교하면 다른 값을 나타내는 같은 문자에 대해서 potential problem 발생
  • data value까지 비교
    복잡성이 증가하지만, 정확함
    ex) =C'EOF' <=> X'454F46'
    EOF라는 문자와 454F46이라는 16진수 값은 아스키코드상 같아서 같다.

LITTAB

LITERAL TABLE을 의미한다.
상수 이름
값과 길이
주소
를 저장한다.

또는 상수의 이름이나 값을 key값으로 갖는 hash table 구조로 LITTAB을 만들기도 한다.

LITAB 만드는 과정
Pass1

  • 상수를 만나면 LITTAB에서 상수 이름이나 값 검색

  • 중복일 경우 : 아무것도 안함

  • 중복이 아닐 경우 LITTAB에 추가함

  • LTORG나 END를 만나면 LITTAB 스캔함

  • 주소값이 비어있는 애들의 주소값을 할당해줌

Pass2

  • 상수 피연산자를 만나면 LITTAB에서 상수의 주소를 찾아서 피연산자 자리에 넣어줌

  • 만약 테이블에서 상수를 못찾으면, ERROR 발생

  • LTORG나 END를 만나면 object program에 상수들의 데이터 값을 넣어줌


EQU

symbol과 값을 정의할 때 EQU를 사용할 수 있다.
symbol EQU value
symbol이라는 라벨에 value값을 할당한다.
value는 상수나 표현식이 될 수 있다.


EQU는 이런 방식으로 사용한다.

+LDT #4096

은 이 명령어가 뭘 하고자 하는지 정확히 알기 힘들다.

MAXLEN EQU 4096
+LDT #MAXLEN

이렇게 수정하면 먼저 MAXLEN이라는 라벨의 값을 4096으로 놓는다.
그리고 사용하면 4096이라는 값의 의미가 MAXLEN(최대길이)구나.. 라고 알 수 있다.

ORG

ORG value
ORG 명령어는 LOCCTR 값을 value로 바꿔준다.

ORG를 사용하면 LOCCTR 값이 변경되기 때문에 다음 ORG를 만날 때까지 뒤의 모든 명령어들이 영향을 받는다.

ORG는 이러한 상황에 사용할 수 있다.

이렇게 한 줄에 6, 3, 2 BYTE인 데이터 구조를 만들고 싶다고 해보자 (총 100줄 = 1100BYTE)

ORG를 쓰지 않으면 이런식으로 +6, +9 로 만들어주는데 가독성이 떨어지고 의미하는 바를 알기 힘들다.

ORG를 쓰면 위와 같이 할 수 있다.
먼저 1100BYTE의 공간을 할당한다.
공간을 할당하면 LOCCTR은 1100BYTE 만큼 이동하게 된다.
ORG STAB으로 LOCCTR 값을 다시 STAB으로 되돌린다.
그리고 이 위치에서 6,3,2 로 데이터구조를 나눠준다.
그리고 다시 ORG STAB+1100으로 STAB 뒤로 LOCCTR을 이동한다.

가독성이 더 좋고 이해하기 쉬운 코드가 되었다..


전방참조 FORWARD-REFERENCE

이런 식으로 ALPHA가 정의되지 않았는데 참조하면 안된다. 전방참조 문제가 이런것임

이런 경우 처음에 DELTA
두번째에 BETA
세번째에 ALPHA 이런식으로 여러번 즉 MULTI PASS를 해야 전방참조 문제를 해결할 수 있다.

표현식

피연산자에는 표현식을 사용할 수 있다.
+,*,-,/ 같은 사칙연산을 사용할 수 있다.
나눗셈 결과는 무조건 정수이다.

표현식에는 절대값 Absoulte , 상대값 Relative를 사용할 수 있다.

상대항은 S+r의 형태로 표현된다.
S : 프로그램의 시작 주소
r : S에 대해 표현식의 상대적인 주소

예시 )

BUFFER : S+r1
BUFFEND : S+r2

상대항끼리 연산하면 빼서 상대항을 소거해야 한다.
상대항은 0 또는 1개만 있어야 한다.

BUFFER - BUFFEND = S-S+r1-r2 = r1-r2
BUFFER + BUFFEND = S+S+r1+r2

BUFFER - BUFFEND는 가능하지만 BUFFER + BUFFEND는 상대항이 2개가 되버려서 안된다.

SYMTAB에 상대항인지 아닌지 FLAG 값으로 기록한다.

Program Block

Program Block은 프로그램을 논리적으로 나누고 싶을 때 사용함
실제로(물리적으로) OBJCODE가 나눠지는 것은 아니고 재배치되기만 함. 즉 OBJCODE는 하나로 유지됨.

예를 들어 이런 식으로 나눌 수 있음
명령어들(DEFAULT), 길이가 짧은 데이터들, 길이가 긴 데이터들

USE 명령어를 사용해서 프로그램 블록을 지정할 수 있음
USE가 없으면 DEFAULT 프로그램 블록에 해당함
USE CDATA, USE CBLKS 명령어로
이하의 내용들을 원하는 블록으로 나눔

이런 식으로 USE만 사용하면 DEFAULT 프로그램 블록임
프로그램 블록을 지정하고 다시 다른 프로그램 블록을 지정하고 또 USE로 원하는 프로그램 블록을 지정하고 하는식임

프로그램 블록을 사용하면

  • 가독성을 높인다
  • BUFFER 같은 크기가 큰 메모리를 뒤에 몰아 넣음
    등의 효과가 있다.

Program Block에 따라 rearragne 하는 방법

Pass 1

  • 각 블록마다 LOCCTR을 따로 둠 (각각 0으로 초기화)
  • 다른 블록으로 넘어가면 값을 저장해놓고 다시 돌아오면 저장해놓은 값부터 진행
  • 각 라벨에 라벨이 속한 블록을 기준으로 한 상대주소를 할당함
  • SYMTAB에 라벨이 속한 블록 이름(또는 번호), 상대주소와 함께 라벨을 저장함
  • Pass1이 끝날 때 각 블록의 LOCCTR 값으로 각 블록의 길이를 알아냄
  • 이 정보를 토대로 objcet program에 각 블록의 시작 주소를 할당함

Pass 2
각 symbol의 주소값을 계산함 (속한 블록의 시작 주소를 더함)


OBJECT PROGRAM에서 실제로 program block에 따라 물리적으로 재배치하지는 않음
그냥 어떤 블록에 속하는지 기록한다.

Object Program은 소스코드랑 같은 순서로 만들고 실제로 메모리에 load 할 때 블록마다 묶어서 load한다.

Control Sections 제어 섹션

제어 섹션은 실제로 OBJCODE를 제어 섹션에 따라 나눠서 만든다.
즉 물리적으로 실제로 나눠짐 = 따로따로 실행 가능
함수별로 (덧셈 기능, 뺄셈 기능.. ) 나누거나 할 때 사용
유연한 프로그래밍 가능

Program Linking

프로그램을 메모리에 load하는 것을 linking이라 함
linking 할 때는 연관된 제어 섹션과 같이 link 해야함
문제는

  • 어셈블러가 각 제어 섹션의 위치를 알 수 없다
  • 다른 제어 섹션의 라벨을 참조하면 그 주소를 알 수 없다

문제를 해결하기 위해 외부참조 external references 를 사용함

External references 외부 참조

COPY START 0 으로 기본적인 제어섹션인 COPY 시작
EXTDEF BUFFER, BUFEND, LENGTH
이 명령어는 다른 제어 섹션에서 COPY 제어 섹션의 라벨인 BUFFER, BUFEND, LENGTH를 가져다 쓸 수 있게 함

EXTREF RDREC, WRREC
이 명령어는 외부 제어 섹션인 RDREC과 WRREC를 COPY 에서 접근할 수 있게 함

다른 제어 섹션인 RDREC임
RDREC CSECT 로 제어 섹션을 선언함
EXTREF BUFFER,LENGTH,BUFEDN 명령어로 COPY 제어섹션에서 외부에서 가져다 쓸 수 있게 해준 라벨들을 가져다 씀
그런데 RDREC 제어 섹션은 가져다 쓰라고 한 적이 없는데 COPY는 어떻게 가져다 쓸까?
제어섹션은 기본적으로 가져다 쓸 수 있다.

START
첫번째 제어 섹션

CSECT
새로운 컨트롤 섹션

EXTDEF
외부 심볼 정의 (외부에서 사용할 수 있게함)

EXTREF
외부에서 가져와 사용할 것을 정의


외부 참조한 라벨들은 주소값을 모른다.

어셈블러가

  • 확장형 (포맷 4) 사용
  • 일단 0으로 주소 채움
  • LOADER한테 나중에 채우라고 말해놓음

외부참조한 라벨들은 모두 포맷 4 사용, 피연산자 주소값을 0으로 채웠다.

Object Program에서는
정의 레코드, 참조 레코드, 수정 레코드를 추가로 사용함

정의 레코드는 D 사용
외부에서 사용할 라벨 이름과 주소를 나열함
참조 레코드는 R 사용
외부 제어 섹션에서 가져와 사용할 라벨들의 이름을 나열함
(주소는 몰름)
수정 레코드 M 사용
프로그램이 실제로 load 되면 이제 주소를 알 수 있음
그래서 외부 참조한 라벨들의 주소를 수정해줘야함
M0000405+RDREC
3번지부터 명령어 시작인데 맨 앞 3번지는 OPCODE임
따라서 4번지부터 5 half-byte만큼 수정함.
그리고 참조해온 제어 섹션의 이름이나 라벨의 이름을 더해줘서 값을 수정해줌

RDREC 제어 섹션인데
주소가 아니라 값 자체를 수정해줘야 하는 경우는 6 half byte 수정함

값은 BUFEND - BUFFER인데 먼저 BUFEND를 더하고 BUFFER를 빼줌

참고로 상대주소 방식에서 연산할 때는 무조건 같은 제어 섹션에 있는 라벨끼리 연산해야함.


ONE - PASS ASSEMBLER

2PASS 는 중간 코드가 필요해서 느림
-> 1 PASS 사용

하지만 1PASS는 전방참조 문제 발생

LOAD-AND-GO ASSEMBLER

LOAD 하고 나서 바로 실행함
OBJECT PROGRAM을 만들지 않음, LOADER 필요 없음

  • 전방참조를 어떻게 해결하는가

    SYMTAB을 만들 때 주소 모르면 일단 *로 놓고 넘어감
    대신에 LINKEDLIST 형태로 이 SYMBOL의 주소를 알게 되면 어디다 적어야 하는지 기록해놓음

LINKED LIST 형태를 사용하는 이유는 다른 곳에서도 알아야 하면 이렇게 연결해놔서 연쇄적으로 알려주기 위함!

MULTI-PASS ASSEMBLER

모든 값을 알 때까지 반함


MAXLEN/2를 해야 하는데 MAXLEN 을 모름
MAXLEN을 위한 공간을 만들고 값을 모르니까 *로 둔다
그리고 LINKED LIST 형태로 어디다가 알려줘야 하는지 기록한다
그리고 HALFSZ에는 &1로 모르는 값이 1개 있다고 표시한다.


MAXLEN을 알기 위해서는 BUFEND와 BUFFER를 알아야함
얘는 2개를 몰라서 &2임


PREVBT도 BUFFER를 알아야함. 그래서 BUFFER에 LINKEDLIST 노드를 추가함

만약에 알게 되면 LINKED LIST에 있는 애들한테 알려주고 노드를 지움
알게 된 애들은 &에서 숫자를 줄임

0개의 댓글