;reverse engineering the Micros 2700 display (Emerging EG64E00BCWU) ;controlled by MSM6255 (marked "M6255") chip ;http://dorkbotpdx.org/wiki/PaulStoffregen ;build this code with TAVRASM ;Copyright (c) 2008 Paul Stoffregen ; ;Permission is hereby granted, free of charge, to any person ;obtaining a copy of this software and associated documentation ;files (the "Software"), to deal in the Software without ;restriction, including without limitation the rights to use, ;copy, modify, merge, publish, distribute, sublicense, and/or sell ;copies of the Software, and to permit persons to whom the ;Software is furnished to do so, subject to the following ;conditions: ; ;The above copyright notice and this permission notice shall be ;included in all copies or substantial portions of the Software. ; ;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ;OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ;HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ;WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ;FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ;OTHER DEALINGS IN THE SOFTWARE. .include "mega88.asm" .equ ubrr_value = 9 ;115200 baud at 18.432 MHz ;***************************************************************** ;** ** ;** Memory Allocation ** ;** ** ;***************************************************************** .def r_zero = r2 ;always zero .def r_image = r3 ;which image to display (0 to 7) .def r_idle_count = r4 ;how long have we been idle .dseg .org 0x100 buffer: .byte 257 .equ ramend = 0x4F0 ;***************************************************************** ;** ** ;** Pinouts ** ;** ** ;***************************************************************** ;port B pinouts .equ un1_pin = 0 .equ un2_pin = 1 .equ ss_pin = 2 .equ ss_port = portb .equ mosi_pin = 3 .equ miso_pin = 4 .equ sck_pin = 5 .equ portb_cfg = (1<> 8) out sph, r16 clr r_zero clr r_image clr r_idle_count ;Initialize all I/O pins (I/O docs start on page 47) init_ports: ldi r16, portb_default out portb, r16 ldi r16, portb_cfg out ddrb, r16 ldi r16, portc_default out portc, r16 ldi r16, portc_cfg out ddrc, r16 ldi r16, portd_default out portd, r16 ldi r16, portd_cfg out ddrd, r16 init_peripherals: rcall init_uart rcall init_spi rcall clear_framebuffer rcall config_lcd rcall update_lcd ;***************************************************************** ;** ** ;** Main Program ** ;** ** ;***************************************************************** main_loop: rcall cin_timeout brtc command ;ldi r16, '.' ;rcall cout inc r_idle_count ldi r16, 20 cp r_idle_count, r16 brlo main_loop inc r_image clr r_idle_count rcall update_lcd rjmp main_loop ;we received a command from the serial port... command: clr r_idle_count cpi r16, 0x95 breq command_read cpi r16, 0x6A breq command_write cpi r16, 0x3B breq command_display ;for all unknown bytes, echo back the byte with some bits inverted command_unknown: ldi r17, 0x5A eor r16, r17 rcall cout rjmp main_loop ;the PC wants us to display a certain image NOW command_display: ldi r16, 0xD6 ;send a byte to let the PC know we rcall cout ;are ready to receive the image number rcall cin_timeout brts main_loop mov r_image, r16 rcall update_lcd rjmp main_loop ;we're supposed to send a page to the PC command_read: ldi r16, 0x46 ;send a byte to let the PC know we rcall cout ;are ready to receive the address rcall cin_timeout brts main_loop mov Xl, r16 ;receive the page address from the PC rcall cin_timeout brts main_loop mov Xh, r16 rcall flash_read ldi Yl, low(buffer) ldi Yh, high(buffer) cmd_read_loop: ld r16, Y+ rcall cout cpi Yl, low(buffer) brne cmd_read_loop rjmp main_loop ;we're supposed to write a page that the PC will send us command_write: ldi r16, 0x1A ;send a byte to let the PC know we rcall cout ;are ready to receive the address rcall cin_timeout brts main_loop mov Xl, r16 ;receive the page address from the PC rcall cin_timeout brts main_loop mov Xh, r16 ldi Yl, low(buffer) ldi Yh, high(buffer) cmd_write_loop: rcall cin_timeout brts main_loop st Y+, r16 ;receive each byte cpi Yl, low(buffer) brne cmd_write_loop rcall flash_write ldi r16, 0xA3 ;send a confirm byte rcall cout rjmp main_loop ;***************************************************************** ;** ** ;** Flash ** ;** ** ;***************************************************************** ;X is page address to read into buffer flash_read: cbi ss_port, ss_pin ;select flash chip ldi r16, 0x52 out SPDR, r16 ;read opcode rcall dly17 movw r16, Xl lsl r16 rol r17 out SPDR, r17 ;24 bit address rcall dly17 out SPDR, r16 ;24 bit address rcall dly17 out SPDR, r_zero ;24 bit address rcall dly17 out SPDR, r_zero ;32 don't care bits rcall dly17 out SPDR, r_zero ;32 don't care bits rcall dly17 out SPDR, r_zero ;32 don't care bits rcall dly17 out SPDR, r_zero ;32 don't care bits rcall dly17 ldi Zl, low(buffer) ldi Zh, high(buffer) flash_read_loop: out SPDR, r_zero rcall dly17 in r16, SPDR ;read each byte st Z+, r16 cpi Zl, low(buffer + 257) brne flash_read_loop cpi Zh, high(buffer + 257) brne flash_read_loop sbi ss_port, ss_pin ;this flash chip seems to have a bug where the entire ;output is off by 1 bit (or the 32 don't care bits ;should really have been 33) ldi Zl, low(buffer) ldi Zh, high(buffer) flash_fixup_loop: ld r16, Z ld r17, Z+1 rol r17 rol r16 st Z+, r16 cpi Zl, low(buffer + 257) brne flash_fixup_loop cpi Zh, high(buffer + 257) brne flash_fixup_loop ret ;X is page address to write from buffer flash_write: cbi ss_port, ss_pin ;select flash chip ldi r16, 0x82 out SPDR, r16 ;write opcode rcall dly17 movw r16, Xl lsl r16 rol r17 out SPDR, r17 ;24 bit address rcall dly17 out SPDR, r16 ;24 bit address rcall dly17 out SPDR, r_zero ;24 bit address rcall dly17 ldi Zl, low(buffer) ldi Zh, high(buffer) flash_write_loop: ld r16, Z+ out SPDR, r16 rcall dly17 cpi Zl, low(buffer + 256) brne flash_write_loop cpi Zh, high(buffer + 256) brne flash_write_loop sbi ss_port, ss_pin rcall delay_20ms ret ;***************************************************************** ;** ** ;** LCD ** ;** ** ;***************************************************************** ;copy an entire image from the flash into the framebuffer ;r_image is 0-7, which image to read from the flash update_lcd: ldi r16, 7 and r_image, r16 mov Xh, r_image clr Xl lsr Xh ;each image is 64 pages (256 bytes per page) ror Xl lsr Xh ror Xl clr r24 clr r25 ldi r19, 64 update_lcd_loop: rcall flash_read rcall copy_to_framebuffer adiw Xl, 1 dec r19 brne update_lcd_loop ret ;copy 256 bytes into framebuffer ;r25/r24 is framebuffer address to write into copy_to_framebuffer: ldi Yl, low(buffer) ldi Yh, high(buffer) copy_to_framebuffer_loop: ld r16, Y+ rcall write_framebuffer adiw r24, 1 cpi Yl, low(buffer) brne copy_to_framebuffer_loop ret ;r16 is data to write ;r25/r24 is address to write into write_framebuffer: out SPDR, r25 rcall dly17 out SPDR, r24 rcall dly17 out SPDR, r16 rcall dly17 ;TODO: examine the CH0 signal and wait for a no-flicker moment cbi ram_en_port, ram_en_pin nop cbi ram_wr_port, ram_wr_pin nop sbi ram_wr_port, ram_wr_pin sbi ram_en_port, ram_en_pin ret clear_framebuffer: clr r24 clr r25 clr r16 clear_framebuffer_loop: rcall write_framebuffer adiw r24, 1 cpi r25, 0x80 brlo clear_framebuffer_loop ret config_lcd: .equ cfg = 0x0F ;graphics mode, 4 bit, LCD on (page 12) .equ Hp = 8 ;horizontal resolution is Hp * Hn .equ Hn = 80 .equ Vl = 400 ;vertical resolution is Vl (no lower half) .equ Vp = 1 ldi r16, cfg ldi r17, 0 ;mode control register rcall write_lcd_ctrl ldi r16, ((Vp - 1) << 4) | (Hp - 1) ldi r17, 1 ;Character pitch register rcall write_lcd_ctrl ldi r16, (Hn - 1) ldi r17, 2 ;Horizontal character number register rcall write_lcd_ctrl ldi r16, ((Vl / 2) - 1) ldi r17, 3 ;Duty number register rcall write_lcd_ctrl ldi r16, 0x77 ldi r17, 4 ;Cursor form register rcall write_lcd_ctrl ldi r16, 0 ldi r17, 5 ;start address low rcall write_lcd_ctrl ldi r16, 0 ldi r17, 6 ;start address high rcall write_lcd_ctrl ldi r16, 0 ldi r17, 7 rcall write_lcd_ctrl ldi r16, 0 ldi r17, 8 rcall write_lcd_ctrl ret ;r16 is data to write ;r17 is register address to write it into write_lcd_ctrl: ldi r18, 255 out SPDR, r18 rcall dly17 out SPDR, r18 rcall dly17 out SPDR, r17 rcall dly17 cbi lcd_wr_port, lcd_wr_pin nop sbi lcd_wr_port, lcd_wr_pin out SPDR, r_zero rcall dly17 out SPDR, r_zero rcall dly17 out SPDR, r16 rcall dly17 cbi lcd_wr_port, lcd_wr_pin nop sbi lcd_wr_port, lcd_wr_pin ret init_spi: ldi r16, (1<