Win3x.Org

Windows & DOS Community

Apprentissage du BASIC Microsoft sous DOS

Répondre   Page 1 sur 1  [ 4 messages ]
Auteur Message
gm86
Sujet du message : Apprentissage du BASIC Microsoft sous DOS
Publié : 21 août 2015 14:04
Membre inscrit
Avatar de l’utilisateur
Hors-ligne
 
Messages : 630
Inscription : 01 sept. 2008 19:07
 
Je fais suite à mon message au lien suivant : viewtopic.php?p=138605#p138605

Je lance un projet sur ce qui m'a fait apprécier l'utilisation d'un ordinateur, l'apprentissage du BASIC. C'était il n'y a pas si longtemps (si, si, je vous promets, je n'ai guère changé depuis !), j'ai découvert avec enthousiasme la possibilité de faire faire ce qu'on désirait à une machine via des bouquins des années 80. Les mots programmation et BASIC me sont restés liés. Cela a commencé brièvement avec un TO8, plus sérieusement sur une TI-82, et enfin, sur PC.

Le langage qui m'a le plus marqué, a été en fait celui le plus basique, c'est le cas de la dire. Il s'agit d'un BASIC générique de la firme Microsoft. Il se trouvait en ROM sur la plupart des vieux micro-ordinateurs, mais surtout, a été présent en version disque pour les systèmes d'exploitation CP/M puis MS-DOS. Pas de graphisme ni de son pour cette dernière, mais des instructions puissantes malgré une certaine lenteur dans l'interprétation et une mémoire limitée.

Voici les prémisses à ce projet sur ce forum :
Citation :
gm86 - Jeudi 7 août 2014

J'ai déjà fait état des BASIC Microsoft pour MS-DOS. J'avais déconseillé la version 5.28 dont les fonctions USR étaient inutilisables.

La version précédente, 5.27, est disponible dans MS-DOS 1.25 pour 8086 SCP Gazelle avec contrôleur Cromemco 16FDC

La révision 5.21 est aussi disponible mais en deux interprètes mis à jour à des dates différentes dans l'archive de MS-DOS 1.25 pour 8086 SCP Gazelle contrôleur Tarbell

La mise à jour la plus récente est aussi plus gourmande en mémoire mais est censée avoir de meilleures routines mathématiques. Étant pour tout DOS, cet interprète BASIC permet d'ouvrir un fichier séquentiel en mode ajout (paramètre "A" de l'instruction OPEN). Néanmoins, OPEN n'y accepte pas encore la syntaxe de GW-BASIC (OPEN fichier FOR mode AS #n). De plus, cette ancienne version n'inclut ni les fonctions non documentées de temps ni les instructions de fichiers mémoire. L'entrée des caractères ASCII étendus n'y est pas non plus permise. Elle n'est donc guère différente des versions pour CP/M.



Big Monstro - Mardi 7 octobre 2014

Proposition datée du 7 août... et acceptée le 7 octobre

Désolé pour le retard, mais ton topic se trouvait dans la section "Informatique et vieux systèmes" ; à la lecture de ta dernière phrase (proposition d'une archive avec texte explicatif), j'ai déplacé le sujet vers la section adéquate.

Cordialement,
Big Monstro



gm86 - Vendredi 17 octobre 2014

C'est gentil. Avant d'en faire une proposition, je voulais déjà présenter ces liens. Néanmoins, vu qu'une bonne partie du travail a été faite par rapport au texte explicatif (cf. post-scriptum dans viewtopic.php?p=95496#p95496), je devrais m'atteler à faire enfin cette archive.


Ajout du 25 octobre.
Je compte réunir les versions 5.27 et 5.28 pour MS-DOS, trois mises à jour de la version 5.21 pour 86-DOS et la version 5.22 pour CP/M-86. Pour pouvoir lancer cette dernière sous DOS, je compte écrire un petit shell destiné à ce fichier de type .CMD :
- lire l'en-tête pour charger correctement les segments de code et de données ;
- rediriger les appels à l'interruption E0h du BDOS vers l'entrée de compatibilité CP/M du DOS.
Quant au texte explicatif, il aura le contenu suivant :
- une introduction à ce qui m'a conduit à m'intéresser à ces interprètes (langage qui était un standard de fait, problème de l'appel BDOS style CP/M sous DOS) ;
- les différences entre versions et principalement avec la version 5 pour CP/M très présente jadis dans les livres et magazines ;
- l'appel de routines en langage 8086 ;
- quelques mots sur la programmation structurée (schéma entrées/traitement/sorties, menus, renumérotation).


Ajout du 6 novembre.
Début et fin du futur shell au format .COM :
INTERR  SEGMENT AT 0            ; À la base, table des vecteurs d'interruption.
 
        ORG     0C0h            ; L'appel lointain situé à l'offset 5 du PSP
Call5   LABEL   FAR             ; pointe cette adresse sauf si le DOS est en
                                ; mémoire basse alors que la HMA est active.
INTERR  ENDS                    ; Le format Fxxx:FEF0h (maxi) en est la cause.



CODE    SEGMENT
        ASSUME  CS:CODE

        ORG     100h
Debut:
        jmp     Installe

CPM:
        call    Call5           ; Remplace l'appel lointain cité plus haut.

BDOS    PROC    FAR
        call    CPM             ; Simule le CALL 5 du CP/M.
        ret     2               ; Préserve l'état des drapeaux.
BDOS    ENDP

Installe:
        mov     ax,25E0h        ; Déroute l'appel à l'interruption du CP/M-86.
        mov     dx,offset BDOS
        int     21h

(...)
CODE    ENDS

        END     Debut
L'appel proche CALL CPM remplace avantageusement un CALL 5 (plus de problème d'appel lointain à l'offset 5 du PSP contournant l'unique Mio de mémoire du processeur 8086 afin d'avoir le haut de la mémoire inscrit à l'offset suivant). Notons qu'un saut lointain (JMP segment:offset) se trouve à l'adresse 0:00C0h et écrase les vecteurs des interruptions 30h et 31h.


Ajout du 6 décembre.
Je teste le chargement des applications CMD à l'aide d'une version modifiée de DOS Plus de Digital Research. En effet, leur en-tête contient des indications à l'instar d'un exécutable EXE. On peut le lire à l'aide du programme BASIC HEADER.BAS d'Emmanuel Roche.
Pour simuler une petite mémoire afin d'étudier l'allocation mémoire, j'utilise une astuce donnée par Peter Norton dans son guide du programmeur : sous DOS et sans gestionnaire de mémoire chargé, écrire la taille mémoire disponible voulue dans le mot situé à l'adresse 40h:13h puis effectuer un démarrage sans POST (interruption 19h).
L'utilitaire de mise au point SID-86 donne l'allocation mémoire du programme qu'il charge. Néanmoins, si ce dernier est un interprète BASIC, je trouve moi-même ses informations dans la « page zéro » du segment de données.



Der Siebte Schatten - Jeudi 12 février 2015

Où en es-tu avec l'archive gm86 ?



gm86 - Mardi 17 mars 2015

Je pense pouvoir écrire bientôt le petit shell. Jusqu'alors, j'ai consacré beaucoup de temps à d'autres loisirs et changé de rythme de vie avec mon nouveau travail ; mais je me remets à la micro-informatique.
MBASIC.CMD est une application CP/M-86 qui suit le modèle compact de mémoire : un segment pour le code et un autre pour les données. Il n'utilise que des services compatibles avec DOS. J'envisage donc une application MBASIC.COM, à ne pas confondre avec la version 8 bits pour CP/M qui ne nous concerne pas. Elle ne servira qu'à détourner l'interruption 224 du BDOS, relever la mémoire disponible indiquée dans le PSP, lire l'en-tête de MBASIC.CMD et le charger.

Quant à la documentation, elle m'occupera afin de redonner goût à l'usage de ces vieilles interfaces de programmation.

_________________

C:\ONGRTLNS.W95


Haut
Profil Citer
Big Monstro
Sujet du message : Re: Apprentissage du BASIC Microsoft sous DOS
Publié : 21 août 2015 16:40
Administrateur
Avatar de l’utilisateur
Hors-ligne
 
Messages : 3476
Inscription : 27 juin 2003 15:15
PC Rétro : 80486 DX2/66, MS-DOS & Windows 3.11
 
Permettre le lancement d'un exécutable CMD sous MS-DOS, voilà une idée bien sympathique...

Non seulement cette initiative est bien ancrée dans la thématique originelle de Win3x.Org (l'informatique 16-bit) mais elle implique aussi de la programmation. Voilà le genre de projet que j'attendais depuis si longtemps !!!

En effet, c'est triste à dire mais on n'a guère vu de projets de programmation 16-bit au-delà de 2007/2008 (à l'exception de SuperKoko en 2010). En outre, le niveau général a bien baissé : les quelques réalisations intéressantes (pour systèmes 32-bit) étaient noyées par une infinité de "projets" insipides se résumant à personnaliser un Windows et à s'en attribuer les mérites pour avoir prétendument créé "son" système d'exploitation. La plupart du temps, ce n'était jamais achevé et parfois même pas entamé ; juste de l'esbroufe pour caresser son ego. Dix fois, ça passe encore, mais quand on voit ça 100x fois...

On a l'impression que les auteurs de projets ont oublié l'existence de la section "Programmation et librairies", créée en novembre 2006 pour les aider à concevoir des logiciels 16-bit. A tel point que j'ai envisagé - à plusieurs reprises - de supprimer la dite section et relocaliser ses sujets dans "Applications". A l'évidence, son contenu n'intéresse plus suffisamment de monde pour justifier la séparation.

Mais vous êtes, gm86, l'exception qui confirme la règle et je vous remercie d'emblée pour nous avoir présenté ce projet. Bonne continuation !

Très cordialement,
Big Monstro


Haut
Profil Citer
gm86
Sujet du message : Re: Apprentissage du BASIC Microsoft sous DOS
Publié : 16 avr. 2017 12:46
Membre inscrit
Avatar de l’utilisateur
Hors-ligne
 
Messages : 630
Inscription : 01 sept. 2008 19:07
 
Vendredi 21/08/2015

Hélas, le shell est vraiment la partie modeste du projet. On peut faire la même chose avec DEBUG. Un lanceur universel d'applications CP/M-86 est trop ambition — d'autres ont déjà relevé le défi. Le chargement des différents modèles de mémoire n'est pas si facile à étudier sous DOS Plus.
En revanche, un patch pour un logiciel CP/M-86 compatible avec la partie de l'API CP/M reprise par le DOS est simple.

Merci pour le soutien, manier le seize bits équivaut à un pêché chez moi !

La partie la plus passionnante est le cours. Le BASIC est vraiment un langage agréable pour parler simplement à une machine. C'est une possibilité de nos machines depuis l'ère des 8 bits.

Mercredi 30/09/15

Je remonte le sujet pour une précision. Lorsque le shell se lancera, la zone de transfert de données, dite DTA, se trouvera dans son PSP. Or MBASIC.CMD possèdera son propre PSP au début du segment de données. Il faudra donc y redéfinir la nouvelle DTA avant de lui passer la main.

Jeudi 28/01/16

Actuellement, j'ai un accès limité à l'informatique. Je présenterai une prochaine fois le format COM, sous DOS bien sûr, et le format CMD de CP/M-86, équivalent à l'exécutable EXE du DOS.
Le but est de mieux appréhender les mécanismes d'exécution des applications.

Samedi 30/04/16

Dans une ancienne revue en anglais archivée sur le net, un lecteur écrivait au sujet du bug des instructions FIX et INT avec le zéro négatif en double précision. Sur processeur 8086, seule la version 5.28 réglait ce souci alors que les révisions 5.2x 8 bits étaient corrigées.
Cf. http://books.google.fr/books?id=WYnHD9W ... =RA2-PA252
N'ayant pas réapparu sur ce forum depuis un moment, j'ai décidé d'attaquer l'impossibilité par cette dernière version pour MS-DOS de reconnaître la fonction USR. J'ai profité du fait que j'ai coupé à mon astreinte cette nuit pour faire une comparaison avec la révision précédente. J'ai deux solutions : taper l'instruction NOP à l'offset 1D4Ah via DEBUG, ou remplacer l'octet 83h à l'offset 1D47h par 81h. Dans les deux cas, on évite l'octet FFh superflu après l'instruction CMP DX,-1 soit en le supprimant (CMP DX,-01h puis NOP) soit en l'intégrant (CMP DX,0FFFFh). En fait, Microsoft avait tenté de remplacer l'ancienne instruction CMP DL,-1 qui causait le même bug lorsque la fonction USR pointait une adresse finissant par FFh.

Je me suis aidé du vieux logiciel CRACKER de la société Wendin. J'expliquerai la méthode utilisée pour faire le diagnostic conjointement à DEBUG.

Vendredi 06/05/16

Aujourd'hui, je vais vous expliquer comment j'ai débuggé le BASIC Microsoft pour reconnaître à nouveau les fonctions USR[x].
Les fonctions USR sont une ancienne façon d'appeler des routines en langage machine. Un maximum de dix peut être défini (par défaut, la première équivaut à USR0). Les appels CALL sont plus souples d'emploi et acceptent un nombre quelconque de paramètres. Néanmoins, bien que USR ne fut gardé que par raison de compatibilité, j'estime que l'emploi d'une fonction en langage machine est un luxe dont je ne me priverai pas.
Malheureusement, le dernier BASIC Microsoft pour MS-DOS (pas spécifique au PC) renvoie un message d'erreur lorsqu'il rencontre une fonction USR, comme si nous avions oublié de la déclarer. Or, cette version 5.28 est améliorée par rapport à la précédente. Par exemple, un 0# enregistré en -0# ne pose plus de problème pour les fonctions INT et FIX qui renvoient la partie entière d'un nombre positif voire négatif.
En effet, on avait droit à une erreur arithmétique avec les anciens BASIC-86 :
10 A#=0
20 A#=A#-0   'ou A#=-A#
30 PRINT FIX(A#),INT(A#)
RUN
-1            -1
Les BASIC 5.2x de CP/M corrigeaient pourtant ce souci apparu après l'antique version 4.51, qui est aux révisions 5.x ce que le FORTRAN 66 est à la norme 77.
C'est donc bien triste que le dernier BASIC-86, enfin débarrassé de cette faiblesse, introduise un nouveau problème. Nouveau ? Pas si sûr, mais je préfère ne rien dire pour l'instant.

Dans un premier temps, modifions un programme de Peter Norton qui illustrait l'appel CALL dans un BASIC interprété. La routine d'assemblage d'origine renvoyait la valeur absolue d'un entier (autre que -32768 vu que 32768 n'existe pas en mot signé de 16 bits). Ce que je souhaite, c'est traiter aussi les réels et avoir une fonction fonctionnant comme ABS.
Voici la routine à assembler avec l'assembleur de Microsoft :
; Assemblage :
;       MASM ABS;
;       LINK ABS;
;       EXE2BIN ABS


CODE    SEGMENT BYTE
        ASSUME  CS:CODE

        DB      0FDh                    ; En-tête pour BLOAD
        DW      2 dup(0)
        DW      TailleRoutine

ABS     PROC    FAR
        cmp     al,3
         jb     Entier
         je     Retour
        and     byte ptr [bx+2],7Fh     ; Efface le bit de signe
Retour:
        ret                             ; Quitte la procédure
Entier:
        mov     ax,[bx]

        cwd                             ; DX = FFFFh si entier négatif
        xor     ax,dx                   ; Si négatif, complément à un
        sub     ax,dx                   ; puis à deux

        mov     [bx],ax                 ; Remplace nombre par valeur absolue
        ret

ABS     ENDP

TailleRoutine   EQU     $-ABS

CODE    ENDS

        END
        
Et voici le programme BASIC — avec erreur de débordement — correspondant :
10 REM Cf. Peter Norton.
100 DEFINT A-Z: OPTION BASE 1
110 ' Réserve la mémoire pour les variables
120 X=0
130 SUBADDR=0
140 ' Réserve la mémoire pour la routine
150 DIM SUBAREA(22)
160 SUBADDR=VARPTR(SUBAREA(1))
170 BLOAD "abs.bin", SUBADDR
180 DEF USR=SUBADDR
190 :
200 FOR X=-32760 TO -32768! STEP -1
210 PRINT "ABS("; X; ")="; USR(X)
220 NEXT
230 END
Résultat avec la version 5.28 :
run
ABS(-32760 )=
Illegal function call in 210
Ok
Et pourtant la fonction a bien été définie auparavant, à la ligne 180.
Résultat avec la révision 5.27 :
run
ABS(-32760 )= 32760
ABS(-32761 )= 32761
ABS(-32762 )= 32762
ABS(-32763 )= 32763
ABS(-32764 )= 32764
ABS(-32765 )= 32765
ABS(-32766 )= 32766
ABS(-32767 )= 32767
ABS(-32768 )=-32768
Overflow in 220
Ok
Là nous sommes d'accord.

Dans un second temps, prenons les choses en main. Utilisons l'outil de mise au point DEBUG sur la version 5.28.
C>debug msbasic.com abs
-t

AX=0000  BX=0000  CX=7C00  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000
DS=1FA3  ES=1FA3  SS=1FA3  CS=1FA3  IP=7A24   NV UP EI PL NZ NA PO NC
1FA3:7A24 8CCA          MOV     DX,CS
-u
1FA3:7A24 8CCA          MOV     DX,CS
1FA3:7A26 81C2CD07      ADD     DX,07CD
1FA3:7A2A B426          MOV     AH,26
1FA3:7A2C CD21          INT     21
1FA3:7A2E 8EDA          MOV     DS,DX
1FA3:7A30 50            PUSH    AX
1FA3:7A31 26            ES:
1FA3:7A32 A11600        MOV     AX,[0016]
1FA3:7A35 90            NOP
1FA3:7A36 A36801        MOV     [0168],AX
1FA3:7A39 8CC0          MOV     AX,ES
1FA3:7A3B 90            NOP
1FA3:7A3C A36A01        MOV     [016A],AX
1FA3:7A3F 26            ES:
1FA3:7A40 A31600        MOV     [0016],AX
1FA3:7A43 58            POP     AX
-g7a30

AX=26F0  BX=0000  CX=7C00  DX=2770  SP=FFFE  BP=0000  SI=0000  DI=0000
DS=2770  ES=1FA3  SS=1FA3  CS=1FA3  IP=7A30   NV UP EI PL NZ AC PO NC
1FA3:7A30 50            PUSH    AX
-g
Microsoft BASIC Version 5.28
[MS-DOS Version]
Copyright 1977-1983 (C) by Microsoft
Created: 24-May-83
62003 Bytes free
ABS(-32760 )=
Illegal function call in 210
Ok
? hex$(subaddr)
C15
Ok
system

Le programme s'est terminé normalement
-s2770:0 ffff 15 c
2770:011D
2770:0C05
2770:2C6F
2770:AD19
-d2770:110
2770:0110  80 52 C7 4F 80 E4 00 CB-3A 00 00 00 00 15 0C FF   .R.O....:.......
2770:0120  FF FF FF FF FF FF FF FF-FF FF FF FF FF FF FF FF   ................
2770:0130  FF 01 00 02 F0 13 FE 18-17 03 FE 18 34 44 A3 1F   ............4D..
2770:0140  C0 45 A3 1F 05 00 00 00-70 84 50 1F 1C 1F 1E 1F   .E......p.P.....
2770:0150  1E 1F 1F 1E 1F 1E 1F 38-00 00 00 00 EE FE FF FF   .......8........
2770:0160  B9 0A 24 05 4F 03 FE 18-ED 18 A3 1F 00 28 0A 01   ..$.O........(..
2770:0170  00 00 00 00 00 7B 7E 01-7E 01 7E 01 9E 01 00 00   .....{~.~.~.....
2770:0180  00 00 00 00 00 00 08 00-92 B2 FF FF BD 02 00 00   ................
-q
Bingo ! notre adresse C15h (rien à voir avec un célèbre utilitaire qui aurait troqué le diesel ou l'essence contre un moteur à hydrogène), qui est l'offset de notre routine dans le segment de données du BASIC, est répertoriée au début d'une table suivie par neuf mots valant FFFFh. Ce ne peut correspondre qu'aux fonctions USR allant de USR0 à USR9.
Désassemblons MSBASIC.COM avec CRACKER de Wendin et cherchons une référence à cette table dans le fichier MSBASIC.LST obtenu :
1000:fffe                  Proc170:
2f18:010c                  Data11:
2f18:0111                  Data147:
2f18:0113                  Data148:
2f18:0131                  Data54:
Non, pas de cette manière. Recherchons plutôt l'offset de cette table qui est 11Dh :
1000:1d86                  Jmp543:
1000:1d86 87 da                     XCHG   BX, DX
1000:1d88 bb 1d01                   MOV    BX, 011dh
1000:1d8b 03 d9                     ADD    BX, CX
1000:1d8d 87 da                     XCHG   BX, DX
1000:1d8f c3                        RET
Il s'agit de la fin de la procédure Proc155. Elle est appelée un peu plus haut :
1000:1d37                  Jmp542:
1000:1d37 e8 c456                   CALL   Proc154
1000:1d3a e8 2f00                   CALL   Proc155
1000:1d3d 52                        PUSH   DX
1000:1d3e e8 e3fd                   CALL   Proc146
1000:1d41 5e                        POP    SI
1000:1d42 87 de                     XCHG   BX, SI
1000:1d44 56                        PUSH   SI
1000:1d45 8b 17                     MOV    DX, WORD PTR [BX]
1000:1d47 83 fa ff                  CMP    DX, ffh
1000:1d4a ff 75 03                  PUSH   WORD PTR [DI+Data105]
1000:1d4d e9 2af4                   JMP    Jmp125
S'ensuit ce qui ressemble à du code obscur, car non traduit par CRACKER, mais qui est la conséquence d'un bug. Regardons l'instruction en 1D45h qui copie un mot de la table dans DX.
DX est ensuite comparé à l'octet FFh. Et là est le piège : c'est un octet signé, donc équivalent à -1 et par conséquent égal au mot signé FFFFh. On a affaire à une instruction optimisée (gain d'espace d'un octet). Cependant, elle est tout de même suivie d'un autre octet FFh qui modifie l'instruction suivante. Remplaçons ce déchet par l'instruction NOP pour le prouver :
C>debug msbasic.com abs
-u1d37
1FA3:1D37 E8C456        CALL    73FE
1FA3:1D3A E82F00        CALL    1D6C
1FA3:1D3D 52            PUSH    DX
1FA3:1D3E E8E3FD        CALL    1B24
1FA3:1D41 5E            POP     SI
1FA3:1D42 87DE          XCHG    BX,SI
1FA3:1D44 56            PUSH    SI
1FA3:1D45 8B17          MOV     DX,[BX]
1FA3:1D47 83FAFF        CMP     DX,-01
1FA3:1D4A FF7503        PUSH    [DI+03]
1FA3:1D4D E92AF4        JMP     117A
1FA3:1D50 0E            PUSH    CS
1FA3:1D51 BB0258        MOV     BX,5802
1FA3:1D54 53            PUSH    BX
1FA3:1D55 FF36D004      PUSH    [04D0]
-a1d4a
1FA3:1D4A nop
1FA3:1D4B
-g
Microsoft BASIC Version 5.28
[MS-DOS Version]
Copyright 1977-1983 (C) by Microsoft
Created: 24-May-83
62003 Bytes free
ABS(-32760 )= 32760
ABS(-32761 )= 32761
ABS(-32762 )= 32762
ABS(-32763 )= 32763
ABS(-32764 )= 32764
ABS(-32765 )= 32765
ABS(-32766 )= 32766
ABS(-32767 )= 32767
ABS(-32768 )=-32768
Overflow in 220
Ok
system

Le programme s'est terminé normalement
-q
Bug résolu. En fait, on peut modifier l'instruction CMP pour comparer un mot à DX.
C>debug msbasic.com abs
-e1d47
1FA3:1D47  83.81
-u1d47,1d50
1FA3:1D47 81FAFFFF      CMP     DX,FFFF
1FA3:1D4B 7503          JNZ     1D50
1FA3:1D4D E92AF4        JMP     117A
1FA3:1D50 0E            PUSH    CS
-w
Écriture de 07C00 octets
-a80
1FA3:0080 db 4
1FA3:0081 db ' abs'
1FA3:0085 db d
1FA3:0086
-g
Microsoft BASIC Version 5.28
[MS-DOS Version]
Copyright 1977-1983 (C) by Microsoft
Created: 24-May-83
62003 Bytes free
ABS(-32760 )= 32760
ABS(-32761 )= 32761
ABS(-32762 )= 32762
ABS(-32763 )= 32763
ABS(-32764 )= 32764
ABS(-32765 )= 32765
ABS(-32766 )= 32766
ABS(-32767 )= 32767
ABS(-32768 )=-32768
Overflow in 220
Ok
system

Le programme s'est terminé normalement
-q
J'ai modifié le programme, vérifié la nouvelle instruction et les suivantes, et surtout enregistré avant le lancement. La modification à l'offset 80h correspond à la queue de la ligne de commande inscrite dans la DTA. La réécriture des FCB #1 et #2 n'est pas nécessaire avec BASIC.

Maintenant, quel est le pourquoi de cette faute lourde de Microsoft ? En fait c'est une correction loupée d'un bug qui se produisait une fois sur 256 et qui n'avait, alors, aucune incidence sur la pile. Et avant de l'oublier, je l'avais déjà rencontré.
Exécutons mon programme BASIC avec la révision 5.27 après avoir modifié la ligne 190 :
190 '
run
ABS(-32760 )=
Illegal function call in 210
Ok
? hex$(subaddr)
BFF
Ok
En effet, Microsoft avait découvert que seul le dernier octet de l'adresse de USR était vérifié. Or c'est un mot entier qui est déposé par DEF USR pour remplacer FFFFh. Il fallait remplacer
80 FA FF cmp dl,FF par l'instruction préférée des assembleurs lorsqu'ils rencontrent CMP DX,0FFFFh,
83 FA FF cmp dx,-01 voire dans la logique du programmeur par
81 FA FF FF cmp dx,FFFF et non pas
83 FA FF cmp dx,-01
FF aïe ! push... ? inc... ?

Pour conclure, voici les modifications à effectuer aux BASIC-86 :
5.28 pour MS-DOS  : remplacer 83h par 81h à l'offset 1D47h
5.27 pour MS-DOS  :     "     80h  "  83h      "     1D2Ah
5.22 pour CP/M-86 :     "      "   "   "       "     1C51h
5.21 pour 86-DOS  :     "      "   "   "       "     1BC4h si 11/11/81
                                                  ou 1BCCh si 11/08/81
Samedi 16/07/16

Deux problèmes principaux restaient avec le BASIC Microsoft 5.28 :
- les arguments incorrects de ligne de commande provoquant parfois l'erreur NO RESUME voire un résultat plus qu'imprévisible, surtout lors du second essai ;
- l'erreur critique (souvent disque ou imprimante) provoquant le plantage de l'interprète de commande avec un DOS moderne.


Le premier vient de la valeur non initialisée d'un mot en DS:04CBh testé en cas d'option non reconnue au démarrage. Le second au fait que les trois adresses de vecteur du PSP (INT22h à 24h) sont initialisés à leur valeur d'origine au retour du traitement d'erreur critique (INT24h). En effet, ce dernier demande à DOS via AL=02h de terminer le programme en se branchant au vecteur de l'INT22h détournée par le BASIC.

Solution du premier : on met à zéro cette valeur dès que le registre de données DS est initialisé.

Pour le second, c'est plus épineux. Le programme sauvegarde le mot en CS:[16h] avant de le modifier. Il s'agit du segment du processus parent (très probablement l'interprète de commande COMMAND.COM). Il est modifié par la valeur du segment de son propre PSP.
Ainsi, le programme est parent de lui-même tel que se comporterait l'interprète de commande lui-même ! Ce mot, apparu avec DOS 2, n'était pas documentée par Microsoft. Il permet à un programme fils de connaître son père.
Son but théorique : puisque le BASIC détourne l'INT24h afin que DOS pense le terminer en pointant le vecteur de l'INT22h redéfinie elle-aussi, on évite que DOS 2 ne change de processus actif et que la mémoire ne soit libérée. En effet, puis que DOS crée un PSP pour chaque programme chargé, son segment sert à définir ce processus.
Cette précaution est plus importante encore à partir de MS-DOS 3, qui apporta le support réseau mais qui n'existait pas encore à l'époque. Or il faut aussi mettre à jour pour ces versions les adresses de vecteur du PSP ; sans oublier de restaurer les originaux lorsqu'on voudra réellement quitter le BASIC ! Le plus simple est de modifier la table d'interruptions puis de recréer le PSP via la fonction 26h du DOS (INT21h).
J'ai pu essayer différentes modifications car le BASIC comporte du code superflu qu'on peut optimiser et remplacer.

J'ai néanmoins choisi un moyen plus simple. La routine de l'INT22h du BASIC suit l'instruction IRET de la routine appelée par l'INT24h du DOS. Le BASIC détecte si la version est DOS 2. J'estime donc, si elle est supérieure, de faire continuer la routine INT24h dans INT22h sans passer par le DOS. Il suffit de remplacer IRET par l'instruction NOP via du code auto-modifiant. D'ailleurs, notre BASIC n'utilise pas les arguments passés sur la pile lors de l'erreur critique mais restaure plus tard son propre pointeur. Donc, on se fiche de l'état du haut de la pile et il nous reste en plus un mot en DS:[016Eh] inutilisé mais décrivant l'erreur ! D'ailleurs, la version 5.27 du BASIC ne comportait pas de routine 22h mais nettoyait [une partie de] la pile de l'INT24h [pour récupérer DS et ES].

Code original (hors bug précisé précédemment) :
C>DEBUG MSBASIC.COM
-u7a24
1ACD:7A24 8CCA          MOV     DX,CS
1ACD:7A26 81C2CD07      ADD     DX,07CD
1ACD:7A2A B426          MOV     AH,26
1ACD:7A2C CD21          INT     21
1ACD:7A2E 8EDA          MOV     DS,DX
1ACD:7A30 50            PUSH    AX
1ACD:7A31 26            ES:
1ACD:7A32 A11600        MOV     AX,[0016]
1ACD:7A35 90            NOP
1ACD:7A36 A36801        MOV     [0168],AX
1ACD:7A39 8CC0          MOV     AX,ES
1ACD:7A3B 90            NOP
1ACD:7A3C A36A01        MOV     [016A],AX
1ACD:7A3F 26            ES:
1ACD:7A40 A31600        MOV     [0016],AX
1ACD:7A43 58            POP     AX
-u4603
1ACD:4603 B430          MOV     AH,30
1ACD:4605 CD21          INT     21
1ACD:4607 3C02          CMP     AL,02
1ACD:4609 721B          JB      4626
1ACD:460B B201          MOV     DL,01
1ACD:460D B001          MOV     AL,01
1ACD:460F B433          MOV     AH,33
1ACD:4611 CD21          INT     21
1ACD:4613 C7063C013444  MOV     WORD PTR [013C],4434
1ACD:4619 C606330102    MOV     BYTE PTR [0133],02
1ACD:461E C7064001C045  MOV     WORD PTR [0140],45C0
-q
(...)
Vous remarquez que le BASIC 5.28 original active, sous DOS 2, le contrôle étendu de Ctrl-C sans se soucier de son état précédent afin de le restaurer. Je préfère laisser le choix à l'utilisateur de DOS via la commande interne BREAK.

Dimanche 24/07/16

L'inconvénient du patch est qu'il laisse le DOS dans un état second jusqu'à l'appel d'une nouvelle fonction disque. En effet, le drapeau d'erreur critique reste activé. L'usage des piles par le DOS est modifié et certains programmes résidents risquent d'attendre pour traiter des actions en tâche de fond. Une solution serait de demander à la routine INT24h de transformer elle-même l'abandon en échec (AL=3) et de modifier l'adresse de retour complète, en BP+18h, pour qu'elle pointe la routine INT22h. Il ne faut pas oublier en effet que l'interruption 24h peut être déclenchée dans une routine en langage machine déposée par l'utilisateur dans le segment de données du BASIC.
Il faut donc étudier le code suivant :
Int23h

:45CE MOV     BYTE PTR [0132],03
:45D3 STI
:45D4 IRET

Sauvegarde de DS (data seg)

:45D5 DW      ?

Int24h

:45D7 CLI
:45D8 PUSH    AX
:45D9 PUSH    DS
:45DA CS:
:45DB MOV     AX,[45D5]
:45DE MOV     DS,AX
:45E0 MOV     [016E],DI
:45E4 MOV     [016C],SP
:45E8 POP     DS
:45E9 POP     AX
:45EA MOV     AL,02
:45EC STI
:45ED IRET

Int22h

:45EE CLI
:45EF CS:
:45F0 MOV     AX,[45D5]
:45F3 MOV     DS,AX
:45F5 MOV     ES,AX
:45F7 MOV     SS,AX
:45F9 MOV     SP,[016C]
:45FD STI
:45FE MOV     DL,39
:4600 JMP     08F2

Version du DOS

:4603 MOV     AH,30
:4605 INT     21
:4607 CMP     AL,02
:4609 JB      4626
:460B MOV     DL,01
:460D MOV     AL,01
:460F MOV     AH,33
:4611 INT     21
:4613 MOV     WORD PTR [013C],4434
:4619 MOV     BYTE PTR [0133],02
:461E MOV     WORD PTR [0140],45C0
:4624 JMP     4637
:4626 MOV     WORD PTR [013C],4453
:462C MOV     BYTE PTR [0133],06
:4631 MOV     WORD PTR [0140],45B8
:4637 MOV     AX,CS
:4639 MOV     [013E],AX
:463C MOV     [0142],AX
:463F JMP     452C
Ne me demandez pas pourquoi on écrit CLI alors [que DOS est déjà dans cet état lors d'une INT24h] ni pourquoi il y a un STI avant IRET [alors qu'on est en mode réel].
En revanche, je peux vous expliquer l'assignation différente des trois cases mémoire selon la version du DOS. La première pointe la routine datant de CP/M pour intercepter Ctrl-C et Ctrl-S (redondante avec le DOS) : sous DOS 2+, on vérifiera cinq fois moins souvent car la fonction six d'E/S appelle régulièrement l'interruption des tâches de fond lorsqu'aucun caractère n'est lu.* Le deuxième pointe la fonction utilisée comme sortie de caractère : sous DOS 2+, on préfère la numéro deux à la six car elle appelle régulièrement cette même interruption.** Enfin, la troisième pointe la routine obtenant le vecteur d'une interruption : sous DOS 2+, il existe une fonction pour cela qui consulte DOS avant de regarder dans le table des interruptions du processeur (par exemple pour une routine personnelle d'interception de Ctrl-C avec DOS comme intermédiaire).

* MS-BASIC utilise une particularité non documentée de la Fct6 en mode entrée (DL=FFh) : le fait qu'AL=0 après appel si rien n'est lu. Cf. http://spike.scu.edu.au/~barry/interrupts.html
** La fréquence d'appel de l'INT28h est passée d'un caractère sur quatre à un sur soixante-quatre depuis DOS 4. Cf. http://sites.google.com/site/pcdosretro/dosapinotes


Jeudi 4 août 2016

Récapitulatif de l'interaction de la routine d'INT24h de MS-BASIC avec différents DOS :
- MS-DOS 1 : système purement monotâche, se branche à la routine INT22h de MS-BASIC ;
- MS-DOS 2 : système possiblement multitâche, ne libère pas la mémoire de MS-BASIC qui est son propre parent et se branche à sa routine INT22h ;
- MS-DOS 3 ou plus : système réseau, retourne à l'invite de commande en plantant car la mémoire de MS-BASIC n'est pas libérée ;
- DOS Plus : système CP/M-86 compatible avec les fonctions documentées de DOS 2.11, retourne à l'invite de commande sans planter ;
- DR DOS : compatible DOS 3.31, remarque que MS-BASIC est son propre parent et transforme l'erreur critique en échec de fonction (possibilité de DOS 3).
Le détournement de l'INT22h via la table des interruptions sous ces trois derniers DOS ne sert plus que lors de la création d'un nouveau PSP (fct 26h de l'INT21h).
(...)
Je patche ensuite le DOS des versions 3 et plus de Microsoft (et IBM) pour éviter que les vecteurs sauvés dans le PSP soient inscrits dans la table des interruptions. L'inconvénient est qu'un processus abandonné après une erreur critique ne restaure pas forcément les vecteurs des interruptions 23h et 24h. Mystérieusement, COMMAND.COM ne requiert pas, dans ce cas précis, de restauration à partir du PSP. Tant pis pour un autre processus parent.
Pour patcher, comparons DOS 2:
        MOV     ES:[user_SP],SP         ; restore our stack
        MOV     ES:[user_SS],SS
        MOV     SP,ES
        MOV     SS,SP
ASSUME  SS:DOSGROUP
        MOV     SP,[CONTSTK]
        INC     BYTE PTR [INDOS]        ; Back in the DOS
        MOV     BYTE PTR [ERRORMODE],0  ; Back from INT 24
        STI
IGNRET:
        LES     BP,[EXITHOLD]
ASSUME  ES:NOTHING
        CMP     AL,2
        JZ      error_abort
        MOV     BYTE PTR [WPERR],-1              ;Forget about WP error
        return

SETIGN:
        XOR     AL,AL                   ;Flag ignore
        JMP     SHORT IGNRET

error_abort:
        PUSH    SS
        POP     DS
ASSUME  DS:DOSGROUP
        CMP     BYTE PTR [CONSWAP],0
        JZ      NOSWAP2
        invoke  SWAPBACK
NOSWAP2:
        MOV     BYTE PTR [exit_Type],Exit_hard_error
        MOV     DS,[CurrentPDB]
ASSUME  DS:NOTHING

;
; reset_environment checks the DS value against the CurrentPDB.  If they
; are different, then an old-style return is performed.  If they are
; the same, then we release jfns and restore to parent.  We still use
; the PDB at DS:0 as the source of the terminate addresses.
;
; output:   none.
;
        entry   reset_environment
        ASSUME  DS:NOTHING,ES:NOTHING
        PUSH    DS                      ; save PDB of process

        MOV     AL,int_Terminate
        invoke  $Get_interrupt_vector   ; and who to go to
        MOV     WORD PTR [EXITHOLD+2],ES ; save return address
        MOV     WORD PTR [EXITHOLD],BX

        MOV     BX,[CurrentPDB]         ; get current process
        MOV     DS,BX                   ;
        MOV     AX,DS:[PDB_Parent_PID]  ; get parent to return to
        POP     CX
;
; AX = parentPDB, BX = CurrentPDB, CX = ThisPDB
; Only free handles if AX <> BX and BX = CX and [exit_code].upper is not
; Exit_keep_process
;
        CMP     AX,BX
        JZ      reset_return            ; parentPDB = CurrentPDB
        CMP     BX,CX
        JNZ     reset_return            ; CurrentPDB <> ThisPDB
        PUSH    AX                      ; save parent
        CMP     BYTE PTR [exit_type],Exit_keep_process
        JZ      reset_to_parent         ; keeping this process

        invoke  arena_free_process

        ; reset environment at [CurrentPDB]; close those handles
        MOV     CX,FilPerProc

reset_free_jfn:
        MOV     BX,CX
        PUSH    CX
        DEC     BX                      ; get jfn
        invoke  $CLOSE                  ; close it, ignore return
        POP     CX
        LOOP    reset_free_jfn          ; and do 'em all

reset_to_parent:
        POP     [CurrentPDB]            ; set up process as parent

reset_return:                           ; come here for normal return
Et DOS 4 :
	MOV	ES:User_SP,SP		; restore our stack
	MOV	ES:User_SS,SS
	MOV	BP,ES
	MOV	SS,BP
ASSUME	SS:DOSDATA
passi24:				; AN000;

					; SS override for nect 3 instructions.
	MOV	SP,CONTSTK
	INC	BYTE PTR InDos		; Back in the DOS
	MOV	BYTE PTR ErrorMode,0	; Back from INT 24
	STI
;;	MOV	ACT_PAGE,-1		; LB. invalidate DOS active page 	;AN000;
;;	invoke	SAVE_MAP		; LB. save user's EMS map                ;AN000;
	fmt TypINT24,LevLog,<"INT 24: User reply = $x\n">,<AX>
FAILRET:

					
	LES	BP,EXITHOLD		; SS override
ASSUME	ES:NOTHING

	;
	; Triage the user's reply.
	;

	CMP	AL,1
	JB	CheckIgnore		; 0 => ignore
	JZ	CheckRetry		; 1 => retry
	CMP	AL,3			; 3 => fail
	JNZ	DoAbort 		; 2, invalid => abort

	;
	; The reply was fail.  See if we are allowed to fail.
	;

					; SS override for ALLOWED, EXTOPEN_ON, 
					; ALLOWED, FAILERR, WpErr, SFN, pJFN
	TESTB	Allowed,allowed_FAIL	; Can we?
	JZ	DoAbort 		; No, do abort
DoFail:
	MOV	AL,3			; just in case...
					; AN000;EO. I24 error disabled
	TESTB	EXTOPEN_ON,EXT_OPEN_I24_OFF 
	JNZ	cleanup 		; AN000;EO. no
	INC	FAILERR			; Tell everybody
CleanUp:
	MOV	WpErr,-1
	CMP	SFN,-1
	retz
	SAVE	<DS,SI,AX>
	MOV	AX,SFN
	LDS	SI,pJFN
	MOV	[SI],AL
	RESTORE <AX,SI,DS>
	return

	;
	; The reply was IGNORE.  See if we are allowed to ignore.
	;

CheckIgnore:
	TESTB	Allowed,allowed_IGNORE 	; Can we?
	JZ	DoFail			; No, do fail
	JMP	CleanUp

	;	
	; The reply was RETRY.	See if we are allowed to retry.
	;

CheckRetry:
	TESTB	Allowed,allowed_RETRY 	; Can we?
	JZ	DoFail			; No, do fail
	JMP	CleanUp

	;
	; The reply was ABORT.
	;

DoAbort:
	Context DS
	CMP	BYTE PTR ConSwap,0
	JZ	NOSWAP2
	invoke	SWAPBACK
NOSWAP2:

	;	
	; See if we are to truly abort.  If we are in the process of aborting, 
	; turn this abort into a fail.
	;

	cmp	fAborting,0
	JNZ	DoFail

	;
	; Set return code
	;

	MOV	BYTE PTR [exit_Type],Exit_hard_error
	XOR	AL,AL

	;
	; we are truly aborting the process.  Go restore information from 
	; the PDB as necessary.
	;

	Transfer    exit_inner
;**
;
; reset_environment checks the DS value against the CurrentPDB.  If they are
; different, then an old-style return is performed.  If they are the same,
; then we release jfns and restore to parent.  We still use the PDB at DS:0 as
; the source of the terminate addresses.
;
; Some subtlety:  We are about to issue a bunch of calls that *may* generate
; INT 24s.  We *cannot* allow the user to restart the abort process; we may
; end up aborting the wrong process or turn a terminate/stay/resident into a
; normal abort and leave interrupt handlers around.  What we do is to set a
; flag that will indicate that if any abort code is seen, we just continue the
; operation.  In essence, we dis-allow the abort response.
;
; output:   none.
;
	entry	reset_environment
	ASSUME	DS:NOTHING,ES:NOTHING

;***	invoke	Reset_Version		; AN007;MS. reset version number
	PUSH	DS			; save PDB of process

	;
	; There are no critical sections in force.  Although we may enter 
	; here with critical sections locked down, they are no longer 
	; relevant. We may safely free all allocated resources.
	;

	MOV	AH,82h
	INT	int_IBM

					; SS override
	MOV	fAborting,-1		; signal abort in progress

					; DOS 4.00 doesn't need it
	CallInstall NetResetEnvironment, multNet, 34  
					; Allow REDIR to clear some stuff
					; On process exit.
	MOV	AL,int_Terminate
	invoke	$Get_interrupt_vector	; and who to go to

	POP	CX			; get ThisPDB
	SAVE	<ES,BX> 		; save return address

	MOV	BX,[CurrentPDB] 	; get currentPDB
	MOV	DS,BX
	MOV	AX,DS:[PDB_Parent_PID]	; get parentPDB

	;
	; AX = parentPDB, BX = CurrentPDB, CX = ThisPDB
	; Only free handles if AX <> BX and BX = CX and [exit_code].upper 
	; is not Exit_keep_process
	;
	
	CMP	AX,BX
	JZ	reset_return		; parentPDB = CurrentPDB
	CMP	BX,CX
	JNZ	reset_return		; CurrentPDB <> ThisPDB
	PUSH	AX			; save parent

					; SS override
	CMP	BYTE PTR [exit_type],Exit_keep_process
	JZ	reset_to_parent 	; keeping this process

	;
	; We are truly removing a process.  Free all allocation blocks 
	; belonging to this PDB
	;

	invoke	arena_free_process

	;
	; Kill off remainder of this process.  Close file handles and signal 
	; to relevant network folks that this process is dead.  Remember that 
	; CurrentPDB is STILL the current process!
	;

	invoke	DOS_ABORT

reset_to_parent:
					; SS override
	POP	[CurrentPDB]		; set up process as parent

reset_return:				; come here for normal return
On remarque aisément ce que remplacent les instructions XOR AL,AL et JMP (transfer exit_inner)...
Par exemple, avec MS-DOS 6.22 en mémoire basse :
a 11:9465
nop
mov ds,[330]

q
Avec PC-DOS 7 en mémoire basse, cela aurait été a 2:902e au début ; et avec le DOS de Win98SE en mémoire haute, a ff3a:99f0 à la place.

Dimanche 14 août 2016

Vous avez compris que je trouve stupide qu'après MS-DOS 2, on ait enlevé autant d'utilité à l'usage de l'interruption 22h. Il est vrai qu'elle était mal documentée :
- son rôle premier de retour au processus parent était à peine officialisé ;
- la documentation officielle citait à sa place l'appel à l'interruption 23h (Ctrl-C) lors de la demande d'abandon dans une routine d'erreur critique (INT 24h) ; à l'exception du guide du programmeur de PC-DOS 7 qui ne change rien à la donne.
On comprend pourquoi on ajoutait des appels bidons au DISK RESET (fct 0Dh) ou GET DOS VERSION (fct 30h) comme alternative sous GW-BASIC pour quitter le mode critique du DOS.

Pour finir le patch de MSBASIC, insistons sur le fait que les messages précis d'erreur disque apportés par MS-DOS ne sont jamais appelés. La révision 5.27 copie bien DI dans le registre général AX après une erreur critique, mais ne traite pas pour autant ce code. Pire, la version 5.28 est un foutoir vu son caractère triple : application destinée à MS-DOS 1, reconnaissant DOS 2 et restant compatible avec son ancêtre sous CP/M. Malgré tout, le code d'erreur critique est bien stocké dans le mot DS:[16eh] pour un usage ultérieur.
Ainsi, la révision 5.27 affiche Disk I/O error car seul un lecteur de disque pouvait provoquer une erreur critique à l'époque. DOS 2 étendit cela aux imprimantes et périphériques divers, d'où le message Device I/O error. Cependant, tous les DOS reconnaissent un lecteur de disquette vide ou une disquette protégée contre l'écriture ; quoique les GW/BASIC pour DOS 3 préfèrent afficher Permission Denied comme pour une violation de partage. Nous avons donc Disk write protected et Disk not Ready (avec une majuscule car Ready était en concurrence avec le prompt Ok !).
Reste l'identification d'une disquette défectueuse.
Voyons les codes d'erreur critique traditionnels du DOS :
00h   (0)  write-protection violation attempted
01h   (1)  unknown unit for driver
02h   (2)  drive not ready
03h   (3)  unknown command given to driver
04h   (4)  data error (bad CRC)
05h   (5)  bad device driver request structure length
06h   (6)  seek error
07h   (7)  unknown media type (non-DOS disk)
08h   (8)  sector not found
09h   (9)  printer out of paper
0Ah  (10)  write fault
0Bh  (11)  read fault
0Ch  (12)  general failure
Les codes impaires ont été ajoutés sous DOS 2. Donc, pour une version de GW-BASIC pour MS-DOS 1, tout code supérieur ou égal à quatre entraîne le message Disk media error. De plus, GW-BASIC étant destinée à une machine plus ou moins compatible IBM, un appel au BIOS permet de savoir si l'imprimante est indisponible (autrement que pour un Out of paper). Voilà pourquoi le message Disk I/O error céda sa place pour Device I/O error.
Sous DOS 2, avec l'arrivée des disques durs et périphériques installables via un gestionnaire, Disk media error fut réservé pour le code 7, ce qui est un peu ambigu : une disquette non-DOS n'est pas un médium forcément défectueux. Le reste redevient Device I/O error.
Par expérience, je peux dire que les codes 6 et 8 sont souvent signe d'un disque défectueux, des informations écrites lors du formatage bas niveau ayant disparu (index de secteur, par exemple). Néanmoins, le premier peut être provoqué par une tête de lecteur désalignée voire une mauvaise alimentation, et le second par une tête sale voire un numéro de secteur erroné. En revanche, l'erreur quatre, valable aussi pour les CD gravés (quel bond dans le temps !), signifie une lecture possible mais altérée des données.

Mardi 23 août 2016

Passons à la mise en œuvre.
La sauvegarde du registre AX lors d'une INT24h est inutile. De plus, on n'est pas obligé de passer par un registre général pour copier une variable mémoire dans DS. J'ai viré la modification de BREAK. J'ai ainsi pu déplacer le saut inconditionnel à la place du saut inconditionnel : les variables sont fixées d'abord pour DOS 2 et ensuite pour DOS 1 le cas échéant.
J'ai économisé cinq octets en utilisant l'instruction LES pour choper deux mots continus et en remplaçant un saut normal en saut court, afin d'y écrire une table d'erreurs. Cette liste de numéros apporte de la souplesse ; je me suis inspiré de GW-BASIC 2 pour cela. L'instruction ROR suivie d'une comparaison m'a permis de ne garder que les cinq premiers codes pairs, tout en transformant AL en index pour XLAT (adresse de base en BX).
Nous obtenons enfin une gestion d'erreur digne du DOS.
(...)
Reste une caractéristique appréciée depuis les premières révisions de GW-BASIC : le fait de sauter l'affichage du copyright et du numéro de version lorsqu'on lance un programme BASIC depuis la ligne de commande. Une fois encore, on a affaire à un code local traduit du langage 8080. Il suit à l'offset 7B79h la sauvegarde de DS dans le mot en CS:45D5h vu plus haut. On économise la manipulation inutile de AX [et on remplace les branchements à cette section en sauts courts]. Enfin, on insère le code servant à vérifier si la future DTA débute par un guillemet que BASIC ajoute devant le nom du programme.
:7C17 803E800022    CMP     BYTE PTR [0080],22
:7C1C 7418          JZ      7C36
L'instruction suivante est une soustraction sans retenue qui n'est pas importunée par l'état du registre FLAG.
(...)
Ce qu'apporte le patch :
- plus de plantage lorsqu'on commet une erreur de ligne de commande ;
- la correction du bug de la fonction USR ;
- l'attribution correcte des numéros d'erreur disque ;
- l'absence de copyright lors du lancement d'un programme BASIC à partir de la ligne de commande.
Ce que n'apporte pas le patch : la compatibilité de la gestion d'erreur critique avec DOS 3+ et DOS Plus. MS-DOS nécessite une petite modification de son noyau pour ne pas planter ; DR DOS transforme l'erreur critique en erreur normale alors que DOS Plus met fin au BASIC.

Vendredi 2 septembre 2016

Il est très facile de rendre MS BASIC compatible DOS 3+. Il suffit de mettre à jour le PSP en le recréant. Cependant, DOS Plus (compatible 2.11) ainsi que DR DOS 6 d'avant le correctif PAT312 planteront. Comme MS-DOS 2 et son prédécesseur, DOS Plus modifie le saut long appelé par CALL 5 du nouveau PSP pour que son offset indique une mémoire libérée de ce dernier (FFF0h au lieu de FEF0h maximum). Or le code pointé tient sur plus d'un paragraphe et dépasse donc la limite du nouveau segment.
Hier matin, j'ai assemblé un passage entier via MASM afin d'inclure dans un code serré les particularités suivantes :
- remettre BREAK dans son état d'origine une fois BASIC terminé ;
- rendre compatible BASIC avec MS-DOS 3 et suivants grâce à la mise à jour du PSP ;
- rendre compatible BASIC avec DR DOS et suivants (correctif PAT312 nécessaire à cause de la mise à jour du PSP ci-dessus) ;
- prendre en compte les nouveaux numéros d'erreur disque ;
- la possibilité d'appeler facilement la routine d'obtention d'un vecteur d'interruption depuis un sous-programme en langage machine (call far [140h]);
- la possibilité plus ardue de détourner le contrôle par le BASIC de Ctrl-C et Ctrl-S (nouvelle routine dans le double-mot [13Ch] qui reçoit IP sur la pile et récupère CS dans le mot [16Ah] !).

Pour les curieux explorant via DEBUG, sachez que MS BASIC contient du code [qui risque de] vous contrer. Il s'agit de l'appel CALL 3041 qui est suivi d'un octet de donnée qu'il compare à la suite d'une instruction BASIC. Cet octet, en plus d'embrouiller le désassemblage, risque d'être écrasé par l'instruction de pas-à-pas INT3.

Samedi 11 septembre 2016

(...)
Les 278 octets inscrits à l'offset 452Ch [seront] obtenus via un fichier à assembler. J'ai même fait en sorte que le pointeur de pile soit après l'erreur critique exactement comme avant.
(...)

Samedi 17 septembre 2016

J'espère que vous aurez compris l'utilité de l'assembleur dans l'utilisation des logiciels anciens.
On peut se poser à présent la question de l'utilité d'un mini-shell pour MBASIC.CMD sous CP/M-86. Il peut être intéressant de modifier l'en-tête du programme pour le transformer au format COM de MS-DOS.
À bientôt.

Jeudi 29 septembre 2016

Merci de la remontée.
(...)

Remarques lors du désassemblage.

L'appel de l'offset 3041h est toujours suivi d'un octet de donnée faisant partie
de la syntaxe d'une instruction BASIC.
La suite d'instructions MOV CX,xxB2h contient en fait une liste d'instructions
MOV DL,xx qui correspondent chacune à un numéro d'erreur BASIC.


Stockage des instructions

Elles sont classées par ordre alphabétique. La dernière lettre de chacune voit
son bit le plus fort mis à un. La première est remplacée par une valeur qui est
zéro si on débute une nouvelle lettre de l'alphabet. Chaque instruction termine
par un octet qui est ni une lettre ni un dollar.
Un peu plus loin, se trouvent les messages d'erreur en toutes lettres. Ils sont
triés par numéro d'erreur et chaque numéro inutilisé est indiqué par un point
d'interrogation.
Dimanche 9 octobre 2016

L'affichage des nombres se trouvent à l'adresse 6158h. On remarque le point décimal (norme américaine) à l'offset 615Eh, et surtout, la déconcertante virgule à l'offset 616Eh séparant les milliers lors d'une sortie formatée (PRINT USING "#######,,.##";nombre par exemple). Rien ne vous empêche de la remplacer par une apostrophe.

Dimanche 16 octobre 2016
Si je devais proposer en ligne le BASIC corrigé, je pense que le mieux serait d'éviter de supprimer du code issu de la conversion 8 bits en 16 bits. J'ai fait un premier essai avec l'aide du désassembleur Soucer v.8 avec qui je n'arrive plus à afficher le code après appel CALL 3041 (puisque non-retour), bien que j'ai pensé à remplacer l'octet de données suivant par un NOP et activé les mêmes options que précédemment. J'ai donc procédé avec l'ancien désassemblage sous forme de fichier LST et non ASM ; ce qui m'oblige à imprimer et faire les corrections manuellement. Quelle galère !
Nous savons qu'un 8086 ne peut faire que des sauts courts lorsqu'ils conditionnels. Ce qui nous obligent, lorsqu'il ne peut être court, à le remplacer par un saut inconditionnel précédé d'un saut à la condition inversée qui pointe après. Or à l'offset 7AFCh cette précaution est inutile.
De même que le saut inconditionnel en 7A19h pourrait être court (JMP SHORT). Nous gagnons donc les quatre octets qui nous avez obligé à supprimer des instructions inutiles.
(...)
J'exposerai une méthode moins risquée.

P.S. : avec SYMDEB à la place de DEBUG, il faut remplacer XCHG DX,AX par XCHG AX,DX pour obtenir la troisième forme optimisée sur un octet, c'est-à-dire, DB 92h.
BASIC a besoin de cinq octets minimum de libre pour éviter de détruire la pile avant un Out memory.

Samedi 5 novembre 2016
BASIC, une fois lancé, peut planter par manque de pile (lors d'une seconde erreur de syntaxe par exemple). D'ailleurs, le dernier paramètre de l'instruction CLEAR ne peut être inférieur à 79 (80 octets en fait).
Au démarrage, il effectue un premier calcul de la mémoire libre, considère son huitième afin de fixer la taille de la pile qui n'excédera pas 512 octets (200h). Or il est dangereux de descendre trop en-dessous de 80 (50h). Je me suis aidé du désassembleur SOURCER de V Communications pour pouvoir créer le fichier DEBUT.ASM dans lequel j'ai souligné les changements sous forme de paragraphes.

Cf. futur message

J'ai mis à jour le fichier DEBUT.ASM du fait d'un bug mineur. La combinaison de touche Ctrl-A (^A) rappelle le contenu du tampon clavier (saisie précédente) débutant à l'adresse DS:0373h. Or au lancement, il vaudrait mieux qu'il soit initialisé par un caractère nul.

18 novembre 2016

On comprend pourquoi DOS alloue le maximum de mémoire aux programmes au format COM : la plupart ne se soucient pas où ils écrivent. Le BASIC pour 8086 a la bonne idée de créer un nouveau PSP, mais il tarde à calculer la mémoire libre (mot à l'offset 6) et initialise plusieurs variables entretemps.

Samedi 25 mars 2017

J'ai légèrement modifié le patch. Pour récapituler, la gestion des fichiers restera incompatible avec la FAT32 en mode MS-DOS sous Windows 9x, ainsi qu'avec Windows Me. Le handler d'erreur critique serait à revoir pour DOS Plus.

Samedi 1er avril 2017

Si DOS Plus, lorsqu'il crée un nouveau PSP, reportait une mémoire libre (offset 6) non grandie de 256 octets par rapport au PSP original, l'appel 5 ne pointerait jamais un code devant se contenter du dernier paragraphe d'un segment. Il se comporterait un peu plus comme DOS 3 qui déduit la taille du PSP lorsqu'on use de la fonction 26h, et qui restitue lui aussi tous les vecteurs DOS lors d'un abandon après une erreur critique.
Corrigé de ce bug, on pourrait lancer MS-BASIC patché sur ce système d'exploitation.

Mardi 4 avril 2017

Corriger DOS Plus est plus facile encore que DR DOS. On recherche la valeur FFF0h dans le premier segment, on observe le code (que je cible précisément ici) et on rectifie avec la valeur de mémoire maximale de FEF0h comme sous DOS 3 et plus :
A>debug dosplus.sys
-s0 ffff f0 ff
1AA2:9F62
1AA2:D97E
-u9f49 9f69
1AA2:9F49 1E            PUSH    DS
1AA2:9F4A 2E            CS:
1AA2:9F4B 8E1E0600      MOV     DS,[0006]
1AA2:9F4F 8E1E4E00      MOV     DS,[004E]
1AA2:9F53 88164700      MOV     [0047],DL
1AA2:9F57 1F            POP     DS
1AA2:9F58 C3            RET
1AA2:9F59 A18101        MOV     AX,[0181]
1AA2:9F5C 3D0010        CMP     AX,1000
1AA2:9F5F 7204          JB      9F65
1AA2:9F61 B8F0FF        MOV     AX,FFF0
1AA2:9F64 C3            RET
1AA2:9F65 B104          MOV     CL,04
1AA2:9F67 D3E0          SHL     AX,CL
1AA2:9F69 C3            RET
-e9f63
1AA2:9F63  FF.FE
-u9f61 9f63
1AA2:9F61 B8F0FE        MOV     AX,FEF0
-w
Écriture de 12880 octets
-q
J'ai opéré sur le noyau configuré d'origine. [On pouvait aussi avoir FFE0h comme valeur.]

16 avril 2017 (Pâques)

J'ai configuré DOS Plus 1.2 en français pour constater que cela ne décalait pas le code ci-dessus. Sa gestion d'erreur critique est comme sous DOS 3+ bien qu'il soit un CP/M-86 compatible DOS 2 (au niveau des fonctions documentées). On peut dire que MS-BASIC fonctionne maintenant avec toute version de MS-DOS (sauf avec la FAT32 en mode MS-DOS sous Windows 9x ou avec les FCB de Windows Me), de PC-DOS, de DR DOS (plus le PAT312 pour la version 6) ainsi qu'avec DOS Plus (si corrigé pour que la création de PSP se fasse comme sous DOS3+) et Windows XP 32 bits. Il faut juste veiller que le DOS soit chargé en HMA si la ligne A20 est activée afin de garder la compatibilité CP/M (CALL 5).
Fin du premier chapitre.

31 mai 2017

Un exemple d'annulation de la scrutation, longue en traitement, des commandes Ctrl-C et Ctrl-S par le BASIC :
DEBUG MSBASIC.COM
e4430 C3
g
Jeudi 1er juin 2017

Revenant au shell du tout début, il va de soi qu'il n'est pas fait pour être compatible avec DOS Plus. En effet, cet OS sait lancer les fichiers CMD. De toute façon, il n'inscrit pas de saut à l'adresse 0:000Ch, ce qu'on font les véritables DOS en gâchant les interruptions 30h et 31h.

Mercredi 21 juin 2017

Il est intéressant de constater que l'initialisation de GW-BASIC est très fortement inspirée de ses ancêtres. Parfois, il s'en sort mieux pour éviter les problèmes du pile trop petite. Le code de la version CP/M aurait pu facilement imposer une pile minimale de 256 octets au lancement du BASIC, à l'instar de la pile maximale qu'il fixait à 512 octets. Il est tellement simple de vérifier s'il y a 256 octets disponibles via la condition du poids fort.

Dernière modification par gm86 le 17 nov. 2019 18:10, modifié 5 fois.

_________________

C:\ONGRTLNS.W95


Haut
Profil Citer
gm86
Sujet du message : Re: Apprentissage du BASIC Microsoft sous DOS
Publié : 20 mai 2020 19:16
Membre inscrit
Avatar de l’utilisateur
Hors-ligne
 
Messages : 630
Inscription : 01 sept. 2008 19:07
 
Le message précédent, se rapportant essentiellement à la version MS-DOS de BASIC, approche les 90.000 caractères. C'est un fourre-tout de ce que j'ai découvert plus ou moins volontairement dans les méandres du DOS et du CP/M au fil des ans.
Place à la version pour CP/M-86, très proche de la version CP/M !

Mardi 3 juillet 2017

Aujourd'hui, j'ai rapidement étudié les codes d'erreur des fonctions traditionnelles du CP/M-86. Comme sous DOS, AH vaut généralement FFh si une telle fonction échoue. Cependant, l'écriture d'un enregistrement RANDOM (FCT22h) en comporte d'autres que le BASIC étudie : les erreurs 1 (disque plein), 3 (problème d'allocation supérieure à 16 Ko) et 5 (répertoire saturé). La dernière n'apparaît pas à ce moment-là sous DOS ; par contre, la première correspond à l'erreur 2 sous DOS. Quant à la restante, spécifique à CP/M, elle provoque le branchement à l'offset 0798h (Disk I/O Error).

Mercredi 4 juillet 2017

Revenant à la fonction 22h, elle comporte deux types seulement d'erreur sous DOS :
- AH=1, disque plein ;
- AH=2, débordement de segment (DTA insuffisante en fin de segment).
Ils seront incrémentés si rencontrés pour correspondre aux erreurs Disk full et Disk I/O Error (Cannot close current extent).
On imitera le comportement de CP/M-86 lorsqu'il charge un programme avec un en-tête de modèle small (un segment de code et un segment de données), sans plus. Ce patch, remplaçant l'en-tête du fichier CMD pour en faire un fichier COM, doit être générique.
On supposera qu'aucun appel à une fonction spécifique au CP/M-86 existe. CALL 5 fait la vérification mais il est incompatible avec une HMA active lorsque le DOS est en mémoire basse.

Mercredi 5 juillet 2017

Comment CP/M-86 charge-t-il MBASIC.CMD ?
Il lit l'en-tête de 128 octets :
DB 01	type 1 : segment de code
DW 070F	longueur dans le fichier (en paragraphes)
DW 0000	base (0 comme souvent)
DW 070F taille minimum (pas plus petit que la longueur)
DW 070F	taille maximum (pas besoin d'être plus grand)
DB 02	type 2 :segment de données
DW 0001	longueur dans le fichier (rien d'important à garder)
DW 0000	base (0 comme souvent)
DW 0100	taille minimum (100h*16=4Ko)
DW 0FFF	taille maximum (64 Ko moins 16 octets)
... octets nuls.
Il s'agit d'un modèle small et non d'un modèle 8080 (qui aurait le code et les données dans le même segment à l'instar d'un fichier COM pour CP/M ou DOS). Vu qu'il est converti depuis un programme 8080, il est normal qu'il n'y ait que deux valeurs à retenir : la longueur du code et le besoin minimum des données.
CP/M-86 lui crée une page zéro de 256 octets (appelée PSP sous DOS). Elle débute après la longueur de code indiquée, alors que le modèle 8080 la place au début du segment unique. Il y fait pointer DS ainsi que ES, vu qu'il n'y a aucun segment extra (type 4). Ensuite, il renseigne certains champs :
ORG 	00
DW	Longueur du groupe de code en octets sur 20 bits
DB	(le quartet de poids fort est toujours nul)
ORG	03
DW	Adresse du segment de code
ORG	05
DB 	Drapeau mis à 1 s'il s'agissait d'un modèle 8080
ORG	06
DW	Longueur du groupe de données en octets sur 20 bits
DB	(le quartet de poids fort est toujours nul)
ORG	09
DW	Adresse du segment de données
ORG	0A
DB	Réservé
... emplacements libres pour extra, pile ou auxilliaires (types 5 à 8)...
ORG	30
... réservés...
ORG	5C
... FCB1 et FCB2 si un ou deux fichiers indiqués en ligne de commande...
ORG	80
DB	Nombre de caractères suivants que contient la DTA
... où sont écrits les paramètres de la ligne de commande plus RETURN.
Du fait qu'il n'y a pas de segment de pile (type 3), c'est celle de l'interpréteur de commandes qui sera utilisée (96 octets).
Nous remarquons que le mot à l'offset 6, ainsi que les 2 FCB et la DTA sont communs à CP/M et DOS.
À présent, CP/M-86 peut donner la main au segment de code.

Jeudi 6 juillet 2017

Voici le fichier source PATCH.ASM de l'en-tête DOS destiné à MBASIC.CMD (BASIC Microsoft pour CP/M-86) :
; En-tête DOS d'un modèle small CP/M-86 issu d'une conversion de programme CP/M.
; Assemblage :
;	MASM 	PATCH;
;	LINK	PATCH;
;	EXE2BIN	PATCH PATCH.BIN
;
; Incorporation dans MBASIC.CMD pour obtenir le programme DOS MBASIC.COM :
;	DEBUG MBASIC.CMD
;	N PATCH.BIN
;	L100
;	N MBASIC.COM
;	R CX
;	7180
;	W
;	Q
;
lngCS	equ	70Fh		; (16 bits) Longueur du code en paragraphes
minDS	equ	100h		; (16 bits) Données requises en paragraphes

code	segment
	assume	cs:code
	org	5
CPM	label	near		; Appel lointain aux fonctions traditionnelles
	org	8
Wrap	equ	this word	; Segment de wrapping avec le mot[6] de mémoire
	org	5Ch
DrvFCB1	equ	this byte	; Numéro de lecteur du premier FCB par défaut
	org	80h
LngDTA	equ	this byte	; Taille de la queue de la ligne de commande
	org	100h		; Fin du PSP
Alloc	proc	near
VectE0	equ	this dword	; Vecteur sauvé sous la future pile
; Crée un nouveau PSP si mémoire suffisante sinon quitte
	mov	bx,cs
	add	bx,18h		; Future valeur de CS dans BX
	lea	dx,[bx+lngCS]	; Future valeur de DS dans DX
	mov	ax,ds:2
	sub	ax,dx
	cmp	ax,minDS	; Assez de place pour les données minimales ?
	 jnb	Init
	ret			; Quitte
Alloc	endp

Init	proc	far
	mov	ah,26h
	int	21h
; Obtient le vecteur de l'interruption CP/M-86
	pop	es		; ES=0
	mov	di,0E0h*4
	les	di,es:[di]
; Définit la pile directement à la suite du PSP actuel et y sauve le vecteur
	mov	sp,offset VectE0+4
	push	es		; Segment du vecteur
	push	di		; Offset du vecteur
; Initialise ES sur le nouveau PSP
	mov	es,dx
; Détourne les appels CP/M-86
	mov	ax,25E0h
	mov	dx,offset DOS
	int	21h
; Initialise DS sur le nouveau PSP
	push	es
	pop	ds
; Y définit la DTA à l'emplacement de la DMA par défaut de CP/M-86
	mov	ah,1Ah
	mov	dx,offset LngDTA
	int	21h
; Rectifie ce PSP en page zéro de CP/M-86 (garde le mot[6], les FCB et la DMA)
	cld
	xor	ax,ax		; AX=0
	mov	cx,offset DrvFCB1-offset Wrap
	mov	di,offset Wrap	; (20 bits) Données allouées dans octets 6 à 8
	rep	stosb
	mov	ds:2,al		; (20 bits) Longueur du code dans octets 0 à 2
	xchg	ax,bx		; Future valeur de CS dans AX
	mov	[bx],lngCS*10h	; BX=0
	mov	ds:3,ax		; Segment de code
	mov	ds:9,ds		; Segment de données
; Dépose les adresses de terminaison et de commencement du programme CP/M-86
	push	cs		; Segment du programme DOS au format COM		
	push	bx		; Offset nul pointant INT 20h
	push	ax
	push	bx
; Effectue un branchement lointain au début du programme CP/M-86
	ret
Init	endp

DOS	proc	far
; +--------------------------------------+
; | Procédure d'appel d'une fonction DOS |
; +--------------------------------------+
	or	cl,cl		; Termine l'application ?
	 jnz	BDOS
	mov	ax,25E0h	; Restitue l'interruption CP/M-86
	lds	dx,VectE0
	int	21h
BDOS:
	call	CPM
	cmp	ah,15h		; Ecriture séquentielle ?
	 je	CodeErr
	cmp	ah,22h		; Ecriture directe ?
	 jne	AutreFct
CodeErr:
	shl	al,1		; Adapte le code DOS en code CP/M ou inconnu
AutreFct:
	cmp	ax,1403h	; Lecture séquentielle incomplète ?
	 je	SansErr
	cmp	ax,2103h	; Lecture aléatoire incomplète ?
	 jne	RetDOS
SansErr:
	xor	al,al		; Annule ce type d'erreur
RetDOS:
	iret
DOS	endp

code	ends

	end	Alloc

Int E0h est l'appel système de CP/M-86. Il remplace le CALL 5 de CP/M que DOS reconnaît.

Lundi 10 juillet 2017

Mise à jour pour prendre en compte l'écriture séquentielle, même si c'est encore moins indispensable vu la faible différence en MS-DOS et CP/M-86 sur ce point (erreur 1 : disc full au lieu de directory full).
Le code occupe exactement les 80 octets de l'ancien en-tête ! La préservation de l'interruption E0h est-elle utile (je pense à DOS Plus) ?

Jeudi 13 juillet 2017

À la lecture de la section 2.7 du CP/M-86 System Guide, je constate que l'adresse complète de terminaison du programme est laissée sur la pile du CCP (à l'instar de CP/M qui laisse un mot nul sur la pile). S'il ne redéfinie pas sa pile, le programme peut donc finir via l'instruction RETF.
Cela mérite une nouvelle mise à jour.

Vendredi 14 juillet 2017 (fête nationale)

Le fichier source est complété d'autres commentaires.
En testant le BASIC pour CP/M-86 d'origine, j'ai remarqué que DOS Plus le stoppait si un mauvais lecteur était indiqué devant le nom du programme BASIC. S'il s'agissait du BASIC pour MS-DOS, DOS Plus l'aurait laissé traiter ce problème ; par contre, il aurait dû le lui signaler via le registre AX (AL=FFh si la lettre du FCB1 est invalide).

Mardi 25 juillet 2017

Le calcul de la mémoire disponible pour les données est plus sûr à présent, car sans risque de débordement.

Jeudi 27 juillet 2017

Un oubli honteux ou comment inventer un code d'erreur pour une opération réussie.

Tout est dans le titre : DOS risque de tromper un programme CP/M-86 en lui renvoyant un code d'erreur non nul. DOS connaît la taille exacte de chaque fichier grâce à la FAT. Par compatibilité CP/M, les fonctions FCB manipulent des blocs de 128 octets ; c'est pourquoi, un caractère EOF (^Z) marque souvent la fin réelle d'un fichier. D'ailleurs, DOS renvoie le code 3 pour prévenir que le dernier bloc lu d'un fichier a dû être comblé par des zéros. À l'inverse, DOS Plus renverrait un code nul s'il s'agissait d'un appel CP/M-86.

Dimanche 7 janvier 2018

J'avais gardé pendant si longtemps un lien sans le consulter.
Il s'agit d'un site formidable, heureusement archivé, sur le désassemblage du Microsoft BASIC de l'Altair :
http://web.archive.org/web/200112121132 ... _dis_1.htm
Je pointe sur cette page précisément car [j'avais pensé à mal]. Ce qui m'avait semblé être une pratique anti-DEBUG dans le BASIC n'était en fait que la [transcription en CALL (3 octets)] d'une merveilleuse [interruption] de vérification de syntaxe du BASIC de ROM. Pour la décrire brièvement, il s'agissait d'une [routine] appelée via une instruction sur un octet (RST 1) comparant l'octet lu avec l'octet suivant l'appel, incrémentant et échangeant avec la pile via l'instruction spécifique XTHL.

Au passage, dans le MS-BASIC 5.28, j'aurais mieux fait de mettre à zéro le mot [372h] au lieu du seul octet [373h]. Car en plus de vider le tampon ligne, j'évite son débordement (tampon à la suite d'une improbable ribambelle d'espaces).
De plus, le désassemblage du BASIC de l'Altair m'a confirmé le fait qu'il y avait bien une vérification de pile au démarrage pour le BASIC Microsoft (CheckEnoughMem) :
http://web.archive.org/web/200112121104 ... _dis_2.htm
Il ne résout pas le problème en cas d'absence quasi-totale d'octets de libre mais il confirme un de mes essais de correction du MS-BASIC 5.28 via DEBUG :
-a7C1B
CALL 2F0B
Dimanche 14 janvier 2018

Parenthèse sur le chapitre précédent (que j'ai épuré).
Si la pile est non nulle, elle peut se permettre de déborder de sept fois sa taille (vu qu'elle représente au moins le huitième de la mémoire libre). La procédure vérifiant sa suffisance (à l'offset 2F0Bh) peut se contenter de huit octets. De même, il serait bien [pour elle de faire débuter la mémoire libre par un mot nul].

Jeudi 18 janvier 2018

Voici une page de liens actuels sur le code source de l'Altair :
http://groups.google.com/forum/m/#!topi ... rUH5Doy1es
Le fichier MAC de la version 8K en plus du désassemblage, cité plus haut, de la version 4K !

Vendredi 2 février 2018

Hier, je me suis rendu compte que FILES de MS-BASIC 5.28 considérait la lettre Z de lecteur comme une erreur.
Le bug se trouve dans le code ci-dessus :
:08C0   B240        MOV     DL,40
:08C2 B9B23F        MOV     CX,3FB2
:08C5 B9B232        MOV     CX,32B2
:08C8 B9B243        MOV     CX,43B2
:08CB B9B23A        MOV     CX,3AB2
:08CE EB22          JMP     08F2
...
:7068 E95598        JMP     08C0
...
:7102 E963FF        JMP     7068
...
:7107 E80C00        CALL    7116
:710A 2C40          SUB     AL,'A'-1
:710C 72F4          JB      7102
:710E 3C1A          CMP     AL,'Z'
:7110 73F0          JNB     7102
:7112 5E            POP     SI
:7113 5F            POP     DI
:7114 5A            POP     DX
:7115 C3            RET
:7116 3C61          CMP     AL,'a'
:7118 7206          JB      7120
:711A 3C7A          CMP     AL,'z'
:711C 7702          JA      7120
:711E 24DF          AND     AL,DF
:7120 C3            RET
Puisque 'A'-1='@', @: correspond au lecteur courant (numéro 0) pour FILES ! Ce qui n'est pas grave en soi.
Par contre, il faut remettre 'Z'+1 à la place de 'Z'.

De plus, une procédure convertissant les minuscules en majuscules avait été ajoutée exprès (JA est une instruction sans équivalent direct 8080). Or elle est redondante à celle-ci :
:6F2D 3C61          CMP     AL,'a'
:6F2F 7206          JB      6F37
:6F31 3C7B          CMP     AL,'z'+1
:6F33 7302          JNB     6F37
:6F35 24DF          AND     AL,DF
:6F37 C3            RET
[Et aussi à une autre précédée à l'offset 1B4Fh de MOV [BX],AL mais terminée par AND AL,5Fh.]
Nous pouvons donc nous en passer en rectifiant aussi l'appel :
:7107 E80C00        CALL    6F2D
:710A 2C40          SUB     AL,'A'-1
:710C 72F4          JB      7102
:710E 3C1B          CMP     AL,'Z'+1
:7110 73F0          JNB     7102
:7112 5E            POP     SI
:7113 5F            POP     DI
:7114 5A            POP     DX
:7115 C3            RET
Et nous y plaçons une routine, directement traduisible du 8080, pour préparer les données d'entrée de la procédure à l'offset 2F0Bh (pile suffisante ?) :
:7116 mov byte [bx],0
:7119 inc bx
:711A mov byte [bx],0
:711D dec bx
:711E mov cl,0
:7120 ret
Avec [BX]=[160h]=offset du début du programme BASIC. Le mot nul annonce qu'aucun programme BASIC n'est encore chargé, tandis que [CL indique que la pile doit équivaloir à tant mots en plus que le minimum (CL non nul peut provoquer un débordement avec BX).]

À propos de FILES sous cette version de BASIC, le paramètre "." [se base sur le nom d'un] précédent appel [de fichier]. Néanmoins, le masque du fichier n'est pas initialisé à zéro comme le numéro de lecteur au démarrage. Ce qui est sans conséquence grave.

Mercredi 7 février 2018 (patch mis à jour le 8 mars 2018 et le 30 mai 2020)

Procédure pour patcher MS-BASIC 5.28
Renommer l'interpréteur MSBASIC.COM ou BASIC86.COM original (toujours version 5.28) MBASIC86.COM (qui est un nom moins courant que MBASIC.COM et les autres).
S'assurer d'avoir MASM [ici ASM d'IBM], LINK et DEBUG.
Créer les fichiers texte suivants.
PATCH.BAT :
@echo off
echo Patch de la gestion d'erreur critique du BASIC Microsoft sous DOS 3.
echo Le succès de ce fichier batch requiert l'accès à ASM, LINK & DEBUG.
echo.
echo Pressez la combinaison Ctrl-C pour revenir à l'invite de commande.
pause

set liste=NOM DOS RET INI
rem **************
set msg=Assemblage
set nom=PATCH
rem **************
asm %nom%,,%nom%/n/x;
if errorlevel 1 goto Erreur
link %nom%;
erase %nom%.OBJ
rem Empêcher d'obtenir des fichiers bidons
if exist %nom%.EXE debug %nom%.EXE<%nom%.PAT>nul
if exist %nom%.EXE erase %nom%.EXE
for %%f in (%liste%) do if not exist %%f.TMP goto Erreur

set f=%nom%
rem *****************
set msg=Désassemblage
set nom=MBASIC86.COM
rem *****************
if not exist %nom% goto Erreur
debug %nom%<%f%>nul


rem ***************
set msg=Liage
set nom=MSBASIC.COM
rem ***************
if not exist %nom% goto Erreur
echo %msg% de %nom% réussi.
goto Fin

:Erreur
echo %msg% de %nom% échoué !

:Fin
for %%f in (%liste%) do if exist %%f.TMP erase %%f.TMP
set f=
set liste=
set msg=
set nom=
PATCH :
e1d47 81
e2f17 c3
e31fe 4f
nNOM.TMP
l3da6
nDOS.TMP
l452c
nRET.TMP
l6e3a
a7107
call 6f2d

e710f 1b
a7116
mov byte [bx],0
inc bx
mov byte [bx],0
dec bx
mov cl,0

nINI.TMP
l7a24
rcx
7c00
nMSBASIC.COM
w
q
PATCH.ASM :
		page	64,132
		title	Patch DOS 3 - BASIC 5.28 (les 8 mars 2018 & 30 mai 2020)
code		segment
		assume	cs:code

; Analyse d'un nom de variable BASIC en contrôlant continuellement sa longueur.
; Cela empêche ainsi d'écraser les numéros de fonction d'accès aléatoire (R/W).

	NAMLEN	equ	40

		org	3DA6h
		mov	ch,al
		push	cx
		MOV	CH,NAMLEN	; Nb de caractères au maximum
		mov	dx,1C5h
l_3DAE:		or	al,80h
		DEC	CH		; Compte à rebours
	jnz	$+5			; Contrôle placé dans la boucle
	jmp	SNERR			; SN si décompte nul
		mov	di,dx
		stosb
		inc	dx
		inc	bx
		mov	al,[bx]
		cmp	al,'9'+1
		jae	l_3DC1
		cmp	al,'0'
		jae	l_3DAE
l_3DC1:		call	s_31B4
		jnc	l_3DAE
		cmp	al,'.'
		je	l_3DAE
		MOV	AL,NAMLEN-1	; Initiale & 2nd caractère déjà lus

	sub	al,ch			; Longueur du nom moins deux

		pop	cx
		mov	ds:[1C5h],al
		mov	al,[bx]

if offset $-3DD9h
if2
%out Mauvaise taille (NOM) !
end
endif
endif
; Adresses absolues : saut à Syntax error, appel du test de lettre capitale.
		org	8D8h
	SNERR	label	near
		org	31B4h
	s_31B4	label	near

;-------------------------------------------------------------------------------

; Numéros.
	C_DCIO	equ	6
	ERRDIO	equ	57
	ERRDWP	equ	70
; Pointeurs.
	MSDCCF	equ	132h
	CTLCAD	equ	134h
	DINTAD	equ	138h
	GETVEC	equ	140h

		org	16h
PSP_Parent	equ	this word	; Mot non documenté (DOS2+)
		org	8F2h
ERROR	label	near
		org	452Ch
MSISET	proc
; Adapte le DOS et configure ses vecteurs après sauvegarde.
	push	ds
	push	es

; Si DOS2+, fait que BASIC :
; - enclenche BREAK si inactif et prévoit sa restauration ;
; - ne scrute ^S & ^C qu'une fois sur cinq lors de l'appel CNTCCN ;
; - affiche avec la FCT2 multitâche depuis la procédure CONSOT ;
; - obtient un vecteur d'INT via la FCT35h.

	mov	word ptr ds:[13Ch],4453h
	mov	byte ptr ds:[133h],C_DCIO
	mov	word ptr ds:GETVEC,offset I8086_vec
	mov	ah,30h
	int	21h
	cmp	al,2
	 jb	Adresses_lointaines
	mov	ax,3300h
	int	21h
	or	al,dl
	 jnz	Routines_DOS2
	mov	byte ptr cs:Bond,al
	inc	dx
	inc	ax
	int	21h
Routines_DOS2:
	mov	word ptr ds:[13Ch],4434h
	mov	byte ptr ds:[133h],2
	mov	word ptr ds:GETVEC,offset MSDOS_vec
Adresses_lointaines:
	mov	ax,cs
	mov	ds:[13Ch]+2,ax
	mov	ds:GETVEC+2,ax

; Sauve puis modifie les vecteurs DOS.

	mov	al,24h
	mov	di,DINTAD
	call	SAVVEC
	dec	ax
	mov	di,CTLCAD
	call	SAVVEC
	dec	ax
	mov	di,164h
	call	SAVVEC

	push	cs
	pop	ds
	mov	ah,25h
	mov	dx,offset Terminaison
	int	21h
	inc	ax
	mov	dx,offset MSCTLC
	int	21h
	inc	ax
	mov	dx,offset DSKERR
	int	21h

	pop	es
	pop	ds

; Actualise les vecteurs du PSP (DOS3+ après ABORT).

Retour:
	mov	dx,cs
	mov	ah,26h
	int	21h
	ret
MSISET	endp

MSIRST	proc
; Restaure les vecteurs DOS et la configuration initiale du système.
	sti				; En cas de 1er CALL GETSTK sans RET
	mov	al,24h
	mov	bx,DINTAD
	call	SETVEC
	dec	ax
	mov	bx,CTLCAD
	call	SETVEC
	dec	ax
	mov	bx,164h
	call	SETVEC

; Restaure le PSP parent (DOS2+ après ABORT).

	lds	ax,ds:[168h]
		assume	ds:code
	mov	PSP_Parent,ax

; Remet BREAK sur OFF si état original (DOS2+).

	 jmp	short Retour		; JMPS $+2 le cas échéant
Bond	equ	$-1
	xor	dl,dl
	mov	ax,3301h
	int	21h
	 jmp	short Retour
MSIRST	endp
		assume	ds:nothing

SAVVEC	proc
; Sauve un vecteur.
	call	dword ptr ds:GETVEC
	mov	[di],bx
	mov	[di+2],es
	ret
SAVVEC	endp

; L'une des deux routines suivantes sera accessible par l'appel GETVEC.
; Entrée :	AL	numéro d'interruption
; Sortie :	ES:BX	vecteur correspondant

I8086_vec	proc	far
; Obtient un vecteur (DOS1).
	xor	bx,bx
	mov	es,bx
	mov	bl,al
	shl	bx,1
	shl	bx,1
	les	bx,es:[bx]
	ret
I8086_vec	endp

MSDOS_vec	proc	far
; Obtient un vecteur (DOS2+).
	push	ax
	mov	ah,35h
	int	21h
	pop	ax
	ret
MSDOS_vec	endp

SETVEC	proc
; Restaure un vecteur.
	push	ds
	lds	dx,[bx]
	mov	ah,25h
	int	21h
	pop	ds
	ret
SETVEC	endp

MSCTLC	proc	far
; Routine INT23h (Ctrl-C).
	mov	byte ptr ds:MSDCCF,3	; ^C
	iret
MSCTLC	endp

Seg_DS	dw	 ?

DSKERR	proc	far
; Routine INT24h (erreur critique) avec ABORT.
; L'adresse de retour au programme pointe la routine INT22h puisque DR DOS
; convertit ABORT en FAIL lorsque le PSP actif est son propre parent.
	mov	bp,sp
	push	ds

	mov	ds,Seg_DS
	mov	word ptr [bp+24],offset Terminaison
	mov	[bp+26],cs
	lea	ax,[bp+30]
	mov	ds:[16Ch],ax
	mov	ds:[16Eh],di

	pop	ds
	mov	al,2
	iret
DSKERR	endp

; Retour INT22h (fin de l'erreur critique).
; Restaure les registres de segment et de pile après l'abandon avorté.

Terminaison:
	cli
	mov	ax,Seg_DS
	mov	ds,ax
	mov	es,ax
	mov	ss,ax	
	mov	sp,ds:[16Ch]		; Suppose que SS=Seg_DS dans Int24... 
	sti

; Convertit l'erreur critique en numéro d'erreur BASIC.

	mov	dl,ERRDIO		; Anciennement "Disk I/O Error" (DOS1)
	mov	ax,ds:[16Eh]		; AL={0; 1;2; 3;4; 5;6; 7;8; 9;A;...}h
	ror	al,1			;   ={0;80;1;81;2;82;3;83;4;84;5;...}h
	cmp	al,4
	 ja	DSKERX
	 jp	Conversion		;   ={ 0; 1; 2; 3; 4}
	inc	ax
Conversion:				;   ={ 0; 2; 3; 3; 4}
	shr	al,1			; AL={ 0; 1; 1; 1; 2}
	adc	al,ERRDWP		; CF={ 0; 0; 1; 1; 0} avant l'addition
	xchg	dx,ax			; DL={70;71;72;72;72}
DSKERX:
	 jmp	ERROR			; Err.critique	DL=ERR (erreur BASIC)
					; -------------------------------------
					; 0		70=Disk write protected
					; 2		71=Disk not Ready
					; 4 (bad CRC)	72=Disk media error
					; 6 (seek err)	idem
					; 8 (sec n/fnd)	idem
					; autre		57=Device I/O Error
if offset $-4642h
if2
%out Mauvaise taille (DOS) !
end
endif
endif
					
;-------------------------------------------------------------------------------

	org	6E3Ah
	call	MSIRST			; Restaure l'état d'origine du DOS
					; avant que le BASIC ne se termine
if offset $-6E3Dh
if2
%out Mauvaise taille (RET) !
end
endif
endif

;-------------------------------------------------------------------------------

; Initialisation du BASIC Microsoft (portage MS-DOS de la version 5 pour CP/M).
; Les corrections du lancement de MS-BASIC portent sur :
; - une procédure incompatible avec les vecteurs DOS3 et DR DOS (proc MSISET) ;
; - un tampon de ligne plein en cas de suite initiale de blancs (byte[372h]) ;
; - un tampon d'édition (^A) aléatoire avant la première entrée (byte[373h]) ;
; - un offset non nul activant l'interception d'erreur du BASIC (word[4CBh]) ;
; - une réservation de mémoire annulée par un débordement (incrémentation à 0) ;
; - une pile trop petite permise au démarrage (proc. de l'Altair mise de côté).
; Tailles.
	DATPSC	equ	128			; DTA sous DOS (DMA sous CP/M)
	FCBSIZ	equ	38			; File Control Block (37 octets)
; File-Data-Block (offsets).
	F_MODE	equ	0
	F_FCB	equ	1+F_MODE		; Mode={1:"I" ; 2:"O|A" ; 3:"R"}
	LOCOFS	equ	F_FCB+FCBSIZ		; CURLOC (secteur de 128 octets)
	ORNOFS	equ	2+LOCOFS		; Nb d'octets dans le tampon
	NMLOFS	equ	1+ORNOFS		; Nb d'octets restant
	DATOFS	equ	1+NMLOFS		; VARPTR(#) pointe ce tampon et
	DBLKSZ	equ	DATOFS+DATPSC		; non le FCB en accès séquentiel
	DBLK_C	equ	0+DBLKSZ
; Informations de la version 5.0 du BASIC (offsets).
	FD_MAX	equ	0
	FD_SIZ	equ	DBLKSZ			; Taille d'enregistrement
	FD_PHY	equ	2+FD_SIZ		; Enregistrement physique actuel
	FD_LOG	equ	2+FD_PHY		; Enregistrement logique courant
	FD_CHG	equ	2+FD_LOG		;Future flag for across block ?s
	FD_OPS	equ	1+FD_CHG		; Position (PRINT, INPUT, WRITE)
	FD_DAT	equ	2+FD_OPS		; Tampon FIELD que pointe VARPTR
	FNZBLK	equ	FD_DAT+FD_MAX		; au lieu du FCB en accès direct
; Pointeurs.
	BUFMIN	equ	372h			; Virgule puis tampon d'édition
	ONELIN	equ	4CBh			; Offset du traitement d'erreur
; Saut initial.
		org	7A24h
		mov	dx,cs
		add	dx,7CDh
		mov	ah,26h
		int	21h
		mov	ds,dx

	assume	cs:nothing,es:code
	mov	ax,es
	mov	ds:[16Ah],ax
	xchg	ax,PSP_Parent
	mov	ds:[168h],ax
	assume	cs:code,es:nothing

		mov	es,dx
		cli
		mov	ss,dx
		mov	bx,3F3h
		mov	ds:[487h],bx
		mov	sp,bx
		sti
		xor	al,al
		mov	ds:[5E6h],al
		mov	ch,67h
		mov	bx,100h
		mov	dx,7E0h
l_7A60:		mov	si,dx
		lods	byte ptr cs:[si]
		mov	[bx],al
		inc	bx
		inc	dx
		dec	ch
		jnz	l_7A60
		call	s_44B3
		CALL	MSISET			; Configure selon la version DOS
		mov	bx,ds:[487h]
		mov	dh,bh
		mov	dl,bl
		mov	ds:[15Ch],bx
		mov	bx,233h
		mov	byte ptr [bx],':'
		call	s_2FFC
		mov	ds:[476h],al
		mov	bx,2221h
		mov	ds:[231h],bx
		xor	al,al
		mov	ds:[159h],al
		mov	ds:[475h],al
		mov	ds:[10Ch],al
		mov	ds:[5EDh],al
		mov	ds:[5E7h],al
		mov	ds:[144h],al
	mov	bx,0				;GET 0 (dixit INIT.MAC)
	mov	ds:BUFMIN,bx			; Fin de ligne, tampon d'édition
	mov	ds:ONELIN,bx			; ON ERROR GOTO 0
		mov	bx,80h
		mov	ds:[5E4h],bx
		mov	bx,48Bh
		mov	ds:[489h],bx
		mov	bx,4FAh
		mov	ds:[562h],bx
		mov	bx,ds:[6]
		mov	ds:[487h],bx
		mov	al,3
		mov	ds:[1C4h],al
		mov	bx,10Ch
		mov	ds:[4B0h],bx
		mov	bx,80h
		mov	al,[bx]
		or	al,al
		mov	ds:[4B0h],bx
		jnz	$+5
		jmp	DONCMD
		mov	ch,[bx]
		inc	bx
l_7AE1:		mov	al,[bx]
		dec	bx
		mov	[bx],al
		inc	bx
		inc	bx
		dec	ch
		jnz	l_7AE1
		dec	bx
		mov	byte ptr [bx],0
		mov	ds:[4B0h],bx
		mov	bx,7Fh
		call	s_103E
		or	al,al
		JZ	DONCMD			; Saut cond. suffisant
		cmp	al,'/'
		je	l_7B1C
		dec	bx
		mov	byte ptr [bx],'"'
		mov	ds:[4B0h],bx
		inc	bx
l_7B0E:		cmp	al,'/'
		je	l_7B1C
		call	s_103E
		or	al,al
		jnz	l_7B0E
		JMP	SHORT DONCMD		; Saut court suffisant
l_7B1C:		mov	byte ptr [bx],0
		call	s_103E
l_7B22:		call	s_1B4F
		cmp	al,'S'
		je	l_7B69
		cmp	al,'M'
		pushf
		je	l_7B35
		cmp	al,'F'
		je	$+5
		jmp	SNERR
l_7B35:		call	s_103E
		call	s_3041
		db	':'
		call	s_1B5C
		popf
		je	l_7B59
		mov	al,dh
		or	al,al
		jz	$+5
		jmp	l_117A
		mov	al,dl
		cmp	al,10h
		jb	$+5
		jmp	l_117A
		mov	ds:[1C4h],al
		jmp	short l_7B5D
l_7B59:		mov	ds:[487h],dx
l_7B5D:		dec	bx
		call	s_103E
		jz	DONCMD
		call	s_3041
		db	'/'
		jmp	short l_7B22
l_7B69:		call	s_103E
		call	s_3041
		db	':'
		call	s_1B5C
		mov	ds:[5E4h],dx
		jmp	short l_7B5D

DONCMD:	mov	Seg_DS,ds

		dec	bx
		mov	bx,ds:[487h]
		dec	bx
		mov	ds:[487h],bx
		dec	bx
		push	bx
		mov	al,ds:[1C4h]
		mov	bx,671h
		mov	ds:[1A2h],bx
		mov	dx,1A4h
		mov	ds:[1C4h],al
		inc	al
		MOV	CX,DBLK_C +1		; FDB interne (#0) +1
l_7BA2:		xchg	bx,dx
		mov	[bx],dx
		inc	bx
		inc	bx
		xchg	bx,dx
		add	bx,cx
		jnc	$+5
		jmp	l_2F37
	dec	bx				; -1 : anticipe un débordement
		push	bx
		mov	bx,ds:[5E4h]
		MOV	CX,FNZBLK +1		; FDB utilisateur +1
		add	bx,cx
		mov	cx,bx
		pop	bx
		dec	al
		jnz	l_7BA2
		inc	bx
		mov	ds:[160h],bx
		mov	ds:[4C3h],bx
		pop	dx
		mov	al,dl
		and	al,0FEh
		mov	dl,al
		mov	al,dl
		sub	al,bl
		mov	bl,al
		mov	al,dh
		sbb	al,bh
		mov	bh,al
		jnb	$+5
		jmp	l_2F37
		mov	cl,3
		shr	bx,cl
		mov	al,bh
		cmp	al,2
		jb	l_7BF0
		mov	bx,200h
l_7BF0:	dec	bx				; "Out of memory" si pile nulle
		mov	al,dl
		sub	al,bl
		mov	bl,al
		mov	al,dh
		sbb	al,bh
		mov	bh,al
		jnb	$+5
		jmp	l_2F37
		mov	ds:[487h],bx
		xchg	bx,dx
		mov	ds:[15Ch],bx
						;REASON n'a pas raison d'être...
		mov	sp,bx
		mov	ds:[4C3h],bx
		mov	bx,ds:[160h]
	cli					; Prévient une pile trop faible
	call	PgmNul				; Prépare la prochaine procédure
		xchg	bx,dx
		CALL	GETSTK			;... mais GETSTK de l'Altair si.
	sti					; Sans retour si "Out of memory"
		sub	bx,dx
		dec	bx
		dec	bx
		push	bx
		mov	bx,7C61h
		call	s_503B
		pop	bx
		call	s_5849
		mov	bx,7C55h
		call	s_503B
		call	s_2E9C
		mov	ds:[4D0h],ds
		jmp	l_7A09
if offset $-7C3Dh
if2
%out Mauvaise taille (INI) !
end
endif
endif
; Offsets : ignore blanc, "Illegal function call", byte min/maj, lit nb, CRLF...
		org	103Eh
	s_103E	label	near
		org	117Ah
	l_117A	label	near
		org	1B4Fh
	s_1B4F	label	near
		org	1B5Ch
	s_1B5C	label	near
		org	2E9Ch
	s_2E9C	label	near
		org	2F0Bh			; CL=nombre de paramètres requis
	GETSTK	label	near			; Vérifie que la pile suffit
		org	2F37h
	l_2F37	label	near
		org	2FFCh
	s_2FFC	label	near
		org	3041h
	s_3041	label	near
		org	44B3h
	s_44B3	label	near
		org	503Bh
	s_503B	label	near
		org	5849h
	s_5849	label	near
		ORG	7116h			; Entrée : BX=début du programme
	PgmNul	LABEL	NEAR			; Sortie : word[BX] nul & CL nul
		org	7A09h
	l_7A09	label	near
code		ends

		end
PATCH.PAT :
rbx
0
nNOM.TMP
rcx
33
w3da6
nDOS.TMP
rcx
116
w452c
nRET.TMP
rcx
3
w6e3a
nINI.TMP
rcx
219
w7a24
q
Le plus dur : lancer le fichier batch.
Fin de la procédure.

Nous retiendrons que les défaillances de DEF USR et FILES"Z:" étaient dues à de malheureuses modifications par le personnel de Microsoft. Pour le reste, l'usage du DOS non documenté a démontré ses limites quant au futur des applications alors qu'un retour au code de l'Altair a permis de corriger élégamment celui issu de CP/M.

Dimanche 11 février 2018

Rédaction en cours d'une petite documentation technique :
BASIC Microsoft 5.28 mis à jour pour DOS 3


Sommaire
--------
1. Particularités BASIC
2. Précaution d'emploi
3. Soucis rencontrés
4. Mise à jour
5. Souci de mise à jour


1. Particularités BASIC
-----------------------
Le BASIC Microsoft est un standard de fait. Un sous-ensemble de ce langage sert
de socle commun à différentes versions ROM, cartouche, cassette ou disque. Celle
présente est spécifique au système d'exploitation MS-DOS, donc orientée télétype
(pas de gestion d'écran). D'où l'usage fréquent sous MS-DOS 2 d'un gestionnaire
ANSI adapté au matériel de l'utilisateur (PRINT CHR$(27);7l; pour désactiver le
retour automatique de MS-DOS qui est redondant avec celui effectué par le BASIC
sur indication de l'instruction WIDTH).
Ce BASIC n'a pas gardé le calcul matriciel permis par l'original de Dartmouth.
...

2. Précaution d'emploi
----------------------
Il s'agit du BASIC-80 pour CP/M adapté pour MS-DOS. L'appel CALL 5 au BDOS passe
la main au DOS via le wrapping du 8086. En effet, un appel lointain s'y trouve :
l'offset de son adresse représente le montant de mémoire libre au format xxx0h.
De même, pour revenir au début de la mémoire, le segment est au format Fxxxh.
Le wrapping du 8086 n'est pas compatible avec l'activation de la ligne A20 du
processeur 80286, à moins de charger DOS en mémoire haute (HMA = segment FFFFh)
via la commande DOS=HIGH dans le fichier CONFIG.SYS du disque système (DOS 3.x).

Pis, Windows Me ainsi que la FAT32 en mode réel sont incompatibles avec les FCB.


3. Soucis rencontrés
--------------------
Les dernières corrections oubliées du BASIC-86 (partie entière du zéro négatif)
se trouvent dans cette version. Par contre, des modifications ont été loupées :
- le correctif de USR est mal transcrit (CMP DX,-1 devant remplacer CMP DL,-1);
- la reconnaissance de la lettre de lecteur dans FILES saute la dernière (Z:) ;
- et la procédure de conversion minuscule/majuscule qui va avec est redondante.

L'initialisation hérite des erreurs de la version CP/M :
- valeurs importantes supposées être mises à zéro ;
- réservation des données BASIC pouvant s'annuler ;
- absence de vérification de la taille de la pile.

S'agissant de ce contrôle de pile, la taille admise doit dépasser les 64 octets
tout en fixant une valeur paire comme paramètre minimal de l'instruction CLEAR.
Cette précaution n'est pas vaine vu la nature interruptible du micro-processeur.

Quant au contrôle de la longueur du nom d'une variable, il n'empêche pas le nom
de déborder et d'écraser le préfixe du tampon de chargement de programme BASIC.

De plus, la configuration du DOS n'est plus compatible avec la version 3 du DOS.
Une erreur critique (pas de papier ou de disque...) plante l'invite de commande.
En effet, le handler INT 24h force l'abandon sachant que DOS 1 & 2 ne restaurent
les vecteurs DOS que pour une fin normale. Or BASIC se fait passer pour l'invite
de commande via le mot non documenté du PSP, le PSP Parent, qui pointe lui-même.
Ainsi, DOS 3 termine le BASIC sans libérer la mémoire obérée par son format COM.
Seul DR DOS transforme alors l'abandon en échec de fonction (permise par DOS 3).
DOS Plus, quant à lui, ignore ce mot secrètement apparu sous DOS 2 mais restaure
les vecteurs DOS à la manière de DOS 3. Il termine donc tranquillement le BASIC.

4. Mise à jour
--------------
C'est PATCH.BAT, en collaboration avec les outils MASM, LINK, EXE2BIN et DEBUG,
qui s'en charge. Il préserve l'apparence du code original (code 8080 converti).
L'interpréteur original est nommé pour l'occasion MBASIC86.COM car peu courant.
L'interpréteur obtenu s'appelle MSBASIC.COM comme attendu des utilisateurs.

Sa gestion d'erreur critique est compatible DOS 3 (plus de plantage) via la mise
à jour du PSP. Celle évite aussi la fin prématurée sous DOS Plus. L'adresse de
de retour est remplacée par le vecteur de terminaison (INT 22h) pour DR DOS. Les
messages d'erreur disque sont enfin exploités en place de Device I/O Error (Disk
I/O Error du temps de DOS 1 et CP/M), comme c'est la cas sous GW-BASIC.

5. Souci de la mise à jour
--------------------------
Par contre la mise à jour du PSP requiert que la fonction Nouveau PSP soit bien
implémentée. Or celle de DR DOS 6 requiert le correctif PAT312 et celle de DOS
Plus ne prend pas en compte le chevauchement de son code de wrapping du 8086.
Digital Research a dû ignorer le test de la fonction 26h qui était obsolète.

DR DOS 6 est un DOS émulé, la business update légère comme PalmDOS mise à part.
Lors de la copie du nouveau PSP, il se réfère à la variable du registre FLAG du
PSP courant comme segment au lieu de celle du registre CS ! L'indice utilisé en
est la cause. Le correctif PAT312 est le DR DOS 6 d'origine dépourvu de ce bug.
Il remplace la valeur d'index fautive 16h par 14h à l'octet 192Ah d'IBMDOS.COM.
Néanmoins, elle est située à l'octet 1B31h du fichier de 39 Ko aussi de Lineo :
--------------------------------------------------
*** Symbolic Instruction Debugger ***  Release 3.2
      Copyright (c) 1983,1984,1985,1988,1990,1991
    Digital Research, Inc. All Rights Reserved
--------------------------------------------------

  Start      End
1693:0100 1693:9C7A
#L1C2B 1C32
  1693:1C2B LES    DI,FC[BP]
  1693:1C2E ES:    MOV    ES,16[DI]
  1693:1C32 ES:    MOV    SI,[0002]
#Q
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Le correctif ne pouvant s'appliquer à ce fichier plus récent, la solution est :
ATTRIB -RSH IBMDOS.COM
SID IBMDOS.COM
S1C31
14
.
WIBMDOS.COM
Q
ATTRIB +RSH IBMDOS.COM
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DOS Plus, au lieu d'écraser les vecteurs d'INT 30h et 31h par un saut lointain
au DOS, appelle depuis l'offset 5 un code néanmoins plus long qu'un paragraphe.
En effet, par similarité avec les versions antérieures à MS-DOS 3, le montant
mémoire à l'offset 6 d'un PSP créé par la fonction 26h peut culminer à FFF0h,
contre FEF0h seulement pour celui créé automatiquement par DOS à l'exécution.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
La solution est que le mot 9E62h de DOSPLUS.SYS (version 1.2) soit mis à FFE0h.
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Dimanche 25 février 2018

Le 8086 peut être interrompu à tout moment. Il aurait plus raisonnable de diminuer la valeur de l'octet situé à l'offset 2F17h. Il s'agit du poids faible d'un mot négatif auquel on soustrait la limite du sommet de pile.
Toutefois, il faudrait en parallèle augmenter la taille minimale de pile permise par CLEAR (octet à l'offset 31FCh). En effet, il serait stupide en cours de programme de définir une pile si petite qu'elle s'empêcherait d'être redéfinie par un nouvel appel à CLEAR !
C'est pourquoi toute interruption 8086 pouvant intervenir en cours de programme utilisateur devrait définir sa propre pile, à moins d'en faire un faible usage. De toute façon, six octets seront obligatoirement utilisés : IP, CS et FLAG.

Mardi 27 février 2018

Mise à jour du fait que le sommet de pile (mot en DS:487h) pointe en fait le dernier octet de mémoire libre (au moins, dans les versions MS-DOS). De plus, CLEAR ne devrait pas avoir un paramètre de pile minimum impair (79 au lieu de 80). D'ailleurs, la routine en 2F0Bh peut imposer une pile avec un mot de plus que prévu sans contrarier l'instruction CLEAR en mode direct (tapée sans numéro de ligne).

Dimanche 4 mars 2018

Pluie : j'en profite pour résoudre un bug dû, de manière inattendue, au fait que la longueur d'un nom de variable n'était vérifiée qu'une fois que celui-ci était mis en forme dans son tampon.
[J'avais chargé un programme BASIC au format ASCII comportant une longue ligne sans caractère séparateur, ce qui écrasa les numéros de fonction de lecture/écriture aléatoire]

Vendredi 9 mars 2018

Mise à jour grâce au code source du BASIC Microsoft 5.x pour CP/M disponible depuis l'été dernier :
http://winworldpc.com/product/microsoft-basic/80

Samedi 24 mars 2018

Pour rappel, le compilateur DOS correspondant est numéroté 5.36, et sa fonction VARPTR(#n) renvoie l'adresse du FCB (comme sous GW-BASIC et non de la DTA).

Samedi 15 décembre 2018

Selon le code source, la commande H de l'éditeur orienté ligne signifie Hack. J'avais donné un sens plus proche du monde fruitier (équeuter une cerise) :
viewtopic.php?p=95496#p95496

Vendredi 6 septembre 2019

Ce sujet oublié est amené à être clos. J'ai ma petite idée pour distinguer discrètement le nouveau (C) par Micro$oft. L'archive comprendra le patch avec le MASM 2 corrigé d'IBM. Cela signifie que je ne suis pas tombé sur d'autre problème majeur.

Jeudi 9 janvier 2020

Il existait deux modes de diffusion des programmes commerciaux, les versions compilées pour CP/M ou MS-DOS et les versions interprétées protégées. Ces dernières gardaient leur code à l'abri de l'utilisateur. Mais que ce passe-t-il si le code en clair est perdu ? Nos yeux pour pleurer ? Non, un petit logiciel de décryptage ou DEBUG encore. Le premier diffère selon qu'il s'agisse de MBASIC ou GW-BASIC, mais le chiffrement est tout aussi faible : genre double XOR, on est loin du cryptage quantique ! Le second utilitaire évite de casser le code car on y charge le BASIC qui décode tout bonnement en mémoire vive.

La troisième solution vient de l'exploitation ingénieuse d'un bug. La personne à l'origine de cela est tout aussi ingénieuse :
http://www.chebucto.ns.ca/~af380/GW-BAS ... s.html#tur
C'est très simple de l'adapter depuis sa version GW-BASIC à notre BASIC Microsoft :
- remplacer 97 de DEF par 98 ;
- remplacer 98 de POKE par 99 ;
- remplacer 1450 par 1510.
Comme il le montre, connaître l'octet incriminé est un jeu d'enfant. Par contre, autour de 1000, on risque d'activer une erreur autre que Illegal function call. Dans notre cas, le désassemblage indique que l'octet 1510 est le premier initialisé.

Ensuite, un copier-coller est facile sous Windows ou TaskMAX de DR DOS. Dans les autres cas, il faudra truander pour ne pas tout retaper. Et pour cela nous avons MERGE et une particularité de l'interpréteur. MERGE charge en mémoire un programme sans effacer l'ancien. Or nous, nous souhaitons exécuter nos ordres directement, donc sans numéro de ligne. BASIC ne signalera pas l'erreur si tout tient sur une ligne ; par contre, notre vieille version est obligée d'attendre l'utilisateur pour analyser :
- supprimer tous les commentaires du bloc sous load"secret.bas" ;
- réunir en une ligne unique en intercalant un double-point à chaque jonction ;
- enregistrer cette LIGNE unique dans un fichier BAS sans retour à la ligne.

Déroulement de la procédure :
- SAVE"PRGM",P ;
- LOAD"PRGM" ;
- LIST échoue ;
- MERGE"LIGNE" ;
- touche ENTRÉE pour obtenir un second Ok ;
- ?val(b$)456 qui provoque le BREAK salvateur ;
- LIST fonctionne à nouveau.

Samedi 11 janvier 2020

Une astuce pour obliger l'utilisateur à appuyer sur Entrée est que la ligne soit déjà pleine (255 octets). De même, il est sage qu'un caractère EOF (ASCII 26) la suive dans le fichier ; cela évite le malheureux message Direct statement in file dû à un éventuel retour à la ligne. CLEAR nous permet d'avoir un tableau à onze valeurs nulles par défaut. Ainsi, nous sommes sûr d'avoir au moins un octet nul de début de ligne BASIC et trois octets nuls de fin de programme.
À présent, nous avons tout ce qu'il faut pour obtenir notre fichier PROFLG.BAS :
clear:a%[1]=&h2020:a%[2]=&h2020:a%[3]=&h2098:a%[4]=&h4553:a%[5]=&h3A47:a%[6]=&h1C99:a%[7]=1510:a%[8]=&h112C:b$="":b$="123"+chr$(28)+":::"+chr$(137)+chr$(13)+mki$(varptr(a%[0]))+":":?val(b$)456'SAVE"protbas",P ... LOAD"protbas":MERGE"PROFLG" [ENTER][ENTER]
Il suffit d'entrer MERGE"PROFLG" après avoir chargé le fichier protégé, puis de rappuyer sur la touche ENTER pour faire sauter la protection.

20 janvier 2020

Vous trouvez complexe la méthode précédente. Il en existe une bien plus simple à comprendre sous DOS. Il s'agit de la seconde exposée ici :
http://www.petesqbsite.com/sections/zin ... s2-gwbasic
Il suffit de charger un moignon de programme BASIC, au format token non protégé, par-dessus celui en mémoire. Lors d'un chargement, BASIC effectue d'abord une commande NEW, annulant par un mot nul l'offset pointant la deuxième ligne. Ensuite, il lit la valeur purement indicative de l'offset du nouveau programme et, si elle n'est pas nulle (fin de programme), la recalcule pour la configuration courante de la mémoire. Notre programme créé par DEBUG comportera donc l'octet descripteur 255 (tokens non cryptés) et un octet non nul :
A>DEBUG
e100 ff 1
rcx
2
nUNPROT.BAS
w
q
On charge le programme crypté dans BASIC, puis on tape LOAD"UNPROT" pour l'avoir en clair. C'est tout !

Cette méthode ne fonctionne pas sous CP/M car cet OS ne connaît la taille des fichiers qu'en groupe de 128 octets, et le caractère EOF ne sert que dans les fichiers ASCII. Par exemple sous CP/M-86, il faut revenir à la méthode précédente avec 1395 au lieu de 1510.

24 mai 2020

Le code source du GW-BASIC (1983) est à présent disponible ici (code 8 bits traduit en 16 plus macro INS86) :
https://github.com/microsoft/GW-BASIC/a ... master.zip

31 mai 2020

Le début du fichier PATCH.ASM apporte une nouvelle façon de corriger le problème suivant. MBASIC n'épargne pas d'un débordement le tampon NAMBUF (nom de variable de plus de 40 caractères). Voyons cet extrait du fichier GWDATA.ASM (GW-BASIC) :
PUBLIC	NAMCNT
NAMCNT	LABEL WORD
	DB	1 DUP(?)	;THE NUMBER OF CHARACTER BEYOND #2 IN A VAR NAME
PUBLIC	NAMBUF
NAMBUF	LABEL WORD
	DB	NAMLEN-2 DUP(?)	;STORAGE FOR CHARS BEYOND #2. USED IN PTRGET
PUBLIC	NAMTMP
NAMTMP	LABEL WORD
	DB	2 DUP(?)	;TEMP STORAGE DURING NAME SAVE AT INDLOP
PUBLIC	DIRTMP
	DIRTMP=CPMWRM+128	;USE CPM DEFAULT BUFFER IN LOW MEMORY
PUBLIC	FILNA2
FILNA2	LABEL WORD
	DB	16 DUP(?)	;USED BY NAME CODE
	DB	17 DUP(?)	;this is used for FILES in SCP version
PUBLIC	FILNAM
FILNAM	LABEL WORD
	DB	33 DUP(?)	;BECAUSE CPM MUST HAVE BUFFER FOR DIRECTORY READS
;
;       CP/M 1.4 and 2.x Support
;

PUBLIC	CPMVRN
CPMVRN	LABEL WORD
	DB	1 DUP(?)	;CP/M Version Number (#0 is 2.x)
PUBLIC	CPMREA
CPMREA	LABEL WORD
	DB	1 DUP(?)	;CP/M Read Call
PUBLIC	CPMWRI
CPMWRI	LABEL WORD
	DB	1 DUP(?)	;CP/M Write Call
	DB	":"		;a colon for restarting input
PUBLIC	KBUF
KBUF	LABEL WORD
	DB	KBFLEN DUP(?)	;THIS IS THE KRUNCH BUFFER
Une suite alphanumérique accidentellement prise pour un nom de variable ne doit pas dépasser une centaine de caractères. Sinon, les numéros de fonctions d'accès direct CPMREA & CPMWRI risquent l'écrasement. Regardons la logique fautive du compteur d'origine :
Compteur := -1
Boucle:
incrémente Compteur
recopie caractère suivant
si lettre ou chiffre, Boucle
si Compteur >= 40-1, Erreur
Le compteur (longueur du nom moins deux) n'est vérifié qu'à la fin. Corrigeons avec un décompte vérifié à chaque boucle :
Décompte := 40
décrémente Décompte
si Décompte nul, Erreur
recopie caractère suivant
si lettre ou chiffre, Boucle
Compteur := Décompte - 39
Dès que le décompte est nul, l'erreur est détectée.

2 juin 2020

J'en profite que le sujet a été remonté pour ajouter de la documentation au fichier PATCH.ASM (cf. le 7 février 2018). Les commentaires indiquent la structure des FBD (File Block Data) du BASIC-86 pour MS-DOS. Elle est très proche de celle du BASIC-80 pour CP/M ; seule la taille du FCB (File Control Block) diffère. Contrairement à GW-BASIC et au compilateur BASCOM, VARPTR(#n°fichier) ne pointe pas le FCB mais :
- le tampon DTA (Disk Transfer Area) en mode d'accès séquentiel (INPUT, OUTPUT, APPEND) ;
- le tampon FIELD en mode d'accès direct (RANDOM).
La DTA était nommée DMA (Direct Memory Access) sous CP/M. Quant au mode d'accès, sa numérotation diffère sous GW-BASIC.


### B R O U I L L O N ### (21 juin 2020)

Pour que la longueur de la partie "initialisation" puisse varier, voici des fichiers PATCH, PATCH.PAT et PATCH.ASM alternatifs :
e1d47 81
e31fe fe
nNOM.TMP
l3da6
nDOS.TMP
l452c
nRET.TMP
l6e3a
e710f 1b
nINI.TMP
l7a24
a80
cld
xor	al,al
mov	di,7a24
add	di,cx
mov	cx,7f
rep	stosb
mov	cl,7
shr	di,cl
shl	di,cl
mov	cx,di
dec	ch

rip
80
g97
nMSBASIC.COM
w
q
a
sub cx,7a24

t
nINI.TMP
w7a24
nNOM.TMP
rcx
33
w3da6
nDOS.TMP
rcx
116
w452c
nRET.TMP
rcx
3
w6e3a
q
 		page	64,132
		title	Patch DOS 3 pour MS BASIC 5.28 (le 20 septembre 2020)
CSEG		segment
		assume	cs:CSEG,ds:DSEG

; Analyse d'un nom de variable BASIC en contrôlant continuellement sa longueur.
; Cela empêche ainsi d'écraser les numéros de fonction d'accès aléatoire (R/W).

		org	3DA6h
		mov	ch,al
		push	cx
		MOV	CH,offset NAMLEN; Nb de caractères au maximum
		mov	dx,offset NAMBUF-1
VMORCH:		or	al,128
		DEC	CH		; Compte à rebours
	jnz	$+5			; Contrôle placé dans la boucle
	jmp	SNERR			; SN si décompte nul
		mov	di,dx
		stosb
		inc	dx
		inc	bx
		mov	al,[bx]
		cmp	al,'9'+1
		jae	VMORC1
		cmp	al,'0'
		jae	VMORCH
VMORC1:		call	ISLET2
		jae	VMORCH
		cmp	al,'.'
		jz	VMORCH
		MOV	AL,NAMLEN-1	; Initiale & 2nd caractère déjà lus

	sub	al,ch			; Longueur du nom moins deux

		pop	cx
		mov	NAMCNT,al
		mov	al,[bx]
if offset $-3DD9h
if2
%out Mauvaise taille (NOM) !
end
endif
endif

;-------------------------------------------------------------------------------

; Numéros.
	C_DCIO	equ	6
	ERRDIO	equ	57
	ERRDWP	equ	70

		org	16h
PSP_Parent	equ	this word	; Mot non documenté (DOS2+)
		org	8F2h
ERROR	label	near
		org	452Ch
MSISET	proc
; Adapte le DOS et configure ses vecteurs après sauvegarde.
	push	es

; Si DOS2+, fait que BASIC :
; - enclenche BREAK si inactif et prévoit sa restauration ;
; - ne scrute ^S & ^C qu'une fois sur cinq lors de l'appel CNTCCN ;
; - affiche avec la FCT2 multitâche depuis la procédure CONSOT ;
; - obtient un vecteur d'INT via la FCT35h.

	mov	word ptr ds:13Ch,4453h
	mov	byte ptr ds:133h,C_DCIO
	mov	word ptr GETVEC,offset I8086_vec
	mov	ah,30h
	int	21h
	cmp	al,2
	 jb	Adresses_lointaines
	mov	ax,3300h
	int	21h
	or	al,dl
	 jnz	Routines_DOS2
	mov	byte ptr cs:Bond,al
	inc	dx
	inc	ax
	int	21h
Routines_DOS2:
	mov	word ptr ds:13Ch,4434h
	mov	byte ptr ds:133h,2
	mov	word ptr GETVEC,offset MSDOS_vec
Adresses_lointaines:
	mov	ax,cs
	mov	ds:13Ch+2,ax
	mov	word ptr GETVEC+2,ax

; Sauve puis modifie les vecteurs DOS.

	mov	al,24h
	mov	di,offset DINTAD
	call	SAVVEC
	dec	ax
	mov	di,offset CTLCAD
	call	SAVVEC
	dec	ax
	mov	di,164h
	call	SAVVEC

	push	cs
	pop	es
	mov	dx,offset Terminaison
	call	SETVEC
	inc	ax
	mov	dx,offset MSCTLC
	call	SETVEC
	inc	ax
	mov	dx,offset DSKERR
	call	SETVEC

	pop	es

; Actualise les vecteurs du PSP (DOS3+ après ABORT).

Retour:
	mov	dx,cs
	mov	ah,26h
	int	21h
	ret
MSISET	endp

MSIRST	proc
; Restaure les vecteurs DOS et la configuration initiale du système.
	mov	al,24h
	les	dx,DINTAD
	call	SETVEC
	dec	ax
	les	dx,CTLCAD
	call	SETVEC
	dec	ax
	les	dx,ds:164h
	call	SETVEC

; Restaure le PSP parent (DOS2+ après ABORT).

	lds	ax,ds:168h
		assume	ds:CSEG
	mov	PSP_Parent,ax

; Remet BREAK sur OFF si état original (DOS2+).

	 jmp	short Retour		; JMPS $+2 le cas échéant
Bond	equ	$-1
	xor	dl,dl
	mov	ax,3301h
	int	21h
	 jmp	short Retour
MSIRST	endp
		assume	ds:DSEG

SAVVEC	proc
; Sauve un vecteur.
	call	dword ptr GETVEC
	mov	[di],bx
	mov	[di+Mot],es
Mot	equ	2
	ret
SAVVEC	endp

; L'une des deux routines suivantes sera accessible par l'appel GETVEC.
; Entrée :	AL	numéro d'interruption
; Sortie :	ES:BX	vecteur correspondant

I8086_vec	proc	far
; Obtient un vecteur (DOS1).
	xor	bx,bx
	mov	es,bx
	mov	bl,al
	shl	bx,1
	shl	bx,1
	les	bx,es:[bx]
	ret
I8086_vec	endp

MSDOS_vec	proc	far
; Obtient un vecteur (DOS2+).
	mov	ah,35h
	int	21h
	ret
MSDOS_vec	endp

SETVEC	proc
; Restaure un vecteur.
	push	ds
	push	es
	pop	ds
	mov	ah,25h
	int	21h
	pop	ds
	ret
SETVEC	endp

MSCTLC	proc	far
; Routine INT23h (Ctrl-C).
	mov	byte ptr MSDCCF,3	; ^C
	iret
MSCTLC	endp

Seg_DS	dw	 ?

DSKERR	proc	far
; Routine INT24h (erreur critique) avec ABORT.
; L'adresse de retour au programme pointe la routine INT22h puisque DR DOS
; convertit ABORT en FAIL lorsque le PSP actif est son propre parent.
	mov	bp,sp
	push	ds

	mov	ds,Seg_DS
	mov	ds:16Eh,di
	mov	word ptr [bp+24],offset Terminaison
	mov	[bp+26],cs
	lea	ax,[bp+30]
	mov	ds:16Ch,ax

	pop	ds
	mov	al,2
	iret
DSKERR	endp

; Retour INT22h (fin de l'erreur critique).
; Restaure les registres de segment et de pile après l'abandon avorté.

Terminaison:
	cli
	mov	ax,Seg_DS
	mov	ds,ax
	mov	es,ax
	mov	ss,ax	
	mov	sp,ds:16Ch		; Suppose que SS=Seg_DS dans Int24... 
	sti

; Convertit l'erreur critique en numéro d'erreur BASIC.

	mov	dl,ERRDIO		; Anciennement "Disk I/O Error" (DOS1)
	mov	ax,ds:16Eh		; AL={0; 1;2; 3;4; 5;6; 7;8; 9;A;...}h
	ror	al,1			;   ={0;80;1;81;2;82;3;83;4;84;5;...}h
	cmp	al,4
	 ja	DSKERX
	 jp	Conversion		;   ={ 0; 1; 2; 3; 4}
	inc	ax
Conversion:				;   ={ 0; 2; 3; 3; 4}
	shr	al,1			; AL={ 0; 1; 1; 1; 2}
	adc	al,ERRDWP		; CF={ 0; 0; 1; 1; 0} avant l'addition
	xchg	dx,ax			; DL={70;71;72;72;72}
DSKERX:
	 jmp	ERROR			; Err.critique	DL=ERR (erreur BASIC)
if offset $-4642h			; -------------------------------------
if2					; 0		70=Disk write protected
%out Mauvaise taille (DOS) !		; 2		71=Disk not Ready
end					; 4 (bad CRC)	72=Disk media error
endif					; 6 (seek err)	idem
endif					; autre		57=Device I/O Error
					
;-------------------------------------------------------------------------------

	org	6E3Ah
	call	MSIRST			; Restaure l'état d'origine du DOS
if offset $-6E3Dh			; avant que le BASIC ne se termine
if2
%out Mauvaise taille (RET) !
end
endif
endif

;-------------------------------------------------------------------------------

; Initialisation du BASIC Microsoft (portage MS-DOS de la version 5 pour CP/M).
; Les corrections du lancement de MS-BASIC portent sur :
; - une procédure incompatible avec les vecteurs DOS3 et DR DOS (proc MSISET) ;
; - un tampon de ligne plein en cas de suite initiale de blancs (byte BUFMIN) ;
; - un tampon d'édition (^A) aléatoire avant la première entrée (byte BUF+0) ;
; - un offset non nul activant l'interception d'erreur du BASIC (word ONELIN).

	SWTCHR	equ	57o			; '/'
; Base page.
	CPMWRM	equ	0
	CPMENT	equ	CPMWRM+5
	TBUFF	equ	CPMWRM+128
; Saut initial.
		org	7A24h
		mov	dx,cs
		add	dx,(LASTWR-CSEG+15)/16
		mov	ah,26h
		int	21h
		mov	ds,dx

	assume	cs:nothing,es:CSEG
	mov	ax,es
	mov	ds:16Ah,ax
	xchg	ax,PSP_Parent
	mov	ds:168h,ax
	assume	cs:CSEG,es:nothing

		mov	es,dx
		cli
		mov	ss,dx
		mov	bx,offset BUF+128
		mov	MEMSIZ,bx
		mov	sp,bx
		sti
		xor	al,al
		mov	PROFLG,al
		mov	ch,offset CNSLEN+3
		mov	bx,offset RAMLOW
		mov	dx,offset CONSTR
MORMOV:		mov	si,dx
		lods	byte ptr cs:[si]
		mov	[bx],al
		inc	bx
		inc	dx
		dec	ch
		jnz	MORMOV
		call	KYBINI
		CALL	MSISET			; Configure selon la version DOS
		mov	bx,MEMSIZ
		mov	dh,bh
		mov	dl,bl
		mov	TOPMEM,bx
		mov	bx,offset KBUF-1
		mov	byte ptr [bx],':'
		call	STKINI
		mov	TTYPOS,al
		mov	bx,34*256+33+0
		mov	word ptr CPMREA,bx
		xor	al,al
		mov	CNTOFL,al
		mov	ENDBUF,al
		mov	DSEGZ,al
		mov	CHNFLG,al
		mov	MRGFLG,al
		mov	ERRFLG,al
	mov	bx,0				;GET 0 (dixit INIT.MAC)
	mov	word ptr BUFMIN,bx		; Fin de ligne, tampon d'édition
	mov	word ptr ONELIN,bx		; ON ERROR GOTO 0
		mov	bx,0+128
		mov	MAXREC,bx
		mov	bx,offset TEMPST
		mov	TEMPPT,bx
		mov	bx,offset PRMSTK
		mov	PRMPRV,bx
		mov	bx,ds:CPMENT+1
		mov	MEMSIZ,bx
		mov	al,3
		mov	MAXFIL,al
		mov	bx,offset DSEGZ
		mov	TEMP8,bx
		mov	bx,TBUFF
		mov	al,[bx]
		or	al,al
		mov	TEMP8,bx
		jnz	$+5
		jmp	DONCMD
		mov	ch,[bx]
		inc	bx
TBFLP:		mov	al,[bx]
		dec	bx
		mov	[bx],al
		inc	bx
		inc	bx
		dec	ch
		jnz	TBFLP
		dec	bx
ENDCMD:		mov	byte ptr [bx],0		; Non publique !
		mov	TEMP8,bx
		mov	bx,offset TBUFF-1
		call	CHRGTR
		or	al,al
		jnz	$+5
		db	0E9h
		dw	DONCMD-$-2
		cmp	al,offset SWTCHR
		jz	FNDSLH
		dec	bx
		mov	byte ptr [bx],34	; '"'
		mov	TEMP8,bx
		inc	bx
ISSLH:		cmp	al,offset SWTCHR
		jz	FNDSLH
		call	CHRGTR
		or	al,al
		jnz	ISSLH
		db	0E9h
		dw	DONCMD-$-2
FNDSLH:		mov	byte ptr [bx],0
		call	CHRGTR
SCANSW:		call	MAKUPL
		cmp	al,'S'
		jz	WASS
		cmp	al,'M'
		pushf
		jz	WASM
		cmp	al,'F'
		jz	$+5
		jmp	SNERR
WASM:		call	CHRGTR
		call	SYNCHR
		db	':'
		call	CNSGET
		popf
		jz	MEM
		mov	al,dh
		or	al,al
		jz	$+5
		jmp	FCERR
		mov	al,dl
		cmp	al,10h
		jb	$+5
		jmp	FCERR
		mov	MAXFIL,al
		jmp	short FOK
MEM:		mov	MEMSIZ,dx
FOK:		dec	bx
		call	CHRGTR
		jz	short DONCMD
		call	SYNCHR
		db	'/'
		jmp	short SCANSW
WASS:		call	CHRGTR
		call	SYNCHR
		db	':'
		call	CNSGET
		mov	MAXREC,dx
		jmp	short FOK

;-------------------------------------------------------------------------------

; Disk Initialization Routine.
; La rectification du calcul de la mémoire concerne : 
; - une pile redéfinie au-delà de la mémoire après un éventuel "Out of memory" ;
; - une réservation de mémoire annulée par un débordement (incrémentation à 0) ;
; - une pile trop petite permise au démarrage (GETSTK mal remplacé par REASON).

	DATPSC	equ	128			; Octets dans un secteur (128)
	FCBSIZ	equ	38			; File Control Block (37 octets)
; File-Data-Block.
	F_MODE	equ	0
	F_FCB	equ	1+F_MODE		; Mode={1:"I" ; 2:"O|A" ; 3:"R"}
	LOCOFS	equ	F_FCB+FCBSIZ		; CURLOC (secteur de 128 octets)
	ORNOFS	equ	2+LOCOFS		; Nb d'octets dans le tampon
	NMLOFS	equ	1+ORNOFS		; Nb d'octets restant
	DATOFS	equ	1+NMLOFS		; VARPTR(#) pointe ce tampon en
	DBLKSZ	equ	DATOFS+DATPSC		; accès séquentiel et non le FCB
	DBLK_C	equ	0+DBLKSZ
; Informations de la version 5.0 du BASIC.
	FD_MAX	equ	0
	FD_SIZ	equ	DBLKSZ			; Taille d'enregistrement
	FD_PHY	equ	2+FD_SIZ		; Enregistrement physique actuel
	FD_LOG	equ	2+FD_PHY		; Enregistrement logique courant
	FD_CHG	equ	2+FD_LOG		;Future flag for across block ?s
	FD_OPS	equ	1+FD_CHG		; Position (PRINT, INPUT, WRITE)
	FD_DAT	equ	2+FD_OPS		; Tampon FIELD que pointe VARPTR
	FNZBLK	equ	FD_DAT+FD_MAX		; en accès direct au lieu du FCB

DONCMD:	mov	Seg_DS,ds

		dec	bx
		mov	bx,MEMSIZ
		dec	bx
		mov	MEMSIZ,bx
		dec	bx
		push	bx
		mov	al,MAXFIL
		mov	bx,offset DSKDAT
		mov	FILPT1,bx
		mov	dx,offset FILPTR
		mov	MAXFIL,al
		inc	al
		MOV	CX,offset DBLK_C +1	; FDB interne (#0) +1
LOPFLB:		xchg	bx,dx
		mov	[bx],dx
		inc	bx
		inc	bx
		xchg	bx,dx
		add	bx,cx
		jae	$+5
		jmp	OMERRR
	dec	bx				; -1 : anticipe un débordement
		push	bx
		mov	bx,MAXREC
		MOV	CX,offset FNZBLK +1	; FDB utilisateur +1
		add	bx,cx
		mov	cx,bx
		pop	bx
		dec	al
		jnz	LOPFLB
		inc	bx
		mov	TXTTAB,bx
						; La pile ne change pas...
		pop	dx
		mov	al,dl
		and	al,0FEh
		mov	dl,al
		mov	al,dl
		sub	al,bl
		mov	bl,al
		mov	al,dh
		sbb	al,bh
		mov	bh,al
		jae	$+5
		jmp	OMERRR
		mov	cl,3
		shr	bx,cl
		mov	al,bh
		CMP	AL,1
	jz	SMLSTK				; Evite que LEVFRE ne déborde en
	mov	bx,0+256			; fin de segment (CLEAR,,BUFLEN)
		jb	SMLSTK

	inc	bh

SMLSTK:		mov	al,dl
		sub	al,bl
		mov	bl,al
		mov	al,dh
		sbb	al,bh
		mov	bh,al
						; Pas de débordement
		mov	MEMSIZ,bx
	mov	FRETOP,bx			; Requis par le prochain appel
		xchg	bx,dx
		mov	TOPMEM,bx
						;REASON n'a pas raison d'être...
		mov	sp,bx
		mov	SAVSTK,bx
		mov	bx,TXTTAB

	xor	al,al				; Aucun programme BASIC
	mov	[bx],al				; (OMERR appelle LINKER afin de
	inc	bx				; terminer une suppression de
	mov	[bx],al				; ligne dans cette version)
	inc	bx
	call	Appel				;... mais son second test si.
	xchg	bx,dx

		sub	bx,dx
						; Futur mot nul déjà ôté
		push	bx
		mov	bx,offset HEDING
		call	STROUT
		pop	bx
		call	LINPRT
		mov	bx,offset WORDS
		call	STROUT
		call	CRDO
		mov	SAVSEG,ds
		jmp	INITSA

AUTTXT:		db	13			; Non publique !
		db	10
		db	10
		db	'Owned by Microsoft'
		db	13
		db	10
		db	0
WORDS:		db	' Bytes free'
		db	0
HEDING:		db	'Microsoft BASIC Version 5.28'
		db	13
		db	10
		db	'[MS-DOS Version]'
		db	13
		db	10
		db	'Copyright 1977-1983 (C) by Microsoft'
		db	13
		db	10
		db	'Created: 24-May-83'
		db	13,10
		db	0
LASTWR:		db	-32000/256		; Numérologie ?

		org	8D8h
	SNERR	label	near
		org	103Eh
	CHRGTR	label	near
		org	117Ah
	FCERR	label	near
		org	1B4Fh
	MAKUPL	label	near
		org	1B5Ch
	CNSGET	label	near
		org	2E9Ch
	CRDO	label	near
		org	2F37h
	OMERRR	label	near
		org	2F53h
	Appel	label	near
		org	2FFCh
	STKINI	label	near
		org	3041h
	SYNCHR	label	near
		org	31B4h
	ISLET2	label	near
		org	44B3h
	KYBINI	label	near
		org	503Bh
	STROUT	label	near
		org	5849h
	LINPRT	label	near
		org	7A09h
	INITSA	label	near
CSEG		ends

DSEG		segment
	NAMLEN	equ	40
	BUFLEN	equ	255
	STRSIZ	equ	3
	NUMTMP	equ	10
	KBFLEN	equ	BUFLEN+(BUFLEN/4)
; Offsets.
	RAMLOW	equ	100h
		org	10Ch
	DSEGZ	equ	this byte		; 0
		org	132h
	MSDCCF	db	1 dup(?)
		db	1 dup(?)		; Fonction de sortie caractère
	CTLCAD	dd	1 dup(?)
	DINTAD	dd	1 dup(?)
		dd	1 dup(?)		; Procédure scrutant ^C & ^S
	GETVEC	dd	1 dup(?)
	ERRFLG	db	1 dup(?)
		org	159h
	CNTOFL	equ	this byte
		org	15Ch
	TOPMEM	dw	1 dup(?)
	CURLIN	dw	1 dup(?)
	TXTTAB	dw	1 dup(?)
		org	1A2h
	FILPT1	dw	1 dup(?)
	FILPTR	dw	16 dup(?)
		org	1C4h
	MAXFIL	db	1 dup(?)
	NAMCNT	db	1 dup(?)
	NAMBUF	db	NAMLEN-2 dup(?)
		org	231h
	CPMREA	db	1 dup(?)
	CPMWRI	db	1 dup(?)
		db	1 dup(?)		; ':'
	KBUF	db	KBFLEN dup(?)
	BUFMIN	db	1 dup(?)		; ','
	BUF	db	BUFLEN+1 dup(?)
		dw	1 dup(?)
	ENDBUF	db	1 dup(?)
	TTYPOS	db	1 dup(?)
		org	487h
	MEMSIZ	dw	1 dup(?)
	TEMPPT	dw	1 dup(?)
	TEMPST	db	STRSIZ*NUMTMP dup(?)
		org	4ACh
	FRETOP	equ	this word
		org	4B0h
	TEMP8	equ	this word
		org	4C3h
	SAVSTK	equ	this word
		org	4CBh
	ONELIN	equ	this word		; Offset du traitement d'erreur
		org	4D0h
	SAVSEG	equ	this word
		org	4FAh
	PRMSTK	equ	this word
		org	562h
	PRMPRV	equ	this word
		org	5E4h
	MAXREC	dw	1 dup(?)
	PROFLG	db	1 dup(?)
	MRGFLG	db	1 dup(?)
		org	5EDh
	CHNFLG	equ	this byte
		org	671h
	DSKDAT	equ	$
	CONSTR	equ	7E0h
	ENDCNS	equ	844h
	CNSLEN	equ	ENDCNS-CONSTR
DSEG		ends

		end

Dernière remontée par Big Monstro le 20 mai 2020 19:16.

_________________

C:\ONGRTLNS.W95


Haut
Profil
Afficher : Trier par : Ordre :
Répondre   Page 1 sur 1  [ 4 messages ]
Revenir à « Projets en cours »
Aller :