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.
#!/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()Pemakaian: python3 sim.py [--trace] program.sSetiap 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.
# 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# menjalankan program penjumlahan
python3 sim.py jumlah.s55Angka 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.
# 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# menjalankan program klasifikasi nilai
python3 sim.py nilai.scukup baikNilai 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, 10danecall, 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 padaa0, sehingga melewatkanla a0, labelmembuat ecall membaca alamat yang salah. Muat alamatnya terlebih dahulu. - Lupa
jdi 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, labelpada register yang lupa diisi olehlimembandingkan 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
jmenuju 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, 10danecallagar program counter tidak jatuh melewati instruksi terakhir.
Referensi#
- RARS — TheThirdOne — Simulator RISC-V pendidikan yang menjadi acuan layanan ecall dan gaya penulisan program pada sim.py.
- RISC-V Instruction Set Manual — RISC-V International — Daftar instruksi branch RV32I beserta semantik perbandingan bertanda dan tanpa tanda.