1001 lines
28 KiB
C
1001 lines
28 KiB
C
/* license:BSD-3-Clause
|
|
copyright-holders:Tony La Porta */
|
|
/**************************************************************************\
|
|
* Microchip PIC16C5x Emulator *
|
|
* *
|
|
* Copyright Tony La Porta *
|
|
* Originally written for the MAME project. *
|
|
* *
|
|
* *
|
|
* Addressing architecture is based on the Harvard addressing scheme. *
|
|
* *
|
|
* *
|
|
* **** Change Log **** *
|
|
* TLP (06-Apr-2003) *
|
|
* - First Public release. *
|
|
* BO (07-Apr-2003) Ver 1.01 *
|
|
* - Renamed 'sleep' function to 'sleepic' to avoid C conflicts. *
|
|
* TLP (09-Apr-2003) Ver 1.10 *
|
|
* - Fixed modification of file register $03 (Status). *
|
|
* - Corrected support for 7FFh (12-bit) size ROMs. *
|
|
* - The 'call' and 'goto' instructions weren't correctly handling the *
|
|
* STATUS page info correctly. *
|
|
* - The FSR register was incorrectly oring the data with 0xe0 when read. *
|
|
* - Prescaler masking information was set to 3 instead of 7. *
|
|
* - Prescaler assign bit was set to 4 instead of 8. *
|
|
* - Timer source and edge select flags/masks were wrong. *
|
|
* - Corrected the memory bank selection in GET/SET_REGFILE and also the *
|
|
* indirect register addressing. *
|
|
* BMP (18-May-2003) Ver 1.11 *
|
|
* - PIC16C5x_get_reg functions were missing 'returns'. *
|
|
* TLP (27-May-2003) Ver 1.12 *
|
|
* - Fixed the WatchDog timer count. *
|
|
* - The Prescaler rate was incorrectly being zeroed, instead of the *
|
|
* actual Prescaler counter in the CLRWDT and SLEEP instructions. *
|
|
* - Added masking to the FSR register. Upper unused bits are always 1. *
|
|
* TLP (27-Aug-2009) Ver 1.13 *
|
|
* - Indirect addressing was not taking into account special purpose *
|
|
* memory mapped locations. *
|
|
* - 'iorlw' instruction was saving the result to memory instead of *
|
|
* the W register. *
|
|
* - 'tris' instruction no longer modifies Port-C on PIC models that *
|
|
* do not have Port-C implemented. *
|
|
* TLP (07-Sep-2009) Ver 1.14 *
|
|
* - Edge sense control for the T0 count input was incorrectly reversed *
|
|
* LE (05-Feb-2017) Ver 1.15 *
|
|
* - Allow writing all bits of the status register except TO and PD. *
|
|
* This enables e.g. bcf, bsf or clrf to change the flags when the *
|
|
* status register is the destination. *
|
|
* - Changed rlf and rrf to update the carry flag in the last step. *
|
|
* Fixes the case where the status register is the destination. *
|
|
* hap (12-Feb-2017) Ver 1.16 *
|
|
* - Added basic support for the old GI PIC1650 and PIC1655. *
|
|
* - Made RTCC(aka T0CKI) pin an inputline handler. *
|
|
* *
|
|
* *
|
|
* **** Notes: **** *
|
|
* PIC WatchDog Timer has a separate internal clock. For the moment, we're *
|
|
* basing the count on a 4MHz input clock, since 4MHz is the typical *
|
|
* input frequency (but by no means always). *
|
|
* A single scaler is available for the Counter/Timer or WatchDog Timer. *
|
|
* When connected to the Counter/Timer, it functions as a Prescaler, *
|
|
* hence prescale overflows, tick the Counter/Timer. *
|
|
* When connected to the WatchDog Timer, it functions as a Postscaler *
|
|
* hence WatchDog Timer overflows, tick the Postscaler. This scenario *
|
|
* means that the WatchDog timeout occurs when the Postscaler has *
|
|
* reached the scaler rate value, not when the WatchDog reaches zero. *
|
|
* CLRWDT should prevent the WatchDog Timer from timing out and generating *
|
|
* a device reset, but how is not known. The manual also mentions that *
|
|
* the WatchDog Timer can only be disabled during ROM programming, and *
|
|
* no other means seem to exist??? *
|
|
* *
|
|
\**************************************************************************/
|
|
|
|
#include <string.h>
|
|
#include "pic16c5x.h"
|
|
|
|
/******************** CPU Internal Registers *******************/
|
|
static uint16 m_PC;
|
|
static uint16 m_PREVPC; /* previous program counter */
|
|
static uint8 m_W;
|
|
static uint8 m_OPTION;
|
|
static uint16 m_CONFIG;
|
|
static uint8 m_ALU;
|
|
static uint16 m_WDT;
|
|
static uint8 m_TRISA;
|
|
static uint8 m_TRISB;
|
|
static uint8 m_TRISC;
|
|
static uint16 m_STACK[2];
|
|
static uint16 m_prescaler; /* Note: this is really an 8-bit register */
|
|
static uint16 m_opcode;
|
|
static uint8 m_internalram[128];
|
|
static uint8 *m_rom;
|
|
|
|
static int m_icount;
|
|
static int m_picmodel;
|
|
static int m_delay_timer;
|
|
static uint16 m_temp_config;
|
|
static int m_rtcc;
|
|
static uint8 m_count_pending; /* boolean type */
|
|
static int8 m_old_data;
|
|
static uint8 m_picRAMmask;
|
|
static uint16 m_picROMmask;
|
|
static int m_inst_cycles;
|
|
static int m_clock2cycle;
|
|
|
|
/*address_space *m_program;
|
|
memory_access_cache<1, -1, ENDIANNESS_LITTLE> *m_cache;
|
|
address_space *m_data;*/
|
|
|
|
/* i/o handlers */
|
|
static pic16c5x_readfunc m_read;
|
|
static pic16c5x_writefunc m_write;
|
|
|
|
static void PIC16C5x(int program_width, int data_width, int picmodel, uint8 *rom, pic16c5x_readfunc _rd, pic16c5x_writefunc _wr)
|
|
{
|
|
m_rom = rom;
|
|
m_picmodel = picmodel;
|
|
m_temp_config = 0;
|
|
m_picRAMmask = ((1 << data_width) - 1);
|
|
m_picROMmask = ((1 << program_width) - 1);
|
|
m_read = _rd;
|
|
m_write = _wr;
|
|
m_PC = 0;
|
|
m_PREVPC = 0;
|
|
m_W = 0;
|
|
m_OPTION = 0;
|
|
m_CONFIG = 0;
|
|
m_ALU = 0;
|
|
m_WDT = 0;
|
|
m_TRISA = 0;
|
|
m_TRISB = 0;
|
|
m_TRISC = 0;
|
|
m_prescaler = 0;
|
|
m_icount = 0;
|
|
m_delay_timer = 0;
|
|
m_rtcc = 0;
|
|
m_count_pending = 0;
|
|
m_old_data = 0;
|
|
m_inst_cycles = 0;
|
|
m_clock2cycle = 0;
|
|
}
|
|
|
|
void pic16c54_init(uint8 *rom, pic16c5x_readfunc _rd, pic16c5x_writefunc _wr) { PIC16C5x( 9, 5, 0x16C54, rom, _rd, _wr); }
|
|
void pic16c55_init(uint8 *rom, pic16c5x_readfunc _rd, pic16c5x_writefunc _wr) { PIC16C5x( 9, 5, 0x16C55, rom, _rd, _wr); }
|
|
void pic16c56_init(uint8 *rom, pic16c5x_readfunc _rd, pic16c5x_writefunc _wr) { PIC16C5x(10, 5, 0x16C56, rom, _rd, _wr); }
|
|
void pic16c57_init(uint8 *rom, pic16c5x_readfunc _rd, pic16c5x_writefunc _wr) { PIC16C5x(11, 7, 0x16C57, rom, _rd, _wr); }
|
|
void pic16c58_init(uint8 *rom, pic16c5x_readfunc _rd, pic16c5x_writefunc _wr) { PIC16C5x(11, 7, 0x16C58, rom, _rd, _wr); }
|
|
void pic1650_init (uint8 *rom, pic16c5x_readfunc _rd, pic16c5x_writefunc _wr) { PIC16C5x( 9, 5, 0x1650, rom, _rd, _wr); }
|
|
void pic1655_init (uint8 *rom, pic16c5x_readfunc _rd, pic16c5x_writefunc _wr) { PIC16C5x( 9, 5, 0x1655, rom, _rd, _wr); }
|
|
|
|
#define M_OPCODE_B0 (m_opcode & 0xFF)
|
|
#define M_OPCODE_B1 ((m_opcode >> 8) & 0xFF)
|
|
#define M_OPCODE_S0 m_opcode
|
|
|
|
#define M_RDRAM(A) m_internalram[A &m_picRAMmask]
|
|
#define M_WRTRAM(A,V) m_internalram[A &m_picRAMmask] =(V)
|
|
#define M_RDOP(A) m_rom[(A &m_picROMmask) <<1 |0] | m_rom[(A &m_picROMmask) <<1 |1] <<8
|
|
#define ADDR_MASK 0x7ff
|
|
|
|
#define TMR0 m_internalram[1]
|
|
#define PCL m_internalram[2]
|
|
#define STATUS m_internalram[3]
|
|
#define FSR m_internalram[4]
|
|
#define PORTA m_internalram[5]
|
|
#define PORTB m_internalram[6]
|
|
#define PORTC m_internalram[7]
|
|
#define PORTD m_internalram[8]
|
|
#define INDF M_RDRAM(FSR)
|
|
|
|
#define ADDR (M_OPCODE_B0 & 0x1f)
|
|
|
|
|
|
/******** The following is the Status Flag register definition. *********/
|
|
/* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | */
|
|
/* | PA | TO | PD | Z | DC | C | */
|
|
#define PA_REG 0xe0 /* PA Program Page Preselect - bit 8 is unused here */
|
|
#define TO_FLAG 0x10 /* TO Time Out flag (WatchDog) */
|
|
#define PD_FLAG 0x08 /* PD Power Down flag */
|
|
#define Z_FLAG 0x04 /* Z Zero Flag */
|
|
#define DC_FLAG 0x02 /* DC Digit Carry/Borrow flag (Nibble) */
|
|
#define C_FLAG 0x01 /* C Carry/Borrow Flag (Byte) */
|
|
|
|
#define PA (STATUS & PA_REG)
|
|
#define TO (STATUS & TO_FLAG)
|
|
#define PD (STATUS & PD_FLAG)
|
|
#define ZERO (STATUS & Z_FLAG)
|
|
#define DC (STATUS & DC_FLAG)
|
|
#define CARRY (STATUS & C_FLAG)
|
|
|
|
|
|
/******** The following is the Option Flag register definition. *********/
|
|
/* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | */
|
|
/* | 0 | 0 | TOCS | TOSE | PSA | PS | */
|
|
#define T0CS_FLAG 0x20 /* TOCS Timer 0 clock source select */
|
|
#define T0SE_FLAG 0x10 /* TOSE Timer 0 clock source edge select */
|
|
#define PSA_FLAG 0x08 /* PSA Prescaler Assignment bit */
|
|
#define PS_REG 0x07 /* PS Prescaler Rate select */
|
|
|
|
#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) */
|
|
#define WDTE_FLAG 0x04 /* WDTE WatchDog Timer enable */
|
|
#define FOSC_FLAG 0x03 /* FOSC Oscillator source select */
|
|
|
|
#define WDTE (m_CONFIG & WDTE_FLAG)
|
|
#define FOSC (m_CONFIG & FOSC_FLAG)
|
|
|
|
|
|
/************************************************************************
|
|
* Shortcuts
|
|
************************************************************************/
|
|
|
|
#define CLR(flagreg, flag) ( flagreg &= (uint8)(~flag) )
|
|
#define SET(flagreg, flag) ( flagreg |= flag )
|
|
|
|
|
|
/* Easy bit position selectors */
|
|
#define POS ((M_OPCODE_B0 >> 5) & 7)
|
|
static const unsigned int bit_clr[8] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f };
|
|
static const unsigned int bit_set[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
|
|
|
|
static INLINE void CALCULATE_Z_FLAG(void)
|
|
{
|
|
if (m_ALU == 0) SET(STATUS, Z_FLAG);
|
|
else CLR(STATUS, Z_FLAG);
|
|
}
|
|
|
|
static INLINE void CALCULATE_ADD_CARRY(void)
|
|
{
|
|
if ((uint8)(m_old_data) > (uint8)(m_ALU)) {
|
|
SET(STATUS, C_FLAG);
|
|
}
|
|
else {
|
|
CLR(STATUS, C_FLAG);
|
|
}
|
|
}
|
|
|
|
static INLINE void CALCULATE_ADD_DIGITCARRY(void)
|
|
{
|
|
if (((uint8)(m_old_data) & 0x0f) > ((uint8)(m_ALU) & 0x0f)) {
|
|
SET(STATUS, DC_FLAG);
|
|
}
|
|
else {
|
|
CLR(STATUS, DC_FLAG);
|
|
}
|
|
}
|
|
|
|
static INLINE void CALCULATE_SUB_CARRY(void)
|
|
{
|
|
if ((uint8)(m_old_data) < (uint8)(m_ALU)) {
|
|
CLR(STATUS, C_FLAG);
|
|
}
|
|
else {
|
|
SET(STATUS, C_FLAG);
|
|
}
|
|
}
|
|
|
|
static INLINE void CALCULATE_SUB_DIGITCARRY(void)
|
|
{
|
|
if (((uint8)(m_old_data) & 0x0f) < ((uint8)(m_ALU) & 0x0f)) {
|
|
CLR(STATUS, DC_FLAG);
|
|
}
|
|
else {
|
|
SET(STATUS, DC_FLAG);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static INLINE uint16 POP_STACK(void)
|
|
{
|
|
uint16 data = m_STACK[1];
|
|
m_STACK[1] = m_STACK[0];
|
|
return (data & ADDR_MASK);
|
|
}
|
|
static INLINE void PUSH_STACK(uint16 data)
|
|
{
|
|
m_STACK[0] = m_STACK[1];
|
|
m_STACK[1] = (data & ADDR_MASK);
|
|
}
|
|
|
|
|
|
|
|
static INLINE uint8 GET_REGFILE(uint32_t addr) /* Read from internal memory */
|
|
{
|
|
uint8 data = 0;
|
|
|
|
if (addr == 0) { /* Indirect addressing */
|
|
addr = (FSR & m_picRAMmask);
|
|
}
|
|
|
|
if ((m_picmodel == 0x16C57) || (m_picmodel == 0x16C58)) {
|
|
addr |= (FSR & 0x60); /* FSR bits 6-5 are used for banking in direct mode */
|
|
}
|
|
|
|
if ((addr & 0x10) == 0) addr &= 0x0f;
|
|
|
|
switch(addr)
|
|
{
|
|
case 0: /* Not an actual register, so return 0 */
|
|
data = 0;
|
|
break;
|
|
case 4: data = (FSR | (uint8)(~m_picRAMmask));
|
|
break;
|
|
case 5: /* read port A */
|
|
if (m_picmodel == 0x1650) {
|
|
data = m_read(PIC16C5x_PORTA) & PORTA;
|
|
}
|
|
else if (m_picmodel == 0x1655) {
|
|
data = m_read(PIC16C5x_PORTA) & 0x0f;
|
|
}
|
|
else {
|
|
data = m_read(PIC16C5x_PORTA);
|
|
data &= m_TRISA;
|
|
data |= ((uint8)(~m_TRISA) & PORTA);
|
|
data &= 0x0f; /* 4-bit port (only lower 4 bits used) */
|
|
}
|
|
break;
|
|
case 6: /* read port B */
|
|
if (m_picmodel == 0x1650) {
|
|
data = m_read(PIC16C5x_PORTB) & PORTB;
|
|
}
|
|
else if (m_picmodel != 0x1655) { /* B is output-only on 1655 */
|
|
data = m_read(PIC16C5x_PORTB);
|
|
data &= m_TRISB;
|
|
data |= ((uint8)(~m_TRISB) & PORTB);
|
|
}
|
|
break;
|
|
case 7: /* read port C */
|
|
if (m_picmodel == 0x1650 || m_picmodel == 0x1655) {
|
|
data = m_read(PIC16C5x_PORTC) & PORTC;
|
|
}
|
|
else if ((m_picmodel == 0x16C55) || (m_picmodel == 0x16C57)) {
|
|
data = m_read(PIC16C5x_PORTC);
|
|
data &= m_TRISC;
|
|
data |= ((uint8)(~m_TRISC) & PORTC);
|
|
}
|
|
else { /* PIC16C54, PIC16C56, PIC16C58 */
|
|
data = M_RDRAM(addr);
|
|
}
|
|
break;
|
|
case 8: /* read port D */
|
|
if (m_picmodel == 0x1650) {
|
|
data = m_read(PIC16C5x_PORTD) & PORTD;
|
|
}
|
|
else {
|
|
data = M_RDRAM(addr);
|
|
}
|
|
break;
|
|
default: data = M_RDRAM(addr);
|
|
break;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static INLINE void STORE_REGFILE(uint32_t addr, uint8 data) /* Write to internal memory */
|
|
{
|
|
if (addr == 0) { /* Indirect addressing */
|
|
addr = (FSR & m_picRAMmask);
|
|
}
|
|
|
|
if ((m_picmodel == 0x16C57) || (m_picmodel == 0x16C58)) {
|
|
addr |= (FSR & 0x60); /* FSR bits 6-5 are used for banking in direct mode */
|
|
}
|
|
|
|
if ((addr & 0x10) == 0) addr &= 0x0f;
|
|
|
|
switch(addr)
|
|
{
|
|
case 0: /* Not an actual register, nothing to save */
|
|
break;
|
|
case 1: m_delay_timer = 2; /* Timer starts after next two instructions */
|
|
if (PSA == 0) m_prescaler = 0; /* Must clear the Prescaler */
|
|
TMR0 = data;
|
|
break;
|
|
case 2: PCL = data;
|
|
m_PC = ((STATUS & PA_REG) << 4) | data;
|
|
break;
|
|
case 3: STATUS = (STATUS & (TO_FLAG | PD_FLAG)) | (data & (uint8)(~(TO_FLAG | PD_FLAG)));
|
|
break;
|
|
case 4: FSR = (data | (uint8)(~m_picRAMmask));
|
|
break;
|
|
case 5: /* write port A */
|
|
if (m_picmodel == 0x1650) {
|
|
m_write(PIC16C5x_PORTA, data);
|
|
}
|
|
else if (m_picmodel != 0x1655) { /* A is input-only on 1655 */
|
|
data &= 0x0f; /* 4-bit port (only lower 4 bits used) */
|
|
m_write(PIC16C5x_PORTA, data & (uint8)(~m_TRISA));
|
|
}
|
|
PORTA = data;
|
|
break;
|
|
case 6: /* write port B */
|
|
if (m_picmodel == 0x1650 || m_picmodel == 0x1655) {
|
|
m_write(PIC16C5x_PORTB, data);
|
|
}
|
|
else {
|
|
m_write(PIC16C5x_PORTB, data & (uint8)(~m_TRISB));
|
|
}
|
|
PORTB = data;
|
|
break;
|
|
case 7: /* write port C */
|
|
if (m_picmodel == 0x1650 || m_picmodel == 0x1655) {
|
|
m_write(PIC16C5x_PORTC, data);
|
|
}
|
|
else if ((m_picmodel == 0x16C55) || (m_picmodel == 0x16C57)) {
|
|
m_write(PIC16C5x_PORTC, data & (uint8)(~m_TRISC));
|
|
}
|
|
PORTC = data; /* also writes to RAM */
|
|
break;
|
|
case 8: /* write port D */
|
|
if (m_picmodel == 0x1650) {
|
|
m_write(PIC16C5x_PORTD, data);
|
|
}
|
|
PORTD = data; /* also writes to RAM */
|
|
break;
|
|
default: M_WRTRAM(addr, data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static INLINE void STORE_RESULT(uint32_t addr, uint8 data)
|
|
{
|
|
if (M_OPCODE_B0 & 0x20)
|
|
{
|
|
STORE_REGFILE(addr, data);
|
|
}
|
|
else
|
|
{
|
|
m_W = data;
|
|
}
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* Emulate the Instructions
|
|
************************************************************************/
|
|
|
|
/* This following function is here to fill in the void for */
|
|
/* the opcode call function. This function is never called. */
|
|
|
|
|
|
static INLINE void illegal(void)
|
|
{
|
|
/* printf("PIC16C5x: PC=%03x, Illegal opcode = %04x\n", (m_PC-1), M_OPCODE_S0); */
|
|
}
|
|
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
static INLINE void addwf(void)
|
|
{
|
|
m_old_data = GET_REGFILE(ADDR);
|
|
m_ALU = m_old_data + m_W;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
CALCULATE_Z_FLAG();
|
|
CALCULATE_ADD_CARRY();
|
|
CALCULATE_ADD_DIGITCARRY();
|
|
}
|
|
|
|
static INLINE void andwf(void)
|
|
{
|
|
m_ALU = GET_REGFILE(ADDR) & m_W;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
CALCULATE_Z_FLAG();
|
|
}
|
|
|
|
static INLINE void andlw(void)
|
|
{
|
|
m_ALU = M_OPCODE_B0 & m_W;
|
|
m_W = m_ALU;
|
|
CALCULATE_Z_FLAG();
|
|
}
|
|
|
|
static INLINE void bcf(void)
|
|
{
|
|
m_ALU = GET_REGFILE(ADDR);
|
|
m_ALU &= bit_clr[POS];
|
|
STORE_REGFILE(ADDR, m_ALU);
|
|
}
|
|
|
|
static INLINE void bsf(void)
|
|
{
|
|
m_ALU = GET_REGFILE(ADDR);
|
|
m_ALU |= bit_set[POS];
|
|
STORE_REGFILE(ADDR, m_ALU);
|
|
}
|
|
|
|
static INLINE void btfss(void)
|
|
{
|
|
if ((GET_REGFILE(ADDR) & bit_set[POS]) == bit_set[POS])
|
|
{
|
|
m_PC++;
|
|
PCL = m_PC & 0xff;
|
|
m_inst_cycles += 1; /* Add NOP cycles */
|
|
}
|
|
}
|
|
|
|
static INLINE void btfsc(void)
|
|
{
|
|
if ((GET_REGFILE(ADDR) & bit_set[POS]) == 0)
|
|
{
|
|
m_PC++;
|
|
PCL = m_PC & 0xff;
|
|
m_inst_cycles += 1; /* Add NOP cycles */
|
|
}
|
|
}
|
|
|
|
static INLINE void call(void)
|
|
{
|
|
PUSH_STACK(m_PC);
|
|
m_PC = ((STATUS & PA_REG) << 4) | M_OPCODE_B0;
|
|
m_PC &= 0x6ff;
|
|
PCL = m_PC & 0xff;
|
|
}
|
|
|
|
static INLINE void clrw(void)
|
|
{
|
|
m_W = 0;
|
|
SET(STATUS, Z_FLAG);
|
|
}
|
|
|
|
static INLINE void clrf(void)
|
|
{
|
|
STORE_REGFILE(ADDR, 0);
|
|
SET(STATUS, Z_FLAG);
|
|
}
|
|
|
|
static INLINE void clrwdt(void)
|
|
{
|
|
m_WDT = 0;
|
|
if (PSA) m_prescaler = 0;
|
|
SET(STATUS, TO_FLAG);
|
|
SET(STATUS, PD_FLAG);
|
|
}
|
|
|
|
static INLINE void comf(void)
|
|
{
|
|
m_ALU = (uint8)(~(GET_REGFILE(ADDR)));
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
CALCULATE_Z_FLAG();
|
|
}
|
|
|
|
static INLINE void decf(void)
|
|
{
|
|
m_ALU = GET_REGFILE(ADDR) - 1;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
CALCULATE_Z_FLAG();
|
|
}
|
|
|
|
static INLINE void decfsz(void)
|
|
{
|
|
m_ALU = GET_REGFILE(ADDR) - 1;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
if (m_ALU == 0)
|
|
{
|
|
m_PC++;
|
|
PCL = m_PC & 0xff;
|
|
m_inst_cycles += 1; /* Add NOP cycles */
|
|
}
|
|
}
|
|
|
|
static INLINE void goto_op(void)
|
|
{
|
|
m_PC = ((STATUS & PA_REG) << 4) | (M_OPCODE_S0 & 0x1ff);
|
|
m_PC &= ADDR_MASK;
|
|
PCL = m_PC & 0xff;
|
|
}
|
|
|
|
static INLINE void incf(void)
|
|
{
|
|
m_ALU = GET_REGFILE(ADDR) + 1;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
CALCULATE_Z_FLAG();
|
|
}
|
|
|
|
static INLINE void incfsz(void)
|
|
{
|
|
m_ALU = GET_REGFILE(ADDR) + 1;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
if (m_ALU == 0)
|
|
{
|
|
m_PC++;
|
|
PCL = m_PC & 0xff;
|
|
m_inst_cycles += 1; /* Add NOP cycles */
|
|
}
|
|
}
|
|
|
|
static INLINE void iorlw(void)
|
|
{
|
|
m_ALU = M_OPCODE_B0 | m_W;
|
|
m_W = m_ALU;
|
|
CALCULATE_Z_FLAG();
|
|
}
|
|
|
|
static INLINE void iorwf(void)
|
|
{
|
|
m_ALU = GET_REGFILE(ADDR) | m_W;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
CALCULATE_Z_FLAG();
|
|
}
|
|
|
|
static INLINE void movf(void)
|
|
{
|
|
m_ALU = GET_REGFILE(ADDR);
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
CALCULATE_Z_FLAG();
|
|
}
|
|
|
|
static INLINE void movlw(void)
|
|
{
|
|
m_W = M_OPCODE_B0;
|
|
}
|
|
|
|
static INLINE void movwf(void)
|
|
{
|
|
STORE_REGFILE(ADDR, m_W);
|
|
}
|
|
|
|
static INLINE void nop(void)
|
|
{
|
|
/* Do nothing */
|
|
}
|
|
|
|
static INLINE void option(void)
|
|
{
|
|
m_OPTION = m_W & (T0CS_FLAG | T0SE_FLAG | PSA_FLAG | PS_REG);
|
|
}
|
|
|
|
static INLINE void retlw(void)
|
|
{
|
|
m_W = M_OPCODE_B0;
|
|
m_PC = POP_STACK();
|
|
PCL = m_PC & 0xff;
|
|
}
|
|
|
|
static INLINE void rlf(void)
|
|
{
|
|
uint8 bit7;
|
|
m_ALU = GET_REGFILE(ADDR);
|
|
bit7 = m_ALU & 0x80;
|
|
m_ALU <<= 1;
|
|
if (STATUS & C_FLAG) m_ALU |= 1;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
if (bit7) SET(STATUS, C_FLAG);
|
|
else CLR(STATUS, C_FLAG);
|
|
}
|
|
|
|
static INLINE void rrf(void)
|
|
{
|
|
uint8 bit0;
|
|
m_ALU = GET_REGFILE(ADDR);
|
|
bit0 = m_ALU & 1;
|
|
m_ALU >>= 1;
|
|
if (STATUS & C_FLAG) m_ALU |= 0x80;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
if (bit0) SET(STATUS, C_FLAG);
|
|
else CLR(STATUS, C_FLAG);
|
|
}
|
|
|
|
static INLINE void sleepic(void)
|
|
{
|
|
if (WDTE) m_WDT = 0;
|
|
if (PSA) m_prescaler = 0;
|
|
SET(STATUS, TO_FLAG);
|
|
CLR(STATUS, PD_FLAG);
|
|
}
|
|
|
|
static INLINE void subwf(void)
|
|
{
|
|
m_old_data = GET_REGFILE(ADDR);
|
|
m_ALU = m_old_data - m_W;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
CALCULATE_Z_FLAG();
|
|
CALCULATE_SUB_CARRY();
|
|
CALCULATE_SUB_DIGITCARRY();
|
|
}
|
|
|
|
static INLINE void swapf(void)
|
|
{
|
|
m_ALU = ((GET_REGFILE(ADDR) << 4) & 0xf0);
|
|
m_ALU |= ((GET_REGFILE(ADDR) >> 4) & 0x0f);
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
}
|
|
|
|
static INLINE void tris(void)
|
|
{
|
|
switch(M_OPCODE_B0 & 0x7)
|
|
{
|
|
case 5: if (m_TRISA == m_W) break;
|
|
else { m_TRISA = m_W | 0xf0; m_write(PIC16C5x_PORTA, 0x1000 | (PORTA & (uint8)(~m_TRISA) & 0x0f)); break; }
|
|
case 6: if (m_TRISB == m_W) break;
|
|
else { m_TRISB = m_W; m_write(PIC16C5x_PORTB, 0x1000 | (PORTB & (uint8)(~m_TRISB))); break; }
|
|
case 7: if ((m_picmodel == 0x16C55) || (m_picmodel == 0x16C57)) {
|
|
if (m_TRISC == m_W) break;
|
|
else { m_TRISC = m_W; m_write(PIC16C5x_PORTC, 0x1000 | (PORTC & (uint8)(~m_TRISC))); break; }
|
|
}
|
|
else {
|
|
illegal(); break;
|
|
}
|
|
default: illegal(); break;
|
|
}
|
|
}
|
|
|
|
static INLINE void xorlw(void)
|
|
{
|
|
m_ALU = m_W ^ M_OPCODE_B0;
|
|
m_W = m_ALU;
|
|
CALCULATE_Z_FLAG();
|
|
}
|
|
|
|
static INLINE void xorwf(void)
|
|
{
|
|
m_ALU = GET_REGFILE(ADDR) ^ m_W;
|
|
STORE_RESULT(ADDR, m_ALU);
|
|
CALCULATE_Z_FLAG();
|
|
}
|
|
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
* Opcode Table (Cycles, Instruction)
|
|
***********************************************************************/
|
|
|
|
static const uint8 s_opcode_main_cycles[256] = {
|
|
/*00*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*08*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*10*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*18*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*20*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*28*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*30*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*38*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*40*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*48*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*50*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*58*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*60*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*68*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*70*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*78*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
|
|
/*80*/ 2, 2, 2, 2, 2, 2, 2, 2,
|
|
/*88*/ 2, 2, 2, 2, 2, 2, 2, 2,
|
|
/*90*/ 2, 2, 2, 2, 2, 2, 2, 2,
|
|
/*98*/ 2, 2, 2, 2, 2, 2, 2, 2,
|
|
/*A0*/ 2, 2, 2, 2, 2, 2, 2, 2,
|
|
/*A8*/ 2, 2, 2, 2, 2, 2, 2, 2,
|
|
/*B0*/ 2, 2, 2, 2, 2, 2, 2, 2,
|
|
/*B8*/ 2, 2, 2, 2, 2, 2, 2, 2,
|
|
|
|
/*C0*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*C8*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*D0*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*D8*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*E0*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*E8*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*F0*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*F8*/ 1, 1, 1, 1, 1, 1, 1, 1
|
|
};
|
|
|
|
static const uint8 s_opcode_00x_cycles[16] = {
|
|
/*00*/ 1, 1, 1, 1, 1, 1, 1, 1,
|
|
/*08*/ 1, 1, 1, 1, 1, 1, 1, 1
|
|
};
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
* Inits CPU emulation
|
|
****************************************************************************/
|
|
|
|
enum
|
|
{
|
|
PIC16C5x_PC=1, PIC16C5x_STK0, PIC16C5x_STK1, PIC16C5x_FSR,
|
|
PIC16C5x_W, PIC16C5x_ALU, PIC16C5x_STR, PIC16C5x_OPT,
|
|
PIC16C5x_TMR0, PIC16C5x_PRTA, PIC16C5x_PRTB, PIC16C5x_PRTC, PIC16C5x_PRTD,
|
|
PIC16C5x_WDT, PIC16C5x_TRSA, PIC16C5x_TRSB, PIC16C5x_TRSC,
|
|
PIC16C5x_PSCL
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Reset registers to their initial values
|
|
****************************************************************************/
|
|
|
|
static INLINE void PIC16C5x_reset_regs(void)
|
|
{
|
|
m_PC = m_picROMmask;
|
|
m_CONFIG = m_temp_config;
|
|
m_TRISA = 0xff;
|
|
m_TRISB = 0xff;
|
|
m_TRISC = 0xff;
|
|
m_OPTION = (T0CS_FLAG | T0SE_FLAG | PSA_FLAG | PS_REG);
|
|
PCL = 0xff;
|
|
FSR |= (uint8)(~m_picRAMmask);
|
|
m_prescaler = 0;
|
|
m_delay_timer = 0;
|
|
m_inst_cycles = 0;
|
|
m_count_pending = 0;
|
|
}
|
|
|
|
void pic16c5x_reset(uint8 hard)
|
|
{
|
|
if (hard) {
|
|
memset(m_internalram, 0, sizeof(m_internalram));
|
|
PIC16C5x_reset_regs();
|
|
CLR(STATUS, PA_REG);
|
|
SET(STATUS, (TO_FLAG | PD_FLAG));
|
|
m_icount = 0;
|
|
m_clock2cycle = 0;
|
|
} else {
|
|
SET(STATUS, (TO_FLAG | PD_FLAG | Z_FLAG | DC_FLAG | C_FLAG));
|
|
PIC16C5x_reset_regs();
|
|
}
|
|
}
|
|
|
|
void pic16c5x_set_config(uint16 data)
|
|
{
|
|
m_temp_config = data;
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
* WatchDog
|
|
****************************************************************************/
|
|
|
|
static INLINE void PIC16C5x_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_S0 != 3) && (M_OPCODE_S0 != 4))
|
|
{
|
|
uint16 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(STATUS, TO_FLAG);
|
|
/* PIC16C5x_soft_reset(); */
|
|
}
|
|
}
|
|
else {
|
|
CLR(STATUS, TO_FLAG);
|
|
/* PIC16C5x_soft_reset(); */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Update Timer
|
|
****************************************************************************/
|
|
|
|
static INLINE void PIC16C5x_update_timer(int counts)
|
|
{
|
|
if (PSA == 0) {
|
|
m_prescaler += counts;
|
|
if (m_prescaler >= (2 << PS)) { /* Prescale values from 2 to 256 */
|
|
TMR0 += (m_prescaler / (2 << PS));
|
|
m_prescaler %= (2 << PS); /* Overflow prescaler */
|
|
}
|
|
}
|
|
else {
|
|
TMR0 += counts;
|
|
}
|
|
}
|
|
|
|
void pic16c5x_set_input(int line, int state)
|
|
{
|
|
switch (line)
|
|
{
|
|
/* RTCC/T0CKI pin */
|
|
case PIC16C5x_RTCC:
|
|
if (T0CS && state != m_rtcc) /* Count mode, edge triggered */
|
|
if ((T0SE && !state) || (!T0SE && state))
|
|
m_count_pending = 1;
|
|
|
|
m_rtcc = state;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void pic16c5x_run(void)
|
|
{
|
|
if ((++m_clock2cycle &3) ==0) m_icount++;
|
|
|
|
while (m_icount > 0) {
|
|
if (PD == 0) /* Sleep Mode */
|
|
{
|
|
m_count_pending = 0;
|
|
m_inst_cycles = 1;
|
|
if (WDTE) {
|
|
PIC16C5x_update_watchdog(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_count_pending) { /* RTCC/T0CKI clocked while in Count mode */
|
|
m_count_pending = 0;
|
|
PIC16C5x_update_timer(1);
|
|
}
|
|
|
|
m_PREVPC = m_PC;
|
|
|
|
M_OPCODE_S0 = M_RDOP(m_PC);
|
|
/* printf("%04X: %03X %03X\n", m_PC, M_OPCODE_S0, M_OPCODE_B0); */
|
|
m_PC++;
|
|
PCL++;
|
|
|
|
if (m_picmodel == 0x1650 || m_picmodel == 0x1655 || (M_OPCODE_S0 & 0xff0) != 0x000) { /* Do all opcodes except the 00? ones */
|
|
uint8 b1 = (M_OPCODE_S0 >> 4) & 0xff;
|
|
m_inst_cycles = s_opcode_main_cycles[b1];
|
|
switch (b1) {
|
|
#include "pic16c5x_ops_main.inc"
|
|
}
|
|
}
|
|
else { /* Opcode 0x00? has many opcodes in its minor nibble */
|
|
uint8 b1 = M_OPCODE_B0 & 0x1f;
|
|
m_inst_cycles = s_opcode_00x_cycles[b1];
|
|
switch (b1) {
|
|
#include "pic16c5x_ops_00x.inc"
|
|
}
|
|
}
|
|
|
|
if (!T0CS) { /* Timer mode */
|
|
if (m_delay_timer) {
|
|
m_delay_timer--;
|
|
}
|
|
else {
|
|
PIC16C5x_update_timer(m_inst_cycles);
|
|
}
|
|
}
|
|
if (WDTE) {
|
|
PIC16C5x_update_watchdog(m_inst_cycles);
|
|
}
|
|
}
|
|
|
|
m_icount -= m_inst_cycles;
|
|
}
|
|
}
|
|
|
|
void AddExState(void *v, uint32 s, int type, char *desc);
|
|
|
|
void pic16c5x_add_statesinfo(void) {
|
|
AddExState(&m_PC, sizeof(m_PC), 0, "PC00");
|
|
AddExState(&m_PREVPC, sizeof(m_PREVPC), 0, "PRPC");
|
|
AddExState(&m_W, sizeof(m_W), 0, "W000");
|
|
AddExState(&m_OPTION, sizeof(m_OPTION), 0, "OPT0");
|
|
AddExState(&m_CONFIG, sizeof(m_CONFIG), 0, "CONF");
|
|
AddExState(&m_ALU, sizeof(m_ALU), 0, "ALU");
|
|
AddExState(&m_WDT, sizeof(m_WDT), 0, "WDT");
|
|
AddExState(&m_TRISA, sizeof(m_TRISA), 0, "TRSA");
|
|
AddExState(&m_TRISB, sizeof(m_TRISB), 0, "TRSB");
|
|
AddExState(&m_TRISC, sizeof(m_TRISC), 0, "TRSC");
|
|
AddExState(&m_STACK[0], sizeof(m_STACK[0]), 0, "STC0");
|
|
AddExState(&m_STACK[1], sizeof(m_STACK[1]), 0, "STC1");
|
|
AddExState(&m_prescaler, sizeof(m_prescaler), 0, "PSCL");
|
|
AddExState(&m_opcode, sizeof(m_opcode), 0, "OPS0");
|
|
AddExState(m_internalram, sizeof(m_internalram), 0, "IRAM");
|
|
AddExState(&m_icount, sizeof(m_icount), 0, "ICNT");
|
|
AddExState(&m_delay_timer, sizeof(m_delay_timer), 0, "DTIM");
|
|
AddExState(&m_rtcc, sizeof(m_rtcc), 0, "RTCC");
|
|
AddExState(&m_count_pending, sizeof(m_count_pending), 0, "CNTP");
|
|
AddExState(&m_inst_cycles, sizeof(m_inst_cycles), 0, "ICYC");
|
|
AddExState(&m_clock2cycle, sizeof(m_clock2cycle), 0, "CL2C");
|
|
} |