ImportGrandDesign

IMPORT: plan de rut.

See CoderDoc for code style and other tips.

Prérequis 1: Multi-fichier.

On souhaite réutiliser la gestion multi-fichier lors d'un import.
Un fichier importé sera chargé en mémoire comme s'il était ouvert dans l'éditeur. Motivations:

  • Factorisation et simplification.
  • Facilite cache (le fichier ne sera pas rechargé à chaque assemblage).
  • Si une erreur est présente dans un fichier importé, on voudra certainement l'éditer de toute façon.
  • Réciproquement, si on est en train d'éditer un fichier, on veut que les dernières modifications soient vues par l'appelant. Voir règle 2 plus bas.

Choix de conception

Deux règles:

  1. Chaque source est identifié par son nom.
  2. Si un source est chargé en mémoire, c'est la version mémoire qui sera utilisée, et non la version disque.

Todo later: identification par chemin complet? (e.g. D:/ovl/tools.o).

Au plus 32 sources ouverts simultanément. Motivation:
* Deux fois plus que nécessaire.

Chaque source comprend des métadonnées (chemin du source lui-même, position du curseur, bloc définis, …). On stocke ces données dans un chunk de &100 octets. Si besoin de plus, l’adresse du chunk suivant est placée en début de chunk (principe de liste chaînée).

Type chunk ID:

MSB: MSB address of chunk in bank (in &40-&77)
LSB: Bank of chunk (e.g. &F4).g
Structure Source MetaData:

00-01:  Id next chunk (0000 if last)
02-0F: Reserved (checksum, internal flags, …)
10-4F: Codec infos
50-8F: Editor infos
90-FF: Source name / path (0 terminated).

new_chunk

new_chunk

; Allocate and connect a new chunk.

; in: Bk base connected.
; out: Carry if ok, 
          ; HL points to chunk start (XX00)
          ; A = bk chunk.
          ; Bk chunk connected.
     ; NC otherwise, A = error code (Memory full), HL trashed.

new_linked_chunk

new_linked_chunk

; Allocate a new chunk, link it to passed chunk

; in: HL points anywhere in the chunk
    ; Bk chunk connected
; out: Carry if ok,
          ; Previous chunk links to new one. 
          ; HL points to new chunk start (XX00)
          ; A = bk new chunk (could be the same).
          ; Bk new chunk connected.
     ; NC otherwise, A = error code (Memory full), HL trashed.

free_chunk_list

free_chunk_list
Generic routine to release a chunk and its linked chunks.
Warning! No verification is made to check if the chunk is in use or not. E.g. linked to by another node. In this latter case, the link dangles and bugs will ensue.
Also, if it isn’t a valid chunk but looks like one, the area will be wrongly 

; in: HL chunk id.
    ; Bk base connected
; out: Carry if ok,
     ; NC otherwise, A = error code (e.g. wasn’t a valid chunk).

insert_in_chunk

insert_in_chunk

Insert arbitrary data in chunk. If not enough room, it shifts the data to the next linked chunk. If there is no such chunk or it’s also full, a new chunk is allocated inserted in the list.

; in: HL: source (out of bank)
    ; DE: destination in chunk
    ;  C: size (0=256)
    ;  B: data start in chunk 
    ; Bk chunk connected
; out: Carry if ok,
     ; NC otherwise, A = error code (memory full, invalid chunk).

insert_in_source_chunk

insert_in_source_chunk

Like insert_in_chunk, expect B is set for you (B = 8)

copy_from_connected_chunk

copy_from_connected_chunk

Almost like a ldir, but automatically following linked chunk (when area to copy isn’t entirely contained in first chunk).

; in: HL: source in chunk
    ; DE: destination (out of bank) 
    ;  B: data start in chunk.
    ;  C: size (0=256)
    ; Bk chunk connected
 ; out: Carry if ok, HL and DE moved as in LDIR.
     ; NC otherwise, A = error code (invalid chunk).
     ; In any case, bk connection is preserved.

copy_from_connected_chunk_to_bk_base

copy_from_connected_chunk_to_bk_base

Like copy_from_connected_chunk, but copy in bk base instead.

; in: HL: source in chunk
    ; DE: destination (in bk base) 
    ;  B: data start in chunk.
    ;  C: size (0=256)
    ; Bk chunk connected
 ; out: Carry if ok, HL and DE moved as in LDIR.
     ; NC otherwise, A = error code (invalid chunk).
     ; In any case, bk connection is preserved.

connect_source_data_chunk

connect_source_data_chunk

Connect source meta-data given index. If no source was associated with such an index, a new chunk is allocated beforehand.
Rational: we want to read/write directly in chunk.

; in: A: index (in 00..1f)

; out: Carry if ok, 
          ; HL points to chunk start (XX00)
          ; A = bk chunk.
          ; Bk chunk connected.
          ; NZ if existing chunk, Z otherwise.
     ; NC otherwise, A = error code (Memory full, invalid index), HL trashed.

free_source_data_chunk

free_source_data_chunk

Free chunk list associated to given index. Needed when closing a particular source.
NB: it is easier to free and re-allocate via connect_source_chunk, rather than to try to reuse chunk since the number of chunks may vary.

; in: A: index (in 00..1f)

; out: Carry if ok
     ; NC otherwise, A = error code (invalid index, invalid code), HL trashed.

sourcename_to_idx

sourcename_to_idx

; Internal helper giving the index corresponding to the name of an opened source.

; In: hl=nt string (case insensitive)
; Out: Carry, NZ: Name found, A = index in 0..1F of corresponding source.
     ; Carry, Z: Name not found, A = index in 0..1F of free slot. 
     ; NC: Error (e.g. no slot left). A = error code.

Détails d'implémentations.

Chaque nom est stocké dans un chunk (cf connect_source_data_chunk) au même endroit (+&90).

Dépendences

Structure partagée avec set_sourcename_at_idx.

Need: connect_source_data_chunk

connect_source_data_chunk
; Dummy routine for test purpose:
; Return &4000 + index * &100
    ld h,a
    cp &20
    ld a,err_invalid_index
    ret nc

    set 6,h
    ld l,0
    ld a,&c4  ; Dummy, nevermind
    scf 
    ret

Routine comparaison (nt-string):

compare_nt_nocase:
; in: hl points to nt string
;   : de points to nt string
; out: Z if strings are the same (case insensitive).
       ; NZ otherwise.
       ; a, hl and de corrupted.

comp_lp
         ld a,(de):xor (hl):jr z,comp_ok
         cp &20:ret nz
         ld a,(de):and &df 
         cp "A":ret c
         sub "Z"+1:inc a:ret nc

comp_ok
         ld a,(de)
         inc de:inc hl
         or a
         jr nz,comp_lp
         ret

TODO mdr: donner l'adresse d'un buffer

NRTs

fail = &be00

test_nonexistant

      call org_init ; init générale
      ld hl,dummyname
      call sourcename_to_idx
      call c,fail  ; Expect NC
      ret

dummyname byte “toto”,0

set_sourcename

C'est le pendant (setter) de sourcename_to_idx

Interface

set_sourcename

; Internal helper association an index to the name of an opened source.

; In: hl=nt string
     ; A = index in 0..1F 
; Out: Carry. Ok, name has been registered at slot. 
     ; NC: Error (e.g. slot not free, invalid slot). A = error code.

Détails d'implémentations.

Chaque nom est stocké dans un chunk (cf connect_source_data_chunk) au même endroit (+&90).

Dépendences

  • Structure partagée avec sourcename_to_idx.
  • connect_source_data_chunk

NRTs

fail = &be00

test_set_sourcename

      call org_init ; init générale
      ld hl,dummyname
      xor a
      call set_sourcename_at_idx
      ld hl,dummyname
      call sourcename_to_idx
      call nc,fail  ; Expect Carry
      or a
      call nz,fail ; Expert 0
      ret

dummyname byte “toto”,0

org_select

Permet à l’éditeur ou à l’assembleur de basculer sur un source présent en mémoire.

Choix de conception

Le code appelant (éditeur, typiquement) sélectionne le source sur lequel travailler (stateful). Motivation:

  • Évite de devoir passer l'id à chaque routine (org_get_line, org_set_line, org_assemble …).
  • Reflète ce qui se passera en pratique : on bascule sur un source dans l'éditeur puis on travaille sur ce source spécifique.
  • Simplifie l'implémentation: on peut utiliser des adresses absolues pour les meta-données du source courant. Basculer de source se résume à un simple swap de ces variables.

Interface

org_select

; Indicate to the codec which source to use. All subsequents operations (org_insert_line, org_save ....) will refer to this source.

; In: A=index in 00..1F
; Out: Carry,  NZ: Switched to known source.
     ; Carry, Z: Switched to new source.
     ; In both cases, previous source is still in memory, and  
     ; NC: Error (e.g. memory full). A = error code.

Dépendences

  • new_chunk
  • copy_trans_bk
  • other

NRTs

test_select_simple

      call org_init ; init générale
      xor a:call org_select   ; No-op since source 0 is selected at init
      call nc,fail   ; sanity check
      call nz,fail   ; should be new source
      INSERT_LINE(1, dummyline)
      INSERT_LINE(2, dummyline)
      CHECK_NB_LINES(2)    ; sanity check

      ld a,1:call org_select   ; No-op since source 0 is selected at init
      call nc,fail   ; sanity check
      call nz,fail   ; should be new source
      INSERT_LINE(1, dummyline)
      CHECK_NB_LINES(1)    ; sanity check 

 ; Check we properly switch back to source 0
      xor a:call org_select   ; No-op since source 0 is selected at init
      call nc,fail   ; sanity check
      call z,fail   ; should be known source
      CHECK_NB_LINES(2)  

 ; Check we properly switch back to source 1
2 ** [  ; check idempotence
      ld a,1:call org_select   ; No-op since source 0 is selected at init
      call nc,fail   ; sanity check
      call z,fail   ; should be known source
      CHECK_NB_LINES(1)
   ]
      ret

TODO MDR: fournir les adresses à swapper.

org_select_or_open_file

Cette routine sélectionne le source dont on indique le nom, le chargeant s’il n’est pas déjà en mémoire.

choix de conception

  • En première instance, on simplement réserver 16 octets pour chaque nom.

Interface

TODO

Dépendences

En toute logique, utilise:
*sourcename_to_idx
*set_sourcename_at_idx
*org_select
*org_load

NRTs

test_select_or_open_file

      call org_init ; init générale
      ld hl,refname1
      call org_select_or_open
      call nc,fail   ; sanity check
      or a:call nz,fail ; Index 0 expected
      CHECK_NB_LINES(12)     ; assuming ref1.o is 12 lines long

      ld hl,refname2
      call org_select_or_open
      call nc,fail   ; sanity check
      dec a:call nz,fail ; Index 0 expected
      CHECK_NB_LINES(6)     ; assuming ref2.o is 6 lines long

; Check we properly switch back to source 0
      xor a:call org_select   ; No-op since source 0 is selected at init
      call nc,fail   ; sanity check
      call z,fail   ; should be known source
      CHECK_NB_LINES(12)  

 ; Check we properly switch back to source 1
2 ** [  ; check idempotence
      ld a,1:call org_select   ; No-op since source 0 is selected at init
      call nc,fail   ; sanity check
      call z,fail   ; should be known source
      CHECK_NB_LINES(6)
      ret

refname1 byte “ref1.o”, 0    ; Ce fichier doit exister sur le disque
refname2 byte “ref2.o”, 0    ; Ce fichier doit exister sur le disque

test_cache
; Sources mustn't be reloaded again.
; Basically, do the previous test x100.
;   * Same indices should be returned.
;   * Should be immediate.
;   * No memory full.

    ld b,100
test_cache_lp
   push bc
      call org_init ; init générale
      ld hl,refname1
      call org_select_or_open
      call nc,fail   ; sanity check
      or a:call nz,fail ; Index 0 expected
      CHECK_NB_LINES(12)     ; assuming ref1.o is 12 lines long

      ld hl,refname2
      call org_select_or_open
      call nc,fail   ; sanity check
      call org_get_lines#
      CHECK_NB_LINES(6)     ; assuming ref2.o is 6 lines long
    pop bc
    djnz test_cache_lp

org_close

TODO

Prérequis 2: Namespace.

Motivation: résout les conflits, permet d'être explicite.

On veut:

import “player”  ; look for player.o 
import “3d” as _3d

play [...]    ; local routine 

  call init  ; // assemblage error: init exists in both imports 
  call player.init ; ok
  call _3d.init    ; ok
  call play ; use local routine
  call player.play
  call get_psg   ; ok, found uniquely in player

TODO: unifier ça avec design labels locaux (.toto inside macro ou bloc répétés).

En gros:

  • open_namespace
  • set_label_value
  • get_label_value
  • close_namespace

A SUIVRE:
Discussion: consolidation labels.

Import: directive.

Voir la page “comment ajouter une nouvelle directive dans Orgams”.

Import: sémantique.

C’est le gros du travail, par l’assembleur.

  • sélectionne le source.
  • assemble-le si ce n’est déjà fait.
  • tout en empaquetant les symboles.
  • revoir la résolution de symboles:

** Si défini dans source: utilise cette version
** Sinon, recherche dans tables “import” (attention, ne peut pas utiliser l’id, on doit passer le label as is — sauf si remapping fait globalement).

TODO: décrire comment marche la table de symboles avant / après.
(nb table persistante pour monogams et trace.)

Sauf mention contraire, le contenu de cette page est protégé par la licence Creative Commons Attribution-ShareAlike 3.0 License