원랜 오늘 사칙연산까지만 하려했는데

잠깐 시간도 남아서 논리연산 조금 해보려고한다.

 

시프트연산

- SHR para1, para2

 para1 : 작업 장소

 para2 : 오른쪽(왼쪽)로 이동할 비트수 주로 4배수

- SHL para1, para2

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    mov ax,0x1234
    
    PRINT_HEX 2,ax
    NEWLINE
    
    shl ax,4
    
    PRINT_HEX 2,ax
    NEWLINE
    
    mov [a], word 0x1234
    PRINT_HEX 2,a
    NEWLINE
    
    shr word [a], 4
    PRINT_HEX 2,a
    NEWLINE
    
    xor rax, rax
    ret

section .bss
    a resw 1

 

 

 

 

AND, OR, XOR, NOT 연산

- AND, OR, XOR para1, para2(para1 = para1연산para2)

- NOT para(para = not para)

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    mov al, 0b10110110
    mov bl, 0b01010101
    PRINT_HEX 1,al
    NEWLINE
    PRINT_HEX 1,bl
    NEWLINE
    
    and al, bl
    PRINT_HEX 1,al
    NEWLINE
    
    mov al, 0b10110110
    mov bl, 0b01010101
    or al,bl
    PRINT_HEX 1,al
    NEWLINE
    
    mov al, 0b10110110
    mov bl, 0b01010101
    xor al,bl
    PRINT_HEX 1,al
    NEWLINE
    
    mov al, 0b10110110
    not al
    PRINT_HEX 1,al
    NEWLINE
    
    xor rax, rax
    ret

 

 

문자열 출력하기

- PRINT_STRING : SASM에서 제공하는 문자열 출력 매크로 함수

- ssection .data에 바이트 연속으로 선언

PRINT_STRING param(출력할곳 주소)

* 문자열 종료시 0x00으로 표기

* NASM에선 ''나 ""둘다 문자열로 허용

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    PRINT_STRING msg1
    NEWLINE
    PRINT_STRING msg2
    NEWLINE
    PRINT_STRING msg3
    
    xor rax, rax
    ret
    
section .data
    msg1 db 'haha ',0x00
    msg2 db 'hellow !',0x00
    msg3 db "mnsg3 ok",0x00

 

 

입력받기

- SASM에서는 아래 두함수로 받을수 있음

- GET_DEC para1, para2 입력된 문자열 10진수로 인식

 para1 입력할 바이트 수 para2 입력받을 곳 레지스터나 메모리주소

- GET_HEX para1, para2 입력된 문자열 16진수로 인식

 para1 입력할 바이트 수 para2 입력받을 곳 레지스터나 메모리주소

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    GET_DEC 1,al    ;1바이트 입력
    GET_DEC 2,a     ;2바이트 입력
    
    PRINT_DEC 1,al
    NEWLINE
    PRINT_DEC 2,a
    xor rax, rax
    ret
    
section .bss
    a resw 1    ;2바이트 초기화되지않은 변수 a1개 선언

 

 

 

 

 

덧셈연산

- ADD para1, para2(para1 = para 1 + para2)

 para 1 : 레지스터나 메모리에있는값

 para2 : 레지스터, 메모리, 값

 *para1,2둘다 메모리인 경우는 안됨

 

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    mov ax,1    ;2바이트 레지스터에 1대입
    mov bx,3    ;2바이트레지스터 bx에 3대입
    
    add ax, bx
    PRINT_DEC 2,ax
    NEWLINE
    
    mov [a], word 7 ;2바이트 변수 a에 7대입
    add ax, [a]
    PRINT_DEC 2,ax
    NEWLINE
    
    mov bx, 2
    add [a], bx
    PRINT_DEC 2,a
    NEWLINE
    
    mov [b], byte 2
    ;add [a], [b] <= error!

    xor rax, rax
    ret
    
section .bss
    a resw 1
    b resw 1

 

 

 

뺄셈연산

- SUB para1, para2(para1 = para1 - para2)

 para1 : 레지스터 나 메모리 값

 para2 : 레지스터, 메모리, 값

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    mov ax,1
    mov bx,3
    
    sub ax, bx
    PRINT_DEC 2, ax
    NEWLINE
    
    mov [a], word 7
    sub ax, [a]
    PRINT_DEC 2,ax
    NEWLINE
    
    mov bx,2
    sub [a],bx
    PRINT_DEC 2,a
    NEWLINE
    
    mov [b], byte 2
    PRINT_DEC 2,b
    ;sub [a], [b] <= 에러
    
    xor rax, rax
    ret
    
section .bss
    a resw 1
    b resw 1

 

 

 

1바이트 곱샘연산

- MUL para(1바이트)

* para가 1바이트일때 AX = AL * para

* para는 레지스터만 허용

*곱되는값은 무조건 AL레지스터에

*결과는 AX레지스터에 반환

 

아래 코드에서 ax레지스터를 0으로 초기화안하면 ah 레지스터값에 따라 영향받기 때문

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    PRINT_DEC 2,ax
    NEWLINE
    
    ;2*3
    mov ax,0
    mov al, 2
    mov bl, 3
    mul bx
    
    PRINT_DEC 1,ax
    NEWLINE

    xor rax, rax
    ret

 

 

 

1바이트 나누기 연산

- DIV para(1바이트)

* para가 1바이트일때 : AX / para : AL(몫), AH(나머지)

* 나눠지는값은 AX에

* 연산결과는 AL, AH로 리턴됨

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    ; 7(AX) / 2 = 3(AL, 몫), 1(AH, 나머지)
    
    mov ax, 7
    mov bl, 2
    div bl
    
    mov bl, ah
    
    PRINT_DEC 1,al
    NEWLINE
    PRINT_DEC 1,bl
    NEWLINE

    xor rax, rax
    ret

 

 

 

실행파일만들고 콘솔에서 실행해보기

코드작성 -> file에서 .exe 로 저장하기 -> 콘솔로 실행

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    PRINT_STRING msg1
    NEWLINE
    
    mov ax,3
    
    PRINT_DEC 2,ax
    NEWLINE

    xor rax, rax
    ret

section .data
    msg1 db "hello",0x00

 

 

 

 

 

사칙연산 계산기 만들기

- 2개의 변수를 1바이트 단위로 받아 메모리에 저장

- 사칙연산 한 값을 1바이트 단위 메모리에 각각 저장

- 나누기 결과는 몫과 나머지를 나누어 저장

- 곱의 경우 결과를 2바이트 변수로 선언하고 사용

- 이후 결과 모두 출력

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    ;input
    PRINT_STRING msg1
    GET_DEC 1,mya
    PRINT_DEC 1,mya
    NEWLINE
    
    PRINT_STRING msg1
    GET_DEC 1,myb
    PRINT_DEC 1,myb
    NEWLINE
    
    
    ;calc
    ;+
    mov al, [mya]
    mov bl, [myb]
    add al, bl
    mov [a], al
    
    ;-
    mov al, [mya]
    mov bl, [myb]
    sub al, bl
    mov [b], al
    
    ;*
    mov al, [mya]
    mov bl, [myb]
    mul bl
    mov [c], ax
    
    ;/
    mov ax,0
    mov al,[mya]
    mov bl,[myb]
    div bl;
    mov [d],al
    mov [e],ah
    
    
    
    
    ;output
    PRINT_STRING msg2
    PRINT_DEC 1,a
    NEWLINE
    
    PRINT_STRING msg3
    PRINT_DEC 1,b
    NEWLINE
    
    PRINT_STRING msg4
    PRINT_DEC 2,c
    NEWLINE
    
    PRINT_STRING msg5
    PRINT_DEC 1,d
    NEWLINE
    
    PRINT_STRING msg6
    PRINT_DEC 1,e
    NEWLINE
    

    xor rax, rax
    ret

section .data
    msg1 db 'input data:',0x00
    msg2 db '+:',0x00
    msg3 db '-:',0x00
    msg4 db '*:',0x00
    msg5 db '/(q):',0x00
    msg6 db '/(r):',0x00

section .bss
    mya resb 1; input 1
    myb resb 1; input 2
    a resb 1 ; +
    b resb 1 ; -
    c resw 1 ; *
    d resb 1 ; /(몫)
    e resb 1 ; /(나머지)

 

콘솔로 돌리기

 

 

워드

- CPU에서 처리하는 단위

- 16비트 CPU의 경우

- 16bit = 2byte = 1word

 

16진수 표기

- 10진수 10 = 16진수 0x0A

 

레지스터 크기

- 64bit OS는 64비트, 32bit os는 32비트 크기 레지스터 사용

- RAX :  64bit

- EAX : 32bit

- AX : 16bit

- AH, AL : 8bit

 

 

SASM 어셈블리 구조

- %include "io64.inc" : OS에 무관한 입출력 매크로 함수 지원

- section .text

  * section : 데이터나 명령을 모은 블록

- global main : 프로그램 시작점

 

 

 

레지스터 데이터 입력하기

- 주로 ABCD 레지스터 사용 (eax, ax, bx)

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    mov eax, 0x1234 ;A레지스터 32bit 크기에 0x1234저장
    mov ax, 0x1234  ;A레지스터 16bit 크기에 0x1234 저장
    mov ax, bx      ;bx의 값을 ax로 복사
    mov ax,ebx      ;[에러발생] ebx(32bit)값을 ax(16bit)에 저장시
    xor rax, rax도
    ret

 

 

 

 

 

어셈블리 메모리

- 메모리 사용 필요한것 : 메모리 크기, 메모리 위치

- 개발자는 메모리 크기 결정은 가능하나 주소는 모르므로 심벌(변수)로 설정하여 사용함.

아래의 경우

변수 명 : a

크기 : 2바이트

시작 주소 : 1

저장 값 : 0x1234

 

 

리틀엔디언 빅엔디언

- 메모리에 값(바이트) 저장하는 순서

- 위에서 0x1234가 1에 0x34, 2에 0x12가 들어가는것은 리틀엔디언방식

https://ko.wikipedia.org/wiki/%EC%97%94%EB%94%94%EC%96%B8

 

 

어셈블리 변수선언 (초기화 x)

- 초기화 하지 않은 변수는 .bss 블록에 언해야함

section .bss

    변수명    크기지시자    개수

 

크기지시자

resb 1바이트  byte?

resw 2바이트 word?

resd 4바이트 double word?

resq  8바이트 qaurd word?

 

 

 

어셈블리 변수선언 (초기화 o)

- 초기값이 있는 경우 section .data 블록에서 선언함

변수명    크기지시자    초기값

 

크기지시자

db 1바이트 바이트

dw 2바이트 워드

dd 4바이트 더블워드?

dq 8바이트 쿼드워드?

 

 

 

메모리 이용하기, 

- mov : 메모리에 데이터 보내기

 ex) 변수 a에 저장된 값을 ax 레지스터로 가져오라

  -> mov ax, [a]

변수명 = 주소, [변수명] = 주소에 있는값

* []는 c언어의 포인터 느낌으로 생각하면 될듯 []가 없는 변수는 주소 []가 있는 변수는 가리키는 곳

 

(주의) 어셈블리에서 변수 선언시 크기 지정해줘도 데이터는 크기를 명시해야함.

 

10번라인에서 데이터 크기 명시하지 않아 빌드 에러
바이트 명시시 빌드 성공

 

 

레지스터와 메모리 값 출력하기

- 레지스터, 메모리에 있는 값을 CPU 어셈블리만으로 출력은 힘듬. OS에 포함되어있기 때문.

- 운영체제와 무관하게 SASM에서 제공해주는 매크로 함수 사용하여 출력해보기

PRINT_HEX 바이트수, 레지스터혹은변수명 : 레지스터혹은변수명을 16진수로 출력

PRINT_DEC 바이트수, 레지스터혹은변수명 : 레지스터혹은변수명을 10진수로 출력

NEWLINE : 화면에 줄변경

 

* 0x12는 10진수로 16 + 2 = 18이 되므로 PRINT_DEC 2, ax(0x12)는 18이 맞음

 

 

데이터 옮기기 예제

1. 초기값 있는 메모리 변수 4개 선언 크기는 1,2,4,8바이트로 값은 16진수로 초기화

2. 초기값없는 메모리 변수 1, 2,4,8바이트 1개씩 선언하고 앞에서 선언한 값을 아래 선언한 변수로 이동

3. 변수 8개 모두 16진수로 출력

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    mov al,[a]      ;1바이트 크기 변수 a를 1바이트 레지스터 al에 저장
    mov [mya],al    ;1바이트 레지스터 al값을 mya에 저장
    
    mov ax,[b]      ;2바이트 크기 변수 b를 2바이트 레지스터 ax에 저장
    mov [myb],ax    ;2바이트 레지스터 ax값을 myb에 저장
    
    mov eax,[c]     ;4바이트 크기 변수 c를 4바이트 레지스터 eax에 저장
    mov [myc],eax
    
    mov rax,[d]
    mov [myd],rax
    
    
    PRINT_HEX 1,a   ;1바이트 단위로 변수 a값 16진수로 출력
    NEWLINE
    
    PRINT_HEX 2,b   ;2바이트 단위로 변수 b값 16진수로 출력
    NEWLINE
    
    PRINT_HEX 4,c   ;4바이트 단위로 변수 c값 16진수로 출력
    NEWLINE
    
    PRINT_HEX 8,d   ;8바이트 단위로 변수 d값 16진수로 출력
    NEWLINE
    
    PRINT_HEX 1,mya
    NEWLINE
    
    PRINT_HEX 2,myb
    NEWLINE
    
    PRINT_HEX 4,myc
    NEWLINE
    
    PRINT_HEX 8,myd
    
    xor rax, rax
    ret

section .data
    a db 0x12                   ;1바이트 변수 0x12
    b dw 0x1234                 ;2바이트 변수 0x1234
    c dd 0x12345678             ;4바이트 변수 c
    d dq 0x1234567812345678     ;8바이트 변수 d
  
section .bss
    mya resb 1  ;1바이트 초기화안된변수 선언
    myb resw 1  ;2바이트 초기화안된변수 myb선언
    myc resd 1  ;4바이트 초기화안된변수 myc선언
    myd resq 1  ;8바이트 초기화안된변수 myq선

 

주로 쓰는 어셈블러

- 윈도우 : MASM

- 리눅스/유닉스 : GAS

- 둘다 가능 : NSAM

 

 

사용할 어셈블러 ide

https://dman95.github.io/SASM/english.html

 

SASM - Simple crossplatform IDE for NASM, MASM, GAS, FASM assembly languages

SASM SASM (SimpleASM) - simple Open Source crossplatform IDE for NASM, MASM, GAS, FASM assembly languages. SASM has syntax highlighting and debugger. The program works out of the box and is great for beginners to learn assembly language. SASM is translated

dman95.github.io

 

 

 

 

 

보다보니

맨위에 

include io64.inc를 안해준게 문제더라

%include "io64.inc"

section .text
global main
main:
    ;write your code here
    mov eax,10
    PRINT_DEC 1, eax
    
    xor eax, eax
    ret

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

어셈블리 - 3. 사칙연산  (0) 2024.04.14
어셈블리 - 2. 기초 개념들  (0) 2024.04.14
OS30 - 7. 글자쓰기  (0) 2024.04.08
OS30 - 6. c언어 사용, 화면 만들기  (0) 2024.04.05
OS30 - 5. ipl 만들기, c언어 진입  (0) 2024.04.05

먼저  어셈블리코드에서 부팅정보 가져오기

 

이전에 어샘해드.nas에 부트정보 표기했었는데 이걸 c에 가져와서 사용

 


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;
    vram = (char *) *binfo_vram;
    xsize = *binfo_scrnx;
    ysize = *binfo_scrny;

 

 

구조체 사용해서 부트인포 정리

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;

 

 

화살표 연산자로 수정

void io_hlt(void);
void io_cli(void);
void io_out8(int prot, 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,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0xff, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0xff,
        0x00, 0xff, 0xff,
        0xff, 0xff, 0xff,
        0xc6, 0xc6, 0xc6,
        0x84, 0x00, 0x00,
        0x00, 0x84, 0x00,
        0x84, 0x84, 0x00,
        0x00, 0x00, 0x84,
        0x84, 0x00, 0x84,
        0x00, 0x84, 0x84,
        0x84, 0x84, 0x84
    };

    set_palette(0, 15, table_rgb);
    return;
}

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;
}

 

 

 

글자 쓰기

putchar8로 폰트데이터 드로잉 구현

void io_hlt(void);
void io_cli(void);
void io_out8(int prot, 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,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0xff, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0xff,
        0x00, 0xff, 0xff,
        0xff, 0xff, 0xff,
        0xc6, 0xc6, 0xc6,
        0x84, 0x00, 0x00,
        0x00, 0x84, 0x00,
        0x84, 0x84, 0x00,
        0x00, 0x00, 0x84,
        0x84, 0x00, 0x84,
        0x00, 0x84, 0x84,
        0x84, 0x84, 0x84
    };

    set_palette(0, 15, table_rgb);
    return;
}

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;
    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;}
    }

}

 

 

 

 

폰트 데이터 가져와 사용하기

위 예제에선 폰트 A 데이터를 직접 그려서 썻으나

아래 택스트 파일을 바이너리 이미지로 만들어 bootpack.obj와 합쳐서 사용함.

hankaku.txt
0.04MB

 

 

 

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 + 'A' * 16);
    putfont8(binfo->vram, binfo->scrnx, 16, 8, COL8_FFFFFF, hankaku + 'B' * 16);
    putfont8(binfo->vram, binfo->scrnx, 24, 8, COL8_FFFFFF, hankaku + 'C' * 16);
    putfont8(binfo->vram, binfo->scrnx, 40, 8, COL8_FFFFFF, hankaku + '1' * 16);
    putfont8(binfo->vram, binfo->scrnx, 48, 8, COL8_FFFFFF, hankaku + '2' * 16);
    putfont8(binfo->vram, binfo->scrnx, 56, 8, COL8_FFFFFF, hankaku + '3' * 16);
    

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

 

 

 

 

화면에 그리기위해

vram에 뭔가 써야함.

naskfunc.nas에 write_mem8 함수 추가

;naskfunc
;TAB=4

[FORMAT "WCOFF"]        ;오브젝트 파일 만드는 모드
[INSTRSET "i486p"]       ;486명령사용하고싶다 작성
[BITS 32]               ;32비트모드용 기계어 만듬

;오브젝트 파일 위한 정보
[FILE "naskfunc.nas"]   ;소스파일명 정보
    GLOBAL  _io_hlt,_write_mem8     ;이프로그램에 포함된 함수명

; 실제 함수

[SECTION .text]         ;오브젝트 파일에서는 이것을 쓴후에 프로그램을 쓴다.
_io_hlt:        ;void io_hlt(void);
    HLT
    RET

_write_mem8:    ; void write_mem8(int addr, int data);
    MOV ECX, [ESP+4]    ; [esp+4]에 addr이 들어있으니 ecx에 읽어들임
    MOV AL, [ESP+8]     ; [ESP+8]에 data가 있으니 al에 읽어들임
    MOV [ECX], AL
    RET

 

 

 

nask에 instrset으로 486용임을 알림

cpu를 32비트 모드로 사용중이라 32비트 레지스터 사용 ecx

vram 구간 전체에 15를 써서 하얗게 됨

 

bootpack.c

void io_hlt(void);
void write_mem8(int addr, int data);

void HariMain(void)
{
    int i;
    
    for (i = 0xa0000; i<= 0xaffff; i++)
    {
        write_mem8(i, 15);  //move byte [i], 15
    }

fin:
    io_hlt();     //naskfunc.nas의 _io_hlt가 실행됨.
    goto fin;
}

 

* vram이 0xa0000에서 시작하는 이유

haribote.nas에서 vram에 0x000a0000으로 지정

 

 

 

 

줄무늬 만들기

i와 0x0f and 연산으로 줄무늬 만들기

void io_hlt(void);
void write_mem8(int addr, int data);

void HariMain(void)
{
    int i;
    
    for (i = 0xa0000; i<= 0xaffff; i++)
    {
        write_mem8(i, i & 0x0f);  //move byte [i], 15
    }

fin:
    io_hlt();     //naskfunc.nas의 _io_hlt가 실행됨.
    goto fin;
}

 

 

 

포인터 사용하기

write_mem8대신 포인터사용해줘도 지정가능

void io_hlt(void);
void write_mem8(int addr, int data);

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

    
    for (i = 0xa0000; i<= 0xaffff; i++)
    {
        p = i;
        *p = i & 0x0f;
        //write_mem8(i, i & 0x0f);  //move byte [i], 15
    }

fin:
    io_hlt();     //naskfunc.nas의 _io_hlt가 실행됨.
    goto fin;
}

 

 

 

색상 사용하기

색상 쓰기위해 rgb table 준비해서 팔레트 설정하는 내용

어셈블리 함수가 여러개 추가됨.

void io_hlt(void);
void io_cli(void);
void io_out8(int prot, 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 HariMain(void)
{
    int i;
    char *p; //p라는 변수는 byte[]용 번지
    
    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,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0xff, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0xff,
        0x00, 0xff, 0xff,
        0xff, 0xff, 0xff,
        0xc6, 0xc6, 0xc6,
        0x84, 0x00, 0x00,
        0x00, 0x84, 0x00,
        0x84, 0x84, 0x00,
        0x00, 0x00, 0x84,
        0x84, 0x00, 0x84,
        0x00, 0x84, 0x84,
        0x84, 0x84, 0x84
    };

    set_palette(0, 15, table_rgb);
    return;
}

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;
}

 

 

 

추가된 함수들

; naskfunc
; TAB=4

[FORMAT "WCOFF"]				; 오브젝트 파일을 만드는 모드	
[INSTRSET "i486p"]				; 486명령까지 사용하고 싶다고 하는 기술
[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

 

 

 

이전보다 더 다양한 색상으로 줄무늬가 그려짐.

 

 

박스 그리기

boxfill8 함수 만들어 박스 띄움

void io_hlt(void);
void io_cli(void);
void io_out8(int prot, 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)
{
    int i;
    char *p; //p라는 변수는 byte[]용 번지
    
    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,
        0xff, 0x00, 0x00,
        0x00, 0xff, 0x00,
        0xff, 0xff, 0x00,
        0x00, 0x00, 0xff,
        0xff, 0x00, 0xff,
        0x00, 0xff, 0xff,
        0xff, 0xff, 0xff,
        0xc6, 0xc6, 0xc6,
        0x84, 0x00, 0x00,
        0x00, 0x84, 0x00,
        0x84, 0x84, 0x00,
        0x00, 0x00, 0x84,
        0x84, 0x00, 0x84,
        0x00, 0x84, 0x84,
        0x84, 0x84, 0x84
    };

    set_palette(0, 15, table_rgb);
    return;
}

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;
}

 

 

 

바탕화면 만들기

 

일단 박스필 4개만하면 이런식으로 화면이 만들어진다.

 

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); //바닥 표시줄


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

 

 

 

 

 

박스필 몇개 더 추가하면

옛날 윈도우 화면 느낌으로 만들어진다.

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();
    }
}

 

 

 

day04.zip
0.01MB

이전 글에서 ipl 초기 프로그램 로더 만들면서

부트섹터 외 부분은 로드 하지 않음

 

이번에 플로피 디스크 읽도록 수정

 

수정된 ipl.nas 이전 ipl.nas
; hello-os
; TAB=4

ORG 0x7c00 ; 이 프로그램이 어디에 read되는가

; 이하는 표준적인 FAT12 포맷 플로피 디스크를 위한 기술

JMP entry
DB 0x90
DB "HELLOIPL" ; boot sector이름을 자유롭게 써도 좋다(8바이트)
DW 512 ; 1섹터 크기(512로 해야 함)
DB 1 ; 클러스터 크기(1섹터로 해야 함)
DW 1 ; FAT가 어디에서 시작될까(보통은 1섹터째부터)
DB 2 ; FAT 개수(2로 해야 함)
DW 224 ; 루트 디렉토리 영역의 크기(보통은 224엔트리로 한다)
DW 2880 ; 드라이브 크기(2880섹터로 해야 함)
DB 0xf0 ; 미디어 타입(0xf0로 해야 함)
DW 9 ; FAT영역의 길이(9섹터로 해야 함)
DW 18 ; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
DW 2 ; 헤드 수(2로 해야 함)
DD 0 ; 파티션을 사용하지 않기 때문에 여기는 반드시 0
DD 2880 ; 드라이브 크기를 한번 더 write
DB 0,0,0x29 ; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
DD 0xffffffff ; 아마, 볼륨 시리얼 번호
DB "HELLO-OS   " ; 디스크 이름(11바이트)
DB "FAT12   " ; 포맷 이름(8바이트)
RESB 18 ; 우선 18바이트를 비어 둔다

; 프로그램 본체

entry:
MOV AX, 0 ; 레지스터 초기화
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX

;디스크 읽기

MOV AX,0x820
MOV ES,AX
MOV CH,0 ; 실린더0
MOV DH,0 ; 헤드 0
MOV CL,2 ; 섹터 2

MOV AH, 0x02 ;AH=0x02; 디스크read
MOV AL, 1 ;1섹터
MOV BX,0
MOV DL,0x00로 ;A드라이브
INT 0x13 ;디스크BIOS 호출
JC error

putloop:
MOV AL,[SI]
ADD SI, 1 ; SI에 1을 더한다
CMP AL,0
JE fin
MOV AH, 0x0e ; 한 글자 표시 Function
MOV BX, 15 ; 칼라 코드
INT 0x10 ; 비디오 BIOS 호출
JMP putloop
fin:
HLT ; 무엇인가 있을 때까지 CPU를 정지시킨다
JMP fin ; Endless Loop

msg:
DB 0x0a, 0x0a ; 개행을 2개
DB "hello, world"
DB 0x0a ; 개행
DB 0

RESB 0x7dfe-$ ; 0x7dfe까지를 0x00로 채우는 명령

DB 0x55, 0xaa
; hello-os
; TAB=4

ORG 0x7c00 ; 이 프로그램이 어디에 read되는가

; 이하는 표준적인 FAT12 포맷 플로피 디스크를 위한 기술

JMP entry
DB 0x90
DB "HELLOIPL" ; boot sector이름을 자유롭게 써도 좋다(8바이트)
DW 512 ; 1섹터 크기(512로 해야 함)
DB 1 ; 클러스터 크기(1섹터로 해야 함)
DW 1 ; FAT가 어디에서 시작될까(보통은 1섹터째부터)
DB 2 ; FAT 개수(2로 해야 함)
DW 224 ; 루트 디렉토리 영역의 크기(보통은 224엔트리로 한다)
DW 2880 ; 드라이브 크기(2880섹터로 해야 함)
DB 0xf0 ; 미디어 타입(0xf0로 해야 함)
DW 9 ; FAT영역의 길이(9섹터로 해야 함)
DW 18 ; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
DW 2 ; 헤드 수(2로 해야 함)
DD 0 ; 파티션을 사용하지 않기 때문에 여기는 반드시 0
DD 2880 ; 드라이브 크기를 한번 더 write
DB 0,0,0x29 ; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
DD 0xffffffff ; 아마, 볼륨 시리얼 번호
DB "HELLO-OS   " ; 디스크 이름(11바이트)
DB "FAT12   " ; 포맷 이름(8바이트)
RESB 18 ; 우선 18바이트를 비어 둔다

; 프로그램 본체

entry:
MOV AX, 0 ; 레지스터 초기화
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV ES,AX

MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI, 1 ; SI에 1을 더한다
CMP AL,0
JE fin
MOV AH, 0x0e ; 한 글자 표시 Function
MOV BX, 15 ; 칼라 코드
INT 0x10 ; 비디오 BIOS 호출
JMP putloop
fin:
HLT ; 무엇인가 있을 때까지 CPU를 정지시킨다
JMP fin ; Endless Loop

msg:
DB 0x0a, 0x0a ; 개행을 2개
DB "hello, world"
DB 0x0a ; 개행
DB 0

RESB 0x7dfe-$ ; 0x7dfe까지를 0x00로 채우는 명령

DB 0x55, 0xaa

 

 

디스크 읽기 설정

- 읽기 : AH=0x02

- 쓰기 : AH=0x03

- DH = 헤드 번호

- DL = 드라이브 번호

- CH = 실린더 번호 & 0xFF

- CL = 섹터번호

 

 

 

일단 책에서 시킨데로 에러는 나오진 않는데 S가 나온다.

S가나오면 안될텐데

 

 

 

 

 

 

책에 설명안된 부분을 안적어서 그랬다.

 

; hello-os
; TAB=4

		ORG		0x7c00			; 이 프로그램이 어디에 read되는가

; 이하는 표준적인 FAT12 포맷 플로피 디스크를 위한 기술

		JMP		entry
		DB		0x90
		DB		"HELLOIPL"		; boot sector이름을 자유롭게 써도 좋다(8바이트)
		DW		512			; 1섹터 크기(512로 해야 함)
		DB		1			; 클러스터 크기(1섹터로 해야 함)
		DW		1			; FAT가 어디에서 시작될까(보통은 1섹터째부터)
		DB		2			; FAT 개수(2로 해야 함)
		DW		224			; 루트 디렉토리 영역의 크기(보통은 224엔트리로 한다)
		DW		2880			; 드라이브 크기(2880섹터로 해야 함)
		DB		0xf0			; 미디어 타입(0xf0로 해야 함)
		DW		9			; FAT영역의 길이(9섹터로 해야 함)
		DW		18			; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
		DW		2			; 헤드 수(2로 해야 함)
		DD		0			; 파티션을 사용하지 않기 때문에 여기는 반드시 0
		DD		2880			; 드라이브 크기를 한번 더 write
		DB		0,0,0x29		; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
		DD		0xffffffff		; 아마, 볼륨 시리얼 번호
		DB		"HELLO-OS   "		; 디스크 이름(11바이트)
		DB		"FAT12   "		; 포맷 이름(8바이트)
		RESB	18				; 우선 18바이트를 비어 둔다

; 프로그램 본체

entry:
		MOV		AX, 0			; 레지스터 초기화
		MOV		SS,AX
		MOV		SP,0x7c00
		MOV		DS,AX

;디스크 읽기

		MOV		AX,0x820
		MOV		ES,AX
		MOV		CH,0			; 실린더0
		MOV		DH,0			; 헤드 0
		MOV		CL,2			; 섹터 2

		MOV		AH, 0x02		;AH=0x02; 디스크read
		MOV		AL, 1			;1섹터
		MOV		BX,0
		MOV		DL,0x00로		;A드라이브
		INT		0x13			;디스크BIOS 호출
		JC error
fin:
		HLT					; 무엇인가 있을 때까지 CPU를 정지시킨다
		JMP		fin			; Endless Loop
error:
		MOV		AX,0
		MOV		ES,AX
		MOV		SI,msg
putloop:
		MOV		AL,[SI]
		ADD		SI, 1			; SI에 1을 더한다
		CMP		AL,0
		JE		fin
		MOV		AH, 0x0e		; 한 글자 표시 Function
		MOV		BX, 15			; 칼라 코드
		INT		0x10			; 비디오 BIOS 호출
		JMP		putloop
msg:
		DB		0x0a, 0x0a		; 개행을 2개
		DB		"load error"
		DB		0x0a			; 개행
		DB		0

		RESB	0x7dfe-$		; 0x7dfe까지를 0x00로 채우는 명령

		DB		0x55, 0xaa

 

수정 결과 정상 동작 확인

 

 

 

 

오류시 retry 추가

; hello-os
; TAB=4

		ORG		0x7c00			; 이 프로그램이 어디에 read되는가

; 이하는 표준적인 FAT12 포맷 플로피 디스크를 위한 기술

		JMP		entry
		DB		0x90
		DB		"HELLOIPL"		; boot sector이름을 자유롭게 써도 좋다(8바이트)
		DW		512			; 1섹터 크기(512로 해야 함)
		DB		1			; 클러스터 크기(1섹터로 해야 함)
		DW		1			; FAT가 어디에서 시작될까(보통은 1섹터째부터)
		DB		2			; FAT 개수(2로 해야 함)
		DW		224			; 루트 디렉토리 영역의 크기(보통은 224엔트리로 한다)
		DW		2880			; 드라이브 크기(2880섹터로 해야 함)
		DB		0xf0			; 미디어 타입(0xf0로 해야 함)
		DW		9			; FAT영역의 길이(9섹터로 해야 함)
		DW		18			; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
		DW		2			; 헤드 수(2로 해야 함)
		DD		0			; 파티션을 사용하지 않기 때문에 여기는 반드시 0
		DD		2880			; 드라이브 크기를 한번 더 write
		DB		0,0,0x29		; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
		DD		0xffffffff		; 아마, 볼륨 시리얼 번호
		DB		"HELLO-OS   "		; 디스크 이름(11바이트)
		DB		"FAT12   "		; 포맷 이름(8바이트)
		RESB	18				; 우선 18바이트를 비어 둔다

; 프로그램 본체

entry:
		MOV		AX, 0			; 레지스터 초기화
		MOV		SS,AX
		MOV		SP,0x7c00
		MOV		DS,AX

;디스크 읽기

		MOV		AX,0x820
		MOV		ES,AX
		MOV		CH,0			; 실린더0
		MOV		DH,0			; 헤드 0
		MOV		CL,2			; 섹터 2

		MOV		SI,0			;실패 횟수 세는 레지스터

retry:
		MOV		AH, 0x02		;AH=0x02; 디스크read
		MOV		AL, 1			;1섹터
		MOV		BX,0
		MOV		DL,0x00로		;A드라이브
		INT		0x13			;디스크BIOS 호출
		JNC		fin				;에러 없으면 fin으로
		ADD		SI,1			;SI에 1더함
		CMP		SI,5			;SI와 5비교
		JAE		error			; SI>=5 면 error
		MOV		AH,0x00
		MOV		DL,0x00			;A드라이브
		INT		0x13			;드라이브리셋
		JMP		retry

fin:
		HLT					; 무엇인가 있을 때까지 CPU를 정지시킨다
		JMP		fin			; Endless Loop
error:
		MOV		AX,0
		MOV		ES,AX
		MOV		SI,msg
putloop:
		MOV		AL,[SI]
		ADD		SI, 1			; SI에 1을 더한다
		CMP		AL,0
		JE		fin
		MOV		AH, 0x0e		; 한 글자 표시 Function
		MOV		BX, 15			; 칼라 코드
		INT		0x10			; 비디오 BIOS 호출
		JMP		putloop
msg:
		DB		0x0a, 0x0a		; 개행을 2개
		DB		"load error"
		DB		0x0a			; 개행
		DB		0

		RESB	0x7dfe-$		; 0x7dfe까지를 0x00로 채우는 명령

		DB		0x55, 0xaa

 

 

 

 

 

18섹터까지 읽기

 

1장의 디스크

- 80 실린더

- 2 헤드

- 1실린더 = 18섹터

- 1섹터 = 512바이트

- 80 x 2 x 18 x 512 = 1,440kb

 

CL로 섹터 번호 지정. 2 ~ 18섹터까지 반복 읽기수행

; hello-os
; TAB=4

		ORG		0x7c00			; 이 프로그램이 어디에 read되는가

; 이하는 표준적인 FAT12 포맷 플로피 디스크를 위한 기술

		JMP		entry
		DB		0x90
		DB		"HELLOIPL"		; boot sector이름을 자유롭게 써도 좋다(8바이트)
		DW		512			; 1섹터 크기(512로 해야 함)
		DB		1			; 클러스터 크기(1섹터로 해야 함)
		DW		1			; FAT가 어디에서 시작될까(보통은 1섹터째부터)
		DB		2			; FAT 개수(2로 해야 함)
		DW		224			; 루트 디렉토리 영역의 크기(보통은 224엔트리로 한다)
		DW		2880			; 드라이브 크기(2880섹터로 해야 함)
		DB		0xf0			; 미디어 타입(0xf0로 해야 함)
		DW		9			; FAT영역의 길이(9섹터로 해야 함)
		DW		18			; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
		DW		2			; 헤드 수(2로 해야 함)
		DD		0			; 파티션을 사용하지 않기 때문에 여기는 반드시 0
		DD		2880			; 드라이브 크기를 한번 더 write
		DB		0,0,0x29		; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
		DD		0xffffffff		; 아마, 볼륨 시리얼 번호
		DB		"HELLO-OS   "		; 디스크 이름(11바이트)
		DB		"FAT12   "		; 포맷 이름(8바이트)
		RESB	18				; 우선 18바이트를 비어 둔다

; 프로그램 본체

entry:
		MOV		AX, 0			; 레지스터 초기화
		MOV		SS,AX
		MOV		SP,0x7c00
		MOV		DS,AX

;디스크 읽기

		MOV		AX,0x820
		MOV		ES,AX
		MOV		CH,0			; 실린더0
		MOV		DH,0			; 헤드 0
		MOV		CL,2			; 섹터 2

readloop:
		MOV		SI,0			;실패 횟수 세는 레지스터
retry:
		MOV		AH, 0x02		;AH=0x02; 디스크read
		MOV		AL, 1			;1섹터
		MOV		BX,0
		MOV		DL,0x00로		;A드라이브
		INT		0x13			;디스크BIOS 호출
		JNC		next			;에러 없으면 fin으로
		ADD		SI,1			;SI에 1더함
		CMP		SI,5			;SI와 5비교
		JAE		error			; SI>=5 면 error
		MOV		AH,0x00
		MOV		DL,0x00			;A드라이브
		INT		0x13			;드라이브리셋
		JMP		retry
next:
		MOV		AX,ES		;어드레스를 0x200 더한다.
		ADD		AX,0x0020	
		MOV		ES,AX		;ADD ES, 0x020이 없어서 이렇게함
		ADD		CL,1		;CL +=1
		CMP		CL,18		;CL과 18비교
		JBE		readloop	; CL <= 18 이면 readloop로

fin:
		HLT					; 무엇인가 있을 때까지 CPU를 정지시킨다
		JMP		fin			; Endless Loop
error:
		MOV		AX,0
		MOV		ES,AX
		MOV		SI,msg
putloop:
		MOV		AL,[SI]
		ADD		SI, 1			; SI에 1을 더한다
		CMP		AL,0
		JE		fin
		MOV		AH, 0x0e		; 한 글자 표시 Function
		MOV		BX, 15			; 칼라 코드
		INT		0x10			; 비디오 BIOS 호출
		JMP		putloop
msg:
		DB		0x0a, 0x0a		; 개행을 2개
		DB		"load error"
		DB		0x0a			; 개행
		DB		0

		RESB	0x7dfe-$		; 0x7dfe까지를 0x00로 채우는 명령

		DB		0x55, 0xaa

 

 

10 실린더 읽기

이전에는 C0-H0-S2 ~ C0-H0-S18까지 읽었는데 이번에는

C9-H1-S18까지 읽어보려고함.

 

맨 위에

CYLS EQU 10으로

CYLS(실린더)=10 설정

; hello-os
; TAB=4


CYLS	EQU		10				; 어디까지 read할지

		ORG		0x7c00			; 이 프로그램이 어디에 read되는가

; 이하는 표준적인 FAT12 포맷 플로피 디스크를 위한 기술

		JMP		entry
		DB		0x90
		DB		"HELLOIPL"		; boot sector이름을 자유롭게 써도 좋다(8바이트)
		DW		512			; 1섹터 크기(512로 해야 함)
		DB		1			; 클러스터 크기(1섹터로 해야 함)
		DW		1			; FAT가 어디에서 시작될까(보통은 1섹터째부터)
		DB		2			; FAT 개수(2로 해야 함)
		DW		224			; 루트 디렉토리 영역의 크기(보통은 224엔트리로 한다)
		DW		2880			; 드라이브 크기(2880섹터로 해야 함)
		DB		0xf0			; 미디어 타입(0xf0로 해야 함)
		DW		9			; FAT영역의 길이(9섹터로 해야 함)
		DW		18			; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
		DW		2			; 헤드 수(2로 해야 함)
		DD		0			; 파티션을 사용하지 않기 때문에 여기는 반드시 0
		DD		2880			; 드라이브 크기를 한번 더 write
		DB		0,0,0x29		; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
		DD		0xffffffff		; 아마, 볼륨 시리얼 번호
		DB		"HELLO-OS   "		; 디스크 이름(11바이트)
		DB		"FAT12   "		; 포맷 이름(8바이트)
		RESB	18				; 우선 18바이트를 비어 둔다

; 프로그램 본체

entry:
		MOV		AX, 0			; 레지스터 초기화
		MOV		SS,AX
		MOV		SP,0x7c00
		MOV		DS,AX

;디스크 읽기

		MOV		AX,0x820
		MOV		ES,AX
		MOV		CH,0			; 실린더0
		MOV		DH,0			; 헤드 0
		MOV		CL,2			; 섹터 2

readloop:
		MOV		SI,0			;실패 횟수 세는 레지스터
retry:
		MOV		AH, 0x02		;AH=0x02; 디스크read
		MOV		AL, 1			;1섹터
		MOV		BX,0
		MOV		DL,0x00			;A드라이브
		INT		0x13			;디스크BIOS 호출
		JNC		next			;에러 없으면 fin으로
		ADD		SI,1			;SI에 1더함
		CMP		SI,5			;SI와 5비교
		JAE		error			; SI>=5 면 error
		MOV		AH,0x00
		MOV		DL,0x00			;A드라이브
		INT		0x13			;드라이브리셋
		JMP		retry
next:
		MOV		AX,ES		;어드레스를 0x200 더한다.
		ADD		AX,0x0020	
		MOV		ES,AX		;ADD ES, 0x020이 없어서 이렇게함
		ADD		CL,1		;CL +=1
		CMP		CL,18		;CL과 18비교
		JBE		readloop	; CL <= 18 이면 readloop로
		MOV		CL,1
		ADD		DH,1
		CMP		DH,2
		JB		readloop	;DH<2라면 readloop로
		MOV		DH,0
		ADD		CH,1
		CMP		CH,CYLS
		JB		readloop	;CH<CYLS면 readloop로

fin:
		HLT					; 무엇인가 있을 때까지 CPU를 정지시킨다
		JMP		fin			; Endless Loop
error:
		MOV		AX,0
		MOV		ES,AX
		MOV		SI,msg
putloop:
		MOV		AL,[SI]
		ADD		SI, 1			; SI에 1을 더한다
		CMP		AL,0
		JE		fin
		MOV		AH, 0x0e		; 한 글자 표시 Function
		MOV		BX, 15			; 칼라 코드
		INT		0x10			; 비디오 BIOS 호출
		JMP		putloop
msg:
		DB		0x0a, 0x0a		; 개행을 2개
		DB		"load error"
		DB		0x0a			; 개행
		DB		0

		RESB	0x7dfe-$		; 0x7dfe까지를 0x00로 채우는 명령

		DB		0x55, 0xaa

 

 

중간에 next 부분을 보면

섹터(CL) +1해서 18까지 보내고 

18넘기면 다시 실린더 1 설정

DH 1로 헤더 반대편 확인

DH2가 넘으면 DH = 0으로 다시 헤드 윗면 설정

실린더 CH+1해서 CYLS(10)이 될때까지 루프하도록 동작 

 

next:
		MOV		AX,ES		;어드레스를 0x200 더한다.
		ADD		AX,0x0020	
		MOV		ES,AX		;ADD ES, 0x020이 없어서 이렇게함
		ADD		CL,1		;CL +=1
		CMP		CL,18		;CL과 18비교
		JBE		readloop	; CL <= 18 이면 readloop로
		MOV		CL,1
		ADD		DH,1
		CMP		DH,2
		JB		readloop	;DH<2라면 readloop로
		MOV		DH,0
		ADD		CH,1
		CMP		CH,CYLS
		JB		readloop	;CH<CYLS면 readloop로

 

 

Makefile 수정하기

지금 왜하는건지 설명해주지 않는데

haribote.sys 파일을 만들어서 haribote.img에다가 집어넣는다고 한다. 왜하는진 모르겠는데

 

make file 수정하다보니 알겟는게

초기 프로그램 로더 ipl과 os 본체 haribote.sys를 분리시켜 어셈블한다음 

하나의 img에 합치려고하는듯하다.

 

수정된 makefile

TOOLPATH = ../z_tools/
MAKE     = $(TOOLPATH)make.exe -r
NASK     = $(TOOLPATH)nask.exe
EDIMG    = $(TOOLPATH)edimg.exe
IMGTOL   = $(TOOLPATH)imgtol.com
COPY     = copy
DEL      = del

# 디폴트 동작

default :
	$(MAKE) img

# 파일 생성 규칙

ipl.bin : ipl.nas Makefile
	$(NASK) ipl.nas ipl.bin ipl.lst

haribote.sys : haribote.nas Makefile
	$(NASK) haribote.nas haribote.sys haribote.lst

haribote.img : ipl.bin haribote.sys Makefile
	$(EDIMG)   imgin:../z_tools/fdimg0at.tek \
		wbinimg src:ipl.bin len:512 from:0 to:0 \
		copy from:haribote.sys to:@: \
		imgout:haribote.img

# 커맨드

asm :
	$(MAKE) ipl.bin

img :
	$(MAKE) haribote.img

run :
	$(MAKE) img
	$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin
	$(MAKE) -C ../z_tools/qemu

install :
	$(MAKE) img
	$(IMGTOL) w a: haribote.img

clean :
	-$(DEL) ipl.bin
	-$(DEL) ipl.lst
	-$(DEL) haribote.sys
	-$(DEL) haribote.lst

src_only :
	$(MAKE) clean
	-$(DEL) haribote.img

 

 

 

작성한 os 본체

기존에 ipl.nas 에도 fin이 있던데 문제되지 않나 싶은데

잘은 모르겟지만 ipl.nas 에선 fin앞에까지만 읽어서 문제없는듯하다.

생각해보니 makefile에서 512바이트만 읽도록 하기도 했엇고.

fin:
        HLT
        JMP     fin

 

 

정상동작 확인

 

 

 

 

헥스에디터로보면 0x01ff에 55aa가 들어가있어서 부트섹터가 제대로 들어가있는것같고

 

 

 

 

os본체 파일명은 0x002600에

파일 내용은 0x004200이후에 있다는데 뭘 잘못햇는지 보이질않는다.

 

 

 

 

아무튼 지나가서

OS 본체 동작 확인

320x200x8bit 컬러 모드로 띄우는데 기존 그대로 나온다.

; haribote-os
; TAB=4

        ORG     0xc200      ;프로그램 로딩될곳

        MOV     AL,0x13     ;VGA 그래픽스, 320x200x8bit컬러
        MOV     AH,0x00
        INT     0x10

fin:
        HLT
        JMP     fin

 

 

기존의 ipl.nas next 레이블 끝에

haribote.sys로 넘어가기위한 코드를 안넣어둿기 때문

 

next:
		MOV		AX,ES		;어드레스를 0x200 더한다.
		ADD		AX,0x0020	
		MOV		ES,AX		;ADD ES, 0x020이 없어서 이렇게함
		ADD		CL,1		;CL +=1
		CMP		CL,18		;CL과 18비교
		JBE		readloop	; CL <= 18 이면 readloop로
		MOV		CL,1
		ADD		DH,1
		CMP		DH,2
		JB		readloop	;DH<2라면 readloop로
		MOV		DH,0
		ADD		CH,1
		CMP		CH,CYLS
		JB		readloop	;CH<CYLS면 readloop로


		MOV		[0x0ff0],CH	;IPL이 어디까지 읽엇는지 메모
		JMP		0xc200

 

0xc200으로 넘어가서 동작하면 QEMU가 검은 스크린이 정상적으로 나온다.

 

 

 

 

0xc200인 이유

- 파일 내용이 0x4200 번지부터 시작됨

- 부트섹터 맨앞이 0x8000에 오도록 지정됨

- 0x8000 + 0x4200 = 0xc200의 메모리를 읽어들이기 때문이라는데 0x8000이 어느 부분에서 그렇게 된건지 못찾겟다.

 

 

 

바이오스 다뤄보기

- 바이오스가 16비트 기계어로 되어있어 32비트 모드에선 사용 불가. 32비트 컴파일러(c언어) 쓰기전에 BIOS 설정부터 해보자.

haribote.nas에 부트 정보랑 키보드 LED 상태 저장하도록 수정

; haribote-os
; TAB=4

;BOOT_INFO
CYLS    EQU     0x0ff0      ;부트섹터가 설정
LEDS    EQU     0x0ff1
VMODE   EQU     0x0ff2      ;색수에 관한 정보
SCRNX   EQU     0x0ff4      ;screen x resolution
SCRNY   EQU     0x0ff6      ;screen y resolution
VRAM    EQU     0x0ff8      ;그래픽버퍼의 개시번지


        ORG     0xc200      ;프로그램 로딩될곳

        MOV     AL,0x13     ;VGA 그래픽스, 320x200x8bit컬러
        MOV     AH,0x00
        INT     0x10

        MOV     BYTE [VMODE],8;화면 모드 메모
        MOV     WORD [SCRNX], 320
        MOV     WORD [SCRNY], 200
        MOV     DWORD [VRAM], 0x000a0000

; 키보드 LED 상태를 BIOS가 알려줌
        MOV     AH,0x020
        INT     0x16        ;keyboard BIOS
        MOV     [LEDS],AL
fin:
        HLT
        JMP     fin

 

 

 

c언어 진입하기 부분이 문제다.

기존에 haribote.nas를 수정한 asmhead.nas가 복잡한것도 그런데

bootpack.c를 기계어로 만드는부분 설명이 요란하다.

내가 전에 어떻게 이걸했지?

일단 asmhead.nas

; haribote-os boot asm
; TAB=4

BOTPAK	EQU		0x00280000		; bootpack의 로드 장소
DSKCAC	EQU		0x00100000		; 디스크 캐쉬 프로그램의 장소
DSKCAC0	EQU		0x00008000		; 디스크 캐쉬 프로그램의 장소(리얼모드)

; BOOT_INFO 관계
CYLS	EQU		0x0ff0			; boot sector가 설정한다
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2			; 색 가지수에 관한 정보.어떤 비트 칼라인가?
SCRNX	EQU		0x0ff4			; 해상도의 X
SCRNY	EQU		0x0ff6			; 해상도의 Y
VRAM	EQU		0x0ff8			; 그래픽 버퍼의 개시 번지

		ORG		0xc200		; 이 프로그램이 어디에 Read되는가

; 화면 모드를 설정

		MOV		AL, 0x13	; VGA 그래픽스, 320 x200x8bit 칼라
		MOV		AH,0x00
		INT		0x10
		MOV		BYTE [VMODE], 8	; 화면 모드를 메모 한다(C언어가 참조한다)
		MOV		WORD [SCRNX],320
		MOV		WORD [SCRNY],200
		MOV		DWORD [VRAM],0x000a0000

; 키보드의 LED 상태를 BIOS가 가르쳐 준다

		MOV		AH,0x02
		INT		0x16 		; keyboard BIOS
		MOV		[LEDS],AL

; PIC가 일절의 세치기를 받아들이지 않게 한다
;	AT호환기의 사양에서는, PIC의 초기화를 한다면,
;	진한 개를 CLI앞에 해 두지 않으면 이따금 행업 한다
;	PIC의 초기화는 나중에 한다

		MOV		AL,0xff
		OUT		0x21,AL
		NOP				; OUT 명령을 연속하면 잘 되지 않는 기종이 있는 것 같기 때문에
		OUT		0xa1,AL

		CLI				; CPU 레벨에서도 인터럽트 금지

; CPU로부터 1MB이상의 메모리에 액세스 할 수 있도록, A20GATE를 설정

		CALL	waitkbdout
		MOV		AL,0xd1
		OUT		0x64,AL
		CALL	waitkbdout
		MOV		AL, 0xdf	; enable A20
		OUT		0x60,AL
		CALL	waitkbdout

; 프로텍트 모드 이행

[INSTRSET "i486p"]				; 486명령까지 사용하고 싶다고 하는 기술

		LGDT	[GDTR0]			; 잠정 GDT를 설정
		MOV		EAX,CR0
		AND		EAX, 0x7fffffff	; bit31를 0으로 한다(페이징 금지를 위해)
		OR		EAX, 0x00000001	; bit0를 1로 한다(프로텍트 모드 이행이므로)
		MOV		CR0,EAX
		JMP		pipelineflush
pipelineflush:
		MOV		AX,1*8		; read, write 가능 세그먼트(segment) 32bit
		MOV		DS,AX
		MOV		ES,AX
		MOV		FS,AX
		MOV		GS,AX
		MOV		SS,AX

; bootpack의 전송

		MOV		ESI, bootpack	; 전송원
		MOV		EDI, BOTPAK	; 전송처
		MOV		ECX,512*1024/4
		CALL	memcpy

; 하는 김에 디스크 데이터도 본래의 위치에 전송

; 우선은 boot sector로부터

		MOV		ESI, 0x7c00	; 전송원
		MOV		EDI, DSKCAC	; 전송처
		MOV		ECX,512/4
		CALL	memcpy

; 나머지 전부

		MOV		ESI, DSKCAC0+512; 전송원
		MOV		EDI, DSKCAC+512	; 전송처
		MOV		ECX,0
		MOV		CL,BYTE [CYLS]
		IMUL	ECX,512*18*2/4		; 실린더수로부터 바이트수/4에 변환
		SUB		ECX,512/4	; IPL분만큼 공제한다
		CALL	memcpy

; asmhead에서 해야 하는 것은 전부 다 했으므로,
;	나머지는 bootpack에 맡긴다

; bootpack의 기동

		MOV		EBX,BOTPAK
		MOV		ECX,[EBX+16]
		ADD		ECX, 3		; ECX += 3;
		SHR		ECX, 2		; ECX /= 4;
		JZ		skip		; 전송 해야 할 것이 없다
		MOV		ESI,[EBX+20]	; 전송원
		ADD		ESI,EBX
		MOV		EDI,[EBX+12]	; 전송처
		CALL	memcpy
skip:
		MOV		ESP,[EBX+12]	; 스택 초기치
		JMP		DWORD 2*8:0x0000001b

waitkbdout:
		IN		 AL,0x64
		AND		 AL,0x02
		IN		 AL, 0x60	; 빈 데이터 READ(수신 버퍼가 나쁜짓을 못하게)
		JNZ		waitkbdout	; AND결과가 0이 아니면 waitkbdout에
		RET

memcpy:
		MOV		EAX,[ESI]
		ADD		ESI,4
		MOV		[EDI],EAX
		ADD		EDI,4
		SUB		ECX,1
		JNZ		memcpy		; 뺄셈 한 결과가 0이 아니면 memcpy에
		RET
; memcpy는 주소 사이즈 prefix 넣는 것을 잊지 않으면, string 명령에서도 쓸 수 있다

		ALIGNB	16
GDT0:
		RESB	8			; null selector
		DW		0xffff, 0x0000, 0x9200, 0x00cf	; read/write 가능 세그먼트(segment) 32bit
		DW		0xffff, 0x0000, 0x9a28, 0x0047	; 실행 가능 세그먼트(segment) 32 bit(bootpack용)

		DW		0
GDTR0:
		DW		8*3-1
		DD		GDT0

		ALIGNB	16
bootpack:

 

 

 

그래도 길이가 작은 부분은 직접 쳐보자

먼저 bootpack.c

void io_hlt(void);

void HariMain(void)
{

fin:
    io_hlt();
    goto fin;
}

 

naskfunc.nas

;naskfunc
;TAB=4

[FORMAT "WCOFF"]        ;오브젝트 파일 만드는 모드
[BITS 32]               ;32비트모드용 기계어 만듬

;오브젝트 파일ㅇ 위한 정보
[FILE "naskfunc.nas"]   ;소스파일명 정보
    GLOBAL  _io_hlt     ;이프로그램에 포함된 함수명

; 실제 함수

[SECTION .text]         ;오브젝트 파일에서는 이것을 쓴후에 프로그램을 쓴다.
_io_hlt:   ;void io_hlt(void);
    HLT
    RET

 

 

 

 

makefile도 너무 길어졋으므로 그냥 복붙해서 사용

TOOLPATH = ../z_tools/
INCPATH  = ../z_tools/haribote/

MAKE     = $(TOOLPATH)make.exe -r
NASK     = $(TOOLPATH)nask.exe
CC1      = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet
GAS2NASK = $(TOOLPATH)gas2nask.exe -a
OBJ2BIM  = $(TOOLPATH)obj2bim.exe
BIM2HRB  = $(TOOLPATH)bim2hrb.exe
RULEFILE = $(TOOLPATH)haribote/haribote.rul
EDIMG    = $(TOOLPATH)edimg.exe
IMGTOL   = $(TOOLPATH)imgtol.com
COPY     = copy
DEL      = del

# 디폴트 동작

default :
	$(MAKE) img

# 파일 생성 규칙

ipl10.bin : ipl10.nas Makefile
	$(NASK) ipl10.nas ipl10.bin ipl10.lst

asmhead.bin : asmhead.nas Makefile
	$(NASK) asmhead.nas asmhead.bin asmhead.lst

bootpack.gas : bootpack.c Makefile
	$(CC1) -o bootpack.gas bootpack.c

bootpack.nas : bootpack.gas Makefile
	$(GAS2NASK) bootpack.gas bootpack.nas

bootpack.obj : bootpack.nas Makefile
	$(NASK) bootpack.nas bootpack.obj bootpack.lst

naskfunc.obj : naskfunc.nas Makefile
	$(NASK) naskfunc.nas naskfunc.obj naskfunc.lst

bootpack.bim : bootpack.obj naskfunc.obj Makefile
	$(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \
		bootpack.obj naskfunc.obj
# 3MB+64KB=3136KB

bootpack.hrb : bootpack.bim Makefile
	$(BIM2HRB) bootpack.bim bootpack.hrb 0

haribote.sys : asmhead.bin bootpack.hrb Makefile
	copy /B asmhead.bin+bootpack.hrb haribote.sys

haribote.img : ipl10.bin haribote.sys Makefile
	$(EDIMG)   imgin:../z_tools/fdimg0at.tek \
		wbinimg src:ipl10.bin len:512 from:0 to:0 \
		copy from:haribote.sys to:@: \
		imgout:haribote.img

# 커맨드

img :
	$(MAKE) haribote.img

run :
	$(MAKE) img
	$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin
	$(MAKE) -C ../z_tools/qemu

install :
	$(MAKE) img
	$(IMGTOL) w a: haribote.img

clean :
	-$(DEL) *.bin
	-$(DEL) *.lst
	-$(DEL) *.gas
	-$(DEL) *.obj
	-$(DEL) bootpack.nas
	-$(DEL) bootpack.map
	-$(DEL) bootpack.bim
	-$(DEL) bootpack.hrb
	-$(DEL) haribote.sys

src_only :
	$(MAKE) clean
	-$(DEL) haribote.img

 

 

 

검정화면만 나와서 모르겟지만 정상동작하는듯하다.

 

 

 

 

 

day03.zip
0.02MB

플로피 디스크에서 512바이트까지가 부트섹터였는데

nask로 부트섹터 만들고, 나머지는 이미지 툴로 만듬

 

 

기존에 어셈블 코드

asm.bat

..\z_tools\nask.exe helloos.nas helloos.img

 

을 bin과 lst (기계어로 번역되는 명령 목록) 출력되도록 수정

..\z_tools\nask.exe ipl.nas ipl.bin ipl.lst

 

making.bat

ipl.bin으로 helloos.img 만듬

edimg.exe로 ipl.bin 먼저 놓고 helloos.img 이미지 출력

..\z_tools\edimg.exe   imgin:../z_tools/fdimg0at.tek   wbinimg src:ipl.bin len:512 from:0 to:0   imgout:helloos.img

 

 

 

ipl.bin에는 부트섹터내용만 들어가므로

helloos3의 맨 끝에 부트섹터외는 제외되어있음.

 

기존 helloos3 ipl.nas
;hello-os
;TAB

ORG 0x7c00      ;메모리내 어디에 로딩되는지

JMP entry
DB  0x90

DB "HELLOIPL" ; boot sector이름을 자유롭게 써도 좋다(8바이트)
DW 512 ; 1섹터 크기(512로 해야 함)
DB 1 ; 클러스터 크기(1섹터로 해야 함)
DW 1 ; FAT가 어디에서 시작될까(보통 1섹터째부터)
DB 2 ; FAT 개수(2로 해야 함)
DW 224 ; 루트 디렉토리 영역의 크기(보통 224엔트리로 해야 한다)
DW 2880 ; 드라이브 크기(2880섹터로 해야 함)
DB 0xf0 ; 미디어 타입(0xf0로 해야 함)
DW 9 ; FAT영역 길이(9섹터로 해야 함)
DW 18 ; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
DW 2 ; 헤드 수(2로 해야 함)
DD 0 ; 파티션을 사용하지 않기 때문에 여기는 반드시 0
DD 2880 ; 드라이브 크기를 한번 더 write
DB 0,0,0x29 ; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
DD 0xffffffff ; 아마, 볼륨 시리얼 번호
DB "HELLO-OS   " ; 디스크 이름(11바이트)
DB "FAT12   " ; 포맷 이름(8바이트)
RESB 18 ; 우선 18바이트를 비어 둔다

;프로그램 본체

entry:
    MOV AX, 0      ;레지스터 초기화
    MOV SS,AX
    MOV SP,0x7c00
    MOV DS,AX
    MOV ES,AX

    MOV SI,msg

putloop:
    MOV AL,[SI]
    ADD SI,1        ;si에 1더함
    CMP AL,0
    JE  fin
    MOV AH,0x0e     ;한문자 표시기능
    MOV BX,15       ;컬러코드
    INT 0x10        ;비디오BIOS호출
    JMP putloop

fin:
    HLT             ;cpu정지
    JMP fin         ;무한루프

msg:
    DB  0x0a, 0x0a  ;줄바꿈문자2개
    DB  "hello, world"
    DB  0x0a        ;줄바꿈문자2개
    DB  0

    RESB 0x7dfe-$   ;0x7dfe까지 0x00으로 채우기
    DB  0x55, 0xaa

; boot sector 이외부분 기술

DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB    4600
DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB    1469432
; hello-os
; TAB=4

ORG 0x7c00 ; 이 프로그램이 어디에 read되는가

; 이하는 표준적인 FAT12 포맷 플로피 디스크를 위한 기술

JMP entry
DB 0x90
DB "HELLOIPL" ; boot sector이름을 자유롭게 써도 좋다(8바이트)
DW 512 ; 1섹터 크기(512로 해야 함)
DB 1 ; 클러스터 크기(1섹터로 해야 함)
DW 1 ; FAT가 어디에서 시작될까(보통은 1섹터째부터)
DB 2 ; FAT 개수(2로 해야 함)
DW 224 ; 루트 디렉토리 영역의 크기(보통은 224엔트리로 한다)
DW 2880 ; 드라이브 크기(2880섹터로 해야 함)
DB 0xf0 ; 미디어 타입(0xf0로 해야 함)
DW 9 ; FAT영역의 길이(9섹터로 해야 함)
DW 18 ; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
DW 2 ; 헤드 수(2로 해야 함)
DD 0 ; 파티션을 사용하지 않기 때문에 여기는 반드시 0
DD 2880 ; 드라이브 크기를 한번 더 write
DB 0,0,0x29 ; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
DD 0xffffffff ; 아마, 볼륨 시리얼 번호
DB "HELLO-OS   " ; 디스크 이름(11바이트)
DB "FAT12   " ; 포맷 이름(8바이트)
RESB 18 ; 우선 18바이트를 비어 둔다

; 프로그램 본체

entry:
MOV AX, 0 ; 레지스터 초기화
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV ES,AX

MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI, 1 ; SI에 1을 더한다
CMP AL,0
JE fin
MOV AH, 0x0e ; 한 글자 표시 Function
MOV BX, 15 ; 칼라 코드
INT 0x10 ; 비디오 BIOS 호출
JMP putloop
fin:
HLT ; 무엇인가 있을 때까지 CPU를 정지시킨다
JMP fin ; Endless Loop

msg:
DB 0x0a, 0x0a ; 개행을 2개
DB "hello, world"
DB 0x0a ; 개행
DB 0

RESB 0x7dfe-$ ; 0x7dfe까지를 0x00로 채우는 명령

DB 0x55, 0xaa 

 

 

 

 

부트섹터 이외부분이 제거되어있는데 왜 정상동작하는지는 모르겠다.

거의 0으로 채우는거긴했는데

생각해보니 msg 맨 뒷부분에 0x7fde까지 0으로 채우고 마지막에 0x55,0xaa를 넣어놧구나

512바이트 맨 마지막에 0x55,0xaa가 있으면 부팅가능 디스크로 판단했다고 정리했었다.

 

 

 

메이크파일 만들기

Makefile

# 파일 생성 규칙
ipl.bin : ipl.nas Makefile
	..\z_tools\nask.exe ipl.nas ipl.bin ipl.lst

helloos.img : ipl.bin Makefile
	..\z_tools\edimg.exe	imgin:..\z_tools\fdimg0at.tek \
	wbinimg src:ipl.bin len:512 from:0 to:0 imgout:helloos.img

 

make -r helloos.img 로 

bin과 lst 생성 후 img까지 생성

 

 

 

 

 

make file 개선

make run만 해주면

img 만들고 실행

소스만 남기도록 비우는 코드도 추가

# 파일 생성 규칙
ipl.bin : ipl.nas Makefile
	..\z_tools\nask.exe ipl.nas ipl.bin ipl.lst

helloos.img : ipl.bin Makefile
	..\z_tools\edimg.exe	imgin:..\z_tools\fdimg0at.tek \
	wbinimg src:ipl.bin len:512 from:0 to:0 imgout:helloos.img


img :
	..\z_tools\make.exe -r helloos.img


asm:
	..\z_tools\make.exe -r ipl.bin

run :
	..\z_tools/make.exe img
	copy helloos.img ..\z_tools\qemu\fdimage0.bin
	..\z_tools\make.exe -C ..\z_tools\qemu

install :
	..\z_tools\make.exe img
	..\z_tools\imgtol.com w a: helloos.img

clean :
	-del ipl.bin
	-del ipl.lst

src_only :
	..\z_tools\make.exe clean
	-del helloos.img

 

 

 

day02.zip
0.00MB

 

 

이번에 사용할 helloos3 이미지 어셈블리파일

 

;hello-os
;TAB

ORG 0x7c00      ;메모리내 어디에 로딩되는지

JMP entry
DB  0x90

DB		"HELLOIPL"		; boot sector이름을 자유롭게 써도 좋다(8바이트)
DW		512			; 1섹터 크기(512로 해야 함)
DB		1			; 클러스터 크기(1섹터로 해야 함)
DW		1			; FAT가 어디에서 시작될까(보통 1섹터째부터)
DB		2			; FAT 개수(2로 해야 함)
DW		224			; 루트 디렉토리 영역의 크기(보통 224엔트리로 해야 한다)
DW		2880			; 드라이브 크기(2880섹터로 해야 함)
DB		0xf0			; 미디어 타입(0xf0로 해야 함)
DW		9			; FAT영역 길이(9섹터로 해야 함)
DW		18			; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
DW		2			; 헤드 수(2로 해야 함)
DD		0			; 파티션을 사용하지 않기 때문에 여기는 반드시 0
DD		2880			; 드라이브 크기를 한번 더 write
DB		0,0,0x29		; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
DD		0xffffffff		; 아마, 볼륨 시리얼 번호
DB		"HELLO-OS   "		; 디스크 이름(11바이트)
DB		"FAT12   "		; 포맷 이름(8바이트)
RESB	18				; 우선 18바이트를 비어 둔다

;프로그램 본체

entry:
    MOV AX, 0      ;레지스터 초기화
    MOV SS,AX
    MOV SP,0x7c00
    MOV DS,AX
    MOV ES,AX

    MOV SI,msg

putloop:
    MOV AL,[SI]
    ADD SI,1        ;si에 1더함
    CMP AL,0
    JE  fin
    MOV AH,0x0e     ;한문자 표시기능
    MOV BX,15       ;컬러코드
    INT 0x10        ;비디오BIOS호출
    JMP putloop

fin:
    HLT             ;cpu정지
    JMP fin         ;무한루프

msg:
    DB  0x0a, 0x0a  ;줄바꿈문자2개
    DB  "hello, world"
    DB  0x0a        ;줄바꿈문자2개
    DB  0

    RESB 0x7dfe-$   ;0x7dfe까지 0x00으로 채우기
    DB  0x55, 0xaa

; boot sector 이외부분 기술

DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB    4600
DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB    1469432

 

 

실행결과

 

 

 

어셈블리 정리

ORG : 여기부터 프로그램 시작점, 여기 기준으로 로드됨

JMP : goto

entry : 레이블 선언

MOV AX,0 : AX에 0 대입

 

16비트 레지스터들

AX(누산기), CX(카운터), DX(데이터), BX(베이스), SP(스텍포인터), BP(베이스 포인터), SI(소스인덱스), DI(목적인덱스)

 

세그먼트 레지스터들

ES 엑스트라 세그먼트, CS 코드 세그먼트, SS 스택세그먼트, DS 데이터세그먼트

 

 

ORG와 레이블 이해

ORG로 0x7c00에서 시작

entry는 0x7c50

msg는 0x7c74

MOV SI, msg => SI 소스인덱스 레지스터에 0x7c74 대입

 

 

[]이해하기

MOV WORD [678], 123 : 678, 679번지에 123 입력(리틀엔디언식)

데이터크기 [번지]

ex : BYTE [SI], WORD[BX], SI에 987이 있을떄 : BYTE[987] == BYTE[SI]

 

MOV AL, [SI] 이해하기

MOV AL, BYTE[SI]에서 데이터크기 BYTE 생략(AL은 8비트 레지스터이므로 크기 생략가능)

MOV AL, [SI] : SI번지의 1바이트를 AL로 대입하라

 

ADD 연산

ADD SI, 1 : SI = SI + 1

 

CMP 비교 연산

CMP AL, 0

JE fin 

AL == 0 이면 fin으로 가라

 

INT

- 인터럽트

- INT 주소 : 주소 함수 호출

- INT 0x10 : 16번 비디오제어 함수 호출

 

BIOS 문자 표시 적기

- AH = 0x0e;

- AL = 캐릭터 코드

- BH = 컬러코드

- 리턴 : 없음

 

putloop 파트와 같이 보기

- AL 누산기LOW에 SI 소스인덱스 레지스터 번지 값 대입

- SI = SI +1

- 누산기 LOW가 0이면 fin으로

- MOV AH, 0x0e로 문자 표 시설정

- MOV BX,15로 컬러 설정

putloop:
    MOV AL,[SI]
    ADD SI,1        ;si에 1더함
    CMP AL,0
    JE  fin
    MOV AH,0x0e     ;한문자 표시기능
    MOV BX,15       ;컬러코드
    INT 0x10        ;비디오BIOS호출
    JMP putloop

 

 

 

helloos2에서 DB로 작성한 어셈블리 코드가

좌측과 같이 고쳐졌는데

putloop 부분이 잘 이해되지 않는다.

 

SI 소스 인덱스 레지스터에다가 msg 레이블 넣고

SI주소값을 AL에다 대입

SI 다음 주소값을 AL에다 대입

계속반복

msg 마지막엔 db 0가 있으므로

putloop CMP AL,0이 true가 되는 때가 온다.

 

 

아 JMP msg가 아니라

MOV SI, msg라 

entry: 레이블 다음에 그대로 putloop에 진행한듯보인다.

 

helloos3 helloos2

 

 

 

 

 

 

이전 글에서 내가 무엇을 했느냐,,,

전에 쓴 글을보면서 

헥스에디터로 이미지파일 만들고 qemu로 돌리기까지 했었다.

 

책 설명이 제대로 안되있는데 내가 어떻게 이걸했을까 생각하다보니 한빛미디어 자료실에 

올라와있는 자료를 보고 했었던것같다

책 링크 걸어둔다.

 

https://m.hanbit.co.kr/store/books/book_view.html?p_code=B9833754652

 

OS 구조와 원리: OS 개발 30일 프로젝트

저자가 수년 동안 개발하여 완성한 OS를 독자와 함께 30일 프로젝트로 함께 만들어가는 내용이다. OS는 플로피디스크로도 구동할 수 있고 우리가 사용하고 있는 윈도우 XP와 같은 OS에서 에뮬레이

m.hanbit.co.kr

https://dw.hanbit.co.kr/exam/1482/

 

Index of /exam/1482

 

dw.hanbit.co.kr

 

 

아무튼 다음으로 할일은 어셈블리를 쓰려고한다.

헥스에디터대신 텍스트에디터로 만들고

nasm 대신 저자가 만든 nask로 어셈블하면 이전처럼 쓸수있다고한다.

 

오랜만에 인성훈련을 위해 어셈블리 그대로 쓰고

(이전 글 헥스 에디터 사진과 같이 보면 내용 동일함)

 

이미지 파일로 빌드

 

 

 

 

방금 nask로 빌드한 이미지가 정상적으로 실행된걸 확인했다!

 

 

뜻정리하면

DB : data byte 1바이트 쓰기

RESB : reserve byte 숫자만큼 0x00으로 채우기

 

 

 

어셈블리 코드 개선 결과

; 는 주석

DW : data word 워드로 18비트, 2바이트

DD : data double word 더블 워드로 4바이트

 

 

 

 

 

추가 용어 정리

FAT12 포맷 : 플로피 디스크 포멧 형식

부트섹터 

 - 플로피디스크 처음섹터, 플로피는 512바이트씩 읽음

 - 플로피는 1,440kb로 512바이트로 나누면 2880섹터로 구성

 * 부팅가능한지 보기위해 첫 섹터의 맨뒤 2바이트를 봄. 마지막이 55 AA가 아니면 부팅할수 없다고 판단.

 =>이게 무슨소린가 싶어서 헥스에디터로보니 첫 섹터 512바이트 맨 끝부분에 55 AA가 들어가있음

IPL : initial program loader 초기 프로그램 읽기 장치, 보통 OS 로드하기위한 프로그램 위치

부트 : 부트스트랩의 약어, 자신의 힘으로 이룬다는 의미

+ Recent posts