v2BoiteAIdees

Fonctionnalités prévues pour la v2. Pour les sujets importants (MACRO, paramètres externes), il serait bon d'ouvrir la discussion ; Offset aura surement des desiderata, puisque Maxam lui fournit certaines fonctionnalités uniques sur CPC.

Refonte écran TRACE

Visualisation source

Edit: c'est fait ! Je laisse l'entrée, sinon wikidot décale tout les liens.

Dump mémoire anté-registres.

Done

MACRO

TODO : Décrire Quoi/comment. "Bibliographie" Reprendre le meilleur de Maxam-Winape, Nasm, voire assembleurs non-Z80s.

Anecdotique mais casse-tête intéressant : comment éviter les conflits avec le nom d'une macro et une future directive ?

Paramètres externes

Équivalent Get/Put de maxam, pour scriptage.

On envisage ici les solutions pour :

  • (a) Définir des labels de manière externe.
  • (b) A contriario fournir à un programme externe la valeur de labels.

Il s'agit de permettre un "linkage" automatisé (script en basic).

Questions :

  • Y a-t'il une autre utilité à ces fonctionnalités (a) & (b) ?
  • Quelle serait la vraie bonne solution pour le linkage ?

Écartons tout de suite le système de '#define' global à la C, et de tout ce qui va avec : c'est laid, piégeux et empêche plein d'optimisations de "build".
Au contraire, il faut identifier et regrouper (?) tous les paramètres qui influent l'assemblage. Cf solution 1.

Solution 0 (naze, indiquée par souci d'exhaustivité)

Solution 1. A la MaXaM.

    GET in1
    GET in2

Avec 2 différences :

  • Permettre valeurs par défauts. E.g. ' GET dev = 1 '. Exécuté sous ORGAMS, dev vaut 1.
  • Passer les valeurs en texte. E.g. ' |assemble,"dev=1 : dest=#4000" '
    • Cela autorise le point précédent
    • Beaucoup moins sensible aux erreurs de synchronisation, avec un tout petit prix à payer : en cas de renommage de label, il faut le reporter.

NB : le mécanisme sera re-exploitable pour les includes :

dest = #4000

   INCLUDE "3d.o" [ dev = 0 : dest = dest ]  ; Passe en paramètre le dest local.
   INCLUDE "3d.o" [ dev = 0 : dest ]         ; Version équivalente.

Symétriquement, un PUT dans un source devra être récupéré par un GET dans l'appel RSX, ainsi que décrit dans la section suivante.

RSX d'assemblage avec paramètres.

Choix du nom :

  • ORGASS (évite conflit avec |ASSEMBLE de Maxam)
  • Vu qu'on ouvre le fichier à assembler, j'avais pensé à OPENASS, mais c'est trop Charlie.

Solution 0

  • Bof, limitée par longueur ligne basic.

Solution 1 (suggérée par Offset)

Utiliser une paire de RSX SETLABEL/GETLABEL

L'exemple devient

|orgopen,"30ymd.o"
|setlabel,"debug",0
|setlabel,"gfx",1
|setlabel,"start",start
|orgass
|getlabel,"init",@init%
|getlabel,"play",@play%
|getlabel,"draw",@draw%

Beaucoup plus propre, et permet de définir/récupérer facilement des batchs de labels (hook00, hook01, hook02, …).

Casse des labels

Levée de la sensibilité à la casse

Les sources Maxam sont insensibles à la casse. Pour les importer plus facilement, on pourrait faire de même dans Orgams.

Le principe serait le suivant : on saisie "call gensin". Si 'GenSin' existe déjà, le rapprochement est fait et on obtient " call GenSin". D'un autre côté, un label saisi avec au moins une majuscule changerait la casse de tous ses isotopes (nécessaire pour problématique import Maxam).

Unique "inconvénient", cela interdit la discrimination par la casse. Eg :

  • 'toto' en minuscule pour début de table, 'TOTO' pour fin.
  • 'x1' vs 'X1'

Mais qui diable ferait ça ? D'ailleurs je peine à trouver des exemples réalistes !

Sommaire

Il est prévu dans le futur d'avoir un accès rapide à des labels "privilégiés" : ceux commençant par une majuscule, ou tout en majuscule. Cela reste à définir (les votes sont ouverts).
Ainsi, on filtrerait tout les labels auxilliaires utilisant une minuscule initiale (loopY, ok_carry…) pour obtenir la liste des routines.

Numérotation automatique des labels (dans bloc répété)

(Où Tistou code un effet de vague masqué en quelques lignes)

Rappel 1: à l'intérieur d'une boucle, le pseudo_label # vaut l'itération en cours.
Avantages :

  • le '#' porte en lui la notion de numérotation.
  • non verbeux, et plus identifiable en tant que pseudo label.
  • surtout, extension simple en cas de boucles imbriquées. Eg, si 3 boucles, ### désignerait le compteur de la boucle externe, ## celui de la boucle intermédiaire et # celui de la boucle la plus interne.

Inconvénients :

  • moins explicite que __iter__ ?
  • plus difficile à trouver avec une recherche texte Non ! Il suffit d'entourer d'espaces le # dans la chaîne de recherche pour sauter les éventuels labels incluant ce caractère.

Problème, un label dans un bloc répété aboutit à une erreur de définition multiple, car tout se passe comme si le bloc était recopié à la main.
Une solution à ce problème est de numéroter automatiquement les labels avec l'indice de l'itération.
Non, la bonne solution à ce problème est d'utiliser un label local : comme pour les MACROs, la boucle insert un nouveau label parent implicite à chaque itération.

La numérotation automatique résout un autre problème : accéder à l'extérieur de la boucle aux labels définis dans la boucle.

[TODO] Choisir : syntaxe (voir l'existant)

Imaginons que step(#) se voit remplacé automatiquement par step0 step1 … dans le code suivant :

  size * [
        LD  a,(hl)
        OR  a          ;Transparent ?
        JR  z,step(#)
        LD  (de),a
step(#) NOP     ;replaced by INC H / DEC H / NOP
        INC L
        INC E
     ]

Cela permet de construire simplement une table d'index, avec les adresses de chaque label :

step_adr size * WORD step(#)

Proposition

Labels locaux.

Tous les labels considérés jusqu'à présent sont "globaux", c'est à dire visibles de n'importe quel point du source. Introduire des labels locaux présente deux avantages :

  1. Éviter les conflits de nom.
  2. Souligner l'aspect privé (interne et temporaire) de tel label : utilisé à l'intérieur d'une routine, mais non censé être appelé en dehors.

Le point 1 se révèle important dès lors qu'on inclue des sources divers. On veut éviter que le "loopY" d'une routine de sprite entre en conflit avec celui d'un affichage de ligne (voir paragraphe suivant).

Un label local reste accessible en dehors de la routine en préfixant par le label global associé. Eg :

drawline
    [...]
.loopY
.mask ld  a,#ff   ;Mask
      [...]
      dec iyl
      jr nz,.loopY

    ret

[Ailleurs dans source]

frame1
   ld a,#55 ;Test !
   ld (drawline.mask+1),a
   ld (texture.mask+1),a

[TODO] Distinguer label réellement temporaires (seraient inaccessibles hors de la routine) ? Pour quoi faire ?

Préfixage automatique

Afin d'éviter des conflits entre labels, on
Préfixer labels fichier inclus :

   include "music.o" as music

puis ... call music.init
         call music.depack_regs
         call music.play_regs

Syntaxe alternative (à choisir : l'une ou l'autre !)

music  include "music.o"

'bookmarks'

Ceux de TurboAss se révèlent trop rustiques pour devenir utiles :

  • Lié à une ligne plutôt qu'un label.
  • C'est idiot d'avoir une commande pour les lister, puis une autre pour y accéder, surtout qu'entre temps il faut repasser par le source, perdant la liste de vue. D'un autre côté la liste n'est pas très parlante !

L'idée serait donc de pouvoir bookmarker des labels, et prévoir une combinaison à trois touches :
CTRL B 0 : Bookmark 0
CTRL B 1 : Bookmark 1

L'astuce étant que CTRL B affiche la liste des bookmarks ! D'un point de vue réactivité, on ne doit pas attendre la fin de l'affichage (même si ça prend 1/10 de seconde) dès lors que le numéro est appuyé.
Idéalement, la liste affiche un peu de contexte (les deux lignes qui suivent le label). Ça prend de la place, mais il n'y a pas besoin d'une douzaine de bookmarks. Six suffiraient amplement, je pense.

Moniteur: saisie commande intuitive.

Coupler l'efficacité de Dams (1 commande = 1 lettre) avec la lisibilité de Maxam, c'est possible :
Les commandes possèdent un nom complet mais se trouvent accessible par leur initiale, à l'instar du Hacker.
Contrairement au hacker, avant même de valider, l'auto-complétion automatique (m -> mdump) permettrait de s'assurer qu'il s'agit bien de la commande attendue.
Nb:

  • l'appui sur delete doit alors effacer toute l'entrée.
  • on peut préremplir un paramètre (eg, reprise adresse pour mdump)

arrays

Would be useful for several settings.

E.g. for your VGA impaired graphist:

c = [&54, &44, &55, &5c    ... you know the list
; then
   palette BYTE c[0], c[1], c[4]  ...

Better yet, if you want to duplicate code while inserting special values

16 ** [  
         ; your RVI code here
         [...]
          ld c,barpal[#]       ; In first copy: barpal[0], then barpal[1]...
          out (c),c
         ; your PSG code here
         [...]
      ]

Last useful use, when injecting sprite data

height ** [
width **  [
             ld (hl),sprite[##*width + #]
             inc l
          ]
           'bc26' code here
          ]

To be perfect, we would load sprite data from a file. Which syntax to choose?
We cannot merely use binary include.

sprite
  LOAD "fsfs.win"     ; would include the bytes, which we don't want

scripting

Orgams makes it possible to assemble in firmware zone and page &C000
The same mecanism should be available from BASIC.

|orgbk,&c2  # connexion bank for Orgams point of view
|orgload,"binary",0
[...]
|orgsave,"fulldemo",start,size,exec

Or:
|orgrun,jump_adr

Includes

Customizing labels values (in include files) from main file.

One way would be to use label in included file and define it in main file.
BUT labels from main file shoudn't be visible from included files:

  • it contredicts the isolation
  • it allows name conflict. E.g. same name in both files.
  • the traditionnal 'C' solution is to allow to redefine labels. We absolutly do NOT want to go this way:
    • we would lose a nice property: each labels is defined at one place (we don't have to track updates).
      • it makes the code harder to follow. We don't need that in asm.
      • there are neater and cleaner ways to achieve the same results.
      • that's so 1960.

Instead, labels acting as parameters should be explicitly stated.

; In included file  tools.o

  get dev = 1   ; parameter with default value
  get height    ; mandatory parameter

That's already what's used for scripting. Cf above.

E.g. in BASIC.

|orgopen,"tools"
|orgset,"dev=1"
|orgset,"height=16"
|orgass

We must settle a similar syntax for include.
Candidate:

[code]
  INCLUDE tools (dev = 0, height = 32)

Note that it allows the 'dev' value used in included file to be distinct to the label 'dev' in main file (which just happens to has the same name).
And if wish to propagate the same value, just use:

  INCLUDE tools (dev = dev, height = fx1_height)

Issue:

  • all parameters must fit in a same line, unless we put the comma first:
  INCLUDE tools (dev = dev
                ,height = fx1_height)

No ! It's ok actually:
  INCLUDE tools (dev = dev,
                 width = fx1_width,   ; trailing comma distinguish from label definition
                 height = fx1_height) ; closing parent distinguish from label definition

Using included file's labels.

The main file can see all included file labels. But in a way these labels are locals to the included files, in the following sense :

  • If the same label 'routoutou' is used in both file,

Temporary conclusion

You can conclude that INCLUDing a source in *not* strictly equivalent to importing textually this file. We get best of both world :

  • no need to prefix labels (for example when including a set of MACROs)
  • no name clash.

Namespace

That being said, an ambiguity remains if you want to access a label present in two different included sources.
Then namespace can help. It would goes like that:

   INCLUDE "tools" as tools
   INCLUDE "player4" as player
   ...
   call tools.Init
   call player.Init
   ...
   call player.Iter

The prefix prevents name conflicts. E.g. all included files and the main file could use an 'Init' label.

Multiple files

When editor allows multiple editing, we might use CONTROL-TAB to switch between them.
Closing files would become handy. Which command/shortcut should we use? (CONTROL-W ?)

Convention for label naming/typing.

Nice conventions are good, and good conventions are nice.

  • It helps being consistent, and consistency is of greatest importance for legibility, keeping track of assumptions and avoiding bugs.
  • It allows static analysis. E.g. warnings, automatic memory map of your program, …

For instance, let's distinguishing (constants) from variables (in-memory).

   ld hl,(var_height)  ; ok
   ld hl,(para_height) ; KO: this constant doesn't refer to a memory address
   ld hl,var_height       ; ok
   inc hl:inc hl:ld (hl),0  ; KO: writting out of 'WORD'

para_height = 12     ; constant
var_height WORD 12   ; in-memory

How that assembler itself can tell the difference?
Solutions :

Standards prefix

Using fixed prefixes as in the example :

  • para_ (or const_)
  • var_ (or absence of prefix)
  • table_

Guess by case.

Use caps lock for all non-address constants.
Variables would be labels followed by BYTE, WORD, FILL or SKIP.

HEIGHT = 12
CHUNKS_BY_BANK = &30

onde1 = &4000  ; table (implicit length &100 since next table is at &4100)
onde2 = &4100  ; table (length = ???)
width WORD 48  ; variable

Sections

This one requires new directives, indicating which kind of labels your are defining.

  SECTION CONSTANTS
height = 12
chunks_by_bank = &30
  END

  SECTION TABLES
onde1 = &4000  ; (implicit length &100 since next table is at &4100)
onde2 = &4100  ; (length = ???)
  END

  SECTION VARIABLES
width WORD 48  ; variable
  END

TODO: use standard sections mechanisms, as found in large-scale assemblers ?

Include (User manual version)

Injecting source

This is done with INCLUDE directive.

   INCLUDE "source"             ; unneeded .o
   INCLUDE "source.s"           ; dams sources can be included
   INCLUDE "source.txt"         ; text sources as well

Some important points :

  • Includes can be nested
  • Labels from parent source are *not* visible from included source. This is by design. See next point.

next point

Injecting binary data

There are two ways to inject binary data, as a sequence of BYTE … would do.

  • INCBIN "sprite" [skip exp] [size exp] ; include data here
  • LOAD "sprite" [[dest ]exp] [skip exp] [size exp] ; load at arbitrary address in current bank connexion

Expressions can be negative, à la shell (cf head/tail), in which case the absolute value is subtracted from file size.

  • skip -100 means 'skip all but the last 100 bytes', i.e 'keep last 100 bytes'.
  • size -100 means 'total size minus 100 bytes'.

For example, if you want to skip both header_size first bytes and 20 last ones, you don't have to know the file size:

    INCBIN "texture" skip header_size size -header_size-20

Or:
    INCBIN "texture" skip header_size size -[header_size+20] ; Might be clearer@@

INCBIN vs LOAD

LOAD "3dscene" &cafe would be equivalent to:

current_pc $
current_obj = $$

       ORG $, &cafe
       INCBIN "3dscene"
       ORG current_pc, current_obj  ; go back where we were

So, we see that :

  • LOAD mimics the BASIC command. It can be used in monitor as well.
  • INCBIN behaves as standard assemblers. It would make no sense in monitor.

Monitor commands from source, exec-time vs assemble-time.

An intrinsic distinction between directives already exists:

  • RESTORE: once reached at exec-time, go back to the editor, whatever the state of the memory, CRTC, etc..
  • SAVE: at assemble-time, save memory zone.

Now, instead of a fixed set of directives we want to execute arbitrary RSX and Monogams commands.

  • At exec-time: e.g. to display a particular memory zone at exit.
  • At assemble-time: e.g. to compress code by using RSX

How to discriminate exec-time vs compile-time?

Candidates:

  • RUN command params
    • Happens at RUN-TIME
    • As its BASIC counterpart, convey the meaning of execution with possibly no return
  • ASS command params
    • Happens at ASSEMBLE-TIME
    • ASS is beautiful

Or:

  • EXEC command params
    • Happens at EXEC-TIME
    • EXECUTE the command
  • COMP command params
    • Happens at 'COMPILE-TIME'
    • COMPUTE the command

For exec-time RSX, how to deal with failure (RSX not found): BRK or silent skipping?

Good question

direct vs included

When creating a pack of macros/routine, we typically want to add unit tests inside the source.
But we do not want to get these tests when including the file. For now two solutions exist:

  • Don't do that (i.e. always put tests in separate source)
  • Use a flag (not very convenient)
; in tools.o

get tests = 1
   [...]
   if tests
      [...]
   end

; in main.o

   include "tools"  tests = 0

Being able to automatically discriminate whether we are included or not (à la __name__ in python) would be handy.
Maybe an __INCLUDED__ pseudo-label could do the job ?

optionally required, an oxymoron in 3 acts

The 'get' directive allows to pass parameters to source (when included or via script. cf: http://orgams.wikidot.com/v2boiteaidees#toc4
The 'get toto = 4' allows to set a default when the source is tested from orgams.

Since typically all the default will be set (in order to be able to assemble source), how to discriminate:

  • optional parameters, when we usually don't want to customize the sensible defaults.
  • required parameters, when we want client code to explicitly set the parameters in order to prevent errors, foresight or dry eye. Cf `tests` exemple in paragraph above.
Sauf mention contraire, le contenu de cette page est protégé par la licence Creative Commons Attribution-ShareAlike 3.0 License