Exercice de calcul en assembleur 6809

Cette catégorie traite de développements récents destinés à nos vieilles machines, applications, jeux ou démos... Amis programmeurs, c'est ici que vous pourrez enfin devenir célèbres!

Modérateurs : Papy.G, fneck, Carl

Daniel
Messages : 17410
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Exercice de calcul en assembleur 6809

Message par Daniel »

Voici un petit problème pour les amateurs d'assembleur 6809 :

SD_LBA est un nombre entier positif sur 32 bits
SD_LB0 est un nombre entier positif sur 32 bits
DK_DRV est un octet (valeurs possibles de 0 à 3)
DK_TRK est un octet (valeurs possibles de 0 à 79)
DK_SEC est un octet (valeurs possibles de 1 à 16)
DK_NUM est un octet (valeurs possibles 0 ou 1)

Il faut calculer SD_LBA avec la formule suivante :

Code : Tout sélectionner

SD_LBA = SD_LB0 + (256 - 255 * DK_NUM) * INT((1280 * DK_DRV + 16 * DK_TRK + DK_SEC - 1) / 2)
Je cherche de mon côté, mais toute aide sera la bienvenue. Le but est d'obtenir le code le plus rapide possible, mais sans dépasser une taille raisonnable (si possible moins d'une centaine d'octets). Ce calcul sera très utile pour un projet en cours, facile à identifier grâce aux noms des variables :wink:
Daniel
L'obstacle augmente mon ardeur.
__sam__
Messages : 7964
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: Exercice de calcul en assembleur 6809

Message par __sam__ »

1er jet direct depuis la formule avec un peu moins d'une 60-taine d'octets qui vont grossir si on ajoute la sauvegarde de contexte:

Code : Tout sélectionner

SD_LBA rmb 4
SD_LB0 rmb 4
DK_DRV rmb 1
DK_TRK rmb 1
DK_SEC rmb 1
DK_NUM rmb 1

calc_LBA:
  ldu  #SD_LBA

  ldb  <DK_TRK ; Utiliser "DK_TRK-SD_LBA,u" plutôt que du direct-page ?
  lda  #16
  mul          ; voir avec lslb rola si les cycles sont importants
  tfr  d,x     ; x = DK_TRK*16

  ldb  <DK_SEC
  decb
  abx          ; x = DK_TRK*16 + DK_SEC - 1

; variante 1
;  lda  #5
;  ldb  <DK_DRV
;  mul         ; astuce : 1280 = 5*256
;  tfr  b,a
;  clrb   

; variante 2: peut être mieux ? toujours basé sur 1280 = 5*256 = (4 + 1)<<8
  clra         ; plus lent que ldd #0, mais 1 octet de moins
  clrb         
  std  ,u
  lda  <DK_DRV
  lsla
  lsla
  adda <DK_DRV
  leax d,x     ; x = 1280 * DK_DRV + 16 * DK_TRK + DK_SEC - 1

  tfr  x,d
  lsra
  rorb         ; d = INT((1280 * DK_DRV + 16 * DK_TRK + DK_SEC - 1) / 2) = XX XX

; la multiplication par 256-255*DK_NUM équivaut à écrire un octet plus haut dans SD_LBA
  leax 4,u
  tst  <DK_NUM
  bne  *+4    ; ---+
  clr  ,-x    ;    |
  std  -2,x   ; <--+  u pointe sur SD_LBA = 0 XX XX 0 ou 0 0 XX XX
  
; SD_LBA = SD_LBA + SD_LB0 sur 32bits
  ldd  <SD_LB0+2
  addd 2,u
  std  2,u
  ldd  <SD_LB0
  adcb 1,u
  adca #0    ; en vrai ",u" mais c'est équivalent car on a toujours 0 à cette adresse
  std  ,u

; En sortie: u pointe sur SD_LBA et ce dernier vaut
; SD_LBA = SD_LB0 + (256 - 255 * DK_NUM) * INT((1280 * DK_DRV + 16 * DK_TRK + DK_SEC - 1) / 2)
Si on s'arrange pour que les données soient assez proche, on peut remplacer l'adressage direct-page par un relatif par rapport à U.
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Daniel
Messages : 17410
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: Exercice de calcul en assembleur 6809

Message par Daniel »

Quelle rapidité :!: __sam__ a terminé alors que je n'ai pas encore aligné plus de 10 instructions. Bravo :D

Dans un premier temps l'optimisation n'est pas la préoccupation principale, le premier objectif est de faire fonctionner le contrôleur de carte SD. Il ira peut-être moins vite que la disquette, car pour lire 256 octets d'une carte SDHC il faut lire tout le bloc de 512 octets. On verra à l'utilisation si l'optimisation est nécessaire.

Dans un premier temps je vais incorporer le code tel quel dans le prototype, c'est le meilleur moyen de le vérifier. Quand le prototype fonctionnera, je publierai le contenu de l'eprom pour une éventuelle optimisation. A priori la taille ne devrait pas être une contrainte trop gênante, nous pourrons perdre des octets pour gagner en vitesse.
Daniel
L'obstacle augmente mon ardeur.
__sam__
Messages : 7964
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: Exercice de calcul en assembleur 6809

Message par __sam__ »

Si les cycles sont importants on peut éviter les transferts entre X et D dans la "grosse formule"
INT((1280 * DK_DRV + 16 * DK_TRK + DK_SEC - 1) / 2:

Code : Tout sélectionner

; grosse formule:
    lda  #80
    ldb  <DK_DRV
    mul          ; D = 80*DK_DRV      
    addb <DK_TRK
    adca #0      ; D = 80*DK_DRV + DK_TRK
    lslb
    rola
    lslb
    rola
    lslb
    rola
    lslb
    rola         ; D = 16*(80*DK_DRV + DK_TRK) = 1280*DK_DRV + 16*DK_TRK 
    decb
    addb <DK_SEC ; D = 1280*DK_DRV + 16*DK_TRK + DK_SEC - 1
    lsra
    rorb         ; D = INT((1280 * DK_DRV + 16 * DK_TRK + DK_SEC - 1) / 2

; Le reste est +/- pareil:
    ldu  #SD_LBA+2
    clr  -1,u    ; u-2 -> ** 00 ** **

; Il y a probablement un truc optimisable ici "ldb DK_NUM; addb #3; std b,u" mais B est écrasé dans la manip. Donc pour l'instant cette version est bien.
    leax 2,u   
    tst  <DK_NUM
    bne  *+4
    clr  ,-x       
    std  -2,x    ; u-2 -> ** XX YY 00 ou ** 00 XX YY

; Addition 32bits
    ldd  ,u
    addd <SD_LB0+2
    std  ,u      ; u-2 -> ** XX (CC DD) ou ** 00 (CC DD)
    ldd  <SD_LB0
    adcb -1,u    ; (ou ",-u") recup de XX ou 00 
    adca #0      ; optim: on a pas besoin d'initaliser ** à 0. On ajoute simplement 0 à A
    std  -2,u    ; (ou ",--u") u -> SD_LB0[0..3] + grosse formule
C'est au moins 5 octets plus court et plus rapide de plusieurs cycles.
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Daniel
Messages : 17410
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: Exercice de calcul en assembleur 6809

Message par Daniel »

Pour l'instant je ne me préoccupe pas trop du nombre de cycles, je garde ton code pour la phase d'optimisation.
Les premiers tests ont montré une erreur dans un terme de ma formule :
(256 - 255 * DK_NUM) est faux et doit être remplacé par (512 - 511 * DK_NUM)
J'ai modifié le code comme suit, mais je n'ai pas vérifié si le décalage pouvait produire un dépassement de capacité

Code : Tout sélectionner

; la multiplication par 256-255*DK_NUM équivaut à écrire un octet plus haut dans SD_LBA
; Attention : formule fausse : la vraie formule est 512-511*DK_NUM
  leax 4,u
  tst  <DK_NUM
  bne  *+4    ; ---+
  lslb        ;    |  ajoute par DC pour corriger l'erreur de formule
  rola        ;    |  ajoute par DC pour corriger l'erreur de formule
  clr  ,-x    ;    |
  std  -2,x   ; <--+  u pointe sur SD_LBA = 0 XX XX 0 ou 0 0 XX XX
Pour information, DK_NUM est le type de carte SD (0=SD, 1=SDHC). L'adresse d'un secteur physique est exprimée en nombre de secteurs de 512 octets pour la SDHC, en nombre d'octets pour la SD.
Pour l'instant la formule est testée sur le secteur de boot (DK_DRV=0, DK_TRK=0, DK_SEC=1, DK_NUM=0) et ça marche :D
Le secteur de boot d'un fichier .fd a été chargé à partir d'une carte SD (en émulation seulement pour l'instant, mais c'est un premier pas). Ensuite il y a d'autres ajustements à faire pour lancer le DOS, mais c'est sur la bonne voie. A suivre...
Daniel
L'obstacle augmente mon ardeur.
__sam__
Messages : 7964
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: Exercice de calcul en assembleur 6809

Message par __sam__ »

INT((1280 * DK_DRV + 16 * DK_TRK + DK_SEC - 1) / 2) vaut au plus (1280*3+16*79 + 15)/2= 2559. C'est bon: le décalage à gauche ne va pas déborder.

Sinon attention au *+4, il devient un *+6 avec le décalage inclus.
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Daniel
Messages : 17410
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: Exercice de calcul en assembleur 6809

Message par Daniel »

Le *+4 est corrigé, j'ai aussi remplacé DK_TRK par DK_TRK+1 car en fait le numéro de piste est codé sur 2 octets, le premier étant toujours nul. Pour finir j'ai corrigé une petite erreur dans mon code de traitement des fonctions standard, et... ça marche :D

Un fichier .fd contenant un jeu sur une disquette bootable (avec le DOS) est copié sur une carte SD à l'adresse SD_LB0. Au démarrage du MO5 (ou du TO7/70), le secteur de boot se charge et s'exécute, le DOS se charge en mémoire, le programme AUTO.BAT s'exécute automatiquement, charge un programme Basic qui a son tour lit des fichiers de données et lance un autre programme Basic. C'est prodigieux : mon rêve devient réalité :D

Bon, ce n'est pas fini : je n'ai programmé que le reset du contrôleur et la lecture d'un secteur. Reste maintenant à coder l'écriture d'un secteur et le formatage, ça devrait suffire pour émuler toutes les disquettes déprotégées existantes, comme dans dcmoto. Il y a peut-être aussi quelques ajustements à faire pour traiter les erreurs : contrôleur SD absent, carte SD non insérée, numéro de secteur invalide, etc. Mais ce sont des détails, aujourd'hui on peut annoncer que l'émulation des disquettes Thomson sur carte SD n'est pas une utopie.

Bien évidemment __sam__ sera crédité pour sa double participation au codage de l'eprom du contrôleur : le calcul de SD_LBA et l'optimisation de la boucle de lecture d'un octet de la carte SD (le fameux LDA #$FE pour boucler 8 fois sans utiliser de compteur). Quand le programme sera terminé et testé sur le matériel réel j'ouvrirai un autre sujet pour le présenter et donner les sources.
Daniel
L'obstacle augmente mon ardeur.
Avatar de l’utilisateur
petitjd
Messages : 2007
Inscription : 23 oct. 2007 11:50

Re: Exercice de calcul en assembleur 6809

Message par petitjd »

C'est genial :D

Messieurs, vous etes trop forts! Faire un controleur de disquette sd en 1 jour je dis chapeau!

Félicitations, le reve devient réalité pour le plus grand bonheur des utilisateurs de Thomsons!
PetitJD
Tortue Jeulin: www.tortue-jeulin.com
Nanoreseau: www.nanoreseau.net
Proteus III: www.proteus-international.fr
Avatar de l’utilisateur
Carl
Modérateur
Messages : 13290
Inscription : 08 avr. 2007 13:21
Localisation : http://www.doledujura.fr
Contact :

Re: Exercice de calcul en assembleur 6809

Message par Carl »

Bravo pour ce nouveau défi !

Carl
Fool-DupleX
Messages : 2338
Inscription : 06 avr. 2009 12:07

Re: Exercice de calcul en assembleur 6809

Message par Fool-DupleX »

Magnifique !

Ca se comporte comment en terme de vitesse ? La lecture de la SD est assez lente mais la disquette n'etait pas un foudre de guerre non plus ...
Daniel
Messages : 17410
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: Exercice de calcul en assembleur 6809

Message par Daniel »

En émulation MO5 + carte SD, il faut environ 4 secondes pour lancer le DOS (entre la commande DOS et l'affichage du message d'accueil). Avec le vrai MO5, un contrôleur CD90-351 et un lecteur Thomson il faut environ 5 secondes. La vitesse est donc du même ordre de grandeur.

Le code du prototype n'est pas optimisé, on peut espérer gagner des cycles, mais ça ne changera pas significativement la vitesse. Le problème est la lecture obligatoire d'un secteur complet de la carte SD (512 octets) pour lire un secteur disquette de 256 octets.

Dans les démonstrations existantes de musique, vidéo, transfert de fichiers, chargement de jeu, etc.. j'utilise la commande CMD18 de lecture "multi-blocs". La lecture séquentielle de la carte SD est 3 ou 4 fois plus rapide que la lecture séquentielle de la disquette. Il faut dire aussi que l'on gagne tous les délais mécaniques de changement de piste et de rotation du disque.
Daniel
L'obstacle augmente mon ardeur.
__sam__
Messages : 7964
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: Exercice de calcul en assembleur 6809

Message par __sam__ »

Magnifique tout ca! :D

Un petit détail m'échappe: Pourquoi effectues-tu un mapping 1 pour 1 entre les secteurs D7 et les secteurs SD ? Tu pourrais placer 2 secteurs de 256octets de la D7 sur un secteur 512 de la SD et gagner à la fois en occupation SD (bof) mais surtout en temps d'accès: accès à cout nul au secteur 2 quand le secteur 1 est chargé. Une sorte de prefetch-cache :)

Est-ce parce que le buffer d'i/o de 512octet n'est pas prévu par les structures du DOS moniteur ? Dans ce cas, ne pourrait on pas modifier certains pointeurs de la page-zéro moniteur pour trouver 512 octets supplémentaires de dispo dans la mémoire sans interférer avec l'existant? Un secteur SD complet serait copié sur ces 512 octets réalisant un cache et quand le moniteur accède aux secteurs impair on copie la partie basse de ce cache, et la partie haute pour les secteurs pairs. (je ne sais pas si je suis clair ?)
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Daniel
Messages : 17410
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: Exercice de calcul en assembleur 6809

Message par Daniel »

Il y a bien 2 secteurs Thomson de 256 octets dans chaque bloc de la carte SD. La correspondance n'est pas biunivoque, il y a dans la formule INT((1280 * DK_DRV + 16 * DK_TRK + DK_SEC - 1) / 2)

En lecture, si le secteur est impair les 256 premiers octets du bloc sont lus et stockés dans le buffer, les 256 suivants sont lus (c'est obligatoire) et ignorés. Si le secteur est pair, c'est l'inverse.

Maintenant j'aborde l'écriture, et là ça coince car il faut impérativement un buffer de 512 octets, et il n'est pas prévu par le DOS. Je ne sais pas encore comment je vais m'en sortir. C'est gênant de modifier le DOS, car si on décale des adresses certains programmes ne fonctionneront plus. Je ne sais pas trop comment l'éviter. Ou alors je considère que les disquettes sont en protection écriture, mais ce serait trop restrictif. Ou encore je ne stocke qu'un secteur Thomson dans un bloc SD, ce n'est pas gênant de perdre de la place, il y en a plus que nécessaire. Par contre ça nécessite de transformer les fichiers .fd pour les adapter à cette structure, et c'est moins élégant.

Il faut y réfléchir encore un peu...
Daniel
L'obstacle augmente mon ardeur.
Avatar de l’utilisateur
6502man
Messages : 12312
Inscription : 12 avr. 2007 22:46
Localisation : VAR
Contact :

Re: Exercice de calcul en assembleur 6809

Message par 6502man »

Super boulot, c'est très impressionnant de voir a la vitesse a laquelle tu as développé :shock: :shock:

En tout cas félicitations ...
Phil.

www.6502man.com

To bit or not to bit.
1 or 0.
__sam__
Messages : 7964
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: Exercice de calcul en assembleur 6809

Message par __sam__ »

Daniel a écrit :En lecture, si le secteur est impair les 256 premiers octets du bloc sont lus et stockés dans le buffer, les 256 suivants sont lus (c'est obligatoire) et ignorés. Si le secteur est pair, c'est l'inverse.
Ah ok. C'est bien alors, ca reste compatible avec le format FD standard. Je suppose que pour ignorer les 256 octets tu fais une boucle de 256 fois la lecture d'un octet. Que donnerait en terme de vitesse une boucle faisant simplement basculer l'horloge 256*8 fois sans vraiment construire l'octet lu? On s'épargnerait les décalages et la lecture du port etc. Le code se réduirait à

Code : Tout sélectionner

  LDX  #addr_du_port
  LDA  ,X
  ORA  #<BIT à 1>
  LDB  ,X
  ANDB #<BIT à 0>
  LDY  #256*8/<nb_unroll>
LOOP
  STA  ,X
  STB  ,X
  ... unroll ...
  STA  ,X
  STB  ,X
  LEAY -1,Y
  BNE  LOOP
Ainsi le saut des 256 octets pourrait se faire à une vitesse bien plus rapide que la lecture à proprement parler. Cela pourrait notablement accélérer les temps d'accès au bon coté du secteur SD. (10 cycles 6809 pour changer l'horloge... donc 20480cycles=20ms pour sauter les 256 octets au lieu de 256/(5ko/sec*1024)=50ms à la louche).
Maintenant j'aborde l'écriture, et là ça coince car il faut impérativement un buffer de 512 octets, et il n'est pas prévu par le DOS. Je ne sais pas encore comment je vais m'en sortir. C'est gênant de modifier le DOS, car si on décale des adresses certains programmes ne fonctionneront plus. Je ne sais pas trop comment l'éviter.
J'ai quelques idées sur le sujet.
  1. On swap une zone de 512 octets (contigus pour la simplicité) de la mémoire thomson le temps de faire une écriture.
    Détail: lors d'une opération d'écriture: tu commences par écrire dans un secteur spécial de la carte SD le contenu de la zone mémoire $9000-$91FF (plage arbitraire, mais à choisir hors du buffer d'I/O et hors de la pile. Les 256 octets au dessus de la valeur du registre S au point d'entrée de la routine moniteur sont peut être une excellente idée?). Puis tu remplies la zone $9000-91FF avec les 512 octets du secteur de la carte SD à modifier. Tu recopies alors le buffer d'I/O de 256 octets dans la bonne moitié de $9000-$91FF, puis tu écris les 512 octets sur la carte. Enfin tu termines en restorant les valeurs précédentes de la zone $9000-$91FF sauvées dans la zone spéciale de la carte SD.

    Donc pour écrire 256 octets on doit: 1) écrire 512, 2) lire 512 3) remplir 256, 4) écrire 512 5) lire 512. Donc 1Ko à lire et 1Ko à écrire. Vitesse 1/4 de celle de la lecture. C'est pas si gênant car les écritures sont en principe rares.
  2. Il y a déjà le buffer de 256 octets à écrire, reste donc à trouver 256 octets pour sauver la demi-moitié non modifiée. En toute logique, pour faire des écritures sur disk, il faut avoir en mémoire la FAT (dont l'adresse est renseignée en $60ED). On peut, le temps de l'écriture écraser le contenu du buffer FAT par la 1/2 moitié de secteur non modifié. A la fin de l'écriture on recharge la FAT depuis le disk.

    Donc, ici pour écrire 256 octets on doit 1) lire 512 2) écrire 512 3) lire 512. 1Ko lus et 0.5 écrits. C'est à peine mieux que le 1). De plus cette technique ne marche pas si la FAT a été modifiée en mémoire, ou si l'on écrit la FAT elle même, ou si l'on a affaire à un programme qui écrit une D7 comme un sauvage sans utiliser la FAT (routine de copie de disk par exemple). Donc pas génial. On oublie cette idée.
  3. Les 256 octets n'ont pas besoin d'être contigus ni même d'avoir leur données préservées. La zone de $C0 en fin de mémoire écran fournit déjà 192 octets. Il faut trouver 64 octets de libres dans le système. Or la zone $608B-$60CC de la pile système fait 66 octets. Donc sur le principe, ici on a rien besoin de charger depuis le disk car on a trouvé 256 octets (non contigus) libres dans la mémoire.

    Mais il y a quelques réserves: il faut que le bas de la zone écran soit vraiment libre (très probable), et que la zone de la pile système soit elle aussi libre. Or ce dernier point ne va pas de soi quand on utilise le gestionnaire de fichiers du menu TO8 par exemple.
  4. En fait on a pas besoin d'utiliser la zone de la pile système, ni même l'ensemble des 192 octets de la fin de mémoire vidéo, car avec la mémoire forme/fond et sous réserve qu'on est pas sur un TO7 qui n'utilise que 6bits en RAM-couleur, on a en réalité 2*192 octets en fin de mémoire video. Dans ce cas on peut couper la poire en deux, et écrire 128 octets en RAMA, 128 en RAMB et hop, on a trouvé notre buffer de 256 manquant.
Bon, ce ne sont que des idées, qui permettent de lancer la réflexion.

Je pense que la plus fiable est la solution 1) car elle ne suppose pas grand chose de l'occupation mémoire du thomson. Elle peut même être compatible TO/MO si on trouve une zone à swapper commune entre les deux types de machine. Elle a juste le défaut d'être un peu lente, mais les écritures sont rares. Si on ne veut pas risquer d'hypothèse c'est une option intéressante.

La 4) utilisera un code un peu plus compliqué pour lire/écrire la moitié de secteur non modifié, mais il sera probablement le plus rapide. Par contre il faut être certain que les 256 octets de RAM video sont bien libres et peuvent être écrasés. Il me semble que certains octets après $5F40 sont utilisés par le menu du TO8 pour sauvegarder la palette ou la date.. mais je ne sais pas à quelles adresse. Une inspection avec DCMOTO de cette zone mémoire initialisée avec des valeurs connues devrait pouvoir trouver une zone de 128 octets non touchés en RAMA ou RAMB et fournir ainsi le buffer qui manque.

4) puis 1) voila mon tiercé (!)

sam.
___
(!) l'option 2) est non-partante. Et la 3) est supplantée par la 4) qui suppose moins de choses.
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Répondre