mirror of
https://github.com/Asraelite/littlebigcomputer.git
synced 2025-07-17 16:16:51 +00:00
Fix V8 emulator bugs
This commit is contained in:
parent
c45ad79440
commit
476972f85a
29 changed files with 281 additions and 18 deletions
|
@ -39,6 +39,10 @@
|
||||||
<label for="config-raw-output-input">raw output: </label>
|
<label for="config-raw-output-input">raw output: </label>
|
||||||
<input type="checkbox" id="config-raw-output-input">
|
<input type="checkbox" id="config-raw-output-input">
|
||||||
</div>
|
</div>
|
||||||
|
<div id="config-focus-emulator">
|
||||||
|
<label for="config-focus-emulator-input">focus emulator: </label>
|
||||||
|
<input type="checkbox" id="config-focus-emulator-input">
|
||||||
|
</div>
|
||||||
<!-- <div id="config-line-numbers">
|
<!-- <div id="config-line-numbers">
|
||||||
<label for="config-line-numbers-input">line numbers: </label>
|
<label for="config-line-numbers-input">line numbers: </label>
|
||||||
<input type="checkbox" id="config-labels-input">
|
<input type="checkbox" id="config-labels-input">
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { ArchName, ArchSpecification, AssemblyInput, AssemblyOutput, Instruction
|
||||||
import { toNote } from './util.js';
|
import { toNote } from './util.js';
|
||||||
|
|
||||||
const CONFIG_VERSION: string = '1';
|
const CONFIG_VERSION: string = '1';
|
||||||
const EMULATOR_SPEED = 60;
|
const EMULATOR_SPEED = 30;
|
||||||
|
|
||||||
window.addEventListener('load', init);
|
window.addEventListener('load', init);
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ type ConfigurationSpecification = {
|
||||||
syntaxHighlighting: string;
|
syntaxHighlighting: string;
|
||||||
inlineSourceFormat: string;
|
inlineSourceFormat: string;
|
||||||
rawOutput: boolean;
|
rawOutput: boolean;
|
||||||
|
focusEmulator: boolean;
|
||||||
sendDelay: number;
|
sendDelay: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -62,6 +63,7 @@ class Configuration {
|
||||||
syntaxHighlighting: string;
|
syntaxHighlighting: string;
|
||||||
inlineSourceFormat: string;
|
inlineSourceFormat: string;
|
||||||
rawOutput: boolean;
|
rawOutput: boolean;
|
||||||
|
focusEmulator: boolean;
|
||||||
sendDelay: number;
|
sendDelay: number;
|
||||||
|
|
||||||
private formElement: HTMLFormElement;
|
private formElement: HTMLFormElement;
|
||||||
|
@ -73,6 +75,7 @@ class Configuration {
|
||||||
private targetArchDocsButton: HTMLButtonElement;
|
private targetArchDocsButton: HTMLButtonElement;
|
||||||
private syntaxHighlightingSelect: HTMLSelectElement;
|
private syntaxHighlightingSelect: HTMLSelectElement;
|
||||||
private rawOutputCheckbox: HTMLInputElement;
|
private rawOutputCheckbox: HTMLInputElement;
|
||||||
|
private focusEmulatorCheckbox: HTMLInputElement;
|
||||||
private sendDelayInput: HTMLInputElement;
|
private sendDelayInput: HTMLInputElement;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -85,6 +88,7 @@ class Configuration {
|
||||||
this.targetArchDocsButton = document.getElementById('config-target-arch-docs-button') as HTMLButtonElement;
|
this.targetArchDocsButton = document.getElementById('config-target-arch-docs-button') as HTMLButtonElement;
|
||||||
this.syntaxHighlightingSelect = document.getElementById('config-syntax-highlighting-select') as HTMLSelectElement;
|
this.syntaxHighlightingSelect = document.getElementById('config-syntax-highlighting-select') as HTMLSelectElement;
|
||||||
this.rawOutputCheckbox = document.getElementById('config-raw-output-input') as HTMLInputElement;
|
this.rawOutputCheckbox = document.getElementById('config-raw-output-input') as HTMLInputElement;
|
||||||
|
this.focusEmulatorCheckbox = document.getElementById('config-focus-emulator-input') as HTMLInputElement;
|
||||||
this.sendDelayInput = document.getElementById('control-send-speed') as HTMLInputElement;
|
this.sendDelayInput = document.getElementById('control-send-speed') as HTMLInputElement;
|
||||||
|
|
||||||
this.formElement.addEventListener('change', () => {
|
this.formElement.addEventListener('change', () => {
|
||||||
|
@ -115,10 +119,17 @@ class Configuration {
|
||||||
this.machineCodeShowLabels = this.labelsCheckbox.checked;
|
this.machineCodeShowLabels = this.labelsCheckbox.checked;
|
||||||
this.inlineSourceFormat = this.inlineSourceSelect.value;
|
this.inlineSourceFormat = this.inlineSourceSelect.value;
|
||||||
this.rawOutput = this.rawOutputCheckbox.checked;
|
this.rawOutput = this.rawOutputCheckbox.checked;
|
||||||
|
this.focusEmulator = this.focusEmulatorCheckbox.checked;
|
||||||
this.sendDelay = parseInt(this.sendDelayInput.value);
|
this.sendDelay = parseInt(this.sendDelayInput.value);
|
||||||
|
|
||||||
document.getElementById('assembly').className = `theme-${this.syntaxHighlighting}`;
|
document.getElementById('assembly').className = `theme-${this.syntaxHighlighting}`;
|
||||||
|
|
||||||
|
if (this.focusEmulator) {
|
||||||
|
document.getElementById('page').classList.add('emulator');
|
||||||
|
} else {
|
||||||
|
document.getElementById('page').classList.remove('emulator');
|
||||||
|
}
|
||||||
|
|
||||||
this.saveToLocalStorage();
|
this.saveToLocalStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,6 +150,7 @@ class Configuration {
|
||||||
configuration.targetArch = parsed.targetArch;
|
configuration.targetArch = parsed.targetArch;
|
||||||
configuration.syntaxHighlighting = parsed.syntaxHighlighting;
|
configuration.syntaxHighlighting = parsed.syntaxHighlighting;
|
||||||
configuration.inlineSourceFormat = parsed.inlineSourceFormat;
|
configuration.inlineSourceFormat = parsed.inlineSourceFormat;
|
||||||
|
configuration.focusEmulator = parsed.focusEmulator;
|
||||||
configuration.rawOutput = parsed.rawOutput;
|
configuration.rawOutput = parsed.rawOutput;
|
||||||
configuration.sendDelay = parsed.sendDelay;
|
configuration.sendDelay = parsed.sendDelay;
|
||||||
|
|
||||||
|
@ -149,6 +161,7 @@ class Configuration {
|
||||||
configuration.syntaxHighlightingSelect.value = configuration.syntaxHighlighting;
|
configuration.syntaxHighlightingSelect.value = configuration.syntaxHighlighting;
|
||||||
configuration.inlineSourceSelect.value = configuration.inlineSourceFormat;
|
configuration.inlineSourceSelect.value = configuration.inlineSourceFormat;
|
||||||
configuration.rawOutputCheckbox.checked = configuration.rawOutput;
|
configuration.rawOutputCheckbox.checked = configuration.rawOutput;
|
||||||
|
configuration.focusEmulatorCheckbox.checked = configuration.focusEmulator;
|
||||||
configuration.sendDelayInput.value = configuration.sendDelay.toString();
|
configuration.sendDelayInput.value = configuration.sendDelay.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,6 +182,7 @@ class Configuration {
|
||||||
inlineSourceFormat: this.inlineSourceFormat,
|
inlineSourceFormat: this.inlineSourceFormat,
|
||||||
syntaxHighlighting: this.syntaxHighlighting,
|
syntaxHighlighting: this.syntaxHighlighting,
|
||||||
rawOutput: this.rawOutput,
|
rawOutput: this.rawOutput,
|
||||||
|
focusEmulator: this.focusEmulator,
|
||||||
sendDelay: this.sendDelay,
|
sendDelay: this.sendDelay,
|
||||||
};
|
};
|
||||||
localStorage.setItem('configuration', JSON.stringify(values));
|
localStorage.setItem('configuration', JSON.stringify(values));
|
||||||
|
|
|
@ -35,6 +35,10 @@ input[type="checkbox"] {
|
||||||
"assembly-status machine-code-status";
|
"assembly-status machine-code-status";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#page.emulator {
|
||||||
|
grid-template-rows: 0fr 1fr 300px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
#page {
|
#page {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
@ -62,7 +66,7 @@ input[type="checkbox"] {
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"target-arch syntax-highlighting labels"
|
"target-arch syntax-highlighting labels"
|
||||||
"output-format source raw-output"
|
"output-format source raw-output"
|
||||||
"address-format none none";
|
"address-format breakpoints focus-emulator";
|
||||||
}
|
}
|
||||||
|
|
||||||
#controls>form>div {
|
#controls>form>div {
|
||||||
|
@ -81,6 +85,14 @@ input[type="checkbox"] {
|
||||||
grid-area: raw-output;
|
grid-area: raw-output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#config-focus-emulator {
|
||||||
|
grid-area: focus-emulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
#config-breakpoints {
|
||||||
|
grid-area: breakpoints;
|
||||||
|
}
|
||||||
|
|
||||||
#config-line-numbers {
|
#config-line-numbers {
|
||||||
grid-area: config-line-numbers;
|
grid-area: config-line-numbers;
|
||||||
}
|
}
|
||||||
|
|
|
@ -618,6 +618,7 @@ class ParvaEmulator implements Emulator {
|
||||||
screen: [] as Array<Array<number>>,
|
screen: [] as Array<Array<number>>,
|
||||||
screenBuffer: [] as Array<Array<number>>,
|
screenBuffer: [] as Array<Array<number>>,
|
||||||
};
|
};
|
||||||
|
previousFrameAccessedMemory: boolean = false;
|
||||||
|
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
|
@ -632,6 +633,12 @@ class ParvaEmulator implements Emulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
step() {
|
step() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
stepCore(): boolean {
|
||||||
|
let coreTerminates = false;
|
||||||
|
|
||||||
const instruction = this.memory[this.pc] ?? 0;
|
const instruction = this.memory[this.pc] ?? 0;
|
||||||
const bits = instruction.toString(2).padStart(24, '0');
|
const bits = instruction.toString(2).padStart(24, '0');
|
||||||
|
|
||||||
|
@ -715,6 +722,8 @@ class ParvaEmulator implements Emulator {
|
||||||
this.memory[address + 1] = valueD1;
|
this.memory[address + 1] = valueD1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.previousFrameAccessedMemory = true;
|
||||||
} else if (operationType === '11') {
|
} else if (operationType === '11') {
|
||||||
// branching
|
// branching
|
||||||
const special = condition.startsWith('11');
|
const special = condition.startsWith('11');
|
||||||
|
@ -747,6 +756,7 @@ class ParvaEmulator implements Emulator {
|
||||||
result = !result;
|
result = !result;
|
||||||
}
|
}
|
||||||
if (result) {
|
if (result) {
|
||||||
|
coreTerminates = true;
|
||||||
this.pc = targetAddress - 1;
|
this.pc = targetAddress - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -755,6 +765,7 @@ class ParvaEmulator implements Emulator {
|
||||||
this.pc &= 0xffffff;
|
this.pc &= 0xffffff;
|
||||||
this.cycle += 1;
|
this.cycle += 1;
|
||||||
this.cycle &= 0xffffff;
|
this.cycle &= 0xffffff;
|
||||||
|
return coreTerminates;
|
||||||
}
|
}
|
||||||
|
|
||||||
ioOut(device: number, command: number, valueA: number, valueB: number) {
|
ioOut(device: number, command: number, valueA: number, valueB: number) {
|
||||||
|
|
|
@ -440,16 +440,16 @@ class V8Emulator implements Emulator {
|
||||||
this.pc = this.memory[immediate] ?? 0;
|
this.pc = this.memory[immediate] ?? 0;
|
||||||
} else if (bits === '00000100') {
|
} else if (bits === '00000100') {
|
||||||
// jz imm
|
// jz imm
|
||||||
this.pc = this.registers[0] === 0 ? immediate : this.pc + 1;
|
this.pc = this.zeroFlag ? immediate : this.pc + 2;
|
||||||
} else if (bits === '00000101') {
|
} else if (bits === '00000101') {
|
||||||
// jnz imm
|
// jnz imm
|
||||||
this.pc = this.registers[0] !== 0 ? immediate : this.pc + 1;
|
this.pc = !this.zeroFlag ? immediate : this.pc + 2;
|
||||||
} else if (bits === '00000110') {
|
} else if (bits === '00000110') {
|
||||||
// jc imm
|
// jc imm
|
||||||
this.pc = this.carryFlag ? immediate : this.pc + 1;
|
this.pc = this.carryFlag ? immediate : this.pc + 2;
|
||||||
} else if (bits === '00000111') {
|
} else if (bits === '00000111') {
|
||||||
// jnc imm
|
// jnc imm
|
||||||
this.pc = !this.carryFlag ? immediate : this.pc + 1;
|
this.pc = !this.carryFlag ? immediate : this.pc + 2;
|
||||||
} else if (bits === '00001000') {
|
} else if (bits === '00001000') {
|
||||||
// jsr imm
|
// jsr imm
|
||||||
this.push(this.pc + 2);
|
this.push(this.pc + 2);
|
||||||
|
@ -486,16 +486,16 @@ class V8Emulator implements Emulator {
|
||||||
this.aluOp(operand, (x, a) => x + a + (this.carryFlag ? 1 : 0));
|
this.aluOp(operand, (x, a) => x + a + (this.carryFlag ? 1 : 0));
|
||||||
} else if (bits.startsWith('00011')) {
|
} else if (bits.startsWith('00011')) {
|
||||||
// inc
|
// inc
|
||||||
this.aluOp(operand, (x, a) => x + 1);
|
this.aluOp(operand, (x, a) => x + 1, false);
|
||||||
} else if (bits.startsWith('00100')) {
|
} else if (bits.startsWith('00100')) {
|
||||||
// sbc
|
// sbc
|
||||||
this.aluOp(operand, (x, a) => x - a - (this.carryFlag ? 1 : 0));
|
this.aluOp(operand, (x, a) => x - a - (this.carryFlag ? 1 : 0));
|
||||||
} else if (bits.startsWith('00101')) {
|
} else if (bits.startsWith('00101')) {
|
||||||
// dec
|
// dec
|
||||||
this.aluOp(operand, (x, a) => x - 1);
|
this.aluOp(operand, (x, a) => x - 1, false);
|
||||||
} else if (bits.startsWith('00110')) {
|
} else if (bits.startsWith('00110')) {
|
||||||
// not
|
// not
|
||||||
this.aluOp(operand, (x, a) => (~x) & 0xff);
|
this.aluOp(operand, (x, a) => (~x) & 0xff, false);
|
||||||
} else if (bits.startsWith('00111')) {
|
} else if (bits.startsWith('00111')) {
|
||||||
// xor
|
// xor
|
||||||
this.aluOp(operand, (x, a) => x ^ a);
|
this.aluOp(operand, (x, a) => x ^ a);
|
||||||
|
@ -519,11 +519,11 @@ class V8Emulator implements Emulator {
|
||||||
this.pc += 1;
|
this.pc += 1;
|
||||||
} else if (bits.startsWith('01100')) {
|
} else if (bits.startsWith('01100')) {
|
||||||
// rol
|
// rol
|
||||||
this.aluOp(operand, (x, a) => (x << 1) + (x >>> 7));
|
this.aluOp(operand, (x, a) => (x << 1) + (x >>> 7), false);
|
||||||
} else if (bits.startsWith('01101')) {
|
} else if (bits.startsWith('01101')) {
|
||||||
// ror
|
// ror
|
||||||
const initialValue = this.getRegister(operand);
|
const initialValue = this.getRegister(operand);
|
||||||
this.aluOp(operand, (x, a) => (x >>> 1) + ((x & 1) << 7));
|
this.aluOp(operand, (x, a) => (x >>> 1) + ((x & 1) << 7), false);
|
||||||
this.carryFlag = (initialValue & 1) === 1;
|
this.carryFlag = (initialValue & 1) === 1;
|
||||||
} else if (bits.startsWith('01110')) {
|
} else if (bits.startsWith('01110')) {
|
||||||
// cmp
|
// cmp
|
||||||
|
@ -575,13 +575,13 @@ class V8Emulator implements Emulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aluOp(register: string, fn: (x: number, a: number) => number) {
|
aluOp(register: string, fn: (x: number, a: number) => number, destinationIsA = true) {
|
||||||
const operandValue = this.getRegister(register);
|
const operandValue = this.getRegister(register);
|
||||||
const aValue = this.registers[0];
|
const aValue = this.registers[0];
|
||||||
const result = fn(operandValue, aValue);
|
const result = fn(operandValue, aValue);
|
||||||
this.zeroFlag = result === 0;
|
this.zeroFlag = result === 0;
|
||||||
this.carryFlag = result > 0xff;
|
this.carryFlag = (result & 0xff) !== result;
|
||||||
this.registers[0] = result & 0xff;
|
this.setRegister(destinationIsA ? '0' : register, result & 0xff);
|
||||||
this.pc += 1;
|
this.pc += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,8 +634,12 @@ class V8Emulator implements Emulator {
|
||||||
memoryString += ' ';
|
memoryString += ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const controlRegistersString = `<span style="background-color: #fcb55b">pc: ${this.pc.toString(16).padStart(2, '0')}</span>, `
|
||||||
|
+ `<span style="background-color: cyan">stack pointer: ${this.memory[STACK_POINTER_ADDRESS].toString(16).padStart(2, '0')}</span>, `
|
||||||
|
+ `carry flag: ${this.carryFlag ? 1 : 0}, `
|
||||||
|
+ `zero flag: ${this.zeroFlag ? 1 : 0}`;
|
||||||
return `
|
return `
|
||||||
<pre><span style="background-color: #fcb55b">pc: ${this.pc.toString(16).padStart(2, '0')}</span>,<span style="background-color: cyan"> stack pointer: ${this.memory[STACK_POINTER_ADDRESS].toString(16).padStart(2, '0')}</span></pre>
|
<pre>${controlRegistersString}</pre>
|
||||||
<pre>${registersString}</pre>
|
<pre>${registersString}</pre>
|
||||||
<pre>memory:\n${memoryString}</pre>
|
<pre>memory:\n${memoryString}</pre>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -166,19 +166,19 @@ blt x2, x3, 2
|
||||||
add x0, x0, x1
|
add x0, x0, x1
|
||||||
slli x1, x1, 6
|
slli x1, x1, 6
|
||||||
srli x2, x0, 6
|
srli x2, x0, 6
|
||||||
and x2, x0, 0o77
|
andi x2, x0, 0o77
|
||||||
blt x2, x3, end_score_change
|
blt x2, x3, end_score_change
|
||||||
add x0, x0, x1
|
add x0, x0, x1
|
||||||
|
|
||||||
slli x1, x1, 6
|
slli x1, x1, 6
|
||||||
srli x2, x0, 6
|
srli x2, x0, 6
|
||||||
and x2, x0, 0o77
|
andi x2, x0, 0o77
|
||||||
blt x2, x3, end_score_change
|
blt x2, x3, end_score_change
|
||||||
add x0, x0, x1
|
add x0, x0, x1
|
||||||
slli x1, x1, 6
|
slli x1, x1, 6
|
||||||
|
|
||||||
srli x2 x0, 6
|
srli x2 x0, 6
|
||||||
and x2, x0, 0o77
|
andi x2, x0, 0o77
|
||||||
blt x2, x3, end_score_change
|
blt x2, x3, end_score_change
|
||||||
add x0, x0, x1
|
add x0, x0, x1
|
||||||
|
|
9
programs/parva_0.2/alloc.parva
Normal file
9
programs/parva_0.2/alloc.parva
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# alloc
|
||||||
|
|
||||||
|
.export malloc, free, realloc
|
||||||
|
|
||||||
|
# a0 = size
|
||||||
|
# a1 = alignment
|
||||||
|
malloc:
|
||||||
|
|
||||||
|
|
11
programs/parva_0.2/boot.parva
Normal file
11
programs/parva_0.2/boot.parva
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# boot
|
||||||
|
|
||||||
|
.address 0
|
||||||
|
|
||||||
|
entry:
|
||||||
|
# todo: get bit representing if we're in supervisor mode and put in a0
|
||||||
|
beqz a0, user_mode_trap
|
||||||
|
b boot_sequence
|
||||||
|
user_mode_trap:
|
||||||
|
int
|
||||||
|
wfi
|
47
programs/parva_0.2/cat.parva
Normal file
47
programs/parva_0.2/cat.parva
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# cat
|
||||||
|
|
||||||
|
.extern f_open, f_close, f_read, write
|
||||||
|
.string_encoding system
|
||||||
|
|
||||||
|
main: # fn (args: [cstr8]) -> int
|
||||||
|
mv s0, a0
|
||||||
|
push a1
|
||||||
|
li s1, 0
|
||||||
|
|
||||||
|
arg_loop:
|
||||||
|
lw t0, 1(sp)
|
||||||
|
beq s1, t0, end
|
||||||
|
lw a0, 0(s0)
|
||||||
|
call f_open # a0 = file descriptor
|
||||||
|
bltz a0, error_open
|
||||||
|
push a0
|
||||||
|
call f_read # a0 = pointer to contents
|
||||||
|
bltz a0, error_read
|
||||||
|
mv a1, a0
|
||||||
|
li a0, 1 # stdout
|
||||||
|
call write
|
||||||
|
pop a0
|
||||||
|
call f_close
|
||||||
|
addi s1, s1, 1
|
||||||
|
b arg_loop
|
||||||
|
|
||||||
|
error_read:
|
||||||
|
pop zero
|
||||||
|
mv s0, a1
|
||||||
|
call f_close
|
||||||
|
mv a1, s0
|
||||||
|
error_open:
|
||||||
|
mv s0, a1
|
||||||
|
li a0, 1
|
||||||
|
li a1, error_message
|
||||||
|
call write
|
||||||
|
mv a1, s0
|
||||||
|
call write
|
||||||
|
b end
|
||||||
|
end:
|
||||||
|
li a0, 0
|
||||||
|
ret
|
||||||
|
|
||||||
|
error_message:
|
||||||
|
.string "\fr" # color red
|
||||||
|
.string "Error: \0"
|
104
programs/parva_0.2/hashmap.parva
Normal file
104
programs/parva_0.2/hashmap.parva
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
# hashmap
|
||||||
|
|
||||||
|
.extern malloc
|
||||||
|
.export hashmap__new
|
||||||
|
|
||||||
|
# structure:
|
||||||
|
# <entry count>
|
||||||
|
# <capacity magnitude> (capacity = 2 ^ capacity magnitude)
|
||||||
|
# {
|
||||||
|
# <hash>
|
||||||
|
# <value>
|
||||||
|
# } * 2^capacity
|
||||||
|
|
||||||
|
|
||||||
|
# Uses open addressing
|
||||||
|
|
||||||
|
|
||||||
|
# a0 = initial capacity magnitude
|
||||||
|
# returns: pointer to hashmap
|
||||||
|
hashmap__new:
|
||||||
|
push ra
|
||||||
|
push s0
|
||||||
|
mv s0, a0
|
||||||
|
li a0, 1
|
||||||
|
sll a0, a0, s0
|
||||||
|
addi a0, s0, 2
|
||||||
|
li a1, 0
|
||||||
|
call malloc
|
||||||
|
li t0, 0
|
||||||
|
zero_fill_loop:
|
||||||
|
addi t0, t0, 1
|
||||||
|
sw zero, [a0 + t0 + 2]
|
||||||
|
bne t0, s0 zero_fill_loop
|
||||||
|
|
||||||
|
sw zero, [a0 + s0 + 0]
|
||||||
|
sw s0, [a0 + s0 + 1]
|
||||||
|
pop s0
|
||||||
|
pop ra
|
||||||
|
ret
|
||||||
|
|
||||||
|
index_from_hash:
|
||||||
|
beqz a1, error
|
||||||
|
li t2, 1
|
||||||
|
lw a3, [a0 + 1]
|
||||||
|
sll t2, t2, a3
|
||||||
|
addi t2, t2, -1
|
||||||
|
and t1, a1, t2
|
||||||
|
add t1, t1, t1
|
||||||
|
jr t0
|
||||||
|
|
||||||
|
# a0 = pointer to hashmap
|
||||||
|
# a1 = 24-bit hash of key
|
||||||
|
# returns: value
|
||||||
|
hashmap__get:
|
||||||
|
jal t0, index_from_hash
|
||||||
|
get_loop:
|
||||||
|
lw t2, [a0 + t1 + 2] # t2 = hash of entry key
|
||||||
|
beq t2, a1, entry_found
|
||||||
|
beqz t2, entry_not_found
|
||||||
|
addi t1, t1, 2
|
||||||
|
b get_loop
|
||||||
|
entry_found:
|
||||||
|
lw a0, [a0 + t0 + 3]
|
||||||
|
ret
|
||||||
|
entry_not_found:
|
||||||
|
li a0, 0
|
||||||
|
ret
|
||||||
|
|
||||||
|
# a0 = pointer to hashmap
|
||||||
|
# a1 = 24-bit hash of key
|
||||||
|
# a2 = 24-bit value
|
||||||
|
# returns: pointer to value
|
||||||
|
hashmap__insert:
|
||||||
|
lw a3, [a0] # entry count
|
||||||
|
lw a4, [a0 + 1] # capacity magnitude
|
||||||
|
li t0, 1
|
||||||
|
addi a5, a4, -1
|
||||||
|
sll a5, t0, a5 # capacity / 2
|
||||||
|
ble a3, a5, insert_no_resize
|
||||||
|
slli a4, a4, 2 # capacity * 2
|
||||||
|
jal s0, resize
|
||||||
|
insert_no_resize:
|
||||||
|
jal t0, index_from_hash
|
||||||
|
add a3, a3, a3
|
||||||
|
insert_loop:
|
||||||
|
lw t2, [a0 + t1 + 2] # t2 = hash of entry key
|
||||||
|
addi t1, t1, 2
|
||||||
|
bnez t2 slot_found
|
||||||
|
|
||||||
|
|
||||||
|
beq t2, a1, entry_found
|
||||||
|
beqz t2, entry_not_found
|
||||||
|
addi t1, t1, 1
|
||||||
|
b insert_loop
|
||||||
|
|
||||||
|
# a4 = new capacity
|
||||||
|
resize:
|
||||||
|
push a0, a1, a2, a3, a4
|
||||||
|
mv a0, a4
|
||||||
|
mv s0, t0
|
||||||
|
call hashmap__new
|
||||||
|
pop t0, a1, a2, a3, a4
|
||||||
|
addi a4, a4, 1
|
||||||
|
sw a4, [a0]
|
47
programs/parva_0.2/terminal.parva
Normal file
47
programs/parva_0.2/terminal.parva
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# terminal
|
||||||
|
|
||||||
|
.extern f_open, f_close, f_read, write
|
||||||
|
.string_encoding system
|
||||||
|
|
||||||
|
main: # fn (args: [cstr8]) -> int
|
||||||
|
mv s0, a0
|
||||||
|
push a1
|
||||||
|
li s1, 0
|
||||||
|
|
||||||
|
arg_loop:
|
||||||
|
lw t0, 1(sp)
|
||||||
|
beq s1, t0, end
|
||||||
|
lw a0, 0(s0)
|
||||||
|
call f_open # a0 = file descriptor
|
||||||
|
bltz a0, error_open
|
||||||
|
push a0
|
||||||
|
call f_read # a0 = pointer to contents
|
||||||
|
bltz a0, error_read
|
||||||
|
mv a1, a0
|
||||||
|
li a0, 1 # stdout
|
||||||
|
call write
|
||||||
|
pop a0
|
||||||
|
call f_close
|
||||||
|
addi s1, s1, 1
|
||||||
|
b arg_loop
|
||||||
|
|
||||||
|
error_read:
|
||||||
|
pop zero
|
||||||
|
mv s0, a1
|
||||||
|
call f_close
|
||||||
|
mv a1, s0
|
||||||
|
error_open:
|
||||||
|
mv s0, a1
|
||||||
|
li a0, 1
|
||||||
|
li a1, error_message
|
||||||
|
call write
|
||||||
|
mv a1, s0
|
||||||
|
call write
|
||||||
|
b end
|
||||||
|
end:
|
||||||
|
li a0, 0
|
||||||
|
ret
|
||||||
|
|
||||||
|
error_message:
|
||||||
|
.string "\fr" # color red
|
||||||
|
.string "Error: \0"
|
Loading…
Add table
Add a link
Reference in a new issue