; interrupt.s
bits 16
%pragma once

; mkidt :: void -> bool             ; make idt
mkidt:
    enter 8,0
    %assign stack   8
    %define PhyHi   bp-2
    %define PhyLo   bp-4
    %define Idtr_   bp-6
    %define Idt     bp-8
    pusha

    ; lea bx,ss:[PhyLo]
    ; pf "DEBUG: phylo=0x%x ",bx
    ; lea bx,ss:[PhyHi]
    ; pfln "phyhi=0x%x ",bx
    pf "  allocating phy memory... "
    ; dx:ax =
        palloc(1)
    jc .errphy

    push ax
    mov word ss:[PhyLo],ax
    xor dh,dh
    mov word ss:[PhyHi],dx
    pf "addr=%x:",dx
    pop bx
    pfln "0x%x",bx

    pf "  allocating vir memory... "
    ; ax =
        valloc(ss:[PhyHi], ss:[PhyLo], (P|Ring0|S|W), 7)
    jc .errvir
    push ax
    mov bx,ax
    pf "idtr_sel=0x%x ",bx
    
    mov word bx,ss:[PhyLo]
    add bx,8               ; bx=offset of idt (offset from idtr)
    ; ax =
        valloc(ss:[PhyHi], bx, (P|Ring0|S|W), ((IdtSz*8)-1))
    jc .errvir

    mov bx,ax               ; bx=idt seg
    push bx
    pfln "idt_sel=0x%x",bx
    pop bx
    pop ax                  ; ax=idtr seg
    mov word ss:[Idtr_],ax
    mov word ss:[Idt],bx
    
    chseg(ds,csselector)
        mov word ax,ss:[Idt]
        xor bx,bx
        mov word ds:[bx+confseg.idtseg],ax
    sret(ds)

    chseg(ds,ss:[Idtr_])
        xor bx,bx                ; bx=idtr addr
        mov si,0x0008            ; si=idt offset from idtr
        add word si,ss:[PhyLo]   ; si=low word of phy addr of idt
        mov word ds:[bx+Idtr(low)],si

        mov word ax,ss:[PhyHi]   ; al=high byte of the above
        and ax,0xff
        mov byte ds:[bx+Idtr(hi)],al
        mov word ds:[bx+Idtr(sz)],((IdtSz*8)-1)
        mov al,0xff
        mov byte ds:[bx+Idtr(res)],al
    sret(ds)

    pf "  zeroing the idt memspace... "
    chseg(es,ss:[Idt])
        xor bx,bx           ; bx=dest addr
                            ; bl=character (\0)
        mov cx,(IdtSz*8)    ; cx=size
        set(bx, bx, cx)
    sret(es)
    pfln "%d chars written",cx

    mov word ax,ss:[Idt]    ; ax=idt seg
    push ax
    call initidt
    jc .erridt
    add sp,2
    ; ctxidt

    showgdt()
    mov word bx,ss:[Idt]
    showidt(bx)
    ; pf "  flushing idt... "
    ; lidt [ss:Idtr_]
    ; pfln "ok"
    pf "  activating the idt... "
    chseg(ds,[Idtr_])
        xor bx,bx
        lidt ds:[bx]
    sret(ds)
    pfln "ok"

    sti
    ; mov bx,0
    ; mov ax,7
    ; div bx

    pfln "Still kicking!"
    ; show(0x0038,0x0000)
    ; show(0x0038,0x0008)
    ; show(0x0038,0x0010)
    ; show(0x0038,0x0018)

    %undef Idt
    %undef Idtr_
    %undef PhyLo
    %undef PhyHi
    jmp .end

 .errphy:
    mov ax,0x0001
    push ax
    jmp .err
 .errvir:
    mov ax,0x0002
    push ax
    jmp .err
 .erridt:
    pop ax
    mov ax,0x0003
    push ax
    jmp .err
 .err:
    mov bx,ax
    pfln "err=%d",bx
    pop ax
    popa
    add sp,stack
    stc
    leave
    ret

 .end:
    xor ax,ax
    popa
    add sp,stack
    %undef stack

    clc
    leave
    ret

mkisr 0,1,2,3,4,5,6,7
    isr8:
        times 4 nop
        iret
        xor ah,ah
        mov byte al,8
        push ax
        jmp isrstub

        %assign alignment   ($-isr8)
        %assign padding     (16-alignment)
         .pad:
            times padding nop
        %undef padding
        %undef alignment
mkisr 9
    isr10:
        times 4 nop
        xor ah,ah
        mov byte al,10
        push ax
        jmp isrstub

        %assign alignment   ($-isr10)
        %assign padding     (16-alignment)
         .pad:
            times padding nop
        %undef padding
        %undef alignment
    isr11:
        times 4 nop
        xor ah,ah
        mov byte al,11
        push ax
        jmp isrstub

        %assign alignment   ($-isr11)
        %assign padding     (16-alignment)
         .pad:
            times padding nop
        %undef padding
        %undef alignment
    isr12:
        times 4 nop
        xor ah,ah
        mov byte al,12
        push ax
        jmp isrstub

        %assign alignment   ($-isr12)
        %assign padding     (16-alignment)
         .pad:
            times padding nop
        %undef padding
        %undef alignment
    isr13:
        times 4 nop
        iret
        xor ah,ah
        mov byte al,13
        push ax
        jmp isrstub

        %assign alignment   ($-isr13)
        %assign padding     (16-alignment)
         .pad:
            times padding nop
        %undef padding
        %undef alignment
 
; parameter olympics 2026
mkisr   14,15,16,17,18,19,20,21,22,23,24,25,26, \
        27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46, \
        47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67, \
        68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88, \
        89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106, \
        107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, \
        122,123,124,125,126,127,128,129,130,131,132,133,134,135,136, \
        137,138,139,140,141,142,143,144,145,146,147,148,149,150,151, \
        152,153,154,155,156,157,158,159,160,161,162,163,164,165,166, \
        167,168,169,170,171,172,173,174,175,176,177,178,179,180,181, \
        182,183,184,185,186,187,188,189,190,191,192,193,194,195,196, \
        197,198,199,200,201,202,203,204,205,206,207,208,209,210,211, \
        212,213,214,215,216,217,218,219,220,221,222,223,224,225,226, \
        227,228,229,230,231,232,233,234,235,236,237,238,239,240,241, \
        242,243,244,245,246,247,248,249,250,251,252,253,254,255

; isrstub :: int32 *seg:retaddr
;   -> int16 flags -> int8 errcode -> int8 interrupt -> void
isrstub:
    enter 0,0
    pusha

    mov cx,[bp+2]           ; cx=int vector
    and cx,0xff
    mov bx,[bp+4]           ; bx=err code
    mov ax,[bp+8]           ; ax=flags
    ; pf "[int %d] code=",cx
    ; pfln "0x%x",bx
    test cl,0b1111111111110000  ; >= 16
    jnz .next

    push bx
    push cx
    call exception
    add sp,4

;  .halt:
;     cli
;     hlt
;     jmp .halt

 .next:
    cmp cl,0x1e
    jge .irq
    jmp .end

 .irq:
    cmp cl,(0x1e+15)
    jg .end
    ; we are in the IRQ range
    mov ax,cx
    xor ah,ah
    sub al,0x1d
    and ax,0b1111
    push ax
    call irq
    add sp,2

 .end:
    popa
    leave
    add sp,4
    iret

; irq :: int8 irq -> void
irq:
    enter 0,0

    mov ax,[bp+4]       ; al=irq
    cmp al,1
    jne .disabled
    push ax
    call pitisr
    add sp,2
    jmp .end

 .disabled:
    push bx
    mov bx,ax
    pfln "IRQ %d not enabled",bx
    pop bx
    eoi(ax)

 .end:
    xor ax,ax
    clc
    leave
    ret

; __showidt :: int16 idtseg? -> void
__showidt:
    enter 0,0
    push bx
    push cx
    push di

    mov bx,[bp+4]       ; bx=idt seg
    mov cx,IdtSz        ; cx=count
    xor di,di           ; di=offset

    push bx
    pf "will try 0x%x:",bx
    pop bx
    push di
    pfln "0x%x",di
    pop di
    pfln "Interrupt Descriptor Table:"
 .loop:
    ; show(bx,di)
    show(0x0038,di)
    add di,8
    loop .loop

 .end:
    pop di
    pop cx
    pop bx
    xor ax,ax
    clc
    leave
    ret

; initidt :: int16 seg -> bool
initidt:
    enter 0,0
    pusha

    mov bx,[bp+4]     ; bx=idt seg
    ; mov cx,IdtSz      ; cx=count
    xor cx,cx
    ; mov di,((IdtSz-1)*8)
    xor di,di
                      ; di=dest offset
    ; mov dl,0          ; dl=wordcount (0 is for testing)
    mov dl,cl          ; dl=wordcount (0 is for testing)
    mov dh, \
     (P|Ring0|IntGate)
                      ; dh=access byte for intgate

    pfln "adding idt entries"
 .loop:
    cmp cx,IdtSz
    jg .end
    ; ax=
        multiply(cx,16)
    xor si,si
    lea si,cs:[isr0]
    ; xor ax,ax
    ; mov al,dl
        xor dl,dl
    ; times 16 add word si,ax ; si=isr addr
    add si,ax ; si=isr addr
                       ; cs=code selector
    
    ; cf=
        setgate(bx,di,cs,si,dl,dh)
    jc .err
    add di,8
    jc .err
    pf "."
    inc cx
    inc dl
    jmp .loop

 .err:
    popa
    mov ax,0x0001
    stc
    leave
    ret

 .end:
    popa
    xor ax,ax
    clc
    leave
    ret

; struc Idt
;   .offset:  resw 1
;   .selector:resw 1 ; <<2
;   .wordcnt: resb 1
;   .xs:      resb 1
;   .res:     resw 1
; endstruc

; __show :: idtentry *seg:addr -> void
__show:
    enter 8,0
    %assign stack 8
    %define Offset      bp-7
    %define Sel         bp-4
    %define Words       bp-2
    %define Access      bp-1
    pusha

    mov dx,[bp+4]   ; dx=seg
    mov di,[bp+6]   ; di=idx

        ; mov dx,0x0038
    ; pf "show(0x%x",dx
    ; pfln ", 0x%x",di

    ; push dx
    ; pf "trying 0x%x:",dx
    ; pop dx
    ; push di
    ; pfln "0x%x",di
    ; pop di

    chseg(ds,dx)
        mov word ax,ds:[di+Idt.offset]
        mov word ss:[Offset],ax
            mov bx,ax
            pfln "     OFF=0x%x",bx
        mov word ax,ds:[di+Idt.selector]
        mov word ss:[Sel],ax
        xor ah,ah
        mov byte al,ds:[di+Idt.wordcnt]
        mov byte ss:[Words],al
        mov byte al,ds:[di+Idt.xs]
        mov byte ss:[Access],al
    sret(ds)

    ; ax=
        divide(di,8)
    mov si,ax
    mov word cx,ss:[Sel]
    mov byte dl,ss:[Words]
    mov byte dh,ss:[Access]
    
    pf "  [%d] ",si
    pf "addr=0x%x:",cx
    
    mov word bx,ss:[Offset]
    pf "0x%x ",bx
    xor bh,bh
    mov bl,dl
    pf "wc=%d ",bx
    mov bl,dh
    pfln "xs=0x%x",bx

 .end:
    popa
    xor ax,ax
    add sp,stack

    %undef stack
    %undef Access
    %undef Words
    %undef Sel
    %undef Offset

    clc
    leave
    ret

%if 0
; mkleaf :: int8 group -> int8 vector
;   -> isr *seg:addr -> struc *IntLeaf
mkleaf:
    %assign stack   10

    enter stack,0
    %define Ring        bp-2
    %define Segm        bp-4
    %define Idx         bp-6
    %define RootSeg     bp-8
    %define RootAddr    bp-10
    pusha

    mov cx,[bp+4]       ; cl=group
    mov ax,[bp+6]
    mov ch,al           ; ch=int no
    mov dx,[bp+8]       ; dx=handler seg
    mov di,[bp+10]      ; di=handler addr

    ; ax=
        getring()
    mov word ss:[Ring],ax
    ; ax=
        getconf(inttreeseg0)
    jc .errconf
    mov bx,ax       ; bx=seg for ring0
    mov word ss:[RootSeg],bx
    cmp word ss:[Ring],0
    je .r0
 .r1:
    ; ax=
        getconf(inttreeseg1)
    jc .errconf
    mov bx,ax          ; bx=seg for ring1
 .r0:
    mov word ss:[Segm],bx
    
    ; ax=
        findslot(bx,0,MaxTrees)
    jc .errfull
    mov si,ax           ; si=idx of empty slot
    mov word ss:[Idx],si
    ; ax=
        multiply(si,IntTreeSz)
    mov si,ax           ; si=offset in bytes
    ; ax=
        getconf(inttreeroot)
    jc .errconf
    mov bx,ax             ; bx=addr (in r0-seg) for root
    mov word ss:[RootAddr],bx
    chseg(es,ss:[Segm])
        set(si,0,IntTreeSz)
        jc .errperm

        ; flags(only zero|group), int vector
        ;   i wait with P flag until the very last min
        mov byte es:[si+IntLeaf.flags],cl
        mov byte es:[si+IntLeaf.int],ch
        
        ; interrupt handler
        xor bx,bx
        mov bx,IntLeaf.handler
        mov word es:[bx+si+0],di    ; +0 is offset
        mov word es:[bx+si+2],dx    ; +2 is seg

        ; reserved part (must be 0)
        mov bx,si
        add bx,IntLeaf.reserved               ; bx=off
        mov dx,(IntLeaf.end-IntLeaf.reserved) ; dx=sz
        set(bx,0,dx)
        jnc .connect
 .errperm:
    sret(es)
    popa
    mov ax,0x0001
    jmp .err
 .errfull:
    popa
    mov ax,0x0002
    jmp .err
 .errconf:
    popa
    mov ax,0x0003
 .err:
    xor dx,dx
    add sp,stack
    stc
    leave
    ret

 .connect:
;     ; overcomplicated solutions
;     ; i think i'll start over (just this section thou)
;     ;

;     mov word di,ss:[RootAddr]
;     chseg(es,ss:[RootSeg])
;         ; 3 links, one for each group
;         xor dx,dx
;         test cl,Exceptions
;         jz .findfault
;         test cl,IRQs
;         jz .findirq
;         test cl,Traps
;         jz .findtrap
;      .findother:
;         or dl,Other
;         jmp .stub
;      .findtrap:
;         or dl,Traps
;         jmp .stub
;      .findirq:
;         or dl,IRQs
;         jmp .stub
;      .findfault:
;         or dl,Exceptions
;      .stub:
;             ; scanchild(struct es:rootidx,
;             ;     prefix IntNode/IntLeaf,
;             ;     suffix fieldparent,
;             ;     suffix fieldchild)
;      .s1:
;         ; ax=
;             scanchild(di,IntNode,south1,flags)
;         jc .s2
;         test al,dl
;         jz .s1ftw
;      .s2:
;         ; ax=
;             scanchild(di,IntNode,south2,flags)
;         jc .s3
;         test al,dl
;         jz .s2ftw
;      .s3:
;         ; ax=
;             scanchild(di,IntNode,south3,flags)
;         jc .errperm
;         test al,dl
;         jz .s3ftw
;         jmp .errperm

;      .s1ftw:
;         mov bx,IntNode.south1
;         jmp .common
;      .s2ftw:
;         mov bx,IntNode.south2
;         jmp .common
;      .s3ftw:
;         mov bx,IntNode.south3

;      .common:
;         ; we can reuse the old stuff
;         mov word ax,es:[bx+di+0]  ; ax=offset
;         mov word ss:[RootAddr],ax ; actually the L2 and
;                                   ;     not the root
;         mov word ax,es:[bx+di+2]  ; ax=seg
;         mov word ss:[RootSeg],ax
    
;         ; next we need to check if the L2 node
;         ;   is connected anywhere downstream

 .present:
    or cl,Pbit
    chseg(es,ss:[Segm])
        mov byte es:[si+IntLeaf.flags],cl
    sret(es)

 .end:
    popa
    mov word dx,ss:[Segm]
    mov ax,si
    add sp,stack

    %undef RootAddr
    %undef RootSeg
    %undef Idx
    %undef Segm
    %undef Ring
    %undef stack

    clc
    leave
    ret
%endif

; exception :: int8 interrupt -> int8 code -> never
exception:
    enter 0,0
    push bx
    push cx
    push dx
    push si

    mov dx,[bp+4]       ; dl=int no
        and dx,0xff
    mov cx,[bp+6]       ; cl=status code
        and cx,0xff
    
    mov bx,ExceptionStrings.pointers
    ; ax =
        multiply(dx,2)
    mov si,ax
    ; add si,dx           ; 2x bytes per ptr
    push cx
    push dx

    mov word dx,ds:[bx+si]
    mov bx,dx          ; bx=addr of str

    pfln "------------"
    pop dx
    pf "Exception 0x%x raised (error code: ",dx
    pop cx
    pfln "%d):",cx
    pfln "  '%s'",bx
    pfln "------------"
    pfln
    pfln "System halted"
    call halt

    pop si
    pop dx
    pop cx
    pop bx

    clc
    leave
    ret


ExceptionStrings:
.ex00: db 'Divide Error',0x00
.ex01: db 'Single Step',0x00
.ex02: db 'NMI',0x00
.ex03: db 'Breakpoint',0x00
.ex04: db 'INTO Detected Overflow',0x00
.ex05: db 'BOUND Range Exceeded',0x00
.ex06: db 'Invalid Opcode',0x00
.ex07: db 'Coprocessor Not Available',0x00
.ex08: db 'Double Fault',0x00
.ex09: db 'Coprocessor Segment Overrun',0x00
.ex0a: db 'Invalid TSS',0x00
.ex0b: db 'Segment Not Present',0x00
.ex0c: db 'Stack Fault',0x00
.ex0d: db 'General Protection Fault',0x00
.ex0e: db 'Intel Reserved (Page Fault on 386)',0x00
.ex0f: db 'Intel Reserved',0x00
.ex10: db 'Coprocessor Error',0x00
.pointers:
    dw .ex00
    dw .ex01
    dw .ex02
    dw .ex03
    dw .ex04
    dw .ex05
    dw .ex06
    dw .ex07
    dw .ex08
    dw .ex09
    dw .ex0a
    dw .ex0b
    dw .ex0c
    dw .ex0d
    dw .ex0e
    dw .ex0f
    dw .ex10
