; $Id: buffer.asm,v 1.5 2000-02-21 09:24:02 bird Exp $
;
; Simple resident buffer for use when overloading tkExecPgm.
;
; Copyright (c) 2000 knut st. osmundsen
;
; Project Odin Software License can be found in LICENSE.TXT
;
    .486p


;
;   Defined Constants And Macros
;
NBR_BUFFERS     EQU 20
BUFFER_SIZE     EQU 1536

;
;   Include files
;
    include devsegdf.inc

;
;   Exported symbols
;
    public AcquireBuffer
    public ReleaseBuffer
    public QueryBufferSegmentOffset
    public QueryBufferPointerFromFilename


;
;   Imported Functions
;
    extrn stricmp:PROC


;
;   Global Variables
;
DATA16 SEGMENT
aachBuffer  db BUFFER_SIZE*NBR_BUFFERS dup(?) ; The buffer
DATA16 ENDS

DATA32 SEGMENT
afBuffer    db NBR_BUFFERS dup(0)       ; Access "semaphore"
                                        ; 0 - not in use
                                        ; 1 - in use
DATA32 ENDS


CODE32 segment
    assume CS:CODE32, DS:NOTHING, SS:NOTHING

;;
; Aquires a resident buffer. (intended use for tkExecPgm)
; @cproto    assembly only for time being.
; @returns   Pointer to buffer
; @uses      eax
; @sketch    if AfBuffer == 0 then
;               ok!
;               fBuffer <- 1
;               return pointer to buffer
;            else
;               fail
;               return NULL
;            endif
; @status    completely implemented.
; @author    knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
; @remark    cbBuffer holds the size of the buffer.
AcquireBuffer PROC NEAR
    push    ds
    push    ecx
    push    edx

    ; make ds flat
    mov     ax, seg FLAT:DATA32
    mov     ds, ax
    ASSUME  DS:FLAT

    ; loop thru all buffers and try reserve one
    mov     ah, 1                       ; afBuffer[ecx] is set to this value (if equal to al)
    mov     ecx, NBR_BUFFERS            ; interations.
    mov     edx, offset FLAT:afBuffer
    add     edx, ecx
AcquireBuffer_loop:
    dec     edx
    mov     al, 0                       ; afBuffer[ecx] should have this value
    lock cmpxchg [edx], ah
    je      AcquireBuffer_ok
    loop    AcquireBuffer_loop

    ; failure
AcquireBuffer_nok:
    xor     eax,eax
    jmp     AcquireBuffer_ret

    ;success - calc buffer pointer
AcquireBuffer_ok:
    mov     eax, BUFFER_SIZE
    dec     ecx
    imul    eax, ecx                    ; ecx has the buffer number
    add     eax, offset FLAT:aachBuffer
AcquireBuffer_ret:
    pop     edx
    pop     ecx
    pop     ds
    ret
AcquireBuffer ENDP

;;
; Release the resident buffer pointed to by eax from use.
; @cproto    assembly only for time being.
; @returns   0 on success, 87 on error.
; @param     eax  Pointer to buffer.
; @uses      eax
; @equiv
; @status
; @author    knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
; @remark
ReleaseBuffer PROC NEAR
    ASSUME  DS:NOTHING
    push    ds
    push    ecx
    push    edx

    ; make ds flat
    push    eax
    mov     ax, seg FLAT:DATA32
    mov     ds, ax
    ASSUME  DS:FLAT
    pop     eax
    push    eax

    ; validate input and calc buffer number
    cmp     eax, offset FLAT:aachBuffer
    jl      ReleaseBuffer_nok           ; if eax (buffer pointer) is less than the first buffer, then fail.
    sub     eax, offset FLAT:aachBuffer ; eax <- offset to buffer from aachBuffer
    xor     edx, edx
    mov     ecx, BUFFER_SIZE
    div     ecx                         ; eax <- buffer number, edx <- offset into buffer (should be NULL)
    or      edx, edx
    jnz     ReleaseBuffer_nok           ; if offset into buffer not 0 the fail.
    cmp     eax, NBR_BUFFERS
    jge     ReleaseBuffer_nok           ; if buffernumber >= number of buffers then fail.

    ; unlock buffer - if locked
    mov     edx, eax
    add     edx, offset FLAT:afBuffer   ; ds:edx  points at buffer "semaphore"
    mov     al, 1
    mov     ah, 0
    lock cmpxchg [edx], ah
    jne     ReleaseBuffer_nok           ; fail if buffer was not locked

ReleaseBuffer_ok:
    ;swipe out buffer
    pop     eax
    push    edi
    push    es
    mov     edi, eax
    mov     ax, ds
    mov     es, ax
    ASSUME  es:FLAT
    xor     eax, eax                    ; ecx is allready BUFFER_SIZE
    rep     stosb
    pop     es
    pop     edi

    ;return successfully
    jmp     ReleaseBuffer_ret

ReleaseBuffer_nok:
    ;failure
    pop     eax
    mov     eax, 87 ;some error

ReleaseBuffer_ret:
    pop     edx
    pop     ecx
    pop     ds
    ret
ReleaseBuffer ENDP


;;
; Gets the 16-bit segment and offset for this buffer.
; @cproto    assembly only for time being.
; @returns   segment in es and offset in eax
;            On error high word of eax is 87.
; @param     eax  Buffer pointer.
; @uses      eax, es
; @status    completely implemented.
; @author    knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
; @remark    cbBuffer holds the size of the buffer.
QueryBufferSegmentOffset PROC NEAR
    ASSUME  DS:NOTHING
    push    ds

    ; make ds FLAT
    push    eax
    mov     ax, seg FLAT:DATA32
    mov     ds, ax
    ASSUME  DS:FLAT
    pop     eax

    ; validate parameter and calc offset relative to aachBuffer
    cmp     eax, offset FLAT:aachBuffer
    jl      QueryBufferSegmentOffset_nok
    sub     eax, offset FLAT:aachBuffer
    cmp     eax, NBR_BUFFERS * BUFFER_SIZE
    jge     QueryBufferSegmentOffset_nok

QueryBufferSegmentOffset_ok:
    jmp     far ptr CODE16:GetBufferSegmentOffset16
QueryBufferSegmentOffset_Back::
    pop     ds
    ret

QueryBufferSegmentOffset_nok:
    mov     eax, 00570000h
    pop     ds
    ret
QueryBufferSegmentOffset ENDP


;;
; Special function which checks all used buffers for the string passed in in eax.
; @cproto    PCHAR _Optlink QueryBufferPointerFromFilename(const char *pszFilename)
; @returns   Pointer to Buffer. NULL on error/notfound.
; @param     ds:eax   Pointer to filename. The filename will later be compared to the string at buffer start.
;                     Currently this parameter is not used, since there is only one buffer.
; @uses      eax.
; @sketch
; @status    completely implemented
; @author    knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
; @remark    assumes ds flat on call.
QueryBufferPointerFromFilename PROC NEAR
    ASSUME  cs:CODE32, ds:FLAT
    push    ecx
    push    edx
    push    ebx

    ; loop thru all buffers until found or non left
    mov     ecx, NBR_BUFFERS
    mov     edx, BUFFER_SIZE
    imul    edx, ecx                    ; edx <- sizeof(aachBuffer)
    add     edx, offset FLAT:aachBuffer ; edx points at the first byte after the buffers.
    mov     ebx, offset FLAT:afBuffer
    add     ebx, ecx                    ; edx points at the first byte after the "semaphore" array (afBuffer).
QueryBufferPointerFromFilename_loop:
    sub     edx, BUFFER_SIZE            ; edx points at the buffer being concidered.
    dec     ebx                         ; ebx points at the "semaphore" for the buffer being concidered.
    cmp     byte ptr [ebx], 1           ; Is buffer in use?
    jne     QueryBufferPointerFromFilename_next

    ; buffer is active - lets compare
    push    eax
    push    ecx
    push    edx
    sub     esp, 8
    call    stricmp
    add     esp, 8
    pop     edx
    pop     ecx
    or      eax, eax
    jz      QueryBufferPointerFromFilename_found
    pop     eax

QueryBufferPointerFromFilename_next:
    loop QueryBufferPointerFromFilename_loop
    ; when we exit this loop we have failed!

QueryBufferPointerFromFilename_nok:
    ; The buffer was not found, return NULL pointer.
    xor     eax, eax
    pop     ebx
    pop     edx
    pop     ecx
    ret

QueryBufferPointerFromFilename_found:
    ; The buffer was found, return the pointer to it!
    pop     eax
    pop     ebx
    mov     eax, edx
    pop     edx
    pop     ecx
    ret

QueryBufferPointerFromFilename ENDP




CODE32 ENDS


CODE16 SEGMENT
;;
; Gets the segment(->es) and offset(+ax -> ax) of the achBuffer.
; @param    ax  offset to buffer relative to aachBuffer
; Jumps back to GetBufferOffset32
GetBufferSegmentOffset16:
    ASSUME CS:CODE16, DS:NOTHING
    push    ax
    mov     ax, seg aachBuffer
    mov     es, ax
    pop     ax
    add     ax, offset aachBuffer
    jmp     far ptr FLAT:QueryBufferSegmentOffset_Back
CODE16 ENDS

END

