|
|
researchv10 Norman
\section{Emitters}
We have an odd problem---we need to be able to emit either a 32-bit
integer or a string.
The order in which the bytes of the integer are emitted depends on
whether the target machine is BigEndian or LittleEndian, but the
bytes of the string should be emitted in the same order on both machines.
This means that the two emission functions depend on each other, but
in a machine-dependent way, so we bundle them up.
We also have to be able to emit two words for floating point constants.
The way to do this can be derived from [[emit_word]], and this
code seems to be the sensible place to do that.
So we define a type [[emitter_triple]]
and pass them around that way.
@ Eventually we want to take all the words and strings that have been
emitted and bundle them up into a single string using [[implode]].
We'll take the following tack with that:
each emitter will squirrel some info away in a reference variable.
A function [[emitted_string: unit -> string]] will take the
squirreled information and return a string that represents
everything emitted so far.
As a side effect, it will reset the emitter system to its initial
state, where ``everything emitted so far'' is the empty string.
The actual implementation will be a list of strings which is reversed
and imploded.
<<signature>>=
signature EMITTERS = sig
type emitter_triple
val LittleEndian : emitter_triple
val BigEndian : emitter_triple
val emitted_string : unit -> string
val MipsAsm : outstream -> emitter_triple
val address : int ref
end
@ First something that's capable of emitting real code, then
something that can print assembly code.
We emit the assembly code to output right away, without any fooling.
<<*>>=
structure Emitters : EMITTERS = struct
type emitter_triple = (int * int -> unit) * (int -> string -> unit)
* (string -> unit)
local
<<memory and basic services>>
in
<<string emitter>>
<<little-endian emitter>>
<<big-endian emitter>>
fun emitted_string () =
let val s = implode (rev (!so_far))
in so_far := nil; s
end
end
structure BigReal = MipsReal(struct val emit_word = emit_pair_big end)
structure LittleReal =MipsReal(struct val emit_word = emit_pair_little end)
val LittleEndian = (emit_pair_little,emit_string,LittleReal.realconst)
val BigEndian = (emit_pair_big,emit_string,BigReal.realconst)
<<assembly-code emitters>>
end
@ Here is a variable to remember what's been emitted so far
<<memory and basic services>>=
val so_far = ref nil : string list ref
fun squirrel s = so_far := s :: !so_far
fun emit_byte n = squirrel (chr n)
<<string emitter>>=
fun emit_string n s = squirrel (substring(s,0,n))
handle e =>
(print "?exception "; print (System.exn_name e);
print (" in emitters.emit_string "^
(Integer.makestring n) ^ " \""^s^"\"\n");
raise e)
@ Little-endian means the little (least significant) end first,
like the VAX.
We parameterize the real emitters by a function that emits a byte.
<<little-endian emitter>>=
fun emit_pair_little(hi,lo) =
let open Bits
fun emit_word(n) =
(emit_byte(andb(n,255));emit_byte(andb(rshift(n,8),255)))
in (emit_word(lo);emit_word(hi))
end
<<big-endian emitter>>=
fun emit_pair_big(hi,lo) =
let open Bits
fun emit_word(n) =
(emit_byte(andb(rshift(n,8),255));emit_byte(andb(n,255)))
in (emit_word(hi);emit_word(lo))
end
@ Now the assembly code.
to make it easier to debug, we print out addresses in the same format
as {\tt dbx}: we use the byte address and we print it in hex.
We have to bend over backwards to handle real numbers
<<assembly-code emitters>>=
val address = ref 0 (* address of next instruction in words *)
<<real number state info and [[decode_real_ptr]]>>
fun MipsAsm stream =
let fun say s = (output stream s; flush_out stream)
fun printaddr addrref =
let val n = !addrref
in (if n<10 then " " else if n < 100 then " " else "")
^ (Integer.makestring n)
end
local
open Bits
fun hexdigit n =
let val d = andb(n,15)
in if d <= 9 then chr(d+ord("0"))
else chr(d-10+ord("a"))
end
fun hex1 n = hexdigit(rshift(n,4))^hexdigit(n)
fun hex2 n = hex1(rshift(n,8))^hex1(n)
fun hex4 n = hex2(rshift(n,16))^hex2(n)
in
fun hex(hi,lo) = hex2(hi) ^ hex2(lo)
fun printaddr addrref =
let val n = 4 * (!addrref) (* address in bytes *)
in "0x" ^ (hex4 n)
end
end
fun decode x = (
say ((printaddr address) ^ ": (" ^ (hex x) ^") "
^ (MipsDecode.decode x));
address := !address + 1; ()
)
fun do_decode_real(w,s) = (
say ((printaddr address) ^ ": (" ^ (hex w) ^") "
^ s ^ "\n");
address := !address + 1; ()
)
fun decode_real s = (real_string := s; AsmReal.realconst s)
fun decode_string n s =
if n > 0 then
(say ((printaddr address)
^ ": \"" ^substring(s,0,4) ^"\"\n");
address := !address + 1;
decode_string (n-4) (substring(s,4,String.length(s)-4))
)
else ()
in
decode_real_ptr := SOME do_decode_real;
(decode,decode_string,decode_real) : emitter_triple
end
<<real number state info and [[decode_real_ptr]]>>=
val decode_real_ptr = ref NONE : ((int * int) * string -> unit) option ref
(* used to emit asm code for a real word *)
val real_least = ref NONE : (int * int) option ref
(* least significant word of real *)
val real_string = ref ""
fun emit_real_word w =
let val decode_real = case !decode_real_ptr of
SOME f => f
| NONE => ErrorMsg.impossible "missed real decoder in mips asm"
in
case !real_least of
NONE => real_least := SOME w
| SOME least =>
(decode_real(least,"[low word of "^(!real_string)^"]");
decode_real(w,"[high word of "^(!real_string)^"]"))
end
structure AsmReal = MipsReal(struct val emit_word = emit_real_word end)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.