• Please review our updated Terms and Rules here

BASIC and the 8087

raifield

Experienced Member
Joined
Aug 3, 2010
Messages
174
Location
NJ, USA
I've been doing some 3d programming on a Tandy 1000SX and I was wondering if there was a specific way to invoke the 8087 coprocessor. I know QuickBASIC 3.0 had a special executable that was compatible with FPU's, but I'm using QuickBASIC 4.5, mostly due to the fact that every download of QB 3.0 does not contain the QB87.exe file for coprocessors. However, is there anything I need to do in QB 4.5 to invoke the coprocessor to perform, say, trignometry operations, is the FPU being called automatically, or is my only hope to CALL assembly routines pointing to the FPU?

I've tried searching for this information for days, but there is a complete lack of information about using the math coprocessors in general.
 
You could try timing a tight loop with a large number of FDIVs, then work back to see if the clock cycles per iteration would be possible without the FPU?
 
I'm trying basic 3d animations, so there is quite some trigonometry involved, which I want to make sure the FPU is handling. But it sounds like there is no way in BASIC to "send" commands to the FPU. Is the process automatic with a supported version of QuickBASIC or is it simply not supported at all? I'm not entirelly sure what you mean by running a loop of FDIVs, though that lends to my theory that the only way to use a FPU in BASIC is to call assembly-level routines within the program.
 
It depends on the version. Prior to QB 4.0, you had to use the QB87 executable, as the native QB floating representation was not IEEE (used by the 8087).

All versions of QB after 4.0 use IEEE floating and will use the NDP if available. On the downside, they're also slower if no NDP is installed.

See about halfway down under "Execution Speed".

Oh, and make sure that the environment variable is not defined!

If you run MSD, does it show a coprocessor installed?
 
Last edited:
If you're doing 3d math in DOS, particularly for an 80286 or lower like the SX, you shouldn't even be THINKING FPU. Even when the FPU is present, it's just NOT fast enough for 3d graphics -- NOT EVEN CLOSE. You're talking pushing and popping massive values (or worse if you do the matrix idiocy massive arrays) which will drag performance down to the deepest PIT of hell.

Typically doing 3d math there are only three routines you need in float -- sin, cos and arctan. Multiply their results by your needed accuracy (usually -32767..32767 is overkill for 640x480 or less), store them in an array of your desired granularity (0..720 is usually enough at that low a resolution), then do everything with word multiplies for longint results, then discard the bottom 16 bits after a multiply. (if you REALLY need more range in the result, you can do 8 bits, but -127..127 is a bit restrictive).

Even with a 8087 installed, floating point math on anything less than a 486 if you're talking about doing it for graphics -- completely impractical. As slow as a 8088 integer MUL is (118-133 clocks 8088, 21 clocks 80286, 2 byte operation on registers), it's piss in the sand compared to an FMUL (130-145 clocks 8087/80287, 4 byte operation, halt of CPU activity line, 8 to 40 bytes of stack activity!)

It's why you rarely if ever find floating point numbers in old games or applications -- even the 3d ones.

Basically XLAT is going to be faster than sin/cos/atan, and integer 'fixed point' math is faster than floating point math.
 
Last edited:
Thanks for the replies. It seems that while it may be possible to use the 8087 for 3D calculations, the speed simply isn't there.

Deadshadow, thanks for the detailed explanation. It seems that to get any greater speed I need to revise and optimize for the 8088 instead of trying to involve the 8087. Moving away from BASIC probably couldn't hurt either.
 
to get any greater speed I need to revise and optimize for the 8088 instead of trying to involve the 8087. Moving away from BASIC probably couldn't hurt either.

Spot-on, Turbo Pascal or Turbo C will run MUCH faster. Nothing wrong with using the FPU if it's there of course, although integer operations on the 8088 are quicker than floating-point on the 8087.
 
Thanks for the replies. It seems that while it may be possible to use the 8087 for 3D calculations, the speed simply isn't there.

To clarify:
  • If you are performing complicated math that requires high precision, the 8087 is definitely faster than trying to emulate IEEE floating point in software.
  • If you are attempting to do something that can be optimized away from requiring high precision, such as realtime 3-D games, then it is faster to simplify your calculations and requirements and use the 8088's integer math.
Most people are unaware that the 8087 is a co-processor. Meaning, it can work on an operation while the 8088 is off doing other things, which means it is doing high-precision math for you "for free". While it should be possible to have both speed and high precision with the 8088 and 8087 working in tandem, pretty much all HLL (basic, pascal, C, etc.) will give the 8087 something to do and then sit and wait for it to complete before moving on. Truly interleaving both processors requires writing your program to take advantage of this, and writing it in assembly language.
 
I'm going to switch to C instead of BASIC and see what that gets me, though I don't know C very well. Running interpreted BASIC, I get about 6 frames a second rotating a 3D cube without background culling. Compiled I get about 10 frames a second. I'm hoping to get to the point where I can draw a simple grid to represent the "ground" and walk through a corridor at 12-15 frames a second. Seems that the 8087 is going to be sitting this one out, unless I get some free time to delve into Assembly alongside C.
 
If you're trying to write a realtime 3-D game, then yes, the 8087 is not something you should be targeting right now. You should also think about not using software floating-point; try defining a 16.16 fixed-point struct and writing simple add/sub/mul/div routines for it.
 
If you post the code you're working with, maybe we can give you some speedup pointers. Some things like the divide for perspective and final world rotation can be.. sidestepped, speeding things up greatly.
 
Sure, though the code isn't my own, it's from a 3D tutorial that I have been working off of, from http://www.petesqbsite.com/sections/tutorials/tutorials/tutor2.htm

Code:
'WIRE3DUO.BAS by Matt Bross, 1997

DEFINT A-Z 'defines all unmarked variables as integers

TYPE PointType 'defines user defined type PointType

x AS INTEGER 'X coordinate

Y AS INTEGER 'Y coordinate

z AS INTEGER 'Z coordinate

END TYPE 'end definition of type PointType

TYPE LineType 'defines user defined type LineType

C1 AS INTEGER 'number of the first point of a line

C2 AS INTEGER 'number of the second point of a line

CLR AS INTEGER 'color of the line

END TYPE 'end definition of type LineType

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%INFO%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SCREEN 0, 0, 0, 0 'set to screen mode 0

WIDTH 80, 25'set height to 25 and width to 80 c

CLS 'clear the screen

COLOR 15 'print in color 15

PRINT "WIRE3DUO.BAS by Matt Bross, 1997" 'print the text in quotes

PRINT

COLOR 7

PRINT "3D WIREFRAME ANIMATION FOR THE QBASIC LANGUAGE"

PRINT "USE FREELY AS LONG AS I RECIEVE CREDIT DUE"

PRINT

COLOR 15

PRINT "--------CONTROLS--------"

COLOR 7

PRINT " 0 - reset rotation"

PRINT " 5 - stop rotation"

PRINT " S - reset location"

PRINT " A - stop translation"

PRINT "2, 8 - rotate around x axis"

PRINT "4, 6 - rotate around y axis"

PRINT "-, + - rotate around z axis"

PRINT CHR$(24); ", "; CHR$(25); " - translate vertically"

PRINT CHR$(27); ", "; CHR$(26); " - translate horizontally"

PRINT "Z, X - translate depthwise"

PRINT " Esc - exit"

COLOR 15

PRINT "----CASE INSENSITIVE----"

PRINT

PRINT "LOADING 0%"

'------------------------------>BEGIN INIT<-----------------------------

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%LOAD VARIABLES%%%%%%%%%%%%%%%%%%%%%%%%%%%

'make fast lookup variables for calling values (e.g. if x = 1 is used

'often,x = 1, is slower than one = 1: x = one)

ZERO = 0: ONE = 1: THREESIXD = 360

intMAX = 32767: intMIN = -32768

bitsX10 = 1024

A$ = "A": S$ = "S": z$ = "Z": x$ = "X"

ZERO$ = "0": TWO$ = "2": FOUR$ = "4": FIVE$ = "5": SIX$ = "6"

EIGHT$ = "8"

PLUS$ = "+": MINUS$ = "-": EQUALS$ = "="

UP$ = CHR$(0) + "H": down$ = CHR$(0) + "P"

LEFT2$ = CHR$(0) + "K": RIGHT2$ = CHR$(0) + "M"

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%SCREEN VARIABLES%%%%%%%%%%%%%%%%%%%%%%%%%%%

D = 300: SD = 60 'distance and perspective

MaxSpin = 20: MaxSpeed = 2 'max. translate and rotate

ScnMode = 7: m = 1: b = 0 'screen mode and pages

IF ScnMode <> 7 AND ScnMode <> 9 THEN ScnMode = 7

IF ScnMode = 7 THEN DX = 160: DY = 100 'center of screen mode 7

IF ScnMode = 9 THEN DX = 320: DY = 175 'center of screen mode 9

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%MAKE TABLES%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

DIM SINx(360) AS LONG 'initialize array for sines

DIM COSx(360) AS LONG 'initialize array for cosines

FOR i = 0 TO THREESIXD 'loop 360 times

'Create sine and cosine tables of all degrees 0 to 360 in radians and

'scale by 1024 or 10 bits and store as a 4-byte integer. The numbers

'will be scaled down again in the main loop. This is called fixed point

'math and is faster that floating point, or math with a decimal.

SINx(i) = SIN(i * (22 / 7) / 180) * bitsX10

COSx(i) = COS(i * (22 / 7) / 180) * bitsX10

IF i MOD 40 = 0 THEN LOCATE 22, 9: PRINT STR$(i \ 4) + "%"

NEXT

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%LOAD POINTS%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

RESTORE PointData 'Set pointer to read from label PointData.

READ MaxPoints 'Reads MaxPoints from appended data.

DIM points(1 TO MaxPoints) AS PointType 'points at start

DIM ScnPnts(1 TO MaxPoints) AS PointType 'points after rotation

DIM SX(1 TO MaxPoints), SY(1 TO MaxPoints) 'points drawn to screen

FOR i = 1 TO MaxPoints: READ points(i).x, points(i).Y, points(i).z: NEXT

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%LOAD LINES%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

RESTORE LineData 'Set pointer to read from label LineData.

READ MaxLines 'Reads MaxLines from appended data.

DIM l(1 TO MaxLines) AS LineType 'line data

DIM OLX1(1 TO MaxLines), OLY1(1 TO MaxLines) 'old line data

DIM OLX2(1 TO MaxLines), OLY2(1 TO MaxLines)

FOR i = 1 TO MaxLines: READ l(i).C1, l(i).C2, l(i).CLR: NEXT

'-------------------------------->END INIT<-----------------------------

LOCATE 22, 9: PRINT "100%"

PRINT "Press a Key"

DO: LOOP UNTIL INKEY$ <> "" 'Do a loop until a key is pressed.

SCREEN ScnMode, 0, 0, 0 'set screen mode ScnMode

'----------------------------->BEGIN MAIN LOOP<-------------------------

DO

'*********************************GET KEY*******************************

K$ = UCASE$(INKEY$) 'get the upper case equivalent of input from the

keyboard

SELECT CASE K$ 'select which case the string variable k$ is

CASE ZERO$

R1 = ZERO: R2 = ZERO: R3 = ZERO 'stop rotation and reset it

D1 = ZERO: D2 = ZERO: D3 = ZERO

CASE FIVE$

D1 = ZERO: D2 = ZERO: D3 = ZERO 'stop rotation

CASE A$

MX = ZERO: MY = ZERO: MZ = ZERO 'stop translation

CASE S$

MX = ZERO: MY = ZERO: MZ = ZERO 'stop translation and reset it

MMX = ZERO: MMY = ZERO: MMZ = ZERO

CASE TWO$

D1 = D1 - ONE 'rotate counter-clockwise around the x axis

CASE EIGHT$

D1 = D1 + ONE 'rotate clockwise around the x axis

CASE FOUR$

D2 = D2 - ONE 'rotate counter-clockwise around the y axis

CASE SIX$

D2 = D2 + ONE 'rotate clockwise around the y axis

CASE PLUS$, EQUALS$

D3 = D3 - ONE 'rotate clockwise around z axis

CASE MINUS$

D3 = D3 + ONE 'rotate counter-clockwise around the z axis

CASE UP$

MY = MY + ONE 'translate positively along the y axis

CASE down$

MY = MY - ONE 'translate negatively along the y axis

CASE LEFT2$

MX = MX + ONE 'translate positively along the x axis

CASE RIGHT2$

MX = MX - ONE 'translate negatively along the x axis

CASE z$

MZ = MZ + ONE 'translate positively along the z axis

CASE x$

MZ = MZ - ONE 'translate negatively along the z axis

CASE CHR$(27)

GOTO ShutDown 'end program

END SELECT

'*********************************ROTATION******************************

'keep sines and cosines in limits of the arrays

R1 = (R1 + D1) MOD THREESIXD

R2 = (R2 + D2) MOD THREESIXD

R3 = (R3 + D3) MOD THREESIXD

IF R1 < ZERO THEN R1 = THREESIXD + R1

IF R2 < ZERO THEN R2 = THREESIXD + R2

IF R3 < ZERO THEN R3 = THREESIXD + R3

'********************************TRANSLATION****************************

MMX = MMX + MX: MMY = MMY + MY: MMZ = MMZ + MZ

'keep variables within limits of integers

IF MMX > intMAX THEN MMX = intMAX

IF MMY > intMAX THEN MMX = intMAX

IF MMZ > intMAX THEN MMZ = intMAX

IF MMX < intMIN THEN MMX = intMIN

IF MMY < intMIN THEN MMX = intMIN

IF MMZ < intMIN THEN MMZ = intMIN

'******************************ROTATE POINTS****************************

S1& = SINx(R1): S2& = SINx(R2): S3& = SINx(R3)

C1& = COSx(R1): C2& = COSx(R2): C3& = COSx(R3)

FOR i = 1 TO MaxPoints

'Rotate points around the y axis.

TEMPX = (points(i).x * C2& - points(i).z * S2&) \ bitsX10

TEMPZ = (points(i).x * S2& + points(i).z * C2&) \ bitsX10

'Rotate points around the x axis.

ScnPnts(i).z = (TEMPZ * C1& - points(i).Y * S1&) \ bitsX10

TEMPY = (TEMPZ * S1& + points(i).Y * C1&) \ bitsX10

'Rotate points around the z axis.

ScnPnts(i).x = (TEMPX * C3& + TEMPY * S3&) \ bitsX10

ScnPnts(i).Y = (TEMPY * C3& - TEMPX * S3&) \ bitsX10

'*****************************CONVERT 3D TO 2D**************************

TEMPZ = ScnPnts(i).z + MMZ - SD

IF TEMPZ < ZERO THEN 'calculate points visible to the viewer

SX(i) = (ScnPnts(i).x + MMX) * D \ TEMPZ + DX

SY(i) = (ScnPnts(i).Y + MMY) * D \ TEMPZ + DY

END IF

NEXT

'******************************DRAW POLYGONS****************************

FOR i = 1 TO MaxLines

coord1 = l(i).C1: coord2 = l(i).C2

'erase old line

LINE (OLX1(i), OLY1(i))-(OLX2(i), OLY2(i)), 0

'get new points

OLX1(i) = SX(coord1): OLY1(i) = SY(coord1)

OLX2(i) = SX(coord2): OLY2(i) = SY(coord2)

'Draw line from (SX1, SY1) to (SX2, SY2) in color CLR

LINE (SX(coord1), SY(coord1))-(SX(coord2), SY(coord2)), l(i).CLR

NEXT

'******************************FRAME COUNTER****************************

F = F + 1

IF TIMER >= T# THEN

T# = TIMER + 1

FPS = F

LOCATE 1, 1

PRINT FPS: F = 0

END IF

'wait for vertical retrace, the electron beam to finish scanning the

'monitor

WAIT &H3DA, 8

LOOP

'----------------------------->END MAIN LOOP<---------------------------

ShutDown:

SCREEN 0, 0, 0, 0: WIDTH 80, 25: CLS

PRINT "Final Location of Points"

PRINT " X", " Y", " Z": PRINT STRING$(31, "-")

FOR i = 1 TO MaxPoints: PRINT ScnPnts(i).x, ScnPnts(i).Y, ScnPnts(i).z:

NEXT

PRINT : PRINT "Free space": PRINT " String Array Stack": PRINT

STRING$(21, "-")

PRINT FRE(""); FRE(-1); FRE(-2): END

PointData:

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%CUBE%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

'number of points

DATA 8

'Location of points (x, y, z)

DATA -10, 10,-10, -10,-10,-10, -10, 10, 10, -10,-10, 10

DATA 10, 10, 10, 10,-10, 10, 10, 10,-10, 10,-10,-10

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%PYRAMID%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

DATA 5

DATA 0, 10, 0, 0, 0,-10, 10, 0, 0, 0, 0, 10

DATA -10, 0, 0

LineData:

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%CUBE%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

'number of lines

DATA 12

'The points above can be numbered, the first data statement is 1. The

'points listed make lines from point 1 to point 2 in color 3.

DATA 1,2, 2, 1,3, 2, 3,4, 2, 2,4, 2, 1,7, 1, 3,5, 1, 5,7, 1

DATA 5,6, 1, 7,8, 1, 6,8, 1, 4,6, 1, 2,8, 1

'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%PYRAMID%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

DATA 8

DATA 1,2, 1, 1,3, 1, 1,4, 1, 1,5, 1, 2,3, 1, 3,4, 1, 4,5, 1

DATA 2,5, 1
 
Back
Top