|
Table des matières
|
Here are some of the pages of the User Guide!
Quick Start
- RUN "#BURN" (to install ROMs.)
- |M then ESC, or |O to go directly to the editor.
- CONTROL-H For online help, available in the editor, monitor, and debugger.
- CONTROL-I (Import) Loads a DAMS source file, Maxam (ASCII) or TurboAss (exported to ASCII).
- or: CONTROL-O to open an exemple source (.o files).
- CONTROL-1 To assemble.
- SPACE To return to the editor.
- CONTROL-4 To cycle through the current errors.
- CONTROL-S to save. CONTROL-O to open.
- CONTROL-2 To assemble & run.
Overview
Like DAMS Orgams breaks down into:
- Editor (with validation on the fly lines seizures)
- Monitor (aka "Monogams")
- Debugger (disassembly, trace step-by-step, etc …)
OrgamS is quite greedy and takes 5 ROMs.
- ORGAMS.ROM: Base Rom place in any rom position 1 to 15 (or up to 31 with the proper firmware).
- ORGASS.ROM: The assembler and routines to scan source (trace visu, CTRL-* …) placed at any rom position 1-127.
- ORGEXT.ROM: Expansion Rom placed at any rom position 1-127.
- MONOGAMS.ROM: Monitor placed at any rom position 1-127.
- BRICBRAC.ROM: Additional additional Rom at any position 1-127.
Several banks are being used (decreasingly), base bank (bkBase) being:
- C7 with 128k
- FF with 512k (allows cohabitation with 256k RAM-DISK).
The last 4 banks are entirely taken:
- bkBase: Housekeeping and internal buffers.
- bkBase-1: Firmware mirror and work zone.
- bkBase-2: Reserved for &c000 screen mirror (but not exploited now, completely wasted. See todo #f2).
- bkBase-3: Source start
Hence, with 128k (CPC stock + ROMs), there is less than 16k available for source, and only the base 64k for the program (that being said, all the memory is usable, including firmware zones, without perturbing Orgams).
If it's too little room, please use Demomaker'sDelight version.
Screenshots of monitor, editor and trace.
RSX
|ORGAMS:
/!\ Delete all sources and jump to monogams (the monitor)
You should never have to use this one, expect when orgams memory itself was corrupted (which should be warned about), to start afresh.
|ORG, |M, |OM
Jump to monogams.
|ORG,bank
Install Orgams from another bank (C7, CF, D7, DF …).
You should never have to use it. Only meant for private debugging purpose (multi-orgams in a single day).
|O, |OE
Jump to editor. Sources are persistent.
|O,"filename" |OE,"filename"
Load "filename" (".o" added if no extension) to free first tab, or tab 9 if no one is free, and jump to editor.
If tab 9 contains an unsaved source, it is proposed to save it (N to skip saving, ESC to cancel the whole operation).
Equivalent to: |ORGLOAD,"filename":|O
|ORGLOAD,"filename"[,tab]
Load "filename" in indicated tab (or free first tab if tab parameter is omitted) and returns to BASIC.
Again, it won't overwrite an unsaved source.
|ORGRESET
Clear all sources (proposing to save modified ones).
Pretty useful to create load a clean workspace:
10 |ORGRESET
20 |ORGLOAD,"journal",5
30 |ORGLOAD,"ply",2
40 |O,"plynrt"|TR[,address[,rom]]
Jump to trace at passed rom and address. Both parameters are optional
|ORGTAB,id,@status%
Select a tab without jumping to editor. Returned status is:
-1: Error (e.g. if memory full)
0: Ok, tab is empty
1: Ok, tab has source
## |ORGSET,"label",value
Set a label in the source of currently selected tab (for instance after |ORGLAD).
For some reason there is no status returned with this RSX.
It will display "Undefined label" if you try to set a label which doesn't exist in the source.
You can set a same label several times, for instance to assemble and save different experiments.
The values are sticky. Re-assembling from Orgams (CTRL-1 or CTRL-2) will continue to use the externally set value, even if you change it in the source.
To unset all values and restart from scratch, use CTRL-5 or CTRL-6.
/!\ Gotcha /!\ This only reset the set for the current source, not its dependencies.
|ORGASS,@nb_errors%,@start%,@size%,@exec%
Assemble the selected tab.
/!\ Gotcha /!\ This doesn't install the code in RAM, as it may conflict with the firmware. The code is just kept in Orgams's internal cache.
In this case, the RSX is meant to be used with |ORGSAVEBIN or |ORGJUMP.
If there is no conflict, you can use |ORGINSTALL.
|ORGGET,"label",@dest%
Get the value of an arbitrary label after assembling.
For some reason there is no status returned with this RSX.
It will display "Undefined label" if you try to get a label which doesn't exist in the source.
A garbage value will be returned for a MACRO label (without error message).
|ORGSAVEBIN,"filename"
Scripted equivalent of CTRL-1 + B.
|ORGINSTALL
Copy the assembled code to RAM.
/!\ Gotcha /!\ if the assembled zone overwrite BASIC or FIRMWARE one, a crash may occur.
|ORGJUMP
Install and jump to the assemble program as CTRL-2 does. No need to call |ORGINSTALL
Returns to BASIC once the program "RET".
|ORGPILOTED,"<commands>"
Experimental feature to pilot the editor without display.
I use it to save or export all sources, given they are in contiguous tabs:
10 ' All tabs from 1, until empty one is met
20 DEFINT a-z:'needed for rsx params
30 FOR t=1 TO 64
40 status = 0
50 |ORGTAB,t,@status
60 IF status < 0 THEN PRINT"error":END
70 IF status = 0 THEN END
80 |ORGPILOTED,CHR$(ASC("E")-&40)+"W"+CHR$(13):'CTRL-E (W)hole ENTER
90 NEXTComplete example
This is from a real life story. In Ayane context, to generate the compiled version of a music, I have to:
- Assemble the player with a given set of parameters. This could be done by modifying those parameters in-source (playconf,i), but it's cleaner and easier to have distinct scripts for distinct sets of parameters.
- Feed the ayane compiler with some addresses only knowable once the player is assembled. Previously that has to be done by manually copying those addresses, which is tedious, error-prone and non-automatisable.
100 PRINT"Compile player + data for kim"
110 DEFINT a-z
120 compiledplayer = &411C
130 compiledinstrbuf = &7E00
140 compiledstack = &7F00
150 ' ----- Setup player paramaters -----
160 |ORGLOAD,"plyconf.i",1
170 |ORGSET,"ayane",0
180 |ORGSET,"pattern_size",&60
190 |ORGSET,"compiled_player",compiledplayer
200 |ORGSET,"compiled_instr_buf",compiledinstrbuf
210 |ORGSET,"compiled_stack",compiledstack
220 ' ----- Load sources of interest in explicit tabs -----
230 |ORGLOAD,"instr.o",2
240 |ORGLOAD,"compile.o",3
250 |ORGLOAD,"justcomp.o",4
260 |ORGLOAD,"ply.o",5
265 ' ----- Assemble and save player -----
270 nberr=0
280 start=0
290 size=0
300 exec=0
310 |ORGASS,@nberr,@start,@size,@exec
320 IF nberr THEN STOP
330 |ORGSAVEBIN,"out/plykimc3.bin"
335 ' ----- Get addresses of interest -----
340 play=0:|ORGGET,"play",@play:PRINT"play:";HEX$(play)
350 psg=0:|ORGGET,"psg",@psg:PRINT"psg:";HEX$(psg)
360 init=0:|ORGGET,"Amorce_song",@init:PRINT"init:";HEX$(init)
370 id=2:GOSUB 600
380 null=0:|ORGGET,"instr_null",@null
390 default=0:|ORGGET,"instr_default",@default
400 chipvib=0:|ORGGET,"instr_chip_down_vib",@chipvib
410 chiparp=0:|ORGGET,"instr_chip_down_arp",@chiparp
415 ' ----- Pass them to compiler -----
420 id=3:GOSUB 600
430 |ORGSET,"compiled_instr_null",null
440 |ORGSET,"compiled_instr_default",default
450 |ORGSET,"compiled_instr_chip_down_vib",chipvib
460 |ORGSET,"compiled_instr_chip_down_arp",chiparp
470 id=1:GOSUB 600
480 |ORGSET,"ayane",1
485 ' ----- Assemble and run compiler -----
490 id=4:GOSUB 600
500 |ORGASS,@nberr,@start,@size,@exec
510 IF nberr THEN STOP
520 |ORGJUMP
510 ' ----- Save compiled module -----
530 datastartpnt=0:|ORGGET,"data_start",@datastartpnt
540 datastart=PEEK(datastartpnt)+PEEK(datastartpnt+1)*256
550 datasizepnt=0:|ORGGET,"data_size",@datasizepnt
560 datasize=PEEK(datasizepnt)+PEEK(datasizepnt+1)*256
570 |SAVE,"out/kimc3.aya",datastart,datasize
580 END
590 ' ----- Helper: swtich tab -----
600 status=0
610 |ORGTAB,id,@status
620 IF status<>1 THEN break
630 RETURNFlow between modules
Here, "Exec" represents the assembled program running.
From Basic
See RSX section.
The current source is checked against memory overwrite than might have happened when using other tools. E.g. Parados doing a destructive memory scan.
(TODO: Do the check for all tabs, not only the current one).
From Monitor
- ESC: Goto Ed
- Order 'basic': (Back) Basic.
- Command 'DEXP' Goto debugger
From Ed
- CTRL-F2: If ok, Goto Exec
- ESC: Goto Monitor
From Exec
- RET: as RESTORE (but the battery must be valid)
- RESTORE Restores system and Goto Ed.
- BRK / rst &30 / call &BE00: Goto Trace
From Debugger
- CONTROL-ESC: Goto Editor to visualized line.
- ESC: Goto Monitor
- J: Goto Exec
Programming session example:
- Tweaking opcodes via the editor.
- Rather than a mere RET, use RESTORE or BRK to exit your program from anywhere (no need to restore SP).
- Save source.
- Test by CONTROL-2: it assembles the code and run the program.
- CONTROL-4 goes directly to the reported errors during assembly.
- The monitor allows to study all memory (except the screen memory this is used by Orgams' display).
Editor
Enter the editor using |O from BASIC, or by pressing ESC in the Monitor.
Leave the editor by either pressing ESC, launching assembly + execution (CTRL 2), or by resetting the CPC.
The current session persists across resets, with source validity check.
Status board (New in EE Beta H)
The status board displays the following information:
- Line 1
- Path in source (in which routine and IF block the cursor currently is).
- Line number / total lines.
- Line 2
- List of opened sources (a.k.a. tabs)
- Line 3
- Dump of current line, with the difference with original code highlighted.
- Block related information (e.g. machine time taken).
- Number of Hectibytes free (like in Monogams)
- Line 4
- Questions and results of actions.
- Banks used by Orgams (like in Monogams)
- Line 5
- Internal debug information.
It looks like something like that ('.' for border):
....................................................................................................
....................................................................................................
.. dev=PRELUDE.GenTexture.loopx.dbg! 133/ 945 ..
..1 orgch 2*crtc 3*cpct 4* 5* 6 nrt 7 player 8 bulgur 9 leprous ..
..1203: 3E 04 09 90 CA 34 FE 32 100..122: 32 bytes, 64 NOPs 123 Hib free ..
.. Found: string Used: 7fdf-ff ..
.. Minidump (activate with CONTROL-V)
..
.......... ; This is the actual source ..........
.......... ; idem ..........Import sources from other assemblers
The first thing you want to do is to listen to Solefald.
The second will be to import your old tatty sources (see "Write a technical manual for Dummies", how to create "user stories").
Via CONTROL-I, Orgams automatically recognises source from DAMS and other ASCII sources (TurboASS, Maxam or ORGAMS).
The various adjustments will be made automatically for example:
- 'NbDots EQU &1000' becomes 'nbDots = &1000'
- 'Defs 100,nbCols%3' becomes 'FILL 100,nbCols MOD 3'
The operation is very slow because much of the pre-assembly and symbol resolution is done at this time.
Possible differences. !!WARNING!!
Modified Arithmetic.
Example:
- LD A, pal&#ff+1; (pal and #FF) + 1
Becomes:
- LD A, pal AND &FF+1; pal and &100
Indeed, as &ff+1 is visually grouped, it is therefore calculated separately.
It must be corrected to either of these:
- LD A, pal AND &FF + 1; Direction left to right
- LD A, [pal AND &FF]+1
ORG behavior
When DAMS assemble with A2, each ORG changes the PC ($) but not the storage address ($$). However ORGAMS does
not allow this mode. To reproduce the same action it must be explicitly replaced:
- (DAMS) ORG dest
- (ORGAMS) ORG dest, $$
Checking
In Monogams, use the X command to compare the assembled code with your referral code.
Limitations
- Editing of lines is limited to 72 characters. If asked why, you will say that you do not know.
Hotkeys
Keyboard shortcuts are described in the online help (press CONTROL-H).
We concentrate here on more sophisticated.
CTRL C Displays the catalog of the disk.
CTRL L str
Go directly to the next label beginning with "str". This is equivalent to the command 'the DAMS, with the following improvements:
- Case Insensitive
- You can iterate through all labels beginning with "str". Just press CTRL-L again then RETURN to move to the next or just press TAB.
Starting with release FF, you can auto-complete the labels (thanks Toms and Cwiiis), with the following caveat:
- The labels are not sorted
- Ghost labels are shown (bug#129)
When you validate a suggested label with CURSOR RIGHT, the exact label is searched.
Afterwards, TAB from the editor meaning "repeat last search", it will go to this label definition (rather than iterating labels which share the prefix).
CTRL * (TAB to continue the search)
Go to the next occurrence of the label under cursor (reminiscent VI).
If there is no label under cursor, it takes the first label from this position(*). If there is no such label, it takes the closest to the left.
E.g.
fx_dispatch call routine; CTRL * will go to next occurrence of 'fx_dispatch' if the cursor is over
; or next occurrence of 'routine' in all other cases.(*) Why the label to the right? An article published in "Psychological Science in the Public Interest" shows a tendency to focus on the label following the cursor rather than the one preceding it. This effect is amplified if the cursor is an instruction (e.g. 'call'), but reversed when the guinea pig just read a verse from the Koran.
CTRL Enter
Go to the definition of the label closest from the cursor.
If the latter is itself a definition, then we take the next.
It also works for imported labels (since HH beta 6), must the source must be assembled without too much errors.
E.g.
fx_dispatch call display3D ; CTRL-ENTER go to 'display3D' whatever the cursor positionTrying is believing, as we say in Denmark.
CTRL Return
Back to previous position (after Ctrl Enter). It works on 8 levels.
As I am not dog (*), I give you a mnemonic: "Enter routine", "Return from routine."
(*) Not that dogs be petty, but they seldom write user guides.
CTRL M
Cycle through the last 8 rows changed. Very handy, I find, having wandered in the source to return to last edits.
CTRL P
Paste the last line cut with CTRL-DEL. UNDO serves rustic but also allows you to duplicate a line faster than the blocks. In this case, must be deleted to better re-insert!
CTRL F (Find) (TAB to continue the search)
Text Search.
- Case insensitive
- Indifferent to the tabs (unlike TurboAss where you have to enter the exact number of spaces to search mnemonics with operands).
- By default, exhaustive search.
- Precede the search string with a space if it is desired to search only at the beginning of words.
- End the search string with a space if one wishes to look only at the end of words.
- Surround the search string with spaces if you want exact search for the word alone, akin to `\b` in regex syntax.
- In all these cases, the space at the beginning and at the end is not wanted literally, as shown by the following examples.
| V Searched / In source -> | OR A | LD HL,orang | LD HL,Cador | LD HL,morel + 4 |
|---|---|---|---|---|
| "Or" | * | * | * | * |
| " Or" | * | * | ||
| "Or " | * | * | ||
| " Or " | * |
CTRL % (CTRL-+ on QWERTY) Cycle through IF-ELSE-END MACRO-ENDM [-]
If the cursor points to IF, this shortcut will jump to the corresponding ELSE (if present) or END.
And vice versa.
Same applies to macros and repetitions blocs.
If used on a line without any of those directives, it will jump to the next one of them.
CTRL B + key: Register bookmark at current line.
A maximum of 16 bookmarks are remember (and stored in .o file).
SHIFT CONTROL key: Go to corresponding bookmark.
Then CONTROL RETURN will return where you were.
/!\ Gotcha
Bug #184 [Editor] Bookmark not registered for key Z (Qwerty) W (Azerty)
CTRL |
Invoke an RSX. The history of entered RSX is acceeded with cursor UP/DOWN.
Same limitations than RSX entered in Monogams.
COPY S / COPY E
Select a bloc with COPY S (start) and COPY E (end).
It will show the time taken by the instructions in the bloc. See [#profiling].
/!\ Gotcha
If a very large bloc is selected, Orgams may hang.
COPY C
Copy the selected bloc at the current cursor position
COPY DEL
Delete the bloc.
Assembler Shortcuts
We reserve CONTROL+number (keyboard row, not function keys) for all assembler related processes (presumably fewer). This allows:
- Avoiding conflict with pure edition shortcuts.
- Using and remembering them more efficiently (less cognitive interference, to speak like my concierge).
- These shortcuts are achievable with one hand, freeing the other to smoke a Cuban.
CONTROL 1: Assemble
Saving options
After a successful assembly, press A, B or S to save all files specified by SAVE directives.
- A: Ascii (headerless) (override SAVE and turn it into SAVEA)
- B: Binary (override SAVEA and turn it into SAVE)
- S: Save as directived (respect SAVE/SAVEA).
If no such SAVE directive in your source, it will save the whole code instead.
/!\ Gotcha in this latter case
- From HH-Beta 2: The last connected bank (by BANK directive) will be used
CONTROL 5: Reload (when LOAD directive used) and Assemble
CONTROL 2: Assemble and Jump = CONTROL-1 + J
CONTROL 6: Reload (when LOAD directive used), Assemble and Jump
When no ENT is used, the jump address is the first ORG.
CONTROL 4: Go to the next assembly error.
Assemble and jump context.
Program is launched with the following setup:
- Firmware reinstalled as it was at Orgams invocation (From Himem+1 to &bfff),
- SP reset, around &BFDA. /!\ Gotcha, this is below typical value when a program is RUN from BASIC (&BFFA).
- Correct firmware values for AF 'and BC'
- The other registers are unspecified
- IM 1
- DI
- LRAM, URAM
- Bank &7fc0
- ASIC page cleared (since HH Beta 1)
Rationale:
- Stability/Reproducibility. We want the context for each execution to remain the same than the first one. The system state is restored, not when your program returns in order to allow to analyse the whole memory, but when each time the source is assembled. Precisely, before the source is assembled, so that you can assemble stuff in firmware area.
Assembler
Compiler directives
Org X [,Y]
Or how to generate a Y address of the code to be executed by X.
We distinguish:
* Code pointer (pseudo label $), the address where the code is supposed to run (ie $ almost plays the role of PC)
* Object pointer (pseudo label $$), the address where the code is stored by the assembler.
In the most common scenario, the two coincide, and the ORG X allows to change this address.
Used with two parameters fixed ORG $ and $$ separately.
The following code:
ORG &1000, &2000
loop JP loopgenerate the code C3 00 10 at &2000.
To change $ only:
byte message "Hello dad!", 0
ORG &C000, $$
; the code is stored after post
toto; this label is &C000To change $$ only:
ORG $,&4000
/!\ Gotcha
For now, ORG doesn't check for overlapping areas. If you want to set several ORGs but check for overlapping, use SKIP instead:
ORG &BC00
; lots of glorious code here
,..
; Will set $ to &BD00, do nothing if $ already &BD00
; and raise an error if $ > &BD00
SKIP &BD00 - $
;Or FILL &BD00 - $, 0 ; to clear the gapAlternative:
ORG &BC00
; lots of glorious code here
ASSERT ($ <= &BD00) ; See ASSERT macro
ORG &BD00Known bugs
Assembled code in &30-&32 is overwritten by JP &BE00 (Breakpoint mechanism).
ALIGN(n) (MACRO example)
Advance $ to the closest multiple of n.
If $ is already a multiple if n, the directive doesn't change anything.
NB: $$ is incremented by the same amount
There is no ALIGN directive. It can be simulated by:
MACRO ALIGN n
SKIP -$ MOD n
ENDMExplanation:
We want to skip x bytes such that $+x = 0 [n] and 0 <= x < n
This amounts to x = -$ MOD n
ASSERT(predicate) (MACRO example)
Raise an error if predicate isn't true.
There is no ASSERT directive. It can be simulated by:
MACRO ASSERT predicate
IF predicate:ELSE
!! ERROR !!
END
ENDMExplanation:
The '!! ERROR !!' part is not seen at all if predicate is true,
otherwise it triggers an assembly error.
Example 2:
MACRO ASSERT_NOT predicate
IF predicate
!! ERROR !!
END
ENDM
[...]
ASSERT_NOT (my_table AND &ff) ; Ensure my_table is &100 alignedLIMIT n (MACRO exemple)
No such directive for now, but here is a workaround.
The difference with a proper directive is that we must place the macro invocation after the code that mustn't overlap.
/!\ Gotcha
Doesn't work for &FFFF, as $ would wrap to 0.
MACRO LIMIT last
ASSERT ($ <= last+1) ; See ASSERT macro
ENDMAfter your code, add:
LIMIT(&3fff) ; To protect overlapping at &4000MINIM n (MACRO example)
The opposite of LIMIT, to ensure some part of the code has reached a certain address. Useful when you are memory-tight and want to reuse some area.
In the following example, a table is created, but not until a certain point. So disposable initialisation code can be placed in this area.
MACRO MINIM address
ASSERT ($ >= address) ; See ASSERT macro
ENDM
ORG &2000
table SKIP &200
table_end
table2 SKIP &100
table2_end
ORG &2000
ENT $
; Lots of init code here,
; only executed once (disposable0
; ...
call make_table
MINIM(table_end) ; From here, musn't overlap with table
; Other code
; ,,,
call make_table2
ASSERT ($ >= table2_end) ; You may also use ASSERT macro directly.BANK n
Directive BANK n re-configures the memory similarly to OUT &7FFF,n.
So,
BANK &C4
ORG &3F00
[...]; &200 bytes of tasty codegenerates code both in main RAM and "&C4".
While
BANK &C2
ORG &3F00
[...]generates code both in banks &C4 and &C5.
The connections &C1 and &C3 also behave as expected. That is:
BANK &C3
ORG &4000
label BYTE "toto" ; generate code at C000, yet label = &4000
ORG &c000
BYTE"tutu" ; generate code at C7:4000
[...]BANK &B8 / &A0 : Asic page
Only for Amstrad PUS. New in HH Beta 4
You can define sprites and setup other stuff.
It will be installed just before execution (CTRL-2 or CTRL-1 + J).
/!\ Gotcha
Don't setup fancy stuff that might interfere with Orgams' internal process pre-execution.
BANK &B8
ORG &4000
LOAD"hiresbob.bin"
ORG &6000
WORD 10 ; Pos x
WORD 20 ; Pos y
BYTE &0f ; Magnification x4
BANK &A0 ; reconnect previously selected bank (C0 by default)That being said, why use PLUS when we are only scratching the surface on CPC?
BYTE and WORD
The BYTE command accepts strings and bytes, possibly mixed:
BYTE 12,"ABC",-1 Encodes 0c 41 42 43 ff
A "BYTE" or "WORD" without a value increments pointers ($ and $$) without writing into memory.
Three possible use-cases:
- Reuse the current value in memory (eg: setting that you want to keep from one run to another, even in the code re-assembling in between).
- A purpose of documentation, note that the value does not need to be initialized by the assembler (eg: variable anyway initialized at run-time).
- Easily define positions in a structure. Eg:
ld a, (ix + pat_flags) Give ld a, (ix + 2)
ld e, (ix + pat_value) Give ld e, (ix + 3)
ld d, (ix + pat_value + 1) Give ld d, (ix + 4)
ORG 0
pat_pos WORD ; Do not write anything at 0!
pat_flags BYTE
pat_value WORDSKIP n
Skip n bytes without emitting any code.
After SKIP:
- $ := $+n
- $$ := $$ + n
Equivalent to n ** BYTE, faster to assemble.
LOAD"filename" (aka INCBIN in other assemblers)
Load binary or headerless file at current $$ destination.
After load:
$ = $ + filesize
$$ = $$ + filesize
/!\ Gotcha
The file is only loaded once, after the cached version is used.
To reload all files, use CTRL-5 or CTRL-6
This directive doesn't take address parameter. Instead, do for instance:
BANK bk_sprite
ORG sprite
LOAD"bob.bin"SAVE"filename",start,size,[start',size' *] [,exec] (New in HH beta 2)
- Register to save what has been assembled in memory (hence rely on $$ pointer, not $).
- The actual saving happens when you press A (headerless), B (AMSDOS binary) or S (AMSDOS binary as well, unless SAVEA is used) after CTRL 1 or CTRL 5
- If no exec is specified, it will take the address defined by ENT.
- The directive can be placed anywhere in the source, since actual saving takes place after the whole code is assembled.
- You can use BANK &C2 to save bytes overlapping in several banks.
You can concatenate data by using several start, size pairs:
bk_data = &c5
ORG &1000
ENT my_execcode0 ; !! Use code0 = $$ if $ != $$
; Your code
; …
size0 = $$ - code0
data0 ; !! Use code0 = $$ if $ != $$
BANK bk_data
LOAD "INFBOOBS.BIN"
LOAD "MAP3D.BIN"
datasize0 = $$ - data0
; … Other stuff
BANK bk_data ; Ensure we save the right bank
SAVE"glup",code0,size0,data0,datasize0
; Will save glup with Start=code0, Size= size0 + datasize0, Exec = my_exec
; If you want a different start address, use:
; SAVE"glup",my_start,0,code0,size0,data0,datasize0
[[/code]]
/!\ Gotcha
- it will save the data from the bank selected when SAVE is met, or lac. And &7c0 if you don't use BANK directive.
- Saving is pretty slow
- Only SAVE/SAVEA directives in host source are considered, not in imported files.
"Warning: Area not covered by source"
When you get this message, it means part of the saved area wasn't produced by the assembler.
Then whatever is in the user memory will be written instead.
It can happen in the following cases:
- Use of BYTE / WORD without parameters or SKIP.
- Error in start address or size.
- In the example just below.
Saving generated data
Sometimes you want to save some data generated by your program, not your program itself.
Well, you could use the |SAVE RSX provided by Unidos or Utopia. But ithere is a flaw: all RSX calls from Orgams reinstall the firmware and connect the bank &7fc0.
Let's say you want to save bank &7fc4 and bank &7fc7 concatenated (32k total). Just open another tab and execute:
BANK &C2
SAVE"glup",0,&4000,&c000,&4000You will get the aforementioned warning, since those areas weren't assembled by this very source, but it will do the expected thing.
Saving without SAVE
If not SAVE directive is specified, ORGAMS propose to save the whole source (the last connected BANK will be used).
SAVEA"filename",start,size,[start',size' *] (New in HH beta 2)
Like SAVE, but produce an headerless file when pressing "S" after assemblage.
Struct
Well, the is no such directive (not yet).
You can emulate it partially with :
savePc = $ ; Not needed if such definitions placed before the first ORG
saveObj = $$ ; Not needed if $$ = $
org 0
point_x BYTE
point_y BYTE
point_z BYTE
point_rgb SKIP 3
point_size
org savePc, saveObj
; ... Later in source
ld ix,point
inc (ix+point_z)
; ... Instantiate the struct
point FILL point_size,0RESTORE
Use that to go back safely to Orgams instead of RET even if everything is thrased, SP modified, etc.
Restore the system, the battery, the oil level before returning to the editor.
The directive takes a handful of bytes. If you are tight in memory, use BRK instead.
- New in HH Beta 1: Also clean ASIC page.
IMPORT"lib.o"
Introduced 25 Sept 2021 in FuriousFlags alpha 8.
- /!\ Warning /!\ This is currently a restricted implementation, where you cannot pass parameters to imported files. That sometimes limits the usefulness of import, might be corrected soon.
- It's slower than equivalent monolith source.
What is it good for?
- Creating a library of common and/or fixed routines. That is, code that is unlikely to change and that has a clear interface, akin to firmware’s “vector”.
- Isolating data that is slow to generate. E.g. 256 ** BYTE SIN(#)….
The directive "imports" lib.o is not a literal inclusion of the source like with INCLUDE or READ in some other assemblers, or with the editor's import command (CONTROL-I). This is on purpose!
- The imported source doesn't see the host's labels. If error-free in isolation, it remains error-free as an import (this good property isn’t guaranteed with textual inclusion).
- The host will see imported source's labels as expected. The differences are:
-
-
- If the same label exists in both sources, the host’s one takes precedence, shadowing the other one. The rationale is that private labels (that is, global labels in lib not meant to be exposed) shouldn’t impact the host. Also, it helps keeping assembling ultra fast.
- There will be a way to explicitly access an imported label: lib.label (not implemented yet). This is required to resolve ambiguity when two libs use the same label.
-
-
- If B and C both import D, and A imports B and C (diamond configuration), D will only be included once. No header guard gymnastics to do. Rationale:
-
- If D only consists of definitions or macros, that doesn’t change anything.
- If D contains routines, there is no reason to duplicate them.
-
- Since the imported file doesn't depend on the host, we only need to assemble it once. Subsequent assemblages will re-use the cached version. To be exact, it depends on the current $ and $$ position. If the import has changed position, it will be re-assembled. That's another reason it's recommended to put all the imports at the beginning of the source and/or after an ORG directive.
As said above, for now it's not possible to pass flags or values to the imported source.
In the future, it will be allowed by explicitly passing parameters:
In lib.o
get dev_mode = 1 ; Parameter with default value
get target ; Mandatory parameter.In host source, something like
IMPORT "lib" [set dev_mode 0]In summary, the effects of IMPORT "lib" are:
- generating code (or not, if IMPORTed source was already met).
- changing $, $$ accordingly (*)
- changing bank if the directive is used (in other words, you don't get back the bank selection you had before the import, very much like $ isn't equal to the address before the import !). Not recommended anyway. The main source should be piloting ORG and BANK, not the libraries.
(*) Beware, $ and $$ are either:
- Unchanged (when IMPORTed source met for the second and subsequent times).
- The value at the end of IMPORTed source, as if the source was textually inserted. That means the latest values, not the highest (Note: latest = highest anyway if you don't use several ORGs out of order).
In the following situation, the code at &200 will be overwritten without warning (due to bug #a9).
; lib.o
org &200
; [...] some code or table
org &100
fill &100,&f7
; $ and $$ = &200 here; main.o
IMPORT "lib"
; $ and $$ = &200 here
call init ; oups, overwrite code &200 from lib.OAnyway, once again, it is not recommended to use ORG at all in sources meant to be imported.
Imports and editor
All imported sources are loaded in free editor tabs.
Rationale:
- They must be in memory anyway, we might as well make them visible.
- In case of errors, give the opportunity to fix them.
- Allow to use the main source as a multi-sources "project".
Importantly, when changing something in imported source, this in-memory version is used, not the one on disc.
Rationale:
- That's what we want: changes to be reflected in assembly, without having to save and reload.
WILL BE IMPLEMENTED LATER
- making lib's label accessible via full qualification: lib.zolabel
IMPORT "crtc"
IMPORT "ui"
IMPORT "player"
call init ; will use `init` below.
; [...]
init ; global init
call ui.init ; no ambiguity
call player.init
ret
; [...]
call player.iter
crtc.SET(4, 0) ; Use macro "SET" defined in crtc.o
call player.psgLabels
- Automatically detects invalid references
- No limitation in size (the name of labels being stored once, do not hesitate to choose long)
- Starts with a letter, followed by letters, numbers, or the following characters _ #'
Local Labels
Introduced in Fluffy Flages version.
When using a label in a Macro or a repetition block, it leads to double definition error.
Solution: local labels! That's just your regular label prefixed by a dot (.).
You can repeat it, as soon it's in distinct scopes.
Now, what defines a fresh scope ? Well, a macro invocation, a block iteration, or a normal (global) label.
This allows:
4 ** [
...
bit 7,h
jr z,bit7off ;
.bit7on
...
jr endBit7
.bit7off
...
.endBit7
...
]ou encore
MACRO ADD_HL_A
; A trashed
add l:jr nc,.nc:inc h
.nc ld l,a
ENDMAs you may have noticed, the dot is optional when the label is referenced.
It is recommend anyway: it's a bit clearer, and avoid assembly error if a global label with the same name happens to exist.
In summary:
hu
toto
inc l:jr nz,hu:inc e ; Ambiguous since global "hu" also exists
inc l:jr nz,.hu:inc e ; correct: explicitly local
inc l:jr nz,ho:inc e. ; correct: no global "ho"
.hu
scf
.hoKnown limitations.
- No such thing as local a EQU. Let me know if you need that.
.toto = 10 ; Won't work- CONTROL-ENTER in the editor might go to the wrong instance.
Digital Expressions
True sign management.
A step = -1 does not encode &ff nor &ffff, but indeed -1. Thus, LD A, step*step passes without worries.
Therefore, an error in an overflow indicates a real problem in your program.
Extended Arithmetic
Labels can take the signed 24-bit values. Authorizes for instance:
ram = &40000; yes, 256k
chunksNb = 16;
chunkSize = ram / chunksNb;Expressions and temporary results can reach 1024 bits.
ld, ram*ram / &100000000; ok, it's 16Expressions
- Warning: No operator precedence!
Sequencing is made only by spaces and square brackets.
- Examples:
LD,1+2*3; 9 (+ and * made in order)
LD,1+2 *3; 9
LD,1+ 2*3; 7 (2 * 3 form an isolated group, calculated separately before adding 1)
LD 1+[2*3]; 7 Idem, more classic.Rationale:
- Simple rule. Nothing to remember. Visually consistent.
Logical operators and modulo in full (and, xor, or, mod)
Easy to remember.
Repeat
An instruction or a block can be repeated. The advantages are obvious:
- Compact Plus (to read, write and store)
- The number of repetitions is controlled by a label.
- "Defs" on steroids:
16 ** byte 1,2,3; 1,2,3,1,2,3,1,2,3 ...For a block repetition, '[' should be on the same line:
; Good
NbLines ** [
INC B: OUTI
INC B: OUTI
EXX
OUT (c),e: OUT (c),d: OUT (c),0
EXX
]
; bad
NbLines **
[
INC B: OUTI
INC B: OUTI
EXX
OUT (c),e: OUT (c),d: OUT (c),0
EXX
]Of course, any label within a block repeated more than once will throw an error (double definition).
Expressions are re-evaluated at each iteration. This facilitates the creation of tables:
ORG &9000
rampe3 256 ** BYTE $*3 and &FF ; stores 00 03 06 09 ... FF 02 05 08 ...Warning!
4 ** LD A, (DE): INC E; buggy. Only 1 E INC
4 ** [LD A, (DE): INC E]; OK.It allows up to 8 levels of nesting. For more, request exemption from the nearest clinic.
Pseudo labels #, ##, ###
The pseudo label # is the index to the containing iteration (starting at 0).
The pseudo label ## is the index of the iteration at the upper level (when several nested repetition, of course).
Etc.
2 ** [
3 ** BYTE &10*## + # ; stores 00 01 02 10 11 12
]Separator ":"
- Used to group instructions that form a logical operation, and to better include the similarities. Eg:
LD hl, awesomeness: inc (hl)
LD hl, brightness : inc (hl)
LD hl, ovlness : inc (hl)- Less line = better overview on a routine.
- Essential for coming pseudo-labels.
Conditional assembly
Orgams allows 8 levels of IF ELSE END. To consume without moderation, with moderation.
Error messages
Ambiguous local vs global
Happens when a referenced label is both local and global.
hu
toto
inc l:jr nc,hu:inc h ; incorrect since global "hu" also exists
inc l:jr nc,.hu:inc h ; correct: explicitly local
.hu
tata
jp hu ; correct since we are not in toto scope anymore.To fix it:
- Rename one of them.
- Or prefix by a dot "." like in the exemple above.
(30) Import: ambiguous label. (Introduced in FF alpha 8)
When a label is missing in host source, Orgams searches for it in imported sources.
If the label exists in several of them, Orgams cannot know which one to pick (if there are equal, no problem though).
How to fix?
- For now, you'll have to rename one of them.
- In the future, you be able to fully qualify the label. E.g:
```
IMPORT "threeD" ; Contains Init
IMPORT "player" ; Also contains Init
[…]
call threeD.Init ; Unambiguous! But doesn't work for now.
```
Reminder: if a label exist both in host and imported sources, it's not considered ambiguous. The host label takes precedence.
Ambiguous Import.
When trying to import <filename>, two or more tabs are using the same filename.
Solution: Rename or close the duplicates.
Self Import.
A source is trying to import itself. That's likely a typo.
Double defined
A global label is defined twice.
Or a local label is defined twice in the same scope.
Or a label is pulled from imported source, and defined in host after its use. See bug #1ea
Source examples
Defining contiguous variables
Sometimes we want to define the adresses of tables and variables without emitting any code. For instance when writing a program for ROM, where such variables aren't part of the code to generate.
An oldschool way of doing that is:
; Beurk
sine_table = &4000
happiness_flag = sine_table + &100
offset = happiness_flag + 1
offset' = offset + 2That's a mess, and painful to re-order.
Instead:
; Yeah!
savePc = $ ; Not needed if such definitions placed before the first ORG
saveObj = $$ ; Not needed if $$ = $
org &4000
sine_table SKIP &100
happiness_flag BYTE
offset WORD
offset' WORD
org savePc,saveObjA variant when you have default initialisation values (placed in the code-emitting area):
variables = &4100
init
ld hl,variables_default
ld de,variables
ld bc,variables_size
ldir
ret
variables_default
ORG variables, $$
happiness_flag BYTE &ff
offset WORD &c000
offset' WORD &8000
variables_size = $ - variables
ORG $$ ; Restore $In both variants, placing the cursor on the line of the variable definition and activating the mini-dump (CTRL-V) will show the content of the variable, at its real address.
See also struct section for a variant on this theme.
Monogams.
d[nn]
Disassembly.
- d: Go to the last visited address (pointer >)
- dnn: Visit from nn.
t[nn]
As d, with the following nuance:
- t: Go to the next opcode to execute (pointer $)
- tnn: Change PC ($) in order to trace from nn.
NB:
- This corresponds to the DAMS t command.
- t is equivalent to d then CLR.
m [nn]
Dump memory from nn. If nothing is specified, use the previous address.
The bytes are displayed in hexa, ASCII and graphically. Useful for checking a curve or detecting anomalies.
Runs of 00 bytes are skipped. Thanks who ?
xp,q (binary diff)
Compare two memory areas and/or files. Only the differences are displayed.
For example, to compare pages &c6 and &c7:
b&c2
x&8000,&c000With the following program:
txt0
BYTE "If a man knows not to which port he sails "
BYTE "no wind is favorable - Seneca"
txt1
BYTE "If a man knows not to which port he sails"
BYTE "all winds are favorable - Kairos"You'll get the following result:
Compare files (NEW in GG Beta I)
Either parameter can be a path to a file, enclosed in double quotes like for all filenames.
So you can compare two files (comparing two identical 64k files will take 4 seconds on a mass storage).
Or compare one file against a memory area.
Known limitation: Files bigger than 64k are not supported.
hnn, size
Injects 'size' bytes from 'nn' into the source as BYTE.
gr
Graphic memory dump.
Attention unlike the Hacker, we do not get 1 non-zero byte = 1 pixel put.
The bytes are roughly ORed in packets of 8.
clear [n] / clr [n]
Filled the first 128 kb with n or 0, regardless of the current connection.
clearpage [n] / clp [n]
Fill the connected page (64 ko) with n or 0.
RSX
Any command not recognized is interpreted as an RSX. You can also type the explicit pipe |.
The first comma is optional.
|del"wrong.o"In this particular exemple the pipe is mandatory, since del would be parsed as d el.
/!\ Gotcha
Before executing the RSX, bank is switched to C0 (TODO: change this behaviour?), and the firmware is restored as it was before ORGAMS invocation.
Breakpoints.
When Orgasm is invoked and at each assembly, a breakpoint routine is installed at &BE00, and a jump to &BE00 at &30.
Usage
Use RST 6 to jump into the monitor in trace mode. Identically, the pseudo BRK instruction (CTRL + SPACE) in the source simply puts RST 6, which jumps to &be00.
Using &BE00 directly facilitates conditional breakpoint. Eg:
break = &BE00
[...]
LD (pattern_pos) ; Must be non-zero
OR a
CALL z,break; Investigate!All registers at the time of the judgment are preserved. The only destructive manipulation is writing two words in the stack:
- The return address placed by the RST (or CALL &BE00), necessary to know the current PC.
- One PUSH AF necessary to probe IFF (state EI / DI).
In conventional use, this does not disturb the return to the program (like an interrupt). But if SP pointed to a table, it will take account of the corruption of 4 bytes.
Interaction with Multiface 2
The Multiface 2 (MF2) allows to break as soon as you press its pause button. Very useful if your code is hanging.
It will then jump into Orgams' debugger, unless you made the MF2 invisible before invoking Orgams.
Even with software BRK, the MF2 is leveraged to get accurate bank connection (e.g. 7FC1 or 7FC3).
Limitations
The BRK directive add an opcode.
The RST 6 is inserted (rather than e.g. silently replacing the following instruction).
It can be an issue for tightly aligned routines we want to break into.
ROM number detection isn't 100% guaranted.
Without Multiface 2, bank detection isn't 100% guaranted.
Without Multiface 2, ROM paging detection isn't 100% guaranted.
Without Multiface 2, special connections (C1, C2, C3, C9…) aren't detected, and standard connections (C4, C5, C6, C7, CC ….) might be erroneous, especially if the content in the banks is similar.
The assembler doesn't verify the usage validity.
Example of failure:
LD BC, &7fC2
OUT (C), C
[...]
BRK; Crash !! Jumps to &30 in the wrong bank.Make sure you restore the RAM banks before using BRK!
Soft BreakPoint.
Using the quick trace allows to break without modifying the code (in ROM for example).
- $start to place the PC on the code to be executed. Leave the trace with ESC.
- dbreak to move to the breakpoint address, then space or t (quick trace until $ reaches >).
For example, if the entry of your program is &9000 and you want to interrupt at &BB5A:
- $&9000, ESC, d&bb5A, space.
For this case it may be more efficient to modify the source:
ORG &BB5A:BRK:RET
ORG &9000:ENT $But then &BB5A is trashed!
Debugger / disassembler.
There are two types of navigation, with or without execution. Besides, the command D of the monitor can be understood (D)ebug or (D)isassemble.
Without execution, this is a classic disassembly, but with all the flexibility Orgams (kiss your lap):
- Up and down scrolling (fast with CONTROL, faster with SHIFT)
- CONTROL-ENTER and RETURN like the editor to explore subroutines (and back!) Without having to enter a single address.
With execution, it provides all the power of a debugger step by step:
- (S)tep executes an instruction. If there is a call, it enters the subroutine.
- (N)ext executes in fast mode. If there is a call, it gives back control when the routine returns.
- Sometimes you neither want to trace nor execute a routine (eg &BB06). In this case, simply move the pointer to the next instruction, and fingers enable CONTROL-G. This changes PC ($) without executing anything.
- To avoid having to painstakingly follow the N iterations of a loop, it will place the cursor appropriately, and then build on T or SPACE: the loop will be well executed, but in fast mode
When a command display "!", it means > and $ are mismatching. Fix that with either:
- CLR: Go to execution pointer > := $
- CTRL G: Exectute from here $ := >
Limitations:
- The bit 3 & 5 of the register F are not reproduced correctly.
- Interruptions are not emulated.
Source/Memory visualization
Orgams try to find source line matching the opcode under cursor '>'. It can fail, rightly or wrongly, thus displaying "source not found". This feature slow down step-by-step trace, mostly in case of failure.
Kindly press CONTROL-V switch to memory dump, to last address selected via 'M' command in monitor. This mode doesn't penalize trace's speed.
Back to source
CONTROL-ESC returns to editor at currently visualized line, contrarily to ESC-ESC which leaves editor cursor untouched.
Back to the program.
Return by pressing J (like 'J'UMP). The state of the Z80 is restored: the point of view of registers it is as if we had called the routine PUSH AF: POP AF: RET. Of course if the program was not traced by step was taken with the current values of registers.
The CRTC registers are restored with the system defaults.
Profiling
Or, how to measure the time taken by a routine, at the microseconde (NOP) precision.
In-editor
Selecting a block displays the numbers of NOPs taken by the block.
That's a crude result, CALLs aren't entered, loops aren't taken into account.
/!\ Gotcha
#1a2 When not assembled, "label ** opcode" reset machine time counter
#198 Assembling needed for conditionals and macros.
#197 Machine time not counted for CALL, DJNZ and other loops, IMPORT.
In-debugger
The debugger shows time taken by each step. Let says want to measure display_3d:
ent $
BRK
call display_3d
;...
display_3d
;...
retThen CTRL-2 + N will display the time taken, including CALL+RET.
To measure the time taken between label1 and label2, from monogams:
- $label1 (set the execution pointer)
- ESC (return to monogams)
- dlabel2 (set to navigation pointer)
- SPACE (fast trace)
PROF.O
Thanks to Toms^PulpoCorrisivo, you can also measure time programmatically.
Super useful to check a routine take a constant time. Example
import "player.o"
import "prof.o"
MACRO CP_HL_BC
or a:sbc hl,bc:add hl,bc
ENDM
ld b,100 ; Test for 100 iterations
.lp
push bc
call profile_jp:WORD player
ld bc,7*64+12 ; Expected time in NOPs, including RET but not CALL
CP_HL_BC():jr z,.ok
BRK
.ok
pop bc
djnz .lp
retMore documentation in the source itself
Download in working working
Known Issues
These are the known issues for GloireGlup releases.
For new bugs in current beta, see Regression section in working.
Go to bugs for complete list and description.
#15d [rsx] Lot of stack used to pass parameters.
#15a [minor] Internal assertions may leave Orgams in a bad state.
#141 [Assembling] "reload" (CONTROL-5) not taken into account if source not changed.
#13e [Import] Filename comparison is too strict (not Amsdos compatible).
#133 [Import] Far too slow when imported file has many labels.
#129 [Editor/Auto-completion] Ghosts labels are shown.
#124 [Editor] CONTROL-L: explicit search for local label doesn't work.
#121 [Display] Lines > 72 characters with errors wrongly display at assembly time.
#120 [Editor/Monogams] Tilde '~' cannot be entered on Azerty keyboard.
#11f [Disassembly] Instructions eaten after invalid &dd / &fd.
#118 [Editeur] Memory full not handled when switching to a new tab.
#114 [Editeur] Wrong state after memory full while opening source.
#113 [Assembler] Too many nested if: neither handled nor detected.
#110 [Monogams] Type command doesn't handle line bigger than 80 charaters.
#106 BRK doesn't always capture right bank connection (MMR) *without Multiface Two.
#105b BRK doesn't capture ROM connection selected via VGA.
#F8 [Editor] Modification lost when reset.
#F5 [Editor] CONTROL-ENTER does not work for macro parameters
#E6 [Assembler/Begnin] Cannot jr from FFxx to 00xx
#E2 [Orgams] Breakpoint reinstalled when met or when returning from exec.
#E1 [Assembler] Should warn in case of memory conflict.
#D4 [Assembler] MACRO/ENDM and IF/END interleaving not supported.
#C2 [Assemblage] Code assembled in &BFCC-&BFFF is overwritten.
#BB [Editeur] very low priority 'DEFS n' from DAMS source not correctly imported.
#BA [Editeur] Wrong error message instead of "memory full" when importing.
#B7 [Trace] $ not shown if in middle of instruction.
#A9 [Assembler] Overlapping output should raise error.
#A6 [Editor/Parser] Parsing too slow
#A4 [Trace] When entering trace screen, navigation history shouldn't be reset.
#94 [Assembleur] Code assemblé écrasé par gestion Jump.
#93 [Assembleur] Code assemblé écrasé par breakpoint.
#7F [Trace/Madram] Get_line_from_pc for source visu still not fast enough.
#6E [Trace/Madram] Remontée navigation (flèche HAUT) est trop lente.
#4D [Trace] CONTROL-ENTER not working for RSTs
#2E [Assembleur/Madram] Sous bug : Numéro lignes erratiques quand erreur fatale.
#1B [Madram] Parser. LD A,1 + 1 not recognized.
Crashes?
Orgams is very stable, used several hours per day. Alas, in case of memory corruption, there is little it can do.
Actually, there is something: a lot of assumptions are checked (aka "asserts"), so, instead of crashing, it would switch to the debugger pointing to the unexpected failure.
If it ever happens, here is what to do:
- Take a screenshot (for bug report!)
- Reset
- |o
- Save your source(s).
- Export them as well (it would be easier to recover if the source itself is corrupted)
- |orgams to start afresh.
Under the hood
Source encoding
The source is pre-assembled.
- Facilitates the detection of errors when typing.
- Identification of the label rather than to editing the assembly: essential for super-fast assembly.
- Much more compact code.
- Reduces the need for file 'object' (assembled code provided with the information necessary for the relocation and / or to access its routines).
- There is the possibility to import / export ASCII versions.
- In the display, imposes indentation (for opcodes / directives). It is also an advantage: uniformity of code without having to format it by hand!
Design choice regrets
!+++ Using two phases
* Lot of duplicated code (dispatch).
* Almost twice as slow!
!+++ Source encoding: Not making newlines implicit
* Since a lot of opcode takes 1 byte (xor a, inc c, …), that almost double the source footprint! For a 4000 lines source, that's almost 2k lost (~ 5%)
* Assembly have to go through them (~10 us per NL. That's 40ms for 4000 lines).
!+++ Putting all the variables in bank.
For instance, the current source pointer (including the bank #) is stored in the base bk.
So, to read some variables, we must remember the current bk, connect bank bk, read and restore bk.
The rationale was: for the trace (Z80 emulation) to be fast enough, all RAM is mapped as in the executed program.
That means there is no place in RAM to store any variable, no ability to call or push.
The upside is that any write to 0000-FFFF can be done directly (e.g. the emulation of push is just the instruction itself).
Any read 0000-BFFF can also be done as-is. The only issue is the page C000 since the ROM is connected.
So far so good. The tragic error was to maintain this principle (all user RAM connected) to the debugger UI and assembler as well.
It proved simpler and almost unnoticeable to reserve an area for variables and stack, and swap back to user memory just before calling the Z80 emulation.
That's what is done in recent versions, yet cannot be used to its full potential: some variables are hard to move, and bank gymnastic is still needed.
Of course, variables meant to be persistent must remain in bank anyway.


