;Loaded by the bootloader
;This will load the highlevel vmm code at the highest memory
;location possible, and setup the system to protected and paged mode

BITS 16
GLOBAL amain
amain:

;should be 3000:0000
;load vmm code into memory
;
;
jmp real_entry


column:
db 0

row:
db 0

display16:
;cl=byte to send
push ax
push dx

mov dl, byte [ds:bp+column-begin]
mov dh, byte [ds:bp+row-begin]


mov ax,0xb800
mov es,ax

mov al,dh
mov ah,160
imul ah ; ax=ah*al  (row*160)

;add the column
push dx
and dx,0x00ff ;remove the row part of the dx register
shl dx,1 ;multiply by 2
add ax, dx
pop dx

xchg si,ax

;mov si,0


mov byte [es:si], cl ;//dl ;cl
mov byte [es:si+1], 15

;mov byte [es:si+2], dh
;mov byte [es:si+3], 15

xchg si,ax


inc dl
cmp dl, 80
jae nextrow
jmp display16_done

nextrow:
mov dl,0 ;set column to 0
inc dh ;go to next row

cmp dh,25
jb display16_done

mov dh,0


display16_done:
mov byte [ds:bp+column-begin], dl
mov byte [ds:bp+row-begin], dh
pop dx
pop ax
ret


;some 16-bit general purpose functions
sendstring16:
;ds:si=pointer to string
mov cl,[si]
call send16

inc si
cmp byte [si],'$'
jne sendstring16
ret


send16:

;param cl=byte to send
push ax
push dx

waitforready:
%ifdef SERIALPORT
%if SERIALPORT != 0
mov dx,SERIALPORT+5 ;3fdh
in al,dx
and al,0x20
cmp al,0x20
jne waitforready

mov dx,SERIALPORT ;0x3f8
mov al,cl
out dx,al
%endif
%endif



pop dx
pop ax
ret

inttostr16:
;ax = value
;di = pointer to buffer
;si = start of string (output)
;cx = max length of buffer
push ax
push bx
push cx
push di


mov si,di
add si,cx
dec si
mov byte [si],'$'
dec si

;worker
inttostr16_worker:
cmp si,di
jb inttostr16_end ; end of stringspace

xor dx,dx
mov bx,10
div bx

add dl,030h
mov [si],dl
dec si

cmp ax,0
jne inttostr16_worker

mov cx,si
inc cx

inttostr16_fillspace:
cmp si,di
jb inttostr16_end
mov byte [si],0x20
dec si

jmp inttostr16_fillspace



inttostr16_end:
mov si,cx

pop di
pop cx
pop bx
pop ax

ret



inttohex16:
;ax = value
;di = pointer to buffer
;si = start of string (output)
;cx = max length of buffer
push ax
push bx
push cx
push di


mov si,di
add si,cx
dec si
mov byte [si],'$' ;make this 0
dec si

;worker
inttohex16_worker:
cmp si,di
jb inttohex16_end ; end of stringspace

xor dx,dx
mov bx,16
div bx

cmp dl,09h
ja alpha
add dl,'0' ; add from '0'
jmp writehex

alpha:
sub dl,0ah
add dl,'a' ; add from 'a'



writehex:
mov [si],dl
dec si

cmp ax,0
jne inttohex16_worker

mov cx,si
inc cx

inttohex16_fillspace:
cmp si,di
jb inttohex16_end
mov byte [si],0x20
dec si

jmp inttohex16_fillspace



inttohex16_end:
mov si,cx

pop di
pop cx
pop bx
pop ax

ret

generatestring16:


str88: db '88=$'
stre801: db 'e801:$'
stre820: db 'e820:',13,10,'$'
strax: db 'ax=$'
strbx: db 'bx=$'
strcx: db 'cx=$'
strdx: db 'dx=$'
strsmaperror: db 'SMAP ERROR!',13,10,'$'
tempbuf: db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

newline: db 13,10,'$'

GLOBAL reservedmem_listcount
reservedmem_listcount: dd 0

GLOBAL readerror
readerror: dd 0

global sectorsread
sectorsread: dd 0

global a20state
a20state: dd 0

global bootdisk
bootdisk: dd 0

global SectorsPerTrack
SectorsPerTrack:	dd 0

global NumberOfHeads
NumberOfHeads:		dd 0

global NumberOfCylinders
NumberOfCylinders: 	dd 0

vesa_info_success:
  db "get VESA info success",13,10,"$"

vesa_info_fail:
  db "get VESA info failed",13,10,"$"

str_failed_getting_data:
  db "   Failure"

str_videomode:
  db "videomode 0x$"



checka20:
  push ds
  push es

  xor ax,ax
  mov ds,ax
  dec ax
  mov es,ax

  mov ax,[es:10h]       ; read word at FFFF:0010 (1 meg)
  not ax                ; 1's complement
  push word [0]         ; save word at 0000:0000 (0)
  mov [0],ax            ; word at 0 = ~(word at 1 meg)
  mov ax,[0]            ; read it back
  cmp ax,[es:10h]       ; fail if word at 0 == word at 1 meg
  pop word [0]

  pop es
  pop ds
  je checka20_noa20 ;it's the same

  mov ax,1
  ret

checka20_noa20:
  mov ax,0
  ret
;^^^^^^^^^^^^^^^^^^^^^

retry: db 'retry reading disk',13

waitforkeypress:
%ifdef SERIALPORT
%if SERIALPORT != 0
mov dx,SERIALPORT+5 ;0x3fd
waitforkeypress2:
in al,dx
and al,1
cmp al,1
jne waitforkeypress2
mov dx,SERIALPORT ;0x3f8
in al,dx
%endif
%endif
ret


real_entry:

;ok

;setup rs232 (serial port communications)



;ok

;-----------------------Real Entry point--------------------



mov ax,0x8000
cli
mov ss,ax
mov sp,0xfff0
sti

xor ebp,ebp
xor esi,esi
mov ax,cs
mov ds,ax
mov es,ax

start:
call begin
begin:
pop bp ; bp now contains the address of begin

;ok



lea di,[ds:bp+reservedmem_listcount-begin]
cmp byte [ds:di],0
jne gdtsetup

mov [ds:bp+bootdisk-begin],dl ;save bootdisk

cmp byte [bp+reservedmem_listcount-begin],0
jne gdtsetup ;skip setting up hardware and enumerating memory, it's already done

%ifdef SERIALPORT
%if SERIALPORT !=0
;jmp serialsetupdone
mov dx,SERIALPORT+1 ;3f9h
mov al,0h
out dx,al

mov dx,SERIALPORT+3; 3fbh
mov al,80h
out dx ,al ;access baud rate generator

mov dx,SERIALPORT; 3f8h
mov al,1h  ;0c=9600 (1152000/divisor)
out dx,al ;9600 baud

mov dx,SERIALPORT+1; 3f9h
mov al,0h ;high part of devisor
out dx,al ;

mov dx,SERIALPORT+3; 3fbh
mov al,3h
out dx,al ;8 bits, no parity, one stop
%endif
%endif

serialsetupdone:



;begin with setting up diskinfo
mov ax,0x6000
mov es,ax
xor di,di

mov ax,0x0800
mov dl,[ds:bp+bootdisk-begin]
int 13h

inc dh
mov [ds:bp+NumberOfHeads-begin],dh
mov ax,cx
and ax,3fh
inc ax
mov [ds:bp+SectorsPerTrack-begin],ax
mov al,ch
shr ch,6
mov ah,ch
mov [ds:bp+NumberOfCylinders-begin],ax


;enumerate the memory regions that can not be used by dbvm
;int 15h

;init buffer
mov ax,08000h
mov es,ax
mov di,0
mov ebx,0

enummem:
;mov byte [es:0xdead],2
;enumerate the memory regions available to the operating system and save them
xor eax,eax
mov eax,0e820h
mov ecx,20
mov edx,0534D4150h
clc
int 15h

jc endofenummem
cmp eax,0534D4150h
jne endofenummem

cmp ecx,20
jne endofenummem

push di
lea di,[ds:bp+reservedmem_listcount-begin]
inc dword [ds:di]
pop di
add di,20

cmp ebx,0
jne enummem ;still more to go

endofenummem:



lea di,[ds:bp+reservedmem_listcount-begin]
mov ax,word [di]

lea di,[ds:bp+tempbuf-begin]
mov cx,16
call inttohex16



lea si,[ds:bp+strnormal-begin]
call sendstring16


mov cl,';'
call send16

;call waitforkeypress


gdtsetup:




;enable a20
in al,92h
test al,02h
jne hasa20

or al,02h
out 92h,al


noa20:
hasa20:

call checka20
lea di,[bp+a20state-begin]
mov [di],ax


;------------------GDT setup
mov ax,0x4000
mov es,ax

mov word [es:0],0x6f
mov dword [es:2],0x50000

; 0x3000:0x02b3
mov ax,0x5000
mov es,ax
mov dword [es:0],0
mov dword [es:4],0
mov dword [es:8],0x0000ffff   ;32-bit data
mov dword [es:12],0x00cf9200
mov dword [es:16],0x0000ffff  ;stack (unused)
mov dword [es:20],0x00cf9600

;lea ax,[bp+entry32-begin]
;shl eax,16
;add eax,0xffff
mov dword [es:24],0x0000ffff  ;32-bit code
mov dword [es:28],0x00cf9b00  ;limit=ffff*4KB base=0



mov dword [es:32],0x0000ffff  ;16-bit code
mov dword [es:36],0x00009a00

mov dword [es:40],0x0000ffff  ;16-bit data
mov dword [es:44],0x00009200

mov dword [es:48],0x0000ffff  ;16-bit code, base 0x30000
mov dword [es:52],0x00009a03

mov dword [es:56],0  ;32-bit task , main vmm task
mov dword [es:60],0

mov dword [es:64],0  ;64-bit task , main vmm64 task
mov dword [es:68],0

mov dword [es:72],0  ;
mov dword [es:76],0

;64-bit code segment
mov dword [es:80], 0x0000ffff        ;64-bit code (there is no limit checking)
mov dword [es:84], 0x00AF9B00
mov dword [es:88],0
mov dword [es:92],0

;64bit tss descriptor (initialized later)
mov dword [es:96], 0
mov dword [es:100],0
mov dword [es:104],0
mov dword [es:108],0

;64-bit data segment
;there is none...

mov ax,0x4000
mov fs,ax
db 0x66
LGDT [fs:0]




mov ax,0xffff
mov ds,ax



;setup to protected mode
cli ;no more ints for now

mov ebx,cr0
or ebx,1
mov cr0,ebx

jmp short shortjump
shortjump:

nop
nop
mov bx,cs

bits 32
db 0x66
jmp 24:entry32

;----------------------------------------------------------;
;					PROTECTED MODE :32 bit section         ;
;----------------------------------------------------------;
bits 32




jmp short entry32

extern _vmloader_main

strnormal:
db 'normal boot',13,10,'$'

strcalledlgdt:
db 'Called LGDT (not)',13,10,'$'

sendstring32:
;esi=pointer to zstring
mov cl,[esi]
call sendchar32

inc esi
cmp byte [esi],0
jne sendstring32
ret


sendchar32:
%ifdef SERIALPORT
%if SERIALPORT != 0
mov edx,SERIALPORT+5 ;3fdh
in al,dx
and al,0x20
cmp al,0x20
jne sendchar32


mov edx,SERIALPORT; 0x3f8
mov al,cl
out dx,al
%endif
%endif
ret


entry32:

shl ebx,16
mov bx,ds

mov cx,es
shl ecx,16
mov cx,fs


;set segments to valid entries (ss is kinda important)
mov ax,8
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
;mov esp,0x003ffffc ;4mb , the user should have that at least, else i will crash anyhow
mov esp,0x0002fffc

;mov [readerror],ebx
;mov [sectorsread],ecx

;zero the stack
xor eax,eax
mov edi,esp
and edi,0xffff0000
mov ecx,0xffff
rep stosb

;0018:0303de
call _vmloader_main  ; 0x30452


mov al,1
lp:
mov byte [0x0b8000],'l'
mov byte [0x0b8001],al
inc al
mov byte [0x0b8002],'o'
mov byte [0x0b8003],al
inc al
mov byte [0x0b8004],'s'
mov byte [0x0b8005],al
inc al
mov byte [0x0b8006],'e'
mov byte [0x0b8007],al
inc al
mov byte [0x0b8008],'r'
mov byte [0x0b8009],al
inc al


jmp lp

strInsideVMM: db 'Inside gotoVMM',13,10,0
strSetGDT: db 'Have set the GDT',13,10,0


global PageMapLevel4
PageMapLevel4: dd 0

global vmmPA
vmmPA: dd 0

global GDTVA
GDTVA: dd 0

global gotoVMM
gotoVMM:
;the new gdt address is at VMMSIZE+1*4096 & 0xfff
;the pagetablebase (cr3) should be at VMMSIZE+2*4096 & 0xfffff000

mov ebx,[vmmPA]

mov esi,strInsideVMM
call sendstring32



mov eax,[PageMapLevel4]
mov cr3,eax

;set the gdt
mov word [0x40000],0x6f
mov eax,[GDTVA]
mov dword [0x40002],eax ;was 0x00400000+VMMSIZE+1*4096 & 0xfffff000
lgdt [0x40000]

mov esi,strSetGDT
call sendstring32


mov dl,[bootdisk] ;so the vmm knows the bootdisk, for whatever reason it migth need it

;mov eax,cr4
;or eax,0x30 ; enable PAE and PSE (to enable 2MB pages)
;mov cr4,eax
mov eax,0x30
mov cr4,eax


;set IA32_EFER_LME to 1 (enable 64 bits)
mov ecx,0xc0000080
rdmsr
or eax,0x100
wrmsr

mov eax,cr0
or eax,0x80000020 ;set the pg bit  (and ne bit)
mov cr0,eax
xor eax,eax ;tell the vmm that it's loaded from a clean setup



jmp 80:0x00400000 ; go to 64-bit

global halt
;----------------------------;
;int halt(void);
;----------------------------;
halt:
hlt
jmp halt

global readsectorasm
;----------------------------;
;int readsectorasm(void);
;----------------------------;
;7000:0000 contains the data to read, 7000:8000 the data needed to return
;6000:0000 points to the data to fill in
;This routine will switch to realmode , execute int13, and then switch back to realmode
readsectorasm:
;save state
mov dword [0x00078000],eax
mov dword [0x00078004],ebx
mov dword [0x00078008],ecx
mov dword [0x0007800c],edx
mov dword [0x00078010],esi
mov dword [0x00078014],edi
mov dword [0x00078018],ebp
mov dword [0x0007801c],esp


mov eax,readsector16protected
and eax,0xffff
mov dword [0x00078020],eax
mov dword [0x00078024],48  ;16-bit code segment starting at 0x30000



nop
nop
nop
jmp far dword [0x00078020]

readsector16protected:
;in 16bit protected mode now

bits 16
mov ax,40 ;set segment registers to 16-bit
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax

;switch to real
xor eax,eax
mov cr0,eax

mov ax,0x6000
mov ds,ax
mov eax,readsector16
mov word [0],ax
mov word [2],0x3000
jmp far word [0]  ;refresh cs to point to proper base

readsector16:


;realmode here
mov ax,0x7000
mov ds,ax
mov ax,0x6000 ;target to write to (512 bytes)
mov es,ax

mov ss,ax
mov sp,0xfffe ;end of that segment

sti

;call int13
mov ax,0x0201 ;read 1 sector
xor bx,bx ;write to 0x0 ;(so ds:bx=7000:0000)

;mov word [0xdead],0xdead  ;;debug for bochs (watch write 0x7dead)

mov cl,[0]  ;sector
mov dh,[1]  ;head
mov ch,[2]  ;cylinder
mov dl,[3]  ;drive

int 0x13

jc readsector_error
mov eax,1
jmp readsector_afterread

readsector_error:
xor eax,eax


readsector_afterread:
mov bx,0x7000
mov ds,bx
mov dword [0x8000],eax ;set new eax value

;go back to the protected mode caller
cli ;no more ints for now

mov ebx,cr0
or ebx,1
mov cr0,ebx

jmp short readsector_sj
readsector_sj:

nop
nop

bits 32
db 0x66
jmp 24:readsector_return

readsector_return:
mov ax,8
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax

mov eax,dword [0x00078000]
mov ebx,dword [0x00078004]
mov ecx,dword [0x00078008]
mov edx,dword [0x0007800c]
mov esi,dword [0x00078010]
mov edi,dword [0x00078014]
mov ebp,dword [0x00078018]
mov esp,dword [0x0007801c]

ret
