From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.3 required=5.0 tests=BAYES_00,INVALID_MSGID autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,63c3482f0fa7ae08,start X-Google-Attributes: gid103376,public From: Ken Garlington Subject: ESP code rewritten in Ada Date: 1996/11/05 Message-ID: <327F5B2B.47E3@lmtas.lmco.com>#1/1 X-Deja-AN: 194697168 to: 72325.1327@compuserve.com cc: garlingtonke content-type: text/plain; charset=us-ascii organization: Lockheed Martin Tactical Aircraft Systems mime-version: 1.0 newsgroups: comp.lang.ada x-mailer: Mozilla 2.02 (Macintosh; I; 68K) Date: 1996-11-05T00:00:00+00:00 List-Id: [comp.lang.ada folks: My news server is down, but hopefully you will get this...] Jack W. Crenshaw has a "Programmer's Toolbox" column in Embedded Systems Programming magazine. I like the column, but it suffers from having all code examples written in C. For fun, I've decided to start coding some of his examples in Ada. I found this month's example to be a good example of what Ada can do for reliability; see if you agree. First, here's the C code example for a simple filter: /* General-purpose low-pass filter, *inplementing a first-order lag. Call *it with the input signal u, and a *gain represented by the factor shift. *Example, shift = 4 inples gain= 1/16 * * Output is the return value, a short. *The long integer x is for working *storage, and should be neither read *nor written by the user. */ short lag_filter(short u, short shift, long*x){ x += u - (x>>shift); return (short)(x>>shift); } Elsewhere in the article, it notes that: (1) "shift" should not change from call to call. (2) "x" must be declared static and initialized to zero. (2) If "shift" is known beforehand, it's better to use division rather than a shift, to handle negative signals properly (although Microsoft Visual C++ defeats this by always replacing the divide with a shift). Here's my Ada version, which has the advantages of (1) forcing "shift" to be constant from call to call (2) hiding the definition of "x", and automatically initializing it (although it still has to be defined "statically" by the user, which is annoying.) (3) Using division to fix the small error for negative signals. (Interestingly, one compiler I tried did substitute a shift for the divide, but if "x" was negative, it added (shift-1) to it first to make the shift correct.) (4) Permits the use of types other than "short" (within reason) -- General-purpose low-pass filter, -- implementing a first-order lag. -- Instantiate a copy for each input -- such that copy has same lifetime -- as input. generic Shift : Positive; -- Gain is 2 to the negative Shift. -- E.g., shift => 4 implies gain = 1/16 -- 2**Shift must fit in max precision type Signal is range <>; -- Signal should be at least Shift+1 -- bits less than max precision package Filter is function Lag (U : Signal) return Signal; end Filter; with System; package body Filter is type Long is range System.Min_Int .. System.Max_Int; X : Long := 0; function Lag (U : Signal) return Signal is D : constant Long := Long(2**Shift); begin X := X + Long(U) - X / D; return Signal (X / D); end Lag; end Filter; I used the following test case: with Filter; package Use_Lag is subtype Short is Integer range -32768 .. 32767; A : Short := 200; package A_Filter is new Filter(Shift => 4, Signal => Short); end Use_Lag; with Use_Lag; use Use_Lag; procedure Test is begin A := A_Filter.Lag(A); -- new A should be 12 end Test; For a 16-bit machine (MIL-STD-1750), I got decent assembly code, similar to: Load R0, U Double-Shift Right Arithmetic R0, 16 bits ; R0/R1 = Long(U) Double-Add R0, X Double-Load R2, X Label1: ["fix" negative X before shifting] Branch Not Negative, Label2 ; skip if R2/R3 not neg. Exclusive-OR R4, R4 ; R4 = 0 Load Immediate R5,15 ; R4/R5 = Long(15) Double-Add R2, R4 ; negative X "fixed" Label2: Double-Shift Right Arithmetic R2, 4 ; R2/R3 = X / D Double-Subtract R0, R2 ; R0/R1 = new X Double-Store R0, X ; save X for next call [here, the block of code at Label1 is repeated for the new X, then...] Label3: Double-Shift Right Arithmetic R0, 4 ; R0/R1 = new X / D [at this point R1 is returned to the user] Even though using a straight division would have been fewer instructions, division is so slow on this machine that the sequence above is still competitive for negative signals (and much faster for positive signals). On a 32-bit machine (VAX), I got a much simpler code sequence, since it used 32 bits for everything: Add X and U and store in R3 Divide X by 16 and store in R2 Subtract R2 from R3 and store in X Divide new X by 16 and return result In either case, it looks pretty competitive to what a C compiler would generate. Note that both tests had all checks off, max optimization. -- LMTAS - "Our Brand Means Quality" For more info, see http://www.lmtas.com or http://www.lmco.com