Assembler-Ecke #19 -------------------- von Tim-Philipp Müller Wer schon einmal versucht hat, einfache Ein- und Ausgaben über die CIO, die in BASIC ein paar Buchstaben in Anspruch nehmen, in Assembler zu programmieren, weiß wie mühselig das ist. Doch es geht meist auch einfacher als über den herkömmlichen Weg! Dazu schauen wir uns erst einmal an, was die CIO eigentlich macht, wenn wir ihr einen Befehl (z.B.OPEN 0,12,0,"E:") erteilen: Jedes Gerät besitzt einen Gerätetreiber und eine Vektortabelle, die die Adresse aller Funktionen dieses Treiers bein- haltet. Hier die Adressen einiger Tabellen: EDITRV $E400 SCRENV $E410 KEYBDV $E420 PRINTV $E430 CASETV $E440 Ihnen wird aufgefallen sein, daß es sich hier nur um die OS-Treiber handelt. Tja, das Problem ist, daß nur bei diesen sich die Tabelle immer an derselben Adresse befindet. Die Adressse für die 'D:'- Tabelle kann je nach DOS-Version variieren. Deswegen kann man sie sich aber trotz- dem WÄHREND DER LAUFZEIT beschaffen: Im RAM von $31A-$328 liegt nämlich die Treibertabelle HATABS, die für jeden von max 11 Treibern drei Bytes enthält: - den ATASCII-Wert des Gerätenamens - die Adresse der Vektortabelle LO'HI Man kann also nun HATABAS durchsuchen (immer 3-Byte-weise), bis man auf ein 'D' als Gerätenamen stNßt. Danach folgt dann die Adresse der 'D'-Vektortabelle. Allerdings ist es meist einfacher, die Floppy auf die herkömmliche Weise anzu- sprechen. So, jede dieser Vektortabellen ist nun 16 Bytes lang: Byte Bedeutung 0,1 Zeiger auf OPEN-Routine-1 2,3 Zeiger auf CLOSE-Routine-1 4,5 Zeiger auf GET-Routine-1 6,7 Zeiger auf PUT-Routine-1 8,9 Zeiger auf STATUS-Routine-1 $A,B Zeiger auf SPECIAL-Routine-1 $C,D,E JMP INIT $F unbenutzt Die Bytes $C-$F sind für uns nicht wichtig. Die nächste Frage ist wohl, warum immer die Adresse der Routinen-1 angegeben wird. Das liegt an der Arbeitsweise des Prozessors: Bei einem RTS-Befehl holt er nämlich zuerst das LO-,dann das HI-Byte der Rücksprungadresse vom Stack und erhöht das Ganze dann um 1. Um eine der Routinen aufzurufen, tun wir also folgendes: LDA VEKTOR+1 PHA LDA VEKTOR PHA RTS So, wie übergeben wir den Routinen nun die Parameter? Die Handler selbst greifen auf eine Kopie des IOCB-Blocks zu, den s.g. ZERO-PAGE-IOCB: ($21 ICDNOZ Geräte-Nr.) $22 ICCOMZ Kommando ($23 ICSTAZ Status) $24,$25 ICBAZ Adresse ($26,$27 ICPTZ nicht zu verwenden) ($28,$29 ICBLZ Länge) $2A ICAX1Z Aux 1 $2B ICAX2Z Aux 2 $2C,$2D ICSPRZ Aux 3+4 ($2E ICIDNO Kanal-Nr.*16) Davon braucht man in der Regel aber nur das Adress-Register und die Aux-Bytes. Das OPEN-Kommando funktioniert wie gewohnt, man muß nur ICBAZ und ICAX1-2Z setzen (evtl. noch ICIDNO und ICDNOZ) Die CLOSE-Routine bietet auch nur das Übliche. Die GET-Routine liest EIN Byte ein und gibt es im AKKU zurück, PUT gibt EIN Byte, das im Akku übergeben wird, aus. STATUS gibt einen Status-Wert in ICSTAZ zurück, und SPECIAL führt alle in ICCOMZ stehenden Kommandos (>=13) aus. Besonders oft benutzten wird man wohl die Editor-,Keyboard- und Screen- Funktionen. Hier die Parameter für die Screen-OPEN- Routine: Aux2: Grafikstufe (0-15) Aux1: Modus Bit 2:Lesen Bit 3:Schreiben Bit 4:Textfenster einblenden Bit 5:Screen nicht löschen Als Beispiel wollen wir einen Graphics-31 Bildschirm öffnen: SCRENV=$E410 ICAX1Z=$2A ICAX2Z=$2B RAMTOP=$6A OPEN=0 BEGIN JSR GR31 ... GR31 LDA SCRENV+OPEN+1 PHA LDA SCRENV+OPEN ! PHA LDA #28 STA ICAX1Z LDA #15 STA ICAX2Z LDA #$80 STA RAMTOP RTS ... Der Bildschirm wird direkt unter RAMTOP aufgebaut. Beim Ausprobieren muß man natürlich beachten, daß zu Graphics-0 zurückge- schaltet wird, sobald der Assembler seine 'Edit>'-Meldung ausgibt. Jetzt wollen wir einen Buchstaben auf den Bildschirm ausgeben. Eine unserer leichtesten Übungen: EDITRV=$E400 PUT=6 BEGIN LDA #'H JSR PUT_CH LDA #'i JSR PUT_CH LDA #'! JMP PUT_CH PUT_CH TAY Buchstaben retten! LDA EDITRV+PUT+1 PHA LDA EDITRV+PUT PHA TYA ..zurückholen RTS Soweit so gut. Bis zum nächsten Mal. Ein weiteres kleines Beispiel findet Ihr auf der Disk.