Improved compression of Z80 assembly language programs
ZXNet echo conference «code.zx»
From Ivan Roshin → To All 19 April 2003
Hello, All!
═══════════════════ bestpack.t ══════════════════
(c) Ivan Roshchin, Moscow
Fido: 2:5020/689.53
ZXNet: 500:95/462.53
E-mail: bestview@mtu-net.ru
WWW: http://www.ivr.da.ru
Improved compression of Z80 assembly language programs
═════════════════════ ══════════════════════
"Radiomir. Your computer" 4/2003.
Last edit date: 12/27/2002.
Most commonly used for compressing assembly programs
HRUST packer of Dmitry Pyankov, as I found out as a result
experiments, compresses even two or three identical ones standing next to each other
byte. Here I want to talk about how it can be used
to improve program compression. Improving compression will save
the disk space occupied by the program, and if the program is written
so that its individual parts are stored in memory in a packed form
and unpacked if necessary, this will help save money
place in memory.
So, what is the essence of the method? This technique is often used
optimization when the variable is placed directly in
loading command, thereby reducing the size of the program and
the execution time is reduced (for more details, see
you can read in [1]).
For example, instead of this:
VAR DS 1
.........
LD A,(VAR)
can be written like this (it will be 2 bytes shorter and 6 clock cycles
faster):
LD A,0
VAR EQU $-1If the original value of a variable is not used in
program, then it makes sense to make this value such that
after compilation, the result was a sequence of identical
bytes, which can be compressed by the packer. That is, a byte
the operand must be equal to the previous byte. In this case
previous byte - operation code "LD A,n", equal to #3E,
Therefore, we write like this:
LD A,#3E
VAR EQU $-1
As a result, after compilation, the corresponding section
program will be two identical bytes #3E.
This method can be used for other commands in which
the original operand value is not used. Let
for example, in the program there is a command CALL 0, where instead of 0 in front
By executing this command, some address is written. Exactly like that
However, to increase the compression ratio, we make the operand equal not 0, but
a number consisting of bytes equal to the previous byte (code
operations CALL nn), resulting in CALL #CDCD.
If in a program using assembler directives DB, DW, DS
variables or memory areas are defined, initial
whose value is not used, then to improve their compression
It also makes sense to fill with the value of the previous byte. Let
For example, the program contains the following fragment:
JP label1
DS 20
The JP instruction in machine code is represented by three bytes;the last of them is the most significant byte of the operand, that is, label1/256.
Accordingly, in the program we write:
JP label1
DS 20,label1/256
So what do we have? When using the described method
to improve compression, the programmer has to remember opcodes
(either refer to the directory each time, or type the command in
debugger and see what code you get). Moreover, so as not to
get confused, for each team in which a substitution is made
operand, you will have to write a comment explaining that the specified
in the command the value is needed only for better compression and not
used when running the program.
It is clear that this is very inconvenient. So I suggest
implement support for this method of improving compression at the level
compiler. You can use the following recording form: if in
instead of the operand value, put the symbol "", then
the compiler will fill the byte(s) of the operand with the value of the previous one
byte of memory. Thus, the entry "LD A," would be equivalent to "LD
A,#3E", "CALL" - equivalent to "CALL #CDCD", "JR" - "JR #18",
and so on. The commands "DB", "DW", "DS n," will
respectively mean that 1, 2, n bytes of memory are filled
the value of the previous byte.
In this case, the programmer will not need to remember command codes
and comment on such sections of the program; without any
comments it will be clear that such a command is used to
increasing the degree of program compression.But there are no such compilers yet, and the operands will have to be replaced
manually. For convenience, I will provide information about what you need
replace the operand for some assembler instructions.
LD A, = LD A,#3E LD IX, = LD IX,#2121
LD B, = LD B,#06 LD IY, = LD IY,#2121
LD C, = LD C,#0E LD SP, = LD SP,#3131
LD D, = LD D,#16 LD (HL), = LD (HL),#36
LD E, = LD E,#1E ADD A, = ADD A,#C6
LD H, = LD H,#26 ADC A, = ADC A,#CE
LD L, = LD L,#2E SUB = SUB #D6
LD XH, = LD XH,#26 SBC A, = SBC A,#DE
LD XL, = LD XL,#2E AND = AND #E6
LD YH, = LD YH,#26 OR = OR #F6
LD YL, = LD YL,#2E XOR = XOR #EE
LD BC, = LD BC,#0101 JP = JP #C3C3
LD DE, = LD DE,#1111 JR = JR #18
LD HL, = LD HL,#2121 CALL = CALL #CDCD
By the way, the compiler can act in more complex ways
thus, without necessarily replacing the byte(s) of the operand with
the value of the previous byte. Let the program have the following
sequence of commands:
LD HL,#1234
CALL subr1
LD HL,\n
CALL subr1
In this case, it would be more profitable to make the third operand
commands are not #2121, but #1234 - then after compilation we will get twoconsecutive identical groups of 6 bytes, which
will shrink more efficiently. So, the compiler can track
similar situations and decide what values the bytes will take
operand in ""-commands.
Another option for the logic of the compiler is not to accept
decide on what values to fill the bytes with
operands in ""-instructions, and shift this task
directly to the packer. In this case, the compiler generates two
output file: in one - the compiled program, and in
friend indicates which bytes this program can accept
arbitrary values. The packer parses both files and
selects the values of these bytes so that the size of the program after
packaging was minimal.
But such a packer still needs to be written! Or if it's
seems more convenient, you can write a separate program,
which would take the two files described above and produce one file with
set byte values, which is then fed to
packer's entrance. But to ensure the best compression, this
the program must know exactly how packaging is carried out.
By the way, in [2] I wrote about a method for increasing the compression of music
modules, based on automatic detection of module bytes,
whose initial values are not used during playback
(which means they can be anything). By filling these bytes
value of the previous byte and an increase in degree was achievedcompression. So, there you can perform the replacement in the same way without
simply to the value of the previous byte, and so that the compression is
the best.
Literature
──────────
1. I. Roshchin. "Optimization using the example of intro 'Start'". "Radiomir.
Your computer" 7-10/2001.
2. I. Roshchin. "Increasing the compression ratio of music modules."
"Radiomir. Your computer" 7/2002.
════════════════════════════════ ════════════════════════════════
My other articles on improving assembler:
1. "Several proposals for improving the assembler."
"ZX-Review" 7-10/1997.
════════════════════════ ════════════════════════
Best regards, Ivan Roshchin.