14.8. A More Reasonable Solution

As we've seen, promoting every variable to long while it's in memory solves the problem, but it can hardly be called efficient, and probably wouldn't be acceptable even for those of us who claim be unconcerned about efficiency. It will mean that all arithmetic operations will be done to 32-bit accuracy, which will DOUBLE the run time for most operations, and make it even worse for multiplication and division. For those operations, we would need to call subroutines to do them, even if the data were byte or word types. The whole thing is sort of a cop-out, too, since it ducks all the real issues.

OK, so that solution's no good. Is there still a relatively easy way to get data conversion? Can we still Keep It Simple?

Yes, indeed. All we have to do is to make the conversion at the other end … that is, we convert on the way out, when the data is stored, rather than on the way in.

But, remember, the storage part of the assignment is pretty much independent of the data load, which is taken care of by procedure Expression. In general the expression may be arbitrarily complex, so how can procedure Assignment know what type of data is left in register D0?

Again, the answer is simple: We'll just _ASK_ procedure Expression! The answer can be returned as a function value.

All of this requires several procedures to be modified, but the mods, like the method, are quite simple. First of all, since we aren't requiring LoadVar to do all the work of conversion, let's go back to the simple version:

{ Load a Variable to Primary Register }
procedure LoadVar(Name, Typ: char);
begin
   Move(Typ, Name + '(PC)', 'D0');
end;

Next, let's add a new procedure that will convert from one type to another:

{ Convert a Data Item from One Type to Another }
procedure Convert(Source, Dest: char);
begin
   if Source <> Dest then begin
      if Source  = 'B' then
         EmitLn('AND.W #$FF,D0');
      if Dest = 'L' then
         EmitLn('EXT.L D0');
   end;
end;

Next, we need to do the logic required to load and store a variable of any type. Here are the routines for that:

{ Load a Variable to the Primary Register }
function Load(Name: char): char;
var Typ : char;
begin
   Typ := VarType(Name);
   LoadVar(Name, Typ);
   Load := Typ;
end;

{ Store a Variable from the Primary Register }
procedure Store(Name, T1: char);
var T2: char;
begin
   T2 := VarType(Name);
   Convert(T1, T2);
   StoreVar(Name, T2);
end;

Note that Load is a function, which not only emits the code for a load, but also returns the variable type. In this way, we always know what type of data we are dealing with. When we execute a Store, we pass it the current type of the variable in D0. Since Store also knows the type of the destination variable, it can convert as necessary.

Armed with all these new routines, the implementation of our rudimentary assignment statement is essentially trivial. Procedure Expression now becomes a function, which returns its type to procedure Assignment:

{ Parse and Translate an Expression }
function Expression: char;
begin
   Expression := Load(GetName);
end;

{ Parse and Translate an Assignment Statement }
procedure Assignment;
var Name: char;
begin
   Name := GetName;
   Match('=');
   Store(Name, Expression);
end;

Again, note how incredibly simple these two routines are. We've encapsulated all the type logic into Load and Store, and the trick of passing the type around makes the rest of the work extremely easy. Of course, all of this is for our special, trivial case of Expression. Naturally, for the general case it will have to get more complex. But you're looking now at the FINAL version of procedure Assignment!

All this seems like a very simple and clean solution, and it is indeed. Compile this program and run the same test cases as before. You will see that all types of data are converted properly, and there are few if any wasted instructions. Only the byte-to-long conversion uses two instructions where one would do, and we could easily modify Convert to handle this case, too.

Although we haven't considered unsigned variables in this case, I think you can see that we could easily fix up procedure Convert to deal with these types as well. This is "left as an exercise for the student."