The Bitcoin cryptocurrency makes extensive use of the SHA-256 hash function. This hash function (in common with many others) takes a block of data and generates a long number (a "hash" or "digest") from it. Retrying the function with slightly different data is overwhelmingly likely to result in a completely different hash. Bitcoin mining requires that you find blocks with hashes smaller than a target value. This target value is set by the current "difficulty level", which adjusted is so that the average time between each sucessful mining operation remains approximately ten minutes. The format of these blocks (Bitcoin Headers) is described here. In practice, the only way to do this is to keep changing the data in some way, then retry the hash function over and over again. The Bitcoin header contains a value called a "nonce" that is specifically designed for this purpose, though the time field can also be used (within limits).
The probability of a given header having a suitably small hash is amazingly small. As a result, realistic mining rigs use dedicated hardware and are capable of trying different hashes at rates measured in tera (trillion, or 10¹²) hashes per second (Th/s). There is clearly no way that an 8-bit microprocessor designed in the 1970s (a Z80) can compete with this.
The SHA-256 algorithm is itself relatively straightforward. It is based on 32-bit arithmetic and involves simple functions such as rotates and exclusive ORs. Most of this processing is repeated 64 times, and as Bitcoin uses double SHA-256, this entire thing must be repeated. The result is that each hash requires quite a lot of processing. My homebrew Z80 system ("ZARC") runs at 8 MHz, and manages approximately 5.5 hashes per second. That's a long way from that required for any practical mining rig. It does, however, demonstrate the principles nicely.
Much of the complexity comes from the fact that the SHA-256 algorithm assumes a big-endian machine, but the Z80 is natively little-endian. In addition, the Bitcoin header is too large for a single SHA-256 pass, so two are required plus the additional one to implement the double SHA-256. Debugging is tricky because almost any mistake leads to a completely unrecognisable hash. Generally, this offers no clue as to what is wrong.
CP/M 2.2 Z80 Code
The code I wrote is available at https://github.com/rundel-tech/ZARC/tree/main/software/cpm/cpm_dev/bitcoin. Documentation is in the file BitcoinGuide.pdf, and the executable file is BITCOIN.COM. Though written for my ZARC system, it should run on any CP/M 2.2 Z80 machine. By default, this code attempts to find the lowest possible hash for the Bitcoin genesis block. Progress is saved as a disk file, and it is possible to mine other blocks by manipulating this file.
This code was inspired by Ken Shirriff's posts, such as http://www.righto.com/2015/05/bitcoin-mining-on-55-year-old-ibm-1401.html. The IBM machine manages one hash every 80 seconds, which makes the Z80 look relatively sprightly!
BITCOIN.COM Sample Run
*** Bitcoin Mining V1.0 ***
Null message test: OK
Short message test: OK
Long message test: OK
Long message test 2: OK
Bitcoin genesis block: OK
Bitcoin example block: OK
Loading state from file D:BITCOIN.SAV
Can't open file
Error reading saved state. Using bitcoin genesis block.
Best nonce / hash found:
0 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
Starting mining. Press ^C to exit.
Best nonce / hash found:
0 2BC1A7F50AB3C6D73BAC757D75C7F35C6BA94DE37339115ABF4CB4A9983948BF
Best nonce / hash found:
6 1F9FA9EC2FDF01241DC3BBF730884806BDE717FC785EF8D6E0FB89EF701EB3E4 [1.1s]
Best nonce / hash found:
29 16794E39C32E835048D460DFBAA770AF2E0CBA537D2D4E873660E16BDA7A55D8 [5.3s]
Best nonce / hash found:
33 0AF70261EC4B4C83F4D0DF9BA890E2259E76A4D2A71C4A9C8D419D5419BB5F48 [6s]
Iteration: 53, delta: 53
Iteration: 108, delta: 55
Best nonce / hash found:
159 048E9AF07BD4B7E4C83E8FAC19B7135D2D03A939D239B5DE5AB915A3B5987712 [28.9 s]
Iteration: 163, delta: 55
Iteration: 218, delta: 55
Best nonce / hash found:
236 005BA61E89AAE83D3D9C841F2D4960D41A26265F885FE3A72D65987E4764EA52 [42.9 s]
Iteration: 273, delta: 55
Iteration: 328, delta: 55
Iteration: 384, delta: 56
Best nonce / hash found:
395 003863A313A4B7809B500D70D33A9592CC85968FDC60979072875F2396250171 [71.8 s]
Iteration: 438, delta: 54
[...]
Iteration: 1377, delta: 55
Best nonce / hash found:
1393 0031CFC620EA408D9D03749547E5C8266E59FB766BF071EDA1DCC5C4D1ECCE5A [253.3 s]
Iteration: 1432, delta: 55
[...]
Iteration: 4193, delta: 55
Best nonce / hash found:
4200 00055472F907066521D4F41ABD2D1B3CBC102C78C60A153C296771793C50921B [763.6 s]
Iteration: 4248, delta: 55
[...]
Iteration: 8555, delta: 55
Best nonce / hash found:
8603 00007CC6EC08C5D53C32CEAF6E8309C32EB07FC903A94EC77AC2BF6E48F7ADB3 [1564.2 s]
Iteration: 8610, delta: 55
[...]
Iteration: 250703, delta: 55
Best nonce / hash found:
250707 000024A026D9AB5671B86FBA972B3A32B41CB69CA5DC43767B59AF5B7B8EA94A [12.6 h]
Iteration: 250758, delta: 55
[...]
Iteration: 465625, delta: 56 [23.5 h]
Saving state to file D:BITCOIN.SAV
Can't delete file
Returning to CP/M.
D>dir
D: ZX81 COM : ZX81ROM BIN : GREEDGUL P : HELLO P
D: CHARSET P : TEST P : BITCOIN COM : GG.SAV P
D: BITCOIN BAK : BITCOIN SAV
D>bitcoin
*** Bitcoin Mining V1.0 ***
Null message test: OK
Short message test: OK
Long message test: OK
Long message test 2: OK
Bitcoin genesis block: OK
Bitcoin example block: OK
Loading state from file D:BITCOIN.SAV
OK
Best nonce / hash found:
250707 000024A026D9AB5671B86FBA972B3A32B41CB69CA5DC43767B59AF5B7B8EA94A
Starting mining. Press ^C to exit.
Iteration: 465723, delta: 55
Text in "[...]" indicates edits. I removed repetitive output omitted for clarity, and added total run time whenever a better hash is found. As one might expect, the better the hash, the lower the probability of improving on it with subsequent hashes. Hence it takes longer and longer. As the hash rate is essentially constant, we can plot a graph of iteration number (a proxy for time) for each occasion a smaller hash is found.
![]() |
Run Time for Each Improvement (Linear) - First Ten Hashes |
![]() |
Run Time for Each Improvement (Log) |
The log graph appears to be roughly a straight line.
From the above, the best nonce / hash I found was:
Nonce: 250707 (decimal)
Hash: 000024A026D9AB5671B86FBA972B3A32B41CB69CA5DC43767B59AF5B7B8EA94A
For comparison, the hash that was used in the blockchain is:
Nonce: 2083236893 (decimal)Save File
D: ZX81 COM : ZX81ROM BIN : GREEDGUL P : HELLO P
D: CHARSET P : TEST P : BITCOIN COM : GG.SAV P
D: BITCOIN BAK : BITCOIN SAV
D>a:dump bitcoin.sav
0000 C1 7B 29 00 01 00 00 00 00 00 00 00 00 00 00 00 .{).............
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0020 00 00 00 00 00 00 00 00 3B A3 ED FD 7A 7B 12 B2 ........;...z{..
0030 7A C7 2C 3E 67 76 8F 61 7F C8 1B C3 88 8A 51 32 z.,>gv.a.....Q2
0040 3A 9F B8 AA 4B 1E 5E 4A 29 AB 5F 49 FF FF 00 1D :...K.^J)._I....
0050 C1 7B 29 00 BA 73 90 26 B9 69 9A D7 FC E6 DA 01 .{)..s.&.i......
0060 13 19 6E 01 52 0C 87 A0 39 72 73 88 55 B8 42 00 ..n.R...9rs.U.B.
0070 00 00 07 3E DA 91 10 00 1A 1A 1A 1A 1A 1A 1A 1A ...>............
D>
The file format is described in the PDF manual, included in the github repository (see link above).