diff options
author | Astatin <[email protected]> | 2024-09-24 15:01:58 +0900 |
---|---|---|
committer | Astatin <astatin@redacted> | 2024-09-24 15:01:58 +0900 |
commit | 748e8ba9e9afd67695ca62f3b691970f943071a6 (patch) | |
tree | 0bc707517ca95fe02e046a11616b18fbd685aa1f | |
parent | 1680bc5146b25d3631e6b7bfee3946ef59d906fd (diff) |
Add labels in go assembler
-rw-r--r-- | instructions.go | 9 | ||||
-rw-r--r-- | main.go | 104 | ||||
-rw-r--r-- | parameters.go | 47 | ||||
-rw-r--r-- | test.gbasm | 4 |
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 } @@ -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 } @@ -1,2 +1,4 @@ LD D, A -LD C, B +Test: + LD C, B + JP =Test |