Coloss

Coloss je programovací jazyk z Ukrajiny pro ZX Spectrum.

Už jsem se o něm zmínil, a to nejednou.

Jazyk má sloužit podobnému účelu, jako jazyk C’ z pera slovenských autorů – umožnit psát jednoduše velmi rychlé programy pomocí nízkoúrovňového jazyka bez nutnosti používat assembler.

V jazyku Coloss vytvořil jeho autor hru Sea Fight.

Kompilátor jazyka Coloss je navíc napsaný sám v sobě!

Po nahrání se spustí editor – jde o Tasword 2, který se ovládá normálně, nápovědu zobrazíte stiskem EDIT.

Pro vyskočení do menu kvůli uložení či načtení textu slouží extend + R.

Kompilaci spustí symbol shift + A.

K editoru a kompilátoru jsou přiloženy i zajímavé příklady, ale bez znalosti jazyka se v nich asi nevyznáte.

Původní dokumentace k jazyku je nejen obtížně srozumitelná a psaná napřeskáčku, ale nejspíš je i neúplná.

Nevysvětluje například naplnění parametrů do konkrétního registru, nevysvětluje některé konstrukce použité v příkladech, jako **, , }} a podobně.

Součástí jazyka je i základní knihovna COLOSS.COL, obsahující některé užitečné povely, jako například write, které se využije velmi často.

Já ale knihovnu v přiložené TAPce nenašel a příkaz write mi nefungoval (chyba při překladu Postdefine error).

Stejně tak grafické knihovny se zachoval jen fragment, navíc pro novější verzi 2.1 (na internetu je Coloss ve verzi 1.2).

Poznámky se vkládají mezi apostrofy.

Ve zdrojácích potkáte složené závorky v zajímavém kontextu: { přepíná do režimu 8bitových hodnot (bajtů), } přepíná do režimu 16bitových hodnot (wordů).

K nim se váže podmíněná kompilace:
?{ následující kompilovat jen pokud je aktivní bajtový režim,
?} následující kompilovat jen pokud je aktivní řežim wordů,
}{ následující kompilovat vždy.

Coloss umožňuje definovat makra, podle kterých kompiluje (uvozená a ukončená dvojtečkou).

Například
: LDIR $$ #B0ED :

po zadání LDIR vygeneruje kód ED B0 (povel $$ způsobí, že se hodnota neuloží do proměnné, ale rovnou jako kód).

Po nadefinování konstanty
: five 5 :
lze vygenerovat napřílad kódy:
{ five – 3E 05 (ld a,5)
} five – 21 05 00 (ld hl,5)
]L five – 2E 05 (ld l,5)

Takže
:RANDOM $$ #5FED ?} }} :
zapracuje tak, že { RANDOM vygeneruje kód ED 5F (ld a,r), zatímco } RANDOM vygeneruje kód ED 5F 6F 26 00 (ld a,r : ld l,a : ld h,0).

Makrem jde i předefinovávat, například
: 2 3 :
způsobí, že výsledek 2 + 2 bude 6, ale vzhledem k tomu, jak preprocesor pracuje, 22 zůstane nezměněno (nestane se z něj 33).

Stejně tak se makra neuplatňují uvnitř textového řetězce.

Zpracování maker při překladu lze zřejmě vypnout povelem #unmacro.

: zero 0 : nadefinuje makro, které se pak způsobí toto:

{ zero se přeloží jako xor a
} zero se přeloží jao ld hl,0
goto zero se přeloží jako jp 0 (goto se překládá jako jp)
gto zero jako jr 0 (gto se překládá jako jr)
write ~zero~ vypíše nezměněný text “zero”.

Pokud nechcete ovlivňovat flagy nulováním registru a pomocí xor, lze místo { 0 zapsat jako { 00 nebo { #0.

Řetězce k tisku se zadávají do tild, samotná čísla představují kódy znaků, takže povel write ~Ahoj!~13
vytiskne text ukončený koncem řádku.

Čísla se tisknou pomocí = !, například
proc print_A }} = !

{ = ! se přeloží jako RST #10, } = ! volá vlastní rutinu pro výpis čísla.

V názvech proměnných nesmí být ~ nebo mezera, rozlišují se velká malá písmena (X a x jsou dvě různé proměnné), název může začínat i číslem.

Podprogram se definuje pomocí proc jméno, případně jde použít proc adresa jméno, například pro procedury v ROM.
proc #3B5 beep
} 1000 % 100 beep ret

jde zkrátit na
proc #3B5 beep
} 1000 % 100 goto beep

Smazání obrazovky:
proc #D6B cls definuje mazací podprogram v ROM.
A teď rychlejší varianta bez ROM, v komentářích je přeložený kód:
proc quick_cls
{ 0 ‘xor a ‘
out #FE ‘out (#FE),a’
} #5B00 ‘ld hl,#5B00‘
begin
‘dec hl‘
{ = [] ‘ld (hl),a‘
| [] ‘or hl‘
untilNZ ‘jr z,begin‘
ret ‘ret‘

Procedury se vlastně jakoby jazykově neukončují a můžou volat jedna druhou nebo skákat samy do sebe:
proc print10 { 10 gto print_A
proc print100 { 100
proc print_A }} = ! ret

kde procedura print_A vytiskne obsah registru a, procedury print10 a print100 vytisknou 10 nebo 100 jako konstantu.

S těmito znalostmi už pomalu porozumíme úvodním příkladům z “manuálu”, které vysvětlují rozdíl mezi osmibitovým a šestnáctibitovým režimem.

Osmibitově:
{ 87 = alpha ‘ ld a,87 : ld (alpha),a ‘
** = beta ‘ add a :ld (beta),a ‘
alpha + beta ‘ ld a,(alpha) : ld hl,beta : add (hl) ‘
= alpha = ! ‘ ld (alpha),a : rst #10 ‘
ret ‘ ret ‘
## alpha , beta ; ‘ alpha: defw 0 : beta: defw 0 ‘

Šestnáctibitově:
} 87 = alpha ‘ ld hl,87 : ld (alpha),hl ‘
** = beta ‘ add hl,hl : ld (beta),hl ‘
alpha + beta ‘ ld hl,(alpha) : ld de,(beta) : add hl,de‘
= alpha = ! ‘ ld (alpha),hl : call writenum ‘
ret ‘ ret ‘
## alpha , beta ; ‘ alpha: defw 0 : beta: defw 0 ‘

Návěští lze definovat dvěma způsoby.

^ definuje návěští pro aktuální adresu, _ definuje návěští pro aktuální adresu + 1.

Příklady použití:

^ label
goto label

Vznikne nekonečná smyčka.

{ _ X -6 ‘ X: ld a,-6 ‘
{ 100 = X ‘ ld a,100 : ld (X+1),a ‘

A zde překlad v šestnáctibitovém režimu:

} _ X -6 ‘ X: ld hl,-6 ‘
} 100 = X ‘ ld hl,100 : ld (X+1),hl ‘

Už jsme viděli definici proměnných pomocí ## (vytvoří v programu dvojbajtové úložné místo), podobně funguje jednobajtová deklarace proměnné #.

Proměnné jde ovšem ukládat na definovanou adresu a řetězit za sebe, dokonce do polí.

@ #FF00 var1 , var2 , ; vytvoří jednobajtové proměnné var1 na adrese #FF00 a var2 na adrese #FF01.

@@ var3 , var4 , ;

po předcházející deklaraci vytvoří šestnáctibitové (dvoubajtové) proměnné var3 na adrese #FF02 a var4 na #FF04.

Pokud se použije jako první (bez deklarace adresy), začnou se proměnné ukládat do printbufferu (var3 na #5B00, var4 na #5B02).

Čárka mezi názvy deklarovaných proměnných je vlastně inkrementace o jeden nebo dva bajty, středník deklaraci proměnných ukončuje.

Můžeme si tak třeba pojmenovat systémové proměnné:

@ 23624 border twice_line ;
@ 23693 paper ink ;

Tyto dva zápisy vytvářející proměnnou na aktuální adrese vygenerují zcela stejný kód:

} _ Memento_Mori % 666

} # , Memento_Mori ; % 666

U víceprvkovéhp pole je třeba dát pozor.

Definujeme takto:
@ 60000 ARRAY [ 1024 ] , eight [ 8 ] ;
@@ 60000 ARRAY [ 1024 ] , eight [ 8 ] ;

Na jakých adresách bude uloženo pole eight?

V prvním případě na adrese 61025, protože 60000+1024, to je jasné.

V druhém případě to bude na adrese 61026, proč?

Protože posun o jeden nebo dva bajty (podle režimu) zařídí čárka, zatímco číslo v hranatých závorkách definuje počet bajtů, ne počet prvků.

Pozor je třeba dát i na to, že při definici proměnných pomocí @ a @@ do prostoru kódu se nealokuje prázdné místo, ale proměnná kód přepíše!

Zde je definice rozložení paměti ze hry Sea Fight:
# loda [ 704 ] pic [ 966 ] MusAdr [ 694 ] RobMus [ 468 ]
Nwin [ 228 ] Ali [ 160 ] Ptg [ 166 ] Ra [ 310 ] Ear [ 132 ]
Et [ 388 ] Nc [ 20 ] Font [ 1024 ] speech [ 20258 ] [ 4 ] MY
[ 100 ] [ 4 ] ZX [ 100 ] clip [ 1348 ] , free [ 2029 ] top ;

Všimněte si, že pomocí top lze zjistit konec obsazené paměti, všimněte si i čárky před bufferem free[].

Čísla lze zapsat desítkově:

-80 600 -896 1024 65535 -10000000

Nadrozměrné číslo (větší než jeden nebo dva bajty, podle režimu) se ořeže modulo 256 nebo modulo 65536.

Záporná čísla se přepočítávají podle režimu, } -1 se přeloží jako 255, zatímco } -1 jako 65535.

Hexadecimálně se zapisují čísla uvozená #:

#BF #0 #20 #FACE #COFFEE

Při použití znaku se jako číslo považuje jeho kód, znak se uvozuje uvozovkami (uzavírající uvozovky jsou nepovinné).

Samotné uvozovky zastupují mezeru, to znamená, že při použití { ” “ se vygeneruje kód 3E 20 3E 20 ( ld a,#20 : ld a,#20 )

Tedy, tyto zápisy jsou rovnocenné:
“Beer”
“B
66
#42

V bajtovém režimu se hodnota uloží do akumulátoru:

{ -60 23 #FF 0 “0” přeložíme jako ld a,-60 : ld a,23 : xor a : ld a,’0′.

Do akumulátoru jde přesunout i obsah jiného registru:
{ B C D E H L R I

Polovinu indexregistru:
{ Xh Xl Yh Yl

Obsah paměti ukazovaný dvojregistrem:
{ [HL] [DE] [BC]

Obsah paměti ukazovaný indexregistrem:
{ [ 0 ] [[ -1 ]] (přeloženo jako ld a,(IX+0) : ld a,(IY+#FF) ).

Naplnění akumulátoru jednobajtovou hodnotou:
{ Value
nebo nižším bajtem dvoubajtové hodnoty:
{ .Value

Podmíněné skoky:

Známe už povely goto (překládá se jako jp) a gto (překládá se jako jr).

Lze je modifikovat takto:
gotoZ – jp z,
gotoNZ – jp nz,
gotoC – jp c,
gotoNC – jp nc,

gotoM a gotoPO nejsou definovány, ale můžete si je vytvořit:
: gotoM $ #FA $$ :

goto ; – jp (hl)
goto ( – jp (ix)
goto (( – jp (iy)

gtoZ – jr z,
gtoNZ – jr nz,
gtoC – jr c,
gtoNC – jr nc,

Nezapomeňte, že jr může skákat jen +127/-128 bajtů daleko.

Protože se procedury a podprogramy volají přímo svým jménem, není implementován povel call, tedy ani jeho podmíněné varianty, lze je ale dodat pomocí makra:
: callNZ $ #C4 $$ :

Návraty z podprogramů a procedur zajišťují tyto konstrukce:
ret
retZ
retNZ
retC
retNC

Cykly lze zapsat i pomocí konstrukcí begin/again nebo begin/until(NZ,C,NC).
(Samotné until má stejný význam jako untilZ.)

K určení, zda se použije jr nebo jp slouží příkazy #rel a #unrel, kdy #rel v konstrukci again/until použije relativní skoky (a ušetří tak v každém cyklu bajt), #unrel použije skoky absolutní.

Zde jsou dva rovnocenné zápisy použití:

begin Play_Sound } [ counter ] ] — retZ again

begin Play_Sound } [ counter ] ] — untilNZ ret

Cykly lze vnořovat:
begin ‘ … ‘
begin ‘ … ‘
begin
‘ … ‘
untilC
‘ … ‘
until ‘ jako untilZ ‘
‘ … ‘
again

Návratové adresy jednotlivých cyklů se ukládají na zásobník, na němž stále zůstává alespoň jedna hodnota, neexistuje tedy chyba typu “again without begin”:
begin ‘ … ‘
begin ‘ … vnořený cyklus ‘
untilNZ
untilNC ‘ tyto tři příkazy ‘
untilC ‘ patří k prvnímu begin‘
again

Coloss umí i počítaný cyklus, využívající instrukci djnz:
do 10 ‘ ld b,10 ‘
‘ label: ‘
loop ‘ djnz label ‘

Pokud už je hodnota v registru B předpřipravená, lze použít i konstrukci
do ;
loop

Rovněž djnz může skákat jen +127/-128 bajtů daleko, Coloss proto zná i konstrukci, která skáče na zadané návěští nebo adresu:
+loop Label ‘ djnz Label ‘
^ Label

Co by to bylo za jazyk, kdyby neuměl podmínky?

Takže tu máme if/else/then.

If existuje opět v několika podobách:

if (odpovídá ifZ)
ifNZ
ifC
ifNC

Then se dá nahradit středníkem ; bez změny významu.

} . 64 if write ~When I’m sixty four…~ ret ;
. 666 if write ~Black Daemon was here!~ ret ;
. 1024 if write ~It’s a KiloByte~ ret then
– 6144 retZ
write ~Speccy rulez!!~ ret

I tento příklad lze upravovat a dosáhnout tím určitých nuancí v překladu:

– 6144 se přeloží jako ld de,6144 : and a : sbc hl,de
+ -6144 se přeloží jako ld de,-6144 : add hl,de.

If i else se vždy překládají relativními skoky jr:

ifelsethen
se přeloží jako
jr z,Label

jr Endc
Label: …
Endc:

Samozřejmě je možné if/then/else konstrukci nahradit příslušnými goto a získat tak absolutní skoky.

Stejně tak je možné k jednomu if přiřadit několik else:
if (splněno) else (jinak) else (pokračování splněno) else (pokračování jinak) then

Příkazy pro řízení běhu procesoru odpovídají svým ekvivalentům v assembleru:

DI EI NOP HALT

Tady dokumentace, kterou mám k dispozici, končí.

Přitom jazyk toho obsahuje mnohem víc.

Význam jednotlivých operátorů, příkazů a funkcí lze ale zjistit asi jen ze zdrojovéko textu Colossu.

Podle něj zná Coloss všechno toto:

D
E
H
L
]D
]E
]H
]L
[
[]
[[]]
[[[]]]
(
)
((
))
(())
)(
))((
{
{{
|
|=
||;
}~
}}
#
##
#=
#inline
#macro
#org
#rel
#skip
#start
#unmacro
#unrel
#unwrite
#write
%
%%
$
$$
&
&&
&=
+=
*
**
+
++
+++



-=
!
!!
=
+loop
,
,,
,,,
,,,,
.
;
/
//
\
\\
:
?}
}{
=
==
===
>
>>
@
@@
^
^^
_
again
begin
di
do
ei
else
goto
gotoC
gotoNC
gotoNZ
gotoZ
gto
gtoC
gtoNC
gtoNZ
gtoZ
if
ifC
ifNC
ifNZ
in
loop
out
proc
ret
retC
retNC
retNZ
retZ
then
until
untilC
untilNC
untilNZ
vv
xor

Význam musíte odvodit ze samotného zdrojáku (ten už teď pro vás snad bude srozumitelnější):


' Z80 Coloss \U 1.2 + Tasword Copyright(c)1997
' Concept and coded by Oleg N. Cher o'Myth Corp.
#org 26400
@ assm , double , ;
@@ dis , txtadr , wrdptr , text , struct , strptr , code ,
macro , lopstk , label , procs , ifthel , cclstk , pasII ,
vars , write , start , rel , cicle , while ;

^ main clear $ #CD $$ 64330 ^ mans { !! | % } ifNZ mod ! 0 ret ;
$ #CD $$ #E6EB = !! ! ++ proc mod [ 62216 ] + !! ++ = ramtop
ret ^ curs clear $ #CD $$ 64333 gto mans
proc clear } % endstr _ ramtop #7D00 goto #E6DA
$$ #73ED $$ stack
{ 0 = assm #= = double
} [ 62216 ] = txtadr 3604 = wrdptr

ramtop ] 0 ++ = struct = start , + 8 = strptr
#5B00 = vars ,, { ] 0 } ^^ +++ ! 8 $$ #B0ED
0 = dis = cicle = while
++ = code = macro
= lopstk = label = procs
= ifthel = cclstk = pasII
= write = rel

{ ! 2 putlast ~rnd $$ #5FED ~13 begin scan translate again

: base { < 21 $$ code do ; [] 83 5F 23 [] 8A 57 23 loop > } :

proc gettxt } txtadr
begin { [] . 32 if } ++ again then
{ \\ if { ! 7 } % struct base %=
^ pas = wrdptr
{ [] \\ if ? $ 0 ; ! 3 search if ! 4 search if
begin [] } ++ { . 32 until } ++ ++ [] = txtadr ? $ 1
{ ; ! ++ ;
} [] { [[]] ^^ } +++ { [[]] } +++ ,,,
{ = D ]E %
{ [[]] -- if lrljr } ,, ++ ++ gto pas ;
{ ++ ifNZ !! \\ ifNZ #CD = [[]] ; } +++ ;
! dis + !! { L = [[]] } +++ { H = [[]] } ,, ++ ++

gto pas then % #4000 { ! 0 }
begin = txtadr
{ [] . 32 if !! \\ retZ [] then
. 126 if !! #= = !! [] then
= [[]] } +++ txtadr ++
again

proc cmp } ,, % wrdptr { [[]]
begin . [] if } ++ +++ { [[]] . 32 until
0 . [] gtoNZ cskip goto ; then 0 ^ cskip
begin . [] } ++ { until -- goto ;

proc scan } wrdptr
begin { [] . 32 } ++ until = wrdptr { [] \\ retNZ
^ ctrl } #4000 = wrdptr = text
begin gettxt { 32 = [[]] } +++ { 0 = [[]] cmp ~'~ if
begin gettxt { 32 = [[]] } +++ { 0 = [[]] cmp ~'~ until
again then { [ #4000 ] . 126 retZ

^ process } struct + code
^ find % text
begin { [[]] . [] if } ++ +++ { . 32 until } , text
begin { [[]] = [] } ++ +++ { \\ until = !! } % text ,, --
begin ++ { [] . 1 if double = !! again then

. 2 if double #= = !! again then
. 3 if ! 0 again then
. 13 gtoZ process !! \\ until [] } , vv +++
^ ins { % [] = [] vv } ++ { \\ gtoNZ ins = [] } ,,
again then
begin { [] } ++ { . 13 until [] \\ gtoNZ find
begin [[]] } +++ { . 32 until } == text { [[]] \\ gtoNZ process
[ #4000 ] \\ retNZ goto ctrl

proc putln { ,,, } , % ramtop base -- -- ] ++ if ++ ] ++ ;
,,, strptr -= = !! _ endstr #CCFF - strptr if ? $ 2 then
strptr ++ = strptr -- -- $$ #B8ED ,,,, ,, { ,,,, ret

proc getln { ,,, } % ramtop base -- -- { [] - 1 = []
ifC } ++ { [] - 1 = [] ifC ? $ 3 then then
} --- { [[]] , }
strptr -- = strptr -= = !! vv ++ $$ #B0ED { ,, ,,,, ret

proc putadr } ramtop + code
proc putnum { putln L = [[]] putln H = [[]] ret
proc getnum { getln , getln = L ,, = H ret

proc putword } wrdptr
begin putln { [] = [[]] } ++ { [] \\ until ret
proc putlast } ,,
begin putln { [] = [[]] } ++ { [] \\ until goto ;

proc search } % struct base
begin wrdptr { [[]] \\ retZ
begin [[]] . [] if } ++ +++ { . 32 until \\ } %= { ret then
begin [[]] } +++ { . 32 until } +++ +++
again

proc byte } wrdptr { [] } ++ {
. """ if ! [] ret then ! 0
. "#" if assm \\ gtoNZ decbyt ^ hexbyt
[] begin - "0" retC . 10 ifNC - 7 . 10 retC
. 16 gtoNC lex then
^^ !! ** ** ** ** += = !! } ++ {
[] . 32 until ret then
. "-" if cbyte ifNC !! $$ #44ED = !! \\ then ret then

} --
proc cbyte { assm \\ gtoNZ hexbyt ^ decbyt
[] begin - "0" retC . 10 ifNC ^ lex || ret then
^^ !! ** ** ** + !! + !! += = !! } ++ {
[] . 32 until ret

proc number } cmp ~"~ if scan byte ifC ^ syntax ? $ 4 then
]L !! , scan byte gtoC syntax ,, ]H !!
ret then % wrdptr { [[]] } +++ {

. """ if [[]] }} ret then 0 { ! 0
. "#" if assm \\ gtoNZ decnum ^ hexnum
[[]] begin - "0" retC . 10 ifNC - 7 . 10 retC
. 16 ^ NClex gtoNC lex then
^^ } ** ** ** ** + !! +++ {
[[]] . 32 until ret then
. "-" if cnumber ifNC } %= 0 -= { \\ then ret then

} ---
proc cnumber { assm \\ gtoNZ hexnum ^ decnum
[[]] begin - "0" retC . 10 gtoNC NClex
} ** = !! ** ** + !! { ^^ ! 0 } + !! +++ {
[[]] . 32 until ret

: comp compi $ : proc compi } ,, { ! 1 putln [] = [[]] ret
: com val $ : proc val } ,, { ! 1 putln [] = [[]] } ++ goto ;
proc compile } ,, { % [] ! 1 } ++
begin { putln [] = [[]] } ++ { % -- until goto ;

proc direct { number ifNC ! 1 goto putnum then
proc indirect { cmp ~[~ if scan number ifNC ! 1 putnum
^ clos scan cmp ~]~ retZ goto syntax ;
ident gto clos ;
proc ident { ! 3 search if ! 4 search if ! 8 putword
^ post putadr } txtadr { putnum ! 1 } 0 goto putnum ; ;
[] + dis { ! 1 goto putnum

proc doind index goto syntax
proc index { ,
cmp ~++~ if ! 1 putln ,, - #42 = [[]] ,, ret ;
cmp ~--~ if ! 1 putln ,, - #41 = [[]] ,, ret
proc value { , then
byte ifNC ,, ,,, , ! 1 putln ,, xor #40 = [[]]
putln ,, = [[]] ,, ret

proc alter { , then
cmp ~!!~ if ! 1 putln ,, - 6 = [[]] ,, ret ;
cmp ~%~ if ! 1 putln ,, - 5 = [[]] ,, ret ;
cmp ~D~ if ! 1 putln ,, - 4 = [[]] ,, ret ;
cmp ~E~ if ! 1 putln ,, - 3 = [[]] ,, ret ;
cmp ~H~ if ! 1 putln ,, -- -- = [[]] ,, ret ;
cmp ~L~ if ! 1 putln ,, -- = [[]] ,, ret ;
,, ret

proc regist index gto para
proc argum value ^ para { ,
cmp ~[]~ if ! 1 putln ,, = [[]] ret ;
cmp ~(~ if ^ x com #DD putln ,, = [[]]
scan byte gotoC syntax ,,, ! 1 putln ,, = [[]]
scan cmp ~)~ retZ goto syntax ;

cmp ~((~ if ^ y com #FD putln ,, = [[]]
scan byte gotoC syntax ,,, ! 1 putln ,, = [[]]
scan cmp ~))~ retZ goto syntax ;
com #21 cmp ~[~ if scan number gotoC syntax
! 1 putnum putln ,, = [[]] goto clos ;
ident putln ,, = [[]] ret

proc dest { , number ifNC ! 1 putln ,, = [[]] goto putnum ;
cmp ~[~ if scan
number ifC ! 1 putln ,, = [[]] ident goto clos ;
,, cmplx goto clos ; ,,
proc cmplx { . #01 if compile $ 2 $$ #4BED gto swtch ;
. #11 if compile $ 2 $$ #5BED gto swtch ;
com #2A ^ swtch number gotoC ident ! 1 goto putnum

: format double \\ if : ^ for ? $ 5
: only_big double \\ gotoZ for :
: only_sml double \\ gotoNZ for :

proc translate { cmp ~0~ if assm \\ if format comp #AF ; ; ;
assm \\ ifNZ ^ bcom byte gtoC constr ,,, assm \\ if com #3E ;
! 1 putln ,, = [[]] ret ;
double \\ gtoZ bcom number gtoC constr } , com #21
,, goto putnum
^ constr wrdptr { [] . "$" gotoNC first

cmp ~!~ if scan format #46 goto regist ;
cmp ~%~ if compile $ 2 $$ #424B ret ;
cmp ~++~ if comp #03 ;
cmp ~--~ if comp #0B ;
cmp ~[]~ 1 gotoNZ dest
compile $ 3 < 4E 23 46 > ret ;

cmp ~!!~ if format comp #78 ; compile $ 2 $$ #6069 ret ;
cmp ~#~ if 0 gto dcl ;
cmp ~##~ if #23 ^ dcl = inr } ramtop + code , begin scan

,, proc define , cmp ~;~ if ,, ret ;
cmp ~[~ untilNZ cmp ~]~ untilNZ
cmp ~,~ if ,, _ inr ++ ++ , again ;
number ifNC ,,,, += , again ;
{ ! 3 search gotoNZ syntax ! 4 putword
} ,, , { putnum again ;

cmp ~#=~ if format comp #2F ;
compile $ 6 < 7D 2F 6F 7C 2F 67 > ret ;
cmp ~#inline~ if scan number gotoC syntax } ,
scan number gotoC syntax %% { ! 1
begin putln [] = [[]] } ++ ,,,, --- ,,, { E | D until
,, ret ;

cmp ~#macro~ if #2A = process ret ;
cmp ~#org~ if scan number gotoC syntax
} - struct = dis { ret ;
cmp ~#rel~ if #FF = rel ret ;
cmp ~#skip~ if scan number gotoC syntax ! 1
begin putln 0 = [[]] } -- \\ { until ret ;

cmp ~#start~ if } ramtop + code = start { ret ;
cmp ~#unmacro~ if #C9 = process ret ;
cmp ~#unrel~ if 0 = rel ret ;
cmp ~#unwrite~ if #FF = write ret ;
cmp ~#write~ gotoNZ ncon 0 = write ret

^ first . "(" gotoNC second
cmp ~$~ if scan assm , 0 = assm byte gotoC syntax
,, = assm ,,, ! 1 putln ,, = [[]] ret ;
cmp ~$$~ if scan assm , 0 = assm direct ,, = assm ret ;

cmp ~%~ if scan format #4E goto regist ;
cmp ~!!~ if compile $ 2 $$ #5059 ret ;
cmp ~++~ if comp #13 ;
cmp ~--~ if comp #1B ;
cmp ~[]~ #11 gotoNZ dest
compile $ 3 < 5E 23 56 > ret ;

cmp ~%%~ if format comp #D9 ; comp #E3 ;
cmp ~%=~ if format comp #08 ; comp #EB ;
cmp ~&~ if only_sml scan #A6 goto argum ;
cmp ~&&~ if comp #3F ;
cmp ~&=~ gotoNZ ncon format comp #A1 ;
compile $ 6 < 7D A3 6F 7C A2 67 > ret
^ second . "*" gotoNC dum

cmp ~(~ if only_sml #7E , goto x ;
cmp ~((~ if only_sml #7E , goto y ;
cmp ~(())~ if compile $ 2 $$ #E5FD ret ;
cmp ~()~ if compile $ 2 $$ #E5DD ret ;

cmp ~)~ if com #DD ^ indy scan
cmp ~+=~ if comp #19 ;
cmp ~**~ if comp #29 ;
cmp ~++~ if comp #23 ;
cmp ~--~ if comp #2B ;
cmp ~+~ if scan
cmp ~!!~ if comp #09 ;
cmp ~%~ gotoNZ syntax comp #19 ;
cmp ~=~ #21 gotoNZ dest
scan com #22 goto indirect ;

cmp ~)(~ if compile $ 2 $$ #E1DD ret ;
cmp ~))~ if com #FD goto indy ;
cmp ~))((~ gotoNZ ncon compile $ 2 $$ #E1FD ret
^ dum . "-" gotoNC third

cmp ~*~ if "*" ^ glob = propr only_big scan #11 dest
com #CD ! 8 putlast ^ propr ~*= ~
goto post ;
cmp ~**~ if format comp #87 ; comp #29 ;
cmp ~+~ if scan format #86 goto argum ;
cmp ~!!~ if comp #09 ;
cmp ~%~ ifNZ #11 dest ; comp #19 ;

cmp ~++~ if format comp #3C ; comp #23 ;
cmp ~+=~ if format comp #81 ; comp #19 ;
cmp ~+++~ if format comp #0C ; comp #13 ;
cmp ~+loop~ if com #10 goto rljplb ;
cmp ~,~ if format comp #F5 ; comp #E5 ;
cmp ~,,~ if format comp #F1 ; comp #E1 ;
cmp ~,,,~ if format comp #C5 ; comp #D5 ;
cmp ~,,,,~ gotoNZ ncon format comp #C1 ; comp #D1

^ third . ";" gotoNC four
cmp ~-~ if scan format #96 goto argum ;
proc minus cmp ~!!~ if compile $ 3 < A7 ED 42 > ret ;
cmp ~%~ ifNZ #11 dest ;
^ subD compile $ 3 < A7 ED 52 > ret ;
cmp ~--~ if format comp #3D ; comp #2B ;
cmp ~-=~ if double \\ gtoNZ subD comp #91 ;

cmp ~---~ if format comp #0D ; comp #1B ;
cmp ~.~ if scan format #BE goto argum ;
com #E5 minus comp #E1 ;
cmp ~/~ if "/" goto glob ;
cmp ~//~ if format compile $ 2 $$ #3FCB ret ;
^ roll compile $ 4 < CB 3C CB 1D > ret ;

cmp ~:~ gotoNZ ncon process , #C9 = process
begin scan ! 2 cmp ~?{~ if putln 1 = [[]] again ;
cmp ~?}~ if putln 2 = [[]] again ;
cmp ~}{~ if putln 3 = [[]] again ;
cmp ~:~ if putln 13 = [[]] ,,
= process ret ;
putword again

^ four . "@" gotoNC five
cmp ~;~ gotoZ then
cmp ~<~ if assm \\ gotoZ form ? $ 5 ; cmp ~<<~ if format comp #07 ; compile $ 4 < CB 25 CB 14 > ret ;
cmp ~=~ gotoNZ Neq scan double \\ gotoNZ dob

cmp ~!!~ if comp #47 ; cmp ~%~ if comp #4F ;
cmp ~D~ if comp #57 ; cmp ~E~ if comp #5F ;
cmp ~H~ if comp #67 ; cmp ~L~ if comp #6F ;
cmp ~[]~ if comp #77 ;
cmp ~[[]]~ if comp #12 ;
cmp ~[[[]]]~ if comp #02 ;
cmp ~(~ if #77 , goto x ;
cmp ~((~ if #77 , goto y ;
cmp ~!~ if comp #D7 ;
com #32 goto indirect

^ dob cmp ~!!~ if compile $ 2 $$ #444D ret ;
cmp ~%~ if compile $ 2 $$ #545D ret ;
cmp ~!~ if com #CD ! 8 putlast ~writenum ~
goto post ;
com #22 goto indirect ^ Neq

cmp ~==~ if only_big scan compile $ 2 $$ #53ED
goto indirect ;
cmp ~===~ if only_big scan compile $ 2 $$ #43ED
goto indirect ;
cmp ~>~ if assm \\ gotoZ for ^ form #= = assm ret ;
cmp ~>>~ gotoNZ ncon double \\ gotoNZ roll comp #0F
^ five . "[" gotoNC six

cmp ~@~ if 0 gto data ;
cmp ~@@~ if #23 ^ data = inr scan
number ifNC } = vars scan ;
vars - dis define + dis = vars { ret ;
cmp ~D~ if only_sml comp #7A ;
cmp ~E~ if only_sml comp #7B ;
cmp ~H~ if only_sml comp #7C ;
cmp ~L~ gotoNZ ncon only_sml comp #7D

^ six . "a" gotoNC seven
cmp ~[~ if double \\ #21 gotoNZ dest
scan com #3A number gotoC syntax
! 1 putnum goto clos ;
cmp ~[]~ if format comp #7E ;
compile $ 4 < 7E 23 66 6F > ret ;
cmp ~[[]]~ if only_sml comp #1A ;
cmp ~[[[]]]~ if only_sml comp #0A ;
cmp ~\~ if "\" goto glob ;
cmp ~\\~ if format comp #B7 ; compile $ 2 $$ #B47D ret ;

cmp ~]~ if scan cmp ~(~ if com #DD
scan byte gotoC syntax ,,,
scan cmp ~)~
^ xy gotoNZ syntax scan #76 doind putln } ,,, byte
,, ,,,, ] D retC -- { [] ] D } ++ { = [] ret ;
cmp ~((~ if com #FD
scan byte gotoC syntax ,,,
scan cmp ~))~ gto xy ;
#76 index cmp ~[]~ gotoNZ syntax comp #76 ;

cmp ~]D~ if scan #56 goto regist ;
cmp ~]E~ if scan #5E goto regist ;
cmp ~]H~ if scan #66 goto regist ;
cmp ~]L~ if scan #6E goto regist ;
cmp ~^~ if scan ! 3 search gotoNZ syntax
! 4 putword goto putadr ;
cmp ~^^~ if format comp #4F ; compile $ 2 $$ #545D ret ;
cmp ~_~ gotoNZ ncon scan ! 3 search gotoNZ syntax
! 4 putword } struct + code {
goto putnum

^ seven . "g" gotoNC eight
cmp ~again~ if rel \\ ifNZ com #18 endcicl goto reljmp ;
com #C3 goto rpt ;
cmp ~begin~ if ! 7 } putadr = while { ret ;
cmp ~di~ if comp #F3 ;
cmp ~do~ if scan cmp ~;~ ifNZ #46 argum ;
! 3 } putadr = cicle { ret ;

cmp ~ei~ if comp #FB ;
cmp ~else~ gotoNZ ncon compile $ 2 $$ #18 ! 6 getnum } --
, ramtop + code putnum ,,,, goto lrljr {
^ eight . "i" gotoNC nine
cmp ~goto~ if scan cmp ~;~ if comp #E9 ;
cmp ~)~ if compile $ 2 $$ #E9DD ret ;
cmp ~))~ if compile $ 2 $$ #E9FD ret ;
com #C3 goto direct ;

cmp ~gotoC~ if scan com #DA goto direct ;
cmp ~gotoNC~ if scan com #D2 goto direct ;
cmp ~gotoNZ~ if scan com #C2 goto direct ;
cmp ~gotoZ~ if scan com #CA goto direct ;
cmp ~gto~ if com #18 ^ rljplb scan byte
ifC ! 3 search if ! 4 search if
! 8 putword putadr } txtadr { putnum comp 1 ; ;
} [] { ^ reljmp ! 1 putln
proc lrljr } --

= !! -= ifC %= , - !! . 129 ifNC
^ longjp ? $ 6 ;
{{ } ,, { $$ #44ED { = [] ret ;
} ! 128 , - !! ,,
gtoNC longjp {{ = [[]] ret ;
,,, ! 1 putln ,, = [[]] ret ;

cmp ~gtoC~ if com #38 gto rljplb ;
cmp ~gtoNC~ if com #30 goto rljplb ;
cmp ~gtoNZ~ if com #20 goto rljplb ;
cmp ~gtoZ~ gotoNZ ncon com #28 goto rljplb
^ nine . "p" gotoNC ten
cmp ~if~ if com #20 ^ if com 0 ! 6 goto putadr ;
cmp ~ifC~ if com #30 gto if ;
cmp ~ifNC~ if com #38 gto if ;
cmp ~ifNZ~ if com #28 gto if ;

cmp ~in~ if scan com #DB byte gotoC syntax
,,, ! 1 putln ,, = [[]] ret ;
cmp ~loop~ if com #10 } lopstk -- \\
ifNZ { ! 3 } getnum = cicle ;
cicle goto reljmp { ;
cmp ~out~ gotoNZ ncon scan com #D3 byte gotoC syntax
,,, ! 1 putln ,, = [[]] ret
^ ten . "u" gotoNC eleven

cmp ~proc~ if scan number ifC ! 4 search gotoNZ syntax
! 5 putword goto putadr ; }
- dis , { scan ! 4 search gotoNZ syntax
! 5 putword } ,, goto putnum ; {
cmp ~ret~ if comp #C9 ;
cmp ~retC~ if comp #D8 ;
cmp ~retNC~ if comp #D0 ;
cmp ~retNZ~ if comp #C0 ;
cmp ~retZ~ if comp #C8 ;
cmp ~then~ gotoNZ ncon ^ then ! 6 getnum } --
, ramtop + code ,,,, goto lrljr

proc endcicl } cclstk -- \\
ifNZ { ! 7 } getnum = while ret ;
while { ret
^ eleven . "{" gotoNC twelve
cmp ~until~ if rel \\ ifNZ com #20 endcicl goto reljmp ;
com #C2 ^ rpt endcicl } + dis
{ ! 1 goto putnum ;

cmp ~untilC~ if rel \\ ifNZ com #30 endcicl goto reljmp ;
com #D2 gto rpt ;
cmp ~untilNC~ if rel \\ ifNZ com #38 endcicl goto reljmp ;
com #DA gto rpt ;
cmp ~untilNZ~ if rel \\ ifNZ com #28 endcicl goto reljmp ;
com #CA gto rpt ;
cmp ~vv~ if format comp #79 ; compile $ 2 $$ #626B ret ;

cmp ~xor~ gotoNZ ncon only_sml scan #AE goto argum
^ twelve . 126 gotoZ lap
cmp ~{~ if 0 = double ret ;
cmp ~{{~ if 0 = double comp #7D ;
cmp ~|~ if only_sml scan #B6 goto argum ;
cmp ~|=~ if format comp #B1 ;
compile $ 6 < 7D B3 6F 7C B2 67 > ret ;
cmp ~||~ if comp #37 ;
cmp ~}~ if ^ big #FF = double ret ;
cmp ~}}~ if compile $ 3 < 6F 26 00 > gto big ;

^ ncon ! 3 search ifNZ } % [] dis += , {
format com #3A else com #2A ; } ,, goto putnum { ;
! 4 search ifNZ } % [] dis += ,
com #CD ,, goto putnum { ;
! 8 putword putadr } txtadr { putnum
format com #3A else com #2A ; compile $ 2 $$ 0 ret
^ lap } ! #100 ++
begin { [] . 126 if vv #= ^^ } ++ { again ;
. 32 if vv \\ gtoZ add } = wrdptr { assm \\ retNZ

comp 0 ;
vv \\ if ^ add putln [] = [[]] } ++ again ;
{ [] - "0" gotoC syntax . 10 gotoNC syntax = E
^ cf } ++ {
[] - "0" gtoC putch . 10 gtoNC putch = D
E ** ** ** + E + E + D = E gto cf
^ putch E , putln ,, = [[]]
again
proc ? } txtadr - [ 62216 ] $ #CD $$ #F46D
,, { % [] ! 0 $ #31 ^ stack $$ 0 ret