2.5 Übungen zur Assemblerprogrammierung Inhalt der Kurseinheit 3

P2/1

Lösungsvorschläge zu den Praktischen Übungen

Zu P2.3-1:

Für pass 1 des Assemblers ergibt sich als Flußdiagramm:

Bild

P2/2

Für pass 2 des Assemblers ergibt sich als Flußdiagramm:

Bild

P2/3

Zu P2.4-1:

  1. Wird das Programm mit der Option "-L" assembliert, ergibt sich das folgende Listing. Ganz links steht die Zeilennummer des Listings. Rechts daneben steht die Speicheradresse mit dem OP-Code. Weiter rechts folgt das Quellcodeprogramm.
    0001                       **************************************************
    0002                       * P24-1.ASM
    0003                       *
    0004                       * Programm zur Addition von BCD-Zahlen ohne Verwen
    0005                       * des DAA-Befehls
    0006                       *
    0007 0400                  ORG   $0400   ;Beginn des Programmbereichs
    0008
    0009 0400 bd f1 10         JSR   CLRDISP ;Anzeige loeschen
    0010 0403 10 8e 06 00      LDY   #$0600  ;Datenzeiger laden
    0011 0407 6f a4            CLR   0,Y     ;Datenbereich mit Null initialisieren
    0012 0409 6f 21            CLR   1,Y     ;
    0013 040b 8e 00 00 START:  LDX   #$0000  ;X loeschen
    0014 040e bd f1 43         JSR   HALTKEY ;Zeichen von Tastatur lesen
    0015 0411 c1 86            CMPB  #$86    ;Vergleich, auf Ende der Eingabe "S"
    0016 0413 27 30            BEQ   ENDE    ;
    0017 0415 c1 09            CMPB  #$09    ;Test der Eingabe auf gueltige Ziffer
    0018 0417 22 f2            BHI   START   ;bei ungueltiger Ziffer zurueck 
    0019 0419 eb a4            ADDB  0,Y     ;Addition der Eingabe mit LSB der Sum
    0020 041b 1f 98            TFR   B,A     ;
    0021 041d 84 f0            ANDA  #$F0    ;Test auf Ueberlauf durch vorherige A
    0022 041f 26 06            BNE   BCDADD  ;wenn ja, Korrektur
    0023 0421 1f 98            TFR   B,A     ;
    0024 0423 81 09            CMPA  #$09    ;Test auf gueltige BCD-Ziffer
    0025 0425 23 02            BLS   CARRY2  ;wenn nein, Korrektur
    0026 0427 cb 06    BCDADD: ADDB  #$06    ;Korrektur, als Ergebnis gueltige BCD 
    0027 0429 1f 98    CARRY2: TFR   B,A     ;
    0028 042b 84 f0            ANDA  #$F0    ;
    0029 042d 27 06            BEQ   SHOW    ;Test auf Ueberlauf durch vorherige A 
    0030 042f 86 01            LDA   #$01    ;
    0031 0431 ab 21            ADDA  1,Y     ;naechsthoeherwertige Ziffer um Eins 
    0032 0433 a7 21            STA   1,Y     ;naechsthoeherwertige Ziffer abspeich
    0033 0435 c4 0f     SHOW:  ANDB  #$0F    ;eventl. Uebertrag der korrigierten Z
    0034 0437 e7 a4            STB   0,Y     ;korrigierte Ziffer abspeichern
    0035 0439 bd f1 1c         JSR   SHOWT7SG;korrigierte Ziffer in Anzeige
    0036 043c e6 21            LDB   1,Y     ;naechsthoeherwertige Ziffer laden
    0037 043e 30 01            LEAX  1,X     ;Anzeigestelle nach links verschieben
    0038 0440 bd f1 1c         JSR   SHOWT7SG;naechsthoeherwertige Ziffer in Anz
    0039 0443 20 c6            BRA   START   ;zurueck zur naechsten Eingabe
    0040 0445 3f       ENDE:   SWI1
    0041 
    0042 f110          CLRDISP  EQU  $F110   ;Loeschen der Anzeige, In:-, 
    0043 f11c          SHOWT7SG EQU  $F11C   ;unteres Nibble von B in Anzeige
    0044 f143          HALTKEY  EQU  $F143   ;Lesen der Tastatur mit Warten, 
    
    Number of errors 0

  2. Das Programm addiert die eingegebene BCD-Ziffer mit der niederstwertigen Ziffer der Summe. Die BCD-Addition wird ohne den DAA-Befehl durchgeführt. Überschreitet die Summe den Wert "99", so wird hexadezimal in der zweiten Stelle weitergezählt. D.h. eine Überprüfung auf eine gültige BCD-Ziffer in der zweiten Stelle findet nicht statt. Beispielsweise ergibt 99 + 1 = A0.

    P2/4

    Das folgende Flußdiagramm zeigt die Problematik:

    Bild

Zu P2.4-2:

  1. In dem Programm sind drei syntaktische Fehler vorhanden:
    Für Zeile 29 gibt der Assembler die Fehlermeldung "29: Branch out of Range". Hier fehlt das Sprungziel "SHOW" im Verzweigungsbefehl.
    Für Zeile 34 meldet er den Fehler "34: Unrecognized Mnemonic". Es fehlt beim Store-Befehl das Quellregister "B".
    Für Zeile 37 meldet der Assembler: "37: Illegal Register for Indexed". In diesem Fall ist bei den Operanden des "LEAX"-Befehls das erste Komma zuviel.

    P2/5

  2. Der logische Fehler des Programms besteht aus einer falschen Wahl eines Verzweigungsbefehls. Statt einen vorzeichenlosen Verzweigungsbefehl zu benutzen, wurde in Zeile 18 der "BGT"-Befehl benutzt. Hierdurch werden die Tasten des Funktionsblockes akzeptiert, so daß die Addition zu falschen Ergebnissen führt.


Zu P2.4-3:

Der "DS9" erzeugt aus der Datei "P24-1.HEX" das folgende Listing:

;                    ***   6809 - Disassembler Ver. 1.3   ***

0400: BD F1 10         JSR    $F110
0403: 10 8E 06 00      LDY    #$0600
0407: 6F A4            CLR    ,Y
0409: 6F 21            CLR    $01,Y
040B: 8E 00 00         LDX    #$0000
040E: BD F1 43         JSR    $F143
0411: C1 86            CMPB   #$86
0413: 27 30            BEQ    $30   ;$0445
0415: C1 09            CMPB   #$09
0417: 22 F2            BHI    $F2   ;$040B
0419: EB A4            ADDB   ,Y
041B: 1F 98            TFR    B,A
041D: 84 F0            ANDA   #$F0
041F: 26 06            BNE    $06   ;$0427
0421: 1F 98            TFR    B,A
0423: 81 09            CMPA   #$09
0425: 23 02            BLS    $02   ;$0429
0427: CB 06            ADDB   #$06
0429: 1F 98            TFR    B,A
042B: 84 F0            ANDA   #$F0
042D: 27 06            BEQ    $06   ;$0435
042F: 86 01            LDA    #$01
0431: AB 21            ADDA   $01,Y
0433: A7 21            STA    $01,Y
0435: C4 0F            ANDB   #$0F
0437: E7 A4            STB    ,Y
0439: BD F1 1C         JSR    $F11C
043C: E6 21            LDB    $01,Y
043E: 30 01            LEAX   $01,X
0440: BD F1 1C         JSR    $F11C
0443: 20 C6            BRA    $C6   ;$040B
0445: 3F               SWI(1)


Symbolische Adressen für Sprungziele und Unterprogramme können bei einer Disassemblierung nicht berücksichtigt werden. Ein "Nach-Disassemblieren" von Hand ist notwendig. Der generierte Quellcode stimmt ansonsten mit dem "Original"-Code überein.
Beachten Sie bitte, daß der "DS9" bei Verzweigungen nicht nur den Offset zum Sprungziel angibt, sondern auch die absolute Adresse des Sprungzieles.

P2/6

Zu P2.5-1:

Für das Unterprogramm von Tabelle 2.4-6 ergibt sich das folgende Flußdiagramm:

Bild

Im Falle eines Übertrages auf die nächsthöhere Ziffer wird das Register A auf einen Wert ungleich Null gesetzt und dem Hauptprogramm übergeben. Die berechnete Ziffer wird abgespeichert. Die Übergabe der zugehörigen Speicheradresse erfolgt durch das Register Y.


Zu P2.5.2:

Das Flußdiagramm für das Hauptprogramm ist auf der folgenden Seite abgebildet. Erzeugt die BCD-Addition einen Übertrag, so wird das Unterprogramm nochmals aufgerufen, um den Übertrag zur nächsthöheren Ziffer der Summe zu addieren. Im worst case muß über die gesamte Zahlenbreite eine Übertragsaddition erfolgen. Wird ein Überlauf erkannt, so wird das Overflow-Symbol zur Anzeige gebracht. Wird danach die "S"-Taste gedrückt, so wird das Programm beendet, ansonsten bei Eingabe einer gültigen Ziffer mit einer neuen Addition begonnen.

P2/7

Bild

Eine mögliche Implementierung für das Hauptprogramm folgt jetzt.

P2/8

;****************************************************************************
; P1a.ASM
;
; Programm zur Addition von BCD-Zahlen mit maximal 7 Ziffern ohne Verwendung
; des 6809-Befehls DAA. Der Befehl wird durch das Unterprogramm UP_ADD er-
; setzt.
; Erweiterung der Wortbreite ohne Probleme moeglich 
; (Praktikumsrechner hat allerdings max. 8 Stellen).
; Bei Ueberlauf Aktivierung der Anzeige.
; Abbruch der Eingabe mit der Taste "S" des Praktikumsrechners.
;
        ORG    $0400      ;Beginn des Programmbereiches

NEW:    JSR    CLRDISP    ;Anzeige loeschen
        CLRA              ;A loeschen
        CLRB              ;B loeschen
        STD    >$0600     ;Datenbereich $0600 - $0607 
        STD    >$0602     ;mit Null initialisieren
        STD    >$0604     ; 
        STD    >$0606     ; 
START:  LDX    #$0000     ;X loeschen
        LDY    #$0600     ;Datenzeiger laden
        LDU    #ZWIDTH    ;Zahlenbreite laden
        JSR    HALTKEY    ;Zeichen von Tastatur lesen
        CMPB   #$86       ;Vergleich, auf Ende der Eingabe (Taste "S")
        BEQ    END        ;
        CMPB   #$09       ;Test der Eingabe auf gueltige Ziffer (0..9)
        BHI    START      ;bei ungueltiger Ziffer zurueck zur Eingabe
CARRY:  JSR    UP_ADD     ;Aufruf UP zur Addition von zwei BCD-Ziffern
        JSR    SHOWT7SG   ;korrigierte Ziffer in Anzeige
        LEAX   1,X        ;X:=X+1
        LEAY   1,Y        ;Y:=Y+1
        LEAU   -1,U       ;U:=U-1
        LDB    #$01       ;Reg. B fuer Addition des Uebertragsbits auf 1 
        CMPA   #$00       ;Vergleich auf Ueberlauf
        BEQ    START      ;wenn nein, zurueck zur naechsten Eingabe
        CMPU   #$0000     ;Vergleich auf max. Zahlenbreite
        BEQ    OVERFL     ;Overflow, Uebertrag ignorieren; zur OV-Behandlung
        BRA    CARRY      ;Uebertrag addieren und anzeigen
OVERFL: LDA    #$5C       ;
        JSR    SHOWA      ;Overflow-Symbol in Anzeigestelle S7
        JSR    HALTKEY    ;Zeichen einlesen
        CMPB   #$86       ;Programmende?
        BNE    NEW        ;nein, neuer Lauf
END:    SWI               ;Programmende

ZWIDTH  EQU    $0007      ;Maximale Ziffernanzahl einer zu speichernden BCD-Zahl

;----------------------------------------------------------------------------
; Hier beginnen die Definitionen der Monitorroutinen und der Adressen der
; Hardware-Register.
;                       Monitorroutinen
;
CLRDISP    EQU  $F110     ;Loeschen der Anzeige, In:-, Out:-
SHOWT7SG   EQU  $F11C     ;unteres Nibble von B in Siebensegmentcode wandeln 
                          ;und anzeigen, Position in X
SHOWA      EQU  $F113     ;Direkte Ansteuerung der Segmente der Anzeige-
                          ;stelle durch das Byte in A, Position in X
HALTKEY    EQU  $F143     ;Lesen der Tastatur mit Warten, In:-, Out:B

Sie finden das Assemblerprogramm in dem Unterverzeichnis "Software" der CD-ROM in der Datei "P1a.ASM".

P2/9

Zu P2.5-3:

Die folgenden drei Unterprogramme repräsentieren die geforderten Zähler. In "UP1" werden die Register über den Stack gerettet, während in "UP2" nicht benötigte Register verwendet werden. Das Programm ist auch als Datei "P25-3.ASM" im Unterverzeichnis "KE2" abgelegt.

;=====================================================================
;Unterprogramm  UP1: Zaehler, für die Anzeigestellen S5,S4 
;                Register B und X werden ueber den Stack gerettet.
;
UP1:    LDX     #$0004     ;Anzeigeposition laden (S5,S4)
        LDB     #$FF       ;Zaehler initialisieren
NEXT1:  INCB               ;Zaehler inkrementieren
        JSR     SHOWB7SG   ;Zaehlerstand anzeigen
        PSHS    B          ;Zaehler und Anzeigeposition ueber
        PSHS    X          ;den Stack retten
        JSR     UP2        ;Aufruf des zweiten UP
        PULS    X          ;Zaehler und Anzeigeposition einlesen
        PULS    B          ;
        CMPB    #$FF       ;Abbruchbedingung erfuellt?
        BNE     NEXT1      ;wenn nein, zurueck zu NEXT1
        RTS                ;Ende Unterprogramm

;=====================================================================
;Unterprogramm  UP2: Zaehler, für die Anzeigestellen S3,S2 
;                Register B und X werden ueber Register gerettet.
;
UP2:    LDX     #$0002     ;Anzeigeposition laden (S3,S2)
        LDB     #$FF       ;Zaehler initialisieren
NEXT2:  INCB               ;Zaehler inkrementieren
        JSR     SHOWB7SG   ;Zaehlerstand anzeigen
        TFR     B,A        ;Zaehler und Anzeigeposition ueber
        TFR     X,Y        ;andere Prozessorregister retten
        JSR     UP3        ;Aufruf des dritten (letzten) UP
        TFR     A,B        ;Zaehler und Anzeigeposition wieder 
        TFR     Y,X        ;zurueckschreiben
        CMPB    #$FF       ;Abbruchbedingung erfuellt?
        BNE     NEXT2      ;wenn nein, zurueck zu NEXT2
        RTS                ;Ende Unterprogramm

;=====================================================================
;Unterprogramm  UP3: Zaehler, für die Anzeigestellen S1,S0 
;
UP3:    LDX     #$0000     ;Anzeigeposition laden (S1,S0)
        LDB     #$FF       ;Zaehler initialisieren
NEXT3:  INCB               ;Zaehler inkrementieren
        JSR     SHOWB7SG   ;Zaehlerstand anzeigen
        CMPB    #$FF       ;Abbruchbedingung erfuellt?
        BNE     NEXT3      ;wenn nein, zurueck zu NEXT3
        RTS                ;Ende Unterprogramm

Im Unterprogramm "UP2" werden je zwei Stackoperationen benutzt, um die Registerinhalte auf den Stack zu schreiben bzw. einzulesen. Es ist auch möglich, die Stackoperationen mit jeweils einem Befehl auszuführen. Hierzu ist entweder der numerische Wert "immediate" anzugeben; oder die einzelnen Register durch Kommata getrennt aufzuzählen.

P2/10

Zu P2.5-4:

Eine mögliche Implementierung des Hauptprogramms und der Interruptroutine zeigt das folgende Listing:

;*********************************************************************
; P4.ASM
;
; Ziffern-Zaehlprogramm mittels einer Software-Interruptroutine.
; Software-Interrupt wird als Monitor- (Betriebssystem-) Gate benutzt, 
; um Funktionen des Monitors zu benutzen. Eingegebener Wert wird in S0 
; dargestellt, Anzahl der eingebenen Werte fuer "0" in S7, fuer "1" in 
; S6. Uebergabeparameter: X-Reg.: call-by-value, B-Reg.: call-by-refr.
; Parameter werden in der Interruptroutine Stack-relativ geladen.
;
         ORG   $0400    ;Beginn des Programmbereiches

         LDX   #MRGATE  ;Interruptroutine in der Vektortabelle
         STX   $004C    ;an Adresse $004C, $004D eintragen
NEW:     LDX   #$0008   ;Uebergabeparameter nach A (Anzeige loeschen)
         SWI3           ;Software-Interrupt ausfuehren
         CLRB           ;Zaehler in den Speicherstellen
         LDX   #$0006   ;$06 und $07 in der Zero-Page
         STB   ,X       ;mit Null initialisieren
         SWI3           ;und jeweiliger Wert (0)
         LEAX  1,X      ;in den Anzeigestellen
         STB   ,X       ;S6 und S7 anzeigen
         SWI3           ;
INPUT:   LDX   #$0009   ;Uebergabeparameter fuer Tastatur nach X und
         SWI3           ;zur Interruptroutine verzweigen
         CMPB  #$01     ;Wurde Taste "1" betaetigt?
         BNE   INPUT0   ;Wenn nein, vielleicht "0"
         LDX   #$0000   ;Wert in Anzeigestelle
         SWI3           ;S0 anzeigen
         LEAX  6,X      ;Zaehler fuer den
         LDB   ,X       ;Wert "1" laden,
         INCB           ;inkrementieren und wieder
         STB   ,X       ;abspeichern
         SWI3           ;In S6 anzeigen
         BRA   INPUT    ;zurueck zur naechsten Eingabe
INPUT0:  CMPB  #$00     ;Vergleich auf Taste "0"
         BNE   INPUT+   ;wenn nein, vielleicht "+"-Taste
         LDX   #$0000   ;Wert in Anzeigestelle
         SWI3           ;S0 anzeigen
         LEAX  7,X      ;Zaehler fuer "0"
         LDB   ,X       ;laden,
         INCB           ;inkrementieren und
         STB   ,X       ;abspeichern
         SWI3           ;In S7 anzeigen
         BRA   INPUT    ;zurueck zur naechsten Eingabe
INPUT+:  CMPB  #$80     ;Vergleich auf "+"-Taste
         BEQ   NEW      ;Wenn ja, Zaehler neu initialisieren
         BRA   INPUT    ;ungueltige Taste, zurueck zur Eingabe

P2/11

; Interruptroutine
MRGATE:  LDX   4,S      ;X-Register vom Stack holen (Stack-relativ)
         CMPX  #$0008   ;Routine fuer Anzeige gefordert ?
         BHS   DISP     ;Wenn nein, vielleicht Anzeige loeschen
         JSR   SHOWT7SG ;Anzeige des unteren Nibbles aus B
         BRA   SUBEND   ;zum RTI
DISP:    CMPX  #$0008   ;Routine zum Loeschen der Anzeige gefordert?  
         BNE   TAST     ;Wenn nein, vielleicht Routine zur Tastatureing.
         JSR   CLRDISP  ;Anzeige loeschen
         BRA   SUBEND   ;zum RTI
TAST:    CMPX  #$0009   ;Routine zur Tastatureingabe gefordert?
         BNE   SUBEND   ;Wenn nein, falscher Uebergabeparameter; zurueck
         JSR   HALTKEY  ;auf Eingabe von Tastatur warten
         STB   2,S      ;B-Register auf den Stack legen (Stack relativ)
SUBEND:  RTI            ;Ruecksprung zum Hauptprogramm


CLRDISP   EQU  $F110    ;Loeschen der Anzeige, In:-, Out:-
SHOWT7SG  EQU  $F11C    ;unteres Nibble von B in Anzeige, Position in X
HALTKEY   EQU  $F143    ;Lesen der Tastatur mit Warten, In:-, Out:B


Es hat sich als gute Programmierpraxis erwiesen, in Unterprogrammroutinen, und insbesondere in Interruptroutinen, nur eine Aussprungstelle zu programmieren. In der obigen Interruptroutine wäre es möglich nach den einzelnen Abfragen nicht mit einem BRA-Befehl zum Ende der Routine zu verzweigen und erst dann einen RTI-Befehl durchzuführen, sondern statt eines BRA-Befehls sofort einen RTI-Befehl zu programmieren. In Bezug auf ein gutes Software-Engineering und einfache Wartungs- und Pflegeoptionen hat sich herausgestellt, daß sich eine Beschränkung auf eine einzige Aussprungstelle positiv auf die Programmierfehlerrate auswirkt.

Sie finden das Assemblerprogramm im Unterverzeichnis "Software" der CD-ROM in der Datei "P4.ASM".


2.5 Übungen zur Assemblerprogrammierung Inhalt der Kurseinheit 3