summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/pic16x8x/pic16x8x.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/pic16x8x/pic16x8x.cpp')
-rw-r--r--src/devices/cpu/pic16x8x/pic16x8x.cpp1413
1 files changed, 1413 insertions, 0 deletions
diff --git a/src/devices/cpu/pic16x8x/pic16x8x.cpp b/src/devices/cpu/pic16x8x/pic16x8x.cpp
new file mode 100644
index 00000000000..4cf256763aa
--- /dev/null
+++ b/src/devices/cpu/pic16x8x/pic16x8x.cpp
@@ -0,0 +1,1413 @@
+// license:BSD-3-Clause
+// copyright-holders:Tony La Porta, Grull Osgo
+/****************************************************************************************
+
+ Microchip PIC16x8x Emulator
+
+ https://ww1.microchip.com/downloads/en/DeviceDoc/30445D.pdf
+
+ Based on MAME's PIC16C5x/62x cpu devices developed by Tony La Porta
+ and improvements to SFR's accesss made by ajrhacker.
+
+ Preliminary version to give support to magicle and other games
+ on misc/magicard.cpp driver, providing some protection level via I2C bus.
+
+ Coded and partially tested:
+ - All instruction set and disassembler.
+ - I/O Ports.
+ - WDT.
+ - Timer/Counter.
+ - All (4) interrupt sources.
+ - Input lines.
+ - Internal EEPROM with default and NVRAM functionality.
+
+ ToDo:
+ - Verify if data ram addreess map mirroring is properly coded acording to datasheet description.
+
+ - Choose wich is the best option to set the config word, if via set instruction or from dump data or some sort of combination via default options.
+
+ - Improve the debug section.
+
+ - Verify eeprom write sequence (see datashhet)
+
+ Improvements:
+ - SFR's Flag description (bit oriented instructions) in disassembler where possible.
+
+****************************************************************************************/
+
+#include "emu.h"
+#include "pic16x8x.h"
+#include "16x8xdsm.h"
+
+DEFINE_DEVICE_TYPE(PIC16CR83, pic16cr83_device, "pic16cr83", "Microchip PIC16CR83")
+DEFINE_DEVICE_TYPE(PIC16CR84, pic16cr84_device, "pic16cr84", "Microchip PIC16CR84")
+DEFINE_DEVICE_TYPE(PIC16F83, pic16f83_device, "pic16f83", "Microchip PIC16F83")
+DEFINE_DEVICE_TYPE(PIC16F84, pic16f84_device, "pic16f84", "Microchip PIC16F84")
+DEFINE_DEVICE_TYPE(PIC16F84A, pic16f84a_device, "pic16f84a", "Microchip PIC16F84A")
+
+/****************************************************************************
+ * Internal Memory Maps
+ ****************************************************************************/
+
+void pic16x8x_device::rom_9(address_map &map)
+{
+ map(0x000, 0x1ff).rom();
+}
+
+void pic16x8x_device::rom_10(address_map &map)
+{
+ map(0x000, 0x3ff).rom();
+}
+
+
+/* Notes from datasheet:
+ The address depends on the device used.
+Devices with 36 bytes ends at 2Fh, devices with
+68 bytes ends at 4Fh.*/
+
+void pic16x8x_device::core_regs(address_map &map, u8 mirror)
+{
+ map(0x00, 0x00).noprw().mirror(mirror); // Not an actual register, reading indirectly (FSR=0) returns 0
+ map(0x01, 0x01).rw(FUNC(pic16x8x_device::tmr0_r), FUNC(pic16x8x_device::tmr0_w));
+ map(0x02, 0x02).rw(FUNC(pic16x8x_device::pcl_r), FUNC(pic16x8x_device::pcl_w)).mirror(mirror);
+ map(0x03, 0x03).rw(FUNC(pic16x8x_device::status_r), FUNC(pic16x8x_device::status_w)).mirror(mirror);
+ map(0x04, 0x04).rw(FUNC(pic16x8x_device::fsr_r), FUNC(pic16x8x_device::fsr_w)).mirror(mirror);
+ map(0x05, 0x05).rw(FUNC(pic16x8x_device::porta_r), FUNC(pic16x8x_device::porta_w));
+ map(0x06, 0x06).rw(FUNC(pic16x8x_device::portb_r), FUNC(pic16x8x_device::portb_w));
+ map(0x07, 0x07).noprw().mirror(mirror); // Not an actual register, returns 0
+ map(0x08, 0x08).rw(FUNC(pic16x8x_device::eedata_r), FUNC(pic16x8x_device::eedata_w));
+ map(0x09, 0x09).rw(FUNC(pic16x8x_device::eeadr_r), FUNC(pic16x8x_device::eeadr_w));
+ map(0x0a, 0x0a).rw(FUNC(pic16x8x_device::pclath_r), FUNC(pic16x8x_device::pclath_w)).mirror(mirror);
+ map(0x0b, 0x0b).rw(FUNC(pic16x8x_device::intcon_r), FUNC(pic16x8x_device::intcon_w)).mirror(mirror);
+ map(0x81, 0x81).rw(FUNC(pic16x8x_device::option_r), FUNC(pic16x8x_device::option_w));
+ map(0x85, 0x85).rw(FUNC(pic16x8x_device::trisa_r), FUNC(pic16x8x_device::trisa_w));
+ map(0x86, 0x86).rw(FUNC(pic16x8x_device::trisb_r), FUNC(pic16x8x_device::trisb_w));
+ map(0x88, 0x88).rw(FUNC(pic16x8x_device::eecon1_r), FUNC(pic16x8x_device::eecon1_w));
+ map(0x89, 0x89).rw(FUNC(pic16x8x_device::eecon2_r), FUNC(pic16x8x_device::eecon2_w));
+}
+
+void pic16x8x_device::ram_6(address_map &map)
+{
+ // 0x00 - 0x0b SFR's Bank 0
+ // 0x0c - 0x2f GPR's
+ // 0x80 - 0x8b SFR's Bank 1
+ // 0x8c - 0xaf GPR Mirrored to 0x0c - 0x2f
+ core_regs(map, 0x80);
+ map(0x0c, 0x2f).ram().mirror(0x80);
+}
+
+void pic16x8x_device::ram_7(address_map &map)
+{
+ // 0x00 - 0x0b SFR's Bank 0
+ // 0x0c - 0x4f GPR's
+ // 0x80 - 0x8b SFR's Bank 1
+ // 0x8c - 0xcf GPR Mirrored to 0x0c - 0x4f
+ core_regs(map, 0x80);
+ map(0x0c, 0x4f).ram().mirror(0x80);
+}
+
+
+pic16x8x_device::pic16x8x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, int program_width, address_map_constructor program_map, address_map_constructor data_map)
+ : cpu_device(mconfig, type, tag, owner, clock)
+ , device_nvram_interface(mconfig, *this)
+ , m_region(*this, DEVICE_SELF)
+ , m_program_config("program", ENDIANNESS_LITTLE, 16, program_width, -1, program_map)
+ , m_data_config("data", ENDIANNESS_LITTLE, 8, 8, 0, data_map)
+ , m_program_width(program_width)
+ , m_CONFIG(0x3fff)
+ , m_read_port(*this, 0)
+ , m_write_port(*this)
+{
+}
+
+pic16x83_device::pic16x83_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
+ : pic16x8x_device(mconfig, type, tag, owner, clock, 9, address_map_constructor(FUNC(pic16x8x_device::rom_9), this), address_map_constructor(FUNC(pic16x8x_device::ram_6), this))
+{
+}
+
+pic16x84_device::pic16x84_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
+ : pic16x8x_device(mconfig, type, tag, owner, clock, 10, address_map_constructor(FUNC(pic16x8x_device::rom_10), this), address_map_constructor(FUNC(pic16x8x_device::ram_7), this))
+{
+}
+
+pic16cr83_device::pic16cr83_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : pic16x83_device(mconfig, PIC16CR83, tag, owner, clock)
+{
+}
+
+pic16cr84_device::pic16cr84_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : pic16x84_device(mconfig, PIC16CR84, tag, owner, clock)
+{
+}
+
+pic16f83_device::pic16f83_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : pic16x83_device(mconfig, PIC16F83, tag, owner, clock)
+{
+}
+
+pic16f84_device::pic16f84_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : pic16x84_device(mconfig, PIC16F84, tag, owner, clock)
+{
+}
+
+pic16f84a_device::pic16f84a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : pic16x84_device(mconfig, PIC16F84A, tag, owner, clock)
+{
+}
+
+device_memory_interface::space_config_vector pic16x8x_device::memory_space_config() const
+{
+ return space_config_vector
+ {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_DATA, &m_data_config)
+ };
+}
+
+std::unique_ptr<util::disasm_interface> pic16x8x_device::create_disassembler()
+{
+ return std::make_unique<pic16x8x_disassembler>();
+}
+
+
+/* Notes about NVRAM / PIC eeprom data
+
+Dump size is 4280 Bytes, 16 bits wide.
+0000h-03ffh is program data
+0400h-3fffh is dummy data
+4000h-400fh is user and config data
+4010h-41ffh is dummy data
+4200h-4280h is eeprom data
+
+NVRAM saved data is 8 bits wide, in accordance with the data width
+of the EEPROM memory of the PIC.
+This means that we need to perform the conversion from the default
+NVRAM dumped data to the NVRAM saved data.
+*/
+
+void pic16x8x_device::nvram_default()
+{
+ u16 dump_size = 0x4280;
+ u16 eeprom_dump = 0x4200;
+
+ // populate from a memory region if present
+ if (m_region.found())
+ {
+ if (m_region->bytes() != dump_size) // pic memory dump total size
+ {
+ fatalerror("Region '%s' wrong size (expected size = 0x%X)\n", tag(), dump_size);
+ }
+
+ if (m_region->bytewidth() != 2) // pic memory dumps are 16 bits wide
+ {
+ fatalerror("Region '%s' needs to be an 16-bit region\n", tag());
+ }
+
+ // Get default NVRAM data from memory region
+ memcpy(m_buff, m_region->base() + eeprom_dump, m_internal_eeprom_size * 2);
+
+ // Data width conversion
+ for (u8 i = 0; i < m_internal_eeprom_size; i++)
+ m_eeprom_data[i] = m_buff[i] & 0x00ff;
+ }
+}
+
+bool pic16x8x_device::nvram_read(util::read_stream &file)
+{
+ auto const [err, actual] = read(file, m_eeprom_data, m_internal_eeprom_size);
+ return !err && (actual == m_internal_eeprom_size);
+}
+
+bool pic16x8x_device::nvram_write(util::write_stream &file)
+{
+ auto const [err, actual] = write(file, m_eeprom_data, m_internal_eeprom_size);
+ return !err && (actual == m_internal_eeprom_size);
+}
+
+
+// EEPROM data access
+u8 pic16x8x_device::m_eeread(offs_t offs)
+{
+ return m_eeprom_data[offs];
+}
+
+void pic16x8x_device::m_eewrite(offs_t offs, u8 data)
+{
+ m_eeprom_data[offs] = data & m_data_mask;
+}
+
+
+// ******** The following is the STATUS flag register definition. ********
+// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+// | IRP | RP1 | RP0 | T0| PD| Z | DC | C |
+
+// IRP Register Bank Select - Unimplemented on this device
+constexpr u8 RP1_FLAG = 0x40; // Register Bank Select Bit 1
+constexpr u8 RP0_FLAG = 0x20; // Register Bank Select Bit 0
+ // 00 = Bank 0 (00h-7fh)
+ // 01 = Bank 1 (80h-ffh)
+ // 10 = Bank 2 (100h-17fh)
+ // 11 = Bank 3 (180h-1ffh)
+ // Each bank is 128 bytes. Only bit RP0 is used by the PIC16F8X. RP1 should be mantained clear.
+constexpr u8 TO_FLAG = 0x10; // TO Time Out flag (WatchDog)
+constexpr u8 PD_FLAG = 0x08; // PD Power Down flag
+constexpr u8 Z_FLAG = 0x04; // Z Zero Flag
+constexpr u8 DC_FLAG = 0x02; // DC Digit Carry/Borrow flag (Nibble)
+constexpr u8 C_FLAG = 0x01; // C Carry/Borrow Flag (Byte)
+
+#define RP0 (m_STATUS & RP0_FLAG)
+#define TO (m_STATUS & TO_FLAG)
+#define PD (m_STATUS & PD_FLAG)
+#define ZERO (m_STATUS & Z_FLAG)
+#define DC (m_STATUS & DC_FLAG)
+#define CARRY (m_STATUS & C_FLAG)
+
+
+// ******** The following is the OPTION flag register definition. ********
+// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+// | RBPU | INTEDG | T0CS | T0SE | PSA | PS2 | PS1 | PS0 |
+constexpr u8 RBPU_FLAG = 0x80; // RBPU PORTB Pull-up Enable bit
+constexpr u8 INTEDG_FLAG = 0x40; // INTEDG Interrupt Edge Select bit
+ // 1 = Interrupt on rising edge of RB0/INT pin
+ // 0 = Interrupt on falling edge of RB0/INT pin
+constexpr u8 T0CS_FLAG = 0x20; // T0CS Timer 0 clock source select
+ // 1 = Transition on RA4/T0CKI pin
+ // 0 = Internal instruction cycle clock (CLKOUT)
+constexpr u8 T0SE_FLAG = 0x10; // T0SE Timer 0 clock source edge select
+ // 1 = Increment on high-to-low transition on RA4/T0CKI pin
+ // 0 = Increment on low-to-high transition on RA4/T0CKI pin
+constexpr u8 PSA_FLAG = 0x08; // PSA Prescaler Assignment bit
+ // 1 = Prescaler is assigned to the WDT
+ // 0 = Prescaler is assigned to the Timer0 module
+constexpr u8 PS_REG = 0x07; // PS Prescaler Rate select
+
+#define RBPU (m_OPTION & RBPU_FLAG)
+#define INTEDG (m_OPTION & INTEDG_FLAG)
+#define T0CS (m_OPTION & T0CS_FLAG)
+#define T0SE (m_OPTION & T0SE_FLAG)
+#define PSA (m_OPTION & PSA_FLAG)
+#define PS (m_OPTION & PS_REG)
+
+
+// ******** The following is the CONFIG Flag register definition. ********
+// | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+// | CP | WDTE | FOSC |
+ // CP Code Protect (ROM read protect)
+constexpr u8 WDTE_FLAG = 0x04; // WDTE WatchDog Timer enable
+constexpr u8 FOSC_FLAG = 0x03; // FOSC Oscillator source select
+
+#define WDTE (m_CONFIG & WDTE_FLAG)
+#define FOSC (m_CONFIG & FOSC_FLAG)
+
+
+// ******** The following is the INTCON flag register definition. ********
+// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+// | GIE | EEIE | T0IE | INTE | RBIE | T0IF | INTF | RBIF |
+
+constexpr u8 GIE_FLAG = 0x80;
+constexpr u8 EEIE_FLAG = 0x40;
+constexpr u8 T0IE_FLAG = 0x20;
+constexpr u8 INTE_FLAG = 0x10;
+constexpr u8 RBIE_FLAG = 0x08;
+constexpr u8 T0IF_FLAG = 0x04;
+constexpr u8 INTF_FLAG = 0x02;
+constexpr u8 RBIF_FLAG = 0x01;
+
+#define GIE (m_INTCON & GIE_FLAG)
+#define EEIE (m_INTCON & EEIE_FLAG)
+#define T0IE (m_INTCON & T0IE_FLAG)
+#define INTE (m_INTCON & INTE_FLAG)
+#define RBIE (m_INTCON & RBIE_FLAG)
+#define T0IF (m_INTCON & T0IF_FLAG)
+#define INTF (m_INTCON & INTF_FLAG)
+#define RBIF (m_INTCON & RBIF_FLAG)
+
+// ******** The following is the EECON1 Flag register definition. ********
+// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+// | | | | EEIF | WRERR | WREN | WR | RD |
+
+constexpr u8 EEIF_FLAG = 0x10;
+constexpr u8 WRERR_FLAG = 0x08;
+constexpr u8 WREN_FLAG = 0x04;
+constexpr u8 EEWR_FLAG = 0x02;
+constexpr u8 EERD_FLAG = 0x01;
+
+#define EEIF (m_EECON1 & EEIF_FLAG)
+#define WRERR (m_EECON1 & WRERR_FLAG)
+#define WREN (m_EECON1 & WREN_FLAG)
+#define EEWR (m_EECON1 & EEWR_FLAG)
+#define EERD (m_EECON1 & EERD_FLAG)
+
+/************************************************************************
+ * Fixed Vectors
+ ************************************************************************/
+
+constexpr u8 RESET_VECTOR = 0x00;
+constexpr u8 INT_VECTOR = 0x04;
+
+/************************************************************************
+ * Shortcuts
+ ************************************************************************/
+
+#define RISING_EDGE_RB0 (( (int)(RB0_in - m_old_RB0) > 0) ? 1 : 0)
+#define FALLING_EDGE_RB0 (( (int)(RB0_in - m_old_RB0) < 0) ? 1 : 0)
+
+#define M_RDEEPROM(A) m_eeread(A)
+#define M_WREEPROM(A,V) m_eewrite(A, V)
+
+#define CLR(flagreg, flag) (flagreg &= u8(~flag))
+#define SET(flagreg, flag) (flagreg |= (flag))
+
+#define ADDR ((m_opcode.b.l & 0x7f) | (RP0 << 2))
+#define BITPOS ((m_opcode.w >> 7) & 7)
+
+
+void pic16x8x_device::calc_zero_flag()
+{
+ if (m_ALU == 0)
+ SET(m_STATUS, Z_FLAG);
+ else
+ CLR(m_STATUS, Z_FLAG);
+}
+
+void pic16x8x_device::calc_add_flags(u8 augend)
+{
+ calc_zero_flag();
+
+ if (augend > m_ALU)
+ SET(m_STATUS, C_FLAG);
+ else
+ CLR(m_STATUS, C_FLAG);
+
+ if ((augend & 0x0f) > (m_ALU & 0x0f))
+ SET(m_STATUS, DC_FLAG);
+ else
+ CLR(m_STATUS, DC_FLAG);
+}
+
+void pic16x8x_device::calc_sub_flags(u8 minuend)
+{
+ calc_zero_flag();
+
+ if (minuend < m_ALU)
+ CLR(m_STATUS, C_FLAG);
+ else
+ SET(m_STATUS, C_FLAG);
+
+ if ((minuend & 0x0f) < (m_ALU & 0x0f))
+ CLR(m_STATUS, DC_FLAG);
+ else
+ SET(m_STATUS, DC_FLAG);
+}
+
+void pic16x8x_device::set_pc(u16 addr)
+{
+ m_PCL = addr & m_program_mask;
+}
+
+/*
+Notes about Stack (from datasheet)
+After the stack has been PUSHed eight times, the ninth
+push overwrites the value that was stored from the first
+push. The tenth push overwrites the second push (and
+so on)
+*/
+
+u16 pic16x8x_device::pop_stack()
+{
+ m_stack_pointer = (m_stack_pointer - 1) & 0x0f;
+ u16 data = m_STACK[m_stack_pointer];
+ return data & m_program_mask;
+}
+
+void pic16x8x_device::push_stack(u16 data)
+{
+ m_STACK[m_stack_pointer] = data & m_program_mask;
+ m_stack_pointer = (m_stack_pointer + 1) & 0x0f;
+}
+
+void pic16x8x_device::store_result(u8 addr, u8 data)
+{
+ if (m_opcode.b.l & 0x80)
+ store_regfile(addr, data);
+ else
+ m_W = data;
+
+}
+
+// *** Special function registers (SFR's) ***
+
+// *** SFR Access ***
+u8 pic16x8x_device::get_regfile(u8 addr)
+{
+ if (addr == 0)
+ { // Indirect addressing
+ addr = m_FSR & m_data_mask;
+ }
+ return m_data.read_byte(addr);
+}
+
+void pic16x8x_device::store_regfile(u8 addr, u8 data)
+{
+ if (addr == 0)
+ { // Indirect addressing
+ addr = m_FSR & m_data_mask;
+ }
+ m_data.write_byte(addr, data);
+}
+
+// *** SFR Functions ***
+u8 pic16x8x_device::tmr0_r()
+{
+ return m_TMR0;
+}
+
+void pic16x8x_device::tmr0_w(u8 data)
+{
+ m_delay_timer = 2; // Timer increment is inhibited for 2 cycles
+ if (PSA == 0) m_prescaler = 0; // Must clear the Prescaler
+ m_TMR0 = data;
+}
+
+u8 pic16x8x_device::pcl_r()
+{
+ return m_PCL;
+}
+
+void pic16x8x_device::pcl_w(u8 data)
+{
+ m_PCL = data;
+ set_pc((m_PCLATH << 8) | data);
+ m_inst_cycles++;
+}
+
+u8 pic16x8x_device::status_r()
+{
+ return m_STATUS;
+}
+
+void pic16x8x_device::status_w(u8 data)
+{
+ m_STATUS = (m_STATUS & (TO_FLAG | PD_FLAG)) | (data & u8(~(TO_FLAG | PD_FLAG)));
+}
+
+u8 pic16x8x_device::fsr_r()
+{
+ return m_FSR | (uint8_t)(~m_data_mask);
+}
+
+void pic16x8x_device::fsr_w(u8 data)
+{
+ m_FSR = data | u8(~m_data_mask);
+}
+
+u8 pic16x8x_device::porta_r()
+{
+ u8 data = m_read_port[PORTA](PORTA, 0xff);
+ data &= m_port_tris[PORTA];
+ data |= (u8(~m_port_tris[PORTA]) & m_port_data[PORTA]);
+ return data & 0x1f; // 5-bit port (only lower 5 bits used)
+}
+
+void pic16x8x_device::porta_w(u8 data)
+{
+ data &= 0x1f; // 5-bit port (only lower 5 bits used)
+ m_write_port[PORTA](PORTA, data & u8(~m_port_tris[PORTA]) & 0x1f, u8(~m_port_tris[PORTA]) & 0x1f);
+ m_port_data[PORTA] = data;
+}
+
+u8 pic16x8x_device::portb_r()
+{
+ u8 data = m_read_port[PORTB](PORTB, 0xff);
+ data &= m_port_tris[PORTB];
+ data |= (u8(~m_port_tris[PORTB]) & m_port_data[PORTB]);
+ return data;
+}
+
+void pic16x8x_device::portb_w(u8 data)
+{
+ m_write_port[PORTB](PORTB, data & u8(~m_port_tris[PORTB]), u8(~m_port_tris[PORTB]));
+ m_port_data[PORTB] = data;
+}
+
+u8 pic16x8x_device::eedata_r()
+{
+ return m_EEDATA;
+}
+
+void pic16x8x_device::eedata_w(u8 data)
+{
+ m_EEDATA = data;
+}
+
+u8 pic16x8x_device::eeadr_r()
+{
+ return m_EEADR;
+}
+
+void pic16x8x_device::eeadr_w(u8 data)
+{
+ m_EEADR = data;
+}
+
+u8 pic16x8x_device::pclath_r()
+{
+ return m_PCLATH;
+}
+
+void pic16x8x_device::pclath_w(u8 data)
+{
+ m_PCLATH = data;
+}
+
+u8 pic16x8x_device::intcon_r()
+{
+ return m_INTCON;
+}
+
+void pic16x8x_device::intcon_w(u8 data)
+{
+ m_INTCON = data;
+}
+
+u8 pic16x8x_device::trisa_r()
+{
+ return m_port_tris[PORTA];
+}
+
+void pic16x8x_device::trisa_w(u8 data)
+{
+ if (m_port_tris[PORTA] != data)
+ {
+ m_port_tris[PORTA] = data | 0xe0;
+ m_write_port[PORTA](PORTA, m_port_data[PORTA] & u8(~m_port_tris[PORTA]) & 0x1f, u8(~m_port_tris[PORTA]) & 0x1f);
+ }
+}
+
+u8 pic16x8x_device::trisb_r()
+{
+ return m_port_tris[PORTB];
+}
+
+void pic16x8x_device::trisb_w(u8 data)
+{
+ if (m_port_tris[PORTB] != data)
+ {
+ m_port_tris[PORTB] = data;
+ m_write_port[PORTB](PORTB, m_port_data[PORTB] & u8(~m_port_tris[PORTB]), u8(~m_port_tris[PORTB]));
+ }
+}
+
+u8 pic16x8x_device::eecon1_r()
+{
+ return m_EECON1;
+}
+
+void pic16x8x_device::eecon1_w(u8 data)
+{
+ if (!((data & WREN_FLAG) && EEWR)) // The WR bit will be inhibited from being set unless the WREN bit is set.(from datasheet)
+ CLR(data, EEWR); // Clear the flag if it don't meet the requirements
+
+ m_EECON1 = data; // update register
+
+ if (EERD) // cheack flag condition to read eeprom
+ {
+ m_EEDATA = M_RDEEPROM(m_EEADR); // do eeprom read
+ CLR(m_EECON1, EERD_FLAG); // clear read enable flag
+ }
+
+ if (WREN && EEWR && (m_EECON2 == 0xff)) // check flags conditions to write eeprom
+ {
+ M_WREEPROM(m_EEADR, data); // do eeprom write
+ CLR(m_EECON1, EEWR_FLAG); // clear write enable flag
+ SET(m_EECON1, EEIF_FLAG); // assuming write was done, set EEIF
+ }
+}
+
+u8 pic16x8x_device::eecon2_r()
+{
+ return m_EECON2;
+}
+
+void pic16x8x_device::eecon2_w(u8 data)
+{
+ if ((m_EECON2 != 0x55) & (data == 0x55))
+ m_EECON2 = data;
+ else if ((m_EECON2 == 0x55) & (data == 0xaa))
+ m_EECON2 = 0xff;
+ else
+ m_EECON2 = data;
+}
+
+u8 pic16x8x_device::option_r()
+{
+ return m_OPTION;
+}
+
+void pic16x8x_device::option_w(u8 data)
+{
+ m_OPTION = data;
+}
+
+/************************************************************************
+ * Emulate the Instructions
+ ************************************************************************/
+
+void pic16x8x_device::illegal()
+{
+ logerror("PIC16x8x: PC=%03x, Illegal opcode = %04x\n", m_PREVPC, m_opcode.w);
+}
+
+/*
+ Note:
+ According to the manual, if the STATUS register is the destination for an instruction that affects the Z, DC or C bits
+ then the write to these three bits is disabled. These bits are set or cleared according to the device logic.
+ To ensure this is correctly emulated, in instructions that write to the file registers, always change the status flags
+ *after* storing the result of the instruction.
+ e.g. CALCULATE_*, SET(STATUS,*_FLAG) and CLR(STATUS,*_FLAG) should appear as the last steps of the instruction emulation.
+*/
+
+void pic16x8x_device::addlw()
+{
+ // New
+ m_ALU = m_opcode.b.l + m_W;
+ m_W = m_ALU;
+ calc_zero_flag();
+}
+
+void pic16x8x_device::addwf()
+{
+ u8 augend = get_regfile(ADDR);
+ m_ALU = augend + m_W;
+ store_result(ADDR, m_ALU);
+ calc_add_flags(augend);
+}
+
+void pic16x8x_device::andlw()
+{
+ m_ALU = m_opcode.b.l & m_W;
+ m_W = m_ALU;
+ calc_zero_flag();
+}
+
+void pic16x8x_device::andwf()
+{
+ m_ALU = get_regfile(ADDR) & m_W;
+ store_result(ADDR, m_ALU);
+ calc_zero_flag();
+}
+
+void pic16x8x_device::bcf()
+{
+ m_ALU = get_regfile(ADDR);
+ m_ALU &= ~(1 << BITPOS);
+ store_regfile(ADDR, m_ALU);
+}
+
+void pic16x8x_device::bsf()
+{
+ m_ALU = get_regfile(ADDR);
+ m_ALU |= 1 << BITPOS;
+ store_regfile(ADDR, m_ALU);
+}
+
+void pic16x8x_device::btfss()
+{
+ if (BIT(get_regfile(ADDR), BITPOS))
+ {
+ set_pc(m_PCL + 1);
+ m_inst_cycles++; // Add NOP cycles
+ }
+}
+
+void pic16x8x_device::btfsc()
+{
+ if (!BIT(get_regfile(ADDR), BITPOS))
+ {
+ set_pc(m_PCL + 1);
+ m_inst_cycles++; // Add NOP cycles
+ }
+}
+
+void pic16x8x_device::call()
+{
+ push_stack(m_PCL);
+ set_pc(((m_PCLATH & 0x18) << 8 ) | (m_opcode.w & 0x07ff));
+}
+
+
+void pic16x8x_device::clrw()
+{
+ m_W = 0;
+ SET(m_STATUS, Z_FLAG);
+}
+
+void pic16x8x_device::clrf()
+{
+ store_regfile(ADDR, 0);
+ SET(m_STATUS, Z_FLAG);
+}
+
+void pic16x8x_device::clrwdt()
+{
+ m_WDT = 0;
+ if (PSA) m_prescaler = 0;
+ SET(m_STATUS, TO_FLAG);
+ SET(m_STATUS, PD_FLAG);
+}
+
+void pic16x8x_device::comf()
+{
+ m_ALU = u8(~(get_regfile(ADDR)));
+ store_result(ADDR, m_ALU);
+ calc_zero_flag();
+}
+
+void pic16x8x_device::decf()
+{
+ m_ALU = get_regfile(ADDR) - 1;
+ store_result(ADDR, m_ALU);
+ calc_zero_flag();
+}
+
+void pic16x8x_device::decfsz()
+{
+ m_ALU = get_regfile(ADDR) - 1;
+ store_result(ADDR, m_ALU);
+ if (m_ALU == 0)
+ {
+ set_pc(m_PCL + 1);
+ m_inst_cycles++; // Add NOP cycles
+ }
+}
+
+void pic16x8x_device::goto_op()
+{
+ set_pc(((m_PCLATH & 0x18) << 8 ) | (m_opcode.w & 0x07ff));
+}
+
+void pic16x8x_device::incf()
+{
+ m_ALU = get_regfile(ADDR) + 1;
+ store_result(ADDR, m_ALU);
+ calc_zero_flag();
+}
+
+void pic16x8x_device::incfsz()
+{
+ m_ALU = get_regfile(ADDR) + 1;
+ store_result(ADDR, m_ALU);
+ if (m_ALU == 0)
+ {
+ set_pc(m_PCL + 1);
+ m_inst_cycles++; // Add NOP cycles
+ }
+}
+
+void pic16x8x_device::iorlw()
+{
+ m_ALU = m_opcode.b.l | m_W;
+ m_W = m_ALU;
+ calc_zero_flag();
+}
+
+void pic16x8x_device::iorwf()
+{
+ m_ALU = get_regfile(ADDR) | m_W;
+ store_result(ADDR, m_ALU);
+ calc_zero_flag();
+}
+
+void pic16x8x_device::movf()
+{
+ m_ALU = get_regfile(ADDR);
+ store_result(ADDR, m_ALU);
+ calc_zero_flag();
+}
+
+void pic16x8x_device::movlw()
+{
+ m_W = m_opcode.b.l;
+}
+
+void pic16x8x_device::movwf()
+{
+ store_regfile(ADDR, m_W);
+}
+
+void pic16x8x_device::nop()
+{
+ // Do nothing
+}
+
+void pic16x8x_device::retfie() // new for 16x8x
+{
+ // New
+ set_pc(pop_stack());
+ m_irq_in_progress = false;
+}
+
+void pic16x8x_device::retlw()
+{
+ m_W = m_opcode.b.l;
+ set_pc(pop_stack());
+}
+
+void pic16x8x_device::retrn() // new for 16x8x
+{
+ set_pc(pop_stack());
+}
+
+void pic16x8x_device::rlf()
+{
+ m_ALU = get_regfile(ADDR);
+ int carry = BIT(m_ALU, 7);
+ m_ALU <<= 1;
+ if (m_STATUS & C_FLAG) m_ALU |= 1;
+ store_result(ADDR, m_ALU);
+
+ if (carry)
+ SET(m_STATUS, C_FLAG);
+ else
+ CLR(m_STATUS, C_FLAG);
+}
+
+void pic16x8x_device::rrf()
+{
+ m_ALU = get_regfile(ADDR);
+ int carry = BIT(m_ALU, 0);
+ m_ALU >>= 1;
+ if (m_STATUS & C_FLAG) m_ALU |= 0x80;
+ store_result(ADDR, m_ALU);
+
+ if (carry)
+ SET(m_STATUS, C_FLAG);
+ else
+ CLR(m_STATUS, C_FLAG);
+}
+
+void pic16x8x_device::sleepic()
+{
+ if (WDTE) m_WDT = 0;
+ if (PSA) m_prescaler = 0;
+ SET(m_STATUS, TO_FLAG);
+ CLR(m_STATUS, PD_FLAG);
+}
+
+void pic16x8x_device::sublw() // new for 16x8x : Substract W from literal
+{
+ // New
+ u8 minuend = m_opcode.b.l;
+ m_ALU = minuend - m_W;
+ m_W = m_ALU;
+ calc_sub_flags(minuend);
+}
+
+void pic16x8x_device::subwf() // Substract W from f
+{
+ u8 minuend = get_regfile(ADDR);
+ m_ALU = minuend - m_W;
+ store_result(ADDR, m_ALU);
+ calc_sub_flags(minuend);
+}
+
+void pic16x8x_device::swapf()
+{
+ u8 reg = get_regfile(ADDR);
+ m_ALU = reg << 4 | reg >> 4;
+ store_result(ADDR, m_ALU);
+}
+
+void pic16x8x_device::xorlw()
+{
+ m_ALU = m_W ^ m_opcode.b.l;
+ m_W = m_ALU;
+ calc_zero_flag();
+}
+
+void pic16x8x_device::xorwf()
+{
+ m_ALU = get_regfile(ADDR) ^ m_W;
+ store_result(ADDR, m_ALU);
+ calc_zero_flag();
+}
+
+
+/***********************************************************************
+ * Opcode Table (Cycles, Instruction)
+ ***********************************************************************/
+
+const pic16x8x_device::pic16x8x_opcode pic16x8x_device::s_opcode_main[128]=
+{
+ {1, &pic16x8x_device::nop },{1, &pic16x8x_device::movwf },{1, &pic16x8x_device::clrw },{1, &pic16x8x_device::clrf }, // 00
+ {1, &pic16x8x_device::subwf },{1, &pic16x8x_device::subwf },{1, &pic16x8x_device::decf },{1, &pic16x8x_device::decf }, // 04
+ {1, &pic16x8x_device::iorwf },{1, &pic16x8x_device::iorwf },{1, &pic16x8x_device::andwf },{1, &pic16x8x_device::andwf }, // 08
+ {1, &pic16x8x_device::xorwf },{1, &pic16x8x_device::xorwf },{1, &pic16x8x_device::addwf },{1, &pic16x8x_device::addwf }, // 0C
+ {1, &pic16x8x_device::movf },{1, &pic16x8x_device::movf },{1, &pic16x8x_device::comf },{1, &pic16x8x_device::comf }, // 10
+ {1, &pic16x8x_device::incf },{1, &pic16x8x_device::incf },{1, &pic16x8x_device::decfsz },{1, &pic16x8x_device::decfsz }, // 14
+ {1, &pic16x8x_device::rrf },{1, &pic16x8x_device::rrf },{1, &pic16x8x_device::rlf },{1, &pic16x8x_device::rlf }, // 18
+ {1, &pic16x8x_device::swapf },{1, &pic16x8x_device::swapf },{1, &pic16x8x_device::incfsz },{1, &pic16x8x_device::incfsz }, // 1C
+ {1, &pic16x8x_device::bcf },{1, &pic16x8x_device::bcf },{1, &pic16x8x_device::bcf },{1, &pic16x8x_device::bcf }, // 20
+ {1, &pic16x8x_device::bcf },{1, &pic16x8x_device::bcf },{1, &pic16x8x_device::bcf },{1, &pic16x8x_device::bcf },
+ {1, &pic16x8x_device::bsf },{1, &pic16x8x_device::bsf },{1, &pic16x8x_device::bsf },{1, &pic16x8x_device::bsf }, // 28
+ {1, &pic16x8x_device::bsf },{1, &pic16x8x_device::bsf },{1, &pic16x8x_device::bsf },{1, &pic16x8x_device::bsf },
+ {1, &pic16x8x_device::btfsc },{1, &pic16x8x_device::btfsc },{1, &pic16x8x_device::btfsc },{1, &pic16x8x_device::btfsc }, // 30
+ {1, &pic16x8x_device::btfsc },{1, &pic16x8x_device::btfsc },{1, &pic16x8x_device::btfsc },{1, &pic16x8x_device::btfsc },
+ {1, &pic16x8x_device::btfss },{1, &pic16x8x_device::btfss },{1, &pic16x8x_device::btfss },{1, &pic16x8x_device::btfss }, // 38
+ {1, &pic16x8x_device::btfss },{1, &pic16x8x_device::btfss },{1, &pic16x8x_device::btfss },{1, &pic16x8x_device::btfss },
+ {2, &pic16x8x_device::call },{2, &pic16x8x_device::call },{2, &pic16x8x_device::call },{2, &pic16x8x_device::call }, // 40
+ {2, &pic16x8x_device::call },{2, &pic16x8x_device::call },{2, &pic16x8x_device::call },{2, &pic16x8x_device::call },
+ {2, &pic16x8x_device::call },{2, &pic16x8x_device::call },{2, &pic16x8x_device::call },{2, &pic16x8x_device::call }, // 48
+ {2, &pic16x8x_device::call },{2, &pic16x8x_device::call },{2, &pic16x8x_device::call },{2, &pic16x8x_device::call },
+ {2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op }, // 50
+ {2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op },
+ {2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op }, // 58
+ {2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op },{2, &pic16x8x_device::goto_op },
+ {1, &pic16x8x_device::movlw },{1, &pic16x8x_device::movlw },{1, &pic16x8x_device::movlw },{1, &pic16x8x_device::movlw }, // 60
+ {1, &pic16x8x_device::movlw },{1, &pic16x8x_device::movlw },{1, &pic16x8x_device::movlw },{1, &pic16x8x_device::movlw },
+ {2, &pic16x8x_device::retlw },{2, &pic16x8x_device::retlw },{2, &pic16x8x_device::retlw },{2, &pic16x8x_device::retlw }, // 68
+ {2, &pic16x8x_device::retlw },{2, &pic16x8x_device::retlw },{2, &pic16x8x_device::retlw },{2, &pic16x8x_device::retlw },
+ {1, &pic16x8x_device::iorlw },{1, &pic16x8x_device::iorlw },{1, &pic16x8x_device::andlw },{1, &pic16x8x_device::andlw }, // 70
+ {1, &pic16x8x_device::xorlw },{1, &pic16x8x_device::xorlw },{1, &pic16x8x_device::xorlw },{1, &pic16x8x_device::xorlw }, // 74
+ {1, &pic16x8x_device::sublw },{1, &pic16x8x_device::sublw },{1, &pic16x8x_device::sublw },{1, &pic16x8x_device::sublw }, // 78
+ {1, &pic16x8x_device::addlw },{1, &pic16x8x_device::addlw },{1, &pic16x8x_device::addlw },{1, &pic16x8x_device::addlw } // 7C
+};
+
+const pic16x8x_device::pic16x8x_opcode pic16x8x_device::s_opcode_00x[128]=
+{
+ {1, &pic16x8x_device::nop },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 00
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {2, &pic16x8x_device::retrn },{2, &pic16x8x_device::retfie },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 08
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 10
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 18
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::nop },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 20
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 28
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 30
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 38
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::nop },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 40
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 48
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 50
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 58
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::nop },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::sleepic }, // 60
+ {1, &pic16x8x_device::clrwdt },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 68
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 70
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }, // 78
+ {1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal },{1, &pic16x8x_device::illegal }
+};
+
+
+/****************************************************************************
+ * Inits CPU emulation
+ ****************************************************************************/
+
+enum
+{
+ PIC16X8x_PC=1, PIC16X8x_W, PIC16X8x_ALU, PIC16X8x_WDT, PIC16X8x_PSCL, PIC16X8x_CONFIG
+};
+
+void pic16x8x_device::device_start()
+{
+ space(AS_PROGRAM).cache(m_program);
+ space(AS_DATA).specific(m_data);
+
+ m_program_mask = (1 << m_program_width) - 1;
+ m_data_mask = 0xff;
+ m_status_mask = 0xff;
+ m_PCL = 0;
+ m_PREVPC = 0;
+ m_W = 0;
+ m_OPTION = 0;
+ m_ALU = 0;
+ m_WDT = 0;
+ m_TMR0 = 0;
+ m_STATUS = ~m_status_mask;
+ m_FSR = 0;
+ std::fill(std::begin(m_port_data), std::end(m_port_data), 0);
+ std::fill(std::begin(m_port_tris), std::end(m_port_tris), 0);
+ std::fill(std::begin(m_STACK), std::end(m_STACK), 0);
+ m_prescaler = 0;
+ m_opcode.w = 0;
+ m_delay_timer = 0;
+ m_rtcc = 0;
+ m_count_cycles = 0;
+ m_inst_cycles = 0;
+ m_stack_pointer = 0;
+ m_debugger_temp = 0;
+ m_portb_chdetect_temp = 0xff;
+ m_irq_in_progress = false;
+
+ // save states
+ save_item(NAME(m_PCL));
+ save_item(NAME(m_PREVPC));
+ save_item(NAME(m_CONFIG));
+ save_item(NAME(m_W));
+ save_item(NAME(m_OPTION));
+ save_item(NAME(m_ALU));
+ save_item(NAME(m_WDT));
+ save_item(NAME(m_TMR0));
+ save_item(NAME(m_STATUS));
+ save_item(NAME(m_FSR));
+ save_item(NAME(m_port_data));
+ save_item(NAME(m_port_tris));
+ save_item(NAME(m_EEDATA));
+ save_item(NAME(m_EEADR));
+ save_item(NAME(m_EECON1));
+ save_item(NAME(m_EECON2));
+ save_item(NAME(m_PCLATH));
+ save_item(NAME(m_INTCON));
+ save_item(NAME(m_STACK));
+ save_item(NAME(m_prescaler));
+ save_item(NAME(m_opcode.w));
+ save_item(NAME(m_delay_timer));
+ save_item(NAME(m_rtcc));
+ save_item(NAME(m_count_cycles));
+ save_item(NAME(m_inst_cycles));
+ save_item(NAME(m_stack_pointer));
+ save_item(NAME(m_old_RB0));
+ save_item(NAME(m_irq_in_progress));
+ save_item(NAME(m_eeprom_data));
+
+
+ // debugger
+ state_add( PIC16X8x_PC, "PC", m_PCL).mask(0xfff).formatstr("%03X");
+ state_add( PIC16X8x_W, "W", m_W).formatstr("%02X");
+ state_add( PIC16X8x_ALU, "ALU", m_ALU).formatstr("%02X");
+ state_add( PIC16X8x_WDT, "WDT", m_WDT).formatstr("%04X");
+ state_add( PIC16X8x_CONFIG, "CNF", m_CONFIG).formatstr("%04X");
+ state_add( PIC16X8x_PSCL, "PSCL", m_debugger_temp).callimport().formatstr("%3s");
+
+ state_add( STATE_GENPC, "GENPC", m_PCL).noshow();
+ state_add( STATE_GENPCBASE, "CURPC", m_PREVPC).noshow();
+ state_add( STATE_GENFLAGS, "GENFLAGS", m_OPTION).formatstr("%13s").noshow();
+
+ set_icountptr(m_icount);
+}
+
+
+void pic16x8x_device::state_import(const device_state_entry &entry)
+{
+ switch (entry.index())
+ {
+ case PIC16X8x_PSCL:
+ m_prescaler = m_debugger_temp;
+ break;
+ }
+}
+
+void pic16x8x_device::state_export(const device_state_entry &entry)
+{
+ switch (entry.index())
+ {
+ }
+}
+
+void pic16x8x_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ switch (entry.index())
+ {
+ case PIC16X8x_PSCL:
+ str = string_format("%c%02X", ((m_OPTION & 0x08) ? 'W' : 'T'), m_prescaler);
+ break;
+
+ case STATE_GENFLAGS:
+ str = string_format("%01x%c%c%c%c%c %c%c%c%03x",
+ (m_STATUS & 0xe0) >> 5,
+ m_STATUS & 0x10 ? '.':'O', // WDT Overflow
+ m_STATUS & 0x08 ? 'P':'D', // Power/Down
+ m_STATUS & 0x04 ? 'Z':'.', // Zero
+ m_STATUS & 0x02 ? 'c':'b', // Nibble Carry/Borrow
+ m_STATUS & 0x01 ? 'C':'B', // Carry/Borrow
+
+ m_OPTION & 0x20 ? 'C':'T', // Counter/Timer
+ m_OPTION & 0x10 ? 'N':'P', // Negative/Positive
+ m_OPTION & 0x08 ? 'W':'T', // WatchDog/Timer
+ m_OPTION & 0x08 ? (1<<(m_OPTION&7)) : (2<<(m_OPTION&7)));
+ break;
+ }
+}
+
+
+/****************************************************************************
+ * Reset registers to their initial values
+ ****************************************************************************/
+
+void pic16x8x_device::reset_regs()
+{
+ set_pc(RESET_VECTOR);
+ m_port_tris[PORTA] = 0x1f;
+ m_port_tris[PORTB] = 0xff;
+ m_OPTION = T0CS_FLAG | T0SE_FLAG | PSA_FLAG | PS_REG;
+ m_PREVPC = m_PCL;
+ m_STATUS = 0x18;
+ m_PCL = 0;
+ m_prescaler = 0;
+ m_delay_timer = 0;
+ m_inst_cycles = 0;
+ m_count_cycles = 0;
+ m_stack_pointer = 0;
+ m_portb_chdetect_temp = 0xff;
+ m_old_RB0 = 0;
+}
+
+void pic16x8x_device::set_config(u16 data)
+{
+ logerror("Writing %04x to the PIC16x8x configuration bits\n", data);
+ m_CONFIG = (data & 0x3fff);
+}
+
+void pic16x8x_device::watchdog_reset()
+{
+ SET(m_STATUS, TO_FLAG | PD_FLAG | Z_FLAG | DC_FLAG | C_FLAG);
+ reset_regs();
+}
+
+void pic16x8x_device::device_reset()
+{
+ reset_regs();
+ CLR(m_STATUS, RP0_FLAG);
+ CLR(m_STATUS, RP1_FLAG);
+
+ // Setting TO_FLAG from Config Word
+ if (WDTE)
+ SET(m_STATUS, TO_FLAG);
+ else
+ CLR(m_STATUS, TO_FLAG);
+
+ SET(m_STATUS, PD_FLAG);
+ store_regfile(3, m_STATUS);
+ store_regfile(4, m_FSR);
+}
+
+
+/****************************************************************************
+ * WatchDog
+ ****************************************************************************/
+
+void pic16x8x_device::update_watchdog(int counts)
+{
+ /*
+ WatchDog is set up to count 18,000 (0x464f hex) ticks to provide
+ the timeout period of 0.018ms based on a 4MHz input clock.
+ Note: the 4MHz clock should be divided by the PIC16C5x_CLOCK_DIVIDER
+ which effectively makes the PIC run at 1MHz internally.
+
+ If the current instruction is CLRWDT or SLEEP, don't update the WDT
+ */
+
+ if ((m_opcode.w != 3) && (m_opcode.w != 4))
+ {
+ u16 old_WDT = m_WDT;
+
+ m_WDT -= counts;
+
+ if (m_WDT > 0x464f)
+ {
+ m_WDT = 0x464f - (0xffff - m_WDT);
+ }
+
+ if (((old_WDT != 0) && (old_WDT < m_WDT)) || (m_WDT == 0))
+ {
+ if (PSA)
+ {
+ m_prescaler++;
+ if (m_prescaler >= (1 << PS))
+ { // Prescale values from 1 to 128
+ m_prescaler = 0;
+ CLR(m_STATUS, TO_FLAG);
+ watchdog_reset();
+ }
+ }
+ else
+ {
+ CLR(m_STATUS, TO_FLAG);
+ watchdog_reset();
+ }
+ }
+ }
+}
+
+
+/****************************************************************************
+ * Update Timer
+ ****************************************************************************/
+
+void pic16x8x_device::update_timer(int counts)
+{
+ if (m_delay_timer > 0)
+ { // Timer increment is inhibited
+ int dt = m_delay_timer;
+ m_delay_timer -= m_inst_cycles;
+ counts -= dt;
+ }
+
+ if (m_delay_timer > 0 || counts <= 0)
+ return;
+
+ if (PSA == 0)
+ { // if PSA is cleared, prescaler is assigned to timer
+ m_prescaler += counts;
+ if (m_prescaler >= (2 << PS))
+ { // Prescale values from 2 to 256
+ m_TMR0 += (m_prescaler / (2 << PS));
+ if(m_TMR0 == 0)
+ SET(m_INTCON, T0IF_FLAG);
+ m_prescaler %= (2 << PS); // Overflow prescaler
+ }
+ }
+ else
+ {
+ m_TMR0 += counts;
+ if(m_TMR0 == 0)
+ SET(m_INTCON, T0IF_FLAG);
+ }
+}
+
+void pic16x8x_device::execute_set_input(int line, int state)
+{
+ u8 RB0_in;
+ switch (line)
+ {
+ // RTCC/RA4_T0CKI pin
+ case PIC16x8x_T0CKI:
+ if (T0CS && state != m_rtcc)
+ { // Count mode, edge triggered
+ if ((T0SE && !state) || (!T0SE && state))
+ m_count_cycles++;
+ }
+ m_rtcc = state;
+ break;
+
+ case PIC16x8x_RB0INT:
+ RB0_in = state;
+ if (INTEDG)
+ { /* Interrupt on Rising edge RB0 input */
+ if (RISING_EDGE_RB0)
+ {
+ SET(m_INTCON, INTF_FLAG);
+ }
+ }
+ else
+ { /* Interrupt on Falling edge RB0 input */
+ if (FALLING_EDGE_RB0)
+ {
+ SET(m_INTCON, INTF_FLAG);
+ }
+ }
+ m_old_RB0 = RB0_in;
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*-------------------------------------------------
+ check_irqs - check for and process IRQs
+-------------------------------------------------*/
+
+void pic16x8x_device::check_irqs()
+{
+ // if something is in progress, we do nothing
+ if (m_irq_in_progress)
+ return;
+
+ if (m_read_port[PORTB](PORTB, 0xf0) != m_portb_chdetect_temp)
+ {
+ m_portb_chdetect_temp = m_read_port[PORTB](PORTB, 0xf0);
+ SET(m_INTCON, RBIF_FLAG);
+ }
+ else if (GIE && RBIE && RBIF)
+ {
+ push_stack(m_PCL);
+ set_pc(INT_VECTOR);
+ standard_irq_callback(0, m_PCL);
+ m_irq_in_progress = true;
+ }
+ else if (GIE && INTE && INTF)
+ {
+ push_stack(m_PCL);
+ set_pc(INT_VECTOR);
+ standard_irq_callback(1, m_PCL);
+ m_irq_in_progress = true;
+ }
+ else if (GIE && T0IE && T0IF)
+ {
+ push_stack(m_PCL);
+ set_pc(INT_VECTOR);
+ standard_irq_callback(2, m_PCL);
+ m_irq_in_progress = true;
+ }
+ else if (GIE && EEIE && EEIF)
+ {
+ push_stack(m_PCL);
+ set_pc(INT_VECTOR);
+ standard_irq_callback(3, m_PCL);
+ m_irq_in_progress = true;
+ }
+}
+
+
+/****************************************************************************
+ * Execute IPeriod. Return 0 if emulation should be stopped
+ ****************************************************************************/
+
+void pic16x8x_device::execute_run()
+{
+ do
+ {
+ // check interrupts
+ check_irqs();
+
+ if (PD == 0)
+ { // Sleep Mode
+ m_count_cycles = 0;
+ m_inst_cycles = 1;
+ debugger_instruction_hook(m_PCL);
+ }
+ else
+ {
+ m_PREVPC = m_PCL;
+
+ debugger_instruction_hook(m_PCL);
+
+ m_opcode.w = m_program.read_word(m_PCL);
+ set_pc(m_PCL + 1);
+
+ if ( (m_opcode.w & 0x3f80) != 0x0000)
+ { // Do all opcodes except the 00? ones
+ m_inst_cycles = s_opcode_main[((m_opcode.w >> 7) & 0x7f)].cycles;
+ (this->*s_opcode_main[((m_opcode.w >> 7) & 0x7f)].function)();
+ }
+ else
+ { // Opcode 0x00? has many opcodes in its minor nibble
+ m_inst_cycles = s_opcode_00x[(m_opcode.b.l & 0x7f)].cycles;
+ (this->*s_opcode_00x[(m_opcode.b.l & 0x7f)].function)();
+ }
+
+ update_timer(T0CS ? m_count_cycles : m_inst_cycles);
+ m_count_cycles = 0;
+ }
+
+ if (WDTE)
+ {
+ update_watchdog(m_inst_cycles);
+ }
+
+ m_icount -= m_inst_cycles;
+
+ } while (m_icount > 0);
+}