( ----- 000 ) AVR MASTER INDEX 301 AVR macros 302 AVR assembler 320 SMS PS/2 controller 345 Arduino blinker 350 Arduino SPI spitter ( ----- 001 ) : AVRA 302 312 LOADR ; : ATMEGA328P 315 LOAD ; ( ----- 002 ) \ AVR assembler. See doc/asm/avr.txt. \ We divide by 2 because each PC represents a word. : PC HERE XORG - >> ; : <<3 << << << ; : <<4 <<3 << ; : _oor ." arg out of range: " .X SPC> ." PC " PC .X NL> ABORT ; : _r8c DUP 7 > IF _oor THEN ; : _r32c DUP 31 > IF _oor THEN ; : _r16+c _r32c DUP 16 < IF _oor THEN ; : _r64c DUP 63 > IF _oor THEN ; : _r256c DUP 255 > IF _oor THEN ; : _Rdp ( op rd -- op', place Rd ) <<4 OR ; ( ----- 003 ) ( 0000 000d dddd 0000 ) : OPRd DOER , DOES> @ SWAP _r32c _Rdp L, ; $9405 OPRd ASR, $9400 OPRd COM, $940a OPRd DEC, $9403 OPRd INC, $9206 OPRd LAC, $9205 OPRd LAS, $9207 OPRd LAT, $9406 OPRd LSR, $9401 OPRd NEG, $900f OPRd POP, $920f OPRd PUSH, $9407 OPRd ROR, $9402 OPRd SWAP, $9204 OPRd XCH, $9200 OPRd _ : STS, ( k16 rd ) _ L, ; $9000 OPRd _ : LDS, ( rd k16 ) SWAP _ L, ; ( ----- 004 ) ( 0000 00rd dddd rrrr ) : OPRdRr DOER C, DOES> C@ ( rd rr op ) OVER _r32c $10 AND >> >> >> OR ( rd rr op' ) <<8 OR $ff0f AND ( rd op' ) SWAP _r32c _Rdp L, ; $1c OPRdRr ADC, $0c OPRdRr ADD, $20 OPRdRr AND, $14 OPRdRr CP, $04 OPRdRr CPC, $10 OPRdRr CPSE, $24 OPRdRr EOR, $2c OPRdRr MOV, $9c OPRdRr MUL, $28 OPRdRr OR, $08 OPRdRr SBC, $18 OPRdRr SUB, ( 0000 0AAd dddd AAAA ) : OPRdA DOER C, DOES> C@ ( rd A op ) OVER _r64c $30 AND >> >> >> OR ( rd A op' ) <<8 OR $ff0f AND ( rd op' ) SWAP _r32c _Rdp L, ; $b0 OPRdA IN, $b8 OPRdA _ : OUT, SWAP _ ; ( ----- 005 ) ( 0000 KKKK dddd KKKK ) : OPRdK DOER C, DOES> C@ ( rd K op ) OVER _r256c $f0 AND >> >> >> >> OR ( rd K op' ) ROT _r16+c <<4 ROT $0f AND OR ( op' rdK ) C, C, ; $70 OPRdK ANDI, $30 OPRdK CPI, $e0 OPRdK LDI, $60 OPRdK ORI, $40 OPRdK SBCI, $60 OPRdK SBR, $50 OPRdK SUBI, ( 0000 0000 AAAA Abbb ) : OPAb DOER C, DOES> C@ ( A b op ) ROT _r32c <<3 ROT _r8c OR C, C, ; $98 OPAb CBI, $9a OPAb SBI, $99 OPAb SBIC, $9b OPAb SBIS, ( ----- 006 ) : OPNA DOER , DOES> @ L, ; $9598 OPNA BREAK, $9488 OPNA CLC, $94d8 OPNA CLH, $94f8 OPNA CLI, $94a8 OPNA CLN, $94c8 OPNA CLS, $94e8 OPNA CLT, $94b8 OPNA CLV, $9498 OPNA CLZ, $9419 OPNA EIJMP, $9509 OPNA ICALL, $9519 OPNA EICALL, $9409 OPNA IJMP, $0000 OPNA NOP, $9508 OPNA RET, $9518 OPNA RETI, $9408 OPNA SEC, $9458 OPNA SEH, $9478 OPNA SEI, $9428 OPNA SEN, $9448 OPNA SES, $9468 OPNA SET, $9438 OPNA SEV, $9418 OPNA SEZ, $9588 OPNA SLEEP, $95a8 OPNA WDR, ( ----- 007 ) ( 0000 0000 0sss 0000 ) : OPb DOER , DOES> @ ( b op ) SWAP _r8c _Rdp L, ; $9488 OPb BCLR, $9408 OPb BSET, ( 0000 000d dddd 0bbb ) : OPRdb DOER , DOES> @ ( rd b op ) ROT _r32c _Rdp SWAP _r8c OR L, ; $f800 OPRdb BLD, $fa00 OPRdb BST, $fc00 OPRdb SBRC, $fe00 OPRdb SBRS, ( special cases ) : CLR, DUP EOR, ; : TST, DUP AND, ; : LSL, DUP ADD, ; ( ----- 008 ) ( a -- k12, absolute addr a, relative to PC in a k12 addr ) : _r7ffc DUP $7ff > IF _oor THEN ; : _raddr12 PC - DUP 0< IF $800 + _r7ffc $800 OR ELSE _r7ffc THEN ; : RJMP _raddr12 $c000 OR ; : RCALL _raddr12 $d000 OR ; : RJMP, RJMP L, ; : RCALL, RCALL L, ; ( ----- 009 ) ( a -- k7, absolute addr a, relative to PC in a k7 addr ) : _r3fc DUP $3f > IF _oor THEN ; : _raddr7 PC - DUP 0< IF $40 + _r3fc $40 OR ELSE _r3fc THEN ; : _brbx ( a b op -- a ) OR SWAP _raddr7 <<3 OR ; : BRBC $f400 _brbx ; : BRBS $f000 _brbx ; : BRCC 0 BRBC ; : BRCS 0 BRBS ; : BREQ 1 BRBS ; : BRNE 1 BRBC ; : BRGE 4 BRBC ; : BRHC 5 BRBC ; : BRHS 5 BRBS ; : BRID 7 BRBC ; : BRIE 7 BRBS ; : BRLO BRCS ; : BRLT 4 BRBS ; : BRMI 2 BRBS ; : BRPL 2 BRBC ; : BRSH BRCC ; : BRTC 6 BRBC ; : BRTS 6 BRBS ; : BRVC 3 BRBC ; : BRVS 3 BRBS ; ( ----- 010 ) 9 CONSTS $100c X $0008 Y $0000 Z $100d X+ $1009 Y+ $1001 Z+ $100e -X $100a -Y $1002 -Z : _ ( Rd XYZ op ) OR ( Rd op' ) SWAP _Rdp L, ; : LD, $8000 _ ; : ST, SWAP $8200 _ ; : LPM, $9004 _ ; ( ----- 011 ) \ LBL! L1 .. L1 ' RJMP LBL, : LBL! ( -- ) PC TO ; : LBL, ( opw pc -- ) 1- SWAP EXECUTE L, ; : SKIP, PC 0 L, ; : TO, ( opw pc ) \ warning: pc is a PC offset, not a mem addr! << XORG + PC 1- HERE ( opw addr tgt hbkp ) ROT 'HERE ! ( opw tgt hbkp ) SWAP ROT EXECUTE HERE ! ( hbkp ) 'HERE ! ; \ FLBL, L1 .. ' RJMP L1 TO, : FLBL, LBL! 0 L, ; : BEGIN, PC ; : AGAIN?, ( pc op ) SWAP LBL, ; : AGAIN, ['] RJMP AGAIN?, ; : IF, ['] BREQ SKIP, ; : THEN, TO, ; ( ----- 012 ) \ Constant common to all AVR models 38 CONSTS 0 R0 1 R1 2 R2 3 R3 4 R4 5 R5 6 R6 7 R7 8 R8 9 R9 10 R10 11 R11 12 R12 13 R13 14 R14 15 R15 16 R16 17 R17 18 R18 19 R19 20 R20 21 R21 22 R22 23 R23 24 R24 25 R25 26 R26 27 R27 28 R28 29 R29 30 R30 31 R31 26 XL 27 XH 28 YL 29 YH 30 ZL 31 ZH ( ----- 015 ) ( ATmega328P definitions ) 87 CONSTS $c6 UDR0 $c4 UBRR0L $c5 UBRR0H $c2 UCSR0C $c1 UCSR0B $c0 UCSR0A $bd TWAMR $bc TWCR $bb TWDR $ba TWAR $b9 TWSR $b8 TWBR $b6 ASSR $b4 OCR2B $b3 OCR2A $b2 TCNT2 $b1 TCCR2B $b0 TCCR2A $8a OCR1BL $8b OCR1BH $88 OCR1AL $89 OCR1AH $86 ICR1L $87 ICR1H $84 TCNT1L $85 TCNT1H $82 TCCR1C $81 TCCR1B $80 TCCR1A $7f DIDR1 $7e DIDR0 $7c ADMUX $7b ADCSRB $7a ADCSRA $79 ADCH $78 ADCL $70 TIMSK2 $6f TIMSK1 $6e TIMSK0 $6c PCMSK1 $6d PCMSK2 $6b PCMSK0 $69 EICRA $68 PCICR $66 OSCCAL $64 PRR $61 CLKPR $60 WDTCSR $3f SREG $3d SPL $3e SPH $37 SPMCSR $35 MCUCR $34 MCUSR $33 SMCR $30 ACSR $2e SPDR $2d SPSR $2c SPCR $2b GPIOR2 $2a GPIOR1 $28 OCR0B $27 OCR0A $26 TCNT0 $25 TCCR0B $24 TCCR0A $23 GTCCR $22 EEARH $21 EEARL $20 EEDR $1f EECR $1e GPIOR0 $1d EIMSK $1c EIFR $1b PCIFR $17 TIFR2 $16 TIFR1 $15 TIFR0 $0b PORTD $0a DDRD $09 PIND $08 PORTC $07 DDRC $06 PINC $05 PORTB $04 DDRB $03 PINB ( ----- 020 ) SMS PS/2 controller (doc/hw/z80/sms) To assemble, load the AVR assembler with AVRA, then "324 342 LOADR". Receives keystrokes from PS/2 keyboard and send them to the '164. On the PS/2 side, it works the same way as the controller in the rc2014/ps2 recipe. However, in this case, what we have on the other side isn't a z80 bus, it's the one of the two controller ports of the SMS through a DB9 connector. The PS/2 related code is copied from rc2014/ps2 without much change. The only differences are that it pushes its data to a '164 instead of a '595 and that it synchronizes with the SMS with a SR latch, so we don't need PCINT. We can also afford to run at 1MHz instead of 8. cont. ( ----- 021 ) Register Usage GPIOR0 flags: 0 - when set, indicates that the DATA pin was high when we received a bit through INT0. When we receive a bit, we set flag T to indicate it. R16: tmp stuff R17: recv buffer. Whenever we receive a bit, we push it in there. R18: recv step: - 0: idle - 1: receiving data - 2: awaiting parity bit - 3: awaiting stop bit cont. ( ----- 022 ) R19: Register used for parity computations and tmp value in some other places R20: data being sent to the '164 Y: pointer to the memory location where the next scan code from ps/2 will be written. Z: pointer to the next scan code to push to the 595 ( ----- 024 ) 18 CONSTS $0060 SRAM_START $015f RAMEND $3d SPL $3e SPH $11 GPIOR0 $35 MCUCR $33 TCCR0B $3b GIMSK $38 TIFR $32 TCNT0 $16 PINB $17 DDRB $18 PORTB 2 CLK 1 DATA 3 CP 0 LQ 4 LR $100 100 - CONSTANT TIMER_INITVAL \ We need a lot of labels in this program... 5 VALUES L4 L5 L6 L7 L8 ( ----- 025 ) FLBL, L1 \ main FLBL, L2 \ hdlINT0 \ Read DATA and set GPIOR0/0 if high. Then, set flag T. \ no SREG fiddling because no SREG-modifying instruction ' RJMP L2 TO, \ hdlINT0 PINB DATA SBIC, GPIOR0 0 SBI, SET, RETI, ( ----- 026 ) ' RJMP L1 TO, \ main R16 RAMEND <<8 >>8 LDI, SPL R16 OUT, R16 RAMEND >>8 LDI, SPH R16 OUT, R18 CLR, GPIOR0 R18 OUT, \ init variables R16 $02 ( ISC01 ) LDI, MCUCR R16 OUT, \ INT0, falling edge R16 $40 ( INT0 ) LDI, GIMSK R16 OUT, \ Enable INT0 YH CLR, YL SRAM_START LDI, \ Setup buffer ZH CLR, ZL SRAM_START LDI, \ Setup timer. We use the timer to clear up "processbit" \ registers after 100us without a clock. This allows us to start \ the next frame in a fresh state. at 1MHZ, no prescaling is \ necessary. Each TCNT0 tick is already 1us long. R16 $01 ( CS00 ) LDI, \ no prescaler TCCR0B R16 OUT, DDRB CP SBI, PORTB LR CBI, DDRB LR SBI, SEI, ( ----- 027 ) LBL! L1 \ loop FLBL, L2 \ BRTS processbit. flag T set? we have a bit to process YL ZL CP, \ if YL == ZL, buf is empty FLBL, L3 \ BRNE sendTo164. YL != ZL? buf has data \ nothing to do. Before looping, let's check if our \ communication timer overflowed. R16 TIFR IN, R16 1 ( TOV0 ) SBRC, FLBL, L4 \ RJMP processbitReset, timer0 overflow? reset \ Nothing to do for real. ' RJMP L1 LBL, \ loop ( ----- 028 ) \ Process the data bit received in INT0 handler. ' BRTS L2 TO, \ processbit R19 GPIOR0 IN, \ backup GPIOR0 before we reset T R19 $1 ANDI, \ only keep the first flag GPIOR0 0 CBI, CLT, \ ready to receive another bit \ We've received a bit. reset timer FLBL, L2 \ RCALL resetTimer \ Which step are we at? R18 TST, FLBL, L5 \ BREQ processbits0 R18 1 CPI, FLBL, L6 \ BREQ processbits1 R18 2 CPI, FLBL, L7 \ BREQ processbits2 ( ----- 029 ) \ step 3: stop bit R18 CLR, \ happens in all cases \ DATA has to be set R19 TST, \ was DATA set? ' BREQ L1 LBL, \ loop, not set? error, don't push to buf \ push r17 to the buffer Y+ R17 ST, FLBL, L8 \ RCALL checkBoundsY ' RJMP L1 LBL, \ loop ( ----- 030 ) ' BREQ L5 TO, \ processbits0 \ step 0 - start bit \ DATA has to be cleared R19 TST, \ was DATA set? ' BRNE L1 LBL, \ loop. set? error. no need to do anything. keep \ r18 as-is. \ DATA is cleared. prepare r17 and r18 for step 1 R18 INC, R17 $80 LDI, ' RJMP L1 LBL, \ loop ( ----- 031 ) ' BREQ L6 TO, \ processbits1 \ step 1 - receive bit \ We're about to rotate the carry flag into r17. Let's set it \ first depending on whether DATA is set. CLC, R19 0 SBRC, \ skip if DATA is cleared SEC, \ Carry flag is set R17 ROR, \ Good. now, are we finished rotating? If carry flag is set, \ it means that we've rotated in 8 bits. ' BRCC L1 LBL, \ loop \ We're finished, go to step 2 R18 INC, ' RJMP L1 LBL, \ loop ( ----- 032 ) ' BREQ L7 TO, \ processbits2 \ step 2 - parity bit R1 R19 MOV, R19 R17 MOV, FLBL, L5 \ RCALL checkParity R1 R16 CP, FLBL, L6 \ BRNE processBitError, r1 != r16? wrong parity R18 INC, ' RJMP L1 LBL, \ loop ( ----- 033 ) ' BRNE L6 TO, \ processBitError R18 CLR, R19 $fe LDI, FLBL, L6 \ RCALL sendToPS2 ' RJMP L1 LBL, \ loop ' RJMP L4 TO, \ processbitReset R18 CLR, FLBL, L4 \ RCALL resetTimer ' RJMP L1 LBL, \ loop ( ----- 034 ) ' BRNE L3 TO, \ sendTo164 \ Send the value of r20 to the '164 PINB LQ SBIS, \ LQ is set? we can send the next byte ' RJMP L1 LBL, \ loop, even if we have something in the \ buffer, we can't: the SMS hasn't read our \ previous buffer yet. \ We disable any interrupt handling during this routine. \ Whatever it is, it has no meaning to us at this point in time \ and processing it might mess things up. CLI, DDRB DATA SBI, R20 Z+ LD, FLBL, L3 \ RCALL checkBoundsZ R16 R8 LDI, ( ----- 035 ) BEGIN, PORTB DATA CBI, R20 7 SBRC, \ if leftmost bit isn't cleared, set DATA high PORTB DATA SBI, \ toggle CP PORTB CP CBI, R20 LSL, PORTB CP SBI, R16 DEC, ' BRNE AGAIN?, \ not zero yet? loop \ release PS/2 DDRB DATA CBI, SEI, \ Reset the latch to indicate that the next number is ready PORTB LR SBI, PORTB LR CBI, ' RJMP L1 LBL, \ loop ( ----- 036 ) ' RCALL L2 TO, ' RCALL L4 TO, LBL! L2 \ resetTimer R16 TIMER_INITVAL LDI, TCNT0 R16 OUT, R16 $02 ( TOV0 ) LDI, TIFR R16 OUT, RET, ( ----- 037 ) ' RCALL L6 TO, \ sendToPS2 \ Send the value of r19 to the PS/2 keyboard CLI, \ First, indicate our request to send by holding both Clock low \ for 100us, then pull Data low lines low for 100us. PORTB CLK CBI, DDRB CLK SBI, ' RCALL L2 LBL, \ resetTimer \ Wait until the timer overflows BEGIN, R16 TIFR IN, R16 1 ( TOV0 ) SBRS, AGAIN, \ Good, 100us passed. \ Pull Data low, that's our start bit. PORTB DATA CBI, DDRB DATA SBI, ( ----- 038 ) \ Now, let's release the clock. At the next raising edge, we'll \ be expected to have set up our first bit (LSB). We set up \ when CLK is low. DDRB CLK CBI, \ Should be starting high now. R16 8 LDI, \ We will do the next loop 8 times R1 R19 MOV, \ Let's remember initial r19 for parity BEGIN, BEGIN, PINB CLK SBIC, AGAIN, \ Wait for CLK to go low PORTB DATA CBI, \ set up DATA R19 0 SBRC, \ skip if LSB is clear PORTB DATA SBI, R19 LSR, \ Wait for CLK to go high BEGIN, PINB CLK SBIS, AGAIN, 16 DEC, ' BRNE AGAIN?, \ not zero? loop ( ----- 039 ) \ Data was sent, CLK is high. Let's send parity R19 R1 MOV, \ recall saved value FLBL, L6 \ RCALL checkParity BEGIN, PINB CLK SBIC, AGAIN, \ Wait for CLK to go low \ set parity bit PORTB DATA CBI, R16 0 SBRC, \ parity bit in r16 PORTB DATA SBI, BEGIN, PINB CLK SBIS, AGAIN, \ Wait for CLK to go high BEGIN, PINB CLK SBIC, AGAIN, \ Wait for CLK to go low \ We can now release the DATA line DDRB DATA CBI, \ Wait for DATA to go low, that's our ACK BEGIN, PINB DATA SBIC, AGAIN, BEGIN, PINB CLK SBIC, AGAIN, \ Wait for CLK to go low ( ----- 040 ) \ We're finished! Enable INT0, reset timer, everything back to \ normal! ' RCALL L2 LBL, \ resetTimer CLT, \ also, make sure T isn't mistakely set. SEI, RET, ( ----- 041 ) ' RCALL L8 TO, \ checkBoundsY \ Check that Y is within bounds, reset to SRAM_START if not. YL TST, IF, RET, ( not zero, nothing to do ) THEN, \ YL is zero. Reset Z YH CLR, YL SRAM_START <<8 >>8 LDI, RET, ' RCALL L3 TO, \ checkBoundsZ \ Check that Z is within bounds, reset to SRAM_START if not. ZL TST, IF, RET, ( not zero, nothing to do ) THEN, \ ZL is zero. Reset Z ZH CLR, ZL SRAM_START <<8 >>8 LDI, RET, ( ----- 042 ) ' RCALL L5 TO, ' RCALL L6 TO, \ checkParity \ Counts the number of 1s in r19 and set r16 to 1 if there's an \ even number of 1s, 0 if they're odd. R16 1 LDI, BEGIN, R19 LSR, ' BRCC SKIP, R16 INC, ( carry set? we had a 1 ) TO, R19 TST, \ is r19 zero yet? ' BRNE AGAIN?, \ no? loop R16 $1 ANDI, RET, ( ----- 045 ) \ A simple LED blinker on the Arduino Uno \ To test the assembler mechanism. Requires ATMEGA328P. DDRB 5 SBI, PORTB 5 CBI, R16 $05 LDI, \ 1024 prescaler, CS00+CS02 TCCR0B R16 OUT, R1 CLR, \ initialize overflow counter BEGIN, R16 TIFR0 IN, R16 0 ( TOV0 ) SBRS, DUP AGAIN, \ no overflow R16 $01 LDI, TIFR0 R16 OUT, R1 INC, PORTB 5 CBI, R1 7 SBRS, PORTB 5 SBI, \ LED is on AGAIN, ( ----- 050 ) \ Arduino SPI Spitter. See doc/hw/avr/spispit 103 CONSTANT BAUD_PRESCALE \ 9600 bauds at 16 MHz R16 $80 LDI, R17 $04 LDI, CLKPR R16 STS, CLKPR R17 STS, \ x16 R16 BAUD_PRESCALE >>8 LDI, UBRR0H R16 STS, R16 BAUD_PRESCALE <<8 >>8 LDI, UBRR0L R16 STS, R16 $08 LDI, UCSR0B R16 STS, \ TXEN0 R16 CLR, PORTB R16 OUT, R16 $2c LDI, DDRB R16 OUT, \ MOSI+SCK+SS/PB5+PB3+PB2 R16 $53 LDI, SPCR R16 OUT, \ SPE+MSTR+f_osc/128 ZH 0 LDI, ZL $ff LDI, R1 Z+ LPM, \ number of 0x100 bytes blocks ( ----- 051 ) BEGIN, \ main loop R16 Z+ LPM, SPDR R16 OUT, BEGIN, R16 SPSR IN, R16 7 ( SPIF ) SBRS, AGAIN, BEGIN, R16 UCSR0A LDS, R16 5 ( UDRE0 ) SBRS, AGAIN, R16 SPDR IN, UDR0 R16 STS, ZL TST, ' BRNE SKIP, R1 DEC, TO, R1 TST, ' BRNE AGAIN?, \ end main R16 $00 LDI, UCSR0B R16 STS, \ Disable UART BEGIN, AGAIN, \ end program