728x90

 

 

컴퓨터 개론 introduction to computer science

- 컴퓨터 과학 전반에 대해 소개

-> 자료 구조, 알고리즘, 컴퓨터구조, 운영체제, 네트워크, 프로그래밍 언어 등

 

 

자료 처리기data processor로서의 컴퓨터

- 자료 data를 입력받아 처리 process 후 결과result를 출력

- ex. 계산기

=> 자료를 처리하는 기계만으로 이해하기는 불충분

 

 

프로그램이 가능한 자료 처리기로서의 컴퓨터

- 입력 -> 자료 처리 + 프로그램 -> 출력

 

 

프로그램 program

- 자료를 어떻게 처리할지 정의한 명령 집합

- 프로그래밍 과정의 결과물

-> 기계어 파일, 실행 파일 exe

 

 

 프로그래밍 과정

- 간단하게 코딩이라 부름

- 문제 해결을 위한 흐름(알고리즘)을 정의함

 

 

처리 속도 단위

- ms : 밀리 세컨드 $10^{-3}$초

- $\mu$s : 마이크로 세컨드 $10^{-6}$초

- ns : 나노 세컨드 $10^{-9}$초

- ps : 피코 세컨드 $10^{-12}$초

 

 

 

 

 

 

 

 

컴퓨터 발전 과정

- ~ 1930년 : 기계식 계산기

- 1930 ~ 1950 : 전자식 컴퓨터

 -> 1942, ABC : 선형 방정식 푸는 컴퓨터

 -> 1944, Colossus : 앨런 튜링, 독일 암호 해독

  ****************** 위는 특수 목적용 ************

 -> 1946, ENIAC : 최초 완전 전자식 범용 컴퓨터

                       (외부에서 프로그램이 저장 => 실행 프로그램이 컴퓨터 외부에 존재, 데이터만 내부 저장)

 -> 1950, EDVAC : 내장 프로그램 방식 최초 컴퓨터

                       (컴퓨터 내부에 프로그램이 저장됨)

 

 

 

 

 

 

* 앨런 튜링 : 현대 컴퓨터 고안(튜링 머신),

튜링 머신

- 태잎과 헤드가 존재

- 태잎에 데이터 읽기 쓰기

- 해드를 이동시켜 읽고쓸 태잎을 변경

 

http://www.aistudy.co.kr/computer/turing_machine.htm

 

 

 

 

 

 

 

세대별 컴퓨터 분류

1세대 1950 ~ 1959

- 진공관

 

2세대 1959 ~ 1965

- 트랜지스터

- 포트란, 코볼

 

3세대 1965 ~ 1975

- 집적회로

- SW 등작

 

4세대 1975 ~ 1985

- 초고밀도집적회로

- 마이크로 컴퓨터 등장 8800

- 네트워크 

 

5세대 1985 ~

- 현재

 

 

 

 

 

 

 

 

 

 

컴퓨터 분류

1. 컴퓨터 사용 목적에 따른 분류

- 범용 컴퓨터

- 전용 컴퓨터

 

2. 자료 표현 방식

- 디지털 컴퓨터 : 이산적인 값을 처리

- 아날로그 컴퓨터 : 연속적인 입력양을 그대로 사용

 

3. 성능에 따른 분류

- 슈퍼 컴퓨터

- 대형 컴퓨터

- 미니 컴퓨터

- 워크 스테이션

- 마이크로 컴퓨터

 

 

 

 

 

 

 

컴퓨터 과학이란? 

- 컴퓨터 제작하는 학문 ..?

- 프로그램 개발하는 학문 ..?

=> 컴퓨터를 이용하여 데이터를 통해 다양한 문제와 방법들을 다루는 학문

 

 

프로그램과 알고리즘

- 컴퓨터는 자료를 처리해서 결과(정보)를 얻는 기계

-> 문제를 해결하기 위한 명령어 집합인 프로그램을 사용

-> 알고리즘 : 문제 해결을 위한 흐름 -> 프로그램

 

 

 

관련 학문

- 자료 : 자료구조, 파일처리, 데이터베이스

- 정보 : 컴퓨터 시스템, 디지털 논리구조, 컴퓨터 구조

- 프로그램 : 운영체제, 컴파일러, 프로그래밍언어 ,정보통신, 컴퓨터그래픽스

- 알고리즘 : 이산구조, 계산이론, 인공지능, 알고리즘, 오토마타

=> 전반적인 분야를 파악해야함

 

 

 

 

 

 

컴퓨터 공학 computer engineering과 컴퓨터 과학computer science의 차이

- 컴퓨터 공학 engineering : 가성비 좋은 제품을 만들기 위해 HW와 SW 조합에 초점

- 컴퓨터 과학 science : 문제 해결의 효율성과 실현 가능성에 초점

 

 

 

 

시스템 system

- 원하는 목적을 달성하기 위해 여러가지 요소들이 모여 연결된 체계

 

HW 하드웨어 : 눈으로 볼수있는 장치로 물리적 기계 및 전자장치

 

 

폰 노이만 구조와 하버드 구조

- 컴퓨터 내부 구조, 처리 과정을 정의한 것

- 컴퓨터 구조는 대표적으로 폰노이만 구조와 하버드 구조가 있음

- 대부분은 폰 노이만 구조를 채택

- 데이터 버스와 명령어 버스가 통합 -> 폰노이만 구조

- 데이터 버스와 명령어 버스가 분리됨 -> 하버드 구조

 

 

 

 

 

4개의 서브시스템

기억 장치

- 데이터와 명령어(프로그램)을 저장하는 곳

-> 주기억 장치(RAM, ROM), 보조기억장치(HDD, SSD)

 

산술논리연산장치 ALU

- 데이터 산술(+-*/), 논리(and, or)연산하는 장치

 

제어 장치 Control Unit

- 기억장치, ALU, 입출력 장치 등 동작 제어

=> CPU = ALU + CU + Register

 

 

입출력 장치 I/O Device

- 입력 : 키보드, 마우스, 마이크

- 출력 : 모니터, 프린터, 스피커 

 

 

 

 

내장 프로그램 stored program

- 실행될 프로그램은 메모리에 저장 stored or 적재 load 되어야 함

- 프로그램과 데이터는 비트 패턴으로 메모리 상에 표현

 

 

자료와 정보

- I = P(D)

- I는 정보

- P는 처리

- D는 자료

- 자료D를 처리P하여 정보 I를 취득

 

 

 

자료의 형태

- 문자, 숫자, 이미지, 오디오, 비디오 등

=> 다 비트로 표현

 

 

 

 

 

 

 

 

 

 

 

 

 

 

자료 단위

- 비트 binary digit : 0과 1을 표현

- 바이트 byte : 비트 8개

- 킬로 바이트 KB kilo byte : 바이트 $2^10$

- MB 메가바이트, GB기가바이트, TB테라바이트, PB페타바이트

- 워드 word : 컴퓨터 연산의 기본 단위가 되는 정보 양 -> 32비트, 64비트

 

 

 

진법 number system

- 수를 세는 단위

- 2, 8, 10, 16진법

- 2진법은 읽거나 오류 찾기 힘듬

-> 8진법, 16진법 사용

- 010 : 8진법으로 2

 * 앞에 0이 붙으면 8진법 수

- 0x0a : 16진법으로 10

 * 앞에 0x가 붙으면 16진법수

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

300x250

'컴퓨터과학 > 컴퓨터, OS' 카테고리의 다른 글

HWSW - 2. 컴파일  (0) 2020.08.06
HWSW - 1. MCU  (0) 2020.08.05
os만들기 - 25. GDT와 IDT 다루기  (0) 2020.08.04
os만들기 - 24. 변수 표시하고, 마우스 띄우기  (0) 2020.08.03
os만들기 - 23. 문자(열) 처리  (0) 2020.08.03
728x90

컴파일러와 리버스 컴파일러

- 컴파일러 : 소스 코드 -> 기계어

- 리버스 컴파일러 : 기계어 -> 어셈블러/소스코드

 

 

인터프리터와 컴파일러

- 인터프리터 : 소스코드 한줄 씩 즉시 실행

- 기계어 : 소스코드 전체 기계어로 변환 후 실행

 

 

컴파일러 도구 정리

- 컴파일러 : 소스 코드 -> 어셈블리어

- 어셈블러 : 어셈블리어 -> 기계어(오브젝트 파일)

- 링커 : 오브젝트 파일들을 연결시켜 하나의 실행 가능한 파일(기계어) 생성

 

 

MCU와 고급언어

- 고급언어로 작성한 소스코드는 컴파일러에의해 어셈블리어로 변환, 이후 어셈블러로 각 MCU에 맞는 기계어로 변환됨

- ARM 어셈블리어 -> ARM 기계어

- AVR 어셈블리어 -> AVR 기계어

- x86 어셈블리어 -> X86기계어

 

 

기계어, 어셈블리어

- 기계어 : 0과 1로 이루어진 바이너리 코드

- 어셈블리어 : 기계어와 사람이 이해할 수 있는 명령어를 매칭시켜 만든 언어

- 아래의 그림은 고급언어, 어셈블리어, 기계어 사이 차이를 보여주고 있음

https://asecurity.dev/2017/07/%EC%97%AD%EB%B6%84%EC%84%9D-%EA%B8%B0%EA%B3%84%EC%96%B4%EB%A5%BC-%EB%B6%84%EC%84%9D%ED%95%9C%EB%8B%A4-disassemblers/

 

 

 

ARM 명령어 집합과 포멧

- ARM은 고정 크기와 형식을 갖는 명령어 사용. 아래의 그림은 32비트 고정 길이 명령어 집합을 보여줌.

- 아래와 같은 포멧으로 요소별 의미를 이해하여 올바른 값을 주면 곱샘, 인터럽트 등 기능 수행

https://damduc.tistory.com/115

- 좌측은 ARM 기계어 명령어, 우측은 디스어셈블한 어셈블리어 코드

http://www.jkelec.co.kr/img/lecture/arm_arch/arm_arch_4.html

 

 

 

명령어 처리기와 기계어

- 명령어 처리기가 해석하는 방식에 다라 기계어 코드가 달라야 함

 => 동일한 기계어 코드도 명령어 처리기가 다르면 다르게 해석

- 대표적인 명령어 처리기 구조 : RISC와 CISC

 

 

RISC와 CISC

- RISC Reduced Instruction Set Computer

  리스크. 축약된 명령어 집합. 적은 수의 명령어들만 제공 -> 단순구조, 고속

           => RISC 명령어 셋을 가진 경우. RISC머신(대표적으로 ARM)

- CISC Complex Instruction Set Computer

   씨스크. 복잡한 명령어 집합. 수많은 명령어들 제공 ->복잡구조, 저속 

          => CISC 명령어 셋을 가진 경우. CISC 머신(대표적으로 x86)

- 곱셈 연산에서의 리스크와 씨스크의 차이

 -> CISC는 mul 명령어로 곱샘 수행. RISC는 값들을 레지스터에 담은 후 반복문 수행

 -> CISC가 단순하다

 

 

 

통합 개발환경

- GUI 환경에서 개발을 편리하게 할수록 도와주는 도구

- 텍스트 에디터(소스 편집기)

- 빌드 도구 : 컴파일러, 어셈블러, 링커

- 디버거 : 실제 MCU를 동작시키며 MCU 변수, 레지스터 등의 상태 변화 학인

- 에뮬레이터 : MCU와 개발 환경을 연결시켜줌

- 시뮬레이터 : 가상의 기계와 개발 환경을 연결시킴

 

 

 

 

 

 

 

 

 

컴파일 과정

1. 전처리 preprocessing

- c언어에서 #define이나 #include 명령어들이 존재 

 => #define은 해당 단어 치환하고, #include는 관련 해더파일 복붙

 

2. 낱말 분석 lexical analysis

- 어셈블리어는 명령어와 데이터만으로 구성 => C언어도 명령어와 데이터로 구분하자

 => 몇개의 데이터, 명령어 등이 필요한지 알 수 있음.

- 아래의 그림은 낱말 분석 입력 예시

- 낱말 분석의 결과 아래와 같이 지시자, 식별자, 연산자, 값 등으로 정리됨

https://www.youtube.com/watch?v=edZfw9Yp7h4

 

3. 코드 최적화 code optimization

- 실행 공간을 절약하거나, 사용하지 않는 변수 제거하는 등 작업 수행

- 아래의 그림은 공간 낭비를 줄여주는 최적화 예시

https://atadiat.com/en/e-application-note-tips-tricks-optimize-c-code/

 

 

4. 메모리 테이블화 (심볼 테이블)

- 코드에 존재하는 변수들을 읽고 저장하기 위해 메모리 필요. -> 메모리 주소와 변수 명을 연결

 => 심볼 테이블 작성 : 해당 변수가 어느 메모리를 사용하는가?

- 심볼 테이블은 변수들을 ID로 관리. 변수들은 고유의 수납장 번호(이름을 갖고 있음)

 

https://victorydntmd.tistory.com/241

 

 

5. 구문 분석 syntax analysis

- 구문 syntax의 의미 : 단어들이 모여서 만든 의미

 => 코드가 언어의 문법을 따르는지 분석. 구문 나무 syntax tree 사용

- 구문 나무 사용 이유 : 명령어와 변수 구분이 편하고, 어셈블리어로 변환이 쉬움

- 아래의 그림은 구문 나무를 이용하여 명령어와 데이터 정리 형태를 보여줌

http://chirucprogvideos.blogspot.com/2015/06/compiler-and-interpreter.html

 

- 단어 분석과 구문분석의 결과 예시

https://homoefficio.github.io/2019/01/31/Back-to-the-Essence-Java-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EC%97%90%EC%84%9C-%EC%8B%A4%ED%96%89%EA%B9%8C%EC%A7%80-1/

 

 

 

6. 어셈블리어 명령어 치환

- 구문 분석과정에서 명령어와 데이터가 구분됨

 => 데이터는 심볼테이블 ID(주소), 명령어는 어셈블러로 치환하자

 

7. 어셈블리어 완성

- 데이터들을 실제 메모리로 할당

 

8. 완성된 어셈블리어로 기계어 생성

- 어셈블리를 이용해서 기계어 작성

https://www.slideserve.com/barny/assembly-machine-language

 

300x250
728x90

MCU Micro Controller Unit

- 전자제품에사용되는 제어 유닛

- 주위 주변장치 GPIO, UART 등 제어함

- CPU보다 저전력, 저성능, 저럼, 많이사용

- 구성 : 메모리, 레지스터, 페리페럴, 버스 등으로 구성

참고 : https://www.hackerschool.org/Sub_Html/HS_University/HardwareHacking/04.html

 

 

 

PCB Printed Circuit Board

- 녹색 전자 회로 기판

- 이 위에 MCU와 다른 전자 소자 등이 올라감

 

https://en.wikipedia.org/wiki/Printed_circuit_board

 

 

IC Integrated Circuit

- 집적화된 회로

- 전자 부품, 소자들을 모아 하나의 회로에 집적화한것

- 아래의 그림은 IC칩의 패키지들. IC칩 내부 소자들을 보호하기 위한 플라스틱 케이스

 

http://blog.daum.net/go_ahead/14

- 아래의 그림은 다이칩 die chip : 실제 패키지 내부 IC

https://metallux.ch/hybridcircuit/chip-on-board-cob/

 

 

 

 

MCU 내부 장치

1. 버스

- 메모리, 페리페럴, CPU 사이 데이터 주고받기위한 공용 통로.

- Control bus 제어버스, Address bus 주소버스, Data bus 데이터버스 등 존재

- MCU가 제어 명령, 주소, 데이터 등을 전송시키는 시간을 조절하여 데이터간 충돌을 회피

 

https://www.renesas.com/us/en/support/technical-resources/engineer-school/mcu-programming-peripherals-01-gpio.html

 

2. 메모리

- 기억 장치. 휘발성 비휘발성에따라 RAM과 ROM으로 구분

- 휘발성 : 전원이 없을때 데이터가 날아감 -> RAM

- 비휘발성 : 전원이 없을때 데이터 보존 -> ROM

 

2.1 ROM Read Only Memory의 종류

 - PROM Programmable ROM - 프로그래밍 가능한 메모리. 한번만 쓰기가능

 - EPROM Erasable Programmable ROM - 자외선으로 쓰고 지우는게 가능한 PROM

 - EEPROM Electric EPROM : 전기적 충격으로 쓰기 읽기가 가능한 ROM

        => 대표적인 EEPROM으로 Flash Memory가 존재

 

2.2 RAM Random Access Memory

 - SRAM, DRAM, SDRAM이 존재

 - SRAM Static Random Access Moery

      -> 트랜지스터만으로 만든 메모리, 고속, 고가 소형화 힘듬 

 - DRAM Dynamic RAM

      -> 캐퍼스터와 트랜지스터로 구성된 메모리                        

      ->  캐퍼시터를 사용한 만큼 데이터 손실을 막기위해 충전하는 Refresh Time 필요. 저가

 - SDRAM Synchronous DRAM

    -> 동기식 메모리. 외부에서 공급해주는 클럭에 따라 동기화

    -> 클럭 신호선이 존재하여 데이터 처리 시 지연시간 latency 필요

 - 아래의 그림은 SRAM의 소자

https://ko.wikipedia.org/wiki/%EC%A0%95%EC%A0%81_%EB%9E%A8

 

 

 

3. 메모리맵

- 대부분의 MCU의 데이터 시트에서 제공하는 정보로 할당된 각 메모리의 주소와 용량을 알 수 있음.

- 아래은 하드웨어 구성을 위한 메모리맵으로 SRAM 영역이 0x2000 0000 ~ 0x3FFF FFFF임을 알 수 있음

 

https://todayis.tistory.com/tag/%EB%A9%94%EB%AA%A8%EB%A6%AC%EB%A7%B5

 

- 아래의 그림은 소프트웨어적으로 분리된 메모리 맵

 스택 영역 stack : 지역변수, 함수 포인터

 힙 영역 heap : 동적 할당된 메모리 공간

 코드 영역 code : 명령어 등이 저장됨

 데이터 영역 data : 전역 변수들이 저장

https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EB%A6%AC_%EB%A7%B5

 

 

4. 레지스터 register

- MCU 내부에 존재하는 가장 빠른 메모리. 비싸 페리페럴이나 CPU 내부에 작은 공간을 가짐

- 프로그램 카운터 PC Program Counter, 스택 포인터 SP Stack Pointer, 명령 레지스터 IR Instruction Register, 데이터 레지스터 DR Data Register, AR Address Register, 범용 레지스터 General Register 등 존재

 

4.1 레지스터 종류

- 프로그램 카운터 : 다음에 실행할 명령어의 주소를 저장하는 레지스터

 * 아래의 그림은 프로그램의 실행과정으로 여기서 프로그램 카운터가 다음에 실행할 명령어 주소를 가지고 있음

   => 프로그램 카운터의 값이 실행할 명령어 위치가 되어 찾아감

      다음 프로그램 카운터값은 명령어 크기만큼 +됨.

      32비트 컴퓨터 즉, 4바이트 크기라면 0x0000 -> 0x0004 -> 0x0008

https://kasckasc.tistory.com/entry/2-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%98-%EC%8B%A4%ED%96%89-%EA%B3%BC%EC%A0%95-%EB%B0%8F-CPU-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9B%90%EB%A6%AC

- 스택 포인터

   -> 함수 호출과 관련된 정보들을 스택 자료구조로 저장하는 공간.

   -> 함수가 호출되고 종료후 돌아갈 지점과 데이터들을 관리

 

 

http://www.tcpschool.com/c/c_memory_stackframe

 

- 명령어 레지스터 : 명령어들을 담는 레지스터

- 데이터 레지스터 : 데이터들을 담는 레지스터

- 범용 레지스터 : 프로그램 카운터, 스택 포인터, 명령/데이터 레지스터 제외한 범용 목적의 레지스터

 

 

 

5. 메모리 관리 유닛 MMU Memory Management Unit

MMU

- 메모리 공간에 프로그램을 로드하여 사용중에 충돌이나 문제를 막기위한 HW.

  * 동일한 문제를 막기위한 SW로 OS가 있음.

 

MMU 기능과 구성 요소

- 기능 : 가상 메모리 Virtual Memory와 실제 Physical memory 사이 변환 및 메모리 보호

- 가상 메모리 : 가상의 메모리로 물리적인 메모리보다 큼

- 물리 메모리 : 실제 존재 존재하는 메모리

 

가상 메모리, MMU를 사용하는 이유

- 소프트웨어는 자신이 사용할 공간을 자기가 고르지 못하고 메모리 관리를 위해 운영체제가 배정해줌

- 프로그램들은 가상 메모리의 0x0000 0000에서 시작하지만 실제로 MMU가 물리 메모리의 주소로 변환해줌

- 여러 프로그램들이 초기 지점인 0x0000 0000에서 시작하고, 물리 메모리 상에서 겹쳐지지 않게 하기 위함

 

https://awesomebit.tistory.com/22

 

 

6. 캐시 메모리

캐시 메모리

- CPU는 매우 빠르게 동작하지만, 메모리는 CPU에 비해서 상대적으로 느림

- 중간에 데이터를 미리 모아놓기 위한 공간으로 캐시메모리 사용.

- 캐시메모리는 레지스터보다 크나 메모리에 보다 매우 작음

- 아래의 그림은 CPU와 캐시 메모리, 메모리 사이의 구조를 보여줌

https://m.blog.naver.com/PostView.nhn?blogId=remocon33&logNo=220129153263&proxyReferer=https%3A%2F%2Fwww.google.com%2F

 

 

 

7. 페리페럴 Peripheral

페리페럴

- MCU 내부에 존재하는 주변장치들, 주변장치들을 사용하기 위한 단자

- 대표적으로 통신 페리페럴로 UART, SPI, I2C 등이 존재

- 아래의 그림은 페리페럴들의 예시

https://www.renesas.com/us/en/support/technical-resources/engineer-school/mcu-programming-peripherals-01-gpio.html

 

UART Universal Asychronous Receive Transmiter

- 범용 비동기 수신 발신기로 송신용 Tx와 수신용 Rx 신호선 2개로 구성됨

- 비동기 인 만큼 클럭에 상관없이 통신은 가능

- 아래의 그림은 UART 통신 시 연결

https://www.weekitech.com/2019/05/13/introduction-to-uart/

- 아래의 그림은 UART 통신시 데이터 프레임(형태)

https://m.blog.naver.com/PostView.nhn?blogId=1992cjm&logNo=220438189832&proxyReferer=https:%2F%2Fwww.google.com%2F

 

 

 

RS-232

- 비동기 통신 중 길이와 속도를 맞추기 위해 RS-232라는 표준이 제정됨

 => RS-232로 특정 규칙에 따라 UART 통신이 안정적으로 수행됨

- 아래의 그림은 RS-232 핀아웃 별 의미

http://raspberrypi.tomasgreno.cz/uart-to-rs-232.html

 

- 아래의 그림은 RS-232 통신 시 데이터 프레임

https://qastack.kr/electronics/110478/difference-between-uart-and-rs-232

 

 

 

 

명령어 실행과정

- 명령어 실행 사이클이라도 함

- 메모리에서 명령어를 가져오고 fetch, 명령어를 해독 후 decode, 실행 excution하는 과정

https://kasckasc.tistory.com/entry/2-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%98-%EC%8B%A4%ED%96%89-%EA%B3%BC%EC%A0%95-%EB%B0%8F-CPU-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9B%90%EB%A6%AC

 

 

 

 

파이프라인

파이프라인 구조

- 명령어 실행 과정으로,  패치 -> 해석 -> 실행 -> 저장 으로 정리할 수 있음

- 이를 고속으로 하기 위한 구조를 파이프라인. 

 

파이프라인과 싱글라인의 차이

- 기존의 싱글 라인 구조는 하나의 명령어가 종료될때까지 다음 명령어가 대기

- 파이프라인 구조에서는 첫 명령어가 다음 단계로 넘어가면 새로운 명령어가 들어와 여러 명령어가 동시 수행

- 아래의 그림은 파이프라인 구조 예시

https://doitnow-man.tistory.com/72

 

 

 

인터럽트

인터럽트란

- 프로그램 수행시 중간에 처리해야하는 상황과 동작 -> 예외처리/인터럽트라 부름

- 인터럽트 발생시 인터럽트 종류에 따라(인터럽트 벡터 테이블을 참고하여) 정해진 명령으로 PC의 값이 변경됨

- 아래의 그림은 인터럽트 벡터 테이블로 인터럽트 종류에 따라 수행해야할 동작들의 주소를 알려줌

 

https://blog.naver.com/seim_ryu/110020336124

 

인터럽트 관련 용어

- 인터럽트 벡터 :인터럽트를 처리하기위한 PC값으로 벡터 테이블에서 프로그램 카운터 값을 가져옴

- 인터럽트 핸들러 ISR Interrupt Service Routine : 인터럽트 발생시 처리해야할 콜백 함수

- 아래의 그림은 인터럽트 처리 과정

을 보여줌

https://m.blog.naver.com/PostView.nhn?blogId=scw0531&logNo=220650635893&proxyReferer=https:%2F%2Fwww.google.com%2F

 

 

 

 

 

 

 

300x250
728x90

문제

- 마우스가 안움직이는건 GDT와 IDT를 설정하지 않음

 

 

 

GDT와 IDT를 다루기 전에

- 이들은 CPU에서 설정하는것 asmhead.nas에서 프로그램을 대충 돌리는 내용만 있지. GDT와 IDT 설정은 안됨.

- 세그먼트와 인터럽트를 다루는게 우선

 

메모리의 사용과 세그먼테이션

- ORG 명령으로 읽을 프로그램이 어디에 있는지 선언하지 않으면, 원하는 프로그램을 읽지 못함

 => ex. ORG 0x1234 하면 프로그램은 메모리의 0x1234번지를 읽어야함

- 여러 프로그램이 돌다가 동일한 번지를 건드릴수 있는데, 막아야함 => 사용중인 메모리 접근 막는 것이 세그먼테이션

 

세그먼테이션

- 총 4GB 메모리를 분할후 각 블록들의 처음 번지를 0으로 다루는 기능

 => 모든 프로그램에서 ORG 0으로 시작. 구분된 블록 하나 하나를 세그먼트라 함.

 

페이징

- 세그먼트는 4GB를 분할하는것이지만 페이징은 4GB를 테스트 수만큼 만들어 메모리 순서를 바꾸어 충돌 막는 방법?

 

 

 

 

 

 

 

 

 

세그먼트 관리

- 세그먼테이션이 세그먼트 1개를 표현하기위한 필요 정보 : 세그먼트 크기, 세그먼트 시작번지, 세그먼트 관리 속성

 => CPU에서 8바이트 데이터로 위 정보들을 표현.

- 세그먼트 설정을 위한 세그먼트 레지스터는 16비트 뿐

 =>그래픽 팔레트와 동일하게, 세그먼트 번호를 세그먼트 레지스터에 담고 세그먼트 번호가 어느 것에 대응하는지 설정

 

 

세그먼트 번호 사용 가능 범위

- 팔레트에서 색번호는 0 ~ 255. 2^8만큼 사용

- 세그먼트 번호는 세그먼트 레지스터가 16비트라 2^16만큼 일것같으나, CPU 성능 상 하위 3비트는 사용불가

  => 사용 가능한 세그먼트 번호는 13비트. 즉,  범위가 0 ~ 8,191

 

 

팔레트와 세그먼트 차이

- 팔레트는 그래픽(외부 장치)를 다루므로 io_out을 사용

- 세그먼트는 CPU 내부만의 레지스터 만으로 정리하므로 필요없음

 

 

세그먼트 정의하기

- 세그먼트 번호는 0 ~ 8,191까지 존재

- 실제 전체 세그먼트는 2^16 => 64KB 요구. 하지만 CPU는 64KB를 기억 불가

 => CPU 대신 메모리에 세그먼트들을 기록은것

 

 

GDT Global (Segment) Descriptor Table

- 전역 세그먼트 기술자 표

- 메모리에 세그먼트들을 나열하고, 세그먼트 맨 앞 번지와 설정할 세그먼트 갯수를 GDTR 레지스터에 설정

 * GDTR : Global (Segment) Descriptor Table Regisor

 

 

IDT Interrupt Descriptor Table

- 인터럽트 기술자 표

- CPU는 외부 상황이나 내부 문제 발생시 잠시 스위칭 함 => 인터럽트 

- 컴퓨터에는 그래픽, 메모리, 키보드, 사운드 등 다양한 처리해야할 장치가 많음

 => 이를 빠르게 처리하기 위해 인터럽트를 사용

 

인터럽트

- 각 장치에 변화가 있을시 발생

- 인터럽트 발생 -> CPU는 기존에 하던일을 멈춤 -> 다시 할수있게 준비 -> 인터럽트 처리 -> 이전작업으로 돌아감

 

 

마우스를 쓰려면

- 인터럽트 설정필요

- IDT는 인터럽트번호 0 ~ 255까지 준비, 특정 번호의 인터럽트가 호출되면 대응되는 콜백함수를 수행함.

- GDT와 IDT 설정을 하자

 

 

 

GDT, IDT 관련 코드들

- SEGMENT_DESCRIPTOR : GDT를 구성하는 8바이트 구조체

- GAST_DESCRIPTOR : IDT를 구성하는 8바이트 구조체

 

struct SEGMENT_DESCRIPTOR {
	short limit_low, base_low;
	char base_mid, access_right;
	char limit_high, base_high;
};

struct GATE_DESCRIPTOR {
	short offset_low, selector;
	char dw_count, access_right;
	short offset_high;
};

void init_gdtidt(void);
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar);
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar);
void load_gdtr(int limit, int addr);
void load_idtr(int limit, int addr);






void init_gdtidt(void)
{
	struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000;
	struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) 0x0026f800;
	int i;

	/* GDT 초기화 */
	for (i = 0; i < 8192; i++) {
		set_segmdesc(gdt + i, 0, 0, 0);
	}
	set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);
	set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a);
	load_gdtr(0xffff, 0x00270000);

	/* IDT 초기화 */
	for (i = 0; i < 256; i++) {
		set_gatedesc(idt + i, 0, 0, 0);
	}
	load_idtr(0x7ff, 0x0026f800);

	return;
}

void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
	if (limit > 0xfffff) {
		ar |= 0x8000; /* G_bit = 1 */
		limit /= 0x1000;
	}
	sd->limit_low    = limit & 0xffff;
	sd->base_low     = base & 0xffff;
	sd->base_mid     = (base >> 16) & 0xff;
	sd->access_right = ar & 0xff;
	sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);
	sd->base_high    = (base >> 24) & 0xff;
	return;
}

void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{
	gd->offset_low   = offset & 0xffff;
	gd->selector     = selector;
	gd->dw_count     = (ar >> 8) & 0xff;
	gd->access_right = ar & 0xff;
	gd->offset_high  = (offset >> 16) & 0xffff;
	return;
}

 

GDT, IDT 초기화

- gdt 변수에 0x0027 0000을 대입 -> 0x0027 0000 ~ 0x0027 ffff를 GDT로 하기 위함

- idt 변수에 0x0026 f800 대입 -> 0x0026 f800 ~ 0x26 ffff 번지를 IDT로 함

 

 

- gdt는 2^13 = 8192번까지 세그먼트 설정

 * 하나의 gdt당 8바이트 이므로 아래와 같이 gdt의 다음 번지로 넘어감

0x0027 0000

0x0027 0008

0x0027 0010

0x0027 0018

      ...

0x0026  ffff

- 결국 아래의 그림에서 반복문으로 모든 세그먼트들에 대해서

 gdt의 번지들에 리미트(세그먼트 크기 - 1) 0, 베이스(시작 번지) 0, 엑세스 권한 속성 0으로 설정

 

- 반복문 다음 구문 set_segmdesc로  gdt + 1-> 1번 GDT,  gdt + 2는 2번 GDT

 => 1번은 크기가 0xffff ffff(4GB), 시작 번지 0, 세그먼트속성 0x4092

 => 2번은 크기가 0x0007 ffff(512KB), 시작번지 0x0028 0000, -> bootpack.hrb를 위해 작성

- 마지막으로 load gdtr. C언어에서 GDTR을 다룰수 없어 어셈블러 상에 함수 작성

- IDT는 GDT와 동일한 과정으로 초기화

 

 

 

 

정리

- 이번 시간에는 GDT, IDT 설정하는 과정만 다루고 실제 인터럽트 처리 같은것들을 하지 않아서 동작은 안함.

- 다시 내용을 정리하면 동일한 메모리 번지 사용 충돌을 막기위해 세그먼테이션 사용

- 세그먼테이션은 메모리를 블록으로 분할한 후, 각 블록(세그먼트)들을 처음 번지를 0으로 다룸.

- 세그먼테이션에서 세그먼트 1개가 갖는 정보들 : 세그먼트 크기, 시작번지, 관리속성

- GDT : 수많은 세그먼트 데이터들을 CPU 대신 메모리에 저장해놓은 테이블 Global (Segment) Descriptor Table

- IDT : 인터럽트 기술자 표, 인터럽트 기능을 수행하기 위해 인터럽트 번호와 콜벡함수를 정리한 테이블

- IDT를 쓰기전에 GDT 먼저 설정이 필요

- GDT와 IDT 구조체는 1개당 8바이트

- 소스 코드 상 모든 가능한 GDT들에 대해 크기 0, 시작점 0, 속성 0으로 설정

- 1, 2번 GDT만 별도 설정 수행

- IDT는 GDT와 유사하게 설정

 

300x250
728x90

현황

- 이전 시간에는 폰트 데이터를 만들어 출력해보고, 이를 간편하게하기 위한 putfont8, 문자열을 출력하기위한 putfonts8 함수 등을 구현했습니다.

 

 

문제

- 프로그램 구현시 잘못된 값이 있을것 같을때 해당 변수를 출력해보면 되지만, 지금의 시스템에서는 디버거를 돌릴수도 없고 힘듬

 

방법

- 변수를 출력하기 위해 sprintf() 함수로 출력할 메모리에 원하는 변수 값을 담고, putfons8로 sprintf()로 할당한 변수 공간을 전달해주면 되겠습니다.

 

 

sprintf

- 첫번째 매개변수에 서식에 맞게 값을 대입하여 저장

- sprintf(주소, 서식, 값)

- ex sprintf(s, "%x", 123); 숫자 123을 16진수 형태의 문자열로 변환하여 s 번지에 담는다.

 

 

 

 

매인 함수

- 대부분의 내용은 이전 예제와 동일

- 출력시키고자 하는 변수를 sprintf()함수로 서식에 맞게 문자열로 변환 -> 주소 s에다가 문자열 담음

- 배열 주소 s를 putfonts8로 출력 수행

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
	char s[40];

	init_palette();
	init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
	putfonts8_asc(binfo->vram, binfo->scrnx,  8, 8, COL8_FFFFFF, "ABC 123");
	putfonts8_asc(binfo->vram, binfo->scrnx, 31, 31, COL8_000000, "Haribote OS.");
	putfonts8_asc(binfo->vram, binfo->scrnx, 30, 30, COL8_FFFFFF, "Haribote OS.");
	sprintf(s, "scrnx = %d", binfo->scrnx);
	putfonts8_asc(binfo->vram, binfo->scrnx, 16, 64, COL8_FFFFFF, s);

	for (;;) {
		io_hlt();
	}
}

 

실행결과

- binfo 구조체에 들어있는 변수값 scrnx를 문자열로 바꾸어 s에 담고 이를 putfont8s로 원하는 (16, 64) 위치에 출력

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

#include <stdio.h>

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen8(char *vram, int x, int y);
void putfont8(char *vram, int xsize, int x, int y, char c, char *font);
void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s);
void init_mouse_cursor8(char *mouse, char bc);
void putblock8_8(char *vram, int vxsize, int pxsize,
	int pysize, int px0, int py0, char *buf, int bxsize);

#define COL8_000000		0
#define COL8_FF0000		1
#define COL8_00FF00		2
#define COL8_FFFF00		3
#define COL8_0000FF		4
#define COL8_FF00FF		5
#define COL8_00FFFF		6
#define COL8_FFFFFF		7
#define COL8_C6C6C6		8
#define COL8_840000		9
#define COL8_008400		10
#define COL8_848400		11
#define COL8_000084		12
#define COL8_840084		13
#define COL8_008484		14
#define COL8_848484		15

struct BOOTINFO {
	char cyls, leds, vmode, reserve;
	short scrnx, scrny;
	char *vram;
};

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
	char s[40], mcursor[256];
	int mx, my;

	init_palette();
	init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);
	mx = (binfo->scrnx - 16) / 2; /*마우스 커서 위치*/
	my = (binfo->scrny - 28 - 16) / 2;
	init_mouse_cursor8(mcursor, COL8_008484);
	putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
	sprintf(s, "(%d, %d)", mx, my); /*마우스 위치 변수를 담음 문자 출력을 위해*/
	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);

	for (;;) {
		io_hlt();
	}
}

void init_palette(void)
{
	static unsigned char table_rgb[16 * 3] = {
		0x00, 0x00, 0x00,	/*  0:검은색 */
		0xff, 0x00, 0x00,	/*  1:밝은 적색 */
		0x00, 0xff, 0x00,	/*  2:밝은 녹색 */
		0xff, 0xff, 0x00,	/*  3:밝은 노란색 */
		0x00, 0x00, 0xff,	/*  4:밝은 청색 */
		0xff, 0x00, 0xff,	/*  5:밝은 보라색 */
		0x00, 0xff, 0xff,	/*  6:밝은 청색*/
		0xff, 0xff, 0xff,	/*  7:흰색 */
		0xc6, 0xc6, 0xc6,	/*  8:밝은 회색 */
		0x84, 0x00, 0x00,	/*  9:어두운 적색 */
		0x00, 0x84, 0x00,	/* 10:어두운 녹색 */
		0x84, 0x84, 0x00,	/* 11:어두운 노란색 */
		0x00, 0x00, 0x84,	/* 12:군청색 */
		0x84, 0x00, 0x84,	/* 13:어두운 보라색 */
		0x00, 0x84, 0x84,	/* 14:어두운 청색 */
		0x84, 0x84, 0x84	/* 15:어두운 회색 */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char 명령은 (주소가 아니라)데이터밖에 쓰지 못하지만, DB명령과 동일함 */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();	/* 인터럽트 허가 플래그 값을 기록 */
	io_cli(); 					/* 허가 플래그를 0으로 하여 인터럽트 금지 */
	io_out8(0x03c8, start);
	for (i = start; i <= end; i++) {
		io_out8(0x03c9, rgb[0] / 4);
		io_out8(0x03c9, rgb[1] / 4);
		io_out8(0x03c9, rgb[2] / 4);
		rgb += 3;
	}
	io_store_eflags(eflags);	/* 인터럽트 허가 플래그를 본래 값으로 되돌린다. */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x, y;
	for (y = y0; y <= y1; y++) {
		for (x = x0; x <= x1; x++)
			vram[y * xsize + x] = c;
	}
	return;
}

void init_screen8(char *vram, int x, int y)
{
	boxfill8(vram, x, COL8_008484,  0,     0,      x -  1, y - 29);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 28, x -  1, y - 28);
	boxfill8(vram, x, COL8_FFFFFF,  0,     y - 27, x -  1, y - 27);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 26, x -  1, y -  1);

	boxfill8(vram, x, COL8_FFFFFF,  3,     y - 24, 59,     y - 24);
	boxfill8(vram, x, COL8_FFFFFF,  2,     y - 24,  2,     y -  4);
	boxfill8(vram, x, COL8_848484,  3,     y -  4, 59,     y -  4);
	boxfill8(vram, x, COL8_848484, 59,     y - 23, 59,     y -  5);
	boxfill8(vram, x, COL8_000000,  2,     y -  3, 59,     y -  3);
	boxfill8(vram, x, COL8_000000, 60,     y - 24, 60,     y -  3);

	boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);
	boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);
	boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);
	boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);
	return;
}

void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
	int i;
	char *p, d /* data */;
	for (i = 0; i < 16; i++) {
		p = vram + (y + i) * xsize + x;
		d = font[i];
		if ((d & 0x80) != 0) { p[0] = c; }
		if ((d & 0x40) != 0) { p[1] = c; }
		if ((d & 0x20) != 0) { p[2] = c; }
		if ((d & 0x10) != 0) { p[3] = c; }
		if ((d & 0x08) != 0) { p[4] = c; }
		if ((d & 0x04) != 0) { p[5] = c; }
		if ((d & 0x02) != 0) { p[6] = c; }
		if ((d & 0x01) != 0) { p[7] = c; }
	}
	return;
}

void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
	extern char hankaku[4096];
	for (; *s != 0x00; s++) {
		putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
		x += 8;
	}
	return;
}

void init_mouse_cursor8(char *mouse, char bc)
/* 마우스 커서 준비 (16x16) */
{
	static char cursor[16][16] = {
		"**************..",
		"*OOOOOOOOOOO*...",
		"*OOOOOOOOOO*....",
		"*OOOOOOOOO*.....",
		"*OOOOOOOO*......",
		"*OOOOOOO*.......",
		"*OOOOOOO*.......",
		"*OOOOOOOO*......",
		"*OOOO**OOO*.....",
		"*OOO*..*OOO*....",
		"*OO*....*OOO*...",
		"*O*......*OOO*..",
		"**........*OOO*.",
		"*..........*OOO*",
		"............*OO*",
		".............***"
	};
	int x, y;

	for (y = 0; y < 16; y++) {
		for (x = 0; x < 16; x++) {
			if (cursor[y][x] == '*') {
				mouse[y * 16 + x] = COL8_000000;
			}
			if (cursor[y][x] == 'O') {
				mouse[y * 16 + x] = COL8_FFFFFF;
			}
			if (cursor[y][x] == '.') {
				mouse[y * 16 + x] = bc;
			}
		}
	}
	return;
}

void putblock8_8(char *vram, int vxsize, int pxsize,
	int pysize, int px0, int py0, char *buf, int bxsize)
{
	int x, y;
	for (y = 0; y < pysize; y++) {
		for (x = 0; x < pxsize; x++) {
			vram[(py0 + y) * vxsize + (px0 + x)] = buf[y * bxsize + x];
		}
	}
	return;
}

현황

- 문자, 문자열, 변수 출력도 잘 됬다. 이제 마우스 커서도 그려보자

 

 

 

마우스 커서 초기화

- 마우스 커서를 16 x 16으로 하면 256바이트 공간이 필요

- 해당 공간에 마우스 커서 데이터를 담자

 *bc는 커서 배경색

void init_mouse_cursor8(char *mouse, char bc)
/* 마우스 커서 준비 (16x16) */
{
	static char cursor[16][16] = {
		"**************..",
		"*OOOOOOOOOOO*...",
		"*OOOOOOOOOO*....",
		"*OOOOOOOOO*.....",
		"*OOOOOOOO*......",
		"*OOOOOOO*.......",
		"*OOOOOOO*.......",
		"*OOOOOOOO*......",
		"*OOOO**OOO*.....",
		"*OOO*..*OOO*....",
		"*OO*....*OOO*...",
		"*O*......*OOO*..",
		"**........*OOO*.",
		"*..........*OOO*",
		"............*OO*",
		".............***"
	};
	int x, y;

	for (y = 0; y < 16; y++) {
		for (x = 0; x < 16; x++) {
			if (cursor[y][x] == '*') {
				mouse[y * 16 + x] = COL8_000000;
			}
			if (cursor[y][x] == 'O') {
				mouse[y * 16 + x] = COL8_FFFFFF;
			}
			if (cursor[y][x] == '.') {
				mouse[y * 16 + x] = bc;
			}
		}
	}
	return;
}

 

 

 

마우스를 출력하기 위한 함수

-  buf에 있는 데이터를 vram에다가 복사

- 마우스 (버퍼) 데이터들을 화면에 띄워주는데 사용

void putblock8_8(char *vram, int vxsize, int pxsize,
	int pysize, int px0, int py0, char *buf, int bxsize)
{
	int x, y;
	for (y = 0; y < pysize; y++) {
		for (x = 0; x < pxsize; x++) {
			vram[(py0 + y) * vxsize + (px0 + x)] = buf[y * bxsize + x];
		}
	}
	return;
}

 

 

메인함수

- 화면 중심을 마우스 커서 위치로 잡고 이를 putlock8_8 함수로 해당 vram 주소에다가 마우스 그리기

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
	char s[40], mcursor[256];
	int mx, my;

	init_palette();
	init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);
	mx = (binfo->scrnx - 16) / 2; /*마우스 커서 위치*/
	my = (binfo->scrny - 28 - 16) / 2;
	init_mouse_cursor8(mcursor, COL8_008484);
	putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
	sprintf(s, "(%d, %d)", mx, my); /*마우스 위치 변수를 담음 문자 출력을 위해*/
	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);

	for (;;) {
		io_hlt();
	}
}

 

실행 결과

- 화면 중심에 마우스 커서와 해당 위치(변수) 문자 출력

 

 

 

 

 

 

 

300x250
728x90
void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen(char *vram, int x, int y);
void putfont8(char *vram, int xsize, int x, int y, char c, char *font);

#define COL8_000000		0
#define COL8_FF0000		1
#define COL8_00FF00		2
#define COL8_FFFF00		3
#define COL8_0000FF		4
#define COL8_FF00FF		5
#define COL8_00FFFF		6
#define COL8_FFFFFF		7
#define COL8_C6C6C6		8
#define COL8_840000		9
#define COL8_008400		10
#define COL8_848400		11
#define COL8_000084		12
#define COL8_840084		13
#define COL8_008484		14
#define COL8_848484		15

struct BOOTINFO {
	char cyls, leds, vmode, reserve;
	short scrnx, scrny;
	char *vram;
};

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
	static char font_A[16] = {
		0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,
		0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00
	};

	init_palette();
	init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
	putfont8(binfo->vram, binfo->scrnx, 10, 10, COL8_FFFFFF, font_A);

	for (;;) {
		io_hlt();
	}
}

void init_palette(void)
{
	static unsigned char table_rgb[16 * 3] = {
		0x00, 0x00, 0x00,	/*  0:검은색 */
		0xff, 0x00, 0x00,	/*  1:밝은 적색 */
		0x00, 0xff, 0x00,	/*  2:밝은 녹색 */
		0xff, 0xff, 0x00,	/*  3:밝은 노란색 */
		0x00, 0x00, 0xff,	/*  4:밝은 청색 */
		0xff, 0x00, 0xff,	/*  5:밝은 보라색 */
		0x00, 0xff, 0xff,	/*  6:밝은 청색*/
		0xff, 0xff, 0xff,	/*  7:흰색 */
		0xc6, 0xc6, 0xc6,	/*  8:밝은 회색 */
		0x84, 0x00, 0x00,	/*  9:어두운 적색 */
		0x00, 0x84, 0x00,	/* 10:어두운 녹색 */
		0x84, 0x84, 0x00,	/* 11:어두운 노란색 */
		0x00, 0x00, 0x84,	/* 12:군청색 */
		0x84, 0x00, 0x84,	/* 13:어두운 보라색 */
		0x00, 0x84, 0x84,	/* 14:어두운 청색 */
		0x84, 0x84, 0x84	/* 15:어두운 회색 */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char 명령은 (주소가 아니라)데이터밖에 쓰지 못하지만, DB명령과 동일함 */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();	/* 인터럽트 허가 플래그 값을 기록 */
	io_cli(); 					/* 허가 플래그를 0으로 하여 인터럽트 금지 */
	io_out8(0x03c8, start);
	for (i = start; i <= end; i++) {
		io_out8(0x03c9, rgb[0] / 4);
		io_out8(0x03c9, rgb[1] / 4);
		io_out8(0x03c9, rgb[2] / 4);
		rgb += 3;
	}
	io_store_eflags(eflags);	/* 인터럽트 허가 플래그를 본래 값으로 되돌린다. */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x, y;
	for (y = y0; y <= y1; y++) {
		for (x = x0; x <= x1; x++)
			vram[y * xsize + x] = c;
	}
	return;
}

void init_screen(char *vram, int x, int y)
{
	boxfill8(vram, x, COL8_008484,  0,     0,      x -  1, y - 29);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 28, x -  1, y - 28);
	boxfill8(vram, x, COL8_FFFFFF,  0,     y - 27, x -  1, y - 27);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 26, x -  1, y -  1);

	boxfill8(vram, x, COL8_FFFFFF,  3,     y - 24, 59,     y - 24);
	boxfill8(vram, x, COL8_FFFFFF,  2,     y - 24,  2,     y -  4);
	boxfill8(vram, x, COL8_848484,  3,     y -  4, 59,     y -  4);
	boxfill8(vram, x, COL8_848484, 59,     y - 23, 59,     y -  5);
	boxfill8(vram, x, COL8_000000,  2,     y -  3, 59,     y -  3);
	boxfill8(vram, x, COL8_000000, 60,     y - 24, 60,     y -  3);

	boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);
	boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);
	boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);
	boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);
	return;
}

void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
	int i;
	char *p, d /* data */;
	for (i = 0; i < 16; i++) {
		p = vram + (y + i) * xsize + x;
		d = font[i];
		if ((d & 0x80) != 0) { p[0] = c; }
		if ((d & 0x40) != 0) { p[1] = c; }
		if ((d & 0x20) != 0) { p[2] = c; }
		if ((d & 0x10) != 0) { p[3] = c; }
		if ((d & 0x08) != 0) { p[4] = c; }
		if ((d & 0x04) != 0) { p[5] = c; }
		if ((d & 0x02) != 0) { p[6] = c; }
		if ((d & 0x01) != 0) { p[7] = c; }
	}
	return;
}

 

현황

- 22번 글에서는 부트 정보를 편리하게 접근할수 있도록 구조체와 화살표 연산자를 사용했다. 이제 메모리에 대한 내용들은 직접 적을 필요없이 포인터를 사용해서 접근하면 되겠지만, 이제 글자도 좀 써려고 한다.

 

 

바이오스로 문자쓰기?

- 어셈블리어로 바이오스에 입출력 요청해서 문자를 띄우면 될수도 있지만 이제 32비트 모드로 바이오스를 쓸수없음.

 

 

문자 쓰기?

- 8 x 16사각형 화소가 존재하면 문자를 어떻게 쓸가

 * 구글링해도 비슷한 사진도 없고 어떻게 보여줄까 고민 중 사진 찍으려다가 그냥 표로 그렸다. 아래의 상자는 8 x 16 화소로 문자 A를 표기한것

               
      000 000      
      000 000      
      000 000      
      000 000      
    000     000    
    000     000    
    000     000    
    000     000    
  000 000 000 000 000 000  
  000         000
 
  000         000  
  000         000  
  000         000  
000 000 000     000 000 000
               
               

- 위 표를 2진수로 나타내보고 16진수로 정리해보자

0 0 0 0 0 0 0 0   => 0x00

0 0 0 1 1 0 0 0   => 0x18

0 0 0 1 1 0 0 0   => 0x18

0 0 0 1 1 0 0 0   => 0x18

0 0 0 1 1 0 0 0   => 0x18

0 0 1 0 0 1 0 0   => 0x24

0 0 1 0 0 1 0 0   => 0x24

0 0 1 0 0 1 0 0   => 0x24

0 0 1 0 0 1 0 0   => 0x24

0 1 1 1 1 1 1 0   => 0x7e

0 1 0 0 0 0 1 0   => 0x42

0 1 0 0 0 0 1 0   => 0x42

0 1 0 0 0 0 1 0   => 0x42

1 1 1 0 0 1 1 1   => 0xe7

0 0 0 0 0 0 0 0   => 0x00

0 0 0 0 0 0 0 0   => 0x00

 

- 위 16진수 값들의 모음을 폰트 데이터가 된다.

static char font_A[16] = {
  0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,
  0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00
};

- 8화소 폰트를 그리기 위해서 위 배열을 16번 반복하면 되며, 이를 정리한 결과가 아래의 putfont8 함수가 된다.

void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
	int i;
	char *p, d /* data */;
	for (i = 0; i < 16; i++) {
		p = vram + (y + i) * xsize + x;
		d = font[i];
		if ((d & 0x80) != 0) { p[0] = c; }
		if ((d & 0x40) != 0) { p[1] = c; }
		if ((d & 0x20) != 0) { p[2] = c; }
		if ((d & 0x10) != 0) { p[3] = c; }
		if ((d & 0x08) != 0) { p[4] = c; }
		if ((d & 0x04) != 0) { p[5] = c; }
		if ((d & 0x02) != 0) { p[6] = c; }
		if ((d & 0x01) != 0) { p[7] = c; }
	}
	return;
}

 

 

폰트를 출력해보자

- 메인 함수를 보면 static char font_A[16] 으로 폰트 데이터를 초기화 해주고, putfont8을 보면 FFFFFFF 색상으로 (10,10)위치에 찍으라고 하는것 같다 실행해보면

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
	static char font_A[16] = {
		0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,
		0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00
	};

	init_palette();
	init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
	putfont8(binfo->vram, binfo->scrnx, 10, 10, COL8_FFFFFF, font_A);

	for (;;) {
		io_hlt();
	}
}

 

- 정상적으로 A가 출력됨

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen(char *vram, int x, int y);
void putfont8(char *vram, int xsize, int x, int y, char c, char *font);

#define COL8_000000		0
#define COL8_FF0000		1
#define COL8_00FF00		2
#define COL8_FFFF00		3
#define COL8_0000FF		4
#define COL8_FF00FF		5
#define COL8_00FFFF		6
#define COL8_FFFFFF		7
#define COL8_C6C6C6		8
#define COL8_840000		9
#define COL8_008400		10
#define COL8_848400		11
#define COL8_000084		12
#define COL8_840084		13
#define COL8_008484		14
#define COL8_848484		15

struct BOOTINFO {
	char cyls, leds, vmode, reserve;
	short scrnx, scrny;
	char *vram;
};

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
	extern char hankaku[4096];

	init_palette();
	init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
	putfont8(binfo->vram, binfo->scrnx,  8, 8, COL8_FFFFFF, hankaku + 'E' * 16);
	putfont8(binfo->vram, binfo->scrnx, 16, 8, COL8_FFFFFF, hankaku + '1' * 16);
	putfont8(binfo->vram, binfo->scrnx, 24, 8, COL8_FFFFFF, hankaku + 'D' * 16);
	putfont8(binfo->vram, binfo->scrnx, 32, 8, COL8_FFFFFF, hankaku + 'l' * 16);
	putfont8(binfo->vram, binfo->scrnx, 40, 8, COL8_FFFFFF, hankaku + 'L' * 16);
	putfont8(binfo->vram, binfo->scrnx, 48, 8, COL8_FFFFFF, hankaku + '1' * 16);
	putfont8(binfo->vram, binfo->scrnx, 56, 8, COL8_FFFFFF, hankaku + 'l' * 16);
	putfont8(binfo->vram, binfo->scrnx, 64, 8, COL8_FFFFFF, hankaku + 'E' * 16);
	putfont8(binfo->vram, binfo->scrnx, 72, 8, COL8_FFFFFF, hankaku + '1' * 16);
	putfont8(binfo->vram, binfo->scrnx, 80, 8, COL8_FFFFFF, hankaku + ' ' * 16);
	putfont8(binfo->vram, binfo->scrnx, 88, 8, COL8_FFFFFF, hankaku + 'O' * 16);
	putfont8(binfo->vram, binfo->scrnx, 96, 8, COL8_FFFFFF, hankaku + '1' * 16);
	putfont8(binfo->vram, binfo->scrnx, 104, 8, COL8_FFFFFF, hankaku + 'O' * 16);
	putfont8(binfo->vram, binfo->scrnx, 112, 8, COL8_FFFFFF, hankaku + 'F' * 16);
	putfont8(binfo->vram, binfo->scrnx, 120, 8, COL8_FFFFFF, hankaku + '7' * 16);
	putfont8(binfo->vram, binfo->scrnx, 128, 8, COL8_FFFFFF, hankaku + '1' * 16);

	for (;;) {
		io_hlt();
	}
}


void init_palette(void)
{
	static unsigned char table_rgb[16 * 3] = {
		0x00, 0x00, 0x00,	/*  0:검은색 */
		0xff, 0x00, 0x00,	/*  1:밝은 적색 */
		0x00, 0xff, 0x00,	/*  2:밝은 녹색 */
		0xff, 0xff, 0x00,	/*  3:밝은 노란색 */
		0x00, 0x00, 0xff,	/*  4:밝은 청색 */
		0xff, 0x00, 0xff,	/*  5:밝은 보라색 */
		0x00, 0xff, 0xff,	/*  6:밝은 청색*/
		0xff, 0xff, 0xff,	/*  7:흰색 */
		0xc6, 0xc6, 0xc6,	/*  8:밝은 회색 */
		0x84, 0x00, 0x00,	/*  9:어두운 적색 */
		0x00, 0x84, 0x00,	/* 10:어두운 녹색 */
		0x84, 0x84, 0x00,	/* 11:어두운 노란색 */
		0x00, 0x00, 0x84,	/* 12:군청색 */
		0x84, 0x00, 0x84,	/* 13:어두운 보라색 */
		0x00, 0x84, 0x84,	/* 14:어두운 청색 */
		0x84, 0x84, 0x84	/* 15:어두운 회색 */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char 명령은 (주소가 아니라)데이터밖에 쓰지 못하지만, DB명령과 동일함 */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();	/* 인터럽트 허가 플래그 값을 기록 */
	io_cli(); 					/* 허가 플래그를 0으로 하여 인터럽트 금지 */
	io_out8(0x03c8, start);
	for (i = start; i <= end; i++) {
		io_out8(0x03c9, rgb[0] / 4);
		io_out8(0x03c9, rgb[1] / 4);
		io_out8(0x03c9, rgb[2] / 4);
		rgb += 3;
	}
	io_store_eflags(eflags);	/* 인터럽트 허가 플래그를 본래 값으로 되돌린다. */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x, y;
	for (y = y0; y <= y1; y++) {
		for (x = x0; x <= x1; x++)
			vram[y * xsize + x] = c;
	}
	return;
}

void init_screen(char *vram, int x, int y)
{
	boxfill8(vram, x, COL8_008484,  0,     0,      x -  1, y - 29);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 28, x -  1, y - 28);
	boxfill8(vram, x, COL8_FFFFFF,  0,     y - 27, x -  1, y - 27);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 26, x -  1, y -  1);

	boxfill8(vram, x, COL8_FFFFFF,  3,     y - 24, 59,     y - 24);
	boxfill8(vram, x, COL8_FFFFFF,  2,     y - 24,  2,     y -  4);
	boxfill8(vram, x, COL8_848484,  3,     y -  4, 59,     y -  4);
	boxfill8(vram, x, COL8_848484, 59,     y - 23, 59,     y -  5);
	boxfill8(vram, x, COL8_000000,  2,     y -  3, 59,     y -  3);
	boxfill8(vram, x, COL8_000000, 60,     y - 24, 60,     y -  3);

	boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);
	boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);
	boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);
	boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);
	return;
}

void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
	int i;
	char *p, d /* data */;
	for (i = 0; i < 16; i++) {
		p = vram + (y + i) * xsize + x;
		d = font[i];
		if ((d & 0x80) != 0) { p[0] = c; }
		if ((d & 0x40) != 0) { p[1] = c; }
		if ((d & 0x20) != 0) { p[2] = c; }
		if ((d & 0x10) != 0) { p[3] = c; }
		if ((d & 0x08) != 0) { p[4] = c; }
		if ((d & 0x04) != 0) { p[5] = c; }
		if ((d & 0x02) != 0) { p[6] = c; }
		if ((d & 0x01) != 0) { p[7] = c; }
	}
	return;
}

여러 문자 출력하기

- A를 띄운건 좋지만 출력하고싶은 문자는 더 다양하다.

 알파벳은 26개의 소, 대문자, 숫자 10개, 기호 30개.. OSASK의 폰트 데이터 사용하자

 

 

OSASK 폰트 파일

- 아래는 OSASK 폰트 데이터 예시 * 어떻게 C언어에서 이걸쓸지는 Make 파일 참고

 

 

 

 

폰트파일 오브젝트 만들기

- 별도의 컴파일러(makefont.exe)로 오브젝트 파일로 만듬

 

 

메인함수

- 폰트 파일을 extern char hankaku[4096]으로 접근해서 사용

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
	extern char hankaku[4096];

	init_palette();
	init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
	putfont8(binfo->vram, binfo->scrnx,  8, 8, COL8_FFFFFF, hankaku + 'E' * 16);
	putfont8(binfo->vram, binfo->scrnx, 16, 8, COL8_FFFFFF, hankaku + '1' * 16);
	putfont8(binfo->vram, binfo->scrnx, 24, 8, COL8_FFFFFF, hankaku + 'D' * 16);
	putfont8(binfo->vram, binfo->scrnx, 32, 8, COL8_FFFFFF, hankaku + 'l' * 16);
	putfont8(binfo->vram, binfo->scrnx, 40, 8, COL8_FFFFFF, hankaku + 'L' * 16);
	putfont8(binfo->vram, binfo->scrnx, 48, 8, COL8_FFFFFF, hankaku + '1' * 16);
	putfont8(binfo->vram, binfo->scrnx, 56, 8, COL8_FFFFFF, hankaku + 'l' * 16);
	putfont8(binfo->vram, binfo->scrnx, 64, 8, COL8_FFFFFF, hankaku + 'E' * 16);
	putfont8(binfo->vram, binfo->scrnx, 72, 8, COL8_FFFFFF, hankaku + '1' * 16);
	putfont8(binfo->vram, binfo->scrnx, 80, 8, COL8_FFFFFF, hankaku + ' ' * 16);
	putfont8(binfo->vram, binfo->scrnx, 88, 8, COL8_FFFFFF, hankaku + 'O' * 16);
	putfont8(binfo->vram, binfo->scrnx, 96, 8, COL8_FFFFFF, hankaku + '1' * 16);
	putfont8(binfo->vram, binfo->scrnx, 104, 8, COL8_FFFFFF, hankaku + 'O' * 16);
	putfont8(binfo->vram, binfo->scrnx, 112, 8, COL8_FFFFFF, hankaku + 'F' * 16);
	putfont8(binfo->vram, binfo->scrnx, 120, 8, COL8_FFFFFF, hankaku + '7' * 16);
	putfont8(binfo->vram, binfo->scrnx, 128, 8, COL8_FFFFFF, hankaku + '1' * 16);

	for (;;) {
		io_hlt();
	}
}

 

 

실행 결과

- 글자는 좀 잘못썻지만 잘 나온다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

함수로 문자열 쓰기

- 방금전 예제에선 putfont8로 문자 하나하나 출력하다보니 힘들었다.

- 이를 함수로 구현해서 다뤄보자.

void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
	extern char hankaku[4096];
	for (; *s != 0x00; s++) {
		putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
		x += 8;
	}
	return;
}

 

 

메인 함수

- 위에서 정의한 putfont8_asc 함수로 지정한 위치에 차례 차례 작성해준다.

 *(31, 31)과 (30, 30)을 함으로서 폰트상 굵게 효과를 준것 같다.

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;

	init_palette();
	init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
	putfonts8_asc(binfo->vram, binfo->scrnx,  8,  8, COL8_FFFFFF, "Hello BABOYA");
	putfonts8_asc(binfo->vram, binfo->scrnx, 31, 31, COL8_000000, "E1DlL1lOlE1 OlOF7l.");
	putfonts8_asc(binfo->vram, binfo->scrnx, 30, 30, COL8_000000, "E1DlL1lOlE1 OlOF7l.");
	putfonts8_asc(binfo->vram, binfo->scrnx, 30, 130, COL8_FFFFFF, "Computer");
	putfonts8_asc(binfo->vram, binfo->scrnx, 60, 150, COL8_FFFFFF, "annyohasseyo?");
	putfonts8_asc(binfo->vram, binfo->scrnx, 50,  70, COL8_FFFFFF, "Hello BABOYA");
	putfonts8_asc(binfo->vram, binfo->scrnx, 90,  75, COL8_FFFFFF, "Hello BABOYA");
	putfonts8_asc(binfo->vram, binfo->scrnx, 55,  60, COL8_FFFFFF, "Hello BABOYA");

	for (;;) {
		io_hlt();
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

300x250
728x90
void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen(char *vram, int x, int y);

#define COL8_000000		0
#define COL8_FF0000		1
#define COL8_00FF00		2
#define COL8_FFFF00		3
#define COL8_0000FF		4
#define COL8_FF00FF		5
#define COL8_00FFFF		6
#define COL8_FFFFFF		7
#define COL8_C6C6C6		8
#define COL8_840000		9
#define COL8_008400		10
#define COL8_848400		11
#define COL8_000084		12
#define COL8_840084		13
#define COL8_008484		14
#define COL8_848484		15

void HariMain(void)
{
	char *vram;
	int xsize, ysize;
	short *binfo_scrnx, *binfo_scrny;
	int *binfo_vram;

	init_palette();
	binfo_scrnx = (short *) 0x0ff4;
	binfo_scrny = (short *) 0x0ff6;
	binfo_vram = (int *) 0x0ff8;
	xsize = *binfo_scrnx;
	ysize = *binfo_scrny;
	vram = (char *) *binfo_vram;

	init_screen(vram, xsize, ysize);

	for (;;) {
		io_hlt();
	}
}



void init_palette(void)
{
	static unsigned char table_rgb[16 * 3] = {
		0x00, 0x00, 0x00,	/*  0:검은색 */
		0xff, 0x00, 0x00,	/*  1:밝은 적색 */
		0x00, 0xff, 0x00,	/*  2:밝은 녹색 */
		0xff, 0xff, 0x00,	/*  3:밝은 노란색 */
		0x00, 0x00, 0xff,	/*  4:밝은 청색 */
		0xff, 0x00, 0xff,	/*  5:밝은 보라색 */
		0x00, 0xff, 0xff,	/*  6:밝은 청색*/
		0xff, 0xff, 0xff,	/*  7:흰색 */
		0xc6, 0xc6, 0xc6,	/*  8:밝은 회색 */
		0x84, 0x00, 0x00,	/*  9:어두운 적색 */
		0x00, 0x84, 0x00,	/* 10:어두운 녹색 */
		0x84, 0x84, 0x00,	/* 11:어두운 노란색 */
		0x00, 0x00, 0x84,	/* 12:군청색 */
		0x84, 0x00, 0x84,	/* 13:어두운 보라색 */
		0x00, 0x84, 0x84,	/* 14:어두운 청색 */
		0x84, 0x84, 0x84	/* 15:어두운 회색 */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char 명령은 (주소가 아니라)데이터밖에 쓰지 못하지만, DB명령과 동일함 */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();	/* 인터럽트 허가 플래그 값을 기록 */
	io_cli(); 					/* 허가 플래그를 0으로 하여 인터럽트 금지 */
	io_out8(0x03c8, start);
	for (i = start; i <= end; i++) {
		io_out8(0x03c9, rgb[0] / 4);
		io_out8(0x03c9, rgb[1] / 4);
		io_out8(0x03c9, rgb[2] / 4);
		rgb += 3;
	}
	io_store_eflags(eflags);	/* 인터럽트 허가 플래그를 본래 값으로 되돌린다. */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x, y;
	for (y = y0; y <= y1; y++) {
		for (x = x0; x <= x1; x++)
			vram[y * xsize + x] = c;
	}
	return;
}

void init_screen(char *vram, int x, int y)
{
	boxfill8(vram, x, COL8_008484,  0,     0,      x -  1, y - 29);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 28, x -  1, y - 28);
	boxfill8(vram, x, COL8_FFFFFF,  0,     y - 27, x -  1, y - 27);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 26, x -  1, y -  1);

	boxfill8(vram, x, COL8_FFFFFF,  3,     y - 24, 59,     y - 24);
	boxfill8(vram, x, COL8_FFFFFF,  2,     y - 24,  2,     y -  4);
	boxfill8(vram, x, COL8_848484,  3,     y -  4, 59,     y -  4);
	boxfill8(vram, x, COL8_848484, 59,     y - 23, 59,     y -  5);
	boxfill8(vram, x, COL8_000000,  2,     y -  3, 59,     y -  3);
	boxfill8(vram, x, COL8_000000, 60,     y - 24, 60,     y -  3);

	boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);
	boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);
	boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);
	boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);
	return;
}

현황

- 지난시간에는 VRAM에 직접 접근해서 다양한 색상의 상자와 이로 테스크마 그리기 까지 했다. 오늘은 부팅 정보들을 가져오는 것 부터 시작하자

 

 

문제

- 지난번 예제에서 작성한 vram의 위치나, x해상도 크기, y해상도 크기는 변수로서 지정해주는게 아니라 원래 어셈블리어에서 가져와야 한다.

 

부팅정보 포인터로 접근하기

- asmhead.nas에서 지정한 주소값을 이용하여 부트 정보 binfo 와 스크린 정보 scrn를 가져오자

 * 아래의 사진은 asmhead.nas에서 x 해상도 주소, y해상도 주소, vram의 주소를 알려주고있다.

 

- 포인터를 이용해서 해당 주소들에 접근하자

void HariMain(void)
{
	char *vram;
	int xsize, ysize;
	short *binfo_scrnx, *binfo_scrny;
	int *binfo_vram;

	init_palette();
	binfo_scrnx = (short *) 0x0ff4;
	binfo_scrny = (short *) 0x0ff6;
	binfo_vram = (int *) 0x0ff8;
	xsize = *binfo_scrnx;
	ysize = *binfo_scrny;
	vram = (char *) *binfo_vram;

	init_screen(vram, xsize, ysize);

	for (;;) {
		io_hlt();
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen(char *vram, int x, int y);

#define COL8_000000		0
#define COL8_FF0000		1
#define COL8_00FF00		2
#define COL8_FFFF00		3
#define COL8_0000FF		4
#define COL8_FF00FF		5
#define COL8_00FFFF		6
#define COL8_FFFFFF		7
#define COL8_C6C6C6		8
#define COL8_840000		9
#define COL8_008400		10
#define COL8_848400		11
#define COL8_000084		12
#define COL8_840084		13
#define COL8_008484		14
#define COL8_848484		15

struct BOOTINFO {
	char cyls, leds, vmode, reserve;
	short scrnx, scrny;
	char *vram;
};

void HariMain(void)
{
	char *vram;
	int xsize, ysize;
	struct BOOTINFO *binfo;

	init_palette();
	binfo = (struct BOOTINFO *) 0x0ff0;
	xsize = (*binfo).scrnx;
	ysize = (*binfo).scrny;
	vram = (*binfo).vram;

	init_screen(vram, xsize, ysize);

	for (;;) {
		io_hlt();
	}
}


void init_palette(void)
{
	static unsigned char table_rgb[16 * 3] = {
		0x00, 0x00, 0x00,	/*  0:검은색 */
		0xff, 0x00, 0x00,	/*  1:밝은 적색 */
		0x00, 0xff, 0x00,	/*  2:밝은 녹색 */
		0xff, 0xff, 0x00,	/*  3:밝은 노란색 */
		0x00, 0x00, 0xff,	/*  4:밝은 청색 */
		0xff, 0x00, 0xff,	/*  5:밝은 보라색 */
		0x00, 0xff, 0xff,	/*  6:밝은 청색*/
		0xff, 0xff, 0xff,	/*  7:흰색 */
		0xc6, 0xc6, 0xc6,	/*  8:밝은 회색 */
		0x84, 0x00, 0x00,	/*  9:어두운 적색 */
		0x00, 0x84, 0x00,	/* 10:어두운 녹색 */
		0x84, 0x84, 0x00,	/* 11:어두운 노란색 */
		0x00, 0x00, 0x84,	/* 12:군청색 */
		0x84, 0x00, 0x84,	/* 13:어두운 보라색 */
		0x00, 0x84, 0x84,	/* 14:어두운 청색 */
		0x84, 0x84, 0x84	/* 15:어두운 회색 */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char 명령은 (주소가 아니라)데이터밖에 쓰지 못하지만, DB명령과 동일함 */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();	/* 인터럽트 허가 플래그 값을 기록 */
	io_cli(); 					/* 허가 플래그를 0으로 하여 인터럽트 금지 */
	io_out8(0x03c8, start);
	for (i = start; i <= end; i++) {
		io_out8(0x03c9, rgb[0] / 4);
		io_out8(0x03c9, rgb[1] / 4);
		io_out8(0x03c9, rgb[2] / 4);
		rgb += 3;
	}
	io_store_eflags(eflags);	/* 인터럽트 허가 플래그를 본래 값으로 되돌린다. */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x, y;
	for (y = y0; y <= y1; y++) {
		for (x = x0; x <= x1; x++)
			vram[y * xsize + x] = c;
	}
	return;
}

void init_screen(char *vram, int x, int y)
{
	boxfill8(vram, x, COL8_008484,  0,     0,      x -  1, y - 29);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 28, x -  1, y - 28);
	boxfill8(vram, x, COL8_FFFFFF,  0,     y - 27, x -  1, y - 27);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 26, x -  1, y -  1);

	boxfill8(vram, x, COL8_FFFFFF,  3,     y - 24, 59,     y - 24);
	boxfill8(vram, x, COL8_FFFFFF,  2,     y - 24,  2,     y -  4);
	boxfill8(vram, x, COL8_848484,  3,     y -  4, 59,     y -  4);
	boxfill8(vram, x, COL8_848484, 59,     y - 23, 59,     y -  5);
	boxfill8(vram, x, COL8_000000,  2,     y -  3, 59,     y -  3);
	boxfill8(vram, x, COL8_000000, 60,     y - 24, 60,     y -  3);

	boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);
	boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);
	boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);
	boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);
	return;
}

 

문제

- 기존의 값을 이제 포인터로 접근하는건 좋다. 근데 부트 인포와 관련된 값들이 많은데 개별적으로 접근하기 너무 불편하다. 구조체를 정의해서 부트 정보를 다루어보자

 * 부트 정보에 대한 개별적인 포인터로 접근해서 다루기는 불편함

 

구조체 사용하기

- 부트 정보에 대한 구조체를 다음과 같이 정의하고, 구조체의 주소를 이용해서 내부의 다른 변수들에 접근하자.

struct BOOTINFO {
	char cyls, leds, vmode, reserve;
	short scrnx, scrny;
	char *vram;
};

- 이전 방식처럼 포인터로 xsize, ysize, vram에 직접 담아줫던것보다. 부트 정보 구조체 포인터 binfo로 담아주니 편리

void HariMain(void)
{
	char *vram;
	int xsize, ysize;
	struct BOOTINFO *binfo;

	init_palette();
	binfo = (struct BOOTINFO *) 0x0ff0;
	xsize = (*binfo).scrnx;
	ysize = (*binfo).scrny;
	vram = (*binfo).vram;

	init_screen(vram, xsize, ysize);

	for (;;) {
		io_hlt();
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen(char *vram, int x, int y);

#define COL8_000000		0
#define COL8_FF0000		1
#define COL8_00FF00		2
#define COL8_FFFF00		3
#define COL8_0000FF		4
#define COL8_FF00FF		5
#define COL8_00FFFF		6
#define COL8_FFFFFF		7
#define COL8_C6C6C6		8
#define COL8_840000		9
#define COL8_008400		10
#define COL8_848400		11
#define COL8_000084		12
#define COL8_840084		13
#define COL8_008484		14
#define COL8_848484		15

struct BOOTINFO {
	char cyls, leds, vmode, reserve;
	short scrnx, scrny;
	char *vram;
};

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;

	init_palette();
	init_screen(binfo->vram, binfo->scrnx, binfo->scrny);

	for (;;) {
		io_hlt();
	}
}

void init_palette(void)
{
	static unsigned char table_rgb[16 * 3] = {
		0x00, 0x00, 0x00,	/*  0:검은색 */
		0xff, 0x00, 0x00,	/*  1:밝은 적색 */
		0x00, 0xff, 0x00,	/*  2:밝은 녹색 */
		0xff, 0xff, 0x00,	/*  3:밝은 노란색 */
		0x00, 0x00, 0xff,	/*  4:밝은 청색 */
		0xff, 0x00, 0xff,	/*  5:밝은 보라색 */
		0x00, 0xff, 0xff,	/*  6:밝은 청색*/
		0xff, 0xff, 0xff,	/*  7:흰색 */
		0xc6, 0xc6, 0xc6,	/*  8:밝은 회색 */
		0x84, 0x00, 0x00,	/*  9:어두운 적색 */
		0x00, 0x84, 0x00,	/* 10:어두운 녹색 */
		0x84, 0x84, 0x00,	/* 11:어두운 노란색 */
		0x00, 0x00, 0x84,	/* 12:군청색 */
		0x84, 0x00, 0x84,	/* 13:어두운 보라색 */
		0x00, 0x84, 0x84,	/* 14:어두운 청색 */
		0x84, 0x84, 0x84	/* 15:어두운 회색 */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char 명령은 (주소가 아니라)데이터밖에 쓰지 못하지만, DB명령과 동일함 */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();	/* 인터럽트 허가 플래그 값을 기록 */
	io_cli(); 					/* 허가 플래그를 0으로 하여 인터럽트 금지 */
	io_out8(0x03c8, start);
	for (i = start; i <= end; i++) {
		io_out8(0x03c9, rgb[0] / 4);
		io_out8(0x03c9, rgb[1] / 4);
		io_out8(0x03c9, rgb[2] / 4);
		rgb += 3;
	}
	io_store_eflags(eflags);	/* 인터럽트 허가 플래그를 본래 값으로 되돌린다. */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x, y;
	for (y = y0; y <= y1; y++) {
		for (x = x0; x <= x1; x++)
			vram[y * xsize + x] = c;
	}
	return;
}

void init_screen(char *vram, int x, int y)
{
	boxfill8(vram, x, COL8_008484,  0,     0,      x -  1, y - 29);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 28, x -  1, y - 28);
	boxfill8(vram, x, COL8_FFFFFF,  0,     y - 27, x -  1, y - 27);
	boxfill8(vram, x, COL8_C6C6C6,  0,     y - 26, x -  1, y -  1);

	boxfill8(vram, x, COL8_FFFFFF,  3,     y - 24, 59,     y - 24);
	boxfill8(vram, x, COL8_FFFFFF,  2,     y - 24,  2,     y -  4);
	boxfill8(vram, x, COL8_848484,  3,     y -  4, 59,     y -  4);
	boxfill8(vram, x, COL8_848484, 59,     y - 23, 59,     y -  5);
	boxfill8(vram, x, COL8_000000,  2,     y -  3, 59,     y -  3);
	boxfill8(vram, x, COL8_000000, 60,     y - 24, 60,     y -  3);

	boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);
	boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);
	boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);
	boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);
	return;
}

 

화살표 연산자 사용하기

- 구조체를 선언해서 부팅 정보 다루는것 까진 다 좋다.

 그런데 (*binfo).scrnx, (*binfo).scrny 와같이 일일히 값을 접근 하긴 불편하니 화살표 연산자 사용하자

 

haribo02b

- 화살표 연산자를 사용하면 원하는 값 편하게 가져오고, 아래와 같이 init_screen의 매개변수로 바로 전달할수 있다.

 

haribo02c

 

 

 

 

 

 

 

300x250
728x90

OS를 만들 때 잘 사용하는 BIOS 함수(AT 호환기종)

OS의 개발이 32 Bit 단계에 다다르면 사용할 수 없는 BIOS 함수들이기는 하지만, 부트 섹터나 OS 개발의 초반까지는 다음 BIOS 함수를 많이 사용하게 된다.

INT(0x10) : 비디오 관련

  • 비디오 모드 설정
    • AH = 0x00
    • AL = 모드(자주 사용되는 화면 모드만 설명)
      • 0x03:16색 텍스트, 80x25
      • 0x12:VGA 그래픽스, 640 x480x4bit 칼라
      • 0x13:VGA 그래픽스, 320 x200x8bit 칼라, Packed Pixel
      • 0x6a:확장 VGA 그래픽스, 800 x600x4bit 칼라
    • 반환값:없음

 

  • 커서 모양 설정
    • AH = 0x01
    • CH = 시작 라인
    • CL = 종료 라인
      • CH < CL라면 1개의 부분으로부터 되는 보통 커서
      • CH > CL라면 2개의 부분으로부터 되는 커서
      • CH == 0x20이면 커서는 표시되지 않는다
    • 반환값:없음

 

  • 커서 위치 지정
    • AH = 0x02
    • BH = 0(페이지 번호)
    • DL = x 좌표
    • DH = y 좌표
    • 반환값:없음

 

  • 점 출력
    • 굳이 이 함수를 이용하지 않고 바로 Video 메모리를 이용할 수도 있다.
    • AH = 0x0c
    • AL = 색상 코드(0 ~ 15)
    • CX = x좌표
    • DX = y좌표
    • 반환값:없음

 

  • 한 문자 출력
    • AH = 0x0e
    • AL = 문자 코드
    • BH = 0(페이지 번호)
    • BL = 문자의 색
    • 반환값:없음
    • 주의) beep(0x07), 백 스페이스(0x08), CR(0x0d), LF(0x0a)는 제어 코드로서 인식된다

 

  • 색상 코드를 대응되는 팔레트에 저장한다.
    • 16색 모드일 때만 사용가능하다.
    • AX = 0x1000
    • BL = 색상 코드(0 ~ 15)
    • BH = 팔레트 코드(0 ~ 63)
    • 주의) EGA 그래픽 카드와의 호환성을 유지하기 위해서 사용됩니다. 잘못 사용하면 상당히 복잡해지기 때문에 기본값 그대로 두고 사용하는 것이 좋습니다.

 

  • 팔레트 설정
    • AX = 0x1010
    • BX = 팔레트 번호(0 ~ 255)
    • DH = Red(0 ~ 63)
    • CH = Green(0 ~ 63)
    • CL = Blue(0 ~ 63)
    • 반환값:없음

 

  • 문자열 출력
    • AH = 0x13
    • AL = 옵션
      • 0x00:문자열의 속성을 BL 레지스터로 지정하고 커서는 이동시키지 않는다.
      • 0x01:문자열의 속성을 BL 레지스터로 지정하고 커서를 이동시킨다.
      • 0x02:문자열을 출력하고 커서는 이동시키지 않는다.
      • 0x03:문자열을 출력하고 커서를 이동시킨다.
      • 실제 데이터는 메모리에 [문자 코드] [칼라 코드] [문자 코드] [칼라 코드]와 같이 저장된다고 보면된다.
    • BH = 0(페이지 번호)
    • BL = 칼라 코드(AL 레지스터의 값이 0x01, 0x02일 경우에만 적용)
    • CX = 문자열의 길이
    • DL = x좌표
    • DH = y좌표
    • ES:BP = 출력할 문자열이 있는 곳의 주소
    • 반환값:없음

 

  • 제일 간단하게 사용할 수 있는 화면모드인 0x13의 사용법
    • 0x13번 화면모드는 그다지 해상도가 좋지는 않지만 Packed Pixel 모드이기 때문에 프로그래밍 하기가 편합니다. 우선 화면 모드를 변경하고 팔레트를 설정합니다.
    • 이 모드는 Video Ram의 0xa0000 ~ 0xafff의 64KB에 위치하게 됩니다. 정확히 말하면 320 x 200 = 64000이 되므로 62.5 KB라고 해야겠지만, VRAM는 0xa0000 ~ 0xaffff의 64 KB입니다.엄밀하게 말하면(자), 320 x200=64000이므로, 62.5 KB가 됩니다. 이 모드에서는 점 하나가 1바이트에 해당되기때문에 읽고 쓰기도 아주 간단합니다.

 

INT(0x11) : H/W 구성 확인

  • 컴퓨터에 설치된 H/W의 리스트를 확인한다.
    • 입력값 없음
    • 반환값:
    • AH == 장치 코드
      • 사용하는 사람이 거의 없기때문에 정확한 내용은 생략하겠습니다.

 

INT(0x12) : 시스템에 있는 메모리의 크기를 조사

  • 시스템에 있는 메모리의 크기를 조사
    • 입력값 없음
    • 반환값:
    • AX == 메모리의 크기(KB 단위)

INT(0x13) : 디스크 관련 함수

  • 디스크 시스템 리셋
    • AH = 0x00
    • DL = 드라이브 번호(0x00 ~ 0x7f:FDD, 0x80 ~ 0xff:HDD)
    • 반환값:
    • FLAGS.CF == 0 : 에러 없음
    • FLAGS.CF == 1 : 에러 발생, AH에 에러 코드가 저장된다.
      • 에러 코드 (FDD, HDD 모두 발생 가능한 에러 코드)
      • 0x01:디스크 Parameter가 잘못 전달되었다.
      • 0x02:Address Mark가 발견되지 않음
      • 0x04:섹터를 찾지 못했다
      • 0x09:DMA 오버플로우
      • 0x10:데이터 에러
      • 0x20:콘트롤러 이상
      • 0x40:탐색 실패(Seek Failure)
      • 0x80:타임 아웃
      • 에러 코드 (FDD일 경우의 에러코드)
      • 0x03:쓰기 금지된 디스크에 쓰기를 시도
      • 0x06:디스크를 찾지 못함
      • 0x08:DMA 오버플로우
      • 에러 코드 (HDD일 경우의 에러코드)
      • 0x05:리셋 실패
      • 0x07:Parameter 테이블이 정확하지 않음
      • 0x0a:섹터 Flag이 잘못되었음
      • 0x11:ECC 데이터 에러
      • 0xaa:드라이브가 아직 준비되지 않음
      • 0xbb:미정의 에러
      • 0xcc:쓰기 에러
      • 0xe0:상태(Status) 에러

Retrieved from "http://osguru.net/index.php/AT-BIOS"

[출처] [펌] OS를 만들 때 잘 사용하는 BIOS 함수(AT 호환기종)

 

 

osguru.net는 들어갈수 없어 다른 분이 펌해온걸 다시가져옴

300x250
728x90
void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);

#define COL8_000000		0
#define COL8_FF0000		1
#define COL8_00FF00		2
#define COL8_FFFF00		3
#define COL8_0000FF		4
#define COL8_FF00FF		5
#define COL8_00FFFF		6
#define COL8_FFFFFF		7
#define COL8_C6C6C6		8
#define COL8_840000		9
#define COL8_008400		10
#define COL8_848400		11
#define COL8_000084		12
#define COL8_840084		13
#define COL8_008484		14
#define COL8_848484		15

void HariMain(void)
{
	char *p;

	init_palette();

	p = (char *) 0xa0000;

	boxfill8(p, 320, COL8_FF0000,  20,  20, 120, 120);
	boxfill8(p, 320, COL8_00FF00,  70,  50, 170, 150);
	boxfill8(p, 320, COL8_0000FF, 120,  80, 220, 180);

	for (;;) {
		io_hlt();
	}
}

void init_palette(void)
{
	static unsigned char table_rgb[16 * 3] = {
		0x00, 0x00, 0x00,	/*  0:검은색 */
		0xff, 0x00, 0x00,	/*  1:밝은 적색 */
		0x00, 0xff, 0x00,	/*  2:밝은 녹색 */
		0xff, 0xff, 0x00,	/*  3:밝은 노란색 */
		0x00, 0x00, 0xff,	/*  4:밝은 청색 */
		0xff, 0x00, 0xff,	/*  5:밝은 보라색 */
		0x00, 0xff, 0xff,	/*  6:밝은 청색*/
		0xff, 0xff, 0xff,	/*  7:흰색 */
		0xc6, 0xc6, 0xc6,	/*  8:밝은 회색 */
		0x84, 0x00, 0x00,	/*  9:어두운 적색 */
		0x00, 0x84, 0x00,	/* 10:어두운 녹색 */
		0x84, 0x84, 0x00,	/* 11:어두운 노란색 */
		0x00, 0x00, 0x84,	/* 12:군청색 */
		0x84, 0x00, 0x84,	/* 13:어두운 보라색 */
		0x00, 0x84, 0x84,	/* 14:어두운 청색 */
		0x84, 0x84, 0x84	/* 15:어두운 회색 */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char 명령은 (주소가 아니라)데이터밖에 쓰지 못하지만, DB명령과 동일함 */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();	/* 인터럽트 허가 플래그 값을 기록 */
	io_cli(); 					/* 허가 플래그를 0으로 하여 인터럽트 금지 */
	io_out8(0x03c8, start);
	for (i = start; i <= end; i++) {
		io_out8(0x03c9, rgb[0] / 4);
		io_out8(0x03c9, rgb[1] / 4);
		io_out8(0x03c9, rgb[2] / 4);
		rgb += 3;
	}
	io_store_eflags(eflags);	/* 인터럽트 허가 플래그를 본래 값으로 되돌린다. */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x, y;
	for (y = y0; y <= y1; y++) {
		for (x = x0; x <= x1; x++)
			vram[y * xsize + x] = c;
	}
	return;
}

 

 

현재 그래픽 모드

- 320 x 200 ( =64,000)개 화소 존재

- (y,x) 픽셀의 VRAM 번지 : 0xa000 + x + y * 320 번지

 * 

- 해당 메모리에 올바른 색 번호를 저장하면 화면상 그위치에 지정한 색이 나오게 됨.

 

boxfill8 함수

- 위와 같은 방식으로 시작점 (x0, y0), 끝점 (x1, y1)사이에 색 번호를 채워넣는 함수

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x, y;
	for (y = y0; y <= y1; y++) {
		for (x = x0; x <= x1; x++)
			vram[y * xsize + x] = c;
	}
	return;
}

 

 

상자 3개 그리기

- HariMain에서 x의 크기가 320인 화면 모드에서 빨강색을 (20, 20) ~ (120, 120),

   파란색, 녹색을 해당 위치에 그리도록 정리

* 어느 색이 색번호에 해당하는지 구분하기 위해서 #define으로 정의

void HariMain(void)
{
	char *p;

	init_palette();

	p = (char *) 0xa0000;

	boxfill8(p, 320, COL8_FF0000,  20,  20, 120, 120);
	boxfill8(p, 320, COL8_00FF00,  70,  50, 170, 150);
	boxfill8(p, 320, COL8_0000FF, 120,  80, 220, 180);

	for (;;) {
		io_hlt();
	}
}

 

 

 

 

 

 

 

 

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);

#define COL8_000000		0
#define COL8_FF0000		1
#define COL8_00FF00		2
#define COL8_FFFF00		3
#define COL8_0000FF		4
#define COL8_FF00FF		5
#define COL8_00FFFF		6
#define COL8_FFFFFF		7
#define COL8_C6C6C6		8
#define COL8_840000		9
#define COL8_008400		10
#define COL8_848400		11
#define COL8_000084		12
#define COL8_840084		13
#define COL8_008484		14
#define COL8_848484		15

void HariMain(void)
{
	char *vram;
	int xsize, ysize;

	init_palette();
	vram = (char *) 0xa0000;
	xsize = 320;
	ysize = 200;

	boxfill8(vram, xsize, COL8_008484,  0,         0,          xsize -  1, ysize - 29);
	boxfill8(vram, xsize, COL8_C6C6C6,  0,         ysize - 28, xsize -  1, ysize - 28);
	boxfill8(vram, xsize, COL8_FFFFFF,  0,         ysize - 27, xsize -  1, ysize - 27);
	boxfill8(vram, xsize, COL8_C6C6C6,  0,         ysize - 26, xsize -  1, ysize -  1);

	boxfill8(vram, xsize, COL8_FFFFFF,  3,         ysize - 24, 59,         ysize - 24);
	boxfill8(vram, xsize, COL8_FFFFFF,  2,         ysize - 24,  2,         ysize -  4);
	boxfill8(vram, xsize, COL8_848484,  3,         ysize -  4, 59,         ysize -  4);
	boxfill8(vram, xsize, COL8_848484, 59,         ysize - 23, 59,         ysize -  5);
	boxfill8(vram, xsize, COL8_000000,  2,         ysize -  3, 59,         ysize -  3);
	boxfill8(vram, xsize, COL8_000000, 60,         ysize - 24, 60,         ysize -  3);

	boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 24, xsize -  4, ysize - 24);
	boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 23, xsize - 47, ysize -  4);
	boxfill8(vram, xsize, COL8_FFFFFF, xsize - 47, ysize -  3, xsize -  4, ysize -  3);
	boxfill8(vram, xsize, COL8_FFFFFF, xsize -  3, ysize - 24, xsize -  3, ysize -  3);

	for (;;) {
		io_hlt();
	}
}


void init_palette(void)
{
	static unsigned char table_rgb[16 * 3] = {
		0x00, 0x00, 0x00,	/*  0:검은색 */
		0xff, 0x00, 0x00,	/*  1:밝은 적색 */
		0x00, 0xff, 0x00,	/*  2:밝은 녹색 */
		0xff, 0xff, 0x00,	/*  3:밝은 노란색 */
		0x00, 0x00, 0xff,	/*  4:밝은 청색 */
		0xff, 0x00, 0xff,	/*  5:밝은 보라색 */
		0x00, 0xff, 0xff,	/*  6:밝은 청색*/
		0xff, 0xff, 0xff,	/*  7:흰색 */
		0xc6, 0xc6, 0xc6,	/*  8:밝은 회색 */
		0x84, 0x00, 0x00,	/*  9:어두운 적색 */
		0x00, 0x84, 0x00,	/* 10:어두운 녹색 */
		0x84, 0x84, 0x00,	/* 11:어두운 노란색 */
		0x00, 0x00, 0x84,	/* 12:군청색 */
		0x84, 0x00, 0x84,	/* 13:어두운 보라색 */
		0x00, 0x84, 0x84,	/* 14:어두운 청색 */
		0x84, 0x84, 0x84	/* 15:어두운 회색 */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char 명령은 (주소가 아니라)데이터밖에 쓰지 못하지만, DB명령과 동일함 */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();	/* 인터럽트 허가 플래그 값을 기록 */
	io_cli(); 					/* 허가 플래그를 0으로 하여 인터럽트 금지 */
	io_out8(0x03c8, start);
	for (i = start; i <= end; i++) {
		io_out8(0x03c9, rgb[0] / 4);
		io_out8(0x03c9, rgb[1] / 4);
		io_out8(0x03c9, rgb[2] / 4);
		rgb += 3;
	}
	io_store_eflags(eflags);	/* 인터럽트 허가 플래그를 본래 값으로 되돌린다. */
	return;
}

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
	int x, y;
	for (y = y0; y <= y1; y++) {
		for (x = x0; x <= x1; x++)
			vram[y * xsize + x] = c;
	}
	return;
}

태스크바 만들기

- boxfill로 다음과같이 태스크바 그림 => 옛날 윈도우 화면같은 창이 만들어졌다.

- 부트섹터를 제외한 운영체제 파일 haribote.sys는 1,216 바이트 밖에 되지 않는다.

 

 

300x250
728x90

현황

- 방금전 C언어에서 직접 메모리 번지를 지정하는 방법이 없다고 하여 write_mem8 함수를 직접 어셈블리어로 구현

- 하지만 포인터를 이용하면 메모리 접근 가능

 

 

 

문제

- 어셈블리어의 문제 : 데이터가 byte인지 word인지, dword인지 알수 없음.

- c언어에서는 공간의 크기에 따라 다양한 자료형 제공

 ex. char 1바이트, short 2바이트, int 4바이트

 

 

 

 

포인터로 메모리 접근하자

- write_mem8로 i번째 주소에 접근했다면

- 지금은 포인터 변수 p에다가 주소를 담아 접근해서 사용

=> naskfunc.nas에서 wirte_mem8은 필요없으므로 지우기

 

 

 

 

색번호 설정하기

- 이제 포인터로 VRAM에 접근해서 값을 읽고 쓸수 있다. 하지만 단색은 재미없으니 색상도 바꿔보자

- 8비트 컬러 모드에서 0~255색을 표현 가능 -> 색번호 지정하는 구조를 팔레트라 부름

- 우리가 사용할 OS에서 16색만 있으면 충분할것같으니 다음과 같이 색들을 지정해주자

#000000 : 검은색 #00ffff : 밝은 청색 #000084 : 군청색
#ff0000 : 밝은 적색 #ffffff : 흰색 #840084 : 어두운 보라색
#00ff00 : 밝은 녹색 #c6c6c6 : 밝은 회색 #008484 : 어두운 청색
#ffff00 : 밝은 노란색 #840000 : 어두운 적색 #848484 : 어두운 회색
#0000ff : 밝은 청색 #008400 : 어두운 녹색  
#ff00ff : 밝은 보라색 #848400 : 어두운 노란색  

 

c언어 코드

- 맨앞의 함수들은 어셈블리어에서 정의함

void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);

/* 같은 소스 내에 있어도 C언어에서 사용하기 위해선 우선 선언 필요 */

void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);

void HariMain(void)
{
	int i;
	char *p;

	init_palette(); /*팔래트 초기화 */

	p = (char *) 0xa0000; /* 번지대입 */

	for (i = 0; i <= 0xffff; i++) {
		p[i] = i & 0x0f;
	}

	for (;;) {
		io_hlt();
	}
}

void init_palette(void)
{
	static unsigned char table_rgb[16 * 3] = {
		0x00, 0x00, 0x00,	/*  0:검은색 */
		0xff, 0x00, 0x00,	/*  1:밝은 적색 */
		0x00, 0xff, 0x00,	/*  2:밝은 녹색 */
		0xff, 0xff, 0x00,	/*  3:밝은 노란색 */
		0x00, 0x00, 0xff,	/*  4:밝은 청색 */
		0xff, 0x00, 0xff,	/*  5:밝은 보라색 */
		0x00, 0xff, 0xff,	/*  6:밝은 청색*/
		0xff, 0xff, 0xff,	/*  7:흰색 */
		0xc6, 0xc6, 0xc6,	/*  8:밝은 회색 */
		0x84, 0x00, 0x00,	/*  9:어두운 적색 */
		0x00, 0x84, 0x00,	/* 10:어두운 녹색 */
		0x84, 0x84, 0x00,	/* 11:어두운 노란색 */
		0x00, 0x00, 0x84,	/* 12:군청색 */
		0x84, 0x00, 0x84,	/* 13:어두운 보라색 */
		0x00, 0x84, 0x84,	/* 14:어두운 청색 */
		0x84, 0x84, 0x84	/* 15:어두운 회색 */
	};
	set_palette(0, 15, table_rgb);
	return;

	/* static char 명령은 (주소가 아니라)데이터밖에 쓰지 못하지만, DB명령과 동일함 */
}

void set_palette(int start, int end, unsigned char *rgb)
{
	int i, eflags;
	eflags = io_load_eflags();	/* 인터럽트 허가 플래그 값을 로드함 */
	io_cli(); 					/* 허가 플래그를 0으로 하여 인터럽트 금지 */
	io_out8(0x03c8, start);
	for (i = start; i <= end; i++) {
		io_out8(0x03c9, rgb[0] / 4);
		io_out8(0x03c9, rgb[1] / 4);
		io_out8(0x03c9, rgb[2] / 4);
		rgb += 3;
	}
	io_store_eflags(eflags);	/* 인터럽트 허가 플래그를 본래 값으로 되돌린다. */
	return;
}

 

 

 

c언어와 어셈블리어 문법 잠깐 보기

- char a[3] a라는 이름의 길이가 3인 배열 공간 생성( 값들은 0)

 => 아래와 동일

     a :

           RESB 3

 

- 색상 테이블은 어셈블리어 상에서 아래와 같이 값을 대입해주는게 경제적임

 => static char로 선언한 변수에 값을 넣는게, DB 명령어로 대입하는것과 동일함

   table_rgb:

      DB 0x00, 0x00, 0x00, 0xff, 0x00, 0xff ....

 

 

 

set_palette 보기

- io_out8 함수 : 장치 번호로 지정한 장치에 데이터를 보내는 함수

- CPU와 메모리만 연결되어있으면 계산과 기억밖에 하지못함.

 => 하드디스크, 그래픽, 사운드 카드 등과 연결되어 다양한 입출력이 가능해짐

 

 

out/in 명령어

- out 명령어 : 특정 장치로 전기 신호를 보내기 위한 명령어

- in 명령어 : 특정 장치로부터 신호를 받기 위한 명령

 

 

그래픽 관련 장치 번호

- 0x03c8, 0x03c9는 그래픽 관련 장치번호

- 팔레트 액세스 순서

- 액세스 중에는 인터럽트 발생해선 안됨 ex. CLI

- 0x03c08에 사용할 팔레트 번호를 쓰고, RGB 순서대로 0x03c9에 입력

  * 다음 팔레트도 같이 설정한다면 팔레트 번호 설정 생략하고 계속 RGB순서로 쓰면됨.

- 팔레트 상태 읽을때 0x03c7에 팔레트 번호 쓰고, 0x03c9를 3번 읽음 => RGB순서대로 처리됨.

- 최초 CLI(인터럽트 금지)를 한경우 마지막에 STI(io_store_eflags, 인터럽트 허가 플래그 원위치)를 한다.

 

 

CLI와 STI

- CLI : 인터럽트 플래그 interrupt flag를 0으로 만듬( clear interrupt flag)

- STI : 인터럽트 플래그를 1로 만드는 명령 (set interrupt flag)

=> 인터럽트 플래그가 0이면 인터럽트 반응 회로가 동작 안함, 1이면 동작함

 

 

EFLAGS

- 16비트 레지스터인 FLAGS가 32비트로 레지스터로 확장된 레지스터.

- FGLAS는 캐리 플래그, 인터럽트 플래그 등으로 구성됨.

- 아래의 그림은 16비트 8086 아키택처의 FLAGS 레지스터

http://mmrcse.blogspot.com/2018/12/write-function-of-segment-register-and.html

 

 

 

set_palette에서 하는일

- 팔레트 설정하기전에 CLI실행. 

- 팔레트 설정후

- 인터럽트 플래그 허용 STI

 

 

 

naskfunc.nas

- PUSHFD : push flags double-word : vmfformfmf ejqmf dnjemfh alfdjsjgsmsek.

- POPFD : pop EFLAGS라는 뜻

=> EFLAGS를 읽거나 쓰는데 ㅏ용.

 

; naskfunc
; TAB=4

[FORMAT "WCOFF"]				; 오브젝트 파일 만드는 모드
[INSTRSET "i486p"]				; 이 프로그램이 486 아키텍처 용 프로그램임을 nask에 알림
[BITS 32]						; 32비트 모드용 기계어 만듬
[FILE "naskfunc.nas"]			; 소스 파일명 정보

		GLOBAL	_io_hlt, _io_cli, _io_sti, _io_stihlt
		GLOBAL	_io_in8,  _io_in16,  _io_in32
		GLOBAL	_io_out8, _io_out16, _io_out32
		GLOBAL	_io_load_eflags, _io_store_eflags

[SECTION .text]

_io_hlt:	; void io_hlt(void);
		HLT
		RET

_io_cli:	; void io_cli(void);
		CLI
		RET

_io_sti:	; void io_sti(void);
		STI
		RET

_io_stihlt:	; void io_stihlt(void);
		STI
		HLT
		RET

_io_in8:	; int io_in8(int port);
		MOV		EDX,[ESP+4]		; port
		MOV		EAX,0
		IN		AL,DX
		RET

_io_in16:	; int io_in16(int port);
		MOV		EDX,[ESP+4]		; port
		MOV		EAX,0
		IN		AX,DX
		RET

_io_in32:	; int io_in32(int port);
		MOV		EDX,[ESP+4]		; port
		IN		EAX,DX
		RET

_io_out8:	; void io_out8(int port, int data);
		MOV		EDX,[ESP+4]		; port
		MOV		AL,[ESP+8]		; data
		OUT		DX,AL
		RET

_io_out16:	; void io_out16(int port, int data);
		MOV		EDX,[ESP+4]		; port
		MOV		EAX,[ESP+8]		; data
		OUT		DX,AX
		RET

_io_out32:	; void io_out32(int port, int data);
		MOV		EDX,[ESP+4]		; port
		MOV		EAX,[ESP+8]		; data
		OUT		DX,EAX
		RET

_io_load_eflags:	; int io_load_eflags(void);
		PUSHFD		; PUSH EFLAGS 라는 뜻
		POP		EAX
		RET

_io_store_eflags:	; void io_store_eflags(int eflags);
		MOV		EAX,[ESP+4]
		PUSH	EAX
		POPFD		; POP EFLAGS 라는 뜻
		RET

 

 

실행 결과

- 색상있는 줄무늬들이 출력됨

 

 

300x250

+ Recent posts