RealMode Segmentation questions for Emulator
Re: RealMode Segmentation questions for Emulator
I don't know what's wrong. It's either some bug in my C# compiler that is causing problems which is possible but I haven't noticed any, or there is some bug in the emulator somewhere. The only one I found was Bochs at some point reads from port 986 as a byte with the value of 1 from the port. Where when my os does that same instruction at the same point, its getting a 0. I guess ill keep investigating this but wow this is annoying, I'm half tempted to just use V8086 but I really wish to make this work because then when I decide to write my 64bit version I can still interface with the Bios.
-
- Member
- Posts: 5604
- Joined: Mon Mar 25, 2013 7:01 pm
Re: RealMode Segmentation questions for Emulator
You mean port 0x3DA? That's the VGA status register. Does the BIOS behavior change depending on the value it reads? If it does, it should loop until it sees a specific value; you can then re-align the logs after the BIOS reads the value that causes it to end the loop. If it doesn't, you can ignore that difference entirely.PearOs wrote:The only one I found was Bochs at some point reads from port 986 as a byte with the value of 1 from the port. Where when my os does that same instruction at the same point, its getting a 0.
Re: RealMode Segmentation questions for Emulator
I don't think it loops and yes its 0x3DA. I had my os print out everytime it would emulate a IN Opcode which is the one that used that, and it was only called like 3 times using 0x3DA. But my Os gets a value of 0 the first time it does a IN Opcode and Bochs gets a 1. Other than that every single Opcode, is the same and all the registers up till that point. So what does that mean and why does this happen? Sometimes though I get a 1, and sometimes I get a 0? Yet it still doesn't work.Octocontrabass wrote:You mean port 0x3DA? That's the VGA status register. Does the BIOS behavior change depending on the value it reads? If it does, it should loop until it sees a specific value; you can then re-align the logs after the BIOS reads the value that causes it to end the loop. If it doesn't, you can ignore that difference entirely.PearOs wrote:The only one I found was Bochs at some point reads from port 986 as a byte with the value of 1 from the port. Where when my os does that same instruction at the same point, its getting a 0.
-
- Member
- Posts: 5604
- Joined: Mon Mar 25, 2013 7:01 pm
Re: RealMode Segmentation questions for Emulator
Port 0x3DA has more than one use, but since the BIOS ignores its value, it must be using it to reset a latch in the video card. (See the description for port 0x3C0 if you want to know exactly what that means.)
In any case, it should be safe to ignore any differences in the value read from port 0x3DA.
In any case, it should be safe to ignore any differences in the value read from port 0x3DA.
Re: RealMode Segmentation questions for Emulator
Ok thanks for the reply. So here's what going on right now, Ill update you on the difference between bochs and my os as soon as my debugger finishes. It takes forever sadly. Anyways, so I tried to set 640x480Octocontrabass wrote:Port 0x3DA has more than one use, but since the BIOS ignores its value, it must be using it to reset a latch in the video card. (See the description for port 0x3C0 if you want to know exactly what that means.)
In any case, it should be safe to ignore any differences in the value read from port 0x3DA.
The screen goes black, it says it set it in Bochs console "demension update x=640 y=400 fontheight=16 and then is sets
1024x400 or something close then it sets 640x400 again but with fontheight=0, then the screen goes all weird with red and blue and then
I reach CLD which is my stopping point for now.
Bochs however, will set the video to 640x400, screen is black and then stops at CLD. So something with mine is messed up and its right after the first read from that IOPort.
Do you know what could be causing this?
The register values I am using are:
Ah = 0
Al = 13
For when I call INT10h
Thanks, Matt
Last edited by PearOs on Sun Dec 15, 2013 1:43 am, edited 1 time in total.
-
- Member
- Posts: 5604
- Joined: Mon Mar 25, 2013 7:01 pm
Re: RealMode Segmentation questions for Emulator
If more than one piece of software is trying to access those ports at the same time, or there are any duplicated or missing reads/writes, the video BIOS will not work.
When you find the next difference in the debugger logs, be sure to post it. I might think of something else to check.
When you find the next difference in the debugger logs, be sure to post it. I might think of something else to check.
Re: RealMode Segmentation questions for Emulator
I don't know if this is possible but my debugger says that there is no difference at all between any of the instructions. I am so ready to rage quit.Octocontrabass wrote:If more than one piece of software is trying to access those ports at the same time, or there are any duplicated or missing reads/writes, the video BIOS will not work.
When you find the next difference in the debugger logs, be sure to post it. I might think of something else to check.
Now I have one idea, my OS copies from 0x0 to 1MB of Ram, allocates an array and shoves all the bytes in there. Then my emulator uses that for its Ram, so any Memory Get's or Set's go to that Array. Could doing this be the problem? I don't think the Card relies on any physical memory within 0x0 to 1MB that the code changes, or does it?
Edit: Well I rewrote the code that handles memory access and gave it the raw address's and no change. Same bug keeps happening. I'm almost wondering if a jump condition isn't working right, though it should have showed up in the debugger.
-
- Member
- Posts: 5604
- Joined: Mon Mar 25, 2013 7:01 pm
Re: RealMode Segmentation questions for Emulator
The strange mode switches sound like you haven't implemented the "in" and "out" instructions properly. What does your code for those look like?
Re: RealMode Segmentation questions for Emulator
I attached the output. My code for IN and OUT is this. Its C# and a little complex.
Emulate is called when those Instructions are ran.
IN:
OUT:
Opcode
Operand:
I think I did it right, but maybe not.
Thanks a ton, Matt
Emulate is called when those Instructions are ran.
IN:
Code: Select all
/// <summary>
/// The outline of a IN Opcode.
/// </summary>
public class IN : Opcode
{
/// <summary>
/// A new IN Opcode.
/// </summary>
/// <param name="value"></param>
/// <param name="bits16"></param>
public IN(Register.Type reg, ushort value, bool bits16)
{
base.Type = OpCodeType.Opcode_IN;
base.Operand1.Type = Operand.OperandType.Operand_Register;
base.Operand1.Value = (uint)reg;
if (bits16)
{
base.Operand2.Type = Operand.OperandType.Operand_Short;
}
else
{
base.Operand2.Type = Operand.OperandType.Operand_Byte;
}
base.Operand2.Value = value;
base.Supported = true;
base.EmulateCallback = Action.ConvertFromAction(Emulate);
}
/// <summary>
/// A new IN Opcode.
/// </summary>
/// <param name="reg"></param>
/// <param name="reg2"></param>
public IN(Register.Type reg, Register.Type reg2)
{
base.Type = OpCodeType.Opcode_IN;
base.Operand1.Type = Operand.OperandType.Operand_Register;
base.Operand1.Value = (uint)reg;
base.Operand2.Type = Operand.OperandType.Operand_Register;
base.Operand2.Value = (uint)reg2;
base.Supported = true;
base.EmulateCallback = Action.ConvertFromAction(Emulate);
}
/// <summary>
/// Emulates a IN Opcode.
/// </summary>
/// <param name="x8086"></param>
/// <param name="op"></param>
public void Emulate()
{
ushort port = Operand2.GetValue(this);
//Now see if the operand is a 8bit register or a 16bit register.
if (Operand1.IsWord())
{
ushort value = (ushort)Hardware.IOPort.Read_16((short)port);
Operand1.SetValue(value, this);
}
else
{
byte value = Hardware.IOPort.Read_8((short)port);
Operand1.SetValue(value, this);
}
}
}
Code: Select all
/// <summary>
/// The outline of a OUT Opcode.
/// </summary>
public class OUT : Opcode
{
/// <summary>
/// A new OUT Opcode.
/// </summary>
/// <param name="value"></param>
/// <param name="bits16"></param>
/// <param name="reg"></param>
public OUT(ushort value, bool bits16, Register.Type reg)
{
base.Type = OpCodeType.Opcode_OUT;
if (bits16)
{
base.Operand1.Type = Operand.OperandType.Operand_Short;
}
else
{
base.Operand1.Type = Operand.OperandType.Operand_Byte;
}
base.Operand1.Value = value;
base.Operand2.Type = Operand.OperandType.Operand_Register;
base.Operand2.Value = (uint)reg;
base.Supported = true;
base.EmulateCallback = Action.ConvertFromAction(Emulate);
}
/// <summary>
/// A new OUT Opcode.
/// </summary>
/// <param name="reg"></param>
/// <param name="reg2"></param>
public OUT(Register.Type reg, Register.Type reg2)
{
base.Type = OpCodeType.Opcode_OUT;
base.Operand1.Type = Operand.OperandType.Operand_Register;
base.Operand1.Value = (uint)reg;
base.Operand2.Type = Operand.OperandType.Operand_Register;
base.Operand2.Value = (uint)reg2;
base.Supported = true;
base.EmulateCallback = Action.ConvertFromAction(Emulate);
}
/// <summary>
/// Emulates a OUT Opcode.
/// </summary>
/// <param name="x8086"></param>
/// <param name="op"></param>
public void Emulate()
{
ushort port = Operand1.GetValue(this);
ushort value = Operand2.GetValue(this);
if (Operand2.IsWord())
{
Hardware.IOPort.Write_16((short)port, (short)value);
}
else
{
Hardware.IOPort.Write_8((short)port, (byte)value);
}
}
}
Opcode
Code: Select all
/// <summary>
/// The class that outlines what a Opcode is.
/// This is used by the emulator a lot.
///
/// Opcodes look like this:
///
///[Prefix] Opcode [Address Mode] [Low Disp] [High Disp] [Low data] [High data]
///
/// Course this changes a lot. The best source for information on 8086 opcodes was here:
/// http://umcs.maine.edu/~cmeadow/courses/cos335/8086-instformat.pdf
/// </summary>
public class Opcode
{
/// <summary>
/// The different types of Opcodes.
/// </summary>
public enum OpCodeType
{
Opcode_Unknown,
Opcode_Mov,
Opcode_Pop,
Opcode_Push,
Opcode_Inc,
Opcode_Dec,
Opcode_Add,
Opcode_Sub,
Opcode_Cli,
Opcode_Sti,
Opcode_Std,
Opcode_Cld,
Opcode_Clc,
Opcode_Stc,
Opcode_Test,
Opcode_And,
Opcode_Or,
Opcode_Adc,
Opcode_Sbb,
Opcode_Daa,
Opcode_Das,
Opcode_Xor,
Opcode_AAA,
Opcode_Cmp,
Opcode_AAS,
Opcode_Pusha,
Opcode_Popa,
Opcode_Jo,
Opcode_Jno,
Opcode_Jb,
Opcode_Jnb,
Opcode_Jz,
Opcode_Jnz,
Opcode_Jbe,
Opcode_Ja,
Opcode_Js,
Opcode_Jns,
Opcode_Jpe,
Opcode_Jpo,
Opcode_Jl,
Opcode_Jge,
Opcode_Jle,
Opcode_Jg,
Opcode_XCHG,
Opcode_LEA,
Opcode_Nop,
Opcode_CBW,
Opcode_CWD,
Opcode_Call,
Opcode_Wait,
Opcode_Pushf,
Opcode_Popf,
Opcode_Sahf,
Opcode_Lahf,
Opcode_Movsb,
Opcode_Movsw,
Opcode_Cmpsb,
Opcode_Cmpsw,
Opcode_Stosb,
Opcode_Stosw,
Opcode_Lodsb,
Opcode_Lodsw,
Opcode_Ret,
Opcode_Retf,
Opcode_INT,
Opcode_IRET,
Opcode_IN,
Opcode_OUT,
Opcode_Jmp,
Opcode_Hlt,
Opcode_Rol,
Opcode_ROR,
Opcode_RCR,
Opcode_SHL,
Opcode_SHR,
Opcode_SAR,
Opcode_RCL,
Opcode_NOT,
Opcode_NEG,
Opcode_MUL,
Opcode_IMUL,
Opcode_DIV,
Opcode_IDIV,
};
/// <summary>
/// The different displacement registers for certain opcodes.
/// </summary>
public enum OpDisp
{
SI,
DI,
BP,
BX,
BX_PLUS_SI,
BP_PLUS_SI,
BX_PLUS_DI,
BP_PLUS_DI,
None,
}
/// <summary>
/// The Opcode Prefixes Availible.
///
/// If no Segment Prefix, the defualt segment DS is used.
/// If the BP register is used the defualt will be SS rather than DS.
/// A segment prefix will override that condition as well.
/// </summary>
public enum OpcodePrefix
{
Prefix_OperandSizeOverride = 0x66,
Prefix_AddressSizeOverride = 0x67,
Prefix_Lock = 0xF0,
Prefix_SegmentOverride_CS = 0x2E,
Prefix_SegmentOverride_DS = 0x3E,
Prefix_SegmentOverride_ES = 0x26,
Prefix_SegmentOverride_SS = 0x36,
Prefix_Repetition_REPREPEREPZ = 0xF3,
Prefix_Repetition_REPNEREPNZ = 0xF2,
Prefix_None = 0,
};
/// <summary>
/// The type of Opcode this is
/// </summary>
public OpCodeType Type = OpCodeType.Opcode_Unknown;
/// <summary>
/// The Opcodes defualt segment to use.
/// </summary>
public Register.Type Segment = Register.Type.DS;
/// <summary>
/// Operand1 of the Opcode.
/// </summary>
public Operand Operand1 = new Operand();
/// <summary>
/// Operand2 of the Opcode.
/// </summary>
public Operand Operand2 = new Operand();
/// <summary>
/// The Opcode's Prefix.
/// </summary>
public OpcodePrefix Prefix = OpcodePrefix.Prefix_None;
/// <summary>
/// The Opcodes Displacement. This is by defualt None.
/// </summary>
public OpDisp Displacement = OpDisp.None;
/// <summary>
/// Whether or not this Opcode is supported.
/// </summary>
public bool Supported = false;
/// <summary>
/// The Emultion address used for Calling the Opcodes Emulation method.
/// </summary>
public uint EmulateCallback;
/// <summary>
/// The CPU That his Opcode belongs to.
/// </summary>
public CPU x8086;
/// <summary>
/// Called when a Opcode has been created.
/// </summary>
public Opcode()
{
((Object)(object)this).DestroyEvent.Attach(Destroy);
}
/// <summary>
/// Called when we need to destroy the Opcode.
/// </summary>
private void Destroy()
{
//We just have to destroy our objects and thats it.
Object.Destroy(Operand1);
Object.Destroy(Operand2);
}
/// <summary>
/// Returns a String That Represents this Opcode.
/// </summary>
/// <returns></returns>
public string GetString()
{
string str = ConvertOpcode(this.Type);
((String)(object)str).AddByte((byte)' ');
//Print out the prefix
str = String.Append(str, ConvertPrefix(this.Prefix), true);
((String)(object)str).AddByte((byte)' ');
//Now print out Operand 1's Data.
str = String.Append(str, Operand1.GetString(this), true);
//Now print a ,
((String)(object)str).AddByte((byte)',');
//Now print our Operand 2's Data.
str = String.Append(str, Operand2.GetString(this), true);
//Return our string.
return str;
}
/// <summary>
/// Converts a OpcodeType to a string.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static string ConvertOpcode(OpCodeType type)
{
switch (type)
{
case OpCodeType.Opcode_AAA:
return "AAA";
case OpCodeType.Opcode_AAS:
return "AAS";
case OpCodeType.Opcode_Adc:
return "ADC";
case OpCodeType.Opcode_Add:
return "ADD";
case OpCodeType.Opcode_And:
return "AND";
case OpCodeType.Opcode_Call:
return "CALL";
case OpCodeType.Opcode_CBW:
return "CBW";
case OpCodeType.Opcode_Clc:
return "CLC";
case OpCodeType.Opcode_Cld:
return "CLD";
case OpCodeType.Opcode_Cli:
return "CLI";
case OpCodeType.Opcode_Cmp:
return "CMP";
case OpCodeType.Opcode_Cmpsb:
return "CMPSB";
case OpCodeType.Opcode_Cmpsw:
return "CMPSW";
case OpCodeType.Opcode_CWD:
return "CWD";
case OpCodeType.Opcode_Daa:
return "DAA";
case OpCodeType.Opcode_Das:
return "DAS";
case OpCodeType.Opcode_Dec:
return "DEC";
case OpCodeType.Opcode_DIV:
return "DIV";
case OpCodeType.Opcode_Hlt:
return "HLT";
case OpCodeType.Opcode_IDIV:
return "IDIV";
case OpCodeType.Opcode_IMUL:
return "IMUL";
case OpCodeType.Opcode_IN:
return "IN";
case OpCodeType.Opcode_Inc:
return "INC";
case OpCodeType.Opcode_INT:
return "INT";
case OpCodeType.Opcode_IRET:
return "IRET";
case OpCodeType.Opcode_Ja:
return "JA";
case OpCodeType.Opcode_Jb:
return "JB";
case OpCodeType.Opcode_Jbe:
return "JBE";
case OpCodeType.Opcode_Jg:
return "JG";
case OpCodeType.Opcode_Jge:
return "JGE";
case OpCodeType.Opcode_Jl:
return "JL";
case OpCodeType.Opcode_Jle:
return "JLE";
case OpCodeType.Opcode_Jmp:
return "JMP";
case OpCodeType.Opcode_Jnb:
return "JNB";
case OpCodeType.Opcode_Jno:
return "JNO";
case OpCodeType.Opcode_Jns:
return "JNS";
case OpCodeType.Opcode_Jnz:
return "JNZ";
case OpCodeType.Opcode_Jo:
return "JO";
case OpCodeType.Opcode_Jpe:
return "JPE";
case OpCodeType.Opcode_Jpo:
return "JPO";
case OpCodeType.Opcode_Js:
return "JS";
case OpCodeType.Opcode_Jz:
return "JZ";
case OpCodeType.Opcode_Lahf:
return "LAHF";
case OpCodeType.Opcode_LEA:
return "LEA";
case OpCodeType.Opcode_Lodsb:
return "LODSB";
case OpCodeType.Opcode_Lodsw:
return "LODSW";
case OpCodeType.Opcode_Mov:
return "MOV";
case OpCodeType.Opcode_Movsb:
return "MOVSB";
case OpCodeType.Opcode_Movsw:
return "MOVSW";
case OpCodeType.Opcode_MUL:
return "MUL";
case OpCodeType.Opcode_NEG:
return "NEG";
case OpCodeType.Opcode_Nop:
return "NOP";
case OpCodeType.Opcode_NOT:
return "NOT";
case OpCodeType.Opcode_Or:
return "OR";
case OpCodeType.Opcode_OUT:
return "OUT";
case OpCodeType.Opcode_Pop:
return "POP";
case OpCodeType.Opcode_Popa:
return "POPA";
case OpCodeType.Opcode_Popf:
return "POPF";
case OpCodeType.Opcode_Push:
return "PUSH";
case OpCodeType.Opcode_Pusha:
return "PUSHA";
case OpCodeType.Opcode_Pushf:
return "PUSHF";
case OpCodeType.Opcode_RCL:
return "RCL";
case OpCodeType.Opcode_RCR:
return "RCR";
case OpCodeType.Opcode_Ret:
return "RET";
case OpCodeType.Opcode_Retf:
return "RETF";
case OpCodeType.Opcode_Rol:
return "ROL";
case OpCodeType.Opcode_ROR:
return "ROR";
case OpCodeType.Opcode_Sahf:
return "SAHF";
case OpCodeType.Opcode_SAR:
return "SAR";
case OpCodeType.Opcode_Sbb:
return "SBB";
case OpCodeType.Opcode_SHL:
return "SHL";
case OpCodeType.Opcode_SHR:
return "SHR";
case OpCodeType.Opcode_Stc:
return "STC";
case OpCodeType.Opcode_Std:
return "STD";
case OpCodeType.Opcode_Sti:
return "STI";
case OpCodeType.Opcode_Stosb:
return "STOSB";
case OpCodeType.Opcode_Stosw:
return "STOSW";
case OpCodeType.Opcode_Sub:
return "SUB";
case OpCodeType.Opcode_Test:
return "TEST";
case OpCodeType.Opcode_Unknown:
return "Unkown";
case OpCodeType.Opcode_Wait:
return "WAIT";
case OpCodeType.Opcode_XCHG:
return "XCHG";
case OpCodeType.Opcode_Xor:
return "XOR";
}
//Bad if we reach here.
return "Invalid Opcode";
}
/// <summary>
/// Converts a Register Type to a string.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static string ConvertRegister(Register.Type type)
{
switch (type)
{
case Register.Type.AH:
return "AH";
case Register.Type.AL:
return "AL";
case Register.Type.AX:
return "AX";
case Register.Type.BH:
return "BH";
case Register.Type.BL:
return "BL";
case Register.Type.BP:
return "BP";
case Register.Type.BX:
return "BX";
case Register.Type.CH:
return "CH";
case Register.Type.CL:
return "CL";
case Register.Type.CS:
return "CS";
case Register.Type.CX:
return "CX";
case Register.Type.DH:
return "DH";
case Register.Type.DI:
return "DI";
case Register.Type.DL:
return "DL";
case Register.Type.DS:
return "DS";
case Register.Type.DX:
return "DX";
case Register.Type.ES:
return "ES";
case Register.Type.None:
return "None";
case Register.Type.SI:
return "SI";
case Register.Type.SP:
return "SP";
case Register.Type.SS:
return "SS";
}
return "Unkown";
}
/// <summary>
/// Converts a Operand Type to a string.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static string ConvertOperandType(Operand.OperandType type)
{
switch (type)
{
case Operand.OperandType.Operand_Byte:
return "Byte";
case Operand.OperandType.Operand_Byte_Memory_Byte:
return "Byte";
case Operand.OperandType.Operand_Byte_Memory_Short:
return "Byte";
case Operand.OperandType.Operand_Register:
return "Register";
case Operand.OperandType.Operand_Short:
return "Short";
case Operand.OperandType.Operand_Short_Memory_Short:
return "Short";
case Operand.OperandType.Operand_Short_Memory_Byte:
return "Short";
case Operand.OperandType.Operand_Unkown:
return "Unkown";
}
return "Unkown";
}
/// <summary>
/// Convets a Prefix Type to a string.
/// </summary>
/// <param name="prefix"></param>
/// <returns></returns>
public static string ConvertPrefix(Opcode.OpcodePrefix prefix)
{
switch (prefix)
{
case OpcodePrefix.Prefix_Lock:
return "Lock";
}
return "";
}
/// <summary>
/// Converts a Opcode Displacement to a String.
/// </summary>
/// <param name="disp"></param>
/// <returns></returns>
public static string ConvertDisplacement(OpDisp disp)
{
switch (disp)
{
case OpDisp.BP:
return "BP";
case OpDisp.BP_PLUS_DI:
return "BP+DI";
case OpDisp.BP_PLUS_SI:
return "BP+SI";
case OpDisp.BX:
return "BX";
case OpDisp.BX_PLUS_DI:
return "BX+DI";
case OpDisp.BX_PLUS_SI:
return "BX+SI";
case OpDisp.DI:
return "DI";
case OpDisp.None:
return "";
case OpDisp.SI:
return "SI";
}
return "";
}
/// <summary>
/// Returns whether or not the Opcode is a word. (Used by some opcodes)
/// </summary>
/// <returns></returns>
public bool IsWord()
{
if (Operand1.Type == Operand.OperandType.Operand_Register && Register.IsWord((Register.Type)Operand1.Value))
{
return true;
}
else if (Operand1.Type == Operand.OperandType.Operand_Short)
{
return true;
}
else if (Operand1.Type == Operand.OperandType.Operand_Short_Memory_Short)
{
return true;
}
else if (Operand1.Type == Operand.OperandType.Operand_Short_Memory_Byte)
{
return true;
}
else if (Operand2.Type == Operand.OperandType.Operand_Register && Register.IsWord((Register.Type)Operand2.Value))
{
return true;
}
else if (Operand2.Type == Operand.OperandType.Operand_Short)
{
return true;
}
else if (Operand2.Type == Operand.OperandType.Operand_Short_Memory_Byte)
{
return true;
}
else if (Operand2.Type == Operand.OperandType.Operand_Short_Memory_Short)
{
return true;
}
else
{
return false;
}
}
}
Operand:
Code: Select all
/// <summary>
/// A Operand is basically a parameter passed with a Opcode.
/// </summary>
public class Operand
{
/// <summary>
/// The Operand Types Availible
/// </summary>
public enum OperandType
{
Operand_Unkown,
Operand_Register,
Operand_Byte,
Operand_Byte_Memory_Byte,
Operand_Byte_Memory_Short,
Operand_Short,
Operand_Short_Memory_Byte,
Operand_Short_Memory_Short,
};
/// <summary>
/// The type of Operand this is.
/// </summary>
public OperandType Type = OperandType.Operand_Unkown;
/// <summary>
/// The Value of the Operand.
/// </summary>
public uint Value = 0;
/// <summary>
/// Returns the value for a Operand whether its a Register, a Immediate byte, or a byte in Memory.
/// </summary>
/// <param name="x8086"></param>
/// <returns></returns>
public ushort GetValue(Opcode op)
{
#region " Gets Offset "
uint offset = 0;
if (op.Displacement != Opcode.OpDisp.None)
{
switch (op.Displacement)
{
case Opcode.OpDisp.BP:
offset = op.x8086.GetRegister(Register.Type.BP);
break;
case Opcode.OpDisp.BP_PLUS_DI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BP) + op.x8086.GetRegister(Register.Type.DI));
break;
case Opcode.OpDisp.BP_PLUS_SI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BP) + op.x8086.GetRegister(Register.Type.SI));
break;
case Opcode.OpDisp.BX:
offset = op.x8086.GetRegister(Register.Type.BX);
break;
case Opcode.OpDisp.BX_PLUS_DI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BX) + op.x8086.GetRegister(Register.Type.DI));
break;
case Opcode.OpDisp.BX_PLUS_SI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BX) + op.x8086.GetRegister(Register.Type.SI));
break;
case Opcode.OpDisp.DI:
offset = op.x8086.GetRegister(Register.Type.DI);
break;
case Opcode.OpDisp.SI:
offset = op.x8086.GetRegister(Register.Type.SI);
break;
}
}
#endregion
//Get the value.
uint address = (ushort)this.Value;
//Now check a few things.
if (this.Type == OperandType.Operand_Register)
{
return (ushort)op.x8086.GetRegister((Register.Type)this.Value);
}
else if (this.Type == OperandType.Operand_Byte || this.Type == OperandType.Operand_Short)
{
return (ushort)this.Value;
}
else if (this.Type == OperandType.Operand_Byte_Memory_Byte || this.Type == OperandType.Operand_Short_Memory_Byte)
{
//Check for a displacement.
if (op.Displacement != Opcode.OpDisp.None)
{
//Now we need to check for a negative.
if (address >= 0x80)
{
//Negative.
address = (uint)(0x80 - (address - 0x80));
//Now we take the offset and subtract the value from it.
address = (uint)(offset - address);
}
else
{
//Positive.
//So we simply add the offset.
address = (uint)(address + offset);
}
}
else
{
//Now we need to check for a negative.
if (address >= 0x80)
{
//Negative.
address = (uint)(0x80 - (address - 0x80));
}
}
}
else if (this.Type == OperandType.Operand_Byte_Memory_Short || this.Type == OperandType.Operand_Short_Memory_Short)
{
//Check for a displacement.
if (op.Displacement != Opcode.OpDisp.None)
{
//Now we need to check for a negative.
if (address >= 0x8000)
{
//Negative.
address = (uint)(0x8000 - (address - 0x8000));
//Now we take the offset and subtract the value from it.
address = (uint)(offset - address);
}
else
{
//Positive.
//So we simply add the offset.
address = (uint)(address + offset);
}
}
else
{
//Now we need to check for a negative.
if (address >= 0x8000)
{
//Negative.
address = (uint)(0x8000 - (address - 0x8000));
}
}
}
else
{
return 0;
}
//Check for General Protection Fault.
if (this.IsWord())
{
if (((address + 2) & 0xFFFF) > 0xFFFF)
{
Console.WriteLine("General Protection Fault!");
Console.ReadLine();
}
}
else
{
if (((address + 1) & 0xFFFF) > 0xFFFF)
{
Console.WriteLine("General Protection Fault!");
Console.ReadLine();
}
}
//If we get here it means were working with memory so lets calculate our address here.
uint segment = 0;
//See if we have a prefix.
if (op.Prefix != Opcode.OpcodePrefix.Prefix_None)
{
switch (op.Prefix)
{
case Opcode.OpcodePrefix.Prefix_SegmentOverride_CS:
//We have to use CS.
segment = (uint)(op.x8086.Code_Seg.Low16 * 0x10);
break;
case Opcode.OpcodePrefix.Prefix_SegmentOverride_DS:
//We have to use DS.
segment = (uint)(op.x8086.Data_Seg.Low16 * 0x10);
break;
case Opcode.OpcodePrefix.Prefix_SegmentOverride_ES:
//We have to use ES.
segment = (uint)(op.x8086.Extra_Seg.Low16 * 0x10);
break;
case Opcode.OpcodePrefix.Prefix_SegmentOverride_SS:
//We have to use SS.
segment = (uint)(op.x8086.Stack_Seg.Low16 * 0x10);
break;
}
}
else
{
//Now if were using BP as a displacement then we have to use SS rather than DS.
if (op.Displacement == Opcode.OpDisp.BP || op.Displacement == Opcode.OpDisp.BP_PLUS_DI || op.Displacement == Opcode.OpDisp.BP_PLUS_SI)
{
//Use ss as the segment.
segment = (uint)(op.x8086.Stack_Seg.Low16 * 0x10);
}
else
{
//Use the segment the opcode specifies.
switch (op.Segment)
{
case Register.Type.CS:
segment = (uint)(op.x8086.Code_Seg.Low16 * 0x10);
break;
case Register.Type.DS:
segment = (uint)(op.x8086.Data_Seg.Low16 * 0x10);
break;
case Register.Type.SS:
segment = (uint)(op.x8086.Stack_Seg.Low16 * 0x10);
break;
}
}
}
//Now just return what we want based on the Memory.
switch (this.Type)
{
case OperandType.Operand_Byte_Memory_Byte:
return (ushort)op.x8086.GetMemoryByte((uint)(segment + (address & 0xFFFF)));
case OperandType.Operand_Byte_Memory_Short:
return (ushort)op.x8086.GetMemoryByte((uint)(segment + (address & 0xFFFF)));
case OperandType.Operand_Short_Memory_Byte:
return (ushort)op.x8086.GetMemoryWord((uint)(segment + (address & 0xFFFF)));
case OperandType.Operand_Short_Memory_Short:
return (ushort)op.x8086.GetMemoryWord((uint)(segment + (address & 0xFFFF)));
}
//Otherwise return 0.
return 0;
}
/// <summary>
/// Returns the value for a Operand whether its a Register, a Immediate byte, or a byte in Memory.
/// </summary>
/// <param name="x8086"></param>
/// <returns></returns>
public ushort GetSegAddress(Opcode op)
{
#region " Gets Offset "
uint offset = 0;
if (op.Displacement != Opcode.OpDisp.None)
{
switch (op.Displacement)
{
case Opcode.OpDisp.BP:
offset = op.x8086.GetRegister(Register.Type.BP);
break;
case Opcode.OpDisp.BP_PLUS_DI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BP) + op.x8086.GetRegister(Register.Type.DI));
break;
case Opcode.OpDisp.BP_PLUS_SI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BP) + op.x8086.GetRegister(Register.Type.SI));
break;
case Opcode.OpDisp.BX:
offset = op.x8086.GetRegister(Register.Type.BX);
break;
case Opcode.OpDisp.BX_PLUS_DI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BX) + op.x8086.GetRegister(Register.Type.DI));
break;
case Opcode.OpDisp.BX_PLUS_SI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BX) + op.x8086.GetRegister(Register.Type.SI));
break;
case Opcode.OpDisp.DI:
offset = op.x8086.GetRegister(Register.Type.DI);
break;
case Opcode.OpDisp.SI:
offset = op.x8086.GetRegister(Register.Type.SI);
break;
}
}
#endregion
//Get the value.
uint address = (ushort)this.Value;
//Now check a few things.
if (this.Type == OperandType.Operand_Byte_Memory_Byte || this.Type == OperandType.Operand_Short_Memory_Byte)
{
//Check for a displacement.
if (op.Displacement != Opcode.OpDisp.None)
{
//Now we need to check for a negative.
if (address >= 0x80)
{
//Negative.
address = (uint)(0x80 - (address - 0x80));
//Now we take the offset and subtract the value from it.
address = (uint)(offset - address);
}
else
{
//Positive.
//So we simply add the offset.
address = (uint)(address + offset);
}
}
else
{
//Now we need to check for a negative.
if (address >= 0x80)
{
//Negative.
address = (uint)(0x80 - (address - 0x80));
}
}
}
else if (this.Type == OperandType.Operand_Byte_Memory_Short || this.Type == OperandType.Operand_Short_Memory_Short)
{
//Check for a displacement.
if (op.Displacement != Opcode.OpDisp.None)
{
//Now we need to check for a negative.
if (address >= 0x8000)
{
//Negative.
address = (uint)(0x8000 - (address - 0x8000));
//Now we take the offset and subtract the value from it.
address = (uint)(offset - address);
}
else
{
//Positive.
//So we simply add the offset.
address = (uint)(address + offset);
}
}
else
{
//Now we need to check for a negative.
if (address >= 0x8000)
{
//Negative.
address = (uint)(0x8000 - (address - 0x8000));
}
}
}
else
{
return 0;
}
//Check for General Protection Fault.
if (this.IsWord())
{
if (((address + 2) & 0xFFFF) > 0xFFFF)
{
Console.WriteLine("General Protection Fault!");
Console.ReadLine();
}
}
else
{
if (((address + 1) & 0xFFFF) > 0xFFFF)
{
Console.WriteLine("General Protection Fault!");
Console.ReadLine();
}
}
//If we get here it means were working with memory so lets calculate our address here.
uint segment = 0;
//See if we have a prefix.
if (op.Prefix != Opcode.OpcodePrefix.Prefix_None)
{
switch (op.Prefix)
{
case Opcode.OpcodePrefix.Prefix_SegmentOverride_CS:
//We have to use CS.
segment = (uint)(op.x8086.Code_Seg.Low16 * 0x10);
break;
case Opcode.OpcodePrefix.Prefix_SegmentOverride_DS:
//We have to use DS.
segment = (uint)(op.x8086.Data_Seg.Low16 * 0x10);
break;
case Opcode.OpcodePrefix.Prefix_SegmentOverride_ES:
//We have to use ES.
segment = (uint)(op.x8086.Extra_Seg.Low16 * 0x10);
break;
case Opcode.OpcodePrefix.Prefix_SegmentOverride_SS:
//We have to use SS.
segment = (uint)(op.x8086.Stack_Seg.Low16 * 0x10);
break;
}
}
else
{
//Now if were using BP as a displacement then we have to use SS rather than DS.
if (op.Displacement == Opcode.OpDisp.BP || op.Displacement == Opcode.OpDisp.BP_PLUS_DI || op.Displacement == Opcode.OpDisp.BP_PLUS_SI)
{
//Use ss as the segment.
segment = (uint)(op.x8086.Stack_Seg.Low16 * 0x10);
}
else
{
//Use the segment the opcode specifies.
switch (op.Segment)
{
case Register.Type.CS:
segment = (uint)(op.x8086.Code_Seg.Low16 * 0x10);
break;
case Register.Type.DS:
segment = (uint)(op.x8086.Data_Seg.Low16 * 0x10);
break;
case Register.Type.SS:
segment = (uint)(op.x8086.Stack_Seg.Low16 * 0x10);
break;
}
}
}
//Now just return what we want based on the Memory.
uint physical = (uint)(segment + (address & 0xFFFF));
return (ushort)(physical - segment);
}
/// <summary>
/// Sets a Value into the Operand whether its a register or memory this will do the real thing.
/// </summary>
/// <param name="x8086"></param>
/// <param name="value"></param>
public void SetValue(ushort value, Opcode op)
{
#region " Gets Offset "
uint offset = 0;
if (op.Displacement != Opcode.OpDisp.None)
{
switch (op.Displacement)
{
case Opcode.OpDisp.BP:
offset = op.x8086.GetRegister(Register.Type.BP);
break;
case Opcode.OpDisp.BP_PLUS_DI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BP) + op.x8086.GetRegister(Register.Type.DI));
break;
case Opcode.OpDisp.BP_PLUS_SI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BP) + op.x8086.GetRegister(Register.Type.SI));
break;
case Opcode.OpDisp.BX:
offset = op.x8086.GetRegister(Register.Type.BX);
break;
case Opcode.OpDisp.BX_PLUS_DI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BX) + op.x8086.GetRegister(Register.Type.DI));
break;
case Opcode.OpDisp.BX_PLUS_SI:
offset = (ushort)(op.x8086.GetRegister(Register.Type.BX) + op.x8086.GetRegister(Register.Type.SI));
break;
case Opcode.OpDisp.DI:
offset = op.x8086.GetRegister(Register.Type.DI);
break;
case Opcode.OpDisp.SI:
offset = op.x8086.GetRegister(Register.Type.SI);
break;
}
}
#endregion
//Get the address
uint address = (ushort)this.Value;
//Now check a few things.
if (this.Type == OperandType.Operand_Register)
{
//Just set the value into the appropriate register.
op.x8086.SetRegister((Register.Type)this.Value, value);
//Just return
return;
}
else if (this.Type == OperandType.Operand_Byte_Memory_Byte || this.Type == OperandType.Operand_Short_Memory_Byte)
{
//Check for a displacement.
if (op.Displacement != Opcode.OpDisp.None)
{
//Now we need to check for a negative.
if (address >= 0x80)
{
//Negative.
address = (uint)(0x80 - (address - 0x80));
//Now we take the offset and subtract the value from it.
address = (uint)(offset - address);
}
else
{
//Positive.
//So we simply add the offset.
address = (uint)(address + offset);
}
}
else
{
//Now we need to check for a negative.
if (address >= 0x80)
{
//Negative.
address = (uint)(0x80 - (address - 0x80)); ;
}
}
}
else if (this.Type == OperandType.Operand_Byte_Memory_Short || this.Type == OperandType.Operand_Short_Memory_Short)
{
//Check for a displacement.
if (op.Displacement != Opcode.OpDisp.None)
{
//Now we need to check for a negative.
if (address >= 0x8000)
{
//Negative.
address = (uint)(0x8000 - (address - 0x8000));
//Now we take the offset and subtract the value from it.
address = (uint)(offset - address);
}
else
{
//Positive.
//So we simply add the offset.
address = (uint)(address + offset);
}
}
else
{
//Now we need to check for a negative.
if (address >= 0x8000)
{
//Negative.
address = (uint)(0x8000 - (address - 0x8000));
}
}
}
else
{
//We return because this isn't right.
return;
}
//Check for general protection fault.
if (this.IsWord())
{
if (((address + 2) & 0xFFFF) > 0xFFFF)
{
Console.WriteLine("General Protection Fault!");
Console.ReadLine();
}
}
else
{
if (((address + 1) & 0xFFFF) > 0xFFFF)
{
Console.WriteLine("General Protection Fault!");
Console.ReadLine();
}
}
//If we get here it means were working with memory so lets calculate our address here.
uint segment = 0;
//See if we have a prefix.
if (op.Prefix != Opcode.OpcodePrefix.Prefix_None)
{
switch (op.Prefix)
{
case Opcode.OpcodePrefix.Prefix_SegmentOverride_CS:
//We have to use CS.
segment = (uint)(op.x8086.Code_Seg.Low16 * 0x10);
break;
case Opcode.OpcodePrefix.Prefix_SegmentOverride_DS:
//We have to use DS.
segment = (uint)(op.x8086.Data_Seg.Low16 * 0x10);
break;
case Opcode.OpcodePrefix.Prefix_SegmentOverride_ES:
//We have to use ES.
segment = (uint)(op.x8086.Extra_Seg.Low16 * 0x10);
break;
case Opcode.OpcodePrefix.Prefix_SegmentOverride_SS:
//We have to use SS.
segment = (uint)(op.x8086.Stack_Seg.Low16 * 0x10);
break;
}
}
else
{
//Now if were using BP as a displacement then we have to use SS rather than DS.
if (op.Displacement == Opcode.OpDisp.BP || op.Displacement == Opcode.OpDisp.BP_PLUS_DI || op.Displacement == Opcode.OpDisp.BP_PLUS_SI)
{
//Use ss as the segment.
segment = (uint)(op.x8086.Stack_Seg.Low16 * 0x10);
}
else
{
//Use the segment the opcode specifies.
switch (op.Segment)
{
case Register.Type.CS:
segment = (uint)(op.x8086.Code_Seg.Low16 * 0x10);
break;
case Register.Type.DS:
segment = (uint)(op.x8086.Data_Seg.Low16 * 0x10);
break;
case Register.Type.SS:
segment = (uint)(op.x8086.Stack_Seg.Low16 * 0x10);
break;
}
}
}
//Now just return what we want based on the Memory.
switch (this.Type)
{
case OperandType.Operand_Byte_Memory_Byte:
op.x8086.SetMemoryByte((uint)(segment + (address & 0xFFFF)), (byte)value);
break;
case OperandType.Operand_Byte_Memory_Short:
op.x8086.SetMemoryByte((uint)(segment + (address & 0xFFFF)), (byte)value);
break;
case OperandType.Operand_Short_Memory_Byte:
op.x8086.SetMemoryWord((uint)(segment + (address & 0xFFFF)), (ushort)value);
break;
case OperandType.Operand_Short_Memory_Short:
op.x8086.SetMemoryWord((uint)(segment + (address & 0xFFFF)), (ushort)value);
break;
}
}
/// <summary>
/// Returns whether or not this opcode is a word.
/// </summary>
/// <returns></returns>
public bool IsWord()
{
if (this.Type == Operand.OperandType.Operand_Register && Register.IsWord((Register.Type)this.Value))
{
return true;
}
else if (this.Type == Operand.OperandType.Operand_Short)
{
return true;
}
else if (this.Type == Operand.OperandType.Operand_Short_Memory_Short)
{
return true;
}
else if (this.Type == Operand.OperandType.Operand_Short_Memory_Byte)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// Returns a string representing this opcode.
/// </summary>
/// <returns></returns>
public string GetString(Opcode op)
{
if (this.Type == OperandType.Operand_Unkown)
{
return "";
}
else
{
string str = Opcode.ConvertOperandType(this.Type);
((String)(object)str).AddByte((byte)' ');
if (this.Type == OperandType.Operand_Register)
{
str = String.Append(str, Opcode.ConvertRegister((Register.Type)this.Value), true);
}
else if (this.Type == OperandType.Operand_Byte || this.Type == OperandType.Operand_Short)
{
str = String.Append(str, Integer.ToHex(this.Value, 4), true);
}
else
{
((String)(object)str).AddByte((byte)'[');
if (op.Displacement != Opcode.OpDisp.None)
{
str = String.Append(str, Opcode.ConvertDisplacement(op.Displacement), true);
}
((String)(object)str).AddByte((byte)' ');
if (this.Type == OperandType.Operand_Short_Memory_Byte || this.Type == OperandType.Operand_Byte_Memory_Byte)
{
str = String.Append(str, "Byte", true);
}
else
{
str = String.Append(str, "Short", true);
}
((String)(object)str).AddByte((byte)' ');
str = String.Append(str, Integer.ToHex(this.Value, 4), true);
((String)(object)str).AddByte((byte)']');
}
return str;
}
}
}
I think I did it right, but maybe not.
Thanks a ton, Matt
Re: RealMode Segmentation questions for Emulator
I even simplified IN and OUT and no change. I use IOPort.Write and IOPort.Read all the time with no issues. I'm not sure if it is those two instructions that are the issue. If it is, I have no idea what's wrong.
-
- Member
- Posts: 5604
- Joined: Mon Mar 25, 2013 7:01 pm
Re: RealMode Segmentation questions for Emulator
That's a lot of code to sift through!
I'm not fluent enough in C# to say for sure if it's correct or not, but I want to see IOPort.Read_16 and IOPort.Write_16. Are those inline assembly or something?
I'm not fluent enough in C# to say for sure if it's correct or not, but I want to see IOPort.Read_16 and IOPort.Write_16. Are those inline assembly or something?
Re: RealMode Segmentation questions for Emulator
Yeah sorry! IOPort.Read_16 and IOPort.Write_16 are plugged methods. Here is the assembly code for those two.Octocontrabass wrote:That's a lot of code to sift through!
I'm not fluent enough in C# to say for sure if it's correct or not, but I want to see IOPort.Read_16 and IOPort.Write_16. Are those inline assembly or something?
Code: Select all
;Input:
; - (int) Address
; - (int) Data
;Description: Writes data to an IO Port.
IOPort_Write_16_Return: db 0,0,0,0
IOPort_Write_16:
call Thread_Lock
pop dword[IOPort_Write_16_Return] ;Get the return address
pop eax ;Get the data.
pop edx ;Get the address
out dx, ax ;Put their 16 bit varients to the ioport.
push dword[IOPort_Write_16_Return] ;Put the return address back
call Thread_Unlock
ret ;Return
;Input:
; - (int) Address
;Ouput:
; - (int) Data
;Description: Reads data from an ioport.
IOPort_Read_16_Return: db 0,0,0,0
IOPort_Read_16:
call Thread_Lock
pop dword[IOPort_Read_16_Return] ;Save the return address
pop edx ;Address
in ax, dx
MOVZX eax, ax
push eax
push dword[IOPort_Read_16_Return] ;Restore the return address
call Thread_Unlock
ret ;Return
-
- Member
- Posts: 5604
- Joined: Mon Mar 25, 2013 7:01 pm
Re: RealMode Segmentation questions for Emulator
That looks correct, so I don't know what's going on. (Although, why are you using push/pop instead of ESP-relative addressing?)
Unfortunately, I can't help you debug the C# portions. I suppose you could show me the trace logs, but if those are identical then I'm out of ideas.
Unfortunately, I can't help you debug the C# portions. I suppose you could show me the trace logs, but if those are identical then I'm out of ideas.
Re: RealMode Segmentation questions for Emulator
Yeah, everything seems to be in order. My best guess is something after the first read is seriously causing the problem because all the code up to that point "as far as I can tell is correct" maybe I will debug everything after the first read and see what happens. My guess is it's something with a Jump statement, but the debugger should have noticed a difference in the IP register and let me know, its supposed to.Octocontrabass wrote:That looks correct, so I don't know what's going on. (Although, why are you using push/pop instead of ESP-relative addressing?)
Unfortunately, I can't help you debug the C# portions. I suppose you could show me the trace logs, but if those are identical then I'm out of ideas.
Thanks, Matt
Re: RealMode Segmentation questions for Emulator
Weird. I am not sure if this was a problem or not but I was passing AL 13 instead of 0x13. When I changed that, my emulator set a video mode and reached Iret. So I guess it does work, I guess maybe 13 wasn't a true video mode? I'm gonna try to set a VESA mode and see what happens. Maybe I just gave it the wrong input but that wouldn't make too much sense because Bochs ran it just fine when it ran that mode in Real Mode.
- Matt
Edit: It ran mode 0x103 just fine and set Bochs to 800x600 so what was the problem I wonder?
- Matt
Edit: It ran mode 0x103 just fine and set Bochs to 800x600 so what was the problem I wonder?