diff options
Diffstat (limited to 'utils/mcpu-asm.c')
| -rw-r--r-- | utils/mcpu-asm.c | 4012 |
1 files changed, 4012 insertions, 0 deletions
diff --git a/utils/mcpu-asm.c b/utils/mcpu-asm.c new file mode 100644 index 0000000..dcc4ead --- /dev/null +++ b/utils/mcpu-asm.c @@ -0,0 +1,4012 @@ +#include <ctype.h> +#include <errno.h> +#include <math.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <libmcpu.h> +#include <libmpuio.h> +#include "mcpu-asm.h" + +#define ASM_NAME_MAX 64 +#define ASM_EXPR_MAX 256 + +typedef struct +{ + char name[ASM_NAME_MAX]; + __mpu_uint64_t addr; +} label_t; +typedef struct +{ + size_t pos; + char expr[ASM_EXPR_MAX]; +} fixup_t; +typedef struct +{ + char name[ASM_NAME_MAX]; + char expr[ASM_EXPR_MAX]; + int line; + int state; /* 0=not evaluated, 1=evaluating, 2=done */ + __int128 value; +} equ_t; +typedef struct +{ + __mpu_UNIT_t *p; + size_t n, cap; +} buf_t; +typedef struct +{ + label_t *p; + size_t n, cap; +} labels_t; +typedef struct +{ + fixup_t *p; + size_t n, cap; +} fixups_t; +typedef struct +{ + equ_t *p; + size_t n, cap; +} equs_t; + +typedef struct +{ + char name[ASM_NAME_MAX]; + int is_global; + int is_extern; + int is_weak; +} symdecl_t; +typedef struct +{ + symdecl_t *p; + size_t n, cap; +} symdecls_t; + +static int +grow( void **p, size_t *cap, size_t need, size_t unit ) +{ + size_t c = *cap ? *cap : 64; + void *q; + while( c < need ) c *= 2; + q = realloc( *p, c *unit ); + if( !q ) return( -1 ); + *p = q; + *cap = c; + return( 0 ); +} + +static int +put( buf_t *b, const void *p, size_t n ) +{ + if( grow( (void **)&b->p, &b->cap, b->n + n, 1 ) ) return( -1 ); + memcpy( b->p + b->n, p, n ); + b->n += n; + return( 0 ); +} + +static int +put8( buf_t *b, unsigned v ) +{ + __mpu_UNIT_t x = (__mpu_UNIT_t)v; + return( put( b, &x, 1 ) ); +} + +static int +put16( buf_t *b, unsigned v ) +{ + __mpu_UNIT_t x[2]; + __cpu_code_put_u16( x, (__mpu_uint16_t)v ); + return( put( b, x, 2 ) ); +} + +static int +put64( buf_t *b, __mpu_uint64_t v ) +{ + __mpu_UNIT_t x[8]; + __cpu_code_put_u64( x, v ); + return( put( b, x, 8 ) ); +} + +static char * +trim( char *s ) +{ + char *e; + while( isspace( (unsigned char)*s ) ) s++; + e = s + strlen( s ); + while( e > s && isspace( (unsigned char)e[-1] ) ) *--e = 0; + return( s ); +} + +static int +streqi( const char *a, const char *b ) +{ + for( ; *a && *b; a++, b++ ) if( toupper( (unsigned char)*a ) != toupper( (unsigned char)*b ) ) return( 0 ); + return( !*a && !*b ); +} + +/* ';' starts a comment only outside a quoted assembler string. */ +static void +strip_comment( char *s ) +{ + int quote = 0, esc = 0; + for( ; *s; s++ ) + { + if( quote ) + { + if( esc ) + { + esc = 0; + continue; + } + if( *s == '\\' ) + { + esc = 1; + continue; + } + if( *s == '"' ) + { + quote = 0; + continue; + } + } + else + { + if( *s == '"' ) + { + quote = 1; + continue; + } + if( *s == ';' ) + { + *s = 0; + return; + } + } + } +} + +/* A label colon is recognized only in the first token, never inside operands. */ +static char * +label_colon( char *s ) +{ + char *p = s; + while( *p && !isspace( (unsigned char)*p ) ) + { + if( *p == ':' ) return( p ); + p++; + } + return( 0 ); +} + +static int +addlabel( labels_t *l, const char *n, __mpu_uint64_t a ) +{ + size_t i; + for( i = 0; i < l->n; i++ ) if( !strcmp( l->p[i].name, n ) ) return( -1 ); + if( strlen( n ) >= ASM_NAME_MAX ) return( -1 ); + if( grow( (void **)&l->p, &l->cap, l->n + 1, sizeof( *l->p ) ) ) return( -1 ); + strcpy( l->p[l->n].name, n ); + l->p[l->n++].addr = a; + return( 0 ); +} + +static int +findlabel( const labels_t *l, const char *n, __mpu_uint64_t *a ) +{ + size_t i; + for( i = 0; i < l->n; i++ ) if( !strcmp( l->p[i].name, n ) ) + { + *a = l->p[i].addr; + return( 0 ); + } + return( -1 ); +} + +static int +addfix( fixups_t *f, size_t pos, const char *expr ) +{ + if( strlen( expr ) >= ASM_EXPR_MAX ) return( -1 ); + if( grow( (void **)&f->p, &f->cap, f->n + 1, sizeof( *f->p ) ) ) return( -1 ); + f->p[f->n].pos = pos; + strcpy( f->p[f->n].expr, expr ); + f->n++; + return( 0 ); +} + +static int +findequ( const equs_t *e, const char *n, size_t *idx ) +{ + size_t i; + for( i = 0; i < e->n; i++ ) if( !strcmp( e->p[i].name, n ) ) + { + if( idx ) * idx = i; + return( 0 ); + } + return( -1 ); +} + +static int +addequ( equs_t *e, const char *n, const char *expr, int line, char *err, size_t es ) +{ + size_t dummy; + if( !*n || strlen( n ) >= ASM_NAME_MAX || strlen( expr ) >= ASM_EXPR_MAX ) + { + snprintf( err, es, "bad .equ name/expression" ); + return( -1 ); + } + if( !findequ( e, n, &dummy ) ) + { + snprintf( err, es, "duplicate .equ symbol: %s", n ); + return( -1 ); + } + if( grow( (void **)&e->p, &e->cap, e->n + 1, sizeof( *e->p ) ) ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + memset( &e->p[e->n], 0, sizeof( e->p[e->n] ) ); + strcpy( e->p[e->n].name, n ); + strcpy( e->p[e->n].expr, expr ); + e->p[e->n].line = line; + e->n++; + return( 0 ); +} + +/* Split comma-separated arguments while respecting parentheses and quoted strings. */ +static int +splitargs( char *s, char **a, int max ) +{ + int n = 0, depth = 0, quote = 0, esc = 0; + char *p = s, *start = s; + for( ; ; p++ ) + { + int end = ( *p == 0 ); + if( !end ) + { + if( quote ) + { + if( esc ) esc = 0; + else if( *p == '\\' ) esc = 1; + else if( *p == '"' ) quote = 0; + } + else + { + if( *p == '"' ) quote = 1; + else if( *p == '(' ) depth++; + else if( *p == ')' ) depth--; + } + } + if( ( !quote && depth == 0 && *p == ',' ) || end ) + { + if( n >= max ) return( -1 ); + if( !end ) * p = 0; + a[n++] = trim( start ); + if( end ) break; + start = p + 1; + } + } + if( quote || depth != 0 ) return( -1 ); + return( n ); +} + +/* ------------------------- integer expressions ------------------------- */ +typedef struct +{ + const char *p; + equs_t *eqs; + const labels_t *labels; /* optional section-local label namespace */ + char *err; + size_t es; +} expr_parser_t; + +static int eval_expression( const char *, equs_t *, const labels_t *, __int128 *, char *, size_t ); + +static void +expr_skip( expr_parser_t *x ) +{ + while( isspace( (unsigned char)*x->p ) ) x->p++; +} + +static int +symfirst( int c ) +{ + return( isalpha( (unsigned char)c ) || c == '_' || c == '.' || c == '$' ); +} + +static int +symrest( int c ) +{ + return( isalnum( (unsigned char)c ) || c == '_' || c == '.' || c == '$' ); +} + +static int +equ_value( equs_t *eqs, size_t i, __int128 *v, char *err, size_t es ) +{ + equ_t *e = &eqs->p[i]; + if( e->state == 2 ) + { + *v = e->value; + return( 0 ); + } + if( e->state == 1 ) + { + snprintf( err, es, "cyclic .equ definition involving %s", e->name ); + return( -1 ); + } + e->state = 1; + if( eval_expression( e->expr, eqs, 0, &e->value, err, es ) ) + { + e->state = 0; + return( -1 ); + } + e->state = 2; + *v = e->value; + return( 0 ); +} + +static int +expr_symbol( expr_parser_t *x, const char *name, __int128 *v ) +{ + size_t i; + __mpu_uint64_t a; + if( !findequ( x->eqs, name, &i ) ) return( equ_value( x->eqs, i, v, x->err, x->es ) ); + if( x->labels && !findlabel( x->labels, name, &a ) ) + { + *v = ( __int128 ) a; + return( 0 ); + } + snprintf( x->err, x->es, "unknown symbol in expression: %s", name ); + return( -1 ); +} + +static int +expr_primary( expr_parser_t *x, __int128 *v ) +{ + char name[ASM_NAME_MAX], *end; + size_t n = 0; + unsigned long long u; + expr_skip( x ); + if( symfirst( (unsigned char)*x->p ) ) + { + while( symrest( (unsigned char)*x->p ) ) + { + if( n + 1 >= sizeof( name ) ) + { + snprintf( x->err, x->es, "symbol name too long" ); + return( -1 ); + } + name[n++] = *x->p++; + } + name[n] = 0; + return( expr_symbol( x, name, v ) ); + } + errno = 0; + u = strtoull( x->p, &end, 0 ); + if( end == x->p ) + { + snprintf( x->err, x->es, "expected integer or symbol in expression" ); + return( -1 ); + } + if( errno == ERANGE ) + { + snprintf( x->err, x->es, "integer literal out of 64-bit range in expression" ); + return( -1 ); + } + x->p = end; + *v = ( __int128 ) u; + return( 0 ); +} + +/* Forward declarations for recursive-descent parentheses. */ +static int expr_add( expr_parser_t *, __int128 * ); + +static int +expr_atom( expr_parser_t *x, __int128 *v ) +{ + expr_skip( x ); + if( *x->p == '(' ) + { + x->p++; + if( expr_add( x, v ) ) return( -1 ); + expr_skip( x ); + if( *x->p != ')' ) + { + snprintf( x->err, x->es, "missing ')' in expression" ); + return( -1 ); + } + x->p++; + return( 0 ); + } + return( expr_primary( x, v ) ); +} + +static int +expr_unary( expr_parser_t *x, __int128 *v ) +{ + expr_skip( x ); + if( *x->p == '+' ) + { + x->p++; + return( expr_unary( x, v ) ); + } + if( *x->p == '-' ) + { + __int128 t; + x->p++; + if( expr_unary( x, &t ) ) return( -1 ); + if( __builtin_sub_overflow( ( __int128 ) 0, t, v ) ) + { + snprintf( x->err, x->es, "expression overflow" ); + return( -1 ); + } + return( 0 ); + } + return( expr_atom( x, v ) ); +} + +static int +expr_mul( expr_parser_t *x, __int128 *v ) +{ + __int128 a, b, r; + char op; + if( expr_unary( x, &a ) ) return( -1 ); + for( ; ; ) + { + expr_skip( x ); + op = *x->p; + if( op != '*' && op != '/' && op != '%' ) break; + x->p++; + if( expr_unary( x, &b ) ) return( -1 ); + if( op == '*' ) + { + if( __builtin_mul_overflow( a, b, &r ) ) + { + snprintf( x->err, x->es, "expression overflow" ); + return( -1 ); + } + } + else + { + if( !b ) + { + snprintf( x->err, x->es, "division by zero in expression" ); + return( -1 ); + } + if( op == '/' ) r = a / b; + else r = a % b; + } + a = r; + } + *v = a; + return( 0 ); +} + +static int +expr_add( expr_parser_t *x, __int128 *v ) +{ + __int128 a, b, r; + char op; + if( expr_mul( x, &a ) ) return( -1 ); + for( ; ; ) + { + expr_skip( x ); + op = *x->p; + if( op != '+' && op != '-' ) break; + x->p++; + if( expr_mul( x, &b ) ) return( -1 ); + if( op == '+' ) + { + if( __builtin_add_overflow( a, b, &r ) ) + { + snprintf( x->err, x->es, "expression overflow" ); + return( -1 ); + } + } + else + { + if( __builtin_sub_overflow( a, b, &r ) ) + { + snprintf( x->err, x->es, "expression overflow" ); + return( -1 ); + } + } + a = r; + } + *v = a; + return( 0 ); +} + +static int +eval_expression( const char *s, equs_t *eqs, const labels_t *labels, __int128 *v, char *err, size_t es ) +{ + expr_parser_t x = + { + s, eqs, labels, err, es + }; + if( expr_add( &x, v ) ) return( -1 ); + expr_skip( &x ); + if( *x.p ) + { + snprintf( err, es, "unexpected token in expression near: %s", x.p ); + return( -1 ); + } + return( 0 ); +} + +static int +eval_i64( const char *s, equs_t *eqs, const labels_t *l, __mpu_int64_t *v, char *err, size_t es ) +{ + __int128 x; + if( eval_expression( s, eqs, l, &x, err, es ) ) return( -1 ); + if( x < ( __int128 ) INT64_MIN || x > ( __int128 ) INT64_MAX ) return( 1 ); + *v = (__mpu_int64_t)x; + return( 0 ); +} + +static int +eval_u64( const char *s, equs_t *eqs, const labels_t *l, __mpu_uint64_t *v, char *err, size_t es ) +{ + __int128 x; + if( eval_expression( s, eqs, l, &x, err, es ) ) return( -1 ); + if( x < 0 || x > ( __int128 ) UINT64_MAX ) return( 1 ); + *v = (__mpu_uint64_t)x; + return( 0 ); +} + +static int +plain_integer_literal( const char *s ) +{ + const unsigned char *p = (const unsigned char *)s; + int base = 10, nd = 0; + while( isspace( *p ) ) p++; + if( *p == '+' || *p == '-' ) p++; + if( !*p ) return( 0 ); + if( p[0] == '0' && ( p[1] == 'x' || p[1] == 'X' ) ) + { + base = 16; + p += 2; + } + else + if( p[0] == '0' && ( p[1] == 'b' || p[1] == 'B' ) ) + { + base = 2; + p += 2; + } + else + if( p[0] == '0' && isdigit( p[1] ) ) + { + base = 8; + p++; + } + while( *p && !isspace( *p ) ) + { + int ok = ( base == 16 ) ? isxdigit( *p ) : ( base == 10 ) ? isdigit( *p ) : ( base == 8 ) ? ( *p >= '0' && *p <= '7' ) : ( *p == '0' || *p == '1' ); + if( !ok ) return( 0 ); + p++; + nd++; + } + while( isspace( *p ) ) p++; + return( nd && !*p ); +} + +static void +i128_decimal( __int128 v, char out[64] ) +{ + char tmp[64]; + int neg = v < 0, n = 0, i = 0; + unsigned __int128 u; + if( neg ) u = (unsigned __int128)( - ( v + 1 ) ) + 1; + else u = (unsigned __int128)v; + do + { + tmp[n++] = (char)( '0' + u % 10 ); + u /= 10; + } + while( u ); + if( neg ) out[i++] = '-'; + while( n ) out[i++] = tmp[--n]; + out[i] = 0; +} + +/* --------------------------- register syntax --------------------------- */ +static int +regparse( const char *s, cpu_register *r, int *isc, equs_t *eqs, char *err, size_t es ) +{ + const char *p = s, *at; + char *e; + unsigned long bits; + __mpu_uint64_t a; + while( isspace( (unsigned char)*p ) ) p++; + if( *p != 'r' && *p != 'R' && *p != 'c' && *p != 'C' ) return( -1 ); + *isc = ( *p == 'c' || *p == 'C' ); + p++; + errno = 0; + bits = strtoul( p, &e, 10 ); + if( errno || e == p || *e != '@' ) return( -1 ); + at = e + 1; + if( !*at ) return( -1 ); + if( eval_u64( at, eqs, 0, &a, err, es ) != 0 || a > UINT32_MAX ) return( -1 ); + r->bits = (unsigned int)bits; + r->address = (__mpu_uint32_t)a; + if( !__cpu_reg_valid( r->bits, r->address ) ) return( -1 ); + if( *isc && r->bits < 64 ) return( -1 ); + return( 0 ); +} + +static int +preg( buf_t *b, const char *s, int require_c, int require_r, equs_t *eqs, char *err, size_t es ) +{ + cpu_register r; + int c; + __mpu_UNIT_t x[3]; + if( regparse( trim( (char *)s ), &r, &c, eqs, err, es ) ) + { + snprintf( err, es, "bad register: %s", s ); + return( -1 ); + } + if( require_c && !c ) + { + snprintf( err, es, "complex instruction requires c<bits>@addr: %s", s ); + return( -1 ); + } + if( require_r && c ) + { + snprintf( err, es, "instruction requires r<bits>@addr: %s", s ); + return( -1 ); + } + if( __cpu_encode_reg24( x, &r ) ) return( -1 ); + return( put( b, x, 3 ) ); +} + +static int +preg_r64( buf_t *b, const char *s, const char *what, equs_t *eqs, char *err, size_t es ) +{ + cpu_register r; + int c; + __mpu_UNIT_t x[3]; + if( regparse( trim( (char *)s ), &r, &c, eqs, err, es ) || c || r.bits != 64 ) + { + snprintf( err, es, "%s requires r64@addr: %s", what, s ); + return( -1 ); + } + if( __cpu_encode_reg24( x, &r ) ) return( -1 ); + return( put( b, x, 3 ) ); +} + +static int +preg_ptr( buf_t *b, const char *s, equs_t *eqs, char *err, size_t es ) +{ + return( preg_r64( b, s, "PTR64 operand", eqs, err, es ) ); +} + +/* ---------------------------- string literals ---------------------------- */ +static int +hex_digit( int c ) +{ + if( c >= '0' && c <= '9' ) return( c - '0' ); + if( c >= 'a' && c <= 'f' ) return( c - 'a' + 10 ); + if( c >= 'A' && c <= 'F' ) return( c - 'A' + 10 ); + return( -1 ); +} + +static int +put_utf8_cp( buf_t *b, unsigned long cp, char *err, size_t es ) +{ + unsigned char x[4]; + size_t n; + if( cp == 0 ) + { + snprintf( err, es, "embedded NUL is not supported in .utf8/.ucs2; use .byte/.integer" ); + return( -1 ); + } + if( cp <= 0x7f ) + { + x[0] = (unsigned char)cp; + n = 1; + } + else + if( cp <= 0x7ff ) + { + x[0] = 0xc0 | ( cp >> 6 ); + x[1] = 0x80 | ( cp & 0x3f ); + n = 2; + } + else + if( cp <= 0xffff ) + { + if( cp >= 0xd800 && cp <= 0xdfff ) + { + snprintf( err, es, "Unicode surrogate is not a valid scalar value" ); + return( -1 ); + } + x[0] = 0xe0 | ( cp >> 12 ); + x[1] = 0x80 | ( ( cp >> 6 ) & 0x3f ); + x[2] = 0x80 | ( cp & 0x3f ); + n = 3; + } + else + if( cp <= 0x10ffff ) + { + x[0] = 0xf0 | ( cp >> 18 ); + x[1] = 0x80 | ( ( cp >> 12 ) & 0x3f ); + x[2] = 0x80 | ( ( cp >> 6 ) & 0x3f ); + x[3] = 0x80 | ( cp & 0x3f ); + n = 4; + } + else + { + snprintf( err, es, "Unicode code point out of range" ); + return( -1 ); + } + if( put( b, x, n ) ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + return( 0 ); +} + +static int +parse_utf8_string( const char *s, buf_t *out, char *err, size_t es ) +{ + const unsigned char *p = (const unsigned char *)s; + int closed = 0; + memset( out, 0, sizeof( *out ) ); + while( isspace( *p ) ) p++; + if( *p != '"' ) + { + snprintf( err, es, "expected quoted string" ); + return( -1 ); + } + p++; + while( *p ) + { + if( *p == '"' ) + { + p++; + closed = 1; + break; + } + if( *p != '\\' ) + { + if( *p == 0 ) + { + break; + } + if( put8( out, *p++ ) ) + { + snprintf( err, es, "out of memory" ); + goto fail; + } continue; + } + p++; + if( !*p ) + { + snprintf( err, es, "unfinished escape sequence" ); + goto fail; + } + switch( *p++ ) + { + case '\\': if( put8( out, '\\' ) ) goto oom; + break; + case '"': if( put8( out, '"' ) ) goto oom; + break; + case 'n': if( put8( out, '\n' ) ) goto oom; + break; + case 'r': if( put8( out, '\r' ) ) goto oom; + break; + case 't': if( put8( out, '\t' ) ) goto oom; + break; + case 'b': if( put8( out, '\b' ) ) goto oom; + break; + case 'f': if( put8( out, '\f' ) ) goto oom; + break; + case 'v': if( put8( out, '\v' ) ) goto oom; + break; + case 'a': if( put8( out, '\a' ) ) goto oom; + break; + case '0': snprintf( err, es, "embedded NUL is not supported in .utf8/.ucs2; use .byte/.integer" ); + goto fail; + case 'x': { + int h1, h2; + unsigned v; + if( !p[0] || !p[1] ) + { + snprintf( err, es, "\\x escape requires exactly two hex digits" ); + goto fail; + } + h1 = hex_digit( p[0] ); + h2 = hex_digit( p[1] ); + if( h1 < 0 || h2 < 0 ) + { + snprintf( err, es, "\\x escape requires exactly two hex digits" ); + goto fail; + } + v = (unsigned)( ( h1 << 4 ) | h2 ); + p += 2; + if( v == 0 ) + { + snprintf( err, es, "embedded NUL is not supported in .utf8/.ucs2; use .byte/.integer" ); + goto fail; + } + if( put8( out, v ) ) goto oom; + break; + } + case 'u': + case 'U': { + int nd = p[-1] == 'u' ? 4 : 8, i, h; + unsigned long cp = 0; + for( i = 0; i < nd; i++ ) + { + if( !p[i] ) + { + snprintf( err, es, "Unicode escape requires %d hex digits", nd ); + goto fail; + } + h = hex_digit( p[i] ); + if( h < 0 ) + { + snprintf( err, es, "Unicode escape requires %d hex digits", nd ); + goto fail; + } + cp = ( cp << 4 ) | (unsigned)h; + } + p += nd; + if( put_utf8_cp( out, cp, err, es ) ) goto fail; + break; + } + default: snprintf( err, es, "unknown string escape sequence" ); + goto fail; + } + + } + if( !closed ) + { + snprintf( err, es, "unterminated string literal" ); + goto fail; + } + + while( isspace( *p ) ) p++; + if( *p ) + { + snprintf( err, es, "unexpected text after string literal" ); + goto fail; + } + + if( put8( out, 0 ) ) goto oom; + if( !mpu_utf8valid( (const __mpu_char8_t *)out->p ) ) + { + snprintf( err, es, "string literal is not valid UTF-8" ); + goto fail; + } + + return( 0 ); +oom: + snprintf( err, es, "out of memory" ); +fail: + free( out->p ); + memset( out, 0, sizeof( *out ) ); + return( -1 ); +} + +static int +real_literal_exact_value( const char *s, int nb, mpu_real *out ) +{ + mpu_wide_real_t src, narrow, back; + memset( src, 0, sizeof( src ) ); + memset( narrow, 0, sizeof( narrow ) ); + memset( back, 0, sizeof( back ) ); + if( ascii_to_real( ( mpu_real * ) src, (__mpu_char8_t *)s, NB_R65536 ) < 0 ) return( -1 ); + r_cvt( ( mpu_real * ) narrow, ( mpu_real * ) src, nb, NB_R65536 ); + r_cvt( ( mpu_real * ) back, ( mpu_real * ) narrow, NB_R65536, nb ); + if( r_cmp( ( mpu_real * ) src, ( mpu_real * ) back, NB_R65536 ) != 0 ) return( 0 ); + if( out ) memcpy( out, narrow, (size_t)nb ); + return( 1 ); +} + +static int +cimm_exact( const char *s, float *r, float *j, char *err, size_t es ) +{ + const char *rp = strstr( s, "r0" ), *jp; + char *rs = 0, *js = 0; + size_t rn, jn; + int ex; + if( !rp || rp == s ) return( -1 ); + jp = strstr( rp + 2, "j0" ); + if( !jp || jp == rp + 2 || jp[2] ) return( -1 ); + rn = (size_t)( rp - s ); + jn = (size_t)( jp - ( rp + 2 ) ); + rs = (char *)malloc( rn + 1 ); + js = (char *)malloc( jn + 1 ); + if( !rs || !js ) + { + free( rs ); + free( js ); + snprintf( err, es, "out of memory" ); + return( -2 ); + } + memcpy( rs, s, rn ); + rs[rn] = 0; + memcpy( js, rp + 2, jn ); + js[jn] = 0; + { + mpu_real32_t xr, xj; + ex = real_literal_exact_value( rs, NB_R32, ( mpu_real * ) xr ); + if( ex != 1 ) + { + free( rs ); + free( js ); + if( ex == 0 ) snprintf( err, es, "LDC immediate component is not exactly representable as real32;\nuse .complex <bits>, real, imag in DATA and LOAD" ); + return( ex == 0 ? -2 : -1 ); + } + ex = real_literal_exact_value( js, NB_R32, ( mpu_real * ) xj ); + if( ex != 1 ) + { + free( rs ); + free( js ); + if( ex == 0 ) snprintf( err, es, "LDC immediate component is not exactly representable as real32;\nuse .complex <bits>, real, imag in DATA and LOAD" ); + return( ex == 0 ? -2 : -1 ); + } + memcpy( r, xr, NB_R32 ); + memcpy( j, xj, NB_R32 ); + } + free( rs ); + free( js ); + return( 0 ); +} + +static int +constid( const char *s, unsigned *v ) +{ + struct K + { + const char *n; + unsigned v; + } + ks[] = + { + { "ZERO", CPU_RCONST_ZERO }, + { "HALF", CPU_RCONST_HALF }, + { "ONE", CPU_RCONST_ONE }, + { "TWO", CPU_RCONST_TWO }, + { "TEN", CPU_RCONST_TEN }, + { "MTEN", CPU_RCONST_MTEN }, + { "32", CPU_RCONST_32 }, + { "PI", CPU_RCONST_PI }, + { "E", CPU_RCONST_E }, + { "1_LN2", CPU_RCONST_1_LN2 }, + { "LN2", CPU_RCONST_LN2 }, + { "1_LN10", CPU_RCONST_1_LN10 }, + { "LN10", CPU_RCONST_LN10 }, + { "1_LG2", CPU_RCONST_1_LG2 }, + { "LG2", CPU_RCONST_LG2 }, + { "PI_2", CPU_RCONST_PI_2 }, + { "PI_3", CPU_RCONST_PI_3 }, + { "PI_4", CPU_RCONST_PI_4 }, + { "PI_5", CPU_RCONST_PI_5 }, + { "PI_6", CPU_RCONST_PI_6 }, + { "1_PI", CPU_RCONST_1_PI }, + { "2_PI", CPU_RCONST_2_PI }, + { "3_PI", CPU_RCONST_3_PI }, + { "4_PI", CPU_RCONST_4_PI }, + { "5_PI", CPU_RCONST_5_PI }, + { "2PI", CPU_RCONST_2PI }, + { "3PI", CPU_RCONST_3PI }, + { "1_2PI", CPU_RCONST_1_2PI }, + { "1_3PI", CPU_RCONST_1_3PI }, + { "1_4PI", CPU_RCONST_1_4PI }, + { "1_5PI", CPU_RCONST_1_5PI }, + { "1_6PI", CPU_RCONST_1_6PI }, + { "3PI_4", CPU_RCONST_3PI_4 }, + { "SQRTPI", CPU_RCONST_SQRTPI }, + { "1_SQRTPI", CPU_RCONST_1_SQRTPI }, + { "2_SQRTPI", CPU_RCONST_2_SQRTPI }, + { "SQRT2", CPU_RCONST_SQRT2 }, + { "1_SQRT2", CPU_RCONST_1_SQRT2 }, + { "SQRT3", CPU_RCONST_SQRT3 }, + { "1_SQRT3", CPU_RCONST_1_SQRT3 }, + { "DEGREE", CPU_RCONST_DEGREE }, + { "1_DEGREE", CPU_RCONST_1_DEGREE }, + { "GOLDENRATIO", CPU_RCONST_GOLDENRATIO }, + { "EULERGAMMA", CPU_RCONST_EULERGAMMA }, + { "CATALAN", CPU_RCONST_CATALAN }, + { "EPSILON", CPU_RCONST_EPSILON }, + { "IND", CPU_RCONST_IND }, + { "PNAN", CPU_RCONST_PNAN }, + { "NNAN", CPU_RCONST_NNAN }, + { "PNANMAX", CPU_RCONST_PNANMAX }, + { "NNANMAX", CPU_RCONST_NNANMAX }, + { "PNANMIN", CPU_RCONST_PNANMIN }, + { "NNANMIN", CPU_RCONST_NNANMIN }, + { "PINF", CPU_RCONST_PINF }, + { "NINF", CPU_RCONST_NINF }, + { "PREALMIN", CPU_RCONST_PREALMIN }, + { "NREALMIN", CPU_RCONST_NREALMIN }, + { "PREALMAX", CPU_RCONST_PREALMAX }, + { "NREALMAX", CPU_RCONST_NREALMAX }, + { "PZERO", CPU_RCONST_PZERO }, + { "NZERO", CPU_RCONST_NZERO } + }; + size_t i; + for( i = 0; i < sizeof( ks ) / sizeof( ks[0] ); i++ ) if( streqi( s, ks[i].n ) ) + { + *v = ks[i].v; + return( 0 ); + } + return( -1 ); +} + +static int +pswbitid( const char *s, unsigned *v ) +{ + struct B + { + const char *n; + unsigned v; + } + bs[] = + { + { "AF", CPU_PSWBIT_AF }, + { "CF", CPU_PSWBIT_CF }, + { "OF", CPU_PSWBIT_OF }, + { "SF", CPU_PSWBIT_SF }, + { "PF", CPU_PSWBIT_PF }, + { "ZF", CPU_PSWBIT_ZF }, + { "RF", CPU_PSWBIT_RF }, + { "VF", CPU_PSWBIT_VF }, + { "DOMF", CPU_PSWBIT_DOMF }, + { "SNGF", CPU_PSWBIT_SNGF }, + { "OVFF", CPU_PSWBIT_OVFF }, + { "UDFF", CPU_PSWBIT_UDFF }, + { "TLSF", CPU_PSWBIT_TLSF }, + { "PLSF", CPU_PSWBIT_PLSF }, + { "INDF", CPU_PSWBIT_INDF }, + { "INXF", CPU_PSWBIT_INXF }, + { "HF", CPU_PSWBIT_HF }, + { "TF", CPU_PSWBIT_TF }, + { "IF", CPU_PSWBIT_IF } + }; + size_t i; + for( i = 0; i < sizeof( bs ) / sizeof( bs[0] ); i++ ) + if( streqi( s, bs[i].n ) ) + { + *v = bs[i].v; + return( 0 ); + } + return( -1 ); +} + +typedef struct +{ + const char *n; + unsigned op; + int nr; + int c; + int r; +} optab_t; +static const optab_t ops[] = +{ + { "NOP", CPU_OP_NOP, 0, 0, 0 }, + { "HALT", CPU_OP_HALT, 0, 0, 0 }, + { "END", CPU_OP_END, 0, 0, 0 }, + { "SYSCALL", CPU_OP_SYSCALL, 0, 0, 0 }, + { "CLRCPUF", CPU_OP_CLRCPUF, 0, 0, 0 }, + { "CLRMPUF", CPU_OP_CLRMPUF, 0, 0, 0 }, + { "CLR", CPU_OP_CLR, 1, 0, 0 }, + { "MOV", CPU_OP_MOV, 2, 0, 0 }, + { "SWAP", CPU_OP_SWAP, 2, 0, 0 }, + { "BSWAP", CPU_OP_BSWAP, 2, 0, 1 }, + { "PUSH", CPU_OP_PUSH, 1, 0, 0 }, + { "POP", CPU_OP_POP, 1, 0, 0 }, + { "PUSHFP", CPU_OP_PUSHFP, 0, 0, 0 }, + { "SETFP", CPU_OP_SETFP, 0, 0, 0 }, + { "RESTSP", CPU_OP_RESTSP, 0, 0, 0 }, + { "POPFP", CPU_OP_POPFP, 0, 0, 0 }, + { "IADD", CPU_OP_IADD, 3, 0, 1 }, + { "IADC", CPU_OP_IADC, 3, 0, 1 }, + { "ISUB", CPU_OP_ISUB, 3, 0, 1 }, + { "ISBB", CPU_OP_ISBB, 3, 0, 1 }, + { "IAND", CPU_OP_IAND, 3, 0, 1 }, + { "IOR", CPU_OP_IOR, 3, 0, 1 }, + { "IXOR", CPU_OP_IXOR, 3, 0, 1 }, + { "INOT", CPU_OP_INOT, 2, 0, 1 }, + { "INEG", CPU_OP_INEG, 2, 0, 1 }, + { "ICMP", CPU_OP_ICMP, 2, 0, 1 }, + { "ITEST", CPU_OP_ITEST, 2, 0, 1 }, + { "ISHL", CPU_OP_ISHL, 2, 0, 1 }, + { "ISHR", CPU_OP_ISHR, 2, 0, 1 }, + { "ISAL", CPU_OP_ISAL, 2, 0, 1 }, + { "ISAR", CPU_OP_ISAR, 2, 0, 1 }, + { "IROL", CPU_OP_IROL, 2, 0, 1 }, + { "IROR", CPU_OP_IROR, 2, 0, 1 }, + { "IRCL", CPU_OP_IRCL, 2, 0, 1 }, + { "IRCR", CPU_OP_IRCR, 2, 0, 1 }, + { "IINC", CPU_OP_IINC, 2, 0, 1 }, + { "IDEC", CPU_OP_IDEC, 2, 0, 1 }, + { "IXCHG", CPU_OP_IXCHG, 2, 0, 1 }, + { "ICPY", CPU_OP_ICPY, 2, 0, 1 }, + { "ICVT", CPU_OP_ICVT, 2, 0, 1 }, + { "IMUL", CPU_OP_IMUL, 3, 0, 1 }, + { "ISMUL", CPU_OP_ISMUL, 3, 0, 1 }, + { "IDIV", CPU_OP_IDIV, 4, 0, 1 }, + { "ISDIV", CPU_OP_ISDIV, 4, 0, 1 }, + { "IBITSLEN", CPU_OP_IBITSLEN, 2, 0, 1 }, + { "RADD", CPU_OP_RADD, 3, 0, 1 }, + { "RSUB", CPU_OP_RSUB, 3, 0, 1 }, + { "RMUL", CPU_OP_RMUL, 3, 0, 1 }, + { "RDIV", CPU_OP_RDIV, 3, 0, 1 }, + { "RSQRT", CPU_OP_RSQRT, 2, 0, 1 }, + { "RSIN", CPU_OP_RSIN, 2, 0, 1 }, + { "RCOS", CPU_OP_RCOS, 2, 0, 1 }, + { "RTAN", CPU_OP_RTAN, 2, 0, 1 }, + { "RASIN", CPU_OP_RASIN, 2, 0, 1 }, + { "RACOS", CPU_OP_RACOS, 2, 0, 1 }, + { "RATAN", CPU_OP_RATAN, 2, 0, 1 }, + { "RLOG", CPU_OP_RLOG, 2, 0, 1 }, + { "RLOG10", CPU_OP_RLOG10, 2, 0, 1 }, + { "RLOG2", CPU_OP_RLOG2, 2, 0, 1 }, + { "REXP", CPU_OP_REXP, 2, 0, 1 }, + { "RSINH", CPU_OP_RSINH, 2, 0, 1 }, + { "RCOSH", CPU_OP_RCOSH, 2, 0, 1 }, + { "RTANH", CPU_OP_RTANH, 2, 0, 1 }, + { "RPOW", CPU_OP_RPOW, 3, 0, 1 }, + { "RHYPOT", CPU_OP_RHYPOT, 3, 0, 1 }, + { "RATAN2", CPU_OP_RATAN2, 3, 0, 1 }, + { "RNEG", CPU_OP_RNEG, 2, 0, 1 }, + { "RABS", CPU_OP_RABS, 2, 0, 1 }, + { "RCPY", CPU_OP_RCPY, 2, 0, 1 }, + { "RCVT", CPU_OP_RCVT, 2, 0, 1 }, + { "RFLOOR", CPU_OP_RFLOOR, 2, 0, 1 }, + { "RCEIL", CPU_OP_RCEIL, 2, 0, 1 }, + { "RROUND", CPU_OP_RROUND, 2, 0, 1 }, + { "RFREXP", CPU_OP_RFREXP, 3, 0, 1 }, + { "RLDEXP", CPU_OP_RLDEXP, 3, 0, 1 }, + { "RLOGB", CPU_OP_RLOGB, 2, 0, 1 }, + { "RMODF", CPU_OP_RMODF, 3, 0, 1 }, + { "RREMAIN", CPU_OP_RREMAIN, 3, 0, 1 }, + { "RLOG1P", CPU_OP_RLOG1P, 2, 0, 1 }, + { "REXPM1", CPU_OP_REXPM1, 2, 0, 1 }, + { "RASINH", CPU_OP_RASINH, 2, 0, 1 }, + { "RACOSH", CPU_OP_RACOSH, 2, 0, 1 }, + { "RATANH", CPU_OP_RATANH, 2, 0, 1 }, + { "ITOR", CPU_OP_ITOR, 2, 0, 1 }, + { "UITOR", CPU_OP_UITOR, 2, 0, 1 }, + { "RTOI", CPU_OP_RTOI, 3, 0, 1 }, + { "RTOUI", CPU_OP_RTOUI, 3, 0, 1 }, + { "CADD", CPU_OP_CADD, 3, 1, 0 }, + { "CSUB", CPU_OP_CSUB, 3, 1, 0 }, + { "CMUL", CPU_OP_CMUL, 3, 1, 0 }, + { "CDIV", CPU_OP_CDIV, 3, 1, 0 }, + { "CEXP", CPU_OP_CEXP, 2, 1, 0 }, + { "CSIN", CPU_OP_CSIN, 2, 1, 0 }, + { "CCOS", CPU_OP_CCOS, 2, 1, 0 }, + { "CSINH", CPU_OP_CSINH, 2, 1, 0 }, + { "CCOSH", CPU_OP_CCOSH, 2, 1, 0 }, + { "CLOG", CPU_OP_CLOG, 2, 1, 0 }, + { "CSQRT", CPU_OP_CSQRT, 2, 1, 0 }, + { "CPOW", CPU_OP_CPOW, 3, 1, 0 }, + { "CCONJ", CPU_OP_CCONJ, 2, 1, 0 }, + { "CCPY", CPU_OP_CCPY, 2, 1, 0 }, + { "CCVT", CPU_OP_CCVT, 2, 1, 0 } +}; + +static int +emit_instruction( buf_t *c, fixups_t *df, fixups_t *cf, fixups_t *pf, equs_t *eqs, char *mn, char *rest, char *err, size_t es ) +{ + char *av[4]; + int ac = rest && *trim( rest ) ? splitargs( rest, av, 4 ) : 0; + size_t i; + unsigned k; + for( i = 0; i < sizeof( ops ) / sizeof( ops[0] ); i++ ) if( streqi( mn, ops[i].n ) ) + { + buf_t tmp = + { + 0 + }; + if( ac != ops[i].nr ) + { + snprintf( err, es, "wrong operand count for %s", mn ); + return( -1 ); + } + if( put16( &tmp, ops[i].op ) ) return( -1 ); + for( k = 0; k < (unsigned)ac; k++ ) if( preg( &tmp, av[k], ops[i].c, ops[i].r, eqs, err, es ) ) + { + free( tmp.p ); + return( -1 ); + } + if( put( c, tmp.p, tmp.n ) ) + { + free( tmp.p ); + return( -1 ); + } + free( tmp.p ); + return( 0 ); + } + if( streqi( mn, "SETF" ) || streqi( mn, "CLRF" ) ) + { + unsigned bit; + if( ac != 1 || pswbitid( av[0], &bit ) || bit > CPU_PSWBIT_WRITE_MAX ) + { + snprintf( err, es, "%s requires one writable PSW flag in range AF..INXF", mn ); + return( -1 ); + } + if( put16( c, streqi( mn, "SETF" ) ? CPU_OP_SETF : CPU_OP_CLRF ) || + put8( c, bit ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "EXIT" ) ) + { + cpu_register r; + int ic; + if( ac != 1 || regparse( av[0], &r, &ic, eqs, err, es ) || ic || r.bits != 32 ) + { + snprintf( err, es, "EXIT requires one r32 status operand" ); + return( -1 ); + } + if( put16( c, CPU_OP_EXIT ) || preg( c, av[0], 0, 1, eqs, err, es ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "LDR" ) ) + { + __mpu_real64_t v; + mpu_real64_t xv; + __mpu_uint64_t u; + cpu_register r; + int ic, ex, nb; + if( ac != 2 || regparse( av[0], &r, &ic, eqs, err, es ) || ic ) + { + snprintf( err, es, "bad LDR destination" ); + return( -1 ); + } + if( r.bits < 32 ) + { + snprintf( err, es, "LDR requires r32 or wider" ); + return( -1 ); + } + nb = ( r.bits == 32 ) ? NB_R32 : NB_R64; + if( nb == NB_R32 ) + { + mpu_real32_t x32; + ex = real_literal_exact_value( av[1], NB_R32, ( mpu_real * ) x32 ); + if( ex == 1 ) r_cvt( ( mpu_real * ) xv, ( mpu_real * ) x32, NB_R64, NB_R32 ); + } + else ex = real_literal_exact_value( av[1], NB_R64, ( mpu_real * ) xv ); + if( ex < 0 ) + { + snprintf( err, es, "bad real immediate" ); + return( -1 ); + } + if( !ex ) + { + snprintf( err, es, "LDR immediate is not exactly representable as real%d;\nuse .real <bits>, value in DATA and LOAD", r.bits == 32 ? 32 : 64 ); + return( -1 ); + } + memcpy( &v, xv, NB_R64 ); + if( put16( c, CPU_OP_LDR ) || preg( c, av[0], 0, 1, eqs, err, es ) ) return( -1 ); + memcpy( &u, &v, 8 ); + return( put64( c, u ) ); + } + if( streqi( mn, "LDC" ) ) + { + float r, j; + __mpu_uint32_t ur, ui; + __mpu_uint64_t u; + cpu_register rg; + int ic, cr; + if( ac != 2 || regparse( av[0], &rg, &ic, eqs, err, es ) || !ic ) + { + snprintf( err, es, "bad LDC destination" ); + return( -1 ); + } + cr = cimm_exact( av[1], &r, &j, err, es ); + if( cr == -1 ) + { + snprintf( err, es, "bad complex immediate" ); + return( -1 ); + } + if( cr == -2 ) return( -1 ); + memcpy( &ur, &r, 4 ); + memcpy( &ui, &j, 4 ); + u = ( (__mpu_uint64_t)ur << 32 ) | ui; + if( put16( c, CPU_OP_LDC ) || preg( c, av[0], 1, 0, eqs, err, es ) || put64( c, u ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "RCONST" ) ) + { + if( ac != 2 || constid( av[1], &k ) ) + { + snprintf( err, es, "bad RCONST" ); + return( -1 ); + } + if( put16( c, CPU_OP_RCONST ) || preg( c, av[0], 0, 1, eqs, err, es ) || put8( c, k ) ) return( -1 ); + return( 0 ); + } + + if( streqi( mn, "ISHLN" ) || streqi( mn, "ISHRN" ) || streqi( mn, "ISALN" ) || streqi( mn, "ISARN" ) || + streqi( mn, "IROLN" ) || streqi( mn, "IRORN" ) || streqi( mn, "IRCLN" ) || streqi( mn, "IRCRN" )) + { + unsigned op; + if( ac != 3 ) + { + snprintf( err, es, "%s requires dst, src, r64 count", mn ); + return( -1 ); + } + if( streqi( mn, "ISHLN" ) ) op = CPU_OP_ISHLN; + else if( streqi( mn, "ISHRN" ) ) op = CPU_OP_ISHRN; + else if( streqi( mn, "ISALN" ) ) op = CPU_OP_ISALN; + else if( streqi( mn, "ISARN" ) ) op = CPU_OP_ISARN; + else if( streqi( mn, "IROLN" ) ) op = CPU_OP_IROLN; + else if( streqi( mn, "IRORN" ) ) op = CPU_OP_IRORN; + else if( streqi( mn, "IRCLN" ) ) op = CPU_OP_IRCLN; + else op = CPU_OP_IRCRN; + if( put16( c, op ) || preg( c, av[0], 0, 1, eqs, err, es ) || preg( c, av[1], 0, 1, eqs, err, es ) || + preg_r64( c, av[2], "shift count", eqs, err, es )) return( -1 ); + return( 0 ); + } + if( streqi( mn, "RCMP" ) ) + { + if( ac != 3 ) + { + snprintf( err, es, "RCMP requires r64 result, real a, real b" ); + return( -1 ); + } + if( put16( c, CPU_OP_RCMP ) || preg_r64( c, av[0], "RCMP result", eqs, err, es ) || + preg( c, av[1], 0, 1, eqs, err, es ) || preg( c, av[2], 0, 1, eqs, err, es )) return( -1 ); + return( 0 ); + } + if( streqi( mn, "RISNEG" ) || streqi( mn, "RSIGN" ) || streqi( mn, "RISIND" ) || + streqi( mn, "RISNAN" ) || streqi( mn, "RISNANMAX" ) || streqi( mn, "RISNANMIN" ) || + streqi( mn, "RISINF" ) || streqi( mn, "RISSIGNULL" )) + { + unsigned op; + if( ac != 2 ) + { + snprintf( err, es, "%s requires r64 result, real source", mn ); + return( -1 ); + } + if( streqi( mn, "RISNEG" ) ) op = CPU_OP_RISNEG; + else if( streqi( mn, "RSIGN" ) ) op = CPU_OP_RSIGN; + else if( streqi( mn, "RISIND" ) ) op = CPU_OP_RISIND; + else if( streqi( mn, "RISNAN" ) ) op = CPU_OP_RISNAN; + else if( streqi( mn, "RISNANMAX" ) ) op = CPU_OP_RISNANMAX; + else if( streqi( mn, "RISNANMIN" ) ) op = CPU_OP_RISNANMIN; + else if( streqi( mn, "RISINF" ) ) op = CPU_OP_RISINF; + else op = CPU_OP_RISSIGNULL; + if( put16( c, op ) || preg_r64( c, av[0], "real predicate result", eqs, err, es ) || + preg( c, av[1], 0, 1, eqs, err, es )) return( -1 ); + return( 0 ); + } + if( streqi( mn, "CREAL" ) || streqi( mn, "CIMAG" ) || streqi( mn, "CABS" ) || + streqi( mn, "CNORM" ) || streqi( mn, "CARG" )) + { + unsigned op; + if( ac != 2 ) + { + snprintf( err, es, "%s requires real destination, complex source", mn ); + return( -1 ); + } + if( streqi( mn, "CREAL" ) ) op = CPU_OP_CREAL; + else if( streqi( mn, "CIMAG" ) ) op = CPU_OP_CIMAG; + else if( streqi( mn, "CABS" ) ) op = CPU_OP_CABS; + else if( streqi( mn, "CNORM" ) ) op = CPU_OP_CNORM; + else op = CPU_OP_CARG; + if( put16( c, op ) || preg( c, av[0], 0, 1, eqs, err, es ) || preg( c, av[1], 1, 0, eqs, err, es ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "CRTOC" ) ) + { + if( ac != 2 ) + { + snprintf( err, es, "CRTOC requires complex destination, real source" ); + return( -1 ); + } + if( put16( c, CPU_OP_CRTOC ) || preg( c, av[0], 1, 0, eqs, err, es ) || preg( c, av[1], 0, 1, eqs, err, es ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "CGEN" ) || streqi( mn, "CPOLAR" ) ) + { + unsigned op = streqi( mn, "CGEN" ) ? CPU_OP_CGEN : CPU_OP_CPOLAR; + if( ac != 3 ) + { + snprintf( err, es, "%s requires complex destination, two real sources", mn ); + return( -1 ); + } + if( put16( c, op ) || preg( c, av[0], 1, 0, eqs, err, es ) || preg( c, av[1], 0, 1, eqs, err, es ) || + preg( c, av[2], 0, 1, eqs, err, es )) return( -1 ); + return( 0 ); + } + if( streqi( mn, "CISEQ" ) || streqi( mn, "CISNE" ) ) + { + unsigned op = streqi( mn, "CISEQ" ) ? CPU_OP_CISEQ : CPU_OP_CISNE; + if( ac != 3 ) + { + snprintf( err, es, "%s requires r64 result, complex a, complex b", mn ); + return( -1 ); + } + if( put16( c, op ) || preg_r64( c, av[0], "complex predicate result", eqs, err, es ) || + preg( c, av[1], 1, 0, eqs, err, es ) || preg( c, av[2], 1, 0, eqs, err, es )) return( -1 ); + return( 0 ); + } + if( streqi( mn, "LOAD" ) || streqi( mn, "STORE" ) ) + { + int store = streqi( mn, "STORE" ); + if( ac != 2 ) + { + snprintf( err, es, "wrong operand count" ); + return( -1 ); + } + if( put16( c, store ? CPU_OP_STORE : CPU_OP_LOAD ) ) return( -1 ); + if( store ) + { + if( addfix( df, c->n, av[0] ) || put64( c, 0 ) || preg( c, av[1], 0, 0, eqs, err, es ) ) return( -1 ); + } + else + { + if( preg( c, av[0], 0, 0, eqs, err, es ) || addfix( df, c->n, av[1] ) || put64( c, 0 ) ) return( -1 ); + } + return( 0 ); + } + if( streqi( mn, "LEA" ) ) + { + if( ac != 2 ) + { + snprintf( err, es, "LEA requires r64 destination and pointer expression" ); + return( -1 ); + } + if( put16( c, CPU_OP_LEA ) || preg_ptr( c, av[0], eqs, err, es ) || addfix( pf, c->n, av[1] ) || put64( c, 0 ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "LEASP" ) ) + { + __mpu_int64_t off; + if( ac != 2 ) + { + snprintf( err, es, "LEASP requires r64 destination and signed SP offset" ); + return( -1 ); + } + { + int er = eval_i64( av[1], eqs, 0, &off, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 ) + { + snprintf( err, es, "LEASP offset must fit signed 64 bits" ); + return( -1 ); + } + } + if( put16( c, CPU_OP_LEASP ) || preg_ptr( c, av[0], eqs, err, es ) || put64( c, (__mpu_uint64_t)off ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "LEAFP" ) ) + { + __mpu_int64_t off; + if( ac != 2 ) + { + snprintf( err, es, "LEAFP requires r64 destination and signed FP offset" ); + return( -1 ); + } + { + int er = eval_i64( av[1], eqs, 0, &off, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 ) + { + snprintf( err, es, "LEAFP offset must fit signed 64 bits" ); + return( -1 ); + } + } + if( put16( c, CPU_OP_LEAFP ) || preg_ptr( c, av[0], eqs, err, es ) || put64( c, (__mpu_uint64_t)off ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "LOADP" ) || streqi( mn, "STOREP" ) ) + { + int store = streqi( mn, "STOREP" ); + if( ac != 2 ) + { + snprintf( err, es, "%s requires two register operands", mn ); + return( -1 ); + } + if( put16( c, store ? CPU_OP_STOREP : CPU_OP_LOADP ) ) return( -1 ); + if( store ) + { + if( preg_ptr( c, av[0], eqs, err, es ) || preg( c, av[1], 0, 0, eqs, err, es ) ) return( -1 ); + } + else + { + if( preg( c, av[0], 0, 0, eqs, err, es ) || preg_ptr( c, av[1], eqs, err, es ) ) return( -1 ); + } + return( 0 ); + } + if( streqi( mn, "PADD" ) || streqi( mn, "PSUB" ) || streqi( mn, "PDIFF" ) ) + { + unsigned op = streqi( mn, "PADD" ) ? CPU_OP_PADD : ( streqi( mn, "PSUB" ) ? CPU_OP_PSUB : CPU_OP_PDIFF ); + if( ac != 3 ) + { + snprintf( err, es, "%s requires three r64 operands", mn ); + return( -1 ); + } + if( put16( c, op ) || + preg_r64( c, av[0], mn, eqs, err, es ) || + preg_r64( c, av[1], mn, eqs, err, es ) || + preg_r64( c, av[2], mn, eqs, err, es )) return( -1 ); + return( 0 ); + } + if( streqi( mn, "PADDI" ) || streqi( mn, "PSUBI" ) ) + { + __mpu_int64_t off; + unsigned op = streqi( mn, "PADDI" ) ? CPU_OP_PADDI : CPU_OP_PSUBI; + if( ac != 3 ) + { + snprintf( err, es, "%s requires r64 destination, r64 PTR64 source and signed OFF64", mn ); + return( -1 ); + } + { + int er = eval_i64( av[2], eqs, 0, &off, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 ) + { + snprintf( err, es, "%s offset must fit signed 64 bits", mn ); + return( -1 ); + } + } + if( put16( c, op ) || + preg_r64( c, av[0], mn, eqs, err, es ) || + preg_r64( c, av[1], mn, eqs, err, es ) || + put64( c, (__mpu_uint64_t)off )) return( -1 ); + return( 0 ); + } + if( streqi( mn, "BMOV" ) ) + { + if( ac != 3 ) + { + snprintf( err, es, "BMOV requires r64 destination PTR64, r64 source PTR64 and r64 byte count" ); + return( -1 ); + } + if( put16( c, CPU_OP_BMOV ) || + preg_ptr( c, av[0], eqs, err, es ) || + preg_ptr( c, av[1], eqs, err, es ) || + preg_r64( c, av[2], "BMOV byte count", eqs, err, es )) return( -1 ); + return( 0 ); + } + if( streqi( mn, "BMOVI" ) ) + { + __mpu_uint64_t count; + if( ac != 3 ) + { + snprintf( err, es, "BMOVI requires r64 destination PTR64, r64 source PTR64 and unsigned IMM64 byte count" ); + return( -1 ); + } + { + int er = eval_u64( av[2], eqs, 0, &count, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 ) + { + snprintf( err, es, "BMOVI byte count must fit unsigned 64 bits" ); + return( -1 ); + } + } + if( put16( c, CPU_OP_BMOVI ) || + preg_ptr( c, av[0], eqs, err, es ) || + preg_ptr( c, av[1], eqs, err, es ) || + put64( c, count )) return( -1 ); + return( 0 ); + } + if( streqi( mn, "JMPP" ) || streqi( mn, "CALLP" ) ) + { + unsigned op = streqi( mn, "CALLP" ) ? CPU_OP_CALLP : CPU_OP_JMPP; + if( ac != 1 ) + { + snprintf( err, es, "%s requires one r64 PROGRAM PTR64 operand", mn ); + return( -1 ); + } + if( put16( c, op ) || preg_ptr( c, av[0], eqs, err, es ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "JMP" ) || streqi( mn, "CALL" ) ) + { + int call = streqi( mn, "CALL" ); + if( ac != 1 ) + { + snprintf( err, es, "%s requires one CODE label", mn ); + return( -1 ); + } + if( put16( c, call ? CPU_OP_CALL : CPU_OP_JMP ) || addfix( cf, c->n, av[0] ) || put64( c, 0 ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "RET" ) ) + { + if( ac != 0 ) + { + snprintf( err, es, "RET takes no operands" ); + return( -1 ); + } + return( put16( c, CPU_OP_RET ) ); + } + if( streqi( mn, "JSET" ) || streqi( mn, "JCLR" ) ) + { + unsigned bit; + if( ac != 2 || pswbitid( av[0], &bit ) ) + { + snprintf( err, es, "bad PSW bit for %s", mn ); + return( -1 ); + } + if( put16( c, streqi( mn, "JSET" ) ? CPU_OP_JSET : CPU_OP_JCLR ) || + put8( c, bit ) || addfix( cf, c->n, av[1] ) || put64( c, 0 )) return( -1 ); + return( 0 ); + } + { + struct A + { + const char *n; + unsigned op; + unsigned bit; + } + as[] = + { + { "JZ", CPU_OP_JSET, CPU_PSWBIT_ZF }, + { "JNZ", CPU_OP_JCLR, CPU_PSWBIT_ZF }, + { "JC", CPU_OP_JSET, CPU_PSWBIT_CF }, + { "JNC", CPU_OP_JCLR, CPU_PSWBIT_CF }, + { "JO", CPU_OP_JSET, CPU_PSWBIT_OF }, + { "JNO", CPU_OP_JCLR, CPU_PSWBIT_OF }, + { "JS", CPU_OP_JSET, CPU_PSWBIT_SF }, + { "JNS", CPU_OP_JCLR, CPU_PSWBIT_SF } + }; + size_t ai; + for( ai = 0; ai < sizeof( as ) / sizeof( as[0] ); ai++ ) if( streqi( mn, as[ai].n ) ) + { + if( ac != 1 ) + { + snprintf( err, es, "%s requires one CODE label", mn ); + return( -1 ); + } + if( put16( c, as[ai].op ) || put8( c, as[ai].bit ) || addfix( cf, c->n, av[0] ) || put64( c, 0 ) ) return( -1 ); + return( 0 ); + } + } + if( streqi( mn, "LDSP" ) || streqi( mn, "STSP" ) ) + { + __mpu_int64_t off; + int store = streqi( mn, "STSP" ); + if( ac != 2 ) + { + snprintf( err, es, "%s requires two operands", mn ); + return( -1 ); + } + { + int er = eval_i64( store ? av[0] : av[1], eqs, 0, &off, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 ) + { + snprintf( err, es, "%s offset must fit signed 64 bits", mn ); + return( -1 ); + } + } + if( put16( c, store ? CPU_OP_STSP : CPU_OP_LDSP ) ) return( -1 ); + if( store ) + { + if( put64( c, (__mpu_uint64_t)off ) || preg( c, av[1], 0, 0, eqs, err, es ) ) return( -1 ); + } + else + { + if( preg( c, av[0], 0, 0, eqs, err, es ) || put64( c, (__mpu_uint64_t)off ) ) return( -1 ); + } + return( 0 ); + } + if( streqi( mn, "LDFP" ) || streqi( mn, "STFP" ) ) + { + __mpu_int64_t off; + int store = streqi( mn, "STFP" ); + if( ac != 2 ) + { + snprintf( err, es, "%s requires two operands", mn ); + return( -1 ); + } + { + int er = eval_i64( store ? av[0] : av[1], eqs, 0, &off, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 ) + { + snprintf( err, es, "%s offset must fit signed 64 bits", mn ); + return( -1 ); + } + } + if( put16( c, store ? CPU_OP_STFP : CPU_OP_LDFP ) ) return( -1 ); + if( store ) + { + if( put64( c, (__mpu_uint64_t)off ) || preg( c, av[1], 0, 0, eqs, err, es ) ) return( -1 ); + } + else + { + if( preg( c, av[0], 0, 0, eqs, err, es ) || put64( c, (__mpu_uint64_t)off ) ) return( -1 ); + } + return( 0 ); + } + if( streqi( mn, "ADJSP" ) ) + { + __mpu_int64_t off; + if( ac != 1 ) + { + snprintf( err, es, "ADJSP requires one signed byte offset" ); + return( -1 ); + } + { + int er = eval_i64( av[0], eqs, 0, &off, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 ) + { + snprintf( err, es, "ADJSP offset must fit signed 64 bits" ); + return( -1 ); + } + } + if( put16( c, CPU_OP_ADJSP ) || put64( c, (__mpu_uint64_t)off ) ) return( -1 ); + return( 0 ); + } + if( streqi( mn, "SETSP" ) ) + { + if( ac != 1 ) + { + snprintf( err, es, "SETSP requires one r64 PTR64 operand" ); + return( -1 ); + } + if( put16( c, CPU_OP_SETSP ) || preg_ptr( c, av[0], eqs, err, es ) ) return( -1 ); + return( 0 ); + } + snprintf( err, es, "unknown mnemonic: %s", mn ); + return( -1 ); +} + +/* Special LDI emitter kept separate to make signed 64-bit parsing explicit. */ +static int +emit_ldi( buf_t *c, equs_t *eqs, char *rest, char *err, size_t es ) +{ + char *av[3]; + __mpu_int64_t v; + __int128 xv; + cpu_register r; + int ic, ac = splitargs( rest, av, 3 ); + if( ac != 2 ) + { + snprintf( err, es, "wrong operand count for LDI" ); + return( -1 ); + } + if( regparse( av[0], &r, &ic, eqs, err, es ) || ic ) + { + snprintf( err, es, "bad LDI destination" ); + return( -1 ); + } + if( eval_expression( av[1], eqs, 0, &xv, err, es ) ) + { + snprintf( err, es, "bad integer immediate: %s", av[1] ); + return( -1 ); + } + if( xv < ( __int128 ) INT64_MIN || xv > ( __int128 ) INT64_MAX ) + { + snprintf( err, es, "LDI immediate is not exactly representable as integer64;\nuse .integer <bits>, value in DATA and LOAD" ); + return( -1 ); + } + v = (__mpu_int64_t)xv; + if( r.bits < 64 ) + { + __mpu_int64_t lo = - ( (__mpu_int64_t)1 << ( r.bits - 1 ) ), hi = ( (__mpu_int64_t)1 << ( r.bits - 1 ) ) - 1; + if( v < lo || v > hi ) + { + snprintf( err, es, "LDI immediate is not exactly representable as integer%u;\nuse .integer <bits>, value in DATA and LOAD", r.bits ); + return( -1 ); + } + } + if( put16( c, CPU_OP_LDI ) || preg( c, av[0], 0, 1, eqs, err, es ) || put64( c, (__mpu_uint64_t)v ) ) return( -1 ); + return( 0 ); +} + +static int datadir( buf_t *d, char *line, fixups_t *cptr_fix, fixups_t *ptr_fix, + __mpu_uint64_t *section_align, equs_t *eqs, char *err, size_t es) +{ + char *p = trim( line ), *mn = p, *rest, *av[256]; + int ac, bits, nb; + __mpu_uint64_t uv; + while( *p && !isspace( (unsigned char)*p ) ) p++; + if( *p ) + { + *p++ = 0; + rest = trim( p ); + } + else rest = (char *)""; + + if( streqi( mn, ".align" ) ) + { + if( !*rest ) + { + snprintf( err, es, ".align requires a value" ); + return( -1 ); + } + { + int er = eval_u64( rest, eqs, 0, &uv, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 || !uv || uv > (__mpu_uint64_t)SIZE_MAX ) + { + snprintf( err, es, "bad .align" ); + return( -1 ); + } + } + if( section_align && uv > *section_align ) * section_align = uv; + while( d->n % (size_t)uv ) if( put8( d, 0 ) ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + return( 0 ); + } + + if( streqi( mn, ".utf8" ) || streqi( mn, ".ucs2" ) ) + { + buf_t text = + { + 0 + }; + if( !*rest ) + { + snprintf( err, es, "%s requires a quoted UTF-8 string", mn ); + return( -1 ); + } + if( parse_utf8_string( rest, &text, err, es ) ) return( -1 ); + if( streqi( mn, ".utf8" ) ) + { + if( put( d, text.p, text.n ) ) + { + free( text.p ); + snprintf( err, es, "out of memory" ); + return( -1 ); + } + } + else + { + __mpu_size_t nch = mpu_utf8_to_ucs2( 0, (const __mpu_char8_t *)text.p, 0 ), i; + __mpu_char16_t *ucs; + if( nch == (__mpu_size_t)-1 ) + { + free( text.p ); + snprintf( err, es, ".ucs2 string is not representable as strict UCS-2" ); + return( -1 ); + } + ucs = (__mpu_char16_t *)calloc( (size_t)nch + 1, sizeof( *ucs ) ); + if( !ucs ) + { + free( text.p ); + snprintf( err, es, "out of memory" ); + return( -1 ); + } + if( mpu_utf8_to_ucs2( ucs, (const __mpu_char8_t *)text.p, nch + 1 ) != nch ) + { + free( ucs ); + free( text.p ); + snprintf( err, es, "UTF-8 to UCS-2 conversion failed" ); + return( -1 ); + } + for( i = 0; i < nch; i++ ) + { + __mpu_UNIT_t x[2]; + __cpu_data_put_u16( x, (__mpu_uint16_t)ucs[i] ); + if( put( d, x, 2 ) ) + { + free( ucs ); + free( text.p ); + snprintf( err, es, "out of memory" ); + return( -1 ); + } + } + { + __mpu_UNIT_t z[2]; + __cpu_data_put_u16( z, 0 ); + if( put( d, z, 2 ) ) + { + free( ucs ); + free( text.p ); + snprintf( err, es, "out of memory" ); + return( -1 ); + } + } + free( ucs ); + } + free( text.p ); + return( 0 ); + } + + if( !*rest ) + { + snprintf( err, es, "data directive requires operands" ); + return( -1 ); + } + ac = splitargs( rest, av, 256 ); + if( ac < 0 ) + { + snprintf( err, es, "too many or malformed directive arguments" ); + return( -1 ); + } + + if( streqi( mn, ".byte" ) ) + { + int i; + if( ac < 1 ) + { + snprintf( err, es, ".byte requires one or more values" ); + return( -1 ); + } + for( i = 0; i < ac; i++ ) + { + int er = eval_u64( av[i], eqs, 0, &uv, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 || uv > 255 ) + { + snprintf( err, es, ".byte value must fit 8 bits: %s", av[i] ); + return( -1 ); + } + if( put8( d, (unsigned)uv ) ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + } + return( 0 ); + } + + if( streqi( mn, ".integer" ) || streqi( mn, ".int" ) ) + { + int i; + mpu_wide_int_t x; + if( ac < 2 ) + { + snprintf( err, es, "%s requires: bits, value[, value...]", mn ); + return( -1 ); + } + { + int er = eval_u64( av[0], eqs, 0, &uv, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 || uv > 65536 ) + { + snprintf( err, es, "bad .integer width" ); + return( -1 ); + } + } + bits = (int)uv; + nb = bits / 8; + if( bits < 8 || ( bits & ( bits - 1 ) ) ) + { + snprintf( err, es, "bad .integer width" ); + return( -1 ); + } + for( i = 1; i < ac; i++ ) + { + const char *value = av[i]; + char dec[64]; + __int128 ev; + memset( x, 0, sizeof( x ) ); + if( !plain_integer_literal( value ) ) + { + if( eval_expression( value, eqs, 0, &ev, err, es ) ) return( -1 ); + i128_decimal( ev, dec ); + value = dec; + } + iatoi( ( mpu_int * ) x, (__mpu_char8_t *)value, nb ); + if( put( d, x, (size_t)nb ) ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + } + return( 0 ); + } + + if( streqi( mn, ".real" ) ) + { + mpu_wide_real_t x; + if( ac != 2 ) + { + snprintf( err, es, ".real requires: bits, value" ); + return( -1 ); + } + { + int er = eval_u64( av[0], eqs, 0, &uv, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 || uv > 65536 ) + { + snprintf( err, es, "bad .real width" ); + return( -1 ); + } + } + bits = (int)uv; + nb = bits / 8; + if( bits < 32 || ( bits & ( bits - 1 ) ) ) + { + snprintf( err, es, "bad .real width" ); + return( -1 ); + } + memset( x, 0, sizeof( x ) ); + if( ascii_to_real( ( mpu_real * ) x, (__mpu_char8_t *)av[1], nb ) < 0 ) + { + snprintf( err, es, "bad .real constant" ); + return( -1 ); + } + if( put( d, x, (size_t)nb ) ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + return( 0 ); + } + + if( streqi( mn, ".complex" ) ) + { + mpu_wide_complex_t x; + mpu_wide_real_t r, j; + if( ac != 3 ) + { + snprintf( err, es, ".complex requires: physical-bits, real, imag" ); + return( -1 ); + } + { + int er = eval_u64( av[0], eqs, 0, &uv, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 || uv > 65536 ) + { + snprintf( err, es, "bad .complex physical width" ); + return( -1 ); + } + } + bits = (int)uv; + nb = bits / 8; + if( bits < 64 || ( bits & ( bits - 1 ) ) ) + { + snprintf( err, es, "bad .complex physical width" ); + return( -1 ); + } + if( ascii_to_real( ( mpu_real * ) r, (__mpu_char8_t *)av[1], nb / 2 ) < 0 || ascii_to_real( ( mpu_real * ) j, (__mpu_char8_t *)av[2], nb / 2 ) < 0 ) + { + snprintf( err, es, "bad .complex component" ); + return( -1 ); + } + c_gen_complex( ( mpu_complex * ) x, ( mpu_real * ) r, ( mpu_real * ) j, nb / 2 ); + if( put( d, x, (size_t)nb ) ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + return( 0 ); + } + + if( streqi( mn, ".cptr" ) || streqi( mn, ".codeptr" ) ) + { + __mpu_uint64_t zero = 0; + if( ac != 1 || !*av[0] ) + { + snprintf( err, es, ".cptr requires one CODE expression" ); + return( -1 ); + } + if( addfix( cptr_fix, d->n, av[0] ) || put( d, &zero, CPU_PTR64_SIZE ) ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + return( 0 ); + } + + if( streqi( mn, ".ptr" ) ) + { + __mpu_uint64_t zero = 0; + if( ac != 1 || !*av[0] ) + { + snprintf( err, es, ".ptr requires one pointer expression" ); + return( -1 ); + } + if( addfix( ptr_fix, d->n, av[0] ) || put( d, &zero, CPU_PTR64_SIZE ) ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + return( 0 ); + } + + snprintf( err, es, "unknown data directive: %s", mn ); + return( -1 ); +} + +static int +valid_symbol_name( const char *s ) +{ + if( !s || !symfirst( (unsigned char)*s ) ) return( 0 ); + for( s++; *s; s++ ) if( !symrest( (unsigned char)*s ) ) return( 0 ); + return( 1 ); +} + +static int +finddecl( const symdecls_t *d, const char *name, size_t *idx ) +{ + size_t i; + for( i = 0; i < d->n; i++ ) if( !strcmp( d->p[i].name, name ) ) + { + if( idx ) * idx = i; + return( 0 ); + } + return( -1 ); +} + +static int adddecl( symdecls_t *d, const char *name, int is_global, int is_extern, int is_weak, + char *err, size_t es) +{ + size_t i; + if( !valid_symbol_name( name ) ) + { + snprintf( err, es, "bad symbol name: %s", name ); + return( -1 ); + } + if( !strcmp( name, "_start_life" ) ) + { + snprintf( err, es, "_start_life is an assembler pseudo-label and cannot be declared" ); + return( -1 ); + } + if( is_weak && ( !strcmp( name, "_start" ) || !strcmp( name, "_life" ) || !strcmp( name, "_fini" ) ) ) + { + snprintf( err, es, "lifecycle symbol %s cannot be weak", name ); + return( -1 ); + } + if( !finddecl( d, name, &i ) ) + { + if( is_global ) d->p[i].is_global = 1; + if( is_extern ) d->p[i].is_extern = 1; + if( is_weak ) d->p[i].is_weak = 1; + return( 0 ); + } + if( grow( (void **)&d->p, &d->cap, d->n + 1, sizeof( *d->p ) ) ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + memset( &d->p[d->n], 0, sizeof( *d->p ) ); + strcpy( d->p[d->n].name, name ); + d->p[d->n].is_global = is_global; + d->p[d->n].is_extern = is_extern; + d->p[d->n].is_weak = is_weak; + d->n++; + return( 0 ); +} + +/* Return 1 for a declaration line, 0 otherwise, -1 on error. */ +static int +collect_decl_line( char *line, symdecls_t *decls, int line_no, char *err, size_t es ) +{ + char *p = trim( line ), *mn = p, *rest, *av[256]; + int ac, i, is_global = 0, is_extern = 0, is_weak = 0; + while( *p && !isspace( (unsigned char)*p ) ) p++; + if( *p ) + { + *p++ = 0; + rest = trim( p ); + } + else rest = (char *)""; + if( streqi( mn, ".global" ) || streqi( mn, ".globl" ) ) is_global = 1; + else + if( streqi( mn, ".extern" ) ) + { + is_global = 1; + is_extern = 1; + } + else + if( streqi( mn, ".weak" ) ) + { + is_weak = 1; + } + else return( 0 ); + if( !*rest ) + { + snprintf( err, es, "line %d: %s requires one or more symbol names", line_no, mn ); + return( -1 ); + } + ac = splitargs( rest, av, 256 ); + if( ac < 1 ) + { + snprintf( err, es, "line %d: malformed %s", line_no, mn ); + return( -1 ); + } + for( i = 0; i < ac; i++ ) if( adddecl( decls, av[i], is_global, is_extern, is_weak, err, es ) ) + { + char t[256]; + snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); + snprintf( err, es, "%s", t ); + return( -1 ); + } + return( 1 ); +} + +/* .bss is SHT_NOBITS: only layout directives are legal and no payload + bytes are emitted into the relocatable object. */ +static int bssdir( size_t *bss_size, char *line, __mpu_uint64_t *section_align, + equs_t *eqs, char *err, size_t es) +{ + char *p = trim( line ), *mn = p, *rest; + __mpu_uint64_t v; + + while( *p && !isspace( (unsigned char)*p ) ) p++; + if( *p ) + { + *p++ = 0; + rest = trim( p ); + } + else rest = (char *)""; + + if( streqi( mn, ".align" ) ) + { + __mpu_uint64_t rem, add; + int er; + if( !*rest ) + { + snprintf( err, es, ".align requires a value" ); + return( -1 ); + } + er = eval_u64( rest, eqs, 0, &v, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 || !v || v > (__mpu_uint64_t)SIZE_MAX || ( v & ( v - 1 ) ) ) + { + snprintf( err, es, "bad .align" ); + return( -1 ); + } + if( section_align && v > *section_align ) * section_align = v; + rem = (__mpu_uint64_t)( *bss_size ) % v; + add = rem ? v - rem : 0; + if( add > (__mpu_uint64_t)SIZE_MAX - *bss_size ) + { + snprintf( err, es, ".bss size overflow" ); + return( -1 ); + } + *bss_size+=(size_t)add; + return( 0 ); + } + + if( streqi( mn, ".zero" ) ) + { + int er; + if( !*rest ) + { + snprintf( err, es, ".zero requires a byte count" ); + return( -1 ); + } + er = eval_u64( rest, eqs, 0, &v, err, es ); + if( er < 0 ) return( -1 ); + if( er > 0 || v > (__mpu_uint64_t)SIZE_MAX - *bss_size ) + { + snprintf( err, es, "bad .zero byte count" ); + return( -1 ); + } + *bss_size+=(size_t)v; + return( 0 ); + } + + snprintf( err, es, "only .align and .zero are valid in .bss" ); + return( -1 ); +} + +/* Return 1 when line is .equ, 0 when it is not, -1 on malformed .equ. */ +static int +collect_equ_line( char *line, equs_t *eqs, int line_no, char *err, size_t es ) +{ + char *p = trim( line ), *mn = p, *rest, *av[2]; + int ac; + while( *p && !isspace( (unsigned char)*p ) ) p++; + if( *p ) + { + *p++ = 0; + rest = trim( p ); + } + else rest = (char *)""; + if( !streqi( mn, ".equ" ) ) return( 0 ); + if( !*rest ) + { + snprintf( err, es, "line %d: .equ requires NAME, expression", line_no ); + return( -1 ); + } + ac = splitargs( rest, av, 2 ); + if( ac != 2 || !valid_symbol_name( av[0] ) || !*av[1] ) + { + snprintf( err, es, "line %d: .equ requires NAME, expression", line_no ); + return( -1 ); + } + if( addequ( eqs, av[0], av[1], line_no, err, es ) ) + { + char t[256]; + snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); + snprintf( err, es, "%s", t ); + return( -1 ); + } + return( 1 ); +} + +enum asm_section +{ + ASM_SEC_NONE = 0, + ASM_SEC_TEXT, + ASM_SEC_DATA, + ASM_SEC_BSS, + ASM_SEC_INIT, + ASM_SEC_FINI, + ASM_SEC_CONTROL, + ASM_SEC_INPUT, + ASM_SEC_OUTPUT, + ASM_SEC_TRACE +}; + +static int label_in_any( const char *name, const labels_t *a, const labels_t *b, + const labels_t *c, const labels_t *d, const labels_t *e, + const labels_t *f) +{ + __mpu_uint64_t x; + return( a && !findlabel( a, name, &x ) ) || ( b && !findlabel( b, name, &x ) ) || + ( c && !findlabel( c, name, &x ) ) || ( d && !findlabel( d, name, &x ) ) || + ( e && !findlabel( e, name, &x ) ) || ( f && !findlabel( f, name, &x ) ); +} + +static int resolve_ptr_expression( const char *expr, equs_t *eqs, + const labels_t *code, const labels_t *data, + const labels_t *control, const labels_t *input, + const labels_t *output, const labels_t *trace, + const size_t sizes[8], __mpu_uint64_t *ptr, + char *err, size_t es) +{ + const labels_t *ls[6] = + { + data, control, input, output, trace, code + }; + const unsigned segs[6] = + { + CPU_PTR_SEG_DATA, CPU_PTR_SEG_CONTROL, CPU_PTR_SEG_INPUT, + CPU_PTR_SEG_OUTPUT, CPU_PTR_SEG_TRACE, CPU_PTR_SEG_PROGRAM + }; + __mpu_uint64_t value = 0; + int i, found = 0, er; + unsigned found_seg = CPU_PTR_SEG_DATA; + char tmp[256]; + + /* A pure absolute/.equ expression keeps historical LEA semantics: DATA. */ + er = eval_u64( expr, eqs, 0, &value, tmp, sizeof( tmp ) ); + if( er == 0 ) + { + if( value > sizes[CPU_PTR_SEG_DATA] ) + { + snprintf( err, es, "LEA DATA expression is outside DATA: %s", expr ); + return( -1 ); + } + return( __cpu_ptr_make( ptr, CPU_PTR_SEG_DATA, value ) ); + } + + for( i = 0; i < 6; i++ ) + { + __mpu_uint64_t v; + if( eval_u64( expr, eqs, ls[i], &v, tmp, sizeof( tmp ) ) == 0 ) + { + found++; + value = v; + found_seg = segs[i]; + } + } + if( found != 1 ) + { + snprintf( err, es, found ? "ambiguous pointer expression: %s" : "unknown symbol in pointer expression: %s", expr ); + return( -1 ); + } + if( value > sizes[found_seg] ) + { + snprintf( err, es, "pointer expression is outside its segment: %s", expr ); + return( -1 ); + } + if( found_seg == CPU_PTR_SEG_HEAP && value < CPU_HEAP_BEGIN ) + { + snprintf( err, es, "pointer expression enters reserved HEAP guard: %s", expr ); + return( -1 ); + } + if( __cpu_ptr_make( ptr, found_seg, value ) ) + { + snprintf( err, es, "cannot encode PTR64 expression: %s", expr ); + return( -1 ); + } + return( 0 ); +} + +static int apply_cptr_fixups( buf_t *dst, const fixups_t *fx, equs_t *eqs, + const labels_t *clabels, size_t code_size, + char *err, size_t es) +{ + size_t i; + for( i = 0; i < fx->n; i++ ) + { + __mpu_uint64_t a, ptr; + int er = eval_u64( fx->p[i].expr, eqs, clabels, &a, err, es ); + if( er < 0 ) + { + char cause[256]; + snprintf( cause, sizeof( cause ), "%s", err ); + snprintf( err, es, ".cptr expression '%s': %s", fx->p[i].expr, cause ); + return( -1 ); + } + if( er > 0 || a >= code_size ) + { + snprintf( err, es, ".cptr expression does not address PROGRAM: %s", fx->p[i].expr ); + return( -1 ); + } + if( __cpu_ptr_make( &ptr, CPU_PTR_SEG_PROGRAM, a ) ) + { + snprintf( err, es, "cannot encode PROGRAM PTR64 for: %s", fx->p[i].expr ); + return( -1 ); + } + __cpu_data_put_u64( dst->p + fx->p[i].pos, ptr ); + } + return( 0 ); +} + +static int apply_ptr_data_fixups( buf_t *dst, const fixups_t *fx, equs_t *eqs, + const labels_t *code, const labels_t *data, + const labels_t *control, const labels_t *input, + const labels_t *output, const labels_t *trace, + const size_t sizes[8], char *err, size_t es) +{ + size_t i; + for( i = 0; i < fx->n; i++ ) + { + __mpu_uint64_t ptr; + if( resolve_ptr_expression( fx->p[i].expr, eqs, code, data, control, input, output, trace, + sizes, &ptr, err, es)) + { + char cause[256]; + snprintf( cause, sizeof( cause ), "%s", err ); + snprintf( err, es, ".ptr expression '%s': %s", fx->p[i].expr, cause ); + return( -1 ); + } + __cpu_data_put_u64( dst->p + fx->p[i].pos, ptr ); + } + return( 0 ); +} + +int +cpu_assemble_text( const char *src, cpu_asm_image *im, char *err, size_t es ) +{ + char *copy = 0, *scan = 0, *save, *line; + buf_t code = + { + 0 + } + , data = + { + 0 + } + , control = + { + 0 + } + , input = + { + 0 + } + , output = + { + 0 + } + , trace = + { + 0 + }; + labels_t clabels = + { + 0 + } + , dlabels = + { + 0 + } + , ctlabels = + { + 0 + } + , ilabels = + { + 0 + } + , olabels = + { + 0 + } + , tlabels = + { + 0 + }; + fixups_t dfix = + { + 0 + } + , cfix = + { + 0 + } + , pfix = + { + 0 + }; + fixups_t dcptr = + { + 0 + } + , ccptr = + { + 0 + } + , icptr = + { + 0 + } + , ocptr = + { + 0 + } + , tcptr = + { + 0 + }; + fixups_t dptr = + { + 0 + } + , ctptr = + { + 0 + } + , iptr = + { + 0 + } + , optr = + { + 0 + } + , tptr = + { + 0 + }; + __mpu_uint64_t dalign = 1, ctalign = 1, ialign = 1, oalign = 1, talign = 1; + equs_t eqs = + { + 0 + }; + int section = ASM_SEC_NONE, line_no = 0, rc = -1; + int init_seen = 0, text_seen = 0, fini_seen = 0, start_seen = 0, life_seen = 0, fini_entry_seen = 0; + __mpu_uint64_t first_text = 0, start_entry = 0, life_entry = 0, fini_entry = 0; + + if( !src || !im ) return( -1 ); + memset( im, 0, sizeof( *im ) ); + if( !mpu_utf8valid( (const __mpu_char8_t *)src ) ) + { + snprintf( err, es, "assembler source is not valid UTF-8" ); + return( -1 ); + } + + /* Resolve absolute .equ symbols first. Section labels intentionally do not + participate in .equ, preserving the distinction between absolute symbols + and relocatable/segment-relative labels. */ + scan = strdup( src ); + if( !scan ) return( -1 ); + for( line = strtok_r( scan, "\n", &save ); line; line = strtok_r( NULL, "\n", &save ) ) + { + int er; + line_no++; + strip_comment( line ); + if( !*trim( line ) ) continue; + er = collect_equ_line( line, &eqs, line_no, err, es ); + if( er < 0 ) goto out; + } + free( scan ); + scan = 0; + { + size_t i; + __int128 v; + for( i = 0; i < eqs.n; i++ ) if( equ_value( &eqs, i, &v, err, es ) ) + { + char t[256]; + snprintf( t, sizeof( t ), "line %d: %s", eqs.p[i].line, err ); + snprintf( err, es, "%s", t ); + goto out; + } + } + + /* DATA keeps its ABI NULL guard. Other image-backed data spaces begin at + offset zero because their non-zero segment selector already distinguishes + them from the all-zero NULL PTR64. */ + while( data.n < CPU_DATA_NULL_GUARD_SIZE ) + if( put8( &data, 0 ) ) + { + snprintf( err, es, "out of memory" ); + goto out; + } + + copy = strdup( src ); + if( !copy ) goto out; + line_no = 0; + for( line = strtok_r( copy, "\n", &save ); line; line = strtok_r( NULL, "\n", &save ) ) + { + char *p, *colon, *mn, *rest, *q; + buf_t *db = 0; + labels_t *dl = 0; + fixups_t *cpfx = 0, *pfx = 0; + __mpu_uint64_t *salign = 0; + line_no++; + strip_comment( line ); + p = trim( line ); + if( !*p ) continue; + + q = p; + while( *q && !isspace( (unsigned char)*q ) ) q++; + if( (size_t)( q - p ) == 4 && !strncasecmp( p, ".equ", 4 ) ) continue; + + if( streqi( p, ".text" ) ) + { + if( !text_seen ) + { + first_text = (__mpu_uint64_t)code.n; + text_seen = 1; + } + section = ASM_SEC_TEXT; + continue; + } + if( streqi( p, ".init" ) ) + { + init_seen = 1; + section = ASM_SEC_INIT; + continue; + } + if( streqi( p, ".fini" ) ) + { + fini_seen = 1; + section = ASM_SEC_FINI; + continue; + } + if( streqi( p, ".data" ) ) + { + section = ASM_SEC_DATA; + continue; + } + if( streqi( p, ".control" ) ) + { + section = ASM_SEC_CONTROL; + continue; + } + if( streqi( p, ".input" ) ) + { + section = ASM_SEC_INPUT; + continue; + } + if( streqi( p, ".output" ) ) + { + section = ASM_SEC_OUTPUT; + continue; + } + if( streqi( p, ".trace" ) ) + { + section = ASM_SEC_TRACE; + continue; + } + + colon = label_colon( p ); + if( colon ) + { + size_t ei; + char *name; + *colon = 0; + name = trim( p ); + if( !valid_symbol_name( name ) ) + { + snprintf( err, es, "line %d: bad label name", line_no ); + goto out; + } + if( !findequ( &eqs, name, &ei ) ) + { + snprintf( err, es, "line %d: label conflicts with .equ symbol: %s", line_no, name ); + goto out; + } + if( label_in_any( name, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels ) ) + { + snprintf( err, es, "line %d: duplicate label: %s", line_no, name ); + goto out; + } + if( section == ASM_SEC_TEXT || section == ASM_SEC_INIT || section == ASM_SEC_FINI ) + { + if( addlabel( &clabels, name, code.n ) ) + { + snprintf( err, es, "line %d: bad/duplicate CODE label", line_no ); + goto out; + } + if( !strcmp( name, "_start" ) ) + { + if( section != ASM_SEC_INIT ) + { + snprintf( err, es, "line %d: _start must be in .init", line_no ); + goto out; + } + start_seen = 1; + start_entry = code.n; + } + if( !strcmp( name, "_life" ) ) + { + if( section != ASM_SEC_TEXT ) + { + snprintf( err, es, "line %d: _life must be in .text", line_no ); + goto out; + } + life_seen = 1; + life_entry = code.n; + } + if( !strcmp( name, "_fini" ) ) + { + if( section != ASM_SEC_FINI ) + { + snprintf( err, es, "line %d: _fini must be in .fini", line_no ); + goto out; + } + fini_entry_seen = 1; + fini_entry = code.n; + } + } + else + { + switch( section ) + { + case ASM_SEC_DATA: dl = &dlabels; + break; + case ASM_SEC_CONTROL: dl = &ctlabels; + break; + case ASM_SEC_INPUT: dl = &ilabels; + break; + case ASM_SEC_OUTPUT: dl = &olabels; + break; + case ASM_SEC_TRACE: dl = &tlabels; + break; + default: break; + } + if( !dl || addlabel( dl, name, ( section == ASM_SEC_DATA ) ? data.n : ( section == ASM_SEC_CONTROL ) ? control.n : ( section == ASM_SEC_INPUT ) ? input.n : ( section == ASM_SEC_OUTPUT ) ? output.n : trace.n ) ) + { + snprintf( err, es, "line %d: label outside a valid section or duplicate label", line_no ); + goto out; + } + } + p = trim( colon + 1 ); + if( !*p ) continue; + } + + switch( section ) + { + case ASM_SEC_DATA: db = &data; + cpfx = &dcptr; + pfx = &dptr; + salign = &dalign; + break; + case ASM_SEC_CONTROL: db = &control; + cpfx = &ccptr; + pfx = &ctptr; + salign = &ctalign; + break; + case ASM_SEC_INPUT: db = &input; + cpfx = &icptr; + pfx = &iptr; + salign = &ialign; + break; + case ASM_SEC_OUTPUT: db = &output; + cpfx = &ocptr; + pfx = &optr; + salign = &oalign; + break; + case ASM_SEC_TRACE: db = &trace; + cpfx = &tcptr; + pfx = &tptr; + salign = &talign; + break; + default:break; + } + if( db ) + { + if( datadir( db, p, cpfx, pfx, salign, &eqs, err, es ) ) + { + char t[256]; + snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); + snprintf( err, es, "%s", t ); + goto out; + } continue; + } + if( section != ASM_SEC_TEXT && section != ASM_SEC_INIT && section != ASM_SEC_FINI ) + { + snprintf( err, es, "line %d: select a code or data section", line_no ); + goto out; + } + + mn = p; + while( *p && !isspace( (unsigned char)*p ) ) p++; + if( *p ) + { + *p++ = 0; + rest = trim( p ); + } + else rest = (char *)""; + if( streqi( mn, "LDI" ) ) + { + if( !*rest || emit_ldi( &code, &eqs, rest, err, es ) ) goto badline; + } + else + { + if( emit_instruction( &code, &dfix, &cfix, &pfix, &eqs, mn, rest, err, es ) ) goto badline; + } continue; +badline: + { + char t[256]; + snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); + snprintf( err, es, "%s", t ); + goto out; + } + } + + if( init_seen && !start_seen ) + { + snprintf( err, es, ".init section requires ABI entry label _start" ); + goto out; + } + if( fini_seen && !fini_entry_seen ) + { + snprintf( err, es, ".fini section requires ABI entry label _fini" ); + goto out; + } + if( !text_seen ) + { + snprintf( err, es, "program requires a .text section" ); + goto out; + } + if( !life_seen ) life_entry = first_text; + /* backward-compatible legacy .text image */ + + { + size_t i; + size_t sizes[8] = + { + data.n, 0, code.n, control.n, input.n, output.n, trace.n, 0 + }; + for( i = 0; i < dfix.n; i++ ) + { + __mpu_uint64_t a; + int er = eval_u64( dfix.p[i].expr, &eqs, &dlabels, &a, err, es ); + if( er < 0 ) + { + char cause[256]; + snprintf( cause, sizeof( cause ), "%s", err ); + snprintf( err, es, "DATA expression '%s': %s", dfix.p[i].expr, cause ); + goto out; + } + if( er > 0 ) + { + snprintf( err, es, "DATA expression out of unsigned 64-bit range: %s", dfix.p[i].expr ); + goto out; + } + __cpu_code_put_u64( code.p + dfix.p[i].pos, a ); + } + for( i = 0; i < cfix.n; i++ ) + { + __mpu_uint64_t a; + int er = eval_u64( cfix.p[i].expr, &eqs, &clabels, &a, err, es ); + if( er < 0 ) + { + char cause[256]; + snprintf( cause, sizeof( cause ), "%s", err ); + snprintf( err, es, "CODE expression '%s': %s", cfix.p[i].expr, cause ); + goto out; + } + if( er > 0 || a >= code.n ) + { + snprintf( err, es, "CODE expression does not address PROGRAM: %s", cfix.p[i].expr ); + goto out; + } + __cpu_code_put_u64( code.p + cfix.p[i].pos, a ); + } + for( i = 0; i < pfix.n; i++ ) + { + __mpu_uint64_t ptr; + if( resolve_ptr_expression( pfix.p[i].expr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, &ptr, err, es ) ) goto out; + __cpu_code_put_u64( code.p + pfix.p[i].pos, ptr ); + } + if( apply_cptr_fixups( &data, &dcptr, &eqs, &clabels, code.n, err, es ) || + apply_cptr_fixups( &control, &ccptr, &eqs, &clabels, code.n, err, es ) || + apply_cptr_fixups( &input, &icptr, &eqs, &clabels, code.n, err, es ) || + apply_cptr_fixups( &output, &ocptr, &eqs, &clabels, code.n, err, es ) || + apply_cptr_fixups( &trace, &tcptr, &eqs, &clabels, code.n, err, es ) || + apply_ptr_data_fixups( &data, &dptr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, err, es ) || + apply_ptr_data_fixups( &control, &ctptr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, err, es ) || + apply_ptr_data_fixups( &input, &iptr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, err, es ) || + apply_ptr_data_fixups( &output, &optr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, err, es ) || + apply_ptr_data_fixups( &trace, &tptr, &eqs, &clabels, &dlabels, &ctlabels, &ilabels, &olabels, &tlabels, sizes, err, es )) goto out; + } + + im->code = code.p; + im->code_size = code.n; + code.p = 0; + im->data = data.p; + im->data_size = data.n; + data.p = 0; + im->control = control.p; + im->control_size = control.n; + control.p = 0; + im->input = input.p; + im->input_size = input.n; + input.p = 0; + im->output = output.p; + im->output_size = output.n; + output.p = 0; + im->trace = trace.p; + im->trace_size = trace.n; + trace.p = 0; + im->has_init = init_seen; + im->init_entry = start_entry; + im->has_life = 1; + im->life_entry = life_entry; + im->has_fini = fini_seen; + im->fini_entry = fini_entry; + rc = 0; + +out: + free( scan ); + free( copy ); + free( code.p ); + free( data.p ); + free( control.p ); + free( input.p ); + free( output.p ); + free( trace.p ); + free( clabels.p ); + free( dlabels.p ); + free( ctlabels.p ); + free( ilabels.p ); + free( olabels.p ); + free( tlabels.p ); + free( dfix.p ); + free( cfix.p ); + free( pfix.p ); + free( dcptr.p ); + free( ccptr.p ); + free( icptr.p ); + free( ocptr.p ); + free( tcptr.p ); + free( dptr.p ); + free( ctptr.p ); + free( iptr.p ); + free( optr.p ); + free( tptr.p ); + free( eqs.p ); + return( rc ); +} + +void +cpu_asm_image_free( cpu_asm_image *im ) +{ + size_t i; + if( !im ) return; + free( im->code ); + free( im->data ); + free( im->control ); + free( im->input ); + free( im->output ); + free( im->trace ); + for( i = 0; i < im->dyn_symbol_count; i++ ) free( im->dyn_symbols[i].name ); + free( im->dyn_symbols ); + free( im->dyn_relocs ); + memset( im, 0, sizeof( *im ) ); +} + +/* ------------------------------------------------------------------------- + * Relocatable assembler path -- MELF logical sections. + * + * ET_REL keeps .init/.text/.fini distinct and stores explicit RELA-like + * semantic records in cpu_asm_object. cpu-object.c maps this logical object + * one-to-one onto MELF sections/symbols/RELA records. + * ------------------------------------------------------------------------- */ + +static int +obj_symbol_defined( const cpu_asm_object *o, const char *name, size_t *idx ) +{ + size_t i; + for( i = 0; i < o->symbol_count; i++ ) + if( !strcmp( o->symbols[i].name, name ) && o->symbols[i].section != CPU_OBJ_SEC_UNDEF ) + { + if( idx ) * idx = i; + return( 1 ); + } + return( 0 ); +} + +static int +obj_find_symbol( const cpu_asm_object *o, const char *name, size_t *idx ) +{ + size_t i; + for( i = 0; i < o->symbol_count; i++ ) if( !strcmp( o->symbols[i].name, name ) ) + { + if( idx ) * idx = i; + return( 0 ); + } + return( -1 ); +} + +static int obj_add_symbol( cpu_asm_object *o, const char *name, unsigned section, unsigned binding, unsigned type, + __mpu_uint64_t value, char *err, size_t es) +{ + cpu_asm_symbol *q; + size_t i; + if( !obj_find_symbol( o, name, &i ) ) + { + cpu_asm_symbol *x = &o->symbols[i]; + if( x->section != CPU_OBJ_SEC_UNDEF && section != CPU_OBJ_SEC_UNDEF ) + { + snprintf( err, es, "duplicate object symbol: %s", name ); + return( -1 ); + } + if( x->section == CPU_OBJ_SEC_UNDEF && section != CPU_OBJ_SEC_UNDEF ) + { + x->section = section; + x->value = value; + x->type = type; + } + if( binding == CPU_OBJ_BIND_WEAK ) x->binding = CPU_OBJ_BIND_WEAK; + else if( binding == CPU_OBJ_BIND_GLOBAL && x->binding != CPU_OBJ_BIND_WEAK ) x->binding = CPU_OBJ_BIND_GLOBAL; + return( 0 ); + } + q = (cpu_asm_symbol *)realloc( o->symbols, ( o->symbol_count + 1 ) * sizeof( *q ) ); + if( !q ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + o->symbols = q; + memset( &o->symbols[o->symbol_count], 0, sizeof( o->symbols[o->symbol_count] ) ); + o->symbols[o->symbol_count].name = strdup( name ); + if( !o->symbols[o->symbol_count].name ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + o->symbols[o->symbol_count].section = section; + o->symbols[o->symbol_count].binding = binding; + o->symbols[o->symbol_count].type = type; + o->symbols[o->symbol_count].value = value; + o->symbol_count++; + return( 0 ); +} + +static int obj_add_reloc( cpu_asm_object *o, unsigned section, unsigned type, size_t sym, + __mpu_uint64_t off, __mpu_int64_t addend, char *err, size_t es) +{ + cpu_asm_reloc *q = (cpu_asm_reloc *)realloc( o->relocs, ( o->reloc_count + 1 ) * sizeof( *q ) ); + if( !q ) + { + snprintf( err, es, "out of memory" ); + return( -1 ); + } + o->relocs = q; + o->relocs[o->reloc_count].section = section; + o->relocs[o->reloc_count].type = type; + o->relocs[o->reloc_count].symbol_index = sym; + o->relocs[o->reloc_count].offset = off; + o->relocs[o->reloc_count].addend = addend; + o->reloc_count++; + return( 0 ); +} + +static int obj_label_lookup( const char *name, + const labels_t *init, const labels_t *text, const labels_t *fini, + const labels_t *data, const labels_t *bss, const labels_t *control, const labels_t *input, + const labels_t *output, const labels_t *trace, + unsigned *section, __mpu_uint64_t *value) +{ + if( init && !findlabel( init, name, value ) ) + { + *section = CPU_OBJ_SEC_INIT; + return( 0 ); + } + if( text && !findlabel( text, name, value ) ) + { + *section = CPU_OBJ_SEC_TEXT; + return( 0 ); + } + if( fini && !findlabel( fini, name, value ) ) + { + *section = CPU_OBJ_SEC_FINI; + return( 0 ); + } + if( data && !findlabel( data, name, value ) ) + { + *section = CPU_OBJ_SEC_DATA; + return( 0 ); + } + if( bss && !findlabel( bss, name, value ) ) + { + *section = CPU_OBJ_SEC_BSS; + return( 0 ); + } + if( control && !findlabel( control, name, value ) ) + { + *section = CPU_OBJ_SEC_CONTROL; + return( 0 ); + } + if( input && !findlabel( input, name, value ) ) + { + *section = CPU_OBJ_SEC_INPUT; + return( 0 ); + } + if( output && !findlabel( output, name, value ) ) + { + *section = CPU_OBJ_SEC_OUTPUT; + return( 0 ); + } + if( trace && !findlabel( trace, name, value ) ) + { + *section = CPU_OBJ_SEC_TRACE; + return( 0 ); + } + return( -1 ); +} + +static int obj_name_defined_any( const char *name, + const labels_t *init, const labels_t *text, const labels_t *fini, + const labels_t *data, const labels_t *bss, const labels_t *control, const labels_t *input, + const labels_t *output, const labels_t *trace) +{ + unsigned s; + __mpu_uint64_t v; + return( !obj_label_lookup( name, init, text, fini, data, bss, control, input, output, trace, &s, &v ) ); +} + +static int reloc_expression_melf( const char *expr, equs_t *eqs, const symdecls_t *decls, + const labels_t *init, const labels_t *text, const labels_t *fini, + const labels_t *data, const labels_t *bss, const labels_t *control, const labels_t *input, + const labels_t *output, const labels_t *trace, + char sym[ASM_NAME_MAX], int *has_sym, __mpu_int64_t *addend, + __mpu_uint64_t *absolute, char *err, size_t es) +{ + const char *p = expr; + char found[ASM_NAME_MAX] = ""; + int seen = 0; + while( *p ) + { + if( isdigit( (unsigned char)*p ) ) + { + char *end = 0; + errno = 0; + (void)strtoull( p, &end, 0 ); + if( end && end > p ) + { + p = end; + continue; + } + } + if( symfirst( (unsigned char)*p ) ) + { + char n[ASM_NAME_MAX]; + size_t k = 0, ei, di; + unsigned sec; + __mpu_uint64_t val; + while( symrest( (unsigned char)*p ) ) + { + if( k + 1 >= sizeof( n ) ) + { + snprintf( err, es, "symbol name too long" ); + return( -1 ); + } + n[k++] = *p++; + } + n[k] = 0; + if( !findequ( eqs, n, &ei ) ) continue; + if( !obj_label_lookup( n, init, text, fini, data, bss, control, input, output, trace, &sec, &val ) || !finddecl( decls, n, &di ) ) + { + if( !seen ) + { + strcpy( found, n ); + seen = 1; + } + else + if( strcmp( found, n ) ) + { + snprintf( err, es, "relocatable expression contains more than one symbol: %s", expr ); + return( -1 ); + } continue; + } + snprintf( err, es, "unknown symbol in relocatable expression: %s", n ); + return( -1 ); + } + p++; + } + if( !seen ) + { + __int128 v; + if( eval_expression( expr, eqs, 0, &v, err, es ) ) return( -1 ); + if( v < 0 || v > ( __int128 ) UINT64_MAX ) + { + snprintf( err, es, "absolute expression out of unsigned 64-bit range: %s", expr ); + return( -1 ); + } + *has_sym = 0; + *absolute = (__mpu_uint64_t)v; + return( 0 ); + } + else + { + labels_t fake = + { + 0 + }; + label_t one; + __int128 v0, v1, d; + memset( &one, 0, sizeof( one ) ); + strcpy( one.name, found ); + fake.p = &one; + fake.n = fake.cap = 1; + one.addr = 0; + if( eval_expression( expr, eqs, &fake, &v0, err, es ) ) return( -1 ); + one.addr = 1; + if( eval_expression( expr, eqs, &fake, &v1, err, es ) ) return( -1 ); + d = v1 - v0; + if( d == 0 ) + { + if( v0 < 0 || v0 > ( __int128 ) UINT64_MAX ) + { + snprintf( err, es, "absolute expression out of unsigned 64-bit range: %s", expr ); + return( -1 ); + } + *has_sym = 0; + *absolute = (__mpu_uint64_t)v0; + return( 0 ); + } + if( d != 1 ) + { + snprintf( err, es, "relocatable expression must be SYMBOL + constant: %s", expr ); + return( -1 ); + } + if( v0 < ( __int128 ) INT64_MIN || v0 > ( __int128 ) INT64_MAX ) + { + snprintf( err, es, "relocation addend out of signed 64-bit range: %s", expr ); + return( -1 ); + } + strcpy( sym, found ); + *has_sym = 1; + *addend = (__mpu_int64_t)v0; + return( 0 ); + } +} + +static int process_obj_fixups_melf( cpu_asm_object *o, buf_t *src, unsigned srcsec, const fixups_t *fx, + unsigned type, equs_t *eqs, const symdecls_t *decls, + const labels_t *init, const labels_t *text, const labels_t *fini, + const labels_t *data, const labels_t *bss, const labels_t *control, const labels_t *input, + const labels_t *output, const labels_t *trace, char *err, size_t es) +{ + size_t i; + for( i = 0; i < fx->n; i++ ) + { + char name[ASM_NAME_MAX]; + int hs = 0; + __mpu_int64_t add = 0; + __mpu_uint64_t abs = 0, ptr; + size_t si; + if( reloc_expression_melf( fx->p[i].expr, eqs, decls, init, text, fini, data, bss, control, input, output, trace, name, &hs, &add, &abs, err, es ) ) return( -1 ); + if( hs ) + { + if( obj_find_symbol( o, name, &si ) ) + { + snprintf( err, es, "internal assembler symbol lookup failed: %s", name ); + return( -1 ); + } + if( o->symbols[si].section != CPU_OBJ_SEC_UNDEF ) + { + unsigned ts = o->symbols[si].section; + if( type == CPU_RELOC_CODE64 && !cpu_obj_sec_is_program( ts ) ) + { + snprintf( err, es, "CODE64 relocation target is not PROGRAM: %s", name ); + return( -1 ); + } + if( type == CPU_RELOC_DATA64 && ts != CPU_OBJ_SEC_DATA && ts != CPU_OBJ_SEC_BSS ) + { + snprintf( err, es, "ADDR64 relocation target is not DATA: %s", name ); + return( -1 ); + } + if( type == CPU_RELOC_CPTR64_DATA && !cpu_obj_sec_is_program( ts ) ) + { + snprintf( err, es, ".cptr relocation target is not PROGRAM: %s", name ); + return( -1 ); + } + } + if( obj_add_reloc( o, srcsec, type, si, fx->p[i].pos, add, err, es ) ) return( -1 ); + continue; + } + switch( type ) + { + case CPU_RELOC_CODE64: case CPU_RELOC_DATA64: __cpu_code_put_u64( src->p + fx->p[i].pos, abs ); + break; + case CPU_RELOC_PTR64_CODE: + if( __cpu_ptr_make( &ptr, CPU_PTR_SEG_DATA, abs ) ) + { + snprintf( err, es, "absolute LEA pointer cannot be encoded: %s", fx->p[i].expr ); + return( -1 ); + } + __cpu_code_put_u64( src->p + fx->p[i].pos, ptr ); + break; + case CPU_RELOC_PTR64_DATA: + if( __cpu_ptr_make( &ptr, CPU_PTR_SEG_DATA, abs ) ) + { + snprintf( err, es, "absolute .ptr cannot be encoded: %s", fx->p[i].expr ); + return( -1 ); + } + __cpu_data_put_u64( src->p + fx->p[i].pos, ptr ); + break; + case CPU_RELOC_CPTR64_DATA: + if( __cpu_ptr_make( &ptr, CPU_PTR_SEG_PROGRAM, abs ) ) + { + snprintf( err, es, "absolute .cptr cannot be encoded: %s", fx->p[i].expr ); + return( -1 ); + } + __cpu_data_put_u64( src->p + fx->p[i].pos, ptr ); + break; + default: snprintf( err, es, "internal unknown relocation type" ); + return( -1 ); + } + } + return( 0 ); +} + +static unsigned +declared_binding( const symdecls_t *d, const char *name ) +{ + size_t i; + if( !strcmp( name, "_start" ) || !strcmp( name, "_life" ) || !strcmp( name, "_fini" ) ) return( CPU_OBJ_BIND_GLOBAL ); + if( !finddecl( d, name, &i ) ) + { + if( d->p[i].is_weak ) return( CPU_OBJ_BIND_WEAK ); + if( d->p[i].is_global ) return( CPU_OBJ_BIND_GLOBAL ); + } + return( CPU_OBJ_BIND_LOCAL ); +} + +static int build_obj_symbols_melf( cpu_asm_object *o, const symdecls_t *decls, + const labels_t *init, const labels_t *text, const labels_t *fini, + const labels_t *data, const labels_t *bss, const labels_t *control, const labels_t *input, + const labels_t *output, const labels_t *trace, char *err, size_t es) +{ + const labels_t *ls[9] = + { + init, text, fini, data, bss, control, input, output, trace + }; + const unsigned sec[9] = + { + CPU_OBJ_SEC_INIT, CPU_OBJ_SEC_TEXT, CPU_OBJ_SEC_FINI, CPU_OBJ_SEC_DATA, CPU_OBJ_SEC_BSS, CPU_OBJ_SEC_CONTROL, CPU_OBJ_SEC_INPUT, CPU_OBJ_SEC_OUTPUT, CPU_OBJ_SEC_TRACE + }; + size_t j, i; + for( j = 0; j < 9; j++ ) for( i = 0; i < ls[j]->n; i++ ) + { + unsigned type = cpu_obj_sec_is_program( sec[j] ) ? CPU_OBJ_TYPE_FUNC : CPU_OBJ_TYPE_OBJECT; + if( obj_add_symbol( o, ls[j]->p[i].name, sec[j], declared_binding( decls, ls[j]->p[i].name ), type, ls[j]->p[i].addr, err, es ) ) return( -1 ); + } + for( i = 0; i < decls->n; i++ ) if( !obj_symbol_defined( o, decls->p[i].name, 0 ) ) + { + const char *name = decls->p[i].name; + unsigned b = decls->p[i].is_weak ? CPU_OBJ_BIND_WEAK : CPU_OBJ_BIND_GLOBAL; + unsigned type = ( !strcmp( name, "_start" ) || !strcmp( name, "_life" ) || !strcmp( name, "_fini" ) ) + ?CPU_OBJ_TYPE_FUNC:CPU_OBJ_TYPE_NOTYPE; + if( obj_add_symbol( o, name, CPU_OBJ_SEC_UNDEF, b, type, 0, err, es ) ) return( -1 ); + } + return( 0 ); +} + +int +cpu_assemble_object( const char *src, cpu_asm_object *o, char *err, size_t es ) +{ + char *copy = 0, *scan = 0, *save = 0, *line; + buf_t init = + { + 0 + } + , text = + { + 0 + } + , fini = + { + 0 + } + , data = + { + 0 + } + , control = + { + 0 + } + , input = + { + 0 + } + , output = + { + 0 + } + , trace = + { + 0 + }; + size_t bss_size = 0; + labels_t ilabs = + { + 0 + } + , tlabs = + { + 0 + } + , flabs = + { + 0 + } + , dlabs = + { + 0 + } + , blabs = + { + 0 + } + , ctlabs = + { + 0 + } + , inlabs = + { + 0 + } + , olabs = + { + 0 + } + , trlabs = + { + 0 + }; + fixups_t idfix = + { + 0 + } + , icfix = + { + 0 + } + , ipfix = + { + 0 + } + , tdfix = + { + 0 + } + , tcfix = + { + 0 + } + , tpfix = + { + 0 + } + , fdfix = + { + 0 + } + , fcfix = + { + 0 + } + , fpfix = + { + 0 + }; + fixups_t dcptr = + { + 0 + } + , ccptr = + { + 0 + } + , incptr = + { + 0 + } + , ocptr = + { + 0 + } + , trcptr = + { + 0 + }; + fixups_t dptr = + { + 0 + } + , ctptr = + { + 0 + } + , inptr = + { + 0 + } + , optr = + { + 0 + } + , trptr = + { + 0 + }; + __mpu_uint64_t dalign = 1, balign = 1, ctalign = 1, inalign = 1, oalign = 1, tralign = 1; + equs_t eqs = + { + 0 + }; + symdecls_t decls = + { + 0 + }; + int section = ASM_SEC_NONE, line_no = 0, rc = -1; + int init_seen = 0, text_seen = 0, fini_seen = 0, bss_seen = 0, pseudo_seen = 0; + + if( !src || !o ) return( -1 ); + memset( o, 0, sizeof( *o ) ); + if( !mpu_utf8valid( (const __mpu_char8_t *)src ) ) + { + snprintf( err, es, "assembler source is not valid UTF-8" ); + return( -1 ); + } + + scan = strdup( src ); + if( !scan ) return( -1 ); + for( line = strtok_r( scan, "\n", &save ); line; line = strtok_r( NULL, "\n", &save ) ) + { + int er; + line_no++; + strip_comment( line ); + if( !*trim( line ) ) continue; + er = collect_equ_line( line, &eqs, line_no, err, es ); + if( er < 0 ) goto out; + } + free( scan ); + scan = 0; + save = 0; + { + size_t i; + __int128 v; + for( i = 0; i < eqs.n; i++ ) if( equ_value( &eqs, i, &v, err, es ) ) + { + char t[256]; + snprintf( t, sizeof( t ), "line %d: %s", eqs.p[i].line, err ); + snprintf( err, es, "%s", t ); + goto out; + } + } + + scan = strdup( src ); + if( !scan ) goto out; + line_no = 0; + for( line = strtok_r( scan, "\n", &save ); line; line = strtok_r( NULL, "\n", &save ) ) + { + int er; + line_no++; + strip_comment( line ); + if( !*trim( line ) ) continue; + er = collect_decl_line( line, &decls, line_no, err, es ); + if( er < 0 ) goto out; + } + free( scan ); + scan = 0; + save = 0; + { + size_t i, j; + for( i = 0; i < eqs.n; i++ ) for( j = 0; j < decls.n; j++ ) if( !strcmp( eqs.p[i].name, decls.p[j].name ) ) + { + snprintf( err, es, "symbol declared global/extern/weak conflicts with .equ: %s", eqs.p[i].name ); + goto out; + } + } + + copy = strdup( src ); + if( !copy ) goto out; + line_no = 0; + for( line = strtok_r( copy, "\n", &save ); line; line = strtok_r( NULL, "\n", &save ) ) + { + char *p, *colon, *mn, *rest, *q; + buf_t *cb = 0, *db = 0; + labels_t *labs = 0; + fixups_t *df = 0, *cf = 0, *pf = 0, *cpfx = 0, *gptr = 0; + __mpu_uint64_t *salign = 0; + int in_bss = 0; + line_no++; + strip_comment( line ); + p = trim( line ); + if( !*p ) continue; + q = p; + while( *q && !isspace( (unsigned char)*q ) ) q++; + if( ( (size_t)( q - p ) == 4 && !strncasecmp( p, ".equ", 4 ) ) || ( (size_t)( q - p ) == 7 && !strncasecmp( p, ".global", 7 ) ) || ( (size_t)( q - p ) == 6 && !strncasecmp( p, ".globl", 6 ) ) || ( (size_t)( q - p ) == 7 && !strncasecmp( p, ".extern", 7 ) ) || ( (size_t)( q - p ) == 5 && !strncasecmp( p, ".weak", 5 ) ) ) continue; + + if( streqi( p, ".init" ) ) + { + init_seen = 1; + section = ASM_SEC_INIT; + continue; + } + if( streqi( p, ".text" ) ) + { + text_seen = 1; + section = ASM_SEC_TEXT; + continue; + } + if( streqi( p, ".fini" ) ) + { + fini_seen = 1; + section = ASM_SEC_FINI; + continue; + } + if( streqi( p, ".data" ) ) + { + section = ASM_SEC_DATA; + continue; + } + if( streqi( p, ".bss" ) ) + { + bss_seen = 1; + section = ASM_SEC_BSS; + continue; + } + if( streqi( p, ".control" ) ) + { + section = ASM_SEC_CONTROL; + continue; + } + if( streqi( p, ".input" ) ) + { + section = ASM_SEC_INPUT; + continue; + } + if( streqi( p, ".output" ) ) + { + section = ASM_SEC_OUTPUT; + continue; + } + if( streqi( p, ".trace" ) ) + { + section = ASM_SEC_TRACE; + continue; + } + + switch( section ) + { + case ASM_SEC_INIT: cb = &init; + labs = &ilabs; + df = &idfix; + cf = &icfix; + pf = &ipfix; + break; + case ASM_SEC_TEXT: cb = &text; + labs = &tlabs; + df = &tdfix; + cf = &tcfix; + pf = &tpfix; + break; + case ASM_SEC_FINI: cb = &fini; + labs = &flabs; + df = &fdfix; + cf = &fcfix; + pf = &fpfix; + break; + case ASM_SEC_DATA: db = &data; + labs = &dlabs; + cpfx = &dcptr; + gptr = &dptr; + salign = &dalign; + break; + case ASM_SEC_BSS: labs = &blabs; + salign = &balign; + in_bss = 1; + break; + case ASM_SEC_CONTROL: db = &control; + labs = &ctlabs; + cpfx = &ccptr; + gptr = &ctptr; + salign = &ctalign; + break; + case ASM_SEC_INPUT: db = &input; + labs = &inlabs; + cpfx = &incptr; + gptr = &inptr; + salign = &inalign; + break; + case ASM_SEC_OUTPUT: db = &output; + labs = &olabs; + cpfx = &ocptr; + gptr = &optr; + salign = &oalign; + break; + case ASM_SEC_TRACE: db = &trace; + labs = &trlabs; + cpfx = &trcptr; + gptr = &trptr; + salign = &tralign; + break; + default:break; + } + + colon = label_colon( p ); + if( colon ) + { + size_t ei; + char *name; + __mpu_uint64_t here; + *colon = 0; + name = trim( p ); + if( !valid_symbol_name( name ) ) + { + snprintf( err, es, "line %d: bad label name", line_no ); + goto out; + } + if( !findequ( &eqs, name, &ei ) ) + { + snprintf( err, es, "line %d: label conflicts with .equ symbol: %s", line_no, name ); + goto out; + } + if( !strcmp( name, "_start_life" ) ) + { + if( section != ASM_SEC_TEXT ) + { + snprintf( err, es, "line %d: _start_life is valid only in .text", line_no ); + goto out; + } + if( pseudo_seen || obj_name_defined_any( "_start", &ilabs, &tlabs, &flabs, &dlabs, &blabs, &ctlabs, &inlabs, &olabs, &trlabs ) || obj_name_defined_any( "_life", &ilabs, &tlabs, &flabs, &dlabs, &blabs, &ctlabs, &inlabs, &olabs, &trlabs ) ) + { + snprintf( err, es, "line %d: _start_life conflicts with an existing _start/_life definition", line_no ); + goto out; + } + here = (__mpu_uint64_t)text.n; + if( addlabel( &tlabs, "_start", here ) || addlabel( &tlabs, "_life", here ) ) + { + snprintf( err, es, "line %d: cannot define _start_life", line_no ); + goto out; + } + pseudo_seen = 1; + } + else + { + if( obj_name_defined_any( name, &ilabs, &tlabs, &flabs, &dlabs, &blabs, &ctlabs, &inlabs, &olabs, &trlabs ) ) + { + snprintf( err, es, "line %d: duplicate label: %s", line_no, name ); + goto out; + } + if( !labs ) + { + snprintf( err, es, "line %d: label outside a valid section", line_no ); + goto out; + } + here = cb ? (__mpu_uint64_t)cb->n : in_bss ? (__mpu_uint64_t)bss_size : (__mpu_uint64_t)db->n; + if( addlabel( labs, name, here ) ) + { + snprintf( err, es, "line %d: bad/duplicate label", line_no ); + goto out; + } + if( !strcmp( name, "_start" ) && section != ASM_SEC_INIT && section != ASM_SEC_TEXT ) + { + snprintf( err, es, "line %d: _start must be in .init or .text", line_no ); + goto out; + } + if( !strcmp( name, "_life" ) && section != ASM_SEC_TEXT ) + { + snprintf( err, es, "line %d: _life must be in .text", line_no ); + goto out; + } + if( !strcmp( name, "_fini" ) && section != ASM_SEC_FINI ) + { + snprintf( err, es, "line %d: _fini must be in .fini", line_no ); + goto out; + } + } + p = trim( colon + 1 ); + if( !*p ) continue; + } + + if( in_bss ) + { + if( bssdir( &bss_size, p, salign, &eqs, err, es ) ) + { + char t[256]; + snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); + snprintf( err, es, "%s", t ); + goto out; + } continue; + } + if( db ) + { + if( datadir( db, p, cpfx, gptr, salign, &eqs, err, es ) ) + { + char t[256]; + snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); + snprintf( err, es, "%s", t ); + goto out; + } continue; + } + if( !cb ) + { + snprintf( err, es, "line %d: select a code or data section", line_no ); + goto out; + } + mn = p; + while( *p && !isspace( (unsigned char)*p ) ) p++; + if( *p ) + { + *p++ = 0; + rest = trim( p ); + } + else rest = (char *)""; + if( streqi( mn, "LDI" ) ) + { + if( !*rest || emit_ldi( cb, &eqs, rest, err, es ) ) goto badline_obj; + } + else if( emit_instruction( cb, df, cf, pf, &eqs, mn, rest, err, es ) ) goto badline_obj; + continue; +badline_obj: +{ + char t[256]; + snprintf( t, sizeof( t ), "line %d: %s", line_no, err ); + snprintf( err, es, "%s", t ); + goto out; +} + } + + if( init_seen && obj_name_defined_any( "_start", 0, &tlabs, 0, 0, 0, 0, 0, 0, 0 ) ) + { + snprintf( err, es, "_start must be in .init when .init exists" ); + goto out; + } + if( init_seen && pseudo_seen ) + { + snprintf( err, es, "_start_life is invalid when .init exists" ); + goto out; + } + + if( build_obj_symbols_melf( o, &decls, &ilabs, &tlabs, &flabs, &dlabs, &blabs, &ctlabs, &inlabs, &olabs, &trlabs, err, es ) ) goto out; +#define PF(SRC,SEC,FX,TYPE) process_obj_fixups_melf(o,&(SRC),(SEC),&(FX),(TYPE),&eqs,&decls,&ilabs,&tlabs,&flabs,&dlabs,&blabs,&ctlabs,&inlabs,&olabs,&trlabs,err,es) + if( PF( init, CPU_OBJ_SEC_INIT, idfix, CPU_RELOC_DATA64 ) || PF( init, CPU_OBJ_SEC_INIT, icfix, CPU_RELOC_CODE64 ) || PF( init, CPU_OBJ_SEC_INIT, ipfix, CPU_RELOC_PTR64_CODE ) || + PF( text, CPU_OBJ_SEC_TEXT, tdfix, CPU_RELOC_DATA64 ) || PF( text, CPU_OBJ_SEC_TEXT, tcfix, CPU_RELOC_CODE64 ) || PF( text, CPU_OBJ_SEC_TEXT, tpfix, CPU_RELOC_PTR64_CODE ) || + PF( fini, CPU_OBJ_SEC_FINI, fdfix, CPU_RELOC_DATA64 ) || PF( fini, CPU_OBJ_SEC_FINI, fcfix, CPU_RELOC_CODE64 ) || PF( fini, CPU_OBJ_SEC_FINI, fpfix, CPU_RELOC_PTR64_CODE ) || + PF( data, CPU_OBJ_SEC_DATA, dcptr, CPU_RELOC_CPTR64_DATA ) || PF( control, CPU_OBJ_SEC_CONTROL, ccptr, CPU_RELOC_CPTR64_DATA ) || PF( input, CPU_OBJ_SEC_INPUT, incptr, CPU_RELOC_CPTR64_DATA ) || PF( output, CPU_OBJ_SEC_OUTPUT, ocptr, CPU_RELOC_CPTR64_DATA ) || PF( trace, CPU_OBJ_SEC_TRACE, trcptr, CPU_RELOC_CPTR64_DATA ) || + PF( data, CPU_OBJ_SEC_DATA, dptr, CPU_RELOC_PTR64_DATA ) || PF( control, CPU_OBJ_SEC_CONTROL, ctptr, CPU_RELOC_PTR64_DATA ) || PF( input, CPU_OBJ_SEC_INPUT, inptr, CPU_RELOC_PTR64_DATA ) || PF( output, CPU_OBJ_SEC_OUTPUT, optr, CPU_RELOC_PTR64_DATA ) || PF( trace, CPU_OBJ_SEC_TRACE, trptr, CPU_RELOC_PTR64_DATA )) goto out; +#undef PF + + o->init = init.p; + o->init_size = init.n; + o->init_align = 1; + o->has_init = init_seen; + init.p = 0; + o->text = text.p; + o->text_size = text.n; + o->text_align = 1; + o->has_text = text_seen; + text.p = 0; + o->fini = fini.p; + o->fini_size = fini.n; + o->fini_align = 1; + o->has_fini = fini_seen; + fini.p = 0; + o->data = data.p; + o->data_size = data.n; + o->data_align = dalign; + o->has_data = ( data.n != 0 ); + data.p = 0; + o->bss_size = bss_size; + o->bss_align = balign; + o->has_bss = bss_seen; + o->control = control.p; + o->control_size = control.n; + o->control_align = ctalign; + o->has_control = ( control.n != 0 ); + control.p = 0; + o->input = input.p; + o->input_size = input.n; + o->input_align = inalign; + o->has_input = ( input.n != 0 ); + input.p = 0; + o->output = output.p; + o->output_size = output.n; + o->output_align = oalign; + o->has_output = ( output.n != 0 ); + output.p = 0; + o->trace = trace.p; + o->trace_size = trace.n; + o->trace_align = tralign; + o->has_trace = ( trace.n != 0 ); + trace.p = 0; + o->code_size = o->init_size + o->text_size + o->fini_size; + o->code_align = 1; + if( o->code_size ) + { + o->code = (__mpu_UNIT_t *)malloc( o->code_size ); + if( !o->code ) + { + snprintf( err, es, "out of memory" ); + goto out; + } + memcpy( o->code, o->init, o->init_size ); + memcpy( o->code + o->init_size, o->text, o->text_size ); + memcpy( o->code + o->init_size + o->text_size, o->fini, o->fini_size ); + } + rc = 0; +out: + free( scan ); + free( copy ); + free( init.p ); + free( text.p ); + free( fini.p ); + free( data.p ); + free( control.p ); + free( input.p ); + free( output.p ); + free( trace.p ); + free( ilabs.p ); + free( tlabs.p ); + free( flabs.p ); + free( dlabs.p ); + free( blabs.p ); + free( ctlabs.p ); + free( inlabs.p ); + free( olabs.p ); + free( trlabs.p ); + free( idfix.p ); + free( icfix.p ); + free( ipfix.p ); + free( tdfix.p ); + free( tcfix.p ); + free( tpfix.p ); + free( fdfix.p ); + free( fcfix.p ); + free( fpfix.p ); + free( dcptr.p ); + free( ccptr.p ); + free( incptr.p ); + free( ocptr.p ); + free( trcptr.p ); + free( dptr.p ); + free( ctptr.p ); + free( inptr.p ); + free( optr.p ); + free( trptr.p ); + free( eqs.p ); + free( decls.p ); + if( rc ) cpu_asm_object_free( o ); + return( rc ); +} + +void +cpu_asm_object_free( cpu_asm_object *o ) +{ + size_t i; + if( !o ) return; + free( o->init ); + free( o->text ); + free( o->fini ); + free( o->data ); + free( o->control ); + free( o->input ); + free( o->output ); + free( o->trace ); + free( o->code ); + for( i = 0; i < o->symbol_count; i++ ) free( o->symbols[i].name ); + free( o->symbols ); + free( o->relocs ); + memset( o, 0, sizeof( *o ) ); +} |
