Thursday, 24 November 2022

Bitcoin Mining on a Z80

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

The following is the result of running BITCOIN.COM with no saved file (BITCOIN.SAV). This means we start with the genesis block and a nonce of zero, tying to find the best (smallest) hash possible:
 
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
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)
Hash: 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
 
I have not attempted to estimate how long it would take for my Z80 system to improve on this hash, but it would clearly be far longer than the current total run time (roughly five days).

Save File

BITCOIN.COM saves the current state and best nonce / hash found in a file, BITCOIN.SAV. The CP/M DUMP command can be used to display this file:
 
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>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).

Wednesday, 9 November 2022

CP/M File System Checker (fsck)

CP/M 2.2 is a very interesting historic operating system. Though very small (approximately 3.6 KB), it manages to achieve quite a lot in this space. Most of the benefits come from its filing system, which though extremely primitive by modern standards is still quite an advance on a "bare metal" machine. The disk format is relatively simple, but still offers file sizes of up to 8 MB - an almost unthinkable size for a machine with  a 64 KB addressing space! As I usually find with such things, there are complexities in the details. It did take me a while to get my head around "logical" versus "physical" extents, for example. Descriptions exist elsewhere on the internet, such as https://www.seasip.info/Cpm/format22.html

fsck

There are a number of ways in which the system could potentially become "confused", though I have not experienced this myself. It is possible for a block to become allocated to more than one file, for example. Some sort of file system checker seems desirable, broadly similar to "fsck" or "chkdsk". A search didn't reveal a suitable one, at least not for generic systems. Writing the CP/M BIOS for ZARC left me with a reasonable understanding of the disk format, so I had a go myself. The results are here: https://github.com/rundel-tech/ZARC/tree/main/software/cpm/cpm_dev/fsck

fsck checks each directory entry for the following errors:

  • User number out of range (>15)
  • Illegal (unprintable) characters in the file name or file type (any character <0x20 or >=0x7f)
  • Extent count out of range (low byte >=32)
  • S1 out of range (not zero)
  • A file contains a duplicated physical extent
  • Block number is out of range

Other errors detected are:

  • "Sparse" files, i.e. ones with gaps in their allocations (warning)
  • Physical extents with no allocations at all (warning)
  • Record count should be 0x80 for all but the last extent.
  • A block is allocated more than once

Note that sparse files are not actually illegal in CP/M, but they are somewhat unusual and so worth warning about.

A disk usage map is displayed, and an optional surface scan is performed to check that all blocks are readable.

Example Run

The following shows fsck being used to check drive E: for errors.

A>fsck e:
*** CP/M File System Checker V1.1 ***

Directory checks complete
Errors and warnings: 0
Files found: 60
Unused directory entries: 446
Blocks used: 212

Block map ('D': directory, 'F': file and '-': unused).
DDDDDDDDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
F-------------------------------------------------------------------------------
---------FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF--------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------
Surface scan skipped
Returning to CP/M.

A>

The optional surface scan was not invoked in this case.  "fsck e: /s" would enable this.

 

Thursday, 3 November 2022

ZARC, the Z80 Anachronistic Retro Computer

I have hinted at my homebrew Z80 project called ZARC before, for example my post about Greedy Gulch. ZARC is now essentially complete, in as much as such projects are ever really finished.

ZARC With Top Cover Removed

I still tinker with the software from time to time, but there have been no hardware changes for at least a year. It seems a reasonable time to make the project data available to anyone else who is interested. As a result, the hardware and project data is now at https://github.com/rundel-tech/ZARC. I doubt anyone will want to duplicate the entire project, but parts of it may be useful for other similar "retro" projects.

ZARC Main PCB

People primarily interested in the hardware will want to look at the schematics. To save navigating through the folder hierarchy, direct links are:

The software side in particular involves a number of sub-projects, and I plan to write more about these from time to time. In the meantime, I have a usable CP/M machine that, along with the ELF Mini-Terminal, forms a stand-alone system. It can also be connected to a PC via a RS-232 to USB adaptor, which is very useful for software development and of course screenshots.

Z80 Bitcoin Mining!


Saturday, 22 October 2022

ELF Mini-Terminal Video Fix

The ELF mini-terminal developed a rather odd fault. The cursor disappeared and reverse video did not work. The CRT alignment test screen looked odd too. Investigating this issue led me to trace out some of the circuitry of the terminal.

Oscillator and Mode Selection

The 20 MHz crystal-controlled oscillator provides the clock for most of the board. A 74hc258 multiplexer is used to select between 40 and 80 column signals. In 80 column mode, the 20 MHz clock is used directly, which is quite challenging for the logic in use. A lower frequency clock is used in 40 column mode.

 

Inverse Video Logic

The 6845 CRT controller generates a CURSOR signal, which is used to invert the video signal in this case. The character generator is a SAA5055, which is intended for the now obsolete Teletext system. This is somewhat overclocked in 80-column mode, but it seems to cope. This device only uses the lower 7-bits of the RAM data, and the terminal uses the otherwise unused bit 7 to invert the video in the same way as the cursor does. There is a significant delay between RAM read data entering the SAA5055 and the associated RGB output. This requires that the invert signal be similarly delayed so the inverted characters (and the cursor) appear in the correct location. This is the purpose of the chains of flip-flops IC23 and IC28.

 

Video Output

The video supplied to the monitor is taken from the SAA5055 character generator and inverted as required. The red, green and blue signals are mixed with a software-controlled bias (the brightness defined in the terminal setup menu) before being supplied to the monitor.

The fault in this case was IC28, a 74ac374 octal latch. This inhibited the invert video signal, which I called INV_DLY2 in the diagrams above. Fortunately, these devices are still available.

The PCB has only two copper layers. The layout must have been quite a challenge, as of course this includes power and ground. In addition, the pixel clock is 20 MHz in 80-column mode, which is quite challenging for the logic families used. A slightly alarming feature is that the +12 V supply is routed to various places mixed with the logic. A momentary short-circuit could do a lot of damage.

Thursday, 18 August 2022

ELF Mini-Terminal Keyboard Debouncing Fix

Though the terminal described in a previous post does work, it suffered frequent keyboard bounce. This means that when you press a key, instead of one character begin generated the terminal will likely generate two (or perhaps even more). This is quite irritating. The most likely issue especially considering the age of the terminal seemed to be that the key switch contacts require cleaning. The real issue turned out to be quite different, however. After dismantling the keyboard and removing the cover from the main unit I noted the following:

  • The keyboard CPU is marked “AMI 8729MAJ S6803P”. The associated EPROM is a 2716. If 8729 is a date code, it is likely the 29th week of 1987.
  • The main board CPU is marked (Motorola) “MC6808P KC78795”. The associated EPROM is a 27256.

Both processors are variants of the once very popular Motorola 6800 series, described here: https://en.wikipedia.org/wiki/Motorola_6800. I have come across these before, and like the architecture. I dumped both EPROMs, as if the data for either were lost that is likely to be the end for this unusual terminal. Curiosity overcame me and I had a look at the contents.

 

Keyboard EPROM Contents

The CPU has various memory mapping modes selected by external pins. In this case, the mode is hardwired to 2, so the internal RAM is available and the ROM is external. Motorola called this an "Expanded Multiplexed Mode". I ran the code through Jeff Tranter's very useful "udis" disassembler, a "Universal Disassembler program for 8-bit microprocessors" (https://github.com/jefftranter/udis). The first few instructions are:



F800 .org $F800
;
; Reset
F800 00        .byte $00        ; Undefined?
F801 C0
F802 97        staa $14 [RAM / EPROM control]
F804 8E 00 FF  lds #$00FF       ; Setup stack
;
; Clear RAM (0xff81 + 0xff = 0x0080) to (0xffff + 0xff = 0x00fe)
F807 CE FF 81  ldx #$FF81
F80A 6F FF     clr $FF,x
F80C 08        inx
F80D 26 FB     bne $F80A        ; Loop until complete


There is something odd about this code. The very first instruction is a 0x00, which is undefined, even considering the extra instructions implemented on the 6803. Examining the main board firmware in the same way reveals the following initial instructions:

; Reset
B395 8E 7F FF  lds #$7FFF       ; Setup stack
B398 86 C0     ldaa #$C0
B39A 97 14     staa $14 [RAM / EPROM control]
B39C CE 00 80  ldx #$0080


The sequence "ldaa #$C0 / staa $14" is similar here, but with an 0x86 (ldaa - "load accumulator A") instead of 0x00. Is the keyboard firmware corrupt? Bear in mind that the CPUs are subtly different, albeit from the same family. I looked inside the keypad. but it uses a completely different approach. It has a no microprocessor, but uses discrete logic and a ROM to generate the appropriate codes. This is unfortunate, as I was hoping to compare the firmware with that of the keyboard.

Examining and commenting the rest of the keyboard code (approximately 370 bytes) revealed no further mysteries, and showed that keyboard debouncing was considered by whoever wrote it. It also revealed that the keyboard emits ASCII codes for almost every key, so there is no need for key code conversion by the main microprocessor. Other codes are used for keys that have no associated code, such as "Setup".

I patched the code so the first byte is a 0x86. The first two instructions now read:

F800 86 C0     ldaa #$C0
F802 97        staa $14 [RAM / EPROM control]

This makes  more sense. My programmer doesn’t seem to work with the 2732 EPROMs. It did seem fine with a Microchip 27C64, though. I programmed that with the 2K image replicated four times. The keyboard has obviously been designed with larger EPROMs in mind, so I didn't have to modify the board.

It works! The key bouncing issue is now resolved. I really didn't expect that. This sort of issue is easy to create when copying EPROMs if you happen to inadvertently edit the data in the programmer. I wonder if that is what happened here. It would seem strange that a hardware failure would result in just the first location being corrupt. I wonder how may other ELF terminals had the same issue.

I noted that VCC at the keyboard is just 4.41 V. That’s very low, and is caused by voltage drop in the thin, flexible coiled cable. It does seem to work, though, and I can't see an easy way of fixing this in a reasonably tidy manner.

Main Board EPROM Contents

I did disassemble the main code using the same disassembler, but didn't spend very long looking at it. The bulk of the space is used occupied by the demonstration pages.

Monday, 15 August 2022

Sennheiser EW100 G2 Repair

We use these Lavalier microphone transmitters with the PA in my local church. They are generally well constructed, and I have not had to open one until recently. The plug-in microphones do suffer, however, and I have to re-wire the microphone itself or connector about once a year. New ones don't fare much better than my repaired ones, so I seem to be doing a reasonable job.

 

Sennheiser EW100 G2

This transmitter failed completely, which is a first despite years of use. Disassembly revealed a very compact design, and an obvious issue. A power inductor had broken, so I suspect the transmitter had been dropped onto a hard surface. Finding a replacement was tricky, as I had to match not only the electrical characteristics, but also the replacement had to fit in the limited space available. After examining the wreckage and the datasheet for the associated boost regulator IC, I selected an EPCOS B82472P6223M000 (22 uH +/-20% 1.45A) part as a good match. Replacement was straightforward.

Inductor Replaced (top centre of PCB)

The inductor is the black square component at the top centre of the PCB. The associated regulator IC is on the underside.

The Inductor Shouldn't Look Like This!

After replacement and reassembly, everything worked as it should. The output signal is clearly visible using a FUNcube Pro+ receiver, which I find very handy for testing radio-related things.

Transmitted Signal

The main purpose of this post is to make life easier for anyone trying to repair one of these or a similar unit. I did see an advertisement offering replacement inductors at vastly inflated prices. I declined the offer, but it does imply that this is a known issue with these units.


 


Sunday, 14 August 2022

Telex Model 530 Cordless Headphone

I look after the PA for my local church. As with most such systems, it includes an inductive loop system. This is fundamentally quite simple, and consists of a single long turn of wire around the inside wall of the church driven by an amplifier. The amplifier contains an aggressive automatic level control (AGC), and attempts to maintain a constant drive level over a wide range of signal levels. A suitably configured hearing aid will receive this signal and deliver it to the listener without unwanted acoustic interference. One issue is that unless you have a suitable aid, it is not easy to tell whether the loop is working or not. Some decades ago, a "Telex Model 530 Cordless Headphone" was purchased. This is a headset with a loop receiver and amplifier built in. It is many years since this was last used and nobody seemed to know much about it. I did find this catalogue entry - from 1989!

Telex 1989 Catalogue Page

Removing the pads from the earphones reveals a battery compartment. Unfortunately, I can find no reference to what battery it requires. Several batteries and cells would physically fit in the holder. The required item would be approximately 12 mm diameter and 28 mm long. This isn't much help, as after some research I concluded that the voltage could be 1.5, 3.6, 7.5 or 12 V! Telex do still exist, but I an not confident that I would receive much help for such an old product. Dismantling revealed the following:

Disassembly
 

Please ignore the glue stick, used to prop the headset apart! The circuit is very simple.

Schematic

Ignore the "12 V" marked by the battery as this was my initial guess based on dimensions. The circuit seems to operate nicely from anything above about 2 V, but was prone to oscillation. The IC is marked "MCC-2637632", but I could not find any information on it. The single electrolytic capacitor is fine in both value and ESR, so I left it alone. The preset potentiometer sets the bias level. I was surprised by the lack of supply decoupling, especially considering the cable run from one side of the headset (with the battery) to the other (with the amplifier). Adding a 100 μF capacitor between pins 2 and 4 of the IC made it stable. In the end, I opted to add a micro USB socket on a cable and wired it across the battery holder contacts. This allows the amplifier to be powered from any convenient USB battery pack. Given the low current consumption (a few mA), it would last very many hours powered this way.

I tried this in the church. It is possible to reduce the PA loudspeaker amplifier drive signals to zero and play music through the loop amplifier only. This allows me to investigate the effectiveness of the loop in various areas without having to contend with audio through both the speakers and headset. It loop really is very effective, with only a few small dead spots.

Popular Posts