굵게 표시된 부분이 오타가 수정된 부분입니다.


==== 1권의 오탈자 내용 ====

*. 50 Page, 2.1.1 Cygwin 설치 위에 여기서 잠깐 박스 추가

  GCC를 이용하여 개발환경을 처음부터 구축하는 것은 어려운 일입니다. 필자가 사용한 개발환경을 가상머신으로 구축해놓았으니, 가상머신을 활용하여 개발을 진행하면 번거로운 시행착오를 피할 수 있습니다. MINT64 OS 개발을 위한 가상머신은 필자의 웹사이트(http://www.mint64os.pe.kr)을 통해서 다운로드할 수 있습니다.


*. 79 Page, 아래에서 5 번째 줄 

보호 모드는 IA-32e 모드로 전환하려면 공식적으로 거쳐야 하는 모드로, 32비트 윈도우나 리눅스 OS가 동작하는 기본 모드입니다.


*. 80 Page, 위에서 6 번째 IA-3e 모드를 IA-32e 모드로 수정

IA-32e 모드


*. 80 Page, 아래에서 5 번째줄

리얼 모드에서 전환할 수 있는 모드는 공식적으로 보호 모드뿐이며, 보호 모드에서는 가상 8086 모드나 IA-32e 모드, 다시 리얼 모드로 전환할 수 있습니다. 시스템 관리 모드는 모든 모드에서 진입할 수 있고, 처리가 끝나 이전의 운영 모드로 복귀하거나 리셋을 통해 리얼 모드로 진입할 수 있습니다.


*. 80 Page, 아래에서 3 번째 줄

화살표가 연결되지 않은 리얼 모드에서 IA-32e 모드로 전환하는 것은 불가능하며, 무리하게 시도하면 리셋이나 예외가 발생할 수있습니다(실제 편법을 사용하면 가능하긴 하지만 여기서는 다루지 않습니다).


*. 83 Page, [그림 3-3]에서 128비트 XMM 레지스터(16개), 64비트 RIP, 64비트 RFLAGS, CR8 레지스터 부분 수정됨

[그림 3-3].PNG


*. 85 Page의 "여기서 잠깐"의 시작에서 6번째 줄

곱셈 명령은 AX와 오퍼랜드를 곱한 후, 그 결과를 DX:AX나 혹은 AX에 저장하도록 설계되었습니다.


*. 87 Page 아래에서 5번째 줄부터...

그렇다면 어떻게 해야 할까요? 프로세서 제조사에서는 이런 경우를 대비하여 무조건 분기 명령어(jmp)에 예외를 두었습니다.

즉, 무조건 분기 명령어의 오퍼랜드의 크기는 기본 64비트로 하여 전체 어드레스 범위에서 이동이 가능하도록 한 것입니다.

[그림 3-5]는 RIP 상대 어드레스를 사용하여 표현 가능한 어드레스 영역과 무조건 분기 명령어를 사용해서 접근할 수 있는 영역에 대해

나타낸 것입니다.


*. 88 Page [그림 3-5]

           [그림 3-5].png

*. 89 Page [표 3-4]에서 왼쪽 열에 있는 FS와 SS 사이에 GS를 추가


*. 102 Page 아래에서 4번째줄

최대로 표현 가능한 어드레스 범위가 256TB(2^48)까지라는 말입니다.


*. 113 Page 아래에서 8번째 줄

그 위쪽에서 두 파일은 a.c와 b.c로부터 생성되는 것을 알 수 있습니다.


*. 141 Page 위에서 6번째 줄, 154 Page 아래서 5번째 줄, 2861 Page 위에서 7번째 줄, 2872 Page 맨 밑줄

xor byte [ HEADNUMBER ], 0x01 ; 헤드 번호를 0x01과 XOR 하여 토글(0->1, 1->0)


*. 142 Page, 아래서 3번째줄

x86 프로세서의 스택은 [그림 5-3]과 같이 데이터가 삽입될 때마다 스택의 상위(Top)를 나타내는 스택 포인터 레지스터(SP)가 낮은 어드레스(0x00에 가까운 어드레스)로 이동합니다.


*. 143 Page, 밑에서 10째줄

또한 스택은 넉넉한 것이 좋으므로 스택 포인터 레지스터(SP)와 베이스 포인터 레지스터(BP)를 0xFFFF로 설정하여, 스택 영역의 크기를 세그먼트의 최대 크기로 지정하겠습니다.


*.149 Page, 아래에서 13번째 줄

        ret 12                   ; 호출한 함수로 복귀한 후, 스택에 삽입된 파라미터 3개를 제거(3*4) "ret" "add esp, 12"와 같은 역할

        ^^^^^^

            ret 수행 후 스택 포인터(SP)를 12만큼 더함. 보호모드 코드이므로

            레지스터의 기본 크기 및 스택의 기본 크기가 32비트(4바이트)이기 때문에 4*3 = 12만큼 더함


*.151 Page, 위에서 15번째 줄

        ret 4                     ; 호출한 함수로 복위한 후, 스택에 삽입된 파라미터 1개를 제거(1*4) "ret" "add esp, 4"와 같은 역할

        ^^^^^^^

             ret 수행 후 스택 포인터(SP)를 4만큼 더함. 보호 모드 코드이므로

             레지스터의 기본 크기와 스택의 기본 크기가 32비트(4바이트)이기 때문에 4*1 = 4만큼 더함


*. 161 Page, 아래서 5번째줄 Code

; 512 - ( $ - $$ ) % 512 : 현재부터 어드레스 512까지

 

*. 164 Page, 위에서 9번째줄

make -C 00.BootLoader clean

make -C 01.Kernel32 clean

rm -f Disk.img


*. 177 Page, 첫번째 줄

GDTR 및 GDT 정보 생성 코드

 

*. 189 Page, 밑에서 6째줄

"$<"라고 표시된 새로운 기호를 사용했는데, 이 매크로는 Dependency(:의 오른쪽)의 첫 번째 파일을 의미하는 매크로입니다.

 

*. 213 Page, 가장 아래번째 줄

보호 모드 엔트리 포인트(EntryPoint.s) 코드에서 최초로 실행되는 C 코드입니다.


*. 226 Page, 311 Page, WriteKernelInfomation() 함수에서 5번째 줄에 off_t 추가

        lPosition = lseek( iTargetFd, (off_t)5, SEEK_SET ); 


*. 251 Page, [그림 9-2]의 PML4 기준 주소페이지 디렉터리 포인터 테이블 기준 주소로 수정


*. 251 Page, [그림 9-2] 페이지 디렉터리 엔트리 부분에서 PS1을 PS로 수정

[그림 9-2].PNG


*. 261 Page, 위에서 4번째 줄

kSetPageEntryData( &( pstPML4TEntry[ 0 ] ), 0x00, 0x101000, PAGE_FLAGS_DEFAULT, 0 );


*. 263 Page, [그림 9-5]의 XMSE를 SMXE로 수정


[그림 9-5].PNG


*. 266 Page, 소스 코드에서 위에서 10째줄

// 1 비트 P, RW, US, PWT, PCD, A, D, PS, G, 3 비트 Avail, 1 비트 PAT, 8 비트 Reserved,


*. 283 Page, [표 10-2] IA32_EFER 레지스터의 비트 구성에서 7번째 줄 LMA 항목

LMA 읽기 전용 -1로 설정되면 프로세서 모드가 IA-32e 모드(호환 모드 또는 64비트 모드)임을 나타내며, 0으로 설정되면 프로세서 모드가 기타 모드임을 나타냄


*.283 Page, 밑에서 10째줄

[표 10-2]에서 보는 것과 같이 IA-32e 모드 활성화 여부는 비트 8에 위치하는 LME 비트와 관계가 있고 이 비트를 1로 설정하면 IA-32e 모드를 활성화할 수 있습니다.


*.288 Page [예제 10-2] 위에서 1째줄

#ifndef __MODESWITCH_H__

#define __MODESWITCH_H__


*.332 Page 밑에서 8번째 줄

bOutputPortData != 0x02;  - A20 게이트 활성화 비트(비트 1)를 1로 설정. 프로세서를 리셋하려면 bOutputPortData=0으로 수정


*. 333 Page 아래쪽과 351 Page 가운데 부분, 482 Page 윗부분

BOOL kChangeKeyboardLED( BOOL bCapsLockOn, BOOL bNumLockOn, BOOL bScrollLockOn )

{

    int i, j;


    // 키보드에 LED 변경 커맨드 전송하고 커맨드가 처리될 때까지 대기

    for( i = 0 ; i < 0xFFFF ; i++ )

    {

        // 입력 버퍼(포트 0x60)가 비었으면 커맨드 전송 가능

        if( kIsInputBufferFull() == FALSE )

        {

            break;

        }

    }

...   생략 ...


*. 334 Page 위에서 3번째 줄

// 입력 버퍼(포트 0x60)로 LED 상태 편경 커맨드(0xED) 전송

kOutPortByte( 0x60, 0xED );

... 생략 ...


*. 341 Page 위에서 15번째 줄, 357 Page 아래에서 5번째 줄

BOOL kIsUseCombinedCode( BYTE bScanCode )


*. 341 Page, 위에서 18번째 줄, 357 Page, 아래에서 2번째 줄

BOOL bUseCombinedKey = FALSE;


*. 343 Page 아래쪽359 Page 가운데 부분

void UpdateCombinationKeyStatusAndLED( BYTE bScanCode )

{

    BOOL bDown;

    BYTE bDownScanCode;

    BOOL bLEDStatusChanged = FALSE;


    // 눌림 또는 떨어짐 상태처리, 최상위 비트(비트 7)가 1이면 키가 떨어졌음을 의미하고

    // 0이면 눌림을 의미함

    if( bScanCode & 0x80 )

    {

        bDown = FALSE;

        bDownScanCode = bScanCode & 0x7F;

    }

    else

    {

        bDown = TRUE;

        bDownScanCode = bScanCode;

    }


*. p.363 페이지 (02.Kernel64/Source/Keyboard.h)의 Down을 On으로 변경
// 키보드의 상태를 관리하는 자료구조
typedef struct kKeyboardManagerStruct
{
    // 조합 키 정보
    BOOL bShiftDown;
    BOOL bCapsLockOn;
    BOOL bNumLockOn;
    BOOL bScrollLockOn;
    
    // 확장 키를 처리하기 위한 정보
    BOOL bExtendedCodeIn;
    int iSkipCountForPause;
} KEYBOARDMANAGER;


*. 363 Page, 아래에서 12번째 줄

BOOL kIsUseCombinedCode( BYTE bScanCode );


*. 365 Page, 아래에서 17번째줄 변수 선언 부분 굵게 처리

void Main( void )

{

    char vcTemp[ 2 ] = {0, };

    BYTE bFlags;

    BYTE bTemp;

    int i = 0;


    ... 생략 ...


*. 374 Page, 위에서 11번째줄

예외 처리에 할당한 0~31번을 제외한 나머지 224개는 OS가 임의로 사용할 수 있는 부분입니다.


*. 383 Page의 [그림 12-7]에서 16~31 비트의 값(크기를 기준 주소로 수정)

그림 12-7.PNG


*. 386 Page, 위에서 21번째 줄 30번째 줄

#define GDT_FLAGS_LOWER_TSS ( GDT_FLAGS_LOWER_DPL0 | GDT_FLAGS_LOWER_P

#define GDT_FLAGS_UPPER_TSS ( GDT_FLAGS_UPPER_G )


*. 388 Page, 밑에서 8번째 줄

kSetGDTEntry16( ( GDTENTRY16* ) &( pstEntry[ 3 ] ), (QWORD) pstTSS,


*. 398 Page, kSetGDTEntry8() 함수에서 5번째줄과 kSetGDTEntry16() 함수에서 5번째줄

pstEntry->bUpperLimitAndUpperFlag = ( ( dwLimit >> 16 ) & 0x0F ) |


*. 428 Page 아래에서 18번째 줄, 436 Page 아래에서 3번째 줄, 600 Page 아래에서 9번째 줄, 606 Page 위에서 7번째 줄

%macro KLOADCONTEXT 0      ; 파라미터를 전달받지 않는 KLOADCONTEXT 매크로 정의


*. 455 Page 아래에서 3번째 줄

키보드를 누를 때마다 [그림 13-3]의 아래와 같이 왼쪽 위에 INT 33과 발생 횟수가 표시됩니다.


*. 483 Page, 위에서 10번째 줄, 굵게 표시

// 키를 저장하는 큐와 버퍼 정의

static QUEUE gs_stKeyQueue;

static KEYDATA gs_vsKeyQueueBuffer[ KEY_MAXQUEUECOUNT ];


*. 492 Page의 위쪽

사실 14장은 앞 장과 비교해서 변한 것이 거의 없습니다.


*. 498 Page의 "GCC 헤더 파일에 정의된 가변 인자 매크로와 리스트"의 위쪽 문단

각 매크로와 데이터 타입이 어떻게 정의되어 있는지 코드를 직접 확인해 보겠습니다.


*. 609 Page, 첫 번째 줄  굵게 표시

{ "createtask", "Create Task", kCreateTestTask },


*. 620 Page의 [그림 18-3]

그림 18-3.png


*. 627 Page, "TCB 할당 및 해제 함수의 코드"에서 위로 2번째 줄

다음은 앞서 설명한 내용에 따라 구현한 TCB 할당 및 해제 함수의 코드입니다. ID를 처리하는 부분을 제외하고는 평이한 코드이므로

이해하는데 큰 어려움이 없을 것입니다. TCB 자료구조에 추가된 stLink 필드는 18.2.4절에서 살펴보겠습니다.


*. 633 Page, 맨 아래 줄

그리고 전환하기 전에 프로세서 시간을 새로 할당하는 것을 볼 수 있습니다.


*. 634 Page, 밑에서 11번째 줄

*. 650 Page, 밑에서 9번째 줄

    pstRunningTask = gs_stScheduler.pstRunningTask;
    kAddTaskToRunList( pstRunningTask );


    // 프로세서 사용 시간을 업데이트
    gs_stScheduler.iProcessorTime = TASK_PROCESSORTIME;


    // 다음 태스크를 현재 수행 중인 태스크로 설정한 후 콘텍스트 전환
    gs_stScheduler.pstRunningTask = pstNextTask;
    kSwitchContext( &( pstRunningTask->stContext ), &( pstNextTask->stContext ) );


    kSetInterruptFlag( bPreviousFlag );

}


*. 660 Page의 위에서 4째줄

콘솔 화면의 주변을 돌면서 문자를 출력하는 태스크와 자신의 ID에 해당하는 위치에 바람개비를 출력하는 태스크를 작성했습니다.


*. 702 Page, 밑에서 14번째 줄 kGetTCBInTCBPool 함수의 내부

if( ( iOffset < -1 ) || ( iOffset > TASK_MAXCOUNT ) ) 


*. 687 Page, 밑에서 19번째 줄

*. 699 Page, 위에서 15번째 줄

*. 736 Page, 밑에서 12번째 줄

*. 761 Page, 위에서 7번째 줄

// 유휴 태스크에서 전환되었다면 사용한 프로세서 시간을 증가시킴
if( ( pstRunningTask->qwFlags & TASK_FLAGS_IDLE ) = = TASK_FLAGS_IDLE )
{
    gs_qwSpendProcessorTimeInIdleTask +=
    TASK_PROCESSORTIME - gs_stScheduler.iProcessorTime;
}


// 프로세서 사용 시간을 업데이트
gs_stScheduler.iProcessorTime = TASK_PROCESSORTIME;


// 태스크 종료 플래그가 설정된 경우 콘텍스트를 저장할 필요가 없으므로, 대기 리스트에
// 삽입하고 콘텍스트 전환
if( pstRunningTask->qwFlags & TASK_FLAGS_ENDTASK )
{
    kAddListToTail( &( gs_stScheduler.stWaitList ), pstRunningTask );
    kSwitchContext( NULL, &( pstNextTask->stContext ) );
}
else
{
    kAddTaskToReadyList( pstRunningTask );
    kSwitchContext( &( pstRunningTask->stContext ), &( pstNextTask->stContext ) );
}


kSetInterruptFlag( bPreviousFlag );


*. 741 Page 위에서 7 번째 줄

태스크의 수를 제한하려면 임계 영역 진입 여부를 나타내는 플래그와 잠금 횟수를 나타내는 카운터, 그리고 임계 영역에 진입한 태스크의 ID만 있으면 처리할 수 있습니다.


*. 742 Page 아래에서 8번째 라인

만약 잠근 태스크가 해제를 요청하는 것이고 잠긴 횟수가 2 이상이면 중복으로 잠긴 것이므로 횟수만 1 감소시키고 실제로 해제하는 작업은 수행하지 않습니다.


*. 751 Page 그림 [20-4]

[그림 20-4].PNG


*. 769 Page 맨 윗 라인

콘솔 셸 파일에는 태스크를 생성하고 종료하는 태스트 때문에 killtask 커맨드의 기능이 수정되고 동기화 기능을 테스트하는 testmutex 커맨드가 추가되었습니다.


*. 791 Page의 밑에서 셋째줄

하지만 멀티코어 프로세서 환경일 경우 자식 스레드는 다른 코어에서 실행될 수 있으며, 이런 경우 태스크를 즉시 대기 리스트로 옮길 수 없습니다.


*. 815 Page의 밑에서 8째줄

[그림 21-4]의 위를 보면 콘솔 셸(태스크 ID: 0x100000000)과 유휴 태스크(태스크 ID: 0x200000001)의 태스크 플래그를 보면 최상위 바이트가 각기 0x60과 0x58로 시작하는 것을 볼 수 있습니다.


*. 825 Page [그림 22-2]

[그림 22-2].PNG


* 839 Page, 밑에서 9번째 줄

* 853 Page, 위에서 4번째 줄

// 다음에 수행할 태스크가 FPU를 쓴 태스크가 아니라면 TS 비트 설정
if( gs_stScheduler.qwLastFPUUsedTaskID != pstNextTask->stLink.qwID )
{
    kSetTS();
}
else
{
    kClearTS();
}


// 프로세서 사용 시간을 업데이트
gs_stScheduler.iProcessorTime = TASK_PROCESSORTIME;


// 태스크 종료 플래그가 설정된 경우 콘텍스트를 저장할 필요가 없으므로, 대기 리스트에
// 삽입하고 콘텍스트 전환
if( pstRunningTask->qwFlags & TASK_FLAGS_ENDTASK )
{
    kAddListToTail( &( gs_stScheduler.stWaitList ), pstRunningTask );
    kSwitchContext( NULL, &( pstNextTask->stContext ) );
}
else

{
    kAddTaskToReadyList( pstRunningTask );
    kSwitchContext( &( pstRunningTask->stContext ), &( pstNextTask->stContext ) );
}


// 임계 영역 끝
kUnlockForSystemData( bPreviousFlag );


* 843 Page, 밑에서 10 째 줄

또 FPU 예외 처리가 끝나면 다시 0으로 설정해서 같은 태스크가 FPU를 사용할 때 예외가 발생하지 않도록 해야 합니다.


* 876 Page, 밑에서 두번째 줄

1KB 블록 2개를 생성한 후 1KB 블록 하나를 할당해주면 남은 블록은 [그림 23-3]과 같이 1KB 블록 1개, 2KB 블록 1개, 4KB 블록 1개가 됩니다.


* 879 Page, 위에서 3번째 줄 그림 제목

[그림 23-6] 블록 크기에 맞지 않는 메모리를 할당받았을 때 할당된 메모리의 구조(4.3MB 할당)


* 886 Page, [그림 23-9]

4MB, 2MB, 1MB4KB, 2KB, 1KB로 변경


* 933 Page 위에서 7 째줄

하지만, LBA 어드레스 모드일 때는 레지스터가 LBA 어드레스로 통합되어 섹터 번호 레지스터는 LBA 어드레스의 0비트~7비트, 실린더 LSB 레지스터는 8비트~15비트, 실린더 MSB 레지스터는 16비트~23비트, 드라이브/헤드 레지스터의 헤드 필드는 24비트~27비트를 저장합니다.


* 1049 Page, 위에서 6번째 줄 자료구조에서 아래의 굵게된 부분 추가

typedef struct kFileHandleStruct
{
    // 파일이 존재하는 디렉터리 엔트리의 오프셋
    int iDirectoryEntryOffset;
    // 파일 크기
    DWORD dwFileSize;
    // 파일의 시작 클러스터 인덱스
    DWORD dwStartClusterIndex;
    // 현재 I/O가 수행중인 클러스터의 인덱스
    DWORD dwCurrentClusterIndex;
    // 현재 클러스터의 바로 이전 클러스터의 인덱스
    DWORD dwPreviousClusterIndex;
    // 파일 포인터의 현재 위치
    DWORD dwCurrentOffset;
} FILEHANDLE;


* 1067 Page 밑에서 17 째줄

kMemSet(gs_vbTempBuffer, 0, sizeof(gs_vbTempBuffer));


* 1107 Page 위에서 3 째줄, return NULL 부분을 아래와 같이 수정

return -1;


* 1108 Page 위에서 18 째줄

if( pstDirectoryBuffer == NULL )


* 1120 Page 위에서 6 째줄

// 파일 이름 삽입

kMemCpy( vcBuffer, pstentry->d_name, kStrLen( pstEntry->d_name ) );


* 1161 Page, [그림 27-5]의 가장 마지막 부분

[그림 27-5].PNG


*. 1174 Page, 위에서 11 번째 줄

iRealReadCount = MIN( gs_stRDDManager.dwTotalSectorCount -

dwLBA, iSectorCount );


*. 1174 Page, 밑에서 9 번째 줄

iRealWriteCount = MIN( gs_stRDDManager.dwTotalSectorCount -

dwLBA, iSectorCount );


*. 1186 Page, 밑에서 16 번째 줄

iRealReadCount = MIN( gs_stRDDManager.dwTotalSectorCount -

dwLBA, iSectorCount );

*. 1186 Page, 밑에서 2 번째 줄

iRealWriteCount = MIN( gs_stRDDManager.dwTotalSectorCount -

dwLBA, iSectorCount );


*. 1120Page, 밑에서 9 번째 줄

kPrintf( "\t\tTotal File Size: %d KByte (%d Cluster)\n", dwTotalByte / 1024,

dwUsedClusterCount );


*. 1239 Page, 위에서 10째줄과 13째줄

// LSB 제수 래치 레지스터(포트 0x3F8)에 제수의 하위 8비트를 전송

// MSB 제수 래치 레지스터(포트 0x3F9)에 제수의 상위 8비트를 전송


*. 1246 Page, 위에서 2째줄과 5째줄

// LSB 제수 래치 레지스터(포트 0x3F8)에 제수의 하위 8비트를 전송

// MSB 제수 래치 레지스터(포트 0x3F9)에 제수의 상위 8비트를 전송


*. 1251 Page, [예제 28-3]의 소스 부분
#include "FileSystem.h" <-- Normal 글씨체로 수정
#include "SerialPort.h" <-- Bold 글씨체로 수정

*. 1280 Page, 위에서 5번째줄, 맨 앞의 "-"를 "*"로 대체하고 위의 *와 같은 레벨로 들여쓰기 수정
* BIOS의 롬 영역 중에서 0x0F0000~ 0x0FFFFF 범위 내에 존재

* 1291 Page, [그림 29-13]에서 전달할 로컬 APIC INTIN을 전달할 로컬 APIC LINTIN으로 수정, 0x000x04로 수정
[그림 29-13].PNG  
* 1399 Page, [그림 31-1]에서 오른쪽 아래에 있는 "ㅁ: 비활성화 된 부분"의 사각형을 회색으로 변경
[그림 31-1].PNG

==== 2권의 오탈자 내용 ====

*. 1506 Page, 위에서 12번째 줄

… 현재 수행 중인 태스크 정보와 TS 비트를 설정하는 코드 …


// 프로세서 사용 시간을 업데이트
gs_stScheduler.iProcessorTime = TASK_PROCESSORTIME;


// 태스크 종료 플래그가 설정된 경우 콘텍스트를 저장할 필요가 없으므로, 대기 리스트에
// 삽입하고 콘텍스트 전환
if( pstRunningTask->qwFlags & TASK_FLAGS_ENDTASK )
{
    kAddListToTail( &( gs_stScheduler.stWaitList ), pstRunningTask );
    // 임계 영역 끝
    kUnlockForSpinLock( &( gs_stScheduler.stSpinLock ) );


    // 태스크 전환
    kSwitchContext( NULL, &( pstNextTask->stContext ) );
}
else
{
    kAddTaskToReadyList( pstRunningTask );
    // 임계 영역 끝
    kUnlockForSpinLock( &( gs_stScheduler.stSpinLock ) );


    // 태스크 전환
    kSwitchContext( &( pstRunningTask->stContext ), &( pstNextTask->stContext ) );
}


// 인터럽트 플래그 복원
kSetInterruptFlag( bPreviousInterrupt );


*. 1528 Page, 아래에서 11번째 줄

태스크 풀을 관리하는 함수를 수정하는 방법은 33.2.2절에서 살펴보므로, 이번 절에서는 수정된 태스크 풀 자료구조만 살펴보고 넘어가겠습니다.


*. 1539 Page, 위에서 7번째 줄, 1580 Page, 아래에서 12번째 줄

// 나머지 코어에서 현재 태스크와 같은 레벨을 검사


*. 1544 Page, 밑에서 5번째 줄

*. 1573 Page, 위에서 10번째 줄

// 다음에 수행할 태스크가 FPU를 쓴 태스크가 아니라면 TS 비트를 설정
if( gs_vstScheduler[ bCurrentAPICID ].qwLastFPUUsedTaskID !=
    pstNextTask->stLink.qwID )
{
    kSetTS();
}
else
{
    kClearTS();
}


// 프로세서 사용 시간을 업데이트
gs_vstScheduler[ bCurrentAPICID ].iProcessorTime = TASK_PROCESSORTIME;


// 태스크 종료 플래그가 설정된 경우 콘텍스트를 저장할 필요가 없으므로, 대기 리스트에
// 삽입하고 콘텍스트 전환
if( pstRunningTask->qwFlags & TASK_FLAGS_ENDTASK )
{
    kAddListToTail( &( gs_vstScheduler[ bCurrentAPICID ].stWaitList ), pstRunningTask );

    // 임계 영역 끝
    kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );


    // 태스크 전환
    kSwitchContext( NULL, &( pstNextTask->stContext ) );
}
else
{
    kAddTaskToReadyList( bCurrentAPICID, pstRunningTask );
    // 임계 영역 끝
    kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) );


    // 태스크 전환
    kSwitchContext( &( pstRunningTask->stContext ), &( pstNextTask->stContext ) );
}




// 인터럽트 플래그 복원
kSetInterruptFlag( bPreviousInterrupt );
return FALSE;


*. 1612 Page, 아래에서 2째줄

하지만, 7부가 지나면 곧 다시 돌아오니 너무 아쉬워 할 필요는 없습니다.


*. 1655 Page, 위에서 5번째 줄

브레슨햄 직선 알고리즘을 사용한 선 그리기 함수의 코드 - 최적화 후


*. 1672 Page, 35.2.1절에서 아래로 8번째줄

하지만, 문자 정보가 비트맵으로 들어 있으므로 확대하거나 축소했을 때 문자가 깨끗하게 표시되지 않고, 굵게하거나 기울일려면 해당 스타일의 비트맵을 모두 생성해야하는 단점이 있습니다.


*.1764 Page, 아래에서 10번째줄, kDrawMouse -> kDrawCursor로 수정

kDrawCursor( &stScreenArea, pstVideoMemory, iX, iY );


*. 1882 Page, 위에서 7번째 줄

이 절에서 작성할 윈도우 매니저는 더 이상 테스트 코드가 아닌 실제 코드로서, 키보드와 마우스 데이터를 이용해 윈도우로 이벤트를 전달하는 기능과 업데이트를 수행하는 기능을 구현합니다.


*. 1946 Page, 위에서 4번째 줄

//----------------------------------------------------------------------------------------------

// 제목 표시줄 그리기

//----------------------------------------------------------------------------------------------

// 제목 표시줄을 채움

        if( bSelectedTitle == TRUE )
        {
            stTitleBarColor = WINDOW_COLOR_TITLEBARACTIVEBACKGROUND;
        }
        else
        {
                stTitleBarColor = WINDOW_COLOR_TITLEBARINACTIVEBACKGROUND;

        }


kInternalDrawRect( &stArea, ... 생략 ...)


*. 1951 Page, 위에서 6번째 줄

39장의 내용을 MINT64 OS에 추가한 뒤 make를 입력하면 Disk.img 파일이 생성됩니다.


*. 1994 Page, 위에서 6번째 줄 제목

41.1.1 Z 순서의 가장 아래에서 위로 그리는 알고리즘의 문제점


*. 1998 Page, 위에서 3번째 줄

윈도우 1은 자신의 영역에 포함된 픽셀 오프셋 5~7 부분을 비디오 메모리로 전송한 뒤 해당 영역의 비트맵을 모두 0으로 표시합니다.


*. 2027 Page, 아래에서 3번째 줄

특히 마우스 데이터를 처리하는 부분과 화면 업데이트 요청을 처리하는 부분은 조금만 수정하면 화면 업데이트 횟수를 많이 줄일 수 있기 때문에 속도에 목마른 우리에겐 아주 중요한 포인트입니다.


*. 2150 Page, 위에서 첫번째 줄

43장에서는 프로세서의 정보와 메모리 정보를 표시하는 시스템 모니터 태스크를 구현해 봤습니다. 


*. 2150 Page, 위에서 6번째 줄

44장에서는 다시 고전으로 돌아가 역사의 뒤안길로 사라졌던 콘솔 셸을 GUI 버전으로 부활시킬 것입니다.


*. 2169 Page, 위에서 7번째 줄 도움말

while( kGetKeyFromGUIKeyQueue( &stData) == FALSE)

        |---------- 그래픽 모드용 키 큐에서 데이터를 꺼내는 함수


*. 2182 Page, "44.3.2 콘솔 파일 수정"에서 아래 첫번째 줄

콘솔 파일은 그래픽 모드 지원을 위해 입출력 함수의 코드를 일부 수정하고, 그래픽 모드용 화면 버퍼와 키 큐를 추가했습니다.


*. 2309 Page 전체를 아래 내용으로 교체2309 페이지의 내용.PNG


*. 2400 Page, 아래에서 4번째 줄

MINT64 OS에는 아직까지 MSR에 읽고 쓰는 함수가 없으므로 MSR에 관련된 어셈블리어 함수부터 만들겠습니다.


*. 2546 Page, "[표 50-2] e_entry 필드의 세부 구성과 의미"를 "[표 50-2] e_ident[16] 필드의 세부 구성과 의미"로 변경


*. 2546 Page, 아래에서 7번째 줄

가장 먼저 할 일은 e_ident 필드의 상위 4바이트가 매직 넘버이므로, 파일의 첫 4바이트가 매직 넘버와 일치하는지를 검사하여 올바른 ELF64 파일인지 확인하는 것입니다.


*. 2548 Page, 아래에서 5번째 줄

현재 섹션의 타입에 따라 필드의 의미가 달라지며 자세한 내용은 [표50-6]을 참조


*. 2616 Page, 위에서 첫번째 줄

[그림 51-2]는 이러한 처리 과정을 나타낸 그림입니다.


*. 2625 Page 위에서 14번째 줄,  2641 Page 아래에서 8번째줄, 2785 Page 밑에서 7번째줄, 

// 현재 라인에서 남은 문자 수와 한 라인에 출력할 수 있는 문자 수 를 비교하여 작은 것을 선택


*. 2692 Page, 위에서 12번째 줄

g_stGameInfo.iBlockX = -1;

g_stGameInfo.iBlockY = -1;


*. 2693 Page, 위에서 10번째 줄

게임판의 블록이 존재하는지 여부는 게임 자료구조의 vvbBoard 필드에 저장되어 있습니다.


*. 2694 Page, 위에서 5번째 줄

게임 자료구조에는 현재 게임판의 정보를 저장하는 vvbBoard 필드가 있으므로 움직이는 블록의 현재 좌표를 그대로 게임판에 저장하면 됩니다.


*. 2720 Page, 아래에서 13번째 줄

g_stGameInfo.iBlockX = -1;

g_stGameInfo.iBlockY = -1;


*. 2766 Page, 위에서 4번째 줄

텍스트 뷰어에 한글 출력 기능을 추가하려면 한글인지를 판단해 2바이트씩 출력하는 코드만 추가하면 되기 때문입니다.


*. 2846 Page, 위에서 1번째 줄

만든 첫 번째 한글 입력 모듈은 오토마타를 사용했습니다.


*. 2893 Page, 아래에서 6번째 줄

jbe READDATA


*. 2894 Page, 위에서 2번째 줄

ja .ADDTRACK


*. 2902 Page, 아래에서 8번째 줄

jbe READDATA


*.2902 Page, 아래에서 1번째 줄

ja .ADDTRACK


*. TODO...