A.I. programming in Prolog and Assembler

February 15, 2009

Assembly Language for Visual Prolog Meta-programming

(This is an experimental repost – for testing purposes, using  Scribefire. Original post is here).

Back in 2005, while working in large-scale programming projects for data-mining in G.I.S. and Hydrology, I wrote a Prolog interpreter called G.I.S. Prolog, equipped with many extra predicates (such as functions to locate points inside polygons, etc).

The G.I.S. Prolog interpreter was originally based on  the “PIE interpreter” (included as free source-code in Visual Prolog) but it ended up enhanced with many extra predicates, as well as an improved core-level inference mechanism.

Ever since I started using the Visual Prolog compilers (and the PDC Prolog compilers preceding them) I was fascinated by the possibilities of implementing additional ISO-Prolog functionality in Visual Prolog through Assembly Language and ‘C’. Of course, such attempts are inherently limited by the internal design of Visual Prolog compilers. So, the only way to implement ISO-Prolog functionality in Visual Prolog is to extend the “PIE Interpreter” (and G.I.S. Prolog as its offspring).

A multitude of extra predicates, implemented in pure Assembly language, became available through G.I.S. Prolog for easy immediate experimentation: Coding in G.I.S. Prolog produced immediate results, without any need for (often tedious) EXE-file compilation. Code modifications could therefore be done very quickly and most mistakes were (semi-)automatically corrected by the interpreter’s own enhanced error-checking capabilities.

Recently, I discovered some Assembly language techniques to enhance G.I.S. Prolog even further, potentially valuable for a multitude of other purposes. They also have an intrinsic fascination in themselves, as general tools for Prolog meta-programming.
E.g. here is an Assembly language predicate, that takes as inputs another (external) predicate’s memory-address and a (Visual Prolog-) argument-list, and calls this (external) predicate, using the (arbitrary-length-) list of N arguments, as arguments of “arity N”:


apply_func(PRED, [Arg,Arg2,…]) <=> PRED(Arg1,Arg2,…)

Now, in ISO-Prolog there is a standard predicate known as “univ”, written as “=..“, which turns a list like [PRED, ARG1, ARG2, ARG3…] into a predicate call such as PRED(ARG1,ARG2,…). However, this does not exist in Visual Prolog, which sacrifices such “luxuries” for speed (which is  the reason I also often use ISO-Prolog compilers, such as LPA-WinProlog and SWI-Prolog).
Anyway… The code you are about to see can be useful more generally, as an example of Prolog meta-programming implemented in Assembly Language. The only difference between the way it works for Visual Prolog and the way it might work for another Prolog (or -indeed- ANY programming language, using a ‘C’-calling convention) is the Visual-Prolog-specific structure of a LIST, which in Visual Prolog has a  different form than in all other languages. If you understand Assembly Language and intend to use this code for other (meta-programming) tasks, all you have to do is modify just a couple of lines in the code that follows. However, before you (even begin to) look at the Assembly Language Code, the following simple definitions in Visual Prolog (5.*) are a prerequisite for easier understanding:

GLOBAL DOMAINS
dom_iii = determ INTEGER (INTEGER,INTEGER,INTEGER) -(i,i,i) language c % <– example domain

GLOBAL PREDICATES
apply_func(DWORD,ListDomain) -(i,i) language c  % where arg-1 is a predicate-domain, such as “dom_iii”

After you compile the Assembly language code, you could create a simple “EasyWin” Visual Prolog project, with the following ines:

PREDICATES
func2dword(dom_iii,DWORD) % converts a predicate to a doubleword memory-address

CLAUSES
func2dword(FUNC,DW):- DW = cast(dword,FUNC).

GOAL
func2dword(add3,DW),
Out = apply_func(DW,[10,20,30]),
write(“result = “,Out), nl, readchar(_).

OK, so here is the Assembly language code:

; ======================= _apply_func.asm ==========================
; Code for TASM 5 Assembler, command-line call for compilation:
; C:\TASM\BIN\TASM32.EXE /p /z /w2 /m2 /ml _apply_func.asm

IDEAL
P586
MODEL    flat
CODESEG
ALIGN 4

public _apply_func    ; (i,i)

PROC _apply_func near
ARG    func:dword, list:dword
LOCAL    fcnt:dword
ENTER 4,0
push    esi        ;
push    edi        ;
push    ebx        ;
mov    ecx,[func]  ; function………… ARG 1
mov    esi,[list]  ; list……………. ARG 2
xor    ebx,ebx     ; make EBX=0
mov    [fcnt],ebx  ; initialize local variable ‘fcnt’ to 0
lodsd              ; load the first list-element’s “element-flag”
dec    al          ; decrement it, to check if it was equal to 1
jnz short @@x1     ; exit if not so (i.e. if it was the end of the list)
; ———————- else…
@@L1:                  ; loop to read the (Visual-Prolog-) argument-list
inc    ebx        ; increment ebx (counter for number_of_arguments)
lodsd             ; load next list-element (argument of function)
push    eax       ; push it into the stack (for later function-call)
lodsd             ; load the pointer to the next list-element in EAX
mov    esi,eax    ; now ESI = (pointer-to-) next list-element
lodsd             ; load element-flag of next list-element
dec    al         ; decrement it, to check if it was a 1
jz short @@L1     ; if so, not yet the end of the list, so repeat!
; ====================== else…
@@x1:
call   ecx         ; call the (external) function (given in ARG-1)
mov    ecx,[fcnt]  ; get the function’s number of arguments from fcnt
@@L2:
jcxz @@x2          ; if the called function had NO arguments, exit
; ———————- else…
@@L3:                  ; loop to POP function-arguments, after the call
pop        edx     ; recover next argument from the stack
mov    ecx,[fcnt]  ; recover remaining arguments_counter
dec        ecx     ; decrement the remaining number_of_arguments
mov    [fcnt],ecx  ; store it again, in the local variable fcnt
jnz short @@L3     ; if not zeroed, continue popping arguments…
; ———————-
@@x2:
pop        ebx     ;
pop        edi     ;
pop        esi     ;
LEAVE
ENDP _apply_func

end

Reblog this post [with Zemanta]

1 Comment »


RSS feed for comments on this post. TrackBack URI

Leave a comment

Create a free website or blog at WordPress.com.