4 ;; Memory management is done by having two fixed-size datatypes,
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 ]
15 ;; The 8-bit type fields describe the Block, Container and Content type.
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
21 ;; Block type [1 bit]:
22 ;; 0 0 - Cons memory block
23 ;; 1 1 - Array memory block
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
35 ;; Content type [4 bits]:
41 ;; 80 5 - Pointer (memory address)
42 ;; 96 6 - Function (instruction address)
43 ;; 112 7 - Empty (distinct from Nil)
48 ;; These represent MAL data types as follows:
50 ;; MAL type Block Container Content
51 ;; --------- | -------- | ---------- | ---------
52 ;; integer Cons Value Int
53 ;; symbol Array Symbol Char
55 ;; vector Cons Vector Any
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
68 ;; Used to store either a single value with type information
69 ;; or a pair of (value, Pointer or Nil) to represent a list
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
80 %define array_chunk_len
32 ; Number of 64-bit values which can be stored in a single chunk
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
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
99 %define block_cons
0 ; Note: This must be zero
100 %define block_array
1
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
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
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
)
138 ;; ------------------------------------------
142 ;; Fixed strings for printing
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
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"
151 ;; ------------------------------------------
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.
161 %define heap_cons_limit
5000 ; Number of cons objects which can be created
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
166 %define heap_array_limit
2000 ; Number of array objects which can be created
168 heap_array_next: dq heap_array_store
169 heap_array_free: dq 0
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
177 heap_array_store: resb heap_array_limit
* Array.
size
182 ;; ------------------------------------------
183 ;; Array alloc_array()
185 ;; Returns the address of an Array object in RAX
187 ;; Working registers: rbx
190 ; Get the address of a free array
191 mov rax
, [heap_array_free
] ; Address of the array
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
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
210 add rbx
, Array.
size ; Address of the next array
211 mov [heap_array_next
], rbx
; for next time
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
223 mov rsi
, error_array_memory_limit
224 mov rdx
, error_array_memory_limit.len
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
234 mov ax, WORD [rsi
+ Array.refcount
]
236 ; Check if reference count is already zero
241 mov WORD [rsi
+ Array.refcount
], ax
242 jz .free
; If the count reaches zero then put on free list
247 mov rbx
, [rsi
+ Array.next
]
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
254 jne .release_next
; If there is another array, then need to release it
259 ; release the next array
266 load_static error_cons_double_free
271 ;; ------------------------------------------
274 ;; Returns the address of a Cons object in RAX
280 ; Get the address of a free cons
281 mov rax
, [heap_cons_free
] ; Address of the cons
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
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
300 add rbx
, Cons.
size ; Address of the next cons
301 mov [heap_cons_next
], rbx
; for next time
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
313 mov rsi
, error_cons_memory_limit
314 mov rdx
, error_cons_memory_limit.len
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
324 ;; Modifies registers:
330 mov ax, WORD [rsi
+ Cons.refcount
]
332 ; Check if already released
337 mov WORD [rsi
+ Cons.refcount
], ax
338 jz .free
; If the count reaches zero then put on free list
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
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
351 ; Check if the CAR needs to be released
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
358 ; CAR is a pointer to either a Cons or Array
359 ; Get the address stored in CAR
360 mov rsi
, [rsi
+ Cons.car
]
363 pop rcx
; This was rsi, the original Cons
364 pop rsi
; This was rcx, the original Cons.cdr
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
376 .
double_free: ; Already released
378 load_static error_cons_double_free
383 ;; Releases either a Cons or Array
384 ;; Address of object in RSI
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
398 ;; Increment reference count of Cons or Array
399 ;; Address of object in RSI
401 ;; This code makes use of the fact that the reference
402 ;; count is in the same place in Cons and Array types
407 mov ax, WORD [rsi
+ Cons.refcount
] ; Same for Array
409 ; Check for overflow?
410 mov [rsi
+ Cons.refcount
], WORD ax
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
418 ;; Input: RSI - address of list/vector
420 ;; Returns: New list/vector in RAX, last Cons in RBX
431 push rsi
; Restored at the end
433 mov r8
, rsi
; Input in R8
434 xor r9
, r9
; Head of list in R9, start in R10
440 jnz .not_seq
; Not a Cons object
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
449 cmp cl, content_pointer
452 ; A pointer, so increment the reference count
457 ; Check if this is the first
463 mov r10
, rdx
; Start of the list, will be returned
467 ; Appending to last Cons
468 mov [r9
+ Cons.cdr
], rdx
469 mov [r9
+ Cons.typecdr
], BYTE content_pointer
474 ; Check if there's another
475 mov al, BYTE [r8
+ Cons.typecdr
]
476 cmp al, content_pointer
479 mov r8
, [r8
+ Cons.cdr
]
483 pop rsi
; Restore input
484 mov rax
, r10
; Output list
485 mov rbx
, r9
; Last Cons
492 ;; -------------------------------------------
495 ;; Create a new string, address in RAX
497 ;; Modifies registers
502 mov [rax
], BYTE maltype_string
503 mov DWORD [rax
+ Array.
length], 0
504 mov QWORD [rax
+ Array.next
], 0
507 ;; Convert a raw string to a String type
509 ;; Input: Address of raw string in RSI, length in EDX
510 ;; Output: Address of string in RAX
512 ;; Modifies registers: R8,R9,RCX
515 ; Save registers to restore at the end
521 call string_new
; String now in RAX
526 add r8
, Array.data
; Address of string data
528 add r10
, Array.
size ; End of the destination data
529 mov r11
, rax
; First Array to return
531 mov r9
, rsi
; Address of raw data
535 test ecx, ecx ; Check if count is zero
542 ; Move the destination
547 ; Hit the end. Set the length of the array
548 mov [rax
+ Array.
length], DWORD (array_chunk_len
* 8)
550 push rax
; Last Array
553 call string_new
; String now in RAX
557 mov [rbx
+ Array.next
], rax
; Point to new Array
560 add r8
, Array.data
; Address of string data
562 add r10
, Array.
size ; End of the destination data
570 ; Set the length of the destination array
573 mov [rax
+ Array.
length], DWORD r8d
575 ; Move first Array into RAX
584 ;; Convert a raw string to a symbol
586 ;; Input: Address of raw string in RSI, length in EDX
587 ;; Output: Address of string in RAX
589 ;; Modifies registers: R8,R9,RCX
592 ; set the content type
593 mov [rax
], BYTE (block_array
+ container_symbol
+ content_char
)
596 ;; Convert a NUL terminated C string to string
598 ;; Input: RSI - Address of string
600 ;; Returns: String in RAX
609 call string_new
; in RAX
613 add rbx
, Array.data
; Start of output
615 add rcx
, Array.
size ; End of output
618 test dl, dl ; Check if NUL (0)
627 ; rbx now contains the length
628 mov [rax
+ Array.
length], DWORD ebx
631 ;; Appends a character to a string
632 ;; Input: Address of string in RSI, character in CL
638 ; Get the end of the string
640 mov rax
, [rsi
+ Array.next
]
647 ; Check if this chunk is full
648 mov eax, DWORD [rsi
+ Array.
length]
649 cmp eax, (array_chunk_len
*8)
652 ; full, need to allocate another
654 mov [rsi
+ Array.next
], rax
656 xor eax, eax ; Set length to zero
659 mov DWORD [rsi
+ Array.
length], eax
662 add rax
, Array.data
; End of data
665 pop rsi
; Restore original value
668 ;; Appends a string to the end of a string
670 ;; Input: String to be modified in RSI
671 ;; String to be copied in RDX
673 ;; Output: Modified string in RSI
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
687 ; source data address in r10
689 add r10
, Array.data
; Start of the data
691 ; source data end address in r11
693 mov r8d
, DWORD [rbx
+ Array.
length]
697 jz .return
; Appending zero-size array
699 ; Find the end of the string in RSI
700 ; and put the address of the Array object into rax
703 mov r8
, QWORD [rax
+ Array.next
]
704 test r8
, r8
; Next chunk is 0
705 je .got_dest_end
; so reached end
707 mov rax
, r8
; Go to next chunk
711 ; destination data address into r8
714 add r8d
, DWORD [rax
+ Array.
length]
716 ; destination data end into r9
720 ; Check if we are at the end of the destination
725 ; Copy one byte from source to destination
729 ; move source to next byte
731 ; Check if we've reached the end of this Array
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
741 ; Get source address into r10
743 add r10
, Array.data
; Start of the data
746 mov r11d
, DWORD [rbx
+ Array.
length] ; Length of the array
751 ; Move destination to next byte
753 ; Check if we've reached end of the Array
755 jne .copy_loop
; Next byte
758 ; Reached the end of the destination
759 ; Need to allocate another Array
762 call alloc_array
; New Array in rax
763 mov r8
, rax
; copy to r8
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
779 ; Compare r8 (destination) with data start
780 ; to get length of string
784 ; r8 now contains length
785 mov DWORD [rax
+ Array.
length], r8d
789 ;; ------------------------------------------
790 ;; void print_string(char array)
791 ;; Address of the char Array should be in RSI
793 ; Push registers we're going to use
799 ; Check that we have a char array
801 cmp al, maltype_string
805 ; write(1, string, length)
807 mov edx, [rsi
+ Array.
length] ; number of bytes
808 add rsi
, Array.data
; address of raw string to output
812 ; Check if this is the end
813 mov rsi
, QWORD [rsi
+ Array.next
]
815 jne .print_chunk
; next chunk
826 mov rdx
, error_msg_print_string.len
; number of bytes
827 mov rsi
, error_msg_print_string
; address of raw string to output
834 ;; Input: RSI - String to copy
836 ;; Output: New string in RAX
845 call string_new
; new string in RAX
851 mov ebx, DWORD [rsi
+ Array.
length]
852 mov [rax
+ Array.
length], ebx
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
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
873 ; Now check if there's another block
874 mov rsi
, [rsi
+ Array.next
]
876 jz .done
; Result in RAX
878 ; Another array chunk
879 push rax
; Save output
881 call string_copy
; Copy next chunk
882 mov rbx
, rax
; The copy in RBX
886 mov [rax
+ Array.next
], rbx
890 ;; ------------------------------------------
891 ;; String itostring(Integer number)
893 ;; Converts an integer to a string (array of chars)
896 ;; Return string address in RAX
898 ; Save registers to restore afterwards
905 mov rcx
, 0 ; counter of how many bytes we need to print in the end
907 mov rbx
, rax
; Original input
909 ; Check if the number is negative
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
917 neg rax
; Make it positive
920 inc rcx
; count each byte to print - number of characters
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
927 mov BYTE [rsp
], dl ; Put onto stack
929 cmp rax
, 0 ; can the integer be divided anymore?
930 jnz .divideLoop
; jump if not zero to the label divideLoop
932 ; Check if the value was negative (in rbx)
942 ; Get an Array object to put the string into
943 call string_new
; Address in RAX
945 ; put length into string
946 mov [rax
+ Array.
length], ecx
948 ; copy data from stack into string
949 ; Note: Currently this does not handle long strings
951 add rdi
, Array.data
; Address where raw string will go
953 mov BYTE dl, [rsp
] ; Copy one byte at a time. Could be more efficient
971 ;; ------------------------------------------------------------
974 ;; These comparison functions take two objects
976 ;; and return a code (not an object) in RAX
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
983 ;; Note that the ordering of objects depends on the type
984 ;; strings - Alphabetical
989 ;; Given an object in RSI, follows pointers
990 ;; to return the value object in RAX
992 ;; Modifies registers:
998 jnz .
nop ; Got an Array
1002 and ch, content_mask
1003 cmp ch, content_pointer
1004 jne .
nop ; Not a pointer
1006 ; Got a pointer, so follow and return
1007 mov rax
, [rsi
+ Cons.car
]
1013 ;; Compare two objects in RSI and RDI.
1014 ;; Note that this does not compare lists
1015 ;; but will just compare the first element
1017 ;; Modifies registers
1018 ;; RAX, RBX, RCX, RDX
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
1026 call compare_get_value
1031 mov cl, BYTE [rsi
] ; Type of RSI
1032 mov bl, BYTE [rdi
] ; Type of RDI
1037 ; Don't care about container type
1038 and cl, block_mask
+ content_mask
1039 and bl, block_mask
+ content_mask
1041 cmp bl, cl ; compare block and content
1042 jne .different_types
1044 ; Here the same block, content type
1045 ; May be different container (value/list, string/symbol)
1047 ; Need to distinguish between map and vector/list
1048 and ch, (block_mask
+ container_mask
)
1049 and bh, (block_mask
+ container_mask
)
1052 ; if either is a map, then different types
1053 cmp ch, container_map
1055 cmp bh, container_map
1059 cmp bl, block_cons
+ content_nil
1060 je .objects_equal
; nil
1062 cmp bl, block_array
+ content_char
1063 je compare_char_array
; strings, symbols
1065 cmp bl, block_cons
+ content_int
1069 jmp .different_types
1072 ; two Cons objects, both containing integers
1073 mov rbx
, [rsi
+ Cons.car
]
1074 cmp rbx
, [rdi
+ Cons.car
]
1083 .
rsi_greater: ; rsi > rdi
1087 .
rdi_greater: ; rdi > rsi
1096 ;; Recursively check objects, including lists
1098 ;; Inputs: Objects in RSI and RDI
1100 ;; Sets ZF if equal, clears flag otherwise
1101 compare_objects_rec:
1102 ; Compare rsi and rdi objects
1111 ; Don't distinguish between [] and ()
1112 and ah, (block_mask
+ content_mask
)
1113 and bh, (block_mask
+ content_mask
)
1118 ; Need to distinguish between map and vector/list
1122 and ah, (block_mask
+ container_mask
)
1123 and bh, (block_mask
+ container_mask
)
1126 ; if either is a map, then different types
1127 cmp ah, container_map
1129 cmp bh, container_map
1134 ; Check the container type
1138 ; Check if a pointer to something
1139 and al, content_mask
1140 cmp al, content_pointer
1145 mov rbx
, [rsi
+ Cons.car
]
1146 mov rcx
, [rdi
+ Cons.car
]
1150 ; Value is the same, so get next
1156 ; Container type (symbol/string) does matter
1160 call compare_char_array
1162 ret ; Array has no next
1166 mov rbx
, [rsi
+ Cons.car
]
1167 mov rcx
, [rdi
+ Cons.car
]
1169 je .next
; Equal pointers
1173 ; Put the addresses to compare into RSI and RDI
1176 call compare_objects_rec
1180 ; fall through to .next
1183 ; Check if both have a 'cdr' pointer
1184 mov al, BYTE [rsi
+ Cons.typecdr
]
1185 mov bl, BYTE [rdi
+ Cons.typecdr
]
1187 cmp al, content_pointer
1190 ; No next pointer in RSI
1191 cmp bl, content_pointer
1192 je .false
; RDI has a next pointer
1194 ; Neither have a next pointer, so done
1198 cmp bl, content_pointer
1199 jne .false
; RDI has no next pointer
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
1208 and ah, 255-64 ; clear zero flag
1213 or ah, 64 ; set zero flag
1217 ;; Char array objects (strings, symbols, keywords) in RSI and RDI
1218 ;; Return code in RAX
1220 ;; Modifies registers:
1226 mov eax, DWORD [rsi
+ Array.
length]
1227 mov ebx, DWORD [rdi
+ Array.
length]
1234 je .equal
; Both zero length
1241 ; get next character
1247 ; this character is equal
1251 jnz .compare_loop
; Next character
1257 .
rsi_greater: ; rsi > rdi
1261 .
rdi_greater: ; rdi > rsi
1269 ;; ------------------------------------------------------------
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
1277 mov [rax
], BYTE (block_cons
+ container_map
+ content_empty
)
1278 mov [rax
+ Cons.typecdr
], BYTE content_nil
1285 ;; Returns: new map in RAX
1288 ;; RAX, RBX, RCX, R13, R14, R15
1294 mov r15
, rax
; start of new map
1298 mov rcx
, [rsi
+ Cons.car
]
1299 mov [rax
], BYTE bl ; copy type
1300 mov [rax
+ Cons.car
], rcx
; copy value
1302 and bl, content_mask
1303 cmp bl, content_pointer
1306 ; A pointer in CAR. Increase reference count
1307 mov bx, WORD [rcx
+ Cons.refcount
]
1309 mov [rcx
+ Cons.refcount
], WORD bx
1315 ; R13 contains last Cons
1316 mov [r13
+ Cons.typecdr
], BYTE content_pointer
1317 mov [r13
+ Cons.cdr
], rax
1321 ; Check if there's another Cons
1322 mov bl, BYTE [rsi
+ Cons.typecdr
]
1323 cmp bl, content_pointer
1326 mov rsi
, [rsi
+ Cons.cdr
] ; next
1335 ;; Add to map. Input is a list with an even number of values
1336 ;; as (key, value, key, value, ...)
1339 ;; RSI - Map to append to. This is not modified
1340 ;; RDI - List to add to the map
1347 ; Check type of input
1350 and ch, block_mask
+ container_mask
1351 cmp ch, block_cons
+ container_map
1355 and cl, block_mask
+ container_mask
1356 cmp cl, block_cons
+ container_list
1359 xor r8
, r8
; Zero r8
1362 ; Copy input list, changing container type
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
1372 cmp cl, (block_cons
+ container_map
+ content_pointer
)
1373 jne .copy_not_pointer
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
1379 mov [rcx
+ Cons.refcount
], WORD bx
1383 ; Check if this is the first object
1386 mov r8
, rax
; Save start of map to R8
1387 mov r9
, rax
; Last cons in R9
1392 mov [r9
+Cons.cdr
], rax
1393 mov [r9
+Cons.typecdr
], BYTE content_pointer
1395 ; Put new Cons in R9 as the latest in the list
1399 ; Check if we've reached the end
1400 mov cl, BYTE [rdi
+ Cons.typecdr
]
1404 ; Not yet. Get next Cons and keep going
1405 mov rdi
, [rdi
+ Cons.cdr
]
1409 ; Start of map in r8, end in r9
1411 ; Check if the original map is empty
1413 and cl, content_mask
1414 cmp cl, content_empty
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.
1422 mov [r9
+ Cons.cdr
], rsi
1423 mov [r9
+ Cons.typecdr
], BYTE content_pointer
1425 ; Increment reference count
1426 mov bx, WORD [rsi
+ Cons.refcount
]
1428 mov [rsi
+ Cons.refcount
], WORD bx
1437 mov [rax
], BYTE maltype_nil
1438 mov [rax
+ Cons.typecdr
], BYTE content_nil
1441 ;; Find a key in a map
1443 ;; Inputs: RSI - map [ Modified ]
1444 ;; RDI - key [ Modified ]
1446 ;; Outputs: RAX - Cons object containing value in CAR
1448 ;; Modifies registers:
1449 ;; RBX [compare_objects, alloc_cons]
1450 ;; RCX [compare_objects]
1453 ;; If value is found then the Zero Flag is set
1456 ;; {a 1 b 2} find a -> {1 b 2}
1457 ;; {1 2 3 4} find a -> {4}
1460 cmp al, maltype_empty_map
1464 ; compare RSI and RDI, ignoring differences in container
1467 call compare_objects
1471 ; rax is now zero if objects are equal
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
1480 mov rsi
, [rsi
+ Cons.cdr
] ; Get value
1481 mov al, [rsi
+ Cons.typecdr
]
1482 cmp al, content_pointer
1485 mov rsi
, [rsi
+ Cons.cdr
] ; Get next key
1487 jmp .map_loop
; Test next key
1492 or ah, 64 ; set zero flag
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
1500 mov rsi
, [rsi
+ Cons.cdr
]
1502 ; ; increment reference count
1503 ; mov ax, WORD [rsi + Cons.refcount]
1505 ; mov [rsi + Cons.refcount], WORD ax
1506 ; Put address in rax
1512 and ah, 255-64 ; clear zero flag
1516 ; increment reference count
1517 ; mov ax, WORD [rsi + Cons.refcount]
1519 ; mov [rsi + Cons.refcount], WORD ax
1520 ; Put address in rax
1528 and ah, 255-64 ; clear zero flag
1533 mov [rax
], BYTE maltype_nil
1534 mov [rax
+ Cons.typecdr
], BYTE content_nil
1539 ;; Sets a key-value pair in a map
1541 ;; Inputs: RSI - map [not modified]
1542 ;; RDI - key [not modified]
1543 ;; RCX - value [not modified]
1545 ;; If references are added to key or value,
1546 ;; then reference counts are incremented.
1548 ;; Modifies registers:
1553 ; Save inputs in less volatile registers
1556 mov r10
, rcx
; value
1558 ; Find the key, to see if it already exists in the map
1559 call map_find
; Cons object in RAX
1562 ; Key not in map. RAX should be address of the last
1563 ; value in the map, or empty
1565 cmp bl, maltype_empty_map
1570 call alloc_cons
; New Cons in rax
1571 pop rbx
; Last Cons in map
1574 mov [rbx
+ Cons.typecdr
], BYTE content_pointer
1575 mov [rbx
+ Cons.cdr
], rax
1576 jmp .set_key
; Put key into rax
1579 ; Key already in map, so replace value
1582 ; check type of value already there
1584 and bl, content_mask
1585 cmp bl, content_pointer
1586 jne .set_value
; Not a pointer, just overwrite
1588 ; A pointer, so need to release
1589 mov rsi
, [rax
+ Cons.car
] ; Address of object
1594 jmp .set_value
; put value into Cons
1597 ; Put key (R9) in RAX
1599 ; Check the type of object
1603 jnz .set_key_pointer
; Array, so point to it
1605 ; Here a Cons object
1607 and bh, container_mask
1608 cmp bh, container_value
1609 jne .set_key_pointer
; Not a simple value, so point to it
1612 mov rcx
, [r9
+ Cons.car
]
1613 mov [rax
+ Cons.car
], rcx
1616 and bl, content_mask
1617 or bl, (block_cons
+ container_map
)
1623 ; The key is a pointer
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
]
1630 mov [r9
+ Cons.refcount
], bx
1631 ; fall through to .set_key_done
1634 ; Key in RAX. allocate and append a Cons for the value
1636 call alloc_cons
; value Cons in rax
1639 mov [rbx
+ Cons.typecdr
], BYTE content_pointer
1640 mov [rbx
+ Cons.cdr
], rax
1642 ; fall through to .set_value
1644 ; --------------------------------
1646 ; Set the value into the Cons at [rax]
1648 ; Check the type of object
1652 jnz .set_value_pointer
; Array, so point to it
1654 ; Here a Cons object
1656 and bh, container_mask
1657 cmp bh, container_value
1658 jne .set_value_pointer
; Not a simple value, so point to it
1660 mov rcx
, [r10
+ Cons.car
]
1661 mov [rax
+ Cons.car
], rcx
1664 and bl, content_mask
1665 or bl, (block_cons
+ container_map
)
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
]
1676 mov [r10
+ Cons.refcount
], bx
1677 ; fall through to .finished
1686 ;; Get a value from a map, incrementing the reference count
1687 ;; of the object returned
1689 ;; Inputs: RSI - map
1692 ;; Returns: If found, Zero Flag is set and address in RAX
1693 ;; If not found, Zero Flag cleared
1695 ;; Modifies registers:
1706 call map_find
; Cons object in RAX
1715 and ah, 255-64 ; clear zero flag
1722 ; Check if the object in RAX is a value or pointer
1724 and bl, content_mask
1725 cmp bl, content_pointer
1732 call alloc_cons
; cons in rax
1733 pop rbx
; content type in bl
1734 pop rcx
; Object to copy
1736 add bl, block_cons
+ container_value
1737 mov [rax
], BYTE bl ; set type
1738 mov [rax
+ Cons.typecdr
], BYTE content_nil
1741 mov rbx
, [rcx
+ Cons.car
]
1742 mov [rax
+ Cons.car
], rbx
1747 ; A pointer, so get the address
1748 mov rax
, [rax
+ Cons.car
]
1750 ; increment reference count
1751 mov bx, WORD [rax
+ Cons.refcount
]
1753 mov [rax
+ Cons.refcount
], bx
1755 ; Fall through to .finished_found
1762 or ah, 64 ; set zero flag
1767 ;; Get a list of keys
1769 ;; Input: Map in RSI
1771 ;; Returns: List in RAX
1773 ;; Modifies registers:
1782 cmp al, maltype_empty_map
1785 and al, container_mask
1786 cmp al, container_map
1787 jne .empty_map
; error
1789 xor r8
, r8
; Return list
1791 ; Take the current value
1793 ; Create a new Cons for this key
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
1802 and cl, content_mask
1803 cmp cl, content_pointer
1806 ; A pointer, so increment reference count
1807 mov cx, WORD [rbx
+ Cons.refcount
]
1809 mov [rbx
+ Cons.refcount
], WORD cx
1816 mov [r9
+ Cons.typecdr
], BYTE content_pointer
1817 mov [r9
+ Cons.cdr
], rax
1821 ; First key, so put into r8
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
]
1832 mov al, BYTE [rsi
+ Cons.typecdr
]
1833 cmp al, content_pointer
1835 mov rsi
, [rsi
+ Cons.cdr
]
1838 ; Finished, return the list
1845 mov [rax
], BYTE maltype_empty_list
1848 ;; Get a list of values
1850 ;; Input: Map in RSI
1852 ;; Returns: List in RAX
1854 ;; Modifies registers:
1863 cmp al, maltype_empty_map
1866 and al, container_mask
1867 cmp al, container_map
1868 jne .empty_map
; error
1870 xor r8
, r8
; Return list
1873 ; Here should have a key in RSI
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
1880 mov rsi
, [rsi
+ Cons.cdr
] ; Now have value in RSI
1882 ; Create a new Cons for this value
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
1891 and cl, content_mask
1892 cmp cl, content_pointer
1895 ; A pointer, so increment reference count
1896 mov cx, WORD [rbx
+ Cons.refcount
]
1898 mov [rbx
+ Cons.refcount
], WORD cx
1905 mov [r9
+ Cons.typecdr
], BYTE content_pointer
1906 mov [r9
+ Cons.cdr
], rax
1910 ; First key, so put into r8
1915 mov al, BYTE [rsi
+ Cons.typecdr
]
1916 cmp al, content_pointer
1918 mov rsi
, [rsi
+ Cons.cdr
]
1921 ; Finished, return the list
1928 mov [rax
], BYTE maltype_empty_list
1932 ;; ------------------------------------------------------------
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
1942 ;; ( addr meta env binds body )
1946 ;; Address of native function in RSI
1947 ;; returns Function object in RAX
1949 call alloc_cons
; for meta
1950 mov [rax
], BYTE maltype_nil
1953 call alloc_cons
; For function address
1954 mov [rax
], BYTE (block_cons
+ container_function
+ content_function
)
1955 mov [rax
+ Cons.car
], rsi
1957 mov [rax
+ Cons.typecdr
], BYTE content_pointer
1959 mov [rax
+ Cons.cdr
], rbx