; protected mode startup for XMS and RAW modes
; copyright Rossi Gianluca [ranger@racine.ra.it]
; V0.6 sept 07 1997
;
; FIXME: cr4 may be != 0 after pmode switch
;
;
	BITS	16

        SEGMENT CONST

	EXTERN map_memory_and_getcr3	; function
        EXTERN cmdl_pages               ; word
        EXTERN cmdl_debug               ; byte
        EXTERN cmdl_text                ; byte
        EXTERN cmdl_fname               ; string
        EXTERN cmdl_kernel              ; string
        EXTERN _dos_mem
        EXTERN driver_code		; word
        EXTERN kernel_buffer
        EXTERN prefixseg

        ; temp tss buffer
        TSS	 times 103 db 0

        ss_sp	dd 0

        ; xms driver entry point
        xms_drv
        xms_o	dw 0
        xms_s   dw 0

        ; XMS block address
        mem_addr
        mem_lo	dw 0
        mem_hi	dw 0

        ; XMS handle
        handle	dw 0

        ; 4K align offset for mem_addr
        mem_ofs4k	dd 0

        ; loaded program entry point far ptr
        far_jmp_ptr
        far_jmp_eip 	dd 0
        far_jmp_cs 	dw 40h


        ; IDT
        idt_limit dw 0
        idt_addr  dd 0
        ; GDT
        gdt_limit dw (10*8)-1
        gdt_addr  dd 0
        ;-- table
        gdt_0     dd 0		; NULL
                  dd 0
        gdt_8	  dd 0          ; reserved for VCPI
        	  dd 0
        gdt_10	  dd 0          ; reserved for VCPI
        	  dd 0
        gdt_18	  dd 0          ; reserved for VCPI
        	  dd 0
        ; code 16 bit
        gdt_20    dw 0xffff
                  dw 0
                  db 0
                  db 9ah
                  dw 0
        ; data 16 bit
        gdt_28    dw 0xffff
                  dw 0
                  db 0
                  db 92h
                  dw 0
        ; stub_stat (point to kernel_buffer)
        gdt_30    dw 255
        	  dw 0
                  db 0
                  db 92h
                  db 0
                  db 0
        ; kdata 32 (4G,W+,B+)
        gdt_38    dw 0xffff
        	  dw 0
                  db 0
                  db 92h
                  db 0xcf
                  db 0
        ; kcode 32 (4G,R+,D+)
        gdt_40    dw 0xffff
        	  dw 0
                  db 0
                  db 9ah
                  db 0xcf
                  db 0
        ; temp TSS
        gdt_48	  dw 103
                  dw 0
                  db 0
                  db 89h
                  db 0
                  db 0


        SEGMENT CODE

;
; check for XMS
;

global XMS_check

XMS_check:
	push	es
	xor	eax,eax
        mov	es,ax
        cmp	dword [es:2fh*4],eax
        jz	.ret_false

        mov	ah,43h
        int	2fh
        cmp	al,80h
        jnz	.ret_false

        mov	ax,4310h
        int	2fh
        mov	[xms_s],es
        mov	[xms_o],bx
        smsw	ax
        and	ax,1
        setz	al
        jmp	.ret

.ret_false:
	xor	ax,ax
.ret
	pop	es
	ret

;
; initialize XMS
;

global XMS_init
XMS_init:
	; enable A20
	mov	ah,3
        call	far [xms_drv]
        dec	ax
        jnz	.err

        cmp	word [cmdl_pages],0
        jnz	.go

   	; alloc all free XMS memory
        mov	ah,8h
        call	far [xms_drv]
        test	bl,80h
        jnz	.err
     	shr	ax,2
        sub	ax,2
        mov	[cmdl_pages],ax
.go
        ; alloc memory
        mov	ah,9h
        mov	dx,[cmdl_pages]
        inc	dx
        shl	dx,2
        call	far [xms_drv]
        dec	ax
        jnz	.err
        mov	word [handle],dx

        ; lock
        mov	ah,0ch
        call	far [xms_drv]
        dec	ax
        jnz	.err
        mov	word [mem_lo],bx
        mov	word [mem_hi],dx

        mov	eax,[mem_addr]
        add	eax,0xfff
        and	eax,0xfffff000
        sub	eax,[mem_addr]
        mov     [mem_ofs4k],eax

        xor	ax,ax
        inc	ax
        ret
.err:
	mov	bh,0
	mov	word [driver_code],bx
        xor	ax,ax
        ret

;
; free XMS resources
;
global XMS_clean
XMS_clean:
	mov	dx,word [handle]
        test	dx,dx
        jz	.nofree

        ; unlock & free
        mov	ah,0dh
        call	far [xms_drv]
        mov	ah,0ah
        call	far [xms_drv]
.nofree:
        mov	ah,04h
        call	far [xms_drv]
	retf

global XMS_move

;
; dword [bp+12] = vaddr (dest)
; dword [bp+8] = from
; word [bp+6] = size
; struct [bp-16] (XMS struct)
;      dw[bp-16]  = count
;      w [bp-12]  = sh
;      dw[bp-10] = soff
;      w [bp-6] = dh
;      dw[bp-4] = doff

XMS_move:
	push	bp
        mov	bp,sp
        sub	sp,16
        mov	eax,[bp+12]       ; vaddr
        add	eax,[mem_ofs4k]   ; 4k alignement
        mov	[bp-4],eax

        mov	ax,[handle]
        mov	[bp-6],ax   	  ; dest handle

        mov	eax,[bp+8]
        mov	[bp-10],eax       ; from

        mov	word [bp-12],0    ; source handle (0 = base)

        mov	ax,word [bp+6]    ; size
        add	ax,1
        and	al,0feh
        movzx	eax,ax
        mov	dword [bp-16],eax

        mov	si,sp
        push	ds
        pop	es
        push	ss
        pop	ds
        mov	ah,0bh
	call	far [es:xms_drv]
        push	es
        pop	ds
        dec	ax
        jnz	.err
        mov	ax,1
	jmp	.ret
.err:
	xor	ax,ax
        xor	bh,bh
        mov	[driver_code],bx
.ret
	add	sp,16
        pop	bp
        retf	10

%macro SET_BASE 1
        mov	[%1+2],ax
        shr	eax,16
        mov	[%1+4],al
        mov     [%1+7],ah
%endm

global RAW_switch
global XMS_switch

XMS_switch:
RAW_switch:

        movzx	esp,sp
        mov	eax,[esp+4]		; eip
        mov	[far_jmp_eip],eax

        push	bp

        mov	[ss_sp],sp
        mov	[ss_sp+2],ss

; set descriptors base
	mov	ax,cs
        movzx	eax,ax
        shl	eax,4
        SET_BASE gdt_20

	mov	ax,ds
        shl	eax,4
        mov	[gdt_addr],eax
        SET_BASE gdt_28

        mov	ax,TSS
        add	eax,[gdt_addr]
        SET_BASE gdt_48

        mov	ax,kernel_buffer
        add	eax,[gdt_addr]
        SET_BASE gdt_30

        mov	ax,gdt_0            ; 16 bit linkers dont like _
        add	[gdt_addr],eax      ; _ add dword [*],gdt_0


        mov	eax,[mem_addr]
        add	eax,[mem_ofs4k]
        mov	[kernel_buffer + 4], eax
        push	eax
        call	map_memory_and_getcr3	; memory mapping
        movzx	eax,ax
        shl	eax,4
        mov	cr3,eax		    ; cr3 in now ignored

; disable all flags (especially IF)
        push	dword 0
	popfd

; now make ss == ds
	mov	ax,ss
        movzx	eax,ax
        shl	eax,4
        movzx	esp,sp
        add	esp,eax

;-- start pmode
        lidt	[idt_limit]
        lgdt	[gdt_limit]
        mov	eax,1
        mov	cr0,eax
        jmp	word 20h:.load_pmcs
.load_pmcs

	mov	ax,28h
        mov	gs,ax           ; gs=data
        mov	ax,30h
        mov	fs,ax           ; fs=stub

	mov	ax,38h
        mov	ss,ax
        mov	ds,ax		; DS&ES&SS CHANGED !!! BASE 0
        mov	es,ax


        mov	ax,48h
        ltr	ax
        xor	ax,ax
        lldt	ax

        mov	eax,80000001h
        mov	cr0,eax           ; enable paging, cr3 already loaded
        jmp	.flush_tlb
.flush_tlb

        and	esp,0xFFFFFFF8	  ; 8 byte stack align

        call	far dword [gs:far_jmp_ptr]
        mov	bx,ax

        mov	eax,1
        mov	cr0,eax
        jmp	.flush_tlb2
.flush_tlb2

	xor	eax,eax
        mov	dr6,eax
        mov	dr7,eax
        mov	cr3,eax
        mov	cr2,eax

        mov	ax,28h
        mov	ds,ax
        mov	es,ax
        mov	gs,ax
        mov	fs,ax
        mov	ss,ax
        mov	word [idt_limit], 1023
        mov	dword [idt_addr], 0
        lidt	[idt_limit]
        xor	eax,eax
        mov	cr0,eax
        jmp	far .load_rmcs
.load_rmcs

        mov	ax,SEG idt_limit ; carica DS
        mov	ds,ax
        mov	es,ax
        mov	fs,ax
        mov	gs,ax
        mov	ss,ax
        lss	sp,[ss_sp]

        movzx	esp,sp
        movzx	ebp,bp
        sti
        mov	ax,bx
        pop	bp
        ret	4

; ofs 0: 'V0'
;     2: header size
;     4: kernel base
;     8: # pages
;    10: cmdl_kernel
;    74: flags        [0:debug,1:text]

global kernel_buf_setup

kernel_buf_setup:
        push	ds
        pop	es
	mov	di,kernel_buffer
        xor	ax,ax
        mov	cx,128
        rep	stosw

        ; set header
	mov	word [kernel_buffer],'V0'
        mov	word [kernel_buffer+2],252  ; 256 - 2 word

        ; # pages
        mov	ax,[cmdl_pages]
        mov	[kernel_buffer+8],ax

        ; set cmdl_kernel field
        mov	di,kernel_buffer+10
        mov	si,cmdl_kernel+1
        mov	cl,[si-1]
        mov	ch,0
        rep	movsb

        ; set flags field

        mov	si,kernel_buffer+74
        xor	ax,ax
        mov	bx,ax
        mov	[si],ax
        cmp	[cmdl_debug],ax
        setne	bl
        or	[si],bx
        cmp	[cmdl_text],ax
        setne	bl
        shl	bl,1
        or	[si],bx

        mov	ax,[prefixseg]
        mov	[si+2],ax
        mov	eax,[_dos_mem]
        mov	[si+4],eax
        ret