.include "m162def.inc" .include "header.asm" .NOLIST .LIST .equ KEYLOCK_TIMEOUT = 255 ;-----variables for basic operation--- .dseg .org 0x100 sec_buff: .BYTE 512 ;sector buffer, in internal RAM for fast access temp128: .BYTE 128 ;128 bytes of tempory space.. lba: .BYTE 3 ;current lba lba_old: .BYTE 3 ;old lba (sector currently in buffer) cluster: .BYTE 3 ;current cluster lba_begin: .BYTE 3 ;start of current fat32 lba fat_begin: .BYTE 3 ;start of FAT32 table cluster_begin: .BYTE 3 ;start of clusters spc: .BYTE 1 ;sectors per cluster root_dir_cluster:.Byte 3 ;cluster in which the root dir is in vram_offset: .BYTE 1 ;master offset line_offset: .BYTE 6 ;each line on the lcd can be offset for scrolling (added onto vram offset) scroll_line: .BYTE 1 ;each bit decies if line is to be scrolled..bit 0 = top line scroll_line_dir:.BYTE 1 ;direction each line scrolls..0 = to the left number_of_files: .BYTE 1 ;total no of files in a dir status: .BYTE 1 ;status,0-mp3 playing, 1 -lcd refresh, 2- play next mp3 ;3 - is keylock on/off buttons: .BYTE 1 ;button values crashed_cnt: .BYTE 1 ;counts how many times mp3 ic crashed.for testing mostly keylock_tmr: .BYTE 1 ;timer for auto keylock keylock_cnt: .BYTE 1 ;counts key presses when keylock on ;---internal variable for mp3 operation-- mp3_current_cp: .BYTE 1 ;current postion in cluster (0 = end) mp3_cluster_16: .BYTE 1 ;cluster size / 16 mp3_next_file: .BYTE 3 ;used to cue up a new mp3 file, contains cluster number mp3_current_file:.BYTE 1 ;current file playing (file_list postions) .def posx = r6 ;graphics cordinates .def posy = r7 ;graphics cordinates ;----Variable below this line are in external memory---- .org 0x500 file_list: .BYTE 16384 ;stores list of files in current directory ;currently there are 128 entries of 128 bytes long. ;0 = attrib, 1-12 short file name,..cluster size(4),..size(4)..LFN cluster_buff: .BYTE 8192 ;8K of cluster space..limits cluster size to 16 sectors.. vram: .BYTE 1540 ;256 per line, for scrolling vram1: .BYTE 512 vram2: .BYTE 512 .equ spi_cs_port = portb ;--mmc pins-- .equ mmc_cs = 0 ;--mp3 pins--- .equ mp3_cs = 4 .equ mp3_bs_port = portd .equ mp3_bs = 0 ;--lcd pins-- .equ lcd_cs_port = portd .equ lcd_cs = 5 .equ lcd_dc_port = portb .equ lcd_dc = 1 ;--button pins-- .equ button_port1 = portd .equ button_port2 = portd .equ button_port3 = porte .equ button_port4 = porte .equ button_pin1 = 1 .equ button_pin2 = 2 .equ button_pin3 = 0 .equ button_pin4 = 2 .cseg .org 0x0000 rjmp prog_start .org 0x01a ;compare A jmp timer1_int .org 0x022 ;overfolw jmp timer0_int .org 0x039 prog_start: ;setup stack LDI R16, HIGH(RAMEND) ; Upper byte OUT SPH,R16 ; to stack pointer LDI R16, LOW(RAMEND) ; Lower byte OUT SPL,R16 ; to stack pointer ;--setup port directions----- sbi spi_cs_port,mp3_cs ;mp3 cs pin sbi spi_cs_port-1,mp3_cs ;SS must be output for SPI to work sbi spi_cs_port,mmc_cs ;mmc cs pin sbi spi_cs_port-1,mmc_cs sbi lcd_cs_port,lcd_cs ;lcd cs pin sbi lcd_cs_port-1,lcd_cs cbi mp3_bs_port,mp3_bs ;mp3 byte sync pin sbi mp3_bs_port-1,mp3_bs cbi lcd_dc_port,lcd_dc ;lcd command/data sbi lcd_dc_port-1,lcd_dc sbi button_port1,button_pin1 cbi button_port1-1,button_pin1 sbi button_port2,button_pin2 cbi button_port2-1,button_pin2 sbi button_port3,button_pin3 cbi button_port3-1,button_pin3 sbi button_port4,button_pin4 cbi button_port4-1,button_pin4 cbi PORTD,4 sbi DDRD,4 ;mp3 reset, testing sbi DDRB,7 ;clock as output sbi DDRB,5 ;spi out as output ;---setup rs232 on USART1-------- ldi r16,0 out UBRR1H,r16 ldi r16,8 out UBRR1L,r16 ldi r16,0x86 out UCSR1C,r16 ldi r16,0 out UCSR1A,r16 ldi r16,0x18 out UCSR1B,r16 ;---setup Timer 1---- ldi r16,0b01000000 out TIMSK,r16 ldi r16,0b00000011 out TCCR1B,r16 ldi r16,0 ldi r17,50 out OCR1AH,r17 out OCR1AL,r16 ;---setup Timer 0---- in r16,TIMSK sbr r16,2 out TIMSK,r16 ldi r16,0b00000100 out TCCR0,r16 ;---setup external memory----- ldi r16,0x80 out MCUCR,r16 ;-----setup SPI---------------- ldi r16,0b01010001 out SPCR,r16 ldi r16,0b1 out SPSR,r16 ;double speed ;----initalise vars-------- ldi r16,0xff sts lba_old+0,r16 sts lba+0,r16 ldi r16,0xff sts lba_old+1,r16 sts lba+1,r16 ldi r16,0xff sts lba_old+2,r16 sts lba+2,r16 ldi r16,0xff sts spc,r16 ldi r16,64 sts mp3_cluster_16,r16 ldi r16,0 sts status,r16 sts scroll_line,r16 sts scroll_line_dir,r16 sts keylock_tmr,r16 clr r16 sts vram_offset,r16 sts line_offset+0,r16 sts line_offset+1,r16 sts line_offset+2,r16 sts line_offset+3,r16 sts line_offset+4,r16 sts line_offset+5,r16 rcall clear_vram inc r16 mov posy,r16 rcall lcd_invert_line ;rcall clear_vram1 k: ;rcall process_buttons ;rjmp k rcall initialise_hardware rjmp main main: ldi r16,'S' rcall rs232_tx ldi r16,'t' rcall rs232_tx ldi r16,'a' rcall rs232_tx ldi r16,'r' rcall rs232_tx ldi r16,'t' rcall rs232_tx rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall mmc_reset rcall initalise_file_list rcall delay_1ms rcall fat32_initalise lds r16,root_dir_cluster+0 sts cluster+0,r16 lds r16,root_dir_cluster+1 sts cluster+1,r16 lds r16,root_dir_cluster+2 sts cluster+2,r16 rcall read_file_list ;read root dir rjmp mp3_player ;----------------------------------------------------------------------- ;mp3_player ;purpose: mp3_player ;Takes: ;Give: .def display_pos = r20 ;postion in list on screen .def file_pos = r21 ;postion in file list mp3_player: clr display_pos clr file_pos lds r16,root_dir_cluster+0 sts cluster+0,r16 lds r16,root_dir_cluster+1 sts cluster+1,r16 lds r16,root_dir_cluster+2 sts cluster+2,r16 rcall read_file_list ;read root dir clr posy rcall display_list rcall lcd_invert_line ldi r16,0 sts status,r16 ldi r16,1 sts scroll_line,r16 ldi r16,3 ; rcall play_file sei mp3_player_main: rcall wait_till_buttons_up mp3_player_loop: lds r17,buttons sbrc r17,1 rjmp mp3_player_down_list sbrc r17,0 rjmp mp3_player_select sbrc r17,3 rjmp mp3_player_up_list rjmp mp3_player_loop ; sbrc r17,0 ;rjmp b2 ;---- mp3_player_down_list: ;down list button pressed lds r16,number_of_files cp file_pos,r16 ;test if at bottom of file list breq mp3_player_main mov posy,display_pos rcall lcd_invert_line ;un-invert current line cpi display_pos,5 ;see if at bottom of display breq mp3_player_down_list_bottom inc display_pos lds r17,scroll_line lsl r17 sts scroll_line,r17 inc posy rcall lcd_invert_line ;invert new line inc file_pos rjmp mp3_player_main mp3_player_down_list_bottom: ;at bottom of display, so scroll up mov r16,file_pos subi r16,4 mov posy,r16 rcall display_list mov posy,display_pos rcall lcd_invert_line ;invert new line inc file_pos rjmp mp3_player_main ;---- mp3_player_up_list: ;up list button pressed cpi file_pos,0 ;test if at top of list breq mp3_player_main mov posy,display_pos rcall lcd_invert_line ;un-invert current line cpi display_pos,0 ;see if at bottom of display breq mp3_player_up_list_top lds r17,scroll_line lsr r17 sts scroll_line,r17 dec display_pos dec posy rcall lcd_invert_line ;invert new line dec file_pos rjmp mp3_player_main mp3_player_up_list_top: ;at bottom of display, so scroll up mov r16,file_pos subi r16,1 mov posy,r16 rcall display_list mov posy,display_pos rcall lcd_invert_line ;invert new line dec file_pos rjmp mp3_player_main ;----- mp3_player_select: mov r16,file_pos rcall play_file rjmp mp3_player_main wait_till_buttons_up: lds r16,buttons tst r16 brne wait_till_buttons_up ret ;----------------------------------------------------------------------- ;play_file ;purpose: plays a file from file list, number in r16 ;Takes: ;Give: play_file: push r0 push r1 push r17 push YL push YH ldi YL,LOW(file_list) ldi YH,HIGH(file_list) ldi r17,128 ;get to correct file entry mul r16,r17 add YL,r0 adc YH,r1 adiw YL,12 ;get past file name and attrib to curster start ld r17,Y+ sts mp3_next_file+0,r17 ld r17,Y+ sts mp3_next_file+1,r17 ld r17,Y+ sts mp3_next_file+2,r17 lds r17,status sbr r17,1 sbr r17,4 ;set second bit, this tell player to start a new mp3 sts mp3_current_file,r16 sts status,r17 pop YH pop YL pop r17 pop r1 pop r0 ret ;----------------------------------------------------------------------- ;display_list ;purpose: displays file list to lcd..posy is start of file list to display ;Takes: ;Give: display_list: push r0 push r1 push posx push posy push r17 push r18 push YL push YH ldi YL,LOW(file_list) ldi YH,HIGH(file_list) ldi r17,128 mul r17,posy add Yl,r0 adc YH,r1 adiw YL,26 clr posx clr posy ldi r18,6 display_list_loop: ldi r17,42 clr posx display_list_loop1: ld r16,Y+ rcall lcd_chr_vram dec r17 brne display_list_loop1 display_list_loop1_exit: adiw YL,63 adiw YL,23 inc posy dec r18 brne display_list_loop pop YH pop YL pop r18 pop r17 pop posy pop posx pop r1 pop r0 ret ;----------------------------------------------------------------------- ;timer1_int ;purpose: slow interupt, used for scrolling, seconds update ect ;Takes: ;Give: timer1_int: push r17 push r16 in r16,SREG push r16 cbi spi_cs_port,mp3_cs ;read seconds ldi r16,0x03 rcall spi_tx_byte ldi r16,0x04 ;seconds reg rcall spi_tx_byte rcall spi_rx_byte mov r17,r16 rcall spi_tx_byte sbi spi_cs_port,mp3_cs rcall Bin2ToBcd5 ;to bcd ldi YL,LOW(temp128) ldi YH,HIGH(temp128) ldi r16,48 mov posx,r16 ldi r16,5 mov posy,r16 ldi r17,48 ld r16,Y+ add r16,r17 rcall lcd_chr_vram ld r16,Y+ add r16,r17 rcall lcd_chr_vram ld r16,Y+ add r16,r17 rcall lcd_chr_vram ld r16,Y+ add r16,r17 rcall lcd_chr_vram ld r16,Y+ add r16,r17 rcall lcd_chr_vram ; lds r16,buttons ; add r16,r17 ; rcall lcd_chr_vram lds r16,crashed_cnt add r16,r17 rcall lcd_chr_vram rcall scroll_lines ;lds r16,line_offset+1 ; inc r16 ;sts line_offset+1,r16 ldi r16,0 ldi r17,0 out TCNT1H,r16 out TCNT1L,r17 in r16,TIFR cbr r16,64 out TIFR,r16 pop r16 out SREG,r16 pop r16 pop r17 reti scroll_lines: ;scrolls lines push r16 push r17 push r18 push YL push YH ldi YL,LOW(line_offset) ldi YH,HIGH(line_offset) lds r17,scroll_line ldi r16,6 scroll_lines_loop: sbrc r17,0 rcall scroll_lines_inc adiw YL,1 lsr r17 dec r16 brne scroll_lines_loop pop YH pop YL pop r18 pop r17 pop r16 ret scroll_lines_inc: ld r18,Y ;TODO check for direction inc r18 st Y,r18 ret ;----------------------------------------------------------------------- ;timer0_int ;purpose: called every 16ms approx, used to update lcd and fill mp3 buffer ;Takes: ;Give: timer0_int: push r16 in r16,SREG push r16 ; clr r16 rcall process_buttons ;get buttons and do auto keylocking ;-- lds r16,status ;find if refresh lcd or not (skips every other int) andi r16,2 ;read second bit tst r16 breq PC+8 rcall lcd_refresh ;nop lds r16,status cbr r16,2 ;toggle sts status,r16 rjmp PC+6 lds r16,status sbr r16,2 ;toggle sts status,r16 ;-- lds r16,status ;find if new mp3 file to play sbrc r16,2 rcall setup_new_mp3 ;-- lds r16,status ;find if playing mp3 or not sbrc r16,0 ;play! rcall mp3_fill_buffer ldi r16,0 out TCNT0,r16 in r16,TIFR cbr r16,2 clr r16 out TIFR,r16 pop r16 out SREG,r16 pop r16 reti ;----------------------------------------------------------------------- ;setup_new_mp3 ;purpose: setups up vs1001 to play a new mp3 file ;Takes: ;Give: setup_new_mp3: push r16 rcall reset_mp3 lds r16,mp3_next_file+0 sts cluster+0,r16 lds r16,mp3_next_file+1 sts cluster+1,r16 lds r16,mp3_next_file+2 sts cluster+2,r16 clr r16 rcall mp3_tx rcall mp3_tx rcall mp3_tx clr r16 sts mp3_current_cp,r16 sts crashed_cnt,r16 ldi YL,LOW(cluster_buff) ;read first cluster to buffer ldi YH,HIGH(cluster_buff) rcall mmc_read_cluster lds r16,status cbr r16,4 ;clear second bit, becuase done new file sts status,r16 pop r16 ret ;----------------------------------------------------------------------- ;mp3_fill_buffer ;purpose: must be called at least approx every 30ms. Fills up the mp3 buffer ;Takes: ;Give: mp3_fill_buffer: push r0 push r1 push r16 push r17 push r18 push r19 push r20 push YL push YH lds r17,mp3_current_cp lds r19,mp3_cluster_16 ldi r20,50 ;safty counter ; tst r17 ; breq mp3_fill_buffer_next_cluster mp3_fill_buffer_fill: sbis PIND,3 ;test if buffer full rjmp mp3_fill_buffer_fill_loop_exit inc r20 ;safty counter breq mp3_crashed ;if r20 has maxed, then mp3 ic has crashed ldi YL,LOW(cluster_buff) ldi YH,HIGH(cluster_buff) ldi r18,16 mul r18,r17 add YL,r0 ;add current postion offset adc YH,r1 mp3_fill_buffer_fill_loop: ldi r18,16 ;fill with 16 bytes mp3_fill_buffer_fill_loop1: ld r16,Y+ rcall mp3_tx dec r18 brne mp3_fill_buffer_fill_loop1 inc r17 ;next 16 bytes.. cp r17,r19 ;check if at end of cluster breq mp3_fill_buffer_next_cluster sbis PIND,3 ;test if buffer full rjmp mp3_fill_buffer_fill_loop_exit rjmp mp3_fill_buffer_fill_loop mp3_fill_buffer_fill_loop_exit: sts mp3_current_cp,r17 ;store current pos for next time. pop YH pop YL pop r20 pop r19 pop r18 pop r17 pop r16 pop r1 pop r0 ret mp3_fill_buffer_next_cluster: rcall cluster_link tst r16 ;check if at last cluster brne mp3_end_of_file ldi YL,LOW(cluster_buff) ldi YH,HIGH(cluster_buff) rcall mmc_read_cluster clr r17 rjmp mp3_fill_buffer_fill mp3_end_of_file: ; lds r16,status ; cbr r16,1 ;stop playing ; sts status,r16 lds r16,mp3_current_file ;TODO, check if at end of play list inc r16 rcall play_file ;play next file rjmp mp3_fill_buffer_fill_loop_exit mp3_crashed: ;called if mp3 ic appears to have crashed lds r16,crashed_cnt inc r16 sts crashed_cnt,r16 clr r17 sts mp3_current_cp,r16 rcall reset_mp3 rjmp mp3_fill_buffer_fill_loop_exit ;----------------------------------------------------------------------- ;process_buttons ;purpose: read in buttons and do auto keylocking ;Takes: ;Give: process_buttons: push r16 push r17 lds r16,status ;find if in keylocked mode sbrc r16,3 rjmp process_buttons_keyon clr r16 sbis button_port1-2,button_pin1 sbr r16,1 sbis button_port2-2,button_pin2 sbr r16,2 sbis button_port3-2,button_pin3 sbr r16,4 sbis button_port4-2,button_pin4 sbr r16,8 lds r17,keylock_tmr tst r16 ;see if anything pressed brne PC+3 ;if any key down, then dont inc, and clear inc r17 rjmp PC+2 clr r17 ;reset counter if key pressed cpi r17,KEYLOCK_TIMEOUT ;timeout value breq process_buttons_key_timeout sts buttons,r16 sts keylock_tmr,r17 process_buttons_exit: pop r17 pop r16 ret process_buttons_keyon: rjmp process_buttons_exit process_buttons_key_timeout: lds r16,status ;find if in keylocked mode sbr r16,8 sts status,r16 clr r16 sts keylock_cnt,r16 ;clear key counter ldi r16, 0x24 ; // LCD in power down rcall lcd_tx rjmp process_buttons_exit ;----------------------------------------------------------------------- ;read_file_list ;purpose: reads files and dirtories at current cluster location(location must point to start of dir) ;Takes: cluster ;Give: file_list read_file_list: push r0 push r1 push r3 push r4 push r16 push r17 push r18 push r19 push YH push YL push ZH push ZL cli ;stop interupts ; ldi YL, LOW(cluster_buff) ;REMOVE ; ldi YH, HIGH(cluster_buff) ; rcall mmc_read_cluster clr r3 ldi ZL,LOW(file_list) ldi ZH,HIGH(file_list) read_file_list_start_cluster: ldi YL, LOW(cluster_buff) ldi YH, HIGH(cluster_buff) rcall mmc_read_cluster ;read cluster into buffer ldi YL, LOW(cluster_buff) ldi YH, HIGH(cluster_buff) ldi r17,16 lds r16,spc mul r16,r17 mov r4,r0 ;records per cluster..fix so not hard coded(+1) inc r4 read_file_list_loop: ;test for end of cluster,..if so, cluster link to next dec r4 brne PC+2 rjmp read_file_list_next_cluster ld r17,Y ;read first byte cpi r17,0x00 ;end of directory listing marker breq read_file_list_finished cpi r17,0xe5 ;deleted file/dir, not valid, so skip it breq read_file_list_invalid_name ;if got to here then valid file/dir?...! adiw YL,11 ;get past name to attrib byte ld r17,Y mov r18,r17 andi r18,0b00001111 ;test if bottom 4 bits set..if so, long file name cpi r18,0b00001111 breq read_file_list_lfn ;should be valid file data now.. st Z+,r17 ;store attrib byte to begining sbiw YH:YL,11 ;get back to file name ldi r17,11 read_file_list_short_name_loop: ;read in short filename ld r16,Y+ st Z+,r16 dec r17 brne read_file_list_short_name_loop adiw YL,15 ;get to low cluster start ld r16,Y+ st Z+,r16 ld r16,Y st Z+,r16 sbiw YH:YL,7 ;get to high cluster start ld r16,Y+ st Z+,r16 ld r16,Y st Z+,r16 adiw YL,7 ;get to start of file size ldi r17,4 read_file_list_get_size: ;read in file size ld r16,Y+ st Z+,r16 dec r17 brne read_file_list_get_size ;---test code--- sbiw ZH:ZL,20 ;get start of Z ldi r17,128 list_out_loop: ld r16,Z+ ;rcall rs232_tx dec r17 brne list_out_loop ldi r16,13 ;rcall rs232_tx ldi r16,10 ;rcall rs232_tx inc r3 ; adiw YL,5 rjmp read_file_list_loop read_file_list_finished: sts number_of_files,r3 mov r16,r3 rcall rs232_tx pop ZL pop ZH pop YL pop YH pop r19 pop r18 pop r17 pop r16 pop r4 pop r3 pop r1 pop r0 ret read_file_list_invalid_name: ;start of file name was invalid or file has been deleted adiw YL,32 ;get to next dir entry rjmp read_file_list_loop read_file_list_lfn: sbiw YH:YL,11 ;back to start of record ld r17,Y+ ;indicates which lfn entry it is andi r17,0b00111111 cpi r17,8 ;due to limit of 128byte for my file record, max of 7 lfn entries (at 13 chars each) brlo PC+3 adiw YL,31 ;get to next dir entry rjmp read_file_list_loop ;jmp back to start ;should be short enough, so carry on... inc r17 ;leaave a 26 byte start ldi r18,13 ;13 chrs per lfn dir entry mul r17,r18 add ZL,r0 ;add offset adc ZH,r1 ldi r19,5 ;counter read_file_list_lfn_0_4: ;do 1 to 5 of lfn entry ld r16,Y+ ld r17,Y+ rcall unicode_to_ascii ;convert 16bit unicode to ascii st Z+,r16 dec r19 brne read_file_list_lfn_0_4 adiw YL,3 ldi r19,6 read_file_list_lfn_5_10: ;do 6 to 11 of lfn entry ld r16,Y+ ld r17,Y+ rcall unicode_to_ascii ;convert 16bit unicode to ascii st Z+,r16 dec r19 brne read_file_list_lfn_5_10 adiw YL,2 ld r16,Y+ ;read chr 12 ld r17,Y+ rcall unicode_to_ascii ;convert 16bit unicode to ascii st Z+,r16 ld r16,Y+ ;read chr 13. ld r17,Y+ ;should now be pointing to next entry rcall unicode_to_ascii ;convert 16bit unicode to ascii st Z+,r16 sub ZL,r0 ;get file list pointer back to begining sbc ZH,r1 sbiw ZH:ZL,13 rjmp read_file_list_loop read_file_list_next_cluster: ; ldi r16,'?' ; rcall rs232_tx rcall cluster_link ;get next cluster tst r16 ;see if was last cluster brne read_file_list_finished ;if r16 = FF then not valid rjmp read_file_list_start_cluster ;otherwise do another cluster ;----------------------------------------------------------------------- ;initalise_file_list ;purpose: resets all file info ;Takes: ;Give: .... initalise_file_list: push r16 push r17 push r18 ldi YL,LOW(file_list) ldi YH,HIGH(file_list) ldi r18,128 initalise_file_list_loop: ldi r17,128 initalise_file_list_loop1: clr r16 st Y+,r16 dec r17 brne initalise_file_list_loop1 dec r18 brne initalise_file_list_loop pop r18 pop r17 pop r16 ret ;----------------------------------------------------------------------- ;fat32_initalise ;purpose: reads filesystem and gains required start address ect ;Takes: ;Give: fills variables..also sets lba to root cluster fat32_initalise: ldi r16,0 ;set lba to 0 to read Master Boot Record sts lba+0,r16 ldi r16,0 sts lba+1,r16 ldi r16,0 sts lba+2,r16 rcall sector_to_buffer ;read Master Boot Record! ldi YH, HIGH(sec_buff) ldi YL, LOW(sec_buff) ;TODO, test for 0x55, 0xAA!! ldi r16,0xbe ;add 446 to Y to get past boot code ldi r17,0x01 add YL,r16 adc YH,r17 ldi r16,0 ;select which partion, hard coded to 0 atm ldi r17,16 mul r16,r17 add YL,r0 adc YH,r1 ;now pointing to start of partion info adiw YL,4 ;find type code ld r16,Y ;TODO, check if correct type# adiw YL,4 ;find start of lba start address ld r16,Y+ mov r22,r16 sts lba_begin+0,r16 ld r16,Y+ mov r23,r16 ;store 3 byte of l sts lba_begin+1,r16 ld r16,Y+ mov r24,r16 sts lba_begin+2,r16 rcall store_lba ;store start of lba_begin into lba rcall sector_to_buffer ;read Volume ID! ldi YH, HIGH(sec_buff) ldi YL, LOW(sec_buff) adiw YL,13 ;find sectors per cluster ld r16,Y+ ;store sts spc,r16 ld r14,Y+ ;find ammount of reserved space ld r15,Y clr r13 ;lba_begin alread in 22-25, now add reserved space to add r22,r14 ;find fat_begin_lba adc r23,r15 adc r24,r13 sts fat_begin+0,r22 sts fat_begin+1,r23 sts fat_begin+2,r24 adiw YL,21 ;find sectors per fat ld r10,Y+ ld r11,Y+ ld r12,Y+ ld r13,Y adiw YL,5 ;find root directory cluster start ld r16,Y+ sts root_dir_cluster+0,r16 ld r16,Y+ sts root_dir_cluster+1,r16 ld r16,Y sts root_dir_cluster+2,r16 lsl r10 ;now X2..(presuming 2 FATs) rol r11 rol r12 rol r13 add r22,r10 ;now add total FAT space onto fat begin to find cluster begin adc r23,r11 adc r24,r12 sts cluster_begin+0,r22 sts cluster_begin+1,r23 sts cluster_begin+2,r24 rcall store_lba ret ;----------------------------------------------------------------------- ;cluster_link ;purpose: reads fat to find next cluster in cluster chain, updates cluster ;Takes: cluster ;Give: next cluster, returns 0 in r16 if valid, FF in r16 if at end of chain cluster_link: push r17 push r3 push r4 push r5 push r22 push r23 push r24 push YH push YL lds r3,cluster+0 lds r4,cluster+1 lds r5,cluster+2 ldi r17,7 ;shit right 7 times cluster_link_find_sec: lsr r5 ror r4 ror r3 dec r17 brne cluster_link_find_sec lds r22,fat_begin+0 lds r23,fat_begin+1 lds r24,fat_begin+2 add r22,r3 ;should have correct sector to look at for fat adc r23,r4 adc r24,r5 rcall store_lba ;store in lba rcall sector_to_buffer ;read FAT sector ldi YH, HIGH(sec_buff) ldi YL, LOW(sec_buff) lds r22,cluster+0 ;find offset in fat sector andi r22,0b01111111 ldi r17,4 ;lenght of Fat32 cluster entries mul r17,r22 add YL,r0 ;add offset adc YH,r1 ld r22,Y+ ;read in next cluster number ld r23,Y+ ld r24,Y rcall store_cluster ;save back into cluster ser r16 ;set to invalid, then test cpi r22,0xFF ;compare to 0xffffff, if equal, then end of chain ldi r17,0xFF cpc r23,r17 cpc r24,r17 breq cluster_link_end clr r16 ;if valid, so clear r16 cluster_link_end: pop YL pop YH pop r24 pop r23 pop r22 pop r5 pop r4 pop r3 pop r17 ret ;----------------------------------------------------------------------- ;cluster_to_lba ;purpose: converts current cluster numner into lba (and stores in lba) ;Takes: cluster ;Give: cluster_to_lba: push r16 push r17 push r18 push r19 lds r16,cluster+0 ;have to subtract 2 from clsuter number(wierd fat32 thing) lds r17,cluster+1 lds r18,cluster+2 ldi r19,2 sub r16,r19 clr r19 sbc r17,r19 sbc r18,r19 lds r19,spc ;load with sectors per cluster(1,2,4,8,16...) cluster_to_lba_loop: ;this loops multiplys buy this number.. lsr r19 breq cluster_to_lba_exit lsl r16 rol r17 rol r18 rjmp cluster_to_lba_loop cluster_to_lba_exit: lds r22,cluster_begin+0 ;now add onto cluster begin offset lds r23,cluster_begin+1 lds r24,cluster_begin+2 add r22,r16 adc r23,r17 adc r24,r18 rcall store_lba pop r19 pop r18 pop r17 pop r16 ret ;----------------------------------------------------------------------- ;mmc_read_cluster ;purpose: reads a whole cluster into cluster buffer ;Takes: cluster ;Give: mmc_read_cluster: push r18 push r17 ; push r18 rcall cluster_to_lba ldi r17,2 lds r18,spc ;load sectors per cluster(counter) mmc_read_cluster_loop: rcall mmc_read_lba add YH,r17 ;add 512 to Y rcall inc_lba ;next sector dec r18 ;any more sectors?? brne mmc_read_cluster_loop pop r17 pop r18 ret ;----------------------------------------------------------------------- ;sector_to_buffer ;purpose: gets sector in lba to sector_buff ;Takes: lba value in lba(ram) ;Give: sector_to_buffer: push r3 push r4 push r5 push r22 push r23 push r24 push YL push YH lds r3,lba_old+0 lds r4,lba_old+1 lds r5,lba_old+2 lds r22,lba+0 lds r23,lba+1 lds r24,lba+2 cp r3,r22 ;see if sector is already in buffer cpc r4,r23 cpc r5,r24 breq sector_to_buffer_exit ldi YH, HIGH(sec_buff) ldi YL, LOW(sec_buff) rcall mmc_read_lba sts lba_old+0,r22 sts lba_old+1,r23 sts lba_old+2,r24 sector_to_buffer_exit: pop YH pop YL pop r24 pop r23 pop r22 pop r5 pop r4 pop r3 ret ;----------------------------------------------------------------------- ;mmc_read_lba ;purpose: read a lba into Y ;Takes: lba value in lba(ram) ;Give: mmc_read_lba: push r22 push r23 push r24 push r25 clr r22 lds r23,(lba+0) ;shift 9 bits to left (x512) lds r24,(lba+1) lds r25,(lba+2) lsl r23 rol r24 rol r25 rcall mmc_read_sector pop r25 pop r24 pop r23 pop r22 ret ;----------------------------------------------------------------------- ;mmc_read_sector ;purpose: read a data into memory ;Takes: r22-r25 as read address. will be put into mem at current Y! ;Give: mmc_read_sector: push r16 push r17 push YH push YL ldi r16,MMC_READ_SINGLE_BLOCK ;read sector command rcall mmc_send_command ldi r17,255 mmc_read_sector_wait: rcall spi_rx_byte ; read byte from MMC cpi r16,0xFE ; check tag breq mmc_read_sector_get_data dec r17 ; retry counter brne mmc_read_sector_wait mmc_read_sector_get_data: clr r17 mmc_read_sector_get_data_loop: rcall spi_rx_byte ; read byte from MMC st Y+,r16 ;rcall rs232_tx rcall spi_rx_byte ; read byte from MMC st Y+,r16 ;rcall rs232_tx dec r17 brne mmc_read_sector_get_data_loop rcall spi_rx_byte ;crc1 rcall spi_rx_byte ;crc2 pop YL pop YH pop r17 pop r16 rjmp mmc_finish ;NOTE< LOOSES R16 ;----------------------------------------------------------------------- ;mmc_send_command ;purpose: send command to spi ;Takes: r16 as command, r22-r25 as params ;Give: mmc_send_command_0: clr r22 clr r23 clr r24 clr r25 mmc_send_command: push r16 push r17 cbi spi_cs_port,mmc_cs ori r16,0x40 rcall spi_tx_byte mov r16,r25 rcall spi_tx_byte mov r16,r24 rcall spi_tx_byte mov r16,r23 rcall spi_tx_byte mov r16,r22 rcall spi_tx_byte ldi r16,0x95 rcall spi_tx_byte rcall spi_rx_byte ; one empty cycle rcall spi_rx_byte ; read card response ;rcall rs232_tx tst r16 brpl mmc_send_command_ok ldi r17, MMC_COMMAND_ERROR ;error in command rcall error mmc_send_command_ok: mmc_send_command_wbusy: rcall spi_rx_byte ; check for busy tst r16 breq mmc_send_command_wbusy ; card is busy pop r17 pop r16 ret ;----------------------------------------------------------------------- ;mmc_reset ;purpose: reset mmc ;Takes: ;Give: mmc_reset: push r16 push r17 sbi spi_cs_port,mmc_cs ldi r17,20 ;send a load of clocks mmc_reset_loop: rcall spi_rx_byte dec r17 brne mmc_reset_loop ldi r16,MMC_GO_IDLE_STATE rcall mmc_send_command_0 ; set card in idle state rcall mmc_finish clr r17 mmc_reset_loop1: rcall delay_1ms ldi r16,MMC_SEND_OP_COND rcall mmc_send_command_0 ; initialize operating conditions rcall mmc_finish sbrs r16,0 rjmp mmc_reset_end dec r17 brne mmc_reset_loop1 ldi r17,MMC_RESET_ERROR ;if got here, then error resetting mmc rcall error mmc_reset_end: pop r17 pop r16 ret ;----------------------------------------------------------------------- ;lcd_invert_line ;purpose:inverts a line on the lcd ;Takes: posy is line to invert ;Give: lcd_invert_line: push r17 push r18 push YH push YL ldi YH,high(vram) ldi YL,low(vram) add YH,posy clr r17 lcd_invert_line_loop: ld r18,Y com r18 ;invert st Y+,r18 dec r17 brne lcd_invert_line_loop pop YL pop YH pop R18 pop r17 ret ;----------------------------------------------------------------------- ;lcd_chr_ram ;purpose:puts char at posx, posy in r16 ;Takes: ;Give: lcd_chr_vram: push ZL ;retain Z value push ZH push YL push YH push r17 push r1 push r0 ;push posx ;push posy ldi ZH,high(font<<1) ;set to start of font table offset ldi ZL, low(font<<1) subi r16,32 ;;subtract 32 because missing first 32 ascii ldi r17,6 ;width of characters mul r17,r16 ;get pos chr offset in font table, into r1:r0 add ZL,r0 ;add chr offset adc ZH,r1 ldi YH,high(vram) ldi YL,low(vram) add YH,posy clr r16 add YL,posx adc YH,r16 ldi r16,6 ;counter add posx,r16 lcd_chr_loop: lpm r17,Z+ ;load byte to write st Y+,r17 dec r16 ;do 6 times brne lcd_chr_loop pop r0 pop r1 pop r17 pop YH pop YL pop ZH pop ZL ret ;----------------------------------------------------------------------- ;clear_vram ;purpose: set Vram to 0 ;Takes: ;Give: ;---------------------- clear_vram: push r16 push r18 push XL push XH ldi XL,LOW(vram) ;set start of clear (indierct mem ad) ldi XH,HIGH(vram) ldi r18,0 ldi r16,0 clear_vram_loop: st X+,r16 st X+,r16 st X+,r16 st X+,r16 st X+,r16 st X+,r16 ;st X+,r16 ;st X+,r16 dec r18 ;test for end of vram brne clear_vram_loop pop XH pop XL pop r18 pop r16 ret ;----------------------------------------------------------------------- ;clear_vram1 ;purpose: set Vram to 0 ;Takes: ;Give: ;---------------------- clear_vram1: push r16 push r18 push XL push XH ldi XL,LOW(vram1) ;set start of clear (indierct mem ad) ldi XH,HIGH(vram1) ldi YL,LOW(vram2) ;set start of clear (indierct mem ad) ldi YH,HIGH(vram2) ldi r18,0 ldi r16,0 clear_vram1_loop: st X+,r16 st X+,r16 st Y+,r16 st Y+,r16 ;st X+,r16 ;st X+,r16 dec r18 ;test for end of vram brne clear_vram1_loop pop XH pop XL pop r18 pop r16 ret ;--- lcd_refresh1: push r18 push r19 push XL push XH push YL push YH ldi r16, 0b10000000 ;set to (0,0) rcall lcd_tx ldi r16, 0b01000000 ;set to (0,0) rcall lcd_tx cbi lcd_cs_port,lcd_cs ;select chip nop sbi lcd_dc_port,lcd_dc ;select data ldi r19,0 lcd_refresh1_loop: ldi r18,84 lcd_refresh1_loop1: ld r16,X+ rcall spi_tx_byte dec r18 ;test for end of vram brne lcd_refresh1_loop1 inc r19 cpi r19,6 brne lcd_refresh1_loop ldi r16, 0b10000000 ;set to (0,0) rcall lcd_tx ldi r16, 0b01000000 ;set to (0,0) rcall lcd_tx pop YH pop YL pop XH pop XL pop r19 pop r18 ret ;----------------------------------------------------------------------- ;lcd_refresh ;purpose: copy Vram to LCD ;Takes: ;Give: lcd_refresh: push r18 push r19 push XL push XH push YL push YH ldi r16, 0b10000000 ;set to (0,0) rcall lcd_tx ldi r16, 0b01000000 ;set to (0,0) rcall lcd_tx cbi lcd_cs_port,lcd_cs ;select chip nop sbi lcd_dc_port,lcd_dc ;select data ldi YL,LOW(line_offset) ldi YH, HIGH(line_offset) ldi r19,0 lcd_refresh_loop: ldi XL,LOW(vram) ;set start of clear (indierct mem ad) ldi XH,HIGH(vram) lds r16,vram_offset add XL,r16 ;add vram offset, also current line offset adc XH,r19 ld r16,Y+ clr r18 add XL,r16 adc XH,r18 ldi r18,84 lcd_refresh_loop1: ld r16,X+ rcall spi_tx_byte dec r18 ;test for end of vram brne lcd_refresh_loop1 inc r19 cpi r19,6 brne lcd_refresh_loop ldi r16, 0b10000000 ;set to (0,0) rcall lcd_tx ldi r16, 0b01000000 ;set to (0,0) rcall lcd_tx pop YH pop YL pop XH pop XL pop r19 pop r18 ret ;----------------------------------------------------------------------- ;mmc_finish ;purpose: deactivates MMC ;Takes: ;Give: mmc_finish: sbi spi_cs_port,mmc_cs ; release chip select rcall spi_rx_byte ; one empty SPI cycle ret ;--------- rs232_tx: sbis UCSR1A,UDRE1 rjmp rs232_tx push r17 mov r17,r16 tst r17 brne PC+2 ldi r17,'|' out UDR1,r17 pop r17 ret ;--------- rs232_rx_wait: sbis UCSR1A,RXC1 rjmp rs232_rx_wait in r16,UDR1 sbi UCSR1A,RXC1 ret ;------- mp3_tx: sbi mp3_bs_port,mp3_bs ; start BSYNC nop nop nop nop out SPDR, r16 ; send over SPI nop nop nop nop cbi mp3_bs_port,mp3_bs ; stop BSYNC mp3_tx1: sbis SPSR,SPIF ; while flag is clear rjmp mp3_tx1 ; wait for SPI ret ;--- lcd_tx: ;*****send lcd command in r16 cbi lcd_cs_port,lcd_cs ;select chip nop cbi lcd_dc_port,lcd_dc ;select command nop rcall spi_tx_byte nop sbi lcd_cs_port,lcd_cs ;unselect nop ret ;----------------------------------------------------------------------- ;spi_rx_byte/spi_rx_byte ;purpose: get or recive spi byte ;Takes: r16 ;Give: r24 spi_rx_byte: ser r16 spi_tx_byte: out SPDR, r16 ; send over SPI spi_tx_byte_loop: sbis SPSR,SPIF ; while flag is clear rjmp spi_tx_byte_loop ; wait for SPI in r16, SPDR ; and get input data ret ;----------------------------------------------------------------------- ;error ;purpose: called when an error occours, ;Takes: error number in r16 ;Give: error: push r16 ldi r16,'E' rcall rs232_tx ldi r16,'r' rcall rs232_tx ldi r16,'r' rcall rs232_tx ldi r16,'o' rcall rs232_tx ldi r16,'r' rcall rs232_tx mov r16,r17 rcall rs232_tx pop r16 ret ;---- Store_lba: sts lba+0,r22 sts lba+1,r23 sts lba+2,r24 ret Store_cluster: sts cluster+0,r22 sts cluster+1,r23 sts cluster+2,r24 ret ;---- Inc_LBA: push r22 push r23 push r24 push r16 ; lds r22,lba+0 lds r23,lba+1 lds r24,lba+2 clr r16 sec adc r22,r16 adc r23,r16 adc r24,r16 sts lba+0,r22 sts lba+1,r23 sts lba+2,r24 pop r16 pop r24 pop r23 pop r22 ret ;------------- unicode_to_ascii:;make proper.. ;mov r16,r16 ret ;--- ;--delay 1ms-- delay_1ms: push r16 push r17 ldi r16,100 delay_1ms1: ldi r17,100 delay_1ms1_loop: dec r17 nop nop nop brne delay_1ms1_loop dec r16 brne delay_1ms1 pop r17 pop r16 ret ;----------------------------------------------------------------------- ;reset_mp3 ;purpose: mp3 ic must be reset every new file ;Takes: ;Give: reset_mp3: push r16 cbi spi_cs_port,mp3_cs ;rst ldi r16,0x02 rcall spi_tx_byte ldi r16,0x00 rcall spi_tx_byte ldi r16,0x00 rcall spi_tx_byte ldi r16,0x04 rcall spi_tx_byte sbi spi_cs_port,mp3_cs rcall delay_1ms ;TODO add code to check if dreq is up pop r16 ret initialise_hardware: rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms sbi PORTD,4 rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms ;----initialise LCD-------- lcd_initialise: ldi r16, 0x21 ; // LCD Extended Commands. rcall lcd_tx ldi r16, 0b10111110 ; // Set LCD Vop (Contrast).c0 rcall lcd_tx ldi r16, 0x06 ; // Set Temp coefficent. rcall lcd_tx ldi r16, 0x13 ; // LCD bias mode 1:48. rcall lcd_tx ;ldi r16, 0xcb ; // LCD CONTRAST cb,4f ;rcall lcd_tx ldi r16, 0x20 ; // LCD Standard Commands, Horizontal addressing mode. rcall lcd_tx ldi r16, 0x0c ; // LCD in normal mode. rcall lcd_tx rcall delay_1ms cbi spi_cs_port,mp3_cs ;rst ldi r16,0x02 rcall spi_tx_byte ldi r16,0x00 rcall spi_tx_byte ldi r16,0x00 rcall spi_tx_byte ldi r16,0x04 rcall spi_tx_byte sbi spi_cs_port,mp3_cs rcall delay_1ms cbi spi_cs_port,mp3_cs ;double clock ldi r16,0x02 rcall spi_tx_byte ldi r16,0x03 rcall spi_tx_byte ldi r16,0x98 rcall spi_tx_byte ldi r16,0x00 rcall spi_tx_byte sbi spi_cs_port,mp3_cs rcall delay_1ms cbi spi_cs_port,mp3_cs ;volume ldi r16,0x02 rcall spi_tx_byte ldi r16,11 rcall spi_tx_byte ldi r16,0x20 rcall spi_tx_byte ldi r16,0x20 rcall spi_tx_byte sbi spi_cs_port,mp3_cs rcall delay_1ms cbi spi_cs_port,mp3_cs ;enhance ldi r16,0x02 rcall spi_tx_byte ldi r16,00 rcall spi_tx_byte ldi r16,0x00 rcall spi_tx_byte ldi r16,0b10000000 rcall spi_tx_byte sbi spi_cs_port,mp3_cs rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms rcall delay_1ms clr r16 rcall mp3_tx rcall mp3_tx rcall mp3_tx rcall mp3_tx ret ;--------------------------------- ;--------COPIED CODE BELOW-------- ;--------------------------------- ;-- ; Bin2ToBcd5 ; ========== ; converts a 16-bit-binary to a 5-digit-BCD ; In: 16-bit-binary in rBin1H:L, Z points to first digit ; where the result goes to ; Out: 5-digit-BCD, Z points to first BCD-digit ; Used registers: rBin1H:L (unchanged), rBin2H:L (changed), ; rmp ; Called subroutines: Bin2ToDigit ; .def rBin1L =r16 .def rBin1H =r17 .def rBin2L =r18 .def rBin2H =r19 .def rmp = r20 Bin2ToBcd5: push ZL push ZH push rBin1H ; Save number push rBin1L push rBin2H push rBin2L push rmp ldi ZH,HIGH(temp128) ldi ZL,LOW(temp128) ldi rmp,HIGH(10000) ; Start with tenthousands mov rBin2H,rmp ldi rmp,LOW(10000) mov rBin2L,rmp rcall Bin2ToDigit ; Calculate digit ldi rmp,HIGH(1000) ; Next with thousands mov rBin2H,rmp ldi rmp,LOW(1000) mov rBin2L,rmp rcall Bin2ToDigit ; Calculate digit ldi rmp,HIGH(100) ; Next with hundreds mov rBin2H,rmp ldi rmp,LOW(100) mov rBin2L,rmp rcall Bin2ToDigit ; Calculate digit ldi rmp,HIGH(10) ; Next with tens mov rBin2H,rmp ldi rmp,LOW(10) mov rBin2L,rmp rcall Bin2ToDigit ; Calculate digit st z,rBin1L ; Remainder are ones sbiw ZL,4 ; Put pointer to first BCD pop rmp pop rBin2L pop rBin2H pop rBin1L ; Restore original binary pop rBin1H pop ZH pop ZL ret ; and return ; ; Bin2ToDigit ; =========== ; converts one decimal digit by continued subraction of a ; binary coded decimal ; Used by: Bin2ToBcd5, Bin2ToAsc5, Bin2ToAsc ; In: 16-bit-binary in rBin1H:L, binary coded decimal in ; rBin2H:L, Z points to current BCD digit ; Out: Result in Z, Z incremented ; Used registers: rBin1H:L (holds remainder of the binary), ; rBin2H:L (unchanged), rmp ; Called subroutines: - ; Bin2ToDigit: clr rmp ; digit count is zero Bin2ToDigita: cp rBin1H,rBin2H ; Number bigger than decimal? brcs Bin2ToDigitc ; MSB smaller than decimal brne Bin2ToDigitb ; MSB bigger than decimal cp rBin1L,rBin2L ; LSB bigger or equal decimal brcs Bin2ToDigitc ; LSB smaller than decimal Bin2ToDigitb: sub rBin1L,rBin2L ; Subtract LSB decimal sbc rBin1H,rBin2H ; Subtract MSB decimal inc rmp ; Increment digit count rjmp Bin2ToDigita ; Next loop Bin2ToDigitc: st z+,rmp ; Save digit and increment ret ; done ;--------------------------------- ; ; Divide r16h:r17l by r18 .DEF rd1l = r16 ; LSB 16-bit-number to be divided .DEF rd1h = r17 ; MSB 16-bit-number to be divided .DEF rd1u = R2 ; interim register .DEF rd2 = r18 ; 8-bit-number to divide with .DEF rel = R4 ; LSB result .DEF reh = R5 ; MSB result .DEF rmp = R19; multipurpose register for loading ; div8: ;push r16 ;push r17 push r18 push r19 push r2 push r4 push r5 clr rd1u ; clear interim register clr reh ; clear result (the result registers clr rel ; are also used to count to 16 for the inc rel ; division steps, is set to 1 at start) ; ; Here the division loop starts ; div8a: clc ; clear carry-bit rol rd1l ; rotate the next-upper bit of the number rol rd1h ; to the interim register (multiply by 2) rol rd1u brcs div8b ; a one has rolled left, so subtract cp rd1u,rd2 ; Division result 1 or 0? brcs div8c ; jump over subtraction, if smaller div8b: sub rd1u,rd2; subtract number to divide with sec ; set carry-bit, result is a 1 rjmp div8d ; jump to shift of the result bit div8c: clc ; clear carry-bit, resulting bit is a 0 div8d: rol rel ; rotate carry-bit into result registers rol reh brcc div8a ; as long as zero rotate out of the result ; registers: go on with the division loop mov r16,r4 mov r17,r5 pop r5 pop r4 pop r2 pop r19 pop r18 ; pop r17 ; pop r16 ret font: .DB 0x00, 0x00, 0x00, 0x00, 0x00 ;sp .DB 0x00, 0x00, 0x2f, 0x00, 0x00 ;! .DB 0x00, 0x07, 0x00, 0x07, 0x00 ;" .DB 0x14, 0x7f, 0x14, 0x7f, 0x14 ;# .DB 0x24, 0x2a, 0x7f, 0x2a, 0x12 ;$ .DB 0xc4, 0xc8, 0x10, 0x26, 0x46 ;% .DB 0x36, 0x49, 0x55, 0x22, 0x50 ;& .DB 0x00, 0x05, 0x03, 0x00, 0x00 ;' .DB 0x00, 0x1c, 0x22, 0x41, 0x00 ;( .DB 0x00, 0x41, 0x22, 0x1c, 0x00 ;) .DB 0x14, 0x08, 0x3E, 0x08, 0x14 ;* .DB 0x08, 0x08, 0x3E, 0x08, 0x08 ;+ .DB 0x00, 0x00, 0x50, 0x30, 0x00 ;, .DB 0x10, 0x10, 0x10, 0x10, 0x10 ;- .DB 0x00, 0x60, 0x60, 0x00, 0x00 ;. .DB 0x20, 0x10, 0x08, 0x04, 0x02 ;/ .DB 0x3E, 0x51, 0x49, 0x45, 0x3E ;0 .DB 0x00, 0x42, 0x7F, 0x40, 0x00 ;1 .DB 0x42, 0x61, 0x51, 0x49, 0x46 ;2 .DB 0x21, 0x41, 0x45, 0x4B, 0x31 ;3 .DB 0x18, 0x14, 0x12, 0x7F, 0x10 ;4 .DB 0x27, 0x45, 0x45, 0x45, 0x39 ;5 .DB 0x3C, 0x4A, 0x49, 0x49, 0x30 ;6 .DB 0x01, 0x71, 0x09, 0x05, 0x03 ;7 .DB 0x36, 0x49, 0x49, 0x49, 0x36 ;8 .DB 0x06, 0x49, 0x49, 0x29, 0x1E ;9 .DB 0x00, 0x36, 0x36, 0x00, 0x00 ;: .DB 0x00, 0x56, 0x36, 0x00, 0x00 ;; .DB 0x08, 0x14, 0x22, 0x41, 0x00 ;< .DB 0x14, 0x14, 0x14, 0x14, 0x14 ;= .DB 0x00, 0x41, 0x22, 0x14, 0x08 ;> .DB 0x02, 0x01, 0x51, 0x09, 0x06 ;? .DB 0x32, 0x49, 0x59, 0x51, 0x3E ;@ .DB 0x7E, 0x11, 0x11, 0x11, 0x7E ;A .DB 0x7F, 0x49, 0x49, 0x49, 0x36 ;B .DB 0x3E, 0x41, 0x41, 0x41, 0x22 ;C .DB 0x7F, 0x41, 0x41, 0x22, 0x1C ;D .DB 0x7F, 0x49, 0x49, 0x49, 0x41 ;E .DB 0x7F, 0x09, 0x09, 0x09, 0x01 ;F .DB 0x3E, 0x41, 0x49, 0x49, 0x7A ;G .DB 0x7F, 0x08, 0x08, 0x08, 0x7F ;H .DB 0x00, 0x41, 0x7F, 0x41, 0x00 ;I .DB 0x20, 0x40, 0x41, 0x3F, 0x01 ;J .DB 0x7F, 0x08, 0x14, 0x22, 0x41 ;K .DB 0x7F, 0x40, 0x40, 0x40, 0x40 ;L .DB 0x7F, 0x02, 0x0C, 0x02, 0x7F ;M .DB 0x7F, 0x04, 0x08, 0x10, 0x7F ;N .DB 0x3E, 0x41, 0x41, 0x41, 0x3E ;O .DB 0x7F, 0x09, 0x09, 0x09, 0x06 ;P .DB 0x3E, 0x41, 0x51, 0x21, 0x5E ;Q .DB 0x7F, 0x09, 0x19, 0x29, 0x46 ;R .DB 0x46, 0x49, 0x49, 0x49, 0x31 ;S .DB 0x01, 0x01, 0x7F, 0x01, 0x01 ;T .DB 0x3F, 0x40, 0x40, 0x40, 0x3F ;U .DB 0x1F, 0x20, 0x40, 0x20, 0x1F ;V .DB 0x3F, 0x40, 0x38, 0x40, 0x3F ;W .DB 0x63, 0x14, 0x08, 0x14, 0x63 ;X .DB 0x07, 0x08, 0x70, 0x08, 0x07 ;Y .DB 0x61, 0x51, 0x49, 0x45, 0x43 ;Z .DB 0x00, 0x7F, 0x41, 0x41, 0x00 ;[ .DB 0x55, 0x2A, 0x55, 0x2A, 0x55 ;55 .DB 0x00, 0x41, 0x41, 0x7F, 0x00 ;] .DB 0x04, 0x02, 0x01, 0x02, 0x04 ;^ .DB 0x40, 0x40, 0x40, 0x40, 0x40 ;_ .DB 0x00, 0x01, 0x02, 0x04, 0x00 ;' .DB 0x20, 0x54, 0x54, 0x54, 0x78 ;a .DB 0x7F, 0x48, 0x44, 0x44, 0x38 ;b .DB 0x38, 0x44, 0x44, 0x44, 0x20 ;c .DB 0x38, 0x44, 0x44, 0x48, 0x7F ;d .DB 0x38, 0x54, 0x54, 0x54, 0x18 ;e .DB 0x08, 0x7E, 0x09, 0x01, 0x02 ;f .DB 0x0C, 0x52, 0x52, 0x52, 0x3E ;g .DB 0x7F, 0x08, 0x04, 0x04, 0x78 ;h .DB 0x00, 0x44, 0x7D, 0x40, 0x00 ;i .DB 0x20, 0x40, 0x44, 0x3D, 0x00 ;j .DB 0x7F, 0x10, 0x28, 0x44, 0x00 ;k .DB 0x00, 0x41, 0x7F, 0x40, 0x00 ;l .DB 0x7C, 0x04, 0x18, 0x04, 0x78 ;m .DB 0x7C, 0x08, 0x04, 0x04, 0x78 ;n .DB 0x38, 0x44, 0x44, 0x44, 0x38 ;o .DB 0x7C, 0x14, 0x14, 0x14, 0x08 ;p .DB 0x08, 0x14, 0x14, 0x18, 0x7C ;q .DB 0x7C, 0x08, 0x04, 0x04, 0x08 ;r .DB 0x48, 0x54, 0x54, 0x54, 0x20 ;s .DB 0x04, 0x3F, 0x44, 0x40, 0x20 ;t .DB 0x3C, 0x40, 0x40, 0x20, 0x7C ;u .DB 0x1C, 0x20, 0x40, 0x20, 0x1C ;v .DB 0x3C, 0x40, 0x30, 0x40, 0x3C ;w .DB 0x44, 0x28, 0x10, 0x28, 0x44 ;x .DB 0x0C, 0x50, 0x50, 0x50, 0x3C ;y .DB 0x44, 0x64, 0x54, 0x4C, 0x44 ;z