bbc-basic: Slight tweak to heap size.
[jackhill/mal.git] / nasm / types.asm
1 ;; Data structures
2 ;; ===============
3 ;;
4 ;; Memory management is done by having two fixed-size datatypes,
5 ;; Cons and Array.
6 ;;
7 ;; Both Cons and Array have the following in common:
8 ;; a type field at the start, a reference count, followed by data
9 ;; [ type (8) | (8) | refs (16) | data ]
10 ;;
11 ;;
12 ;; Type bit fields
13 ;; ---------------
14 ;;
15 ;; The 8-bit type fields describe the Block, Container and Content type.
16 ;;
17 ;; The Block type is used for memory management, to determine the kind of memory block
18 ;; The Container type indicates the data structure that the Cons or Array block is being used to represent
19 ;; The Content type indicates the raw type of the data in the content
20 ;;
21 ;; Block type [1 bit]:
22 ;; 0 0 - Cons memory block
23 ;; 1 1 - Array memory block
24 ;;
25 ;; Container type [3 bits]:
26 ;; 0 0 - Value (single boxed value for Cons blocks, multiple values for Array blocks).
27 ;; 2 1 - List (value followed by pointer). Only for Cons blocks
28 ;; 4 2 - Symbol (special char array). Only for Array blocks
29 ;; 6 3 - Keyword. Only for Array blocks
30 ;; 8 4 - Map
31 ;; 10 5 - Function
32 ;; 12 6 - Atom
33 ;; 14 7 - Vector
34 ;;
35 ;; Content type [4 bits]:
36 ;; 0 0 - Nil
37 ;; 16 1 - True
38 ;; 32 2 - Char
39 ;; 48 3 - Int
40 ;; 64 4 - Float
41 ;; 80 5 - Pointer (memory address)
42 ;; 96 6 - Function (instruction address)
43 ;; 112 7 - Empty (distinct from Nil)
44 ;; 208 8 - False
45 ;; 224 9 - Macro
46 ;;
47 ;;
48 ;; These represent MAL data types as follows:
49 ;;
50 ;; MAL type Block Container Content
51 ;; --------- | -------- | ---------- | ---------
52 ;; integer Cons Value Int
53 ;; symbol Array Symbol Char
54 ;; list Cons List Any
55 ;; vector Cons Vector Any
56 ;; nil Cons Value Nil
57 ;; true Cons Value True
58 ;; false Cons Value False
59 ;; string Array Value Char
60 ;; keyword Array Keyword Char
61 ;; hash-map Cons Map Alternate key, values
62 ;; atom Cons Atom Pointer
63 ;;
64
65 %include "macros.mac"
66
67 ;; Cons type.
68 ;; Used to store either a single value with type information
69 ;; or a pair of (value, Pointer or Nil) to represent a list
70 STRUC Cons
71 .typecar: RESB 1 ; Type information for car (8 bit)
72 .typecdr: RESB 1 ; Type information for cdr (8 bits)
73 .refcount: RESW 1 ; Number of references to this Cons (16 bit)
74 .car: RESQ 1 ; First value (64 bit)
75 .cdr: RESQ 1 ; Second value (64 bit)
76 .size: ; Total size of struc
77 ENDSTRUC
78
79
80 %define array_chunk_len 32 ; Number of 64-bit values which can be stored in a single chunk
81
82 STRUC Array
83 .type: RESB 1 ; Type information (8 bits)
84 .control: RESB 1 ; Control data (8 bits)
85 .refcount: RESW 1 ; Number of references to this Array (16 bit)
86 .length: RESD 1 ; Number of elements in this part of the array (32 bit)
87 .next RESQ 1 ; Pointer to the next chunk (64 bit)
88 .data: RESQ array_chunk_len ; Data storage
89 .size: ; Total size of struc
90 ENDSTRUC
91
92 ;; Type information
93
94 %define block_mask 1 ; LSB for block type
95 %define container_mask 2 + 4 + 8 ; Next three bits for container type
96 %define content_mask 16 + 32 + 64 + 128 ; Four bits for content type
97
98 ;; Block types
99 %define block_cons 0 ; Note: This must be zero
100 %define block_array 1
101
102 ;; Container types
103 %define container_value 0 ; Note: This must be zero
104 %define container_list 2
105 %define container_symbol 4
106 %define container_keyword 6
107 %define container_map 8
108 %define container_function 10
109 %define container_atom 12
110 %define container_vector 14
111
112 ;; Content type
113 %define content_nil 0
114 %define content_true 16
115 %define content_char 32
116 %define content_int 48
117 %define content_float 64
118 %define content_pointer 80 ; Memory pointer (to Cons or Array)
119 %define content_function 96 ; Function pointer
120 %define content_empty 112
121 %define content_false 208
122 %define content_macro 224
123
124 ;; Common combinations for MAL types
125 %define maltype_integer (block_cons + container_value + content_int)
126 %define maltype_string (block_array + container_value + content_char)
127 %define maltype_symbol (block_array + container_symbol + content_char)
128 %define maltype_nil (block_cons + container_value + content_nil)
129 %define maltype_empty_list (block_cons + container_list + content_empty)
130 %define maltype_empty_map (block_cons + container_map + content_empty)
131 %define maltype_empty_vector (block_cons + container_vector + content_empty)
132 %define maltype_function (block_cons + container_function + content_function)
133 %define maltype_macro (block_cons + container_function + content_macro)
134 %define maltype_true (block_cons + container_value + content_true)
135 %define maltype_false (block_cons + container_value + content_false)
136 %define maltype_atom (block_cons + container_atom + content_pointer)
137
138 ;; ------------------------------------------
139
140 section .data
141
142 ;; Fixed strings for printing
143
144 static error_msg_print_string, db "Error in print string",10
145 static error_array_memory_limit, db "Error: Run out of memory for Array objects. Increase heap_array_limit.",10
146 static error_cons_memory_limit, db "Error: Run out of memory for Cons objects. Increase heap_cons_limit.",10
147
148 static error_cons_double_free, db "Error: double free error releasing Cons"
149 static error_array_double_free, db "Error: double free error releasing Array"
150
151 ;; ------------------------------------------
152 ;; Memory management
153 ;;
154 ;; For each object (Cons or Array), there is a block of memory (in BSS).
155 ;; When an object is requested it is first taken from the free list
156 ;; If the free list is empty (address 0) then the next object in the block
157 ;; is used, and the heap_x_number counter is incremented. When an object
158 ;; is free'd it is pushed onto the heap_x_free list.
159
160
161 %define heap_cons_limit 5000 ; Number of cons objects which can be created
162
163 heap_cons_next: dq heap_cons_store ; Address of next cons in memory
164 heap_cons_free: dq 0 ; Address of start of free list
165
166 %define heap_array_limit 2000 ; Number of array objects which can be created
167
168 heap_array_next: dq heap_array_store
169 heap_array_free: dq 0
170
171 section .bss
172
173 ;; Reserve space to store Cons and Array objects
174 heap_cons_store: resb heap_cons_limit * Cons.size
175 .end: ; Address of end of the store
176
177 heap_array_store: resb heap_array_limit * Array.size
178 .end:
179
180 section .text
181
182 ;; ------------------------------------------
183 ;; Array alloc_array()
184 ;;
185 ;; Returns the address of an Array object in RAX
186 ;;
187 ;; Working registers: rbx
188 alloc_array:
189
190 ; Get the address of a free array
191 mov rax, [heap_array_free] ; Address of the array
192
193 ; Check if it's null
194 cmp rax, 0
195 je .create_array
196
197 mov rbx, [rax + Array.next] ; Get the address of the next array in the linked list
198 mov [heap_array_free], rbx ; Put this address at the front of the list
199 jmp .initialise_array
200
201 .create_array:
202
203 ; Get the address of the next Array
204 mov rax, [heap_array_next]
205 ; Check if we've reached the end
206 cmp rax, heap_array_store.end
207 je .out_of_memory
208
209 mov rbx, rax
210 add rbx, Array.size ; Address of the next array
211 mov [heap_array_next], rbx ; for next time
212
213 .initialise_array:
214 ; Address of Array now in rax
215 mov BYTE [rax + Array.type], block_array
216 mov WORD [rax + Array.refcount], 1 ; Only one reference
217 mov DWORD [rax + Array.length], 0
218 mov QWORD [rax + Array.next], 0 ; null next address
219
220 ret
221
222 .out_of_memory:
223 mov rsi, error_array_memory_limit
224 mov rdx, error_array_memory_limit.len
225 call print_rawstring
226 jmp quit_error
227
228
229 ;; -------------------------------------------
230 ;; Decrements the reference count of the array in RSI
231 ;; If the count reaches zero then push the array
232 ;; onto the free list
233 release_array:
234 mov ax, WORD [rsi + Array.refcount]
235
236 ; Check if reference count is already zero
237 test ax,ax
238 jz .double_free
239
240 dec ax
241 mov WORD [rsi + Array.refcount], ax
242 jz .free ; If the count reaches zero then put on free list
243 ret
244
245 .free:
246 ; Get the next field
247 mov rbx, [rsi + Array.next]
248
249 mov rax, [heap_array_free] ; Get the current head
250 mov [rsi + Array.next], rax ; Put current head into the "next" field
251 mov [heap_array_free], rsi ; Push Array onto free list
252
253 cmp rbx, 0
254 jne .release_next ; If there is another array, then need to release it
255
256 ret
257
258 .release_next:
259 ; release the next array
260 mov rsi, rbx
261 call release_array
262 ret
263
264 .double_free:
265 ret
266 load_static error_cons_double_free
267 call raw_to_string
268 mov rsi, rax
269 jmp error_throw
270
271 ;; ------------------------------------------
272 ;; Cons alloc_cons()
273 ;;
274 ;; Returns the address of a Cons object in RAX
275 ;;
276 ;; Modifies:
277 ;; RBX
278 alloc_cons:
279
280 ; Get the address of a free cons
281 mov rax, [heap_cons_free] ; Address of the cons
282
283 ; Check if it's null
284 cmp rax, 0
285 je .create_cons
286
287 mov rbx, [rax + Cons.cdr] ; Get the address of the next cons in the linked list
288 mov [heap_cons_free], rbx ; Put this address at the front of the list
289 jmp .initialise_cons
290
291 .create_cons:
292
293 ; Get the address of the next Cons
294 mov rax, [heap_cons_next]
295 ; Check if we've reached the end
296 cmp rax, heap_cons_store.end
297 je .out_of_memory
298
299 mov rbx, rax
300 add rbx, Cons.size ; Address of the next cons
301 mov [heap_cons_next], rbx ; for next time
302
303 .initialise_cons:
304 ; Address of Cons now in rax
305 mov BYTE [rax + Cons.typecar], 0
306 mov BYTE [rax + Cons.typecdr], 0
307 mov WORD [rax + Cons.refcount], 1 ; Only one reference
308 mov QWORD [rax + Cons.car], 0
309 mov QWORD [rax + Cons.cdr], 0
310 ret
311
312 .out_of_memory:
313 mov rsi, error_cons_memory_limit
314 mov rdx, error_cons_memory_limit.len
315 call print_rawstring
316 jmp quit_error
317
318
319 ;; -------------------------------------------
320 ;; Decrements the reference count of the cons in RSI
321 ;; If the count reaches zero then push the cons
322 ;; onto the free list
323 ;;
324 ;; Modifies registers:
325 ;; RAX
326 ;; RBX
327 ;; RCX
328 ;;
329 release_cons:
330 mov ax, WORD [rsi + Cons.refcount]
331
332 ; Check if already released
333 test ax,ax
334 jz .double_free
335
336 dec ax
337 mov WORD [rsi + Cons.refcount], ax
338 jz .free ; If the count reaches zero then put on free list
339 ret
340
341 .free:
342 ; Get and push cdr onto stack
343 mov rcx, [rsi + Cons.cdr]
344 push rcx ; Content of CDR
345 push rsi ; Original Cons object being released
346
347 mov rax, [heap_cons_free] ; Get the current head
348 mov [rsi + Cons.cdr], rax ; Put current head into the "cdr" field
349 mov [heap_cons_free], rsi ; Push Cons onto free list
350
351 ; Check if the CAR needs to be released
352
353 mov al, BYTE [rsi+Cons.typecar]
354 and al, content_mask ; Test content type
355 cmp al, content_pointer
356 jne .free_cdr ; Jump if CAR not pointer
357
358 ; CAR is a pointer to either a Cons or Array
359 ; Get the address stored in CAR
360 mov rsi, [rsi + Cons.car]
361 call release_object
362 .free_cdr:
363 pop rcx ; This was rsi, the original Cons
364 pop rsi ; This was rcx, the original Cons.cdr
365
366 ; Get the type from the original Cons
367 mov al, BYTE [rcx+Cons.typecdr]
368 and al, content_mask ; Test content type
369 cmp al, content_pointer
370 jne .done
371
372 call release_object
373 .done:
374 ret
375
376 .double_free: ; Already released
377 ret
378 load_static error_cons_double_free
379 call raw_to_string
380 mov rsi, rax
381 jmp error_throw
382
383 ;; Releases either a Cons or Array
384 ;; Address of object in RSI
385 ;;
386 ;; May modify:
387 ;; RAX
388 ;; RBX
389 ;; RCX
390 ;;
391 release_object:
392 mov al, BYTE [rsi] ; Get first byte
393 and al, block_mask ; Test block type
394 cmp al, block_array ; Test if it's an array
395 je release_array
396 jmp release_cons
397
398 ;; Increment reference count of Cons or Array
399 ;; Address of object in RSI
400 ;;
401 ;; This code makes use of the fact that the reference
402 ;; count is in the same place in Cons and Array types
403 ;;
404 ;; Modifies
405 ;; RAX
406 incref_object:
407 mov ax, WORD [rsi + Cons.refcount] ; Same for Array
408 inc ax
409 ; Check for overflow?
410 mov [rsi + Cons.refcount], WORD ax
411 ret
412
413 ;; -------------------------------------------
414 ;; Copying lists/vectors
415 ;; This does a shallow copy, copying only the
416 ;; top level of objects. Any objects pointed to are not copied
417 ;;
418 ;; Input: RSI - address of list/vector
419 ;;
420 ;; Returns: New list/vector in RAX, last Cons in RBX
421 ;;
422 ;; Modifies:
423 ;; RBX
424 ;; RCX
425 ;; RDX
426 ;; R8
427 ;; R9
428 ;; R10
429 ;;
430 cons_seq_copy:
431 push rsi ; Restored at the end
432
433 mov r8, rsi ; Input in R8
434 xor r9, r9 ; Head of list in R9, start in R10
435 .loop:
436 ; Check the type
437 mov cl, BYTE [r8]
438 mov ch, cl
439 and ch, block_mask
440 jnz .not_seq ; Not a Cons object
441
442 call alloc_cons
443 mov rdx, rax ; New Cons in RDX
444 mov [rdx], BYTE cl ; Copy type in RCX
445 mov rbx, [r8 + Cons.car] ; Value in RBX
446 mov [rdx + Cons.car], rbx ; Copy value
447
448 and cl, content_mask
449 cmp cl, content_pointer
450 jne .copied
451
452 ; A pointer, so increment the reference count
453 mov rsi, rbx
454 call incref_object
455
456 .copied:
457 ; Check if this is the first
458 test r9,r9
459 jnz .append
460
461 ; First Cons
462 mov r9, rdx
463 mov r10, rdx ; Start of the list, will be returned
464 jmp .next
465
466 .append:
467 ; Appending to last Cons
468 mov [r9 + Cons.cdr], rdx
469 mov [r9 + Cons.typecdr], BYTE content_pointer
470 ; Replace
471 mov r9, rdx
472
473 .next:
474 ; Check if there's another
475 mov al, BYTE [r8 + Cons.typecdr]
476 cmp al, content_pointer
477 jne .done ; No more
478 ; Got another
479 mov r8, [r8 + Cons.cdr]
480 jmp .loop
481
482 .done:
483 pop rsi ; Restore input
484 mov rax, r10 ; Output list
485 mov rbx, r9 ; Last Cons
486 ret
487
488 .not_seq:
489 xor rsi,rsi
490 jmp error_throw
491
492 ;; -------------------------------------------
493 ;; String type
494 ;;
495 ;; Create a new string, address in RAX
496 ;;
497 ;; Modifies registers
498 ;; RBX
499 ;;
500 string_new:
501 call alloc_array
502 mov [rax], BYTE maltype_string
503 mov DWORD [rax + Array.length], 0
504 mov QWORD [rax + Array.next], 0
505 ret
506
507 ;; Convert a raw string to a String type
508 ;;
509 ;; Input: Address of raw string in RSI, length in EDX
510 ;; Output: Address of string in RAX
511 ;;
512 ;; Modifies registers: R8,R9,RCX
513 ;;
514 raw_to_string:
515 ; Save registers to restore at the end
516 push r10
517 push r11
518
519 push rsi
520 push rdx
521 call string_new ; String now in RAX
522 pop rdx
523 pop rsi
524
525 mov r8, rax
526 add r8, Array.data ; Address of string data
527 mov r10, rax
528 add r10, Array.size ; End of the destination data
529 mov r11, rax ; First Array to return
530
531 mov r9, rsi ; Address of raw data
532 mov ecx, edx ; Count
533
534 .copy_loop:
535 test ecx, ecx ; Check if count is zero
536 jz .done
537
538 ; Copy one byte
539 mov bl, BYTE [r9]
540 mov [r8], BYTE bl
541
542 ; Move the destination
543 inc r8
544 cmp r8, r10
545 jne .dest_ok
546
547 ; Hit the end. Set the length of the array
548 mov [rax + Array.length], DWORD (array_chunk_len * 8)
549
550 push rax ; Last Array
551 push rsi
552 push rdx
553 call string_new ; String now in RAX
554 pop rdx
555 pop rsi
556 pop rbx ; Last Array
557 mov [rbx + Array.next], rax ; Point to new Array
558
559 mov r8, rax
560 add r8, Array.data ; Address of string data
561 mov r10, rax
562 add r10, Array.size ; End of the destination data
563
564 .dest_ok:
565
566 inc r9
567 dec ecx
568 jmp .copy_loop
569 .done:
570 ; Set the length of the destination array
571 sub r8, Array.data
572 sub r8, rax
573 mov [rax + Array.length], DWORD r8d
574
575 ; Move first Array into RAX
576 mov rax, r11
577
578 ; Restore registers
579 pop r11
580 pop r10
581
582 ret
583
584 ;; Convert a raw string to a symbol
585 ;;
586 ;; Input: Address of raw string in RSI, length in EDX
587 ;; Output: Address of string in RAX
588 ;;
589 ;; Modifies registers: R8,R9,RCX
590 raw_to_symbol:
591 call raw_to_string
592 ; set the content type
593 mov [rax], BYTE (block_array + container_symbol + content_char)
594 ret
595
596 ;; Convert a NUL terminated C string to string
597 ;;
598 ;; Input: RSI - Address of string
599 ;;
600 ;; Returns: String in RAX
601 ;;
602 ;; Modifies:
603 ;; RBX
604 ;; RCX
605 ;; RDX
606
607 cstring_to_string:
608 push rsi
609 call string_new ; in RAX
610 pop rsi
611
612 mov rbx, rax
613 add rbx, Array.data ; Start of output
614 mov rcx, rax
615 add rcx, Array.size ; End of output
616 .loop:
617 mov dl, BYTE [rsi]
618 test dl, dl ; Check if NUL (0)
619 jz .done
620 mov [rbx], BYTE dl
621 inc rbx
622 inc rsi
623 jmp .loop
624 .done:
625 sub rbx, rax
626 sub rbx, Array.data
627 ; rbx now contains the length
628 mov [rax + Array.length], DWORD ebx
629 ret
630
631 ;; Appends a character to a string
632 ;; Input: Address of string in RSI, character in CL
633 ;;
634 ;; Modifies
635 ;; RAX
636 string_append_char:
637 push rsi
638 ; Get the end of the string
639 .get_end:
640 mov rax, [rsi + Array.next]
641 test rax, rax
642 jz .got_dest_end
643 mov rsi, rax
644 jmp .get_end
645 .got_dest_end:
646
647 ; Check if this chunk is full
648 mov eax, DWORD [rsi + Array.length]
649 cmp eax, (array_chunk_len*8)
650 jne .append
651
652 ; full, need to allocate another
653 call alloc_array
654 mov [rsi + Array.next], rax
655 mov rsi, rax
656 xor eax, eax ; Set length to zero
657 .append:
658 inc eax
659 mov DWORD [rsi + Array.length], eax
660 dec eax
661 add rax, rsi
662 add rax, Array.data ; End of data
663 mov [rax], BYTE cl
664
665 pop rsi ; Restore original value
666 ret
667
668 ;; Appends a string to the end of a string
669 ;;
670 ;; Input: String to be modified in RSI
671 ;; String to be copied in RDX
672 ;;
673 ;; Output: Modified string in RSI
674 ;;
675 ;; Working registers:
676 ;; rax Array chunk for output (copied to)
677 ;; rbx Array chunk for input (copied from)
678 ;; cl Character being copied
679 ;; r8 Address of destination
680 ;; r9 Destination end address
681 ;; r10 Address of source
682 ;; r11 Source end address
683 string_append_string:
684 ; copy source Array address to rbx
685 mov rbx, rdx
686
687 ; source data address in r10
688 mov r10, rbx
689 add r10, Array.data ; Start of the data
690
691 ; source data end address in r11
692 mov r11, r10
693 mov r8d, DWORD [rbx + Array.length]
694 add r11, r8
695
696 test r8d, r8d
697 jz .return ; Appending zero-size array
698
699 ; Find the end of the string in RSI
700 ; and put the address of the Array object into rax
701 mov rax, rsi
702 .find_string_end:
703 mov r8, QWORD [rax + Array.next]
704 test r8, r8 ; Next chunk is 0
705 je .got_dest_end ; so reached end
706
707 mov rax, r8 ; Go to next chunk
708 jmp .find_string_end
709 .got_dest_end:
710
711 ; destination data address into r8
712 mov r8, rax
713 add r8, Array.data
714 add r8d, DWORD [rax + Array.length]
715
716 ; destination data end into r9
717 mov r9, rax
718 add r9, Array.size
719
720 ; Check if we are at the end of the destination
721 cmp r8, r9
722 je .alloc_dest
723
724 .copy_loop:
725 ; Copy one byte from source to destination
726 mov cl, BYTE [r10]
727 mov BYTE [r8], cl
728
729 ; move source to next byte
730 inc r10
731 ; Check if we've reached the end of this Array
732 cmp r10, r11
733 jne .source_ok
734
735 ; have reached the end of the source Array
736 mov rbx, QWORD [rbx + Array.next] ; Get the next Array address
737 test rbx, rbx ; Test if it's null
738 je .finished ; No more, so we're done
739 ; Move on to next Array object
740
741 ; Get source address into r10
742 mov r10, rbx
743 add r10, Array.data ; Start of the data
744
745 ; Source end address
746 mov r11d, DWORD [rbx + Array.length] ; Length of the array
747 add r11, r10
748
749 .source_ok:
750
751 ; Move destination to next byte
752 inc r8
753 ; Check if we've reached end of the Array
754 cmp r8, r9
755 jne .copy_loop ; Next byte
756
757 .alloc_dest:
758 ; Reached the end of the destination
759 ; Need to allocate another Array
760 push rax
761 push rbx
762 call alloc_array ; New Array in rax
763 mov r8, rax ; copy to r8
764 pop rbx
765 pop rax
766
767 ; Previous Array in rax.
768 ; Add a reference to the new array and set length
769 mov QWORD [rax + Array.next], r8
770 mov DWORD [rax + Array.length], (Array.size - Array.data)
771 mov rax, r8 ; new array
772 add r8, Array.data ; Start of data
773
774 mov r9, rax
775 add r9, Array.size
776 jmp .copy_loop
777
778 .finished:
779 ; Compare r8 (destination) with data start
780 ; to get length of string
781 sub r8, rax
782 sub r8, Array.data
783 inc r8
784 ; r8 now contains length
785 mov DWORD [rax + Array.length], r8d
786 .return:
787 ret
788
789 ;; ------------------------------------------
790 ;; void print_string(char array)
791 ;; Address of the char Array should be in RSI
792 print_string:
793 ; Push registers we're going to use
794 push rax
795 push rdi
796 push rdx
797 push rsi
798
799 ; Check that we have a char array
800 mov al, [rsi]
801 cmp al, maltype_string
802 jne .error
803
804 .print_chunk:
805 ; write(1, string, length)
806 push rsi
807 mov edx, [rsi + Array.length] ; number of bytes
808 add rsi, Array.data ; address of raw string to output
809 call print_rawstring
810 pop rsi
811
812 ; Check if this is the end
813 mov rsi, QWORD [rsi + Array.next]
814 cmp rsi, 0
815 jne .print_chunk ; next chunk
816
817 ; Restore registers
818 pop rsi
819 pop rdx
820 pop rdi
821 pop rax
822
823 ret
824 .error:
825 ; An error occurred
826 mov rdx, error_msg_print_string.len ; number of bytes
827 mov rsi, error_msg_print_string ; address of raw string to output
828 call print_rawstring
829 ; exit
830 jmp quit_error
831
832 ;; Copy a string
833 ;;
834 ;; Input: RSI - String to copy
835 ;;
836 ;; Output: New string in RAX
837 ;;
838 ;; Modifies:
839 ;; RBX
840 ;; RCX
841 ;; RDX
842 ;; RSI
843 ;;
844 string_copy:
845 call string_new ; new string in RAX
846
847 push rsi
848 push rax
849
850 ; Get lengths
851 mov ebx, DWORD [rsi + Array.length]
852 mov [rax + Array.length], ebx
853
854 ; Copy the whole block of data
855 ; Not sure if this is quicker than copying byte-by-byte
856 ; Could divide ebx by 8 (rounded up) to get the number
857 ; of blocks needed
858
859 add rsi, Array.data ; Start of input data
860 add rax, Array.data ; Start of output data
861 mov ecx, array_chunk_len ; Number of 64-bit chunks
862
863 .loop:
864 mov rbx, QWORD [rsi]
865 mov [rax], QWORD rbx
866 add rsi, 8
867 add rax, 8
868 dec ecx
869 jnz .loop
870
871 pop rax
872 pop rsi
873 ; Now check if there's another block
874 mov rsi, [rsi + Array.next]
875 cmp rsi, 0
876 jz .done ; Result in RAX
877
878 ; Another array chunk
879 push rax ; Save output
880
881 call string_copy ; Copy next chunk
882 mov rbx, rax ; The copy in RBX
883
884 pop rax
885 ; append
886 mov [rax + Array.next], rbx
887 .done:
888 ret
889
890 ;; ------------------------------------------
891 ;; String itostring(Integer number)
892 ;;
893 ;; Converts an integer to a string (array of chars)
894 ;;
895 ;; Input in RAX
896 ;; Return string address in RAX
897 itostring:
898 ; Save registers to restore afterwards
899 push rbx
900 push rcx
901 push rdx
902 push rsi
903 push rdi
904
905 mov rcx, 0 ; counter of how many bytes we need to print in the end
906
907 mov rbx, rax ; Original input
908
909 ; Check if the number is negative
910 cmp rax, 0
911 jge .divideLoop
912
913 ; a negative number. To get the '-' sign
914 ; at the front the test is done again at the end
915 ; using the value stored in rbx
916
917 neg rax ; Make it positive
918
919 .divideLoop:
920 inc rcx ; count each byte to print - number of characters
921 xor rdx, rdx
922 mov rsi, 10
923 idiv rsi ; divide rax by rsi
924 add rdx, 48 ; convert rdx to it's ascii representation - rdx holds the remainder after a divide instruction
925 ; Character is now in DL
926 dec rsp
927 mov BYTE [rsp], dl ; Put onto stack
928
929 cmp rax, 0 ; can the integer be divided anymore?
930 jnz .divideLoop ; jump if not zero to the label divideLoop
931
932 ; Check if the value was negative (in rbx)
933 cmp rbx, 0
934 jge .create_string
935
936 ; a negative number
937 dec rsp
938 mov BYTE [rsp], '-'
939 inc rcx
940
941 .create_string:
942 ; Get an Array object to put the string into
943 call string_new ; Address in RAX
944
945 ; put length into string
946 mov [rax + Array.length], ecx
947
948 ; copy data from stack into string
949 ; Note: Currently this does not handle long strings
950 mov rdi, rax
951 add rdi, Array.data ; Address where raw string will go
952 .copyLoop:
953 mov BYTE dl, [rsp] ; Copy one byte at a time. Could be more efficient
954 mov [rdi], BYTE dl
955 inc rsp
956 inc rdi
957 dec rcx
958 cmp rcx, 0
959 jnz .copyLoop
960
961 ; Restore registers
962 pop rdi
963 pop rsi
964 pop rdx
965 pop rcx
966 pop rbx
967
968 ret
969
970
971 ;; ------------------------------------------------------------
972 ;; Object comparison
973 ;;
974 ;; These comparison functions take two objects
975 ;; in RSI and RDI
976 ;; and return a code (not an object) in RAX
977 ;;
978 ;; RAX = 0 Objects are equal
979 ;; 1 RSI object is greater than RDI
980 ;; 2 RSI object is less than RDI
981 ;; -1 Different object types, or no ordering
982 ;;
983 ;; Note that the ordering of objects depends on the type
984 ;; strings - Alphabetical
985 ;;
986 ;;
987 ;;
988
989 ;; Given an object in RSI, follows pointers
990 ;; to return the value object in RAX
991 ;;
992 ;; Modifies registers:
993 ;; RCX
994 compare_get_value:
995 mov cl, BYTE [rsi]
996 mov ch, cl
997 and ch, block_mask
998 jnz .nop ; Got an Array
999
1000 ; Here got Cons
1001 mov ch, cl
1002 and ch, content_mask
1003 cmp ch, content_pointer
1004 jne .nop ; Not a pointer
1005
1006 ; Got a pointer, so follow and return
1007 mov rax, [rsi + Cons.car]
1008 ret
1009 .nop:
1010 mov rax, rsi
1011 ret
1012
1013 ;; Compare two objects in RSI and RDI.
1014 ;; Note that this does not compare lists
1015 ;; but will just compare the first element
1016 ;;
1017 ;; Modifies registers
1018 ;; RAX, RBX, RCX, RDX
1019 ;;
1020 compare_objects:
1021 ; Get the value that RSI points to
1022 call compare_get_value
1023 mov rbx, rax ; Save in RBX
1024 ; Get the value that RDI points to
1025 mov rsi, rdi
1026 call compare_get_value
1027 mov rdi, rax
1028 mov rsi, rbx
1029
1030 ; now get types
1031 mov cl, BYTE [rsi] ; Type of RSI
1032 mov bl, BYTE [rdi] ; Type of RDI
1033
1034 mov ch, cl
1035 mov bh, bl
1036
1037 ; Don't care about container type
1038 and cl, block_mask + content_mask
1039 and bl, block_mask + content_mask
1040
1041 cmp bl, cl ; compare block and content
1042 jne .different_types
1043
1044 ; Here the same block, content type
1045 ; May be different container (value/list, string/symbol)
1046
1047 ; Need to distinguish between map and vector/list
1048 and ch, (block_mask + container_mask)
1049 and bh, (block_mask + container_mask)
1050 cmp ch, bh
1051 je .same_container
1052 ; if either is a map, then different types
1053 cmp ch, container_map
1054 je .different_types
1055 cmp bh, container_map
1056 je .different_types
1057
1058 .same_container:
1059 cmp bl, block_cons + content_nil
1060 je .objects_equal ; nil
1061
1062 cmp bl, block_array + content_char
1063 je compare_char_array ; strings, symbols
1064
1065 cmp bl, block_cons + content_int
1066 je .integers
1067
1068 ; Unknown
1069 jmp .different_types
1070
1071 .integers:
1072 ; two Cons objects, both containing integers
1073 mov rbx, [rsi + Cons.car]
1074 cmp rbx, [rdi + Cons.car]
1075 je .objects_equal
1076 jl .rdi_greater
1077 jmp .rsi_greater
1078
1079 .objects_equal:
1080 mov rax, 0
1081 ret
1082
1083 .rsi_greater: ; rsi > rdi
1084 mov rax, 1
1085 ret
1086
1087 .rdi_greater: ; rdi > rsi
1088 mov rax, 2
1089 ret
1090
1091 .different_types:
1092 mov rax, -1
1093 ret
1094
1095
1096 ;; Recursively check objects, including lists
1097 ;;
1098 ;; Inputs: Objects in RSI and RDI
1099 ;;
1100 ;; Sets ZF if equal, clears flag otherwise
1101 compare_objects_rec:
1102 ; Compare rsi and rdi objects
1103
1104 ; Check type
1105 mov al, BYTE [rsi]
1106 mov bl, BYTE [rdi]
1107
1108 mov ah, al
1109 mov bh, bl
1110
1111 ; Don't distinguish between [] and ()
1112 and ah, (block_mask + content_mask)
1113 and bh, (block_mask + content_mask)
1114
1115 cmp ah, bh
1116 jne .false
1117
1118 ; Need to distinguish between map and vector/list
1119 mov ah, al
1120 mov bh, bl
1121
1122 and ah, (block_mask + container_mask)
1123 and bh, (block_mask + container_mask)
1124 cmp ah, bh
1125 je .same_container
1126 ; if either is a map, then different types
1127 cmp ah, container_map
1128 je .false
1129 cmp bh, container_map
1130 je .false
1131
1132 .same_container:
1133
1134 ; Check the container type
1135 and bh, block_mask
1136 jnz .array
1137
1138 ; Check if a pointer to something
1139 and al, content_mask
1140 cmp al, content_pointer
1141 je .pointer
1142
1143 ; Get the values
1144
1145 mov rbx, [rsi + Cons.car]
1146 mov rcx, [rdi + Cons.car]
1147 cmp rbx, rcx
1148 jne .false
1149
1150 ; Value is the same, so get next
1151 jmp .next
1152
1153 .array:
1154 ; Comparing arrays
1155
1156 ; Container type (symbol/string) does matter
1157 cmp al, bl
1158 jne .false
1159
1160 call compare_char_array
1161 cmp rax, 0
1162 ret ; Array has no next
1163
1164 .pointer:
1165
1166 mov rbx, [rsi + Cons.car]
1167 mov rcx, [rdi + Cons.car]
1168 cmp rbx, rcx
1169 je .next ; Equal pointers
1170
1171 push rsi
1172 push rdi
1173 ; Put the addresses to compare into RSI and RDI
1174 mov rsi, rbx
1175 mov rdi, rcx
1176 call compare_objects_rec
1177 pop rdi
1178 pop rsi
1179 jne .false
1180 ; fall through to .next
1181
1182 .next:
1183 ; Check if both have a 'cdr' pointer
1184 mov al, BYTE [rsi + Cons.typecdr]
1185 mov bl, BYTE [rdi + Cons.typecdr]
1186
1187 cmp al, content_pointer
1188 je .rsi_has_next
1189
1190 ; No next pointer in RSI
1191 cmp bl, content_pointer
1192 je .false ; RDI has a next pointer
1193
1194 ; Neither have a next pointer, so done
1195 jmp .true
1196
1197 .rsi_has_next:
1198 cmp bl, content_pointer
1199 jne .false ; RDI has no next pointer
1200
1201 ; Both have a next pointer, so keep going
1202 mov rsi, [rsi + Cons.cdr]
1203 mov rdi, [rdi + Cons.cdr]
1204 jmp compare_objects_rec
1205
1206 .false:
1207 lahf ; flags in AH
1208 and ah, 255-64 ; clear zero flag
1209 sahf
1210 ret
1211 .true:
1212 lahf ; flags in AH
1213 or ah, 64 ; set zero flag
1214 sahf
1215 ret
1216
1217 ;; Char array objects (strings, symbols, keywords) in RSI and RDI
1218 ;; Return code in RAX
1219 ;;
1220 ;; Modifies registers:
1221 ;; RBX
1222 ;; RCX
1223 ;; RDX
1224 compare_char_array:
1225 ; Check length
1226 mov eax, DWORD [rsi + Array.length]
1227 mov ebx, DWORD [rdi + Array.length]
1228 cmp eax, ebx
1229 jne .different
1230
1231 ; same length
1232
1233 cmp eax, 0
1234 je .equal ; Both zero length
1235
1236 mov rbx, rsi
1237 add rbx, Array.data
1238 mov rcx, rdi
1239 add rcx, Array.data
1240 .compare_loop:
1241 ; get next character
1242 mov dl, BYTE [rbx]
1243 cmp dl, BYTE [rcx]
1244 jl .rdi_greater
1245 jg .rsi_greater
1246
1247 ; this character is equal
1248 inc rbx
1249 inc rcx
1250 dec eax
1251 jnz .compare_loop ; Next character
1252
1253 .equal:
1254 mov rax, 0
1255 ret
1256
1257 .rsi_greater: ; rsi > rdi
1258 mov rax, 1
1259 ret
1260
1261 .rdi_greater: ; rdi > rsi
1262 mov rax, 2
1263 ret
1264
1265 .different:
1266 mov rax, -1
1267 ret
1268
1269 ;; ------------------------------------------------------------
1270 ;; Map type
1271 ;;
1272 ;; This uses a list (Cons type) to represent key-value pairs in
1273 ;; a single chain. The only map which consists of an odd number of Cons
1274 ;; objects is the empty map, created by map_new
1275 map_new:
1276 call alloc_cons
1277 mov [rax], BYTE (block_cons + container_map + content_empty)
1278 mov [rax + Cons.typecdr], BYTE content_nil
1279 ret
1280
1281 ;; Copy map
1282 ;;
1283 ;; Input: RSI - map
1284 ;;
1285 ;; Returns: new map in RAX
1286 ;;
1287 ;; Modifies:
1288 ;; RAX, RBX, RCX, R13, R14, R15
1289 ;;
1290 map_copy:
1291 mov r14, rsi
1292
1293 call alloc_cons
1294 mov r15, rax ; start of new map
1295 xor r13, r13
1296 .loop:
1297 mov bl, BYTE [rsi]
1298 mov rcx, [rsi + Cons.car]
1299 mov [rax], BYTE bl ; copy type
1300 mov [rax + Cons.car], rcx ; copy value
1301
1302 and bl, content_mask
1303 cmp bl, content_pointer
1304 jne .set_cdr
1305
1306 ; A pointer in CAR. Increase reference count
1307 mov bx, WORD [rcx + Cons.refcount]
1308 inc bx
1309 mov [rcx + Cons.refcount], WORD bx
1310
1311 .set_cdr:
1312 test r13,r13
1313 jz .next
1314
1315 ; R13 contains last Cons
1316 mov [r13 + Cons.typecdr], BYTE content_pointer
1317 mov [r13 + Cons.cdr], rax
1318 .next:
1319 mov r13, rax
1320
1321 ; Check if there's another Cons
1322 mov bl, BYTE [rsi + Cons.typecdr]
1323 cmp bl, content_pointer
1324 jne .done ; no more
1325
1326 mov rsi, [rsi + Cons.cdr] ; next
1327 call alloc_cons
1328 jmp .loop
1329 .done:
1330 mov rax, r15
1331 mov rsi, r14
1332 ret
1333
1334
1335 ;; Add to map. Input is a list with an even number of values
1336 ;; as (key, value, key, value, ...)
1337 ;;
1338 ;; Inputs:
1339 ;; RSI - Map to append to. This is not modified
1340 ;; RDI - List to add to the map
1341 ;; Outputs:
1342 ;; RAX - New map
1343 ;;
1344 ;; Modifies:
1345 ;; RCX
1346 map_add:
1347 ; Check type of input
1348 mov cl, BYTE [rsi]
1349 mov cl, ch
1350 and ch, block_mask + container_mask
1351 cmp ch, block_cons + container_map
1352 jne .error
1353
1354 mov cl, BYTE [rdi]
1355 and cl, block_mask + container_mask
1356 cmp cl, block_cons + container_list
1357 jne .error
1358
1359 xor r8, r8 ; Zero r8
1360
1361 .copy_input:
1362 ; Copy input list, changing container type
1363 call alloc_cons
1364
1365 mov cl, BYTE [rdi]
1366 and cl, content_mask ; Keep the content
1367 add cl, block_cons + container_map
1368 mov [rax], BYTE cl ; Set type
1369 mov rcx, [rdi+Cons.car] ; Copy data
1370 mov [rax+Cons.car], rcx
1371
1372 cmp cl, (block_cons + container_map + content_pointer)
1373 jne .copy_not_pointer
1374
1375 ; Copying a pointer to data
1376 ; so need to increase the reference count
1377 mov bx, WORD [rcx + Cons.refcount] ; Same offset for Array
1378 inc bx
1379 mov [rcx + Cons.refcount], WORD bx
1380
1381 .copy_not_pointer:
1382
1383 ; Check if this is the first object
1384 cmp r8, 0
1385 jnz .copy_not_first
1386 mov r8, rax ; Save start of map to R8
1387 mov r9, rax ; Last cons in R9
1388 jmp .copy_next
1389
1390 .copy_not_first:
1391 ; Append to R9
1392 mov [r9+Cons.cdr], rax
1393 mov [r9+Cons.typecdr], BYTE content_pointer
1394
1395 ; Put new Cons in R9 as the latest in the list
1396 mov r9, rax
1397
1398 .copy_next:
1399 ; Check if we've reached the end
1400 mov cl, BYTE [rdi + Cons.typecdr]
1401 cmp cl, content_nil
1402 je .copy_finished
1403
1404 ; Not yet. Get next Cons and keep going
1405 mov rdi, [rdi + Cons.cdr]
1406 jmp .copy_input
1407
1408 .copy_finished:
1409 ; Start of map in r8, end in r9
1410
1411 ; Check if the original map is empty
1412 mov cl, [rsi]
1413 and cl, content_mask
1414 cmp cl, content_empty
1415 je .return
1416
1417 ; Put old map on the end of the new map
1418 ; For now this avoids the need to overwrite
1419 ; values in the map, since a search will find
1420 ; the new values first.
1421
1422 mov [r9 + Cons.cdr], rsi
1423 mov [r9 + Cons.typecdr], BYTE content_pointer
1424
1425 ; Increment reference count
1426 mov bx, WORD [rsi + Cons.refcount]
1427 inc bx
1428 mov [rsi + Cons.refcount], WORD bx
1429
1430 .return:
1431 mov rax, r8
1432 ret
1433
1434 .error:
1435 ; Return nil
1436 call alloc_cons
1437 mov [rax], BYTE maltype_nil
1438 mov [rax + Cons.typecdr], BYTE content_nil
1439 ret
1440
1441 ;; Find a key in a map
1442 ;;
1443 ;; Inputs: RSI - map [ Modified ]
1444 ;; RDI - key [ Modified ]
1445 ;;
1446 ;; Outputs: RAX - Cons object containing value in CAR
1447 ;;
1448 ;; Modifies registers:
1449 ;; RBX [compare_objects, alloc_cons]
1450 ;; RCX [compare_objects]
1451 ;;
1452 ;;
1453 ;; If value is found then the Zero Flag is set
1454 ;;
1455 ;; Examples:
1456 ;; {a 1 b 2} find a -> {1 b 2}
1457 ;; {1 2 3 4} find a -> {4}
1458 map_find:
1459 mov al, BYTE [rsi]
1460 cmp al, maltype_empty_map
1461 je .not_found
1462
1463 .map_loop:
1464 ; compare RSI and RDI, ignoring differences in container
1465 push rsi
1466 push rdi
1467 call compare_objects
1468 pop rdi
1469 pop rsi
1470
1471 ; rax is now zero if objects are equal
1472 cmp rax, 0
1473 je .found
1474
1475 ; Move along two cons to the next key
1476 mov al, [rsi + Cons.typecdr]
1477 cmp al, content_pointer
1478 jne .error ; Expecting value after key
1479
1480 mov rsi, [rsi + Cons.cdr] ; Get value
1481 mov al, [rsi + Cons.typecdr]
1482 cmp al, content_pointer
1483 jne .not_found
1484
1485 mov rsi, [rsi + Cons.cdr] ; Get next key
1486
1487 jmp .map_loop ; Test next key
1488
1489 .found:
1490
1491 lahf ; flags in AH
1492 or ah, 64 ; set zero flag
1493 sahf
1494
1495 ; key in rsi. Get next value
1496 mov al, [rsi + Cons.typecdr]
1497 cmp al, content_pointer
1498 jne .error ; Expecting value after key
1499
1500 mov rsi, [rsi + Cons.cdr]
1501
1502 ; ; increment reference count
1503 ; mov ax, WORD [rsi + Cons.refcount]
1504 ; inc ax
1505 ; mov [rsi + Cons.refcount], WORD ax
1506 ; Put address in rax
1507 mov rax, rsi
1508 ret
1509
1510 .not_found:
1511 lahf ; flags in AH
1512 and ah, 255-64 ; clear zero flag
1513 sahf
1514
1515 ; last cons in rsi
1516 ; increment reference count
1517 ; mov ax, WORD [rsi + Cons.refcount]
1518 ; inc ax
1519 ; mov [rsi + Cons.refcount], WORD ax
1520 ; Put address in rax
1521 mov rax, rsi
1522
1523 ret
1524
1525 .error:
1526
1527 lahf ; flags in AH
1528 and ah, 255-64 ; clear zero flag
1529 sahf
1530
1531 ; return nil
1532 call alloc_cons
1533 mov [rax], BYTE maltype_nil
1534 mov [rax + Cons.typecdr], BYTE content_nil
1535 ret
1536
1537 ;; Map set
1538 ;;
1539 ;; Sets a key-value pair in a map
1540 ;;
1541 ;; Inputs: RSI - map [not modified]
1542 ;; RDI - key [not modified]
1543 ;; RCX - value [not modified]
1544 ;;
1545 ;; If references are added to key or value,
1546 ;; then reference counts are incremented.
1547 ;;
1548 ;; Modifies registers:
1549 ;; R8
1550 ;; R9
1551 ;; R10
1552 map_set:
1553 ; Save inputs in less volatile registers
1554 mov r8, rsi ; map
1555 mov r9, rdi ; key
1556 mov r10, rcx ; value
1557
1558 ; Find the key, to see if it already exists in the map
1559 call map_find ; Cons object in RAX
1560 je .found_key
1561
1562 ; Key not in map. RAX should be address of the last
1563 ; value in the map, or empty
1564 mov bl, BYTE [rax]
1565 cmp bl, maltype_empty_map
1566 je .set_key
1567
1568 ; Append key
1569 push rax
1570 call alloc_cons ; New Cons in rax
1571 pop rbx ; Last Cons in map
1572
1573 ; append rax to rbx
1574 mov [rbx + Cons.typecdr], BYTE content_pointer
1575 mov [rbx + Cons.cdr], rax
1576 jmp .set_key ; Put key into rax
1577
1578 .found_key:
1579 ; Key already in map, so replace value
1580 ; address in RAX
1581
1582 ; check type of value already there
1583 mov bl, BYTE [rax]
1584 and bl, content_mask
1585 cmp bl, content_pointer
1586 jne .set_value ; Not a pointer, just overwrite
1587
1588 ; A pointer, so need to release
1589 mov rsi, [rax + Cons.car] ; Address of object
1590 push rax
1591 call release_object
1592 pop rax
1593
1594 jmp .set_value ; put value into Cons
1595
1596 .set_key:
1597 ; Put key (R9) in RAX
1598
1599 ; Check the type of object
1600 mov bl, BYTE [r9]
1601 mov bh, bl
1602 and bh, block_mask
1603 jnz .set_key_pointer ; Array, so point to it
1604
1605 ; Here a Cons object
1606 mov bh, bl
1607 and bh, container_mask
1608 cmp bh, container_value
1609 jne .set_key_pointer ; Not a simple value, so point to it
1610
1611 ; A value, so copy
1612 mov rcx, [r9 + Cons.car]
1613 mov [rax + Cons.car], rcx
1614
1615 ; Set the type
1616 and bl, content_mask
1617 or bl, (block_cons + container_map)
1618 mov [rax], BYTE bl
1619
1620 jmp .set_key_done
1621
1622 .set_key_pointer:
1623 ; The key is a pointer
1624
1625 mov [rax + Cons.car], r9
1626 mov [rax], BYTE (block_cons + container_map + content_pointer)
1627 ; Increment reference count
1628 mov bx, WORD [r9 + Cons.refcount]
1629 inc bx
1630 mov [r9 + Cons.refcount], bx
1631 ; fall through to .set_key_done
1632
1633 .set_key_done:
1634 ; Key in RAX. allocate and append a Cons for the value
1635 push rax
1636 call alloc_cons ; value Cons in rax
1637 pop rbx ; key Cons
1638 ; append rax to rbx
1639 mov [rbx + Cons.typecdr], BYTE content_pointer
1640 mov [rbx + Cons.cdr], rax
1641
1642 ; fall through to .set_value
1643
1644 ; --------------------------------
1645 .set_value:
1646 ; Set the value into the Cons at [rax]
1647
1648 ; Check the type of object
1649 mov bl, BYTE [r10]
1650 mov bh, bl
1651 and bh, block_mask
1652 jnz .set_value_pointer ; Array, so point to it
1653
1654 ; Here a Cons object
1655 mov bh, bl
1656 and bh, container_mask
1657 cmp bh, container_value
1658 jne .set_value_pointer ; Not a simple value, so point to it
1659 ; A value, so copy
1660 mov rcx, [r10 + Cons.car]
1661 mov [rax + Cons.car], rcx
1662
1663 ; Set the type
1664 and bl, content_mask
1665 or bl, (block_cons + container_map)
1666 mov [rax], BYTE bl
1667
1668 jmp .finished
1669
1670 .set_value_pointer:
1671 mov [rax + Cons.car], r10 ; Put address into CAR
1672 mov [rax], BYTE (block_cons + container_map + content_pointer) ; Mark as a pointer
1673 ; Increment reference count
1674 mov bx, WORD [r10 + Cons.refcount]
1675 inc bx
1676 mov [r10 + Cons.refcount], bx
1677 ; fall through to .finished
1678
1679 .finished:
1680 ; Restore inputs
1681 mov rsi, r8
1682 mov rdi, r9
1683 mov rcx, r10
1684 ret
1685
1686 ;; Get a value from a map, incrementing the reference count
1687 ;; of the object returned
1688 ;;
1689 ;; Inputs: RSI - map
1690 ;; RDI - key
1691 ;;
1692 ;; Returns: If found, Zero Flag is set and address in RAX
1693 ;; If not found, Zero Flag cleared
1694 ;;
1695 ;; Modifies registers:
1696 ;; RAX
1697 ;; RBX
1698 ;; RCX
1699 ;; R8
1700 ;; R9
1701 map_get:
1702 ; Save inputs
1703 mov r8, rsi ; map
1704 mov r9, rdi ; key
1705
1706 call map_find ; Cons object in RAX
1707 je .found_key
1708
1709 ; Not found
1710
1711 mov rsi, r8
1712 mov rdi, r9
1713
1714 lahf ; flags in AH
1715 and ah, 255-64 ; clear zero flag
1716 sahf
1717
1718 ret
1719 ; ---------------
1720 .found_key:
1721
1722 ; Check if the object in RAX is a value or pointer
1723 mov bl, BYTE [rax]
1724 and bl, content_mask
1725 cmp bl, content_pointer
1726 je .got_pointer
1727
1728 ; A value, so copy
1729
1730 push rax
1731 push rbx
1732 call alloc_cons ; cons in rax
1733 pop rbx ; content type in bl
1734 pop rcx ; Object to copy
1735
1736 add bl, block_cons + container_value
1737 mov [rax], BYTE bl ; set type
1738 mov [rax + Cons.typecdr], BYTE content_nil
1739
1740 ; Copy value
1741 mov rbx, [rcx + Cons.car]
1742 mov [rax + Cons.car], rbx
1743
1744 jmp .finished_found
1745
1746 .got_pointer:
1747 ; A pointer, so get the address
1748 mov rax, [rax + Cons.car]
1749
1750 ; increment reference count
1751 mov bx, WORD [rax + Cons.refcount]
1752 inc bx
1753 mov [rax + Cons.refcount], bx
1754
1755 ; Fall through to .finished_found
1756 .finished_found:
1757 mov rsi, r8
1758 mov rdi, r9
1759
1760 mov rbx, rax
1761 lahf ; flags in AH
1762 or ah, 64 ; set zero flag
1763 sahf
1764 mov rax, rbx
1765 ret
1766
1767 ;; Get a list of keys
1768 ;;
1769 ;; Input: Map in RSI
1770 ;;
1771 ;; Returns: List in RAX
1772 ;;
1773 ;; Modifies registers:
1774 ;; RAX
1775 ;; RBX
1776 ;; RCX
1777 ;; R8
1778 ;; R9
1779 map_keys:
1780 ; check type
1781 mov al, BYTE [rsi]
1782 cmp al, maltype_empty_map
1783 je .empty_map
1784
1785 and al, container_mask
1786 cmp al, container_map
1787 jne .empty_map ; error
1788
1789 xor r8, r8 ; Return list
1790
1791 ; Take the current value
1792 .loop:
1793 ; Create a new Cons for this key
1794 call alloc_cons
1795 mov cl, BYTE [rsi]
1796 and cl, content_mask
1797 add cl, block_cons + container_list
1798 mov [rax], BYTE cl ; Set type
1799 mov rbx, [rsi + Cons.car]
1800 mov [rax + Cons.car], rbx ; Set value
1801
1802 and cl, content_mask
1803 cmp cl, content_pointer
1804 jne .append
1805
1806 ; A pointer, so increment reference count
1807 mov cx, WORD [rbx + Cons.refcount]
1808 inc cx
1809 mov [rbx + Cons.refcount], WORD cx
1810
1811 .append:
1812 cmp r8, 0
1813 je .first
1814
1815 ; appending
1816 mov [r9 + Cons.typecdr], BYTE content_pointer
1817 mov [r9 + Cons.cdr], rax
1818 mov r9, rax
1819 jmp .next
1820 .first:
1821 ; First key, so put into r8
1822 mov r8, rax
1823 mov r9, rax
1824 .next:
1825 ; First get the value
1826 mov al, BYTE [rsi + Cons.typecdr]
1827 cmp al, content_pointer
1828 jne .done ; error. Should be a value
1829 mov rsi, [rsi + Cons.cdr]
1830
1831 ; Get the next key
1832 mov al, BYTE [rsi + Cons.typecdr]
1833 cmp al, content_pointer
1834 jne .done
1835 mov rsi, [rsi + Cons.cdr]
1836 jmp .loop
1837 .done:
1838 ; Finished, return the list
1839 mov rax, r8
1840 ret
1841
1842 .empty_map:
1843 ; return empty list
1844 call alloc_cons
1845 mov [rax], BYTE maltype_empty_list
1846 ret
1847
1848 ;; Get a list of values
1849 ;;
1850 ;; Input: Map in RSI
1851 ;;
1852 ;; Returns: List in RAX
1853 ;;
1854 ;; Modifies registers:
1855 ;; RAX
1856 ;; RBX
1857 ;; RCX
1858 ;; R8
1859 ;; R9
1860 map_vals:
1861 ; check type
1862 mov al, BYTE [rsi]
1863 cmp al, maltype_empty_map
1864 je .empty_map
1865
1866 and al, container_mask
1867 cmp al, container_map
1868 jne .empty_map ; error
1869
1870 xor r8, r8 ; Return list
1871
1872 .loop:
1873 ; Here should have a key in RSI
1874
1875 ; First get the value
1876 mov al, BYTE [rsi + Cons.typecdr]
1877 cmp al, content_pointer
1878 jne .done ; error. Should be a value
1879
1880 mov rsi, [rsi + Cons.cdr] ; Now have value in RSI
1881
1882 ; Create a new Cons for this value
1883 call alloc_cons
1884 mov cl, BYTE [rsi]
1885 and cl, content_mask
1886 add cl, block_cons + container_list
1887 mov [rax], BYTE cl ; Set type
1888 mov rbx, [rsi + Cons.car]
1889 mov [rax + Cons.car], rbx ; Set value
1890
1891 and cl, content_mask
1892 cmp cl, content_pointer
1893 jne .append
1894
1895 ; A pointer, so increment reference count
1896 mov cx, WORD [rbx + Cons.refcount]
1897 inc cx
1898 mov [rbx + Cons.refcount], WORD cx
1899
1900 .append:
1901 cmp r8, 0
1902 je .first
1903
1904 ; appending
1905 mov [r9 + Cons.typecdr], BYTE content_pointer
1906 mov [r9 + Cons.cdr], rax
1907 mov r9, rax
1908 jmp .next
1909 .first:
1910 ; First key, so put into r8
1911 mov r8, rax
1912 mov r9, rax
1913 .next:
1914 ; Get the next key
1915 mov al, BYTE [rsi + Cons.typecdr]
1916 cmp al, content_pointer
1917 jne .done
1918 mov rsi, [rsi + Cons.cdr]
1919 jmp .loop
1920 .done:
1921 ; Finished, return the list
1922 mov rax, r8
1923 ret
1924
1925 .empty_map:
1926 ; return empty list
1927 call alloc_cons
1928 mov [rax], BYTE maltype_empty_list
1929 ret
1930
1931
1932 ;; ------------------------------------------------------------
1933 ;; Function type
1934 ;;
1935 ;; Functions are consist of a list
1936 ;; - First car is the function address to call
1937 ;; - Second is the Meta data (nil by default)
1938 ;; - Third is the environment
1939 ;; - Fourth is the binds list
1940 ;; - Fifth is the body of the function
1941 ;;
1942 ;; ( addr meta env binds body )
1943 ;;
1944 ;;
1945
1946 ;; Address of native function in RSI
1947 ;; returns Function object in RAX
1948 native_function:
1949 call alloc_cons ; for meta
1950 mov [rax], BYTE maltype_nil
1951 push rax
1952
1953 call alloc_cons ; For function address
1954 mov [rax], BYTE (block_cons + container_function + content_function)
1955 mov [rax + Cons.car], rsi
1956
1957 mov [rax + Cons.typecdr], BYTE content_pointer
1958 pop rbx ; meta
1959 mov [rax + Cons.cdr], rbx
1960 ret