Script with 4-bit commands - scripting in the style of fort commands.

Script with 4-bit commands
 by Alone Coder

I wanted to once again optimize ACEdit for size - using
scripting. But after I finally came up with the script, I
I gave up on writing anything on it :)

The idea of the script is that each command is encoded not by a byte, but
nibble, and addresses in commands (jump, call, read
variable) instead of two bytes are encoded as one. In addition,
The register-stack model is somewhat nontrivial: after
writing a bunch of test snippets, I came up with merging stacks
data and calls and storing the top of the data stack (it is not included in
call stack) in the HL register - for passing parameters and
interface with machine code.

Wait a minute - what does machine code have to do with this? The point is that
a full set of fort commands did not fit (for example, there is no entry
variable), and the remaining operations are supposed to be implemented
by calling procedures. The call command(callr) calls exactly
machine code. And the entrance to the script interpreter isRST #10.

Another subtlety is processing conditions. It is assumed that
The script is written in a structured style. At the same time, it is implemented natively
end-to-end return of "errors" in the CY flag. It is assumed that CY=1
correspondserror, but in reality error codes may
you will need to alternate at each call level - in this
the last hellish optimization with the removal of call is to blame, which
cut the interpreter in half.

It was never possible to check whether this terrible thing works
the number is a grinder, because to write at least some script
we need to invent a whole compiler :)

Maybe it will be possible to build a script generator from pi codes into
some Java compiler?

So, the list of commands with codes:

0. ccf:NC for < (executed after each command! for chain
reterr must alternate C/NC at each call level)
1. callronly in the current segment (8 bits)
2. jrNC(ok)only in the current segment (8 bits) (for
loop, break, djnz and even if with a complete alternative - instead
callcc) (full alternative="jrNC:...:jr:...")
3. getvar, address var=8 bits (does not exist in ifcc - it’s cheaper to do
jrcc, than adding to the script): push<=reg:reg<=(var) (1bbit)
4. retNC(ok):ifNC{ret}
5. retC(err):ifC{ret}
6. push:push<=reg
7. pop:pop>=reg
8. swapregtoc
9. popsub:reg<=reg-pop
A. popadd:reg<=reg+pop
B. dec:reg<=reg-1
C. inc:reg<=reg+1
D. scf:CY=1(err) (jr="scf[+ccf]:jrNC")
E. eq:ifNZ{scf}: NC for = (loop="eq[+ccf]:jrNC")
(retz="eq[+ccf]:retC")
F. leq:ifNZ{ccf}: NC for <=

The interpreter itself:

;before starting you need to set RST#10 to _q (main registers and
the flags are preserved, but exx is done (hl,de gets corrupted there). means
at the entrance to the script and when ret you need to do exx)
ld hl,_startscript
ld (#5c51),hl
;+6 (needed only during initialization)

_startscript
pop hl;skip #1Sfe (return from call #162c)
pop hl;restore hl'
exx
pop hl;address after RST #10
ld c,0;read byte immediately
;(hl=addr, de=reg, b=cmd, c=bit):
_ccf
ccf
_q
push af
ld a,%00010000
getcmd:
sla c
jr nz,$+6
ld c,(hl)
inc hl
rl c
rla
jr nc,getcmd
ld b,a
pop af
djnz _ncall
push bc;return bit
push hl;return addr
ld l,(hl);read 8bit
call jphl;(in 48 basic) call machine code procedure
pop hl;return addr
pop bc;return bit
inc hl;skip 8bit
_ncall:
djnz _njrc
jr nc,_q
ld l,(hl)
_njrc:
djnz _ngetvar
;getvar: push<=reg:reg<=(var) ;1 byte less if no push
push de
push hl
ld l,(hl);read 8bit
ld h,tvar/256
ld e,(hl)
inc hl
ld d,(hl)
pop hl
inc hl;skip 8bit
_ngetvar:
djnz _nretnc
ret nc
_nretnc:
djnz _nretc
ret c
_nretc:
djnz _npush
push de
_npush:
djnz _npop
pop de
_npop:
djnz _nswap
ex de,hl
ex (sp),hl
ex de,hl
_nswap:
djnz _nsub
;reg<=reg-pop ;1 byte less if "pop-reg" ;1 byte less if sbc
ex (sp),hl
ex de,hl
or a
sbc hl,de
ex de,hl
pop hl
_nsub:
djnz _nadd
;reg<=reg+pop
ex (sp),hl
add hl,de
ex de,hl
pop hl
_nadd:
djnz _ndec
dec de
_ndec
djnz _ninc
inc de
_ninc
djnz _nscf;#D
scf
_nscf
jr z,_ccf;#E
djnz _neq
scf
_neq
djnz _ccf;#F
jr _q ;instead of ccf:jr _ccf
;+100

;=106 (можно -1 при popantisub, -1 при sbc, -1 при getvar без
push

Share your thoughts about the article