|
Basic interaction with the
|
|
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.
PC Card Software". So you can simply check
for a task of that name.
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.
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.
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
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
: