[1] 제목 : 감히 어셈블러 강의를 해볼까 합니다.
 올린이 : 까망벌레(정태식  )    94/10/24 17:25    읽음 : 599  관련자료 없음

안녕하세요...
후후....아직 가입도 안된 상태에서 무슨 글을 올린다는것이.
조금 그렇네요,
하지만 여기 와 보니깐 어셈블러에 대한 강의가 하나도 없기에
제가 먼저 싲가해 볼까 합니다.
저도 사실 아는거 별로 없지만 그래도 이것만이라도 여러분과 함께
나누고 싶어서 .....
우선 어셈블러에 들어가기 앞서 하드웨어의 기본적 구성에 대해
올릴까 합니다. 조금 지겹더라도 읽어 주세요...
그리고 혹시 모르시는 점이나, 아님 궁금한점,
또는 흉을 보실일이나 분제점 등이 있다면,
과감히 말씀 하셔요~
헤헤~
그럼 시삽님에게 양해를 구하며 시작 해 보겠습니다.
처음 시작하는 것은 [초급-기초]로서 여기서는 하드웨어의
기본적인 구성에 대해서 아주 간략히 소개해 드립니다.
그 다음 [초급-실습] 에서는 직접 프로그램들을 해 보면서
각각에 대해 관련된 설명들을 해 나가겠습니다.
그리고 [초급-보충]은 설명 중간중간에 말하기에는
너무 양이 많고 중요한 것들을 따로 묶어서 설명을 하겠습니다.

그 다음 과정으로 물론 [중급]이겠지요....
여기서는 프로시져 및 함수들, 그리고 C언어와의 링크등을
설명할 계획이고요, 또한 의사명령에 대해 알아보겠습니다.

[상급]과정은 과연 제가 할 수 있을지는 모르지만...
헤헤.,...아직 까지는 저도 상급과정을 공부하는 중이라...
아마 한다고 하면 여기서는 시스템 프로그래밍 기법에 대해
할 계획 입니다.
시스템 프로그램이란 SUPER VGA메모리및 직접 제어, 또는
모뎀이나 클럭주파수 등과 같은 것들을 다루어 볼까 합니다.
사실 저로서는 자신이 없네요....
저도 딸리니깐요...헤헤~
일단은 하는데 까지 해보겠습니다.


 [3] 제목 : [초급-기초] 맛보기...
 올린이 : 까망벌레(정태식  )    94/10/24 17:28    읽음 : 828  관련자료 없음


맛보기...


어떤 프로그램을 실행시키게 되면, 과연 컴퓨터는 그 과정을 어떻게 처리하는지

에 대해 일단 간단히 설명을 드리겠습니다.

프로그램은 DOS 에서 화일이라는 방법으로 디스크에 저장되어 있습니다.

하지만 컴퓨터 자체에서 이 프로그램을 수행할려면, 디스크 상에서 직접 수행을

하지는 못함니다. 그래서 이 프로그램을 램(이하 메모리)으로 읽어 들이게 됩니다.

그럼 메모리로 읽어 들인것 만으로 수행이 가능 할까요?

그렇지는 않습니다. 왜냐하면 램에서는 연산기능 ( 예를 들면 덧셈 뺄셈 등등 )

을 직접 하는 기능이 없기 때문 입니다. 램에서는 그냥 데이터를 보존하는 기능

밖에는 없습니다. 이런 점에서 디스크와 별다른 차이점은 없습니다. 하지만 램이 꼬옥

필요한 이유중의 하나는 데이터를 가져오거나 보관할때 디스크와는 속도가 차원이

다르게 빠르다는 것이지요.            그럼 왜 프로그램을 수행하는데 있어서

이러한 연산기능이 꼬옥 필요할까요? 그건 모든 프로그램들은 근본적으로 어떠한

주어진 데이터를 더하거나 빼거나 해서 수행되어지는 일련의 과정들이라고 할 수 있기

때문입니다.

그럼 과연 어디서 이러한 연산들을 수행하는가 하면, 여러분도 아시다 시피 바로

컴퓨터의 가장 핵심부분이라고 할 수 있는 CPU라는 곳에서 수행하고 있습니다.

정리해서 총괄적으로 다시 말하자면, 일단 어떤 프로그램을 실행하기 위해서는

디스크로 부터 화일의 내용을 메모리로 읽어 들여 그 메모리에 저장되어 있는

데이터                                                                                                                                                                                                                                                           
레지스터란 과연 무엇인가?

비유를 하자면, 연습장이라고 할 수 있습니다. 물론 이 '연습장'이라는 단어가

레지스터의 모든 기능을 나타내어 주는 단어는 결코 아닙니다. 그냥 레지스터가

담당하는 수많은 기능들의 한 부분(기능)만을 뜻하는 것입니다.

그리고 메모리라는 것은 어떤 주어진 데이타 목록 이라고나 할까요.

( 여기서 데이타는 컴퓨터에게 직접 수행 명령을 주는 명령어도 포함 됩니다 )

우리는 이 목록에서 원하는 값을 얻기 위해 연습장에다가 쓰고 지우고 하게

됩니다. 그래서 최종적으로 원하는 값을 얻게 되지요.

사실 레지스터라는것은 별거 없습니다. 메모리와 거의 같은 구조로 되어 있고,

여기에 그 메모리(레지스터) 속에 들어 있는 데이터를 연산하는 기능이 하나

추가된 것이라고 생각 하시면 간단합니다.


예를 들어 '3+4=' 이라는 문제의 답을 원한다고 합시다.

이것은 우선 어떤 화일(디스크)에다가 저장을 시켜야 겠지요.

그럼 일단 컴퓨터에서는 이 '3+4='라는 '데이터'를 메모리로 읽어 오게 됩니다.

( 엄격히 말하자면 여기서 데이터라는 것은 '3' 과 '4'뿐이고 '+' 나 '='라는 것은

컴퓨터에게 주는 연산 명령이라고 할수 있습니다. 하지만 이 '데이터'와 '연산명령'을

일단은 메모리로 모두 읽어오게 됩니다 )

다음으로 이 첫번째 값인 '3'을 레지스터에 집어넣게 됩니다. 그 다음 '+' 라는 것을

읽어 옴으로써 '아.. 3 에다가 어떤 값을 더하라는 것이구나' 라고 생각하고

준비합니다. 그 다음에 오는 '4' 를 보구서 미리 준비했던것 같이 바로 '3'에다가

'4'를 더해서 그 결과를 얻게 되는 것입니다.

그 결과를 어디에 놓는지는 나중에 설명 드리겠습니다.


( 혹시 램(RAM) 에 대해서 모르시는 분이 계시지는 않겠지요 )


흐~~~ 역시 버벅거리는 경향이 있네요...

여러분꼐서 읽어 보시고 부족한 점이나 궁금하신 점이 있으시면 과감히 물어보세요

사사로운것도 좋습니다.

그리고 흉을 보셔도 괜찮습니다. ( 뽀드득 ) 히~~~

다음에는 메모리(RAM)에 대해 간단히 설명해 드리겠습니다.









 [4] 제목 : [초급-기초] 메모리에 대해서 #1
 올린이 : 까망벌레(정태식  )    94/10/24 17:30    읽음 : 627  관련자료 없음

두번째로 메모리에 대해 설명을 드리고자 합니다.

어셈블러 강의를 한답시고 램, 아니면 이상한 것들만 주욱 늘어놓는지에 대해

투덜거리시는 분이 계실 지도 모르지만... 사실 이런것들을 제껴 놓고 어셈블러에

대해 설명한다는 건 불가능 하다고 생각하기 때문 입니다. 다른 어떤 교재를 봐도

다아 이렇게 시작해요...히힛~


그럼 메모리에 대해서...

메모리가 몰까.... 아마 램이 1메가라는 둥 4메가라는 둥.... 이런 말들은 마니

들어보셨을줄로 압니다. 이게 바로 메모리라는 것이지요. 뒤에 '메가'라는 것은

그 메모리가 저장할 수 있는 데이터 양을 말하는 것입니다.

메모리의 구조에 대해 말씀드리겠습니다.


1. 비트, 바이트???

아마도 여러분은 '비트' 또는 '바이트' 라는 용어에 대해 한번쯤은 들어보셨을 줄로

압니다. ( 처음 듣는 거라도 상관은 없지요 ... )

과연 이 비트(bit)라는 것은 무엇일까..... 여러분 2진수에 대해 아시죠?

갑자기 왠 2진수냐고요? 흐...그거에 대해 또 설명을 드리지요.

컴퓨터 ( 이하 PC ) 는 모든 움직임이 전기로 되도록 만들어 졌습니다. 하지만

멍청하게도 이 PC라는 것이 전기가 '흐른다', '안흐른다' 라는 것 밖에는 구분을

못합니다.

사람 같으면 전기의 세기정도를 구분할 수 있지만 아쉽게도 그걸 구분하지는

못합니다. 그래서 PC는 이 '흐른다', '안흐른다' 로 모든 것을 인식할 수 밖에는

없지요. ( 보통 이것을 '스위치'를 '켯다' 또는 '껏다' 라고 비유하기도 합니다.)

여기서 2진수와의 공통점을 발견할 수 있습니다. 2진수에서 역시 0 과 1의

두가지 숫자밖에 사용할 수 없고, 이 두가지 숫자로 모든 수를 표현 할 수 밖에

없지요. 그래서 이 두가지 공통점을 연결해서 '흐른다 = 1', '안흐른다 = 0' 으로

보통 약속되어져 있습니다. 그래서 PC 자체에서 일어나는 과정들을 2진수로 표현하게

됩니다.


다시 '비트' 로 넘어오지요...

우선 10진수 54라는 것이 메모리에 어떤 모양으로  저장 되는지 살펴보겠습니다.

그냥 54라고 저장하면 된다고요? 헤헤헤.... 어떻게요? PC는 0과 1이라는 숫자만

사용할 수 있다고 했는데....

그럼 이 54를 2진수로 바꿔 보겠습니다.

110110

이렇게 바뀌지요. 그럼 이걸 어떻게 저장하지요?

아까 '스위치'라는 걸 예를 들었는데요, 이 각각의 자릿수의 숫자


'1' , '1' , '0' , '1' , '1' , '0'


들을 각각의 스위치로 대비시켜 볼 수 있습니다.

'1' 이라는 숫자는 스위치를 '켜다', '0' 은 '끄다' 로 정한다면


'켜다' , '켜다' , '끄다' , '켜다' , '켜다' , '끄다'   <-- 스위치

  1        1         0       1         1        0     <-- 2진수


라고 이쁘게 바뀌었지요? 그리고 스위치가 전부 6개가 사용 되었습니다.

이렇게 하면 드디어 PC에서 알아 들을 수 있는 방법으로 바뀌게 되었습니다.

메모리에는 이런 스위치가 수도없이 들어 있습니다.

이런 스위치들의 어떤 부분이 위와 같이 고정되어 있다고 한다면, PC는 메모리를

보면서 스위치들의 조합 상태를 보고 언제라도 54라는 숫자를 꺼내올 수가 있는 거죠.

이제 '비트'의 정의를 내리겠습니다.

위의 예에서 스위치가 전부 6개가 사용되었다고 했지요?

이 비트라는 것은 '스위치' 하나를 지칭하는 단어 입니다.

위의 예를 본다면 '10진수 54를 나타내기 위해서 전부 6비트가 사용되었다'

라고 말 할수 있습니다.

메모리라는 것이 바로 이 수 많은 스위치들의 집합체인 것입니다.

그럼 바이트(Byte)는 ?

바로 이 비트 8개를 묶어 1바이트라고 합니다.

예를 들어 연필 12개를 1타스 라고 부르는 것과 동등 합니다.

정리 하자면


8 bit  =  1 byte

1024 byte  =  1 Kbyte

1024 Kbyte  =  1 Mbyte


라고 요약할 수 있겠지요...


2. 메모리 처리방식

PC의 처리 방법은 이 'bit'라는 개념 보다는 'byte'라는 개념을 주로 사용

합니다. 다시 말하면 만약 이 54라는 10진 숫자를 2진수로 바꾸면 , 110110

이라는 6개의 비트만으로 표현이 가능 하지만, PC 자체에서는 00110110 과같이

항상 8개의 비트를 묶어서 저장하게 됩니다.  좀더 구체적으로 만약 54 와 3이라는

숫자를 연달아 메모리에 저장하게 되면

110110 11   이라고 저장하면 비트(메모리)를 조금만 사용해도 되는것을

구지 00110110 00000011 이렇게 항상 각각의 데이터들을 8비트, 즉 1바이트로

저장하게 된다는 뜻입니다.

이유는 원래 생겨먹은게 그렇게 생겨 머거서 그렇죠 머...히히~

(자세한건 다음 기회에... )

그래서 앞으로 모든 수행은 ( 특별한 경우 이외에는 ) 이 byte 개념으로 설명이

됩니다.


3. ASCII 코드에 관해

그럼 과연  영문자 ( abcdefg ) 와 같은 것들은 메모리에 어떻게 저장이

될까.....라고 궁금해 하신다면,

컴퓨터는 숫자밖에 인식을 못합니다. 그래서 이 영문자들도 다른 컴퓨터가 인식할 수

있는 숫자로 대치해 주어야만 합니다.

일단 이것부터 설명을 드리지요... character (문자) 의 개념에 대해서 말이지요.

이부분을 잘 이해하셔야만 합니다. 사실 이해하기 쉽도록 설명하기가 여간 곤란한

내용이 아닐 수 없네요...하지만 용기를 가지고...

PC에서는 모든 문자, 예를 들면

a 부터 z, A 부터 Z, 0 부터 9, 또는 ~!#@$#$%^&*()_+=-\| 와 같은 특수 문자들...

이 모든 문자들에 각기 값이 주어져 있습니다. 이것은 IBM PC 어느 컴퓨터에서나

각 대응되는 값들은 항상 같습니다.

( 참고로 숫자 0 부터 9들도 모두 문자로 취급 합니다 )

예를 들어 ,

a = 97

b = 98

...

2 = 50

A = 65

B = 66

..

$ = 36

...

이와 같이 각 문자들에는 고유 값들이 있습니다.

이것은 전 세계 표준으로 어딜가나 같습니다.

( 대부분 어셈블리어 책에는 이런 ASCII코드 표가 수록되어 있습니다. )

특히 주의 하실 점 두가지는 PC에서는 영문 대문자와 소문자의 대응 값(ASCII 코드

값) 이 다르게 주어진다는 것입니다.

그리고 또 하나는 숫자 5 와 문자 5는 엄연히 종류가 다른 내용입니다.


그럼 문자 'a'라는 것이 메모리에 어떻게 저장 되는지 알아 봅시다.

a 라는 문자의 ACSII코드 값은 97 이라고 정의 되어 있습니다.

그럼 이것을 2진수로 바꾸어 보겠습니다.

01100001  이렇게 바뀌지요...

그럼 끝 입니다. 이걸 바로 메모리에 저장 시키면 되는 거지요...

하지만 여기서 문제가 하나 발생 합니다.

과연 PC가 메모리에서 01100001이라는 값을 받아들일때 과연 이것을 10진수로 97이라는 값으로 받아들이냐... 아니면 이것을 문자 'a'로 받아들이냐가 발생합니다.

97이라는 값으로 받아들이냐... 아니면 이것을 문자 'a'로 받아들이냐가 발생합니다.

하지만 걱정 마시길....사용자 측에서 만약 이것을 문자로 취급해라 하면 문자로

취급하고 숫자(값)으로 취급해라 하면 값으로 취급한다는 것입니다.

다시 말해 만약 97이라는 값을 저장 시켰다고 했을때, 사용자가 이것을 값이 아닌

문자로 취급해라 하면 이 값이 문자로 바뀌어서 취급된다는 점...

반대로 'a'라고 저장을 시키더라도 메모리에는 97이라는 값이 저장되게 되므로 이를

값으로 취급하라고 하면 값으로 취급한다는 것입니다.

이제 문자 'I have 35$' ( 이거 문법에 맞는지는 모르지만 ) 를 메모리에 어떻게

저장 시키는지 알아봅니다.

I = 73, h = 104, a = 97, v = 121, e = 101, 3 = 51, 5 = 53, $ = 36

이렇게 정의 되어 있습니다. 그리고 문자들 사이의 Blank(공백)은 0으로 값이 정해져

있습니다. 이걸 토대로 메모리에 저장 되는 형태를 살펴 보지요...

01001001 00000000 01101000 01100001 01111001 01100101 00000000

   I       공백      h         a        v        e      공백


00110011 00110101 00100100

   3         5        $


이와 같이 메모리에 순서대로 저장되어 집니다.

여기서 하나 중요한 점은 한 문자를 기억 하는데는 8비트, 즉 1바이트의 메모리를

사용한다는 점을 알 수 있습니다.

그리고 영문 모두 합치고 숫자 그리고 특수문자 모두 합쳐바야 문자의 종류가 255개가

되질 않습니다. ( 126까지에서 모든 사용 가능한 문자가 대응될 수 있습니다)

그럼 127번 부터는 어떤 문자가 되응 되어 있을까요?

그건 확장 ACSII코드라고 하는 특수 문자가 대응되어지고 있습니다.

보통 우리들이 키보드 상으로는 입력할 수 없는 문자들.. 예를 들어 하트 모양이나

네모, 또는 직선, 십자표시 등등이 대응되어 있습니다.

만약 이런 문자들을 화면상에 찍어보고 싶다 하신 분들은,

일단 ALT키를 누르고 계세요, 그다음 오른쪽 숫자 키패드에서 228을 친 다음 알트키를

때 보세요...그럼 아마 시그마 기호가 찍힐 겁니다. 이런 식으로 확장 아스키 문자도

사용 할 수 있습니다.


제가 보기에도 설명이 좀 미흡한데....모르시는 점 계시면 물어보시길...

메모리에 관한 설명은 다음에 계속 됩니다.



 [5] 제목 : [초급-기초] 메모리에 대해서 #2
 올린이 : 까망벌레(정태식  )    94/10/24 19:41    읽음 : 540  관련자료 없음

1. 메모리 찾아가기 ...

혹시 1메가의 메모리에는 전부 몇개의 스위치(비트)가 있는지 한번 계산해

봅시다.

겨산해 보면......8388608개의 어마어마한 숫자가 나옵니다.

이걸 byte로 바꾼다고 해도 1048576byte 라는 무시 못할 숫자가 나옵니다.

전에도 말씀 드렸다 시피, PC에서는 대부분의 데이터 처리를 byte로 한다고

말씀드렸습니다.

예를 들어 큰 건물을 생객하 보죠. 여기에는 전부 1048576개라는 갯수의 방이 있

다고 해봅시다. 그리고 각각의 방에 순서대로 1번 부터 2,3,4,5....1048576 이렇게

번호를 매길 수 있겠죠. 그리고 각 방은 크기가 모두 같습니다. 그럼 이 각각의

방들에 어떤 물건들을 넣어 놓습니다. 그리고 모든 방들이 꽈악 찼습니다.

여러분은 만약 여기서 지금 필요로 하는 물건을 꺼내기 위해서는 이렇게 많은

방들 중에 필요로 하는 물건이 어디에 있는지 어떻게 알수 있죠?

당연히 그 물건이 들어있는 방의 번호를 알 고 있다면 아주 쉽게 찾아 올 수

있습니다. 마찬가지로 메모리도 이와 같이 각각의 바이트 마다 번호가 매겨져

있다고 할 수 있습니다. 그리고 각 방의 크기는 1바이트의 크기로 정해져 있는

것이지요.

이것을 3차원 적이 아닌 1차원 적으로 생각하시는 것이 훨씬 편합니다.

다음과 같이 ...

 5번방  6번방  7번방  8번방  9번방  10번방.....

;------------------------------------------

; 사과 ;  배  ; 휴지 ; 담배 ; 성냥 ;  꽃  ; .......

;------------------------------------------

그럼 성냥을 꺼내오기 위해서는 9번방을 찾아가서 꺼내오면 돼겠지요.

그럼 메모리로 시선을 바꾸어 보겠습니다.

만약 메모리에 'IBM PC 486' 이란 문자들을 메모리로 기억 시켰다고 하면,

  15  16  17  18 19  20  21  22  23  24   <-- 방 번호

;----------------------------------------

; I ; B ; M ;   ; P ; C ;   ; 4 ; 8 ; 6 ; ................

;----------------------------------------

 이와 같이 그림으로 나타낼 수 있습니다. 만약 pc에서 지금 4라는 문자를

가져오고 싶다고 한다면 그냥 22번 방에 있는 내용을 읽어오면 되는 겁니다.

보통 위와 같이 15, 16, 17,18 과 같은 숫자들을 주소(Address)라고 명합니다.


-- 참고 -- 16진수에 대해

컴퓨터에서 주로 사용하는 숫자의 진수는 2진수라고 했습니다.

하지만 2진수라는 것은 PC에서 보기에 사용하기 좋지만 사실 사용자가 보기에는

정말 까다롭고 번거롭기 그지없는 진수 입니다. 그럼 이 2진수라는 숫자를 좀 더

사용자 쪽에서 사용하기 편한 숫자로 대치해 보는 것도 좋겠지요. 그럼 이걸 10

진수로 바꾼다고 한다면 물론 사용자 쪽에서는 더이상 바랄것이 없겠지요.

하지만 이 10진수와 2진수와의 상호간 변환은 많은 계산을 필요로 한다는 문제점을

안고 있습니다. 그 대신 사용하는 진수가 바로 16진수라는 것입니다.

이것은 2진수와의 상호 변환이 매우 간단하기 때문 입니다.

가령 11101001 이라는 1바이트 숫자를 16진수로 바꾼다고 한다면, 아마 이걸

10진수로 바꾸어서 다시 16진수로 바꾸는 방법이 있을 수 있겠지요. 하지만

이런 방법 보다 더욱 편한 방법이 있습니다.

바로 이 2진수 숫자들을 오른쪽에서 부터 4개씩 짤라가는 것입니다.

1110, 1001 이렇게 말이지요 그럼 이 뒷부분의 숫자를 16진수로 바꾸면

9 라는 숫자가 나옵니다. 그리고 앞의 것은 14가 나오죠. 이걸 각각 16진수로 하면

9 와 E 가 됩니다. 그럼 이걸 그대로 쓰면 9E 라는 16진수로 쉽게 변환 되는 것이자요


1110 1001   <-- 2진수

  9    E    <-- 16진수

이렇기 때문에 PC에서는 16진수를 많이 사용하게 됩니다.



2. 세그먼트(Segment) 와 오프셋(Offset)

그럼 PC에서는 이러한 주소들을 어떻게 관리하는지에 대해 알아보도록 하겠습니다.

우선 레지스터에 관해 조금만 말씀드리겠습니다.

CPU에는 레지스터라는 장소가 있습니다. 전에 말씀드렸다시피 레지스터라는것은

메모리와 거의 차이가 없다고 했습니다. 그래서 레지스터라는 것은 데이터를 저장

하고 그것을 연산하는 것인데, 역시 데이터를 저장하는데 있어서 그 용량(크기)

의 제한이 있다고 할수 있습니다.

모든 레지스터들은 그 크기가 65535로 제한이 되어 있습니다. 이걸 2진수로 바꾸면

1111111111111111 이 되어 전부 16비트가 됩니다. 즉 바로 2바이트의 크기 제한을

가지고 있습니다. ( 참고로 2바이트를 보통 1워드라고 불릅니다 )

16진수로 바꾸면 FFFF 로 제한이 되어 있다고 할 수 있죠.


메모리 주소를 알아내고 찾고 하기 위해서는 일단 레지스터라는 곳에 메모리

주소를 넣어놓으면 그 주소에 있는 데이터를 가지고 작업을 하게 되는 것이지요.

그럼 여기서 문제가 생갑니다. 그건 바로 1048576개의 주소들을 각각 1대1로

대응되도록 해야 하는데, 레지스터의 제한은 65535로 제한되어 있다는 것입니다.

만약 3450번지에 있는 데이터를 조작하기 위해서는 레지스터에 먼저 3450이라는

수를 넣어주면 되지만, 98500번지에 있는 데이터를 조작하기 위해서는 65535로

제한되어 있는 레지스터에서는 불가능 하게 됩니다.

그럼 어떻게 하면 될까요?

바로 레지스터 2개를 사용하면 됩니다. 즉 2개의 레지스터들이 메모리의 주소를

지시하게 하면 가능하게 되는 것이지요.

그럼 2개의 레지스터, 즉 4바이트의 용량으로 컨트롤 할 수 있는 메모리의 크기는

4294967295바이트, 즉 약 4095메가의 메모리를 컨트롤 할 수 있다는 결론이 나옵니다.

충분하지요? 하지만, 하지만 말이죠 도스 상에서 직접 컨트롤 할 수 있는 메모리의

크기는 위의 숫자와는 다른 약 1메가의 메모리밖에 컨트롤 할 수 없습니다.

왜 그럴까요? 분명 2개의 레지스터가 메모리주소를 지시하는것만은 틀림 없습니다.


여기에 바로 세그먼트와 오프셋이라는 개념이 나옵니다.

바로 위의 두개가 메모리주소를 조작하는데 사용되는 용어 입니다.

잠시,절대주소 라는 것이 있습니다. 각 메모리의 방에는 각각 고유의 숫자가 정해져

있습니다. 이 숫자들을 절대주소 라고 부릅니다. 위에서 보았듯이 이 절대주소를

나타내기 위해서는 하나의 레지스터만으로는 불가능 하겠죠?

세그먼트와 오프셋 모두 각각 1워드(2바이트)의 크기를 가지고 있습니다.

이 두개가 과연 어떻게 메모리의 절대 주소를 나타낼까....

여기선 간단한 계산이 필요 합니다.

예를 들어 만약 세그먼트가 1FE4 이고 오프셋이 43DA 라고 한다면 ,

( 물론 16진수 입니다 )

절대 주소를 구하는 방법은 식으로 나타내자면

세그먼트 * 10(16진수에서의 10입니다) + 오프셋

이렇게 하면 절대 주소가 나오는 것이지요.

위의 예를 가지고 계산해 보면

세그먼트에 우선 16진수 10을 곱해 봅니다

1FE4 * 10 = 1FE40

그리고 여기에 오프셋을 더하면

1FE40 + 43DA = 2421A

그럼 2421A가 바로 절대 주소를 나타내는 값입니다.

10진수로 바꾸면 147994 의 절대주소값이 되는 것입니다.

이것을 보통 1FE4:43DA 이렇게 씁니다.

앞에것이 세그먼트, 뒤에것이 오프셋이 되는 것입니다.

참고로 여기서 주소 지정의 비 유일성이라는것이 있는데,

이것은 다음에 설명을 드리지요...

 그리고 다음에는 레지스터에 관해 대략적으로 설명을 드리지요..




 [6] 제목 : [초급-기초] 레지스터에 대해서
 올린이 : 까망벌레(정태식  )    94/10/25 02:55    읽음 : 524  관련자료 없음

레지스터에 관해...

전에 말씀 드렸듯이 메모리로만 읽어오는 것으로는 프로그램이 수행되어

지는 것은 아니라고 말씀 드렸습니다. 그리고 이 프로그램을 직접 수행하고

처리하는 곳이 바로 레지스터라는 말씀도 드렸습니다.

레지스터라는 것을 따로 설명하기에는  많은 무리가 따르기 때문에 후에

직접 프로그램을 실습해가면서 익히도록 하겠습니다.

그런 의미에서 이 내용들에 너무 매달리지 마시라는 부탁을 드리고 싶습니다.



어셈블리어를 하는데 있어서 가장 중요하고 자주 쓰이는 것이

바로 이 레지스터들 입니다.

'들' 에서도 알 수 있듯이 레지스터라는 것이 하나가 아닌 여러가지가 있다는

걸 알 수 있습니다. 그럼 간단히 모든 레지스터들의 구조를 그림으로 간략히

그려보겠습니다.


EU : Execution Unit

;---------;

; AH ; AL ;   AX

;---------;                       BIU : Bus Interface Unit

; BH ; BL ;   BX                  ;----------;

;---------;                       ;    CS    ;

; CH ; CL ;   CX                  ;----------;

;---------;                       ;    DS    ;

; DH : DL ;   DX                  ;----------;

;---------;                       ;    SS    ;

;   SP    ;                       ;----------;

;---------;                       ;    ES    ;

;   BP    ;                       ;----------;

;---------;

;   SI    ;

;---------;

;   DI    ;

;---------;


;---------;

;   IP    ;

;---------;


Flag Register

;-----------------------------------------------;

;  ;  ;  ;  ;OF;DF;IF;TF;SF;ZF;  ;AF;  ;PF;  ;CF;

;-----------------------------------------------;


<< 참고 서적 : IBM PC Assembly Language and Programming  ( Peter Abel ) >>


대략적 구조를 그려 봤습니다. 이것만 가지구는 전혀 모르시겠지요?

그럼 위의 그림을 가지고 설명을 하겠습니다.


1. 대이타 레지스터 ( Data Registers )

데이타 제리스터란 바로 AX, BX, CX, DX 이 네개의 레지스터들을 한꺼번에

가르키는 용어 입니다.

구지 이렇게 용어를 정해서 나누는 이유는 확실하게 이런 레지스터들의 기능이

구분되기 때문에 주저없이 이렇게 나누고 있는 것입니다.

그럼 과연 데이타 레지스터들의 용도와 구조는 어떻게 되어 있을까...

일단 정리를 해 보면


 AX : 누산기 ( 산술연산, 포트로 부터의 I/O )

    I/O --> ( Input, Output ) 의 약칭

 BX : 베이스 ( 데이타 구조의 시작 주소 )

 CX : 카운트 ( 카운트, 비트 쉬프트 카운트 )

 DX : 데이타 ( 산술연산, I/O주소 )


<< 참고 서적 : Microsoft Macro Assembler Bible ( Barkakati & Hyde ) >>


이렇게 개요적으로 말할 수 있습니다.

일단은 이렇게 넘어가기로 하지요. 자세한 기능들은 하나하나 설명하기엔 너무나

방대하고 또 아직까지 거기까진 좀 무리라는 생각때문 입니다.


그럼 다음으로 이 레지스터들의 생김새에 대해 말하지요.

일단 위의 그림에서 보면 네모칸 오른쪽에 AX, BX와 같이 써 있고 네모칸 안에는

AH AL, BH BL 이라고 써 있는데요, 이건 무엇을 의미 하냐면, 전에 말씀 드렸듯이

레지스터라는 것의 크기가 1워드라는 말씀을 드렸을겁니다. 그리고 또한 PC의

데이터 처리는 1바이트 단위로 이루어 진다고도 말씀 드렸고요.

그런것들을 연결시키면, AX를 예를 들어서, AX 는 2바이트고 이것을 반으로 쪼개

(쪼개면 1바이트씩 짤리겠지요) 서 워드단위 연산뿐이 아닌 바이트 단위의 연산도

가능해게 하여 준다는 것입니다. 다시 말해


             AX = 16bit ( 2byte )

;------------------------------------------;

; AH = 8bit ( 1byte ) ; AL = 8bit (1byte ) ;

;------------------------------------------;


그림과 같이 AX라는 것은 2바이트의 크기로 2바이트 연산도 가능 하지만,

AH, AL 등과 같이 각각 1바이트씩으로 쪼개어 1바이트 연산도 수행한다는 것입니다.

( 여기서 AH 의 H 는 'High' , AL 의 'L'은 'Low' 를 의미합니다 )

그럼 레지스터의 연산 기능을 이용해 예를 보여 드리겠습니다.

만약 04F2 + 2F4A 라는 덧셈을 수행한다고 치면, 이 두 수는 모두 2바이트의 크기로

연산이 되며 결과또한 3447 이라는 2바이트로 나옵니다. 그래서 이와 같은 것은

워드 연산이기 때문에 바로 AX를 사용해 덧셈을 하게 됩니다.

그리고 3447 를 예를 들면, 이것은 2바이트 크기 이므로 AX레지스터에 들어갈 때는

1바이트씩 짤라서 AH에는 상위 34 가 , 그리고 AL에는 하위 47이 들어가게 됩니다.


레지스터의 변화            명령어

    ;---------;

 AX ; 04 ; F2 ;            MOV    AX, 04F2

    ;---------;            ( AX 레지스터에 04F2를 넣어라 )


    ;---------;

 DX ; 2F ; 4A ;            MOV    DX, 2F4A   

    ;---------;            ( DX 레지스터에 2F4A를 넣어라 )


    ;---------;

 AX ; 34 ; 47 ;            ADD    AX, DX

    ;---------;            ( AX 와 DX 를 더하여서 그 결과를 AX에 넣어라 )


다음과 같이 레지스터가 변하게 됩니다. 물론 위에 나온 명령어 들에는 신경쓰지

마시고 그 아래 괄호로 되어있는 설명만 아시면 됩니다.

( 흐..가능하면 명령어는 사용하지 않을려고 했는데... )


그리고 만약 2C + A2 와 같은 연산이라면, 피연산자와 결과 값 모두 1바이트 크기

이므로 ( 결과 : CE ) 이것은 1바이트 연산만으로 가능 합니다.


    ;----;

 AL ; 2C ;                  MOV AL, 2C

    ;----;                  ( AL레지스터에 2C를 넣어라 )


    ;----;

 DL ; A2 ;                  MOV DL, A2

    ;----;                  ( DL 레지스터에 A2를 넣어라 )


    ;----;

 AL ; CE ;                  ADD AL, DL

    ;----;                  ( AL 과 DL 을 더하여 결과를 AL에 넣어라 )


아시겠죠?

그리고 또 한가지.

만약 9B + C3 과 같은 경우는 어떨까요? 이것은 피연산자가 1바이트인것에 대해

결과값이  15E  와 같이 2바이트의 크기가 되어 버립니다.

이런 경우는 다음에 설명 드리도록 하겠습니다.



2. SP, BP, SI, DI, 세그먼트 레지스터 ( CS, DS, SS, ES ), IP


이제 다음 나머지의 레지스터에 관하여 간략히 설명 드리겠습니다.

이것들은 모두 메모리에 관련된 레지스터들로 앞서 배운 메모리 주소에

관한 것들입니다.

AX 등의 레지스터와는 달리 이들은 모두 1워드 단위의 처리밖에 할 수 없

습니다. 그 이유는 메모리의 오프셋 및 세그먼트들의 주소는 모두 1워드를

기본으로 조작이 되어지기 때문 입니다.

그리고 CS, DS, SS, ES 들은 모두 세그먼트를 조작하는 레지스터들입니다.

여기에 한가지 IP 라는 것이 포함 되는데 이것은 CS와 항상 함께 짝을

이루는 레지스터 입니다.

( 마찬가지로 SS 와 SP또한 짝을 이룹니다 )

여기서는 일단 간단한 표만을 그리고 끝내겠습니다.

이유는 이것들을 지금 설명한다고 해 봤자, 더 복잡하고 헥갈리기만 할 뿐

이기 때문 입니다. 이것들에 대한 설명은 차차 프로그램 예를 들어가면서

설명을 해 드리겠습니다.


;-------------------------------------------------------;

; SP : Stack Pointer     : 스텍의 현재 탑(Top)의 오프셋 ;

; BP : Base Pointer      : 스텍으로의 프레임 포인터     ;

; SI : Source Index      : 스트링 명령어                ;

; DI : Destination Index : 스트링 명령어                ;

;-------------------------------------------------------;


;------------------------;

; CS : Code Segment      ;

; DS : Data Segment      ;

; SS : Stack Segment     ;

; ES : Extra Segment     ;

;------------------------;


;--------------------------;

; IP : Instruction Pointer ;

;--------------------------;



3. 플래그 레지스터

플래그 레지스터라는 것은 우리가 어떤 프로그래밍을 하고서 그 프로그램을

수행할 때 PC 내에서 발생되는 눈에 드러나지 않는 여러가지 상태들을

나타내어 주고 또 사용자가 직접 채크할 수 있도록 하여 주는 레지스터 입니다.

이것 또한 간단히 넘어가겠습니다.


Flag Register

;---------------------------;

; OF : Overflow Flag        ;

; DF : Direction Flag       ;

; IF : Interrupt Flag       ;

; TF : Trap Flag            ;

; SF : Sign Flag            ;

; ZF : Zero Flag            ;

; AF : Auxiliary Carry Flag ;

; PF : Parity Flag          ;

; CF : Carry Flag           ;

;---------------------------;


후후...이렇게 쓴 제 자신도 잘 모르겠는데 여러분은 어떻겠습니까...

그냥 부담없이 한번 읽어 보시고 넘어가세요...

차차 차근차근 설명을 할테니까요...

^_^

^

 [7] 제목 : [초급-실습] 프로그램 하나 ( 소스 & 설명 )#1
 올린이 : 까망벌레(정태식  )    94/10/26 00:41    읽음 : 610  관련자료 없음

이제 DEBUG 라는 것으로 간단한 어셈블리어 프로그래밍을 해보겠습니다.


음...DEBUG라는게 과연 어디에 쓰이는 것인지 우선 말하자면,

DEBUG 말그대로 버그를 잡은 데에 쓰이는 것입니다. 도스상에서 지원되는

이런 버그잡는거 말고라도 터보 디버그같은 여러가지 유틸리티가 있습니다.

하나하나 단계별로 실행을 시켜가며 과연 무슨 버그가 있나 하고

레지스터나 플래그 등등을 일일이 살펴보며 확인할 수 있는 유틸중의 하나죠....


헤...혹시 '버그'가 먼지 모르시는 분....을 위해 간단히...

프로그래머가 작성한 프로그램 중에 발생할 수 있는 대표적인 것 중에

ERROR와 WORNING이 있고 또 여기에 가장 골치아픈 버그라는 것이 있지요.

에러가 나면 물론 프로그램 실행이 불가능 하게 되는건 당연 하고요,

WORNNING같은 것은 실행하는데 지장은 없지만 자칫 엉뚱한 결과가 출력될수도

있는 위험성을 안고 있을때 생기는 메시지 입니다. 이 두가지(ERROR,WORNNING)

모두는 컴파일러나 링크 ( 컴파일과 링크에 대해서는 다음 기회에...) 시에

알아서 프로그래머에게 알려 줍니다. 하지만 이 놈(버그)은 위의 두가지 와는

달리 어떠한 출력 메시지도 없이 실행이 되지만 엉뚱한 결과가 나오거나 자칫

시스템이 다운 되어 버리는 경우도 있습니다.

이럴때는 귀찮지만 일일이 프로그램 소스를 검사 하던가, 그래도 안되면

이 DEBUG라는 것을 돌려가지구 버그를 잡게 됩니다.


저희 학교 교수님의 예를 들자면, 이분께서 5000라인 가까이 되는 C프로그램을

작성을 했는데 에러나 경고가 전혀 없이 결과값이 엉뚱하게 나오더랍니다.

그래서 그 5000라인 이나 되는 소스를 일일이 검사하고 검사해도 전혀 이상이

없었답니다. 그것을 1주일 동안 꼬박 밤새가며 찾다가 드디어 찾았는데,

그것이 무엇이냐...하믄


if ( i == 0 ) { 이라고 해야 하는 걸

if ( i = 0 ) { 이라고 했다는 것입니다.


그걸 알았을 때의 그 황당함....

꼬옥 이런걸 버그라고하지는 않습니다만, 이런것도 버그가 될 수 있다는 것이지요.


그럼 다시 원점으로 돌아와서...

아마 여러분들 컴퓨터에는 모두 이 DEBUG라는 것이 있을겁니다.

흐....도스 없이 컴을 사용하시는 분은 없겠지요?


그럼 이 DEBUG를 돌려 봅시다.


C:\DOS>debug

-


이와 같이 화면에는 '-' 표시가 찍힙니다. 이건 바로 디버그를 사용할

준비가 되었다는 것을 의미 합니다. 그럼 다음과 같이 입력을 해 보세요.

( 대문자는 자동으로 표시되는 것이고, 소문자는 직접 입력을 하는 것입니다. )


-a

0A48:0100 mov ax,0123

0A48:0103 add ax,0025

0A48:0106 mov bx,ax

0A48:0108 add bx,ax

0A48:010A mov cx,bx

0A48:010C sub cx,ax

0A48:010E sub ax,ax

0A48:0110                    <--- 여기서는 그냥 엔터만 칩니다.

-


그럼 간단한 예제 프로그램 입력을 마쳤습니다.

여기서 한가지, 앞의 숫자 0A48:0100 에서 ':'앞의 0A48은 시스템 마다 다를

수 있습니다.

( '다를 수 있습니다' 가 아니고 아마 10중 8,9는 다를 것입니다 )

그건 신경쓰지 마시고......

일단 이들이 맞게 입력이 되었는지 확인을 해 바야겠죠.

그럼 또 다음과 같이...




-u100

0A48:0100 B82301           MOV AX,0123

0A48:0103 052500           ADD AX,0025

0A48:0106 89C3             MOV BX,AX

0A48:0108 01C3             ADD BX,AX

0A48:010A 89D9             MOV CX,BX

0A48:010C 29C1             SUB CX,AX

0A48:010E 29C0             SUB AX,AX

........

-

( 바로 위의 '....'은 그 뒤로도 몇가지 비슷한 형태로 주루룩 나온다는 것을

말합니다. 이 부분은 시스템 마다 다른 내용이 출력 되기 때문에 별로 신경쓸

것은 없습니다. 우리가 필요한 부분은 위에 쓴 저 내용뿐입니다. )


제대로 되었는지요? 잘못되었다고요? 그럼 'Q'로 디버그에서 빠져 나온뒤 다시

시작해 보세요...( 다른 방법이 있긴 하지만 일단은 그렇게 하시기를... )

사실 위의 프로그램은 실행 시켜도 아무런 결과 없이 시스템만 다운 됩니다.

절대 실행 시키지 마시길....헤헤~~


그리고 한번 다음과 같이 입력을 해보세요..


-d100

0A48:0100  B8 23 01 05 25 00 89 C3-01 C3 89 D9 29 C1 29 C0   .#..%.......).).

0A48:0110  8B C6 F7 D0 D3 48 DA 2B-D0  .....등등 주루룩~~~


이렇게 출력이 됩니다.

내용은 다를 수 있지만 첫째줄의 모든 내용은 모두 같습니다.

( 설명은 조금 후에... )


그럼 이제 본격적으로 위의 프로그램이 과연 제대로 돌아가는지, 또 어떻게

레지스터들이 변하는지 확인을 해 봐야 겠죠?

그럼 일단 프로그램이 실행되기 전의 레지스터들의 상태를 체크해 봅시다.


-r

AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=0100   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        MOV     AX,0123

-


다음과 같이 출력이 될것입니다.

하지만 위에 DS, ES, SS, CS 값들은 모두 컴퓨터 마다 다르게 출력이 될겁니다.

하지만 위의 DS, ES, SS, CS 네개의 값이 모두 동일하다는 것은 어느 PC나

같습니다.

그럼 하나씩 단계적으로 실행을 해 보지요.

( 출력에 대한 설명은 조금 후에 하겠습니다. )

( '()' 안의 숫자는 실제로는 출력이 되지 않는 내용 입니다.

  이 숫자는 설명의 편의상 순서를 정하기 위해 사용 했습니다 )

-t

(1)

AX=0123  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=0103   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        ADD     AX,0025

-t

(2)

AX=0148  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=0106   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        MOV     BX,AX

-t

(3)

AX=0148  BX=0148  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=0108   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        ADD     BX,AX

-t

(4)

AX=0148  BX=0290  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=010A   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        MOV     CX,BX

-t

(5)

AX=0148  BX=0290  CX=0290  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=010C   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        SUB     CX,AX

-t

(6)

AX=0148  BX=0290  CX=0148  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=010E   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        SUB     AX,AX

-t

(7)

AX=0000  BX=0290  CX=0148  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000

DS=0A48  ES=0A48  SS=0A48  CS=0A48  IP=0110   NV UP EI PL NZ NA PO NC

0A48:0100 B82301        MOV     AX,SI <-- 이부분은 다를 수 있습니다.

-


흐...뭐가 먼지... 정신이 없죠?

그럼 이제 이 결과 값들을 가지고 한번 설명을 해 보겠습니다.


우선 이같이 실행도 안되는 프로그램을 뭐하러 짰는고....라고 생각하신다면,

사실 이 프로그램을 예로 든 이유는 프로그램에 따라서 변하는 레지스터의

값들을 확인해 보기 위해서 입니다.


우선 위에서 보았던 'u'에 대해서 나오는 결과들에 대해 말씀 드리지요...

'u'는 'unassemble'의 약자로 'assemble'의 반대적인 의미를 가집니다.

위에서 보았던 'd'의 결과들과 'u'의 결과들에서 발견할 수 있는 공통점이

있지요? 그건 바로 'u'의 결과중에 보이는 '0A48:0100' 과 ' MOV     AX,0123'

사이에 있는 'B82301'이라는 것이 보이죠?

그럼 바로 이 위치에 있는 문자들을 주욱 모아 보면....


B82301 052500 89C3 01C3 89D9 29C1 29C0


와 같은 내용이 됩니다.

그리고 'd'의 결과 값들의 내용을 보면...


B8 23 01 05 25 00 89 C3-01 C3 89 D9 29 C1 29 C0


와 같습니다. 어때요? 전자의 내용을 두글자씩 분리해 정리하면 후자의 내용과

꼬옥 들어맞는 것을 알 수 있습니다.


그럼 과연 위의 이상한 숫자와 문자들은 무엇을 의미 할까.....

'd'라는 것은 'Dump'의 약자로 즉, 현재 메모리의 내용을 보여주는 명령입니다.

바로 화면에 주루룩 나오는 내용들이 메모리의 현재 내용이라는 것이지요.


그럼 위의 내용을 분석해 보겠습니다.

일단 'B82301'을 시작해 보죠...

이것은 바로 기계어 코드로 'MOV AX,0123'이라는 명령어를 기계어로 바꾼

것입니다. 그럼 왜 이것을 기계어로 바꾸어야 할까요...

그것을 'MOV AX,0123'이라는 명령어는 PC에서는 전혀 알아 듣지 못하는 내용

입니다. 그래서 이것을 PC에서 알아들을 수 있는 내용으로 바꾸기 위해

위와 같은 기계어 코드를 생성하게 된 것입니다. 어셈블러는 이와 같이

사용자가 입력하여준 어셈블리어 언어를 바로 기계어 코드로 변환 하여 주는

역할을 합니다. 사실 사람이 직접 이 기계어 코드를 사용해서 프로그래밍

한다면 더없이 좋겠지요...하지만....그 기계어 코드를 일일이 다아 왜우는

사람은 아마 없을 겁니다. ( 제가 알기로는 말이죠..흐흐 )

그래서 좀 더 사용자가 편리하게 프로그래밍을 할 수 있도록 영문으로 되어진

'MOV' 와 같은 명령어를 만들어 내게 된 것입니다.

C언어나, 베이직 등등 모든 프로그래밍 언어들이 바로 프로그래머들을 위해

좀 더 편리하게 프로그래밍 할 수 있도록 만들어 졌습니다.

결과적으로 모든 언어는 최종적으로 바로 위와 같은 기계어 코드로 변환을

하게 된다는 것입니다.


다시 'B82301'이라는 것을 뚜러지게 봅시다... ( '뻥~' )

모두 3바이트로 된 내용 입니다.

'B8' <-- 요것은 과연 어떤 어셈블리어 명령어를 기계어로 변환한 것일까...

답은 간단...'MOV AX' 라는 것을 변환한 기계어 코드 입니다.

하필 'MOV' 가 아니고 'MOV AX' 를 통채로 변환을 했느냐...하시는 분들을 위해

잠시....

솔직히 'MOV' 따로 'AX'따로 각각 한 바이트의 기계어 코드로 변환을 한다고

하면 알아보기도 쉽고 좋겠죠...하지만 여기에 하나 단점이 발생합니다.

만약 'MOV' 가 기계어 코드로 'C3' 이라고 하고 'AX'는 '25' 라고 칩시다

그럼 'MOV AX'라는 것을 기계어 코드로 바꾸게 되면 결과적으로


C325 라는 결과가 되겠지요?


과연..... 'B8'이라는 코드가 'C325'라는 코드로 변환이 됨에 있어서

원래는 1바이트 만으로 되었던 것이 2바이트로 늘어나 버렸습니다.

이건 쓸데 없는 메모리 낭비만 가져올 뿐입니다.


혹시나 왜, 'MOV' 를 'C' 라는 것으로 하고 'AX'를 '2' 라고 한다면 역시

같은 1바으트로 변환이 가능하지 않냐고 하실 분이 계실 지도 모릅니다.

제 대답은 '벌러덩~' 입니다. 왜냐하면 전에도 말씀 드렸듯이 모든 PC에서

수행되는 기본 단위는 바이트 단위라고 했습니다. 그러므로 'C'라는 것은

완전한 하나의 바이트가 되질 못하기 때문에 처리를 할 수 없게 됩니다.


그럼 혹시 'MOV BX' 같은 것은 또 다른 1바이트의 기계어 코드가 있느냐..

라는 질문에 '예' 입니다. 그럼 그 많은 명령어와 레지스터들의 조합들을

이렇게 따로따로 코드를 정한다면 1바이트 가지고는 부족하게 되는건 아닌지..

하지만 걱정 마세요... 1바이트로 안된다면 2바이트를 써서 하면 되는 거니까..

메모리 낭비? 하지만 일단은 프로그램이 제대로 도라가는게 중요하자나요..

가급적이면 PC에서 메모리를 적게 먹는 방향으로 설계되었을 줄로 압니다.

됐지요?


이제 뒤의 '2301' 에 대해 말씀 드리지요.

이건 가만히 살펴보면 입력하는 데이터 값인 '0123' 과 숫자는 모두 같은데,

대신 순서가 바뀌었다는 것을 알 수 있습니다.

즉 '01' 과 '23' 각각 1바이트의 내용이 서로 자리바꿈을 했음을 알 수

있습니다. 이 이유는 메모리 구조상 원래 이렇게 저장이 되어지도록 됴습니다.

그 '이유'라는 것은 다음으로 넘기기로 하지요...히힛~~

어쨋든 '2301'은 AX레지스터에 넣을 값을 알려주는 것입니다.


휴~~~ 일단 1행은 마쳤습니다.

역시 다음 행들 또한 같은 맥락으로 이해 하시면 되고요...주루룩~~~

왜 어셈블리어 명령어에 대한 설명은 안하냐고요?

후후...이 프로그램을 해보는 목적은 명령어 익히기가 아니구여

바로 메모리의 실제 모습과 레지스터들을 이해하기 위해 해 보는 것이여요..



이제 'd'라는 명령으로 나오는 출력값들이 무엇을 의미 하는지 알겠죠?

바로 프로그램을 기계어 코드로 바꾸어서 메모리로 저장 시킨 모양을

알려주는 것입니다.

그럼 'd'로 나오는 출력들 중에 왼쪽의 숫자 '0A48:0100' 같은 것들은 무엇을

의미 할까요...

이것들은 바로 메모리의 주소값을 표시해 주는 것입니다.

왼쪽이 세그먼트가 되고 오른쪽이 오프셋이 되는 것입니다.

그리고 제일 오른쪽의 알 수 없는 문자들은 무엇인가 하믄...

변환된 기계어 코드는 어디까지나 그냥 숫자들 입니다.

이 숫자들은 전에 말씀드렸던 아스키 코드로 1바이트씩 표시해 준 것입니다.

'23' 같은 것은(16진수) 아스키 코드표를 찾아보면 '#'로 정해져 있습니다.

나머지 들도 마찬가지구요. 그리고 '.'이 상당히 많은데, 이것들은

표준 아스키 코드 이외의 확장 아스키 코드들을 그냥 모두'.' 로 표시한

것입니다.


다음에는  'r' 과 't' 로 나오는 출력들을 가지고 설명을 드리겠습니다.





 







 [8] 제목 : [초급-실습] 프로그램 하나 ( 소스 & 설명 )#2
 올린이 : 까망벌레(정태식  )    94/10/29 20:06    읽음 : 460  관련자료 없음

후.....넘 늦게 올려서 죄송 합니다.

그럼 다시 그 소스를 가지고 설명을 계속 하겠습니다.

이번에는 'r' 명령과 't'명령에 대해 설명한다고 했지요?

그럼 해보겠습니다.


우선 데이타, 스텍, 코드 세그먼트에 대해 설명을 해 드리겠습니다.

여러분도 보시다 시피 소스에서 입력하여준 명령어들 'mov, add, sub'등등

이것들도 우선은 메모리에 저장되어 지는것을 보았지요?

이와 같이 PC에 어떠한 연산 명령을 내려 주는 것들을 저장하는 메모리 영역을

코드 세그먼트라고 부릅니다.

말그대로 메인 프로그램 이라고나 할까요.

전에 말씀드렸다 시피 메모리 구조는 세그먼트와 오프셋으로 구성되어 진다고

했습니다. 그럼 과연 한 세그먼트의 크기는 얼마나 될까요?

그것은 오프셋의 크기와 관련이 됩니다.

다음과 같은 경우를 생각해 보지요.

만약 메모리에 'abcdefg'라고 차례대로 저장을 시킨다고 해 봅시다.

그럼 이것들이 과연 메모리에 어떠한 모양으로 저장이 되어지는지 아시겠지요?


;--------------------------------

; a ; b ; c ; d ; e ; f ; g ;   ;

;--------------------------------

다음과 같습니다.

여기서 만약 'a'라는 문자가 저장된 곳이 바로 0123:53F2 라고 가정을 해 봅시다

그럼 세그먼트는 '0123'이 되고 오프셋은 '53F2' 라고 할 수 있습니다.

그럼 'b'가 저장된 곳은 주소가 어떻게 될까요...

그건 '0123:53F3'으로 세그먼트가 아닌 오프셋이 하나 증가한 메모리 주소를

갖습니다. 'c'역시 차례대로 '0123:53TF4'가 되는건 예측할 수 있죠.

이와 같이 하나의 세그먼트에는 오프셋의 범위가 '0000' 부터 'FFFF' 까지 됩니다.

그러므로 바로 오프셋의 범위가 하나의 세그먼트의 크기라고 할 수 있는 것입니다.]

이걸 계산해 보면 약 64K 바이트의 메모리가 됩니다.

정의해서, 하나의 세그먼트 크기는 64K의 용량을 갖는다는 것을 알 수 있습니다.

그래서 이 세그먼트 내에 이 코드들이 들어간다는 것이지요.

그럼 1메가의 메모리에는 과연 몇개의 세그먼트가 존재 할 수 있는지 계산해 보죠.

1 메가면 1024K바이트라는 것은 알고 있습니다.

그럼 계산은 간단.


1024 / 64 = 64


라는걸 알 수 있습니다. 그러므로 '1메가의 메모리 안에는 64개의 64K바이트를

같은 세그먼트가 존재 한다.' 라고 할 수 있습니다.


그럼 이와 같이 세그먼트들을 나누는데 있어서 왜 데이타 라느니, 코드라느니

이런것들을 나누느냐, 라는 질문에

만약 프로그램이 메모리에 저장되어지는데 있어서 그 크기가 64K를 넘어간다고

생각해 보세요. 그럼 이것을 어떻게 처리해야 하느냐....

물론 두개의 세그먼트를 사용하면 되겠지요.

그럼 이 두개의 세그먼트를 어떻게 사용해야 효율적일까요?

보통 만약 두개의 세그먼트를 사용한다고 하면, 전에 예를 들었던 '3+4'에서

'3'과 '4'는 데이터 라고 했고 '+'는 연산 명령이라고 했지요?

이와 같은 덧셈을 1000개 수행해야 한다고 해 보세요.

덧셈인데, 수 많은 데이터 들을 일일이 다아 더하는 명령을 주어도 되지만

데이타 영역을 따로 만들어 주고, 여기에 이 데이터 영역에 있는 값들을

꺼내다가 계산한다면, 훨씬 효율적이 될 수 있습니다.


여기서 데이터 영역이 저장되어지는 세그먼트를 데이타 세그먼트(Data Segment)

라고 부르고 바로 연산 명령 같은 것이 저장되어지는 세그먼트를 코드 세그먼트

(Code Segment) 라고 부릅니다.

물론 이 두가지(데이타, 연산명령) 를 하나의 세그먼트 안에 넣어 주어도 됩니다.

그리고 위의 내용에선 분명 스텍 세그먼트(Stack Segment) 라는것이 있다고 했습니다.

이 스텍이라는 것은 나중에 설명을 드리겠습니다.


그럼 대충 세그먼트라는 것에 대해 이해가 가셨을 줄로 믿습니다.


그럼 이제 IP ( Instruction Pointer ) 가 무엇인지 설명을 또 드리죠..

PC 상의 레지스터 상태를 보여주는 'r'명령을 내려 보면 알 수 있듯이

모든 레지스터의 저장된 값들이 나옵니다. 거기에 보면 'IP'라는 것이 있습니다.

이것은 보통 초기에는 '100'이라고 나옵니다. 이것은 오프셋 값을 나타낸 것으로,

무엇의 오프셋이냐 하믄, 여러분이 직접 넣어준 프로그램을 'u'명령으로 보면

'MOV AX,0123'이라는 프로그램이 바로 오프셋 100에 저장이 되어 있다고 나옵니다.

그럼  'IP'와 프로그램의 가장 선두 부분의 오프셋 값이 같다는 것을 알 수 있죠.

또 'r' 명령후 나오는 값들중에 제일 마지막 부분에 ' MOV AX,0123'이라고 써 있는

부분이 있죠.. 그것은 지금 상태에서  프로그램을 실행시키면, 다음으로 실행이

되어지는 프로그램을 나타내는 것입니다.


이제 't'명령을 실행시켜 보면 ( 't' 는 'Trace'의 약자로 프로그램을 하나씩

단독적으로 실행시켜 나가면서 레지스터의 상태를 확인해 볼 수 있습니다 )

일단은 'MOV AX,0123'이라는 것을 실행 시킵니다. 그럼 다음으로 실행시키는 명령

이 'ADD AX,0025'라고 표시해 줍니다. (표시된 것은 실행시킨 프로그램이 아닌

앞으로 실행된 프로그램이라는 것을 나타냅니다 )

그럼 이때까지 't'를 한번 해 주었습니다.

소스에서 보면 (1)번에 해당하는 내용 입니다.

그럼 여기서 이제 'IP'값을 보세요.

변했죠?

증가했음을 알 수 있습니다. 그럼 얼마나 증가를 했습니까?

'MOV AX,0123'이 차지하는 메모리는 3바이트라고 했습니다

그리고 그 다음 명령행이 저장되어진 메모리 주소는 'MOV AX,0123'이 저장되어진

오프셋 값에 바로 이 크기인 3바이트 증가한 '103'이라는 것을 알 수 있습니다.

다시 말해 'ADD AX,0025'라는 명령이 저장되어진 메모리 주소가 '103'부터 시작된

다는 것을 알 수 있습니다.

그럼 'IP'레지스터의 '103'과 값이 같지요...

(2) 번 (3) 번 모두 그 'IP'레지스터의 값과 지금까지 실행하고 다음으로 실행되

어질 메모리의 오프셋 값이 동일하다는 것을 알 수 있습니다.


여기서 보듯이 'IP'라는 레지스터는 프로그램 소스를 실행 시키는데 있어서

프로세서 (CPU) 가 과연 그 많은 메모리 중에 어떤곳에 실행시켜야 될 프로그램들이

있는지 프로세서에 알려 주어야 합니다.

그것을 알아야 프로그램이 실행이 되어지지요.

그럼 과연 프로세서가 어느것을 보구서 메모리의 어떤 부분에 실행되어질 프로그램

이 있는지 알 수 있을까요?

그것은 'CS'레지스터에 저장되어진 값으로 프로그램이 들어 있는 세그먼트를 알아

내고 'IP'레지스터에 저장되어진 값으로 프로그램이 들어있는 메모리의 오프셋

값을 알아 냅니다.

그럼 프로그램을 한 라인 실행시키면 그 명령을 수행하고 다시 프로세서는

'CS'값과 'IP'값을 확인 해 보고, 그 다음 명령를 수행하게 됩니다.

예를 들어 보죠.

전의 소스를 보면서....

'IP' 가 100인 상태, 그리고 'CS'가 '0A48', 에서 세그먼트 '0A48' 오프셋

100번지에 있는 'MOV AX,0123'을 실행하게 됩니다.

그럼 다시 그 다음 명령인 'ADD AX,0025'를 실행시키기 위해선 'IP'값이 3바이트

증가된 '103'이 되어야 합니다. 그것은 사용자가 할 필요 없이 PC가 알아서

해 줍니다. 그럼 프로세서는 그 두가지의 레지스터를 확인해 보고 다음으로

실행시켜야 하는 명령을 수행하게 됩니다.

그래서 'CS'와 'IP' 가 항상 짝을 이룬다는 말을 하게 됩니다.


데이타 세그먼트 또한 마찬가지 입니다.

그 예는 다음 프로그램 소스에서 알아보도록 하겠습니다.

















그리고 프로그램 모델이라는 것이 있습니다.

'small' 'compact' 'large' 'huge' 등등의 모델들이 있습니다.



 [9] 제목 : [초급-보충] 스텍?
 올린이 : 까망벌레(정태식  )    94/11/05 06:47    읽음 : 410  관련자료 없음

그럼 스텍 (STACK)에 관해 설명을 드리겠습니다.

스텍은 말 그대로 '쌓다'라는 의미를 지니고 있습니다.

쌓아서 무얼 할까요?

아마 '하노이의 탑' 이라는 유명한 문제를 기억하고 계실 겁니다.

세개의 막대중에 한개의 막대예 크기가 다른 원반들이 여러개 역삼각형 모양으로

꽂혀 있습니다. 이 원반들을 다른 막대에 삼각형 형태( 제일 아래에 가장 큰 원반,

그리고 위로 순서대로 크기가 점점 작은 원반을 쌓는 형태)로 순서대로 나열하는

것인데, 원반은 한번에 하나씩 밖에 옮기지 못합니다.

그리고 원반이 두개 이상 쌓여 있을때는 제일 아래것을 꺼내기 위해서는 위에서

부터 차레차레 하나씩 빼낸 다음 마지막으로 제일 아래것을 꺼낼 수 있는 거죠.


여기서 보듯이 이 구조를 LIFO( LAST-IN-FIRST-OUT ) 이라는 용어를 정의 할 수

있습니다. 우리말로 후입전출 이 됩니다.

이것은 가장 나중에 드러간 것이 가장 먼저 나온다는 뜻입니다.

그러니깐 1,2,3,4,5 번으로 번호가 매겨진 접시가 있다고 생각 해 보죠.

이걸 착착 포개서 제일 아래에 5번 그 위에 4번...이렇게 제일 위에 마지막으로

1번 접시가 오게 됩니다. 그럼 이걸 하나하나 꺼내게 되면 가장 나중에 올려 놓은

1번 접시가 제일 먼저 꺼내어 지게 됩니다.

그리고 제일 처음에 포개어 놓은 5번 접시는 제일 마지막에 꺼내어 지게 되는 것

이지요.

이걸 잘 이해 하셨다면, 스텍이란 것도 별 문제 없습니다.

좀 더 스텍에 가까운 모양을 예를 들면,

딱 동전만한 지름의 원통이 있는데 그 한쪽이 막혀있고 나머지 한 쪽은 뚤려

있습니다.

여기에 동전을 넣게 되면 하나씩 차례로 들어가죠.

그리고 이것을 하나씩 차례로 꺼낼땐 들어갈                                                                                                                痼都求.


이제 이 예를 메모리로 그대로 옴겨 보죠.


;---------------;

;       ;       ; FFFF

;---------------;

;       ;       ; FFFD

;---------------;

;       ;       ; FFFB

;---------------;

;       ;       ; FFF9


메모리의 형태를 그림으로 나타낸 겁니다.

왜 여기서 주소값이 FFFF 에서 그 다음이 FFFE 가 아닌 FFFD 가 되느냐고 의아해

하신다면, 스텍의 처리는 항상 1바이트가 아닌 1워드로 처리 된다는 것입니다.

그림에서 보면 한 칸의 메모리를 1바이트씩 둘로 나눈 것이죠.

그리고 그림의 바로 윗부분이 막혀 있고 아랫 부분이 뚤려 있다고 생각합시다.


여기에 동전 대신에 어떠한 데이터들을 순서대로 넣는다고 해 보죠.

데이터 값을 1034, 325D, 35F3 이렇게 순서대로 넣게 되면,


;---------;

; 10 ; 34 ; FFFF

;---------;

; 32 ; 5D ; FFFD

;---------;

; 35 ; F3 ; FFFB

;---------;

;    ;    ; FFF9


위의 그림과 같이 들어가게 됩니다.

그럼 만약 위의 데이터 중에 1034를 꺼내기 위해서는 ...

물론 깡통이라고 생각하면, 위의 뚜껑을 따고 꺼내는 방법도 있지만...

헤헤...이건 메모리니깐...

일단은 그 아래에 있는 두 워드의 데이터 들을 꺼내고 나서야 비로소 우리가 원하

는 1034 라는 데이터 값을 꺼낼 수 있게 됩니다.


;---------;         ;---------;         ;---------;         ;---------;

; 10 ; 34 ;         ; 10 ; 34 ;         ; 10 ; 34 ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

; 32 ; 5D ;         ; 32 ; 5D ;         ;    ;    ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

; 35 ; F3 ;         ;    ;    ;         ;    ;    ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

;    ;    ;         ;    ;    ;         ;    ;    ;         ;    ;    ;


원래의 메모리       여기서 35F3         그 다음 325D        드디어 1034를

                    을 먼저 꺼낸다.     를 꺼내고,          꺼내게 된다.


잘 이해가 가셨는지요....


그럼 어셈블리 명령어를 가지고 설명을...


        PUSH    AX

        PUSH    DX

        PUSH    CX

        PUSH    BX


일단 이와 같은 명령어를 주었다면, 가장 먼저 있는 AX레지스터의 값을 스텍에

쌓아 놓게 됩니다. 그 다음은 물론 DX이고, 다음은 CX, BX 순서로 스텍에 쌓이

겠죠.


예로 각 레지스터의 값들이 위의 명령어를 하기 전에 다음과 같이 들어있다고

가정 합시다.

AX = 0001

BX = 0002

CX = 0003

DX = 0004


그림을 그려보면


;---------;         ;---------;         ;---------;         ;---------;

; 00 ; 01 ; FFFF    ; 00 ; 01 ;         ; 00 ; 01 ;         ; 00 ; 01 ;

;---------;         ;---------;         ;---------;         ;---------;

;    ;    ; FFFD    ; 00 ; 04 ;         ; 00 ; 04 ;         ; 00 ; 04 ;

;---------;         ;---------;         ;---------;         ;---------;

;    ;    ; FFFB    ;    ;    ;         ; 00 ; 03 ;         ; 00 ; 03 ;

;---------;         ;---------;         ;---------;         ;---------;

;    ;    ; FFF9    ;    ;    ;         ;    ;    ;         ; 00 ; 02 ;


 PUSH AX             PUSH DX             PUSH CX              PUSH BX

 SP = FFFD           SP = FFFB           SP = FFF9            SP = FFF7


다음과 같이 순서대로 드러 갑니다.

그럼 위의 그림에서 SP란 무언가....

SP 란 STACK POINTER의 약자로서 IP와 같은 기능을 합니다.

PUSH AX와 같은 경우 PC는 과연 이 AX의 값을 어디에 쌓아 넣으라는 것인지를

이 SP 를 보고 알게 됩니다. PUSH AX를 하기 전에는 SP 는 FFFF를 가리키게 되므로

pc는 '아, AX레지스터의 값을 FFFF번지에 있는 메모리로 넣으라는 말이구나' 라고

인식을 하게 됩니다. 그리고 저장되는 값은 항상 2바이트 크기 이므로, 다음에

저장할 곳을 지정하게 될 때는 2바이트 하위 쪽인 FFFD 를 가리키게 되는 것이지요

근데 여기서 하나 주목 할 것은 SP 는 IP와는 반대로 갈수록 POINTER 값이 감소

한다는 것입니다. 이것은 스텍의 특성으로 언제나 이와 같이 상위메모리로 부터

하위 메모리로 차근차근 저장을 하게 되도록 만들어져 있습니다.

그럼 이 값들을 꺼내는 것은....


        POP     BX

        POP     CX     

        POP     DX

        POP     AX


이와 같이 했다고 합시다.

POP명령은 스텍에 쌓여 있는 데이터들을 하나씩 순서대로 꺼내오는 명령으로

가장 먼저 POP BX 부터 시작을 하게 됩니다.

여기서 하나 또 관심있게 볼 것은,

PUSH 와는 순서가 반대가 되었다는 것입니다.

PUSH 에서는 AX,DX,CX,BX 순으로 넣었는데, POP에서는 거꾸로

BX,CX,DX,AX 순으로 꺼내 온다는 것입니다.


;---------;         ;---------;         ;---------;         ;---------;

; 00 ; 01 ; FFFF    ; 00 ; 01 ;         ; 00 ; 01 ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

; 00 ; 04 ; FFFD    ; 00 ; 04 ;         ;    ;    ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

; 00 ; 03 ; FFFB    ;    ;    ;         ;    ;    ;         ;    ;    ;

;---------;         ;---------;         ;---------;         ;---------;

;    ;    ; FFF9    ;    ;    ;         ;    ;    ;         ;    ;    ;


 POP BX              POP CX              POP DX              POP AX

 SP = FFF9           SP = FFFB           SP = FFFD           SP = FFFF


다음과 같이 원래의 레지스터 값들이 다시 각 레지스터에 들어가게 됩니다.

근데 만약 혹시, PUSH 를 위와 같이 한 후에 POP 명령을 다음과 같이 했다면?


        POP     AX

        POP     BX

        POP     CX

        POP     DX


우선 POP 명령은 무조건 스텍에 싸여 있는 데이터 들을 꺼내오게 됩니다.

그리고 그 뒤에 있는 레지스터에 그 값을 무조건 집어 넣게 됩니다.

그래서 결과적으로 각 레지스터에는 다음과 같은 값들이 들어가게 됩니다.


        AX = 0002

        BX = 0003

        CX = 0004

        DX = 0001


이런 결과가 되어 버립니다.


그럼 스텍이라는 것이 왜 필요할까요?

그것에 대한 설명은 다음에 여러개의 프로시져를 사용한 예에서 자세히 설명을

드리겠습니다.



 [10] 제목 : [초급-실습] 프로그램 두울 #1
 올린이 : 까망벌레(정태식  )    94/11/05 06:48    읽음 : 453  관련자료 없음

이번엔 좀 더 실질적인 프로그램을 하나 해 보도록 하겠습니다.

이건 화일의 Attribute(속성)을 바꾸는 것으로 화일을 읽기 전용,

또는 읽기 전용 해제 를 위한 프로그램 입니다.

우선은 소스를 써 보죠.


        .MODEL  SMALL

        .CODE

        ORG     100H


PROTECT PROC    NEAR

        MOV     DX,82H

        MOV     DI,82H

        MOV     AL, 13

        MOV     CX,12

REPNE   SCASB

        MOV     BYTE PTR [DI-1],0

        MOV     AL,1

        MOV     CX,1

        MOV     AH,43H

        INT     21H

        INT     20H

PROTECT ENDP

        END     PROTECT


위의 것은 화일을 읽기 전용으로 세팅 하여 주는 프로그램 소스 입니다.


        .MODEL  SMALL

        .CODE

        ORG     100H

       

RELEASE PROC    NEAR

        MOV     DX,82H

        MOV     DI,82H

        MOV     AL,13

        MOV     CX,12

REPNE   SCASB

        MOV     BYTE PTR [DI-1],0

        MOV     AL,1

        MOV     CX,0

        MOV     AH,43H

        INT     21H

        INT     20H

RELEASE ENDP


        END     RELEASE


그리고 위의 것은 프로텍트(읽기전용)해제 프로그램 입니다.

( 이 소스는 피터 노턴의 ADVANCED ASSEMBLY LANGUAGE 에서 발췌한 것입니다.)


위의 것을 실행시키기 위해 서는 일단은 간단한 에디터로 위와 같이 입력을 하여

줍니다. 그리고 확장자가 ASM인 화일로 만들어 줍니다.

꼬옥 ASM일 필요는 없습니다만, 보통 어셈블리 프로그램의 소스는 ASM이라는

확장자를 쓰는게 일반 입니다.

위의 것을 파일로 저장한다음,

메크로 어셈블러나 터보 어셈블러로 실행 가능한 파일로 만들어야 합니다.

여러분께서 혹시 메크로 어셈블러나 터보 어셈블러가 없다면, 별수 없죠

구해서 해 보는수 밖에....헤헤..


MASM [FILENAME]


이렇게 해 주면 무슨 메시지가 나오는데 , 그냥 엔터를 치세요.

그럼 화일이 하나 생기는데, 확장자가 OBJ인 화일이 생깁니다.

이 화일은 여러분이 짠 프로그램 소스를 기계어 코드로 바꾸어 준 화일 입니다.

이것은 그 자체로는 바로 실행이 불가능 합니다.

실행하기 위해서는 링크라는 것을 시켜 주어야 하는데, 이것은


LINK [FILENAME]


이렇게 해 주면 됩니다.

그럼 EXE화일이 생겼을 겁니다.

( 위의 예는 메크로 어셈블러를 예로 들은 것입니다. 만약 터보어셈블러로 한다면

MASM --> TASM, LINK --> LINK 로 하여 주면 됩니다. )


만약 EXE2BIN 이라는 유틸이 있다면,


EXE2BIN [FILENAME] [FILENAME]


으로 해 주면 EXE화일을 COM화일로 바꾸어 줍니다.

구지 EXE화일을 COM 화일로 바꾸는 이유는 COM화일이 EXE보다 용량도 훨씬 작고

메모리에 로드되는 속도 또한 매우 빠르기 때문 입니다.

그렇다고 해서 모든 소스들이 COM화일로 변환이 가능 한 것은 아닙니다.

그 이유는 다음으로 미루기로 하죠...


그럼 한번 실행을 해 보죠.

만약 만든 화일이름이 'PROTECT'와 'RELEASE'라고 가정하고,


PROTECT [FILENAME]


그리고 한번 그 화일을 DELETE 해 보세요.

지우기 불가능이라는 메시지가 나올 겁니다.

그리고 다시 그 화일을


RELEASE [FILENAME]


이렇게 한 후,

DELETE해 보면 이번엔 지워 지는 것을 볼 수 있습니다.


그럼 프로그램 소스를 다시 보도록 하죠.


여기서 중요한 것은 각 라인의 명령어의 해설이 아닌 어셈블리어 프로그램 작성의

틀을 알아보기 위한 것이므로, 자세한 명령어 소개는 미루도록 합니다.


보면 위에 .MODEL SMALL 이라는 부분이 있습니다.

이것은 프로그램의 모델을 결정하여 주는 것으로, 코드 또는 데이터 같은 것들의

크기에 따라 하나의 세그먼트에서 각각의 코드와 데이터, 및 스텍이 들어갈 수 있는

크기라면 이 모델을 써 주게 됩니다. 즉 코드와 데이터 각각의 크기가 메모리에 로드

됐을때 한개의 세그먼트 크기인 64Kbyte 미만일때는 small모델로 하게 됩니다.

그리고 만약 데이터 영역이 하니의 세그먼트 크기를 초과 하고, 코드의 크기는

그 미만이라고 한다면,  그 모델은 COMPACT모델이 됩니다.

이와 같은 것을 표로 나타내면,


TINY : .COM 화일 형태

SMALL : 데이터와 코드를 각각 하나의 세그먼트에 저장

MEDIUM : 데이터는 64K 이하, 코드는 64K이상 가능

COMPACT : 데이터는 64K 이상(복수 배열 가능), 코드는 64K 이하

LARGE : 데이타와 코드가 64K 이상, 단일 배열이 아닐 경우

HUGE : 데이터, 코드, 데이터 배열이 64K이상


그냥 한번 읽고 넘어 가세요.

그리고 보통 HUGE모델은 거의 쓰이지 않습니다.

그리고 TASM에서는 HUGE모델을 지원하지 않는다고 합니다.


그 다음으로 .CODE 라는 것이 있습니다.

이것은 .CODE 다음에 오는 것이 바로 코드들이라는 것을 알려 줍니다.

즉, 본 프로그램으로 실질적인 명령어들이 온다는 뜻이지요.

그렇다고 데이터 영역이 올 수 없는 것은 아닙니다.

마찬가지로 .DATA라는 것도 따로 있으니깐요...


그 다음 ORG 100H 라는 것이 있는데, 이것은 본 프로그램이 메모리에 로드되는

오프셋 번지를 16진수 100번지부터 하라는 뜻입니다.

구지 이것을 부치는 이유는, 그 소스를 링크 시켜 실행 가능한 화일로 만들때

이 화일을 COM화일로 하기 위해서는 ORG 100H를 써 주게 되고, EXE 화일을

만들때에는 그것을 써 줄 필요가 없습니다.

그 이유는 나중에 COM화일과 EXE 화일의 차이점을 설명할 때 해 드리겠습니다.

참고로 오프셋 번지 0000 부터 0100H 까지의 영역은 COM화일일 경우에 한해서

PSP ( PROGRAM SEGMENT PREFIX ) 영역이라고 해서 프로그램 로드시 필요한

여러가지 정보들을 담게 됩니다.

이 또한 자세한 세부 사항은 다음 기회로...


좀 설명이 많이 어려워 진것 같습니다.

여기서 모르시는 부분이 있다면 질문란에 질문 하여 주시기 바랍니다...

그럼 아주 자세하게 설명을 해 드리지요..


소스 설명은 다음에 계속..... 2 부를 기대해 주세용~~헤헤...









 [11] 제목 : [초급-실습] 프로그램 두울 #2
 올린이 : 까망벌레(정태식  )    94/11/08 04:05    읽음 : 377  관련자료 없음

안냐세요./..

계속 강의를 해 볼꼐요...


바로 전에 올렸던 글을 다시 읽어보니..

훗..저도 잘 모르는 내용을 마구마구 올린거 가타서..

죄송~


다시 마음을 진정하고 좀 더 자세하게 써 보죠.


그럼 그 모델이라는 것에 대해 조금 더 설명을 하겠습니다.

과일 가계를 생각해 보죠.


사과랑, 배랑, 바나나... 세가지만 파는 과일 가계가 있습니다.

근데 이 가계는 너무 영세하고 작아서, 그리 많은 과일을 팔지는

못합니다.

그래서 구지 다른 전문 판매점 처럼 사과 코너, 배 코너..등등

이렇게 코너를 나누어 팔 필요가 없지요.

그래서 그냥 바구니 하나 가따 놓고 이 바구니 않에 사과니 배니 바나나니..

등등 한꺼번에 담아놓고 판다고나 할까요.

그러니깐, 바구니는 하나만 있으면 세가지 모두 가따놓고 팔 수 있게 되는거죠.


그리고 또 조금 큰 상점을 가 봅시다.

여기에선 역시 사과, 배 , 바나나를 파는데, 그 양이 많아서 이 세가지를

한꺼번에 하나의 바구니에 도저히 담을 수 없습니다.

그래서 세개의 바구니를 가져다 놓고 하나는 사과 , 또 하나는 배...

이런 식으로 나누어서 담아놓고 팔고 있습니다.

전부 세개의 바구니를 사용해서 물건을 팔고 있군요.


그리고 마지막으로 아주 큰 상점을 가 봤습니다.

여기에선, 역시 세가지의 과일을 파는데, 사과 하나만 보더라도, 양이 엄청 많아

하나의 바구니만 가지고는 사과를 전부 담을수가 없습니다.

그래서 한 다섯개의 바구니에 사과를 넣고, 또 여섯개의 바구니에 배를 넣고..

이렇게 아주 많은 바구니가 준비되어 있습니다.


그럼 위의 예를 봐서, 각 가계마다 파는 물건의 양에 따라 바구니의 수가

각기 다릅니다. 이게 바로 모델이라는 계념 입니다.


사과는 코드를 말하고

배는 데이터

바나나는 스텍을 말한다고 하면

바구니는 바로 세그먼트라고 할 수 있습니다.

즉 영세한 가계 ( 코드, 데이터, 스텍 모두 양이 적은) 에서는

많은 바구니가 필요치 않고, 오직 하나의 바구니 ( 세그먼트 ) 만 있으면

물건을 팔 수 있습니다.


그리고 좀 더 많은 과일을 파는 가계에서는 그 종류별로 각각 하나씩의

바구니 ( 세그먼트 ) 를 준비해서 팔게 됩니다.


아주 큰 가계는 여러개의 바구니 ( 세그먼트 ) 를 필요로 하는건 당연 하지요.


만약 가계도 조그맣고, 파는 과일도 얼마 없는데 바구니를 여러개 가져다 놓고

텅텅 빈 바구니에 사과 하나 , 배 하나 .. 이런 식으로 판다면, 공간낭비만

가져올 뿐이죠...


이해가 가셨나요?

그리고 아주 영세한 가계 ( 하나의 세그먼트 안에 코드, 데이타, 스텍이

모두 들어가는 ) 가 바로 com화일로 변환이 가능 하다는 것입니다.


바구니의 갯수와 과일의 양이 잘 낭비없이 조화되게 배치하는것이 매우 중요하다는

것은 두말 할 필요도 없죠....


그럼 이제 .code 라는 것에대해 또한 보충 설명을 드리겠습니다.

이것은 프로그램의 코드가 오는 부분이라고 말씀 드렸습니다.

또한 여기에 데이타가 올 수도 있다고 했습니다.

그리고 따로 .data 라는 지시어가 있다는 말도 했구요..

그럼 과연 이 차이가 무엇인지...


'데이타가 올 수 있다' 에서 데이타라는 것은 바로 다른 프로그램에서

보는 바와 같이 '변수'를 선언하여 주는 곳이 바로 데이터 영역이 됩니다.


C 언어에서 보면


int a = 1, b = 4;

char c = 45, d = 64;


이와 간은 기능의 변수들을 선언하여 주는 것이 바로 데이터를 정의 하는 곳입니다.

물론 어셈블러에선 위와 같은 형식으로 쓰지는 않죠.

그냥 어셈블러 에서 사용하는 방법을 써 보자면,


a        dw        1

b        dw        4

c        db        45

d        db        64


이와 같이 선언을 하게 되는 것입니다.

그럼 과연 이 변수 선언을 '.code'안에서 하는 것과

'.data' 에서 하는 것은 무슨 차이가 있을까요


이것은 바로 데이터 영역을 따로 하나의 세그먼트로 분리시키느냐 마느냐

하는 차이 입니다.

'.code' 안에서 변수 선언을 하여 주면 이 변수들이 갖는 데이터는 바로

코드가 들어있는 세그먼트 안에 저장하게 됩니다.

즉 코드와 데이터가 하나의 세그먼트 안에 공존하게 된다는 것입니다.


반면 '.data' 이 안에 선언되어진 변수들은 따로 세그먼트를 하나

만들어서 그곳에 이 변수들의 데이터 값들을 저장하게 되는 것입니다.

즉, 코드가 하나의 세그먼트를 갖고, 또한 데이터 영역이 하나의 세그먼트를

갖게 되어 2개의 세그먼트를 사용하게 되는 것입니다.


스텍도 같은 형식으로 선언이 되어 집니다.


그래서 '.code'안에 모든 변수가 선언이 되면, 그 소스는 바로 하나의 세그먼트

밖에 갖지 못하는 com화일로 변환이 가능하게 되는 것입니다.


그리고 'org 100h'

이것은 com화일로 변화할 때 써 주는 것이라고 했는데요.

프로그램이 돌아가기 위해서는 프로세서에게 프로그램이 읽혀진 상태,

즉, 프로그램이 어떠한 형태로 되어 있나, 또는 그 밖에 여러가지 정보를

미리 알려 주어야 합니다. 이럴 경우 com화일일 경우에는 하나의 세그먼트 밖에

처리를 하지 못하기 때문에 그 하나의 세그먼트 안에 이러한 정보도 들어가게

되는 것이지요.

그래서 그 정보를 저장하기 위해 0000번지 부터 0100h 번지까지 이 정보를 넣

어주게 되는 것입니다.

즉, 본 프로그램은 오프셋 100h 번지부터 시작하라는 뜻이 됩니다.


반면 exe화일 같은 경우에는 여러개의 세그먼트를 사용할 수 있기 때문에,

구지 하나의 세그먼트에 이 PSP영역과 코드, 또는 데이터 영역을 함께 넣을

필요가 없게 되는 것입니다.


이제 다음 단계로 넘어가죠.

가만히 보면 프로그램 내용이 'PROTECT PROC      NEAR' 에 둘러 싸여 있는 것을

볼 수 있습니다. 이것은 C언어에서의 '{' '}' 와 같은 역할을 한다고 생각하면

됩니다. 이것을 프로시져라고 부르는데, C에서의 함수의 개념과 같은

개념이라고 생각하시면 편합니다. 마찬가지로 C에서 여러개의 함수가 사용

되는 것처럼 어셈블리어에서는 이 프로시져 라는 개념으로 여러개의 프로시져가

사용될 수 있다는 뜻입니다. 베이식에서는 서브루틴과 비슷한 개념 입니다.


C언어를 잘 모르시는 분들은 일단은 그냥

'아 어셈블리어의 프로그램 형태는 대충 이런 것이구나' 라고 넘어가세요..

나중에 여러개의 프로시져를 사용하는 예에서 확실히 설명을 드리겠습니다.

그냥 외우세요...이런걸 꼬옥 부쳐 주어야 되는거라고...헤헤~


그리고 여기서 'PROC'다음에 'NEAR'라고 쓰여 있는걸 볼 수 있습니다.

뜻대로 하자면 '가깝다' 라고 되겠지요..

그럼 과연 머가 가까운걸까요...

이건 데이터 정의 및 코드들이 모두 하나의 세그먼트 안에 있을때 씁니다.

그러니깐 세그먼트 하나를 각각 하나의 아파트 건물에 비유 하면,

하나의 세그먼트 안에서 다른 오프셋 주소로 가게 되는건 바로 하나의 건물안에서

이집에서 바로 저 집으로 간다는 것이지요.

반면 하나의 세그먼트 안에서 다른 세그먼트로 간다는건, 즉 다른 건물로 옴겨 간

다는 의미가 됩니다.

생각해 보세요. 하나의 건물안에서 와따가따 하는게 가까운지 , 아니면 다른 건물

로 와따가따 하는게 가까운지...


COM화일 같은 경우에는 모두 이 NEAR 이지만 EXE 화일일 경우에는 FAR를 사용하게

됩니다. 구지 이런걸 미리 해 주는 이유는 프로세서가 주소를 참조할 때

NEAR일 경우에는 OFFSET만 참조하면 되지만, EXE같은 경우 에서는 오프셋 이외의

SEGMENT주소값까지 참조를 해야 하기 때문에 이와 같은 것을 써 주어야만 합니다.


끝에 'PROTECT ENDP'가 있는에 이것은 프로시져가 끝났다는 걸 알려 줍니다.

'END   PROTECT'또한 프로그램이 완전히 끝났다는걸 나타내 주는 것이고요...


그럼 다음 예제 에서는 변수를 선언하여 주는 예를 보이겠습니다.

( 원래 변수라고 하는것 자체가 우습지만... )


참고로 이 예제들 안의 어셈블리 명령어는 따로 하나씩 설명을 하겠습니다.


 [12] 제목 : [초금-명령] MOV 에 대해
 올린이 : 까망벌레(정태식  )    94/11/08 04:06    읽음 : 387  관련자료 없음

MOV 명령에 관해..


이 명령어는 아주 쉽기 때문에 간략히 하겠습니다.

말 그대로 '옮기다' 라는 뜻을 갖는 명령어 입니다.

그럼 MOV 명령어의 사용 예를 주욱 나열해 보겠습니다.


         MOV       DL,41H

         MOV       AH,2

         MOV       CH,41

         MOV       DL,'B'

         MOV       AH,BL

         MOV       AL,01001011B


위의 것들은 모두 1바이트 연산문으로 되어 있습니다.


MOV      DL,41H


이것은 말 그대로 DL레지스터 ( DX 의 하위 바이트 ) 에 16진수 41을

넣으라는 명령 입니다. 그럼 DL 레지스터에는 16진수 41이 들어가게 됩니다.

뒤의 'H'는 바로 16진수 라는 것을 나타냅니다.

아무것도 안부치면 10진수가 되고, 'B'라는 것을 부치게 되면 바로 2진수라는 의미가

됩니다.


MOV      AH,2


이것은 AH 레지스터에 2를 넣으라는 명령 입니다. '2' 라는 숫자는 10진수나

16진수나 같은 값을 나타내기 때문에 구분을 하지 않아도 됩니다.


MOV      CH,41


이것은 CH레지스터에 10진수 41을 넣으라는 명령 입니다.


MOV      DL,'B'


이것은 DL 레지스터에 아스키 코드 'B'의 아스키 값을 넣어주게 됩니다.

즉 DL에 문자 'B'를 넣어주기 위해서는 꼭 'B'의 아스키 값을 찾아서 일일이

넣어줄 필요 없이 그냥 문자를 써어 넣어 주어도 된다는 것을 말합니다.


MOV      AH,BL


이것은 레지스터 AH에 레지스터 BL에 들어있는 값을 넣어 주라는 명령 입니다.

이와 같이 MOV명령은 레지스터에 숫자를 즉치대입 하는것 말고도 바로 레지스터

에 레지스터의 값을 바로 넣을수 있습니다.

( 예외로 이와 같은 것이 될 수 없는 경우도 있습니다...그것은 조금 후에.. )


MOV      AL,01001011B


이것은 AL 레지스터에 2진수 01001011이라는 값을 넣어주라는 명령 입니다.



위와 같이 MOV 명령은 좌변에 우변의 값을 넣어주게 됩니다.


다음과 같은 명령은 잘못된 것입니다.


         MOV       23H,AL

         MOV       AL,DX

         MOV       AL,F3H


위와 같은 명령은 잘몬된 것으로


MOV      23H,AL


이것은 23이라는 상수에 AL레지스터 안의 값을 넣어주라는 뜻이 되므로

틀린 것이 됩니다.


MOV      AL,DX


이것은 AL이 1바이트인데 반하여, DX는 1워드의 크기 이므로 데이터 간의 전송시

데이터 형이 다르므로 이것은 틀린 것이 됩니다.


MOV      AL,F3H


그럼 이건 왜 틀린가...

이건 AL 보다는 F3H라는 것이 잘못 되어서 틀린 것입니다.

F3이라는 16진수를 AL에 넣으라는 것인데, 여기서 5EH 나 32H 같은 것은 되는데

F3H 라는 것은 어셈블러가 이것이 과연 변수를 말하는 것인지..아님 16진수를

말하는 것인지 알 수 없게 됩니다.

그래서 16진수 중 앞에 영문자가 먼저 오는 경우는 항상 '0'을 먼저 써 주어야

됩니다.


즉, 'F3H' 이것을 '0F3H'이렇게 써 주어야만 하는 것입니다.



또 하나 레지스터간의 데이터 전송에 있어서 불가능한 것이 있습니다.


MOV      DS,CS


이와 같은 경우가 있습니다.

DS, CS 모두 세그먼트 주소를를 나타내는 레지스터 입니다.

이런것은 바로 레지스터간의 데이터 전송이 되지 않습니다.

위와 같은 결과를 원할 경우에는


MOV      AX,CS

MOV      DS,AX


위와 같이 두 줄로 늘려서 중간 매개체인 AX레지스터를 사용하여 전송을 하여

주어야만 합니다.

꼬옥 AX일 필요는 없습니다. BX,CX,DX 모두 가능 합니다.



그리고 MOV 명령중엔 주소 지정 방식이라는 것이 있는데...


MOV      AX,[0432]


이와 같은 형태가 있습니다. 이런것을 주소 지정 방식이라고 하는데,

이것에 대해서는 다음 프로그램 소스를 하나 본 후 따로 설명을 드리겠습니다.




 [15] 제목 : [초급-실습] 프로그램 세엣 #1
 올린이 : 까망벌레(정태식  )    94/11/09 05:58    읽음 : 375  관련자료 없음


이번엔 조금 긴 프로그램을 하나 해 보도록 하겠습니다.



    .model tiny

    .code

    org 100h


start:  jmp st1


info    dw  ?

yes db  'exist',13,10,'$'

no  db  'not exist',13
  ,'$'

flo_msg db  'Floppy is $'

co_msg  db  'Coprocessor is $'

mod_msg db  'Video Mode is $'

m1_msg  db  '40X25 Color Mode',13,10,'$'

m2_msg  db  '80X25 Color Mode',13,10,'$'

m3_msg  db  '80X25 Mono Mode',13,10,'$'


st1 proc    near

    int 11h

    mov info,ax

    mov ax,cs

    mov es,ax


    call    floppy

    call    copro

    call    mode

    int 20h

st1 endp



floppy  proc    near

    lea dx,flo_msg

    call    prt

    mov bx,info

    and bx,0000000000000001b

    cmp bx,00

    je  nofloppy

    lea dx,yes

    call    prt

    ret

nofloppy:      

    lea dx,no

    call    prt

    ret

floppy  endp



copro   proc    near

    mov dx,offset co_msg

    call    prt

    mov bx,info

    and bx,0000000000000010b

    cmp bx,00

    je  nocopro

    mov dx,offset yes

    call    prt

    ret

nocopro:

    mov dx,offset no

    call    prt

    ret

copro   endp



mode    proc    near

    lea dx,mod_msg

    call    prt

    mov bx,info

    and bx,0000000000110000b

    cmp bx,0000000000010000b

    je  mod1

    cmp bx,0000000000100000b

    je  mod2

    cmp bx,0000000000110000b

    je  mod3

mod1:

    lea dx,m1_msg

    call    prt

    ret

mod2:

    lea dx,m2_msg

    call    prt

    ret

mod3:

    lea dx,m3_msg

    call    prt

    ret

mode   endp



prt proc    near

    xor ax,ax

    mov ah,09h

    int 21h

    ret

prt endp

    end start




좀 길죠?

이것은 지금 PC의 상태를 표시해 주는 것으로


Floppy 의 유무

Coprocessor 의 유무

Video Mode 의 상태


이 세가지를 채크하여 상태를 나타내어 주는 프로그램 입니다.

( 후...이거 짜느라고 한참 해맸네요. )

여기서는 변수의 정의, 간접주소의 지정 방식, 여러개의 프로시져 호출,

그리고 인터럽트에 대해 배워볼까 합니다.

그리고 프로시져 호출시의 스텍 변화 같은 것도 다루고요...

그럼 이걸 에디터로 짠 후에 info1.asm 으로 저장 하세요

그리고


masm info1.asm


link info1.obj


exe2bin info1.exe info1.com


이렇게 하고서 info1.com을 실행해 보시면 세가지의 정보를 얻을 수 있습니다.


그럼 이 프로그램에 대한 설명은 다음에 하죠..


참고 : 사실 프로시져 호출 후에 push 로 레지스터를 보존해야 하는데,

       짧은 프로그램이기 때문에 그런 과정은 생략 했습니다.




 [16] 제목 : [초급-보충] 변수 선언에 대하여
 올린이 : 까망벌레(정태식  )    94/11/10 12:50    읽음 : 401  관련자료 없음


여러분의 성원(?)에 기픈 감사를 드리며...흐흐..

훌쩍훌쩍~~

감기 조심들 하세요...

( 푸르르~~~ 팽~ )


그럼 우선 변수의 정의에 대해 설명을 드리겠습니다.


변수 정의란 바로


info    dw      ?

yes     db      'exist',13,10,'$'

no      db      'not exist',13,10,'$'

flo_msg db      'Floppy is $'

co_msg  db      'Coprocessor is $'

mod_msg db      'Video Mode is $'

m1_msg  db      '40X25 Color Mode',13,10,'$'

m2_msg  db      '80X25 Color Mode',13,10,'$'

m3_msg  db      '80X25 Mono Mode',13,10,'$'


이 부분을 말하는 것으로,

일단은


yes    db    'exist',13,10,'$'


와 같은 경우 앞의 'yes'가 바로 변수의 이름을 말하는 것입니다.

즉 사람마다 이름이 있는 것과 같이 각 변수에도 각각의 이름이 있어야 하는

것이지요.

( 혹시 변수가 뭔지 모르시는 분은? 흐흐.. 각성을... )


그리고 'db'같은건 변수의 형태를 결정하여 주는 것입니다.

즉 db 란 'define byte'의 약자로 'yes'라는 변수의 형태는 바로 바이트 형이라는

것을 알려 줍니다.


그리고 마지막의 'exist' 하는 마지막 까지의 부분은 바로 변수의 내용을

설정하여 주는 것입니다.


그럼 하나씩...

우선은 변수의 형태를 결정하여 주는 것에 대해 어떻한 종류가 있는지 주욱

나열해 보도록 하지요.


DB      :      DEFINE BYTE      ( 1 BYTE )

DW      :      DEFINE WORD      ( 2BYTE = 1WORD )

DD      :      DOUBLE WORD      ( 4BYTE = 2WORD )

DF      :      DEFINE FARWORD   ( 6BYTE = 3WORD )   < 80386/ 80486 >

DQ      :      DEFINE QUADWORD  ( 8BYTE = 4WORD )

DT      :      DEFINE TENBYTES  ( 10BYTE = 5WORD )


다음과 같은 종류가 있습니다.


일단은 DB에 대한것 부터


               FLD1DB    DB    ?

               FLD2DB    DB    32

               FLD3DB    DB    20H

               FLD4DB    DB    01011001B

               FLD5DB    DB    10 DUP(0)

               FLD6DB    DB    'Personal Computer'

               FLD7DB    DB    '32654'

               FLD8DB    DB    01,'Jan',02,'Feb',03,'Mar'


( 발췌 : IBM PC ASSEMBLY LANGUAGE AND PROGRAMMING ( PETER ABEL ) )


위와 같은 여러가지의 형식으로 DB라는 형식의 변수를 정의하여 줄 수 있습니다.

그럼 첫번째 변수 'FLD1DB'에 대해서 말씀 드리자면,

뒤에 '?' 라는 것이 바로 변수의 값이라고 정의 되어 있습니다.

이것은 이 변수에 '?'가 들어가는 것이 결코 아닙니다.

이것은 '?' 라는 속 뜻 그대로 '모르겠다' 라는 의미의 '?' 가 됩니다.

즉, 'FLD1DB'라는 변수는 형태는 분명 1바이트 형인데, 그곳에다가 무얼

넣을지는 아직은 모르는 상태고, 그냥 'FLD1DB'라는 이름으로 메모리에 1바이트의

변수를 위한 공간을 잡아 달라는 얘기 입니다.

나중에 이 변수에 따로 어떻한 값이 들어갈지 지금 상태는 모르기 때문에

이와 같이 정의를 하여 주는 것입니다.


두번째 'FLD2DB'로 넘어가죠.

이것은 '32'라는 것을 변수에 넣으라는 뜻입니다.

즉, 십진수 32라는 값을 'FLD2DB' 라는 변수에 넣으라는 뜻이 됩니다.

간단하죠?


세번째 것은 'FLD3DB'인데,

이것은 'FLD2DB'와 별다른 것은 없습니다.

'20'뒤에 'H'를 붙여 줌으로서 'FLD3DB'라는 이름의 변수에 16진수 20을

넣으라는 것이 됩니다.


네번째 'FLD4DB' 도 역시 간단히

이 변수 안에 2진수 01011001 이라는 값을 넣으라는 뜻이 됩니다.


그리고 다섯번째 'FLD5DB' 이것은 좀 눈 여겨 볼 필요가 있습니다.

자세히 보면 'DUP'라는 것이 있습니다.

이것은 변수에 넣을 데이터가 아닌 변수에 어떠한 조치를 취해 주는 명령어의

일종입니다.

'10 DUP(0)' 이라고 되어 있는데요, 여기서 '10'이란것은 'FLD5DB'라는 변수의

이름으로 메모리 공간을 1바이트로 잡아 주는데 있어서 1바이트 공간을

10개 잡아 달라는 명령 입니다.

즉, 이것은 배열과 같은 기능을 하는 것입니다.

그리고 괄호 안의 '0'은 열개를 잡는데 있어서 그 10바이트의 내용을 모두

'0'으로 채워
 牝遮 ( 초기화를 하라는 ) 뜻이 됩니다.

만약 '10 DUP(43)' 이라고 한다면 10바이트의 메모리를 잡을때 10바이트 모두

각각 그 안의 내용이 '43'으로 하여 달라는 뜻이 됩니다.

이번것은 특별히 메모리 그림을 그려 드리겠습니다.


;---------------------------------------------------------------

; 00 ; 00 ; 00 ; 00 ; 00 ; 00 ; 00 ; 00 ; 00 ; 00 ;

;---------------------------------------------------------------

10 DUP (0) 의 경우


;--------------------------------------------------------------

; 43 ; 43 ; 43 ; 43 ; 43 ; 43 ; 43 ; 43 ; 43 ; 43 ;

;---------------------------------------------------------------

10 DUP (43) 의 경우


이와 같은 결과를 얻게 됩니다.


그리고 이렇게 저장 되어진 내용을 꺼내어 사용하는 법에 대해서는

메모리 주소 지정 방식을 설명할 때 자세히 설명을 드리도록 하겠습니다.



다음으로 'FLD6DB' 라는 명의 변수가 있습니다.

여기서는 변수에 대입 하여 주는 값이 상수가 아닌 문자열이 됩니다.

근데 여기서 위의 문자열을 보면 1바이트 크기를 훨씬 넘는 크기로

모두 17바이트의 크기를 갖는 문자열 입니다.

그럼 과연 하나의 바이트만 사용하도록 정의 되는 'DB'에서 이 17바이트나

되는 문자열을 집어넣으라는 말은 무슨뜻?

역시 이것은 위에서 설명을 드린 'DUP'의 개념을 생각하시면 됩니다.

즉 1 바이트씩 메모리에 저장을 하여 주는 것입니다.

모두 17바이트의 문자 이므로 메모리 상에도 마찬가지로 17바이트의 연속된

공간을 할당하고 여기에 각각 한 바이트(한 문자) 씩 넣어 주게 됩니다.

메모리 그림을 다시 그려 보죠.


;---------------------------------------------------------------------

; P ; e ; r ; s ; o ; n ; a ; l ;   ; C ; o ; m ; p ; u ; t ; e ; r ;

;----------------------------------------------------------------------


위와 같은 메모리 구조로 저장이 되어 집니다.

사실 엄격히 따지자면 메모리에 위와 같은 문자가 직접 들어가는 것은 아니구요

이 문자들을 모두 아스키 코드 값으로 변환을 하여 저장을 하게 됩니다.


;---------------------------------------------------------------------

; 50 ; 65 ; 72 ; 73 ; 6F ; 6E ; 61 ; 6C ; 20 ; 43 ; ....등등...

;---------------------------------------------------------------------

( 이 값들은 모두 16진수 입니다. )

이와 같이 메모리에 저장을 하게 됩니다.

역시 이 변수를 사용하는 법에 대해서는 메모리 주소지정 방식에 대해 설명을

드릴때 자세히 설명을 드리겠습니다.


다음으로 'FLD7DB' 가 있는데...

들어갈 데이터 값이 '32654'가 됩니다.

이것은 혹 착각을 하실 수도 있습니다만, 이 것은 32654라는 값을 나타내는

것이 아니고 문자 32654를 나타내는 것입니다.

즉 아스키 코드값이 메모리에 들어가게 되는 것이지요.

'FLD6DB'와 같이 하나씩 문자로 처리하여 메모리에 들어가게 되는 것입니다.


마지막으로 'FLD8DB'가 있습니다.

이것은 대입할 데이터가 마구마구 섞여 있습니다.

문자열과 숫자가 막 섞여 있는 예가 됩니다.

간단히 그림을 그려 보면 쉽습니다.


;---------------------------------------------------------

; 01 ; J ; a ; n ; 02 ; F ; e ; b ; 03 ; M ; a ; r ;

;---------------------------------------------------------

위와 같이 들어가는데, 이 역시 문자는 아스키 코드로 들어가게 되겠죠?


;----------------------------------------------------------------------

; 01 ; 4A ; 61 ; 6E ; 02 ; 46 ; 65 ; 62 ; 03 ; 4D ; 61 ; 72 ;

;----------------------------------------------------------------------


이와 같이 들어가게 됩니다.



이제 다음 단계로 'DW'라는 것에 대해서 알아 보겠습니다.

간단한 예를 보이죠.



          FLD1DW    DW    0FFF0H

          FLD2DW    DW    01011001B

          FLD3DW    DW    3,4,7,8,9

          FLD4DW    DW    5 DUP (0)

         

          FLD1DB    DB    '32654'

          FLD5DW    DW    FLD1DB


자...위와 같은 예를 들었습니다.

'DW'는 'DEFINE WORD' 라고 했다시피 2바이트, 즉 1워드의 메모리 공간을 할당하여

줍니다.


그럼 또 순서대로...


'FLD1DW' 이 변수는 메모리에 2바아트 크기의 메모리를 잡아주게 됩니다.

그리고 여기에 16진수 'FFF0' 이라는 2바이트 크기의 값이 들어가게 됩니다.

그림을 그려 보자면,


;------------------------

; 0F ; FF ;

;------------------------

이와 같이 들어가게 되는 것이지요.

( 여기서 또한 1바이트씩 내용이 앞뒤가 바뀌어 들어가는데 그 이유는 마지막에

  간단히 설명을 드기겠습니다. )




( 앞으로 메모리 그림을 직접 그리기 보다는 그냥 나열 식으로 표현을 하겠

  습니다.

  예를 들면 위와 같은 경우는


      FFF0


  이렇게 쓰고 'DB'에서 정의 했던 'Personal Computer' 같은 경우는 그냥


  50 65 72 73 6F 6E 61 6C 20 43 6F 6D 70 75 74 65 72


  와 같이 쓰겠습니다. 여기서는 1바이트씩 앞뒤가 바뀌는 것은 나타내지 않고

  그냥 보기 편한대로 나열하겠습니다.

  왜? ... 피곤하니깐! )


그럼 사실 이 변수를 정의할때 'FLD1DB    DB    0FFH,F0H' 와 같이 해주어도

메모리에 들어가는 형태는 똑같습니다.

하지만 여기서 중요한것 하나.

만약 위에 예에서 'FLD1DB'를 불러오게 되면은 사실 'FF'라는 1 바이트 만을

가져오게 됩니다. 즉 처리가 1바이트 크기로 된다는 것입니다.

하지만 'FLD1DW'같은 경우, 불러올때는 2바이트 크기인 'FFF0'을 한꺼번에

불러와 처리를 하게 되는 것입니다.


그럼 다음으로 'FLD2DW'를 보죠

이것또한 간단히 01011001 이라는 2진수 값을 2바이트 크기의 메모리에 넣으라는

것이 됩니다.


    0059


이와 같이 저장이 되는 것입니다.


'FLD3DW'를 보면,

각 값들을 ','(콤마) 로 구분을 하였습니다.

모두 5개의 다른 데이터 값이 주어 졌습니다.

이럴 경우에는 모두 5개의 워드를 메모리 공간으로 잡아 줍니다.

그리고 각각의 하나의 워드안에 콤마로 구분된 각각의 값들을 넣어 주게 됩니다.


    0003 0004 0007 0008 0009


위와 같은 형태로 저장이 되어 지는 것입니다.


이제 마지막으로 'FLD4DW'를 보도록 하겠습니다.

이 변수를 선언하기 전에 바로 'FLD1DB'라는 변수가 선언 되어 있고

'FLD4DW'에 넣는 변수값이 바로 'FLD1DB'라는 변수를 그대로 넣어주도록

되어 있습니다.

이런 경우는 생각과는 달리 'FLD1DB'의 내용이 들어가는 것이 아니고

바로 'FLD1DB'로 정의되었을때 잡혀지는 메모리의 주소값이 'FLD4DW'에 들어가도록

되어 있습니다.


다시 말해 만약 'FLD1DB'라고 변수를 선언 하여 주었을때, 그 할당되어진 메모리

주소가 '0103'이라고 한다면 'FLD4DW'에는 그 메모리의 주소값인

'0103'이 들어가게 된다는 뜻입니다.




다음으로 'DD', 'DF' , 'DQ', 'DT' 들이 있는데, 이것은 간단히 메모리에 들어가는

형태만을 설명드리고 필요에 따라 간단한 설명을 조금씩만 해 드리겠습니다.

방식은 모두 DB,DW 와 별 다를게 없기 때문입니다.



         FLD1DD    DD        ?

         FLD2DD    DD        32572

         FLD3DD    DD        14,49

         FLD4DD    DD        'PC'

        

         FLD1DB    DB        34

         FLD2DB    DB        65

         FLD5DD    DD        FLD2DB - FLD1DB



1. 'FLD1DD'


  00000000


2. 'FLD2DD'


  00007F3C

 

  32572의 십진수 값이 16진수로 변환되어 메모리에 들어갑니다.


3. 'FLD3DD'

 

  0000000E  00000031


  각 10진수 14 와 49 를 16진수로 변환하여 따로 더블워드에 넣게 됩니다.


4. 'FLD4DD'


  00005043

 

  아스키 문자 'PC'를 각각의 아스키 값으로 변환 하여 더블 워드 하나에 넣습니다.


  'P' = 50

  'C' = 43


5. 'FLD5DD'


  00000001


  이것은 위의 'DW'에서 비슷한 걸 본 적이 있지요?

  마찬가지로 'FLD1DB','FLD2DB'라는 두 개의 바이트형 변수가 'FLD5DD'의 선언

  전에 선언되어 져 있습니다.

  그리고 이 변수값은 1이 저장 되어 있습니다.

  왜 '1'일까요?

  변수에 넣어주는 값은 분명히 'FLD2DB - FLD1DB'라고 되어 있는데...

  65 - 34 의 값인 21이 들어가야 정상이 아닐까요?

  역시 위의 'FLD5DD'에 들어 가는 값은 메모리의 주소값이 되어 지는 것입니다.

  즉 'FLD2DB'와 'FLD1DB'라고 변수가 선언되어질 때의 메모리 주소값들의 그

  차이를 넣어주게 됩니다.

 

  위에서 보는 바와 같이 'FLD1DB'와 'FLD2DB'라는 두 개의 변수선언부는 바로

  연속적으로 되어 있습니다.

  그렇기 때문에 그 메모리 주소의 차이가 1이 되는 것입니다.

  이와 같은 경우의 두가지 예를 더 보여 드리겠습니다.


         FLD1DB    DB     32

         FLD1DD    DD     'PC'

         FLD2DB    DB     53

         FLD2DD    FLD2DB - FLD1DB


  이런 경우 과연 FLD2DD에는 무엇이 들어갈까요?

  답은 5 입니다.


  00000005


  이렇게요..

  즉 'FLD1DB' 와  'FLD2DB'사이에 더블워드형의 변수가 하나 더 선정이 되어

  있습니다. 그래서 'FLD2DB'까지의 변수 선언으로 되어지는 메모리 형태를 보면,


  20 00005043 35


  이런 형태가 됩니다.

  만약 'FLD1DB'라고 주어진 변수의 메모리 주소값이 105라고 가정한다면,

  그 다음에 선언되어진 더블워드 형식의 변수'FLD1DD'의 주소값은 106부터

  시작합니다.  그리고 그 다음에 오는 1바이트 형의 변수 'FLD2DB'가 시작하는

  변수의 주소값은 10A 가 되어집니다 ..

  그래서 결과 적으로는


  10A - 105


  가 되어 5가 들어가게 되는 것입니다.


  다른 예를 하나 더 보여드리겠습니다.


           FLD1DW    DW    'PC'

           FLD2DW    DW    45

           FLD1DD    DD    FLD2DW - FLD1DW


  그럼 위와 같은 경우는 'FLD1DD'에 어떠한 값이 들어가겠습니까?

 

  00000001 이라고요?

  '땡~~' 입니다.

  역시 위의 예에선 'FLD1DW'와 'FLD2DW'의 변수가 연속적으로 선언되어 있긴

  합니다. 하지만 아까와의 차이는 바로 바이트 형이 아닌 워드 형의 변수가

  선언 되어졌다는 것입니다.

  이걸 메모리에 들어가는 구조로 나타내 보죠


  5043 002D


  이렇게 들어 갑니다.

  이런 경우 'FLD1DW'가 잡혀있는 메모리의 주소값이 100 이라고 가정 한다면,

  'FLD2DW'가 시작하는 주소값은 102가 되어 집니다. 왜냐하면 'FLD1DW'의 크기가

  1 바이트가 아닌 2바이트의 크기를 차지하기 때문 입니다.

  결과적으로 'FLD1DD'에는


  00000002


  입니다.



 

DF 라는 형의 변수는 아마도 80386/80486 이상에서만 돌아가는 변수형인것

같습니다. 형식은 위와 같지만 아무래도 시스템의 제약이 따르기 때문에,

가능하면 사용하지 않는편이 좋을듯 싶습니다.



이제 마지막으로 DQ,DT 를 한꺼번에 몰아서 설명을...



0000000000000000        FLD1DQ    DQ    ?

474D000000000000        FLD2DQ    DQ    0474DH

3CF7000000000000        FLD3DQ    DQ    32572



00000000000000000000    FLD1DT    DT    ?

56341200000000000000    FLD2DT    DT    123456

43500000000000000000    FLD3DT    DT    'PC'



위와 같습니다..

왼쪽은 실질적으로 메모리에 들어가는 형태를 쓴 것입니다.


하지만...하나! 중요한 차이점.

이것은 바로 메모리에 들어가는 형태 입니다.


         FLD1DD    DD    'PC'

         FLD1DQ    DQ    'PC'

         FLD1DT    DT    'PC'


다음과 같이 각각 'PC'라는 것에 대해서 정의를 해 주었다고 해 봅시다.

그럼 각각 메모리의 저장 상태를 보면,


         00005043

         5043000000000000

         43500000000000000000


이와 같습니다.

다른점은?

물론 각 각의 크기가 다르다는 것은 당연한 사실이고요,

여기에 또 한가지 다른점은,

'DD'의 선언 같은경우는 형태가 '0000'다음에 '5043'이 들어갔다는 점입니다.

하지만 'DQ'는 5043 다음에 '0000' 이 왔다는 것이지요.

즉 'DD'는 메모리의 하위 바이트 부분에 실제의 값이 써 지지만, 'DQ'나 'DT'같은

것들은 메모리의 상위 바이트부터 채워 진다는 것입니다.

또한 'DQ'와 'DT'의 차이점은,

'DQ' 나 'DD'와 같은 것은 '5043'인 점에 반해

'DT'는 '4350' 과 같이 1 바이트씩 앞뒤가 서로 바뀌어 저장 된다는 점입니다.


   참고 : 전에 메모리의 저장형태에 대해 'DEBUG'를 사용할때 본 적이 있습니다.

          여기서는 가령 ' MOV CX,0123'인 경우 '2301'과 같이 들어가는 것을

          본 적이 있습니다. 이것은 메모리가 아래로 갈 수록 그 주소값이 증가

          하는 형태이기 때문에 실제 넣는 데이터의 앞 부분 '01'은 상위부분의

          메모리로, 뒷 부분'23'은 메모리의 하위부분에 들어가기 때문에 그런

          현상이 발생하게 되는 것입니다.


           ;------------------;

    FFFF   ;                  ;  상위 부분

           ;                  ;

           ;------------------;

           ;        01        ;

           ;------------------;

           ;        23        ;

           ;------------------;

           ;                  ;

    0000   ;   
             ;  하위 부분

           ;------------------;

             하나의 세그먼트


 

 다음에는 주소 지정 방식에 대해 설명을 해 보겠습니다.


                    


 [17] 제목 : [초급-보충] 주소지정방식에 대하여...
 올린이 : 까망벌레(정태식  )    94/11/19 11:07    읽음 : 358  관련자료 없음

후... 넘 늦게 올려서 죄송 합니다.

헐~

사정이 좀 있어가지구여

그럼 이번엔 계속 해서

주소 지정 방식에 대해 배워볼까 합니다.

참...

어떤분이 오타가 좀 있다구....흐흐~

사실 일일이 오타 찾아내기가....읔~~~

제가 넘 게을러서 말이죠...솔직히 그런거 찾아내기가 영 구찮네요...

흐흐...그래도 치명적인 오타는 없도록 할테니깐요..

구냥 세겨 들으세요....헤헤~~~




번지 지정 방식에는 일단은 두 가지가 있다고 봅니다.

그것은 직접 주소 지정 방식과, 간접 주소 지정 방식이라고 불리우고 있습니다.

음...하지만, 직접 주소 지정 방식은 별로 중요한 것은 없습니다.

그 이유는 별로 쓰이지도 않고 비 효율적이기 때문 이죠.

그래도 일단은 직접 주소 지정 방식에 대해 간단히 설명을 해 드리겠습니다.


그럼 과연 주소 지정 방식이란게 무엇인가?

헐~

그럼 그에 해당하는 예를 보이겠습니다.

여러분, 변수 선언을 보셨지요?

그럼 과연 그 변수 선언에서 그 변수들을 직접 가지고 와서 사용할땐

프로세서는 과연 어떻게 처리를 할까요?


         FLD1    DB    34


이와 같은 변수를 선언을 했다고 가정을 합시다.

그럼 메모리에는 이 변수를 위한 하나의 공간을 잡아 줍니다.

즉 메모리에 FLD1이라는 이름으로 1바이트 크기의 방을 잡고서, 거기에다 데이터

값인 '34'라는 값을 16진수로 변환 하여 22라는 값으로 넣게 됩니다.

그럼 이 34라는 숫자가 들어가 있는 곳도 메모리 이기 때문에 역시나 그 메모리의

고유의 주소값이 있는것은 당연 합니다.

( 모든 메모리는 각각의 고유의 주소값을 가지고 있기 때문이죠. )

그럼 그 메모리로 저장된 값이 다음과 같다고 가정을 해 봅시다.


         SEGMENT : 03A2

         OFFSET  : 0106


이와 같다고 가정을 하면,

 ( 사실 프로그램 소스가 실행 화일로 변환이 되어서 메모리로 로드될 때는 항상

   이 주소값이 변합니다. 즉, 실행할 때 마다 그 시스템의 상태에 따라 로드되는

   메모리의 값이 항상 일정하지는 않다는 것입니다. )

그림으로 나타내 보면


;---------------------------------------------------

; ?? ; ?? ; ?? ; 22 ; ?? ; ?? ; ?? ;

;---------------------------------------------------

 0103 0104 0105 0106 0107 0108 0109


여기서 '??' 란 어떠한 다른 ( 우리가 알 수 없는, 미리 들어 있던 쓰레기 값들 )

수가 들어 있다는 뜻이고, 우리가 원하는 값은 바로 106 번지의 22 입니다.


이와 같이 정의 되어진 변수를 사용하는 하나의 예를 잠깐 봅시다.


       MOV      AX, FLD1


이와 같은 명령어가 프로그램 내에 있다고 가정을 해 보면,

이 명령어가 들어있는 소스를 실행 가능한 화일로 만들고서 이를 메모리에 로드하게

되면 다음과 같이 변환이 됩니다.


       MOV      AX, [106]


위와 같이 변환이 됩니다.

즉,원래의 변수명은 사실 프로세서에게는 아무 의미도 없는 쓰레기에 불과 합니다.

전에 말씀 드렸던, 모든 명령어는 그에 해당하는 숫자로 바뀌어서 프로세서에게

인식을 시켜 준다고 말씀을 드렸습니다. 그래서 여기에서도 FLD1 이라는 영문자가

아닌 프로세서가 직접 인식을 할 수 있는 언어인 기계어 ( 숫자 ) 로 변환을 시켜

주게 됩니다.

그래서 위와 같은 경우는 바로 FLD1의 내용인 34라는 값이 메모리 주소인 106번지에

들어 있기 때문에 직접 이를 가져오도록 하여 주는 것입니다.

음...( 흐..넘 어렵당 )


'[]'로 둘러 싸여 있는 숫자인 '106'은 메모리의 주소값을 나타내는 것으로 '[]'

로 인해 프로세서는 이것이 바로 '106번지에 있는 데이터 값을 가져오라!' 라는

것으로 인식을 하게 됩니다.

즉, 프로세서는 변수명을 인식하지 못하기 때문에 어떠한 변수가 선언되어 지면,

그 변수이름으로 잡혀져 있는 메모리의 주소값을 프로세서에게 직접 알려 주어

그 메모리에 있는 데이터 내용을 인식하고, 가져오고, 여러가지 작업을 하게 되는

것입니다.


만약 프로그램 소스에서


      MOV      AX,FLD1


대신에


      MOV      AX,[106]


이라고 한다면 같은 결과가 나올꺼라고 한다면?

땡~~~

후자와 같은 경우를 보통 직접 주소 지정 방식이라고 하는데, 여기서는 위와같은

방식으로 프로그래밍을 하여 직접 실행을 하여 보면, 간혹 운 좋게 원래의 원하는

값인 34가 들어갈 수도 있지만, 대부분은 아마도 틀린 값이 들어가게 될겁니다.

이유는 간단.

앞서 말씀 드렸듯이, 프로그램이 메모리로 실행을 위해서 로드되면 그 로드되는

메모리의 위치가 항상 같지가 않다는 것입니다.

프로그램이 로드될때는 프로세서가 알아서 그 프로그램을 로드시키기에 알맞은

빈 메모리를 임의적으로 결정하기 때문에 그 위치는 수시로 변하게 됩니다.

그래서 위와 같은 표현은 결코 원하는 값을 기대하기는 어렵습니다.

그래서 이 보다는 간접주소 지정방식이 주로 사용됩니다.


하지만 직접 주소지정방식이 전혀 쓰이지 않는 것은 아닙니다.

그것은 가령 메모리의 어떻한 부분은 항상 MS-DOS를 사용하는 PC에서는 거의 같은

메모리 주소에 같은 정보가 들어가는 것도 있습니다.

하드 디스크의 크기라든가, 메모리의 크기, 날짜. 등등 이와 같은 것들은 시스템이

부팅이 될 때 메모리로 읽혀 들어가는 부분이 항상 같습니다.

그렇기 때문에 만약, 위와 같은 데이터들을 참조하기를 원할때는 직접 메모리 주소를

써서 이 들을 참조 할 필요가 있습니다.


그럼 이제 간접 주소 지정 방식에 대해 자세히 들어가 보도록 하겠습니다.


         FLD1      DB      34

         FLD2      DB      'R'

         FLD3      DB      'COMPUTER'


다음과 같이 변수를 선언 하였다고 해 봅시다.

그럼 메모리에는 다음과 같이 들어가겠죠?


      22 52 43 4F 4E 50 55 54 45 52


위와 같은 순서로 메모리에 들어가게 됩니다.

여기서 메모리의 주소값은 아직 정해지지 않은 상태 입니다.

주소들은 실행을 위해 메모리에 로드될때 정해 지게 되는 것입니다.

그럼 일단은


         MOV      AL,FLD1


과 같이 정의를 하였다고 해 봅시다.

이것은 아시다 시피 FLD1이라는 변수의 값인 34라는 값이 AL레지스터에 들어가게

됩니다.


         MOV      AL,FLD2


이 것도 마찬가지로 문자 'R'의 아스키 코드 값인 52라는 16진수가 AL 레지스터에

들어가게 됩니다.


         MOV      AL,FLD3


과연 위와 같은 것은 레지스터 AL에 어떻한 것이 들어갈까요?

답은 'C'라는 문자의 아스키 코드 값인 '43'이라는 16진수 숫자만이 들어가게 됩니다.

그 이유는 FLD3라는 변수는 1바이트 형 변수이기 때문에 'COMPUTER'이라는 8바이트의

내용을 모두 가져 오지는 못합니다.

그럼 여기서 여러분은 뭣하러 FLD3 에 'C'하나만 넣지 왜 쓸데없이 'COMPUTER'이라고

할 필요가 있느냐?

라고 질문 하신다면.

위에서 메모리에 들어가 있는 형태를 보면 알 수 있듯이 'COMPUTER'은 결코 따로

각각 한 문자 ( 1 바이트 ) 씩 떨어져 들어가는 법이 없이 모두 연속적으로 메모리에

들어가게 됩니다. 그래서 그 다음 문자인 'O'를 가져오고 싶다면 FLD3로 정의 되어진

변수의 그 다음 메모리 번지를 검색하면 'O'라는 문자를 가져올 수 있게 됩니다.


      FLD3      : 'C'

      FLD3 + 1  : 'O'

      FLD3 + 2  : 'M'

      FLD3 + 3  : 'P'

      .....


과 같이 사용한다면, 모든 문자를 참조 할 수 있겠죠.


      MOV      AL, FLD3 + 2


위와 같이 명령을 한다면, FLD3가 지정하고 있는 메모리의 데이터 내용인 'C'다음의

세번째 문자 'P'를 AL 레지스터에 넣게 되는 것입니다.

하지만 위와 같이 하면 'COMPUTER'의 전체 내용을 각각이 가져오는데 불편한 점이

있습니다.

그래서 FLD3라는 변수로 지정되어진 메모리의 주소값을 직접 가지고 와서 조작할 수

있는 기능이 있습니다.


      MOV      BX, OFFSET FLD3


라고 하면 BX레지스터에 FLD3로 정의 되어진 메모리의 주소값이 들어가게 됩니다.

그래서 이 BX레지스터의 값을 직접 변화 시켜가며 훨씬 빠르고 손쉽게 그 문자열을

조작 할 수 있게 됩니다.

 여기서 하나, BX 대신에 BL로 하면 안되는가....

안됩니다.

그 이유는 메모리의 주소값 ( 여기서는 오프셋 ) 이 1 바이트값이 아닌 항상 1워드

값을 가지는 이유 때문에 FLD3가 1바이트 형 변수이기는 하지만 그 변수의 데이터

값을 가져오는 것이 아닌 바로 그 변수의 ( 메모리의 ) OFFSET 값을 가져오기 때문에

항상 1워드 크기의 레지스터에 넣어야만 합니다.


위에서의 'OFFSET'이라는 의사 명령이 바로 위와 같은 역할을 하는 명령이 되는 것이지요.

이것이 좀 길다 하면


      LEA      BX,FLD3


라고 해도 결과는 똑같습니다.

가능하다면 'LEA'명령을 사용하는 것이 보기에 좋기 때문에 'LEA'명령을 사용하는 것이

바람직 하다고 할 수 있겠지요?


      LEA      BX,FLD3

      MOV      AL,[BX]


이와 같이 하면 우선은 BX 레지스터에는 FLD3의 주소값이 들어가게 됩니다.

그 다음 AL 레지스터에는 바로 BX레지스터가 지정하는 메모리의 주소에 들어있는

데이터 내용, 즉 'C'라는 문자가 들어가게 되는 것입니다.


      LEA      BX,FLD3

      INC      BX

      MOV      AL,[BX]


위와 같은 경우에는 역시 BX레지스터에 FLD3의 주소값이 들어가고

그 다음의 명령인 'INC'에 의해 BX레지스터의 값이 하나 증가하게 됩니다.

( INC 명령은 레지스터의 값에 1을 더하는 효과를 봅니다.

  만약 BX레지스터의 값이 0231 이라고 한다면 ' INC    BX' 에 의해 BX레지스터의

  값은 1이 증가한 0232가 되는 것입니다. )

그 다음에는 AL레지스터에 FLD3가 지정하고 있는 문자인 'C' 다음의 문자 'O'가

들어가게 됩니다.

이런 식으로 변수를 조작 하게 됩니다.


         FLD1      DB      34

         FLD2      DB      'R'

         FLD3      DB      'COMPUTER'


그럼 위와 같이 지정 되어진 프로그램에서 다음과 같은 경우를 생각해 보죠.


         LEA      BX,FLD1

         INC      BX

         MOV      AL,[BX]


위와 같이 명령을 한다면 과연 AX레지스터에는 어떻한 값이 들어갈까요.

FLD1이라는 변수는 오직 하나의 내용인 '34'라는 값을 지정하고 있습니다.

거기서 34라는 값이 저장되어진 메모리의 번지값 다음의 주소값에 있는 내용을

AL 레지스터에 넣으라고 되어 있는데요.

모순이죠?

하지만 프로그램상으로는 어떻한 에러 메시지도 출력하지 않고 잘 돌아 갑니다.

위와 같이 변수를 선언하여 주면 메모리에는 다음과 같이 들어가죠.


      22 52 43 4F 4E 50 55 54 45 52


위에서 FLD1이 저장되어진 곳이 첫번째인 '22'라는 값입니다.

( 34를 16진수로 바꾸면 22 가 되죠 )

여기서 FLD1이 가리키는 메모리의 주소값이 하나 증가하게 되면 바로 그 옆의 내용인

'52'를 가리키게 됩니다.

즉 FLD2가 지정하는 메모리의 주소값이 되어 버리게 됩니다.

그래서 위와 같은 프로그램에서는 AL 레지스터에 바로 'FLD2'의 데이터 값인

'C' 즉 '52'라는 값이 들어가게 되는 것입니다.

마찬가지로 'FLD1 + 3'을 하게 되면 '4F'를 지정하게 됩니다.

이것은 'FLD3'의 내용중의 하나인 'O'를 가리키게 됩니다.

이와 같이 되는 이유는 변수선언을 하게 되면 주로 연속되어서 여러개의 변수들이

메모리로 잡히기 때문에 위와 같이 해게 되면 그러한 값들을 얻게 되는 것입니다.

     

이제 선언되어진 변수값에 어떻한 값을 넣는 방법에 대해 설명을 하겠습니다.


      FLD1      DB      ?


위와 같이 선언되어진 변수는 메모리에 FLD1이라는 이름으로 메모리의 1바이트의

크기를 잡아주고 여기에는 어떻한 값이 들어갈지는 아직 모른다고 전에 말씀드렸습니다.

그럼 위와 같이 변수를 잡아 주는 이유는 무엇일까요?

그것은 어떻한 연산을 한 후에 그 결과 값들을 넣어주기 위한 것입니다.

물론 레지스터에 보관을 하면 됩니다만, 레지스터의 갯수가 제한 되어져 있기 때문에

저장할 데이터들이 매우 많을 경우에는 레지스터들이 감당을 할 수 없게 됩니다.

이럴때 남아도는 메모리를 이용하여 일시적으로 저장을 하여 다시 꺼내거나 조작을

하면 됩니다.

그런 의미에서 위와 같은 변수 선언을 아마 자주 사용하게 될겁니다.


그럼 이제 이와 같이 선언되어진 변수에 어떻한 값을 넣는 방법을 알아보죠.

방법은 간단 합니다.


           MOV       FLD1, AL


이와 같이 하여 주면 FLD1이라고 잡혀져 있는 메모리에 AL레지스터의 값을 넣어주게

됩니다. 다시 말해 메모리의 어떻한 부분 ( FLD1 이라고 이름지어진 ) 에 AL레지스터

값을 넣어 주게 됩니다.

그래서 다음에 FLD1을 참조하게 되면 위에서 넣어준 AL 레지스터의 값이 그대로 오게

되는 효과를 보게 됩니다.


           FLD1      DB      7 DUP (0)


위와 같이 선언되어진 변수가 있다고 칩시다.

그럼 메모리에는


  00 00 00 00 00 00 00


위와 같은 장소가 잡히게 됩니다.

이런 경우 이 변수는 배열과 같은 효과를 본다고 했습니다.

그럼 이 장소에 다음과 같은 값들을 넣는다면,


     FLD1      : 'A'

     FLD1 + 1  : 'B'

     FLD1 + 2  : 'C'

     FLD1 + 5  : 'D'


위와 같은 값들을 넣는다면 메모리의 구조는


  41 42 43 00 00 44 00


이렇게 들어 가겠죠?

이를 직접 어셈블리어 명령어로 하면


      LEA      BX,FLD1

      MOV      [BX],'A'

      MOV      [BX + 1], 'B'

      MOV      [BX + 2], 'C'

      MOV      [BX + 5], 'D'


위와 같이 하여 주면 우리가 원하는 결과를 얻을 수 있습니다.

여기서 하나 눈여겨 볼 것은

주소를 지정하는 방식에서 '[]'안에 직접적인 연산 명령을 줄 수도 있다는 점입니다

즉 '[BX + 5]' 와 같이 직접 그 안에 '+' 라는 연산 명령을 줄 수도 있다는 사실이죠.


마지막으로 다음과 같은 경우를 보죠.


      FLD1DB      DB      ?

      FLD2DB      DB      43


      MOV      FLD1DB, FLD2DB


이런 경우는 여지없이 꽝~~~ 'ERROR'메시지를 출력합니다.

그 이유는


      MOV      [105], [106]


위와 같은 명령어는 사용이 불가능 하기 때문 입니다.

이와 같은 맥락에서 그런 명령어는 사용이
   합니다.

그럼 위와 같은 결과를 얻기 위해서는 어떻게 해야 할까요?

그것은 다음과 같이 하면 됩니다.


      MOV      AL,FLD1DB

      MOV      FLD2DB,AL


위와 같이 하여 주면 원하는 결과를 얻을 수 있습니다.




에고...역시나 버벅...헐~

사실 주소지정 방식에 대해서 모두 설명 한 것은 아닙니다.

그 이유는 일단은 전에 올린 프로그램 소스를 분석하고, 이해하기 위해서 이기 때문에

분석 이외의 내용에 대해서는 나중에 따로 조금씩 추가로 설명을 해 드리겠습니다

앞으로의 설명을 모두 이런 맥락에서 해 나가겠습니다.


그럼 다음에는 여러개의 프로시져를 처리하고, 그 때의 스택의 변화등을 살펴 보겠

습니다.




 [18] 제목 : [초급-보충] 프로시져 및 스텍의 변화에 대해...
 올린이 : 까망벌레(정태식  )    94/11/24 00:09    읽음 : 326  관련자료 없음

음....이번에는 여러개의 프로시져에 대한 설명과 그에 따른 스텍의 변화 등을

설명 드리겠습니다.


일단은 프로시져에 대해 설명을 드리겠습니다.

음...프로시져란, C언어 에서의 '함수'와 같은 기능을 한다고 전에 말씀 드렸습니다.

즉, 어떠한 특수한 기능을 하는 명령어의 집합이라고 할 수 있죠.

다음 간단한 예제를 보이겠습니다.




    .MODEL SMALL

    .CODE

   

    ORG 100H


START      PROC    NEAR 

    MOV    DL,41H

    MOV    AH,02H

    INT    21H

START      ENDP

    END


위와 같은 프로그램을 하나 생각해 봅시다.

이 프로그램은 문자 'A'를 하나 출력하여 주는 프로그램 입니다.

또 다른 프로그램을 하나 보죠.



    .MODEL SMALL

    .CODE

   

    ORG 100H


START      PROC    NEAR 

    MOV    DL,41H

    CALL   PRINT_CHAR

    INT    20H

START      ENDP


PRINT_CHAR PROC    NEAR

    PUSH   AX

    MOV    AH,02H

    INT    21H

    POP    AX

    RET

PRINT_CHAR ENDP

    END


앞에것 보단 좀 길죠?

하지만 이 역시 위와 똑같은 결과, 즉 문자 'A'를 하나 출력하여 주는 프로그램 입니다.

그럼 이 두 프로그램의 차이는 무엇일까요?

위에것은 바로 프로시져를 사용하지 않고 최대한 짧고 간결하게 문자 'A'를 찍어주는

프로그램 입니다. 그리고 그 다음 후자의 것은 이와 같은 기능을 '프로시져'라는 것을

사용하여 문자를 찍어 주는 프로그램 입니다.


( 앞으로 전자의 프로그램을 1번 프로그램, 후자를 2번 프로그램 이라고 부르겠습니다.)


여기서 보듯 프로시져를 이용한 프로그래밍이 훨씬 더 길고 보기에 복잡해 보입니다.

그래서 구지 프로시져를 사용할 필요성이 있을까...라는 생각을 할 수도 있습니다.

하지만, 프로시져란 기능은 생각과는 달리 아주 막강한 기능을 발휘하여 주는

아주 편리한 것입니다.


2번 프로그램을 한번 보죠.


    MOV    DL,41


이것은 문자의 아스키 코드 값을 DL 레지스터에 넣어주는 것입니다.

그 이유는 프로세서가 DL 레지스터에 있는 아스키 코드값 ( 여기서는 16진수 41이고,

이 값을 문자로 하면 대문자 'A'가 됩니다.)을 보구서 여기에 해당하는 문자를 찍게

하기 위해 넣어주게 됩니다.


    CALL    PRINT_CHAR


이것에 의해 DL레지스터에 들어있는 값을 보고서 그 값에 해당하는 아스키 코드 문자

를 하나 찍어 주게 됩니다.


( 프로시져 : 흐~ 혹시 2번 프로그램에서 어떤게 프로시져인지 모르시는 분을 위해..

  2번 프로그램에서


      PRINT_CHAR    PROC    NEAR

      .....


      PRINT_CHAR    ENDP


  이 부분으로 싸여 있는 부분이 하나의 프로시져를 나타내는 것입니다.

  그러니깐 이 프로시져를 'PRINT_CHAR 프로시져' 라고 말 할 수 있는 것입니다.)


CALL을 사용하여 프로시져를 호출하게 되면 프로그램의 흐름은 바로 호출되어진

프로시져 내로 옮겨가게 되는 것입니다.

2번 프로그램을 한번 전체적으로 실행되어지는 순서를 보죠.


      MOV    DL,41H

     

      PUSH   AX       --;

      MOV    AL,02H     ;---> 이 부분이 바로 PRINT_CHAR이라는 이름의 프로시져

      INT    21H        ;     부분이 됩니다.

      POP    AX       --;     ( CALL PRINT_CHAR 에 의해 실행되는 부분입니다. )


      INT    20H


이를 보면 알 수 있듯이 PUSH,POP 을 제외하고는 1번 프로그램이랑 완전히 같은 순서로

실행이 되어진다는 것을 알 수 있습니다.


PRINT_CHAR 프로시져는 DL레지스터에 들어 있는 문자를 출력하여 주는 기능만을 하는

것입니다. 즉, 문자를 하나 찍는 명령어를 PRINT_CHAR이라는 이름으로 프로그래머가

직접 만들어 주는 경우와 같습니다.

MOV 라는 것이 어떠한 값을 옮겨 주는 것과 같이 PRINT_CHAR라는 명령어로 문자를

하나 출력하여 주는 명령어를 만들었다고 할 수 있습니다.

단지 이 명령어를 실행하기 위해서는 CALL이라는 명령어를 앞에 해 주어야 한다는

차이 뿐이라고 생각하시면 되겠지요.


그럼 이 같이 프로시져를 만들어서 사용하면 무슨 잇점이 있을까요?

만약 메인 프로그램 ( 2번 프로그램에서는 'START    PROC    NEAR' 부분으로 되어있는

곳이 됩니다. ) 에서 하나의 문자를 출력하는 기능을 여러군데에서 필요로 하게

된다면, 원래는 2줄의 명령문 집합을 하나의 명령으로 줄일 수 있게 됩니다.

다시 말해, 만약 프로그램에서 이와 같은 기능을 10군데에서 필요로 하게 된다면,

1번 프로그램과 같이 프로시져 없이 그냥 프로그래밍을 한다면 20라인이 더 추가되는

반면, 2번프로그램과 같이 프로시져를 사용하게 되면, 10라인으로 줄일 수 있게 됩니다.


    MOV    AH,02H

    INT    21H


이과 같이 두줄의 명령문을 필요할 때 마다 넣어주어야 하는 반면,


    CALL   PRINT_CHAR


이 같이 단 한줄의 명령문으로 똑 같은 기능을 기대할 수 있게 되는 것입니다.



이와 같이 생각한다면, 만약 어떠한 특수한 기능을 하는 명령어의 집합

 ( 예를 들어 진수를 변환하여 준다던가, 키보드로 부터 어떠한 문자를 하나 입력

   받는 기능이라든가.. ) 이 10라인 이상이 넘어가게 된다면, 그리고 이와 같은

필요로 하는 기능이 프로그램 여기저기 에서 자주 필요로 하게 된다면, 프로시져를

사용하지 않고서는 너무나 길어지고, 메모리 소비 또한 매우 많아 지게 됩니다.


위의 프로그램 예에서 'COMPUTER'라는 문자열을 출력한다고 하면,

1번 프로그램 에서는


    MOV    DL,43H

    MOV    AH,02H

    INT    21H

   

    MOV    DL,4FH

    MOV    AH,02H

    INT    21H


    MOV    DL,4EH

    MOV    AH,02H

    INT    21H

    ......등등


인 반면,

2번 프로그램 에서는


    MOV    DL,43H

    CALL   PRINT_CHAR

   

    MOV    DL,4FH

    CALL   PRINT_CHAR

  

    MOV    DL,4EH

    CALL   PRINT_CHAR

    ......등등


이 같이 꽤 간결하고, 알아 보기 쉽게 변환이 됩니다.

PRINT_CHAR 이라는 프로시져가 단순히


    MOV    AH,02H

    INT    21H

 

의 단 두줄이기 때문에 1번 프로그램과 같이 해도 해 볼만 하겠지만, 만약 50라인이

훨씬 넘어가는 프로시져라고 한다면 그 차이는 엄청나게 되는 것이지요.




그럼 이제 여기에 스텍이라는 개념을 끼워 넣어 보겠습니다.

스텍은 push, pop등에 의해 스텍에 넣거나 빼 올 수도 있습니다.

하지만 프로시져를 호출할 때나 인터럽트 ( 다음에 설명 ) 호출시에 프로세서가

스텍을 조작하게 됩니다.


이 프로그램을 실행 가능 화일로 만들어서 메모리에 읽어온 후에 이를 실행하게 되면,

우선은 IP 레지스터에 실행되어질 오프셋의 메모리 주소 값이 들어가게 됩니다.

전에도 말씀 드렸다 시피, 프로세서는 항상 IP 레지스터를 조사하여 다음에 실행되어질

프로그램의 내용이 있는 메모리의 오프셋 주소를 알아내어 찾아 가서 실행을 한다고

말씀 드렸습니다. 그럼 위의 프로그램 2번과 같은 경우 이것이 메모리로 읽어 들여

실행될 때의 메모리에 읽어들인 모양을 보겠습니다.


0A8B:0200 B241               MOV      DL,41

0A8B:0202 E80200             CALL     0207

0A8B:0205 CD20               INT      20

0A8B:0207 50                 PUSH     AX

0A8B:0208 B402               MOV      AH,02

0A8B:020A CD21               INT      21

0A8B:020C 58                 POP      AX

0A8B:020D C3                 RET


위와 같은 형태로 메모리에 로드 되어 집니다.

( 확인을 위해서는 DEBUG [FILENAME] 으로 읽어서 디버그의 프롬프트 '-'가 나왔을떠

  'U 200' 명령으로 확인을 해 볼 수 있습니다. )


우선 이것을 실행하기 전에 IP 레지스터는 200 을 가리키고 있습니다. 즉, 프로그램이

시작되어질 위치가 오프셋 200번지라는 뜻이 됩니다.

그리고 실행을 하면 우선은 첫번째 명령인 'MOV      DL,41'을 실행하게 됩니다.

이것을 실행한 후에 IP레지스터의 값은 그 다음 실행 명령이 들어 있는 메모리의

 오프셋 번지인 202 번지를 가리키게 됩니다.


그 다음 'CALL    0207' 이 것이 매우 중요한데, 이것은 바로


 'CALL      PRINT_CHAR' 부분이 컴파일 된 상태가 됩니다. 즉, 프로시져를 호출하게

되는데, 그 호출되는 프로시져의 이름 대신에 프로세서가 알 수 있는 메모리의 번지를

직접 가리키도록 변환이 되어 졌습니다.

이것을 실행하게 되면, IP레지스터는 바로 '207'번지를 가리키게 됩니다.

즉, 프로그램의 흐름을 중간에서 바꾸어 버리게 되는 것으로, 207번지로 바로 점프를

하게 되는 것입니다.


그럼 프로세서는 역시 IP레지스터를 보고서, 실행될 위치를 찾고서 207번지에 있는

'PUSH     AX'를 실행하게 됩니다. 그럼 역시 IP레지스터에는 그 다음 명령이 들어있는

'208'번지를 가리키게 됩니다.


이런 식으로 '20C'번지 까지 실행을 하면, IP레지스터는 그 다음 명령인 'RET'명령이

들어있는 '20D'를 가리키게 됩니다. 이 'RET'명령은 아까 'CALL    0207'로 프로시져를

호출하는 명령 그 다음 명령 부분으로 돌아가도록 하는 명령 입니다.

즉, 205번지인 'INT    20'으로 돌아가도록 하는 명령이 됩니다. 그럼 프로그램의

흐름은 아까 202번지 까지 실행이 되어 지다가 'CALL' 명령에 의해 207번지로 건너 뛴

다음, 'RET'명령에 의해 위치상으로 'CALL'명령 다음에 있는 명령으로 순서를 옮겨 가게

됩니다.


그럼 한번 생각을 해 봅시다.

CALL명령으로 207번지로 점프를 하게 됴습니다. 그 상태에서 'RET'명령을 만나게 되면,

'CALL'다음에 있는 'INT    20'이 있는 곳으로 IP레지스터를 바꾸어 주어야 프로그램의

흐름이 원래 상태로 돌아오게 됩니다. 즉,'RET'명령을 만나게 되면 IP레지스터에 205를

다시 넣어 주어야만 합니다.

다시 좀 더 자세히...

아래에 프로그램이 실행되어 짐에 따라 IP레지스터의 변화를 보여 보겠습니다.



         
                                             [200]   

0A8B:0200 B241               MOV      DL,41             [202]    (1)

0A8B:0202 E80200             CALL     0207              [207]    (2)

0A8B:0205 CD20               INT      20                         (8) 

0A8B:0207 50                 PUSH     AX                [208]    (3)

0A8B:0208 B402               MOV      AH,02             [20A]    (4)

0A8B:020A CD21               INT      21                [20C]    (5)

0A8B:020C 58                 POP      AX                [20D]    (6)

0A8B:020D C3                 RET                        [205]    (7)


'[]'안의 숫자가 바로 IP레지스터의 값을 나타냅니다.

여기서는  'MOV    DL,41'을 실행하면 실행한 다음 IP레지스터에 '202'가 들어가게

된다는 뜻입니다.

그리고 제일 오른쪽'()'안의 숫자는 순서대로 실행이 되어 지는 순서를 나타냅니다.


위에서 보면 'CALL' 명령을 실행 하면 IP 값이 207으로 변환이 됩니다. 만약 'CALL'

명령이 아니고 다른명령 ( 프로그램 실행 순서와 관계없는 명령들 )이 있었다고 한다면,

IP레지스터에는 205가 들어가는게 정상이지만, 여기서는 프로그램의 실행 순서를 바꾸게

되는 명령인 'CALL'이 있기 때문에 IP레지스터에는 '207'이 들어가게 됩니다.

그럼 프로세서는 'CALL'명령의 다음 실행 시킬 명령으로 'INT   20' 이 아닌

바로 207 번지에 있는 'PUSH    AX'를 실행하게 됩니다.

그 다음은 물론 ' MOV    AH,02'가 되겠죠.

그러다가 바로 'RET'명령을 만나면 다시 원래의 프로그램 실행 순서를 찾아게게 됩니다.

즉 순서가 뒤게뀌게 된 곳인 'CALL'이 있는곳, 그 다음의 위치인 205번지 'INT    20'을

실행하도록 하기 위해 IP레지스터에 205라는 값을 넣게 됩니다.


그럼 여기서 프로세서는 'RET'명령을 만난 후 돌아갈 위치를 어떻게 올바로 찾을 수

있는지...프로그램 상으로는 어떻한 부분에서도 직접 그 IP레지스터를 어느 다른 곳에

보관을 시키는 명령은 없습니다.

하지만 'RET'명령을 만나면 프로세서는 정확히 원래의 위치로 찾아가게 됩니다.

즉, 'CALL'명령을 만날 때의 그 위치( IP 레지스터의 값 )를 어디 다른 안전한

곳에 보관을 하였다가 'RET'명령을 만나게 되면, 다시 꺼내어 와서 IP레지스터에 다시

넣어주게 되는 것입니다.

그럼 역시 일시적으로 IP레지스터의 값을 보관하는 곳은 메모리가 되겠죠..

그 메모리 영역을 바로 스텍이라고 부릅니다.

스텍의 구조는 전에 설명을 드렸었죠.


CALL    207 을 실행하게 되면 프로세서는 스스로 스텍에 원래의 그 다음 오프셋

값인 205를 보관하게 됩니다. 즉


    PUSH      IP


를 스스로 하게 되는 것입니다.

그리고 다시 RET명령을 만나게 되면 스텍에 프쉬하였던 IP레지스터의 값을 다시 팝을

하여서 IP레지스터에 넣어주게 됩니다.


    POP       IP


와 같은 기능을 스스로 하게 됩니다.



그럼 프로그램 소스와 스텍 세그먼트의 변화등을 직접 살펴 보겠습니다.

보통 프로그램을 실행하게 되면 최초의 스텍 포인터 값은 'FFFF'번지를 가리키게 됩니다.

즉 COM화일인 경우 하나의 세그먼트만을 사용하게 되는데 그 세그먼트 내에서 가장

최 상위 번지인 'FFFF'번지를 스텍 포인터가 가리키게 됩니다.

그래서 제일 처음 스텍에 푸쉬하는 값은 메모리 번지 'FFFF'에 저장이 되어 집니다.

그 다음은 물론 스텍 포인터가 'FFFD'를 가리키는건 아시는 것이고요...


프로그램을 실행하게 되면, 프로세서는 일단 스텍에 '0000'이라는 값을 푸쉬하게

됩니다. 이것은 프로그래머가 프로그래밍을 하지 않아도 프로세서가 스스로 하는 작업

입니다. 이 이유는 인터럽트를 설명드릴때 간단히 설명을 드리겠습니다.

그래서 그럼 '0000'이라는 값이 푸쉬된 상태에서 프로그래머가 프로그래밍한 프로그램을

실행하게 됩니다. 하나의 워드값이 푸쉬 되었기 때문에 SP( 스텍 포인터 )의 값은

'FFFD' 를 가지게 됩니다.

그럼 프로그램 소스를 보면서...


                                                        [FFFD]   

0A8B:0200 B241               MOV      DL,41             [FFFD]    (1)

0A8B:0202 E80200             CALL     0207              [FFFB]    (2)

0A8B:0205 CD20               INT      20                [FFFD]    (8) 

0A8B:0207 50                 PUSH     AX                [FFF9]    (3)

0A8B:0208 B402               MOV      AH,02             [FFF9]    (4)

0A8B:020A CD21               INT      21                [FFF9]    (5)

0A8B:020C 58                 POP      AX                [FFFB]    (6)

0A8B:020D C3                 RET                        [FFFD]    (7)



그림으로 마지막 정리를 해 보겠습니다.

 


        (1)         (2)         (3)         (4)         (5)


  SP =  FFFD        FFFB        FFF9        FFFB        FFFD

      

      ;------;    ;------;    ;------;    ;------;    ;------;

 FFFF ; 0000 ;    ; 0000 ;    ; 0000 ;    ; 0000 ;    ; 0000 ;

      ;------;    ;------;    ;------;    ;------;    ;------;

 FFFD ;      ;    ; 0205 ;    ; 0205 ;    ; 0205 ;    ;      ;

      ;------;    ;------;    ;------;    ;------;    ;------;

 FFFB ;      ;    ;      ;    ;  AX  ;    ;      ;    ;      ;

      ;------;    ;------;    ;------;    ;------;    ;------;

 FFF9 ;      ;    ;      ;    ;      ;    ;      ;    ;      ;

      ;------;    ;------;    ;------;    ;------;    ;------;

 FFF7 ;      ;    ;      ;    ;      ;    ;      ;    ;      ;


     

(1)

   이것은 프로그램이 시작 되기 전에 스텍에 0000을 푸쉬한 상태를 나타냅니다.


(2)

   이것은 CALL명령을 한 후에 그 다음 메모리의 오프셋 값인 205 가 스텍에

   푸쉬된 상태를 나타냅니다.


(3) 

   이것은 'PUSH    AX'라는 명령에 의해 AX레지스터의 값이 스텍에 푸쉬되어진

   상태를 나타내고 있습니다.


(4)

   이것은 'POP    AX'에 의해 스텍에 들어있던 AX레지스터의 값을 다시 AX레지스터에

   넣어 주는 명령을 실행 한 후를 나타냅니다.


(5)

   이것은 'RET'명령에 의해 IP레지스터에 'CALL'명령 다음으로 실행되어질 프로그램의

   메모리 주소값을 다시 IP레지스터에 넣어준 후 그 상태가 됩니다.







참고 : 'PUSH    AX',  'POP     AX' 라는 부분에 대해서...


    이것은 프로그램을 실행하기 전에 미리 AX레지스터의 값을 보관하기 위해

    하여 주는 것입니다. 즉 AX레지스터의 값이 변하는 것을 방지하기 위해서지요.

    다음과 같은 경우를 보죠.




    MOV      AX,0123

    CALL     EXT1

    MOV      CX,AX

    ......


EXT1    PROC    NEAR

    MOV      AX,0206

    INT      21H

    RET

EXT1    ENDP

    ....


위와 같은 프로그램이 있다고 한다면, CALL명령에 의해 EXT1이라는 프로시져가 호출 되었

습니다. 여기서 하나, CALL명령이 실행되기 전의 AX레지스터의 값은 0123으로 되어

있음을 알 수 있습니다. ( MOV      AX,0123 에 의해서.. )

그럼 프로시저를 호출하게 되면 바로 'MOV      AX,0206'이라는 명령어를 실행하게

됩니다. 그럼 원래 AX레지스터에 있던 값인 '0123'이 날아가 버리고 다시 새로운

값인 '0206'이라는 값이 들어가게 됩니다.

그 후 프로시져를 끈낸 후 다시 본 프로그램으로 돌아와서 다음 명령을 실행하게

되는데, 여기서는 'MOV      CX,AX'라는 것이 됩니다. 그럼 과연 CX레지스터에는

어떻한 값이 들어가겠습니까? 원래 우리가 원했던 값은 '0123'이었는데 엉뚱한 값인

'0206'이 들어가 버리게 됩니다.

즉 프로시져 호출을 해서 그 안에서 AX레지스터를 사용하였기 때문에 원래의 AX레지스터

안에 가지고 있던 값을 잃어 버리게 되는 것입니다. 이것을 방지 하기 위해서

PUSH, POP 으로 레지스터의 값을 잃어 버리는 일 없이 보관을 할 수 있습니다.

수정된 프로그램을 보죠.



    MOV      AX,0123

    CALL     EXT1

    MOV      CX,AX

    ......


EXT1    PROC    NEAR

    PUSH     AX                  <--- AX 레지스터의 값을 스텍에 저장

    MOV      AX,0206

    INT      21H

    POP      AX                  <--- 스텍에 저장된 원래의 AX레지스터의 값을

    RET                               다시 AX레지스터로 복원

EXT1    ENDP

    ....


이와 같이 하면 CX레지스터에는 우리가 원했던 '0123'이라는 값이 들어갈 수 있게

됩니다.




음....솔직히 제가 다시 읽어 보아도 되게 답답 하네요...

좀 더 명쾌하게 설명을 하고 싶었는데...헐헐~

어셈을 배우시고자 하는 분들에게 이 부분은 좀 상당부분 이해하기 힘들거 같아

보입니다. 근데 솔직히 이거 읽으시는 분들이 과연 어떤 부분이 이해가 잘 안가시는지

제가 모르기 때문에, 정말 이해가 한 군데라도 안가는 부분이 있다면 질문 하세요...

자세히 다시 설명을 드리겠습니다.



그럼 다음에는 '프로그램 세엣'의 분석을 하기 위해 필요한 명령어 설명을 간단히

드린 후 세번째 프로그램 소스를 자세히 분석해 보도록 하겠습니다.

'C/C++언어' 카테고리의 다른 글

함수포인터란 ?  (0) 2007.09.06
[본문스크랩] 파일 입.출력  (0) 2007.09.06
[본문스크랩] VC++ Article  (0) 2007.09.06
[강좌] MASM 6.0 사용법  (0) 2007.09.06
어셈블리 초급 기초 #2(정태식님)  (0) 2007.09.06

+ Recent posts