; ========================= SOURCE FILE: _list2bin.asm =======================
; PURPOSE:
;  Converts a Visual Prolog list (of many possible list-domains) into an array
;  of element-pointers (and vice-versa), in the form of a Visual Prolog binary.
;
; APPLICATIONS:
;  Large-scale fast Data Mining Applications and/or very fast List Processing.
;
; ASSEMBLY LANGUAGE CODE begins here:

IDEAL          ; Use TASM Assembler, version 5.*, "ideal mode" syntax
P586           ; Use Pentium-specific instructions
MODEL    FLAT  ; Use Flat memory model (accessing all hardware-RAM)
CODESEG        ; Code-segment follows
ALIGN    4     ; 4-byte (dword-)alignment, always required by Visual Prolog

; **************** ASSEMBLY LANGUAGE PUBLIC PROCEDURES: *********************
public _list2bin_0     ; (i,i)
public _list2bin_1     ; (i,o)
public _list2bin_2     ; (o,i)

extrn   _MEM_MakeBinaryGStack:near
extrn   _MEM_AllocGStack:near

PROC _list2bin_0 near  ; -(i,i)
    mov ecx,[esp+4] ; list_pointer in ECX
    mov edx,[esp+8] ; binary_pointer in EDX
    push    ebx     ;
    push    edi     ;
    mov edi,4       ; let EDI = 4
    mov ah,2        ; let AH = 2 (end_element_flag)
@@P1:   
    mov al,[byte ptr ecx]   ; load list_element_flag in AL
    cmp al,ah       ; is it an end-element?
    jz  @@X1        ; if so, skip next...
; -------------------------------
    mov ebx,[ecx+edi]   ; load actual element into EBX
    mov [edx],ebx       ; write it to the output
    mov ecx,[ecx+8] ; make ECX = next_element_pointer
    add edx,edi     ; advance target_pointer
    jmp short @@P1      ; repeat the loop
; ===============================
@@X1:   pop edi
    pop ebx
    retn
ENDP _list2bin_0


PROC _list2bin_1 near   ; -(i,o)
    mov ecx,[esp+4] ; let ECX = list_pointer
    mov edx,[esp+8] ; binary_pointer in EDX
    push    edx         ;
    xor edx,edx    ; initialize EDX as number-of-elements counter
    push    ecx     ; save ECX in the stack
    mov    ah,2    ; prepare AH = 2 (end-element flag)
@@p1:
     mov    al,[byte ptr ecx] ; put the "element-type indicator" in AL
    cmp    al,ah   ; compare with 2 (end-of-list indicator)
    jz @@p2        ; if end of list, then exit!   
; -------------------
    inc    edx     ; else, increment element_counter EDX
    mov    ecx,[ecx+8] ; make ECX = next list-element-pointer
    jmp short @@p1         ; repeat the loopENDP _list2bin_1
; ===========================
@@p2:    shl    edx,1   ;
    shl    edx,1       ; now EDX = size of bytes required (in binary)
    push   esi         ;
     push  edi         ;
      push edx         ;
       call _MEM_MakeBinaryGStack  ; allocate memory for the binary
      pop  edx         ;
     pop   edi         ;
    pop    esi         ;
    pop ecx             ; restore ECX = list_pointer (again!)
    mov edx,eax     ; let EDX = pointer to the new binary
    push    eax         ;
    push    ebx         ;
    push    edi         ;
    mov edi,4           ; let EDI = 4
    mov ah,2            ; let AH = 2 (end_element_flag)
@@Q1:   mov al,[byte ptr ecx]   ; load list_element_flag in AL
    cmp al,ah           ; is it an end-element?
    jz  @@X2            ; if so, skip next...
; -----------------------
    mov ebx,[ecx+edi]   ; load actual element into EBX
    mov [edx],ebx       ; write it to the output
    mov ecx,[ecx+8] ; make ECX = next_element_pointer
    add edx,edi     ; advance target_pointer
    jmp short @@Q1      ; repeat the loop
; =======================
@@X2:   pop edi     ;
    pop ebx         ;
    pop eax         ;
    pop edx         ; restore arg2
    mov [edx],eax   ; make output_arg2 = binary
    retn
ENDP _list2bin_1


PROC _list2bin_2 near        ; -(o,i)
    mov ecx,[esp+4] ; let ECX = list_pointer
    mov edx,[esp+8] ; binary_pointer in EDX
    push    ECX     ;
    push    ESI     ;
    push    EDI     ;
    push    ebx     ;
    push   edx     ;
     mov   ebx,4       ;   
     mov eax,[edx-4]   ; read binary_length
     sub   eax,ebx ;
     shr   eax,1       ; now EAX = 2 * number of list_elements
     mov   edx,eax ;
     shr   eax,1       ; now EAX =  number of 4-byte elements
     push  eax         ; save it !
      add  eax,edx ; now EAX =  3 * number of list_elements
      shl  eax,1       ; now EAX =  6 * number of list_elements
      shl  eax,1       ; now EAX = 12 * number of list_elements
      add  eax,12      ; + 12 more elements (for "end_element" in list)
      push eax     ;
       call _MEM_AllocGStack   ; allocate memory for the dword_List
      pop  ecx     ;
     pop   ecx     ; restore number of elements into ECX
     mov edi,eax   ; now EDI = start of new List_object
    pop    esi     ; restore ARG2 (binary), but into ESI
    push   eax     ; save the "new list" also in the stack
     mov   ebx,4       ; make ebx = 4
@@p1:     
      mov   eax,1       ; make eax = 1 (normal list element flag)
     stosd         ; write "normal list_element flag" to EDI, EDI->EDI+4
     movsd         ; move 4 bytes, advance both ESI and EDI by 4
     mov   eax,edi ;
     add   eax,ebx ; EAX := ptr to "next list element" on target EDI
     stosd         ; write it, advancing EDI by 4,
     dec   ecx         ; decrement counter
     jnz   @@p1        ; repeat loop, if ECX > 0
; -----------------------
@@p2:     mov   eax,2   ;
     stosd         ; write an "end of list flag" into EDI, EDI -> EDI+4
     xor   eax,eax ;
     stosd         ;
     stosd         ; write 8 dummy bytes to last list-element
    pop    eax         ; recover new_list_start_pointer, into EAX
    pop ebx     ;
    pop edi     ;
    pop esi     ;
    pop ecx     ;
    mov [ecx],eax   ; make output_arg2 = number_list
    retn
ENDP _list2bin_2

    end   ; End of all ASM code. (Anything below this point is ignored by TASM)

/************************* VISUAL PROLOG test-program: **********************/
GLOBAL DOMAINS
 ILIST = INTEGER*
 SLIST = INTEGER*
 
GLOBAL PREDICATES
 list2bin(ILIST,BINARY) -(i,i),(i,o),(o,i) language c
 slist2bin(SLIST,BINARY) -(i,i) language c as "_list2bin_0"
 slist2binx(SLIST,BINARY) -(i,o) language c as "_list2bin_1"
 slistx2bin(SLIST,BINARY) -(o,i) language c as "_list2bin_2"
 nondeterm repeat % defined elsewhere

GOAL
 getbacktrack(TEST1),
  repeat, write("give an integer-list:\n> "), readln(S), term_str(ilist,ILx,S),
  Len = length(ILx), Bin = makebinary(Len,4),
  write("First, creating a new binary: ",Bin), nl, list2bin(ILx,Bin),
  write("this existing binary became: ",Bin), nl, list2bin(ILx,Binx1),
  SZx1 = getbinarysize(Binx1), write("binary size = ",SZx1), nl,
  write("and a new 'fresh' binary is: ",Binx1), nl, list2bin(IL2x,Binx1),
  write("converting the binary back into a list: ",IL2x),
  write("\nPress ESC to exit, any other key for the next test...\n"),
  readchar(C1x), C1x='\27',
 cutbacktrack(TEST1),
 getbacktrack(TEST2),
  repeat, write("give a string-list:\n> "), readln(S2), term_str(slist,SLx,S2),
  slist2binx(SLx,BNSx), SZx = getbinarysize(BNSx), write("binary size = ",SZx), nl,
  write("Binary with string-pointers: ",BNSx), readchar(_), slistx2bin(SLx2,BNSx),
  write("\nNew list generated from this binary:\n",SLx2), nl,
  write("\nPress ESC to exit, any other key to repeat...\n"),
  readchar(C2x), C2x='\27',
 cutbacktrack(TEST2), !.

; **************  "32-bit Assembly Language Extensions for Visual Prolog" ****************
; Written by George A. Stathis (c) 2005,       E-mail: omadeon@yahoo.com,  gstathis@enm.gr
; URL: http://www.omadeon.com/asm    Company: ENB Ltd.     Company site: http://www.enb.gr
; **** Use of this source-code is unrestricted provided that the author is mentioned. ****