Wilbert implemented the THRSim11's ports, timer, A/D converter, I/O pins, external components, interrupts, and special instructions. He currently works for the Royal Dutch Navy and can be contacted at [email protected]. Harry, who designed the architecture's Observer pattern, UI, and GUI, is an instructor at the Rijswijk Institute of Technology. He can be contacted at [email protected]. Alex, a systems designer for GTI Rotterdam Industries, developed THRSim11's external components via COM. He can be contacted at [email protected].
Sidebar: The THRSim11 68HC11 Simulator
The Wireless Object-Oriented Kindly Interfaced Emulator (WOOKIE) is a freely available Win32 emulator for 68HC11-based software development. When we designed and implemented the WOOKIE as a senior project at the Milwaukee School of Engineering (http://www .msoe.edu/), our goals included allowing program execution to start and stop at any time, even during ISRs. We wanted ease of use with a point-and-click GUI methodology. Most importantly, though, was our goal of expendability. The WOOKIE needed to be developed in only six months, half of which was spent doing research and design. Because of our time constraints, we designed the WOOKIE with an extendable architecture to allow new modules to be added. In the end, all the 68HC11 features we had planned on implementing were completed, but the WOOKIE can be further expanded.
In this article, we'll discuss both the development and use of the WOOKIE simulator. Source code and executable versions for the WOOKIE are available at http:// www.msoe.edu/eecs/ce/ceb/ resources/ and from DDJ (see "Resource Center," page 5). This includes a user's manual, programmer's documentation, and a demo application.
Designing the WOOKIE
The WOOKIE GUI was written using Visual C++ with MFC for Windows 95/NT. Some of the features we built into the WOOKIE include unique displays of I/O ports, registers, and memory. Other output devices, such as a 4×7 display and a pin scope, are also built in. We split the task of writing the WOOKIE into two major sections -- the GUI and the 68HC11 emulator core. Isolating the emulator core from the GUI created the possibility for another GUI to be written in a different platform, such as X Windows or Curses. We built a generic and flexible interface to integrate the GUI and the core with minimal connections.
To create a platform-independent emulator core, we implemented the entire core in Standard C++. We used an object-oriented approach by creating base classes for bytes and words. We decided not to use bit fields to represent the individual bits of bytes and words. The ANSI/ ISO Standard does not guarantee that bit fields will be ordered or packed in a consistent format. In cases where we needed bit-level access, we called inline accessors and mutators. These inline routines use shifting and masking to manipulate the bits.
One of the most difficult parts of the WOOKIE to design was the interface between the GUI and emulator core. This interface provides the means for displaying run time changes to I/O devices. Because polling adds unnecessary overhead, we decided to make the visual displays update asynchronously. To accomplish this, we decided to implement display classes in the GUI that inherited from pure virtual classes in the core. PortConnection (see Listing One) is one of these pure virtual classes.
The display class in the GUI implements graphical display changes by overriding the Write function of the PortConnection class. The emulator core calls the Write function when a port changes state. Because the Write function is implemented in the GUI, the display of the port is confined to the GUI.
The memory map of the 68HC11 varies between operation modes and chip models, so we needed to accommodate a constantly changing memory map. We accomplished this by creating a MemoryMap class, where memory could be written to and read from. The MemoryMap consists of a linked list of MemoryObjects, each with its own implementation of Read and Write functions. The MemoryObject is a base class that represents a contiguous block of memory. Specific memory blocks, such as the registers and RAM, require their own classes derived from the MemoryObject class. The implementations of the Read and Write operations are then hidden within the specific memory class. This way, even though writing to a register and to RAM is very different, the MemoryMap calls the Write routine of the associated MemoryObject. This design feature made remapping memory a simple operation.
Program execution was an area where we definitely needed to keep overhead to a minimum. The best way to minimize overhead when calling an instruction from the vast collection of instructions was to create an array of function pointers. The actual op-code value was the index into the function pointer array, thereby allowing instructions to be called dynamically. This method then requires a separate function to be written for every instruction. Although the instruction code is probably the most critical code to ensure an accurate emulator, the coding work for these instructions became somewhat tedious.
Another area of focus for our team was the timing. We had originally thought of having a general Clock function that would step the emulator along one clock at a time. We later decided against that idea because it required us to write each instruction in microcode. Instead, we created a general Step function that stepped the emulator through one instruction at a time. Since interrupts can't barge in on a running instruction, we felt that the execution of the program would not restrict timing resolution. To preserve the precise timing within the instructions, we wrote an internal_clock routine that each instruction would call when a clock period expired. As an example of our timing, Listing Two demonstrates the BRA (branch) instruction, which takes a total of three clock cycles to execute. The fetching of the op-code (outside the BRA instruction) expends one clock, so the BRA instruction is responsible for the remaining two clocks.
Using the WOOKIE
Now that we have explained most of the design of the WOOKIE, let's get down to the actual operation. After launching, the WOOKIE is ready to accept a compiled .S19 file. The listing file will also be read from the same directory and is displayed in the large window; see Figure 1. At program load time, the HC11 is reset. The WOOKIE waits for users to enter the operating mode and starting address of execution. If users don't remember the starting address, the WOOKIE finds it by parsing for the ORG command. If the program contains any variables using the RMB statement, then users may select "Load RMB Watches" from the Tools menu and all of the variables will be added to the memory-watch dialog window.
The WOOKIE is just a simulator -- it does not compile or link HC11 assembly or C programs (at least not currently). However, there are numerous free assemblers, compilers, and linkers available for the HC11. These programs commonly compile source code into a downloadable file format called a "Motorola S-file." In our case, an .S19 file is required to download code into the WOOKIE. The assembler we used is the AS11M assembler provided by Motorola. This can be downloaded free of charge from the Motorola or MSOE web site (see http:// www.msoe.edu/eecs/ce/ceb/resources/). This assembler takes the user's assembly code, assembles it into a .LST file and .S19 file. The .LST file is a text file that contains the user's source code, coupled with the generated machine code. The .LST file is used by the WOOKIE to display and step through the assembly language code. The .S19 file contains the actual machine code to be downloaded to the HC11 with some header information such as where to put the machine code and a CRC. Usually, the .S19 file is used by a Boot Loader program that runs on a PC and performs the physical download to the HC11. The WOOKIE requires that users have an .S19 file to download. If the .LST file does not exist, the simulator will still run, but no code will be displayed while it is running.
To start simulation, users may either step through the code line-by-line, or press the Go button to run the program at full speed. If execution is done at full speed, the program runs full blast without timing delays. The speed of the simulator depends on the speed of the host computer. Users may also adjust the flow of program execution by changing the value of the program counter in the MCU dialog window.
Many people enjoy stepping through their code line-by-line, but what happens during a timing loop? The WOOKIE supports breakpoints at any line of code so users don't have to labor through loops. By putting the address of the desired line of code in the breakpoint text box, the program stops execution at that line. This is useful for debugging inside interrupt service routines.
To handle I/O with the virtual HC11, users may utilize the I/O port dialog boxes and the IRQ and XIRQ pin buttons. After opening all the port dialog boxes, users can see the status of each port at all times. For the port pins that are configured as input pins, a left-mouse click on the pin toggles the pin high and low. To alter the active state of the LEDs, users must simply press the Active High or Active Low button. This helps to impersonate hardware that inverts output signals.
A Demo Program
The rest of the WOOKIE's features and functions are best demonstrated with a sample program. The sample program is called DEMO.ASM and was written for a project in an Operating Systems course. The program runs a multitasking kernel that schedules a maximum of eight processes at one time. Using this kernel program, we can write many different processes to do different things, allowing us to show eight different features of the WOOKIE at once.
Some of the operations involved within the kernel program include: a Knight-Rider display on some of Port A's LEDs, a real-time interrupt acting as a counter on Port D, and Ports B and C being used to send characters to a 4×7 display.
In the background, the real-time interrupt is being used to periodically increment the value of Port D. By watching Port D change, users can observe the RTI run on the HC11. Listing Three is an example of the real-time interrupt and the ISR. The sole function of one process is to display characters to the 4×7 display. A group of previously loaded messages are used in our example program. Port C is used to hold the index of the character to be written and Port B holds the ASCII value of the character to write. When the STRB pins go low, the display character is written out.
Also using the 4×7 display is a pair of ISRs -- the IRQ and XIRQ routines. By pressing the IRQ button, the IRQ becomes active and fires the IRQ interrupt. The IRQ ISR takes over the function of the HC11 and displays "IRQ" on the 4×7 display. By pressing the XIRQ button, the XIRQ ISR (see Listing Four) takes over the display ahead of the IRQ, and displays "XIRQ" on the display. Once the pins are deactivated again, regular operations continue.
The IRQ and XIRQ are not the only pins acting as inputs. The I/O port input pins (or pins that can be setup to be inputs) work in the same manner as the IRQ and XIRQ buttons. By clicking on the LED icon of the port pin, the pin will toggle. Port A pin 7 is setup to act as an input. When this pin is set to high, the pulse accumulator interrupt fires and turns Port D into a running counter; see Listing Five.
The WOOKIE also does a great job with pulse-width modulation functions. The kernel program has a process that sets up output compares OC1 and OC5 to toggle Port A pin 3 high and low, resulting in a square wave signal on that pin. Listing Six is the setup code.
The square wave on Port A pin 3 can be viewed using the Pin Scope dialog window. Use the Attach button of the Pin Scope to monitor a specific pin. In this case, PA3 should be monitored. Once the pin is chosen, the waveform begins to draw in the Pin Scope window. A slider bar is available to adjust the number of clock periods shown in the graph window. The Pin Scope also displays the current value of the period, the frequency of the signal (according to a 2-MHz E-clock), and the duty cycle. The signal can be changed during simulation by simply changing the value of the TOC1 register. A value of BFFF will produce a 75 percent duty cycle, 3FFF would make 25 percent, and so on.
DDJ
Listing One
class PortConnection {public: virtual void Write(byte_t) = 0; }; </p>
Listing Two
// BRA instructionvoid HC11:op_20h(HC11 *hc11) // HC11 passed in { // because function is static signed char offset; hc11->_clock(); offset = (signed)hc11->memory[hc11->PC]; hc11->PC++; hc11->_clock(); hc11->PC = hc11->PC + offset; } </p>
Listing Three
ldaa #%00000011 ; Set up RTI to 32 ms staa $1026 ; PACTL ldaa #%01000000 staa $1024 ; TMSK2 - enable RTI interrupts staa $1025 ; TFLG2 - clear RTI flag </p> rti_isr: </p> inc $1008 ; increment PORT D ldaa #%01000000 ; re-enables the RTI staa $1025 ; TFLG2 - rti ; return </p>
Listing Four
xirqstr fcc 'XIRQ' </p> XIRQ_ISR: ldx #xirqstr ldaa #3 staa $1003 ;PORT C ldaa 0,x staa $1004 ;PORT B ldaa #2 staa $1003 ;PORT C ldaa 1,x staa $1004 ;PORT B ldaa #1 staa $1003 ;PORT C ldaa 2,x staa $1004 ;PORT B ldaa #0 staa $1003 ;PORT C ldaa 3,x staa $1004 ;PORT B rti </p>
Listing Five
PULSE_ACCUMULATOR_RTI: inc $1008 ; increment PORT D bclr $1025 %00010000 ; TFLG2 - Clear pulse accumulator flag rti </p>
Listing Six
LDX #$1000 ;Base address for registers BSET $20,x %00000011 ;TCTL1: OC5 set high at TCNT=TOC5=0 BSET $23,x %10000000 ;TFLG1: Clear OC1F flag BSET $0C,x %00001000 ;OC1M: Have OC1 control OC5 BSET $0D,x %10000000 ;OC1D: Force OC5 pin low at OC1 compare LDD #$7FFF ;Since TOC5=0, setting TOC1 = $7FFF STD $16,x ; would give a 50% duty cycle
Copyright © 1999, Dr. Dobb's Journal