EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

memory.htm
Classes
BUFFER
LIST
POOL
STACK
STREAM
Macros
BufferClear
BufferCreate
BufferDecrement
BufferNew
BufferResize
BufferRetrieve
BufferStore
BufferStoreByte
BufferStoreDword
BufferStorePascalString
BufferStoreWord
ListCreate
ListGetFirst
ListGetLast
ListGetNext
ListGetPrev
ListInsert
ListNew
ListRemove
ListStore
PoolCreate
PoolDestroy
PoolGetSIze
PoolNew
PoolStore
StackClear
StackCreate
StackPeekLast
StackPeekPrev
StackPop
StackPush
StreamClear
StreamCreate
StreamDump
StreamGetLines
StreamGetSize
StreamReadByte
StreamReadLn
StreamReset
StreamRetrieve
StreamStore
StreamStoreByte
StreamStore$
StreamStoreDword
StreamStoreLn
StreamStoreWord

This file can be included to 32bit programs written in Euro Assembler. It contains OS-independent macros for dynamic memory management.
The file memory.htm defines memory allocation class POOL (similar to system heap functions), and its derivations BUFFER, LIST, STACK, STREAM. See also Memory management.

Derived methods Buffer*, List*, Stack*, Stream* allocate their memory from the object Pool, and the Pool allocation and deallocation depends on macroinstructions SysGetAllocationGranularity, SysAlloc and SysFree which encapsulate system calls. Those three system macros must be defined before any macro from this library is expanded.

For an example of MS Windows implementation of such functions see SysGetAllocationGranularity, SysAlloc and SysFree in easource/syswin.htm.
Memory management dependency ┌──────┬──────┬──────┬──────┐ │Buffer│ List │Stack │Stream│ └──────┴──────┴──────┴──────┘ ┌───────────────────────────┐ │ Pool │ └───────────────────────────┘ ┌───────────────────────────┐ │ Sys │ └───────────────────────────┘ ┌───────────────────────────┐ │ OS │ └───────────────────────────┘

All macros in this library return carry flag set if some error occurs, which usually signalizes lack of system memory or bad parameter. Returned values are not valid when CF=1.

Keyword parameter ErrorHandler= of macro PoolCreate allows to specify a callback procedure which will be invoked on allocation error. Allocation exceptions then can be treated by ErrorHandler (for instance by aborting the program gracefully), so the derived methods don't need to catch errors.


memory HEAD ; Library interface.
↑ POOL

Object POOL allocates virtual memory from operating system in big blocks (their size is multiple of virtual-memory granularity). Block are not continuous in addressing space, they are bound in a unidirectional list. Each pool block allocated from OS has the block size and the pointer to previous block in 2 DWORDs at its bottom. The POOL structure itself is created in the very first pool block.

Amount of continuos memory requested by PoolNew, BufferStore*, StreamStore* etc. is not limited. Whenever there is no enough free memory in the last pool block, PoolNew or PoolStore will request an additional empty pool block from OS, with size of the requested memory rounded up to allocation granularity.

Allocated memory block cannot be returned to OS individually. The whole list of pool blocks will be freed at once by PoolDestroy.

After PoolCreate. ┌─────────┐ ┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌ ├─────────┤<─┐ │ │ │.Ptr ├──┘ │ │ ├─────────┤ │ │ ┌─┤.Last │ │ POOL│ │ ├─────────┤ ┌─┤ │ │ │.ErrH │ │ │ │ │ ├─────────┤ │ │ │ │ │.Gran=64K│ │ │ └ │ ├─────────┤ │ │ │ │Prev=0 │ │ │ │ ├─────────┤ │ │ │ │Size=64K │<─┘ │ └>└─────────┘ ┘ After many PoolNew allocations. ┌─────────┐ ┐ The last block was increased. │ │ │ │ │ │ │ │ │ ┌──────────────────>│ │ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ ┌─────────┐ │ ┌─────────┐ │░░░░░░░░░│ ┌─┤ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │░░░░░░░░░│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ ┌ ├─────────┤ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ │.Ptr ├─┘ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ ├─────────┤ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ │.Last ├───┐ │░░░░░░░░░│ │░░░░░░░░░│ │ │ POOL│ ├─────────┤ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ │.ErrH │ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ ├─────────┤ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ │ │.Gran=64K│ │ │░░░░░░░░░│ │░░░░░░░░░│ │ │ └ ├─────────┤ │ ├─────────┤ ├─────────┤ │ │ │Prev=0 │ ┌─┼─┤ Prev │ ┌───┤ Prev │ │ │ ├─────────┤ │ │ ├─────────┤ │ ├─────────┤ │ │ │Size=64K │ │ │ │ Size=64K│ │ │Size=128K│<─┘ │ └─────────┘<┘ │ └─────────┘<┘ ┌>└─────────┘ ┘ │ │ └───────────────┘
POOL STRUC
 .Gran  D D ; Memory granularity (usually 64K).
 .ErrH  D D ; Address of error handler callback procedure. Ignored when 0.
 .Last  D D ; Pointer to the last pool block allocated from OS.
 .Ptr   D D ; Pointer to the unoccupied memory in the last pool block.
 ENDSTRUC POOL
 
↑ PoolCreate Size=64K, ErrorHandler=0

will allocate one block from OS virtual memory and create the POOL structure near its bottom.
This is the first pool block, other blocks may be allocated later on demand.

Input
Size=64K is the default size of memory blocks which will be automatically allocated from OS whenever the current block is full.
ErrorHandler=0 is a pointer to the callback procedure which is called when some POOL method detects an error. The procedure gets no input and it may change any register. This parameter is ignored when ErrorHandler=0 (default value).
Output
CF=0, EAX=pointer to the just created POOL structure (pool handle).
Error
CF=1, EAX=0, ErrorHandler is called before return from PoolCreate (if it is nonzero).
Depends on
SysAlloc, SysGetAllocationGranularity
PoolCreate %MACRO Size=64K, ErrorHandler=0
    PUSHD %ErrorHandler, %Size
    CALL PoolCreate@RT::
PoolCreate@RT:: PROC1
    PUSHAD
      MOV EBP,ESP
      XOR EAX,EAX
      MOV [EBP+28],EAX ; Initialize %ReturnEAX for the case of error.
      MOV ECX,[EBP+36] ; %Size.
      SysGetAllocationGranularity
      TEST EAX
      JZ .30: ; If SysGetAllocationGranularity failed, abort.
      MOV ESI,EAX
      MOV EDX,EAX
      ; ESI is granularity (64KB), ECX is requested 1st block size, EDX is the final Alloc size.
 .10: CMP ECX,EDX
      JBE .20:
      ADD EDX,ESI
      JMP .10:
 .20: SysAlloc EDX
 .30: MOV ECX,[EBP+40] ; Offset of ErrorHandler.
      JNZ .50: ; If SysAlloc worked OK, continue.
      STC
      JECXZ .90: ; If no ErrorHandler specified.
      CALL ECX
      STC
      JMP .90: ; Abort.
 .50: MOV EDI,EAX ; Address of the allocated memory block.
      MOV EAX,EDX ; Size of the allocated block.
      STOSD ; Store the size of this pool block as the first DWORD at its bottom.
      SUB EAX,EAX
      STOSD ; Pointer to the Prev block is zero.
      MOV EBX,EDI ; Where the POOL structure will be created.
      MOV [EBP+28],EDI ; %ReturnEAX will point to the POOL structure.
      SUB EDI,8 ; EDI now points to the first and only pool block.
      LEA EAX,[EBX + SIZE# POOL] ; EAX now points to the vacant memory space.
      MOV [EBX+POOL.Gran],ESI
      MOV [EBX+POOL.ErrH],ECX
      MOV [EBX+POOL.Last],EDI
      MOV [EBX+POOL.Ptr],EAX
 .90: MOV ESP,EBP
     POPAD
     RET 2*4
  ENDPROC1 PoolCreate@RT::
 %ENDMACRO PoolCreate
↑ PoolDestroy aPool
will free all pool blocks allocated from operating system.
Input
aPool is a pointer to POOL structure which was returned from PoolCreate.
Output
CF=0, EAX=total amount of just released memory in bytes.
Error
CF=1 if deallocation failed.
EAX=total amount of just released memory until deallocation failed.
Depends on
SysFree
PoolDestroy %MACRO aPool
      PUSHD %aPool
      CALL PoolDestroy@RT::
PoolDestroy@RT:: PROC1
      PUSHAD
       MOV EBP,ESP
       MOV EBX,[EBP+36] ; Pointer to the POOL structure.
       SUB ECX,ECX      ; ECX is a counter of total freed memory of this pool.
       TEST EBX
       JZ .90:          ; Abort if bad aPool was provided.
       MOV ESI,[EBX+POOL.Last] ; Begin deallocation with the last block.
.10:   TEST ESI         ; VA of the pool block which is being deallocated.
       JZ .90:          ; If there are no more pool blocks on the list.
       MOV EAX,[ESI+0]  ; Brutto size of the block.
       MOV EDI,[ESI+4]  ; Pointer to the previous block in the list.
       ADD ECX,EAX      ; Total released pool size.
       SysFree ESI,EAX  ; Ask OS to free the memory block addressed with ESI.
       JZ .Error:
       MOV ESI,EDI      ; Try the previous block.
       JMP .10:
.Error:STC
.90:   MOV [EBP+28],ECX ; Total amount of freed memory in %ReturnEAX.
      POPAD
      RET 4
  ENDPROC1 PoolDestroy@RT
%ENDMACRO PoolDestroy
↑ PoolGetSize aPool
returns total allocated size of all pool blocks.
Input
aPool is a pointer to POOL structure which was returned from PoolCreate.
Output
ECX= total comitted size of all pool blocks.
Error
-
PoolGetSize %MACRO aPool
      PUSH EBX,ESI
        MOV EBX,%aPool
        SUB ECX,ECX
        MOV ESI,[EBX+POOL.Last]
PoolGetSizeA%.:
        TEST ESI
        JZ PoolGetSizeZ%.:
        ADD ECX,[ESI+0]  ; Brutto size of the block.
        MOV ESI,[ESI+4]  ; Pointer to the previous block in the list.
        JMP PoolGetSizeA%.:
PoolGetSizeZ%.:
      POP ESI,EBX
  %ENDMACRO PoolGetSize
↑ PoolNew aPool, DataSize, Align=DWORD, ZeroTerminate=No
will reserve a piece of memory (DataSize bytes long) from aPool. Contents of the reserved memory is undefined.
This macro PoolNew requires another macro SysAlloc be defined before, which provides the interaction with OS.
Input
aPool is a pointer to POOL structure which was returned from PoolCreate
DataSize is requested size in bytes.
Align= may specify BYTE/UNICHAR/WORD/DWORD/QWORD/OWORD or shortcuts B/U/W/D/Q/O (case insensitive) as requested alignment of the allocated memory.
ZeroTerminate=No If Yes, the requested DataSize size will be incremented by 1, making room for zero-byte terminating. This is used by PoolStore.
Output
CF=0
EAX=pointer to the allocated memory in pool block of memory.
Error
CF=1 if VirtualAlloc() failed. Handler POOL.ErrH was called.
EAX=0
Depends on
SysAlloc
PoolNew %MACRO aPool, DataSize, Align=DWORD, ZeroTerminate=NO
        %PoolNewFlags %SETA "%ZeroTerminate[1]" !== "N" & 0x8000_0000
        %PoolNewFlags %SETA %PoolNewFlags | "%Align[1]" == "O" & 15
        %PoolNewFlags %SETA %PoolNewFlags | "%Align[1]" == "Q" & 7
        %PoolNewFlags %SETA %PoolNewFlags | "%Align[1]" == "D" & 3
        %PoolNewFlags %SETA %PoolNewFlags | "%Align[1]" == "W" & 1
        %PoolNewFlags %SETA %PoolNewFlags | "%Align[1]" == "U" & 1
        PUSHD %PoolNewFlags, %DataSize, %aPool
        CALL PoolNew@RT::
PoolNew@RT:: PROC1
     PUSHAD
      MOV EBP,ESP
      MOV EBX,[EBP+36] ; %aPool.
      XOR EDI,EDI
      TEST EBX
      JZ .60: ; Error if no pool provided.
.10:  MOV ECX,[EBP+40] ; %DataSize.
      MOV EDX,[EBP+44] ; %PoolNewFlags.
      BTR EDX,31 ; ZeroTerminate?
      ADC ECX,8 ; Increment if ZeroTerminated=Yes and add 8 bytes for two bottom DWORDs.
      MOV EDI,[EBX+POOL.Ptr]
      ADD EDI,EDX ; Apply the alignment. EDX=0,1,3,7,15.
      NOT EDX
      AND EDI,EDX ; EDI is now aligned pointer to the new area.
      LEA EDX,[EDI+ECX] ; EDX is now pointer to the end of requested area.
      MOV ESI,[EBX+POOL.Last] ; Pointer to the current pool block with our area.
      ADD ESI,[ESI+0] ; Add pool block size. ESI now points to the top of the last pool block.
      CMP EDX,ESI
      JA .30: ; Skip if the new area does not fit into remaining space in the current pool block.
      MOV [EBX+POOL.Ptr],EDX ; Update pointer to the free space in the last pool block.
      CLC
      JMP .80: ; Done. EDI is the aligned memory pointer.
.30:  ; Not enough free space in this pool block. Allocate a new pool block.
      ; EBX=^POOL, ECX=DataSize.
      MOV EDX,[EBX-8] ; Default block size is in DWORD below the POOL pointed to with EBX.
.40:  CMP ECX,EDX
      JBE .50: ; Continue when the requested size ECX is below the future pool block size EDX.
      ADD EDX,[EBX+POOL.Gran] ; Otherwise this particular pool block must be enlarged.
      JMP .40:
.50:  SysAlloc EDX
      MOV EDI,EAX
      JNZ .70: ; If allocation succeeded.
      MOV ECX,[EBX+POOL.ErrH] ; Allocation error happened.
      JECXZ .60:
      CALL ECX
.60:  SUB EDI,EDI
      STC
      JMP .80:
.70:  MOV ECX,[EBX+POOL.Last]
      MOV [EBX+POOL.Last],EDI
      MOV [EDI+0],EDX ; Store the size of the new pool block.
      MOV [EDI+4],ECX ; Store the pointer to its previous pool block.
      ADD EDI,8
      MOV [EBX+POOL.Ptr],EDI
      JMP .10: ; This time the reservation attempt will not fail.
 .80: MOV [%ReturnEAX],EDI
 .90: MOV ESP,EBP
     POPAD
     RET 12
   ENDPROC1 PoolNew@RT
 %ENDMACRO PoolNew
↑ PoolStore aPool, DataPtr, DataSize, Align=BYTE, ZeroTerminate=No
This inline macro PoolStore will reserve DataSize bytes of memory on pool and copy the Data there.
Input
aPool is a pointer to POOL structure which was returned from PoolCreate.
DataPtr points to the data which should be stored on pool.
DataSize is the size of data in bytes.
Align= may specify BYTE/UNICHAR/WORD/DWORD/QWORD/OWORD or shortcuts B/U/W/D/Q/O (case insensitive) as alignment of the stored data.
ZeroTerminate=No. If Yes, one NULL character is stored after the data on aPool.
Output
CF=0
EAX=pointer to the just stored data in pool block.
Error
CF=1 if VirtualAlloc() failed. Handler POOL.ErrH was called.
EAX=0
Depends on
PoolNew
PoolStore %MACRO aPool, DataPtr, DataSize, Align=BYTE, ZeroTerminate=No
            PUSH %DataSize, %DataPtr, %aPool
            PUSH ECX,ESI,EDI
              MOV EAX,[ESP+12] ; %aPool.
              MOV ESI,[ESP+16] ; %DataPtr.
              MOV ECX,[ESP+20] ; %DataSize.
              PoolNew EAX,ECX,Align=%Align,ZeroTerminate=%ZeroTerminate
:             JC PoolStoreError%.:
              MOV EDI,EAX
              REP MOVSB
              %IF "%ZeroTerminate[1]" !== "N"
                MOV [EDI],CL
              %ENDIF
PoolStoreError%.:
            POP EDI,ESI,ECX
            LEA ESP,[ESP+3*4] ; Discard 3 pushed arguments, keep CF.
         %ENDMACRO PoolStore
↑ BUFFER

Object BUFFER is an unformated storage for objects (strings) of arbitrary size.

Items with fixed or variable size can be stored to the buffer one after another and the entire buffer content is always available as a continuous block.

The initial Size specified at BUFFER creation should be large enough to accomodate all expected data. Whenever the total buffer size tries to exceed the allocated size specified at buffer creation, data area is reallocated with doubled size, the old buffer contents is copied to the new position and the original buffer space is abandoned. The BUFFER structure itself always stays in its original position.

Buffer can be destroyed only when the hosting pool is destroyed but it can be cleared and reused.

Not yet exhausted buffer ┌──>┌───────┐┐ │ │ ││ │┌─>│ ││ ││ │░░░░░░░││ ││ │░░░░░░░││Size ││ │░░░░░░░││ ││ │░░░░░░░││ ││ │░░░░░░░││ ││┌>├───────┤┘ ││└─│.Bottom│ ││ ├───────┤ │└──│.Ptr │ │ ├───────┤ └───│.Top │ ├───────┤ │.Pool │ └───────┘ Reallocated buffer with doubled Size ┌────────>┌───────┐┐ │ │ ││ │ │ ││ │ │ ││ │ │ ││ │ │ ││ │ ┌─────>│ ││ │ │ │░░░░░░░││ ┌───────┐┐ │ │ │░░░░░░░││2*Size │ ││ │ │ │░░░░░░░││ │ ││ │ │ │░░░░░░░││ │░░░░░░░││ │ │ │░░░░░░░││ │░waste░││Size│ │ │░░░░░░░││ │░░░░░░░││ │ │ │░░░░░░░││ │░░░░░░░││ │ │ │░░░░░░░││ │░░░░░░░││ │ │ │░░░░░░░││ ├───────┤┘ │ │ ┌───>└───────┘┘ │.Bottom│─────┼──┼─┘ ├───────┤ │ │ │.Ptr │─────┼──┘ ├───────┤ │ │.Top │─────┘ ├───────┤ │.Pool │ └───────┘
BUFFER STRUC
 .Pool   D D ; Pool handle obtained from PoolCreate.
 .Top    D D ; Pointer to the end of allocated buffer space.
 .Ptr    D D ; Ptr to the next free position in buffer.
 .Bottom D D ; Ptr to the beginning of buffer data.
 ENDSTRUC BUFFER
↑ BufferCreate aPool, Size=16K
will allocate an empty (cleared) buffer on the pool specified with the first parameter. Buffer memory is DWORD aligned.
Input
aPool is a pointer to POOL structure which was returned from PoolCreate.
Size= is the initial size of the BUFFER plus payload data.
When underestimated, it will be automatically increased if necessary but this wastes memory a little.
Output
CF=0, EAX=pointer to the BUFFER (buffer handle).
Error
CF=1, EAX=0
BufferCreate %MACRO aPool, Size=16K
      PUSHD %Size, %aPool
      CALL BufferCreate@RT::
BufferCreate@RT:: PROC1
     PUSHAD
      MOV EBP,ESP
      MOV EAX,1K        ; Minimal acceptable buffer size.
      MOV ECX,[%Param2] ; %Size.
      MOV EDX,[%Param1] ; aPool.
      CMP ECX,EAX
      JAE .20:
      XCHG EAX,ECX
.20:  PoolNew EDX,ECX, Align=DWORD
      MOV [%ReturnEAX],EAX
      JC .90:
      MOV EBX,EAX
      MOV [EBX+BUFFER.Pool],EDX
      LEA EAX,[EBX+SIZE#BUFFER]
      MOV [EBX+BUFFER.Ptr],EAX
      MOV [EBX+BUFFER.Bottom],EAX
      ADD ECX,EBX ; Also set CF=0.
      MOV [EBX+BUFFER.Top],ECX
.90: POPAD
     RET 8
   ENDPROC1 BufferCreate@RT::
 %ENDMACRO BufferCreate
↑ BufferClear aBuffer
marks the buffer as empty. It does not overwrite the old data.
Input
aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
Output
aBuffer was marked as empty.
Error
-
BufferClear %MACRO aBuffer
      PUSH ECX
        MOV ECX,%aBuffer
        JECXZ BufferClear%.: ; If bad aBuffer was provided, skip.
       PUSHD [ECX+BUFFER.Bottom]
       POPD  [ECX+BUFFER.Ptr]
      BufferClear%.:
      POP ECX
 %ENDMACRO BufferClear
↑ BufferNew aBuffer, DataSize
will allocate DataSize bytes on aBuffer and return pointer to the allocated memory.
Input
aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
DataSize is the requested allocation size.
Output
CF=0, EAX=pointer to the just allocated space on aBuffer.
Error
CF=1 EAX=0
BufferNew %MACRO aBuffer, DataSize
     PUSHD %DataSize, %aBuffer
     CALL BufferNew@RT::
BufferNew@RT:: PROC1
     PUSHAD
      MOV EBP,ESP
      SUB EAX,EAX
      MOV EBX,[%Param1] ; %aBuffer.
      MOV [%ReturnEAX],EAX ; For the case of error.
      TEST EBX
      STC
      JZ .90: ; If bad buffer provided.
 .10: MOV ECX,[%Param2] ; %DataSize
      MOV EAX,[EBX+BUFFER.Ptr]
      MOV [%ReturnEAX],EAX
      TEST EAX
      JNZ .20:
      ; Buffer not initialized or other error. Abort.
      MOV ECX,[EBX+BUFFER.Pool]
      JECXZ .15:
      MOV EAX,[ECX+POOL.ErrH]
      TEST EAX
      JZ .15:
      CALL EAX
 .15: STC
      JMP .90: ; Abort.
 .20: ADD EAX,ECX ; EAX is now the top of wanna-be-allocated area.
      CMP EAX,[EBX+BUFFER.Top]
      JBE .70: ; If free space on buffer is sufficient.
      ; Buffer is too small. Allocate new space with doubled (or more) size.
      MOV EAX,[EBX+BUFFER.Top]
      SUB EAX,[EBX+BUFFER.Bottom]
      MOV EDX,EAX ; Old buffer size.
      CMP EAX,ECX
      JAE .30: ; If doubled size is sufficient.
      MOV EAX,ECX
 .30: ADD EDX,EAX ; Add the old buffer's size.
      PoolNew [EBX+BUFFER.Pool],EDX, Align=DWORD
      JC .90:
      MOV EDI,EAX
      MOV ESI,[EBX+BUFFER.Bottom]
      MOV [EBX+BUFFER.Bottom],EAX
      ADD EAX,EDX
      MOV [EBX+BUFFER.Top],EAX
      MOV ECX,[EBX+BUFFER.Ptr]
      SUB ECX,ESI
      REP MOVSB ; Copy the old buffer's contents.
      MOV [EBX+BUFFER.Ptr],EDI
      JMP .10: ; Try again. This time it will succeed.
 .70: CLC
 .80: MOV [EBX+BUFFER.Ptr],EAX
 .90:POPAD
     RET 8
   ENDPROC1 BufferNew@RT::
 %ENDMACRO BufferNew
↑ BufferStore aBuffer, DataPtr, DataSize
will allocate DataSize bytes and copy the data specified with DataPtr,DataSize.
Input
aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
DataPtr is a pointer to data to be stored on the buffer.
DataSize is the requested data size in bytes.
Output
CF=0, data are stored to buffer.
Error
CF=1
BufferStore %MACRO aBuffer, DataPtr, DataSize
     PUSHD %DataSize, %DataPtr, %aBuffer
     CALL BufferStore@RT::
BufferStore@RT:: PROC1
     PUSHAD
      MOV EBP,ESP
      MOV EDX,[%Param1] ; aBuffer.
      MOV ECX,[%Param3] ; DataSize.
      TEST EDX
      STC
      JZ .90:
      BufferNew EDX,ECX
      JC .90:
      MOV EDI,EAX
      MOV ESI,[%Param2] ; DataPtr.
      REP MOVSB
 .90:POPAD
     RET 3*4
    ENDPROC1 BufferStore@RT::
 %ENDMACRO BufferStore
↑ BufferStoreByte aBuffer, Value
will allocate 1 byte and copy the BYTE value to the buffer.
Input
aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
Value is an immediate 8bit value or a 32bit register/memory with the value in LSB.
Output
CF=0, Byte Value was stored to aBuffer.
Error
CF=1
BufferStoreByte %MACRO aBuffer, Value
     PUSHD %Value, %aBuffer
     CALL BufferStoreByte@RT::
BufferStoreByte@RT:: PROC1
     PUSHAD
      MOV EBP,ESP
      MOV EDX,[%Param1] ; aBuffer.
      MOV EBX,[%Param2] ; Value.
      TEST EDX
      STC
      JZ .90:
      BufferNew EDX,1
      JC .90:
      MOV [EAX],BL ; Store the BYTE value.
 .90:POPAD
     RET 8
     ENDPROC1 BufferStoreByte@RT::
 %ENDMACRO BufferStoreByte
↑ BufferStoreWord aBuffer, Value
will allocate 2 bytes and copy the WORD value to the buffer.
Input
aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
Value is an immediate 16bit value or a 32bit register/memory with the value in the lower word.
Output
CF=0, word value was stored to buffer.
Error
CF=1
BufferStoreWord %MACRO aBuffer, Value
     PUSHD %Value, %aBuffer
     CALL BufferStoreWord@RT
BufferStoreWord@RT:: PROC1
     PUSHAD
      MOV EBP,ESP
      MOV EDX,[%Param1] ; aBuffer.
      MOV EBX,[%Param2] ; Value.
      TEST EDX
      STC
      JZ .90:
      BufferNew EDX,2
      JC .90:
      MOV [EAX],BX ; Store the WORD value.
 .90:POPAD
     RET 8
    ENDPROC1 BufferStoreWord@RT::
 %ENDMACRO BufferStoreWord
↑ BufferStoreDword aBuffer, Value
will allocate 4 bytes and copy the DWORD value to the buffer.
Input
aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
Value is a 32bit register/memory or immediate value to be stored on the buffer.
Output
CF=0, Dword value was stored to buffer.
Error
CF=1
BufferStoreDword %MACRO aBuffer, Value
     PUSHD %Value, %aBuffer
     CALL BufferStoreDword@RT::
BufferStoreDword@RT:: PROC1
     PUSHAD
      MOV EBP,ESP
      MOV EDX,[%Param1] ; aBuffer.
      MOV EBX,[%Param2] ; Value.
      TEST EDX
      STC
      JZ .90:
      BufferNew EDX,4
      JC .90:
      MOV [EAX],EBX ; Store the DWORD value.
 .90:POPAD
     RET 8
    ENDPROC1 BufferStoreDword@RT::
 %ENDMACRO BufferStoreDword
↑ BufferStorePascalString aBuffer, StringPtr, Size=-1
Macro BufferStorePascalString will store the string to aBuffer, prefixed with one usigned byte integer which contains string size (0..255).
Size of the string is either explicitly specified or zero-termination is assumed.
If the string netto size exceeds 254 bytes, only 255 is stored and the macro returns CF=1.
Input
aBuffer is a pointer to BUFFER structure as returned from BufferCreate.
StringPtr is a pointer to the first byte of the string.
Size= -1 specifies the maximal string size. If the string is terminated with 0 byte, only its real size is used. The terminating zero is not stored as a part of the string.
Output
CF=0, data are stored to buffer.
Error
CF=1 when string size exceeded 255 bytes or wrong aBuffer.
BufferStorePascalString %MACRO aBuffer, StringPtr, Size=-1
     PUSHD %Size, %StringPtr, %aBuffer
     CALL BufferStorePascalString@RT::
BufferStorePascalString@RT:: PROC1
     PUSHAD
      MOV EBP,ESP
      MOV EDX,[%Param1] ; aBuffer.
      MOV EDI,[%Param2] ; StringPtr.
      MOV ECX,[%Param3] ; Size.
      TEST EDX
      MOV ESI,EDI
      STC
      JZ .90: ; Bad buffer.
      JECXZ .20:
      SUB EAX,EAX
      REPNE SCASB ; Find zero terminator within specified %Size.
      JNE .20:
      DEC EDI ; Skip terminating zero.
 .20: MOV EBX,255
      SUB EDI,ESI
      CMP EDI,EBX
      MOV ECX,EDI
      JBE .40:
      MOV ECX,EBX ; Saturate netto size ECX to max. 0..255 characters.
 .40: LEA EDI,[ECX+1]
      BufferNew EDX,EDI
      JC .90:
      MOV EDI,EAX ; Allocated room on buffer.
      MOV EAX,ECX
      DEC EBX ; 254.
      STOSB ; Store netto string size.
      CMP EBX,ECX ; Set CF when ECX was saturated to 255.
      REP MOVSB ; Store string.
 .90:POPAD
     RET 12
   ENDPROC1 BufferStorePascalString@RT::
 %ENDMACRO BufferStorePascalString
↑ BufferDecrement aBuffer, Size=1
omits the %Size byte(s) last stored on aBuffer.
Input
aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
Size=1 is number of bytes to omit (immediate number or GPR other than EAX,ECX).
Output
CF=0 buffer contents was decremented.
Error
CF=1 (bad buffer)
BufferDecrement %MACRO aBuffer, Size=1
     PUSH EAX,ECX
      MOV ECX,%aBuffer
      STC
      JECXZ BufferDecrementC%.:
      MOV EAX,[ECX+BUFFER.Ptr]
      SUB EAX,%Size
      CMP EAX,[ECX+BUFFER.Ptr]
      JBE BufferDecrementA%.:
      MOV EAX,[ECX+BUFFER.Ptr] ; If %Size was negative - ignore.
   BufferDecrementA%.:
      CMP EAX,[ECX+BUFFER.Bottom]
      JAE BufferDecrementB%.:
      MOV EAX,[ECX+BUFFER.Bottom] ; If %Size was greater than buffer contents.
   BufferDecrementB%.:
      MOV [ECX+BUFFER.Ptr],EAX
      CLC
   BufferDecrementC%.:
     POP ECX,EAX
  %ENDMACRO BufferDecrement
↑ BufferResize aBuffer, Bytes
moves the buffer pointer by %Bytes value forward or backward. This will virtually change the size of data in aBuffer. Resizing does not change the data stored in %aBuffer.

Macro does nothing when %Bytes=0. If %Bytes is negative, the data block returned with subsequent BufferRetrieve will be shorter. If %Bytes is positive, returned data block will be larger. The enlarged portion of data contents is undefined, it may contain old information previously stored to %aBuffer and abandoned by BufferDecrement or by previous invokation of BufferResize with negative %Bytes.

If the negative %Bytes number is larger than current data contents available in %aBuffer, buffer pointer is reset to bottom, which is equivalent to BufferClear.

If the buffer data enlarged with positive %Bytes is greater than the size allocated at BufferCreate, %aBuffer will be reallocated to this enlarged new size doubled, and old data contents will be copied from previous position to the new reallocated memory.

Input
aBuffer is a pointer to BUFFER structure which was returned from BufferCreate.
Bytes is number of bytes to virtually resize the buffer contents.
Output
CF=0 Buffer pointer moved, allocation enlarged if necessary.
Error
CF=1 (bad buffer)
BufferResize %MACRO aBuffer, Bytes
     PUSHD %Bytes, %aBuffer
     CALL BufferResize@RT::
BufferResize@RT:: PROC1
     PUSHAD
      MOV EBX,[ESP+36] ; ^aBuffer.
      MOV EDX,[ESP+40] ; Bytes.
      TEST EBX
      STC
      JZ .90:          ; No valid buffer.
      TEST EDX
      JZ .90:          ; Resize by zero does nothing.
      MOV EAX,[EBX+BUFFER.Ptr]
      MOV ESI,[EBX+BUFFER.Bottom]
      ADD EAX,EDX      ; Resize .Ptr.
      CMP EAX,ESI
      JB .70:          ; Underflowed, saturate .Ptr to .Bottom.
.20:  CMP EAX,[EBX+BUFFER.Top]
      JNA .80:         ; If not overflowed.
      ; Buffer EBX is not large enough.
      SUB EAX,ESI
      MOV EDX,EAX      ; EDX is now the new requested size.
      LEA ECX,[EAX+EAX]; It will be doubled to ECX bytes.
      PoolNew [EBX+BUFFER.Pool],ECX, Align=DWORD
      JC .90:
      MOV EDI,EAX
      MOV [EBX+BUFFER.Bottom],EAX
      ADD EDX,EAX      ; Resized .Ptr in the new buffer.
      ADD EAX,ECX
      MOV ECX,[EBX+BUFFER.Top]
      MOV [EBX+BUFFER.Top],EAX
      SUB ECX,ESI      ; Old buffer allocated size.
      REP MOVSB
      MOV ESI,EDX
.70:  MOV EAX,ESI
.80:  MOV [EBX+BUFFER.Ptr],EAX
      CLC
.90: POPAD
     RET
    ENDP1 BufferResize@RT::
 %ENDMACRO BufferResize
↑ BufferRetrieve aBuffer
will return pointer and size of all data currently stored in the buffer.
Input
aBuffer is a pointer to BUFFER structure which was returned from BufferCreate. Macro returns ECX=0 when aBuffer is NULL.
Output
CF=0
ESI=Ptr to the stored data (BUFFER.Bottom)
ECX=Size of stored data (BUFFER.Ptr - BUFFER.Bottom)
Error
CF=1,ECX=0 (uncreated buffer)
BufferRetrieve %MACRO aBuffer
      %IF "%aBuffer" !== "ECX"
        MOV ECX,%aBuffer
      %ENDIF
      STC
      JECXZ BufferRetrieve%.:
      MOV ESI,[ECX+BUFFER.Bottom]
      MOV ECX,[ECX+BUFFER.Ptr]
      SUB ECX,ESI
    BufferRetrieve%.:
  %ENDMACRO BufferRetrieve
↑ LIST

Object LIST is FIFO/LIFO bidirectional storage for items (leaves) of the same size. Leaves are DWORD aligned. Below the payload data of every leaf are pointers to the next and previous leaf data.

Leaves are appended to the list with ListNew or ListStore methods and the list can be searched sequentially with ListGetNext or ListGetPrev.

The LIST structure is created in pool memory with ListCreate , it can be destroyed only when the hosting pool is destroyed.

Example
; Walking the previously created MyList backwards: ListGetLast [MyList] JZ .NoMore: .N:CALL ProcessLeafAtEAX ListGetPrev EAX JNZ .N: .NoMore:
1st 2nd 3rd ┌ ┌────┐ ┌────┐ ┌────┐ │ │░░░░│ │░░░░│ │░░░░│ ┌──────┐ │ │░░░░│ │░░░░│ │░░░░│ │.Count│ │ │░░░░│ │░░░░│ │░░░░│ ├──────┤ │ │░░░░│ │░░░░│ │░░░░│ │.Size │─┤ │░░░░│ │░░░░│ │░░░░│ ├──────┤ │ │░░░░│ │░░░░│ │░░░░│ │.Pool │ │ │░░░░│ │░░░░│ │░░░░│ ├──────┤ └ ┌> 0├────┤<┐┌>├────┤<┐┌>├────┤<┐ │.Last │─┐ │ │ 0 │ └┼─│Prev│ └┼─│Prev│ │ ├──────┤ │ │ -4├────┤ │ ├────┤ │ ├────┤ │ │.First│─┼─┘ │Next│──┘ │Next│──┘ │ 0 │ │ └──────┘ │ -8└────┘ └────┘ └────┘ │ └────────────────────────────────┘
LIST STRUC
 .First D D ; Pointer to the first stored leaf.
 .Last  D D ; Pointer to the last stored leaf.
 .Pool  D D ; Pool handle obtained from PoolCreate.
 .Size  D D ; Data item netto size.
 .Count D D ; Number of stored items.
 ENDSTRUC LIST
↑ ListCreate aPool, Size
will allocate an empty LIST structure on the pool specified with the first parameter.
Input
aPool is a pointer to a POOL structure which was returned from PoolCreate.
Size= is the size of data items which will be stored on the list later.
Output
CF=0, EAX=pointer to the LIST structure (list handle).
Error
CF=1, EAX undefined (Pool error)
Depends on
PoolNew
ListCreate %MACRO aPool, Size
        PUSHD %Size, %aPool
        CALL ListCreate@RT::
ListCreate@RT:: PROC1
        PUSHAD
         MOV EBP,ESP
         MOV EDX,[%Param1] ; %aPool.
         MOV ECX,[%Param2] ; %Size.
         PoolNew EDX, SIZE#LIST, Align=DWORD
         MOV [%ReturnEAX],EAX
         JC .90:
         MOV [EAX+LIST.Pool],EDX
         MOV [EAX+LIST.Size],ECX
  .90:  POPAD
        RET 8
  ENDPROC1 ListCreate@RT::
 %ENDMACRO ListCreate
↑ ListNew aList, Zeroed=No
will allocate an empty space for one leaf (plus two pointers) and append it on the list.
Input
aList is a pointer to LIST structure which was returned from ListCreate.
Zeroed=No Zeroed=Yes will clear the just allocated memory.
Output
CF=0, EAX=pointer to the allocated space (LIST.Size bytes).
Error
CF=1, EAX=0 (Pool error)
ListNew %MACRO aList, Zeroed=No
        %IF "%Zeroed[1]"=="N"
          PUSHD 0
        %ELSE
          PUSHD -1
        %ENDIF
        PUSHD %aList
        CALL ListNew@RT::
ListNew@RT:: PROC1
        PUSHAD
         MOV EBP,ESP
         MOV EBX,[%Param1] ; %aList.
         MOV [%ReturnEAX],EBX
         TEST EBX
         STC
         JZ .90: ; Bad object.
         MOV ECX,[EBX+LIST.Size]
         ADD ECX,8 ; Additional room for two pointers.
         PoolNew [EBX+LIST.Pool],ECX, Align=DWORD
         MOV [%ReturnEAX],EAX
         JC .90:
         MOV EDI,EAX
         MOV ESI,[EBX+LIST.Last]
         SUB EAX,EAX
         STOSD ; Pointer Next.
         MOV EAX,ESI ; LIST.Last may be 0 if this is the first leaf.
         STOSD ; Pointer Prev.
         TEST EAX
         JZ .30: ; If this leaf EDI is the first.
         MOV [ESI-8],EDI ; EDI is pointer to this new leaf.
    .30: MOV [EBX+LIST.Last],EDI
         CMPD [EBX+LIST.First],0
         JNE .80:
         MOV [EBX+LIST.First],EDI
    .80: MOV [%ReturnEAX],EDI
         INCD [EBX+LIST.Count]
         XOR EAX,EAX
         CMP EAX,[%Param2] ; %Zeroed.
         JE .90: ; If Zeroed=No.
         MOV ECX,[EBX+LIST.Size]
         REP STOSB ; Clear the leaf payload area.
         CLC
    .90:POPAD
        RET 8
  ENDPROC1 ListNew@RT::
 %ENDMACRO ListNew
↑ ListStore aList, DataPtr
will allocate an empty space for one leaf, append it on the list and copy the Data to this space.
Input
aList is a pointer to LIST structure which was returned from ListCreate.
DataPtr is pointer to the data to be stored on list. Size of the data was specified in ListCreate.
Output
CF=0, EAX=pointer to the stored data on the list.
Error
CF=1, EAX=0
Depends on
ListNew
ListStore %MACRO aList, DataPtr
        PUSHD %DataPtr, %aList
        CALL ListStore@RT::
ListStore@RT:: PROC1
       PUSHAD
        MOV EBP,ESP
        MOV EBX,[%Param1] ; %aList.
        MOV ESI,[%Param2] ; %DataPtr.
        SUB EAX,EAX
        TEST EBX
        STC
        JZ .90:
        ListNew EBX, Zeroed=No
        JC .90:
        MOV EDI,EAX
        MOV ECX,[EBX+LIST.Size]
        REP MOVSB
   .90: MOV [%ReturnEAX],EAX
       POPAD
       RET 8
  ENDPROC1 ListStore@RT::
 %ENDMACRO ListStore
↑ ListInsert aList, PrevLeaf, DataPtr
will insert a new leaf right behind PrevLeaf on the List, update links and copy the Data to this space.
Input
aList is a pointer to LIST structure which was returned from ListCreate.
PrevLeaf is pointer to an existing leaf. When it is 0, data will be stored to the 1st position
on the list then (same as ListStore).
DataPtr is pointer to the data to be stored on list. It may be 0, an empty list is inserted in this case. Size of the data was specified in ListCreate.
Output
CF=0, EAX=pointer to the stored data (LIST.Size bytes).
Error
CF=1, EAX=0
ListInsert %MACRO aList, PrevLeaf, DataPtr
       PUSHD %DataPtr, %PrevLeaf, %aList
       CALL ListInsert@RT::
ListInsert@RT:: PROC1
      PUSHAD
       MOV EBP,ESP
       MOV EBX,[%Param1] ; %aList.
       MOV ESI,[%Param2] ; %PrevLeaf.
       XOR EAX,EAX
       MOV [%ReturnEAX],EAX
       TEST EBX
       STC
       JZ .90:
       MOV ECX,[EBX+LIST.Size]
       ADD ECX,8 ; Additional room for two pointers.
       PoolNew [EBX+LIST.Pool],ECX,Align=DWORD
       JC .90:
       INCD [EBX+LIST.Count]
       MOV EDI,EAX
       TEST ESI
       JNZ .30:
       MOV EAX,[EBX+LIST.First]
       MOV EDX,EAX
       STOSD ; Next.
       MOV EAX,ESI
       STOSD ; Prev.
       MOV [EBX+LIST.First],EDI
       JMP .40:
.30:   MOV EAX,[ESI-8] ; Next.
       MOV EDX,EAX
       STOSD ; Next.
       MOV EAX,ESI
       STOSD ; Prev.
       MOV [ESI-8],EDI
 .40:  MOV [%ReturnEAX],EDI
       TEST EDX
       JZ .50:
       MOV [EDX-4],EDI ; Prev.
 .50:  JNZ .60:
       MOV [EBX+LIST.Last],EDI
 .60:  SUB ECX,8
       MOV ESI,[%Param3] ; %DataPtr.
       TEST ESI
       JNZ .80:
       XOR EAX,EAX
       REP STOSB
       JMP .90:
 .80:  REP MOVSB
 .90: POPAD
      RET 12
 ENDPROC1 ListInsert@RT::
%ENDMACRO ListInsert
↑ ListRemove aList, LeafPtr
removes one leaf from the list. The old data stays on pool but links are updated and the removed leaf is skipped.
Input
aList is a pointer to LIST structure which was returned from ListCreate.
LeafPtr is pointer to the leaf data which is to be removed.
Output
ZF=1 if the list is now empty
Error
-
ListRemove %MACRO aList, LeafPtr
        PUSHD %LeafPtr, %aList
        CALL ListRemove@RT::
ListRemove@RT:: PROC1
       PUSHAD
        MOV EBP,ESP
        MOV EBX,[%Param1] ; %aList.
        MOV EDX,[%Param2] ; %LeafPtr.
        TEST EBX
        JZ .90:
        TEST EDX
        JZ .90:
        MOV ESI,[EDX-4]   ; Prev.
        MOV EDI,[EDX-8]   ; Next.
        TEST ESI
        JZ .30:
        MOV [ESI-8],EDI ; Next.
        TEST EDI
        JZ .50:
  .20:  MOV [EDI-4],ESI ; Prev.
        JMP .80:
  .30:  MOV [EBX+LIST.First],EDI ; The first leaf was removed.
        TEST EDI
        JNZ .20:
  .50:  MOV [EBX+LIST.Last],ESI ; The last leaf was removed.
  .80:  DECD [EBX+LIST.Count]
 .90:  POPAD
       RET 8
  ENDPROC1 ListRemove@RT::
 %ENDMACRO ListRemove
↑ ListGetFirst aList
will return pointer to the data which was stored as the first leaf.
Input
aList is a pointer to LIST structure which was returned from ListCreate.
Output
ZF=0, EAX=pointer to the first leaf of stored data.
Error
ZF=1, EAX=0 if the list is empty.
ListGetFirst %MACRO aList
       PUSH ECX
        MOV ECX,%aList
        SUB EAX,EAX
        JECXZ ListGetFirst%.:
        MOV EAX,[ECX+LIST.First]
        ListGetFirst%.:
        TEST EAX
       POP ECX
  %ENDMACRO ListGetFirst
↑ ListGetNext LeafPtr
will return pointer to the leaf which was stored right after the leaf specified with the first parameter.
Input
LeafPtr is a pointer to data which was returned from ListGetFirst/Next/Last/Prev.
Output
ZF=0, EAX=pointer to the following leaf of stored data.
ZF=1 EAX=0 if there are no more leaves on list (LeafPtr was the last).
ListGetNext %MACRO LeafPtr
       PUSH ECX
        MOV ECX,%LeafPtr
        SUB EAX,EAX
        JECXZ ListGetNext%.:
        MOV EAX,[ECX-8]
        ListGetNext%.:
        TEST EAX
       POP ECX
  %ENDMACRO ListGetNext
↑ ListGetLast aList
will return pointer to the data which was stored as the last leaf.
Input
aList is a pointer to LIST structure which was returned from ListCreate.
Output
ZF=0, EAX=pointer to the last leaf of stored data.
Error
ZF=1, EAX=0 if the list is empty.
ListGetLast %MACRO aList
       PUSH ECX
        MOV ECX,%aList
        SUB EAX,EAX
        JECXZ ListGetLast%.:
        MOV EAX,[ECX+LIST.Last]
        ListGetLast%.:
        TEST EAX
       POP ECX
   %ENDMACRO ListGetLast
↑ ListGetPrev LeafPtr
will return pointer to the leaf which was stored right before the leaf specified with the 1st parameter.
Input
LeafPtr is a pointer to data which was returned from ListGetFirst/Next/Last/Prev.
Output
ZF=0, EAX=poiubter to the previous leaf of stored data.
ZF=1, EAX=0 if there are no more leaves on list (LeafPtr was the first).
ListGetPrev %MACRO LeafPtr
       PUSH ECX
        MOV ECX,%LeafPtr
        SUB EAX,EAX
        JECXZ ListGetPrev%.:
        MOV EAX,[ECX-4]
        ListGetPrev%.:
        TEST EAX
       POP ECX
  %ENDMACRO ListGetPrev
↑ STACK

Object STACK is LIFO storage for items of the same size. Stacked items are DWORD aligned and stored continuously in one pool block. Stack grows upward, STACK.Ptr is increased with StackPush and decreased with StackPop.

Whenever the initial stack depth specified in StackCreate is not big enough, which may happen in StackPush, the stack is reallocated with doubled size, old content is copied to the new location and abandoned. The STACK structure itself stays in its original position.

Stack contents can be erased with StackClear.

The STACK object should not be confused with machine stack adressed with SS:ESP.
Example
INCLUDE1 "syswin.htm", "memory.htm" ; PoolCreate requires the macro SysAlloc from "sys*.htm". MyObject STRUC ; Define object which will be using the stack. .Member1 DD DWORD .Member2 DD DWORD ENDSTRUC MyObject MyPool DD 0 MyStack DD 0 [.text] PoolCreate Size=16K ; Create a pool (or reuse previously created one). MOV [MyPool],EAX JC .Error: StackCreate EAX, SIZE# MyObject, Depth=8 MOV [MyStack],EAX JC .Error: ; MyStack can be used now (StackPush, StackPeekLast, StackPop etc). PoolDestroy [MyPool] ; Return pool memory to OS at the end of program.
Depth=5 ┌───────┐<──┐ │ │ │ │ │ │ ├───────┤ │ │ │ │ │ │ │ ├───────┤<─┐│ │░░░░░░░│ ││ │░░░░░░░│ ││ ├───────┤ ││ │░░░░░░░│ ││ │░░░░░░░│ ││ ┌├───────┤ ││ ││░░░░░░░│ ││ ┌┤│░░░░░░░│ ││ │└├───────┤<┐││ │ │.Bottom│─┘││ │ ├───────┤ ││ │ │.Ptr │──┘│ │ ├───────┤ │ │ │.Top │───┘ │ ├───────┤ └─│.Size │ ├───────┤ │.Pool │ └───────┘
STACK STRUC
 .Pool   D D ; Pool handle obtained from PoolCreate.
 .Size   D D ; Size of one stacked item.
 .Top    D D ; Pointer to the end of allocated space.
 .Ptr    D D ; Pointer to the free space on STACK.
 .Bottom D D ; Pointer to the oldest pushed item.
 ENDSTRUC STACK
↑ StackCreate aPool, Size, Depth=16
will allocate an empty STACK on the pool specified with the first parameter.
Input
aPool is a pointer to a POOL structure which was returned from PoolCreate.
Size is the size of each data object which will be pushed on the stack later.
Depth=16 is the estimated maximal number of pushed objects.
Underestimated depth will be increased automatically if necessary but this will waste the memory a little.
Output
CF=0 EAX=pointer to the STACK (Stack handle).
Error
CF=1, EAX=0 if pool allocation failed.
StackCreate %MACRO aPool, Size, Depth=16
      PUSHD %Depth, %Size, %aPool
      CALL StackCreate@RT::
StackCreate@RT:: PROC1
     PUSHAD
      MOV EBP,ESP
      MOV EAX,[%Param2] ; %Size.
      MOV ESI,[%Param1] ; %aPool.
      MOV ECX,EAX
      MULD [%Param3]    ; %Depth.
      MOV EBX,EAX
      ADD EAX,SIZE#STACK
      PoolNew ESI,EAX,Align=DWORD
      MOV [%ReturnEAX],EAX
      JC .90:
      MOV [EAX+STACK.Pool],ESI
      MOV [EAX+STACK.Size],ECX
      LEA EDI,[EAX+SIZE#STACK]
      MOV [EAX+STACK.Bottom],EDI
      MOV [EAX+STACK.Ptr],EDI
      ADD EDI,EBX
      MOV [EAX+STACK.Top],EDI
 .90:POPAD
     RET 12
  ENDPROC1 StackCreate@RT::
 %ENDMACRO StackCreate
↑ StackClear aStack
will empty the stack. Old data are not erased and the stack memory remains allocated.
Input
aStack is a pointer to STACK structure which was returned from StackCreate.
Output
-
Error
-
StackClear %MACRO aStack
      PUSHD EAX,EBX,%aStack
        POP EBX
        MOV EAX,[EBX+STACK.Bottom]
        MOV [EBX+STACK.Ptr],EAX
      POP EBX,EAX
   %ENDMACRO StackClear
↑ StackPush aStack, DataPtr
will copy the data on stack and increase STACK.Ptr.
Input
aStack is a pointer to STACK structure which was returned from StackCreate.
DataPtr is pointer to the data to be pushed. DataPtr may be 0; zeroes will be pushed on the Stack in this case. Data size was specified in StackCreate.
Output
CF=0, EAX=pointer to the just pushed data item on stack, i.e. STACK.Ptr before this macro was invoked.
Error
CF=1 (Pool error)
StackPush %MACRO aStack, DataPtr
     PUSHD %DataPtr, %aStack
     CALL StackPush@RT::
StackPush@RT:: PROC1
     PUSHAD
      MOV EBP,ESP
      MOV EBX,[%Param1] ; %aStack
      TEST EBX
      STC
      JZ .90:
 .10: MOV EDI,[EBX+STACK.Ptr]
      CMP EDI,[EBX+STACK.Top]
      JB .50:
     ; Not enough space on stack.
      MOV EAX,[EBX+STACK.Top]
      MOV ESI,[EBX+STACK.Bottom]
      SUB EAX,ESI
      MOV ECX,EAX
      LEA EDX,[EAX+EAX] ; Double the stack room.
      PoolNew [EBX+STACK.Pool],EDX, Align=DWORD
      JC .90:
      MOV EDI,EAX ; New room with doubled size (STACK.Bottom).
      MOV [EBX+STACK.Bottom],EAX
      ADD EDX,EAX ; New STACK.Top.
      REP MOVSB ; Copy old stack content.
      MOV [EBX+STACK.Ptr],EDI
      MOV [EBX+STACK.Top],EDX
 .50: ; There is a free space on stack now.
      MOV ESI,[%Param2] ; %DataPtr.
      MOV ECX,[EBX+STACK.Size]
      MOV [%ReturnEAX],EDI ; STACK.Ptr before StackPush.
      TEST ESI
      JZ .70:   ; Go to store zeroes when NULL data pointer was provided.
      REP MOVSB ; Otherwise copy the data.
      JMP .80:
 .70: SUB EAX,EAX
      REP STOSB
 .80: MOV [EBX+STACK.Ptr],EDI ; Update the new incremented stack pointer.
      CLC
 .90:POPAD
     RET 8
  ENDPROC1 StackPush@RT::
 %ENDMACRO StackPush
↑ StackPop aStack
will decrease STACK.Ptr and return pointer to the popped data.
Input
aStack is a pointer to STACK structure which was returned from StackCreate.
Output
CF=0, EAX=pointer to the just popped data item on stack, i.e. STACK.Ptr after this macro was invoked.
Although the data item is technically popped from the stack, it is still available at the pointer returned in EAX until another StackPush will overwrite it.
Error
CF=1, EAX=undefined when he stack was empty.
StackPop %MACRO aStack
     PUSHD ECX,%aStack
      POP ECX
      STC
      JECXZ StackPop%.:  ; Bad parameter.
      MOV EAX,[ECX+STACK.Ptr]
      SUB EAX,[ECX+STACK.Size]
      CMP EAX,[ECX+STACK.Bottom]
      JC StackPop%.: ; If the stack is empty.
      MOV [ECX+STACK.Ptr],EAX
 StackPop%.:
     POP ECX
 %ENDMACRO StackPop
↑ StackPeekLast aStack
will return pointer to the latest pushed data but, unlike StackPop, it does not remove the data from stack. STACK.Ptr is unchanged.
Input
aStack is a pointer to the STACK structure which was returned from StackCreate.
Output
CF=0, EAX=pointer to the last data object on stack.
Error
CF=1, EAX=undefined. The stack was empty, nothing to peek at.
StackPeekLast %MACRO aStack
      PUSH ECX
       SUB EAX,EAX
       MOV ECX,%aStack
       STC
       JECXZ StackPeekLast%.:
       MOV EAX,[ECX+STACK.Ptr]
       SUB EAX,[ECX+STACK.Size]
       CMP EAX,[ECX+STACK.Bottom]
      StackPeekLast%.:
      POP ECX
 %ENDMACRO StackPeekLast
↑ StackPeekPrev aStack, DataPtr
will return pointer to the data item predecessing the object specified with DataPtr. STACK.Ptr is unchanged.
Input
aStack is a pointer to STACK structure which was returned from StackCreate.
DataPtr is pointer to data item returned from invocation of StackPeekLast or StackPeekPrev.
Output
CF=0, EAX=pointer to the previous data item on stack.
Error
CF=1, EAX=undefined at the bottom of stack, nothing to peek at.
StackPeekPrev %MACRO aStack, DataPtr
    PUSH ECX
     PUSHD %aStack
     POP ECX
     STC
     JECXZ StackPeekPrev%.:
     %IF "%DataPtr" == "ECX"
       MOV EAX,[ESP]
     %ELSE
       MOV EAX,%DataPtr
     %ENDIF
     SUB EAX,[ECX+STACK.Size]
     CMP EAX,[ECX+STACK.Bottom]
    StackPeekPrev%.:
   POP ECX
 %ENDMACRO StackPeekPrev
↑ STREAM

Object STREAM is an unformated FIFO storage for items of unlimited size. Unlike the BUFFER, data is not guaranteed to be stored continuously. Memory blocks (containers for stored data) are allocated on demand with fixed size and their every byte is used, thus the memory is exploited very efficiently.
The STREAM structure itself is appended below the first block, all other continuation block have pointer to the next block appended below them.

Stream-store operations advance STREAM.WritePtr, stream-read operations advance STREAM.ReadPtr. Stream should not be read by more threads concurently.

Typical usage of stream is collecting data with StreamStore* and then retrieving all data at once.

┌ ┌──────────┐ ┌──────────┐ ┌──────────┐<───┐ │ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ │ │ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ │ │ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ │ │ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │<─┐ │ │ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ┌┤ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ││ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ││ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ││ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ││ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ ││ │░░░░░░░░░░│ │░░░░░░░░░░│ │░░░░░░░░░░│ │ │ │└ ┌>├──────────┤ ┌─>├──────────┤ ┌─>├──────────┤ │ │ │ │ │.Next │─┘ │.Next │─┘ │.Next=0 │ │ │ │ │ ├──────────┤ └──────────┘ └──────────┘ │ │ │ │ │.WritePtr │──────────────────────────────────┘ │ │ │ ├──────────┤ │ │ └─│.ReadPtr │ │ │ ├──────────┤ │ │ │.Top │────────────────────────────────────┘ │ ├──────────┤ └────│.BufSize │ ├──────────┤ │.Pool │ └──────────┘
 STREAM STRUC
.Pool     D D ; Pool handle obtained from PoolCreate.
.BufSize  D D ; Netto size of each block (without the linking pointer).
.Top      D D ; End of the last block.
.ReadPtr  D D ; Pointer to the unread data.
.WritePtr D D ; Pointer to the free position in the last block.
.Next     D D ; Pointer to the bottom of the next block. NULL if only 1 block is allocated.
ENDSTRUC STREAM
 
↑ StreamCreate Pool, BufSize=16K
will allocate stream object on the pool specified with the first parameter.
Input
Pool is a pointer to POOL structure which was returned from PoolCreate.
BufSize= is the requested netto size of each buffer.
Output
CF=0 EAX=pointer to the stream instance.
Error
CF=1 EAX=undefined on pool error.
Depends on
PoolNew
StreamCreate %MACRO Pool, BufSize=16K
      PUSHD %BufSize, %Pool
      CALL StreamCreate@RT::
StreamCreate@RT:: PROC1
      PUSHAD
       MOV EBP,ESP
       MOV EBX,[%Param1] ; %Pool.
       MOV ECX,[%Param2] ; %BufSize.
       LEA EDX,[ECX+SIZE#STREAM]
       PoolNew EBX, EDX, Align=DWORD
       MOV [%ReturnEAX],EAX
       JC .90:
       MOV [EAX+STREAM.Pool],EBX
       LEA EBX,[EAX+SIZE#STREAM]
       MOV [EAX+STREAM.BufSize],ECX
       MOV [EAX+STREAM.ReadPtr],EBX
       MOV [EAX+STREAM.WritePtr],EBX
       MOVD [EBX-4],0
       ADD EBX,ECX
       MOV [EAX+STREAM.Top],EBX
 .90: POPAD
      RET 8
  ENDPROC1 StreamCreate@RT::
 %ENDMACRO StreamCreate
↑ StreamGetSize Stream
returns the total amount of data written to the stream in bytes.
Input
Stream ^STREAM obtained in StreamCreate.
Output
EAX= total size of data in the stream.
StreamGetSize %MACRO Stream
      PUSHD %Stream
      CALL StreamGetSize@RT::
StreamGetSize@RT:: PROC1
      PUSHAD
       MOV EBP,ESP
       SUB EAX,EAX
       MOV EBX,[%Param1] ; %Stream.
       MOV [%ReturnEAX],EAX ; Returned size.
       LEA ESI,[EBX+SIZE#STREAM]
       MOV EAX,[EBX+STREAM.WritePtr]
       MOV ECX,[EBX+STREAM.BufSize]
 .10:  CMP EAX,ESI
       JB .20:
       LEA EDI,[ESI+ECX]
       CMP EAX,EDI
       JNA .30:
 .20:  ADD [%ReturnEAX],ECX
       MOV ESI,[ESI-4]
       JMP .10:
 .30:  SUB EAX,ESI
       ADD [%ReturnEAX],EAX
 .90: POPAD
      RET 4
  ENDPROC1 StreamGetSize@RT::
 %ENDMACRO StreamGetSize
↑ StreamReadByte Stream
Macro StreamReadByte will retrieve one byte from the Stream at position STREAM.ReadPtr and advance the position. The stream should be reset to start with the first byte.
Input
Stream is pointer to STREAM object obtained in StreamCreate.
Output
CF=0, EAX= a byte read from the stream is in AL.
End of data
CF=1, EAX=undefined at the end of stream data.
Example
MOV EBX,aStream
See also
StreamReset
StreamReadByte %MACRO Stream
      PUSHD %Stream
      CALL StreamReadByte@RT::
StreamReadByte@RT:: PROC1
      PUSHAD
        MOV EBX,[ESP+36] ; %Stream.
        MOV EBP,[EBX+STREAM.BufSize]
        MOV EDX,[EBX+STREAM.Next] ; Pointer to the next block, or 0.
        LEA ESI,[EBX+SIZE#STREAM] ; Bottom of data in the first block.
        LEA ECX,[ESI+EBP]         ; Top of the first block.
.10:    ; If the .WritePtr is in the block ESI..ECX, decrement ECX to its value.
        CMP [EBX+STREAM.WritePtr],ESI
        JB .20:
        CMP [EBX+STREAM.WritePtr],ECX
        JAE .20:
        MOV ECX,[EBX+STREAM.WritePtr]
.20:    MOV EDI,[EBX+STREAM.ReadPtr]
        ; If the .ReadPtr is in the block ESI..ECX, read one byte from
        CMP EDI,ESI
        JB .50:  ; Try the next block.
        CMP EDI,ECX ; Check if STREAM.ReadPtr EDI belongs to this data block ESI..ECX.
        JA .50: ; Try the next block.
        JE .60: ; If .ReadPtr points to the top of block, move it to the bottom of the next block.
        XCHG ESI,EDI
        LODSB
        MOV [ESP+28],EAX ; ReturnEAX.
        MOV [EBX+STREAM.ReadPtr],ESI
        CLC
        JMP .90:
.50:    TEST EDX ; Is the next block allocated?
        STC
        JZ .90:
        MOV ESI,EDX ; Bottom of data in the next block.
        JMP .70:
.60:    TEST EDX ; Is the next block allocated?
        STC
        JZ .90:
        MOV ESI,EDX ; Bottom of data in the next block.
        MOV [EBX+STREAM.ReadPtr],ESI
.70:    MOV EDX,[EDX-4] ; Pointer to the next block, or 0.
        LEA ECX,[ESI+EBP]
        JMP .10:
.90:  POPAD
      RET 4
  ENDPROC1 StreamReadByte@RT::
 %ENDMACRO StreamReadByte
↑ StreamReadLn Stream, LineBuffer
Macro StreamReadLn will retrieve one physical line (terminated with LineFeed 0x10 from the Stream at position STREAM.ReadPtr and copy it to the LineBuffer. The stream should be reset to start with the first line.
The last line stored in Stream doesn't have to be necessarily terminated with LF.
Input
Stream is pointer to STREAM object obtained in StreamCreate.
LineBuffer is pointer to the output BUFFER where the line will be copied. It is allocated by the caller.
Output
CF=0, one physical line from the stream is copied to LineBuffer, Stream.ReadPtr is advanced.
End of data
CF=1, no more lines.
Example
MOV EBX,aStream BufferCreate aPool StreamReset EBX Loop: StreamReadLn EBX,EAX JC End: BufferRetrieve EAX CALL ProcessTheLine: JMP Loop: End:
See also
StreamReset
StreamReadLn %MACRO Stream, LineBuffer
      PUSHD %LineBuffer, %Stream
      CALL StreamReadLn@RT::
StreamReadLn@RT:: PROC1
      PUSHAD
        MOV EBX,[ESP+36] ; %Stream.
        MOV EBP,[EBX+STREAM.BufSize]
        MOV EDX,[EBX+STREAM.Next] ; Pointer to the next block, or 0.
        LEA ESI,[EBX+SIZE#STREAM] ; Bottom of data in the first block.
        LEA ECX,[ESI+EBP]         ; Top of the first block.
        MOV EAX,10  ; AL=LF, AH=0. Nonzero AH signalizes incomplete line (not LF terminated).
.10:    ; Find the physical line starting at .ReadPtr in the block ESI..ECX.
        TEST AH,AH
        JZ .30:
        MOV [EBX+STREAM.ReadPtr],ESI ; Bottom of the next block.
        XOR AH,AH
.30:    ; If the .WritePtr is in the block ESI..ECX, decrement ECX to its value.
        CMP [EBX+STREAM.WritePtr],ESI
        JB .40:
        CMP [EBX+STREAM.WritePtr],ECX
        JAE .40:
        MOV ECX,[EBX+STREAM.WritePtr]
.40:    ; If the .ReadPtr is in the block ESI..ECX, search for the LF.
        MOV EDI,[EBX+STREAM.ReadPtr]
        CMP EDI,ESI
        JB .50:  ; Try the next block.
        CMP EDI,ECX ; Check if STREAM.ReadPtr EDI belongs to this data block ESI..ECX.
        JA .50: ; Try the next block.
        JE .60: ; .ReadPtr points to the top of block. Move it to the bottom of the next block.
        SUB ECX,EDI ; Max size of searched data in block EDI..ECX. At least 1.
        MOV ESI,EDI ; Remember the start of line.
        REPNE SCASB ; Search for the LF.
        MOV [EBX+STREAM.ReadPtr],EDI
        JE .80: ; If terminator LF found, we're almost done.
        OR AH,1 ; The line ESI..EDI is incomplete, perhaps split between blocks.
        SUB EDI,ESI
        MOV ECX,[ESP+40] ; %LineBuffer.
        BufferStore ECX,ESI,EDI ; Store the first part of incomplete line.
        ; The line continues in the next block.
.50:    TEST AH
        JZ .55:
        TEST EDX ; Is the next block allocated?
        JZ .90: ; If not, the last line is incomplete, return it.
.55:    TEST EDX ; Is the next block allocated?
        STC
        JZ .90: ; If not and line not incomplete, signalize end of data.
        MOV ESI,EDX ; Bottom of data in the next block.
        JMP .70:
.60:    TEST EDX ; Is the next block allocated?
        STC
        JZ .90:
        MOV ESI,EDX ; Bottom of data in the next block.
        MOV [EBX+STREAM.ReadPtr],ESI
.70:    MOV EDX,[EDX-4] ; Pointer to the next block, or 0.
        LEA ECX,[ESI+EBP]
        JMP .10:
.80:    SUB EDI,ESI
        MOV EDX,[ESP+40] ; %LineBuffer.
        BufferStore EDX,ESI,EDI ; Copy line ESI,EDI to the buffer EDX.
        CLC
 .90: POPAD
      RET 8
  ENDPROC1 StreamReadLn@RT::
 %ENDMACRO StreamReadLn
↑ StreamGetLines StreamPtr
returns the number of physical lines in the data in the stream. The stream should be reset first.
Physical line ends with LineFeed character (0x10). The last line in the stream may have LF omitted.
Input
StreamPtr is pointer to the STREAM obtained in StreamCreate.
Output
EAX= total lines in the stream.
Depends on
StreamReadLn
StreamGetLines %MACRO StreamPtr
      SUB EAX,EAX
      DEC EAX
StreamGetLines%.:
      INC EAX
      StreamReadLn %StreamPtr, 0 ; The read lines won't be stored due to NULL buffer.
      JNC StreamGetLines%.:
 %ENDMACRO StreamGetLines
↑ StreamReset Stream
StreamReset moves the STREAM.ReadPtr to the beginning of data, so the stream contents can be retrieved once again.
Input
Stream is pointer to STREAM obtained in StreamCreate.
Output
none.
StreamReset %MACRO Stream
      PUSH EAX,EBX
        MOV EBX,%Stream
        LEA EAX,[EBX+SIZE#STREAM]
        MOV [EBX+STREAM.ReadPtr],EAX
      POP EBX,EAX
 %ENDMACRO StreamReset
↑ StreamClear Stream
StreamClear discards the contents of a stream. In fact it just moves the .WritePtr and .ReadPtr to the beginning of data. Allocated buffers are not discarded, so their memory can be reused.
Input
Stream is pointer to STREAM obtained in StreamCreate.
Output
-
Error
-
StreamClear %MACRO Stream
      PUSH EAX,EBX
        MOV EBX,%Stream
        LEA EAX,[EBX+SIZE#STREAM]
        MOV [EBX+STREAM.WritePtr],EAX
        MOV [EBX+STREAM.ReadPtr],EAX
        ADD EAX,[EBX+STREAM.BufSize]
        MOV [EBX+STREAM.Top],EAX
      POP EBX,EAX
 %ENDMACRO StreamClear
↑ StreamDump Stream, DumpProc
Macro StreamDump will copy the contens of the stream to callback procedure DumpProc.
DumpProc is provided with pointer and size of one memory block, it is expected to write the block to a file or to another stream or buffer.
Input
Stream points to STREAM obtained in StreamCreate.
DumpProc is the entry point of callback procedure which will write the memory block ESI,ECX.
DumpProc input
ESI= pointer to the data to be written.
ECX= size of the data.
EBP= EBP on StreamDump entry.
EBX= pointer to the Stream. Other registers are undefined.
DumpProc output
CF=0 to continue with the next block. All GPR may be destroyed in DumpProc.
DumpProc error
CF=1 if error occured during writting the block. This will cancel further dumping.
Output
-
See also
StreamRetrieve
Example
; Flush the stream contents to aFile: FileCreate aFile StreamDump aStream, aDumpProc FileClose aFile aDumpProc PROC1 FileWrite aFile,ESI,ECX RET ENDP1 aDumpProc
StreamDump %MACRO Stream, DumpProc
      PUSHD %DumpProc, %Stream
      CALL StreamDump@RT::
StreamDump@RT:: PROC1
      PUSHAD
       MOV EBX,[ESP+36] ; %Stream.
       TEST EBX
       JZ .90:
       LEA ESI,[EBX+SIZE#STREAM]
       MOV EAX,[EBX+STREAM.WritePtr]
       TEST EAX
       JZ .90:
.20:   MOV ECX,[EBX+STREAM.BufSize]
       LEA EDX,[ESI+ECX]
       CMP EAX,EDX
       JNB .40:
       CMP EAX,ESI
       JNB .70:
.40:   PUSH EAX,EBX,ESI
        CALL [ESP+40+12] ; DumpProc, not the last block.
       POP ESI,EBX,EAX
       JC .90:
       MOV ESI,[ESI-4]
       TEST ESI
       JNZ .20:
       JMP .90:
.70:   MOV ECX,EAX
       SUB ECX,ESI
       JZ .90:
       CALL [ESP+40] ; DumpProc, the last block.
.90:  POPAD
      RET 8
  ENDPROC1 StreamDump@RT::
 %ENDMACRO StreamDump
↑ StreamRetrieve Stream
Retrieving the stream returns pointer and size of one internal buffer. The .ReadPtr is then advanced to the next buffer.
Repeated invocation of StreamRetrieve returns the next buffer. Size of the last buffer may be smaller than .BufSize.
Input
Stream is pointer to STREAM obtained in StreamCreate.
Output
ZF=0, ESI= pointer to data in stream buffer.
ECX= size of the data. In most cases ECX=STREAM.BufSize but the last data buffer may be shorter.
On end of data
ZF=1, ECX=0 ESI=undefined
See also
StreamDump
StreamRetrieve %MACRO Stream
      PUSHD %Stream
      CALL StreamRetrieve@RT::
StreamRetrieve@RT:: PROC1
      PUSHAD
       MOV EBP,ESP
       MOV EBX,[%Param1] ; %Stream.
       MOV ECX,[EBX+STREAM.BufSize]
       MOV EAX,[EBX+STREAM.ReadPtr]
       MOV EDX,[EBX+STREAM.WritePtr]
       LEA ESI,[EBX+SIZE#STREAM]
       MOV [%ReturnESI],EAX
 .10:  LEA EDI,[ESI+ECX]
       CMP EDX,ESI
       JB .20:
       CMP EDX,EDI
       JNA .70:
 .20:  CMP EAX,ESI
       JB .30:
       CMP EAX,EDI
       JNA .50:
 .30:  MOV ESI,[ESI-4]
       TEST ESI
       JNZ .10:
       JMP .80:
 .50:  CMP EAX,EDI
       JNE .60:
       MOV ESI,[ESI-4]
       TEST ESI
       JZ .80:
       MOV [EBX+STREAM.ReadPtr],ESI
       MOV [%ReturnESI],ESI
       JMP .10:
 .60:  MOV ECX,EDI
       SUB ECX,EAX
       JMP .90:
 .70:  CMP EAX,ESI
       JB .80:
       CMP EAX,EDI
       JA .80:
       MOV ECX,EDX
       SUB ECX,EAX
       JA .90:
 .80:  SUB ECX,ECX
 .90:  MOV [%ReturnECX],ECX
       ADD [EBX+STREAM.ReadPtr],ECX
       TEST ECX
      POPAD
      RET 4
  ENDPROC1 StreamRetrieve@RT::
 %ENDMACRO StreamRetrieve
↑ StreamStoreRT
is a macro used internally by StreamStore*. It returns CF=0,EAX=written size or CF=1,EAX=0.
This macro also defines the subprocedure StreamStore$size@RT used in streaming ASCIIZ strings.
StreamStoreRT %MACRO ; Common runtime proc for macros StreamStore, StreamStoreLn etc.
  StreamStore@RT:: PROC1
     PUSHAD
      MOV EBX,[ESP+40] ; Stream.
      SUB EAX,EAX
      LEA ESI,[ESP+44] ; 1st data pair.
      MOV [ESP+28],EAX ; ReturnEAX.
 .10: LODSD ; DataPtr.
      MOV EBP,EAX
      TEST EAX
      JZ .90:
      LODSD ; DataSize.
      MOV EDX,EAX
      ADD [ESP+28],EAX ; Total written size.
      PUSH ESI
       ; EBP=DataPtr EDX=DataSize EBX=Stream
       MOV EDI,[EBX+STREAM.WritePtr]
       ; Start with the first block.
       LEA ESI,[EBX+SIZE#STREAM]
.15:   CMP EDI,ESI ; Find the block pointed to with ESI, where EDI=WritePtr is in.
       JB  .20:
       MOV ECX,[EBX+STREAM.BufSize]
       ADD ECX,ESI
       CMP EDI,ECX
       JNA .30:
.20:   ; WritePtr is not in block ESI. Try the next one.
       MOV ESI,[ESI-4]
       TEST ESI
       JNZ .15:
       STC ; Inconsistent data. This should never happen.
       JMP .80:
.30:   ; EDI=WritePtr inside block ESI..ECX.
       SUB ECX,EDI ; Remaining free room on the last buffer.
       XCHG ESI,EBP
.35: ; ESI=DataPtr EDI=WritePtr EBP=block EDX=requested data size ECX=remaining room.
       CMP EDX,ECX
       JA .40:
       MOV ECX,EDX
 .40:  SUB EDX,ECX
       REP MOVSB
       MOV [EBX+STREAM.WritePtr],EDI
       TEST EDX ; Data size not written yet.
       JZ .80:
       ; EDX=remaining nonstored datasize ESI=nonstored data EBX=Stream EBP=block.
       MOV EDI,[EBP-4]
       TEST EDI
       JZ .50:
       ; Next block EDI is already allocated, reuse it.
       MOV EBP,EDI
.45:   MOV [EBX+STREAM.WritePtr],EDI
       MOV ECX,[EBX+STREAM.BufSize]
       JMP .35:
.50: ; Block EBP was the last in chain. A new block needs to be allocated.
       MOV ECX,[EBX+STREAM.BufSize]
       ADD ECX,4
       PoolNew [EBX+STREAM.Pool],ECX
       JC .80:
       MOVD [EAX],0
       ADD EAX,4
       MOV [EBP-4],EAX ; Link from the old block.
       MOV EBP,EAX
       MOV EDI,EAX
       JMP .45:
 .80: POP ESI
      JNC .10:
 .90:POPAD
     RET
StreamStore$size@RT::
     PUSH ECX,EDI
      SUB ECX,ECX
      SUB EAX,EAX
      DEC ECX
      MOV EDI,[ESP+12] ; DataPtr
      REPNE SCASB
      SUB EAX,ECX
      SUB EAX,2
      MOV [ESP+16],EAX ; DataSize
     POP EDI,ECX
     RET
  ENDPROC1 StreamStore@RT::
 %ENDMACRO StreamStoreRT
↑ StreamStore Stream, DataPtr1, DataSize1, DataPtr2, DataSize2,,,
This macro writes data to the stream. The data is specified with pointer and size. More than one pointer,size pair can be specified in one invocation.
Input
Stream ^STREAM obtained in StreamCreate.
DataPtr* Pointer to data which will be copied to stream buffer.
DataSize* Size of data in bytes.
Output
CF=0, EAX= total written size.
Error
CF=1, EAX=0
StreamStore %MACRO Stream, DataPtr, DataSize
    %IF %# & 1 = 0
       ID=5971, 'Macro "StreamStore" expects odd number of arguments.'
      %EXITMACRO StreamStore
    %ENDIF
    PUSHD 0 ; Mark the end of arguments.
    ArgNr %FOR %#..2,STEP=-2
      PUSHD %*{%ArgNr}, %*{%ArgNr-1}
    %ENDFOR ArgNr
    PUSHD %Stream
    PUSH ESP
     ADDD [ESP],4*(%#+1)
     CALL StreamStore@RT::
    POP ESP
    StreamStoreRT ; Declare the runtime procedure.
 %ENDMACRO StreamStore
↑ StreamStore$ Stream, DataPtr1, DataPtr2, DataPtr3,,,
This macro writes data to the stream. The data is specified with pointer to a zero-terminated byte string. The terminating zero is not stored to the stream. More than one pointer can be specified in the invocation.
Input
Stream Pointer to STREAM obtained in StreamCreate.
DataPtr* Pointer(s) to ASCIIZ data which will be copied to stream buffer.
Output
CF=0, EAX= total written size.
Error
CF=1, EAX=0
StreamStore$ %MACRO Stream, DataPtr
    PUSHD 0 ; Mark the end of arguments.
    ArgNr %FOR %#..2,STEP=-1
      PUSHD EAX, %*{%ArgNr} ;
      CALL StreamStore$size@RT::
    %ENDFOR ArgNr
    PUSHD %Stream
    PUSH ESP
     ADDD [ESP],8*(%#)
     CALL StreamStore@RT
    POP ESP
    StreamStoreRT ; Declare the runtime procedure.
 %ENDMACRO StreamStore$
↑ StreamStoreLn Stream, DataPtr1, DataSize1, DataPtr2, DataSize2,,,
This macro writes data to the stream. More than one pointer/size pair can be specified in one invocation. After storing all data StreamStoreLn stores end-of-line characters CR+LF.
Input
Stream ^STREAM obtained in StreamCreate.
DataPtr* Pointer to data which will be copied to stream buffer.
DataSize* Size of data in bytes.
Output
CF=0, EAX= total written size including CR+LF.
Error
CF=1 EAX=0
StreamStoreLn %MACRO Stream, DataPtr, DataSize
    %IF %# & 1 = 0
      %ERROR ID=5972, 'Macro "StreamStoreLn" expects odd number of arguments.'
      %EXITMACRO StreamStoreLn
    %ENDIF
    PUSHD 0x00000A0D ; EOL data.
    PUSHD 0 ; Mark end of arguments.
    PUSHD 2 ; Size of EOL.
    PUSHD ESP ; Pointer to EOL.
    ADDD [ESP],8 ; Adjust the pointer.
    ArgNr %FOR %#..2,STEP=-2
      PUSHD %*{%ArgNr}, %*{%ArgNr-1}
    %ENDFOR ArgNr
    PUSHD %Stream
    PUSH ESP
     ADDD [ESP],4*(%#+4)
     CALL StreamStore@RT::
    POP ESP
    StreamStoreRT ; Declare runtime procedure.
 %ENDMACRO StreamStoreLn
↑ StreamStoreByte Stream, Data
This macro writes 1 bytes of data to the stream.
Input
Stream ^STREAM obtained in StreamCreate.
Data Byte memory variable or 8bit register.
Output
CF=0, EAX=1
Error
CF=1, EAX=0
StreamStoreByte %MACRO Stream, Data
    %IF "%Data" !== "AL"
      MOV AL,%Data
    %ENDIF
    PUSHD EAX
    PUSHD 0 ; Mark the end of arguments.
    PUSHD 1 ; Size of %Data.
    PUSHD ESP ; Pointer to %Data.
    ADDD [ESP],8 ; Adjust the pointer.
    PUSHD %Stream
    PUSH ESP
     ADDD [ESP],20
     CALL StreamStore@RT::
    POP ESP
    StreamStoreRT ; Declare runtime procedure.
 %ENDMACRO StreamStoreByte
↑ StreamStoreWord Stream, Data
This macro writes 2 bytes of data to the stream.
Input
Stream ^STREAM obtained in StreamCreate.
Data Word memory variable or 16bit register.
Output
CF=0, EAX=2
Error
CF=1, EAX=0
StreamStoreWord %MACRO Stream, Data
    %IF "%Data" !== "AX"
      MOV AX,%Data
    %ENDIF
    PUSHD EAX
    PUSHD 0 ; Mark the end of arguments.
    PUSHD 2 ; Size of %Data.
    PUSHD ESP ; Pointer to %Data.
    ADDD [ESP],8 ; Adjust the pointer.
    PUSHD %Stream
    PUSH ESP
     ADDD [ESP],20
     CALL StreamStore@RT::
    POP ESP
    StreamStoreRT ; Declare runtime procedure.
 %ENDMACRO StreamStoreWord
↑ StreamStoreDword Stream, Data
This macro writes 4 bytes of data to the stream.
Input
Stream ^STREAM obtained in StreamCreate.
Data Doubleword memory variable or 32bit register.
Output
CF=0, EAX=4
Error
CF=1, EAX=0
Example
StreamStoreDword aStream,EBX store the contents of EBX.
StreamStore aStream,EBX,4 store 4 bytes of the memory addressed with EBX.
StreamStoreDword %MACRO Stream, Data
    PUSHD %Data
    PUSHD 0 ; Mark the end of arguments.
    PUSHD 4 ; Size of %Data.
    PUSHD ESP ; Pointer to %Data.
    ADDD [ESP],8 ; Adjust the pointer.
    PUSHD %Stream
    PUSH ESP
     ADDD [ESP],20
     CALL StreamStore@RT::
    POP ESP
    StreamStoreRT ; Declare runtime procedure.
 %ENDMACRO StreamStoreDword
 ENDHEAD memory ; End of library interface.

▲Back to the top▲