Tips und Tricks für Programmierer

Programmfehler zurückverfolgen

Das Betriebssystem des ST bietet einiges, um Programmierern bei der Fehlersuche zu helfen. Die auffälligste Hilfe kennt jeder: Die vielgefürchteten Bomben auf dem Bildschirm informieren den Programmierer, welche Art von Fehler aufgetreten ist. Die Anzahl der Bomben entspricht dabei der Exceptionnummer. Exceptions (zu Deutsch Ausnahmen) treten auf, wenn der Prozessor auf Anweisungen trifft, die einen Bus- (Exception 2), Adressierungs- (Exception 3), Illegal-Fehler (Exception 4) usw. verursachen. Werden die Ausnahme-Vektoren für den CHK- (Exception 6) und TRAPV-Prozessorbefehl (Exception 7) nicht von Ihrem Programm genutzt, so erzeugen auch sie Bomben. Das gleiche gilt für den Trace- (Exception 9) und Privilegs-Verletzungs-Interrupt (Exception 8) und nichtinitialisierte TRAP-Interrupts. Zwei Bomben weisen somit auf einen Busfehler hin, drei Bomben auf einen Adressfehler usw.

Adresse Belegung
$380.L enthält $12345678, wenn die folgenden Daten gültig sind, d.h. wenn wirklich ein Bus- bzw. Adress-Fehler auftrat.
$3844.L - $3a3 enthält den Inhalt der Datenregister D0-D7 in dieser Reihenfolge.
$3a4.L - $3bf enthält den Inhalt der Adressregister A0-A6.
$3c0.L enthält den Supervisor-Stack (SSP).
$3c4.L enthält den Inhalt Exceptionvektors
$3c8.L enthält den User-Stack (USP).
$3cc.W bis $3eb enthält 16 Wort vom Stack.

Tabelle 1. Die Belegung des Debug-Bereichs $380

Oft reicht diese »bombige« Information allerdings nicht aus. Im Falle eines Bus- oder eines Adress-Fehlers speichert das Betriebssystem deswegen detailliertere Informationen ab. Diese finden Sie ab Speicheradresse $380 (Tabelle 1). Das Langwort in $380 gibt an, ob die folgenden Daten gültig sind. Wenn ja, enthält $380 den Wert $12345678. Von $384 bis $3a3 liegt der Inhalt der Datenregister D0 bis D7 in dieser Reihenfolge. Von $3a4 bis $3bf finden Sie den Inhalt der Adressregister A0 bis A6. $3c0.L enthält den Supervisor-Stackpointer (SSP), $3c4.L die Adresse, auf die der Exceptionvektor zeigte, und $3c8.L den Inhalt des User-Stack-pointers (USP). Schließlich finden Sie im Bereich von $3cc bis $3eb die obersten 16 Worte vom Stack. Das Betriebssystem legt alle gespeicherten Informationen unmittelbar nach Auslösen der Exception ab. Damit ist gewährleistet, daß die Register noch den ursprünglichen Inhalt besitzen. Listing 1 zeigt, wie Sie den Bereich ab $380 in C und Omikron-Basic auswerten. (ba)

0 IF LPEEK($380)<>$12345678 THEN PRINT "$380-Bereich ungÅltig": END
1 FOR D%L=0 TO 7: PRINT "D";D%L;" = "; HEX$( LPEEK($384+D%L*4)): NEXT D%L
2 FOR A%L=0 TO 6: PRINT "A";A%L;" = "; HEX$( LPEEK($3A4+A%L*4)): NEXT A%L
3 PRINT "SSP = "; HEX$( LPEEK($3C0))
4 PRINT "Exceptionnummer = "; HEX$( LPEEK($3C4))
5 PRINT "USP = "; HEX$( LPEEK($3C8))
6 PRINT "Stack:";
7 FOR D%L=0 TO 7: PRINT HEX$( LPEEK($3CC+D%L*4));",";: NEXT D%L

/*--------- $380-Abfrage in C ---------*/
#include <tos.h>	/* bzw. <osbind.h> */
#include <stdio.h>

struct DEBUGINFO {
	unsigned long magic;
	unsigned long dregs[8];
	unsigned long aregs[7];
	unsigned long ssp;
	unsigned long excep_no;
	unsigned long usp;
	unsigned int stack[16];
} *dinfo;

main()
{
  long ssp;
  int i;

	ssp = Super(0l);
	dinfo = (struct DEBUGINFO *)0x380l;
	if (dinfo->magic != 0x12345678)
		printf("\n$380-Bereich ungÅltig");
	else {
		for (i = 0; i < 8; i++)
			printf("\nD%d = $%08lx",i,dinfo->dregs[i]);
		for (i = 0; i < 7; i++)
			printf("\nA%d = $%08lx",i,dinfo->aregs[i]);
		printf("\nSSP = $%08lx",dinfo->ssp);
		printf("\nExceptionnummer = $%08lx",dinfo->excep_no);
		printf("\nUSP = $%#08lx",dinfo->usp);
		printf("\nStack: ");
		for (i = 0; i < 16; i++)
			printf("$%04x,",dinfo->stack[i]);
	}
	Super(ssp);
}

Listing 1. Die Auswertung der Debug-Informationen im $380-Bereich unter Omikron-Basic und C

Das Format einer Dosound-Melodie

Kaum zu glauben, aber der ST ist tatsächlich musikalisch. Denn er stellt Programmierern oder vielmehr Musikern eine Betriebssystemfunktion (XBIOS 32, »Dosound«) zur Verfügung, mit der Sie beliebige Melodien abspielen können. Das ganze geht im Hintergrund vor sich, so daß das Programm unmittelbar nach dem Aufruf normal weiterläuft. Dazu nutzt die Abspielroutine den 200 Hz-Timer C. Als Argument benötigt die Funktion die Adresse der Soundbefehle. Diese arbeiten mit den Registern und Fähigkeiten des ST-Soundchips. Folgende Soundbefehle stehen zur Verfügung:

Befehle $0-$f
Diese Befehle erwarten ein Argument, das in das der Befehlsnummer ($0-$f) entsprechende Register des Soundchips geladen wird. Die Bytefolge 0,5 schreibt beispielsweise den Wert 5 in das Soundregister 0.

Befehl $80
Diesem Befehl folgt ein Argument, das in ein temporäres Register geladen wird. Dieser Befehl ist nur in Zusammenarbeit mit Befehl $81 sinnvoll, der den Inhalt des temporären Registers weiterverarbeitet.

Befehl $81
Diesem Befehl müssen drei Argumente (Bytes) folgen. Das erste Argument ist die Nummer des Soundchip-Registers, in das der Inhalt des temporären Registers (siehe Befehl $80) gelangt. Das zweite Argument ist ein Zweierkomplement, das zum Inhalt des temporären Registers addiert wird. Dieser Vorgang wiederholt sich solange, bis der Inhalt des temporären Registers gleich dem dritten Argument ist.

Befehl $82-$ff
Diesen Befehlen folgt jeweils ein weiteres Argument. Lautet dieses Argument Null, so bricht die Soundverarbeitung ab. Ansonsten gibt dieses Argument an, wieviele Timerticks (2ms, 50 Hz) bis zur Verarbeitung des nächsten Soundbefehls vergehen.

Listing 2 zeigt, wie Sie in Omikron-Basic über die Do-sound-Funktion eine Melodie abspielen. (ba)

0 DATA 0,0,1,10,7,%110110,8,16,11,0,12,5,13,11,$FF,0,$FE
1 Schuss%L= MEMORY(100):A%L=0: READ I%L
2 WHILE I%L<>$FE
3   POKE Schuss%L+A%L,I%L:A%L=A%L+1: READ I%L
4 WEND
5 XBIOS (,32, HIGH(Schuss%L), LOW(Schuss%L))

Listing 2. Die Dosound-Routine unter Omikron-Basic

Beliebige Speicherkonfigurationen simulieren

Für Programmentwicklungen ist es oft wichtig, auszutesten, ob das Programm mit anderen Speicherkonfigurationen zurechtkommt. Dabei sind vor allem die »speicherarmen« Konfigurationen 512 KByte und 1 MByte interessant. Das Betriebssystem stellt dazu diverse Systemvariablen zur Verfügung, die es erlauben, eine niedrigere Konfiguration softwaremäßig zu simulieren.

Die dazu wichtigste Systemvariable $42e.L (»phystop«) bestimmt das Ende des physikalischen Speichers. Bei einem 1 MByte-Rechner enthält die Variable den Wert $100000, bei einem 4 MByte-Rechner $400000. Wollen Sie nun beispielsweise einen 512 KByte-Rechner simulieren, so schreiben Sie als neue physikalische Grenze den Wert $80000.

Nun müssen Sie sicherstellen, daß das Betriebssystem den neuen Wert bei einem Reset auch akzeptiert. Denn normalerweise ermittelt es die Speicherkonfiguration über den Memory-Controller und überschreibt den Wert in »phystop« mit der neu errechneten oberen Speichergrenze. Um dies zu unterbinden, gibt es drei sogenannte magische Systemvariablen. Enthalten diese einen bestimmten Wert, so übernimmt das Betriebssystem die obere Speichergrenze aus »phystop«, ohne den Memory-Controller zu konsultieren. Dazu muß im Langwort $420 (»memvalid«) der Wert $752019f3, in $43a (»memval2«) der Wert $237698aa und in $51a (»memval3«) der Wert $5555aaaa stehen.

Haben Sie die vier Systemvariablen »phystop«, »memvalid«, »memval2« und »memval3« entsprechend gesetzt, so müssen Sie einen Warmstart durchführen. Listing 3 demonstriert die Simulation eines 512 KByte-ST in GFA-Basic und Assembler. (ba)

!---— GFA-Basic-Routine zum Simulieren von 512 KB
~GEMDOS(32,0)   ! Supervisor anfordern
LPOKE &H42E,&H80000    ! $80000 = 512 KB
LPOKE &H420,&H752019F3 ! magic 1
LPOKE &H43A,&H237698AA ! magic 2
LPOKE &H51A,&H5555AAAA ! magic 3
a%=LPEEK(4)   ! Resetadresse
CALL a%       ! Warmstart durchführen

; -------- Die Assemblerroutine --------
sim512K:
	clr.l	-(sp)
	move.w	#$20,-(sp)
	trap	#1	; enter Supervisor
	move.l	#$80000,$42e	; $80000 = 512 KB
	move.l	#$752019f3,$420 ; magic 1
	move.l	#$237698aa,$43a ; magic 2
	move.l	#$5555aaaa,$51a	; magic 3
	move.l	4,a0	; warmstart durchführen
	jmp	(a0)

Listing 3. Simulieren von 512 KB in GFA-Basic und Assembler

Die erweiterte Farbpalette des STE

Eine der wichtigsten und interessantesten Neuerungen des STE im Vergleich zum Mega ST und den »alten« STs ist seine erweiterte Farbpalette mit 4096 statt bisher 512 Farbtönen. Auf dem STE liegt die Farbpalette mit seinen 16 Registern - für max. 16 Farben gleichzeitig - wie bisher ab der Adresse $ff8240. Der 16 Bit-Wert eines Registers läßt sich auf dem STE wie im Bild dargestellt aufschlüsseln. Jedem Farbanteil Rot, Grün und Blau entsprechen nun 4 Bit (0-15). Um farbkompatibel zu den älteren Modellen mit nur 3 Bit zu bleiben, ist die Wertigkeit der Bits 0 und 3 vertauscht. Der Registerwert %1100 für Rot stellt demnach den Rotanteil 9 (%1001) dar. (ba)

Die Farbregister des STE haben vier Bit pro Farbanteil

GDOS-Fonts laden

Der ST verfügt nicht nur über seine internen Systemzeichensätze. Über die »ASSIGN.SYS«-Datei bestimmen Sie, welche Zeichensätze für welche Auflösungen zusätzlich zur Verfügung stehen. Listing 4 demonstriert das ordnungsgemäße Installieren, Anzeigen und De-Installieren der verfügbaren GDOS-Fonts für SPC-Modula-2.

Das Programm geht wie folgt vor: Zunächst macht es alle im System über »ASSIGN.SYS« angemeldeten Zeichensätze mit der VDI-Funktion »load_fonts« verfügbar. Die Funktion gibt beim ersten Aufruf die Anzahl der neu geladenen Zeichensätze zurück. Nun erfragen wir die Zeichensatznummer und geben die erhaltenen Informationen auf dem Bildschirm aus. Mit der Funktion »set_font« selektieren Sie den aktuellen Zeichensatz. Dabei übergeben Sie einfach seine Zeichensatznummer. Am Ende löschen wir alle geladenen Fonts mit »unload_fonts«. (ba)

MODULE GDosFontTest;
IMPORT	VDIAttributes,
		VDIControls ,
		VDIInquires ,
		AESGraphics ,
		InOut	;
VAR 	FontName	:ARRAY [0..31] OF INTEGER;
		FontNameStr	:ARRAY [0..31] OF CHAR;
		FontNr		:INTEGER;
		Search		:CARDINAL;
		VDIHandle	:INTEGER; 
		i			:CARDINAL;
		In			:VDIControls.WorkstationInitRec;
		Out			:VDIControls.WorkstationDescription;

PROCEDURE InitVDI;
	BEGIN
		WITH In DO
			DeviceId		:=	1;
			LineStyle		:=	VDIAttributes.Solid;
			LineColour		:=	1;
			MarkerType		:=	VDIAttributes.Dot;
			MarkerColour	:=	1;
			Font			:=	VDIAttributes.BigFont;
			TextColour		:=	1;
			FillStyle		:=	VDIAttributes.Filled;
			FillIndex		:=	1;
			FillColour		:=	1;
			CoordinateSystem:= VDIAttributes.RasterCoords; 
		END;
		VDIHandle:=AESGraphics.Handle(VDIHandle, VDIHandle,VDIHandle,VDIHandle); 
		VDIControls.OpenVirtualWorkstation(In,VDIHandle,Out);
		INC (Out.MaxFonts,VDIControls.LoadFonts(VDIHandle, 0));
	END InitVDI;

PROCEDURE TermVDI;
	BEGIN
		VDIControls.UnloadFonts(VDIHandle, 0);
		VDIControls.CloseVirtualWorkstatior(VDIHandle);
	END TermVDI;

BEGIN
	InitVDI;
	FOR Search:=1 TO Out.MaxFonts DO
		FontNr :=VDIInquires.InquireFaceName(VDIHandle,Search,FontName);
		FOR i:=0 TO 31 DO
			FontNameStr[i]:=CHR(FontName[i])
		END;
		InOut.WriteInt(FontNr,5);
		InOut.WriteString('  ');
		InOut.WriteString(FontNameStr);
		InOut.WriteLn;
	END;
	TermVDI;
END GDosFontTest.

Listing 4. GDOS-Fonts unter SPC-Modula-2 (von M. Krischik)

Eigene Maus- und Joystick-Routinen

Für Spiele und spezielle Applikationen ist es oft notwendig, die vom Betriebssystem installierte Maus- oder Joystick-Routine durch eine eigene zu ersetzen. Über die XBIOS-Funktion 34 (»Kbdvbase«) erhalten Sie einen Zeiger auf eine Vektortabelle, über die Sie neben der Maus-Routine unter anderem auch eine eigene Joystick-Routine installieren können. Tabelle 2 zeigt den Aufbau der Vektortabelle. Der Tabelleneintrag 5 (Offset 16) enthält die Adresse der aktuellen Maus-Routine und Eintrag 7 (Offset 24) die Adresse der aktuellen Joystick-Routine. Die anderen Einträge dienen zum Abfangen von Tastatur- und MIDI-Übertragungsfehlern, sowie zum Verwalten des Keyboard-, Uhrzeit- und MIDI-Ports.

Um einen Vektor auf eine eigene Routine umzulegen, überschreiben Sie einfach den Tabelleneintrag mit der neuen Adresse. Dabei gehört es zum guten Stil, den alten Eintrag zuvor zu retten und bei Programmende wieder zurückzuschreiben. Ihre Routine erhält ab nun die entsprechenden Datenpakete. Sollten Sie die Ml-Dl-Port-Routine ersetzt haben, so erhalten Sie das übertragene Byte in DO. Alle anderen Routinen der Vektortabelle - so auch die Maus- und Joystick-Routine - erhalten einen Zeiger auf das übertragene Datenpaket in AO und dem Stack. Die Routinen werden mit »rts« abgeschlossen und sollten nicht länger als 1 ms Zeit beanspruchen. Listing 5 demonstriert die Joystick-Verwaltung in Assembler. Sie können es als Gerüst für eigene Joystick-Routinen verwenden. Brechen Sie das Programm mit Knopfdruck ab. (ba)

Offset Bezeichnung Bedeutung
$0 midivec MIDI Eingabe (Datenbyte in D0.b)
$4 vkbderr Tastaturfehler
$8 vmierr MIDI-Fehler
$c statvec IKBD-Status
$10 mousevec Maus-Verwaltung
$14 clockvec Uhrzeit-Verwaltung
$18 joyvec Joystick-Verwaltung

Tabelle 2. Die KDBVBASE-Vektortablle

	bsr	init	; Alles vorbereiten
	bsr	request	; Joystick-Abfrage
	bsr	deinit	; Alles wieder herstellen
	clr.w	-(sp)
	trap	#1	; chiao

init:
	pea	joy_on
	move.w	#1,-(sp)
	move.w	#25,-(sp)
	trap	#14	; Joystick aktivieren
	addq.l	#8,sp
	move.w	#34,-(sp)
	trap	#14	; Joy/mouse-Vektoren setzen
	addq.l	#2,sp
	move.l	d0,a0
	lea	$18(a0),a0
	move.l	a0,oldjoyadr	; Alte Adresse merken
	move.l	(a0),oldjoyvec
	move.l	#joyvec,(a0)	; Neue Adr. setzen	
	rts
deinit:
	move.l	oldjoyadr,a0	; Alte Joystick-Routine
	move.l	oldjoyvec,(a0)	; wieder setzen
	pea	joy_off
	move.w	#1,-(sp)
	move.w	#25,-(sp)
	trap	#14		; Joystick deaktivieren
	addq.l	#8,sp
	rts

; Eigener Joystick-Vektor
joyvec:
	cmp.b	#$ff,(a0)	; Joystick 1?
	bne	nothing
	move.b	2(a0),joystate  ; Bit-kodiert
nothing:rts

; Bit 0=hoch,1=runter,2=links,3=rechts,7=Knopf (auch kombin.)
; kehrt bei Knopfdruck zurÅck
request:
	move.b	joystate,d7
	btst	#7,d7	; Knopf gedrÅckt?
	beq	next1
	lea	strbutton,a0
	bra	print	; bei Knopf Schleife verlassen
next1:	btst	#0,d7	; Hoch?
	beq	next2
	lea	strtop,a0
	bsr	print
next2:	btst	#1,d7	; Runter?
	beq	next3
	lea	strdown,a0
	bsr	print
next3:	btst	#2,d7	; Links?
	beq	next4
	lea	strleft,a0
	bsr	print
next4:	btst	#3,d7	; Rechts?
	beq	next5
	lea	strright,a0
	bsr	print
next5:	bra	request

print:
	pea	(a0)
	move.w	#9,-(sp)
	trap	#1
	addq.l	#6,sp
	rts
	
joy_on:		dc.b	$12,$14
joy_off:	dc.b	$15,$8
strbutton:	dc.b	13,10,"Knopf gedrückt!",0
strtop:		dc.b	13,10,"Hoch",0
strdown:	dc.b	13,10,"Runter",0
strleft:	dc.b	13,10,"Links",0
strright:	dc.b	13,10,"Rechts",0
BSS
oldjoyadr:	ds.l	1
oldjoyvec:	ds.l	1
joystate:	ds.b	1
END

Listing 5. Das Gerüst einer Joystick-Verwaltung in Assembler

Schnelle Sprites in Farbe

Wer schon einmal versucht hat, ein Spiel - vielleicht sogar ein Ballerspiel - zu schreiben, der hat sicherlich festgestellt, daß die Sprite-Routinen des Betriebssystems nicht ideal sind. Einerseits sind sie zu langsam und andererseits erlauben sie nur Objekte der Größe 16x16 Pixel. Listing 6 stellt eine flexible Routine dar, die beliebig hohe und 16 Pixel breite Sprites auf dem Bildschirm darstellt. Dazu übergeben Sie fünf Parameter: die x- (DO.w) und y-Koordinate (Dl.w) der oberen linken Ecke des Bildschirm, an der das Objekt erscheinen soll, die Höhe des Objekts in Bildschirmzeilen (D2.w), die Bildschirmbasis (A0) und die Adresse der Objektdaten (A1). Die Objektdaten sind zeilenweise abgelegt, wobei 8 Byte (4 Bitplanes a 16 Bit) einer Objektzeile entsprechen.

Die Routine berechnet sich aus den Koordinaten und der Bildschirmbasis die Bildschirmzieladresse und den Bitoffset. Nun kopiert es das Objekt in einer Schleife zeilenweise in den Bildschirmspeicher. Dabei verwendet es den ODER-Schreibmodus. Denn somit bleibt der ursprüngliche Hintergrund bei nicht gesetzten Punkten des Objekts bestehen. (ba)

; d0=x,d1=y,d2=h,a0=bildschirmbasis,a1=spritedaten
show_16sprite:
	movem.l	d0-a6,-(sp)
	move.w	d1,d3	; y-Koordinate
	lsl.w	#5,d3	; x 32
	lsl.w	#7,d1	; x 128
	add.w	d3,d1	; -> x 160
	move.w	d0,d3	;x-Koordinate
	moveq.l	#16,d4
	and.w	#$f,d3	; pixels to shift
	sub.w	d3,d4
	;
	and.b	#$f0,d0
	lsr.w	#1,d0	;XPOS/2
	add.w	d1,d0	;y+x
	add.w	d0,a0	;add offset to base screen address
	;
	lea	16(a0),a0  ; da mit -(Ax) operiert wird
	subq	#1,d2	; ZeilenzÑhler
	move.w	d2,d6
nextline:
	moveq.l	#0,d0
	moveq.l	#0,d1
	moveq.l	#0,d2
	moveq.l	#0,d3
	move.w	(a1)+,d0
	move.w	(a1)+,d1
	move.w	(a1)+,d2
	move.w	(a1)+,d3
	lsl.l	d4,d0	;shift all bit planes
	lsl.l	d4,d1
	lsl.l	d4,d2
	lsl.l	d4,d3
	move.l	d0,d5	; Maske bilden
	or.l	d1,d5
	or.l	d2,d5
	or.l	d3,d5
	not.l	d5
	and.w	d5,-(a0)  ;now map data onto the screen
	or.w	d3,(a0)
	swap	d3
	and.w	d5,-(a0)
	or.w	d2,(a0)
	swap	d2
	and.w	d5,-(a0)
	or.w	d1,(a0)
	swap	d1
	and.w	d5,-(a0)
	or.w	d0,(a0)
	swap	d0
	swap	d5	; nÑchste 16 Bit
	and.w	d5,-(a0)
	or.w	d3,(a0)
	and.w	d5,-(a0)
	or.w	d2,(a0)
	and.w	d5,-(a0)
	or.w	d1,(a0)
	and.w	d5,-(a0)
	or.w	d0,(a0)
	lea	176(a0),a0  ;set up for next line
	dbf	d6,nextline
	movem.l	(sp)+,d0-a6
	rts

Listing 6. Schnelle farbige Sprites in Assembler

Integer-Floats ersetzen Floats

Rotationen, perspektivische Verzerrungen, Verkleinerungen und Vergrößerungen von grafischen Objekten sind nur einige Beispiele, in denen man der Genauigkeit wegen besser mit Fließkommazahlen als mit Integerzahlen rechnet. Doch eins ist gewiß: Die Berechnungsgeschwindigkeit leidet darunter enorm. Um einerseits genau zu rechnen, aber andererseits in der Geschwindigkeit von Integeroperationen zu arbeiten, gibt es einen einfachen Trick, der vor allem in schnellen 3D-Programmen eingesetzt wird: Integerzahlen, bei denen ein Teil den Vor- und ein anderer den Nachkommateil darstellt. Listing 7 demonstriert die Verwendung von Integer-Floats anhand einfacher Rechenoperationen und Typumwandlungen in C.

Für ein durchschnittliche Genauigkeit reicht es, einen 16 Bit-Vor- und einen 16 Bit-Nachkommateil -zusammen ein Langwort (32 Bit) - zu verwenden.

Um nicht alle Float-Operationen wie etwa »arcsin« gänzlich neu zu schreiben, können Sie Integer-Floats in echte Fließkommazahlen umwandeln und anders rum. Elementare Fließkommaoperationen wie »sin« und »cos« können Sie stark beschleunigen: Sie legen für eine vernünftige Auflösung - sagen wir 1/10 Grad - die vorberechneten Integer-Floats in einer Tabelle ab. Nun können Sie Berechnungen durch Tabellenzugriffe ersetzen. (ba)

/* Long: 11111111 11111111 11111111 11111111 */
/*       ----------------- ----------------- */
/*         Vorkommawert       Nachkommawert  */

#include <stdio.h>
#include <math.h>
typedef	unsigned long INTFL;	/* Integerfloat */

double intfl_to_double(INTFL i) {
	return (double)(i>>16)+((double)(i&0xffff)/(double)65536);
}

INTFL double_to_intfl(double f) {
  double f2 = floor(f);
	return ((INTFL)f2<<16)+((f-(double)(INTFL)f2)*(double)65536);
}

main()
{
  INTFL i = 0x00010000;	/* i = 1.0 */

	for (; i < 0x000a0000; i += 0x00000010)
		printf("\ni = $%lx (%lf)",i,intfl_to_double(i));
	printf("\n$%lx=1.5,$%lx=2.25,$%lx=10.9",
		double_to_intfl((double)1.5),
		double_to_intfl((double)2.25),
		double_to_intfl((double)10.9));
	printf("\n%lf=$18000,%lf=$24000,%lf=$ae666",
		intfl_to_double((INTFL)0x00018000),
		intfl_to_double((INTFL)0x00024000),
		intfl_to_double((INTFL)0x000ae666));
}

Listing 7. Mit diesem Gerüst können Sie sich in C eine eigene Integer-Float-Library aufbauen



Aus: TOS 06 / 1990, Seite 94

Links

Copyright-Bestimmungen: siehe Über diese Seite