aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2024-09-24 15:01:58 +0900
committerAstatin <astatin@redacted>2024-09-24 15:01:58 +0900
commit748e8ba9e9afd67695ca62f3b691970f943071a6 (patch)
tree0bc707517ca95fe02e046a11616b18fbd685aa1f
parent1680bc5146b25d3631e6b7bfee3946ef59d906fd (diff)
Add labels in go assembler
-rw-r--r--instructions.go9
-rw-r--r--main.go104
-rw-r--r--parameters.go47
-rw-r--r--test.gbasm4
4 files changed, 131 insertions, 33 deletions
diff --git a/instructions.go b/instructions.go
index e78dbab..32e2794 100644
--- a/instructions.go
+++ b/instructions.go
@@ -5,7 +5,7 @@ import (
"strings"
)
-type ParamType func(param string) (uint16, error)
+type ParamType func(state *ProgramState, param string) (uint16, error)
type InstructionParams struct {
Types []ParamType
@@ -392,7 +392,10 @@ func InstructionSetNew() InstructionSet {
return result
}
-func (set InstructionSet) Parse(line string) ([]byte, error) {
+func (set InstructionSet) Parse(
+ state *ProgramState,
+ line string,
+) ([]byte, error) {
words := strings.Fields(strings.ReplaceAll(strings.Trim(line, " \t\n"), ",", " "))
if len(words) < 1 {
@@ -414,7 +417,7 @@ instruction_param_loop:
parsed_params := make([]uint16, len(params))
for i, paramType := range instrParam.Types {
- parsed, err := paramType(params[i])
+ parsed, err := paramType(state, params[i])
if err != nil {
continue instruction_param_loop
}
diff --git a/main.go b/main.go
index 101e8d7..b4fa941 100644
--- a/main.go
+++ b/main.go
@@ -7,9 +7,88 @@ import (
"strings"
)
+type ProgramState struct {
+ Labels map[string]uint
+ Defs map[string]any
+}
+
+func parseFile(input_file_name string, input []byte) ([]byte, error) {
+ lines := strings.Split(string(input), "\n")
+
+ state := ProgramState{
+ Labels: make(map[string]uint),
+ Defs: make(map[string]any),
+ }
+
+ label_line_number := uint(0)
+ for line_nb, line := range lines {
+ is_label_defined := strings.Contains(line, ":")
+
+ if is_label_defined {
+ parts := strings.Split(line, ":")
+ for _, label := range parts[:len(parts)-1] {
+ label = strings.ToUpper(label)
+ if _, ok := state.Labels[label]; ok {
+ fmt.Fprintf(
+ os.Stderr,
+ "File %s, line %d:\nLabel %s is already defined",
+ input_file_name,
+ line_nb,
+ label,
+ )
+ os.Exit(1)
+ }
+ state.Labels[label] = label_line_number
+ }
+
+ line = parts[len(parts)-1]
+ }
+
+ line = strings.TrimSpace(line)
+
+ next_instruction, err := Instructions.Parse(nil, line)
+ if err != nil {
+ return nil, fmt.Errorf(
+ "File %s, line %d (1st pass):\n%w",
+ input_file_name,
+ line_nb,
+ err,
+ )
+ }
+
+ // TODO: Handle the case of program bigger than MBC (or maybe do it directly in the parameters)
+ label_line_number += uint(len(next_instruction))
+ }
+
+ result := []byte{}
+ for line_nb, line := range lines {
+ is_label_defined := strings.Contains(line, ":")
+
+ if is_label_defined {
+ parts := strings.Split(line, ":")
+
+ line = parts[len(parts)-1]
+ }
+
+ next_instruction, err := Instructions.Parse(&state, line)
+ if err != nil {
+ return nil, fmt.Errorf(
+ "File %s, line %d (2nd pass): %w",
+ input_file_name,
+ line_nb,
+ err,
+ )
+ }
+
+ result = append(result, next_instruction...)
+ }
+
+ return result, nil
+}
+
func main() {
if len(os.Args) != 3 {
- fmt.Fprintf(os.Stderr, "Usage: gbasm [input_file] [output_file]")
+ fmt.Fprintf(os.Stderr, "Usage: gbasm [input_file] [output_file]\n")
os.Exit(1)
}
@@ -18,38 +97,31 @@ func main() {
input_file, err := os.Open(input_file_name)
if err != nil {
- fmt.Fprintf(os.Stderr, "Error while opening input file: %s", err.Error())
+ fmt.Fprintf(os.Stderr, "Error while opening input file: %s\n", err.Error())
os.Exit(1)
}
input, err := io.ReadAll(input_file)
if err != nil {
- fmt.Fprintf(os.Stderr, "Error while reading input file: %s", err.Error())
+ fmt.Fprintf(os.Stderr, "Error while reading input file: %s\n", err.Error())
os.Exit(1)
}
- lines := strings.Split(string(input), "\n")
-
- result := []byte{}
- for _, line := range lines {
- next_instruction, err := Instructions.Parse(line)
- if err != nil {
- fmt.Fprintf(os.Stderr, "%s", err.Error())
- os.Exit(1)
- }
-
- result = append(result, next_instruction...)
+ result, err := parseFile(input_file_name, input)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
+ os.Exit(1)
}
output_file, err := os.Create(output_file_name)
if err != nil {
- fmt.Fprintf(os.Stderr, "Error while opening output file: %s", err.Error())
+ fmt.Fprintf(os.Stderr, "Error while opening output file: %s\n", err.Error())
os.Exit(1)
}
_, err = output_file.Write(result)
if err != nil {
- fmt.Fprintf(os.Stderr, "Error while writing to output file: %s", err.Error())
+ fmt.Fprintf(os.Stderr, "Error while writing to output file: %s\n", err.Error())
os.Exit(1)
}
}
diff --git a/parameters.go b/parameters.go
index 665c551..02991cf 100644
--- a/parameters.go
+++ b/parameters.go
@@ -3,9 +3,10 @@ package main
import (
"fmt"
"strconv"
+ "strings"
)
-func Reg8(param string) (uint16, error) {
+func Reg8(_ *ProgramState, param string) (uint16, error) {
switch param {
case "A":
return 7, nil
@@ -27,35 +28,35 @@ func Reg8(param string) (uint16, error) {
return 0, fmt.Errorf("Invalid reg8")
}
-func A(param string) (uint16, error) {
+func A(_ *ProgramState, param string) (uint16, error) {
if param == "A" {
return 0, nil
}
return 0, fmt.Errorf("Invalid A")
}
-func HL(param string) (uint16, error) {
+func HL(_ *ProgramState, param string) (uint16, error) {
if param == "HL" {
return 0, nil
}
return 0, fmt.Errorf("Invalid HL")
}
-func SP(param string) (uint16, error) {
+func SP(_ *ProgramState, param string) (uint16, error) {
if param == "SP" {
return 0, nil
}
return 0, fmt.Errorf("Invalid SP")
}
-func IndirectC(param string) (uint16, error) {
+func IndirectC(_ *ProgramState, param string) (uint16, error) {
if param == "(C)" {
return 0, nil
}
return 0, fmt.Errorf("Invalid (C)")
}
-func Reg16(param string) (uint16, error) {
+func Reg16(_ *ProgramState, param string) (uint16, error) {
switch param {
case "BC":
return 0, nil
@@ -72,17 +73,37 @@ func Reg16(param string) (uint16, error) {
return 0, fmt.Errorf("Invalid reg16")
}
-func Raw8(param string) (uint16, error) {
+func Raw8(_ *ProgramState, param string) (uint16, error) {
res, err := strconv.ParseInt(param, 0, 8)
return uint16(res), err
}
-func Raw16(param string) (uint16, error) {
+func Raw16(state *ProgramState, param string) (uint16, error) {
res, err := strconv.ParseInt(param, 0, 16)
+
+ if strings.HasPrefix(param, "=") {
+ if state == nil {
+ return 0, nil
+ }
+
+ label := strings.ToUpper(strings.TrimPrefix(param, "="))
+ labelValue, ok := state.Labels[label]
+ if !ok {
+ return 0, fmt.Errorf("Label \"%s\" not found", label)
+ }
+
+ // TODO: Manage when multiple MBC
+ if labelValue > 0x8000 {
+ panic("Switchable ROM banks are not implemented yet")
+ }
+
+ return uint16(labelValue), nil
+ }
+
return uint16(res), err
}
-func Reg16Indirect(param string) (uint16, error) {
+func Reg16Indirect(_ *ProgramState, param string) (uint16, error) {
switch param {
case "(BC)":
return 0, nil
@@ -96,7 +117,7 @@ func Reg16Indirect(param string) (uint16, error) {
return 0, fmt.Errorf("Invalid reg16 indirect")
}
-func Raw8Indirect(param string) (uint16, error) {
+func Raw8Indirect(_ *ProgramState, param string) (uint16, error) {
if len(param) < 2 || param[0] != '(' || param[len(param)-1] != ')' {
return 0, fmt.Errorf("Invalid raw8indirect")
}
@@ -105,7 +126,7 @@ func Raw8Indirect(param string) (uint16, error) {
return uint16(res), err
}
-func Raw16Indirect(param string) (uint16, error) {
+func Raw16Indirect(_ *ProgramState, param string) (uint16, error) {
if len(param) < 2 || param[0] != '(' || param[len(param)-1] != ')' {
return 0, fmt.Errorf("Invalid raw16indirect")
}
@@ -114,7 +135,7 @@ func Raw16Indirect(param string) (uint16, error) {
return uint16(res), err
}
-func Condition(param string) (uint16, error) {
+func Condition(_ *ProgramState, param string) (uint16, error) {
switch param {
case "NZ":
return 0, nil
@@ -128,7 +149,7 @@ func Condition(param string) (uint16, error) {
return 0, fmt.Errorf("Invalid condition")
}
-func BitOrdinal(param string) (uint16, error) {
+func BitOrdinal(_ *ProgramState, param string) (uint16, error) {
res, err := strconv.ParseInt(param, 0, 3)
return uint16(res), err
}
diff --git a/test.gbasm b/test.gbasm
index f64992a..2e71378 100644
--- a/test.gbasm
+++ b/test.gbasm
@@ -1,2 +1,4 @@
LD D, A
-LD C, B
+Test:
+ LD C, B
+ JP =Test