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.
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.
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:
- Master calculates data
- Master sets data ready bit to 1
- Slave sees that data ready bit is 1
- Slave receives and stores data
- Slave sets data received bit to 1
- Master sets data ready bit to 0
- Slave sets data received bit to 0
- 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:
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
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
Working hard or hardly working?
Sneak attack by Bruce with his camera...we are concentrating!
An amazing solder job by Aylin
The entire system
The mess that amazingly worked.