Z80 Machine Code, 8 6 bytes*
<8ww8>
* Assumes certain conditions by entering from Amstrad BASIC
< INC A // A=A+1
8w JR C, #77 ## C is unset unless A has overflowed, does nothing
w LD (HL), A // Saves A to memory location in HL (randomly initialised)
8> JR C, #3E ## C is still unset, does nothing
A
is initially 0 when entered from BASIC. It increments A
n times, then writes it n times to the same memory location (which is set to a slightly random location by BASIC)! The JR
Jump Relative operation never does anything since the C
flag is always unset, so is used to "comment out" the following byte! This version is slightly cheating by assuming certain entry conditions, namely entering from BASIC guarantees that A
is always 0. The location of (HL)
is not guaranteed to be safe, and in fact, is probably a dangerous location. The below code is much more robust which is why it's so much longer.
Z80 Machine Code, 30 bytes
As ASCII:
o!.ww.!>A=o>{))((}<o=A<!.ww.!o
Basically, the first half guarantees creation of a zero value and the second half increments it and writes it to memory. In the expanded version below ##
denotes code that serves no purpose in its half of the mirror.
o LD L, A ##
!.w LD HL, #772E // Load specific address to not corrupt random memory!
w LD (HL), A ## Save random contents of A to memory
.! LD L, #21 ##
>A LD A, #41 // A=#41
= DEC A // A=#40
o LD L, A // L=#40
>{ LD A, #7B ##
) ADD HL, HL // HL=#EE80
) ADD HL, HL // HL=#DD00. L=#00 at this point
(( JR Z, #28 ##
} LD A, L // A=L
< INC A // A=L+1
o LD L, A // L=L+1
= DEC A // A=L
A LD B, C ##
< INC A // A=L+1
!.w LD HL, #772E // Load address back into HL
w LD (HL), A // Save contents of A to memory
.! LD L, #21 ##
o LD L, A // L=A
Breakdown of allowed instructions:
n op description
-- ---- -----------
28 LD LoaD 8-bit or 16-bit register
3 DEC DECrement 8-bit or 16-bit register
1 INC INCrement 8-bit or 16-bit register
1 ADD ADD 8-bit or 16-bit register
Available but useless instructions:
3 JR Jump Relative to signed 8-bit offset
1 DAA Decimal Adjust Accumulator (treats the A register as two decimal digits
instead of two hexadecimal digits and adjusts it if necessary)
1 CPL 1s ComPLement A
1 HALT HALT the CPU until an interrupt is received
Out of the 39 instructions allowed, 28 are load operations (the block from 0x40 to 0x7F are all single byte LD
instructions), most of which are of no help here! The only load to memory instruction still allowed is LD (HL), A
which means I have to store the value in A
. Since A
is the only register left with an allowed INC
instruction this is actually quite handy!
I can't load A
with 0x00 to start with because ASCII 0x00 is not an allowed character! All the available values are far from 0 and all mathematical and logical instructions have been disallowed! Except... I can still do ADD HL, HL
, add 16-bit HL
to itself! Apart from directly loading values (no use here!), INCrementing A
and DECrementing A
, L
or HL
this is the only way I have of changing the value of a register! There is actually one specialised instruction that could be helpful in the first half but a pain to work around in the second half, and a ones-complement instruction that is almost useless here and would just take up space.
So, I found the closest value to 0 I could: 0x41. How is that close to 0? In binary it is 0x01000001. So I decrement it, load it into L
and do ADD HL, HL
twice! L
is now zero, which I load back into A
! Unfortunately, the ASCII code for ADD HL, HL
is )
so I now need to use (
twice. Fortunately, (
is JR Z, e
, where e
is the next byte. So it gobbles up the second byte and I just need to make sure it doesn't do anything by being careful with the Z
flag! The last instruction to affect the Z
flag was DEC A
(counter-intuitively, ADD HL, HL
doesn't change it) and since I know that A
was 0x40 at that point it's guaranteed that Z
is not set.
The first instruction in the second half JR Z, #28
will do nothing the first 255 times because the Z flag can only be set if A has overflowed from 255 to 0. After that the output will be wrong, however since it's only saving 8-bits values anyway that shouldn't matter. The code shouldn't be expanded more than 255 times.
The code has to be executed as a snippet since all available ways of returning cleanly have been disallowed. All the RETurn instructions are above 0x80 and the few Jump operations allowed can only jump to a positive offset, because all 8-bit negative values have been disallowed too!
#
is its own relection as well, but, you're right, not in consolas. \$\endgroup\$