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 -*-
|
;;; Directory Local Variables -*- no-byte-compile: t -*-
|
||||||
;;; For more information see (info "(emacs) Directory Variables")
|
;;; 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")))
|
(compile-command . "make all VERBOSE=2 RELEASE=0")))
|
||||||
(c-mode . ((mode . clang-format)
|
(c-mode . ((mode . clang-format)
|
||||||
(eval . (eglot-ensure)))))
|
(eval . (eglot-ensure)))))
|
||||||
|
|||||||
13
lib/base.c
13
lib/base.c
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-10-26
|
* Created: 2023-10-26
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
|
|||||||
78
lib/base.h
78
lib/base.h
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-10-15
|
* Created: 2023-10-15
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
@@ -28,6 +27,16 @@
|
|||||||
#define TERM_RED "\033[31m"
|
#define TERM_RED "\033[31m"
|
||||||
#define TERM_RESET "\033[0m"
|
#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)
|
// Flags for program behaviour (usually related to printing)
|
||||||
#ifndef VERBOSE
|
#ifndef VERBOSE
|
||||||
#define VERBOSE 0
|
#define VERBOSE 0
|
||||||
@@ -92,8 +101,7 @@ typedef union
|
|||||||
} data_t;
|
} data_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Enum of type tags for the data_t structure to provide
|
@brief Enum of type tags for the data_t structure to provide context.
|
||||||
context.
|
|
||||||
*/
|
*/
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@@ -117,11 +125,10 @@ static const hword_t __i = 0xFFFF0000;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Safely subtract SUB from W, where both are words (64 bit
|
@brief Safely subtract SUB from W, where both are words (64 bit integers).
|
||||||
integers).
|
|
||||||
|
|
||||||
@details In case of underflow (i.e. where W - SUB < 0) returns 0
|
@details In case of underflow (i.e. where W - SUB < 0) returns 0 instead of
|
||||||
instead of the underflowed result.
|
the underflowed result.
|
||||||
*/
|
*/
|
||||||
#define WORD_SAFE_SUB(W, SUB) ((W) > (SUB) ? ((W) - (SUB)) : 0)
|
#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.
|
@brief Return the Nth half word of WORD.
|
||||||
|
|
||||||
@details N should range from 0 to 1 as there are 2 half words in a
|
@details N should range from 0 to 1 as there are 2 half words in a word
|
||||||
word
|
|
||||||
*/
|
*/
|
||||||
#define WORD_NTH_HWORD(WORD, N) (((WORD) >> ((N) * 32)) & 0xFFFFFFFF)
|
#define WORD_NTH_HWORD(WORD, N) (((WORD) >> ((N) * 32)) & 0xFFFFFFFF)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Convert a buffer of bytes to a short
|
@brief Convert a buffer of bytes to a short
|
||||||
|
|
||||||
@details It is assumed that the buffer of bytes are in virtual
|
@details It is assumed that the buffer of bytes are in virtual machine byte
|
||||||
machine byte code format (little endian) and that they are at least
|
code format (little endian) and that they are at least SHORT_SIZE in size.
|
||||||
SHORT_SIZE in size.
|
|
||||||
*/
|
*/
|
||||||
short_t convert_bytes_to_short(const byte_t *buffer);
|
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 s: Short to convert
|
||||||
|
|
||||||
@param buffer: Buffer to store into. It is assumed that the buffer
|
@param buffer: Buffer to store into. It is assumed that the buffer has at
|
||||||
has at least SHORT_SIZE space.
|
least SHORT_SIZE space.
|
||||||
*/
|
*/
|
||||||
void convert_short_to_bytes(const short_t s, byte_t *buffer);
|
void convert_short_to_bytes(const short_t s, byte_t *buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Convert a buffer of bytes to a half word.
|
@brief Convert a buffer of bytes to a half word.
|
||||||
|
|
||||||
@details It is assumed that the buffer of bytes are in virtual
|
@details It is assumed that the buffer of bytes are in virtual machine byte
|
||||||
machine byte code format (little endian) and that they are at least
|
code format (little endian) and that they are at least HWORD_SIZE in size.
|
||||||
HWORD_SIZE in size.
|
|
||||||
*/
|
*/
|
||||||
hword_t convert_bytes_to_hword(const byte_t *buffer);
|
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 h: Half word to convert
|
||||||
|
|
||||||
@param buffer: Buffer to store into. It is assumed that the buffer
|
@param buffer: Buffer to store into. It is assumed that the buffer has at
|
||||||
has at least HWORD_SIZE space.
|
least HWORD_SIZE space.
|
||||||
*/
|
*/
|
||||||
void convert_hword_to_bytes(const hword_t h, byte_t *buffer);
|
void convert_hword_to_bytes(const hword_t h, byte_t *buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Convert a buffer of bytes to a word.
|
@brief Convert a buffer of bytes to a word.
|
||||||
|
|
||||||
@details It is assumed that the buffer of bytes are in virtual
|
@details It is assumed that the buffer of bytes are in virtual machine byte
|
||||||
machine byte code format (little endian) and that they are at least
|
code format (little endian) and that they are at least WORD_SIZE in size.
|
||||||
WORD_SIZE in size.
|
|
||||||
*/
|
*/
|
||||||
word_t convert_bytes_to_word(const byte_t *);
|
word_t convert_bytes_to_word(const byte_t *);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Convert a word into a VM byte code format bytes (little
|
@brief Convert a word into a VM byte code format bytes (little endian)
|
||||||
endian)
|
|
||||||
|
|
||||||
@param w: Word to convert
|
@param w: Word to convert
|
||||||
|
|
||||||
@param buffer: Buffer to store into. It is assumed that the buffer
|
@param buffer: Buffer to store into. It is assumed that the buffer has at
|
||||||
has at least WORD_SIZE space.
|
least WORD_SIZE space.
|
||||||
*/
|
*/
|
||||||
void convert_word_to_bytes(const word_t w, byte_t *buffer);
|
void convert_word_to_bytes(const word_t w, byte_t *buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Swap the ordering of bytes within an short
|
@brief Swap the ordering of bytes within an short
|
||||||
|
|
||||||
@details The ordering of the bytes in the short are reversed (2
|
@details The ordering of the bytes in the short are reversed (2 bytes in a
|
||||||
bytes in a short).
|
short).
|
||||||
|
|
||||||
@param s: short to swap
|
@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
|
@brief Swap the ordering of bytes within an half word
|
||||||
|
|
||||||
@details The ordering of the bytes in the half word are reversed (4
|
@details The ordering of the bytes in the half word are reversed (4 bytes in
|
||||||
bytes in a half word).
|
a half word).
|
||||||
|
|
||||||
@param h: Half word to swap
|
@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
|
@brief Swap the ordering of bytes within an word
|
||||||
|
|
||||||
@details The ordering of the bytes in the word are reversed (8
|
@details The ordering of the bytes in the word are reversed (8 bytes in a
|
||||||
bytes in a word).
|
word).
|
||||||
|
|
||||||
@param w: Word to swap
|
@param w: Word to swap
|
||||||
*/
|
*/
|
||||||
|
|||||||
13
lib/darr.c
13
lib/darr.c
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-10-15
|
* Created: 2023-10-15
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
|
|||||||
76
lib/darr.h
76
lib/darr.h
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-10-15
|
* Created: 2023-10-15
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
@@ -43,10 +42,10 @@ typedef struct
|
|||||||
/**
|
/**
|
||||||
@brief Get the `IND`th item of type `TYPE` in `DARR_DATA`
|
@brief Get the `IND`th item of type `TYPE` in `DARR_DATA`
|
||||||
|
|
||||||
@details Cast `DARR_DATA` to `TYPE`, taking the `IND`th item.
|
@details Cast `DARR_DATA` to `TYPE`, taking the `IND`th item. NOTE: This is
|
||||||
NOTE: This is unsafe as bound checks are not done i.e. if
|
unsafe as bound checks are not done i.e. if `DARR_DATA` has at least space
|
||||||
`DARR_DATA` has at least space for `IND` * sizeof(`TYPE`) items.
|
for `IND` * sizeof(`TYPE`) items. It is presumed the caller will check
|
||||||
It is presumed the caller will check themselves.
|
themselves.
|
||||||
|
|
||||||
@param[TYPE] Type to cast internal byte array
|
@param[TYPE] Type to cast internal byte array
|
||||||
@param[DARR_DATA] Byte array of darr
|
@param[DARR_DATA] Byte array of darr
|
||||||
@@ -59,24 +58,23 @@ typedef struct
|
|||||||
/**
|
/**
|
||||||
@brief Initialise a dynamic array `darr` with n bytes of space.
|
@brief Initialise a dynamic array `darr` with n bytes of space.
|
||||||
|
|
||||||
@details All properties of `darr` are initialised. `darr`.used is
|
@details All properties of `darr` are initialised. `darr`.used is set to 0,
|
||||||
set to 0, `darr`.available is set to `n` and `darr`.data is set to
|
`darr`.available is set to `n` and `darr`.data is set to a pointer of `n`
|
||||||
a pointer of `n` bytes. NOTE: If `n` = 0 then it is set to
|
bytes. NOTE: If `n` = 0 then it is set to DARR_DEFAULT_SIZE
|
||||||
DARR_DEFAULT_SIZE
|
|
||||||
|
|
||||||
@param[darr] Pointer to darr_t object to initialise
|
@param[darr] Pointer to darr_t object to initialise
|
||||||
@param[n] Number of bytes to allocate. If equal to 0 then
|
@param[n] Number of bytes to allocate. If equal to 0 then considered treated
|
||||||
considered treated as DARR_DEFAULT_SIZE
|
as DARR_DEFAULT_SIZE
|
||||||
*/
|
*/
|
||||||
void darr_init(darr_t *darr, size_t n);
|
void darr_init(darr_t *darr, size_t n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Ensure a dynamic array has at least n bytes of space free.
|
@brief Ensure a dynamic array has at least n bytes of space free.
|
||||||
|
|
||||||
@details If `darr` has n or more bytes free, nothing occurs.
|
@details If `darr` has n or more bytes free, nothing occurs. Otherwise, the
|
||||||
Otherwise, the byte array in `darr` is reallocated such that it has
|
byte array in `darr` is reallocated such that it has at least `n` bytes of
|
||||||
at least `n` bytes of free space. NOTE: `darr` has at least `n`
|
free space. NOTE: `darr` has at least `n` bytes free if and only if
|
||||||
bytes free if and only if `darr`.used + `n` <= `darr`.available
|
`darr`.used + `n` <= `darr`.available
|
||||||
|
|
||||||
@param[darr] Dynamic array to check
|
@param[darr] Dynamic array to check
|
||||||
@param[n] Number of bytes
|
@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.
|
@brief Append a byte to a dynamic array.
|
||||||
|
|
||||||
@details Append a byte to the end of the byte buffer in a dyamic
|
@details Append a byte to the end of the byte buffer in a dyamic array. If
|
||||||
array. If the dynamic array doesn't have enough free space to fit
|
the dynamic array doesn't have enough free space to fit the byte, it will
|
||||||
the byte, it will reallocate to ensure it can fit it in via
|
reallocate to ensure it can fit it in via darr_ensure_capacity().
|
||||||
darr_ensure_capacity().
|
|
||||||
|
|
||||||
@param[darr] Dynamic arrary to append to
|
@param[darr] Dynamic arrary to append to
|
||||||
@param[b] Byte to append
|
@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.
|
@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
|
@details Append an array of bytes to the end of a byte buffer. If the
|
||||||
the dynamic array doesn't have enough free space to fit all n bytes
|
dynamic array doesn't have enough free space to fit all n bytes it will
|
||||||
it will reallocate to ensure it can fit it in via
|
reallocate to ensure it can fit it in via darr_ensure_capacity().
|
||||||
darr_ensure_capacity().
|
|
||||||
|
|
||||||
@param[darr] Dynamic array to append to
|
@param[darr] Dynamic array to append to
|
||||||
@param[b] Array of bytes to append
|
@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
|
@brief Get the nth byte of a dynamic array
|
||||||
|
|
||||||
@details Get the nth byte of the dynamic array. 0 based. NOTE: If
|
@details Get the nth byte of the dynamic array. 0 based. NOTE: If the
|
||||||
the dynamic array has less than n bytes used, it will return 0 as a
|
dynamic array has less than n bytes used, it will return 0 as a default
|
||||||
default value, so this is a safe alternative to DARR_AT().
|
value, so this is a safe alternative to DARR_AT().
|
||||||
|
|
||||||
@param[darr] Dynamic array to index
|
@param[darr] Dynamic array to index
|
||||||
@param[n] Index to get byte at
|
@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
|
@brief Write the bytes of a dynamic array to a file pointer
|
||||||
|
|
||||||
@details Given a dynamic array and a file pointer, write the
|
@details Given a dynamic array and a file pointer, write the internal buffer
|
||||||
internal buffer of bytes to the file pointer. NOTE: The file
|
of bytes to the file pointer. NOTE: The file pointer is assumed to be open
|
||||||
pointer is assumed to be open and suitable for writing.
|
and suitable for writing.
|
||||||
|
|
||||||
@param[darr] Dynamic array to write
|
@param[darr] Dynamic array to write
|
||||||
@param[fp] File pointer to write on
|
@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
|
@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
|
@details Read a file pointer as a buffer of bytes then return that buffer
|
||||||
buffer wrapped in a darr_t structure. NOTE: the file pointer is
|
wrapped in a darr_t structure. NOTE: the file pointer is assumed to be open
|
||||||
assumed to be open and suitable for reading.
|
and suitable for reading.
|
||||||
|
|
||||||
@param[fp]: File pointer to read
|
@param[fp]: File pointer to read
|
||||||
|
|
||||||
@return Dynamic array structure with available set to the size of
|
@return Dynamic array structure with available set to the size of the
|
||||||
the `buffer` read and `data` set to the buffer of bytes.
|
`buffer` read and `data` set to the buffer of bytes.
|
||||||
*/
|
*/
|
||||||
darr_t darr_read_file(FILE *fp);
|
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
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-11-01
|
* Created: 2023-11-01
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
@@ -60,8 +59,7 @@ bool heap_free(heap_t *heap, page_t *page)
|
|||||||
if (cur == page)
|
if (cur == page)
|
||||||
{
|
{
|
||||||
page_delete(cur);
|
page_delete(cur);
|
||||||
// TODO: When does this fragmentation become a performance
|
// TODO: When does this fragmentation become a performance issue?
|
||||||
// issue?
|
|
||||||
DARR_AT(page_t *, heap->page_vec.data, i) = NULL;
|
DARR_AT(page_t *, heap->page_vec.data, i) = NULL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
47
lib/heap.h
47
lib/heap.h
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-11-01
|
* Created: 2023-11-01
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
@@ -27,8 +26,8 @@
|
|||||||
/**
|
/**
|
||||||
@brief Some fixed portion of bytes allocated on the heap.
|
@brief Some fixed portion of bytes allocated on the heap.
|
||||||
|
|
||||||
@details A fixed allocation of bytes. Cannot be resized nor can it
|
@details A fixed allocation of bytes. Cannot be resized nor can it be stack
|
||||||
be stack allocated (the usual way) due to flexible array attached.
|
allocated (the usual way) due to flexible array attached.
|
||||||
|
|
||||||
@prop[next] Next page in the linked list
|
@prop[next] Next page in the linked list
|
||||||
@prop[available] Available number of bytes in page
|
@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.
|
@brief Allocate a new page on the heap with the given properties.
|
||||||
|
|
||||||
@details Allocates a new page using malloc with the given size and
|
@details Allocates a new page using malloc with the given size and pointer to
|
||||||
pointer to next page. NOTE: all memory is 0 initialised by
|
next page. NOTE: all memory is 0 initialised by default.
|
||||||
default.
|
|
||||||
|
|
||||||
@param[max] Maximum available memory in page
|
@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
|
@brief Delete a page, freeing its memory
|
||||||
|
|
||||||
@details Free's the memory associated with the page via free().
|
@details Free's the memory associated with the page via free(). NOTE: any
|
||||||
NOTE: any pointers to the page's memory are considered invalid once
|
pointers to the page's memory are considered invalid once this is called.
|
||||||
this is called.
|
|
||||||
|
|
||||||
@param[page] Page to delete
|
@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
|
@brief A collection of pages through which generic allocations can
|
||||||
occur.
|
occur.
|
||||||
|
|
||||||
@details Collection of pages maintained through a vector of
|
@details Collection of pages maintained through a vector of pointers to
|
||||||
pointers to pages.
|
pages.
|
||||||
|
|
||||||
@prop[page_vec] Vector of pages
|
@prop[page_vec] Vector of pages
|
||||||
*/
|
*/
|
||||||
@@ -81,9 +78,8 @@ typedef struct
|
|||||||
/**
|
/**
|
||||||
@brief Instantiate a new heap structure
|
@brief Instantiate a new heap structure
|
||||||
|
|
||||||
@details Initialises the heap structure given. No heap allocation
|
@details Initialises the heap structure given. No heap allocation occurs
|
||||||
occurs here until a new page is created, so this may be called
|
here until a new page is created, so this may be called safely.
|
||||||
safely.
|
|
||||||
|
|
||||||
@param[heap] Pointer to heap to initialise
|
@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
|
@brief Allocate a new page on the heap
|
||||||
|
|
||||||
@details Creates and joins a new page onto the linked list
|
@details Creates and joins a new page onto the linked list maintained by the
|
||||||
maintained by the heap. heap.end is set to this new page.
|
heap. heap.end is set to this new page.
|
||||||
|
|
||||||
@param[heap] Heap to create a new page on
|
@param[heap] Heap to create a new page on
|
||||||
@param[size] Size of page to allocate
|
@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
|
@brief Free a page of memory from the heap
|
||||||
|
|
||||||
@details The page given is removed from the linked list of pages
|
@details The page given is removed from the linked list of pages then freed
|
||||||
then freed from the heap via page_delete(). If the page does not
|
from the heap via page_delete(). If the page does not belong to this heap
|
||||||
belong to this heap (O(heap.pages) time) then false is returned,
|
(O(heap.pages) time) then false is returned, otherwise true.
|
||||||
otherwise true.
|
|
||||||
|
|
||||||
@param[heap] Heap to free page from
|
@param[heap] Heap to free page from
|
||||||
@param[page] Page to delete
|
@param[page] Page to delete
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2024 Aryadev Chavali
|
/* Copyright (C) 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2024-04-28
|
* Created: 2024-04-28
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
|
|||||||
62
lib/inst.c
62
lib/inst.c
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-10-15
|
* Created: 2023-10-15
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
@@ -77,14 +76,6 @@ const char *opcode_as_cstr(opcode_t code)
|
|||||||
return "MALLOC_HWORD";
|
return "MALLOC_HWORD";
|
||||||
case OP_MALLOC_WORD:
|
case OP_MALLOC_WORD:
|
||||||
return "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:
|
case OP_MSET_BYTE:
|
||||||
return "MSET_BYTE";
|
return "MSET_BYTE";
|
||||||
case OP_MSET_SHORT:
|
case OP_MSET_SHORT:
|
||||||
@@ -93,14 +84,6 @@ const char *opcode_as_cstr(opcode_t code)
|
|||||||
return "MSET_HWORD";
|
return "MSET_HWORD";
|
||||||
case OP_MSET_WORD:
|
case OP_MSET_WORD:
|
||||||
return "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:
|
case OP_MGET_BYTE:
|
||||||
return "MGET_BYTE";
|
return "MGET_BYTE";
|
||||||
case OP_MGET_SHORT:
|
case OP_MGET_SHORT:
|
||||||
@@ -109,14 +92,6 @@ const char *opcode_as_cstr(opcode_t code)
|
|||||||
return "MGET_HWORD";
|
return "MGET_HWORD";
|
||||||
case OP_MGET_WORD:
|
case OP_MGET_WORD:
|
||||||
return "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:
|
case OP_MDELETE:
|
||||||
return "MDELETE";
|
return "MDELETE";
|
||||||
case OP_MSIZE:
|
case OP_MSIZE:
|
||||||
@@ -267,8 +242,6 @@ const char *opcode_as_cstr(opcode_t code)
|
|||||||
return "PRINT_SWORD";
|
return "PRINT_SWORD";
|
||||||
case OP_JUMP_ABS:
|
case OP_JUMP_ABS:
|
||||||
return "JUMP_ABS";
|
return "JUMP_ABS";
|
||||||
case OP_JUMP_STACK:
|
|
||||||
return "JUMP_STACK";
|
|
||||||
case OP_JUMP_IF_BYTE:
|
case OP_JUMP_IF_BYTE:
|
||||||
return "JUMP_IF_BYTE";
|
return "JUMP_IF_BYTE";
|
||||||
case OP_JUMP_IF_SHORT:
|
case OP_JUMP_IF_SHORT:
|
||||||
@@ -279,8 +252,6 @@ const char *opcode_as_cstr(opcode_t code)
|
|||||||
return "JUMP_IF_WORD";
|
return "JUMP_IF_WORD";
|
||||||
case OP_CALL:
|
case OP_CALL:
|
||||||
return "CALL";
|
return "CALL";
|
||||||
case OP_CALL_STACK:
|
|
||||||
return "CALL_STACK";
|
|
||||||
case OP_RET:
|
case OP_RET:
|
||||||
return "RET";
|
return "RET";
|
||||||
case NUMBER_OF_OPCODES:
|
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)
|
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));
|
fprintf(fp, "%s(", opcode_as_cstr(instruction.opcode));
|
||||||
if (UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_PUSH))
|
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");
|
fprintf(fp, "reg=0x");
|
||||||
data_print(instruction.operand, DATA_TYPE_BYTE, fp);
|
data_print(instruction.operand, DATA_TYPE_BYTE, fp);
|
||||||
}
|
}
|
||||||
else if (UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_DUP) ||
|
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))
|
|
||||||
{
|
{
|
||||||
fprintf(fp, "n=0x%lX", instruction.operand.as_word);
|
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)
|
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
|
size_t size = 1; // for opcode
|
||||||
if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH))
|
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)
|
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;
|
bytes[0] = inst.opcode;
|
||||||
size_t written = 1;
|
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_DUP) ||
|
||||||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_MOV) ||
|
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_MOV) ||
|
||||||
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_DUP) ||
|
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) ||
|
UNSIGNED_OPCODE_IS_TYPE(inst.opcode, OP_JUMP_IF) ||
|
||||||
inst.opcode == OP_JUMP_ABS || inst.opcode == OP_CALL)
|
inst.opcode == OP_JUMP_ABS || inst.opcode == OP_CALL)
|
||||||
to_append = DATA_TYPE_WORD;
|
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)
|
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++);
|
opcode_t opcode = *(bytes++);
|
||||||
if (opcode >= NUMBER_OF_OPCODES || opcode < OP_NOOP)
|
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))
|
if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH))
|
||||||
success = read_type_from_darr(bytes, size_bytes, (data_type_t)opcode,
|
success = read_type_from_darr(bytes, size_bytes, (data_type_t)opcode,
|
||||||
&inst.operand);
|
&inst.operand);
|
||||||
// Read register (as a byte)
|
// Read operand as a word
|
||||||
else if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH_REGISTER) ||
|
else if (UNSIGNED_OPCODE_IS_TYPE(opcode, OP_PUSH_REGISTER) ||
|
||||||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MOV) ||
|
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_MOV) ||
|
||||||
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_DUP) ||
|
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) ||
|
UNSIGNED_OPCODE_IS_TYPE(opcode, OP_JUMP_IF) ||
|
||||||
opcode == OP_JUMP_ABS || opcode == OP_CALL)
|
opcode == OP_JUMP_ABS || opcode == OP_CALL)
|
||||||
success =
|
success =
|
||||||
|
|||||||
56
lib/inst.h
56
lib/inst.h
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-10-15
|
* Created: 2023-10-15
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
@@ -65,31 +64,16 @@ typedef enum
|
|||||||
OP_MALLOC_HWORD,
|
OP_MALLOC_HWORD,
|
||||||
OP_MALLOC_WORD,
|
OP_MALLOC_WORD,
|
||||||
|
|
||||||
OP_MALLOC_STACK_BYTE,
|
|
||||||
OP_MALLOC_STACK_SHORT,
|
|
||||||
OP_MALLOC_STACK_HWORD,
|
|
||||||
OP_MALLOC_STACK_WORD,
|
|
||||||
|
|
||||||
OP_MSET_BYTE,
|
OP_MSET_BYTE,
|
||||||
OP_MSET_SHORT,
|
OP_MSET_SHORT,
|
||||||
OP_MSET_HWORD,
|
OP_MSET_HWORD,
|
||||||
OP_MSET_WORD,
|
OP_MSET_WORD,
|
||||||
|
|
||||||
OP_MSET_STACK_BYTE,
|
|
||||||
OP_MSET_STACK_SHORT,
|
|
||||||
OP_MSET_STACK_HWORD,
|
|
||||||
OP_MSET_STACK_WORD,
|
|
||||||
|
|
||||||
OP_MGET_BYTE,
|
OP_MGET_BYTE,
|
||||||
OP_MGET_SHORT,
|
OP_MGET_SHORT,
|
||||||
OP_MGET_HWORD,
|
OP_MGET_HWORD,
|
||||||
OP_MGET_WORD,
|
OP_MGET_WORD,
|
||||||
|
|
||||||
OP_MGET_STACK_BYTE,
|
|
||||||
OP_MGET_STACK_SHORT,
|
|
||||||
OP_MGET_STACK_HWORD,
|
|
||||||
OP_MGET_STACK_WORD,
|
|
||||||
|
|
||||||
OP_MDELETE,
|
OP_MDELETE,
|
||||||
OP_MSIZE,
|
OP_MSIZE,
|
||||||
|
|
||||||
@@ -184,7 +168,6 @@ typedef enum
|
|||||||
|
|
||||||
// Program control flow
|
// Program control flow
|
||||||
OP_JUMP_ABS,
|
OP_JUMP_ABS,
|
||||||
OP_JUMP_STACK,
|
|
||||||
OP_JUMP_IF_BYTE,
|
OP_JUMP_IF_BYTE,
|
||||||
OP_JUMP_IF_SHORT,
|
OP_JUMP_IF_SHORT,
|
||||||
OP_JUMP_IF_HWORD,
|
OP_JUMP_IF_HWORD,
|
||||||
@@ -192,7 +175,6 @@ typedef enum
|
|||||||
|
|
||||||
// Subroutines
|
// Subroutines
|
||||||
OP_CALL,
|
OP_CALL,
|
||||||
OP_CALL_STACK,
|
|
||||||
OP_RET,
|
OP_RET,
|
||||||
|
|
||||||
// Should not be an opcode
|
// Should not be an opcode
|
||||||
@@ -211,10 +193,10 @@ typedef struct
|
|||||||
/**
|
/**
|
||||||
@brief Serialise an instruction into a byte buffer
|
@brief Serialise an instruction into a byte buffer
|
||||||
|
|
||||||
@details Given an instruction and a suitably sized byte buffer,
|
@details Given an instruction and a suitably sized byte buffer, write the
|
||||||
write the bytecode for the instruction into the buffer. NOTE: This
|
bytecode for the instruction into the buffer. NOTE: This function does NOT
|
||||||
function does NOT check the bounds of `bytes` i.e. we assume the
|
check the bounds of `bytes` i.e. we assume the caller has created a suitably
|
||||||
caller has created a suitably sized buffer.
|
sized buffer.
|
||||||
|
|
||||||
@param[inst] Instruction to serialise
|
@param[inst] Instruction to serialise
|
||||||
@param[bytes] Buffer to write on
|
@param[bytes] Buffer to write on
|
||||||
@@ -234,21 +216,19 @@ typedef enum
|
|||||||
/**
|
/**
|
||||||
@brief Deserialise an instruction from a bytecode buffer
|
@brief Deserialise an instruction from a bytecode buffer
|
||||||
|
|
||||||
@details Given a buffer of bytes, deserialise an instruction,
|
@details Given a buffer of bytes, deserialise an instruction, storing the
|
||||||
storing the result in the pointer given. The number of bytes read
|
result in the pointer given. The number of bytes read in the buffer is
|
||||||
in the buffer is returned, which should be opcode_bytecode_size().
|
returned, which should be opcode_bytecode_size(). NOTE: If bytes is not
|
||||||
NOTE: If bytes is not suitably sized for the instruction expected
|
suitably sized for the instruction expected or it is not well formed i.e. not
|
||||||
or it is not well formed i.e. not the right schema then a negative
|
the right schema then a negative number is returned.
|
||||||
number is returned.
|
|
||||||
|
|
||||||
@param[inst] Pointer to instruction which will store result
|
@param[inst] Pointer to instruction which will store result
|
||||||
@param[bytes] Bytecode buffer to deserialise
|
@param[bytes] Bytecode buffer to deserialise
|
||||||
@param[size_bytes] Number of bytes in buffer
|
@param[size_bytes] Number of bytes in buffer
|
||||||
|
|
||||||
@return[int] Number of bytes read. If negative then an error
|
@return[int] Number of bytes read. If negative then an error occurred in
|
||||||
occurred in deserialisation (either buffer was not suitably sized
|
deserialisation (either buffer was not suitably sized or instruction was not
|
||||||
or instruction was not well formed) so any result must be
|
well formed) so any result must be considered invalid.
|
||||||
considered invalid.
|
|
||||||
*/
|
*/
|
||||||
int inst_read_bytecode(inst_t *inst, byte_t *bytes, size_t size_bytes);
|
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
|
#+description: A specification of instructions for the virtual machine
|
||||||
#+date: 2023-11-02
|
#+date: 2023-11-02
|
||||||
|
|
||||||
* WIP Data types
|
* Data types
|
||||||
There are 3 main data types of the virtual machine. They are all
|
There are 4 main data types of the virtual machine. They are all
|
||||||
unsigned. There exist signed versions of these data types, though
|
unsigned.
|
||||||
there is no difference (internally) between them. For an unsigned
|
|
||||||
type <T> the signed version is simply S_<T>.
|
|
||||||
|-------+------|
|
|-------+------|
|
||||||
| Name | Bits |
|
| Name | Bits |
|
||||||
|-------+------|
|
|-------+------|
|
||||||
| Byte | 8 |
|
| Byte | 8 |
|
||||||
|
| Short | 16 |
|
||||||
| HWord | 32 |
|
| HWord | 32 |
|
||||||
| Word | 64 |
|
| Word | 64 |
|
||||||
|-------+------|
|
|-------+------|
|
||||||
|
|
||||||
Generally, the abbreviations B, H and W are used for Byte, HWord and
|
Generally, the abbreviations B, S, H and W are used for Byte, Short,
|
||||||
Word respectively. The following table shows a comparison between the
|
HWord and Word respectively. The following table shows a comparison
|
||||||
data types where an entry (row and column) $A\times{B}$ refers to "How
|
between the data types where an entry (row and column) $A\times{B}$
|
||||||
many of A can I fit in B".
|
refers to "How many of A can I fit in B".
|
||||||
|-------+------+-------+------|
|
|-------+------+-------+-------+------|
|
||||||
| | Byte | Hword | Word |
|
| | Byte | Short | HWord | Word |
|
||||||
|-------+------+-------+------|
|
|-------+------+-------+-------+------|
|
||||||
| Byte | 1 | 4 | 8 |
|
| Byte | 1 | 2 | 4 | 8 |
|
||||||
| HWord | 1/4 | 1 | 2 |
|
| Short | 1/2 | 1 | 2 | 4 |
|
||||||
| Word | 1/8 | 1/2 | 1 |
|
| 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
|
* WIP Instructions
|
||||||
An instruction for the virtual machine is composed of an *opcode* and,
|
An instruction for the virtual machine is composed of an *opcode* and,
|
||||||
potentially, an *operand*. The /opcode/ represents the behaviour of
|
optionally, an *operand*. The /opcode/ represents the specific
|
||||||
the instruction i.e. what _is_ the instruction. The /operand/ is an
|
behaviour of the instruction i.e. what the instruction does. The
|
||||||
element of one of the /data types/ described previously.
|
/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
|
The *order* of an operation is the number of specialisations it has
|
||||||
type of instructions are called *UNIT* instructions while the latter
|
i.e. the number of opcodes that specialise one operation.
|
||||||
type are called *MULTI* instructions[fn:1].
|
|
||||||
|
|
||||||
All /opcodes/ (with very few exceptions[fn:2]) have two components:
|
Some operations may not be generic over data types in which case they
|
||||||
the *root* and the *type specifier*. The /root/ represents the
|
are of order 1 i.e. the opcode describes the exact behaviour of only
|
||||||
general behaviour of the instruction: ~PUSH~, ~POP~, ~MOV~, etc. The
|
one operation.
|
||||||
/type specifier/ specifies what /data type/ it manipulates. A
|
|
||||||
complete opcode will be a combination of these two e.g. ~PUSH_BYTE~,
|
There are only 3 possible orders for operations: 1, 4 and 8. They are
|
||||||
~POP_WORD~, etc. Some /opcodes/ may have more /type specifiers/ than
|
given the names Nil, Unsigned and Signed for specialising over:
|
||||||
others.
|
+ 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
|
* TODO Bytecode format
|
||||||
Bytecode files are byte sequence which encode instructions for the
|
Bytecode files are byte sequence which encode instructions for the
|
||||||
virtual machine. Any instruction (even with an operand) has one and
|
virtual machine. Any instruction (even with an operand) has one and
|
||||||
only one byte sequence associated with it.
|
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
|
* 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
|
/* Copyright (C) 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2024-04-28
|
* Created: 2024-04-28
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2024 Aryadev Chavali
|
/* Copyright (C) 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2024-04-28
|
* Created: 2024-04-28
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2024 Aryadev Chavali
|
/* Copyright (C) 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2024-04-28
|
* Created: 2024-04-28
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
@@ -71,8 +70,8 @@ void test_lib_darr_ensure_capacity_expands(void)
|
|||||||
{10, 10, 10, 20},
|
{10, 10, 10, 20},
|
||||||
{50, 100, 300, 350},
|
{50, 100, 300, 350},
|
||||||
{1 << 20, 2 << 20, 2 << 20, 3 << 20},
|
{1 << 20, 2 << 20, 2 << 20, 3 << 20},
|
||||||
// When we reallocate we allocate MORE than needed (for
|
// When we reallocate we allocate MORE than needed (for amortized
|
||||||
// amortized constant)
|
// constant)
|
||||||
{1, 5, 5, 10},
|
{1, 5, 5, 10},
|
||||||
{85, 100, 40, 200},
|
{85, 100, 40, 200},
|
||||||
{4 << 20, 5 << 20, 1 << 23, 5 << 21},
|
{4 << 20, 5 << 20, 1 << 23, 5 << 21},
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2024 Aryadev Chavali
|
/* Copyright (C) 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2024-04-28
|
* Created: 2024-04-28
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
@@ -25,16 +24,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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);
|
typedef void (*test_fn)(void);
|
||||||
struct Test
|
struct Test
|
||||||
{
|
{
|
||||||
|
|||||||
147
todo.org
147
todo.org
@@ -3,6 +3,153 @@
|
|||||||
#+date: 2023-11-02
|
#+date: 2023-11-02
|
||||||
#+startup: noindent
|
#+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
|
* TODO Rework heap to use one allocation
|
||||||
The current approach for the heap is so:
|
The current approach for the heap is so:
|
||||||
+ Per call to ~malloc~, allocate a new ~page_t~ structure by
|
+ 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
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-10-15
|
* Created: 2023-10-15
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
* Description: Entrypoint to program
|
* Description: Entrypoint to program
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "lib/base.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ int main(int argc, char *argv[])
|
|||||||
const char *filename = argv[1];
|
const char *filename = argv[1];
|
||||||
|
|
||||||
#if VERBOSE >= 1
|
#if VERBOSE >= 1
|
||||||
printf("[" TERM_YELLOW "INTERPRETER" TERM_RESET "]: `%s`\n", filename);
|
INFO("INTERPRETER", "`%s`\n", filename);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILE *fp = fopen(filename, "rb");
|
FILE *fp = fopen(filename, "rb");
|
||||||
@@ -53,16 +53,14 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (!header_read)
|
if (!header_read)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "[ERROR]: Could not deserialise program header in `%s`\n",
|
FAIL("ERROR", "Could not deserialise program header in `%s`\n", filename);
|
||||||
filename);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// Ensure that we MUST have something to read
|
// Ensure that we MUST have something to read
|
||||||
else if (program.count == 0)
|
else if (program.count == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// After reading header, we can allocate the buffer of instrutions
|
// After reading header, we can allocate the buffer of instrutions exactly
|
||||||
// exactly
|
|
||||||
program.instructions = calloc(program.count, sizeof(*program.instructions));
|
program.instructions = calloc(program.count, sizeof(*program.instructions));
|
||||||
size_t bytes_read = 0;
|
size_t bytes_read = 0;
|
||||||
read_err_prog_t read_err =
|
read_err_prog_t read_err =
|
||||||
@@ -71,7 +69,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (bytes_read == 0)
|
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)
|
switch (read_err.type)
|
||||||
{
|
{
|
||||||
case READ_ERR_INVALID_OPCODE:
|
case READ_ERR_INVALID_OPCODE:
|
||||||
@@ -93,8 +91,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if VERBOSE >= 1
|
#if VERBOSE >= 1
|
||||||
printf("\t[" TERM_GREEN "SETUP" TERM_RESET "]: Read %lu instructions\n",
|
SUCCESS("SETUP", "Read %lu instructions\n", program.count);
|
||||||
program.count);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
size_t stack_size = 256;
|
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);
|
vm_load_call_stack(&vm, call_stack, call_stack_size);
|
||||||
|
|
||||||
#if VERBOSE >= 1
|
#if VERBOSE >= 1
|
||||||
printf("\t[" TERM_GREEN "SETUP" TERM_RESET
|
SUCCESS("SETUP", "Loaded internals\n%s", "");
|
||||||
"]: Loaded stack and program into VM\n");
|
INFO("INTERPRETER", "Beginning execution\n%s", "");
|
||||||
#endif
|
|
||||||
#if VERBOSE >= 1
|
|
||||||
printf("[" TERM_YELLOW "INTERPRETER" TERM_RESET "]: Beginning execution\n");
|
|
||||||
#endif
|
#endif
|
||||||
err_t err = vm_execute_all(&vm);
|
err_t err = vm_execute_all(&vm);
|
||||||
|
|
||||||
@@ -126,7 +120,7 @@ int main(int argc, char *argv[])
|
|||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
const char *error_str = err_as_cstr(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);
|
vm_print_all(&vm, stderr);
|
||||||
ret = 255 - err;
|
ret = 255 - err;
|
||||||
}
|
}
|
||||||
@@ -134,7 +128,7 @@ int main(int argc, char *argv[])
|
|||||||
vm_stop(&vm);
|
vm_stop(&vm);
|
||||||
|
|
||||||
#if VERBOSE >= 1
|
#if VERBOSE >= 1
|
||||||
printf("[%sINTERPRETER%s]: Finished execution\n", TERM_GREEN, TERM_RESET);
|
SUCCESS("INTEPRETER", "Finished execution\n%s", "");
|
||||||
#endif
|
#endif
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
172
vm/runtime.c
172
vm/runtime.c
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-10-15
|
* Created: 2023-10-15
|
||||||
* Author: Aryadev Chavali
|
* 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)
|
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 "
|
"Code using OPCODE_DATA_TYPE for quick same type opcode "
|
||||||
"conversion may be out of date.");
|
"conversion may be out of date.");
|
||||||
|
|
||||||
// NOTE: We always use the first register to hold the result of
|
// NOTE: We always use the first register to hold the result of this pop.
|
||||||
// this pop.
|
|
||||||
|
|
||||||
// Here we add OP_MOV_BYTE and the data_type_t of the opcode to
|
// Here we add OP_MOV_BYTE and the data_type_t of the opcode to get the
|
||||||
// get the right typed OP_MOV opcode.
|
// right typed OP_MOV opcode.
|
||||||
opcode_t mov_opcode =
|
opcode_t mov_opcode =
|
||||||
OPCODE_DATA_TYPE(instruction.opcode, OP_POP) + OP_MOV_BYTE;
|
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_PLUS) ||
|
||||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_SUB) ||
|
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_SUB) ||
|
||||||
UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_MULT) ||
|
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_LT) ||
|
||||||
SIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_LTE) ||
|
SIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_LTE) ||
|
||||||
SIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_GT) ||
|
SIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_GT) ||
|
||||||
SIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_GTE) ||
|
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)
|
instruction.opcode == OP_MDELETE || instruction.opcode == OP_MSIZE)
|
||||||
{
|
{
|
||||||
err_t err = STACK_ROUTINES[instruction.opcode](vm);
|
err_t err = STACK_ROUTINES[instruction.opcode](vm);
|
||||||
@@ -137,15 +135,6 @@ err_t vm_execute(vm_t *vm)
|
|||||||
// Opcodes defined in loop
|
// Opcodes defined in loop
|
||||||
else if (instruction.opcode == OP_JUMP_ABS)
|
else if (instruction.opcode == OP_JUMP_ABS)
|
||||||
return vm_jump(vm, instruction.operand.as_word);
|
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))
|
else if (UNSIGNED_OPCODE_IS_TYPE(instruction.opcode, OP_JUMP_IF))
|
||||||
{
|
{
|
||||||
static_assert(DATA_TYPE_NIL == -1 && DATA_TYPE_WORD == 3,
|
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.");
|
"conversion may be out of date.");
|
||||||
|
|
||||||
data_t datum = {0};
|
data_t datum = {0};
|
||||||
// Here we add OP_POP_BYTE and the data_type_t of the opcode to
|
// Here we add OP_POP_BYTE and the data_type_t of the opcode to get the
|
||||||
// get the right OP_POP opcode.
|
// right OP_POP opcode.
|
||||||
opcode_t pop_opcode =
|
opcode_t pop_opcode =
|
||||||
OPCODE_DATA_TYPE(instruction.opcode, OP_JUMP_IF) + OP_POP_BYTE;
|
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;
|
vm->call_stack.address_pointers[vm->call_stack.ptr++] = vm->program.ptr + 1;
|
||||||
return vm_jump(vm, instruction.operand.as_word);
|
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)
|
else if (instruction.opcode == OP_RET)
|
||||||
{
|
{
|
||||||
if (vm->call_stack.ptr == 0)
|
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
|
// 2) create a format string for each datum type possible
|
||||||
|
|
||||||
// TODO: Figure out a way to ensure the ordering of OP_PRINT_* is
|
// TODO: Figure out a way to ensure the ordering of OP_PRINT_* is exactly
|
||||||
// exactly BYTE, SBYTE, SHORT, SSHORT, HWORD, SHWORD, WORD, SWORD
|
// BYTE, SBYTE, SHORT, SSHORT, HWORD, SHWORD, WORD, SWORD via static_assert
|
||||||
// via static_assert
|
|
||||||
|
|
||||||
// lookup table
|
// lookup table
|
||||||
const char *format_strings[] = {
|
const char *format_strings[] = {
|
||||||
@@ -286,7 +263,7 @@ err_t vm_execute_all(vm_t *vm)
|
|||||||
program->data.instructions[program->ptr].opcode != OP_HALT)
|
program->data.instructions[program->ptr].opcode != OP_HALT)
|
||||||
{
|
{
|
||||||
#if VERBOSE >= 2
|
#if VERBOSE >= 2
|
||||||
fprintf(stdout, "[vm_execute_all]: Trace(Cycle %lu)\n", cycles);
|
INFO("vm_execute_all", "Trace(Cycle%lu)\n", cycles);
|
||||||
fputs(
|
fputs(
|
||||||
"----------------------------------------------------------------------"
|
"----------------------------------------------------------------------"
|
||||||
"----------\n",
|
"----------\n",
|
||||||
@@ -342,8 +319,7 @@ err_t vm_execute_all(vm_t *vm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if VERBOSE >= 1
|
#if VERBOSE >= 1
|
||||||
fprintf(stdout, "[%svm_execute_all%s]: Final VM state(Cycle %lu)\n",
|
INFO("vm_execute_all", "Final VM State(Cycle %lu)\n", cycles);
|
||||||
TERM_YELLOW, TERM_RESET, cycles);
|
|
||||||
vm_print_all(vm, stdout);
|
vm_print_all(vm, stdout);
|
||||||
#endif
|
#endif
|
||||||
return err;
|
return err;
|
||||||
@@ -514,11 +490,15 @@ VM_DUP_CONSTR(short, SHORT)
|
|||||||
VM_DUP_CONSTR(hword, HWORD)
|
VM_DUP_CONSTR(hword, HWORD)
|
||||||
VM_DUP_CONSTR(word, WORD)
|
VM_DUP_CONSTR(word, WORD)
|
||||||
|
|
||||||
#define VM_MALLOC_CONSTR(TYPE, TYPE_CAP) \
|
#define VM_MALLOC_CONSTR(TYPE, TYPE_CAP) \
|
||||||
err_t vm_malloc_##TYPE(vm_t *vm, word_t n) \
|
err_t vm_malloc_##TYPE(vm_t *vm) \
|
||||||
{ \
|
{ \
|
||||||
page_t *page = heap_allocate(&vm->heap, n * TYPE_CAP##_SIZE); \
|
data_t n = {0}; \
|
||||||
return vm_push_word(vm, DWORD((word_t)page)); \
|
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)
|
VM_MALLOC_CONSTR(byte, BYTE)
|
||||||
@@ -526,22 +506,26 @@ VM_MALLOC_CONSTR(short, SHORT)
|
|||||||
VM_MALLOC_CONSTR(hword, HWORD)
|
VM_MALLOC_CONSTR(hword, HWORD)
|
||||||
VM_MALLOC_CONSTR(word, WORD)
|
VM_MALLOC_CONSTR(word, WORD)
|
||||||
|
|
||||||
#define VM_MSET_CONSTR(TYPE, TYPE_CAP) \
|
#define VM_MSET_CONSTR(TYPE, TYPE_CAP) \
|
||||||
err_t vm_mset_##TYPE(vm_t *vm, word_t nth) \
|
err_t vm_mset_##TYPE(vm_t *vm) \
|
||||||
{ \
|
{ \
|
||||||
data_t object = {0}; \
|
data_t n = {0}; \
|
||||||
err_t err = vm_pop_##TYPE(vm, &object); \
|
err_t err = vm_pop_word(vm, &n); \
|
||||||
if (err) \
|
if (err) \
|
||||||
return err; \
|
return err; \
|
||||||
data_t ptr = {0}; \
|
data_t object = {0}; \
|
||||||
err = vm_pop_word(vm, &ptr); \
|
err = vm_pop_##TYPE(vm, &object); \
|
||||||
if (err) \
|
if (err) \
|
||||||
return err; \
|
return err; \
|
||||||
page_t *page = (page_t *)ptr.as_word; \
|
data_t ptr = {0}; \
|
||||||
if (nth >= (page->available / TYPE_CAP##_SIZE)) \
|
err = vm_pop_word(vm, &ptr); \
|
||||||
return ERR_OUT_OF_BOUNDS; \
|
if (err) \
|
||||||
DARR_AT(TYPE##_t, page->data, nth) = object.as_##TYPE; \
|
return err; \
|
||||||
return ERR_OK; \
|
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)
|
VM_MSET_CONSTR(byte, BYTE)
|
||||||
@@ -549,22 +533,26 @@ VM_MSET_CONSTR(short, SHORT)
|
|||||||
VM_MSET_CONSTR(hword, HWORD)
|
VM_MSET_CONSTR(hword, HWORD)
|
||||||
VM_MSET_CONSTR(word, WORD)
|
VM_MSET_CONSTR(word, WORD)
|
||||||
|
|
||||||
#define VM_MGET_CONSTR(TYPE, TYPE_CAP) \
|
#define VM_MGET_CONSTR(TYPE, TYPE_CAP) \
|
||||||
err_t vm_mget_##TYPE(vm_t *vm, word_t n) \
|
err_t vm_mget_##TYPE(vm_t *vm) \
|
||||||
{ \
|
{ \
|
||||||
data_t ptr = {0}; \
|
data_t n = {0}; \
|
||||||
err_t err = vm_pop_word(vm, &ptr); \
|
err_t err = vm_pop_word(vm, &n); \
|
||||||
if (err) \
|
if (err) \
|
||||||
return err; \
|
return (err); \
|
||||||
page_t *page = (page_t *)ptr.as_word; \
|
data_t ptr = {0}; \
|
||||||
if (n >= (page->available / TYPE_CAP##_SIZE)) \
|
err = vm_pop_word(vm, &ptr); \
|
||||||
return ERR_OUT_OF_BOUNDS; \
|
if (err) \
|
||||||
else if (vm->stack.ptr + TYPE_CAP##_SIZE >= vm->stack.max) \
|
return err; \
|
||||||
return ERR_STACK_OVERFLOW; \
|
page_t *page = (page_t *)ptr.as_word; \
|
||||||
memcpy(vm->stack.data + vm->stack.ptr, \
|
if (n.as_word >= (page->available / TYPE_CAP##_SIZE)) \
|
||||||
page->data + (n * (TYPE_CAP##_SIZE)), TYPE_CAP##_SIZE); \
|
return ERR_OUT_OF_BOUNDS; \
|
||||||
vm->stack.ptr += TYPE_CAP##_SIZE; \
|
else if (vm->stack.ptr + TYPE_CAP##_SIZE >= vm->stack.max) \
|
||||||
return ERR_OK; \
|
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)
|
VM_MGET_CONSTR(byte, BYTE)
|
||||||
@@ -572,30 +560,6 @@ VM_MGET_CONSTR(short, SHORT)
|
|||||||
VM_MGET_CONSTR(hword, HWORD)
|
VM_MGET_CONSTR(hword, HWORD)
|
||||||
VM_MGET_CONSTR(word, WORD)
|
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)
|
err_t vm_mdelete(vm_t *vm)
|
||||||
{
|
{
|
||||||
data_t ptr = {0};
|
data_t ptr = {0};
|
||||||
|
|||||||
185
vm/runtime.h
185
vm/runtime.h
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
/* Copyright (C) 2023, 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2023-10-15
|
* Created: 2023-10-15
|
||||||
* Author: Aryadev Chavali
|
* 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_hword(vm_t *, word_t);
|
||||||
err_t vm_dup_word(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);
|
typedef err_t (*word_f)(vm_t *, word_t);
|
||||||
static const word_f WORD_ROUTINES[] = {
|
static const word_f WORD_ROUTINES[] = {
|
||||||
[OP_PUSH_REGISTER_BYTE] = vm_push_byte_register,
|
[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_SHORT] = vm_dup_short,
|
||||||
[OP_DUP_HWORD] = vm_dup_hword,
|
[OP_DUP_HWORD] = vm_dup_hword,
|
||||||
[OP_DUP_WORD] = vm_dup_word,
|
[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 */
|
/* Operations that take input from the stack */
|
||||||
err_t vm_malloc_stack_byte(vm_t *);
|
err_t vm_malloc_byte(vm_t *);
|
||||||
err_t vm_malloc_stack_short(vm_t *);
|
err_t vm_malloc_short(vm_t *);
|
||||||
err_t vm_malloc_stack_hword(vm_t *);
|
err_t vm_malloc_hword(vm_t *);
|
||||||
err_t vm_malloc_stack_word(vm_t *);
|
err_t vm_malloc_word(vm_t *);
|
||||||
|
|
||||||
err_t vm_mset_stack_byte(vm_t *);
|
err_t vm_mset_byte(vm_t *);
|
||||||
err_t vm_mset_stack_short(vm_t *);
|
err_t vm_mset_short(vm_t *);
|
||||||
err_t vm_mset_stack_hword(vm_t *);
|
err_t vm_mset_hword(vm_t *);
|
||||||
err_t vm_mset_stack_word(vm_t *);
|
err_t vm_mset_word(vm_t *);
|
||||||
|
|
||||||
err_t vm_mget_stack_byte(vm_t *);
|
err_t vm_mget_byte(vm_t *);
|
||||||
err_t vm_mget_stack_short(vm_t *);
|
err_t vm_mget_short(vm_t *);
|
||||||
err_t vm_mget_stack_hword(vm_t *);
|
err_t vm_mget_hword(vm_t *);
|
||||||
err_t vm_mget_stack_word(vm_t *);
|
err_t vm_mget_word(vm_t *);
|
||||||
|
|
||||||
err_t vm_mdelete(vm_t *);
|
err_t vm_mdelete(vm_t *);
|
||||||
err_t vm_msize(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 *);
|
typedef err_t (*stack_f)(vm_t *);
|
||||||
static const stack_f STACK_ROUTINES[] = {
|
static const stack_f STACK_ROUTINES[] = {
|
||||||
[OP_MALLOC_STACK_BYTE] = vm_malloc_stack_byte,
|
[OP_MALLOC_BYTE] = vm_malloc_byte, [OP_MALLOC_SHORT] = vm_malloc_short,
|
||||||
[OP_MALLOC_STACK_SHORT] = vm_malloc_stack_short,
|
[OP_MALLOC_HWORD] = vm_malloc_hword, [OP_MALLOC_WORD] = vm_malloc_word,
|
||||||
[OP_MALLOC_STACK_HWORD] = vm_malloc_stack_hword,
|
|
||||||
[OP_MALLOC_STACK_WORD] = vm_malloc_stack_word,
|
|
||||||
|
|
||||||
[OP_MGET_STACK_BYTE] = vm_mget_stack_byte,
|
[OP_MGET_BYTE] = vm_mget_byte, [OP_MGET_SHORT] = vm_mget_short,
|
||||||
[OP_MGET_STACK_SHORT] = vm_mget_stack_short,
|
[OP_MGET_HWORD] = vm_mget_hword, [OP_MGET_WORD] = vm_mget_word,
|
||||||
[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_MDELETE] = vm_mdelete,
|
[OP_MSET_BYTE] = vm_mset_byte, [OP_MSET_SHORT] = vm_mset_short,
|
||||||
[OP_MSIZE] = vm_msize,
|
[OP_MSET_HWORD] = vm_mset_hword, [OP_MSET_WORD] = vm_mset_word,
|
||||||
|
|
||||||
[OP_NOT_BYTE] = vm_not_byte,
|
[OP_MDELETE] = vm_mdelete, [OP_MSIZE] = vm_msize,
|
||||||
[OP_NOT_SHORT] = vm_not_short,
|
|
||||||
[OP_NOT_HWORD] = vm_not_hword,
|
|
||||||
[OP_NOT_WORD] = vm_not_word,
|
|
||||||
|
|
||||||
[OP_OR_BYTE] = vm_or_byte,
|
[OP_NOT_BYTE] = vm_not_byte, [OP_NOT_SHORT] = vm_not_short,
|
||||||
[OP_OR_SHORT] = vm_or_short,
|
[OP_NOT_HWORD] = vm_not_hword, [OP_NOT_WORD] = vm_not_word,
|
||||||
[OP_OR_HWORD] = vm_or_hword,
|
|
||||||
[OP_OR_WORD] = vm_or_word,
|
|
||||||
|
|
||||||
[OP_AND_BYTE] = vm_and_byte,
|
[OP_OR_BYTE] = vm_or_byte, [OP_OR_SHORT] = vm_or_short,
|
||||||
[OP_AND_SHORT] = vm_and_short,
|
[OP_OR_HWORD] = vm_or_hword, [OP_OR_WORD] = vm_or_word,
|
||||||
[OP_AND_HWORD] = vm_and_hword,
|
|
||||||
[OP_AND_WORD] = vm_and_word,
|
|
||||||
|
|
||||||
[OP_XOR_BYTE] = vm_xor_byte,
|
[OP_AND_BYTE] = vm_and_byte, [OP_AND_SHORT] = vm_and_short,
|
||||||
[OP_XOR_SHORT] = vm_xor_short,
|
[OP_AND_HWORD] = vm_and_hword, [OP_AND_WORD] = vm_and_word,
|
||||||
[OP_XOR_HWORD] = vm_xor_hword,
|
|
||||||
[OP_XOR_WORD] = vm_xor_word,
|
|
||||||
|
|
||||||
[OP_EQ_BYTE] = vm_eq_byte,
|
[OP_XOR_BYTE] = vm_xor_byte, [OP_XOR_SHORT] = vm_xor_short,
|
||||||
[OP_EQ_SHORT] = vm_eq_short,
|
[OP_XOR_HWORD] = vm_xor_hword, [OP_XOR_WORD] = vm_xor_word,
|
||||||
[OP_EQ_HWORD] = vm_eq_hword,
|
|
||||||
[OP_EQ_WORD] = vm_eq_word,
|
|
||||||
|
|
||||||
[OP_LT_BYTE] = vm_lt_byte,
|
[OP_EQ_BYTE] = vm_eq_byte, [OP_EQ_SHORT] = vm_eq_short,
|
||||||
[OP_LT_SBYTE] = vm_lt_sbyte,
|
[OP_EQ_HWORD] = vm_eq_hword, [OP_EQ_WORD] = vm_eq_word,
|
||||||
[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_LTE_BYTE] = vm_lte_byte,
|
[OP_LT_BYTE] = vm_lt_byte, [OP_LT_SBYTE] = vm_lt_sbyte,
|
||||||
[OP_LTE_SBYTE] = vm_lte_sbyte,
|
[OP_LT_SHORT] = vm_lt_short, [OP_LT_SSHORT] = vm_lt_sshort,
|
||||||
[OP_LTE_SHORT] = vm_lte_short,
|
[OP_LT_SHWORD] = vm_lt_shword, [OP_LT_HWORD] = vm_lt_hword,
|
||||||
[OP_LTE_SSHORT] = vm_lte_sshort,
|
[OP_LT_SWORD] = vm_lt_sword, [OP_LT_WORD] = vm_lt_word,
|
||||||
[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_GT_BYTE] = vm_gt_byte,
|
[OP_LTE_BYTE] = vm_lte_byte, [OP_LTE_SBYTE] = vm_lte_sbyte,
|
||||||
[OP_GT_SBYTE] = vm_gt_sbyte,
|
[OP_LTE_SHORT] = vm_lte_short, [OP_LTE_SSHORT] = vm_lte_sshort,
|
||||||
[OP_GT_SHORT] = vm_gt_short,
|
[OP_LTE_SHWORD] = vm_lte_shword, [OP_LTE_HWORD] = vm_lte_hword,
|
||||||
[OP_GT_SSHORT] = vm_gt_sshort,
|
[OP_LTE_SWORD] = vm_lte_sword, [OP_LTE_WORD] = vm_lte_word,
|
||||||
[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_GTE_BYTE] = vm_gte_byte,
|
[OP_GT_BYTE] = vm_gt_byte, [OP_GT_SBYTE] = vm_gt_sbyte,
|
||||||
[OP_GTE_SBYTE] = vm_gte_sbyte,
|
[OP_GT_SHORT] = vm_gt_short, [OP_GT_SSHORT] = vm_gt_sshort,
|
||||||
[OP_GTE_SHORT] = vm_gte_short,
|
[OP_GT_SHWORD] = vm_gt_shword, [OP_GT_HWORD] = vm_gt_hword,
|
||||||
[OP_GTE_SSHORT] = vm_gte_sshort,
|
[OP_GT_SWORD] = vm_gt_sword, [OP_GT_WORD] = vm_gt_word,
|
||||||
[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_PLUS_BYTE] = vm_plus_byte,
|
[OP_GTE_BYTE] = vm_gte_byte, [OP_GTE_SBYTE] = vm_gte_sbyte,
|
||||||
[OP_PLUS_SHORT] = vm_plus_short,
|
[OP_GTE_SHORT] = vm_gte_short, [OP_GTE_SSHORT] = vm_gte_sshort,
|
||||||
[OP_PLUS_HWORD] = vm_plus_hword,
|
[OP_GTE_SHWORD] = vm_gte_shword, [OP_GTE_HWORD] = vm_gte_hword,
|
||||||
[OP_PLUS_WORD] = vm_plus_word,
|
[OP_GTE_SWORD] = vm_gte_sword, [OP_GTE_WORD] = vm_gte_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_PLUS_BYTE] = vm_plus_byte, [OP_PLUS_SHORT] = vm_plus_short,
|
||||||
[OP_MULT_SHORT] = vm_mult_short,
|
[OP_PLUS_HWORD] = vm_plus_hword, [OP_PLUS_WORD] = vm_plus_word,
|
||||||
[OP_MULT_HWORD] = vm_mult_hword,
|
[OP_SUB_BYTE] = vm_sub_byte, [OP_SUB_SHORT] = vm_sub_short,
|
||||||
[OP_MULT_WORD] = vm_mult_word,
|
[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
|
#endif
|
||||||
|
|||||||
37
vm/struct.c
37
vm/struct.c
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2024 Aryadev Chavali
|
/* Copyright (C) 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2024-04-25
|
* Created: 2024-04-25
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
@@ -53,18 +52,16 @@ void vm_stop(vm_t *vm)
|
|||||||
{
|
{
|
||||||
#if VERBOSE >= 1
|
#if VERBOSE >= 1
|
||||||
bool leaks = false;
|
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)
|
if (vm->call_stack.ptr > 0)
|
||||||
{
|
{
|
||||||
leaks = true;
|
leaks = true;
|
||||||
printf("\t[" TERM_RED "DATA" TERM_RESET "]: Call stack at %lu\n\t[" TERM_RED
|
FAIL("vm_stop", "Call stack at %lu\n", vm->call_stack.ptr);
|
||||||
"DATA" TERM_RESET "]\n\t[" TERM_RED "DATA" TERM_RESET "]: Call "
|
FAIL("vm_stop", "Call stack trace:%s", "");
|
||||||
"stack trace:",
|
|
||||||
vm->call_stack.ptr);
|
|
||||||
for (size_t i = vm->call_stack.ptr; i > 0; --i)
|
for (size_t i = vm->call_stack.ptr; i > 0; --i)
|
||||||
{
|
{
|
||||||
word_t w = vm->call_stack.address_pointers[i - 1];
|
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)
|
if (i != 1)
|
||||||
printf(", ");
|
printf(", ");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@@ -81,22 +78,20 @@ void vm_stop(vm_t *vm)
|
|||||||
capacities[i] = cur->available;
|
capacities[i] = cur->available;
|
||||||
total_capacity += capacities[i];
|
total_capacity += capacities[i];
|
||||||
}
|
}
|
||||||
printf("\t[" TERM_RED "DATA" TERM_RESET
|
FAIL("vm_stop", "Heap: %luB (over %lu %s) not reclaimed\n", total_capacity,
|
||||||
"]: Heap: %luB (over %lu %s) not reclaimed\n",
|
size_pages, size_pages == 1 ? "page" : "pages");
|
||||||
total_capacity, size_pages, size_pages == 1 ? "page" : "pages");
|
|
||||||
for (size_t i = 0; i < size_pages; i++)
|
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)
|
if (vm->stack.ptr > 0)
|
||||||
{
|
{
|
||||||
leaks = true;
|
leaks = true;
|
||||||
printf("\t[" TERM_RED "DATA" TERM_RESET "]: Stack: %luB not reclaimed\n",
|
FAIL("vm_stop", "Stack: %luB not reclaimed\n", vm->stack.ptr);
|
||||||
vm->stack.ptr);
|
|
||||||
}
|
}
|
||||||
if (leaks)
|
if (leaks)
|
||||||
printf("[" TERM_RED "DATA" TERM_RESET "]: Leaks found\n");
|
FAIL("vm_stop", "Leaks found\n%s", "");
|
||||||
else
|
else
|
||||||
printf("[" TERM_GREEN "DATA" TERM_RESET "]: No leaks found\n");
|
SUCCESS("vm_stop", "No leaks found\n%s", "");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
vm->registers = (struct Registers){0};
|
vm->registers = (struct Registers){0};
|
||||||
|
|||||||
13
vm/struct.h
13
vm/struct.h
@@ -1,13 +1,12 @@
|
|||||||
/* Copyright (C) 2024 Aryadev Chavali
|
/* Copyright (C) 2024 Aryadev Chavali
|
||||||
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
|
||||||
* General Public License for more details.
|
* more details.
|
||||||
|
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License Version 2
|
||||||
* along with this program. If not, see
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
* <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
* Created: 2024-04-25
|
* Created: 2024-04-25
|
||||||
* Author: Aryadev Chavali
|
* Author: Aryadev Chavali
|
||||||
|
|||||||
Reference in New Issue
Block a user