aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2025-01-07 17:24:46 +0900
committerAstatin <[email protected]>2025-01-07 17:24:46 +0900
commitc2fb41b27cb4294520cd73aa3d81d51d0f969bf8 (patch)
tree187caf2f08e3e9625daa18146c0798ea491f84ac
parentb5b9637ff2d222841632b8644b94835ec1c93359 (diff)
Add support for relative labels in parameters
-rw-r--r--instructions.go5
-rw-r--r--macros.go10
-rw-r--r--main.go116
-rw-r--r--parameters.go75
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
}
diff --git a/macros.go b/macros.go
index 5daf3c9..bf91eaa 100644
--- a/macros.go
+++ b/macros.go
@@ -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])
diff --git a/main.go b/main.go
index dd6ce6c..7d289a0 100644
--- a/main.go
+++ b/main.go
@@ -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
}