Lhogho  0.0.027
Defines | Functions
i386.c File Reference

Defines

#define EXIT   (void*)ctx->exit_addr
#define START_IP   (ctx->generate?(int)MEMORY(ctx->generate):0)
 start IP
#define LOCAL_IP   (ctx->size)
 local IP (0-based)
#define IP   START_IP+LOCAL_IP
 current IP
#define REL(addr)   relative( ctx, (uint_t)addr )
 relative address
#define EMIT1(a)   emit_1(ctx,a)
#define EMIT2(a)   emit_2(ctx,a)
#define EMIT3(a, b)   emit_2(ctx,a); emit_1(ctx,b)
#define EMIT4(a)   emit_4(ctx,a)
#define EMIT5(a, b)   emit_1(ctx,a); emit_4(ctx,(uint_t)b)
#define EMIT6(a, b)   emit_2(ctx,a); emit_4(ctx,(uint_t)b)
#define PUSH_EAX   { EMIT1(0x50); INSTR("push eax"); }
#define PUSH_EDX   { EMIT1(0x52); INSTR("push edx"); }
#define PUSH_EBP   { EMIT1(0x55); INSTR("push ebp"); }
#define PUSH_ESI   { EMIT1(0x56); INSTR("push esi"); }
#define PUSH_EBX   { EMIT1(0x53); INSTR("push ebx"); }
#define POP_EAX   { EMIT1(0x58); INSTR("pop eax"); }
#define POP_EDX   { EMIT1(0x5A); INSTR("pop edx"); }
#define POP_EBP   { EMIT1(0x5D); INSTR("pop ebp"); }
#define POP_ESI   { EMIT1(0x5E); INSTR("pop esi"); }
#define POP_EBX   { EMIT1(0x5B); INSTR("pop ebx"); }
#define PUSH_MEM_EAX(ofs)   { EMIT6(0xB0FF,ofs); INSTR("push [eax%p]",ofs); }
#define PUSH_MEM_EBP(ofs)   { EMIT6(0xB5FF,ofs); INSTR("push [ebp%p]",ofs); }
#define PUSH_MEM_ESI(ofs)   { EMIT6(0xB6FF,ofs); INSTR("push [esi%p]",ofs); }
#define PUSH_MEM(ofs)   { EMIT6(0x35FF,ofs); INSTR("push [%d]",ofs); }
#define PUSH_SOURCE(s)   { EMIT5(0x68,s); INSTR("push (%l)",s); }
#define PUSH_CONST(n)   { EMIT5(0x68,n); INSTR("push %d",n); }
#define PUSH_ATOM(a)   { EMIT5(0x68,a); INSTR("push %a",a); }
#define PUSH_ATOMSTR(a, s)   { EMIT5(0x68,a); INSTR("push %s",s); }
#define MOV_EAX_ATOM(atom)   { EMIT5(0xB8,atom); INSTR("mov eax,%a",atom); }
#define MOV_ESI_CONST(n)   { EMIT5(0xBE,n); INSTR("mov esi,%d",n); }
#define MOV_ESI_CONST(n)   { EMIT5(0xBE,n); INSTR("mov esi,%d",n); }
#define MOV_EBX_CONST(n)   { EMIT5(0xBB,n); INSTR("mov ebx,%d",n); }
#define MOV_EAX_EBP   { EMIT2(0xE889); INSTR("mov eax,ebp"); }
#define MOV_EBP_ESP   { EMIT2(0xE589); INSTR("mov ebp,esp"); }
#define MOV_ESP_EBP   { EMIT2(0xEC89); INSTR("mov esp,ebp"); }
#define MOV_ESI_EBP   { EMIT2(0xEE89); INSTR("mov esi,ebp"); }
#define MOV_EAX_MEM_ESP   { EMIT3(0x048B,0x24); INSTR("mov eax,[esp]"); }
#define MOV_EAX_MEM_EBP(ofs)   { EMIT3(0x458B,ofs); INSTR("mov eax,[ebp%p]",ofs); }
#define MOV_EAX_MEM_EAX(ofs)   { EMIT3(0x408B,ofs); INSTR("mov eax,[eax%p]",ofs); }
#define MOV_EAX_MEM_OFS(ofs)   { EMIT6(0x058B,ofs); INSTR("mov eax,[%d]",ofs); }
#define MOV_EBP_MEM_OFS(ofs)   { EMIT6(0x2D8B,ofs); INSTR("mov ebp,[%d]",ofs); }
#define MOV_EBX_MEM_OFS(ofs)   { EMIT6(0x1D8B,ofs); INSTR("mov ebp,[%d]",ofs); }
#define MOV_MEM_EBP_EAX(ofs)   { EMIT6(0x8589,ofs); INSTR("mov [ebp%p],eax",ofs); }
#define MOV_MEM_ESI_EAX(ofs)   { EMIT6(0x8689,ofs); INSTR("mov [esi%p],eax",ofs); }
#define MOV_MEM_OFS_EAX(ofs)   { EMIT6(0x0589,ofs); INSTR("mov [%d],eax",ofs); }
#define MOV_MEM_OFS_EBP(ofs)   { EMIT6(0x2D89,ofs); INSTR("mov [%d],ebp",ofs); }
#define MOV_MEM_OFS_CONST(ofs, n)
#define MOV_ESI_SSMEM_ESI_4   { EMIT4(0xFC768B36); INSTR("mov esi,ss:[esi-4]"); }
#define MOV_ESI_SSMEM_EBP_4   { EMIT4(0xFC758B36); INSTR("mov esi,ss:[ebp-4]"); }
#define INC_EAX_MEM(n)   { EMIT3(0x40FF,n); INSTR("inc [eax%p]",n); }
#define DEC_EAX_MEM(n)   { EMIT3(0x48FF,n); INSTR("dec [eax%p]",n); }
#define RET   { EMIT1(0xC3); INSTR("ret"); }
#define ADD_EAX_CONST_4(n)   { EMIT5(0x05,n); INSTR("add eax,%d",n); }
#define ADD_ESI_CONST_4(n)   { EMIT6(0xC681,n); INSTR("add esi,%d",n); }
#define CMP_EBX_CONST(n)   { EMIT3(0xFB83,n); INSTR("cmp ebx,%d",n); }
#define CMP_EAX_CONST(n)   { EMIT5(0x3D,n); INSTR("cmp eax,%d",n); }
#define CMP_EAX_ATOM(a)   { EMIT5(0x3D,a); INSTR("cmp eax,%l",a); }
#define CMP_ID_ERROR
#define CMP_MEM_EBP(ofs, val)
#define CMP_MEM_OFS_CONST_4(ofs, n)   { EMIT6(0x3D81,ofs); EMIT4(n); INSTR("cmp [%d],%a", ofs, n); }
#define CMP_MEM_OFS_CONST_1(ofs, n)
#define JE_EXIT   { EMIT6(0x840F,REL(EXIT)); INSTR("je exit"); }
#define JE(addr, name)   { EMIT6(0x840F,REL(addr)); INSTR("je %s",name); }
#define JNE(addr, name)   { EMIT6(0x850F,REL(addr)); INSTR("jne %s",name); }
#define JNZ(addr, name)   { EMIT6(0x850F,REL(addr)); INSTR("jnz %s",name); }
#define JMP_EXIT   { EMIT5(0xE9,REL(EXIT)); INSTR("jmp exit"); }
#define JMP(addr, name)   { EMIT5(0xE9,REL(addr)); INSTR("jmp %s",name); }
#define JMP_ATOM(addr, name)   { EMIT5(0xE9,REL(addr)); INSTR("jmp %a",name); }
#define JMP_EAX(name)   { EMIT2(0xE0FF); INSTR("jmp %a",name); }
#define CALL(addr, name)   { EMIT5(0xE8,REL(addr)); INSTR("call %s",name); }
#define CALL_ATOM(addr, name)   { EMIT5(0xE8,REL(addr)); INSTR("call %a",name); }
#define CALL_MEM(addr, name)   { EMIT6(0x15FF,addr); INSTR("call [%a]",name); }
#define CALL_MEM_EAX(addr, name)   { EMIT6(0x90FF,addr); INSTR("call [eax+%s]",name); }
#define FSTPF_MEM_ESP   { EMIT2(0x1CD9); EMIT1(0x24); INSTR("fstpf [esp]"); }
#define FSTPD_MEM_ESP   { EMIT2(0x1CDD); EMIT1(0x24); INSTR("fstpd [esp]"); }
#define INT_3   { EMIT1(0xCC); INSTR("int 3"); }
#define NOP   { EMIT1(0x90); INSTR("nop"); }

Functions

uint_t relative (context_t *ctx, int addr)
 calculates relative offset
void asm_label (context_t *ctx, chars_t label)
 disassemble label
void asm_prologue (context_t *ctx, atom_t func)
 generates a function prologue
void asm_preepilogue (context_t *ctx)
void asm_epilogue (context_t *ctx, atom_t func, int is_macro)
 generates a function epilogue
void asm_push_atom (context_t *ctx, atom_t atom)
 genarates a code to push atom
void asm_pop_atom (context_t *ctx)
 genarates a code to pop atom
void asm_pop_dummy (context_t *ctx)
 genarates a code to pop (and ignore)
void asm_push_result (context_t *ctx)
 genarates a code to push function's result
void asm_result_proc (context_t *ctx, atom_t source)
 check result of a command
void asm_result_unknown (context_t *ctx, atom_t source)
 check result of expression
void asm_result_func (context_t *ctx, atom_t source)
 check result of a function
int asm_static_link (context_t *ctx, atom_t var)
 generated code for static link
void compile_system_reference (context_t *ctx, atom_t name)
 compiles reference to system variable
void asm_reference (context_t *ctx, atom_t var, atom_t source, int check)
 generated code for reference
void asm_make_direct (context_t *ctx, atom_t var, atom_t source)
 generated code for direct make statement
void asm_push_value_addr (context_t *ctx, atom_t var)
 generated code for address of var's value
void asm_call_atom (context_t *ctx, atom_t var, int params)
 genarates a code to call atom
void asm_runtime_reference (context_t *ctx)
 genarates a code to get variable value
void asm_dump_source (context_t *ctx, atom_t source)
 dump current source
void asm_empty_body (context_t *ctx)
 generates the body of an empty function
void asm_output (context_t *ctx, atom_t source, int true_output)
 generated code for OUTPUT statement
void asm_stop (context_t *ctx, atom_t source)
 generated code for STOP statement
void asm_boolean (context_t *ctx, atom_t source)
 generated code for checking boolean values
int asm_if_prologue (context_t *ctx)
 generates prologue for IF command
int asm_if_epilogue (context_t *ctx)
 generates epilogue for IF command
void asm_fix (context_t *ctx, int addr)
 fixes unresolved offset
int asm_repeat_prologue_const (context_t *ctx, int cnt)
 generates const-prologue for REPEAT command
int asm_repeat_prologue_expr (context_t *ctx, atom_t source, int *branch2)
 generates expr-prologue for REPEAT command
void asm_repeat_epilogue (context_t *ctx, int branch, int branch2)
 generates epilogue for REPEAT command
int asm_forever_prologue (context_t *ctx)
 generates prologue for FOREVER command
void asm_forever_epilogue (context_t *ctx, int branch)
 generates epilogue for FOREVER command
void asm_push_frame (context_t *ctx)
 generates code to push frame pointer
void asm_pop_frame (context_t *ctx)
 generates code to pop frame pointer
void asm_push_mode (context_t *ctx, int mode)
 generates code to push mode
void asm_adjust_result (context_t *ctx)
 generated code for result adjustment
void asm_pop_result (context_t *ctx)
 generated code for result adjustment
int asm_while_prologue (context_t *ctx, int is_while, int is_do)
 generates prologue for WHILE command
int asm_while_inlogue (context_t *ctx, atom_t source, int is_while)
 generates inlogue for WHILE command
void asm_while_epilogue (context_t *ctx, int loop_branch, int skip_branch, int is_while)
 generates epilogue for WHILE/UNTIL command
void asm_set_output_status (context_t *ctx, int n)
 generates code for output status
int asm_catch_prologue (context_t *ctx)
 generates prologue for CATCH command
void asm_catch_epilogue (context_t *ctx)
 generates epilogue for CATCH command
void asm_goto (context_t *ctx, atom_t tag)
 generates jump to specific address
void asm_goto_prologue (context_t *ctx, atom_t source)
 generates prologue for GOTO command
void asm_goto_epilogue (context_t *ctx, atom_t source)
 generates epilogue for GOTO command
int asm_test_prologue (context_t *ctx, int criteria)
 generates prologue for IFTRUE/IFFALSE command
void asm_run_epilogue (context_t *ctx)
 generates epilogue for RUN command
void asm_runresult_epilogue (context_t *ctx)
 generates epilogue for RUNRESULT command
int asm_for_prologue (context_t *ctx, atom_t step_var, atom_t source, int *branch2)
 generates prologue for FOR command
void asm_for_epilogue (context_t *ctx, int branch, int branch2)
 generates epilogue for FOR command
void asm_external_function (context_t *ctx, atom_t func)
 generates trampoline to external function
void asm_internal_function (context_t *ctx, int static_link, atom_t func)
 generates trampoline to external function
void asm_int_3 (context_t *ctx)
 generates Interrupt 3
void asm_nop (context_t *ctx)
 generates NOP

Define Documentation

#define EXIT   (void*)ctx->exit_addr
#define START_IP   (ctx->generate?(int)MEMORY(ctx->generate):0)
#define LOCAL_IP   (ctx->size)
#define IP   START_IP+LOCAL_IP
#define REL (   addr)    relative( ctx, (uint_t)addr )
#define EMIT1 (   a)    emit_1(ctx,a)
#define EMIT2 (   a)    emit_2(ctx,a)
#define EMIT3 (   a,
 
)    emit_2(ctx,a); emit_1(ctx,b)
#define EMIT4 (   a)    emit_4(ctx,a)
#define EMIT5 (   a,
 
)    emit_1(ctx,a); emit_4(ctx,(uint_t)b)
#define EMIT6 (   a,
 
)    emit_2(ctx,a); emit_4(ctx,(uint_t)b)
#define PUSH_EAX   { EMIT1(0x50); INSTR("push eax"); }
#define PUSH_EDX   { EMIT1(0x52); INSTR("push edx"); }
#define PUSH_EBP   { EMIT1(0x55); INSTR("push ebp"); }
#define PUSH_ESI   { EMIT1(0x56); INSTR("push esi"); }
#define PUSH_EBX   { EMIT1(0x53); INSTR("push ebx"); }
#define POP_EAX   { EMIT1(0x58); INSTR("pop eax"); }
#define POP_EDX   { EMIT1(0x5A); INSTR("pop edx"); }
#define POP_EBP   { EMIT1(0x5D); INSTR("pop ebp"); }
#define POP_ESI   { EMIT1(0x5E); INSTR("pop esi"); }
#define POP_EBX   { EMIT1(0x5B); INSTR("pop ebx"); }
#define PUSH_MEM_EAX (   ofs)    { EMIT6(0xB0FF,ofs); INSTR("push [eax%p]",ofs); }
#define PUSH_MEM_EBP (   ofs)    { EMIT6(0xB5FF,ofs); INSTR("push [ebp%p]",ofs); }
#define PUSH_MEM_ESI (   ofs)    { EMIT6(0xB6FF,ofs); INSTR("push [esi%p]",ofs); }
#define PUSH_MEM (   ofs)    { EMIT6(0x35FF,ofs); INSTR("push [%d]",ofs); }
#define PUSH_SOURCE (   s)    { EMIT5(0x68,s); INSTR("push (%l)",s); }
#define PUSH_CONST (   n)    { EMIT5(0x68,n); INSTR("push %d",n); }
#define PUSH_ATOM (   a)    { EMIT5(0x68,a); INSTR("push %a",a); }
#define PUSH_ATOMSTR (   a,
 
)    { EMIT5(0x68,a); INSTR("push %s",s); }
#define MOV_EAX_ATOM (   atom)    { EMIT5(0xB8,atom); INSTR("mov eax,%a",atom); }
#define MOV_ESI_CONST (   n)    { EMIT5(0xBE,n); INSTR("mov esi,%d",n); }
#define MOV_ESI_CONST (   n)    { EMIT5(0xBE,n); INSTR("mov esi,%d",n); }
#define MOV_EBX_CONST (   n)    { EMIT5(0xBB,n); INSTR("mov ebx,%d",n); }
#define MOV_EAX_EBP   { EMIT2(0xE889); INSTR("mov eax,ebp"); }
#define MOV_EBP_ESP   { EMIT2(0xE589); INSTR("mov ebp,esp"); }
#define MOV_ESP_EBP   { EMIT2(0xEC89); INSTR("mov esp,ebp"); }
#define MOV_ESI_EBP   { EMIT2(0xEE89); INSTR("mov esi,ebp"); }
#define MOV_EAX_MEM_ESP   { EMIT3(0x048B,0x24); INSTR("mov eax,[esp]"); }
#define MOV_EAX_MEM_EBP (   ofs)    { EMIT3(0x458B,ofs); INSTR("mov eax,[ebp%p]",ofs); }
#define MOV_EAX_MEM_EAX (   ofs)    { EMIT3(0x408B,ofs); INSTR("mov eax,[eax%p]",ofs); }
#define MOV_EAX_MEM_OFS (   ofs)    { EMIT6(0x058B,ofs); INSTR("mov eax,[%d]",ofs); }
#define MOV_EBP_MEM_OFS (   ofs)    { EMIT6(0x2D8B,ofs); INSTR("mov ebp,[%d]",ofs); }
#define MOV_EBX_MEM_OFS (   ofs)    { EMIT6(0x1D8B,ofs); INSTR("mov ebp,[%d]",ofs); }
#define MOV_MEM_EBP_EAX (   ofs)    { EMIT6(0x8589,ofs); INSTR("mov [ebp%p],eax",ofs); }
#define MOV_MEM_ESI_EAX (   ofs)    { EMIT6(0x8689,ofs); INSTR("mov [esi%p],eax",ofs); }
#define MOV_MEM_OFS_EAX (   ofs)    { EMIT6(0x0589,ofs); INSTR("mov [%d],eax",ofs); }
#define MOV_MEM_OFS_EBP (   ofs)    { EMIT6(0x2D89,ofs); INSTR("mov [%d],ebp",ofs); }
#define MOV_MEM_OFS_CONST (   ofs,
 
)
Value:
{EMIT6(0x05C7,ofs); EMIT4(n); \
              INSTR("mov   [%d],%a", ofs, n); }
#define MOV_ESI_SSMEM_ESI_4   { EMIT4(0xFC768B36); INSTR("mov esi,ss:[esi-4]"); }
#define MOV_ESI_SSMEM_EBP_4   { EMIT4(0xFC758B36); INSTR("mov esi,ss:[ebp-4]"); }
#define INC_EAX_MEM (   n)    { EMIT3(0x40FF,n); INSTR("inc [eax%p]",n); }
#define DEC_EAX_MEM (   n)    { EMIT3(0x48FF,n); INSTR("dec [eax%p]",n); }
#define RET   { EMIT1(0xC3); INSTR("ret"); }
#define ADD_EAX_CONST_4 (   n)    { EMIT5(0x05,n); INSTR("add eax,%d",n); }
#define ADD_ESI_CONST_4 (   n)    { EMIT6(0xC681,n); INSTR("add esi,%d",n); }
#define CMP_EBX_CONST (   n)    { EMIT3(0xFB83,n); INSTR("cmp ebx,%d",n); }
#define CMP_EAX_CONST (   n)    { EMIT5(0x3D,n); INSTR("cmp eax,%d",n); }
#define CMP_EAX_ATOM (   a)    { EMIT5(0x3D,a); INSTR("cmp eax,%l",a); }
#define CMP_ID_ERROR
Value:
{ EMIT2(0x7880); EMIT1(OFFSET_ID); EMIT1(ERROR_ID);\
              INSTR("cmp   [eax+ID],ERROR_ID"); }
#define CMP_MEM_EBP (   ofs,
  val 
)
Value:
{ EMIT2(0x7D80); EMIT1(ofs); EMIT1(val);\
              INSTR("cmp   [eax%p],%d",ofs,val); }
#define CMP_MEM_OFS_CONST_4 (   ofs,
 
)    { EMIT6(0x3D81,ofs); EMIT4(n); INSTR("cmp [%d],%a", ofs, n); }
#define CMP_MEM_OFS_CONST_1 (   ofs,
 
)
Value:
{ EMIT6(0x3D80,ofs); EMIT1(n); \
                 INSTR("cmp   [%d],%d", ofs, n); }
#define JE_EXIT   { EMIT6(0x840F,REL(EXIT)); INSTR("je exit"); }
#define JE (   addr,
  name 
)    { EMIT6(0x840F,REL(addr)); INSTR("je %s",name); }
#define JNE (   addr,
  name 
)    { EMIT6(0x850F,REL(addr)); INSTR("jne %s",name); }
#define JNZ (   addr,
  name 
)    { EMIT6(0x850F,REL(addr)); INSTR("jnz %s",name); }
#define JMP_EXIT   { EMIT5(0xE9,REL(EXIT)); INSTR("jmp exit"); }
#define JMP (   addr,
  name 
)    { EMIT5(0xE9,REL(addr)); INSTR("jmp %s",name); }
#define JMP_ATOM (   addr,
  name 
)    { EMIT5(0xE9,REL(addr)); INSTR("jmp %a",name); }
#define JMP_EAX (   name)    { EMIT2(0xE0FF); INSTR("jmp %a",name); }
#define CALL (   addr,
  name 
)    { EMIT5(0xE8,REL(addr)); INSTR("call %s",name); }
#define CALL_ATOM (   addr,
  name 
)    { EMIT5(0xE8,REL(addr)); INSTR("call %a",name); }
#define CALL_MEM (   addr,
  name 
)    { EMIT6(0x15FF,addr); INSTR("call [%a]",name); }
#define CALL_MEM_EAX (   addr,
  name 
)    { EMIT6(0x90FF,addr); INSTR("call [eax+%s]",name); }
#define FSTPF_MEM_ESP   { EMIT2(0x1CD9); EMIT1(0x24); INSTR("fstpf [esp]"); }
#define FSTPD_MEM_ESP   { EMIT2(0x1CDD); EMIT1(0x24); INSTR("fstpd [esp]"); }
#define INT_3   { EMIT1(0xCC); INSTR("int 3"); }
#define NOP   { EMIT1(0x90); INSTR("nop"); }

Function Documentation

uint_t relative ( context_t ctx,
int  addr 
)
Parameters:
ctxcompilation context
addrtarget address
Returns:
calculated offset

Calculates a relative offset, which is commonly used in JPM and CALL instructions. The parameter ADDR is absolute target address. The result is the same target, but relative to the current IP position.

{
  if( ctx->generate )
    {
      int next_instruction = IP + 4;
      return (uint_t)(addr-next_instruction);
    }
 else
   return 0;
}
void asm_label ( context_t ctx,
chars_t  label 
)
Parameters:
ctxcontext
labellabel

Dumps disassembler's label followed by semicolon

{
  #ifdef ADVANCED
  disasm( ctx, 4, label );
#endif //ADVANCED
}
void asm_prologue ( context_t ctx,
atom_t  func 
)
Parameters:
ctxcompilation context
funcfunction which prologue to generate

Generates the prologue of a function. This is an initializating sequences of machine instructions needed to setup stack, system data and local vars.

Generated code:

;; initialize stack push ebp ; caller's frame [EBP+0] mov ebp,esp ; EBP=current frame push esi ; parent's frame [EBP-4] push func ; parent atom [EBP-8]

;; initialize system data push repeat_chain ; for root; unbound for non-root call use push 2 ; result of IFTRUE [EBP-16] mov eax,unbound ; default result of the function

;; initialize local variables push empty_list ; runtime locals [EBP-20] push eax ; once for each local variable

{
  atom_t a;

  INFO( "" );
  INFO( "=========================" );
  INFO( " FUNCTION %a", NAME(func) );
  INFO( "=========================" );
  INFO( "" );

  LABEL( "%a:", NAME(func) );

  PUSH_EBP; REM( "function prologue" );
  MOV_EBP_ESP;
  PUSH_ESI;
  PUSH_ATOM( func ); REM( "parent atom" );

  INFO( "" );
  INFO( "initialize system data" );
  if( func==root )
    {
      MOV_MEM_OFS_EBP( &root_frame ); REM( "store root frame" );
      PUSH_ATOMSTR( repeat_chain, TEXT("repeat_chain") );
      CALL( use, TEXT("use") );
    }
  else
    {
      PUSH_ATOM( unbound );
    }

  PUSH_CONST(2); REM( "IFTRUE status" );
  MOV_EAX_ATOM(unbound); REM( "default result of %a", NAME(func) );

  INFO( "" );
  INFO( "initialize local variables" );
  PUSH_ATOM( empty_list ); REM( "runtime locals [EBP-16]" );

  int ofs = BASE_OFFSET_LOCALS-4;
  for( a=LOCALS(func); IS_NOT_EMPTY(a); a=CDR(a) )
    if( IS_VARIABLE(CAR(a))&& IS_NORMAL(CAR(a)) )
      {
   // print the name and the address of the local var
   PUSH_EAX; REM( "local %a [EBP%p]", NAME(CAR(a)), ofs );
   ofs-=4;
      }
}
{
  MOV_EBX_CONST(0);
}
void asm_epilogue ( context_t ctx,
atom_t  func,
int  is_macro 
)
Parameters:
ctxcompilation context
funcfunction which epilogue to generate
is_macroif set then local variables must be copies

Generates the epilogue of a function. This is a finalizating sequences of machine instructions needed to recover stack and local variables.

Generated code:

mov ebx,0 exit:

push ebp ; push current frame only if is_macro is set call copy_local_vars ; only if is_macro is set

;; free local variables call deuse ; once for each local call deuse ; for runtime locals

;; free system data pop edx ; free result of IFTRUE call deuse ; free repeat chain

;; recover frame pointers pop edx ; free parent atom pop esi mov esp,ebp pop ebp ret

{
 atom_t a;
 
  INFO( "" );
LABEL( "exit:" );

  if( is_macro==COMPILE_AS_MACRO )
  {
    //INT_3; NOP; NOP; NOP;
   PUSH_EAX;
   PUSH_EBX;
   PUSH_EBP; REM("push current frame");
   CALL( copy_local_vars, TEXT("copy_local_vars") );
   POP_EDX;
   POP_EBX;
   POP_EAX;
   //NOP; NOP; NOP;
  }

  INFO( "free local variables" );
  for( a=LOCALS(func); IS_NOT_EMPTY(a); a=CDR(a) )
    if( IS_VARIABLE(CAR(a)) && IS_NORMAL(CAR(a)) )
      CALL( deuse, TEXT("deuse") );
  CALL( deuse, TEXT("deuse") ); REM("also free dynamic variables");

  INFO( "" );
  INFO( "free system data" );
  POP_EDX;        REM( "free result of IFTRUE" );
  CALL( deuse, TEXT("deuse") ); REM( "free repeat chain" );

  INFO( "" );
  INFO( "recover frame pointers" );
  POP_EDX;        REM( "delete link to parent" );
  POP_ESI;
  MOV_ESP_EBP;
  POP_EBP;
  RET;            REM( "end of %a", NAME(func) );
}
void asm_push_atom ( context_t ctx,
atom_t  atom 
)
Parameters:
ctxcompilation context
atomatom to push

Generates machine code which pushes atom in the stack and increases its reference count. The atom reference count is NOT increased if it is UNBOUND or EMPTY LIST.

Note:
Assumes the reference count is at the beginning of the atom - i.e. at offset 0.

Generated code:

push {atom} call {use} ; if not UNBOUND or EMPTY LIST

{
  PUSH_ATOM( atom );
  if( !IS_UNBOUND(atom) && !IS_EMPTY(atom) )
    {
      CALL( use, TEXT("use") );
    }
}
Parameters:
ctxcompilation context

Generates machine code which pops atom from the stack and deuses it.

Generated code: call {deuse}

{
  CALL( deuse, TEXT("deuse") );
}
Parameters:
ctxcompilation context

Generates machine code which pops data from the the stack and then ignores it.

Generated code: pop edx

{
  POP_EDX;
}
Parameters:
ctxcompilation context

Generates machine code which push the value returned by a function into the stack.

Generated code: push eax

{
  PUSH_EAX; REM( "move result in stack " );
}
void asm_result_proc ( context_t ctx,
atom_t  source 
)
Parameters:
ctxcompilation context
sourcesource of the function

Generates code which check whether the result of a command is acceptible. The source parameter points to the source where the procedure is called. It is used to locate the error position in case of errors.

{
  INFO( "" );
  INFO( "check for errors in proc %a", CAR(source) );
  PUSH_SOURCE( source );
  CALL( rt_cmdchk, TEXT("rt_cmdchk") ); REM( "check result, exit if error" );
  CMP_ID_ERROR;
  JE_EXIT;
  MOV_EBX_CONST( 0 );
}
void asm_result_unknown ( context_t ctx,
atom_t  source 
)
Parameters:
ctxcompilation context
sourcesource of the function

Generates code which check whether the result of an expression is acceptible. Actually, any result is acceptible.

{
  INFO( "" );
  INFO( "check for errors in %a", CAR(source) );
  PUSH_SOURCE( source );
  CALL( rt_exprchk, TEXT("rt_exprchk") ); REM( "check result" );
}
void asm_result_func ( context_t ctx,
atom_t  source 
)
Parameters:
ctxcompilation context
sourcesource of the function

Generates code which check whether the result of a function is acceptible. The source parameter points to the source where the function is called. It is used to locate the error position in case of errors.

{
  INFO( "" );
  INFO( "check for errors in func %a", CAR(source) );
  PUSH_SOURCE( source ); REM( "source" );
  CALL( rt_funchk, TEXT("rt_funchk") );  REM( "check result" );
  asm_push_result( ctx );
}
int asm_static_link ( context_t ctx,
atom_t  var 
)
Parameters:
ctxcompilation context
varvar to which link is sought
Returns:
offset of the variable

Generates code with store the stack frame address of a variable in ESI regiser. The result of the function is the offset of the variable (if it is normal variable) or its address (if it is function or command).

Generated code:

mov esi,ebp mov esi,ss:[esi-4] ; repeated depth times

{
  int depth = LEVEL(ctx->parent)-LEVEL(var)+1;

  #ifdef SAFEMODE
    assert(depth>=0);
  #endif

  MOV_ESI_EBP; REM( "ESI=stack frame of %a", NAME(var) );
  for( ; depth; depth-- ) MOV_ESI_SSMEM_ESI_4;

  return OFFSET( var );
}
Parameters:
ctxcompilation context
namevariable's name

Compiles a reference to a system variable. It is assumed that the variable exists.

{
  atom_t var = find_var( name, ctx->parent );

  #ifdef SAFEMODE
    assert( var );
    assert( IS_VARIABLE( var ) );
  #endif //SAFEMODE

  asm_reference( ctx, var, unbound, 0 );
  CALL( use, TEXT("use") );
}
void asm_reference ( context_t ctx,
atom_t  var,
atom_t  source,
int  check 
)
Parameters:
ctxcompilation context
varvariable which value is referenced
sourcesource of var reference
checkif 0 then do not check result

Generates code for referencing variable's value. The generated code depends on depth - this is the difference between depths of syntax levels of the current context and the context where the var's value exist.

If the variable is primitive then it is stored in globals and is accessed directly -- i.e. it's value is not stored in the stack.

If check==0 push the value to the stack and exit. Otherwise generate code to check whether the value is not UNBOUND (i.e. to check whether the variable has been assigned a value)

(a) var is primitive push [{address}]

(b) user-defined var {static link} push [esi+offset] ; push value

if check!=0 then also generate:

push {source} call asm_use_var ; use new value cmp [eax+OFFSET_ID],ERROR_ID je exit push eax

{
  if( IS_PRIMITIVE(var) )
    { // case (a) - primitive variable
      int addr = (int)&VALUE(var);
      PUSH_MEM( addr );
    }
  else
    { // case (b) - user-defined variable
      int offset = asm_static_link( ctx, var );
      PUSH_MEM_ESI( offset );
    }
  REM( "value of %a", NAME(var) );

  // generate code to check whether
  // the variable has a value
  if( check )
    {
      PUSH_SOURCE( source ); REM( "source" );
      CALL( rt_use_var, TEXT("rt_use_var") );
      //CMP_ID_ERROR;
      //JE_EXIT;
      PUSH_EAX; REM( "still value of %a", NAME(var) );
    }
}
void asm_make_direct ( context_t ctx,
atom_t  var,
atom_t  source 
)

For all user-defined variables the value of depth determines what code is generated. This variable is the difference between the context where the var is defined and the context where the var is used. The value of delpth is used to generate code to reach the proper context.

Code generation patterns (assumes the new value is already pushed onto the stack):

(a) primitive (system) variables push [{address}] ; push old value call deuse ; deuse and pop old value mov eax,[esp] ; get new value mov [{address}],eax ; store new value

(b) user-defined var {static} ; calculate base pointer in ESI push [esi+offset] ; push old value call deuse ; deuse and pop old value mov eax,[esp] ; get new value mov [esi+offset],eax ; store new value

{
  if( IS_PRIMITIVE(var) || IS_GLOBAL(var) )
    { // case (a)
      int addr = (int)&VALUE(var);

      // NB! No need to override default segment,
      // because it is already SS
      PUSH_MEM( addr ); REM( "free old value of %a", NAME(var) );
      CALL( deuse, TEXT("deuse") );
      MOV_EAX_MEM_ESP;  REM( "set new value of %a", NAME(var) );
      MOV_MEM_OFS_EAX( addr );
    }
  else
    {
      INFO( "" );
      INFO( "assign new value to %a", NAME(var) );
      int offset = asm_static_link( ctx, var );
      PUSH_MEM_ESI( offset ); REM( "free old value of %a", NAME(var) );
      CALL( deuse, TEXT("deuse") );
      MOV_EAX_MEM_ESP; REM( "set new value of %a", NAME(var) );
      MOV_MEM_ESI_EAX( offset );
    }

  // Generates code which check whether the result
  // of a make is acceptible.  The \c source parameter
  // points to the source where the procedure is called.
  // It is used to locate the error position in case
  // of errors.

  INFO( "" );
  INFO( "check for errors of %a", CAR(source) );
  PUSH_SOURCE( source );
  CALL( rt_makechk, TEXT("rt_makechk") ); REM( "check result atom" );
  CMP_ID_ERROR; REM( "is error atom?" );
  JE_EXIT;
}
void asm_push_value_addr ( context_t ctx,
atom_t  var 
)
Parameters:
ctxcompilation context
varvariable which value is looked for

Generates code that calculates and pushes in the the stack the address of the value of a variable known at compile time.

(a) primitive (system) variables push {address} ; push value's address

(b) user-defined var {static} ; calculate base pointer in ESI add esi,offset ; adjust for offset push esi ; push value's address

{
  if( IS_PRIMITIVE(var) )
    { // case (a)
      int addr = (int)&VALUE(var);

      PUSH_CONST( addr ); REM( "address of %a's value", NAME(var) );
    }
  else
    {
      int offset = asm_static_link( ctx, var );
      ADD_ESI_CONST_4( offset ); REM( "address of %a's value", NAME(var) );
      PUSH_ESI;
    }
}
void asm_call_atom ( context_t ctx,
atom_t  var,
int  params 
)
Parameters:
ctxcompilation context
varatom to call
paramsnumber of actual inputs

Generates machine code which calls a logo function. The input atom contains the name of the function.

Generated code (for functions with fixed number of inputs):

  • call {function}

Generated code (for functions with variable number of inputs or not-primitive):

  • push {param count}
  • call {function}
  • pop edx
{
  int variadic = GET_FLAGS(var,FLAG_INFINITE_ARGS|FLAG_CAN_BE_UNARY|FLAG_MAY_HAVE_EXTRA_ARG);

  // all variadic functions need one more parameter
  if( variadic || !GET_FLAGS(var,FLAG_PRIMITIVE) )
    {
      PUSH_CONST( params ); REM( "param count of %a", NAME(var) );
    }


  // test for special system-defined variables-inputs
  // first test for print-related system inputs
  if( GET_FLAGS(var,FLAG_PRINT_VARS) )
    {
      compile_system_reference( ctx, NAME(fullprintp) ); 
      compile_system_reference( ctx, NAME(printwidthlimit) );
      compile_system_reference( ctx, NAME(printdepthlimit) );
    }
  if( GET_FLAGS(var,FLAG_EQUAL_VARS) )
    {
      compile_system_reference( ctx, NAME(caseignoredp) );
    }

  // test for run-time access to parent
  if( GET_FLAGS(var,FLAG_PUSH_PARENT) )
    {
      PUSH_CONST( (int)ctx->parent ); REM( "push parent of %a", NAME(var) );
      PUSH_EBP; REM( "push current frame" );
    }

  if( IS_PRIMITIVE(var) )
    {
      if( !ADDRESS(var) )
   {
     printf("This primitive is not defined yet -> ");
     dumpln(NAME(var));
     return;
   }
      CALL_ATOM( (void*)ADDRESS(var), NAME(var) );
    }
  else
    {
      // prepare static link
      asm_static_link( ctx, var );
      CALL_MEM( &ADDRESS(var), NAME(var) );
    }

  // postprocessing of run-time access to parent
  if( GET_FLAGS(var,FLAG_PUSH_PARENT) )
    {
      POP_EDX; REM("remove static link");
      POP_EDX; REM("remove parent");
    }

  // postprocessing of system inputs
  if( GET_FLAGS(var,FLAG_EQUAL_VARS) )
    {
      asm_pop_atom( ctx ); REM( "remove %a", NAME(caseignoredp) );
    }
  if( GET_FLAGS(var,FLAG_PRINT_VARS) )
    {
      asm_pop_atom( ctx ); REM( "remove %a", NAME(printdepthlimit) );
      asm_pop_atom( ctx ); REM( "remove %a", NAME(printwidthlimit) );
      asm_pop_atom( ctx ); REM( "remove %a", NAME(fullprintp) );
    }

  // postprocessing of all variadic functions
  if( variadic || !GET_FLAGS(var,FLAG_PRIMITIVE) )
    {
      POP_EDX; REM("remove param count");
    }
}
Parameters:
ctxcompilation context

Generates machine code which finds the value of a variable and pushes it in the stack. The name is in the stack, i.e. it is not processed at compile time.

push {parent} push ebp call : pop edx pop edx

{
  // always push the parent
  PUSH_CONST( (int)ctx->parent ); REM( "push parent" );
  PUSH_EBP; REM( "push static link" );
  CALL( rt_reference, TEXT(":") );
  POP_EDX; REM( "remove static link" );
  POP_EDX; REM( "remove parent" );
}
void asm_dump_source ( context_t ctx,
atom_t  source 
)
Parameters:
ctxcompilation context
sourcesource to dump

Generates code which dumps the current source. This code is generally placed before calling user-defined function of command.

{
  INFO( "" );
  PUSH_SOURCE( source ); REM( "dump source" );
  CALL( rt_predump, TEXT("rt_predump") );
  INFO( "" );
}
Parameters:
ctxcompilation context

Generates the body of an empty function. This body just sets the result to be unbound atom.

Generated code:

  • push (unbound)
  • call use
  • pop eax
{
  INFO( "" );
  MOV_EAX_ATOM( unbound ); REM( "there is no result" );
}
void asm_output ( context_t ctx,
atom_t  source,
int  true_output 
)
Parameters:
ctxcompilation context
sourcesource of the OUTPUT command
true_outputflag whether OUPUT is real or simulated

Generates code for output statement. Before the execution of asm_output() the stack contains the new value to output.

Generated code:

mov ebx,true_output pop eax ; load value in EAX jmp exit

{
  INFO( "" );
  INFO( "output current result" );

  #ifdef ADVANCED
  if( OPTION_RUNTIME )
    {
      PUSH_SOURCE( source ); REM( "dump source" );
      CALL( rt_dump, TEXT("dump") );
    }
  #endif

  MOV_EBX_CONST( true_output );
  POP_EAX; REM( "get current result in EAX" );
  JMP_EXIT;
}
void asm_stop ( context_t ctx,
atom_t  source 
)
Parameters:
ctxcompilation context
sourcesource of the STOP command

Generates code for stop statement.

Generated code:

mov ebx,1 mov eax,{stopped} ; load value in EAX jmp exit

{
  INFO( "exit with no result" );

  #ifdef ADVANCED
  if( OPTION_RUNTIME )
    {
      PUSH_SOURCE( source ); REM( "dump source" );
      CALL( rt_dump, TEXT("rt_dump") );
    }
  #endif

  MOV_EBX_CONST( 1 );
  MOV_EAX_ATOM( stopped ); REM( "stopped" );
  JMP_EXIT;
}
void asm_boolean ( context_t ctx,
atom_t  source 
)
Parameters:
ctxcompilation context
sourcesource of the expression

Generates code that checks the top value in the stack is TRUE or FALSE. In case of TRUE sets EAX = false_true[1]. In case of FALSE sets EAX = false_true[0]. If the value is neither TRUE nor FALSE sets EAX to an error atom

Generated code:

call rt_boolchk cmp [eax+OFFSET_ID],ERROR_ID je exit

{
  INFO( "" );
  INFO( "check boolean result" );
  PUSH_SOURCE( source ); REM( "source" );
  CALL( rt_boolchk, TEXT("rt_boolchk") ); REM( "exit if not boolean" );
  CMP_ID_ERROR;
  JE_EXIT;
}
int asm_if_prologue ( context_t ctx)
Parameters:
ctxcompilation context
Returns:
branch address

Generates prologue code for the if command. Prologue is the code which stands right before the code for then. It is used to direct execution towards the then or the else parts.

It is assumed that the code for the condition is already ready and EAX contains the address of either false_true[0] or false_true[1].

The return value is set to the address of the else component of the je instruction. It is used to set the correct offset once the size of then-commands is known.

Generated code: cmp eax,FALSE je else

{
  CMP_EAX_ATOM( false_true[0] ); REM( "is it false?" );
  JE( 0, TEXT("$else") ); REM( "goto if-else block" );
  return LOCAL_IP-4;
}
int asm_if_epilogue ( context_t ctx)
Parameters:
ctxcompilation context
Returns:
branch address

Generates epilogue code for the if command. Epilogue is the code which stands right after the code for the then-instructions. It contains only a jump over the else-instructions directly to the end of the if command.

Generated code: jmp ifend

{
  JMP( 0, TEXT("ifend") ); REM( "skip if-else block" );
  return LOCAL_IP-4;
}
void asm_fix ( context_t ctx,
int  addr 
)
Parameters:
ctxcompilation context
addraddress of data to fix
Returns:
branch address

Fixes a relative offset from a given position to the current position as defined by ctx. This is used when there is a forward conditional or inconditional jump, which target is unknown at compile time.

The parameter addr is a 0-based address (i.e. it is an offset relative to START_IP).

{
  reemit_4( ctx, addr, ctx->size-addr-4 );
}
int asm_repeat_prologue_const ( context_t ctx,
int  cnt 
)
Parameters:
ctxcompilation context
cntnumber of repetitions
Returns:
branch address

Generates const-prologue code for the repeat command. Const-prologue is used when the number of repetitions is known at compile time (currently this means it is a constant).

The prologue initializes the repeat by calling rt_repeat_enter to set up a new element of the repeat chain.

The return value of the function is the address of the instruction right after the prologue. This address is used by asm_repeat_epilogue() and is absolute address.

Generated code:

push {count} push ebp call rt_repeat_enter

{
  PUSH_CONST( cnt ); REM( "repeat count" );
  PUSH_EBP; REM( "static link" );
  CALL( rt_repeat_enter, TEXT("rt_repeat_enter") ); REM( "initialize repeat" );

  INFO( "" );
  asm_label( ctx, TEXT("$rep:") );
  INFO( "start bofy of repeat" );

  return IP;
}
int asm_repeat_prologue_expr ( context_t ctx,
atom_t  source,
int *  branch2 
)
Parameters:
ctxcompilation context
sourcesource of the expression
branch2address of branch to skip over the whole repeat
Returns:
branch address

Generates expr-prologue code for the repeat command. Expr-prologue is used when the number of repetitions is an expression, i.e. it is not known at compile time.

The prologue generates code to calculate the actual number of repetitions. It checks whether there was an error during calculation. If not, it checks whether the number of repetitions is valid. Finally the prologue initializes the repeat by calling rt_repeat_enter to set up a new element of the repeat chain.

The return value of the function is the address of the instruction right after the prologue. This address is used by asm_repeat_epilogue() and is absolute address.

Generated code:

{expr} push {source} call rt_repchk cmp [eax+OFFSET_ID],ERROR_ID je exit

push eax mov [eax+REPLIMIT],eax call deuse

cmp eax,0 je skip_repeat ][ ; <-branch2

push eax push ebp call rt_repeat_enter $rep:

{
  INFO( "" );
  INFO( "check validity of repeat count" );
  PUSH_SOURCE( source ); REM( "source" );
  CALL( rt_repchk, TEXT("rt_repchk") ); REM( "exit if invalid" );
  CMP_ID_ERROR;
  JE_EXIT;

  PUSH_EAX; REM( "free repeat count expression" );
  MOV_EAX_MEM_EAX( OFFSET_INT ); REM( "get repeat count" );
  CALL( deuse, TEXT("deuse") );

  CMP_EAX_CONST( 0 ); REM( "exit if it is 0" );
  JE( 0, TEXT("skip_repeat") );
  *branch2 = LOCAL_IP-4;

  PUSH_EAX; REM( "repeat count" );
  PUSH_EBP; REM( "static link" );
  CALL( rt_repeat_enter, TEXT("rt_repeat_enter") ); REM( "initialize repeat" );

  asm_label( ctx, TEXT("$rep:") );

  return IP;
}
void asm_repeat_epilogue ( context_t ctx,
int  branch,
int  branch2 
)
Parameters:
ctxcompilation context
branchbranch for repeat's loop
branch2branch for skipping repeat

Generates epilogue code for the repeat command. There are two prologues for repeat, but the epilogue is the same for both of them.

The epilogue of repeat decrements the repeat count and if it is not zero, then loops back to the branch address, which is provided as parameter.

If the value of branch2 is -1, then the repeat with constant number of repetitions and branch2 is ignored. Otherwise it contains the address where a branch is made over the whole body of the repeat when the repetition number is 0.

Generated code: (note: {body} is not a part of the epilogue)

$rep: {body} mov eax,[ebp+REPEATCHAIN] mov eax,[eax+CAR_OFFSET] inc [eax+REPCOUNT] dec [eax+REPLIMIT] jnz $rep

$skip: push ebp call rt_repeat_exit

{

  INFO( "" );
  INFO( "calculate next loop of repeat" );
  MOV_EAX_MEM_EBP( BASE_OFFSET_REPEATCHAIN ); REM( "get repeat chain" );
  MOV_EAX_MEM_EAX( CAR_OFFSET ); REM( "get current repeat node" );
  INC_EAX_MEM( OFFSET_REPCOUNT ); REM( "increase done repeats" );
  DEC_EAX_MEM( OFFSET_REPLIMIT ); REM( "decrease left repeats" );

  //PUSH_MEM_EBP(BASE_OFFSET_REPEATCHAIN);
  //PUSH_MEM_EAX(OFFSET_REPCOUNT);
  //PUSH_MEM_EAX(OFFSET_REPLIMIT);
  //CALL( rt_repeat_debug, TEXT("") );

  JNZ( branch, TEXT("$rep") );

  if( branch2!=-1 )
    {
      INFO( "" );
      asm_label( ctx, TEXT("skip_repeat:") );
      INFO( "no more loops" );
      asm_fix( ctx, branch2 );
    }

  PUSH_EBP; REM( "static link" );
  CALL( rt_repeat_exit, TEXT("rt_repeat_exit") );
}
Parameters:
ctxcompilation context
Returns:
branch address

Generates prologue code for the forever command.

The prologue initializes the forever by calling rt_forever_enter to set up a new element of the repeat chain.

The return value of the function is the address of the instruction right after the prologue. This address is used by asm_forever_epilogue().

Generated code:

push ebp call rt_forever_enter forever:

{
  PUSH_EBP; REM("static link");
  CALL( rt_forever_enter, TEXT("rt_forever_enter") ); REM( "initialize forever" );

  INFO( "" );
  asm_label( ctx, TEXT("forever:") );
  INFO( "start bofy of forever" );

  return IP;
}
void asm_forever_epilogue ( context_t ctx,
int  branch 
)
Parameters:
ctxcompilation context
branchbranch for forever's loop

Generates epilogue code for the forever command.

Generated code: (note: {body} is not a part of the epilogue)

forever:: {body} mov eax,[ebp+REPEATCHAIN] mov eax,[eax+CAR_OFFSET] inc [eax+REPCOUNT] jmp forever

{
  INFO( "" );
  INFO( "calculate next loop of forever" );
  MOV_EAX_MEM_EBP( BASE_OFFSET_REPEATCHAIN ); REM( "get current repeat node" );
  MOV_EAX_MEM_EAX( CAR_OFFSET ); REM( "get current repeat node" );
  INC_EAX_MEM( OFFSET_REPCOUNT ); REM( "increase done repeats" );
  JMP( branch, TEXT("forever") );
}
Parameters:
ctxcompilation context

Generates code for pushing the frame pointer which is stored in EBP.

Generated code:

push ebp

{
  PUSH_EBP; REM("push current frame");
}
Parameters:
ctxcompilation context

Generates code for popping the frame pointer previously pushed by asm_push_frame(). The actual value that is popped out is not used for anything.

Generated code:

pop edx

{
  POP_EDX; REM( "remove pushed frame" );
}
void asm_push_mode ( context_t ctx,
int  mode 
)
Parameters:
ctxcompilation context
modemode to push

Generates code for pushing the mode of a function call. The mode indicates whether the function is called as a function or as a command. The values of mode should be either COMPILE_AS_FUNC or COMPILE_AS_PROC.

Generated code:

push {mode}

{
  PUSH_CONST( mode );
  if( mode==COMPILE_AS_FUNC )
    REM( "call as function" );
  else
    REM( "call as procedure" );
}
Parameters:
ctxcompilation context

Generates code which adjust the result of a command. This is needed when EAX register is left unchanged from the last expression.

Generated code:

mov eax,{unbound} ; load value in EAX

{
  MOV_EAX_ATOM( unbound ); REM( "set void result" );
}
Parameters:
ctxcompilation context

Generates code which adjust the result of a function. This is needed when the result is in the stack, but it is needed to be in EAX.

Generated code:

pop eax

{
  POP_EAX; REM( "get current result" );
}
int asm_while_prologue ( context_t ctx,
int  is_while,
int  is_do 
)
Parameters:
ctxcompilation context
is_whilewhile=1, until=0
is_dodo.while&do.until=1, while&until=0
Returns:
branch address

Generates prologue code for the while or the until command (depending on the value of is_while parameter). Actually no code is generated, as long as the the prologue is just a label.

Generated code (is_while==1 & is_do==0):

while:

Generated code (is_while==0 & is_do==0):

until:

Generated code (is_while==1 & is_do==1):

do.while:

Generated code (is_while==0 & is_do==1):

do.until:

{
  if( is_do )
      {
          if( is_while )
              asm_label( ctx, TEXT("do.while:") );
          else
              asm_label( ctx, TEXT("do.until:") );
      }
      else
      {
          if( is_while )
              asm_label( ctx, TEXT("while:") );
          else
              asm_label( ctx, TEXT("until:") );
      }
  return IP;
}
int asm_while_inlogue ( context_t ctx,
atom_t  source,
int  is_while 
)

push eax mov eax,[eax+INT] call deuse cmp eax,0 (for while) or 1 (for until) je skip_while

{
  INFO( "" );
  INFO( "test condition of while/until" );
  PUSH_SOURCE( source ); REM( "source" );
  CALL( rt_whlchk, TEXT("rt_whlchk") ); REM( "check condition" );
  CMP_ID_ERROR; REM( "exit if error" );
  JE_EXIT;

  PUSH_EAX; REM( "free condition atom" );
  MOV_EAX_MEM_EAX( OFFSET_INT ); REM( "get condition result" );
  CALL( deuse, TEXT("deuse") );

  CMP_EAX_CONST( 1-is_while ); REM( "exit if condition fails" );
  JE( 0, is_while?TEXT("skip_while"):TEXT("skip_until") );

  return LOCAL_IP-4;
}
void asm_while_epilogue ( context_t ctx,
int  loop_branch,
int  skip_branch,
int  is_while 
)
Parameters:
ctxcompilation context
loop_branchbranch for while/until's loop
skip_branchbranch to skip while/until
is_whilewhile=1, until=0

Generates epilogue code for the while or until command.

Generated code: (note: {body} is not a part of the epilogue)

$whl: ... {body} jmp $whl skip_while: mov eax,(unbound)

{
  INFO( "" );
  INFO( "finalization of while/until" );
  JMP( loop_branch, is_while?TEXT("while"):TEXT("until") );

  asm_label( ctx, is_while?TEXT("skip_while"):TEXT("$skip_until") );
  asm_fix( ctx, skip_branch );

  MOV_EAX_ATOM( unbound ); REM( "set no result" );
}
void asm_set_output_status ( context_t ctx,
int  n 
)
Parameters:
ctxcompilation context
noutput status

Generates code to set output/stop status in EBX

Generated code: mov ebx,n

{
  MOV_EBX_CONST( n );
}
int asm_catch_prologue ( context_t ctx)
Parameters:
ctxcompilation context
Returns:
catch exit address

Generates prologue code for the catch command. Prologue is the code which stands right before the code for the body of the catch. The prologue is a trampoline which temporarily overwrited the default function exit address. Thus, all exits done in the commands of the catch are directed to this trampoline.

Note! This function changes the exit_addr to point to jump_addr!

Generated code: jmp $1 jump_addr: jmp catch_epilogue $1: {body}

{
  JMP( IP+9, TEXT("catch") ); // skip over next jump
  ctx->exit_addr = IP;
  JMP( 0, TEXT("exit_catch") );  // to be fixed later

  INFO( "" );
  asm_label( ctx, TEXT("catch:") );
  INFO( "start of catch block" );

  return LOCAL_IP-4;
}
Parameters:
ctxcompilation context

Generates epilogue code for the catch command. It fixes unset branch in the prologue and generates code which captures and verifies the result of the monitored block of commands.

Generated code: push {tag} ; already done push {eax} ; already done call rt_catchchk mov ebx,[catch_output_flag] cmp ebx,1 je exit push eax

{
  INFO( "" );
  INFO( "check result of catch block" );

  PUSH_EBX;
  CALL( rt_catchchk, TEXT("rt_catchchk") );
  MOV_EBX_MEM_OFS( &catch_output_flag );
  CMP_EBX_CONST(1);
  JE_EXIT;

//__asm__ volatile ("int $3; nop");

  PUSH_EAX;
}
void asm_goto ( context_t ctx,
atom_t  tag 
)
Parameters:
ctxcompilation context
tagtag for the target address

Generates unconditional jump to a given address specified by a tag variable.

Generated code: jmp addr

{
  if( ctx->generate )
    {
      #ifdef SAFEMODE
        assert( IS_INTEGER(VALUE(tag)) );
      #endif

      JMP_ATOM( START_IP+INTEGER(VALUE(tag)), NAME(tag) );
    }
  else
    {
      JMP( 0, TEXT("") );
    }
}
void asm_goto_prologue ( context_t ctx,
atom_t  source 
)
Parameters:
ctxcompilation context
sourcesource of the function

Generates code to push the source. It will be used by rt_goto().

Generated code:

push {source}

{
  PUSH_SOURCE( source ); REM( "source" );
}
void asm_goto_epilogue ( context_t ctx,
atom_t  source 
)
Parameters:
ctxcompilation context
sourcesource of goto command

Generates epilogue for goto command which input cannot be resolve at compile time.

The epilogue generates code to find the target address of the goto command. The code assumes that EAX contains an integer atom with the target address.

Generated code:

{expr} ; already generated cmp [eax+OFFSET_ID],ERROR_ID je exit

push eax ; tag mov eax,[eax+INTEGER] ; EAX=target call deuse ; tag

add eax,{base} jmp eax

{
  CMP_ID_ERROR; REM( "exit if error" );
  JE_EXIT;

  PUSH_EAX; REM( "get target relative address" );
  MOV_EAX_MEM_EAX( OFFSET_INT );
  CALL( deuse, TEXT("deuse") );

  ADD_EAX_CONST_4( START_IP ); REM( "make it absolute" );
  JMP_EAX( source );
}
int asm_test_prologue ( context_t ctx,
int  criteria 
)
Parameters:
ctxcompilation context
criteriaiftrue=1, iffalse=0
Returns:
address of branch to skip over iftrue/iffalse

Generates prologue code for the iftrue or the iffalse command (depending on the value of criteria parameter). Prologue is used to skip the body if the criyeria is not fulfilled.

The return value of the function is the address of the instruction where a fix-up is needed.

Generated code:

cmp [ebp+OFFSET_TEST],criteria jne skip

{
  CMP_MEM_EBP( BASE_OFFSET_TEST, criteria ); REM( "check test result" );
  JNE( 0, TEXT("skip") );

  return LOCAL_IP-4;
}
Parameters:
ctxcompilation context

Generates epilogue for runmacro command which does the actual execution of commands.

The epilogue generates code to get the address of compiled code and execute it.

Generated code:

{run} ; already generated cmp [eax+OFFSET_ID],ERROR_ID je exit

push eax ; EAX=var atom mov eax,[eax+DESCR1] ; get DESCR1 which contains the address call [eax+ADDRESS] ; code to run, EAX=result of run code call deuse ; deuse var atom (including run code)

cmp ebx,1 je exit

cmp eax,{stopped} je exit

{
  INFO( "" );
  INFO( "actual execution of run" );

  CMP_ID_ERROR; REM( "exit if error" );
  JE_EXIT;

  MOV_ESI_EBP; REM( "static link" );
  PUSH_EAX; REM( "EAX=var atom" );
  MOV_EAX_MEM_EAX( OFFSET_DESCR1 ); REM( "EAX=var descriptor" );

  CALL_MEM_EAX( OFFSET_ADDRESS, TEXT("ADDRESS") ); REM( "get and call var address" );

  CALL( deuse, TEXT("deuse") );

  CMP_EBX_CONST( 1 );
  JE_EXIT;

  CMP_EAX_ATOM(stopped); REM( "exit if stopped" );
  JE_EXIT;
}
Parameters:
ctxcompilation context

Generates epilogue for runresult command which does the actual execution of commands.

The epilogue generates code to get the address of compiled code and execute it. It reuses code generated by asm_run_epilogue().

Generated code:

{run} ; already generated cmp [eax+OFFSET_ID],ERROR_ID je skip

push eax ; EAX=var atom mov eax,[eax+DESCR1] ; get DESCR1 which contains the address call [eax+ADDRESS] ; code to run, EAX=result of run code call deuse ; deuse var atom (including run code) skip: push eax call rt_runresult_fix

{
  INFO( "" );
  INFO( "actual execution of runresult" );

  CMP_ID_ERROR; REM( "skip if error" );
  JE( 0, TEXT("$skip") );
  int branch=LOCAL_IP-4;

  MOV_ESI_EBP; REM( "static link" );
  PUSH_EAX; REM( "EAX=var atom" );
  MOV_EAX_MEM_EAX( OFFSET_DESCR1 ); REM( "EAX=var descriptor" );

  CALL_MEM_EAX( OFFSET_ADDRESS, TEXT("ADDRESS") ); REM( "get and call var address" );

  CALL( deuse, TEXT("deuse") );
  
LABEL( "$skip:" );
  asm_fix( ctx, branch );
  
  PUSH_EAX;
  CALL( rt_runresult_fix, TEXT("rt_runresult_fix") );
}
int asm_for_prologue ( context_t ctx,
atom_t  step_var,
atom_t  source,
int *  branch2 
)
Parameters:
ctxcompilation context
step_varvariable which will hold the value of FOR's step
sourcesource of the expression
branch2address of branch to skip over the whole repeat
Returns:
branch address

Generates prologue code for the for command.

The prologue generates code to calculate the actual number of repetitions. It checks whether there was an error during calculation. If not, it checks whether the number of repetitions is valid. Finally the prologue initializes the for by calling rt_repeat_enter to set up a new element of the repeat chain.

The return value of the function is the address of the instruction right after the prologue. This address is used by asm_for_epilogue() and is absolute address.

Generated code:

{expr_from} {expr_to} {expr_step} push {addr of step_var's value} push {source} call rt_forchk cmp [eax+OFFSET_ID],ERROR_ID je exit

push eax mov [eax+REPLIMIT],eax call deuse

cmp eax,0 je skip_for ][ ; <-branch2

push eax push ebp call rt_repeat_enter $for:

{
    
  asm_push_value_addr( ctx, step_var );
  
  INFO( "" );
  INFO( "check validity of for count" );
  PUSH_SOURCE( source ); REM( "source" );
  CALL( rt_forchk, TEXT("rt_forchk") ); REM( "exit if invalid" );
  CMP_ID_ERROR;
  JE_EXIT;

  PUSH_EAX; REM( "free for count expression" );
  MOV_EAX_MEM_EAX( OFFSET_INT ); REM( "get for count" );
  CALL( deuse, TEXT("deuse") );

  CMP_EAX_CONST( 0 ); REM( "exit if it is 0" );
  JE( 0, TEXT("skip_for") );
  *branch2 = LOCAL_IP-4;

  PUSH_EAX; REM( "for count" );
  PUSH_EBP; REM( "static link" );
  CALL( rt_repeat_enter, TEXT("rt_repeat_enter") ); REM( "initialize for" ); // reuse rt_repeat_enter

  asm_label( ctx, TEXT("$for:") );

  return IP;
}
void asm_for_epilogue ( context_t ctx,
int  branch,
int  branch2 
)
Parameters:
ctxcompilation context
branchbranch for for's loop
branch2branch for skipping for

Generates epilogue code for the for command.

The epilogue of for decrements the repeat count and if it is not zero, then loops back to the branch address, which is provided as parameter.

branch2 contains the address where a branch is made over the whole body of the for when the repetition number is 0.

Generated code: (note: {body} is not a part of the epilogue)

$for: {body} mov eax,[ebp+REPEATCHAIN] mov eax,[eax+CAR_OFFSET] inc [eax+REPCOUNT] dec [eax+REPLIMIT] jnz $for

$skip: push ebp call rt_repeat_exit // reuses repeat_exit

{

  INFO( "" );
  INFO( "calculate next loop of for" );
  MOV_EAX_MEM_EBP( BASE_OFFSET_REPEATCHAIN ); REM( "get repeat chain" );
  MOV_EAX_MEM_EAX( CAR_OFFSET ); REM( "get current repeat node" );
  INC_EAX_MEM( OFFSET_REPCOUNT ); REM( "increase done repeats" );
  DEC_EAX_MEM( OFFSET_REPLIMIT ); REM( "decrease left repeats" );

  //PUSH_MEM_EBP(BASE_OFFSET_REPEATCHAIN);
  //PUSH_MEM_EAX(OFFSET_REPCOUNT);
  //PUSH_MEM_EAX(OFFSET_REPLIMIT);
  //CALL( rt_repeat_debug, TEXT("") );

  JNZ( branch, TEXT("$for") );

  if( branch2!=-1 )
    {
      INFO( "" );
      asm_label( ctx, TEXT("skip_for:") );
      INFO( "no more loops" );
      asm_fix( ctx, branch2 );
    }

  PUSH_EBP; REM( "static link" );
  CALL( rt_repeat_exit, TEXT("rt_repeat_exit") ); // same as exit from repeat
}
void asm_external_function ( context_t ctx,
atom_t  func 
)
Parameters:
ctxcompilation context
funcfunction which trampoline to generate

Generates a trampoline to call an external function. The trampoline's code converts Logo parameters into C-type data and prepares the stack to be as expected by a sdtcall function.

Generated code:

func: push ebp mov ebp,esp

; clear error flag mov [error_flag],0

; for each parameter do: push [ebp+{ofs}] ; push param call atom_to_xxx ; convert to c-type push edx ; push c-type hi word (if 64-bit) push eax ; push c-type lo word fstp [esp] ; store 32- or 64-bit fp number to stack

call func

; check error flag cmp [error_flag],0 je ok mov eax,[error_flag] jmp exit

; push result ok: push edx ; push c-type hi word (if 64-bit) push eax ; push c-type lo word fstp [esp] ; store 32- or 64-bit fp number to stack call xxx-to_atom ; convert back to atom

exit: mov esp,ebp pop ebp ret

{
  //INT_3;
  INFO( "" );
  INFO( "=========================" );
  INFO( " EXTERNAL FUNCTION %a", NAME(func) );
  INFO( "=========================" );
  INFO( "" );

  LABEL( "%a:", NAME(func) );
  PUSH_EBP; REM( "function prologue" );
  MOV_EBP_ESP;

  INFO( "" );
  MOV_MEM_OFS_CONST( &error_flag, 0 );
  REM( "clear error flag" );
  INFO( "" );
  
  INFO( "convert parameters to C-types" );

  atom_t params;
  for( params=LOCALS(func); IS_NOT_EMPTY(params); params=CDR(params) )
  {
    atom_t param = CAR(params);
    if( !IS_EXTERNAL(param) ) continue;

    int c_type = VARCLASS( param );

    // push param's atom and convert it to c-type
    PUSH_MEM_EBP( OFFSET(param) ); REM( "convert %a", NAME(param) );
    CALL( c_types[c_type].atom_to_c, c_types[c_type].atom_to_c_name );

    // push the result to the stack. If the result is 1..32 bits, then
    // it is in EAX. If it is 33..64 bits, then it is stored in EDX:EAX.
    if( c_types[c_type].size>32 ) { PUSH_EDX; }
    PUSH_EAX;
    
    // floating-point numbers are stored in FPU's st0 register.
    // Pop the value from the register and store it in the stack.
    if( c_types[c_type].class==C_TYPE_FLOAT )
      {
   if( c_types[c_type].size>32 )
     { FSTPD_MEM_ESP; } // store 64-bit float
   else
     { FSTPF_MEM_ESP; } // store 32-bit float
      }
  } //params=LOCALS(func)
 
  INFO( "" );
  INFO( "call to external function" );
  CALL_ATOM( ADDRESS(func), NAME(func) );

  INFO( "" );
  INFO( "check error flag" );

  CMP_MEM_OFS_CONST_4( &error_flag, 0 );
  JE( 0, TEXT("$ok") ); REM( "continue if no error" );
  int branch=LOCAL_IP-4;
  MOV_EAX_MEM_OFS( &error_flag );
  JMP( 0, TEXT("exit") );
  int branch2=LOCAL_IP-4;

  // function result must be converted back into atom
  INFO( "" );
  INFO( "convert function result" );
LABEL( "$ok:" );
  asm_fix( ctx, branch );
  int c_type = VARCLASS( func );
  if( c_types[c_type].size>32 ) { PUSH_EDX; }
  PUSH_EAX;
  if( c_types[c_type].class==C_TYPE_FLOAT )
    {
      if( c_types[c_type].size>32 )
        { FSTPD_MEM_ESP; } // store 64-bit float
      else
        { FSTPF_MEM_ESP; } // store 32-bit float
    }
  CALL( c_types[c_type].c_to_atom, c_types[c_type].c_to_atom_name );
  INFO( "" );

  // fix the stack by removing all c-type data
  // (actually this is not needed, because the
  // next MOV ESP,EBP command fixes the stack
LABEL( "exit:" );
  asm_fix( ctx, branch2 );
  MOV_ESP_EBP;
  POP_EBP;
  RET;   REM( "end of %a", NAME(func) );
}
void asm_internal_function ( context_t ctx,
int  static_link,
atom_t  func 
)
Parameters:
ctxcompilation context
static_linkstatic link from the current frame
funcfunction which trampoline to generate

Generates a trampoline to call an external function. The trampoline's code converts Logo parameters into C-type data and prepares the stack to be as expected by a sdtcall function.

Generated code:

func:

exit: ret

{
  //INT_3;
  INFO( "" );
  INFO( "=========================" );
  INFO( " INTERNAL FUNCTION %a", NAME(func) );
  INFO( "=========================" );
  INFO( "" );
  asm_prologue( ctx, func );

  INFO( "convert parameters to atoms" );
  atom_t params;
  int offset = 8; // this is offset from a C calling code
  int count = 0;
  for( params = LOCALS(func); IS_NOT_EMPTY(params); params=CDR(params))
  {
    atom_t param = CAR( params );
    if( !IS_INTERNAL(param) ) continue;
    int c_type = VARCLASS( param );

    PUSH_MEM_EBP( offset );
    CALL( c_types[c_type].c_to_atom, c_types[c_type].c_to_atom_name );
    PUSH_EAX;
    offset += c_types[c_type].size/8;
    count++;
  }

  PUSH_CONST( count );

  //MOV_EBP_CONST( static_link );
  //MOV_ESI_CONST( static_link );
  MOV_MEM_OFS_EBP( &backup_frame );
  MOV_EBP_MEM_OFS( &root_frame );
  MOV_ESI_EBP;
  INFO( "" );
  INFO( "call to internal function" );
  //INT_3;
  CALL_ATOM( ADDRESS(func), NAME(func) );
  MOV_EBP_MEM_OFS( &backup_frame );

  CMP_ID_ERROR;
  JNE( 0, TEXT("$skip") );
  int branch = LOCAL_IP-4;
  MOV_MEM_OFS_EAX( &error_flag ); REM( "store result in error flag");
  asm_fix( ctx, branch );
LABEL( "$skip" );

  INFO( "" );
  INFO( "convert function result" );
  int c_type = VARCLASS( func );
  PUSH_EAX;
  PUSH_EAX;
  CALL( c_types[c_type].atom_to_c, c_types[c_type].atom_to_c_name );
  INFO( "" );
  CALL( deuse, TEXT("deuse result") );

  POP_EDX;
  for( params=LOCALS(func); IS_NOT_EMPTY(params); params=CDR(params) )
    if( IS_INTERNAL(CAR(params)) )
      CALL( deuse, TEXT("deuse") );

  //INT_3;
  asm_epilogue( ctx, func, COMPILE_AS_NON_MACRO );
}
void asm_int_3 ( context_t ctx)
Parameters:
ctxcompilation context

Generates instruction for trapping debugger.

Generated code:

int 3

{
  INT_3;
}
void asm_nop ( context_t ctx)
Parameters:
ctxcompilation context

Generates instruction for no operation.

Generated code:

nop

{
  NOP;
}

[ HOME | INDEX | ATOMS | VARS | REFERENCE ]
Lhogho Developer's Documentation
Tue Feb 7 2012