home *** CD-ROM | disk | FTP | other *** search
- Pure C isn't
-
- If you have an Atari ST which has been upgraded with a 68882 maths
- co-processor (FPU) you may have run into problems using programs
- compiled with Pure C or Pure Pascal.
- The Pure C/Pascal compilers insert code in a program to detect the
- presence of an FPU and to use it if it is there.
- On 68020/68030 equipped machines it calls subroutines written in FPU
- machine code but on a 68000 equipped machine it has to use subroutines
- which emulate the CPU/FPU communication protocol built into the 68020
- and later chips.
-
- The problem is that the code in Pure C/Pascal is not a true emulation
- of that protocol. The CPU should read a response from the FPU and react
- as requested, but the emulation code waits for an expected response and
- does nothing until it gets it. The 68882 in many cases gives a
- different response from that given by the 68881. This is because the
- 68882 is 'pipelined'. It divides the execution of an floating point
- instruction into an interpretation/addressing phase and a calculation
- phase and is prepared to release the CPU to process in parallel with it
- at the end of the first phase.
-
- The following programs have been tested and appear to work OK:
-
- - Pre v1.5 versions of CAB. CAB was doing floating point calculations,
- (although Alexander denies it).
- - Kandinsky: Contains 5 subroutines to be patched.
- - HDDriver Utility program (partitioning won't work without the patch)
- - DAVector (demo version)
- - Zorg registered version (the un-registered version appears to unpack
- itself at run-time - in any case this patching program won't fix it)
- - Five-to-Five
- - Gemview v2.24 (and maybe other versions).
- - Atari CD Master from Homa Systems House (INFOPEDI.PRG and
- PLAYSND.PRG)
-
- The patch program may well work on other problem programs but neither I
- nor Atari Computing accept any liability or responsibility for any
- direct or indirect damage that may arise, either financial, material or
- any other kind from either the use or misuse of this software and
- associated documentation. All trademarks used are recognised and
- acknowledged.
-
- The following is a dissassembly of the standard Pure C FPU subroutine
- for operations between two floating point inputs. The entry to the code
- is at line 3. On entry a0 and a1 point to two floating point operands
- in memory and d2 contains the opcode for the FPU instruction to be
- executed.
-
- 1 l0 move.w #3,-14(a2) ; reset FPU (doesn't clear interrupts on
- 68882)
- 2 movea.l d1,a2 ; restore saved a2 - see line 4
-
- 3 moveq #$7F,d0 ; set time-out value
- ; **** entry point of subroutine ****
- 4 move.l a2,d1 ; save a2
- 5 lea -$5B0,a2 ; address of FPU operand register
- 6 l1 cmpi.w #$802,-16(a2) ; read FPU status - $802 means FPU idle
- 7 dbeq d0,l1 ; wait for idle
- 8 move.w #$4800,-6(a2) ; opcode - CPU tells FPU to accept an
- ; floating point number
- 9 l2 cmpi.w #$960C,-16(a2) ; FPU asks CPU to pass it 12 bytes
- 10 dbeq d0,l2 ; wait for response
- 11 move.l (a0),(a2) ; a0 points to first floating point number
- ; in memory
- 12 move.l 2(a0),(a2) ; Pure C packs floating point number into
- ; 10 bytes
- 13 move.l 6(a0),(a2) ; last 4 bytes of floating point number
- 14 l3 cmpi.w #$802,-16(a2) ; read FPU status again
- 15 dbeq d0,l1 ; wait for idle
- 16 move.w d2,-6(a2) ; pass opcode to FPU
- 17 l4 cmpi.w #$960C,-16(a2) ; FPU asks CPU to pass it 12 bytes
- 18 dbeq d0,l4 ; wait for response
- 19 move.l (a1),(a2) ; a1 points to second floating point number
- ; in memory
- 20 move.l 2(a1),(a2)
- 21 move.l 6(a1),(a2) ; last 4 bytes of floating point number
- 22 l5 cmpi.w #$802,-16(a2) ; read FPU status again
- 23 dbeq d0,l5 ; wait for idle
- 24 move.w #$6800,-6(a2) ; CPU asks FPU to hand over result
- 25 l6 cmpi.w #$B20C,-16(a2) ; FPU asks CPU to store 12 bytes
- 26 dbeq d0,l6 ; wait for response
- 27 BNE.S l0 ; try again if failed
- 28 move.l (a2),(a0) ; store result
- 29 move.l (a2),2(a0)
- 30 move.l (a2),6(a0)
- 31 movea.l d1,a2 ; restore a2
- 32 rts ; and exit
-
- The responses looked for at lines 9, 17 and 25 have meanings bit by
- bit. For example, the C at the end of $960C does ask for 12 bytes.
- However, the bit of interest is the msb, called the 'come again' bit.
- The 68881 wants the CPU to check with it again so it has that bit set,
- giving $960C, whereas the 68882 responds with $160C at both lines 9 and
- 17. At line 25 the 68882 will respond with $320C EXCEPT when the
- operation creates an exception, such as division by zero, when it
- responds with $B20C, as does the 68881 at all times.
-
- Note that checking for the incorrect response does not of itself
- prevent the program from working on the 68882. The DBEQ loops at l2, l4
- and l6 will terminate and the program will run, although slowly. It is
- the test at line 27 that is the killer. Because the preceeding dbeq has
- cleared the zero bit, the branch will be taken and the code goes into
- an infinite loop, which is how the program dies.
-
- The code can be made to work on both a 68881 and 68882 simply by
- replacing the BNE.S l0 ($668E) with an NOP ($4E71) but it will run
- considerably faster on a 68881. If the two occurrences of $960C are
- changed to $160C and the $B20C to $320C the 68882 will have the
- advantage.
-
- Most Pure C or Pascal programs contain several similar subroutines, one
- for each type of calculation that floating point numbers are involved
- in, in those programs. Each requires patching.
-
- Ideally, programmers doing floating point calculations in Pure C or
- Pure Pascal should modify the library code so that the FPU response is
- read into a CPU register and the msb masked off or set before testing.
- Then the code would run reasonably quickly on either chip.
-
- Fix FPU
- -------
- Here's a HiSoft BASIC listing for FIX_FPU.PRG a program which patches
- another program complied using either PURE C or PURE PASCAL so it will
- run on an Atari equipped with a 68000 and 68882 FPU.
- The FPU support code in both Pure C and Pure Pascal appears identical
- in both compilers.
-
- No guarantee is offered that the information in this distribution is
- correct but if you do find any mistakes let me know and I'll do my best
- to put them right!
-
- Contact
- -------
- David Leaver
- 10 Goodparla St,
- Hawker, ACT 2614
- Australia
-
- Email: leaver@netinfo.com.au
-
- Notes concerning the program listing
- ------------------------------------
- The odd bit of code around the call to dgetpath is there because, as
- far as I can tell, the library routine doesn't work as documented. It
- wants the buffer address and returns a TOS/C style string rather than a
- BASIC string.
-
- ---
- LIBRARY "gemdos", "gemaes"
-
-
- ? "PurePatch patches programs complied using either"
- ? "PURE C or PURE PASCAL so that it will run on an"
- ? "Atari equipped with a 68000 and 68882"
- ? : ?
- ? " ******** WARNING *********"
- ?
- ? "The input file is overwritten - if you have not"
- ? "backed it up, choose CANCEL at the file selector"
- ? : ?
- ? "Press a key to continue"
- while inkey$ = ""
- wend
- drive$ = CHR$(FNdgetdrv% + 65)
- path$ = "" : filename$ = ""
- for i% = 1 to 128 : path$ = path$ + CHR$(0) : next i%
- dgetpath sadd(path$) , 0
- fullpath$ = drive$ + ":"
- i% = 1
- temp$ = mid$(path$,i%,1)
- while ASC(temp$) <> 0
- fullpath$ = fullpath$ + temp$
- i% = i% +1
- temp$ = MID$(path$,i%,1)
- wend
- fullpath$ = fullpath$ + "\*.*"
- fsel_input fullpath$, filename$, ok%
- if ok% = 0 then end
- clearw 2
- ? "Patching ";filename$
- ? : ? "could take a minute ot two........"
- drive$ = LEFT$(fullpath$,1)
- fullpath$ = MID$(fullpath$,3,128)
- i% = LEN(fullpath$)
- while RIGHT$(fullpath$,1) <> "\"
- i% = i%-1
- fullpath$ = LEFT$(fullpath$, i%)
- wend
- drivemap% = FNdsetdrv%(ASC(drive$)-65)
- if FNdsetpath%(fullpath$)<>0 THEN
- ? " Error in setting path : "; fullpath$
- end
- end if
- handle% = FNfopen%(filename$,2)
- if handle% < 0 then
- ? " Error opening file - # "; handle%
- end
- end if
- buffer$ = CHR$(0)
- repeat readloop
- readbyte read&, byte%
- if read& = 0 then exit readloop
- if byte% = 12 then call patch
- end repeat readloop
- ok% = FNfclose%(handle%)
- end
-
- SUB readbyte(n&, byte%)
- SHARED handle%
- buffer$ = CHR$(0)
- n& = FNfread&(handle%,1,sadd(buffer$))
- byte% = ASC(buffer$)
- end SUB
-
- SUB writebyte(byte%)
- SHARED handle%
- buffer$ = CHR$(byte%)
- n& = FNfwrite&(handle%,1,sadd(buffer$))
- end SUB
-
- SUB patch
- SHARED handle%
- readbyte n&, byte%
- if n& = 1 then
- if byte% = 106 or byte% = 105 then
- readbyte n&, byte1%
- if n& = 1 then readbyte n&, byte2%
- if n& = 1 then readbyte n&, byte3%
- if n& = 1 then readbyte n&, byte4%
- if n& = 1 and byte3% = 255 and byte4% = 240 then
- if byte1% = 150 and byte2% = 12 then
- ok& = FNfseek&(-4, handle%, 1)
- writebyte 22
- elseif byte1% = 178 and byte2% = 12 then
- ok& = FNfseek&(-4, handle%, 1)
- writebyte 50
- ok& = FNfseek&(7, handle%, 1)
- if ok& >= 0 then
- readbyte n&, byte%
- if byte% = 102 then
- ok& = FNfseek&(-1, handle%, 1)
- writebyte 78
- writebyte 113
- end if
- end if
- end if
- end if
- end if
- end if
- end sub
- ---
- EOF
-