Sesi Assembly Pertama

Menulis dan menjalankan program RISC-V pertama: perulangan, percabangan, dan struktur kondisional.

Daftar isi

Bahasa assembly memberi kendali penuh atas urutan instruksi yang dijalankan mesin, tetapi tidak menyediakan perulangan for maupun if milik bahasa tingkat tinggi. Topik ini menulis program RISC-V pertama yang utuh, yaitu satu program perulangan dan satu program percabangan, lalu menjalankan keduanya dengan simulator RV32I. Setelah membacanya, pola penghitung, pembanding, dan lompatan pada tingkat mesin dapat dibaca dan ditulis secara langsung.

Struktur berkas dan simulator#

Program assembly disimpan sebagai berkas .s yang berisi instruksi, label, dan direktif. Instruksi diterjemahkan menjadi instruksi mesin, label memberi nama pada satu alamat sebagai target lompatan, sedangkan direktif .data dan .text membagi berkas menjadi bagian data dan bagian instruksi. Direktif .globl main menandai label main sebagai titik masuk, mengikuti kebiasaan assembler yang menerjemahkan assembly menjadi instruksi mesin.

Simulator RV32I yang dipakai sepanjang modul ini tersedia kembali di bundle topik ini sebagai sim.py. Simulator menjalankan berkas .s dengan perintah python3 sim.py nama-berkas.s dan mencetak keluaran program melalui layanan ecall. Nomor layanan ditaruh pada a7 dan argumennya pada a0, yaitu layanan 1 untuk mencetak bilangan bertanda, layanan 4 untuk string, layanan 11 untuk satu karakter, dan layanan 10 untuk keluar.

python sim.py
#!/usr/bin/env python3
"""sim.py — simulator RV32I subset untuk pembelajaran arsitektur komputer.

Pemakaian:
    python3 sim.py [--trace] program.s
    python3 sim.py            (tanpa argumen: cetak bantuan)

Cakupan:
  - Instruksi dasar RV32I (aritmetika, logika, shift, branch, jump,
    load/store, lui, auipc) beserta pseudo-instruksi umum.
  - Direktif: .text, .data, .globl, .align, .word, .byte, .asciz, .space.
  - ecall gaya RARS: a7=1 cetak bilangan bulat (a0), a7=4 cetak string
    di alamat a0, a7=11 cetak karakter (a0), a7=10 keluar.
  - sp (x2) diinisialisasi ke 0x10000; memori di-nol-kan; pc mulai 0.
"""
from __future__ import annotations

import re
import sys

MASK32 = 0xFFFFFFFF
STACK_TOP = 0x10000
STEP_LIMIT = 1_000_000

REGS = {"zero": 0, "ra": 1, "sp": 2, "gp": 3, "tp": 4,
        "t0": 5, "t1": 6, "t2": 7, "s0": 8, "fp": 8, "s1": 9,
        "a0": 10, "a1": 11, "a2": 12, "a3": 13, "a4": 14,
        "a5": 15, "a6": 16, "a7": 17,
        "s2": 18, "s3": 19, "s4": 20, "s5": 21, "s6": 22,
        "s7": 23, "s8": 24, "s9": 25, "s10": 26, "s11": 27,
        "t3": 28, "t4": 29, "t5": 30, "t6": 31}
for _i in range(32):
    REGS[f"x{_i}"] = _i

INT_RE = re.compile(r"^[+-]?(0[xX][0-9a-fA-F]+|0[bB][01]+|\d+)$")
MEMOP_RE = re.compile(r"^([+-]?(?:0[xX][0-9a-fA-F]+|0[bB][01]+|\d+))\s*\(\s*([a-zA-Z0-9]+)\s*\)$")


class SimError(Exception):
    pass


def reg(name: str) -> int:
    key = name.strip().lower()
    if key not in REGS:
        raise SimError(f"register tidak dikenal `{name}`")
    return REGS[key]


def to_int(text: str) -> int:
    s = text.strip().replace("_", "")
    if not INT_RE.match(s):
        raise SimError(f"angka tidak valid `{text}`")
    neg = s.startswith("-")
    s = s.lstrip("+-")
    value = int(s, 0)
    return -value if neg else value


def signed(value: int) -> int:
    value &= MASK32
    return value - (1 << 32) if value & 0x80000000 else value


def u32(value: int) -> int:
    return value & MASK32


class Program:
    def __init__(self) -> None:
        self.labels: dict[str, int] = {}
        self.instructions: list[tuple[int, str, str]] = []  # (pc, teks, mnemonic)
        self.data: dict[int, int] = {}  # alamat byte -> nilai byte


def parse_source(text: str) -> Program:
    prog = Program()
    pc = 0
    dataptr = None  # None = sedang tidak menandai alamat data berikutnya
    pending: list[str] = []

    def flush_labels() -> None:
        for name in pending:
            if name in prog.labels:
                raise SimError(f"label duplikat `{name}`")
            prog.labels[name] = dataptr if dataptr is not None else pc

    for lineno, raw in enumerate(text.split("\n"), start=1):
        line = raw.split("#", 1)[0].strip()
        if not line:
            continue
        pending = []
        while True:
            m = re.match(r"^([A-Za-z_.][\w.]*)\s*:\s*(.*)$", line)
            if not m:
                break
            pending.append(m.group(1))
            line = m.group(2).strip()
        if not line:
            flush_labels()
            continue
        parts = line.split(None, 1)
        head = parts[0].lower()
        rest = parts[1].strip() if len(parts) > 1 else ""
        if head.startswith("."):
            flush_labels()
            if head in (".text", ".data", ".globl", ".global", ".section"):
                if head == ".text":
                    dataptr = None
                elif head == ".data":
                    dataptr = pc
                continue
            if head == ".align":
                align = 1 << to_int(rest)
                if dataptr is not None:
                    while dataptr % align:
                        prog.data[dataptr] = 0
                        dataptr += 1
                else:
                    pad = (align - pc % align) % align
                    pc += pad
                continue
            if head in (".word", ".byte", ".asciz", ".string", ".space", ".zero"):
                addr = pc if dataptr is None else dataptr
                addr_end = addr
                if head == ".space" or head == ".zero":
                    addr_end = addr + to_int(rest)
                elif head in (".asciz", ".string"):
                    s = rest.strip()
                    if not (s.startswith('"') and s.endswith('"')):
                        raise SimError(f"baris {lineno}: .asciz butuh string berkutip")
                    body = s[1:-1].encode("latin-1", "backslashreplace").decode("latin-1")
                    for ch in body + "\x00":
                        prog.data[addr_end] = ord(ch) & 0xFF
                        addr_end += 1
                else:
                    width = 4 if head == ".word" else 1
                    items = [t.strip() for t in rest.split(",") if t.strip()]
                    for item in items:
                        value = to_int(item) & (MASK32 if width == 4 else 0xFF)
                        for k in range(width):
                            prog.data[addr_end] = (value >> (8 * k)) & 0xFF
                            addr_end += 1
                if dataptr is None:
                    pc = addr_end
                else:
                    dataptr = addr_end
                continue
            raise SimError(f"baris {lineno}: direktif tidak didukung `{head}`")
        # instruksi
        flush_labels()
        if dataptr is not None:
            raise SimError(f"baris {lineno}: instruksi setelah label data")
        prog.instructions.append((pc, line, head))
        pc += 4
    return prog


def operands(rest: str) -> list[str]:
    if not rest:
        return []
    return [t.strip() for t in rest.split(",") if t.strip()]


class Sim:
    def __init__(self, prog: Program, trace: bool) -> None:
        self.prog = prog
        self.trace = trace
        self.regs = [0] * 32
        self.regs[2] = STACK_TOP
        self.mem: dict[int, int] = dict(prog.data)
        self.pc = 0
        self.out: list[str] = []

    def read_byte(self, addr: int) -> int:
        return self.mem.get(addr & MASK32, 0)

    def read(self, addr: int, width: int) -> int:
        if addr % width:
            raise SimError(f"akses tidak sejajar: alamat 0x{addr & MASK32:08x} lebar {width}")
        value = 0
        for k in range(width):
            value |= self.read_byte(addr + k) << (8 * k)
        return value

    def write(self, addr: int, width: int, value: int) -> None:
        if addr % width:
            raise SimError(f"akses tidak sejajar: alamat 0x{addr & MASK32:08x} lebar {width}")
        for k in range(width):
            self.mem[(addr + k) & MASK32] = (value >> (8 * k)) & 0xFF

    def label(self, name: str) -> int:
        key = name.strip()
        if key not in self.prog.labels:
            raise SimError(f"label tidak dikenal `{name}`")
        return self.prog.labels[key]

    def emit(self, text: str) -> None:
        self.out.append(text)

    def do_ecall(self) -> bool:
        service = signed(self.regs[17])
        a0 = self.regs[10]
        if service == 1:
            self.emit(str(signed(a0)))
        elif service == 4:
            chars = []
            addr = a0
            while True:
                byte = self.read_byte(addr)
                if byte == 0:
                    break
                chars.append(chr(byte))
                addr += 1
            self.emit("".join(chars))
        elif service == 11:
            self.emit(chr(a0 & 0xFF))
        elif service == 10:
            return True
        else:
            raise SimError(f"ecall layanan {service} tidak didukung")
        return False

    def step(self) -> bool:
        """Jalankan satu instruksi; kembalikan True bila program selesai."""
        index = self.pc // 4
        if index < 0 or index >= len(self.prog.instructions):
            raise SimError(f"pc 0x{self.pc & MASK32:08x} berada di luar instruksi "
                           "(program berakhir tanpa ecall exit?)")
        pc, text, mnem = self.prog.instructions[index]
        if self.trace:
            print(f"pc=0x{pc & MASK32:08x}  {text}")
        args = operands(text.split(None, 1)[1] if len(text.split(None, 1)) > 1 else "")
        r = self.regs

        def imm_load(rd: int, value: int) -> None:
            r[rd] = u32(value)

        if mnem in ("addi", "slti", "sltiu", "xori", "ori", "andi", "slli", "srli", "srai"):
            rd, rs1 = reg(args[0]), reg(args[1])
            imm = to_int(args[2])
            if mnem == "addi":
                imm_load(rd, r[rs1] + imm)
            elif mnem == "slti":
                imm_load(rd, 1 if signed(r[rs1]) < imm else 0)
            elif mnem == "sltiu":
                imm_load(rd, 1 if r[rs1] < u32(imm) else 0)
            elif mnem == "xori":
                imm_load(rd, r[rs1] ^ imm)
            elif mnem == "ori":
                imm_load(rd, r[rs1] | imm)
            elif mnem == "andi":
                imm_load(rd, r[rs1] & imm)
            elif mnem == "slli":
                imm_load(rd, r[rs1] << (imm & 31))
            elif mnem == "srli":
                imm_load(rd, r[rs1] >> (imm & 31))
            else:
                imm_load(rd, signed(r[rs1]) >> (imm & 31))
        elif mnem in ("add", "sub", "sll", "slt", "sltu", "xor", "srl", "sra", "or", "and"):
            rd, rs1, rs2 = reg(args[0]), reg(args[1]), reg(args[2])
            if mnem == "add":
                imm_load(rd, r[rs1] + r[rs2])
            elif mnem == "sub":
                imm_load(rd, r[rs1] - r[rs2])
            elif mnem == "sll":
                imm_load(rd, r[rs1] << (r[rs2] & 31))
            elif mnem == "slt":
                imm_load(rd, 1 if signed(r[rs1]) < signed(r[rs2]) else 0)
            elif mnem == "sltu":
                imm_load(rd, 1 if r[rs1] < r[rs2] else 0)
            elif mnem == "xor":
                imm_load(rd, r[rs1] ^ r[rs2])
            elif mnem == "srl":
                imm_load(rd, r[rs1] >> (r[rs2] & 31))
            elif mnem == "sra":
                imm_load(rd, signed(r[rs1]) >> (r[rs2] & 31))
            elif mnem == "or":
                imm_load(rd, r[rs1] | r[rs2])
            else:
                imm_load(rd, r[rs1] & r[rs2])
        elif mnem == "lui":
            imm_load(reg(args[0]), to_int(args[1]) << 12)
        elif mnem == "auipc":
            imm_load(reg(args[0]), pc + (to_int(args[1]) << 12))
        elif mnem in ("beq", "bne", "blt", "bge", "bltu", "bgeu"):
            rs1, rs2 = reg(args[0]), reg(args[1])
            target = self.label(args[2])
            taken = {"beq": r[rs1] == r[rs2], "bne": r[rs1] != r[rs2],
                     "blt": signed(r[rs1]) < signed(r[rs2]),
                     "bge": signed(r[rs1]) >= signed(r[rs2]),
                     "bltu": r[rs1] < r[rs2], "bgeu": r[rs1] >= r[rs2]}[mnem]
            if taken:
                self.pc = target
                return False
        elif mnem == "jal":
            if len(args) == 1:
                rd, target = 1, self.label(args[0])
            else:
                rd, target = reg(args[0]), self.label(args[1])
            imm_load(rd, pc + 4)
            self.pc = target
            return False
        elif mnem == "jalr":
            if len(args) == 1:
                rd, base, off = 1, reg(args[0]), 0
            else:
                rd, base = reg(args[0]), reg(args[1])
                off = to_int(args[2]) if len(args) > 2 else 0
            dest = (r[base] + off) & MASK32 & ~1
            imm_load(rd, pc + 4)
            self.pc = dest
            return False
        elif mnem in ("lb", "lh", "lw", "lbu", "lhu"):
            rd, m = reg(args[0]), MEMOP_RE.match(args[1])
            if not m:
                raise SimError(f"operand memori tidak valid `{args[1]}`")
            addr = u32(r[reg(m.group(2))] + to_int(m.group(1)))
            width = {"lb": 1, "lbu": 1, "lh": 2, "lhu": 2, "lw": 4}[mnem]
            value = self.read(addr, width)
            if mnem in ("lb", "lh") and value & (1 << (width * 8 - 1)):
                value -= 1 << (width * 8)
            imm_load(rd, value)
        elif mnem in ("sb", "sh", "sw"):
            rs2, m = reg(args[0]), MEMOP_RE.match(args[1])
            if not m:
                raise SimError(f"operand memori tidak valid `{args[1]}`")
            addr = u32(r[reg(m.group(2))] + to_int(m.group(1)))
            self.write(addr, {"sb": 1, "sh": 2, "sw": 4}[mnem], r[rs2])
        elif mnem == "ecall":
            if self.do_ecall():
                return True
        elif mnem == "ebreak":
            return True
        else:
            # pseudo-instruksi
            if mnem == "nop":
                pass
            elif mnem == "li":
                imm_load(reg(args[0]), to_int(args[1]))
            elif mnem == "la":
                imm_load(reg(args[0]), self.label(args[1]))
            elif mnem == "mv":
                imm_load(reg(args[0]), r[reg(args[1])])
            elif mnem == "not":
                imm_load(reg(args[0]), ~r[reg(args[1])])
            elif mnem == "neg":
                imm_load(reg(args[0]), -r[reg(args[1])])
            elif mnem == "seqz":
                imm_load(reg(args[0]), 1 if r[reg(args[1])] == 0 else 0)
            elif mnem == "snez":
                imm_load(reg(args[0]), 1 if r[reg(args[1])] != 0 else 0)
            elif mnem == "sltz":
                imm_load(reg(args[0]), 1 if signed(r[reg(args[1])]) < 0 else 0)
            elif mnem == "j":
                self.pc = self.label(args[0])
                return False
            elif mnem == "jr":
                self.pc = r[reg(args[0])] & MASK32 & ~1
                return False
            elif mnem == "ret":
                self.pc = r[1] & MASK32 & ~1
                return False
            elif mnem == "call":
                target = self.label(args[0])
                imm_load(1, pc + 4)
                self.pc = target
                return False
            elif mnem in ("beqz", "bnez", "bgez", "bltz", "bgtz", "blez"):
                rs1 = reg(args[0])
                taken = {"beqz": r[rs1] == 0, "bnez": r[rs1] != 0,
                         "bgez": signed(r[rs1]) >= 0, "bltz": signed(r[rs1]) < 0,
                         "bgtz": signed(r[rs1]) > 0, "blez": signed(r[rs1]) <= 0}[mnem]
                if taken:
                    self.pc = self.label(args[1])
                    return False
            elif mnem in ("bgt", "ble", "bgtu", "bleu"):
                rs1, rs2 = reg(args[0]), reg(args[1])
                taken = {"bgt": signed(r[rs1]) > signed(r[rs2]),
                         "ble": signed(r[rs1]) <= signed(r[rs2]),
                         "bgtu": r[rs1] > r[rs2], "bleu": r[rs1] <= r[rs2]}[mnem]
                if taken:
                    self.pc = self.label(args[2])
                    return False
            else:
                raise SimError(f"instruksi tidak dikenal `{mnem}`")
        self.pc = u32(self.pc + 4)
        return False

    def run(self) -> None:
        for _ in range(STEP_LIMIT):
            if self.step():
                return
        raise SimError("batas langkah terlampaui (kemungkinan perulangan tak berujung)")


def main() -> None:
    argv = sys.argv[1:]
    trace = "--trace" in argv
    argv = [a for a in argv if a != "--trace"]
    if not argv:
        print("Pemakaian: python3 sim.py [--trace] program.s")
        return
    try:
        with open(argv[0], encoding="utf-8") as fh:
            source = fh.read()
    except OSError as exc:
        print(f"sim: {exc}", file=sys.stderr)
        raise SystemExit(1)
    try:
        prog = parse_source(source)
        if not prog.instructions:
            raise SimError("tidak ada instruksi untuk dijalankan")
        sim = Sim(prog, trace)
        sim.run()
        for line in sim.out:
            print(line)
    except SimError as exc:
        print(f"sim: {exc}", file=sys.stderr)
        raise SystemExit(1)


if __name__ == "__main__":
    main()
text Keluaran
Pemakaian: python3 sim.py [--trace] program.s

Setiap program pada topik ini berakhir dengan li a7, 10 diikuti ecall. Tanpa penutup itu, program counter jatuh melewati instruksi terakhir dan simulator menimbulkan error karena tidak ada instruksi lagi untuk dijalankan. Penutup yang sama juga menjamin keluaran program selalu berasal dari ecall yang ditulis secara eksplisit.

Perulangan: menjumlahkan satu sampai sepuluh#

Program pertama menjumlahkan seluruh bilangan dari 1 sampai 10. Konsepnya sama seperti perulangan for pada bahasa tingkat tinggi, yaitu satu penghitung yang dinaikkan dan satu syarat yang diuji, tetapi seluruhnya ditulis dengan register, label, dan instruksi branch, yakni percabangan bersyarat pada tingkat mesin.

asm jumlah.s
# tidak-dijalankan
# jumlah.s - menjumlahkan bilangan 1 sampai 10 dengan perulangan

        .text
        .globl main
main:
        li t0, 10               # batas atas N
        li t1, 0                # penjumlah hasil
        li t2, 1                # penghitung mulai dari 1
ulang:
        add t1, t1, t2          # jumlah += penghitung
        addi t2, t2, 1          # penghitung naik satu
        ble t2, t0, ulang       # ulang selama penghitung <= N
        mv a0, t1               # hasil ke a0
        li a7, 1                # layanan cetak bilangan
        ecall                   # cetak 55
        li a7, 10               # layanan keluar
        ecall
bash
# menjalankan program penjumlahan
python3 sim.py jumlah.s
text Keluaran
55

Angka 55 adalah hasil penjumlahan 1 sampai 10. Register t2 berperan sebagai penghitung, t1 menampung jumlah sementara, dan ble t2, t0, ulang kembali ke label ulang selama penghitung masih di bawah atau sama dengan batas. Ketika penghitung mencapai 11, syaratnya gagal, eksekusi lanjut ke baris berikutnya, dan hasilnya dicetak sekali saja.

Struktur penghitung ini adalah cetakan umum untuk semua perulangan pada tingkat mesin. Nilai t0 diubah untuk menjumlahkan rentang yang lain, dan pekerjaan apa pun yang diletakkan di antara label ulang dan instruksi ble ikut berulang. Sepuluh iterasi hanya berarti puluhan langkah eksekusi, sehingga simulator menyelesaikannya tanpa kesulitan.

Mesin tidak mengenal nama variabel, sehingga peran setiap register ditentukan murni oleh kebiasaan penulisnya. Komentar pada tiap baris menjadi satu-satunya pengingat peran itu, dan kebiasaan yang baik adalah menuliskannya sejak baris pertama. Dengan komentar yang tertib, perulangan sepuluh baris seperti ini masih dapat dibaca tanpa menjalankannya.

Cara perulangan dieksekusi#

Perulangan pada tingkat mesin hanyalah lompatan mundur. Program counter bergerak lurus empat byte per instruksi sampai ble diuji. Bila syaratnya benar, program counter dimuat ulang dengan alamat label ulang, sehingga urutan eksekusi kembali ke awal badan perulangan. Tidak ada mekanisme tersembunyi, karena setiap iterasi benar-benar mengeksekusi instruksi yang sama berulang kali. Perhatikan pula bahwa syarat diuji di akhir badan perulangan, bukan di awal seperti while, sehingga badan selalu berjalan sedikitnya satu kali.

Instruksi branch RV32I membandingkan dua register, bukan register dengan konstanta, sehingga batas perulangan harus disiapkan di register terlebih dahulu. Baris li t0, 10 menyiapkan batas itu. beq, bne, blt, dan bge menjadi pembanding bertanda, sedangkan bltu dan bgeu membandingkan tanpa memperhatikan tanda.

Batas yang salah satu kali pun menimbulkan kesalahan yang disebut off-by-one, yaitu iterasi berlebih atau kurang satu kali. Pada program di atas, badan perulangan dieksekusi tepat sepuluh kali karena penghitung naik sebelum syarat diuji kembali. Menggeser addi t2, t2, 1 ke posisi lain mengubah hitungan itu, sehingga batasnya wajib diperiksa ulang setiap kali struktur perulangan disunting.

Percabangan: if-else dalam assembly#

Program kedua mengklasifikasikan satu nilai ujian menjadi tiga kategori dengan pola yang sama seperti if-else berantai pada bahasa tingkat tinggi. Nilai 75 diperiksa terhadap dua batas, yaitu 80 dan 60, lalu alamat string yang sesuai dimuat ke a0 untuk dicetak.

asm nilai.s
# tidak-dijalankan
# nilai.s - mengklasifikasikan satu nilai ujian dengan percabangan

        .data
s_baik:   .asciz "baik sekali"
s_cukup:  .asciz "cukup baik"
s_kurang: .asciz "perlu latihan"

        .text
        .globl main
main:
        li t0, 75               # nilai ujian yang diperiksa
        li t1, 80
        bge t0, t1, cetak_baik  # nilai >= 80
        li t2, 60
        bge t0, t2, cetak_cukup # nilai >= 60
        la a0, s_kurang         # tidak masuk dua syarat di atas
        j cetak
cetak_baik:
        la a0, s_baik
        j cetak
cetak_cukup:
        la a0, s_cukup
cetak:
        li a7, 4                # layanan cetak string
        ecall                   # cetak isinya
        li a7, 10               # layanan keluar
        ecall
bash
# menjalankan program klasifikasi nilai
python3 sim.py nilai.s
text Keluaran
cukup baik

Nilai 75 tidak memenuhi batas 80, tetapi memenuhi batas 60, sehingga eksekusi mendarat di label cetak_cukup dan mencetak cukup baik. Masing-masing cabang memuat alamat stringnya ke a0 melalui la, kemudian melompat ke label cetak yang sama, sehingga layanan ecall cukup ditulis satu kali di akhir.

Urutan pengujian menentukan hasil. Bila batas 60 diuji lebih dahulu, nilai 90 akan masuk kategori cukup sebelum sempat diuji terhadap 80, karena bge pertama yang lulus langsung melompatkan eksekusi ke cabangnya. Aturannya satu, yaitu menguji syarat yang paling ketat terlebih dahulu atau menyusun syarat yang saling menutup.

Percabangan yang benar juga diuji dari sisi datanya. Nilai 75 pada program berasal dari li, sehingga menggantinya dengan angka lain langsung menunjukkan jalur yang ditempuh. Percobaan dengan 80, 79, 60, dan 59 memeriksa tepi setiap batas, karena tepat pada batas itulah kesalahan arah pertidaksamaan paling sering muncul.

Rantai kondisi dan lompatan tak bersyarat#

Pseudo-instruksi j adalah lompatan tak bersyarat, yaitu lompatan yang selalu dieksekusi sehingga berperan sebagai penutup satu cabang. Tanpa j cetak, eksekusi yang selesai di cabang baik akan jatuh ke label berikutnya dan mencetak string yang salah. Kebalikannya juga berlaku, karena cabang yang mendarat langsung di cetak tidak memerlukan j sama sekali.

Pseudo-instruksi percabangan lain merapikan kasus yang sering muncul. beqz t1, label menyalurkan eksekusi bila register bernilai nol dan bnez sebaliknya, sedangkan bgtz, bgez, bltz, dan blez membandingkan register dengan nol tanpa register pembanding kedua. Semuanya diterjemahkan menjadi instruksi dasar dengan zero sebagai salah satu operandnya.

Struktur kondisional yang lebih besar tetap tersusun dari blok yang sama. else if adalah rantai bge berikutnya, else adalah blok setelah semua cabang gagal, dan bgt serta ble adalah pseudo-instruksi yang menyingkat penulisan perbandingan. Bentuknya lebih bertele-tele daripada bahasa tingkat tinggi, tetapi setiap barisnya jelas artinya, dan string tetap diambil dari bagian .data melalui la.

Kesalahan umum#

  • Perulangan tak berujung. Penghitung lupa dinaikkan atau syarat tidak pernah gagal, sehingga simulator berhenti dengan error batas langkah. Pastikan ada instruksi yang mengubah kondisi syarat di dalam badan perulangan, karena instruksi yang berada di luar perulangan tidak pernah dicapai eksekusi.
  • Program jatuh dari akhir berkas. Tanpa li a7, 10 dan ecall, program counter keluar dari daftar instruksi dan simulator menimbulkan error. Akhiri setiap jalur eksekusi, termasuk jalur percabangan, dengan instruksi keluar.
  • String dicetak tanpa la. Layanan 4 membaca string dari alamat pada a0, sehingga melewatkan la a0, label membuat ecall membaca alamat yang salah. Muat alamatnya terlebih dahulu.
  • Lupa j di akhir cabang. Eksekusi jatuh ke label cabang berikutnya dan menjalankan perintah yang tidak seharusnya. Tutup setiap cabang dengan lompatan ke titik pertemuan.
  • Menguji register yang belum diisi. Branch membandingkan isi register apa pun tanpa peringatan, sehingga bge t0, t1, label pada register yang lupa diisi oleh li membandingkan nol dengan nol. Isi setiap register sebelum dipakai sebagai pembanding.

Ringkasan#

  • Perulangan tingkat mesin adalah penghitung di register yang diuji instruksi branch mundur ke label awal badan perulangan.
  • Percabangan adalah instruksi branch yang membandingkan dua register, ditutup lompatan j menuju titik pertemuan agar cabang tidak saling tumpang-tindih.
  • Keluaran program dihasilkan ecall dengan nomor layanan pada a7, yaitu 1 untuk bilangan, 4 untuk string, 11 untuk karakter, dan 10 untuk keluar.
  • Program wajib berakhir dengan li a7, 10 dan ecall agar program counter tidak jatuh melewati instruksi terakhir.

Referensi#