Compare commits
10 Commits
8b2192a0b6
...
30bed795fd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30bed795fd | ||
|
|
a408ccacb9 | ||
|
|
a7f14c8f58 | ||
|
|
7a5eee932a | ||
|
|
74c5746bff | ||
|
|
edf90780af | ||
|
|
ebdb1a948d | ||
|
|
2b1e7a49d2 | ||
|
|
6014620baa | ||
|
|
3a09beb582 |
30
.clang-format
Normal file
30
.clang-format
Normal file
@@ -0,0 +1,30 @@
|
||||
# .clang-format -*- mode: yaml; lexical-binding: t; -*-
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignConsecutiveBitFields: true
|
||||
AlignConsecutiveMacros: true
|
||||
AlignEscapedNewlines: true
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BasedOnStyle: LLVM
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterEnum: true
|
||||
AfterStruct: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterClass: true
|
||||
AfterUnion: true
|
||||
AfterControlStatement: true
|
||||
AfterExternBlock: true
|
||||
BeforeLambdaBody: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
SplitEmptyFunction: true
|
||||
IndentBraces: false
|
||||
ColumnLimit: 80
|
||||
IndentWidth: 2
|
||||
NamespaceIndentation: All
|
||||
@@ -1,7 +1,7 @@
|
||||
;;; Directory Local Variables -*- no-byte-compile: t -*-
|
||||
;;; For more information see (info "(emacs) Directory Variables")
|
||||
|
||||
((nil . ((+license/license-choice . "GPLv2")
|
||||
((nil . ((+license/license-choice . "GNU General Public License Version 2")
|
||||
(compile-command . "make all VERBOSE=2 RELEASE=0")))
|
||||
(c-mode . ((mode . clang-format)
|
||||
(eval . (eglot-ensure)))))
|
||||
|
||||
13
lib/base.c
13
lib/base.c
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-10-26
|
||||
* Author: Aryadev Chavali
|
||||
|
||||
78
lib/base.h
78
lib/base.h
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
@@ -28,6 +27,16 @@
|
||||
#define TERM_RED "\033[31m"
|
||||
#define TERM_RESET "\033[0m"
|
||||
|
||||
#define MESSAGE(FILE, COLOUR, NAME, FORMAT, ...) \
|
||||
fprintf(FILE, "[" COLOUR "%s" TERM_RESET "]: " FORMAT, NAME, __VA_ARGS__)
|
||||
|
||||
#define INFO(NAME, FORMAT, ...) \
|
||||
MESSAGE(stdout, TERM_YELLOW, NAME, FORMAT, __VA_ARGS__)
|
||||
#define FAIL(NAME, FORMAT, ...) \
|
||||
MESSAGE(stderr, TERM_RED, NAME, FORMAT, __VA_ARGS__)
|
||||
#define SUCCESS(NAME, FORMAT, ...) \
|
||||
MESSAGE(stdout, TERM_GREEN, NAME, FORMAT, __VA_ARGS__)
|
||||
|
||||
// Flags for program behaviour (usually related to printing)
|
||||
#ifndef VERBOSE
|
||||
#define VERBOSE 0
|
||||
@@ -92,8 +101,7 @@ typedef union
|
||||
} data_t;
|
||||
|
||||
/**
|
||||
@brief Enum of type tags for the data_t structure to provide
|
||||
context.
|
||||
@brief Enum of type tags for the data_t structure to provide context.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
@@ -117,11 +125,10 @@ static const hword_t __i = 0xFFFF0000;
|
||||
#endif
|
||||
|
||||
/**
|
||||
@brief Safely subtract SUB from W, where both are words (64 bit
|
||||
integers).
|
||||
@brief Safely subtract SUB from W, where both are words (64 bit integers).
|
||||
|
||||
@details In case of underflow (i.e. where W - SUB < 0) returns 0
|
||||
instead of the underflowed result.
|
||||
@details In case of underflow (i.e. where W - SUB < 0) returns 0 instead of
|
||||
the underflowed result.
|
||||
*/
|
||||
#define WORD_SAFE_SUB(W, SUB) ((W) > (SUB) ? ((W) - (SUB)) : 0)
|
||||
|
||||
@@ -142,17 +149,15 @@ static const hword_t __i = 0xFFFF0000;
|
||||
/**
|
||||
@brief Return the Nth half word of WORD.
|
||||
|
||||
@details N should range from 0 to 1 as there are 2 half words in a
|
||||
word
|
||||
@details N should range from 0 to 1 as there are 2 half words in a word
|
||||
*/
|
||||
#define WORD_NTH_HWORD(WORD, N) (((WORD) >> ((N) * 32)) & 0xFFFFFFFF)
|
||||
|
||||
/**
|
||||
@brief Convert a buffer of bytes to a short
|
||||
|
||||
@details It is assumed that the buffer of bytes are in virtual
|
||||
machine byte code format (little endian) and that they are at least
|
||||
SHORT_SIZE in size.
|
||||
@details It is assumed that the buffer of bytes are in virtual machine byte
|
||||
code format (little endian) and that they are at least SHORT_SIZE in size.
|
||||
*/
|
||||
short_t convert_bytes_to_short(const byte_t *buffer);
|
||||
|
||||
@@ -162,17 +167,16 @@ short_t convert_bytes_to_short(const byte_t *buffer);
|
||||
|
||||
@param s: Short to convert
|
||||
|
||||
@param buffer: Buffer to store into. It is assumed that the buffer
|
||||
has at least SHORT_SIZE space.
|
||||
@param buffer: Buffer to store into. It is assumed that the buffer has at
|
||||
least SHORT_SIZE space.
|
||||
*/
|
||||
void convert_short_to_bytes(const short_t s, byte_t *buffer);
|
||||
|
||||
/**
|
||||
@brief Convert a buffer of bytes to a half word.
|
||||
|
||||
@details It is assumed that the buffer of bytes are in virtual
|
||||
machine byte code format (little endian) and that they are at least
|
||||
HWORD_SIZE in size.
|
||||
@details It is assumed that the buffer of bytes are in virtual machine byte
|
||||
code format (little endian) and that they are at least HWORD_SIZE in size.
|
||||
*/
|
||||
hword_t convert_bytes_to_hword(const byte_t *buffer);
|
||||
|
||||
@@ -182,36 +186,34 @@ hword_t convert_bytes_to_hword(const byte_t *buffer);
|
||||
|
||||
@param h: Half word to convert
|
||||
|
||||
@param buffer: Buffer to store into. It is assumed that the buffer
|
||||
has at least HWORD_SIZE space.
|
||||
@param buffer: Buffer to store into. It is assumed that the buffer has at
|
||||
least HWORD_SIZE space.
|
||||
*/
|
||||
void convert_hword_to_bytes(const hword_t h, byte_t *buffer);
|
||||
|
||||
/**
|
||||
@brief Convert a buffer of bytes to a word.
|
||||
|
||||
@details It is assumed that the buffer of bytes are in virtual
|
||||
machine byte code format (little endian) and that they are at least
|
||||
WORD_SIZE in size.
|
||||
@details It is assumed that the buffer of bytes are in virtual machine byte
|
||||
code format (little endian) and that they are at least WORD_SIZE in size.
|
||||
*/
|
||||
word_t convert_bytes_to_word(const byte_t *);
|
||||
|
||||
/**
|
||||
@brief Convert a word into a VM byte code format bytes (little
|
||||
endian)
|
||||
@brief Convert a word into a VM byte code format bytes (little endian)
|
||||
|
||||
@param w: Word to convert
|
||||
|
||||
@param buffer: Buffer to store into. It is assumed that the buffer
|
||||
has at least WORD_SIZE space.
|
||||
@param buffer: Buffer to store into. It is assumed that the buffer has at
|
||||
least WORD_SIZE space.
|
||||
*/
|
||||
void convert_word_to_bytes(const word_t w, byte_t *buffer);
|
||||
|
||||
/**
|
||||
@brief Swap the ordering of bytes within an short
|
||||
|
||||
@details The ordering of the bytes in the short are reversed (2
|
||||
bytes in a short).
|
||||
@details The ordering of the bytes in the short are reversed (2 bytes in a
|
||||
short).
|
||||
|
||||
@param s: short to swap
|
||||
*/
|
||||
@@ -220,8 +222,8 @@ short_t short_byteswap(const short_t s);
|
||||
/**
|
||||
@brief Swap the ordering of bytes within an half word
|
||||
|
||||
@details The ordering of the bytes in the half word are reversed (4
|
||||
bytes in a half word).
|
||||
@details The ordering of the bytes in the half word are reversed (4 bytes in
|
||||
a half word).
|
||||
|
||||
@param h: Half word to swap
|
||||
*/
|
||||
@@ -230,8 +232,8 @@ hword_t hword_byteswap(const hword_t h);
|
||||
/**
|
||||
@brief Swap the ordering of bytes within an word
|
||||
|
||||
@details The ordering of the bytes in the word are reversed (8
|
||||
bytes in a word).
|
||||
@details The ordering of the bytes in the word are reversed (8 bytes in a
|
||||
word).
|
||||
|
||||
@param w: Word to swap
|
||||
*/
|
||||
|
||||
13
lib/darr.c
13
lib/darr.c
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
|
||||
76
lib/darr.h
76
lib/darr.h
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
@@ -43,10 +42,10 @@ typedef struct
|
||||
/**
|
||||
@brief Get the `IND`th item of type `TYPE` in `DARR_DATA`
|
||||
|
||||
@details Cast `DARR_DATA` to `TYPE`, taking the `IND`th item.
|
||||
NOTE: This is unsafe as bound checks are not done i.e. if
|
||||
`DARR_DATA` has at least space for `IND` * sizeof(`TYPE`) items.
|
||||
It is presumed the caller will check themselves.
|
||||
@details Cast `DARR_DATA` to `TYPE`, taking the `IND`th item. NOTE: This is
|
||||
unsafe as bound checks are not done i.e. if `DARR_DATA` has at least space
|
||||
for `IND` * sizeof(`TYPE`) items. It is presumed the caller will check
|
||||
themselves.
|
||||
|
||||
@param[TYPE] Type to cast internal byte array
|
||||
@param[DARR_DATA] Byte array of darr
|
||||
@@ -59,24 +58,23 @@ typedef struct
|
||||
/**
|
||||
@brief Initialise a dynamic array `darr` with n bytes of space.
|
||||
|
||||
@details All properties of `darr` are initialised. `darr`.used is
|
||||
set to 0, `darr`.available is set to `n` and `darr`.data is set to
|
||||
a pointer of `n` bytes. NOTE: If `n` = 0 then it is set to
|
||||
DARR_DEFAULT_SIZE
|
||||
@details All properties of `darr` are initialised. `darr`.used is set to 0,
|
||||
`darr`.available is set to `n` and `darr`.data is set to a pointer of `n`
|
||||
bytes. NOTE: If `n` = 0 then it is set to DARR_DEFAULT_SIZE
|
||||
|
||||
@param[darr] Pointer to darr_t object to initialise
|
||||
@param[n] Number of bytes to allocate. If equal to 0 then
|
||||
considered treated as DARR_DEFAULT_SIZE
|
||||
@param[n] Number of bytes to allocate. If equal to 0 then considered treated
|
||||
as DARR_DEFAULT_SIZE
|
||||
*/
|
||||
void darr_init(darr_t *darr, size_t n);
|
||||
|
||||
/**
|
||||
@brief Ensure a dynamic array has at least n bytes of space free.
|
||||
|
||||
@details If `darr` has n or more bytes free, nothing occurs.
|
||||
Otherwise, the byte array in `darr` is reallocated such that it has
|
||||
at least `n` bytes of free space. NOTE: `darr` has at least `n`
|
||||
bytes free if and only if `darr`.used + `n` <= `darr`.available
|
||||
@details If `darr` has n or more bytes free, nothing occurs. Otherwise, the
|
||||
byte array in `darr` is reallocated such that it has at least `n` bytes of
|
||||
free space. NOTE: `darr` has at least `n` bytes free if and only if
|
||||
`darr`.used + `n` <= `darr`.available
|
||||
|
||||
@param[darr] Dynamic array to check
|
||||
@param[n] Number of bytes
|
||||
@@ -86,10 +84,9 @@ void darr_ensure_capacity(darr_t *darr, size_t n);
|
||||
/**
|
||||
@brief Append a byte to a dynamic array.
|
||||
|
||||
@details Append a byte to the end of the byte buffer in a dyamic
|
||||
array. If the dynamic array doesn't have enough free space to fit
|
||||
the byte, it will reallocate to ensure it can fit it in via
|
||||
darr_ensure_capacity().
|
||||
@details Append a byte to the end of the byte buffer in a dyamic array. If
|
||||
the dynamic array doesn't have enough free space to fit the byte, it will
|
||||
reallocate to ensure it can fit it in via darr_ensure_capacity().
|
||||
|
||||
@param[darr] Dynamic arrary to append to
|
||||
@param[b] Byte to append
|
||||
@@ -99,10 +96,9 @@ void darr_append_byte(darr_t *darr, byte_t b);
|
||||
/**
|
||||
@brief Append an array of n bytes to a dynamic array.
|
||||
|
||||
@details Append an array of bytes to the end of a byte buffer. If
|
||||
the dynamic array doesn't have enough free space to fit all n bytes
|
||||
it will reallocate to ensure it can fit it in via
|
||||
darr_ensure_capacity().
|
||||
@details Append an array of bytes to the end of a byte buffer. If the
|
||||
dynamic array doesn't have enough free space to fit all n bytes it will
|
||||
reallocate to ensure it can fit it in via darr_ensure_capacity().
|
||||
|
||||
@param[darr] Dynamic array to append to
|
||||
@param[b] Array of bytes to append
|
||||
@@ -113,9 +109,9 @@ void darr_append_bytes(darr_t *darr, byte_t *b, size_t n);
|
||||
/**
|
||||
@brief Get the nth byte of a dynamic array
|
||||
|
||||
@details Get the nth byte of the dynamic array. 0 based. NOTE: If
|
||||
the dynamic array has less than n bytes used, it will return 0 as a
|
||||
default value, so this is a safe alternative to DARR_AT().
|
||||
@details Get the nth byte of the dynamic array. 0 based. NOTE: If the
|
||||
dynamic array has less than n bytes used, it will return 0 as a default
|
||||
value, so this is a safe alternative to DARR_AT().
|
||||
|
||||
@param[darr] Dynamic array to index
|
||||
@param[n] Index to get byte at
|
||||
@@ -127,9 +123,9 @@ byte_t *darr_at(darr_t *darr, size_t n);
|
||||
/**
|
||||
@brief Write the bytes of a dynamic array to a file pointer
|
||||
|
||||
@details Given a dynamic array and a file pointer, write the
|
||||
internal buffer of bytes to the file pointer. NOTE: The file
|
||||
pointer is assumed to be open and suitable for writing.
|
||||
@details Given a dynamic array and a file pointer, write the internal buffer
|
||||
of bytes to the file pointer. NOTE: The file pointer is assumed to be open
|
||||
and suitable for writing.
|
||||
|
||||
@param[darr] Dynamic array to write
|
||||
@param[fp] File pointer to write on
|
||||
@@ -139,14 +135,14 @@ void darr_write_file(darr_t *darr, FILE *fp);
|
||||
/**
|
||||
@brief Read a file pointer in its entirety into a dynamic array
|
||||
|
||||
@details Read a file pointer as a buffer of bytes then return that
|
||||
buffer wrapped in a darr_t structure. NOTE: the file pointer is
|
||||
assumed to be open and suitable for reading.
|
||||
@details Read a file pointer as a buffer of bytes then return that buffer
|
||||
wrapped in a darr_t structure. NOTE: the file pointer is assumed to be open
|
||||
and suitable for reading.
|
||||
|
||||
@param[fp]: File pointer to read
|
||||
|
||||
@return Dynamic array structure with available set to the size of
|
||||
the `buffer` read and `data` set to the buffer of bytes.
|
||||
@return Dynamic array structure with available set to the size of the
|
||||
`buffer` read and `data` set to the buffer of bytes.
|
||||
*/
|
||||
darr_t darr_read_file(FILE *fp);
|
||||
|
||||
|
||||
16
lib/heap.c
16
lib/heap.c
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-11-01
|
||||
* Author: Aryadev Chavali
|
||||
@@ -60,8 +59,7 @@ bool heap_free(heap_t *heap, page_t *page)
|
||||
if (cur == page)
|
||||
{
|
||||
page_delete(cur);
|
||||
// TODO: When does this fragmentation become a performance
|
||||
// issue?
|
||||
// TODO: When does this fragmentation become a performance issue?
|
||||
DARR_AT(page_t *, heap->page_vec.data, i) = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
47
lib/heap.h
47
lib/heap.h
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-11-01
|
||||
* Author: Aryadev Chavali
|
||||
@@ -27,8 +26,8 @@
|
||||
/**
|
||||
@brief Some fixed portion of bytes allocated on the heap.
|
||||
|
||||
@details A fixed allocation of bytes. Cannot be resized nor can it
|
||||
be stack allocated (the usual way) due to flexible array attached.
|
||||
@details A fixed allocation of bytes. Cannot be resized nor can it be stack
|
||||
allocated (the usual way) due to flexible array attached.
|
||||
|
||||
@prop[next] Next page in the linked list
|
||||
@prop[available] Available number of bytes in page
|
||||
@@ -43,9 +42,8 @@ typedef struct Page
|
||||
/**
|
||||
@brief Allocate a new page on the heap with the given properties.
|
||||
|
||||
@details Allocates a new page using malloc with the given size and
|
||||
pointer to next page. NOTE: all memory is 0 initialised by
|
||||
default.
|
||||
@details Allocates a new page using malloc with the given size and pointer to
|
||||
next page. NOTE: all memory is 0 initialised by default.
|
||||
|
||||
@param[max] Maximum available memory in page
|
||||
*/
|
||||
@@ -54,9 +52,8 @@ page_t *page_create(size_t max);
|
||||
/**
|
||||
@brief Delete a page, freeing its memory
|
||||
|
||||
@details Free's the memory associated with the page via free().
|
||||
NOTE: any pointers to the page's memory are considered invalid once
|
||||
this is called.
|
||||
@details Free's the memory associated with the page via free(). NOTE: any
|
||||
pointers to the page's memory are considered invalid once this is called.
|
||||
|
||||
@param[page] Page to delete
|
||||
*/
|
||||
@@ -66,8 +63,8 @@ void page_delete(page_t *page);
|
||||
@brief A collection of pages through which generic allocations can
|
||||
occur.
|
||||
|
||||
@details Collection of pages maintained through a vector of
|
||||
pointers to pages.
|
||||
@details Collection of pages maintained through a vector of pointers to
|
||||
pages.
|
||||
|
||||
@prop[page_vec] Vector of pages
|
||||
*/
|
||||
@@ -81,9 +78,8 @@ typedef struct
|
||||
/**
|
||||
@brief Instantiate a new heap structure
|
||||
|
||||
@details Initialises the heap structure given. No heap allocation
|
||||
occurs here until a new page is created, so this may be called
|
||||
safely.
|
||||
@details Initialises the heap structure given. No heap allocation occurs
|
||||
here until a new page is created, so this may be called safely.
|
||||
|
||||
@param[heap] Pointer to heap to initialise
|
||||
*/
|
||||
@@ -92,8 +88,8 @@ void heap_create(heap_t *heap);
|
||||
/**
|
||||
@brief Allocate a new page on the heap
|
||||
|
||||
@details Creates and joins a new page onto the linked list
|
||||
maintained by the heap. heap.end is set to this new page.
|
||||
@details Creates and joins a new page onto the linked list maintained by the
|
||||
heap. heap.end is set to this new page.
|
||||
|
||||
@param[heap] Heap to create a new page on
|
||||
@param[size] Size of page to allocate
|
||||
@@ -105,10 +101,9 @@ page_t *heap_allocate(heap_t *heap, size_t size);
|
||||
/**
|
||||
@brief Free a page of memory from the heap
|
||||
|
||||
@details The page given is removed from the linked list of pages
|
||||
then freed from the heap via page_delete(). If the page does not
|
||||
belong to this heap (O(heap.pages) time) then false is returned,
|
||||
otherwise true.
|
||||
@details The page given is removed from the linked list of pages then freed
|
||||
from the heap via page_delete(). If the page does not belong to this heap
|
||||
(O(heap.pages) time) then false is returned, otherwise true.
|
||||
|
||||
@param[heap] Heap to free page from
|
||||
@param[page] Page to delete
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2024-04-28
|
||||
* Author: Aryadev Chavali
|
||||
|
||||
62
lib/inst.c
62
lib/inst.c
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
@@ -77,14 +76,6 @@ const char *opcode_as_cstr(opcode_t code)
|
||||
return "MALLOC_HWORD";
|
||||
case OP_MALLOC_WORD:
|
||||
return "MALLOC_WORD";
|
||||
case OP_MALLOC_STACK_BYTE:
|
||||
return "MALLOC_STACK_BYTE";
|
||||
case OP_MALLOC_STACK_SHORT:
|
||||
return "MALLOC_STACK_SHORT";
|
||||
case OP_MALLOC_STACK_HWORD:
|
||||
return "MALLOC_STACK_HWORD";
|
||||
case OP_MALLOC_STACK_WORD:
|
||||
return "MALLOC_STACK_WORD";
|
||||
case OP_MSET_BYTE:
|
||||
return "MSET_BYTE";
|
||||
case OP_MSET_SHORT:
|
||||
@@ -93,14 +84,6 @@ const char *opcode_as_cstr(opcode_t code)
|
||||
return "MSET_HWORD";
|
||||
case OP_MSET_WORD:
|
||||
return "MSET_WORD";
|
||||
case OP_MSET_STACK_BYTE:
|
||||
return "MSET_STACK_BYTE";
|
||||
case OP_MSET_STACK_SHORT:
|
||||
return "MSET_STACK_SHORT";
|
||||
case OP_MSET_STACK_HWORD:
|
||||
return "MSET_STACK_HWORD";
|
||||
case OP_MSET_STACK_WORD:
|
||||
return "MSET_STACK_WORD";
|
||||
case OP_MGET_BYTE:
|
||||
return "MGET_BYTE";
|
||||
case OP_MGET_SHORT:
|
||||
@@ -109,14 +92,6 @@ const char *opcode_as_cstr(opcode_t code)
|
||||
return "MGET_HWORD";
|
||||
case OP_MGET_WORD:
|
||||
return "MGET_WORD";
|
||||
case OP_MGET_STACK_BYTE:
|
||||
return "MGET_STACK_BYTE";
|
||||
case OP_MGET_STACK_SHORT:
|
||||
return "MGET_STACK_SHORT";
|
||||
case OP_MGET_STACK_HWORD:
|
||||
return "MGET_STACK_HWORD";
|
||||
case OP_MGET_STACK_WORD:
|
||||
return "MGET_STACK_WORD";
|
||||
case OP_MDELETE:
|
||||
return "MDELETE";
|
||||
case OP_MSIZE:
|
||||
@@ -267,8 +242,6 @@ const char *opcode_as_cstr(opcode_t code)
|
||||
return "PRINT_SWORD";
|
||||
case OP_JUMP_ABS:
|
||||
return "JUMP_ABS";
|
||||
case OP_JUMP_STACK:
|
||||
return "JUMP_STACK";
|
||||
case OP_JUMP_IF_BYTE:
|
||||
return "JUMP_IF_BYTE";
|
||||
case OP_JUMP_IF_SHORT:
|
||||
@@ -279,8 +252,6 @@ const char *opcode_as_cstr(opcode_t code)
|
||||
return "JUMP_IF_WORD";
|
||||
case OP_CALL:
|
||||
return "CALL";
|
||||
case OP_CALL_STACK:
|
||||
return "CALL_STACK";
|
||||
case OP_RET:
|
||||
return "RET";
|
||||
case NUMBER_OF_OPCODES:
|
||||
@@ -312,7 +283,7 @@ void data_print(data_t datum, data_type_t type, FILE *fp)
|
||||
|
||||
void inst_print(inst_t instruction, FILE *fp)
|
||||
{
|
||||
static_assert(NUMBER_OF_OPCODES == 129, "inst_print: Out of date");
|
||||
static_assert(NUMBER_OF_OPCODES == 115, "inst_print: Out of date");
|
||||
fprintf(fp, "%s(", opcode_as_cstr(instruction.opcode));
|
||||
if (UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_PUSH))
|
||||
{
|
||||
@@ -326,10 +297,7 @@ void inst_print(inst_t instruction, FILE *fp)
|
||||
fprintf(fp, "reg=0x");
|
||||
data_print(instruction.operand, DATA_TYPE_BYTE, fp);
|
||||
}
|
||||
else if (UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_DUP) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MALLOC) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MSET) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MGET))
|
||||
else if (UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_DUP))
|
||||
{
|
||||
fprintf(fp, "n=0x%lX", instruction.operand.as_word);
|
||||
}
|
||||
@@ -345,7 +313,7 @@ void inst_print(inst_t instruction, FILE *fp)
|
||||
|
||||
size_t opcode_bytecode_size(opcode_t opcode)
|
||||
{
|
||||
static_assert(NUMBER_OF_OPCODES == 129, "inst_bytecode_size: Out of date");
|
||||
static_assert(NUMBER_OF_OPCODES == 115, "inst_bytecode_size: Out of date");
|
||||
size_t size = 1; // for opcode
|
||||
if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH))
|
||||
{
|
||||
@@ -370,7 +338,7 @@ size_t opcode_bytecode_size(opcode_t opcode)
|
||||
|
||||
size_t inst_write_bytecode(inst_t inst, byte_t *bytes)
|
||||
{
|
||||
static_assert(NUMBER_OF_OPCODES == 129, "inst_write_bytecode: Out of date");
|
||||
static_assert(NUMBER_OF_OPCODES == 115, "inst_write_bytecode: Out of date");
|
||||
|
||||
bytes[0] = inst.opcode;
|
||||
size_t written = 1;
|
||||
@@ -382,9 +350,6 @@ size_t inst_write_bytecode(inst_t inst, byte_t *bytes)
|
||||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_DUP) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_MOV) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_DUP) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_MALLOC) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_MSET) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_MGET) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_JUMP_IF) ||
|
||||
inst.opcode == OP_JUMP_ABS || inst.opcode == OP_CALL)
|
||||
to_append = DATA_TYPE_WORD;
|
||||
@@ -457,7 +422,7 @@ bool read_type_from_darr(byte_t *bytes, size_t size, data_type_t type,
|
||||
|
||||
int inst_read_bytecode(inst_t *ptr, byte_t *bytes, size_t size_bytes)
|
||||
{
|
||||
static_assert(NUMBER_OF_OPCODES == 129, "inst_read_bytecode: Out of date");
|
||||
static_assert(NUMBER_OF_OPCODES == 115, "inst_read_bytecode: Out of date");
|
||||
|
||||
opcode_t opcode = *(bytes++);
|
||||
if (opcode >= NUMBER_OF_OPCODES || opcode < OP_NOOP)
|
||||
@@ -472,13 +437,10 @@ int inst_read_bytecode(inst_t *ptr, byte_t *bytes, size_t size_bytes)
|
||||
if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH))
|
||||
success = read_type_from_darr(bytes, size_bytes, (data_type_t)opcode,
|
||||
&inst.operand);
|
||||
// Read register (as a byte)
|
||||
// Read operand as a word
|
||||
else if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH_REGISTER) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MOV) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_DUP) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MALLOC) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MSET) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MGET) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_JUMP_IF) ||
|
||||
opcode == OP_JUMP_ABS || opcode == OP_CALL)
|
||||
success =
|
||||
|
||||
56
lib/inst.h
56
lib/inst.h
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
@@ -65,31 +64,16 @@ typedef enum
|
||||
OP_MALLOC_HWORD,
|
||||
OP_MALLOC_WORD,
|
||||
|
||||
OP_MALLOC_STACK_BYTE,
|
||||
OP_MALLOC_STACK_SHORT,
|
||||
OP_MALLOC_STACK_HWORD,
|
||||
OP_MALLOC_STACK_WORD,
|
||||
|
||||
OP_MSET_BYTE,
|
||||
OP_MSET_SHORT,
|
||||
OP_MSET_HWORD,
|
||||
OP_MSET_WORD,
|
||||
|
||||
OP_MSET_STACK_BYTE,
|
||||
OP_MSET_STACK_SHORT,
|
||||
OP_MSET_STACK_HWORD,
|
||||
OP_MSET_STACK_WORD,
|
||||
|
||||
OP_MGET_BYTE,
|
||||
OP_MGET_SHORT,
|
||||
OP_MGET_HWORD,
|
||||
OP_MGET_WORD,
|
||||
|
||||
OP_MGET_STACK_BYTE,
|
||||
OP_MGET_STACK_SHORT,
|
||||
OP_MGET_STACK_HWORD,
|
||||
OP_MGET_STACK_WORD,
|
||||
|
||||
OP_MDELETE,
|
||||
OP_MSIZE,
|
||||
|
||||
@@ -184,7 +168,6 @@ typedef enum
|
||||
|
||||
// Program control flow
|
||||
OP_JUMP_ABS,
|
||||
OP_JUMP_STACK,
|
||||
OP_JUMP_IF_BYTE,
|
||||
OP_JUMP_IF_SHORT,
|
||||
OP_JUMP_IF_HWORD,
|
||||
@@ -192,7 +175,6 @@ typedef enum
|
||||
|
||||
// Subroutines
|
||||
OP_CALL,
|
||||
OP_CALL_STACK,
|
||||
OP_RET,
|
||||
|
||||
// Should not be an opcode
|
||||
@@ -211,10 +193,10 @@ typedef struct
|
||||
/**
|
||||
@brief Serialise an instruction into a byte buffer
|
||||
|
||||
@details Given an instruction and a suitably sized byte buffer,
|
||||
write the bytecode for the instruction into the buffer. NOTE: This
|
||||
function does NOT check the bounds of `bytes` i.e. we assume the
|
||||
caller has created a suitably sized buffer.
|
||||
@details Given an instruction and a suitably sized byte buffer, write the
|
||||
bytecode for the instruction into the buffer. NOTE: This function does NOT
|
||||
check the bounds of `bytes` i.e. we assume the caller has created a suitably
|
||||
sized buffer.
|
||||
|
||||
@param[inst] Instruction to serialise
|
||||
@param[bytes] Buffer to write on
|
||||
@@ -234,21 +216,19 @@ typedef enum
|
||||
/**
|
||||
@brief Deserialise an instruction from a bytecode buffer
|
||||
|
||||
@details Given a buffer of bytes, deserialise an instruction,
|
||||
storing the result in the pointer given. The number of bytes read
|
||||
in the buffer is returned, which should be opcode_bytecode_size().
|
||||
NOTE: If bytes is not suitably sized for the instruction expected
|
||||
or it is not well formed i.e. not the right schema then a negative
|
||||
number is returned.
|
||||
@details Given a buffer of bytes, deserialise an instruction, storing the
|
||||
result in the pointer given. The number of bytes read in the buffer is
|
||||
returned, which should be opcode_bytecode_size(). NOTE: If bytes is not
|
||||
suitably sized for the instruction expected or it is not well formed i.e. not
|
||||
the right schema then a negative number is returned.
|
||||
|
||||
@param[inst] Pointer to instruction which will store result
|
||||
@param[bytes] Bytecode buffer to deserialise
|
||||
@param[size_bytes] Number of bytes in buffer
|
||||
|
||||
@return[int] Number of bytes read. If negative then an error
|
||||
occurred in deserialisation (either buffer was not suitably sized
|
||||
or instruction was not well formed) so any result must be
|
||||
considered invalid.
|
||||
@return[int] Number of bytes read. If negative then an error occurred in
|
||||
deserialisation (either buffer was not suitably sized or instruction was not
|
||||
well formed) so any result must be considered invalid.
|
||||
*/
|
||||
int inst_read_bytecode(inst_t *inst, byte_t *bytes, size_t size_bytes);
|
||||
|
||||
|
||||
294
spec.org
294
spec.org
@@ -3,86 +3,250 @@
|
||||
#+description: A specification of instructions for the virtual machine
|
||||
#+date: 2023-11-02
|
||||
|
||||
* WIP Data types
|
||||
There are 3 main data types of the virtual machine. They are all
|
||||
unsigned. There exist signed versions of these data types, though
|
||||
there is no difference (internally) between them. For an unsigned
|
||||
type <T> the signed version is simply S_<T>.
|
||||
* Data types
|
||||
There are 4 main data types of the virtual machine. They are all
|
||||
unsigned.
|
||||
|-------+------|
|
||||
| Name | Bits |
|
||||
|-------+------|
|
||||
| Byte | 8 |
|
||||
| Short | 16 |
|
||||
| HWord | 32 |
|
||||
| Word | 64 |
|
||||
|-------+------|
|
||||
|
||||
Generally, the abbreviations B, H and W are used for Byte, HWord and
|
||||
Word respectively. The following table shows a comparison between the
|
||||
data types where an entry (row and column) $A\times{B}$ refers to "How
|
||||
many of A can I fit in B".
|
||||
|-------+------+-------+------|
|
||||
| | Byte | Hword | Word |
|
||||
|-------+------+-------+------|
|
||||
| Byte | 1 | 4 | 8 |
|
||||
| HWord | 1/4 | 1 | 2 |
|
||||
| Word | 1/8 | 1/2 | 1 |
|
||||
|-------+------+-------+------|
|
||||
Generally, the abbreviations B, S, H and W are used for Byte, Short,
|
||||
HWord and Word respectively. The following table shows a comparison
|
||||
between the data types where an entry (row and column) $A\times{B}$
|
||||
refers to "How many of A can I fit in B".
|
||||
|-------+------+-------+-------+------|
|
||||
| | Byte | Short | HWord | Word |
|
||||
|-------+------+-------+-------+------|
|
||||
| Byte | 1 | 2 | 4 | 8 |
|
||||
| Short | 1/2 | 1 | 2 | 4 |
|
||||
| HWord | 1/4 | 1/2 | 1 | 2 |
|
||||
| Word | 1/8 | 1/4 | 1/2 | 1 |
|
||||
|-------+------+-------+-------+------|
|
||||
|
||||
These unsigned types can be trivially considered signed via 2s
|
||||
complement. The signed version of some unsigned type is abbreviated
|
||||
by prefixing the type with a =S_=. So the signed version of each type
|
||||
is S_B, S_S, S_H, S_W.
|
||||
* TODO Storage
|
||||
There are 4 forms of storage available in the virtual machine: the
|
||||
*stack*, *registers* and *heap*. The stack, registers and call stack
|
||||
are considered *fixed storage* in that they have an exact fixed
|
||||
capacity within the virtual machine. The heap, on the other hand, can
|
||||
grow dynamically as it supports user requested allocations and is thus
|
||||
considered *dynamic storage*.
|
||||
** Stack
|
||||
+ FILO data structure
|
||||
+ ~S~ in shorthand
|
||||
+ ~ptr~ represents the top of the stack at any one point during
|
||||
execution, ~0~ refers to the address for the bottom of the stack
|
||||
(aka the minimal value of ~ptr~) and ~n~ refers to the address of
|
||||
the end of the usable stack space (aka the maximal value for
|
||||
~ptr~, ~MAX_STACK~)
|
||||
** Registers
|
||||
+ constant time read/write data structure
|
||||
+ ~R~ in shorthand
|
||||
+ Reserves ~m~ bytes of space (called the ~MAX_REG~), where m must
|
||||
be a positive multiple of 8
|
||||
+ May be indexed via a pointer in one of the 4 following forms:
|
||||
+ ~b<i>~: the ith byte, where i in [0, m)
|
||||
+ ~s<i>~: the ith short, where i in [0, m/2)
|
||||
+ ~h<i>~: the ith hword, where i in [0, m/4)
|
||||
+ ~w<i>~: the ith word, where i in [0, m/8)
|
||||
+ w<i> refers to the 8 bytes between [8i, 8(i+1)), which implicitly
|
||||
refers to the:
|
||||
+ 8 byte registers {b<j> | j in [8i, 8(i + 1))}
|
||||
+ 4 short registers {s<j> | j in [4i, 4(i + 1))}
|
||||
+ 2 hword registers {h<j> | j in [2i, 2(i + 1))}
|
||||
** TODO Heap
|
||||
+ Random access storage which can be allocated into chunks
|
||||
+ ~H~ in shorthand
|
||||
** Call stack
|
||||
+ FILO data structure containing program addresses (indexes in the
|
||||
program)
|
||||
+ ~C~ in shorthand
|
||||
+ Is reserved for a very small subset of operations for control flow
|
||||
* WIP Instructions
|
||||
An instruction for the virtual machine is composed of an *opcode* and,
|
||||
potentially, an *operand*. The /opcode/ represents the behaviour of
|
||||
the instruction i.e. what _is_ the instruction. The /operand/ is an
|
||||
element of one of the /data types/ described previously.
|
||||
optionally, an *operand*. The /opcode/ represents the specific
|
||||
behaviour of the instruction i.e. what the instruction does. The
|
||||
/operand/ is an element of one of the /data types/ described
|
||||
previously which the opcode uses as part of its function. The operand
|
||||
is optional based on the opcode: certain opcodes will never require an
|
||||
operand.
|
||||
** Operations: abstracting over opcodes
|
||||
An *operation* is some generic behaviour, potentially involving data
|
||||
storage. Many operations are generic over data types i.e. they
|
||||
describe some behaviour that works for some subset of types. Opcodes
|
||||
are simply specialisations of operations over some data type. For
|
||||
example the generic behaviour of the operation ~PUSH~, which pushes
|
||||
the operand onto the stack, is specialised into the opcode
|
||||
~PUSH_WORD~, which pushes the operand, a word, onto the stack. An
|
||||
operation may, thus, describe many opcodes and each opcode is a
|
||||
specialisation of exactly one operation.
|
||||
|
||||
Some instructions do have /operands/ while others do not. The former
|
||||
type of instructions are called *UNIT* instructions while the latter
|
||||
type are called *MULTI* instructions[fn:1].
|
||||
The *order* of an operation is the number of specialisations it has
|
||||
i.e. the number of opcodes that specialise one operation.
|
||||
|
||||
All /opcodes/ (with very few exceptions[fn:2]) have two components:
|
||||
the *root* and the *type specifier*. The /root/ represents the
|
||||
general behaviour of the instruction: ~PUSH~, ~POP~, ~MOV~, etc. The
|
||||
/type specifier/ specifies what /data type/ it manipulates. A
|
||||
complete opcode will be a combination of these two e.g. ~PUSH_BYTE~,
|
||||
~POP_WORD~, etc. Some /opcodes/ may have more /type specifiers/ than
|
||||
others.
|
||||
Some operations may not be generic over data types in which case they
|
||||
are of order 1 i.e. the opcode describes the exact behaviour of only
|
||||
one operation.
|
||||
|
||||
There are only 3 possible orders for operations: 1, 4 and 8. They are
|
||||
given the names Nil, Unsigned and Signed for specialising over:
|
||||
+ No types
|
||||
+ The 4 unsigned data types described earlier
|
||||
+ The 4 unsigned data types and their signed variants as well
|
||||
** Arity
|
||||
The arity of an operation is the number of input data it takes. An
|
||||
operation can take input in two ways:
|
||||
+ From the operand, encoded in the bytecode
|
||||
+ From the stack by popping from the top
|
||||
|
||||
An operation that takes n input data from the stack pops n data from
|
||||
the stack to use as input.
|
||||
|
||||
Since there can only be at most one operand, an operation that takes
|
||||
input from the operand must have an arity of at least one.
|
||||
|
||||
Hence the arity is the sum of inputs taken from both. This can be 0,
|
||||
in which case the operation is *nullary*. An operation that takes one
|
||||
input, whether that be from the stack or operand, is *unary*. An
|
||||
operation that takes two inputs, whichever source either are from, is
|
||||
*binary*.
|
||||
** Orientation
|
||||
An operation can be considered *oriented* around a data storage if it
|
||||
only takes input from that data storage. So an operation that only
|
||||
takes input from the stack is *stack-oriented*. Or an operation that
|
||||
only takes input from the operand is *operand-oriented*.
|
||||
** Categorisation of operations
|
||||
With the notation done, we can now describe all operations that the
|
||||
virtual machine supports. Through describing all of these operations,
|
||||
including their orders and what operand they accept (if any), we can
|
||||
describe all opcodes.
|
||||
|
||||
*** Trivial nullary operations
|
||||
These are NIL order operations which are super simple to describe.
|
||||
+ =NOOP=: Doesn't do anything.
|
||||
+ =HALT=: Stops execution at point
|
||||
*** Moving data in fixed storage
|
||||
There are 5 operations that move data through fixed storage in the
|
||||
virtual machine. They are of Unsigned order, unary and
|
||||
operand-oriented.
|
||||
|
||||
|-----------------+---------------------------------------------------|
|
||||
| Name | Behaviour |
|
||||
|-----------------+---------------------------------------------------|
|
||||
| =PUSH= | Pushes operand onto stack |
|
||||
| =POP= | Pops datum off stack |
|
||||
| =PUSH_REGISTER= | Pushes datum from (operand)th register onto stack |
|
||||
| =MOV= | Moves datum off stack to the (operand)th register |
|
||||
| =DUP= | Pushes the (operand)th datum in stack onto stack |
|
||||
|-----------------+---------------------------------------------------|
|
||||
*** Using the heap
|
||||
The heap is utilised through a set of "helper" operations that safely
|
||||
abstract the underlying implementation. All of these operations are
|
||||
stack-oriented.
|
||||
|
||||
|-----------+----------------------------------------------------------+-------|
|
||||
| Name | Behaviour | Arity |
|
||||
|-----------+----------------------------------------------------------+-------|
|
||||
| =MALLOC= | Allocate n amount of data in the heap, pushing a pointer | 1 |
|
||||
| =MSET= | Pop a value, set the nth datum of data in the heap | 3 |
|
||||
| =MGET= | Push the nth datum of data in the heap onto the stack | 3 |
|
||||
| =MDELETE= | Free data in the heap | 1 |
|
||||
| =MSIZE= | Get the size of allocation in the heap | 1 |
|
||||
|-----------+----------------------------------------------------------+-------|
|
||||
|
||||
=MALLOC=, =MSET= and =MGET= are of Unsigned order. Due to unsigned
|
||||
and signed types taking the same size, they can be used for signed
|
||||
data as well.
|
||||
*** Boolean operations
|
||||
There are 5 boolean operations. They are of Unsigned order, binary
|
||||
and stack-oriented. These are:
|
||||
+ =NOT=
|
||||
+ =OR=
|
||||
+ =AND=
|
||||
+ =XOR=
|
||||
+ =EQ=
|
||||
|
||||
Though they are all of unsigned order they can be used for signed data
|
||||
trivially.
|
||||
*** Comparison operations
|
||||
There are 4 comparison operations. They are all signed operations,
|
||||
binary and stack-oriented. They are:
|
||||
+ LT: Less Than
|
||||
+ LTE: Less Than or Equal
|
||||
+ GT: Greater Than
|
||||
+ GTE: Greater Than or Equal
|
||||
|
||||
As =EQ= is an unsigned order operation and doesn't assert anything on
|
||||
the actual values, it can be used for comparing two signed inputs. It
|
||||
doesn't perform a cast when comparing and unsigned and signed input
|
||||
which may mean certain non equivalent values may be considered equal
|
||||
(e.g. =0xFAA9= is a negative number in 2s complement but a positive
|
||||
number in unsigned, considered the same under =EQ=).
|
||||
*** Mathematical operations
|
||||
There are 3 mathematical operations. They are of unsigned order,
|
||||
binary and stack-oriented. These are:
|
||||
+ PLUS
|
||||
+ SUB
|
||||
+ MULT
|
||||
|
||||
Though they are unsigned, any overflowing operation is wrapped around.
|
||||
With some thought these operations can treat unsigned data and be used
|
||||
to generate them.
|
||||
*** Control flow operations
|
||||
There are 2 control flow operations. Each perform a "jump", changing
|
||||
the point of execution to a different point in the program.
|
||||
|
||||
|--------------+----------+---------------+-------|
|
||||
| Name | Order | Orientation | Arity |
|
||||
|--------------+----------+---------------+-------|
|
||||
| =JUMP_ABS= | NIL | Operand | 1 |
|
||||
| =JUMP_IF= | UNSIGNED | Operand+Stack | 2 |
|
||||
|--------------+----------+---------------+-------|
|
||||
|
||||
+ =JUMP_ABS= interprets the operand as an absolute program address and
|
||||
sets point of execution to that address
|
||||
+ =JUMP_IF= pops a datum off the stack and compares it to 0. If true,
|
||||
the point of execution is set to the operand (interpreted as an
|
||||
absolute program address). If false, execution continues past it.
|
||||
*** Subroutine operations
|
||||
There are 2 subroutine operations. They are the only operations that
|
||||
can mutate the call stack. Through utilising reserved storage in the
|
||||
virtual machine that can only be altered through these methods, they
|
||||
abstract control flow to a higher degree than the jump operations.
|
||||
|
||||
|------------+-------------+-------|
|
||||
| Name | Orientation | Arity |
|
||||
|------------+-------------+-------|
|
||||
| CALL | Operand | 1 |
|
||||
| RET | - | 0 |
|
||||
|------------+-------------+-------|
|
||||
|
||||
The CALL* operations take a program address as input (either from the
|
||||
operand or from the stack). They push the current program address
|
||||
onto the call stack and perform a jump to the input address.
|
||||
|
||||
The RET operation pops a program address off the call stack,
|
||||
performing a jump to that address.
|
||||
|
||||
These operations allow the implementation of /subroutines/: sequences
|
||||
of code that can be self contained and generic over a variety of call
|
||||
sites i.e. can return to the address where it was called without hard
|
||||
coding the address.
|
||||
*** TODO IO
|
||||
Currently IO is really bad: the PRINT_* routines are not a nice
|
||||
abstraction over what's really happening and programs cannot take
|
||||
input from stdin.
|
||||
* TODO Bytecode format
|
||||
Bytecode files are byte sequence which encode instructions for the
|
||||
virtual machine. Any instruction (even with an operand) has one and
|
||||
only one byte sequence associated with it.
|
||||
* TODO Storage
|
||||
Two types of storage:
|
||||
+ Data stack which all core VM routines manipulate and work on (FILO)
|
||||
+ ~DS~ in shorthand, with indexing from 0 (referring to the top of the
|
||||
stack) up to n (referring to the bottom of the stack). B(DS)
|
||||
refers to the bytes in the stack (the default).
|
||||
+ Register space which is generally reserved for user space code
|
||||
i.e. other than ~mov~ no other core VM routine manipulates the
|
||||
registers
|
||||
+ ~R~ in shorthand, with indexing from 0 to $\infty$.
|
||||
* TODO Standard library
|
||||
Standard library subroutines reserve the first 16 words (128 bytes) of
|
||||
register space (W(R)[0] to W(R)[15]). The first 8 words (W(R)[0] to
|
||||
W(R)[7]) are generally considered "arguments" to the subroutine while
|
||||
the remaining 8 words (W(R)[8] to W(R)[15]) are considered additional
|
||||
space that the subroutine may access and mutate for internal purposes.
|
||||
|
||||
The stack may have additional bytes pushed, which act as the "return
|
||||
value" of the subroutine, but no bytes will be popped off (*Stack
|
||||
Preservation*).
|
||||
|
||||
If a subroutine requires more than 8 words for its arguments, then it
|
||||
will use the stack. This is the only case where the stack is mutated
|
||||
due to a subroutine call, as those arguments will always be popped off
|
||||
the stack.
|
||||
|
||||
Subroutines must always end in ~RET~. Therefore, they must always be
|
||||
called via ~CALL~, never by ~JUMP~ (which will always cause error
|
||||
prone behaviour).
|
||||
* Footnotes
|
||||
[fn:2] ~NOOP~, ~HALT~, ~MDELETE~, ~MSIZE~, ~JUMP_*~
|
||||
|
||||
[fn:1] /UNIT/ refers to the fact that the internal representation of
|
||||
these instructions are singular: two instances of the same /UNIT/
|
||||
instruction will be identical in terms of their binary. On the other
|
||||
hand, two instances of the same /MULTI/ instruction may not be
|
||||
equivalent due to the operand they take. Crucially, most if not all
|
||||
/MULTI/ instructions have different versions for each /data type/.
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2024-04-28
|
||||
* Author: Aryadev Chavali
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2024-04-28
|
||||
* Author: Aryadev Chavali
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2024-04-28
|
||||
* Author: Aryadev Chavali
|
||||
@@ -71,8 +70,8 @@ void test_lib_darr_ensure_capacity_expands(void)
|
||||
{10, 10, 10, 20},
|
||||
{50, 100, 300, 350},
|
||||
{1 << 20, 2 << 20, 2 << 20, 3 << 20},
|
||||
// When we reallocate we allocate MORE than needed (for
|
||||
// amortized constant)
|
||||
// When we reallocate we allocate MORE than needed (for amortized
|
||||
// constant)
|
||||
{1, 5, 5, 10},
|
||||
{85, 100, 40, 200},
|
||||
{4 << 20, 5 << 20, 1 << 23, 5 << 21},
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2024-04-28
|
||||
* Author: Aryadev Chavali
|
||||
@@ -25,16 +24,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MESSAGE(FILE, COLOUR, NAME, FORMAT, ...) \
|
||||
fprintf(FILE, "\t[" COLOUR "%s" TERM_RESET "]: " FORMAT, NAME, __VA_ARGS__)
|
||||
|
||||
#define INFO(NAME, FORMAT, ...) \
|
||||
MESSAGE(stdout, TERM_YELLOW, NAME, FORMAT, __VA_ARGS__)
|
||||
#define FAIL(NAME, FORMAT, ...) \
|
||||
MESSAGE(stderr, TERM_RED, NAME, FORMAT, __VA_ARGS__)
|
||||
#define SUCCESS(NAME, FORMAT, ...) \
|
||||
MESSAGE(stdout, TERM_GREEN, NAME, FORMAT, __VA_ARGS__)
|
||||
|
||||
typedef void (*test_fn)(void);
|
||||
struct Test
|
||||
{
|
||||
|
||||
147
todo.org
147
todo.org
@@ -3,6 +3,153 @@
|
||||
#+date: 2023-11-02
|
||||
#+startup: noindent
|
||||
|
||||
* TODO Completely rework opcodes
|
||||
Instead of having an opcode per type, we can implement a generic
|
||||
opcode using two operands just on bytes.
|
||||
|
||||
Instead of ~PUSH_(BYTE|SHORT|HWORD|WORD) n~ where n is the data to
|
||||
push of the precomposed type, we make a generic ~PUSH m, {n}~ where m
|
||||
is the number of bytes and {n} is the set of bytes to push.
|
||||
|
||||
In bytecode that would look like ~<OP_PUSH>|m|n1|n2|n3...|nm~.
|
||||
Opcodes are already variably sized so we may as well allow this. And
|
||||
we reduce the number of opcodes by 3.
|
||||
|
||||
Each opcode can be encoded this way, but we need to describe the
|
||||
semantics clearly.
|
||||
** Register encoding
|
||||
Firstly, registers are now only encoded by byte pointer. No short,
|
||||
half word or word pointers.
|
||||
|
||||
Since they're so easy to translate between anyway, why should the
|
||||
virtual machine do the work to handle that?
|
||||
|
||||
So a register r is the byte register at index r.
|
||||
** PUSH
|
||||
=PUSH m {n}= pushes m bytes of data, encoded by {n}.
|
||||
** POP
|
||||
=POP m= pops m bytes of data
|
||||
** PUSH_REGISTER
|
||||
=PUSH_REGISTER m r= pushes the m bytes from register space starting at
|
||||
index r.
|
||||
** MOV
|
||||
=MOV m r= moves m bytes of data off the stack into the register space,
|
||||
starting at index r.
|
||||
Easy to error check as well in one go.
|
||||
** DUP
|
||||
=DUP m= duplicates the last m bytes, pushing them onto the top of the
|
||||
stack.
|
||||
** NOT
|
||||
=NOT m= takes the last m bytes and pushes the ~NOT~ of each byte onto
|
||||
the stack in order.
|
||||
|
||||
Say the top of the stack has the m bytes {n_i} where i is from 1 to m.
|
||||
Then =NOT m= would pop those bytes then push {!n_i} onto the stack in
|
||||
the exact same order.
|
||||
** Binary boolean operators
|
||||
=<OP> m= pops the last 2m bytes off the stack and does a byte by byte
|
||||
operation, pushing the result onto the stack.
|
||||
|
||||
Say the top of the stack has m bytes {a_i} and then m bytes {b_i}.
|
||||
These would both be popped off and what would be pushed is {<OP>(a_i,
|
||||
b_i)} onto the stack in order.
|
||||
** Mathematical and comparison operations
|
||||
PLUS, SUB and MULT will now have two versions: U<OP> and <OP> for
|
||||
unsigned <OP> and signed <OP>. This allows us to deal with edge case
|
||||
2s complement arithmetic.
|
||||
|
||||
=<OP> m= pops the last 2m bytes off the stack then applies the
|
||||
operation on the two portions of bytes, considering them as signed or
|
||||
unsigned based on the OP. It then pushes that result back onto the
|
||||
stack.
|
||||
|
||||
NOTE: We can still optimise by checking if m is within some bound of
|
||||
the known types we have already (i.e. is it about the size of a short,
|
||||
or a word) then using those known types to do computations faster.
|
||||
What this provides is a generic algorithm for =m= byte arithmetic
|
||||
which is what all cool programming languages do.
|
||||
|
||||
Comparison operations can be done in basically the same way.
|
||||
** JUMP_IF
|
||||
JUMP_IF can check the truthiness of some m bytes of memory, which we
|
||||
can optimise if the m bytes are in some known bound already.
|
||||
|
||||
=JUMP_IF m= pops m bytes off the stack and examines them: if it's all
|
||||
zero then it doesn't perform the jump, but otherwise it does.
|
||||
** Shifting
|
||||
I want to really work on making shifting operators. These move the
|
||||
stack pointer without manipulating the actual data on the stack, which
|
||||
can be useful when performing an operation that pops some resource
|
||||
over and over again (i.e. =MSET='ing data from some heap allocation
|
||||
requires popping the pointer and data off the stack). Since all
|
||||
operations use the stack pointer when manipulating it (even ~POP~),
|
||||
shifting the stack pointer doesn't change their behaviour a whole lot
|
||||
but may require some extra mental work on the developer.
|
||||
+ =SHIFT_DOWN m= moves the stack pointer down m bytes. Error may
|
||||
happen if pointer is shifted further than 0
|
||||
+ =SHIFT_UP m= moves the stack pointer down m bytes. Error may
|
||||
occur if pointer shifts past the ~STACK_MAX~.
|
||||
** Memory model
|
||||
Something different will have to happen here. I have a few ideas
|
||||
around making pages and reserving "space" as a generic sense, allowing
|
||||
the virtual machine to use that space in a variety of ways regardless
|
||||
of what storage is being used for that space.
|
||||
|
||||
Essentially I want a better model which will allow me to use the stack
|
||||
as generic memory space: pointers to the stack. So a tentative API
|
||||
would be:
|
||||
+ A page is a reserved space in some storage, whether that be the heap
|
||||
or the stack. It is represented by a word which is a pointer to the
|
||||
start of it. The structure of a page in memory has a word
|
||||
representing the size of the page and a number of bytes following
|
||||
it.
|
||||
+ =RESERVE_STACK m= reserves a page of m bytes on the stack. The
|
||||
stack pointer is shifted up m+8 bytes and a pointer to the page is
|
||||
pushed onto the stack.
|
||||
+ =RESERVE_HEAP m= reserves a page of m bytes in the heap, which is a
|
||||
VM managed resource that cannot be directly accessed by the user.
|
||||
The page is pushed onto the stack.
|
||||
+ =PAGE_WRITE m= writes m bytes of memory, stored on the stack, to a
|
||||
page. The data to write and the page pointer are popped off the
|
||||
stack in that order.
|
||||
+ =PAGE_READ a b= pushes the bytes of a page between indexes [a, b)
|
||||
onto the stack. The page pointer is popped off the stack.
|
||||
+ =PAGE_REALLOC m= reallocates the page to the new size of m bytes,
|
||||
allowing for dynamic memory management. The page pointer is popped
|
||||
off the stack and a new page pointer is pushed onto the stack.
|
||||
+ If the page is a stack page, this errors out because that stack
|
||||
space will be forcibly leaked.
|
||||
+ =PAGE_FREE= returns ownership of a page back to the runtime. The
|
||||
page pointer is popped off the stack.
|
||||
+ In the case of a stack page, this does nothing but zero the space
|
||||
originally in the stack (including the first 8 bytes for the size
|
||||
of the page) which means the user must shift down and/or pop data
|
||||
to use the space effectively and avoid stack leaks.
|
||||
** I/O
|
||||
Something better needs to happen here. Perhaps writing a better
|
||||
wrapper over C file I/O such that users can open file handles and deal
|
||||
with them. Tentative API:
|
||||
+ A file handle is a word representing a pointer to it. This can
|
||||
either be the raw C pointer or an index in some abstraction such as
|
||||
a dynamic array of file pointers
|
||||
+ =FILE_OPEN m t= interprets the top m bytes of the stack as the file
|
||||
name to open. t is a byte encoding the file mode. File handle is
|
||||
pushed onto the stack.
|
||||
+ 0 -> Read
|
||||
+ 1 -> Write
|
||||
+ 2 -> Append
|
||||
+ 3 -> Read+
|
||||
+ 4 -> Write+
|
||||
+ 5 -> Append+
|
||||
+ =FILE_READ m= reads the m bytes from a file handle, pushing them
|
||||
onto the stack. File handle is popped off the stack.
|
||||
+ =FILE_WRITE m= writes the m bytes on the top of the stack to the
|
||||
file handle given. Both the bytes to write and the handle are
|
||||
stored on the stack, first the bytes then the handle.
|
||||
+ =FILE_STATUS= pushes the current position of the file onto the
|
||||
stack. File handle is popped off the stack.
|
||||
+ =FILE_CLOSE= closes and frees the file handle. File handle is
|
||||
popped off the stack.
|
||||
* TODO Rework heap to use one allocation
|
||||
The current approach for the heap is so:
|
||||
+ Per call to ~malloc~, allocate a new ~page_t~ structure by
|
||||
|
||||
38
vm/main.c
38
vm/main.c
@@ -1,19 +1,19 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
* Description: Entrypoint to program
|
||||
*/
|
||||
|
||||
#include "lib/base.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -40,7 +40,7 @@ int main(int argc, char *argv[])
|
||||
const char *filename = argv[1];
|
||||
|
||||
#if VERBOSE >= 1
|
||||
printf("[" TERM_YELLOW "INTERPRETER" TERM_RESET "]: `%s`\n", filename);
|
||||
INFO("INTERPRETER", "`%s`\n", filename);
|
||||
#endif
|
||||
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
@@ -53,16 +53,14 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (!header_read)
|
||||
{
|
||||
fprintf(stderr, "[ERROR]: Could not deserialise program header in `%s`\n",
|
||||
filename);
|
||||
FAIL("ERROR", "Could not deserialise program header in `%s`\n", filename);
|
||||
return 1;
|
||||
}
|
||||
// Ensure that we MUST have something to read
|
||||
else if (program.count == 0)
|
||||
return 0;
|
||||
|
||||
// After reading header, we can allocate the buffer of instrutions
|
||||
// exactly
|
||||
// After reading header, we can allocate the buffer of instrutions exactly
|
||||
program.instructions = calloc(program.count, sizeof(*program.instructions));
|
||||
size_t bytes_read = 0;
|
||||
read_err_prog_t read_err =
|
||||
@@ -71,7 +69,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (bytes_read == 0)
|
||||
{
|
||||
fprintf(stderr, "[ERROR]:%s [%lu]:", filename, read_err.index);
|
||||
FAIL("ERROR", "%s [%lu]:", filename, read_err.index);
|
||||
switch (read_err.type)
|
||||
{
|
||||
case READ_ERR_INVALID_OPCODE:
|
||||
@@ -93,8 +91,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
#if VERBOSE >= 1
|
||||
printf("\t[" TERM_GREEN "SETUP" TERM_RESET "]: Read %lu instructions\n",
|
||||
program.count);
|
||||
SUCCESS("SETUP", "Read %lu instructions\n", program.count);
|
||||
#endif
|
||||
|
||||
size_t stack_size = 256;
|
||||
@@ -114,11 +111,8 @@ int main(int argc, char *argv[])
|
||||
vm_load_call_stack(&vm, call_stack, call_stack_size);
|
||||
|
||||
#if VERBOSE >= 1
|
||||
printf("\t[" TERM_GREEN "SETUP" TERM_RESET
|
||||
"]: Loaded stack and program into VM\n");
|
||||
#endif
|
||||
#if VERBOSE >= 1
|
||||
printf("[" TERM_YELLOW "INTERPRETER" TERM_RESET "]: Beginning execution\n");
|
||||
SUCCESS("SETUP", "Loaded internals\n%s", "");
|
||||
INFO("INTERPRETER", "Beginning execution\n%s", "");
|
||||
#endif
|
||||
err_t err = vm_execute_all(&vm);
|
||||
|
||||
@@ -126,7 +120,7 @@ int main(int argc, char *argv[])
|
||||
if (err)
|
||||
{
|
||||
const char *error_str = err_as_cstr(err);
|
||||
fprintf(stderr, "[ERROR]: %s\n", error_str);
|
||||
FAIL("ERROR", "%s\n", error_str);
|
||||
vm_print_all(&vm, stderr);
|
||||
ret = 255 - err;
|
||||
}
|
||||
@@ -134,7 +128,7 @@ int main(int argc, char *argv[])
|
||||
vm_stop(&vm);
|
||||
|
||||
#if VERBOSE >= 1
|
||||
printf("[%sINTERPRETER%s]: Finished execution\n", TERM_GREEN, TERM_RESET);
|
||||
SUCCESS("INTEPRETER", "Finished execution\n%s", "");
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
172
vm/runtime.c
172
vm/runtime.c
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
@@ -62,7 +61,7 @@ const char *err_as_cstr(err_t err)
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(NUMBER_OF_OPCODES == 129, "vm_execute: Out of date");
|
||||
static_assert(NUMBER_OF_OPCODES == 115, "vm_execute: Out of date");
|
||||
|
||||
err_t vm_execute(vm_t *vm)
|
||||
{
|
||||
@@ -99,11 +98,10 @@ err_t vm_execute(vm_t *vm)
|
||||
"Code using OPCODE_DATA_TYPE for quick same type opcode "
|
||||
"conversion may be out of date.");
|
||||
|
||||
// NOTE: We always use the first register to hold the result of
|
||||
// this pop.
|
||||
// NOTE: We always use the first register to hold the result of this pop.
|
||||
|
||||
// Here we add OP_MOV_BYTE and the data_type_t of the opcode to
|
||||
// get the right typed OP_MOV opcode.
|
||||
// Here we add OP_MOV_BYTE and the data_type_t of the opcode to get the
|
||||
// right typed OP_MOV opcode.
|
||||
opcode_t mov_opcode =
|
||||
OPCODE_DATA_TYPE(instruction.opcode, OP_POP) + OP_MOV_BYTE;
|
||||
|
||||
@@ -120,13 +118,13 @@ err_t vm_execute(vm_t *vm)
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_PLUS) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_SUB) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MULT) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MALLOC_STACK) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MSET_STACK) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MGET_STACK) ||
|
||||
SIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_LT) ||
|
||||
SIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_LTE) ||
|
||||
SIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_GT) ||
|
||||
SIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_GTE) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MALLOC) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MSET) ||
|
||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MGET) ||
|
||||
instruction.opcode == OP_MDELETE || instruction.opcode == OP_MSIZE)
|
||||
{
|
||||
err_t err = STACK_ROUTINES[instruction.opcode](vm);
|
||||
@@ -137,15 +135,6 @@ err_t vm_execute(vm_t *vm)
|
||||
// Opcodes defined in loop
|
||||
else if (instruction.opcode == OP_JUMP_ABS)
|
||||
return vm_jump(vm, instruction.operand.as_word);
|
||||
else if (instruction.opcode == OP_JUMP_STACK)
|
||||
{
|
||||
data_t ret = {0};
|
||||
// Set prog->ptr to the word on top of the stack
|
||||
err_t err = vm_pop_word(vm, &ret);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_jump(vm, ret.as_word);
|
||||
}
|
||||
else if (UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_JUMP_IF))
|
||||
{
|
||||
static_assert(DATA_TYPE_NIL == -1 && DATA_TYPE_WORD == 3,
|
||||
@@ -153,8 +142,8 @@ err_t vm_execute(vm_t *vm)
|
||||
"conversion may be out of date.");
|
||||
|
||||
data_t datum = {0};
|
||||
// Here we add OP_POP_BYTE and the data_type_t of the opcode to
|
||||
// get the right OP_POP opcode.
|
||||
// Here we add OP_POP_BYTE and the data_type_t of the opcode to get the
|
||||
// right OP_POP opcode.
|
||||
opcode_t pop_opcode =
|
||||
OPCODE_DATA_TYPE(instruction.opcode, OP_JUMP_IF) + OP_POP_BYTE;
|
||||
|
||||
@@ -174,17 +163,6 @@ err_t vm_execute(vm_t *vm)
|
||||
vm->call_stack.address_pointers[vm->call_stack.ptr++] = vm->program.ptr + 1;
|
||||
return vm_jump(vm, instruction.operand.as_word);
|
||||
}
|
||||
else if (instruction.opcode == OP_CALL_STACK)
|
||||
{
|
||||
if (vm->call_stack.ptr >= vm->call_stack.max)
|
||||
return ERR_CALL_STACK_OVERFLOW;
|
||||
vm->call_stack.address_pointers[vm->call_stack.ptr++] = vm->program.ptr + 1;
|
||||
data_t ret = {0};
|
||||
err_t err = vm_pop_word(vm, &ret);
|
||||
if (err)
|
||||
return err;
|
||||
return vm_jump(vm, ret.as_word);
|
||||
}
|
||||
else if (instruction.opcode == OP_RET)
|
||||
{
|
||||
if (vm->call_stack.ptr == 0)
|
||||
@@ -229,9 +207,8 @@ err_t vm_execute(vm_t *vm)
|
||||
|
||||
// 2) create a format string for each datum type possible
|
||||
|
||||
// TODO: Figure out a way to ensure the ordering of OP_PRINT_* is
|
||||
// exactly BYTE, SBYTE, SHORT, SSHORT, HWORD, SHWORD, WORD, SWORD
|
||||
// via static_assert
|
||||
// TODO: Figure out a way to ensure the ordering of OP_PRINT_* is exactly
|
||||
// BYTE, SBYTE, SHORT, SSHORT, HWORD, SHWORD, WORD, SWORD via static_assert
|
||||
|
||||
// lookup table
|
||||
const char *format_strings[] = {
|
||||
@@ -286,7 +263,7 @@ err_t vm_execute_all(vm_t *vm)
|
||||
program->data.instructions[program->ptr].opcode != OP_HALT)
|
||||
{
|
||||
#if VERBOSE >= 2
|
||||
fprintf(stdout, "[vm_execute_all]: Trace(Cycle %lu)\n", cycles);
|
||||
INFO("vm_execute_all", "Trace(Cycle%lu)\n", cycles);
|
||||
fputs(
|
||||
"----------------------------------------------------------------------"
|
||||
"----------\n",
|
||||
@@ -342,8 +319,7 @@ err_t vm_execute_all(vm_t *vm)
|
||||
}
|
||||
|
||||
#if VERBOSE >= 1
|
||||
fprintf(stdout, "[%svm_execute_all%s]: Final VM state(Cycle %lu)\n",
|
||||
TERM_YELLOW, TERM_RESET, cycles);
|
||||
INFO("vm_execute_all", "Final VM State(Cycle %lu)\n", cycles);
|
||||
vm_print_all(vm, stdout);
|
||||
#endif
|
||||
return err;
|
||||
@@ -514,11 +490,15 @@ VM_DUP_CONSTR(short, SHORT)
|
||||
VM_DUP_CONSTR(hword, HWORD)
|
||||
VM_DUP_CONSTR(word, WORD)
|
||||
|
||||
#define VM_MALLOC_CONSTR(TYPE, TYPE_CAP) \
|
||||
err_t vm_malloc_##TYPE(vm_t *vm, word_t n) \
|
||||
{ \
|
||||
page_t *page = heap_allocate(&vm->heap, n * TYPE_CAP##_SIZE); \
|
||||
return vm_push_word(vm, DWORD((word_t)page)); \
|
||||
#define VM_MALLOC_CONSTR(TYPE, TYPE_CAP) \
|
||||
err_t vm_malloc_##TYPE(vm_t *vm) \
|
||||
{ \
|
||||
data_t n = {0}; \
|
||||
err_t err = vm_pop_word(vm, &n); \
|
||||
if (err) \
|
||||
return err; \
|
||||
page_t *page = heap_allocate(&vm->heap, n.as_word * TYPE_CAP##_SIZE); \
|
||||
return vm_push_word(vm, DWORD((word_t)page)); \
|
||||
}
|
||||
|
||||
VM_MALLOC_CONSTR(byte, BYTE)
|
||||
@@ -526,22 +506,26 @@ VM_MALLOC_CONSTR(short, SHORT)
|
||||
VM_MALLOC_CONSTR(hword, HWORD)
|
||||
VM_MALLOC_CONSTR(word, WORD)
|
||||
|
||||
#define VM_MSET_CONSTR(TYPE, TYPE_CAP) \
|
||||
err_t vm_mset_##TYPE(vm_t *vm, word_t nth) \
|
||||
{ \
|
||||
data_t object = {0}; \
|
||||
err_t err = vm_pop_##TYPE(vm, &object); \
|
||||
if (err) \
|
||||
return err; \
|
||||
data_t ptr = {0}; \
|
||||
err = vm_pop_word(vm, &ptr); \
|
||||
if (err) \
|
||||
return err; \
|
||||
page_t *page = (page_t *)ptr.as_word; \
|
||||
if (nth >= (page->available / TYPE_CAP##_SIZE)) \
|
||||
return ERR_OUT_OF_BOUNDS; \
|
||||
DARR_AT(TYPE##_t, page->data, nth) = object.as_##TYPE; \
|
||||
return ERR_OK; \
|
||||
#define VM_MSET_CONSTR(TYPE, TYPE_CAP) \
|
||||
err_t vm_mset_##TYPE(vm_t *vm) \
|
||||
{ \
|
||||
data_t n = {0}; \
|
||||
err_t err = vm_pop_word(vm, &n); \
|
||||
if (err) \
|
||||
return err; \
|
||||
data_t object = {0}; \
|
||||
err = vm_pop_##TYPE(vm, &object); \
|
||||
if (err) \
|
||||
return err; \
|
||||
data_t ptr = {0}; \
|
||||
err = vm_pop_word(vm, &ptr); \
|
||||
if (err) \
|
||||
return err; \
|
||||
page_t *page = (page_t *)ptr.as_word; \
|
||||
if (n.as_word >= (page->available / TYPE_CAP##_SIZE)) \
|
||||
return ERR_OUT_OF_BOUNDS; \
|
||||
DARR_AT(TYPE##_t, page->data, n.as_word) = object.as_##TYPE; \
|
||||
return ERR_OK; \
|
||||
}
|
||||
|
||||
VM_MSET_CONSTR(byte, BYTE)
|
||||
@@ -549,22 +533,26 @@ VM_MSET_CONSTR(short, SHORT)
|
||||
VM_MSET_CONSTR(hword, HWORD)
|
||||
VM_MSET_CONSTR(word, WORD)
|
||||
|
||||
#define VM_MGET_CONSTR(TYPE, TYPE_CAP) \
|
||||
err_t vm_mget_##TYPE(vm_t *vm, word_t n) \
|
||||
{ \
|
||||
data_t ptr = {0}; \
|
||||
err_t err = vm_pop_word(vm, &ptr); \
|
||||
if (err) \
|
||||
return err; \
|
||||
page_t *page = (page_t *)ptr.as_word; \
|
||||
if (n >= (page->available / TYPE_CAP##_SIZE)) \
|
||||
return ERR_OUT_OF_BOUNDS; \
|
||||
else if (vm->stack.ptr + TYPE_CAP##_SIZE >= vm->stack.max) \
|
||||
return ERR_STACK_OVERFLOW; \
|
||||
memcpy(vm->stack.data + vm->stack.ptr, \
|
||||
page->data + (n * (TYPE_CAP##_SIZE)), TYPE_CAP##_SIZE); \
|
||||
vm->stack.ptr += TYPE_CAP##_SIZE; \
|
||||
return ERR_OK; \
|
||||
#define VM_MGET_CONSTR(TYPE, TYPE_CAP) \
|
||||
err_t vm_mget_##TYPE(vm_t *vm) \
|
||||
{ \
|
||||
data_t n = {0}; \
|
||||
err_t err = vm_pop_word(vm, &n); \
|
||||
if (err) \
|
||||
return (err); \
|
||||
data_t ptr = {0}; \
|
||||
err = vm_pop_word(vm, &ptr); \
|
||||
if (err) \
|
||||
return err; \
|
||||
page_t *page = (page_t *)ptr.as_word; \
|
||||
if (n.as_word >= (page->available / TYPE_CAP##_SIZE)) \
|
||||
return ERR_OUT_OF_BOUNDS; \
|
||||
else if (vm->stack.ptr + TYPE_CAP##_SIZE >= vm->stack.max) \
|
||||
return ERR_STACK_OVERFLOW; \
|
||||
memcpy(vm->stack.data + vm->stack.ptr, \
|
||||
page->data + (n.as_word * (TYPE_CAP##_SIZE)), TYPE_CAP##_SIZE); \
|
||||
vm->stack.ptr += TYPE_CAP##_SIZE; \
|
||||
return ERR_OK; \
|
||||
}
|
||||
|
||||
VM_MGET_CONSTR(byte, BYTE)
|
||||
@@ -572,30 +560,6 @@ VM_MGET_CONSTR(short, SHORT)
|
||||
VM_MGET_CONSTR(hword, HWORD)
|
||||
VM_MGET_CONSTR(word, WORD)
|
||||
|
||||
// TODO: rename this to something more appropriate
|
||||
#define VM_MEMORY_STACK_CONSTR(ACTION, TYPE) \
|
||||
err_t vm_##ACTION##_stack_##TYPE(vm_t *vm) \
|
||||
{ \
|
||||
data_t n = {0}; \
|
||||
err_t err = vm_pop_word(vm, &n); \
|
||||
if (err) \
|
||||
return err; \
|
||||
return vm_##ACTION##_##TYPE(vm, n.as_word); \
|
||||
}
|
||||
|
||||
VM_MEMORY_STACK_CONSTR(malloc, byte)
|
||||
VM_MEMORY_STACK_CONSTR(malloc, short)
|
||||
VM_MEMORY_STACK_CONSTR(malloc, hword)
|
||||
VM_MEMORY_STACK_CONSTR(malloc, word)
|
||||
VM_MEMORY_STACK_CONSTR(mset, byte)
|
||||
VM_MEMORY_STACK_CONSTR(mset, short)
|
||||
VM_MEMORY_STACK_CONSTR(mset, hword)
|
||||
VM_MEMORY_STACK_CONSTR(mset, word)
|
||||
VM_MEMORY_STACK_CONSTR(mget, byte)
|
||||
VM_MEMORY_STACK_CONSTR(mget, short)
|
||||
VM_MEMORY_STACK_CONSTR(mget, hword)
|
||||
VM_MEMORY_STACK_CONSTR(mget, word)
|
||||
|
||||
err_t vm_mdelete(vm_t *vm)
|
||||
{
|
||||
data_t ptr = {0};
|
||||
|
||||
185
vm/runtime.h
185
vm/runtime.h
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2023-10-15
|
||||
* Author: Aryadev Chavali
|
||||
@@ -86,21 +85,6 @@ err_t vm_dup_short(vm_t *, word_t);
|
||||
err_t vm_dup_hword(vm_t *, word_t);
|
||||
err_t vm_dup_word(vm_t *, word_t);
|
||||
|
||||
err_t vm_malloc_byte(vm_t *, word_t);
|
||||
err_t vm_malloc_short(vm_t *, word_t);
|
||||
err_t vm_malloc_hword(vm_t *, word_t);
|
||||
err_t vm_malloc_word(vm_t *, word_t);
|
||||
|
||||
err_t vm_mset_byte(vm_t *, word_t);
|
||||
err_t vm_mset_short(vm_t *, word_t);
|
||||
err_t vm_mset_hword(vm_t *, word_t);
|
||||
err_t vm_mset_word(vm_t *, word_t);
|
||||
|
||||
err_t vm_mget_byte(vm_t *, word_t);
|
||||
err_t vm_mget_short(vm_t *, word_t);
|
||||
err_t vm_mget_hword(vm_t *, word_t);
|
||||
err_t vm_mget_word(vm_t *, word_t);
|
||||
|
||||
typedef err_t (*word_f)(vm_t *, word_t);
|
||||
static const word_f WORD_ROUTINES[] = {
|
||||
[OP_PUSH_REGISTER_BYTE] = vm_push_byte_register,
|
||||
@@ -117,38 +101,23 @@ static const word_f WORD_ROUTINES[] = {
|
||||
[OP_DUP_SHORT] = vm_dup_short,
|
||||
[OP_DUP_HWORD] = vm_dup_hword,
|
||||
[OP_DUP_WORD] = vm_dup_word,
|
||||
|
||||
[OP_MALLOC_BYTE] = vm_malloc_byte,
|
||||
[OP_MALLOC_SHORT] = vm_malloc_short,
|
||||
[OP_MALLOC_HWORD] = vm_malloc_hword,
|
||||
[OP_MALLOC_WORD] = vm_malloc_word,
|
||||
|
||||
[OP_MGET_BYTE] = vm_mget_byte,
|
||||
[OP_MGET_SHORT] = vm_mget_short,
|
||||
[OP_MGET_HWORD] = vm_mget_hword,
|
||||
[OP_MGET_WORD] = vm_mget_word,
|
||||
|
||||
[OP_MSET_BYTE] = vm_mset_byte,
|
||||
[OP_MSET_SHORT] = vm_mset_short,
|
||||
[OP_MSET_HWORD] = vm_mset_hword,
|
||||
[OP_MSET_WORD] = vm_mset_word,
|
||||
};
|
||||
|
||||
/* Operations that take input from the stack */
|
||||
err_t vm_malloc_stack_byte(vm_t *);
|
||||
err_t vm_malloc_stack_short(vm_t *);
|
||||
err_t vm_malloc_stack_hword(vm_t *);
|
||||
err_t vm_malloc_stack_word(vm_t *);
|
||||
err_t vm_malloc_byte(vm_t *);
|
||||
err_t vm_malloc_short(vm_t *);
|
||||
err_t vm_malloc_hword(vm_t *);
|
||||
err_t vm_malloc_word(vm_t *);
|
||||
|
||||
err_t vm_mset_stack_byte(vm_t *);
|
||||
err_t vm_mset_stack_short(vm_t *);
|
||||
err_t vm_mset_stack_hword(vm_t *);
|
||||
err_t vm_mset_stack_word(vm_t *);
|
||||
err_t vm_mset_byte(vm_t *);
|
||||
err_t vm_mset_short(vm_t *);
|
||||
err_t vm_mset_hword(vm_t *);
|
||||
err_t vm_mset_word(vm_t *);
|
||||
|
||||
err_t vm_mget_stack_byte(vm_t *);
|
||||
err_t vm_mget_stack_short(vm_t *);
|
||||
err_t vm_mget_stack_hword(vm_t *);
|
||||
err_t vm_mget_stack_word(vm_t *);
|
||||
err_t vm_mget_byte(vm_t *);
|
||||
err_t vm_mget_short(vm_t *);
|
||||
err_t vm_mget_hword(vm_t *);
|
||||
err_t vm_mget_word(vm_t *);
|
||||
|
||||
err_t vm_mdelete(vm_t *);
|
||||
err_t vm_msize(vm_t *);
|
||||
@@ -235,97 +204,59 @@ err_t vm_mult_word(vm_t *);
|
||||
|
||||
typedef err_t (*stack_f)(vm_t *);
|
||||
static const stack_f STACK_ROUTINES[] = {
|
||||
[OP_MALLOC_STACK_BYTE] = vm_malloc_stack_byte,
|
||||
[OP_MALLOC_STACK_SHORT] = vm_malloc_stack_short,
|
||||
[OP_MALLOC_STACK_HWORD] = vm_malloc_stack_hword,
|
||||
[OP_MALLOC_STACK_WORD] = vm_malloc_stack_word,
|
||||
[OP_MALLOC_BYTE] = vm_malloc_byte, [OP_MALLOC_SHORT] = vm_malloc_short,
|
||||
[OP_MALLOC_HWORD] = vm_malloc_hword, [OP_MALLOC_WORD] = vm_malloc_word,
|
||||
|
||||
[OP_MGET_STACK_BYTE] = vm_mget_stack_byte,
|
||||
[OP_MGET_STACK_SHORT] = vm_mget_stack_short,
|
||||
[OP_MGET_STACK_HWORD] = vm_mget_stack_hword,
|
||||
[OP_MGET_STACK_WORD] = vm_mget_stack_word,
|
||||
[OP_MSET_STACK_BYTE] = vm_mset_stack_byte,
|
||||
[OP_MSET_STACK_SHORT] = vm_mset_stack_short,
|
||||
[OP_MSET_STACK_HWORD] = vm_mset_stack_hword,
|
||||
[OP_MSET_STACK_WORD] = vm_mset_stack_word,
|
||||
[OP_MGET_BYTE] = vm_mget_byte, [OP_MGET_SHORT] = vm_mget_short,
|
||||
[OP_MGET_HWORD] = vm_mget_hword, [OP_MGET_WORD] = vm_mget_word,
|
||||
|
||||
[OP_MDELETE] = vm_mdelete,
|
||||
[OP_MSIZE] = vm_msize,
|
||||
[OP_MSET_BYTE] = vm_mset_byte, [OP_MSET_SHORT] = vm_mset_short,
|
||||
[OP_MSET_HWORD] = vm_mset_hword, [OP_MSET_WORD] = vm_mset_word,
|
||||
|
||||
[OP_NOT_BYTE] = vm_not_byte,
|
||||
[OP_NOT_SHORT] = vm_not_short,
|
||||
[OP_NOT_HWORD] = vm_not_hword,
|
||||
[OP_NOT_WORD] = vm_not_word,
|
||||
[OP_MDELETE] = vm_mdelete, [OP_MSIZE] = vm_msize,
|
||||
|
||||
[OP_OR_BYTE] = vm_or_byte,
|
||||
[OP_OR_SHORT] = vm_or_short,
|
||||
[OP_OR_HWORD] = vm_or_hword,
|
||||
[OP_OR_WORD] = vm_or_word,
|
||||
[OP_NOT_BYTE] = vm_not_byte, [OP_NOT_SHORT] = vm_not_short,
|
||||
[OP_NOT_HWORD] = vm_not_hword, [OP_NOT_WORD] = vm_not_word,
|
||||
|
||||
[OP_AND_BYTE] = vm_and_byte,
|
||||
[OP_AND_SHORT] = vm_and_short,
|
||||
[OP_AND_HWORD] = vm_and_hword,
|
||||
[OP_AND_WORD] = vm_and_word,
|
||||
[OP_OR_BYTE] = vm_or_byte, [OP_OR_SHORT] = vm_or_short,
|
||||
[OP_OR_HWORD] = vm_or_hword, [OP_OR_WORD] = vm_or_word,
|
||||
|
||||
[OP_XOR_BYTE] = vm_xor_byte,
|
||||
[OP_XOR_SHORT] = vm_xor_short,
|
||||
[OP_XOR_HWORD] = vm_xor_hword,
|
||||
[OP_XOR_WORD] = vm_xor_word,
|
||||
[OP_AND_BYTE] = vm_and_byte, [OP_AND_SHORT] = vm_and_short,
|
||||
[OP_AND_HWORD] = vm_and_hword, [OP_AND_WORD] = vm_and_word,
|
||||
|
||||
[OP_EQ_BYTE] = vm_eq_byte,
|
||||
[OP_EQ_SHORT] = vm_eq_short,
|
||||
[OP_EQ_HWORD] = vm_eq_hword,
|
||||
[OP_EQ_WORD] = vm_eq_word,
|
||||
[OP_XOR_BYTE] = vm_xor_byte, [OP_XOR_SHORT] = vm_xor_short,
|
||||
[OP_XOR_HWORD] = vm_xor_hword, [OP_XOR_WORD] = vm_xor_word,
|
||||
|
||||
[OP_LT_BYTE] = vm_lt_byte,
|
||||
[OP_LT_SBYTE] = vm_lt_sbyte,
|
||||
[OP_LT_SHORT] = vm_lt_short,
|
||||
[OP_LT_SSHORT] = vm_lt_sshort,
|
||||
[OP_LT_SHWORD] = vm_lt_shword,
|
||||
[OP_LT_HWORD] = vm_lt_hword,
|
||||
[OP_LT_SWORD] = vm_lt_sword,
|
||||
[OP_LT_WORD] = vm_lt_word,
|
||||
[OP_EQ_BYTE] = vm_eq_byte, [OP_EQ_SHORT] = vm_eq_short,
|
||||
[OP_EQ_HWORD] = vm_eq_hword, [OP_EQ_WORD] = vm_eq_word,
|
||||
|
||||
[OP_LTE_BYTE] = vm_lte_byte,
|
||||
[OP_LTE_SBYTE] = vm_lte_sbyte,
|
||||
[OP_LTE_SHORT] = vm_lte_short,
|
||||
[OP_LTE_SSHORT] = vm_lte_sshort,
|
||||
[OP_LTE_SHWORD] = vm_lte_shword,
|
||||
[OP_LTE_HWORD] = vm_lte_hword,
|
||||
[OP_LTE_SWORD] = vm_lte_sword,
|
||||
[OP_LTE_WORD] = vm_lte_word,
|
||||
[OP_LT_BYTE] = vm_lt_byte, [OP_LT_SBYTE] = vm_lt_sbyte,
|
||||
[OP_LT_SHORT] = vm_lt_short, [OP_LT_SSHORT] = vm_lt_sshort,
|
||||
[OP_LT_SHWORD] = vm_lt_shword, [OP_LT_HWORD] = vm_lt_hword,
|
||||
[OP_LT_SWORD] = vm_lt_sword, [OP_LT_WORD] = vm_lt_word,
|
||||
|
||||
[OP_GT_BYTE] = vm_gt_byte,
|
||||
[OP_GT_SBYTE] = vm_gt_sbyte,
|
||||
[OP_GT_SHORT] = vm_gt_short,
|
||||
[OP_GT_SSHORT] = vm_gt_sshort,
|
||||
[OP_GT_SHWORD] = vm_gt_shword,
|
||||
[OP_GT_HWORD] = vm_gt_hword,
|
||||
[OP_GT_SWORD] = vm_gt_sword,
|
||||
[OP_GT_WORD] = vm_gt_word,
|
||||
[OP_LTE_BYTE] = vm_lte_byte, [OP_LTE_SBYTE] = vm_lte_sbyte,
|
||||
[OP_LTE_SHORT] = vm_lte_short, [OP_LTE_SSHORT] = vm_lte_sshort,
|
||||
[OP_LTE_SHWORD] = vm_lte_shword, [OP_LTE_HWORD] = vm_lte_hword,
|
||||
[OP_LTE_SWORD] = vm_lte_sword, [OP_LTE_WORD] = vm_lte_word,
|
||||
|
||||
[OP_GTE_BYTE] = vm_gte_byte,
|
||||
[OP_GTE_SBYTE] = vm_gte_sbyte,
|
||||
[OP_GTE_SHORT] = vm_gte_short,
|
||||
[OP_GTE_SSHORT] = vm_gte_sshort,
|
||||
[OP_GTE_SHWORD] = vm_gte_shword,
|
||||
[OP_GTE_HWORD] = vm_gte_hword,
|
||||
[OP_GTE_SWORD] = vm_gte_sword,
|
||||
[OP_GTE_WORD] = vm_gte_word,
|
||||
[OP_GT_BYTE] = vm_gt_byte, [OP_GT_SBYTE] = vm_gt_sbyte,
|
||||
[OP_GT_SHORT] = vm_gt_short, [OP_GT_SSHORT] = vm_gt_sshort,
|
||||
[OP_GT_SHWORD] = vm_gt_shword, [OP_GT_HWORD] = vm_gt_hword,
|
||||
[OP_GT_SWORD] = vm_gt_sword, [OP_GT_WORD] = vm_gt_word,
|
||||
|
||||
[OP_PLUS_BYTE] = vm_plus_byte,
|
||||
[OP_PLUS_SHORT] = vm_plus_short,
|
||||
[OP_PLUS_HWORD] = vm_plus_hword,
|
||||
[OP_PLUS_WORD] = vm_plus_word,
|
||||
[OP_SUB_BYTE] = vm_sub_byte,
|
||||
[OP_SUB_SHORT] = vm_sub_short,
|
||||
[OP_SUB_HWORD] = vm_sub_hword,
|
||||
[OP_SUB_WORD] = vm_sub_word,
|
||||
[OP_GTE_BYTE] = vm_gte_byte, [OP_GTE_SBYTE] = vm_gte_sbyte,
|
||||
[OP_GTE_SHORT] = vm_gte_short, [OP_GTE_SSHORT] = vm_gte_sshort,
|
||||
[OP_GTE_SHWORD] = vm_gte_shword, [OP_GTE_HWORD] = vm_gte_hword,
|
||||
[OP_GTE_SWORD] = vm_gte_sword, [OP_GTE_WORD] = vm_gte_word,
|
||||
|
||||
[OP_MULT_BYTE] = vm_mult_byte,
|
||||
[OP_MULT_SHORT] = vm_mult_short,
|
||||
[OP_MULT_HWORD] = vm_mult_hword,
|
||||
[OP_MULT_WORD] = vm_mult_word,
|
||||
[OP_PLUS_BYTE] = vm_plus_byte, [OP_PLUS_SHORT] = vm_plus_short,
|
||||
[OP_PLUS_HWORD] = vm_plus_hword, [OP_PLUS_WORD] = vm_plus_word,
|
||||
[OP_SUB_BYTE] = vm_sub_byte, [OP_SUB_SHORT] = vm_sub_short,
|
||||
[OP_SUB_HWORD] = vm_sub_hword, [OP_SUB_WORD] = vm_sub_word,
|
||||
|
||||
[OP_MULT_BYTE] = vm_mult_byte, [OP_MULT_SHORT] = vm_mult_short,
|
||||
[OP_MULT_HWORD] = vm_mult_hword, [OP_MULT_WORD] = vm_mult_word,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
37
vm/struct.c
37
vm/struct.c
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2024-04-25
|
||||
* Author: Aryadev Chavali
|
||||
@@ -53,18 +52,16 @@ void vm_stop(vm_t *vm)
|
||||
{
|
||||
#if VERBOSE >= 1
|
||||
bool leaks = false;
|
||||
printf("[" TERM_YELLOW "DATA" TERM_RESET "]: Checking for leaks...\n");
|
||||
INFO("vm_stop", "Checking for leaks...\n%s", "");
|
||||
if (vm->call_stack.ptr > 0)
|
||||
{
|
||||
leaks = true;
|
||||
printf("\t[" TERM_RED "DATA" TERM_RESET "]: Call stack at %lu\n\t[" TERM_RED
|
||||
"DATA" TERM_RESET "]\n\t[" TERM_RED "DATA" TERM_RESET "]: Call "
|
||||
"stack trace:",
|
||||
vm->call_stack.ptr);
|
||||
FAIL("vm_stop", "Call stack at %lu\n", vm->call_stack.ptr);
|
||||
FAIL("vm_stop", "Call stack trace:%s", "");
|
||||
for (size_t i = vm->call_stack.ptr; i > 0; --i)
|
||||
{
|
||||
word_t w = vm->call_stack.address_pointers[i - 1];
|
||||
printf("\t\t%lu: %lX", vm->call_stack.ptr - i, w);
|
||||
printf("\t[%lu]: %lX", vm->call_stack.ptr - i, w);
|
||||
if (i != 1)
|
||||
printf(", ");
|
||||
printf("\n");
|
||||
@@ -81,22 +78,20 @@ void vm_stop(vm_t *vm)
|
||||
capacities[i] = cur->available;
|
||||
total_capacity += capacities[i];
|
||||
}
|
||||
printf("\t[" TERM_RED "DATA" TERM_RESET
|
||||
"]: Heap: %luB (over %lu %s) not reclaimed\n",
|
||||
total_capacity, size_pages, size_pages == 1 ? "page" : "pages");
|
||||
FAIL("vm_stop", "Heap: %luB (over %lu %s) not reclaimed\n", total_capacity,
|
||||
size_pages, size_pages == 1 ? "page" : "pages");
|
||||
for (size_t i = 0; i < size_pages; i++)
|
||||
printf("\t\t[%lu]: %luB lost\n", i, capacities[i]);
|
||||
printf("\t[%lu]: %luB lost\n", i, capacities[i]);
|
||||
}
|
||||
if (vm->stack.ptr > 0)
|
||||
{
|
||||
leaks = true;
|
||||
printf("\t[" TERM_RED "DATA" TERM_RESET "]: Stack: %luB not reclaimed\n",
|
||||
vm->stack.ptr);
|
||||
FAIL("vm_stop", "Stack: %luB not reclaimed\n", vm->stack.ptr);
|
||||
}
|
||||
if (leaks)
|
||||
printf("[" TERM_RED "DATA" TERM_RESET "]: Leaks found\n");
|
||||
FAIL("vm_stop", "Leaks found\n%s", "");
|
||||
else
|
||||
printf("[" TERM_GREEN "DATA" TERM_RESET "]: No leaks found\n");
|
||||
SUCCESS("vm_stop", "No leaks found\n%s", "");
|
||||
#endif
|
||||
|
||||
vm->registers = (struct Registers){0};
|
||||
|
||||
13
vm/struct.h
13
vm/struct.h
@@ -1,13 +1,12 @@
|
||||
/* Copyright (C) 2024 Aryadev Chavali
|
||||
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||
* more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
* You should have received a copy of the GNU General Public License Version 2
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
* Created: 2024-04-25
|
||||
* Author: Aryadev Chavali
|
||||
|
||||
Reference in New Issue
Block a user