!PCCache

Par
Benoît Gilon

Cet article et le programme inclus sont les développements d'un programme du numéro précédent, portant le nom de PC Gate.

De façon pratique, le programme présenté dans cet article permet de contrôler l'état du cache ARM3 à partir de la ligne de commande du PC. Trois syntaxes sont admises:

C:\CACHE
affiche l'état courant de ce dernier;
C:\CACHE OFF
désactive le cache ARM3;
C:\CACHE ON
active celui-ci.
Vous aurez deviné que l'intérêt ici ne réside pas dans le but mais plutôt dans la manière de l'atteindre.

Présentation

Implémentation

Module PCSupport

Rappelons que, à l'issue d'un SWI pouvant retourner une erreur (son nom est donc préfixé par la lettre X), soit le bit V est nul en retour et le SWI s'est bien déroulé, soit le bit V est 1, et une erreur s'est produite: à ce moment là, le registre R0 pointe sur un bloc de description d'erreur:
[R0] Code d'erreur
[R0], #4 Message d'erreur (se terminant par un caractère nul)

Une fois récupéré l'adresse du bloc de description au sein de l'environnement PC. Aucune assistance n'est possible puisque:

Notre module comporte donc un seul SWI de récupération des divers champs de la structure au sein de l'espace d'adresse de l'émulation PC. Notre SWI s'appelle "PCSGetError", son numéro est &C0000 soit le premier numéro du premier chunk réservé aux applications de l'utilisateur.
SWIBASE    *       (1<<18) :OR: (1<<19)
; Bit inhibant la génération d'erreurs au
; sein des SWI appelés
XBit    *       (1<<17)
MBITV   *       (1<<28)
debut   DCD     0       ; startcode
        DCD     0       ; initialisation code
        DCD     0       ; finalisation code
        DCD     0       ; service handler
        DCD     title-debut
        DCD     help-debut
        DCD     0       ; help-command table
        DCD     SWIBASE
        DCD     swientry-debut  ; swi handler code
        DCD     swidecodetable-debut
        DCD     0 ;SWIname decode code pointer

swientry
        CMP     R11, #(EndOfJumpTable -JumpTable)/4
; Dispatch if in range
        ADDLO   PC, PC, R11, LSL #2
        B       UnknownSWIError
JumpTable
        B       SWIGetError
EndOfJumpTable
UnknownSWIError
        ADR     R0, ErrToken
        ORRS    PC, R14, #MBITV

title   DCB     "PCSupport",0
help    DCB     "PCSupport",9,"0.01 (16 Jan 1996)",0
        ALIGN

swidecodetable
        DCB     "PCS",0 ; prefix name
; Get error block from inside PC code
        DCB     "GetError",0
        ALIGN

ErrToken DCD    &1B6
        DCB     "PCS internal error",0
        ALIGN

SWIGetError
; En entrée:
; R0 contient le block d'erreur en entrée
; R1 contient l'adresse ou transmettre le
; texte de l'erreur (un buffer de 256 octets
; est supposé exister à cet emplacement)
; En sortie:
; R0: contient le code de l'erreur
; [R1] contient le texte du block d'erreur
; (standard C)
        ADD     R2, R0, #4
        MOV     R4, #0
1       LDRB    R3, [R2, R4]
        STRB    R3, [R1, R4]
        ADD     R4, R4, #1
        TEQ     R4, #256
        BEQ     %F2
        TEQ     R3, #0
        BNE     %B1
2       LDR     R0, [R0, #0]
        MOVS    PC, R14

        END

Librairie d'interface

Le module d'interface est constitué de deux modules:
  1. Un module en assembleur chargé d'appeler directement l'A.P.I. définie par Acorn;
  2. Un module en Turbo Pascal constituant l'interface d'appel par des programmes client Turbo Pascal.

Fichier Acorn.ASM

        .MODEL  TPASCAL

        .CODE
AcornSWI PROC   NEAR pSWIBlock: DWORD RETURNS Res: BYTE
        PUBLIC  AcornSWI
        MOV     DX, 'sa'
        MOV     AX, 'fe'
        LES     BX, pSWIBlock
        DW      -1, 258
        XOR     AX, AX
        JNC     AFin
        INC     AX
AFin:   RET
        ENDP

IntelTAcorn PROC FAR pIntel: DWORD RETURNS Res: DWORD;
; Retourne une adresse ARM correspondant
; une paire segment/offset en mode Intel.
        PUBLIC  IntelTAcorn
        LES     BX, pIntel
        DW      -1, 257
        JNC     ICont
; Retourne la valeur 0 en cas d'erreur...
        XOR     AX, AX
        MOV     DX, AX
ICont:
        RET
        ENDP

        ENDS
        END

Fichier ACORN.PAS

{       Unité permettant de faire communiquer une
        application MS/DOS écrite en Turbo Pascal
        (à partir de la version 5) avec le monde
        RISC OS:
        Version 0.3 (bas niveau uniquement).
        Droit de copie réservé:
                © Benoit GILON 1995-96
  Projet Janus
  Appel de SWI a partir d'une application
  Référence biblio:
                PC Emulator User Guide pages 69-71 }

UNIT Acorn;

INTERFACE
TYPE
tSWIBlock = ARRAY[0..16] OF LONGINT;

{       Fonction de conversion:
        Addresse Intel > Adresse ARM }
FUNCTION IntelTAcorn(IntelAddress: POINTER):
        LONGINT;
PROCEDURE AcornMSWI(VAR vSWI: tSWIBlock);

IMPLEMENTATION
TYPE
tpSwiBlock = ^tSwiBlock;

VAR
{       L'alignement de tableau sur
        frontière de mot (long)
        n'étant pas garanti par
        Turbo Pascal, nous réservons ici une place
        suffisante dans lequel tiendront 17 mots
        longs sur une telle frontière }
SwiBlock: ARRAY[0..34] OF INTEGER;
mPointer: tpSwiBlock;
errorText: STRING;
pErrorText: LONGINT;

PROCEDURE AdjustOffset;
VAR
mOff: WORD;
BEGIN
mOff:= Ofs(SwiBlock);
IF (mOff AND 3)<> 0  THEN Inc(mOff,2);
mPointer:= Ptr(Seg(SwiBlock), mOff);
END;

FUNCTION IntelTAcorn(IntelAddress: POINTER):
        LONGINT; EXTERNAL;
FUNCTION AcornSWI(pSwi: tpSwiBlock): BYTE; EXTERNAL;
{$L ACORN.OBJ }

PROCEDURE AcornMSWI(VAR vSWI: tSWIBlock);

CONST
BitVMask : LONGINT = (1 SHL 28);
SpecialPurposeSWI : LONGINT = (1 SHL 18) OR (1 SHL 19);
{d'erreurs retournables au client }
ChainResult : ARRAY [0..5] OF STRING[32] =
        ('General failure',
         'Invalid signature',
         'Command block not in user RAM',
         'Alignement error',
         'SWI number is out of range',
         'SWI number is protected');
{       Certaines erreurs sont signe d'une erreur
        interne de l'unité et doivent donc
        terminer le programme au plus tot...
        D'autres sont signes d'une erreur de
        paramétrage au niveau du client, il n'est
        pas alors nécessaire de sortir }
ExResult : ARRAY [0..5] OF BOOLEAN =
        (FALSE, TRUE, TRUE, TRUE, FALSE, FALSE);
VAR
Resultat: BYTE;
vC: CHAR;
vIChar: BYTE;
BEGIN
mpointer^ := vSWI;
Resultat:= AcornSWI(mPointer);
IF Resultat <> 0 THEN
        BEGIN
        Resultat:= mPointer^[0] AND $7fffffff;
        WriteLn(ChainResult[Resultat]);
        IF ExResult[Resultat] THEN
                Halt(Resultat+1);
        END
ELSE
        BEGIN
        { Teste le bit V }
        IF (mPointer^[16] AND BitVMask) <> 0 THEN
                BEGIN
                {       mpointer^[1] contient déjà le
                        pointeur sur le bloc descripteur }
                {       mpointer^[2]: adresse où stocker
                        le message textuel }
                mpointer^[2]:= pErrorText;
                mpointer^[0]:= SpecialPurposeSWI;
                Resultat:= AcornSWI(mpointer);
                WriteLn(
                        'SWI failed, error code, r0:',
                        mpointer^[1]);
                { Impression du message au standard C }
                vIChar:= 0;
                REPEAT
                        vC:= errorText[vIChar];
                        Inc(vIchar);
                        IF vC <> #0 THEN Write(vC);
                UNTIL vC= #0;
                WriteLn;
                END
        ELSE
                BEGIN
                vSWI:= mpointer^;
                END;
        END;
END;

BEGIN
AdjustOffset;
pErrorText:= IntelTACorn(@errorText);
END.

Programme client CACHE

Le programme client exposé ici est un exemple simple afin d'illustrer l'emploi de la librairie d'interface.
PROGRAM Cache;
USES ACORN;

CONST
cXCacheControl : LONGINT = (1 SHL 17) OR $280;

VAR
SB: tSWIBlock;
vPstring: STRING;

PROCEDURE Usage;
BEGIN
WriteLn('Cache turns the cache on or off, or returns the cache state');
WriteLn('Syntax: Cache [on|off]');
Halt;
END;

BEGIN
SB[0]:= cXCacheControl;
CASE ParamCount OF
0:      BEGIN
        SB[1]:= 0;
        SB[2]:= 1;
        END;
1:      BEGIN
        vPstring:= ParamStr(1);
        IF      (vPstring<> 'on') AND
                (vPstring<> 'off') THEN Usage;
        SB[2]:= 0;
        IF (vPstring= 'on') THEN SB[1]:= 1
        ELSE SB[1]:= 0;
  END;
ELSE    Usage;
END;

AcornMSWI(SB);
IF ParamCount= 0 THEN
        BEGIN
        Write('Cache o');
        IF SB[1] = 0 THEN WriteLn('ff')
        ELSE    WriteLn('n');
        END;
END.

Appendice

Voici en guise de conclusion, la traduction de l'«appendice D: détails de programmation» issus du manuel de l'émulateur PC (v1.8). Un prochain article traitera de la mise en place de la sollicitation ayant pour origine un programme RISC OS (c'est le mode réciproque).

Accès à RISC OS

Les programmes DOS peuvent accéder à RISC OS en utilisant une instruction spéciale du 8086 (nouveau SVC). Celui ci est un code op. inutilisé sur un 8086 réel et que l'émulateur PC trappe et utilise pour communiquer avec RISC OS. Les programmes utilitaires GETFILE.EXE et PUTFILE.EXE utilisent ce code op. SVC pour transférer des fichiers entre les systèmes de fichiers RISC OS et MS/DOS.

Il existe aussi un périphérique PC virtuel "I/O mapped" qui permet que des évènements RISC OS soient détectés par le PC et puissent engendrer une interruption des programmes tournant dans l'environnement MS/DOS.

Le code opération SVC

Le nouveau code op. 8086 SVC a le format suivant:
FF FF nn nn
4 octets le constituent, les deux premiers formant la valeur FFFF, les troisièmes et quatrième formant un nombre 16 bits qui indiquent à l'émulateur quels services sont requis. Par exemple, le code op. SVC pour traduire une adresse 8086 en une adresse ARM peut être assemblée avec un assembleur compatible MASM en utilisant la ligne suivante:
       dw      -1, 257
Seule une toute petite partie parmi les SVCs possibles est réellement utilisée. Certains SVCs qui sont utilisés ne sont pas décrits ici car ils le sont uniquement pour assurer des fonctions internes de l'émulateur. Seuls les SVCs décrits ici peuvent être utilisés au sein d'applications DOS.
SVC 257
Traduit une adresse 8086 en adresse ARM
SVC 258
Interface générique pour accéder au SWIs

Le périphérique RISC OS

Ce périphérique occupe les ports 0x700 et 0x701. Il permet à des évènements d'être vus par l'émulateur PC. Un évènement RISC OS #13 va générer une interruption PC IRQ3 (correspondant à l'interruption 0BH). 4 interruptions peuvent être bufferisés dans ce périphérique avant la prise en compte de la première. Le périphérique RISC OS PC permet au 8086 d'examiner les registres ARM tels qu'ils étaient à l'instant où l'évènement ARM a été généré.
Port 700H
Lecture
bit 0 Interruption en attente
bit 1 Dépassement de capacité
Ecriture
bit 0-1 Sélection de l'octet à l'intérieur du registre (00: octet le moins significatif)
bit 2-5 # du registre (0 à 15) (seuls les registres R1 et R2 sont lisibles)
bit 6 Doit etre zéro (réservé)
bit 7 Réinitialise le statut d'interruption, réautorise la prise en compte des évènements consécutifs
Port 701H (lecture seule)
Fournit la valeur de l'octet spécifé par écriture préalable sur le port 700H