Yet Another ASCII MSX1 Emulator その5

ePX-7を使ってます!でもPX-7持ってません(笑)

http://homepage3.nifty.com/takeda-toshiya/msx/

f:id:tanam:20140524154100p:image:w360

FD1を実装しましたので、CART#1で以下の改造したDISK.ROMを読み込ませます。

http://www.geocities.jp/parallel_computer_inc/android.html

/** Places in DiskROM to be patched with ED FE C9 ************/

uint16 DiskPatches[] = { 0x4010,0x4013,0x4016,0x401C,0x401F,0 };

f:id:tanam:20140524154101p:image:w360

fMSXを参考にZ80未定義命令を使った、かなりトリッキーな実装です。

px7.rc

    POPUP "Laser Disc"
    BEGIN
        MENUITEM "Insert",                      ID_OPEN_LASER_DISC
        MENUITEM "Eject",                       ID_CLOSE_LASER_DISC
        MENUITEM SEPARATOR
        MENUITEM "Recent",                      ID_RECENT_LASER_DISC
    END
    POPUP "FD1"
    BEGIN
        MENUITEM "Insert",                      ID_OPEN_FD1
        MENUITEM "Eject",                       ID_CLOSE_FD1
        MENUITEM SEPARATOR
        MENUITEM "Recent",                      ID_RECENT_FD1
    END

resource.h

// PIONEER PX-7
#ifdef _PX7
#define MENU_POS_CONTROL                0
#define MENU_POS_CART1                  1
#define MENU_POS_CART2                  2
#define MENU_POS_TAPE                   3
#define MENU_POS_LASER_DISC             4
#define MENU_POS_FD1                    5
#define MENU_POS_SCREEN                 6
#define MENU_POS_SOUND                  7
#endif

msx1.cpp


bool VM::laser_disc_inserted()
{
	return ldp->disc_inserted();
}

extern byte ChangeDisk(byte ID,char *Name);
bool status=false;

void VM::open_disk(int drv, _TCHAR* file_path, int offset)
{
	ChangeDisk(drv, file_path);
	status=true;
}

void VM::close_disk(int drv)
{
	ChangeDisk(drv, NULL);
	status=false;
}

bool VM::disk_inserted(int drv)
{
	return status;
}

msx1.h

/*
	ASCII MSX1 Emulator 'yaMSX1'
	Pioneer PX-7 Emulator 'ePX-7'

	Author : tanam
	Date   : 2013.06.29-

	modified by Takeda.Toshiya, umaiboux

	[ virtual machine ]
*/

#ifndef _MSX1_H_
#define _MSX1_H_

#define DEVICE_NAME		"PIONEER PX-7"
#define CONFIG_NAME		"px7"

// device informations for virtual machine
#define FRAMES_PER_SEC		60
#define LINES_PER_FRAME		262
#define CPU_CLOCKS		3579545
//#define SCREEN_WIDTH		256
//#define SCREEN_HEIGHT		192
#define SCREEN_WIDTH		512
#define SCREEN_HEIGHT		384
#define TMS9918A_VRAM_SIZE	0x4000
#define TMS9918A_LIMIT_SPRITES
#define TMS9918A_SUPER_IMPOSE
#define HAS_AY_3_8910
// for Flappy Limited '85
#define YM2203_PORT_MODE	0x80

// device informations for win32
#define USE_CART1
#define USE_CART2
#define USE_TAPE
#define USE_LASER_DISC
#define USE_FD1
#define USE_ALT_F10_KEY
#define USE_AUTO_KEY		6
#define USE_AUTO_KEY_RELEASE	10

#include "../../common.h"

class EMU;
class DEVICE;
class EVENT;

class DATAREC;
class I8255;
class IO;
class LD700;
class NOT;
class YM2203;
class PCM1BIT;
class TMS9918A;
class Z80;

class JOYSTICK;
class KEYBOARD;
class MEMORY;
class SLOT_CART;
class SLOT_MAIN;
class SLOT_SUB;

class VM
{
protected:
	EMU* emu;
	
	// devices
	EVENT* event;
	
	DATAREC* drec;
	I8255* pio;
	IO* io;
	LD700* ldp;
	NOT* not;
	YM2203* psg;
	PCM1BIT* pcm;
	TMS9918A* vdp;
	Z80* cpu;
	
	JOYSTICK* joystick;
	KEYBOARD* keyboard;
	MEMORY* memory;
	SLOT_MAIN *slot0;
	SLOT_CART *slot1;
	SLOT_SUB *slot2;
	SLOT_CART *slot3;
	
public:
	// ----------------------------------------
	// initialize
	// ----------------------------------------
	
	VM(EMU* parent_emu);
	~VM();
	
	// ----------------------------------------
	// for emulation class
	// ----------------------------------------
	
	// drive virtual machine
	void reset();
	void run();
	
	// draw screen
	void draw_screen();
	
	// sound generation
	void initialize_sound(int rate, int samples);
	uint16* create_sound(int* extra_frames);
	int sound_buffer_ptr();
	void movie_sound_callback(uint8 *buffer, long size);
	
	// user interface
	void open_cart(int drv, _TCHAR* file_path);
	void close_cart(int drv);
	bool cart_inserted(int drv);
	void play_tape(_TCHAR* file_path);
	void rec_tape(_TCHAR* file_path);
	void close_tape();
	bool tape_inserted();
	void open_laser_disc(_TCHAR* file_path);
	void close_laser_disc();
	bool laser_disc_inserted();
	void open_disk(int drv, _TCHAR* file_path, int offset);
	void close_disk(int drv);
	bool disk_inserted(int drv);
	bool now_skip();
	
	void update_config();
	
	// ----------------------------------------
	// for each device
	// ----------------------------------------
	
	// devices
	DEVICE* get_device(int id);
	DEVICE* dummy;
	DEVICE* first_device;
	DEVICE* last_device;
};

#endif

z80.cpp

	case 0xbb: OTDR(); break;						/* OTDR             */
	default:
#ifdef _CPU_DEBUG_LOG
		if(debug_count) {
			emu->out_debug(_T("%4x\tDB ED\n"), prevpc);
		}
#endif
#ifdef _PX7
        uint8 DiskPresent(uint8 ID);
        byte DiskRead(byte ID,byte *Buf, int N);
        byte DiskWrite(byte ID,byte *Buf,int N);
        #define C_FLAG      0x01       /* 1: Carry/Borrow occured    */
        static byte BootBlock[] = {
          0xEB,0xFE,0x90,0x56,0x46,0x42,0x2D,0x31,0x39,0x38,0x39,0x00,0x02,0x02,0x01,0x00,
          0x02,0x70,0x00,0xA0,0x05,0xF9,0x03,0x00,0x09,0x00,0x02,0x00,0x00,0x00,0xD0,0xED,
          0x53,0x58,0xC0,0x32,0xC2,0xC0,0x36,0x55,0x23,0x36,0xC0,0x31,0x1F,0xF5,0x11,0x9D,
          0xC0,0x0E,0x0F,0xCD,0x7D,0xF3,0x3C,0x28,0x28,0x11,0x00,0x01,0x0E,0x1A,0xCD,0x7D,
          0xF3,0x21,0x01,0x00,0x22,0xAB,0xC0,0x21,0x00,0x3F,0x11,0x9D,0xC0,0x0E,0x27,0xCD,
          0x7D,0xF3,0xC3,0x00,0x01,0x57,0xC0,0xCD,0x00,0x00,0x79,0xE6,0xFE,0xFE,0x02,0x20,
          0x07,0x3A,0xC2,0xC0,0xA7,0xCA,0x22,0x40,0x11,0x77,0xC0,0x0E,0x09,0xCD,0x7D,0xF3,
          0x0E,0x07,0xCD,0x7D,0xF3,0x18,0xB4,0x42,0x6F,0x6F,0x74,0x20,0x65,0x72,0x72,0x6F,
          0x72,0x0D,0x0A,0x50,0x72,0x65,0x73,0x73,0x20,0x61,0x6E,0x79,0x20,0x6B,0x65,0x79,
          0x20,0x66,0x6F,0x72,0x20,0x72,0x65,0x74,0x72,0x79,0x0D,0x0A,0x24,0x00,0x4D,0x53,
          0x58,0x44,0x4F,0x53,0x20,0x20,0x53,0x59,0x53,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF3,0x2A,
          0x51,0xF3,0x11,0x00,0x01,0x19,0x01,0x00,0x01,0x11,0x00,0xC1,0xED,0xB0,0x3A,0xEE,
          0xC0,0x47,0x11,0xEF,0xC0,0x21,0x00,0x00,0xCD,0x51,0x52,0xF3,0x76,0xC9,0x18,0x64,
          0x3A,0xAF,0x80,0xF9,0xCA,0x6D,0x48,0xD3,0xA5,0x0C,0x8C,0x2F,0x9C,0xCB,0xE9,0x89,
          0xD2,0x00,0x32,0x26,0x40,0x94,0x61,0x19,0x20,0xE6,0x80,0x6D,0x8A,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
          0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
        };

        static const struct
        { int Sectors;byte Heads,Names,PerTrack,PerFAT,PerCluster; }
        Info[8] = {
            {  720,1,112,9,2,2 },
            { 1440,2,112,9,3,2 },
            {  640,1,112,8,1,2 },
            { 1280,2,112,8,2,2 },
            {  360,1, 64,9,2,1 },
            {  720,2,112,9,2,2 },
            {  320,1, 64,8,1,1 },
            {  640,2,112,8,1,2 }
        };
        byte Buf[512],Count,N,*P;
        int Sector, i, j;
        uint16 Addr;
        /** PHYDIO: Read/write sectors to disk **************************
        *** Input:                                                    ***
        *** [F] CARRY=WRITE                  [A] Drive number (0=A:)  ***
        *** [B] Number of sectors to write   [C] Media descriptor     ***
        *** [DE] Logical sector number (starts at 0)                  ***
        *** [HL] Transfer address                                     ***
        *** Output:                                                   ***
        *** [F] CARRY=ERROR                  [A] If error: errorcode  ***
        *** [B] Number of sectors remaining (not read/written)        ***
        *** Error codes in [A] can be:                                ***
        *** 0 Write protected      8  Record not found                ***
        *** 2 Not ready            10 Write fault                     ***
        *** 4 Data (CRC) error     12 Other errors                    ***
        *** 6 Seek error                                              ***
        ****************************************************************/
        if (prevpc==0x4010) {
            iff1|=1; /// R->IFF|=1;
            Addr  = HL; /// R->HL.W;
            Count = B; /// R->BC.B.h;
            if (!DiskPresent(A)) {
                AF=0x0201; ///R->AF.W=0x0201;
                return;
            }  // No disk      -> "Not ready"
            if (DE+Count>Info[C-0xF8].Sectors) {
                AF=0x0801;
                return;
            }  // Wrong sector -> "Record not found"
            // If data does not fit into 64kB address space, trim it
            if (HL+Count*512>0x10000) Count=(0x10000-HL)/512;
            // Save slot states
            // Turn on RAM in all slots
            if (F&C_FLAG)
                for (Sector=DE;Count--;Sector++) { // WRITE
                    for (j=0;j<512;j++) Buf[j]=RM8(Addr++);
                    if (DiskWrite(A,Buf,Sector)) B--;
                    else {
                        AF=0x0A01;
                        return;
                    }
                }
            else for(Sector=DE;Count--;Sector++) { // READ
                if (DiskRead(A,Buf,Sector)) {
                    B--;
                } else {
                    AF=0x0401;
                    return;
                }
                for(j=0;j<512;j++) WM8(Addr++,Buf[j]);
            }
            F&=~C_FLAG;
            return;
        /** DSKCHG: Check if the disk was changed ***********************
        *** Input:                                                    ***
        *** [A] Drive number (0=A:)       [B]  Media descriptor       ***
        *** [C] Media descriptor          [HL] Base address of DPB    ***
        *** Output:                                                   ***
        *** [F] CARRY=ERROR       [A] If error: errorcode (see DSKIO) ***
        *** [B] If success: 1=Unchanged, 0=Unknown, -1=Changed        ***
        *** Note:                                                     ***
        *** If the disk has been changed or may have been  changed    ***
        *** (unknown) read the boot sector or the FAT sector for disk ***
        *** media descriptor and transfer a new DPB as with GETDPB.   ***
        ****************************************************************/
        } else if (prevpc==0x4013) {
            iff1|=1;
            // If no disk, return "Not ready":
            if(!DiskPresent(A)) {
                AF=0x0201;
                return;
            }
            // This requires some major work to be done:
            B=0;
            F &= ~C_FLAG;
            // We continue with GETDPB now...
        /** GETDPB: Disk format *****************************************
        *** Input:                                                    ***
        *** [A] Drive number   [B] 1st byte of FAT (media descriptor) ***
        *** [C] Media descriptor  [HL] Base address of DPB            ***
        *** Output:                                                   ***
        *** [HL+1] .. [HL+18] = DPB for specified drive               ***
        *** DPB consists of:                                          ***
        *** Name   Offset Size Description                            ***
        *** MEDIA    0     1   Media type (F8..FF)                    ***
        *** SECSIZ   1     2   Sector size (must be 2^n)              ***
        *** DIRMSK   3     1   (SECSIZE/32)-1                         ***
        *** DIRSHFT  4     1   Number of one bits in DIRMSK           ***
        *** CLUSMSK  5     1   (Sectors per cluster)-1                ***
        *** CLUSSHFT 6     1   (Number of one bits in CLUSMSK)+1      ***
        *** FIRFAT   7     2   Logical sector number of first FAT     ***
        *** FATCNT   8     1   Number of FATs                         ***
        *** MAXENT   A     1   Number of directory entries (max 254)  ***
        *** FIRREC   B     2   Logical sector number of first data    ***
        *** MAXCLUS  D     2   Number of clusters (not including      ***
        ***                    reserved, FAT and directory sectors)+1 ***
        *** FATSIZ   F     1   Number of sectors used                 ***
        *** FIRDIR   10    2   FAT logical sector number of start of  ***
        ***                    directory                              ***
        ****************************************************************/
        } else if (prevpc==0x4016) {
            int BytesPerSector,SectorsPerDisk,SectorsPerFAT,ReservedSectors;

            // If no disk, return "Not ready":
            if (!DiskPresent(A)) {
                AF=0x0201;
                return;
            }
            // If can't read, return "Other error":
            if (!DiskRead(A, Buf , 0)) {
                AF=0x0C01;
                return;
            }
            BytesPerSector  = (int)Buf[0x0C]*256+Buf[0x0B];
            SectorsPerDisk  = (int)Buf[0x14]*256+Buf[0x13];
            SectorsPerFAT   = (int)Buf[0x17]*256+Buf[0x16];
            ReservedSectors = (int)Buf[0x0F]*256+Buf[0x0E];

            Addr=HL+1;
            WM8(Addr++,Buf[0x15]);             // Format ID [F8h-FFh]
            WM8(Addr++,Buf[0x0B]);             // Sector size
            WM8(Addr++,Buf[0x0C]);
            j=(BytesPerSector>>5)-1;
            for(i=0;j&(1<<i);i++);
            WM8(Addr++,j);                     // Directory mask/shft
            WM8(Addr++,i);
            j=Buf[0x0D]-1;
            for(i=0;j&(1<<i);i++);
            WM8(Addr++,j);                     // Cluster mask/shift
            WM8(Addr++,i+1);
            WM8(Addr++,Buf[0x0E]);             // Sector # of 1st FAT
            WM8(Addr++,Buf[0x0F]);
            WM8(Addr++,Buf[0x10]);             // Number of FATs
            WM8(Addr++,Buf[0x11]);             // Number of dirent-s
            j=ReservedSectors+Buf[0x10]*SectorsPerFAT;
            j+=32*Buf[0x11]/BytesPerSector;
            WM8(Addr++,j&0xFF);                // Sector # of data
            WM8(Addr++,(j>>8)&0xFF);
            j=(SectorsPerDisk-j)/Buf[0x0D];
            WM8(Addr++,j&0xFF);                // Number of clusters
            WM8(Addr++,(j>>8)&0xFF);
            WM8(Addr++,Buf[0x16]);             // Sectors per FAT
            j=ReservedSectors+Buf[0x10]*SectorsPerFAT;
            WM8(Addr++,j&0xFF);                // Sector # of dir.
            WM8(Addr,(j>>8)&0xFF);
            // Return success
            F&=~C_FLAG;
            return;
        /** DSKFMT: Disk format *****************************************
        *** Input:                                                    ***
        *** [A]  Specified choice (1-9)      [D]  Drive number (0=A:) ***
        *** [HL] Begin address of work area  [BC] Length of work area ***
        *** Output:                                                   ***
        *** [F] CARRY=ERROR                                           ***
        *** Notes:                                                    ***
        *** 1) Also writes a MSX boot sector at sector 0, clears all  ***
        ***    FATs (media descriptor at first byte, 0FFh at second/  ***
        ***    third byte and rest zero) and clears the directory     ***
        ***    filling it with zeros.                                 ***
        *** 2) Error codes are:                                       ***
        ***    0 Write protected       10 Write fault                 ***
        ***    2 Not ready             12 Bad parameter               ***
        ***    4 Data (CRC) error      14 Insufficient memory         ***
        ***    6 Seek error            16 Other errors                ***
        ***    8 Record not found                                     ***
        ****************************************************************/
        } else if (prevpc==0x401c) {
            iff1|=1;
            // If invalid choice, return "Bad parameter":
            if (!A || A>2) {
                AF=0x0C01;
                return;
            }
            // If no disk, return "Not ready":
            if (!DiskPresent(D)) {
                AF=0x0201;
                return;
            }

            // Fill bootblock with data:
            P=BootBlock+3;
            N=2-A;
            memcpy(P,"fMSXdisk",8);P+=10;    // Manufacturer's ID
            *P=Info[N].PerCluster;P+=4;      // Sectors per cluster
            *P++=Info[N].Names;*P++=0x00;    // Number of names
            *P++=Info[N].Sectors       // Number of sectors
            *P++=(Info[N].Sectors>>8)
            *P++=N+0xF8;                     // Format ID [F8h-FFh]
            *P++=Info[N].PerFAT;*P++=0x00;   // Sectors per FAT
            *P++=Info[N].PerTrack;*P++=0x00; // Sectors per track
            *P++=Info[N].Heads;*P=0x00;      // Number of heads

            // If can't write bootblock, return "Write protected":
            if (!DiskWrite(D, BootBlock, 0)) {
                AF=0x0001;
                return;
            }

            // Writing FATs:
            for (Sector=1,j=0;j<2;j++) {
                Buf[0]=N+0xF8;
                Buf[1]=Buf[2]=0xFF;
                memset(Buf+3,0x00,509);

                if (!DiskWrite(D, Buf, Sector++)) {
                    AF=0x0A01;
                    return;
                }

                memset(Buf,0x00,512);

                for (i=Info[N].PerFAT;i>1;i--)
                    if (!DiskWrite(D, Buf, Sector++)) {
                        AF=0x0A01;
                        return;
                    }
            }

            j=Info[N].Names/16;                     // Directory size
            i=Info[N].Sectors-2*Info[N].PerFAT-j-1; // Data size

            for (memset(Buf,0x00,512);j;j--)
                if (!DiskWrite(D, Buf, Sector++)) {
                    AF=0x0A01;
                    return;
                }
            for (memset(Buf,0xFF,512);I;I--)
                if (!DiskWrite(D, Buf, Sector++)) {
                    AF=0x0A01;
                    return;
                }

            // Return success
            F&=~C_FLAG;
            return;
        /*** DRVOFF: Stop drives *****************************************
        *** Input:  None                                              ***
        *** Output: None                                              ***
        ****************************************************************/
        } else if (prevpc==0x401f) {
            return;
        }
        break;
    }
}

#define MAXDRIVES 2
#define O_BINARY 0
static FILE *Drives[2]={NULL,NULL};        /* Disk image files */
static int RdOnly[2];                    /* 1 = read-only    */

/** ChangeDisk() *********************************************/
/** Change disk image in a given drive. Closes current disk **/
/** image if Name=0 was given. Returns 1 on success or 0 on **/
/** failure.                                                **/
/*************************************************************/
byte ChangeDisk(byte ID,char *Name)
{
    /* We only have MAXDRIVES drives */
    if (ID >= MAXDRIVES) return(0);
    /* Close previous disk image */
    if (Drives[ID] != NULL) {
        fclose(Drives[ID]);
        Drives[ID] = NULL;
    }
    if (Name == NULL) return (0);
    /* If no disk image given, consider drive empty */
    if (!Name) return(1);
    /* Open new disk image */
    Drives[ID]=fopen(Name,"r+b");
    RdOnly[ID]=0;
    /* If failed to open for writing, open read-only */
    if (Drives[ID] == NULL) {
        Drives[ID]=fopen(Name,"rb");
        RdOnly[ID]=1;
    }
    /* Return operation result */
    return (Drives[ID] != NULL);
}

/** DiskPresent() ********************************************/
/** Return 1 if disk drive with a given ID is present.      **/
/*************************************************************/
uint8 DiskPresent(uint8 ID)
{
    return((ID<MAXDRIVES)&&(Drives[ID] != NULL));
}

/** DiskRead() ***********************************************/
/** Read requested sector from the drive into a buffer.     **/
/*************************************************************/
byte DiskRead(byte ID,byte *Buf, int N)
{
    if ((ID<MAXDRIVES) && (Drives[ID] != NULL))
        if (0 == fseek(Drives[ID], N*512L, SEEK_SET)) {
            int tmp=fread(Buf, 1, 512, Drives[ID]);
            return (tmp==512);
        }
    return (0);
}

/** DiskWrite() **********************************************/
/** Write contents of the buffer into a given sector of the **/
/** disk.                                                   **/
/*************************************************************/
byte DiskWrite(byte ID,byte *Buf,int N)
{
    if ((ID<MAXDRIVES) && (Drives[ID] != NULL) && !RdOnly[ID])
        if (0 == fseek(Drives[ID], N*512L, SEEK_SET))
            return(fwrite(Buf, 1, 512, Drives[ID]) == 512);
    return (0);
}
#else
		OP(code);
		break;
	}
}
#endif

void Z80::OP(uint8 code)
{
	prevpc = PC - 1;
	icount -= cc_op[code];