forked from virtualagc/virtualagc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMEDP3.c
411 lines (404 loc) · 12.9 KB
/
MEDP3.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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/*
* Copyright: None. This code is in the public domain.
* Filename: GeminiCatchUpAndRendezvousProgram/MEDP3.c.
* Purpose: This is part of the Virtual AGC port of the
* the original 1965 simulation program for the Gemini 7/6
* mission catch-up and rendezvous flight phases. This
* particular file ports the IBM 7090/7094 assembly-language
* subroutines collectively identified as "MEDP3" (modified
* Euler integration subroutine), by Eckstrom of IBM Owego.
* Refer to the file MEDP3.s for the actual assembly code.
* Compiler: The details of linking C-language subroutines to FORTRAN
* main programs assume that the GNU gcc compiler collection
* has been used. My understanding is that it may work as-is
* with Intel's compiler as well, though I don't really know.
* If some other compilers are used, the details will have to
* be modified accordingly.
* Website: http://www.ibiblio.org/apollo
* History: 2010-09-02 RSB Began.
* 2010-09-04 RSB Completed coding. No idea of correctness
* yet.
*
* The website linked above contains a variety of documents in the
* Gemini section of its document library that are necessary to understand
* details of the IBM 7090/7094 system, FORTRAN II, the assembly-language,
* and the subroutine linkage of FORTRAN-to-assembly. I've included the
* original assembly language in-line as comments. It is probably
* important to realize that I have no more knowledge or experience with
* IBM 7090/7094 than does an andouille sausage, and the existing
* documentation is a little hard to decipher completely, so there's some
* guesswork involved.
*
* The port is a simple-minded translation of each assembly-language
* instruction to C, on a line-by-line basis. The resulting code
* can very trivially be replaced by much-more-understandable structured
* code, and can be simplified in a number of other ways, but I've chosen
* to keep it this way both to more-accurately reflect the style of the
* original code and to make verifying it against the original easier.
*/
// These are fake CPU registers. We make them unions to
// allow them to contain any data type. The only data
// types that I observe I need are added.
static union {
int i;
float x;
float *xp;
} acc, mq, xr1;
// The original assembly-language code is self-modifying.
// In particular, many of the instructions (at labels
// like X1, expected to have their address fields modified
// at runtime. We handle this by having a variable for each
// of these instructions that contains a pointer to the
// data which is supposed to be operated upon.
static float *x1_address;
static float *x2_address;
static float *x3_address;
static float *x4_address;
static float *x5_address;
static float *x6_address;
static float *xd1_address;
static float *xd2_address;
static float *xd3_address;
static float *xp1_address;
static float *xp2_address;
static float *xdp1_address;
static float *xdp2_address;
static float *xl1_address;
static float *xl2_address;
static float *xl3_address;
// Variables actually defined by the original code:
// DT PZE
static float dt = 0.0;
// N PZE
static int n = 0;
// D1 PZE ,,1
static int d1 = 0;
// D2 PZE ,,2
static int d2 = 0;
// COUNT PZE
static int count = 0;
// IND PZE
static int ind = 0;
// TWO DEC 2.
static float two = 2.0;
// TEMP PZE
static float temp = 0.0;
// The PRT and DEV subroutines expect to have access to the
// parameter list of the MAR subroutine. So we provide
// some temporary variables for saving that parameter
// list.
static int *i_saved;
static float *x_saved;
static float *y_saved;
static int *j_saved;
static int *k_saved;
static int *nout_saved;
// This appears to be a function that initializes the Euler
// computation, but doesn't actually compute anything. It sets
// up a bunch of stuff used by PRT and DEV.
// ENTRY MAR
void
mar_ (int *i, float *x, float *y, int *j, int *k, int *nout)
{
// The PRT and DEV subroutines expect to have access to the
// parameter list of the MAR subroutine, so we save it.
i_saved = i;
x_saved = x;
y_saved = y;
j_saved = j;
k_saved = k;
nout_saved = nout;
// Here's the actual translation of the code.
// MAR STZ COUNT
count = 0;
//------------------------------------------------------------
// All of this block is devoted to fetching some of the
// function parameters and storing them in variables or setting
// up self-modifying code ... which for us is just storing stuff
// in pointer variables. And yes, if this code was being
// created from scratch rather than trying to mimic previously
// created code in a very mindless way, it could be coded much
// better. All of the function parameters are pointers, since
// FORTRAN II used "call by reference" rather than "call by
// value" for everything. CPU index register XR4 contains the
// address from which the call was made, and the succeeding
// locations from the instruction that perform the call contain
// the parameters (which are pointers). An instruction like
// "CLA 3,4" is relative to XR4 and would load the third parameter
// (again, a pointer!) into the accumulator, while "CLA* 3,4"
// uses indirect addressing and would load the actual value which
// the third parameter points to into the accumulator.
// CLA* 2,4
// STO DT
acc.x = *x;
dt = acc.x;
// CLA* 1,4
// ARS 18
// STO N
acc.i = *i;
acc.i = acc.i >> 18;
n = acc.i;
// CLA 3,4
acc.xp = y;
// ADD N
acc.xp += n;
// STA X1
// STA X2
// STA X3
// STA X4
// STA X5
// STA X6
x1_address = acc.xp;
x2_address = acc.xp;
x3_address = acc.xp;
x4_address = acc.xp;
x5_address = acc.xp;
x6_address = acc.xp;
// ADD N
acc.xp += n;
// STA XD1
// STA XD2
// STA XD3
xd1_address = acc.xp;
xd2_address = acc.xp;
xd3_address = acc.xp;
// ADD N
acc.xp += n;
// STA XP1
// STA XP2
xp1_address = acc.xp;
xp2_address = acc.xp;
// ADD N
acc.xp += n;
// STA XDP1
// STA XDP2
xdp1_address = acc.xp;
xdp2_address = acc.xp;
// ADD N
acc.xp += n;
// STA XL1
// STA XL2
// STA XL3
xl1_address = acc.xp;
xl2_address = acc.xp;
xl3_address = acc.xp;
//------------------------------------------------------------
// I think what's happening with the next two instructions
// is that CPU index-register XR1 is being temporarily saved
// by modifying the AXT instruction somewhat below, so that
// the register can be used in the loop that precedes the AXT.
// SXA *+4,1
// LXA N,1
xr1.i = n;
// XL3 STZ *,1
// TIX *-1,1,1
xl3: xl3_address[xr1.i] = 0;
if (xr1.i > 1)
{
xr1.i--;
goto xl3;
}
// I think the AXT is how XR1 gets restored after the loop
// above. Of course, we don't care if it gets restored or
// not (since there isn't one anyway), so we just ignore
// the instruction completely.
// AXT *,1
// What the following nasty instruction does is indirectly
// to modify the return addresses of the DEV and PRT
// subroutines, so that they return to the point after
// the "CALL MAR" rather than to the points after "CALL DEV"
// or "CALL PRT". We have no way to handle that at this
// level, so we actually handle it in the FORTRAN code where
// the calls are made and just ignore the instruction.
// SXA S4,4
// Back to non-sickening code ...
// CLA D1
// STO* 6,4
acc.i = d1;
*nout = acc.i;
// STZ IND
ind = 0;
// The following is simply a 'return'.
// TRA 7,4
}
// ENTRY PRT
void
prt_ (void)
{
// PRT STZ COUNT
count = 0;
// What the following instruction does is to alter the
// return address to be that of the "CALL MAR", and to
// make the parameter list of the "CALL MAR" available
// to us. We handle all that elsewhere, and so ignore
// the instruction completely.
// LXA S4,4
// The following instruction accesses one of MAR's
// input parameters.
// CLA* 2,4
// STO DT
acc.x = *x_saved;
dt = acc.x;
// Transfer to the DEV subroutine. Not needed.
// TRA LD
// In the original code, the following was actually a part
// of the DEV subroutine. For us it's easiest and safest
// simply to duplicate it here.
// LD CLA D1
acc.i = d1;
// Output through a pointer provided in the MAR input
// list.
// STO* 6,4
*nout_saved = acc.i;
// A simple 'return'. It should really return to the
// point after the "CALL MAR", but that's now handled in
// the FORTRAN instead of here, so we can simply return
// with taking any special action.
// TRA 7,4
}
// ENTRY DEV
static double DummyX;
void
dev_ (void)
{
// The following instruction is used to save CPU index
// register XR1 so that it can be restored later after.
// We don't care anything about XR1, which doesn't exist
// anyway, so we can just ignore the instruction.
// DEV SXA EXIT,1
// The following instruction is used for changing the
// return address to that of mar_(), and to allow access
// to the parameter list of mar_(). The former is handled
// instead in the FORTRAN, while the latter is handled
// instead with global variables, so we can just ignore
// the instruction.
// S4 AXT *,4
// LXA N,1
xr1.i = n;
// Back to "real" code ...
// ZET IND
if (ind == 0)
goto x1;
// TRA XDP2 =I
goto xdp2;
// X1 CLS *,1 = O, V+N
// XP1 STO *,1 V+3N
x1: acc.x = -x1_address[xr1.i];
xp1_address[xr1.i] = acc.x;
// XD1 CLA *,1 V+2N
// XDP1 STO *,1 V+4N
acc.x = xd1_address[xr1.i];
xdp1_address[xr1.i] = acc.x;
// XD3 LDQ *,1 V+2N
mq.x = xd3_address[xr1.i];
// FMP DT
DummyX = dt;
DummyX *= mq.x;
acc.x = DummyX;
mq.x = DummyX - acc.x;
// X2 FAD *,1 V+N
DummyX = acc.x;
DummyX += x2_address[xr1.i];
acc.x = DummyX;
mq.x = DummyX - acc.x;
// X3 STO *,1 V+N
x3_address[xr1.i] = acc.x;
// TIX X1,1,1
if (xr1.i > 1)
{
xr1.i--;
goto x1;
}
// CLA D1
// STO IND
acc.i = d1;
ind = acc.i;
// The following instruction just reloads register XR1
// from the value we previously saved before using the
// register as a loop counter. We don't care what its
// original value was, so we don't have to actually do
// anything with the instruction.
// LXA EXIT,1
// TRA LD
goto ld;
// XDP2 CLA *,1 V+4N
xdp2: acc.x = xdp2_address[xr1.i];
// XD2 FAD *,1 V+2N
DummyX = xd2_address[xr1.i];
DummyX += acc.x;
acc.x = DummyX;
mq.x = DummyX - acc.x;
// FDP TWO
DummyX = acc.x;
mq.x = DummyX / two;
acc.x = DummyX - mq.x * two;
// FMP DT
DummyX = mq.x;
DummyX *= dt;
acc.x = DummyX;
mq.x = DummyX - acc.x;
// XP2 FAD *,1 V+3N
DummyX = xp2_address[xr1.i];
DummyX += acc.x;
acc.x = DummyX;
mq.x = DummyX - acc.x;
// X4 STO *,1 V+N
x4_address[xr1.i] = acc.x;
// STQ TEMP
// CLA TEMP
temp = mq.x;
acc.x = temp;
// XL1 FAD *,1
DummyX = xl1_address[xr1.i];
DummyX += acc.x;
acc.x = DummyX;
mq.x = DummyX - acc.x;
// X5 FAD *,1
DummyX = x5_address[xr1.i];
DummyX += acc.x;
acc.x = DummyX;
mq.x = DummyX - acc.x;
// X6 STO *,1
// XL2 STQ *,1
x6_address[xr1.i] = acc.x;
xl2_address[xr1.i] = mq.x;
// TIX XDP2,1,1
if (xr1.i > 1)
{
xr1.i--;
goto xdp2;
}
// STZ IND
ind = 0;
// The following instruction just restores XR1, which
// is irrelevant to us, so we can ignore the instruction.
// EXIT AXT *,1
// CLA COUNT
acc.i = count;
// ADD D1
acc.i += d1;
// STO COUNT
count = acc.i;
// SUB* 5,4
acc.i -= *k_saved;
// The labels StarPlus and StarMinus which I've used below
// have nothing to do with whether or not the tests below
// are plus or minus. They relate instead to whether the
// jump targets were things like "*+something" or "*-something".
// TZE *+5
if (acc.i == 0)
goto StarPlus;
// TPL *+4
if (acc.i > 0)
goto StarPlus;
// LD CLA D1
ld: acc.i = d1;
// STO* 6,4
StarMinus: *nout_saved = acc.i;
// TRA 7,4
return;
// CLA D2
StarPlus: acc.i = d2;
// TRA *-3
goto StarMinus;
}