Talk:Higher Subleq
Esoteric?
Is this an esoteric language?--TehZ 20:32, 25 December 2010 (UTC)
- Probably; even if it isn't, it is on-topic, as it is intended to compile to Subleq, which is definitely esoteric. —ehird 20:59, 25 December 2010 (UTC)
Accessing internal registers
I've found that although the sp
(stack top pointer), bp
(stack frame base pointer), ra
(local return address), and ax
(most recent return value) variables aren't directly accessible to the programmer, it's possible to read them and write to them by calling a function and accessing or altering the stack frame. Some C functions that require the use of internal registers are setjmp()
, longjmp()
, and alloca()
. I wrote getter/setter functions for all of the registers as well as working alloca()
, setjmp()
, and longjmp()
functions. These require no modifications to the compiler and seem to work correctly even in complex situations. They sometimes require a few static variables, which begin with a single underscore followed by the function name without any leading underscores. These static variables are so the functions can store data without using stack-relative local variables or arguments, which will become inaccessible when the stack pointer is moved. By tracing bp
s up the stack, it's possible to alter the sp
, bp
, or ra
of a function further up the stack.
void *_get_ra(int dummy) {return -(&dummy)[1];} void *_get_bp(int dummy) {return -(&dummy)[2];} void *_get_sp() {return _get_bp()-2;} void *_get_ax() {return;} void *_set_ra(void *val) {return (&val)[1] = -val;} void *_set_bp(void *val) {return (&val)[2] = -val;} void *_set_sp_ra; void *_set_sp_bp; void *_set_sp(void *val) { _set_sp_ra = (&val)[1]; _set_sp_bp = (&val)[2]; void *p = _set_bp(val+3); (&val)[1] = _set_sp_ra; (&val)[2] = _set_sp_bp; return p; } void *_set_ax(int val) {return val;} void *_alloca_ra; void *_alloca_bp; void *_alloca_ptr; void *alloca(int n) { _alloca_ra = (&n)[1]; _alloca_bp = (&n)[2]; _alloca_ptr = _get_bp()-3; _set_bp(_get_bp()+n); (&n)[1] = _alloca_ra; (&n)[2] = _alloca_bp; return _alloca_ptr; } /* a jmp_buf is an array of 3 cells */ int setjmp(void **env) { env[0] = _get_bp(); //sp env[1] = (&env)[1]; //ra env[2] = (&env)[2]; //bp return 0; } void **_longjmp_env; int _longjmp_val; int longjmp(void **env, int val) { _longjmp_env = env; _longjmp_val = val?val:1; _set_bp(_longjmp_env[0]+1); //sp (&env)[1] = _longjmp_env[1]; //ra (&env)[2] = _longjmp_env[2]; //bp return _longjmp_val; }
Ian 05:13, 9 August 2011 (UTC)
- I made a small automatic-allocation string library based on
alloca()
and using the_get_bp()
,_set_bp()
, and_set_sp()
functions defined previously. The functions aregetsa()
,strdupa()
, andstrndupa()
. The last two are identical to the GNU libc functions. The first one is similar togets()
, reading a line until newline or EOF, but automatically allocates the buffer into the caller's automatic storage. They each have their own static variables even though the variables could be shared. Ideally, these would use thedo
-while
loop style, but that's not implemented yet, so an infinite loop with abreak
is used instead. They can't use plainwhile
because they need space for the null terminator and need to work with zero-length strings. Althoughgetsa()
's prototype has an argument, it should be called with no arguments, since that's just a dummy variable used to provide access to the stack frame. These functions andalloca()
can safely be called multiple times (infinite times if the Subleq machine has infinite memory). Alongjmp()
will corrupt or destroy any data allocated by these functions since the lastsetjmp()
. To explicitly free data,alloca()
can be called with a negative number, which will pop that number of cells from the stack. Like pointers to ordinary automatic variables, the pointers returned by these functions can't safely be returned to a previous caller, but it should be possible to make some type of combinedmemmove()
-longjmp()
that can move the string into a previous stack frame and then return.
void *_getsa_ra; void *_getsa_bp; int _getsa_c; char *_getsa_ptr; char *_getsa_ret; char *getsa(int dummy) { _getsa_ra = (&dummy)[1]; _getsa_bp = (&dummy)[2]; _getsa_ret = _getsa_ptr = _get_bp()-2; while (1) { _set_bp(_getsa_ptr+3); _set_sp(_getsa_ptr); if ((_getsa_c = *_getsa_ptr++ = __in) == '\n' || _getsa_c < 0) break; } *--_getsa_ptr = '\0'; (&dummy)[1] = _getsa_ra; (&dummy)[2] = _getsa_bp; return _getsa_ret; } void *_strdupa_ra; void *_strdupa_bp; char *_strdupa_str; char *_strdupa_ptr; char *_strdupa_ret; char *strdupa(char* str) { _strdupa_ra = (&str)[1]; _strdupa_bp = (&str)[2]; _strdupa_str = str; _strdupa_ret = _strdupa_ptr = _get_bp()-3; while (1) { _set_bp(_strdupa_ptr+4); _set_sp(_strdupa_ptr); if (!(*_strdupa_ptr++ = *_strdupa_str++)) break; } (&str)[1] = _strdupa_ra; (&str)[2] = _strdupa_bp; return _strdupa_ret; } void *_strndupa_ra; void *_strndupa_bp; char *_strndupa_str; int _strndupa_n; char *_strndupa_ptr; char *_strndupa_ret; char *strndupa(char* str, int n) { _strndupa_ra = (&str)[1]; _strndupa_bp = (&str)[2]; _strndupa_str = str; _strndupa_n = n; _strndupa_ret = _strndupa_ptr = _get_bp()-4; while (1) { _set_bp(_strndupa_ptr+5); _set_sp(_strndupa_ptr); if (_strndupa_n-- <= 0 || !(*_strndupa_ptr++ = *_strndupa_str++)) break; } *_strndupa_ptr = '\0'; (&str)[1] = _strndupa_ra; (&str)[2] = _strndupa_bp; return _strndupa_ret; }
Ian 18:12, 9 August 2011 (UTC)
- Here's a
memdupa()
to go with the other functions. This will allocate space forn
bytes, copy them from the memory pointed to bymem
, and then return a pointer to the new space. Likestrdupa()
andstrndupa()
, it is undefined behavior if the source and the newly allocated memory overlap, but this can only happen if the source is above the top of stack (like a dangling pointer). This function is mostly equivalent tomemcpy(alloca(n),mem,n)
, except this implementation allocates and copies one cell at a time.
void *_memdupa_ra; void *_memdupa_bp; void *_memdupa_mem; int _memdupa_n; void *_memdupa_ptr; void *_memdupa_ret; void *memdupa(void* mem, int n) { _memdupa_ra = (&mem)[1]; _memdupa_bp = (&mem)[2]; _memdupa_mem = mem; _memdupa_n = n; _memdupa_ret = _memdupa_ptr = _get_bp()-4; while (--_memdupa_n >= 0) { _set_bp(_memdupa_ptr+5); _set_sp(_memdupa_ptr); *_memdupa_ptr++ = *_memdupa_mem++; } (&mem)[1] = _memdupa_ra; (&mem)[2] = _memdupa_bp; return _memdupa_ret; }
Ian 22:52, 9 August 2011 (UTC)