; std headers
%pragma once
%ifndef Xstd
%define Xstd
bits 16

%define multiply(x,y)   _multiply x,y     ; multiply :: int16 -> int16 -> int16
%define divide(x,y)     _divide x,y       ; divide :: int16 -> int16 -> int16
%define divide64(x,y,z)  _divmodtmp x,y,z,0
;%define divide64(x,y,z) _divmod64 x,y,z,0 ; divide64 :: int64 *seg:number
                                          ;     -> int16 divisor
                                          ;     -> int64 *seg:addr
%define mod(x,y)        _mod x,y          ; mod :: int16 -> int16 -> int16
%define mod64(x,y,z)      _divmodtmp x,y,z,1
; %define mod64(x,y,z)    _divmod64 x,y,z,1 ; mod64 :: int64 *seg:number
                                          ;     -> int16 divisor
                                          ;     -> int16 *seg:addr
%define iopl2ring(x)    ((x*3) % 4)       ; iopl2ring :: int8 iopl -> int8 ring
%define ring2iopl(x)    iopl2ring(x)      ; ring2iopl :: int8 ring -> int8 iopl
%define setiopl(x)      _setiopl x        ; setiopl :: int8 iopl -> bool
%define getiopl()       _getiopl          ; getiopl :: void -> int8 iopl
%define getring()       _getring          ; getring :: void -> int8 ring

%define IORing0 0
%define IORing2 2
%define IORing3 1

%ifdef xstd
    global Multiply
    global Divmod64
%else
    extern Multiply
    extern Divmod64
%endif

%macro _getiopl 0
    pushf
    pop ax
    shr ax,13
    and ax,0b11
%endmacro

%macro _setiopl 1
    push bx
    pushf
    pop ax      ; ax=flags
    xor bx,bx
    mov bx,%1
    and bx,0b11

    shl bx,13   ; bx=new iopl
    and ax,0b1001111111111111
    or ax,bx
    push ax
    popf

    xor bx,bx
    mov bx,%1
    ; ax=
        getiopl()
    cmp ax,bx
    jne %%err
 %%end:
    pop bx
    xor ax,ax
    clc
    jmp %%exit
 %%err:
    pop bx
    mov ax,0xff
    stc
 %%exit:
%endmacro

%macro _getring 0
    push bx

    ; ax=
        getiopl()
    mov bx,ax
    ; ax=
        multiply(bx,3)
    mov bx,ax
    ; ax=
        mod(bx,4)
    
    pop bx
%endmacro
    
%macro _divide 2
    %if %2 = 0
        %error "divide(): division by zero is a rookie move"
    %endif

    push bx
    push dx

    mov ax,%2
    cmp ax,0
    je %%err

    mov ax,%1
    mov bx,%2
    xor dx,dx
    div bx
    jmp %%end

 %%err:
    pop dx
    pop bx
    mov ax,0xffff
    stc
    jmp %%exit

 %%end:
    pop dx
    pop bx
    clc
 %%exit:
%endmacro

%macro _mod 2
    %if %2 = 0
        %error "mod(): division by zero is a rookie move"
    %endif

    push bx
    push dx

    mov ax,%2
    cmp ax,0
    je %%err

    mov ax,%1
    mov bx,%2
    xor dx,dx
    div bx
    jmp %%end

 %%err:
    pop dx
    pop bx
    mov ax,0xffff
    stc
    jmp %%exit

 %%end:
    mov ax,dx
    pop dx
    pop bx
    clc
 %%exit:
%endmacro

%macro _multiply 2
    mov ax,%2
    push ax
    mov ax,%1
    push ax
    call Multiply
    add sp,4
%endmacro

; BROKEN
; _divmodtmp :: int64 *seg:number -> int32 divisor -> bool ismod
;   -> result32 [hi?]:low
%macro _divmodtmp 4
    push ebx
    push ecx
    push edx
    push edi

    chseg(ds,%1)
        mov bx,%2
        mov word dx,ds:[bx+6]
        shl dword edx,16
        mov word dx,ds:[bx+4]

        mov word ax,ds:[bx+2]
        shl dword eax,16
        mov word ax,ds:[bx+0]
        
        mov dword ecx,%3
        div cx
        %if %4 = 0
            mov dword edi,eax
        %else
            mov dword edi,edx
        %endif
    sret(ds)

    mov dword eax,edi
    pop edi
    pop edx
    pop ecx
    pop ebx

    clc
%endmacro

; _divmod64 :: int64 *seg:number -> int16 divisor -> bool ismod
;   -> result32 *[hi?]:low
%macro _divmod64 4
 %if %4 != ax
    mov ax,%4
 %endif
    push ax
 %if %3 != ax
    mov ax,%3
 %endif
    push ax
 %if %2 != ax
    mov ax,%2
 %endif
    push ax
 %if %1 != ax
    mov ax,%1
 %endif
    and ax,1
    push ax

    call Divmod64
    add sp,8
%endmacro

; carry <num args>
%macro carry 1
    jc %%carry
    add sp,(%1*2)
    clc
    jmp %%end

    %%carry:
        add sp,(%1*2)
        stc
    %%end:
%endmacro

%endif