public Micron_MT25Q(MappedMemory underlyingMemory) { // original MT25Q supports capacity 8MB to 256MB, // but we extended it down to 64KB // to become compatible with N25Q line if (underlyingMemory.Size < 64.KB() || underlyingMemory.Size > 256.MB() || !Misc.IsPowerOfTwo((ulong)underlyingMemory.Size)) { throw new ConstructionException("Size of the underlying memory must be a power of 2 value in range 64KB - 256MB"); } volatileConfigurationRegister = new ByteRegister(this, 0xfb).WithFlag(3, name: "XIP"); nonVolatileConfigurationRegister = new WordRegister(this, 0xffff).WithFlag(0, out numberOfAddressBytes, name: "addressWith3Bytes"); enhancedVolatileConfigurationRegister = new ByteRegister(this, 0xff) .WithValueField(0, 3, name: "Output driver strength") .WithReservedBits(3, 1) .WithTaggedFlag("Reset/hold", 4) //these flags are intentionally not implemented, as they described physical details .WithFlag(5, name: "Double transfer rate protocol") .WithFlag(6, name: "Dual I/O protocol") .WithFlag(7, name: "Quad I/O protocol"); statusRegister = new ByteRegister(this).WithFlag(1, out enable, name: "volatileControlBit"); flagStatusRegister = new ByteRegister(this) .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => numberOfAddressBytes.Value, name: "Addressing") //other bits indicate either protection errors (not implemented) or pending operations (they already finished) .WithReservedBits(3, 1) .WithFlag(7, FieldMode.Read, valueProviderCallback: _ => true, name: "ProgramOrErase"); this.underlyingMemory = underlyingMemory; underlyingMemory.ResetByte = EmptySegment; deviceData = GetDeviceData(); }
private void SetRegister(ByteRegister register, byte value) { if (register != ByteRegister.None) { _registers[(int)register] = value; } }
public Micron_MT25Q(MicronFlashSize size, Endianess dataEndianess = Endianess.LittleEndian) { if (!Enum.IsDefined(typeof(MicronFlashSize), size)) { throw new ConstructionException($"Undefined memory size: {size}"); } this.dataEndianess = dataEndianess; volatileConfigurationRegister = new ByteRegister(this, 0xfb).WithFlag(3, name: "XIP"); nonVolatileConfigurationRegister = new WordRegister(this, 0xffff).WithFlag(0, out numberOfAddressBytes, name: "addressWith3Bytes"); enhancedVolatileConfigurationRegister = new ByteRegister(this, 0xff) .WithValueField(0, 3, name: "Output driver strength") .WithReservedBits(3, 1) .WithTaggedFlag("Reset/hold", 4) //these flags are intentionally not implemented, as they described physical details .WithFlag(5, name: "Double transfer rate protocol") .WithFlag(6, name: "Dual I/O protocol") .WithFlag(7, name: "Quad I/O protocol"); statusRegister = new ByteRegister(this).WithFlag(1, out enable, name: "volatileControlBit"); flagStatusRegister = new ByteRegister(this) .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => numberOfAddressBytes.Value, name: "Addressing") //other bits indicate either protection errors (not implemented) or pending operations (they already finished) .WithReservedBits(3, 1); fileBackendSize = (uint)size; isCustomFileBackend = false; dataBackend = DataStorage.Create(fileBackendSize, 0xFF); deviceData = GetDeviceData(); }
private byte GetRegister(ByteRegister register) { if (register == ByteRegister.None) { return(0xFF); } return(_registers[(int)register]); }
public void Setup() { _byteFactory = new ByteFactory(new Base10Converter()); _and = new And(); _memoryGateFactory = new MemoryGateFactory(new NAnd(new Not(), _and)); _byteMemoryGate = new ByteMemoryGate(_memoryGateFactory, _byteFactory); _byteEnabler = new ByteEnabler(_and, _byteFactory); _sut = new ByteRegister(_byteMemoryGate, _byteEnabler, _byteFactory, wire => {}); }
public IRegister <IByte> Create(Action <IByte> dataToUpdate, string name) { var reg = new ByteRegister(_byteMemoryGateFactory.Create(), _byteEnabler, _byteFactory, dataToUpdate) { Name = name }; return(reg); }
public ExecutionResult Execute(Processor cpu, InstructionPackage package) { Instruction instruction = package.Instruction; InstructionData data = package.Data; Flags flags = cpu.Flags; Registers r = cpu.Registers; sbyte offset = (sbyte)(data.Argument1); ByteRegister register = instruction.Target.AsByteRegister(); bool previousCarry = flags.Carry; byte original, shifted; if (register != ByteRegister.None) { original = r[register]; shifted = (byte)(original >> 1); shifted = shifted.SetBit(7, previousCarry); setFlags(original, shifted, original.GetBit(0)); r[register] = shifted; } else { ushort address = instruction.Prefix switch { 0xCB => r.HL, 0xDDCB => (ushort)(r.IX + offset), 0xFDCB => (ushort)(r.IY + offset), _ => (ushort)0xFFFF }; original = cpu.Memory.Timed.ReadByteAt(address); shifted = (byte)(original >> 1); shifted = shifted.SetBit(7, previousCarry); setFlags(original, shifted, original.GetBit(0)); if (instruction.IsIndexed) { cpu.Timing.InternalOperationCycle(4); } cpu.Memory.Timed.WriteByteAt(address, shifted); if (instruction.CopyResultTo != ByteRegister.None) { r[instruction.CopyResultTo.Value] = shifted; } } void setFlags(byte original, byte shifted, bool overflowBit) { flags = FlagLookup.BitwiseFlags(original, BitwiseOperation.RotateRightThroughCarry, previousCarry); flags.Carry = overflowBit; flags.HalfCarry = false; flags.Subtract = false; } return(new ExecutionResult(package, flags)); }
private void InitializeFlashRegisters() { //These registers are for the Winbond W25Q80DV FlashStatus1 = new ByteRegister(this, 0x0) .WithFlag(0, name: "BUSY") .WithFlag(1, name: "WEL", mode: FieldMode.ReadToClear | FieldMode.Write) .WithValueField(2, 3, name: "BLOCK_PROTECT_BITS") .WithFlag(5, name: "TB") .WithFlag(6, name: "SEC") .WithFlag(7, name: "SRP0"); }
public ExecutionResult Execute(Processor cpu, InstructionPackage package) { Instruction instruction = package.Instruction; InstructionData data = package.Data; Flags flags = cpu.Flags; Registers r = cpu.Registers; sbyte offset = (sbyte)(data.Argument1); ByteRegister register = instruction.Target.AsByteRegister(); void setFlags(byte original) { flags = FlagLookup.BitwiseFlags(original, BitwiseOperation.ShiftLeftSetBit0, flags.Carry); flags.Carry = original.GetBit(7); flags.HalfCarry = false; flags.Subtract = false; } byte original, shifted; if (register != ByteRegister.None) { original = r[register]; shifted = (byte)((original << 1) + 1); // bit 0 is always set, adding 1 does this quicker than calling SetBit setFlags(original); r[register] = shifted; } else { ushort address = instruction.Prefix switch { 0xCB => r.HL, 0xDDCB => (ushort)(r.IX + offset), 0xFDCB => (ushort)(r.IY + offset), _ => (ushort)0xFFFF }; original = cpu.Memory.Timed.ReadByteAt(address); shifted = (byte)((original << 1) + 1); setFlags(original); cpu.Memory.Timed.WriteByteAt(address, shifted); if (instruction.CopyResultTo != ByteRegister.None) { r[instruction.CopyResultTo.Value] = shifted; } } return new ExecutionResult(package, flags); }
public ExecutionResult Execute(Processor cpu, InstructionPackage package) { Instruction instruction = package.Instruction; InstructionData data = package.Data; Registers r = cpu.Registers; byte offset = data.Argument1; Flags flags = cpu.Flags; if (instruction.TargetsWordRegister) { // inc 16-bit WordRegister register = instruction.Target.AsWordRegister(); ushort value = r[register]; r[register] = (ushort)(value + 1); } else { byte value = 0; if (instruction.TargetsByteInMemory) { // inc byte in memory if (instruction.IsIndexed) { cpu.Timing.InternalOperationCycle(5); } value = instruction.MarshalSourceByte(data, cpu, out ushort address, out ByteRegister source); cpu.Memory.Timed.WriteByteAt(address, (byte)(value + 1)); } else { // it's an 8-bit inc ByteRegister register = instruction.Target.AsByteRegister(); value = r[register]; r[register] = (byte)(value + 1); } bool carry = flags.Carry; flags = FlagLookup.ByteArithmeticFlags(value, 1, false, false); flags.ParityOverflow = (value == 0x7F); flags.Carry = carry; // always unaffected flags.Subtract = false; } return(new ExecutionResult(package, flags)); }
public ExecutionResult Execute(Processor cpu, InstructionPackage package) { Instruction instruction = package.Instruction; InstructionData data = package.Data; Registers r = cpu.Registers; Flags flags = cpu.Flags; byte bitIndex = instruction.GetBitIndex(); byte value; ByteRegister register = instruction.Source.AsByteRegister(); if (register != ByteRegister.None) { value = r[register]; // BIT b, r } else { if (instruction.IsIndexed) { cpu.Timing.InternalOperationCycle(5); } value = instruction.MarshalSourceByte(data, cpu, out ushort address, out ByteRegister source); byte valueXY = instruction.IsIndexed ? address.HighByte() : r.WZ.HighByte(); // this is literally the only place the WZ value is *ever* actually used flags.X = (valueXY & 0x08) > 0; // copy bit 3 flags.Y = (valueXY & 0x20) > 0; // copy bit 5 } bool set = value.GetBit(bitIndex); flags.Sign = bitIndex == 7 && set; if (register == ByteRegister.None) { flags.Y = bitIndex == 5 && set; flags.X = bitIndex == 3 && set; } flags.Zero = !set; flags.ParityOverflow = flags.Zero; flags.HalfCarry = true; flags.Subtract = false; return(new ExecutionResult(package, flags)); }
public ExecutionResult Execute(Processor cpu, InstructionPackage package) { Instruction instruction = package.Instruction; InstructionData data = package.Data; Registers r = cpu.Registers; byte bitIndex = instruction.GetBitIndex(); sbyte offset = (sbyte)(data.Argument1); ByteRegister register = instruction.Source.AsByteRegister(); if (register != ByteRegister.None) { byte value = r[register].SetBit(bitIndex, true); r[register] = value; } else { ushort address = instruction.Prefix switch { 0xCB => r.HL, 0xDDCB => (ushort)(r.IX + offset), 0xFDCB => (ushort)(r.IY + offset), _ => (ushort)0xFFFF }; if (instruction.IsIndexed) { cpu.Timing.InternalOperationCycle(5); } byte value = cpu.Memory.Timed.ReadByteAt(address); value = value.SetBit(bitIndex, true); cpu.Memory.Timed.WriteByteAt(address, value); if (instruction.CopyResultTo != ByteRegister.None) { r[instruction.CopyResultTo.Value] = value; } } return(new ExecutionResult(package, null)); }
public static ByteRegister AsByteRegister(this InstructionElement argument) { ByteRegister register = argument switch { InstructionElement.A => ByteRegister.A, InstructionElement.B => ByteRegister.B, InstructionElement.C => ByteRegister.C, InstructionElement.D => ByteRegister.D, InstructionElement.E => ByteRegister.E, InstructionElement.F => ByteRegister.F, InstructionElement.H => ByteRegister.H, InstructionElement.L => ByteRegister.L, InstructionElement.IXh => ByteRegister.IXh, InstructionElement.IYh => ByteRegister.IYh, InstructionElement.IXl => ByteRegister.IXl, InstructionElement.IYl => ByteRegister.IYl, InstructionElement.I => ByteRegister.I, InstructionElement.R => ByteRegister.R, _ => ByteRegister.None }; return(register); }
public byte this[ByteRegister register] { get { return(GetRegister(register)); } set { SetRegister(register, value); } }
public void SRA(ByteRegister op) { _registerFile.F = (byte)(op.Value & FlagRegisterDefinition.C); op.Value = (byte)((op.Value & 0x80) | (op.Value >> 1)); _registerFile.F |= _lookupTables.Sz53P[op.Value]; }
public void SRL(ByteRegister op) { _registerFile.F = (byte)(op.Value & FlagRegisterDefinition.C); op.Value >>= 1; _registerFile.F |= _lookupTables.Sz53P[op.Value]; }
public static ByteRegister Bind(this IConvertible o, IProvidesRegisterCollection <ByteRegisterCollection> p, ByteRegister reg, string name = "") { if (!o.GetType().IsEnum) { throw new ArgumentException("This method should be called on enumerated type"); } return(p.RegistersCollection.AddRegister(Convert.ToInt64(o), reg)); }
public static byte MarshalSourceByte(this Instruction instruction, InstructionData data, Processor cpu, out ushort address, out ByteRegister source) { // this fetches a byte operand value for the instruction given, adjusting how it is fetched based on the addressing of the instruction Registers r = cpu.Registers; address = 0x0000; byte value; source = instruction.Source.AsByteRegister(); if (source != ByteRegister.None) { // operand comes from another byte register directly (eg LD A,B) value = r[source]; } else { if (instruction.Argument1 == InstructionElement.ByteValue) { // operand is supplied as an argument (eg LD A,n) value = data.Argument1; } else if (instruction.Source == InstructionElement.None) { // operand is fetched from a memory location but the source and target are the same (eg INC (HL) or INC (IX+d)) address = instruction.Target.AsWordRegister() switch { WordRegister.IX => (ushort)(r.IX + (sbyte)data.Argument1), WordRegister.IY => (ushort)(r.IY + (sbyte)data.Argument1), _ => r.HL }; value = cpu.Memory.Timed.ReadByteAt(address); } else { // operand is fetched from a memory location and assigned elsewhere (eg LD A,(HL) or LD B,(IX+d)) address = instruction.Source.AsWordRegister() switch { WordRegister.IX => (ushort)(r.IX + (sbyte)data.Argument1), WordRegister.IY => (ushort)(r.IY + (sbyte)data.Argument1), _ => r.HL }; value = cpu.Memory.Timed.ReadByteAt(address); } } return(value); }
/// <summary> /// Execution of ED xx codes /// </summary> /// <param name="opcode">opcode to execute</param> private void Execute_ED_Prefix(byte opcode) { if ((opcode & 0xC7) == 0x40) // IN r,(C) { ByteRegister reg = GetByteRegisterByOpcode((byte) (opcode >> 3)); // In this case 110 does not write in (HL) but affects only the flags if (reg == null) reg = new ByteRegister(); _cpuTicks += 12; IN(reg, _registerFile.BC); } else if ((opcode & 0xC7) == 0x41) // OUT (C),r { // The contents of register C are placed on the bottom half (A0 through A7) of // the address bus to select the I/O device at one of 256 possible ports. The // contents of Register B are placed on the top half (A8 through A15) of the // address bus at this time. Then the byte contained in register r is placed on // the data bus and written to the selected peripheral device. ByteRegister reg = GetByteRegisterByOpcode((byte) (opcode >> 3)); // In this case 110 outputs 0 in out port if (reg == null) reg = new ByteRegister(0); _cpuTicks += 12; _inputOutputDevice.WritePort(_registerFile.BC, reg.Value); } else if ((opcode & 0xC7) == 0x42) // ALU operations with HL { _cpuTicks += 15; WordRegister reg = GetWordRegister(opcode, true); switch (opcode & 0x08) { case 0: // SBC HL,ss _alu.SBC_HL(reg.Word); break; case 8: // ADC HL,ss _alu.ADC_HL(reg.Word); break; default: throw new Exception("No no no!!!"); } } else if ((opcode & 0xC7) == 0x43) // Load register from to memory address { _cpuTicks += 20; WordRegister reg = GetWordRegister(opcode, true); switch (opcode & 0x08) { case 0: // LD (nnnn),ss LD_nndd(reg); break; case 8: // LD ss,(nnnn) LD_ddnn(reg); break; default: throw new Exception("No no no!!!"); } } else { switch (opcode) { case 0x44: case 0x4c: case 0x54: case 0x5c: case 0x64: case 0x6c: case 0x74: case 0x7c: // NEG // The contents of the Accumulator are negated (two’s complement). This is // the same as subtracting the contents of the Accumulator from zero. Note // that 80H is left unchanged. // Condition Bits Affected: // S is set if result is negative; reset otherwise // Z is set if result is 0; reset otherwise // H is set if borrow from bit 4; reset otherwise // P/V is set if Accumulator was 80H before operation; reset otherwise // N is set // C is set if Accumulator was not 00H before operation; reset otherwise _cpuTicks += 8; { byte _b = _registerFile.A; _registerFile.A = 0; _alu.SUB_r(_b); } break; case 0x45: case 0x4d: // RETI // This instruction is used at the end of a maskable interrupt service routine to: // • Restore the contents of the Program Counter (PC) (analogous to the // RET instruction) // • Signal an I/O device that the interrupt routine is completed. The RETI // instruction also facilitates the nesting of interrupts, allowing higher // priority devices to temporarily suspend service of lower priority // service routines. However, this instruction does not enable interrupts // that were disabled when the interrupt routine was entered. Before // doing the RETI instruction, the enable interrupt instruction (EI) // should be executed to allow recognition of interrupts after completion // of the current service routine. // TODO: Reading Z80 specs this instruction should not copy IFF2 to IFF1 but // in real Z80 seems that the operation is done. case 0x55: case 0x5d: case 0x65: case 0x6d: case 0x75: case 0x7d: // RETN // This instruction is used at the end of a non-maskable interrupts service // routine to restore the contents of the Program Counter (PC) (analogous to // the RET instruction). The state of IFF2 is copied back to IFF1 so that // maskable interrupts are enabled immediately following the RETN if they // were enabled before the nonmaskable interrupt. _cpuTicks += 14; _registerFile.IFF1 = _registerFile.IFF2; RET(); break; case 0x46: case 0x4e: case 0x66: case 0x6e: // IM 0 // The IM 0 instruction sets interrupt mode 0. In this mode, the interrupting // device can insert any instruction on the data bus for execution by the // CPU. The first byte of a multi-byte instruction is read during the interrupt // acknowledge cycle. Subsequent bytes are read in by a normal memory // read sequence. _cpuTicks += 8; _registerFile.IM = 0; break; case 0x47: // LD I,A _cpuTicks += 9; _registerFile.I = _registerFile.A; break; case 0x4F: // LD R,A _cpuTicks += 9; _registerFile.R = _registerFile.R7 = _registerFile.A; break; case 0x56: case 0x76: // IM 1 _cpuTicks += 8; // The IM 1 instruction sets interrupt mode 1. In this mode, the processor // responds to an interrupt by executing a restart to location 0038H. _registerFile.IM = 1; break; case 0x57: // LD A,I _cpuTicks += 9; // The contents of the Interrupt Vector Register I are loaded to the Accumulator. // Condition Bits Affected: // S is set if I-Register is negative; reset otherwise // Z is set if I-Register is zero; reset otherwise // H is reset // P/V contains contents of IFF2 // N is reset // C is not affected // If an interrupt occurs during execution of this instruction, the Parity // flag contains a 0. _registerFile.A = _registerFile.I; _registerFile.F = (byte) ((_registerFile.F & FlagRegisterDefinition.C) | _lookupTables.Sz53[_registerFile.A] | (_registerFile.IFF2 ? FlagRegisterDefinition.V : 0)); break; case 0x5E: case 0x7E: // IM 2 // The IM 2 instruction sets the vectored interrupt mode 2. This mode allows // an indirect call to any memory location by an 8-bit vector supplied from the // peripheral device. This vector then becomes the least-significant eight bits // of the indirect pointer, while the I register in the CPU provides the most-significant // eight bits. This address points to an address in a vector table that // is the starting address for the interrupt service routine. _cpuTicks += 8; _registerFile.IM = 2; break; case 0x5F: // LD A,R _cpuTicks += 9; _registerFile.A = (byte) ((_registerFile.R & 0x7F) | (_registerFile.R7 & 0x80)); _registerFile.F = (byte) ((_registerFile.F & FlagRegisterDefinition.C) | _lookupTables.Sz53[_registerFile.A] | (_registerFile.IFF2 ? FlagRegisterDefinition.V : 0)); break; case 0x67: // RRD _cpuTicks += 18; _alu.RRD(); break; case 0x6F: // RLD _cpuTicks += 18; _alu.RLD(); break; case 0xA0: // LDI _cpuTicks += 16; LDI(); break; case 0xA1: // CPI _cpuTicks += 16; CPI(); break; case 0xA2: // INI _cpuTicks += 16; INI(); break; case 0xA3: // OUTI _cpuTicks += 16; OUTI(); break; case 0xA8: // LDD _cpuTicks += 16; LDD(); break; case 0xA9: // CPD _cpuTicks += 16; CPD(); break; case 0xAA: // IND _cpuTicks += 16; IND(); break; case 0xAB: // OUTD _cpuTicks += 16; OUTD(); break; case 0xB0: // LDIR _cpuTicks += 16; LDIR(); break; case 0xB1: // CPIR _cpuTicks += 16; CPIR(); break; case 0xB2: // INIR _cpuTicks += 16; INIR(); break; case 0xB3: // OTIR _cpuTicks += 16; OTIR(); break; case 0xB8: // LDDR _cpuTicks += 17; LDDR(); break; case 0xB9: // CPDR _cpuTicks += 16; CPDR(); break; case 0xba: // INDR _cpuTicks += 16; INDR(); break; case 0xbb: // OTDR _cpuTicks += 16; OTDR(); break; default: // All other opcodes are NOPD _cpuTicks += 8; break; } } }
/// <summary> /// Execution of DD CB xx codes or FD CB xx codes /// </summary> /// <param name="Address">Address to act on - Address = I_ + d</param> /// <param name="opcode">opcode</param> private void Execute_DDFD_CB_Prefix(ushort Address, byte opcode) { // This is a mix of DD/FD opcodes (Normal operation but access to // I_ register instead of HL register) and CB op codes. // Behaviour is a little different: // if (Opcodes use B, C, D, E, H, L) - opcodes with rrr different from 110 // r = (I_ + d) // execute_op r // (I_ + d) = r // if (Opcodes use (HL)) - opcodes with rrr = 110 // execute_op (I_ + d) // // if execute_op is a bit checking operation BIT n,r no assignement are done ByteRegister reg; // Check if the operation is a bit checking operation // The format is 01 bbb rrr if (opcode >> 6 == 0x01) { reg = new ByteRegister(); _cpuTicks += 20; } else { // Retrieve the register from opcode xxxxx rrr reg = GetByteRegisterByOpcode(opcode); // Check if the source is (I_ + d) so the op will not act on any register // but only on memory if (reg == null) { _cpuTicks += 23; reg = new ByteRegister(); } else _cpuTicks += 23; // In case reg is not null the timings are not documented. I think the operation // take at least 23 CpuTicks (the same of the operation without storing the // result in register too). } // Assign (I_ + d) value to reg reg.Value = _memory.ReadByte(Address); Execute_CB_on_reg(opcode, reg); _memory.WriteByte(Address, reg.Value); }
public void INC(ByteRegister op) { op.Value++; _registerFile.F = (byte)( (_registerFile.F & FlagRegisterDefinition.C) | (op.Value == 0x80 ? FlagRegisterDefinition.V : (byte)0) | ((op.Value & 0x0F) != 0 ? (byte)0 : FlagRegisterDefinition.H) | (op.Value != 0 ? (byte)0 : FlagRegisterDefinition.Z) | _lookupTables.Sz53[op.Value]); }
public void RL(ByteRegister op) { byte _op = op.Value; op.Value = (byte)((op.Value << 1) | (_registerFile.F & FlagRegisterDefinition.C)); _registerFile.F = (byte)((_op >> 7) | _lookupTables.Sz53P[op.Value]); }
public void IN(ByteRegister reg, ushort port) { reg.Value = _inputOutputDevice.ReadPort(port); _registerFile.F = (byte)((_registerFile.F & FlagRegisterDefinition.C) | _lookupTables.Sz53P[reg.Value]); }
/// <summary> /// This is the low level function called within a CB opcode fetch /// (single byte or DD CB or FD CB) /// It must be called after the execution unit has determined on /// wich register act /// </summary> /// <param name="opcode">opcode</param> /// <param name="reg">Register to act on</param> private void Execute_CB_on_reg(byte opcode, ByteRegister reg) { switch (opcode >> 3) { case 0: // RLC r _alu.RLC(reg); break; case 1: // RRC r _alu.RRC(reg); break; case 2: // RL r _alu.RL(reg); break; case 3: // RR r _alu.RR(reg); break; case 4: // SLA r _alu.SLA(reg); break; case 5: // SRA r _alu.SRA(reg); break; case 6: // SLL r _alu.SLL(reg); break; case 7: // SRL r _alu.SRL(reg); break; default: // Work on bits // The format is oo bbb rrr // oo is the operation (01 BIT, 10 RES, 11 SET) // bbb is the bit number // rrr is the register var bit = (byte) ((opcode >> 3) & 0x07); switch (opcode >> 6) { case 1: // BIT n,r if (bit == 7) BIT7(reg.Value); else BIT(bit, reg.Value); break; case 2: // RES n,r reg.Value &= (byte) ~(1 << bit); break; case 3: // SET n,r reg.Value |= (byte) (1 << bit); break; default: throw new Exception("What am I doing here?!?"); } break; } }
public void INC(ByteRegister op) { _alu.INC(op); }
public void LD_d_r(ByteRegister destinationRegister, ByteRegister sourceRegister) { throw new NotImplementedException(); }
public void RLC(ByteRegister op) { _alu.RLC(op); }
public void DEC(ByteRegister op) { _alu.DEC(op); }
public void RLC(ByteRegister op) { op.Value = (byte)((op.Value << 1) | (op.Value >> 7)); _registerFile.F = (byte)( (op.Value & FlagRegisterDefinition.C) | _lookupTables.Sz53P[op.Value]); }
private void INC_R(ByteRegister reg) { if (reg == null) { // The target is (HL) CpuTicks += 7; reg = new ByteRegister(_memory.ReadByte(_registerFile.HL)); _alu.INC(reg); _memory.WriteByte(_registerFile.HL, reg.Value); } else { // The target is a normal registry CpuTicks += 4; _alu.INC(reg); } }
public void RR(ByteRegister op) { byte _op = op.Value; op.Value = (byte)((op.Value >> 1) | (_registerFile.F << 7)); _registerFile.F = (byte)( (_op & FlagRegisterDefinition.C) | _lookupTables.Sz53P[op.Value]); }
private void Execute_DDFD_Prefix(WordRegister RegisterI_, byte opcode) { ByteRegister _I__; ushort Address; if (opcode == 0x76) // HALT { // The first check is for HALT otherwise it could be // interpreted as LD (I_ + d),(I_ + d) _cpuTicks += 4; _registerFile.Halted = true; } else if ((opcode & 0xC0) == 0x40) // LD r,r' { ByteRegister reg1 = GetByteRegisterByOpcode((byte)(opcode >> 3)); ByteRegister reg2 = GetByteRegisterByOpcode(opcode); if (reg1 == null) { // The target is (I_ + d) _cpuTicks += 19; Address = (ushort)(RegisterI_.Word + (sbyte)_memory.ReadByte(_registerFile.PC++)); _memory.WriteByte(Address, reg2.Value); } else if (reg2 == null) { // The source is (I_ + d) _cpuTicks += 19; Address = (ushort)(RegisterI_.Word + (sbyte)_memory.ReadByte(_registerFile.PC++)); reg1.Value = _memory.ReadByte(Address); } else { // Source and target are normal registers but HL is now substituted by I_ if (reg1 == _registerFile.RegisterHL.High) reg1 = RegisterI_.High; if (reg1 == _registerFile.RegisterHL.Low) reg1 = RegisterI_.Low; if (reg2 == _registerFile.RegisterHL.High) reg2 = RegisterI_.High; if (reg2 == _registerFile.RegisterHL.Low) reg2 = RegisterI_.Low; _cpuTicks += 8; reg1.Value = reg2.Value; } } else if ((opcode & 0xC0) == 0x80) { // Operation beetween accumulator and other registers // Usually are identified by 10 ooo rrr where ooo is the operation and rrr is the source register ByteRegister reg = GetByteRegisterByOpcode(opcode); byte _Value; if (reg == null) { // The source is (I_ + d) _cpuTicks += 19; _Value = _memory.ReadByte((ushort)(RegisterI_.Word + (sbyte)_memory.ReadByte(_registerFile.PC++))); } else { // The source is a normal registry but HL is substituted by I_ _cpuTicks += 8; if (reg == _registerFile.RegisterHL.High) _Value = RegisterI_.High.Value; else if (reg == _registerFile.RegisterHL.Low) _Value = RegisterI_.Low.Value; else _Value = reg.Value; } switch (opcode & 0xF8) { case 0x80: // ADD A,r _alu.ADC_A_r(_Value); break; case 0x88: // ADC A,r _alu.ADC_A_r(_Value); break; case 0x90: // SUB r _alu.SUB_r(_Value); break; case 0x98: // SBC A,r _alu.SBC_A_r(_Value); break; case 0xA0: // AND r _alu.AND_r(_Value); break; case 0xA8: // XOR r _alu.XOR_r(_Value); break; case 0xB0: // OR r _alu.OR_r(_Value); break; case 0xB8: // CP r CP_r(_Value); break; default: throw new Exception("Wrong place in the right time..."); } } else { switch (opcode) { case 0x09: // ADD I_,BC _cpuTicks += 15; ADD_16(RegisterI_, _registerFile.BC); break; case 0x19: // ADD I_,DE _cpuTicks += 15; ADD_16(RegisterI_, _registerFile.DE); break; case 0x21: // LD I_,nnnn _cpuTicks += 14; RegisterI_.Word = _memory.ReadWord(_registerFile.PC); _registerFile.PC += 2; break; case 0x22: // LD (nnnn),I_ _cpuTicks += 20; LD_nndd(RegisterI_); break; case 0x23: // INC I_ _cpuTicks += 10; RegisterI_.Word++; break; case 0x24: // INC I_.h _cpuTicks += 8; INC(RegisterI_.High); break; case 0x25: // DEC I_.h _cpuTicks += 8; DEC(RegisterI_.High); break; case 0x26: // LD I_.h,nn _cpuTicks += 11; RegisterI_.High.Value = _memory.ReadByte(_registerFile.PC++); break; case 0x29: // ADD I_,I_ _cpuTicks += 15; ADD_16(RegisterI_, RegisterI_.Word); break; case 0x2A: // LD I_,(nnnn) _cpuTicks += 20; LD_ddnn(RegisterI_); break; case 0x2B: // DEC I_ _cpuTicks += 10; RegisterI_.Word--; break; case 0x2C: // INC I_.l _cpuTicks += 8; INC(RegisterI_.Low); break; case 0x2D: // DEC I_.l _cpuTicks += 8; DEC(RegisterI_.Low); break; case 0x2E: // LD I_.l,nn _cpuTicks += 11; RegisterI_.Low.Value = _memory.ReadByte(_registerFile.PC++); break; case 0x34: // INC (I_ + d) _cpuTicks += 23; Address = (ushort)(RegisterI_.Word + (sbyte)_memory.ReadByte(_registerFile.PC++)); _I__ = new ByteRegister(_memory.ReadByte(Address)); INC(_I__); _memory.WriteByte(Address, _I__.Value); break; case 0x35: // DEC (I_ + d) _cpuTicks += 23; Address = (ushort)(RegisterI_.Word + (sbyte)_memory.ReadByte(_registerFile.PC++)); _I__ = new ByteRegister(_memory.ReadByte(Address)); DEC(_I__); _memory.WriteByte(Address, _I__.Value); break; case 0x36: // LD (I_ + d),nn _cpuTicks += 19; Address = (ushort)(RegisterI_.Word + (sbyte)_memory.ReadByte(_registerFile.PC++)); _memory.WriteByte(Address, _memory.ReadByte(_registerFile.PC++)); break; case 0x39: // ADD I_,SP _cpuTicks += 15; ADD_16(RegisterI_, _registerFile.SP); break; case 0xCB: // {DD|FD}CBxx opcodes { Address = (ushort)(RegisterI_.Word + (sbyte)_memory.ReadByte(_registerFile.PC++)); Execute_DDFD_CB_Prefix(Address, _memory.ReadByte(_registerFile.PC++)); } break; case 0xE1: // POP I_ _cpuTicks += 14; Pop(RegisterI_); break; case 0xE3: // EX (SP),I_ _cpuTicks += 23; { ushort _w = _memory.ReadWord(_registerFile.SP); _memory.WriteWord(_registerFile.SP, RegisterI_.Word); RegisterI_.Word = _w; } break; case 0xE5: // PUSH I_ _cpuTicks += 15; Push(RegisterI_); break; case 0xE9: // JP I_ _cpuTicks += 8; _registerFile.PC = RegisterI_.Word; break; // Note EB (EX DE,HL) does not get modified to use either IX or IY; // this is because all EX DE,HL does is switch an internal flip-flop // in the Z80 which says which way round DE and HL are, which can't // be used with IX or IY. (This is also why EX DE,HL is very quick // at only 4 T states). case 0xF9: // LD SP,I_ _cpuTicks += 10; _registerFile.SP = RegisterI_.Word; break; default: // Instruction did not involve H or L, so backtrack one instruction and parse again _cpuTicks += 4; _registerFile.PC--; break; } } }
public void SLA(ByteRegister op) { _registerFile.F = (byte)(op.Value >> 7); op.Value <<= 1; _registerFile.F |= _lookupTables.Sz53P[op.Value]; }
/// <summary> /// Execution of CB xx codes /// </summary> /// <param name="opcode">opcode to execute</param> private void Execute_CB_Prefix(byte opcode) { // Operations with single byte register // The format is 00 ooo rrr where ooo is the operation and rrr is the register ByteRegister reg = GetByteRegisterByOpcode(opcode); ByteRegister _HL_ = null; // Check if the source/target is (HL) if (reg == null) reg = _HL_ = new ByteRegister(_memory.ReadByte(_registerFile.HL)); Execute_CB_on_reg(opcode, reg); if (reg == _HL_) { // The target is (HL) _cpuTicks += 15; _memory.WriteByte(_registerFile.HL, _HL_.Value); //We should not do this when we check bits (BIT n,r)! } else { // The source is a normal registry _cpuTicks += 8; } }
public void SLL(ByteRegister op) { _registerFile.F = (byte)(op.Value >> 7); op.Value = (byte)((op.Value << 1) | 0x01); _registerFile.F |= _lookupTables.Sz53P[op.Value]; }
public void DEC(ByteRegister op) { _registerFile.F = (byte)((_registerFile.F & FlagRegisterDefinition.C) | ((op.Value & 0x0F) != 0 ? (byte)0 : FlagRegisterDefinition.H) | FlagRegisterDefinition.N); op.Value--; _registerFile.F |= (byte)((op.Value == 0x79 ? FlagRegisterDefinition.V : (byte)0) | _lookupTables.Sz53[op.Value]); }
public void RR(ByteRegister op) { _alu.RR(op); }
public ENC28J60() { sync = new object(); ResetPointers(); var econ1 = new ByteRegister(this).WithValueField(0, 2, out currentBank, name: "BSEL") .WithFlag(2, out ethernetReceiveEnabled, name: "RXEN") .WithFlag(3, FieldMode.Read, writeCallback: (_, value) => { if (value) { TransmitPacket(); } }, name: "TXRTS") .WithFlag(7, name: "TXRST"); var econ2 = new ByteRegister(this, 0x80).WithFlag(6, FieldMode.Read, writeCallback: delegate { waitingPacketCount = Math.Max(0, waitingPacketCount - 1); RefreshInterruptStatus(); }, name: "PKTDEC") .WithFlag(7, out autoIncrement, name: "AUTOINC"); var estat = new ByteRegister(this, 1).WithReadCallback(delegate { transmitPacketInterrupt.Value = false; RefreshInterruptStatus(); }) // not sure .WithFlag(0, FieldMode.Read, name: "CLKRDY"); // we're always ready, so the reset value is 1 var eie = new ByteRegister(this).WithFlag(3, out transmitPacketInterruptEnabled, writeCallback: delegate { RefreshInterruptStatus(); }, name: "TXIE") .WithFlag(6, out receivePacketInterruptEnabled, writeCallback: delegate { RefreshInterruptStatus(); }, name: "PKTIE") .WithFlag(7, out interruptsEnabled, writeCallback: delegate { RefreshInterruptStatus(); }, name: "INTIE"); var eir = new ByteRegister(this).WithFlag(0, name: "RXERIF") .WithFlag(3, out transmitPacketInterrupt, writeCallback: delegate { RefreshInterruptStatus(); }, name: "TXIF") .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => IsReceiveInterruptActive(), name: "PKTIF"); var bank0Map = new Dictionary <long, ByteRegister> { // ERDPTL { 0x00, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(bufferReadPointer), writeCallback: (_, value) => SetLowByteOf(ref bufferReadPointer, (byte)value)) }, // ERDPTH { 0x01, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(bufferReadPointer), writeCallback: (_, value) => SetHighByteOf(ref bufferReadPointer, (byte)value)) }, // EWRPTL { 0x02, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(bufferWritePointer), writeCallback: (_, value) => SetLowByteOf(ref bufferWritePointer, (byte)value)) }, // EWRPTH { 0x03, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(bufferWritePointer), writeCallback: (_, value) => SetHighByteOf(ref bufferWritePointer, (byte)value)) }, // ETXSTL { 0x04, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(transmitBufferStart), writeCallback: (_, value) => SetLowByteOf(ref transmitBufferStart, (byte)value)) }, // ETXSTH { 0x05, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(transmitBufferStart), writeCallback: (_, value) => SetHighByteOf(ref transmitBufferStart, (byte)value)) }, // ETXNDL { 0x06, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(transmitBufferEnd), writeCallback: (_, value) => SetLowByteOf(ref transmitBufferEnd, (byte)value)) }, // ETXNDH { 0x07, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(transmitBufferEnd), writeCallback: (_, value) => SetHighByteOf(ref transmitBufferEnd, (byte)value)) }, // ERXSTL { 0x08, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(receiveBufferStart), writeCallback: (_, value) => { SetLowByteOf(ref receiveBufferStart, (byte)value); currentReceiveWritePointer = receiveBufferStart; }) }, // ERXSTH { 0x09, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(receiveBufferStart), writeCallback: (_, value) => { SetHighByteOf(ref receiveBufferStart, (byte)value); currentReceiveWritePointer = receiveBufferStart; }) }, // ERXNDL { 0x0A, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(receiveBufferEnd), writeCallback: (_, value) => SetLowByteOf(ref receiveBufferEnd, (byte)value)) }, // ERXNDH { 0x0B, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(receiveBufferEnd), writeCallback: (_, value) => SetHighByteOf(ref receiveBufferEnd, (byte)value)) }, // ERXRDPTL { 0x0C, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(receiveReadPointer), writeCallback: (_, value) => bufferedLowByteOfReceiveReadPointer = (byte)value) }, // ERXRDPTH { 0x0D, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(receiveReadPointer), writeCallback: (_, value) => receiveReadPointer = (int)(bufferedLowByteOfReceiveReadPointer | ((value << 8)))) } }; var bank1Map = new Dictionary <long, ByteRegister> { // ERXFCON { 0x18, new ByteRegister(this, 0xA1).WithFlag(5, out crcEnabled, name: "CRCEN") }, // EPKTCNT { 0x19, new ByteRegister(this).WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (uint)waitingPacketCount) } }; var bank2Map = new Dictionary <long, ByteRegister> { // MACON1 // note that we currently ignore all the Pause Control Frame stuff { 0x00, new ByteRegister(this).WithFlag(0, out macReceiveEnabled, name: "MARXEN").WithFlag(2, name: "RXPAUS").WithFlag(3, name: "TXPAUS") }, // MACON3 { 0x02, new ByteRegister(this).WithFlag(0, name: "FULDPX") }, // MABBIPG (too low level parameter for emulation) { 0x04, new ByteRegister(this).WithValueField(0, 7) }, // MAIPGL (same as above) { 0x06, new ByteRegister(this).WithValueField(0, 7) }, // MAIPGH (same as above) { 0x07, new ByteRegister(this).WithValueField(0, 7) }, // MICMD { 0x12, new ByteRegister(this).WithFlag(0, writeCallback: (_, value) => { if (value) { ReadPhyRegister(); } }, name: "MIIRD") }, // MIREGADR { 0x14, new ByteRegister(this).WithValueField(0, 5, out miiRegisterAddress) }, // MIWRL { 0x16, new ByteRegister(this).WithValueField(0, 8, out phyWriteLow) }, // MIWRH { 0x17, new ByteRegister(this).WithValueField(0, 8, writeCallback: (_, value) => WritePhyRegister((ushort)(phyWriteLow.Value | (value << 8)))) }, // MIRDL { 0x18, new ByteRegister(this).WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (byte)lastReadPhyRegisterValue) }, // MIRDH { 0x19, new ByteRegister(this).WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (byte)(lastReadPhyRegisterValue >> 8)) } }; var bank3Map = new Dictionary <long, ByteRegister> { // MAADR5 { 0x00, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.E, writeCallback: (_, value) => MAC = MAC.WithNewOctets(e: (byte)value)) }, // MADDR6 { 0x01, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.F, writeCallback: (_, value) => MAC = MAC.WithNewOctets(f: (byte)value)) }, // MADDR3 { 0x02, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.C, writeCallback: (_, value) => MAC = MAC.WithNewOctets(c: (byte)value)) }, // MADDR4 { 0x03, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.D, writeCallback: (_, value) => MAC = MAC.WithNewOctets(d: (byte)value)) }, // MADDR1 { 0x04, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.A, writeCallback: (_, value) => MAC = MAC.WithNewOctets(a: (byte)value)) }, // MADDR2 { 0x05, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.B, writeCallback: (_, value) => MAC = MAC.WithNewOctets(b: (byte)value)) }, // MISTAT { 0x0A, new ByteRegister(this).WithFlag(0, FieldMode.Read, name: "BUSY") } // we're never busy }; var maps = new[] { bank0Map, bank1Map, bank2Map, bank3Map }; // registers below are available in all banks foreach (var map in maps) { map.Add(0x1B, eie); // EIE map.Add(0x1C, eir); // EIR map.Add(0x1D, estat); // ESTAT map.Add(0x1E, econ2); // ECON2 map.Add(0x1F, econ1); // ECON1 } registers = maps.Select(x => new ByteRegisterCollection(this, x)).ToArray(); ethernetBuffer = new byte[8.KB()]; phyRegisters = new WordRegisterCollection(this, new Dictionary <long, WordRegister> { // PHCON1 { 0x00, new WordRegister(this).WithFlag(8, name: "PDPXMD") }, // full duplex stuff, ignored // PHCON2 { 0x10, new WordRegister(this) } }); IRQ = new GPIO(); IRQ.Set(); // the interrupt output is negated }
public void SRL(ByteRegister op) { throw new NotImplementedException(); }