;************************************** ;* Sinus- und Cosinusfunktion mittels * ;* Coprozessor berechnen und grafisch * ;* darstellen. 2010 H.V.Viehof * ;* Aenderungen: * ;* Amplitude steuerbar, es wird auf * ;* erreichen von Min / Max getested * ;* Das Farbige Beispiel :-) * ;************************************** ;************************************** ;* Grundlegende Grafik Macros * ;* Menory Mapped Input/Output * ;* 2010 H.V.Viehof * ;************************************** ;-------------------------------------- ; SetGraph Macro - Graphikmodus setzen ;-------------------------------------- SetGraph MACRO ; al = Modus mov ah, 0 ; die Funktion 0 des Videointerupts int 010 #EM ;-------------------------------------- ; ClearScreen Macro ;-------------------------------------- ClearScrn MACRO ; loescht den Inhalt des Videorams mov bx, 0 mov ax, 0a000 ; Segmentaddresse des Videoram mov ds, ax ; setzen C1: mov ds: b[bx], 0 ; 0 an die Addresse speichern inc bx ; bx hochzaehlen fuer naechsten Punkt cmp bx, 38400 ; das sind 80x480 Bytes mit je 8 Bildpunkten jnz C1 ; tekrarla! #EM ;-------------------------------------- ; DrawPixelD Macro - einzelnen Punkt ; zeichnen durch unmittelbaren Zugriff ; auf den Grafikspeicher bei a000:0000 ;-------------------------------------- DrawPixelD MACRO ; einzelner Punkt unmittelbar ins Videoram push es ; Register sichern push dx push cx mov ax, 10000000xb ; ein Bit links setzen für einen Punkt and cx, 7 ; die unteren drei Bits von CX addressieren das Bit shr ax, cl ; das Bit fuer den Punkt auf Position schieben pop cx ; alten Wert von CX zurueckholen push cx ; und auch wieder auf den Stack push ax ; AL von AX zeigt auf das einzelne Bit shr cx, 3 ; CX / 8 denn drei Bits ergaben ja die Punktposition mov bx, 80 ; 80 Bytes ist die Zeile lang mov ax, dx ; DX (Spaltenposition) in AX mov dx, 0 ; DX loeschen denn es wird multipliziert mul bx ; Speicheraddresse = Zeile + Spalte x 80 add ax, cx ; deshalb: addieren mov bx, ax ; In BX, das wird als Pointer benutzt mov dx, 0a000 ; Segmentaddresse des Videoram mov es, dx ; setzen pop ax ; im 'alten' AL befindet sich die Bitposition or al, es: b[bx] ; diesen neuen Punkt per Oder in das Speicherbyte einfuegen mov es: b[bx], al ; an dieselbe Addresse zurueckspeichern pop cx ; 'alte' Register wiederherstellen pop dx pop es #EM ;-------------------------------------- ; DrawLine Macro - horizontale Linie ; dx = position y / hoehe ; ; kurze Erklaerung: ein Byte in einer ; Ebene des Videorams sind 8 ; aufeinanderfolgende Pixel, also ist es ; effizienter zum Zeichnen horizontaler ; Linien die Bytes komplett zu fuellen. ; Dabei sollte die Zaehlschleife die ; die Bytes addressiert moeglichst kurz ; gehalten werden. ;-------------------------------------- DrawLineD MACRO push es push dx ; Register sichern mov cx, 0 ; CX zaehlt die Pos innerhalb der Zeile und faengt mit 0 an mov ax, 0a000 ; Segmentaddresse des Videoram mov es, ax ; setzen mov bx, 80 ; 80 Bytes ist die Zeile lang, das ist der Multiplikator mov ax, dx ; DX (Zeilenposition) in AX mov dx, 0 ; DX loeschen denn es wird multipliziert mul bx ; Speicheraddresse = Spalte + Zeile x 80 L1: mov bx, ax ; In BX, das wird als Pointer benutzt add bx, cx ; deshalb: Spaltenposition addieren mov es: b[bx], 0ff ; 0ff an die Addresse speichern inc cx ; cx runterzaehlen fuer naechsten Punkt cmp cx, 79 ; 0 bis 79 = 80 Positionen (der Konflikt Kardinal- zu Ordinalzaehlung) jnz L1 ; den naechsten Punkt zeichnen pop dx ; alle gesicherten Registerinhalte zurueckholen pop es #EM ;-------------------------------------- ; DrawVLine Macro - zeichnet vertikale Linie ; cx = position x ;-------------------------------------- DrawVLineD MACRO ; dieses Macro ist etwas anders mov dx, 0 ; DX zaehlt PunktPositionen in einer Reihe von oben nach unten L2: DrawPixelD inc dx cmp dx, 01e0 ; zeichne eine vertikale Linie bis dx = 480 unterer Bildschirmrand jl L2 #EM ;______________________________________ ; ; Stapelsegment: ;______________________________________ stack segment stack word 'stack' dw 120 dup (?) ; 120 Words, ich denke das reicht 'so gerade'... stack ends ;______________________________________ ; ; Start des Hauptteils ;______________________________________ CODE SEGMENT WORD PUBLIC 'CODE' ASSUME CS:CODE, DS:data, SS:stack, ES:variablen start: mov ax, seg variablen ; Vorbereiten von mov es, ax ; Datensegment und mov es: b color, 7 ; Farbe des Textes: verweist auf die Palette mov es: b Merker, 0 ; Text anzeigen / verbergen mov es: w Amplitude, 220 ; Amplitude des Sinus ;-------------------------------------- ; In Videomodus 640 * 480 mit 16 Farben wechseln ;-------------------------------------- mov al, 012 ; 012h ist genau der richtige Modus SetGraph ; 06a wäre 800 x 600 evtl. 18 = 1024 x 768 moegl. ;-------------------------------------- ; VGA Palette abspeichern, wird innerhalb ; des Programms evtl. aber beim Verlassen ; auf jeden Fall gebraucht ;-------------------------------------- GetPal: mov dx, 03c8 ; Paletten gehoeren ins RAMDAC mov al, 0 ; Funktionswahl Palette schreiben cli ; maskable Interrupts sperren out dx, al ; Indexregister cld mov dx, 03c9 ; Datenregister Portadresse S1: mov cx, 768 ; Die Palette besteht aus 256 Farbtrippels mit je drei Byte mov si, offset VgaPal ; allerdings gelten nur Bits 0 bis 5, rot, gruen, blau S2: in al, dx es: stosb ; wir speichern die 'alte' VGA Palette mit 786 Werten ab loop S2 ;-------------------------------------- ; Palette generieren, nicht sehr elegant ; aber zum testen ausreichend ; (wenn man keine eigene Palette definiert ; werden Standart VGA Farben benutzt) ; die ersten 16 Palettenfarben werden ; gesetzt, der Rest wiederholt sich ;-------------------------------------- NewPal: mov ax, seg data mov ds, ax mov bx, 16 ; ergibt insgesammt 768 Farbwerte mov dx, 03c8 ; Paletten gehoeren ins RAMDAC mov al, 0 ; Funktionswahl Palette schreiben ;cli ; maskable Interrupts sperren (ist weiter oben schon geschehen) out dx, al ; Indexregister cld mov dx, 03c9 ; Datenregister Portadresse S1: mov cx, 48 ; Die Palette besteht aus 256 Farbtrippels mit je drei Byte mov si, offset Colors ; allerdings gelten nur Bits 0 bis 5, rot, gruen, blau S2: lodsb ; wir fuellen die ersten 16 Trippels mit individuellen Werten, out dx, al ; der Rest wiederholt sich... loop S2 dec bx jnz S1 ;sti ; Ext. Int. zulassen (noch nicht, erst spaeter wieder) ;-------------------------------------- ; Bildaufbau ;-------------------------------------- drawall: ClearScrn ; Bildschirm 'schwarz malen' ; dieses loeschen des Bildschirms muss auch fuer jeden Plane (Farbebene) mov dx, 03c4 ; extra erfolgen, deshalb: Planes umschalten! mov al, 2 ; weiter unten ist erklaert wie diese Sequenz arbeitet out dx, al mov dx, 03c5 mov al, 1 out dx, al ClearScrn ;mov dx, 03c4 ; auskommentiert: z.Zt. nicht erforderlich ;mov al, 2 ; Wuerde man hier jetzt einen bestimmten Plane ansprechen ;out dx, al ; wuerde das den Rahmen gesondert einfaerben ;mov dx, 03c5 ;mov al, 1 ;out dx, al ;-------------------------------------- ; Rahmen zeichnen ;-------------------------------------- mov dx, 0 ; Startwert fuer die erste hor. Linie DrawLineD ; Das Macro oben mov dx, 479 ; neue Position der naechsten hor. Linie DrawLineD ; zeichnen... mov cx, 0 ; Startwert fuer die erste ver. Linie DrawVLineD ; im Prinzip dasselbe wie fuer die horz. Linien... mov cx, 639 ; wieder: neue Linie DrawVLineD ;-------------------------------------- ; Funktionsgraph zeichnen ;-------------------------------------- mov ax, seg data ; dort sind Konstanten mov ds, ax mov ax, seg variablen ; und da Variablen mov es, ax fninit ; Coprozessor initialisieren mov es: w ein, 5120 ; entro: mov cx, es: w ein ; cx = x Wert shr cx, 3 fild es: w ein ; Wert aus Speicherstelle laden fdiv ds: d korr ; durch Korrekturwert teilen fldpi ; und mit PI fmul ; multiplizieren = Grad zu Rad umwandeln fsincos ; jetzt kann man den Sinus (ST1) / Cosinus (ST0) bilden fimul es: w Amplitude ; mit Faktor 240 multiplizieren frndint ; auf ganze Zahl runden fistp es: w aus ; ST0 in den Speicher schreiben fimul es: w Amplitude ; mit Faktor 240 multiplizieren frndint ; auf ganze Zahl runden fistp es: w aus2 ; ST1 in den Speicher schreiben cmp es: w aus, 240 ; Minimum jl >O1 cmp es: w aus, -240 ; Maximum jl >O1 jmp Exception O1: mov dx, 240 ; 240 ist die Bildschirmitte von oben sub dx, es: w aus2 ; davon subtrahiere ich die Ausgabe der Sin. Funkt. push dx ; Hier wird die Farbe durch aufrufen des Planes gesetzt mov dx, 03C4 ; Registersatz des Timing Sequencers mov al, 2 ; Funktion: Plane select out dx, al ; Index Register mov dx, 03C5 ; DX + 1 = Daten Register ansteuern mov al, 1 ; Plane aufrufen out dx, al pop dx DrawPixelD ; das Macro zeichnet den Punkt an cx/dx mov dx, 240 ; 240 ist die Bildschirmitte von oben sub dx, es: w aus ; davon subtrahiere ich die Ausgabe der Sin. Funkt. push dx mov dx, 03C4 ; Das ist aehnlich wie oben mov al, 2 ; out dx, al ; mov dx, 03C5 mov al, 2 out dx, al pop dx DrawPixelD ; das Macro zeichnet den Punkt an cx/dx dec es: w ein jz TextOut jmp entro ; solange Wert > 0 das oben alles wiederholen Exception: mov ax, seg data ; Ausnahmebehandlung - Hinweis Bereich ueber-/unterschritten mov ds, ax mov bl, 4 ; Farbnummer 4 in der Palette (siehe unten) mov dh, 0 ; Positionen laden: Anfang Textfensteroberkante mov si, offset minmax ; Anfang des Textes jmp Display ;-------------------------------------- ; Textdarstellung ;-------------------------------------- TextOut: cmp es: b Merker, 0 ; Merker == 0: Fenster anzeigen jnz Key ; Merker != 0: Fenster verbergen mov ax, seg data mov ds, ax mov bl, es: b color mov dh, 0 ; Positionen laden: Anfang Textfensteroberkante mov si, offset message ; Anfang des Textes jmp Display ;-------------------------------------- ; Auf Eingabe eines Zeichens warten ;-------------------------------------- Key: ;sti mov ah, 7 ; auf Tastendruck warten int 021 ; ein 'Minimenu' cmp al, '+' jnz >W1 inc es: w Amplitude jmp drawall W1: cmp al, '-' jnz W2 dec es: w Amplitude jmp drawall W2: cmp al, 'C' jnz W3 dec es: b color jmp drawall W3: cmp al, 'c' jnz W6 inc es: b color jmp drawall W6: cmp al, 'h' jnz W7 xor es: w Merker, 1 ; das XOR realisiert einen 'Toggleswitch', cok güzel! jmp drawall W7: cmp al, 'v' jnz W8 call SetPal jmp drawall W8: cmp al, 'n' jnz W9 jmp NewPal W9: cmp al, 'q' jnz Key ;-------------------------------------- ; Videomodus zurückschalten und ; Programm beenden ;-------------------------------------- sti call SetPal ; faktisch zwar nicht erforderlich, aber sauber programmieren mov al, 3 ; bedeutet auch das alle Zustaende wieder so hergestellt werden SetGraph ; wie vor dem Programmaufruf mov ax, 04C00 int 021 ;-------------------------------------- ; Textdarstellung ;-------------------------------------- Display: xor bh, bh ; geloescht, weil das die Textbildschirmseite ist mov cx, 1 ; nur einmal das Zeichen ausgeben L5: inc dh mov dl, 2 ; Anfang der dargestellten Zeile L3: ds: lodsb ; zeichen holen cmp al, 0 ; 0 beendet die Textausgabe jz >L2 ; Ende Textdarstellung cmp al, 0a ; oa ist Ende der dargestellten Zeile jz L5 L1: mov ah, 2 ; cursor setzen int 010 mov ah, 9 ; Funktion 9 von Int 010: Zeichendarstellung mit Attributen int 010 inc dl jmp L3 L2: jmp Key ;-------------------------------------- ; VGA Palette wiederherstellen, nicht ; sehr elegant aber zum testen ausreichend ;-------------------------------------- SetPal: mov ax, seg variablen mov ds, ax mov dx, 03c8 ; Paletten gehoeren ins RAMDAC mov al, 0 ; Funktionswahl Palette schreiben ;cli ; maskable Interrupts sperren out dx, al ; Indexregister cld mov dx, 03c9 ; Datenregister Portadresse S1: mov cx, 768 ; Die Palette besteht aus 256 Farbtrippels mit je drei Byte mov si, offset VgaPal ; allerdings gelten nur Bits 0 bis 5, rot, gruen, blau S2: ds: lodsb ; wir fuellen die ersten 16 Trippels mit individuellen Werten, out dx, al ; der Rest wiederholt sich... loop S2 ret CODE ENDS ;______________________________________ ; ; Datensegment I: Text, feste Faktoren ;______________________________________ data segment public word 'daten' message db "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿",0a db "³ uit toggle Message Window ³",0a db "³ change Messagecolor ³",0a db "³ <+> <-> inc/dec Amplitude ³",0a db "³ ga Palette eue Palette ³",0a db "³ 2010 ³",0a db "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ",0 minmax db "ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿",0a db "³ Maximum / Minimum erreicht ³",0a db "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ",0 korr dd 1440.0 Colors db 0,0,0, 32,0,32, 46,16,0, 16,0,16, 48,0,0, 48,0,32, 52,16,0, 0,0,32 db 0,0,16, 32,0,40, 46,16,8, 16,0,24, 48,0,8, 48,0,40, 52,16,8, 0,0,40 data ends ;______________________________________ ; ; Datensegment II: 'echte' Variablen ;______________________________________ variablen segment public word 'daten' ein dw ? aus dw ? aus2 dw ? Amplitude dw ? color db ? Merker db ? VgaPal db 786 dup (?) variablen ends END start