Here’s the code for a C program I conceived during a rather boring night shift while performing national service some years back. The code was later refined using GNU Bison and GNU Flex for parsing the operand field, yielding the possibility of processing whole expressions involving addition, subtraction, multiplication and division.

Instead of specifying IEEE 754 floating point constants in hexadecimal using the pseudo operators OCTA or TETRA, like this:

* Here are some constants from Don Knuth's world:
MIX			OCTA	#408f880000000000	comments are still allowed
MMIX			OCTA	#409f640000000000	after the operand field

* Here are some well known constants from the mathematical world:
Pi			OCTA	#400921fb54442d18	TeX's ultimate version number
e			OCTA	#4005bf0a8b145769	ditto for METAFONT

* Here is a list of simple numbers:
ListOfNumbers		OCTA	#3ff0000000000000,#4000000000000000,#4008000000000000

* 32-bit floating point numbers are also available.
* But do not use them if you do not need them,
* FLOATs have better properties than SINGLEs.
MIX32			TETRA	#447c4000
MMIX32			TETRA	#44fb2000

you can write these numbers a lot simpler using one of the new pseudo operations FLOAT and SINGLE I made up, like this:

* Here are some constants from Don Knuth's world:
MIX			FLOAT	1009	comments are still allowed
MMIX			FLOAT	2009	after the operand field

* Here are some well known constants from the mathematical world:
Pi			FLOAT	3.14159265358979323846	TeX's ultimate version number
e			FLOAT	2.7182818284590452354	ditto for METAFONT

* Here is a list of simple numbers:
ListOfNumbers		FLOAT	1,2,3

* 32-bit floating point numbers are also available.
* But do not use them if you do not need them,
* FLOATs have better properties than SINGLEs.
MIX32			SINGLE	1009
MMIX32			SINGLE	2009

and preprocess your assembly code with mmixalfpp0 prior to running mmixal.

This C program has two snags, it relies on the computer and the C compiler implementing IEEE 754 correctly and on the implementation of the sscanf() library function.

mmixalfpp0.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
  mmixalfpp0.c - MMIX assembly language floating point number preprocessor
 
  Since Don Knuth's MMIXAL program does not process floating point numbers
  in its input, you may use this program as a simple preprocessor for your
  MMIX assembly programs.
 
  Simply use FLOAT or SINGLE instead of OCTA and TETRA and type your
  floats as you would in your ordinary C programs.
 
  Typical usage is: mmixalfpp0 myfile.mmS myfile.mms
 
  This implementation relies on how the underlying hardware represents
  floats internally and how the library function sscanf() works.
 
  Copyright © 2004 Trond Endrestøl <Trond.Endrestol@ximalas.info>
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
 
  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  MA 02111-1307 USA.
*/
 
#include <machine/endian.h>
 
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define isletter(c) (isalpha((c)) || (c) == '_' || (c) == ':' || (c) > 126)
#define iswhitespace(c) ((c) == ' ' || (c) == '\t')
 
enum floattype_t { SINGLE, FLOAT } floattype;
typedef union { float f; unsigned char b[4]; } singleprec_t;
typedef union { double d; unsigned char b[8]; } doubleprec_t;
 
const size_t bufferlen = 1024;
 
FILE *infile, *outfile;
unsigned char *buffer, *keyword;
 
void print_hex(void *p)
{
  singleprec_t *s;
  doubleprec_t *d;
 
  fputc('#', outfile);
 
  if (floattype == FLOAT) {
    d = (doubleprec_t *)p;
#if BYTE_ORDER == LITTLE_ENDIAN
    fprintf(outfile, "%02x%02x%02x%02x%02x%02x%02x%02x",
            d->b[7], d->b[6], d->b[5], d->b[4],
            d->b[3], d->b[2], d->b[1], d->b[0]);
#elif BYTE_ORDER == BIG_ENDIAN
    fprintf(outfile, "%02x%02x%02x%02x%02x%02x%02x%02x",
            d->b[0], d->b[1], d->b[2], d->b[3],
            d->b[4], d->b[5], d->b[6], d->b[7]);
#else
#error unknown endian
#endif
  } /* if */
  else {
    s = (singleprec_t *)p;
#if BYTE_ORDER == LITTLE_ENDIAN
    fprintf(outfile, "%02x%02x%02x%02x",
            s->b[3], s->b[2], s->b[1], s->b[0]);
#elif BYTE_ORDER == BIG_ENDIAN
    fprintf(outfile, "%02x%02x%02x%02x",
            s->b[0], s->b[1], s->b[2], s->b[3]);
#endif
  } /* else */
} /* print_hex() */
 
void process_line(void)
{
  singleprec_t singleprec;
  doubleprec_t doubleprec;
 
  unsigned char *p = buffer, *comment;
 
  while (p != keyword) { /* Print all ahead of SINGLE or FLOAT */
    fputc(*p++, outfile);
  } /* while */
 
  p += 5; /* Skip the keyword */
 
  if (floattype == FLOAT) {
    fputs("OCTA", outfile);
  } /* if */
  else {
    p++; /* Skip the E in SINGLE */
    fputs("TETRA", outfile);
  } /* else */
 
  while (iswhitespace(*p)) { /* Print the whitespace after the keyword */
    fputc(*p++, outfile);
  } /* while */
 
  if ( (comment = strchr(p, '\t')) == NULL) { /* Scan for the comment field, a newline, or EOL */
    if ( (comment = strchr(p, ' ')) == NULL) {
      if ( (comment = strchr(p, '\n')) == NULL) {
        comment = strchr(p, '\0');
      } /* if */
    } /* if */
  } /* if */
 
  while (p != comment) { /* Keep going until we are done with the operand field */
    while (!isdigit(*p) && *p != '+' && *p != '-' && *p != '.' && p != comment) { /* Skip any whitespace */
      fputc(*p++, outfile);
    } /* while */
 
    if ((isdigit(*p) || *p == '.' || *p == '+' || *p == '-')
        && p != comment) { /* We have found something resembling a number */
      if (floattype == FLOAT) {
        doubleprec.d = 0.0;
        sscanf(p, "%lf", &doubleprec.d); /* Read the number as a double */
        print_hex((void *)&doubleprec);
      } /* if */
      else {
        singleprec.f = 0.0;
        sscanf(p, "%f", &singleprec.f); /* Read the number as a float */
        print_hex((void *)&singleprec);
      } /* else */
    } /* if */
 
    while ((isdigit(*p) || *p == '.' || *p == '+' || *p == '-'
           || *p == 'e' || *p == 'E') && p != comment) { /* Read past this number */
      p++;
    } /* while */
 
    while (*p == ',') { /* Print any comma */
      fputc(*p++, outfile);
    } /* while */
  } /* while */
 
  while (*p != '\0') { /* Print the remainder of the line */
    fputc(*p++, outfile);
  } /* while */
} /* process_line() */
 
int main(int argc, char **argv)
{
  if (argc < 3) { /* Check the command line */
    fprintf(stderr,
            "Usage: %s <infile | - > <outfile | - >\n",
            argv[0]);
    return EXIT_FAILURE;
  } /* if */
 
  if (!strcmp(argv[1], "-")) { /* Use stdin as infile */
    infile = stdin;
  } /* if */
  else {
    if ( (infile = fopen(argv[1], "r")) == NULL) { /* Open infile */
      fprintf(stderr,
              "%s: couldn't open %s for reading\n",
              argv[0], argv[1]);
      return EXIT_FAILURE;
    } /* if */
  } /* else */
 
  if (!strcmp(argv[2], "-")) { /* Use stdout as outfile */
    outfile = stdout;
  } /* if */
  else {
    if ( (outfile = fopen(argv[2], "w")) == NULL) { /* Open outfile */
      fprintf(stderr,
              "%s: couldn't open %s for writing\n",
              argv[0], argv[2]);
      return EXIT_FAILURE;
    } /* if */
  } /* else */
 
  if ( (buffer = malloc(bufferlen * sizeof(char))) == NULL) { /* Allocate memory */
    fprintf(stderr,
            "%s: couldn't allocate %ld bytes for buffer\n",
            argv[0], (long)(bufferlen * sizeof(char)));
    return EXIT_FAILURE;
  } /* if */
 
  while (!feof(infile)) { /* Read until the end of infile */
    if (fgets(buffer, bufferlen, infile) == NULL) { /* Read the next line from infile */
      if (feof(infile)) { /* We are at the end of infile */
        return EXIT_SUCCESS; /* Everything is OK */
      } /* if */
      else {
        fprintf(stderr,
                "%s: could not read line from %s\n",
                argv[0], argv[1]);
        return EXIT_FAILURE;
      } /* else */
    } /* if */
 
    if (isletter(buffer[0]) || isdigit(buffer[0]) || iswhitespace(buffer[0])) { /* This line is not a comment */
      if ( (keyword = strstr(buffer, "FLOAT")) != NULL) { /* Found FLOAT */
        floattype = FLOAT;
        process_line();
      } /* if */
      else if ( (keyword = strstr(buffer, "SINGLE")) != NULL) { /* Found SINGLE */
        floattype = SINGLE;
        process_line();
      } /* else if */
      else { /* This line does not contain SINGLE nor FLOAT */
        fputs(buffer, outfile);
      } /* else*/
    } /* if */
    else { /* This line is a comment */
      fputs(buffer, outfile);
    } /* else */
  } /* while */
 
  /* NOTREACHED */
  return EXIT_SUCCESS; /* Everything is OK */
} /* main() */
 
/* End of mmixalfpp0.c */