#include <stdio.h>
int main(){
int iTotalSectorCount = 1024;
int iSectorNumber = 2;
int iHeadMumber = 0;
int iTrackNumber = 0;
// 실제 이미지를 복사할 address(물리주소)
char* pcTargetAddress = (char*) 0x1000;
while(1){
//전체 섹터 수를 하나씩 감소시키면서 0이 될때까지 섹터를 복사
if(iTotalSectorCount == 0){
break;
}
iTotalSectorCount = iTotalSectorCount - 1;
// 1섹터를 읽어들여서 메모리 어드레스에 복사
// BIOSReadOneSector: BIOS의 섹터 읽기 기능을 호출하는 임의의 함수
if(BIOSReadOneSector(iSectorNumber, iHeadNumber, iTrackNumber, pcTargetAddress) == ERROR){
HandleDiskError();
}
// 1섹터는 512(0x200) 바이트이므로, 복사한 섹터 수만큼 어드레스 중가
pcTargetAddress = pcTargetAddress + 0x200;
// 섹터 -> 헤드 -> 트랙 순으로 번호 증가
iSectorNumber = iSectorNumber + 1;
if(iSectorNumber < 19){
continue;
}
// 헤드번호는 0과 1을 반복하므로, XOR연산을 사용함
// 아래 코드는 iHeadNumber = (iHeadNumber == 0x00)? 0x01:0x00; 과 같은 의미
iHeadNumber = iHeadNumber ^ 0x01;
iSectorNumber = 1;
if(iHeadNumber != 0){
continue;
}
iTrackNumber = iTrackNumber + 1;
}
}
void HandleDiskError(){
printf("Disk Error-!!");
while( 1 );
}
TOTALSECTORCONT: dw 1024 ;부트로더를 제외한 MINT64 OS 이미지의 크기. 최대 1152 섹터(0x90000byte)까지 가능
SECTORNUMBER: db 0x02 ;OS 이미지가 시작하는 섹터 번호를 저장하는 영역
HEADNUMBER: db 0x00 ;OS 이미지가 시작하는 헤드 번호를 저장하는 영역
TRACKNUMBER: db 0x00 ;OS 이미지가 시작하는 트랙 번호를 저장하는 영역
;디스크의 내용을 메모리로 복사할 어드레스(ES:BX)를 0x10000으로 설정
mov si, 0x1000 ;OS 이미지를 복사할 어드레스(0x10000)를 세그먼트 레지스터 값으로 변환
mov es, si ;ES 세그먼트 레지스터에 값 설정
mov bx, 0x0000 ;BX 레지스터에 0x0000을 설정하여 복사할 어드레스를 0x1000:0000(0x10000)으로 최종 설정
mob di, word [TOTALSECTORCOUNT] ;복사할 os이미지의 섹터 수를 di레지스터에 설정
;디스크를 읽는 코드의 시작
READDATA: ;모든 섹터를 다 읽었는지 확인
cmp di, 0 ;복사할 OS 이미지의 섹터 수를 0과 비교
je READEND ;복사할 섹터수가 0이라면 다 복사했으므로 READEND로 이동
sub di, 0x1 ;복사할 섹터 수 1 감소
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; BIOS READ Function 호출
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov ah, 0x02 ;BIOS 서비스 번호 2 (Read Sector)
mov al, 0x1 ;읽을 섹터 수는 1
mov ch, byte [TRACKNUMBER] ;읽을 트랙 번호 설정
mov cl, byte [SECTORNUMBER] ;읽을 섹터 번호 설정
mov dh, byte [HEADNUMBER] ;읽을 헤드 번호 설정
mov dl, 0x00 ;읽을 드라이브 번호(0==Floppy) 설정
int 0x13 ;인터럽트 서비스 수행
jc HANDLEDISKERROR ;에러가 발생했다면 HANDELEDISKERROR로 이동
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 복사할 어드레스, 트랙, 헤드, 섹터 어드레스 계산
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
add si, 0x0020 ;512(0x200)바이트만큼 읽었으므로, 이를 세그먼트 레지스터 값으로 변환
mov es, si ;ES 세그먼트 레지스터에 더해서 어드레스를 한 섹터만큼 증가
;한 섹터를 읽었으므로 섹터 번호를 증가시키고 마지막 섹터(18)까지 읽었는지 판단
;마지막 섹터가 아니면 섹터 읽기로 이동해서 다시 섹터 읽기 수행
mov al, byte [SECTORNUMBER] ;섹터 번호를 AL레지스터 설정
add al, 0x1 ;섹터 번호를 1증가
mov byte [SECTORNUMBER], al ;증가시킨 섹터 번호를 SECTORNUMBER에 다시 설정
cmp al, 19 ;증가시킨 섹터 번호를 19와 비교
jl READDATA ;섹터 번호가 19미만이라면 READDATA로 이동
;마지막 섹터까지 읽었으면(섹터 번호가 19일 경우) 헤드를 토글 (0->1, 1->0)하고, 섹터 번호를 1로 설정
xor byte [HEADNUMBER], 0x1 ;헤드 번호를 0x01과 xor하여 토글(0->1, 1->0)
mov byte [SECTORNUMBER], 0x01 ;섹터 번호를 다시 1로 설정
;만약 헤드가 1->0으로 바뀌었다면 양쪽 헤드를 모두 읽은 것이므로 아래로 이동하여 트랙번호를 1 증가
cmp byte [HEADNUMBER], 0x00 ;헤드 번호를 0x00과 비교
jne READDATA ;헤드 번호가 0이 아니면 READDATA로 이동
;트랙을 1 증가시킨 후, 다시 섹터 읽기로 이동
add byte [TRACKNUMBER], 0x01 ;트랙 번호를 1 증가
READEND:
HANDELEDISKERROR: ;에러 처리하는 코드
```생략```
;stack을 0x0000:0000~0x0000:FFFF 영역에 64KB 크기로 생성
mov ax, 0x0000 ;스택 세그먼트의 시작 어드레스(0x0000)를 세그먼트 레지스터 값으로 변환
mov ss, ax ;SS 세그먼트 레지스터에 설정
mov sp, 0xFFFE ;SP 레지스터의 어드레스를 0xFFFE로 설정
mov bp, 0xFFFE ;BP 레지스터의 어드레스를 0xFFFE로 설정
화면에서 원하는 위치에 문자열을 출력하려면 X좌표, Y좌표, 출력할 문자열 어드레스가 필요함
필요한 3가지를 함수 파라미터로 정의하고 스택에 삽입하는 순서를 지정해야함
C언어 경우, 파라미터의 역순(오른쪽 → 왼쪽)으로 삽입하고 스택에서 꺼낸 순서가 파라미터 순서와 같게 함
Example
PrintMessage( iX, iY, pcString );
push word [pcString] ;문자열 어드레스를 스택에 삽입
push word [iY] ;화면의 Y좌표를 스택에 삽입
push word [iX] ;화면의 X좌표를 스택에 삽입
call PRINTMESSAGE ;PRINTMESSAGE 함수 호출
add sp, 6 ;2byte*3하여, 6만큼 sp 증가
push bp ;베이스 포인터 레지스터(BP)를 스택에 삽입
mov bp, sp ;베이스 포인터 레지스터(BP)에 스택 포인터 레지스터(SP)의 값 설정
;베이스 포인터 레지스터(BP)를 통해 파라미터에 접근할 목적
push es ;ES 세그먼트 레지스터부터 DX레지스터까지 스택에 삽입
push si ;함수에서 임시로 사용하는 레지스터로 함수의 마지막 부분에서
push di ;스택에 삽입된 값을 꺼내 원래 값으로 복원
push ax
push cx
push dx
```생략```
mov ax, word [bp+4] ;파라미터 1(iX, 화면 X좌표)
mov bx, word [bp+6] ;파라미터 2(iY, 화면 Y좌표)
mov cx, word [bp+8] ;파라미터 3(pcString, 출력할 문자열의 어드레스)
```생략```
pop dx ;함수에서 사용이 끝난 DX 레지스터부터 ES레지스터까지 스택에 삽입된 값을 이용해서 복원
pop cx ;스택은 가장 마지막에 들어간 데이터가 가장 먼저 나오는 자료구조이므로
pop ax ;삽입의 역순으로 제거해야함
pop di
pop si
pop es
pop bp ;베이스 포인터 레지스터(BP) 복원
ret ;함수를 호출한 다음 코드의 위치로 복원
PRINTMESSAGE함수의 코드
;메시지 출력하는 함수 (PARAM: x좌표, y좌표, 문자열)
PRINTMESSAGE:
push bp ;베이스 포인터 레지스터(BP)를 스택에 삽입
mov bp, sp ;베이스 포인터 레지스터(BP)에 스택 포인터 레지스터(SP)의 값 설정
;베이스 포인터 레지스터(BP)를 통해 파라미터에 접근할 목적
push es ;ES 세그먼트 레지스터부터 DX레지스터까지 스택에 삽입
push si ;함수에서 임시로 사용하는 레지스터로 함수의 마지막 부분에서
push di ;스택에 삽입된 값을 꺼내 원래 값으로 복원
push ax
push cx
push dx
; ES 세그먼트 레지스터에 비디오 모드 어드레스 설정
mov ax, 0xB800 ;비디오 메모리 시작 어드레스(0x0B8000)를 세그먼트 레지스터 값으로 변환
mov es, ax ;ES 세그먼트 레지스터에 설정
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; X, Y의 좌표로 비디오 메모리의 어드레스를 계산함
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Y좌표를 이용해서 라인 어드레스를 먼저 구함
mov ax, word [bp+6] ;파라미터 2(화면 Y좌표)를 AX레지스터에 설정
mov si, 160 ;한 라인의 바이트 수(2*80 컬럼)을 SI레지스터에 설정
mul si ;AX레지스터와 SI레지스터를 곱하여, 화면 Y어드레스 계산
mov di, ax ;계산된 화면 Y어드레스를 DI레지스터에 설정
;X좌표를 이용해서 2를 곱한 후, 최종 어드레스 구함
mov ax, word [bp+4] ;파라미터 1(화면 X좌표)를 AX레지스터에 설정
mov si, 2 ;한 문자를 나타내는 바이트수(2)를 SI레지스터에 설정
mul si ;AX레지스터와 SI레지스터를 곱하여, 화면 X어드레스를 계산
add di, ax ;화면 y어드레스와 계산된 X 어드레스를 더해서 실제 비디오 메모리 어드레스 계산
; 출력한 문자열의 어드레스
mov si, word [bp+8] ;파라미터 3(출력한 문자열의 어드레스)
.MESSAGELOOP: ;메시지를 출력하는 루프
mov cl, byte [si] ;SI레지스터가 가르키는 문자열 위치에 한 문자를 CL레지스터에 복사.
;CL레지스터는 CX레지스터의 하위 1byte를 의미
cmp cl, 0 ;복사된 문자와 0을 비교
je .MESSAGEEND ;복사한 문자의 값이 0이면 문자열이 종료되었음을 의미하며, .MESSAGEEND로 이동하여 문자 출력 종료
mov byte [es:di], cl ;0이 아니라면 비디오 메모리 어드레스 0xB800:di에 문자를 출력
add si, 1 ;SI레지스터에 1을 더하여, 다음 문자열로 이동
add di, 2 ;DI레지스터에 2을 더하여, 비디오 메모리의 다음 문자 위치로 이동
;비디오 메모리는 (문자, 속성)의 쌍으로 구성되므로 문자만 출력하려면 2를 더해야함
jmp .MESSAGELOOP ;메시지 출력 루프로 이동하여 다음 문자 출력
.MESSAGEEND:
pop dx ;함수에서 사용이 끝난 DX 레지스터부터 ES레지스터까지 스택에 삽입된 값을 이용해서 복원
pop cx ;스택은 가장 마지막에 들어간 데이터가 가장 먼저 나오는 자료구조이므로
pop ax ;삽입의 역순으로 제거해야함
pop di
pop si
pop es
pop bp ;베이스 포인터 레지스터(BP) 복원
ret ;함수를 호출한 다음 코드의 위치로 복원