diff options
author | Astatin <[email protected]> | 2024-09-26 16:12:49 +0900 |
---|---|---|
committer | Astatin <astatin@redacted> | 2024-09-26 16:12:49 +0900 |
commit | 7657996ad68689b437f742516f0f285e53e8f0e3 (patch) | |
tree | 9f22f50c30dfe174200a388c2a6c5c7a5b848741 | |
parent | 0b44780a7b142147d25f0f436159b876f95f17f7 (diff) |
Add inline macros
-rw-r--r-- | instructions.go | 35 | ||||
-rw-r--r-- | macros.go | 74 | ||||
-rw-r--r-- | main.go | 30 | ||||
-rw-r--r-- | parameters.go | 20 |
4 files changed, 133 insertions, 26 deletions
diff --git a/instructions.go b/instructions.go index 0a7afb9..94b48bd 100644 --- a/instructions.go +++ b/instructions.go @@ -8,9 +8,10 @@ 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 + Types []ParamType + Assembler func(currentAddress uint16, args []uint16) ([]uint8, error) + Wildcard bool + MacroForbidden bool } type InstructionSet map[string][]InstructionParams @@ -283,7 +284,26 @@ func InstructionSetNew() InstructionSet { return []byte{0b00100000 | (uint8(args[0]) << 3), uint8(args[1])}, nil }, }, - // TODO: Add the relative thingies somehow + { + Types: []ParamType{MacroLabel}, + Assembler: func(currentAddress uint16, args []uint16) ([]byte, error) { + relativeAddress, err := absoluteJPValueToRelative(currentAddress, args[0]) + if err != nil { + return nil, err + } + return []byte{0b00011000, relativeAddress}, nil + }, + }, + { + Types: []ParamType{Condition, MacroLabel}, + Assembler: func(currentAddress uint16, args []uint16) ([]byte, error) { + relativeAddress, err := absoluteJPValueToRelative(currentAddress, args[1]) + if err != nil { + return nil, err + } + return []byte{0b00100000 | (uint8(args[0]) << 3), relativeAddress}, nil + }, + }, { Types: []ParamType{Raw16}, Assembler: func(currentAddress uint16, args []uint16) ([]byte, error) { @@ -293,6 +313,7 @@ func InstructionSetNew() InstructionSet { } return []byte{0b00011000, relativeAddress}, nil }, + MacroForbidden: true, }, { Types: []ParamType{Condition, Raw16}, @@ -303,6 +324,7 @@ func InstructionSetNew() InstructionSet { } return []byte{0b00100000 | (uint8(args[0]) << 3), relativeAddress}, nil }, + MacroForbidden: true, }, } result["CALL"] = []InstructionParams{ @@ -494,6 +516,7 @@ func InstructionSetNew() InstructionSet { func (set InstructionSet) Parse( labels *Labels, defs *Definitions, + isMacro bool, currentAddress uint16, line string, ) ([]byte, error) { @@ -533,6 +556,10 @@ instruction_param_loop: parsed_params[i] = parsed } + if instrParam.MacroForbidden && isMacro { + return nil, fmt.Errorf("This instruction cannot be used with this set of params inside of a macro") + } + return instrParam.Assembler(currentAddress, parsed_params) } return nil, fmt.Errorf( @@ -10,6 +10,17 @@ import ( var MacroInstructions = NewInstructionSetMacros() +func InlineMacroAssembler(b []byte) []InstructionParams { + return []InstructionParams{ + { + Types: []ParamType{}, + Assembler: func(currentAddress uint16, args []uint16) ([]uint8, error) { + return b, nil + }, + }, + } +} + func NewInstructionSetMacros() InstructionSet { result := make(InstructionSet) @@ -17,14 +28,9 @@ func NewInstructionSetMacros() InstructionSet { { Types: []ParamType{Raw16}, Assembler: func(currentAddress uint16, args []uint16) ([]byte, error) { - fmt.Printf( - ".PADTO 0x%04x, currentAddress: 0x%04x, inserting: 0x%04x\n", - args[0], - currentAddress, - args[0]-currentAddress, - ) return make([]byte, args[0]-currentAddress), nil }, + MacroForbidden: true, }, } @@ -54,9 +60,10 @@ type ( func MacroParse( line string, + lines []string, result *[]byte, state *ProgramState, - _ *int, // line_nb + line_nb *int, // line_nb is_first_pass bool, offset uint, ) error { @@ -71,6 +78,7 @@ func MacroParse( new_instruction, err := MacroInstructions.Parse( &state.Labels, &state.Defs, + state.IsMacro, uint16(uint(len(*result))+offset), line, ) @@ -80,7 +88,7 @@ func MacroParse( *result = append(*result, new_instruction...) return nil - } else if macroName == ".INCLUDE" { + } else if macroName == ".INCLUDE" && !state.IsMacro { filePath := strings.Trim(strings.TrimSpace(strings.TrimPrefix(line, ".INCLUDE")), "\"'") input_file, err := os.Open(filePath) @@ -107,7 +115,7 @@ func MacroParse( } *result = append(*result, included...) } - } else if macroName == ".DEFINE" { + } else if macroName == ".DEFINE" && !state.IsMacro { if len(words) != 3 { return fmt.Errorf(".DEFINE must have 2 arguments (%v)", words) } @@ -132,8 +140,56 @@ func MacroParse( } state.Defs[name] = definedValue + } else if macroName == ".MACRODEF" && !state.IsMacro { + if len(words) != 2 { + return fmt.Errorf(".MACRODEF should have one argument, followed by the definition") + } + definedMacroName := strings.ToUpper(words[1]) + (*line_nb) += 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 + } + + state := ProgramState{ + Labels: Clone(state.Labels), + Defs: Clone(state.Defs), + IsMacro: true, + } + + if is_first_pass { + if _, ok := MacroInstructions[definedMacroName]; ok { + return fmt.Errorf("Macro %s is already defined", definedMacroName) + } + + new_instructions, err := firstPass("MACRO$"+definedMacroName, macroContent, 0, &state) + if err != nil { + return err + } + MacroInstructions["."+definedMacroName] = InlineMacroAssembler(new_instructions) + } else { + _, err := firstPass("MACRO$"+definedMacroName, macroContent, 0, &state) + if err != nil { + return err + } + new_instructions, err := secondPass("MACRO$"+definedMacroName, macroContent, offset, state) + if err != nil { + return err + } + MacroInstructions["."+definedMacroName] = InlineMacroAssembler(new_instructions) + + } } else { return fmt.Errorf("Unknown macro \"%s\"", macroName) } return nil } + +func Clone[K comparable, V any](arg map[K]V) map[K]V { + result := make(map[K]V) + for k, v := range arg { + result[k] = v + } + return result +} @@ -13,14 +13,16 @@ type ( ) type ProgramState struct { - Labels Labels - Defs Definitions + Labels Labels + Defs Definitions + IsMacro bool } func parseFile(input_file_name string, input []byte, offset uint) ([]byte, error) { state := ProgramState{ - Labels: make(map[string]uint), - Defs: make(map[string]any), + Labels: make(map[string]uint), + Defs: make(map[string]any), + IsMacro: false, } _, err := firstPass(input_file_name, input, offset, &state) @@ -51,15 +53,21 @@ func firstPass( for _, label := range parts[:len(parts)-1] { label = strings.TrimSpace(strings.ToUpper(label)) if _, ok := state.Labels[label]; ok { - fmt.Fprintf( - os.Stderr, + return nil, fmt.Errorf( "File %s, line %d:\nLabel %s is already defined", input_file_name, line_nb, label, ) - os.Exit(1) } + + if label[0] == '$' && !state.IsMacro { + return nil, fmt.Errorf("Labels starting with $ can only be used inside macros") + } + if label[0] != '$' && state.IsMacro { + return nil, fmt.Errorf("Labels inside a macro must start with $") + } + state.Labels[label] = uint(len(result)) + offset } @@ -70,7 +78,7 @@ 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, &result, state, &line_nb, true, offset) + err := MacroParse(line, lines, &result, state, &line_nb, true, offset) if err != nil { return nil, fmt.Errorf( "File %s, line %d (1st pass|macro):\n%w", @@ -80,7 +88,7 @@ func firstPass( ) } } else { - next_instruction, err := Instructions.Parse(nil, &state.Defs, 0, line) + next_instruction, err := Instructions.Parse(nil, &state.Defs, state.IsMacro, 0, line) if err != nil { return nil, fmt.Errorf( "File %s, line %d (1st pass):\n%w", @@ -123,7 +131,7 @@ func secondPass( line = strings.TrimSpace(line) if strings.HasPrefix(line, ".") { - err := MacroParse(line, &result, &state, &line_nb, false, offset) + err := MacroParse(line, lines, &result, &state, &line_nb, false, offset) if err != nil { return nil, fmt.Errorf( "File %s, line %d (2nd pass|macro):\n%w", @@ -133,7 +141,7 @@ func secondPass( ) } } else { - next_instruction, err := Instructions.Parse(&state.Labels, &state.Defs, uint16(uint(len(result))+offset), line) + next_instruction, err := Instructions.Parse(&state.Labels, &state.Defs, state.IsMacro, 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 8a3174b..5e234f5 100644 --- a/parameters.go +++ b/parameters.go @@ -120,7 +120,7 @@ func Raw16(labels *Labels, defs *Definitions, param string) (uint16, error) { return uint16(res), nil } - if strings.HasPrefix(param, "=") { + if strings.HasPrefix(param, "=") && !strings.HasPrefix(param, "=$") { if labels == nil { return 0, nil } @@ -144,6 +144,23 @@ func Raw16(labels *Labels, defs *Definitions, param string) (uint16, error) { return uint16(res), err } +func MacroLabel(labels *Labels, defs *Definitions, param string) (uint16, error) { + if strings.HasPrefix(param, "=$") { + if labels == nil { + return 0, nil + } + + label := strings.ToUpper(strings.TrimPrefix(param, "=")) + labelValue, ok := (*labels)[label] + if !ok { + return 0, fmt.Errorf("Label \"%s\" not found", label) + } + + return uint16(labelValue), nil + } + return 0, fmt.Errorf("Invalid macro label") +} + func Reg16Indirect(_ *Labels, _ *Definitions, param string) (uint16, error) { switch param { case "(BC)": @@ -202,7 +219,6 @@ func Raw16Indirect(labels *Labels, defs *Definitions, param string) (uint16, err if !ok { return 0, fmt.Errorf("$%s is of type %T but Indirect16b expected", param, res) } - fmt.Println("YOU CAN LOOSE YOUR MIND NOW ! IT OFFICIALLY DOESN'T MAKE SENSE", param, res) return uint16(res), nil } if len(param) < 2 || param[0] != '(' || param[len(param)-1] != ')' { |