Quick-Tips

Cache oder nicht Cache

Die Flaggschiffe der ATARI-Reihe TT und Falcon030 beherbergen beide den leistungsstarken Motorola-Prozessor MC68030, der von haus aus mit 2*256 Bytes internem Cache ausgestattet ist. Ein Cachespeicher dient zum Zwischenlagern von häufig benutzten Daten, der andere zur schnellen Verfügbarkeit von Prozessor-Instruktionen, wie z.B. kleine Schleifen, die komplett in die 256 Bytes passen. Die Caches sind in 16 Zeilen ä 4 Langwörter aufgeteilt, die zeilenweise verwaltet und im sogenannten Burst-Modus auch zeilenweise gefüllt werden, d.h es werden 4 aufeinanderfolgende Langwörter mit einem Schlag übertragen. Im Sing-le-Entry-Modus hingegen werden die Langwörter einzeln bearbeitet, was natürlich entsprechend langsamer aber in manchen Fällen wesentlich vorteilhafter ist, weil unter umständen die gewünschten Daten im Speicher verteilt sind. Der Burst-Modus würde hier die Cache-Effizienz drastisch verkleinern. Nun ist es möglich für jeden der beiden Caches einige Einstellungen vorzunehmen, die man nur im Supervisor-Modus der CPU durchführen kann. Zu diesem Zweck existiert ein neues 32 Bit Register im Prozessor mit Namen CACR, das wie folgt aufgebaut ist:

CACR:

BitNr:      31....14 13 12 11 10  9  8 7 6 5  4  3  2  1  0
Bedeutung:  00....00 WA DB CD CE FD ED 0 0 0 IB CI CE FI EI

WA=Write Allocate

Bei gelöschtem Bit werden die Daten, die der Prozessor schreibt nicht im Cache neu angelegt (dupliziert), sondern gehen direkt nach draußen. Bei gesetztem Bit werden die zu schreibenden Daten zusätlich im Datencache abgelegt. Der lesende Zugriff mit eventuellem Neueintragen wird hingegen nicht beeinflußt.

DB=Data Burst Enable

Ein gesetztes Bit sorgt dafür, daß der Datencache im Burst-Modus gefüllt wird.

CD=Clear Data Cache

Ein setzen dieses Bits bewirkt das löschen des Datencaches.

CE=Clear Entry

Im Gegensatz zu CD bewirkt das setzen dieses Bits nur das löschen eines einzelnen Eintrags im Datencache. Die Auswahl dieses Eintrags erfolgt über das 32 Bit Cache-Adreß-Register CAAR in dem die Bits 2-7 den entsprechenden Eintrag adressieren.

FD=Freeze Data Cache

Ein gesetztes Bit signalisiert, daß beim Lesen oder Schreiben der Cacheinhalt nicht aktualisiert werden soll, wenn der gewünschte Eintrag nicht vorhanden ist. Eine Ausnahme stellt die Aktualisierung eines vorhanden Eintrags beim Schreiben dar, damit es für einen physikalischen Speicherplatz nicht 2 Einträge gibt. Ansonsten wird die beschleunigende Funktion des Caches nicht beeinträchtigt.

ED=Enable Data Cache

Wenn dieses Bit gelöscht ist, ist der Datencache absolut inaktiv. Bei erneutem Einschalten, ist der alte Zustand vor dem Abschalten immer noch gültig, es wurde also nichts gelöscht. Diese Bit kann man zu Debug-Zwecken oder Emulationen verwenden, da hier jeder Zugriff nach außen geht.

Für die Instruction Cache-Flags sind die Bedeutungen analog:

IB=Instruction Burst Enable

CI=Clear Instruction Cache

CE=Clear Entry

FI=Freeze Instruction Cache

Da es im Instruction Cache kein Schreibzugriff geben kann, fällt hier kein WA-Register an und im Eingefrorenen Zustand werden dementsprechend auch keine Einträge aktualisiert sondern nur ausgelesen.

EI=Enable Instruction Cache

Um also beide Caches anzuschalten und im Burst-Modus zu betreiben reichen ein paar einfache Assemblerbefehle, die im Supervisor-Modus ausgeführt werden müssen:

moved #$1111,d0
movec d0,CACR

Oder das Löschen der beiden Caches:

movec   CACR,d0   ;behalte alten Status
or.l    #$808,d0 ;und ändere 2 Bits ab
movec   d0,CACR

Natalie Lübcke

68000er Multipiikation/Division in C

Es kommt oft vor, daß man 16 Bit-Integer-Multiplikationen und -Divisionen verschachteln muß. Die Zwischenergebnisse sind in der Regel größer als 16 Bit (int, short) und so ist jedesmal eine Konvertierung nach 32 Bit (long) notwendig. Als Beispiel diene die Slider-Positionierung in einer Fensterverwaltung:

Sliderposition = (int)Position*1000 / (int)Maximum;

Für viele C-Compiler ist dies unmöglich, da es bereits bei der ersten Multiplikation sehr schnell zum Überlauf kommt. Es müßte dann heißen:

Sliderposition = (int)((long)Position*1000L/(long)Maximum )

Für den M68000er Prozessor ist dies jedoch kein Problem: Das Produkt einer Multiplikation und der Divident einer Division dürfen 32 Bit breit sein, solange beide Faktoren, Divisor und Quotient in 16 Bit passen. Ab dem MC68020 gibt es diese Befehle auch für 32 Bit, mit der Möglichkeit auf 64 Bit zu erweitern. Eine Lösung wäre die Benutzung von Inline-Routinen. Für Lattice C würde dies folgendermaßen aussehen:

long imul(short,short);
#pragma inline d0=imul(d0,d1) {"C1C1";} // muls.w d1,d0 
short idiv(long,short);
#pragma inline d0=idiv(d0,d1) {"81C1";} // divs.w d1,d0 
short imuldiv(short,short,short) ;
#pragma inline d0=imuldiv(d0,d1,d2) {"C1C1";"81C2";} // muls.w d1,d0 divs.w d2,d0

Aus dem bereits benutzten Beispiel wird dann:

Sliderposition = idiv( imul( Position , 1000 ) , Maximum );

bzw.

Sliderposition = imuldiv( Position , 1000 , Maximum );

Der erzeugte Maschinencode ist nicht nur kürzer und somit auch schneller, sondern der Quellcode auch übersichtlicher.

Volker Hemsen

Ergänzung zu "Falcon030 CPU- und Blitter-Takt"

Das Löschen von Bit 5 des Registers 0xFFFF8007 bewirkt, daß der Falcon030 ST(E)-kompatibler wird. Allerdings entspricht dies nicht, wie im obigen Quicktip beschrieben, dem Einstellen des Kompatibilitätsmodus vom Desktop aus, ohne die Auflösung zu ändern. Der vom Desktop aus erreichbare Kompatibilitätsmodus betrifft nur den Grafikmodus, während ein gelöschtes Bit 5 des Registers 0xFFFF8007 einen ST(E)-kompatiblen Busmodus bewirkt. Beim ST(E) waren Zugriffe auf Speicherbereiche, in denen "nichts" (kein RAM, kein ROM und keine Hardwareregister) war, möglich (das TOS nutzte dies, um das verfügbare RAM zu ermitteln). Beim Falcon030 werden solche Zugriffe allerdings, wie zu erwarten, mit einem Busfehler bestraft. Ist der Falcon jedoch durch Bit 5 von 0xFFFFA007 ST(E)-kompatibel geschaltet, verhält sich der Rechner hier wie ein ST(E). Dadurch können viele Spiele, die sich (aus welchem Grund auch immer) auf dieses Busverhalten verlassen haben und daher mit 2 Bomben abgestürzt sind, auch auf dem Falcon weiterbenutzt werden. Es steht zu hoffen, daß ATARI dieses Register noch dokumentiert und/oder einen passenden XBIOS-Aufruf implementiert, damit auch auf zukünftigen Falcon-Modellen alte Spielesoftware benutzt werden kann.

Thomas Binder

GEMDOS-Handles ermitteln

Bekanntlich verwaltet GFA-BASIC Dateien unter eigenen Kennummern (Handles) manchmal kann es aber ganz nützlich sein, die dazugehörigen GEMDOS-Handles zu ermitteln. Mit dem nachfolgendem Listing ist dies möglich.

Es funktioniert mit den GFA-BASIC-Versionen:

Compiler: Interpreter:
3.03 D 3.07D
3.50 E 3.50 D
3.6 TT D 3.6 TT

Christoph Conrad

' (c)1993 by MAXON-Computer 
' Autor: Christoph Conrad
' Ermitteln der Adresse des GFA-internen
' GEMDOS-Handle-Arrays bei OPEN 
' Sei adr% die Adresse des Handle-Arrays.
' Dann steht bei einem 
' OPEN mode$,»iS,name$
' das GEMDOS-Handle als Byte an Adresse (adr%+i&)
' get_channel_adr am besten gleich am Programmanfang 
' auf jeden Fall BEVOR 
' irgendein File eroeffnet wird.
'
channel_adr%=@get_channel_adr
'
' Zur Demonstration mal ein paar Sachen oeffnen 
OPEN "i",#1,"COM:"
OPEN "i",#2,"AUX:"
OPEN "i",#3,"PRN:"
OPEN "i",#88,"INIT.O" ! Irgendein File im aktuellen Verzeichnis einsetzen
'
PRINT "Gemdos-Filehandle COM: ";HEX$(@get_handle(1)) 
PRINT "Gemdos-Filehandle AUX: ";HEX$(@get_handle(2)) ! wie COM:
PRINT "Gemdos-Filehandle PRN: ";HEX$(@get_handle(3)) 
PRINT "Gemdos-Filehandle DIM.GFA: ";HEX$(@get_handle(88))
'
FUNCTION get_handle(channel&)
    ' channel&: wie bei OPEN mode$,#channel&,name$ 
    ' negative GEMDOS-Handles werden positiv zurueckgeliefert!!!
    $F%
    IF channel_adr%<>0
        RETURN BYTE{channel_adr%+channel&}
    ELSE
        RETURN 0 
    ENDIF 
ENDFUNC
'
FUNCTION get_channel_adr 
    $F%
    LOCAL geta6%,pgeta6%,i&,a6%,adr%,offs| 
    ' Register A6 holen
    LET geta6%=&H200E4E75 ! move.l a6,d0 // rts
    pgeta6%=V:geta6% 
    a6%=C:pgeta6%()
    FOR i&=0 TO 79
        ' Bei COM: kein GEMDOS-Fopen!
        OPEN "i",#i&,"COM:"
    NEXT i&
    ' 4 KByte ab a6% nach 80 aufeinanderfolgenden &HFB absuchen...
    FOR adr%=a6% TO a6%+&H1000 
        IF BYTE{adr%}=&HFB 
            FOR offs|=1 TO 79
                EXIT IF BYTE{adr%+offs|}<>&HFB 
            NEXT offs| 
            IF offs|=80 
                ' Erfolg!
                CLOSE
                RETURN adr%
            ENDIF 
        ENDIF 
    NEXT adr%
    ' Nichts gefunden...
    CLOSE 
    RETURN 0 
ENDFUNC


Links

Copyright-Bestimmungen: siehe Über diese Seite