How to simulate “add eax, 1” in Prolog?

2019-01-29 08:25发布

问题:

I am trying to simulate some simple asm code using Prolog. (32 bit)

I am new in Prolog and I am stucked in some problems without any solutions.

Basically if here is the code:

...
add eax, 1
...

and I want to simulate in this way:

...
EAX is EAX - 1,
...

and swipl will generate errors like :

  Call: (7) 1 is 1-1 ? creep
 Fail: (7) 1 is 1-1 ? creep
 ....
 false

I know basically I could do like this:

EAX_temp is EAX + 1 

But how can I keep manipulate EAX in next instructions..?

Could any one give me some help..? Thank you!

回答1:

There are probably several good ways to do this. And the answer might further be refined by your context which is currently not clear.

One way, is you could create dynamic facts for register values:

:- dynamic(register/2).  % Fill in as needed

register(eax, 0).
register(ebx, 0).
...

add(Reg, Value) :-
    (   retract(register(Reg, OldValue))
    ->  NewValue is OldValue + Value
    ;   NewValue = Value                % If the register wasn't defined
    ),
    assertz(register(Reg, NewValue)).

Then do:

add(eax, 4).           % add eax,4

To read a register, you would just use, for example:

register(eax, EAXValue).

The main drawback of assert and retract is that they take a lot more CPU time than list manipulation. But I think they make sense for this kind of application where you have a CPU "state" that is represented by several register values.



回答2:

The "Prolog" way would be to actually maintain the state of all your registers in a term which you pass along the main predicate that runs the simulation. So, for example:

% general-purpose registers
% regs(EAX, EBX, ECX, EDX, ESI, EDI)
regs(0, 0, 0, 0, 0, 0)

But please note: this is not a predicate (hence the missing dot at the end)! This is a term, and it will be initialized to all zeros (I am assuming here):

init_regs(regs(0,0,0,0,0,0)).

So at the beginning of your program you can initialize your registers with:

main :-
    init_regs(Regs),
    step(Regs).

step(Regs) :-
    read_instruction(Instruction),
    apply_instruction(Instruction, Regs, New_regs),
    step(New_regs).

apply_instruction(add(eax, Addend),
        regs(EAX, EBX, ECX, EDX, ESI, EDI),
        regs(New_EAX, EBX, ECX, EDX, ESI, EDI)) :-
    New_EAX is EAX + Addend.

You can leave it at this, or you can have a helper predicate that provides access to the one register you need, for example:

reg_eax(reg(EAX, _, _, _, _, _), EAX).
reg_ebx(reg(_, EBX, _, _, _, _), EBX).
% and so on

And to set a register:

set_reg_eax(reg(EAX, EBX, ECX, EDX, ESI, EDI),
            New_EAX,
            reg(New_EAX, EBX, ECX, EDX, ESI, EDI)).
% and so on

which you can then use like this to define your apply_instruction/3:

apply_instruction(add(eax, Addend), Regs, New_regs) :-
    reg_eax(Regs, EAX),
    New_EAX is EAX + Addend,
    set_reg_eax(Regs, New_EAX, New_regs).

The sort of predicates, reg_eax and set_reg_eax can be automatically generated by a library, library(record) (see here), with the initial idea proposed by Richard o'Keefe in his book "The Craft of Prolog" for doing exaclty this sort of stuff. If you use the libary, you don't need to write all the access and set predicates yourself.

If you are using SWI-Prolog, however, you can also make use of Dicts; see here. This is part of the current development version of SWI-Prolog (version 7) and makes dealing with structures with named arguments much easier.



标签: prolog