Montag, 30. Januar 2012

Automatische variable Arrays


Ich habe kürzlich etwas Neues über die Programmiersprache C gelernt. Bisher war ich immer der Ansicht, dass Arrays, deren Größe zur Kompilierzeit nicht feststehen, auf dem Heap allokiert (->malloc) werden müssen. Seit ISO C99 bietet der Sprachumfang variable automatische Arrays, die wie alle automatischen Variablen auf dem Stack liegen.

Folgende Funktion

void func(uint a) { 
uint size=a+123;   
int array[size];   
}                   

ist gültig. Natürlich kann es hier zum Stacküberlauf kommen, es sollte daher eher mit etwas kleineren Feldern gearbeitet werden. Auf jeden Fall handelt man sich so kein Speicherleck ein, da das Array nach Funktionsende wieder freigegeben wird. Auch die Allokierung auf dem Stack ist schneller als auf dem Heap, da der Stackpointer dazu nur um die Arraygröße erhöht werden muss.

Weitere Infos findet man in den GCC-Docs. In diesem Zusammenhang möchte ich auch noch auf alloca() hinweisen. Diese Funktion allokiert ebenfalls ein variables Array auf dem Stack, ist aber scheinbar nicht ISO-C sondern eine BSD-Extension.

Der Unterschied zwischen alloca() und einem automatischen variablem Array liegt in der Gültigkeit des Speichers:
Der mit alloca() reservierte Speicher wird für die Laufzeit seiner Funktion belegt, das variable Array ist gültig innerhalb seiner Klammerebene.

Donnerstag, 5. Januar 2012

AY-3-8912

Ich habe es nun endlich geschafft, dem AY-3-8912 ein paar Töne zu entlocken. Der Baustein ist offensichtlich sehr Timing-empfindlich, z.B. müssen innerhalb von 50ns die Bus-Steuerleitungen ge-setup't sein. Da ich die Busleitungen mit nem Mikrocontroller gesteuert habe, muss man schon überlegt verdrahten und programmieren  :-)

Der Chip wird mit einem externen Takt im Bereich 1-2 MHz getaktet. DA0-DA7 sind Datenbus und Adressbus, die sind gemultiplext. BC1 und BDIR sind die Bussteuerleitungen, die das kritische Timing erfordern. A8 und BC2 sind eine Art Chipselect und können fest auf +5V gelegt werden. CH_A-CH_C sind die drei Audiokanäle, die man einfach stumpf verbinden und in einen Kopfhörer einspeisen kann. IOA0-IOA7 ist ein digitaler "User-Port".

Der AY-3-8912 besitzt 16 interne Register. Um diese Register zu beschreiben/zu lesen, gibt es diesen prinzipiellen Ablauf:
Zum Schreiben versetzt man den Bus in den Inaktiven Zustand, dann wird die Adresse des zu selektierenden Registers auf den Bus DA0-DA7 gelegt und der Bus in den Latch-Zustand und zurück in den Inaktiven Zustand versetzt. Beim Übergang in den Inaktiven Zustand wird die Registeradresse gelatcht. Nun wird der in das Register zu schreibende Wert auf den Datenbus gelegt, in den Write-Zustand und zurück in den Inaktiven Zustand gewechselt. Nun wird der Wert in des Register geschrieben.

Den Buszustand legt man mit BDIR und BC1 fest. Der Inaktive Zustand wird durch BDIR=low und BC1=low gesetzt. Zuerst hatte ich BDIR an Portpin RB5 und BC1 an Portpin RE3 des dsPICs angebunden. Wenn man dann mit

_LATB5=0; 
_LATE3=0; 

den Bus steuern will, geht das schief, da jede Anweisung deutlich länger als 50ns benötigt, zumal der dsPIC auch nur mit 8MHz internen Takt ohne PLL läuft. Der Pegelwechsel selber von 0V auf 5V dauert ca. 7ns, das ist also kein Problem. Es müssen nur beide Pins gleichzeitig wechseln, um die 50ns nicht zu überschreiten. Also müssen beide Pins auf dem selben Port liegen. Um dann beide Pins gleichzeitig zu schalten, kann man dann das gesamte Portregister schreiben:

LATE = LATE & 0xFFFC;

Hier werden beim Port E die ersten beiden Bits gleichzeitig genullt. Möchte man ein Bit nullen, aber das andere "einsen", muss man einen Zwischenschritt einlegen:

LATE = (LATE & 0xFFFC) | 0x01;

So wird nur das zweite Bit genullt, das erste Bit gesetzt.

Den AY-3-8912 kann man auf diese Weise an den Z80 anbinden:
/AY_CS ist das Chipselect des Z80. Mit der untersten Adressleitung A0 des Z80 kann man so bestimmen, ob in das Adresslatch des AY-3-8912 geschrieben werden soll oder in eines der dadurch adressierten Register.

Versuchsaufbau