mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 15:28:29 +08:00
Fixed bugs in bin object code parsing and the assembler.
This commit is contained in:
parent
1da64b8632
commit
7d89c870cc
2
.gitignore
vendored
2
.gitignore
vendored
@ -26,3 +26,5 @@
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
# node error reports
|
||||||
|
report.*.json
|
||||||
|
@ -124,11 +124,11 @@ export function parse_bin(cursor: Cursor, lenient: boolean = false): BinFile {
|
|||||||
const label_offset_count = Math.floor((cursor.size - label_offset_table_offset) / 4);
|
const label_offset_count = Math.floor((cursor.size - label_offset_table_offset) / 4);
|
||||||
cursor.seek_start(label_offset_table_offset);
|
cursor.seek_start(label_offset_table_offset);
|
||||||
|
|
||||||
const label_offsets = cursor.i32_array(label_offset_count);
|
const label_offset_table = cursor.i32_array(label_offset_count);
|
||||||
const offset_to_labels = new Map<number, number[]>();
|
const offset_to_labels = new Map<number, number[]>();
|
||||||
|
|
||||||
for (let label = 0; label < label_offsets.length; label++) {
|
for (let label = 0; label < label_offset_table.length; label++) {
|
||||||
const offset = label_offsets[label];
|
const offset = label_offset_table[label];
|
||||||
|
|
||||||
if (offset !== -1) {
|
if (offset !== -1) {
|
||||||
let labels = offset_to_labels.get(offset);
|
let labels = offset_to_labels.get(offset);
|
||||||
@ -173,7 +173,7 @@ export function parse_bin(cursor: Cursor, lenient: boolean = false): BinFile {
|
|||||||
|
|
||||||
// Verify labels.
|
// Verify labels.
|
||||||
outer: for (let label = 0; label < label_offset_count; label++) {
|
outer: for (let label = 0; label < label_offset_count; label++) {
|
||||||
if (label_offsets[label] !== -1) {
|
if (label_offset_table[label] !== -1) {
|
||||||
for (const segment of segments) {
|
for (const segment of segments) {
|
||||||
if (segment.label === label) {
|
if (segment.label === label) {
|
||||||
continue outer;
|
continue outer;
|
||||||
@ -181,7 +181,7 @@ export function parse_bin(cursor: Cursor, lenient: boolean = false): BinFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`Label ${label} with offset ${label_offsets[label]} does not point to anything.`
|
`Label ${label} with offset ${label_offset_table[label]} does not point to anything.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,34 +265,38 @@ function parse_object_code(
|
|||||||
const labels: number[] | undefined = offset_to_labels.get(offset);
|
const labels: number[] | undefined = offset_to_labels.get(offset);
|
||||||
|
|
||||||
// Check whether we've encountered a data segment.
|
// Check whether we've encountered a data segment.
|
||||||
// If a single label that points to this segment is referred to from a data context we assume the segment is a data segment.
|
// If a label that points to this segment is referred to from a data context we assume the segment is a data segment.
|
||||||
if (labels && labels.some(label => data_labels.has(label))) {
|
if (labels && labels.some(label => data_labels.has(label))) {
|
||||||
for (const [label_offset, labels] of offset_to_labels.entries()) {
|
let last_label = -1;
|
||||||
if (label_offset > offset) {
|
let data_segment_size = cursor.size - offset;
|
||||||
|
|
||||||
|
// Get the next label's offset.
|
||||||
|
for (let i = offset + 1; i < cursor.size; i++) {
|
||||||
|
if (offset_to_labels.has(i)) {
|
||||||
// We create empty segments for all but the last label.
|
// We create empty segments for all but the last label.
|
||||||
// The data will be in the last label's segment.
|
// The data will be in the last label's segment.
|
||||||
for (let i = 0; i < labels.length - 1; i++) {
|
for (let j = 0; j < labels.length - 1; j++) {
|
||||||
segments.push({
|
segments.push({
|
||||||
type: SegmentType.Data,
|
type: SegmentType.Data,
|
||||||
label: labels[i],
|
label: labels[j],
|
||||||
data: new ArrayBuffer(0),
|
data: new ArrayBuffer(0),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
segments.push({
|
last_label = labels[labels.length - 1];
|
||||||
type: SegmentType.Data,
|
data_segment_size = i - offset;
|
||||||
label: labels[labels.length - 1],
|
|
||||||
data: cursor.array_buffer(label_offset - offset),
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instructions = undefined;
|
segments.push({
|
||||||
continue;
|
type: SegmentType.Data,
|
||||||
}
|
label: last_label,
|
||||||
|
data: cursor.array_buffer(data_segment_size),
|
||||||
|
});
|
||||||
|
|
||||||
|
instructions = undefined;
|
||||||
|
} else {
|
||||||
// Parse as instruction.
|
// Parse as instruction.
|
||||||
if (labels == undefined) {
|
if (labels == undefined) {
|
||||||
if (instructions == undefined) {
|
if (instructions == undefined) {
|
||||||
@ -366,6 +370,7 @@ function parse_object_code(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (lenient) {
|
if (lenient) {
|
||||||
logger.error("Couldn't fully parse object code.", e);
|
logger.error("Couldn't fully parse object code.", e);
|
||||||
|
@ -76,13 +76,9 @@ class Assembler {
|
|||||||
for (const line of this.assembly) {
|
for (const line of this.assembly) {
|
||||||
this.tokens = this.lexer.tokenize_line(line);
|
this.tokens = this.lexer.tokenize_line(line);
|
||||||
|
|
||||||
if (this.tokens.length === 0) {
|
if (this.tokens.length > 0) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = this.tokens.shift()!;
|
const token = this.tokens.shift()!;
|
||||||
|
|
||||||
if (this.code_section) {
|
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
case TokenType.Label:
|
case TokenType.Label:
|
||||||
this.parse_label(token);
|
this.parse_label(token);
|
||||||
@ -93,8 +89,27 @@ class Assembler {
|
|||||||
case TokenType.DataSection:
|
case TokenType.DataSection:
|
||||||
this.parse_data_section(token);
|
this.parse_data_section(token);
|
||||||
break;
|
break;
|
||||||
|
case TokenType.Int:
|
||||||
|
if (this.code_section) {
|
||||||
|
this.add_error({
|
||||||
|
col: token.col,
|
||||||
|
length: token.len,
|
||||||
|
message: "Unexpected token.",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.parse_bytes(token);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TokenType.Ident:
|
case TokenType.Ident:
|
||||||
|
if (this.code_section) {
|
||||||
this.parse_instruction(token);
|
this.parse_instruction(token);
|
||||||
|
} else {
|
||||||
|
this.add_error({
|
||||||
|
col: token.col,
|
||||||
|
length: token.len,
|
||||||
|
message: "Unexpected token.",
|
||||||
|
});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TokenType.InvalidSection:
|
case TokenType.InvalidSection:
|
||||||
this.add_error({
|
this.add_error({
|
||||||
@ -118,15 +133,6 @@ class Assembler {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
switch (token.type) {
|
|
||||||
case TokenType.Label:
|
|
||||||
this.parse_label(token);
|
|
||||||
break;
|
|
||||||
case TokenType.Int:
|
|
||||||
this.parse_bytes(token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.line_no++;
|
this.line_no++;
|
||||||
@ -425,7 +431,7 @@ class Assembler {
|
|||||||
this.add_error({
|
this.add_error({
|
||||||
col: token.col,
|
col: token.col,
|
||||||
length: token.len,
|
length: token.len,
|
||||||
message: "Argument expected.",
|
message: "Expected an argument.",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (param.type !== Type.U8Var && param.type !== Type.ILabelVar) {
|
if (param.type !== Type.U8Var && param.type !== Type.ILabelVar) {
|
||||||
@ -442,7 +448,7 @@ class Assembler {
|
|||||||
this.add_error({
|
this.add_error({
|
||||||
col,
|
col,
|
||||||
length: token.col - col,
|
length: token.col - col,
|
||||||
message: "Comma expected.",
|
message: "Expected a comma.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,37 +509,37 @@ class Assembler {
|
|||||||
|
|
||||||
switch (param.type) {
|
switch (param.type) {
|
||||||
case Type.U8:
|
case Type.U8:
|
||||||
type_str = "unsigned 8-bit integer";
|
type_str = "an unsigned 8-bit integer";
|
||||||
break;
|
break;
|
||||||
case Type.U16:
|
case Type.U16:
|
||||||
type_str = "unsigned 16-bit integer";
|
type_str = "an unsigned 16-bit integer";
|
||||||
break;
|
break;
|
||||||
case Type.U32:
|
case Type.U32:
|
||||||
type_str = "unsigned 32-bit integer";
|
type_str = "an unsigned 32-bit integer";
|
||||||
break;
|
break;
|
||||||
case Type.I32:
|
case Type.I32:
|
||||||
type_str = "signed 32-bit integer";
|
type_str = "a signed 32-bit integer";
|
||||||
break;
|
break;
|
||||||
case Type.F32:
|
case Type.F32:
|
||||||
type_str = "float";
|
type_str = "a float";
|
||||||
break;
|
break;
|
||||||
case Type.Register:
|
case Type.Register:
|
||||||
type_str = "register reference";
|
type_str = "a register reference";
|
||||||
break;
|
break;
|
||||||
case Type.ILabel:
|
case Type.ILabel:
|
||||||
type_str = "instruction label";
|
type_str = "an instruction label";
|
||||||
break;
|
break;
|
||||||
case Type.DLabel:
|
case Type.DLabel:
|
||||||
type_str = "data label";
|
type_str = "a data label";
|
||||||
break;
|
break;
|
||||||
case Type.U8Var:
|
case Type.U8Var:
|
||||||
type_str = "unsigned 8-bit integer";
|
type_str = "an unsigned 8-bit integer";
|
||||||
break;
|
break;
|
||||||
case Type.ILabelVar:
|
case Type.ILabelVar:
|
||||||
type_str = "instruction label";
|
type_str = "an instruction label";
|
||||||
break;
|
break;
|
||||||
case Type.String:
|
case Type.String:
|
||||||
type_str = "string";
|
type_str = "a string";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,13 +564,13 @@ class Assembler {
|
|||||||
this.add_error({
|
this.add_error({
|
||||||
col,
|
col,
|
||||||
length: len,
|
length: len,
|
||||||
message: `${bit_size}-Bit unsigned integer can't be less than 0.`,
|
message: `Unsigned ${bit_size}-bit integer can't be less than 0.`,
|
||||||
});
|
});
|
||||||
} else if (value > max_value) {
|
} else if (value > max_value) {
|
||||||
this.add_error({
|
this.add_error({
|
||||||
col,
|
col,
|
||||||
length: len,
|
length: len,
|
||||||
message: `${bit_size}-Bit unsigned integer can't be greater than ${max_value}.`,
|
message: `Unsigned ${bit_size}-bit integer can't be greater than ${max_value}.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,13 +589,13 @@ class Assembler {
|
|||||||
this.add_error({
|
this.add_error({
|
||||||
col,
|
col,
|
||||||
length: len,
|
length: len,
|
||||||
message: `${bit_size}-Bit signed integer can't be less than ${min_value}.`,
|
message: `Signed ${bit_size}-bit integer can't be less than ${min_value}.`,
|
||||||
});
|
});
|
||||||
} else if (value > max_value) {
|
} else if (value > max_value) {
|
||||||
this.add_error({
|
this.add_error({
|
||||||
col,
|
col,
|
||||||
length: len,
|
length: len,
|
||||||
message: `${bit_size}-Bit signed integer can't be greater than ${max_value}.`,
|
message: `Signed ${bit_size}-bit integer can't be greater than ${max_value}.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,13 +630,13 @@ class Assembler {
|
|||||||
this.add_error({
|
this.add_error({
|
||||||
col: token.col,
|
col: token.col,
|
||||||
length: token.len,
|
length: token.len,
|
||||||
message: `8-Bit unsigned integer can't be less than 0.`,
|
message: "Unsigned 8-bit integer can't be less than 0.",
|
||||||
});
|
});
|
||||||
} else if (token.value > 255) {
|
} else if (token.value > 255) {
|
||||||
this.add_error({
|
this.add_error({
|
||||||
col: token.col,
|
col: token.col,
|
||||||
length: token.len,
|
length: token.len,
|
||||||
message: `8-Bit unsigned integer can't be greater than 255.`,
|
message: "Unsigned 8-bit integer can't be greater than 255.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,7 +653,7 @@ class Assembler {
|
|||||||
this.add_error({
|
this.add_error({
|
||||||
col: token.col,
|
col: token.col,
|
||||||
length: token.len,
|
length: token.len,
|
||||||
message: "Unexpected token.",
|
message: "Expected an unsigned 8-bit integer.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,12 +12,22 @@ export function disassemble(object_code: Segment[], manual_stack: boolean = fals
|
|||||||
if (segment.type === SegmentType.Data) {
|
if (segment.type === SegmentType.Data) {
|
||||||
if (code_block !== false) {
|
if (code_block !== false) {
|
||||||
code_block = false;
|
code_block = false;
|
||||||
lines.push(".data");
|
|
||||||
|
if (lines.length) {
|
||||||
|
lines.push("");
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push(".data", "");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (code_block !== true) {
|
if (code_block !== true) {
|
||||||
code_block = true;
|
code_block = true;
|
||||||
lines.push(".code");
|
|
||||||
|
if (lines.length) {
|
||||||
|
lines.push("");
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push(".code", "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,10 +13,14 @@ const ASM_SYNTAX: languages.IMonarchLanguage = {
|
|||||||
|
|
||||||
tokenizer: {
|
tokenizer: {
|
||||||
root: [
|
root: [
|
||||||
|
// Strings.
|
||||||
|
[/"([^"\\]|\\.)*$/, "string.invalid"], // Unterminated string.
|
||||||
|
[/"/, { token: "string.quote", bracket: "@open", next: "@string" }],
|
||||||
|
|
||||||
// Registers.
|
// Registers.
|
||||||
[/r\d+/, "predefined"],
|
[/r\d+/, "predefined"],
|
||||||
|
|
||||||
[/\.[^\s]+|(^|\s+)bytes($|\s+)/, "keyword"],
|
[/\.[^\s]+/, "keyword"],
|
||||||
|
|
||||||
// Labels.
|
// Labels.
|
||||||
[/[^\s]+:/, "tag"],
|
[/[^\s]+:/, "tag"],
|
||||||
@ -36,10 +40,6 @@ const ASM_SYNTAX: languages.IMonarchLanguage = {
|
|||||||
|
|
||||||
// Delimiters.
|
// Delimiters.
|
||||||
[/,/, "delimiter"],
|
[/,/, "delimiter"],
|
||||||
|
|
||||||
// Strings.
|
|
||||||
[/"([^"\\]|\\.)*$/, "string.invalid"], // Unterminated string.
|
|
||||||
[/"/, { token: "string.quote", bracket: "@open", next: "@string" }],
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// comment: [
|
// comment: [
|
||||||
@ -66,6 +66,19 @@ const INSTRUCTION_SUGGESTIONS = OPCODES.filter(opcode => opcode != null).map(opc
|
|||||||
} as any) as languages.CompletionItem;
|
} as any) as languages.CompletionItem;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const KEYWORD_SUGGESTIONS = [
|
||||||
|
{
|
||||||
|
label: ".code",
|
||||||
|
kind: languages.CompletionItemKind.Keyword,
|
||||||
|
insertText: "code",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: ".data",
|
||||||
|
kind: languages.CompletionItemKind.Keyword,
|
||||||
|
insertText: "data",
|
||||||
|
},
|
||||||
|
] as languages.CompletionItem[];
|
||||||
|
|
||||||
languages.register({ id: "psoasm" });
|
languages.register({ id: "psoasm" });
|
||||||
languages.setMonarchTokensProvider("psoasm", ASM_SYNTAX);
|
languages.setMonarchTokensProvider("psoasm", ASM_SYNTAX);
|
||||||
languages.registerCompletionItemProvider("psoasm", {
|
languages.registerCompletionItemProvider("psoasm", {
|
||||||
@ -78,6 +91,8 @@ languages.registerCompletionItemProvider("psoasm", {
|
|||||||
});
|
});
|
||||||
const suggestions = /^\s*([a-z][a-z0-9_=<>!]*)?$/.test(value)
|
const suggestions = /^\s*([a-z][a-z0-9_=<>!]*)?$/.test(value)
|
||||||
? INSTRUCTION_SUGGESTIONS
|
? INSTRUCTION_SUGGESTIONS
|
||||||
|
: /^\s*\.[a-z]+$/.test(value)
|
||||||
|
? KEYWORD_SUGGESTIONS
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
Loading…
Reference in New Issue
Block a user