Close
0%
0%

Pong in AVR

Final project of the AVR Architecture Course

Similar projects worth following
Final project of the AVR Architecture Course - HackadayU

Project Requirements
Create a PONG game. The game has three elements: user paddle, ball, and the computer's paddle. The ball should bounce when it hits the paddle. If the ball misses the paddle, the game is over. The user's paddle is controlled by two pushbuttons, and the computer's paddle is controlled automatically by your code.

The game has to be written entirely in AVR assembly, without relying on any of Arduino's built-in library functions.

Single Player PONG Game

  • Player Paddle controlled by two buttons
  • Computer Paddle controlled by an algorithm to chase the ball wherever it goes
  • The ball changes X and Y direction when it collides with the top, bottom, or either of the paddles
  • GAME OVER if the ball touches the left and right side
  • Restart the program once GAME OVER
  • No score system implemented

diagram.json

Wokwi Diagram File

JavaScript Object Notation (JSON) - 1.27 kB - 03/01/2021 at 10:57

Download

pong.S

Final Assembly File

Assembler Source File - 6.87 kB - 03/01/2021 at 10:14

Download

  • Attempt 2 - From Scratch

    Paulson K Antony03/01/2021 at 12:00 0 comments

    So.

    Wrote the whole assembly program from scratch.

    Checked the output after writing each and every function. 

    Always check the registers and program counter if something isn't happening as per your logic.

    You can check out the Wokwi simulation and the final Assembly file to see the final results.

    Shoutout to Wokwi - It is an amazing simulator and the in built web debugger is a godsend for anyone working with AVR Assembly. I so wish I had this when I was getting sick and tired of working with Proteus simulations for my Bachelor's degree coursework.

    But I don't think there would be another time that I would write a whole program in Assembly XD

    Why write 450 lines of code when you can get it done in 100 lines?

    Efficiency? Maybe.

    That said, learning AVR Assembly provided me with a whole new perspective into how an Arduino works.

    Now I know how to blink an LED with Ports and Timers :)

    (People who made the AVR libraries are geniuses.)

  • Attempt 1 - C Code without Library functions

    Paulson K Antony03/01/2021 at 11:26 0 comments

    Initial idea was to look at the generated assembly code and copy the required lines into my assembly program.

    But since the original C code depended heavily on Arduino library functions, many unnecessary assembly lines were generated.

    So rewrote the whole code to make user-defined function/ code for the library functions like SPI.transfer( ), SPI.begin( ), digitalWrite( ) and so on. Also, removed all small or unnecessary loops.

    This can be done by direct PORT and Register Manipulation.

    #define lo8(x) ((x)&0xff)
    #define hi8(x) ((x)>>8)
    
    uint8_t playerPaddle = 2;
    uint8_t compPaddle = 2;
    uint8_t ballx = 6;
    uint8_t bally = 4;
    int8_t speedx = 1;
    int8_t speedy = -1;
    boolean gameOver = false;
    
    uint16_t buffer = 0 ; 
    

    Single-Byte Global Variables

    void spiTransfer(uint8_t data)
    {
      SPDR = data;
      while(!(SPSR & (1<<SPIF)));
    }

    User-Defined SPI.Transfer

    bitClear(PORTB, 2);
    spiTransfer(i);
    spiTransfer(lo8(buffer));
    spiTransfer(i);
    spiTransfer(hi8(buffer));
    bitSet(PORTB, 2);

    Single SPI Transaction

    //SPI.begin( ), pinMode( ), digitalWrite( )
    DDRB = bit(5)|bit(3)|bit(2);
    PORTB = bit(2);
    SPCR = 0b01010000;
    DDRD = 0;
    PORTD = bit(7)|bit(6) ;
    
    //digitalRead( )
    if(!bitRead(PIND, LB))
        movePaddleLeft();
    if(!bitRead(PIND, RB))
        movePaddleRight();

     Direct Port/ Register Manipulation

    The assembly code generated was way more simpler and straightforward and gave me an idea of how to approach the assembly code.

    Unfortunately, I got cocky and wrote the whole code in one go without testing. Random LEDs started lighting up on the LED matrix and wasted hours trying to find out the errors.

    Gave up and started from scratch

  • Attempt 1 - C Code

    Paulson K Antony03/01/2021 at 11:19 0 comments

    I coded the whole thing in C to make sure my logic is sound. 

    Hit a few bumps in interfacing SPI with the MAX7219 LED Matrix but it worked eventually

    Implemented a simple ballChase algorithm for the computer's paddle to make things easier for me while coding in assembly. The downside is you can never win against the computer :P

    #include <SPI.h>
    
    #define CLK_PIN   13
    #define DATA_PIN  11 
    #define CS_PIN    10 
    #define LB 7         
    #define RB 6         
    
    #define lo8(x) ((x)&0xff)
    #define hi8(x) ((x)>>8)
    
    int playerPaddle = 2;
    int compPaddle = 2;
    int compDirection = 1;
    int ballx = 6;
    int bally = 4;
    int speedx = 1;
    int speedy = -1;
    boolean gameOver = false;
    
    uint16_t buffer = 0 ; 
    
    void refresh(){
      for(int i=1;i<=8;++i)
      {
        if(i==(playerPaddle-1)|i==(playerPaddle)|i==(playerPaddle+1))
          bitSet(buffer,0);
        if(i==(compPaddle-1)|i==(compPaddle)|i==(compPaddle+1))
          bitSet(buffer,15);
        if(i==bally)
          bitSet(buffer,ballx);
    
        digitalWrite(CS_PIN, LOW);
        SPI.transfer(i);
        SPI.transfer(lo8(buffer));
        SPI.transfer(i);
        SPI.transfer(hi8(buffer));
        digitalWrite(CS_PIN, HIGH);
        
        buffer=0;
      }
    }
    
    void updateBall()
    {
      // Reverse direction When hitting the bat
      if ((ballx > 13) & (speedx == 1) & (bally <= (compPaddle + 1)) & (bally >= (compPaddle -1)))
        speedx = -1;
      else
        if ((ballx >14) & (speedx == 1))
        {
          speedx=0;
          speedy=0;
          gameOver = true;
        }
      
      if ((ballx < 2) & (speedx == -1) & (bally <= (playerPaddle + 1)) & (bally >= (playerPaddle -1)))
        speedx = 1;
      else
        if ((ballx < 1) & (speedx == -1))
        {
          speedx=0;
          speedy=0;
          gameOver = true;
        }
      if (bally == 8) 
        speedy = -1;
      if (bally == 1) 
        speedy = 1;
    
      ballx += speedx;
      bally += speedy;
    
    }
    
    void ballChaser()
    {
      if((compPaddle + 1) < bally)
        ++compPaddle;
      if((compPaddle - 1) > bally)
        --compPaddle;
    }
    
    void lose()
    {
      while(true)
      {
        clearDisplays();
        delay(500);
        refresh();
        delay(500);
      }
    }
    
    
    void movePaddleLeft(){
      if(playerPaddle!=2)
        --playerPaddle;
    }
    
    void movePaddleRight(){
      if(playerPaddle!=7)
        ++playerPaddle;
    }
    
    void sendAll(int registerIndex, int value) {
      digitalWrite(CS_PIN, LOW);
      for (int i = 0; i < 2; i++) {
        SPI.transfer(registerIndex);
        SPI.transfer(value);
      }
      digitalWrite(CS_PIN, HIGH);
    }
    
    void clearDisplays() {
      for (int row = 1; row <= 8; row++) {
        sendAll(row, 0);
      }
    }
    
    void setup() {
      SPI.begin();
      sendAll(0xf, 0); // Disable test mode
      sendAll(0xb, 7); // Set scanlines to 8
      clearDisplays();
      sendAll(0xc, 1); // Enable display
    
      pinMode(LB, INPUT_PULLUP);
      pinMode(RB, INPUT_PULLUP);
    
    }
    
    
    void loop() {
      updateBall();
      if(gameOver)
        lose();
    
      ballChaser();
      
      if(!digitalRead(LB))
        movePaddleLeft();
      if(!digitalRead(RB))
        movePaddleRight();
      
      delay(100);
      refresh();
    }

    Setup initializes all the pins required for SPI and the buttons

    Loop calls the following functions -

    • updateBall( ) - Update the position of the ball every loop
    • lose( ) - If the gameOver flag is set, then stop the game
    • ballChaser( ) - Make the computer paddle follow the ball
    • movePaddleLeft( ) - Make the player paddle go left (up)
    • movePaddleRight( ) - Make the player paddle go right (down)

View all 3 project logs

  • 1
    Watch the HackadayU Classes

    Amazing Class 10/10

    AVR: Architecture, Assembly & Reverse Engineering

    • I realized that this course was way above my level, way too late in the game. 
    • Had invested in this and wanted to see this through. 
    • So just push through! You can do it!
  • 2
    Code the program in C

    Make sure it works.

  • 3
    Try to reverse engineer the code (cheat) by looking at the assembly code generated

    Wokwi is the best Arduino simulator on the planet!

    Proteus is crap.


View all 6 instructions

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates