.model small .stack 100h .data Titel db 13,10,' ',78 dup(196),13,10 db ' Assembler-Beleg von Thiemo M„ttig, Seminargruppe II99, Matrikelnummer 21566',13,10 db ' ',78 dup(196),13,10,13,10,'$' ZahlFehlt db ' Bitte geben Sie beim Start des Programmes die Geheimzahl als Parameter an.' db 13,10,' Drcken Sie eine beliebige Taste, um fortzusetzen...',13,10,'$' ZahlFalsch db ' Die Geheimzahl ist falsch, das Programm wird beendet...',7,13,10,'$' EingabeH db ' Dieses Programm berechnet das Volumen einer Kugelschicht. Eine Kugelschicht',13,10 db ' ist definiert als das Volumen, das von zwei zueinander parallelen Ebenen aus',13,10 db ' der Kugel geschnitten wird. Einzugeben sind die Radien der beiden Schnitt-',13,10 db ' fl„chen sowie ihr Abstand zueinander. Einer der Radien kann auch Null sein,',13,10 db ' wobei das berechnete Volumen dann einem Kugelegment entspricht. Es sind nur',13,10 db ' Zahlen bis etwa 200 erlaubt.',13,10,13,10 db ' H”he der Kugelschicht: $' EingabeR1 db 13,10,' Radius des ersten Schnittes: $' EingabeR2 db 13,10,' Radius des zweiten Schnittes: $' Ueberlauf db 13,10,13,10,' Eines der Zwischenergebnisse wird zu groá. Bitte geben Sie kleinere Ausgangs-' db 13,10,' werte ein.',13,10,13,10 db ' Eine beliebige Taste drcken, um fortzusetzen...$' ISRText1 db 13,10,13,10,' Aufruf der Interrupt-Service-Routine...',13,10 db ' Registerinhalt AX: $' ISRText2 db 13,10,' Registerinhalt BX: $' ISRText3 db 13,10,' Registerinhalt CX: $' ISRText4 db 13,10,' Registerinhalt DX: $' Ergebnis db 13,10,13,10,' Das Volumen der Kugelschicht betr„gt: $' Endtext db 13,10,13,10,' Soll eine weitere Berechnung durchgefhrt werden? ',13,10,'$' .code Bildloeschen macro ;Makro zum Loeschen des Bildschirmes mov ax,0003h ;die 3 ist der normale, farbige Textmodus int 10h ;Modus setzen und Bildschirm gleichzeitig loeschen endm TextOut macro Txt ;Makro zur Textausgabe, mit einem Parameter push ds ;DS zur Sicherheit auf dem Stack vermerken mov ax,@data mov ds,ax ;Segmentadresse des Datensegmentes nach DS mov dx,offset Txt ;Offsetadresse des auszugebenden Textes nach DX mov ah,9 ;Unterfunktion 9 schreibt $-terminierte Strings int 21h pop ds ;DS wieder herstellen endm Taste macro ;wartet auf einen beliebigen Tastendruck locals ;alle mit '@@' beginnenden Labels sind lokal @@WaLabel: mov ah,7 ;Zeichen von Standardeingabe lesen, ohne Echo int 21h cmp al,0 ;die gedrueckte Taste ist am Ende in AL gesichert jz @@WaLabel ;so lange im Kreis laufen, bis Taste gedrueckt wurde endm RegAnz proc far push ax ;alle vier Register zur Rekonstruktion sichern push bx push cx push dx push dx ;Registerinhalte zur Ausgabe vorbereiten push cx push bx push ax TextOut ISRText1 call AusgabeZahl16 ;Registerinhalte auf dem Bildschirm ausgeben TextOut ISRText2 call AusgabeZahl16 TextOut ISRText3 call AusgabeZahl16 TextOut ISRText4 call AusgabeZahl16 Taste pop dx ;alle Register wieder restaurieren pop cx pop bx pop ax iret ;Ende der Interrupt-Service-Routine RegAnz endp EingabeZahl16 proc ;Unterprogramm Zahleneingabe, Rueckgabe ueber Stack push di ;den Registerinhalt DI vorsichtshalber sichern mov di,3 ;Maximallaenge der Eingabe ist 3 Zeichen mov cx,10 ;normale Zahlenbasis 10 einstellen xor dx,dx ;das Ergebnisregister DX auf 0 zuruecksetzen EzLabel1: mov ah,1 ;Unterfunktion 1 zum Lesen eines Zeichens int 21h cmp al,0dh ;wenn Eingabezeichen Return ist je EzLabel2 ;Sprung zum Ende sub al,30h ;Ascii-Code in passenden Zahlenwert umrechnen push ax ;Zahl auf den Stack mov ax,dx ;die Summe aus DX in AX mul cx ;mit 10 multiplizieren pop bx ;die neue Zahl aus dem Stack zurueck holen mov bh,0 add ax,bx ;und addieren mov dx,ax ;wieder in DX sichern dec di cmp di,0 ;wenn Maximallaenge noch nicht erreicht jne EzLabel1 ;Sprung, um weitere Zahl einzugeben EzLabel2: pop di ;DI wieder herstellen pop ax ;Ruecksprungadresse aus dem Stack holen push dx ;die eingegebene Zahl auf den Stack werfen push ax ;Ruecksprungadresse wieder auf den Stack werfen ret EingabeZahl16 endp AusgabeZahl16 proc ;Unterprogramm zur Ausgabe einer Zahl aus dem Stack pop bx ;die Ruecksprungadresse aus dem Stack holen pop ax ;die auszugebende Zahl aus dem Stack holen push bx ;Ruecksprungadresse wieder auf den Stack werfen mov bx,10 ;normale Zahlenbasis 10 einstellen xor cx,cx ;Zaehler CX loeschen PzLabel1: xor dx,dx ;Ueberlaufregister loeschen div bx ;die Zahl in AX durch 10 teilen push dx ;Rest der Division auf den Stack werfen inc cx ;Zaehler erhoehen cmp ax,0 ;wenn die Zahl in AX noch groesser als Null ist jnz PzLabel1 ;weiter machen PzLabel2: pop ax ;einen Wert vom Stack holen add al,"0" ;in passendes Ascii-Zeichen umrechnen mov ah,14 ;Unterfunktion 14 schreibt Zeichen mit Vorschub int 10h loop PzLabel2 ;Zaehler erniedrigen und weiter machen ret AusgabeZahl16 endp AusgabeKomma proc ;Unterprogramm zur Ausgabe des Restes als Kommazahl mov ah,14 mov al,"," ;zuerst einmal das Komma schreiben int 10h pop bx ;die Ruecksprungadresse aus dem Stack holen pop ax ;die auszugebende Zahl aus dem Stack holen push bx ;Ruecksprungadresse wieder auf den Stack werfen mov cx,3 ;die Anzahl der auszugebenden Kommastellen mov di,10 ;Zahlenbasis einstellen, mit der multipliziert wird mov bx,21 ;durch 21 wird dividiert, genau wie im Hauptprogramm PkLabel1: mul di ;AX = AX * 10, DX = in jedem Fall 0 div bx ;AX = AX / 21, DX = Rest dieser Division add al,"0" ;Zahlenwert in ASCII-Zeichen umrechnen mov ah,14 ;Unterfunktion 14 schreibt Zeichen mit Vorschub int 10h mov ax,dx ;der Rest der Division wird weiter bearbeitet loop PkLabel1 ret AusgabeKomma endp Start: push ds ;Segmentadresse DS auf den Stack sichern mov ax,cs mov ds,ax ;Offset des Codesegments nach DS kopieren mov dx,offset RegAnz ;jetzt steht in DS:DX die Adresse der ISR mov ah,25h mov al,100 ;der ungenutzte Interrupt 100 wird veraendert int 21h pop ds ;Segmentadresse wieder restaurieren Neustart: Bildloeschen ;Aufruf des Makros zum Loeschen des Bildschirmes TextOut Titel mov ah,62h int 21h ;Segmentadresse des PSP herausfinden mov es,bx ;Segmentadresse nach ES schieben mov al,es:[80h] ;Offsetadresse fuer die Laenge ist 80h cmp al,0 ;wenn gar kein Parameter angegeben wurde jz GeheimzahlFehlt mov al,es:[82h] ;Offsetadresse 82h, weil ein Leerzeichen davor steht cmp al,"0" ;Vergleich mit gesuchtem Ascii-Zeichen "0" jz Richtig ;Fehlerausgabe ueberspringen, wenn richtig TextOut ZahlFalsch ;boese Fehlermeldung bei falscher Zahl jmp Ende GeheimzahlFehlt: TextOut ZahlFehlt ;nettere Fehlermeldung, wenn keine Zahl angegeben Taste ;hier wird auch auf eine Taste gewartet jmp Ende Richtig: TextOut EingabeH call EingabeZahl16 ;Eingabe fuer den Wert h TextOut EingabeR1 call EingabeZahl16 ;Eingabe fuer einen der Radien TextOut EingabeR2 call EingabeZahl16 ;Eingabe fuer einen der Radien ;Hier beginnt die eigentliche Berechnung des Volumens... xor cx,cx ;CX auf Null setzen, wird fuer Ueberlauf benoetigt pop ax ;r1 vom Stack holen und nach AX cmp ax,256 ;wenn die Zahl nicht kleiner als 256 ist jnb ZuViel ;dann springe zur Fehlerbehandlung mul al ;r1 * r1 nach AX mov bx,ax ;Ergebnis in BX sichern pop ax ;r2 vom Stack holen und nach AX cmp ax,256 ;wenn die Zahl nicht kleiner als 256 ist jnb ZuViel ;dann springe zur Fehlerbehandlung mul al ;r2 * r2 nach AX add ax,bx ;r1*r1 + r2*r2 in AX speichern jnb KsLabel1 ;wenn die Addition einen Ueberlauf verursachte mov cx,3 ;Ueberlauf in CX gleich auf 3 setzen, spart ein "mul" KsLabel1: mov bx,3 ;mit der 3 soll multipliziert werden mul bx ;(r1*r1 + r2*r2) * 3 nach DX:AX add dx,cx ;diesen zum vorherigen Ueberlauf addieren mov cx,ax ;das Gesamtergebnis steht jetzt in DX:CX pop ax ;h wird vom Stack geholt und nach AX geschoben cmp ax,256 ;wenn die Zahl nicht kleiner als 256 ist jnb ZuViel ;dann springe zur Fehlerbehandlung push ax ;h zur zweiten Verwendung sichern mul al ;h * h nach AX add cx,ax ;(r1*r1 + r2*r2)*3 + h*h nach CX jnb KsLabel2 ;wenn diese Addition einen Ueberlauf verursachte inc dx ;Ueberlauf um eins erhoehen, Ergebnis steht in DX:CX KsLabel2: pop ax ;h noch einmal nach AX holen mov bl,11 mul bl ;h * 11 nach AX, kann keinen Ueberlauf verursachen mov bx,ax ;Ergebnis von h*11 nach BX schieben mov ax,dx ;Ueberlauf aus CX nach AX mul bx ;Ueberlauf mit h*11 multiplizieren xchg ax,cx ;Ergebnis davon mit CX vertauschen mul bx ;auch das mit h*11 multiplizieren add dx,cx ;wieder die beiden letzten Ueberlaeufe addieren cmp dx,15h ;eine Zwischensumme >14FFFFh ist unzulaessig jnb ZuViel ;Sprung zur Fehlerbehandlung mov bx,21 div bx ;alles durch 21 teilen, Ergebnis in AX,DX ;Die eigentliche Berechnung ist hier zu Ende int 100 ;den selbst gebauten Interrupt aufrufen jmp AllesOkay ;die folgende Fehlerbehandlung ueberspringen ZuViel: TextOut Ueberlauf Taste jmp Neustart ;das Programm neu starten AllesOkay: push dx ;Rest auf den Stack push ax ;ganzzahliges Ergebnis auf den Stack TextOut Ergebnis call AusgabeZahl16 ;den ganzzahligen Anteil ausgeben call AusgabeKomma ;den Rest ausgeben (Zahl zwischen 0 und maximal 20) TextOut Endtext Taste cmp al,"j" ;Vergleich, ob die Taste "j" gedrueckt wurde jnz Ende ;bei jeder anderen Taste zum Ende springen jmp Neustart ;bei "j" ganz von vorn beginnen Ende: mov ax,4c00h ;Programm regulaer beenden, mit Errorlevel 0 int 21h end Start