/ src / firmware.c
firmware.c
  1  #include <stdint.h>
  2  #include <string.h>
  3  #include <stdio.h>
  4  
  5  typedef uint8_t bool;
  6  
  7  // Addresses
  8  // 0x25 [8] copy of LFSR/cryptovariable
  9  //     0x25 is set to 0x9c at the beginning of the mainloop.
 10  // 0x2d [8] LFSR/crypto variable
 11  // 0x35 [7] effective crypto variable
 12  // 0x3f [1] index into cipherblock for next byte to xor with, 0x2c..0x34
 13  // 0x7f41 [8] = cryptovariable/state
 14  // 0x72d7 [15] crypto key - keyed in via keyboard
 15  
 16  static uint8_t ctrlvar[7];      // INTMEM[0x35..0x3c]
 17  static uint8_t cipherblock_idx; // INTMEM[0x3f]
 18  static uint8_t cipherblock[8];  // INTMEM[0x2d..0x34]
 19  static uint8_t state[8]; // EXTMEM[0x7f41..0x7f49]
 20  
 21  uint8_t INTMEM[256] = {0};
 22  uint8_t EXTMEM[65536] = {0};
 23  uint8_t PROGMEM[32768] = {0};
 24  
 25  static const uint8_t fixed_key[15]="2V58RGKLXN49TKD"; // 0x2c4d
 26  
 27  #define TRACE(pc) printf("PC: %04x ", pc); \
 28    printf("                                                        ");\
 29    for(int iter=0;iter<8;iter++) {          \
 30      if(iter>0 && iter%4==0)                \
 31        printf(" ");                         \
 32      printf("%02x", INTMEM[0x25+iter]); }   \
 33    printf(" ");                             \
 34    for(int iter=0;iter<8;iter++) {          \
 35      if(iter>0 && iter%4==0)                \
 36        printf(" ");                         \
 37      printf("%02x", cipherblock[iter]); }   \
 38    printf(" ");                             \
 39    for(int iter=0;iter<7;iter++) {          \
 40      if(iter>0 && iter%4==0)                \
 41        printf(" ");                         \
 42      printf("%02x", ctrlvar[iter]); }       \
 43    printf("\n");
 44  
 45  const uint8_t SBOX[128] = {0xF4, 0xAF, 0x8A, 0xD1, 0x3B, 0x02, 0xE8, 0x20,
 46                             0xCD, 0x65, 0x96, 0x1C, 0x47, 0xB3, 0x79, 0x5E,
 47                             0x18, 0x8B, 0xE3, 0xAE, 0x7D, 0x4A, 0x94, 0xDF,
 48                             0x69, 0x30, 0xBC, 0x56, 0xF5, 0x07, 0x21, 0xC2,
 49                             0xBD, 0x72, 0x9C, 0x59, 0xAE, 0x17, 0xF3, 0x61,
 50                             0x24, 0xC8, 0x40, 0xDF, 0xE6, 0x8A, 0x35, 0x0B,
 51                             0x27, 0x4D, 0x56, 0xC8, 0x91, 0xB3, 0x70, 0x84,
 52                             0xF5, 0xEF, 0xD2, 0xAE, 0x3A, 0x1C, 0x0B, 0x69,
 53                             0x47, 0x9F, 0x80, 0x5C, 0x0A, 0x68, 0xA1, 0xEB,
 54                             0xB9, 0x2D, 0x75, 0xF3, 0x1E, 0x32, 0xD6, 0xC4,
 55                             0xB3, 0xAE, 0xED, 0x09, 0x91, 0xD4, 0x38, 0x26,
 56                             0x6A, 0xC0, 0xFB, 0x75, 0x82, 0x5F, 0x4C, 0x17,
 57                             0x59, 0x27, 0x16, 0x4D, 0xDB, 0xEF, 0x04, 0x9C,
 58                             0xF0, 0xB8, 0x62, 0xCE, 0x3A, 0xA1, 0x73, 0x85,
 59                             0x18, 0x5D, 0x47, 0x6E, 0xC5, 0xA0, 0x9B, 0xFA,
 60                             0x32, 0xE3, 0x8C, 0x01, 0xDF, 0x74, 0x29, 0xB6};
 61  
 62  #define BITv(B,n) ((B >> n) & 1)
 63  #define BYTE(B7,B6,B5,B4,B3,B2,B1,B0) ((B7 << 7) | (B6 << 6) | (B5 << 5) | (B4 << 4) | (B3 << 3) | (B2 << 2) | (B1 << 1) | B0)
 64  
 65  static uint8_t assign_bit(const uint8_t dst, const int bit, const int pos) {
 66    return (dst & ~(1U << pos)) | (bit << pos);
 67  }
 68  
 69  static uint8_t rrc(const uint8_t val, const uint8_t cf_in, uint8_t *cf_out) {
 70    *cf_out = val & 1;
 71    return (cf_in << 7) | (val >> 1);
 72  }
 73  
 74  
 75  
 76  /*?*/ void sbt(uint8_t cb_start) { // fn_1d20
 77    // main function doing the dance called only from sbt_full() and sbt_short()
 78    // short: sbt(0x7f44, 0x30);
 79    // full:  sbt(0x7f41, 0x2d);
 80  
 81    uint8_t cf = 0; // carry flag
 82    uint8_t tmp, r1, tmp1;
 83  
 84    // initialize LFSR
 85    // workbuf, state
 86    memcpy(cipherblock + cb_start, state + cb_start, 8-cb_start); // 1d27
 87    TRACE(0x1d27);
 88  
 89    // step 1 - initial fill  - LFSR (1 + x^32 + x^63) 64 steps
 90    // workbuf
 91    for(int i = 0; i<8; i++) {
 92      tmp = (cipherblock[0] ^ cipherblock[4]) >> 1 | (cipherblock[1] ^ cipherblock[5]) << 7;
 93      for(int j = 7; j >= 0; j--) {
 94        tmp1 = cipherblock[j];
 95        cipherblock[j] = tmp;
 96        tmp = tmp1;
 97      }
 98    } // 1d3c
 99    TRACE(0x1d3c);
100  
101    // copy back LFSRed key to extmem src.
102    // workbuf, state
103    memcpy(state, cipherblock, 8); // 1d48
104    TRACE(0x1d48);
105  
106    // copy work-array to bitmapped area, as a copy from which to
107    // calculate the fixed permutation of the next step.
108    uint8_t fillcopy[8];
109    memcpy(fillcopy, cipherblock, 8); // 1d60
110    TRACE(0x1d60);
111  
112    // step 2 - fixed bit permutation
113    cipherblock[0] = BYTE(BITv(fillcopy[4],4),BITv(fillcopy[1],5),BITv(fillcopy[3],1),BITv(fillcopy[7],6),BITv(fillcopy[0],5),BITv(fillcopy[6],0),BITv(fillcopy[5],7),BITv(fillcopy[2],3));
114    cipherblock[1] = BYTE(BITv(fillcopy[3],2),BITv(fillcopy[0],7),BITv(fillcopy[7],1),BITv(fillcopy[1],0),BITv(fillcopy[6],3),BITv(fillcopy[4],5),BITv(fillcopy[5],4),BITv(fillcopy[2],0));
115    cipherblock[2] = BYTE(BITv(fillcopy[7],5),BITv(fillcopy[3],3),BITv(fillcopy[1],3),BITv(fillcopy[5],1),BITv(fillcopy[0],3),BITv(fillcopy[2],4),BITv(fillcopy[6],2),BITv(fillcopy[4],1));
116    cipherblock[3] = BYTE(BITv(fillcopy[5],5),BITv(fillcopy[3],0),BITv(fillcopy[0],1),BITv(fillcopy[4],3),BITv(fillcopy[1],6),BITv(fillcopy[6],7),BITv(fillcopy[2],2),BITv(fillcopy[7],3));
117    cipherblock[4] = BYTE(BITv(fillcopy[2],5),BITv(fillcopy[6],4),BITv(fillcopy[4],7),BITv(fillcopy[0],6),BITv(fillcopy[5],6),BITv(fillcopy[7],7),BITv(fillcopy[3],5),BITv(fillcopy[1],2));
118    cipherblock[5] = BYTE(BITv(fillcopy[6],5),BITv(fillcopy[4],2),BITv(fillcopy[3],6),BITv(fillcopy[5],2),BITv(fillcopy[1],7),BITv(fillcopy[2],6),BITv(fillcopy[7],4),BITv(fillcopy[0],2));
119    cipherblock[6] = BYTE(BITv(fillcopy[5],0),BITv(fillcopy[7],2),BITv(fillcopy[1],4),BITv(fillcopy[3],4),BITv(fillcopy[4],6),BITv(fillcopy[6],1),BITv(fillcopy[0],0),BITv(fillcopy[2],1));
120    cipherblock[7] = BYTE(BITv(fillcopy[6],6),BITv(fillcopy[0],4),BITv(fillcopy[1],1),BITv(fillcopy[7],0),BITv(fillcopy[3],7),BITv(fillcopy[2],7),BITv(fillcopy[4],0),BITv(fillcopy[5],3));
121    TRACE(0x1e30);
122  
123    // 1e30
124    // main loop
125    uint8_t thingy, // wth is thingy
126      ctrlvar_iter_ctrl = 0x9c; // 1e32
127    for(int round = 0; round < 8; round++) {
128  
129      // update control variable
130      for(int i=(ctrlvar_iter_ctrl & 0x80)?5:2;i!=0; i--) { // 1e35, 1e3b, 1e59
131        // ctrlvar_iter_ctrl has this binary value: 10011100 so 4x2
132        // and 4x5 iterations
133        cf = BITv(ctrlvar[3],4); // 1e44
134        TRACE(0x1e46);
135        for(int i=0; i<7; i++) {
136          ctrlvar[i] = rrc(ctrlvar[i], cf, &cf); // 1e4a
137          TRACE(0x1e4e);
138        } // 1e4e
139        TRACE(0x1e57);
140        ctrlvar[3] = assign_bit(ctrlvar[3],cf,3); // 1e57
141      }
142      // rotate ctrlvar_iter_ctrl left
143      ctrlvar_iter_ctrl = ctrlvar_iter_ctrl << 1 | ctrlvar_iter_ctrl >> 7; // 1e5c .. 1e5f
144      TRACE(0x1e61);
145  
146      // 1e61 .. 1e8e
147      uint8_t nibble_swap_ctrl_words[4];
148      for(int i=0;i<4;i++) nibble_swap_ctrl_words[i] = state[3-i];
149      // rotate state right 8x
150      tmp = state[7];
151      memmove(state+1,state,7);
152      state[0]=tmp;
153  
154      TRACE(0x1e8e);
155      // 1f1c
156      nibble_swap_ctrl_words[0] ^= BYTE(BITv(ctrlvar[1], 5), BITv(ctrlvar[4], 1), BITv(ctrlvar[1], 2), BITv(ctrlvar[5], 6), BITv(ctrlvar[2], 7), BITv(ctrlvar[5], 3), BITv(ctrlvar[2], 4), BITv(ctrlvar[5], 0));
157      nibble_swap_ctrl_words[1] ^= BYTE(BITv(ctrlvar[2], 1), BITv(ctrlvar[6], 5), BITv(ctrlvar[3], 6), BITv(ctrlvar[6], 2), BITv(ctrlvar[0], 7), BITv(ctrlvar[3], 3), BITv(ctrlvar[0], 4), BITv(ctrlvar[3], 0));
158      nibble_swap_ctrl_words[2] ^= BYTE(BITv(ctrlvar[0], 1), BITv(ctrlvar[4], 5), BITv(ctrlvar[1], 6), BITv(ctrlvar[4], 2), BITv(ctrlvar[1], 3), BITv(ctrlvar[5], 7), BITv(ctrlvar[1], 0), BITv(ctrlvar[5], 4));
159      nibble_swap_ctrl_words[3] ^= BYTE(BITv(ctrlvar[2], 5), BITv(ctrlvar[5], 1), BITv(ctrlvar[2], 2), BITv(ctrlvar[6], 6), BITv(ctrlvar[3], 7), BITv(ctrlvar[6], 3), BITv(ctrlvar[3], 4), BITv(ctrlvar[6], 0)); // 1f2c
160      TRACE(0x1f2c);
161      int nibble_swap_index = 0;
162  
163      for(int i = 0; i < 4; i++) {  // 1f34
164        TRACE(0x1f36);
165        uint8_t nibble_swap_ctrl = nibble_swap_ctrl_words[i]; // 1f36
166        while(1) { // really true? nah there's a break at the end,
167                   // but it's not trivial to convert to for(;;)
168                   // basically this loops 4 times, based on INTMEM[0x28] % 4 == 0
169  
170          tmp = cipherblock[nibble_swap_index >> 1];
171          if(!(nibble_swap_index & 1)) {
172            tmp >>= 4;
173          }
174          tmp &= 0xf;
175          thingy = tmp;
176  
177          if(nibble_swap_ctrl >> 6 == 0) {
178            if((tmp & 0xc) == 0) {  // 1f52
179              tmp = cipherblock[(nibble_swap_index ^ 8) >> 1]; // 1f60 .. 1f64
180              TRACE(0x1f64);
181              if(!((nibble_swap_index ^ 8) & 1)) {
182                // swap nibbles
183                TRACE(0x1f6b);
184                tmp >>= 4; // 1f6b
185              }
186              tmp &= 0xf; // 1f6c
187  
188              thingy += tmp & 3; // 1f6f
189  
190              thingy = ((((tmp << 6) | (tmp >> 2)) + thingy) & 3) | 0xc; // 1f78 .. 1f7e
191              TRACE(0x1f80);
192            } else {
193              TRACE(0x1f52);
194              thingy -= 4;
195            }
196  
197          } else if(nibble_swap_ctrl >> 6 == 1) {
198            if((tmp & 0x3) == 0) {  // 1f94 .. 1f96
199              thingy = (thingy << 6) | (thingy >> 2); // 1fa2 .. 1fa6
200  
201              tmp = cipherblock[(nibble_swap_index ^ 2) >> 1];
202              TRACE(0x1fb1);
203              if(!((nibble_swap_index ^ 2) & 1)) {
204                  TRACE(0x1fb3);
205                  tmp >>= 4; // 1fb3
206              }
207              TRACE(0x1fb4);
208              tmp &= 0xf; // 1fb4
209  
210              thingy += tmp & 3; // 1fb7, 1fbb
211  
212              tmp = ((((tmp << 6) | (tmp >> 2)) + thingy) & 3);
213              thingy = ((tmp >> 6) | (tmp << 2)) | 3;  // 1fbd .. 1fc8
214  
215              TRACE(0x1fca);
216            } else {
217              thingy-=1; // 1f98 .. 1f9d
218              TRACE(0x1f9f);
219            }
220  
221          } else if(nibble_swap_ctrl >> 6 == 2) {
222            if((tmp & 0xc) == 0xc) { // 1fde .. 1fe0
223              TRACE(0x1fe3);
224              thingy &= 3; // 1fe3 .. 1fe7
225  
226              tmp = cipherblock[(nibble_swap_index ^ 4) >> 1]; // 1fe9 .. 1ff1
227              TRACE(0x1ff1);
228              if(!((nibble_swap_index ^ 4) & 1)) {
229                TRACE(0x1ff4);
230                tmp >>= 4; // 1ff4
231              }
232              TRACE(0x1ff5);
233              tmp &= 0xf; // 1ff5
234  
235              thingy += tmp & 3; // 1ff8, 1fbb
236  
237              thingy = ((((tmp << 6) | (tmp >> 2)) + thingy) & 3); // 1ffe .. 2005
238              TRACE(0x2007);
239            } else {
240              thingy += 4; // 200a .. 200e
241              TRACE(0x2010);
242            }
243  
244          } else {
245            if((tmp & 3) == 3) { // 2021 .. 2023
246              thingy = (thingy >> 2) & 3; // 202c
247  
248              TRACE(0x202e);
249              tmp = cipherblock[(nibble_swap_index ^ 1) >> 1];  // .. 202e..2036
250              TRACE(0x2036);
251              if(!((nibble_swap_index ^ 1) & 1)) {
252                TRACE(0x2039);
253                tmp >>= 4; // 2039
254              }
255              tmp &= 0xf; // 203a
256  
257              thingy += tmp & 3; // 203d, 2041
258  
259              tmp = ((((tmp << 6) | (tmp >> 2)) + thingy) & 3); // 2043 .. 2048
260  
261              thingy = ((tmp << 2) | (tmp >> 6)) ; // 204a .. 204c
262              TRACE(0x204e);
263            } else {
264              thingy++;
265            }
266          }
267          TRACE(0x2052);
268          r1 = nibble_swap_index >> 1;
269          tmp = cipherblock[r1]; // 2058
270          TRACE(0x2058);
271          if(!(nibble_swap_index & 1)) {
272            tmp &= 0xf;
273            cipherblock[r1] = tmp; // 205d
274            TRACE(0x205d);
275            tmp = thingy;
276            tmp = (tmp <<4) | (tmp >> 4); // 2060
277          } else {
278            TRACE(0x2063);
279            tmp &= 0xf0;        // 2063 ..
280            cipherblock[r1] = tmp;
281            TRACE(0x2066);
282            tmp = thingy; // 2066
283          }
284  
285          TRACE(0x2068);
286          cipherblock[r1] |= tmp; // 2068 .. 2069
287          nibble_swap_index++;
288          if((nibble_swap_index & 3) == 0)  {
289            break;
290          }
291          nibble_swap_ctrl = (nibble_swap_ctrl << 2) | (nibble_swap_ctrl >> 6); // 2072 .. 2076
292          TRACE(0x2076);
293        }
294      }
295  
296      TRACE(0x2087);
297      // step 6. fix byte permutation
298      tmp = cipherblock[0];           // 2087 ..
299      cipherblock[0] = cipherblock[3];
300      cipherblock[3] = cipherblock[4];
301      cipherblock[4] = cipherblock[6];
302      cipherblock[6] = cipherblock[7];
303      cipherblock[7] = cipherblock[2];
304      cipherblock[2] = cipherblock[1];
305      cipherblock[1] = cipherblock[5];
306      cipherblock[5] = tmp;              // .. 2097
307  
308      // step 8 nibble switch
309      tmp = BYTE(BITv(ctrlvar[3], 5), BITv(ctrlvar[6], 1), BITv(ctrlvar[0], 6), BITv(ctrlvar[3], 2),
310                 BITv(ctrlvar[0], 3), BITv(ctrlvar[4], 7), BITv(ctrlvar[0], 0), BITv(ctrlvar[4], 4));
311  
312      TRACE(0x20c1);
313      cf = BITv(ctrlvar[6], 1);
314      for(int i = 0;  // 20c3
315          i < 8;      // 20cc
316          i++) {      // 20cb
317        tmp = rrc(tmp, cf, &cf); // 20c5
318        tmp1 = cipherblock[i];
319        cipherblock[i] = tmp;
320        tmp = tmp1;
321        if(cf) {
322          // swap nibbles
323          tmp = ((tmp & 0xf) <<4) | ((tmp & 0xf0) >> 4); // 20c9
324        }
325        tmp1 = cipherblock[i]; //
326        cipherblock[i] = tmp;  // 20ca
327        tmp = tmp1;          //
328        TRACE(0x20cb);
329      }
330      TRACE(0x20d2);
331      // step 10 SBOXes
332      for(int i = 0;        // 20d2
333            //ptr = 0x2102; // 20cf
334            // ptr = 0;     // 20cf
335          i < 8;            // 20ef
336          i++) {            // 20e3
337        //ptr+=0x10)     {  // 20e4 .. 20ed
338        TRACE(0x20d4);
339        tmp1 = SBOX[(i*16) + (cipherblock[i] & 0xf)] & 0xf; // 20d4 .. 20d8
340        tmp = cipherblock[i];
341        cipherblock[i] = tmp1;  // 20da
342        TRACE(0x20db);
343        tmp = ((tmp & 0xf) <<4) | ((tmp & 0xf0) >> 4); // 20db
344        tmp &= 0xf;
345        cipherblock[i] |= (SBOX[tmp + (i*16)] & 0xf0); // 20de .. 20e2
346        TRACE(0x20e3);
347      }
348    }
349    TRACE(0x20f9);
350  }
351  
352  void sbt_short(void) { // 1d13
353    // (deep) entry point
354    // called from 25b4
355    //sbt(0x7f44, 3);
356    sbt(3);
357  }
358  
359  void sbt_full(void) {  // 1d04
360    // called from prepare() 2x and from crypt_byte()
361    //sbt(0x7f41, 0);
362    sbt(0);
363  }
364  
365  
366  uint8_t crypt_byte(uint8_t res) { // 1cf5
367    // entry point
368    // called from: 1b77 1bea 2368 247a 261b 2731
369    res ^= cipherblock[/*r1*/cipherblock_idx++];
370    cipherblock_idx++;
371    if(cipherblock_idx == 7) {
372       sbt_full();
373       cipherblock_idx=0;
374    }
375    return res;
376  }
377  
378  void sbt_init(uint8_t *output, const uint8_t *key) {
379    // only called from default_entry() and parametric_entry()
380    if(key == NULL) {
381      key = fixed_key;
382    }
383  
384    // preliminary fill = key[:8] ^ intmem[0x2d:0x30]
385    //for(i=0x2d;i<0x35;i++, ptr++) {
386    for(int i=0;i<8;i++) {
387      if(i<3) {
388        cipherblock[i] ^= key[i]; // 1c91
389      } else {
390        cipherblock[i]= key[i];
391      }
392    } // 1c98
393  
394    // preliminary control (56 bits) = key[8:]
395    memcpy(ctrlvar, key+8, 7); // 1c9f
396    TRACE(0x1ca2);
397  
398    // preserve preliminary fill
399    memcpy(state, cipherblock, 8); // 1cab
400    TRACE(0x1cae);
401  
402    sbt_full();
403    TRACE(0x1cb1);
404  
405    // hard coded fixed fill at 0x2c5c
406    uint8_t fixed_fill[8] = { 0xf5, 0xc0, 0x7a, 0x10, 0x8a, 0xaf, 0x17, 0xcf };
407  
408    // output of sbt_full becomes control variable
409    memcpy(ctrlvar, cipherblock, 7);
410    // initial fill becomes fixed_fill
411    memcpy(cipherblock, fixed_fill, 8); // 1cbf, 1cc4
412    TRACE(0x1cc2);
413  
414    // 0x7f41 also becomes fixed fill
415    memcpy(state, cipherblock, 8);
416    TRACE(0x1cd1)
417  
418    sbt_full();
419    TRACE(0x1cd4);
420  
421    memcpy(INTMEM + 0x25, output,3);
422    cipherblock_idx=0;
423    memcpy(state + 5, output, 3);
424    TRACE(0x1cf4)
425  }
426  
427  void default_entry(void) { // 1c5d
428    // entry point
429    // called from 1aad if (EXTMEM[0x7308] & 1)
430    // 1aad seems to print the "key: {key}"
431    // push 0x7f41    // 1c5d .. 1c63
432    memset(INTMEM+0x2d, 0, 3); // 1c67 .. 1c6b
433    sbt_init(state, PROGMEM + 0x72d7);
434  } // 1c6d
435  
436  /*?*/ void parametric_entry(uint8_t *output, const uint8_t *key) { // 1c6f
437    // entry point
438    // called from 1b42, 1bc3, 2362, 25d6
439    // input is dptr, flag is passed in the carry flag of the psw
440    // push input // 1c6f .. 1c71
441    memcpy(cipherblock, output, 3);
442    sbt_init(output, key);
443  }
444  
445  /*
446   *  checks if what ever intmem[42:43] points to at is 5 letter codes
447   *  seperated by spaces and terminated by \xfe.  if so, uses the first
448   *  5 letter block with parametric_entry() and then does something
449   *  weird with each remaining letter block with bitwise manipulations
450   *  together with intmem[0x4a:0x4c] and calls crypt byte on the result
451   *  of the manipulation.
452   */
453  void decrypt() {
454    uint8_t r6, tmp, r2, r3; //, r5=1;
455    uint8_t tbit, CY;
456    // r5 = print_output_query();
457    uint16_t dptr = INTMEM[42] << 8 | INTMEM[43];
458    do {
459      for(int i=6;i>0;i--) {
460        tmp = EXTMEM[dptr];
461        if(tmp == 0xfe) break;
462        dptr++;
463        if((i!=1 && tmp & 0x80) || tmp!=' ') {
464          INTMEM[44] = dptr >> 8;
465          INTMEM[45] = dptr & 0xff;
466          printf("ERROR IN TEXT, PRESS LEFT ARROW!");
467          return;
468        }
469      }
470    } while(EXTMEM[dptr]!=0xfe);
471  
472    parametric_entry(&EXTMEM[INTMEM[42]<<8|INTMEM[43]],0);
473    r6=0;
474    dptr = (INTMEM[42] << 8 | INTMEM[43]) + 6;
475    // see sketchy.py for how the bits of *dptr and *intmem[4a..4c] are processed before output
476    do {
477        uint8_t r0 = 0x4a;
478        for(r2 = 5; r2>0; r2--) {
479          tmp = EXTMEM[dptr];        // 1b58
480          CY=tmp<0xfe;               // set carry due to CJNE at 1b59
481          if(tmp!=0xfe) dptr++;
482          tmp--; // 1b5f dec a
483          tmp = (tmp << 4) | (tmp >> 4); // 1b60 swap a
484          for(r3 = 4;r3>0;r3--) {
485            // 1b61 rlc a
486            tbit=CY;
487            CY=tmp & 0x80;
488            tmp=(tmp<<1) | tbit;
489            // 1b62 xchg A, INTMEM[r0]
490            uint8_t tmp1 = tmp;
491            tmp=INTMEM[r0];
492            INTMEM[r0]=tmp1;
493            // 1b63 rrc a
494            tbit = CY;
495            CY = tmp & 1;
496            tmp = tbit | (tmp >> 1);
497            // 1b64 xchg a, intmem[r0]
498            tmp1 = tmp;
499            tmp=INTMEM[r0];
500            INTMEM[r0]=tmp1;
501            r0++;
502            CY=r0<0x4d;
503            if(r0==0x4d) r0=0x4a;
504          }
505        }
506        for(r0 = 0x4a;r0!=0x4d;r0++) {
507          tmp = INTMEM[r0]; // 1b71
508          CY=r0<0x4c;
509          if(r0==0x4c) {
510            // 1b75 rrc a
511            tbit=CY;
512            CY=tmp&1;
513            tmp = (tbit << 7) | (tmp >> 1);
514          }
515  
516          // 1b76 rrc a
517          tbit=CY;
518          CY=tmp&1;
519          tmp = (tbit << 7) | (tmp >> 1);
520  
521          tmp = crypt_byte(tmp);
522  
523          tmp &= 0x3f; // 1b7a
524          printf("%c", tmp); // really putc_maybe_print()
525        }
526        r6++;
527        //if(((r6 & 3)==0) && (r5!=0)) get_keypress(); // r5 == skip_print
528  
529        // 1b91..
530      } while (EXTMEM[dptr++]!=0xfe && EXTMEM[dptr]!=' ' && EXTMEM[dptr]!=0xfe);
531      //if(r5!=0) {
532      //  print_crlf();
533      //}
534  }
535  
536  void encrypt(uint8_t *xram, uint8_t *ram) {
537    // skip_print = print_output_query();
538    uint16_t dptr = 0x72d2; //&DAT_EXTMEM_72d2;
539    uint8_t tmp, in_PSW=0;
540  
541    // get 5 values [1..16] by xoring what is in ram
542    // with the value of the 8bit reloading TL0 timer
543    // this is like a "nonce/iv" but quite predictable
544    // since this loop is static, if there are no interrupts
545    // then TL0 increases by 11 in each iteration.
546    // unless there are interrupts...
547    // see also emu-timer-1ba8.py
548  
549    // the following are "nonces" from original hw
550    // BAMLK HCAMM EGLLA LEEIC JDMNJ
551  
552    for(int i = 5; i>0; i--, dptr++) {
553      tmp = 19; //TL0;
554      tmp = (xram[dptr] ^ tmp) & 0xf;
555      in_PSW = (in_PSW & 0xdd) | (tmp == 0) << 1;
556      tmp = tmp + 1;
557      *xram = tmp;
558      //putc_maybe_print(tmp,skip_print);
559    }
560    //putc_maybe_print(' ',skip_print);
561    //parametric_entry(0x72d2,0);
562  
563    //copy result of parametric_entry to 0x72d2
564    memcpy(xram+0x72d2, ram+0x2d, 5); //1bc6..1bcf
565  
566    uint8_t tbit, CY;
567    uint8_t r6=0;
568    dptr = ram[0x42] << 8 | ram[0x43];
569    // see sketchy.py for how the bits of *dptr and *intmem[4a..4c] are processed before ouptu
570    while(xram[dptr]!=0xfe) {
571      for(uint8_t r0 = 0x4a;r0<0x4d;r0++) {
572        tmp = xram[dptr++];
573        if(tmp == 0xfe) {
574          tmp = 0x5f;
575        } else {
576          tmp++;
577        }
578        tmp = crypt_byte(tmp);
579        ram[r0] = tmp;
580      }
581      uint8_t r0 = 0x4a;
582      CY = 0; // the cjne at 1bef clears CY due to r0==0x4d
583      for(uint8_t r2 = 5; r2>0; r2--) {
584        tmp = 0; // 1bf8
585        for(uint8_t r3 = 4;r3>0;r3--) {
586          // 1bf9 xchg a, intmem[r0]
587          uint8_t tmp1 = tmp;
588          tmp=ram[r0];
589          ram[r0]=tmp1;
590  
591          // 1bfa rrc a
592          tbit = CY;
593          CY = tmp & 1;
594          tmp = tbit | (tmp >> 1);
595  
596          // 1bfb xchg a, intmem[r0]
597          tmp1 = tmp;
598          tmp=ram[r0];
599          ram[r0]=tmp1;
600  
601          // 1bfc rlc a
602          tbit=CY;
603          CY=tmp & 0x80;
604          tmp=(tmp<<1) | tbit;
605  
606          if(++r0 == 0x4d) r0 = 0x4a;
607        }
608        tmp++;
609        //putc(tmp); // really putc_maybe_print()
610      }
611      //putc(' '); // really putc_maybe_print(' ')
612      tmp = ++r6; // 1c10 .. 1c11
613      //if (((tmp & 1) != 1) && (r5  != '\0')) get_keypress(); // r5 = skip_print
614    }
615    // if(r5==0) print_crlf(); // r5 := skip_print
616  }
617  
618  uint8_t compact_str(uint8_t param, uint8_t *ptr, uint8_t op) { // 279f - 27d4
619    // compact 6 bit symbol param at address ptr
620    INTMEM[0x21] &= ~(1<<3); // clr 21.3
621    switch(op) {
622    case 0: { // just store the lower 6 bits at the lower 6 bits of the destination.
623      INTMEM[*ptr] = param & 0x3f;
624      if(*ptr == 0x49) {
625        INTMEM[0x21] |= (1 << 3); // set 21.3
626      }
627      return 1;
628    }
629    case 1: { // store the lowest 2 bits at the top 2 bits of the destination
630              // store the bits 3-6 at the low nibble at dest+1
631      uint8_t tmp = (param >> 2 | param << 6);
632      INTMEM[*ptr++] |= (tmp & 0xc0);
633      INTMEM[*ptr] = (INTMEM[*ptr] & 0xf0) | (tmp & 0xf);
634      return 2;
635    }
636    case 2: { // store the low nibble at the hi nibble of dest
637              // and the high nibble at the low nibble of dest
638      INTMEM[*ptr] = (INTMEM[*ptr] & 0xf) | (param << 4);
639      INTMEM[++*ptr] = param >> 4;
640      return 3;
641    }
642    default: { // store the low 6 bits at the high 6 bits of dest
643      INTMEM[*ptr++] |= ((param >> 2) | (param << 6)) & 0xfc;
644      return 0;
645    }
646    }
647  }
648  
649  uint16_t fn_2807(uint16_t ptr) {
650    INTMEM[0x49] &= 0x3f;
651    //EXTMEM[ptr] = INTMEM[0x40];
652    //for(int r0 = 0x41; r0 != 0x4a; r0++) {
653    //  EXTMEM[++ptr] = INTMEM[r0];
654    //}
655    memcpy(EXTMEM + ptr, INTMEM + 40, 10);
656    uint8_t last;
657    for(int r3 = 78; r3 != 0; r3--) {
658      // shift 80 bits INTMEM[0x49:0x40:-1] to the right by 1,
659      // shifting the last bit into the carry
660      bool cf=0;
661      for(int r0 = 0x49, r4 = 0xa; r4 != 0; r4--, r0--) {
662        last = rrc(INTMEM[r0], cf, &cf);
663        INTMEM[r0] = last;
664      }
665      if(cf) {
666        INTMEM[0x40] ^= 0x26;
667        INTMEM[0x41] ^= 0x3;
668        INTMEM[0x42] ^= 0x10;
669        INTMEM[0x43] ^= 0x37;
670        INTMEM[0x44] ^= 0x1a;
671        INTMEM[0x45] ^= 0x64;
672        INTMEM[0x46] ^= 0x1;
673      }
674    }
675    // INTMEM[0x40..0x48] = [last] + INTMEM[0x40:0x47]
676    for(int r0 = 0x40, tmp = last; ++r0 != 0x48;) {
677      // swap tmp and intmem[r0]
678      int tmp1 = INTMEM[r0];
679      INTMEM[r0] = tmp;
680      tmp = tmp1;
681    }
682  
683    // the CJNZ at 0x2840 (previous loop) clears CY when falling through
684    bool cf = 0;
685    for(int r3 = 2;r3 != 0; r3--) {
686      // rotate INTMEM[0x47:0x40:-1] right
687      for(int r0 = 0x47, r4 = 8; r4 != 0; r4--, r0--) {
688        INTMEM[r0] = rrc(INTMEM[r0],cf,&cf);
689      }
690    }
691    INTMEM[0x40] &= 0xc0;
692    EXTMEM[ptr++] |= INTMEM[0x40];
693    for(int r0 = 0x41; r0 != 0x47; r0++, ptr++) {
694      EXTMEM[ptr] = INTMEM[r0];
695    }
696    return ptr;
697  } // 2862
698  
699  bool fn_2beb(uint8_t param) {
700    uint16_t ptr = (*((uint16_t*)(INTMEM + 0x52)) + 1) & 0xfbff ;
701    if(INTMEM[0x54] != (ptr>>8) || INTMEM[0x55] != (ptr&0xff)) {
702      EXTMEM[ptr]=param;
703      // disable interrupts
704      *((uint16_t*) INTMEM + 0x52) = ptr;
705      // enable interrupts
706      return 0;
707    }
708    return 1;
709  } // 2c1e
710  
711  void fn_259c(const uint8_t *key) {
712    // does it all, first calls sbt_short, then parametric_entry, then crypt_byte
713    INTMEM[0x21] &= 0xef; // clear bit 4
714  
715    EXTMEM[0x840a] = 2; // 259e .. 25a3
716  
717    INTMEM[0x2d] = EXTMEM[0x8410];
718    INTMEM[0x2e] = EXTMEM[0x8411];
719    INTMEM[0x2f] = EXTMEM[0x7104];
720    sbt_short();  // 25b4
721    INTMEM[0x2f] &= 0xfe; // 25b7
722  
723    uint8_t r0 = 0x40, r1 = 0x2d;
724    uint16_t ptr = 0x7104;
725    for(;r1 != 0x30; r0++, r1++, ptr++) {
726      EXTMEM[ptr] = INTMEM[r1];
727      INTMEM[r0] = INTMEM[r1];
728    }
729  
730    INTMEM[0x42] |= EXTMEM[0x7308] & 1;
731  
732    // originally EXTMEM[0x7308] & 1 was used as a param to
733    // parametric_entry to toggle the hardcoded fixed key
734    parametric_entry(EXTMEM + 0x7104, key); // 25d6
735    // fn_0d58 inlined
736    // calls fn_d84(*((uint16_t*) (EXTMEM+0x7163)))
737    uint8_t r2, r3, r6, tmp;
738    uint8_t r4 = EXTMEM[0x7163];
739    uint8_t r5 = EXTMEM[0x7164];
740    // fn_d84 inlined
741    // uint16_t fn_0d84(uint16_t r4r5)
742    do {
743      r6 = EXTMEM[0x8412];
744      r3 = ~r6 + 1;
745      r2 = ~EXTMEM[0x8413] + (r3==0);
746    } while(EXTMEM[0x8412] != r6); // if 0x8412 changed since the start of the loop repeat the calc.
747    r3 = r3 - r5;
748    tmp = r2 - r4; // d9e
749    if(r2 < r4) tmp+=0x90;
750    if(0x8f < tmp) tmp+=0x70;
751    uint16_t fn_0d58res = (tmp << 8) | r3;
752    // eofn fn_d84 - might be buggy, but is close enough.
753    *((uint16_t*) (INTMEM + 0x44)) = fn_0d58res; // 25dc .. 25df
754    INTMEM[0x43] = INTMEM[0x18]; // INTMEM[0x18] == BANK3_R0
755    if(INTMEM[0x43] == 0) {
756      tmp = (INTMEM[0x19] & 3);
757      if(tmp & 2) {
758        ptr = 0x72cf;
759      } else {
760        ptr = 0x72f7;
761      }
762    } else {
763      ptr = 0x7102;
764      tmp = 0;
765    }
766    INTMEM[0x46]=tmp;
767    uint8_t code = 1;
768    uint8_t iptr = 0x46;
769    code = compact_str(EXTMEM[ptr], &iptr, code);
770    code = compact_str(EXTMEM[++ptr], &iptr, code);
771    ptr = 0x7300;
772    code = compact_str(EXTMEM[ptr++], &iptr, code);
773    compact_str(EXTMEM[ptr], &iptr, code);
774  
775    for(r0=0x44;r0!=0x4a;r0++) {
776      crypt_byte(INTMEM[r0]);
777    }
778    fn_2807(0x7600);
779    INTMEM[0x52]=0x78;
780    INTMEM[0x53]=0x78;
781    INTMEM[0x54]=0;
782    INTMEM[0x55]=0;
783    for(int i=16, ptr=0x7600;i!=0;i--, ptr++) {
784      fn_2beb(EXTMEM[ptr]);
785    }
786  } // 2641
787  
788  void fn_2362(const uint8_t *key) {
789    // todo starts really at 22f0
790    // calls parametric_entry and in a loop crypt_byte
791    // ...
792    EXTMEM[0x7107] = INTMEM[0x40]; // 234d
793    EXTMEM[0x7108] = INTMEM[0x41];
794    //bool cf = INTMEM[0x42] & 1; // INTMEM[0x42] & 1 determines if key is default or daily.
795    EXTMEM[0x7109] = INTMEM[0x41] & 0xfe;
796  
797    parametric_entry(EXTMEM + 0x7107, key); // 2362 // instead of key, CY is passed as param, toggling the fixed key
798  
799    for(uint8_t r0 = 0x44; // 2365
800        r0 != 0x4a;
801        r0++) {
802      crypt_byte(INTMEM[r0]);
803    }
804  
805    INTMEM[0x18 /* BANK3_R0 */] = INTMEM[0x43];
806    INTMEM[0x40] = INTMEM[0x46]; // 2373
807    // fn_27d5(0x46, 1) inlined below
808    INTMEM[0x21] &= 0xf7; // clear bit 3
809    uint8_t tmp = INTMEM[0x46] & 0xc0;  // 27e8 .. 27e9
810    // exchange low nibble of tmp with INTMEM[0x47] // 27ec
811    tmp = (tmp & 0xf0) | (INTMEM[0x47] & 0xf);
812    INTMEM[0x47] = (INTMEM[0x47] & 0xf0); // no lower nibble from tmp necessary since it has been &ed with 0xc0
813    // rotate 2x left
814    uint8_t r3 = (tmp >> 2) | (tmp << 6); (void)r3;
815    // fn_27d5(0x47, 2) inlined below
816    tmp = (INTMEM[0x48] & 3 ) | INTMEM[0x47]; // 27f3 .. 27fa
817    // swap nibbles of tmp
818    uint8_t r4 = (tmp >> 4) | (tmp << 4); (void)r4;
819    // ....
820    // todo ends at 2563
821  }
822  
823  int main(void) {
824    //const uint8_t foo[16]="\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\xa5";
825    //memcpy(EXTMEM + 0x7f41, foo, sizeof foo);
826  
827    //sbt();
828    const uint8_t key[15]="\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01";
829    memcpy(PROGMEM + 0x72d7,key, sizeof key);
830  
831    default_entry();
832  
833    printf("result of sbt: ");
834    for(int i=0;i<8;i++) {
835      printf("%02x ", cipherblock[i]);
836    }
837    printf("\n");
838    for(int i=7;i>0;i-=2) {
839      putchar((((cipherblock[i]>>4^cipherblock[i-1]) & 0xf) | 0x40) + 1);
840    }
841    printf("\n");
842  
843    return 0;
844  }