앞에서 만든 부트 로더는 QEMU 기본 출력만 나왔었습니다.
FS64 OS(앞으로 만들 저의 OS 이름)를 부팅할 때 환영 메시지와 함께 부팅 과정을 화면에 표시하도록 하겠습니다.
문자를 화면에 출력하려면 현재 동작 중인 화면 모드와 관련된 비디오 메모리의 주소를 알아야 합니다.
비디오 메모리는 화면 출력과 관련된 메모리로 모드별로 정해진 형식에 따라 데이터를 채우면 화면에 원하는 문자나 그림을 출력하는 구조로 되어 있습니다.
PC 부팅 후 기본적으로 설정되어 있는 화면 모드는 텍스트 모드로 화면 크기가 가로 80, 세로 25 문자이며 비디오 주소는 0xB8000에서 시작합니다.
화면에 표시되는 한 개의 문자는 총 2Byte로 문자값인 1Byte, 속성값인 1Byte로 구성되며 총 메모리 크기는 80 x 25 x 2 = 4000Byte입니다.
문자값과 속성값 둘 다 1Byte이며, 속성값은 하위 4Bit의 전경색과 상위 4Bit의 배경색으로 구분됩니다.
전경색(4bit)과 배경색(4bit)은 다시 최상위의 특수 기능 1Bit와 하위 3bit의 색상으로 구분됩니다.
전경색의 최상위 특수 기능 비트는 강조 효과만 지원하지만, 배경색의 최상위 특수 기능 비트는 강조 효과와 깜빡임 두 기능을 제공합니다.
전경색과 배경색의 최상위 비트를 모두 강조 기능으로 사용하면 16가지 색상을 표현할 수 있기 때문에 이를 이용하여 그럴싸한 텍스트 화면을 표시할 수 있습니다.
참고)
속성값인 1Byte에서 최상위 비트(배경색의 최상위 비트)는 배경색 강조 효과 또는 깜빡임 효과를 설정하는 역할이지만, 실제 역할은 비디오 컨트롤러의 속성 모드 제어 레지스터(Attribute Mode Control Register)에 따라 결정됩니다.
속성 모드 제어 레지스터의 Blink 비트를 1로 설정하면 깜빡임 효과가 되고, 0으로 설정하면 배경색 강조 효과가 됩니다.
QEMU에서는 기본으로 배경색 강조 효과로 설정되어 있기 때문에 깜빡임 효과를 쓸 수 없습니다.
→ 화면 맨 위에 M을 빨간색 배경에 밝은 녹색으로 출력
화면에 문자를 표시하려면 0xB8000 주소에 문자와 속성을 순서대로 지정하면 됩니다.
0xB8000 주소에 접근하려면 세그먼트 레지스터에 세그먼트의 기준 주소부터 설정해야 합니다.
리얼 모드의 주소 계산 방식에 따라 세그먼트 레지스터와 범용 레지스터를 조합하여 어떤 값을 설정하더라도 0xB8000에만 접근할 수 있다면 문제가 없지만
세그먼트 레지스터의 값을 0xB800으로 설정한다면, 세그먼트 레지스터:오프셋이 0xB800:0x0000이 되어 범용 레지스터의 0을 비디오 메모리의 첫 번째 주소로 지정할 수 있기 때문에 편리합니다.
아래의 코드는 세그먼트 레지스터에 0xB800을 설정하는 어셈블리어 코드입니다.
DS 세그먼트 레지스터에 0xB800의 값을 설정했으니, 이후 데이터에 접근하는 명령어는 물리 주소 0xB8000이 기준 주소로 사용됩니다.
화면 맨 위의 주소는 0xB8000과 같기 때문에 0xB8000과 0xB8001에 각각 'M'과 0x4A를 쓰면 빨간색 배경에 밝은 녹색으로 'M'을 출력할 수 있습니다.
참고)
어셈블리어에서 주소에 해당하는 메모리 값을 참조할 때 [ ] 기호를 사용하며, 기호 앞에 byte, word(2byte), dword(4byte), qword(8byte)를 사용하여 메모리 크기를 지정합니다.
아래는 [ ] 기호와 mov 명령을 이용하여 0xB8000와 0xB8001 주소에 값을 설정하는 코드입니다.
DS 세그먼트의 값이 0xB800이므로 0x00, 0x01을 지정하면 세그먼트:오프셋이 0xB800:0x0000과 0xB800:0x0001이 되고, 이를 물리 주소로 변환하면 0xB8000, 0xB8001이 됩니다.
위의 코드를 실제 부트 로더에 적용하면 아래와 같습니다.
빌드 후 QEMU를 실행하면 화면의 맨 처음 부분에 빨간색 배경에 밝은 녹색으로 출력된 M을 확인할 수 있습니다.
→ 세그먼트 레지스터 초기화와 Hello, World
앞에서는 부트 로더가 잘 동작하는지 눈으로 확인할 수 있게 문자를 출력하는 데 초점을 맞추었습니다.
앞으로의 작업을 위해서 문자를 출력하는 코드 이전에 세그먼트 레지스터를 초기화하는 코드가 필요한데
BIOS가 부트 로더를 실행했을 때 세그먼트 레지스터에는 BIOS가 사용하던 값이 들어 있으니, 엉뚱한 주소에 접근할 수 있으므로 초기화하고 사용하면 좋습니다.
FS64 OS(앞으로 만들 저의 OS의 이름)에서는 BIOS가 부트 로더를 디스크에서 읽어 메모리에 복사하는 위치가 0x07C0이기 때문에 0x07C0으로 초기화 합니다.
또한 부트 로더의 코드(Code segment)와 데이터(Data segment)는 0x7C00부터 512Byte 범위에 존재하기 때문에 CS와 DS 세그먼트 레지스터들 모두 0x07C0으로 설정합니다.
CS 세그먼트 레지스터를 제외한 세그먼트 레지스터는 mov 명령으로 처리할 수 있지만, CS 세그먼트 레지스터는 mov 명령으로 처리할 수 없으며, 수정하려면 jmp 명령과 세그먼트 레지스터 접두사를 이용합니다.
아래의 코드는 jmp 명령과 mov 명령을 이용하여 세그먼트를 초기화하는 코드입니다.
CS와 DS 세그먼트 레지스터는 0x07C0을 설정하여 부트 로더의 시작을 기준으로 하도록 했으며, ES 세그먼트 레지스터는 화면 출력에 관련된 세그먼트로 사용하려고 0xB800을 설정합니다.
그리고 위의 코드 중 ; code 부분은 다른 코드들이 들어간다라는 의미의 주석입니다.
비디오 모드에 관련된 세그먼트 레지스터가 DS에서 ES 세그먼트 레지스터로 변경되었기 때문에 이후 출력에 관련된 코드는 모두 ES 세그먼트 레지스터를 기준으로 하게 수정해야 합니다.
만약 별다른 처리 없이 메모리에 접근한다면 암시적으로 DS 세그먼트 레지스터가 사용됩니다.
비디오 메모리에 접근하려면 ES 세그먼트 레지스터를 사용해야 하는데 이때 해당 명령을 수행하는 동안 일시적으로 세그먼트를 교체하는 세그먼트 레지스터 접두사를 사용하면 됩니다.
세그먼트 레지스터 접두사 사용법은 주소를 지정하는 오퍼랜드에 'ES:0x01'과 같이 [세그먼트 레지스터: 오프셋] 형식으로 사용합니다.
아래의 코드는 앞에서 만든 M을 출력하는 코드를 세그먼트 레지스터 접두사를 사용하게 수정한 것입니다.
→ 화면 정리
QEMU를 실행했을 때 기본으로 출력되는 문자들을 깨끗이 지우는 방법 중에 가장 간단한 방법은 0xB8000 주소부터 80 x 25 x 2byte를 모두 0으로 채우는 것이지만
속성까지 모두 0으로 채우게 되면 후에 화면에 출력할 문자는 속성값을 같이 지정해야 하는 불편함이 있기 때문에 문자 부분만 0으로 채우고 속성값은 0이 아닌 다른 값으로 채웁니다.
참고) 저는 문자가 검은색 바탕에 밝은 녹색으로 표시하도록 속성값을 0x0A로 합니다.
아래의 두 사진은 0xB8000 주소부터 80 x 25 x 2byte를 0과 0x0A로 채우는 코드를 C언어와 어셈블리어로 작성한 것입니다.
참고) C언어로 작성한 코드를 이용하여 어셈블리어 코드를 얻으려면 objdump 프로그램을 이용하면 됩니다.
objdump를 이용하여 어셈블리어 코드를 추출하려면 오브젝트 파일이나 실행 파일이 있어야 하기에
GCC를 이용할 때 -c 옵션을 이용하면 됩니다.
ex) gcc -c a.c -o a.o -02
-c : 컴파일만 수행하여 오브젝트 파일을 생성
-o : 실행한 결과 생성할 파일 이름
-02 : 최적화 옵션 2단계로 설정하여 필요 없는 코드를 생성하지 않도록 합니다.
ex) objdump.exe -d a.o
-d : 역어셈블하는 옵션, 바이너리 코드를 어셈블리어 코드로 복원
→ 메시지 출력
이제 화면을 깨끗하게 정리했으니 메시지를 출력합니다.
루프와 메시지 문자열을 사용하여 메시지를 한 문자씩 출력하여 화면에 표시하는 것보다 코드가 효율적이도록 합니다.
아래의 코드는 루프와 문자열을 사용하여 메시지를 출력하는 C언어와 어셈블리어 코드입니다.
→ 최종 코드와 테스트
위의 세그먼트 레지스터 초기화와 화면 정리 그리고 메시지 출력까지의 모든 코드를 합하면 아래와 같습니다.
Make 또는 elipse에서 빌드 후 가상머신 QEMU에서 부트 로더를 실행하면 됩니다.
'시작하지 말았어야 했던 것 > 64비트 멀티코어 OS' 카테고리의 다른 글
64비트 멀티코어 OS[4] - 2. 보호 모드에서 사용되는 세 가지 함수 호출 규약과 최종 부트 로더 코드 (0) | 2021.03.03 |
---|---|
64비트 멀티코어 OS[4] - 1. 플로피 디스크에서 OS 이미지를 로딩 (0) | 2021.03.03 |
64비트 멀티코어 OS[3] - 3. 간단한 부트 로더 만들어보기 (0) | 2021.02.13 |
64비트 멀티코어 OS[3] - 2. Eclipse 프로젝트 생성과 Makefile 생성 (0) | 2021.02.11 |
64비트 멀티코어 OS[3] - 1. 부팅과 부트로더란 (0) | 2021.02.10 |