현황
- 방금전 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 레지스터
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
실행 결과
- 색상있는 줄무늬들이 출력됨
'컴퓨터과학 > 컴퓨터, OS' 카테고리의 다른 글
os만들때 자주사용하는 BIOS 함수 (0) | 2020.08.02 |
---|---|
os만들기 - 21. 사각형 그리고 완성시키자 (0) | 2020.08.02 |
os만들기 - 19. c언어를 이용해 메모리 쓰기 (0) | 2020.08.02 |
os만들기를 하는중에 (0) | 2020.08.02 |
os만들기 - 18. 32비트 모드 준비와 c언어 (0) | 2020.08.02 |