$PAGE(104)
;
; Pour visualiser ou imprimer correctement certains commentaires avec un éditeur
; de texte windauze, il est nécessaire d'utiliser une police à chasse fixe comme
; Consolas...

; ******************************************************************************

; Les µC de la famille MCS48 possèdent la faculté de pouvoir adresser une
; mémoire programme d'une taille maximum de 4 ko. Certains disposent d'une
; mémoire interne enfouie, d'autres nécessitent une mémoire programme externe :
; - 8035, 8039 et 8040 version ROMless respectivement des 8048, 8049 et 8050.
; - 8048 (ROM interne de 1Ko), 8049 (2 Ko) et 8050 (4 Ko),
; - 8748 et 8749 : version EPROM des 8048 et 8049,
; - NS87P50 : version piggy-back. La ROM interne est remplacée par une EPROM
;   externe enfichée dans les lyres disposées sur la face supérieure du boîtier.

; Le mode de fonctionnement dépend du niveau logique présent sur l'entrée EA :
; - Les instructions sont recherchées dans la ROM/EPROM interne ou l'EPROM
;   piggy-back quand cette entrée est positionnée à zéro, 
; - Les instructions sont recherchées dans la mémoire externe connectée sur le
;   bus externe du µC quand cette entrée est positionnée au niveau 1.
; La mémoire externe est systématiquement adressée dés que l'adresse des
; instructions à exécuter est en dehors de la mémoire interne ou plus exactement
; supérieure à l'adresse ultime de cette dernière, quelle soit enfouie ou pas. 

; "For proper operation, Reset should be applied while the EA input is changed"
; Phrase qui semble être plus un conseil qu'un impératif et qui sous-entend un
; comportement occulté du µC si l'on procède différemment.

; ******************************************************************************

; Cette méthode non officielle consiste à faire exécuter aux µC de cette famille
; MCS48 un programme externe (entrée EA à 1) et de leur imposer un changement de
; mode au moment le plus opportun du cycle d'une instruction "MOVP A,@A" pour
; qu'il lise l'octet de la donnée dans sa ROM interne plutôt que dans la mémoire
; programme externe.

; Description et fonctionnement du dispositif de lecture :
; 1) L'entrée EA du µC étant positionnée à 1, celui-ci va alors rechercher les 
;    instructions du programme ci-dessous dans l'EPROM 2732.
; 2) Les adresses présentes sur le bus du µC à un instant précis de chaque cycle
;    sont latchées sur le front descendant du signal ALE.
; 3) Un LS688 compare en permanence l'octet présent sur le bus du µC avec la
;    valeur 0A3H de l'instruction "MOVP A,@A". Cette instruction permet de lire
;    un octet dans la page courante de la mémoire programme. Le registre "A"
;    sert d'index dans cette page et est ensuite destinataire de l'octet qui y a
;    sera lu.
; 4) Le résultat de la comparaison issu du LS688 est mémorisé sur le front
;    montant du signal PSEN/.
; 5) Un résultat positif autorise l'entrée EA à changer d'état sur le front
;    montant du signal ALE concomitant au 2ème cycle de l'instruction.
; 6) Cet évènement, dont l'occurence n'est pas "politiquement correcte" puisque
;    survenu au cours d'une instruction, ne provoque pas pour autant un
;    comportement irrationnel du µC, il obtempère en adressant docilement la
;    ROM interne au lieu de la mémoire externe.
;    C'est ainsi que tous les octets de cette mémoire interne pourront être lus
;    en mettant à profit ce comportement inédit quoique tout à fait cohérent.
; 7) Le front montant du signal ALE concomitant au 1er cycle de l'instruction
;    suivante fait rebasculer l'entrée EA dans son état initial afin que la
;    nouvelle instruction soit recherchée dans la mémoire programme externe.
; 8) L'index est incrémenté pour lire l'octet suivant de la page courante. Une
;    fois les 256 octets de la page tous lus, le programme se poursuit dans la
;    page suivante jusqu'à ce que les 4, 8 ou 16 pages de la mémoire programme
;    aient toutes été lues.
; 9) Ainsi le tour est joué, la boucle est bouclée !

; Le programme ci-dessous liste le contenu de la ROM interne du µC au format
; Intel Hex à raison de 16 octets/ligne. Cette sortie de listing s'effectue en
; série sur le port P27 (38), au format 7E1 et à la vitesse de 1200 Bds pour un
; quartz de 4 MHz.


; ATTENTION ! En cas de modification de ce fichier source et après assemblage :
; Veillez à ce que le 2ème octet des instructions à 2 octets ne soit en aucun
; cas égal à 0A3H car cette valeur 0A3H correspond au code de l'instruction 
; MOVP A,@A.


NUL	EQU	0
CR	EQU	0DH		; Carriage RETurn
LF	EQU	0AH		; Line Feed
FF	EQU	1AH		; Form Feed
;
IDL	EQU	01H		; Idle, opération inconnue par certains assembleurs
;
	ORG	0		; Page 0 : 8048, 8049 et 8050
Start:
	JMP	Init
;
	ORG	3		; Interruption issue de l'entrée INT/
PinInt:
	DB	IDL		; Attente indéfinie
;
	ORG	7		; Interruption causée par le timer/counter
TmrInt:
	DB	IDL		; Attente indéfinie
;
L0008:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
Init:
	MOV	R0,#16
L000D:
	MOV	A,#NUL
	CALL	SorCar
	DJNZ	R0,L000D
	MOV	A,#CR+80H
	CALL	SorCar
	MOV	A,#LF
	CALL	SorCar
	MOV	R1,#HIGH(0)
SorPage:
	MOV	R2,#LOW(0)
SorLigne:
	MOV	A,#':'		; Caractère d'entête
	CALL	SorCar
	MOV	A,#16		; Nombre d'octets par ligne
	MOV	R4,A		; Initialisation du checksum
	CALL	SorOct
	MOV	A,R1
	CALL	SorHex		; Sortie adresse haute
	MOV	A,R2
	CALL	SorHex		; Sortie adresse basse
	MOV	A,#0		; Type du record (Data)
	CALL	SorHex
L0032:
	CALL	LectOct		; Lecture octet en mémoire programme interne
	SEL	MB0		; Car retour avec bit Memory Bank non affecté
	CALL	SorHex
	INC	R2
	MOV	A,R2		; Récupération de l'adresse basse
	ANL	A,#0FH
	JNZ	L0032
	MOV	A,R4		; Récupération du checksum calculé 
	CPL	A		; Complément à 1 du calcul
	INC	A
	CALL	SorOct		; Sortie du checksum
	MOV	A,#CR+80H
	CALL	SorCar
	MOV	A,#LF
	CALL	SorCar
	MOV	A,R2		; Récupération de l'adresse basse
	JNZ	SorLigne
	INC	R1
	MOV	A,R1
	JB4	L0053		; JB4 pour 8050, JB3 pour 8049 ou JB2 pour 8048 
	JMP	SorPage
L0053:				; Fin de mémoire atteinte
	MOV	A,#':'		; Caractère d'entête
	CALL	SorCar
	MOV	A,#0		; Nombre d'octets = 0
	MOV	R4,A		; Initialisation du checksum
	CALL	SorOct
	MOV	A,#0		; Adresse haute = 0
	CALL	SorHex
	MOV	A,#0		; Adresse basse = 0
	CALL	SorHex
	MOV	A,#1		; Type du record (End Of File)
	CALL	SorHex
	MOV	A,R4		; Récupération du checksum calculé 
	CPL	A		; Complément à 1 du calcul
	INC	A
	CALL	SorOct		; Sortie du checksum
	MOV	A,#CR+80H
	CALL	SorCar
	MOV	A,#LF
	CALL	SorCar
;
	JMP	$		; Boucle indéfiniment
;
; Sortie d'un octet avec ou sans calcul du checksum :
;	-  A = Octet à émettre
;	- R3 = Stockage temporaire
;	- R4 = Checksum

SorHex:				; Sortie d'un octet avec calcul du checksum
	XCH	A,R4
	ADD	A,R4
	XCH	A,R4
SorOct:				; Sortie d'un octet
	MOV	R3,A
	SWAP	A
	CALL	SorQuar
	MOV	A,R3
SorQuar:			; Sortie d'un quartet
	ANL	A,#00001111B	; Annulation quartet de gauche
	MOVP3	A,@A		; Transcodage en caractère hexadécimal avec CharTable
;
;Sortie d'un caractère quelconque :
;	-  A = Caractère à émettre
;	- R5 = Durée ½ moment
;	- R6 = UART logicielle
;	- R7 = Décompage des moments
;
SorCar:
	MOV	R6,A		; Initialisation de l'UART logicielle
	MOV	R7,#10		; 8 moments : start + 8 bits + stop
	CLR	C		; Stop = bit à zéro
SorBit:
	JC	SorUn
;
SorZero:
	ANL	P2,#01111111B	; Emission bit à 0
	JMP	L0092
;
SorUn:
	ORL	P2,#10000000B	; Emission bit à 1
	JMP	L0092
;
L0092:
	MOV	R5,#50		; 2 µcycles/boucle, correspond à ½ moment à 1200 Bds
L0094:
	DJNZ	R5,L0094
	MOV	R5,#35		; 3 µcycles/boucle, correspond à ½ moment à 1200 Bds
L0098:
	NOP
	DJNZ	R5,L0098
;
	CLR	C		; Mise à 1 préventive pour le bit de stop
	CPL	C
; 
	MOV	A,R6		; Préparation émission bit suivant
	RRC	A
	MOV	R6,A
	DJNZ	R7,SorBit	; Décomptage des bits émis
	RET
;
LectOct:			; Lecture octet en mémoire programme interne
	MOV	A,R1
	JB3	SelMB1
	JMP	L00AA
SelMB1:
	SEL	MB1		; Memory Bank 1 pour adresses >= 0800H
L00AA:
	ADD	A,#LOW(JmpTable)
	JMPP	@A		; Indirect jump
;
JMPTable:
	DB	LOW(PageTable)
	DB	LOW(PageTable+2)
	DB	LOW(PageTable+4)
	DB	LOW(PageTable+6)
	DB	LOW(PageTable+8)
	DB	LOW(PageTable+10)
	DB	LOW(PageTable+12)
	DB	LOW(PageTable+14)
	DB	LOW(PageTable+16)
	DB	LOW(PageTable+18)
	DB	LOW(PageTable+20)
	DB	LOW(PageTable+22)
	DB	LOW(PageTable+24)
	DB	LOW(PageTable+26)
	DB	LOW(PageTable+28)
	DB	LOW(PageTable+30)
;
PageTable:
	JMP	L0008
	JMP	L0100
	JMP	L0200
	JMP	L0310
	JMP	L0400
	JMP	L0500
	JMP	L0600
	JMP	L0700
	JMP	L0800
	JMP	L0900
	JMP	L0A00
	JMP	L0B00
	JMP	L0C00
	JMP	L0D00
	JMP	L0E00
	JMP	L0F00
;
	ORG	0100H		; Page 1 : 8048, 8049 et 8050
L0100:
;
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0200H		; Page 2 : 8048, 8049 et 8050
L0200:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0300H		; Page 3 : 8048, 8049 et 8050
;
CharTable:			; Transcodage quartet en caractère ASCII parité paire
	DB	'0'
	DB	80H +'1'
	DB	80H +'2'
	DB	'3'
	DB	80H +'4'
	DB	'5'
	DB	'6'
	DB	80H +'7'
	DB	80H +'8'
	DB	'9'
	DB	'A'
	DB	'B'
	DB	80H +'C'
	DB	'D'
	DB	80H +'E'
	DB	80H +'F'
;
L0310:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0400H		; Page 4 : 8049 et 8050
L0400:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;	
	ORG	0500H		; Page 5 : 8049 et 8050
L0500:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0600H		; Page 6 : 8049 et 8050
L0600:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0700H		; Page 7 : 8049 et 8050
L0700:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0800H		; Page 8 : 8050 uniquement
L0800:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;	
	ORG	0900H		; Page 9 : 8050 uniquement
L0900:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0A00H		; Page 10 : 8050 uniquement
L0A00:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0B00H		; Page 11 : 8050 uniquement
L0B00:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0C00H		; Page 12 : 8050 uniquement
L0C00:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;	
	ORG	0D00H		; Page 13 : 8050 uniquement
L0D00:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0E00H		; Page 14 : 8050 uniquement
L0E00:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	ORG	0F00H		; Page 15 : 8050 uniquement
L0F00:
	MOV	A,R2
	MOVP	A,@A		; Entrée EA forcée à zéro pour la lecture de la donnée
	RET
;
	END	Start