; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: SimpleDebug library. Provides simple debugging macros and code.
;
;  Target OS: Any
;
;  Dependencies: Not dependent on other libraries.
;
;  Notes: use options.DebugMode={1|0} to enable/disable debug output of this library.
;_________________________________________________________________________________________
module "Simple debug library"

SimpleDebugLoaded = 1

include "%TargetOS%/debug.asm"


match =DBG_CHAR_NEWLINE, DBG_CHAR_NEWLINE {
  DBG_CHAR_NEWLINE equ $0d, $0a
}


macro dbrk {
if defined options.DebugMode & options.DebugMode
        int3
        nop
end if
}

macro DebugMsg msg {
common
if defined options.DebugMode & options.DebugMode
  local ..msg, ..skip

        stdcall DebugLock

        stdcall Output, ..msg
        jmp     ..skip
  label ..msg byte
    if ~msg eq
        db msg
    end if
        db DBG_CHAR_NEWLINE, 0

  label ..skip

        stdcall DebugUnlock
end if
}


macro Message msg {
common
  local ..msg, ..skip

        stdcall Output, ..msg
        jmp     ..skip
  ..msg db msg, DBG_CHAR_NEWLINE, 0

  label ..skip
}



macro Output string {
if defined options.DebugMode & options.DebugMode = 1
        stdcall Output, string
end if
}


macro OutputLn string {
if defined options.DebugMode & options.DebugMode = 1
        stdcall Output, string
        stdcall Output, <" ", DBG_CHAR_NEWLINE>
end if
}



proc OutputMemory, .ptr, .size
begin
        pushad

        mov     esi, [.ptr]
        mov     edx, [.size]

.loop:
        stdcall OutputNumber, esi, 16, 8
        stdcall Output, .cSimpDbgSep1

        mov     ecx, 4

.lineloop:
        stdcall OutputNumber, [esi], 16, 8
        stdcall Output, .cSimpDbgSep2
        lea     esi, [esi+4]

        sub     edx, 4
        cmp     edx, 0
        jle     .exit

        dec     ecx
        jnz     .lineloop

.exit:
        stdcall Output, .cSimpDbgNewLine

        cmp     edx, 0
        jg      .loop

        stdcall Output, .cSimpDbgNewLine

        popad
        return

.cSimpDbgSep1 db ': ',0
.cSimpDbgSep2 db '  ',0
.cSimpDbgNewLine db DBG_CHAR_NEWLINE,0


endp

macro OutputMemory pointer, size {
if defined options.DebugMode & options.DebugMode
        stdcall OutputMemory, pointer, size
end if
}


proc OutputMemoryByte, .ptr, .size
.buffer dd ?
begin
        pushad

        mov     esi, [.ptr]
        mov     edx, [.size]

.loop:
        mov     eax, esi
        sub     eax, [.ptr]
        stdcall OutputNumber, eax, 16, 8
        stdcall Output, .cSimpDbgSep1

        mov     ecx, 16

        push    esi edx

.lineloop1:
        movzx   eax, byte [esi]

        stdcall OutputNumber, eax, 16, 2
        stdcall Output, .cSimpDbgSep2

        inc     esi
        dec     edx
        dec     ecx
        jz      .ascii

        test    edx, edx
        jnz     .lineloop1


.align_loop:
        jecxz   .ascii

        stdcall Output, .cSimpDbgSep3
        dec     ecx
        jnz     .align_loop


.ascii:
        stdcall Output, .cSimpDbgSep1

        mov     ecx, 16
        pop     edx esi

.lineloop2:

        xor     eax, eax
        mov     al, [esi]
        mov     ah, " "

        cmp     al, ah
        jae     @f
        mov     al, "."
@@:
        mov     [.buffer], eax

        lea     eax, [.buffer]
        stdcall Output, eax

        inc     esi
        dec     edx
        dec     ecx
        jz      .exit

        test    edx, edx
        jnz     .lineloop2


.exit:
        stdcall Output, .cSimpDbgNewLine

        cmp     edx, 0
        jne     .loop

        stdcall Output, .cSimpDbgNewLine

        popad
        return

.cSimpDbgSep1 db ': ',0
.cSimpDbgSep2 db '  ',0
.cSimpDbgSep3 db '    ',0
.cSimpDbgNewLine db DBG_CHAR_NEWLINE,0


endp


macro OutputMemoryByte pointer, size {
if defined options.DebugMode & options.DebugMode
        stdcall OutputMemoryByte, pointer, size
end if
}



proc OutputNumber, .number, .radix, .digits
begin
        pushad

        cld
        mov     edi, _simpdebugstr

        mov     eax, [.number]
        mov     ecx, [.radix]
        mov     esi, [.digits]

        test    esi, esi
        jz      .varU
        jns     .fixed

        call    __simpNumToStr
        jmp     .end

.varU:
        call    __simpNumToStrU
        jmp     .end

.fixed:
        call    __simpNumToStrUF

.end:
        xor     eax, eax
        stosd

        stdcall Output, _simpdebugstr

.exit:
        popad
        return
endp


macro OutputNumber number, radix, digits {
if defined options.DebugMode & options.DebugMode
        stdcall OutputNumber, number, radix, digits
end if
}



proc OutputRegister, .reg, .radix
begin
        pushad

        cmp     [.reg], 7
        ja      .exit

        cld
        mov     edi, _simpdebugstr
        mov     ebx, [.reg]

        mov     eax, [.regnames+4*ebx]
        stosd

        mov     eax, [esp+4*ebx]
        mov     ecx, [.radix]
        mov     esi, 8

        call    __simpNumToStrUF
        mov     eax, dword [.tempstr]
        stosd

        stdcall Output, _simpdebugstr

.exit:
        popad
        return
  .tempstr db DBG_CHAR_NEWLINE
           dd 0
           align 4

  .regnames:
        dd      'edi='
        dd      'esi='
        dd      'ebp='
        dd      'esp='
        dd      'ebx='
        dd      'edx='
        dd      'ecx='
        dd      'eax='
endp



macro OutputRegister reg, radix {
if defined options.DebugMode & options.DebugMode
        stdcall OutputRegister, reg, radix
end if
}


macro OutputRect x, y, width, height {
if defined options.DebugMode & options.DebugMode
  local ..crlf, ..skip, ..ss

  stdcall  Output, 'Rectangle: x='
  stdcall  OutputNumber, x, 10, -1
  stdcall  Output, ..ss
  stdcall  OutputNumber, y, 10, -1
  stdcall  Output, '  width='
  stdcall  OutputNumber, width, 10, -1
  stdcall  Output, '  height='
  stdcall  OutputNumber, height, 10, -1
  stdcall  Output, ..crlf
  jmp      ..skip
  ..crlf db DBG_CHAR_NEWLINE, 00
  ..ss   db ' y=', 0
label ..skip
end if
}


macro OutputValue lbl, val, radix, digits {
if defined options.DebugMode & options.DebugMode
  local ..crlf, ..txt, ..skip

  stdcall  DebugLock

  stdcall  Output, ..txt
  stdcall  OutputNumber, val, radix, digits
  stdcall  Output, ..crlf
  jmp      ..skip

  ..txt  db lbl, 0
  ..crlf db DBG_CHAR_NEWLINE, 00

label ..skip
  stdcall  DebugUnlock
end if
}



uglobal
  if used _simpdebugstr
    _simpdebugstr rb 256
  end if
endg



proc __simpStrLength;, .pString
begin
        mov     ecx, [esp+4]
        jecxz   .ret

.scan:
        cmp     byte [ecx], 0
        lea     ecx, [ecx+1]
        jne     .scan

        stc
        sbb     ecx, [esp+4]

.ret:
        retn 4
endp


;*****************************************************
; NumToStrF:
;   Converts signed integer value to string.
; NumToStrUF:
;   Converts unsigned integer value to string.
;
; edi - pointer to string buffer
; eax - Number to convert
; ecx - radix from 2 to $ff
; esi - length of the number in chars
;
; returns: edi - pointer to the end of converted num
;
; Note: Don't use 1 as radix.
;*****************************************************
proc __simpNumToStrF
begin
        test    eax,eax
        jns     __simpNumToStrUF
        neg     eax
        mov     byte [edi],'-'
        push    esi
        dec     esi
        add     edi,esi
        push    edi
        jmp     __simpNumToStrUF.loopc
endp

proc __simpNumToStrUF
begin
        push    esi
        add     edi, esi
        push    edi
        dec     edi
.loopc:
        xor     edx,edx
        div     ecx
        xchg    al,dl
        cmp     al,$0a
        sbb     al,$69
        das
        mov     [edi],al
        dec     edi
        xchg    al,dl
        dec     esi
        jnz     .loopc
        pop     edi
        pop     esi
        return
endp


;*****************************************************
; _simpNumToStr:
;   Converts signed integer value to string.
; _simpNumToStrU:
;   Converts unsigned integer value to string.
;
; edi - pointer to string buffer
; eax - Number to convert
; ecx - radix from 2 to $ff
;
; returns: edi - pointer to the end of converted num
;
; Note: Don't use 1 as radix.
;*****************************************************
proc __simpNumToStr
begin
    test  eax,eax
    jns   __simpNumToStrU
    neg   eax
    mov   byte [edi],"-"
    inc   edi
endp

proc __simpNumToStrU
begin
    cmp   eax,ecx
    jb    .lessA
    xor   edx,edx
    div   ecx
    push  edx
    call  __simpNumToStrU
    pop   eax
.lessA:
    cmp   al, 10
    sbb   al, 69h
    das
    stosb
    return
endp



uglobal
  DebugBusy dd ?
endg


proc DebugLock
begin
        pushad

.loop:
        xor     eax, eax
        dec     eax

        xchg    eax, [DebugBusy]
        test    eax, eax
        jz      .lock_ok

        stdcall Sleep, 10
        jmp     .loop

.lock_ok:
        popad
        return
endp



proc DebugUnlock
begin
        push    eax
        xor     eax, eax
        mov     [DebugBusy], eax
        pop     eax
        return
endp





endmodule