Esempio n. 1
0
        /// <summary>
        /// For any given offset into the program we can generate the requisite
        /// IL based upon the opcode (first byte of the span) and any operands
        /// (further bytes from the span)
        ///
        /// This returns the opcodes to emit (with args) and the number of bytes
        /// consumed
        /// </summary>
        /// <param name="program">
        /// The full ROM (not just the portion we're looking at)
        /// </param>
        /// <param name="programCounter">
        /// Indicates what index into the ROM we are generating an instruction for
        /// </param>
        /// <param name="internals">
        /// Provides references to all internal fields/methods on this class
        /// for e.g. register access
        /// </param>
        /// <param name="jumpLabels">
        /// Provides labels for every address in the ROM for jump/call commands
        /// </param>
        /// <returns>
        /// A tuple containing:
        /// 1. The emitter which can be called to put instructions on an IL stream
        /// 2. The number of bytes consumed by this operation
        /// 3. The number of cycles taken by the operation (non-branching)
        /// </returns>
        private static (IEmitter, byte, long) GenerateILForOpcode(Span <byte> program, int programCounter,
                                                                  CpuInternalBuilders internals, Label[] jumpLabels)
        {
            var opcodeByte  = program[programCounter];
            var operand1    = program[(programCounter + 1) % program.Length];
            var operand2    = program[(programCounter + 2) % program.Length];
            var operandWord = (ushort)((operand2 << 8) + operand1);
            var opcode      = Opcodes8080Decoder.Decode(opcodeByte);

            IEmitter emitter = opcode switch
            {
                Opcodes8080.NOP => new NOPEmitter(),
                Opcodes8080.LXI => new LXIEmitter(opcodeByte, operand1, operand2, operandWord),
                Opcodes8080.STAX => new STAXEmitter(opcodeByte),
                Opcodes8080.INX => new INXDCXEmitter(opcodeByte),
                Opcodes8080.INR => new INRDCREmitter(opcodeByte),
                Opcodes8080.DCR => new INRDCREmitter(opcodeByte),
                Opcodes8080.MVI => new MVIEmitter(opcodeByte, operand1),
                Opcodes8080.RLC => new RLCEmitter(),
                Opcodes8080.DAD => new DADEmitter(opcodeByte),
                Opcodes8080.LDAX => new LDAXEmitter(opcodeByte),
                Opcodes8080.DCX => new INXDCXEmitter(opcodeByte),
                Opcodes8080.RRC => new RRCEmitter(),
                Opcodes8080.RAL => new RALEmitter(),
                Opcodes8080.RAR => new RAREmitter(),
                Opcodes8080.SHLD => new SHLDEmitter(operandWord),
                Opcodes8080.DAA => new NOPEmitter(), // TODO - Actually implement DAA after handling aux carry flag
                Opcodes8080.LHLD => new LHLDEmitter(operandWord),
                Opcodes8080.CMA => new CMAEmitter(),
                Opcodes8080.STA => new STAEmitter(operandWord),
                Opcodes8080.STC => new STCEmitter(),
                Opcodes8080.LDA => new LDAEmitter(operandWord),
                Opcodes8080.CMC => new CMCEmitter(),
                Opcodes8080.MOV => new MOVEmitter(opcodeByte),
                Opcodes8080.HLT => new HLTEmitter(),
                Opcodes8080.ADD => new General8BitALUEmitter(opcodeByte, opcode),
                Opcodes8080.ADC => new General8BitALUEmitter(opcodeByte, opcode),
                Opcodes8080.SUB => new General8BitALUEmitter(opcodeByte, opcode),
                Opcodes8080.SBB => new General8BitALUEmitter(opcodeByte, opcode),
                Opcodes8080.ANA => new General8BitALUEmitter(opcodeByte, opcode),
                Opcodes8080.XRA => new General8BitALUEmitter(opcodeByte, opcode),
                Opcodes8080.ORA => new General8BitALUEmitter(opcodeByte, opcode),
                Opcodes8080.CMP => new General8BitALUEmitter(opcodeByte, opcode),
                Opcodes8080.RNZ => new RetEmitter(opcodeByte, internals.ZeroFlag, false),
                Opcodes8080.POP => new POPEmitter(opcodeByte),
                Opcodes8080.JNZ => new JumpOnFlagEmitter(opcodeByte, internals.ZeroFlag, false,
                                                         jumpLabels[operandWord % program.Length],
                                                         operandWord),
                Opcodes8080.JMP => new JumpOnFlagEmitter(opcodeByte, jumpLabels[operandWord % program.Length],
                                                         operandWord),
                Opcodes8080.CNZ => new CallEmitter(opcodeByte, (ushort)(programCounter + opcode.Length()),
                                                   internals.ZeroFlag, false, jumpLabels[operandWord % program.Length], (ushort)(operandWord % program.Length)),
                Opcodes8080.PUSH => new PUSHEmitter(opcodeByte),
                Opcodes8080.ADI => new General8BitALUEmitter(opcodeByte, opcode, operand1),
                Opcodes8080.RST => new CallEmitter(opcodeByte, (ushort)(programCounter + opcode.Length()),
                                                   jumpLabels[opcodeByte & 0b0011_1000], (ushort)(opcodeByte & 0b0011_1000)),
Esempio n. 2
0
        private static MethodBuilder CreateSetFlagRegister(TypeBuilder typeBuilder, CpuInternalBuilders internals)
        {
            var method = typeBuilder.DefineMethod("SetFlagRegister", MethodAttributes.Public,
                                                  CallingConventions.Standard, null, new[] { typeof(byte) });

            method.SetImplementationFlags(MethodImplAttributes.IL | MethodImplAttributes.AggressiveInlining);

            var methodIL = method.GetILGenerator();

            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldarg_1);
            methodIL.Emit(OpCodes.Ldc_I4_1);
            methodIL.Emit(OpCodes.And);
            methodIL.Emit(OpCodes.Stfld, internals.CarryFlag);

            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldarg_1);
            methodIL.Emit(OpCodes.Ldc_I4_4);
            methodIL.Emit(OpCodes.And);
            methodIL.Emit(OpCodes.Ldc_I4_2);
            methodIL.Emit(OpCodes.Shr_Un);
            methodIL.Emit(OpCodes.Stfld, internals.ParityFlag);

            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldarg_1);
            methodIL.EmitLd8Immediate(0b0001_0000);
            methodIL.Emit(OpCodes.And);
            methodIL.Emit(OpCodes.Ldc_I4_4);
            methodIL.Emit(OpCodes.Shr_Un);
            methodIL.Emit(OpCodes.Stfld, internals.AuxCarryFlag);

            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldarg_1);
            methodIL.EmitLd8Immediate(0b0100_0000);
            methodIL.Emit(OpCodes.And);
            methodIL.Emit(OpCodes.Ldc_I4_6);
            methodIL.Emit(OpCodes.Shr_Un);
            methodIL.Emit(OpCodes.Stfld, internals.ZeroFlag);

            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldarg_1);
            methodIL.Emit(OpCodes.Ldc_I4, 0b1000_0000);
            methodIL.Emit(OpCodes.And);
            methodIL.Emit(OpCodes.Ldc_I4_7);
            methodIL.Emit(OpCodes.Shr_Un);
            methodIL.Emit(OpCodes.Stfld, internals.SignFlag);

            methodIL.Emit(OpCodes.Ret);

            return(method);
        }
Esempio n. 3
0
        internal static void EmitDebugString(this ILGenerator ilGenerator, CpuInternalBuilders internals)
        {
            const string formatString = "A={0:X2} B={1:X2} C={2:X2} D={3:X2} E={4:X2} H={5:X2} L={6:X2} SP={7:X4} S={8} C={9} A={10} P={11} Z={12}";
            var          wlParams     = new[]
            {
                typeof(string),
                typeof(object[])
            };

            var writeLineMethodInfo = typeof(Console).GetMethod("WriteLine", wlParams);

            ilGenerator.Emit(OpCodes.Ldstr, formatString);
            ilGenerator.Emit(OpCodes.Ldc_I4, 13);
            ilGenerator.Emit(OpCodes.Newarr, typeof(object));

            foreach (var(field, ix) in new[]
Esempio n. 4
0
        void IEmitter.Emit(ILGenerator methodIL, CpuInternalBuilders internals)
        {
            switch (_opcode)
            {
            case 0x01:     // LXI BC
                methodIL.Emit(OpCodes.Ldarg_0);
                methodIL.EmitLd8Immediate(_operand1);
                methodIL.Emit(OpCodes.Stfld, internals.C);
                methodIL.Emit(OpCodes.Ldarg_0);
                methodIL.EmitLd8Immediate(_operand2);
                methodIL.Emit(OpCodes.Stfld, internals.B);
                break;

            case 0x11:     // LXI DE
                methodIL.Emit(OpCodes.Ldarg_0);
                methodIL.EmitLd8Immediate(_operand1);
                methodIL.Emit(OpCodes.Stfld, internals.E);
                methodIL.Emit(OpCodes.Ldarg_0);
                methodIL.EmitLd8Immediate(_operand2);
                methodIL.Emit(OpCodes.Stfld, internals.D);
                break;

            case 0x21:     // LXI HL
                methodIL.Emit(OpCodes.Ldarg_0);
                methodIL.EmitLd8Immediate(_operand1);
                methodIL.Emit(OpCodes.Stfld, internals.L);
                methodIL.Emit(OpCodes.Ldarg_0);
                methodIL.EmitLd8Immediate(_operand2);
                methodIL.Emit(OpCodes.Stfld, internals.H);
                break;

            case 0x31:     // LXI SP
                methodIL.Emit(OpCodes.Ldarg_0);
                methodIL.Emit(OpCodes.Ldc_I4, _operandWord);
                methodIL.Emit(OpCodes.Stfld, internals.StackPointer);
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(_opcode),
                                                      $"LXI shouldn't be run for opcode byte {_opcode}");
            }
        }
Esempio n. 5
0
        private static MethodBuilder CreateFlagRegister(TypeBuilder typeBuilder, CpuInternalBuilders internals)
        {
            var method = typeBuilder.DefineMethod("GetFlagRegister", MethodAttributes.Public,
                                                  CallingConventions.Standard,
                                                  typeof(byte), Type.EmptyTypes);

            method.SetImplementationFlags(MethodImplAttributes.IL | MethodImplAttributes.AggressiveInlining);
            var methodIL = method.GetILGenerator();

            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, internals.SignFlag);
            methodIL.Emit(OpCodes.Ldc_I4_7);
            methodIL.Emit(OpCodes.Shl);
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, internals.ZeroFlag);
            methodIL.Emit(OpCodes.Ldc_I4_6);
            methodIL.Emit(OpCodes.Shl);
            methodIL.Emit(OpCodes.Or);
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, internals.AuxCarryFlag);
            methodIL.Emit(OpCodes.Ldc_I4_4);
            methodIL.Emit(OpCodes.Shl);
            methodIL.Emit(OpCodes.Or);
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, internals.ParityFlag);
            methodIL.Emit(OpCodes.Ldc_I4_2);
            methodIL.Emit(OpCodes.Shl);
            methodIL.Emit(OpCodes.Or);
            methodIL.Emit(OpCodes.Ldc_I4_2);
            methodIL.Emit(OpCodes.Or);
            methodIL.Emit(OpCodes.Ldarg_0);
            methodIL.Emit(OpCodes.Ldfld, internals.CarryFlag);
            methodIL.Emit(OpCodes.Or);
            methodIL.Emit(OpCodes.Ret);

            return(method);
        }
Esempio n. 6
0
 void IEmitter.Emit(ILGenerator methodIL, CpuInternalBuilders internals) =>
 methodIL.Emit(OpCodes.Nop);