%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 ; /(나머지)
%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
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선
;naskfunc
;TAB=4
[FORMAT "WCOFF"] ;오브젝트 파일 만드는 모드
[INSTRSET "i486p"] ;486명령사용하고싶다 작성
[BITS 32] ;32비트모드용 기계어 만듬
;오브젝트 파일 위한 정보
[FILE "naskfunc.nas"] ;소스파일명 정보
GLOBAL _io_hlt,_write_mem8 ;이프로그램에 포함된 함수명
; 실제 함수
[SECTION .text] ;오브젝트 파일에서는 이것을 쓴후에 프로그램을 쓴다.
_io_hlt: ;voidio_hlt(void);
HLT
RET
_write_mem8: ; voidwrite_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
voidio_hlt(void);
voidwrite_mem8(int addr, int data);
voidHariMain(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 연산으로 줄무늬 만들기
voidio_hlt(void);
voidwrite_mem8(int addr, int data);
voidHariMain(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대신 포인터사용해줘도 지정가능
voidio_hlt(void);
voidwrite_mem8(int addr, int data);
voidHariMain(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;
}
; 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: ; voidio_hlt(void);
HLT
RET
_io_cli: ; voidio_cli(void);
CLI
RET
_io_sti: ; voidio_sti(void);
STI
RET
_io_stihlt: ; voidio_stihlt(void);
STI
HLT
RET
_io_in8: ; intio_in8(int port);
MOV EDX,[ESP+4] ; port
MOV EAX,0
IN AL,DX
RET
_io_in16: ; intio_in16(int port);
MOV EDX,[ESP+4] ; port
MOV EAX,0
IN AX,DX
RET
_io_in32: ; intio_in32(int port);
MOV EDX,[ESP+4] ; port
IN EAX,DX
RET
_io_out8: ; voidio_out8(int port, int data);
MOV EDX,[ESP+4] ; port
MOV AL,[ESP+8] ; data
OUT DX,AL
RET
_io_out16: ; voidio_out16(int port, int data);
MOV EDX,[ESP+4] ; port
MOV EAX,[ESP+8] ; data
OUT DX,AX
RET
_io_out32: ; voidio_out32(int port, int data);
MOV EDX,[ESP+4] ; port
MOV EAX,[ESP+8] ; data
OUT DX,EAX
RET
_io_load_eflags: ; intio_load_eflags(void);
PUSHFD ; PUSH EFLAGS의 의미
POP EAX
RET
_io_store_eflags: ; voidio_store_eflags(int eflags);
MOV EAX,[ESP+4]
PUSH EAX
POPFD ; POP EFLAGS의 의미
RET
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바이트를 비어 둔다
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바이트를 비어 둔다
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
- 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) 32bit(bootpack용)
DW 0
GDTR0:
DW 8*3-1
DD GDT0
ALIGNB 16
bootpack:
;naskfunc
;TAB=4
[FORMAT "WCOFF"] ;오브젝트 파일 만드는 모드
[BITS 32] ;32비트모드용 기계어 만듬
;오브젝트 파일ㅇ 위한 정보
[FILE "naskfunc.nas"] ;소스파일명 정보
GLOBAL _io_hlt ;이프로그램에 포함된 함수명
; 실제 함수
[SECTION .text] ;오브젝트 파일에서는 이것을 쓴후에 프로그램을 쓴다.
_io_hlt: ;voidio_hlt(void);
HLT
RET
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바이트를 비어 둔다
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바이트를 비어 둔다
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가 있으면 부팅가능 디스크로 판단했다고 정리했었다.
;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