
; Run in dos (not under windows) and it will take us to 32 bit protected mode

[ORG 0x100]         ; Reserve 256 bytes for dos


[BITS 16]           ; Dos is 16 bits


; assemble using 'nasm' assembler

; C:>nasm asm_2.asm -o test.exe

jmp entry           ; Jump to the start of our code

msg1 db 'Where good to go..$';

jumpOffset:
    dd go_pm
    dw 0x08

entry:
 
; Display a message showing where alive!

mov dx, msg1        ; register dx=msg1
mov ah, 9           ; register ah=9 -- the print string function
int 21h             ; dos service interrupt .. looks at register ah to figure out what to do

; Thanks from Brendan, as we have to make sure our GDTR points to the actual
; memory address, add code location and dos 0x100 onto our loaded offset 

    mov eax,0
    mov ax,cs
    shl eax,4
    add [gdtr+2],eax
    add [idtr+2],eax         ; set idtr and gdtr so it points to the 'real' address in memory
    add [jumpOffset],eax     ; do the same for our 32 pm addr

    add [int_start], eax     ; Set the value of int_start (our simple interrupt function) so
                             ; it has the "physical" address
    
    mov eax, [int_start]
    mov word [idt_start], ax
    shr eax,16
    mov word [idt_start+6], ax

  cli		    ; Clear or disable interrupts
  
  mov     al, 0x70
  mov     dx, 0x80
  out     dx, al      ; outb(0x80, 0x70) - disable NMI
          

  lgdt[gdtr]	    ; Load GDT
  
  lidt[idtr]       ; Load IDT
     
  mov eax,cr0	    ; The lsb of cr0 is the protected mode bit
  or al,0x01	    ; Set protected mode bit
  mov cr0,eax	    ; Mov modified word to the control register

jmp far dword [jumpOffset]  ;can't just use "jmp go_pm" as where in dos!


nop                 ; ignore - no operation opcodes :)
nop

align 4

;---------------------------------------------------------------------------
;                                 32 BIT
;---------------------------------------------------------------------------
; Once we reach here where in protected mode!  32 Bit!  Where not in
; the real world (mode) anymore :)
[BITS 32]
go_pm :

mov ax, 0x10        ; use our datasel selector ( alternatively mov ax, datasel )
mov ds, ax,
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax

nop

mov esp, 0afffh    ; we need a simple stack if where calling functions!

; to jump over a nop operation
;push eip
;call fnx ; can't use push eip, so we sneekily use call fnx which pushes eip onto the stack :)
;fnx:
;pop eax        ; 1
;add eax, 13    ; 5  0502000000
;jmp eax        ; 2  FFE0 

nop             ; 1  90
nop
nop
nop
nop


mov word [es: 0xb8000],0x740 ; put a char to the screen!...yeahh!
                             ; Where all okay here :)

; Small delay loop
; on a p4 2ghz its about 4 seconds :)
;-------------------------------------------------------------------
mov eax, 1000
xloop:

mov ebx, 200
yloop:

mov ecx, 100 
zloop:
      dec ecx     
      jne zloop  

      dec ebx     
      jne yloop  

      dec eax     
      jne xloop  
;------------------------------------------------------------------


; Force a call to interrupt 0!
;  Int 0x0           ; We call our interrupt 0 subroutine

; Do a divide by 0 error, so we force a call to our interrupt 0
  mov eax, 0
  mov ebx, 0
  div ebx            ; eax divided by ebx, and stored back in eax

;IMPORTANT
; when you trigger an interrupt, e.g. the divide by zero, the actual
; address at which the interrupt occured is saved on the stack.  So if
; we return from our interrupt routine iret, this will return us to 
; the place that caused the interrupt.
; So in this example, we add 2 bytes to our retrun address in our
; interrupt routine, so goes to the next piece of code in the list

; Here is what the hex code and mnemonic look like
;B800000000        mov eax,0x0    (size 5 bytes)
;BB00000000        mov ebx,0x0    (size 5 bytes)
;F7F3              div ebx        (size 2 bytes)
;12 bytes

nop
nop


mov byte [es:0xb8000], "L" ; poke a character into the graphics output screen
                           ; so we know we've come back from our interrupt and
                           ; we are in our loop forever :)


nop
nop


mov ebx, 5
mov ax,0x10
mov es,ax
mov byte [es:0xb8030], "G" ; poke a character into the graphics output screen


      

 


lp: jmp lp  ; loops here forever and ever...

; We use 16 bits here - as you'll notice we use dw and dd only,
; and out data will be packed together nice and tight.

[BITS 16]
align 4

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Our GDTR register value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

gdtr :
   dw gdt_end-gdt-1    ; Length of the gdt
   dd gdt	       ; physical address of gdt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This is the start of our gdt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4

gdt:    
gdt0 		       
   dd 0		      
   dd 0                
codesel:          
   dw 0x0ffff	      
   dw 0x0000	       
   db 0x00             	
   db 0x09a	      
   db 0x0cf	       
   db 0x00	       
datasel:        
   dw 0x0ffff	       
   dw 0x0000	      
   db 0x00	       
   db 0x092
   db 0x0cf
   db 0x00
gdt_end:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

align 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Our IDTR register value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

idtr :
   dw idt_end - idt_start - 1  ; Length of the idt
   dd idt_start        ; physical address of idt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This is the start of our idt - its actual value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
idt_start:
   dw int_test         ; Address of our interrupt function
   dw 0x0008	       ; selector
   db 0x00             ; control parameters
   db 0x8E             ; access details
   dw 0x0              ; higher 16bits of our int address
idt_end:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

BITS 32
; Offset values to our simple test interrupt
;-------------------------------------------------------------------------
int_start :
   dd int_test
   dw 0x08
 

align 4

; Nothing special, just a very basic interrupt function so we know something
; has happened
;-------------------------------------------------------------------------
[BITS 32]
int_test:
pushad
push es

nop  ; just ignore.. :)  no operations
nop

mov ebx, 5
mov ax,0x10
mov es,ax
mov byte [es:0xb8020], "R" ; poke a character into the graphics output screen
mov byte [es:0xb8022], "S" ; poke a character into the graphics output screen

pop es
popad

pop eax     ; get the return address on the stack
add eax, 2  ; increment it by 2, eg. div eax is 2 bytes long
push eax    ; push our new return address onto the stack so iret uses it :)
iret

;-------------------------------------------------------------------------

TIMES 0x500-($-$$) DB 0x90    ; And of course, this will make our file size
                             ; equal to 0x500 a nice round number -
                             ; 0x500 ... so if you assemble the file
                             ; you should find its that size exactly.


;-------------------------------------------------------------------------





