Field-dependent information

Field categories

There are a number of things one wants to know about each field, depending on whether one is to assemble, disassemble, or generate C code for assembly. Here's a tentative classification: If field names are specified, they are kept in a name-to-value table. That table is associated with the field in the global table fnt (standing for ``field-name table'').
<*>= [D->]
global fnt              # field -> name -> value
Defines fnt (links are to index).

<*>+= [<-D->]
procedure fieldinfo(f, info) 
    initial { /fnt := table(); /fieldasm := table() }
    case type(info) of {
        "string" :  case info of { "checked"    : { <make f checked> }
                                   "unchecked"  : { <make f unchecked> }
                                   "guaranteed" : { <make f guaranteed> }
                                   default : { <complain about fieldspec info> }
        "namespec"  : (/fnt[f] := check_namespec(info, f)) | 
                         error("Field names for field ",, " already specified")
        default     : fail
Defines fieldinfo (links are to index).

<make f checked>= (<-U)
every delete(guaranteed_fields | unchecked_fields, f)
<make f unchecked>= (<-U)
insert(unchecked_fields, f)
delete(guaranteed_fields, f)
<make f guaranteed>= (<-U)
every insert(guaranteed_fields | unchecked_fields, f)
<complain about fieldspec info>= (<-U)
error("Unknown field info specifier `", info, "'")

Fields can be checked or unchecked at assembly time.

<*>+= [<-D->]
global unchecked_fields, guaranteed_fields
Defines guaranteed_fields, unchecked_fields (links are to index).

Field values may be assigned pneumonic names that are used during disassembly of instructions. The names bound to field values within an enumeration are used to synthesize the sames of opcodes.

<*>+= [<-D->]
record namespec(nametable, full)
procedure sparse_name_table(bindlist)
  t := table()
  every b := !bindlist do
    (/t[] := b.val) | error("duplicate field names")
  return namespec(t)
Defines namespec, sparse_name_table (links are to index).

<*>+= [<-D->]
record enumbinding(name, val)
Defines enumbinding (links are to index).

<*>+= [<-D->]
procedure full_name_table(namelist, f)
  t := table()
  every i := 1 to *namelist do
    (/t[namelist[i]] := i - 1) | error("duplicate field names")
  return namespec(t, 1)
Defines full_name_table (links are to index).

<*>+= [<-D->]
procedure check_namespec(spec, f)
  if \spec.full then
    *spec.nametable = 2^fwidth(f) | 
       error("Field ",, " has ", 2^fwidth(f), " values, not ", *spec.nametable)
  if x := spec.nametable[n := key(spec.nametable)] & (x < 0 | x >= 2^fwidth(f)) then
    error("name ", image(n), " = ", x, 
          " falls outside the value range of field ",
  return spec.nametable
Defines check_namespec (links are to index).

<*>+= [<-D->]
procedure fieldname_table(f)
  initial /fnt := table()
  return fnt[f]
Defines fieldname_table (links are to index).

<*>+= [<-D->]
procedure fieldname_env_for(f)
  return [\fieldname_table(f)] | []

procedure fieldname_env_for_ipt(ipt)
  return (type(ipt.meaning) == "field", fieldname_env_for(ipt.meaning)) | []
Defines fieldname_env_for, fieldname_env_for_ipt (links are to index).

Emitting field names

We concoct a namearray and pass it to pretty.
<*>+= [<-D->]
procedure emit_fieldnames(base)
  local header, data, fields
  data := open(base || implementation_extension, "w")
  header := open(base || interface_extension, "w");
  if interface_extension == ".h" then
    write(data, "#include \"", base, interface_extension, "\"")
  pp := PPnew(data);
  <make fields an array of all fields sorted by name>
  every PPxwrite(pp, pretty(Gdeclnamearray(fieldnamearray(!fields))), ";")
  every write(header, "extern char *", (!fields).name, "_names[];")
  every close(data | header)
Defines emit_fieldnames (links are to index).

<make fields an array of all fields sorted by name>= (<-U)
t := table()
fields := []
every f := !symtab & type(f) == "field" do t[] := f
every put(fields, (!sort(t))[2])

To build a name array, we first create a table of all invalid field values. The valid ones are then overwritten using names from fieldname_table(f).

<*>+= [<-D->]
procedure fieldnamearray(f)
  return name_array_from_table(\fieldname_table(f), 2^fwidth(f),
procedure name_array_from_table(t, limit, fieldname)
  local name
  limit <= 1024 |
    error("Tried to enumerate ", limit, " names for field or operand ", fieldname)
  na := namearray(field, table(), limit, fieldname || "_names", "")
  every i := 0 to na.hi - 1 do
    na.tbl[i] := bad_operand_name(fieldname, i)
  every name := key(t) do
    na.tbl[t[name]] := name
  return na
procedure bad_operand_name(name, value)
  return "??" || name || "=" || value || "?!"
Defines bad_operand_name, fieldnamearray, name_array_from_table (links are to index).

For use in identifying fields with identical naming patterns, we have to turn a name table into a suitable string. This means covering the keys in order.

<*>+= [<-D]
procedure nametablekey(nametab)
  local min, max
  static cache
  initial cache := table()
  if \cache[nametab] then return cache[nametab]
  if /nametab then return cache[f] := "(no name table)"
  u := table()
  every k := key(nametab) do u[nametab[k]] := k
  <make min and max smallest and largest values in nametab>  
  s := ""
  every i := min to max do
    s ||:= \u[i] || "@" || i || "\0"
  return cache[f] := s
Defines nametablekey (links are to index).

<make min and max smallest and largest values in nametab>= (<-U)
min := max := !nametab | 0
every min >:= !nametab
every max <:= !nametab