{"id":24312472,"url":"https://github.com/mytechnotalent/atmega328p_ssd1306_driver","last_synced_at":"2026-04-29T13:34:42.869Z","repository":{"id":272529159,"uuid":"916906812","full_name":"mytechnotalent/ATmega328P_SSD1306_Driver","owner":"mytechnotalent","description":"An ATmega328P SSD1306 driver written entirely in Assembler.","archived":false,"fork":false,"pushed_at":"2025-01-15T01:32:37.000Z","size":1272,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-15T02:56:31.638Z","etag":null,"topics":["arduino","arduino-platform","arduino-programming","assembler","assembly","atmega328p","avr","avr-programming","avrdude","ssd1306"],"latest_commit_sha":null,"homepage":"","language":"Assembly","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mytechnotalent.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-15T01:27:08.000Z","updated_at":"2025-01-15T01:35:39.000Z","dependencies_parsed_at":"2025-01-15T03:10:22.159Z","dependency_job_id":null,"html_url":"https://github.com/mytechnotalent/ATmega328P_SSD1306_Driver","commit_stats":null,"previous_names":["mytechnotalent/atmega328p_ssd1306_driver"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FATmega328P_SSD1306_Driver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FATmega328P_SSD1306_Driver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FATmega328P_SSD1306_Driver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mytechnotalent%2FATmega328P_SSD1306_Driver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mytechnotalent","download_url":"https://codeload.github.com/mytechnotalent/ATmega328P_SSD1306_Driver/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242667474,"owners_count":20166303,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","arduino-platform","arduino-programming","assembler","assembly","atmega328p","avr","avr-programming","avrdude","ssd1306"],"created_at":"2025-01-17T08:17:53.224Z","updated_at":"2026-04-29T13:34:42.863Z","avatar_url":"https://github.com/mytechnotalent.png","language":"Assembly","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"https://raw.githubusercontent.com/mytechnotalent/ATmega328P_SSD1306_Driver/refs/heads/main/ATmega328P%20SSD1306%20Driver.png\"\u003e\n\n## FREE Reverse Engineering Self-Study Course [HERE](https://github.com/mytechnotalent/Reverse-Engineering-Tutorial)\n\n\u003cbr\u003e\n\n# ATmega328P SSD1306 Driver\nAn ATmega328P SSD1306 driver written entirely in Assembler.\n\n\u003cbr\u003e\n\n# Code\n```assembler\n; ===================================================================\n; Project: ATmega328P SSD1306 Driver\n; ===================================================================\n; Author: Kevin Thomas\n; E-Mail: ket189@pitt.edu\n; Version: 1.0\n; Date: 01/09/25\n; Target Device: ATmega328P (Arduino Nano)\n; Clock Frequency: 16 MHz\n; Toolchain: AVR-AS, AVRDUDE\n; License: Apache License 2.0\n; Description: This program is a simple SSD1306 driver that works\n;              with capital letters.\n; ===================================================================\n\n; ===================================================================\n; SYMBOLIC DEFINITIONS\n; ===================================================================\n.equ     TWBR, 0xB8               ; TWI Bit Rate Register\n.equ     TWSR, 0xB9               ; TWI Status Register\n.equ     TWDR, 0xBB               ; TWI Data Register\n.equ     TWCR, 0xBC               ; TWI Control Register\n.equ     TWINT, 7                 ; TWI Interrupt Flag\n.equ     TWEA, 6                  ; TWI Enable Acknowledge\n.equ     TWSTA, 5                 ; TWI Start Condition\n.equ     TWSTO, 4                 ; TWI Stop Condition\n.equ     TWEN, 2                  ; TWI Enable Bit\n.equ     SSD1306_ADDR, 0x3C       ; SSD1306 I2C Address\n\n; ===================================================================\n; PROGRAM ENTRY POINT\n; ===================================================================\n.global  program                  ; global label; make avail external\n.section .text                    ; start of the .text (code) section\n\n; ===================================================================\n; PROGRAM LOOP\n; ===================================================================\n; Description: Main program loop which executes all subroutines and \n;              then repeats indefinitely.\n; -------------------------------------------------------------------\n; Instructions: AVR Instruction Set Manual\n;               6.87 RCALL – Relative Call to Subroutine\n;               6.95 LDI – Load Immediate\n;               6.90 RJMP – Relative Jump\n; ===================================================================\nprogram:\n  RCALL  TWI_Init                 ; initialize TWI\n  RCALL  SSD1306_Init             ; initialize SSD1306\n  RCALL  SSD1306_Clear            ; clear the OLED screen\n  LDI    R16, 0                   ; set page to 0\n  RCALL  Set_Cursor_Page          ; move cursor to page 0\n  RCALL  Display_Eext_KEVIN_T     ; display \"KEVIN T\"\n  LDI    R16, 1                   ; set page to 1\n  RCALL  Set_Cursor_Page          ; move cursor to page 1\n  RCALL  Display_Text_KEVIN_T     ; display \"KEVIN T\"\n  LDI    R16, 2                   ; set page to 2\n  RCALL  Set_Cursor_Page          ; move cursor to page 2\n  RCALL  Display_Text_KEVIN_T     ; display \"KEVIN T\"\n  LDI    R16, 3                   ; set page to 3\n  RCALL  Set_Cursor_Page          ; move cursor to page 3\n  RCALL  Display_Text_KEVIN_T     ; display \"KEVIN T\"\n  LDI    R16, 4                   ; set page to 4\n  RCALL  Set_Cursor_Page          ; move cursor to page 4\n  RCALL  Display_Text_KEVIN_T     ; display \"KEVIN T\"\n  LDI    R16, 5                   ; set page to 5\n  RCALL  Set_Cursor_Page          ; move cursor to page 5\n  RCALL  Display_Text_KEVIN_T     ; display \"KEVIN T\"\n  LDI    R16, 6                   ; set page to 6\n  RCALL  Set_Cursor_Page          ; move cursor to page 6\n  RCALL  Display_Text_KEVIN_T     ; display \"KEVIN T\"\n  LDI    R16, 7                   ; set page to 7\n  RCALL  Set_Cursor_Page          ; move cursor to page 7\n  RCALL  Display_Text_KEVIN_T     ; display \"KEVIN T\"\nprogram_loop:\n  RJMP   program_loop             ; infinite loop\n\n; ===================================================================\n; SUBROUTINE: TWI_Init\n; ===================================================================\n; Description: Initializes the Two-Wire Interface (TWI/I2C) module \n;              on the ATmega128P for communication at ~100kHz.\n; -------------------------------------------------------------------\n; Instructions: AVR Instruction Set Manual\n;               6.95 LDI – Load Immediate\n;               6.117 STS – Store Direct to Data Space\n;               6.88 RET – Return from Subroutine\n; ===================================================================\nTWI_Init:\n  ; -----------------------------------------------------------------\n  ; STEP 1: Set TWI Prescaler to 1\n  ; -----------------------------------------------------------------\n  LDI    R16, 0x00                ; load immediate val 0x00 into R16\n                                  ; 0x00 sets TWI Prescaler bits \n                                  ; (TWPS1:0) to 0\n                                  ; prescaler value: 1 (as 4^TWPS)\n  STS    TWSR, R16                ; store the value in TWI Status\n                                  ; Register (TWSR)\n                                  ; TWSR[1:0] (TWPS1, TWPS0) set the \n                                  ; prescaler\n  ; -----------------------------------------------------------------\n  ; STEP 2: Set TWI Bit Rate Register for ~100kHz\n  ; -----------------------------------------------------------------\n  LDI    R16, 72                  ; load immediate value 72 into R16\n                                  ; this value sets the SCL freq\n                                  ; SCL = F_CPU / \n                                  ; (16 + 2 * TWBR * 4^TWPS)\n                                  ; for F_CPU = 16MHz, TWPS = 1, \n                                  ; TWBR = 72:\n                                  ; SCL ≈ 100kHz\n  STS    TWBR, R16                ; store the value in TWI Bit Rate \n                                  ; Register (TWBR)\n                                  ; TWBR determines the SCL freq\n  ; -----------------------------------------------------------------\n  ; STEP 3: Enable TWI Module\n  ; -----------------------------------------------------------------\n  LDI    R16, (1 \u003c\u003c TWEN)         ; load immediate value with TWEN \n                                  ; bit set\n                                  ; TWEN (TWI Enable Bit) enables the\n                                  ; TWI hardware\n  STS    TWCR, R16                ; store the value in TWI Control \n                                  ; Register (TWCR)\n                                  ; TWCR enables TWI operations with\n                                  ; TWEN\n  RET                             ; return from subroutine\n\n; ===================================================================\n; SUBROUTINE: TWI_Write_Byte\n; ===================================================================\n; Description: Sends a start condition, writes the TWI address, \n;              control byte, and data byte, then issues a stop \n;              condition.\n; -------------------------------------------------------------------\n; Inputs: R22 - Control Byte (0x00: Command, 0x40: Data)\n;         R23 - Data Byte\n; -------------------------------------------------------------------\n; Instructions: AVR Instruction Set Manual\n;               6.87 RCALL – Relative Call to Subroutine\n;               6.95 LDI – Load Immediate\n;               6.75 MOV – Copy Register\n;               6.88 RET – Return from Subroutine\n;               6.39 CLR - Clear Register\n;               6.83 ORI – Logical OR with Immediate\n;               6.117 STS – Store Direct to Data Space\n;               6.70 LDS – Load Direct from Data Space\n;               6.101 SBRS – Skip if Bit in Register is Set\n;               6.90 RJMP – Relative Jump\n; ===================================================================\nTWI_Write_Byte:\n  RCALL  .TWI_Start               ; send start condition\n  LDI    R24, (SSD1306_ADDR \u003c\u003c 1) ; load SSD1306 addr w/ write bit\n  RCALL  .TWI_Write               ; write SSD1306 address\n  MOV    R24, R22                 ; load control byte (Command/Data)\n  RCALL  .TWI_Write               ; write control byte\n  MOV    R24, R23                 ; load data byte\n  RCALL  .TWI_Write               ; write data byte\n  RCALL  .TWI_Stop                ; send stop condition\n  RET                             ; return from subroutine\n.TWI_Start:\n  CLR    R16                      ; clear R16 (initialize to 0)\n  ORI    R16, (1\u003c\u003cTWINT)          ; set TWINT bit to clear int flag\n  ORI    R16, (1\u003c\u003cTWSTA)          ; set TWSTA bit for start cond\n  ORI    R16, (1\u003c\u003cTWEN)           ; set TWEN bit to enable TWI\n  STS    TWCR, R16                ; write to TWCR to start cond\n.TWI_Wait_Start:\n  LDS    R17, TWCR                ; read TWCR\n  SBRS   R17, TWINT               ; skip if TWINT flag is set\n  RJMP   .TWI_Wait_Start          ; wait for start cond complete\n  RET                             ; return from subroutine\n.TWI_Write:\n  STS    TWDR, r24                ; Load data into TWDR\n  CLR    R16                      ; clear R16 (initialize to 0)\n  ORI    R16, (1\u003c\u003cTWINT)          ; set TWINT bit clear int flag\n  ORI    R16, (1\u003c\u003cTWEN)           ; set TWEN bit to enable TWI trans\n  STS    TWCR, R16                ; write to TWCR to start trans\n.TWI_Write_Wait:\n  LDS    R17, TWCR                ; read TWCR\n  SBRS   R17, TWINT               ; skip if TWINT flag is set\n  RJMP   .TWI_Write_Wait          ; wait until write is complete\n  RET                             ; return from subroutine\n.TWI_Stop:\n  CLR    R16                      ; clear R16 (initialize to 0)\n  ORI    R16, (1\u003c\u003cTWINT)          ; set TWINT bit to clear int flag\n  ORI    R16, (1\u003c\u003cTWSTO)          ; set TWSTO bit for stop condition\n  ORI    R16, (1\u003c\u003cTWEN)           ; set TWEN bit to enable TWI\n  STS    TWCR, R16                ; write to TWCR to init stop cond\n  RET                             ; return from subroutine\n\n; ===================================================================\n; SUBROUTINE: SSD1306_Init\n; ===================================================================\n; Description: Initializes the SSD1306 OLED display by sending a \n;              sequence of commands.\n; -------------------------------------------------------------------\n; Instructions: AVR Instruction Set Manual\n;               6.95 LDI – Load Immediate\n;               6.87 RCALL – Relative Call to Subroutine\n;               6.88 RET – Return from Subroutine\n; ===================================================================\nSSD1306_Init:\n  ; -----------------------------------------------------------------\n  ; STEP 1: Turn Display OFF\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xAE                ; cmd: Display OFF\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 2: Set Display Clock Divide Ratio/Oscillator Frequency\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xD5                ; cmd: set Disp Clock Divide Ratio\n  RCALL  TWI_Write_Byte           ; send cmd to SSD1306\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x80                ; div Ratio and Oscillator Freq\n  RCALL  TWI_Write_Byte           ; send value to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 3: Set Multiplex Ratio\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xA8                ; cmd: Set Multiplex Ratio\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x3F                ; 1/64 duty for 128x64 display\n  RCALL  TWI_Write_Byte           ; send value to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 4: Set Display Offset\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xD3                ; cmd: Set Display Offset\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x00                ; value: No offset\n  RCALL  TWI_Write_Byte           ; send value to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 5: Set Start Line\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x40                ; cmd: Set Start Line at 0\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 6: Enable Charge Pump\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x8D                ; cmd: Charge Pump Setting\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x14                ; value: Enable charge pump\n  RCALL  TWI_Write_Byte           ; send value to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 7: Set Memory Addressing Mode\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x20                ; cmd: Set Memory Addressing Mode\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x00                ; value: Horizontal Addressing Mode\n  RCALL  TWI_Write_Byte           ; send value to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 8: Set Segment Re-map\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xA1                ; cmd: Segment Re-map\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 9: Set COM Output Scan Direction\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xC8                ; cmd: COM Output Scan Direction\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 10: Set COM Pins Hardware Configuration\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xDA                ; cmd: Set COM Pins Hardware Config\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x12                ; value: alt COM pin config\n  RCALL  TWI_Write_Byte           ; send value to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 11: Set Contrast Control\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x81                ; cmd: Set Contrast Control\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xCF                ; value: Set contrast\n  RCALL  TWI_Write_Byte           ; send value to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 12: Set Pre-Charge Period\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xD9                ; cmd: Set Pre-charge Period\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xF1                ; value: P1 = 15 DCLKs, P2 = 1 DCLK\n  RCALL  TWI_Write_Byte           ; send value to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 13: Set VCOMH Deselect Level\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xDB                ; cmd: Set VCOMH Deselect Level\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0x40                ; Value: 0.77 x Vcc\n  RCALL  TWI_Write_Byte           ; send value to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 14: Set Entire Display On/Resume\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xA4                ; cmd: Resume to RAM Content Disp\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 15: Set Normal Display\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xA6                ; cmd: Normal Display\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  ; -----------------------------------------------------------------\n  ; STEP 16: Turn Display On\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; load Control Byte (Command Mode)\n  LDI    R23, 0xAF                ; cmd: Display ON\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  RET                             ; return from subroutine\n\n; ===================================================================\n; SUBROUTINE: SSD1306_Clear\n; ===================================================================\n; Description: Clears the OLED screen and resets the cursor to the \n;              top-left.\n; -------------------------------------------------------------------\n; Instructions: AVR Instruction Set Manual\n;               6.95 LDI – Load Immediate\n;               6.86 PUSH – Push Register on Stack\n;               6.87 RCALL – Relative Call to Subroutine\n;               6.49 DEC – Decrement\n;               6.23 BRNE – Branch if Not Equal\n;               6.85 POP – Pop Register from Stack\n;               6.88 RET – Return from Subroutine\n; ===================================================================\nSSD1306_Clear:\n  ; -----------------------------------------------------------------\n  ; STEP 1: Clear OLED Screen\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x40                ; Control Byte: Data Mode\n  LDI    R23, 0x00                ; Data Byte: 0x00 (Clear)\n  LDI    R24, 8                   ; 8 pages (for 128x64 display)\n.Clear_Loop:\n  PUSH   R24                      ; save page counter\n  LDI    R25, 128                 ; 128 columns per page\n.Clear_Page:\n  RCALL  TWI_Write_Byte           ; write 0x00 to clear pixel\n  DEC    R25                      ; decrement column counter\n  BRNE   .Clear_Page              ; repeat until all cols cleared\n  POP    R24                      ; restore page counter\n  DEC    R24                      ; decrement page counter\n  BRNE   .Clear_Loop              ; repeat for all 8 pages\n  ; -----------------------------------------------------------------\n  ; STEP 2: Reset Cursor to Top-Left (0,0)\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00                ; Control Byte: Command Mode\n  LDI    R23, 0x21                ; cmd: Set Column Address\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  LDI    R23, 0x00                ; Column Start Address (0)\n  RCALL  TWI_Write_Byte           ; send start address\n  LDI    R23, 0x7F                ; Column End Address (127)\n  RCALL  TWI_Write_Byte           ; send end address\n  LDI    R22, 0x00                ; Control Byte: Command Mode\n  LDI    R23, 0x22                ; cmd: Set Page Address\n  RCALL  TWI_Write_Byte           ; send command to SSD1306\n  LDI    R23, 0x00                ; Page Start Address (0)\n  RCALL  TWI_Write_Byte           ; send start address\n  LDI    R23, 0x07                ; Page End Address (7)\n  RCALL  TWI_Write_Byte           ; send end address\n  RET                             ; return from subroutine\n\n; ===================================================================\n; SUBROUTINE: Set_Cursor_Page\n; ===================================================================\n; Description: Sets the cursor to a specific page (row) on the OLED\n;              display.\n; -------------------------------------------------------------------\n; Inputs: R16 - Page Number (0 to 7 for 128x64 display)\n; -------------------------------------------------------------------\n; Instructions: AVR Instruction Set Manual\n;               6.86 PUSH – Push Register on Stack\n;               6.95 LDI – Load Immediate\n;               6.87 RCALL – Relative Call to Subroutine\n;               6.85 POP – Pop Register from Stack\n;               6.75 MOV – Copy Register\n;               6.88 RET – Return from Subroutine\n; ===================================================================\nSet_Cursor_Page:\n  ; -----------------------------------------------------------------\n  ; STEP 1: Set Column Address to Start at 0\n  ; -----------------------------------------------------------------\n  PUSH   R16                     ; save R16 to the stack\n  LDI    R22, 0x00               ; Control Byte: Command Mode\n  LDI    R3, 0x21                ; cmd: Set Column Address\n  RCALL  TWI_Write_Byte          ; send command\n  LDI    R23, 0x00               ; Column Start Address: 0\n  RCALL  TWI_Write_Byte          ; send start address\n  LDI    R23, 0x7F               ; Column End Address: 127\n  RCALL  TWI_Write_Byte          ; send end address\n  ; -----------------------------------------------------------------\n  ; STEP 2: Set Page Address\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00               ; Control Byte: Command Mode\n  LDI    R23, 0x22               ; cmd: Set Page Address\n  RCALL  TWI_Write_Byte          ; send command\n  POP    R16                     ; restore R16 from the stack\n  MOV    R23, R16                ; copy page number from R16 to R23\n  RCALL  TWI_Write_Byte          ; send page start address\n  ; -----------------------------------------------------------------\n  ; STEP 3: Reset Cursor to Leftmost Column (Column Address 0)\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x00               ; Control Byte: Command Mode\n  LDI    R23, 0x00               ; cmd: Set Column Address Start\n  RCALL  TWI_Write_Byte          ; send column start address\n  RET                            ; return from subroutine\n\n; ===================================================================\n; SUBROUTINE: Display_Char\n; ===================================================================\n; Description: Displays a single character on the OLED screen.\n; -------------------------------------------------------------------\n; Inputs: R16 - ASCII code of the letter to display\n; -------------------------------------------------------------------\n; Instructions: AVR Instruction Set Manual\n;               6.95 LDI – Load Immediate\n;               6.47 CPI – Compare with Immediate\n;               6.14 BREQ – Branch if Equal\n;               6.20 BRLO – Branch if Lower (Unsigned)\n;               6.11 BRCC – Branch if Carry Cleared\n;               6.120 SUBI – Subtract Immediate\n;               6.87 RCALL – Relative Call to Subroutine\n;               6.75 MOV – Copy Register\n;               6.73 LSL – Logical Shift Left\n;               6.2 ADD – Add without Carry\n;               6.1 ADC – Add with Carry\n;               6.72 LPM – Load Program Memory\n;               6.88 RET – Return from Subroutine\n; ===================================================================\nDisplay_Char:\n  ; -----------------------------------------------------------------\n  ; STEP 1: Check Character Validity\n  ; -----------------------------------------------------------------\n  LDI    R22, 0x40                ; Control Byte: Data Mode\n  CPI    R16, 0x20                ; check if char is SPACE (0x20)\n  BREQ   .Is_Space                ; branch if equal to .Is_Space \n  CPI    R16, 'A'                 ; check if char is 'A' or higher\n  BRLO   .Invalid_Char            ; if less than 'A', invalid\n  CPI    R16, 'Z'                 ; check if char is 'Z' or lower\n  BRCC   .Invalid_Char            ; if greater than 'Z', invalid\n  SUBI   R16, 'A'                 ; .calculate index: R16 - 'A'\n  RJMP   .Calculate_Index         ; jump to .Calculate_Index\n.Is_Space:\n  LDI    R16, 26                  ; index for space in Font_Table\n  RJMP   .Calculate_Index         ; jump to .Calculate_Index\n.Invalid_Char:\n  LDI    R16, 26                  ; SPACE as default invalid chars\n.Calculate_Index:\n  ; -----------------------------------------------------------------\n  ; STEP 2: Calculate Font Data Address\n  ; -----------------------------------------------------------------\n  LDI    R30, LO8(font_table)     ; load lower byte into base addr\n  LDI    R31, HI8(font_table)     ; load higher byte into base addr\n  MOV    R17, R16                 ; copy index to R17\n  LSL    R17                      ; R17 = index * 2\n  ADD    R17, R16                 ; R17 = index * 3\n  LSL    R17                      ; R17 = index * 6\n  ADD    R17, R16                 ; R17 = index * 7\n  ADD    R30, R17                 ; add offset Z-pointer (low byte)\n  ADC    R31, R1                  ; add carry Z-pointer (high byte)\n  ; -----------------------------------------------------------------\n  ; STEP 3: Display Character\n  ; -----------------------------------------------------------------\n  LPM    R23, Z+                  ; load font byte 1\n  RCALL  TWI_Write_Byte           ; write font byte 1\n  LPM    R23, Z+                  ; load font byte 2\n  RCALL  TWI_Write_Byte           ; write font byte 2\n  LPM    R23, Z+                  ; load font byte 3\n  RCALL  TWI_Write_Byte           ; write font byte 3\n  LPM    R23, Z+                  ; load font byte 4\n  RCALL  TWI_Write_Byte           ; write font byte 4\n  LPM    R23, Z+                  ; load font byte 5\n  RCALL  TWI_Write_Byte           ; write font byte 5\n  LPM    R23, Z+                  ; load font byte 6\n  RCALL  TWI_Write_Byte           ; write font byte 6\n  LPM    R23, Z+                  ; load font byte 7\n  RCALL  TWI_Write_Byte           ; write font byte 7\n  RET                             ; return from subroutine\n\n; ===================================================================\n; SUBROUTINE: Display_Text_KEVIN_T\n; ===================================================================\n; Description: Displays the text \"KEVIN T\" on the current cursor \n;              position of the OLED display.\n; -------------------------------------------------------------------\n; Instructions: AVR Instruction Set Manual\n;               6.95 LDI – Load Immediate\n;               6.87 RCALL – Relative Call to Subroutine\n;               6.88 RET – Return from Subroutine\n; ===================================================================\nDisplay_Text_KEVIN_T:\n  ; Display \"KEVIN T\"\n  LDI    R16, 'K'                 ; load ASCII value of 'K' into R16\n  RCALL  Display_Char             ; display 'K'\n  LDI    R16, 'E'                 ; load ASCII value of 'E' into R16\n  RCALL  Display_Char             ; display 'E'\n  LDI    R16, 'V'                 ; load ASCII value of 'V' into R16\n  RCALL  Display_Char             ; display 'V'\n  LDI    R16, 'I'                 ; load ASCII value of 'I' into R16\n  RCALL  Display_Char             ; display 'I'\n  LDI    R16, 'N'                 ; load ASCII value of 'N' into R16\n  RCALL  Display_Char             ; display 'N'\n  LDI    R16, ' '                 ; load ASCII value of space R16\n  RCALL  Display_Char             ; display space\n  LDI    R16, 'T'                 ; load ASCII value of 'T' into R16\n  RCALL  Display_Char             ; display 'T'\n  RET                             ; return from subroutine\n\n; ===================================================================\n; FONT TABLE: A-Z AND SPACE\n; ===================================================================\n; Description: Defines the font data for characters A-Z and space as  \n;              each character consists of 7 bytes.\n; ===================================================================\nFont_Table:\n  ; LETTER_A\n  .byte 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00\n  ; LETTER_B\n  .byte 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00\n  ; LETTER_C\n  .byte 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00\n  ; LETTER_D\n  .byte 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, 0x00 \n  ; LETTER_E\n  .byte 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00\n  ; LETTER_F\n  .byte 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x00\n  ; LETTER_G\n  .byte 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, 0x00\n  ; LETTER_H\n  .byte 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00\n  ; LETTER_I\n  .byte 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x00\n  ; LETTER_J\n  .byte 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00\n  ; LETTER_K\n  .byte 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x00\n  ; LETTER_L\n  .byte 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00\n  ; LETTER_M\n  .byte 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, 0x00\n  ; LETTER_N\n  .byte 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00\n  ; LETTER_O\n  .byte 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00\n  ; LETTER_P\n  .byte 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00\n  ; LETTER_Q\n  .byte 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00\n  ; LETTER_R\n  .byte 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00\n  ; LETTER_S\n  .byte 0x00, 0x46, 0x49, 0x49, 0x49, 0x31, 0x00\n  ; LETTER_T\n  .byte 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, 0x00\n  ; LETTER_U\n  .byte 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00\n  ; LETTER_V\n  .byte 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00\n  ; LETTER_W\n  .byte 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00\n  ; LETTER_X\n  .byte 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, 0x00\n  ; LETTER_Y\n  .byte 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, 0x00\n  ; LETTER_Z\n  .byte 0x00, 0x61, 0x51, 0x49, 0x45, 0x43, 0x00\n  ; SPACE\n  .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n```\n\n\u003cbr\u003e\n\n# License\n[Apache License 2.0](https://github.com/mytechnotalent/ATmega328P_SSD1306_Driver/blob/main/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmytechnotalent%2Fatmega328p_ssd1306_driver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmytechnotalent%2Fatmega328p_ssd1306_driver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmytechnotalent%2Fatmega328p_ssd1306_driver/lists"}