The Epson HX-20 is a very early laptop from about 1982. It's got a pair of H63C01 CPUs running at 614kHz, a microcassette drive (which can be swapped for a ROM cartridge), a printer, and a built in 4 line graphics LCD, and 16kB of RAM. Mine comes with the expansion unit which gives it an extra 16kB of RAM. It's got an internal OS in ROM and a pretty good BASIC in ROM as well. There's space internally for an extra ROM and the expansion unit has room for two more.
One nice feature is that it could run for quite a while off internal NiCD batteries, and they would preserve the RAM while it was off; together with the OS in ROM, it meant it was ready to use instantly.
In some ways, it's as much an exhibition of Epson's capabilities with the tiny printer, they sell similar ones for tills, the LCD and the microcassette. A good stash of documents is on Phil's page. The documentation is excellent, although the Tech reference manual doesn't always agree with the main schematic, which in general is correct.
As I received it, it was clean and basically worked. The NiCD's had been removed and a power lead bodged on the NiCD connector. It ran on 6v but the LCD was faint, and I had problems with stability and with the RS232 port. Also, it really shouldn't need 6v, the NiCD was supposed to be 4.8v max, with a 4.5v low voltage cutoff.
It's pretty easy to work on, the two halves of the case fold out (after unplugging a couple of ribbon cables) and the main board is a simple two sided PCB with through-hole components. The back of the main board is very well labelled, so finding pins to check is easy. One constraint is that there's not much height, so they used pretty flat components. After some basic checks I started digging further.
One mistake I made was concentrating too much on short spikes on the power caused by the DC-DC from the 232. Setting a trigger on the 4.5v (which is what triggers the 'charge battery') and zooming in on the spikes initially got me hideous output like:
The ~40kHz repetition is the frequency of the DC-DC, and we can see a truly horrid drop to under 3V and a peak of 6.52v (higher than the input supply). While this is awful, it hides a deeper problem which is only seen on a slower timebase (albeit taken closer to the DC-DC than the last one):
That slow drop off made me realise the problem was not a high frequency issue; tracing that back led me to the bodged battery in connector which was high resistence, but didn't really cause a problem until the higher current load when the 232 was operating. Replacing the battery lead got me the, slightly more sensible:
While this is still very spikey, it's worth noting it's on the Q2 transistor right next to the DC-DC. I finally added an extra 1uF capacitor between R2 and the nearest ground I could find (which is still a little bit far away).
With all these fixes, I could run some basic 232 testing right down to 4.9v input.
Now that I was running with the capacitors in the battery box, I was supplying power via the original input socket. I noticed that the diode D2 (a S5277B), has a drop of about 0.7v, I replaced this with a 11DQ10 whose drop is closer to about 0.5v. This lets me keep the capacitors full at about 5.8v input, and even 5v input would be useful to keep memory contents. Note: It's not safe to remove the diode, it's not only for reverse input protection, it also stops the batteries feeding back to the power input.
Examining the listing it's a simple till program for a Garden centre in St.Albans, listing items such as Shrubs, Conifers, Bedding Plants etc. I captured the listing over 232 to Minicom on a Linux PC by using the command:
SAVE"COM0:(68N1F)",AWhich is 4800bps, 8bit, no parity, 1 bit stop, no hardware flow control, with a A for ASCII.
(A fun aside is that the RAM chips in the expansion are 6117 rather than the more common 6116. They have a lower power mode where they disconnect from the bus more to save power, this does lead to them having slightly different enable signals).
Intext is a very simple word processor and comms package. One of it's uses was for journalists to enter a story on the road and upload it (over an acoustic coupler). It has the ability to embed simple formatting controls, but isn't WYSIWYG on the display. The manual explains that they've agreed the code with the Journalist and typographical unions!
Selecting Intext gave an error about not having enough memory though and to look at the manual. Alas the only manual I could find was for the Cassette version which didn't mention the error.
My friend had assumed that this was a RAM fault, but PRINT FRE(0) from BASIC showed the magic 29275 bytes free that everyone was expecting there to be with the expansion unit. I later confirmed that I could read and write the expansion unit RAM by using the 'S' command in the monitor.
Entering the monitor normally shows the BASIC ROM paged in, at one - non repeatable - point I managed to get Intext to trap as it printed its error out, and that landed in the monitor, and I could see the INTEXT header around 8000. Alas that was not repeatable. I eventually managed it by running the BASIC commands:
POKE &H7E,&H80 POKE &H30,&H30The first of those enables poking at IO space from BASIC, the second changes the paging bit, paging out BASIC, and paging INTEXT. Unsurprisingly BASIC isn't too happy to be paged out, and traps - landing in the monitor, with INTEXT paged in.
It's easy to show the memory from the monitor using D 8000 and I also verified that it seemd to be 8kB, not 16kB. The monitor offers the 'W' command to write out an area of memory, alas this only allows writing to the internal or external cassette, and not to serial. Based on the documentation in the Operations manual chapter 9, I came up with the magic:
A T 8000 L A000 O 0 E 0 WC,INTEXT.BINWhich dumps from 8000..A000 to the external cassette with the given filename. (I think L should actually be 9FFF since my file was one byte too long).
parecord -r --channels=1 epson.raw --file-format=raw --format=u8to record the audio as an unsigned 8 bit, mono, raw file; for me this came out at 44.1kHz. The Epson audio output is a surprisingly crude square wave, with 0 and 1 represented by either a 1kHz or 2kHz cycle:
Looking at the output file showed the INTEXT header near the beginning. But the file was 8960 bytes rather than the 8192 I had expected.
After some time I realised that this was because the file was in SAVEM format, and had to write another level of decoding. Thus that repo also includes desavem to extract the contents. This left me with an 8193 byte file - I think the extra byte is because the 'L' of the monitor command should be one byte before the end.
Note that neither of my programs checks the CRC or checksums.
git clone https://github.com/NationalSecurityAgency/ghidra.git git fetch origin pull/6314/head:h6031and then copied the files from 'Ghidra/Processors/MC6800/data/languages/*6303*' into an existing, prebuilt Ghidra installation. I had to run 'ant' in the in the 'data' directory for it to use the new processor definition.
I could easily see the expected entry header, that I'd also seen in the Epson's Monitor:
8000 ba ?? BAh
8001 41 ?? 41h A
8002 ff ?? FFh
8003 ff ?? FFh
8004 80 0d addr entry
8006 49 4e 54 ds "INTEXT"
45 58 54
that indicates the entry is at 800d.
I could also see the error text I was trying to understand:
str_memtoosmall
80d7 4d 65 6d ds "Memory too small. See Manual.\f"
6f 72 79
20 74 6f
As I was going along I'd add labels for things I spotted. The start of the code had:
entry
800d 96 78 LDA A,INIFLA_0078
800f 84 80 AND A,#0x80
8011 27 47 BEQ print_notenoughmem
That 78 is labelled in the Epson docs as 'INIFLA' and it tells you if BASIC has initialised. The function it
was branching to, I could see referenced the error message above and following it through I could see it
printed it and then waited for a key to go back to the menu, so labelled it 'print_notenoughmem'.
OK, so the first thing then is to make sure I start BASIC before I try starting INTEXT. Doesn't help, OK, digging further I see that Ghidra is showing a separate piece of code that ends up at the 'print_notenoughmem' routine:
8047 fc 01 34 LDD BSWTAD_0134
804a c3 00 06 ADD D,#0x6
804d fd 7f 58 STD DAT_7f58
8050 fe 7f 58 LDX DAT_7f58
8053 ee 02 LDX 0x2, IX
8055 8c 02 00 CPX #0x200
8058 2e 0c BGT LAB_8066
print_notenoughmem
Ghidra reconstructed this as:
_DAT_7f58 = (short *)(BSWTAD_0134 + 6);
sVar1 = *(short *)(BSWTAD_0134 + 8);
if (sVar1 != 0x200 && sVar1 + -0x200 < 0 != (SBORROW2(sVar1,0x200) != false))
goto print_notenoughmem;
Using the Software Reference manual, I get that BSWTAD is the 'start of the BASIC application area' and the value 8 bytes in from that is 'RAM file area size'.
mail: fromwebpage@treblig.org irc: penguin42 on libera.chat | matrix: penguin42 on matrix.org | mastodon: penguin42 on mastodon.org.uk