Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

A Wavelet Analyzer


APR93: A WAVELET ANALYZER

A WAVELET ANALYZER

An alternative to the FFT-based spectrum analyzer

This article contains the following executables: WAVELET.ARC

Mac A. Cody

Mac is an engineering specialist for a Dallas, Texas defense contractor. He can be contacted at 214-205-6452.


The ubiquitous (and indispensable) oscilloscope and its cousin, the spectrum analyzer, are the basic tools of the trade for electronic engineers. The oscilloscope is used for viewing signals; its display shows the signal as a function of voltage vs. time. The spectrum analyzer allows you to view the signal as a function of voltage vs. frequency. Some versions of the spectrum analyzer use a frequency-adjustable, narrow-band filter to extract the spectral content of the signal. As the filter's center frequency is swept from lower to higher frequencies, the output-signal energy is traced upon an oscilloscope screen. The resulting plot is the time-averaged spectrum of the input signal. More modern spectrum analyzers use the fast Fourier transform (FFT) to directly calculate the spectrum of the sampled signal.

An alternative to the Fourier transform is the wavelet transform (discussed in my article "The Fast Wavelet Transform," Dr Dobb's Journal, April 1992), which is capable of simultaneous analysis of both time and frequency in signals. In addition, an infinite variety of wavelets enables you to tailor that analysis according to the wavelet chosen. The fast wavelet transform provides an efficient implementation of the wavelet transform for use on digital computers.

This article presents an implementation of a wavelet analyzer (a variation of the spectrum analyzer), which uses the fast wavelet transform rather than the FFT to analyze the signal. The fast wavelet transform is implemented as a recursive routine on a digital signal processing (DSP) board based on an AT&T DSP32 or DSP32C DSP IC. The input is a voice-grade audio signal sampled and digitized by a "codec" (a type of analog-to-digital converter) on the DSP board. The wavelet analyzer uses the features and capabilities of a standard VGA controller to display the input signal and wavelet transform coefficients as they are generated in real time.

No Recourse But to Recurse

My previous article provided a complete description of the fast wavelet transform algorithm; here, I'll focus on one variation. The tree or pyramid structure of the fast wavelet-transform algorithm naturally leads to a recursive implementation; see Figure 1. The input of each level of the tree or pyramid algorithm depends upon the output of the previous approximation level. This is true even for the first level, since the input samples are themselves an approximation of the continuous function which the samples represent. Note, also, that the filter pairs for all levels are identical. Each level of "filtering" is followed by down-sampling (or decimation) by a factor of 2 prior to sending a sample to the next-lower level.

Therefore, the recursion for each level acts upon the same filter pair with the same set of wavelet-filter coefficients. Each level of recursion occurs half as often as each higher level. Recursion terminates under two conditions. The first (and most common) condition is that, at any level, two samples have not been generated, so decimation cannot occur. The decimation operation occurs as a result of sliding the samples through the filters two at a time, generating one output. Before proceeding to the next-lower level, recursion must occur twice, generating two approximation coefficient samples. The other condition for termination of recursion is execution of the lowest (and last) level of approximation and detail filters. When this happens, the approximation and detail-coefficient samples are both output. Depending upon the level of recursion performed, there can be from three to nine signal, detail-coefficient, and approximation-coefficient samples output from each recursion for a six-level fast wavelet transform; see Table 1.

Table 1: The recursive fast wavelet transform generates a quantity of detail samples for every two signal samples it is supplied.

  Signal        Detail level sample      Approximation  Samples
  sample    5    4     3    2   1    0     sample       to plot

   0,  1    0                                              3
   2,  3    1    0                                         4
   4,  5    2                                              3
   6,  7    3    1     0                                   5
   8,  9    4                                              3
  10, 11    5    2                                         4
  12, 13    6                                              3
  14, 15    7    3     1    0                              6
  16, 17    8                                              3
  18, 19    9    4                                         4
  20, 21   10                                              3
  22, 23   11    5     2                                   5
  24, 25   12                                              3
  26, 27   13    6                                         4
  28, 29   14                                              3
  30, 31   15    7     3    1   0                          7
  32, 33   16                                              3
  34, 35   17    8                                         4
  36, 37   18                                              3
  38, 39   19    9     4                                   5
  40, 41   20                                              3
  42, 43   21   10                                         4
  44, 45   22                                              3
  46, 47   23   11     5    2                              6
  48, 49   24                                              3
  50, 51   25   12                                         4
  52, 53   26                                              3
  54, 55   27   13     6                                   5
  56, 57   28                                              3
  58, 59   29   14                                         4
  60, 61   30                                              3
  62, 63   31   15     7    3   1    0       0             9

In my previous article, the program listings for the transform contain control statements that handle the ends of data arrays. This is necessary because the data arrays have finite length in both directions. In the case of real signals, however, once sampling of data begins, it can go on endlessly (or as long as the hardware and power supply hold out). As a result, the algorithm must now deal with a nonterminating data sequence, which actually makes the algorithm and code implementation simpler, since new data will always be available.

DSP Implementation

The recursive fast wavelet transform is implemented on the AT&T DSP32 and DSP32C digital signal processors. I chose the DSP32 and DSP32C because of their support for floating-point math at high speeds (8 and 24 MFLOPs, respectively), pointer addressing, and C-like assembly language. These DSPs are described more fully in Jim Bittman's article, "Adding the Power of DSP to Your Application" (Dr. Dobb's Journal, May 1991).

The fast wavelet-transform algorithm borrows heavily from the field of DSP, so it can easily be taken to a full DSP implementation. Four source-code modules, written in AT&T DSP32 and DSP32C assembly languages, form the DSP component of the wavelet analyzer. Listing One (page 82) is the recursive transform; Listing Two (page 82) is the pixel plotting routine; Listing Three (page 87) the main loop; Listing Four (page 90) storage allocator for variable and constants; and Listing Five (page 90) the make and header files.

Recursive Fast Wavelet Transform

The routine DECOMP (see Listing One) implements the recursive fast wavelet transform, or signal decomposition. The routine first retrieves pointers to the input-and output-approximation storage arrays. The input-approximation coefficients are supplied by the next-higher level of the transform (or recursion). In the case of the first level of recursion, the input consists of the signal samples from the codec. The output-approximation coefficients are those generated by the current level of the transform.

The routine then performs two data convolutions between the input-approximation coefficients and the high-pass and low-pass wavelet filters (the wavelet coefficients). This operation is coded inline to avoid the execution-time penalty of control loops. A jump vector supplied by the calling function causes DECOMP to execute the necessary number of convolution instructions for the length of the wavelet filters.

The first convolution produces one wavelet-detail coefficient sample. This sample is stored in an array for eventual display on the host system's VGA. The second convolution produces one wavelet-approximation coefficient sample. At the same time, the input-approximation coefficients are shifted by two storage locations, thereby producing the decimation factor of 2 and opening up two free storage locations for new approximation samples from the next-higher transform level. What happens to the newly calculated approximation-coefficient sample depends on the current level of recursion of DECOMP and the number of samples previously generated at this level of recursion.

As DECOMP executes each level of recursion, a recursion counter is decremented. When the count reaches 0, DECOMP has reached the desired lowest level of the wavelet transform, level 0. At this point the new approximation-coefficient sample is placed into the array with the other detail-coefficient samples for display. This coefficient is the level-0 approximation coefficient for the wavelet transform.

If the recursion count has not reached 0, the approximation-coefficient sample is destined for input into the next lower level of the wavelet transform. If the sample is the first of a pair of samples for the next lower level, then it is stored in the appropriate input-approximation coefficient array, and DECOMP then returns to the next higher level of recursion. If this sample is the second sample of the pair, then it is stored and DECOMP calls itself to proceed down to the next level of the transform (and the recursion). At the same time, the output pointer is realigned to allow two more approximation samples to be placed into the output array during future recursions.

Pixel-plotting Routine

The DRAWIMAG routine plots each sample generated by the codec or DECOMP into one of two sets of image bitmaps in the DSP memory; see Listing Two. Each set consists of eight bitmaps corresponding to the displays of the input signal, the six detail levels, and the level-0 approximation. Each map is designed to hold eight unit intervals of sample data or wavelet coefficients. A unit interval is the number of samples required to generate a wavelet transform to a desired number of levels. In this application, a six-level wavelet transform has a unit interval of 21, or 64, samples at the input.

Each map consists of 50 rows of bytes, which correspond to the 50 rows assigned for each signal trace on the VGA display. The number of bytes per row depends upon the number of samples in eight-unit intervals for the input signal or wavelet-coefficient level displayed. For example, the level-2 detail bitmap in this application has four samples per unit interval. With eight-unit intervals, this bitmaps need four bytes per row. Exceptions to this general rule are the level-0 detail and approximation-display bitmaps, which require two, rather than one byte per row. This is necessary because the DSP32 and DSP32C are restricted to 16-and 32-bit DMA transfers through the parallel data register. The additional byte in each row is unused and set to 0.

Plotting a sample value into the bitmap involves translating the sample value into a count (either up or down) from the baseline in the bitmap; see Figure 2. The count determines the length of a line of pixels drawn from the base-line. The byte-column pointer and bit pointer determine in which pixel column the line will be drawn. There is a bit pointer, byte-column pointer, and baseline offset value for each of the eight bitmaps of the image.

Depending upon the value of the bit pointer, the drawing operation is either a direct write of the pixel values to memory or a logical OR with the current contents of the bitmap prior to the write. When the bit pointer selects the most significant bit in the byte, all 50 bytes in the currently selected byte column are written to so that the previously displayed image is overwritten while the new image is written. The write operation proceeds from the upper-blank portion of the image to the trace pixels, and then to the lower-blank portion of the image. If the sample value is full scale in the positive direction, the upper-blank fill operation is skipped; if it is full scale in the negative direction, the lower-blank fill operation is skipped. Trace pixels are not drawn if the signal level is below the scale of the least-significant pixel.

When the bit pointer points to a bit other than the most significant, the pixel-drawing routine uses logical ORing of the bit pointer and the bytes in the column to turn on the trace pixels. This process takes longer than the direct write operation described previously, but it is performed only on the bytes where the trace image will reside. The baseline offset determines the starting byte for the line of pixels drawn. While calculating the pixel draw count, the direction of the draw operation from the baseline is established. A draw count of 0 results in no pixels being drawn on that bit column.

Once the column of pixels is drawn, the bit pointer is shifted down one bit and saved for the next call of DRAWIMAG for that bitmap. If the bit pointer is 0 after the shift, then it is set to point to the most-significant bit, and the byte column pointer is incremented and saved. The pointers move through all the bit columns of their respective bitmaps until all samples of the eight-unit intervals are plotted.

Main DSP Control Loop

ANALYZER is the main routine for the DSP portion of the wavelet-analyzer program; see Listing Three. The routine initializes the DSP registers, memory, and codec prior to entering the main processor loop. Within the main processor loop, signal samples are taken from the codec and passed to DECOMP for processing to generate the next group of wavelet coefficients. After returning from DECOMP, DRAWIMAG is called several times to plot the group of signal samples and wavelet-coefficient samples into the various image bitmaps for eventual display on the VGA screen. The plotting operation can exceed the time of arrival of the next sample from the codec. To prevent loss of a sample during plotting, the serial-input buffer flag is checked after each plot until the sample arrives. Once the sample arrives, it is acquired and the remainder of the plotting operations are completed.

The ANALYZER main loop processes eight-unit intervals (512 data samples) to fill the bitmaps with new information to display. Then the signal processor flags the host processor and informs the host program which set of bitmaps (IMAGE_0 or IMAGE_1) holds the latest set of display data. The signal processor then reinitializes the pointers for DRAWIMAG to plot to the other set of bitmaps. Before proceeding with processing the next pair of signal samples, the DSP checks that the host processor has acknowledged the flag and waits if it hasn't.

Watchin' the Waves Roll In...

Once the DSP is running, the host PC essentially acts as an I/O server for the DSP board. Whenever a new block of image data is plotted and ready for display, the DSP board flags the host PC to retrieve the image and display it on the VGA screen. This process is extremely time critical, since new image data is ready to be displayed every 64 milliseconds. The PC must shift the pixels of previously displayed images to make room for the new image and then transfer the image from the DSP memory to the display memory. Thus, the display device must be able to receive, manipulate, and display the image data as quickly as it becomes available. A standard VGA controller and display are used for this task. The VGA provides the graphics resolution, color, and control features necessary to display and manipulate images at fairly rapid rates. It is also a well-established display standard and is now quite affordable.

Smooth update of the display requires page flipping. Michael Abrash ("Graphics Programming," DDJ, December 1991) suggested that this is possible on a 640x480 VGA display. However, Michael's technique allows only the upper two-thirds of the display area to be flipped; more area is needed for the wavelet-analyzer display. Since only 512 columns are needed to display data, a compromise between horizontal display resolution and page size can be made.

A display with 592 columns and 480 rows is created, with the top 400 rows capable of being page flipped; see Figure 3. The horizontal display dimension is altered by setting the Horizontal Display Enable End and the Offset registers in the VGA's CRTC to display 74 bytes (or 592 pixels) on each horizontal scan. The size of the flip page is determined by setting the Line Compare Count register in the VGA's CRTC to reset the line count after 400 lines have been displayed. Page flipping is controlled by setting the CRTC Start Address High and Start Address Low registers to point to the specific page. The routine Set592x480, see Listing Six (page 90), performs the setup of the VGA display by initially setting the display to mode 16H (640x480 pixels, 16 colors) and modifying the registers described earlier.

The image data currently displayed on the VGA must be shifted to make room for new image data coming from the DSP memory. The backfields of the latest image alternate between dark and light gray to delineate between the eight unit intervals. These backfields need to be changed to a blue background to indicate past wavelet coefficients when they are shifted. Therefore, a trick is played with the VGA color palette to produce multiple mappings of signal and backfield colors; see Table 2. Write Mode 1 and the Map Mask register are used to enable pixel-color changes to occur simultaneously with the movement of image data; see Figure 4.

Table 2: The colors of the VGA palette registers can be selected to remap the colors of the signal traces during a data-shift operation so that the background colors are changed while the signal itself appears unaffected.

  Palette      DAC palette     Pixel shift-and-mask operation
  register   register number        map pixel to register

      0        0 (black)                      0
      1       63 (white)                      1
      2       60 (light red)                  2
      3        3 (cyan)                       3
      4        4 (red)                        0
      5        5 (magenta)                    1
      6        6 (olive)                      2
      7       55 (pale yellow)                3
      8        1 (blue)                       8
      9       62 (yellow)                     9
     10        1 (blue)                      10
     11       62 (yellow)                    11
     12       56 (dark gray)                  8
     13       62 (yellow)                     9
     14        7 (light gray)                10
     15       62 (yellow)                    11

When the CPU reads the VGA memory, all four bytes in the pixel planes are placed in latch registers. When the CPU writes to the VGA memory with Write Mode 1 enabled, the contents of the latch registers are written back into the pixel planes. Writing latch contents to memory can be blocked by setting the corresponding bits of the Map Mask register to 0. Setting bit 2 of the Map Mask register to 0 and all others to 1 causes the pixels' color-register values to be remapped to other values at the time of writing. The remapping causes the light gray and dark backfields to be changed to blue while the traces remain yellow.

The routine ShiftWaveTraces in Listing Six performs the operations of image shifting and color modification. The routine moves eight rectangles of the screen image representing the input signal and wavelet-transform coefficient traces. Each rectangle consists of 50 rows of pixel bytes. Although the routine is written in C, an assembly language loop (written using Borland Turbo C 2.0 __emit__ statements) is used to move each block of pixel bytes a row at a time. The REP MOVSB instruction is used to move the bytes of each row quickly. The data in the array shiftblocks describes the number of bytes to move in each rectangle and the screen-wrap index to keep the source and destination pointers aligned with the block boundaries. The source and destination addresses for the shift are chosen so that the image blocks are moved from the page being displayed to the other page. In this fashion, the screen can be updated with the new image from the DSP without disturbing the screen image.

With the pixel images shifted in the VGA's memory, the routine GetDSPimage can now transfer the pixel image from the DSP's memory to that of the VGA; see Listing Five. The design of the image bitmaps in the DSP memory allow for rapid transfer of the images. The bytes in the DSP image bitmaps correspond directly to the bytes in pixel plane 0 memory of the VGA.

To transfer the pixel data, the Write Mode register is set to mode 0 to allow the external data to be written into the VGA memory. The Bit Mask register is set so that all bits in a byte of VGA memory can be written to. The Map Mask register is set so that only pixel-plane 0 (and the least-significant bit of the color-register value) is affected by the write. Transfers can now proceed by performing word transfers from the DSP's memory directly into the VGA memory using the DMA capabilities of the DSP32 and DSP32C; see Figure 5. As in ShiftWaveTraces, GetDSPimage uses an assembly language loop to optimize the data transfers. The REP INSW instruction is used to move individual rows of pixel bytes.

Making Waves, Again...

The code presented here is only a portion of a complete wavelet-analyzer package called ANALYZER. The user interface of ANALYZER allows the operator to select the coefficients to be used by the analyzer and view their values and the recursive scaling and wavelet functions defined by those coefficients. The user can also run and halt the wave-transform operation on the incoming signal and pause the screen display at any time. The vertical scale of the input signal and wavelet-transform coefficient traces can be independently adjusted to allow detailed viewing of the data.

The code for the host PC is written in Borland's Turbo C 2.0 with the small memory model. Although the assembly language components of the VGA graphics code are written using __emit__ statements, it should not be too difficult to convert the code over to full assembly language. Assembling the DSP code requires the AT&T WE DSP32-SL Support Software Library. The batch files supplied with the analyzer source code are sufficient to control the library's make utility. Because of its size, the program is available electronically; see "Availability" on page 5. The electronic version includes the ANALYZER executable and source, the DSP source and executables, and documentation.

A PC equipped with an 80286 (or higher) processor running at 12 MHz or higher and a VGA card is required. To run the DSP software, the user must supply either a Burr-Brown ZPB32 (DSP32) or ZPB34 (DSP32C) DSP board and a ZPB100 codec board. Other DSP boards can be supported by modifying the DSP I/0 control software.

Wave Goodbye (Again)!

The ANALYZER program turns the PC and a DSP board into a test instrument that uses the recursive fast wavelet transform (much as the spectrum analyzer uses the Fourier transform) to analyze signals in real time. With this tool, you can study interactively how different wavelet and scaling functions can interpret a signal. Someday, the wavelet-transform analyzer may even reside alongside the oscilloscope and spectrum analyzer as an essential tool in every engineer's toolbox!

References

Abrash, Michael. "Catching Up." Dr. Dobb's Journal (December, 1991).

Bittman, Jim. "Adding the Power of DSP to Your Application." Dr. Dobb's Journal (May, 1991).

Cody, Mac A. "The Fast Wavelet Transform." Dr. Dobb's Journal (April, 1992).

iAPX 286 Programmer's Reference Manual, Intel Corporation, 1983.

Kliewer, Bradley Dyck. EGA/VGA: A Programmer's Reference Guide, second edition. Berkeley, CA: McGraw-Hill, 1990.



_A WAVELET ANALYZER_
by Mac A. Cody


[LISTING ONE]
<a name="00fc_0010">

#include "dsp_type.h"
#if DSP32
#include "dspregs.h"
#endif

.global DECOMP
        /*  DECOMP
            perform recursive decomposition on non-terminating data sequence
            registers used: r1 r2 r3 r4 r5 r6 r11 r12 r13 r14 r15 r16
            accumulators used: a0 a1
            input: r1 - pointer to wavelet high-pass filter coefficients
                   r2 - pointer to wavelet low-pass filter coefficients
                   r6 - pointer to wavelet output data list
                   r11 - jump address for proper filter length
                   r12 - pointer to data pointer array
                   r13 - pointer to stack
                   r14 - return stack register, i.e. "TOP OF STACK"
                   r15 - recursion counter
                   r16 - filter coefficient pointer wrap back index
        */
DECOMP:   r3e = *r12++; /* load pointer to approx. input array source */
          r5e = *r12;   /* load pointer to approx. output array destination */
          goto r11;     /* jump to appropriate filter processing for length */
          r4e = r3 + 8; /* set pointer to approx. input array destination */
DT6:      a0 = a0 + *r3++ * *r1++; /* pass data through high-pass wavelet */
          a0 = a0 + *r3++ * *r1++; /* filter (a0 = 0.0 initially) */
DT4:      a0 = a0 + *r3++ * *r1++; /* the destination of the jump depends */
          a0 = a0 + *r3++ * *r1++; /* upon the length of the wavelet */
DT2:      a0 = a0 + *r3++ * *r1++; /* filter */
          goto r11 + 28; /* jump to appropriate filter processing for length */
          *r6++ = a0 = a0 + *r3++r16 * *r1++r16; /* output is detail point */
AT6:      a1 = a1 + (*r4++ = *r3++) * *r2++; /* pass data through low-pass */
          a1 = a1 + (*r4++ = *r3++) * *r2++; /* filter (a1 = 0.0 initially) */
AT4:      a1 = a1 + (*r4++ = *r3++) * *r2++;
          a1 = a1 + (*r4++ = *r3++) * *r2++;
AT2:      a1 = a1 + *r3++ * *r2++;
          a1 = a1 + *r3++ * *r2++r16;
          r15 = r15 - 1;         /* check the recursion count */
          if (eq) goto NO_RECUR; /* if true, recursion at bottom of tree */
          r5e & 0x0004;          /* check for even/odd status */
          *r5-- = a1 = a1;       /* save approx. data for next level */
          if (ne) goto CLEAN_UP; /* if true, await another data point */
          r4e = r5 + 8;          /* wrap output pointer back */
          *r12++ = r4e;          /* save wrapped pointer to approx. O/P */
          *r13-- = r14e;         /* save return address to stack */
          a0 = a0 - a0;          /* clear the accumulators */
          call DECOMP (r14);     /* recurse down the tree */
          a1 = a0;
#if DSP32C
          r13e = r13 + 4;        /* align stack pointer to return address */
#else
          r13 = r13 + 2;         /* align stack pointer to return address */
#endif
          r14e = *r13;           /* pop the return address */
          nop;
          return (r14);
          nop;

NO_RECUR: *r6 = a1 = a1;         /* save approx. coeff. as next value */
CLEAN_UP: return (r14);
          *r12++ = r5e;          /* save unwrapped pointer to approx. output */
        /* END OF DECOMP */





<a name="00fc_0011">
<a name="00fc_0012">
[LISTING TWO]
<a name="00fc_0012">

#include "dsp_type.h"
#if DSP32
#include "dspregs.h"
#endif

.global DRAWIMAG
     /*  DRAWIMAG -- draw image of supplied point and connect to previous point
            registers used: r1 r2 r3 r4 r5 r6 r7 r12 r13 r14
            accumulators used: a0 a1
            input: r12 - pointer to data pointer array
                   r13 - pointer to stack
                   r14 - return stack register, i.e. "TOP OF STACK"
                   a0 - data point to draw
    */
DRAWIMAG: a0 = *r12++ - a0 * *r12++; /* multiply by scalling coefficient */
          a1 = -a0 + *r12;    /* determine if value above upper threshold */
          a0 = ifalt(*r12++); /* if true, limit data to threshold */
          a1 = a0 - *r12;     /* determine if value below lower threshold */
          a0 = ifalt(*r12++); /* if true, limit data to threshold */
          *r12++ = a0 = int(a0); /* convert data to integer format */
          nop;
          r6e = r12 - 2;        /* point to temporary storage */
          r1 = *r12++;          /* load the row increment value */
          r4 = *r12++;          /* load the row baseline offset value */
          r2 = *r12++;          /* load the bit pointer */
          r3e = *r12;           /* load the byte column pointer */
          r6 = *r6;             /* read the new byte column pointer offset */
          r2 - 0x80;            /* check if first bit of new byte column */
          if (ne) goto ADD_BAR; /* if true, add new bar to byte column */
          nop;
#if DSP32C
          r6 - 23;               /* check if datum point is above baseline */
          if (le) goto ABOVE_BL; /* if true, it is above the baseline */
          nop;
          r4 = 25;       /* top counter for clearing of pixel over bar */
          r5 = r6 - 24;  /* middle counter for draw of bar pixels */
          goto DRAW_COL; /* go draw the column of pixels */
          r6 = 48 - r6;  /* bottom counter for clearing of pixel under bar */

ABOVE_BL: r4 = r6;      /* top counter for clearing of pixel over bar */
          r5 = r6;
          r5 = 24 - r5; /* middle counter for draw of bar pixels */
          r6 = 25;      /* bottom counter for clearing of pixel under bar */
DRAW_COL: r4 = r4 - 1;  /* check if no top pixels are to be cleared */
          if (lt) goto NO_TOP; /* if true, skip clearing bytes above bar */
          r7 = r7 - r7;        /* force the register to zero */
          do 0, r4;            /* repeat next instruntion r4+1 times */
          *r3++r1 = r7l;       /* zero the bytes above the bar */
NO_TOP:   r5 = r5 - 1;         /* check if no bar pixels are to be set */
          if (lt) goto NO_MID; /* if true, skip setting bytes of bar */
          nop;
          do 0, r5;         /* repeat next instruntion r5+1 times */
          *r3++r1 = r2l;    /* MSB of byte is bar, others cleared */
NO_MID:   r6 = r6 - 1;      /* check if no bottom pixels are to be cleared */
          if (lt) goto NO_BOT; /* if true, skip clearing bytes below bar */
          nop;
          do 0, r6;            /* repeat next instruntion r6+1 times */
          *r3++r1 = r7l;       /* zero the bytes below the bar */
NO_BOT:   goto SHIFTBIT; /* go shift the bit pointer and clear up */
          r3e = *r12;    /* reload the byte pointer */

ADD_BAR:  r6 - 24;              /* check if datum below baseline */
          if (gt) goto BELOW_BL; /* if true, datum is below baseline */
          r6 = r6 - 24;  /* calculate length of bar */
          r6 = -r6;      /* datum above baseline length was negative */
          r1e = -r1;     /* datum above baseline, increment is decrement */
BELOW_BL: r4e = r4 + r3; /* add baseline offset to byte column pointer */
          r5e = r4 + r1; /* move pointer away from the baseline */
          r7 = 0;        /* zero initial storage value */
          do 2, r6;      /* repeat the next 3 instructions r6+1 times */
          r6l = *r5++r1; /* load the byte */
          *r4++r1 = r7l; /* store the byte and move the pointer */
          r7 = r6 | r2;  /* OR byte with bit pointer to set the bit */
#else
          r6 = r6 * 2;     /* multiply offset by four to account for ... */
          r6 = r6 * 2;     /* four bytes per instruction in MEMSET */
          r6 - 92;         /* check if datum point is above baseline */
          if (le) goto ABOVE_BL; /* if true, it is above the baseline */
          r5 = -r6;      /* middle counter for draw of bar pixels */
          r4 = 0;        /* top counter for clearing of pixel over bar */
          r5 = r5 + 196; /* middle counter for draw of bar pixels */
          goto DRAW_COL; /* go draw the column of pixels */
          r6 = r6 - 92;  /* bottom counter for clearing of pixel under bar */

ABOVE_BL: r4 = -r6;      /* top counter for clearing of pixel over bar */
          r4 = r4 + 100; /* top counter for clearing of pixel over bar */
          r5 = r6 + 4;   /* middle counter for draw of bar pixels */
          r6 = 0;        /* bottom counter for clearing of pixel under bar */
DRAW_COL: *r13-- = r14;  /* save return address to the stack */
          call r4+MEMSET (r14); /* zero the bytes above the bar */
          r7 = r7 - r7;         /* force the register to zero */
          call r5+MEMSET (r14); /* set the bytes of the bar */
          r7 = r2;              /* MSB of byte is bar, others cleared */
          call r6+MEMSET (r14); /* zero the bytes below the bar */
          r7 = r7 - r7;         /* force the register to zero */
          r13 = r13 + 2;        /* point to return address on the stack */
          r14 = *r13;           /* load the return address from the stack */
NO_BOT:   goto SHIFTBIT; /* go shift the bit pointer and clear up */
          r3 = *r12;     /* reload the byte pointer */

ADD_BAR:  r6 = r6 * 2;   /* multiply offset by twelve to account for ... */
          r6 = r6 * 2;   /* four bytes per instruction and ... */
          r7 = r6 * 2;   /* three instructions per byte in MEM_OR */
          r6 = r6 + r7;
          r6 - 288;               /* check if datum below baseline */
          if (gt) goto BELOW_BL; /* if true, datum is below baseline */
          r6 = r6 - 288; /* calculate length of bar */
          r6 = -r6;      /* datum above baseline length was negative */
          r1 = -r1;      /* datum above baseline, increment is decrement */
BELOW_BL: r6 = 288 - r6  /* align the counter for proper call */
          r4 = r4 + r3;  /* add baseline offset to byte column pointer */
          r5 = r4;
          r5 = r5 + r1;  /* move pointer away from the baseline */
          *r13-- = r14;  /* save return address to the stack */
          r7 = 0;        /* zero initial storage value */
          call r6+MEM_OR (r14);
          r6 = r7;
          r13 = r13 + 2;        /* point to return address on the stack */
          r14 = *r13;           /* load the return address from the stack */
#endif

SHIFTBIT: r2 = r2 >> 1;    /* shift the pixel pointed to right */
          if (ne) goto NO_WRAP; /* if true, don't wrap the pixel pointer */
          r12e = r12 - 2;  /* point to bit pointer storage */
          r2 = 0x0080;     /* new bit mask if pixel wraps */
          r3e = r3 + 1;    /* increment base to next byte upon wrap */
NO_WRAP:  *r12++ = r2;     /* save the next bit pointer */
          return (r14);
          *r12++ = r3e;    /* save the next byte pointer start value */
        /* END OF DRAWIMAG */

#if DSP32
        /*  MEMSET -- set column of image bytes to a given value
            registers used: r1 r3 r7 r14
            input: r1 - postincrement value
                   r3 - byte column pointer
                   r7 - storage value
                   r14 - return stack register, i.e. "TOP OF STACK"
        */
MEMSET:   *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;
          *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;
          *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;
          *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;
          *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;
          *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;  *r3++r1 = r7l;
          *r3++r1 = r7l;

          return (r14);
          nop;
        /* END OF MEMSET */

        /*  MEM_OR -- logical OR column of image bytes with a given value
            registers used: r1 r4 r5 r6 r7 r14
            input: r1 - postincrement value
                   r2 - 'OR' value
                   r4 - lagging byte column pointer
                   r5 - leading byte column pointer
                   r14 - return stack register, i.e. "TOP OF STACK"
        */
MEM_OR:   r6l = *r5++r1;  /* load column byte */
          *r4++r1 = r7l;  /* save initial 'dummy' value */
          r6 = r6 | r2;   /* 'OR' column byte with byte value */
          r7l = *r5++r1;  /* load the next column byte */
          *r4++r1 = r6l;  /* save the new column byte value */
          r7 = r7 | r2;   /* 'OR' the next column byte with the byte value */
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;  r6 = r6 | r2;
          r7l = *r5++r1;  *r4++r1 = r6l;  r7 = r7 | r2;
          r6l = *r5++r1;  *r4++r1 = r7l;
          return (r14);
          nop;
        /* END OF MEMSET */
#endif




<a name="00fc_0013">
<a name="00fc_0014">
[LISTING THREE]
<a name="00fc_0014">

#include "dsp_type.h"
#if DSP32
#include "dspregs.h"
#endif

.extern DECOMP, DRAWIMAG
.extern WAVEADRS, WAVELVLS, IMAGSHOW, LVLADDRS
.extern SIG_DRAW, DRAW_CNT, H_FILTER, L_FILTER
.extern RST_DATA, IM0_PTRS, IM1_PTRS, IMAGE_0
.extern STACKEND, SIGNALIN, DATA_OUT

        /*  ANALYZER
            main control program for the wavelet analyzer
            registers used: r1 r2 r3 r4 r5 r6 r8 r9
                            r11 r12 r13 r14 r15 r16 r17
            accumulators used: a0 a1 a2 a3
        */
.rsect ".bank0"
          dauc = 0x0000;  /* initialize DAU formats */
          r1e = WAVELVLS; /* point to jump address for wavelet filter size */
          r2e = SIGNALIN; /* point to approximation data storage */
          r3 = *r1++;     /* load number of levels in wavelet transform */
          r4 = 8;         /* load number of unit intervals per image */
          r1e = r1 + 2;   /* point to the unit interval count storage */
          *r1++ = r4;     /* store the unit interval count value */
          r4 = r4 - r4;   /* zero the register */
          *r1++ = r4;     /* set active image flag storage to 0 (IMAGE 0) */
          a3 = a3 - a3;   /* intial data value is zero */
#if DSP32C
          r3 = r3 - 1;    /* number of levels in wavelet transform - 1 */
          do 8, r3;       /* repeat next nine instructions r5+1 times */
#else
          r3 = r3 - 2;    /* number of levels in wavelet transform - 2 */
#endif
INITAPPX: *r1++ = r2e;   /* store base address of each approximation level */
          *r2++ = a3 = a3; /* zero storage for approximation level */
          *r2++ = a3 = a3; /* r2 ends up pointing to first location .... */
          *r2++ = a3 = a3; /* in the next approximation level */
          *r2++ = a3 = a3;
          *r2++ = a3 = a3;
          *r2++ = a3 = a3;
#if DSP32C
          nop;
          *r1++ = r2e;    /* store the next location */
          ioc = 0x40987;  /* initialize the serial I/O to the codec */
          r1e = RST_DATA; /* point to reset pointer initialization storage */
          r2e = SIG_DRAW; /* point to signal drawing data sets */
          r3 = 26;        /* integer pointer index, 4 bytes per pointer */
          r2e = r2 + 22;  /* point to bit pointer storage */
          do 3, 7;        /* perform next 4 instructions eight times */
          r4e = *r1++;    /* load initial bit pointer */
          r5e = *r1++;    /* load initial byte column pointer */
          *r2++ = r4;     /* store initial bit pointer */
          *r2++r3 = r5e;  /* store initial byte column pointer */
#else
          if (r3-- >=0) goto INITAPPX; /* repeat for all arrays */
          *r1++ = r2e;   /* store the next location */
          ioc = 0x0987;  /* initialize the serial I/O to the codec */
          r1 = RST_DATA; /* point to reset pointer initialization storage */
          r2 = SIG_DRAW; /* point to signal drawing data storage */
          r3 = 26;       /* integer pointer index, 2 bytes per pointer */
          r2 = r2 + 22;  /* point to bit pointer storage */
          r4 = 6;        /* initialize loop counter for eight loops */
INIT_PTR: r5 = *r1++;    /* load initial bit pointer */
          r6 = *r1++;    /* load initial byte column pointer */
          *r2++ = r5;    /* store initial bit pointer */
          if (r4-- >=0) goto INIT_PTR; /* repeat for all pointers */
          *r2++r3 = r6;  /* store initial byte column pointer */
#endif
          r12e = WAVEADRS; /* load pointer to filter jump address */
          r13e = STACKEND; /* load pointer to top of stack memory */
          r11e = *r12++;   /* load jump address for filter size */
          r16e = *r12++;   /* load coefficient pointer wrap back index */
          r17 = *r12++;    /* load number of levels */
          r15 = *r12;      /* load image data space size / 4 */
          r3e = IMAGE_0;   /* point to the detail level float output */
#if DSP32C
          r15 = r15 - 1;   /* number of data points minus one */
          do 0, r15;       /* repeat next instruction r15+1 times */
          *r3++ = a3 = a3; /* clear four bytes in IMAGE_0 array */
#else
          r15 = r15 - 2;   /* number of data points minus two */
CLR_IMAG: if (r15-- >=0) goto CLR_IMAG; /* repeat until all image cleared */
          *r3++ = a3 = a3; /* clear four bytes in IMAGE_0 array */
#endif
          goto ENTRY_PT;   /* jump to the entry point */
          nop;

MAINLOOP: r8 = DATA_OUT;        /* point to output data array */
          r12e = SIG_DRAW;      /* point to signal drawing data sets */
          r9e = DRAW_CNT;       /* point to draw count array */
          a0 = *r8++;           /* load first signal data point */
          r9e = r9 + r15;       /* index into draw count array */
          call DRAWIMAG (r14);  /* draw the first input sample point */
          r9l = *r9;            /* load the draw loop counter */
          r12e = SIG_DRAW;      /* point to signal drawing data sets */
DRWLOOP1: if (ibf) goto SAMPL_IN; /* if true, next sample available */
          nop;
          a0 = *r8++;           /* load data point */
          call DRAWIMAG (r14);  /* draw data point */
          nop;
          if (r9-- >= 0) goto DRWLOOP1; /* repeat for all levels of xfrm */
#if DSP32C
          nop;
#else
          r12 = r12 + 2;        /* point to data set for next level */
#endif
WAITIBF1: if (ibe) goto WAITIBF1; /* wait until next data sample arrives */
          nop;
          goto TEST_LVL;
SAMPL_IN: a3 = float(ibuf);      /* load the new signal sample */
DRWLOOP2: a0 = *r8++;            /* load data point */
          call DRAWIMAG (r14);   /* draw data point */
          nop;
          if (r9-- >= 0) goto DRWLOOP2;
#if DSP32C
          nop;
#else
          r12 = r12 + 2;         /* point to data set for next level */
#endif
TEST_LVL: r15 - 0;               /* test for process of all levels */
          if (ne) goto ENTRY_PT; /* if true, all levels have not been done */
          r12e = IMAGSHOW;       /* point to the unit interval count down */
          nop;
          r3 = *r12++;           /* load the unit interval count down */
          r4 = *r12--;           /* load the image draw flag */
          r3 = r3 - 1;           /* decrement the unit interval count */
          if (ne) goto ENTRY_PT; /* if true, not done with new data set */
          *r12++ = r3;           /* save the new count */
          r3 = 8;                /* reset count to eight unit intervals */
          r12 = r12 - 2;         /* point to unit interval count storage */
          *r12++ = r3;           /* save the reset count */
          pir = r4;              /* interrupt host processor for new image */
          r4 = r4;               /* tickle CAU flags for image set */
          if (ne) goto IM0_NEXT; /* if true, set up for drawing on IMAGE 0 */
          nop;
          r4 = 1;                /* next image displayed is IMAGE 1 */
          goto PNTRINIT;         /* go to image pointer initialization */
          r2e = IM1_PTRS;        /* set up for drawing on IMAGE 1 */

IM0_NEXT: r4 = 0;          /* next image displayed is IMAGE 0 */
          r2e = IM0_PTRS;  /* set up for drawing on IMAGE 0 */
PNTRINIT: r1 = SIG_DRAW;   /* point to signal drawing data sets */
          *r12++ = r4;     /* save the image draw flag */
          r1e = r1 + 24;   /* point to byte column pointer storage */
#if DSP32C
          r15e = 28;       /* set up post increment value */
          do 0, 7;         /* perform next instruction eight times */
          a0 = (*r1++r15 = *r2++) + a0; /* moves four bytes at once! */
#else
          r3 = 28;         /* set up post increment value */
          r4 = 6;          /* set up loop counter for eight iterations */
REINIPTR: r5 = *r2++;      /* load image array pointer */
          if (r4-- >= 0) goto REINIPTR; /* repeat for all pointers */
          *r1++r3 = r5;    /* store image array pointer */
#endif
WAIT_PIE: if (pif) goto WAIT_PIE; /* wait for pif flag to be cleared */
          nop;
ENTRY_PT: r3e = SIGNALIN;           /* point to input signal storage array */
WAITIBF2: if (ibe) goto WAITIBF2;   /* wait until next data sample arrives */
          r15 = r17;                /* inititialize the recursion counter */
          *r3++ = a2 = float(ibuf); /* output second data sample first */
          r6e = DATA_OUT;    /* point to output data array */
          *r3 = a3 = a3;     /* output first data sample last */
          *r6++ = a3 = a3;   /* place first sample in data output array */
          *r6++ = a2 = a2;   /* place second sample in data output array */
          r1e = H_FILTER;    /* point to detail filter coefficients */
          r2e = L_FILTER;    /* point to approx. filter coefficients */
          r12e = LVLADDRS;   /* point to data level address pointers */
          a0 = a0 - a0;      /* zero the accumulators */
          call DECOMP (r14); /* start the recursive decomposition */
          a1 = a0;
          goto MAINLOOP;
          nop;





<a name="00fc_0015">
<a name="00fc_0016">
[LISTING FOUR]
<a name="00fc_0016">

/* WAVEDATA.S */
#include "dsp_type.h"
#if DSP32
#include "dspregs.h"
#endif

.global WAVEADRS, WAVELVLS, IMAGSHOW, LVLADDRS
.global SIG_DRAW, DRAW_CNT, H_FILTER, L_FILTER
.global RST_DATA, IM0_PTRS, IM1_PTRS, IMAGE_0
.global STACKEND, SIGNalIN, DATA_OUT

.align 4
WAVEADRS: int24 0;       /* jump address for wavelet filter length */
WAVEINDX: int24 0;       /* wrap back index for wavelet filter length */
WAVELVLS: int   6, 1625; /* number of levels, clear loop counter */
IMAGSHOW: int   8, 0;    /* unit interval count, active image pointer */
LVLADDRS: int24 SIGNalIN, APPROX_5; /* data pointer storage for level 5 */

          int24 APPROX_5, APPROX_4; /* data pointer storage for level 4 */
          int24 APPROX_4, APPROX_3; /* data pointer storage for level 3 */
          int24 APPROX_3, APPROX_2; /* data pointer storage for level 2 */
          int24 APPROX_2, APPROX_1; /* data pointer storage for level 1 */
          int24 APPROX_1, 0;        /* data pointer storage for level 0 */
.align 4
SIG_DRAW: float 0.0, 24.0, 48.0, 0.0; /* scaling and offset coefficients */
          int   0, 64;                /* temp storage, row increment */
          int   1536, 0;              /* baseline value, bit pointer */
          int24 0;                    /* byte column pointer */
.align 4
          float 0.0, 24.0, 48.0, 0.0;
          int   0, 32;
          int   768, 0;
          int24 0;
.align 4
          float 0.0, 24.0, 48.0, 0.0;
          int   0, 16;
          int   384, 0;
          int24 0;
.align 4
          float 0.0, 24.0, 48.0, 0.0;
          int   0, 8;
          int   192, 0;
          int24 0;
.align 4
          float 0.0, 24.0, 48.0, 0.0;
          int   0, 4;
          int   96, 0;
          int24 0;
.align 4
          float 0.0, 24.0, 48.0, 0.0;
          int   0, 2;
          int   48, 0;
          int24 0;
.align 4
          float 0.0, 24.0, 48.0, 0.0;
          int   0, 2;
          int   48, 0;
          int24 0;
.align 4
          float 0.0, 24.0, 48.0, 0.0;
          int   0, 2;
          int   48, 0;
          int24 0;

DRAW_CNT: byte 6, 4, 3, 2, 1, 0;

.align 4
H_FILTER: 6*float 0.0; /* highpass wavelet filter storage allocation */
L_FILTER: 6*float 0.0; /* lowpass wavelet filter storage allocation */

/* image pointer reset initialization data */
RST_DATA: int24 0x02; /* position of first pixel in unit interval at reset */
          int24 IM0INITS; /* 00 00 00 00 00 00 00 01 */

          int24 0x01, IM0INIT5; /* 00 00 00 01 */

          int24 0x01, IM0INIT4; /* 00 01 */

          int24 0x01, IM0_LVL3; /* 01 */

          int24 0x10, IM0_LVL2; /* 10 */

          int24 0x40, IM0_LVL1; /* 40 */

          int24 0x80, IM0_LVL0; /* 80 */

          int24 0x80, IM0_LVLA; /* 80 */

/* image pointer switch initialization data */
IM0_PTRS: int24 IM0SIGNL, IM0_LVL5, IM0_LVL4, IM0_LVL3;
          int24 IM0_LVL2, IM0_LVL1, IM0_LVL0, IM0_LVLA;
IM1_PTRS: int24 IM1SIGNL, IM1_LVL5, IM1_LVL4, IM1_LVL3;
          int24 IM1_LVL2, IM1_LVL1, IM1_LVL0, IM1_LVLA;

/* IMAGE 0 storage allocation */
IMAGE_0:
IM0SIGNL: 7*byte 0;
IM0INITS: 3193*byte 0;

IM0_LVL5: 3*byte 0;
IM0INIT5: 1597*byte 0;

IM0_LVL4: byte 0;
IM0INIT4: 799*byte 0;

IM0_LVL3: 400*byte 0;

IM0_LVL2: 200*byte 0;

IM0_LVL1: 100*byte 0;

IM0_LVL0: 100*byte 0;

IM0_LVLA: 100*byte 0;

/* IMAGE 1 storage allocation */
IM1SIGNL: 3200*byte 0;
IM1_LVL5: 1600*byte 0;
IM1_LVL4: 800*byte 0;
IM1_LVL3: 400*byte 0;
IM1_LVL2: 200*byte 0;
IM1_LVL1: 100*byte 0;
IM1_LVL0: 100*byte 0;
IM1_LVLA: 100*byte 0;

.align 2
STACKBSE: 31*int24 0; /* subroutine stack storage allocation */
STACKEND: int24 0;

.rsect ".hi_ram"
SIGNalIN: 6*float 0.0; /* approximation data storage allocation */
APPROX_5: 6*float 0.0;
APPROX_4: 6*float 0.0;
APPROX_3: 6*float 0.0;
APPROX_2: 6*float 0.0;
APPROX_1: 6*float 0.0;
DATA_OUT: 9*float 0.0; /* output data storage allocation */





<a name="00fc_0017">
<a name="00fc_0017"><B>[LISTING FIVE]</B><pre>

DSP_REGS.H

/* register file redefinition */
#define  r1e   r1
#define  r2e   r2
#define  r3e   r3
#define  r4e   r4
#define  r5e   r5
#define  r6e   r6
#define  r7e   r7
#define  r8e   r8
#define  r9e   r9
#define  r10e  r10
#define  r11e  r11
#define  r12e  r12
#define  r13e  r13
#define  r14e  r14
#define  r15e  r15
#define  r16e  r16
#define  r17e  r17
#define  r18e  r18
#define  r19e  r19
#define  r20e  r20
#define  r21e  r21

/* integer and float redefinition */
#define  int24  int
#define  float24  float

DSP_TYPE.32
#define DSP32 1

DSP_TYPE.32C
#define DSP32C 1

MAKE32.BAT
copy dsp_type.32 dsp_type.h
d3make -M2 -N -l analyzer.s decomp.s drawimag.s wavedata.s -o anlyzr32.dsp

MAKE32C.BAT
copy dsp_type.32c dsp_type.h
d3make -M6 -Q -O -l analyzer.s decomp.s drawimag.s wavedata.s -o anlyz32c.dsp




<a name="00fc_0019">
<a name="00fc_001a">
[LISTING SIX]
<a name="00fc_001a">

V592x480.h
void Set592x480(void);
void ShiftWaveTraces(unsigned int src, unsigned int dest);
void GetDSPimage(unsigned int dest, unsigned int io_addr);

V592x480.c
/* Mode set routine for VGA 592x480 16-color mode. Tested with Borland C 2.0 */
#include <stdlib.h>
#include <dos.h>

unsigned char palette_set[17]={0,64,60,3,4,5,6,55,1,62,1,62,56,62,7,62,0};
void Set592x480(void)
{
  union REGS regset;
  struct SREGS sregset;

  /* First, set to standard 640x480 mode (mode 12h) */
  regset.x.ax = 0x0012;
  int86(0x10, ®set, ®set);

  /* Next, set up the new color palette */
  regset.x.ax = 0x1002;
  regset.x.dx = (unsigned int) palette_set;
  sregset.es = _DS;
  int86x(0x10, ®set, ®set, &sregset);

  /* Now, tweak the registers needed to convert the horizontal character
     count from 80 to 74 characters (640 to 592 pixels) per line */
 outportb(0x3D4, 0x11);           /* allow access to CRTC registers 0 - 7 */
  outportb(0x3D5, inportb(0x3D5) & 0x7f);
  outport(0x3D4, 0x4901);        /* adjust the Horizontal Display Enable End
                                 register for 74 byte display area width */
  outport(0x3D4, 0x2513);        /* adjust the Offset register for 74 byte
                                 (37 word) display area width */
  /* adjust Line Compare register to start display of non-flipping area at
   line 400 (row scan number 399 (0x18f) */
  outportb(0x3D4, 9);              /* clear tenth bit of Line Compare count */
  outportb(0x3D5, inportb(0x3D5) & 0xbf);
  outportb(0x3D4, 7);              /* set ninth bit of Line Compare count */
  outportb(0x3D5, inportb(0x3D5) | 0x10);
  outport(0x3D4, 0x8f18);         /* remaining eight bits of Line Compare */

  /* adjust the Start Address High and Start Address Low registers
     to start screen display on page 0 */
  outport(0x3D4, 0x170c);
  outport(0x3D4, 0x200d);

  outportb(0x3D4, 0x11); /* block access to CRTC registers 0 - 7 */
  outportb(0x3D5, inportb(0x3D5) | 0x80);
}
#define CLD        0xfc
#define PUSH_DS    0x1e
#define PUSH_CX    0x51
#define POP_DS     0x1f
#define POP_CX     0x59
#define REP        0xf3
#define MOVSB      0xa4
#define STOSB      0xaa
#define INSW       0x6d
#define DEC_AX     0X48
#define DEC_CX     0X49
#define DEC_DX     0X4a
#define INC_DX     0X42
#define DEC_SI     0X4e
#define JNE        0X75
#define OUT_DX_AL  0xee
#define USE_ES     0x26
#define MOV_AL_DI  0x058a
#define MOV_CX_AX  0xc88b
#define MOV_CX_DX  0xca8b
#define ADD_DI_BX  0xfb03
#define ADD_SI_BX  0xf303
#define ADD_SI_CX  0xf103
#define SUB_SI_CX  0xf12b

unsigned char colorarray[74];
unsigned char leftedges[8]={0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
unsigned char rightedges[8]={0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
static unsigned int shift_blocks[7][2] = { {42, 32}, {26, 48}, {18, 56},
      {14, 60}, {12, 62}, {11, 63}, {11, 63}};
void ShiftWaveTraces(unsigned int src, unsigned int dest)
{
  int i;
  /* set the Mode Register Write Mode to 1 */
  outportb(0x3ce, 5);
  outportb(0x3cf, (inportb(0x3cf) & 0xfc) | 0x01);
  /* set the Map Mask Register to enable writes to pixel planes
     0, 1, and 3 and disable writes to pixel plane 2 */
  outport(0x3c4, 0x0b02);
  _SI = src;
  _DI = dest + 10;
  _ES = 0xa000;
  __emit__(CLD); /* assure that MOVSB increments SI and DI */
  for (i = 0; i < 7; i++)
  {
    _DX = shift_blocks[i][1]; /* load the length of each line for block */
    _BX = shift_blocks[i][0]; /* init middle loop for wrap value for block */

    __emit__(PUSH_DS);

    _DS = 0xa000;
    _AX = 50;          /* init middle loop for number of lines in block */

    __emit__(ADD_SI_BX, MOV_CX_DX, REP, MOVSB);
    __emit__(ADD_DI_BX, DEC_AX, JNE, 0xf5, POP_DS);
   }
  /* set the Mode Register Write Mode to 0 */
  outportb(0x3ce, 5);
  outportb(0x3cf, inportb(0x3cf) & 0xfc);
  /* set the Map Mask Register to enable writes to all pixel planes */
  outport(0x3c4, 0x0f02);
}
static unsigned int imag_blocks[6][2]={{10,32},{42,16},{58,8},
                                                        {66,4},{70,2},{72,1}};
void GetDSPimage(unsigned int dest, unsigned io_addr)
{
  int i;
  /* Map Mask register - set pixel planes 1, 2, and 3 to "0",
     pixel plane 0 to "1" */
  outport(0x3c4, 0x0102);
  _DX = io_addr;
  _DI = dest;
  _ES = 0xa000;
  __emit__(CLD); /* assure that INSW increments DI */
  for (i = 0; i < 6; i++)
  {
    /* point to fill element offset */
    _AX = imag_blocks[i][1]; /* load the length of each line for block */
    _BX = imag_blocks[i][0]; /* init middle loop for wrap value for block */
    _SI = 50;       /* init middle loop for number of lines in image block */

    __emit__(ADD_DI_BX); /* wrap destination pointer to next row of display */
    __emit__(MOV_CX_AX, REP, INSW, DEC_SI, JNE, 0xf7);
  }
  _DI += 1;                /* offset pointer by one */
  _CX = imag_blocks[5][0]; /* init middle loop for wrap value for block */
  _SI = 100;        /* init middle loop for number of lines in image block */
  __emit__(ADD_DI_BX, INSW, DEC_SI, JNE, 0xfa);
  /* Map Mask register - set pixel planes 0 - 3 to "1" */
  outport(0x3c4, 0x0f02);
}












Copyright © 1993, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.