非専門的シンギュラリティー研究所

無限に動き続けるシステムを表す方法を AI なども使って考えていきます。

関数電卓コマンド版(4)

乗算・除算をインラインアセンブラで記述した shiftasm.x を以下のように修正しました。

  • iostream と string を使わないように変更しました。実行ファイルのサイズは 98KB になりました。
  • 実行方法を選択することができるようにしました。

乗算・除算をインラインアセンブラで記述するサンプル

を切り替えることができます。

使い方

16 ビットの符号なし整数の乗算・除算を行います。

  • 「数値 * 数値」で乗算
  • 「数値 / 数値」で除算

を行います。「exit」と入力をすると終了します。

処理の方法を切り替えることができます。

  • 「asm」と入力するとアセンブリ言語で書かれた処理を実行します。
  • 「cpp」と入力すると C++ で実行します。
  • 「sys」と入力すると浮動小数点のファンクションコールを実行します。
//#define USE_WINGCC
//#define USE_X68000
//#define USE_IOSTREAM

#ifdef USE_IOSTREAM
#include <iostream>
#define USE_STDSTRING
#ifdef USE_X68000
#include <limits>
#endif
#else
#include <cstring>
#endif
#include <string>

#define BITRANGE 16
#define C_OP_SIZE 1000

//int multiply2(int x, int y, int bitsize)
#ifdef USE_X68000
asm(
    "shift_multiply_a:\n\t"
    "move.l 8(%sp), %d1\n\t"
    "move.w #16, %a0\n\t"
    "moveq  #0, %d0\n\t"
    "smulL3:\n\t"
    "add.l  %d1, %d1\n\t"
    "add.l  %d0, %d0\n\t"
    "btst   #16, %d1\n\t"
    "jeq    smulL2\n\t"
    "add.l  4(%sp), %d0\n\t"
    "smulL2:\n\t"
    "subq.l #1, %a0\n\t"
    "cmp.w  #0, %a0\n\t"
    "jne    smulL3\n\t"
    "rts\n\t"
);
int shift_multiply(int x, int y) {
    int result = 0;
    asm(
        "move.l %1, -(%%sp)\n\t"
        "move.l %2, -(%%sp)\n\t"
        "jsr shift_multiply_a\n\t"
        "add.l  #8, %%sp\n\t"
        "move.l %%d0, %0\n\t"
        : "=r"(result)
        : "r"(x), "r"(y)
		: "d0", "d0", "a0", "a1"
    );
    return result;
}
#else
#ifdef USE_WINGCC
    asm(
        "shift_multiply_a:\n\t"
        "movl $16, %r8d\n\t"
        "xorl %eax, %eax\n\t"
        "smulL3:\n\t"
        "addl %edx, %edx\n\t"
        "addl %eax, %eax\n\t"
        "btl  $16, %edx\n\t"
        "jnc  smulL2\n\t"
        "addl %ecx, %eax\n\t"
        "smulL2:\n\t"
        "decl %r8d\n\t"
        "jne  smulL3\n\t"
        "ret\n\t"
    );
int shift_multiply(int x, int y) {
    int result = 0;
    asm(
        "call shift_multiply_a\n\t"
		: "=a"(result)
		: "c"(x), "d"(y)
		: "r8"
    );
	return result;
}
#else
int shift_multiply(int x, int y) {
    int c = 1 << BITRANGE;
    int result = 0;
    for (int i = 0; i < BITRANGE; i++) {
        y <<= 1;
        result <<= 1;
        if (y & c) {
            result += x;
        }
    }
    return result;
}
#endif
#endif

//int divide1(int x, int y, int bitsize)
#ifdef USE_X68000
asm(
    "shift_divide_a:\n\t"
    "move.l 4(%sp), %a1\n\t"
    "move.l 8(%sp), %d1\n\t"
    "swap   %d1\n\t"
    "clr.w  %d1\n\t"
    "move.w #16, %a0\n\t"
    "moveq  #0, %d0\n\t"
    "sdivL10:\n\t"
    "asr.l  #1, %d1\n\t"
    "add.l  %d0, %d0\n\t"
    "cmp.l  %d1, %a1\n\t"
    "jlt    sdivL9\n\t"
    "addq.l #1, %d0\n\t"
    "sub.l  %d1, % a1\n\t"
    "sdivL9:\n\t"
    "subq.l #1, %a0\n\t"
    "cmp.w  #0, %a0\n\t"
    "jne    sdivL10\n\t"
    "rts\n\t"
);
int shift_divide(int x, int y) {
    int result = 0;
    asm(
        "move.l %1, -(%%sp)\n\t"
        "move.l %2, -(%%sp)\n\t"
        "jsr shift_divide_a\n\t"
        "add.l  #8, %%sp\n\t"
        "move.l %%d0, %0\n\t"
        : "=r"(result)
        : "r"(x), "r"(y)
        : "d0", "d0", "a0", "a1"
    );
    return result;
}
#else
#ifdef USE_WINGCC
asm(
    "shift_divide_a:\n\t"
    "movl $16, %r8d\n\t"
    "xorl %eax, %eax\n\t"
    "sall $16, %edx\n\t"
    "sdivL12:\n\t"
    "sarl %edx\n\t"
    "addl %eax, %eax\n\t"
    "cmpl %ecx, %edx\n\t"
    "jg sdivL11\n\t"
    "incl %eax\n\t"
    "subl %edx, %ecx\n\t"
    "sdivL11:\n\t"
    "decl %r8d\n\t"
    "jne sdivL12\n\t"
    "ret\n\t"
);
int shift_divide(int x, int y) {
    int result = 0;
    asm(
        "call shift_divide_a\n\t"
        : "=a"(result)
        : "c"(x), "d"(y)
        : "r8"
    );
    return result;
}
#else
int shift_divide(int x, int y) {
    int result = 0;
    y <<= BITRANGE;
    for (int i = 0; i < BITRANGE; i++) {
        y >>= 1;
        result <<= 1;
        if (x >= y) {
            result++;
            x -= y;
        }
    }
    return result;
}
#endif
#endif

#ifdef USE_IOSTREAM
void print_line(const std::string& str)
{
    std::cout << str << std::endl;
}
void print_string(const std::string& str)
{
    std::cout << str;
}
void print_int(const std::string& str, int val)
{
    std::cout << str << val << std::endl;
}
#else
void print_line(const char* str)
{
	printf("%s\n", str);
}
void print_string(const char* str)
{
    printf("%s", str);
}
void print_int(const char* str, int val)
{
    printf("%s%d\n", str, val);
}
#endif

#ifdef USE_STDSTRING
bool str_eq(std::string str1, std::string str2) {
	return str1 == str2;
}
#else
bool str_eq(char* str1, const char* str2) {
	return strcmp(str1, str2) == 0;
}
#endif

void clear_input_buffer() {
#ifdef USE_X68000
    scanf("%*[^\n]"); // Clear the rest of the line
#else
    scanf_s("%*[^\n]"); // Clear the rest of the line
#endif
}

int read_expression(int* x, char* op, int* y)
{
#ifdef USE_X68000
    int field_count = scanf("%d %s %d", x, op, y);
#else
    int field_count = scanf_s("%d %s %d", x, op, C_OP_SIZE - 1, y);
#endif
	return field_count;
}

int read_string(char* str)
{
#ifdef USE_X68000
    int field_count = scanf("%s", str);
#else
    int field_count = scanf_s("%s", str, C_OP_SIZE - 1);
#endif
    return field_count;
}

void string_copy(char* dst, char* src)
{
#ifdef USE_X68000
    strncpy(dst, src, C_OP_SIZE - 1);
    dst[C_OP_SIZE - 1] = '\0'; // Ensure null termination
#else
    strcpy_s(dst, C_OP_SIZE - 1, src);
#endif
}

enum ClacMethod {
    Asm, // Assembler
    CPP, // C++
    SysLong, // System Long
};

enum ClacMethod clac_method = Asm;

#ifdef USE_STDSTRING
bool change_clac_method(std::string method) {
    if (method == "asm") {
        clac_method = Asm;
        return true;
    }
    else if (method == "cpp") {
        clac_method = CPP;
        return true;
    }
    else if (method == "sys") {
        clac_method = SysLong;
        return true;
    }
    return false;
}
std::string get_clac_method() {
    switch (clac_method) {
    case Asm:
        return "asm";
    case CPP:
        return "cpp";
    case SysLong:
        return "sys";
    default:
        return "unknown";
    }
}
#else
bool change_clac_method(const char* method) {
    if (strcmp(method, "asm") == 0) {
        clac_method = Asm;
        return true;
    }
    else if (strcmp(method, "cpp") == 0) {
        clac_method = CPP;
        return true;
    }
    else if (strcmp(method, "sys") == 0) {
        clac_method = SysLong;
        return true;
    }
    return false;
}
const char* get_clac_method() {
    switch (clac_method) {
    case Asm:
        return "asm";
    case CPP:
        return "cpp";
    case SysLong:
        return "sys";
    default:
        return "unknown";
    }
}
#endif

int test_multiply(int x, int y) {
    switch (clac_method)
    {
    case Asm:
        return shift_multiply(x, y);
    case CPP:
        return x * y; // Simple C++ multiplication
    case SysLong:
    {
        int result = 0;
#ifdef USE_X68000
        asm(
            "move.l %1, %%d0\n\t"
            "move.l %2, %%d1\n\t"
            "dc.w   0xfe00\n\t"    // __LMUL
            "move.l %%d0, %0\n\t"
            : "=r"(result)         // Output operand
            : "r"(x), "r"(y)       // Input operand
            : "d0", "d1"           // Clobbered register
        );
#endif
        return result; // Return the result of the multiplication
    }
    default:
        return 0; // Default case to avoid warnings
    }
}

int test_divide(int x, int y) {
    switch (clac_method)
    {
    case Asm:
        return shift_divide(x, y);
    case CPP:
		return x / y; // Simple C++ division
    case SysLong:
    {
        int result = 0;
#ifdef USE_X68000
        asm(
            "move.l %1, %%d0\n\t"
            "move.l %2, %%d1\n\t"
            "dc.w   0xfe01\n\t"    // __LDIV
            "move.l %%d0, %0\n\t"
            : "=r"(result)         // Output operand
            : "r"(x), "r"(y)       // Input operand
            : "d0", "d1"           // Clobbered register
        );
#endif
        return result; // Return the result of the multiplication
    }
    default:
        return 0; // Default case to avoid warnings
    }
}

int main()
{
    print_line("Start");
    for (;;) {
        print_string(get_clac_method());
        print_string(" exp> ");
        int x, y;
#ifdef USE_STDSTRING
        std::string op;
#else
		char op[C_OP_SIZE] = { 0 };
#endif
#ifdef USE_IOSTREAM
        if (!(std::cin >> x >> op >> y)) {
            std::cin.clear();
            std::string command;
			std::cin >> command;
            if (command == "exit") {
                std::cout << "Exiting..." << std::endl;
                break;
            }
            if(change_clac_method(command)) {
                continue;
			}
            std::cin.clear();
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            std::cout << "Invalid command" << std::endl;
            continue;
        }
#else
		char c_op[C_OP_SIZE] = { 0 };
        int field_count = read_expression(&x, c_op, &y);
        if (field_count != 3) {
            read_string(c_op);
            if (strcmp(c_op, "exit") == 0) {
				printf("Exiting...\n");
				break;
            }
            if (change_clac_method(c_op)) {
                continue;
            }
            clear_input_buffer();
			printf("Invalid command\n");
			continue;
        }
		string_copy(op, c_op);
#endif
        int result;
        if (str_eq(op, "*")) {
            result = test_multiply(x, y);
            print_int("Multiply: ", result);
        }
        else if (str_eq(op, "/")) {
            result = test_divide(x, y);
			print_int("Divide: ", result);
        }
        else {
			print_line("Invalid operation");
        }
    }
	return 0;
}