diff options
author | Astatin <[email protected]> | 2025-01-07 17:24:46 +0900 |
---|---|---|
committer | Astatin <[email protected]> | 2025-01-07 17:24:46 +0900 |
commit | c2fb41b27cb4294520cd73aa3d81d51d0f969bf8 (patch) | |
tree | 187caf2f08e3e9625daa18146c0798ea491f84ac | |
parent | b5b9637ff2d222841632b8644b94835ec1c93359 (diff) |
Add support for relative labels in parameters
-rw-r--r-- | instructions.go | 5 | ||||
-rw-r--r-- | macros.go | 10 | ||||
-rw-r--r-- | main.go | 116 | ||||
-rw-r--r-- | parameters.go | 75 |
4 files changed, 134 insertions, 72 deletions
diff --git a/instructions.go b/instructions.go index ddcb833..ca3df54 100644 --- a/instructions.go +++ b/instructions.go @@ -5,7 +5,7 @@ import ( "strings" ) -type ParamType func(labels *Labels, defs *Definitions, param string) (uint16, error) +type ParamType func(labels *Labels, lastAbsoluteLabel string, defs *Definitions, param string) (uint16, error) type InstructionParams struct { Types []ParamType @@ -506,6 +506,7 @@ func (set InstructionSet) Parse( isMacro bool, isFirstPass bool, currentAddress uint16, + lastAbsoluteLabel string, line string, ) ([]byte, error) { words := strings.Fields(strings.ReplaceAll(strings.Trim(line, " \t\n"), ",", " ")) @@ -541,7 +542,7 @@ instruction_param_loop: if isFirstPass && !instrParam.LabelsBeforeOnly { accessibleLabels = nil } - parsed, err := paramType(accessibleLabels, defs, params[i]) + parsed, err := paramType(accessibleLabels, lastAbsoluteLabel, defs, params[i]) if err != nil { continue instruction_param_loop } @@ -87,6 +87,7 @@ func MacroParse( lineNb *int, isFirstPass bool, offset uint, + LastAbsoluteLabel string, ) error { words := strings.Split(line, " ") if len(words) == 0 { @@ -102,6 +103,7 @@ func MacroParse( state.IsMacro, isFirstPass, uint16(uint(len(*result))+offset), + LastAbsoluteLabel, line, ) if err != nil { @@ -149,13 +151,13 @@ func MacroParse( } var definedValue any - if v, err := Raw8Indirect(&state.Labels, &state.Defs, words[2]); err == nil { + if v, err := Raw8Indirect(&state.Labels, LastAbsoluteLabel, &state.Defs, words[2]); err == nil { definedValue = Indirect8b(v) - } else if v, err := Raw16Indirect(&state.Labels, &state.Defs, words[2]); err == nil { + } else if v, err := Raw16Indirect(&state.Labels, LastAbsoluteLabel, &state.Defs, words[2]); err == nil { definedValue = Indirect16b(v) - } else if v, err := Raw8(&state.Labels, &state.Defs, words[2]); err == nil { + } else if v, err := Raw8(&state.Labels, LastAbsoluteLabel, &state.Defs, words[2]); err == nil { definedValue = Raw8b(v) - } else if v, err := Raw16(&state.Labels, &state.Defs, words[2]); err == nil { + } else if v, err := Raw16(&state.Labels, LastAbsoluteLabel, &state.Defs, words[2]); err == nil { definedValue = Raw16b(v) } else { return fmt.Errorf("\"%s\" could not be parsed as a .DEFINE argument", words[2]) @@ -19,38 +19,38 @@ type ProgramState struct { IsMacro bool } -func parseFile(input_file_name string, input []byte, offset uint) ([]byte, error) { +func parseFile(inputFileName string, input []byte, offset uint) ([]byte, error) { state := ProgramState{ Labels: make(map[string]uint), Defs: make(map[string]any), IsMacro: false, } - _, err := firstPass(input_file_name, input, offset, &state) + _, err := firstPass(inputFileName, input, offset, &state) if err != nil { return nil, err } - return secondPass(input_file_name, input, offset, state) + return secondPass(inputFileName, input, offset, state) } func firstPass( - input_file_name string, + inputFileName string, input []byte, offset uint, state *ProgramState, ) ([]byte, error) { lines := strings.Split(string(input), "\n") - line_nb := 0 + lineNb := 0 result := []byte{} lastAbsoluteLabel := "" - for line_nb < len(lines) { - line := lines[line_nb] - line_parts := strings.Split(line, ";") - line = line_parts[0] - is_label_defined := strings.Contains(line, ":") + for lineNb < len(lines) { + line := lines[lineNb] + lineParts := strings.Split(line, ";") + line = lineParts[0] + isLabelDefined := strings.Contains(line, ":") - if is_label_defined { + if isLabelDefined { parts := strings.Split(line, ":") for _, label := range parts[:len(parts)-1] { label = strings.TrimSpace(strings.ToUpper(label)) @@ -58,8 +58,8 @@ func firstPass( if !isCharsetAllowed { return nil, fmt.Errorf( "File %s, line %d:\nLabel \"%s\" contains special characters. Only alphanumeric, dashes and underscores are allowed", - input_file_name, - line_nb+1, + inputFileName, + lineNb+1, label, ) } @@ -85,8 +85,8 @@ func firstPass( if _, ok := state.Labels[label]; ok { return nil, fmt.Errorf( "File %s, line %d:\nLabel %s is already defined", - input_file_name, - line_nb+1, + inputFileName, + lineNb+1, label, ) } @@ -108,82 +108,106 @@ func firstPass( // nil sets all the labels and defintion to 0 & thus, to not crash JR, the currentAddress should also be 0 if strings.HasPrefix(line, ".") { - err := MacroParse(line, lines, &result, state, &line_nb, true, offset) + err := MacroParse(line, lines, &result, state, &lineNb, true, offset, lastAbsoluteLabel) if err != nil { return nil, fmt.Errorf( "File %s, line %d (1st pass|macro):\n%w", - input_file_name, - line_nb+1, + inputFileName, + lineNb+1, err, ) } } else { - next_instruction, err := Instructions.Parse(&state.Labels, &state.Defs, state.IsMacro, true, 0, line) + nextInstruction, err := Instructions.Parse(&state.Labels, &state.Defs, state.IsMacro, true, 0, lastAbsoluteLabel, line) if err != nil { return nil, fmt.Errorf( "File %s, line %d (1st pass):\n%w", - input_file_name, - line_nb+1, + inputFileName, + lineNb+1, err, ) } - result = append(result, next_instruction...) + result = append(result, nextInstruction...) } - line_nb += 1 + lineNb += 1 } return result, nil } func secondPass( - input_file_name string, + inputFileName string, input []byte, offset uint, state ProgramState, ) ([]byte, error) { lines := strings.Split(string(input), "\n") - line_nb := 0 + lineNb := 0 result := []byte{} - for line_nb < len(lines) { - line := lines[line_nb] - line_parts := strings.Split(line, ";") - line = line_parts[0] - is_label_defined := strings.Contains(line, ":") + lastAbsoluteLabel := "" + for lineNb < len(lines) { + line := lines[lineNb] + lineParts := strings.Split(line, ";") + line = lineParts[0] + isLabelDefined := strings.Contains(line, ":") - if is_label_defined { + if isLabelDefined { parts := strings.Split(line, ":") line = parts[len(parts)-1] + + for _, label := range parts[:len(parts)-1] { + if !strings.HasPrefix(label, ".") { + labelParts := strings.Split(label, ".") + if len(labelParts) < 1 { + return nil, fmt.Errorf( + "Unknown issue while retrieving label absolute part ! (label: \"%s\")", + label, + ) + } + + lastAbsoluteLabel = labelParts[0] + } + } } line = strings.TrimSpace(line) if strings.HasPrefix(line, ".") { - err := MacroParse(line, lines, &result, &state, &line_nb, false, offset) + err := MacroParse( + line, + lines, + &result, + &state, + &lineNb, + false, + offset, + lastAbsoluteLabel, + ) if err != nil { return nil, fmt.Errorf( "File %s, line %d (2nd pass|macro):\n%w", - input_file_name, - line_nb+1, + inputFileName, + lineNb+1, err, ) } } else { - next_instruction, err := Instructions.Parse(&state.Labels, &state.Defs, state.IsMacro, false, uint16(uint(len(result))+offset), line) + nextInstruction, err := Instructions.Parse(&state.Labels, &state.Defs, state.IsMacro, false, uint16(uint(len(result))+offset), lastAbsoluteLabel, line) if err != nil { return nil, fmt.Errorf( "File %s, line %d (2nd pass): %w", - input_file_name, - line_nb+1, + inputFileName, + lineNb+1, err, ) } - result = append(result, next_instruction...) + result = append(result, nextInstruction...) } - line_nb += 1 + lineNb += 1 } return result, nil @@ -195,34 +219,34 @@ func main() { os.Exit(1) } - input_file_name := os.Args[1] - output_file_name := os.Args[2] + inputFileName := os.Args[1] + outputFileName := os.Args[2] - input_file, err := os.Open(input_file_name) + inputFile, err := os.Open(inputFileName) if err != nil { fmt.Fprintf(os.Stderr, "Error while opening input file: %s\n", err.Error()) os.Exit(1) } - input, err := io.ReadAll(input_file) + input, err := io.ReadAll(inputFile) if err != nil { fmt.Fprintf(os.Stderr, "Error while reading input file: %s\n", err.Error()) os.Exit(1) } - result, err := parseFile(input_file_name, input, 0) + result, err := parseFile(inputFileName, input, 0) if err != nil { fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) os.Exit(1) } - output_file, err := os.Create(output_file_name) + outputFile, err := os.Create(outputFileName) if err != nil { fmt.Fprintf(os.Stderr, "Error while opening output file: %s\n", err.Error()) os.Exit(1) } - _, err = output_file.Write(result) + _, err = outputFile.Write(result) if err != nil { 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 ddb4532..ac17f70 100644 --- a/parameters.go +++ b/parameters.go @@ -6,7 +6,7 @@ import ( "strings" ) -func Reg8(_ *Labels, _ *Definitions, param string) (uint16, error) { +func Reg8(_ *Labels, lastAbsoluteLabel string, _ *Definitions, param string) (uint16, error) { switch param { case "A": return 7, nil @@ -28,35 +28,35 @@ func Reg8(_ *Labels, _ *Definitions, param string) (uint16, error) { return 0, fmt.Errorf("Invalid reg8") } -func A(_ *Labels, _ *Definitions, param string) (uint16, error) { +func A(_ *Labels, lastAbsoluteLabel string, _ *Definitions, param string) (uint16, error) { if param == "A" { return 0, nil } return 0, fmt.Errorf("Invalid A") } -func HL(_ *Labels, _ *Definitions, param string) (uint16, error) { +func HL(_ *Labels, lastAbsoluteLabel string, _ *Definitions, param string) (uint16, error) { if param == "HL" { return 0, nil } return 0, fmt.Errorf("Invalid HL") } -func SP(_ *Labels, _ *Definitions, param string) (uint16, error) { +func SP(_ *Labels, lastAbsoluteLabel string, _ *Definitions, param string) (uint16, error) { if param == "SP" { return 0, nil } return 0, fmt.Errorf("Invalid SP") } -func IndirectC(_ *Labels, _ *Definitions, param string) (uint16, error) { +func IndirectC(_ *Labels, lastAbsoluteLabel string, _ *Definitions, param string) (uint16, error) { if param == "(C)" { return 0, nil } return 0, fmt.Errorf("Invalid (C)") } -func Reg16(_ *Labels, _ *Definitions, param string) (uint16, error) { +func Reg16(_ *Labels, lastAbsoluteLabel string, _ *Definitions, param string) (uint16, error) { switch param { case "BC": return 0, nil @@ -73,7 +73,7 @@ func Reg16(_ *Labels, _ *Definitions, param string) (uint16, error) { return 0, fmt.Errorf("Invalid reg16") } -func Raw8(_ *Labels, defs *Definitions, param string) (uint16, error) { +func Raw8(_ *Labels, lastAbsoluteLabel string, defs *Definitions, param string) (uint16, error) { if strings.HasPrefix(param, "$") { param = strings.ToUpper(strings.TrimPrefix(param, "$")) if res, err := strconv.ParseUint(param, 16, 16); err == nil { @@ -101,7 +101,12 @@ func Raw8(_ *Labels, defs *Definitions, param string) (uint16, error) { return uint16(res), err } -func Raw16(labels *Labels, defs *Definitions, param string) (uint16, error) { +func Raw16( + labels *Labels, + lastAbsoluteLabel string, + defs *Definitions, + param string, +) (uint16, error) { if strings.HasPrefix(param, "$") { param = strings.ToUpper(strings.TrimPrefix(param, "$")) if res, err := strconv.ParseUint(param, 16, 16); err == nil { @@ -122,14 +127,14 @@ func Raw16(labels *Labels, defs *Definitions, param string) (uint16, error) { if strings.HasPrefix(param, "=") { var offset uint16 = 0 - labelWithoutOffset := param + labelWithoutOffset := param[1:] if strings.Contains(param, "+") { - labelParts := strings.Split(param, "+") + labelParts := strings.Split(param[1:], "+") if len(labelParts) != 2 { return 0, fmt.Errorf( "Labels with offset should have exactly 1 offset (in \"%s\")", - param, + param[1:], ) } labelWithoutOffset = labelParts[0] @@ -144,6 +149,16 @@ func Raw16(labels *Labels, defs *Definitions, param string) (uint16, error) { return 0, nil } + if strings.HasPrefix(labelWithoutOffset, ".") { + if lastAbsoluteLabel == "" { + return 0, fmt.Errorf( + "Relative label \"%s\" referenced outside of parent", + labelWithoutOffset, + ) + } + labelWithoutOffset = lastAbsoluteLabel + labelWithoutOffset + } + label := strings.ToUpper(strings.TrimPrefix(labelWithoutOffset, "=")) labelValue, ok := (*labels)[label] if !ok { @@ -163,17 +178,27 @@ func Raw16(labels *Labels, defs *Definitions, param string) (uint16, error) { return uint16(res), err } -func Raw16MacroRelativeLabel(labels *Labels, defs *Definitions, param string) (uint16, error) { +func Raw16MacroRelativeLabel( + labels *Labels, + lastAbsoluteLabel string, + defs *Definitions, + param string, +) (uint16, error) { if !strings.HasPrefix(param, "=$") { return 0, fmt.Errorf( "label \"%s\" is external to the macro", param, ) } - return Raw16(labels, defs, param) + return Raw16(labels, lastAbsoluteLabel, defs, param) } -func Reg16Indirect(_ *Labels, _ *Definitions, param string) (uint16, error) { +func Reg16Indirect( + _ *Labels, + lastAbsoluteLabel string, + _ *Definitions, + param string, +) (uint16, error) { switch param { case "(BC)": return 0, nil @@ -187,7 +212,12 @@ func Reg16Indirect(_ *Labels, _ *Definitions, param string) (uint16, error) { return 0, fmt.Errorf("Invalid reg16 indirect") } -func Raw8Indirect(labels *Labels, defs *Definitions, param string) (uint16, error) { +func Raw8Indirect( + labels *Labels, + lastAbsoluteLabel string, + defs *Definitions, + param string, +) (uint16, error) { if strings.HasPrefix(param, "$") { param = strings.ToUpper(strings.TrimPrefix(param, "$")) @@ -207,14 +237,19 @@ func Raw8Indirect(labels *Labels, defs *Definitions, param string) (uint16, erro return 0, fmt.Errorf("Invalid raw8indirect") } - res, err := Raw8(labels, defs, param[1:len(param)-1]) + res, err := Raw8(labels, lastAbsoluteLabel, defs, param[1:len(param)-1]) if err == nil { return res, nil } return 0, err } -func Raw16Indirect(labels *Labels, defs *Definitions, param string) (uint16, error) { +func Raw16Indirect( + labels *Labels, + lastAbsoluteLabel string, + defs *Definitions, + param string, +) (uint16, error) { if strings.HasPrefix(param, "$") { param = strings.ToUpper(strings.TrimPrefix(param, "$")) @@ -237,10 +272,10 @@ func Raw16Indirect(labels *Labels, defs *Definitions, param string) (uint16, err return 0, fmt.Errorf("Invalid raw16indirect") } - return Raw16(labels, defs, param[1:len(param)-1]) + return Raw16(labels, lastAbsoluteLabel, defs, param[1:len(param)-1]) } -func Condition(_ *Labels, _ *Definitions, param string) (uint16, error) { +func Condition(_ *Labels, lastAbsoluteLabel string, _ *Definitions, param string) (uint16, error) { switch param { case "NZ": return 0, nil @@ -254,7 +289,7 @@ func Condition(_ *Labels, _ *Definitions, param string) (uint16, error) { return 0, fmt.Errorf("Invalid condition") } -func BitOrdinal(_ *Labels, _ *Definitions, param string) (uint16, error) { +func BitOrdinal(_ *Labels, lastAbsoluteLabel string, _ *Definitions, param string) (uint16, error) { res, err := strconv.ParseUint(param, 0, 3) return uint16(res), err } |