rizin/librz/arch/isa/dcpu16/asm.c
wargio d47ceedbd3 Merge rz_asm and rz_analysis into one library but keep deprecated apis.
The tms320c64x has been merged into tms320.
2024-03-07 18:38:49 +08:00

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;
}