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.
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 !
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 :
Les BASIC 5.2x de CP/M corrigeaient pourtant ce souci apparu après l'antique version 4.51, qui est aux révisions 5.x ce que le FORTRAN 66 est à la norme 77.
C'est donc bien triste que le dernier BASIC-86, enfin débarrassé de cette faiblesse, introduise un nouveau problème. Nouveau ? Pas si sûr, mais je préfère ne rien dire pour l'instant.
Dans un premier temps, modifions un programme de Peter Norton qui illustrait l'appel CALL dans un BASIC interprété. La routine d'assemblage d'origine renvoyait la valeur absolue d'un entier (autre que -32768 vu que 32768 n'existe pas en mot signé de 16 bits). Ce que je souhaite, c'est traiter aussi les réels et avoir une fonction fonctionnant comme ABS.
Voici la routine à assembler avec l'assembleur de Microsoft :
; Assemblage :
; MASM ABS;
; LINK ABS;
; EXE2BIN ABS
CODE SEGMENT BYTE
ASSUME CS:CODE
DB 0FDh ; En-tête pour BLOAD
DW 2 dup(0)
DW TailleRoutine
ABS PROC FAR
cmp al,3
jb Entier
je Retour
and byte ptr [bx+2],7Fh ; Efface le bit de signe
Retour:
ret ; Quitte la procédure
Entier:
mov ax,[bx]
cwd ; DX = FFFFh si entier négatif
xor ax,dx ; Si négatif, complément à un
sub ax,dx ; puis à deux
mov [bx],ax ; Remplace nombre par valeur absolue
ret
ABS ENDP
TailleRoutine EQU $-ABS
CODE ENDS
END
Et voici le programme BASIC — avec erreur de débordement — correspondant :
10 REM Cf. Peter Norton.
100 DEFINT A-Z: OPTION BASE 1
110 ' Réserve la mémoire pour les variables
120 X=0
130 SUBADDR=0
140 ' Réserve la mémoire pour la routine
150 DIM SUBAREA(22)
160 SUBADDR=VARPTR(SUBAREA(1))
170 BLOAD "abs.bin", SUBADDR
180 DEF USR=SUBADDR
190 :
200 FOR X=-32760 TO -32768! STEP -1
210 PRINT "ABS("; X; ")="; USR(X)
220 NEXT
230 END
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: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
-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].
(...)
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 :
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).
Récapitulatif de l'interaction de la routine d'INT24h de MS-BASIC avec différents DOS :
- MS-DOS 1 : système purement monotâche, se branche à la routine INT22h de MS-BASIC ;
- MS-DOS 2 : système possiblement multitâche, ne libère pas la mémoire de MS-BASIC qui est son propre parent et se branche à sa routine INT22h ;
- MS-DOS 3 ou plus : système réseau, retourne à l'invite de commande en plantant car la mémoire de MS-BASIC n'est pas libérée ;
- DOS Plus : système CP/M-86 compatible avec les fonctions documentées de DOS 2.11, retourne à l'invite de commande sans planter ;
- DR DOS : compatible DOS 3.31, remarque que MS-BASIC est son propre parent et transforme l'erreur critique en échec de fonction (possibilité de DOS 3).
Le détournement de l'INT22h via la table des interruptions sous ces trois derniers DOS ne sert plus que lors de la création d'un nouveau PSP (fct 26h de l'INT21h).
(...)
Je patche ensuite le DOS des versions 3 et plus de Microsoft (et IBM) pour éviter que les vecteurs sauvés dans le PSP soient inscrits dans la table des interruptions. L'inconvénient est qu'un processus abandonné après une erreur critique ne restaure pas forcément les vecteurs des interruptions 23h et 24h. Mystérieusement, COMMAND.COM ne requiert pas, dans ce cas précis, de restauration à partir du PSP. Tant pis pour un autre processus parent.
Pour patcher, comparons DOS 2:
MOV ES:[user_SP],SP ; restore our stack
MOV ES:[user_SS],SS
MOV SP,ES
MOV SS,SP
ASSUME SS:DOSGROUP
MOV SP,[CONTSTK]
INC BYTE PTR [INDOS] ; Back in the DOS
MOV BYTE PTR [ERRORMODE],0 ; Back from INT 24
STI
IGNRET:
LES BP,[EXITHOLD]
ASSUME ES:NOTHING
CMP AL,2
JZ error_abort
MOV BYTE PTR [WPERR],-1 ;Forget about WP error
return
SETIGN:
XOR AL,AL ;Flag ignore
JMP SHORT IGNRET
error_abort:
PUSH SS
POP DS
ASSUME DS:DOSGROUP
CMP BYTE PTR [CONSWAP],0
JZ NOSWAP2
invoke SWAPBACK
NOSWAP2:
MOV BYTE PTR [exit_Type],Exit_hard_error
MOV DS,[CurrentPDB]
ASSUME DS:NOTHING
;
; reset_environment checks the DS value against the CurrentPDB. If they
; are different, then an old-style return is performed. If they are
; the same, then we release jfns and restore to parent. We still use
; the PDB at DS:0 as the source of the terminate addresses.
;
; output: none.
;
entry reset_environment
ASSUME DS:NOTHING,ES:NOTHING
PUSH DS ; save PDB of process
MOV AL,int_Terminate
invoke $Get_interrupt_vector ; and who to go to
MOV WORD PTR [EXITHOLD+2],ES ; save return address
MOV WORD PTR [EXITHOLD],BX
MOV BX,[CurrentPDB] ; get current process
MOV DS,BX ;
MOV AX,DS:[PDB_Parent_PID] ; get parent to return to
POP CX
;
; AX = parentPDB, BX = CurrentPDB, CX = ThisPDB
; Only free handles if AX <> BX and BX = CX and [exit_code].upper is not
; Exit_keep_process
;
CMP AX,BX
JZ reset_return ; parentPDB = CurrentPDB
CMP BX,CX
JNZ reset_return ; CurrentPDB <> ThisPDB
PUSH AX ; save parent
CMP BYTE PTR [exit_type],Exit_keep_process
JZ reset_to_parent ; keeping this process
invoke arena_free_process
; reset environment at [CurrentPDB]; close those handles
MOV CX,FilPerProc
reset_free_jfn:
MOV BX,CX
PUSH CX
DEC BX ; get jfn
invoke $CLOSE ; close it, ignore return
POP CX
LOOP reset_free_jfn ; and do 'em all
reset_to_parent:
POP [CurrentPDB] ; set up process as parent
reset_return: ; come here for normal return
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 :
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.
(...)
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 :
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 :
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.
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) :
; 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 :
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].
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 :
[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 :
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]
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 :
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 :
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.
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 :
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.
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
@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=
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 :
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 :
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 :
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 :
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 :
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) :
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.
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.
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.
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.
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).
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.
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.
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.
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).
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
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 :
;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 :
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.
;********************************************************************
;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 :
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.
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.