Generating Instructions
Turbo Pascal uses few procedures to generate any x86 instruction needed. In general two intermediate code records are used: one for the actual inctruction opcodes and immediate data and one for references. Reference records are processed by code generator which generates spaces where Linker will generate remaining parameters of instructions and place references to program code or data.
Procedure GenerateInstruction_Byte (Code: Byte);
begin
With PIntermediateCodeRecord (IncreaseSymbolTable (stIntermediateCode, 2))^ do
begin
Recordtype := icByte;
ByteCode := Code;
end;
end;
Procedure GenerateInstruction_TwoBytes (B0, B1: Byte);
begin
With PIntermediateCodeRecord (IncreaseSymbolTable (stIntermediateCode, 3))^ do
begin
Recordtype := icWord;
WordCode.ByteL := B0;
WordCode.ByteH := B1;
end;
end;
Procedure GenerateInstruction_Word (Code: Word);
begin
With PIntermediateCodeRecord (IncreaseSymbolTable (stIntermediateCode, 3))^ do
begin
Recordtype := icWord;
WordCode.Word := Code;
end;
end;
If expression is not in data segment, this procedure generates code for segment override.
Procedure TExpression.GenerateSegmentOverride;
begin
If LocationData.Flags * [ofsBP, segDS] = [] then
GenerateInstruction_Byte (SegmentOverride or ExpressionSegment);
end;
Procedure TExpression.GenerateInstructionWithSegment (OpCode: Byte);
begin
GenerateInstruction_Byte (OpCode);
If efRelocatable in LocationData.Flags then GenerateReference (Value.Segment, Value.BlockRecord, 0, [rfSegment])
else GenerateInstruction_Word (Value.VarSegment);
end;
If expression is not in data or code segment, this procedure generates immediate offset value, otherwise it generates offset reference.
Procedure TExpression.GenerateOffsetReferenceForExpressionInMemory;
Var Flags: TReferenceFlagSet;
begin
If LocationData.Flags * [efTypedConstant, segCS, segDS] = [] then GenerateInstruction_Word (Value.Offset) else
begin
Case segDS in LocationData.Flags of
True: If efTypedConstant in LocationData.Flags then
Flags := [rfDataSegment, rfConstant, rfOffset] else Flags := [rfDataSegment, rfOffset];
else If segCS in LocationData.Flags then Flags := [rfConstant, rfOffset] else Flags := [rfOffset];
end;
GenerateReference (Value.Segment, Value.BlockRecord, Value.Offset, Flags);
end;
end;
This procedure generates instruction which references memory location.
Procedure TExpression.GenerateInstructionWithMemoryReference (MainOpCode, MODEM_OpCode: Byte);
begin
Case ofsBP in LocationData.Flags of
True: begin
If ofsDI in LocationData.Flags then MODEM_OpCode := MODEM_OpCode xor rmSS_BP_DI_Displacement
else MODEM_OpCode := MODEM_OpCode xor rmSS_BP_Displacement;
end;
else Case ofsDI in LocationData.Flags of
True: begin
MODEM_OpCode := MODEM_OpCode xor rmDS_DI;
If LocationData.Flags * [efTypedConstant, segCS, segDS] <> [] then
begin
GenerateInstruction_TwoBytes (MainOpCode, MODEM_OpCode or modMemoryDisp16);
GenerateOffsetReferenceForExpressionInMemory;
Exit;
end;
If Value.Word = 0 then
begin
GenerateInstruction_TwoBytes (MainOpCode, MODEM_OpCode);
Exit;
end;
end;
else begin
GenerateInstruction_TwoBytes (MainOpCode, MODEM_OpCode or rmDS_Displacement16);
GenerateOffsetReferenceForExpressionInMemory;
Exit;
end;
end;
end;
If Value.Integer = Value.ShortInt then
begin
GenerateInstruction_TwoBytes (MainOpCode, MODEM_OpCode or modMemoryDisp8);
GenerateInstruction_Byte (Value.Byte);
end else begin
GenerateInstruction_TwoBytes (MainOpCode, MODEM_OpCode or modMemoryDisp16);
GenerateOffsetReferenceForExpressionInMemory;
end;
end;
This procedure generates general instruction with register or memory reference.
Procedure TExpression.GenerateInstructionWithExpressionInMemOrReg (MainOpCode, MODEM_OpCode: Byte);
begin
Case Location of
elRegister: GenerateInstruction_TwoBytes (MainOpCode, modRegister or MODEM_OpCode or LocationData.Register)
else begin
GenerateSegmentOverride;
GenerateInstructionWithMemoryReference (MainOpCode, MODEM_OpCode);
end;
end;
end;
This procedure generates 8-bit arithmetic instruction with AL or 16-bit arithmetic instruction with AX
.
Procedure TExpression.GenerateArithmeticInstructionWith_ACC (OpCode: Byte);
begin
If Location = elConstant then
begin
If it16Bit in DataType then
begin
OpCode := OpCode or ArithmeticOp_AX_Immediate;
GenerateInstruction_Byte (OpCode);
GenerateInstruction_Word (Value.Word);
end else GenerateInstruction_TwoBytes (OpCode or ArithmeticOp_AL_Immediate, Value.Byte);
end else GenerateInstruction_8_16_bit (OpCode or reg__mod_rm, rAX);
end;
This procedure generates 16-bit arithmetic instruction with DX
.
Procedure TExpression.GenerateArithmeticInstructionWith_DX (OpCode: Byte);
begin
If Location = elConstant then
GenerateArithmeticInstruction_16BitReg_Immediate (modRegister or OpCode or rDX, Value.Word) else
GenerateInstructionWithExpressionInMemOrReg (OpCode or reg__mod_rm or OperandMode16Bit, rDX shl 3);
end;
This procedure generates arithmetic instruction with immediate value. If value is in range of ShortInt
then it generates shorter version of instruction.
Procedure TExpression.GenerateArithmeticInstructionWithImmediateValue (ImmediateValue: Integer; SecondOpCode: Byte);
begin
If it16Bit in DataType then
begin
If ImmediateValue = ShortInt (ImmediateValue) then
begin
GenerateInstructionWithExpressionInMemOrReg (ArithmeticOperation or SignExtendImmediateByte or
OperandMode16Bit, SecondOpCode);
GenerateInstruction_Byte (ImmediateValue);
end else begin
GenerateInstructionWithExpressionInMemOrReg (ArithmeticOperation or OperandMode16Bit, SecondOpCode);
GenerateInstruction_Word (ImmediateValue);
end;
end else begin
GenerateInstructionWithExpressionInMemOrReg (ArithmeticOperation, SecondOpCode);
GenerateInstruction_Byte (ImmediateValue);
end;
end;
This procedure generates instruction to move 8, 16 or 32-bit register to memory.
Procedure TExpression.GenerateInstruction_MOV_Memory_Register (Register: Byte);
begin
GenerateInstruction_16bit_MOV_Memory_Register (Register);
If it32Bit in DataType then
begin
Inc (Value.Offset, 2);
GenerateInstruction_16bit_MOV_Memory_Register (Register + 2);
Dec (Value.Offset, 2);
end;
end;
This procedure generates instruction to move memory to 8, 16 or 32-bit register.
Procedure TExpression.GenerateInstruction_16bit_MOV_Memory_Register (Register: Byte);
begin
If (Register = rAX) and (LocationData.Flags * [ofsDI, ofsBP] = []) then
begin
GenerateSegmentOverride;
If it16Bit in DataType then GenerateInstruction_Byte (MOV_Mem_AX)
else GenerateInstruction_Byte (MOV_Mem_AL);
GenerateOffsetReferenceForExpressionInMemory;
end else GenerateInstruction_8_16_bit (MOV_Operations, Register shl 3);
end;
This procedure generates general instruction with 8 or 16-bit register or memory reference.
Procedure TExpression.GenerateInstruction_8_16_bit (MainOpcode, SecondOpCode: Byte);
begin
If it16Bit in DataType then MainOpcode := MainOpcode or OperandMode16Bit;
GenerateInstructionWithExpressionInMemOrReg (MainOpcode, SecondOpCode);
end;
This procedure generates instruction to move immediate value to memory.
Procedure TExpression.GenerateInstruction_MOV_Memory_Immediate (ImmediateValue: Word);
begin
GenerateInstruction_8_16_bit (MOV_RegisterOrMemory_Immediate, modMemory);
If it16Bit in DataType then GenerateInstruction_Word (ImmediateValue) else GenerateInstruction_Byte (Lo (ImmediateValue));
end;
This procedure generates instruction for far call. It marks unit for 80×87 opcodes (if they are used) and generates CALL FAR
instruction for system procedure. Input parameter is index in Procedures
symbol table in the system unit.
Procedure GenerateInstruction_CALL_FAR (SysProc: Word);
begin
If SysProc and $8000 <> 0 then MarkUnitForInstructions80x87;
ES_DI_PointerDestroyed;
GenerateInstruction_Byte (CALL_FAR);
GenerateReference (SystemUnitSegment, SysProc shl 1, 0, [rfSegment, rfOffset]);
end;
This procedure generates instruction to access the stack frame.
Procedure GenerateInstructionForStackFrameDisplacement (MainOpCode, SecondOpCode: Byte; Displacement: Integer);
begin
If (SecondOpCode and rmMask <> rmSS_BP_Displacement) and (Displacement = 0) then
GenerateInstruction_TwoBytes (MainOpCode, SecondOpCode) else
begin
If Displacement = ShortInt (Displacement) then
begin
GenerateInstruction_TwoBytes (MainOpCode, SecondOpCode or modMemoryDisp8);
GenerateInstruction_Byte (Displacement);
end else begin
GenerateInstruction_TwoBytes (MainOpCode, SecondOpCode or modMemoryDisp16);
GenerateInstruction_Word (Displacement);
end;
end;
end;
This procedure generates instruction to push immediate value.
Procedure GenerateInstruction_PUSH_Immediate (PushWord: Integer);
begin
If PushWord = ShortInt (Lo (PushWord)) then GenerateInstruction_TwoBytes (PUSH_SignedByte, Lo (PushWord))
else begin
GenerateInstruction_Byte (PUSH_Word);
GenerateInstruction_Word (PushWord);
end;
end;
This procedure generates code for arithmetic instruction with immediate value.
Procedure GenerateArithmeticInstruction_16BitReg_Immediate (SecondOpCode: Byte; Value: Integer);
begin
If Value = ShortInt (Value) then
begin
GenerateInstruction_TwoBytes (ArithmeticOperation or SignExtendImmediateByte or OperandMode16Bit, SecondOpCode);
GenerateInstruction_Byte (Lo (Value));
end else begin
GenerateInstruction_TwoBytes (ArithmeticOperation or OperandMode16Bit, SecondOpCode);
GenerateInstruction_Word (Value);
end;
end;