GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/joels/Current/lispbm/src/lbm_image.c Lines: 83 445 18.7 %
Date: 2025-04-09 11:39:30 Branches: 34 377 9.0 %

Line Branch Exec Source
1
/*
2
    Copyright 2025 Joel Svensson  svenssonjoel@yahoo.se
3
4
    This program is free software: you can redistribute it and/or modify
5
    it under the terms of the GNU General Public License as published by
6
    the Free Software Foundation, either version 3 of the License, or
7
    (at your option) any later version.
8
9
    This program is distributed in the hope that it will be useful,
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
    GNU General Public License for more details.
13
14
    You should have received a copy of the GNU General Public License
15
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
18
19
#include <extensions.h>
20
#include <lbm_image.h>
21
#include <heap.h>
22
#include <env.h>
23
#include <lbm_flat_value.h>
24
#include <eval_cps.h>
25
#include <extensions.h>
26
27
// Assumptions about the image memory:
28
// * It is part of the address space.
29
// * Image is always available at the same address (across reboots)
30
// * It is a write-once memory.
31
// * Can be cleared in its entirety.
32
// * Can check on a byte-level is "is-writable" (has a known initial state when cleared)
33
34
// Details
35
// * @const_start @const_end is tricky.
36
// * Constant heap is needed because of small amount of RAM.
37
// * Arbitrary pointers will be tricky.
38
39
// Want to be able to build an image incrementally.
40
// Can @const_start @const_end be used in conjuction with a
41
// more exlicit image manipulation subsystem.
42
43
//TBD:
44
// * does an image contain running threads ? (I would prefer if no)
45
//   instead there is a startup-entry that represents some code that will
46
//   be executed after setting up an image.
47
//   This startup-entry can spawn threads and initialize resources.
48
// * Will we have a heap-image or will all bindings move into the const heap.
49
50
// FEB 26:
51
// -- There will be no "heap-image" or "memory-image"
52
//    Just flattened and stored bindings from the environment (which is as good but likely smaller).
53
// -- There will be an image of the const-heap. in fact the const heap lives in the image always.
54
//    A bit problematic with building on image incrementally as it is in flash and the contents cannot be changed.
55
//    Contents can be added though!  (keep track of const-heap write-ptr is an issue)
56
// -- Maybe we should implement image for a read-write memory and then make the flash situation a special case?
57
// -- Maybe size fields should always be in bytes.
58
// -- TODO: a flatten function that flattens a value directly to flash and also does not flatten things that are
59
//          already in flash, but rather then just refers to them.
60
61
// FEB 27:
62
// -- Symbol numbering will be an issue.
63
//    * Store the symboltable in the image and restore it on boot.
64
//    * Names already in flash can be refered to.
65
//    * Names in ram can be copied.
66
//    * Entire subtable may already be in flash - leave in place and refer to it.
67
// -- loading an image and then adding to it can be tricky to make possible.
68
//    * const-heap write pointer needs to be stored. (but if it is stored then it cannot be changed)
69
//      Could allow multiple "const-heap-write-pointer" fields in the image and use the one that appears last...
70
//    * Symboltable could be created incrementally in a similar way. Append later symbol_table data fields
71
//      to the previously loaded.
72
73
// FEB 28:
74
// -- Symbol numbering problem. The structure of the symboltable may
75
//    need to change. It is currently impossible to append a symbol list stored
76
//    in flash to the global symbol table. A richer structure is needed.
77
// -- The symbols in the SYMTAB can all be in flash (all in the image) and
78
//    all name strings can be written to flash as well.
79
//    * May be easiest if names go to the flash heap (as it is now)
80
//      and table entries are a tagged field in the image (1 extra byte per symbol...)
81
//    * Means the image must be initialized (to a degree) before symbols are created.
82
83
// MARCH 1:
84
// -- Symbols are added to and restored from the image.
85
// -- lbm_add_symbol_const, still creates a lbm_memory list structure.
86
//    Const symbols should also be stored into the image and add_symbol_const
87
//    should check and reuse stored symbol id.
88
//    Check order of initialization to see how easy this is to fix.
89
90
// Offline image tools
91
// - Image compaction: remove overwrite fields and compact the image.
92
// - Change of base address: relabel all memory accesses.
93
// - ...
94
95
96
// MARCH 5
97
// -- Can constants (anything on const heap) contain references into non-constant heap?
98
//    - I think not, but should verify this.
99
//    - eval_cps move_to_flash performs a deep copy into flash.
100
101
// Endianess woes...
102
// - little endian     least-significant byte at least address
103
// - big endian        most-significant byte at least address
104
// - all platforms we target currently are little-endian
105
//
106
//  0x11223344
107
//     | | | '--- [44] addr
108
//     | | '----- [33] addr + 1
109
//     | '------- [22] addr + 2
110
//     '--------- [11] addr + 3
111
//
112
// Images are going to be mainly little endian.  (what endianess does flatvalues use? I think BE)
113
114
// constant heap should be 4byte aligned so that there are 2 unused low end bits
115
// in all cell-pointers into constant heap.
116
117
// March 8
118
// -- flattening lead to duplication of shared nodes.
119
//    if a = '(1 2 3) and b = (cons 4 a) and c = (cons 5 a)
120
//    then the result of flattening a b c each contains a full copy of a.
121
// -- flattening a value that in turn points to a constant value, duplicates
122
//    the constant value.
123
124
//  TODO: Put more info into the IMAGE_INITIALIZED FIELD
125
//        - 32/64 bit  etc
126
127
#ifdef LBM64
128
#define IMAGE_INITIALIZED (uint32_t)0xBEEF4001    // [ 0xBEEF4001 ]
129
#else
130
#define IMAGE_INITIALIZED (uint32_t)0xBEEF2001    // [ 0xBEEF2001 ]
131
#endif
132
                                            // Address downwards ->
133
#define CONSTANT_HEAP_IX  (uint32_t)0x02    // [ 0x02 | uint32]
134
#define BINDING_CONST     (uint32_t)0x03    // [ 0x03 | key | lbm_uint ]
135
#define BINDING_FLAT      (uint32_t)0x04    // [ 0x04 | size | key | flatval ]
136
#define STARTUP_ENTRY     (uint32_t)0x05    // [ 0x05 | symbol ])
137
#define SYMBOL_ENTRY      (uint32_t)0x06    // [ 0x06 | NEXT_PTR |  ID | NAME PTR ] // symbol_entry with highest address is root.
138
#define SYMBOL_LINK_ENTRY (uint32_t)0x07    // [ 0x07 | C_LINK_PTR | NEXT_PTR | ID | NAME PTR ]
139
#define EXTENSION_TABLE   (uint32_t)0x08    // [ 0x08 | NUM | EXT ...]
140
#define VERSION_ENTRY     (uint32_t)0x09    // [ 0x09 | size | string ]
141
// Size is in number of 32bit words, even on 64 bit images.
142
143
// To be able to work on an image incrementally (even though it is not recommended)
144
// many fields are allowed to be duplicated and the later ones have priority
145
// over earlier ones.
146
147
148
#define DOWNWARDS true
149
#define UPWARDS   false
150
151
static lbm_image_write_fun image_write = NULL;
152
153
static uint32_t *image_address = NULL;
154
static int32_t write_index = 0;
155
static uint32_t image_size = 0;
156
static bool image_has_extensions = false;
157
static char* image_version = NULL;
158
159
uint32_t *lbm_image_get_image(void) {
160
  return image_address;
161
}
162
163
uint32_t lbm_image_get_size(void) {
164
  return image_size;
165
}
166
167
934148
int32_t lbm_image_get_write_index(void) {
168
934148
  return write_index;
169
}
170
171
bool lbm_image_has_extensions(void) {
172
  return image_has_extensions;
173
}
174
175
87696
uint32_t read_u32(int32_t index) {
176
87696
  return *((uint32_t*)(image_address + index));
177
}
178
179
uint64_t read_u64(int32_t index) {
180
  // image_addres is an u32 ptr. so addr + i is a step of i * 4 bytes
181
  return *((uint64_t*)(image_address + index));
182
}
183
184
4714836
bool write_u32(uint32_t w, int32_t *i, bool direction) {
185
4714836
  bool r = image_write(w, *i, false);
186
4714836
  (*i) += direction ? -1 : 1;
187
4714836
  return r;
188
}
189
190
191
bool write_u64(uint64_t dw, int32_t *i, bool direction) {
192
  uint32_t *words = (uint32_t*)&dw;
193
194
  // downwards   ... hw   lw
195
  //                 ix  ix-1
196
  // upwards     hw   lw ...
197
  //            ix+1  ix
198
199
  // true = downwards
200
201
  bool r = true;
202
  if (direction) {
203
    r = r && write_u32(words[1], i, direction);
204
    r = r && write_u32(words[0], i, direction);
205
  } else {
206
    r = r && write_u32(words[0], i, direction);
207
    r = r && write_u32(words[1], i, direction);
208
  }
209
  return r;
210
}
211
212
// fv_write function write values as big endian.
213
214
uint32_t fv_buf_ix = 0;
215
uint8_t  fv_buf[4] = {0};
216
bool fv_write_u8(uint8_t b) {
217
  bool r = true;
218
  if (fv_buf_ix >= 4) {
219
    r = write_u32(((uint32_t*)fv_buf)[0], &write_index, UPWARDS);
220
    memset(fv_buf,0,4);
221
    fv_buf_ix = 0;
222
  }
223
  fv_buf[fv_buf_ix] = b;
224
  fv_buf_ix++;
225
  return r;
226
}
227
228
bool fv_write_flush(void) {
229
  if (fv_buf_ix == 0) return true;
230
  else {
231
    bool r = write_u32(((uint32_t*)fv_buf)[0], &write_index, UPWARDS);;
232
    fv_buf_ix = 0;
233
    memset(fv_buf,0,4);
234
    return r;
235
  }
236
}
237
238
bool fv_write_u32(uint32_t w) {
239
  uint8_t * bytes = (uint8_t*)&w;
240
  return
241
    fv_write_u8(bytes[3]) &&
242
    fv_write_u8(bytes[2]) &&
243
    fv_write_u8(bytes[1]) &&
244
    fv_write_u8(bytes[0]);
245
}
246
247
bool fv_write_u64(uint64_t dw) {
248
  uint8_t * bytes = (uint8_t*)&dw;
249
   return
250
     fv_write_u8(bytes[7]) &&
251
     fv_write_u8(bytes[6]) &&
252
     fv_write_u8(bytes[5]) &&
253
     fv_write_u8(bytes[4]) &&
254
     fv_write_u8(bytes[3]) &&
255
     fv_write_u8(bytes[2]) &&
256
     fv_write_u8(bytes[1]) &&
257
     fv_write_u8(bytes[0]);
258
}
259
260
261
2207070
bool write_lbm_uint(lbm_uint ptr_val, int32_t *i, bool direction) {
262
#ifdef LBM64
263
  return write_u64(ptr_val, i, direction);
264
#else
265
2207070
  return write_u32(ptr_val, i, direction);
266
#endif
267
}
268
269
bool write_lbm_value(lbm_value v, int32_t *i, bool direction) {
270
#ifdef LBM64
271
  return write_u64(v, i, direction);
272
#else
273
  return write_u32(v, i, direction);
274
#endif
275
}
276
277
// ////////////////////////////////////////////////////////////
278
// Flatten a value into image
279
280
// TODO: Consants things that are stored in the image
281
//       does not need to be flattened. Could refer to these by
282
//       reference. Some new kinds of flat values needs to be added
283
//       for this referencing to work.
284
285
// TODO: Symbols in a flat_value in an image can be stored as
286
//       its numerical representation rather than its string rep.
287
288
static bool i_f_cons(void ) {
289
  return fv_write_u8(S_CONS);
290
}
291
292
static bool i_f_lisp_array(uint32_t size) {
293
  // arrays are smaller than 2^32 elements long
294
  bool r = fv_write_u8(S_LBM_LISP_ARRAY);
295
  r = r && fv_write_u32(size);
296
  return r;
297
}
298
299
static bool i_f_sym(lbm_value sym) {
300
  lbm_uint sym_id = lbm_dec_sym(sym);
301
  bool r = fv_write_u8(S_SYM_VALUE);
302
  #ifndef LBM64
303
  r = r && fv_write_u32(sym_id);
304
  #else
305
  r = r && fv_write_u64(sym_id);
306
  #endif
307
  return r;
308
}
309
310
static bool i_f_i(lbm_int i) {
311
  bool res = true;
312
#ifndef LBM64
313
  res = res && fv_write_u8(S_I28_VALUE);
314
  res = res && fv_write_u32((uint32_t)i);
315
#else
316
  res = res && fv_write_u8(S_I56_VALUE);
317
  res = res && fv_write_u64((uint64_t)i);
318
#endif
319
  return res;
320
}
321
322
static bool i_f_u(lbm_uint u) {
323
  bool res = true;
324
#ifndef LBM64
325
  res = res && fv_write_u8(S_U28_VALUE);
326
  res = res && fv_write_u32((uint32_t)u);
327
#else
328
  res = res && fv_write_u8(S_U56_VALUE);
329
  res = res && fv_write_u64((uint64_t)u);
330
#endif
331
  return res;
332
}
333
334
static bool i_f_b(uint8_t b) {
335
  bool res = true;
336
  res = res && fv_write_u8(S_BYTE_VALUE);
337
  res = res && fv_write_u8(b);
338
  return res;
339
}
340
341
static bool i_f_i32(int32_t w) {
342
  bool res = true;
343
  res = res && fv_write_u8(S_I32_VALUE);
344
  res = res && fv_write_u32((uint32_t)w);
345
  return res;
346
}
347
348
static bool i_f_u32(uint32_t w) {
349
  bool res = true;
350
  res = res && fv_write_u8(S_U32_VALUE);
351
  res = res && fv_write_u32(w);
352
  return res;
353
}
354
355
static bool i_f_float(float f) {
356
  bool res = true;
357
  res = res && fv_write_u8(S_FLOAT_VALUE);
358
  uint32_t u;
359
  memcpy(&u, &f, sizeof(uint32_t));
360
  res = res && fv_write_u32((uint32_t)u);
361
  return res;
362
}
363
364
static bool i_f_double(double d) {
365
  bool res = true;
366
  res = res && fv_write_u8(S_DOUBLE_VALUE);
367
  uint64_t u;
368
  memcpy(&u, &d, sizeof(uint64_t));
369
  res = res && fv_write_u64(u);
370
  return res;
371
}
372
373
static bool i_f_i64(int64_t w) {
374
  bool res = true;
375
  res = res && fv_write_u8(S_I64_VALUE);
376
  res = res && fv_write_u64((uint64_t)w);
377
  return res;
378
}
379
380
static bool i_f_u64(uint64_t w) {
381
  bool res = true;
382
  res = res && fv_write_u8(S_U64_VALUE);
383
  res = res && fv_write_u64(w);
384
  return res;
385
}
386
387
// num_bytes is specifically an uint32_t
388
static bool i_f_lbm_array(uint32_t num_bytes, uint8_t *data) {
389
  bool res = fv_write_u8(S_LBM_ARRAY);
390
  res = res && fv_write_u32(num_bytes);
391
  if (res) {
392
    for (uint32_t i = 0; i < num_bytes; i ++ ) {
393
      if (!fv_write_u8(data[i])) return false;
394
    }
395
  }
396
  return res;
397
}
398
399
400
static void size_acc(lbm_value v, void *acc) {
401
 int32_t *s = (int32_t*)acc;
402
403
  lbm_uint t = lbm_type_of(v);
404
405
  if (t >= LBM_POINTER_TYPE_FIRST && t < LBM_POINTER_TYPE_LAST) {
406
    t = t & ~(LBM_PTR_TO_CONSTANT_BIT);
407
  }
408
409
  if (lbm_is_ptr(v) && (v & LBM_PTR_TO_CONSTANT_BIT)) {
410
    *s += (int32_t)sizeof(lbm_uint) + 1;
411
    return;
412
  }
413
414
  switch (t) {
415
  case LBM_TYPE_CONS:
416
    *s += 1;
417
    break;
418
  case LBM_TYPE_LISPARRAY:
419
    *s += 4 + 1;
420
    break;
421
  case LBM_TYPE_BYTE:
422
    *s += 2;
423
    break;
424
  case LBM_TYPE_U:
425
    *s += (int32_t)sizeof(lbm_uint) + 1;
426
    break;
427
  case LBM_TYPE_I:
428
    *s += (int32_t)sizeof(lbm_uint) + 1;
429
    break;
430
  case LBM_TYPE_U32:
431
    *s += 4 + 1;
432
    break;
433
  case LBM_TYPE_I32:
434
    *s += 4 + 1;
435
    break;
436
  case LBM_TYPE_U64:
437
    *s += 8 + 1;
438
    break;
439
  case LBM_TYPE_I64:
440
    *s += 8 + 1;
441
    break;
442
  case LBM_TYPE_FLOAT:
443
    *s += 4 + 1;
444
    break;
445
  case LBM_TYPE_DOUBLE:
446
    *s += 8 + 1;
447
    break;
448
  case LBM_TYPE_SYMBOL:
449
    *s += (int32_t)sizeof(lbm_uint) + 1;
450
    break;
451
  case LBM_TYPE_ARRAY: {
452
    lbm_int arr_size = lbm_heap_array_get_size(v);
453
    const uint8_t *d = lbm_heap_array_get_data_ro(v);
454
    if (arr_size > 0 && d != NULL) {
455
      *s += 1 + 4 + arr_size;
456
    }
457
  }break;
458
  }
459
}
460
461
static void flatten_node(lbm_value v, void *res) {
462
  bool *acc = (bool*)res;
463
  lbm_uint t = lbm_type_of(v);
464
465
  if (t >= LBM_POINTER_TYPE_FIRST && t < LBM_POINTER_TYPE_LAST) {
466
    t = t & ~(LBM_PTR_TO_CONSTANT_BIT);
467
  }
468
469
  if (lbm_is_ptr(v) && (v & LBM_PTR_TO_CONSTANT_BIT)) {
470
    *acc = *acc && fv_write_u8(S_CONSTANT_REF);
471
#ifdef LBM64
472
    *acc = *acc && fv_write_u64((lbm_uint)v);
473
#else
474
    *acc = *acc && fv_write_u32((lbm_uint)v);
475
#endif
476
    return;
477
  }
478
479
  switch (t) {
480
  case LBM_TYPE_CONS:
481
    *acc = *acc && i_f_cons();
482
    break;
483
  case LBM_TYPE_LISPARRAY: {
484
    lbm_array_header_t *header = (lbm_array_header_t*)lbm_car(v);
485
    if (header) {
486
      uint32_t size = (uint32_t)(header->size / sizeof(lbm_value));
487
      *acc = *acc && i_f_lisp_array(size);
488
    } else {
489
      // hmm
490
    }
491
  } break;
492
  case LBM_TYPE_BYTE:
493
    *acc = *acc && i_f_b((uint8_t)lbm_dec_as_char(v));
494
    break;
495
  case LBM_TYPE_U:
496
    *acc = *acc && i_f_u(lbm_dec_u(v));
497
    break;
498
  case LBM_TYPE_I:
499
    *acc = *acc && i_f_i(lbm_dec_i(v));
500
    break;
501
  case LBM_TYPE_U32:
502
    *acc = *acc && i_f_u32(lbm_dec_as_u32(v));
503
    break;
504
  case LBM_TYPE_I32:
505
    *acc = *acc && i_f_i32(lbm_dec_as_i32(v));
506
    break;
507
  case LBM_TYPE_U64:
508
    *acc = *acc && i_f_u64(lbm_dec_as_u64(v));
509
    break;
510
  case LBM_TYPE_I64:
511
    *acc = *acc && i_f_i64(lbm_dec_as_i64(v));
512
    break;
513
  case LBM_TYPE_FLOAT:
514
    *acc = *acc && i_f_float(lbm_dec_as_float(v));
515
    break;
516
  case LBM_TYPE_DOUBLE:
517
    *acc = *acc && i_f_double(lbm_dec_as_double(v));
518
    break;
519
  case LBM_TYPE_SYMBOL:
520
    *acc = *acc && i_f_sym(v);
521
    break;
522
  case LBM_TYPE_ARRAY: {
523
    lbm_int s = lbm_heap_array_get_size(v);
524
    const uint8_t *d = lbm_heap_array_get_data_ro(v);
525
    if (s > 0 && d != NULL) {
526
      *acc = *acc && i_f_lbm_array((uint32_t)s, (uint8_t*)d);
527
    }
528
  }break;
529
  default:
530
    break;
531
  }
532
}
533
534
static int32_t image_flatten_size(lbm_value v) {
535
  int32_t s = 0;
536
  if (lbm_ptr_rev_trav(size_acc, v, &s))
537
    return s;
538
  return -1;
539
}
540
541
static bool image_flatten_value(lbm_value v) {
542
  bool ok = true;
543
  bool trav_ok = lbm_ptr_rev_trav(flatten_node, v, &ok);
544
  return trav_ok && ok; // ok = enough space in image for flat val.
545
                        // trav_ok = no cycles in input value.
546
}
547
548
// ////////////////////////////////////////////////////////////
549
//
550
551
char *lbm_image_get_version(void) {
552
  if (image_version) {
553
    return image_version;
554
  } else {
555
    int32_t pos = (int32_t)image_size-2; // fixed position version string.
556
    uint32_t val = read_u32(pos); pos --;
557
    if (val == VERSION_ENTRY) {
558
      int32_t size = (int32_t)read_u32(pos);
559
      image_version = (char*)(image_address + (pos - size));
560
      return image_version;
561
    }
562
  }
563
  return NULL;
564
}
565
566
// ////////////////////////////////////////////////////////////
567
// Constant heaps as part of an image.
568
569
lbm_const_heap_t image_const_heap;
570
lbm_uint image_const_heap_start_ix = 0;
571
572
1786692
bool image_const_heap_write(lbm_uint w, lbm_uint ix) {
573
#ifdef LBM64
574
  int32_t i = (int32_t)(image_const_heap_start_ix + (ix * 2));
575
  uint32_t *words = (uint32_t*)&w;
576
  bool r = image_write(words[0], i, false);
577
  r = r && image_write(words[1], i + 1, false);
578
  return r;
579
#else
580
1786692
  int32_t i = (int32_t)(image_const_heap_start_ix + ix);
581
1786692
  return write_u32(w, &i, false);
582
#endif
583
}
584
585
// ////////////////////////////////////////////////////////////
586
// Image manipulation
587
588
151050
lbm_uint *lbm_image_add_symbol(char *name, lbm_uint id, lbm_uint symlist) {
589
  // 64 bit                             | 32 bit
590
  // image[i] = SYMBOL_ENTRY            | image[i] = SYMBOL_ENTRY
591
  // image[i-1] = symlist_ptr_high_word | image[i-1] = symlist_ptr
592
  // image[i-2] = symlist_ptr_low_word  | image[i-2] = id
593
  // image[i-3] = id_high_word          | image[i-3] = name_ptr
594
  // image[i-4] = id_low_word
595
  // image[i-5] = name_ptr_high_word
596
  // image[i-6] = name_ptr_low_word
597
151050
  bool r = write_u32(SYMBOL_ENTRY, &write_index,DOWNWARDS);
598

151050
  r = r && write_lbm_uint(symlist, &write_index, DOWNWARDS);
599

151050
  r = r && write_lbm_uint(id, &write_index, DOWNWARDS);
600

151050
  r = r && write_lbm_uint((lbm_uint)name, &write_index, DOWNWARDS);
601
151050
  lbm_uint entry_ptr = (lbm_uint)(image_address + write_index + 1);
602
151050
  if (r)
603
151050
    return (lbm_uint*)entry_ptr;
604
  return NULL;
605
}
606
607
// The symbol id is written to the link address upon image-boot
608
438480
lbm_uint *lbm_image_add_and_link_symbol(char *name, lbm_uint id, lbm_uint symlist, lbm_uint *link) {
609
  // 64 bit                             | 32 bit
610
  // image[i] = SYMBOL_ENTRY            | image[i] = SYMBOL_ENTRY
611
  // image[i-1] = link_ptr_high         | image[i-1] link_ptr
612
  // image[i-2] = link_ptr_low          | image[i-2] = symlist_ptr
613
  // image[i-3] = symlist_ptr_high_word | image[i-3] = id
614
  // image[i-4] = symlist_ptr_low_word  | image[i-4] = name_ptr
615
  // image[i-5] = id_high_word
616
  // image[i-6] = id_low_word
617
  // image[i-7] = name_ptr_high_word
618
  // image[i-8] = name_ptr_low_word
619
438480
  bool r = write_u32(SYMBOL_LINK_ENTRY, &write_index,DOWNWARDS);
620

438480
  r = r && write_lbm_uint((lbm_uint)link, &write_index, DOWNWARDS);
621

438480
  r = r && write_lbm_uint(symlist, &write_index, DOWNWARDS);
622

438480
  r = r && write_lbm_uint(id, &write_index, DOWNWARDS);
623

438480
  r = r && write_lbm_uint((lbm_uint)name, &write_index, DOWNWARDS);
624
438480
    lbm_uint entry_ptr = (lbm_uint)(image_address + write_index + 1);
625
438480
  if (r)
626
438480
    return (lbm_uint*)entry_ptr;
627
  return NULL;
628
}
629
630
bool lbm_image_save_global_env(void) {
631
  lbm_value *env = lbm_get_global_env();
632
  if (env) {
633
    for (int i = 0; i < GLOBAL_ENV_ROOTS; i ++) {
634
      lbm_value curr = env[i];
635
      while(lbm_is_cons(curr)) {
636
        lbm_value name_field = lbm_caar(curr);
637
        lbm_value val_field  = lbm_cdr(lbm_car(curr));
638
639
        if (lbm_is_constant(val_field)) {
640
          write_u32(BINDING_CONST, &write_index, DOWNWARDS);
641
          write_lbm_value(name_field, &write_index, DOWNWARDS);
642
          write_lbm_value(val_field, &write_index, DOWNWARDS);
643
        } else {
644
645
          int fv_size = image_flatten_size(val_field);
646
          if (fv_size > 0) {
647
            fv_size = (fv_size % 4 == 0) ? (fv_size / 4) : (fv_size / 4) + 1; // num 32bit words
648
            int tot_size =  fv_size; //+ 1 + (int)(sizeof(lbm_uint) / 4);
649
650
            if (write_index + tot_size >= (int32_t)image_size) {
651
              return false;
652
            }
653
            write_u32(BINDING_FLAT, &write_index, DOWNWARDS);
654
            write_u32((uint32_t)fv_size , &write_index, DOWNWARDS);
655
            write_lbm_value(name_field, &write_index, DOWNWARDS);
656
            write_index = write_index - fv_size; // subtract fv_size
657
            if (image_flatten_value(val_field)) {      // adds fv_size back
658
	      // TODO: What error handling makes sense?
659
	      fv_write_flush();
660
	    }
661
            write_index = write_index - fv_size - 1; // subtract fv_size
662
          } else {
663
            return false;
664
          }
665
        }
666
        curr = lbm_cdr(curr);
667
      }
668
    }
669
    return true;
670
  }
671
  return false;
672
}
673
674
// The extension table is created at system startup.
675
// Extensions can also be added dynamically.
676
// Dynamically added extensions have names starting with "ext-"
677
// and their names are placed in RAM by the reader.
678
//
679
// Symbol_id -> index in extension table mapping
680
// is created as extensions are added.
681
// dynamic extensions are added after "built-in" extensions
682
// and have higher indices.
683
684
bool lbm_image_save_extensions(void) {
685
  bool r = true;
686
  lbm_uint num = lbm_get_num_extensions();
687
  if (num > 0) {
688
    r = r && write_u32(EXTENSION_TABLE, &write_index, DOWNWARDS);
689
    r = r && write_u32((uint32_t)num , &write_index, DOWNWARDS);
690
    for (lbm_uint i = 0; i < num; i ++) {
691
      if (!r) return r;
692
693
      char *name_ptr = extension_table[i].name;
694
      lbm_uint addr;
695
      // when PIC, name pointers may move around
696
      // between restarts. It is also the case that
697
      // the FPTRs will move around as well.
698
      // This makes dynamic extensions useless on Linux.
699
      // Static extensions are fine as they will be re-added after image-boot
700
      // and faulty FPTRs will be replaced.
701
      //#ifdef __PIC__
702
      //r = store_symbol_name_flash(name_ptr, &addr);
703
      //if (!r) return r;
704
      //name_ptr = (char *)addr;
705
      //#else
706
      if (lbm_memory_ptr_inside((lbm_uint *)name_ptr)) {
707
        r = store_symbol_name_flash(name_ptr, &addr);
708
        if (!r) return r;
709
        name_ptr = (char *)addr;
710
      }
711
      //#endif
712
#ifdef LBM64
713
      r = r && write_u64((uint64_t)name_ptr, &write_index, DOWNWARDS);
714
      r = r && write_u64((uint64_t)extension_table[i].fptr, &write_index, DOWNWARDS);
715
#else
716
      r = r && write_u32((uint32_t)name_ptr, &write_index, DOWNWARDS);
717
      r = r && write_u32((uint32_t)extension_table[i].fptr, &write_index, DOWNWARDS);
718
#endif
719
    }
720
  }
721
  return true;
722
}
723
724
static uint32_t last_const_heap_ix = 0;
725
726
bool lbm_image_save_constant_heap_ix(void) {
727
  bool r = true; // saved or no need to save it.
728
  if (image_const_heap.next != last_const_heap_ix) {
729
    r = write_u32(CONSTANT_HEAP_IX, &write_index, DOWNWARDS);
730
    r = r && write_u32((uint32_t)image_const_heap.next, &write_index, DOWNWARDS);
731
  }
732
  return r;
733
}
734
735
bool lbm_image_exists(void) {
736
  uint32_t val = read_u32((int32_t)image_size - 1);
737
  return val == IMAGE_INITIALIZED;
738
}
739
740
21924
void lbm_image_init(uint32_t* image_mem_address,
741
                    uint32_t image_size_words,
742
                    lbm_image_write_fun image_write_fun) {
743
21924
  image_write = image_write_fun;
744
21924
  image_address = image_mem_address;
745
21924
  image_size = image_size_words;
746
21924
  write_index = (int32_t)image_size_words -1;
747
21924
  image_has_extensions = false;
748
21924
  image_version = NULL;
749
21924
}
750
751
21924
void lbm_image_create(char *version_str) {
752
21924
  write_u32(IMAGE_INITIALIZED, &write_index, DOWNWARDS);
753
21924
  if (version_str) {
754
21924
    uint32_t bytes = strlen(version_str) + 1;
755
21924
    uint32_t words = (bytes % 4 == 0) ? bytes / 4 : (bytes / 4) + 1;
756
21924
    write_u32(VERSION_ENTRY, &write_index, DOWNWARDS);
757
21924
    write_u32(words, &write_index, DOWNWARDS);
758
21924
    uint32_t w = 0;
759
21924
    char *buf = (char*)&w;
760
21924
    uint32_t i = 0;
761
21924
    int32_t ix = write_index - (int32_t)(words -1);
762
21924
    int wi = 0;
763
263088
    while (i < bytes) {
764
241164
      if (wi == 0 ) {
765
21924
        w = 0;
766
      }
767
241164
      if (wi == 4) wi = 0;
768
241164
      buf[wi] = version_str[i];
769
241164
      if (wi == 3) {
770
43848
        write_u32(w, &ix, UPWARDS);
771
      }
772
241164
      i ++;
773
241164
      wi ++;
774
    }
775
21924
    if (wi != 0) {
776
21924
      write_u32(w, &ix, UPWARDS);
777
    }
778
21924
    write_index -= (int32_t)words;
779
  }
780
21924
}
781
782
783
21924
bool lbm_image_boot(void) {
784
  //process image
785
21924
  int32_t pos = (int32_t)image_size-1;
786
21924
  last_const_heap_ix = 0;
787
788

65772
  while (pos >= 0 && pos > (int32_t)last_const_heap_ix) {
789
65772
    uint32_t val = read_u32(pos);
790
65772
    pos --;
791


65772
    switch(val) {
792
21924
    case IMAGE_INITIALIZED: {
793
21924
      image_const_heap_start_ix = 0; // const heap starts at 0
794
21924
      lbm_const_heap_init(image_const_heap_write,
795
                          &image_const_heap,
796
                          (lbm_uint*)(image_address));
797
      // initialized is a one word field
798
21924
    } break;
799
21924
    case VERSION_ENTRY: {
800
21924
      uint32_t size = read_u32(pos); pos --;
801
21924
      image_version = (char*)(image_address + (pos - (int32_t)size + 1));
802
21924
      pos -= (int32_t)size;
803
21924
    } break;
804
    case CONSTANT_HEAP_IX: {
805
      uint32_t next = read_u32(pos);
806
      pos --;
807
      last_const_heap_ix = next;
808
      image_const_heap.next = next;
809
    } break;
810
    case BINDING_CONST: {
811
      // on 64 bit           | on 32 bit
812
      // pos     -> key_high | pos     -> key
813
      // pos - 1 -> key_low  | pos - 1 -> val
814
      // pos - 2 -> val_high
815
      // pos - 3 -> val_low
816
#ifdef LBM64
817
      lbm_uint bind_key = read_u64(pos-1);
818
      lbm_uint bind_val = read_u64(pos-3);
819
      pos -= 4;
820
#else
821
      lbm_uint bind_key = read_u32(pos);
822
      lbm_uint bind_val = read_u32(pos-1);
823
      pos -= 2;
824
#endif
825
      lbm_uint ix_key  = lbm_dec_sym(bind_key) & GLOBAL_ENV_MASK;
826
      lbm_value *global_env = lbm_get_global_env();
827
      lbm_uint orig_env = global_env[ix_key];
828
      lbm_value new_env = lbm_env_set(orig_env,bind_key,bind_val);
829
830
      if (lbm_is_symbol(new_env)) {
831
        return false;
832
      }
833
      global_env[ix_key] = new_env;
834
    } break;
835
    case BINDING_FLAT: {
836
      // on 64 bit           | on 32 bit
837
      // pos     -> size     | pos     -> size
838
      // pos - 1 -> key_high | pos - 1 -> key
839
      // pos - 2 -> key_low
840
      //
841
      int32_t s = (int32_t)read_u32(pos);
842
      // size in 32 or 64 bit words.
843
#ifdef LBM64
844
      lbm_uint bind_key = read_u64(pos-2);
845
      pos -= 3;
846
#else
847
      lbm_uint bind_key = read_u32(pos-1);
848
      pos -= 2;
849
#endif
850
851
      pos -= s;
852
      lbm_flat_value_t fv;
853
      fv.buf = (uint8_t*)(image_address + pos);
854
      fv.buf_size = (uint32_t)s * sizeof(lbm_uint); // GEQ to actual buf
855
      fv.buf_pos = 0;
856
      lbm_value unflattened;
857
      lbm_unflatten_value(&fv, &unflattened);
858
      if (lbm_is_symbol_merror(unflattened)) {
859
        lbm_perform_gc();
860
        lbm_unflatten_value(&fv, &unflattened);
861
      }
862
863
      lbm_uint ix_key  = lbm_dec_sym(bind_key) & GLOBAL_ENV_MASK;
864
      lbm_value *global_env = lbm_get_global_env();
865
      lbm_uint orig_env = global_env[ix_key];
866
      lbm_value new_env = lbm_env_set(orig_env,bind_key,unflattened);
867
868
      if (lbm_is_symbol(new_env)) {
869
        return false;
870
      }
871
      global_env[ix_key] = new_env;
872
      pos --;
873
    } break;
874
    case SYMBOL_ENTRY: {
875
      // on 64 bit                         | on 32 bit
876
      // pos     -> symlist_addr_high_word | pos     -> symlist_ptr
877
      // pos - 1 -> symlist_addr_low_word  | pos - 1 -> id
878
      // pos - 2 -> id_high_word           | pos - 2 -> name_ptr
879
      // pos - 3 -> id_low_word            |
880
      // pos - 4 -> name_ptr_high_word     |
881
      // pos - 5 -> name_ptr_low_word      |
882
#ifdef LBM64
883
      int32_t entry_pos = pos - 5;
884
      lbm_symrepr_set_symlist((lbm_uint*)(image_address + entry_pos));
885
      pos -= 6;
886
#else
887
      int32_t entry_pos = pos - 2;
888
      lbm_symrepr_set_symlist((lbm_uint*)(image_address + entry_pos));
889
      pos -= 3;
890
#endif
891
    } break;
892
    case SYMBOL_LINK_ENTRY: {
893
      // on 64 bits                        | on 32 bit
894
      // pos     -> link_ptr_high          | pos     -> link_ptr
895
      // pos - 1 -> link_ptr_low           | pos - 1 -> symlist_ptr
896
      // pos - 2 -> symlist_addr_high_word | pos - 2 -> id
897
      // pos - 3 -> symlist_addr_low_word  | pos - 3 -> name_ptr;
898
      // pos - 4 -> id_high_word
899
      // pos - 5 -> id_low_word
900
      // pos - 6 -> name_ptr_high_word
901
      // pos - 7 -> name_ptr_low_word
902
      //int32_t entry_pos = pos - (int32_t)(3 * (sizeof(lbm_uint) / 4));
903
      lbm_uint link_ptr;
904
      lbm_uint sym_id;
905
#ifdef LBM64
906
      link_ptr = read_u64(pos-1);
907
      sym_id   = read_u64(pos-5);
908
      *((lbm_uint*)link_ptr) = sym_id;
909
      lbm_symrepr_set_symlist((lbm_uint*)(image_address + (pos - 7)));
910
      pos -= 8;
911
#else
912
      link_ptr = read_u32(pos);
913
      sym_id   = read_u32(pos-2);
914
      *((lbm_uint*)link_ptr) = sym_id;
915
      lbm_symrepr_set_symlist((lbm_uint*)(image_address + (pos - 3)));
916
      pos -= 4;
917
#endif
918
    } break;
919
    case EXTENSION_TABLE: {
920
      // on 64 bit                | on 32 bit
921
      // pos     -> name_ptr_high | pos     -> name_ptr
922
      // pos - 1 -> name_ptr_low  | pos - 1 -> fptr
923
      // pos - 2 -> fptr_high
924
      // pos - 3 -> fptr_low
925
      int32_t num = (int32_t)read_u32(pos); pos --;
926
927
      int32_t i = 0;
928
      for (i = 0; i < num; i ++) {
929
        lbm_uint name;
930
        lbm_uint fptr;
931
#ifdef LBM64
932
        name = read_u64(pos-1);
933
        fptr = read_u64(pos-3);
934
        pos -= 4;
935
#else
936
        name = read_u32(pos);
937
        fptr = read_u32(pos-1);
938
        pos -= 2;
939
#endif
940
        extension_table[i].name = (char*)name;
941
        extension_table[i].fptr = (extension_fptr)fptr;
942
      }
943
      lbm_extensions_set_next((lbm_uint)i);
944
      image_has_extensions = true;
945
    } break;
946
21924
    default:
947
21924
      write_index = pos+1;
948
21924
      goto done_loading_image;
949
      break;
950
    }
951
  }
952
 done_loading_image:
953
21924
  return true;
954
}