;=============================================================================
; kernel.asm - Example operating system kernel.
;
; Copyright (C) 1992-93, Dennis Marer               File created: 12/21/92
;                                                   Last modified: 1/27/93
;
; Description:
;  This file represents the kernel of some fictional operating system.  At
;  this point, it supplies only basic functionality, just as an example of
;  the 386 Protected Mode and 32-bit code segments.
;
; Portability:
;  Requires Turbo Assembler 1.0 (TASM) or better to be assembled.
;  Dependent on the IBM PC 386 or better platform and its standard devices.
;
; Modifications:
;  DPM, 1/27/93 Finished the first revision of this file.
;=============================================================================

                .386p

                include kernel.inc
                include 386p.inc                ; 386 Protected mode
                include ibmpc.inc               ; IBM PC hardware



;============================ Kernel data segment ============================

KernelData      segment para use32 'KernelData'



;-----------------------------------------------------------------------------
; Messages and general data.
;-----------------------------------------------------------------------------

msgDivide       db      'Divide error, system halted.',13,0
msgNMI          db      'NMI, system halted.',13,0
msgOverflow     db      'Overflow error, system halted.',13,0
msgBounds       db      'Bound error, system halted.',13,0
msgOpcode       db      'Opcode error, system halted.',13,0
msgMathGone     db      'Math coprocessor gone error, system halted.',13,0
msgDouble       db      'Double fault error, system halted.',13,0
msgMathOver     db      'Math coprocessor overrun error, system halted.',13,0
msgTSS          db      'Invalid TSS error, system halted.',13,0
msgSegment      db      'Segment present error, system halted.',13,0
msgStack        db      'Stack fault error, system halted.',13,0
msgGeneral      db      'General protection error, system halted.',13,0
msgPage         db      'Page fault error, system halted.',13,0
msgMathErr      db      'Math coprocessor error, system halted.',13,0
kernelMsg       db      'Welcome to 80386 Protected Mode!',13,0
intrMsg         db      'This is generated by the 8253 timer interrupt: ',0
statusMsg       db      'This is generated by an infinite loop: ',0
megMsg          db      'Memory above 1 megabyte has been accessed!',13,0
hex                 db      '0x',0

clock           dd      0

cursorX         dw      0
cursorY         dw      0



;-----------------------------------------------------------------------------
; Stacks.
;-----------------------------------------------------------------------------

                align   16
                dw      256 dup (?)
stackI          equ     $

                dw      256 dup (?)
stack0          equ     $

                dw      128 dup (?)
stack1          equ     $

                dw      128 dup (?)
stack2          equ     $

                dw      256 dup (?)             ; This is the initial (level
stack3          equ     $                       ; 3) VM stack

initStack       label   dword                   ; Initial PM stack
                dw      stackI
                dw      KernelData

KernelData      ends



;========================= Basic Display functions ===========================

Kernel          segment para use32 'Kernel'
                assume  CS:Kernel,DS:KernelData



;-----------------------------------------------------------------------------
; ClearScreen - Clear the screen, reset the cursor location.
;
; No registers modified.
;-----------------------------------------------------------------------------

ClearScreen     proc    near

                push    AX                      ; Store the used registers
                push    CX
                push    EDI
                push    ES

                mov     AX,selScreen            ; Put the screen segment
                mov     ES,AX                   ;   into ES
                mov     CX,80*25                ; Get ready to clear each
                mov     AX,0700h                ;   character on the screen
                xor     EDI,EDI
                rep stosw

                mov     AX,selKDS               ; Put the data segment
                mov     ES,AX                   ;   into ES
                mov     ES:cursorX,0            ; Reset the cursor location
                mov     ES:cursorY,0            ;   back to the origin

                pop     ES                      ; Restore the used registers
                pop     EDI
                pop     CX
                pop     AX
                ret

ClearScreen     endp



;-----------------------------------------------------------------------------
; OutChar - Output a character to the screen, and update the cursor location.
;
; Parameters:
;  AL           - The character to be output.
;
; No registers modified.
;-----------------------------------------------------------------------------

OutChar         proc    near

                push    AX                      ; Store the used registers
                push    BX
                push    CX
                push    EDI
                push    DS
                push    ES
                push    AX

                mov     AX,selKDS               ; Put the data segment
                mov     DS,AX                   ;   into FS

                mov     AX,selScreen            ; Put the screen segment
                mov     ES,AX                   ;   into ES

                mov     BX,cursorY              ; Load the current cursor
                mov     CX,cursorX              ;   position into CX,BX

                mov     AL,80                   ; Calculate the address of
                mul     BL                      ;   the character and place
                add     AX,CX                   ;   this into EDI
                movzx   EDI,AX
                shl     EDI,1

                pop     AX                      ; Get the character
                cmp     AL,13                   ; New line character?
                je      OutcharLine

                mov     AH,07h                  ; Add character attributes
                mov     ES:[EDI],AX             ; Write character to screen

                inc     CX                      ; Increment the X location
                cmp     CX,80                   ; If the line length has been
                je      OutcharLine             ;   exceeded, go to next
                mov     cursorX,CX              ; Store line X location
                jmp     OutcharDone

OutcharLine:    inc     BX                      ; Increment the cursor Y
                mov     cursorX,0               ;   location and clear the
                mov     cursorY,BX              ;   X location

OutcharDone:    pop     ES                      ; Restore the used registers
                pop     DS
                pop     EDI
                pop     CX
                pop     BX
                pop     AX
                ret

Outchar         endp



;----------------------------------------------------------------------------
; Message - Write a message to the screen at the current cursor location.
;  The cursor location is updated to the last character of the message;
;
; Parameters:
;  DS:ESI       - A pointer to the null-terminated message to be displayed.
;
; Registers modified:
;  None.
;-----------------------------------------------------------------------------

Message         proc    near

                push    AX                      ; Store the used registers
                push    ESI

                xor     AH,AH                   ; Clear AH for comparisons

MessageLoop:    lodsb                           ; Load a character
                cmp     AL,AH                   ; See if its the NULL char,
                je      MessageDone             ;   through if found
                call    Outchar                 ; Output the character
                jmp     MessageLoop

MessageDone:    pop     ESI                     ; Restore the used registers
                pop     AX
                ret

Message         endp



;-----------------------------------------------------------------------------
; Hexout - Output the 16-bit number stored in AX in hexidecimal format.
;
; Parameters:
;  AX           - The 16-bit value to be output.
;
; No registers modified.
;-----------------------------------------------------------------------------

Hexout          proc    near

                push    AX                      ; Store the used registers
                push    BX
                push    ECX

                mov     BX,selKDS               ; Output the string '0x' to
                mov     DS,BX                   ;   indicate a hexidecimal
                mov     ESI,offset hex          ;   number is printed
                call    Message

                mov     BX,AX                   ; Copy the 16-bit value
                mov     ECX,4                   ; Set count to 4 digits
HexoutLoop:     rol     BX,4                    ; Put next digit into view
                mov     AL,BL                   ; Copy into AL, then mask
                and     AL,0Fh                  ;   out all but 4 bits
                cmp     AL,9                    ; If it's a decimal number
                jle     HexoutDec               ;   jump ahead
                add     AL,'A'-'9'-1            ; Compensate for alphabet

HexoutDec:      add     AL,'0'                  ; Convert into ASCII
                call    Outchar                 ; Output the character
                loop    HexoutLoop

                pop     ECX                     ; Restore the used registers
                pop     BX
                pop     AX
                ret

Hexout          endp



;-----------------------------------------------------------------------------
; HexoutL - Output the 32-bit number stored in EAX in hexidecimal format.
;
; Parameters:
;  EAX          - The 32-bit value to be output.
;
; No registers modified.
;-----------------------------------------------------------------------------

HexoutL         proc    near

                push    EAX                     ; Store the used registers
                push    EBX
                push    ECX
                push    DS

                mov     BX,selKDS               ; Output the string '0x' to
                mov     DS,BX                   ;   indicate a hexidecimal
                mov     ESI,offset hex          ;   number is printed
                call    Message

                mov     EBX,EAX                 ; Copy the 32-bit value
                mov     ECX,8                   ; Set count to 8 digits
HexoutLLoop:    rol     EBX,4                   ; Put next digit into view
                mov     AL,BL                   ; Copy  into AL, then mask
                and     AL,0Fh                  ;   out all but 4 bits
                cmp     AL,9                    ; If it's a decimal number
                jle     HexoutLDec              ;   jump ahead
                add     AL,'A'-'9'-1            ; Compensate for alphabet

HexoutLDec:     add     AL,'0'                  ; Convert into ASCII
                call    Outchar                 ; Output the character
                loop    HexoutLLoop

                pop     DS                      ; Restore the used registers
                pop     ECX
                pop     EBX
                pop     EAX
                ret

HexoutL         endp



;============================ Interrupt Handlers =============================



;-----------------------------------------------------------------------------
; IntrDivide - Divide error (interrupt 0) handler.
;-----------------------------------------------------------------------------

IntrDivide:     mov     ESI,offset msgDivide
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrDebug - Debug exception (interrupt 1) handler.
;-----------------------------------------------------------------------------

IntrDebug:      iretd



;-----------------------------------------------------------------------------
; IntrNMI - Non-Maskable Interrupt (interrupt 2) handler.
;-----------------------------------------------------------------------------

IntrNMI:        jmp     Abort



;-----------------------------------------------------------------------------
; IntrBreakpoint - One byte interrupt (interrupt 3) handler.
;-----------------------------------------------------------------------------

IntrBreakpoint: iretd



;-----------------------------------------------------------------------------
; IntrOverflow - Interrupt on overflow (interrupt 4) handler.
;-----------------------------------------------------------------------------

IntrOverflow:   mov     ESI,offset msgOverflow
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrBounds - Array bounds check (interrupt 5) handler.
;-----------------------------------------------------------------------------

IntrBounds:     mov     ESI,offset msgBounds
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrOpcode - Illegal opcode (interrupt 6) handler.
;-----------------------------------------------------------------------------

IntrOpcode:     mov     ESI,offset msgOpcode
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrMathGone - Math coprocessor device not available (interrupt 7) handler.
;-----------------------------------------------------------------------------

IntrMathGone:   mov     ESI,offset msgMathGone
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrDouble - Double fault (interrupt 8) handler.
;-----------------------------------------------------------------------------

IntrDouble:     mov     ESI,offset msgDouble
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrMathOver - Math coprocessor segment overrun (interrupt 9) handler.
;-----------------------------------------------------------------------------

IntrMathOver:   mov     ESI,offset msgMathOver
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrTSS - Illegal TSS (interrupt 10) handler.
;-----------------------------------------------------------------------------

IntrTSS:        mov     ESI,offset msgTSS
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrSegment - Segment not present (interrupt 11) handler.
;-----------------------------------------------------------------------------

IntrSegment:    mov     ESI,offset msgSegment
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrStack - Stack fault (interrupt 12) handler.
;-----------------------------------------------------------------------------

IntrStack:      mov     ESI,offset msgStack
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrGeneral - General protection (interrupt 13) handler.
;-----------------------------------------------------------------------------

IntrGeneral:    mov     ESI,offset msgGeneral
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrPage - Page fault (interrupt 14) handler.
;-----------------------------------------------------------------------------

IntrPage:       mov     ESI,offset msgPage
                jmp     Abort



;-----------------------------------------------------------------------------
; IntrMathErr - Math coprocessor error (interrupt 16) handler.
;-----------------------------------------------------------------------------

IntrMathErr:    mov     ESI,offset msgMathErr
                jmp     Abort



;-----------------------------------------------------------------------------
; KernelClock - Kernel system clock interrupt (IRQ 0) handler.
;-----------------------------------------------------------------------------

KernelClock:    push    EAX                     ; Store the used registers
                push    BX
                push    CX
                push    ESI
                push    DS
                push    ES

                mov     AX,selKDS               ; Load the data segment
                mov     DS,AX

                mov     BX,cursorX              ; Save the cursor location
                mov     CX,cursorY

                mov     cursorX,0               ; Set a new location
                mov     cursorY,12

                mov     ESI,offset intrMsg      ; Print interrupt message
                call    Message

                mov     EAX,clock               ; Load the clock count
                inc     EAX                     ; Increment the clock count
                mov     clock,EAX               ;   and save it for later
                call    HexoutL                 ; Output the clock count

                mov     cursorX,BX              ; Restore the cursor location
                mov     cursorY,CX

                mov     AL,PIC_EOI              ; Signal to the PIC that the
                out     PIC1_CMND,AL            ;   interrupt was serviced

                pop     ES                      ; Restore the used registers
                pop     DS
                pop     ESI
                pop     CX
                pop     BX
                pop     EAX

                iretd



;-----------------------------------------------------------------------------
; Reserved - Intel reserved interrupt (interrupt 15, 17-32) handler.  Do
;  nothing except notify both PICs the interrupt has been serviced.
;-----------------------------------------------------------------------------

Reserved:       push    AX                      ; Store the used registers
                mov     AL,PIC_EOI              ; Signal both the slave and
                out     PIC1_CMND,AL            ;   master PIC the interrupt
                out     PIC2_CMND,AL            ;   has been serviced
                pop     AX                      ; Restore the used registers
                iretd



;-----------------------------------------------------------------------------
; Unused - Unused vector interrupt handler.  Do nothing except notify both
;  PICs the interrupt has been serviced.
;-----------------------------------------------------------------------------

Unused:         push    AX                      ; Store the used registers
                mov     AL,PIC_EOI              ; Signal both the slave and
                out     PIC1_CMND,AL            ;   master PIC the interrupt
                out     PIC2_CMND,AL            ;   has been serviced
                pop     AX                      ; Restore the used registers
                iretd



;-----------------------------------------------------------------------------
; Abort - One of the fault handlers has been called, print a message then
;  jump into an infinite loop.
;-----------------------------------------------------------------------------

Abort:          mov     AX,selKDS
                mov     DS,AX
                call    Message

AbortLoop:      jmp     AbortLoop



;=========================== Kernel Initialization ===========================



;-----------------------------------------------------------------------------
; Start up the kernel, load the stack and data segments, insure ESP is going
;  to be 32-bits, perform a few tests, print a few messages, then go into
;  an infinite loop to indicate we're running in protected mode.
;-----------------------------------------------------------------------------

KernelStart:    movzx   ESP,SP                  ; Ensure that ESP is 32-bits
                mov     AX,selKDS               ; Set up initial DS and SS
                mov     SS,AX
                mov     AX,selKDS
                mov     DS,AX
                mov     AX,selKernel            ; Set the current TSS
                ltr     AX

                mov     AX,selKDS               ; Set all selectors to
                mov     ES,AX                   ;   a valid value
                mov     FS,AX
                mov     GS,AX

                call    ClearScreen             ; Clear the screen

                sti                             ; Enable interrupts

                mov     ESI,offset kernelMsg    ; Print a welcome message
                call    Message

                mov     AX,sel1Meg              ; Even though this doesn't
                mov     ES,AX                   ;   prove anything, access
                xor     EBX,EBX                 ;   memory above 1 megabyte
                mov     EAX,ES:[EBX]

                mov     ESI,offset megMsg       ; Print 1 meg success message
                call    Message



                mov     ESI,offset statusMsg    ; Display a status message
                mov     cursorX,0               ; Set a good cursor location
                mov     cursorY,10
                call    Message
                xor     EAX,EAX                 ; Clear the counter

KernelLoop:     mov     cursorX,39              ; Set a good cursor location
                mov     cursorY,10
                call    HexoutL                 ; Display counter hex value
                inc     EAX                     ; Increment the counter and
                jmp     KernelLoop              ;   loop infinitely



Kernel          ends

                end
