Apprentissage du BASIC Microsoft sous DOS

Projets en cours de réalisation par les membres de Win3x.Org.
Avatar de l’utilisateur
gm86
Membre inscrit
Messages : 626
Inscription : 01 sept. 2008 19:07

Re: Apprentissage du BASIC Microsoft sous DOS

Message par gm86 »

Le message précédent, se rapportant essentiellement à la version MS-DOS de BASIC, approche les 90.000 caractères. C'est un fourre-tout de ce que j'ai découvert plus ou moins volontairement dans les méandres du DOS et du CP/M au fil des ans.
Place à la version pour CP/M-86, très proche de la version CP/M !

Mardi 3 juillet 2017

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

Mercredi 4 juillet 2017

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

Mercredi 5 juillet 2017

Comment CP/M-86 charge-t-il MBASIC.CMD ?
Il lit l'en-tête de 128 octets :

Code : Tout sélectionner

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 :

Code : Tout sélectionner

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

Code : Tout sélectionner

; 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 :

Code : Tout sélectionner

-a7C1B
CALL 2F0B
Dimanche 14 janvier 2018

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

Jeudi 18 janvier 2018

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

Vendredi 2 février 2018

Hier, je me suis rendu compte que FILES de MS-BASIC 5.28 considérait la lettre Z de lecteur comme une erreur.
Le bug se trouve dans le code ci-dessus :

Code : Tout sélectionner

: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 :

Code : Tout sélectionner

:6F2D 3C61          CMP     AL,'a'
:6F2F 7206          JB      6F37
:6F31 3C7B          CMP     AL,'z'+1
:6F33 7302          JNB     6F37
:6F35 24DF          AND     AL,DF
:6F37 C3            RET
[Et aussi à une autre précédée à l'offset 1B4Fh de MOV [BX],AL mais terminée par AND AL,5Fh.]
Nous pouvons donc nous en passer en rectifiant aussi l'appel :

Code : Tout sélectionner

:7107 E80C00        CALL    6F2D
:710A 2C40          SUB     AL,'A'-1
:710C 72F4          JB      7102
:710E 3C1B          CMP     AL,'Z'+1
:7110 73F0          JNB     7102
:7112 5E            POP     SI
:7113 5F            POP     DI
:7114 5A            POP     DX
:7115 C3            RET
Et nous y plaçons une routine, directement traduisible du 8080, pour préparer les données d'entrée de la procédure à l'offset 2F0Bh (pile suffisante ?) :

Code : Tout sélectionner

:7116 mov byte [bx],0
:7119 inc bx
:711A mov byte [bx],0
:711D dec bx
:711E mov cl,0
:7120 ret
Avec [BX]=[160h]=offset du début du programme BASIC. Le mot nul annonce qu'aucun programme BASIC n'est encore chargé, tandis que [CL indique que la pile doit équivaloir à tant mots en plus que le minimum (CL non nul peut provoquer un débordement avec BX).]

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

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

Procédure pour patcher MS-BASIC 5.28
Renommer l'interpréteur MSBASIC.COM ou BASIC86.COM original (toujours version 5.28) MBASIC86.COM (qui est un nom moins courant que MBASIC.COM et les autres).
S'assurer d'avoir MASM [ici ASM d'IBM], LINK et DEBUG.
Créer les fichiers texte suivants.
PATCH.BAT :

Code : Tout sélectionner

@echo off
echo Patch de la gestion d'erreur critique du BASIC Microsoft sous DOS 3.
echo Le succès de ce fichier batch requiert l'accès à ASM, LINK & DEBUG.
echo.
echo Pressez la combinaison Ctrl-C pour revenir à l'invite de commande.
pause

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

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


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

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

:Fin
for %%f in (%liste%) do if exist %%f.TMP erase %%f.TMP
set f=
set liste=
set msg=
set nom=
PATCH :

Code : Tout sélectionner

e1d47 81
e2f17 c3
e31fe 4f
nNOM.TMP
l3da6
nDOS.TMP
l452c
nRET.TMP
l6e3a
a7107
call 6f2d

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

nINI.TMP
l7a24
rcx
7c00
nMSBASIC.COM
w
q
PATCH.ASM :

Code : Tout sélectionner

		page	64,132
		title	Patch DOS 3 - BASIC 5.28 (les 8 mars 2018 & 30 mai 2020)
code		segment
		assume	cs:code

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

	NAMLEN	equ	40

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

	sub	al,ch			; Longueur du nom moins deux

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

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

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

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

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

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

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

; Sauve puis modifie les vecteurs DOS.

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

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

	pop	es
	pop	ds

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

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

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

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

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

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

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

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

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

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

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

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

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

Seg_DS	dw	 ?

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

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

	pop	ds
	mov	al,2
	iret
DSKERR	endp

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

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

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

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

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

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

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

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

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

DONCMD:	mov	Seg_DS,ds

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

		end
PATCH.PAT :

Code : Tout sélectionner

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

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

Dimanche 11 février 2018

Rédaction en cours d'une petite documentation technique :

Code : Tout sélectionner

BASIC Microsoft 5.28 mis à jour pour DOS 3


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


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

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

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


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

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

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

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

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

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

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

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

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

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

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

Dimanche 25 février 2018

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

Mardi 27 février 2018

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

Dimanche 4 mars 2018

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

Vendredi 9 mars 2018

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

Samedi 24 mars 2018

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

Samedi 15 décembre 2018

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

Vendredi 6 septembre 2019

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

Jeudi 9 janvier 2020

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

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

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

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

Samedi 11 janvier 2020

Une astuce pour obliger l'utilisateur à appuyer sur Entrée est que la ligne soit déjà pleine (255 octets). De même, il est sage qu'un caractère EOF (ASCII 26) la suive dans le fichier ; cela évite le malheureux message Direct statement in file dû à un éventuel retour à la ligne. CLEAR nous permet d'avoir un tableau à onze valeurs nulles par défaut. Ainsi, nous sommes sûr d'avoir au moins un octet nul de début de ligne BASIC et trois octets nuls de fin de programme.
À présent, nous avons tout ce qu'il faut pour obtenir notre fichier PROFLG.BAS :

Code : Tout sélectionner

clear:a%[1]=&h2020:a%[2]=&h2020:a%[3]=&h2098:a%[4]=&h4553:a%[5]=&h3A47:a%[6]=&h1C99:a%[7]=1510:a%[8]=&h112C:b$="":b$="123"+chr$(28)+":::"+chr$(137)+chr$(13)+mki$(varptr(a%[0]))+":":?val(b$)456'SAVE"protbas",P ... LOAD"protbas":MERGE"PROFLG" [ENTER][ENTER]
Il suffit d'entrer MERGE"PROFLG" après avoir chargé le fichier protégé, puis de rappuyer sur la touche ENTER pour faire sauter la protection.

20 janvier 2020

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

Code : Tout sélectionner

A>DEBUG
e100 ff 1
rcx
2
nUNPROT.BAS
w
q
On charge le programme crypté dans BASIC, puis on tape LOAD"UNPROT" pour l'avoir en clair. C'est tout !

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

24 mai 2020

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

31 mai 2020

Le début du fichier PATCH.ASM apporte une nouvelle façon de corriger le problème suivant. MBASIC n'épargne pas d'un débordement le tampon NAMBUF (nom de variable de plus de 40 caractères). Voyons cet extrait du fichier GWDATA.ASM (GW-BASIC) :

Code : Tout sélectionner

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 :

Code : Tout sélectionner

Compteur := -1
Boucle:
incrémente Compteur
recopie caractère suivant
si lettre ou chiffre, Boucle
si Compteur >= 40-1, Erreur
Le compteur (longueur du nom moins deux) n'est vérifié qu'à la fin. Corrigeons avec un décompte vérifié à chaque boucle :

Code : Tout sélectionner

Décompte := 40
décrémente Décompte
si Décompte nul, Erreur
recopie caractère suivant
si lettre ou chiffre, Boucle
Compteur := Décompte - 39
Dès que le décompte est nul, l'erreur est détectée.

2 juin 2020

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


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

Pour que la longueur de la partie "initialisation" puisse varier, voici des fichiers PATCH, PATCH.PAT et PATCH.ASM alternatifs :

Code : Tout sélectionner

e1d47 81
e2f17 c3
e31fe 4f
nNOM.TMP
l3da6
nDOS.TMP
l452c
nRET.TMP
l6e3a
e710f 1b
nINI.TMP
l7a24
a80
cld
xor	al,al
mov	di,7a24
add	di,cx
mov	cx,7f
rep	stosb
mov	cl,7
shr	di,cl
shl	di,cl
mov	cx,di
dec	ch

rip
80
g97
nMSBASIC.COM
w
q

Code : Tout sélectionner

a
sub cx,7a24

t
nINI.TMP
w7a24
nNOM.TMP
rcx
33
w3da6
nDOS.TMP
rcx
116
w452c
nRET.TMP
rcx
3
w6e3a
q

Code : Tout sélectionner

		page	64,132
		title	Patch DOS3 - BASIC 5.28 (les 8 mars 2018 & 21 juin 2020)
CSEG		segment
		assume	cs:CSEG,ds:DSEG

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

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

	sub	al,ch			; Longueur du nom moins deux

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

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

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

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

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

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

; Sauve puis modifie les vecteurs DOS.

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

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

	pop	es
	pop	ds

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

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

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

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

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

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

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

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

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

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

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

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

MSCTLC	proc	far
; Routine INT23h (Ctrl-C).
	sti				; Comme dans la version 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 PSP actif est son propre parent.
	mov	bp,sp
	push	ds

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

	pop	ds
	mov	al,2
	iret
DSKERR	endp

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

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

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

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

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

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

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

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

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

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

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

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

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

DONCMD:	mov	Seg_DS,ds

		dec	bx
		mov	bx,MEMSIZ
		dec	bx
		mov	MEMSIZ,bx
		dec	bx
		push	bx
		mov	al,MAXFIL
		mov	bx,offset DSKDAT
		mov	FILPT1,bx
		mov	dx,offset FILPTR
		mov	MAXFIL,al
		inc	al
		MOV	CX,offset DBLK_C +1	; FDB interne (#0) +1
LOPFLB:		xchg	bx,dx
		mov	[bx],dx
		inc	bx
		inc	bx
		xchg	bx,dx
		add	bx,cx
		jae	$+5
		jmp	OMERRR
	dec	bx				; -1 : anticipe un débordement
		push	bx
		mov	bx,MAXREC
		MOV	CX,offset FNZBLK +1	; FDB utilisateur +1
		add	bx,cx
		mov	cx,bx
		pop	bx
		dec	al
		jnz	LOPFLB
		inc	bx
		mov	TXTTAB,bx
						; La pile ne change pas...
		pop	dx
		mov	al,dl
		and	al,0FEh
		mov	dl,al
		mov	al,dl
		sub	al,bl
		mov	bl,al
		mov	al,dh
		sbb	al,bh
		mov	bh,al
		jae	$+5
		jmp	OMERRR
		mov	cl,3
		shr	bx,cl
		mov	al,bh
		cmp	al,2
		jb	SMLSTK
		mov	bx,0+512
SMLSTK:	dec	bx				; "Out of memory" si pile nulle
		mov	al,dl
		sub	al,bl
		mov	bl,al
		mov	al,dh
		sbb	al,bh
		mov	bh,al
		jae	$+5
		jmp	OMERRR
		mov	MEMSIZ,bx
		xchg	bx,dx
		mov	TOPMEM,bx
						;REASON n'a pas raison d'être...
		mov	sp,bx
		mov	SAVSTK,bx
		mov	bx,TXTTAB
	cli					; Prévient une pile trop faible
	mov cl,0				; Aucun paramètre requis
	mov [bx],cl				; Aucun programme BASIC
	inc bx					; (OMERR appelle LINKER dans
	mov [bx],cl				;  cette version de BASIC)
	inc bx					; Deux futurs octets nuls à ôter
		xchg	bx,dx
		CALL	GETSTK			;... mais GETSTK de l'Altair si.
	sti					; Sans retour si "Out of memory"
		sub	bx,dx
						; Mot nul déjà soustrait...
		push	bx
		mov	bx,offset HEDING
		call	STROUT
		pop	bx
		call	LINPRT
		mov	bx,offset WORDS
		call	STROUT
		call	CRDO
		mov	SAVSEG,ds
		jmp	INITSA

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

; Pile réservée par GETSTK : NUMLEV=0*20+19+2*5 +1 (30 mots du BASIC-86).
; CONS1=256-(2*NUMLEV) -1 dans la procédure (-61 octets au lieu de -58).
; CLEAR,,0+(2*NUMLEV)+20 minimum (voir CLEART dans BIMISC.MAC ou .ASM).

		org	8D8h
	SNERR	label	near
		org	103Eh
	CHRGTR	label	near
		org	117Ah
	FCERR	label	near
		org	1B4Fh
	MAKUPL	label	near
		org	1B5Ch
	CNSGET	label	near
		org	2E9Ch
	CRDO	label	near
		org	2F0Bh			; CL=nombre de paramètres requis
	GETSTK	label	near			; Vérifie que la pile suffit
		org	2F37h
	OMERRR	label	near
		org	2FFCh
	STKINI	label	near
		org	3041h
	SYNCHR	label	near
		org	31B4h
	ISLET2	label	near
		org	44B3h
	KYBINI	label	near
		org	503Bh
	STROUT	label	near
		org	5849h
	LINPRT	label	near
		org	7A09h
	INITSA	label	near
CSEG		ends

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

		end
C:\ONGRTLNS.W95
Répondre