diff options
author | Astatin <[email protected]> | 2025-01-07 16:20:52 +0900 |
---|---|---|
committer | Astatin <[email protected]> | 2025-01-07 16:20:52 +0900 |
commit | b5b9637ff2d222841632b8644b94835ec1c93359 (patch) | |
tree | e7239875fd331effe67e62c62569c0cff5b8c1d0 | |
parent | e4c337519fb9842de5eb12e5975f50efa02784d0 (diff) |
Add label offset + allow to use labels with .DB macro + relative labels
-rw-r--r-- | instructions.go | 17 | ||||
-rw-r--r-- | macros.go | 40 | ||||
-rw-r--r-- | main.go | 24 | ||||
-rw-r--r-- | parameters.go | 33 |
4 files changed, 96 insertions, 18 deletions
diff --git a/instructions.go b/instructions.go index 59daa3a..ddcb833 100644 --- a/instructions.go +++ b/instructions.go @@ -8,10 +8,11 @@ import ( type ParamType func(labels *Labels, defs *Definitions, param string) (uint16, error) type InstructionParams struct { - Types []ParamType - Assembler func(currentAddress uint16, args []uint16) ([]uint8, error) - Wildcard bool - MacroForbidden bool + Types []ParamType + Assembler func(currentAddress uint16, args []uint16) ([]uint8, error) + Wildcard bool + MacroForbidden bool + LabelsBeforeOnly bool } type InstructionSet map[string][]InstructionParams @@ -503,6 +504,7 @@ func (set InstructionSet) Parse( labels *Labels, defs *Definitions, isMacro bool, + isFirstPass bool, currentAddress uint16, line string, ) ([]byte, error) { @@ -534,7 +536,12 @@ instruction_param_loop: paramType = instrParam.Types[i] } - parsed, err := paramType(labels, defs, params[i]) + accessibleLabels := labels + + if isFirstPass && !instrParam.LabelsBeforeOnly { + accessibleLabels = nil + } + parsed, err := paramType(accessibleLabels, defs, params[i]) if err != nil { continue instruction_param_loop } @@ -30,7 +30,16 @@ func NewInstructionSetMacros() InstructionSet { Assembler: func(currentAddress uint16, args []uint16) ([]byte, error) { return make([]byte, args[0]-currentAddress), nil }, - MacroForbidden: true, + MacroForbidden: true, + LabelsBeforeOnly: true, + }, + { + Types: []ParamType{Raw16MacroRelativeLabel}, + Assembler: func(currentAddress uint16, args []uint16) ([]byte, error) { + return make([]byte, args[0]-currentAddress), nil + }, + MacroForbidden: false, + LabelsBeforeOnly: true, }, } @@ -46,6 +55,18 @@ func NewInstructionSetMacros() InstructionSet { }, Wildcard: true, }, + { + Types: []ParamType{Raw16}, + Assembler: func(currentAddress uint16, args []uint16) ([]byte, error) { + result := make([]byte, len(args)*2) + for i := range args { + result[i*2] = uint8(args[i] >> 8) + result[i*2+1] = uint8(args[i] & 0xff) + } + return result, nil + }, + Wildcard: true, + }, } return result @@ -63,8 +84,8 @@ func MacroParse( lines []string, result *[]byte, state *ProgramState, - line_nb *int, // line_nb - is_first_pass bool, + lineNb *int, + isFirstPass bool, offset uint, ) error { words := strings.Split(line, " ") @@ -79,6 +100,7 @@ func MacroParse( &state.Labels, &state.Defs, state.IsMacro, + isFirstPass, uint16(uint(len(*result))+offset), line, ) @@ -102,7 +124,7 @@ func MacroParse( } fileStartOffset := uint(len(*result)) + offset - if is_first_pass { + if isFirstPass { included, err := firstPass(filePath, input, fileStartOffset, state) if err != nil { return err @@ -145,14 +167,14 @@ func MacroParse( return fmt.Errorf(".MACRODEF should have one argument, followed by the definition") } definedMacroName := strings.ToUpper(words[1]) - (*line_nb) += 1 + (*lineNb) += 1 macroContent := []byte{} - for *line_nb < len(lines) && strings.TrimSpace(strings.Split(lines[*line_nb], ";")[0]) != ".END" { - macroContent = append(macroContent, (lines[*line_nb] + "\n")...) - (*line_nb) += 1 + for *lineNb < len(lines) && strings.TrimSpace(strings.Split(lines[*lineNb], ";")[0]) != ".END" { + macroContent = append(macroContent, (lines[*lineNb] + "\n")...) + (*lineNb) += 1 } - if is_first_pass { + if isFirstPass { if _, ok := MacroInstructions[definedMacroName]; ok { return fmt.Errorf("Macro %s is already defined", definedMacroName) } @@ -43,6 +43,7 @@ func firstPass( line_nb := 0 result := []byte{} + lastAbsoluteLabel := "" for line_nb < len(lines) { line := lines[line_nb] line_parts := strings.Split(line, ";") @@ -62,6 +63,25 @@ func firstPass( label, ) } + + if strings.HasPrefix(label, ".") { + if lastAbsoluteLabel == "" { + return nil, fmt.Errorf( + "Relative label \"%s\" found without a parent", + label, + ) + } + + label = lastAbsoluteLabel + label + } else { + 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] + } + if _, ok := state.Labels[label]; ok { return nil, fmt.Errorf( "File %s, line %d:\nLabel %s is already defined", @@ -98,7 +118,7 @@ func firstPass( ) } } else { - next_instruction, err := Instructions.Parse(nil, &state.Defs, state.IsMacro, 0, line) + next_instruction, err := Instructions.Parse(&state.Labels, &state.Defs, state.IsMacro, true, 0, line) if err != nil { return nil, fmt.Errorf( "File %s, line %d (1st pass):\n%w", @@ -151,7 +171,7 @@ func secondPass( ) } } else { - next_instruction, err := Instructions.Parse(&state.Labels, &state.Defs, state.IsMacro, uint16(uint(len(result))+offset), line) + next_instruction, err := Instructions.Parse(&state.Labels, &state.Defs, state.IsMacro, false, uint16(uint(len(result))+offset), line) if err != nil { return nil, fmt.Errorf( "File %s, line %d (2nd pass): %w", diff --git a/parameters.go b/parameters.go index 4c787f3..ddb4532 100644 --- a/parameters.go +++ b/parameters.go @@ -121,11 +121,30 @@ func Raw16(labels *Labels, defs *Definitions, param string) (uint16, error) { } if strings.HasPrefix(param, "=") { + var offset uint16 = 0 + labelWithoutOffset := param + + if strings.Contains(param, "+") { + labelParts := strings.Split(param, "+") + if len(labelParts) != 2 { + return 0, fmt.Errorf( + "Labels with offset should have exactly 1 offset (in \"%s\")", + param, + ) + } + labelWithoutOffset = labelParts[0] + o, err := strconv.ParseUint(labelParts[1], 0, 16) + if err != nil { + return 0, fmt.Errorf("Error while parsing label offset: %w", err) + } + offset = uint16(o) + } + if labels == nil { return 0, nil } - label := strings.ToUpper(strings.TrimPrefix(param, "=")) + label := strings.ToUpper(strings.TrimPrefix(labelWithoutOffset, "=")) labelValue, ok := (*labels)[label] if !ok { return 0, fmt.Errorf("Label \"%s\" not found", label) @@ -136,7 +155,7 @@ func Raw16(labels *Labels, defs *Definitions, param string) (uint16, error) { panic("Switchable ROM banks are not implemented yet") } - return uint16(labelValue), nil + return uint16(labelValue) + offset, nil } res, err := strconv.ParseUint(param, 0, 16) @@ -144,6 +163,16 @@ func Raw16(labels *Labels, defs *Definitions, param string) (uint16, error) { return uint16(res), err } +func Raw16MacroRelativeLabel(labels *Labels, 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) +} + func Reg16Indirect(_ *Labels, _ *Definitions, param string) (uint16, error) { switch param { case "(BC)": |