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:
- Chaque source est identifié par son nom.
- 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.)