; virmem.c
%pragma once
bits 16

; Valloc :: addr24 *hi:low -> int8 access -> int16 limit? -> int16 selector
Valloc:
    enter 2,0
    %define Gdtseg bp-2
    pusha

    chseg(ds,csselector)
        mov bx,confseg.gdtseg
        mov word dx,ds:[bx] ; dx=gdt seg
    sret(ds)
    mov word ss:[Gdtseg],dx

    mov bx,[bp+4]       ; bl=high byte
    mov cx,[bp+6]       ; cx=low word
    mov dx,[bp+8]       ; dl=access byte
   ;mov ax,[bp+10]      ; ax=limit (for doc)


    and bx,0xff
    and dx,0xff
    mov si,dx
    and si,(0b11 << 5)   ; si=ring
    shr si,5

    ; ax =
        getempty()
    jc .errdesc
    mov di,ax             ; di=idx
    ; ax =
        gdtidx2sel(di,si)
    push ax
    mov word si,ss:[Gdtseg] ; si=gdt seg

    shl di,3                ; di=offset into gdt
    push bp
    mov bp,[bp+10]          ; bp=limit
    setgdt(si,di,bx,cx,dx,bp)
    jc .errmem
 .ok:
    ; let's reuse the stack pos
    pop bp
    pop ax                      ; ax=selector
    mov word ss:[Gdtseg],ax

    shr di,3                     ; di=idx again
    ; cf =
        setgdtbitmap(di,1)
    jc .errdesc

    popa
    mov word ax,ss:[Gdtseg]     ; ax=still selector
    add sp,2
    clc
    leave
    ret

 .errdesc:
    ; no available descriptors
    pop bp
    pop ax
    popa
    mov ax,0x0001
    add sp,2
    stc
    leave
    ret

 .errbitmap:
    ; unable to update the virtual memory bitmap
    pop bp
    pop ax
    popa
    mov ax,0x0003
    add sp,2
    stc
    leave
    ret

 .errmem:
    ; unable to write to gdt memory
    %undef Gdtseg
    pop bp
    pop ax
    popa
    mov ax,0x0002
    add sp,2
    stc
    leave
    ret

initvir:
    enter 0,0
    pfln "    [09:41] Virtual memory mgr successfully loaded"
    
    xor ax,ax
    clc
    leave
    ret
    
;  .init:
;     mov ax,0xfffe
;     mov bx,0x0020
;     mov ds,bx
;     mov es,bx
;     mov ss,bx
;     mov sp,ax
;     mov cx,ax
;     pfln "We are inside the kernel!"
;     call main
;     jmp $

; main:
;     enter 0,0
;     ; ax =
;         valloc(0x30,0x8000,0x92,0x7fff)
;     jc .err
;     mov bx,ax
;     pfln "GDT sel = 0x%x",bx
    
;     showgdt()
;     jmp .halt

;  .err:
;     mov bx,ax
;     pfln "Error %d",bx

;   .halt:
;     cli
;     hlt
;  .loop:
;     nop
;     jmp .loop
;     retf

; __gdtidx2sel :: int16 gdtidx -> int8 ring -> int16 selector
__gdtidx2sel:
    enter 0,0
    push bx
    push si

    mov si,[bp+4]       ; si=idx
    mov bx,[bp+6]       ; bl=ring

    shl si,3
    and bl,0b11
    or si,bx            ; si=desc

    mov ax,si
    pop si
    pop bx
    clc
    leave
    ret

; __getempty :: void -> int16 gdtidx
__getempty:
    enter 2,0
    %define Idx bp-2
    push bx
    push si

    ; ax =
        findbit(csselector, confseg.virbm, 31, 0)
    jc .err
    mov bx,ax
    mov word ss:[Idx],bx

    ; do we need to grow the gdt?
    
    inc bx
    ; ax =
        multiply(bx,8)
    mov bx,ax
    dec bx
    ; if idx=1, then bx is now 15 and so is gdtsz (in case there's room)

    chseg(ds,csselector)
    mov si,confseg.gdtsz
    mov word ax,ds:[si]
    mov si,ax
    sret(ds)
    cmp bx,si
    ; jg .growgdt
    jg .err

    ; we're fine. let's return
    jmp .end

 .growgdt:
    growgdt()
    jc .err

 .end:
    mov word bx,ss:[Idx]
    setbit(csselector, confseg.virbm, bx, 1)
    mov ax,bx
    pop si
    pop bx

    add sp,2
    clc
    leave
    ret

 .err:
    xor ax,ax
    pop si
    pop bx
    add sp,2
    stc

    %undef Idx
    leave
    ret

; __growgdt :: void -> bool
__growgdt:
    enter 0,0
    push si

    chseg(ds,csselector)
    mov si,confseg.gdtsz
    mov word ax,ds:[si]
    add ax,9
    cmp ax,(MaxGdtSz*8)
    jge .err

 .ok:
    dec ax
    mov word ds:[si],ax
    sret(ds)
    xor ax,ax
    pop si
    clc
    leave
    ret

 .err:
    sret(ds)
    xor ax,ax
    pop si
    stc
    leave
    ret

; ; getempty :: int8 xdt -> gdtentry *seg:addr
; getempty:
;     enter 6,0
;     %define SavedGdtr   bp-6
;     %define Seg         bp-4
;     %define Addr        bp-2
;     pusha

;     mov bx,[bp+4]           ; bx=gdt idx

;     ; dx:ax =
;         xdtr(bx)            ; dx=seg,cx=addr of xdtr
;     mov cx,ax
;     mov word ss:[SavedGdtr],cx

;     chseg(ds,dx)
;     ;push cx                 ; save xdtr addr
;     mov bp,cx               ; ds:bp=xdtr addr
;     ; dx:ax =
;         xdt(bx)          ; dx=seg,ax=addr of xdt
;     ;add ax,0x08
;     mov di,ax
;     chseg(es,dx)

;     mov ax,bp
;     push ax
;     mov bp,cx
;     mov word ax,ds:[bp]
;     mov cx,ax               ; cx=gdt size
;     inc cx

;     mov bp,di               ; es:bp=xdt addr
;     xor ax,ax
;     mov bx,gdtentry.xs
;  .loop:
;     sub cx,0x0008
;     jc .notfound
;     test cx,0xffff
;     jz .notfound

;     mov si,bx
;     mov byte al,es:[bp+si]  ; al=access byte
    
;     test al,(1<<7)
;     jnz .next

;     ; P bit is clear, let's use this one
;     jmp .found

;  .next:
;     add bp,0x0008
;     jmp .loop

;  .found:
;     mov ax,bp
;     pop bx
;     mov bp,bx
;     mov word ss:[Seg],es
;     mov word ss:[Addr],ax
;     sret(es)
;     sret(ds)
;     popa

;     mov word dx,ss:[Seg]
;     mov word ax,ss:[Addr]
;     add sp,6
;     clc
;     leave
;     ret

;  .notfound:
;     ; can we grow the xdt?
;     pop ax
;         ;xdtr(0)
;     mov bp,ax 
;     mov word si,ss:[SavedGdtr]
;     mov word ax,ds:[si]  ; ax=current xdt size
    
;     cmp ax,((8*MaxGdtSz)-1)
;     jge .xdtisfull

;  .grow:
;     add ax,8
;     mov word ds:[si],ax
;     add di,ax

;     mov word ss:[Seg],es
;     mov word ss:[Addr],di
;     sret(es)
;     sret(ds)
;     popa

;     mov word dx,ss:[Seg]
;     mov word ax,ss:[Addr]
;     add sp,6
;     clc
;     leave
;     ret

;  .xdtisfull:
;         mov bx,3
;         breakpoint
;     sret(es)
;     sret(ds)
;     popa

;     xor dx,dx
;     xor ax,ax
;     add sp,6
;     stc
;     leave
;     ret

; Setgate :: gdtentry *seg:dest
                ;   -> addr *sel:offset -> int8 wordcnt -> int8 access -> bool
Setgate:
    enter 0,0
    pusha

    mov cx,[bp+4]           ; cx=dest seg of gdt
    mov di,[bp+6]           ; di=dest in gdt
    mov bx,[bp+8]           ; bx=segment selector for isr
    mov si,[bp+10]          ; si=offset to isr
    mov ax,[bp+12]
    mov dh,al               ; dh=wordcnt
    mov ax,[bp+14]
    mov dl,al               ; dl=access byte

    and dh,0b00011111
    and bx,0b1111111111111100

    chseg(es,cx)
        set(di, 0, 8)
        mov cx,bx               ; cx=sel
        mov bx,Idt.offset
        mov word es:[di+bx],si
        mov bx,Idt.selector
        mov word es:[di+bx],cx
        mov bx,Idt.wordcnt
        mov byte es:[di+bx],dh
        mov bx,Idt.xs
        mov byte es:[di+bx],dl

    sret(es)
    popa

    xor ax,ax
    clc
    leave
    ret

; Setgdt :: gdtentry *seg:dest
                ;   -> addr24 *hi:low -> int8 access -> int16 limit? -> bool
Setgdt:
    enter 0,0
    pusha

    mov cx,[bp+4]           ; cx=dest seg
    mov di,[bp+6]           ; di=dest
    mov bx,[bp+8]           ; bl=hi addr
    mov si,[bp+10]           ; si=lo addr
    mov dx,[bp+12]          ; dl=access byte
    mov bp,[bp+14]          ; bp=limit

    chseg(es,cx)
    set(di, 0, 8)
    mov dh,bl               ; dh=hi addr

    ; mov word es:[di],0xffff
    mov word es:[di],bp
    mov bx,gdtentry.baselo
    mov word es:[di+bx],si
    mov bx,gdtentry.basehi
    mov byte es:[di+bx],dh
    mov bx,gdtentry.xs
    mov byte es:[di+bx],dl

    sret(es)
    popa
    xor ax,ax
    clc
    leave
    ret

; Showgdt :: void -> void
Showgdt:
    enter 0,0
    pusha

    pfln "Global Descriptor Table:"
    chseg(ds,csselector)
    mov word ax,ds:[confseg.gdtsz]
    ; mov ax,((IdtSz*8)-1)
    inc ax
    xor dx,dx
    mov bx,8
    idiv bx
    mov cx,ax
    inc cx
    mov word dx,ds:[confseg.gdtseg]
    ; mov word dx,ds:[confseg.idtseg]
    mov si,dx                       ; si=gdt seg
    sret(ds)
    xor dx,dx                       ; dx=gdt start offset
    mov di,dx                       ; di=counter

 .loop:
    pf "  [%d] ",di
    show(si, dx)
    add dx,8
    inc di
    loop .loop

 .end:
    popa

    xor ax,ax
    clc
    leave
    ret

; __show :: gdtentry *seg:addr -> void
__show:
    enter 9,0
    pusha

    %define Gdtentry    bp-2
    %define Limit       bp-4
    %define Basehi      bp-6
    %define Baselow     bp-8
    %define Access      bp-9

    mov dx,[bp+4]       ; dx=seg
    mov si,[bp+6]       ; si=addr

    chseg(ds,dx)
;struc gdtentry
;    .limit:   resw 1
;    .baselo:  resw 1
;    .basehi   resb 1
;    .xs:      resb 1
;    .reserv:  resw 1
;endstruc
    mov word [Gdtentry],si
    mov word cx,ds:[si]
    mov word [Limit],cx

    mov bx,gdtentry.baselo
    mov word cx,ds:[si+bx]
    mov bx,gdtentry.basehi
    xor ax,ax
    mov byte al,ds:[si+bx]
    mov di,ax
    mov word [Basehi],di
    mov word [Baselow],cx
    
    mov bx,gdtentry.xs
    mov byte cl,ds:[si+bx]
    mov byte [Access],cl

    sret(ds)
;    pf "gdtentry @ ds:0x%x",[Gdtentry]
;    pf "  Limit:  0x%x",[Limit]
;    pf "  Base:   %x:",[Basehi]
;    pf "          0x%x",[Baselow]
    pf "limit=0x%x ",[Limit]
    pf "base=%x:",[Basehi]
    pf "0x%x",[Baselow]
    xor cx,cx
    mov byte cl,[Access]
    pfln " xs=0x%x",cx
    popa
    add sp,9

    %undef Gdtentry
    %undef Limit
    %undef Basehi
    %undef Baselow
    %undef Access

    xor ax,ax
    clc
    leave
    ret

; Dalloc :: int8 ring -> [ int32 sel:gdtidx ]
Dalloc:
    enter 0,0

    mov ax,[bp+4]   ; al=ring
    and ax,0xff

    ; ax=
        getempty()
    jc .errdesc
    mov dx,ax       ; dx=index
    ; ax=
        gdtidx2sel(dx,ax)
    jc .errsel
    xchg ax,dx
    jmp .end

 .errdesc:
    mov ax,0x0001
    jmp .err
 .errsel:
    mov ax,0x0002
 .err:
    xor dx,dx
    stc
    leave
    ret

 .end:
    clc
    leave
    ret