Elcotel reverse engineering
Initially I was struggling trying to program Elcotel 5 controller and attempted to reverse-engineer the configuration files. Now that I can program the phone, it is less of a priority, but I'll still be working on the rate files. Unlike with Protel, you can't easily create one, since the local NPA-s (area codes) cannot be removed once added, and they determine the home area code. You can't start from scratch - need to work on the existing file, which usually is one for Florida supplied with the software.
With a limited time, I have some goals. Ultimate goal is to better understand it and be able to program in alternative ways.
Rate files. To be able to create one myself. Unfortunately the binary structure is very irregular, so far I wasn't able to decode it.
28C64 rates EEPROM. Most of them are soldered, I removed mine and put a socket so I can easily see the memory map and what's changed. It might be worth sometimes to preserve the original state, vs. just erasing. At first I just wanted to pull the station ID, bypass and password. Its location varies and overall it seems overengineered, just as the Elcotel electronics
Firmware EPROM structure. Initially I wanted to disassemble it, tools are available even online for this popular Z80 CPU. But, surprisingly the output is sort of a nonsense. Perhaps obusfcated in some way? But data & address lines to the CPU are not scrambled. The other goal in my mind and potentially a super easy way to help other people programming their phones, is to replace the standard rates and config which you get upon factory reset. That way we could just send out the program ROM, which sits in the socket.
Modem transmission. Someone already started, there's a
youtube video essentially to hack the station ID. Pretty simple, but not so easy with the password. The idea is to create alternative, modern program so you don't need to install windows 95.
Strange story: I ordered a “new” 28C64 chip on ebay, and I got a recycled one. It's hard to get a really new - old stock. Once I read it in my programmer, I thought I was going insane. It wasn't blank and it was… Elcotel 5 rate chip. Station number I extracted appears to be 989-773-7717 (Saginaw, Midland, and Bay City in Michigan).
Strange story 2: Payphone I removed in Phoenix AZ, rate EPROM had a station number from Albuquerque - 505
Rates EEPROM
Some random notes so far:
When phone is reset to factory defaults (battery disconnect –> press the button / short pins –> connect battery –> off-hook, release button once relay clicked). EEPROM is not wiped completely. Only the first byte is set to 0xAA and I think the program runs from EPROM.
There likely is some sort of checksum. Altering some bytes (e.g. phone number) results in a general malfunction. I wasn't able to dial out anymore, but didn't experiment enough to be sure.
10-digit payphone number as set in the PNM, is not saved as such in the EEPROM. NPA (area code) is altered to the first listed intra-state NPA of the rate file. Intra-state NPAs are those “local” NPAs that have all NXX listed (exchange codes, first 3 digits of the 7-digit number) with specific rates. But the first one (my assumption) serves as this payphone's NPA. This is important, because any 7-digit (local) dialing by the user results in sending 10-digit number into the line, with that NPA. Why is it so - I don't know. In my case I set the phone number to 602-555-6050, which resulted in a number stored in the EEPROM (and reported when locally accessing register 404) to be 941-555-6050, because the only rate file I had was for Florida, the one delivered with PNM Plus.
Stored numbers and strings are a combination of coding seen in different systems. Some examples:
null-terminated (0x00) strings, like in the file header (C-style)
First byte determining the length, no extra termination (Turbo-Pascal style) - rate file description or the password in EEPROM (which can be letters too! e.g. 12341234 –> 08 31 32 33 34 31 32 33 34 [hex])
First byte for length, then numbers, 00 replaced with 00 01 (bypass code, station ID and payphone number in the EEPROM). Example of bypass code 77777777: 08 07 07 07 07 07 07 07 07 (initial 08 being the length), station ID 6050: 04 06 00 01 05 00 01 (04 = length, 01 meaning the preceding was an actual zero, not the termination)
EEPROM structure is dynamic, i.e. from two examples I had, they're very different. Perhaps due to difference in software version? So I decided to first work on the rate file.
Rate file structure is even more complex and I'm trying to figure it out by making small changes and saving each step for comparison. But so far I'm only getting confused.
Voice EPROM
Briefly looking into 128kB voice EPROM, I made some conclusions:
most common bytes are 0x55 and 0xAA. This is binary 01010101 / 10101010, indicating it is most likely a one-bit delta modulation of some sort
EPROM is split in two pages, each starting with some sort of index I ignored for now.
A simple program to decode is listed below. It does work, although the sound quality is not as good as original. Listen to the samples on Elcotel page. Input file must be VOICE_ROM.BIN and it outputs to output.raw. Output format is 8 bit signed PCM, 9690 Hz [estimation based on the digitized dial tone in the EPROM, but 9600 number seems much more familiar - a common modem baud rate].
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
int main(void) {
FILE *ifp, *ofp;
uint8_t src_value; //byte read
float DSP_0 = 0; //current integrator output
float DSP_1 = 0; //previous integrator output (needed for averaging two samples, somehow it reduces the noise)
float n = 0.2151; //filter coefficient - strength of the new bit
int8_t out_value; //output PCM stream value (signed 8-bit) (somehow 16-bit would not improve anything)
int b=0; //bit, converted to +/-1
ifp = fopen("VOICE_ROM.BIN", "rb");
ofp = fopen("output.raw", "wb");
assert(ifp);
assert(ofp);
do {
src_value = fgetc(ifp);
for (int i=0; i<8; i++) {
DSP_1=DSP_0;
b=(src_value&1)?1:(-1); //+1 for 1, -1 for 0
src_value>>=1;
DSP_0=DSP_0*(1-n)+n*b;
out_value=round((DSP_0/2+DSP_1/2)*127); //averaging 2 samples + rescale +/-1 into +/-127 (signed int8)
if (i&1) fputc(out_value, ofp); //writing every other bit
}
} while (!feof(ifp));
fclose(ofp);
fclose(ifp);
return 0;
}