Personal tools

Module memory usage

From Wij vertrouwen stemcomputers niet

programming / reader slot

the slot closest to the edge of the circuit board is the reader/voting slot, this slot has databit 7 not connected, so it always reads as '1', due to a pull-up resistor.

on the module itself, this bit is pulled down to ground, causing the flashchip to see a '0', when it is plugged into the reader slot.

this is to prevent this slot from issuing a flashchip erase command, see P28F010_Summary

this slot is visible in memory at address 0x7c0000

the other slot, the 'programming' slot is visible in memory at address 0x3c0000

Vote storage

If we study a memory module dump, we see that votes are stored as eight bytes, such as "02 02 04 04 05 05 06 06", where this is a vote for party two, candidate 4. The last four bytes correspond in the sense that each nibble maps to the corresponding nibble in the first four according to the following table:

1 > 3
2 > 5
3 > 6
4 > 6
5 > 5
6 > 3
7 > 0
8 > 7
9 > 4
A > 2
B > 1
C > 1
D > 2
E > 4
F > 7

This is a Hamming code error correction value which can detect single and double-bit errors. Hamming codes can also correct single-bit errors. If we ever decide to not only create but also read the code to fix votes, this code might be useful. FYI: the table in the ES3B code is here:

ROM:0001FD1E low_nibble_fec_table:dc.b 0, 3, 5, 6, 6, 5, 3, 0, 7, 4, 2, 1, 1, 2, 4, 7
ROM:0001FD2E high_nibble_fec_table:dc.b 0, $30, $50, $60, $60, $50, $30, 0, $70, $40, $20, $10, $10, $20, $40, $70

Stored in order

We can tell that the votes in our short test elections were stored in order, and that the time the votes was cast makes up the location: here's the second test case (locations relative to module offset)

157a0 (4f8 more than previous)
15d68 (5c8 more)
16410 (6a8 more)
188a8 (2498 more)

As you can see the last vote was cast after a longer pause.

  • Pascal: Hmmm. Lets use a microphone to obtain accurate system-on times and vote-cast timings?

Inverted copy

Then there is also a second copy of each vote in a different memory location. Here the votes are stored in inverted form such as "7D 7D 7E 7E 02 02 04 04" for party 2 candidate 1. Inverted location is at offset 0x6000 from normal location.

Voting Memory Module Address Map

All offsets relative to 3c0000

0000 Module type
0002 A1 or A3 - identifies odd/even flash chip
0003 A2 or A4 - identifies odd/even flash chip
0004 Erasure count (stored as 7 bits per byte)
0008 Module id
0018 Voting date
0028 FF = (1st?) election open, 00 = election closed) 
002c Checksum of candidate list, stored as 7bits-per-byte, 4 bytes, but only 28 bits are used.
       ( every bit7 is 0, checksum=(b0<<21)|(b1<<14)|(b2<<7)|b3 )
0030 Voting computer serial number, every byte repeated once, docs say recorded on first vote 
0040 Null terminated string with name of the election
0090 Two bytes 31 31  - election type
.... Filled with FF
0092-1000 .. also seems to have significance
1000 Candidate lists per party.
   36 party slots with 30 candidates each.
   each party list is 155 bytes long, with 5 bytes per candidate
   the first entry points to the party name
      +00: (1 byte) List Number
      +01: (1 byte) Candidate number (0=party name )
      +02: (3 bytes) (7 bits-per-byte): Offset from 0x1000 to string
25cc-28a4 Null terminated strings, first five party names, then all candidate names
28a5-13fff Random data (see below)
13000-14000 .. also seems to have significance
14000-1ffff Single votes
  14000-19fff Normal storage
  1a000-1ffff Inverted storage

so with 8 bytes per vote, a maximum of 3072 votes can be stored in each module.

About the seemingly random data

this perlscript will exactly generate the random data as found in the memory module from offset 0x2d5c - 0x14000

use integer;
sub genrandom {
return ($_[0]>>16)&0xff;
my $seed1=0x3d98;
my $seed2=0x400a;
for (my $ofs=0x2d5c ; $ofs<0x14000 ; $ofs+=2) {
    if (($ofs&0x1f)==0) {
        printf("%08lx: ", $ofs);
    printf(" %02x", genrandom($seed1));
    printf(" %02x", genrandom($seed2));
    if (($ofs&0x1f)==0x1e) {

the top 8 bits of the seed don't matter. the seed is a 100Hz clocktick counter since startup. so the first random sequence was written 157.68 seconds after the reader-writer was turned on. and the second sequence 6.26 seconds later.

... assuming the clock really is 100Hz, that would mean it would take approximately 1 minute to flash an entire 256k module.

the memmodule checksum