;***************************************************************** ; ; File Name : 'mp3.asm' ; Title : MP3 Player ; Date : 2001.11.25 ; Version : 0.01 ; Author : Baris Inan ; Target MCU : ATmega163 ; ;***************************************************************** ;.device ATMEGA163 ; Prohibits use of non-implemented instructions; .nolist .include "m163def.inc" .list
;.equ CPU_CLOCK =7872800 .equ BAUD_RATE =38400 .equ CPU_CLOCK =8000000 .equ UART_BAUD_DIV =((CPU_CLOCK+8*BAUD_RATE)/(16*BAUD_RATE))-1
; Number of MP3 files stored in SRAM[$60] .equ NUM_TRACKS =$0060
.def SECTOR =r2 .def CYL_LO =r3 .def CYL_HI =r4 .def HEAD =r5
.def TRACK =r6 ; Keeps the current track no .def CLUS_LO =r7 ; Current cluster low .def CLUS_HI =r8 ; Current cluster hi
.def UARTREG =r10 ; Uart receive register
.def RESETREG =r11 ; .def FLAGREG =r12 ;
.def TEMP0 =r16 ; temporary register 0 .def TEMP1 =r17 ; temporary register 1 .def TEMP2 =r18 ; temporary register 2 .def RETURN0 =r19 ; function return 0 .def RETURN1 =r20 ; function return 1 .def ARG0 =r21 ; function argument 0 .def ARG1 =r22 ; function argument 1 .def ARG2 =r23 ; function argument 2 .def TEMP3 =r24
; and as a reminder ; m163def.inc contains ;.def XL =r26 ;.def XH =r27 ;.def YL =r28 ;.def YH =r29 ;.def ZL =r30 ;.def ZH =r31
; code segment .cseg .org 0x000 jmp reset ; $000 Reset Handler jmp Int0Isr ; $002 IRQ0 Handler jmp reset ; $004 IRQ1 Handler jmp reset ; $006 Timer2 Compare Handler jmp Timer2Isr ; $008 Timer2 Overflow Handler jmp reset ; $00a Timer1 Capture Handler jmp reset ; $00c Timer1 CompareA Handler jmp reset ; $00e Timer1 CompareB Handler jmp reset ; $010 Timer1 Overflow Handler jmp reset ; $012 Timer0 Overflow Handler jmp SPICompIsr ; $014 SPI Transfer Complete Handler jmp reset ; $016 UART RX Complete Handler jmp reset ; $018 UDR Empty Handler jmp reset ; $01a UART TX Complete Handler jmp reset ; $01c ADC Conversion Complete Interrupt Handler jmp reset ; $01e EEPROM Ready Handler jmp reset ; $020 Analog Comparator Handler jmp reset ; $022 2-wire Serial Interface Interrupt Handler
;File size 2020 (07E4 hex)words .include "p02_0609.inc"
;File size 2011 words (07DB hex) ;.include "p02_0609_mine.inc"
reset: ; initialize stack pointer ldi TEMP0, low(RAMEND) out SPL, TEMP0 ldi TEMP0, high(RAMEND) out SPH, TEMP0
clr TEMP0 out UBRRHI, TEMP0 ldi TEMP0, UART_BAUD_DIV ; set baud rate out UBRR, TEMP0 sbi UCSRB, TXEN ; enable serial transmitter sbi UCSRB, RXEN ; enable serial receiver ; sbi UCSRB, RXCIE ; enable receiver interrupts
; Timer2 ldi TEMP0, $07 ; Prescale 1024 out TCCR2, TEMP0 in TEMP0, TIMSK sbr TEMP0, 1<<TOIE2 ; Enable timer2 interrupt out TIMSK, TEMP0
;I2C bit rate register ; TWBR = 80 gives around 45Kbps for 8Mhz ldi TEMP0, $C0 ; 80 out TWBR, TEMP0
;SPI configuration ;Fclk/16=500Kbps ;Fclk/2 and Fclk/4 doesn't work ;Fclk/8 has some skips ;Fclk/16 can support 128Kbps well and some skips for 192Kbps ldi TEMP0, 0b11010101 ; out SPCR, TEMP0 cbi SPSR, 0 ; SPI2x=0
; INT0 configuration in TEMP0, GIMSK sbr TEMP0, 1<<INT0 ; Enable external interrupt INT0 out GIMSK, TEMP0 ldi TEMP0, $03 ; 0000-0011 out MCUCR, TEMP0 ; Rising edge on INT0
ldi ARG0, $41 ; A rcall putchar ; Debug
ldi TEMP0, $FF ; port C all outputs out DDRC, TEMP0 ldi TEMP0, $7F ; 0111-1111 out PORTC, TEMP0 ; CS1=CS0=1 -> CF in standby, PC7=0 -> display off
ldi TEMP0, $B0 ; 0b10110000: MISO is input, B0-B3 input out DDRB, TEMP0 ldi TEMP0, $30 ; 0b00110000: SCK=0 MOSI=1 SS=1 out PORTB, TEMP0
ldi TEMP0, $E0 ; 7-6-5 output, 2 input 111-00-0-00 out DDRD, TEMP0 ; Port D clr TEMP0 out PORTD, TEMP0 ; All zeros
; Wait for CF reset rcall chbsy
ldi ARG0, $61 ; a rcall putchar ; Debug
; Set feature to 8-bit mode ldi ARG1, $FF ; Write operation ldi ARG0, $04 ; 000-001-00: A2-A0 = 001 feature reg. ldi ARG2, $01 ; Enable 8-bit data transfers rcall memrw
ldi ARG0, $42 ; B rcall putchar ; Debug
; Send Set Feature Command ldi ARG1, $FF ; Write operation ldi ARG0, $1C ; 000-111-00: A2-A0 = 111 command reg. ldi ARG2, $EF ; Set Features rcall memrw
ldi ARG0, $62 ; b rcall putchar ; Debug
;Wait for CF rcall chbsy ; Check if CF busy
rcall is_lcd_busy ; Check if LCD is busy
; Timer 1 for LCD's PWM input ldi TEMP0, $81 ; 1000-0001: COM1A1=PWM10=1 out TCCR1A, TEMP0 ldi TEMP0, $01 ; 0000-0001: CS10=1 (Prescale=1) out TCCR1B, TEMP0 clr TEMP0 out OCR1AH, TEMP0 ldi TEMP0, 21 ; 21/256 duty cycle for contrast out OCR1AL, TEMP0
; Initialize LCD ldi ARG0, $38 ; 8-bit, 2 lines, 5x7 rcall lcd_command_write ldi ARG0, $0C ; Display ON rcall lcd_command_write ldi ARG0, $06 ; Increment, no shift
ldi ARG0, $43 ; C rcall putchar ; Debug
;Wait for "C" ;mp3_pause1: ; rcall getchar ; cpi RETURN0, $43 ; "C" ; brne mp3_pause1
; Read IDENT register in MP3 decoder ldi ARG1, $01 rcall I2C_read_byte
; Write DATA_REQ_ENABLE register ldi ARG1, $18 ldi ARG2, $04 rcall I2C_write_byte
; Read DATA_REQ_ENABLE register ldi ARG1, $18 rcall I2C_read_byte
;mp3_pause4: ; rcall getchar ; cpi RETURN0, $43 ; "C" ; brne mp3_pause4
; Send the MP3 decoder configuration file clr XH ; Counter for .inc file clr XL ldi ZH, high(config*2) ldi ZL, low (config*2) mp3_wr: lpm ; read least signif. byte mov ARG1, r0 ; Register address adiw ZL, 1 lpm ; read most signif. byte mov ARG2, r0 ; Data adiw ZL, 1 ; increment
rcall I2C_write_byte
;30 ms delay after SOFT RESET command (addr=16) cpi ARG1, 16 brne mp3_wr_skip clr TEMP0 out TCNT2, TEMP0 ; Start timer clr TEMP3 sei timer2: cpi TEMP3, $00 nop nop breq timer2 cli
in TEMP0, TIMSK cbr TEMP0, 1<<TOIE2 ; Disable timer2 interrupt out TIMSK, TEMP0
mp3_wr_skip:
adiw XL, 1 cpi XL, $E4 ;Compare with file size brne mp3_wr cpi XH, $07 ; brne mp3_wr
;Send RUN command to decoder ldi ARG1, 114 ; RUN ldi ARG2, 1 ; RUN rcall I2C_write_byte
;Send Play Command to decoder ldi ARG1, 19 ; PLAY ldi ARG2, 1 ; PLAY rcall I2C_write_byte
;Read PLAY register ; rcall I2C_read_byte
;Read SCKL_POL register ; ldi ARG1, $0D ; rcall I2C_read_byte
;Wait for "C" ;mp3_pause: ; rcall getchar ; cpi RETURN0, $43 ; "C" ; brne mp3_pause
rcall read_root_dir
rcall LF_CR
; Print Number of Files clr ARG2 ; Don't send to LCD lds ARG1, NUM_TRACKS mov TEMP0, ARG1 rcall print_hex ldi ARG0, $2E ; . rcall putchar ldi ARG0, $2E ; . rcall putchar ;Print The first cluster no's for files ldi YH, $00 ldi YL, $62 files_loop: clr ARG2 ; Don't send to LCD ld ARG1, Y+ ; Low byte rcall print_hex ld ARG1, Y+ ; High byte rcall print_hex ldi ARG0, $2E ; . rcall putchar ldi ARG0, $2E ; . rcall putchar dec TEMP0 cpi TEMP0, 0 brne files_loop ;Print the last files last cluster no + 1 clr ARG2 ; Don't send to LCD ld ARG1, Y+ ; Low byte rcall print_hex ld ARG1, Y+ ; High byte rcall print_hex
; Print the sector addresses of tracks rcall LF_CR clr TRACK inc TRACK ; Start at Track #1 lds TEMP0, NUM_TRACKS inc TEMP0 print_tracks: rcall compute_addr clr ARG2 ; Don't send to LCD mov ARG1, CYL_HI rcall print_hex ldi ARG0, $2E ; . rcall putchar mov ARG1, CYL_LO rcall print_hex ldi ARG0, $2E ; . rcall putchar mov ARG1, HEAD rcall print_hex ldi ARG0, $2E ; . rcall putchar mov ARG1, SECTOR rcall print_hex inc TRACK rcall LF_CR cp TRACK, TEMP0 brne print_tracks
; Initialize to TRACK #1 clr TRACK inc TRACK rcall compute_addr rcall read_tag
; Wait here for PLAY input play_loop2: sbic PINB, 3 ; Skip rjmp if button is pressed rjmp play_loop2
ldi YH, $02 clr YL clr RESETREG
sei
main: ;Set Cylinder Low Reg. ldi ARG1, $FF ; Write operation ldi ARG0, $10 ; 000-100-00: cyl low reg. mov ARG2, CYL_LO ; cyl_lo = CYL_LO rcall memrw
;Set Cylinder Hi Reg. ldi ARG1, $FF ; Write operation ldi ARG0, $14 ; 000-101-00: cyl hi reg. mov ARG2, CYL_HI ; cyl_hi = 00 rcall memrw
;Set Head No. ldi ARG1, $FF ; Write operation ldi ARG0, $18 ; 000-110-00: select card/head reg. mov ARG2, HEAD ; 1010-0000: head no = 0 rcall memrw
;Set Sector Number Register ldi ARG1, $FF ; Write operation ldi ARG0, $0C ; 000-011-00: sec num reg. mov ARG2, SECTOR ; sec_num = SECTOR rcall memrw
;Set 01H in sector count register (read one sector) ldi ARG1, $FF ; Write operation ldi ARG0, $08 ; 000-010-00: sec cnt. ldi ARG2, $01 ; 1 sector rcall memrw
; Set 20H in Command register ldi ARG1, $FF ; Write operation ldi ARG0, $1C ; 000-111-00: A2-A0 = 111 command reg. ldi ARG2, $20 ; Read Sector(s) rcall memrw
rcall chbsy ; Check if busy
;cbi PORTB, 0 ; LED0 on nop ; instead of upper instr.
sbrs RESETREG, 0 rjmp rd_lp_2a_end ; Z=512 ; Case1: Y<256 pause here until Y=256 ; Case2: Y>=256 continue rd_lp2a: sbrs YH, 0 rjmp rd_lp2a
rd_lp_2a_end:
;sbi PORTB, 0 ; LED0 off nop ; instead of upper instr.
; Write to SRAM locations 512-1023 ldi ZH, $02 ; clr ZL clr ARG0 clr ARG1
;Read loop 1 ; 0 <= Z <= 255 read into SRAM and SRAM+1 ; 256 <= Z <= 511 ignore twice rd_lp1:
; Fast read ldi TEMP0, 0b01000000 ; Set PC6 to 1 and 000 address out PORTC, TEMP0 cbi PORTC, 5 ; clear PC5 = -CS0 sbi PORTD, 7 ; set PD7 = -IOWR cbi PORTD, 6 ; clear PD6 = -IORD ldi TEMP0, $00 ; also delay out DDRA, TEMP0 ; PORTA input in RETURN0, PINA ; Read from the memory bus sbi PORTD, 6 ; set -IORD sbi PORTC, 5 ; set -CS0
sbrs ZH, 0 ; skip store if greater than 256 st Z, RETURN0 ; Store data adiw ZL, 1
; Fast read ldi TEMP0, 0b01000000 ; Set PC6 to 1 and 000 address out PORTC, TEMP0 cbi PORTC, 5 ; clear PC5 = -CS0 sbi PORTD, 7 ; set PD7 = -IOWR cbi PORTD, 6 ; clear PD6 = -IORD ldi TEMP0, $00 ; also delay out DDRA, TEMP0 ; PORTA input in RETURN0, PINA ; Read from the memory bus sbi PORTD, 6 ; set -IORD sbi PORTC, 5 ; set -CS0
sbrs ZH, 0 st Z, RETURN0 adiw ZL, 1 ; increment
cpi ZH, $04 brne rd_lp1
rcall chbsy
;RESETREG is cleared at reset and set to $FF after on. ;So, the start_spi_tx function call is executed only once sbrs RESETREG, 0 rcall start_spi_tx
; Set 20H in Command register ldi ARG1, $FF ; Write operation ldi ARG0, $1C ; 000-111-00: A2-A0 = 111 command reg. ldi ARG2, $20 ; Read Sector(s) rcall memrw
rcall chbsy ; Check if busy
sbrs RESETREG, 0 rjmp rd_lp_1a_end ; Z=512 ; Case1: Y>=256 pause here until Y=0 ; Case2: Y<256 continue rd_lp1a: sbrc YH, 0 rjmp rd_lp1a
rd_lp_1a_end:
ldi ZH, $02 ; clr ZL ;Read loop 2 ; 0 <= Z <= 255 ignore twice ; 256 <= Z <= 511 read into SRAM and SRAM+1 rd_lp2: ; Fast read ldi TEMP0, 0b01000000 ; Set PC6 to 1 and 000 address out PORTC, TEMP0 cbi PORTC, 5 ; clear PC5 = -CS0 sbi PORTD, 7 ; set PD7 = -IOWR cbi PORTD, 6 ; clear PD6 = -IORD ldi TEMP0, $00 ; also delay out DDRA, TEMP0 ; PORTA input in RETURN0, PINA ; Read from the memory bus sbi PORTD, 6 ; set -IORD sbi PORTC, 5 ; set -CS0
sbrc ZH, 0 ; skip store if less than 256 st Z, RETURN0 ; Store data adiw ZL, 1
; Fast read ldi TEMP0, 0b01000000 ; Set PC6 to 1 and 000 address out PORTC, TEMP0 cbi PORTC, 5 ; clear PC5 = -CS0 sbi PORTD, 7 ; set PD7 = -IOWR cbi PORTD, 6 ; clear PD6 = -IORD ldi TEMP0, $00 ; also delay out DDRA, TEMP0 ; PORTA input in RETURN0, PINA ; Read from the memory bus sbi PORTD, 6 ; set -IORD sbi PORTC, 5 ; set -CS0
sbrc ZH, 0 st Z, RETURN0 adiw ZL, 1 ; increment
cpi ZH, $04 brne rd_lp2
rcall chbsy
ldi ARG0, 1 rcall inc_sector ; increment sector by one mov TEMP0, SECTOR ; Check if SECTOR == xxxxxx00 andi TEMP0, $03 ; increment CLUS if so cpi TEMP0, $00 brne skip_inc_clus1 ldi TEMP0, 1 ; increment CLUS by 1 clr TEMP1 add CLUS_LO, TEMP0 adc CLUS_HI, TEMP1 rcall check_overflow skip_inc_clus1:
;----------------------------
; Check buttons for commands ; B3:PLAY - B2:STOP - B1:FF - B0:REV
; Check FF button sbic PINB, 1 ; Skip rjmp if button is pressed rjmp ff_skip1
in TEMP0, TIMSK sbr TEMP0, 1<<TOIE2 ; Enable timer2 interrupt out TIMSK, TEMP0
clr TEMP0 out TCNT2, TEMP0 ; Start timer clr TEMP3 timer2_loop1: ; 200ms delay cpi TEMP3, $06 nop nop brne timer2_loop1
sbis PINB, 1 ; Skip rjmp if button is not pressed rjmp ff_skip2 inc TRACK
lds TEMP0, NUM_TRACKS inc TEMP0 cp TRACK, TEMP0 brne ff_skip4 ; This is the last Track clr TRACK inc TRACK ; Reset to Track #1 ; Wait here for PLAY input play_loop1: sbic PINB, 3 ; Skip rjmp if button is pressed rjmp play_loop1
ff_skip4: rcall compute_addr rcall read_tag rjmp ff_skip3
ff_skip2: ; Button is still pressed sbic PINB, 1 ; Skip rjmp if button is pressed rjmp ff_skip3 clr TEMP0 out TCNT2, TEMP0 ; Start timer clr TEMP3 timer2_loop2: ; 33ms delay cpi TEMP3, $01 nop nop brne timer2_loop2
; 33ms ~= 500 bytes (128 kbps) ; 16 increment = 16X fast forward (slower for higher bitrates) ldi ARG0, 16 rcall inc_sector ; increment sector by 16
ldi TEMP0, 4 ; increment CLUS by 4 clr TEMP1 add CLUS_LO, TEMP0 adc CLUS_HI, TEMP1 rcall check_overflow
rjmp ff_skip2
ff_skip3: in TEMP0, TIMSK cbr TEMP0, 1<<TOIE2 ; Disable timer2 interrupt out TIMSK, TEMP0
ff_skip1:
;------------------------------
; Check REV button sbic PINB, 0 ; Skip rjmp if button is pressed rjmp rev_skip1
in TEMP0, TIMSK sbr TEMP0, 1<<TOIE2 ; Enable timer2 interrupt out TIMSK, TEMP0
clr TEMP0 out TCNT2, TEMP0 ; Start timer clr TEMP3 timer2_loop3: ; 200ms delay cpi TEMP3, $06 nop nop brne timer2_loop3
rev_skip2: sbis PINB, 0 ; Skip rjmp if button is not pressed rjmp rev_skip2 dec TRACK
clr TEMP0 cp TRACK, TEMP0 brne rev_skip4 ; This is the first Track clr TRACK inc TRACK ; Reset to Track #1
rev_skip4: rcall compute_addr rcall read_tag rjmp rev_skip3
; Did not implement Rewinding as inc_sector and check_overflow functions ; need to be modified.
;rev_skip2: ; Button is still pressed ; sbic PINB, 0 ; Skip rjmp if button is pressed ; rjmp rev_skip3 ; clr TEMP0 ; out TCNT2, TEMP0 ; Start timer ; clr TEMP3 ;timer2_loop4: ; 33ms delay ; cpi TEMP3, $01 ; nop ; nop ; brne timer2_loop4 ; ; ; 33ms ~= 500 bytes (128 kbps) ; ; 16 increment = 16X fast forward (slower for higher bitrates) ; ldi ARG0, 16 ; rcall inc_sector ; increment sector by 16
; ldi TEMP0, 4 ; increment CLUS by 4 ; clr TEMP1 ; add CLUS_LO, TEMP0 ; adc CLUS_HI, TEMP1 ; rcall check_overflow ; ; rjmp rev_skip2
rev_skip3: in TEMP0, TIMSK cbr TEMP0, 1<<TOIE2 ; Disable timer2 interrupt out TIMSK, TEMP0
rev_skip1: ;------------------------------ ldi TEMP0, $FF mov RESETREG, TEMP0
rjmp main
;No arguments: Address always 000 ;RETURN0 is output fastread: push TEMP0
ldi TEMP0, 0b01000000 ; Set PC6 to 1 and 000 address out PORTC, TEMP0
cbi PORTC, 5 ; clear PC5 = -CS0
sbi PORTD, 7 ; set PD7 = -IOWR cbi PORTD, 6 ; clear PD6 = -IORD
ldi TEMP0, $00 ; also delay out DDRA, TEMP0 ; PORTA input in RETURN0, PINA ; Read from the memory bus
sbi PORTD, 6 ; set -IORD
sbi PORTC, 5 ; set -CS0
pop TEMP0 ret
; Function memrw ; Read and Write to the Flash Card. ; CS1=1 and CS0=0 when reading or writing. ; So, don't use for Device Control, Alt Status and Drive Address regs.
; ARG0: Address 0-2 : PC2-4 (xxxnnnxx) ; ARG1= $00 -> read ; ARG1= $FF -> write ; ARG2: Write value (not used in read) ; RETURN0: Read value (unchanged in write) memrw: push TEMP0 push TEMP1 push TEMP2 push ARG0
mov TEMP1, ARG0 andi TEMP1, $1C ; 00011100 and TEMP1 = 000xxx00
ldi TEMP2, $00 ; port C all inputs out DDRC, TEMP2 ; in TEMP0, PINC ; Read current bus value ldi TEMP2, $FF ; port C all outputs out DDRC, TEMP2 ;
or TEMP0, TEMP1 ; Set ones
mov TEMP1, ARG0 ori TEMP1, $E3 ; 11100011 or TEMP1 = 111xxx11
and TEMP0, TEMP1 ; Set zeros
out PORTC, TEMP0 ; Addr 0-2
sbi PORTC, 6 ; set PC6 = -CS1 cbi PORTC, 5 ; clear PC5 = -CS0
cpi ARG1, $00 brne wr_jmp ;Read sbi PORTD, 7 ; set PD7 = -IOWR cbi PORTD, 6 ; clear PD6 = -IORD
ldi TEMP1, $00 ; also delay out DDRA, TEMP1 ; PORTA input in RETURN0, PINA ; Read from the memory bus
sbi PORTD, 6 ; set -IORD
rjmp wr_end
;Write wr_jmp: sbi PORTD, 6 ; set PD6 = -IORD cbi PORTD, 7 ; clear PD7 = -IOWR
ldi TEMP1, $FF out DDRA, TEMP1 ; PORTA output out PORTA, ARG2 ; Write to the memory bus nop ; tsu(IOWR)
sbi PORTD, 7 ; set -IOWR
wr_end: sbi PORTC, 5 ; set -CS0
pop ARG0 pop TEMP2 pop TEMP1 pop TEMP0 ret
; Read CF status register for busy chbsy: push ARG1 push ARG0 push TEMP0
bsy_lp: ldi ARG1, $00 ; Read operation ldi ARG0, $1C ; 000-111-00: status reg. rcall memrw mov TEMP0, RETURN0 ; Save original status andi RETURN0, $F7 ; ignore DRQ bit cpi RETURN0, $51 brne no_err
; Ready but, Error bit is set ldi ARG1, $00 ; Read operation ldi ARG0, $04 ; 000-001-00: error reg. rcall memrw com RETURN0 ;out PORTB, RETURN0 ; Display error rjmp bsy_lp
no_err: mov RETURN0, TEMP0
andi RETURN0, $F7 cpi RETURN0, $50 ; if still busy, just loop back brne bsy_lp
pop TEMP0 pop ARG0 pop ARG1 ret
; Function print_hex: Gets ARG1 as 8-bit input and outputs hex value ; If ARG2(0) is cleared, function does not send characters to LCD print_hex: push TEMP0 push ARG0
;higher nibble mov TEMP0, ARG1 swap TEMP0 andi TEMP0, $0F cpi TEMP0, $0A brsh let1 ldi ARG0, $30 add ARG0, TEMP0 rcall putchar sbrc ARG2, 0 rcall lcd_data_write rjmp nib1 let1: ldi ARG0, $37 add ARG0, TEMP0 rcall putchar sbrc ARG2, 0 rcall lcd_data_write ;lower nibble nib1: mov TEMP0, ARG1 andi TEMP0, $0F ; Lower nibble cpi TEMP0, $0A brsh let2 ; Branch if same or higher than "A" ldi ARG0, $30 add ARG0, TEMP0 rcall putchar sbrc ARG2, 0 rcall lcd_data_write rjmp nib2 let2: ldi ARG0, $37 add ARG0, TEMP0 rcall putchar sbrc ARG2, 0 rcall lcd_data_write nib2: pop ARG0 pop TEMP0 ret
; Function LF_CR ; Take the cursor to new line in terminal LF_CR: push ARG0
ldi ARG0, $0A ; Line Feed rcall putchar ldi ARG0, $0D ; Carriage Return rcall putchar
pop ARG0 ret
;Function putchar ;Transmit to UART, waits for empty buffer putchar: sbis UCSRA, UDRE ; loop until USR:UDRE is 1 rjmp putchar out UDR, ARG0 ; write ARG0 to transmitter buffer ret
;Function putchar2 ;Transmit to UART, both true value and MSB inverted waits for empty buffer putchar2: push TEMP0
putc1: sbis UCSRA, UDRE ; loop until USR:UDRE is 1 rjmp putc1 out UDR, ARG0 ; write ARG0 to transmitter buffer putc2: sbis UCSRA, UDRE rjmp putc2 ldi TEMP0, $80 eor TEMP0, ARG0 out UDR, TEMP0
pop TEMP0 ret
;Function getchar ;Waits to receive data from UART getchar: sbis UCSRA, RXC ; loop until USR:RXC is 1 rjmp getchar in RETURN0, UDR ; return receive data in RETURN0 ret
;I2C master transmitter Function ; Writes ARG2 into the MP3 decoder register ARG1 ; Refer to Mega163 manual I2C_write_byte: push TEMP0 push ARG0
; Initialization for writing ldi TEMP0, $04 ; 00000100 out TWCR, TEMP0 nop nop
; Initialize TWBR ldi TEMP0, $C0 ; 30 out TWBR, TEMP0 nop nop
; Send Start Condition ldi TEMP0, (1<<TWSTA) | (1<<TWEN) retry_w:out TWCR, TEMP0 nop nop
wait1: in TEMP0, TWCR nop sbrs TEMP0, TWINT rjmp wait1
; Check Status in TEMP0, TWSR cpi TEMP0, $08 ; START brne WR_ERR
; Select decoder chip for Write ldi TEMP0, 0x86 out TWDR, TEMP0 ldi TEMP0, (1<<TWINT) | (1<<TWEN) out TWCR, TEMP0 nop nop
wait2: in TEMP0, TWCR nop sbrs TEMP0, TWINT rjmp wait2
;Check Status in TEMP0, TWSR cpi TEMP0, $18 ; MT_SLA_ACK brne WR_ERR
; Write the address out TWDR, ARG1 ldi TEMP0, (1<<TWINT) | (1<<TWEN) out TWCR, TEMP0 nop nop
wait3: in TEMP0, TWCR nop sbrs TEMP0, TWINT rjmp wait3
;Check Status in TEMP0, TWSR cpi TEMP0, $28 ; MT_DATA_ACK brne WR_ERR
; Write the data out TWDR, ARG2 ldi TEMP0, (1<<TWINT) | (1<<TWEN) out TWCR, TEMP0 nop nop
wait4: in TEMP0, TWCR nop sbrs TEMP0, TWINT rjmp wait4
;Check Status in TEMP0, TWSR cpi TEMP0, $28 ; MT_DATA_ACK brne WR_ERR
; Send Stop Condition ldi TEMP0, (1<<TWINT) | (1<<TWSTO) | (1<<TWEN) out TWCR, TEMP0 nop nop
rjmp skip_w
; Send Status to UART WR_ERR: ldi ARG0, $57 ; "W" rcall putchar
;ldi TEMP0, (1<<TWSTA) | (1<<TWEN) | (1<<TWINT) ;rjmp retry_w
wr_pause2: call getchar cpi RETURN0, $43 ; "C" brne wr_pause2
mov TEMP0, ARG1 com TEMP0 ;out PORTB, TEMP0
;Wait for "C" wr_pause1: rcall getchar cpi RETURN0, $43 ; "C" brne wr_pause1
; Deactivate interface before ending skip_w: clr TEMP0 out TWCR, TEMP0 nop nop
pop ARG0 pop TEMP0 ret
;I2C master receiver Function ; Reads the address in MP3 decoder chip. ; First writes the address to be read (without writing a data) ; Then reads one byte ; ARG1: read address ; RETURN0: return data ; Refer to Mega163 manual I2C_read_byte: push TEMP0 push ARG0
; WRITING the address to be read ; Initialization for writing ldi TEMP0, $04 ; 00000100 out TWCR, TEMP0 nop nop
; Initialize TWBR ldi TEMP0, $C0 ; 30 out TWBR, TEMP0
; Send Start Condition ldi TEMP0, (1<<TWSTA) | (1<<TWEN) out TWCR, TEMP0 nop nop
wait1r: in TEMP0, TWCR nop sbrs TEMP0, TWINT rjmp wait1r
ldi ARG0, $31 ; "1" rcall putchar
; Check Status in TEMP0, TWSR cpi TEMP0, $08 ; START brne RD_ERR
; Select decoder chip for Write ldi TEMP0, 0x86 out TWDR, TEMP0 ldi TEMP0, (1<<TWINT) | (1<<TWEN) out TWCR, TEMP0 nop nop
wait2r: in TEMP0, TWCR nop sbrs TEMP0, TWINT rjmp wait2r
ldi ARG0, $32 ; "2" rcall putchar
;Check Status in TEMP0, TWSR cpi TEMP0, $18 ; MT_SLA_ACK brne RD_ERR
; Write the address out TWDR, ARG1 ldi TEMP0, (1<<TWINT) | (1<<TWEN) out TWCR, TEMP0 nop nop
wait3r: in TEMP0, TWCR nop sbrs TEMP0, TWINT rjmp wait3r
ldi ARG0, $33 ; "3" rcall putchar
;Check Status in TEMP0, TWSR ;out PORTB, TEMP0 cpi TEMP0, $28 ; MT_DATA_ACK brne RD_ERR
; Send Stop Condition ;ldi TEMP0, (1<<TWINT) | (1<<TWSTO) | (1<<TWEN) ;out TWCR, TEMP0
; Branch out of reach rjmp skip_next RD_ERR: rjmp RD_ERR2 skip_next: ; READING ; Send a REPEATED Start Condition ;ldi TEMP0, (1<<TWINT) | (1<<TWSTO) | (1<<TWSTA) | (1<<TWEN) ;out TWCR, TEMP0 ldi TEMP0, (1<<TWINT) | (1<<TWSTO) | (1<<TWEN) out TWCR, TEMP0 ;sbi TWCR, TWSTA ori TEMP0, $20 ; TWSTA: 00100000 out TWCR, TEMP0
nop nop
wait5: in TEMP0, TWCR nop sbrs TEMP0, TWINT rjmp wait5
ldi ARG0, $34 ; "4" rcall putchar
; Check Status in TEMP0, TWSR cpi TEMP0, $08 ; START brne RD_ERR
; Select decoder chip for Read ldi TEMP0, 0x87 out TWDR, TEMP0 ldi TEMP0, (1<<TWINT) | (1<<TWEN) out TWCR, TEMP0 nop nop
wait6: in TEMP0, TWCR nop sbrs TEMP0, TWINT rjmp wait6
ldi ARG0, $35 ; "5" rcall putchar
; Check Status in TEMP0, TWSR cpi TEMP0, $40 ; MR_SLA_ACK brne RD_ERR
; Read one byte (TWEA not set to send NACK after reading which stops reading) ldi TEMP0, (1<<TWINT) | (1<<TWEN) out TWCR, TEMP0 nop nop
wait9: in TEMP0, TWCR nop sbrs TEMP0, TWINT rjmp wait9
ldi ARG0, $36 ; "6" rcall putchar
; Check Status in TEMP0, TWSR cpi TEMP0, $58 ; MR_DATA_NACK brne RD_ERR
; Get the return value in RETURN0, TWDR
; Send Stop Condition ldi TEMP0, (1<<TWINT) | (1<<TWSTO) | (1<<TWEN) out TWCR, TEMP0 nop nop
rjmp skip_r
; Send Status to UART RD_ERR2: ldi ARG0, $52 ; "R" rcall putchar
com ARG0 ;out PORTB, ARG0
;Wait for "C" rd_pause1: rcall getchar cpi RETURN0, $43 ; "C" brne rd_pause1
com TEMP0 ;out PORTB, TEMP0
;Wait for "C" rd_pause2: rcall getchar cpi RETURN0, $43 ; "C" brne rd_pause2
; Disable interface before exit skip_r: clr TEMP0 out TWCR, TEMP0 nop
pop ARG0 pop TEMP0 ret
; Function start_spi_tx ; Y register has the current reading position ; Takes the byte from SRAM(Y) and transmits over SPI ; This function starts a burst of transfers until DATA_REQ = 0 start_spi_tx: push TEMP0
; Load from current position ld TEMP0, Y ; Transmit over SPI out SPDR, TEMP0
adiw YL, 1 ; increment to next location sbrc YH, 2 ; Wrap around at 1024 (YH=$04) ; doesn't reset YH if YH=010 or 011 ldi YH, $02
pop TEMP0 ret
; Function read_tag ; read_tag: push SECTOR push CYL_LO push CYL_HI push HEAD push CLUS_LO push CLUS_HI push TEMP0 push TEMP1 push TEMP2
clr TEMP1 ; State=0 no letter found ; State=1 "T" has been found ; State=2 "TA" has been found ; State=3 "TAG" has been found
rcall lcd_clear
; Set SECTOR, CYL_LO etc. to next track's beginning inc TRACK rcall compute_addr
; Note: compute_addr goes to the location one cluster before next song ; So, no need to decrement
; Decrement sector by 1 to read the last 4 sectors of current TRACK ;ldi TEMP0, 4 ;tag_dec_sec: ; rcall dec_sector ; dec TEMP0 ; cpi TEMP0, 0 ; brne tag_dec_sec
clr ZH clr ZL tag_loop: ;Set Cylinder Low Reg. ldi ARG1, $FF ; Write operation ldi ARG0, $10 ; 000-100-00: cyl low reg. mov ARG2, CYL_LO ; cyl_lo = CYL_LO rcall memrw
;Set Cylinder Hi Reg. ldi ARG1, $FF ; Write operation ldi ARG0, $14 ; 000-101-00: cyl hi reg. mov ARG2, CYL_HI ; cyl_hi = 00 rcall memrw
;Set Head No. ldi ARG1, $FF ; Write operation ldi ARG0, $18 ; 000-110-00: select card/head reg. mov ARG2, HEAD ; 1010-0000: head no = 0 rcall memrw
;Set Sector Number Register ldi ARG1, $FF ; Write operation ldi ARG0, $0C ; 000-011-00: sec num reg. mov ARG2, SECTOR ; sec_num = SECTOR rcall memrw
;Set 01H in sector count register (read one sector) ldi ARG1, $FF ; Write operation ldi ARG0, $08 ; 000-010-00: sec cnt. ldi ARG2, $01 ; 1 sector rcall memrw
; Set 20H in Command register ldi ARG1, $FF ; Write operation ldi ARG0, $1C ; 000-111-00: A2-A0 = 111 command reg. ldi ARG2, $20 ; Read Sector(s) rcall memrw
rcall chbsy ; Check if busy
rjmp tag_rd_lp ; Relative branch was out of reach tag_loop1: rjmp tag_loop
tag_rd_lp: rcall fastread ;mov ARG0, RETURN0 ;rcall putchar ; State=0 cpi TEMP1, 0 brne state_1 cpi RETURN0, $54 ; "T" brne tag_skip ldi TEMP1, 1 ; Found first letter mov TEMP2, ZL ; Record the starting offset rjmp tag_skip ; State=1 state_1: cpi TEMP1, 1 brne state_2 cpi RETURN0, $41 ; "A" brne state_1T ldi TEMP1, 2 ; Found second letter rjmp tag_skip state_1T: cpi RETURN0, $54 ; "T" brne state_1_other ldi TEMP1, 1 ; Found first letter mov TEMP2, ZL ; Record the starting offset rjmp tag_skip state_1_other: ldi TEMP1, 0 ; Not "A" or "T" rjmp tag_skip ; State=2 state_2: cpi TEMP1, 2 brne state_3 cpi RETURN0, $47 ; "G" brne state_2T ldi TEMP1, 3 ; Found third letter rjmp tag_skip state_2T: cpi RETURN0, $54 ; "T" brne state_2_other ldi TEMP1, 1 ; Found first letter mov TEMP2, ZL ; Record the starting offset rjmp tag_skip state_2_other: ldi TEMP1, 0 ; Not "G" or "T" rjmp tag_skip ; State=3 state_3: sub ZL, TEMP2 cpi ZL, 33 ; (3-32): Title brsh tag_artist cpi ZL, 3 brne tag_title_skip ldi ARG0, $80 rcall lcd_command_write ; Go to first line of LCD tag_title_skip: mov ARG0, RETURN0 cpi ARG0, $00 ; If char = $00 make it $20 brne space_skip1 ldi ARG0, $20
rjmp space_skip1 ; Relative branch was out of reach tag_loop2: rjmp tag_loop1 tag_rd_lp1: rjmp tag_rd_lp
space_skip1: rcall lcd_data_write rcall putchar rjmp state_3_end tag_artist: cpi ZL, 63 ; (33-62): Artist brsh tag_escape ; SHOULD I READ UNTIL THE END OF SECTOR? cpi ZL, 33 brne tag_artist_skip ldi ARG0, $C0 rcall lcd_command_write ; Go to second line of LCD tag_artist_skip: mov ARG0, RETURN0 cpi ARG0, $00 brne space_skip2 ldi ARG0, $20 space_skip2: rcall lcd_data_write rcall putchar state_3_end: add ZL, TEMP2 ; restore ZL
tag_skip: adiw ZL, 1 ; increment mov TEMP0, ZH andi TEMP0, $01 cpi TEMP0, $00 brne tag_rd_lp1 cpi ZL, $00 brne tag_rd_lp1
rcall chbsy
ldi ARG0, $01 rcall inc_sector
cpi ZH, $08 brne tag_loop2 tag_escape: dec TRACK ldi ARG0, $E0 rcall lcd_command_write ; Go to address $60 ldi ARG0, $54 ; "T" rcall lcd_data_write ldi ARG0, $52 ; "R" rcall lcd_data_write ldi ARG0, $41 ; "A" rcall lcd_data_write ldi ARG0, $43 ; "C" rcall lcd_data_write ldi ARG0, $4B ; "K" rcall lcd_data_write ldi ARG0, $20 ; Space rcall lcd_data_write ldi ARG2, $FF ; Send to display mov ARG1, TRACK rcall print_hex ; Display track number
pop TEMP2 pop TEMP1 pop TEMP0 pop CLUS_HI pop CLUS_LO pop HEAD pop CYL_HI pop CYL_LO pop SECTOR ret
; Function read_root_dir ; Reads the Root Directory in FAT(0, 00, 01, 1C). Stores the first cluster ; address of each file in SRAM starting at SRAM[0x62]. Each entry is 2 bytes. ; Maximum entries is around 200 NUM_TRACKS is the number of files. Also reads ; the file size and stores it in TEMP:0-1-2. This is used to find the last ; cluster number for the last file. read_root_dir: push ARG0 push ARG1 push ARG2 push TEMP0 push TEMP1 push TEMP2 push TEMP3 ; used as temporary
ldi TEMP3, $1C mov SECTOR, TEMP3 ; Sector = $1C ldi TEMP3, $01 mov CYL_LO, TEMP3 ; cyl_lo = $01 clr CYL_HI ; cyl_hi = $00 ldi TEMP3, $A0 ; Head = 0 mov HEAD, TEMP3
ldi YH, $00 ldi YL, $62
clr FLAGREG
root_dir_loop: ;Set Cylinder Low Reg. ldi ARG1, $FF ; Write operation ldi ARG0, $10 ; 000-100-00: cyl low reg. mov ARG2, CYL_LO ; cyl_lo = CYL_LO rcall memrw
;Set Cylinder Hi Reg. ldi ARG1, $FF ; Write operation ldi ARG0, $14 ; 000-101-00: cyl hi reg. mov ARG2, CYL_HI ; cyl_hi = 00 rcall memrw
;Set Head No. ldi ARG1, $FF ; Write operation ldi ARG0, $18 ; 000-110-00: select card/head reg. mov ARG2, HEAD ; 1010-0000: head no = 0 rcall memrw
;Set Sector Number Register ldi ARG1, $FF ; Write operation ldi ARG0, $0C ; 000-011-00: sec num reg. mov ARG2, SECTOR ; sec_num = SECTOR rcall memrw
;Set 01H in sector count register (read one sector) ldi ARG1, $FF ; Write operation ldi ARG0, $08 ; 000-010-00: sec cnt. ldi ARG2, $01 ; 1 sector rcall memrw
; Set 20H in Command register ldi ARG1, $FF ; Write operation ldi ARG0, $1C ; 000-111-00: A2-A0 = 111 command reg. ldi ARG2, $20 ; Read Sector(s) rcall memrw
rcall chbsy ; Check if busy
;Read 512 times the data register. Write to SRAM ldi ZH, $02 ; 512 clr ZL root_rd_lp: rcall fastread st Z, RETURN0 ; Store data adiw ZL, 1 ; increment cpi ZH, $04 brne root_rd_lp
rcall chbsy
; Parse the stored Sector to find first clusters ldi ZH, $02 ldi ZL, $00 mov TEMP3, FLAGREG clr FLAGREG cpi TEMP3, 0 brne root_skip2 ; If 1 was at last 32 bytes of previous sector ; Just start reading the first cluster and file size root_parse: ld TEMP3, Z cpi ZH, $04 breq root_skip adiw ZL, 32 cpi TEMP3, 1 ; 44-3-2-1 if one Z brne root_parse
cpi ZH, $04 brne root_skip2 inc FLAGREG ; If 1 is at last 32 bytes of sector set this flag rjmp root_skip
root_skip2:
; Store first cluster address adiw ZL, 26 ld TEMP3, Z+ ; FirstCluster lower byte (location 26) st Y+, TEMP3 ld TEMP3, Z+ ; FirstCluster higher byte (location 27) st Y+, TEMP3
; Store file size into temporary registers ; $0061-$0064: Lower to Higher adiw ZL, 1 ; Skip FileSize byte[0] ld TEMP0, Z+ ; FileSize byte[1] (lowest byte - loc. 29) ld TEMP1, Z+ ; FileSize byte[2] (lowest byte - loc. 30) ld TEMP2, Z+ ; FileSize byte[3] (lowest byte - loc. 31)
cpi ZH, $04 breq root_skip
; Z=0 (mod 32) here ; LOCK TEMP0, TEMP1, TEMP2 here
ld TEMP3, Z cpi TEMP3, $E5 ; End of entries brne root_parse
; Divide file size by 2048 (shift by 11). Done by shifting right by three ; TEMP0 and TEMP1 holds the file size in clusters ldi TEMP3, 3 div_8: lsr TEMP0 lsr TEMP1 brcc div_skip1 ; Skip next instruction if C bit is zero ori TEMP0, $80 ; Shift TEMP1[0] into TEMP0[7] div_skip1: lsr TEMP2 brcc div_skip_2 ; Skip next instruction if C bit is zero ori TEMP1, $80 ; Shift TEMP2[0] into TEMP1[7] div_skip_2: dec TEMP3 cpi TEMP3, 0 brne div_8
;NUM_TRACKS = Y/2 - $31 (Y<256 so YH = 0) mov TEMP3, YL lsr TEMP3 subi TEMP3, $31 sts NUM_TRACKS, TEMP3
mov ZL, YL mov ZH, YL sbiw ZL, 2 ; Z = address of last entry in SRAM ld TEMP2, Z+ ; Last cluster entry lower byte ld TEMP3, Z+ ; Last cluster entry higher byte add TEMP0, TEMP2 adc TEMP1, TEMP3 ;TEMP0 and TEMP1 holds last files last cluster mov ZL, TEMP0 mov ZH, TEMP1 adiw ZL, 1 ;Store lastfile_lastcluster+1 into the next empty location in SRAM st Y+, ZL st Y+, ZH rjmp root_exit
root_skip: ; Increment sector. If sector = 20h increment HEAD mov TEMP3, SECTOR cpi TEMP3, $20 brne root_inc_sc clr SECTOR inc HEAD mov TEMP3, HEAD cpi TEMP3, $A4 brne root_inc_sc ldi TEMP3, $A0 mov HEAD, TEMP3 inc CYL_LO
; CYL_HI = 0 $00<=CYL_LO<=$FF mov TEMP3, CYL_HI cpi TEMP3, $00 brne root_hi_1 mov TEMP3, CYL_LO cpi TEMP3, $00 ; Overflow brne root_inc_sc inc CYL_HI rjmp root_inc_sc ; CYL_HI = 1 $00<=CYL_LO<=$E9 root_hi_1: mov TEMP3, CYL_LO cpi TEMP3, $EA brne root_inc_sc clr CYL_HI clr CYL_LO root_inc_sc: inc SECTOR
rjmp root_dir_loop
root_exit: pop TEMP3 pop TEMP2 pop TEMP1 pop TEMP0 pop ARG2 pop ARG1 pop ARG0 ret
; Function inc_sector ; Increments SECTOR by the number specified with ARG0 inc_sector: push TEMP2 ; Increment sector. If sector = 20h increment HEAD mov TEMP2, SECTOR cpi TEMP2, $20 brne inc_sc clr SECTOR inc HEAD mov TEMP2, HEAD cpi TEMP2, $A4 brne inc_sc ldi TEMP2, $A0 mov HEAD, TEMP2 inc CYL_LO
; CYL_HI = 0 $00<=CYL_LO<=$FF mov TEMP2, CYL_HI cpi TEMP2, $00 brne hi_1 mov TEMP2, CYL_LO cpi TEMP2, $00 ; Overflow brne inc_sc inc CYL_HI rjmp inc_sc ; CYL_HI = 1 $00<=CYL_LO<=$E9 hi_1: mov TEMP2, CYL_LO cpi TEMP2, $EA brne inc_sc clr CYL_HI clr CYL_LO inc_sc: add SECTOR, ARG0 ; Increment by AGR0 mov TEMP2, SECTOR cpi TEMP2, $20 ; If SECTOR>=$20 SECTOR=$20 brlo sc_skip ; This gives a maximum increment amount ldi TEMP2, $20 ; of 16. e.g.: (20+12)/2 = 16 mov SECTOR, TEMP2 sc_skip: pop TEMP2 ret
; Function dec_sector ; Decrements SECTOR by 1 dec_sector: push TEMP2 ; Decrement sector. If sector = 01h decrement HEAD mov TEMP2, SECTOR cpi TEMP2, $01 brne dec_sc ldi TEMP2, $21 mov SECTOR, TEMP2 dec HEAD mov TEMP2, HEAD cpi TEMP2, $9F brne dec_sc ldi TEMP2, $A3 mov HEAD, TEMP2 dec CYL_LO
; CYL_HI = 0 $00<=CYL_LO<=$FF mov TEMP2, CYL_HI cpi TEMP2, $00 brne dec_hi_1 mov TEMP2, CYL_LO cpi TEMP2, $FF ; Overflow brne dec_sc inc CYL_HI ldi TEMP2, $E9 mov CYL_LO, TEMP2 rjmp dec_sc ; CYL_HI = 1 $00<=CYL_LO<=$E9 dec_hi_1: mov TEMP2, CYL_LO cpi TEMP2, $FF brne dec_sc clr CYL_HI dec_sc: dec SECTOR
pop TEMP2 ret
; Function check_overflow ; Checks whether the current cluster no exceeds the next track's cluster no ; If the cluster no overflows there are two cases: ; -this was the last track: resets TRACK# to 1 and waits for PLAY button ; -else: Increment TRACK, Read and Display Tag information of next track check_overflow: push TEMP0 push TEMP1 push YH push YL
;Read next TRACK's first cluster no ;SRAM offset (r1:r0) = 2*(TRACK+1) + $60 ldi TEMP0, 2 ldi TEMP1, 1 add TEMP1, TRACK ; TEMP1 = TRACK+1 mul TEMP1, TEMP0 ; 2*(TRACK+1) -> r1:r0 mov YH, r1 mov YL, r0 adiw YL, $30 adiw YL, $30 ; Y = Y + $60
; Y has the next TRACK's first cluster no ; TEMP0 = Cluster lower byte, TEMP1 = Cluster higher byte ld TEMP0, Y+ ld TEMP1, Y+
cp CLUS_HI, TEMP1 brlo ovfl_skip1 ; skip if CLUS_HI < TEMP1 cp TEMP1, CLUS_HI brlo ovfl_skip2 ; skip if CLUS_HI > TEMP1 cp CLUS_LO, TEMP0 ; CLUS_HI = TEMP1 here brlo ovfl_skip1 ; skip if CLUS_LO < TEMP0 ; Overflow detected ovfl_skip2: lds TEMP0, NUM_TRACKS cp TRACK, TEMP0 brne ovfl_skip3 ; This is the last Track clr TRACK inc TRACK ; Reset to Track #1 rcall compute_addr rcall read_tag ; Wait here for PLAY input play_loop: sbic PINB, 3 ; Skip rjmp if button is pressed rjmp play_loop
rjmp ovfl_skip1 ovfl_skip3: ; Not Last Track: Skip to next track inc TRACK
ldi ARG0, $54 ; T rcall putchar ; Debug ldi ARG0, $52 ; R rcall putchar ; Debug ldi ARG0, $30 add ARG0, TRACK rcall putchar
rcall compute_addr rcall read_tag ovfl_skip1:
pop YL pop YH pop TEMP1 pop TEMP0 ret
; Function compute_addr ; Input is the TRACK register ; Grabs the FirstCluster no from SRAM and computes HEAD, CYL_HI, ; CYL_LO and SECTOR values ; Also sets CLUS_LO and CLUS_HI registers to FisrtCluster no ; SRAM offset = $60 + 2*TRACK (2 bytes) ; Sector = $AF + 4*FirstCluster (2 bytes) ; 15 | 14 13 12 11 10 9 8 7 | 6 5 | 4 3 2 1 0 ; CYL_HI | CYL_LO | HEAD | SECTOR ; Add 1 to SECTOR as it starts from 1 compute_addr: push TEMP0 push TEMP1 push YH push YL
;SRAM offset (r1:r0) = 2*TRACK + $60 ldi TEMP0, 2 mul TRACK, TEMP0 ; 2*TRACK -> r1:r0 mov YH, r1 mov YL, r0 adiw YL, $30 adiw YL, $30 ; Y = Y + $60
; TEMP0 = Cluster lower byte, TEMP1 = Cluster higher byte ld TEMP0, Y+ ld TEMP1, Y+
mov CLUS_LO, TEMP0 mov CLUS_HI, TEMP1
;Sector(r1:r0) = $AF + 4*FirstCluster ;MaxCluster= $3CF1 $3C*4 = $00F0 -----> stored in YH ; $FF*4 = $ 03FC ; $ B0 ; +________ ldi YL, 4 mul TEMP1, YL ; 4*Cluster_hi -> r1:r0 mov YH, r0 ; r1 is always 0 mul TEMP0, YL ; 4*Cluster_lo -> r1:r0 ldi YL, $AF add r0, YL adc r1, YH ; r1:r0 = sector(15:0)
clr CYL_HI bst r1, 7 ; Store bit 15 to T bld CYL_HI, 0 ; Load T into CYL_HI(0)
mov CYL_LO, r1 ; CYL_LO = bits 15:8 lsl CYL_LO ; CYL_LO(7:1) = bits 14:8 bst r0, 7 ; Store bit 7 to T bld CYL_LO, 0 ; CYL_LO(0) = bit 7
ldi TEMP0, $A0 mov HEAD, TEMP0 bst r0, 6 ; Store bit 6 to T bld HEAD, 1 ; HEAD(1) = bit 6 bst r0, 5 ; Store bit 5 to T bld HEAD, 0 ; HEAD(0) = bit 5
mov TEMP0, r0 andi TEMP0, $1F ; and with 0001-1111 mov SECTOR, TEMP0 ; SECTOR(4:0) = bits 4:0 inc SECTOR ; SECTOR starts at 1 and ends at 32
pop YL pop YH pop TEMP1 pop TEMP0 ret
; Function is_lcd_busy is_lcd_busy: push TEMP0 push TEMP1
rcall five_nops ; Enable cycle delay rcall five_nops
cbi PORTC, 2 ; Control word sbi PORTD, 7 ; read clr TEMP1 out PORTA, TEMP1 out DDRA, TEMP1 ; Port A input sbi PORTC, 7 ; Enable rcall five_nops ; Min 160ns delay in TEMP0, PINA ; Read LCD data nop nop cbi PORTC, 7
;Wait here if LCD is busy lcd_busy_loop: sbrs TEMP0, 7 ; skip rjmp if set (busy) rjmp lcd_busy_end rcall five_nops ; Enable cycle delay rcall five_nops sbi PORTC, 7 ; Enable rcall five_nops ; Min 160ns delay in TEMP0, PINA ; Read LCD data nop nop cbi PORTC, 7 rjmp lcd_busy_loop lcd_busy_end: pop TEMP1 pop TEMP0 ret
; Function five_nops five_nops: nop nop nop nop nop ret
; Function lcd_data_write ; ARG0 is the data to write lcd_data_write: push TEMP0 sbi PORTC, 2 ; Data word cbi PORTD, 7 ; Write out PORTA, ARG0 ldi TEMP0, $FF out DDRA, TEMP0 ; Port A output sbi PORTC, 7 ; Enable rcall five_nops rcall five_nops cbi PORTC, 7 rcall is_lcd_busy pop TEMP0 ret
; Function lcd_command_write ; ARG0 is the command to write lcd_command_write: push TEMP0 cbi PORTC, 2 ; Control word cbi PORTD, 7 ; Write out PORTA, ARG0 ldi TEMP0, $FF out DDRA, TEMP0 ; Port A output sbi PORTC, 7 ; Enable rcall five_nops rcall five_nops cbi PORTC, 7 rcall is_lcd_busy pop TEMP0 ret
; Function lcd_clear lcd_clear: push ARG0 ldi ARG0, $01 rcall lcd_command_write pop ARG0 ret
; SPI transfer Complete Interrupt Handler ; Transmits next byte and increments Y register ; Continously transfer until DATA_REQ=0 ; For a rate of F/32 there are 256 clocks of execution ; until the next interrupt. SPICompIsr: push TEMP0 push TEMP1 ; Stores only SREG in here in TEMP1, SREG ; Save Status register
;cbi PORTB, 1 ; LED1 on nop ; Instead of instr. above
sbis PIND, 2 ; skip rjmp if DATA_REQ is high rjmp spi1 ; Load from current position ld TEMP0, Y ; Transmit over SPI out SPDR, TEMP0 adiw YL, 1 ; increment to next location sbrc YH, 2 ; Wrap around at 1024 (YH=$04) ; doesn't reset YH if YH=010 or 011 ldi YH, $02
;sbi PORTB, 1 ; LED1 off nop ; Instead of instr. above
spi1: out SREG, TEMP1 ; Save Status register pop TEMP1 pop TEMP0 reti
; INT0 interrupt handler. ; Triggered by a rising edge on DATA_REQ input from decoder Int0Isr: push TEMP0 ; save registers in TEMP0, SREG ; save status register
rcall start_spi_tx ; Start a burst of transfer
out SREG, TEMP0 ; restore status register pop TEMP0 ; restore registers reti ; return from the interrupt
; Timer2 interrupt handler ; This routine will be automatically called whenever timer 2 overflows ; Overflows after 33ms Timer2Isr: push TEMP0 ; save registers in TEMP0, SREG ; save status register
inc TEMP3
out SREG, TEMP0 ; restore status register pop TEMP0 ; restore registers reti ; return from the interrupt