Ⅰ. 1024 섹터 크기의 이미지를 메모리로 복사하는 코드

1. C언어

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

2. asm

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:       ;에러 처리하는 코드
    ```생략```

Ⅱ. 스택 초기화와 함수 구현

1. 스택 사용할 영역

2. 스택 초기화 코드

;stack을 0x0000:0000~0x0000:FFFF 영역에 64KB 크기로 생성
mov ax, 0x0000  ;스택 세그먼트의 시작 어드레스(0x0000)를 세그먼트 레지스터 값으로 변환
mov ss, ax      ;SS 세그먼트 레지스터에 설정
mov sp, 0xFFFE  ;SP 레지스터의 어드레스를 0xFFFE로 설정
mov bp, 0xFFFE  ;BP 레지스터의 어드레스를 0xFFFE로 설정

3. 메시지 출력 함수

;메시지 출력하는 함수  (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         ;함수를 호출한 다음 코드의 위치로 복원

Ⅲ. 보호 모드에서 사용되는 3가지 함수 호출 규약