366 lines
9.1 KiB
C
366 lines
9.1 KiB
C
// SPDX-FileCopyrightText: 2012 Karl Hobley <turbodog10@yahoo.co.uk>
|
|
// SPDX-FileCopyrightText: 2012 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
/* this code has been modified to be integrated into r2 */
|
|
/* pancake // nopcode.org */
|
|
|
|
/* dcpu16asm.c - Assembler for the DCPU-16 CPU
|
|
version 4.1, 5 April 2012
|
|
|
|
Copyright (C) 2012 Karl Hobley
|
|
|
|
Permission is hereby granted, free of charge, to any person
|
|
obtaining a copy of this software and associated documentation
|
|
files (the "Software"), to deal in the Software without
|
|
restriction, including without limitation the rights to use, copy,
|
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Karl Hobley <turbodog10@yahoo.co.uk>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include "dcpu16.h"
|
|
|
|
#define NOTEND 1 /* endian hack */
|
|
|
|
static ut8 get_register_id(char reg) {
|
|
const char *regs = "ABCXYZIJ";
|
|
const char *p = strchr(regs, reg);
|
|
return p ? (int)(size_t)(p - regs) : 0;
|
|
}
|
|
|
|
static void clean_line(char *oline, const char *line) {
|
|
int cn = 0, n = 0;
|
|
|
|
while (line[cn] != 0 && line[cn] != '\n' && line[cn] != ';') {
|
|
if (line[cn] >= '!' && line[cn] <= '~') {
|
|
char current_char = line[cn];
|
|
|
|
/* Convert to upper case */
|
|
if (current_char >= 'a' && current_char <= 'z')
|
|
current_char = toupper((unsigned char)current_char);
|
|
|
|
/* Place in cleaned line */
|
|
oline[n] = current_char;
|
|
n++;
|
|
}
|
|
cn++;
|
|
}
|
|
oline[n] = 0;
|
|
}
|
|
|
|
/*
|
|
* Convert a parameter string. eg, "A", "0x1234", "[0x1234]" into a 6 bit value
|
|
*/
|
|
static ut8 decode_parameter(char *param, int *extra_word_needed, ut16 *extra_word_value) {
|
|
/* Check for square brackets */
|
|
int square_brackets = 0;
|
|
int first_sqbracket = 0, last_sqbracket = 0;
|
|
if (param[0] == '[') {
|
|
first_sqbracket = 1;
|
|
param++;
|
|
}
|
|
if (param[strlen(param) - 1] == ']') {
|
|
last_sqbracket = 1;
|
|
param[strlen(param) - 1] = 0;
|
|
}
|
|
|
|
/* Check for errors */
|
|
if (first_sqbracket == 1) {
|
|
square_brackets = 1;
|
|
if (last_sqbracket != 1) {
|
|
fprintf(stderr, "Missing last square bracket\n");
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (last_sqbracket == 1) {
|
|
fprintf(stderr, "Missing first square bracket\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Check if this is a hex literal */
|
|
if (param[0] == '0' && param[1] == 'X') {
|
|
/* Decode hex */
|
|
ut16 value = 0;
|
|
param = param + 2; /* Remove 0x */
|
|
int digit_count = strlen(param);
|
|
int digit_num = 0;
|
|
int reg = -1;
|
|
for (digit_num = 0; digit_num < digit_count; digit_num++) {
|
|
/* Get Digit value */
|
|
int digit_val = -1;
|
|
char current_digit = param[digit_num];
|
|
if (current_digit >= '0' && current_digit <= '9')
|
|
digit_val = current_digit - '0';
|
|
if (current_digit >= 'A' && current_digit <= 'F')
|
|
digit_val = current_digit - 'A' + 10;
|
|
|
|
if (current_digit == '+' && square_brackets == 1) {
|
|
reg = get_register_id(param[digit_num + 1]);
|
|
digit_num++;
|
|
} else {
|
|
/* Check for errors */
|
|
if (digit_val == -1) {
|
|
fprintf(stderr, "invalid literal\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Merge into final number */
|
|
value = (value << 4) + digit_val;
|
|
}
|
|
}
|
|
|
|
if (value <= 0x1f && square_brackets == 0)
|
|
return value + 0x20;
|
|
|
|
*extra_word_needed = 1;
|
|
*extra_word_value = value;
|
|
|
|
if (square_brackets == 1) {
|
|
if (reg != -1)
|
|
return 0x10 + reg;
|
|
return 0x1e;
|
|
}
|
|
return 0x1f;
|
|
}
|
|
|
|
/* Check if this is a decimal literal */
|
|
if (param[0] >= '0' && param[0] <= '9') {
|
|
/* Decode decimal */
|
|
ut16 value = 0;
|
|
int digit_count = strlen(param);
|
|
int digit_num = 0;
|
|
int reg = -1;
|
|
for (digit_num = 0; digit_num < digit_count; digit_num++) {
|
|
/* Get Digit value */
|
|
int digit_val = -1;
|
|
char current_digit = param[digit_num];
|
|
if (current_digit >= '0' && current_digit <= '9')
|
|
digit_val = current_digit - '0';
|
|
|
|
if (current_digit == '+' && square_brackets == 1) {
|
|
reg = get_register_id(param[digit_num + 1]);
|
|
digit_num++;
|
|
} else {
|
|
/* Check for errors */
|
|
if (digit_val == -1) {
|
|
fprintf(stderr, "invalid literal\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Merge into final number */
|
|
value = (value * 10) + digit_val;
|
|
}
|
|
}
|
|
if (value <= 0x1f && square_brackets == 0)
|
|
return value + 0x20;
|
|
|
|
*extra_word_needed = 1;
|
|
*extra_word_value = value;
|
|
|
|
if (square_brackets == 1) {
|
|
if (reg != -1)
|
|
return 0x10 + reg;
|
|
return 0x1e;
|
|
}
|
|
return 0x1f;
|
|
}
|
|
|
|
/* Check if this is a register */
|
|
if (param[1] == 0) { /* This is a quick way to check that this is 1 character long */
|
|
ut8 reg = get_register_id(param[0]);
|
|
if (square_brackets == 1)
|
|
reg += 0x08;
|
|
return reg;
|
|
}
|
|
|
|
/* Check if this is a word */
|
|
if (!strncmp("POP", param, 3))
|
|
return 0x18;
|
|
if (!strncmp("PEEK", param, 4))
|
|
return 0x19;
|
|
if (!strncmp("PUSH", param, 4))
|
|
return 0x1a;
|
|
if (!strncmp("SP", param, 2))
|
|
return 0x1b;
|
|
if (!strncmp("PC", param, 2))
|
|
return 0x1c;
|
|
if (!strncmp("O", param, 1))
|
|
return 0x1d;
|
|
|
|
/* Must be a label, store a labelref */
|
|
*extra_word_needed = 1;
|
|
/* Allocate blank extra word, this will be where the
|
|
pointer to the label will be stored at link stage */
|
|
*extra_word_value = 0;
|
|
return 0x1f;
|
|
}
|
|
|
|
int dcpu16_assemble(ut8 *out, const char *unoline) {
|
|
ut16 wordA = 0, wordB = 0;
|
|
int basic_opcode = 0;
|
|
int non_basic_opcode = 0;
|
|
char line[256] = { 0 }, *param;
|
|
int off = 0;
|
|
// uberflow!
|
|
clean_line(line, unoline);
|
|
|
|
if (!(*line))
|
|
return 0;
|
|
if (strlen(line) < 4)
|
|
return 0;
|
|
param = line + 3; /* Cut off first 3 characters */
|
|
|
|
/* Basic instructions */
|
|
// cmon! use an array
|
|
if (!strncmp("SET", line, 3))
|
|
basic_opcode = 0x1;
|
|
else if (!strncmp("ADD", line, 3))
|
|
basic_opcode = 0x2;
|
|
else if (!strncmp("SUB", line, 3))
|
|
basic_opcode = 0x3;
|
|
else if (!strncmp("MUL", line, 3))
|
|
basic_opcode = 0x4;
|
|
else if (!strncmp("DIV", line, 3))
|
|
basic_opcode = 0x5;
|
|
else if (!strncmp("MOD", line, 3))
|
|
basic_opcode = 0x6;
|
|
else if (!strncmp("SHL", line, 3))
|
|
basic_opcode = 0x7;
|
|
else if (!strncmp("SHR", line, 3))
|
|
basic_opcode = 0x8;
|
|
else if (!strncmp("AND", line, 3))
|
|
basic_opcode = 0x9;
|
|
else if (!strncmp("BOR", line, 3))
|
|
basic_opcode = 0xA;
|
|
else if (!strncmp("XOR", line, 3))
|
|
basic_opcode = 0xB;
|
|
else if (!strncmp("IFE", line, 3))
|
|
basic_opcode = 0xC;
|
|
else if (!strncmp("IFN", line, 3))
|
|
basic_opcode = 0xD;
|
|
else if (!strncmp("IFG", line, 3))
|
|
basic_opcode = 0xE;
|
|
else if (!strncmp("IFB", line, 3))
|
|
basic_opcode = 0xF;
|
|
|
|
/* Non basic instructions */
|
|
if (basic_opcode == 0) {
|
|
if (!strncmp("JSR", line, 3)) {
|
|
non_basic_opcode = 0x1;
|
|
} else {
|
|
fprintf(stderr, "Unknown instruction\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Decode basic instructions */
|
|
if (basic_opcode != 0) {
|
|
ut8 paramA = 0, paramB = 0;
|
|
|
|
/* Find comma */
|
|
int cn = 0;
|
|
while (cn < 256 && param[cn] != ',' && param[cn] != '\n' && param[cn] != 0)
|
|
cn++;
|
|
|
|
if (param[cn] == ',') {
|
|
ut16 first_word;
|
|
int extraA = 0;
|
|
int extraB = 0;
|
|
char *pa, *pb;
|
|
/* Split parameter string to A and B */
|
|
param[cn] = 0;
|
|
pa = param;
|
|
pb = param + cn + 1;
|
|
|
|
/* Increment address for the start word */
|
|
// current_address++;
|
|
|
|
/* Parameter A */
|
|
paramA = decode_parameter(pa, &extraA, &wordA);
|
|
// if (extraA == 1) current_address++;
|
|
|
|
/* Parameter B */
|
|
paramB = decode_parameter(pb, &extraB, &wordB);
|
|
// if (extraB == 1) current_address++;
|
|
|
|
/* Put everything together */
|
|
first_word = ((paramB & 0x3F) << 10) | ((paramA & 0x3F) << 4) | (basic_opcode & 0xF);
|
|
|
|
/* write opcode */
|
|
#if NOTEND
|
|
memcpy(out, &first_word, 2);
|
|
if (extraA == 1) {
|
|
memcpy(out + 2, &wordA, 2);
|
|
off = 4;
|
|
} else
|
|
off = 2;
|
|
if (extraB == 1) {
|
|
memcpy(out + off, &wordB, 2);
|
|
off += 2;
|
|
}
|
|
#else
|
|
out[0] = (first_word >> 8) & 0xff;
|
|
out[1] = first_word & 0xff;
|
|
if (extraA == 1) {
|
|
out[2] = (wordA >> 8) & 0xff;
|
|
out[3] = wordA & 0xff;
|
|
off = 4;
|
|
} else
|
|
off = 2;
|
|
if (extraB == 1) {
|
|
out[off] = (wordB >> 8) & 0xff;
|
|
out[off + 1] = wordB & 0xff;
|
|
off += 2;
|
|
}
|
|
#endif
|
|
} else {
|
|
fprintf(stderr, "Missing comma\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Non basic instructions */
|
|
if (non_basic_opcode == 0x1) { /* JSR */
|
|
int extraX = 0;
|
|
ut16 first_word, wordX = 0;
|
|
ut8 p = decode_parameter(param, &extraX, &wordX);
|
|
|
|
first_word = ((p & 0x3F) << 10) | ((non_basic_opcode & 0x3F) << 4) | (basic_opcode & 0xF);
|
|
#if NOTEND
|
|
memcpy(out, &first_word, 2);
|
|
if (extraX == 1) {
|
|
memcpy(out + 2, &wordX, 2);
|
|
off = 4;
|
|
} else
|
|
off = 2;
|
|
#else
|
|
out[0] = (first_word >> 8) & 0xff;
|
|
out[1] = first_word & 0xff;
|
|
if (extraX == 1) {
|
|
out[2] = (wordX >> 8) & 0xff;
|
|
out[3] = wordX & 0xff;
|
|
off = 4;
|
|
} else
|
|
off = 2;
|
|
#endif
|
|
}
|
|
return off;
|
|
}
|