Ассемблер и среда разработки Arduino
- Войдите на сайт для отправки комментариев
Вс, 30/12/2012 - 16:45
Господа, возник такой ламерский вопрос. можно ли в скетч вставлять куски ассемблерного кода? к примеру, если есть желание поковыряться с АЦП или еще каким узлом контроллера. если возможноть работы с регистрами напрямую есть в скетчах, то где покурить подробный мануал?
если возможноть работы с регистрами напрямую есть в скетчах, то где покурить подробный мануал?
http://www.atmel.com/Images/doc8161.pdf
спасибо, даташитом на мою мегу 1280 владею, и кое-какой опыт работы в avr studio тоже есть. Вопрос заключается в том, можно ли в теле С/С++ скетча вставлять куски ассемблерного кода? собсно, мануал нужен на более подробное описание языка ардуино, так как список функций, представленный на сайте неполон. к примеру, обратные тригонометрические функции здесь не описаны.
Можно
01
void
setup
()
02
{
03
pinMode(13, OUTPUT);
04
pinMode(12, INPUT);
05
Serial
.begin(115200);
06
}
07
void
loop
()
08
{
09
unsigned
char
reg=26;
10
cli();
11
asm volatile(
"push %0 \n"
12
13
"label_1%=: \n"
14
"COM %0 \n"
15
"out %1, %0 \n"
16
"rjmp label_1%= \n"
17
18
"pop %0 \n"
19
:
"=&r"
(reg)
20
:
"I"
(_SFR_IO_ADDR(PORTB)) ) ;
21
sei();
22
}
спасибо, надо попробовать
а как ассемблеру передать ссылку на массив?
писать можно на чистом асме без всяких извращений с кавычками и подчёркиваниями,просто код..
http://robocraft.ru/blog/981.html
ответ не заставил себя ждать многим более 2-х лет )))
ответ не заставил себя ждать многим более 2-х лет )))
Медаль нашла своего героя )))
Помогите сделать ассемблерную вставку: определить блок 512байт, затем на ассемблере байт АЦП в 1ю и так 512 раз, выскочить. Можно так? Это для пультоскопа.
Курсовики тут не делают.
Помогите сделать ассемблерную вставку: определить блок 512байт, затем на ассемблере байт АЦП в 1ю и так 512 раз, выскочить. Можно так? Это для пультоскопа.
Можно, я разрешаю.
значит сделаешь!
значит сделаешь!
Странная логика. Он разрешает.
Вот ты у учителя спрашиваешь: "Можно выйти?", и он разрешает. Ты думаешь, что курить в сортире он тоже сам будет или ты? ;))
Как вариант... Напиши это на ардуино, посмотри код ассемблера и отдай преподу . Google подскажет как посмотреть листинг кода ардуиновского в асме.
Нету препода...
Могли бы деду и помочь, а не хихикать...
Вставка на ассемблере для родного АЦП не даёт преимущества ...
Как без боли писать на ассемблере в arduino IDE
http://robocraft.ru/blog/981.html
https://embedderslife.wordpress.com/2012/02/19/avr-gcc-asm-and-c/
Нисмотря на то что автор по первой ссылке в названии написал какую-то дичь, статья именно про лучший способ писать на ассемблере в этой среде. Смысл в том что надо создать отдельный файл и писать в нем и затем вызывать по мере надобности.
Могли бы деду и помочь, а не хихикать...
Если ты знаешь ассемблер AVR, то вот, пользуйся без головняков
http://arduino.ru/forum/otvlechennye-temy/znaete-li-vy-shto-assembler-arduino-ide
Помогите сделать ассемблерную вставку: определить блок 512байт, затем на ассемблере байт АЦП в 1ю и так 512 раз, выскочить. Можно так? Это для пультоскопа.
Для ускорения пультоскопа нужно менять МК на 32-битный :). Ассемблер иногда конечно нужен, но не в данном случае. Смотрим:
Опрос АЦП из скетча пультоскопа на СИ:
1
ADMUX = (1<<REFS0)|(1<<ADLAR);
// channel adc0
2
ADCSRB=0;
3
ADCSRA = (1<<ADEN)|(1<<ADSC)|(1<<ADATE) |(1<<ADPS1);
4
for
(
int
i = 0; i < BUFSIZE; i++) {
//for..
5
while
(!(ADCSRA & ADIF));
//wait adif flag
6
ADCSRA |= 0x10;
//clear adif
7
adcBuf[i] = ADCH;
//load to buff
8
}
Дизассемблируем этот код с помощью avr-objdump, справа мои комментарии
01
ldi r24, 0x60 ; r24 = (1<<REFS0)|(1<<ADLAR);
02
sts 0x007C, r24 ; ADMUX=r24
03
sts 0x007B, r1 ; ADCSRB=0
04
ldi r24, 0xE2 ; r24=(1<<ADEN)|(1<<ADSC)|(1<<ADATE) |(1<<ADPS1);
05
sts 0x007A, r24 ; ADCSRA=r24
06
ldi r24, 0x00 ; r25+r24=512
07
ldi r25, 0x02 ; r25+r24=512
08
lds r18, 0x007A ; r18= ADCSRA
09
sbrs r18, 2 ; в цикл если ADIF не установлен
10
rjmp .-8 ; в цикл если ADIF не установлен
11
lds r18, 0x007A ; r18=ADCSRA
12
ori r18, 0x10 ; r18 OR ADIF
13
sts 0x007A, r18 ; ADCSRA =r18
14
lds r18, 0x0079 ; r18=ADCH
15
sbiw r24, 0x01 ; цикл
for
512--
16
brne .-26 ; в большой цикл если не 0
17
ldi r25, 0x00 ; иначе обнулить адресный регистр
18
ldi r24, 0x00 ; иначе обнулить адресный регистр
19
ret
Получаем ассемблерный код практически без излишеств. Т.е. если переписать тот фрагмент на голом ассемблере то выигрыша не будет совсем.
А если 512 раз написать: читать АЦП, писать в ячейку буфера, модифицировать адрес - так моно? Интересно, ск тактов надо АЦП для преобразования?
АЦП медленный (даже на самой высокой скорости) и нет разницы на каком языке писать программу !
uldin@mail.ru, как уже сказал командир - слабое звено тут АЦП, а не алгоритм. Пока АЦП тужится что-б выдать свой очередной семпл -МК за это время успевает отстрочить полсотни тактов. Алгоритм любой медлительности успеет положить взятый ранее байт в буфер, вернуться, и крутиться дальше в цикле ожидая флага готовности. Так что если хочется быстрее - вперёд осваивать ARM :)
Внук помог - написал расчет в массив и загрузка на комп. Осталось быстро-быстро заполнить массив из АЦП в 8266, передать по WiFi на Андроид и получить пультоскоп))
uldin@mail.ru http://arduino.ru/forum/proekty/virtualnyi-ostsillograf
float привести к int, а потом запихать во write()? За какой ассемблер тут речь вообще?
Подпрограмма AddMeasuring() не нужна, Короче и проще
а кто проверит, поддерживает ли компилятор, RAW формат ввода ?
01
asm(R"(
02
дальше какой-то код...
03
TIMER1_COMPA:
04
CLI
05
OUT TCNT1H, Temp0
06
OUT TCNT1L, Temp0
07
CPI Temp3, 255
08
BREQ LeftToRight
09
CPI Temp3, 0
10
BREQ RightToLeft
11
RJMP Timer1Out
12
LeftToRight:
13
LSR Temp2
14
CPI Temp2, 0b00000001
15
BREQ ChangeDirection
16
RJMP Timer1Out
17
RightToLeft:
18
LSL Temp2
19
CPI Temp2, 0b10000000
20
BREQ ChangeDirection
21
RJMP Timer1Out
22
)");
с11 уже включена была такая возможность.
а кто проверит, поддерживает ли компилятор, RAW формат ввода ?
А сам? Если компилятор не заругался- значит поддерживает.
у меня небыло возможности
Проверил, работает.
01
void
loop
() {
02
digitalWrite(LED_BUILTIN, HIGH);
// turn the LED on (HIGH is the voltage level)
03
delay(100);
// wait for a second
04
digitalWrite(LED_BUILTIN, LOW);
// turn the LED off by making the voltage LOW
05
delay(100);
06
07
asm(R"(
08
ldi r16,0x03
09
out
0x05,r16
10
out
0x04,r17
11
)");
но имена портов не понимает PORTB PINB и т.д. . Такие конструкции: ldi r17,(1<<DDB3)|(1<<DDB2)|(1<<DDB1)|(1<<DDB0) - тоже не понимает возможно еще потому что формат assembler'a стоит по умолчанию AT&T а не intel. Надо как-то ключ компилятору отправить и попробовать.
Но это уже лучше чем через запятую все писать. Однако отдельным файлом еще лучше подключать.
Но это уже лучше чем через запятую все писать.
Это о чем Вы ?
Чем это лучше, чем:
asm volatile(
"ldi r16,0x03 \n\t"
"out 0x05,r16 \n\t"
"out 0x04,r17 \n\t"
)
Это о чем Вы ?
Чем это лучше, чем:
asm volatile(
"ldi r16,0x03 \n\t"
"out 0x05,r16 \n\t"
"out 0x04,r17 \n\t"
)
Ты дурак или прикидываешся ? По-моему даже дарак видит разницу,а ты не увидел...
Между твоим венегретом с какой-то левой х-той
asm
volatile(
"ldi r16,0x03 \n\t"
"out 0x05,r16 \n\t"
"ldi r16,0x03 \n\t"
)
и предложенным RAW форматом, безо всякой твоей х.еты с лишними символами в коде в каждой строчке.
Дурака вы каждое утро видите в зеркале !!!
Значит хуже дурака...
Только дураки не знают как пользоваться именами портов и пинов !
difine сделаны только для того что бы быстро переназначить порты/выводы
\n\t вставляются только для того, что бы в листинге было красиво.
01
#define SCL0PORT PORTB
02
#define SCL0PIN PORTB1
03
#define SDA0PORT PORTB
04
#define SDA0PIN PORTB2
05
#define SCL1PORT PORTB
06
#define SCL1PIN PORTB3
07
#define SDA1PORT PORTB
08
#define SDA1PIN PORTB4
09
void
__attribute__ ((noinline)) i2c_init(uint8_t device=0) {
10
asm volatile(
11
"TST %8\n\t"
12
"BRNE i2c_init_1\n\t"
13
"CBI %0-1,%1\n\t"
14
"CBI %2-1,%3\n\t"
15
"CBI %2,%3\n\t"
16
"CBI %0,%1\n\t"
17
"RJMP i2c_init_2\n\t"
18
"i2c_init_1:\n\t"
19
"CBI %4-1,%5\n\t"
20
"CBI %6-1,%7\n\t"
21
"CBI %6,%7\n\t"
22
"CBI %4,%5\n\t"
23
"NOP\n\t"
24
"i2c_init_2:\n\t"
25
::
"I"
(_SFR_IO_ADDR(SCL0PORT)),
"I"
(SCL0PIN),
"I"
(_SFR_IO_ADDR(SDA0PORT)),
"I"
(SDA0PIN),
26
"I"
(_SFR_IO_ADDR(SCL1PORT)),
"I"
(SCL1PIN),
"I"
(_SFR_IO_ADDR(SDA1PORT)),
"I"
(SDA1PIN),
27
"r"
(device)
28
);
29
}
Круто! Вот только ардуины не вижу в последнем сообщении. Может быть можно хоть один ардуиновский оператор вставить? А то как то совсем не по теме форума получается. В отвлечённые темы больше просится.
Могу весь скетч вставить, если интересно ... (Пишем в два i2c девайса на любых пинах под камень 16Мгц с полностью выдержанными интервалами в посылках). В качестве i2c - дисплей SSD1306 128x64.
001
#define SCL0PORT PORTB
002
#define SCL0PIN PORTB1
003
#define SDA0PORT PORTB
004
#define SDA0PIN PORTB2
005
#define SCL1PORT PORTB
006
#define SCL1PIN PORTB3
007
#define SDA1PORT PORTB
008
#define SDA1PIN PORTB4
009
#include <avr/pgmspace.h>
010
void
__attribute__ ((noinline)) i2c_init(uint8_t device=0) {
011
asm volatile(
012
"TST %8\n\t"
013
"BRNE i2c_init_1\n\t"
014
"CBI %0-1,%1\n\t"
015
"CBI %2-1,%3\n\t"
016
"CBI %2,%3\n\t"
017
"CBI %0,%1\n\t"
018
"RJMP i2c_init_2\n\t"
019
"i2c_init_1:\n\t"
020
"CBI %4-1,%5\n\t"
021
"CBI %6-1,%7\n\t"
022
"CBI %6,%7\n\t"
023
"CBI %4,%5\n\t"
024
"NOP\n\t"
025
"i2c_init_2:\n\t"
026
::
"I"
(_SFR_IO_ADDR(SCL0PORT)),
"I"
(SCL0PIN),
"I"
(_SFR_IO_ADDR(SDA0PORT)),
"I"
(SDA0PIN),
027
"I"
(_SFR_IO_ADDR(SCL1PORT)),
"I"
(SCL1PIN),
"I"
(_SFR_IO_ADDR(SDA1PORT)),
"I"
(SDA1PIN),
028
"r"
(device)
029
);
030
}
031
void
__attribute__ ((noinline)) i2c_start(uint8_t device=0) {
032
asm volatile(
033
"SBRS %4,0\n\t"
034
"SBI %0-1,%1\n\t"
035
"SBRC %4,0\n\t"
036
"SBI %2-1,%3\n\t"
037
::
"I"
(_SFR_IO_ADDR(SDA0PORT)),
"I"
(SDA0PIN),
"I"
(_SFR_IO_ADDR(SDA1PORT)),
"I"
(SDA1PIN),
038
"r"
(device)
039
);
040
}
041
void
__attribute__ ((noinline)) i2c_write(uint8_t data, uint8_t device=0) {
042
uint8_t b,i;
043
asm (
044
"LDI %10,8\n\t"
045
"i2c_write_1:"
046
"SBRS %8,0\n\t"
047
"SBI %0-1,%1\n\t"
048
"SBRC %8,0\n\t"
049
"SBI %4-1,%5\n\t"
050
"ROL %9\n\t"
051
"BRCS i2c_write_2\n\t"
052
"SBRS %8,0\n\t"
053
"SBI %2-1,%3\n\t"
054
"SBRC %8,0\n\t"
055
"SBI %6-1,%7\n\t"
056
"RJMP i2c_write_3\n"
057
"i2c_write_2:\n\t"
058
"SBRS %8,0\n\t"
059
"CBI %2-1,%3\n\t"
060
"SBRC %8,0\n\t"
061
"CBI %6-1,%7\n\t"
062
"NOP\n"
063
"i2c_write_3:\n\t"
064
"LDI %11,4\n"
065
"i2c_write_4:\n\t"
066
"DEC %11\n\t"
067
"BRNE i2c_write_4\n\t"
068
"SBRS %8,0\n\t"
069
"CBI %0-1,%1\n\t"
070
"SBRC %8,0\n\t"
071
"CBI %4-1,%5\n\t"
072
"LDI %11,2\n"
073
"i2c_write_5:\n\t"
074
"DEC %11\n\t"
075
"BRNE i2c_write_5\n\t"
076
"DEC %10\n\t"
077
"BRNE i2c_write_1\n\t"
078
"NOP\n\t"
079
"SBRS %8,0\n\t"
080
"SBI %0-1,%1\n\t"
081
"SBRC %8,0\n\t"
082
"SBI %4-1,%5\n\t"
083
"SBRS %8,0\n\t"
084
"CBI %2-1,%3\n\t"
085
"SBRC %8,0\n\t"
086
"CBI %6-1,%7\n\t"
087
"TST %8\n\t"
088
"BRNE i2c_write_6\n\t"
089
"NOP\n"
090
"i2c_write_7:\n\t"
091
"SBIC %2-2,%3\n\t"
092
"RJMP i2c_write_7\n\t"
093
"RJMP i2c_write_8\n"
094
"i2c_write_6:\n\t"
095
"SBIC %6-2,%7\n\t"
096
"RJMP i2c_write_6\n\t"
097
"NOP\n\t"
098
"NOP\n"
099
"i2c_write_8:\n\t"
100
"LDI %11,3\n\t"
101
"i2c_write_9:\n\t"
102
"DEC %11\n\t"
103
"BRNE i2c_write_9\n\t"
104
"SBRS %8,0\n\t"
105
"CBI %0-1,%1\n\t"
106
"SBRC %8,0\n\t"
107
"CBI %4-1,%5\n\t"
108
"LDI %11,3\n"
109
"i2c_write_10:\n\t"
110
"DEC %11\n\t"
111
"BRNE i2c_write_10\n\t"
112
"SBRS %8,0\n\t"
113
"SBI %0-1,%1\n\t"
114
"SBRC %8,0\n\t"
115
"SBI %4-1,%5\n\t"
116
"LDI %11,4\n"
117
"i2c_write_11:\n\t"
118
"DEC %11\n\t"
119
"BRNE i2c_write_11\n\t"
120
"NOP\n\t"
121
"NOP\n\t"
122
::
"I"
(_SFR_IO_ADDR(SCL0PORT)),
"I"
(SCL0PIN),
"I"
(_SFR_IO_ADDR(SDA0PORT)),
"I"
(SDA0PIN),
123
"I"
(_SFR_IO_ADDR(SCL1PORT)),
"I"
(SCL1PIN),
"I"
(_SFR_IO_ADDR(SDA1PORT)),
"I"
(SDA1PIN),
124
"r"
(device),
"r"
(data),
"r"
(b),
"r"
(i)
125
);
126
}
127
void
__attribute__ ((noinline)) i2c_stop(uint8_t device=0) {
128
uint8_t i;
129
asm volatile(
130
"SBRS %8,0\n\t"
131
"SBI %2-1,%3\n\t"
132
"SBRC %8,0\n\t"
133
"SBI %6-1,%7\n\t"
134
"LDI %9,6\n"
135
"i2c_stop_1:\n\t"
136
"DEC %9\n\t"
137
"BRNE i2c_stop_1\n\t"
138
"SBRS %8,0\n\t"
139
"CBI %0-1,%1\n\t"
140
"SBRC %8,0\n\t"
141
"CBI %4-1,%5\n\t"
142
"LDI %9,2\n\t"
143
"i2c_stop_2:\n\t"
144
"DEC %9\n\t"
145
"BRNE i2c_stop_2\n\t"
146
"SBRS %8,0\n\t"
147
"CBI %2-1,%3\n\t"
148
"SBRC %8,0\n\t"
149
"CBI %6-1,%7\n\t"
150
"LDI %9,3\n"
151
"i2c_stop_3:\n\t"
152
"DEC %9\n\t"
153
"BRNE i2c_stop_3\n\t"
154
::
"I"
(_SFR_IO_ADDR(SCL0PORT)),
"I"
(SCL0PIN),
"I"
(_SFR_IO_ADDR(SDA0PORT)),
"I"
(SDA0PIN),
155
"I"
(_SFR_IO_ADDR(SCL1PORT)),
"I"
(SCL1PIN),
"I"
(_SFR_IO_ADDR(SDA1PORT)),
"I"
(SDA1PIN),
156
"r"
(device),
"r"
(i)
157
);
158
}
159
static
const
uint8_t PROGMEM init_bytes[]={0x3C<<1,0x00,0xAE,0xD5,0x80,0xA8,0x1F,0xD3,0x00,0x40,0x8D,0x14,0x20,0x00,0xA0,0xC0,
160
0xDA,0x02,0xD9,0xF1,0xDB,0x40,0x21,0x00,0x7f,0x22,0x00,0x03,0xA4,0xA6,0xAF};
161
static
const
uint8_t PROGMEM impreza[] = {
162
0xF8, 0xF8, 0x00, 0x00, 0xF8, 0xF8, 0x38, 0x70, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
163
0x80, 0xC0, 0xE0, 0x70, 0x38, 0xF8, 0xF8, 0x00, 0x00, 0xF8, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18,
164
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x38, 0xF0, 0xE0, 0x00, 0x00, 0xF8, 0xF8,
165
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x38, 0xF0,
166
0xE0, 0x00, 0x00, 0xE0, 0xF0, 0x38, 0x18, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
167
0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
168
0x18, 0x98, 0x98, 0xD8, 0xD8, 0x78, 0x78, 0x38, 0x38, 0x18, 0x18, 0x00, 0x00, 0xE0, 0xF0, 0x38,
169
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x38, 0xF0, 0xE0,
170
0x1F, 0x1F, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0E, 0x1C, 0x0E, 0x07,
171
0x03, 0x01, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x06, 0x06, 0x06,
172
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x03, 0x01, 0x00, 0x00, 0x1F, 0x1F,
173
0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0E, 0x1F, 0x1B,
174
0x11, 0x00, 0x00, 0x07, 0x0F, 0x1C, 0x18, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
175
0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x00, 0x00, 0x18, 0x18, 0x1C, 0x1C, 0x1E, 0x1E, 0x1B, 0x1B,
176
0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x1F, 0x1F, 0x00,
177
0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1F, 0x1F};
178
void
setup
() {
179
i2c_init();
180
i2c_start();
181
for
(uint8_t i=0;i<
sizeof
(init_bytes);i++) i2c_write(pgm_read_byte(init_bytes+i));
182
asm volatile (
"nop\n\t"
"nop"
);
183
i2c_stop();
184
i2c_start();
185
i2c_write(0x3C<<1);
186
asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop"
);
187
i2c_write(0x40);
188
asm volatile (
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop"
);
189
}
190
void
loop
() {
191
for
(uint8_t i=0;i<128;i++) i2c_write(0x00);
192
for
(uint8_t i=0;i<128;i++) i2c_write(pgm_read_byte(impreza+i));
193
for
(uint8_t i=0;i<128;i++) i2c_write(pgm_read_byte(impreza+i+128));
194
for
(uint8_t i=0;i<128;i++) i2c_write(0x00);
195
while
(1) {};
196
}
Только дураки не знают как пользоваться именами портов и пинов !
Так это же Серёжа Селево! Хочет всё и сразу.) Он безобидный парень, только ругается иногда.)))
и предложенным RAW форматом, безо всякой твоей х.еты с лишними символами в коде в каждой строчке.
Ну так покажите нам как в вашей не х.ете (и она не моя, а разработана GCC для не скудоумных) обращаться к переменным, константам, ..., ... ?
Ты оказывается не только глуп но еще и ленив, тут два раза давали ссылки на статьи в которых описано как происходит взаимодействие Си-ассемблер в функциях и переменных
Ну вы же подняли тему RAW формата, а он про inline вставки на ассемблере. Чего завиляли то сразу ??? Нет сил/ума освоить несколько страниц - https://web.stanford.edu/class/ee281/projects/aut2002/yingzong-mouse/media/GCCAVRInlAsmCB.pdf