-
Attempt 2 - From Scratch
03/01/2021 at 12:00 • 0 commentsSo.
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
03/01/2021 at 11:26 • 0 commentsInitial 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
03/01/2021 at 11:19 • 0 commentsI 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)