Win3x.Org

Windows & DOS Community

Apprentissage du BASIC Microsoft sous DOS

Post Reply   Page 1 of 1  [ 6 posts ]
Author Message
gm86
Post subject: Apprentissage du BASIC Microsoft sous DOS
Posted: 21 August, 14:04
Membre inscrit
User avatar
Offline
 
Posts: 676
Joined: 01 September, 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 :
Quote:
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


Top
Quote
Big Monstro
Post subject: Re: Apprentissage du BASIC Microsoft sous DOS
Posted: 21 August, 16:40
Administrateur
User avatar
Offline
 
Posts: 3390
Joined: 27 June, 15:15
Retro PC: 80486 DX2/66, MS-DOS & Windows
 
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


Top
Quote
gm86
Post subject: Re: Apprentissage du BASIC Microsoft sous DOS
Posted: 16 April, 12:46
Membre inscrit
User avatar
Offline
 
Posts: 676
Joined: 01 September, 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 :
  SPOILER Disabled
; 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 :
  SPOILER Disabled
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 :
  SPOILER Disabled
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 :
  SPOILER Disabled
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:
  SPOILER Disabled
        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 :
  SPOILER Disabled
	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.
(...)
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...
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])...

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.
(...)
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.
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.

Last edited by gm86 on 15 November, 19:21, edited 6 times in total.

_________________

C:\ONGRTLNS.W95


Top
Quote
gm86
Post subject: Re: Apprentissage du BASIC Microsoft sous DOS
Posted: 24 June, 10:25
Membre inscrit
User avatar
Offline
 
Posts: 676
Joined: 01 September, 19:07
 
Le message précédent, se rapportant essentiellement à la version MS-DOS de BASIC, approchait les 90.000 caractères. C'était un fourre-tout de ce que j'avais 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) :
  SPOILER Disabled
; 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.]

À 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.

Mercredi 7 février 2018

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 :
  SPOILER Disabled
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.

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 1A2Ah 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.
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

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). 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 (hull, équeuter une cerise) :
viewtopic.php?p=95496#p95496

Vendredi 6 septembre 2019

Ce sujet oublié est amené à être clos. 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 :
  SPOILER Disabled
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) :
http://github.com/microsoft/GW-BASIC/archive/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.

Mardi 6 octobre 2020

Le bug analysé le 6/5/2016 s'explique par une mauvaise utilisation de la macro INS86 (OEM.H), dont le but est d'insérer des opcodes 8086 dans le code 8080 à convertir : INS86 203,372,65535D.
INS86   MACRO   A,B,C,D
        DB      A&O
IFNB    <B>
        DB      B&O
ENDIF
IFNB    <D>
        DB      C&O
        DB      D&O
ENDIF
IFB     <D>
 IFNB   <C>
        DW      C
  ENDIF
 ENDIF
ENDM
En l'absence de paramètre D, C n'est plus un octet mais un mot. Le code source de GW-BASIC, GWEVAL.ASM donne cette solution :
	INS86	203,372		;CMPI DX,
	DB	377O		; 0FFFFH
Dans le code de MS-BASIC 5.28, j'utilise la forme longue de l'instruction (4 octets au lieu de 3) :
	INS86	201,372,65535D	;CMPI DX,0FFFFH
Mardi 20 octobre 2020

Je suis satisfait du patch peaufiné grâce au code source de GW-BASIC. D'ailleurs, on trouve dans ce dernier la correction de la fonction de partie entière--cf. plus haut, INT(-0#) ou FIX (-0!) donnant -1 dans la révision 5.27. Elle agit sur les routines $INT et $DINT dans la seconde partie du fichier mathématique (les commentaires ne sont pas en capitales) :
http://github.com/microsoft/GW-BASIC/bl ... /MATH2.ASM

Vendredi 6 novembre 2020

Microsoft avait reconnu le bug de RESUME NEXT avec le compilateur BASIC pour MS-DOS (Q21323, RESUME NEXT enchaînant sur ELSE) :
http://www.pcorner.com/list/OS2/BUG110.ZIP/INFO/
Cependant, les interpréteurs BASIC Microsoft ont tous un problème analogue. Si une erreur se produit juste après ELSE, RESUME NEXT enchaîne après l'instruction qui suit THEN (BLUE - BASIC Language User Essay, chapitre 9, p.142) :
http://read.pudn.com/downloads173/ebook/806233/BlueBas/BlueBas.pdf
Ouvrage qu'on retrouve ici, accompagné d'astuces et programmes :
http://www.antonis.de/qbebooks/index.htm#englische

Dimanche 15 novembre 2020

Un bug se trouve à l'étiquette SIN30 des routines trigonométriques du premier GW-BASIC :
http://github.com/microsoft/GW-BASIC/bl ... /MATH2.ASM
L'information vient du lien suivant :
http://www.os2museum.com/wp/well-hello/
On retrouve cette erreur dans BASIC-86 5.27 (offset 4C96h) alors qu'elle est absente de la version 5.28 (offset 4D80h).

Pour que la longueur de la partie "initialisation" puisse varier, voici depuis le 21 juin de nouveaux fichiers (dernière mise à jour le 14/8/2022).
  SPOILER Disabled
UPDATE.BAT (il y a un caractère 7 avant %msg% après :Erreur) :
@echo off
echo Patch de la gestion d'erreur critique du BASIC Microsoft sous DOS 3.
echo Le succès de la mise à jour requiert l'accès à ASM, LINK, DEBUG et EXE2BIN.
echo.
echo Pressez la combinaison Ctrl-C pour revenir à l'invite de commande.
pause

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

set f=%nom%
rem *****************
set msg=Désassemblage
set nom=BASIC86.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 :
e813 6
e81c 53 44
e1d47 81
e31fe 02 1
nVEC.TMP
l820
nNOM.TMP
l3da6
e4296 34
nDOS.TMP
l452c
nRET.TMP
l6e3a
e710f 1b
nINI.TMP
l7a24
a80
mov	di,7a24
add	di,cx
mov	cx,7f
rep	stosb
and	di,ff80
mov	cx,di
dec	ch

g=80 92
nMSBASIC.COM
w
q

PATCH.PAT :
a
sub cx,7a24

t
nINI.TMP
w7b24
nVEC.TMP
rcx
2
w920
nNOM.TMP
rcx
33
w3ea6
nDOS.TMP
rcx
116
w462c
nRET.TMP
rcx
3
w6f3a
q

PATCH.ASM :
		page	64,132			; Mars s'éloigne, mars approche
		title	Patch DOS 3 pour MS BASIC 5.28 (les 29/11/20 & 12/02/21)
CSEG		segment	public 'CODESG'		;CMP AX,2100 en DATE2--an>[20]77
		assume	cs:CSEG,ds:DSEG
		org	16h
PSP_Parent	equ	this word		; Mot non documenté (DOS2+)
		org	820h
		dw	offset GETVEC
;-------------------------------------------------------------------------------
; 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 maxi de caractères & non -1
		mov	dx,offset NAMBUF-1
VMORCH:		or	al,128
		DEC	CH			; Compte à rebours (dorénavant)
	jnz	$+5				; Contrôle placé dans la boucle
	jmp	SNERR				; "Syntax error" 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		; Moins initiale & 2nd caractère

	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
;-------------------------------------------------------------------------------
	ERRDIO	equ	57
	ERRDME	equ	72
		org	452Ch
MSISET	proc
; Adapte le DOS et configure ses vecteurs après sauvegarde.
; Si DOS2+, fait que BASIC :
; - enclenche BREAK et prévoit maintenant de revenir à son état actuel ;
; - ne scrute ^S et ^C qu'une fois sur cinq depuis la procédure CNTCCN ;
; - affiche avec la FCT2 multitâche (INT28h récurrente) ;
; - obtient un vecteur d'interruption via la fonction 35h.

	push	es
	mov	ah,48			; DOSIO 48D
	int	33
	cmp	al,2
	 jb	Adresses_lointaines
	mov	al,0
	mov	ah,51			; DOSIO 51D
	int	33
	neg	dl			; AL équivaut DL sous DOS Plus
	and	byte ptr cs:Bond,dl	; DL={-1;0}
	mov	dl,1
	mov	al,1
	mov	ah,51			; DOSIO 51D
	int	33
	mov	word ptr ds:13Ch,4434h	;Valeurs ROM : 4453h, 6, offset GETVEC
	mov	byte ptr ds:133h,2
	mov	word ptr ds:140h,offset Get_Vec
Adresses_lointaines:
	mov	ax,cs
	mov	ds:13Ch+2,ax
	mov	ds:140h+2,ax

; Sauve puis modifie les vecteurs DOS.

	mov	al,36
	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 le PSP (sinon plantage de DOS3+ et abandon de DOS Plus après ABORT).

Retour:	mov	dx,ds:16Ah		; Correctifs de la fonction chez DRI :
	mov	ah,38			; - PAT312 de DR DOS 6 (avant 1993) ; 
	int	33			; - limiter CS:6 sous DOS Plus à FFE0h.
	ret				; Voir word 9E62h de DOSPLUS.SYS (1.2) &
MSISET	endp				; byte 1B31h à 14h du DR DOS de Lineo...

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

; Restitue la valeur du PSP parent (DOS2+ après ABORT).

		assume	cs:nothing,es:CSEG
	les	ax,ds:168h
	mov	PSP_Parent,ax
		assume	cs:CSEG,es:nothing

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

	jmp	short Retour		; JMPS $+2 le cas échéant
Bond	equ	$-1
	mov	dl,0
	mov	al,1
	mov	ah,51			; DOSIO 51D
	int	33
	jmp	short Retour
MSIRST	endp

SAVVEC	proc
; Sauve un vecteur.
	call	dword ptr ds:140h
	mov	[di],bx
	mov	bx,es
	mov	Mot[di],bx
	ret
Mot	equ	2
SAVVEC	endp

; L'une des deux routines suivantes sera accessible par SAVVEC ou CALLF [140h].
; Entrée :	AL	numéro d'interruption
; Sortie :	ES:BX	vecteur correspondant

GETVEC	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
GETVEC	endp

Get_Vec	proc	far
; Obtient un vecteur (DOS2+).
	push	ax
	mov	ah,53			; DOSIO 53D
	int	33
	pop	ax
	ret
Get_Vec	endp

SETVEC	proc
; Affecte le vecteur ES:DX à l'interruption AL.
	push	ax			; Paranoïa pour le registre AX
	push	ds
	push	es
	pop	ds
	mov	ah,37			; DOSIO 37D
	int	33
	pop	ds
	pop	ax
	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 processus actif est son propre parent.
	mov	bp,sp
	push	ds
	mov	ax,Seg_DS
	mov	ds,ax
	mov	ds:16Eh,di
	mov	Ret_IP[bp],offset Terminaison
	mov	ax,cs
	mov	Ret_CS[bp],ax		; Au cas où CALL[S] ou USR serait fautif
	lea	ax,Ret_SP[bp]		; Nettoiera le haut de la pile au retour
	mov	ds:16Ch,ax
	pop	ds
	mov	al,2
	iret
Ret_IP	equ	24
Ret_CS	equ	26
Ret_SP	equ	30
DSKERR	endp

; Retour INT22h (fin de l'erreur critique).
; Restaure les registres de segment et de pile après l'abandon avorté, puis
; convertit (à présent) l'erreur critique en numéro d'erreur BASIC.

Terminaison:
	cli				; Pallie le bug SS/SP des premiers 8086
	mov	ax,Seg_DS
	mov	ds,ax
	mov	es,ax
	mov	ss,ax
	mov	sp,ds:16Ch		; Suppose que SS=DSEG dans DSKERR... 
	sti
	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; B;C}h
	ror	al,1			;   ={0;80;1;81;2;82;3;83;4;84;5;85;6}h
	cmp	al,4			; Ne traite pas les codes impairs (DOS2)
	 ja	DSKERX			; CF={ 1; 1; 1; 1; 0}
	adc	al,-4			; AL={-3;-2;-1; 0; 0}
	sbb	al,not ERRDME		;   ={70;71;72;72;72}
	xchg	dx,ax			; Erreur DOS	Erreur BASIC (ERR)
DSKERX:	jmp	ERROR			; 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					; NEXT86.ASM suit
;-------------------------------------------------------------------------------
	org	6E3Ah
	call	MSIRST			; Restaure l'état d'origine du DOS
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 incompatibilité des vecteurs DOS 3+, DR DOS et DOS Plus (proc MSISET) ;
; - un tampon de répertoire non vide (".") restant accessible (byte FILNAM+1) ;
; - 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 (CP/M).
	CPMWRM	equ	0			; Warm boot
	CPMENT	equ	CPMWRM+5		; BDOS call
	TBUFF	equ	CPMWRM+128		; DMA
; Branchement initial.
		assume	ds:CSEG
		org	7A24h
		mov	dx,cs			; INS86	214,312
		add	dx,DSEG			; INS86	201,302,DSEG
		mov	ah,38			; INS86	264,46
		int	33			; INS86	315,41
	mov	ax,ds				; INS86	214,330
	xchg	ax,PSP_Parent			; INS86	207,6,CPMWRM+22D
		mov	ds,dx			; INS86	216,332
		assume	ds:DSEG

		nop				; DW C car INS86 A,B,C,D sans D
		mov	ds:168h,ax		; INS86	220,243,Adresse
		MOV	DS:16Ah,ES		; INS86	214,6,Adresse+2

		mov	es,dx			; INS86	216,302
		cli
		mov	ss,dx			; INS86	216,322
		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		;MOVE ROM INITIALIZATION VALUES TO RAM
		lods	byte ptr cs:[si]	; INS86	213,362,56,254
		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		; CP/M 2.x
		mov	word ptr CPMREA,bx
		xor	al,al
						; CNTOLF mis à zéro par MORMOV
		mov	ENDBUF,al
						; Idem DSEGZ (ZEROB d'INIT.MAC)
		mov	CHNFLG,al
		mov	MRGFLG,al
						; Idem ERRFLG
	mov	FILNAM+1,al			; Nom de fichier suit n° lecteur
	mov	bx,0				;GET 0
	mov	word ptr BUFMIN,bx		; Fin de ligne, tampon d'édition
	mov	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,offset TBUFF
		mov	al,[bx]
		or	al,al
		mov	TEMP8,bx
		jnz	$+5
		jmp	DONCMD
		mov	ch,[bx]
TBFLP:		inc	bx			; Etiquette mieux ici qu'après
		mov	al,[bx]
		dec	bx
		mov	[bx],al
		inc	bx
						; Incrémentation à l'étiquette
		dec	ch
		jnz	TBFLP
						; Plus besoin de décrémenter
ENDCMD:		mov	byte ptr [bx],0		; Nom non publique
		mov	TEMP8,bx
		mov	bx,offset TBUFF-1
		call	CHRGTR
		or	al,al
		jnz	$+5
		db	0E9h			; JMP DONCMD
		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			; JMP DONCMD
		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,16
		jb	$+5
		jmp	FCERR
		mov	MAXFIL,al
		jmp	short FOK
MEM:		mov	MEMSIZ,dx
FOK:		dec	bx
		call	CHRGTR
		jz	DONCMD
		call	SYNCHR
		db	'/'
		jmp	short SCANSW
WASS:		call	CHRGTR
		call	SYNCHR
		db	':'
		call	CNSGET
	mov	al,dh				; INTID2 remplacerait CNSGET si
	and	al,177o				; MEMSIZ pointait en deçà du
	mov	dh,al				; sommet de la pile (cf. GETSTK)
		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 faussée par des débordements (itération LOPFLB) ;
; - une pile trop petite autorisée (appel REASON remplaçant GETSTK de l'Altair).

	DATPSC	equ	128			; Octets dans un secteur (128)
	FCBSIZ	equ	38			; File Control Block (37 octets)
; File-Data-Block.
	F_MODE	equ	0			; Mode={1:"I" ; 2:"O|A" ; 3:"R"}
	F_FCB	equ	1+F_MODE		; FCB normal (DOS)
	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		; FDB interne (fichier #0)
; 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
ERRCMD:
DONCMD:		MOV	CS:Seg_DS,DS		; INS86	56
						; INS86	214,36,DSKERR-2
		dec	bx
		mov	bx,MEMSIZ
		dec	bx
		mov	MEMSIZ,bx
		dec	bx			; STKTOP
		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
LOPFLB:		xchg	bx,dx
		mov	[bx],dx
		inc	bx
		inc	bx
		xchg	bx,dx
		add	bx,cx
		jae	$+5
		jmp	OMERRR
		push	bx
		mov	bx,MAXREC		; Entier signé positif
		mov	cx,offset FNZBLK
		add	bx,cx
		mov	cx,bx
		pop	bx
		dec	al
		jnz	LOPFLB
		inc	bx
		mov	TXTTAB,bx
						; Ne pas sauver la pile là !
	dec	bx				; Contre un débordement (TXTTAB
						; ayant alors une valeur nulle)
		pop	dx
		mov	al,dl
		and	al,254
		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			; INS86	323,353
		MOV	AL,1			; Pile [256;512[+3 & non [0;512]
		CMP	AL,BH			; Evite le débordement en LEVFRE
		JZ	SMLSTK			; si le segment est alloué--/M:0

	mov	bh,al				; Cas de l'entrée d'une ligne de
	sbb	al,al				; programme pleine avec numéro à
	mov	bl,al				; un chiffre non suivi de blanc,
SMLSTK:	inc	bx				; sinon 1/8ème ou 512 au maximum
	inc	bx				; L'apostrophe (REM) vaut triple
	inc	bx

		mov	al,dl
		sub	al,bl
		mov	bl,al
		mov	al,dh
		sbb	al,bh
		mov	bh,al
						; Pas de débordement ici
	mov	byte ptr [bx],0			; Fin de chaîne pour VAL$ (sic)
	dec	bx				;MOV BX,OFFSET ENDBUF-BUF évince
						;(2*NUMLEV)+20 en CLEART--BIMISC
		mov	MEMSIZ,bx
	dec	bx				; Soustrait 2 futurs octets nuls
	dec	bx				; compris entre TXTTAB et STREND
		xchg	bx,dx
		mov	TOPMEM,bx
						; Sans FRETOP car REASON n'a...
		mov	sp,bx
		mov	SAVSTK,bx
		mov	bx,TXTTAB
		xchg	bx,dx
		CALL	DCOMPR			; ... pas raison d'être.
	jae	$+5				; Risque de dépassement ici
	jmp	OMERRR				; Suite absente de GWINIT.ASM
		sub	bx,dx
						; 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		; INS86	214,36,SAVSEG
		jmp	INITSA
AUTTXT:		db	13			; Nom 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			; Fin de la version 86-DOS
LASTWR:		db	high -32000		; Comble le secteur MS-DOS
		org	7E0h
	CONSTR	equ	$
		org	844h
	ENDCNS	equ	$
	CNSLEN	equ	ENDCNS-CONSTR
		org	8D8h
	SNERR	label	near
		org	8F2h
	ERROR	label	near
		org	103Eh
	CHRGTR	label	near
		org	117Ah
	FCERR	label	near
		org	1B4Fh
	MAKUPL	label	near
		org	1B5Ch
	CNSGET	label	near			; Va à LINGET si nombre décimal
		org	2E9Ch
	CRDO	label	near
		org	2F37h
	OMERRR	label	near
		org	2FFCh
	STKINI	label	near
		org	303Eh
	DCOMPR	label	near			; Macro COMPAR (version RSTLES)
		org	3041h
	SYNCHR	label	near			; SYNCHK (RST 8080)
		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	public 'DATASG'
	NAMLEN	equ	40
	BUFLEN	equ	255
	STRSIZ	equ	3
	NUMTMP	equ	10
	KBFLEN	equ	BUFLEN+(BUFLEN/4)
		assume	cs:DSEG
		org	100h			; Fin du second PSP et de sa DTA
	RAMLOW	equ	$			; JMP 0:0
		org	10Ch
	DSEGZ	label	byte			; 0
		org	11Dh			;INS86 201,372,65535 en USRFN...
	USRTAB	dw	10 dup(?)		; 65535 (absence d'adresse USR)
	NULCNT	db	1 dup(?)		; 1 (pour NULL 0)
	MSDCCF	db	1 dup(?)		; 0 (en l'absence de ^S ou ^C)
		db	1 dup(?)		; Fonction de sortie caractère
	CTLCAD	dd	1 dup(?)
	DINTAD	dd	1 dup(?)
		dd	1 dup(?)		; CNTCCN se branche sur l'offset
		dd	1 dup(?)		; Adresse GetVector 8086 ou DOS2
		org	15Ch
	TOPMEM	dw	1 dup(?)		; TSTACK+100
	CURLIN	dw	1 dup(?)		; 65534 (quitter si faute)
	TXTTAB	dw	1 dup(?)		; TSTACK+1
		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	20Fh			;CMP AL,LOW OFFSET "Z"+1 AND 37O
	FILNAM	db	33 dup(?)		;en DSKNAM--lettre du lecteur...
	CPMVRN	db	1 dup(?)
	CPMREA	db	1 dup(?)
	CPMWRI	db	1 dup(?)
		db	1 dup(?)		; ':' pour "Redo from start"
	KBUF	db	KBFLEN dup(?)
	BUFMIN	db	1 dup(?)		; ',' qu'ajoute INPUT[$]
	BUF	db	BUFLEN+1 dup(?)
		db	2 dup(?)	;ALLOW FOR SINGLE QUOTE IN BIG LINE
	ENDBUF	db	1 dup(?)
	TTYPOS	db	1 dup(?)
		org	487h
	MEMSIZ	dw	1 dup(?)
	TEMPPT	dw	1 dup(?)
	TEMPST	db	STRSIZ*NUMTMP dup(?)
		org	4B0h
	TEMP8	label	word
		org	4C3h
	SAVSTK	label	word
		org	4CBh
	ONELIN	label	word			; Adresse du traitement d'erreur
		org	4D0h
	SAVSEG	label	word
		org	4FAh
	PRMSTK	label	word
		org	562h
	PRMPRV	label	word
		org	5E4h
	MAXREC	dw	1 dup(?)
	PROFLG	db	1 dup(?)
	MRGFLG	db	1 dup(?)
		org	5EDh
	CHNFLG	label	byte
		org	671h
	DSKDAT	equ	$			; TSTACK
DSEG		ends

		end

Vendredi 11 décembre 2020

Aparté sur le compilateur 5.36 : les exécutables non autonomes produits par le compilateur nécessitent BASRUN.EXE (run-time). Ils sont plus petits mais il y a un risque que ce dernier soit directement exécuté par un curieux. D'autres versions afficheraient un message avant de quitter, celle-ci planterait aussitôt. Pour éviter cela, on peut rendre BASRUN.EXE fictivement gourmand en mémoire par l'intermédiaire d'une astuce rencontrée dans les exécutables au format NE :
http://www.os2museum.com/wp/multitaskin ... ent-117516
Il s'agit de rendre la valeur MINALLOC de l'en-tête MZ prohibitive (nombre minimal de paragraphes requis impossible à satisfaire). Cela est sans conséquence pour le programme BASIC nécessitant son chargement. En outre, la somme de contrôle est déjà incorrecte (mot 7C22h à l'offset 12h au lieu de 7C20h).

Le lendemain

L'astuce ne fonctionne qu'à partir de DOS 2. Avec DOS 1, le code chargeant les exécutables se trouve dans COMMAND.COM comme sous PC-DOS 2. Mais l'en-tête considéré est très simplifié (entre crochets ce qui est ignoré) :
00h		[MZ]
02h		[Longueur du fichier modulo 512]
04h PAGES	Taille du fichier en nombre de pages
06h RELCNT	Nombre de relogements
08h HEADSIZ	Taille de l'en-tête en paragraphes
0Ah		[MIN-ALLOC]
0Ch LOADLOW	[MAX-ALLOC] -1 sinon chargement HIGH
0Eh INITSS	Segment de pile par rapport au programme
10h INITSP	SP à l'entrée
12h		[Complément à un du checksum 16 bits]
14h INITIP	IP à l'entrée
16h INITCS	Segment de code par rapport au PSP
18h RELTAB	Offset du premier relogement
Les programmes BASIC se fichant du bit de poids fort de la taille rapportée sur 24 bits par l'en-tête, autant ajouter 2048 PAGES à PSIZE (cf. COMMAND.ASM) soit un méga-octet fictif pour le fichier RUN-TIME !

Le surlendemain

Patch contre l'exécution en invite de commande de BASRUN.EXE :
RENAME BASRUN.EXE *.BAK
COPY BASRUN.BAK *.BIN
DEBUG BASRUN.BIN
e105 8
e10A FF FF
w
q
RENAME BASRUN.BIN *.EXE
DOS 2+ signalera Programme trop grand pour entrer en mémoire (Program too big to fit in memory) à chaque tentative de lancement. COMMAND.COM émettra quant à lui Error in EXE file sous DOS 1.

Pas de souci pour tout programme BASIC nécessitant ce fichier RUN-TIME. Preuve à l'appui avec l'utilitaire SID de DR DOS :
  SPOILER Disabled
  Start      End
17E3:0000 17E3:077F
#T
           AX   BX   CX   DX   SP   BP   SI   DI   IP
--I------ 0000 0000 0000 0000 0200 0000 0000 0000 001A CALLF  1819:0000
*1819:0000
#G,8A
*1819:008A
#T#26
           AX   BX   CX   DX   SP   BP   SI   DI   IP
--I------ 2700 0000 001A 0C1A 01FA 0000 01FD 224F 008A MOV    AX,[0C08] =0020
--I------ 0020 0000 001A 0C1A 01FA 0000 01FD 224F 008D ADD    AX,001F
--I----P- 003F 0000 001A 0C1A 01FA 0000 01FD 224F 0090 AND    AX,FFE0
--I------ 0020 0000 001A 0C1A 01FA 0000 01FD 224F 0093 MOV    [0C3B],AX =001A
--I------ 0020 0000 001A 0C1A 01FA 0000 01FD 224F 0096 MOV    WORD [0C28],0010 =0001
--I------ 0020 0000 001A 0C1A 01FA 0000 01FD 224F 009C MOV    DX,[0C04] =082A
--I------ 0020 0000 001A 082A 01FA 0000 01FD 224F 00A0 DEC    DX
--I------ 0020 0000 001A 0829 01FA 0000 01FD 224F 00A1 MOV    CL,05
--I------ 0020 0000 0005 0829 01FA 0000 01FD 224F 00A3 SHL    DX,CL
--I-----C 0020 0000 0005 0520 01FA 0000 01FD 224F 00A5 SUB    DX,AX
--I----P- 0020 0000 0005 0500 01FA 0000 01FD 224F 00A7 MOV    AX,[0C02] =0168
--I----P- 0168 0000 0005 0500 01FA 0000 01FD 224F 00AA ADD    AX,000F
--I---AP- 0177 0000 0005 0500 01FA 0000 01FD 224F 00AD MOV    CL,04
--I---AP- 0177 0000 0004 0500 01FA 0000 01FD 224F 00AF SHR    AX,CL
--I---AP- 0017 0000 0004 0500 01FA 0000 01FD 224F 00B1 ADD    DX,AX
--I----P- 0017 0000 0004 0517 01FA 0000 01FD 224F 00B3 MOV    [0C04],DX =082A
--I----P- 0017 0000 0004 0517 01FA 0000 01FD 224F 00B7 SUB    DI,DX
--I------ 0017 0000 0004 0517 01FA 0000 01FD 1D38 00B9 SUB    DI,0014
--I----P- 0017 0000 0004 0517 01FA 0000 01FD 1D24 00BC MOV    SI,SS
--I----P- 0017 0000 0004 0517 01FA 0000 1911 1D24 00BE MOV    BX,SP
--I----P- 0017 01FA 0004 0517 01FA 0000 1911 1D24 00C0 ADD    BX,0011
--I------ 0017 020B 0004 0517 01FA 0000 1911 1D24 00C3 MOV    CL,04
--I------ 0017 020B 0004 0517 01FA 0000 1911 1D24 00C5 SHR    BX,CL
--I-----C 0017 0020 0004 0517 01FA 0000 1911 1D24 00C7 ADD    SI,BX
--I------ 0017 0020 0004 0517 01FA 0000 1931 1D24 00C9 CMP    DI,SI
--I----P- 0017 0020 0004 0517 01FA 0000 1931 1D24 00CB JNB    00D0
*1819:00D0
#q
Le décalage de cinq bits vers la gauche transforme les pages de 512 octets en paragraphes de 16 octets. De plus, le bit 7 disparaît dans le drapeau de retenue : 82Ah équivaut à 2Ah. Le mot en [C02h] est considéré comme le nombre d'octets de la dernière page. Or c'est aussi un modulo par 512, ce qui signifie qu'une valeur nulle indique une page pleine. La décrémentation du mot [C04h] serait alors une erreur. D'ailleurs, ce premier mot était inconnu des premières versions de MS-LINK et valait quatre :
http://www.delorie.com/djgpp/doc/rbinter/it/94/15.html

Mercredi 16 décembre 2020

Une réédition d'un bon ouvrage sur la compilation BASIC (plus les fichiers de la disquette du livre original) :
http://ethanwiner.com/BTU_BOOK.PDF
http://ethanwiner.com/BTU_DISK.ZIP
Sont relatées les optimisations, l'arithmétique pas forcément signée (sauf option /d du compilateur), etc.

Samedi 2 janvier 2021

Le compilateur BASCOM peut planter à cause de sa programmation non structurée. Lorsqu'il rencontre une instruction FOR, il commence la vérification par la dernière limite puis le pas (BC.EXE de QuickBASIC effectuerait l'opération en même temps). Supposant que l'indice soit entier, une première limite non comprise dans [-32768,32767] alors que le restant est correcte provoquera un message pointant la fin de ligne :
 002B   0002    10 FOR I%=32768 TO 0 STEP -2
                                            ^ OV
Si la dernière limite est en cause avec une valeur positive, le message OV (débordement) pointera avant l'option STEP (pas). En revanche, l'affichage de l'erreur /0 (division par zéro) sera elle-même une faute :
 002B   0002    10 FOR I%=0 TO 32768 STEP 2
                                     ^ OV
                                     ^ /0
Le code en cause est une version légèrement adaptée d'une routine mathématique de l'interpréteur. Le label OVERFLOW pointe un appel d'erreur alors que le traitement d'erreur est lui-même inactif :
:2833 CALL   28FF
:2836 DB     09
:2837 CALL   28FF
:283A DB     0A
:283B MOV    AL,[152E]
:283E CMP    AL,08
:2840 JNB    2848
:2842 SUB    AL,03
:2844 OR     AL,AL
:2846 STC
:2847 RET
:2848 SUB    AL,03
:284A OR     AL,AL
:284C RET
...
:65FC JS     661A
:65FE POP    CX
:65FF POP    AX
:6600 MOV    [151F],BX
:6604 RET
:6605 CMP    BX,8000
:6609 JNZ    661A
:660B OR     AL,AL
:660D JZ     65FE
:660F TEST   BYTE [1A00],20
:6614 JNZ    661A
:6616 TEST   AL,80
:6618 JZ     65FE
:661A JMP    2833
:661D OR     AH,AH
:661F JS     65FE
:6621 JMPS   661A
CALL 28FF retourne en CS:2837h vu que le pointeur en DS:29AAh est nul. S'ensuit l'erreur /0 puis un retour aléatoire. En effet, les registres AX et CX sont sur la pile lors de l'entrée en CINT et ne sont dépilés qu'en CXRET, c'est-à-dire, en l'absence d'erreur.

Moins grave est la mauvaise conversion des tabulations en espaces lors du listing. La logique lors de la rencontre d'une tabulation est (position+7)/8 :
:26E1 CMP    AL,09
:26E3 JNZ    26F7
:26E5 MOV    AL,07
:26E7 ADD    AL,[BX]
:26E9 AND    AL,F8
:26EB CALL   27BE
:26EE INC    BYTE [BX]
:26F0 POP    BX
:26F1 POP    AX
:26F2 MOV    AL,20
:26F4 PUSH   AX
:26F5 JMP    BX
:26F7 ...
AL=8 à la place de AL=7 était l'erreur.

Surlendemain du nouvel an

Le compilateur 5.36 pouvait être livré pour l'ACT Apricot. On reconnaît le disque à la présence de fichiers GSX*.* et au fait que BASRUN.EXE n'est plus de l'année 1983 (version 5.35).
La vieille version de LINK incluse peut créer des EXE défectueux. Prenons les programmes de test des patches 3 et 4 pour IBM 1.0 et compilons-les avec l'option /O (autonome) :
http://ftpmirror.your.org/pub/misc/dos/ ... ASCOM1.TXT
Après liage, analysons le nombre d'octets de la dernière page dans l'en-tête EXE : 146h au lieu de 140h pour le test 4, mais surtout 6 au lieu de 0 pour le test 3. Or 0 équivalant à 512 octets, le nombre de pages indiqué a dû être incrémenté. D'où le message du DOS qui constate que la dernière page n'existe pas : Erreur dans le fichier EXE (Error in EXE file)).

La solution est d'utiliser la version 2.45 pour DOS 1 (LINK.V1 du Pascal Microsoft 3.2).

Mardi 5 janvier 2021

Le chargeur EXE de BASIC 5.36 est défectueux (cf. déroulement de SID plus haut). Il ne sait pas que zéro octet dans la dernière page est en fait un reste de division signifiant 512, c'est-à-dire, le modulo 512 de la taille du fichier exécutable. D'où la nécessité de modifier les deux bibliothèques fournies.

Les bibliothèques IBM 2.00 corrigeait le problème de façon plus classique (instruction CMP) :
:0116 8B16640B      MOV     DX,[0B64]
:011A 833E620B00    CMP     WORD PTR [0B62],+00
:011F 7401          JZ      0122
:0121 4A            DEC     DX
:0122 B105          MOV     CL,05
:0124 D3E2          SHL     DX,CL
:0126 2BD0          SUB     DX,AX
:0128 A1620B        MOV     AX,[0B62]
:012B 050F00        ADD     AX,000F
:012E B104          MOV     CL,04
:0130 D3E8          SHR     AX,CL
:0132 03D0          ADD     DX,AX
:0134 8916640B      MOV     [0B64],DX
Retenons notre formule qui fait fi du cas nul :
TailleEXE = Word[4]*512 - (-Word[2] AND 511) en octets.

Le surlendemain

Merci Big Monstro de la remontée. J'ai consacré du temps pour apporter de la stabilité à ces vieux logiciels Microsoft.

Après test, j'ai vu que le chargeur EXE de BASRUN.LIB ne sert qu'à charger BASRUN.EXE ! C'est ce dernier qui recopie l'exécutable appelé par CHAIN ou RUN. Donc, modification de cette run-time, calcul du nouveau et inutilisé checksum (imite LINK) puis modification décrite précédement ; BASRUN.EXE ne devant pas être lancé depuis l'invite de commande.

Dimanche 17 janvier 2021

Désirez-vous tester la démo GSX du compilateur 5.36 qui se trouve sur la disquette d'Apricot ?
http://actapricot.org/disks/aprid5ks.htm#apr00036.dsk
Il faudra se munir des gestionnaires GSX accompagnés des sources pour DOS, de leur documentation, des outils de DRI pour assembler ces derniers et d'un assembleur Microsoft :
http://www.seasip.info/Cpm/software/gsx86.html
http://www.bitsavers.org/pdf/digitalRes ... _Jul83.pdf
http://www.retroarchive.org/cpm/lang/TOOLS86.ZIP
http://actapricot.org/disks/aprid5ks.htm#apr00191.dsk

Une précision sur les routines assembleur. Il reste une alternative à CALL : la fonction USR. Contrairement à l'interpréteur, son argument est ignoré et son résultat un entier. De ce fait, la valeur renvoyée l'est directement par le registre BX qui contient au départ l'adresse de la routine. De plus, si on insère USRx(arg) dans une formule dont les opérandes viennent d'être utilisés, le compilateur aura gardé les valeurs dans DX, CX puis AX avant d'user de la pile via BP. C'est pourquoi il est préférable que la routine sauve tous les registres sauf ES et, bien sûr, FLAGS.
Enfin, CALL de l'interpréteur devient CALL ABSOLUTE et, contrairement au compilateur IBM, l'emploi de BLOAD n'est plus possible.

Dimanche 31 janvier 2021

Le compilateur produit le même bug que l'interpréteur à propos de FILES : la lettre Z n'est pas reconnue (cf. note du 2 février 2018). BASRUN.EXE et BASCOM.LIB sont à modifier en conséquence.
  SPOILER Disabled
RENAME BASRUN.EXE *.BAK
COPY BASRUN.BAK *.BIN
DEBUG BASRUN.BIN
a155F
MOV     CL,05
SHL     DX,CL
dec     cx
SUB     DX,AX
MOV     AX,[0972]
neg     ax
and     ax,01FF
SHR     AX,CL
sub     dx,ax

e112 0 0
e22F6 1B
a80
push cx
mov si,100
add si,cx
mov [si],bh
sub si,cx
inc cx
shr cx,1
lodsw
add dx,ax
loop 8D
not dx
mov [112],dx
pop cx
ret

g=80 99
e105 8
e10A FF FF
w
q

RENAME BASRUN.BIN *.EXE
On retrouve le calcul de la somme de contrôle dans ce sujet :
viewtopic.php?f=2&t=6868

Vendredi 12 février 2021

Il faut penser au futur. MSBASIC accepte les années 78 à 99 (XXème siècle) et 00 à 77 (XXIème siècle). La plupart des machines refusant 1978 et 1979, la fonction DOS correspondante renverra une erreur.

À l'instar de GW-BASIC, le compilateur accepte aussi les années 2078 à 2099. Il suffit de le vérifier au label DATE2 du fichier GWSTS.ASM :
	CMP	AX,1978D
	JNB	DATE2		;branch if .GE. 1978
	CMP	AX,100D
	JNB	DATERR		;error if between 100 and 1977
	CMP	AX,78D
	JNB	DATE1		;add 1900 if .GE. 78
	ADD	AX,100D		;add 2000 if .LE. 77
DATE1:	ADD	AX,1900D
DATE2:
	CMP	AX,2100D
	JNB	DATERR		;branch if year too large
	MOV	CX,AX		;CX=year
	POP	BX		;Text pointer
	RET			;Exit.
http://github.com/microsoft/GW-BASIC/bl ... /GWSTS.ASM
Or nous avons DATE2: CMP AX,2078D dans l'interpréteur pour MS-DOS. Nous devons donc modifier l'octet à l'offset 4296h dans le fichier PATCH (voir plus haut).

Samedi 20 février 2021

Le compilateur MS-DOS, à l'inverse de la version IBM 1.0, risque de planter lorsqu'il rencontre un fichier dit binaire alors qu'une disquette est présente dans le lecteur B:. En effet, sachant qu'il n'y a aucun enregistrement à écrire, il saute le changement de DTA et l'écriture d'enregistrements consécutifs. Or, c'est lors de cette dernière opération que le FCB à fermer est pointé. Vu que le guide Peter Norton explique qu'un nombre d'enregistrement nul est accepté, on raccourcit ce saut (JCXZ) pour corriger le problème.

De même, les caractères en ASCII étendu placés ailleurs que dans les chaînes posent problème. Alors que l'inspection des caractères ASCII 7 bits ne comporte qu'une entrée dans la version IBM 2.0 (CS:531Ch) pour deux appels différents, il existe une entrée pour la vérification d'initiale après le signe égal et une autre avant pour le message d'erreur mais qui annule le bit de poids fort. Cette dernière risque donc de planter l'affichage de ^SN voire du message ^UC qui est incorrecte. Si on inverse l'attribution des entrées, le souci disparaît.

Procédure de correction :
  SPOILER Disabled
RENAME BASCOM.COM *.BAK
COPY BASCOM.BAK *.COM
DEBUG BASCOM.COM
a1BE0
CALL	214D

a1EAA
CALL	214B

e26E6 7
a2833
CALL	28FF
DB	0A
CALL	28FF
DB	09

a5701
JCXZ	570B

a5AF7
JMP	2837

a5F82
JMP	2837

a5FF3
JMP	2837

a60CD
JMP	2833

a61F6
JMP	2837

a62AC
JMP	2837

a6374
JMP	2833

a65DD
JB	6618

a65FC
JS	6618

a6605
CMP	BX,8000
JNZ	6618
DB	0A,C0
JZ	65FE
JS	6618
TEST	BYTE PTR [1A00],20
JZ	65FE
POP	CX
POP	AX
JMP	2837
DB	0A,E4
JS	65FE
JMP	6618

a6672
JMP	2837

a669A
JMP	2833

w
q

Vendredi 16 avril 2021

Mise à jour du complément à un des sommes de contrôle des bibliothèques. Ici, il s'agit du dernier octet des enregistrements A0h qui ont été modifiés.
  SPOILER Disabled
RENAME BASCOM.LIB *.OLD
COPY BASCOM.OLD *.LIB
DEBUG BASCOM.LIB
a2273
MOV     CL,05
SHL     DX,CL
dec     cx
SUB     DX,AX
MOV     AX,[0000]
neg     ax
and     ax,01FF
SHR     AX,CL
sub     dx,ax

e7BC0 1B
rSP
100
a80
jmp 91
lodsb
xchg dx,ax
mov cx,[si]
inc cx
lodsb
add dl,al
loop 87
neg dl
mov [si],dl
ret
cld
push cx
mov si,21e3
call 82
mov si,787c
call 82
pop cx
jmp 0

g=80 a0
w
q

RENAME BASRUN.LIB *.OLD
COPY BASRUN.OLD *.LIB
DEBUG BASRUN.LIB
a4C0
MOV     CL,05
SHL     DX,CL
dec     cx
SUB     DX,AX
MOV     AX,[0000]
neg     ax
and     ax,01FF
SHR     AX,CL
sub     dx,ax

a80
cld
push cx
mov si,41a
lodsb
xchg dx,ax
mov cx,[si]
inc cx
lodsb
add dl,al
loop 8a
neg dl
mov [si],dl
pop cx
jmp 0

g=80 94
w
q

Notons que le pointeur vierge ne change pas de place dans le code, le fichier relogeable DOSRUN.OBJ intégré aux *.LIB contenant un fix-up d'adresse.

Lundi 27 septembre 2021

Les instructions PEEK, POKE, INP et OUT peuvent recevoir une valeur entière entre -32768 et 65535. Cependant, si le nombre a besoin d'être arrondi à 32768, une erreur de débordement se produit. Ce problème n'existe pas sous QuickBASIC 4.x et suivants (format IEEE et non MBF).

Lundi 31 janvier 2022 et dimanche 13 février

Quelques petits soucis du compilateur documentés par Microsoft :
http://www.pcorner.com/list/BASIC/MSKB. ... B-PDS.000/
Title: Large OBJ from a Specific Small Source File
Document Number: Q23752 Publ Date: 8-JUN-1988
Product Name: Microsoft BASIC Compiler
Product Version: 5.36
Operating System: MS-DOS

Summary:
The following short program generates a 53K OBJ file when compiled
with the /N switch. The resulting OBJ file gives an "Invalid Object
File Error" at link time in the linker (1.1 or 3.06). The program is
as follows:
defint x : defstr z
if code$=z then 2650
if code$<>z then gosub 6100
2650 for x=1 to 7 : next x
2680 on x5 goto 6100,6100,6100,6100,6100,6100,6100,6100,6100
on x4 goto 6100,6100,2680,2680,2680,2680,2680,2680
6100 rem
More Information:
To work around this problem, add one more line number to the end of
the "ON xx GOTO" statements, as follows:
2680 on x5 goto 6100,6100,6100,6100,6100,6100,6100,6100,6100,6100
on x4 goto 6100,6100,2680,2680,2680,2680,2680,2680,2680
You also can work around the problem by using the QuickBASIC
compiler. The problem does not occur with this product.
Je sais que /N pause parfois problème lors de la mise au point d'un programme. En l'absence de numéro de ligne, la première erreur OV ou /0 est signalée tout en affichant une ligne vide, puis l'erreur similaire suivante est ignorée. Sinon, c'est la ligne comportant le prochain numéro qui est affichée.

Autre rédaction en cours :
  SPOILER Disabled
BASIC Microsoft 5.28 mis à jour pour DOS 3


Sommaire
--------
1. Particularités du BASIC
2. Précaution d'emploi (sur compatible AT)
3. Soucis rencontrés
4. Mise à jour
5. Souci de la mise à jour (sous DOS Plus et DR DOS 6)


1. Particularités du BASIC
--------------------------
NULL est spécifique à la gestion de ligne dont la largeur est définie par WIDTH.
L'appel CALLS est particulier au BASIC-86 pour MS-DOS. De même que les fonctions
DATE et TIME. A l'opposé, l'instruction RETURN à numéro de ligne y est absente.
L'option "A" de 86-DOS et la syntaxe GW-BASIC de l'instruction OPEN sont admises
ainsi que BLOAD et BSAVE qu'on ne retrouve pas dans le compilateur pour MS-DOS.
De plus, ce dernier pointe le FCB via VARPTR(#) et non le tampon du fichier #,
ignore l'argument de la fonction USR qui ne renvoie plus qu'une valeur entière,
sans compter le fait que sa nature diffère forcément de celle de l'interpréteur.

Via la FAT, LOF rapporte une taille en octets et non en enregistrements (CP/M).
Néanmoins, les numéros d'enregistrements relatifs restent inscrits sur 15 bits.
En outre, la fin d'un fichier à accès direct se teste après une premier lecture.

Vu qu'on ne peut pas utiliser le tampon du fichier #0, l'usage du périphérique
NUL est une solution. Cela permet de passer par FIELD pour traiter toute chaîne
ainsi que la sortie de PRINT USING# après PUT#. Autres techniques pour éviter le
"garbage collection" : stocker les noms de fichier en nombres double-précision ;
réécrire une chaîne à longueur fixe en mettant à profit LSET & RSET. D'ailleurs,
l'instruction INSTR permet de traîter des initiales pour un menu ON GOTO/GOSUB.


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).

Pire, Windows Millennium ainsi que la FAT32 en mode réel ignorent les FCB.


3. Soucis rencontrés
--------------------
L'impossibilité qu'un nombre décimal suive PRINT ERL est écrite dans BINTRP.MAC.
RESUME NEXT considère que la commande fautive précédée d'ELSE suit THEN, tandis
que le compilateur MS-DOS ignore ELSE qui suit l'instruction fautive (Q21323).

Les dernières corrections oubliées du BASIC-86 (partie entière de -0#, par ex.)
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:).

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 cette dernière, elle devrait être plus longue qu'une ligne pleine
afin que le calcul d'une nouvelle ligne de programme ne déborde pas du segment.

Quant au contrôle de la longueur du nom d'une variable, il n'empêche pas ce nom
de déborder et d'écraser les numéros d'entrée/sortie en accès direct de MS-DOS.

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.

Contrairement à GW-BASIC et au compilateur, DATE$ se limite aux années comprises
entre 1978 et 2077 alors que l'intervale du DOS englobe 1980 à 2079 voire 2099.


4. Mise à jour
--------------
C'est UPDATE.BAT, en collaboration avec les outils MASM, LINK, EXE2BIN et DEBUG,
qui s'en charge. Il préserve la structure du code source (conversion 8080/8086).
L'interpréteur original est nommé pour l'occasion BASIC86.COM car moins courant.
L'interpréteur obtenu s'appellera 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
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 & CP/M), comme c'est la cas sous GW-BASIC et BASCOM.


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 1A2Ah 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

Quant à DOS Plus, au lieu d'écraser les vecteurs d'INT 30h et 31h par un saut
au DOS, appelle depuis l'offset 5 un code de plus d'un paragraphe en longueur.
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.
J'ai bien veillé à n'employer que le format [BP+imm16] dans le patch de l'interpréteur : voir note du dimanche 15 novembre 2020. Toutefois, je ne comprends toujours pas l'absence d'instructions seg,mem ou mem,seg dans le code 16 bits d'origine. Il faudrait que je trouve l'assembleur compatible MASM 1.06 capable d'assembler à l'identique les sources de GW-BASIC.

Mercredi 16 février 2022

Pour le petit programme précédent, la faute n'est pas due à l'option /N. Au moins, l'erreur semble visible par toutes les versions de LINK : vu l'absurdité du code objet ajouté, le contraire aurait été étonnant. Je me suis concocté une archive regroupant BASIC 5.28 & 5.36, ASM 2.00 & MASM 1.27, CREF 3.00 ainsi que leurs patches, LINK 2.30, 2.45 (DOS1), 3.04, LIB 2.20, DEBUG (ASCII 7 bits), EXE2BIN et SID sous sa forme décompressée.
  SPOILER Disabled
1              <REP>        16/02/22  20:14 1
2              <REP>        16/02/22  20:14 2
ASM            <REP>        16/02/22  20:14 ASM
BASCOM         <REP>        16/02/22  20:14 BASCOM
BASIC          <REP>        16/02/22  20:14 BASIC
REF            <REP>        16/02/22  20:14 REF
ASM      EXE        52 186  10/11/20  22:15 ASM.EXE
BASCOM   COM        39 936  20/02/21  13:17 BASCOM.COM
BASCOM   LIB        75 776  16/04/21  19:55 BASCOM.LIB
BASRUN   EXE        21 376  31/01/21   8:05 BASRUN.EXE
BASRUN   LIB         3 072  16/04/21  20:17 BASRUN.LIB
CREF     EXE        10 548  23/01/21  12:02 CREF.EXE
DEBUG    EXE        15 232  14/11/84   9:13 DEBUG.EXE
EXE2BIN  EXE         2 816  14/11/84   9:15 EXE2BIN.EXE
LIB      EXE        23 168  27/11/84   8:20 LIB.EXE
LINK     EXE        38 528  14/11/84   9:19 LINK.EXE
MASM     EXE        81 280  10/11/20  22:27 MASM.EXE
MSBASIC  COM        31 744  14/08/22  17:14 MSBASIC.COM
SID      EXE        26 112  27/03/92   5:00 SID.EXE
        13 fichier(s)            421 774 octets

\1

LINK     EXE        41 656  21/06/84  16:58 LINK.EXE
         1 fichier(s)             41 656 octets

\2

LINK     EXE        43 716  02/10/85  10:26 LINK.EXE
         1 fichier(s)             43 716 octets

\ASM

ASM      EXE        52 182  18/07/84  10:01 ASM.EXE
MACRO    BAT            56  26/11/20  18:53 MACRO.BAT
MACRO    TXT           570  27/11/20  11:15 MACRO.TXT
MASM     EXE        81 280  27/11/84   7:25 MASM.EXE
NOTES    TXT           844  13/02/22  16:36 NOTES.TXT
SMALL    BAT           115  26/11/20  18:50 SMALL.BAT
SMALL    TXT           370  27/11/20  11:43 SMALL.TXT
         7 fichier(s)            135 417 octets

\BASCOM

BASCOM   COM        39 936  24/04/84  11:00 BASCOM.COM
BASCOM   LIB        75 776  24/04/84  11:00 BASCOM.LIB
BASRUN   EXE        21 376  24/04/84  11:00 BASRUN.EXE
BASRUN   LIB         3 072  17/03/83  12:12 BASRUN.LIB
COMPILER BAT            76  05/01/21  20:55 COMPILER.BAT
COMPILER PAT           476  20/02/21  14:33 COMPILER.PAT
DOSCOM   BAT            74  16/04/21  19:47 DOSCOM.BAT
DOSCOM   PAT           324  16/04/21  20:10 DOSCOM.PAT
DOSRUN   BAT            74  16/04/21  20:15 DOSRUN.BAT
DOSRUN   PAT           256  16/04/21  20:30 DOSRUN.PAT
Q21323   TXT         1 719  13/02/22  16:16 Q21323.TXT
Q23752   TXT         1 017  13/02/22  16:20 Q23752.TXT
RUNTIME  BAT            97  05/01/21  20:55 RUNTIME.BAT
RUNTIME  PAT           306  31/01/21   8:03 RUNTIME.PAT
        14 fichier(s)            144 579 octets

\BASIC

BASIC86  COM        31 744  25/06/83  10:12 BASIC86.COM
PATCH                  260  13/08/22  12:02 PATCH
PATCH    ASM        16 384  14/08/22  17:22 PATCH.ASM
PATCH    PAT           143  13/08/22  11:48 PATCH.PAT
PATCH    TXT         7 154  20/02/22  18:40 PATCH.TXT
UPDATE   BAT         1 246  13/08/22  11:41 UPDATE.BAT
         6 fichier(s)             56 931 octets

\REF

CREF     EXE        10 544  21/11/84  13:51 CREF.EXE
XREF     BAT           120  23/01/21  10:53 XREF.BAT
XREF     TXT           322  23/01/21  12:02 XREF.TXT
         3 fichier(s)             10 986 octets

Nombre total de fichiers listés :
        46 fichier(s)            854 739 octets
Mardi 15 mars 2022

La fin de l'initialisation de l'interpréteur BASIC était la partie la plus foireuse. L'octet nul requis par la fonction VAL après MEMSIZ manquait. Il est bien présent dans GW-BASIC ou après l'instruction CLEAR. Je l'ai intégré après une vérification toute bête de la pile.
Taille de la pile :
		Pile mini	Pile par défaut
Interpréteur	259		514
Compilateur	384		512
Auparavant, l'interpréteur avait une pile de 512 octets par défaut en absence de l'octet zéro, ainsi qu'une pile minimale nulle alors que CLEAR n'acceptait pas moins de 2*NUMLEV+20 octets.

La difficulté majeure qu'avait rencontée Microsoft était que la version CP/M incorporait les tampons dans son code alors que DOS allouait un segment de données au contenu quelconque. La compatibilité de DSKERR restreinte à MS-DOS 1 & 2 était compréhensible. Par contre, les problèmes de mémoire et de pile résultaient du code mal mis à jour de l'Altair.

Mon fichier PATCH.ASM est le genre de modification qui aurait pu se retrouver dans une copie personnalisée d'un client de Microsoft.

Samedi 18 juin 2022

Je ne me suis jamais penché sur ce qui suit :
Title: OPENING "PRN" Invalid Error
Document Number: Q11736 Publ Date: 9-JUN-1988
Product Name: Microsoft BASIC Compiler
Product Version: 5.36
Operating System: MS-DOS

Problem:
When opening the file "PRN" under DOS 3.10, we get the error
message "DISK FULL". This does not occur with DOS 2.11. The error
message is not always printed when accessing PRN.
Response:
Microsoft is researching this problem and will post new information
as it becomes available.
Dimanche 14 août 2022

Libération de place dans MSISET en initialisant les valeurs DOS 1 en "ROM" (qui débute offset 7E0h). Le résultat est indépendant du format COM (pas de présomption sur CS).

_________________

C:\ONGRTLNS.W95


Top
Quote
jnf80
Post subject: Re: Apprentissage du BASIC Microsoft sous DOS
Posted: 28 August, 12:16
Membre inscrit
User avatar
Offline
 
Posts: 127
Joined: 20 August, 21:19
Retro PC: XT 8088, 486DX2 50, Pentium 75, P 100, P II, P III
 
Bonjour,
J'ai encore une disquette avec Gwbasic, que je pourrai essayer des que j'aurai reçu le lecteur 5.25 pouces ;)
D'ailleurs sur ce même format j'ai aussi Turbo Pascal et Cobol
A l'époque je passais mes soirées voir mes nuits à faire de la programmation, j'ai même eu un programme en basique qui es apparu dans dans un magasine qu'on s'échangeait entre copains qui listait des programmes en basiques pour divers machines. J'étais vert il est apparu sous un nom différent du mien lorsque je leur ai envoyé le même à 2 ou 3 lignes prets :evil:


Top
Quote
gm86
Post subject: Re: Apprentissage du BASIC Microsoft sous DOS
Posted: 24 September 2023 17:07
Membre inscrit
User avatar
Offline
 
Posts: 676
Joined: 01 September, 19:07
 
Je suis curieux de savoir si c'est une version de GW-BASIC spécifique à une machine.

Quant au BASIC pour DOS présenté dans ce sujet, il produit des binaires incompatibles avec GW-BASIC et même avec MBASIC sous CP/M. Par contre, son code source est très proche de celui de GW-BASIC. Comme en témoigne ce code issu de BIMISC.ASM :
  SPOILER Disabled
;OMERR fixes program links (starting at TXTTAB), resets SAVSTK to TOPMEM-2
; and issues an Out-of-Memory error message
;
OMERR:
	CALL	LINKER		;Fix links incase OMERR was called after
				;deleting a program line because user
				;was attempting to replace it.
				;ONLY IMPORTANT IN VERSIONS WHERE
				;STACK CONTEXT SURVIVES OTHER ERRORS
	MOV	BX,TOPMEM
				;ELIMINATE ALL STACK CONTEXT TO FREE
	DEC	BX		; UP SOME MEMORY SPACE
	DEC	BX		;MAKE SURE THE FNDFOR STOPPER IS SAVED
	MOV	SAVSTK,BX	;PLACE STACK IS RESTORED FROM
OMERRR:
	MOV	DX,OFFSET ERROM	;"OUT OF MEMORY"
	JMP	ERROR
	EXTRN	GARBA2:NEAR
DSEG	SEGMENT PUBLIC 'DATASG'
	EXTRN	CHNFLG:WORD
DSEG	ENDS
REASON:
	INS86	71,36,FRETOP	;CMP FRETOP,BX
	JNAE	SHORT $+3
	RET			;YES
	MOV	AL,BYTE PTR CHNFLG
	OR	AL,AL		;can't garbage collect if CHAINING
	JNZ	SHORT OMERR
	PUSH	CX		;SAVE ALL REGS
	PUSH	DX
	PUSH	BX
	CALL	GARBA2		;DO A GARBAGE COLLECTION
	POP	BX		;RESTORE ALL REGS
	POP	DX
	POP	CX
	INS86	71,36,FRETOP	;CMP FRETOP,BX
	JNAE	SHORT $+3
	RET			;YES
	JMP	SHORT OMERR	;NO, GIVE "OUT OF MEMORY BUT DONT TOUCH STACK
Cet ultime interpréteur MS-DOS comporte ce morceau, alors que le révision précédente et celles sous CP/M, 86-DOS et CP/M-86 respectaient le dernier commentaire. Ainsi, BIMISC.MAC emploie bien JMP OMERRR à la place de JMP OMERR :
  SPOILER Disabled
OMERR:

					;FOR SPACE REASONS LEAVE THIS CODE OUT

					;ONLY IMPORTANT IN VERSIONS WHERE
					;STACK CONTEXT SURVIVES OTHER ERRORS
	LHLD	TOPMEM
	DCX	H			;UP SOME MEMORY SPACE
	DCX	H			;MAKE SURE THE FNDFOR STOPPER IS SAVED
	SHLD	SAVSTK			;PLACE STACK IS RESTORED FROM
OMERRR:	LXI	D,0+ERROM		;"OUT OF MEMORY"
	JMP	ERROR
	EXTRN	GARBA2
REASON:	CALL	REALLY			;ENOUGH SPACE BETWEEN STRING & STACK
	RNC				;YES
	PUSH	B			;SAVE ALL REGS
	PUSH	D
	PUSH	H
	CALL	GARBA2			;DO A GARBAGE COLLECTION
	POP	H			;RESTORE ALL REGS
	POP	D
	POP	B
	CALL	REALLY			;ENOUGH SPACE THIS TIME?
	RNC				;YES
	JMP	OMERRR			;NO, GIVE "OUT OF MEMORY BUT DONT TOUCH STACK

Samedi 17 septembre 2022

L'en-tête de la version CP/M-86 de MBASIC demande la réservation de 100h paragraphes de données, tandis que COMMAND.COM réserve la fin de la mémoire conventionnelle à sa partie transitoire.
  SPOILER Disabled
;********************************************************************
;START OF TRANSIENT PORTION
;This code is loaded at the end of memory and may be overwritten by
;memory-intensive user programs.

TRANCODE        SEGMENT PARA
ASSUME  CS:TRANGROUP,DS:TRANGROUP,ES:TRANGROUP,SS:TRANGROUP

WSWITCH EQU     1               ;Wide display during DIR
PSWITCH EQU     2               ;Pause (or Page) mode during DIR
VSWITCH EQU     4               ;Verify during COPY
ASWITCH EQU     8               ;ASCII mode during COPY
BSWITCH EQU     10H             ;Binary mode during COPY

        ORG     0
ZERO    =       $

        ORG     100H            ;Allow for 100H parameter area
Le souci est qu'une pile initiale de seulement deux octets est requise entre le programme au format COM et cette zone temporaire.
Nouveau fichier PATCH.ASM :
  SPOILER Disabled
		page	64,132			; Mars s'éloigne, mars approche
		title	Patch DOS 3 pour MS BASIC 5.28 (les 29/11/20 & 12/02/21)
CSEG		segment	public 'CODESG'		;CMP AX,2100 en DATE2--an>[20]77
		assume	cs:CSEG,ds:DSEG
		org	16h
PSP_Parent	equ	this word		; Mot non documenté (DOS2+)
		org	820h
		dw	offset GETVEC		; On l'initialise enfin
;-------------------------------------------------------------------------------
; 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 maxi de caractères & non -1
		mov	dx,offset NAMBUF-1
VMORCH:		or	al,128
		DEC	CH			; Compte à rebours (dorénavant)
	jnz	$+5				; Contrôle placé dans la boucle
	jmp	SNERR				; "Syntax error" 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		; Moins initiale & 2nd caractère

	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
;-------------------------------------------------------------------------------
	ERRDIO	equ	57
	ERRDME	equ	72
		org	452Ch
MSISET	proc
; Adapte le DOS et configure ses vecteurs après sauvegarde.
; Si DOS2+, fait que BASIC :
; - enclenche BREAK et prévoit maintenant de revenir à son état actuel ;
; - ne scrute ^S et ^C qu'une fois sur cinq depuis la procédure CNTCCN ;
; - affiche avec la FCT2 multitâche (INT28h récurrente) ;
; - obtient un vecteur d'interruption via la fonction 35h.
	push	es
	mov	ah,48			; DOSIO 48D
	int	33
	cmp	al,2
	 jb	Loin
	mov	ax,51*256
	int	33
	neg	dl			; AL équivaut DL sous DOS Plus
	and	byte ptr cs:Retour+1,dl	; DL={-1;0}
	mov	word ptr ds:13Ch,4434h	;Valeurs ROM : 4453h, 6, offset GETVEC
	mov	byte ptr ds:133h,2
	mov	word ptr ds:140h,offset Get_Vec
Loin:	mov	ax,cs
	mov	ds:13Ch+2,ax
	mov	ds:140h+2,ax

; Sauve puis modifie les vecteurs DOS.

	mov	al,36
	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

; Actualise le PSP (sinon plantage de DOS3+ et abandon de DOS Plus après ABORT).
; DR DOS dirigerait PRN sur CON si la fonction 38 écrasait le PSP actif !

	mov	dl,1			; BREAK ON le cas échéant
Retour: jmp	short Bond		; JMP $+2 si BREAK OFF d'origine
	mov	ax,51*256+1
	int	33
Bond:	push	ds			; Sauve les vecteurs
	xor	ax,ax
	mov	ds,ax			; Table INT
	mov	cx,6
	mov	si,34*4
	mov	di,0Ah
	cld				; CS=PSP (format COM)
	rep	movsw			; Correctifs de la fonction chez DRI :
	pop	ds			; - PAT312 de DR DOS 6 (avant 1993) ;
	pop	es			; - limiter CS:6 sous DOS Plus à FFE0h.
	ret				; Voir word 9E62h de DOSPLUS.SYS (1.2) ;
MSISET	endp				; byte 1B31h du DR DOS de Lineo à 14h...

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

; Restitue la valeur du PSP parent (DOS2+ après ABORT).

	les	ax,ds:168h
	mov	es:PSP_Parent,ax

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

	mov	dl,0
	jmp	short Retour
MSIRST	endp

SAVVEC	proc
; Sauve un vecteur.
	call	dword ptr ds:140h
	mov	[di],bx
	mov	bx,es
	mov	Mot[di],bx
	ret
Mot	equ	2
SAVVEC	endp

; L'une des deux routines suivantes sera accessible par SAVVEC.
; Entrée :	AL	numéro d'interruption
; Sortie :	ES:BX	vecteur correspondant
GETVEC	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
GETVEC	endp

Get_Vec	proc	far
; Obtient un vecteur (DOS2+).
	mov	ah,53			; DOSIO 53D
	int	33
	ret
Get_Vec	endp

SETVEC	proc
; Affecte le vecteur ES:DX à l'interruption AL.
	push	ds
	push	es
	pop	ds
	mov	ah,37			; DOSIO 37D
	int	33
	pop	ds
	ret
SETVEC	endp

MSCTLC	proc	far
; Routine INT23h (Ctrl-C).
	sti				; Comme la révision 5.27
	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 processus actif est son propre parent.
	mov	bp,sp
	push	ds
	mov	ax,Seg_DS
	mov	ds,ax
	mov	ds:16Eh,di
	mov	Ret_IP[bp],offset Terminaison
	mov	ax,cs
	mov	Ret_CS[bp],ax		; Au cas où CALL[S] ou USR serait fautif
	lea	ax,Ret_SP[bp]		; Nettoiera le haut de la pile au retour
	mov	ds:16Ch,ax
	pop	ds
	mov	al,2
	iret
Ret_IP	equ	24
Ret_CS	equ	26
Ret_SP	equ	30
DSKERR	endp

; Retour INT22h (fin de l'erreur critique).
; Restaure les registres de segment et de pile après l'abandon avorté, puis
; convertit (à présent) l'erreur critique en numéro d'erreur BASIC.
Terminaison:
	cli				; Pallie le bug SS/SP des INTEL (C)1978
	mov	ax,Seg_DS
	mov	ds,ax
	mov	es,ax
	mov	ss,ax
	mov	sp,ds:16Ch		; Suppose que SS=DSEG dans DSKERR... 
	sti
	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; B;C}h
	ror	al,1			;   ={0;80;1;81;2;82;3;83;4;84;5;85;6}h
	cmp	al,4			; Ne traite pas les codes impairs (DOS2)
	 ja	DSKERX			; CF={ 1; 1; 1; 1; 0}
	adc	al,-4			; AL={-3;-2;-1; 0; 0}
	sbb	al,not ERRDME		;   ={70;71;72;72;72}
	xchg	dx,ax			; Erreur DOS	Erreur BASIC (ERR)
DSKERX:	jmp	ERROR			; 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					; NEXT86.ASM suit
;-------------------------------------------------------------------------------
	org	6E3Ah
	call	MSIRST			; Fin
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 pile COM restreinte par la partie transitoire de COMMAND.COM (new PSP) ;
; - une incompatibilité des vecteurs DOS 3+, DR DOS et DOS Plus (proc MSISET) ;
; - un tampon de répertoire non vide (".") restant accessible (byte FILNAM+1) ;
; - 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 (CP/M).
	CPMWRM	equ	0			; Warm boot
	CPMENT	equ	CPMWRM+5		; BDOS call
	TBUFF	equ	CPMWRM+128		; DMA
; Branchement initial.
		org	7A24h
		mov	dx,cs			; INS86	214,312
		add	dx,DSEG			; INS86	201,302,DSEG

	assume	ds:CSEG
	mov	ax,ds				; INS86	214,330
	xchg	ax,PSP_Parent			; INS86	207,6,CPMWRM+22D
	mov	ds,dx				; INS86	216,332
	assume	ds:DSEG

		cli
		mov	ss,dx			; INS86	216,322
		mov	bx,offset BUF+128
		mov	MEMSIZ,bx
		mov	sp,bx
		sti
	nop					; DW C car INS86 A,B,C,D sans D
	mov	ds:168h,ax			; INS86	220,243,Adresse
	mov	ds:16Ah,es			; INS86	214,6,Adresse+2
	mov	ah,38				; INS86	264,46
	int	33				; INS86	315,41
	mov	es,dx				; INS86	216,302
		xor	al,al
		mov	PROFLG,al
		mov	ch,offset CNSLEN+3
		mov	bx,offset RAMLOW
		mov	dx,offset CONSTR
MORMOV:		mov	si,dx		;MOVE ROM INITIALIZATION VALUES TO RAM
		lods	byte ptr cs:[si]	; INS86	213,362,56,254
		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		; CP/M 2.x
		mov	word ptr CPMREA,bx
		xor	al,al
						; CNTOLF mis à zéro par MORMOV
		mov	ENDBUF,al
						; Idem DSEGZ (ZEROB d'INIT.MAC)
		mov	CHNFLG,al
		mov	MRGFLG,al
						; Idem ERRFLG
	mov	FILNAM+1,al			; Nom de fichier suit n° lecteur
	mov	bx,0				;GET 0
	mov	word ptr BUFMIN,bx		; Fin de ligne, tampon d'édition
	mov	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,offset TBUFF
		mov	al,[bx]
		or	al,al
		mov	TEMP8,bx
		jnz	$+5
		jmp	DONCMD
		mov	ch,[bx]
TBFLP:		inc	bx			; Etiquette mieux ici qu'après
		mov	al,[bx]
		dec	bx
		mov	[bx],al
		inc	bx
						; Incrémentation à l'étiquette
		dec	ch
		jnz	TBFLP
						; Plus besoin de décrémenter
ENDCMD:		mov	byte ptr [bx],0
		mov	TEMP8,bx
		mov	bx,offset TBUFF-1
		call	CHRGTR
		or	al,al
		jnz	$+5
		db	0E9h			; JMP DONCMD
		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			; JMP DONCMD
		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,16
		jb	$+5
		jmp	FCERR
		mov	MAXFIL,al
		jmp	short FOK
MEM:		mov	MEMSIZ,dx
FOK:		dec	bx
		call	CHRGTR
		jz	DONCMD
		call	SYNCHR
		db	'/'
		jmp	short SCANSW
WASS:		call	CHRGTR
		call	SYNCHR
		db	':'
		call	CNSGET
	mov	al,dh				; INTID2 remplacerait CNSGET si
	and	al,177o				; MEMSIZ pointait en deçà du
	mov	dh,al				; sommet de la pile (cf. GETSTK)
		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 faussée par des débordements (itération LOPFLB) ;
; - une pile trop petite autorisée (appel REASON remplaçant GETSTK de l'Altair).

	DATPSC	equ	128			; Octets dans un secteur (128)
	FCBSIZ	equ	38			; File Control Block (37 octets)
; File-Data-Block.
	F_MODE	equ	0			; Mode={1:"I" ; 2:"O|A" ; 3:"R"}
	F_FCB	equ	1+F_MODE		; FCB normal (DOS)
	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		; FDB interne (fichier #0)
; 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
ERRCMD:
DONCMD:		MOV	CS:Seg_DS,DS		; INS86	56
						; INS86	214,36,DSKERR-2
		dec	bx
		mov	bx,MEMSIZ
		dec	bx
		mov	MEMSIZ,bx
		dec	bx			; STKTOP
		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
LOPFLB:		xchg	bx,dx
		mov	[bx],dx
		inc	bx
		inc	bx
		xchg	bx,dx
		add	bx,cx
		jae	$+5
		jmp	OMERRR
		push	bx
		mov	bx,MAXREC		; Entier signé positif
		mov	cx,offset FNZBLK
		add	bx,cx
		mov	cx,bx
		pop	bx
		dec	al
		jnz	LOPFLB
		inc	bx
		mov	TXTTAB,bx
						; Ne pas sauver la pile là !
	dec	bx				; Contre un débordement (TXTTAB
						; ayant alors une valeur nulle)
		pop	dx
		mov	al,dl
		and	al,254
		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			; INS86	323,353
		MOV	AL,1			; Pile [256;512[+3 & non [0;512]
		CMP	AL,BH			; Evite le dépassement en LEVFRE
		JZ	SMLSTK			; du segment alloué entier--/M:0

	mov	bh,al				; Cas de l'entrée d'une ligne de
	sbb	al,al				; programme pleine avec numéro à
	mov	bl,al				; un chiffre non suivi de blanc,
SMLSTK:	inc	bx				; sinon 1/8ème ou 512 au maximum
	inc	bx				; L'apostrophe (REM) vaut triple

		mov	al,dl
		sub	al,bl
		mov	bl,al
		mov	al,dh
		sbb	al,bh
		mov	bh,al
						; Pas de débordement ici
	dec	bx
	mov	byte ptr [bx],0			; Fin de chaîne pour VAL$ (sic)
	dec	bx				;MOV BX,OFFSET ENDBUF-BUF évince
						;2*NUMLEV+20 en CLEART--BIMISC.*
		mov	MEMSIZ,bx
	dec	bx				; Soustrait 2 futurs octets nuls
	dec	bx				; compris entre TXTTAB et STREND
		xchg	bx,dx
		mov	TOPMEM,bx
						; Sans FRETOP car REASON n'a...
		mov	sp,bx
		mov	SAVSTK,bx
		mov	bx,TXTTAB
		xchg	bx,dx
		CALL	DCOMPR			; ... pas raison d'être.
	jae	$+5				; Risque de débordement ici
	jmp	OMERRR				; Suite absente de GWINIT.ASM
		sub	bx,dx
						; 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		; INS86	214,36,SAVSEG
		jmp	INITSA
AUTTXT:		db	13
		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			; Fin de la version 86-DOS
LASTWR:		db	high -32000		; 128+ETX ? Secteur comblé
		org	7E0h
	CONSTR	equ	$
		org	844h
	ENDCNS	equ	$
	CNSLEN	equ	ENDCNS-CONSTR
		org	8D8h
	SNERR	label	near
		org	8F2h
	ERROR	label	near
		org	103Eh
	CHRGTR	label	near
		org	117Ah
	FCERR	label	near
		org	1B4Fh
	MAKUPL	label	near
		org	1B5Ch
	CNSGET	label	near			; Va à LINGET si nombre décimal
		org	2E9Ch
	CRDO	label	near
		org	2F37h
	OMERRR	label	near
		org	2FFCh
	STKINI	label	near
		org	303Eh
	DCOMPR	label	near			; Macro COMPAR (version RSTLES)
		org	3041h
	SYNCHR	label	near			; SYNCHK (RST 8080)
		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	public 'DATASG'
	NAMLEN	equ	40
	BUFLEN	equ	255
	STRSIZ	equ	3
	NUMTMP	equ	10
	KBFLEN	equ	BUFLEN+(BUFLEN/4)
		assume	cs:DSEG
		org	100h
	RAMLOW	equ	$			; JMP 0:0
		org	10Ch
	DSEGZ	label	byte			; 0
		org	11Dh			;INS86 201,372,65535 en USRFN...
	USRTAB	dw	10 dup(?)		; 65535 (absence d'adresse USR)
	NULCNT	db	1 dup(?)		; 1 (pour NULL 0)
	MSDCCF	db	1 dup(?)		; 0 (en l'absence de ^S ou ^C)
		db	1 dup(?)		; Fonction de sortie caractère
	CTLCAD	dd	1 dup(?)
	DINTAD	dd	1 dup(?)
		dd	1 dup(?)		; CNTCCN se branche sur l'offset
		dd	1 dup(?)		; Adresse GetVector 8086 ou DOS2
		org	15Ch
	TOPMEM	dw	1 dup(?)		; TSTACK+100
	CURLIN	dw	1 dup(?)		; 65534 (quitter si faute)
	TXTTAB	dw	1 dup(?)		; TSTACK+1
		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	20Fh			;CMP AL,LOW OFFSET "Z"+1 AND 37O
	FILNAM	db	33 dup(?)		;en DSKNAM--lettre du lecteur...
	CPMVRN	db	1 dup(?)
	CPMREA	db	1 dup(?)
	CPMWRI	db	1 dup(?)
		db	1 dup(?)		; ':' pour "Redo from start"
	KBUF	db	KBFLEN dup(?)
	BUFMIN	db	1 dup(?)		; ',' qu'ajoute INPUT[$]
	BUF	db	BUFLEN+1 dup(?)
		db	2 dup(?)	;ALLOW FOR SINGLE QUOTE IN BIG LINE
	ENDBUF	db	1 dup(?)
	TTYPOS	db	1 dup(?)
		org	487h
	MEMSIZ	dw	1 dup(?)
	TEMPPT	dw	1 dup(?)
	TEMPST	db	STRSIZ*NUMTMP dup(?)
		org	4B0h
	TEMP8	label	word
		org	4C3h
	SAVSTK	label	word
		org	4CBh
	ONELIN	label	word			; Adresse du traitement d'erreur
		org	4D0h
	SAVSEG	label	word
		org	4FAh
	PRMSTK	label	word
		org	562h
	PRMPRV	label	word
		org	5E4h
	MAXREC	dw	1 dup(?)
	PROFLG	db	1 dup(?)
	MRGFLG	db	1 dup(?)
		org	5EDh
	CHNFLG	label	byte
		org	671h
	DSKDAT	equ	$			; TSTACK
DSEG		ends

		end
EOF le 27/09/23 à 21:50.

Lundi 1er mai 2023

Le bug de débordement FOR/NEXT du compilateur 5.36 révèle une chose : l'expression après TO est calculée la première. Le manuel de référence du BASIC Microsoft indique, à la page 2-34, que la boucle suivante s'exécute dix fois au lieu des six d'une version antérieure.
10 I=5
20 FOR I=1 TO I+5
30 PRINT I;
40 NEXT
Et le commutateur /T de BASCOM 5.x ne change rien à l'affaire ; on ne peut imiter le comportement de l'interpréteur 4.51 sur ce point précis.

Samedi 16 septembre 2023

Le contenu d'une disquette d'Apricot où se côtoient le compilateur et l'interpréteur pour MS-DOS :
http://actapricot.org/disks/apr00090.zip

Dimanche 24 septembre 2023

Un bug étrange rencontré sous DR DOS 6 : si on met à jour les vecteurs DOS sauvés dans le PSP actuel via la fonction 26h (DX=CS au format COM), les instructions LLIST et LPRINT envoient le texte à l'écran et non à l'imprimante...

Last bumped by Big Monstro on 24 September, 17:07.

_________________

C:\ONGRTLNS.W95


Top
Display: Sort by: Direction:
Post Reply   Page 1 of 1  [ 6 posts ]
Return to “Projets en cours”
Jump to: