アセンブリ言語でわからないので教えてください。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ビルド

アセンブリ言語でわからないので教えてください。

#1

投稿記事 by ビルド » 7年前

昨日の論理設計に関してで、間違えて投稿していました。気にしないでください。
今回アセンブリ言語を使って二進数表現の各桁を下位から順に必要な桁数だけ表示するプログラムとユーグリッドの互徐法を用いて計算するプログラム。
問1実行例(二進数表示)

コード:

 Executing program... 
? 1234567
 1
 1
 1
 0
 0
 0
 0
 1
 0
 1
 1
 0
 1
 0
 1
 1
 0
 1
 0
 0
 1
 (debug): stack_ptr = 0; If non 0, something is WRONG! 
Execution terminated. 
 例 番号の上位6桁と下位6桁
問2 ①1234567 なら 123456 と 234567
②1728363 と 1765036

問1の僕の考え

コード:

0, nop, -1 ;     なにもしない。
1, read, -1 ;    整数値を読み込みスタックに積む(push)
2, store, 15 ;   スタックトップを指定アドレスに格納し、pop
3, load, 16 ;    指定アドレスの整数値をpush
4, opr1, mod ;   2項演算 gtr をスタック上で実行。結果は 0 or 1
5, cjump,12 ;    スタックトップが0なら指定アドレスにジャンプし、pop
6, load, 15 ;    指定アドレスの整数値をpush
7, print, -1 ; スタックトップを表示して、pop
8, jump, 14 ;   指定アドレスに無条件ジャンプ
9, load, 16 ;   指定アドレスの整数値をpush
10, print, -1 ; スタックトップを表示して、pop
11, halt, -1 ;  停止する
もし間違っていたら訂正してください。        

かずま

Re: アセンブリ言語でわからないので教えてください。

#2

投稿記事 by かずま » 7年前

ビルド さんが書きました: もし間違っていたら訂正してください。
全く間違っています。
実行して、想定通りの結果が出ましたか?

3, load, 16 で mem[16] の値を push しますが、
その値は不定です。

4, opr1, mod がなぜ 2項演算gtr なんですか?
2項演算を実行しようにも、スタックには 値が一つしか
push されていません。

5, cjump, 12 で、アドレス12にジャンプすると、
そこにはどんな命令があるのか不明です。
8, jump, 14 もダメです。

二進数表現の各桁を下位から順に必要な桁数だけ表示するプログラム
を、アセンブリ言語ではなく、もっとハイレベルな言語で書いて
それから、それをアセンブリ言語に変換したほうが良いでしょう。

コード:

	mem[0] = read();          // read; store 0
	do {
		mem[1] = mem[0] % 2;  // load 0; ldi 2; opr2 mod; store 1
		mem[0] = mem[0] / 2;  // load 0; ldi 2; opr2 div; store 0
		print([mem[1]);       // load 1; print
	} while (mem[0] != 0);    // load 0; opr1 not; cjump <do>
	halt()                    // halt
命令セットの一覧が必要です。

かずま

Re: アセンブリ言語でわからないので教えてください。

#3

投稿記事 by かずま » 7年前

} while (mem[0] != 0); は別の変換方法もあります。

load 0; ldi 0; opr2 equ; cjump <do>

または

コード:

13: load 0
14: cjump 16
15: jump <do>
16: halt

かずま

Re: アセンブリ言語でわからないので教えてください。

#4

投稿記事 by かずま » 7年前

参考までに。

コード:

#include <stdio.h>
#include <stdlib.h>
#define div div_

enum OP { nop, ldi, opr1, opr2, print, read, jump, cjump, store, load, halt };
enum OP1 { neg, not };
enum OP2 { add, sub, mul, div, mod, less, lseq, gteq, gtr, equ, nteq };

typedef struct { int op; int operand; } Code;

Code code[512] = {
    /*  0: */  nop,  -1,
    /*  1: */  read, -1,
    /*  2: */  store, 0,
    /*  3: */  load,  0,
    /*  4: */  ldi,   2,
    /*  5: */  opr2, mod,
    /*  6: */  store, 1,
    /*  7: */  load,  0,
    /*  8: */  ldi,   2,
    /*  9: */  opr2, div,
    /* 10: */  store, 0,
    /* 11: */  load,  1,
    /* 12: */  print, -1,
    /* 13: */  load,  0,
    /* 14: */  cjump, 16,
    /* 15: */  jump,  3,
    /* 16: */  halt,  -1,
};

int mem[1024], stack[256], sp, pc;

void push(int x)
{
    (sp < 256) ? stack[sp++] = x : puts("stack overflow");
}

int pop(void)
{
    return (sp > 0) ? stack[--sp] : puts("stack empty");
}

void do_opr1(int x)
{
    switch (x) {
    case neg: push(-pop()); break;
    case not: push(!pop()); break;
    }
}

void do_opr2(int x)
{
    int b;
    switch (x) {
    case add: b = pop(); push(pop() + b); break;
    case sub: b = pop(); push(pop() - b); break;
    case mul: b = pop(); push(pop() * b); break;
    case div: (b = pop()) ? push(pop() / b) : puts("zero divide"); break;
    case mod: (b = pop()) ? push(pop() % b) : puts("zero divide"); break;
    case less: b = pop(); push(pop() <  b); break;
    case lseq: b = pop(); push(pop() <= b); break;
    case gteq: b = pop(); push(pop() >= b); break;
    case gtr:  b = pop(); push(pop() >  b); break;
    case equ:  b = pop(); push(pop() == b); break;
    case nteq: b = pop(); push(pop() != b); break;
    }
}

void do_read(void)
{
    int x;
    printf("? ");
    if (scanf("%d", &x) != 1) puts("read failed"), exit(1);
    push(x);
}

void do_print(void) { printf(" %d\n", pop()); }

void execute(int a)
{
    pc = a;
    while (1){
        int operand = code[pc].operand;
        switch (code[pc++].op) {
        case  nop   : break;
        case  ldi   : push(operand); break;
        case  opr1  : do_opr1(operand); break;
        case  opr2  : do_opr2(operand); break;
        case  print : do_print(); break;
        case  read  : do_read(); break;
        case  jump  : pc = operand; break;
        case  cjump : if (pop() == 0) pc = operand; break;
        case  store : mem[operand] = pop(); break;
        case  load  : push(mem[operand]); break;
        case  halt  : return;
        default: puts("illegal op code");
        };
    }
}

int main(void) { execute(0); }
実行結果

コード:

? 12345
 1
 0
 0
 1
 1
 1
 0
 0
 0
 0
 0
 0
 1
 1

かずま

Re: アセンブリ言語でわからないので教えてください。

#5

投稿記事 by かずま » 7年前

コードをファイルから入力するようにしてみました。

sml0.c

コード:

#include <stdio.h>
#include <stdlib.h> // exit
#include <string.h> // strtok
#define div div_

enum OP { nop, ldi, opr1, opr2, print, read, jump, cjump, store, load, halt };
enum OPR { neg, not, add, sub, mul, div, mod, less, lseq, gteq, gtr, equ, nteq };

typedef struct { int op; int operand; } Code;

Code code[512];
int mem[1024], stack[256], sp, pc;

void error(const char *msg)
{
    printf("[pc:%d, sp:%d] %s\n", pc, sp, msg);
    exit(1);
}

void push(int x)
{
    if (sp >= 256) error("stack overflow");
    stack[sp++] = x;
}

int pop(void)
{
    if (sp <= 0) error("stack empty");
    return stack[--sp];
}

void do_opr1(int x)
{
    int a = pop();
    switch (x) {
    case neg: push(-a); break;
    case not: push(!a); break;
    }
}

void do_opr2(int x)
{
    int b = pop(), a = pop();
    switch (x) {
    case add: push(a + b); break;
    case sub: push(a - b); break;
    case mul: push(a * b); break;
    case div: if (b == 0) error("zero divide");
              push(a / b); break;
    case mod: if (b == 0) error("zero divide");
              push(a % b); break;
    case less: push(a <  b); break;
    case lseq: push(a <= b); break;
    case gteq: push(a >= b); break;
    case gtr:  push(a >  b); break;
    case equ:  push(a == b); break;
    case nteq: push(a != b); break;
    }
}

void do_read(void)
{
    int x;
    printf("? ");
    if (scanf("%d", &x) != 1) error("read failed");
    push(x);
}

void do_print(void) { printf(" %d\n", pop()); }

void execute(int a)
{
    pc = a;
    while (1){
        int operand = code[pc].operand;
        switch (code[pc++].op) {
        case  nop    : break;
        case  ldi    : push(operand); break;
        case  opr1    : do_opr1(operand); break;
        case  opr2    : do_opr2(operand); break;
        case  print    : do_print(); break;
        case  read    : do_read(); break;
        case  jump    : pc = operand; break;
        case  cjump    : if (pop() == 0) pc = operand; break;
        case  store    : mem[operand] = pop(); break;
        case  load    : push(mem[operand]); break;
        case  halt  : return;
        default: error("illegal op code");
        };
    }
}

const char *str_op[] = {
    "nop", "ldi", "opr1", "opr2", "print", "read", "jump", "cjump",
    "store", "load", "halt"
};
const char *str_opr[] = {
    "neg", "not", "add", "sub", "mul", "div", "mod",
    "less", "lseq", "gteq", "gtr", "equ", "nteq"
};
const int n_op = sizeof(str_op) / sizeof(*str_op);
const int n_opr = sizeof(str_opr) / sizeof(*str_opr);

int compile(const char *fname)
{
    FILE *fp = fopen(fname, "r");
    if (!fp) return printf("can't open %s\n", fname), -1;
    int ret = 0;
    char buf[256], *p;
    while (fgets(buf, sizeof buf, fp)) {
        char *addr = strtok(buf, " ,:");
        char *op = strtok(NULL, " ,");
        char *opr = strtok(NULL, " ,;\n");
        if (!addr || !op || !opr) { ret = 1; break; }
        int i, j, k = strtol(addr, &p, 10);
        if (p == addr) { ret = 2; break; }
        for (i = 0; i < n_op && strcmp(op, str_op[i]); i++) ;
        if (i == n_op) { ret = 3; break; }
        int str = 0;
        j = strtol(opr, &p, 10);
        if (p == opr) {
            for (j = 0; j < n_opr && strcmp(opr, str_opr[j]); j++) ;
            if (j == n_opr) { ret = 4; break; }
            str = 1;
        }
        code[k].op = i, code[k].operand = j;
        if (str)
            printf("%3d:  %-6s %s\n", k, str_op[i], str_opr[j]);
        else
            printf("%3d:  %-6s %d\n", k, str_op[i], j);
    }
    fclose(fp);
    return ret;
}

int main(int argc, char *argv[])
{
    if (argc != 2) return printf("usage: %s file\n", argv[0]), 1;
    if (compile(argv[1])) return puts("complie error"), 2;
    puts("Executing program...");
    execute(0);
    printf("[pc:%d, sp:%d] Execution terminated\n", pc, sp);
}
sample2.txt

コード:

0:  nop,  -1
1,  read, -1
2,  store  0
3:  load   0,
4:  ldi,   2  ;
5:  opr2, mod ; mem[0] % 2
6:  store, 1
7:  load,  0
8:  ldi,   2
9:  opr2, div
10:  store, 0
11:  load,  1
12:  print, -1
13:  load,  0
14:  cjump, 16
15:  jump,  3
16:  halt,  -1
実行例

コード:

C:\tmp\SML0>sml0 sample2.txt
  0:  nop    -1
  1:  read   -1
  2:  store  0
  3:  load   0
  4:  ldi    2
  5:  opr2   mod
  6:  store  1
  7:  load   0
  8:  ldi    2
  9:  opr2   div
 10:  store  0
 11:  load   1
 12:  print  -1
 13:  load   0
 14:  cjump  16
 15:  jump   3
 16:  halt   -1
Executing program...
? 12345
 1
 0
 0
 1
 1
 1
 0
 0
 0
 0
 0
 0
 1
 1
[pc:17, sp:0] Execution terminated

C:\tmp\SML0>

ビルド

Re: アセンブリ言語でわからないので教えてください。

#6

投稿記事 by ビルド » 7年前

ありがとうございます。参考になりました。

かずま

Re: アセンブリ言語でわからないので教えてください。

#7

投稿記事 by かずま » 7年前

ビルド さんが書きました: 今回アセンブリ言語を使って二進数表現の各桁を下位から順に必要な桁数だけ表示するプログラムとユーグリッドの互徐法を用いて計算するプログラム。
ユーグリッドの互徐法を用いて計算するプログラム
のほうはどうなっているのですか?
ビルド さんが書きました:  例 番号の上位6桁と下位6桁
問2 ①1234567 なら 123456 と 234567
②1728363 と 1765036
これは何ですか?
ユークリッドの互除法ですか?

さて、アセンブリ言語というのは一つではありません。
CPU によって異なります。インテル、ARM、MIPS など
たくさんあります。CASL というのもあります。
スクリプト言語といっても、Python、Ruby、JavaScript、
Perl などたくさんあり、コンパイラ言語も、C、C++、
Java などがあるのと同じですね。

以前、スタックマシン sml0 のアセンブリ言語がわからない。
というのがあったので、それだとわかりましたが、
質問としては、再度説明するべきです。
今回も、不明な命令が必要だったので、
私は、div や not を想定して回答しましたが、
それが正しいとは限りません。

結局どういうコードになったのですか?

ビルド

Re: アセンブリ言語でわからないので教えてください。

#8

投稿記事 by ビルド » 7年前

ユーグリッドのプログラムできました。

コード:

[@localhost w11]$ ./sml0x64 gcd.txt
   0:     nop      -1
   1:    read      -1
   2:   store       0
   3:    read      -1
   4:   store       1
   5:     nop      -1
   6:     ldi       0
   7:    load       1
   8:    opr2    nteq
   9:   cjump      17
  10:    load       0
  11:    load       1
  12:    opr2     mod
  13:    load       1
  14:   store       0
  15:   store       1
  16:    jump       5
  17:     nop      -1
  18:    load       0
  19:   store       2
  20:    load       2
  21:   print      -1
  22:    halt      -1
Executing program...
? 160187
? 601879
1
(debug): stack_ptr = 0; If non 0, something is WRONG!
Execution terminated.
[@localhost w11]$ ./sml0x64 gcd.txt
   0:     nop      -1
   1:    read      -1
   2:   store       0
   3:    read      -1
   4:   store       1
   5:     nop      -1
   6:     ldi       0
   7:    load       1
   8:    opr2    nteq
   9:   cjump      17
  10:    load       0
  11:    load       1
  12:    opr2     mod
  13:    load       1
  14:   store       0
  15:   store       1
  16:    jump       5
  17:     nop      -1
  18:    load       0
  19:   store       2
  20:    load       2
  21:   print      -1
  22:    halt      -1
Executing program...
? 1728363
? 1765036
1183
(debug): stack_ptr = 0; If non 0, something is WRONG!
Execution terminated.
[@localhost w11]$ 

こんな感じです。

ビルド

Re: アセンブリ言語でわからないので教えてください。

#9

投稿記事 by ビルド » 7年前

いつもすみませんが、今度は配列を用いてアセンブリコードで表示するのがわからなくて教えください。
(1) val, ass, assv命令を活用し,mem領域を配列のように使用して,キーボードから入力したデータを降順にソートして大きい順に表示する sml2のアセンブラプログラムを作成する。

3) 上記の課題(1),(2)とも実行時に与えるデータは,各自の学籍番号の10進表記7桁が abcdefg であるとき,abc,bcd,cde, def, efg, fga, gabの7つの10進表記を作り,それらを使用せよ.

ビルド

Re: アセンブリ言語でわからないので教えてください。

#10

投稿記事 by ビルド » 7年前

コード:

nop, -1; test for val and ass; these provide indexed indirect mem access.
1, ldi, 0;  displacement
2, read, -1; data
3, nop, -1
4, ass, 11; mem[11+0]:=data
5, ldi, 0; displacement
6, val, 11;  mem[11+0] is pushed
7, print, -1
8, del, -1
9, halt, -1
できるところまでやってみました。そこでデータを降順にソートして大きい順にするプログラムしたいのですがどうすればよいでしょうか?

返信

“C言語何でも質問掲示板” へ戻る