Dissembling
Vector 4:4, page 103
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 103
If, like me, you find some of the APL examples in Vector tough going, you might enjoy this account of what happens when high-level APL meets a low-level Assembly Language.
There’s no doubt that there’s a slightly loony flavour to the application described. Please bear in mind that it was thrown together in less than an hour, used briefly and, having served its purpose, jettisoned unceremoniously.
Recently, I had occasion to write some System/370 Assembler. A client required a routine to convert IBM mainframe TOD clock values embedded in accounting records to something printable.
The TOD - or time of day - clock is simply a 64-bit counter, the bits being numbered from 0 on the left to 63 on the right. The clock steps at a rate equivalent to incrementing bit 51 once per microsecond. A zero value corresponds to 00:00 hours GMT on January 1st 1900. The clock will wrap round to zero again after approximately 143 years. Come to think of it, there’s probably a Vector competition in there somewhere.
I used MSDOS Edit on my Olivetti M21 to prepare the System/370 Assembler program statements. I knew the client had an IRMA card, so I’d be able to pump the code through to his IBM mainframe from a diskette. The trouble was that I couldn’t run a System/370 assembly on the 8086-based Olivetti to verify statement syntax and I couldn’t test the program either.
I had APL*Plus PC Release 6.2 and, at first, I toyed with the idea of programming the algorithm in APL. Cogitation revealed that this would prove almost nothing. The APL code would be at such a high level that it would have little in common with the Assembler solution. It was evident that the real issue was the detailed mechanics of the Assembler program; would I, for example, accidentally overwrite a register value which I needed later? Besides, APL*Plus PC already provides the FTIMEREP function for date conversion. Writing another FTIMEREP would prove nothing.
FTIMEREP would, however, be useful for checking results. Admittedly, the TOD clock value is not in microseconds but I could get my routine in sync with FTIMEREP by inserting a 12-bit register shift in the real Assembler code after I’d verified it. What’s more, APL*Plus PC’s WSTS quad function would be a useful source of testdata since it yields the workspace timestamp in microseconds.
The next idea was that I code a simulation of the System/370 Assembler in APL to produce some fake object code. I could then write some sort of executor to gallop through this invoking APL functions which simulated individual System/370 instructions such as Divide, Load Address and what-have-you.
That would have been fun but I feared the disapproval of my more favoured colleagues who have had the benefit of C&D’s System Design Course. There would have been some dreadfully infra dig module couplings in such a system. If I were not to be debagged by a screaming horde of outraged purists I would have to be very circumspect. For Heaven’s sake don’t tell them I thought of doing such a thing.
The fact that the Assembler manual runs to over 400 pages had absolutely nothing to do with my decision to abandon this idea. For the time being.
In the end, of course, I cheated. I wrote a function to simulate the System/370 Divide instruction and transliterated the relevant Assembler statements into a crude APL function which called it. Another function was then placed atop the lot to exercise the code, convert the resulting date via a utility function which I already had to hand, and report the converted date and the timestamp.
To simulate System/370 registers, I just used APL integer variables. Because I was going to throw the workspace away after use, I didn’t bother to localise them. If I needed a scalar, I’d just wang a vector into line without bothering why it was a vector in the first place.
When I ran the DATETEST function, to my infinite gratification, I got an incorrect result. It turned out that, although the Assembler code correctly implemented the algorithm, the algorithm was faulty. Without going into the humiliating details, I’ll just confess that I had failed to distinguish between cardinal and ordinal numbers. There’s probably another Vector competition in that, too.
Finally, we come to the APL code itself. When I reviewed it today, I formed the view that code of such surpassing rambunctiousness should not be permitted to foul the august pages of Vector. For this reason, I’d have preferred to leave it as an exercise for the reader. On the other hand, bad code can have a heartening effect. It gives courage to beginners and makes experts split their sides.
The System/370 Assembler code has yet to be tested on a mainframe, so don’t accept the extract below unreservedly. Here goes, and don’t say I didn’t warn you ...
DATETEST ⎕WSTS ⍝ SAMPLE EXECUTION OF THE APL SIMULATION.
02OCT87 14 5 25.800000
FTIMEREP ⎕WSTS ⍝ A CROSSCHECK ON THE RESULTS.
1987 10 2 14 5 25 800
∇ R←DATETEST MS;∆Q;⎕IO
[1] ⎕IO←1 ⍝ USE ORIGIN-1 IN ALL FUNCTIONS.
[2] ∆Q←MSCONV MS ⍝ RUN THE SIMULATED CONVERTER.
[3] ∆Q← 2↓⍕ 1000 1000 ⊥∆Q ⍝ GET YEAR & DAY AS 'YYDDD' TEXT.
[4] R←UTTSVC ∆Q ⍝ CONVERT 'YYDDD' TO 'DDMMMYY'.
[5] ⍝ APPEND TIME OF DAY TO RESULT.
[6] R←R,' ',(⍕ 60 60 60 ⊤(⍳0)⍴R9),'.',⍕R11
∇
∇ R←MSCONV MS
[1] ⍝ MICROSECONDS CONVERTOR - SIMULATED SYSTEM/370 ASSEMBLY LANGUAGE.
[2] ⍝
[3] R7←MS ⍝ MICROSECONDS IN.
[4] D 6,1000000 ⍝ R7←ELAPSED SECONDS. R6←MICROSECONDS OVER.
[5] R11←R6 ⍝ R11←MICROSECONDS OVER.
[6] ⍝
[7] D 6,86400 ⍝ R7←ELAPSED DAYS. R6←SECONDS OVER.
[8] R7←R7-365 ⍝ (FOR 1900).
[9] R9←R6 ⍝ R9←SECONDS OVER.
[10] R7←R7+1 ⍝ CHANGE ELAPSED DAYS TO DAY NUMBER.
[11] ⍝
[12] D 6,1461 ⍝ R7←ELAPSED QUADYEARS. R6←DAYS OVER.
[13] R10←4×R7 ⍝ R10←ELAPSED YEARS IN THE QUADYEARS.
[14] ⍝
[15] R7←R6 ⍝ R7 IS DAYS OVER (A PART-QUADYEAR).
[16] D 6,365 ⍝ R7←YEARS OVER. R6←DAYS OVER.
[17] ⍝
[18] R10←R10+R7+1900+1 ⍝ R10←FINAL ADJUSTED YEAR NUMBER
[19] ⍝ (+1900 FOR ID. +1 CONVERTS FROM ELAPSED).
[20] R←R10,R6 2-ELEMENT VECTOR RESULT: YEARNO AND DAYNO.
∇
∇ D NN;REG;BY;EVEN;ODD
[1] REG←1↑NN
[2] BY←1↓NN
[3] ⍝
[4] ⍝ SIMULATE SYSTEM/370 EVEN/ODD REGISTER-PAIR DIVISION
[5] ⍝ ('REG' IS EVEN-REGISTER-NUMBER. 'BY' IS DIVISOR')
[6] ⍝
[7] EVEN←'R',⍕REG ⍝ DEVISE NAME OF EVEN-NUMBERED REGISTER.
[8] ODD←'R',⍕REG+1 ⍝ DEVISE NAME OF ODD-NUMBERED REGISTER.
[9] ⍎EVEN,'←',ODD ⍝ START BOTH WITH A COPY OF THE INPUT.
[10] ⍝
[11] ⍎EVEN,'←BY|',EVEN ⍝ THE EVEN REGISTER GETS THE REMAINDER.
[12] ⍎ODD,'←⌊',ODD,'÷BY' ⍝ THE ODD REGISTER GETS THE QUOTIENT.
∇