Basic interaction with the
80x86 co-processor

 

80x86 co-processor; JPEG 22K

Introduction

No doubt some people are screaming and yelling sacrilige! The way I see it, your RiscPC may have a co-processor fitted. If so, then either the thing sits there consuming electricity until you have the inclination to load PCPro and start up Windows™.....OR you can have a little bit of interaction with it, well, for the hell of it!

 

So that is what this document is all about. Interacting with your Intel silicon. Please, do not ask why a person might want to do this. I'm in it for "hack value", others might see it as a way to make a 'More Impressive Demo' (the 80x86 processing data, the ARM managing, and displaying the data). If you are unable to think of any possible reason for talking to the co-processor, then you probably won't want to read the rest of this... :-)

 

 

Here's our destination:

And, of course, what we will NOT be covering:

Our objective is NOT to cobble together some form of DOS™ based system. Aleph1 have put a lot of work into getting a system capable of running DOS™, Windows™ 3.1x, Windows™ 95, and Windows™ 98. It doesn't cost a lot when you consider what you're getting. Go buy the real deal. Or if you're a sad cheapskate, there is the Acorn !PCx86 (which will screw up if you try to run Windows™ 95) on the old Acorn ftp site, and countless Acorn User CD-ROMs...
As several ex-Acorn people might have said, making a stable DOS™ environment is non-trivial.

 

Index

 

How to detect if !PC / !PCx86 / !PCPro is currently running

This is fairly simple. It appears that the various versions of the PC software set themselves up as a task named "PC Card Software". So you can simply check for a task of that name.
It is suggested that if you have a multitasking application that interacts with the co-processor, that you also call it PC Card Software so that it may be detected by this method...

   DIM block% 32
   offset% = 0
   REPEAT
     SYS "TaskManager_EnumerateTasks", offset%, block%, 16 TO offset%
     IF FNgetstring(block%!4) = "PC Card Software" THEN
       PRINT "PC CARD SOFTWARE IS LOADED"
     ENDIF
   UNTIL (offset% < 0)
   END
  
   DEFFNgetstring(ptr%)
     LOCAL out$
     out$ = ""
     WHILE ( ((?ptr%)<>0) AND ((?ptr%)<>13) )
       out$+=CHR$(?ptr%)
       ptr%+=1
     ENDWHILE
   =out$
Obviously, if the PC card software is running, we'll not want to continue with our software, but instead warn the user to shut down !PCx86/!PCPro before running our code. Modify the above program as necessary.

 

How to detect if a co-processor is fitted

Detecting the co-processor ASIC version is performed by clearing and setting the Gemini 2 ID, and reading the value back. I am not sure exactly which ASIC is on which co-processor card, save the basic Acorn 80486SXL-40 which has Gemini 1.

If the co-processor is not installed, you should fail at the first test (the one that asks if the co-processor is fitted).

   REM >gemitype
   REM Detect Gemini 1 or Gemini 2

   ON ERROR PRINT REPORT$+" at "+STR$(ERL/10):END

   asic_gemi2_base% = &37A0000
   asic_gemi2_id%   = &1000
   asic_gemi2_rev2% = &8000

   asic_masr_base%  = &3730000

   DIM code% 256

   FOR loop% = 0 TO 2 STEP 2
     P% = code%
     [ OPT     loop%

     .write
       ; Entry:
       ;   R0 = Address to write to
       ;   R1 = Value to write
       ; Exit:
       ;   Registers preserved
       ;
       SWI     "OS_EnterOS"
       STR     R1, [R0]
       TEQP    PC, #0
       MOV     R0, R0
       MOVS    PC, R14

     .read
       ; Entry:
       ;   R0 = Address to read from
       ; Exit:
       ;   R0 = Value read
       ;   Other registers preserved
       ;
       SWI     "OS_EnterOS"
       LDR     R0, [R0]
       TEQP    PC, #0
       MOV     R0, R0
       MOVS    PC, R14
     ]
   NEXT


   REM First, clear the Gemini 2 ID bit
   A% = asic_gemi2_base%
   B% = asic_gemi2_id%
   CALL write

   REM Read the mailbox address/status register
   A% = asic_masr_base%
   value% = USR(read)
   IF (value% AND &7F00000) THEN
     PRINT "MASR read back was &"+STR$~(value%)
     PRINT "(bits 20...26 should be zero)"
     PRINT "[ ** IS THE CO-PROCESSOR FITTED? ** ]"
     END
   ENDIF

   REM Now set the Gemini 2 ID bit
   A% = asic_gemi2_base%
   B% = (asic_gemi2_id% OR asic_gemi2_rev2%)
   CALL write
 
   REM Read the mailbox again
   A% = asic_masr_base%
   value% = USR(read)
   IF ((value% AND &7F00000) = 0) THEN
     PRINT "Gemini 1 detected"
     END
   ENDIF

   IF ((value% AND &7F00000) = &7F00000) THEN
     PRINT "Gemini 2 detected"
     END
   ENDIF

   PRINT "Unknown Gemini version."
   PRINT "MASR bits 20...26 should be all zero or all one."
   END
There is no absolute need to know which ASIC is fitted unless you plan to do deep stuff. For our purposes, a co-processor card is a co-processor card. This check was included for interest.
The code in this document was developed on the 80486SXL-40 co-processor.
If you are only interested in knowing that the co-processor is present, perform the first test, skip the rest.

 

How to detect what sort of co-processor is fitted

Not too easily.
You see, the best way to tell which co-processor is fitted is to run a few simple tests on the processor. These are documented on-line, try a search on Intel's site, if you are interested.
By the end of this document, you should know how to send and receive information to/from the co-processor, which can include running a test to see what processor is fitted.

You should not run into problems if you assume that the processor is a standard 80486SX, though if you plan to hive off FP intensive work to the processor you will be wanting to run a test to see if the 'DX' version is fitted.

 

A way to determine the processor type is...

      This code is untested; it is based largely upon the
      information provided in one of Intel's data sheets.

   .data
         public _cpu_type
         public _fpu_type

         _cpu_type db 0
         _fpu_type db 0
         fp_status dw 0


      ; the < 80386 tests have been omitted as the hardware is
      ; not available as a RiscPC co-processor.
      mov       _cpu_type, 0     ; unkonwn processor

      ; The AC bit, bit #18, is a new bit introduced in the EFLAGS 
      ; register on the Intel486 processor to generate alignment 
      ; faults. This bit cannot be set on the Intel386 processor. 
   .386
   check_80386:

         pushfd                  ; push original EFLAGS
         pop    eax              ; get original EFLAGS
         mov    ecx, eax         ; save original EFLAGS
         xor    eax, 40000h      ; flip AC bit in EFLAGS
         push   eax              ; save new EFLAGS value on stack
         popfd                   ; replace current EFLAGS
         pushfd                  ; get new EFLAGS
         pop    eax              ; store new EFLAGS in EAX
         xor    eax, ecx         ; ca'nt toggle AC bit, processor is 80386

         mov    _cpu_type, 3     ; set processor type to 3 (80386)
         jz     end_cpu_type     ; jump if 80386
  
         push   ecx              ; restore AC bit in EFLAGS
         popfd

      ; Checking for ability to set/clear ID flag (Bit 21) in EFLAGS 
      ; which indicates the presence of a processor with the CPUID 
      ; instruction.
   .486
   check_80486:
         mov    _cpu_type, 4     ; set processor type to 80486
         mov    eax, ecx         ; get original EFLAGS
         xor    eax, 200000h     ; flip ID bit in EFLAGS
         push   eax              ; save new value on stack
         popfd                   ; replace current EFLAGS
         pushfd                  ; get new EFLAGS
         pop    eax              ; store new EFLAGS in EAX
         xor    eax, ecx         ; can't toggle ID bit...
         je     end_cpu_type     ; processor is an 80486

      ; for further detection of processor type (SX, DX, etc) we
      ; would execute the CPUID instruction and examine the
      ; results...


   end_cpu_type:
      ; now we will check for FPU. If FP status and control words
      ; are present, an FPU exists - otherwise it doesn't.
      ; we are only interested in FPU/noFPU, there are further
      ; tests to determine what *type* of FPU is present.

         fninit                  ; reset FP status word
         mov    fp_status, 5a5ah ; initialise temp word non-zero
         fnstsw fp_status        ; save FP status word
         mov    ax, fp_status    ; check FP status word
         cmp    al, 0            ; was correct information written?
         mov    _fpu_type, 0
         jne    end_fpu_type     ; incorrect information, no FPU

         fnstcw fp_status        ; save FP control word
         mov    ax, fp_status    ; check FP control word
         and    ax, 103fh        ; selected parts to examine
         cmp    ax, 3fh          ; was control word correct?
         mov    _fpu_type, 0
         jne    end_fpu_type     ; incorrect control word, no FPU
 
         mov    _fpu_type, 1     ; all correct, FPU present

   end_fpu_type:

      ; here:
      ;   _cpu_type is 0, 3, or 4.
      ;   _fpu_type is 0 or 1.

      ret

 

How to load a chunk of code into 80x86 accessible memory

Now we're getting to the Good Stuff!

First, however, we need routines to initialise the co-processor...

   REM >coprosetup
   REM Initialise the processor

   ON ERROR PRINT REPORT$+" at "+STR$(ERL/10):END

   MODE 12

   PROCcoproc_setup
   PROChardware_initialise

   END

   :
   :

   DEFPROCcoproc_setup
     REM Gemini II ASIC
     asic_gemi2_base% = &37A0000
     asic_gemi2_id%   = &1000
     asic_gemi2_rev2% = &8000

     REM Configuration register
     cnfg_reg%        = &3760000

     REM Miscellaneous control register
     misc_ctrl_reg%   = &3750000
     misc_pwrgood%    = 1
     misc_a20gate%    = &20

     REM Processor/Interrupt control register
     picr_reg%        = &3740000

     REM Mailbox address/status register
     asic_masr_base%  = &3730000

     REM I/O map control register
     iomap_ctrl_reg%  = &3720000

     REM Memory map control register
     memmap_ctrl_reg% = &3710000
 
     DIM code% 256

     FOR loop% = 0 TO 2 STEP 2
       P% = code%
       [ OPT     loop%

       .write
         ; Entry:
         ;   R0 = Address to write to
         ;   R1 = Value to write
         ; Exit:
         ;   Registers preserved
         ;
         SWI     "OS_EnterOS"
         STR     R1, [R0]
         TEQP    PC, #0
         MOV     R0, R0
         MOVS    PC, R14

       .read
         ; Entry:
         ;   R0 = Address to read from
         ; Exit:
         ;   R0 = Value read
         ;   Other registers preserved
         ;
         SWI     "OS_EnterOS"
         LDR     R0, [R0]
         TEQP    PC, #0
         MOV     R0, R0
         MOVS    PC, R14
       ]
     NEXT
   ENDPROC
   :
   DEFPROChardware_initialise
     PRINT "Initialising hardware..."

     A% = asic_gemi2_base%
     B% = asic_gemi2_id%
     CALL write

     REM Put CPU in reset
     A% = misc_ctrl_reg%
     B% = misc_a20gate%
     CALL write

     REM (in case of new A20 modification)
     A% = &3770000  : REM External register
     B% = 1
     CALL write

     REM Blank memory map
     A% = memmap_ctrl_reg%
     B% = 0
     CALL write

     REM Blank I/O map
     A% = iomap_ctrl_reg%
     B% = &4000 OR &8000
     CALL write

     REM Blank processor/interrupt control register
     A% = picr_reg%
     B% = 0
     CALL write

     REM Disable caching for now
     A% = cnfg_reg%
     B% = 1         : REM Memory = 1Mb
     CALL write

     REM Disable general purpose I/O decodes
     FOR l% = 0 TO 3
       A% = &3780000 + (l% << 2)
       B% = 0
       CALL write
     NEXT

     REM Disable floppy disc control register
     A% = &3790004
     B% = 0
     CALL write

     REM Set test timing value
     A% = &37F0000
     B% = 4
     CALL write
     PROCwait(50)
 
     REM Clearing mailbox
     PROCclear_mbx
     PROCwait(10)

     REM Setting PWRGOOD
     A% = misc_ctrl_reg%
     B% = (misc_pwrgood% OR misc_a20gate%)
     CALL write
     PROCwait(20)

     PRINT "Done!"
   ENDPROC
   :
   :
   DEFPROCwait(for%)
     LOCAL now%, then%
     SYS "OS_ReadMonotonicTime" TO now%
     REPEAT
       SYS "OS_ReadMonotonicTime" TO then%
     UNTIL then% < (now% + for%)
   ENDPROC
   :
   DEFPROCclear_mbx
     LOCAL word%

     FOR loop% = 0 TO 99
       A% = asic_masr_base%
       word% = USR(read)
   
       IF (word% AND &80000000) THEN ENDPROC
  
       IF (word% AND &20000000) THEN
         A% = asic_masr_base%
         word% = USR(read)
       ELSE
         A% = asic_masr_base%
         B% = 0
         CALL write
       ENDIF
     NEXT
   ENDPROC
   :
.....unfinished...:-).....
(and untested!)

 


Return to assembler index
Copyright © 2002 Richard Murray