25/08/2021

Un micro-ordinateur FLEX/6809 DIY - Partie 5

Dans cette cinquième partie, nous allons enfin démarrer le système d'exploitation !


 

Le processus est très bien décrit dans le "Flex Adaptation Guide", les étapes sont les suivantes :

  • Ecrire un driver pour la console
  • Ecrire un driver pour le disque
  • Avec un petit programme "QLoad" et le driver disque, charger depuis le disque vers la mémoire le contenu du fichier FLEX.COR (exécutable Flex sans les pilotes)
  • Démarrer Flex
  • Depuis Flex, Créer le fichier FLEX.SYS
  • Créer le secteur de boot et l'inscrire sur la disquette
  • Ecrire un chargeur Flex pour le moniteur

Comme on le voit il y a du travail, mais pas de panique nous allons voir chaque étape en détail !

Le Driver pour la console

Le squelette du pilote est le suivant :

   ; *
  ;**************************************************
  ; ACTUAL ROUTINES START HERE
  ;*****************************

    ORG $D370 

 ; <ici le code>

 ;**************************************************
 ; *
 ; I/O ROUTINE VECTOR TABLE *
 ; *

    ORG $D3E5  ; TABLE STARTS AT $D3E5 ;

; *

INCHNE  FDB INNECH      ; INPUT CHAR - NO ECHO ;
IHNDLR  FDB IHND        ; IRQ INTERRUPT HANDLER ;
SWIVEC  FDB $DFC2       ; SWI3 VECTOR LOCATION ;
IRQVEC  FDB $DFC8       ; IRQ VECTOR LOCATION ;
TMOFF   FDB TOFF        ;  TIMER OFF ROUTINE ;
TMON    FDB TON         ; TIMER ON ROUTINE ;
TMINT   FDB TINT        ; TIMER INITIALIZE ROUTINE ;
MONITR  FDB $F814       ; MONITOR RETURN ADDRESS ;
TINIT   FDB INIT        ; TERMINAL INITIALIZATION ;
STAT    FDB STATUS      ; CHECK TERMINAL STATUS ;
OUTCH   FDB OUTPUT      ; TERMINAL CHAR OUTPUT ;
INCH    FDB INPUT       ; TERMINAL CHAR INPUT ;

    END

On voit que le code démarre à l'adresse $D370, et qu'à l'adresse $D3E5 on trouve une table indiquant les adresses de chaque routine. Reste donc à implémenter les différents points d'entrée, pour l'instant seuls INCHNE, TINIT, STAT, OUTCH et INCH sont utiles.

Le code complet est disponible dans le gihtub (io.asm), pour le charger en mémoire il faut utiliser la commande 'L' sur le fichier io.s19 (comme on a fait dans les parties précédentes).

Pour tester le code, on peut écrire des petits programmes de test, par exemple pour écrire la lettre X à l'écran :

    ORG $0080

OUTPUT  EQU $D37F

        LDA #'X
        LBSR OUTPUT

        RTS

    END


Le driver pour les disques

Cette fois-ci le squelette du code est comme ceci :

    ;**********************************************
    ; DISK DRIVER ROUTINE JUMP TABLE
    ;**********************************************

        ORG $DE00

DREAD   JMP READ
DWRITE  JMP WRITE
DVERFY  JMP VERIFY
RESTOR  JMP RST
DRIVE   JMP DRV
DCHECK  JMP CHKRDY
DQUICK  JMP CHKRDY
DINIT   JMP INIT
DWARM   JMP WARM
DSEEK   JMP SEEK
 
     <ici le code>
 
   END

Curieusement cela ne fonctionne pas comme le driver console, ici on a une table de sauts à l'adresse $DE00 suivi du code effectif. Je trouve cela plus simple et je ne sais pas pourquoi les deux pilotes ne fonctionnent pas de la même manière (peut être qu'on veut éviter un saut supplémentaire dans le pilote console pour gagner en performance ?).

Pour tester le pilote, il faut comme d'habitude le charger en mémoire avec la commande "L", puis charger ce petit programme (testdisk.asm) :

0001                               ; Test Disk - Read the boot sector
0002                               ; Laurent FRANCOISE 2021
0003
0004                               ; Disk driver must be loaded first
0005
0006                               ; Disk driver entry points
0007 de15                          INIT    EQU $DE15
0008 de0c                          DRIVE   EQU $DE0C
0009 de09                          RESTORE EQU $DE09
0010 de00                          READ    EQU $DE00
0011
0012 c200                          SCTBUF  EQU $C200 ; DATA SECTOR BUFFER
0013
0014 0200                          BUFFER  EQU $0200 ; SECTOR DATA
0015
0016 0100                              ORG $0100
0017
0018 0100 bd de 15           [ 8 ]     JSR INIT        ; INIT DISK DRIVER
0019
0020 0103 8e c2 00           [ 3 ]     LDX #SCTBUF
0021 0106 6f 03              [ 7 ]     CLR 3,X         ;
0022 0108 bd de 0c           [ 8 ]     JSR DRIVE       ; SELECT DRIVE 0
0023
0024 010b 8e c2 00           [ 3 ]     LDX #SCTBUF
0025 010e bd de 09           [ 8 ]     JSR RESTORE     ; GO TO TRACK 0
0026
0027 0111 8e 02 00           [ 3 ]     LDX #BUFFER     ; SECTOR DEST.
0028 0114 86 00              [ 2 ]     LDA #0          ; TRACK
0029 0116 c6 01              [ 2 ]     LDB #1          ; SECTOR
0030 0118 bd de 00           [ 8 ]     JSR READ        ; READ SECTOR
0031
0032 011b 39                 [ 5 ]     RTS

Ce programme va lire le secteur de boot (piste 0, secteur 1). Une fois chargé avec la commande "L", il faut le lancer avec la commande "C 0100" :

>C 0100                                                                         
PC-F5F1 A-80 B-80 X-02FD Y-E002 U-E7C2 S-E751 CC-D4 DP-00                       
>D 0200 0300                                                                    
                                                                                
      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F                            
0200 0A 00 00 00 07 04 00 C1 00 00 00 10 CE C0 7F FC  ...............          
0210 C1 05 FD C3 00 10 8E C4 00 8D 35 81 02 27 10 81  ..........5..'..          
0220 16 26 F6 8D 2B B7 C1 08 8D 26 B7 C1 09 20 EA 8D  .&..+....&... ..          
0230 1F B7 C1 0A 8D 1A B7 C1 0B 8D 15 1F 89 4D 27 D9  .............M'.          
0240 BE C1 0A 34 14 8D 09 35 14 A7 80 5A 26 F5 20 C9  ...4...5...Z&. .          
0250 10 8C C4 00 26 0F 8E C3 00 EC 84 27 0B 8D 0D 26  ....&......'...&          
0260 9E 10 8E C3 04 A6 A0 39 6E 9F C1 08 8D 2F 86 8C  .......9n..../..          
0270 B7 E8 18 8D 5F 8E C3 00 B6 E8 18 85 02 26 08 85  ...._........&..          
      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F                            
0280 01 26 F5 1F 89 20 0A B6 E8 1B A7 80 5A 26 E9 8D  .&... ......Z&..          
0290 03 C5 1C 39 F6 E8 18 C5 01 26 F9 39 34 02 B6 E8  ...9.....&.94...          
02A0 10 84 FB C1 0A 23 02 8A 04 B7 E8 10 35 02 F7 E8  .....#......5...          
02B0 1A B1 E8 19 27 10 B7 E8 1B 8D 0B 86 18 B7 E8 18  ....'...........          
02C0 04 8D D0 C5 10 BD C1 CB BD C1 CE 39 88 3B 8D 35  ...........9.;.5          
02D0 20 D5 8E C2 D1 BD CD 1E 20 8B 8E C2 E2 BD C2 C6   ....... .......          
02E0 26 17 8E C3 05 BD C2 C6 26 0F 8E 01 00 86 0C A7  &.......&.......          
02F0 84 BD D4 06 26 06 7E C1 7E 7E CD 03 7E 7F 7F 7F  ....&.~.~~..~          

Il faut regarder le registre B, si il contient la valeur $80 c'est que le secteur a été lu sans erreur. Dans l'exemple au dessus j'ai utilisé une disquette avec un secteur de boot, avec celle que l'on a fait dans le dernier article il n'y aura que des zéros dans le dump !

Pour lire un autre endroit de la disquette, il suffit de changer directement la piste et le secteur dans le code (avec la commande M), le fichier LST nous apprend qu'il faut modifier les octets $0115 (piste) et $0117 (secteur).

L'air de rien, arrivé à ce point on a un lecteur de disquette qui fonctionne !

QLoad

On commence vraiment ici les choses sérieuses. Maintenant qu'on a les 2 pilotes de périphériques à disposition, on va pouvoir s'en servir pour lancer Flex pour la première fois, avec la disquette que l'on a créé dans le précédent article.

Si vous vous souvenez on a besoin du fichier FLEX.COR, que l'on a positionné sur la piste 1 / secteur 1 de la disquette. C'est le code exécutable de Flex sans les pilotes, et il occupe une vingtaine de secteurs.

Le programme QLoad (pour "Quick Load") va charger en mémoire le contenu de la piste 1 / secteur 1 et suivre la liste chaînée (les 2 premiers octets de chaque secteur) pour charger successivement toutes les parties du programme, jusqu'à trouver un secteur dont l'élément suivant est 0/0.

Il faut que je précise la signification de "charger en mémoire" car contrairement au programme "testdisk", on ne va pas simplement copier les octets les uns à la suite des autres. Il s'agit ici d'un exécutable Flex, qui fonctionne comme ceci : 

  • l'octet $02 indique le début d'un bloc :
    • les 2 octets suivants indiquent l'adresse de destination
    • l'octet suivant indique la taille du bloc
    • les données sont à la suite
  • l'octet $16 contient l'adresse d'exécution
    • les 2 octets suivants indiquent l'adresse

 Le programme se lance avec la commande "G C100", en ayant préalablement chargé le pilote console et le pilote disque (cela fait en tout donc 3 programmes à charger, en comptant QLoad). La sortie va ressembler à quelque chose comme ça :

>G C100                                                                         
0101 80 0102 80 0103 80 0104 80 0105 80 0106 80 0107 80 0108 80 0109 80 010A 80
010B 80 010C 80 010D 80 010E 80 010F 80 0110 80 0111 80 0112 80 0113 80 0114 80
0201 80 0202 80 000ü                                                            
ASSIST09                                                                        
>  

L'affichage correspond au couple piste/secteur suivi du code de retour de la routine READ ($80 si tout s'est bien passé). Une fois FLEX.COR chargé on revient sur le prompt du moniteur.

Le premier démarrage de Flex !

Pour lancer flex, il suffit de faire un saut à l'adresse de démarrage, qui est $CD00 :

>G CD00                                                                         
                                                                                
FLEX 9.1                                                                        
                                                                                
DATE (MM,DD,YY)? 08,24,21                                                       
                                                                                
+++  

Il faut tout d'abord saisir la date au format indiqué, et appuyer sur ENTREE. Le prompt de Flex apparaît (les 3 signes "plus"). On peut essayer de taper une commande, par exemple "DATE" :

+++DATE                                                                         
AUGUST 24, 1921                                                                 
+++ 
   

... et on constate qu'on est bien en 1921 ! (Il existe un patch pour gérer l'an 2000). La commande "CAT" permet de voir le contenu du disque :

 +++CAT                                                                        
                                                                                
CATALOG OF DRIVE NUMBER 0                                                       
DISK: FLEX  #1                                                                  
                                                                                
 NAME   TYPE  SIZE  PRT                                                         
                                                                                
FLEX    .COR    22                                                              
APPEND  .CMD     3                                                              
ASMB    .CMD    48                                                              
ASN     .CMD     1                                                              
BUILD   .CMD     1  
....
SECTORS LEFT = 50                                                              
                                                                                
+++

Ça ressemble à quelque chose qui fonctionne !

Création de FLEX.SYS

On pourrait refaire tout ce processus (charger les pilotes, charger Qload, lancer Qload, faire un saut sur le point d'entrée de Flex) à chaque fois, mais reconnaissons que c'est un peu fastidieux (je l'ai fait quelque temps et je confirme !). 

Maintenant qu'on a un système Flex qui tourne, on va pouvoir s'en servir pour créer les briques qui nous manquent pour avoir un démarrage "automatique" du système.

La première étape consiste à créer le fichier FLEX.SYS, qui est la concaténation des 2 pilotes console+disque et de FLEX.COR. Les commandes sont décrites page 25 du "Flex Adaptation Guide".

Tout d'abord il faut regarder io.lst et disk.lst pour voir l'adresse de début et de fin de ces 2 programmes. En regardant on trouve :

  • io.lst  commence en $D370 et se termine en $D3FC
  • disk.lst commence en $DE00 et se termine en $DF3E

En suivant le guide il faut donc taper les commandes :

+++SAVE DISK,DE00,DF3E                                                    
+++SAVE CONSOLE,D370,D3FC,CD00                                                     
+++APPEND FLEX.COR,DISK.BIN,CONSOLE.BIN,FLEX.SYS                         
 APPEND COMPLETE                                                                
+++

 

Si on veut on peut utiliser la commande "CAT FLEX" pour s'assurer que le fichier FLEX.SYS est bien créé :

+++CAT FLEX                                                                     
                                                                                
CATALOG OF DRIVE NUMBER 0                                                       
DISK: FLEX  #1                                                                  
                                                                                
 NAME   TYPE  SIZE  PRT                                                         
                                                                                
FLEX    .COR    22                                                              
FLEX    .SYS    25                                                                                                                          
                                                                                
SECTORS LEFT = 26


Le secteur de boot

C'est là où il va falloir bien suivre ! Ce que l'on veut faire c'est charger en mémoire le fichier FLEX.SYS, qui contient tout ce dont on a besoin (l'exécutable Flex + les pilotes). On pourrait utiliser Qload, mais ça ne serait pas très malin car il faudrait quand même charger les pilotes au préalable, justement pour lancer Qload. Alors comment faire ?

C'est pour cela que le démarrage d'un système d'exploitation se fait classiquement en plusieurs étapes. Dans notre cas on va en avoir 3 :

  1. Le moniteur ASSIST09 est lancé au RESET de l'ordinateur, et met en place la communication série vers le terminal
  2. Un chargeur présent dans le moniteur va récupérer et exécuter le contenu du secteur 1/piste 0 du lecteur de disquette (le "bootsector")
  3. Le bootsector charge le contenu de FLEX.SYS et lance le système d'exploitation.

L'intérêt de fonctionner comme cela est d'isoler les différentes couches et de les rendre "génériques" (dans le moniteur on peut très bien faire autre chose que récupérer le bootsector, et le bootsector peut très bien faire autre chose que charger Flex).

C'est quelque chose de très standard et encore actuel (par exemple pour Linux cela fait l'enchaînement BIOS->Grub->Kernel->Init).

Voici le code complet du secteur de boot , qui fait 206 octets (et donc tient bien dans un seul secteur !)

0001
0002                                   ; EQUATES
0003
0004 c07f                          STACK   EQU $C07F
0005 c300                          SCTBUF  EQU $C300   ; DATA SECTOR BUFFER
0006 e810                          PIA     EQU $E810
0007
0008 c100                              ORG $C100
0009
0010 c100 20 0a              [ 3 ] LOAD    BRA LOAD0
0011
0012 c102 00 00 00                         FCB 0,0,0
0013 c105 07                       TRK     FCB $07         ; FILE START TRACK
0014 c106 04                       SCT     FCB $04         ; FILE START SECTOR
0015 c107 00                       DNS     FCB 0           ; DENSITY FLAG
0016 c108 c1 00                    TADR    FDB $c100       ; TRANSFER ADDRESS
0017 c10a 00 00                    LADR    FDB 0           ; LOAD ADDRESS
0018
0019 c10c 10 ce c0 7f        [ 4 ] LOAD0   LDS #STACK      ; SETUP STACK
0020 c110 fc c1 05           [ 6 ]         LDD TRK         ; SETUP STARTING TRK & SCT
0021 c113 fd c3 00           [ 6 ]         STD SCTBUF
0022 c116 10 8e c4 00        [ 4 ]         LDY #SCTBUF+256
0023
0024                                   ; PERFORM ACTUAL FILE LOAD
0025
0026 c11a 8d 35              [ 7 ] LOAD1   BSR GETCH       ; GET A CHARACTER
0027 c11c 81 02              [ 2 ]         CMPA #$02       ; DATA RECORD HEADER?
0028 c11e 27 10              [ 3 ]         BEQ LOAD2       ; SKIP IF SO
0029 c120 81 16              [ 2 ]         CMPA #$16       ; XFR ADDRESS HEADER?
0030 c122 26 f6              [ 3 ]         BNE LOAD1       ; LOOP IF NEITHER
0031 c124 8d 2b              [ 7 ]         BSR GETCH       ; GET TRANSFER ADDRESS
0032 c126 b7 c1 08           [ 5 ]         STA TADR
0033 c129 8d 26              [ 7 ]         BSR GETCH
0034 c12b b7 c1 09           [ 5 ]         STA TADR+1
0035 c12e 20 ea              [ 3 ]         BRA LOAD1       ; CONTINUE LOAD
0036 c130 8d 1f              [ 7 ] LOAD2   BSR GETCH       ; GET LOAD ADDRESS
0037 c132 b7 c1 0a           [ 5 ]         STA LADR
0038 c135 8d 1a              [ 7 ]         BSR GETCH
0039 c137 b7 c1 0b           [ 5 ]         STA LADR+1
0040 c13a 8d 15              [ 7 ]         BSR GETCH       ; GET BYTE COUNT
0041 c13c 1f 89              [ 6 ]         TFR A,B         ; PUT IN B
0042 c13e 4d                 [ 2 ]         TSTA            ; TFR A,B + TSTA = TAB
0043 c13f 27 d9              [ 3 ]         BEQ LOAD1       ; LOOP IF COUNT=0
0044 c141 be c1 0a           [ 6 ]         LDX LADR        ; GET LOAD ADDRESS
0045 c144 34 14              [ 8 ] LOAD3   PSHS B,X
0046 c146 8d 09              [ 7 ]         BSR GETCH       ; GET A DATA CHARACTER
0047 c148 35 14              [ 8 ]         PULS B,X
0048 c14a a7 80              [ 6 ]         STA 0,X+        ; PUT CHARACTER
0049 c14c 5a                 [ 2 ]         DECB            ; END OF DATA IN RECORD?
0050 c14d 26 f5              [ 3 ]         BNE LOAD3       ; LOOP IF NOT
0051 c14f 20 c9              [ 3 ]         BRA LOAD1       ; GET ANOTHER RECORD
0052
0053                                   ; GET CHARACTER ROUTINE - READS A SECTOR IF NECESSARY
0054
0055 c151 10 8c c4 00        [ 5 ] GETCH   CMPY #SCTBUF+256    ; OUT OF DATA?
0056 c155 26 0f              [ 3 ]         BNE GETCH4          ; GO READ CHARACTER IF NOT
0057 c157 8e c3 00           [ 3 ] GETCH2  LDX #SCTBUF         ; POINT TO BUFFER
0058 c15a ec 84              [ 5 ]         LDD 0,X             ; GET FORWARD LINK
0059 c15c 27 0b              [ 3 ]         BEQ GO              ; IF ZERO, FILE IS LOADED
0060 c15e 8d 0d              [ 7 ]         BSR READ            ; READ NEXT SECTOR
0061 c160 26 9e              [ 3 ]         BNE LOAD            ; START OVER IF ERROR
0062 c162 10 8e c3 04        [ 4 ]         LDY #SCTBUF+4       ; POINT PAST LINK
0063 c166 a6 a0              [ 6 ] GETCH4  LDA 0,Y+            ; ELSE, GET A CHARACTER
0064 c168 39                 [ 5 ]         RTS
0065
0066                                   ; FILE IS LOADED, JUMP TO IT
0067
0068 c169 6e 9f c1 08        [ 8 ] GO      JMP [TADR]      ; JUMP TO TRANSFER ADDRESS
0069
0070                                   ; READ SINGLE SECTOR
0071                                   ;
0072                                   ; THIS ROUTINE MUST READ THE SECTOR WHOSE TRACK
0073                                   ; AND SECTOR ADDRESS ARE IN A ANB B ON ENTRY.
0074                                   ; THE DATA FROM THE SECTOR IS TO BE PLACED AT
0075                                   ; THE ADDRESS CONTAINED IN X ON ENTRY.
0076                                   ; IF ERRORS, A NOT-EQUAL CONDITION SHOULD BE
0077                                   ; RETURNED. THIS ROUTINE WILL HAVE TO DO SEEKS.
0078                                   ; A,B,X, AND U MAY BE DESTROYED BY THIS ROUTINE,
0079                                   ; BUT Y MUST BE PRESERVED.
0080                                   ; WESTERN DIGITAL EQUATES
0081
0082 e818                          COMREG  EQU $E818   ; COMMAND REGISTER
0083 e819                          TRKREG  EQU $E819   ; TRACK REGISTER
0084 e81a                          SECREG  EQU $E81A   ; SECTOR REGISTER
0085 e81b                          DATREG  EQU $E81B   ; DATA REGISTER
0086 0002                          DRQ     EQU 2       ; DRQ BIT MASK
0087 0001                          BUSY    EQU 1       ; BUSY MASK
0088 001c                          RDMSK   EQU $1C     ; READ ERROR MASK
0089 008c                          RDCMND  EQU $8C     ; READ COMMAND
0090 0018                          SKCMND  EQU $18     ; SEEK COMMAND
0091
0092 e810                          PRA     EQU $E810
0093
0094                                   ; READ ONE SECTOR
0095
0096 c16d 8d 2f              [ 7 ] READ    BSR XSEEK       ; SEEK TO TRACK
0097 c16f 86 8c              [ 2 ]         LDA #RDCMND     ; SETUP READ SECTOR COMMAND
0098 c171 b7 e8 18           [ 5 ]         STA COMREG      ; ISSUE READ COMMAND
0099 c174 8d 52              [ 7 ]         BSR DEL28       ; DELAY
0100 c176 5f                 [ 2 ]         CLRB            ; GET SECTOR LENGTH (=256)
0101 c177 8e c3 00           [ 3 ]         LDX #SCTBUF     ; POINT TO SECTOR BUFFER
0102 c17a b6 e8 18           [ 5 ] READ3   LDA COMREG      ; GET WD STATUS
0103 c17d 85 02              [ 2 ]         BITA #DRQ       ; CHECK FOR DATA
0104 c17f 26 08              [ 3 ]         BNE READ5       ; BRANCH IF DATA PRESENT
0105 c181 85 01              [ 2 ]         BITA #BUSY      ; CHECK IF BUSY
0106 c183 26 f5              [ 3 ]         BNE READ3       ; LOOP IF SO
0107 c185 1f 89              [ 6 ]         TFR A,B         ; SAVE ERROR CONDITION
0108 c187 20 0a              [ 3 ]         BRA READ6
0109 c189 b6 e8 1b           [ 5 ] READ5   LDA DATREG      ; GET DATA BYTE
0110 c18c a7 80              [ 6 ]         STA 0,X+        ; PUT IN MEMORY
0111 c18e 5a                 [ 2 ]         DECB            ; DEC THE COUNTER
0112 c18f 26 e9              [ 3 ]         BNE READ3       ; LOOP TIL DONE
0113 c191 8d 03              [ 7 ]         BSR XWAIT       ; WAIT TIL WD IS FINISHED
0114 c193 c5 1c              [ 2 ] READ6   BITB #RDMSK     ; MASK ERRORS
0115 c195 39                 [ 5 ]         RTS             ; RETURN
0116
0117                                   ; WAIT FOR 1771 TO FINISH COMMAND
0118
0119 c196 f6 e8 18           [ 5 ] XWAIT   LDB COMREG      ; GET WD STATUS
0120 c199 c5 01              [ 2 ]         BITB #BUSY      ; CHECK IF BUSY
0121 c19b 26 f9              [ 3 ]         BNE XWAIT       ; LOOP TIL NOT BUSY
0122 c19d 39                 [ 5 ]         RTS             ; RETURN
0123
0124                                   ; SEEK THE SPECIFIED TRACK
0125
0126 c19e 34 02              [ 6 ] XSEEK   PSHS A
0127 c1a0 b6 e8 10           [ 5 ]         LDA PRA
0128 c1a3 84 fb              [ 2 ]         ANDA #%11111011 ; pre-select side 0
0129 c1a5 c1 0a              [ 2 ]         CMPB #$0A       ; side 0 or 1 ?
0130 c1a7 23 02              [ 3 ]         BLS  XSEEK1     ; side 0
0131 c1a9 8a 04              [ 2 ]         ORA #%100       ; side 1
0132 c1ab b7 e8 10           [ 5 ] XSEEK1  STA PRA         ; set side
0133 c1ae 35 02              [ 6 ]         PULS A
0134 c1b0 f7 e8 1a           [ 5 ]         STB SECREG      ; SET SECTOR
0135 c1b3 b1 e8 19           [ 5 ]         CMPA TRKREG     ; DIF THAN LAST?
0136 c1b6 27 10              [ 3 ]         BEQ DEL28       ; EXIT IF NOT
0137 c1b8 b7 e8 1b           [ 5 ]         STA DATREG      ; SET NEW WD TRACK
0138 c1bb 8d 0b              [ 7 ]         BSR DEL28       ; GO DELAY
0139 c1bd 86 18              [ 2 ]         LDA #SKCMND     ; SETUP SEEK COMMAND
0140 c1bf b7 e8 18           [ 5 ]         STA COMREG      ; ISSUE SEEK COMMAND
0141 c1c2 8d 04              [ 7 ]         BSR DEL28       ; GO DELAY
0142 c1c4 8d d0              [ 7 ]         BSR XWAIT       ; WAIT TIL DONE
0143 c1c6 c5 10              [ 2 ]         BITB #$10 ; CHECK FOR SEEK ERROR
0144                                   ; DELAY
0145
0146 c1c8 bd c1 cb           [ 8 ] DEL28   JSR DEL14
0147 c1cb bd c1 ce           [ 8 ] DEL14   JSR DEL
0148 c1ce 39                 [ 5 ] DEL     RTS
0149
0150                                   END

On y retrouve en grande partie le code de QLoad et les routines du pilote de disque dont on a besoin ici (READ et SEEK). A la fin l'instruction "JMP [TADR]" lance Flex.

Les octets 5 et 6 contiennent la piste et le secteur où commence le fichier FLEX.SYS. Pour connaître les valeurs à indiquer, on peut utiliser la commande 

+++LINK FLEX.SYS

Qui va positionner ces 2 octets dans le secteur de boot. On peut soit relire le secteur de boot depuis le moniteur avec TestDisk pour trouver les valeurs.

Autre solution avec la commande DUMP, les 2 premiers chiffres sont la piste et le secteur (07 et 04 ici). Il faut appuyer sur ESC pour stopper le défilement :

+++DUMP FLEX.SYS                                                                
07 04                                                                           
07 05 00 01 02 CC 00 35 08 18 3A 00 00 04 00 00 _____L_5__:_____                
00 FF 1B 00 00 00 00 00 00 00 00 00 00 00 00 00 _______________                
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ________________                
00 00 00 00 00 00 00 01 00 00 00 00 00 02 CC 49 ______________LI                
01 00 02 CC 4E 5A 2B 2B 2B 04 3F 3F 3F 04 57 48 ___LNZ+++_???_WH                
41 54 3F 04 43 41 4E 27 54 20 54 52 41 4E 53 46 AT?_CAN'T TRANSF                
45 52 04 4E 4F 54 20 46 4F 55 4E 44 04 44 49 53 ER_NOT FOUND_DIS                
4B 20 45 52 52 4F 52 20 23 04 44 52 49 56 45 53 K ERROR #_DRIVES                
20 4E 4F 54 20 52 45 41 44 59 04 47 45 54 00 D2  NOT READY_GET_R                
0E 4D 4F 4E 00 D3 4A 00 27 10 03 E8 00 64 00 0A _MON_SJ_'__h_d__                
02 CC C0 01 39 02 CC D8 01 39 02 CC E4 01 39 02 _L@_9_LX_9_Ld_9_                
CC F8 C4 01 00 00 00 00 00 00 00 7E CD 57 7E CD LxD________~MW~M                
6B 7E CD B2 7E CD 09 7E CD 0C 7E CD 0F 7E CD 12 k~M2~M_~M_~M_~M_                
7E CE FA 7E CF 40 7E CE 2C 7E CE 82 7E CF ED 7E ~Nz~O@~N,~N_~Om~                
CE B4 7E D0 0E 7E CD EB 7E D0 36 7E D1 A1 7E D0 N4~P_~Mk~P6~Q!~P                
EA 7E D3 48 7E CF 85 7E CF D4 7E D2 7E 7E D1 2E j~SH~O_~OT~R~~Q.                

                                            

Pour écrire le secteur sur le disque j'ai fait un petit programme qui est l'équivalent de TestDisk, mais en écriture :

0001
0002 de15                          INIT    EQU $DE15
0003 de0c                          DRIVE   EQU $DE0C
0004 de09                          RESTORE EQU $DE09
0005 de00                          READ    EQU $DE00
0006 de03                          WRITE   EQU $DE03
0007
0008 c200                          SCTBUF  EQU $C200 ; DATA SECTOR BUFFER
0009
0010 c100                          BUFFER  EQU $C100
0011
0012 0020                              ORG $020
0013
0014 0020                          TRACK RMB 1
0015 0021                          SECTOR RMB 1
0016
0017 0500                              ORG $0500
0018
0019 0500 bd de 15           [ 8 ]     JSR INIT
0020
0021 0503 8e c2 00           [ 3 ]     LDX #SCTBUF
0022 0506 6f 03              [ 7 ]     CLR 3,X         ; SET DRIVE 0
0023 0508 bd de 0c           [ 8 ]     JSR DRIVE
0024
0025 050b 8e c2 00           [ 3 ]     LDX #SCTBUF
0026 050e bd de 09           [ 8 ]     JSR RESTORE
0027
0028 0511 8e c1 00           [ 3 ]     LDX #BUFFER
0029
0030 0514 86 00              [ 2 ]     LDA #0
0031 0516 97 20              [ 4 ]     STA TRACK
0032 0518 86 01              [ 2 ]     LDA #1
0033 051a 97 21              [ 4 ]     STA SECTOR
0034
0035 051c dc 20              [ 5 ]     LDD TRACK
0036 051e bd de 03           [ 8 ]     JSR WRITE
0037
0038
0039 0521 39                 [ 5 ]     RTS


Pour écrire le secteur de boot depuis le moniteur, il faut :
  • Charger le pilote disque en mémoire (Commande L)
  • Charger le secteur de boot en mémoire (Commande L)
  • Charger WriteBootSector en mémoire (Commande L)
  • Lancer WriteBootSector (Commande C 0500)

On peut ensuite utiliser TestDisk pour relire le secteur de boot et vérifier que son contenu est bien le bon.

Le chargeur pour le moniteur

Dernier élément, le chargeur du moniteur, dont le rôle est de récupérer le secteur de boot et de l'exécuter. 

J'ai ajouté à Assist09 la routine nécessaire, qui ressemble beaucoup à TestDisk + une partie du pilote disque :

1759
1760     *******************************************************
1761                               * FLEX LOADER
1762     *******************************************************
1763
1764
1765 c100                          XLOADER  EQU $C100
1766
1767 f804 bd f8 15           [ 8 ] FLEX    JSR XINIT
1768
1769 f807 bd f8 56           [ 8 ]         JSR XRESTORE
1770
1771 f80a 8e c1 00           [ 3 ]         LDX #XLOADER
1772 f80d 86 01              [ 2 ]         LDA #1
1773 f80f bd f8 29           [ 8 ]         JSR XREAD
1774
1775 f812 7e c1 00           [ 4 ]         JMP XLOADER
1776
1777
1778 0002                          DRQ     EQU 2       ; DRQ BIT MASK
1779 0001                          BUSY    EQU 1       ; BUSY MASK
1780 001c                          RDMSK   EQU $1C     ; READ ERROR MASK
1781
1782 e818                          COMREG  EQU $E818   ; COMMMAND REGISTER
1783 e819                          TRKREG  EQU $E819   ; TRACK REGISTER
1784 e81a                          SECREG  EQU $E81A   ; SECTOR REGISTER
1785 e81b                          DATREG  EQU $E81B   ; DATA REGISTER
1786 008c                          RDCMND  EQU $8C     ; READ COMMAND
1787 0008                          RSCMND  EQU $08     ; RESTORE COMMAND
1788
1789
1790                                   ; INIT AND WARM
1791                                   ;
1792                                   ; DRIVER INITIALIZATION
1793
1794
1795 f815 4f                 [ 2 ] XINIT   CLRA            ; select PIA control register
1796 f816 b7 e8 11           [ 5 ]         STA CRA
1797 f819 86 0f              [ 2 ]         LDA #$0F        ; PORTA b0..b3 = output
1798 f81b b7 e8 10           [ 5 ]         STA DDRA
1799 f81e 86 3c              [ 2 ]         LDA #%00111100  ; select PIA output register
1800 f820 b7 e8 11           [ 5 ]         STA CRA
1801 f823 86 09              [ 2 ]         LDA #%00001001  ; b3=1 (FM)
1802                                                       ; b2=0 (side 0)
1803                                                       ; b1-0 = 01 ( drive 1 select)
1804 f825 b7 e8 10           [ 5 ]         STA PRA
1805
1806 f828 39                 [ 5 ]         RTS
1807
1808                                   ; READ
1809                                   ;
1810                                   ; READ ONE SECTOR
1811                                   ;
1812                                   ; ENTRY -   (X) = Address in memory where sector is to be placed.
1813                                   ;           (A) = Track Number
1814                                   ; EXIT -    (X) May be destroyed
1815                                   ;           (A) May be destroyed
1816                                   ;           (B) = Error condition
1817                                   ;           (Z) = 1 if no error
1818                                   ;               = 0 if an erro
1819
1820 f829 86 8c              [ 2 ] XREAD   LDA #RDCMND     ; SETUP READ SECTOR COMMAND
1821 f82b b7 e8 18           [ 5 ]         STA COMREG      ; ISSUE READ COMMAND
1822 f82e 17 00 32           [ 9 ]         LBSR XDEL28      ; DELAY
1823 f831 5f                 [ 2 ]         CLRB            ; GET SECTOR LENGTH (=256)
1824 f832 b6 e8 18           [ 5 ] XREAD3   LDA COMREG      ; GET WD STATUS
1825 f835 85 02              [ 2 ]         BITA #DRQ       ; CHECK FOR DATA
1826 f837 26 08              [ 3 ]         BNE XREAD5       ; BRANCH IF DATA PRESENT
1827 f839 85 01              [ 2 ]         BITA #BUSY      ; CHECK IF BUSY
1828 f83b 26 f5              [ 3 ]         BNE XREAD3       ; LOOP IF SO
1829 f83d 1f 89              [ 6 ]         TFR A,B         ; ERROR IF NOT
1830 f83f 20 0a              [ 3 ]         BRA XREAD6
1831 f841 b6 e8 1b           [ 5 ] XREAD5   LDA DATREG      ; GET DATA BYTE
1832 f844 a7 80              [ 6 ]         STA 0,X+        ; PUT IN MEMORY
1833 f846 5a                 [ 2 ]         DECB            ; DEC THE COUNTER
1834 f847 26 e9              [ 3 ]         BNE XREAD3       ; LOOP TIL DONE
1835 f849 8d 03              [ 7 ]         BSR XWAIT        ; WAIT TIL WD IS FINISHED
1836 f84b c5 1c              [ 2 ] XREAD6   BITB #RDMSK     ; MASK ERRORS
1837 f84d 39                 [ 5 ]         RTS             ; RETURN
1838
1839                                   ; WAIT
1840                                   ;
1841                                   ; WAIT FOR 1771 TO FINISH COMMAND
1842
1843 f84e f6 e8 18           [ 5 ] XWAIT    LDB COMREG      ; GET WD STATUS
1844 f851 c5 01              [ 2 ]         BITB #BUSY      ; CHECK IF BUSY
1845 f853 26 f9              [ 3 ]         BNE XWAIT        ; LOOP TIL NOT BUSY
1846 f855 39                 [ 5 ]         RTS             ; RETURN
1847
1848                                   ; RESTORE
1849
1850 f856 86 08              [ 2 ] XRESTORE LDA #RSCMND     ; SETUP RESTORE COMMAND
1851 f858 b7 e8 18           [ 5 ]         STA COMREG      ; ISSUE RESTORE COMMAND
1852 f85b 8d 06              [ 7 ]         BSR XDEL28       ; DELAY
1853 f85d 17 ff ee           [ 9 ]         LBSR XWAIT       ; WAIT TIL WD IS FINISHED
1854 f860 c5 d8              [ 2 ]         BITB #$D8       ; CHECK FOR ERROR
1855 f862 39                 [ 5 ]         RTS             ; RETURN
1856
1857                                   ; DELAY
1858
1859 f863 17 00 00           [ 9 ] XDEL28   LBSR XDEL14
1860 f866 17 00 00           [ 9 ] XDEL14   LBSR XDEL
1861 f869 39                 [ 5 ] XDEL     RTS

 
Pour en faire une commande du moniteur c'est assez simple, il y a dans le code de ASSIST09 un tableau "CMDTBL" contenant la lettre correspondant à la commande et l'adresse permettant de l'exécuter :

0541 f1c3 04                               FCB     4
0542 f1c4 46                               FCC     /F/             ; 'FLEX' COMMAND
0543 f1c5 06 3f                            FDB     FLEX-*

 
J'ai choisi très originalement la commande "F". Une fois le code de Assist09 assemblé et programmé dans l'EPROM, au prochain boot on peut enfin démarrer le système d'exploitation en appuyant juste sur une lettre, comme ceci :

ASSIST09                                                                        
>F                                                                              
                                                                                
FLEX 9.1                                                                        
                                                                                
DATE (MM,DD,YY)?


Et voilà le travail :-)

Conclusion provisoire

Avec cet article (et les 4 précédents) j'ai décrit tout ce que j'ai réalisé jusqu'à aujourd'hui sur cet ordinateur. Mais il reste encore de nombreuses tâches à réaliser !

En voici quelques unes :

  • Adapter NEWDISK pour pouvoir formatter des disquettes depuis Flex
  • Gérer la double densité
  • Porter PUTBOOT pour rendre une disquette bootable depuis Flex
  • Trouver un moyen de copier des fichiers sur la disquette via le port série depuis Assist09
  • Ajouter le pilote pour l'imprimante
  • Utiliser l'horloge RTC pour éviter d'avoir à saisir la date à chaque démarrage
  • Porter Flex V2 (le portage dans ces articles a été fait avec la V1)
  • Assembler la version PCB
  • Ca serait bien d'avoir la gestion clavier/écran intégré à l'ordinateur, peut être que le connecteur d'extension sur la version PCB pourrait être utile !
  • Explorer l'archive de disques Flex pour augmenter la logithèque de cet ordinateur

Et il y en a probablement d'autres !

Ressources

Le projet github est disponible ici : https://github.com/laurent-fr/LFlex21

  • Le code source présenté dans cet article est dans le dossier /software/flex 
  • Une copie du Flex Adaptation Guide est dans le dossier /doc (fichier 6809fadg.pdf)