(** This module provides an interpreter for the LIN language. *) let error_prefix = "LIN interpret" let error s = Error.global_error error_prefix s module Mem = Driver.LINMemory module Val = Mem.Value let chunk = Driver.LINMemory.int_size module Eval = I8051.Eval (Val) (* Memory *) type memory = LIN.function_def Mem.memory (* Hardware registers environments. They associate a value to each hardware register. *) type hdw_reg_env = Val.t I8051.RegisterMap.t (* Execution states. *) type state = { pc : Val.address ; isp : Val.address ; exit : Val.address ; carry : Val.t ; renv : hdw_reg_env ; mem : memory ; trace : CostLabel.t list } (* Helpers *) let change_pc st pc = { st with pc = pc } let change_isp st isp = { st with isp = isp } let change_exit st exit = { st with exit = exit } let change_carry st carry = { st with carry = carry } let change_renv st renv = { st with renv = renv } let change_mem st mem = { st with mem = mem } let change_trace st trace = { st with trace = trace } let add_trace st cost_lbl = change_trace st (cost_lbl :: st.trace) let empty_state = { pc = Val.null ; isp = Val.null ; exit = Val.null ; carry = Val.undef ; renv = I8051.RegisterMap.empty ; mem = Mem.empty ; trace = [] } let int_fun_of_ptr mem ptr = match Mem.find_fun_def mem ptr with | LIN.F_int def -> def | _ -> error "Trying to fetch the definition of an external function." let current_int_fun st = int_fun_of_ptr st.mem st.pc let fetch_stmt st = let msg = Printf.sprintf "%s does not point to a statement." (Val.string_of_address st.pc) in if Val.is_mem_address st.pc then let off = Val.offset_of_address st.pc in let def = int_fun_of_ptr st.mem st.pc in List.nth def (Val.Offset.to_int off) else error msg let init_fun_call st ptr = change_pc st (Val.change_address_offset ptr Val.Offset.zero) let next_pc st = change_pc st (Val.add_address st.pc Val.Offset.one) let add_reg r v st = let renv = I8051.RegisterMap.add r v st.renv in change_renv st renv let get_reg r st = if I8051.RegisterMap.mem r st.renv then I8051.RegisterMap.find r st.renv else error ("Unknown hardware register " ^ (I8051.print_register r) ^ ".") let push st v = let mem = Mem.store st.mem chunk st.isp v in let isp = Val.add_address st.isp (Val.Offset.of_int chunk) in change_mem (change_isp st isp) mem let pop st = let isp = Val.add_address st.isp (Val.Offset.of_int (-chunk)) in let st = change_isp st isp in let v = Mem.load st.mem chunk st.isp in (st, v) let save_ra st = let ra = Val.add_address st.pc Val.Offset.one in let st = push st (List.nth ra 0) in let st = push st (List.nth ra 1) in st let find_label lbl = let rec aux i = function | [] -> error (Printf.sprintf "Unknown label %s." lbl) | LIN.St_label lbl' :: _ when lbl' = lbl -> i | _ :: code -> aux (i+1) code in aux 0 let pointer_of_label st lbl = let code = current_int_fun st in let off = find_label lbl code in Val.change_address_offset st.pc (Val.Offset.of_int off) let goto st lbl = change_pc st (pointer_of_label st lbl) let return_pc st = let (st, pch) = pop st in let (st, pcl) = pop st in (st, [pcl ; pch]) let dptr st = List.map (fun r -> get_reg r st) [I8051.dpl ; I8051.dph] (* State pretty-printing *) let print_renv renv = let f r v = if not (Val.eq v Val.undef) then Printf.printf "\n%s = %s%!" (I8051.print_register r) (Val.to_string v) in I8051.RegisterMap.iter f renv let print_state st = Printf.printf "PC: %s\n%!" (Val.string_of_address st.pc) ; Printf.printf "SP: %s\n%!" (Val.string_of_address [get_reg I8051.spl st ; get_reg I8051.sph st]) ; Printf.printf "ISP: %s%!" (Val.string_of_address st.isp) ; print_renv st.renv ; Printf.printf "\nC = %s%!" (Val.to_string st.carry) ; Mem.print st.mem ; Printf.printf "\n%!" module InterpretExternal = Primitive.Interpret (Mem) let interpret_external mem f args = match InterpretExternal.t mem f args with | (mem', InterpretExternal.V vs) -> (mem', vs) | (mem', InterpretExternal.A addr) -> (mem', addr) let fetch_external_args f st = let size = Mem.size_of_quantity (Primitive.args_byte_size f) in let params = MiscPottier.prefix size I8051.parameters in List.map (fun r -> get_reg r st) params let set_result st vs = let f st (r, v) = add_reg r v st in List.fold_left f st (MiscPottier.combine I8051.rets vs) let interpret_external_call st f = let args = fetch_external_args f st in let (mem, vs) = interpret_external st.mem f args in let st = change_mem st mem in set_result st vs let interpret_call st ptr = match Mem.find_fun_def st.mem ptr with | LIN.F_int def -> let st = save_ra st in init_fun_call st ptr | LIN.F_ext def -> let st = next_pc st in interpret_external_call st def.AST.ef_tag let interpret_return st = let (st, pc) = return_pc st in change_pc st pc let interpret_stmt st stmt = match stmt with | LIN.St_goto lbl -> goto st lbl | LIN.St_label _ -> next_pc st | LIN.St_comment s -> (* Printf.printf "*** %s ***\n\n%!" s ; *) next_pc st | LIN.St_cost cost_lbl -> let st = add_trace st cost_lbl in next_pc st | LIN.St_int (r, i) -> let st = add_reg r (Val.of_int i) st in next_pc st | LIN.St_pop -> let (st, v) = pop st in let st = add_reg I8051.a v st in next_pc st | LIN.St_push -> let v = get_reg I8051.a st in let st = push st v in next_pc st | LIN.St_addr x -> let vs = Mem.find_global st.mem x in let st = add_reg I8051.dpl (List.nth vs 0) st in let st = add_reg I8051.dph (List.nth vs 1) st in next_pc st | LIN.St_from_acc destr -> let st = add_reg destr (get_reg I8051.a st) st in next_pc st | LIN.St_to_acc srcr -> let st = add_reg I8051.a (get_reg srcr st) st in next_pc st | LIN.St_opaccs opaccs -> let (a, b) = Eval.opaccs opaccs (get_reg I8051.a st) (get_reg I8051.b st) in let st = add_reg I8051.a a st in let st = add_reg I8051.b b st in next_pc st | LIN.St_op1 op1 -> let v = Eval.op1 op1 (get_reg I8051.a st) in let st = add_reg I8051.a v st in next_pc st | LIN.St_op2 (op2, srcr) -> let (v, carry) = Eval.op2 st.carry op2 (get_reg I8051.a st) (get_reg srcr st) in let st = change_carry st carry in let st = add_reg I8051.a v st in next_pc st | LIN.St_clear_carry -> let st = change_carry st Val.zero in next_pc st | LIN.St_set_carry -> let st = change_carry st (Val.of_int 1) in next_pc st | LIN.St_load -> let addr = dptr st in let v = Mem.load st.mem chunk addr in let st = add_reg I8051.a v st in next_pc st | LIN.St_store -> let addr = dptr st in let mem = Mem.store st.mem chunk addr (get_reg I8051.a st) in let st = change_mem st mem in next_pc st | LIN.St_call_id f -> interpret_call st (Mem.find_global st.mem f) | LIN.St_call_ptr -> interpret_call st (dptr st) | LIN.St_condacc lbl_true -> let v = get_reg I8051.a st in if Val.is_true v then goto st lbl_true else if Val.is_false v then next_pc st else error "Undecidable branchment." | LIN.St_return -> interpret_return st let compute_result st = let vs = List.map (fun r -> get_reg r st) I8051.rets in let f res v = res && (Val.is_int v) in let is_int vs = (List.length vs > 0) && (List.fold_left f true vs) in if is_int vs then let chunks = List.map (fun v -> IntValue.Int32.cast (Val.to_int_repr v)) vs in IntValue.Int32.merge chunks else IntValue.Int32.zero let rec iter_small_step debug st = let print_and_return_result (res, cost_labels) = if debug then Printf.printf "Result = %s\n%!" (IntValue.Int32.to_string res) ; (res, cost_labels) in if debug then print_state st ; match fetch_stmt st with | LIN.St_return when Val.eq_address (snd (return_pc st)) st.exit -> print_and_return_result (compute_result st, List.rev st.trace) | stmt -> let st' = interpret_stmt st stmt in iter_small_step debug st' let add_global_vars = List.fold_left (fun mem (id, size) -> Mem.add_var mem id (AST.SQ (AST.QInt size)) None) let add_fun_defs = List.fold_left (fun mem (f_id, f_def) -> Mem.add_fun_def mem f_id f_def) let init_prog (st : state) (p : LIN.program) : state = let mem = add_global_vars (add_fun_defs st.mem p.LIN.functs) p.LIN.vars in change_mem st mem let init_sp st = let (mem, sp) = Mem.alloc st.mem I8051.ext_ram_size in let sp = Val.change_address_offset sp (Val.Offset.of_int I8051.ext_ram_size) in let st = change_mem st mem in (st, sp) let init_isp st = let (mem, isp) = Mem.alloc st.mem I8051.int_ram_size in let st = change_mem (change_isp st isp) mem in let (mem, exit) = Mem.alloc st.mem 1 in let st = change_exit st exit in let st = push st (List.nth exit 0) in let st = push st (List.nth exit 1) in st let init_renv st sp = let f r st = add_reg r Val.undef st in let st = I8051.RegisterSet.fold f I8051.registers st in let spl = List.nth sp 0 in let sph = List.nth sp 1 in let st = add_reg I8051.spl spl st in let st = add_reg I8051.sph sph st in st let init_main_call st main = let ptr = Mem.find_global st.mem main in match Mem.find_fun_def st.mem ptr with | LIN.F_int def -> init_fun_call st ptr | _ -> error ("Cannot execute the main (\"" ^ main ^ "\"): it is external.") (* Before interpreting, the environment is initialized: - Add function definitions to the memory and reserve space for the globals. - Allocate memory to emulate the external stack and initialize the external stack pointer. - Allocate memory to emulate the internal stack and initialize the internal stack pointer. - Initialiaze the physical register environment. All are set to 0, except for the stack pointer registers that take the high and low bits of the external stack pointer. - Initialize a call to the main (set the current program counter to the beginning of the function). - Initialize the carry flag to 0. *) let interpret debug p = Printf.printf "*** LIN interpret ***\n%!" ; match p.LIN.main with | None -> (IntValue.Int32.zero, []) | Some main -> let st = empty_state in let st = init_prog st p in let (st, sp) = init_sp st in let st = init_isp st in let st = init_renv st sp in let st = init_main_call st main in let st = change_carry st Val.zero in iter_small_step debug st