package asmc import ( "fmt" "strings" "git.urbach.dev/cli/q/src/asm" "git.urbach.dev/cli/q/src/config" "git.urbach.dev/cli/q/src/x86" ) type x86Compiler struct { *compiler } func (c *x86Compiler) Compile(instruction asm.Instruction) { switch instruction.Mnemonic { case asm.MOVE: c.handleMoveInstruction(instruction) case asm.CALL: c.handleCallInstruction(instruction) case asm.LABEL: label := c.assembler.Param.Label[instruction.Index] c.codeLabels[label.Name] = Address(len(c.code)) case asm.LOAD: c.handleLoadInstruction(instruction) case asm.STORE: c.handleStoreInstruction(instruction) case asm.RETURN: c.code = x86.Return(c.code) case asm.SYSCALL: c.code = x86.Syscall(c.code) case asm.ADD: c.handleAddInstruction(instruction) case asm.AND: c.handleAndInstruction(instruction) case asm.DIV: c.handleDivInstruction(instruction) case asm.MODULO: c.handleModuloInstruction(instruction) case asm.MUL: c.handleMulInstruction(instruction) case asm.OR: c.handleOrInstruction(instruction) case asm.SUB: c.handleSubInstruction(instruction) case asm.XOR: c.handleXorInstruction(instruction) case asm.NEGATE: c.handleNegateInstruction(instruction) case asm.POP: c.handlePopInstruction(instruction) case asm.PUSH: c.handlePushInstruction(instruction) case asm.SHIFTL: c.handleShiftLeftInstruction(instruction) case asm.SHIFTRS: c.handleShiftRightSignedInstruction(instruction) case asm.COMPARE: c.handleCompareInstruction(instruction) case asm.DLLCALL: c.handleDllCallInstruction(instruction) case asm.JE, asm.JNE, asm.JG, asm.JGE, asm.JL, asm.JLE, asm.JUMP: c.handleJumpInstruction(instruction) default: panic("unknown mnemonic: " + instruction.Mnemonic.String()) } } func (c *x86Compiler) handleMoveInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] c.code = x86.MoveRegisterNumber(c.code, operands.Register, operands.Number) case asm.TypeRegisterRegister: operands := c.assembler.Param.RegisterRegister[instruction.Index] c.code = x86.MoveRegisterRegister(c.code, operands.Destination, operands.Source) case asm.TypeRegisterLabel: operands := c.assembler.Param.RegisterLabel[instruction.Index] start := Address(len(c.code)) c.code = x86.LoadAddress(c.code, operands.Register, 0x00_00_00_00) end := Address(len(c.code)) position := end - 4 opSize := position - start if operands.Label.Type == asm.DataLabel { c.dataPointers = append(c.dataPointers, &pointer{ Position: position, OpSize: uint8(opSize), Size: uint8(4), Resolve: func() Address { destination, exists := c.dataLabels[operands.Label.Name] if !exists { panic("unknown label") } destination += c.dataStart - c.codeStart distance := destination - end return distance + 8 }, }) } else { c.codePointers = append(c.codePointers, &pointer{ Position: position, OpSize: uint8(opSize), Size: uint8(4), Resolve: func() Address { destination, exists := c.codeLabels[operands.Label.Name] if !exists { panic("unknown label") } return destination - end }, }) } } } func (c *x86Compiler) handleCallInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeLabel: data := c.assembler.Param.Label[instruction.Index] c.code = x86.Call(c.code, 0x00_00_00_00) size := 4 pointer := &pointer{ Position: Address(len(c.code) - size), OpSize: 1, Size: uint8(size), } pointer.Resolve = func() Address { destination, exists := c.codeLabels[data.Name] if !exists { panic(fmt.Sprintf("unknown jump label %s", data.Name)) } distance := destination - (pointer.Position + Address(pointer.Size)) return distance } c.codePointers = append(c.codePointers, pointer) case asm.TypeRegister: data := c.assembler.Param.Register[instruction.Index] c.code = x86.CallRegister(c.code, data.Register) case asm.TypeMemory: data := c.assembler.Param.Memory[instruction.Index] c.code = x86.CallAtMemory(c.code, data.Base, data.Offset) } } func (c *x86Compiler) handleLoadInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeMemoryRegister: operands := c.assembler.Param.MemoryRegister[instruction.Index] if operands.Address.OffsetRegister < 0 { c.code = x86.LoadRegister(c.code, operands.Register, operands.Address.Base, operands.Address.Offset, operands.Address.Length) } else { c.code = x86.LoadDynamicRegister(c.code, operands.Register, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length) } } } func (c *x86Compiler) handleStoreInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeMemoryNumber: operands := c.assembler.Param.MemoryNumber[instruction.Index] if operands.Address.OffsetRegister < 0 { c.code = x86.StoreNumber(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Number) } else { c.code = x86.StoreDynamicNumber(c.code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Number) } case asm.TypeMemoryLabel: operands := c.assembler.Param.MemoryLabel[instruction.Index] start := len(c.code) if operands.Address.OffsetRegister < 0 { c.code = x86.StoreNumber(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, 0b00_00_00_00) } else { c.code = x86.StoreDynamicNumber(c.code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, 0b00_00_00_00) } size := 4 opSize := len(c.code) - size - start c.codePointers = append(c.codePointers, &pointer{ Position: Address(len(c.code) - size), OpSize: uint8(opSize), Size: uint8(size), Resolve: func() Address { destination, exists := c.codeLabels[operands.Label.Name] if !exists { panic("unknown label") } return config.BaseAddress + c.codeStart + destination }, }) case asm.TypeMemoryRegister: operands := c.assembler.Param.MemoryRegister[instruction.Index] if operands.Address.OffsetRegister < 0 { c.code = x86.StoreRegister(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Register) } else { c.code = x86.StoreDynamicRegister(c.code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Register) } } } func (c *x86Compiler) handleAddInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] c.code = x86.AddRegisterNumber(c.code, operands.Register, operands.Number) case asm.TypeRegisterRegister: operands := c.assembler.Param.RegisterRegister[instruction.Index] c.code = x86.AddRegisterRegister(c.code, operands.Destination, operands.Source) } } func (c *x86Compiler) handleAndInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] c.code = x86.AndRegisterNumber(c.code, operands.Register, operands.Number) case asm.TypeRegisterRegister: operands := c.assembler.Param.RegisterRegister[instruction.Index] c.code = x86.AndRegisterRegister(c.code, operands.Destination, operands.Source) } } func (c *x86Compiler) handleDivInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] if operands.Register != x86.RAX { c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Register) } c.code = x86.MoveRegisterNumber(c.code, x86.TMP, operands.Number) c.code = x86.ExtendRAXToRDX(c.code) c.code = x86.DivRegister(c.code, x86.TMP) if operands.Register != x86.RAX { c.code = x86.MoveRegisterRegister(c.code, operands.Register, x86.RAX) } case asm.TypeRegisterRegister: operands := c.assembler.Param.RegisterRegister[instruction.Index] if operands.Destination != x86.RAX { c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination) } c.code = x86.ExtendRAXToRDX(c.code) c.code = x86.DivRegister(c.code, operands.Source) if operands.Destination != x86.RAX { c.code = x86.MoveRegisterRegister(c.code, operands.Destination, x86.RAX) } } } func (c *x86Compiler) handleModuloInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] if operands.Register != x86.RAX { c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Register) } c.code = x86.MoveRegisterNumber(c.code, x86.TMP, operands.Number) c.code = x86.ExtendRAXToRDX(c.code) c.code = x86.DivRegister(c.code, x86.TMP) if operands.Register != x86.RDX { c.code = x86.MoveRegisterRegister(c.code, operands.Register, x86.RDX) } case asm.TypeRegisterRegister: operands := c.assembler.Param.RegisterRegister[instruction.Index] if operands.Destination != x86.RAX { c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination) } c.code = x86.ExtendRAXToRDX(c.code) c.code = x86.DivRegister(c.code, operands.Source) if operands.Destination != x86.RDX { c.code = x86.MoveRegisterRegister(c.code, operands.Destination, x86.RDX) } } } func (c *x86Compiler) handleMulInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] c.code = x86.MulRegisterNumber(c.code, operands.Register, operands.Number) case asm.TypeRegisterRegister: operands := c.assembler.Param.RegisterRegister[instruction.Index] c.code = x86.MulRegisterRegister(c.code, operands.Destination, operands.Source) } } func (c *x86Compiler) handleOrInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] c.code = x86.OrRegisterNumber(c.code, operands.Register, operands.Number) case asm.TypeRegisterRegister: operands := c.assembler.Param.RegisterRegister[instruction.Index] c.code = x86.OrRegisterRegister(c.code, operands.Destination, operands.Source) } } func (c *x86Compiler) handleSubInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] c.code = x86.SubRegisterNumber(c.code, operands.Register, operands.Number) case asm.TypeRegisterRegister: operands := c.assembler.Param.RegisterRegister[instruction.Index] c.code = x86.SubRegisterRegister(c.code, operands.Destination, operands.Source) } } func (c *x86Compiler) handleXorInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] c.code = x86.XorRegisterNumber(c.code, operands.Register, operands.Number) case asm.TypeRegisterRegister: operands := c.assembler.Param.RegisterRegister[instruction.Index] c.code = x86.XorRegisterRegister(c.code, operands.Destination, operands.Source) } } func (c *x86Compiler) handleNegateInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegister: operands := c.assembler.Param.Register[instruction.Index] c.code = x86.NegateRegister(c.code, operands.Register) } } func (c *x86Compiler) handlePopInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegister: operands := c.assembler.Param.Register[instruction.Index] c.code = x86.PopRegister(c.code, operands.Register) } } func (c *x86Compiler) handlePushInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeNumber: operands := c.assembler.Param.Number[instruction.Index] c.code = x86.PushNumber(c.code, int32(operands.Number)) case asm.TypeRegister: operands := c.assembler.Param.Register[instruction.Index] c.code = x86.PushRegister(c.code, operands.Register) } } func (c *x86Compiler) handleShiftLeftInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] c.code = x86.ShiftLeftNumber(c.code, operands.Register, byte(operands.Number)&0b111111) case asm.TypeRegisterRegister: panic("not implemented") } } func (c *x86Compiler) handleShiftRightSignedInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] c.code = x86.ShiftRightSignedNumber(c.code, operands.Register, byte(operands.Number)&0b111111) case asm.TypeRegisterRegister: panic("not implemented") } } func (c *x86Compiler) handleCompareInstruction(instruction asm.Instruction) { switch instruction.Type { case asm.TypeRegisterNumber: operands := c.assembler.Param.RegisterNumber[instruction.Index] c.code = x86.CompareRegisterNumber(c.code, operands.Register, operands.Number) case asm.TypeRegisterRegister: operands := c.assembler.Param.RegisterRegister[instruction.Index] c.code = x86.CompareRegisterRegister(c.code, operands.Destination, operands.Source) } } func (c *x86Compiler) handleDllCallInstruction(instruction asm.Instruction) { label := c.assembler.Param.Label[instruction.Index] c.code = x86.CallAt(c.code, 0x00_00_00_00) next := Address(len(c.code)) position := next - 4 pointer := &pointer{ Position: Address(position), OpSize: 2, Size: 4, } pointer.Resolve = func() Address { dot := strings.Index(label.Name, ".") library := label.Name[:dot] funcName := label.Name[dot+1:] index := c.dlls.Index(library, funcName) if index == -1 { panic("unknown DLL function " + label.Name) } destination := c.importsStart + Address(index*8) from := c.codeStart + next return destination - from } c.dllPointers = append(c.dllPointers, pointer) } func (c *x86Compiler) handleJumpInstruction(instruction asm.Instruction) { switch instruction.Mnemonic { case asm.JE: c.code = x86.Jump8IfEqual(c.code, 0x00) case asm.JNE: c.code = x86.Jump8IfNotEqual(c.code, 0x00) case asm.JG: c.code = x86.Jump8IfGreater(c.code, 0x00) case asm.JGE: c.code = x86.Jump8IfGreaterOrEqual(c.code, 0x00) case asm.JL: c.code = x86.Jump8IfLess(c.code, 0x00) case asm.JLE: c.code = x86.Jump8IfLessOrEqual(c.code, 0x00) case asm.JUMP: c.code = x86.Jump8(c.code, 0x00) } label := c.assembler.Param.Label[instruction.Index] size := 1 pointer := &pointer{ Position: Address(len(c.code) - size), OpSize: 1, Size: uint8(size), } pointer.Resolve = func() Address { destination, exists := c.codeLabels[label.Name] if !exists { panic(fmt.Sprintf("unknown jump label %s", label.Name)) } distance := destination - (pointer.Position + Address(pointer.Size)) return distance } c.codePointers = append(c.codePointers, pointer) }