Makra v assambleru

Pri psaní maker v assambleru AVRA (Version 1.2.3 Build 1 (15. November 2007)) jsem narazil na problém při používání jejich parametrů. Vysvětlím na shematickém příkladu:

.makro makro1
;pro makro1 se zadává několik paramterů (@0@3)

nějaký kód

makro2

další kód

.endmakro
*

Je možné nějakým způsobem používat parametry makra * makro1* i v makro2 jako “Immediate” hodnoty?

Díky za jakoukoli inspiraci.

Popiš podrobněji nějaký konkrétní příklad.

Vytvářím si sadu maker na práci s TIMERem1. Lze ho provozovat v mnoha různých režimech. Vytvořím si tedy pro každý režim jedno makro např:

timer1_pfc_icr; Phase and Frequency Correct PWM Mode, Top hodnota v ICR1 registru

nebo,

timer1_pc_ocr; Phase Correct PWM Mode, Top hodnota v OCR1A registru

Parametry makra určují konkrétní nastavení jednotlivých registrů:

;@0 - TOP hodnota
;@1 - hodnota pro OCR1A
;@2 - hodnota pro OCR1B
;@3 - výstup A (off, toggle, normal, revers)
;@4 - výstup B (off, toggle, normal, revers)

Samotné makro pak vypadá tak, že nejdřív nastavím registry TCCR1 A a TCCR1B a pak registry ICR1 a OCR1A a OCR1B.
Nastavování posledních třech, resp. hldavně dvou registrů, se ale ve všech makrech do zblbnutí opakuje, činí kód zbyteně dlouhým a méně přehledným. Proto jsem je chtěl vyřešit dalším makrem a to takto:

;nastavení TCCR1A a TCCR1B

ldi temp, (0<<com1a0)|(@4<<com1b0)|(1<<WGM11)|(1<<WGM10)
sts TCCR1A, temp
ldi temp, (1<<WGM13)|(0<<WGM12)
sts TCCR1B, temp

;nastavení OCR1A a OCR1B
nastav_ocr_registry
*

a právě s makrem použitým zcela na konci pířkladu mám problém. Muselo by totiž používat parametry @1 a @2 z makra, ve kterém je použité a to podle všeho nejde (AVRA mi tvrdí, že nejsou k dispozici potřebné parametry - což je v podstatě pravda)

Takže já bych potřeboval makru nastav_ocr_registry vnutit parametry příslušných maker, ve kterých je použito. Jde to nějak?

Nějak tomu nerozumím.
K čemu chceš to makro použít?
Pokud k inicializaci pwm módu, tak proč v něm nastavovat OCR registry?
Ty se přece mění v hlavní smyčce např podle hodnoty ADC apod.

Nebo chceš v hlavní smyčce pro zápis do OCR používat celé to makro místo jednoduchého zápisu hodnoty? Doufám že ne.

Podle mne je rozumné použít takové makro pouze pro inicializaci.
A jen s nutnými hodnotami, třeba takto:

[code]// avrasm2
.include “m88def.inc”

.def temp = r16

; @0 = 0 - non-inverting mode, kanál A
; @0 = 1 - inverting mode, kanál A
; @1 = 0 - non-inverting mode, kanál B
; @1 = 1 - inverting mode, kanál B

.macro pwm_mod14
ldi temp, (1<<WGM11)|(1<<COM1A1)|(@0<<COM1A0)|(1<<COM1B1)|(@1<<COM1B0)
sts TCCR1A, temp
ldi temp, (1<<WGM12)|(1<<WGM13)|(1<<CS10) // prescaler = 1
sts TCCR1B, temp
.endmacro

.org 0
rjmp init

init:

// init ports
sbi DDRB, 1 // pin OC1A output

// init timer1, fast pwm mode 14, top = ICR1, non-inverting
pwm_mod14 0, 0

// nastav frekvenci pwm - např ICR1 = 40000
ldi temp, high(40000)
sts ICR1H, temp
ldi temp, low(40000)
sts ICR1L, temp

mainloop:

// tady měníme OCR registry

rjmp mainloop
[/code]

Nerozumíš, protože jsi stále nepochopil moji filozofii. Pokusím se Ti to tedy vysvětlit ještě detailněji.
Makra samozrřejmě budou sloužit pouze k inicializaci TIMERu1.

Proč nastavovat registry OCR1x při inicializaci? Protože timer často používám takovým způsobem, že si (OCR1x registry) nastavím jen jednou. Např. pokud je používám ke generování sinchronizačních impulzů pro LCD zobrazovač.
Proto jsem tahle makra taky původně začal psát. Přestalo mě totiž bavit nastavovat to (Timery) vždycky znova podle datasheetu. Rozhodnul jsem se tedy napsat si sadu “dokonale” univerzálních a maximálně komplexních maker. Která budou tuhle práci dělat za mě.
Vycházel jsem při tom z předpokladu, že se použijí vždycky jen jednou, při inicializaci a tudíž není nutné aby fungovala vysloveně efektivně. Důležité ale je, aby uměla nastavit, pokud možno, všechno sama a snadno. (Mám je napsaná tak, že si sama nastavují i příslušné piny na portech jako “output”)

A teť tedy spátky k tomu makru uvnitř makra. Jde mi o to, že pokud velké množství maker používá jednu a tu samou část kódu pak je výhodnější, když je ta část v příslušném souboru s makry napsaná jen jednou - pro případ, že bych ji upravoval, nebo k ní něco přidával. Je lepší dělat úpravy na jednom místě, než to přepisovat v každém makru zvlášť A to v mém případě platí jak pro nastavování OCR1x registů, tak pro nastavování pinů jako výstupy. V každém makru se opadkují a já nenalézám způsob, jak je v tom souboru s makry mít jen jednou. Problém spočívá v tom, že ty části kódu, které se neustále opakují, využívají parametry maker, kterých jsou součástí - tudíž z níich nemůžu udělat makra (nebo možná můžu ale nevím jak)

Možná existuje i jiné řešení než přes makra v makrech, ale mě žádné takové nenapadá a proto jsem se začal ptát.

Půjde to, když uložíš argumenty do registrů
a ty pak použiješ ve vnořeném makru.

[code].macro nastav_ocr_registry
out OCR1AL, r20
out OCR1AH, r21
out OCR1BL, r22
out OCR1BH, r23
.endmacro

.macro pwm
;@0 - TOP hodnota
;@1 - hodnota pro OCR1A
;@2 - hodnota pro OCR1B
;@3 - výstup A (off, toggle, normal, revers)
;@4 - výstup B (off, toggle, normal, revers)

;nastavení TCCR1A a TCCR1B

ldi temp, (0<<com1a0)|(@4<<com1b0)|(1<<WGM11)|(1<<WGM10)
sts TCCR1A, temp
ldi temp, (1<<WGM13)|(0<<WGM12)
sts TCCR1B, temp

;nastavení OCR1A a OCR1B
ldi r20, low(@1)
ldi r21, high(@1)
ldi r22, low(@2)
ldi r23, high(@2)

nastav_ocr_registry

.endmacro
[/code]

Ve Tvojí odpověďi čtu dvě zprávy, jednu doboru a jednu špatnou.

Ta dobrá je, že mě stejné řešení taky napadlo (zdá se, že jsem občas schopen vymyslet podobná řešení, jako mnohe zkušenější programátoři… a to mě těší :smiley: )

Ta špatná je, že: tohle řešení je, z mého pohledu, tak trošku kočkopes. Já pořád doufal, že existuje nějaká (mě neznámá) vychytávka, jak ty argumenty použít ve vnořeném makru jako “immediate” hodnoty a nemuset je ukládat do pracovních registrů.

Připouštím, že při ukládání hodnot do OXR1x rexistrů je to více-méně kosmetická záležitost , která “jen” trošku zkrátí a zpřehlední kód, ale jsou i jiné situace, kde bych to (hlavně při ladění programu) uměl plně využítl.

No nevadí, infomace, že to nejde (a musí se to řešit přes pracovní registry) je taky důležitá informace, takže já Ti tímto děkuji za trpělivost a odpovědi.

Předávání argumentů přes registry je věc běžně používaná např. při volání funkcí.

Jestli se ti to zdá škaredé, tak můžeš vnořenému makru předat původní argumenty.
Ale mně se zdá první způsob přehlednější.

[code].macro nastav_ocr_registry
ldi temp, low(@0)
out OCR1AL, temp
ldi temp, high(@0)
out OCR1AH, temp
ldi temp, low(@1)
out OCR1BL, temp
ldi temp, high(@1)
out OCR1BH, temp
.endmacro

.macro pwm
;@1 - hodnota pro OCR1A
;@2 - hodnota pro OCR1B

nastav_ocr_registry @1,@2
.endmacro
[/code]

Já bych zrovna netvrdil, že je to škaredé, jsou situace, kde je to určitě z různých důvodů výhodnější a přehlednější, než jiné spůsoby zápisu. V tomhle konkrétním případě mi to přehlednější nepřišlo. To ale záleží na tom, jakou kdo máme představu o přehlednosti; v tomhle směru jsme každý unikát. Nejspíš mi nezbyde nic jiného, než se tomu přizpůsobit a řešit to taky tak.

Co se Tvojí ukázky kódu týče: bylo by to také řešení, ale, pokud jsem to správně pochopil, musel bych před každým použitím makra “pwm” otevřít soubor , ve kterém je uložené a přepsat parametry @1 a @2 . Tím se ale vytrácí požadavaná jednoduchost jeho použití. To už by bylo rozhodně lešpí předat ty argumenty přes pracovní registry.
Uznávám, že předávání argumentů přes pracovní registry je pro mě více-méně estetickou záležitostí a (v situaci, kdy nejsou kladeny žádné zvláštní nároky na rychlost, s jakou se kód vykonává) v podstatě nepředstavuje problém.

Pokud chceš syntakicky krátkej kód, přejdi na C :wink:

Nevím co tím myslíš. Jaké přepisování?
Použití makra “pwm” je úplně stejné v obou případech.
Parametry @1 a @2 zadáš při volání makra “pwm”.
Pak se předají vnořenému makru.
V tom vnořeném makru mají samozřejmě hodnoty @0 a @1 (je to první a druhý argument makra “nastav_ocr_registry”).

Tady je kompletní kód, můžeš si ho krokovat v simulátoru,

// avrasm2
.nolist
.include "m8def.inc"  
.list
.listmac

.def temp = r16

.org 0
rjmp mainloop

.macro nastav_ocr_registry
   ldi temp,   low(@0)
   out OCR1AL, temp  
   ldi temp,   high(@0)
   out OCR1AH, temp
   ldi temp,   low(@1)
   out OCR1BL, temp 
   ldi temp,   high(@1)
   out OCR1BH, temp  
.endmacro

.macro pwm 
   ;@0 - TOP hodnota
   ;@1 - hodnota pro OCR1A
   ;@2 - hodnota pro OCR1B
   ;@3 - výstup A (off, toggle, normal, revers)
   ;@4 - výstup B (off, toggle, normal, revers) 

   nastav_ocr_registry @1,@2
.endmacro


mainloop:

   pwm 0,0x3322,0x5544,0,0 // OCR1A=0x3322, OCR1B=0x5544

rjmp  mainloop

Promiň, trošku špatně jsem pochopil Tvůj zápis. Když jsi napsal:

nastav_ocr_registry @1,@2 

myslel jsem, že tím myslíš, že se ty parametry zadávají u makra “nastav_ocr_registry”.

Pokud se Tvého posledního příkladu týče, tak přesně tohle jsem původně skoušel a přesně tohle mi AVRA odmítnul skousnout. Dostal jsm vynadaný, že:

Missing macro argument (for @1)
Missing macro argument (for @2)

Avra zřejmě testuje přítomnost argumentů jëště před tím, než makra “sesadí” dohromady s ostatním kódem, tím pádem mu ve vnořeném makru chybějí argumenty (teda alespoň tak si to vysvětluju já). To by ale znamenalo, že některé jiné assamblery s tím problém nemají, jaký assambler používáš Ty?

Od C jsem znechuceně odešel. Uplně mě drtí, když mikroprocesor něco dělá a já nevím co. Nechci, aby to tu kdokoli pochopil jako útok na Cčkaře a tohle vlákno se změnilo v další nesmyslnou diskusi na téma: “je lepší Cčko nebo assambler?” Kdo chce používat Cčko, ať ho klidně používá, nic proti tomu nemám; jen jsem vysvětlil, proč ho nepoužívám já.

Avrasm2 (součást AvrStudia).

Chápu Tvůj postoj, taky jsem hodně let dělal jen v asm, ale s postupem času už se to nedalo. Kód byl moc složitej a nepřehlednej. Po dalších cca 10 letech práce v C už bych neměnil. V asm už píšu jen nějaký hacky nebo krátký optimalizovaný rutiny, který stejně volám z C.

Při psaní větších, rozsáhleších, kódů asi nejlepší způsob; využívat výhody Cčka v kobinaci s možnostmi assambleru.

Já jsem ale jen radioamatér, který potřebuje občas něco vyřešit pomocí mikroprocesoru, za využití relativně krátkého kódu a často mi jde o přesné časování. Takže pro mě je assambler zkrátka výhodnější.

Ze Tvých příspěvků jsem pochopil, že automaticky předpokládáš, že používám AVRStudio…nepoužívám. Pracuju v prostředí Linuxu. Ke psaní kódu používám textový editor a k převodu do .hex a následnému programování mikroprocesoru si vystačím s příkazovou řádkou (mi kluci vychovaný drsnými Krkonošskými podmínkami si vystačíme s málem :wink: )

AVRA by měl být plnohodnotnou náhradou AVRASM, ale podle všecho není, alespoň teda ne ve všem. Existuje i AVRASM pro linux, ale ten makra nepodporuje vůbec. Pak je pro linux ještě GAVRASM, ale ten jsem zase nenašel zkompolovaný tak, aby šel rozchodit na 32bitových procesorech. Nakonec jsem ale našel způsob, jak svůj problém vyřešit v AVRA.
Následující řádky napíšu jako inspiraci pro další linuxáky, kteří používají AVRA. Uživatelé Windows a AVRStudia je mohou v klidu ignorovat.

Takže, jak by řekl klasik: “Selže-li všecko, přečtěte si návod!”. Po prostudování možností všech možný i nemožných direktiv jsem narazil na .EQU.

.macro nastav_ocr_registry; vnořené makro, které nastaví OCR1x registry

ldi temp, high(_ocr1a)
sts OCR1AH, temp
ldi temp, low(_ocr1a)
sts OCR1AL, temp

ldi temp, high(_ocr1b)
sts OCR1BH, temp
ldi temp, low(_ocr1b)
sts OCR1BL, temp

.endmacro

.macro timer1_pc_8bit; makro nastavující pracovní režim TCNT1 na "8bit phase correct"

;@0			- na hodnotě nezáleží (0)
;@1			- hodnota pro OCR1A
;@2			- hodnota pro OCR1B
;@3			- výstup A (off, toggle, normal, revers)
;@4			- výstup B (off, toggle, normal, revers)

.equ _top = @0
.equ _ocr1a = @1
.equ _ocr1b = @2
.equ _OC1A = @3
.equ _OC1B = @4

ldi temp, (_OC1A<<com1a0)|(_OC1B<<com1b0)|(0<<WGM11)|(1<<WGM10)
sts TCCR1A, temp
ldi temp, (0<<WGM13)|(0<<WGM12)
sts TCCR1B, temp

nastav_ocr_registry

; další část kódu je propochopení principu již nepodstatná

.endmacro

Pointa spočívá v tom, že si v příslušném makru, které nastavuje TCNT “převedu” argumenty makra na proměnné definovamé direktivou .EQU (zřejmě by to fungovalo i s direktivou .SET, ale to jsem ještě nezkoušel). Pak už s nimi pracuju jako s obyčejnými proměnnými a není tak problém je používat ve vnořených makrech.

Jde to udělat i tak, že se tyhle proměnné vytvoří už na začátku programu a makra se pak používají zcela bez argumentů (u rozsáhlejších kódů s velkým počtem maker to pak ale bude asi trošku nepřehledné)

aa