; 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 mod(x,y)        _mod x,y        ; mod :: int16 -> int16 -> int16
%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
%else
    extern Multiply
%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

; carry <num args>
%macro carry 1
    jc %%carry
    add sp,(%1*2)
    clc
    jmp %%end

    %%carry:
        add sp,(%1*2)
        stc
    %%end:
%endmacro

%endif