Introduction

Our project is a 3-dimensional game control for a video game displayed on a black and white television set.

Motivation and Overview

In the recent push in technology, many new computer and game interfaces have been created, many of which include wireless control. Our unit is a novel method for 3-dimensional control. The interface we have designed for this project has many other possible applications other than video game control. This may include 3-dimensional drawing or animation. For instance, instead of using a scroll mouse to zoom into and out of an image, it would be possible using 3-dimensional control to simply draw all of the dimensions of the figure out in air without zooming. Because of the limited time available to complete this project, however, we have written a video game to demonstrate how our interface functions and how it may be used.


The project is to realize a user-friendly control, in which the user simply grabs a ball and moves it in a three-dimensional space. The exact coordinates of the position of the ball are then calculated and outputted onto video using a 3-D to 2-D projection method that we have developed.

High Level Design

The three-dimensional video game project was inspired by a desire to
utilize sensors which track in three dimensions. Several recent technologies may serve as inspiration. In particular, the hugely popular Wii gaming system uses motions in all three dimensions for play. While the Wii served as inspiration, our interface is an original concept and more closely resembles a three-dimensional take with non-wireless sensing hardware because of hardware limitations.

The Concept

The controller consists of a ping-pong ball mounted in the center of a triangular board by three stretch sensors. The user controls the game by holding the ping-pong ball and moving it in the space above the board as desired to control the video game. We use a 3-D to 2-D projection method that directly maps the position of the ping pong ball to a position on the screen. The geometry of the board is shown in the appendix.

The output from the control unit is converted to 2D coordinates, which are used to generate video code displayed on a black and white TV. The overall data-path for the system is shown below.

Flowchart

Figure: Design Flowchart

 

Background Mathematics

The calculations used to compute our 3D to 2D projection were based on several known parameters and equations expressing the board's geometric properties. The equations, and our setup are as follows, a diagram showing the correspondence of the variable to the board is available in the appendix:

Figure: Photograph of controller.

Geometric Eqns

From these equations, with only x, y, and z being unknown, it was possible to use Mathematica to solve this system. The resulting equations are used to calculate the absolute positions of our coordinate system (given z2 and z3 are both zero):

Hardware-Software Balance

Implementing this data-path successfully requires a delicate combination of hardware and software. The two important functions of the software are (1) to calculate the 3D coordinate position using the data from the control unit and project it to two dimensions, and (2) to generate the video code. The main hardware components are the control unit, two Mega32 CPUs, and a black and white TV. Because one Mega32 CPU cannot carry out both functions mentioned above within one frame refresh of the tv, we have used two CPUs in the project. One Mega32 CPU is dedicated for 3-dimensional coordinate detection and 3D-to-2D projection. The resultant 2D coordinate data is transmitted to the second Mega32 CPU. The second CPU, in turn, plots the video game output to the black-and-white TV.

Standards and Trademarks

IEEE standards are not relevant to our project because we have no wireless components.

Hardware Design

The game controller is an essential hardware aspect for this project. The controller design was entirely original. The sensors used for position dectection are strech sensors whose resistances change as they are stretched (stretch sensor). The controller base is an equilateral triangle built from hardboard. A 4-inch length of stretch sensor is bolted to each corner of the triangle and tied to Vcc rail of the Mega32. The other end of the stretch sensors are each tied to one end of a 3-kΩ resistor and secured to a ping-pong ball. The other ends of the resistors are connected to the MCU ground rail. The voltages across the 3-kΩ resistors are fed into the A/D input of the MCU. The combination of the stretch sensors and resistors creates three voltage dividers. The voltages read at the A/D input of the MCU increases when the stretch sensor is less stretched, As a result, the voltage across the 3-kΩ resistors become higher. The sensors are sized to have an initial position in which all three are slightly extended. The ability to compute the absolute position is lost if any one sensor goes slack. This defines a playable space above the triangular frame. The following images show the hardware components:

Figure: Our STK500 setup

Figure:Our custom PC board.

Our hardware also contains a crucial set of two communicating Mega32 microcontrollers. The first Mega32 is mounted in a custom PC board while the second is on a STK500. Both are powered with AC 9-12V power supplies. The transmitting (first) Mega32 receives the voltage divider signals on Port A; Ports C and D of this MCU transmit calculated position data to the receiving Mega32's Ports A and C, respectively. These connections allow the transfer of the calculated x, y, and z positions. Pins B.0-B.3 of the transmitting and receiving MCUs are connected for synchronized communication signals. Additionally, Pin B.4 of the recieving end is linked to switch 0 on the STK500 to signal "walk mode" vs. "fly mode" of the game.
Finally the video code generated by the receiving microcontroller is outputted to a black and white television through the DAC circuit shown in the appendix and via a RCA phone jack.

Software Design

The software for this project is composed of three main portions: MCU communications, mathematical calculations, and video code.

Positioning Mathematics

All mathematical calculations used to determine exact position of the ping pong ball and the locations of the video output are performed on the Mega32 connected to the sensing system. By moving the mathematical calculations off of the video MCU, this allows more time for the video MCU to run, allowing it to print more video code.


The first step in calculating the position of the ping pong ball is to retrieve the data from the sensing system. First, the sensing system must be calibrated in order to provide a direct mapping between the A/D conversion and the distance the ping pong ball is from each of the three corners of the control interface base. This is simply done by entering values into six arrays. Three of the arrays contain the distance of the ping pong ball from each of the three corners of the control base. The other three arrays are the values that are read from the ADC inputs that correspond to the distances in the first three distance arrays. A direct mapping between the ADC values and the real distances are made by making linear extrapolations between every two data points in the distance arrays. Once the calibration is complete, the distance may be found in every subsequent use of the control system. The ADC inputs on Ports A.0-A.2 are then read by the code and the real distances are found based on the extrapolations and the calibration values.
Once the distances are found, the distance calculations must be made to determine the real (x,y,z) coordinate values of the ping pong balls. The equations are those shown in the High Level Design of this web page. However, rather than using floating mathematics to calculate the (x,y,z) values, fixed point arithmetic is used to reduce the number of clock cycles used by the MCU in order to decrease the amount of time it takes to perform these operations. The fixed point arithmetic functions we used are by courtesy of Bruce Land.


Once the fixed point values of the (x,y,z) positions are found, we must project these values onto a 2-D coordinate system to be sent to the video code. In our projection scheme, movement of the ball in the x-y plane projects onto the screen as semi-3-D motion where an object is moved upon a 3-D platform. Movement in the z-direction would cause the object to appear to lift from the platform. However, because of the noise existing in our hardware sensing system, we require a push-button be pushed to have the object move in the z-direction. Therefore, when the button is pushed, the video code is in “fly mode”. Otherwise, it is in “walk mode”. If this method of showing movement in the z-direction were not used, there would be too much noise and the video code would become very jittery and thus the interface would become difficult to use.


When all of the calculations for the 3-D to 2-D projection are completed and the result is scaled to the tv monitor pixel coordinate, the data is sent to the video Mega32. There are three sets of data that must be sent. They are labeled in variables as x_draw, y_draw, and y_track. The reason three values are required is because of the 3-D to 2-D mapping we have implemented. The x_draw is identical in both fly mode and walk mode. The two y values that are sent differentiates fly mode from walk mode in the video code. Y_track is the y position of the object at all times regardless of what mode the video code is in. Y_draw is the y position plus the z position. The rationale behind this is that when the object is moving in the z-direction, it appears to be moving in the y-direction on the tv monitor.

Communications

The communications code is based on a Master/Slave scheme. The MCU connected to the sensing system is the Master while the MCU connected to the tv is the Slave. The Master and the Slave communicate to each other using 4 bits to signal data ready, data received, and the data to be sent.
The following sequence is performed during the communications process:

  1. Master calculates data
  2. Master sets data ready bit to 1
  3. Slave sees that data ready bit is 1
  4. Slave receives and stores data
  5. Slave sets data received bit to 1
  6. Master sets data ready bit to 0
  7. Slave sets data received bit to 0
  8. Repeat

Based on this sequence, the data can be appropriately sent from the Master to the Slave. Otherwise, it is possible for the Master to begin to recalculate the data before the Slave has had a chance to finish fetching it.
In addition to this, another sequence is used to determine what data is to be sent from the Master to the Slave. This is necessary because three sets of 8-bit data must be sent to the Slave while only two ports are available for use in communication. To implement this, a series of switch-case statements is used in which the Slave sets two data bits to different values and the Master sends data based on those two values. The data bits are set by the Slave cycles through its values so that at any given time two out of the three data values are being transmitted. By using this method, it is ensured that the data sent is appropriately synchronized. Otherwise, the Slave may store the data from the two ports to incorrect variables because it would not know which two out of three data values are being sent at the moment.

Video

After the coordinate data has been sent to the video MCU, it is sent to be drawn onto the TV monitor. The video code uses several functions that Bruce Land wrote for our video game lab.

The object’s position is based on (x_draw, y_track) when the code is in “walk mode”, or when the button is not pushed. Similarly, when the button is pushed and the code is in “fly mode”, the object’s position is based on (x_draw, y_draw). In “fly mode”, the object’s shadow, whose position is (x_draw, y_track), is also drawn onto the screen in order to show the user how high in the z-direction the object actually is based on the z-position of the controller.

The video game we wrote is based loosely off of the arcade game Pac-man. The character controlled by the player is a pac-man-like figure that runs around the screen eating objects. Certain objects would increase the score while others would decrease the score. If the score ever reached a negative value, the pac-man dies. Some objects that the pac-man eats lay in the z=0 plane (directly on the platform), and some lay at a certain z value. The objects that are not directly on the plane have their (x,y) position on the platform indicated by a “shadow”.

Our game deviates from the original Pac-man because there are no ghosts and no maze. The reason behind removing the maze is because our character can move at all angles and in all directions. Limiting the direction of movement would reduce the usefulness of our control unit. If the player obtains a certain score, he would move onto the next level.

To add character to pac-man and also emphasize the range of motion, the character turns right, left, toward the user, and away from the user depending on the direction of travel. This is evaluated by keeping track of one set of previous x and y values and comparing them to the next set to be drawn. In the x direction, if the previous x value was less than the current x value, and the change in x is greater than the change in y, the left-facing character is drawn. Likewise, if the previous x value was greater than the current x value, and the change in x is greater than the change in y, then the right-facingcharacter is display. Detecting forward and back motion is slightly more complex because to add the illusion of 3D to the screen a change in y requires a change in x also. To detect a change in y, it is required that the delta x is smaller than the delta y. If this were true, then if the previous y value were less than the current y value, the character faces away from the user. If the opposite were true, the character faces the user. (Note: the origin for the TV is the upper lefthand corner and thus y increases down the screen).

Challenges Encountered

During the weeks we worked on this project we encountered several problems.

The first challenge was to gather useful data from the controller. On a data printout to hyperterm we encountered the strange error that only two of the three ADC conversions would appear correct at a time. After much time spent examining the ADC section of the Mega32 manual and attempts at varying code structure, we had assured ourselves that the software was functioning correctly but a solution still escaped us. Turning to hardware and tracing our signal path using an oscilloscope we finally discovered a potential source of error. The voltage dividers responding to the stretch sensors' changes in resistance had been designed based on an initial estimation of the range of resistance the sensors would provide. However, it was found that the 1-kΩ resistors we had originally used did not have enough resistance for there to be a significant voltage drop across to be measured. To solve this problem, we swapped the 1-kΩ resistors with 3-kΩ resistors. As a result, reasonable A to D values were observed.

Another issue which haunted us throughout the project was the stabilization of the position. This issue presented itself at nearly ever level of design. During the beginning of this project, the ADC values that were read from the sensing interface was extremely unstable. After spending much time testing various aspects of our system, it was found that this was caused by the poor conductivity of the stretch sensors that we used. In our setup, we connected the stretch sensors to the voltage divider circuit by crimping terminals to the ends of the sensors and connected to the rest of the circuit. The sensors are not made of conductive material and thus the crimps did not have enough contacts to the sensors to read stable values. To solve this problem, we wrapped stripped copper wire around the extra sensor beyond the crimp to increase the surface area of the sensor that is in contact with conductive material. After this was done, the ADC yielded results that were much more stable.

An interesting problem that still remains unresolved is that a slight delay is required in the function that fetches the video code coordinates. If the delay were removed, the figure drawn onto the screen becomes extremely jumpy, to the extent that the game is unplayable.

Results

The results of our three dimensional controller unit were excellent. The sensor is highly sensitive to motions in all three dimensions and the speed of printout to hyperterm is well beyond a human's fastest change in direction. The communication system between the two MCUs is also highly responsive and responds instantaneously by human standards. The video code executes sufficiently quickly so as to print to the screen without flickering, tearing, or printing debris, but is not organized to optimize the code execution time for the greatest possible amount of video content displayed.

There is no major safety concern associated with our project. The STK500 and the custom PC board are both running off of 9-12 V AC power supplies and these are isolated by their power supplies. The controller board's safety is loosely implemented. The current board setup could provide at worst a mild shock but we have never witnessed this in our many hours of play. If we were to try to modify the design for distribution this could be completely eliminated by insulating the wires on the edges of the stretch sensors. The game did not interfere at all with other projects nearby.

The controller and game were both designed to be very useable by both the designers and other users. As with most gaming systems, the first few times the game is played using the new control system some adjustment will be need. After a very short period of time, however, the user grows accustomed to the motion and boundaries of motion of the controller. Based on our design, the feel of the rubberish stretch sensors and the feel of the ping-pong ball in the hand are appealing and could be as comfortable during prolonged use as any repetitive motion can be.

Overall, our project was successful.

Conclusions

The overall expectations we had for project were met at its completion, however, there are several small details which were not what we expected to encounter and there are several things we might do differently if repeating our work.


By the end of our project the stretch sensors were processing motion at levels of speed and smoothness which met all expectations. Dealing with these stretch sensors and building the mechanical device of the control unit, however, took far more project time than we anticipated. The cause of the extra time needed was the delicacy of the sensors. Once set up the sensors were sturdy and only one breakage occurred but finding the optimal length and method of securing the sensors was trickier than planned.


The concept behind the video code would also be modified, if not replaced, if we repeated the project. While we successfully implemented the 3D to 2D conversion and made an entertaining game, the success of our sensors in detecting motion led us to feel that the two dimensional video display in which individual pixels are easily resolved by eye was not a demonstration method which did justice to the data being decoded from user motion.

Potential for Elaboration

 

There are many other interfaces which could be controlled via our system. Some exciting possibilities would be a robotic arm, a helicopter controller, virtual reality game control, or control for a GUI for 3D drawings and models (e.g. protein structure, mathematical graphs, CAD drawings). Another exciting possibility for continuing this project would be to redesign the controller in such a way that it provides tactile feedback to the user indicating resistance etc. from the simulated region in which you are moving.

Intellectual Property

The design for our controller was conceived solely from our own intellectual property. No parts sampled required signing of a non-disclosure form, and we were not reverse engineering any product. A patent search revealed only only vaguely similar products (closest product found). This indicates that there may be patent opportunities for our design. On the software side of the design, some code used for video display and the fixed point arithmetic operations was written by Bruce Land. The remainder was entirely original. The only potential legal considerations that needs to be considered for the technology we have choosen to use is the similarity of our video game character to the classic video game character PACMAN.

Ethical Considerations

At all times during the design and construction of this project the IEEE Code of Ethics was followed. First and foremost this required us to be constantly mindful of the safety of other lab members and any potential users. This entailed careful construction of all parts to be free from sharp edges of wire or wood. All AC power supplies were isolated at all times. Above and beyond physical health, we were mindful to always be kind and courteous in lab even in times of stress.

 Additionally, we constantly sought to promote technologies by truthfully representing our problems to those with more expertise, namely our TA and professor. We were also eager to accept ideas and even criticism from TAs, our professor, and our peers. We accurately represent our results and setbacks publicly on this website and we were happy to explain our work to anyone who approached us about it. We have also been very careful to provide all external credit to others whose work contributed to the final success of this project. Our system seeks to treat all users equally to the greatest extent possible and is based only on ability to view the monitor and move the controller.

No parts we used in our construction are monitor by outside organizations. If we were to use this product outside of an academic setting we would be sure to address any complications resulting from the similarity to the Pac-man game licensed in the U.S. by Midway.

           

Appendix

Schematics:

Triangle Coordinates

Figure: Coordinates of platform.

 

Figure: Game controller voltage dividers

Figure: DAC circuit between Mega32 and TV, courtesy of Bruce Land.

Figure: Dimensions of controller base. Units are in inches.

References

Bruce Land's Video Code

Bruce Land's Fixed pt

ATMEL Mega32 datasheet

Stretch Sensor Supply

476 webpage

Tools Used

Mathematica was used to solve the linear equations and extract the equations used for finding the (x,y,z) exact position.

CodeVision was used for C coding.

Macromedia Dreamweaver was used to assemble this webpage.

Cost/Parts List

Division of Tasks

We both worked on every aspect of this project. They include:

-Soldering/Hardware Setup

-Assembling the control unit

-Video/game code

-Mathematical calculations code

-Testing

-Website

Acknowledgements

Thanks to AMTEL.

Thanks to our friends and family for putting up with us during this project and the support and understanding why we haven't had much time to spend with you.

Last but not least, thanks to Bruce Land and the ECE476 TAs for your time and patience throughout this project and providing us with the opportunity for this experience.

Source Code

Calculations code

//  A to D test code

// NOTE -- You MUST MOUNT the Aref jumper

 

#include <Mega32.h>

#include <stdio.h>

#include <math.h>

#include <delay.h>

//I like these definitions

#define begin {

#define end   }

#define t1 10

#define x2 -1.708

#define x3 1.708

#define y2 3.084

#define y3 3.084

#define  x3sqr float2fix(2.917)

#define  y2sqr float2fix(9.511)

#define  y3sqr float2fix(9.511)

 

unsigned char time1;

 

float searchDist(char Ain, int sel);

void initialize();

void fetchData();

void getCoords();

 

char Ain1, Ain2, Ain3;            //raw A to D number

float d1, d2, d3;

float x, y, z;

int fix_x, fix_y, fix_z;

int scalex_x, scalex_y, scalex_z;

int scaley_x, scaley_y, scaley_z;

int x_track, y_track;

int x_draw, y_draw;

int fly_mode;

int CoordSend;

float float_x;

 

 

int fixd1, fixd2, fixd3;

 

int sqd1, sqx, sqy, sqroot;

int d1sqr, d2sqr, d3sqr;

int fix_x1, fix_x2, fix_x3, fix_y1, fix_y2, fix_y3;

int num_x, den_x, num_y, den_y;

 

int MuxCtrl;

int FirstData;

 

/*// inches

flash float dist1[15] = {5.5,6,6.5,7,7.5,8,8.5,9};

flash float v1[15] = {108,96,90,79,68,58,54,44};

flash float dist2[15] = {5,5.5,6,6.5,7,7.5,8};

flash float v2[15] = {102,87,80,73,66,56,52};

flash float dist3[15] = {5,5.5,6,6.5,7,7.5,8,8.5};

flash float v3[15] = {118,108,99,92,82,69,59,55};      */

 

// 3 inches

flash float dist1[15] = {1.83,2,2.167,2.33,2.5,2.67,2.83,3};

flash float volt1[15] = {108,96,90,79,68,58,54,44};

flash float dist2[15] = {1.67,1.83,2,2.167,2.33,2.5,2.67,2.83};

flash float volt2[15] = {102,87,80,73,66,56,52};

flash float dist3[15] = {1.67,1.83,2,2.167,2.33,2.5,2.67,2.83};

flash float volt3[15] = {118,108,99,92,82,69,59,55};

 

#pragma regalloc+

 

char syncON, syncOFF;

int LineCount;

int time;

char screen[1600];

 

 

//=== fixed conversion macros =========================================

#define int2fix(a)   (((int)(a))<<8)            //Convert char to fix. a is a char

//#define fix2int(a)   ((signed char)((a)>>8))    //Convert fix to char. a is an int

#define float2fix(a) ((int)((a)*256.0))         //Convert float to fix. a is a float

#define fix2float(a) ((float)(a)/256.0)         //Convert fix to float. a is an int

#define multfixSlow(a,b) ((int)((((long)(a))*((long)(b)))>>8)) //multiply two fixed #

 

 

//lsqrt is in math.h

#define sqrtfixSlow(a)   (lsqrt(((long)(a))<<8))   //square root

 

//==Fast fixed multiply=================================

char fix2int(int a)

begin

    #asm

    ldd r30, Y+1 ;moves the high byte of the input to low byte

    #endasm

end

//==Fast fixed multiply=================================

 

int multfix(int a,int b)

begin

    #asm

  ;*********************************************************************

;*

;* FUNCTION

;*     muls16x16_24

;* DECRIPTION

;*     Signed multiply of two 16bits numbers with 24bits result.

;* USAGE

;*     r31:r30:rxx = r23:r22 * r21:r20

 

;**********************************************************************

    ;push r20

    ;push r21

    mov  r24, r20

    mov  r25, r21

 

    LDD  R22,Y+2  ;load a

       LDD  R23,Y+3

 

       LD   R20,Y    ;load b

       LDD  R21,Y+1

 

       muls   r23, r21             ; (signed)ah * (signed)bh

       mov           r31, r0         ;r18, r0

       mul           r22, r20             ; al * bl

       mov     r30, r1         ;movw     r17:r16, r1:r0

       ;mov     r16, r0

       mulsu  r23, r20             ; (signed)ah * bl

       add           r30, r0         ;r17, r0

       adc           r31, r1         ;r18, r1

       mulsu  r21, r22             ; (signed)bh * al

       add           r30, r0         ;r17, r0

       adc           r31, r1         ;r18, r1

 

       ;pop r21

       ;pop r20

       mov r20, r24

       mov r21, r25

    #endasm

end

 

//========================================================

int sqrtfix(int aa)

begin

    // do not change the order of these variables

    // the compiler puts them in

    // r16:17, r18, r19, r20:21

    int a;

    char nextbit, ahigh;

    int root ;

    a = aa;

    #asm("mov r19, r17")     //set: ahigh = a>>8

    //

    // range sort to get integer part and to

    // check for weird bits near the top of the range

    if (ahigh >= 0x40)     //bigger than 64?

    begin

        if (a > 0x7e8f)    //>=126.562 = 11.25^2

        begin

            root = 0x0b40;  // 11

            nextbit = 0x10 ;

        end

        else if (ahigh >= 0x79)    //>=121

        begin

            root = 0x0b00;  // 11

            nextbit = 0x40 ;

        end

        else if (ahigh >= 0x64)    //>=100

        begin

            root = 0x0a00;  // 10

            nextbit = 0x80 ;

        end

        else if (ahigh >= 0x51)    //>=81

        begin

            root = 0x0900;  // 9

            nextbit = 0x80 ;

        end

        else   //64

        begin

            root = 0x0800;      //8

            nextbit = 0x80 ;

        end

    end

    else if  (ahigh >= 0x10)  //16    //smaller than 64 and bigger then 16

    begin

        if (ahigh >= 0x31)  //49

        begin

            root = 0x0700;      //7

            nextbit = 0x80 ;

        end

        else if (ahigh >= 0x24)  //36

        begin

            root = 0x0600;      //6

            nextbit = 0x80 ;

        end

        else if (ahigh >= 0x19)  //25

        begin

            root = 0x0500;      //5

            nextbit = 0x80 ;

        end

        else //16

        begin

            root = 0x0400;      //4

            nextbit = 0x80 ;

        end

    end

    else       //smaller than 16

    begin

         if (ahigh >= 0x09)  //9

        begin

            root = 0x0300;      //3

            nextbit = 0x80 ;

        end

        else if (ahigh >= 0x04)  //4

        begin

            root = 0x0200;      //2

            nextbit = 0x80 ;

        end

        else if (ahigh >= 0x01)  //1

        begin

            root = 0x0100;      //1

            nextbit = 0x80 ;

        end

        else     //less than one

        begin

            root = 0;

            nextbit = 0x80 ;

        end

    end

    // now get the low order bits

    // while (nextbit)

    // begin

        #asm

        beginloop:

        ;tst r18   ;dont need-- lsr at bottom of loop sets current SREG

        breq endit ; jumps out of loop if nextbit is zero

       ;r21:r20 contains root because of compiler trick

       ; root = nextbit + root;

       add r20, r18

       ;adc r21, r19    ; dont need: never any carry

        ;

        ; p =  multfix(root,root);

        mov r22, r20            ;load root into other mult input

        mov r23, r21

        ;

       muls   r23, r21             ; (signed)ah * (signed)bh

       mov           r31, r0         ;r18, r0

       mul           r22, r20             ; al * bl

       mov     r30, r1         ;movw     r17:r16, r1:r0

       mulsu  r23, r20             ; (signed)ah * bl

       add           r30, r0         ;r17, r0

       adc           r31, r1         ;r18, r1

       mulsu  r21, r22             ; (signed)bh * al

       add           r30, r0         ;r17, r0

       adc           r31, r1         ;r18, r1

       ; p is in r30:31

       ;

       ; if (p >= a) root = root - nextbit ;

       cp  r30, r16            ; compare root^2 with a

       cpc r31, r17

       brlt nosub

       sub r20, r18            ; if r^2 >= a form r=r-nextbit

       ;sbc r21, r19           ; dont need--high byte always zero

       nosub:

       ; nextbit = nextbit>>1 ;

       ;lsr r19                 ;shift nextbit right

       ;ror r18                  ;simplify--high byte always zero

       lsr r18

       rjmp beginloop

       endit:

       #endasm

   // end

    return root ;

end

 

//========================================================

int mult_opt(int a, int b)

begin

    #asm

    ;push r20

    ;push r21

    mov  r24,r20

    mov  r25,r21

 

    ldd  R22,Y+2  ;load a

    ldd  R23,Y+3

 

    ld  r20,Y   ;load b

    ldd r21,Y+1

 

    muls      r23, r21      ; (signed)ah * (signed)bh

    mov              r31, r0              ;

    mul              r22, r20      ; al * bl

    mov       r30, r1              ;

    ;mov      r16, r0

    mulsu     r23, r20      ; (signed)ah * bl

    add              r30, r0     ;

    adc              r31, r1     ;

    mulsu     r21, r22      ; (signed)bh * al

    add              r30, r0     ;

    adc              r31, r1     ;

 

    mov r20,r24

    mov r21,r25

    ;pop r21

    ;pop r20

    #endasm

end

 

//========================================================

int reciprocalfix(int dd)

begin

 

    signed char count, neg ;    //r16, r17

    int x, d ;    //r18:19, r20:21

 

    d=dd ;

    #asm

    clr r16     ;count = 0;

    clr r17     ;neg = 0;

    ;only works with + numbers

    ;if (d & 0x8000)

    ;begin

    ;     neg = 1;

    ;     d = -d ;

    ;end

    tst r21     ;d negative?

    brpl notneg

      ldi r17, 1    ; neg = 1;  negative flag

      clr r18       ; d = -d ;

      clr r19

      sub r18, r20

      sbc r19, r21

      mov r20, r18

      mov r21, r19

    notneg:

    #endasm

 

    // range reduction

    // bigger than one

    while (d>0x0100)

    begin

        --count ;

        d >>= 1 ;

    end

    // less than 1/2

    while (d<0x0080)

    begin

        ++count ;

        d <<= 1 ;

    end

 

    // Newton interation

   //x = 0x02ea - (d<<1) ;

    //x = multfix(x, 0x0201 + ~multfix(d,x));

    //x = multfix(x, 0x0200-multfix(d,x));

    #asm

    ; form initial x

    ldi r22, 0xea ;load the constant 2.914

    ldi r23, 0x02

    mov r24, r20  ; copy d

    mov r25, r21

    lsl r24       ; and form 2*d

    rol r25

    sub r22, r24  ; form 2.914-2*d

    sbc r23, r25

    mov r18, r22  ; store x

    mov r19, r23

    ;mov r22, r18    ; load x

    ;mov r23, r19

    ;mov  r20,r20   ; load d

    ;mov r21,r21

    muls      r23, r21      ; (signed)ah * (signed)bh

    mov              r31, r0              ;

    mul              r22, r20      ; al * bl

    mov       r30, r1              ;

    ;mov      r16, r0

    mulsu     r23, r20      ; (signed)ah * bl

    add              r30, r0     ;

    adc              r31, r1     ;

    mulsu     r21, r22      ; (signed)bh * al

    add              r30, r0     ;

    adc              r31, r1     ;

    com r30

    com r31

    ldi r24, 0x01

    ldi r25, 0x02

    add r30, r24

    adc r31, r25

    mov r22, r18  ;load x

    mov r23, r19

    mov r20,r30   ;load 2-d*x

    mov r21,r31

    muls      r23, r21      ; (signed)ah * (signed)bh

    mov              r31, r0              ;

    mul              r22, r20      ; al * bl

    mov       r30, r1              ;

    ;mov      r16, r0

    mulsu     r23, r20      ; (signed)ah * bl

    add              r30, r0     ;

    adc              r31, r1     ;

    mulsu     r21, r22      ; (signed)bh * al

    add              r30, r0     ;

    adc              r31, r1     ;

 

 

    ; range expansion

    ; if (count>0)  x = x<<count ;

    ; else if (count<0) x = x>>(-count) ;

    tst r16

    breq recipEnd

    brmi recipNeg

 

    lsl r30

    rol r31

    dec r16

    breq recipEnd

    lsl r30

    rol r31

    dec r16

    breq recipEnd

    lsl r30

    rol r31

    dec r16

    breq recipEnd

    lsl r30

    rol r31

    dec r16

    breq recipEnd

    lsl r30

    rol r31

    dec r16

    breq recipEnd

    lsl r30

    rol r31

    dec r16

    breq recipEnd

    lsl r30

    rol r31

    dec r16

    rjmp  recipEnd

 

    recipNeg:

    lsr r31

    ror r30

    inc r16

    breq recipEnd

    lsr r31

    ror r30

    inc r16

    breq recipEnd

    lsr r31

    ror r30

    inc r16

    breq recipEnd

    lsr r31

    ror r30

    inc r16

    breq recipEnd

    lsr r31

    ror r30

    inc r16

    breq recipEnd

    lsr r31

    ror r30

    inc r16

    breq recipEnd

    lsr r31

    ror r30

    inc r16

    breq recipEnd

    lsr r31

    ror r30

    inc r16

 

    recipEnd:

    ; fix sign

    ; if (neg==1) x=-x;

    tst r17  ;neg flag set?

    breq recip1  ;if not, do nothing

    clr r24

    clr r25

    sub r24, r30

    sbc r25, r31

    mov r30, r24

    mov r31, r25

 

    recip1:

    ; return x  in r30:31

    #endasm

end

//========================================================

int divfix(int nn, int dd)

begin

     signed char count, neg ;    //r16, r17

    int x, d ;    //r18:19, r20:21

    int n ;

    d=dd ;

    n=nn ;

    #asm

    clr r16     ;count = 0;

    clr r17     ;neg = 0;

    ;only works with + numbers

    ;if (d & 0x8000)

    ;begin

    ;     neg = 1;

    ;     d = -d ;

    ;end

    tst r21     ;d negative?

    brpl Dnotneg

      ldi r17, 1    ; neg = 1;  negative flag

      clr r18       ; d = -d ;

      clr r19

      sub r18, r20

      sbc r19, r21

      mov r20, r18

      mov r21, r19

    Dnotneg:

    #endasm

 

    // range reduction

    // bigger than one

    while (d>0x0100)

    begin

        --count ;

        d >>= 1 ;

    end

    // less than 1/2

    while (d<0x0080)

    begin

        ++count ;

        d <<= 1 ;

    end

    // Newton interation

   //x = 0x02ea - (d<<1) ;

    //x = multfix(x, 0x0201 + ~multfix(d,x));

    //x = multfix(x, 0x0200-multfix(d,x));

    #asm

    ; form initial x

    ldi r22, 0xea ;load the constant 2.914

    ldi r23, 0x02

    mov r24, r20  ; copy d

    mov r25, r21

    lsl r24       ; and form 2*d

    rol r25

    sub r22, r24  ; form 2.914-2*d

    sbc r23, r25

    mov r18, r22  ; store x

    mov r19, r23

    ;mov r22, r18    ; load x

    ;mov r23, r19

    ;mov  r20,r20   ; load d

    ;mov r21,r21

    muls      r23, r21      ; (signed)ah * (signed)bh

    mov              r31, r0              ;

    mul              r22, r20      ; al * bl

    mov       r30, r1              ;

    ;mov      r16, r0

    mulsu     r23, r20      ; (signed)ah * bl

    add              r30, r0     ;

    adc              r31, r1     ;

    mulsu     r21, r22      ; (signed)bh * al

    add              r30, r0     ;

    adc              r31, r1     ;

    com r30

    com r31

    ldi r24, 0x01

    ldi r25, 0x02

    add r30, r24

    adc r31, r25

    mov r22, r18  ;load x

    mov r23, r19

    mov r20,r30   ;load 2-d*x

    mov r21,r31

    muls      r23, r21      ; (signed)ah * (signed)bh

    mov              r31, r0              ;

    mul              r22, r20      ; al * bl

    mov       r30, r1              ;

    ;mov      r16, r0

    mulsu     r23, r20      ; (signed)ah * bl

    add              r30, r0     ;

    adc              r31, r1     ;

    mulsu     r21, r22      ; (signed)bh * al

    add              r30, r0     ;

    adc              r31, r1     ;

 

 

    ; range expansion

    ; if (count>0)  x = x<<count ;

    ; else if (count<0) x = x>>(-count) ;

    tst r16

    breq DrecipEnd

    brmi DrecipNeg

 

    lsl r30

    rol r31

    dec r16

    breq DrecipEnd

    lsl r30

    rol r31

    dec r16

    breq DrecipEnd

    lsl r30

    rol r31

    dec r16

    breq DrecipEnd

    lsl r30

    rol r31

    dec r16

    breq DrecipEnd

    lsl r30

    rol r31

    dec r16

    breq DrecipEnd

    lsl r30

    rol r31

    dec r16

    breq DrecipEnd

    lsl r30

    rol r31

    dec r16

    rjmp  DrecipEnd

 

    DrecipNeg:

    lsr r31

    ror r30

    inc r16

    breq DrecipEnd

    lsr r31

    ror r30

    inc r16

    breq DrecipEnd

    lsr r31

    ror r30

    inc r16

    breq DrecipEnd

    lsr r31

    ror r30

    inc r16

    breq DrecipEnd

    lsr r31

    ror r30

    inc r16

    breq DrecipEnd

    lsr r31

    ror r30

    inc r16

    breq DrecipEnd

    lsr r31

    ror r30

    inc r16

    breq DrecipEnd

    lsr r31

    ror r30

    inc r16

 

    DrecipEnd:

    ; fix sign

    ; if (neg==1) x=-x;

    tst r17  ;neg flag set?

    breq Drecip1  ;if not, do nothing

    clr r24

    clr r25

    sub r24, r30

    sbc r25, r31

    mov r30, r24

    mov r31, r25

 

    Drecip1:

    ; x = multfix(x,nn) ;

    mov r22, r30

    mov r23, r31

    ldd r20,Y+6   ; load n

    ldd r21,Y+7

    muls      r23, r21      ; (signed)ah * (signed)bh

    mov              r31, r0              ;

    mul              r22, r20      ; al * bl

    mov       r30, r1              ;

    ;mov      r16, r0

    mulsu     r23, r20      ; (signed)ah * bl

    add              r30, r0     ;

    adc              r31, r1     ;

    mulsu     r21, r22      ; (signed)bh * al

    add              r30, r0     ;

    adc              r31, r1     ;

    #endasm

end

 

 

//========================================================

 

signed char cIn; //an input integer

float fIn;       //an input float

char fInString[16];

 

signed int cInFix, fInFix, prod, root, ratio, s, c;

 

 

 

 

interrupt [TIM0_COMP] void timer0_compare(void)

begin

  //Decrement the time if not already zero

  if (time1>0)

    --time1;

end

 

 

float searchDist(char Ain, int sel)

{

  int k;

  float currDistance, nextDistance, nextVoltage;

  float prevDistance;

  float prevVoltage;

  int m, b;

 

  for(k = 0; k < 13; k++)

  {

    if(sel == 1)

    {

         prevVoltage = volt1[k];

         prevDistance = dist1[k];

         nextVoltage = volt1[k+1];

         nextDistance = dist1[k+1];

    }

    else if(sel == 2)

    {

         prevVoltage = volt2[k];

         prevDistance = dist2[k];

         nextVoltage = volt2[k+1];

         nextDistance = dist2[k+1];

    }

    else

    {

         prevVoltage = volt3[k];

         prevDistance = dist3[k];

         nextVoltage = volt3[k+1];

         nextDistance = dist3[k+1];

    }

 

     // value found; extrapolate

     if((Ain <= prevVoltage) && (Ain > nextVoltage))

     {

        m = divfix(( float2fix(nextDistance) - float2fix(prevDistance)),(float2fix(nextVoltage) - float2fix(prevVoltage)));

        b = float2fix(nextDistance) - mult_opt(m,float2fix(nextVoltage));

        currDistance = fix2float(mult_opt(m,float2fix(Ain))+ b);

     }

     else

     {

        prevDistance = nextDistance;

        prevVoltage = nextVoltage;

     }

  }

 

  return currDistance;

}

 

void main(void)

begin

 

   initialize();

 

   while (1)

   begin

           if(time1 == 0)

           {

             fetchData();

             time1 = t1;

           }

 

           if(MuxCtrl == 3)

           {

             getCoords();

           }

 

 

  end

end

 

// find exact position (x,y,z)

void getCoords()

{

 

    PORTB.1 = 0;    // data not ready

 

    num_x = ((mult_opt(d1sqr,fix_y2))-(mult_opt(d3sqr,fix_y2))-(mult_opt(d1sqr,fix_y3))+(mult_opt(d2sqr,fix_y3)));

    den_x = float2fix(21.0699);

    fix_x = divfix(num_x,den_x);

 

 

    num_y = ((mult_opt(-d1sqr,fix_x2))+(mult_opt(d3sqr,fix_x2))+(mult_opt(d1sqr,fix_x3))-(mult_opt(d2sqr,fix_x3)) + float2fix(42.455));

    den_y = float2fix(21.0699);

    fix_y = divfix(num_y, den_y);

 

 

    sqd1 = mult_opt(fixd1,fixd1);

    sqx = mult_opt(fix_x,fix_x);

    sqy = mult_opt(fix_y,fix_y);

    sqroot = sqd1 - sqx - sqy;

 

    if(sqroot < 0)

        sqroot = 0;

 

    fix_z = sqrtfix(sqroot);

    if(fix_z < 0)

       fix_z = 0;

 

    x_draw = ((mult_opt(fix_x,(scalex_x<<8))+(165<<8)) >>6)-550;

    y_draw = (mult_opt(fix_y,(scaley_y<<8))>>2) -(mult_opt(fix_z, scaley_z<<8)>>8)-330;

    y_track = (mult_opt(fix_y,(scaley_y<<8))>>2)-330;

 

    printf("x_draw: %d, y_draw: %d, y_track: %d\n\r", (x_draw>>2)+50, (y_draw>>2)+20, (y_track>>2)+20);

 

 

    // B.0 = data received

    // B.1 = data ready

    // B.2 = fly mode bit 1

    // B.3 = fly mode bit 0

 

    // a/d chip is the Master

 

       if(PINB.0 == 0)   //was last data received?

    {

              if((PINB.2 == 0) && (PINB.2 == 0)) // walk mode

        {

                PORTC = x_draw;

             PORTD = y_draw;

          PORTB.1 = 1;    //data ready

        }

        else

        {

               if((PINB.2 == 0) && (PINB.3 == 1))

               {

                            CoordSend = 1;

               }

               else if((PINB.2 == 1) && (PINB.3 == 0))

               {

                     CoordSend = 2;

               }

               else if((PINB.2 == 1) && (PINB.3 == 1))

               {

                     CoordSend = 3;

               }

 

              switch (CoordSend)

            begin

              case 1:

                     PORTC = x_draw;

                                    PORTD = y_draw;

                break;

                case 2:

                       PORTC = y_track;

                                  PORTD = y_draw;

                break;

                case 3:

                       PORTC = y_track;

                                  PORTD = x_draw;

                break;

            end

            PORTB.1 = 1;    //data ready

        }

    }

    if(PINB.0 == 1)

    {

      PORTB.1 = 0;

    }

}

 

// gets data from a/d input

void fetchData()

{

    switch(MuxCtrl)

    begin

      case 1:

         Ain1 = ADCH;

         d1 = searchDist(Ain1, 1);

          fixd1 = float2fix(d1);

          d1sqr = mult_opt(fixd1,fixd1);

         MuxCtrl++;

         ADMUX = 0b01100001;

       break;

 

      case 2:

         Ain2 = ADCH;

         d2 = searchDist(Ain2, 2);

         d2sqr = mult_opt(fixd2,fixd2);

          fixd2 = float2fix(d2);

         MuxCtrl++;

         ADMUX = 0b01100010;

      break;

 

      case 3:

             Ain3 = ADCH;

             d3 = searchDist(Ain3, 3);

             fixd3 = float2fix(d3);

          d3sqr = mult_opt(fixd3,fixd3);

             MuxCtrl = 1;

             ADMUX = 0b01100000;

      break;

 

    end

    ADCSR.6 = 1;

}

 

 

void initialize()

{

  //set up timer 0 for 1 mSec timebase

  TIMSK=2;           //turn on timer 0 cmp match ISR

  OCR0 = 250;        //set the compare re to 250 time ticks

  //prescalar to 64 and turn on clear-on-match

  TCCR0=0b00001011;

  ADCSR = 0b11000111;

  ADMUX = 0b01100000;

  DDRA = 0x00; //input (data from controller)

  DDRC = 0xFF; //outputs

  DDRD = 0xFF;

  DDRB.0 = 0;

  DDRB.1 = 1;

  DDRB.2 = 0;

  DDRB.3 = 0;

 

  fix_x2 = float2fix(x2);

  fix_x3 = float2fix(x3);

  fix_y2 = float2fix(y2);

  fix_y3 = float2fix(y3);

 

  scalex_x = 50;

  scalex_y = -1;

  scalex_z = 0;

  scaley_x = 0;

  scaley_y = 3;

  scaley_z = 50;

 

  //init the UART

   UCSRB = 0x18;

   UBRRL = 103;

   printf("starting...\n\r"); 

 

   MuxCtrl = 1;

   time1 = t1;

 

  //crank up the ISRs

  #asm

 

       sei

  #endasm

 

}

 

Video code


//video gen with fixed pint animationi

//D.5 is sync:1000 ohm + diode to 75 ohm resistor

//D.6 is video:330 ohm + diode to 75 ohm resistor

 

#pragma regalloc-    //I allocate the registers myself

#pragma optsize-     //optimize for speed

 

#include <Mega32.h>

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include <delay.h>

 

//cycles = 63.625 * 16 Note NTSC is 63.55

//but this line duration makes each frame exactly 1/60 sec

//which is nice for keeping a realtime clock

#define lineTime 1018

 

#define begin {

#define end   }

#define ScreenTop 30

#define ScreenBot 230

 

#define g 2   

#define right 1

#define left 2

#define forward 3

#define backward 4

 

 

int fly_mode;

int neg = -1;

int y_track;                

int wPlatform, fPlatform;

int x_draw, y_draw,sx_draw,sy_draw,sy_track;

int prev_x_draw, prev_y_track,prev2_y_track,prev2_x_draw;

int delt_x_draw, delt_y_track;

char O_pos; 

int calcO;

int open; 

int dir;         

int off;

int eat;

int fheight;

 

int Ox,Oy,Ox_shadow,Oy_shadow,Xx,Xy,Xy_shadow,time1, gametime,score;

 

int check;   

int feat;                          

 

void initialize();

 

void getCoords();

 

int MuxCtrl;

int CoordCtrl;

int Align;    

 

// 3 inches

flash float dist1[15] = {1.83,2,2.167,2.33,2.5,2.67,2.83,3};

flash float volt1[15] = {108,96,90,79,68,58,54,44};

flash float dist2[15] = {1.67,1.83,2,2.167,2.33,2.5,2.67,2.83};

flash float volt2[15] = {102,87,80,73,66,56,52};

flash float dist3[15] = {1.67,1.83,2,2.167,2.33,2.5,2.67,2.83};

flash float volt3[15] = {118,108,99,92,82,69,59,55};

 

 

 

//NOTE that v1 to v8 and i must be in registers!

register char v1 @4;

register char v2 @5;

register char v3 @6;

register char v4 @7;

register char v5 @8;

register char v6 @9;

register char v7 @10;

register char v8 @11;

register int i @12;

 

#pragma regalloc+

 

char syncON, syncOFF;

int LineCount;

int time;

char screen[1600];

/*  //platform

  video_line(33,70,93,70,1);

  video_line(53,30,113,30,1);

  video_line(33,70,53,30,1);

  video_line(93,70,113,30,1);

 

  //3D

  video_line(33,70,33,74,1);

  video_line(93,70,93,74,1);

  video_line(113,30,113,34,1);

  video_line(33,74,93,74,1);

  video_line(93,74,113,34,1);   */

 

flash char x_pacpt1 [22] = {45,50,55,60,65,70,75,80,85,90,94,45,50,55,60,65,70,75,80,85,90,94};

flash char y_pacpt1 [22] = {77,79,81,89,77,75,77,79,81,79,77,73,70,78,70,73,75,73,70,88,80,83};

 

flash char x_pacpt2 [22] = {45,50,55,60,65,70,75,80,85,90,94,96,98,96,94,90,85,80,75,70};

flash char y_pacpt2 [22] = {46,44,42,50,59,40,58,50,53,50,52,54,57,61,66,59,53,62,55,60};

flash char y_base2 [22]  = {77,83,83,85,79,75,78,88,78,80,75,85,85,81,83,88,75,72,80,73}; 

 

//=== fixed conversion macros =========================================

#define int2fix(a)   (((int)(a))<<8)            //Convert char to fix. a is a char

//#define fix2int(a)   ((signed char)((a)>>8))    //Convert fix to char. a is an int

#define float2fix(a) ((int)((a)*256.0))         //Convert float to fix. a is a float

#define fix2float(a) ((float)(a)/256.0)         //Convert fix to float. a is an int

#define multfixSlow(a,b) ((int)((((long)(a))*((long)(b)))>>8)) //multiply two fixed #

 

//Point plot lookup table

//One bit masks

flash char pos[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};

 

//================================

//3x5 font numbers, then letters

//packed two per definition for fast

//copy to the screen at x-position divisible by 4

flash char smallbitmap[40][5]={

       //0

    0b11101110,

       0b10101010,

       0b10101010,

       0b10101010,

       0b11101110,

       //1

       0b01000100,

       0b11001100,

       0b01000100,

       0b01000100,

       0b11101110,

       //2

       0b11101110,

       0b00100010,

       0b11101110,

       0b10001000,

       0b11101110,

       //3

       0b11101110,

       0b00100010,

       0b11101110,

       0b00100010,

       0b11101110,

       //4

       0b10101010,

       0b10101010,

       0b11101110,

       0b00100010,

       0b00100010,

       //5

       0b11101110,

       0b10001000,

       0b11101110,

       0b00100010,

       0b11101110,

       //6

       0b11001100,

       0b10001000,

       0b11101110,

       0b10101010,

       0b11101110,

       //7

       0b11101110,

       0b00100010,

       0b01000100,

       0b10001000,

       0b10001000,

       //8

       0b11101110,

       0b10101010,

       0b11101110,

       0b10101010,

       0b11101110,

       //9

       0b11101110,

       0b10101010,

       0b11101110,

       0b00100010,

       0b01100110,

       //:

       0b00000000,

       0b01000100,

       0b00000000,

       0b01000100,

       0b00000000,

       //=

       0b00000000,

       0b11101110,

       0b00000000,

       0b11101110,

       0b00000000,

       //blank

       0b00000000,

       0b00000000,

       0b00000000,

       0b00000000,

       0b00000000,

       //A

       0b11101110,

       0b10101010,

       0b11101110,

       0b10101010,

       0b10101010,

       //B

       0b11001100,

       0b10101010,

       0b11101110,

       0b10101010,

       0b11001100,

       //C

       0b11101110,

       0b10001000,

       0b10001000,

       0b10001000,

       0b11101110,

       //D

       0b11001100,

       0b10101010,

       0b10101010,

       0b10101010,

       0b11001100,

       //E

       0b11101110,

       0b10001000,

       0b11101110,

       0b10001000,

       0b11101110,

       //F

       0b11101110,

       0b10001000,

       0b11101110,

       0b10001000,

       0b10001000,

       //G

       0b11101110,

       0b10001000,

       0b10001000,

       0b10101010,

       0b11101110,

       //H

       0b10101010,

       0b10101010,

       0b11101110,

       0b10101010,

       0b10101010,

       //I

       0b11101110,

       0b01000100,

       0b01000100,

       0b01000100,

       0b11101110,

       //J

       0b00100010,

       0b00100010,

       0b00100010,

       0b10101010,

       0b11101110,

       //K

       0b10001000,

       0b10101010,

       0b11001100,

       0b11001100,

       0b10101010,

       //L

       0b10001000,

       0b10001000,

       0b10001000,

       0b10001000,

       0b11101110,

       //M

       0b10101010,

       0b11101110,

       0b11101110,

       0b10101010,

       0b10101010,

       //N

       0b00000000,

       0b11001100,

       0b10101010,

       0b10101010,

       0b10101010,

       //O

       0b01000100,

       0b10101010,

       0b10101010,

       0b10101010,

       0b01000100,

       //P

       0b11101110,

       0b10101010,

       0b11101110,

       0b10001000,

       0b10001000,

       //Q

       0b01000100,

       0b10101010,

       0b10101010,

       0b11101110,

       0b01100110,

       //R

       0b11101110,

       0b10101010,

       0b11001100,

       0b11101110,

       0b10101010,

       //S

       0b11101110,

       0b10001000,

       0b11101110,

       0b00100010,

       0b11101110,

       //T

       0b11101110,

       0b01000100,

       0b01000100,

       0b01000100,

       0b01000100,

       //U

       0b10101010,

       0b10101010,

       0b10101010,

       0b10101010,

       0b11101110,

       //V

       0b10101010,

       0b10101010,

       0b10101010,

       0b10101010,

       0b01000100,

       //W

       0b10101010,

       0b10101010,

       0b11101110,

       0b11101110,

       0b10101010,

       //X

       0b00000000,

       0b10101010,

       0b01000100,

       0b01000100,

       0b10101010,

       //Y

       0b10101010,

       0b10101010,

       0b01000100,

       0b01000100,

       0b01000100,

       //Z

       0b11101110,

       0b00100010,

       0b01000100,

       0b10001000,

       0b11101110,

       //-

       0b00000000,

       0b00000000,

       0b11101110,

       0b00000000,

       0b00000000

       };

// see: http://instruct1.cit.cornell.edu/courses/ee476/FinalProjects/s2005

/dp93/index.html

 

flash char ascii[131][7] = {

//0

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//1

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//2

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//3

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//4

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//5

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//6

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//7

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//8

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//9

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//10

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//11

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//12

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//13

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//14

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//15

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//16

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//17

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//18

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//19

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//20

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//21

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//22

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//23

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//24

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//25

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//26

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//27

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//28

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//29

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//30

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//31

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//32 Space

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//33 Exclamation !

0b01100000,

0b01100000,

0b01100000,

0b01100000,

0b00000000,

0b00000000,

0b01100000,

//34 Quotes "

0b01010000,

0b01010000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//35 Number #

0b00000000,

0b01010000,

0b11111000,

0b01010000,

0b11111000,

0b01010000,

0b00000000,

//36 Dollars $

0b01110000,

0b10100000,

0b10100000,

0b01110000,

0b00101000,

0b00101000,

0b01110000,

//37 Percent %

0b01000000,

0b10101000,

0b01010000,

0b00100000,

0b01010000,

0b10101000,

0b00010000,

//38 Ampersand &

0b00100000,

0b01010000,

0b10100000,

0b01000000,

0b10101000,

0b10010000,

0b01101000,

//39 Single Quote '

0b01000000,

0b01000000,

0b01000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//40 Left Parenthesis (

0b00010000,

0b00100000,

0b01000000,

0b01000000,

0b01000000,

0b00100000,

0b00010000,

//41 Right Parenthesis )

0b01000000,

0b00100000,

0b00010000,

0b00010000,

0b00010000,

0b00100000,

0b01000000,

//42 Star *

0b00010000,

0b00111000,

0b00010000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//43 Plus +

0b00000000,

0b00100000,

0b00100000,

0b11111000,

0b00100000,

0b00100000,

0b00000000,

//44 Comma ,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00010000,

0b00010000,

//45 Minus -

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b11111000,

0b00000000,

0b00000000,

//46 Period .

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00010000,

// 47 Backslahs /

0b00000000,

0b00001000,

0b00010000,

0b00100000,

0b01000000,

0b10000000,

0b00000000,

// 48 Zero

0b01110000,

0b10001000,

0b10011000,

0b10101000,

0b11001000,

0b10001000,

0b01110000,

//49 One

0b00100000,

0b01100000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b01110000,

//50 two

0b01110000,

0b10001000,

0b00001000,

0b00010000,

0b00100000,

0b01000000,

0b11111000,

 //51 Three

0b11111000,

0b00010000,

0b00100000,

0b00010000,

0b00001000,

0b10001000,

0b01110000,

//52 Four

0b00010000,

0b00110000,

0b01010000,

0b10010000,

0b11111000,

0b00010000,

0b00010000,

//53 Five

0b11111000,

0b10000000,

0b11110000,

0b00001000,

0b00001000,

0b10001000,

0b01110000,

//54 Six

0b01000000,

0b10000000,

0b10000000,

0b11110000,

0b10001000,

0b10001000,

0b01110000,

//55 Seven

0b11111000,

0b00001000,

0b00010000,

0b00100000,

0b01000000,

0b10000000,

0b10000000,

//56 Eight

0b01110000,

0b10001000,

0b10001000,

0b01110000,

0b10001000,

0b10001000,

0b01110000,

//57 Nine

0b01110000,

0b10001000,

0b10001000,

0b01111000,

0b00001000,

0b00001000,

0b00010000,

//58 :

0b00000000,

0b00000000,

0b00100000,

0b00000000,

0b00000000,

0b00000000,

0b00100000,

//59 ;

0b00000000,

0b00000000,

0b00100000,

0b00000000,

0b00000000,

0b00100000,

0b00100000,

//60 <

0b00000000,

0b00011000,

0b01100000,

0b10000000,

0b01100000,

0b00011000,

0b00000000,

//61 =

0b00000000,

0b00000000,

0b01111000,

0b00000000,

0b01111000,

0b00000000,

0b00000000,

//62 >

0b00000000,

0b11000000,

0b00110000,

0b00001000,

0b00110000,

0b11000000,

0b00000000,

//63 ?

0b00110000,

0b01001000,

0b00010000,

0b00100000,

0b00100000,

0b00000000,

0b00100000,

//64 @

0b01110000,

0b10001000,

0b10111000,

0b10101000,

0b10010000,

0b10001000,

0b01110000,

//65 A

0b01110000,

0b10001000,

0b10001000,

0b10001000,

0b11111000,

0b10001000,

0b10001000,

//B

0b11110000,

0b10001000,

0b10001000,

0b11110000,

0b10001000,

0b10001000,

0b11110000,

//C

0b01110000,

0b10001000,

0b10000000,

0b10000000,

0b10000000,

0b10001000,

0b01110000,

//D

0b11110000,

0b10001000,

0b10001000,

0b10001000,

0b10001000,

0b10001000,

0b11110000,

//E

0b11111000,

0b10000000,

0b10000000,

0b11111000,

0b10000000,

0b10000000,

0b11111000,

//F

0b11111000,

0b10000000,

0b10000000,

0b11111000,

0b10000000,

0b10000000,

0b10000000,

//G

0b01110000,

0b10001000,

0b10000000,

0b10011000,

0b10001000,

0b10001000,

0b01110000,

//H

0b10001000,

0b10001000,

0b10001000,

0b11111000,

0b10001000,

0b10001000,

0b10001000,

//I

0b01110000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b01110000,

//J

0b00111000,

0b00010000,

0b00010000,

0b00010000,

0b00010000,

0b10010000,

0b01100000,

//K

0b10001000,

0b10010000,

0b10100000,

0b11000000,

0b10100000,

0b10010000,

0b10001000,

//L

0b10000000,

0b10000000,

0b10000000,

0b10000000,

0b10000000,

0b10000000,

0b11111000,

//M

0b10001000,

0b11011000,

0b10101000,

0b10101000,

0b10001000,

0b10001000,

0b10001000,

//N

0b10001000,

0b10001000,

0b11001000,

0b10101000,

0b10011000,

0b10001000,

0b10001000,

//O

0b01110000,

0b10001000,

0b10001000,

0b10001000,

0b10001000,

0b10001000,

0b01110000,

//P

0b11110000,

0b10001000,

0b10001000,

0b11110000,

0b10000000,

0b10000000,

0b10000000,

//Q

0b01110000,

0b10001000,

0b10001000,

0b10001000,

0b10101000,

0b10010000,

0b01101000,

//R

0b11110000,

0b10001000,

0b10001000,

0b11110000,

0b10100000,

0b10010000,

0b10001000,

//S

0b01111000,

0b10000000,

0b10000000,

0b01110000,

0b00001000,

0b00001000,

0b11110000,

//T

0b11111000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

//U

0b10001000,

0b10001000,

0b10001000,

0b10001000,

0b10001000,

0b10001000,

0b01110000,

//V

0b10001000,

0b10001000,

0b10001000,

0b10001000,

0b10001000,

0b01010000,

0b00100000,

//W

0b10001000,

0b10001000,

0b10001000,

0b10101000,

0b10101000,

0b10101000,

0b01010000,

//X

0b10001000,

0b10001000,

0b01010000,

0b00100000,

0b01010000,

0b10001000,

0b10001000,

//Y

0b10001000,

0b10001000,

0b10001000,

0b01010000,

0b00100000,

0b00100000,

0b00100000,

//Z

0b11111000,

0b00001000,

0b00010000,

0b00100000,

0b01000000,

0b10000000,

0b11111000,

//91 [

0b11100000,

0b10000000,

0b10000000,

0b10000000,

0b10000000,

0b10000000,

0b11100000,

//92 \

0b00000000,

0b10000000,

0b01000000,

0b00100000,

0b00010000,

0b00001000,

0b00000000,

//93 ]

0b00111000,

0b00001000,

0b00001000,

0b00001000,

0b00001000,

0b00001000,

0b00111000,

//94 ^

0b00100000,

0b01010000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//95 _

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b11111000,

//96 `

0b10000000,

0b01000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//97 a

0b00000000,

0b01100000,

0b00010000,

0b01110000,

0b10010000,

0b01100000,

0b00000000,

//98 b

0b10000000,

0b10000000,

0b11100000,

0b10010000,

0b10010000,

0b11100000,

0b00000000,

//99 c

0b00000000,

0b00000000,

0b01110000,

0b10000000,

0b10000000,

0b01110000,

0b00000000,

// 100 d

0b00010000,

0b00010000,

0b01110000,

0b10010000,

0b10010000,

0b01110000,

0b00000000,

//101 e

0b00000000,

0b01100000,

0b10010000,

0b11110000,

0b10000000,

0b01110000,

0b00000000,

//102 f

0b00110000,

0b01000000,

0b11100000,

0b01000000,

0b01000000,

0b01000000,

0b00000000,

//103 g

0b00000000,

0b01100000,

0b10010000,

0b01110000,

0b00010000,

0b00010000,

0b01100000,

//104 h

0b10000000,

0b10000000,

0b11100000,

0b10010000,

0b10010000,

0b10010000,

0b00000000,

//105 i

0b00000000,

0b00100000,

0b00000000,

0b00100000,

0b00100000,

0b00100000,

0b00000000,

//106 j

0b00000000,

0b00010000,

0b00000000,

0b00010000,

0b00010000,

0b00010000,

0b01100000,

//107 k

0b10000000,

0b10010000,

0b10100000,

0b11000000,

0b10100000,

0b10010000,

0b00000000,

//108 l

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b00000000,

//109 m

0b00000000,

0b00000000,

0b01010000,

0b10101000,

0b10101000,

0b10101000,

0b00000000,

//110 n

0b00000000,

0b00000000,

0b01100000,

0b10010000,

0b10010000,

0b10010000,

0b00000000,

//111 o

0b00000000,

0b00000000,

0b01100000,

0b10010000,

0b10010000,

0b01100000,

0b00000000,

//112 p

0b00000000,

0b00000000,

0b01100000,

0b10010000,

0b11110000,

0b10000000,

0b10000000,

//113 q

0b00000000,

0b00000000,

0b01100000,

0b10010000,

0b11110000,

0b00010000,

0b00010000,

//114 r

0b00000000,

0b00000000,

0b10111000,

0b01000000,

0b01000000,

0b01000000,

0b00000000,

//115 s

0b00000000,

0b00000000,

0b01110000,

0b01000000,

0b00010000,

0b01110000,

0b00000000,

//116 t

0b01000000,

0b01000000,

0b11100000,

0b01000000,

0b01000000,

0b01000000,

0b00000000,

// 117u

0b00000000,

0b00000000,

0b10010000,

0b10010000,

0b10010000,

0b01100000,

0b00000000,

//118 v

0b00000000,

0b00000000,

0b10001000,

0b10001000,

0b01010000,

0b00100000,

0b00000000,

//119 w

0b00000000,

0b00000000,

0b10101000,

0b10101000,

0b01010000,

0b01010000,

0b00000000,

//120 x

0b00000000,

0b00000000,

0b10010000,

0b01100000,

0b01100000,

0b10010000,

0b00000000,

//121 y

0b00000000,

0b00000000,

0b10010000,

0b10010000,

0b01100000,

0b01000000,

0b10000000,

//122z

0b00000000,

0b00000000,

0b11110000,

0b00100000,

0b01000000,

0b11110000,

0b00000000,

//123 {

0b00100000,

0b01000000,

0b01000000,

0b10000000,

0b01000000,

0b01000000,

0b00100000,

//124 |

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

0b00100000,

//125 }

0b00100000,

0b00010000,

0b00010000,

0b00001000,

0b00010000,

0b00010000,

0b00100000,

//126 ~

0b00000000,

0b00000000,

0b01000000,

0b10101000,

0b00010000,

0b00000000,

0b00000000,

//127 DEL

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

0b00000000,

//128 o invert

0b11111111,

0b11111111,

0b10011111,

0b01101111,

0b01101111,

0b10011111,

0b11111111,

 

//129 o shadow

0b00000000,

0b00000000,

0b00110000,

0b01001000,

0b10010000,

0b01100000,

0b00000000,

 

//130 x shadow

0b00000000,

0b00000000,

0b01001000,

0b01010000,

0b01100000,

0b10100000,

0b00000000

};

 

flash char pacpic[7][6] = {

  // blank

  0b00000000,

  0b00000000,

  0b00000000,

  0b00000000,

  0b00000000,

  0b00000000,

  // right open

       0b00000000,

  0b01110000,

  0b10001000,

  0b10010000,

  0b10001000,

  0b01110000,

  // right closed

       0b00000000,

  0b01110000,

  0b10001000,

  0b10011000,

  0b10001000,

  0b01110000,

  // left open

       0b00000000,

  0b01110000,

  0b10001000,

  0b01001000,

  0b10001000,

  0b01110000,

  // left closed

  0b00000000,

  0b01110000,

  0b10001000,

  0b11001000,

  0b10001000,

  0b01110000,

  // front

  0b00000000,

  0b01110000,

  0b10001000,

  0b11111000,

  0b10001000,

  0b01110000,   

  // back

  0b00000000,

  0b01110000,

  0b10001000,

  0b10001000,

  0b10001000,

  0b01110000 

       };

//==================================

//This is the sync generator and raster generator. It MUST be entered from

//sleep mode to get accurate timing of the sync pulses

#pragma warn-

interrupt [TIM1_COMPA] void t1_cmpA(void)

begin

  //start the Horizontal sync pulse

  PORTD = syncON;

  //update the curent scanline number

  LineCount ++ ;

  //begin inverted (Vertical) synch after line 247

  if (LineCount==248)

  begin

    syncON = 0b00100000;

    syncOFF = 0;

  end

  //back to regular sync after line 250

  if (LineCount==251)

  begin

    syncON = 0;

    syncOFF = 0b00100000;

  end

  //start new frame after line 262

  if (LineCount==263)

  begin

     LineCount = 1;

  end

 

  delay_us(2); //adjust to make 5 us pulses

  //end sync pulse

  PORTD = syncOFF;

 

  if (LineCount<ScreenBot && LineCount>=ScreenTop)

    begin

 

       //compute byte index for beginning of the next line

       //left-shift 4 would be individual lines

       // <<3 means line-double the pixels

       //The 0xfff8 truncates the odd line bit

       //i=(LineCount-ScreenTop)<<3 & 0xfff8; //

 

       #asm

       push r16

       lds   r12, _LineCount

       lds   r13, _Linecount+1

       ldi   r16, 30

       sub  r12, r16

       ldi  r16,0

       sbc  r13, r16

       lsl  r12

       rol  r13

       lsl  r12

       rol  r13

       lsl  r12

       rol  r13

       mov  r16,r12

       andi r16,0xf0

       mov  r12,r16

       pop r16

       #endasm

 

       //load 16 registers with screen info

       #asm

       push r14

       push r15

       push r16

       push r17

       push r18

       push r19

       push r26

       push r27

 

       ldi  r26,low(_screen)   ;base address of screen

       ldi  r27,high(_screen)

       add  r26,r12            ;offset into screen (add i)

       adc  r27,r13

       ld   r4,x+          ;load 16 registers and inc pointer

       ld   r5,x+

       ld   r6,x+

       ld   r7,x+

       ld   r8,x+

       ld   r9,x+

       ld   r10,x+

       ld   r11,x+

       ld   r12,x+

       ld   r13,x+

       ld   r14,x+

       ld   r15,x+

       ld   r16,x+

       ld   r17,x+

       ld   r18,x+

       ld   r19,x

 

       pop  r27

       pop  r26

       #endasm

 

       delay_us(4);  //adjust to center image on screen

 

       //blast 16 bytes to the screen

       #asm

       ;but first a macro to make the code shorter

       ;the macro takes a register number as a parameter

       ;and dumps its bits serially to portD.6

       ;the nop can be eliminated to make the display narrower

       .macro videobits ;regnum

        BST  @0,7

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,6

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,5

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,4

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,3

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,2

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,1

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

 

       BST  @0,0

       IN   R30,0x12

       BLD  R30,6

       nop

       OUT  0x12,R30

       .endm

 

       videobits r4 ;video line -- byte 1

        videobits r5 ;byte 2

        videobits r6 ;byte 3

        videobits r7 ;byte 4

        videobits r8 ;byte 5

        videobits r9 ;byte 6

        videobits r10 ;byte 7

        videobits r11 ;byte 8

        videobits r12 ;byte 9

        videobits r13 ;byte 10

        videobits r14 ;byte 11

        videobits r15 ;byte 12

        videobits r16 ;byte 13

        videobits r17 ;byte 14

        videobits r18 ;byte 15

        videobits r19 ;byte 16

       clt   ;clear video after the last pixel on the line

       IN   R30,0x12

       BLD  R30,6

       OUT  0x12,R30

 

       pop r19

       pop r18

       pop r17

       pop r16

       pop r15

       pop r14

       #endasm

 

    end

end

#pragma warn+

 

//==================================

//plot one point

//at x,y with color 1=white 0=black 2=invert

#pragma warn-

void video_pt(char x, char y, char c)

begin

 

       #asm

       ;  i=(x>>3) + ((int)y<<4) ;   the byte with the pixel in it

 

       push r16

       ldd r30,y+2          ;get x

       lsr r30

       lsr r30

       lsr r30              ;divide x by 8

       ldd r12,y+1          ;get y

              lsl r12              ;mult y by 16

              clr r13

       lsl r12

       rol r13

       lsl r12

       rol r13

       lsl r12

       rol r13

       add r12, r30         ;add in x/8

 

       ;v2 = screen[i];   r5

        ;v3 = pos[x & 7];  r6

       ;v4 = c            r7

       ldi r30,low(_screen)

       ldi r31,high(_screen)

       add r30, r12

       adc r31, r13

       ld r5,Z              ;get screen byte

       ldd r26,y+2          ;get x

       ldi r27,0

       andi r26,0x07           ;form x & 7

       ldi r30,low(_pos*2)

       ldi r31,high(_pos*2)

       add r30,r26

       adc r31,r27

       lpm r6,Z

       ld r16,y             ;get c

 

       ;if (v4==1) screen[i] = v2 | v3 ;

       ;if (v4==0) screen[i] = v2 & ~v3;

       ;if (v4==2) screen[i] = v2 ^ v3 ;

 

       cpi r16,1

       brne tst0

       or  r5,r6

       tst0:

       cpi r16,0

       brne tst2

       com r6

       and r5,r6

       tst2:

       cpi r16,2

       brne writescrn

       eor r5,r6

       writescrn:

              ldi r30,low(_screen)

       ldi r31,high(_screen)

       add r30, r12

       adc r31, r13

       st Z, r5             ;write the byte back to the screen

 

       pop r16

       #endasm

 

end

#pragma warn+

 

//==Fast fixed multiply=================================

char fix2int(int a)

begin

    #asm

    ldd r30, Y+1 ;moves the high byte of the input to low byte

    #endasm

end

//========================================================

int mult_opt(int a, int b)

begin

    #asm

    ;push r20

    ;push r21

    mov  r24,r20

    mov  r25,r21

 

    ldd  R22,Y+2  ;load a

    ldd  R23,Y+3

 

    ld  r20,Y   ;load b

    ldd r21,Y+1

 

    muls      r23, r21      ; (signed)ah * (signed)bh

    mov              r31, r0              ;

    mul              r22, r20      ; al * bl

    mov       r30, r1              ;

    ;mov      r16, r0

    mulsu     r23, r20      ; (signed)ah * bl

    add              r30, r0     ;

    adc              r31, r1     ;

    mulsu     r21, r22      ; (signed)bh * al

    add              r30, r0     ;

    adc              r31, r1     ;

 

    mov r20,r24

    mov r21,r25

    ;pop r21

    ;pop r20

    #endasm

end

 

 

//========================================================

 

signed char cIn; //an input integer

float fIn;       //an input float

char fInString[16];

 

signed int cInFix, fInFix, prod, root, ratio;

 

 

//==================================

// put a big character on the screen

// c is index into bitmap

void video_putchar(char x, char y, char c)

begin

    v7 = x;

    for (v6=0;v6<7;v6++)

    begin

        v1 = ascii[c][v6];

        v8 = y+v6;

        video_pt(v7,   v8, (v1 & 0x80)==0x80);

        video_pt(v7+1, v8, (v1 & 0x40)==0x40);

        video_pt(v7+2, v8, (v1 & 0x20)==0x20);

        video_pt(v7+3, v8, (v1 & 0x10)==0x10);

        video_pt(v7+4, v8, (v1 & 0x08)==0x08);

    end

end

 

//==================================

// put a string of big characters on the screen

void video_puts(char x, char y, char *str)

begin

       char i ;

       for (i=0; str[i]!=0; i++)

       begin

              video_putchar(x,y,str[i]);

              x = x+6;

       end

end

 

//==================================

// put a small character on the screen

// x-cood must be on divisible by 4

// c is index into bitmap

void video_smallchar(char x, char y, char c)

begin

       char mask;

       i=((int)x>>3) + ((int)y<<4) ;

       if (x == (x & 0xf8)) mask = 0x0f;     //f8

       else mask = 0xf0;

 

       screen[i] =    (screen[i] & mask) | (smallbitmap[c][0] & ~mask);

       screen[i+16] = (screen[i+16] & mask) | (smallbitmap[c][1] & ~mask);

    screen[i+32] = (screen[i+32] & mask) | (smallbitmap[c][2] & ~mask);

    screen[i+48] = (screen[i+48] & mask) | (smallbitmap[c][3] & ~mask);

       screen[i+64] = (screen[i+64] & mask) | (smallbitmap[c][4] & ~mask);

end

 

//==================================

// put a string of small characters on the screen

// x-cood must be on divisible by 4

void video_putsmalls(char x, char y, char *str)

begin

       char i ;

       x = x & 0b1111100 ; //make it divisible by 4

       for (i=0; str[i]!=0; i++)

       begin

         if (str[i] == ' ')      // space

        video_smallchar(x,y,12);

    else if (str[i] == '-')      // minus

        video_smallchar(x,y,39);

              else if (str[i]>=0x30 && str[i]<=0x3a)

                     video_smallchar(x,y,str[i]-0x30);

              else

                  video_smallchar(x,y,str[i]-0x40+12);

              x = x+4;

       end

end

 

//==================================

//plot a line

//at x1,y1 to x2,y2 with color 1=white 0=black 2=invert

//NOTE: this function requires signed chars

//Code is from David Rodgers,

//"Procedural Elements of Computer Graphics",1985

void video_line(char xpt1, char ypt1, char xpt2, char ypt2, char c)

begin

       int e;

       signed char dx,dy,j, temp;

       signed char s1,s2, xchange;

    signed char x,y;

 

       x = xpt1;

       y = ypt1;

       dx = cabs(xpt2-xpt1);

       dy = cabs(ypt2-ypt1);

       s1 = csign(xpt2-xpt1);

       s2 = csign(ypt2-ypt1);

       xchange = 0;

       if (dy>dx)

       begin

              temp = dx;

              dx = dy;

              dy = temp;

              xchange = 1;

       end

       e = ((int)dy<<1) - dx;

       for (j=0; j<=dx; j++)

       begin

              video_pt(x,y,c) ;

              if (e>=0)

              begin

                     if (xchange==1) x = x + s1;

                     else y = y + s2;

                     e = e - ((int)dx<<1);

              end

              if (xchange==1) y = y + s2;

              else x = x + s1;

              e = e + ((int)dy<<1);

       end

end

 

//==================================

//return the value of one point

//at x,y with color 1=white 0=black 2=invert

char video_set(char x, char y)

begin

       //The following construction

       //detects exactly one bit at the x,y location

       i=((int)x>>3) + ((int)y<<4) ;

    return ( screen[i] & 1<<(7-(x & 0x7)));

end

 

//===draw pacman =======================

void draw_pac(char x, char y, char erase)

{  

    x = sx_draw;

    if(!fly_mode)

    {

        y = sy_track; 

    }              

    else

    {

        y = sy_draw;

    }

    delt_x_draw = sx_draw - prev_x_draw;

    delt_y_track = sy_track - prev_y_track;    

   

    // determine direction

    // right

    if(((delt_x_draw) > 0)  

      && (((delt_y_track > 0)&&(delt_x_draw > delt_y_track))||((delt_y_track < 0)&&(-delt_y_track < delt_x_draw))))

    {

        dir = right;

    }

          // face left

    else if(((delt_x_draw) < 0)

        && (((delt_y_track > 0)&&(-delt_x_draw > delt_y_track))||((delt_y_track < 0)&&(-delt_y_track < -delt_x_draw))))

         {

             dir = left;

         }

         // face forward

    else if(((delt_y_track) > 0)

        && (((delt_x_draw > 0)&&(delt_y_track > delt_x_draw))||((delt_x_draw < 0)&&(-delt_x_draw < delt_y_track))))

    {

        dir = forward;

    }

    // face backward

    else if(((delt_y_track) < 0)

        && (((delt_x_draw > 0)&&(-delt_y_track > delt_x_draw))||((delt_x_draw < 0)&&(-delt_x_draw < -delt_y_track))))

    {

        dir = backward;

    }

        

        

    if(erase)

    {

                v7 = x;

           for (v6=0;v6<6;v6++)

           {

               v1 = pacpic[0][v6];

                

               v8 = y+v6;

               video_pt(v7,   v8, (v1 & 0x80)==0x80);

               video_pt(v7+1, v8, (v1 & 0x40)==0x40);

               video_pt(v7+2, v8, (v1 & 0x20)==0x20);

               video_pt(v7+3, v8, (v1 & 0x10)==0x10);

               video_pt(v7+4, v8, (v1 & 0x08)==0x08);

          video_pt(v7+5, v8, (v1 & 0x04)==0x04);

           }

    }

         // face right

    else if(dir == right)

    {

                v7 = x;

           for (v6=0;v6<6;v6++)

           {

               if(open) 

               {

                 v1 = pacpic[1][v6];

               }

               else

               {

                 v1 = pacpic[2][v6];

               }

               v8 = y+v6;

               video_pt(v7,   v8, (v1 & 0x80)==0x80);

               video_pt(v7+1, v8, (v1 & 0x40)==0x40);

               video_pt(v7+2, v8, (v1 & 0x20)==0x20);

               video_pt(v7+3, v8, (v1 & 0x10)==0x10);

               video_pt(v7+4, v8, (v1 & 0x08)==0x08);

          video_pt(v7+5, v8, (v1 & 0x04)==0x04);

           }

    }

    // face left

    else if(dir == left)

    {

                v7 = x;

           for (v6=0;v6<6;v6++)

           {

               if(open) 

               {

                 v1 = pacpic[3][v6];

               }

               else

               {

                 v1 = pacpic[4][v6];

               }

               v8 = y+v6;

               video_pt(v7,   v8, (v1 & 0x80)==0x80);

               video_pt(v7+1, v8, (v1 & 0x40)==0x40);

               video_pt(v7+2, v8, (v1 & 0x20)==0x20);

               video_pt(v7+3, v8, (v1 & 0x10)==0x10);

               video_pt(v7+4, v8, (v1 & 0x08)==0x08);

          video_pt(v7+5, v8, (v1 & 0x04)==0x04);

           }

    }

   

    // face forward

    else if(dir == forward)

    {

                v7 = x;

           for (v6=0;v6<6;v6++)

           {

 

          v1 = pacpic[5][v6];

                

               v8 = y+v6;

               video_pt(v7,   v8, (v1 & 0x80)==0x80);

               video_pt(v7+1, v8, (v1 & 0x40)==0x40);

               video_pt(v7+2, v8, (v1 & 0x20)==0x20);

               video_pt(v7+3, v8, (v1 & 0x10)==0x10);

               video_pt(v7+4, v8, (v1 & 0x08)==0x08);

          video_pt(v7+5, v8, (v1 & 0x04)==0x04);

           }

    }

   

     // face backward

    else if(dir == backward)

    {

                v7 = x;

           for (v6=0;v6<6;v6++)

           {

               v1 = pacpic[6][v6];

 

               v8 = y+v6;

               video_pt(v7,   v8, (v1 & 0x80)==0x80);

               video_pt(v7+1, v8, (v1 & 0x40)==0x40);

               video_pt(v7+2, v8, (v1 & 0x20)==0x20);

               video_pt(v7+3, v8, (v1 & 0x10)==0x10);

               video_pt(v7+4, v8, (v1 & 0x08)==0x08);

          video_pt(v7+5, v8, (v1 & 0x04)==0x04);

           }

    }

   

    if(open)

    {

      open = 0;

    }

    else

    {

      open = 1;

    }

}

 

 

// find exact position (x,y,z)

void getCoords()

{

  // B.0 = data received

       // B.1 = data ready

       // B.2 = fly mode bit 1

       // B.3 = fly mode bit 0

 

       // this is the slave

       if(PINB.1 == 1)         // data ready

    {

             

 

         switch(CoordCtrl)

         begin

             case 1:

                 x_draw = PINA;      // read port

                          y_draw = PINC;

                CoordCtrl++;

            break;

            case 2:

                y_track = PINA;      // read port

                         y_draw = PINC;

                CoordCtrl++;

            break;

            case 3:

                y_track = PINA;

                x_draw = PINC;

                CoordCtrl = 1;

            break;

         end

        

        

         //scaled values

         sx_draw = mult_opt((x_draw>>2),(3<<8));

         sy_draw = (mult_opt((y_draw>>2),(2<<8)))+45;

         sy_track = (mult_opt((y_track>>2),(2<<8)))+45;

        

         // boundaries        

         if(sy_draw <= 12)

         {

            sy_draw = 12;

         }

         else if(sy_draw >= 93)

         {

            sy_draw = 93;

         }

         if(sy_track <= 12)

         {

            sy_track = 12;

         }

         else if(sy_track >= 93)

         {

            sy_track = 93;

         }       

         if(sx_draw <= 26)

         {

            sx_draw = 26;

         }              

         else if(sx_draw >= 119)

         {

            sx_draw = 119;

         }   

         if(sy_track <=68)

         {

            sy_track = 68;

         }

 

          // ground edge detection

          if((sy_track<=70)&&(sy_track>=64))

          {

            wPlatform = 1;

          }   

          else if((sy_track<=68))

          {

            wPlatform = 1;

          }           

          else

            wPlatform = 0;

         

          if((sy_draw<=70)&&(sy_draw>=64))

          {

            fPlatform = 1;

          }              

          else

            fPlatform = 0; 

            

          if(!fly_mode && sy_track <64)

         {

            sy_track = 64;

         }

           

         // draw pac-man

         if(fly_mode)

         {

                  draw_pac(sx_draw,sy_draw, 0);

                  video_smallchar(sx_draw,sy_track,39);

         }   

         else

         {

           // if(!wPlatform)

              draw_pac(sx_draw,sy_track, 0);           

         }

       

        

         if(CoordCtrl == 1)

         {

            PORTB.2 = 0;

            PORTB.3 = 1;

            //CoordCtrl++;

         }

         else if(CoordCtrl == 2)

         {

            PORTB.2 = 1;

            PORTB.3 = 0;

            //CoordCtrl++;

         }

         else if(CoordCtrl == 3)

         {

            PORTB.2 = 1;

            PORTB.3 = 1;

            //CoordCtrl = 1;

         }

 

 

         PORTB.0 = 1;         //got data

         

        delay_ms(4);

     

        video_line(25,70,126,70,1);

 

    }

 

    if(PINB.1 == 0)

    {

      PORTB.0 = 0;

    }

   prev_x_draw = sx_draw;

   prev_y_track = sy_track;

   prev2_y_track = prev_y_track;

   prev2_x_draw = prev_x_draw;

}

 

//===simulation stuff==================================================

 

char cu1[]="3D PACMAN";

char xpos[] = "XPOS";

char ypos[] = "YPOS";

char zpos[] = "ZPOS";

char fly[] = "F";

char walk[] = "W";

char scor[] = "SCORE";   

char lose[] = "GAME OVER"

char l1[] = "LEV1";

char l2[] = "LEV2";

char ts[10];

 

char strx[10], stry[10], strz[10], strscore[10];

 

 

//==================================

// set up the ports and timers

void main(void)

begin  

 

  int j = 0;

  //init timer 1 to generate sync

  OCR1A = lineTime; //One NTSC line

  TCCR1B = 9;              //full speed; clear-on-match

  TCCR1A = 0x00;     //turn off pwm and oc lines

  TIMSK = 0x10;            //enable interrupt T1 cmp

 

  //init ports

  DDRD = 0xf0;             //video out and switches

  //D.5 is sync:1000 ohm + diode to 75 ohm resistor

  //D.6 is video:330 ohm + diode to 75 ohm resistor

  DDRA = 0x00; //inputs

  DDRC = 0x00;

  DDRB.0 = 1;

  DDRB.1 = 0;

  DDRB.2 = 1;

  DDRB.3 = 1;

  PORTB.0 = 0; //ready for new data   

  neg = -1;

 

  //initialize synch constants

  LineCount = 1;

  syncON = 0b00000000;

  syncOFF = 0b00100000;

 

  // A/D stuff

  ADCSR = 0b11000111;

  ADMUX = 0b01100000;

  MuxCtrl = 1;

  CoordCtrl = 1;

  Align = 0;

  wPlatform = 0;

  fPlatform = 0;   

  calcO = 0;  

  open = 0;   

  dir = 3;

  x_draw = 55;

  y_track = 55;

  y_draw = 55;

  sx_draw = 55;

  sy_draw = 55;

  sy_track = 55;

 

 

  fly_mode = 0;     // default: WALK MODE

  time1 = 30;       //object timer

  score=0;

  check = 0;

  off = 0; 

  gametime = 0; 

  eat = 0;   

  feat = 0;   

  fheight = 0;

  //Oy=11;

 

   //init the UART

   UCSRB = 0x18;

   UBRRL = 103;

   //printf("starting...\n\r");   

 

  //Print "PACMAN"

  video_puts(13,3,cu1);

 

 

  //side lines

  #define width 126

  video_line(0,0,0,99,1);

  video_line(width,0,width,99,1);

  video_line(25,11,25,99,1);   

 

  //top line & bottom lines

  video_line(0,0,width,0,1);

  video_line(0,99,width,99,1);

  video_line(0,11,width,11,1);

 

 

  //horizon

  video_line(25,70,width,70,1);

 

  //video_

 

  //enable sleep mode

  MCUCR = 0b10000000;

  #asm ("sei");

 

  //The following loop executes once/video line during lines

  //1-230, then does all of the frame-end processing

  while(1)

  begin

    //stall here until next line starts

    //sleep enable; mode=idle

    //use sleep to make entry into sync ISR uniform time

 

    #asm ("sleep");

 

    //The following code executes during the vertical blanking

    //Code here can be as long as

    //a total of 60 lines x 63.5 uSec/line x 8 cycles/uSec

    if (LineCount==231)

    begin

   

      draw_pac(sx_draw,sy_draw, 1);

          video_smallchar(sx_draw,sy_track,12);

         draw_pac(sx_draw,sy_track, 1);

         draw_pac(prev2_x_draw,prev2_y_track,1);  

 

        getCoords(); 

       

        // button 0 pushed puts this game in fly mode

        if(PINB.4 == 0)

        {

              fly_mode = 1;

            video_putsmalls(28,93,fly);

        }

        else

        {

              fly_mode = 0;

            video_putsmalls(28,93,walk);

        }

         // detect when object is eaten     

        if((((sx_draw>=Ox)&&(sx_draw<=(Ox+5))&&(sy_track>=Oy)&&(sy_track<=(Oy+7)))

               ||(((sx_draw+5)>=Ox)&&((sx_draw+5)<=(Ox+5))&&(sy_track>=Oy)&&(sy_track<=(Oy+7)))

               ||(((sx_draw)>=Ox)&&(sx_draw<=(Ox+5))&&((sy_track+7)>=Oy)&&((sy_track+7)<=(Oy+7)))

               ||(((sx_draw+5)>=Ox)&&((sx_draw+5)<=(Ox+5))&&((sy_track+7)>=Oy)&&

((sy_track+7)<=(Oy+7))))&& (!wPlatform))

        {

            eat = 1;

        }  

        else if((((sx_draw>=Xx)&&(sx_draw<=(Xx+5))&&(sy_track>=Xy)&&(sy_track<=(Xy+7)))

               ||(((sx_draw+5)>=Xx)&&((sx_draw+5)<=(Xx+5))&&(sy_track>=Oy)&&(sy_track<=(Xy+7)))

               ||(((sx_draw)>=Xx)&&(sx_draw<=(Xx+5))&&((sy_track+7)>=Xy)&&((sy_track+7)<=(Xy+7)))

               ||(((sx_draw+5)>=Xx)&&((sx_draw+5)<=(Xx+5))&&((sy_track+7)>=Xy)

&&((sy_track+7)<=(Xy+7))))&& (!wPlatform))

        {

            eat = 1;

        }

        else

        {

            eat = 0;

        }    

            

        if((((sx_draw>=Ox)&&(sx_draw<=(Ox+5))&&(sy_draw>=Oy)&&(sy_draw<=(Oy+7)))

               ||(((sx_draw+5)>=Ox)&&((sx_draw+5)<=(Ox+5))&&(sy_draw>=Oy)&&(sy_draw<=(Oy+7)))

               ||(((sx_draw)>=Ox)&&(sx_draw<=(Ox+5))&&((sy_draw+7)>=Oy)&&((sy_draw+7)<=(Oy+7)))

               ||(((sx_draw+5)>=Ox)&&((sx_draw+5)<=(Ox+5))&&((sy_draw+7)>=Oy)&&((sy_draw+7)<=(Oy+7))))&& (!wPlatform))

        {

            feat = 1;

        }        

        else if((((sx_draw>=Xx)&&(sx_draw<=(Xx+5))&&(sy_draw>=Xy)&&(sy_draw<=(Xy+7)))

               ||(((sx_draw+5)>=Xx)&&((sx_draw+5)<=(Xx+5))&&(sy_draw>=Xy)&&(sy_draw<=(Xy+7)))

               ||(((sx_draw)>=Xx)&&(sx_draw<=(Xx+5))&&((sy_draw+7)>=Oy)&&((sy_draw+7)<=(Oy+7)))

               ||(((sx_draw+5)>=Xx)&&((sx_draw+5)<=(Xx+5))&&((sy_draw+7)>=Xy)&&((sy_draw+7)<=(Xy+7))))&& (!wPlatform))

        {

            feat = 1;

        } 

        else

        {

            feat = 0;

        }   

        if((sy_track>=(Oy_shadow-3))&&(sy_track<=(Oy_shadow+7)))

        {

            fheight = 1;

        }              

        else

        {

            fheight = 0;

        }

                

         //erase object

        video_putchar(Ox,Oy,127);

        video_putchar(80,60,127);

       

        //objects   

        if(score <=5)                // Level 1

        begin       

            video_putsmalls(4,13,l1);

            if(gametime<300)

            {     

            gametime++;

                         

             if(j<22)

              {

                Ox = x_pacpt1[j];

                Oy = y_pacpt1[j]; 

              }

              else

                j = 0;

               

              video_putchar(Ox,Oy,111);   //O     

             

            

              if((video_set(((char)(sx_draw)),((char)(sy_track + 6)))!=0))

              {

                  off = 1;

              } 

            

              if((eat==1)&&(!fPlatform))

                  {

                    score+=2;

                    video_putchar(Ox,Oy,127);     // erase figure

                    gametime=300;   

                    calcO = 0;

                  }                    

                       

            }    

            else if(gametime == 300)

            {

                j++;

                gametime++;

            }

            else if ((300<gametime) && (gametime<500))     

            {

              gametime++;

              if(j<22)

              {

                Xx = x_pacpt1[j];

                Xy = y_pacpt1[j]; 

              }

              else

                j = 0;

              video_putchar(Xx,Xy,120);    //X

             

              if((eat==1)&&(!fPlatform)) 

                  {

                    score--;

                    video_putchar(Xx,Xy,127);     // erase figure

                    gametime=0;  

                  }   

            }

            else if(gametime == 500)

            {

                j++;  

                video_putchar(Xx,Xy,127);     // erase figure

                gametime = 0;

            }                

        end

        else if((15<score))            // Level 2

        begin        

            video_putsmalls(4,13,l2);

            if(gametime<300)

            {     

            gametime++;

                         

             if(j<22)

              {

                Ox = x_pacpt2[j];

                Oy = y_pacpt2[j];   

                Oy_shadow = y_base2[j];

              }

              else

                j = 0;

               

              video_putchar(Ox,Oy,111);   //O    

              video_smallchar(Ox, Oy_shadow, 39);  // shadow

            

              if((video_set(((char)(sx_draw)),((char)(sy_track + 6)))!=0))

              {

                  off = 1;

              } 

            

              if((feat==1)&&(!fPlatform))  

                  {

                    score+=2;

                    video_putchar(Ox,Oy,127);     // erase figure  

                    video_smallchar(Ox,Oy_shadow,12);

                    gametime=300;   

                    calcO = 0;

                  }      

                 

                 // if(time1%10 == 0){score++;}//temp              

            }    

            else if(gametime == 300)

            {

                j++;

                gametime++;

            }

            else if ((300<gametime) && (gametime<500))     

            {

              gametime++;

              if(j<22)

              {

                Xx = x_pacpt2[j];

                Xy = y_pacpt2[j];

                Xy_shadow = y_base2[j];

              }

              else

                j = 0;

              video_putchar(Xx,Xy,120);    //X            

              video_smallchar(Xx, Xy_shadow, 39);  // shadow

             

              if((feat==1)&& (fly_mode==0))  

                  {

                    score--;

                    video_smallchar(Xx,Xy_shadow,12);     // erase figure

                    gametime=0;  

                  }   

            }

            else if(gametime == 500)

            {

                j++;  

                video_putchar(Xx,Xy,127);     // erase figure

                video_putchar(Xx,Xy_shadow,12);  

                gametime = 0;

            }          

        end

        else                          // Level 3

        begin

        end    

      if(score < 0)

      {

          video_puts(45,50,lose); 

          break;

      }

 

           video_putsmalls(4,38,scor);   

           sprintf(strz, "%04d", score); 

           video_putsmalls(4,45,strz);  

    

    end  //line 231

  end  //while

end  //main

 

 


usWorking hard or hardly working?

 

 

 

 

 

us againSneak attack by Bruce with his camera...we are concentrating!

 

 

 

 

 

 

 

circuit

An amazing solder job by Aylin

 

 

 

 

 

 

system

The entire system

 

 

 

 

 

 

 

proto

The mess that amazingly worked.

 

 

 

 

 

 

interface

The user interface