/*
    Caprice32 - Amstrad CPC Emulator
    (c) Copyright 1997-2005 Ulrich Doewich

    CaPriCe for Palm OS - Amstrad CPC 464/664/6128 emulator for Palm devices
    Copyright (C) 2006-2011 by Frédéric Coste
    CaPriCe Forever - Amstrad CPC 464/664/6128 emulator
    Copyright (C) 2014-2018 by Frédéric Coste

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#if defined(__PALMOS__)
#include <ByteOrderUtils.h>
#endif

#include "..\Native_CPC.h"
#include "..\Keys.h"
#include "..\..\Common\CRC_CCITT.h"
#include "..\..\Common\FDC.h"
#include "..\..\Common\Math.h"
#include "z80_opcodes.h"
#ifdef _TESTU
#include "..\..\Common\STD\strings.h"
#endif /* _TESTU */

#if defined(__PALMOS__)
//
// PALMOS CAUTION:
// CPC Engine patches must be confirmed on TE !!
// TX allows code optimization TE doesn't accept !!
//
#endif /* __PALMOS__ */


#undef USE_KEYBOARD_CLASH
#ifndef CAPRICE_X64_ENGINE
#define USE_KEYBOARD_CLASH
#endif /* CAPRICE_X64_ENGINE */

// According to some documentation but confirmed NOK on real CPC6128
// To be defined for Kevin Thacker Acid tests
// To be undefined for ADI CM1
#undef FDC_INVERT_ST3_TS
//#define FDC_INVERT_ST3_TS

#undef FDC_REVOLUTION_VARIABLE
#define FDC_REVOLUTION_VARIABLE

#undef USE_PAL_MEMORY
#define USE_PAL_MEMORY


//===================
// PATCH begin
#ifdef _PATCH_ENABLE

#undef DEBUG_VDU_CONSTANTS
#define DEBUG_VDU_CONSTANTS

#undef CRTC_OPTIMIZE
#define CRTC_OPTIMIZE
#undef CRTC_PATCH_1
#undef CRTC_PATCH_2
//#define CRTC_PATCH_1 // CRTC1 tests = ?
//#define CRTC_PATCH_2 // Better for Waves but horizontal offset

#undef TAPE_PATCH_1
//#define TAPE_PATCH_1 // Bride of Frankenstein OK

#undef PALETTE_PATCH_1
#undef PALETTE_PATCH_2
#define PALETTE_PATCH_1 // GateArray Color select Delay after OUT
//#define PALETTE_PATCH_2 // Need PALETTE_PATCH_1. Better for Rhino

#undef XMEM_PATCH_1
#define XMEM_PATCH_1

#endif /* _PATCH_ENABLE */
// PATCH end
//===================


/*
** Make sure we can call this stuff from C++.
*/
#ifdef __cplusplus
extern "C" {
#endif


#ifdef __PALMOS__
tULong PNOMain(const tVoid*,
               tVoid*,
               Call68KFuncType*);
#endif /* __PALMOS__ */

#if defined(__GNUC__)
// This seemingly bogus function satisfies the Windows LoadLibrary() call,
// (and must be the first function in our PNO .dll).
int /*WINAPI*/ my_bogus_cygwin_dll_entry(unsigned long /*HANDLE*/ h,
                                         unsigned long /*DWORD*/ reason,
                                         void* p)
{
  return 1;
}
#endif /* __GNUC__ */


extern void CreateDeclaredSizeSector(tTrack* trackP, tULong CurrentSector);


// Handling specific registers of PSG AY_3_8910
//#define PSG_AY_3_8910


//
// Z80
//
static tVoid z80_pfx_cb(tNativeCPC* NativeCPC);
static tVoid z80_pfx_dd(tNativeCPC* NativeCPC);
static tVoid z80_pfx_ddcb(tNativeCPC* NativeCPC);
static tVoid z80_pfx_ed(tNativeCPC* NativeCPC);
static tVoid z80_pfx_fd(tNativeCPC* NativeCPC);
static tVoid z80_pfx_fdcb(tNativeCPC* NativeCPC);

//
// CPC
//
tULong z80_IN_handler(tNativeCPC* NativeCPC,
                      tRegister port);
tVoid  z80_OUT_handler(tNativeCPC* NativeCPC,
                       tRegister port,
                       tULong val);

//
// Gate Array
//
static tVoid ga_init(tNativeCPC* NativeCPC);
static tVoid ga_memory_manager(tNativeCPC* NativeCPC);
static tVoid ga_init_banking(tNativeCPC* NativeCPC);

//
// PPI
//
static tUChar ppi_get_tape_level(tNativeCPC* NativeCPC);
static tVoid  ppi_write(tNativeCPC* NativeCPC, tRegister port, tULong val);
static tULong ppi_read(tNativeCPC* NativeCPC, tRegister port);

//
// IDE
//
static tUChar ide_read(tNativeCPC* NativeCPC, tUChar reg_index);
static tVoid  ide_write(tNativeCPC* NativeCPC, tUChar reg_index, tUChar val);
static tVoid  ide_execute_command(tIDE* IDE, tUChar command);
static tULong ide_get_sector_from_registers(tIDE* IDE);

//
// Breakpoints
//
static tBool IsBreakpointDetected(tNativeCPC* NativeCPC);

//
// Video
//
static tVoid video_init(tNativeCPC* NativeCPC);
static tVoid video_no_display(tNativeCPC* NativeCPC, tULong repeat_count);
#ifndef CRTC_ENGINE_V2
static tULong video_get_CRTC_Register(tNativeCPC* NativeCPC,
                                      tULong Index);
static tVoid video_set_CRTC_Register(tNativeCPC* NativeCPC,
                                     tULong Index,
                                     tULong val);
static tVoid video_access_memory(tNativeCPC* NativeCPC,
                                 tULong repeat_count);
static tVoid video_draw_mode0(tVDU* VDU,
                              tULong addr);
static tVoid video_draw_mode1(tVDU* VDU,
                              tULong addr);
static tVoid video_draw_mode2(tVDU* VDU,
                              tULong addr);
static tVoid video_draw_mode2_antialiased(tVDU* VDU,
                                          tULong addr);
#else /* !CRTC_ENGINE_V2 */
// CRTC 0
static tULong CRTC0_get_register(tNativeCPC* NativeCPC,
                                      tULong Index);
static tVoid CRTC0_set_register(tNativeCPC* NativeCPC,
                                tULong Index,
                                tULong val);
static tULong CRTC0_get_status(tNativeCPC* NativeCPC);
static tVoid CRTC0_video_engine(tNativeCPC* NativeCPC,
                                 tULong repeat_count);
static tVoid video_update_skew(tCRTC* CRTC);
static tVoid video_match_hsw(tNativeCPC * NativeCPC);
static tVoid video_change_mode(tNativeCPC* NativeCPC);
static tVoid video_restart_frame(tNativeCPC* NativeCPC);
static tVoid video_end_vdu_hsync(tCRTC* CRTC);
static tVoid video_match_line_count(tNativeCPC* NativeCPC);
static tVoid video_reload_addr(tNativeCPC* NativeCPC);
static tVoid video_frame_finished(tVDU* VDU);
static tVoid video_NoChar(tCRTC* CRTC);
static tVoid video_CharSL1(tCRTC* CRTC);
static tVoid video_CharSL2(tCRTC* CRTC);
static tVoid video_CharMR1(tCRTC* CRTC);
static tVoid video_CharMR2(tCRTC* CRTC);
static tVoid video_set_prerender(tCRTC* CRTC);
static tVoid video_prerender_normal(tNativeCPC* NativeCPC);
static tVoid video_prerender_border(tNativeCPC* NativeCPC);
static tVoid video_prerender_sync(tNativeCPC* NativeCPC);
static tVoid video_render8bpp(tNativeCPC* NativeCPC);
static tVoid video_render24bpp(tNativeCPC* NativeCPC);
static tVoid video_render32bpp(tNativeCPC* NativeCPC);
// CRTC 1
static tULong CRTC1_get_register(tNativeCPC* NativeCPC,
                                      tULong Index);
static tVoid CRTC1_set_register(tNativeCPC* NativeCPC,
                                tULong Index,
                                tULong val);
static tULong CRTC1_get_status(tNativeCPC* NativeCPC);
static tVoid CRTC1_video_engine(tNativeCPC* NativeCPC,
                                tULong repeat_count);
static tVoid CRTC1_CharMR1(tCRTC* CRTC);
static tVoid CRTC1_CharMR2(tCRTC* CRTC);
#endif /* !CRTC_ENGINE_V2 */

//
// Audio
//
static tVoid audio_init(tNativeCPC* NativeCPC);
static INLINE tVoid audio_set_AY_Register(tPSG* PSG,
                                          tULong Num,
                                          tULong Value);
static INLINE tVoid audio_set_case_env(tPSG* PSG);
static tVoid audio_Case_EnvType_0_3__9(tPSG* PSG);
static tVoid audio_Case_EnvType_4_7__15(tPSG* PSG);
static tVoid audio_Case_EnvType_8(tPSG* PSG);
static tVoid audio_Case_EnvType_10(tPSG* PSG);
static tVoid audio_Case_EnvType_11(tPSG* PSG);
static tVoid audio_Case_EnvType_12(tPSG* PSG);
static tVoid audio_Case_EnvType_13(tPSG* PSG);
static tVoid audio_Case_EnvType_14(tPSG* PSG);
static tVoid audio_Synthesizer_Logic_Q(tPSG* PSG);
static tVoid audio_Synthesizer_Mixer_Q(tPSG* PSG, tLong StartLevel);
static tVoid audio_Synthesizer_Stereo16(tNativeCPC* NativeCPC);
static tVoid audio_Synthesizer_Stereo8(tNativeCPC* NativeCPC);
static tVoid audio_Synthesizer_Mixer_Q_Mono(tPSG* PSG, tLong StartLevel);
static tVoid audio_Synthesizer_Mono16(tNativeCPC* NativeCPC);
static tVoid audio_Synthesizer_Mono8(tNativeCPC* NativeCPC);


//
// PlayCity
//
extern tVoid PlayCity_reset(tPlayCity* PlayCity);
static tVoid PlayCity_audio_init(tPlayCity* PlayCity);
static tVoid PlayCity_receive_channel_0(tPlayCity* PlayCity, tUChar Value);
static tVoid PlayCity_receive_channel_1_2_3(tPlayCity* PlayCity, tUChar Channel, tUChar Value);
static tVoid PlayCity_trigger_channel_1(tNativeCPC* NativeCPC);
static tVoid PlayCity_trigger_channel_3(tNativeCPC* NativeCPC);
static tLong PlayCity_audio_Mixer_Q(tPSG* PSG);
static tVoid PlayCity_audio_Stereo16(tNativeCPC* NativeCPC);
static tVoid PlayCity_audio_Stereo16_Invert(tNativeCPC* NativeCPC);
static tVoid PlayCity_audio_Mono16(tNativeCPC* NativeCPC);


//
// FDC
//
static tVoid fdc_init(tNativeCPC* NativeCPC);

// Fast FDC
static tBool fdc_cmd_motor_fast(tFDC* FDC, tULong val);
static tVoid fdc_z80_wait_state_fast(tFDC* FDC, tULong CycleCount);
static tULong fdc_read_status_fast(tFDC* FDC);
static tULong fdc_read_data_fast(tFDC* FDC);
static tVoid fdc_write_data_fast(tFDC* FDC, tULong val);
static tVoid fdc_specify_fast(tFDC* FDC);
static tVoid fdc_drvstat_fast(tFDC* FDC);
static tVoid fdc_recalib_fast(tFDC* FDC);
static tVoid fdc_intstat_fast(tFDC* FDC);
static tVoid fdc_seek_fast(tFDC* FDC);
static tVoid fdc_readtrk_fast(tFDC* FDC);
static tVoid fdc_write_fast(tFDC* FDC);
static tVoid fdc_read_fast(tFDC* FDC);
static tVoid fdc_readID_fast(tFDC* FDC);
static tVoid fdc_format_fast(tFDC* FDC);
static tVoid fdc_scan_fast(tFDC* FDC);
static tVoid fdc_cmd_readtrk_fast(tFDC* FDC);
static tVoid fdc_cmd_read_fast(tFDC* FDC);
static tSector* fdc_find_sector_fast(tFDC* FDC, tULong* requested_CHRN);
static tVoid fdc_cmd_scan_fast(tFDC* FDC);
static tVoid fdc_cmd_write_fast(tFDC* FDC);
static tULong fdc_init_status_regs_fast(tFDC* FDC);

// Real FDC
static tBool fdc_cmd_motor(tFDC* FDC, tULong val);
static tVoid fdc_z80_wait_state(tFDC* FDC, tULong CycleCount);
static tULong fdc_read_status(tFDC* FDC);
static tULong fdc_read_data(tFDC* FDC);
static tVoid fdc_write_data(tFDC* FDC, tULong val);
static tVoid fdc_specify(tFDC* FDC);
static tVoid fdc_drvstat(tFDC* FDC);
static tULong fdc_get_status(tFDC* FDC);
static tVoid fdc_recalib(tFDC* FDC);
static tVoid fdc_intstat(tFDC* FDC);
static tVoid fdc_seek(tFDC* FDC);
static tVoid fdc_readtrk(tFDC* FDC);
static tVoid fdc_write(tFDC* FDC);
static tVoid fdc_read(tFDC* FDC);
static tVoid fdc_readID(tFDC* FDC);
static tVoid fdc_format(tFDC* FDC);
static tVoid fdc_format_alt(tFDC* FDC);
static tVoid fdc_scan(tFDC* FDC);
static tVoid fdc_track_first_sector(tFDC* FDC);
static tVoid fdc_track_sector_data(tFDC* FDC);
static tVoid fdc_track_next_sector(tFDC* FDC);
static tVoid fdc_head_step(tFDC* FDC);
static tVoid fdc_sector_begin_data_transfer(tFDC* FDC);
static tVoid fdc_sector_data_read(tFDC* FDC);
static tVoid fdc_sector_data_write(tFDC* FDC);
static tVoid fdc_track_begin_read(tFDC* FDC);
static tVoid fdc_track_read_data(tFDC* FDC);
static tVoid fdc_track_read_data_crc(tFDC* FDC);
static tVoid fdc_track_read_gap3(tFDC* FDC);
static tVoid fdc_track_read_id_sync(tFDC* FDC);
static tVoid fdc_track_read_idam(tFDC* FDC);
static tVoid fdc_track_read_chrn(tFDC* FDC);
static tVoid fdc_track_read_gap2(tFDC* FDC);
static tVoid fdc_track_read_data_sync(tFDC* FDC);
static tVoid fdc_track_read_dataam(tFDC* FDC);
static tVoid fdc_track_read_gap4b(tFDC* FDC);
static tVoid fdc_track_read_index(tFDC* FDC);
static tVoid fdc_track_read_gap4a(tFDC* FDC);
static tVoid fdc_track_read_index_sync(tFDC* FDC);
static tVoid fdc_track_read_iam(tFDC* FDC);
static tVoid fdc_track_read_gap1(tFDC* FDC);
static tVoid fdc_readid_index(tFDC* FDC);
static tVoid fdc_readid_sector_chrn(tFDC* FDC);
static tVoid fdc_readid_sector_found(tFDC* FDC);
static tVoid fdc_readid_result(tFDC* FDC);
static tVoid fdc_readid_abort(tFDC* FDC);
static tVoid fdc_read_index(tFDC* FDC);
static tVoid fdc_read_sector(tFDC* FDC);
static tVoid fdc_read_end_sector(tFDC* FDC);
static tVoid fdc_read_next_sector(tFDC* FDC);
static tVoid fdc_read_result(tFDC* FDC);
static tVoid fdc_read_abort(tFDC* FDC);
static tVoid fdc_write_index(tFDC* FDC);
static tVoid fdc_write_sector(tFDC* FDC);
static tVoid fdc_write_end_sector(tFDC* FDC);
static tVoid fdc_write_result(tFDC* FDC);
static tVoid fdc_write_abort(tFDC* FDC);
static tVoid fdc_readtrack_index(tFDC* FDC);
static tVoid fdc_readtrack_begin(tFDC* FDC);
static tVoid fdc_readtrack_end(tFDC* FDC);
static tVoid fdc_readtrack_result(tFDC* FDC);
static tVoid fdc_readtrack_abort(tFDC* FDC);
static tVoid fdc_format_index(tFDC* FDC);
static tVoid fdc_format_index_alt(tFDC* FDC);
static tVoid fdc_format_sector_chrn(tFDC* FDC);
static tVoid fdc_format_sector_chrn_alt(tFDC* FDC);
static tVoid fdc_format_receive_sector_C(tFDC* FDC);
static tVoid fdc_format_receive_sector_H(tFDC* FDC);
static tVoid fdc_format_receive_sector_R(tFDC* FDC);
static tVoid fdc_format_receive_sector_N(tFDC* FDC);
static tVoid fdc_format_receive_sector_N_alt(tFDC* FDC);
static tVoid fdc_format_prepare_write_sector(tFDC* FDC);
static tVoid fdc_format_write_sector(tFDC* FDC);
static tVoid fdc_format_terminate(tFDC* FDC);
static tVoid fdc_format_result(tFDC* FDC);
static tVoid fdc_format_abort(tFDC* FDC);
static tVoid fdc_format_abort_alt(tFDC* FDC);
static tVoid fdc_scan_index(tFDC* FDC);
static tVoid fdc_scan_sector(tFDC* FDC);
static tVoid fdc_scan_sector_begin(tFDC* FDC);
static tVoid fdc_sector_data_scan_equal(tFDC* FDC);
static tVoid fdc_sector_data_scan_low_equal(tFDC* FDC);
static tVoid fdc_sector_data_scan_high_equal(tFDC* FDC);
static tVoid fdc_scan_end_sector(tFDC* FDC);
static tVoid fdc_scan_result(tFDC* FDC);
static tVoid fdc_scan_abort(tFDC* FDC);


//
// Tape
//
#ifdef ENABLE_TAPE

tVoid Tape_GetCycleCount(tTape* Tape);
tLong Tape_ReadDataBit(tTape* Tape);
tLong Tape_ReadSampleDataBit(tTape* Tape);
static tVoid Tape_BlockDone(tTape* Tape);
static tVoid Tape_UpdateLevel(tTape* Tape);
static tVoid Tape_UpdateCSWLevel(tTape* Tape);

#endif /* ENABLE_TAPE */


//
// TMPI Speech synthesis
//
#ifdef ENABLE_TMPI_SPEECH

static tULong tmpi_read_status(tTMPISpeech* TMPISpeech);
static tVoid  tmpi_write_data(tTMPISpeech* TMPISpeech, tULong Val);
static tVoid  tmpi_write_command(tTMPISpeech* TMPISpeech, tULong Val);
static tLong  tmpi_interp_i(tTMPISpeech* TMPISpeech, tLong org, tLong dst);
#ifndef TMPI_SPEECH_FLOAT_MODE
  static tLong tmpi_filter_step(tTMPISpeech* TMPISpeech, tULong i, tLong input);
  static tLong tmpi_noise_gen(tTMPISpeech* TMPISpeech);
  static tLong tmpi_freq_gen(tTMPISpeech* TMPISpeech);
  static tLong tmpi_compute_sample(tTMPISpeech* TMPISpeech);
#else /* !TMPI_SPEECH_FLOAT_MODE */
  static tDouble tmpi_interp_f(tTMPISpeech* TMPISpeech, tDouble org, tDouble dst);
  static tDouble tmpi_filter_step(tTMPISpeech* TMPISpeech, tULong i, tDouble input);
  static tDouble tmpi_noise_gen(tTMPISpeech* TMPISpeech);
  static tDouble tmpi_freq_gen(tTMPISpeech* TMPISpeech);
  static tLong   tmpi_compute_sample(tTMPISpeech* TMPISpeech);
#endif /* !TMPI_SPEECH_FLOAT_MODE */
static tVoid tmpi_shift_frame(tTMPISpeech* TMPISpeech);
static tVoid tmpi_decode_frame(tTMPISpeech* TMPISpeech);
static tVoid tmpi_start_frame(tTMPISpeech* TMPISpeech);
static tVoid tmpi_stop_frame(tTMPISpeech* TMPISpeech);
static tVoid tmpi_sampling(tTMPISpeech* TMPISpeech);
static tVoid tmpi_output(tTMPISpeech* TMPISpeech);

#endif /* ENABLE_TMPI_SPEECH */


//
// Printer
//
#ifdef ENABLE_PRINTER

static tULong printer_getbusy(tPrinter* PrinterP);
static tVoid  printer_receivedata(tPrinter* PrinterP, tUChar data);

#endif /* ENABLE_PRINTER */


//
// Digiblaster
//
#ifdef ENABLE_DIGIBLASTER

static tVoid digiblaster_write_data(tDigiblaster* Digiblaster, tULong Val);
static tVoid digiblaster_output(tDigiblaster* Digiblaster);

#endif /* ENABLE_DIGIBLASTER */


//
// X-MEM
//
#ifdef USE_XMEM

// Winbond W29C040
static tVoid XMEM_Winbond_Detect_Preamble(tNativeCPC* NativeCPC);
static tVoid XMEM_Winbond_Detect_Preamble_End(tNativeCPC* NativeCPC);
static tVoid XMEM_Winbond_Detect_Command(tNativeCPC* NativeCPC);
static tVoid XMEM_Winbond_Write_Data(tNativeCPC* NativeCPC);
// SST39SF040
static tVoid XMEM_SST_Detect_Preamble(tNativeCPC* NativeCPC);
static tVoid XMEM_SST_Detect_Preamble_End(tNativeCPC* NativeCPC);
static tVoid XMEM_SST_Detect_Command(tNativeCPC* NativeCPC);
static tVoid XMEM_SST_Detect_Erase_Preamble(tNativeCPC* NativeCPC);
static tVoid XMEM_SST_Detect_Erase_Preamble_End(tNativeCPC* NativeCPC);
static tVoid XMEM_SST_Detect_Erase_Command(tNativeCPC* NativeCPC);
static tVoid XMEM_SST_Byte_Program(tNativeCPC* NativeCPC);

#endif /* USE_XMEM */


// Routines
static INLINE tVoid MemMoveByte(tUChar* destP,
                                const tUChar* sourceP,
                                tULong numBytes);
static INLINE tVoid MemSetByte(tUChar* destP,
                               tULong numBytes,
                               tULong value);
static INLINE tVoid MemSetLong(tULong* destP,
                               tULong numLongs,
                               tULong value);

//
// TestU
//
#ifdef _TESTU
static tULong PerformTestU(tNativeCPC* NativeCPC);
#endif /* _TESTU */

// Safety
#ifdef _SAFETY
static tBool CheckRAMMarkers(tNativeCPC* NativeCPC);
#endif /* _SAFETY */


#ifdef CRTC_ENGINE_V2

// Version 2 translation tables - static
static const tULong M0Map[0x200] =
{
   0x00000000,0x00000000,0x00000000,0x08080808,0x08080808,0x00000000,0x08080808,0x08080808,
   0x00000000,0x02020202,0x00000000,0x0A0A0A0A,0x08080808,0x02020202,0x08080808,0x0A0A0A0A,
   0x02020202,0x00000000,0x02020202,0x08080808,0x0A0A0A0A,0x00000000,0x0A0A0A0A,0x08080808,
   0x02020202,0x02020202,0x02020202,0x0A0A0A0A,0x0A0A0A0A,0x02020202,0x0A0A0A0A,0x0A0A0A0A,
   0x00000000,0x04040404,0x00000000,0x0C0C0C0C,0x08080808,0x04040404,0x08080808,0x0C0C0C0C,
   0x00000000,0x06060606,0x00000000,0x0E0E0E0E,0x08080808,0x06060606,0x08080808,0x0E0E0E0E,
   0x02020202,0x04040404,0x02020202,0x0C0C0C0C,0x0A0A0A0A,0x04040404,0x0A0A0A0A,0x0C0C0C0C,
   0x02020202,0x06060606,0x02020202,0x0E0E0E0E,0x0A0A0A0A,0x06060606,0x0A0A0A0A,0x0E0E0E0E,
   0x04040404,0x00000000,0x04040404,0x08080808,0x0C0C0C0C,0x00000000,0x0C0C0C0C,0x08080808,
   0x04040404,0x02020202,0x04040404,0x0A0A0A0A,0x0C0C0C0C,0x02020202,0x0C0C0C0C,0x0A0A0A0A,
   0x06060606,0x00000000,0x06060606,0x08080808,0x0E0E0E0E,0x00000000,0x0E0E0E0E,0x08080808,
   0x06060606,0x02020202,0x06060606,0x0A0A0A0A,0x0E0E0E0E,0x02020202,0x0E0E0E0E,0x0A0A0A0A,
   0x04040404,0x04040404,0x04040404,0x0C0C0C0C,0x0C0C0C0C,0x04040404,0x0C0C0C0C,0x0C0C0C0C,
   0x04040404,0x06060606,0x04040404,0x0E0E0E0E,0x0C0C0C0C,0x06060606,0x0C0C0C0C,0x0E0E0E0E,
   0x06060606,0x04040404,0x06060606,0x0C0C0C0C,0x0E0E0E0E,0x04040404,0x0E0E0E0E,0x0C0C0C0C,
   0x06060606,0x06060606,0x06060606,0x0E0E0E0E,0x0E0E0E0E,0x06060606,0x0E0E0E0E,0x0E0E0E0E,
   0x00000000,0x01010101,0x00000000,0x09090909,0x08080808,0x01010101,0x08080808,0x09090909,
   0x00000000,0x03030303,0x00000000,0x0B0B0B0B,0x08080808,0x03030303,0x08080808,0x0B0B0B0B,
   0x02020202,0x01010101,0x02020202,0x09090909,0x0A0A0A0A,0x01010101,0x0A0A0A0A,0x09090909,
   0x02020202,0x03030303,0x02020202,0x0B0B0B0B,0x0A0A0A0A,0x03030303,0x0A0A0A0A,0x0B0B0B0B,
   0x00000000,0x05050505,0x00000000,0x0D0D0D0D,0x08080808,0x05050505,0x08080808,0x0D0D0D0D,
   0x00000000,0x07070707,0x00000000,0x0F0F0F0F,0x08080808,0x07070707,0x08080808,0x0F0F0F0F,
   0x02020202,0x05050505,0x02020202,0x0D0D0D0D,0x0A0A0A0A,0x05050505,0x0A0A0A0A,0x0D0D0D0D,
   0x02020202,0x07070707,0x02020202,0x0F0F0F0F,0x0A0A0A0A,0x07070707,0x0A0A0A0A,0x0F0F0F0F,
   0x04040404,0x01010101,0x04040404,0x09090909,0x0C0C0C0C,0x01010101,0x0C0C0C0C,0x09090909,
   0x04040404,0x03030303,0x04040404,0x0B0B0B0B,0x0C0C0C0C,0x03030303,0x0C0C0C0C,0x0B0B0B0B,
   0x06060606,0x01010101,0x06060606,0x09090909,0x0E0E0E0E,0x01010101,0x0E0E0E0E,0x09090909,
   0x06060606,0x03030303,0x06060606,0x0B0B0B0B,0x0E0E0E0E,0x03030303,0x0E0E0E0E,0x0B0B0B0B,
   0x04040404,0x05050505,0x04040404,0x0D0D0D0D,0x0C0C0C0C,0x05050505,0x0C0C0C0C,0x0D0D0D0D,
   0x04040404,0x07070707,0x04040404,0x0F0F0F0F,0x0C0C0C0C,0x07070707,0x0C0C0C0C,0x0F0F0F0F,
   0x06060606,0x05050505,0x06060606,0x0D0D0D0D,0x0E0E0E0E,0x05050505,0x0E0E0E0E,0x0D0D0D0D,
   0x06060606,0x07070707,0x06060606,0x0F0F0F0F,0x0E0E0E0E,0x07070707,0x0E0E0E0E,0x0F0F0F0F,
   0x01010101,0x00000000,0x01010101,0x08080808,0x09090909,0x00000000,0x09090909,0x08080808,
   0x01010101,0x02020202,0x01010101,0x0A0A0A0A,0x09090909,0x02020202,0x09090909,0x0A0A0A0A,
   0x03030303,0x00000000,0x03030303,0x08080808,0x0B0B0B0B,0x00000000,0x0B0B0B0B,0x08080808,
   0x03030303,0x02020202,0x03030303,0x0A0A0A0A,0x0B0B0B0B,0x02020202,0x0B0B0B0B,0x0A0A0A0A,
   0x01010101,0x04040404,0x01010101,0x0C0C0C0C,0x09090909,0x04040404,0x09090909,0x0C0C0C0C,
   0x01010101,0x06060606,0x01010101,0x0E0E0E0E,0x09090909,0x06060606,0x09090909,0x0E0E0E0E,
   0x03030303,0x04040404,0x03030303,0x0C0C0C0C,0x0B0B0B0B,0x04040404,0x0B0B0B0B,0x0C0C0C0C,
   0x03030303,0x06060606,0x03030303,0x0E0E0E0E,0x0B0B0B0B,0x06060606,0x0B0B0B0B,0x0E0E0E0E,
   0x05050505,0x00000000,0x05050505,0x08080808,0x0D0D0D0D,0x00000000,0x0D0D0D0D,0x08080808,
   0x05050505,0x02020202,0x05050505,0x0A0A0A0A,0x0D0D0D0D,0x02020202,0x0D0D0D0D,0x0A0A0A0A,
   0x07070707,0x00000000,0x07070707,0x08080808,0x0F0F0F0F,0x00000000,0x0F0F0F0F,0x08080808,
   0x07070707,0x02020202,0x07070707,0x0A0A0A0A,0x0F0F0F0F,0x02020202,0x0F0F0F0F,0x0A0A0A0A,
   0x05050505,0x04040404,0x05050505,0x0C0C0C0C,0x0D0D0D0D,0x04040404,0x0D0D0D0D,0x0C0C0C0C,
   0x05050505,0x06060606,0x05050505,0x0E0E0E0E,0x0D0D0D0D,0x06060606,0x0D0D0D0D,0x0E0E0E0E,
   0x07070707,0x04040404,0x07070707,0x0C0C0C0C,0x0F0F0F0F,0x04040404,0x0F0F0F0F,0x0C0C0C0C,
   0x07070707,0x06060606,0x07070707,0x0E0E0E0E,0x0F0F0F0F,0x06060606,0x0F0F0F0F,0x0E0E0E0E,
   0x01010101,0x01010101,0x01010101,0x09090909,0x09090909,0x01010101,0x09090909,0x09090909,
   0x01010101,0x03030303,0x01010101,0x0B0B0B0B,0x09090909,0x03030303,0x09090909,0x0B0B0B0B,
   0x03030303,0x01010101,0x03030303,0x09090909,0x0B0B0B0B,0x01010101,0x0B0B0B0B,0x09090909,
   0x03030303,0x03030303,0x03030303,0x0B0B0B0B,0x0B0B0B0B,0x03030303,0x0B0B0B0B,0x0B0B0B0B,
   0x01010101,0x05050505,0x01010101,0x0D0D0D0D,0x09090909,0x05050505,0x09090909,0x0D0D0D0D,
   0x01010101,0x07070707,0x01010101,0x0F0F0F0F,0x09090909,0x07070707,0x09090909,0x0F0F0F0F,
   0x03030303,0x05050505,0x03030303,0x0D0D0D0D,0x0B0B0B0B,0x05050505,0x0B0B0B0B,0x0D0D0D0D,
   0x03030303,0x07070707,0x03030303,0x0F0F0F0F,0x0B0B0B0B,0x07070707,0x0B0B0B0B,0x0F0F0F0F,
   0x05050505,0x01010101,0x05050505,0x09090909,0x0D0D0D0D,0x01010101,0x0D0D0D0D,0x09090909,
   0x05050505,0x03030303,0x05050505,0x0B0B0B0B,0x0D0D0D0D,0x03030303,0x0D0D0D0D,0x0B0B0B0B,
   0x07070707,0x01010101,0x07070707,0x09090909,0x0F0F0F0F,0x01010101,0x0F0F0F0F,0x09090909,
   0x07070707,0x03030303,0x07070707,0x0B0B0B0B,0x0F0F0F0F,0x03030303,0x0F0F0F0F,0x0B0B0B0B,
   0x05050505,0x05050505,0x05050505,0x0D0D0D0D,0x0D0D0D0D,0x05050505,0x0D0D0D0D,0x0D0D0D0D,
   0x05050505,0x07070707,0x05050505,0x0F0F0F0F,0x0D0D0D0D,0x07070707,0x0D0D0D0D,0x0F0F0F0F,
   0x07070707,0x05050505,0x07070707,0x0D0D0D0D,0x0F0F0F0F,0x05050505,0x0F0F0F0F,0x0D0D0D0D,
   0x07070707,0x07070707,0x07070707,0x0F0F0F0F,0x0F0F0F0F,0x07070707,0x0F0F0F0F,0x0F0F0F0F
};

static const tULong M1Map[0x200] =
{
   0x00000000,0x00000000,0x00000000,0x02020000,0x00000000,0x00000202,0x00000000,0x02020202,
   0x02020000,0x00000000,0x02020000,0x02020000,0x02020000,0x00000202,0x02020000,0x02020202,
   0x00000202,0x00000000,0x00000202,0x02020000,0x00000202,0x00000202,0x00000202,0x02020202,
   0x02020202,0x00000000,0x02020202,0x02020000,0x02020202,0x00000202,0x02020202,0x02020202,
   0x00000000,0x01010000,0x00000000,0x03030000,0x00000000,0x01010202,0x00000000,0x03030202,
   0x02020000,0x01010000,0x02020000,0x03030000,0x02020000,0x01010202,0x02020000,0x03030202,
   0x00000202,0x01010000,0x00000202,0x03030000,0x00000202,0x01010202,0x00000202,0x03030202,
   0x02020202,0x01010000,0x02020202,0x03030000,0x02020202,0x01010202,0x02020202,0x03030202,
   0x00000000,0x00000101,0x00000000,0x02020101,0x00000000,0x00000303,0x00000000,0x02020303,
   0x02020000,0x00000101,0x02020000,0x02020101,0x02020000,0x00000303,0x02020000,0x02020303,
   0x00000202,0x00000101,0x00000202,0x02020101,0x00000202,0x00000303,0x00000202,0x02020303,
   0x02020202,0x00000101,0x02020202,0x02020101,0x02020202,0x00000303,0x02020202,0x02020303,
   0x00000000,0x01010101,0x00000000,0x03030101,0x00000000,0x01010303,0x00000000,0x03030303,
   0x02020000,0x01010101,0x02020000,0x03030101,0x02020000,0x01010303,0x02020000,0x03030303,
   0x00000202,0x01010101,0x00000202,0x03030101,0x00000202,0x01010303,0x00000202,0x03030303,
   0x02020202,0x01010101,0x02020202,0x03030101,0x02020202,0x01010303,0x02020202,0x03030303,
   0x01010000,0x00000000,0x01010000,0x02020000,0x01010000,0x00000202,0x01010000,0x02020202,
   0x03030000,0x00000000,0x03030000,0x02020000,0x03030000,0x00000202,0x03030000,0x02020202,
   0x01010202,0x00000000,0x01010202,0x02020000,0x01010202,0x00000202,0x01010202,0x02020202,
   0x03030202,0x00000000,0x03030202,0x02020000,0x03030202,0x00000202,0x03030202,0x02020202,
   0x01010000,0x01010000,0x01010000,0x03030000,0x01010000,0x01010202,0x01010000,0x03030202,
   0x03030000,0x01010000,0x03030000,0x03030000,0x03030000,0x01010202,0x03030000,0x03030202,
   0x01010202,0x01010000,0x01010202,0x03030000,0x01010202,0x01010202,0x01010202,0x03030202,
   0x03030202,0x01010000,0x03030202,0x03030000,0x03030202,0x01010202,0x03030202,0x03030202,
   0x01010000,0x00000101,0x01010000,0x02020101,0x01010000,0x00000303,0x01010000,0x02020303,
   0x03030000,0x00000101,0x03030000,0x02020101,0x03030000,0x00000303,0x03030000,0x02020303,
   0x01010202,0x00000101,0x01010202,0x02020101,0x01010202,0x00000303,0x01010202,0x02020303,
   0x03030202,0x00000101,0x03030202,0x02020101,0x03030202,0x00000303,0x03030202,0x02020303,
   0x01010000,0x01010101,0x01010000,0x03030101,0x01010000,0x01010303,0x01010000,0x03030303,
   0x03030000,0x01010101,0x03030000,0x03030101,0x03030000,0x01010303,0x03030000,0x03030303,
   0x01010202,0x01010101,0x01010202,0x03030101,0x01010202,0x01010303,0x01010202,0x03030303,
   0x03030202,0x01010101,0x03030202,0x03030101,0x03030202,0x01010303,0x03030202,0x03030303,
   0x00000101,0x00000000,0x00000101,0x02020000,0x00000101,0x00000202,0x00000101,0x02020202,
   0x02020101,0x00000000,0x02020101,0x02020000,0x02020101,0x00000202,0x02020101,0x02020202,
   0x00000303,0x00000000,0x00000303,0x02020000,0x00000303,0x00000202,0x00000303,0x02020202,
   0x02020303,0x00000000,0x02020303,0x02020000,0x02020303,0x00000202,0x02020303,0x02020202,
   0x00000101,0x01010000,0x00000101,0x03030000,0x00000101,0x01010202,0x00000101,0x03030202,
   0x02020101,0x01010000,0x02020101,0x03030000,0x02020101,0x01010202,0x02020101,0x03030202,
   0x00000303,0x01010000,0x00000303,0x03030000,0x00000303,0x01010202,0x00000303,0x03030202,
   0x02020303,0x01010000,0x02020303,0x03030000,0x02020303,0x01010202,0x02020303,0x03030202,
   0x00000101,0x00000101,0x00000101,0x02020101,0x00000101,0x00000303,0x00000101,0x02020303,
   0x02020101,0x00000101,0x02020101,0x02020101,0x02020101,0x00000303,0x02020101,0x02020303,
   0x00000303,0x00000101,0x00000303,0x02020101,0x00000303,0x00000303,0x00000303,0x02020303,
   0x02020303,0x00000101,0x02020303,0x02020101,0x02020303,0x00000303,0x02020303,0x02020303,
   0x00000101,0x01010101,0x00000101,0x03030101,0x00000101,0x01010303,0x00000101,0x03030303,
   0x02020101,0x01010101,0x02020101,0x03030101,0x02020101,0x01010303,0x02020101,0x03030303,
   0x00000303,0x01010101,0x00000303,0x03030101,0x00000303,0x01010303,0x00000303,0x03030303,
   0x02020303,0x01010101,0x02020303,0x03030101,0x02020303,0x01010303,0x02020303,0x03030303,
   0x01010101,0x00000000,0x01010101,0x02020000,0x01010101,0x00000202,0x01010101,0x02020202,
   0x03030101,0x00000000,0x03030101,0x02020000,0x03030101,0x00000202,0x03030101,0x02020202,
   0x01010303,0x00000000,0x01010303,0x02020000,0x01010303,0x00000202,0x01010303,0x02020202,
   0x03030303,0x00000000,0x03030303,0x02020000,0x03030303,0x00000202,0x03030303,0x02020202,
   0x01010101,0x01010000,0x01010101,0x03030000,0x01010101,0x01010202,0x01010101,0x03030202,
   0x03030101,0x01010000,0x03030101,0x03030000,0x03030101,0x01010202,0x03030101,0x03030202,
   0x01010303,0x01010000,0x01010303,0x03030000,0x01010303,0x01010202,0x01010303,0x03030202,
   0x03030303,0x01010000,0x03030303,0x03030000,0x03030303,0x01010202,0x03030303,0x03030202,
   0x01010101,0x00000101,0x01010101,0x02020101,0x01010101,0x00000303,0x01010101,0x02020303,
   0x03030101,0x00000101,0x03030101,0x02020101,0x03030101,0x00000303,0x03030101,0x02020303,
   0x01010303,0x00000101,0x01010303,0x02020101,0x01010303,0x00000303,0x01010303,0x02020303,
   0x03030303,0x00000101,0x03030303,0x02020101,0x03030303,0x00000303,0x03030303,0x02020303,
   0x01010101,0x01010101,0x01010101,0x03030101,0x01010101,0x01010303,0x01010101,0x03030303,
   0x03030101,0x01010101,0x03030101,0x03030101,0x03030101,0x01010303,0x03030101,0x03030303,
   0x01010303,0x01010101,0x01010303,0x03030101,0x01010303,0x01010303,0x01010303,0x03030303,
   0x03030303,0x01010101,0x03030303,0x03030101,0x03030303,0x01010303,0x03030303,0x03030303
};

static const tULong M2Map[0x200] =
{
   0x00000000,0x00000000,0x00000000,0x01000000,0x00000000,0x00010000,0x00000000,0x01010000,
   0x00000000,0x00000100,0x00000000,0x01000100,0x00000000,0x00010100,0x00000000,0x01010100,
   0x00000000,0x00000001,0x00000000,0x01000001,0x00000000,0x00010001,0x00000000,0x01010001,
   0x00000000,0x00000101,0x00000000,0x01000101,0x00000000,0x00010101,0x00000000,0x01010101,
   0x01000000,0x00000000,0x01000000,0x01000000,0x01000000,0x00010000,0x01000000,0x01010000,
   0x01000000,0x00000100,0x01000000,0x01000100,0x01000000,0x00010100,0x01000000,0x01010100,
   0x01000000,0x00000001,0x01000000,0x01000001,0x01000000,0x00010001,0x01000000,0x01010001,
   0x01000000,0x00000101,0x01000000,0x01000101,0x01000000,0x00010101,0x01000000,0x01010101,
   0x00010000,0x00000000,0x00010000,0x01000000,0x00010000,0x00010000,0x00010000,0x01010000,
   0x00010000,0x00000100,0x00010000,0x01000100,0x00010000,0x00010100,0x00010000,0x01010100,
   0x00010000,0x00000001,0x00010000,0x01000001,0x00010000,0x00010001,0x00010000,0x01010001,
   0x00010000,0x00000101,0x00010000,0x01000101,0x00010000,0x00010101,0x00010000,0x01010101,
   0x01010000,0x00000000,0x01010000,0x01000000,0x01010000,0x00010000,0x01010000,0x01010000,
   0x01010000,0x00000100,0x01010000,0x01000100,0x01010000,0x00010100,0x01010000,0x01010100,
   0x01010000,0x00000001,0x01010000,0x01000001,0x01010000,0x00010001,0x01010000,0x01010001,
   0x01010000,0x00000101,0x01010000,0x01000101,0x01010000,0x00010101,0x01010000,0x01010101,
   0x00000100,0x00000000,0x00000100,0x01000000,0x00000100,0x00010000,0x00000100,0x01010000,
   0x00000100,0x00000100,0x00000100,0x01000100,0x00000100,0x00010100,0x00000100,0x01010100,
   0x00000100,0x00000001,0x00000100,0x01000001,0x00000100,0x00010001,0x00000100,0x01010001,
   0x00000100,0x00000101,0x00000100,0x01000101,0x00000100,0x00010101,0x00000100,0x01010101,
   0x01000100,0x00000000,0x01000100,0x01000000,0x01000100,0x00010000,0x01000100,0x01010000,
   0x01000100,0x00000100,0x01000100,0x01000100,0x01000100,0x00010100,0x01000100,0x01010100,
   0x01000100,0x00000001,0x01000100,0x01000001,0x01000100,0x00010001,0x01000100,0x01010001,
   0x01000100,0x00000101,0x01000100,0x01000101,0x01000100,0x00010101,0x01000100,0x01010101,
   0x00010100,0x00000000,0x00010100,0x01000000,0x00010100,0x00010000,0x00010100,0x01010000,
   0x00010100,0x00000100,0x00010100,0x01000100,0x00010100,0x00010100,0x00010100,0x01010100,
   0x00010100,0x00000001,0x00010100,0x01000001,0x00010100,0x00010001,0x00010100,0x01010001,
   0x00010100,0x00000101,0x00010100,0x01000101,0x00010100,0x00010101,0x00010100,0x01010101,
   0x01010100,0x00000000,0x01010100,0x01000000,0x01010100,0x00010000,0x01010100,0x01010000,
   0x01010100,0x00000100,0x01010100,0x01000100,0x01010100,0x00010100,0x01010100,0x01010100,
   0x01010100,0x00000001,0x01010100,0x01000001,0x01010100,0x00010001,0x01010100,0x01010001,
   0x01010100,0x00000101,0x01010100,0x01000101,0x01010100,0x00010101,0x01010100,0x01010101,
   0x00000001,0x00000000,0x00000001,0x01000000,0x00000001,0x00010000,0x00000001,0x01010000,
   0x00000001,0x00000100,0x00000001,0x01000100,0x00000001,0x00010100,0x00000001,0x01010100,
   0x00000001,0x00000001,0x00000001,0x01000001,0x00000001,0x00010001,0x00000001,0x01010001,
   0x00000001,0x00000101,0x00000001,0x01000101,0x00000001,0x00010101,0x00000001,0x01010101,
   0x01000001,0x00000000,0x01000001,0x01000000,0x01000001,0x00010000,0x01000001,0x01010000,
   0x01000001,0x00000100,0x01000001,0x01000100,0x01000001,0x00010100,0x01000001,0x01010100,
   0x01000001,0x00000001,0x01000001,0x01000001,0x01000001,0x00010001,0x01000001,0x01010001,
   0x01000001,0x00000101,0x01000001,0x01000101,0x01000001,0x00010101,0x01000001,0x01010101,
   0x00010001,0x00000000,0x00010001,0x01000000,0x00010001,0x00010000,0x00010001,0x01010000,
   0x00010001,0x00000100,0x00010001,0x01000100,0x00010001,0x00010100,0x00010001,0x01010100,
   0x00010001,0x00000001,0x00010001,0x01000001,0x00010001,0x00010001,0x00010001,0x01010001,
   0x00010001,0x00000101,0x00010001,0x01000101,0x00010001,0x00010101,0x00010001,0x01010101,
   0x01010001,0x00000000,0x01010001,0x01000000,0x01010001,0x00010000,0x01010001,0x01010000,
   0x01010001,0x00000100,0x01010001,0x01000100,0x01010001,0x00010100,0x01010001,0x01010100,
   0x01010001,0x00000001,0x01010001,0x01000001,0x01010001,0x00010001,0x01010001,0x01010001,
   0x01010001,0x00000101,0x01010001,0x01000101,0x01010001,0x00010101,0x01010001,0x01010101,
   0x00000101,0x00000000,0x00000101,0x01000000,0x00000101,0x00010000,0x00000101,0x01010000,
   0x00000101,0x00000100,0x00000101,0x01000100,0x00000101,0x00010100,0x00000101,0x01010100,
   0x00000101,0x00000001,0x00000101,0x01000001,0x00000101,0x00010001,0x00000101,0x01010001,
   0x00000101,0x00000101,0x00000101,0x01000101,0x00000101,0x00010101,0x00000101,0x01010101,
   0x01000101,0x00000000,0x01000101,0x01000000,0x01000101,0x00010000,0x01000101,0x01010000,
   0x01000101,0x00000100,0x01000101,0x01000100,0x01000101,0x00010100,0x01000101,0x01010100,
   0x01000101,0x00000001,0x01000101,0x01000001,0x01000101,0x00010001,0x01000101,0x01010001,
   0x01000101,0x00000101,0x01000101,0x01000101,0x01000101,0x00010101,0x01000101,0x01010101,
   0x00010101,0x00000000,0x00010101,0x01000000,0x00010101,0x00010000,0x00010101,0x01010000,
   0x00010101,0x00000100,0x00010101,0x01000100,0x00010101,0x00010100,0x00010101,0x01010100,
   0x00010101,0x00000001,0x00010101,0x01000001,0x00010101,0x00010001,0x00010101,0x01010001,
   0x00010101,0x00000101,0x00010101,0x01000101,0x00010101,0x00010101,0x00010101,0x01010101,
   0x01010101,0x00000000,0x01010101,0x01000000,0x01010101,0x00010000,0x01010101,0x01010000,
   0x01010101,0x00000100,0x01010101,0x01000100,0x01010101,0x00010100,0x01010101,0x01010100,
   0x01010101,0x00000001,0x01010101,0x01000001,0x01010101,0x00010001,0x01010101,0x01010001,
   0x01010101,0x00000101,0x01010101,0x01000101,0x01010101,0x00010101,0x01010101,0x01010101
};

static const tULong M3Map[0x200] =
{
   0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
   0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,
   0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,
   0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
   0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,
   0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,
   0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
   0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,
   0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,
   0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
   0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,
   0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,0x02020202,0x00000000,
   0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,0x02020202,
   0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,
   0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,
   0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,
   0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,
   0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,
   0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,
   0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,
   0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,
   0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,
   0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,
   0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,
   0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,
   0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,
   0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,
   0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,
   0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,
   0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,
   0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,
   0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,
   0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,
   0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,
   0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,
   0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,
   0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,
   0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,
   0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,
   0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,
   0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,
   0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,0x01010101,0x00000000,
   0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,0x01010101,0x02020202,
   0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,0x03030303,0x00000000,
   0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,0x03030303,0x02020202,
   0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,
   0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,
   0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,
   0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,
   0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,
   0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,
   0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,
   0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,
   0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,
   0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,
   0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,
   0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,
   0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,0x01010101,
   0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,
   0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,0x03030303,0x01010101,
   0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303,0x03030303
};

#endif /* CRTC_ENGINE_V2 */


#ifdef ENABLE_TMPI_SPEECH

//
// quantization tables
//
/* frequency, in Hz */
static const tLong TMPI_fm1_table[32] = 
{
	150,  162,  174,  188,  202,  217,  233,  250, 
	267,  286,  305,  325,  346,  368,  391,  415, 
	440,  466,  494,  523,  554,  587,  622,  659, 
	698,  740,  784,  830,  880,  932,  988, 1047
};

static const tLong TMPI_fm2_table[32] =
{
	440,  466,  494,  523,  554,  587,  622,  659, 
	698,  740,  784,  830,  880,  932,  988, 1047,
	1100, 1179, 1254, 1337, 1428, 1528, 1639, 1761,
	1897, 2047, 2214, 2400, 2609, 2842, 3105, 3400
};

static const tLong TMPI_fm3_table[8] = 
{
	1179, 1337, 1528, 1761, 2047, 2400, 2842, 3400
};

static const tLong TMPI_fm4_table[1] =
{
  3500
};

/* bandwidth, in Hz */
static const tLong TMPI_bw_table[4] =
{
  726, 309, 125, 50
};

#ifndef TMPI_SPEECH_FLOAT_MODE
/* amplitude * 1024 */
static const tLong TMPI_ampl_table[16] =
{
	0,   8,  11,  16,  22,  32,  45,   63,
	90, 128, 181, 256, 362, 512, 723, 1024
};
#else /* !TMPI_SPEECH_FLOAT_MODE */
/* amplitude * 1000 */
static const tLong TMPI_ampl_table[16] =
{
	0,   8,  11,  16,  22,  31,  44,   62,
	88, 125, 177, 250, 354, 500, 707, 1000
};
#endif /* !TMPI_SPEECH_FLOAT_MODE */


/* pitch increment, in Hz / 8 ms */
static const tLong TMPI_pi_table[32] = 
{
	0, 1,  2,  3,  4,  5,  6,  7, 
	8, 9, 10, 11, 12, 13, 14, 15,
	0 /* noise */, -15, -14, -13, -12, -11, -10, -9, 
	-8, -7, -6, -5, -4, -3, -2, -1
};

#endif /* ENABLE_TMPI_SPEECH */


static const tUChar BasicColors[] =
{
/*  0 */ 13,
/*  1 */ 13,
/*  2 */ 19,
/*  3 */ 25,
/*  4 */ 1,
/*  5 */ 7,
/*  6 */ 10,
/*  7 */ 16,
/*  8 */ 7,
/*  9 */ 25,
/* 10 */ 24,
/* 11 */ 26,
/* 12 */ 6,
/* 13 */ 8,
/* 14 */ 15,
/* 15 */ 17,
/* 16 */ 30,
/* 17 */ 31,
/* 18 */ 18,
/* 19 */ 20,
/* 20 */ 0,
/* 21 */ 2,
/* 22 */ 9,
/* 23 */ 11,
/* 24 */ 4,
/* 25 */ 22,
/* 26 */ 21,
/* 27 */ 23,
/* 28 */ 3,
/* 29 */ 5,
/* 30 */ 12,
/* 31 */ 14,
};


// GateArray color from BASIC index
static const tUChar GateArrayColors[] =
{
/*  0 */ 20,    // RGB=#000000, (TColor)=0x000000
/*  1 */ 4,     // RGB=#000080, (TColor)=0x800000
/*  2 */ 21,    // RGB=#0000FF, (TColor)=0xFF0000
/*  3 */ 28,    // RGB=#800000, (TColor)=0x000080
/*  4 */ 24,    // RGB=#800080, (TColor)=0x800080
/*  5 */ 29,    // RGB=#8000FF, (TColor)=0xFF0080
/*  6 */ 12,    // RGB=#FF0000, (TColor)=0x0000FF
/*  7 */ 5,     // RGB=#ff0080, (TColor)=0x8000FF
/*  8 */ 13,    // RGB=#FF00FF, (TColor)=0xFF00FF
/*  9 */ 22,    // RGB=#008000, (TColor)=0x008000
/* 10 */ 6,     // RGB=#008080, (TColor)=0x808000
/* 11 */ 23,    // RGB=#0080FF, (TColor)=0xFF8000
/* 12 */ 30,    // RGB=#808000, (TColor)=0x008080
/* 13 */ 0,     // RGB=#808080, (TColor)=0x808080
/* 14 */ 31,    // RGB=#8080FF, (TColor)=0xFF8080
/* 15 */ 14,    // RGB=#FF8000, (TColor)=0x0080FF
/* 16 */ 7,     // RGB=#FF8080, (TColor)=0x8080FF
/* 17 */ 15,    // RGB=#FF80FF, (TColor)=0xFF80FF
/* 18 */ 18,    // RGB=#00FF00, (TColor)=0x00FF00
/* 19 */ 2,     // RGB=#00FF80, (TColor)=0x80FF00
/* 20 */ 19,    // RGB=#00FFFF, (TColor)=0xFFFF00
/* 21 */ 26,    // RGB=#80FF00, (TColor)=0x00FF80
/* 22 */ 25,    // RGB=#80FF80, (TColor)=0x80FF80
/* 23 */ 27,    // RGB=#80FFFF, (TColor)=0xFFFF80
/* 24 */ 10,    // RGB=#FFFF00, (TColor)=0x00FFFF
/* 25 */ 3,     // RGB=#FFFF80, (TColor)=0x80FFFF
/* 26 */ 11,    // RGB=#FFFFFF, (TColor)=0xFFFFFF
/* 27 */ 1,
/* 28 */ 8,
/* 29 */ 9,
/* 30 */ 16,
/* 31 */ 17,
};



//
// Z80 Opcodes timings
//

#ifndef Z80_USE_WS_ADJUST
  #define Z80_INC_WS_ADJUST
  #define Z80_CHECK_WS_ADJUST
  #define Z80_RESET_WS_ADJUST
#else /* !Z80_USE_WS_ADJUST */
  #define Z80_INC_WS_ADJUST \
  { \
    Z80->iWSAdjust++; \
  }
  #define Z80_CHECK_WS_ADJUST \
  { \
    if (Z80->iWSAdjust) \
    { \
      Z80->iCycleCount -= 4; \
    } \
  }
  #define Z80_RESET_WS_ADJUST \
  { \
    Z80->iWSAdjust = 0; \
  }
#endif /* !Z80_USE_WS_ADJUST */


static const tUChar cc_op[256] =
{
/* 00 */  cc_nop, cc_ld_bc_word, cc_ld_mbc_a, cc_inc_bc, cc_inc_b, cc_dec_b, cc_ld_b_byte, cc_rlca,
/* 08 */  cc_ex_af_af, cc_add_hl_bc, cc_ld_a_mbc, cc_dec_bc, cc_inc_c, cc_dec_c, cc_ld_c_byte, cc_rrca,
/* 10 */  cc_djnz, cc_ld_de_word, cc_ld_mde_a, cc_inc_de, cc_inc_d, cc_dec_d, cc_ld_d_byte, cc_rla,
/* 18 */  cc_jr, cc_add_hl_de, cc_ld_a_mde, cc_dec_de, cc_inc_e, cc_dec_e, cc_ld_e_byte, cc_rra,
/* 20 */  cc_jr_nz, cc_ld_hl_word, cc_ld_mword_hl, cc_inc_hl, cc_inc_h, cc_dec_h, cc_ld_h_byte, cc_daa,
/* 28 */  cc_jr_z, cc_add_hl_hl, cc_ld_hl_mword, cc_dec_hl, cc_inc_l, cc_dec_l, cc_ld_l_byte, cc_cpl,
/* 30 */  cc_jr_nc, cc_ld_sp_word, cc_ld_mword_a, cc_inc_sp, cc_inc_mhl, cc_dec_mhl, cc_ld_mhl_byte, cc_scf,
/* 38 */  cc_jr_c, cc_add_hl_sp, cc_ld_a_mword, cc_dec_sp, cc_inc_a, cc_dec_a, cc_ld_a_byte, cc_ccf,
/* 40 */  cc_ld_b_b, cc_ld_b_c, cc_ld_b_d, cc_ld_b_e, cc_ld_b_h, cc_ld_b_l, cc_ld_b_mhl, cc_ld_b_a,
/* 48 */  cc_ld_c_b, cc_ld_c_c, cc_ld_c_d, cc_ld_c_e, cc_ld_c_h, cc_ld_c_l, cc_ld_c_mhl, cc_ld_c_a,
/* 50 */  cc_ld_d_b, cc_ld_d_c, cc_ld_d_d, cc_ld_d_e, cc_ld_d_h, cc_ld_d_l, cc_ld_d_mhl, cc_ld_d_a,
/* 58 */  cc_ld_e_b, cc_ld_e_c, cc_ld_e_d, cc_ld_e_e, cc_ld_e_h, cc_ld_e_l, cc_ld_e_mhl, cc_ld_e_a,
/* 60 */  cc_ld_h_b, cc_ld_h_c, cc_ld_h_d, cc_ld_h_e, cc_ld_h_h, cc_ld_h_l, cc_ld_h_mhl, cc_ld_h_a,
/* 68 */  cc_ld_l_b, cc_ld_l_c, cc_ld_l_d, cc_ld_l_e, cc_ld_l_h, cc_ld_l_l, cc_ld_l_mhl, cc_ld_l_a,
/* 70 */  cc_ld_mhl_b, cc_ld_mhl_c, cc_ld_mhl_d, cc_ld_mhl_e, cc_ld_mhl_h, cc_ld_mhl_l, cc_halt, cc_ld_mhl_a,
/* 78 */  cc_ld_a_b, cc_ld_a_c, cc_ld_a_d, cc_ld_a_e, cc_ld_a_h, cc_ld_a_l, cc_ld_a_mhl, cc_ld_a_a,
/* 80 */  cc_add_b, cc_add_c, cc_add_d, cc_add_e, cc_add_h, cc_add_l, cc_add_mhl, cc_add_a,
/* 88 */  cc_adc_b, cc_adc_c, cc_adc_d, cc_adc_e, cc_adc_h, cc_adc_l, cc_adc_mhl, cc_adc_a,
/* 90 */  cc_sub_b, cc_sub_c, cc_sub_d, cc_sub_e, cc_sub_h, cc_sub_l, cc_sub_mhl, cc_sub_a,
/* 98 */  cc_sbc_b, cc_sbc_c, cc_sbc_d, cc_sbc_e, cc_sbc_h, cc_sbc_l, cc_sbc_mhl, cc_sbc_a,
/* A0 */  cc_and_b, cc_and_c, cc_and_d, cc_and_e, cc_and_h, cc_and_l, cc_and_mhl, cc_and_a,
/* A8 */  cc_xor_b, cc_xor_c, cc_xor_d, cc_xor_e, cc_xor_h, cc_xor_l, cc_xor_mhl, cc_xor_a,
/* B0 */  cc_or_b, cc_or_c, cc_or_d, cc_or_e, cc_or_h, cc_or_l, cc_or_mhl, cc_or_a,
/* B8 */  cc_cp_b, cc_cp_c, cc_cp_d, cc_cp_e, cc_cp_h, cc_cp_l, cc_cp_mhl, cc_cp_a,
/* C0 */  cc_ret_nz, cc_pop_bc, cc_jp_nz, cc_jp, cc_call_nz, cc_push_bc, cc_add_byte, cc_rst00,
/* C8 */  cc_ret_z, cc_ret, cc_jp_z, cc_pfx_cb, cc_call_z, cc_call, cc_adc_byte, cc_rst08,
/* D0 */  cc_ret_nc, cc_pop_de, cc_jp_nc, cc_outa, cc_call_nc, cc_push_de, cc_sub_byte, cc_rst10,
/* D8 */  cc_ret_c, cc_exx, cc_jp_c, cc_ina, cc_call_c, cc_pfx_dd, cc_sbc_byte, cc_rst18,
/* E0 */  cc_ret_po, cc_pop_hl, cc_jp_po, cc_ex_msp_hl, cc_call_po, cc_push_hl, cc_and_byte, cc_rst20,
/* E8 */  cc_ret_pe, cc_ld_pc_hl, cc_jp_pe, cc_ex_de_hl, cc_call_pe, cc_pfx_ed, cc_xor_byte, cc_rst28,
/* F0 */  cc_ret_p, cc_pop_af, cc_jp_p, cc_di, cc_call_p, cc_push_af, cc_or_byte, cc_rst30,
/* F8 */  cc_ret_m, cc_ld_sp_hl, cc_jp_m, cc_ei, cc_call_m, cc_pfx_fd, cc_cp_byte, cc_rst38
};

// Used by z80_pfx_cb
// 1 NOP added by prefix CB
static const tUChar cc_cb[256] =
{
/* 00 */  cc_rlc_b, cc_rlc_c, cc_rlc_d, cc_rlc_e, cc_rlc_h, cc_rlc_l, cc_rlc_mhl, cc_rlc_a,
/* 08 */  cc_rrc_b, cc_rrc_c, cc_rrc_d, cc_rrc_e, cc_rrc_h, cc_rrc_l, cc_rrc_mhl, cc_rrc_a,
/* 10 */  cc_rl_b, cc_rl_c, cc_rl_d, cc_rl_e, cc_rl_h, cc_rl_l, cc_rl_mhl, cc_rl_a,
/* 18 */  cc_rr_b, cc_rr_c, cc_rr_d, cc_rr_e, cc_rr_h, cc_rr_l, cc_rr_mhl, cc_rr_a,
/* 20 */  cc_sla_b, cc_sla_c, cc_sla_d, cc_sla_e, cc_sla_h, cc_sla_l, cc_sla_mhl, cc_sla_a,
/* 28 */  cc_sra_b, cc_sra_c, cc_sra_d, cc_sra_e, cc_sra_h, cc_sra_l, cc_sra_mhl, cc_sra_a,
/* 30 */  cc_sll_b, cc_sll_c, cc_sll_d, cc_sll_e, cc_sll_h, cc_sll_l, cc_sll_mhl, cc_sll_a,
/* 38 */  cc_srl_b, cc_srl_c, cc_srl_d, cc_srl_e, cc_srl_h, cc_srl_l, cc_srl_mhl, cc_srl_a,
/* 40 */  cc_bit_b, cc_bit_c, cc_bit_d, cc_bit_e, cc_bit_h, cc_bit_l, cc_bit_mhl, cc_bit_a,
/* 48 */  cc_bit_b, cc_bit_c, cc_bit_d, cc_bit_e, cc_bit_h, cc_bit_l, cc_bit_mhl, cc_bit_a,
/* 50 */  cc_bit_b, cc_bit_c, cc_bit_d, cc_bit_e, cc_bit_h, cc_bit_l, cc_bit_mhl, cc_bit_a,
/* 58 */  cc_bit_b, cc_bit_c, cc_bit_d, cc_bit_e, cc_bit_h, cc_bit_l, cc_bit_mhl, cc_bit_a,
/* 60 */  cc_bit_b, cc_bit_c, cc_bit_d, cc_bit_e, cc_bit_h, cc_bit_l, cc_bit_mhl, cc_bit_a,
/* 68 */  cc_bit_b, cc_bit_c, cc_bit_d, cc_bit_e, cc_bit_h, cc_bit_l, cc_bit_mhl, cc_bit_a,
/* 70 */  cc_bit_b, cc_bit_c, cc_bit_d, cc_bit_e, cc_bit_h, cc_bit_l, cc_bit_mhl, cc_bit_a,
/* 78 */  cc_bit_b, cc_bit_c, cc_bit_d, cc_bit_e, cc_bit_h, cc_bit_l, cc_bit_mhl, cc_bit_a,
/* 80 */  cc_res_b, cc_res_c, cc_res_d, cc_res_e, cc_res_h, cc_res_l, cc_res_mhl, cc_res_a,
/* 88 */  cc_res_b, cc_res_c, cc_res_d, cc_res_e, cc_res_h, cc_res_l, cc_res_mhl, cc_res_a,
/* 90 */  cc_res_b, cc_res_c, cc_res_d, cc_res_e, cc_res_h, cc_res_l, cc_res_mhl, cc_res_a,
/* 98 */  cc_res_b, cc_res_c, cc_res_d, cc_res_e, cc_res_h, cc_res_l, cc_res_mhl, cc_res_a,
/* A0 */  cc_res_b, cc_res_c, cc_res_d, cc_res_e, cc_res_h, cc_res_l, cc_res_mhl, cc_res_a,
/* A8 */  cc_res_b, cc_res_c, cc_res_d, cc_res_e, cc_res_h, cc_res_l, cc_res_mhl, cc_res_a,
/* B0 */  cc_res_b, cc_res_c, cc_res_d, cc_res_e, cc_res_h, cc_res_l, cc_res_mhl, cc_res_a,
/* B8 */  cc_res_b, cc_res_c, cc_res_d, cc_res_e, cc_res_h, cc_res_l, cc_res_mhl, cc_res_a,
/* C0 */  cc_set_b, cc_set_c, cc_set_d, cc_set_e, cc_set_h, cc_set_l, cc_set_mhl, cc_set_a,
/* C8 */  cc_set_b, cc_set_c, cc_set_d, cc_set_e, cc_set_h, cc_set_l, cc_set_mhl, cc_set_a,
/* D0 */  cc_set_b, cc_set_c, cc_set_d, cc_set_e, cc_set_h, cc_set_l, cc_set_mhl, cc_set_a,
/* D8 */  cc_set_b, cc_set_c, cc_set_d, cc_set_e, cc_set_h, cc_set_l, cc_set_mhl, cc_set_a,
/* E0 */  cc_set_b, cc_set_c, cc_set_d, cc_set_e, cc_set_h, cc_set_l, cc_set_mhl, cc_set_a,
/* E8 */  cc_set_b, cc_set_c, cc_set_d, cc_set_e, cc_set_h, cc_set_l, cc_set_mhl, cc_set_a,
/* F0 */  cc_set_b, cc_set_c, cc_set_d, cc_set_e, cc_set_h, cc_set_l, cc_set_mhl, cc_set_a,
/* F8 */  cc_set_b, cc_set_c, cc_set_d, cc_set_e, cc_set_h, cc_set_l, cc_set_mhl, cc_set_a
};


// Used by z80_pfx_ed
// 1 NOP added by prefix ED
static const tUChar cc_ed[256] =
{
/* 00 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 08 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 10 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 18 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 20 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 28 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 30 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 38 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 40 */  cc_in_r_c, cc_out_c_r, cc_sbc_hl_bc, cc_ld_EDmword_bc, cc_neg, cc_retn, cc_im_0, cc_ld_i_a,
/* 48 */  cc_in_r_c, cc_out_c_r, cc_adc_hl_bc, cc_ld_EDbc_mword, cc_neg, cc_reti, cc_im_0_1, cc_ld_r_a,
/* 50 */  cc_in_r_c, cc_out_c_r, cc_sbc_hl_de, cc_ld_EDmword_de, cc_neg, cc_retn, cc_im_1, cc_ld_a_i,
/* 58 */  cc_in_r_c, cc_out_c_r, cc_adc_hl_de, cc_ld_EDde_mword, cc_neg, cc_reti, cc_im_2, cc_ld_a_r,
/* 60 */  cc_in_r_c, cc_out_c_r, cc_sbc_hl_hl, cc_ld_EDmword_hl, cc_neg, cc_retn, cc_im_0_2, cc_rrd,
/* 68 */  cc_in_r_c, cc_out_c_r, cc_adc_hl_hl, cc_ld_EDhl_mword, cc_neg, cc_reti, cc_im_0_2, cc_rld,
/* 70 */  cc_in_f_c, cc_out_c_0, cc_sbc_hl_sp, cc_ld_EDmword_sp, cc_neg, cc_retn, cc_im_1_1, cc_nop,
/* 78 */  cc_in_r_c, cc_out_c_r, cc_adc_hl_sp, cc_ld_EDsp_mword, cc_neg, cc_reti, cc_im_2_1, cc_nop,
/* 80 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 88 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 90 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* 98 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* A0 */  cc_ldi, cc_cpi, cc_ini, cc_outi,  cc_nop, cc_nop, cc_nop, cc_nop,
/* A8 */  cc_ldd, cc_cpd, cc_ind, cc_outd,  cc_nop, cc_nop, cc_nop, cc_nop,
/* B0 */  cc_ldir, cc_cpir, cc_inir, cc_otir,  cc_nop, cc_nop, cc_nop, cc_nop,
/* B8 */  cc_lddr, cc_cpdr, cc_indr, cc_otdr,  cc_nop, cc_nop, cc_nop, cc_nop,
/* C0 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* C8 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* D0 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* D8 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* E0 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* E8 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* F0 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
/* F8 */  cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop, cc_nop,
};


// Used by z80_pfx_dd and z80_pfx_fd
// 1 NOP added by prefix DD or FD
static const tUChar cc_xy[256] =
{
/* 00 */  cc_nop, cc_ld_bc_word, cc_ld_mbc_a, cc_inc_bc, cc_inc_b, cc_dec_b, cc_ld_b_byte, cc_rlca,
/* 08 */  cc_rlca, cc_add_xy_bc, cc_ld_a_mbc, cc_dec_bc, cc_inc_c, cc_dec_c, cc_ld_c_byte, cc_rrca,
/* 10 */  cc_djnz, cc_ld_de_word, cc_ld_mde_a, cc_inc_de, cc_inc_d, cc_dec_d, cc_ld_d_byte, cc_rla,
/* 18 */  cc_jr, cc_add_xy_de, cc_ld_a_mde, cc_dec_de, cc_inc_e, cc_dec_e, cc_ld_e_byte, cc_rra,
/* 20 */  cc_jr_nz, cc_ld_xy_word, cc_ld_mword_xy, cc_inc_xy, cc_inc_xyh, cc_dec_xyh, cc_ld_xyh_byte, cc_daa,
/* 28 */  cc_jr_z, cc_add_xy_xy, cc_ld_xy_mword, cc_dec_xy, cc_inc_xyl, cc_dec_xyl, cc_ld_xyl_byte, cc_cpl,
/* 30 */  cc_jr_nc, cc_ld_sp_word, cc_ld_mword_a, cc_inc_sp, cc_inc_mxy, cc_dec_mxy, cc_ld_mxy_byte, cc_scf,
/* 38 */  cc_jr_c, cc_add_xy_sp, cc_ld_a_mword, cc_dec_sp, cc_inc_a, cc_dec_a, cc_ld_a_byte, cc_ccf,
/* 40 */  cc_ld_b_b, cc_ld_b_c, cc_ld_b_d, cc_ld_b_e, cc_ld_b_xyh, cc_ld_b_xyl, cc_ld_b_mxy, cc_ld_b_a,
/* 48 */  cc_ld_c_b, cc_ld_c_c, cc_ld_c_d, cc_ld_c_e, cc_ld_c_xyh, cc_ld_c_xyl, cc_ld_c_mxy, cc_ld_c_a,
/* 50 */  cc_ld_d_b, cc_ld_d_c, cc_ld_d_d, cc_ld_d_e, cc_ld_d_xyh, cc_ld_d_xyl, cc_ld_d_mxy, cc_ld_d_a,
/* 58 */  cc_ld_e_b, cc_ld_e_c, cc_ld_e_d, cc_ld_e_e, cc_ld_e_xyh, cc_ld_e_xyl, cc_ld_e_mxy, cc_ld_e_a,
/* 60 */  cc_ld_xyh_b, cc_ld_xyh_c, cc_ld_xyh_d, cc_ld_xyh_e, cc_ld_xyh_xyh, cc_ld_xyh_xyl, cc_ld_h_mxy, cc_ld_xyh_a,
/* 68 */  cc_ld_xyl_b, cc_ld_xyl_c, cc_ld_xyl_d, cc_ld_xyl_e, cc_ld_xyl_xyh, cc_ld_xyl_xyl, cc_ld_l_mxy, cc_ld_xyl_a,
/* 70 */  cc_ld_mxy_b, cc_ld_mxy_c, cc_ld_mxy_d, cc_ld_mxy_e, cc_ld_mxy_h, cc_ld_mxy_l, cc_halt, cc_ld_mxy_a,
/* 78 */  cc_ld_a_b, cc_ld_a_c, cc_ld_a_d, cc_ld_a_e, cc_ld_a_xyh, cc_ld_a_xyl, cc_ld_a_mxy, cc_ld_a_a,
/* 80 */  cc_add_b, cc_add_c, cc_add_d, cc_add_e, cc_add_xyh, cc_add_xyl, cc_add_mxy, cc_add_a,
/* 88 */  cc_adc_b, cc_adc_c, cc_adc_d, cc_adc_e, cc_adc_xyh, cc_adc_xyl, cc_adc_mxy, cc_adc_a,
/* 90 */  cc_sub_b, cc_sub_c, cc_sub_d, cc_sub_e, cc_sub_xyh, cc_sub_xyl, cc_sub_mxy, cc_sub_a,
/* 98 */  cc_sbc_b, cc_sbc_c, cc_sbc_d, cc_sbc_e, cc_sbc_xyh, cc_sbc_xyl, cc_sbc_mxy, cc_sbc_a,
/* A0 */  cc_and_b, cc_and_c, cc_and_d, cc_and_e, cc_and_xyh, cc_and_xyl, cc_and_mxy, cc_and_a,
/* A8 */  cc_xor_b, cc_xor_c, cc_xor_d, cc_xor_e, cc_xor_xyh, cc_xor_xyl, cc_xor_mxy, cc_xor_a,
/* B0 */  cc_or_b, cc_or_c, cc_or_d, cc_or_e, cc_or_xyh, cc_or_xyl, cc_or_mxy, cc_or_a,
/* B8 */  cc_cp_b, cc_cp_c, cc_cp_d, cc_cp_e, cc_cp_xyh, cc_cp_xyl, cc_cp_mxy, cc_cp_a,
/* C0 */  cc_ret_nz, cc_pop_bc, cc_jp_nz, cc_jp, cc_call_nz, cc_push_bc, cc_add_byte, cc_rst00,
/* C8 */  cc_ret_z, cc_ret, cc_jp_z, cc_pfx_cb, cc_call_z, cc_call, cc_adc_byte, cc_rst08,
/* D0 */  cc_ret_nc, cc_pop_de, cc_jp_nc, cc_outa, cc_call_nc, cc_push_de, cc_sub_byte, cc_rst10,
/* D8 */  cc_ret_c, cc_exx, cc_jp_c, cc_ina, cc_call_c, cc_pfx_dd, cc_sbc_byte, cc_rst18,
/* E0 */  cc_ret_po, cc_pop_xy, cc_jp_po, cc_ex_msp_xy, cc_call_po, cc_push_xy, cc_and_byte, cc_rst20,
/* E8 */  cc_ret_pe, cc_ld_pc_xy, cc_jp_pe, cc_ex_de_hl, cc_call_pe, cc_pfx_ed, cc_xor_byte, cc_rst28,
/* F0 */  cc_ret_p, cc_pop_af, cc_jp_p, cc_di, cc_call_p, cc_push_af, cc_or_byte, cc_rst30,
/* F8 */  cc_ret_m, cc_ld_sp_xy, cc_jp_m, cc_ei, cc_call_m, cc_pfx_fd, cc_cp_byte, cc_rst38
};

// Used by z80_pfx_ddcb and z80_pfx_fdcb
// 2 NOPs added by prefixes DD+CB or FD+CB
static const tUChar cc_xycb[256] =
{
/* 00 */  cc_rlc_mxy_b, cc_rlc_mxy_c, cc_rlc_mxy_d, cc_rlc_mxy_e, cc_rlc_mxy_h, cc_rlc_mxy_l, cc_rlc_mxy, cc_rlc_mxy_a,
/* 08 */  cc_rrc_mxy_b, cc_rrc_mxy_c, cc_rrc_mxy_d, cc_rrc_mxy_e, cc_rrc_mxy_h, cc_rrc_mxy_l, cc_rrc_mxy, cc_rrc_mxy_a,
/* 10 */  cc_rl_mxy_b, cc_rl_mxy_c, cc_rl_mxy_d, cc_rl_mxy_e, cc_rl_mxy_h, cc_rl_mxy_l, cc_rl_mxy, cc_rl_mxy_a,
/* 18 */  cc_rr_mxy_b, cc_rr_mxy_c, cc_rr_mxy_d, cc_rr_mxy_e, cc_rr_mxy_h, cc_rr_mxy_l, cc_rr_mxy, cc_rr_mxy_a,
/* 20 */  cc_sla_mxy_b, cc_sla_mxy_c, cc_sla_mxy_d, cc_sla_mxy_e, cc_sla_mxy_h, cc_sla_mxy_l, cc_sla_mxy, cc_sla_mxy_a,
/* 28 */  cc_sra_mxy_b, cc_sra_mxy_c, cc_sra_mxy_d, cc_sra_mxy_e, cc_sra_mxy_h, cc_sra_mxy_l, cc_sra_mxy, cc_sra_mxy_a,
/* 30 */  cc_sll_mxy_b, cc_sll_mxy_c, cc_sll_mxy_d, cc_sll_mxy_e, cc_sll_mxy_h, cc_sll_mxy_l, cc_sll_mxy, cc_sll_mxy_a,
/* 38 */  cc_srl_mxy_b, cc_srl_mxy_c, cc_srl_mxy_d, cc_srl_mxy_e, cc_srl_mxy_h, cc_srl_mxy_l, cc_srl_mxy, cc_srl_mxy_a,
/* 40 */  cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy,
/* 48 */  cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy,
/* 50 */  cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy,
/* 58 */  cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy,
/* 60 */  cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy,
/* 68 */  cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy,
/* 70 */  cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy,
/* 78 */  cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy, cc_bit_mxy,
/* 80 */  cc_res_mxy_b, cc_res_mxy_c, cc_res_mxy_d, cc_res_mxy_e, cc_res_mxy_h, cc_res_mxy_l, cc_res_mxy, cc_res_mxy_a,
/* 88 */  cc_res_mxy_b, cc_res_mxy_c, cc_res_mxy_d, cc_res_mxy_e, cc_res_mxy_h, cc_res_mxy_l, cc_res_mxy, cc_res_mxy_a,
/* 90 */  cc_res_mxy_b, cc_res_mxy_c, cc_res_mxy_d, cc_res_mxy_e, cc_res_mxy_h, cc_res_mxy_l, cc_res_mxy, cc_res_mxy_a,
/* 98 */  cc_res_mxy_b, cc_res_mxy_c, cc_res_mxy_d, cc_res_mxy_e, cc_res_mxy_h, cc_res_mxy_l, cc_res_mxy, cc_res_mxy_a,
/* A0 */  cc_res_mxy_b, cc_res_mxy_c, cc_res_mxy_d, cc_res_mxy_e, cc_res_mxy_h, cc_res_mxy_l, cc_res_mxy, cc_res_mxy_a,
/* A8 */  cc_res_mxy_b, cc_res_mxy_c, cc_res_mxy_d, cc_res_mxy_e, cc_res_mxy_h, cc_res_mxy_l, cc_res_mxy, cc_res_mxy_a,
/* B0 */  cc_res_mxy_b, cc_res_mxy_c, cc_res_mxy_d, cc_res_mxy_e, cc_res_mxy_h, cc_res_mxy_l, cc_res_mxy, cc_res_mxy_a,
/* B8 */  cc_res_mxy_b, cc_res_mxy_c, cc_res_mxy_d, cc_res_mxy_e, cc_res_mxy_h, cc_res_mxy_l, cc_res_mxy, cc_res_mxy_a,
/* C0 */  cc_set_mxy_b, cc_set_mxy_c, cc_set_mxy_d, cc_set_mxy_e, cc_set_mxy_h, cc_set_mxy_l, cc_set_mxy, cc_set_mxy_a,
/* C8 */  cc_set_mxy_b, cc_set_mxy_c, cc_set_mxy_d, cc_set_mxy_e, cc_set_mxy_h, cc_set_mxy_l, cc_set_mxy, cc_set_mxy_a,
/* D0 */  cc_set_mxy_b, cc_set_mxy_c, cc_set_mxy_d, cc_set_mxy_e, cc_set_mxy_h, cc_set_mxy_l, cc_set_mxy, cc_set_mxy_a,
/* D8 */  cc_set_mxy_b, cc_set_mxy_c, cc_set_mxy_d, cc_set_mxy_e, cc_set_mxy_h, cc_set_mxy_l, cc_set_mxy, cc_set_mxy_a,
/* E0 */  cc_set_mxy_b, cc_set_mxy_c, cc_set_mxy_d, cc_set_mxy_e, cc_set_mxy_h, cc_set_mxy_l, cc_set_mxy, cc_set_mxy_a,
/* E8 */  cc_set_mxy_b, cc_set_mxy_c, cc_set_mxy_d, cc_set_mxy_e, cc_set_mxy_h, cc_set_mxy_l, cc_set_mxy, cc_set_mxy_a,
/* F0 */  cc_set_mxy_b, cc_set_mxy_c, cc_set_mxy_d, cc_set_mxy_e, cc_set_mxy_h, cc_set_mxy_l, cc_set_mxy, cc_set_mxy_a,
/* F8 */  cc_set_mxy_b, cc_set_mxy_c, cc_set_mxy_d, cc_set_mxy_e, cc_set_mxy_h, cc_set_mxy_l, cc_set_mxy, cc_set_mxy_a,
};


#ifdef USE_XMASS

static const tUChar identify_device_dom_reply_template[] =
{
	/* Word 0 General configuration bit-significant information */
	0x5A, 0x04,
	/* Word 1 Number of logical cylinders */
	0xFB, 0x00,
	/* Word 2 Reserved */
	0x00, 0x00,
	/* Word 3 Number of logical heads */
	0x10, 0x00,
	/* Word 4-5 retired */
	0x00, 0x00, 0x00, 0x02,
	/* Word 6 Number of logical sectors per logical track */
	0x3F, 0x00,
	/* Word 7-9 retired */
	0x03, 0x00, 0x50, 0xDC, 0x00, 0x00,
	/* Word 10-19 Serial number (20 ASCII characters) */
	0x4F, 0x44, 0x41, 0x4D, 0x41, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
	0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x37, 0x38,
	/* Word 20-21 retired */
	0x01, 0x00, 0x01, 0x00,
	/* Word 22 obsolete */
	0x04, 0x00,
	/* Word 23-26 Firmware revision (8 ASCII characters) */
	0x55, 0x59, 0x4E, 0x41, 0x30, 0x31, 0x36, 0x32,
	/* Word 27-46 Model number (40 ASCII characters) */
	0x51, 0x50, 0x20, 0x49, 0x44, 0x49, 0x20, 0x45, 0x69, 0x44, 0x6B, 0x73, 0x6E, 0x4F, 0x6F, 0x4D,
	0x75, 0x64, 0x65, 0x6C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	/* Word 47 */
	0x01, 0x00,
	/* Word 48 Reserved */
	0x00, 0x00,
	/* Word 49 Capabilities */
	0x00, 0x0F,
	/* Word 50 Capabilities */
	0x00, 0x00,
	/* Word 51 */
	0x00, 0x02,
	/* Word 52 retired */
	0x00, 0x00,
	/* Word 53 */
	0x07, 0x00,
	/* Word 54 Number of current logical cylinders */
	0xFB, 0x00, /* 251 */
	/* Word 55 Number of current logical heads */
	0x10, 0x00, /* 16 */
	/* Word 56 Number of current logical sectors per track */
	0x3F, 0x00, /* 63 */
	/* Word 57-58 Current capacity in sectors */
	0x50, 0xDC, 0x03, 0x00, /* 253008 */
	/* Word 59 */
	0x00, 0x01,
	/* Word 60-61 Total number of user addressable sectors (LBA mode only) */
	0x50, 0xDC, 0x03, 0x00,
	/* Word 62 retired */
	0x00, 0x00,
	/* Word 63 */
	0x01, 0x00,
	/* Word 64 */
	0x02, 0x09,
	/* Word 65 Minimum Multiword DMA transfer cycle time per word */
	0x09, 0x19,
	/* Word 66 Manufacturer’s recommended Multiword DMA transfer cycle time */
	0xFF, 0xFF,
	/* Word 67 Minimum PIO transfer cycle time without flow control */
	0xFF, 0x00,
	/* Word 68 Minimum PIO transfer cycle time with IORDY flow control */
	0x02, 0x00,
	/* Word 69-70 Reserved (for future command overlap and queuing) */
	0xBE, 0x34, 0xC9, 0x35,
	/* Word 71-74 Reserved for IDENTIFY PACKET DEVICE command. */
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	/* Word 75 Queue depth */
	0x00, 0x00,
	/* Word 76-79 */
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	/* Word 80 Major version number */
	0x00, 0x00,
	/* Word 81 Minor version number */
	0x00, 0x00,
	/* Word 82 Command set supported. */
	0x00, 0x00,
	/* Word 83 Command set supported. */
	0x00, 0x00,
	/* Word 84 Command set/feature supported extension. */
	0x02, 0x01,
	/* Word 85 Command set/feature enabled. */
	0x47, 0x46,
	/* Word 86 Command set/feature enabled. */
	0x58, 0x20,
	/* Word 87 Command set/feature default. */
	0x20, 0x20,
	/* Word 88 */
	0x20, 0x20,
	/* Word 89 Time required for security erase unit completion */
	0x20, 0x20,
	/* Word 90 Time required for Enhanced security erase completion */
	0x20, 0x00,
	/* Word 91 Current advanced power management value */
	0x00, 0x00,
	/* Word 92-126 Reserved */
	0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 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, 0x10, 0x00, 0x00, 0x17, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	/* Word 127 Removable Media Status Notification feature set support */
	0x00, 0x00,
	/* Word 128 Security status */
	0x00, 0x00,
	/* Word 129-159 Vendor specific */
	0x01, 0x43, 0x61, 0x50, 0x72, 0x69, 0x43, 0x65, 0x20, 0x46, 0x6F, 0x72, 0x65, 0x76, 0x65, 0x72,
	0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 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,
	/* Word 160-255 Reserved */
	0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x03, 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, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x43, 0x61, 0x50, 0x72,
	0x69, 0x43, 0x65, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x10, 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, 0x10, 0x00, 0x00, 0x17, 0x03,
};

#endif /* USE_XMASS */


#ifdef ENABLE_TAPE

  #define Z80_WAIT_STATES_TAPE \
  { \
    tTape* Tape = NativeCPC->Tape; \
    if (Tape->motor_change_counter) \
    { \
      /* Relay command changed ? */ \
      if (Tape->tape_motor != (NativeCPC->PPI->portC & 0x10)) \
      { \
        Tape->motor_change_counter -= Z80->iCycleCount; \
        if (Tape->motor_change_counter <= 0) \
        { \
          Tape->motor_change_counter = 0; \
          Tape->tape_motor = NativeCPC->PPI->portC & 0x10; \
          Tape->relay_activations++; \
        } \
      } \
      else \
      { \
        Tape->motor_change_counter += Z80->iCycleCount; \
        if (Tape->motor_change_counter > Tape->motor_change_counter_init) \
        { \
          Tape->motor_change_counter = 0; \
        } \
      } \
    } \
    \
    if ( (Tape->tape_motor) && (Tape->tape_play_button) ) \
    { \
      Tape->timer_play_second += Z80->iCycleCount; \
      if (Tape->timer_play_second >= CLOCK_Z80) \
      { \
        Tape->timer_play_second -= CLOCK_Z80; \
        Tape->timer_from_play++; \
      } \
      \
      /* CSW */ \
      if (Tape->pbCSWBuffer) \
      { \
        Tape->iTapeCycleCount -= Z80->iCycleCount << TAPE_CSW_CYCLE_SHIFT; \
        if (Tape->iTapeCycleCount <= 0) \
        { \
          Tape->iTapeCycleCount += (tLong)Tape->csw_cycles_per_sample; \
          Tape->csw_nbsamples--; \
          if (!Tape->csw_nbsamples) \
          { \
            Tape_UpdateCSWLevel(Tape); \
          } \
        } \
      } \
      else /* CDT */ \
      { \
        Tape->iTapeCycleCount -= Z80->iCycleCount; \
        if (Tape->iTapeCycleCount <= 0) \
        { \
          Tape_UpdateLevel(Tape); \
        } \
      } \
    } \
  }

#else /* ENABLE_TAPE */

  #define Z80_WAIT_STATES_TAPE
  
#endif /* ENABLE_TAPE */


#ifdef ENABLE_PLAYCITY

  #define Z80_WAIT_STATES_PLAYCITY \
  { \
    tPlayCity* PlayCity = NativeCPC->PlayCity; \
    if (PlayCity->enabled) \
    { \
      if (PlayCity->CTC_timer[CTC_CHANNEL1]) \
      { \
        PlayCity->CTC_timer[CTC_CHANNEL1] -= Z80->iCycleCount; \
        if (PlayCity->CTC_timer[CTC_CHANNEL1] <= 0) \
        { \
          PlayCity->CTC_timer[CTC_CHANNEL1] += PlayCity->CTC_timer_init[CTC_CHANNEL1]; \
          \
          /* Request NMI */ \
          Z80->Regs.nmi_pending = 1; \
          \
          /* INT request ? */ \
          if (PlayCity->CTC_control_word[CTC_CHANNEL1] & CTC_CONTROL_INT) \
          { \
            Z80->Regs.int_pending = 1; \
          } \
        } \
      } \
      if (PlayCity->CTC_timer[CTC_CHANNEL2]) \
      { \
        PlayCity->CTC_timer[CTC_CHANNEL2] -= Z80->iCycleCount; \
        if (PlayCity->CTC_timer[CTC_CHANNEL2] <= 0) \
        { \
          PlayCity->CTC_timer[CTC_CHANNEL2] += PlayCity->CTC_timer_init[CTC_CHANNEL2]; \
          \
          PlayCity_trigger_channel_3(NativeCPC); \
          \
          /* INT request ? */ \
          if (PlayCity->CTC_control_word[CTC_CHANNEL2] & CTC_CONTROL_INT) \
          { \
            Z80->Regs.int_pending = 1; \
          } \
        } \
      } \
      if (PlayCity->CTC_timer[CTC_CHANNEL3]) \
      { \
        PlayCity->CTC_timer[CTC_CHANNEL3] -= Z80->iCycleCount; \
        if (PlayCity->CTC_timer[CTC_CHANNEL3] <= 0) \
        { \
          PlayCity->CTC_timer[CTC_CHANNEL3] += PlayCity->CTC_timer_init[CTC_CHANNEL3]; \
          \
          /* INT request ? */ \
          if (PlayCity->CTC_control_word[CTC_CHANNEL3] & CTC_CONTROL_INT) \
          { \
            Z80->Regs.int_pending = 1; \
          } \
        } \
      } \
    } \
  }

#else /* ENABLE_PLAYCITY */

  #define Z80_WAIT_STATES_PLAYCITY

#endif /* ENABLE_PLAYCITY */


#ifndef ENABLE_TMPI_SPEECH
  #define Z80_WAIT_STATES_AUDIO_TMPI
#else /* !ENABLE_TMPI_SPEECH */
  #define Z80_WAIT_STATES_AUDIO_TMPI \
  { \
    if (NativeCPC->TMPISpeech->enabled) \
    { \
      tmpi_output(NativeCPC->TMPISpeech); \
    } \
  }
#endif /* !ENABLE_TMPI_SPEECH */

#ifndef ENABLE_PLAYCITY
  #define Z80_WAIT_STATES_AUDIO_PLAYCITY
#else /* ENABLE_PLAYCITY */
  #define Z80_WAIT_STATES_AUDIO_PLAYCITY \
  { \
    if (NativeCPC->PlayCity->snd_enabled) \
    { \
      NativeCPC->PlayCitySynthetizerPtr(NativeCPC); \
    } \
  }
#endif /* USE_PLAY_CITY */

#ifndef ENABLE_DIGIBLASTER
  #define Z80_WAIT_STATES_AUDIO_DIGIBLASTER
#else /* !ENABLE_DIGIBLASTER */
  #define Z80_WAIT_STATES_AUDIO_DIGIBLASTER \
  { \
    if (NativeCPC->Digiblaster->enabled) \
    { \
      digiblaster_output(NativeCPC->Digiblaster); \
    } \
  }
#endif /* !ENABLE_DIGIBLASTER */


#ifndef USE_TURBO_FULLSPEED
#define Z80_WAIT_STATES_AUDIO \
  if (PSG->snd_enabled) \
  { \
    PSG->cycle_count += Z80->iCycleCount << SND_CYCLE_SOUND_SHIFT; \
    if (PSG->cycle_count >= SND_CYCLE_SOUND_INIT) \
    { \
      PSG->cycle_count -= SND_CYCLE_SOUND_INIT; \
      NativeCPC->SpeakerSynthetizerPtr(NativeCPC); \
      \
      Z80_WAIT_STATES_AUDIO_TMPI \
      Z80_WAIT_STATES_AUDIO_PLAYCITY \
      Z80_WAIT_STATES_AUDIO_DIGIBLASTER \
    } \
  }
#else /* USE_TURBO_FULLSPEED */
#define Z80_WAIT_STATES_AUDIO \
  if ( (PSG->snd_enabled) && (NativeCPC->turbo_mode == 0) ) \
  { \
    PSG->cycle_count += Z80->iCycleCount << SND_CYCLE_SOUND_SHIFT; \
    if (PSG->cycle_count >= SND_CYCLE_SOUND_INIT) \
    { \
      PSG->cycle_count -= SND_CYCLE_SOUND_INIT; \
      NativeCPC->SpeakerSynthetizerPtr(NativeCPC); \
      \
      Z80_WAIT_STATES_AUDIO_TMPI \
      Z80_WAIT_STATES_AUDIO_PLAYCITY \
      Z80_WAIT_STATES_AUDIO_DIGIBLASTER \
    } \
  }
#endif /* USE_TURBO_FULLSPEED */

  
#ifdef ENABLE_TMPI_SPEECH
  #define Z80_WAIT_STATES_TMPI \
  { \
    tTMPISpeech* TMPISpeech = NativeCPC->TMPISpeech; \
    if (TMPISpeech->enabled) \
    { \
      if (TMPISpeech->state != MEA8000_STOPPED) \
      { \
        TMPISpeech->cycle_count -= Z80->iCycleCount << TMPI_CYCLE_INIT_SHIFT; \
        if (TMPISpeech->cycle_count <= 0) \
        { \
          TMPISpeech->cycle_count += TMPI_CYCLE_INIT; \
          tmpi_sampling(TMPISpeech); \
        } \
      } \
    } \
  }
#else /* ENABLE_TMPI_SPEECH */
  #define Z80_WAIT_STATES_TMPI
#endif /* ENABLE_TMPI_SPEECH */


#ifndef CRTC_ENGINE_V2
  #define Z80_WAIT_STATES_VIDEO \
  { \
    video_access_memory(NativeCPC, \
                        Z80->iCycleCount >> 2); /* 1MHz CLK */ \
  }
#else /* !CRTC_ENGINE_V2 */
#ifndef USE_TURBO_FULLSPEED
  #define Z80_WAIT_STATES_VIDEO \
  { \
    NativeCPC->VideoEnginePtr(NativeCPC, \
                              Z80->iCycleCount >> 2); /* 1MHz CLK */ \
  }
#else /* USE_TURBO_FULLSPEED */
  #define Z80_WAIT_STATES_VIDEO \
  { \
    if (NativeCPC->turbo_mode == 0) \
    { \
      NativeCPC->VideoEnginePtr(NativeCPC, \
                                Z80->iCycleCount >> 2); /* 1MHz CLK */ \
    } \
    else \
    { \
      video_no_display(NativeCPC, Z80->iCycleCount >> 2); \
    } \
  }
#endif /* USE_TURBO_FULLSPEED */
#endif /* !CRTC_ENGINE_V2 */


#ifdef ENABLE_PRINTER
  #define Z80_WAIT_STATES_PRINTER \
  { \
    if (NativeCPC->Printer->busy) \
    { \
      NativeCPC->Printer->busy_counter -= Z80->iCycleCount; \
      if (NativeCPC->Printer->busy_counter <= 0) \
      { \
        /* Printer no longer busy */ \
        NativeCPC->Printer->busy = 0; \
      } \
    } \
  }
#else /* ENABLE_PRINTER */
  #define Z80_WAIT_STATES_PRINTER
#endif /* ENABLE_PRINTER */


#define Z80_WAIT_STATES \
{ \
  if (Z80->iCycleCount) \
  { \
    /* Video */ \
    Z80_WAIT_STATES_VIDEO \
    \
    /* Audio */ \
    Z80_WAIT_STATES_AUDIO \
    \
    /* FDC */ \
    FDC->z80_wait_state_fct(FDC, Z80->iCycleCount); \
    \
    /* TAPE */ \
    Z80_WAIT_STATES_TAPE \
    \
    /* TMPI */ \
    Z80_WAIT_STATES_TMPI \
    \
    /* Printer */ \
    Z80_WAIT_STATES_PRINTER \
    \
    /* PlayCity */ \
    Z80_WAIT_STATES_PLAYCITY \
    \
    NativeCPC->cycle_count -= Z80->iCycleCount; \
    NativeCPC->timer_from_reset += Z80->iCycleCount; \
  } \
}


// ==================================================================================
//
// Z80 Instruction macros
//
// ==================================================================================

#define GET_ADDR \
{ \
  _WZl = read_mem_pc_sp(NativeCPC, \
                        _PC++); /* subroutine address low byte */ \
  _WZh = read_mem_pc_sp(NativeCPC, \
                        _PC++); /* subroutine address high byte */ \
}



#define Z80_FLAGS_UPDATED \

{ \
  Z80->Regs.flags1++; \
}


// From MAME: Portable Z80 emulator V3.9
#define ADC(value) \
{ \
  tULong ah = _AF & 0xff00; \
  tULong c = _F & Cflag; \
  tULong res = (tUChar)((ah >> 8) + value + c); \
  _F = Z80->SZHVC_add[(c << 16) | ah | res]; \
  _A = res; \
  Z80_FLAGS_UPDATED \
}


#define ADC_MXY(index) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  ADC(read_mem(NativeCPC, \
               (index+o))); \
}
#define ADC_MIX ADC_MXY(_IX)
#define ADC_MIY ADC_MXY(_IY)


/* From Caprice Reloaded */
#define ADC16(reg) \
{ \
  tULong res = _HLdword + Z80->Regs.reg.ULong + (_F & Cflag); \
  _WZdword = _HL + 1; \
  _F = (((_HLdword ^ res ^ Z80->Regs.reg.ULong) >> 8) & Hflag) \
       | ((res >> 16) & Cflag) \
       | ((res >> 8) & (Sflag | Xflags)) \
       | ((res & 0xffff) ? 0 : Zflag) \
       | (((Z80->Regs.reg.ULong ^ _HLdword ^ 0x8000) & (Z80->Regs.reg.ULong ^ res) & 0x8000) >> 13); \
  _HL = res; \
  Z80_FLAGS_UPDATED \
}


// From MAME: Portable Z80 emulator V3.9
#define ADD(value) \
{ \
  tULong ah = _AF & 0xff00; \
  tULong res = (tUChar)((ah >> 8) + value); \
  _F = Z80->SZHVC_add[ah | res]; \
  _A = res; \
  Z80_FLAGS_UPDATED \
}


#define ADD_MXY(index) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  ADD(read_mem(NativeCPC, \
               (index+o))); \
}
#define ADD_MIX ADD_MXY(_IX)
#define ADD_MIY ADD_MXY(_IY)


/* from Caprice Reloaded r598 */
#define ADD16(dest, src) \
{ \
  tULong res = Z80->Regs.dest.UShort.l + Z80->Regs.src.UShort.l; \
  _WZdword = Z80->Regs.dest.ULong + 1; \
  _F = (_F & (Sflag | Zflag | Vflag)) \
       | (((Z80->Regs.dest.ULong ^ res ^ Z80->Regs.src.ULong) >> 8) & Hflag) \
       | ((res >> 16) & Cflag) \
       | ((res >> 8) & Xflags); \
  Z80->Regs.dest.UShort.l = (tUShort)res; \
  Z80_FLAGS_UPDATED \
}


#define AND(val) \
{ \
  _A &= val; \
  _F = Z80->SZP[_A] \
       | Hflag; \
  Z80_FLAGS_UPDATED \
}


#define AND_MXY(index) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  AND(read_mem(NativeCPC, \
               (index+o))); \
}
#define AND_MIX AND_MXY(_IX)
#define AND_MIY AND_MXY(_IY)


#define BIT(bit, reg) \
{ \
  _F = (_F & Cflag) \
       | Hflag \
       | (Z80->SZ_BIT[reg & (1 << bit)] & ~Xflags) \
       | (reg & Xflags); \
  Z80_FLAGS_UPDATED \
}


#define BIT_HL(bit, reg) \
{ \
  _F = (_F & Cflag) \
       | Hflag \
       | (Z80->SZ_BIT[reg & (1 << bit)] & ~Xflags) \
       | (_WZh & Xflags); \
  Z80_FLAGS_UPDATED \
}

#define BIT_XY BIT_HL


// from Caprice Reloaded
#define CALL_PC \
{ \
  write_mem(NativeCPC, \
            --_SP, \
            Z80->Regs.PC.UChar.h); /* store high byte of current PC */ \
  write_mem(NativeCPC, \
            --_SP, \
            Z80->Regs.PC.UChar.l); /* store low byte of current PC */ \
  _PC = _WZ; /* continue execution at subroutine (address was loaded by GET_ADDR) */ \
}
#define CALL    { GET_ADDR; CALL_PC; }
#define CALL_C  { GET_ADDR; if (_F & Cflag)    { Z80->iCycleCount += cc_ex_call_c;  CALL_PC; } }
#define CALL_M  { GET_ADDR; if (_F & Sflag)    { Z80->iCycleCount += cc_ex_call_m;  CALL_PC; } }
#define CALL_NC { GET_ADDR; if (!(_F & Cflag)) { Z80->iCycleCount += cc_ex_call_nc; CALL_PC; } }
#define CALL_NZ { GET_ADDR; if (!(_F & Zflag)) { Z80->iCycleCount += cc_ex_call_nz; CALL_PC; } }
#define CALL_P  { GET_ADDR; if (!(_F & Sflag)) { Z80->iCycleCount += cc_ex_call_p;  CALL_PC; } }
#define CALL_PE { GET_ADDR; if (_F & Pflag)    { Z80->iCycleCount += cc_ex_call_pe; CALL_PC; } }
#define CALL_PO { GET_ADDR; if (!(_F & Pflag)) { Z80->iCycleCount += cc_ex_call_po; CALL_PC; } }
#define CALL_Z  { GET_ADDR; if (_F & Zflag)    { Z80->iCycleCount += cc_ex_call_z;  CALL_PC; } }


#define CCF \
{ \
  if (Z80->Regs.flags2) \
  { \
    _F = ( (_F & (Sflag | Zflag | Pflag | Cflag)) \
           | ((_F & Cflag) << 4) \
           | (_A & Xflags) ) \
         ^ Cflag; \
  } \
  else \
  { \
    _F = ( (_F & (Sflag | Zflag | Pflag | Cflag | Xflags)) \
           | ((_F & Cflag) << 4) \
           | (_A & Xflags) ) \
         ^ Cflag; \
  } \
  Z80_FLAGS_UPDATED \
}


// From MAME: Portable Z80 emulator V3.9
#define CP(value) \
{ \
  tULong val = value; \
  tULong ah = _AF & 0xff00; \
  tULong res = (tUChar)((ah >> 8) - val); \
  _F = (Z80->SZHVC_sub[ah | res] & ~Xflags) \
       | (val & Xflags); \
  Z80_FLAGS_UPDATED \
}


#define CP_MXY(index) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  CP(read_mem(NativeCPC, \
              index+o)); \
  _WZ = index+o; \
}
#define CP_MIX CP_MXY(_IX)
#define CP_MIY CP_MXY(_IY)


/* from Caprice Reloaded r598 */
#define CPD \
{ \
  tULong val = read_mem(NativeCPC, \
                        _HL); \
  tULong res = _A - val; \
  _HL--; \
  _BC--; \
  _F = (_F & Cflag) \
       | (Z80->SZ[(tUChar)res] & ~Xflags) \
       | ((_A ^ val ^ res) & Hflag) \
       | Nflag; \
  if (_F & Hflag) res -= 1; \
	if (res & 0x02) _F |= Yflag; \
	if (res & 0x08) _F |= Xflag; \
  if (_BC) _F |= Vflag; \
  _WZ--; \
  Z80_FLAGS_UPDATED \
}


#define CPDR \
{ \
  CPD; \
  if (_BC && !(_F & Zflag)) \
  { \
    Z80->iCycleCount += cc_ex_cpdr; \
    _PC -= 2; \
    Z80_INC_WS_ADJUST \
    _WZ = _PC+1; /* from Caprice Reloaded r598 */\
  } \
}


#define CPI \
{ \
  tULong val = read_mem(NativeCPC, \
                        _HL); \
  tULong res = _A - val; \
  _HL++; \
  _BC--; \
  _F = (_F & Cflag) \
       | (Z80->SZ[(tUChar)res] & ~Xflags) \
       | ((_A ^ val ^ res) & Hflag) \
       | Nflag; \
  if (_F & Hflag) res -= 1; \
	if (res & 0x02) _F |= Yflag; \
	if (res & 0x08) _F |= Xflag; \
  if (_BC) _F |= Vflag; \
  _WZ++; /* from Caprice Reloaded r598 */\
  Z80_FLAGS_UPDATED \
}


/* from Caprice Reloaded r598 */
#define CPIR \
{ \
  CPI; \
  if (_BC && !(_F & Zflag)) \
  { \
    Z80->iCycleCount += cc_ex_cpir; \
    _PC -= 2; \
    Z80_INC_WS_ADJUST \
    _WZ = _PC+1; \
  } \
}


#define CPL \
{ \
  _A ^= 0xff; \
  _F = (_F & (Sflag | Zflag | Pflag | Cflag)) \
       | Hflag \
       | Nflag \
       | (_A & Xflags); \
  Z80_FLAGS_UPDATED \
}


#define DAA \
{ \
  tULong idx = _A; \
  if (_F & Cflag) \
    idx |= 0x100; \
  if (_F & Hflag) \
    idx |= 0x200; \
  if (_F & Nflag) \
    idx |= 0x400; \
  _AF = NativeCPC->DAATable[idx]; \
  Z80_FLAGS_UPDATED \
}


#define DEC(reg) \
{ \
  reg--; \
  _F = (_F & Cflag) \
       | Z80->SZHV_dec[(tUChar)reg]; \
  Z80_FLAGS_UPDATED \
}


#define DEC_MHL \
{ \
  tULong byte = read_mem(NativeCPC, \
                            _HL); \
  DEC(byte); \
  write_mem(NativeCPC, \
            _HL, \
            byte); \
}


#define DEC_MXY(reg) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  tULong byte = read_mem(NativeCPC, \
                            reg+o); \
  DEC(byte); \
  write_mem(NativeCPC, \
            reg+o, \
            byte); \
  _WZ = reg+o; \
}
#define DEC_MIX DEC_MXY(_IX);
#define DEC_MIY DEC_MXY(_IY);


#define DEC_PAIR(reg) \
{ \
  reg--; \
  Z80_INC_WS_ADJUST \
}


#define DI \
{ \
  _IFF1 = _IFF2 = 0; \
  Z80->Regs.EI_issued = 0; \
}


#define DJNZ \
{ \
  if (--_B) \
  { \
    Z80->iCycleCount += cc_ex_djnz; \
    JR; \
  } \
  else \
  { \
    _PC++; \
  } \
}

#define EI \
{ \
  Z80->Regs.EI_issued = 2; \
}


#define EX(op1, op2) \
{ \
  tRegister temp = op1; \
  op1 = op2; \
  op2 = temp; \
}


/* from Caprice Reloaded r598 */
#define EX_SP(reg) \
{ \
  _WZl = read_mem_pc_sp(NativeCPC, \
                        _SP++); \
  _WZh = read_mem_pc_sp(NativeCPC, \
                        _SP); \
  write_mem_sp(NativeCPC, \
               _SP--, \
               Z80->Regs.reg.UChar.h); \
  write_mem_sp(NativeCPC, \
               _SP, \
               Z80->Regs.reg.UChar.l); \
  Z80->Regs.reg.UShort.l = _WZ; \
}


#define EXX \
{ \
  tRegister Temp; \
  Temp = Z80->Regs.BCx; \
  Z80->Regs.BCx = Z80->Regs.BC; \
  Z80->Regs.BC = Temp; \
  Temp = Z80->Regs.DEx; \
  Z80->Regs.DEx = Z80->Regs.DE; \
  Z80->Regs.DE = Temp; \
  Temp = Z80->Regs.HLx; \
  Z80->Regs.HLx = Z80->Regs.HL; \
  Z80->Regs.HL = Temp; \
}


#define Z80_HALT \
{ \
  _HALT = 1; \
  _PC--; \
}


#define IN_F_C \
{ \
  tUChar res; \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_in_f_c_; \
  res = (tUChar)z80_IN_handler(NativeCPC, \
                               Z80->Regs.BC); \
  _F = (_F & Cflag) \
       | Z80->SZP[res]; \
  Z80_FLAGS_UPDATED \
}


#define IN_REG_C(reg) \
{ \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_in_r_c_; \
  reg = z80_IN_handler(NativeCPC, \
                       Z80->Regs.BC); \
  _F = (_F & Cflag) \
       | Z80->SZP[reg]; \
  _WZ = _BC; \
  Z80_FLAGS_UPDATED \
}


/* from Caprice Reloaded r598 */
#define INA \
{ \
  Z80_WAIT_STATES; \
  Z80->iCycleCount = cc_ina_; \
  \
  _WZl = read_mem_pc_sp(NativeCPC, \
                        _PC++); \
  _WZh = _A; \
  _A = z80_IN_handler(NativeCPC, \
                      Z80->Regs.WZ); \
  _WZ++; \
}


#define INC(reg) \
{ \
  reg++; \
  _F = (_F & Cflag) \
       | Z80->SZHV_inc[(tUChar)reg]; \
  Z80_FLAGS_UPDATED \
}


#define INC_MHL \
{ \
  tULong byte = read_mem(NativeCPC, \
                            _HL); \
  INC(byte); \
  write_mem(NativeCPC, \
            _HL, \
            byte); \
}


#define INC_MXY(reg) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  tULong byte = read_mem(NativeCPC, \
                            reg+o); \
  INC(byte); \
  write_mem(NativeCPC, \
            reg+o, \
            byte); \
  _WZ = reg+o; \
}
#define INC_MIX INC_MXY(_IX);
#define INC_MIY INC_MXY(_IY);


#define INC_PAIR(reg) \
{ \
  reg++; \
  Z80_INC_WS_ADJUST \
}


// From Caprice Reloaded
#define IND \
{ \
  tULong io = z80_IN_handler(NativeCPC, \
                             Z80->Regs.BC); \
  tULong temp; \
  _WZ = _BC - 1; \
  _B--; \
  write_mem(NativeCPC, \
            _HL, \
            io); \
  _HL--; \
  _F = Z80->SZ[_B]; \
  if (io & Sflag) _F |= Nflag; \
  temp = ((_C - 1) & 0xff) + io; \
  if (temp & 0x100) _F |= Hflag | Cflag; \
  _F |= Z80->SZP[(temp & 0x07) ^ _B] & Pflag; \
  Z80_FLAGS_UPDATED \
}


#define IND_ED \
{ \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_ind_; \
  IND; \
}


#define INDR \
{ \
  IND; \
  if (_B) \
  { \
    Z80->iCycleCount += cc_ex_indr; \
    _PC -= 2; \
  } \
}


#define INDR_ED \
{ \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_indr_; \
  INDR; \
}


// From Caprice Reloaded
#define INI \
{ \
  tULong io = z80_IN_handler(NativeCPC, \
                             Z80->Regs.BC); \
  tULong temp; \
  _WZ = _BC + 1; \
  _B--; \
  write_mem(NativeCPC, \
            _HL, \
            io); \
  _HL++; \
  _F = Z80->SZ[_B]; \
  if (io & Sflag) _F |= Nflag; \
  temp = ((_C + 1) & 0xff) + io; \
  if(temp & 0x100) _F |= Hflag | Cflag; \
  _F |= Z80->SZP[(temp & 0x07) ^ _B] & Pflag; \
  Z80_FLAGS_UPDATED \
}


#define INI_ED \
{ \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_ini_; \
  INI; \
}


#define INIR \
{ \
  INI; \
  if(_B) \
  { \
    Z80->iCycleCount += cc_ex_inir; \
    _PC -= 2; \
  } \
}


#define INIR_ED \
{ \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_inir_; \
  INIR; \
}


// From Caprice Reloaded
#define JP_PC \
{ \
  _PC = _WZ; \
}
#define JP    { GET_ADDR; JP_PC; }
#define JP_C  { GET_ADDR; if (_F & Cflag)    { JP_PC; } }
#define JP_M  { GET_ADDR; if (_F & Sflag)    { JP_PC; } }
#define JP_NC { GET_ADDR; if (!(_F & Cflag)) { JP_PC; } }
#define JP_NZ { GET_ADDR; if (!(_F & Zflag)) { JP_PC; } }
#define JP_P  { GET_ADDR; if (!(_F & Sflag)) { JP_PC; } }
#define JP_PE { GET_ADDR; if (_F & Pflag)    { JP_PC; } }
#define JP_PO { GET_ADDR; if (!(_F & Pflag)) { JP_PC; } }
#define JP_Z  { GET_ADDR; if (_F & Zflag)    { JP_PC; } }


/* from Caprice Reloaded r598 */
#define JR \
{ \
  tLong offset = signed_read_mem_pc(NativeCPC, \
                                    _PC); /* grab signed jump offset */ \
  _PC += offset + 1; /* add offset & correct PC */ \
  _WZ = _PC; \
}
#define JR_C  { if (_F & Cflag)    { Z80->iCycleCount += cc_ex_jr_c;  JR; } else { _PC++; } }
#define JR_NC { if (!(_F & Cflag)) { Z80->iCycleCount += cc_ex_jr_nc; JR; } else { _PC++; } }
#define JR_NZ { if (!(_F & Zflag)) { Z80->iCycleCount += cc_ex_jr_nz; JR; } else { _PC++; } }
#define JR_Z  { if (_F & Zflag)    { Z80->iCycleCount += cc_ex_jr_z;  JR; } else { _PC++; } }


#define LD_A_I \
{ \
  _A = _I; \
  _F = (_F & Cflag) \
       | Z80->SZ[_A] \
       | _IFF2; \
  Z80_INC_WS_ADJUST \
  Z80_FLAGS_UPDATED \
}


/* from Caprice Reloaded r598 */
#define LD_A_MPAIR(reg) \
{ \
  _A = read_mem(NativeCPC, reg); \
  _WZ = reg + 1; \
}


#define LD_A_MWORD \
{ \
  _WZl = read_mem_pc_sp(NativeCPC, _PC++); \
  _WZh = read_mem_pc_sp(NativeCPC, _PC++); \
  _A = read_mem(NativeCPC, _WZ); \
  _WZ++; \
}


#define LD_A_R \
{ \
  _A = (_R & 0x7f) \
       | _Rb7; \
  _F = (_F & Cflag) \
       | Z80->SZ[_A] \
       | _IFF2; \
  Z80_INC_WS_ADJUST \
  Z80_FLAGS_UPDATED \
}


#define LD_PAIR_WORD(first, second) \
{ \
  first = read_mem_pc_sp(NativeCPC, _PC++); \
  second = read_mem_pc_sp(NativeCPC, _PC++); \
}
#define LD_BC_WORD LD_PAIR_WORD(_C, _B)
#define LD_DE_WORD LD_PAIR_WORD(_E, _D)
#define LD_HL_WORD LD_PAIR_WORD(_L, _H)
#define LD_IX_WORD LD_PAIR_WORD(_IXl, _IXh)
#define LD_IY_WORD LD_PAIR_WORD(_IYl, _IYh)


#define LD_I_A \
{ \
  _I = _A; \
  Z80_INC_WS_ADJUST \
}


#define LD_MHL_BYTE \
{ \
  tULong byte = read_mem_pc_sp(NativeCPC, \
                               _PC++); \
  write_mem(NativeCPC, \
            _HL, \
            byte); \
}


#define LD_MI_REG(reg, index) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  write_mem(NativeCPC, \
            (index+o), \
            reg); \
  _WZ = index+o; \
}
#define LD_MIX_REG(reg) LD_MI_REG(reg, _IX)
#define LD_MIY_REG(reg) LD_MI_REG(reg, _IY)


#define LD_MXY_BYTE(reg) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  tULong byte = read_mem_pc_sp(NativeCPC, \
                               _PC++); \
  write_mem(NativeCPC, \
            (reg+o), \
            byte); \
  _WZ = reg+o; \
}
#define LD_MIX_BYTE LD_MXY_BYTE(_IX)
#define LD_MIY_BYTE LD_MXY_BYTE(_IY)


// From Caprice Reloaded
#define LD_MWORD_A \
{ \
  _WZl = read_mem_pc_sp(NativeCPC, \
                        _PC++); \
  _WZh = read_mem_pc_sp(NativeCPC, \
                        _PC++); \
  write_mem(NativeCPC, \
            _WZ, \
            _A); \
  _WZ++; \
  _WZh = _A; \
}

#define LD_MPAIR_A(reg) \
{ \
  write_mem(NativeCPC, reg, _A); \
  _WZl = (reg + 1) & 0xff; \
  _WZh = _A; \
}
#define LD_MBC_A LD_MPAIR_A(_BC)
#define LD_MDE_A LD_MPAIR_A(_DE)


#define LD_R_A \
{ \
  _R = _A; \
  _Rb7 = _A & 0x80; \
  Z80_INC_WS_ADJUST \
}


#define LD_REG_MHL_I_(reg, index) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  reg = read_mem(NativeCPC, \
                 (index+o)); \
  _WZ = index+o; \
}


#define LD_SP_WORD \
{ \
  Z80->Regs.SP.UChar.l = read_mem_pc_sp(NativeCPC, \
                                        _PC++); \
  Z80->Regs.SP.UChar.h = read_mem_pc_sp(NativeCPC, \
                                        _PC++); \
}


/* from Caprice Reloaded r598 */
#define LD16_MEM(reg) \
{ \
  _WZl = read_mem_pc_sp(NativeCPC, \
                        _PC++); \
  _WZh = read_mem_pc_sp(NativeCPC, \
                        _PC++); \
  Z80->Regs.reg.UChar.l = read_mem(NativeCPC, \
                                   _WZ); \
  Z80->Regs.reg.UChar.h = read_mem(NativeCPC, \
                                   ++_WZ); \
}


/* from Caprice Reloaded r598 */
#define LDMEM_16(reg) \
{ \
  _WZl = read_mem_pc_sp(NativeCPC, \
                        _PC++); \
  _WZh = read_mem_pc_sp(NativeCPC, \
                        _PC++); \
  write_mem(NativeCPC, \
            _WZ, \
            Z80->Regs.reg.UChar.l); \
  write_mem(NativeCPC, \
            ++_WZ, \
            Z80->Regs.reg.UChar.h); \
}


/* from Caprice Reloaded r598 */
#define LDD \
{ \
  tULong io = read_mem(NativeCPC, \
                       _HL); \
  write_mem(NativeCPC, \
            _DE, \
            io); \
  _F &= Sflag | Zflag | Cflag; \
	if ((_A + io) & 0x02) _F |= Yflag; \
	if ((_A + io) & 0x08) _F |= Xflag; \
  _HL--; \
  _DE--; \
  _BC--; \
  if(_BC) _F |= Vflag; \
  Z80_FLAGS_UPDATED \
}


#define LDD_ED \
{ \
  LDD; \
  Z80_INC_WS_ADJUST \
}


/* from Caprice Reloaded r598 */
#define LDDR \
{ \
  LDD; \
  if(_BC) \
  { \
    Z80->iCycleCount += cc_ex_lddr; \
    _PC -= 2; \
    _WZ = _PC + 1; \
  } \
}


#define LDDR_ED \
{ \
  LDDR; \
  Z80_INC_WS_ADJUST \
}


/* from Caprice Reloaded r598 */
#define LDI \
{ \
  tULong io = read_mem(NativeCPC, \
                       _HL); \
  write_mem(NativeCPC, \
            _DE, \
            io); \
  _F &= Sflag | Zflag | Cflag; \
	if ((_A + io) & 0x02) _F |= Yflag; \
	if ((_A + io) & 0x08) _F |= Xflag; \
  _HL++; \
  _DE++; \
  _BC--; \
  if(_BC) _F |= Vflag; \
  Z80_FLAGS_UPDATED \
}


#define LDI_ED \
{ \
  LDI; \
  Z80_INC_WS_ADJUST \
}


/* from Caprice Reloaded r598 */
#define LDIR \
{ \
  LDI; \
  if(_BC) \
  { \
    Z80->iCycleCount += cc_ex_ldir; \
    _PC -= 2; \
    _WZ = _PC + 1; \
  } \
}


#define LDIR_ED \
{ \
  LDIR; \
  Z80_INC_WS_ADJUST \
}


#define NEG \
{ \
  tULong value = _A; \
  _A = 0; \
  SUB(value); \
}


#define OR(val) \
{ \
  _A |= val; \
  _F = Z80->SZP[_A]; \
  Z80_FLAGS_UPDATED \
}


#define OR_MI_(index) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  OR(read_mem(NativeCPC, \
              (index+o))); \
}
#define OR_MIX OR_MI_(_IX)
#define OR_MIY OR_MI_(_IY)


#define OTDR \
{ \
  OUTD; \
  if(_B) \
  { \
    Z80->iCycleCount += cc_ex_otdr; \
    _PC -= 2; \
  } \
}


#define OTDR_ED \
{ \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_otdr_; \
  OTDR; \
}

#define OTIR \
{ \
  OUTI; \
  if(_B) \
  { \
    Z80->iCycleCount += cc_ex_otir; \
    _PC -= 2; \
  } \
}


#define OTIR_ED \
{ \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_otir_; \
  OTIR; \
}


#define OUT_C_REG(reg) \
{ \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_out_c_r_; \
  z80_OUT_handler(NativeCPC, \
                  Z80->Regs.BC, \
                  reg); \
}
#define OUT_C_0 OUT_C_REG(0)


#define OUTA \
{ \
  Z80_WAIT_STATES; \
  Z80->iCycleCount = cc_outa_; \
  \
  _WZl = read_mem_pc_sp(NativeCPC, \
                        _PC++); \
  _WZh = _A; \
  z80_OUT_handler(NativeCPC, \
                  Z80->Regs.WZ, \
                  _A); \
}


// From Caprice Reloaded
#define OUTD \
{ \
  tULong io = read_mem(NativeCPC, \
                       _HL); \
  _B--; \
  _WZ = _BC + 1; \
  z80_OUT_handler(NativeCPC, \
                  Z80->Regs.BC, \
                  io); \
  _HL--; \
  _F = Z80->SZ[_B]; \
  if (io & Sflag) _F |= Nflag; \
  if ((_L + io) & 0x100) _F |= Hflag | Cflag; \
  _F |= Z80->SZP[((_L + io) & 0x07) ^ _B] & Pflag; \
  Z80_FLAGS_UPDATED \
}


#define OUTD_ED \
{ \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_outd_; \
  OUTD; \
}


// From Caprice Reloaded
#define OUTI \
{ \
  tULong io = read_mem(NativeCPC, \
                       _HL); \
  _B--; \
  _WZ = _BC + 1; \
  z80_OUT_handler(NativeCPC, \
                  Z80->Regs.BC, \
                  io); \
  _HL++; \
  _F = Z80->SZ[_B]; \
  if (io & Sflag) _F |= Nflag; \
  /* From Caprice Reloaded */ \
  if ((_L + io) & 0x100) _F |= Hflag | Cflag; \
  /* From Caprice Reloaded */ \
  _F |= Z80->SZP[((_L + io) & 0x07) ^ _B] & Pflag; \
  Z80_FLAGS_UPDATED \
}


#define OUTI_ED \
{ \
  Z80_WAIT_STATES \
  Z80->iCycleCount = cc_outi_; \
  OUTI; \
}


#define POP(reg) \
{ \
  Z80->Regs.reg.UChar.l = read_mem_pc_sp(NativeCPC, \
                                         _SP++); \
  Z80->Regs.reg.UChar.h = read_mem_pc_sp(NativeCPC, \
                                         _SP++); \
}


#define PUSH(reg) \
{ \
  write_mem(NativeCPC, \
            --_SP, \
            Z80->Regs.reg.UChar.h); \
  write_mem(NativeCPC, \
            --_SP, \
            Z80->Regs.reg.UChar.l); \
}


INLINE tULong RES(tULong bit,
                  tULong val)
{
  return ( val
           & ~(1 << bit));
}


#define RES_MHL_ADDR(bit, addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            RES(bit, byte)); \
}
#define RES_MHL(bit) RES_MHL_ADDR(bit, _HL)


#define RES_REG_ADDR(reg, addr, bit) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            reg = RES(bit, byte)); \
}
#define RES_A_ADDR(addr, bit) RES_REG_ADDR(_A, addr, bit)
#define RES_B_ADDR(addr, bit) RES_REG_ADDR(_B, addr, bit)
#define RES_C_ADDR(addr, bit) RES_REG_ADDR(_C, addr, bit)
#define RES_D_ADDR(addr, bit) RES_REG_ADDR(_D, addr, bit)
#define RES_E_ADDR(addr, bit) RES_REG_ADDR(_E, addr, bit)
#define RES_H_ADDR(addr, bit) RES_REG_ADDR(_H, addr, bit)
#define RES_L_ADDR(addr, bit) RES_REG_ADDR(_L, addr, bit)


/* from Caprice Reloaded r598 */
#define RET \
{ \
  _WZl = read_mem_pc_sp(NativeCPC, \
                        _SP++); \
  _WZh = read_mem_pc_sp(NativeCPC, \
                        _SP++); \
  _PC = _WZ; \
}
#define RET_C  { if (_F & Cflag)    { Z80->iCycleCount += cc_ex_ret_c;  RET; } else { Z80_INC_WS_ADJUST } }
#define RET_M  { if (_F & Sflag)    { Z80->iCycleCount += cc_ex_ret_m;  RET; } else { Z80_INC_WS_ADJUST } }
#define RET_NC { if (!(_F & Cflag)) { Z80->iCycleCount += cc_ex_ret_nc; RET; } else { Z80_INC_WS_ADJUST } }
#define RET_NZ { if (!(_F & Zflag)) { Z80->iCycleCount += cc_ex_ret_nz; RET; } else { Z80_INC_WS_ADJUST } }
#define RET_P  { if (!(_F & Sflag)) { Z80->iCycleCount += cc_ex_ret_p;  RET; } else { Z80_INC_WS_ADJUST } }
#define RET_PE { if (_F & Pflag)    { Z80->iCycleCount += cc_ex_ret_pe; RET; } else { Z80_INC_WS_ADJUST } }
#define RET_PO { if (!(_F & Pflag)) { Z80->iCycleCount += cc_ex_ret_po; RET; } else { Z80_INC_WS_ADJUST } }
#define RET_Z  { if (_F & Zflag)    { Z80->iCycleCount += cc_ex_ret_z;  RET; } else { Z80_INC_WS_ADJUST } }


INLINE tULong RL(tZ80* Z80,
                 tULong val)
{
tULong res = val;
tULong carry = (res & 0x80) >> 7; // Cflag

  res = ( (res << 1)
          | (_F & Cflag) )
        & 0xff;
  _F = Z80->SZP[res]
       | carry;

  Z80_FLAGS_UPDATED

  return (res);
}


#define RL_MHL_ADDR(addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            RL(Z80, byte)); \
}
#define RL_MHL RL_MHL_ADDR(_HL)


#define RL_REG_ADDR(reg, addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            reg = RL(Z80, byte)); \
}
#define RL_A_ADDR(addr) RL_REG_ADDR(_A, addr)
#define RL_B_ADDR(addr) RL_REG_ADDR(_B, addr)
#define RL_C_ADDR(addr) RL_REG_ADDR(_C, addr)
#define RL_D_ADDR(addr) RL_REG_ADDR(_D, addr)
#define RL_E_ADDR(addr) RL_REG_ADDR(_E, addr)
#define RL_H_ADDR(addr) RL_REG_ADDR(_H, addr)
#define RL_L_ADDR(addr) RL_REG_ADDR(_L, addr)


// From Caprice Reloaded
#define RLA \
{ \
  tULong res = (_A << 1) \
               | (_F & Cflag); \
  tUChar carry = (_A & 0x80) ? Cflag : 0; \
  _F = (_F & (Sflag | Zflag | Pflag)) \
       | carry \
       | (res & Xflags); \
  _A = res; \
  Z80_FLAGS_UPDATED \
}


INLINE tULong RLC(tZ80* Z80,
                  tULong val)
{
tULong res = val;
tULong carry = (res & 0x80) >> 7; // Cflag

  res = ( (res << 1)
          | (res >> 7) )
        & 0xff;
  _F = Z80->SZP[res]
       | carry;

  Z80_FLAGS_UPDATED

  return (res);
}

#define RLC_MHL_ADDR(addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            RLC(Z80, byte)); \
}
#define RLC_MHL RLC_MHL_ADDR(_HL)


#define RLC_REG_ADDR(reg, addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                         addr); \
  write_mem(NativeCPC, \
            addr, \
            reg = RLC(Z80, byte)); \
}
#define RLC_A_ADDR(addr) RLC_REG_ADDR(_A, addr)
#define RLC_B_ADDR(addr) RLC_REG_ADDR(_B, addr)
#define RLC_C_ADDR(addr) RLC_REG_ADDR(_C, addr)
#define RLC_D_ADDR(addr) RLC_REG_ADDR(_D, addr)
#define RLC_E_ADDR(addr) RLC_REG_ADDR(_E, addr)
#define RLC_H_ADDR(addr) RLC_REG_ADDR(_H, addr)
#define RLC_L_ADDR(addr) RLC_REG_ADDR(_L, addr)


#define RLCA \
{ \
  _A = (_A << 1) \
       | (_A >> 7); \
  _F = (_F & (Sflag | Zflag | Pflag)) \
       | (_A & (Xflags | Cflag)); \
  Z80_FLAGS_UPDATED \
}


/* from Caprice Reloaded r598 */
#define RLD \
{ \
tULong n = read_mem(NativeCPC, \
                    _HL); \
  _WZ = _HL + 1; \
  write_mem(NativeCPC, \
            _HL, \
            (n << 4) | (_A & 0x0f)); \
  _A = (_A & 0xf0) \
       | (n >> 4); \
  _F = (_F & Cflag) \
       | Z80->SZP[_A]; \
  Z80_FLAGS_UPDATED \
}


INLINE tULong RR(tZ80* Z80,
                 tULong val)
{
tULong res = val;
tULong carry = res & Cflag; // Cflag

  res = ( (res >> 1)
          | (_F << 7) )
        & 0xff;
  _F = Z80->SZP[res]
       | carry;

  Z80_FLAGS_UPDATED

  return (res);
}


#define RR_MHL_ADDR(addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            RR(Z80, byte)); \
}
#define RR_MHL RR_MHL_ADDR(_HL);


#define RR_REG_ADDR(reg, addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                         addr); \
  write_mem(NativeCPC, \
            addr, \
            reg = RR(Z80, byte)); \
}
#define RR_A_ADDR(addr) RR_REG_ADDR(_A, addr)
#define RR_B_ADDR(addr) RR_REG_ADDR(_B, addr)
#define RR_C_ADDR(addr) RR_REG_ADDR(_C, addr)
#define RR_D_ADDR(addr) RR_REG_ADDR(_D, addr)
#define RR_E_ADDR(addr) RR_REG_ADDR(_E, addr)
#define RR_H_ADDR(addr) RR_REG_ADDR(_H, addr)
#define RR_L_ADDR(addr) RR_REG_ADDR(_L, addr)


#define RRA \
{ \
  tULong res = (_A >> 1) \
               | (_F << 7); \
  tUChar carry = (_A & 0x01) ? Cflag : 0; \
  _F = (_F & (Sflag | Zflag | Pflag)) \
       | carry \
       | (res & Xflags); \
  _A = res; \
  Z80_FLAGS_UPDATED \
}


#define RRCA \
{ \
  _F = (_F & (Sflag | Zflag | Pflag)) \
       | (_A & Cflag); \
  _A = (_A >> 1) \
       | (_A << 7); \
  _F |= (_A & Xflags); \
  Z80_FLAGS_UPDATED \
}


INLINE tULong RRC(tZ80* Z80,
                  tULong val)
{
tULong res = val;
tULong carry = res & Cflag; // Cflag

  res = ( (res >> 1)
          | (res << 7) )
        & 0xff;
  _F = Z80->SZP[res]
       | carry;

  Z80_FLAGS_UPDATED

  return (res);
}


#define RRC_MHL_ADDR(addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            RRC(Z80, byte)); \
}
#define RRC_MHL RRC_MHL_ADDR(_HL);


#define RRC_REG_ADDR(reg, addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                         addr); \
  write_mem(NativeCPC, \
            addr, \
            reg = RRC(Z80, byte)); \
}
#define RRC_A_ADDR(addr) RRC_REG_ADDR(_A, addr)
#define RRC_B_ADDR(addr) RRC_REG_ADDR(_B, addr)
#define RRC_C_ADDR(addr) RRC_REG_ADDR(_C, addr)
#define RRC_D_ADDR(addr) RRC_REG_ADDR(_D, addr)
#define RRC_E_ADDR(addr) RRC_REG_ADDR(_E, addr)
#define RRC_H_ADDR(addr) RRC_REG_ADDR(_H, addr)
#define RRC_L_ADDR(addr) RRC_REG_ADDR(_L, addr)


/* from Caprice Reloaded r598 */
#define RRD \
{ \
tULong n = read_mem(NativeCPC, \
                    _HL); \
  _WZ = _HL + 1; \
  write_mem(NativeCPC, \
            _HL, \
            (n >> 4) | (_A << 4)); \
  _A = (_A & 0xf0) \
       | (n & 0x0f); \
  _F = (_F & Cflag) \
       | Z80->SZP[_A]; \
  Z80_FLAGS_UPDATED \
}


/* from Caprice Reloaded r598 */
#define RST(addr) \
{ \
  write_mem(NativeCPC, \
            --_SP, \
            Z80->Regs.PC.UChar.h); /* store high byte of current PC */ \
  write_mem(NativeCPC, \
            --_SP, \
            Z80->Regs.PC.UChar.l); /* store low byte of current PC */ \
  _PC = addr; /* continue execution at restart address */ \
  _WZ = addr; \
}


// From MAME: Portable Z80 emulator V3.9
#define SBC(value) \
{ \
  tULong ah = _AF & 0xff00; \
  tULong c = _F & Cflag; \
  tULong res = (tUChar)((ah >> 8) - value - c); \
  _F = Z80->SZHVC_sub[(c << 16) | ah | res]; \
  _A = res; \
  Z80_FLAGS_UPDATED \
}


#define SBC_MXY(index) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  SBC(read_mem(NativeCPC, \
               (index+o))); \
}
#define SBC_MIX SBC_MXY(_IX)
#define SBC_MIY SBC_MXY(_IY)


// From Caprice Reloaded
#define SBC16(reg) \
{ \
  tULong res = _HLdword - Z80->Regs.reg.ULong - (_F & Cflag); \
  _WZdword = _HLdword + 1; \
  _F = (((_HLdword ^ res ^ Z80->Regs.reg.ULong) >> 8) & Hflag) \
       | Nflag \
       | ((res >> 16) & Cflag) \
       | ((res >> 8) & (Sflag | Xflags)) \
       | ((res & 0xffff) ? 0 : Zflag) \
       | (((Z80->Regs.reg.ULong ^ _HLdword) & (_HLdword ^ res) & 0x8000) >> 13); \
  _HL = res; \
  Z80_FLAGS_UPDATED \
}


#define SCF \
{ \
  if (Z80->Regs.flags2) \
  { \
    _F = (_F & (Sflag | Zflag | Pflag)) \
         | Cflag \
         | (_A & Xflags); \
  } \
  else \
  { \
    _F = (_F & (Sflag | Zflag | Pflag | Xflags)) \
         | Cflag \
         | (_A & Xflags); \
  } \
  Z80_FLAGS_UPDATED \
}


INLINE tULong SET(tULong bit,
                  tULong val)
{
  return (val | (1 << bit));
}


#define SET_MHL_ADDR(bit, addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            SET(bit, byte)); \
}
#define SET_MHL(bit) SET_MHL_ADDR(bit, _HL)


#define SET_REG_ADDR(reg, addr, bit) \
{ \
  tULong byte = read_mem(NativeCPC, \
                         addr); \
  write_mem(NativeCPC, \
            addr, \
            reg = SET(bit, byte)); \
}
#define SET_A_ADDR(addr, bit) SET_REG_ADDR(_A, addr, bit)
#define SET_B_ADDR(addr, bit) SET_REG_ADDR(_B, addr, bit)
#define SET_C_ADDR(addr, bit) SET_REG_ADDR(_C, addr, bit)
#define SET_D_ADDR(addr, bit) SET_REG_ADDR(_D, addr, bit)
#define SET_E_ADDR(addr, bit) SET_REG_ADDR(_E, addr, bit)
#define SET_H_ADDR(addr, bit) SET_REG_ADDR(_H, addr, bit)
#define SET_L_ADDR(addr, bit) SET_REG_ADDR(_L, addr, bit)


INLINE tULong SLA(tZ80* Z80,
                  tULong val)
{
tULong res = val;
tULong carry = (res & 0x80) >> 7; // Cflag

  res = (res << 1)
        & 0xff;
  _F = Z80->SZP[res]
       | carry;

  Z80_FLAGS_UPDATED

  return (res);
}


#define SLA_MHL_ADDR(addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            SLA(Z80, byte)); \
}
#define SLA_MHL SLA_MHL_ADDR(_HL)


#define SLA_REG_ADDR(reg, addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                         addr); \
  write_mem(NativeCPC, \
            addr, \
            reg = SLA(Z80, byte)); \
}
#define SLA_A_ADDR(addr) SLA_REG_ADDR(_A, addr)
#define SLA_B_ADDR(addr) SLA_REG_ADDR(_B, addr)
#define SLA_C_ADDR(addr) SLA_REG_ADDR(_C, addr)
#define SLA_D_ADDR(addr) SLA_REG_ADDR(_D, addr)
#define SLA_E_ADDR(addr) SLA_REG_ADDR(_E, addr)
#define SLA_H_ADDR(addr) SLA_REG_ADDR(_H, addr)
#define SLA_L_ADDR(addr) SLA_REG_ADDR(_L, addr)


INLINE tULong SLL(tZ80* Z80,
                  tULong val)
{
tULong res = val;
tULong carry = (res & 0x80) >> 7; // Cflag

  res = ( (res << 1)
          | 0x01 )
        & 0xff;
  _F = Z80->SZP[res]
       | carry;

  Z80_FLAGS_UPDATED

  return (res);
}


#define SLL_MHL_ADDR(addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            SLL(Z80, byte)); \
}
#define SLL_MHL SLL_MHL_ADDR(_HL)


#define SLL_REG_ADDR(reg, addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                         addr); \
  write_mem(NativeCPC, \
            addr, \
            reg = SLL(Z80, byte)); \
}
#define SLL_A_ADDR(addr) SLL_REG_ADDR(_A, addr)
#define SLL_B_ADDR(addr) SLL_REG_ADDR(_B, addr)
#define SLL_C_ADDR(addr) SLL_REG_ADDR(_C, addr)
#define SLL_D_ADDR(addr) SLL_REG_ADDR(_D, addr)
#define SLL_E_ADDR(addr) SLL_REG_ADDR(_E, addr)
#define SLL_H_ADDR(addr) SLL_REG_ADDR(_H, addr)
#define SLL_L_ADDR(addr) SLL_REG_ADDR(_L, addr)


INLINE tULong SRA(tZ80* Z80,
                  tULong val)
{
tULong res = val;
tULong carry = res & Cflag;

  res = ( (res >> 1)
          | (res & 0x80) )
        & 0xff;
  _F = Z80->SZP[res]
       | carry;

  Z80_FLAGS_UPDATED

  return (res);
}


#define SRA_MHL_ADDR(addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            SRA(Z80, byte)); \
}
#define SRA_MHL SRA_MHL_ADDR(_HL)


#define SRA_REG_ADDR(reg, addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                         addr); \
  write_mem(NativeCPC, \
            addr, \
            reg = SRA(Z80, byte)); \
}
#define SRA_A_ADDR(addr) SRA_REG_ADDR(_A, addr)
#define SRA_B_ADDR(addr) SRA_REG_ADDR(_B, addr)
#define SRA_C_ADDR(addr) SRA_REG_ADDR(_C, addr)
#define SRA_D_ADDR(addr) SRA_REG_ADDR(_D, addr)
#define SRA_E_ADDR(addr) SRA_REG_ADDR(_E, addr)
#define SRA_H_ADDR(addr) SRA_REG_ADDR(_H, addr)
#define SRA_L_ADDR(addr) SRA_REG_ADDR(_L, addr)


INLINE tULong SRL(tZ80* Z80,
                  tULong val)
{
tULong res = val;
tULong carry = res & Cflag;

  res = (res >> 1) 
        & 0xff;
  _F = Z80->SZP[res]
       | carry;

  Z80_FLAGS_UPDATED

  return (res);
}


#define SRL_MHL_ADDR(addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                            addr); \
  write_mem(NativeCPC, \
            addr, \
            SRL(Z80, byte)); \
}
#define SRL_MHL SRL_MHL_ADDR(_HL)


#define SRL_REG_ADDR(reg, addr) \
{ \
  tULong byte = read_mem(NativeCPC, \
                         addr); \
  write_mem(NativeCPC, \
            addr, \
            reg = SRL(Z80, byte)); \
}
#define SRL_A_ADDR(addr) SRL_REG_ADDR(_A, addr)
#define SRL_B_ADDR(addr) SRL_REG_ADDR(_B, addr)
#define SRL_C_ADDR(addr) SRL_REG_ADDR(_C, addr)
#define SRL_D_ADDR(addr) SRL_REG_ADDR(_D, addr)
#define SRL_E_ADDR(addr) SRL_REG_ADDR(_E, addr)
#define SRL_H_ADDR(addr) SRL_REG_ADDR(_H, addr)
#define SRL_L_ADDR(addr) SRL_REG_ADDR(_L, addr)


// From MAME: Portable Z80 emulator V3.9
#define SUB(value) \
{ \
  tULong ah = _AF & 0xff00; \
  tULong res = (tUChar)((ah >> 8) - value); \
  _F = Z80->SZHVC_sub[ah | res]; \
  _A = res; \
  Z80_FLAGS_UPDATED \
}


#define SUB_MXY(index) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  SUB(read_mem(NativeCPC, \
               (index+o))); \
}
#define SUB_MIX SUB_MXY(_IX)
#define SUB_MIY SUB_MXY(_IY)


#define XOR(val) \
{ \
  _A ^= val; \
  _F = Z80->SZP[_A]; \
  Z80_FLAGS_UPDATED \
}


#define XOR_MXY(index) \
{ \
  tLong o = signed_read_mem_pc(NativeCPC, \
                               _PC++); \
  XOR(read_mem(NativeCPC, \
               (index+o))); \
}
#define XOR_MIX XOR_MXY(_IX)
#define XOR_MIY XOR_MXY(_IY)


// ==================================================================================

#ifndef USE_Z80_TIMING_ADJUST
  #define Z80_NMI_CYCLECOUNT      Z80->iCycleCount = Z80_NMI_CC;
  #define Z80_IM1_CYCLECOUNT      Z80->iCycleCount = Z80_IM1_CC;
  #define Z80_IM2_CYCLECOUNT      Z80->iCycleCount = Z80_IM2_CC;
#else /* USE_Z80_TIMING_ADJUST */
  #define Z80_NMI_CYCLECOUNT      Z80->iCycleCount = Z80->cc_nmi;
  #define Z80_IM1_CYCLECOUNT      Z80->iCycleCount = Z80->cc_im1;
  #define Z80_IM2_CYCLECOUNT      Z80->iCycleCount = Z80->cc_im2;
#endif /* USE_Z80_TIMING_ADJUST */

#define Z80_NMI_HANDLER \
{ \
  if (_HALT) /* HALT instruction active? */ \
  { \
    _HALT = 0; /* exit HALT 'loop' */ \
    _PC++; /* correct PC */ \
  } \
  \
  _R++; \
  _IFF1 = 0; /* clear interrupt flip-flops */ \
  Z80->Regs.nmi_pending = 0; \
  \
  Z80_NMI_CYCLECOUNT \
  Z80_CHECK_WS_ADJUST \
  RST(0x0066); \
  _WZ = _PC; /* from Caprice Reloaded r598 */\
  Z80_WAIT_STATES \
}


#define Z80_INT_HANDLER \
{ \
  if (_IFF1) /* process interrupts? */ \
  { \
    _R++; \
    _IFF1 = _IFF2 = 0; /* clear interrupt flip-flops */ \
    Z80->Regs.int_pending = 0; \
    NativeCPC->GateArray->sl_count &= 0x1f; /* clear bit 5 of GA scanline counter */ \
    \
    if (_HALT) /* HALT instruction active? */ \
    { \
      _HALT = 0; /* exit HALT 'loop' */ \
      _PC++; /* correct PC */ \
    } \
    \
    if (_IM < 2) /* interrupt mode 0 or 1? (IM0 = IM1 on the CPC) */ \
    { \
      Z80_IM1_CYCLECOUNT \
      Z80_CHECK_WS_ADJUST \
      RST(0x0038); \
    } \
    else /* interrupt mode 2 */ \
    { \
      tRegister addr; \
      Z80_IM2_CYCLECOUNT \
      Z80_CHECK_WS_ADJUST \
      write_mem(NativeCPC, \
                --_SP, \
                Z80->Regs.PC.UChar.h); /* store high byte of current PC */ \
      write_mem(NativeCPC, \
                --_SP, \
                Z80->Regs.PC.UChar.l); /* store low byte of current PC */ \
      addr.UChar.l = 0xff; /* assemble pointer */ \
      addr.UChar.h = _I; \
      Z80->Regs.PC.UChar.l = read_mem(NativeCPC, \
                                      addr.UShort.l); /* retrieve low byte of vector */ \
      Z80->Regs.PC.UChar.h = read_mem(NativeCPC, \
                                      addr.UShort.l+1); /* retrieve high byte of vector */ \
    } \
    \
    _WZ = _PC; /* from Caprice Reloaded r598 */\
    Z80_WAIT_STATES \
  }\
}

// ==================================================================================


tVoid Engine_GAMemoryManager(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  Engine_GAMemoryManager
 *
 ***********************************************************************/
{
  ga_memory_manager(NativeCPC);
}

tUChar Engine_Z80_IN_Handler(tNativeCPC* NativeCPC, tRegister port)
/***********************************************************************
 *
 *  Engine_Z80_IN_Handler
 *
 ***********************************************************************/
{
  return (z80_IN_handler(NativeCPC, port));
}

/*----------------------------------------------------------------------------*/
tVoid Engine_Z80_OUT_Handler(tNativeCPC* NativeCPC, tRegister port, tULong val)
/***********************************************************************
 *
 *  Engine_Z80_OUT_Handler
 *
 ***********************************************************************/
{
  z80_OUT_handler(NativeCPC, port, val);
}
/*----------------------------------------------------------------------------*/




//
// CAUTION : addr MUST be 16-bits
//
INLINE tUChar read_mem(tNativeCPC* NativeCPC,
                       tULong addr)
/***********************************************************************
 *
 *  read_mem
 *
 ***********************************************************************/
{
	NativeCPC->memread_addr = addr;
	NativeCPC->memdata = *(NativeCPC->membank_read[(addr >> 14) & 3] + (addr & 0x3fff));
	return (NativeCPC->memdata); // returns a byte from a 16KB memory bank
}

INLINE tUChar read_mem_pc_sp(tNativeCPC* NativeCPC,
														 tULong addr)
/***********************************************************************
 *
 *  read_mem_pc_sp
 *
 ***********************************************************************/
{
	return (*(NativeCPC->membank_read[(addr >> 14) & 3] + (addr & 0x3fff))); // returns a byte from a 16KB memory bank
}


//
// CAUTION : addr MUST be 16-bits
//
INLINE tChar signed_read_mem(tNativeCPC* NativeCPC,
														 tULong addr)
/***********************************************************************
 *
 *  signed_read_mem
 *
 ***********************************************************************/
{
	NativeCPC->memread_addr = addr;
	NativeCPC->memdata = *(NativeCPC->membank_read[(addr >> 14) & 3] + (addr & 0x3fff));
	return (NativeCPC->memdata); // returns a byte from a 16KB memory bank
}

INLINE tChar signed_read_mem_pc(tNativeCPC* NativeCPC,
                                tULong addr)
/***********************************************************************
 *
 *  signed_read_mem_pc
 *
 ***********************************************************************/
{
  return (*(NativeCPC->membank_read[(addr >> 14) & 3] + (addr & 0x3fff))); // returns a byte from a 16KB memory bank
}


//
// CAUTION : addr MUST be 16-bits
//
INLINE tVoid write_mem(tNativeCPC* NativeCPC,
                       tULong addr,
                       tUChar val)
/***********************************************************************
 *
 *  write_mem
 *
 ***********************************************************************/
{
	NativeCPC->memwrite_addr = addr;
	NativeCPC->memdata = val;
	*(NativeCPC->membank_write[(addr >> 14) & 3] + (addr & 0x3fff)) = val; // writes a byte to a 16KB memory bank
}

INLINE tVoid write_mem_sp(tNativeCPC* NativeCPC,
                          tULong addr,
													tUChar val)
/***********************************************************************
 *
 *  write_mem_sp
 *
 ***********************************************************************/
{
  *(NativeCPC->membank_write[(addr >> 14) & 3] + (addr & 0x3fff)) = val; // writes a byte to a 16KB memory bank
}


// ==================================================================================

#ifdef PSG_AY_3_8910
  #define PSG_WRITE_PSG_VALID_REG  if (PSG->reg_select < 16)
#else /* PSG_AY_3_8910 */
  #define PSG_WRITE_PSG_VALID_REG  if (PSG->reg_select < 14)
#endif /* PSG_AY_3_8910 */

#define PSG_WRITE \
{ \
  tULong control; \
  \
  control = PSG->control & 0xc0; /* isolate PSG control bits */ \
  if (control == 0xc0) /* latch address? */ \
  { \
    PSG->reg_select = psg_data; /* select new PSG register */ \
  } \
  else if (control == 0x80) /* write? */ \
  { \
    PSG_WRITE_PSG_VALID_REG /* valid register? */ \
    { \
      audio_set_AY_Register(PSG, \
                            PSG->reg_select, \
                            psg_data); \
    } \
  } \
}

// ==================================================================================

#define LOAD_RESULT_WITH_STATUS \
{ \
  FDC->result[RES_ST0] |= 0x40; /* AT */ \
  FDC->result[RES_ST1] |= 0x80; /* End of Cylinder */ \
  \
  if (FDC->command[CMD_CODE] != 0x42) /* continue only if not a read track command */ \
  { \
    if ((FDC->result[RES_ST1] & 0x7f) || (FDC->result[RES_ST2] & 0x7f)) /* any 'error bits' set? */ \
    { \
      FDC->result[RES_ST1] &= 0x7f; /* mask out End of Cylinder */ \
      \
      if ((FDC->result[RES_ST1] & 0x20) || (FDC->result[RES_ST2] & 0x20)) /* DE and/or DD? */ \
      { \
        FDC->result[RES_ST2] &= 0xbf; /* mask out Control Mark */ \
      } \
      else if (FDC->result[RES_ST2] & 0x40) /* Control Mark? */ \
      { \
        FDC->result[RES_ST0] &= 0x3f; /* mask out AT */ \
        FDC->result[RES_ST1] &= 0x7f; /* mask out End of Cylinder */ \
      } \
    } \
  } \
}


#define LOAD_RESULT_WITH_CHRN \
{ \
   FDC->result[RES_C] = FDC->command[CMD_C]; /* load result with current CHRN values */ \
   FDC->result[RES_H] = FDC->command[CMD_H]; \
   FDC->result[RES_R] = FDC->command[CMD_R]; \
   FDC->result[RES_N] = FDC->command[CMD_N]; \
}



#if defined(__WIN32__) || defined(_WIN64)
tULong Engine_FirstCPCExecute(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  Engine_FirstCPCExecute
 *
 ***********************************************************************/
{
#ifdef _TESTU
tULong Result;
#endif /* _TESTU */

  fdc_init(NativeCPC);
  video_init(NativeCPC);
  audio_init(NativeCPC);
  ga_init(NativeCPC);

#if defined(__WIN32__) || defined(_WIN64)
  NativeCPC->Z80OUTHandlerPtr = z80_OUT_handler;
	NativeCPC->SetAYRegisterPtr = (tSetAYRegisterPtr)audio_set_AY_Register;
#ifdef USE_XMEM
	if (NativeCPC->XMEM_SST)
	{
		NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble;
	}
	else
	{
		NativeCPC->XMEMPtr = XMEM_Winbond_Detect_Preamble;
	}
#endif /* USE_XMEM */
#endif /* __WIN32__ || _WIN64 */

#ifdef USE_Z80_TIMING_ADJUST
  {
    // Copy const Z80 timings
    tULong Loop;

    for (Loop=0; Loop < 256; Loop++)
    {
      NativeCPC->Z80->cc_op[Loop] = cc_op[Loop];
      NativeCPC->Z80->cc_cb[Loop] = cc_cb[Loop];
      NativeCPC->Z80->cc_ed[Loop] = cc_ed[Loop];
      NativeCPC->Z80->cc_xy[Loop] = cc_xy[Loop];
      NativeCPC->Z80->cc_xycb[Loop] = cc_xycb[Loop];
    }
    NativeCPC->Z80->cc_nmi = Z80_NMI_CC;
    NativeCPC->Z80->cc_im1 = Z80_IM1_CC;
    NativeCPC->Z80->cc_im2 = Z80_IM2_CC;
  }
#endif /* USE_Z80_TIMING_ADJUST */

#ifdef _TESTU
  Result = PerformTestU(NativeCPC);
  if (Result != errNone)
  {
    return (Result); // within EC_ERROR_MASK
  }
#endif /* _TESTU */

  return (errNone);
}
#endif /* __WIN32__ || _WIN64 */


/***********************************************************************
 *
 *  Entry Points
 *
 ***********************************************************************/

#if defined(__PALMOS__)
tULong PNOMain(const tVoid* emulStateP,
               tVoid* userData68KP,
               Call68KFuncType* call68KFuncP)
#elif defined(__WIN32__) || defined(_WIN64)
tULong Engine_CPCExecute(tNativeCPC* NativeCPC)
#endif /* __PALMOS__ || __WIN32__ || _WIN64 */
/***********************************************************************
 *
 *  PNOMain
 *
 ***********************************************************************/
{
#ifdef __PALMOS__
tNativeCPC* NativeCPC = (tNativeCPC*)userData68KP;
#endif /* __PALMOS__ */
tULong ExitConditions;
tZ80* Z80 = NativeCPC->Z80;
tPSG* PSG = NativeCPC->PSG;
tFDC* FDC = NativeCPC->FDC;
tVDU* VDU = NativeCPC->VDU;
#ifdef _TESTU
tULong Result;
#endif /* _TESTU */
tUChar bOpCode;

#ifdef __PALMOS__
  NOT_USED(emulStateP);
  NOT_USED(call68KFuncP);
#endif /* __PALMOS__ */

#ifdef __PALMOS__
  // First launch
  if (NativeCPC->FirstInitToPerform)
  {
#ifdef _TESTU
    Result = PerformTestU(NativeCPC);
    if (Result != errNone)
    {
      return (Result); // within EC_ERROR_MASK
    }
#endif /* _TESTU */

    fdc_init(NativeCPC);
    video_init(NativeCPC);
    audio_init(NativeCPC);
    ga_init(NativeCPC);

#if defined(__WIN32__) || defined (_WIN64)
    NativeCPC->Z80OUTHandlerPtr = z80_OUT_handler;
    NativeCPC->SetAYRegisterPtr = (tSetAYRegisterPtr)audio_set_AY_Register;
#endif /* __WIN32__ || _WIN64 */

    NativeCPC->FirstInitToPerform = 0;
  }
#endif /* __PALMOS__ */


#ifdef __PALMOS__
  // Initialisation after Restoration
  if (NativeCPC->RestorationPerformed)
  {
    fdc_init(NativeCPC);
    video_init(NativeCPC);
    ga_memory_manager(NativeCPC);

    audio_set_case_env(PSG);

    NativeCPC->RestorationPerformed = 0;
  }
#endif /* __PALMOS__ */

#ifdef ENABLE_MULTIFACE2
  if (NativeCPC->mf2_stop_pressed)
  {
    NativeCPC->mf2_stop_pressed = 0;

    if ( (NativeCPC->pbMF2ROM) && (!(NativeCPC->mf2_flags & MF2_ACTIVE)) )
    {
      // Switch to Multiface 2 memory
      NativeCPC->membank_read[0] = NativeCPC->pbMF2ROM;
      NativeCPC->membank_write[0] = NativeCPC->pbMF2ROM;

      NativeCPC->mf2_exit_stack = _SPdword;
      NativeCPC->mf2_flags = MF2_ACTIVE | MF2_RUNNING;
      
      // MF2 stop button causes a Z80 NMI
      Z80_NMI_HANDLER
      Z80_WAIT_STATES

			// Breakpoints
			if (NativeCPC->nb_breakpoints)
			{
				if (IsBreakpointDetected(NativeCPC))
				{
					return EC_BREAKPOINT;
				}
			}
		}
  }
#endif /* ENABLE_MULTIFACE2 */

  do
  {
#ifdef ENABLE_MULTIFACE2
    if (NativeCPC->mf2_flags & MF2_RUNNING)
    {
      // have we returned from the MF2?
      if (_SPdword == NativeCPC->mf2_exit_stack)
      {
        // clear running flag and make the MF2 'invisible'
        NativeCPC->mf2_flags = MF2_INVISIBLE;
      }
    }
#endif /* ENABLE_MULTIFACE2 */

    _PrevPC = _PC;
    
    bOpCode = read_mem(NativeCPC,
                       _PC++);
#ifndef USE_Z80_TIMING_ADJUST
    Z80->iCycleCount = cc_op[bOpCode];
#else /* USE_Z80_TIMING_ADJUST */
    Z80->iCycleCount = Z80->cc_op[bOpCode];
#endif /* USE_Z80_TIMING_ADJUST*/
    _R++;

    Z80->Regs.flags2 = Z80->Regs.flags1;
    Z80->Regs.flags1 = 0;

    NativeCPC->memread_addr = BREAKPOINT_INVALID;
    NativeCPC->memwrite_addr = BREAKPOINT_INVALID;

    switch (bOpCode)
    {
      case adc_a:       ADC(_A); break;
      case adc_b:       ADC(_B); break;
      case adc_byte:    ADC(read_mem(NativeCPC, _PC++)); break;
      case adc_c:       ADC(_C); break;
      case adc_d:       ADC(_D); break;
      case adc_e:       ADC(_E); break;
      case adc_h:       ADC(_H); break;
      case adc_l:       ADC(_L); break;
      case adc_mhl:     ADC(read_mem(NativeCPC, _HL)); break;
      case add_a:       ADD(_A); break;
      case add_b:       ADD(_B); break;
      case add_byte:    ADD(read_mem(NativeCPC, _PC++)); break;
      case add_c:       ADD(_C); break;
      case add_d:       ADD(_D); break;
      case add_e:       ADD(_E); break;
      case add_h:       ADD(_H); break;
      case add_hl_bc:   ADD16(HL, BC); break;
      case add_hl_de:   ADD16(HL, DE); break;
      case add_hl_hl:   ADD16(HL, HL); break;
      case add_hl_sp:   ADD16(HL, SP); break;
      case add_l:       ADD(_L); break;
      case add_mhl:     ADD(read_mem(NativeCPC, _HL)); break;
      case and_a:       AND(_A); break;
      case and_b:       AND(_B); break;
      case and_byte:    AND(read_mem(NativeCPC, _PC++)); break;
      case and_c:       AND(_C); break;
      case and_d:       AND(_D); break;
      case and_e:       AND(_E); break;
      case and_h:       AND(_H); break;
      case and_l:       AND(_L); break;
      case and_mhl:     AND(read_mem(NativeCPC, _HL)); break;
      case call:        CALL; break;
      case call_c:      CALL_C;  break;
      case call_m:      CALL_M;  break;
      case call_nc:     CALL_NC; break;
      case call_nz:     CALL_NZ;  break;
      case call_p:      CALL_P;  break;
      case call_pe:     CALL_PE;  break;
      case call_po:     CALL_PO;  break;
      case call_z:      CALL_Z;  break;
      case ccf:         CCF; break;
      case cpl:         CPL; break;
      case cp_a:        CP(_A); break;
      case cp_b:        CP(_B); break;
      case cp_byte:     CP(read_mem(NativeCPC, _PC++)); break;
      case cp_c:        CP(_C); break;
      case cp_d:        CP(_D); break;
      case cp_e:        CP(_E); break;
      case cp_h:        CP(_H); break;
      case cp_l:        CP(_L); break;
      case cp_mhl:      CP(read_mem(NativeCPC, _HL)); break;
      case daa:         DAA; break;
      case dec_a:       DEC(_A); break;
      case dec_b:       DEC(_B); break;
      case dec_bc:      DEC_PAIR(_BC); break;
      case dec_c:       DEC(_C); break;
      case dec_d:       DEC(_D); break;
      case dec_de:      DEC_PAIR(_DE); break;
      case dec_e:       DEC(_E); break;
      case dec_h:       DEC(_H); break;
      case dec_hl:      DEC_PAIR(_HL); break;
      case dec_l:       DEC(_L); break;
      case dec_mhl:     DEC_MHL; break;
      case dec_sp:      DEC_PAIR(_SP); break;
      case di:          DI; break;
      case djnz:        DJNZ;  break;
      case ei:          EI; break;
      case exx:         EXX; break;
      case ex_af_af:    EX(Z80->Regs.AF, Z80->Regs.AFx); break;
      case ex_de_hl:    EX(Z80->Regs.DE, Z80->Regs.HL); break;
      case ex_msp_hl:   EX_SP(HL); Z80_INC_WS_ADJUST break;
      case halt:        Z80_HALT; break;
      case ina:         INA;  break;
      case inc_a:       INC(_A); break;
      case inc_b:       INC(_B); break;
      case inc_bc:      INC_PAIR(_BC); break;
      case inc_c:       INC(_C); break;
      case inc_d:       INC(_D); break;
      case inc_de:      INC_PAIR(_DE); break;
      case inc_e:       INC(_E); break;
      case inc_h:       INC(_H); break;
      case inc_hl:      INC_PAIR(_HL); break;
      case inc_l:       INC(_L); break;
      case inc_mhl:     INC_MHL; break;
      case inc_sp:      INC_PAIR(_SP); break;
      case jp:          JP; break;
      case jp_c:        JP_C; break;
      case jp_m:        JP_M; break;
      case jp_nc:       JP_NC; break;
      case jp_nz:       JP_NZ; break;
      case jp_p:        JP_P; break;
      case jp_pe:       JP_PE; break;
      case jp_po:       JP_PO; break;
      case jp_z:        JP_Z; break;
      case jr:          JR; break;
      case jr_c:        JR_C; break;
      case jr_nc:       JR_NC; break;
      case jr_nz:       JR_NZ; break;
      case jr_z:        JR_Z; break;
      case ld_a_a:      break;
      case ld_a_b:      _A = _B; break;
      case ld_a_byte:   _A = read_mem(NativeCPC, _PC++); break;
      case ld_a_c:      _A = _C; break;
      case ld_a_d:      _A = _D; break;
      case ld_a_e:      _A = _E; break;
      case ld_a_h:      _A = _H; break;
      case ld_a_l:      _A = _L; break;
      case ld_a_mbc:    LD_A_MPAIR(_BC); break;
      case ld_a_mde:    LD_A_MPAIR(_DE); break;
      case ld_a_mhl:    LD_A_MPAIR(_HL); break;
      case ld_a_mword:  LD_A_MWORD; break;
      case ld_bc_word:  LD_BC_WORD; break;
      case ld_b_a:      _B = _A; break;
      case ld_b_b:      break;
      case ld_b_byte:   _B = read_mem(NativeCPC, _PC++); break;
      case ld_b_c:      _B = _C; break;
      case ld_b_d:      _B = _D; break;
      case ld_b_e:      _B = _E; break;
      case ld_b_h:      _B = _H; break;
      case ld_b_l:      _B = _L; break;
      case ld_b_mhl:    _B = read_mem(NativeCPC, _HL); break;
      case ld_c_a:      _C = _A; break;
      case ld_c_b:      _C = _B; break;
      case ld_c_byte:   _C = read_mem(NativeCPC, _PC++); break;
      case ld_c_c:      break;
      case ld_c_d:      _C = _D; break;
      case ld_c_e:      _C = _E; break;
      case ld_c_h:      _C = _H; break;
      case ld_c_l:      _C = _L; break;
      case ld_c_mhl:    _C = read_mem(NativeCPC, _HL); break;
      case ld_de_word:  LD_DE_WORD; break;
      case ld_d_a:      _D = _A; break;
      case ld_d_b:      _D = _B; break;
      case ld_d_byte:   _D = read_mem(NativeCPC, _PC++); break;
      case ld_d_c:      _D = _C; break;
      case ld_d_d:      break;
      case ld_d_e:      _D = _E; break;
      case ld_d_h:      _D = _H; break;
      case ld_d_l:      _D = _L; break;
      case ld_d_mhl:    _D = read_mem(NativeCPC, _HL); break;
      case ld_e_a:      _E = _A; break;
      case ld_e_b:      _E = _B; break;
      case ld_e_byte:   _E = read_mem(NativeCPC, _PC++); break;
      case ld_e_c:      _E = _C; break;
      case ld_e_d:      _E = _D; break;
      case ld_e_e:      break;
      case ld_e_h:      _E = _H; break;
      case ld_e_l:      _E = _L; break;
      case ld_e_mhl:    _E = read_mem(NativeCPC, _HL); break;
      case ld_hl_mword: LD16_MEM(HL); break;
      case ld_hl_word:  LD_HL_WORD; break;
      case ld_h_a:      _H = _A; break;
      case ld_h_b:      _H = _B; break;
      case ld_h_byte:   _H = read_mem(NativeCPC, _PC++); break;
      case ld_h_c:      _H = _C; break;
      case ld_h_d:      _H = _D; break;
      case ld_h_e:      _H = _E; break;
      case ld_h_h:      break;
      case ld_h_l:      _H = _L; break;
      case ld_h_mhl:    _H = read_mem(NativeCPC, _HL); break;
      case ld_l_a:      _L = _A; break;
      case ld_l_b:      _L = _B; break;
      case ld_l_byte:   _L = read_mem(NativeCPC, _PC++); break;
      case ld_l_c:      _L = _C; break;
      case ld_l_d:      _L = _D; break;
      case ld_l_e:      _L = _E; break;
      case ld_l_h:      _L = _H; break;
      case ld_l_l:      break;
      case ld_l_mhl:    _L = read_mem(NativeCPC, _HL); break;
      case ld_mbc_a:    LD_MBC_A; break;
      case ld_mde_a:    LD_MDE_A break;
      case ld_mhl_a:    write_mem(NativeCPC, _HL, _A); break;
      case ld_mhl_b:    write_mem(NativeCPC, _HL, _B); break;
      case ld_mhl_byte: LD_MHL_BYTE; break;
      case ld_mhl_c:    write_mem(NativeCPC, _HL, _C); break;
      case ld_mhl_d:    write_mem(NativeCPC, _HL, _D); break;
      case ld_mhl_e:    write_mem(NativeCPC, _HL, _E); break;
      case ld_mhl_h:    write_mem(NativeCPC, _HL, _H); break;
      case ld_mhl_l:    write_mem(NativeCPC, _HL, _L); break;
      case ld_mword_a:  LD_MWORD_A; break;
      case ld_mword_hl: LDMEM_16(HL); break;
      case ld_pc_hl:    _PC = _HL; break;
      case ld_sp_hl:    _SP = _HL; Z80_INC_WS_ADJUST break;
      case ld_sp_word:  LD_SP_WORD; break;
      case nop:         break;
      case or_a:        OR(_A); break;
      case or_b:        OR(_B); break;
      case or_byte:     OR(read_mem(NativeCPC, _PC++)); break;
      case or_c:        OR(_C); break;
      case or_d:        OR(_D); break;
      case or_e:        OR(_E); break;
      case or_h:        OR(_H); break;
      case or_l:        OR(_L); break;
      case or_mhl:      OR(read_mem(NativeCPC, _HL)); break;
      case outa:        OUTA; break;
      case pfx_cb:      z80_pfx_cb(NativeCPC); break;
      case pfx_dd:      z80_pfx_dd(NativeCPC); break;
      case pfx_ed:      z80_pfx_ed(NativeCPC); break;
      case pfx_fd:      z80_pfx_fd(NativeCPC); break;
      case pop_af:      POP(AF); break;
      case pop_bc:      POP(BC); break;
      case pop_de:      POP(DE); break;
      case pop_hl:      POP(HL); break;
      case push_af:     PUSH(AF); break;
      case push_bc:     PUSH(BC); break;
      case push_de:     PUSH(DE); break;
      case push_hl:     PUSH(HL); break;
      case ret:         RET; break;
      case ret_c:       RET_C; break;
      case ret_m:       RET_M; break;
      case ret_nc:      RET_NC; break;
      case ret_nz:      RET_NZ; break;
      case ret_p:       RET_P; break;
      case ret_pe:      RET_PE; break;
      case ret_po:      RET_PO; break;
      case ret_z:       RET_Z; break;
      case rla:         RLA; break;
      case rlca:        RLCA; break;
      case rra:         RRA; break;
      case rrca:        RRCA; break;
      case rst00:       RST(0x0000); break;
      case rst08:       RST(0x0008); break;
      case rst10:       RST(0x0010); break;
      case rst18:       RST(0x0018); break;
      case rst20:       RST(0x0020); break;
      case rst28:       RST(0x0028); break;
      case rst30:       RST(0x0030); break;
      case rst38:       RST(0x0038); break;
      case sbc_a:       SBC(_A); break;
      case sbc_b:       SBC(_B); break;
      case sbc_byte:    SBC(read_mem(NativeCPC, _PC++)); break;
      case sbc_c:       SBC(_C); break;
      case sbc_d:       SBC(_D); break;
      case sbc_e:       SBC(_E); break;
      case sbc_h:       SBC(_H); break;
      case sbc_l:       SBC(_L); break;
      case sbc_mhl:     SBC(read_mem(NativeCPC, _HL)); break;
      case scf:         SCF; break;
      case sub_a:       SUB(_A); break;
      case sub_b:       SUB(_B); break;
      case sub_byte:    SUB(read_mem(NativeCPC, _PC++)); break;
      case sub_c:       SUB(_C); break;
      case sub_d:       SUB(_D); break;
      case sub_e:       SUB(_E); break;
      case sub_h:       SUB(_H); break;
      case sub_l:       SUB(_L); break;
      case sub_mhl:     SUB(read_mem(NativeCPC, _HL)); break;
      case xor_a:       XOR(_A); break;
      case xor_b:       XOR(_B); break;
      case xor_byte:    XOR(read_mem(NativeCPC, _PC++)); break;
      case xor_c:       XOR(_C); break;
      case xor_d:       XOR(_D); break;
      case xor_e:       XOR(_E); break;
      case xor_h:       XOR(_H); break;
      case xor_l:       XOR(_L); break;
      case xor_mhl:     XOR(read_mem(NativeCPC, _HL)); break;
    }

    Z80_WAIT_STATES

    if (Z80->Regs.EI_issued) // EI 'delay' in effect?
    {
      if (--Z80->Regs.EI_issued == 0)
      {
        _IFF1 = _IFF2 = Pflag; // set interrupt flip-flops

#ifdef Z80_USE_NMI_HANDLER
        if (Z80->Regs.nmi_pending)
        {
          Z80_NMI_HANDLER
        }
        else
#endif /* Z80_USE_NMI_HANDLER */
        if (Z80->Regs.int_pending)
        {
          Z80_INT_HANDLER
        }
      }
    }
#ifdef Z80_USE_NMI_HANDLER
    else if (Z80->Regs.nmi_pending)
    {
      Z80_NMI_HANDLER
    }
#endif /* Z80_USE_NMI_HANDLER */
    else if (Z80->Regs.int_pending) // any interrupts pending?
    {
      Z80_INT_HANDLER
    }

    Z80_RESET_WS_ADJUST

    //
    // Exit conditions
    //
    ExitConditions = 0;
    if (NativeCPC->step_by_step)
    {
      ExitConditions |= EC_END_INSTRUCTION;
    }
		// Breakpoints
		if (NativeCPC->nb_breakpoints)
		{
			if (IsBreakpointDetected(NativeCPC))
			{
      	ExitConditions |= EC_BREAKPOINT;
			}
		}

    if (VDU->frame_completed) // video emulation finished building frame?
    {
      VDU->frame_completed = 0;
#ifndef CRTC_ENGINE_V2
      VDU->scr_base = (tULong*)NativeCPC->BmpOffScreenBits; // reset to surface start
#else /* !CRTC_ENGINE_V2 */
      {
        tULong dwOffset = VDU->scr_pos - VDU->scr_base; // offset in current surface row
        if (VDU->scrln > 0)
        {
          // determine current position
          VDU->scr_base = (tULong*)NativeCPC->BmpOffScreenBits + (VDU->scrln * VDU->scr_line_offs);
        }
        else
        {
          VDU->scr_base = (tULong*)NativeCPC->BmpOffScreenBits; // reset to surface start
        }
        VDU->scr_pos = VDU->scr_base + dwOffset; // update current rendering position
      }
#endif /* !CRTC_ENGINE_V2 */

      ExitConditions |= EC_FRAME_COMPLETE;
    }

    if (PSG->buffer_full) // sound emulation finished filling a buffer?
    {
      PSG->buffer_full = 0;

      ExitConditions |= EC_SOUND_BUFFER;
    }

    if (NativeCPC->cycle_count <= 0) // emulation loop ran for one frame?
    {
      NativeCPC->cycle_count += CYCLE_COUNT_INIT;

      ExitConditions |= EC_CYCLE_COUNT;
    }

    //
    // Monitoring conditions
    //
    if (NativeCPC->fdc_event_capture)
    {
      if (NativeCPC->FDC->signal_updated)
      {
        ExitConditions |= EC_FDC_EVENT;
      }
    }
#ifdef ENABLE_METROLOGY
    if (NativeCPC->signal_ROM_external_operation)
    {
      ExitConditions |= EC_ROM_INOUT_EVENT;
    }
#endif /* ENABLE_METROLOGY */

#ifdef ENABLE_TEXT_CAPTURE
    if (NativeCPC->text_capture)
    {
      if (_PC == 0xbdd3) // TXT WRITE CHAR
      {
        ExitConditions |= EC_TEXT_CAPTURE;
      }
      else if (_PC == 0xbdd9) // TXT OUT ACTION
      {
        ExitConditions |= EC_TEXT_CAPTURE;
      }
    }
#endif /* ENABLE_TEXT_CAPTURE */

#ifdef USE_XMEM
		if (NativeCPC->memwrite_addr != BREAKPOINT_INVALID)
		{
			if (NativeCPC->XMEM_writable)
			{
				NativeCPC->XMEMPtr(NativeCPC);
			}
		}
#endif /* USE_XMEM */

#ifdef _SAFETY
		if (CheckRAMMarkers(NativeCPC) == cFalse)
		{
			ExitConditions |= EC_SAFETY_ERROR;
		}
#endif /* _SAFETY */
	}
  while (!ExitConditions);

  return ExitConditions;
}
/*---------------------------------------------------------------------*/


static tVoid z80_pfx_cb(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  z80_pfx_cb
 *
 ***********************************************************************/
{
tZ80* Z80 = NativeCPC->Z80;
tUChar bOpCode;

  bOpCode = read_mem(NativeCPC, _PC++);
#ifndef USE_Z80_TIMING_ADJUST
  Z80->iCycleCount += cc_cb[bOpCode];
#else /* USE_Z80_TIMING_ADJUST */
  Z80->iCycleCount += Z80->cc_cb[bOpCode];
#endif /* USE_Z80_TIMING_ADJUST */
  _R++;

  switch (bOpCode)
  {
    case bit0_a:      BIT(0, _A); break;
    case bit0_b:      BIT(0, _B); break;
    case bit0_c:      BIT(0, _C); break;
    case bit0_d:      BIT(0, _D); break;
    case bit0_e:      BIT(0, _E); break;
    case bit0_h:      BIT(0, _H); break;
    case bit0_l:      BIT(0, _L); break;
    case bit0_mhl:    BIT_HL(0, read_mem(NativeCPC, _HL)); break;
    case bit1_a:      BIT(1, _A); break;
    case bit1_b:      BIT(1, _B); break;
    case bit1_c:      BIT(1, _C); break;
    case bit1_d:      BIT(1, _D); break;
    case bit1_e:      BIT(1, _E); break;
    case bit1_h:      BIT(1, _H); break;
    case bit1_l:      BIT(1, _L); break;
    case bit1_mhl:    BIT_HL(1, read_mem(NativeCPC, _HL)); break;
    case bit2_a:      BIT(2, _A); break;
    case bit2_b:      BIT(2, _B); break;
    case bit2_c:      BIT(2, _C); break;
    case bit2_d:      BIT(2, _D); break;
    case bit2_e:      BIT(2, _E); break;
    case bit2_h:      BIT(2, _H); break;
    case bit2_l:      BIT(2, _L); break;
    case bit2_mhl:    BIT_HL(2, read_mem(NativeCPC, _HL)); break;
    case bit3_a:      BIT(3, _A); break;
    case bit3_b:      BIT(3, _B); break;
    case bit3_c:      BIT(3, _C); break;
    case bit3_d:      BIT(3, _D); break;
    case bit3_e:      BIT(3, _E); break;
    case bit3_h:      BIT(3, _H); break;
    case bit3_l:      BIT(3, _L); break;
    case bit3_mhl:    BIT_HL(3, read_mem(NativeCPC, _HL)); break;
    case bit4_a:      BIT(4, _A); break;
    case bit4_b:      BIT(4, _B); break;
    case bit4_c:      BIT(4, _C); break;
    case bit4_d:      BIT(4, _D); break;
    case bit4_e:      BIT(4, _E); break;
    case bit4_h:      BIT(4, _H); break;
    case bit4_l:      BIT(4, _L); break;
    case bit4_mhl:    BIT_HL(4, read_mem(NativeCPC, _HL)); break;
    case bit5_a:      BIT(5, _A); break;
    case bit5_b:      BIT(5, _B); break;
    case bit5_c:      BIT(5, _C); break;
    case bit5_d:      BIT(5, _D); break;
    case bit5_e:      BIT(5, _E); break;
    case bit5_h:      BIT(5, _H); break;
    case bit5_l:      BIT(5, _L); break;
    case bit5_mhl:    BIT_HL(5, read_mem(NativeCPC, _HL)); break;
    case bit6_a:      BIT(6, _A); break;
    case bit6_b:      BIT(6, _B); break;
    case bit6_c:      BIT(6, _C); break;
    case bit6_d:      BIT(6, _D); break;
    case bit6_e:      BIT(6, _E); break;
    case bit6_h:      BIT(6, _H); break;
    case bit6_l:      BIT(6, _L); break;
    case bit6_mhl:    BIT_HL(6, read_mem(NativeCPC, _HL)); break;
    case bit7_a:      BIT(7, _A); break;
    case bit7_b:      BIT(7, _B); break;
    case bit7_c:      BIT(7, _C); break;
    case bit7_d:      BIT(7, _D); break;
    case bit7_e:      BIT(7, _E); break;
    case bit7_h:      BIT(7, _H); break;
    case bit7_l:      BIT(7, _L); break;
    case bit7_mhl:    BIT_HL(7, read_mem(NativeCPC, _HL)); break;
    case res0_a:      _A = RES(0, _A); break;
    case res0_b:      _B = RES(0, _B); break;
    case res0_c:      _C = RES(0, _C); break;
    case res0_d:      _D = RES(0, _D); break;
    case res0_e:      _E = RES(0, _E); break;
    case res0_h:      _H = RES(0, _H); break;
    case res0_l:      _L = RES(0, _L); break;
    case res0_mhl:    RES_MHL(0); break;
    case res1_a:      _A = RES(1, _A); break;
    case res1_b:      _B = RES(1, _B); break;
    case res1_c:      _C = RES(1, _C); break;
    case res1_d:      _D = RES(1, _D); break;
    case res1_e:      _E = RES(1, _E); break;
    case res1_h:      _H = RES(1, _H); break;
    case res1_l:      _L = RES(1, _L); break;
    case res1_mhl:    RES_MHL(1); break;
    case res2_a:      _A = RES(2, _A); break;
    case res2_b:      _B = RES(2, _B); break;
    case res2_c:      _C = RES(2, _C); break;
    case res2_d:      _D = RES(2, _D); break;
    case res2_e:      _E = RES(2, _E); break;
    case res2_h:      _H = RES(2, _H); break;
    case res2_l:      _L = RES(2, _L); break;
    case res2_mhl:    RES_MHL(2); break;
    case res3_a:      _A = RES(3, _A); break;
    case res3_b:      _B = RES(3, _B); break;
    case res3_c:      _C = RES(3, _C); break;
    case res3_d:      _D = RES(3, _D); break;
    case res3_e:      _E = RES(3, _E); break;
    case res3_h:      _H = RES(3, _H); break;
    case res3_l:      _L = RES(3, _L); break;
    case res3_mhl:    RES_MHL(3); break;
    case res4_a:      _A = RES(4, _A); break;
    case res4_b:      _B = RES(4, _B); break;
    case res4_c:      _C = RES(4, _C); break;
    case res4_d:      _D = RES(4, _D); break;
    case res4_e:      _E = RES(4, _E); break;
    case res4_h:      _H = RES(4, _H); break;
    case res4_l:      _L = RES(4, _L); break;
    case res4_mhl:    RES_MHL(4); break;
    case res5_a:      _A = RES(5, _A); break;
    case res5_b:      _B = RES(5, _B); break;
    case res5_c:      _C = RES(5, _C); break;
    case res5_d:      _D = RES(5, _D); break;
    case res5_e:      _E = RES(5, _E); break;
    case res5_h:      _H = RES(5, _H); break;
    case res5_l:      _L = RES(5, _L); break;
    case res5_mhl:    RES_MHL(5); break;
    case res6_a:      _A = RES(6, _A); break;
    case res6_b:      _B = RES(6, _B); break;
    case res6_c:      _C = RES(6, _C); break;
    case res6_d:      _D = RES(6, _D); break;
    case res6_e:      _E = RES(6, _E); break;
    case res6_h:      _H = RES(6, _H); break;
    case res6_l:      _L = RES(6, _L); break;
    case res6_mhl:    RES_MHL(6); break;
    case res7_a:      _A = RES(7, _A); break;
    case res7_b:      _B = RES(7, _B); break;
    case res7_c:      _C = RES(7, _C); break;
    case res7_d:      _D = RES(7, _D); break;
    case res7_e:      _E = RES(7, _E); break;
    case res7_h:      _H = RES(7, _H); break;
    case res7_l:      _L = RES(7, _L); break;
    case res7_mhl:    RES_MHL(7); break;
    case rlc_a:       _A = RLC(Z80, _A); break;
    case rlc_b:       _B = RLC(Z80, _B); break;
    case rlc_c:       _C = RLC(Z80, _C); break;
    case rlc_d:       _D = RLC(Z80, _D); break;
    case rlc_e:       _E = RLC(Z80, _E); break;
    case rlc_h:       _H = RLC(Z80, _H); break;
    case rlc_l:       _L = RLC(Z80, _L); break;
    case rlc_mhl:     RLC_MHL; break;
    case rl_a:        _A = RL(Z80, _A); break;
    case rl_b:        _B = RL(Z80, _B); break;
    case rl_c:        _C = RL(Z80, _C); break;
    case rl_d:        _D = RL(Z80, _D); break;
    case rl_e:        _E = RL(Z80, _E); break;
    case rl_h:        _H = RL(Z80, _H); break;
    case rl_l:        _L = RL(Z80, _L); break;
    case rl_mhl:      RL_MHL; break;
    case rrc_a:       _A = RRC(Z80, _A); break;
    case rrc_b:       _B = RRC(Z80, _B); break;
    case rrc_c:       _C = RRC(Z80, _C); break;
    case rrc_d:       _D = RRC(Z80, _D); break;
    case rrc_e:       _E = RRC(Z80, _E); break;
    case rrc_h:       _H = RRC(Z80, _H); break;
    case rrc_l:       _L = RRC(Z80, _L); break;
    case rrc_mhl:     RRC_MHL; break;
    case rr_a:        _A = RR(Z80, _A); break;
    case rr_b:        _B = RR(Z80, _B); break;
    case rr_c:        _C = RR(Z80, _C); break;
    case rr_d:        _D = RR(Z80, _D); break;
    case rr_e:        _E = RR(Z80, _E); break;
    case rr_h:        _H = RR(Z80, _H); break;
    case rr_l:        _L = RR(Z80, _L); break;
    case rr_mhl:      RR_MHL; break;
    case set0_a:      _A = SET(0, _A); break;
    case set0_b:      _B = SET(0, _B); break;
    case set0_c:      _C = SET(0, _C); break;
    case set0_d:      _D = SET(0, _D); break;
    case set0_e:      _E = SET(0, _E); break;
    case set0_h:      _H = SET(0, _H); break;
    case set0_l:      _L = SET(0, _L); break;
    case set0_mhl:    SET_MHL(0); break;
    case set1_a:      _A = SET(1, _A); break;
    case set1_b:      _B = SET(1, _B); break;
    case set1_c:      _C = SET(1, _C); break;
    case set1_d:      _D = SET(1, _D); break;
    case set1_e:      _E = SET(1, _E); break;
    case set1_h:      _H = SET(1, _H); break;
    case set1_l:      _L = SET(1, _L); break;
    case set1_mhl:    SET_MHL(1); break;
    case set2_a:      _A = SET(2, _A); break;
    case set2_b:      _B = SET(2, _B); break;
    case set2_c:      _C = SET(2, _C); break;
    case set2_d:      _D = SET(2, _D); break;
    case set2_e:      _E = SET(2, _E); break;
    case set2_h:      _H = SET(2, _H); break;
    case set2_l:      _L = SET(2, _L); break;
    case set2_mhl:    SET_MHL(2); break;
    case set3_a:      _A = SET(3, _A); break;
    case set3_b:      _B = SET(3, _B); break;
    case set3_c:      _C = SET(3, _C); break;
    case set3_d:      _D = SET(3, _D); break;
    case set3_e:      _E = SET(3, _E); break;
    case set3_h:      _H = SET(3, _H); break;
    case set3_l:      _L = SET(3, _L); break;
    case set3_mhl:    SET_MHL(3); break;
    case set4_a:      _A = SET(4, _A); break;
    case set4_b:      _B = SET(4, _B); break;
    case set4_c:      _C = SET(4, _C); break;
    case set4_d:      _D = SET(4, _D); break;
    case set4_e:      _E = SET(4, _E); break;
    case set4_h:      _H = SET(4, _H); break;
    case set4_l:      _L = SET(4, _L); break;
    case set4_mhl:    SET_MHL(4); break;
    case set5_a:      _A = SET(5, _A); break;
    case set5_b:      _B = SET(5, _B); break;
    case set5_c:      _C = SET(5, _C); break;
    case set5_d:      _D = SET(5, _D); break;
    case set5_e:      _E = SET(5, _E); break;
    case set5_h:      _H = SET(5, _H); break;
    case set5_l:      _L = SET(5, _L); break;
    case set5_mhl:    SET_MHL(5); break;
    case set6_a:      _A = SET(6, _A); break;
    case set6_b:      _B = SET(6, _B); break;
    case set6_c:      _C = SET(6, _C); break;
    case set6_d:      _D = SET(6, _D); break;
    case set6_e:      _E = SET(6, _E); break;
    case set6_h:      _H = SET(6, _H); break;
    case set6_l:      _L = SET(6, _L); break;
    case set6_mhl:    SET_MHL(6); break;
    case set7_a:      _A = SET(7, _A); break;
    case set7_b:      _B = SET(7, _B); break;
    case set7_c:      _C = SET(7, _C); break;
    case set7_d:      _D = SET(7, _D); break;
    case set7_e:      _E = SET(7, _E); break;
    case set7_h:      _H = SET(7, _H); break;
    case set7_l:      _L = SET(7, _L); break;
    case set7_mhl:    SET_MHL(7); break;
    case sla_a:       _A = SLA(Z80, _A); break;
    case sla_b:       _B = SLA(Z80, _B); break;
    case sla_c:       _C = SLA(Z80, _C); break;
    case sla_d:       _D = SLA(Z80, _D); break;
    case sla_e:       _E = SLA(Z80, _E); break;
    case sla_h:       _H = SLA(Z80, _H); break;
    case sla_l:       _L = SLA(Z80, _L); break;
    case sla_mhl:     SLA_MHL; break;
    case sll_a:       _A = SLL(Z80, _A); break;
    case sll_b:       _B = SLL(Z80, _B); break;
    case sll_c:       _C = SLL(Z80, _C); break;
    case sll_d:       _D = SLL(Z80, _D); break;
    case sll_e:       _E = SLL(Z80, _E); break;
    case sll_h:       _H = SLL(Z80, _H); break;
    case sll_l:       _L = SLL(Z80, _L); break;
    case sll_mhl:     SLL_MHL; break;
    case sra_a:       _A = SRA(Z80, _A); break;
    case sra_b:       _B = SRA(Z80, _B); break;
    case sra_c:       _C = SRA(Z80, _C); break;
    case sra_d:       _D = SRA(Z80, _D); break;
    case sra_e:       _E = SRA(Z80, _E); break;
    case sra_h:       _H = SRA(Z80, _H); break;
    case sra_l:       _L = SRA(Z80, _L); break;
    case sra_mhl:     SRA_MHL; break;
    case srl_a:       _A = SRL(Z80, _A); break;
    case srl_b:       _B = SRL(Z80, _B); break;
    case srl_c:       _C = SRL(Z80, _C); break;
    case srl_d:       _D = SRL(Z80, _D); break;
    case srl_e:       _E = SRL(Z80, _E); break;
    case srl_h:       _H = SRL(Z80, _H); break;
    case srl_l:       _L = SRL(Z80, _L); break;
    case srl_mhl:     SRL_MHL; break;
  }
}
/*---------------------------------------------------------------------*/


static tVoid z80_pfx_dd(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  z80_pfx_dd
 *
 ***********************************************************************/
{
tZ80* Z80 = NativeCPC->Z80;
tPSG* PSG = NativeCPC->PSG;
tFDC* FDC = NativeCPC->FDC;
tUChar bOpCode;

  bOpCode = read_mem(NativeCPC, _PC++);
#ifndef USE_Z80_TIMING_ADJUST
  Z80->iCycleCount += cc_xy[bOpCode];
#else /* USE_Z80_TIMING_ADJUST */
  Z80->iCycleCount += Z80->cc_xy[bOpCode];
#endif /* USE_Z80_TIMING_ADJUST */
  _R++;

  switch (bOpCode)
  {
    case adc_a:       ADC(_A); break;
    case adc_b:       ADC(_B); break;
    case adc_byte:    ADC(read_mem(NativeCPC, _PC++)); break;
    case adc_c:       ADC(_C); break;
    case adc_d:       ADC(_D); break;
    case adc_e:       ADC(_E); break;
    case adc_h:       ADC(_IXh); break;
    case adc_l:       ADC(_IXl); break;
    case adc_mhl:     ADC_MIX; break;
    case add_a:       ADD(_A); break;
    case add_b:       ADD(_B); break;
    case add_byte:    ADD(read_mem(NativeCPC, _PC++)); break;
    case add_c:       ADD(_C); break;
    case add_d:       ADD(_D); break;
    case add_e:       ADD(_E); break;
    case add_h:       ADD(_IXh); break;
    case add_hl_bc:   ADD16(IX, BC); break;
    case add_hl_de:   ADD16(IX, DE); break;
    case add_hl_hl:   ADD16(IX, IX); break;
    case add_hl_sp:   ADD16(IX, SP); break;
    case add_l:       ADD(_IXl); break;
    case add_mhl:     ADD_MIX; break;
    case and_a:       AND(_A); break;
    case and_b:       AND(_B); break;
    case and_byte:    AND(read_mem(NativeCPC, _PC++)); break;
    case and_c:       AND(_C); break;
    case and_d:       AND(_D); break;
    case and_e:       AND(_E); break;
    case and_h:       AND(_IXh); break;
    case and_l:       AND(_IXl); break;
    case and_mhl:     AND_MIX; break;
    case call:        CALL; break;
    case call_c:      CALL_C;  break;
    case call_m:      CALL_M;  break;
    case call_nc:     CALL_NC;  break;
    case call_nz:     CALL_NZ;  break;
    case call_p:      CALL_P; break;
    case call_pe:     CALL_PE;  break;
    case call_po:     CALL_PO; break;
    case call_z:      CALL_Z;  break;
    case ccf:         CCF; break;
    case cpl:         CPL; break;
    case cp_a:        CP(_A); break;
    case cp_b:        CP(_B); break;
    case cp_byte:     CP(read_mem(NativeCPC, _PC++)); break;
    case cp_c:        CP(_C); break;
    case cp_d:        CP(_D); break;
    case cp_e:        CP(_E); break;
    case cp_h:        CP(_IXh); break;
    case cp_l:        CP(_IXl); break;
    case cp_mhl:      CP_MIX; break;
    case daa:         DAA; break;
    case dec_a:       DEC(_A); break;
    case dec_b:       DEC(_B); break;
    case dec_bc:      DEC_PAIR(_BC); break;
    case dec_c:       DEC(_C); break;
    case dec_d:       DEC(_D); break;
    case dec_de:      DEC_PAIR(_DE); break;
    case dec_e:       DEC(_E); break;
    case dec_h:       DEC(_IXh); break;
    case dec_hl:      DEC_PAIR(_IX); break;
    case dec_l:       DEC(_IXl); break;
    case dec_mhl:     DEC_MIX; break;
    case dec_sp:      DEC_PAIR(_SP); break;
    case di:          DI; break;
    case djnz:        DJNZ;  break;
    case ei:          EI; break;
    case exx:         EXX; break;
    case ex_af_af:    EX(Z80->Regs.AF, Z80->Regs.AFx); break;
    case ex_de_hl:    EX(Z80->Regs.DE, Z80->Regs.HL); break;
    case ex_msp_hl:   EX_SP(IX); Z80_INC_WS_ADJUST break;
    case halt:        Z80_HALT; break;
    case ina:         INA; break;
    case inc_a:       INC(_A); break;
    case inc_b:       INC(_B); break;
    case inc_bc:      INC_PAIR(_BC); break;
    case inc_c:       INC(_C); break;
    case inc_d:       INC(_D); break;
    case inc_de:      INC_PAIR(_DE); break;
    case inc_e:       INC(_E); break;
    case inc_h:       INC(_IXh); break;
    case inc_hl:      INC_PAIR(_IX); break;
    case inc_l:       INC(_IXl); break;
    case inc_mhl:     INC_MIX; break;
    case inc_sp:      INC_PAIR(_SP); break;
    case jp:          JP; break;
    case jp_c:        JP_C; break;
    case jp_m:        JP_M; break;
    case jp_nc:       JP_NC; break;
    case jp_nz:       JP_NZ; break;
    case jp_p:        JP_P; break;
    case jp_pe:       JP_PE; break;
    case jp_po:       JP_PO; break;
    case jp_z:        JP_Z; break;
    case jr:          JR; break;
    case jr_c:        JR_C; break;
    case jr_nc:       JR_NC; break;
    case jr_nz:       JR_NZ; break;
    case jr_z:        JR_Z; break;
    case ld_a_a:      break;
    case ld_a_b:      _A = _B; break;
    case ld_a_byte:   _A = read_mem(NativeCPC, _PC++); break;
    case ld_a_c:      _A = _C; break;
    case ld_a_d:      _A = _D; break;
    case ld_a_e:      _A = _E; break;
    case ld_a_h:      _A = _IXh; break;
    case ld_a_l:      _A = _IXl; break;
    case ld_a_mbc:    LD_A_MPAIR(_BC); break;
    case ld_a_mde:    LD_A_MPAIR(_DE); break;
    case ld_a_mhl:    LD_REG_MHL_I_(_A, _IX); break;
    case ld_a_mword:  LD_A_MWORD; break;
    case ld_bc_word:  LD_BC_WORD; break;
    case ld_b_a:      _B = _A; break;
    case ld_b_b:      break;
    case ld_b_byte:   _B = read_mem(NativeCPC, _PC++); break;
    case ld_b_c:      _B = _C; break;
    case ld_b_d:      _B = _D; break;
    case ld_b_e:      _B = _E; break;
    case ld_b_h:      _B = _IXh; break;
    case ld_b_l:      _B = _IXl; break;
    case ld_b_mhl:    LD_REG_MHL_I_(_B, _IX); break;
    case ld_c_a:      _C = _A; break;
    case ld_c_b:      _C = _B; break;
    case ld_c_byte:   _C = read_mem(NativeCPC, _PC++); break;
    case ld_c_c:      break;
    case ld_c_d:      _C = _D; break;
    case ld_c_e:      _C = _E; break;
    case ld_c_h:      _C = _IXh; break;
    case ld_c_l:      _C = _IXl; break;
    case ld_c_mhl:    LD_REG_MHL_I_(_C, _IX); break;
    case ld_de_word:  LD_DE_WORD; break;
    case ld_d_a:      _D = _A; break;
    case ld_d_b:      _D = _B; break;
    case ld_d_byte:   _D = read_mem(NativeCPC, _PC++); break;
    case ld_d_c:      _D = _C; break;
    case ld_d_d:      break;
    case ld_d_e:      _D = _E; break;
    case ld_d_h:      _D = _IXh; break;
    case ld_d_l:      _D = _IXl; break;
    case ld_d_mhl:    LD_REG_MHL_I_(_D, _IX); break;
    case ld_e_a:      _E = _A; break;
    case ld_e_b:      _E = _B; break;
    case ld_e_byte:   _E = read_mem(NativeCPC, _PC++); break;
    case ld_e_c:      _E = _C; break;
    case ld_e_d:      _E = _D; break;
    case ld_e_e:      break;
    case ld_e_h:      _E = _IXh; break;
    case ld_e_l:      _E = _IXl; break;
    case ld_e_mhl:    LD_REG_MHL_I_(_E, _IX); break;
    case ld_hl_mword: LD16_MEM(IX); break;
    case ld_hl_word:  LD_IX_WORD; break;
    case ld_h_a:      _IXh = _A; break;
    case ld_h_b:      _IXh = _B; break;
    case ld_h_byte:   _IXh = read_mem(NativeCPC, _PC++); break;
    case ld_h_c:      _IXh = _C; break;
    case ld_h_d:      _IXh = _D; break;
    case ld_h_e:      _IXh = _E; break;
    case ld_h_h:      break;
    case ld_h_l:      _IXh = _IXl; break;
    case ld_h_mhl:    LD_REG_MHL_I_(_H, _IX); break;
    case ld_l_a:      _IXl = _A; break;
    case ld_l_b:      _IXl = _B; break;
    case ld_l_byte:   _IXl = read_mem(NativeCPC, _PC++); break;
    case ld_l_c:      _IXl = _C; break;
    case ld_l_d:      _IXl = _D; break;
    case ld_l_e:      _IXl = _E; break;
    case ld_l_h:      _IXl = _IXh; break;
    case ld_l_l:      break;
    case ld_l_mhl:    LD_REG_MHL_I_(_L, _IX); break;
    case ld_mbc_a:    write_mem(NativeCPC, _BC, _A); break;
    case ld_mde_a:    write_mem(NativeCPC, _DE, _A); break;
    case ld_mhl_a:    LD_MIX_REG(_A); break;
    case ld_mhl_b:    LD_MIX_REG(_B); break;
    case ld_mhl_byte: LD_MIX_BYTE; break;
    case ld_mhl_c:    LD_MIX_REG(_C); break;
    case ld_mhl_d:    LD_MIX_REG(_D); break;
    case ld_mhl_e:    LD_MIX_REG(_E); break;
    case ld_mhl_h:    LD_MIX_REG(_H); break;
    case ld_mhl_l:    LD_MIX_REG(_L); break;
    case ld_mword_a:  LD_MWORD_A; break;
    case ld_mword_hl: LDMEM_16(IX); break;
    case ld_pc_hl:    _PC = _IX; break;
    case ld_sp_hl:    _SP = _IX; Z80_INC_WS_ADJUST break;
    case ld_sp_word:  LD_SP_WORD; break;
    case nop:         break;
    case or_a:        OR(_A); break;
    case or_b:        OR(_B); break;
    case or_byte:     OR(read_mem(NativeCPC, _PC++)); break;
    case or_c:        OR(_C); break;
    case or_d:        OR(_D); break;
    case or_e:        OR(_E); break;
    case or_h:        OR(_IXh); break;
    case or_l:        OR(_IXl); break;
    case or_mhl:      OR_MIX; break;
    case outa:        OUTA; break;
    case pfx_cb:      z80_pfx_ddcb(NativeCPC); break;
    case pfx_dd:      z80_pfx_dd(NativeCPC); break;
    case pfx_ed:      z80_pfx_ed(NativeCPC); break;
    case pfx_fd:      z80_pfx_fd(NativeCPC); break;
    case pop_af:      POP(AF); break;
    case pop_bc:      POP(BC); break;
    case pop_de:      POP(DE); break;
    case pop_hl:      POP(IX); break;
    case push_af:     PUSH(AF); break;
    case push_bc:     PUSH(BC); break;
    case push_de:     PUSH(DE); break;
    case push_hl:     PUSH(IX); break;
    case ret:         RET; break;
    case ret_c:       RET_C; break;
    case ret_m:       RET_M; break;
    case ret_nc:      RET_NC; break;
    case ret_nz:      RET_NZ; break;
    case ret_p:       RET_P; break;
    case ret_pe:      RET_PE; break;
    case ret_po:      RET_PO; break;
    case ret_z:       RET_Z; break;
    case rla:         RLA; break;
    case rlca:        RLCA; break;
    case rra:         RRA; break;
    case rrca:        RRCA; break;
    case rst00:       RST(0x0000); break;
    case rst08:       RST(0x0008); break;
    case rst10:       RST(0x0010); break;
    case rst18:       RST(0x0018); break;
    case rst20:       RST(0x0020); break;
    case rst28:       RST(0x0028); break;
    case rst30:       RST(0x0030); break;
    case rst38:       RST(0x0038); break;
    case sbc_a:       SBC(_A); break;
    case sbc_b:       SBC(_B); break;
    case sbc_byte:    SBC(read_mem(NativeCPC, _PC++)); break;
    case sbc_c:       SBC(_C); break;
    case sbc_d:       SBC(_D); break;
    case sbc_e:       SBC(_E); break;
    case sbc_h:       SBC(_IXh); break;
    case sbc_l:       SBC(_IXl); break;
    case sbc_mhl:     SBC_MIX; break;
    case scf:         SCF; break;
    case sub_a:       SUB(_A); break;
    case sub_b:       SUB(_B); break;
    case sub_byte:    SUB(read_mem(NativeCPC, _PC++)); break;
    case sub_c:       SUB(_C); break;
    case sub_d:       SUB(_D); break;
    case sub_e:       SUB(_E); break;
    case sub_h:       SUB(_IXh); break;
    case sub_l:       SUB(_IXl); break;
    case sub_mhl:     SUB_MIX; break;
    case xor_a:       XOR(_A); break;
    case xor_b:       XOR(_B); break;
    case xor_byte:    XOR(read_mem(NativeCPC, _PC++)); break;
    case xor_c:       XOR(_C); break;
    case xor_d:       XOR(_D); break;
    case xor_e:       XOR(_E); break;
    case xor_h:       XOR(_IXh); break;
    case xor_l:       XOR(_IXl); break;
    case xor_mhl:     XOR_MIX; break;
  }
}
/*---------------------------------------------------------------------*/


static tVoid z80_pfx_ddcb(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  z80_pfx_ddcb
 *
 ***********************************************************************/
{
tZ80* Z80 = NativeCPC->Z80;
tLong o;
tUChar bOpCode;

  o = signed_read_mem(NativeCPC, _PC++); // offset
  _WZ = _IX + o;
  bOpCode = read_mem(NativeCPC, _PC++);
#ifndef USE_Z80_TIMING_ADJUST
  Z80->iCycleCount += cc_xycb[bOpCode];
#else /* USE_Z80_TIMING_ADJUST */
  Z80->iCycleCount += Z80->cc_xycb[bOpCode];
#endif /* USE_Z80_TIMING_ADJUST*/

  switch (bOpCode)
  {
    case bit0_a:
    case bit0_b:
    case bit0_c:
    case bit0_d:
    case bit0_e:
    case bit0_h:
    case bit0_l:      BIT_XY(0, read_mem(NativeCPC, _WZ)); break;
    case bit0_mhl:    BIT_HL(0, read_mem(NativeCPC, _WZ)); break;
    case bit1_a:
    case bit1_b:
    case bit1_c:
    case bit1_d:
    case bit1_e:
    case bit1_h:
    case bit1_l:      BIT_XY(1, read_mem(NativeCPC, _WZ)); break;
    case bit1_mhl:    BIT_HL(1, read_mem(NativeCPC, _WZ)); break;
    case bit2_a:
    case bit2_b:
    case bit2_c:
    case bit2_d:
    case bit2_e:
    case bit2_h:
    case bit2_l:      BIT_XY(2, read_mem(NativeCPC, _WZ)); break;
    case bit2_mhl:    BIT_HL(2, read_mem(NativeCPC, _WZ)); break;
    case bit3_a:
    case bit3_b:
    case bit3_c:
    case bit3_d:
    case bit3_e:
    case bit3_h:
    case bit3_l:      BIT_XY(3, read_mem(NativeCPC, _WZ)); break;
    case bit3_mhl:    BIT_HL(3, read_mem(NativeCPC, _WZ)); break;
    case bit4_a:
    case bit4_b:
    case bit4_c:
    case bit4_d:
    case bit4_e:
    case bit4_h:
    case bit4_l:      BIT_XY(4, read_mem(NativeCPC, _WZ)); break;
    case bit4_mhl:    BIT_HL(4, read_mem(NativeCPC, _WZ)); break;
    case bit5_a:
    case bit5_b:
    case bit5_c:
    case bit5_d:
    case bit5_e:
    case bit5_h:
    case bit5_l:      BIT_XY(5, read_mem(NativeCPC, _WZ)); break;
    case bit5_mhl:    BIT_HL(5, read_mem(NativeCPC, _WZ)); break;
    case bit6_a:
    case bit6_b:
    case bit6_c:
    case bit6_d:
    case bit6_e:
    case bit6_h:
    case bit6_l:      BIT_XY(6, read_mem(NativeCPC, _WZ)); break;
    case bit6_mhl:    BIT_HL(6, read_mem(NativeCPC, _WZ)); break;
    case bit7_a:
    case bit7_b:
    case bit7_c:
    case bit7_d:
    case bit7_e:
    case bit7_h:
    case bit7_l:      BIT_XY(7, read_mem(NativeCPC, _WZ)); break;
    case bit7_mhl:    BIT_HL(7, read_mem(NativeCPC, _WZ)); break;
    case res0_a:      RES_A_ADDR(_WZ, 0); break;
    case res0_b:      RES_B_ADDR(_WZ, 0); break;
    case res0_c:      RES_C_ADDR(_WZ, 0); break;
    case res0_d:      RES_D_ADDR(_WZ, 0); break;
    case res0_e:      RES_E_ADDR(_WZ, 0); break;
    case res0_h:      RES_H_ADDR(_WZ, 0); break;
    case res0_l:      RES_L_ADDR(_WZ, 0); break;
    case res0_mhl:    RES_MHL_ADDR(0, _WZ); break;
    case res1_a:      RES_A_ADDR(_WZ, 1); break;
    case res1_b:      RES_B_ADDR(_WZ, 1); break;
    case res1_c:      RES_C_ADDR(_WZ, 1); break;
    case res1_d:      RES_D_ADDR(_WZ, 1); break;
    case res1_e:      RES_E_ADDR(_WZ, 1); break;
    case res1_h:      RES_H_ADDR(_WZ, 1); break;
    case res1_l:      RES_L_ADDR(_WZ, 1); break;
    case res1_mhl:    RES_MHL_ADDR(1, _WZ); break;
    case res2_a:      RES_A_ADDR(_WZ, 2); break;
    case res2_b:      RES_B_ADDR(_WZ, 2); break;
    case res2_c:      RES_C_ADDR(_WZ, 2); break;
    case res2_d:      RES_D_ADDR(_WZ, 2); break;
    case res2_e:      RES_E_ADDR(_WZ, 2); break;
    case res2_h:      RES_H_ADDR(_WZ, 2); break;
    case res2_l:      RES_L_ADDR(_WZ, 2); break;
    case res2_mhl:    RES_MHL_ADDR(2, _WZ); break;
    case res3_a:      RES_A_ADDR(_WZ, 3); break;
    case res3_b:      RES_B_ADDR(_WZ, 3); break;
    case res3_c:      RES_C_ADDR(_WZ, 3); break;
    case res3_d:      RES_D_ADDR(_WZ, 3); break;
    case res3_e:      RES_E_ADDR(_WZ, 3); break;
    case res3_h:      RES_H_ADDR(_WZ, 3); break;
    case res3_l:      RES_L_ADDR(_WZ, 3); break;
    case res3_mhl:    RES_MHL_ADDR(3, _WZ); break;
    case res4_a:      RES_A_ADDR(_WZ, 4); break;
    case res4_b:      RES_B_ADDR(_WZ, 4); break;
    case res4_c:      RES_C_ADDR(_WZ, 4); break;
    case res4_d:      RES_D_ADDR(_WZ, 4); break;
    case res4_e:      RES_E_ADDR(_WZ, 4); break;
    case res4_h:      RES_H_ADDR(_WZ, 4); break;
    case res4_l:      RES_L_ADDR(_WZ, 4); break;
    case res4_mhl:    RES_MHL_ADDR(4, _WZ); break;
    case res5_a:      RES_A_ADDR(_WZ, 5); break;
    case res5_b:      RES_B_ADDR(_WZ, 5); break;
    case res5_c:      RES_C_ADDR(_WZ, 5); break;
    case res5_d:      RES_D_ADDR(_WZ, 5); break;
    case res5_e:      RES_E_ADDR(_WZ, 5); break;
    case res5_h:      RES_H_ADDR(_WZ, 5); break;
    case res5_l:      RES_L_ADDR(_WZ, 5); break;
    case res5_mhl:    RES_MHL_ADDR(5, _WZ); break;
    case res6_a:      RES_A_ADDR(_WZ, 6); break;
    case res6_b:      RES_B_ADDR(_WZ, 6); break;
    case res6_c:      RES_C_ADDR(_WZ, 6); break;
    case res6_d:      RES_D_ADDR(_WZ, 6); break;
    case res6_e:      RES_E_ADDR(_WZ, 6); break;
    case res6_h:      RES_H_ADDR(_WZ, 6); break;
    case res6_l:      RES_L_ADDR(_WZ, 6); break;
    case res6_mhl:    RES_MHL_ADDR(6, _WZ); break;
    case res7_a:      RES_A_ADDR(_WZ, 7); break;
    case res7_b:      RES_B_ADDR(_WZ, 7); break;
    case res7_c:      RES_C_ADDR(_WZ, 7); break;
    case res7_d:      RES_D_ADDR(_WZ, 7); break;
    case res7_e:      RES_E_ADDR(_WZ, 7); break;
    case res7_h:      RES_H_ADDR(_WZ, 7); break;
    case res7_l:      RES_L_ADDR(_WZ, 7); break;
    case res7_mhl:    RES_MHL_ADDR(7, _WZ); break;
    case rlc_a:       RLC_A_ADDR(_WZ); break;
    case rlc_b:       RLC_B_ADDR(_WZ); break;
    case rlc_c:       RLC_C_ADDR(_WZ); break;
    case rlc_d:       RLC_D_ADDR(_WZ); break;
    case rlc_e:       RLC_E_ADDR(_WZ); break;
    case rlc_h:       RLC_H_ADDR(_WZ); break;
    case rlc_l:       RLC_L_ADDR(_WZ); break;
    case rlc_mhl:     RLC_MHL_ADDR(_WZ); break;
    case rl_a:        RL_A_ADDR(_WZ); break;
    case rl_b:        RL_B_ADDR(_WZ); break;
    case rl_c:        RL_C_ADDR(_WZ); break;
    case rl_d:        RL_D_ADDR(_WZ); break;
    case rl_e:        RL_E_ADDR(_WZ); break;
    case rl_h:        RL_H_ADDR(_WZ); break;
    case rl_l:        RL_L_ADDR(_WZ); break;
    case rl_mhl:      RL_MHL_ADDR(_WZ); break;
    case rrc_a:       RRC_A_ADDR(_WZ); break;
    case rrc_b:       RRC_B_ADDR(_WZ); break;
    case rrc_c:       RRC_C_ADDR(_WZ); break;
    case rrc_d:       RRC_D_ADDR(_WZ); break;
    case rrc_e:       RRC_E_ADDR(_WZ); break;
    case rrc_h:       RRC_H_ADDR(_WZ); break;
    case rrc_l:       RRC_L_ADDR(_WZ); break;
    case rrc_mhl:     RRC_MHL_ADDR(_WZ); break;
    case rr_a:        RR_A_ADDR(_WZ); break;
    case rr_b:        RR_B_ADDR(_WZ); break;
    case rr_c:        RR_C_ADDR(_WZ); break;
    case rr_d:        RR_D_ADDR(_WZ); break;
    case rr_e:        RR_E_ADDR(_WZ); break;
    case rr_h:        RR_H_ADDR(_WZ); break;
    case rr_l:        RR_L_ADDR(_WZ); break;
    case rr_mhl:      RR_MHL_ADDR(_WZ); break;
    case set0_a:      SET_A_ADDR(_WZ, 0); break;
    case set0_b:      SET_B_ADDR(_WZ, 0); break;
    case set0_c:      SET_C_ADDR(_WZ, 0); break;
    case set0_d:      SET_D_ADDR(_WZ, 0); break;
    case set0_e:      SET_E_ADDR(_WZ, 0); break;
    case set0_h:      SET_H_ADDR(_WZ, 0); break;
    case set0_l:      SET_L_ADDR(_WZ, 0); break;
    case set0_mhl:    SET_MHL_ADDR(0, _WZ); break;
    case set1_a:      SET_A_ADDR(_WZ, 1); break;
    case set1_b:      SET_B_ADDR(_WZ, 1); break;
    case set1_c:      SET_C_ADDR(_WZ, 1); break;
    case set1_d:      SET_D_ADDR(_WZ, 1); break;
    case set1_e:      SET_E_ADDR(_WZ, 1); break;
    case set1_h:      SET_H_ADDR(_WZ, 1); break;
    case set1_l:      SET_L_ADDR(_WZ, 1); break;
    case set1_mhl:    SET_MHL_ADDR(1, _WZ); break;
    case set2_a:      SET_A_ADDR(_WZ, 2); break;
    case set2_b:      SET_B_ADDR(_WZ, 2); break;
    case set2_c:      SET_C_ADDR(_WZ, 2); break;
    case set2_d:      SET_D_ADDR(_WZ, 2); break;
    case set2_e:      SET_E_ADDR(_WZ, 2); break;
    case set2_h:      SET_H_ADDR(_WZ, 2); break;
    case set2_l:      SET_L_ADDR(_WZ, 2); break;
    case set2_mhl:    SET_MHL_ADDR(2, _WZ); break;
    case set3_a:      SET_A_ADDR(_WZ, 3); break;
    case set3_b:      SET_B_ADDR(_WZ, 3); break;
    case set3_c:      SET_C_ADDR(_WZ, 3); break;
    case set3_d:      SET_D_ADDR(_WZ, 3); break;
    case set3_e:      SET_E_ADDR(_WZ, 3); break;
    case set3_h:      SET_H_ADDR(_WZ, 3); break;
    case set3_l:      SET_L_ADDR(_WZ, 3); break;
    case set3_mhl:    SET_MHL_ADDR(3, _WZ); break;
    case set4_a:      SET_A_ADDR(_WZ, 4); break;
    case set4_b:      SET_B_ADDR(_WZ, 4); break;
    case set4_c:      SET_C_ADDR(_WZ, 4); break;
    case set4_d:      SET_D_ADDR(_WZ, 4); break;
    case set4_e:      SET_E_ADDR(_WZ, 4); break;
    case set4_h:      SET_H_ADDR(_WZ, 4); break;
    case set4_l:      SET_L_ADDR(_WZ, 4); break;
    case set4_mhl:    SET_MHL_ADDR(4, _WZ); break;
    case set5_a:      SET_A_ADDR(_WZ, 5); break;
    case set5_b:      SET_B_ADDR(_WZ, 5); break;
    case set5_c:      SET_C_ADDR(_WZ, 5); break;
    case set5_d:      SET_D_ADDR(_WZ, 5); break;
    case set5_e:      SET_E_ADDR(_WZ, 5); break;
    case set5_h:      SET_H_ADDR(_WZ, 5); break;
    case set5_l:      SET_L_ADDR(_WZ, 5); break;
    case set5_mhl:    SET_MHL_ADDR(5, _WZ); break;
    case set6_a:      SET_A_ADDR(_WZ, 6); break;
    case set6_b:      SET_B_ADDR(_WZ, 6); break;
    case set6_c:      SET_C_ADDR(_WZ, 6); break;
    case set6_d:      SET_D_ADDR(_WZ, 6); break;
    case set6_e:      SET_E_ADDR(_WZ, 6); break;
    case set6_h:      SET_H_ADDR(_WZ, 6); break;
    case set6_l:      SET_L_ADDR(_WZ, 6); break;
    case set6_mhl:    SET_MHL_ADDR(6, _WZ); break;
    case set7_a:      SET_A_ADDR(_WZ, 7); break;
    case set7_b:      SET_B_ADDR(_WZ, 7); break;
    case set7_c:      SET_C_ADDR(_WZ, 7); break;
    case set7_d:      SET_D_ADDR(_WZ, 7); break;
    case set7_e:      SET_E_ADDR(_WZ, 7); break;
    case set7_h:      SET_H_ADDR(_WZ, 7); break;
    case set7_l:      SET_L_ADDR(_WZ, 7); break;
    case set7_mhl:    SET_MHL_ADDR(7, _WZ); break;
    case sla_a:       SLA_A_ADDR(_WZ); break;
    case sla_b:       SLA_B_ADDR(_WZ); break;
    case sla_c:       SLA_C_ADDR(_WZ); break;
    case sla_d:       SLA_D_ADDR(_WZ); break;
    case sla_e:       SLA_E_ADDR(_WZ); break;
    case sla_h:       SLA_H_ADDR(_WZ); break;
    case sla_l:       SLA_L_ADDR(_WZ); break;
    case sla_mhl:     SLA_MHL_ADDR(_WZ); break;
    case sll_a:       SLL_A_ADDR(_WZ); break;
    case sll_b:       SLL_B_ADDR(_WZ); break;
    case sll_c:       SLL_C_ADDR(_WZ); break;
    case sll_d:       SLL_D_ADDR(_WZ); break;
    case sll_e:       SLL_E_ADDR(_WZ); break;
    case sll_h:       SLL_H_ADDR(_WZ); break;
    case sll_l:       SLL_L_ADDR(_WZ); break;
    case sll_mhl:     SLL_MHL_ADDR(_WZ); break;
    case sra_a:       SRA_A_ADDR(_WZ); break;
    case sra_b:       SRA_B_ADDR(_WZ); break;
    case sra_c:       SRA_C_ADDR(_WZ); break;
    case sra_d:       SRA_D_ADDR(_WZ); break;
    case sra_e:       SRA_E_ADDR(_WZ); break;
    case sra_h:       SRA_H_ADDR(_WZ); break;
    case sra_l:       SRA_L_ADDR(_WZ); break;
    case sra_mhl:     SRA_MHL_ADDR(_WZ); break;
    case srl_a:       SRL_A_ADDR(_WZ); break;
    case srl_b:       SRL_B_ADDR(_WZ); break;
    case srl_c:       SRL_C_ADDR(_WZ); break;
    case srl_d:       SRL_D_ADDR(_WZ); break;
    case srl_e:       SRL_E_ADDR(_WZ); break;
    case srl_h:       SRL_H_ADDR(_WZ); break;
    case srl_l:       SRL_L_ADDR(_WZ); break;
    case srl_mhl:     SRL_MHL_ADDR(_WZ); break;
  }
}
/*---------------------------------------------------------------------*/


static tVoid z80_pfx_ed(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  z80_pfx_ed
 *
 ***********************************************************************/
{
tZ80* Z80 = NativeCPC->Z80;
tPSG* PSG = NativeCPC->PSG;
tFDC* FDC = NativeCPC->FDC;
tUChar bOpCode;

  bOpCode = read_mem(NativeCPC, _PC++);
#ifndef USE_Z80_TIMING_ADJUST
  Z80->iCycleCount += cc_ed[bOpCode];
#else /* USE_Z80_TIMING_ADJUST */
  Z80->iCycleCount += Z80->cc_ed[bOpCode];
#endif /* USE_Z80_TIMING_ADJUST */
  _R++;

  switch (bOpCode)
  {
    case adc_hl_bc:   ADC16(BC); break;
    case adc_hl_de:   ADC16(DE); break;
    case adc_hl_hl:   ADC16(HL); break;
    case adc_hl_sp:   ADC16(SP); break;
    case cpd:         CPD; break;
    case cpdr:        CPDR; break;
    case cpi:         CPI; break;
    case cpir:        CPIR; break;
    case ed_00:       break;
    case ed_01:       break;
    case ed_02:       break;
    case ed_03:       break;
    case ed_04:       break;
    case ed_05:       break;
    case ed_06:       break;
    case ed_07:       break;
    case ed_08:       break;
    case ed_09:       break;
    case ed_0a:       break;
    case ed_0b:       break;
    case ed_0c:       break;
    case ed_0d:       break;
    case ed_0e:       break;
    case ed_0f:       break;
    case ed_10:       break;
    case ed_11:       break;
    case ed_12:       break;
    case ed_13:       break;
    case ed_14:       break;
    case ed_15:       break;
    case ed_16:       break;
    case ed_17:       break;
    case ed_18:       break;
    case ed_19:       break;
    case ed_1a:       break;
    case ed_1b:       break;
    case ed_1c:       break;
    case ed_1d:       break;
    case ed_1e:       break;
    case ed_1f:       break;
    case ed_20:       break;
    case ed_21:       break;
    case ed_22:       break;
    case ed_23:       break;
    case ed_24:       break;
    case ed_25:       break;
    case ed_26:       break;
    case ed_27:       break;
    case ed_28:       break;
    case ed_29:       break;
    case ed_2a:       break;
    case ed_2b:       break;
    case ed_2c:       break;
    case ed_2d:       break;
    case ed_2e:       break;
    case ed_2f:       break;
    case ed_30:       break;
    case ed_31:       break;
    case ed_32:       break;
    case ed_33:       break;
    case ed_34:       break;
    case ed_35:       break;
    case ed_36:       break;
    case ed_37:       break;
    case ed_38:       break;
    case ed_39:       break;
    case ed_3a:       break;
    case ed_3b:       break;
    case ed_3c:       break;
    case ed_3d:       break;
    case ed_3e:       break;
    case ed_3f:       break;
    case ed_77:       break;
    case ed_7f:       break;
    case ed_80:       break;
    case ed_81:       break;
    case ed_82:       break;
    case ed_83:       break;
    case ed_84:       break;
    case ed_85:       break;
    case ed_86:       break;
    case ed_87:       break;
    case ed_88:       break;
    case ed_89:       break;
    case ed_8a:       break;
    case ed_8b:       break;
    case ed_8c:       break;
    case ed_8d:       break;
    case ed_8e:       break;
    case ed_8f:       break;
    case ed_90:       break;
    case ed_91:       break;
    case ed_92:       break;
    case ed_93:       break;
    case ed_94:       break;
    case ed_95:       break;
    case ed_96:       break;
    case ed_97:       break;
    case ed_98:       break;
    case ed_99:       break;
    case ed_9a:       break;
    case ed_9b:       break;
    case ed_9c:       break;
    case ed_9d:       break;
    case ed_9e:       break;
    case ed_9f:       break;
    case ed_a4:       break;
    case ed_a5:       break;
    case ed_a6:       break;
    case ed_a7:       break;
    case ed_ac:       break;
    case ed_ad:       break;
    case ed_ae:       break;
    case ed_af:       break;
    case ed_b4:       break;
    case ed_b5:       break;
    case ed_b6:       break;
    case ed_b7:       break;
    case ed_bc:       break;
    case ed_bd:       break;
    case ed_be:       break;
    case ed_bf:       break;
    case ed_c0:       break;
    case ed_c1:       break;
    case ed_c2:       break;
    case ed_c3:       break;
    case ed_c4:       break;
    case ed_c5:       break;
    case ed_c6:       break;
    case ed_c7:       break;
    case ed_c8:       break;
    case ed_c9:       break;
    case ed_ca:       break;
    case ed_cb:       break;
    case ed_cc:       break;
    case ed_cd:       break;
    case ed_ce:       break;
    case ed_cf:       break;
    case ed_d0:       break;
    case ed_d1:       break;
    case ed_d2:       break;
    case ed_d3:       break;
    case ed_d4:       break;
    case ed_d5:       break;
    case ed_d6:       break;
    case ed_d7:       break;
    case ed_d8:       break;
    case ed_d9:       break;
    case ed_da:       break;
    case ed_db:       break;
    case ed_dc:       break;
    case ed_dd:       break;
    case ed_de:       break;
    case ed_df:       break;
    case ed_e0:       break;
    case ed_e1:       break;
    case ed_e2:       break;
    case ed_e3:       break;
    case ed_e4:       break;
    case ed_e5:       break;
    case ed_e6:       break;
    case ed_e7:       break;
    case ed_e8:       break;
    case ed_e9:       break;
    case ed_ea:       break;
    case ed_eb:       break;
    case ed_ec:       break;
    case ed_ed:       break;
    case ed_ee:       break;
    case ed_ef:       break;
    case ed_f0:       break;
    case ed_f1:       break;
    case ed_f2:       break;
    case ed_f3:       break;
    case ed_f4:       break;
    case ed_f5:       break;
    case ed_f6:       break;
    case ed_f7:       break;
    case ed_f8:       break;
    case ed_f9:       break;
    case ed_fa:       break;
    case ed_fb:       break;
    case ed_fc:       break;
    case ed_fd:       break;
    case ed_fe:       break;
    case ed_ff:       break;
    case im_0:        _IM = 0; break;
    case im_0_1:      _IM = 0; break;
    case im_0_2:      _IM = 0; break;
    case im_0_3:      _IM = 0; break;
    case im_1:        _IM = 1; break;
    case im_1_1:      _IM = 1; break;
    case im_2:        _IM = 2; break;
    case im_2_1:      _IM = 2; break;
    case ind:         IND_ED; break;
    case indr:        INDR_ED; break;
    case ini:         INI_ED; break;
    case inir:        INIR_ED; break;
    case in_a_c:      IN_REG_C(_A); break;
    case in_b_c:      IN_REG_C(_B); break;
    case in_c_c:      IN_REG_C(_C); break;
    case in_d_c:      IN_REG_C(_D); break;
    case in_e_c:      IN_REG_C(_E); break;
    case in_f_c:      IN_F_C; break;
    case in_h_c:      IN_REG_C(_H); break;
    case in_l_c:      IN_REG_C(_L); break;
    case ldd:         LDD_ED; break;
    case lddr:        LDDR_ED; break;
    case ldi:         LDI_ED; break;
    case ldir:        LDIR_ED; break;
    case ld_a_i:      LD_A_I; break;
    case ld_a_r:      LD_A_R; break;
    case ld_EDbc_mword:  LD16_MEM(BC); break;
    case ld_EDde_mword:  LD16_MEM(DE); break;
    case ld_EDhl_mword:  LD16_MEM(HL); break;
    case ld_EDmword_bc:  LDMEM_16(BC); break;
    case ld_EDmword_de:  LDMEM_16(DE); break;
    case ld_EDmword_hl:  LDMEM_16(HL); break;
    case ld_EDmword_sp:  LDMEM_16(SP); break;
    case ld_EDsp_mword:  LD16_MEM(SP); break;
    case ld_i_a:      LD_I_A; break;
    case ld_r_a:      LD_R_A; break;
    case neg:         NEG; break;
    case neg_1:       NEG; break;
    case neg_2:       NEG; break;
    case neg_3:       NEG; break;
    case neg_4:       NEG; break;
    case neg_5:       NEG; break;
    case neg_6:       NEG; break;
    case neg_7:       NEG; break;
    case otdr:        OTDR_ED; break;
    case otir:        OTIR_ED; break;
    case outd:        OUTD_ED; break;
    case outi:        OUTI_ED; break;
    case out_c_0:     OUT_C_0; break;
    case out_c_a:     OUT_C_REG(_A); break;
    case out_c_b:     OUT_C_REG(_B); break;
    case out_c_c:     OUT_C_REG(_C); break;
    case out_c_d:     OUT_C_REG(_D); break;
    case out_c_e:     OUT_C_REG(_E); break;
    case out_c_h:     OUT_C_REG(_H); break;
    case out_c_l:     OUT_C_REG(_L); break;
    case reti:        _IFF1 = _IFF2; RET; break;
    case reti_1:      _IFF1 = _IFF2; RET; break;
    case reti_2:      _IFF1 = _IFF2; RET; break;
    case reti_3:      _IFF1 = _IFF2; RET; break;
    case retn:        _IFF1 = _IFF2; RET; break;
    case retn_1:      _IFF1 = _IFF2; RET; break;
    case retn_2:      _IFF1 = _IFF2; RET; break;
    case retn_3:      _IFF1 = _IFF2; RET; break;
    case rld:         RLD; break;
    case rrd:         RRD; break;
    case sbc_hl_bc:   SBC16(BC); break;
    case sbc_hl_de:   SBC16(DE); break;
    case sbc_hl_hl:   SBC16(HL); break;
    case sbc_hl_sp:   SBC16(SP); break;
  }
}
/*---------------------------------------------------------------------*/


static tVoid z80_pfx_fd(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  z80_pfx_fd
 *
 ***********************************************************************/
{
tZ80* Z80 = NativeCPC->Z80;
tPSG* PSG = NativeCPC->PSG;
tFDC* FDC = NativeCPC->FDC;
tUChar bOpCode;

  bOpCode = read_mem(NativeCPC, _PC++);
#ifndef USE_Z80_TIMING_ADJUST
  Z80->iCycleCount += cc_xy[bOpCode];
#else /* USE_Z80_TIMING_ADJUST */
  Z80->iCycleCount += Z80->cc_xy[bOpCode];
#endif /* USE_Z80_TIMING_ADJUST */
  _R++;

  switch (bOpCode)
  {
    case adc_a:       ADC(_A); break;
    case adc_b:       ADC(_B); break;
    case adc_byte:    ADC(read_mem(NativeCPC, _PC++)); break;
    case adc_c:       ADC(_C); break;
    case adc_d:       ADC(_D); break;
    case adc_e:       ADC(_E); break;
    case adc_h:       ADC(_IYh); break;
    case adc_l:       ADC(_IYl); break;
    case adc_mhl:     ADC_MIY; break;
    case add_a:       ADD(_A); break;
    case add_b:       ADD(_B); break;
    case add_byte:    ADD(read_mem(NativeCPC, _PC++)); break;
    case add_c:       ADD(_C); break;
    case add_d:       ADD(_D); break;
    case add_e:       ADD(_E); break;
    case add_h:       ADD(_IYh); break;
    case add_hl_bc:   ADD16(IY, BC); break;
    case add_hl_de:   ADD16(IY, DE); break;
    case add_hl_hl:   ADD16(IY, IY); break;
    case add_hl_sp:   ADD16(IY, SP); break;
    case add_l:       ADD(_IYl); break;
    case add_mhl:     ADD_MIY; break;
    case and_a:       AND(_A); break;
    case and_b:       AND(_B); break;
    case and_byte:    AND(read_mem(NativeCPC, _PC++)); break;
    case and_c:       AND(_C); break;
    case and_d:       AND(_D); break;
    case and_e:       AND(_E); break;
    case and_h:       AND(_IYh); break;
    case and_l:       AND(_IYl); break;
    case and_mhl:     AND_MIY; break;
    case call:        CALL; break;
    case call_c:      CALL_C;  break;
    case call_m:      CALL_M;  break;
    case call_nc:     CALL_NC;  break;
    case call_nz:     CALL_NZ;  break;
    case call_p:      CALL_P; break;
    case call_pe:     CALL_PE;  break;
    case call_po:     CALL_PO; break;
    case call_z:      CALL_Z;  break;
    case ccf:         CCF; break;
    case cpl:         CPL; break;
    case cp_a:        CP(_A); break;
    case cp_b:        CP(_B); break;
    case cp_byte:     CP(read_mem(NativeCPC, _PC++)); break;
    case cp_c:        CP(_C); break;
    case cp_d:        CP(_D); break;
    case cp_e:        CP(_E); break;
    case cp_h:        CP(_IYh); break;
    case cp_l:        CP(_IYl); break;
    case cp_mhl:      CP_MIY; break;
    case daa:         DAA; break;
    case dec_a:       DEC(_A); break;
    case dec_b:       DEC(_B); break;
    case dec_bc:      DEC_PAIR(_BC); break;
    case dec_c:       DEC(_C); break;
    case dec_d:       DEC(_D); break;
    case dec_de:      DEC_PAIR(_DE); break;
    case dec_e:       DEC(_E); break;
    case dec_h:       DEC(_IYh); break;
    case dec_hl:      DEC_PAIR(_IY); break;
    case dec_l:       DEC(_IYl); break;
    case dec_mhl:     DEC_MIY; break;
    case dec_sp:      DEC_PAIR(_SP); break;
    case di:          DI; break;
    case djnz:        DJNZ;  break;
    case ei:          EI; break;
    case exx:         EXX; break;
    case ex_af_af:    EX(Z80->Regs.AF, Z80->Regs.AFx); break;
    case ex_de_hl:    EX(Z80->Regs.DE, Z80->Regs.HL); break;
    case ex_msp_hl:   EX_SP(IY); Z80_INC_WS_ADJUST break;
    case halt:        Z80_HALT; break;
    case ina:         INA; break;
    case inc_a:       INC(_A); break;
    case inc_b:       INC(_B); break;
    case inc_bc:      INC_PAIR(_BC); break;
    case inc_c:       INC(_C); break;
    case inc_d:       INC(_D); break;
    case inc_de:      INC_PAIR(_DE); break;
    case inc_e:       INC(_E); break;
    case inc_h:       INC(_IYh); break;
    case inc_hl:      INC_PAIR(_IY); break;
    case inc_l:       INC(_IYl); break;
    case inc_mhl:     INC_MIY; break;
    case inc_sp:      INC_PAIR(_SP); break;
    case jp:          JP; break;
    case jp_c:        JP_C; break;
    case jp_m:        JP_M; break;
    case jp_nc:       JP_NC; break;
    case jp_nz:       JP_NZ; break;
    case jp_p:        JP_P; break;
    case jp_pe:       JP_PE; break;
    case jp_po:       JP_PO; break;
    case jp_z:        JP_Z; break;
    case jr:          JR; break;
    case jr_c:        JR_C; break;
    case jr_nc:       JR_NC; break;
    case jr_nz:       JR_NZ; break;
    case jr_z:        JR_Z; break;
    case ld_a_a:      break;
    case ld_a_b:      _A = _B; break;
    case ld_a_byte:   _A = read_mem(NativeCPC, _PC++); break;
    case ld_a_c:      _A = _C; break;
    case ld_a_d:      _A = _D; break;
    case ld_a_e:      _A = _E; break;
    case ld_a_h:      _A = _IYh; break;
    case ld_a_l:      _A = _IYl; break;
    case ld_a_mbc:    LD_A_MPAIR(_BC); break;
    case ld_a_mde:    LD_A_MPAIR(_DE); break;
    case ld_a_mhl:    LD_REG_MHL_I_(_A, _IY); break;
    case ld_a_mword:  LD_A_MWORD; break;
    case ld_bc_word:  LD_BC_WORD; break;
    case ld_b_a:      _B = _A; break;
    case ld_b_b:      break;
    case ld_b_byte:   _B = read_mem(NativeCPC, _PC++); break;
    case ld_b_c:      _B = _C; break;
    case ld_b_d:      _B = _D; break;
    case ld_b_e:      _B = _E; break;
    case ld_b_h:      _B = _IYh; break;
    case ld_b_l:      _B = _IYl; break;
    case ld_b_mhl:    LD_REG_MHL_I_(_B, _IY); break;
    case ld_c_a:      _C = _A; break;
    case ld_c_b:      _C = _B; break;
    case ld_c_byte:   _C = read_mem(NativeCPC, _PC++); break;
    case ld_c_c:      break;
    case ld_c_d:      _C = _D; break;
    case ld_c_e:      _C = _E; break;
    case ld_c_h:      _C = _IYh; break;
    case ld_c_l:      _C = _IYl; break;
    case ld_c_mhl:    LD_REG_MHL_I_(_C, _IY); break;
    case ld_de_word:  LD_DE_WORD; break;
    case ld_d_a:      _D = _A; break;
    case ld_d_b:      _D = _B; break;
    case ld_d_byte:   _D = read_mem(NativeCPC, _PC++); break;
    case ld_d_c:      _D = _C; break;
    case ld_d_d:      break;
    case ld_d_e:      _D = _E; break;
    case ld_d_h:      _D = _IYh; break;
    case ld_d_l:      _D = _IYl; break;
    case ld_d_mhl:    LD_REG_MHL_I_(_D, _IY); break;
    case ld_e_a:      _E = _A; break;
    case ld_e_b:      _E = _B; break;
    case ld_e_byte:   _E = read_mem(NativeCPC, _PC++); break;
    case ld_e_c:      _E = _C; break;
    case ld_e_d:      _E = _D; break;
    case ld_e_e:      break;
    case ld_e_h:      _E = _IYh; break;
    case ld_e_l:      _E = _IYl; break;
    case ld_e_mhl:    LD_REG_MHL_I_(_E, _IY); break;
    case ld_hl_mword: LD16_MEM(IY); break;
    case ld_hl_word:  LD_IY_WORD; break;
    case ld_h_a:      _IYh = _A; break;
    case ld_h_b:      _IYh = _B; break;
    case ld_h_byte:   _IYh = read_mem(NativeCPC, _PC++); break;
    case ld_h_c:      _IYh = _C; break;
    case ld_h_d:      _IYh = _D; break;
    case ld_h_e:      _IYh = _E; break;
    case ld_h_h:      break;
    case ld_h_l:      _IYh = _IYl; break;
    case ld_h_mhl:    LD_REG_MHL_I_(_H, _IY); break;
    case ld_l_a:      _IYl = _A; break;
    case ld_l_b:      _IYl = _B; break;
    case ld_l_byte:   _IYl = read_mem(NativeCPC, _PC++); break;
    case ld_l_c:      _IYl = _C; break;
    case ld_l_d:      _IYl = _D; break;
    case ld_l_e:      _IYl = _E; break;
    case ld_l_h:      _IYl = _IYh; break;
    case ld_l_l:      break;
    case ld_l_mhl:    LD_REG_MHL_I_(_L, _IY); break;
    case ld_mbc_a:    write_mem(NativeCPC, _BC, _A); break;
    case ld_mde_a:    write_mem(NativeCPC, _DE, _A); break;
    case ld_mhl_a:    LD_MIY_REG(_A); break;
    case ld_mhl_b:    LD_MIY_REG(_B); break;
    case ld_mhl_byte: LD_MIY_BYTE; break;
    case ld_mhl_c:    LD_MIY_REG(_C); break;
    case ld_mhl_d:    LD_MIY_REG(_D); break;
    case ld_mhl_e:    LD_MIY_REG(_E); break;
    case ld_mhl_h:    LD_MIY_REG(_H); break;
    case ld_mhl_l:    LD_MIY_REG(_L); break;
    case ld_mword_a:  LD_MWORD_A; break;
    case ld_mword_hl: LDMEM_16(IY); break;
    case ld_pc_hl:    _PC = _IY; break;
    case ld_sp_hl:    _SP = _IY; Z80_INC_WS_ADJUST break;
    case ld_sp_word:  LD_SP_WORD; break;
    case nop:         break;
    case or_a:        OR(_A); break;
    case or_b:        OR(_B); break;
    case or_byte:     OR(read_mem(NativeCPC, _PC++)); break;
    case or_c:        OR(_C); break;
    case or_d:        OR(_D); break;
    case or_e:        OR(_E); break;
    case or_h:        OR(_IYh); break;
    case or_l:        OR(_IYl); break;
    case or_mhl:      OR_MIY; break;
    case outa:        OUTA; break;
    case pfx_cb:      z80_pfx_fdcb(NativeCPC); break;
    case pfx_dd:      z80_pfx_dd(NativeCPC); break;
    case pfx_ed:      z80_pfx_ed(NativeCPC); break;
    case pfx_fd:      z80_pfx_fd(NativeCPC); break;
    case pop_af:      POP(AF); break;
    case pop_bc:      POP(BC); break;
    case pop_de:      POP(DE); break;
    case pop_hl:      POP(IY); break;
    case push_af:     PUSH(AF); break;
    case push_bc:     PUSH(BC); break;
    case push_de:     PUSH(DE); break;
    case push_hl:     PUSH(IY); break;
    case ret:         RET; break;
    case ret_c:       RET_C; break;
    case ret_m:       RET_M; break;
    case ret_nc:      RET_NC; break;
    case ret_nz:      RET_NZ; break;
    case ret_p:       RET_P; break;
    case ret_pe:      RET_PE; break;
    case ret_po:      RET_PO; break;
    case ret_z:       RET_Z; break;
    case rla:         RLA; break;
    case rlca:        RLCA; break;
    case rra:         RRA; break;
    case rrca:        RRCA; break;
    case rst00:       RST(0x0000); break;
    case rst08:       RST(0x0008); break;
    case rst10:       RST(0x0010); break;
    case rst18:       RST(0x0018); break;
    case rst20:       RST(0x0020); break;
    case rst28:       RST(0x0028); break;
    case rst30:       RST(0x0030); break;
    case rst38:       RST(0x0038); break;
    case sbc_a:       SBC(_A); break;
    case sbc_b:       SBC(_B); break;
    case sbc_byte:    SBC(read_mem(NativeCPC, _PC++)); break;
    case sbc_c:       SBC(_C); break;
    case sbc_d:       SBC(_D); break;
    case sbc_e:       SBC(_E); break;
    case sbc_h:       SBC(_IYh); break;
    case sbc_l:       SBC(_IYl); break;
    case sbc_mhl:     SBC_MIY; break;
    case scf:         SCF; break;
    case sub_a:       SUB(_A); break;
    case sub_b:       SUB(_B); break;
    case sub_byte:    SUB(read_mem(NativeCPC, _PC++)); break;
    case sub_c:       SUB(_C); break;
    case sub_d:       SUB(_D); break;
    case sub_e:       SUB(_E); break;
    case sub_h:       SUB(_IYh); break;
    case sub_l:       SUB(_IYl); break;
    case sub_mhl:     SUB_MIY; break;
    case xor_a:       XOR(_A); break;
    case xor_b:       XOR(_B); break;
    case xor_byte:    XOR(read_mem(NativeCPC, _PC++)); break;
    case xor_c:       XOR(_C); break;
    case xor_d:       XOR(_D); break;
    case xor_e:       XOR(_E); break;
    case xor_h:       XOR(_IYh); break;
    case xor_l:       XOR(_IYl); break;
    case xor_mhl:     XOR_MIY; break;
  }
}
/*---------------------------------------------------------------------*/


static tVoid z80_pfx_fdcb(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  z80_pfx_fdcb
 *
 ***********************************************************************/
{
tZ80* Z80 = NativeCPC->Z80;
tUChar bOpCode;
tLong o;

  o = signed_read_mem(NativeCPC, _PC++); // offset
  _WZ = _IY + o;
  bOpCode = read_mem(NativeCPC, _PC++);
#ifndef USE_Z80_TIMING_ADJUST
  Z80->iCycleCount += cc_xycb[bOpCode];
#else /* USE_Z80_TIMING_ADJUST */
  Z80->iCycleCount += Z80->cc_xycb[bOpCode];
#endif /* USE_Z80_TIMING_ADJUST */

  switch (bOpCode)
  {
    case bit0_a:
    case bit0_b:
    case bit0_c:
    case bit0_d:
    case bit0_e:
    case bit0_h:
    case bit0_l:      BIT_XY(0, read_mem(NativeCPC, _WZ)); break;
    case bit0_mhl:    BIT_HL(0, read_mem(NativeCPC, _WZ)); break;
    case bit1_a:
    case bit1_b:
    case bit1_c:
    case bit1_d:
    case bit1_e:
    case bit1_h:
    case bit1_l:      BIT_XY(1, read_mem(NativeCPC, _WZ)); break;
    case bit1_mhl:    BIT_HL(1, read_mem(NativeCPC, _WZ)); break;
    case bit2_a:
    case bit2_b:
    case bit2_c:
    case bit2_d:
    case bit2_e:
    case bit2_h:
    case bit2_l:      BIT_XY(2, read_mem(NativeCPC, _WZ)); break;
    case bit2_mhl:    BIT_HL(2, read_mem(NativeCPC, _WZ)); break;
    case bit3_a:
    case bit3_b:
    case bit3_c:
    case bit3_d:
    case bit3_e:
    case bit3_h:
    case bit3_l:      BIT_XY(3, read_mem(NativeCPC, _WZ)); break;
    case bit3_mhl:    BIT_HL(3, read_mem(NativeCPC, _WZ)); break;
    case bit4_a:
    case bit4_b:
    case bit4_c:
    case bit4_d:
    case bit4_e:
    case bit4_h:
    case bit4_l:      BIT_XY(4, read_mem(NativeCPC, _WZ)); break;
    case bit4_mhl:    BIT_HL(4, read_mem(NativeCPC, _WZ)); break;
    case bit5_a:
    case bit5_b:
    case bit5_c:
    case bit5_d:
    case bit5_e:
    case bit5_h:
    case bit5_l:      BIT_XY(5, read_mem(NativeCPC, _WZ)); break;
    case bit5_mhl:    BIT_HL(5, read_mem(NativeCPC, _WZ)); break;
    case bit6_a:
    case bit6_b:
    case bit6_c:
    case bit6_d:
    case bit6_e:
    case bit6_h:
    case bit6_l:      BIT_XY(6, read_mem(NativeCPC, _WZ)); break;
    case bit6_mhl:    BIT_HL(6, read_mem(NativeCPC, _WZ)); break;
    case bit7_a:
    case bit7_b:
    case bit7_c:
    case bit7_d:
    case bit7_e:
    case bit7_h:
    case bit7_l:      BIT_XY(7, read_mem(NativeCPC, _WZ)); break;
    case bit7_mhl:    BIT_HL(7, read_mem(NativeCPC, _WZ)); break;
    case res0_a:      RES_A_ADDR(_WZ, 0); break;
    case res0_b:      RES_B_ADDR(_WZ, 0); break;
    case res0_c:      RES_C_ADDR(_WZ, 0); break;
    case res0_d:      RES_D_ADDR(_WZ, 0); break;
    case res0_e:      RES_E_ADDR(_WZ, 0); break;
    case res0_h:      RES_H_ADDR(_WZ, 0); break;
    case res0_l:      RES_L_ADDR(_WZ, 0); break;
    case res0_mhl:    RES_MHL_ADDR(0, _WZ); break;
    case res1_a:      RES_A_ADDR(_WZ, 1); break;
    case res1_b:      RES_B_ADDR(_WZ, 1); break;
    case res1_c:      RES_C_ADDR(_WZ, 1); break;
    case res1_d:      RES_D_ADDR(_WZ, 1); break;
    case res1_e:      RES_E_ADDR(_WZ, 1); break;
    case res1_h:      RES_H_ADDR(_WZ, 1); break;
    case res1_l:      RES_L_ADDR(_WZ, 1); break;
    case res1_mhl:    RES_MHL_ADDR(1, _WZ); break;
    case res2_a:      RES_A_ADDR(_WZ, 2); break;
    case res2_b:      RES_B_ADDR(_WZ, 2); break;
    case res2_c:      RES_C_ADDR(_WZ, 2); break;
    case res2_d:      RES_D_ADDR(_WZ, 2); break;
    case res2_e:      RES_E_ADDR(_WZ, 2); break;
    case res2_h:      RES_H_ADDR(_WZ, 2); break;
    case res2_l:      RES_L_ADDR(_WZ, 2); break;
    case res2_mhl:    RES_MHL_ADDR(2, _WZ); break;
    case res3_a:      RES_A_ADDR(_WZ, 3); break;
    case res3_b:      RES_B_ADDR(_WZ, 3); break;
    case res3_c:      RES_C_ADDR(_WZ, 3); break;
    case res3_d:      RES_D_ADDR(_WZ, 3); break;
    case res3_e:      RES_E_ADDR(_WZ, 3); break;
    case res3_h:      RES_H_ADDR(_WZ, 3); break;
    case res3_l:      RES_L_ADDR(_WZ, 3); break;
    case res3_mhl:    RES_MHL_ADDR(3, _WZ); break;
    case res4_a:      RES_A_ADDR(_WZ, 4); break;
    case res4_b:      RES_B_ADDR(_WZ, 4); break;
    case res4_c:      RES_C_ADDR(_WZ, 4); break;
    case res4_d:      RES_D_ADDR(_WZ, 4); break;
    case res4_e:      RES_E_ADDR(_WZ, 4); break;
    case res4_h:      RES_H_ADDR(_WZ, 4); break;
    case res4_l:      RES_L_ADDR(_WZ, 4); break;
    case res4_mhl:    RES_MHL_ADDR(4, _WZ); break;
    case res5_a:      RES_A_ADDR(_WZ, 5); break;
    case res5_b:      RES_B_ADDR(_WZ, 5); break;
    case res5_c:      RES_C_ADDR(_WZ, 5); break;
    case res5_d:      RES_D_ADDR(_WZ, 5); break;
    case res5_e:      RES_E_ADDR(_WZ, 5); break;
    case res5_h:      RES_H_ADDR(_WZ, 5); break;
    case res5_l:      RES_L_ADDR(_WZ, 5); break;
    case res5_mhl:    RES_MHL_ADDR(5, _WZ); break;
    case res6_a:      RES_A_ADDR(_WZ, 6); break;
    case res6_b:      RES_B_ADDR(_WZ, 6); break;
    case res6_c:      RES_C_ADDR(_WZ, 6); break;
    case res6_d:      RES_D_ADDR(_WZ, 6); break;
    case res6_e:      RES_E_ADDR(_WZ, 6); break;
    case res6_h:      RES_H_ADDR(_WZ, 6); break;
    case res6_l:      RES_L_ADDR(_WZ, 6); break;
    case res6_mhl:    RES_MHL_ADDR(6, _WZ); break;
    case res7_a:      RES_A_ADDR(_WZ, 7); break;
    case res7_b:      RES_B_ADDR(_WZ, 7); break;
    case res7_c:      RES_C_ADDR(_WZ, 7); break;
    case res7_d:      RES_D_ADDR(_WZ, 7); break;
    case res7_e:      RES_E_ADDR(_WZ, 7); break;
    case res7_h:      RES_H_ADDR(_WZ, 7); break;
    case res7_l:      RES_L_ADDR(_WZ, 7); break;
    case res7_mhl:    RES_MHL_ADDR(7, _WZ); break;
    case rlc_a:       RLC_A_ADDR(_WZ); break;
    case rlc_b:       RLC_B_ADDR(_WZ); break;
    case rlc_c:       RLC_C_ADDR(_WZ); break;
    case rlc_d:       RLC_D_ADDR(_WZ); break;
    case rlc_e:       RLC_E_ADDR(_WZ); break;
    case rlc_h:       RLC_H_ADDR(_WZ); break;
    case rlc_l:       RLC_L_ADDR(_WZ); break;
    case rlc_mhl:     RLC_MHL_ADDR(_WZ); break;
    case rl_a:        RL_A_ADDR(_WZ); break;
    case rl_b:        RL_B_ADDR(_WZ); break;
    case rl_c:        RL_C_ADDR(_WZ); break;
    case rl_d:        RL_D_ADDR(_WZ); break;
    case rl_e:        RL_E_ADDR(_WZ); break;
    case rl_h:        RL_H_ADDR(_WZ); break;
    case rl_l:        RL_L_ADDR(_WZ); break;
    case rl_mhl:      RL_MHL_ADDR(_WZ); break;
    case rrc_a:       RRC_A_ADDR(_WZ); break;
    case rrc_b:       RRC_B_ADDR(_WZ); break;
    case rrc_c:       RRC_C_ADDR(_WZ); break;
    case rrc_d:       RRC_D_ADDR(_WZ); break;
    case rrc_e:       RRC_E_ADDR(_WZ); break;
    case rrc_h:       RRC_H_ADDR(_WZ); break;
    case rrc_l:       RRC_L_ADDR(_WZ); break;
    case rrc_mhl:     RRC_MHL_ADDR(_WZ); break;
    case rr_a:        RR_A_ADDR(_WZ); break;
    case rr_b:        RR_B_ADDR(_WZ); break;
    case rr_c:        RR_C_ADDR(_WZ); break;
    case rr_d:        RR_D_ADDR(_WZ); break;
    case rr_e:        RR_E_ADDR(_WZ); break;
    case rr_h:        RR_H_ADDR(_WZ); break;
    case rr_l:        RR_L_ADDR(_WZ); break;
    case rr_mhl:      RR_MHL_ADDR(_WZ); break;
    case set0_a:      SET_A_ADDR(_WZ, 0); break;
    case set0_b:      SET_B_ADDR(_WZ, 0); break;
    case set0_c:      SET_C_ADDR(_WZ, 0); break;
    case set0_d:      SET_D_ADDR(_WZ, 0); break;
    case set0_e:      SET_E_ADDR(_WZ, 0); break;
    case set0_h:      SET_H_ADDR(_WZ, 0); break;
    case set0_l:      SET_L_ADDR(_WZ, 0); break;
    case set0_mhl:    SET_MHL_ADDR(0, _WZ); break;
    case set1_a:      SET_A_ADDR(_WZ, 1); break;
    case set1_b:      SET_B_ADDR(_WZ, 1); break;
    case set1_c:      SET_C_ADDR(_WZ, 1); break;
    case set1_d:      SET_D_ADDR(_WZ, 1); break;
    case set1_e:      SET_E_ADDR(_WZ, 1); break;
    case set1_h:      SET_H_ADDR(_WZ, 1); break;
    case set1_l:      SET_L_ADDR(_WZ, 1); break;
    case set1_mhl:    SET_MHL_ADDR(1, _WZ); break;
    case set2_a:      SET_A_ADDR(_WZ, 2); break;
    case set2_b:      SET_B_ADDR(_WZ, 2); break;
    case set2_c:      SET_C_ADDR(_WZ, 2); break;
    case set2_d:      SET_D_ADDR(_WZ, 2); break;
    case set2_e:      SET_E_ADDR(_WZ, 2); break;
    case set2_h:      SET_H_ADDR(_WZ, 2); break;
    case set2_l:      SET_L_ADDR(_WZ, 2); break;
    case set2_mhl:    SET_MHL_ADDR(2, _WZ); break;
    case set3_a:      SET_A_ADDR(_WZ, 3); break;
    case set3_b:      SET_B_ADDR(_WZ, 3); break;
    case set3_c:      SET_C_ADDR(_WZ, 3); break;
    case set3_d:      SET_D_ADDR(_WZ, 3); break;
    case set3_e:      SET_E_ADDR(_WZ, 3); break;
    case set3_h:      SET_H_ADDR(_WZ, 3); break;
    case set3_l:      SET_L_ADDR(_WZ, 3); break;
    case set3_mhl:    SET_MHL_ADDR(3, _WZ); break;
    case set4_a:      SET_A_ADDR(_WZ, 4); break;
    case set4_b:      SET_B_ADDR(_WZ, 4); break;
    case set4_c:      SET_C_ADDR(_WZ, 4); break;
    case set4_d:      SET_D_ADDR(_WZ, 4); break;
    case set4_e:      SET_E_ADDR(_WZ, 4); break;
    case set4_h:      SET_H_ADDR(_WZ, 4); break;
    case set4_l:      SET_L_ADDR(_WZ, 4); break;
    case set4_mhl:    SET_MHL_ADDR(4, _WZ); break;
    case set5_a:      SET_A_ADDR(_WZ, 5); break;
    case set5_b:      SET_B_ADDR(_WZ, 5); break;
    case set5_c:      SET_C_ADDR(_WZ, 5); break;
    case set5_d:      SET_D_ADDR(_WZ, 5); break;
    case set5_e:      SET_E_ADDR(_WZ, 5); break;
    case set5_h:      SET_H_ADDR(_WZ, 5); break;
    case set5_l:      SET_L_ADDR(_WZ, 5); break;
    case set5_mhl:    SET_MHL_ADDR(5, _WZ); break;
    case set6_a:      SET_A_ADDR(_WZ, 6); break;
    case set6_b:      SET_B_ADDR(_WZ, 6); break;
    case set6_c:      SET_C_ADDR(_WZ, 6); break;
    case set6_d:      SET_D_ADDR(_WZ, 6); break;
    case set6_e:      SET_E_ADDR(_WZ, 6); break;
    case set6_h:      SET_H_ADDR(_WZ, 6); break;
    case set6_l:      SET_L_ADDR(_WZ, 6); break;
    case set6_mhl:    SET_MHL_ADDR(6, _WZ); break;
    case set7_a:      SET_A_ADDR(_WZ, 7); break;
    case set7_b:      SET_B_ADDR(_WZ, 7); break;
    case set7_c:      SET_C_ADDR(_WZ, 7); break;
    case set7_d:      SET_D_ADDR(_WZ, 7); break;
    case set7_e:      SET_E_ADDR(_WZ, 7); break;
    case set7_h:      SET_H_ADDR(_WZ, 7); break;
    case set7_l:      SET_L_ADDR(_WZ, 7); break;
    case set7_mhl:    SET_MHL_ADDR(7, _WZ); break;
    case sla_a:       SLA_A_ADDR(_WZ); break;
    case sla_b:       SLA_B_ADDR(_WZ); break;
    case sla_c:       SLA_C_ADDR(_WZ); break;
    case sla_d:       SLA_D_ADDR(_WZ); break;
    case sla_e:       SLA_E_ADDR(_WZ); break;
    case sla_h:       SLA_H_ADDR(_WZ); break;
    case sla_l:       SLA_L_ADDR(_WZ); break;
    case sla_mhl:     SLA_MHL_ADDR(_WZ); break;
    case sll_a:       SLL_A_ADDR(_WZ); break;
    case sll_b:       SLL_B_ADDR(_WZ); break;
    case sll_c:       SLL_C_ADDR(_WZ); break;
    case sll_d:       SLL_D_ADDR(_WZ); break;
    case sll_e:       SLL_E_ADDR(_WZ); break;
    case sll_h:       SLL_H_ADDR(_WZ); break;
    case sll_l:       SLL_L_ADDR(_WZ); break;
    case sll_mhl:     SLL_MHL_ADDR(_WZ); break;
    case sra_a:       SRA_A_ADDR(_WZ); break;
    case sra_b:       SRA_B_ADDR(_WZ); break;
    case sra_c:       SRA_C_ADDR(_WZ); break;
    case sra_d:       SRA_D_ADDR(_WZ); break;
    case sra_e:       SRA_E_ADDR(_WZ); break;
    case sra_h:       SRA_H_ADDR(_WZ); break;
    case sra_l:       SRA_L_ADDR(_WZ); break;
    case sra_mhl:     SRA_MHL_ADDR(_WZ); break;
    case srl_a:       SRL_A_ADDR(_WZ); break;
    case srl_b:       SRL_B_ADDR(_WZ); break;
    case srl_c:       SRL_C_ADDR(_WZ); break;
    case srl_d:       SRL_D_ADDR(_WZ); break;
    case srl_e:       SRL_E_ADDR(_WZ); break;
    case srl_h:       SRL_H_ADDR(_WZ); break;
    case srl_l:       SRL_L_ADDR(_WZ); break;
    case srl_mhl:     SRL_MHL_ADDR(_WZ); break;
  }
}
/*---------------------------------------------------------------------*/


tULong z80_IN_handler(tNativeCPC* NativeCPC,
                      tRegister port)
/***********************************************************************
 *
 *  z80_IN_handler
 *
 ***********************************************************************/
{
tULong ret_val;

  ret_val = 0xff; // default return value

  // CRTC ----------------------------------------------------------------------
  if (!(port.UChar.h & 0x40)) // A14=0 => CRTC chip selected
  {
    if ((port.UChar.h & 3) == 3) // read CRTC register?
    {
#ifndef CRTC_ENGINE_V2
      ret_val &= video_get_CRTC_Register(NativeCPC,
																				 NativeCPC->CRTC->reg_select);
#else /* !CRTC_ENGINE_V2 */
			ret_val &= NativeCPC->GetCRTCRegisterPtr(NativeCPC,
                                               NativeCPC->CRTC->reg_select);
#endif /* !CRTC_ENGINE_V2 */
    }
#ifdef CRTC_ENGINE_V2
    else if ((port.UChar.h & 3) == 2) // read CRTC status?
    {
      ret_val &= NativeCPC->GetCRTCStatusPtr(NativeCPC);
    }
#endif /* CRTC_ENGINE_V2 */
  }

  // PPI -----------------------------------------------------------------------
  if (!(port.UChar.h & 0x08)) // A11=0 => PPI CS/
	{
		ret_val &= ppi_read(NativeCPC, port);
  }

  // External peripherals ------------------------------------------------------
  if (!(port.UChar.h & 0x04))
  {
    // FDC ---------------------------------------------------------------------
    if ((port.UChar.h == 0xfb) && (!(port.UChar.l & 0x80))) // A10=0 AND A7=0 => FDC selected
    {
      if (NativeCPC->floppy_drive)
			{
        if (!(port.UChar.l & 0x01)) // A0=0 => Read main status register
        {
					ret_val &= NativeCPC->FDC->read_status_fct(NativeCPC->FDC);
				}
				else // A0=1 => Read data register
				{
					ret_val &= NativeCPC->FDC->read_data_fct(NativeCPC->FDC);
				}
      }
    }

#ifndef CAPRICE_4_2_ENGINE
    // From Caprice Reloaded
    else if( (port.UChar.h == 0xfa) && (port.UChar.l == 0xfe) )
		{
      if (NativeCPC->floppy_drive)
      {
				ret_val &= NativeCPC->FDC->read_status_fct(NativeCPC->FDC);
			}
		}
#endif /* !CAPRICE_4_2_ENGINE */

#ifdef ENABLE_TMPI_SPEECH
    // TMPI SPEECH SYNTHESIS ---------------------------------------------------
    else if ((port.UChar.h == 0xfb) && (port.UChar.l == 0xff)) // A10=0 => TMPI selected
    {
      if (NativeCPC->TMPISpeech->enabled)
      {
				ret_val &= tmpi_read_status(NativeCPC->TMPISpeech);
      }
    }
#endif /* ENABLE_TMPI_SPEECH */

  }

#ifdef USE_XMASS
	if ( ((port.UShort.l & 0xfff8) == 0xfd08) && (NativeCPC->IDE->connected) )
	{
		ret_val &= ide_read(NativeCPC, (port.UChar.l & 7));
	}
#endif /* USE_XMASS */

#ifdef ENABLE_SYMBIFACE_MOUSE
	// Symbiface mouse port ------------------------------------------------------
	if (port.UShort.l == 0xfd10)
	{
		if (NativeCPC->symbiface_mouse_active)
		{
			// X offset available ?
			if (NativeCPC->symbiface_mouse_X_offset)
			{
				ret_val = SYMBIFACE_MOUSE_OFFSET_X | (NativeCPC->symbiface_mouse_X_offset & SYMBIFACE_MOUSE_OFFSET_MASK);
				NativeCPC->symbiface_mouse_X_offset = 0;
			}
			// Y offset available ?
			else if (NativeCPC->symbiface_mouse_Y_offset)
			{
				ret_val = SYMBIFACE_MOUSE_OFFSET_Y | (NativeCPC->symbiface_mouse_Y_offset & SYMBIFACE_MOUSE_OFFSET_MASK);
				NativeCPC->symbiface_mouse_Y_offset = 0;
			}
			// State Changes ?
			else if (NativeCPC->symbiface_mouse_state ^ NativeCPC->symbiface_mouse_prev_state)
			{
				ret_val &= (tUChar)NativeCPC->symbiface_mouse_state;
				NativeCPC->symbiface_mouse_prev_state = NativeCPC->symbiface_mouse_state;
			}
			else
			{
				ret_val = SYMBIFACE_MOUSE_IDLE;
			}
		}
		else
		{
			ret_val = SYMBIFACE_MOUSE_IDLE;
		}
	}
#endif /* ENABLE_SYMBIFACE_MOUSE */

#ifdef ENABLE_MULTIPLAY_MOUSE
	// Multiplay mouse port ------------------------------------------------------
	if ((port.UShort.l & 0xfff0) == 0xf990)
	{
		if (NativeCPC->multiplay_mouse_A_active)
		{
			// Action bits
			if (port.UShort.l == 0xf990)
			{
				ret_val &= NativeCPC->multiplay_mouse_state;
			}
			// Mouse A Pos-X
			else if (port.UShort.l == 0xf992)
			{
				ret_val &= NativeCPC->multiplay_mouse_X_offset;
				NativeCPC->multiplay_mouse_X_offset = 0;
			}
			// Mouse A Pos-Y
			else if (port.UShort.l == 0xf993)
			{
				ret_val &= NativeCPC->multiplay_mouse_Y_offset;
				NativeCPC->multiplay_mouse_Y_offset = 0;
			}
		}
	}
#endif /* ENABLE_MULTIPLAY_MOUSE */


#ifdef ENABLE_METROLOGY
  if (NativeCPC->enable_ROM_operations)
  {
    // Filtered Upper ROM enabled
    // PC located into Upper ROM
    if ( (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)
         && (NativeCPC->ROM_operations_upper_ROM == NativeCPC->GateArray->upper_ROM)
         && (((NativeCPC->_PC >> 14) & 3) == 3) )
    {
      NativeCPC->signal_ROM_external_operation++;
      NativeCPC->ROM_operations_direction = 0; // IN
      NativeCPC->ROM_operations_port = port.ULong;
      NativeCPC->ROM_operations_data = ret_val;
    }
  }
#endif /* ENABLE_METROLOGY */

  return ret_val;
}
/*---------------------------------------------------------------------*/



#define Z80_OUT_HANDLER_PPI_TAPE \
{ \
  /* Relay command changed ? */ \
  if (NativeCPC->Tape->tape_motor != (PPI->portC & 0x10)) \
  { \
    NativeCPC->Tape->bPPICommand = (PPI->portC & 0x10); \
    /* start relay change counter */ \
    if (!NativeCPC->Tape->motor_change_counter) \
    { \
      NativeCPC->Tape->motor_change_counter_init = \
        (PPI->portC & 0x10) ? (tLong)TAPE_CYCLES_ACTIVATION : (tLong)TAPE_CYCLES_DEACTIVATION;\
      \
      NativeCPC->Tape->motor_change_counter = NativeCPC->Tape->motor_change_counter_init;\
    } \
  } \
}

tVoid z80_OUT_handler(tNativeCPC* NativeCPC,
                      tRegister port,
                      tULong val)
/***********************************************************************
 *
 *  z80_OUT_handler
 *
 ***********************************************************************/
{
#ifdef ENABLE_METROLOGY
  if (NativeCPC->enable_ROM_operations)
  {
    // Filtered Upper ROM enabled
    // PC located into Upper ROM
    if ( (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)
         && (NativeCPC->ROM_operations_upper_ROM == NativeCPC->GateArray->upper_ROM)
         && (((NativeCPC->_PC >> 14) & 3) == 3) )
    {
      NativeCPC->signal_ROM_external_operation++;
      NativeCPC->ROM_operations_direction = 1; // OUT
      NativeCPC->ROM_operations_port = port.ULong;
      NativeCPC->ROM_operations_data = val;
    }
  }
#endif /* ENABLE_METROLOGY */

#ifdef USE_RAM_4MB
  // 4MB extented memory
  if (NativeCPC->prefP->Use4MMemoryExtension)
  {
    if ( ((port.UChar.h & 0xf8) == 0x78) && ((val & 0xc0) == 0xc0) )
    {
      // Set 512Ko banks offset
      NativeCPC->ram_4mb_new_offset = (7 - (port.UChar.h & 7)) * 524288;
    }
  }
#endif /* USE_RAM_4MB */

#ifdef USE_PAL_MEMORY
#ifndef CAPRICE_4_2_ENGINE
  // Memory PAL
  if ((port.UChar.h & 0x80) == 0)
  {
    // set memory configuration
	  if ((val >> 6) == 3)
	  {
      NativeCPC->GateArray->RAM_config = val;
      ga_memory_manager(NativeCPC);
	  }
  }
#endif /* !CAPRICE_4_2_ENGINE */
#endif /* USE_PAL_MEMORY */

  // Gate Array ----------------------------------------------------------------
  // GA chip select?
  if ((port.UChar.h & 0xc0) == 0x40)
  {
    tGateArray* GateArray = NativeCPC->GateArray;
#ifndef CRTC_ENGINE_V2
    tCRTC* CRTC = NativeCPC->CRTC;
#endif /* !CRTC_ENGINE_V2 */

    switch (val >> 6)
    {
      case 0: // select pen
      {
        GateArray->pen = val & 0x10 ? 0x10 : val & 0x0f; // if bit 5 is set, pen indexes the border colour
#ifdef ENABLE_MULTIFACE2
        if (NativeCPC->pbMF2ROM)
        {
          // Gate-Array pen index write (7fxx with bit 7=0 and bit 6=0): 3fcf
          *(NativeCPC->pbMF2ROM + 0x03fcf) = (tUChar)val;
        }
#endif /* ENABLE_MULTIFACE2 */
      }
      break;

      case 1: // set colour
      {
        tUChar colour = val & 0x1f; // isolate colour value
#ifndef PALETTE_PATCH_1
				GateArray->ink_values[GateArray->pen] = colour;
				GateArray->palette[GateArray->pen] = NativeCPC->colours[colour]; // isolate colour value
#else /* PALETTE_PATCH_1 */
				if (!(GateArray->pen & 0x10))
				{
					GateArray->newcolor = (tULong)colour;
					GateArray->newcolorcount = 8; // 8 pixels delay
#ifdef PALETTE_PATCH_2
					if ((port.UChar.h & 0x28) == 0x08)
					{
						GateArray->newcolorcount += 6; // Add extra pixels delay
					}
#endif /* PALETTE_PATCH_2 */
				}
				else
				{
					GateArray->ink_values[GateArray->pen] = colour;
					GateArray->palette[GateArray->pen] = NativeCPC->colours[colour]; // isolate colour value
				}
#endif /* PALETTE_PATCH_1 */

#ifdef ENABLE_METROLOGY
				GateArray->signal_palette_update++;
#endif /* ENABLE_METROLOGY */

#ifndef CRTC_ENGINE_V2
        // Prepare border rendering
        if (GateArray->pen == 0x10)
        {
          tULong border = GateArray->palette[0x10];
          border |= border << 8;
          border |= border << 16;
          NativeCPC->VDU->border = border;
        }

        // Antialiased mode 2
        if ( (CRTC->current_mode == 2) &&
             (NativeCPC->prefP->Mode2AntiAliasing) )
        {
          tULong back_index = GateArray->palette[0];
          tULong bright_index = GateArray->palette[1];
          tULong shadowIndex;

#if defined(__PALMOS__)
          // Is dark really darker ?
          if ( NativeCPC->PalmPaletteIntensity[back_index] >
               NativeCPC->PalmPaletteIntensity[bright_index] )
          {
            bright_index = back_index;
          }

          // Prepare medium color
          shadowIndex = NativeCPC->active_colours[NativeCPC->PalmToCPCPalette[bright_index]].shadow_index;
          NativeCPC->VDU->antialiased_color = shadowIndex > 31 ? back_index : NativeCPC->colours[shadowIndex];
#else /* __PALMOS__ */
          // Is dark really darker ?
          if ( NativeCPC->WinPalettePtr[back_index].intensity >
               NativeCPC->WinPalettePtr[bright_index].intensity )
          {
            bright_index = back_index;
          }

          // Prepare medium color
          shadowIndex = NativeCPC->WinPalettePtr[bright_index].shadow_index;
          NativeCPC->VDU->antialiased_color = shadowIndex > 31 ? back_index : NativeCPC->colours[shadowIndex];
#endif /* __PALMOS__ */
        }
#endif /* !CRTC_ENGINE_V2 */

#ifdef ENABLE_MULTIFACE2
        if (NativeCPC->pbMF2ROM)
        {
          tUChar iPen = *(NativeCPC->pbMF2ROM + 0x03fcf);
          // Border ?
          if (iPen == 0x10)
          {
            // Gate-Array border colour write (7fxx with bit 7=0 and bit 6=1): 3fd0
            *(NativeCPC->pbMF2ROM + 0x03fd0) = (tUChar)val;
          }
          else
          {
            // Gate-Array pen colour write (7fxx with bit 7=0 and bit 6=1): 3f90-3f9f (pens 0 to 15)
            *(NativeCPC->pbMF2ROM + 0x03f90 + (iPen & 0x0f)) = (tUChar)val;
          }
        }
#endif /* ENABLE_MULTIFACE2 */
      }
      break;

      case 2: // set mode
      {
        GateArray->ROM_config = val;
#ifndef CRTC_ENGINE_V2
        CRTC->requested_mode = val & 0x03;
        CRTC->RequestedHandler = CRTC->DrawHandler[val & 0x03];
#else /* !CRTC_ENGINE_V2 */
        GateArray->requested_scr_mode = val & 0x03; // request a new CPC screen mode
#endif /* !CRTC_ENGINE_V2 */
        ga_memory_manager(NativeCPC);
        if (val & 0x10) // delay Z80 interrupt?
        {
          NativeCPC->Z80->Regs.int_pending = 0;  // clear pending interrupts
          GateArray->sl_count = 0; // reset GA scanline counter
        }
#ifdef ENABLE_MULTIFACE2
        if (NativeCPC->pbMF2ROM)
        {
          // Gate-Array mode write (7fxx with bit 7=1 and bit 6=0): 3fef
          *(NativeCPC->pbMF2ROM + 0x03fef) = (tUChar)val;
        }
#endif /* ENABLE_MULTIFACE2 */
      }
      break;

      case 3: // set memory configuration
      {
        GateArray->RAM_config = val;
        ga_memory_manager(NativeCPC);
#ifdef ENABLE_MULTIFACE2
        if (NativeCPC->pbMF2ROM)
        {
          // PAL write (7fxx with bit 7=1 and bit 6=1): 3fff
          *(NativeCPC->pbMF2ROM + 0x03fff) = (tUChar)val;
        }
#endif /* ENABLE_MULTIFACE2 */
      }
      break;
    }
  }

  // CRTC ----------------------------------------------------------------------
  if (!(port.UChar.h & 0x40)) // A14=0 => CRTC chip selected
  {
    tCRTC* CRTC = NativeCPC->CRTC;
    tULong crtc_port;

    // A9 = CRTC R/W/
    // A8 = CRTC RS (Register select)
    crtc_port = port.UChar.h & 3; // Check A9 and A8
    if (crtc_port == 0) // CRTC register select?
    {
      CRTC->reg_select = val & 0x1f;
#ifdef ENABLE_MULTIFACE2
      if (NativeCPC->pbMF2ROM)
      {
        // CRTC register select write (bcxx): 3cff
        *(NativeCPC->pbMF2ROM + 0x03cff) = (tUChar)val;
      }
#endif /* ENABLE_MULTIFACE2 */
    }
    else if (crtc_port == 1) // CRTC write data?
    {
      if (CRTC->reg_select < 16) // only registers 0-15 can be written to
      {
#ifdef ENABLE_METROLOGY
        CRTC->signal_register_update++;
#endif /* ENABLE_METROLOGY */

#ifndef CRTC_ENGINE_V2
        video_set_CRTC_Register(NativeCPC,
                                CRTC->reg_select,
                                val);
#else /* !CRTC_ENGINE_V2 */
        NativeCPC->SetCRTCRegisterPtr(NativeCPC,
                                      CRTC->reg_select,
                                      val);
#endif /* !CRTC_ENGINE_V2 */
#ifdef ENABLE_MULTIFACE2
        if (NativeCPC->pbMF2ROM)
        {
          // CRTC register data (bdxx): 3db0-3dbf (only registers 0-15, others are ignored)
          *(NativeCPC->pbMF2ROM + 0x03db0 + (*(NativeCPC->pbMF2ROM + 0x03cff) & 0x0f)) = (tUChar)val;
        }
#endif /* ENABLE_MULTIFACE2 */
      }
    }
  }

  // ROM select ----------------------------------------------------------------
  if (!(port.UChar.h & 0x20)) // A13=0 => ROM select
  {
    tGateArray* GateArray = NativeCPC->GateArray;

    GateArray->upper_ROM = val;
		NativeCPC->pbExpansionROM = NativeCPC->memmap_ROM[val];
		if (NativeCPC->pbExpansionROM == cNull) // selected expansion ROM not present?
		{
			NativeCPC->pbExpansionROM = NativeCPC->pbROMhi; // revert to BASIC ROM
		}
		if (!(GateArray->ROM_config & 0x08)) // upper/expansion ROM is enabled?
		{
#ifdef USE_XMEM
			if (NativeCPC->XMEM_read_id)
			{
				NativeCPC->pbExpansionROM = NativeCPC->pbXMEMID;
			}
			else if ( (NativeCPC->prefP->UseXMEM) && (val & 0x40) )
			{
				NativeCPC->pbExpansionROM = NativeCPC->pbXMEMROM + ((val & 0x1F) * ROM_SIZE);
			}
#endif /* USE_XMEM */

      NativeCPC->membank_read[3] = NativeCPC->pbExpansionROM; // 'page in' upper/expansion ROM
    }
#ifdef CAPRICE_X64_ENGINE
#ifdef ENABLE_MULTIFACE2
    if (NativeCPC->pbMF2ROM)
    {
      *(NativeCPC->pbMF2ROM + 0x03aac) = (tUChar)val;
    }
#endif /* ENABLE_MULTIFACE2 */
#endif /* CAPRICE_X64_ENGINE */
  }

  // printer port --------------------------------------------------------------
  if (!(port.UChar.h & 0x10)) // A12=0 => printer port
  {
    NativeCPC->printer_port = val ^ 0x80; // invert STROBE

#ifdef ENABLE_DIGIBLASTER
    if (NativeCPC->Digiblaster->enabled)
    {
      digiblaster_write_data(NativeCPC->Digiblaster, NativeCPC->printer_port);
    }
    else
#endif /* ENABLE_DIGIBLASTER */
    {
#ifdef ENABLE_PRINTER
      printer_receivedata(NativeCPC->Printer, NativeCPC->printer_port);
#endif /* ENABLE_PRINTER */
    }
  }

  // PPI -----------------------------------------------------------------------
  if (!(port.UChar.h & 0x08)) // A11=0 => PPI CS/
	{
		ppi_write(NativeCPC, port, val);
	}

  // FDC -----------------------------------------------------------------------
  // floppy motor control? (FA7E)
#ifndef CAPRICE_X64_ENGINE
  if ((!(port.UChar.h & 0x05)) && (!(port.UChar.l & 0x80)))
#else /* !CAPRICE_X64_ENGINE */
  if ((port.UChar.h == 0xfa) && (!(port.UChar.l & 0x80)))
#endif /* !CAPRICE_X64_ENGINE */
  {
    if (NativeCPC->floppy_drive)
    {
#ifdef ENABLE_METROLOGY
      NativeCPC->FDC->signal_updated++;
      NativeCPC->FDC->signal_motor_change++;
#endif /* ENABLE_METROLOGY */

      if (NativeCPC->FDC->cmd_motor_fct(NativeCPC->FDC, val) == cTrue)
      {
#ifdef ENABLE_METROLOGY
        NativeCPC->FDC->motor_on_time = NativeCPC->timer_from_reset;
#endif /* ENABLE_METROLOGY */
      }

    }
  }
	// FDC data register? (FB7F)
  if ((port.UChar.h == 0xfb) && (!(port.UChar.l & 0x80)))
  {
	if (NativeCPC->floppy_drive)
	{
	  NativeCPC->FDC->write_data_fct(NativeCPC->FDC, val);
	}
  }

#ifdef ENABLE_TMPI_SPEECH
  // TMPI SPEECH SYNTHESIS -----------------------------------------------------
  if (port.UChar.h == 0xfb)
  {
    if (NativeCPC->TMPISpeech->enabled)
    {
      if (port.UChar.l == 0xfe)
      {
        tmpi_write_data(NativeCPC->TMPISpeech, val);
      }
      else if (port.UChar.l == 0xff)
      {
        tmpi_write_command(NativeCPC->TMPISpeech, val);
      }
    }
  }
#endif /* ENABLE_TMPI_SPEECH */

#ifdef ENABLE_PLAYCITY
  // PLAYCITY ------------------------------------------------------------------
  if (port.UChar.h == 0xf9)
  {
    tPlayCity* PlayCity = NativeCPC->PlayCity;

    if (PlayCity->enabled)
		{
			// Right channel
			if ((port.UChar.l & 0x84) == 0x84)
			{
				PlayCity->PSG_Right->reg_select = val;
			}
			// Left channel
			if ((port.UChar.l & 0x88) == 0x88)
			{
				PlayCity->PSG_Left->reg_select = val;
			}
		}
  }
	else if (port.UChar.h == 0xf8)
  {
    tPlayCity* PlayCity = NativeCPC->PlayCity;

    if (PlayCity->enabled)
		{
			// RESET
			if (port.UChar.l == 0xff)
			{
				PlayCity_reset(PlayCity);
				PlayCity_audio_init(PlayCity);
			}
			else if ((port.UChar.l & 0x80) == 0x80)
			{
				// Right channel
				if ((port.UChar.l & 0x0F) & 4)
				{
					audio_set_AY_Register(PlayCity->PSG_Right,
																PlayCity->PSG_Right->reg_select,
																val);
				}
				// Left channel
				if ((port.UChar.l & 0x0F) & 8)
				{
					audio_set_AY_Register(PlayCity->PSG_Left,
																PlayCity->PSG_Left->reg_select,
																val);
				}

				// CTC Channel 0
				if ((port.UChar.l & 0x0F) == 0)
				{
					PlayCity_receive_channel_0(PlayCity, val);
				}
				// CTC Channel 1
				else if ((port.UChar.l & 0x0F) == 1)
				{
					PlayCity_receive_channel_1_2_3(PlayCity, CTC_CHANNEL1, val);
				}
				// CTC Channel 2
				else if ((port.UChar.l & 0x0F) == 2)
				{
					PlayCity_receive_channel_1_2_3(PlayCity, CTC_CHANNEL2, val);
				}
				// CTC Channel 3
				else if ((port.UChar.l & 0x0F) == 3)
				{
					PlayCity_receive_channel_1_2_3(PlayCity, CTC_CHANNEL3, val);
				}
			}
    }
  }
#endif /* ENABLE_PLAYCITY */

#ifdef ENABLE_MULTIFACE2
  // Multiface 2?
  if ( (NativeCPC->pbMF2ROM) && (port.UChar.h == 0xfe) )
  {
    // page in MF2 ROM?
    if ((port.UChar.l == 0xe8) && (!(NativeCPC->mf2_flags & MF2_INVISIBLE)))
    {
      NativeCPC->mf2_flags |= MF2_ACTIVE;
      ga_memory_manager(NativeCPC);
    }
    // page out MF2 ROM?
    else if (port.UChar.l == 0xea)
    {
      NativeCPC->mf2_flags &= ~MF2_ACTIVE;
      ga_memory_manager(NativeCPC);
    }
  }
#endif /* ENABLE_MULTIFACE2 */

#ifdef USE_XMASS
	if ( ((port.UShort.l & 0xfff8) == 0xfd08) && (NativeCPC->IDE->connected) )
	{
		ide_write(NativeCPC, (port.UChar.l & 7), val);
	}
#endif /* USE_XMASS */
}
/*---------------------------------------------------------------------*/


static tVoid ga_init(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  ga_init
 *
 ***********************************************************************/
{
tGateArray* GateArray = NativeCPC->GateArray;

  GateArray->BasicColorsP = BasicColors;
  GateArray->GateArrayColorsP = GateArrayColors;
}


static tVoid ga_memory_manager(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  ga_memory_manager
 *
 ***********************************************************************/
{
tGateArray* GateArray = NativeCPC->GateArray;
tULong mem_bank;
tULong mem_bank_mask;
tULong n;

  // RAM bank not used ?
  mem_bank = (GateArray->RAM_config >> 3) & 7; // extract expansion memory bank
  mem_bank_mask = 1 << mem_bank;
  if (!(NativeCPC->rambank_used & mem_bank_mask))
  {
    mem_bank = 0; // force default mapping
    if (NativeCPC->ram_size == 64)
    {
      GateArray->RAM_config = 0; // the only valid configuration is 0
    }
  }

#ifndef USE_RAM_4MB
  if (mem_bank != GateArray->RAM_bank) // requested bank is different from the active one?
  {
    GateArray->RAM_bank = mem_bank;
    ga_init_banking(NativeCPC);
  }
#else /* USE_RAM_4MB */
  // requested bank is different from the active one ?
  if ( (mem_bank != GateArray->RAM_bank)
       || (NativeCPC->ram_4mb_new_offset != NativeCPC->ram_4mb_offset) )
  {
    NativeCPC->ram_4mb_offset = NativeCPC->ram_4mb_new_offset;
    GateArray->RAM_bank = mem_bank;
    ga_init_banking(NativeCPC);
  }
#endif /* USE_RAM_4MB */

  for (n = 0; n < 4; n++) // remap active memory banks
  {
		NativeCPC->membank_read[n] = NativeCPC->membank_config[GateArray->RAM_config & 7][n];
    NativeCPC->membank_write[n] = NativeCPC->membank_read[n];
  }

  if (!(GateArray->ROM_config & 0x04)) // lower ROM is enabled?
  {
    NativeCPC->membank_read[0] = NativeCPC->pbROMlo; // 'page in' lower ROM
  }

#ifdef ENABLE_MULTIFACE2
  if ( NativeCPC->pbMF2ROM && (NativeCPC->mf2_flags & MF2_ACTIVE) ) // is the Multiface 2 paged in?
  {
    NativeCPC->membank_read[0] = NativeCPC->pbMF2ROM;
    NativeCPC->membank_write[0] = NativeCPC->pbMF2ROM;
  }
#endif /* ENABLE_MULTIFACE2 */

  if (!(GateArray->ROM_config & 0x08)) // upper/expansion ROM is enabled?
  {
    NativeCPC->membank_read[3] = NativeCPC->pbExpansionROM; // 'page in' upper/expansion ROM

#ifdef USE_XMEM
		if (NativeCPC->prefP->UseXMEM)
		{
			if ((GateArray->RAM_config & 7) == 3)
			{
				NativeCPC->membank_read[1] = NativeCPC->pbExpansionROM;
			}
		}
#endif /* USE_XMEM */
	}
}
/*---------------------------------------------------------------------*/


static tVoid ga_init_banking(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  ga_init_banking
 *
 ***********************************************************************/
{
tUChar *romb0, *romb1, *romb2, *romb3, *romb4, *romb5, *romb6, *romb7;
tUChar* pbRAMbank;

  romb0 = NativeCPC->pbRAM;
  romb1 = romb0 + 16384;
  romb2 = romb1 + 16384;
  romb3 = romb2 + 16384;

#ifndef USE_RAM_4MB
  pbRAMbank = NativeCPC->pbRAM + ((NativeCPC->GateArray->RAM_bank + 1) * 65536);
#else /* USE_RAM_4MB */
  pbRAMbank = NativeCPC->pbRAM
              + 65536 /* First 64Ko */
              + NativeCPC->ram_4mb_offset
              + (NativeCPC->GateArray->RAM_bank * 65536);
#endif /* USE_RAM_4MB */

  romb4 = pbRAMbank;
  romb5 = romb4 + 16384;
  romb6 = romb5 + 16384;
  romb7 = romb6 + 16384;

  NativeCPC->membank_config[0][0] = romb0;
  NativeCPC->membank_config[0][1] = romb1;
  NativeCPC->membank_config[0][2] = romb2;
  NativeCPC->membank_config[0][3] = romb3;

  NativeCPC->membank_config[1][0] = romb0;
  NativeCPC->membank_config[1][1] = romb1;
  NativeCPC->membank_config[1][2] = romb2;
  NativeCPC->membank_config[1][3] = romb7;

  NativeCPC->membank_config[2][0] = romb4;
  NativeCPC->membank_config[2][1] = romb5;
  NativeCPC->membank_config[2][2] = romb6;
  NativeCPC->membank_config[2][3] = romb7;

  NativeCPC->membank_config[3][0] = romb0;
  NativeCPC->membank_config[3][1] = romb3;
  NativeCPC->membank_config[3][2] = romb2;
  NativeCPC->membank_config[3][3] = romb7;

  NativeCPC->membank_config[4][0] = romb0;
  NativeCPC->membank_config[4][1] = romb4;
  NativeCPC->membank_config[4][2] = romb2;
  NativeCPC->membank_config[4][3] = romb3;

  NativeCPC->membank_config[5][0] = romb0;
  NativeCPC->membank_config[5][1] = romb5;
  NativeCPC->membank_config[5][2] = romb2;
  NativeCPC->membank_config[5][3] = romb3;

  NativeCPC->membank_config[6][0] = romb0;
  NativeCPC->membank_config[6][1] = romb6;
  NativeCPC->membank_config[6][2] = romb2;
  NativeCPC->membank_config[6][3] = romb3;

  NativeCPC->membank_config[7][0] = romb0;
  NativeCPC->membank_config[7][1] = romb7;
  NativeCPC->membank_config[7][2] = romb2;
  NativeCPC->membank_config[7][3] = romb3;
}
/*---------------------------------------------------------------------*/


static tBool IsBreakpointDetected(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  DetectBreakpoints
 *
 ***********************************************************************/
{
tULong Loop = NativeCPC->nb_breakpoints;
tBreakpoint* BreakpointP = NativeCPC->Breakpoints;
tZ80* Z80 = NativeCPC->Z80;
tULong LowerROM = ((NativeCPC->GateArray->ROM_config & 0x04) == 0) ? 1U : 0U;
tULong UpperROM = ((NativeCPC->GateArray->ROM_config & 0x08) == 0) ? 1U : 0U;
tULong UpperROMIndex = NativeCPC->GateArray->upper_ROM;

	while (Loop--)
	{
		if (_PCdword == BreakpointP->pc)
		{
			if ( (LowerROM) && (_PCdword < 0x4000) )
			{
				if (BreakpointP->lowerROM)
				{
					return cTrue;
				}
			}
			else if ( (UpperROM) && (_PCdword >= 0xc000) )
			{
				if ( (BreakpointP->upperROM) && (UpperROMIndex == BreakpointP->upperROMindex) )
				{
					return cTrue;
				}
			}
			else
			{
				return cTrue;
			}
		}
		else if (NativeCPC->memread_addr == BreakpointP->mem_read)
		{
			if ( (LowerROM) && (NativeCPC->memread_addr < 0x4000) )
			{
				if (BreakpointP->lowerROM)
				{
					return cTrue;
				}
			}
			else if ( (UpperROM) && (NativeCPC->memread_addr >= 0xc000) )
			{
				if ( (BreakpointP->upperROM) && (UpperROMIndex == BreakpointP->upperROMindex) )
				{
					return cTrue;
				}
			}
			else
			{
				return cTrue;
			}
		}
		else if (NativeCPC->memwrite_addr == BreakpointP->mem_write)
		{
			return cTrue;
		}

		// Next breakpoint
		BreakpointP++;
	}

	return (cFalse);
}
/*---------------------------------------------------------------------*/


//
// PPI
//
static tUChar ppi_get_tape_level(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  ppi_get_tape_level
 *
 ***********************************************************************/
{
#ifdef ENABLE_TAPE
	if (NativeCPC->Tape->connected)
	{
		// Tape level when reading (TAPE_LEVEL_HIGH or TAPE_LEVEL_LOW)
		return ((!(NativeCPC->PPI->portC & 0x10)) ? TAPE_LEVEL_LOW : NativeCPC->Tape->bTapeLevel);
	}
	else
	{
		return (TAPE_LEVEL_HIGH);
	}
#else /* ENABLE_TAPE */
	return (TAPE_LEVEL_HIGH);
#endif /* ENABLE_TAPE */
}
/*---------------------------------------------------------------------*/

static tVoid ppi_write(tNativeCPC* NativeCPC, tRegister port, tULong val)
/***********************************************************************
 *
 *  ppi_write
 *
 ***********************************************************************/
{
	tPPI* PPI = NativeCPC->PPI;
	tPSG* PSG = NativeCPC->PSG;
	tULong psg_data;
	tULong bit;

	switch (port.UChar.h & 3) // A9=PPI A1 & A8=PPI A0
	{
		case 0: // write to port A?
		{
			if (!(PPI->control & 0x10)) // port A set to output?
			{
				PPI->portA = val;
			}
			else
			{
				PPI->portA = 0xff;
			}
			psg_data = PPI->portA;
			PSG_WRITE;
		}
		break;

		case 1: // write to port B?
		{
			if (!(PPI->control & 0x02)) // port B set to output?
			{
				PPI->portB = val;
			}
		}
		break;

		case 2: // write to port C?
		{
			PPI->portC = val;
			if (!(PPI->control & 1)) // output lower half?
			{
				NativeCPC->keyboard_line = val & 0x0f;
			}
			if (!(PPI->control & 8)) // output upper half?
			{
#ifdef ENABLE_TAPE
				Z80_OUT_HANDLER_PPI_TAPE
#endif /* ENABLE_TAPE */
				PSG->control = val; // change PSG control
				psg_data = PPI->portA;
				PSG_WRITE;
			}
		}
		break;

		case 3: // modify PPI control
		{
			if (val & 0x80) // change PPI configuration
			{
				PPI->control = val; // update control byte
				PPI->portA = 0; // clear data for all ports
				PPI->portB = 0;
				PPI->portC = 0;

#ifndef CAPRICE_X64_ENGINE
				//
				// Update outputs
				//
				if (!(PPI->control & 1)) // output port C lower half?
				{
					NativeCPC->keyboard_line = 0;
				}
				if (!(PPI->control & 8)) // output port C upper half?
				{
#ifdef ENABLE_TAPE
					Z80_OUT_HANDLER_PPI_TAPE
#endif /* ENABLE_TAPE */
					PSG->control = PPI->portC; // change PSG control
					psg_data = PPI->portA;
					PSG_WRITE;
				}
#endif /* CAPRICE_X64_ENGINE */
			}
			else // bit manipulation of port C data
			{
				bit = (val >> 1) & 7; // isolate bit to change
				if (val & 1) // set bit?
				{
					PPI->portC |= (1 << bit); // set requested bit
				}
				else
				{
					PPI->portC &= ~(1 << bit); // reset requested bit
				}
				if (!(PPI->control & 1)) // output lower half?
				{
					NativeCPC->keyboard_line = PPI->portC & 0x0f;
				}
				if (!(PPI->control & 8)) // output upper half?
				{
#ifdef ENABLE_TAPE
					Z80_OUT_HANDLER_PPI_TAPE
#endif /* ENABLE_TAPE */
					PSG->control = PPI->portC; // change PSG control
					psg_data = PPI->portA;
					PSG_WRITE;
				}
			}
#ifdef ENABLE_MULTIFACE2
			if (NativeCPC->pbMF2ROM)
			{
				// 8255 Control port write (f7xx): 37ff
				*(NativeCPC->pbMF2ROM + 0x037ff) = (tUChar)val;
			}
#endif /* ENABLE_MULTIFACE2 */
		}
		break;
	}
}
/*---------------------------------------------------------------------*/

static tULong ppi_read(tNativeCPC* NativeCPC, tRegister port)
/***********************************************************************
 *
 *  ppi_read
 *
 ***********************************************************************/
{
	tPPI* PPI = NativeCPC->PPI;
	tPSG* PSG = NativeCPC->PSG;
	tULong ret_val = 0xff;
#ifdef USE_KEYBOARD_CLASH
	tUChar loop;
	tUChar temp1,temp2;
#endif /* USE_KEYBOARD_CLASH */

	switch (port.UChar.h & 3) // A9=PPI A1 & A8=PPI A0
	{
		case 0: // read from port A?
		{
			if (PPI->control & 0x10) // port A set to input?
			{
				if ((PSG->control & 0xc0) == 0x40) // PSG control set to read?
				{
					if (PSG->reg_select == 14) // PSG port A?
					{
						if (!(PSG->RegisterAY.Index[7] & 0x40)) // port A in input mode?
						{
							ret_val &= NativeCPC->keyboard_matrix[NativeCPC->keyboard_line & 0x0f]; // read keyboard matrix node status

#ifdef USE_KEYBOARD_CLASH
							//
							// Keyboard clash
							//
							if (NativeCPC->keyboard_clash_type == 1)
							{
								temp1 = ~ret_val;
								if (temp1)
								{
									for (loop=0; loop < NATIVECPC_KEYBOARD_MATRIX_NBELEMENT; loop++)
									{
										temp2 = ~NativeCPC->keyboard_matrix[loop];
										if ((temp1 & temp2) == temp1)
										{
											ret_val &= ~temp2;
										}
									}
								}
							}
							else if (NativeCPC->keyboard_clash_type == 2)
							{
							}
#endif /* USE_KEYBOARD_CLASH */

							//
							// lightgun emulation
							//
							if (NativeCPC->lightgun_counter)
							{
								if ((NativeCPC->keyboard_line & 0x0f) == 0x09) // Joystick 0 port read
								{
									ret_val &= NativeCPC->lightgun_beam_key_mask;
#ifdef ENABLE_METROLOGY
									NativeCPC->signal_lightgun_read_joystick++;
#endif /* ENABLE_METROLOGY */
								}
							} // END lightgun emulation

#ifdef ENABLE_AMX_MOUSE
							//
							// AMX Mouse emulation
							//
							else if (NativeCPC->amx_mouse_active)
							{
								if ((NativeCPC->keyboard_line & 0x0f) == 0x09) // Joystick 0 port read
								{
									// Horizontal movement requested
									if (NativeCPC->amx_mouse_X_pulses)
									{
										if (NativeCPC->amx_mouse_X_pulses > 0)
										{
											ret_val &= ROWMASK_FROM_KEYCODE(AMX_MOUSE_RIGHT_KEY);
											NativeCPC->amx_mouse_X_pulses--;
										}
										else
										{
											ret_val &= ROWMASK_FROM_KEYCODE(AMX_MOUSE_LEFT_KEY);
											NativeCPC->amx_mouse_X_pulses++;
										}
									} // Horizontal move finished

									// Vertical movement requested
									if (NativeCPC->amx_mouse_Y_pulses)
									{
										if (NativeCPC->amx_mouse_Y_pulses > 0)
										{
											ret_val &= ROWMASK_FROM_KEYCODE(AMX_MOUSE_DOWN_KEY);
											NativeCPC->amx_mouse_Y_pulses--;
										}
										else
										{
											ret_val &= ROWMASK_FROM_KEYCODE(AMX_MOUSE_UP_KEY);
											NativeCPC->amx_mouse_Y_pulses++;
										}
									} // Vertical move finished
								}
							} // END AMX mouse emulation
#endif /* ENABLE_AMX_MOUSE */
						}
						else
						{
							// return last value w/ logic AND of input
							ret_val &= PSG->RegisterAY.Index[14] &
												 (NativeCPC->keyboard_matrix[NativeCPC->keyboard_line & 0x0f]);
						}
					}
#ifdef PSG_AY_3_8910
					else if (PSG->reg_select == 15) // PSG port B?
					{
						if ((PSG->RegisterAY.Index[7] & 0x80)) // port B in output mode?
						{
							ret_val &= PSG->RegisterAY.Index[15]; // return stored value
						}
					}
#endif /* PSG_AY_3_8910 */
					else if (PSG->reg_select < 14) // within valid range?
					{
						ret_val &= PSG->RegisterAY.Index[PSG->reg_select]; // read PSG register
					}
				}
			}
			else
			{
				ret_val &= PPI->portA; // return last programmed value
			}
		}
		break;

		case 1: // read from port B?
		{
			if (PPI->control & 2) // port B set to input?
			{
				ret_val &=
									ppi_get_tape_level(NativeCPC)
#ifdef ENABLE_PRINTER
									| (printer_getbusy(NativeCPC->Printer) ? 0x40 : 0) // busy line of connected printer
#else /* ENABLE_PRINTER */
									| 0x40 // Pull-up
#endif /* ENABLE_PRINTER */
									| (NativeCPC->jumpers & (CPC_BRAND_MASK | CPC_JUMPER_50HZ))
#ifndef CRTC_ENGINE_V2
									| (NativeCPC->CRTC->flags & VS_flag); // VSYNC status (VS_flag=0x01)
#else /* !CRTC_ENGINE_V2 */
									| (NativeCPC->CRTC->flag_invsync ? 1 : 0); // VSYNC status
#endif /* !CRTC_ENGINE_V2 */
			}
			else
			{
				ret_val &= PPI->portB; // return last programmed value
			}
		}
		break;

		case 2: // read from port C?
		{
			tULong direction = PPI->control & 9; // isolate port C directions
			ret_val &= PPI->portC; // default to last programmed value

			if (direction) // either half set to input?
			{
				if (direction & 8) // upper half set to input?
				{
					ret_val |= 0xf0; // invalid - set all bits
				}
				if (direction & 1) // lower half set to input?
				{
					ret_val |= 0x0f; // invalid - set all bits
				}
			}
		}
		break;

		default: // read control?
		{
			ret_val &= PPI->control | 0x80; // D7 is always set when control is read
		}
		break;
	}

	return (ret_val);
}
/*---------------------------------------------------------------------*/


//
// IDE
//
static tUChar ide_read(tNativeCPC* NativeCPC, tUChar reg_index)
/***********************************************************************
 *
 *  ide_read
 *
 ***********************************************************************/
{
tIDE* IDE = NativeCPC->IDE;
tUChar ret_val;

	switch (reg_index)
	{
		case 0:
		{
			// Pending read operation ?
			if (IDE->read_count)
			{
				ret_val = *(IDE->currentP++);

				// End of transfer
				if (IDE->currentP == IDE->end_cacheP)
				{
					IDE->reg_status &= ~(IDE_STATUS_DATA_REQ);

					// Still something to read
					IDE->read_count -= IDE->sector_size;
					if (IDE->read_count)
					{
						// Request next sector READ
						IDE->read_address += IDE->sector_size;
						IDE->currentP = IDE->cacheP;
						IDE->display_read_frames = IDE_DISPLAY_ACCESS_NB_FRAMES;

						// Ready data from XMass
						IDE->reg_status |= (IDE_STATUS_BUSY);
					}
				}
			}
		}
		break;

		case 1:
		{
			ret_val = IDE->reg_error;
		}
		break;

		case 7:
		{
			ret_val = IDE->reg_status;
		}
		break;

		default:
		{
			ret_val =	IDE->registers[reg_index];
		}
	}

	return (ret_val);
}
/*---------------------------------------------------------------------*/

static tVoid ide_write(tNativeCPC* NativeCPC, tUChar reg_index, tUChar val)
/***********************************************************************
 *
 *  ide_write
 *
 ***********************************************************************/
{
tIDE* IDE = NativeCPC->IDE;

	IDE->registers[reg_index] = val;

	switch (reg_index)
	{
		case 0:
		{
			// Pending write operation ?
			if (IDE->write_count)
			{
				*(IDE->currentP++) = val;

				// End of transfer
				if (IDE->currentP == IDE->end_cacheP)
				{
					IDE->reg_status &= ~(IDE_STATUS_DATA_REQ);
					IDE->reg_status |= (IDE_STATUS_BUSY);
				}
			}
		}
		break;

		case 7:
		{
			ide_execute_command(NativeCPC->IDE, val);
		}
		break;
	}
}
/*---------------------------------------------------------------------*/

static tVoid ide_execute_command(tIDE* IDE, tUChar command)
/***********************************************************************
 *
 *  ide_execute_command
 *
 ***********************************************************************/
{
tULong nb_sectors;

	switch (command)
	{
		case IDE_COMMAND_READ_SECTOR:
		{
			if (IDE->registers[6] & 0x40) // LBA ?
			{
				// Prepare read opration
				IDE->read_address = ide_get_sector_from_registers(IDE) * IDE->sector_size;
				nb_sectors = IDE->registers[2];
				if (!nb_sectors) nb_sectors = 256;
				IDE->read_count = nb_sectors * IDE->sector_size;

				IDE->currentP = IDE->cacheP;
				IDE->display_read_frames = IDE_DISPLAY_ACCESS_NB_FRAMES;

				// Ready data from XMass
				IDE->reg_status |= (IDE_STATUS_BUSY);
				IDE->reg_status &= ~(IDE_STATUS_ERROR);
			}
			else //CHS Mode
			{
				// ERROR
				IDE->reg_status |= IDE_STATUS_ERROR;
			}
		}
		break;

		case IDE_COMMAND_WRITE_SECTOR:
		{
			if (IDE->registers[6] & 0x40) // LBA ?
			{
				// Prepare write opration
				IDE->write_address = ide_get_sector_from_registers(IDE) * IDE->sector_size;
				nb_sectors = IDE->registers[2];
				if (!nb_sectors) nb_sectors = 256;
				IDE->write_count = nb_sectors * IDE->sector_size;

				IDE->currentP = IDE->cacheP;
				IDE->display_write_frames = IDE_DISPLAY_ACCESS_NB_FRAMES;

				// Data are ready to be written
				IDE->reg_status |= (IDE_STATUS_DATA_REQ);
				IDE->reg_status &= ~(IDE_STATUS_ERROR);
			}
			else //CHS Mode
			{
				// ERROR
				IDE->reg_status |= IDE_STATUS_ERROR;
			}
		}
		break;

		case IDE_COMMAND_IDENTIFY_DEVICE:
		{
			//
			// Prepare device identity (LITTLE ENDIAN)
			//
			MemMoveByte(IDE->cacheP, identify_device_dom_reply_template, IDE->sector_size);
			// Word 1 : Set Number of logical cylinders
			*(IDE->cacheP + (1*2) + 0) = (IDE->nb_cylinders & 0xff);
			*(IDE->cacheP + (1*2) + 1) = ((IDE->nb_cylinders >> 8) & 0xff);
			// Word 3 : Set Number of logical heads
			*(IDE->cacheP + (3*2) + 0) = (IDE->nb_heads & 0xff);
			*(IDE->cacheP + (3*2) + 1) = ((IDE->nb_heads >> 8) & 0xff);
			// Word 6 : Set Number of logical sector per logical track
			*(IDE->cacheP + (6*2) + 0) = (IDE->nb_sectors_per_cylinder & 0xff);
			*(IDE->cacheP + (6*2) + 1) = ((IDE->nb_sectors_per_cylinder >> 8) & 0xff);
			// Word 54 : Set Number of current logical cylinders
			*(IDE->cacheP + (54*2) + 0) = (IDE->nb_cylinders & 0xff);
			*(IDE->cacheP + (54*2) + 1) = ((IDE->nb_cylinders >> 8) & 0xff);
			// Word 55 : Set Number of current logical heads
			*(IDE->cacheP + (55*2) + 0) = (IDE->nb_heads & 0xff);
			*(IDE->cacheP + (55*2) + 1) = ((IDE->nb_heads >> 8) & 0xff);
			// Word 56 : Set Number of current logical sector per logical track
			*(IDE->cacheP + (56*2) + 0) = (IDE->nb_sectors_per_cylinder & 0xff);
			*(IDE->cacheP + (56*2) + 1) = ((IDE->nb_sectors_per_cylinder >> 8) & 0xff);
			// Word 57&58 : Total number of user addressable sectors (LBA mode only)
			*(IDE->cacheP + (57*2) + 0) = ((IDE->nb_sectors >>  0) & 0xff);
			*(IDE->cacheP + (57*2) + 1) = ((IDE->nb_sectors >>  8) & 0xff);
			*(IDE->cacheP + (58*2) + 0) = ((IDE->nb_sectors >> 16) & 0xff);
			*(IDE->cacheP + (58*2) + 1) = ((IDE->nb_sectors >> 24) & 0xff);
			// Word 60&61 : Total number of user addressable sectors (LBA mode only)
			*(IDE->cacheP + (60*2) + 0) = ((IDE->nb_sectors >>  0) & 0xff);
			*(IDE->cacheP + (60*2) + 1) = ((IDE->nb_sectors >>  8) & 0xff);
			*(IDE->cacheP + (61*2) + 0) = ((IDE->nb_sectors >> 16) & 0xff);
			*(IDE->cacheP + (61*2) + 1) = ((IDE->nb_sectors >> 24) & 0xff);

			IDE->read_count = IDE->sector_size;
			IDE->currentP = IDE->cacheP;

			// Data are ready to be read
			IDE->reg_status |= IDE_STATUS_DATA_REQ;
			IDE->reg_status &= ~(IDE_STATUS_ERROR | IDE_STATUS_BUSY);
		}
		break;

		// Unknown command
		default:
		{
			// ERROR
			IDE->reg_status |= IDE_STATUS_ERROR;
		}
		break;
	}
}
/*---------------------------------------------------------------------*/

static tULong ide_get_sector_from_registers(tIDE* IDE)
/***********************************************************************
 *
 *  ide_get_sector_from_registers
 *
 ***********************************************************************/
{
tULong Sector;

	// LBA ?
	if (IDE->registers[6] & 0x40)
	{
		Sector = (IDE->registers[6] & 0x0f);
		Sector = Sector << 8;
		Sector |= IDE->registers[5];
		Sector = Sector << 8;
		Sector |= IDE->registers[4];
		Sector = Sector << 8;
		Sector |= IDE->registers[3];
	}
	else
	{
		// CHS => LBA
		// NT = Nombre Tetes
		// NS = Nombre secteurs
		// A = ( C × NT × NS ) + ( H × NS ) + S - 1
		Sector  = (((tULong)IDE->registers[5] << 8) + (tULong)IDE->registers[4])
						 * IDE->nb_heads * IDE->nb_sectors_per_cylinder;
		Sector += (IDE->registers[6] & 0x0f) * IDE->nb_sectors_per_cylinder;
		Sector += IDE->registers[3] - 1;
	}

	return (Sector);
}
/*---------------------------------------------------------------------*/

tUChar Engine_IDEIsFileOperationRequested(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  Engine_IDEIsFileOperationRequested
 *
 ***********************************************************************/
{
	return (NativeCPC->IDE->reg_status & IDE_STATUS_BUSY);
}
/*---------------------------------------------------------------------*/

tVoid Engine_IDESetError(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  Engine_IDESetError
 *
 ***********************************************************************/
{
	NativeCPC->IDE->reg_status |= (IDE_STATUS_ERROR);
}
/*---------------------------------------------------------------------*/

tVoid Engine_IDEResumeReadOperation(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  Engine_IDEResumeReadOperation
 *
 ***********************************************************************/
{
tIDE* IDE = NativeCPC->IDE;

	// IDE is no longer BUSY
	IDE->reg_status &= ~(IDE_STATUS_BUSY);

	// Data are ready to be read
	IDE->reg_status |= IDE_STATUS_DATA_REQ;
}
/*---------------------------------------------------------------------*/

tVoid Engine_IDEResumeWriteOperation(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  Engine_IDEResumeWriteOperation
 *
 ***********************************************************************/
{
tIDE* IDE = NativeCPC->IDE;

	// IDE is no longer BUSY
	IDE->reg_status &= ~(IDE_STATUS_BUSY);

	// Still something to write
	IDE->write_count -= IDE->sector_size;
	if (IDE->write_count)
	{
		// Request WRITE next sector
		IDE->write_address += IDE->sector_size;
		IDE->currentP = IDE->cacheP;

		IDE->display_write_frames = IDE_DISPLAY_ACCESS_NB_FRAMES;

		// Data are ready to be written
		IDE->reg_status |= IDE_STATUS_DATA_REQ;
		IDE->reg_status &= ~(IDE_STATUS_ERROR);
	}
}
/*---------------------------------------------------------------------*/


//
// VIDEO
//

static tVoid video_init(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_init
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;
#ifndef CRTC_ENGINE_V2
tVDU* VDU = NativeCPC->VDU;
#else /* !CRTC_ENGINE_V2 */
tUShort l;
tUShort j;
#endif /* !CRTC_ENGINE_V2 */

#ifndef CRTC_ENGINE_V2
  CRTC->DrawHandler[0] = video_draw_mode0;
  CRTC->DrawHandler[1] = video_draw_mode1;
  if (NativeCPC->prefP->Mode2AntiAliasing == 0)
  {
    CRTC->DrawHandler[2] = video_draw_mode2;
  }
  else
  {
    CRTC->DrawHandler[2] = video_draw_mode2_antialiased;
  }
  CRTC->DrawHandler[3] = video_draw_mode0;

  CRTC->CurrentHandler = CRTC->DrawHandler[CRTC->current_mode];
  CRTC->RequestedHandler = CRTC->DrawHandler[CRTC->requested_mode];

  VDU->palette = NativeCPC->GateArray->palette;
  VDU->pbRAM = NativeCPC->pbRAM;
  VDU->mode0_table = NativeCPC->mode0_table;
  VDU->mode1_table = NativeCPC->mode1_table;

#else /* !CRTC_ENGINE_V2 */

  CRTC->StartCharInstMR = CRTC->CharInstMR = video_NoChar;
  CRTC->StartCharInstSL = CRTC->CharInstSL = video_NoChar;

  CRTC->ModeMaps[0] = M0Map;
  CRTC->ModeMaps[1] = M1Map;
  CRTC->ModeMaps[2] = M2Map;
  CRTC->ModeMaps[3] = M3Map;
  CRTC->StartModeMap = CRTC->ModeMap = CRTC->ModeMaps[0];

  if (NativeCPC->BitsPerPixel == 32)
  {
    CRTC->Render = video_render32bpp;
  }
  else if (NativeCPC->BitsPerPixel == 24)
  {
    CRTC->Render = video_render24bpp;
  }
  else
  {
    CRTC->Render = video_render8bpp;
  }

#endif /* !CRTC_ENGINE_V2 */

#ifndef CAPRICE_4_2_ENGINE
  if (NativeCPC->crtc_type == CRTC_TYPE_1)
  {
    NativeCPC->GetCRTCRegisterPtr = CRTC1_get_register;
    NativeCPC->SetCRTCRegisterPtr = CRTC1_set_register;
    NativeCPC->GetCRTCStatusPtr = CRTC1_get_status;
    NativeCPC->VideoEnginePtr = CRTC1_video_engine;
  }
  else
#endif /* !CAPRICE_4_2_ENGINE */
  {
    NativeCPC->GetCRTCRegisterPtr = CRTC0_get_register;
    NativeCPC->SetCRTCRegisterPtr = CRTC0_set_register;
    NativeCPC->GetCRTCStatusPtr = CRTC0_get_status;
    NativeCPC->VideoEnginePtr = CRTC0_video_engine;
  }

#ifdef ENABLE_METROLOGY  
  CRTC->signal_register_update = 0;
#endif /* ENABLE_METROLOGY */
}
/*---------------------------------------------------------------------*/


static tVoid video_no_display(tNativeCPC* NativeCPC,
                              tULong repeat_count)
/***********************************************************************
 *
 *  video_no_display
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;
tVDU* VDU = NativeCPC->VDU;

  while (repeat_count--)
  {
    // are we within the rendering area?
    /*if (VDU->flag_drawing)
    {
      // below horizontal cut-off?
      if (CRTC->HorzChar < CRTC->HorzMax)
      {
        if (CRTC->flags1.combined != CRTC->LastPreRend)
        {
          video_set_prerender(CRTC); // change pre-renderer if necessary
        }
        
        CRTC->PreRender(NativeCPC); // translate CPC video memory bytes to entries referencing the palette
        CRTC->Render(NativeCPC); // render to the video surface at the current bit depth
      }
    }*/

#ifdef PALETTE_PATCH_1
    if (NativeCPC->GateArray->newcolorcount)
    {
      NativeCPC->GateArray->newcolorcount = 0;
      NativeCPC->GateArray->ink_values[NativeCPC->GateArray->pen] = NativeCPC->GateArray->newcolor;
      NativeCPC->GateArray->palette[NativeCPC->GateArray->pen] = NativeCPC->colours[NativeCPC->GateArray->newcolor]; // isolate colour value
    }
#endif /* PALETTE_PATCH_1 */

#ifdef ENABLE_PLAYCITY
    if (NativeCPC->PlayCity->enabled)
    {
      if ( (CRTC->raster_count >= CRTC->cursor_start_ras)
           && (CRTC->raster_count <= CRTC->cursor_end_ras) )
      {
        if ((CRTC->addr + CRTC->char_count) == (CRTC->cursor_addr + CRTC->start_addr))
        {
          PlayCity_trigger_channel_1(NativeCPC);
        }
      }
    }
#endif /* ENABLE_PLAYCITY */

    // next address for PreRender    
    //CRTC->next_address = VDU->MAXlate[(CRTC->addr + CRTC->char_count) & 0x73ff] | CRTC->scr_base;
    CRTC->flags1.dt.combined = CRTC->new_dt.combined; // update the DISPTMG flags

    CRTC->iMonHSStartPos += 0x100;
    CRTC->iMonHSEndPos += 0x100;
    CRTC->iMonHSPeakPos += 0x100;
    CRTC->HorzPos += 0x100;
    CRTC->HorzChar++;

    if (CRTC->HorzPos >= CRTC->MonHSYNC)
    {
      /*if (VDU->flag_drawing)
      {
        VDU->scr_base += VDU->scr_line_offs; // advance surface pointer to next row
      }*/
      CRTC->HadP = 1;
      CRTC->iMonHSPeakPos = CRTC->HorzPos - CRTC->MonHSYNC;
      CRTC->iMonHSStartToPeak = CRTC->iMonHSStartPos - CRTC->iMonHSPeakPos;
      CRTC->iMonHSEndToPeak = CRTC->iMonHSEndPos - CRTC->iMonHSPeakPos;
      CRTC->HorzPos = CRTC->iMonHSPeakPos - CRTC->HSyncDuration;

      // CAUTION : CRTC->HorzPos can be negative and CRTC->HorzChar is unsigned !!
      //CRTC->HorzChar = CRTC->HorzPos >> 8;

      /*{
        tULong val = (CRTC->HorzPos & 0xf0) >> 4; // PosShift
        if (!val)
        {
          CRTC->HorzMax = 48;
          CRTC->HorzPix[0] = CRTC->HorzPix[1];
          CRTC->RendPos = CRTC->RendStart;
          CRTC->HorzChar--;
        }
        else
        {
          tULong tmp;
          CRTC->RendPos = (tULong*)&CRTC->RendBuff[val];
          tmp = (tULong)CRTC->RendStart - (tULong)CRTC->RendPos;
          CRTC->HorzPix[48] = (tUChar)tmp;
          CRTC->HorzPix[0] = CRTC->HorzPix[1] - (tUChar)tmp;
          CRTC->HorzMax = 49;
        }
      }*/

      //CRTC->RendOut = (tUChar*)CRTC->RendStart;
      //CRTC->RendWid = &CRTC->HorzPix[0];
      //VDU->scr_pos = VDU->scr_base;
      VDU->scrln++;
      VDU->scanline++;
      /*if ((tULong)VDU->scrln >= MAX_DRAWN)
      {
        VDU->flag_drawing = 0;
      }
      else
      {
        VDU->flag_drawing = 1;
      }*/
    }

    // ----------------------------------------------------------------------------

    // matches horizontal total?
    if (CRTC->char_count == CRTC->registers[0])
    {
      CRTC->last_hend = CRTC->char_count; // preserve current line length in chars
      CRTC->flag_newscan = 1; // request starting a new scan line
      CRTC->char_count = 0; // reset the horizontal character count
    }
    else
    {
      CRTC->char_count++; // update counter
      CRTC->char_count &= 255; // limit to 8 bits
    }

    // matches horizontal total?
    if (CRTC->char_count == CRTC->registers[0])
    {
      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->flag_reschar = 1; // request a line count update
      }
      else
      {
        CRTC->flag_reschar = 0; // still within the current character line
      }
      // ready to restart frame?
      if (CRTC->flag_resnext)
      {
        CRTC->flag_resnext = 0;
        CRTC->flag_resframe = 1; // request a frame restart
      }
      // ready to start vertical total adjust?
      if (CRTC->flag_startvta)
      {
        CRTC->flag_startvta = 0;
        CRTC->flag_invta = 1; // entering vertical total adjust
      }
      // in vertical total adjust?
      if (CRTC->flag_invta)
      {
        if ( (CRTC->raster_count == CRTC->registers[9])
             && (CRTC->line_count == CRTC->registers[4]) )
        {
          CRTC->flag_resscan = 1; // raster counter only resets once at start of vta
        }
        else
        {
          CRTC->flag_resscan = 0; // raster counter keeps increasing while in vta
        }
      }
    }

    // matches horizontal displayed?
    if (CRTC->char_count == CRTC->registers[1])
    {
      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->next_addr = CRTC->addr + CRTC->char_count;
      }
    }

    // not in HSYNC?
    if (!CRTC->flags1.inHSYNC)
    {
      // matches horizontal sync position?
      if (CRTC->char_count == CRTC->registers[2])
      {
        CRTC->flags1.inHSYNC = 0xff; // turn HSYNC on
        CRTC->flag_hadhsync = 1; // prevent GA from processing more than one HSYNC per scan line
        CRTC->hsw_count = 0; // initialize horizontal sync width counter
        video_match_hsw(NativeCPC);
      }
    }
    else
    {
      video_match_hsw(NativeCPC);
    }

    CRTC->CharInstSL(CRTC); // if necessary, process vertical total delay
    CRTC->CharInstMR(CRTC); // if necessary, process maximum raster count delay

    // scanline change requested?
    if (CRTC->flag_newscan)
    {
      CRTC->flag_newscan = 0;
      CRTC->addr = CRTC->next_addr;

      // VSYNC active?
      if (CRTC->flag_invsync)
      {
        CRTC->vsw_count++; // update counter
        /* from Caprice Reloaded r623 */
        /* OK Backtro */
        CRTC->vsw_count &= 31; // limit to 5 bits (to handle "double vbl" case)

        // matches vertical sync width?
        if (CRTC->vsw_count == CRTC->vsw)
        {
          CRTC->vsw_count = 0; // reset counter
          CRTC->flag_resvsync = 1; // request VSYNC reset
        }
      }

      // frame restart requested?
      if (CRTC->flag_resframe)
      {
        video_restart_frame(NativeCPC);
      }
      else
      {
        // raster counter reset requested?
        if (CRTC->flag_resscan)
        {
          CRTC->flag_resscan = 0;
          CRTC->raster_count = 0; // reset counter
          CRTC->scr_base = 0;
        }
        else
        {
          CRTC->raster_count++; // update counter
          CRTC->raster_count &= 31; // limit to 5 bits
          // did the counter wrap around?
          if (!CRTC->raster_count)
          {
            video_match_line_count(NativeCPC);
          }
          CRTC->scr_base = (CRTC->scr_base + 0x0800) & 0x3800;
        }
      }

      CRTC->CharInstSL = video_CharSL1;

      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->flag_resscan = 1; // request a raster counter reset
        CRTC->r9match = 1;
        CRTC->CharInstMR = video_CharMR1;
      }
      else
      {
        CRTC->r9match = 0;
      }

      // in vertical total adjust?
      if (CRTC->flag_invta)
      {
        // matches vertical total adjust?
        if (CRTC->raster_count == CRTC->reg5)
        {
          video_restart_frame(NativeCPC);
          // maximum raster address is zero?
          if (CRTC->registers[9] == 0)
          {
            CRTC->flag_resscan = 1; // request a raster counter reset
          }
        }
      }

      // line count update requested?
      if (CRTC->flag_reschar)
      {
        CRTC->line_count++; // update counter
        CRTC->line_count &= 127; // limit to 7 bits
        video_reload_addr(NativeCPC);
      }

      // in VSYNC?
      if (CRTC->flag_invsync)
      {
        // end of VSYNC?
        if (CRTC->flag_resvsync)
        {
          CRTC->flag_invsync = 0; // turn VSYNC off
          CRTC->flag_resvsync = 0;
          // above maximum scanline count?
          if (VDU->scanline >= CRTC->MaxVSync)
          {
            video_frame_finished(VDU);
          }
        }
        else
        {
          // above minimum scanline count?
          if (VDU->scanline > CRTC->MinVSync)
          {
            video_frame_finished(VDU);
          }
        }
      }
      // above maximum scanline count?
      else if (VDU->scanline >= CRTC->MaxVSync)
      {
        video_frame_finished(VDU);
      }
    }

    // leaving border area?
    if (CRTC->char_count == CRTC->hstart)
    {
      CRTC->new_dt.NewHDSPTIMG |= 0x01;
    }
    // entering border area?
    if (CRTC->char_count == CRTC->hend)
    {
      CRTC->new_dt.NewHDSPTIMG &= 0xfe;
    }
  }
}


#define VIDEO_DRAW_BORDER \
{ \
  *(VDU->scr_current++) = VDU->border; /* write four pixels */ \
  *(VDU->scr_current++) = VDU->border; /* write four pixels */ \
}

#ifndef CRTC_ENGINE_V2

static tULong video_get_CRTC_Register(tNativeCPC* NativeCPC,
                                      tULong Index)
/***********************************************************************
 *
 *  video_get_CRTC_Register
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;
tULong ret_val = 0xff;

  if ((Index > 11) && (Index < 16)) // valid range?
  {
    ret_val = CRTC->registers[Index];
  }
  else if (Index == 16)
  {
    ret_val = ((CRTC->start_addr + CRTC->lpen_offset) >> 8) & 0x3f;
    CRTC->registers[16] = ret_val;
#ifdef ENABLE_METROLOGY
    CRTC->signal_register_update++;
#endif /* ENABLE_METROLOGY */
  }
  else if (Index == 17)
  {
    if (NativeCPC->lightgun_random_crtc)
    {
      // return random value different from previous one
      ret_val = (tUChar)NativeCPC->cycle_count;
    }
    else
    {
      ret_val = (CRTC->start_addr + CRTC->lpen_offset) & 0xff;
    }
    CRTC->registers[17] = ret_val;
#ifdef ENABLE_METROLOGY
    CRTC->signal_register_update++;
#endif /* ENABLE_METROLOGY */
  }
  else
  {
    ret_val = 0; // write only registers return 0
  }

  return ret_val;
}


static tVoid video_set_CRTC_Register(tNativeCPC* NativeCPC,
                                     tULong Index,
                                     tULong val)
/***********************************************************************
 *
 *  video_set_CRTC_Register
 *
 ***********************************************************************/
{
tVDU* VDU = NativeCPC->VDU;
tCRTC* CRTC = NativeCPC->CRTC;

  switch (Index)
  {
    case 0:  // R0  = Horizontal Total
    {
      CRTC->registers[0] = val;

      #define CRTC_RASTER_LENGTH_COUNT       63
      #define CRCT_RASTER_LENGTH_TOLERANCE   1

      VDU->raster_length = 64;

      if (val > CRTC_RASTER_LENGTH_COUNT)
      {
        if ((val - CRTC_RASTER_LENGTH_COUNT) <= CRCT_RASTER_LENGTH_TOLERANCE)
        {
          VDU->raster_length = val + 1;
        }
      }
      else if (val < CRTC_RASTER_LENGTH_COUNT)
      {
        if ((CRTC_RASTER_LENGTH_COUNT - val) <= CRCT_RASTER_LENGTH_TOLERANCE)
        {
          VDU->raster_length = val + 1;
        }
      }
    }
    break;

    case 3:  // R3  = Sync Width
    {
      CRTC->registers[3] = val;
      CRTC->hsw = val & 0x0f; // isolate horizontal sync width
      VDU->hsw = CRTC->hsw - 2; // GA delays HSYNC by 2 chars
      if (VDU->hsw < 0) // negative value?
      {
        VDU->hsw = 0; // no VDU HSYNC
      }
      else if (VDU->hsw > 4) // HSYNC longer than 4 chars?
      {
        VDU->hsw = 4; // maximum of 4
      }
      CRTC->vsw = val >> 4; // isolate vertical sync width
      if (!CRTC->vsw)
      {
        CRTC->vsw = 16; // 0 = width of 16
      }
    }
    break;

    case 4:  // R4  = Vertical Total
    case 6:  // R6  = Vertical Displayed
    case 7:  // R7  = Vertical Sync Position
    case 10: // R10 = Cursor Start Raster
    {
      CRTC->registers[Index] = val & 0x7f;
    }
    break;

    case 5:  // R5  = Vertical Total Adjust
    {
      CRTC->registers[5] = val & 0x1f;
      //CRTC->vt_adjust = val & 0x1f;
    }
    break;

    case 8:  // R8  = Interlace and Skew
    {
      CRTC->registers[8] = val;
      CRTC->skew = (val >> 4) & 3; // isolate display timing skew
      if (CRTC->skew == 3) // no output?
      {
        CRTC->skew = 0xff;
      }
    }
    break;

    case 9:  // R9  = Maximum Raster Address
    {
      CRTC->registers[9] = val & 0x1f;
      CRTC->max_raster = val << 3; // modify value for easier handling
    }
    break;

    case 11: // R11 = Cursor End Raster
    {
      CRTC->registers[11] = val & 0x1f;
    }
    break;

    case 12: // R12 = Start Address (H)
    case 13: // R13 = Start Address (L)
    {
      tULong val1;
      tULong val2;

      CRTC->registers[Index] = val;
      
      val1 = CRTC->registers[12] & 0x3f;
      CRTC->start_addr = (val1 << 8) + CRTC->registers[13];
      val2 = val1 & 0x0f; // isolate screen size
      val1 = (val1 << 1) & 0x60; // isolate CPC RAM bank
      val2 |= val1; // combine
      CRTC->requested_addr = (CRTC->registers[13] + (val2 << 8)) << 1;
    }
    break;

    case 14: // R14 = Cursor (H)
    {
      CRTC->registers[14] = val & 0x3f;
    }
    break;

    // R1  = Horizontal Displayed
    // R2  = Horizontal Sync Position
    // R15 = Cursor (L)
    default:
    {
      CRTC->registers[Index] = val;
    }
  } /* switch */
}


static tVoid video_access_memory(tNativeCPC* NativeCPC,
                                 tULong repeat_count)
/***********************************************************************
 *
 *  video_access_memory
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;
tVDU* VDU = NativeCPC->VDU;
tGateArray* GateArray = NativeCPC->GateArray;
tULong flags;

  flags = CRTC->flags;

  do
  {
    CRTC->char_count++; // update character count (cc)
    VDU->char_count++;

    if (flags & HT_flag) // reached horizontal total on last cc?
    {
      flags &= ~HT_flag;

      CRTC->char_count = 0; // reset cc
      
      // new CRTC register value taken into account after Horizontal Total
      CRTC->hsw_active = CRTC->hsw; 
      VDU->hsw_active = VDU->hsw;

      // next raster -------------------------------------------------

      CRTC->raster_count += 8; // advance rc by one

      if (flags & VS_flag) // in VSYNC?
      {
        CRTC->vsw_count++; // update width counter
        if (CRTC->vsw_count >= CRTC->vsw) // reached end of VSYNC?
        {
          flags = (flags & ~VS_flag) | VSf_flag; // reset VSYNC, set 'just finished'
        }
      }

      if (flags & MR_flag) // reached maximum raster address on last rc?
      {
        flags &= ~MR_flag;
        CRTC->raster_count = 0; // reset rc
        if (!(flags & HDT_flag)) // HDISPTMG still on (i.e. R01 > R00)?
        {
          CRTC->addr += CRTC->last_hdisp * 2; // advance CPC screen address to next line
        }
        CRTC->line_count++; // update line count
        CRTC->line_count &= 127; // limit to 7 bits
      }

      if (CRTC->vt_adjust_count) // vertical total adjust active?
      {
        if (--CRTC->vt_adjust_count == 0) // done with vta?
        {
          flags = (flags & ~VSf_flag) | VDT_flag; // enable VDISPTMG
          CRTC->raster_count = 0; // reset rc
          CRTC->line_count = 0; // reset lc
          CRTC->addr = CRTC->requested_addr; // update start of CPC screen address
        }
      }

      if (flags & VT_flag) // reached vertical total on last lc?
      {
        flags &= ~VT_flag;
        if (CRTC->registers[5]) // do a vertical total adjust?
        {
          CRTC->vt_adjust_count = CRTC->registers[5]; // init vta counter
        }
        else
        {
          flags = (flags & ~VSf_flag) | VDT_flag; // enable VDISPTMG
          CRTC->raster_count = 0; // reset rc
          CRTC->line_count = 0; // reset lc
          CRTC->addr = CRTC->requested_addr; // update start of CPC screen address
        }
      }

      if (CRTC->raster_count >= CRTC->max_raster) // rc = maximum raster address?
      {
        flags |= MR_flag; // process max raster address on next rc

        if (!CRTC->vt_adjust_count) // no vertical total adjust?
        {
          if (CRTC->line_count >= CRTC->registers[4]) // lc = vertical total?
          {
            flags |= VT_flag; // takes effect on next lc
          }
        }
      }

      if (CRTC->line_count == CRTC->registers[6]) // lc = vertical displayed?
      {
        flags &= ~VDT_flag; // disable VDISPTMG
      }

      if (CRTC->line_count == CRTC->registers[7]) // lc = vertical sync position?
      {
        if (!(flags & (VSf_flag | VS_flag))) // not in VSYNC?
        {
          flags |= VS_flag;
          CRTC->vsw_count = 0; // clear vsw counter
          VDU->vdelay = 2; // GA delays vsync by 2 scanlines
          VDU->vsw_count = 4; // GA vsync is always 4 scanlines long
          GateArray->int_delay = 2; // arm GA two scanlines interrupt delay
        }
      }

      flags |= HDT_flag; // enable horizontal display

    } /* if (flags & HT_flag) */

    // ----------------------------------------------------------------

    if (CRTC->char_count >= CRTC->registers[0]) // cc = horizontal total?
    {
      flags |= HT_flag; // takes effect on next cc
    }

    if (CRTC->char_count == CRTC->registers[1]) // cc = horizontal displayed?
    {
      flags &= ~HDT_flag; // disable horizontal display
      CRTC->last_hdisp = CRTC->char_count; // save width for line advancement
    }

    // check hsw ------------------------------------------------------

    if (flags & HS_flag) // in horizontal sync?
    {
      if (VDU->hdelay == 2) // ready to trigger VDU HSYNC?
      {
        if (VDU->hsw_count) VDU->hsw_count--;
        if (VDU->hsw_count == 0)
        {
          if (VDU->scr_line++ < CPC_SCR_HEIGHT)
          {
            if (VDU->vcount) // in the visible portion of the screen?
            {
              VDU->scr_base += VDU->scr_line_offs; // advance to next line
            }
          }

          VDU->scr_current = VDU->scr_base;
          VDU->char_count = 0;

          VDU->hdelay++; // prevent reentry
        }
      }
      else
      {
        VDU->hdelay++; // update delay counter
      }

      // hsw end -----------------------------------------------------
      if (CRTC->hsw_count) CRTC->hsw_count--; // end of HSYNC?
      if (CRTC->hsw_count == 0) // end of HSYNC?
      {
        flags &= ~HS_flag; // reset HSYNC
        CRTC->current_mode = CRTC->requested_mode;
        CRTC->CurrentHandler = CRTC->RequestedHandler;
        VDU->scanline++;

        // monitor delaying VSYNC?
        if (VDU->vdelay) VDU->vdelay--;
        if (VDU->vdelay == 0) // enter monitor VSYNC?
        {
          if (VDU->vsw_count) // still in monitor VSYNC?
          {
            if (--VDU->vsw_count == 0) // done with monitor VSYNC?
            {
              if (VDU->scanline > VDU_SCANLINE_MIN) // above minimum scanline count?
              {
                VDU->scanline = 0;
                VDU->scr_line = 0;
                VDU->scr_current = VDU->scr_base;
                VDU->frame_completed = 1; // force exit of emulation loop
              }
            }
          }
        }

        // GA interrupt trigger -------------------------------------

        GateArray->sl_count++; // update GA scanline counter
        if (GateArray->int_delay) // delay on VSYNC?
        {
          if (--GateArray->int_delay == 0) // delay expired?
          {
            if (GateArray->sl_count >= 32) // counter above save margin?
            {
              NativeCPC->Z80->Regs.int_pending = 1; // queue interrupt
            }

            GateArray->sl_count = 0; // clear counter
          }
        }

        if (GateArray->sl_count == 52) // trigger interrupt?
        {
          NativeCPC->Z80->Regs.int_pending = 1; // queue interrupt
          GateArray->sl_count = 0; // clear counter
        }
      }
    } /* if (flags & HS_flag) */

    // ----------------------------------------------------------------

    if (CRTC->char_count == CRTC->registers[2]) // cc = horizontal sync position?
    {
      if (CRTC->hsw_active) // allow HSYNCs?
      {
        flags |= HS_flag; // set HSYNC
        CRTC->hsw_count = CRTC->hsw_active; // load hsw counter
        VDU->hdelay = 0; // clear VDU 2 chars HSYNC delay
        VDU->hsw_count = VDU->hsw_active; // load VDU hsw counter
      }
    }

    // Visible portion of the screen
    if (VDU->hcount)
    {
      VDU->hcount--;

      if (!CRTC->stop_rendering)
      {
        if ((flags & (HDT_flag | VDT_flag)) == (HDT_flag | VDT_flag)) // DISPTMG enabled?
        {
          if (CRTC->skew)
          {
            CRTC->skew--;

            VIDEO_DRAW_BORDER;
          }
          else
          {
            tULong addr = (CRTC->char_count * 2) + CRTC->addr; // cc x2 + CPC screen memory address
            if (addr & 0x2000) // 32K screen?
            {
              addr += 0x4000; // advance to next 16K segment
            }
            addr &= 0xC7FF; // apply 11000111 mask to MSB (bits 15-8)
            addr |= (CRTC->raster_count & 0x38) << 8; // insert rc, masked with 00111000

            // call apropriate mode handler
            CRTC->CurrentHandler(VDU,
                                 addr);
          }
        }
        else
        {
          VIDEO_DRAW_BORDER;
        }
      }
	  }
    else /* if (VDU->hcount) */
    {
      if (VDU->char_count == VDU_HSTART)
      {
        if (VDU->vcount)
        {
          VDU->vcount--;
          VDU->hcount = VDU_HWIDTH;
        }
        else if (VDU->scr_line == VDU_VSTART)
        {
          VDU->vcount = VDU_VHEIGHT;
        }
      }
      else if (VDU->char_count >= VDU->raster_length) // raster line finished !
      {
        VDU->scanline++;
        if (VDU->scr_line++ < CPC_SCR_HEIGHT)
        {
          if (VDU->vcount) // in the visible portion of the screen?
          {
            VDU->scr_base += VDU->scr_line_offs; // advance to next line
          }
        }

        VDU->scr_current = VDU->scr_base;
        VDU->char_count = 0;
      }
      else if (VDU->scr_line >= (CPC_SCR_HEIGHT * 2))
      {
        VDU->scanline = 0;
        VDU->scr_line = 0;
        VDU->scr_current = VDU->scr_base;
        VDU->frame_completed = 1; // force exit of emulation loop
      }
    } /* if (VDU->hcount) */

    // lightgun beam detection should be performed once screen written.
    if (NativeCPC->lightgun_beam_detect)
    {
      if (!NativeCPC->lightgun_counter)
      {
        // Test Y first for optimization
        if ( (NativeCPC->lightgun_y_pos == CRTC->line_count) &&
             (NativeCPC->lightgun_x_pos == CRTC->char_count) )
        {
          // Check if drawn pixel is bright enough.
#if defined(__PALMOS__)
          if (NativeCPC->PalmPaletteIntensity[(tUChar)*(VDU->scr_current-1)] >= (tUChar)NativeCPC->lightgun_sensitivity)
#else /* __PALMOS__ */
          if (NativeCPC->WinPalettePtr[(tUChar)*(VDU->scr_current-1)].intensity >= (tUChar)NativeCPC->lightgun_sensitivity)
#endif /* __PALMOS__ */
          {
            NativeCPC->lightgun_counter = NativeCPC->lightgun_beam_duration;
          }
        }
      }
      else
      {
        // Decrement lightgun beam duration counter (in character)
        NativeCPC->lightgun_counter--;
      }
    }
  }
  while (--repeat_count);

  CRTC->flags = flags;
}
/*---------------------------------------------------------------------*/

static tVoid video_draw_mode0(tVDU* VDU,
                              tULong addr)
/***********************************************************************
 *
 *  video_draw_mode0
 *
 ***********************************************************************/
{
tULong Pixels1, Pixels2;
tULong* Palette_ptr;
tUShort ModeTable1, ModeTable2;
tUShort RAM;

  Palette_ptr = VDU->palette;
  RAM = *((tUShort*)(VDU->pbRAM + addr));

  ModeTable1 = *((tUShort*)(VDU->mode0_table + ((RAM & 0xff) * 2)));
  ModeTable2 = *((tUShort*)(VDU->mode0_table + ((RAM & 0xff00) >> 7)));

  Pixels1  = *(Palette_ptr + ((ModeTable1 >> 8) & 0xff)) << 16;
  Pixels1 |= *(Palette_ptr + (ModeTable1 & 0xff)) & 0xff;
  Pixels1 |= Pixels1 << 8;

  Pixels2  = *(Palette_ptr + ((ModeTable2 >> 8) & 0xff)) << 16;
  Pixels2 |= *(Palette_ptr + (ModeTable2 & 0xff)) & 0xff;
  Pixels2 |= Pixels2 << 8;

  // 8 pixels transfer to screen memory
  *(VDU->scr_current++) = Pixels1; // write four pixels
  *(VDU->scr_current++) = Pixels2; // write four pixels
}
/*---------------------------------------------------------------------*/


static tVoid video_draw_mode1(tVDU* VDU,
                              tULong addr)
/***********************************************************************
 *
 *  video_draw_mode1
 *
 ***********************************************************************/
{
tULong Pixels1, Pixels2;
tULong* Palette_ptr;
tULong ModeTable1;
tULong ModeTable2;
tUShort RAM;

  Palette_ptr = VDU->palette;

  RAM = *((tUShort*)(VDU->pbRAM + addr));
  ModeTable1 = *(tULong*)(VDU->mode1_table + ((RAM & 0xff) * 4));
  ModeTable2 = *(tULong*)(VDU->mode1_table + ((RAM & 0xff00) >> 6));

  Pixels1  = *(Palette_ptr + (ModeTable1 & 0xff));
  Pixels1 |= *(Palette_ptr + ((ModeTable1 >>  8) & 0xff)) << 8;
  Pixels1 |= *(Palette_ptr + ((ModeTable1 >> 16) & 0xff)) << 16;
  Pixels1 |= *(Palette_ptr + ((ModeTable1 >> 24) & 0xff)) << 24;

  Pixels2  = *(Palette_ptr + (ModeTable2 & 0xff));
  Pixels2 |= *(Palette_ptr + ((ModeTable2 >>  8) & 0xff)) << 8;
  Pixels2 |= *(Palette_ptr + ((ModeTable2 >> 16) & 0xff)) << 16;
  Pixels2 |= *(Palette_ptr + ((ModeTable2 >> 24) & 0xff)) << 24;

  // 8 pixels transfer to screen memory
  *(VDU->scr_current++) = Pixels1; // write four pixels
  *(VDU->scr_current++) = Pixels2; // write four pixels
}
/*---------------------------------------------------------------------*/


#define VIDEO_DRAW_MODE_2 \
{ \
  val1  = (RAM & 0x80 ? pen_on : pen_off); \
  val1 |= (RAM & 0x20 ? pen_on : pen_off) << 8; \
  val1 |= (RAM & 0x08 ? pen_on : pen_off) << 16; \
  val1 |= (RAM & 0x02 ? pen_on : pen_off) << 24; \
\
  val2  = (RAM & 0x8000 ? pen_on : pen_off); \
  val2 |= (RAM & 0x2000 ? pen_on : pen_off) << 8; \
  val2 |= (RAM & 0x0800 ? pen_on : pen_off) << 16; \
  val2 |= (RAM & 0x0200 ? pen_on : pen_off) << 24; \
}


static tVoid video_draw_mode2(tVDU* VDU,
                              tULong addr)
/***********************************************************************
 *
 *  video_draw_mode2
 *
 ***********************************************************************/
{
tULong pen_off = VDU->palette[0];
tULong pen_on = VDU->palette[1];
tULong val1, val2;
tUShort RAM = *((tUShort*)(VDU->pbRAM + addr));

  VIDEO_DRAW_MODE_2;

  // 8 pixels transfer to screen memory
  *(VDU->scr_current++) = val1; // write four pixels
  *(VDU->scr_current++) = val2; // write four pixels
}
/*---------------------------------------------------------------------*/


static tVoid video_draw_mode2_antialiased(tVDU* VDU,
                                          tULong addr)
/***********************************************************************
 *
 *  video_draw_mode2_antialiased
 *
 ***********************************************************************/
{
tULong pen_off = VDU->palette[0];
tULong pen_on = VDU->palette[1];
tULong pen_half = VDU->antialiased_color;
tULong val1, val2;
tUShort RAM = *((tUShort*)(VDU->pbRAM + addr));

  if (pen_half == pen_off)
  {
    VIDEO_DRAW_MODE_2;
  }
  else
  {
    tULong Mask = RAM & 0xC0;
    val1  = (Mask == 0 ? pen_off : Mask == 0xC0 ? pen_on : pen_half);
    Mask = RAM & 0x30;
    val1 |= (Mask == 0 ? pen_off : Mask == 0x30 ? pen_on : pen_half) << 8;
    Mask = RAM & 0x0C;
    val1 |= (Mask == 0 ? pen_off : Mask == 0x0C ? pen_on : pen_half) << 16;
    Mask = RAM & 0x03;
    val1 |= (Mask == 0 ? pen_off : Mask == 0x03 ? pen_on : pen_half) << 24;

    Mask = RAM & 0xC000;
    val2  = (Mask == 0 ? pen_off : Mask == 0xC000 ? pen_on : pen_half);
    Mask = RAM & 0x3000;
    val2 |= (Mask == 0 ? pen_off : Mask == 0x3000 ? pen_on : pen_half) << 8;
    Mask = RAM & 0x0C00;
    val2 |= (Mask == 0 ? pen_off : Mask == 0x0C00 ? pen_on : pen_half) << 16;
    Mask = RAM & 0x0300;
    val2 |= (Mask == 0 ? pen_off : Mask == 0x0300 ? pen_on : pen_half) << 24;
  }

  // 8 pixels transfer to screen memory
  *(VDU->scr_current++) = val1; // write four pixels
  *(VDU->scr_current++) = val2; // write four pixels
}
/*---------------------------------------------------------------------*/

#else /* !CRTC_ENGINE_V2 */

//
// CRTC 0 : HITACHI HD6845S, UMC UM6845
//
static tULong CRTC0_get_register(tNativeCPC* NativeCPC,
                                 tULong Index)
/***********************************************************************
 *
 *  CRTC0_get_register
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;
tULong ret_val;

  if ((Index > 11) && (Index < 16)) // valid range?
  {
    return (CRTC->registers[Index]);
  }
  else if (Index == 16)
  {
    ret_val = ((CRTC->start_addr + CRTC->lpen_offset) >> 8) & 0x3f;
    CRTC->registers[16] = ret_val;
#ifdef ENABLE_METROLOGY
    CRTC->signal_register_update++;
#endif /* ENABLE_METROLOGY */
  }
  else if (Index == 17)
  {
    if (NativeCPC->lightgun_random_crtc)
    {
      // return random value different from previous one
      ret_val = (tUChar)NativeCPC->cycle_count;
    }
    else
    {
      ret_val = (CRTC->start_addr + CRTC->lpen_offset) & 0xff;
    }
    CRTC->registers[17] = ret_val;
#ifdef ENABLE_METROLOGY
    CRTC->signal_register_update++;
#endif /* ENABLE_METROLOGY */
  }
  else
  {
    return (0); // write only registers return 0
  }

  return ret_val;
}

static tVoid CRTC0_set_register(tNativeCPC* NativeCPC,
                                tULong Index,
                                tULong val)
/***********************************************************************
 *
 *  CRTC0_set_register
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;

  switch (Index)
  {
    case 1:// R1  = Horizontal Displayed
    {
      CRTC->registers[1] = val;
      video_update_skew(CRTC);
    }
    break;

    case 3:  // R3  = Sync Width
    {
      CRTC->registers[3] = val;
      CRTC->hsw = val & 0x0f; // isolate horizontal sync width
      CRTC->vsw = val >> 4; // isolate vertical sync width
      /* UM6845 Datasheet */
      if (CRTC->vsw == 0)
      {
        CRTC->vsw = 16;
      }
    }
    break;

    case 4:  // R4  = Vertical Total
    {
      CRTC->registers[4] = val & 0x7f;
      if (CRTC->CharInstMR == video_CharMR2)
      {
        // matches vertical total?
        // matches maximum raster address?
        if ( ( (CRTC->line_count == CRTC->registers[4]) && (CRTC->raster_count == CRTC->registers[9]) )
             || (CRTC->registers[4] == 0) )
        {
          CRTC->flag_startvta = 1;
        }
      }
    }
    break;

    case 5:  // R5  = Vertical Total Adjust
    {
      CRTC->registers[5] = val & 0x1f;
    }
    break;

    case 6:  // R6  = Vertical Displayed
    {
      CRTC->registers[6] = val & 0x7f;
      // matches vertical displayed?
      if (CRTC->line_count == CRTC->registers[6])
      {
        CRTC->new_dt.NewDISPTIMG = 0;
      }
    }
    break;

    case 7: // R7  = Vertical Sync Position
    {
      CRTC->registers[7] = val & 0x7f;

      // matches vertical sync position?
      if (CRTC->line_count == CRTC->registers[7])
      {
        if (!CRTC->r7match)
        {
          CRTC->r7match = 1;
          if (CRTC->char_count >= 2)
          {
            CRTC->flag_resvsync = 0;
            if (!CRTC->flag_invsync)
            {
              CRTC->vsw_count = 0;
              CRTC->flag_invsync = 1;
              CRTC->flags1.monVSYNC = 26;
              NativeCPC->GateArray->hs_count = 2; // GA delays its VSYNC by two CRTC HSYNCs
            }
          }
        }
      }
      else
      {
        CRTC->r7match = 0;
      }
    }
    break;

    case 8:  // R8  = Interlace and Skew
    {
      CRTC->registers[8] = val;
      video_update_skew(CRTC);
    }
    break;

    case 9:  // R9  = Maximum Raster Address
    {
      CRTC->registers[9] = val & 0x1f;

      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->flag_resscan = 1; // request a raster counter reset
        if (!CRTC->r9match)
        {
          CRTC->r9match = 1;
          CRTC->CharInstMR = video_CharMR1;
        }
      }
      else
      {
        CRTC->r9match = 0;
      }
      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        if (CRTC->char_count == CRTC->registers[1])
        {
          CRTC->next_addr = CRTC->addr + CRTC->char_count;
        }
        // matches horizontal total?
        if (CRTC->char_count == CRTC->registers[0])
        {
          CRTC->flag_reschar = 1; // request a line count update
        }
        if (!CRTC->flag_startvta)
        {
          CRTC->flag_resscan = 1;
        }
      }
      else
      {
        // not in vertical total adjust?
        if (!CRTC->flag_invta)
        {
          CRTC->flag_resscan = 0;
        }
      }
    }
    break;

    case 10: // R10 = Cursor Start Raster
    {
      CRTC->registers[10] = val & 0x7f;
      CRTC->cursor_start_ras = val & 0x7f;
    }
    break;

    case 11: // R11 = Cursor End Raster
    {
      CRTC->registers[11] = val & 0x1f;
      CRTC->cursor_end_ras = val & 0x7f;
    }
    break;

    case 12: // R12 = Start Address (H)
    {
      CRTC->registers[12] = val & 0x3f;
      CRTC->requested_addr = CRTC->registers[13] + (CRTC->registers[12] << 8);
	    // Needed for lightpen and magnum light phaser
      CRTC->start_addr = CRTC->requested_addr;
    }
    break;

    case 13: // R13 = Start Address (L)
    {
      CRTC->registers[13] = val;
      CRTC->requested_addr = CRTC->registers[13] + (CRTC->registers[12] << 8);
	    // Needed for lightpen and magnum light phaser
      CRTC->start_addr = CRTC->requested_addr;
    }
    break;

    case 14: // R14 = Cursor (H)
    {
      CRTC->registers[14] = val & 0x3f;
      CRTC->cursor_addr = (CRTC->registers[14] << 8) | CRTC->registers[15];
    }
    break;

    case 15: // R15 = Cursor (L)
    {
      CRTC->registers[15] = val;
      CRTC->cursor_addr = (CRTC->registers[14] << 8) | CRTC->registers[15];
    }
    break;

    // R0  = Horizontal Total
    // R2  = Horizontal Sync Position
    default:
    {
      CRTC->registers[Index] = val;
    }
  } /* switch */
}


static tULong CRTC0_get_status(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  CRTC0_get_status
 *
 ***********************************************************************/
{
	NOT_USED(NativeCPC);

  return 0xff; // default return value
}

static tVoid CRTC0_video_engine(tNativeCPC* NativeCPC, tULong repeat_count)
/***********************************************************************
 *
 *  CRTC0_video_engine
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;
tVDU* VDU = NativeCPC->VDU;

  while (repeat_count--)
  {
    // are we within the rendering area?
    if (VDU->flag_drawing)
    {
      // below horizontal cut-off?
      if (CRTC->HorzChar < CRTC->HorzMax)
      {
        if (CRTC->flags1.combined != CRTC->LastPreRend)
        {
          video_set_prerender(CRTC); // change pre-renderer if necessary
        }
        
        CRTC->PreRender(NativeCPC); // translate CPC video memory bytes to entries referencing the palette
        CRTC->Render(NativeCPC); // render to the video surface at the current bit depth
      }
    }

#ifdef PALETTE_PATCH_1
    if (NativeCPC->GateArray->newcolorcount)
    {
      NativeCPC->GateArray->newcolorcount = 0;
      NativeCPC->GateArray->ink_values[NativeCPC->GateArray->pen] = NativeCPC->GateArray->newcolor;
      NativeCPC->GateArray->palette[NativeCPC->GateArray->pen] = NativeCPC->colours[NativeCPC->GateArray->newcolor]; // isolate colour value
    }
#endif /* PALETTE_PATCH_1 */

#ifdef ENABLE_PLAYCITY
    if (NativeCPC->PlayCity->enabled)
    {
      if ( (CRTC->raster_count >= CRTC->cursor_start_ras)
           && (CRTC->raster_count <= CRTC->cursor_end_ras) )
      {
        if ((CRTC->addr + CRTC->char_count) == (CRTC->cursor_addr + CRTC->start_addr))
        {
          PlayCity_trigger_channel_1(NativeCPC);
        }
      }
    }
#endif /* ENABLE_PLAYCITY */

    // next address for PreRender    
    CRTC->next_address = VDU->MAXlate[(CRTC->addr + CRTC->char_count) & 0x73ff] | CRTC->scr_base;
    CRTC->flags1.dt.combined = CRTC->new_dt.combined; // update the DISPTMG flags

    CRTC->iMonHSStartPos += 0x100;
    CRTC->iMonHSEndPos += 0x100;
    CRTC->iMonHSPeakPos += 0x100;
    CRTC->HorzPos += 0x100;
    CRTC->HorzChar++;

    if (CRTC->HorzPos >= CRTC->MonHSYNC)
    {
      if (VDU->flag_drawing)
      {
        VDU->scr_base += VDU->scr_line_offs; // advance surface pointer to next row
      }
      CRTC->HadP = 1;
      CRTC->iMonHSPeakPos = CRTC->HorzPos - CRTC->MonHSYNC;
      CRTC->iMonHSStartToPeak = CRTC->iMonHSStartPos - CRTC->iMonHSPeakPos;
      CRTC->iMonHSEndToPeak = CRTC->iMonHSEndPos - CRTC->iMonHSPeakPos;
      CRTC->HorzPos = CRTC->iMonHSPeakPos - CRTC->HSyncDuration;

      // CAUTION : CRTC->HorzPos can be negative and CRTC->HorzChar is unsigned !!
      CRTC->HorzChar = CRTC->HorzPos >> 8;

      {
        tULong val = (CRTC->HorzPos & 0xf0) >> 4; // PosShift
        if (!val)
        {
          CRTC->HorzMax = 48;
          CRTC->HorzPix[0] = CRTC->HorzPix[1];
          CRTC->RendPos = CRTC->RendStart;
          CRTC->HorzChar--;
        }
        else
        {
          tULong tmp;
          CRTC->RendPos = (tULong*)&CRTC->RendBuff[val];
          tmp = (tULong)CRTC->RendStart - (tULong)CRTC->RendPos;
          CRTC->HorzPix[48] = (tUChar)tmp;
          CRTC->HorzPix[0] = CRTC->HorzPix[1] - (tUChar)tmp;
          CRTC->HorzMax = 49;
        }
      }

      CRTC->RendOut = (tUChar*)CRTC->RendStart;
      CRTC->RendWid = &CRTC->HorzPix[0];
      VDU->scr_pos = VDU->scr_base;
      VDU->scrln++;
      VDU->scanline++;
      if ((tULong)VDU->scrln >= MAX_DRAWN)
      {
        VDU->flag_drawing = 0;
      }
      else
      {
        VDU->flag_drawing = 1;
      }
    }

    // ----------------------------------------------------------------------------

    // lightgun beam detection should be performed once screen written.
    if (NativeCPC->lightgun_beam_detect)
    {
      if (NativeCPC->timer_from_reset > NativeCPC->lightgun_trigger_delay)
      {
        if (!NativeCPC->lightgun_counter)
        {
          // Get Y difference
          tULong YDiff;
          if (NativeCPC->lightgun_y_pos > (tULong)VDU->scrln)
          {
            YDiff = NativeCPC->lightgun_y_pos - (tULong)VDU->scrln;
          }
          else
          {
            YDiff = (tULong)VDU->scrln - NativeCPC->lightgun_y_pos;
          }

          if (YDiff <= NativeCPC->lightgun_y_radius)
          {
            if (NativeCPC->lightgun_x_pos == CRTC->char_count)
            {
              // Check if last drawn pixel is bright enough.
              tULong PixelIntensity;
              // Use of color palette ?
              if (NativeCPC->BitsPerPixel == 8)
              {
                tUChar PixelColor = *(VDU->scr_pos-1);
                PixelIntensity = NativeCPC->WinPalettePtr[PixelColor].intensity;
              }
              // monochrome display
              else if (NativeCPC->active_colours == NativeCPC->colours_green)
              {
                tULong PixelColor = *(VDU->scr_pos-1);
                PixelIntensity = PixelColor & 0xff;
                PixelIntensity += (PixelColor >> 8) & 0xff;
                PixelIntensity += (PixelColor >> 16) & 0xff;
              }
              // 24bits and 32bits display
              else
              {
                tULong PixelColor = *(VDU->scr_pos-1);
                PixelIntensity = PixelColor & 0xff;
                PixelIntensity += (PixelColor >> 8) & 0xff;
                PixelIntensity += (PixelColor >> 16) & 0xff;
                PixelIntensity /= 3;
              }

              if (PixelIntensity >= NativeCPC->lightgun_sensitivity)
              {
                NativeCPC->lightgun_counter = NativeCPC->timer_from_reset + NativeCPC->lightgun_beam_duration;
#ifdef ENABLE_METROLOGY
                NativeCPC->signal_lightgun_beam_detected++;
#endif /* ENABLE_METROLOGY */
              }
            }
          }
        }
        else if (NativeCPC->timer_from_reset > NativeCPC->lightgun_counter)
        {
          // Stop lightgun beam key pressed generation
          NativeCPC->lightgun_counter=0;
        }
      }
    }

    // ----------------------------------------------------------------------------

    // matches horizontal total?
    if (CRTC->char_count == CRTC->registers[0])
    {
      CRTC->last_hend = CRTC->char_count; // preserve current line length in chars
      CRTC->flag_newscan = 1; // request starting a new scan line
      CRTC->char_count = 0; // reset the horizontal character count
    }
    else
    {
      CRTC->char_count++; // update counter
      CRTC->char_count &= 255; // limit to 8 bits
    }

    // matches horizontal total?
    if (CRTC->char_count == CRTC->registers[0])
    {
      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->flag_reschar = 1; // request a line count update
      }
      else
      {
        CRTC->flag_reschar = 0; // still within the current character line
      }
      // ready to restart frame?
      if (CRTC->flag_resnext)
      {
        CRTC->flag_resnext = 0;
        CRTC->flag_resframe = 1; // request a frame restart
      }
      // ready to start vertical total adjust?
      if (CRTC->flag_startvta)
      {
        CRTC->flag_startvta = 0;
        CRTC->flag_invta = 1; // entering vertical total adjust
      }
      // in vertical total adjust?
      if (CRTC->flag_invta)
      {
        if ( (CRTC->raster_count == CRTC->registers[9])
             && (CRTC->line_count == CRTC->registers[4]) )
        {
          CRTC->flag_resscan = 1; // raster counter only resets once at start of vta
        }
        else
        {
          CRTC->flag_resscan = 0; // raster counter keeps increasing while in vta
        }
      }
    }

    // matches horizontal displayed?
    if (CRTC->char_count == CRTC->registers[1])
    {
      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->next_addr = CRTC->addr + CRTC->char_count;
      }
    }

    // not in HSYNC?
    if (!CRTC->flags1.inHSYNC)
    {
      // matches horizontal sync position?
      if (CRTC->char_count == CRTC->registers[2])
      {
        CRTC->flags1.inHSYNC = 0xff; // turn HSYNC on
        CRTC->flag_hadhsync = 1; // prevent GA from processing more than one HSYNC per scan line
        CRTC->hsw_count = 0; // initialize horizontal sync width counter
#ifndef CRTC_PATCH_2
        video_match_hsw(NativeCPC);
#endif /* CRTC_PATCH_2 */
      }
    }
    else
    {
      video_match_hsw(NativeCPC);
    }

    CRTC->CharInstSL(CRTC); // if necessary, process vertical total delay
    CRTC->CharInstMR(CRTC); // if necessary, process maximum raster count delay

    // scanline change requested?
    if (CRTC->flag_newscan)
    {
      CRTC->flag_newscan = 0;
      CRTC->addr = CRTC->next_addr;

      // VSYNC active?
      if (CRTC->flag_invsync)
      {
        CRTC->vsw_count++; // update counter
        /* from Caprice Reloaded r623 */
        /* OK Backtro */
        CRTC->vsw_count &= 31; // limit to 5 bits (to handle "double vbl" case)

        // matches vertical sync width?
        if (CRTC->vsw_count == CRTC->vsw)
        {
          CRTC->vsw_count = 0; // reset counter
          CRTC->flag_resvsync = 1; // request VSYNC reset
        }
      }

      // frame restart requested?
      if (CRTC->flag_resframe)
      {
        video_restart_frame(NativeCPC);
      }
      else
      {
        // raster counter reset requested?
        if (CRTC->flag_resscan)
        {
          CRTC->flag_resscan = 0;
          CRTC->raster_count = 0; // reset counter
          CRTC->scr_base = 0;
        }
        else
        {
          CRTC->raster_count++; // update counter
          CRTC->raster_count &= 31; // limit to 5 bits
          // did the counter wrap around?
          if (!CRTC->raster_count)
          {
            video_match_line_count(NativeCPC);
          }
          CRTC->scr_base = (CRTC->scr_base + 0x0800) & 0x3800;
        }
      }

      CRTC->CharInstSL = video_CharSL1;

      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->flag_resscan = 1; // request a raster counter reset
        CRTC->r9match = 1;
        CRTC->CharInstMR = video_CharMR1;
      }
      else
      {
        CRTC->r9match = 0;
      }

      // in vertical total adjust?
      if (CRTC->flag_invta)
      {
        // matches vertical total adjust?
        if (CRTC->raster_count == CRTC->reg5)
        {
          video_restart_frame(NativeCPC);
          // maximum raster address is zero?
          if (CRTC->registers[9] == 0)
          {
            CRTC->flag_resscan = 1; // request a raster counter reset
          }
        }
      }

      // line count update requested?
      if (CRTC->flag_reschar)
      {
        CRTC->line_count++; // update counter
        CRTC->line_count &= 127; // limit to 7 bits
        video_reload_addr(NativeCPC);
      }

      // in VSYNC?
      if (CRTC->flag_invsync)
      {
        // end of VSYNC?
        if (CRTC->flag_resvsync)
        {
          CRTC->flag_invsync = 0; // turn VSYNC off
          CRTC->flag_resvsync = 0;
          // above maximum scanline count?
          if (VDU->scanline >= CRTC->MaxVSync)
          {
            video_frame_finished(VDU);
          }
        }
        else
        {
          // above minimum scanline count?
          if (VDU->scanline > CRTC->MinVSync)
          {
            video_frame_finished(VDU);
          }
        }
      }
      // above maximum scanline count?
      else if (VDU->scanline >= CRTC->MaxVSync)
      {
        video_frame_finished(VDU);
      }
    }

    // leaving border area?
    if (CRTC->char_count == CRTC->hstart)
    {
      CRTC->new_dt.NewHDSPTIMG |= 0x01;
    }
    // entering border area?
    if (CRTC->char_count == CRTC->hend)
    {
      CRTC->new_dt.NewHDSPTIMG &= 0xfe;
    }
  }
}
/*---------------------------------------------------------------------*/


static tVoid video_update_skew(tCRTC* CRTC)
/***********************************************************************
 *
 *  video_update_skew
 *
 ***********************************************************************/
{
tULong skew;

  CRTC->new_dt.NewHDSPTIMG |= 0x02; // enable horizontal DISPTMG by default

  skew = (CRTC->registers[8] >> 4) & 3; // isolate the display skew
  if (skew == 3) // disable output?
  {
    CRTC->new_dt.NewHDSPTIMG &= 0xfd; // disable horizontal DISPTMG
  }
  else
  {
    CRTC->hstart = skew; // position at which horizontal display starts
    CRTC->hend = CRTC->hstart + CRTC->registers[1]; // position at which it ends
  }
}
/*---------------------------------------------------------------------*/


static tVoid video_match_hsw(tNativeCPC * NativeCPC)
/***********************************************************************
 *
 *  video_match_hsw
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;

  // matches horizontal sync width?
  if (CRTC->hsw_count == CRTC->hsw)
  {
    tGateArray* GateArray = NativeCPC->GateArray;
    
    GateArray->sl_count++; // update GA scan line counter

    // trigger interrupt?
    if (GateArray->sl_count == 52)
    {
      NativeCPC->Z80->Regs.int_pending = 1; // queue Z80 interrupt
      GateArray->sl_count = 0; // clear counter
    }

    // delaying on VSYNC?
    if (GateArray->hs_count)
    {
      GateArray->hs_count--;
      if (!GateArray->hs_count)
      {
        // counter above save margin?
        if (GateArray->sl_count >= 32)
        {
          NativeCPC->Z80->Regs.int_pending = 1; // queue interrupt
        }
        GateArray->sl_count = 0; // clear counter
      }
    }

    CRTC->flags1.inHSYNC = 0; // turn HSYNC off

    // in vertical blanking period?
    if (CRTC->flags1.monVSYNC)
    {
      CRTC->flags1.monVSYNC--; // update counter
    }

    video_change_mode(NativeCPC); // process possible mode change

    // monitor HSYNC still active?
    if (CRTC->flag_inmonhsync)
    {
      video_end_vdu_hsync(CRTC);
    }
  }
  else
  {
    CRTC->hsw_count++; // update counter
    CRTC->hsw_count &= 15; // limit to 4 bits

    // ready to start monitor HSYNC?
    if (CRTC->hsw_count == 3)
    {
      CRTC->flag_inmonhsync = 1; // enter monitor HSYNC
      CRTC->iMonHSStartPos = 0;
      CRTC->iMonHSPeakToStart = CRTC->iMonHSPeakPos;
    }
    // reached GA HSYNC output cutoff?
    else if (CRTC->hsw_count == 7)
    {
      video_change_mode(NativeCPC);
      video_end_vdu_hsync(CRTC);
    }
  }
}
/*---------------------------------------------------------------------*/


static tVoid video_change_mode(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_change_mode
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;

  // have we had an HSYNC on this scan line?
  if (CRTC->flag_hadhsync)
  {
    tGateArray* GateArray = NativeCPC->GateArray;
    
    CRTC->flag_hadhsync = 0;
    GateArray->scr_mode = GateArray->requested_scr_mode; // execute mode change
    CRTC->ModeMap = CRTC->ModeMaps[GateArray->scr_mode]; // update ModeMap pointer
  }
}
/*---------------------------------------------------------------------*/


static tVoid video_restart_frame(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_restart_frame
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;

  CRTC->flag_invta = 0;
  CRTC->flag_resframe = 0;
  CRTC->flag_resscan = 0;
  CRTC->flag_reschar = 0;
  CRTC->raster_count = 0; // reset raster line counter
  CRTC->scr_base = 0;
  CRTC->line_count = 0; // reset character line counter

  video_reload_addr(NativeCPC);
}
/*---------------------------------------------------------------------*/


static tVoid video_end_vdu_hsync(tCRTC* CRTC)
/***********************************************************************
 *
 *  video_end_vdu_hsync
 *
 ***********************************************************************/
{
tLong temp;

  CRTC->flag_inmonhsync = 0;
  CRTC->iMonHSPeakToEnd = CRTC->iMonHSPeakPos;
  if (CRTC->HadP)
  {
    CRTC->HadP = 0;
    if (CRTC->iMonHSPeakPos >= CRTC->iMonHSStartPos)
    {
      temp = CRTC->iMonHSEndPos - CRTC->HSyncDuration;
      if (temp < CRTC->MonFreeSync)
      {
        if (CRTC->MonFreeSync != CRTC->MinHSync)
        {
          CRTC->MonFreeSync--;
        }
      }
      else if (temp > CRTC->MonFreeSync)
      {
        if (CRTC->MonFreeSync != CRTC->MaxHSync)
        {
          CRTC->MonFreeSync++;
        }
      }

      temp = CRTC->iMonHSPeakToEnd - CRTC->iMonHSEndToPeak;
      if (temp < 0)
      {
        temp = -temp;
        if (temp > CRTC->iMonHSStartPos)
        {
          temp = CRTC->iMonHSStartPos;
        }
        temp >>= 3;
        if (!temp)
        {
          temp++;
        }
#ifndef DEBUG_VDU_CONSTANTS
        if (temp > MAX_SYNC_INC)
        {
          temp = MAX_SYNC_INC;
        }
#else /* DEBUG_VDU_CONSTANTS */
        if (temp > (tLong)CRTC->sync_inc_max)
        {
          temp = CRTC->sync_inc_max;
        }
#endif /* DEBUG_VDU_CONSTANTS */
        CRTC->MonHSYNC = CRTC->MonFreeSync + temp;
        if (CRTC->MonHSYNC > CRTC->MaxHSync)
        {
          CRTC->MonHSYNC = CRTC->MaxHSync;
        }
      }
      else
      {
        if (temp > CRTC->iMonHSStartPos)
        {
          temp = CRTC->iMonHSStartPos;
        }
        temp >>= 3;
        if (!temp)
        {
          temp++;
        }
#ifndef DEBUG_VDU_CONSTANTS
        if (temp > MAX_SYNC_DEC)
        {
          temp = MAX_SYNC_DEC;
        }
#else /* DEBUG_VDU_CONSTANTS */
        if (temp > (tLong)CRTC->sync_dec_max)
        {
          temp = CRTC->sync_dec_max;
        }
#endif /* DEBUG_VDU_CONSTANTS */
        CRTC->MonHSYNC = CRTC->MonFreeSync - temp;
        if (CRTC->MonHSYNC < CRTC->MinHSync)
        {
          CRTC->MonHSYNC = CRTC->MinHSync;
        }
      }
    }
    else
    {
      temp = CRTC->iMonHSStartToPeak - CRTC->iMonHSPeakToEnd;
      if (!temp)
      {
        CRTC->MonHSYNC = CRTC->MonFreeSync;
      }
      else if (temp < 0)
      {
        temp = -temp;
        if (temp > CRTC->iMonHSStartPos)
        {
          temp = CRTC->iMonHSStartPos;
        }
        temp >>= 3;
        if (!temp)
        {
          temp++;
        }
#ifndef DEBUG_VDU_CONSTANTS
        if (temp > MAX_SYNC_INC)
        {
          temp = MAX_SYNC_INC;
        }
#else /* DEBUG_VDU_CONSTANTS */
        if (temp > (tLong)CRTC->sync_inc_max)
        {
          temp = CRTC->sync_inc_max;
        }
#endif /* DEBUG_VDU_CONSTANTS */
        CRTC->MonHSYNC = CRTC->MonFreeSync + temp;
        if (CRTC->MonHSYNC > CRTC->MaxHSync)
        {
          CRTC->MonHSYNC = CRTC->MaxHSync;
        }
      }
      else if (temp > 0)
      {
        if (temp > CRTC->iMonHSStartPos)
        {
          temp = CRTC->iMonHSStartPos;
        }
        temp >>= 3;
        if (!temp)
        {
          temp++;
        }
#ifndef DEBUG_VDU_CONSTANTS
        if (temp > MAX_SYNC_DEC)
        {
          temp = MAX_SYNC_DEC;
        }
#else /* DEBUG_VDU_CONSTANTS */
        if (temp > (tLong)CRTC->sync_dec_max)
        {
          temp = CRTC->sync_dec_max;
        }
#endif /* DEBUG_VDU_CONSTANTS */
        CRTC->MonHSYNC = CRTC->MonFreeSync - temp;
        if (CRTC->MonHSYNC < CRTC->MinHSync)
        {
          CRTC->MonHSYNC = CRTC->MinHSync;
        }
      }
    }
  }
  CRTC->iMonHSEndPos = 0;
}
/*---------------------------------------------------------------------*/


static tVoid video_match_line_count(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_match_line_count
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;

  // matches vertical displayed?
  if (CRTC->line_count == CRTC->registers[6])
  {
    CRTC->new_dt.NewDISPTIMG = 0; // disable vertical DISPTMG
  }

  // matches vertical sync position?
  if (CRTC->line_count == CRTC->registers[7])
  {
    // line length was at least 2 chars?
    if (CRTC->last_hend >= 2)
    {
      if (!CRTC->r7match)
      {
        CRTC->flag_resvsync = 0;
        // not in VSYNC?
        if (!CRTC->flag_invsync)
        {
          CRTC->vsw_count = 0; // reset vertical sync width counter
          CRTC->flag_invsync = 1; // enter VSYNC
          CRTC->flags1.monVSYNC = 26; // enter vertical blanking period for 26 scanlines
          NativeCPC->GateArray->hs_count = 2; // GA delays its VSYNC by two CRTC HSYNCs
        }
      }
    }
    CRTC->r7match = 1;
  }
  else
  {
    CRTC->r7match = 0;
  }
}
/*---------------------------------------------------------------------*/


static tVoid video_reload_addr(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_reload_addr
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;

  // has line count been reset?
  if (CRTC->line_count == 0)
  {
    CRTC->new_dt.NewDISPTIMG = 0xff; // enable vertical DISPTMG
    CRTC->addr = CRTC->next_addr = CRTC->requested_addr; // load with screen start address
  }

  video_match_line_count(NativeCPC);
}
/*---------------------------------------------------------------------*/


static tVoid video_frame_finished(tVDU* VDU)
/***********************************************************************
 *
 *  video_frame_finished
 *
 ***********************************************************************/
{
  VDU->frame_completed = 1; // emulation loop exit condition met
#ifndef DEBUG_VDU_CONSTANTS
  VDU->scrln = -(((VDU->scanline - MIN_VHOLD) + 1) >> 1);
#else /* DEBUG_VDU_CONSTANTS */
  VDU->scrln = -((tLong)(((VDU->scanline - VDU->vhold_min) + 1) >> 1));
#endif /* DEBUG_VDU_CONSTANTS */
  VDU->scanline = 0;
  VDU->flag_drawing = 0;
}
/*---------------------------------------------------------------------*/


static tVoid video_NoChar(tCRTC* CRTC)
/***********************************************************************
 *
 *  video_NoChar
 *
 ***********************************************************************/
{
  //
  // nothing to do
  //

NOT_USED(CRTC);
}
/*---------------------------------------------------------------------*/

static tVoid video_CharSL1(tCRTC* CRTC)
/***********************************************************************
 *
 *  video_CharSL1
 *
 ***********************************************************************/
{
   CRTC->CharInstSL = video_CharSL2;
}
/*---------------------------------------------------------------------*/

static tVoid video_CharSL2(tCRTC* CRTC)
/***********************************************************************
 *
 *  video_CharSL2
 *
 ***********************************************************************/
{
   CRTC->reg5 = CRTC->registers[5];
   CRTC->CharInstSL = video_NoChar;
}
/*---------------------------------------------------------------------*/

static tVoid video_CharMR1(tCRTC* CRTC)
/***********************************************************************
 *
 *  video_CharMR1
 *
 ***********************************************************************/
{
  if ( ( (CRTC->raster_count == CRTC->registers[9]) && (CRTC->line_count == CRTC->registers[4]) )
       || (CRTC->registers[4] == 0) )
  {
    CRTC->flag_invta = 0;
    CRTC->flag_startvta = 1; // request start of vertical total adjust
  }
  else
  {
    CRTC->flag_startvta = 0; // not yet at end of frame
  }

  CRTC->CharInstMR = video_CharMR2;
}
/*---------------------------------------------------------------------*/

static tVoid video_CharMR2(tCRTC* CRTC)
/***********************************************************************
 *
 *  video_CharMR2
 *
 ***********************************************************************/
{
  // starting vertical total adjust?
  if (CRTC->flag_startvta)
  {
    // matches vertical total?
    if ( (CRTC->line_count == CRTC->registers[4])
         || (CRTC->registers[4] == 0) )
    {
      // no vertical total adjust?
      if (CRTC->registers[5] == 0)
      {
        CRTC->flag_resnext = 1; // request a frame restart
      }
    }
  }

  CRTC->CharInstMR = video_NoChar;
}
/*---------------------------------------------------------------------*/

static tVoid video_set_prerender(tCRTC* CRTC)
/***********************************************************************
 *
 *  video_set_prerender
 *
 ***********************************************************************/
{
  CRTC->LastPreRend = CRTC->flags1.combined;
  if (CRTC->LastPreRend == 0x03ff0000)
  {
    CRTC->PreRender = video_prerender_normal;
  }
  else
  {
    if (!(tUShort)CRTC->LastPreRend)
    {
      CRTC->PreRender = video_prerender_border;
    }
    else
    {
      CRTC->PreRender = video_prerender_sync;
    }
  }
}
/*---------------------------------------------------------------------*/

static tVoid video_prerender_normal(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_prerender_normal
 *
 ***********************************************************************/
{
#ifndef CRTC_OPTIMIZE

tCRTC* CRTC = NativeCPC->CRTC;

	tUChar bVidMem = *(NativeCPC->pbRAM + CRTC->next_address);
	*CRTC->RendPos = *(CRTC->ModeMap + (bVidMem * 2));
	*(CRTC->RendPos + 1) = *(CRTC->ModeMap + (bVidMem * 2) + 1);

	bVidMem = *(NativeCPC->pbRAM + CRTC->next_address + 1);
	*(CRTC->RendPos + 2) = *(CRTC->ModeMap + (bVidMem * 2));
	*(CRTC->RendPos + 3) = *(CRTC->ModeMap + (bVidMem * 2) + 1);

	CRTC->RendPos += 4;

#else /* CRTC_OPTIMIZE */

tCRTC* CRTC = NativeCPC->CRTC;

	const tULong* ModeMap = CRTC->ModeMap + (*(NativeCPC->pbRAM + CRTC->next_address) * 2);
	*CRTC->RendPos = *ModeMap++;
	*(CRTC->RendPos + 1) = *ModeMap;

	ModeMap = CRTC->ModeMap + (*(NativeCPC->pbRAM + CRTC->next_address + 1) * 2);
	*(CRTC->RendPos + 2) = *ModeMap++;
	*(CRTC->RendPos + 3) = *ModeMap;

	CRTC->RendPos += 4;

#endif /* CRTC_OPTIMIZE */
}
/*---------------------------------------------------------------------*/

static tVoid video_prerender_border(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_prerender_border
 *
 ***********************************************************************/
{
#ifndef CRTC_OPTIMIZE

tCRTC* CRTC = NativeCPC->CRTC;
tULong dwVal = 0x10101010;

  *CRTC->RendPos = dwVal;
  *(CRTC->RendPos + 1) = dwVal;
  *(CRTC->RendPos + 2) = dwVal;
  *(CRTC->RendPos + 3) = dwVal;

	CRTC->RendPos += 4;

#else /* CRTC_OPTIMIZE */

#undef CRTC_VALUE
#define CRTC_VALUE 0x10101010
tCRTC* CRTC = NativeCPC->CRTC;

  *CRTC->RendPos = CRTC_VALUE;
  *(CRTC->RendPos + 1) = CRTC_VALUE;
  *(CRTC->RendPos + 2) = CRTC_VALUE;
  *(CRTC->RendPos + 3) = CRTC_VALUE;

	CRTC->RendPos += 4;

#endif /* CRTC_OPTIMIZE */
}
/*---------------------------------------------------------------------*/

static tVoid video_prerender_sync(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_prerender_sync
 *
 ***********************************************************************/
{
#ifndef CRTC_OPTIMIZE

tCRTC* CRTC = NativeCPC->CRTC;
tULong dwVal = 0x11111111;

   *CRTC->RendPos = dwVal;
   *(CRTC->RendPos + 1) = dwVal;
   *(CRTC->RendPos + 2) = dwVal;
   *(CRTC->RendPos + 3) = dwVal;

	 CRTC->RendPos += 4;

#else /* CRTC_OPTIMIZE */

#undef CRTC_VALUE
#define CRTC_VALUE 0x11111111
tCRTC* CRTC = NativeCPC->CRTC;

  *CRTC->RendPos = CRTC_VALUE;
  *(CRTC->RendPos + 1) = CRTC_VALUE;
  *(CRTC->RendPos + 2) = CRTC_VALUE;
  *(CRTC->RendPos + 3) = CRTC_VALUE;

	CRTC->RendPos += 4;

#endif /* CRTC_OPTIMIZE */
}
/*---------------------------------------------------------------------*/

static tVoid video_render8bpp(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_render8bpp
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;
tUChar* pbPos = (tUChar*)NativeCPC->VDU->scr_pos;
tUChar bCount = *CRTC->RendWid++;

  while (bCount--)
  {
    *pbPos++ = NativeCPC->GateArray->palette[*CRTC->RendOut++];

#ifdef PALETTE_PATCH_1
    if (NativeCPC->GateArray->newcolorcount)
    {
      NativeCPC->GateArray->newcolorcount--;
      if (!NativeCPC->GateArray->newcolorcount)
      {
        NativeCPC->GateArray->ink_values[NativeCPC->GateArray->pen] = NativeCPC->GateArray->newcolor;
        NativeCPC->GateArray->palette[NativeCPC->GateArray->pen] = NativeCPC->colours[NativeCPC->GateArray->newcolor]; // isolate colour value
      }
    }
#endif /* PALETTE_PATCH_1 */
  }

  NativeCPC->VDU->scr_pos = (tULong*)pbPos;
}
/*---------------------------------------------------------------------*/

static tVoid video_render24bpp(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_render24bpp
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;
tUChar* pbPos = (tUChar *)NativeCPC->VDU->scr_pos;
tUChar bCount = *CRTC->RendWid++;

  while (bCount--)
  {
    tULong val = NativeCPC->GateArray->palette[*CRTC->RendOut++];
    *(tUShort*)pbPos = (tUShort)val;
    *(pbPos + 2) = (tUChar)(val >> 16);
    pbPos += 3;

#ifdef PALETTE_PATCH_1
    if (NativeCPC->GateArray->newcolorcount)
    {
      NativeCPC->GateArray->newcolorcount--;
      if (!NativeCPC->GateArray->newcolorcount)
      {
        NativeCPC->GateArray->ink_values[NativeCPC->GateArray->pen] = NativeCPC->GateArray->newcolor;
        NativeCPC->GateArray->palette[NativeCPC->GateArray->pen] = NativeCPC->colours[NativeCPC->GateArray->newcolor]; // isolate colour value
      }
    }
#endif /* PALETTE_PATCH_1 */
  }

  NativeCPC->VDU->scr_pos = (tULong *)pbPos;
}
/*---------------------------------------------------------------------*/

static tVoid video_render32bpp(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  video_render32bpp
 *
 ***********************************************************************/
{
tUChar bCount = *NativeCPC->CRTC->RendWid++;

  while (bCount--)
  {
    *NativeCPC->VDU->scr_pos++ = NativeCPC->GateArray->palette[*NativeCPC->CRTC->RendOut++];

#ifdef PALETTE_PATCH_1
    if (NativeCPC->GateArray->newcolorcount)
    {
      NativeCPC->GateArray->newcolorcount--;
      if (!NativeCPC->GateArray->newcolorcount)
      {
        NativeCPC->GateArray->ink_values[NativeCPC->GateArray->pen] = NativeCPC->GateArray->newcolor;
        NativeCPC->GateArray->palette[NativeCPC->GateArray->pen] = NativeCPC->colours[NativeCPC->GateArray->newcolor]; // isolate colour value
      }
    }
#endif /* PALETTE_PATCH_1 */
  }
}
/*---------------------------------------------------------------------*/


//
// CRTC 1 : UMC UM6845R
//
static tULong CRTC1_get_register(tNativeCPC* NativeCPC,
                                 tULong Index)
/***********************************************************************
 *
 *  CRTC1_get_register
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;
tULong ret_val;

  if ((Index == 14) || (Index == 15)) // valid range?
  {
    return (CRTC->registers[Index]);
  }
  else if (Index == 16)
  {
    ret_val = ((CRTC->start_addr + CRTC->lpen_offset) >> 8) & 0x3f;
    CRTC->registers[16] = ret_val;
    CRTC->lpen_strobe = 0;
#ifdef ENABLE_METROLOGY
    CRTC->signal_register_update++;
#endif /* ENABLE_METROLOGY */
  }
  else if (Index == 17)
  {
    if (NativeCPC->lightgun_random_crtc)
    {
      // return random value different from previous one
      ret_val = (tUChar)NativeCPC->cycle_count;
    }
    else
    {
      ret_val = (CRTC->start_addr + CRTC->lpen_offset) & 0xff;
    }
    CRTC->registers[17] = ret_val;
    CRTC->lpen_strobe = 0;
#ifdef ENABLE_METROLOGY
    CRTC->signal_register_update++;
#endif /* ENABLE_METROLOGY */
  }
  else if (Index == 31)
  {
    return (0xff);
  }
  else
  {
    return (0); // write only registers return 0
  }

  return ret_val;
}

static tVoid CRTC1_set_register(tNativeCPC* NativeCPC,
                                tULong Index,
                                tULong val)
/***********************************************************************
 *
 *  CRTC1_set_register
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;

  switch (Index)
  {
    case 1:  // R1  = Horizontal Displayed
    {
      CRTC->registers[1] = val;
      video_update_skew(CRTC);
    }
    break;

    case 3:  // R3  = Sync Width
    {
      CRTC->registers[3] = val;
      CRTC->hsw = val & 0x0f;
      CRTC->vsw = 16;
    }
    break;

    case 4:  // R4  = Vertical Total
    {
      CRTC->registers[4] = val & 0x7f;
      if (CRTC->CharInstMR == CRTC1_CharMR2)
      {
        // matches vertical total?
        // matches maximum raster address?
        if ( ( (CRTC->line_count == CRTC->registers[4]) && (CRTC->raster_count == CRTC->registers[9]) )
             || (CRTC->registers[4] == 0) )
        {
          CRTC->flag_startvta = 1;
        }
      }
    }
    break;

    case 5:  // R5  = Vertical Total Adjust
    {
      CRTC->registers[5] = val & 0x1f;
    }
    break;

    case 6:  // R6  = Vertical Displayed
    {
      CRTC->registers[6] = val & 0x7f;
      // matches vertical displayed?
      if (CRTC->line_count == CRTC->registers[6])
      {
        CRTC->new_dt.NewDISPTIMG = 0;
      }
    }
    break;

    case 7: // R7  = Vertical Sync Position
    {
      CRTC->registers[7] = val & 0x7f;

      // matches vertical sync position?
      if (CRTC->line_count == CRTC->registers[7])
      {
        if (!CRTC->r7match)
        {
          CRTC->r7match = 1;
          if (CRTC->char_count >= 2)
          {
            CRTC->flag_resvsync = 0;
            if (!CRTC->flag_invsync)
            {
              CRTC->vsw_count = 0;
              CRTC->flag_invsync = 1;
              CRTC->flags1.monVSYNC = 26;
              NativeCPC->GateArray->hs_count = 2; // GA delays its VSYNC by two CRTC HSYNCs
            }
          }
        }
      }
      else
      {
        CRTC->r7match = 0;
      }
    }
    break;

    case 8:  // R8  = Mode Control
    {
      CRTC->registers[8] = val;
      video_update_skew(CRTC);
    }
    break;

    case 9:  // R9  = Maximum Raster Address
    {
      CRTC->registers[9] = val & 0x1f;

      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->flag_resscan = 1; // request a raster counter reset
        if (!CRTC->r9match)
        {
          CRTC->r9match = 1;
          CRTC->CharInstMR = CRTC1_CharMR1;
        }
      }
      else
      {
        CRTC->r9match = 0;
      }
      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        if (CRTC->char_count == CRTC->registers[1])
        {
          CRTC->next_addr = CRTC->addr + CRTC->char_count;
        }
        // matches horizontal total?
        if (CRTC->char_count == CRTC->registers[0])
        {
          CRTC->flag_reschar = 1; // request a line count update
        }
        if (!CRTC->flag_startvta)
        {
          CRTC->flag_resscan = 1;
        }
      }
#ifdef CRTC_PATCH_1
      else
      {
        // not in vertical total adjust?
        if (!CRTC->flag_invta)
        {
          CRTC->flag_resscan = 0;
        }
      }
#endif /* CRTC_PATCH_1 */
    }
    break;

    case 10: // R10 = Cursor Start Raster
    {
      CRTC->registers[10] = val & 0x1f;
      CRTC->cursor_start_ras = val & 0x7f;
    }
    break;

    case 11: // R11 = Cursor End Raster
    {
      CRTC->registers[11] = val & 0x1f;
      CRTC->cursor_end_ras = val & 0x7f;
    }
    break;

    case 12: // R12 = Start Address (H)
    {
      CRTC->registers[12] = val & 0x3f;
      CRTC->requested_addr = CRTC->registers[13] + (CRTC->registers[12] << 8);
	    // Needed for lightpen and magnum light phaser
      CRTC->start_addr = CRTC->requested_addr;
      /* from Caprice Reloaded r623 */
      if ( (CRTC->registers[4] == 0) || CRTC->flag_invsync )
      {
        CRTC->next_addr = CRTC->requested_addr; // load with screen start address
        video_match_line_count(NativeCPC);
      }
    }
    break;

    case 13: // R13 = Start Address (L)
    {
      CRTC->registers[13] = val;
      CRTC->requested_addr = CRTC->registers[13] + (CRTC->registers[12] << 8);
	    // Needed for lightpen and magnum light phaser
      CRTC->start_addr = CRTC->requested_addr;
      /* from Caprice Reloaded r623 */
      if ( (CRTC->registers[4] == 0) || CRTC->flag_invsync )
      {
        CRTC->next_addr = CRTC->requested_addr; // load with screen start address
        video_match_line_count(NativeCPC);
      }
    }
    break;

    case 14: // R14 = Cursor (H)
    {
      CRTC->registers[14] = val & 0x3f;
      CRTC->cursor_addr = (CRTC->registers[14] << 8) | CRTC->registers[15];
    }
    break;

    case 15: // R15 = Cursor (L)
    {
      CRTC->registers[15] = val;
      CRTC->cursor_addr = (CRTC->registers[14] << 8) | CRTC->registers[15];
    }
    break;

    // R0  = Horizontal Total
    // R2  = Horizontal Sync Position
    default:
    {
      CRTC->registers[Index] = val;
    }
  } /* switch */
}

static tULong CRTC1_get_status(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  CRTC1_get_status
 *
 ***********************************************************************/
{
tULong ret_val = 0;

  ret_val |= NativeCPC->CRTC->lpen_strobe ? 0x40 : 0x00;

  ret_val |= NativeCPC->CRTC->flag_startvta ? 0x20 : 0x00;
  ret_val |= NativeCPC->CRTC->flag_invta ?    0x20 : 0x00;
  ret_val |= NativeCPC->CRTC->flag_invsync ?  0x20 : 0x00;

  return ret_val;
}


static tVoid CRTC1_video_engine(tNativeCPC* NativeCPC, tULong repeat_count)
/***********************************************************************
 *
 *  CRTC1_video_engine
 *
 ***********************************************************************/
{
tCRTC* CRTC = NativeCPC->CRTC;
tVDU* VDU = NativeCPC->VDU;

  while (repeat_count--)
  {
    // are we within the rendering area?
    if (VDU->flag_drawing)
    {
      // below horizontal cut-off?
      if (CRTC->HorzChar < CRTC->HorzMax)
      {
        if (CRTC->flags1.combined != CRTC->LastPreRend)
        {
          video_set_prerender(CRTC); // change pre-renderer if necessary
        }
        
        CRTC->PreRender(NativeCPC); // translate CPC video memory bytes to entries referencing the palette
        CRTC->Render(NativeCPC); // render to the video surface at the current bit depth
      }
    }

#ifdef PALETTE_PATCH_1
    if (NativeCPC->GateArray->newcolorcount)
    {
      NativeCPC->GateArray->newcolorcount = 0;
      NativeCPC->GateArray->ink_values[NativeCPC->GateArray->pen] = NativeCPC->GateArray->newcolor;
      NativeCPC->GateArray->palette[NativeCPC->GateArray->pen] = NativeCPC->colours[NativeCPC->GateArray->newcolor]; // isolate colour value
    }
#endif /* PALETTE_PATCH_1 */

#ifdef ENABLE_PLAYCITY
    if (NativeCPC->PlayCity->enabled)
    {
      if ( (CRTC->raster_count >= CRTC->cursor_start_ras)
           && (CRTC->raster_count <= CRTC->cursor_end_ras) )
      {
        if ((CRTC->addr + CRTC->char_count) == (CRTC->cursor_addr + CRTC->start_addr))
        {
          PlayCity_trigger_channel_1(NativeCPC);
        }
      }
    }
#endif /* ENABLE_PLAYCITY */

    // next address for PreRender    
    CRTC->next_address = VDU->MAXlate[(CRTC->addr + CRTC->char_count) & 0x73ff] | CRTC->scr_base;
    CRTC->flags1.dt.combined = CRTC->new_dt.combined; // update the DISPTMG flags

    CRTC->iMonHSStartPos += 0x100;
    CRTC->iMonHSEndPos += 0x100;
    CRTC->iMonHSPeakPos += 0x100;
    CRTC->HorzPos += 0x100;
    CRTC->HorzChar++;

    if (CRTC->HorzPos >= CRTC->MonHSYNC)
    {
      if (VDU->flag_drawing)
      {
        VDU->scr_base += VDU->scr_line_offs; // advance surface pointer to next row
      }
      CRTC->HadP = 1;
      CRTC->iMonHSPeakPos = CRTC->HorzPos - CRTC->MonHSYNC;
      CRTC->iMonHSStartToPeak = CRTC->iMonHSStartPos - CRTC->iMonHSPeakPos;
      CRTC->iMonHSEndToPeak = CRTC->iMonHSEndPos - CRTC->iMonHSPeakPos;
      CRTC->HorzPos = CRTC->iMonHSPeakPos - CRTC->HSyncDuration;

      // CAUTION : CRTC->HorzPos can be negative and CRTC->HorzChar is unsigned !!
      CRTC->HorzChar = CRTC->HorzPos >> 8;
	  
      {
        tULong val = (CRTC->HorzPos & 0xf0) >> 4; // PosShift
        if (!val)
        {
          CRTC->HorzMax = 48;
          CRTC->HorzPix[0] = CRTC->HorzPix[1];
          CRTC->RendPos = CRTC->RendStart;
          CRTC->HorzChar--;
        }
        else
        {
          tULong tmp;
          CRTC->RendPos = (tULong*)&CRTC->RendBuff[val];
          tmp = (tULong)CRTC->RendStart - (tULong)CRTC->RendPos;
          CRTC->HorzPix[48] = (tUChar)tmp;
          CRTC->HorzPix[0] = CRTC->HorzPix[1] - (tUChar)tmp;
          CRTC->HorzMax = 49;
        }
      }
	  
      CRTC->RendOut = (tUChar*)CRTC->RendStart;
      CRTC->RendWid = &CRTC->HorzPix[0];
      VDU->scr_pos = VDU->scr_base;
      VDU->scrln++;
      VDU->scanline++;
      if ((tULong)VDU->scrln >= MAX_DRAWN)
      {
        VDU->flag_drawing = 0;
      }
      else
      {
        VDU->flag_drawing = 1;
      }
    }

    // ----------------------------------------------------------------------------

    // lightgun beam detection should be performed once screen written.
    if (NativeCPC->lightgun_beam_detect)
    {
      if (NativeCPC->timer_from_reset > NativeCPC->lightgun_trigger_delay)
      {
        if (!NativeCPC->lightgun_counter)
        {
          // Get Y difference
          tULong YDiff;
          if (NativeCPC->lightgun_y_pos > (tULong)VDU->scrln)
          {
            YDiff = NativeCPC->lightgun_y_pos - (tULong)VDU->scrln;
          }
          else
          {
            YDiff = (tULong)VDU->scrln - NativeCPC->lightgun_y_pos;
          }

          if (YDiff <= NativeCPC->lightgun_y_radius)
          {
            if (NativeCPC->lightgun_x_pos == CRTC->char_count)
            {
              // Check if last drawn pixel is bright enough.
              tULong PixelIntensity;
              // Use of color palette ?
              if (NativeCPC->BitsPerPixel == 8)
              {
                tUChar PixelColor = *(VDU->scr_pos-1);
                PixelIntensity = NativeCPC->WinPalettePtr[PixelColor].intensity;
              }
              // monochrome display
              else if (NativeCPC->active_colours == NativeCPC->colours_green)
              {
                tULong PixelColor = *(VDU->scr_pos-1);
                PixelIntensity = PixelColor & 0xff;
                PixelIntensity += (PixelColor >> 8) & 0xff;
                PixelIntensity += (PixelColor >> 16) & 0xff;
              }
              // 24bits and 32bits display
              else
              {
                tULong PixelColor = *(VDU->scr_pos-1);
                PixelIntensity = PixelColor & 0xff;
                PixelIntensity += (PixelColor >> 8) & 0xff;
                PixelIntensity += (PixelColor >> 16) & 0xff;
                PixelIntensity /= 3;
              }

              if (PixelIntensity >= NativeCPC->lightgun_sensitivity)
              {
                NativeCPC->lightgun_counter = NativeCPC->timer_from_reset + NativeCPC->lightgun_beam_duration;
#ifdef ENABLE_METROLOGY
                NativeCPC->signal_lightgun_beam_detected++;
#endif /* ENABLE_METROLOGY */
              }
            }
          }
        }
        else if (NativeCPC->timer_from_reset > NativeCPC->lightgun_counter)
        {
          // Stop lightgun beam key pressed generation
          NativeCPC->lightgun_counter=0;
        }
      }
    }

    // ----------------------------------------------------------------------------

    // matches horizontal total?
    if (CRTC->char_count == CRTC->registers[0])
    {
      CRTC->last_hend = CRTC->char_count; // preserve current line length in chars
      CRTC->flag_newscan = 1; // request starting a new scan line
      CRTC->char_count = 0; // reset the horizontal character count

      /* from Caprice Reloaded r623 */
      // ready to start vertical total adjust?
      if (CRTC->flag_startvta)
      {
        CRTC->flag_startvta = 0;
        CRTC->flag_invta = 1; // entering vertical total adjust
      }

#ifdef CRTC_PATCH_1
      // in vertical total adjust?
      if (CRTC->flag_invta)
      {
        if ( ( (CRTC->raster_count == CRTC->registers[9]) && (CRTC->line_count == CRTC->registers[4]) )
             || (CRTC->registers[4] == 0) )
        {
          CRTC->flag_resscan = 1; // raster counter only resets once at start of vta
        }
        else
        {
          CRTC->flag_resscan = 0; // raster counter keeps increasing while in vta
        }
      }
#endif /* CRTC_PATCH_1 */
    }
    else
    {
      CRTC->char_count++; // update counter
      CRTC->char_count &= 255; // limit to 8 bits
    }

    // matches horizontal total?
    if (CRTC->char_count == CRTC->registers[0])
    {
      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->flag_reschar = 1; // request a line count update
      }
      else
      {
        CRTC->flag_reschar = 0; // still within the current character line
      }

      // ready to restart frame?
      if (CRTC->flag_resnext)
      {
        CRTC->flag_resnext = 0;
        CRTC->flag_resframe = 1; // request a frame restart
      }

#ifdef CRTC_PATCH_1
      // ready to start vertical total adjust?
      if (CRTC->flag_startvta)
      {
        CRTC->flag_startvta = 0;
        CRTC->flag_invta = 1; // entering vertical total adjust
      }
#endif /* CRTC_PATCH_1 */

      // in vertical total adjust?
      if (CRTC->flag_invta)
      {
        if ( (CRTC->raster_count == CRTC->registers[9])
             && (CRTC->line_count == CRTC->registers[4]) )
        {
          CRTC->flag_resscan = 1; // raster counter only resets once at start of vta
        }
        else
        {
          CRTC->flag_resscan = 0; // raster counter keeps increasing while in vta
        }
      }
    }

    // matches horizontal displayed?
    if (CRTC->char_count == CRTC->registers[1])
    {
      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->next_addr = CRTC->addr + CRTC->char_count;
      }
    }

    // not in HSYNC?
    if (!CRTC->flags1.inHSYNC)
    {
      // matches horizontal sync position?
      if (CRTC->char_count == CRTC->registers[2])
      {
        CRTC->flags1.inHSYNC = 0xff; // turn HSYNC on
        CRTC->flag_hadhsync = 1; // prevent GA from processing more than one HSYNC per scan line
        CRTC->hsw_count = 0; // initialize horizontal sync width counter
#ifndef CRTC_PATCH_2
        video_match_hsw(NativeCPC);
#endif /* CRTC_PATCH_2 */
      }
    }
    else
    {
      video_match_hsw(NativeCPC);
    }

    CRTC->CharInstSL(CRTC); // if necessary, process vertical total delay
    CRTC->CharInstMR(CRTC); // if necessary, process maximum raster count delay

    // scanline change requested?
    if (CRTC->flag_newscan)
    {
      CRTC->flag_newscan = 0;
      CRTC->addr = CRTC->next_addr;

      // VSYNC active?
      if (CRTC->flag_invsync)
      {
        CRTC->vsw_count++; // update counter
        /* from Caprice Reloaded r623 */
        CRTC->vsw_count &= 31; // limit to 5 bits (to handle "double vbl" case)

        // matches vertical sync width?
        if (CRTC->vsw_count == CRTC->vsw)
        {
          CRTC->vsw_count = 0; // reset counter
          CRTC->flag_resvsync = 1; // request VSYNC reset
        }
      }

      // frame restart requested?
      if (CRTC->flag_resframe)
      {
        video_restart_frame(NativeCPC);
      }
      else
      {
        // raster counter reset requested?
        if (CRTC->flag_resscan)
        {
          CRTC->flag_resscan = 0;
          CRTC->raster_count = 0; // reset counter
          CRTC->scr_base = 0;
        }
        else
        {
          CRTC->raster_count++; // update counter
          CRTC->raster_count &= 31; // limit to 5 bits
          // did the counter wrap around?
          if (!CRTC->raster_count)
          {
            video_match_line_count(NativeCPC);
          }
          CRTC->scr_base = (CRTC->scr_base + 0x0800) & 0x3800;
        }
      }

      CRTC->CharInstSL = video_CharSL1;

      // matches maximum raster address?
      if (CRTC->raster_count == CRTC->registers[9])
      {
        CRTC->flag_resscan = 1; // request a raster counter reset
        CRTC->r9match = 1;
        CRTC->CharInstMR = CRTC1_CharMR1;
      }
      else
      {
        CRTC->r9match = 0;
      }

      // in vertical total adjust?
      if (CRTC->flag_invta)
      {
        // matches vertical total adjust?
        if (CRTC->raster_count == CRTC->reg5)
        {
          video_restart_frame(NativeCPC);
          // maximum raster address is zero?
          if (CRTC->registers[9] == 0)
          {
            CRTC->flag_resscan = 1; // request a raster counter reset
          }
        }
      }

      // line count update requested?
      if (CRTC->flag_reschar)
      {
        CRTC->line_count++; // update counter
        CRTC->line_count &= 127; // limit to 7 bits
        video_reload_addr(NativeCPC);
      }

      // in VSYNC?
      if (CRTC->flag_invsync)
      {
        // end of VSYNC?
        if (CRTC->flag_resvsync)
        {
          CRTC->flag_invsync = 0; // turn VSYNC off
          CRTC->flag_resvsync = 0;

          // above maximum scanline count?
          if (VDU->scanline >= CRTC->MaxVSync)
          {
            video_frame_finished(VDU);
          }
        }
        else
        {
          // above minimum scanline count?
          if (VDU->scanline > CRTC->MinVSync)
          {
            video_frame_finished(VDU);
          }
        }
      }
      // above maximum scanline count?
      else if (VDU->scanline >= CRTC->MaxVSync)
      {
        video_frame_finished(VDU);
      }
    }

    // leaving border area?
    if (CRTC->char_count == CRTC->hstart)
    {
      CRTC->new_dt.NewHDSPTIMG |= 0x01;
    }
    // entering border area?
    if (CRTC->char_count == CRTC->hend)
    {
      CRTC->new_dt.NewHDSPTIMG &= 0xfe;
    }
  }
}
/*---------------------------------------------------------------------*/

static tVoid CRTC1_CharMR1(tCRTC* CRTC)
/***********************************************************************
 *
 *  CRTC1_CharMR1
 *
 ***********************************************************************/
{
  if ( ( (CRTC->raster_count == CRTC->registers[9]) && (CRTC->line_count == CRTC->registers[4]) )
       || (CRTC->registers[4] == 0) )
  {
    CRTC->flag_invta = 0;
    CRTC->flag_startvta = 1; // request start of vertical total adjust
  }
  else
  {
    CRTC->flag_startvta = 0; // not yet at end of frame
  }

  CRTC->CharInstMR = CRTC1_CharMR2;
}
/*---------------------------------------------------------------------*/

static tVoid CRTC1_CharMR2(tCRTC* CRTC)
/***********************************************************************
 *
 *  CRTC1_CharMR2
 *
 ***********************************************************************/
{
  // starting vertical total adjust?
  if (CRTC->flag_startvta)
  {
    // matches vertical total?
    if ( (CRTC->line_count == CRTC->registers[4])
         || (CRTC->registers[4] == 0) )
    {
      // no vertical total adjust?
      if (CRTC->registers[5] == 0)
      {
        CRTC->flag_resnext = 1; // request a frame restart
      }
    }
  }

  CRTC->CharInstMR = video_NoChar;
}
/*---------------------------------------------------------------------*/

#endif /* !CRTC_ENGINE_V2 */


//
// AUDIO
//

static tVoid audio_init(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  audio_init
 *
 ***********************************************************************/
{
tPSG* PSG = NativeCPC->PSG;
tULong n;

  // init sound emulation with valid values
  for (n = 0; n < 16; n++)
  {
    // PSG
    audio_set_AY_Register(PSG,
                          n,
                          PSG->RegisterAY.Index[n]);
  }

#ifdef ENABLE_PLAYCITY
  // PLAYCITY
  PlayCity_audio_init(NativeCPC->PlayCity);
#endif /* ENABLE_PLAYCITY */

  //
  // Init synthetizer
  //
  NativeCPC->SpeakerSynthetizerStereo16Ptr = audio_Synthesizer_Stereo16;
  NativeCPC->SpeakerSynthetizerMono16Ptr = audio_Synthesizer_Mono16;
#ifdef ENABLE_PLAYCITY
  NativeCPC->PlayCitySynthetizerStereo16Ptr = PlayCity_audio_Stereo16;
  NativeCPC->PlayCitySynthetizerStereo16InvertPtr = PlayCity_audio_Stereo16_Invert;
  NativeCPC->PlayCitySynthetizerMono16Ptr = PlayCity_audio_Mono16;
#endif /* ENABLE_PLAYCITY */

  NativeCPC->SpeakerSynthetizerPtr = (NativeCPC->prefP->SpeakerStereo) ?
        audio_Synthesizer_Stereo16 : audio_Synthesizer_Mono16;

#ifdef ENABLE_PLAYCITY
  if (NativeCPC->prefP->PlayCityStereo)
  {
    if (NativeCPC->prefP->PlayCityInvert)
    {
      NativeCPC->PlayCitySynthetizerPtr = PlayCity_audio_Stereo16_Invert;
    }
    else
    {
      NativeCPC->PlayCitySynthetizerPtr = PlayCity_audio_Stereo16;
    }
  }
  else
  {
    NativeCPC->PlayCitySynthetizerPtr = PlayCity_audio_Mono16;
  }
#endif /* ENABLE_PLAYCITY */
}
/*----------------------------------------------------------------------------*/


static INLINE tVoid audio_set_AY_Register(tPSG* PSG,
                                          tULong Num,
                                          tULong Value)
/***********************************************************************
 *
 *  audio_set_AY_Register
 *
 ***********************************************************************/
{
  switch (Num)
  {
    case 0:
    case 2:
    case 4:
    case 11:
    case 12:
      PSG->RegisterAY.Index[Num] = Value;
      break;

    case 1:
    case 3:
    case 5:
      PSG->RegisterAY.Index[Num] = Value & 15;
      break;

    case 6:
      PSG->RegisterAY.UChar.Noise = Value & 31;
      break;

    case 7:
      PSG->RegisterAY.UChar.Mixer = Value & 63;
      PSG->Ton_EnA = Value & 1 ? cFalse : cTrue;
      PSG->Noise_EnA = Value & 8 ? cFalse : cTrue;
      PSG->Ton_EnB = Value & 2 ? cFalse : cTrue;
      PSG->Noise_EnB = Value & 16 ? cFalse : cTrue;
      PSG->Ton_EnC = Value & 4 ? cFalse : cTrue;
      PSG->Noise_EnC = Value & 32 ? cFalse : cTrue;
      break;

    case 8:
      PSG->RegisterAY.UChar.AmplitudeA = Value & 15;
      PSG->Envelope_EnA = Value & 16 ? cFalse : cTrue;
      break;

    case 9:
      PSG->RegisterAY.UChar.AmplitudeB = Value & 15;
      PSG->Envelope_EnB = Value & 16 ? cFalse : cTrue;
      break;

    case 10:
      PSG->RegisterAY.UChar.AmplitudeC = Value & 15;
      PSG->Envelope_EnC = Value & 16 ? cFalse : cTrue;
      break;

    case 13:
      PSG->Envelope_Counter = 0;
      PSG->FirstPeriod = cTrue;
      if (!(Value & 4))
      {
        PSG->AmplitudeEnv = 32;
      }
      else
      {
        PSG->AmplitudeEnv = -1;
      }
      PSG->RegisterAY.UChar.EnvType = Value & 15;
      audio_set_case_env(PSG);
      break;
  }
}
/*----------------------------------------------------------------------------*/


static INLINE tVoid audio_set_case_env(tPSG* PSG)
{
  switch (PSG->RegisterAY.UChar.EnvType)
  {
    case 0:
    case 1:
    case 2:
    case 3:
    case 9:
      PSG->Case_Env = audio_Case_EnvType_0_3__9;
      break;
    case 4:
    case 5:
    case 6:
    case 7:
    case 15:
      PSG->Case_Env = audio_Case_EnvType_4_7__15;
      break;
    case 8:
      PSG->Case_Env = audio_Case_EnvType_8;
      break;
    case 10:
      PSG->Case_Env = audio_Case_EnvType_10;
      break;
    case 11:
      PSG->Case_Env = audio_Case_EnvType_11;
      break;
    case 12:
      PSG->Case_Env = audio_Case_EnvType_12;
      break;
    case 13:
      PSG->Case_Env = audio_Case_EnvType_13;
      break;
    case 14:
      PSG->Case_Env = audio_Case_EnvType_14;
      break;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Case_EnvType_0_3__9(tPSG* PSG)
/***********************************************************************
 *
 *  audio_Case_EnvType_0_3__9
 *
 ***********************************************************************/
{
  if (PSG->FirstPeriod)
  {
    PSG->AmplitudeEnv--;
    if (!PSG->AmplitudeEnv)
    {
      PSG->FirstPeriod = cFalse;
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Case_EnvType_4_7__15(tPSG* PSG)
/***********************************************************************
 *
 *  audio_Case_EnvType_4_7__15
 *
 ***********************************************************************/
{
  if (PSG->FirstPeriod)
  {
    PSG->AmplitudeEnv++;
    if (PSG->AmplitudeEnv == 32)
    {
      PSG->FirstPeriod = cFalse;
      PSG->AmplitudeEnv = 0;
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Case_EnvType_8(tPSG* PSG)
/***********************************************************************
 *
 *  audio_Case_EnvType_8
 *
 ***********************************************************************/
{
  PSG->AmplitudeEnv = (PSG->AmplitudeEnv - 1) & 31;
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Case_EnvType_10(tPSG* PSG)
/***********************************************************************
 *
 *  audio_Case_EnvType_10
 *
 ***********************************************************************/
{
  if (PSG->FirstPeriod)
  {
    PSG->AmplitudeEnv--;
    if (PSG->AmplitudeEnv == -1)
    {
      PSG->FirstPeriod = cFalse;
      PSG->AmplitudeEnv = 0;
    }
  }
  else
  {
    PSG->AmplitudeEnv++;
    if (PSG->AmplitudeEnv == 32)
    {
      PSG->FirstPeriod = cTrue;
      PSG->AmplitudeEnv = 31;
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Case_EnvType_11(tPSG* PSG)
/***********************************************************************
 *
 *  audio_Case_EnvType_11
 *
 ***********************************************************************/
{
  if (PSG->FirstPeriod)
  {
    PSG->AmplitudeEnv--;
    if (PSG->AmplitudeEnv == -1)
    {
      PSG->FirstPeriod = cFalse;
      PSG->AmplitudeEnv = 31;
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Case_EnvType_12(tPSG* PSG)
/***********************************************************************
 *
 *  audio_Case_EnvType_12
 *
 ***********************************************************************/
{
  PSG->AmplitudeEnv = (PSG->AmplitudeEnv + 1) & 31;
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Case_EnvType_13(tPSG* PSG)
/***********************************************************************
 *
 *  audio_Case_EnvType_13
 *
 ***********************************************************************/
{
  if (PSG->FirstPeriod)
  {
    PSG->AmplitudeEnv++;
    if (PSG->AmplitudeEnv == 32)
    {
      PSG->FirstPeriod = cFalse;
      PSG->AmplitudeEnv = 31;
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Case_EnvType_14(tPSG* PSG)
/***********************************************************************
 *
 *  audio_Case_EnvType_14
 *
 ***********************************************************************/
{
  if (!PSG->FirstPeriod)
  {
    PSG->AmplitudeEnv--;
    if (PSG->AmplitudeEnv == -1)
    {
      PSG->FirstPeriod = cTrue;
      PSG->AmplitudeEnv = 0;
    }
  }
  else
  {
    PSG->AmplitudeEnv++;
    if (PSG->AmplitudeEnv == 32)
    {
      PSG->FirstPeriod = cFalse;
      PSG->AmplitudeEnv = 31;
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Synthesizer_Logic_Q(tPSG* PSG)
/***********************************************************************
 *
 *  audio_Synthesizer_Logic_Q
 *
 ***********************************************************************/
{
  PSG->Ton_Counter_A++;
#if ENGINE_ENDIANNESS == LITTLE_ENDIAN
  if (PSG->Ton_Counter_A >= (tULong)(*(tUShort*)&PSG->RegisterAY.UChar.TonALo))
#else /* ENGINE_ENDIANNESS */
  if ( PSG->Ton_Counter_A >=
       (tULong)(((tULong)(PSG->RegisterAY.UChar.TonAHi) << 8) + (tULong)(PSG->RegisterAY.UChar.TonALo)) )
#endif /* ENGINE_ENDIANNESS */
  {
    PSG->Ton_Counter_A = 0;
    PSG->Ton_A ^= 1;
  }

  PSG->Ton_Counter_B++;
#if ENGINE_ENDIANNESS == LITTLE_ENDIAN
  if (PSG->Ton_Counter_B >= (tULong)(*(tUShort*)&PSG->RegisterAY.UChar.TonBLo))
#else /* ENGINE_ENDIANNESS */
  #error To do !!
  if ( PSG->Ton_Counter_B >=
       (tULong)(((tULong)(PSG->RegisterAY.UChar.TonBHi) << 8) + (tULong)(PSG->RegisterAY.UChar.TonBLo)) )
#endif /* ENGINE_ENDIANNESS */
  {
    PSG->Ton_Counter_B = 0;
    PSG->Ton_B ^= 1;
  }

  PSG->Ton_Counter_C++;
#if ENGINE_ENDIANNESS == LITTLE_ENDIAN
  if (PSG->Ton_Counter_C >= (tULong)(*(tUShort*)&PSG->RegisterAY.UChar.TonCLo))
#else /* ENGINE_ENDIANNESS */
  if ( PSG->Ton_Counter_C >=
       (tULong)(((tULong)(PSG->RegisterAY.UChar.TonCHi) << 8) + (tULong)(PSG->RegisterAY.UChar.TonCLo)) )
#endif /* ENGINE_ENDIANNESS */
  {
    PSG->Ton_Counter_C = 0;
    PSG->Ton_C ^= 1;
  }

  PSG->Noise_Counter++;
  if ( (!(PSG->Noise_Counter & 1)) && (PSG->Noise_Counter >= (tULong)(PSG->RegisterAY.UChar.Noise << 1)) )
  {
    PSG->Noise_Counter = 0;
    PSG->Noise.Seed = (((((PSG->Noise.Seed >> 13) ^ (PSG->Noise.Seed >> 16)) & 1) ^ 1) | PSG->Noise.Seed << 1) & 0x1ffff;
  }

  if (!PSG->Envelope_Counter)
  {
    PSG->Case_Env(PSG);
  }

  PSG->Envelope_Counter++;

#if ENGINE_ENDIANNESS == LITTLE_ENDIAN
  if ( PSG->Envelope_Counter >= (tULong)(*(tUShort*)&PSG->RegisterAY.UChar.EnvelopeLo))
#else /* ENGINE_ENDIANNESS */
  if ( PSG->Envelope_Counter >=
       (tULong)(((tULong)(PSG->RegisterAY.UChar.EnvelopeHi) << 8) + (tULong)(PSG->RegisterAY.UChar.EnvelopeLo)) )
#endif /* ENGINE_ENDIANNESS */
  {
    PSG->Envelope_Counter = 0;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Synthesizer_Mixer_Q(tPSG* PSG, tLong StartLevel)
/***********************************************************************
 *
 *  audio_Synthesizer_Mixer_Q
 *
 ***********************************************************************/
{
tLong LevL;
tLong LevR;
tLong k;

  LevL = StartLevel;
  LevR = LevL;

  //
  // Channel A
  //
  if (PSG->Channel_EnA)
  {
    if (PSG->Ton_EnA)
    {
      if ( (!PSG->Envelope_EnA) ||
           (*(tUShort*)&PSG->RegisterAY.UChar.TonALo > 4)) // Little Endian ONLY !!
      {
        k = PSG->Ton_A;
      }
      else
      {
        k = 1;
      }
    }
    else
    {
      k = 1;
    }

    if (PSG->Noise_EnA)
    {
      k &= PSG->Noise.s.Val;
    }

    if (k)
    {
      if (PSG->Envelope_EnA)
      {
        LevL += PSG->Level_AL[PSG->RegisterAY.UChar.AmplitudeA * 2 + 1];
        LevR += PSG->Level_AR[PSG->RegisterAY.UChar.AmplitudeA * 2 + 1];
      }
      else
      {
        LevL += PSG->Level_AL[PSG->AmplitudeEnv];
        LevR += PSG->Level_AR[PSG->AmplitudeEnv];
      }
    }
  }

  //
  // Channel B
  //
  if (PSG->Channel_EnB)
  {
    if (PSG->Ton_EnB)
    {
      if ( (!PSG->Envelope_EnB) ||
           (*(tUShort*)&PSG->RegisterAY.UChar.TonBLo > 4)) // Little Endian ONLY !!
      {
        k = PSG->Ton_B;
      }
      else
      {
        k = 1;
      }
    }
    else
    {
      k = 1;
    }

    if (PSG->Noise_EnB)
    {
      k &= PSG->Noise.s.Val;
    }
    if (k)
    {
      if (PSG->Envelope_EnB)
      {
        LevL += PSG->Level_BL[PSG->RegisterAY.UChar.AmplitudeB * 2 + 1];
        LevR += PSG->Level_BR[PSG->RegisterAY.UChar.AmplitudeB * 2 + 1];
      }
      else
      {
        LevL += PSG->Level_BL[PSG->AmplitudeEnv];
        LevR += PSG->Level_BR[PSG->AmplitudeEnv];
      }
    }
  }

  //
  // Channel C
  //
  if (PSG->Channel_EnC)
  {
    if (PSG->Ton_EnC)
    {
      if ( (!PSG->Envelope_EnC) ||
           (*(tUShort*)&PSG->RegisterAY.UChar.TonCLo > 4)) // Little Endian ONLY !!
      {
        k = PSG->Ton_C;
      }
      else
      {
        k = 1;
      }
    }
    else
    {
      k = 1;
    }

    if (PSG->Noise_EnC)
    {
      k &= PSG->Noise.s.Val;
    }
    if (k)
    {
      if (PSG->Envelope_EnC)
      {
        LevL += PSG->Level_CL[PSG->RegisterAY.UChar.AmplitudeC * 2 + 1];
        LevR += PSG->Level_CR[PSG->RegisterAY.UChar.AmplitudeC * 2 + 1];
      }
      else
      {
        LevL += PSG->Level_CL[PSG->AmplitudeEnv];
        LevR += PSG->Level_CR[PSG->AmplitudeEnv];
      }
    }
  }

  PSG->Left_Chan += LevL;
  PSG->Right_Chan += LevR;
}
/*----------------------------------------------------------------------------*/


#undef AUDIO_DISABLE_LEFT
//#define AUDIO_DISABLE_LEFT
#undef AUDIO_DISABLE_RIGHT
//#define AUDIO_DISABLE_RIGHT

static tVoid audio_Synthesizer_Stereo16(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  audio_Synthesizer_Stereo16
 *
 ***********************************************************************/
{
tPSG* PSG = NativeCPC->PSG;
tULong Tick_Counter = PSG->LoopCount >> SND_LOOP_COUNT_SHIFT;
tNativeULongField SampleValue;
tLong StartLevel;

#ifdef ENABLE_TAPE
  StartLevel = (NativeCPC->Tape->tape_motor && NativeCPC->Tape->bTapeLevel) ? PSG->LevelTape : 0; // start with the tape signal
#else /* ENABLE_TAPE */
  StartLevel = 0;
#endif /* ENABLE_TAPE */
#ifdef ENABLE_PRINTER
  if (PSG->snd_pp_device)
  {
    StartLevel += PSG->Level_PP[NativeCPC->printer_port];
  }
#endif /* ENABLE_PRINTER */

  while (PSG->LoopCount >= SND_LOOP_COUNT_INCREMENT)
  {
    PSG->LoopCount -= SND_LOOP_COUNT_INCREMENT;

    audio_Synthesizer_Logic_Q(PSG);
    audio_Synthesizer_Mixer_Q(PSG, StartLevel);
  }

  PSG->LoopCount += SND_LOOP_COUNT_INIT;

#ifndef AUDIO_DISABLE_LEFT
  SampleValue.UShort.usL = SND_SAMPLE_OFFSET_16BIT + PSG->Left_Chan / Tick_Counter;
#else /* !AUDIO_DISABLE_LEFT */
  SampleValue.UShort.usL = 0;
#endif /* !AUDIO_DISABLE_LEFT */

#ifndef AUDIO_DISABLE_RIGHT
  SampleValue.UShort.usH = SND_SAMPLE_OFFSET_16BIT + PSG->Right_Chan / Tick_Counter;
#else /* !AUDIO_DISABLE_RIGHT */
  SampleValue.UShort.usH = 0;
#endif /* !AUDIO_DISABLE_RIGHT */

  if (PSG->FilledBufferSize < PSG->SndBufferSize)
  {
    *(tULong*)PSG->snd_bufferptr = SampleValue.ULong; // write to mixing buffer
    PSG->snd_bufferptr += 4;
    PSG->FilledBufferSize += 4;
  }

#ifdef ENABLE_METROLOGY
  PSG->signal_Left_Chan_Ready++;
  PSG->signal_Right_Chan_Ready++;
  PSG->Left_Chan_Sample = SampleValue.UShort.usL;
  PSG->Right_Chan_Sample = SampleValue.UShort.usH;
#endif /* ENABLE_METROLOGY */

  PSG->Left_Chan = 0;
  PSG->Right_Chan = 0;

  if (PSG->snd_bufferptr >= PSG->pbSndBufferEnd)
  {
    PSG->snd_bufferptr = PSG->pbSndBuffer;
    PSG->buffer_full = 1;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Synthesizer_Stereo8(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  audio_Synthesizer_Stereo8
 *
 ***********************************************************************/
{
tPSG* PSG = NativeCPC->PSG;
tULong Tick_Counter = PSG->LoopCount >> SND_LOOP_COUNT_SHIFT;
tNativeUShortField SampleValue;
tLong StartLevel;

#ifdef ENABLE_TAPE
  StartLevel = (NativeCPC->Tape->tape_motor && NativeCPC->Tape->bTapeLevel) ? PSG->LevelTape : 0; // start with the tape signal
#else /* ENABLE_TAPE */
  StartLevel = 0;
#endif /* ENABLE_TAPE */
#ifdef ENABLE_PRINTER
  if (PSG->snd_pp_device)
  {
    StartLevel += PSG->Level_PP[NativeCPC->printer_port];
  }
#endif /* ENABLE_PRINTER */

  while (PSG->LoopCount >= SND_LOOP_COUNT_INCREMENT)
  {
    PSG->LoopCount -= SND_LOOP_COUNT_INCREMENT;

    audio_Synthesizer_Logic_Q(PSG);
    audio_Synthesizer_Mixer_Q(PSG, StartLevel);
  }

  PSG->LoopCount += SND_LOOP_COUNT_INIT;

#ifndef AUDIO_DISABLE_LEFT
  SampleValue.UChar.ucL = SND_SAMPLE_OFFSET_8BIT + PSG->Left_Chan / Tick_Counter;
#else /* !AUDIO_DISABLE_LEFT */
  SampleValue.UChar.ucL = 0;
#endif /* !AUDIO_DISABLE_LEFT */

#ifndef AUDIO_DISABLE_RIGHT
  SampleValue.UChar.ucH = SND_SAMPLE_OFFSET_8BIT + PSG->Right_Chan / Tick_Counter;
#else /* !AUDIO_DISABLE_RIGHT */
  SampleValue.UChar.ucH = 0;
#endif /* !AUDIO_DISABLE_RIGHT */

  if (PSG->FilledBufferSize < PSG->SndBufferSize)
  {
    *(tUShort*)PSG->snd_bufferptr = SampleValue.UShort; // write to mixing buffer
    PSG->snd_bufferptr += 2;
    PSG->FilledBufferSize += 2;
  }

#ifdef ENABLE_METROLOGY
  PSG->signal_Left_Chan_Ready++;
  PSG->signal_Right_Chan_Ready++;
  PSG->Left_Chan_Sample = SampleValue.UChar.ucL;
  PSG->Right_Chan_Sample = SampleValue.UChar.ucH;
#endif /* ENABLE_METROLOGY */
  
  PSG->Left_Chan = 0;
  PSG->Right_Chan = 0;

  if (PSG->snd_bufferptr >= PSG->pbSndBufferEnd)
  {
    PSG->snd_bufferptr = PSG->pbSndBuffer;
    PSG->buffer_full = 1;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid audio_Synthesizer_Mixer_Q_Mono(tPSG* PSG, tLong StartLevel)
/***********************************************************************
 *
 *  audio_Synthesizer_Mixer_Q_Mono
 *
 ***********************************************************************/
{
tLong Lev;
tLong k;

  Lev = StartLevel;

  //
  // Channel A
  //
  if (PSG->Channel_EnA)
  {
    if (PSG->Ton_EnA)
    {
      if ((!PSG->Envelope_EnA) || (*(tUShort*)&PSG->RegisterAY.UChar.TonALo > 4)) // Little Endian ONLY !!
      {
        k = PSG->Ton_A;
      }
      else
      {
        k = 1;
      }
    }
    else
    {
      k = 1;
    }

    if (PSG->Noise_EnA)
    {
      k &= PSG->Noise.s.Val;
    }

    if (k)
    {
      if (PSG->Envelope_EnA)
      {
        Lev += PSG->Level_AL[PSG->RegisterAY.UChar.AmplitudeA * 2 + 1];
      }
      else
      {
        Lev += PSG->Level_AL[PSG->AmplitudeEnv];
      }
    }
  }

  //
  // Channel B
  //
  if (PSG->Channel_EnB)
  {
    if (PSG->Ton_EnB)
    {
      if ((!PSG->Envelope_EnB) || (*(tUShort*)&PSG->RegisterAY.UChar.TonBLo > 4)) // Little Endian ONLY !!
      {
        k = PSG->Ton_B;
      }
      else
      {
        k = 1;
      }
    }
    else
    {
      k = 1;
    }

    if (PSG->Noise_EnB)
    {
      k &= PSG->Noise.s.Val;
    }
    if (k)
    {
      if (PSG->Envelope_EnB)
      {
        Lev += PSG->Level_BL[PSG->RegisterAY.UChar.AmplitudeB * 2 + 1];
      }
      else
      {
        Lev += PSG->Level_BL[PSG->AmplitudeEnv];
      }
    }
  }

  //
  // Channel C
  //
  if (PSG->Channel_EnC)
  {
    if (PSG->Ton_EnC)
    {
      if ((!PSG->Envelope_EnC) || (*(tUShort*)&PSG->RegisterAY.UChar.TonCLo > 4)) // Little Endian ONLY !!
      {
        k = PSG->Ton_C;
      }
      else
      {
        k = 1;
      }
    }
    else
    {
      k = 1;
    }

    if (PSG->Noise_EnC)
    {
      k &= PSG->Noise.s.Val;
    }
    if (k)
    {
      if (PSG->Envelope_EnC)
      {
        Lev += PSG->Level_CL[PSG->RegisterAY.UChar.AmplitudeC * 2 + 1];
      }
      else
      {
        Lev += PSG->Level_CL[PSG->AmplitudeEnv];
      }
    }
  }

  PSG->Left_Chan += Lev;
}
/*----------------------------------------------------------------------------*/

static tVoid audio_Synthesizer_Mono16(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  audio_Synthesizer_Mono16
 *
 ***********************************************************************/
{
tPSG* PSG = NativeCPC->PSG;
tULong Tick_Counter = PSG->LoopCount >> SND_LOOP_COUNT_SHIFT;
tUShort SampleValue;
tLong StartLevel;

#ifdef ENABLE_TAPE
  StartLevel = (NativeCPC->Tape->tape_motor && NativeCPC->Tape->bTapeLevel) ? PSG->LevelTape : 0; // start with the tape signal
#else /* ENABLE_TAPE */
  StartLevel = 0;
#endif /* ENABLE_TAPE */
#ifdef ENABLE_PRINTER
  if (PSG->snd_pp_device)
  {
    StartLevel += PSG->Level_PP[NativeCPC->printer_port];
  }
#endif /* ENABLE_PRINTER */

  while (PSG->LoopCount >= SND_LOOP_COUNT_INCREMENT)
  {
    PSG->LoopCount -= SND_LOOP_COUNT_INCREMENT;

    audio_Synthesizer_Logic_Q(PSG);
    audio_Synthesizer_Mixer_Q_Mono(PSG, StartLevel);
  }

  PSG->LoopCount += SND_LOOP_COUNT_INIT;

  SampleValue = SND_SAMPLE_OFFSET_16BIT + PSG->Left_Chan / Tick_Counter;

  if (PSG->FilledBufferSize < PSG->SndBufferSize)
  {
    *(tUShort*)PSG->snd_bufferptr = SampleValue; // write to mixing buffer
    PSG->snd_bufferptr+=2;
    PSG->FilledBufferSize+=2;
  }

#ifdef ENABLE_METROLOGY
  PSG->signal_Left_Chan_Ready++;
  PSG->Left_Chan_Sample = SampleValue;
#endif /* ENABLE_METROLOGY */

  PSG->Left_Chan = 0;

  if (PSG->snd_bufferptr >= PSG->pbSndBufferEnd)
  {
    PSG->snd_bufferptr = PSG->pbSndBuffer;
    PSG->buffer_full = 1;
  }
}
/*----------------------------------------------------------------------------*/

static tVoid audio_Synthesizer_Mono8(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  audio_Synthesizer_Mono8
 *
 ***********************************************************************/
{
tPSG* PSG = NativeCPC->PSG;
tULong Tick_Counter = PSG->LoopCount >> SND_LOOP_COUNT_SHIFT;
tUChar SampleValue;
tLong StartLevel;

#ifdef ENABLE_TAPE
  StartLevel = (NativeCPC->Tape->tape_motor && NativeCPC->Tape->bTapeLevel) ? PSG->LevelTape : 0; // start with the tape signal
#else /* ENABLE_TAPE */
  StartLevel = 0;
#endif /* ENABLE_TAPE */
#ifdef ENABLE_PRINTER
  if (PSG->snd_pp_device)
  {
    StartLevel += PSG->Level_PP[NativeCPC->printer_port];
  }
#endif /* ENABLE_PRINTER */

  while (PSG->LoopCount >= SND_LOOP_COUNT_INCREMENT)
  {
    PSG->LoopCount -= SND_LOOP_COUNT_INCREMENT;

    audio_Synthesizer_Logic_Q(PSG);
    audio_Synthesizer_Mixer_Q_Mono(PSG, StartLevel);
  }

  PSG->LoopCount += SND_LOOP_COUNT_INIT;

  SampleValue = SND_SAMPLE_OFFSET_8BIT + PSG->Left_Chan / Tick_Counter;

  if (PSG->FilledBufferSize < PSG->SndBufferSize)
  {
    *PSG->snd_bufferptr = SampleValue; // write to mixing buffer
    PSG->snd_bufferptr++;
    PSG->FilledBufferSize++;
  }

#ifdef ENABLE_METROLOGY
  PSG->signal_Left_Chan_Ready++;
  PSG->Left_Chan_Sample = SampleValue;
#endif /* ENABLE_METROLOGY */

  PSG->Left_Chan = 0;

  if (PSG->snd_bufferptr >= PSG->pbSndBufferEnd)
  {
    PSG->snd_bufferptr = PSG->pbSndBuffer;
    PSG->buffer_full = 1;
  }
}
/*----------------------------------------------------------------------------*/


//
// PLAYCITY
//
/*

  Z84C30 CTC Registers

  Channel Control Word (D0=1)
  ===========================
  D7: Interrupt: 0=Disable, 1=Enable
  D7: Mode: 0=Timer mode, 1=Counter mode
  D5: Prescaler: 0=16, 1=256
  D4: CLK/TRQ Edge selection: 0=Falling, 1=Rising
  D3: Timer trigger: 0=Automatic when time constant is loaded, 1=CLK/TRG pulse starts timer
  D2: Time Constant: 0=No time constant follows, 1=Time constant follows
  D1: Reset: 0=Continued operation, 1=Software reset
  D0: Control or Vector: 0=Vector, 1=Control word

  Time constant Word
  ==================
  00 is interpreted as 256

  Interrupt Vector Word (D0=0)
  ============================
  D7-3: Vector supplied by user
  D1-2: Channel identifier (same as CS0-CS1)
  D0: Control or Vector: 0=Vector, 1=Control word
  
*/

static tVoid PlayCity_audio_init(tPlayCity* PlayCity)
/***********************************************************************
 *
 *  PlayCity_audio_init
 *
 ***********************************************************************/
{
tULong n;

  // init sound emulation with valid values
  for (n = 0; n < 16; n++)
  {
    // PLAYCITY
    audio_set_AY_Register(PlayCity->PSG_Right,
                          n,
                          PlayCity->PSG_Right->RegisterAY.Index[n]);
    audio_set_AY_Register(PlayCity->PSG_Left,
                          n,
                          PlayCity->PSG_Left->RegisterAY.Index[n]);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid PlayCity_receive_channel_0(tPlayCity* PlayCity, tUChar Value)
{
  // Time constant ?
  if (PlayCity->CTC_control_word[CTC_CHANNEL0] & CTC_CONTROL_TIMECONST)
  {
    PlayCity->CTC_time_constant[CTC_CHANNEL0] = Value ? Value : 256;
    PlayCity->CTC_control_word[CTC_CHANNEL0] &= ~CTC_CONTROL_TIMECONST;

    // Set frequency
    // YMZ Clock is obtained by ORing CLK with ZC/TO0
    PlayCity->Freq = CLOCK_Z80 * ((Value*2)-1) / (Value*2);
    PlayCity->LoopCountInit = (((PlayCity->Freq / 2) / 8) << PLAYCITY_LOOP_COUNT_SHIFT) / SND_FREQ;
  }
  else
  {
    // Control Word ?
    if (Value & CTC_CONTROL_WORD)
    {
      PlayCity->CTC_control_word[CTC_CHANNEL0] = Value;
    }
  }
}
/*----------------------------------------------------------------------------*/
static tVoid PlayCity_receive_channel_1_2_3(tPlayCity* PlayCity, tUChar Channel, tUChar Value)
{
  // Time constant ?
  if (PlayCity->CTC_control_word[Channel] & CTC_CONTROL_TIMECONST)
  {
    PlayCity->CTC_time_constant[Channel] = Value ? Value : 256;
    PlayCity->CTC_control_word[Channel] &= ~CTC_CONTROL_TIMECONST;

    // Counter mode
    if (PlayCity->CTC_control_word[Channel] & CTC_CONTROL_MODE)
    {
      // Channel 2 has 4MHZ CLK as Trigger input
      if (Channel == CTC_CHANNEL2)
      {
        PlayCity->CTC_timer[Channel] = PlayCity->CTC_time_constant[Channel];
        PlayCity->CTC_timer_init[Channel] = PlayCity->CTC_time_constant[Channel];
      }
      else
      {
        PlayCity->CTC_counter[Channel] = PlayCity->CTC_time_constant[Channel];
      }
    }
    // Timer mode
    else
    {
      // Use prescaler 16 by default
      PlayCity->CTC_timer_init[Channel] = (tLong)(PlayCity->CTC_time_constant[Channel] << 4);
      // Prescaler = 256 ?
      if (PlayCity->CTC_control_word[Channel] & CTC_CONTROL_PRESCALER)
      {
        PlayCity->CTC_timer_init[Channel] <<= 4; // Multiply by 16
      }

      PlayCity->CTC_timer[Channel] = PlayCity->CTC_timer_init[Channel];
    }
  }
  else
  {
    // Control Word ?
    if (Value & CTC_CONTROL_WORD)
    {
      PlayCity->CTC_control_word[Channel] = Value;

      // Software reset
      if (Value & CTC_CONTROL_RESET)
      {
        PlayCity->CTC_counter[Channel] = 0;
        PlayCity->CTC_timer[Channel] = 0;
      }
    }
  }
}
/*----------------------------------------------------------------------------*/

static tVoid PlayCity_trigger_channel_1(tNativeCPC* NativeCPC)
{
tPlayCity* PlayCity = NativeCPC->PlayCity;

  if (PlayCity->CTC_counter[CTC_CHANNEL1])
  {
    PlayCity->CTC_counter[CTC_CHANNEL1]--;
    if (!PlayCity->CTC_counter[CTC_CHANNEL1])
    {
      PlayCity->CTC_counter[CTC_CHANNEL1] = PlayCity->CTC_time_constant[CTC_CHANNEL1];

      // Request Z80 NMI
      NativeCPC->Z80->Regs.nmi_pending = 1;

      // INT request ?
      if (PlayCity->CTC_control_word[CTC_CHANNEL1] & CTC_CONTROL_INT)
      {
        NativeCPC->Z80->Regs.int_pending = 1;
      }
    }
  }
}
/*----------------------------------------------------------------------------*/

static tVoid PlayCity_trigger_channel_3(tNativeCPC* NativeCPC)
{
tPlayCity* PlayCity = NativeCPC->PlayCity;

  if (PlayCity->CTC_counter[CTC_CHANNEL3])
  {
    PlayCity->CTC_counter[CTC_CHANNEL3]--;
    if (!PlayCity->CTC_counter[CTC_CHANNEL3])
    {
      PlayCity->CTC_counter[CTC_CHANNEL3] = PlayCity->CTC_time_constant[CTC_CHANNEL3];

      // INT request ?
      if (PlayCity->CTC_control_word[CTC_CHANNEL3] & CTC_CONTROL_INT)
      {
        NativeCPC->Z80->Regs.int_pending = 1;
      }
    }
  }
}
/*----------------------------------------------------------------------------*/


static tLong PlayCity_audio_Mixer_Q(tPSG* PSG)
/***********************************************************************
 *
 *  PlayCity_audio_Mixer_Q
 *
 ***********************************************************************/
{
tLong Level;
tLong k;

  Level = 0;

  //
  // Channel A
  //
  if (PSG->Channel_EnA)
  {
    if (PSG->Ton_EnA)
    {
      if ( (!PSG->Envelope_EnA) ||
           (*(tUShort*)&PSG->RegisterAY.UChar.TonALo > 4)) // Little Endian ONLY !!
      {
        k = PSG->Ton_A;
      }
      else
      {
        k = 1;
      }
    }
    else
    {
      k = 1;
    }

    if (PSG->Noise_EnA)
    {
      k &= PSG->Noise.s.Val;
    }

    if (k)
    {
      if (PSG->Envelope_EnA)
      {
        Level += PSG->Level_PLAYCITY[PSG->RegisterAY.UChar.AmplitudeA * 2 + 1];
      }
      else
      {
        Level += PSG->Level_PLAYCITY[PSG->AmplitudeEnv];
      }
    }
  }

  //
  // Channel B
  //
  if (PSG->Channel_EnB)
  {
    if (PSG->Ton_EnB)
    {
      if ( (!PSG->Envelope_EnB) ||
           (*(tUShort*)&PSG->RegisterAY.UChar.TonBLo > 4)) // Little Endian ONLY !!
      {
        k = PSG->Ton_B;
      }
      else
      {
        k = 1;
      }
    }
    else
    {
      k = 1;
    }

    if (PSG->Noise_EnB)
    {
      k &= PSG->Noise.s.Val;
    }
    if (k)
    {
      if (PSG->Envelope_EnB)
      {
        Level += PSG->Level_PLAYCITY[PSG->RegisterAY.UChar.AmplitudeB * 2 + 1];
      }
      else
      {
        Level += PSG->Level_PLAYCITY[PSG->AmplitudeEnv];
      }
    }
  }

  //
  // Channel C
  //
  if (PSG->Channel_EnC)
  {
    if (PSG->Ton_EnC)
    {
      if ( (!PSG->Envelope_EnC) ||
           (*(tUShort*)&PSG->RegisterAY.UChar.TonCLo > 4)) // Little Endian ONLY !!
      {
        k = PSG->Ton_C;
      }
      else
      {
        k = 1;
      }
    }
    else
    {
      k = 1;
    }

    if (PSG->Noise_EnC)
    {
      k &= PSG->Noise.s.Val;
    }
    if (k)
    {
      if (PSG->Envelope_EnC)
      {
        Level += PSG->Level_PLAYCITY[PSG->RegisterAY.UChar.AmplitudeC * 2 + 1];
      }
      else
      {
        Level += PSG->Level_PLAYCITY[PSG->AmplitudeEnv];
      }
    }
  }

  return Level;
}
/*----------------------------------------------------------------------------*/

static tVoid PlayCity_audio_Stereo16(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  PlayCity_audio_Stereo16
 *
 ***********************************************************************/
{
tPlayCity* PlayCity = NativeCPC->PlayCity;
tULong Tick_Counter = PlayCity->LoopCount >> PLAYCITY_LOOP_COUNT_SHIFT;
tNativeULongField SampleValue;

  while (PlayCity->LoopCount >= PLAYCITY_LOOP_COUNT_INCREMENT)
  {
    PlayCity->LoopCount -= PLAYCITY_LOOP_COUNT_INCREMENT;

    audio_Synthesizer_Logic_Q(PlayCity->PSG_Left);
    audio_Synthesizer_Logic_Q(PlayCity->PSG_Right);

    PlayCity->Left_Chan += PlayCity_audio_Mixer_Q(PlayCity->PSG_Left);
    PlayCity->Right_Chan += PlayCity_audio_Mixer_Q(PlayCity->PSG_Right);
  }

  PlayCity->LoopCount += PlayCity->LoopCountInit;

#ifndef AUDIO_DISABLE_LEFT
  SampleValue.UShort.usL = SND_SAMPLE_OFFSET_16BIT + PlayCity->Left_Chan / Tick_Counter;
#else /* !AUDIO_DISABLE_LEFT */
  SampleValue.UShort.usL = 0;
#endif /* !AUDIO_DISABLE_LEFT */

#ifndef AUDIO_DISABLE_RIGHT
  SampleValue.UShort.usH = SND_SAMPLE_OFFSET_16BIT + PlayCity->Right_Chan / Tick_Counter;
#else /* !AUDIO_DISABLE_RIGHT */
  SampleValue.UShort.usH = 0;
#endif /* !AUDIO_DISABLE_RIGHT */

  if (PlayCity->FilledBufferSize < PlayCity->SndBufferSize)
  {
    *(tULong*)PlayCity->snd_bufferptr = SampleValue.ULong; // write to mixing buffer
    PlayCity->snd_bufferptr += 4;
    PlayCity->FilledBufferSize += 4;
  }

  PlayCity->Left_Chan = 0;
  PlayCity->Right_Chan = 0;

  if (PlayCity->snd_bufferptr >= PlayCity->pbSndBufferEnd)
  {
    PlayCity->snd_bufferptr = PlayCity->pbSndBuffer;
    PlayCity->buffer_full = 1;
  }
}
/*----------------------------------------------------------------------------*/

static tVoid PlayCity_audio_Stereo16_Invert(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  PlayCity_audio_Stereo16_Invert
 *
 ***********************************************************************/
{
tPlayCity* PlayCity = NativeCPC->PlayCity;
tULong Tick_Counter = PlayCity->LoopCount >> PLAYCITY_LOOP_COUNT_SHIFT;
tNativeULongField SampleValue;

  while (PlayCity->LoopCount >= PLAYCITY_LOOP_COUNT_INCREMENT)
  {
    PlayCity->LoopCount -= PLAYCITY_LOOP_COUNT_INCREMENT;

    audio_Synthesizer_Logic_Q(PlayCity->PSG_Left);
    audio_Synthesizer_Logic_Q(PlayCity->PSG_Right);

    PlayCity->Right_Chan += PlayCity_audio_Mixer_Q(PlayCity->PSG_Left);
    PlayCity->Left_Chan += PlayCity_audio_Mixer_Q(PlayCity->PSG_Right);
  }

  PlayCity->LoopCount += PlayCity->LoopCountInit;

#ifndef AUDIO_DISABLE_LEFT
  SampleValue.UShort.usL = SND_SAMPLE_OFFSET_16BIT + PlayCity->Left_Chan / Tick_Counter;
#else /* !AUDIO_DISABLE_LEFT */
  SampleValue.UShort.usL = 0;
#endif /* !AUDIO_DISABLE_LEFT */

#ifndef AUDIO_DISABLE_RIGHT
  SampleValue.UShort.usH = SND_SAMPLE_OFFSET_16BIT + PlayCity->Right_Chan / Tick_Counter;
#else /* !AUDIO_DISABLE_RIGHT */
  SampleValue.UShort.usH = 0;
#endif /* !AUDIO_DISABLE_RIGHT */

  if (PlayCity->FilledBufferSize < PlayCity->SndBufferSize)
  {
    *(tULong*)PlayCity->snd_bufferptr = SampleValue.ULong; // write to mixing buffer
    PlayCity->snd_bufferptr += 4;
    PlayCity->FilledBufferSize += 4;
  }

  PlayCity->Left_Chan = 0;
  PlayCity->Right_Chan = 0;

  if (PlayCity->snd_bufferptr >= PlayCity->pbSndBufferEnd)
  {
    PlayCity->snd_bufferptr = PlayCity->pbSndBuffer;
    PlayCity->buffer_full = 1;
  }
}
/*----------------------------------------------------------------------------*/

static tVoid PlayCity_audio_Mono16(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  PlayCity_audio_Mono16
 *
 ***********************************************************************/
{
tPlayCity* PlayCity = NativeCPC->PlayCity;
tULong Tick_Counter = PlayCity->LoopCount >> PLAYCITY_LOOP_COUNT_SHIFT;
tUShort SampleValue;

  while (PlayCity->LoopCount >= PLAYCITY_LOOP_COUNT_INCREMENT)
  {
    PlayCity->LoopCount -= PLAYCITY_LOOP_COUNT_INCREMENT;

    audio_Synthesizer_Logic_Q(PlayCity->PSG_Left);
    audio_Synthesizer_Logic_Q(PlayCity->PSG_Right);

    PlayCity->Left_Chan += PlayCity_audio_Mixer_Q(PlayCity->PSG_Left);
    PlayCity->Left_Chan += PlayCity_audio_Mixer_Q(PlayCity->PSG_Right);
  }

  PlayCity->LoopCount += PlayCity->LoopCountInit;

  SampleValue = SND_SAMPLE_OFFSET_16BIT + PlayCity->Left_Chan / Tick_Counter;

  if (PlayCity->FilledBufferSize < PlayCity->SndBufferSize)
  {
    *(tUShort*)PlayCity->snd_bufferptr = SampleValue; // write to mixing buffer
    PlayCity->snd_bufferptr += 2;
    PlayCity->FilledBufferSize += 2;
  }

  PlayCity->Left_Chan = 0;

  if (PlayCity->snd_bufferptr >= PlayCity->pbSndBufferEnd)
  {
    PlayCity->snd_bufferptr = PlayCity->pbSndBuffer;
    PlayCity->buffer_full = 1;
  }
}
/*----------------------------------------------------------------------------*/


//
// FDC
//

/*
  Status Register Identification
  ==============================

  Invalid
                D7  D6  D5  D4  D3  D2  D1  D0
  Command   W  <------- Invalid code --------> Command codes
  Result    R  <------------ ST0 ------------> ST0=0x80


  ST0
  ===
  D7-6: IC  = Interrupt Code : 00=NT=Normal Terminaison
                               01=AT=Abnormal Terminaison
                               10=IC=Invalid Command Issue
                               11=Abnormal terminaison
  D5:   SE  = Seek End
  D4:   EC  = Equipment Check
  D3:   NR  = Not Ready
  D2:   HD  = Head Address
  D1:   US1 = Unit Select 1
  D0:   US0 = Unit Select 0

  ST1
  ===
  D7:   EN  = End of Cylinder
  D6:   (not used, always 0)
  D5:   DE  = Data Error
  D4:   OR  = Overrun
  D3:   (not used, always 0)
  D2:   ND  = No Data
  D1:   NW  = Not Writable
  D0:   MA  = Missing Address Mark

  ST2
  ===
  D7:   (not used, always 0)
  D6:   CM  = Control Mark
  D5:   DD  = Data Error in Data Field
  D4:   WC  = Wrong Cylinder
  D3:   SH  = Scan Equal Hit
  D2:   SN  = Scan Not Satisfied
  D1:   BC  = Bad Cylinder
  D0:   MD  = Missing Address Mark in Data Field

  ST3
  ===
  D7:   FT  = Fault
  D6:   WP  = Write Protected
  D5:   RY  = Ready
  D4:   T0  = Track 0
  D3:   TS  = Two side
  D2:   HD  = Head Address
  D1:   US1 = Unit Select 1
  D0:   US0 = Unit Select 0
                               
  
  Command Symbol Description:
  ===========================
  C       : Cylinder Number
  DTL     : Data Length (When N is defined as 00, Data Length to read or write)
  EOT     : End of Track (Final sector number on a cylinder)
  GPL     : Gap Length (Length of GAP 3)
  H       : Head Address (Head number 0 or 1)
  HD      : Head Address
  HLT     : Head Load Time (2 to 254ms LSB=2ms)
  HUT     : Head Unload Time (16 to 240ms LSB=16ms)
  MF      : FM or MFM Mode (0=FM, 1=MFM)
  N       : Number (number of data bytes written in a sector)
  NCN     : New Cylinder Number
  ND      : Non-DMA Mode
  PCN     : Present Cylinder Number
  R       : Record (Sector Number to be read or written)
  SK      : Skip (Skip deleted data address mark)
  SRT     : Step Rate Time (1 to 16ms LSB=1ms)
  ST0-ST3 : Status 0-3
  STP     : Contiguous sectors / Alternate sectors
  US0-US1 : Unit Select 0-1
*/

static tVoid fdc_init(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  fdc_init
 *
 ***********************************************************************/
{
tFDC* FDC = NativeCPC->FDC;
fdc_cmd_table_def* TableIndex;

  FDC->pbGPBuffer = NativeCPC->pbGPBuffer;
  FDC->NewMemFct = NativeCPC->MemPtrNewPtr;
  FDC->DeleteMemFct = NativeCPC->MemPtrDeletePtr;

  // Use fast commands interface
  if (NativeCPC->fast_fdc)
  {
    FDC->z80_wait_state_fct = fdc_z80_wait_state_fast;
    FDC->cmd_motor_fct = fdc_cmd_motor_fast;
    FDC->read_status_fct = fdc_read_status_fast;
    FDC->read_data_fct = fdc_read_data_fast;
    FDC->write_data_fct = fdc_write_data_fast;
  }
  else
  {
    FDC->z80_wait_state_fct = fdc_z80_wait_state;
    FDC->cmd_motor_fct = fdc_cmd_motor;
    FDC->read_status_fct = fdc_read_status;
    FDC->read_data_fct = fdc_read_data;
    FDC->write_data_fct = fdc_write_data;
  }

  TableIndex = FDC->FDCCommandTable;
  if (NativeCPC->fast_fdc)
  {
    // Fast FDC commands
    /* Cmd=0x03 */ TableIndex[0].cmd_handler  = fdc_specify_fast;
    /* Cmd=0x04 */ TableIndex[1].cmd_handler  = fdc_drvstat_fast;
    /* Cmd=0x07 */ TableIndex[2].cmd_handler  = fdc_recalib_fast;
    /* Cmd=0x08 */ TableIndex[3].cmd_handler  = fdc_intstat_fast;
    /* Cmd=0x0f */ TableIndex[4].cmd_handler  = fdc_seek_fast;
    /* Cmd=0x42 */ TableIndex[5].cmd_handler  = fdc_readtrk_fast;
    /* Cmd=0x45 */ TableIndex[6].cmd_handler  = fdc_write_fast;
    /* Cmd=0x46 */ TableIndex[7].cmd_handler  = fdc_read_fast;
    /* Cmd=0x49 */ TableIndex[8].cmd_handler  = fdc_write_fast;
    /* Cmd=0x4a */ TableIndex[9].cmd_handler  = fdc_readID_fast;
    /* Cmd=0x4c */ TableIndex[10].cmd_handler = fdc_read_fast;
    /* Cmd=0x4d */ TableIndex[11].cmd_handler = fdc_format_fast;
    /* Cmd=0x51 */ TableIndex[12].cmd_handler = fdc_scan_fast;
    /* Cmd=0x59 */ TableIndex[13].cmd_handler = fdc_scan_fast;
    /* Cmd=0x5d */ TableIndex[14].cmd_handler = fdc_scan_fast;
  }
  else
  {
    // Real FDC commands
    /* Cmd=0x03 */ TableIndex[0].cmd_handler  = fdc_specify;
    /* Cmd=0x04 */ TableIndex[1].cmd_handler  = fdc_drvstat;
    /* Cmd=0x07 */ TableIndex[2].cmd_handler  = fdc_recalib;
    /* Cmd=0x08 */ TableIndex[3].cmd_handler  = fdc_intstat;
    /* Cmd=0x0f */ TableIndex[4].cmd_handler  = fdc_seek;
    /* Cmd=0x42 */ TableIndex[5].cmd_handler  = fdc_readtrk;
    /* Cmd=0x45 */ TableIndex[6].cmd_handler  = fdc_write;
    /* Cmd=0x46 */ TableIndex[7].cmd_handler  = fdc_read;
    /* Cmd=0x49 */ TableIndex[8].cmd_handler  = fdc_write;
    /* Cmd=0x4a */ TableIndex[9].cmd_handler  = fdc_readID;
    /* Cmd=0x4c */ TableIndex[10].cmd_handler = fdc_read;
    /* Cmd=0x4d */ TableIndex[11].cmd_handler = fdc_format_alt;
    /* Cmd=0x51 */ TableIndex[12].cmd_handler = fdc_scan;
    /* Cmd=0x59 */ TableIndex[13].cmd_handler = fdc_scan;
    /* Cmd=0x5d */ TableIndex[14].cmd_handler = fdc_scan;
  }
}
/*----------------------------------------------------------------------------*/


#define FDC_CHECK_UNIT_FAST \
{ \
  FDC->active_drive = FDC->command[CMD_UNIT] & 1 ? FDC->DriveB : FDC->DriveA; \
}


static tBool fdc_cmd_motor_fast(tFDC* FDC, tULong val)
/***********************************************************************
 *
 *  fdc_cmd_motor_fast
 *
 ***********************************************************************/
{
  FDC->motor = val & 0x01;
  FDC->flags |= STATUSDRVA_flag | STATUSDRVB_flag;

  return (FDC->motor ? cTrue : cFalse);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_z80_wait_state_fast(tFDC* FDC, tULong CycleCount)
/***********************************************************************
 *
 *  fdc_z80_wait_state_fast
 *
 ***********************************************************************/
{
  if (FDC->phase == EXEC_PHASE)
  {
    FDC->timeout -= CycleCount;
    if (FDC->timeout <= 0)
    {
      FDC->flags |= OVERRUN_flag;
      if (FDC->cmd_direction == FDC_TO_CPU)
      {
        fdc_read_data_fast(FDC);
      }
      else
      {
        fdc_write_data_fast(FDC, 0xff);
      }
    }
  }
}
/*----------------------------------------------------------------------------*/


static tULong fdc_read_status_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_status_fast
 *
 ***********************************************************************/
{
tULong val;

  val = MSR_RQM; // data register ready

  if (FDC->phase == EXEC_PHASE) // in execution phase?
  {
    if (FDC->read_status_delay)
    {
      val = MSR_CB; // FDC is busy
      FDC->read_status_delay--;
    }
    else
    {
      val |= MSR_EXM | MSR_CB; // FDC is executing & busy
    }

    if (FDC->cmd_direction == FDC_TO_CPU)
    {
      val |= MSR_DIO; // FDC is sending data to the CPU
    }
  }

  else if (FDC->phase == RESULT_PHASE) // in result phase?
  {
    val |= MSR_DIO | MSR_CB; // FDC is sending data to the CPU, and is busy
  }

  else // in command phase
  {
    if (FDC->byte_count) // receiving command parameters?
    {
      val |= MSR_CB; // FDC is busy
    }
  }
  
#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_read_status++;
  FDC->read_status_value = val;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */

  return val;
}
/*----------------------------------------------------------------------------*/


static tULong fdc_read_data_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_data_fast
 *
 ***********************************************************************/
{
tULong val;

  val = 0xff; // default value

  //
  // in execution phase?
  //
  if (FDC->phase == EXEC_PHASE)
  {
    if (FDC->cmd_direction == FDC_TO_CPU) // proper direction?
    {
      FDC->timeout = OVERRUN_TIMEOUT;
      val = *FDC->buffer_ptr++; // read byte from current sector

      if (FDC->buffer_ptr >= FDC->buffer_endptr)
      {
        FDC->buffer_ptr = FDC->active_track->data; // wrap around
      }

      // completed the data transfer?
      // Original: if (!(--FDC->buffer_count))
      if (FDC->buffer_count) FDC->buffer_count--;
      if (!FDC->buffer_count) // completed the data transfer?
      {
        if (FDC->flags & RNDDE_flag) // simulate random Data Errors?
        {
          // ***! random DE handling
        }

        FDC->active_drive->current_sector++; // increase sector index
#ifdef ENABLE_METROLOGY
        //FDC->signal_updated++; // Done at the end
        FDC->signal_new_position++;
#endif /* ENABLE_METROLOGY */

        if (FDC->flags & OVERRUN_flag) // overrun condition detected?
        {
          FDC->flags &= ~OVERRUN_flag;
          FDC->result[RES_ST0] |= 0x40; // AT
          FDC->result[RES_ST1] |= 0x10; // Overrun

          LOAD_RESULT_WITH_CHRN;

          FDC->phase = RESULT_PHASE; // switch to result phase
#ifdef ENABLE_METROLOGY
          //FDC->signal_updated++; // Done at the end
          FDC->signal_command_result++;
          FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
        }
        else
        {
          if (FDC->command[CMD_CODE] == 0x42) // read track command?
          {
            if ((--FDC->command[CMD_EOT])) // continue reading sectors?
            {
              if (FDC->active_drive->current_sector >= FDC->active_track->sectors) // index beyond number of sectors for this track?
              {
                FDC->active_drive->current_sector = 0; // reset index
#ifdef ENABLE_METROLOGY
                //FDC->signal_updated++; // Done at the end
                FDC->signal_new_position++;
#endif /* ENABLE_METROLOGY */
              }

              FDC->command[CMD_R]++; // advance to next sector

              fdc_cmd_readtrk_fast(FDC);
            }
            else
            {
              LOAD_RESULT_WITH_STATUS;
              LOAD_RESULT_WITH_CHRN;

              FDC->phase = RESULT_PHASE; // switch to result phase
#ifdef ENABLE_METROLOGY
              //FDC->signal_updated++; // Done at the end
              FDC->signal_command_result++;
              FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
            }
          }
          else // normal read (deleted) data command
          {
            if (!((FDC->result[RES_ST1] & 0x31) || (FDC->result[RES_ST2] & 0x21))) // no error bits set?
            {
              if (FDC->command[CMD_R] != FDC->command[CMD_EOT]) // haven't reached End of Track?
              {
                FDC->command[CMD_R]++; // advance to next sector

                fdc_cmd_read_fast(FDC);
              }
              else
              {
                LOAD_RESULT_WITH_STATUS;
                LOAD_RESULT_WITH_CHRN;

                FDC->phase = RESULT_PHASE; // switch to result phase
#ifdef ENABLE_METROLOGY
                //FDC->signal_updated++; // Done at the end
                FDC->signal_command_result++;
                FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
              }
            }
            else
            {
              LOAD_RESULT_WITH_STATUS;
              LOAD_RESULT_WITH_CHRN;

              FDC->phase = RESULT_PHASE; // switch to result phase
#ifdef ENABLE_METROLOGY
              //FDC->signal_updated++; // Done at the end
              FDC->signal_command_result++;
              FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
            }
          }
        }
      }
    }
  }

  //
  // in result phase?
  //
  else if (FDC->phase == RESULT_PHASE)
  {
    val = FDC->result[FDC->byte_count++]; // copy value from buffer

    if (FDC->byte_count == FDC->res_length) // sent all result bytes?
    {
      FDC->flags &= ~SCAN_flag; // reset scan command flag
      FDC->byte_count = 0; // clear byte counter
      FDC->phase = CMD_PHASE; // switch to command phase
      FDC->led = 0; // turn the drive LED off
    }
  }

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_read_data++;
  FDC->signal_data = val;
#endif /* ENABLE_METROLOGY */

  return val;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_write_data_fast(tFDC* FDC, tULong val)
/***********************************************************************
 *
 *  fdc_write_data_fast
 *
 ***********************************************************************/
{
tUChar* pbPtr;
tUChar* pbDataPtr;
tULong sector;
tULong idx;
tULong sector_size;
tULong track_size;

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_write_data++;
  FDC->signal_data = val;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */

  //
  // in command phase?
  //
  if (FDC->phase == CMD_PHASE)
  {
    if (FDC->byte_count) // receiving command parameters?
    {
      FDC->command[FDC->byte_count++] = val; // copy to buffer

      if (FDC->byte_count == FDC->cmd_length) // received all command bytes?
      {
#ifdef ENABLE_METROLOGY
        //FDC->signal_updated++; // Done at the beginning
        FDC->signal_new_command++;
#endif /* ENABLE_METROLOGY */

        FDC->byte_count = 0; // clear byte counter
        FDC->phase = EXEC_PHASE; // switch to execution phase

        FDC->cmd_handler(FDC);
      }
    }
    else // first command byte received
    {
      if (val & 0x20) // skip DAM or DDAM?
      {
        FDC->flags |= SKIP_flag; // DAM/DDAM will be skipped
        val &= ~0x20; // reset skip bit in command byte
      }
      else
      {
        FDC->flags &= ~SKIP_flag; // make sure skip indicator is off
      }

      for (idx = 0; idx < MAX_CMD_COUNT; idx++) // loop through all known FDC commands
      {
        if (FDC->FDCCommandTable[idx].cmd == val) // do we have a match?
        {
          break;
        }
      }

      if (idx != MAX_CMD_COUNT) // valid command received
      {
        FDC->cmd_index = idx;
        FDC->cmd_length = FDC->FDCCommandTable[idx].cmd_length; // command length in bytes
        FDC->res_length = FDC->FDCCommandTable[idx].res_length; // result length in bytes
        FDC->cmd_direction = FDC->FDCCommandTable[idx].cmd_direction; // direction is CPU to FDC, or FDC to CPU
        FDC->cmd_handler = FDC->FDCCommandTable[idx].cmd_handler; // pointer to command handler

        FDC->command[FDC->byte_count++] = val; // copy command code to buffer

        if (FDC->byte_count == FDC->cmd_length) // 1 byte command requested ?
        {
#ifdef ENABLE_METROLOGY
          //FDC->signal_updated++; // Done at the beginning
          FDC->signal_new_command++;
#endif /* ENABLE_METROLOGY */

          FDC->byte_count = 0; // clear byte counter
          FDC->phase = EXEC_PHASE; // switch to execution phase
          FDC->cmd_handler(FDC);
        }
      }
      else // unknown command received
      {
#ifdef ENABLE_METROLOGY
        //FDC->signal_updated++; // Done at the beginning
        FDC->signal_new_command++;
        FDC->signal_command_result++;
        FDC->cmd_index = (tULong)-1;
        FDC->command[0] = val; // copy command code to buffer
#endif /* ENABLE_METROLOGY */

        FDC->result[RES_ST0] = 0x80; // indicate invalid command
        FDC->res_length = 1;
        FDC->phase = RESULT_PHASE; // switch to result phase
      }
    }
  }

  //
  // in execution phase?
  //
  else if (FDC->phase == EXEC_PHASE)
  {
    if (FDC->cmd_direction == CPU_TO_FDC) // proper direction?
    {
      FDC->timeout = OVERRUN_TIMEOUT;

      if ((FDC->flags & SCAN_flag)) // processing any of the scan commands?
      {
        if (val != 0xff) // no comparison on CPU byte = 0xff
        {
          // scan equal
          if (FDC->command[CMD_CODE] == 0x51)
          {
            if (val != (tULong)(*FDC->buffer_ptr))
            {
              FDC->result[RES_ST2] &= 0xf7; // reset Scan Equal Hit
              FDC->flags |= SCANFAILED_flag;
            }
          }
          // scan low or equal
          else if (FDC->command[CMD_CODE] == 0x59)
          {
            if (val != (tULong)(*FDC->buffer_ptr))
            {
              FDC->result[RES_ST2] &= 0xf7; // reset Scan Equal Hit

              if (val > (tULong)(*FDC->buffer_ptr))
              {
                FDC->flags |= SCANFAILED_flag;
              }
            }
          }
          // scan high or equal
          else if (FDC->command[CMD_CODE] == 0x5d)
          {
            if (val != (tULong)(*FDC->buffer_ptr))
            {
              FDC->result[RES_ST2] &= 0xf7; // reset Scan Equal Hit

              if (val < (tULong)(*FDC->buffer_ptr))
              {
                FDC->flags |= SCANFAILED_flag;
              }
            }
          }
        }
        FDC->buffer_ptr++; // advance sector data pointer
      }
      else
      {
        *FDC->buffer_ptr++ = val; // write byte to sector
      }

      if (FDC->buffer_ptr > FDC->buffer_endptr)
      {
        FDC->buffer_ptr = FDC->active_track->data; // wrap around
      }

      // processed all data?
      // Original: if (--FDC->buffer_count == 0)
      // Fix to load "Top Secret"
      if (FDC->buffer_count) FDC->buffer_count--;
      if (!FDC->buffer_count) // processed all data?
      {
        if ((FDC->flags & SCAN_flag)) // processing any of the scan commands?
        {
          if ( (FDC->flags & SCANFAILED_flag) &&
               (FDC->command[CMD_R] != FDC->command[CMD_EOT]) )
          {
            FDC->command[CMD_R] += FDC->command[CMD_STP]; // advance to next sector

            fdc_cmd_scan_fast(FDC);
          }
          else
          {
            if ((FDC->flags & SCANFAILED_flag))
            {
              FDC->result[RES_ST2] |= 0x04; // Scan Not Satisfied
            }

            LOAD_RESULT_WITH_CHRN;

            FDC->phase = RESULT_PHASE; // switch to result phase
#ifdef ENABLE_METROLOGY
            //FDC->signal_updated++; // Done at the beginning
            FDC->signal_command_result++;
#endif /* ENABLE_METROLOGY */
          }
        }
        else if (FDC->command[CMD_CODE] == 0x4d) // write ID command?
        {
          sector_size = 128 << FDC->command[CMD_C]; // determine number of bytes from N value
          track_size = sector_size * FDC->command[CMD_H];

          if (FDC->active_track->sectors != 0) // track is formatted?
          {
            MemSetByte(FDC->active_track->data,
                       track_size,
                       0);
          }

          if (((sector_size + 62 + FDC->command[CMD_R]) * FDC->command[CMD_H]) > FDC_MAX_TRACK_SIZE) // track size exceeds maximum ?
          {
            FDC->active_track->sectors = 0; // 'unformat' track
          }
          else
          {
            FDC->active_track->sectors = FDC->command[CMD_H];

            pbDataPtr = FDC->active_track->data;
            pbPtr = FDC->pbGPBuffer;
            
            for (sector = 0;
                 sector < FDC->command[CMD_H];
                 sector++)
            {
              //*((tULong*)FDC->active_track->sector[sector].CHRN) = *((tULong*)pbPtr); // copy CHRN
							MemMoveByte(FDC->active_track->sector[sector].CHRN,
                          (const tUChar*)pbPtr,
                          4); // copy CHRN
              FDC->active_track->sector[sector].flags[0] = 0; // clear ST1
              FDC->active_track->sector[sector].flags[1] = 0; // clear ST2

              FDC->active_track->sector[sector].data = pbDataPtr; // store pointer to sector data

              pbDataPtr += sector_size;
              pbPtr += 4;
            }

            MemSetByte(FDC->active_track->data,
                       track_size,
                       FDC->command[CMD_N]); // fill track data with specified byte value
          }

          pbPtr = FDC->pbGPBuffer + ((FDC->command[CMD_H]-1) * 4); // pointer to the last CHRN passed to writeID

          // FDC->result = tULong*
          // pbPtr = tUChar*
          FDC->result[RES_C] = *pbPtr++;
          FDC->result[RES_H] = *pbPtr++;
          FDC->result[RES_R] = *pbPtr;
          FDC->result[RES_N] = FDC->command[CMD_C]; // overwrite with the N value from the writeID command

          FDC->active_drive->altered = 1; // indicate that the image has been modified

          FDC->phase = RESULT_PHASE; // switch to result phase
#ifdef ENABLE_METROLOGY
          //FDC->signal_updated++; // Done at the beginning
          FDC->signal_command_result++;
          FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
        }
        else if (FDC->command[CMD_R] != FDC->command[CMD_EOT]) // haven't reached End of Track?
        {
          FDC->command[CMD_R]++; // advance to next sector

          fdc_cmd_write_fast(FDC);
        }
        else
        {
          FDC->active_drive->altered = 1; // indicate that the image has been modified

          FDC->result[RES_ST0] |= 0x40; // AT
          FDC->result[RES_ST1] |= 0x80; // End of Cylinder

          LOAD_RESULT_WITH_CHRN;

          FDC->phase = RESULT_PHASE; // switch to result phase
#ifdef ENABLE_METROLOGY
          //FDC->signal_updated++; // Done at the beginning
          FDC->signal_command_result++;
          FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
        }
      }
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_specify_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_specify_fast
 *
 ***********************************************************************/
{
  FDC->phase = CMD_PHASE; // switch back to command phase (fdc_specify has no result phase!)
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_drvstat_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_drvstat_fast
 *
 ***********************************************************************/
{
tULong val;

  FDC_CHECK_UNIT_FAST;

  val = FDC->command[CMD_UNIT] & 7; // keep head and unit of command

#ifdef FDC_INVERT_ST3_TS
  if (FDC->active_drive->sides)
#else /* FDC_INVERT_ST3_TS */
  if (!FDC->active_drive->sides)
#endif /* FDC_INVERT_ST3_TS */
  {
    val |= 0x08;
  }

  if ( (FDC->active_drive->write_protected) ||
       (FDC->active_drive->tracks == 0) ) // write protected, or disk missing?
  {
    // Original
    //val |= 0x48; // set Write Protect + Two Sided (?)
    val |= 0x40; // set Write Protect
  }

  if ((FDC->active_drive->tracks) && (FDC->motor))
  {
    val |= 0x20; // set Ready
  }

  if (FDC->active_drive->current_track == 0) // drive head is over track 0?
  {
    val |= 0x10; // set Track 0
  }

  FDC->result[RES_ST0] = val;
  FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_recalib_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_recalib_fast
 *
 ***********************************************************************/
{
  FDC->command[CMD_C] = 0; // seek to track 0
  fdc_seek_fast(FDC);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_intstat_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_intstat_fast
 *
 ***********************************************************************/
{
tULong val;

  val = FDC->result[RES_ST0] & ~(ST0_HD | ST0_US1 | ST0_US0); // clear Head Address and Unit bits

  if (FDC->flags & SEEKDRVA_flag) // seek completed on drive A?
  {
    val |= ST0_SE; // set Seek End

    FDC->flags &= ~(SEEKDRVA_flag | STATUSDRVA_flag); // clear seek done and status change flags
    FDC->result[RES_ST0] = val;
    FDC->result[RES_ST1] = FDC->DriveA->current_track; // PCN
  }
  else if (FDC->flags & SEEKDRVB_flag) // seek completed on drive B?
  {
    val |= (ST0_SE | ST0_US0); // set Seek End

    FDC->flags &= ~(SEEKDRVB_flag | STATUSDRVB_flag); // clear seek done and status change flags
    FDC->result[RES_ST0] = val;
    FDC->result[RES_ST1] = FDC->DriveB->current_track; // PCN
  }
  else if (FDC->flags & STATUSDRVA_flag) // has the status of drive A changed?
  {
    val = ST0_ATR; // status change

    if ((FDC->DriveA->tracks == 0) || (!FDC->motor)) // no DSK in the drive, or drive motor is turned off?
    {
      val |= ST0_NR; // not ready
    }

    FDC->flags &= ~STATUSDRVA_flag; // clear status change flag
    FDC->result[RES_ST0] = val;
    FDC->result[RES_ST1] = FDC->DriveA->current_track; // PCN
  }
  else if (FDC->flags & STATUSDRVB_flag) // has the status of drive B changed?
  {
    val = (ST0_ATR | ST0_US0); // status change

    if ((FDC->DriveB->tracks == 0) || (!FDC->motor)) // no DSK in the drive, or drive motor is turned off?
    {
      val |= ST0_NR; // not ready
    }

    FDC->flags &= ~STATUSDRVB_flag; // clear status change flag
    FDC->result[RES_ST0] = val;
    FDC->result[RES_ST1] = FDC->DriveB->current_track; // PCN
  }
  else
  {
    val = ST0_IC; // Invalid Command

    FDC->result[RES_ST0] = val;
    FDC->res_length = 1;
  }

  // Prepare result
  FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_seek_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_seek_fast
 *
 ***********************************************************************/
{
  FDC_CHECK_UNIT_FAST;

  if (fdc_init_status_regs_fast(FDC) == 0) // drive Ready?
  {
    FDC->active_drive->current_track = FDC->command[CMD_C];
#ifdef ENABLE_METROLOGY
    FDC->signal_updated++;
    FDC->signal_new_position++;
#endif /* ENABLE_METROLOGY */

    if (FDC->active_drive->current_track >= DSK_TRACKMAX) // beyond valid range?
    {
      FDC->active_drive->current_track = DSK_TRACKMAX-1; // limit to maximum
    }
  }

  FDC->flags |= FDC->command[CMD_UNIT] & 1 ? SEEKDRVB_flag : SEEKDRVA_flag; // signal completion of seek operation
  FDC->phase = CMD_PHASE; // switch back to command phase (fdc_seek has no result phase!)
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readtrk_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readtrk_fast
 *
 ***********************************************************************/
{
tULong side;

  FDC->led = 1; // turn the drive LED on

  FDC_CHECK_UNIT_FAST;

  if (fdc_init_status_regs_fast(FDC) == 0) // drive Ready?
  {
    FDC->active_drive->current_side = (FDC->command[CMD_UNIT] & 4) >> 2; // extract target side
    // Double sided ?
    if (FDC->active_drive->sides)
    {
      side = FDC->active_drive->current_side;
      // did the user request to access the "other" side?
      if (FDC->active_drive->flipped)
      {
			  side = side ? 0 : 1; // reverse the side to access
      }
    }
    else
    {
      side = 0;
    }

    FDC->active_track = &FDC->active_drive->track[FDC->active_drive->current_track][side];

    if (FDC->active_track->sectors != 0) // track is formatted?
    {
      FDC->command[CMD_R] = 1; // set sector ID to 1
      FDC->active_drive->current_sector = 0; // reset sector table index

#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_new_position++;
#endif /* ENABLE_METROLOGY */

      fdc_cmd_readtrk_fast(FDC);
    }
    else // unformatted track
    {
      FDC->result[RES_ST0] |= 0x40; // AT
      FDC->result[RES_ST1] |= 0x01; // Missing AM

      LOAD_RESULT_WITH_CHRN;

      FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_command_result++;
      FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
    }
  }
  else // drive was not ready
  {
    LOAD_RESULT_WITH_CHRN;

    FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
    FDC->signal_updated++;
    FDC->signal_command_result++;
    FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_write_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_write_fast
 *
 ***********************************************************************/
{
tULong side;

  FDC->led = 1; // turn the drive LED on

  FDC_CHECK_UNIT_FAST;

  if (fdc_init_status_regs_fast(FDC) == 0) // drive Ready?
  {
    FDC->active_drive->current_side = (FDC->command[CMD_UNIT] & 4) >> 2; // extract target side
    // Double sided ?
    if (FDC->active_drive->sides)
    {
      side = FDC->active_drive->current_side;
      // did the user request to access the "other" side?
      if (FDC->active_drive->flipped)
      {
			  side = side ? 0 : 1; // reverse the side to access
      }
    }
    else
    {
      side = 0;
    }

    FDC->active_track = &FDC->active_drive->track[FDC->active_drive->current_track][side];

    if (FDC->active_drive->write_protected) // is write protect tab set?
    {
      FDC->result[RES_ST0] |= 0x40; // AT
      FDC->result[RES_ST1] |= 0x02; // Not Writable

      LOAD_RESULT_WITH_CHRN;

      FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_command_result++;
      FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
    }
    else if (FDC->active_track->sectors != 0) // track is formatted?
    {
      fdc_cmd_write_fast(FDC);
    }
    else // unformatted track
    {
      FDC->result[RES_ST0] |= 0x40; // AT
      FDC->result[RES_ST1] |= 0x01; // Missing AM

      LOAD_RESULT_WITH_CHRN;

      FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_command_result++;
      FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
    }
  }
  else // drive was not ready
  {
    LOAD_RESULT_WITH_CHRN;

    FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
    FDC->signal_updated++;
    FDC->signal_command_result++;
    FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_read_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_fast
 *
 ***********************************************************************/
{
tULong side;

  FDC->led = 1; // turn the drive LED on

  FDC_CHECK_UNIT_FAST;

  if (fdc_init_status_regs_fast(FDC) == 0) // drive Ready?
  {
    FDC->active_drive->current_side = (FDC->command[CMD_UNIT] & 4) >> 2; // extract target side
    // Double sided ?
    if (FDC->active_drive->sides)
    {
      side = FDC->active_drive->current_side;
      // did the user request to access the "other" side?
      if (FDC->active_drive->flipped)
      {
			  side = side ? 0 : 1; // reverse the side to access
      }
    }
    else
    {
      side = 0;
    }

    FDC->active_track = &FDC->active_drive->track[FDC->active_drive->current_track][side];

    if (FDC->active_track->sectors != 0) // track is formatted?
    {
      fdc_cmd_read_fast(FDC);
    }
    else // unformatted track
    {
      FDC->result[RES_ST0] |= 0x40; // AT
      FDC->result[RES_ST1] |= 0x01; // Missing AM
      FDC->result[RES_ST2] |= 0x01; // Missing Address Mark in Data Field

      LOAD_RESULT_WITH_CHRN;

      FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_command_result++;
      FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
    }
  }
  else // drive was not ready
  {
    LOAD_RESULT_WITH_CHRN;

    FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
    FDC->signal_updated++;
    FDC->signal_command_result++;
    FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readID_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readID_fast
 *
 ***********************************************************************/
{
tULong idx;
tULong side;

  FDC->led = 1; // turn the drive LED on

  FDC_CHECK_UNIT_FAST;

  if (fdc_init_status_regs_fast(FDC) == 0) // drive Ready?
  {
    FDC->active_drive->current_side = (FDC->command[CMD_UNIT] & 4) >> 2; // extract target side
    // Double sided ?
    if (FDC->active_drive->sides)
    {
      side = FDC->active_drive->current_side;
      // did the user request to access the "other" side?
      if (FDC->active_drive->flipped)
      {
			  side = side ? 0 : 1; // reverse the side to access
      }
    }
    else
    {
      side = 0;
    }

    FDC->active_track = &FDC->active_drive->track[FDC->active_drive->current_track][side];

    if (FDC->active_track->sectors != 0) // track is formatted?
    {
      idx = FDC->active_drive->current_sector; // get the active sector index
      if (idx >= FDC->active_track->sectors) // index beyond number of sectors for this track?
      {
        idx = 0; // reset index
      }

      // FDC->result = tULong*, FDC->active_track->sector[idx].CHRN = tUChar*
      FDC->result[RES_C] = FDC->active_track->sector[idx].CHRN[0];
      FDC->result[RES_C+1] = FDC->active_track->sector[idx].CHRN[1];
      FDC->result[RES_C+2] = FDC->active_track->sector[idx].CHRN[2];
      FDC->result[RES_C+3] = FDC->active_track->sector[idx].CHRN[3];

      FDC->active_drive->current_sector = idx + 1; // update sector table index for active drive

#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_new_position++;
#endif /* ENABLE_METROLOGY */
    }
    else // unformatted track
    {
      FDC->result[RES_ST0] |= 0x40; // AT
      FDC->result[RES_ST1] |= 0x01; // Missing AM

      LOAD_RESULT_WITH_CHRN;
    }
  }

  FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_fast
 *
 ***********************************************************************/
{
tULong side;

  FDC->led = 1; // turn the drive LED on

  FDC_CHECK_UNIT_FAST;

  if (fdc_init_status_regs_fast(FDC) == 0) // drive Ready?
  {
    FDC->active_drive->current_side = (FDC->command[CMD_UNIT] & 4) >> 2; // extract target side
    // Double sided ?
    if (FDC->active_drive->sides)
    {
      side = FDC->active_drive->current_side;
      // did the user request to access the "other" side?
      if (FDC->active_drive->flipped)
      {
			  side = side ? 0 : 1; // reverse the side to access
      }
    }
    else
    {
      side = 0;
    }

    FDC->active_track = &FDC->active_drive->track[FDC->active_drive->current_track][side];

    if (FDC->active_drive->write_protected) // is write protect tab set?
    {
      FDC->result[RES_ST0] |= 0x40; // AT
      FDC->result[RES_ST1] |= 0x02; // Not Writable

      LOAD_RESULT_WITH_CHRN

      FDC->phase = RESULT_PHASE; // switch to result phase
#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_command_result++;
      FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
    }
    else
    {
      FDC->buffer_count = FDC->command[CMD_H] << 2; // number of sectors * 4 = number of bytes still outstanding
      FDC->buffer_ptr = FDC->pbGPBuffer; // buffer to temporarily hold the track format
      FDC->buffer_endptr = FDC->pbGPBuffer + FDC->buffer_count;
      FDC->timeout = INITIAL_TIMEOUT;
      FDC->read_status_delay = READ_STATUS_DELAY;
    }
  }
  else // drive was not ready
  {
    LOAD_RESULT_WITH_CHRN;

    FDC->phase = RESULT_PHASE; // switch to result phase
  }

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_scan_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_scan_fast
 *
 ***********************************************************************/
{
tULong side;

  FDC->led = 1; // turn the drive LED on

  FDC_CHECK_UNIT_FAST;

  if (fdc_init_status_regs_fast(FDC) == 0) // drive Ready?
  {
    FDC->active_drive->current_side = (FDC->command[CMD_UNIT] & 4) >> 2; // extract target side
    // Double sided ?
    if (FDC->active_drive->sides)
    {
      side = FDC->active_drive->current_side;
      // did the user request to access the "other" side?
      if (FDC->active_drive->flipped)
      {
			  side = side ? 0 : 1; // reverse the side to access
      }
    }
    else
    {
      side = 0;
    }

    FDC->active_track = &FDC->active_drive->track[FDC->active_drive->current_track][side];

    if (FDC->active_track->sectors != 0) // track is formatted?
    {
      if (FDC->command[CMD_STP] > 2)
      {
        FDC->command[CMD_STP] = 2; // step can only be 1 or 2
      }

      FDC->flags |= SCAN_flag; // scan command active
      fdc_cmd_scan_fast(FDC);
    }
    else // unformatted track
    {
      FDC->result[RES_ST0] |= 0x40; // AT
      FDC->result[RES_ST1] |= 0x01; // Missing AM

      LOAD_RESULT_WITH_CHRN;

      FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_command_result++;
			FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
    }
  }
  else // drive was not ready
  {
    LOAD_RESULT_WITH_CHRN;

    FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
    FDC->signal_updated++;
    FDC->signal_command_result++;
    FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_cmd_readtrk_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_cmd_readtrk_fast
 *
 ***********************************************************************/
{
tSector* sector;
tLong sector_size;

  sector = &FDC->active_track->sector[FDC->active_drive->current_sector];

  // sector does not match requested ID?
  if (    (sector->CHRN[0] != (tUChar)(FDC->command[CMD_C]))
       || (sector->CHRN[1] != (tUChar)(FDC->command[CMD_C+1]))
       || (sector->CHRN[2] != (tUChar)(FDC->command[CMD_C+2]))
       || (sector->CHRN[3] != (tUChar)(FDC->command[CMD_C+3])) )
  {
    FDC->result[RES_ST1] |= 0x04; // No Data
  }

  FDC->result[RES_ST2] &= 0xbf; // clear Control Mark, if it was set
  FDC->result[RES_ST1] |= sector->flags[0] & 0x25; // copy ST1 to result, ignoring unused bits
  FDC->result[RES_ST2] |= sector->flags[1] & 0x61; // copy ST2 to result, ignoring unused bits

  if (FDC->command[CMD_N] == 0) // use DTL for length?
  {
    sector_size = FDC->command[CMD_DTL]; // size of sector is defined by DTL value

    if (sector_size > 0x80)
    {
      sector_size = 0x80; // max DTL value is 128
    }
  }
  else
  {
    sector_size = 128 << FDC->command[CMD_N]; // determine number of bytes from N value
  }

  FDC->buffer_count = sector_size; // init number of bytes to transfer
  FDC->buffer_ptr = sector->data; // pointer to sector data
  FDC->buffer_endptr = FDC->active_track->data + FDC->active_track->size; // pointer beyond end of track data
  FDC->timeout = INITIAL_TIMEOUT;
  FDC->read_status_delay = READ_STATUS_DELAY;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_cmd_read_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_cmd_read_fast
 *
 ***********************************************************************/
{
tSector* sector;
tULong sector_size;
tULong loop;

  do
  {
    loop = 0;
    sector = fdc_find_sector_fast(FDC,
                                  &FDC->command[CMD_C]); // locate the requested sector on the current track

    if (sector) // sector found
    {
      FDC->result[RES_ST1] = sector->flags[0] & 0x25; // copy ST1 to result, ignoring unused bits
      FDC->result[RES_ST2] = sector->flags[1] & 0x61; // copy ST2 to result, ignoring unused bits

      if (FDC->command[CMD_CODE] == 0x4c) // read deleted data command?
      {
        FDC->result[RES_ST2] ^= 0x40; // invert Control Mark
      }

      if ((FDC->flags & SKIP_flag) && (FDC->result[RES_ST2] &= 0x40)) // skip sector?
      {
        if (FDC->command[CMD_R] != FDC->command[CMD_EOT]) // continue looking?
        {
          FDC->command[CMD_R]++; // advance to next sector
          loop++;
          continue;
        }
        else // no data to transfer -> no execution phase
        {
          LOAD_RESULT_WITH_STATUS;
          LOAD_RESULT_WITH_CHRN;

          FDC->phase = RESULT_PHASE; // switch to result phase
#ifdef ENABLE_METROLOGY
          FDC->signal_updated++;
          FDC->signal_command_result++;
          FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
        }
      }
      else // sector data is to be transferred
      {
        if (FDC->result[RES_ST2] &= 0x40) // does the sector have an AM opposite of what we want?
        {
          FDC->command[CMD_EOT] = FDC->command[CMD_R]; // execution ends on this sector
        }

        if (FDC->command[CMD_N] == 0) // use DTL for length?
        {
          sector_size = FDC->command[CMD_DTL]; // size of sector is defined by DTL value

          if (sector_size > 0x80)
          {
            sector_size = 0x80; // max DTL value is 128
          }
        }
        else
        {
          sector_size = 128 << FDC->command[CMD_N]; // determine number of bytes from N value
        }

        FDC->buffer_count = sector_size; // init number of bytes to transfer
        FDC->buffer_ptr = sector->data; // pointer to sector data

        // Handle weak sectors
        if (sector->weak_count > 1)
        {
          FDC->weak_counter = (FDC->weak_counter + 1) % sector->weak_count;
          FDC->buffer_ptr += sector->declared_size * FDC->weak_counter;
        }

        FDC->buffer_endptr = FDC->active_track->data + FDC->active_track->size; // pointer beyond end of track data
        FDC->timeout = INITIAL_TIMEOUT;
        FDC->read_status_delay = READ_STATUS_DELAY;
      }
    }
    else // sector not found
    {
      FDC->result[RES_ST0] |= 0x40; // AT
      FDC->result[RES_ST1] |= 0x04; // No Data

      /* Inspired by Caprice Reloaded r459 */
      if (FDC->active_track->sectors == 0)
      {
        // Not formatted track
        FDC->result[RES_ST1] |= 0x01; // Set MA Flag
        FDC->result[RES_ST2] |= 0x01; // Set MD Flag
      }

      LOAD_RESULT_WITH_CHRN;

      FDC->phase = RESULT_PHASE; // switch to result phase
#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_command_result++;
      FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
    }
  }
  while (loop);
}
/*----------------------------------------------------------------------------*/


static tSector* fdc_find_sector_fast(tFDC* FDC,
                                     tULong* requested_CHRN)
/***********************************************************************
 *
 *  fdc_find_sector_fast
 *
 ***********************************************************************/
{
tSector *sector;
tULong loop_count;
tULong idx;
tULong cylinder;

  sector = cNull; // return value indicates 'sector not found' by default
  loop_count = 0; // detection of index hole counter
  idx = FDC->active_drive->current_sector; // get the active sector index

  do
  {
    /* From Caprice Reloaded r462 */
    // If R (sector number) and N (sector size) match the request, transfer can be
    // initialized. We must still set the "no cylinder" flag.
    if ( (requested_CHRN[2] == (tULong)(FDC->active_track->sector[idx].CHRN[2]))
         && (requested_CHRN[3] == (tULong)(FDC->active_track->sector[idx].CHRN[3])) )
    {
      sector = &FDC->active_track->sector[idx]; // return value points to sector information

      if ((sector->flags[0] & 0x20) || (sector->flags[1] & 0x20)) // any Data Errors?
      {
        if (FDC->active_drive->random_DEs) // simulate 'random' DEs?
        {
          FDC->flags |= RNDDE_flag;
        }
      }

      // Check the cylinder for the sector we found. If it's wrong, the flags are set,
      // and the transfer won't occur
      cylinder = FDC->active_track->sector[idx].CHRN[0]; // extract C
      if (cylinder == 0xff)
      {
        FDC->result[RES_ST2] |= 0x02; // Bad Cylinder
        sector = cNull; // abort transfer
      }
      else if (cylinder != FDC->command[CMD_C]) // does not match requested C?
      {
        FDC->result[RES_ST2] |= 0x10; // No Cylinder
        sector = cNull; // abort transfer
      }
      else
      {
        FDC->result[RES_ST2] &= ~(0x02 | 0x10); // remove possible Bad Cylinder + No Cylinder flags
        break;
      }
    }

    idx++; // increase sector table index
    if (idx >= FDC->active_track->sectors) // index beyond number of sectors for this track?
    {
      idx = 0; // reset index
      loop_count++; // increase 'index hole' count
      /* From Caprice Reloaded */
      FDC->result[RES_ST1] |= 0x80; // EN (end of cylinder)
    }

  }
  while (loop_count < 2); // loop until sector is found, or index hole has passed twice

  FDC->active_drive->current_sector = idx; // update sector table index for active drive

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_new_position++;
#endif /* ENABLE_METROLOGY */

  return sector;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_cmd_scan_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_cmd_scan_fast
 *
 ***********************************************************************/
{
tSector *sector;
tULong sector_size;
tULong loop = 1;

  do
  {
    sector = fdc_find_sector_fast(FDC,
                                  &FDC->command[CMD_C]); // locate the requested sector on the current track

    // sector found
    if (sector)
    {
      FDC->result[RES_ST1] = sector->flags[0] & 0x25; // copy ST1 to result, ignoring unused bits
      FDC->result[RES_ST2] = sector->flags[1] & 0x61; // copy ST2 to result, ignoring unused bits

      if ((FDC->flags & SKIP_flag) && (FDC->result[RES_ST2] &= 0x40)) // skip sector?
      {
        if (FDC->command[CMD_R] != FDC->command[CMD_EOT]) // continue looking?
        {
          FDC->command[CMD_R] += FDC->command[CMD_STP]; // advance to next sector
          continue;
        }
        else // no data to transfer -> no execution phase
        {
          LOAD_RESULT_WITH_STATUS;
          LOAD_RESULT_WITH_CHRN;

          FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
          FDC->signal_updated++;
          FDC->signal_command_result++;
          FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
        }
      }
      else // sector data is to be transferred
      {
        if (FDC->result[RES_ST2] &= 0x40) // does the sector have an AM opposite of what we want?
        {
          FDC->command[CMD_EOT] = FDC->command[CMD_R]; // execution ends on this sector
        }

        sector_size = 128 << FDC->command[CMD_N]; // determine number of bytes from N value

        FDC->buffer_count = sector_size; // init number of bytes to transfer
        FDC->buffer_ptr = sector->data; // pointer to sector data
        FDC->buffer_endptr = FDC->active_track->data + FDC->active_track->size; // pointer beyond end of track data
        FDC->flags &= ~SCANFAILED_flag; // reset scan failed flag
        FDC->result[RES_ST2] |= 0x08; // assume data matches: set Scan Equal Hit
        FDC->timeout = INITIAL_TIMEOUT;
        FDC->read_status_delay = READ_STATUS_DELAY;
      }
    }

    // sector not found
    else
    {
      FDC->result[RES_ST0] |= 0x40; // AT
      FDC->result[RES_ST1] |= 0x04; // No Data

      LOAD_RESULT_WITH_CHRN

      FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_command_result++;
      FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
    }

    loop = 0;
  }
  while (loop);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_cmd_write_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_cmd_write_fast
 *
 ***********************************************************************/
{
tSector *sector;
tLong sector_size;

  sector = fdc_find_sector_fast(FDC,
                                &FDC->command[CMD_C]); // locate the requested sector on the current track

  //
  // sector found
  //
  if (sector)
  {
    sector->flags[0] = 0; // clear ST1 for this sector

    if (FDC->command[CMD_CODE] == 0x45) // write data command?
    {
      sector->flags[1] = 0; // clear ST2
    }
    else // write deleted data
    {
      sector->flags[1] = 0x40; // set Control Mark
    }

    if (FDC->command[CMD_N] == 0) // use DTL for length?
    {
      sector_size = FDC->command[CMD_DTL]; // size of sector is defined by DTL value
      if (sector_size > 0x80)
      {
        sector_size = 0x80; // max DTL value is 128
      }
    }
    else
    {
      sector_size = 128 << FDC->command[CMD_N]; // determine number of bytes from N value
    }

    FDC->buffer_count = sector_size; // init number of bytes to transfer
    FDC->buffer_ptr = sector->data; // pointer to sector data
    FDC->buffer_endptr = FDC->active_track->data + FDC->active_track->size; // pointer beyond end of track data
    FDC->timeout = INITIAL_TIMEOUT;
    FDC->read_status_delay = READ_STATUS_DELAY;
  }

  // sector not found
  else
  {
    FDC->result[RES_ST0] |= 0x40; // AT
    FDC->result[RES_ST1] |= 0x04; // No Data

    LOAD_RESULT_WITH_CHRN;

    FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
    FDC->signal_updated++;
    FDC->signal_command_result++;
    FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
  }
}
/*----------------------------------------------------------------------------*/


static tULong fdc_init_status_regs_fast(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_init_status_regs_fast
 *
 ***********************************************************************/
{
tULong val;

  MemSetLong(FDC->result,
             NUMBER_OF_ITEMS(FDC->result),
             0); // clear result codes buffer

  val = FDC->command[CMD_UNIT] & 7; // keep head and unit of command

  if ((FDC->active_drive->tracks == 0) || (!FDC->motor)) // no DSK in the drive, or drive motor is turned off?
  {
     val |= 0x48; // Abnormal Termination + Not Ready
  }

  FDC->result[RES_ST0] = val; // write ST0 to result codes buffer

  return (val & 8); // return value indicates whether drive is ready (0) or not (8)
}
/*----------------------------------------------------------------------------*/


#define FDC_CHECK_UNIT \
{ \
tDrive* last_active_drive = FDC->active_drive; \
\
  FDC->active_drive = FDC->command[CMD_UNIT] & 1 ? FDC->DriveB : FDC->DriveA; \
  \
  /* Change of active drive */ \
  if (FDC->active_drive != last_active_drive) \
  { \
    /* Reset sector detection */ \
    \
    FDC->on_index_ptr = cNull; \
    FDC->on_sector_ptr = cNull; \
    FDC->head_on_track_ptr = cNull; \
    FDC->head_step_ptr = cNull; \
  } \
  \
  if ( (FDC->active_drive != cNull) \
       && (FDC->active_drive->tracks > 0) ) \
  { \
    tULong side; \
		FDC->active_drive->current_side = (FDC->command[CMD_UNIT] & 4) >> 2; \
		/*side = FDC->active_drive->sides ? FDC->active_drive->current_side : 0;*/ \
    \
    /* Double sided ? */ \
    if (FDC->active_drive->sides) \
    { \
      side = FDC->active_drive->current_side; \
      \
      /* did the user request to access the "other" side? */ \
      if (FDC->active_drive->flipped) \
      { \
        side = side ? 0 : 1; /* reverse the side to access */ \
      } \
    } \
    else \
    { \
      side = 0; \
    } \
    \
    FDC->active_track = &FDC->active_drive->track[FDC->active_drive->current_track][side]; \
  } \
  else \
  { \
    FDC->active_track = cNull; \
  } \
}

#define FDC_OVERRUN_CHECK \
{ \
  /* Overrun check */ \
  if (FDC->data_transfer_ack) \
  { \
    /* Reset transfer acknowledge */ \
    FDC->data_transfer_ack = 0; \
  } \
  else \
  { \
    /* Signal overrun */ \
    FDC->ST1_register |= ST1_OR; \
  } \
}


static tBool fdc_cmd_motor(tFDC* FDC, tULong val)
/***********************************************************************
 *
 *  fdc_cmd_motor
 *
 ***********************************************************************/
{
tBool Ret = cFalse;

  // state changed ?
  if (FDC->motor != (val & 0x01))
  {
    FDC->motor = val & 0x01;
    FDC->flags |= STATUSDRVA_flag | STATUSDRVB_flag;
    if (FDC->motor_state == FDC_MOTOR_STOPPING)
    {
      // Motor was stopping but still ready.
      FDC->motor_state = FDC_MOTOR_READY;
    }
    else if (FDC->motor_state == FDC_MOTOR_STARTING)
    {
      // Motor was starting but not ready yet.
      FDC->motor_state = FDC_MOTOR_STOPPED;
    }
    else
    {
      FDC->motor_state = val & 0x01 ? FDC_MOTOR_STARTING : FDC_MOTOR_STOPPING;
    }

    if (FDC->motor_state == FDC_MOTOR_STARTING)
    {
      // Motor starting delay before drive ready
      FDC->motor_start_stop_timer = FDC_MOTOR_START_DELAY;
      Ret = cTrue;
    }
    else if (FDC->motor_state == FDC_MOTOR_STOPPING)
    {
      // Motor stopping delay before drive not ready
      FDC->motor_start_stop_timer = FDC_MOTOR_STOP_DELAY;
    }

    // Abort operation
    if (FDC->on_abort_ptr)
    {
      FDC->on_abort_ptr(FDC);
    }
  }

  return (Ret);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_z80_wait_state(tFDC* FDC, tULong CycleCount)
/***********************************************************************
 *
 *  fdc_z80_wait_state
 *
 ***********************************************************************/
{
  /* Step motor pulse */
  if (FDC->head_step_ptr != cNull)
  {
    FDC->head_step_timer -= CycleCount;
    if (FDC->head_step_timer <= 0)
    {
      FDC->head_step_ptr(FDC);
    }
  }

  /* Most of the time, Motor is stopped. */
  if (FDC->motor_state == FDC_MOTOR_STOPPED)
  {
    FDC->led = 0; /* turn the drive LED off */

    // Head no longer move
    if (FDC->head_step_ptr)
    {
      FDC->flags |= (FDC->command[CMD_UNIT] & ST0_US0) ? SEEKDRVB_flag : SEEKDRVA_flag; // signal completion of seek operation
      FDC->ST0_register |= (ST0_AT); // Abnormal end
      FDC->active_drive->current_track = 0;
      FDC->head_step_ptr = cNull;
    }
    if (FDC->delayed_intstat)
    {
      fdc_intstat(FDC);
    }
  }
  /* Motor is ready */
  else if (FDC->motor_state == FDC_MOTOR_READY)
  {
    /* Disk index detection */
    FDC->index_timer -= CycleCount;
    if (FDC->index_timer <= 0)
		{
			FDC->from_index_timer = 0;
#ifndef FDC_REVOLUTION_VARIABLE
      FDC->index_timer += DISC_REVOLUTION;
#else /* FDC_REVOLUTION_VARIABLE */
			FDC->index_timer += DISC_REVOLUTION_VARIABLE - CLOCK_PER_US(CycleCount << 4);
			if (FDC->active_drive)
			{
        FDC->index_timer -= CLOCK_PER_US(FDC->active_drive->current_track << 5);
      }
#endif /* FDC_REVOLUTION_VARIABLE */

#ifdef ENABLE_METROLOGY
      FDC->signal_index++;
#endif /* ENABLE_METROLOGY */

      if (FDC->on_sector_ptr == cNull)
      {
        /* Signal index detected */
        if (FDC->on_index_ptr != cNull)
        {
          FDC->on_index_ptr(FDC);
        }

        if (FDC->multi_revolution_command == cFalse)
        {
          /* Track is formatted */
          if (    (FDC->active_track != cNull)
               && (FDC->active_track->sectors > 0) )
          {
            /* Prepare detection of first sector */
            FDC->sector_timer = FDC_DELAY_FROM_INDEX_TO_CHRN;
            FDC->on_sector_ptr = fdc_track_first_sector;
          }
        }
				else
				{
					FDC->multi_revolution_command = cFalse;
				}
			}
		}
		else
		{
			FDC->from_index_timer += CycleCount;
		}

    /* Next sector detection */
    if (FDC->on_sector_ptr != cNull)
    {
			FDC->sector_timer -= CycleCount;
      if (FDC->sector_timer <= 0)
      {
        FDC->on_sector_ptr(FDC);
      }
    }

    /* Head progress on current track */
    if (FDC->head_on_track_ptr != cNull)
    {
      FDC->head_on_track_timer -= CycleCount;
      if (FDC->head_on_track_timer <= 0)
      {
        FDC->head_on_track_ptr(FDC);
      }
    }
  }
  /* Motor is starting or stopping. */
  else
  {
    FDC->motor_start_stop_timer -= CycleCount;
    if (FDC->motor_start_stop_timer <= 0)
    {
      FDC->motor_state = (FDC->motor_state == FDC_MOTOR_STARTING) ? FDC_MOTOR_READY : FDC_MOTOR_STOPPED;
      FDC->index_timer = 0; /* Index will be detected at next cycle */
    }
  }
}
/*----------------------------------------------------------------------------*/


static tULong fdc_read_status(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_status
 *
 *  Read Main Status Register
 *
 *  D7: RQM = Request for Master
 *  D6: DIO = Data Input/Output
 *  D5: EXM = Execution mode
 *  D4: CB  = FDC Busy
 *  D3: D3B = FDD 3 Busy
 *  D2: D2B = FDD 2 Busy
 *  D1: D1B = FDD 1 Busy
 *  D0: D0B = FDD 0 Busy
 *
 ***********************************************************************/
{
#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_read_status++;
  FDC->read_status_value = FDC->main_status_register;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */

  return (FDC->main_status_register);
}
/*----------------------------------------------------------------------------*/


static tULong fdc_read_data(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_data
 *
 ***********************************************************************/
{
tULong val;

  //val = 0xff; // default value

  //
  // EXEC PHASE
  //
  if (FDC->phase == EXEC_PHASE)
  {
    val = FDC->data_register;
    FDC->data_transfer_ack = 1;
    
    // Disable transfer
    FDC->main_status_register &= ~(MSR_RQM);
  }
  //
  // RESULT PHASE
  //
  else if (FDC->phase == RESULT_PHASE)
  {
    val = FDC->result[FDC->byte_count++]; // copy value from buffer

    if (FDC->byte_count == FDC->res_length) // sent all result bytes?
    {
      FDC->byte_count = 0; // clear byte counter
      FDC->led = 0; // turn the drive LED off
      FDC->phase = CMD_PHASE; // switch to command phase
      FDC->main_status_register &= ~(MSR_RQM | MSR_DIO | MSR_EXM | MSR_CB);
      FDC->main_status_register |= (MSR_RQM);
    }
  }
  else // CMD_PHASE
  {
    val = fdc_get_status(FDC);
  }

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_read_data++;
  FDC->signal_data = val;
#endif /* ENABLE_METROLOGY */

  return (val);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_write_data(tFDC* FDC, tULong val)
/***********************************************************************
 *
 *  fdc_write_data
 *
 ***********************************************************************/
{
tULong idx;

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_write_data++;
  FDC->signal_data = val;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */

  //
  // CMD Phase
  //
  if ( (FDC->phase == CMD_PHASE) || (FDC->phase == RESULT_PHASE) )
  {
    // receiving command parameters?
    if (FDC->byte_count) 
    {
      FDC->command[FDC->byte_count++] = val; // copy to buffer

      if (FDC->byte_count == FDC->cmd_length) // received all command bytes?
      {
#ifdef ENABLE_METROLOGY
        FDC->signal_updated++;
        FDC->signal_new_command++;
#endif /* ENABLE_METROLOGY */

        FDC->byte_count = 0; // clear byte counter
        FDC->phase = EXEC_PHASE; // switch to execution phase

        FDC->cmd_handler(FDC);
      }
    }
    else // first command byte received
    {
      // loop through all known FDC commands
      for (idx = 0; idx < MAX_CMD_COUNT; idx++) 
      {
        // do we have a match?
        if ((FDC->FDCCommandTable[idx].cmd & 0x1f) == (val & CMD_OP_MASK))
        {
          break;
        }
      }

      if (val & CMD_SK)
      {
        FDC->flags |= SKIP_flag;
      }
      else
      {
        FDC->flags &= ~SKIP_flag;
      }

      if (val & CMD_MT)
      {
        FDC->flags |= MULTITRACK_flag;
      }
      else
      {
        FDC->flags &= ~MULTITRACK_flag;
      }

      if (idx != MAX_CMD_COUNT) // valid command received
      {
        FDC->cmd_index = idx;
        FDC->cmd_length = FDC->FDCCommandTable[idx].cmd_length; // command length in bytes
        FDC->res_length = FDC->FDCCommandTable[idx].res_length; // result length in bytes
        FDC->cmd_direction = FDC->FDCCommandTable[idx].cmd_direction; // direction is CPU to FDC, or FDC to CPU
        FDC->cmd_handler = FDC->FDCCommandTable[idx].cmd_handler; // pointer to command handler

        FDC->command[FDC->byte_count++] = val; // copy command code to buffer

        FDC->main_status_register |= (MSR_CB); // FDC becomes busy

        if (FDC->byte_count == FDC->cmd_length) // 1 byte command requested ?
        {
#ifdef ENABLE_METROLOGY
          FDC->signal_updated++;
          FDC->signal_new_command++;
#endif /* ENABLE_METROLOGY */

          FDC->byte_count = 0; // clear byte counter
          FDC->phase = EXEC_PHASE; // switch to execution phase
          FDC->main_status_register &= ~(MSR_RQM);
          FDC->main_status_register |= (MSR_EXM);
          FDC->cmd_handler(FDC);
        }
      }
      else // unknown command received
      {
        FDC->cmd_index = idx;
        FDC->result[RES_ST0] = 0x80; // indicate invalid command
        FDC->res_length = 1;
        FDC->phase = RESULT_PHASE; // switch to result phase
        FDC->main_status_register &= ~(MSR_RQM | MSR_DIO | MSR_EXM);
        FDC->main_status_register |= (MSR_RQM | MSR_DIO | MSR_CB);
        FDC->ST0_register &= ~ST0_NT_MASK;
        FDC->ST0_register |= ST0_IC;
        
#ifdef ENABLE_METROLOGY
        FDC->command[0] = val; // copy command code to buffer
        FDC->signal_updated++;
        FDC->signal_new_command++;
        FDC->signal_command_result++;
#endif /* ENABLE_METROLOGY */
      }
    }
  }
  //
  // EXEC phase
  //
  else if (FDC->phase == EXEC_PHASE)
  {
    FDC->data_register = val;
    FDC->data_transfer_ack = 1;
    
    // Disable transfer
    FDC->main_status_register &= ~(MSR_RQM);
    
    // Signal data register has been updated
    if (FDC->on_data_register_ptr != cNull)
    {
      FDC->on_data_register_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_specify(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_specify
 *
 *  Specify
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W    0   0   0   0   0   0   1   1 Command codes
 *            W  <---- SRT ----> <---- HUT ---->
 *            W  <---------- HLT ---------->  ND
 *
 ***********************************************************************/
{
  //FDC->phase = CMD_PHASE; // switch back to command phase (fdc_specify has no result phase!)

  // Reset Status
  FDC->ST0_register = FDC->command[CMD_UNIT] & (ST0_HD | ST0_US1 | ST0_US0);

  FDC->step_rate_time = CLOCK_PER_MS((16 - (FDC->command[CMD_UNIT] >> 4)) * 2);

  // Update Status
  FDC->phase = CMD_PHASE; // switch back to command phase (fdc_seek has no result phase!)
  FDC->main_status_register &= ~(MSR_RQM | MSR_DIO | MSR_EXM | MSR_CB);
  FDC->main_status_register |= (MSR_RQM);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_drvstat(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_drvstat
 *
 *  Sense Drive Status
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W    0   0   0   0   0   1   0   0 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *  Result    R  <------------ ST3 ------------> Status information about FDD
 *
 ***********************************************************************/
{
  // Prepare Result
  FDC->ST3_register = fdc_get_status(FDC);
  FDC->result[RES_ST0] = FDC->ST3_register;

  FDC->phase = RESULT_PHASE; // switch to result phase
  FDC->main_status_register &= ~(MSR_RQM | MSR_DIO | MSR_EXM);
  FDC->main_status_register |= (MSR_RQM | MSR_DIO);

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tULong fdc_get_status(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_get_status
 *
 ***********************************************************************/
{
tULong val;

  // Set drive selected
	FDC_CHECK_UNIT;

  val = FDC->command[CMD_UNIT] & (ST3_HD | ST3_US1 | ST3_US0);
  
  if (FDC->active_drive)
  {
#ifndef FDC_INVERT_ST3_TS
    if (FDC->active_drive->sides)
#else /* FDC_INVERT_ST3_TS */
    if (!FDC->active_drive->sides)
#endif /* FDC_INVERT_ST3_TS */
    {
      val |= ST3_TS;
    }

    if ( (FDC->active_drive->write_protected)
				 && (FDC->active_drive->tracks) )
		{
			val |= ST3_WP;
		}
		else if (!FDC->active_drive->tracks)
		{
			val |= ST3_WP;
		}

    if ( ( (FDC->motor_state == FDC_MOTOR_READY)
           || (FDC->motor_state == FDC_MOTOR_STOPPING) )
         && (FDC->active_drive->tracks) )
    {
      val |= ST3_RY; // set Ready
    }

    if ( (FDC->active_drive->tracks)
         && (FDC->active_drive->current_track == 0) )
    {
      val |= ST3_T0;
    }
  }

  return val;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_recalib(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_recalib
 *
 *  Recalibrate
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W    0   0   0   0   0   1   1   1 Command codes
 *            W    X   X   X   X   X   0 US1 US0
 *  Execution                                    Head Retracted to track 0
 *
 ***********************************************************************/
{
  // Set drive selected
  FDC_CHECK_UNIT;
  // Reset Status
  FDC->ST0_register = FDC->command[CMD_UNIT] & (ST0_HD | ST0_US1 | ST0_US0);
  // Update Status
  FDC->phase = CMD_PHASE; // switch back to command phase (fdc_recalib has no result phase!)
  FDC->main_status_register &= ~(MSR_RQM | MSR_DIO | MSR_EXM | MSR_CB);
  // clear status change flags
  FDC->flags &= ~(STATUSDRVA_flag | STATUSDRVB_flag | SEEKMAX_flag);

  // Check Disk presence
  if ( (FDC->active_drive != cNull)
       && (FDC->active_drive->tracks > 0) )
  {
    FDC->active_drive->dest_track = 0;

    // drive motor not ready ?
    if (FDC->motor_state != FDC_MOTOR_READY)
    {
      FDC->ST0_register |= (ST0_AT | ST0_NR); // not ready
    }

    if ( (FDC->active_drive->dest_track != FDC->active_drive->current_track)
         && (FDC->motor_state == FDC_MOTOR_READY) )
    {
      // Move head step by step
      FDC->head_step_timer = FDC->step_rate_time;
      FDC->head_step_ptr = fdc_head_step;

      // Reset max steps detection
      FDC->recalibsteps = FDC_RECALIB_MAX_STEPS;

      // Update status
      FDC->main_status_register |= (MSR_RQM);
      FDC->main_status_register |= (FDC->command[CMD_UNIT] & ST0_US0) ? MSR_D1B : MSR_D0B;

      // Get number of steps
      FDC->headsteps = FDC->active_drive->current_track;
    }
    else
    {
      if (FDC->active_drive->dest_track == FDC->active_drive->current_track)
      {
        FDC->head_step_ptr = cNull;
      }

      FDC->active_drive->current_track = 0;

      // FDC Busy ?
      if ( (FDC->motor_state != FDC_MOTOR_READY) && (FDC->motor_state != FDC_MOTOR_STARTING) )
      {
        FDC->main_status_register |= (MSR_CB);
      }

      // Update status
      FDC->main_status_register |= (MSR_RQM);
      FDC->main_status_register |= (FDC->command[CMD_UNIT] & ST0_US0) ? MSR_D1B : MSR_D0B;

      // Already at destination
      FDC->flags |= (FDC->command[CMD_UNIT] & ST0_US0) ? SEEKDRVB_flag : SEEKDRVA_flag; // signal completion of seek operation

#ifdef ENABLE_METROLOGY
      FDC->signal_updated++;
      FDC->signal_track++;
#endif /* ENABLE_METROLOGY */
    }
  }
  else
  {
    // drive motor not ready
    FDC->ST0_register |= (ST0_AT | ST0_NR);

    // Update status
    FDC->main_status_register |= (MSR_RQM);
    FDC->main_status_register |= (FDC->command[CMD_UNIT] & ST0_US0) ? MSR_D1B : MSR_D0B;

    // signal completion of seek operation
    FDC->flags |= (FDC->command[CMD_UNIT] & ST0_US0) ? SEEKDRVB_flag : SEEKDRVA_flag;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_intstat(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_intstat
 *
 *  Sense Interrupt Status
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W    0   0   0   0   1   0   0   0 Command codes
 *  Result    R  <------------ ST0 ------------> Status information about the FDC at the end of seek operation
 *            R  <------------ PCN ------------>
 *
 ***********************************************************************/
{
tULong val;

  FDC->delayed_intstat = 0;

  // No SEEK or RECALIB in progress
  if (FDC->head_step_ptr == cNull)
  {
    // clear Head Address and Unit bits
    val = FDC->ST0_register & ~(ST0_HD | ST0_US1 | ST0_US0);

    //
    // seek completed on drive A?
    //
    if (FDC->flags & SEEKDRVA_flag)
    {
      val |= (ST0_SE); // set Seek End
      FDC->flags &= ~(SEEKDRVA_flag | STATUSDRVA_flag); // clear seek done and status change flags

      // Max recalib steps ?
      if (FDC->flags & SEEKMAX_flag)
      {
        FDC->flags &= ~(SEEKMAX_flag);
        // Abnormal end
        val |= (ST0_AT | ST0_EC);
      }

      FDC->result[RES_ST0] = val;
      FDC->result[RES_ST1] = FDC->DriveA->current_track; // PCN

      // Update Status
      FDC->main_status_register &= ~(MSR_D0B);
    }
    //
    // seek completed on drive B?
    //
    else if (FDC->flags & SEEKDRVB_flag)
    {
      val |= (ST0_SE | ST0_US0); // set Seek End
      FDC->flags &= ~(SEEKDRVB_flag | STATUSDRVB_flag); // clear seek done and status change flags

      // Max recalib steps ?
      if (FDC->flags & SEEKMAX_flag)
      {
        FDC->flags &= ~(SEEKMAX_flag);
        // Abnormal end
        //val &= ~(ST0_NT_MASK);
        val |= (ST0_AT | ST0_EC);
      }

      FDC->result[RES_ST0] = val;
      FDC->result[RES_ST1] = FDC->DriveB->current_track; // PCN

      // Update Status
      FDC->main_status_register &= ~(MSR_D1B);
    }
    //
    // has the status of drive A changed?
    //
    else if (FDC->flags & STATUSDRVA_flag)
    {
      val = ST0_ATR; // status change

      // no DSK in the drive ?
      if (FDC->DriveA->tracks == 0)
      {
        val |= ST0_NR; // not ready
      }
      // drive motor is turned off ?
      else if ( (FDC->motor_state != FDC_MOTOR_READY)
                && (FDC->motor_state != FDC_MOTOR_STOPPING) )
      {
        val |= ST0_NR; // not ready
      }

      FDC->flags &= ~(STATUSDRVA_flag); // clear status change flag
      FDC->result[RES_ST0] = val;
      FDC->result[RES_ST1] = FDC->DriveA->current_track; // PCN
    }
    //
    // has the status of drive B changed?
    //
    else if (FDC->flags & STATUSDRVB_flag)
    {
      val = (ST0_ATR | ST0_US0); // status change

      // no DSK in the drive ?
      if (FDC->DriveB->tracks == 0)
      {
        val |= ST0_NR; // not ready
      }
      // drive motor is turned off ?
      else  if ( (FDC->motor_state != FDC_MOTOR_READY)
                 && (FDC->motor_state != FDC_MOTOR_STOPPING) )
      {
        val |= ST0_NR; // not ready
      }

      FDC->flags &= ~(STATUSDRVB_flag); // clear status change flag
      FDC->result[RES_ST0] = val;
      FDC->result[RES_ST1] = FDC->DriveB->current_track; // PCN
    }
    else
    {
      // No interrupt
      FDC->result[RES_ST0] = (ST0_IC);
      FDC->res_length = 1;
    }
  }
  else
  {
    FDC->delayed_intstat = 1;
  }

  if (!FDC->delayed_intstat)
  {
    // Update Status
    FDC->main_status_register &= ~(MSR_RQM | MSR_DIO | MSR_EXM);
    FDC->main_status_register |= (MSR_RQM | MSR_DIO);

    // Prepare result
    FDC->phase = RESULT_PHASE; // switch to result phase

#ifdef ENABLE_METROLOGY
    FDC->signal_updated++;
    FDC->signal_command_result++;
    FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
  }
  else
  {
    FDC->data_register = 0xff;
    FDC->phase = EXEC_PHASE;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_seek(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_seek
 *
 *  Seek
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W    0   0   0   0   1   1   1   1 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------ NCN ------------>
 *  Execution                                    Head is positionned over proper cylinder on diskette
 *
 ***********************************************************************/
{
  // Set drive selected
  FDC_CHECK_UNIT;
  // Reset Status
  FDC->ST0_register = FDC->command[CMD_UNIT] & (ST0_HD | ST0_US1 | ST0_US0);
  // Update Status
  FDC->phase = CMD_PHASE; // switch back to command phase (fdc_seek has no result phase!)
  FDC->main_status_register &= ~(MSR_RQM | MSR_DIO | MSR_EXM | MSR_CB);
  FDC->main_status_register |= (FDC->command[CMD_UNIT] & ST0_US0) ? MSR_D1B : MSR_D0B;
  // clear status change flags
  FDC->flags &= ~(STATUSDRVA_flag | STATUSDRVB_flag | SEEKMAX_flag);

  if ( (FDC->active_drive != cNull)
       && (FDC->active_drive->tracks > 0) )
  {
		FDC->active_drive->dest_track = FDC->command[CMD_C];

		// Destination track saturation
		if (FDC->active_drive->dest_track >= DSK_TRACKMAX) // beyond valid range?
		{
			FDC->active_drive->dest_track = DSK_TRACKMAX-1; // limit to maximum
		}

    // drive motor not ready ?
    if (FDC->motor_state != FDC_MOTOR_READY)
    {
      FDC->ST0_register |= (ST0_NR); // not ready
    }

		if (FDC->active_drive->dest_track != FDC->active_drive->current_track)
		{
			// Move head step by step
			FDC->head_step_timer = FDC->step_rate_time;
			FDC->head_step_ptr = fdc_head_step;

			// No recalib step detection
			FDC->recalibsteps = 0;

			// Update status
			FDC->main_status_register |= (MSR_RQM);

			// Get number of steps
			if (FDC->active_drive->dest_track > FDC->active_drive->current_track)
			{
				FDC->headsteps = FDC->active_drive->dest_track - FDC->active_drive->current_track;
			}
			else
			{
				FDC->headsteps = FDC->active_drive->current_track - FDC->active_drive->dest_track;
			}
		}
		else
    {
      FDC->head_step_ptr = cNull;

      // Update status
      FDC->main_status_register |= (MSR_RQM);

      // Already at destination
      FDC->flags |= (FDC->command[CMD_UNIT] & ST0_US0) ? SEEKDRVB_flag : SEEKDRVA_flag; // signal completion of seek operation
    }
  }
  else
  {
    // drive motor not ready
    FDC->ST0_register |= (ST0_AT | ST0_NR);

    // Update status
    FDC->main_status_register |= (MSR_RQM);

    // No Disk
    FDC->flags |= (FDC->command[CMD_UNIT] & ST0_US0) ? SEEKDRVB_flag : SEEKDRVA_flag; // signal completion of seek operation
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readtrk(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readtrk
 *
 *  Read A Track
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W    0  MF  SK   0   0   0   1   0 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------  C  ------------>
 *            W  <------------  H  ------------>
 *            W  <------------  R  ------------>
 *            W  <------------  N  ------------>
 *            W  <------------ EOT ------------>
 *            W  <------------ GPL ------------>
 *            W  <------------ DTL ------------>
 *  Execution                                    Data transfer between the FDD and main system.
 *                                               FDC reads all data fields from index hole to EOT.
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> Sector ID information after command completion
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 ***********************************************************************/
{
  FDC->led = 1; // turn the drive LED on

  // Set drive selected
  FDC_CHECK_UNIT;
  // Reset status
  FDC->ST0_register = FDC->command[CMD_UNIT] & (ST0_HD | ST0_US1 | ST0_US0);
  FDC->ST1_register = 0;
  FDC->ST2_register = 0;
  // clear status change flags
  FDC->flags &= ~(STATUSDRVA_flag | STATUSDRVB_flag);

  // Disk is present
  if ( (FDC->active_drive->tracks)
       && (FDC->motor_state == FDC_MOTOR_READY) )
  {
    // Update Status
    FDC->main_status_register &= ~(MSR_RQM);
    FDC->main_status_register |= (MSR_EXM);

    // Prepare detection of next index.
    FDC->phase = EXEC_PHASE; // switch to execution phase
    FDC->index_count = 0;

    FDC->sector_read_count = FDC->command[CMD_EOT];
    if (FDC->flags & MULTITRACK_flag)
    {
      FDC->sector_read_count *= 2;
    }
    if (!FDC->sector_read_count)
    {
      FDC->sector_read_count = 256;
    }

    FDC->on_abort_ptr = fdc_readtrack_abort;
    FDC->on_index_ptr = fdc_readtrack_index;
  }
  else // drive was not ready
  {
    // Update status
    FDC->ST0_register |= (ST0_AT | ST0_NR);

    // Prepare result
    fdc_readtrack_result(FDC);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_write(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_write
 *
 *  Write Data
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W   MT  MF   0   0   0   1   0   1 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------  C  ------------> Sector ID information prior to command execution.
 *            W  <------------  H  ------------> The 4 bytes are compared against header on floppy disk.
 *            W  <------------  R  ------------>
 *            W  <------------  N  ------------>
 *            W  <------------ EOT ------------>
 *            W  <------------ GPL ------------>
 *            W  <------------ DTL ------------>
 *  Execution                                    Data transfer between the main system and FDD
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> Sector ID information after command execution
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 *  Write Deleted Data
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W   MT  MF   0   0   1   0   0   1 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------  C  ------------> Sector ID information prior to command execution.
 *            W  <------------  H  ------------> The 4 bytes are compared against header on floppy disk.
 *            W  <------------  R  ------------>
 *            W  <------------  N  ------------>
 *            W  <------------ EOT ------------>
 *            W  <------------ GPL ------------>
 *            W  <------------ DTL ------------>
 *  Execution                                    Data transfer between the main system and FDD
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> Sector ID information after command execution
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 ***********************************************************************/
{
tUChar sector;

  FDC->led = 1; // turn the drive LED on

  // Set drive selected
  FDC_CHECK_UNIT;
  // Reset status
  FDC->ST0_register = FDC->command[CMD_UNIT] & (ST0_HD | ST0_US1 | ST0_US0);
  FDC->ST1_register = 0;
  FDC->ST2_register = 0;
  // clear status change flags
  FDC->flags &= ~(STATUSDRVA_flag | STATUSDRVB_flag);

	// Disk is present
  if ( (FDC->active_track)
       && ( (FDC->motor_state == FDC_MOTOR_READY)
            || (FDC->motor_state == FDC_MOTOR_STARTING) ) )
	{
		if (FDC->active_track->sectors) // track is formatted?
		{
			if (FDC->active_drive->write_protected) // is write protect tab set?
      {
        // Update status
        FDC->ST0_register |= (ST0_AT);
        FDC->ST1_register |= (ST1_NW); // Non writable

        // Prepare result
        fdc_write_result(FDC);
      }
      else
      {
        // Update Status
        FDC->main_status_register &= ~(MSR_RQM);

        // Prepare write
        if (FDC->command[CMD_N] == 0) // use DTL for length?
        {
          FDC->requested_sector_size = FDC->command[CMD_DTL]; // size of sector is defined by DTL value

          if (FDC->requested_sector_size > 0x80)
          {
            FDC->requested_sector_size = 0x80; // max DTL value is 128
          }
        }
        else
        {
          FDC->requested_sector_size = 128 << FDC->command[CMD_N]; // determine number of bytes from N value
        }

        // Create declared data
        for (sector = 0; sector < (tUChar)FDC->active_track->sectors; sector++) // loop for all sectors
        {
          tSector* active_sector = &FDC->active_track->sector[sector];
          if (    (FDC->command[CMD_C] == (tULong)active_sector->CHRN[SECTOR_CHRN_C])
               && (FDC->command[CMD_H] == (tULong)active_sector->CHRN[SECTOR_CHRN_H])
               && (FDC->command[CMD_R] == (tULong)active_sector->CHRN[SECTOR_CHRN_R])
               && (FDC->command[CMD_N] == (tULong)active_sector->CHRN[SECTOR_CHRN_N]) )
          {
            if (FDC->requested_sector_size == active_sector->declared_size)
            {
              active_sector->data_size = active_sector->declared_size;
              if (active_sector->data_size > FDC_MAX_BYTES_PER_SECTOR)
              {
                active_sector->data_size = FDC_MAX_BYTES_PER_SECTOR;
              }

              // Delete overwritten sectors.
              if (active_sector->size < active_sector->data_size)
              {
                tUChar nb_sectors_to_delete = 0;
                tUChar loop = sector + 1;
                tUChar deleted;
                tLong size = active_sector->data_size - active_sector->size;

                // Get number of sectors to delete
                while ( (size > 0) && (loop < (tUChar)FDC->active_track->sectors) )
                {
                  size -= FDC->active_track->sector[loop].size;
                  nb_sectors_to_delete++;
                  loop++;
                }

                // Remove sectors
                for (deleted = sector + nb_sectors_to_delete +1;
                     deleted < (tUChar)FDC->active_track->sectors;
                     deleted++)
                {
                  MemMoveByte((tUChar*)&FDC->active_track->sector[deleted - nb_sectors_to_delete],
                              (const tUChar*)&FDC->active_track->sector[deleted],
                              sizeof(tSector));
                }

                // Update number of sectors
                active_sector->size = active_sector->data_size;
                FDC->active_track->sectors -= nb_sectors_to_delete;
              }
            }

            if (active_sector->declared_data != cNull)
            {
              FDC->DeleteMemFct(active_sector->declared_data);
              active_sector->declared_data = cNull;
            }

            if (active_sector->data_size != active_sector->declared_size)
            {
              active_sector->declared_data = FDC->NewMemFct(active_sector->declared_size);
            }
          }
        }

        // Seek first sector
        FDC->phase = EXEC_PHASE; // switch to execution phase
        FDC->index_count = 0;
        FDC->on_index_ptr = fdc_write_index;
        FDC->on_sector_chrn_ptr = fdc_write_sector;
        FDC->on_abort_ptr = fdc_write_abort;
        if ( (FDC->on_sector_ptr != cNull)
             && (FDC->on_sector_ptr != fdc_track_first_sector) )
        {
          fdc_write_sector(FDC);
        }
      }
    }
    else // Not formatted
    {
      // Update status
      FDC->ST0_register |= (ST0_AT);
      FDC->ST1_register |= (ST1_MA);
      FDC->ST2_register |= (ST2_MD);

      // Prepare result
      fdc_write_result(FDC);
    }
  }
  else // drive was not ready
  {
    // Update status
    FDC->ST0_register |= (ST0_AT | ST0_NR);

    // Prepare result
    fdc_write_result(FDC);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_read(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read
 *
 *  Read Data
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W   MT  MF  SK   0   0   1   1   0 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------  C  ------------> Sector ID Information prior to command execution.
 *            W  <------------  H  ------------> The 4 bytes are compared against header on floppy disk.
 *            W  <------------  R  ------------>
 *            W  <------------  N  ------------>
 *            W  <------------ EOT ------------>
 *            W  <------------ GPL ------------>
 *            W  <------------ DTL ------------>
 *  Execution                                    Data transfer between the FDD and main system.
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> Sector ID information after command completion
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 *  Read Deleted Data
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W   MT  MF  SK   0   1   1   0   0 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------  C  ------------> Sector ID Information prior to command execution.
 *            W  <------------  H  ------------> The 4 bytes are compared against header on floppy disk.
 *            W  <------------  R  ------------>
 *            W  <------------  N  ------------>
 *            W  <------------ EOT ------------>
 *            W  <------------ GPL ------------>
 *            W  <------------ DTL ------------>
 *  Execution                                    Data transfer between the FDD and main system.
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> Sector ID information after command completion
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 ***********************************************************************/
{
  FDC->led = 1; // turn the drive LED on

  // Set drive selected
  FDC_CHECK_UNIT;
  // Reset status
  FDC->ST0_register = FDC->command[CMD_UNIT] & (ST0_HD | ST0_US1 | ST0_US0);
  FDC->ST1_register = 0;
  FDC->ST2_register = 0;
  // clear status change flags
  FDC->flags &= ~(STATUSDRVA_flag | STATUSDRVB_flag);

  if ((FDC->command[CMD_CODE] & CMD_OP_MASK) == CMD_OP_READ_DELETED)
  {
    FDC->flags |= DELETED_flag;
  }
  else
  {
    FDC->flags &= ~DELETED_flag;
  }

  // Disk is present
  if ( (FDC->active_track)
       && ( (FDC->motor_state == FDC_MOTOR_READY)
            || (FDC->motor_state == FDC_MOTOR_STARTING) ) )
  {
    // track is formatted and MFM ?
    if ( (FDC->active_track->sectors)
         && (FDC->command[CMD_CODE] & CMD_MF) )
    {
      // Update Status
      FDC->main_status_register &= ~(MSR_RQM);
      FDC->ST0_register &= ~(ST0_NR);


      // Seek first sector
      FDC->phase = EXEC_PHASE; // switch to execution phase
      FDC->index_count = 0;
      FDC->on_index_ptr = fdc_read_index;
      FDC->on_sector_chrn_ptr = fdc_read_sector;
      FDC->on_abort_ptr = fdc_read_abort;
    }
    else // Not formatted
    {
      // Update status
      FDC->ST0_register |= (ST0_AT);
      FDC->ST1_register |= (ST1_MA);

      // Prepare result
      fdc_read_result(FDC);
    }
  }
  else // drive was not ready
  {
    // Update status
    FDC->ST0_register |= (ST0_AT | ST0_NR);

    // Prepare result
    fdc_read_result(FDC);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readID(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readID
 *
 *  Read ID
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W    0  MF   0   0   1   0   1   0 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *  Execution                                    The first correct ID information on the cylinder is
 *                                               stored in data register.
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> Sector ID information after command completion
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 ***********************************************************************/
{
  FDC->led = 1; // turn the drive LED on

  // Set drive selected
  FDC_CHECK_UNIT;
  // Reset Status
  FDC->ST0_register = FDC->command[CMD_UNIT] & (ST0_HD | ST0_US1 | ST0_US0);
  FDC->ST1_register = 0;
  FDC->ST2_register = 0;
  // clear status change flags
  FDC->flags &= ~(STATUSDRVA_flag | STATUSDRVB_flag);

  // Drive ready ?
  if ( (FDC->active_track)
       && ( (FDC->motor_state == FDC_MOTOR_READY)
            || (FDC->motor_state == FDC_MOTOR_STARTING) ) )
  {
    // Prepare detection of next sector.
    FDC->index_count = 0;
    FDC->on_index_ptr = fdc_readid_index;
    FDC->on_sector_chrn_ptr = fdc_readid_sector_chrn;
    FDC->on_abort_ptr = fdc_readid_abort;
    FDC->phase = EXEC_PHASE; // switch to execution phase
    FDC->main_status_register &= ~(MSR_RQM);
    FDC->main_status_register |= (MSR_EXM);
  }
  else
  {
    // Update status
    FDC->ST0_register |= (ST0_AT | ST0_NR);

    // Prepare result
    fdc_readid_result(FDC);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format
 *
 *  Format A Track
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W    0  MF   0   0   1   1   0   1 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------  N  ------------> Bytes / sector
 *            W  <------------  SC ------------> Sectors / Track
 *            W  <------------ GPL ------------> Gap 3
 *            W  <------------  D  ------------> Filler Byte
 *  Execution                                    FDC formats entire track
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> In this case, this ID information has no meaning
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 ***********************************************************************/
{
tUChar* dataP;
tULong dwTrackHeaderSize;
tULong dwPreviousTrackHeaderSize;
tULong dwPreviousTrackSize;
tULong dwSectorSize;
tUChar sector;

  FDC->led = 1; // turn the drive LED on

  // Set drive selected
  FDC_CHECK_UNIT;
	// Reset status
  FDC->ST0_register = FDC->command[CMD_UNIT] & (ST0_HD | ST0_US1 | ST0_US0);
  FDC->ST1_register = 0;
  FDC->ST2_register = 0;
  // clear status change flags
  FDC->flags &= ~(STATUSDRVA_flag | STATUSDRVB_flag);

  // Drive Not Ready ?
  if ( (!FDC->active_track)
       || (FDC->motor_state != FDC_MOTOR_READY) )
  {
    // Update status
    FDC->ST0_register |= (ST0_AT | ST0_NR);

    // Prepare result
    FDC->result[RES_C]   = 0x00;
    FDC->result[RES_H]   = 0x04;
    FDC->result[RES_R]   = 0x00; // No track formatted
    FDC->result[RES_N]   = FDC->command[CMD_C];
    fdc_format_result(FDC);
  }
  // Is write protect tab set?
  else if (FDC->active_drive->write_protected)
  {
    // Update status
    FDC->ST0_register |= (ST0_AT);
    FDC->ST1_register |= (ST1_NW); // Non writable

    // Prepare result
    fdc_format_result(FDC);
  }
  else
  {
    // Get previous track data
    dwPreviousTrackSize = FDC->active_track->size;
    dwPreviousTrackHeaderSize = (FDC->active_track->sectors) ? DSK_TRACKHEADER_SIZE : 0;
    if (FDC->active_track->sectors > DSK_SECTORMAX)
    {
      sector = FDC->active_track->sectors - DSK_SECTORMAX;
      sector = ((sector / DSK_NBSECTORPERHEADER) + 1) * DSK_NBSECTORPERHEADER;
      dwPreviousTrackHeaderSize += sector * DSK_SECTORID_SIZE;
    }

    // Free already formatted track content
    if (FDC->active_track->new_data != cNull)
    {
      FDC->DeleteMemFct(FDC->active_track->new_data);

      // Restore original track
      FDC->active_track->new_data = cNull;
      FDC->active_track->new_size = 0;
    }

    // Free declared data
    for (sector = 0; sector < (tUChar)FDC->active_track->sectors; sector++) // loop for all sectors
    {
      tSector* active_sector = &FDC->active_track->sector[sector];
      if (active_sector->declared_data != cNull)
      {
        FDC->DeleteMemFct(active_sector->declared_data);
        active_sector->declared_data = cNull;
      }
    }

    //
    // Prepare new track format
    //
    FDC->active_track->sectors = FDC->command[CMD_H];
    FDC->active_track->gap3_length = FDC->command[CMD_R];
    FDC->active_track->filler = FDC->command[CMD_N];
    dwSectorSize = 128 << (FDC->command[CMD_C] & 7);
    if (dwSectorSize < FDC_MAX_BYTES_PER_SECTOR) // use size 6 sector to unformat track
    {
      // Compute new track size
      tULong real_sector_size =   FDC_BYTES_FROM_GAP1_TO_CHRN
                                + FDC_BYTES_FROM_CHRN_TO_DATA
                                + dwSectorSize
                                + FDC_BYTES_CRC + FDC->active_track->gap3_length;
      tULong real_track_size = real_sector_size * FDC->active_track->sectors;
      FDC->active_track->first_sector_overwritten = 0;
      if (real_track_size < FDC_MAX_BYTES_PER_SECTOR)
      {
        FDC->active_track->new_size = ROUND_UPPER_256(FDC_MAX_BYTES_PER_SECTOR);
      }
      else // Oversize format requested
      {
        tULong real_nb_sectors;

        // Determine real number of sectors
        real_nb_sectors = FDC_MAX_BYTES_PER_SECTOR / real_sector_size;
        if ( (FDC->active_track->sectors * real_sector_size) > FDC_MAX_BYTES_PER_SECTOR)
        {
          real_nb_sectors++;
        }

        // First sector overwritten ?
        if ((real_track_size - FDC_MAX_BYTES_PER_SECTOR) > FDC_BYTES_FROM_INDEX_TO_GAP1)
        {
          FDC->active_track->first_sector_overwritten++;
          real_nb_sectors--;
        }

        FDC->active_track->sectors = real_nb_sectors;
        FDC->active_track->new_size = ROUND_UPPER_256(dwSectorSize * FDC->active_track->sectors);
      }
    }
    else
    {
      FDC->active_track->new_size = 0; // unformat track
    }

    if (FDC->active_track->new_size)
    {
      // Allocate memory for new track
      FDC->active_track->new_data = FDC->NewMemFct(FDC->active_track->new_size);
      if (FDC->active_track->new_data == cNull)
      {
        // Stop format, simulate disk not writable
        FDC->ST0_register |= (ST0_AT);
        FDC->ST1_register |= (ST1_NW);

        // Prepare result
        fdc_format_result(FDC);
      }
      else
      {
        // Use new track
        FDC->active_track->data = FDC->active_track->new_data;
        FDC->active_track->size = FDC->active_track->new_size;
        FDC->active_track->real_size = FDC_BYTES_FROM_INDEX_TO_GAP1;

        // Prepare all sectors data
        dataP = FDC->active_track->data;
        for (sector = 0; sector < (tUChar)FDC->active_track->sectors; sector++) // loop for all sectors
        {
          // Set default sector data
          tSector* active_sector = &FDC->active_track->sector[sector];
          active_sector->size = dwSectorSize;
          active_sector->declared_size = dwSectorSize;
          active_sector->data_size = dwSectorSize;
          active_sector->weak_count = 1;
          active_sector->data_gap3_length = 0;
          active_sector->info_gap3_length = FDC->active_track->gap3_length;
          active_sector->flags[0] = 0x00;
          active_sector->flags[1] = 0x00;
          active_sector->data = dataP;

          dataP += active_sector->declared_size;

          // Update track real size
          FDC->active_track->real_size += FDC_BYTES_FROM_GAP1_TO_CHRN + FDC_BYTES_FROM_CHRN_TO_DATA;
          active_sector->track_offset = FDC->active_track->real_size;
          FDC->active_track->real_size += dwSectorSize + active_sector->info_gap3_length;
        }

        dwTrackHeaderSize = DSK_TRACKHEADER_SIZE;
        if (FDC->active_track->sectors > DSK_SECTORMAX)
        {
          sector = FDC->active_track->sectors - DSK_SECTORMAX;
          sector = ((sector / DSK_NBSECTORPERHEADER) + 1) * DSK_NBSECTORPERHEADER;
          dwTrackHeaderSize += sector * DSK_SECTORID_SIZE;
        }

        // Update drive size
        if (FDC->active_drive->new_data_size == 0)
        {
          FDC->active_drive->new_data_size = FDC->active_drive->data_size;
        }
        FDC->active_drive->new_data_size =   FDC->active_drive->new_data_size
                                           - dwPreviousTrackSize
                                           - dwPreviousTrackHeaderSize
                                           + FDC->active_track->size
                                           + dwTrackHeaderSize;
        // Update drive tracks count
        if ((FDC->active_drive->current_track+1) > FDC->active_drive->tracks)
        {
          FDC->active_drive->tracks = FDC->active_drive->current_track+1;
        }

        FDC->active_drive->altered = 1; // indicate that the image has been modified

        // Update Status
        FDC->main_status_register &= ~(MSR_RQM);
        FDC->main_status_register |= (MSR_EXM);

        // Prepare detection of first sector.
        FDC->phase = EXEC_PHASE; // switch to execution phase
        FDC->index_count = 0;
        FDC->on_abort_ptr = fdc_format_abort;
        FDC->on_index_ptr = fdc_format_index;
      }
    }
    else
    {
      // remove track format
      FDC->active_track->sectors = 0;
      FDC->active_track->data = cNull;
      FDC->active_track->size = 0;

      FDC->active_drive->altered = 1; // indicate that the image has been modified

      // Update drive size
      if (FDC->active_drive->new_data_size == 0)
      {
        FDC->active_drive->new_data_size = FDC->active_drive->data_size;
      }
      FDC->active_drive->new_data_size =   FDC->active_drive->new_data_size
                                         - dwPreviousTrackSize
                                         - dwPreviousTrackHeaderSize;

      // Prepare detection of first sector.
      FDC->phase = EXEC_PHASE; // switch to execution phase
      FDC->index_count = 0;
      FDC->on_abort_ptr = fdc_format_abort;
      FDC->on_index_ptr = fdc_format_index;
    }
  }
}
/*----------------------------------------------------------------------------*/
static tVoid fdc_format_alt(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format
 *
 *  Format A Track
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W    0  MF   0   0   1   1   0   1 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------  N  ------------> Bytes / sector
 *            W  <------------  SC ------------> Sectors / Track
 *            W  <------------ GPL ------------> Gap 3
 *            W  <------------  D  ------------> Filler Byte
 *  Execution                                    FDC formats entire track
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> In this case, this ID information has no meaning
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 ***********************************************************************/
{
tUChar* dataP;
tULong dwSectorSize;
tULong TrackOffset;
tULong HeaderSize;
tULong sector;

  FDC->led = 1; // turn the drive LED on

  // Set drive selected
	FDC_CHECK_UNIT;
  // Reset status
  FDC->ST0_register = FDC->command[CMD_UNIT] & (ST0_HD | ST0_US1 | ST0_US0);
  FDC->ST1_register = 0;
  FDC->ST2_register = 0;
  // clear status change flags
  FDC->flags &= ~(STATUSDRVA_flag | STATUSDRVB_flag);

  // Drive Not Ready ?
  if ( (!FDC->active_track)
       || (FDC->motor_state != FDC_MOTOR_READY) )
  {
    // Update status
    FDC->ST0_register |= (ST0_AT | ST0_NR);

    // Prepare result
    FDC->result[RES_C]   = 0x00;
    FDC->result[RES_H]   = 0x04;
    FDC->result[RES_R]   = 0x00; // No track formatted
    FDC->result[RES_N]   = FDC->command[CMD_C];
    fdc_format_result(FDC);
  }
  // Is write protect tab set?
  else if (FDC->active_drive->write_protected)
  {
    // Update status
    FDC->ST0_register |= (ST0_AT);
    FDC->ST1_register |= (ST1_NW); // Non writable

    // Prepare result
    fdc_format_result(FDC);
  }
  else
  {
    // Free already formatted track content
    if (FDC->active_track->new_data != cNull)
    {
      FDC->DeleteMemFct(FDC->active_track->new_data);

      // Restore original track
      FDC->active_track->new_data = cNull;
      FDC->active_track->new_size = 0;
    }

    // Free declared data
    for (sector = 0; sector < FDC->active_track->sectors; sector++) // loop for all sectors
    {
      tSector* current_sector = &FDC->active_track->sector[sector];
      if (current_sector->declared_data != cNull)
      {
        FDC->DeleteMemFct(current_sector->declared_data);
        current_sector->declared_data = cNull;
      }
    }

    // Update drive size
    HeaderSize = (FDC->active_track->sectors) ? DSK_TRACKHEADER_SIZE : 0;
    if (FDC->active_track->sectors > DSK_SECTORMAX)
    {
      sector = FDC->active_track->sectors - DSK_SECTORMAX;
      sector = ((sector / DSK_NBSECTORPERHEADER) + 1) * DSK_NBSECTORPERHEADER;
      HeaderSize += sector * DSK_SECTORID_SIZE;
    }
    if (FDC->active_drive->new_data_size == 0)
    {
      FDC->active_drive->new_data_size = FDC->active_drive->data_size;
    }
    FDC->active_drive->new_data_size -= FDC->active_track->size + HeaderSize;

    // Prepare new track format
    FDC->active_track->sectors = FDC->command[CMD_H];
    if (FDC->active_track->sectors > EDSK_SECTORMAX)
    {
      FDC->active_track->sectors = EDSK_SECTORMAX;
    }
    FDC->active_track->gap3_length = FDC->command[CMD_R];
    FDC->active_track->filler = FDC->command[CMD_N];
    dwSectorSize = 128 << (FDC->command[CMD_C] & 7);
    FDC->active_track->first_sector_overwritten = ((tULong)-1);

    // Prepare all sectors data
    TrackOffset = FDC_BYTES_FROM_INDEX_TO_GAP1;
    for (sector = 0; sector < FDC->active_track->sectors; sector++) // loop for all sectors
    {
      // Set default sector data
      tSector* current_sector = &FDC->active_track->sector[sector];
      current_sector->size = dwSectorSize;
      current_sector->declared_size = dwSectorSize;
      current_sector->data_size = dwSectorSize;
      current_sector->weak_count = 1;
      current_sector->data_gap3_length = 0;
      current_sector->info_gap3_length = FDC->active_track->gap3_length;
      current_sector->flags[0] = 0x00;
      current_sector->flags[1] = 0x00;
      current_sector->data = cNull;
      current_sector->new_data = cNull;
      current_sector->track_offset = TrackOffset;

      // Update track offset
      TrackOffset +=   FDC_BYTES_FROM_GAP1_TO_CHRN
                     + FDC_BYTES_FROM_CHRN_TO_DATA
                     + dwSectorSize
                     + FDC_BYTES_CRC + current_sector->info_gap3_length;
      while (TrackOffset > FDC_MAX_BYTES_PER_TRACK)
      {
        TrackOffset -= FDC_MAX_BYTES_PER_TRACK;
      }
    }

    // Allocation memory for each sector
    for (sector = 0; sector < FDC->active_track->sectors; sector++) // loop for all sectors
    {
      tSector* current_sector = &FDC->active_track->sector[sector];
      current_sector->new_data = FDC->NewMemFct(dwSectorSize);
      current_sector->data = current_sector->new_data;
      if (current_sector->data == cNull)
      {
         break;
      }
    }
    // memory allocation issue ?
    if (sector < FDC->active_track->sectors)
    {
      // Stop format, simulate disk not writable
      FDC->ST0_register |= (ST0_AT);
      FDC->ST1_register |= (ST1_NW);

      // Free sectors memory
      for (sector = 0; sector < FDC->active_track->sectors; sector++) // loop for all sectors
      {
        tSector* active_sector = &FDC->active_track->sector[sector];
        if (active_sector->data != cNull)
        {
          FDC->DeleteMemFct(active_sector->data);
          active_sector->data = cNull;
        }
      }

      // Prepare result
      fdc_format_result(FDC);
    }
    else
    {
      // Update Status
      FDC->main_status_register &= ~(MSR_RQM);
      FDC->main_status_register |= (MSR_EXM);

      // Prepare detection of first sector.
      FDC->phase = EXEC_PHASE; // switch to execution phase
      FDC->index_count = 0;
      FDC->on_abort_ptr = fdc_format_abort_alt;
      FDC->on_index_ptr = fdc_format_index_alt;
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_scan(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_scan
 *
 *  Scan Equal
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W   MT  MF  SK   1   0   0   0   1 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------  C  ------------> Sector ID Information prior to command execution.
 *            W  <------------  H  ------------>
 *            W  <------------  R  ------------>
 *            W  <------------  N  ------------>
 *            W  <------------ EOT ------------>
 *            W  <------------ GPL ------------>
 *            W  <------------ STP ------------>
 *  Execution                                    Data compared between the FDD and main system.
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> Sector ID information after command completion
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 *  Scan Low or Equal
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W   MT  MF  SK   1   1   0   0   1 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------  C  ------------> Sector ID Information prior to command execution.
 *            W  <------------  H  ------------>
 *            W  <------------  R  ------------>
 *            W  <------------  N  ------------>
 *            W  <------------ EOT ------------>
 *            W  <------------ GPL ------------>
 *            W  <------------ STP ------------>
 *  Execution                                    Data compared between the FDD and main system.
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> Sector ID information after command completion
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 *  Scan High or Equal
 *                D7  D6  D5  D4  D3  D2  D1  D0
 *  Command   W   MT  MF  SK   1   1   1   0   1 Command codes
 *            W    X   X   X   X   X  HD US1 US0
 *            W  <------------  C  ------------> Sector ID Information prior to command execution.
 *            W  <------------  H  ------------>
 *            W  <------------  R  ------------>
 *            W  <------------  N  ------------>
 *            W  <------------ EOT ------------>
 *            W  <------------ GPL ------------>
 *            W  <------------ STP ------------>
 *  Execution                                    Data compared between the FDD and main system.
 *  Result    R  <------------ ST0 ------------> Status information after command execution
 *            R  <------------ ST1 ------------>
 *            R  <------------ ST2 ------------>
 *            R  <------------  C  ------------> Sector ID information after command completion
 *            R  <------------  H  ------------>
 *            R  <------------  R  ------------>
 *            R  <------------  N  ------------>
 *
 ***********************************************************************/
{
  // Set drive selected
  FDC_CHECK_UNIT;
  // Reset status
  FDC->ST0_register = FDC->command[CMD_UNIT] & (ST0_HD | ST0_US1 | ST0_US0);
  FDC->ST1_register = 0;
  FDC->ST2_register = 0;
  // clear status change flags
  FDC->flags &= ~(STATUSDRVA_flag | STATUSDRVB_flag);

  // At start, sector has not been found yet.
  FDC->ST2_register |= ST2_SN;

	// Disk is present
	if (FDC->active_track)
	{
		if (FDC->active_track->sectors) // track is formatted?
		{
      // Update Status
      FDC->main_status_register &= ~(MSR_RQM);
      FDC->main_status_register |= (MSR_EXM);

      // Seek first sector
      FDC->phase = EXEC_PHASE; // switch to execution phase
      FDC->index_count = 0;
			FDC->on_index_ptr = fdc_scan_index;
			FDC->on_sector_chrn_ptr = fdc_scan_sector;
			FDC->on_abort_ptr = fdc_scan_abort;
      if ( (FDC->on_sector_ptr != cNull)
           && (FDC->on_sector_ptr != fdc_track_first_sector) )
      {
        fdc_scan_sector(FDC);
      }
		}
    else // Not formatted
    {
      // Update status
      FDC->ST0_register |= (ST0_AT);
      FDC->ST1_register |= (ST1_MA);
      FDC->ST2_register |= (ST2_MD);

      // Prepare result
      fdc_scan_result(FDC);
    }
	}
  else // drive was not ready
  {
    // Update status
    FDC->ST0_register |= (ST0_AT);
    FDC->ST1_register |= (ST1_MA);
    FDC->ST2_register |= (ST2_MD);

    // Prepare result
    fdc_scan_result(FDC);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_first_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_first_sector
 *
 ***********************************************************************/
{
#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_new_position++;
  FDC->signal_sector++;
  FDC->active_drive->signal_updated++;
  FDC->active_drive->signal_new_position++;
#endif /* ENABLE_METROLOGY */

  // Initialise pointer on first sector
  FDC->active_drive->current_sector = 0;
	FDC->active_sector = &FDC->active_track->sector[0];
	FDC->current_track_count = FDC_BYTES_FROM_INDEX_TO_CHRN;
  FDC->last_track_data_count = 0;
  
  // Prevent unexpected DATA event
  FDC->on_sector_data_ptr = cNull;
  
  // Save last read CHRN
  FDC->last_read_CHRN[SECTOR_CHRN_C] = (tULong)FDC->active_sector->CHRN[SECTOR_CHRN_C];
  FDC->last_read_CHRN[SECTOR_CHRN_H] = (tULong)FDC->active_sector->CHRN[SECTOR_CHRN_H];
  FDC->last_read_CHRN[SECTOR_CHRN_R] = (tULong)FDC->active_sector->CHRN[SECTOR_CHRN_R];
  FDC->last_read_CHRN[SECTOR_CHRN_N] = (tULong)FDC->active_sector->CHRN[SECTOR_CHRN_N];

  // Signal sector CHRN data available
  if (FDC->on_sector_chrn_ptr != cNull)
  {
    FDC->on_sector_chrn_ptr(FDC);
  }

  // Prepare timing to DATA 
  FDC->sector_timer += FDC_DELAY_FROM_CHRN_TO_DATA;
  FDC->on_sector_ptr = fdc_track_sector_data;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_sector_data(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_sector_data
 *
 ***********************************************************************/
{
  FDC->current_track_count += FDC_BYTES_FROM_CHRN_TO_DATA;

  // Signal sector DATA available
  if (FDC->on_sector_data_ptr != cNull)
  {
    FDC->on_sector_data_ptr(FDC);
    FDC->on_sector_data_ptr = cNull;
  }

  // No more data
  if (!FDC->active_drive->dataP)
  {
    // Stop sector detection
    FDC->on_sector_ptr = cNull;
  }
  // No more sectors
  else if ((FDC->active_drive->current_sector + 1) >= FDC->active_track->sectors)
  {
    // Stop sector detection
    FDC->on_sector_ptr = cNull;
  }
  else
  {
    tSector* sector = &FDC->active_track->sector[FDC->active_drive->current_sector];
    tULong sector_size = sector->data_size;
		tULong gap3_length = sector->data_gap3_length ? sector->data_gap3_length : (FDC_BYTES_CRC + sector->info_gap3_length);

    // Prepare timing to next sector CHRN
    tULong to_next_sector_bytes =   sector_size
                                  + gap3_length
                                  + FDC_BYTES_FROM_GAP1_TO_CHRN;
    FDC->last_track_data_count = to_next_sector_bytes;
    FDC->sector_timer += (to_next_sector_bytes * FDC_DRQ_CYCLE_TIME);
    FDC->on_sector_ptr = fdc_track_next_sector;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_next_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_next_sector
 *
 ***********************************************************************/
{
#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_new_position++;
  FDC->signal_sector++;
  if (FDC->active_drive)
  {
    FDC->active_drive->signal_updated++;
    FDC->active_drive->signal_new_position++;
  }
#endif /* ENABLE_METROLOGY */

  FDC->current_track_count += FDC->last_track_data_count;

  // Increment sector
  FDC->active_drive->current_sector++;
  FDC->active_sector++;
  
  // Prevent unexpected DATA event
  FDC->on_sector_data_ptr = cNull;

  // Save last read CHRN
  FDC->last_read_CHRN[SECTOR_CHRN_C] = (tULong)FDC->active_sector->CHRN[SECTOR_CHRN_C];
  FDC->last_read_CHRN[SECTOR_CHRN_H] = (tULong)FDC->active_sector->CHRN[SECTOR_CHRN_H];
  FDC->last_read_CHRN[SECTOR_CHRN_R] = (tULong)FDC->active_sector->CHRN[SECTOR_CHRN_R];
  FDC->last_read_CHRN[SECTOR_CHRN_N] = (tULong)FDC->active_sector->CHRN[SECTOR_CHRN_N];
  
  // Signal sector CHRN data available
  if (FDC->on_sector_chrn_ptr != cNull)
  {
    FDC->on_sector_chrn_ptr(FDC);
	}

  // Prepare timing to DATA
  FDC->sector_timer += FDC_DELAY_FROM_CHRN_TO_DATA;
  FDC->on_sector_ptr = fdc_track_sector_data;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_head_step(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_head_step
 *
 ***********************************************************************/
{
tULong side;
tULong sector;
tULong bytes_from_index;
tSector* sectorP;

  // Update current track
  if (FDC->active_drive->dest_track > FDC->active_drive->current_track)
	{
		FDC->active_drive->current_track++;
	}
  else
  {
    FDC->active_drive->current_track--;
  }

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_track++;
#endif /* ENABLE_METROLOGY */

  side = FDC->active_drive->sides ? FDC->active_drive->current_side : 0;
	FDC->active_track = &FDC->active_drive->track[FDC->active_drive->current_track][side];

	// Stop sector detection
	FDC->on_sector_ptr = cNull;
	FDC->head_on_track_ptr = cNull;

	/* Track is formatted */
	if (    (FDC->active_track != cNull)
			 && (FDC->active_track->sectors > 0) )
	{
		// Get sector at head position
		bytes_from_index = FDC->from_index_timer >> 7; // /4 (cycles->us) /32 (us->bytes)
		for (sector=0; sector < FDC->active_track->sectors; sector++)
		{
			sectorP = &FDC->active_track->sector[sector];
			if (sectorP->track_offset > bytes_from_index)
				break;
		}
		if (!sector)
		{
			// Start sector detection with first sector
			FDC->active_drive->current_sector = 0;
			FDC->sector_timer = ((sectorP->track_offset - bytes_from_index) * FDC_DRQ_CYCLE_TIME);
			FDC->on_sector_ptr = fdc_track_first_sector;
		}
		else if (sector < (FDC->active_track->sectors - 1))
		{
			// Resume sector detection on this track
			FDC->active_drive->current_sector = sector-1;
			FDC->active_sector = &FDC->active_track->sector[sector-1];
			FDC->sector_timer = ((sectorP->track_offset - bytes_from_index) * FDC_DRQ_CYCLE_TIME);
			FDC->on_sector_ptr = fdc_track_next_sector;
		}
	}

  if (FDC->recalibsteps)
  {
    FDC->recalibsteps--;
    if (!FDC->recalibsteps)
    {
      // Max steps detected
      FDC->flags |= SEEKMAX_flag;
		}
  }

	if (FDC->active_drive->dest_track != FDC->active_drive->current_track)
	{
		// Continue stepping
		FDC->head_step_timer += FDC->step_rate_time;
	}
	else
	{
		// signal completion of seek operation
		FDC->flags |= (FDC->command[CMD_UNIT] & ST0_US0) ? SEEKDRVB_flag : SEEKDRVA_flag;
		FDC->head_step_ptr = cNull;

		if (FDC->delayed_intstat)
		{
			fdc_intstat(FDC);
		}
	}
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_sector_begin_data_transfer(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_sector_begin_data_transfer
 *
 *  Just before first DATA byte
 *
 ***********************************************************************/
{
  // Sector has been found
  FDC->on_index_ptr = cNull;
  FDC->on_sector_chrn_ptr = cNull;

  // Read or Write ?
  if (FDC->cmd_direction == FDC_TO_CPU)
  {
    // Read first byte
    FDC->data_transfer_ack = 1; // No overrun for the first byte
    FDC->head_on_track_timer = FDC->sector_timer + FDC_DRQ_CYCLE_TIME;
    FDC->head_on_track_ptr = fdc_sector_data_read;
  }
  else
  {
    // CPU write first byte
    // Note : Event should occur one byte earlier to allow FDC write after DATA AM.
    FDC->main_status_register &= ~(MSR_DIO);
    FDC->main_status_register |= (MSR_RQM);
    FDC->data_transfer_ack = 0;
    FDC->head_on_track_timer = FDC->sector_timer + FDC_DRQ_CYCLE_TIME;
    FDC->head_on_track_ptr = fdc_sector_data_write;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_sector_data_read(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_sector_data_read
 *
 *  Byte has been read by FDC and should now be read by CPU.
 *
 ***********************************************************************/
{
  // Last byte has been read ?
  if (FDC->buffer_count)
  {
    // Enable read transfer
    FDC->main_status_register |= (MSR_RQM | MSR_DIO);

    // Overrun ?
    FDC_OVERRUN_CHECK;
      
    // Read data from disk
    FDC->data_register = *FDC->buffer_ptr;
    
    FDC->buffer_count--;
    
    // Prepare next byte content
    FDC->buffer_ptr++;
    if (FDC->buffer_ptr >= FDC->buffer_endptr)
    {
      FDC->buffer_ptr = FDC->active_track->data; // wrap around
    }

    // Prepare timing to next data.
    FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_sector_data_write(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_sector_data_write
 *
 ***********************************************************************/
{
	// Overrun check
  FDC_OVERRUN_CHECK;

  if (FDC->buffer_ptr < FDC->buffer_endptr)
  {
    // Write data to disk
    *FDC->buffer_ptr = FDC->data_register;
  }

  if (FDC->data_register != FDC_BYTE_FORMAT)
  {
    FDC->active_sector->empty = cFalse;
  }

  // Another byte to write ?
  FDC->buffer_count--;
  if (FDC->buffer_count)
  {
    // Enable transfer
    FDC->main_status_register |= (MSR_RQM);

    // Prepare next byte content
    if (FDC->buffer_ptr < FDC->buffer_endptr)
    {
      FDC->buffer_ptr++;
    }

    // Prepare timing to next data.
    FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_begin_read(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_begin_read
 *
 ***********************************************************************/
{
tSector* sector = FDC->active_sector;

  FDC->on_sector_chrn_ptr = cNull;

  // Start Track READ operation
  FDC->requested_read_count = FDC->requested_sector_size;
  if (sector->declared_size <= sector->size)
  {
    // Read declared size + GAP3
    FDC->buffer_count = sector->data_size + sector->data_gap3_length;
  }
  else
  {
    // Read real size of sector
    FDC->buffer_count = sector->size;
  }

  FDC->data_transfer_ack = 1;
  FDC->head_on_track_timer = FDC->sector_timer + FDC_DRQ_CYCLE_TIME;
  FDC->head_on_track_ptr = fdc_track_read_data;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_data(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_data
 *
 ***********************************************************************/
{
tSector* sector;

  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Still byte of current sector to read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;
      
      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;
      
      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;

      // Read data from disk
      FDC->data_register = *(FDC->buffer_ptr++);
    
      // Next byte
      if (FDC->buffer_ptr >= FDC->buffer_endptr)
      {
        FDC->buffer_ptr = FDC->active_track->data; // wrap around
      }
    }
    else
    {
      sector = FDC->active_sector;

      // Next step
      if (sector->data_gap3_length)
      {
        // Another sector ?
        if (FDC->on_sector_ptr != cNull)
        {
          // Next step: Next sector SYNC
          FDC->buffer_count = FDC_BYTES_SYNC;
          FDC->data_register = FDC_BYTE_SYNC;
          FDC->head_on_track_ptr = fdc_track_read_id_sync;
          fdc_track_read_id_sync(FDC);
        }
        else
        {
          // Next step : End of track until Index detection
          FDC->data_register = FDC_BYTE_GAP4B;
          FDC->head_on_track_ptr = fdc_track_read_gap4b;
          fdc_track_read_gap4b(FDC);
        }
      }
      else
      {
        // Next step : Data CRC
        FDC->buffer_count = FDC_BYTES_CRC;
        FDC->buffer_ptr = sector->data_crc;
        FDC->head_on_track_ptr = fdc_track_read_data_crc;
        fdc_track_read_data_crc(FDC);
      }
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_data_crc(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_data_crc
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Still CRC byte to read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;
    
      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;
      
      // Read data from disk
      FDC->data_register = *(FDC->buffer_ptr++);
    
      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Next step : GAP3
      FDC->buffer_count = FDC->active_sector->info_gap3_length;
			FDC->data_register = FDC->active_sector->gap3_value;
      FDC->head_on_track_ptr = fdc_track_read_gap3;
      fdc_track_read_gap3(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_gap3(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_gap3
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Still GAP3 byte to read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;

      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Another sector ?
      if (FDC->on_sector_ptr != cNull)
      {
        // Next step : Next sector SYNC
        FDC->buffer_count = FDC_BYTES_SYNC;
        FDC->data_register = FDC_BYTE_SYNC;
        FDC->head_on_track_ptr = fdc_track_read_id_sync;
        fdc_track_read_id_sync(FDC);
      }
      else
      {
        // Next step : End of track and Index detection
        FDC->data_register = FDC_BYTE_GAP4B;
        FDC->head_on_track_ptr = fdc_track_read_gap4b;
        fdc_track_read_gap4b(FDC);
      }
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_id_sync(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_id_sync
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Still ID byte to read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;

      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Next step
      FDC->buffer_count = FDC_BYTES_IDAM;
      FDC->buffer_ptr = (tUChar*)fdc_idam;
      FDC->head_on_track_ptr = fdc_track_read_idam;
      fdc_track_read_idam(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_idam(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_idam
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Still IDAM byte to read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;

      // Read data from disk
      FDC->data_register = *(FDC->buffer_ptr++);

      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Next step
      FDC->buffer_count = FDC_BYTES_CHRN + FDC_BYTES_CRC;
      FDC->buffer_ptr = FDC->active_sector->CHRN;
      FDC->head_on_track_ptr = fdc_track_read_chrn;
      fdc_track_read_chrn(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_chrn(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_chrn
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Still CHRN byte to read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;

      // Read data from disk
      FDC->data_register = *(FDC->buffer_ptr++);

      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Next step
      FDC->buffer_count = FDC_BYTES_GAP2;
			FDC->data_register = FDC->active_sector->gap2_value;
			FDC->head_on_track_ptr = fdc_track_read_gap2;
      fdc_track_read_gap2(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_gap2(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_gap2
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Still GAP2 byte to read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;
      
      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Next step
      FDC->buffer_count = FDC_BYTES_SYNC;
      FDC->data_register = FDC_BYTE_SYNC;
      FDC->head_on_track_ptr = fdc_track_read_data_sync;
      fdc_track_read_data_sync(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_data_sync(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_data_sync
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Still Data SYNC byte to read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;
      
      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Next step
      FDC->buffer_count = FDC_BYTES_DATAAM;
      if (FDC->active_sector->flags[1] & ST2_CM)
      {
        FDC->buffer_ptr = (tUChar*)fdc_dataam_deleted;
      }
      else
      {
        FDC->buffer_ptr = (tUChar*)fdc_dataam;
      }
      FDC->head_on_track_ptr = fdc_track_read_dataam;
      fdc_track_read_dataam(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_dataam(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_dataam
 *
 ***********************************************************************/
{
tSector* sector;

  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Still DATAAM byte to read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;
      
      // Read data from disk
      FDC->data_register = *(FDC->buffer_ptr++);
    
      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      sector = FDC->active_sector;

      // Read data
      FDC->buffer_ptr = sector->data;
      if (sector->declared_size <= sector->size)
      {
        // Read declared size + GAP3
        FDC->buffer_count = sector->data_size + sector->data_gap3_length;
      }
      else
      {
        // Read real size of sector
        FDC->buffer_count = sector->size;
      }

      FDC->head_on_track_ptr = fdc_track_read_data;
      fdc_track_read_data(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_gap4b(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_gap4b
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    FDC->requested_read_count--;

    // Enable transfer
    FDC->main_status_register |= (MSR_RQM | MSR_DIO);

    // Overrun check
    FDC_OVERRUN_CHECK;
    
    // Prepare timing to next data.
    FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
  }
  else
  {
    if (FDC->next_track_count)
    {
      // Continue read at track begin
      if (FDC->next_track_count > FDC->active_track->real_size)
      {
        FDC->requested_read_count = FDC->active_track->real_size;
        FDC->next_track_count -= FDC->active_track->real_size;
      }
      else
      {
        FDC->requested_read_count = FDC->next_track_count;
        FDC->next_track_count = 0;
      }

      fdc_track_read_index(FDC);
    }
    else
    {
      FDC->head_on_track_ptr = cNull;

      // Signal end of transfer
      if (FDC->on_end_of_transfer_ptr != cNull)
      {
        FDC->on_end_of_transfer_ptr(FDC);
      }
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_index(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_index
 *
 ***********************************************************************/
{
  // Remove end of track
  if ((tULong)FDC->active_sector->CHRN[SECTOR_CHRN_R] == FDC->command[CMD_EOT])
  {
    FDC->ST1_register &= ~(ST1_EN);
  }

  // Prepare Track read
  FDC->buffer_count = FDC_BYTES_GAP4A;
  FDC->data_register = FDC_BYTE_GAP4A;
  FDC->head_on_track_ptr = fdc_track_read_gap4a;
  fdc_track_read_gap4a(FDC);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_gap4a(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_gap4a
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Last byte has been read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;
      
      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Next step
      FDC->buffer_count = FDC_BYTES_SYNC;
      FDC->data_register = FDC_BYTE_SYNC;
      FDC->head_on_track_ptr = fdc_track_read_index_sync;
      fdc_track_read_index_sync(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_index_sync(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_index_sync
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Last byte has been read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;

      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Next step
      FDC->buffer_count = FDC_BYTES_IAM;
      FDC->buffer_ptr = (tUChar*)fdc_iam;
      FDC->head_on_track_ptr = fdc_track_read_iam;
      fdc_track_read_iam(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_iam(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_iam
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Last byte has been read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;

      // Read data from disk
      FDC->data_register = *(FDC->buffer_ptr++);

      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Next step
      FDC->buffer_count = FDC_BYTES_GAP1;
      FDC->data_register = FDC_BYTE_GAP1;
      FDC->head_on_track_ptr = fdc_track_read_gap1;
      fdc_track_read_gap1(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_track_read_gap1(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_track_read_gap1
 *
 ***********************************************************************/
{
  // Still requested byte to read ?
  if (FDC->requested_read_count)
  {
    // Last byte has been read ?
    if (FDC->buffer_count)
    {
      FDC->buffer_count--;
      FDC->requested_read_count--;

      // Enable transfer
      FDC->main_status_register |= (MSR_RQM | MSR_DIO);

      // Overrun check
      FDC_OVERRUN_CHECK;

      // Prepare timing to next data.
      FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
    }
    else
    {
      // Next step: First sector SYNC
      FDC->buffer_count = FDC_BYTES_SYNC;
      FDC->data_register = FDC_BYTE_SYNC;
      FDC->head_on_track_ptr = fdc_track_read_id_sync;
      fdc_track_read_id_sync(FDC);
    }
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readid_index(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readid_index
 *
 ***********************************************************************/
{
  FDC->index_count++;

  if (FDC->index_count == 2)
  {
    // Update status
    FDC->ST0_register |= ST0_AT;
    FDC->ST1_register |= (ST1_MA);

    // Prepare result
    fdc_readid_result(FDC);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readid_sector_chrn(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readid_sector_chrn
 *
 ***********************************************************************/
{
  // MFM ?
  if (FDC->command[CMD_CODE] & CMD_MF)
  {
    // Stop sector detection
    FDC->on_sector_chrn_ptr = cNull;
    FDC->on_index_ptr = cNull;
    
    // Prepare timing to CHRN CRC prior preparing result
    FDC->head_on_track_timer = FDC->sector_timer + FDC_DELAY_CHRN;
    FDC->head_on_track_ptr = fdc_readid_sector_found;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readid_sector_found(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readid_sector_found
 *
 ***********************************************************************/
{
tSector* sector;

  sector = FDC->active_sector;
  FDC->head_on_track_ptr = cNull;
  
  // Update status
  FDC->main_status_register &= ~(MSR_RQM | MSR_DIO | MSR_EXM);
  FDC->main_status_register |= (MSR_RQM | MSR_DIO);
  FDC->ST1_register &= ~(ST1_ND | ST1_MA);
  FDC->ST1_register |= sector->flags[0] & (ST1_MA);

  // Prepare result
  FDC->result[RES_C]   = sector->CHRN[SECTOR_CHRN_C];
  FDC->result[RES_H]   = sector->CHRN[SECTOR_CHRN_H];
  FDC->result[RES_R]   = sector->CHRN[SECTOR_CHRN_R];
  FDC->result[RES_N]   = sector->CHRN[SECTOR_CHRN_N];

  fdc_readid_result(FDC);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readid_result(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readid_result
 *
 ***********************************************************************/
{
  // Terminate operation
  FDC->on_index_ptr = cNull;
  FDC->on_sector_chrn_ptr = cNull;
  FDC->on_abort_ptr = cNull;

  // Update Status
  FDC->main_status_register &= ~(MSR_EXM);
  FDC->main_status_register |= (MSR_RQM | MSR_DIO);

  // Prepare result
  FDC->phase = RESULT_PHASE; // switch to result phase
  FDC->result[RES_ST0] = FDC->ST0_register;
  FDC->result[RES_ST1] = FDC->ST1_register;
  FDC->result[RES_ST2] = FDC->ST2_register;

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readid_abort(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readid_abort
 *
 ***********************************************************************/
{
  // Update status
  FDC->ST0_register |= (ST0_AT | ST0_NR);

  // Prepare result
  fdc_readid_result(FDC);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_read_index(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_index
 *
 ***********************************************************************/
{
  FDC->index_count++;

  if (    (FDC->index_count >= 2)
       && (FDC->on_end_of_transfer_ptr == cNull) )
  {
    // Update status
    FDC->ST0_register |= ST0_AT;
    FDC->ST1_register |= ST1_ND;
    
    // Prepare result
    fdc_read_result(FDC);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_read_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_sector
 *
 ***********************************************************************/
{
tSector* sector;
tULong track_size;

  sector = FDC->active_sector;

  if (FDC->phase == EXEC_PHASE)
  {
    FDC->main_status_register |= (MSR_EXM);
  }

  if (FDC->command[CMD_C] != (tULong)sector->CHRN[SECTOR_CHRN_C])
  {
		// Wrong cylinder
		FDC->ST2_register |= (ST2_WC);

    if (sector->CHRN[SECTOR_CHRN_C] == 0xff)
    {
      // Bad cylinder
			FDC->ST2_register |= (ST2_BC);
    }
  }
  // Matching CHRN ?
  else if (
               (FDC->command[CMD_H] == (tULong)sector->CHRN[SECTOR_CHRN_H])
            && (FDC->command[CMD_R] == (tULong)sector->CHRN[SECTOR_CHRN_R])
            && (FDC->command[CMD_N] == (tULong)sector->CHRN[SECTOR_CHRN_N]) )
	{
		// Reset Wrong cylinder and bad cylinder flags
		FDC->ST2_register &= ~(ST2_WC | ST2_BC);

    // Update Status
		FDC->ST1_register |= sector->flags[0] & (ST1_DE | ST1_ND | ST1_MA);
    FDC->ST2_register |= sector->flags[1] & (ST2_CM | ST2_DD | ST2_MD | ST2_WC | ST2_BC);

    // End of track detected ?
    FDC->end_of_track_detected = ((tULong)sector->CHRN[SECTOR_CHRN_R] == FDC->command[CMD_EOT]) ? cTrue : cFalse;
    if (FDC->end_of_track_detected)
    {
      // No Data error
      if ((FDC->ST1_register & ST1_DE) == 0)
      {
        FDC->ST1_register |= (ST1_EN);
      }
    }

    // Skip non deleted data ?
    if ( (FDC->flags & DELETED_flag)
         && (FDC->flags & SKIP_flag)
         && ((sector->flags[1] & ST2_CM) == 0) )
    {
      // skip sector
      fdc_read_end_sector(FDC);
    }
    else
    // Skip deleted data ?
    if ( ((FDC->flags & DELETED_flag) == 0)
         && (FDC->flags & SKIP_flag)
         && (sector->flags[1] & ST2_CM) )
    {
      // skip sector
      fdc_read_end_sector(FDC);
    }
    else
    // Data present ?
    if ( ((sector->flags[0] & (ST1_ND | ST1_MA)) == 0)
				 && ((sector->flags[1] & (ST2_MD)) == 0) )
		{
			// Prepare read
			FDC->buffer_ptr = sector->data;
			FDC->buffer_endptr = FDC->active_track->data + FDC->active_track->size; // pointer beyond end of track data
			if (FDC->command[CMD_N] == 0) // use DTL for length?
			{
				FDC->requested_sector_size = FDC->command[CMD_DTL]; // size of sector is defined by DTL value

				if (FDC->requested_sector_size >= 0x80)
				{
					FDC->requested_sector_size = 0x50; // max DTL value is 128
				}
			}
			else
			{
        if (FDC->command[CMD_N] < 8)
        {
				  FDC->requested_sector_size = 128 << FDC->command[CMD_N]; // determine number of bytes from N value
        }
        else
        {
          FDC->requested_sector_size = 32768;  // 128 << 8
        }
			}

      // Stop index detection
      FDC->on_index_ptr = cNull;

      // Handle weak sectors (different data to emulate weak bits)
      if (sector->weak_count > 1)
      {
        FDC->weak_counter = (FDC->weak_counter + 1) % sector->weak_count;
        FDC->buffer_ptr += sector->declared_size * FDC->weak_counter;
      }

      // Read this sector data
      FDC->buffer_count = sector->data_size;
      FDC->next_track_count = 0;

      // use read_sector method with declared data ?
      if ( (sector->declared_data)
           && (FDC->requested_sector_size == sector->declared_size) )
      {
        // Read declared data
        FDC->buffer_count = sector->declared_size;
        FDC->buffer_ptr = sector->declared_data;
        FDC->buffer_endptr = sector->declared_data + sector->declared_size;
        FDC->requested_read_count = (FDC->requested_sector_size > sector->declared_size) ?
                                    FDC->requested_sector_size - sector->declared_size :
                                    0;
        FDC->on_sector_data_ptr = fdc_sector_begin_data_transfer;
      }
      // use read_sector method
      else if (sector->size >= sector->declared_size)
      {
        if (FDC->requested_sector_size >= sector->data_size)
        {
          FDC->requested_read_count = FDC->requested_sector_size - sector->data_size;
        }
        else
        {
          FDC->buffer_count = FDC->requested_sector_size;
          FDC->requested_read_count = 0;
        }

        FDC->on_sector_data_ptr = fdc_sector_begin_data_transfer;
      }
      // use read_track method
      else
      {
        FDC->on_sector_data_ptr = fdc_track_begin_read;
        track_size = (FDC->active_track->real_size > FDC_MAX_BYTES_PER_TRACK) ? FDC->active_track->real_size : FDC_MAX_BYTES_PER_TRACK;
        if ((FDC->current_track_count + FDC->requested_sector_size) > track_size)
        {
          FDC->next_track_count = FDC->current_track_count + FDC->requested_sector_size - track_size;
          FDC->requested_sector_size = track_size - FDC->current_track_count;
        }

        // Signal data error
        FDC->ST1_register |= ST1_DE;
        FDC->ST2_register |= ST2_DD;
      }

      FDC->on_end_of_transfer_ptr = fdc_read_end_sector;
    }
    else // No Data
    {
      // Update Status
      FDC->ST0_register |= (ST0_AT);

      // Terminate operation
      fdc_read_result(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_read_end_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_end_sector
 *
 ***********************************************************************/
{
  // Stop transfer
  FDC->on_end_of_transfer_ptr = cNull;
  FDC->head_on_track_ptr = cNull;

  // Overrun ?
  if (FDC->ST1_register & ST1_OR)
  {
    // End of Operation
    FDC->ST0_register |= ST0_AT;
    FDC->ST1_register &= ~(ST1_EN);

    // Prepare result
    fdc_read_result(FDC);
  }
  else
  // All requested data has been read ?
  if (FDC->requested_read_count)
  {
    // Prepare to read next sector
    FDC->on_sector_chrn_ptr = fdc_read_next_sector;
  }
  else if (FDC->next_track_count)
  {
    // Continue read at track begin
    FDC->on_end_of_transfer_ptr = fdc_read_end_sector;
    FDC->requested_read_count = FDC->next_track_count;
    FDC->next_track_count = 0;
    fdc_track_read_index(FDC);
  }
  else
  {
    // Control mark during Read Data
    if ( (FDC->active_sector->flags[1] & ST2_CM)
         && ((FDC->flags & SKIP_flag) == 0)
         && ((FDC->flags & DELETED_flag) == 0) )
    {
      // Update Status
      FDC->ST1_register &= ~(ST1_EN);

      // End Of Track
      if (FDC->ST1_register & ST1_DE)
      {
        FDC->ST0_register |= (ST0_AT);
      }

      // Prepare result
      fdc_read_result(FDC);
    }
    else
    {
      // Data during Read deleted data
      if ( ((FDC->active_sector->flags[1] & ST2_CM) == 0)
           && (FDC->flags & DELETED_flag) )
      {
        // Update Status
        FDC->ST2_register |= (ST2_CM);

        if ((FDC->flags & SKIP_flag) == 0)
        {
          // Update Status
          FDC->ST1_register &= ~(ST1_EN);
          // Prepare result
          fdc_read_result(FDC);
        }
        // End Of Track ?
        else if (FDC->end_of_track_detected)
        {
          // End Of Track
          FDC->ST0_register |= (ST0_AT);

          // Prepare result
          fdc_read_result(FDC);
        }
        else
        {
          // Seek next sector
          FDC->command[CMD_R]++;
          FDC->index_count = 0;
          FDC->on_index_ptr = fdc_read_index;
          FDC->on_sector_chrn_ptr = fdc_read_sector;
        }
      }
      else
      {
        if ( (FDC->flags & DELETED_flag)
             && ((FDC->flags & SKIP_flag) == 0) )
        {
          // Update Status
          FDC->ST2_register &= ~(ST2_CM); // Remove Control Mark detected
        }

        // End Of Track ?
        if (FDC->end_of_track_detected)
        {
          if ( (FDC->flags & MULTITRACK_flag) && ((FDC->command[CMD_H] & 0x01) == 0) )
          {
            tULong side;

            // Invert head
            FDC->command[CMD_H] ^= 0x01;
            FDC->ST0_register ^= ST0_HD;

            // Update current track
		        FDC->active_drive->current_side ^= 0x01;
		        side = FDC->active_drive->sides ? FDC->active_drive->current_side : 0;
            FDC->active_track = &FDC->active_drive->track[FDC->active_drive->current_track][side];

            // Remove End of cylinder detection
            FDC->ST1_register &= ~(ST1_EN);

            // Seek next sector
            FDC->command[CMD_R] = 0x01;
            FDC->index_count = 0;
            FDC->on_index_ptr = fdc_read_index;
            FDC->on_sector_chrn_ptr = fdc_read_sector;
          }
          else
          {
            // End Of Track
            FDC->ST0_register |= (ST0_AT);

            // Prepare result
            fdc_read_result(FDC);
          }
        }
        else
        {
          // Seek next sector
          FDC->command[CMD_R]++;
          FDC->index_count = 0;
          FDC->on_index_ptr = fdc_read_index;
          FDC->on_sector_chrn_ptr = fdc_read_sector;
        }
      }
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_read_next_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_next_sector
 *
 ***********************************************************************/
{
tSector* sector = FDC->active_sector;

  // Data present ?
  if ( ((sector->flags[0] & (ST1_ND | ST1_MA)) == 0)
       && ((sector->flags[1] & (ST2_MD)) == 0) )
  {
    // Read this sector data
    FDC->on_sector_chrn_ptr = cNull;
    FDC->buffer_count = sector->data_size;
    FDC->requested_read_count = (FDC->requested_read_count > FDC->buffer_count) ? FDC->requested_read_count - FDC->buffer_count : 0;
    FDC->on_sector_data_ptr = fdc_sector_begin_data_transfer;
    FDC->on_end_of_transfer_ptr = fdc_read_end_sector;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_read_result(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_result
 *
 ***********************************************************************/
{
  // Terminate operation
  FDC->on_index_ptr = cNull;
  FDC->on_sector_chrn_ptr = cNull;
  FDC->on_sector_data_ptr = cNull;
  FDC->on_end_of_transfer_ptr = cNull;
  FDC->head_on_track_ptr = cNull;
  FDC->on_abort_ptr = cNull;

  // Update Status
  FDC->main_status_register &= ~(MSR_EXM);
  FDC->main_status_register |= (MSR_RQM | MSR_DIO);

  // Prepare result
  FDC->phase = RESULT_PHASE; // switch to result phase
  FDC->result[RES_ST0] = FDC->ST0_register;
  FDC->result[RES_ST1] = FDC->ST1_register;
  FDC->result[RES_ST2] = FDC->ST2_register;
  FDC->result[RES_C]   = FDC->command[CMD_C];
  FDC->result[RES_H]   = FDC->command[CMD_H];
  FDC->result[RES_R]   = FDC->command[CMD_R];
  FDC->result[RES_N]   = FDC->command[CMD_N];

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_read_abort(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_read_abort
 *
 ***********************************************************************/
{
  // Update status
  FDC->ST0_register |= (ST0_ATR);

  // Terminate operation
  fdc_read_result(FDC);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_write_index(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_write_index
 *
 ***********************************************************************/
{
  FDC->index_count++;
  
  if (    (FDC->index_count == 2)
       && (FDC->on_end_of_transfer_ptr == cNull) )
  {
    // Update status
    FDC->ST0_register |= ST0_AT;
    FDC->ST1_register |= ST1_ND;

    // Prepare result
    fdc_write_result(FDC);

#ifdef ENABLE_METROLOGY
    FDC->active_drive->signal_updated++;
    FDC->active_drive->signal_sector_write++;
#endif /* ENABLE_METROLOGY */
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_write_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_write_sector
 *
 ***********************************************************************/
{
tSector* sector;

  sector = FDC->active_sector;

  if (FDC->phase == EXEC_PHASE)
  {
    FDC->main_status_register |= (MSR_EXM);
  }

  // Different Cylinder ?
  if (FDC->command[CMD_C] != (tULong)sector->CHRN[SECTOR_CHRN_C])
  {
    if (sector->CHRN[SECTOR_CHRN_C] == 0xff)
    {
      FDC->ST2_register |= (ST2_BC);
    }
    else
    {
      FDC->ST2_register |= (ST2_WC);
    }
  }
  // Matching CHRN ?
  else if (    (FDC->command[CMD_H] == (tULong)sector->CHRN[SECTOR_CHRN_H])
            && (FDC->command[CMD_R] == (tULong)sector->CHRN[SECTOR_CHRN_R])
            && (FDC->command[CMD_N] == (tULong)sector->CHRN[SECTOR_CHRN_N]) )
  {
		// Reset Wrong cylinder and bad cylinder flags
		FDC->ST2_register &= ~(ST2_WC | ST2_BC);

    sector->flags[0] = 0;
    sector->flags[1] = ((FDC->command[CMD_CODE] & CMD_OP_MASK) == CMD_OP_WRITE_DELETED) ? ST2_CM : 0;

    // Stop index detection
    FDC->on_index_ptr = cNull;

    if (sector->declared_size > FDC_MAX_BYTES_PER_SECTOR)
    {
      sector->flags[0] |= ST1_DE;
      sector->flags[1] |= ST2_DD;
    }

    // End of track detected ?
    FDC->end_of_track_detected = ((tULong)sector->CHRN[SECTOR_CHRN_R] == FDC->command[CMD_EOT]) ? cTrue : cFalse;
    if (FDC->end_of_track_detected)
    {
      FDC->ST1_register |= (ST1_EN);
    }

    // Prepare write
    FDC->buffer_ptr = sector->data;
    FDC->buffer_endptr = FDC->active_track->data + FDC->active_track->size; // pointer beyond end of track data

    FDC->active_drive->altered = 1; // indicate that the image has been modified

    // Prepare timing until begin of data.
    FDC->buffer_count = FDC->requested_sector_size;
    FDC->on_sector_data_ptr = fdc_sector_begin_data_transfer;
    FDC->on_end_of_transfer_ptr = fdc_write_end_sector;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_write_end_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_write_end_sector
 *
 ***********************************************************************/
{
  // Stop transfer
  FDC->on_end_of_transfer_ptr = cNull;
  FDC->head_on_track_ptr = cNull;

  // Update Status
  FDC->ST1_register |= FDC->active_sector->flags[0];
  FDC->ST2_register |= FDC->active_sector->flags[1];

  // Remove Control Mark
  FDC->ST2_register &= ~(ST2_CM);

  if (FDC->active_sector->declared_data)
  {
    CreateDeclaredSizeSector(FDC->active_track, FDC->active_drive->current_sector);
  }

  // Overrun ?
  if (FDC->ST1_register & ST1_OR)
  {
    // End of Operation
    FDC->ST0_register |= ST0_AT;
    FDC->ST1_register &= ~(ST1_EN);

    // Prepare result
    fdc_write_result(FDC);
  }
  else
  // Not yet end Of Track
  if (FDC->end_of_track_detected == cFalse)
  {
    // Seek next sector
    FDC->command[CMD_R]++;
    FDC->index_count = 0;
    FDC->on_index_ptr = fdc_write_index;
    FDC->on_sector_chrn_ptr = fdc_write_sector;
  }
  else
  {
    if ( (FDC->flags & MULTITRACK_flag) && ((FDC->command[CMD_H] & 0x01) == 0) )
    {
      tULong side;

      // Invert head
      FDC->command[CMD_H] ^= 0x01;
      FDC->ST0_register ^= ST0_HD;

      // Update current track
		  FDC->active_drive->current_side ^= 0x01;
		  side = FDC->active_drive->sides ? FDC->active_drive->current_side : 0;
      FDC->active_track = &FDC->active_drive->track[FDC->active_drive->current_track][side];

      // Remove End of cylinder detection
      FDC->ST1_register &= ~(ST1_EN);

      // Seek first sector
      FDC->command[CMD_R] = 1;
      FDC->index_count = 0;
      FDC->on_index_ptr = fdc_write_index;
      FDC->on_sector_chrn_ptr = fdc_write_sector;
    }
    else
    {
      // End Of Track
      FDC->ST0_register |= (ST0_AT);

      // Prepare result
      fdc_write_result(FDC);
    }

#ifdef ENABLE_METROLOGY
    FDC->active_drive->signal_updated++;
    FDC->active_drive->signal_sector_write++;
#endif /* ENABLE_METROLOGY */
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_write_result(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_write_result
 *
 ***********************************************************************/
{
  // Terminate operation
  FDC->on_index_ptr = cNull;
  FDC->on_sector_chrn_ptr = cNull;
  FDC->on_sector_data_ptr = cNull;
  FDC->on_end_of_transfer_ptr = cNull;
  FDC->head_on_track_ptr = cNull;
  FDC->on_abort_ptr = cNull;

  // Update Status
  FDC->main_status_register &= ~(MSR_EXM);
  FDC->main_status_register |= (MSR_RQM | MSR_DIO);

  // Prepare result
  FDC->phase = RESULT_PHASE; // switch to result phase
  FDC->result[RES_ST0] = FDC->ST0_register;
  FDC->result[RES_ST1] = FDC->ST1_register;
  FDC->result[RES_ST2] = FDC->ST2_register;
  FDC->result[RES_C]   = FDC->command[CMD_C];
  FDC->result[RES_H]   = FDC->command[CMD_H];
  FDC->result[RES_R]   = FDC->command[CMD_R];
  FDC->result[RES_N]   = FDC->command[CMD_N];
  
#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_write_abort(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_write_abort
 *
 ***********************************************************************/
{
  // Transfer in progress ?
  if (FDC->on_end_of_transfer_ptr)
  {
    // Update sector flags
    FDC->active_sector->flags[0] |= ST1_DE;
    FDC->active_sector->flags[1] |= ST2_DD;
  }

  if (FDC->active_sector->declared_data)
  {
    CreateDeclaredSizeSector(FDC->active_track, FDC->active_drive->current_sector);
  }

  // Update status
  FDC->ST0_register |= (ST0_ATR);

  // Terminate operation
  fdc_write_result(FDC);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readtrack_index(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readtrack_index
 *
 ***********************************************************************/
{
  FDC->index_count++;

  if (FDC->index_count == 1)
  {
    // MFM mode ?
    if (FDC->command[CMD_CODE] & CMD_MF)
    {
      // track is formatted?
      if (FDC->active_track->sectors)
      {
        // Update Status
        FDC->ST1_register |= (ST1_ND); // Set No Data

        // Start track read
        FDC->on_sector_chrn_ptr = fdc_readtrack_begin;
      }
      else // Not formatted track
      {
        // Update Status
        FDC->ST1_register |= (ST1_MA);
        FDC->ST0_register |= ST0_AT;

        fdc_readtrack_result(FDC);
      }
    }
  }
  else if (FDC->index_count >= 2)
  {
    // FM mode ?
    if ((FDC->command[CMD_CODE] & CMD_MF) == 0)
    {
      // No data found
      FDC->ST0_register |= ST0_AT;
      FDC->ST1_register |= ST1_ND;

      // Prepare result
      fdc_readtrack_result(FDC);
    }
    else if ( (!FDC->sector_read_count)
              && (!FDC->requested_read_count)
              && (!FDC->next_track_count) )
    {
      // End of Track
      FDC->ST0_register |= ST0_AT;

      fdc_readtrack_result(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readtrack_begin(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readtrack_begin
 *
 ***********************************************************************/
{
tSector* sector;

  sector = FDC->active_sector;
  FDC->on_sector_chrn_ptr = cNull;

  // Matching CHRN ?
  if (    (FDC->command[CMD_R] == FDC->last_read_CHRN[SECTOR_CHRN_R])
       && (FDC->command[CMD_N] == FDC->last_read_CHRN[SECTOR_CHRN_N])
       && (FDC->command[CMD_EOT]) )
  {
    // No Data Error
    if ((FDC->active_sector->flags[0] & ST1_DE) == 0)
    {
      // Update Status
      FDC->ST1_register &= ~(ST1_ND); // Sector has been found, remove No Data.
    }
  }

  // Update Status
  FDC->ST1_register |= sector->flags[0] & (ST1_DE | ST1_ND | ST1_MA);
  FDC->ST2_register |= sector->flags[1] & (ST2_CM | ST2_DD | ST2_MD | ST2_WC | ST2_BC);

  // End of track detected ?
  FDC->sector_read_count--;
  FDC->end_of_track_detected = (!FDC->sector_read_count) ? cTrue : cFalse;

  // Determine size of sector
  if (FDC->command[CMD_N] == 0) // use DTL for length?
  {
    // Pass fdctest &28
    FDC->requested_sector_size = 80;
  }
  else if (FDC->command[CMD_N] < 8)
  {
    FDC->requested_sector_size = 128 << FDC->command[CMD_N]; // determine number of bytes from N value
  }
  else
  {
    FDC->requested_sector_size = 32768; // 128 << 8
  }

  if (FDC->requested_sector_size > FDC->active_track->real_size)
  {
    FDC->next_track_count = FDC->requested_sector_size - FDC->active_track->real_size;
    FDC->requested_sector_size = FDC->active_track->real_size;
  }
  else
  {
    FDC->next_track_count = 0;
  }

  // Data to read
  if (FDC->requested_sector_size)
  {
    // Valid address mark ?
    if ((sector->flags[1] & ST2_MD) == 0)
    {
      if (FDC->end_of_track_detected)
      {
        FDC->ST1_register |= (ST1_EN);
      }

      // Skip control mark
      if ( (FDC->flags & SKIP_flag)
           && (sector->flags[1] & ST2_CM) )
      {
        // Skip sector
        fdc_readtrack_end(FDC);
      }
      else
      {
        // Prepare data to read
        FDC->buffer_ptr = sector->data;
        FDC->buffer_endptr = sector->data + sector->size;

        // Prepare timing until begin of data.
        FDC->on_sector_data_ptr = fdc_track_begin_read;
        FDC->on_end_of_transfer_ptr = fdc_readtrack_end;

        // Stop index detection
        FDC->on_index_ptr = cNull;

        // Sector size are incompatible ?
        if (FDC->requested_sector_size != sector->declared_size)
        {
          // Signal data error
          FDC->ST1_register |= ST1_DE;
          FDC->ST2_register |= ST2_DD;
        }
      }
    }
    else
    {
      // End of Operation
      FDC->ST0_register |= ST0_AT;
      FDC->ST1_register |= ST1_MA;
      
      // Terminate operation
      fdc_readtrack_result(FDC);
    }
  }
  else // No Data
  {
    // Terminate operation
    fdc_readtrack_end(FDC);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readtrack_end(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readtrack_end
 *
 ***********************************************************************/
{
  // Overrun ?
  if (FDC->ST1_register & ST1_OR)
  {
    // End of Operation
    FDC->ST0_register |= ST0_AT;

    fdc_readtrack_result(FDC);
  }
  else
  {
    if (FDC->next_track_count)
    {
      if (FDC->next_track_count > FDC->active_track->real_size)
      {
        FDC->requested_read_count = FDC->active_track->real_size;
        FDC->next_track_count -= FDC->active_track->real_size;
      }
      else
      {
        FDC->requested_read_count = FDC->next_track_count;
        FDC->next_track_count = 0;
      }
      fdc_track_read_index(FDC);
    }
    else
    {
      // Stop transfer
      FDC->on_end_of_transfer_ptr = cNull;

      // Continue read track
      if (FDC->sector_read_count)
      {
        // Continue sector detection
        FDC->on_sector_chrn_ptr = fdc_readtrack_begin;
      }
      else
      {
        // End of Operation
        FDC->ST0_register |= ST0_AT;

        fdc_readtrack_result(FDC);
      }
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readtrack_result(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readtrack_result
 *
 ***********************************************************************/
{
  // Terminate operation
  FDC->on_index_ptr = cNull;
  FDC->on_sector_chrn_ptr = cNull;
  FDC->on_sector_data_ptr = cNull;
  FDC->on_end_of_transfer_ptr = cNull;
  FDC->head_on_track_ptr = cNull;
  FDC->on_abort_ptr = cNull;

  // Update Status
  FDC->main_status_register &= ~(MSR_EXM);
  FDC->main_status_register |= (MSR_RQM | MSR_DIO);

  // Prepare result
  FDC->phase = RESULT_PHASE; // switch to result phase
  FDC->result[RES_ST0] = FDC->ST0_register;
  FDC->result[RES_ST1] = FDC->ST1_register;
  FDC->result[RES_ST2] = FDC->ST2_register;
  FDC->result[RES_C]   = FDC->command[CMD_C];
  FDC->result[RES_H]   = FDC->command[CMD_H];
  if (FDC->flags & MULTITRACK_flag)
  {
    // Invert head
    FDC->result[RES_ST0] ^= ST0_HD;
    FDC->result[RES_H] ^= 0x01;
  }
  if (FDC->ST0_register & ST0_NR)
  {
    FDC->result[RES_R]   = FDC->command[CMD_R];
  }
  else
  {
    FDC->result[RES_R]   = FDC->command[CMD_EOT];
  }
  FDC->result[RES_N]   = FDC->command[CMD_N];
  
#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_readtrack_abort(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_readtrack_abort
 *
 ***********************************************************************/
{
  // Update status
  FDC->ST0_register |= (ST0_ATR);

  // Terminate operation
  fdc_readtrack_result(FDC);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_index(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_index
 *
 ***********************************************************************/
{
  FDC->index_count++;

  if (FDC->index_count == 1)
  {
    // Start format first sector
    FDC->active_drive->current_sector = 0;
    FDC->on_sector_chrn_ptr = fdc_format_sector_chrn;
    FDC->active_track->formatted_sectors = 0;
  }
  // End of format operation
  else
  {
    // Prepare result
    fdc_format_result(FDC);

#ifdef ENABLE_METROLOGY
    FDC->active_drive->signal_updated++;
    FDC->active_drive->signal_track_format++;
#endif /* ENABLE_METROLOGY */
  }
}
/*----------------------------------------------------------------------------*/
static tVoid fdc_format_index_alt(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_index_alt
 *
 ***********************************************************************/
{
  FDC->index_count++;

  if (FDC->index_count == 1)
  {
    // Start format first sector
    FDC->active_drive->current_sector = 0;
    FDC->on_sector_chrn_ptr = fdc_format_sector_chrn_alt;
    FDC->active_track->formatted_sectors = 0;

    FDC->active_drive->altered = 1; // indicate that the image has been modified
  }
  // End of format operation
  else if (FDC->active_track->formatted_sectors == FDC->active_track->sectors)
  {
    // Prepare result
    fdc_format_terminate(FDC);
    fdc_format_result(FDC);

#ifdef ENABLE_METROLOGY
    FDC->active_drive->signal_updated++;
    FDC->active_drive->signal_track_format++;
#endif /* ENABLE_METROLOGY */
  }
  // Writting in progress ?
  else if (FDC->head_on_track_ptr == fdc_format_write_sector)
  {
    tUChar loop;
    FDC->multi_revolution_command = cTrue;
    for (loop = 0; loop < (tUChar)FDC->active_track->sectors; loop++) // loop for all sectors
    {
      tSector* sector = &FDC->active_track->sector[loop];
      if (sector->data)
      {
        if (FDC->buffer_count > sector->track_offset)
        {
          FDC->active_track->first_sector_overwritten = FDC->buffer_count - sector->track_offset;
        }
        else
        {
          FDC->active_track->first_sector_overwritten = ((tULong)-1);
        }
        break;
      }
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_sector_chrn(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_chrn_received
 *
 ***********************************************************************/
{
  // First sector to be overwritten ?
  if ( (FDC->active_track->formatted_sectors == 1)
       && (FDC->active_track->first_sector_overwritten) )
  {
    FDC->active_track->first_sector_overwritten = 0;
    FDC->active_drive->current_sector--;
    FDC->active_sector--;
  }

  // Prepare CHRN receive
  FDC->main_status_register |= (MSR_RQM);
  FDC->on_data_register_ptr = fdc_format_receive_sector_C;
  
  // Prepare CHRN CRC
  FDC->temp_crc = FDC->idam_crc;
}
/*----------------------------------------------------------------------------*/
static tVoid fdc_format_sector_chrn_alt(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_sector_chrn_alt
 *
 ***********************************************************************/
{
  // Prepare CHRN receive
  FDC->main_status_register |= (MSR_RQM);
  FDC->on_data_register_ptr = fdc_format_receive_sector_C;

  // Prepare CHRN CRC
  FDC->temp_crc = FDC->idam_crc;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_receive_sector_C(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_receive_sector_C
 *
 ***********************************************************************/
{
  // Store received C
  FDC->active_sector->CHRN[SECTOR_CHRN_C] = FDC->data_register;

  // Update CHRN CRC
  FDC->temp_crc = CRC_CCITT_Add_Byte((tUShort)FDC->temp_crc,
                                     (tUChar)FDC->data_register);

  // Prepare reception of sector H
  FDC->main_status_register |= (MSR_RQM);
  FDC->on_data_register_ptr = fdc_format_receive_sector_H;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_receive_sector_H(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_receive_sector_H
 *
 ***********************************************************************/
{
  // Store received H
  FDC->active_sector->CHRN[SECTOR_CHRN_H] = FDC->data_register;

  // Update CHRN CRC
  FDC->temp_crc = CRC_CCITT_Add_Byte((tUShort)FDC->temp_crc,
                                     (tUChar)FDC->data_register);

  // Prepare reception of sector R
  FDC->main_status_register |= (MSR_RQM);
  FDC->on_data_register_ptr = fdc_format_receive_sector_R;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_receive_sector_R(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_receive_sector_R
 *
 ***********************************************************************/
{
  // Store received R
  FDC->active_sector->CHRN[SECTOR_CHRN_R] = FDC->data_register;
  // Update result C
  FDC->result[RES_C] = FDC->data_register;

  // Update CHRN CRC
  FDC->temp_crc = CRC_CCITT_Add_Byte((tUShort)FDC->temp_crc,
                                     (tUChar)FDC->data_register);

  // Prepare reception of sector N
  FDC->main_status_register |= (MSR_RQM);
  FDC->on_data_register_ptr = fdc_format_receive_sector_N_alt;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_receive_sector_N(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_receive_sector_N
 *
 ***********************************************************************/
{
tULong dwDeclaredSize;
tUChar Loop;

  FDC->on_data_register_ptr = cNull;
  
  // Store received N
  FDC->active_sector->CHRN[SECTOR_CHRN_N] = FDC->data_register;

  // Update CHRN CRC
  FDC->temp_crc = CRC_CCITT_Add_Byte((tUShort)FDC->temp_crc,
                                     (tUChar)FDC->data_register);
  FDC->active_sector->CHRN[SECTOR_CHRN_CRC_MSB] = (FDC->temp_crc >> 8) & 0xff;
  FDC->active_sector->CHRN[SECTOR_CHRN_CRC_LSB] = FDC->temp_crc & 0xff;

  // Wait for data
  FDC->on_sector_data_ptr = fdc_format_prepare_write_sector;

  // Different declared size
  if (FDC->data_register < 6)
  {
    dwDeclaredSize = 128 << FDC->data_register;
  }
  else
  {
    dwDeclaredSize = 8192;
  }

  if (dwDeclaredSize != FDC->active_sector->declared_size)
  {
    tULong dwPreviousTrackHeaderSize;
    tULong dwPreviousTrackSize;
    tULong dwTrackHeaderSize;
    tUChar* TrackEnd = FDC->active_track->data + FDC->active_track->size;
    FDC->active_sector->declared_size = dwDeclaredSize;

    // Update flags
    if (FDC->data_register != FDC->command[CMD_C])
    {
      FDC->active_sector->flags[0] |= ST1_DE;
      FDC->active_sector->flags[1] |= ST2_DD;
    }

    // Get previous track data
    dwPreviousTrackSize = FDC->active_track->size;
    dwPreviousTrackHeaderSize = (FDC->active_track->sectors) ? DSK_TRACKHEADER_SIZE : 0;
    if (FDC->active_track->sectors > DSK_SECTORMAX)
    {
      tUChar sector = FDC->active_track->sectors - DSK_SECTORMAX;
      sector = ((sector / DSK_NBSECTORPERHEADER) + 1) * DSK_NBSECTORPERHEADER;
      dwPreviousTrackHeaderSize += sector * DSK_SECTORID_SIZE;
    }

    // Update all remaining sectors
    for (Loop = (tUChar)(FDC->active_drive->current_sector + 1);
         Loop < (tUChar)FDC->active_track->sectors;
         Loop++)
    {
      tSector* sector = &FDC->active_track->sector[Loop];
      if ((tULong)(TrackEnd - sector->data) < sector->declared_size)
      {
        FDC->active_track->sectors = Loop;
      }
    }

    // Update track header size
    dwTrackHeaderSize = DSK_TRACKHEADER_SIZE;
    if (FDC->active_track->sectors > DSK_SECTORMAX)
    {
      tUChar sector = FDC->active_track->sectors - DSK_SECTORMAX;
      sector = ((sector / DSK_NBSECTORPERHEADER) + 1) * DSK_NBSECTORPERHEADER;
      dwTrackHeaderSize += sector * DSK_SECTORID_SIZE;
    }

    // Update drive size
    if (FDC->active_drive->new_data_size == 0)
    {
      FDC->active_drive->new_data_size = FDC->active_drive->data_size;
    }
    FDC->active_drive->new_data_size =   FDC->active_drive->new_data_size
                                       - dwPreviousTrackSize
                                       - dwPreviousTrackHeaderSize
                                       + FDC->active_track->size
                                       + dwTrackHeaderSize;
  }
}
/*----------------------------------------------------------------------------*/
static tVoid fdc_format_receive_sector_N_alt(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_receive_sector_N_alt
 *
 ***********************************************************************/
{
tULong dwDeclaredSize;
tUChar Loop;

  FDC->on_data_register_ptr = cNull;

  // Store received N
  FDC->active_sector->CHRN[SECTOR_CHRN_N] = FDC->data_register;

  // Update CHRN CRC
  FDC->temp_crc = CRC_CCITT_Add_Byte((tUShort)FDC->temp_crc,
                                     (tUChar)FDC->data_register);
  FDC->active_sector->CHRN[SECTOR_CHRN_CRC_MSB] = (FDC->temp_crc >> 8) & 0xff;
  FDC->active_sector->CHRN[SECTOR_CHRN_CRC_LSB] = FDC->temp_crc & 0xff;

  // Wait for data
  FDC->on_sector_data_ptr = fdc_format_prepare_write_sector;

  // Update declared size
  if (FDC->data_register != FDC->command[CMD_C])
  {
    FDC->active_sector->flags[0] |= ST1_DE;
    FDC->active_sector->flags[1] |= ST2_DD;
  }
  FDC->active_sector->declared_size = 128 << FDC->data_register;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_prepare_write_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_prepare_write_sector
 *
 ***********************************************************************/
{
  // Prepare format
  FDC->buffer_count = FDC->active_sector->size;
  FDC->buffer_ptr = FDC->active_sector->data;
  FDC->buffer_endptr = FDC->active_sector->data + FDC->active_sector->size;
  // Prepare data CRC
  FDC->temp_crc = FDC->dataam_crc;

  // Start periodic write  
  FDC->head_on_track_timer = FDC->sector_timer + FDC_DRQ_CYCLE_TIME;
  FDC->head_on_track_ptr = fdc_format_write_sector;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_write_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_write_sector
 *
 ***********************************************************************/
{
  // Write filler data
  *FDC->buffer_ptr = FDC->command[CMD_N];
  
  // Update Data CRC
  FDC->temp_crc = CRC_CCITT_Add_Byte((tUShort)FDC->temp_crc,
                                     (tUChar)FDC->command[CMD_N]);

  // Another byte to write ?
  FDC->buffer_count--;
  if (FDC->buffer_count)
  {
    // Prepare next byte content
    FDC->buffer_ptr++;
    if (FDC->buffer_ptr >= FDC->buffer_endptr)
    {
      FDC->buffer_ptr = FDC->active_track->data; // wrap around
    }

    // Prepare timing to next data.
    FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Update sector Data CRC
    FDC->active_sector->data_crc[SECTOR_CRC_MSB] = (FDC->temp_crc >> 8) & 0xff;
    FDC->active_sector->data_crc[SECTOR_CRC_LSB] = FDC->temp_crc & 0xff;

    FDC->active_sector->empty = cTrue;

    // Increment result C
    FDC->result[RES_C]++;

    // Increment formatted sectors
    FDC->active_track->formatted_sectors++;

    // First sector overwritten ?
    if (FDC->active_track->first_sector_overwritten != ((tULong)-1))
    {
      // Remove first sector data
      tUChar loop;
      for (loop = 0; loop < (tUChar)FDC->active_track->sectors; loop++) // loop for all sectors
      {
        tSector* sector = &FDC->active_track->sector[loop];
        if (sector->data)
        {
          FDC->DeleteMemFct(sector->data);
          sector->data = cNull;
          break;
        }
      }
    }

    //
    // Wait for next index detection
    //
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_terminate(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_terminate
 *
 ***********************************************************************/
{
tUChar* dataP;
tULong dwSectorSize;
tULong dwTrackHeaderSize;
tULong SectorHeaderSize;
tULong SectorLoop, FormattedSectorLoop;

  // Free memory of not formatted sectors
  if (FDC->active_track->formatted_sectors < FDC->active_track->sectors)
  {
    for (SectorLoop = FDC->active_track->formatted_sectors;
         SectorLoop < FDC->active_track->sectors;
         SectorLoop++)
    {
      tSector* current_sector = &FDC->active_track->sector[SectorLoop];
      if (current_sector->new_data)
      {
        FDC->DeleteMemFct(current_sector->new_data);
        current_sector->new_data = cNull;
        current_sector->data = cNull;
      }
    }
  }

  // Count formatted sectors
  FDC->active_track->formatted_sectors = 0;
  for (SectorLoop = 0;
       SectorLoop < FDC->active_track->sectors;
       SectorLoop++)
  {
    if (FDC->active_track->sector[SectorLoop].data != cNull)
    {
      FDC->active_track->formatted_sectors++;
    }
  }

  // No formatted sectors ?
  if (!FDC->active_track->formatted_sectors)
  {
    // remove track format
    FDC->active_track->sectors = 0;
    FDC->active_track->data = cNull;
    FDC->active_track->size = 0;
  }
  else
  {
		tULong sector;
		FDC->active_track->new_size = 0;
		for (sector = 0; sector < FDC->active_track->sectors; sector++) // loop for all sectors
		{
			tSector* current_sector = &FDC->active_track->sector[sector];
			FDC->active_track->new_size += current_sector->declared_size;
		}
		if (FDC->active_track->new_size > ROUND_UPPER_256(FDC_MAX_BYTES_PER_SECTOR))
		{
			FDC->active_track->new_size = ROUND_UPPER_256(FDC_MAX_BYTES_PER_SECTOR);
		}

    // Allocate memory for new track
    FDC->active_track->new_data = FDC->NewMemFct(FDC->active_track->new_size);
    if (FDC->active_track->new_data == cNull)
    {
      // Stop format, simulate disk not writable
      FDC->ST0_register |= (ST0_AT);
      FDC->ST1_register |= (ST1_NW);
    }
    else
    {
      // Use new track
      dwSectorSize = 128 << (FDC->command[CMD_C] & 7);
      FDC->active_track->data = FDC->active_track->new_data;
      FDC->active_track->size = FDC->active_track->new_size;
      FDC->active_track->real_size = FDC_BYTES_FROM_INDEX_TO_GAP1;

      //
      // Keep formatted sectors only
      //
      dataP = FDC->active_track->data;
      for (FormattedSectorLoop = 0;
           FormattedSectorLoop < FDC->active_track->formatted_sectors;
           FormattedSectorLoop++) // loop for all formatted sectors
      {
        tSector* formatted_sector = &FDC->active_track->sector[FormattedSectorLoop];

        for (SectorLoop = FormattedSectorLoop;
             SectorLoop < FDC->active_track->sectors;
             SectorLoop++) // loop for all sectors
        {
          // Formatted sector ?
          tSector* current_sector = &FDC->active_track->sector[SectorLoop];
          if (current_sector->new_data != cNull)
          {
            tULong RemainingSize = FDC->active_track->size - (dataP - FDC->active_track->data);
            tULong CopySize = dwSectorSize > RemainingSize ? RemainingSize : dwSectorSize;

            // Copy sector info
            if (current_sector->new_data != formatted_sector->new_data)
            {
              MemMoveByte((tUChar*)formatted_sector, (const tUChar*)current_sector, sizeof(tSector));
              current_sector->new_data = cNull;
              current_sector->data = cNull;
            }

            // Copy sector data
            MemMoveByte(dataP, (const tUChar*)formatted_sector->new_data, CopySize);
            // Free formatted sector data
            FDC->DeleteMemFct(formatted_sector->new_data);
            formatted_sector->new_data = cNull;
            formatted_sector->data = dataP;

            // Update pointer on track
            if (CopySize == RemainingSize)
            {
              dataP = FDC->active_track->data;
            }
            else
            {
              dataP += CopySize;
            }
            break;
          }
        }

        // Update track real size
        FDC->active_track->real_size +=   FDC_BYTES_FROM_GAP1_TO_CHRN
                                        + FDC_BYTES_FROM_CHRN_TO_DATA
                                        + formatted_sector->data_size
                                        + FDC_BYTES_CRC + formatted_sector->info_gap3_length;
      }
    }

    // Create declared data
    for (SectorLoop = 0;
         SectorLoop < FDC->active_track->sectors;
         SectorLoop++)
    {
      tSector* current_sector = &FDC->active_track->sector[SectorLoop];
      // Create declared data
      if (current_sector->data_size != current_sector->declared_size)
      {
        current_sector->declared_data = FDC->NewMemFct(current_sector->declared_size);
        if (current_sector->declared_data != cNull)
        {
          CreateDeclaredSizeSector(FDC->active_track, SectorLoop);
        }
      }
    }

    // Update drive size
    dwTrackHeaderSize = DSK_TRACKHEADER_SIZE;
    if (FDC->active_track->sectors > DSK_SECTORMAX)
    {
      tUChar sector = FDC->active_track->sectors - DSK_SECTORMAX;
      sector = ((sector / DSK_NBSECTORPERHEADER) + 1) * DSK_NBSECTORPERHEADER;
      dwTrackHeaderSize += sector * DSK_SECTORID_SIZE;
    }
    FDC->active_drive->new_data_size += FDC->active_track->size + dwTrackHeaderSize;

    // Update drive tracks count
    if ((FDC->active_drive->current_track+1) > FDC->active_drive->tracks)
    {
      FDC->active_drive->tracks = FDC->active_drive->current_track+1;
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_result(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_result
 *
 ***********************************************************************/
{
  // Terminate operation
  FDC->on_index_ptr = cNull;
  FDC->on_index_end_operation_ptr = cNull;
  FDC->on_sector_chrn_ptr = cNull;
  FDC->on_abort_ptr = cNull;

  // Update Status
  FDC->main_status_register &= ~(MSR_EXM);
  FDC->main_status_register |= (MSR_RQM | MSR_DIO);

  // Prepare result
  FDC->phase = RESULT_PHASE; // switch to result phase
  FDC->result[RES_ST0] = FDC->ST0_register;
  FDC->result[RES_ST1] = FDC->ST1_register;
  FDC->result[RES_ST2] = FDC->ST2_register;
  FDC->result[RES_C]   = FDC->active_drive->current_track;
  FDC->result[RES_H]   = 0x04;
  if (FDC->active_track)
  {
    FDC->result[RES_R]   = FDC->active_track->formatted_sectors;
  }
  else
  {
    FDC->result[RES_R]   = 0;
  }
  FDC->result[RES_N]   = FDC->command[CMD_C];

#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_format_abort(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_abort
 *
 ***********************************************************************/
{
  // Update status
  FDC->ST0_register |= (ST0_ATR);

  // Terminate operation
  fdc_format_result(FDC);
}
/*----------------------------------------------------------------------------*/
static tVoid fdc_format_abort_alt(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_format_abort_alt
 *
 ***********************************************************************/
{
  // Update status
  FDC->ST0_register |= (ST0_ATR);

  // First sector overwritten ?
  if ( (FDC->active_track->first_sector_overwritten != ((tULong)-1))
       && (FDC->buffer_count < FDC->active_track->first_sector_overwritten) )
  {
    // Remove first sector data
    tUChar loop;
    for (loop = 0; loop < (tUChar)FDC->active_track->sectors; loop++) // loop for all sectors
    {
      tSector* sector = &FDC->active_track->sector[loop];
      if (sector->data)
      {
        FDC->DeleteMemFct(sector->data);
        sector->data = NULL;
        break;
      }
    }
  }

  // Writting in progress ?
  if (FDC->buffer_count)
  {
    // Set errors
    FDC->active_sector->flags[0] |= ST1_DE;
    FDC->active_sector->flags[1] |= ST2_DD;
    // Update sector size
    FDC->active_sector->size = FDC->active_sector->declared_size - FDC->buffer_count;
    FDC->active_sector->data_size = FDC->active_sector->size;

    // Increment formatted sectors
    FDC->active_track->formatted_sectors++;
  }

#ifdef ENABLE_METROLOGY
  FDC->active_drive->signal_updated++;
  FDC->active_drive->signal_track_format++;
#endif /* ENABLE_METROLOGY */

  // Terminate operation
  fdc_format_terminate(FDC);
  fdc_format_result(FDC);
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_scan_index(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_scan_index
 *
 ***********************************************************************/
{
  FDC->index_count++;

  if (    (FDC->index_count == 2)
       && (FDC->on_end_of_transfer_ptr == cNull) )
  {
    // Update status
    FDC->ST0_register |= ST0_AT;
    FDC->ST1_register |= ST1_ND;

    // Prepare result
    fdc_scan_result(FDC);
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_scan_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_scan_sector
 *
 ***********************************************************************/
{
tSector* sector;

  sector = FDC->active_sector;

  if (FDC->command[CMD_C] != (tULong)sector->CHRN[SECTOR_CHRN_C])
  {
    // Wrong cylinder
    FDC->ST2_register |= (ST2_WC);

    if (sector->CHRN[SECTOR_CHRN_C] == 0xff)
    {
      // Bad cylinder
      FDC->ST2_register |= (ST2_BC);
    }
  }
  // Matching CHRN ?
	else if ( 	 (FDC->command[CMD_H] == (tULong)sector->CHRN[SECTOR_CHRN_H])
            && (FDC->command[CMD_R] == (tULong)sector->CHRN[SECTOR_CHRN_R])
            && (FDC->command[CMD_N] == (tULong)sector->CHRN[SECTOR_CHRN_N]) )
  {
		// Reset Wrong cylinder and bad cylinder flags
		FDC->ST2_register &= ~(ST2_WC | ST2_BC);

    // Update Status
    FDC->ST1_register |= sector->flags[0] & (ST1_DE | ST1_ND | ST1_MA);
		FDC->ST2_register |= sector->flags[1] & (ST2_CM | ST2_DD | ST2_MD | ST2_WC | ST2_BC);
		// At start, sector has been found.
		FDC->ST2_register &= ~(ST2_SN);

    // Data present ?
    if ( ((sector->flags[0] & (ST1_ND | ST1_MA)) == 0)
         && ((sector->flags[1] & (ST2_MD)) == 0) )
    {
			// Prepare scan
			FDC->buffer_ptr = sector->data;
		  FDC->buffer_endptr = FDC->active_track->data + FDC->active_track->size; // pointer beyond end of track data
			if (FDC->command[CMD_N] == 0) // use DTL for length?
			{
				FDC->requested_sector_size = FDC->command[CMD_DTL]; // size of sector is defined by DTL value

				if (FDC->requested_sector_size > 0x80)
				{
					FDC->requested_sector_size = 0x80; // max DTL value is 128
				}
			}
			else
			{
				FDC->requested_sector_size = 128 << FDC->command[CMD_N]; // determine number of bytes from N value
			}

      // Handle weak sectors (different data to emulate weak bits)
      if (sector->weak_count > 1)
      {
        FDC->weak_counter = (FDC->weak_counter + 1) % sector->weak_count;
        FDC->buffer_ptr += sector->declared_size * FDC->weak_counter;
      }

      FDC->buffer_count = sector->data_size;
      FDC->next_track_count = 0;

      // Prepare timing until begin of data.
			FDC->requested_read_count = 0;
			FDC->on_sector_data_ptr = fdc_scan_sector_begin;
			FDC->on_end_of_transfer_ptr = fdc_scan_end_sector;
		}
    else // No Data
    {
      // Update Status
      FDC->ST0_register |= (ST0_AT);

      // Terminate operation
      fdc_scan_result(FDC);
    }
	}
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_scan_sector_begin(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_scan_sector_begin
 *
 *  Just before first DATA byte
 *
 ***********************************************************************/
{
  // Sector has been found
  FDC->on_index_ptr = cNull;
  FDC->on_sector_chrn_ptr = cNull;

	// Read first byte
	FDC->data_transfer_ack = 1; // No overrun for the first byte
	FDC->head_on_track_timer = FDC->sector_timer + FDC_DRQ_CYCLE_TIME;

	// SCAN "Equal" requested
	if (FDC->command[CMD_CODE] == 0x51)
	{
		FDC->head_on_track_ptr = fdc_sector_data_scan_equal;
	}
	// SCAN "Low or Equal" requested
	else if (FDC->command[CMD_CODE] == 0x59)
	{
		FDC->head_on_track_ptr = fdc_sector_data_scan_low_equal;
	}
	// SCAN "High or Equal" requested
  else
	{
		FDC->head_on_track_ptr = fdc_sector_data_scan_high_equal;
	}

	// Request first byte from CPU
	FDC->main_status_register &= ~(MSR_DIO);
	FDC->main_status_register |= (MSR_RQM);

	// At start, scan is OK
	FDC->ST2_register |= ST2_SH;
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_sector_data_scan_equal(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_sector_data_scan_equal
 *
 *  Byte has been read by FDC and should now be read by CPU to be checked.
 *
 ***********************************************************************/
{
	// Overrun ?
	FDC_OVERRUN_CHECK;

	// Check data
	if ((tUChar)FDC->data_register != (tUChar)*FDC->buffer_ptr)
	{
		// Condition not satisfied
		FDC->ST2_register &= ~(ST2_SH);
	}

  // Another byte to scan ?
  FDC->buffer_count--;
  if (FDC->buffer_count)
  {
		// Request another byte from CPU
		FDC->main_status_register |= (MSR_RQM);

    // Prepare next byte content
    if (FDC->buffer_ptr < FDC->buffer_endptr)
    {
      FDC->buffer_ptr++;
    }

    // Prepare timing to next data.
    FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_sector_data_scan_low_equal(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_sector_data_scan_low_equal
 *
 *  Byte has been read by FDC and should now be read by CPU to be checked.
 *
 ***********************************************************************/
{
	// Overrun ?
	FDC_OVERRUN_CHECK;

	// Check data
	if ((tUChar)FDC->data_register > (tUChar)*FDC->buffer_ptr)
	{
		// Condition not satisfied
		FDC->ST2_register &= ~(ST2_SH);
	}

  // Another byte to scan ?
  FDC->buffer_count--;
  if (FDC->buffer_count)
  {
		// Request another byte from CPU
		FDC->main_status_register |= (MSR_RQM);

    // Prepare next byte content
    if (FDC->buffer_ptr < FDC->buffer_endptr)
    {
      FDC->buffer_ptr++;
    }

    // Prepare timing to next data.
    FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_sector_data_scan_high_equal(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_sector_data_scan_high_equal
 *
 *  Byte has been read by FDC and should now be read by CPU to be checked.
 *
 ***********************************************************************/
{
	// Overrun ?
	FDC_OVERRUN_CHECK;

	// Check data
	if ((tUChar)FDC->data_register < (tUChar)*FDC->buffer_ptr)
	{
		// Condition not satisfied
		FDC->ST2_register &= ~(ST2_SH);
	}

  // Another byte to scan ?
  FDC->buffer_count--;
  if (FDC->buffer_count)
  {
		// Request another byte from CPU
		FDC->main_status_register |= (MSR_RQM);

    // Prepare next byte content
    if (FDC->buffer_ptr < FDC->buffer_endptr)
    {
      FDC->buffer_ptr++;
    }

    // Prepare timing to next data.
    FDC->head_on_track_timer += FDC_DRQ_CYCLE_TIME;
  }
  else
  {
    FDC->head_on_track_ptr = cNull;

    // Signal end of transfer
    if (FDC->on_end_of_transfer_ptr != cNull)
    {
      FDC->on_end_of_transfer_ptr(FDC);
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_scan_end_sector(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_scan_end_sector
 *
 ***********************************************************************/
{
  // Stop transfer
  FDC->on_end_of_transfer_ptr = cNull;
  FDC->head_on_track_ptr = cNull;

	// Update Status
	FDC->ST2_register &= ~(ST2_CM); // Remove Control Mark detected

	// End Of Track ?
	if (FDC->end_of_track_detected)
	{
		// Prepare result
    fdc_scan_result(FDC);
	}
	else
	{
		// Seek next sector
		FDC->command[CMD_R] += FDC->command[CMD_STP];
		FDC->index_count = 0;
		FDC->on_index_ptr = fdc_scan_index;
		FDC->on_sector_chrn_ptr = fdc_scan_sector;
	}
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_scan_result(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_scan_result
 *
 ***********************************************************************/
{
	// Terminate operation
	FDC->on_index_ptr = cNull;
  FDC->on_sector_chrn_ptr = cNull;
  FDC->on_abort_ptr = cNull;

  // Update Status
  FDC->main_status_register &= ~(MSR_EXM);
  FDC->main_status_register |= (MSR_RQM | MSR_DIO);

  // Prepare result
  FDC->phase = RESULT_PHASE; // switch to result phase
  FDC->result[RES_ST0] = FDC->ST0_register;
  FDC->result[RES_ST1] = FDC->ST1_register;
  FDC->result[RES_ST2] = FDC->ST2_register;
  FDC->result[RES_C]   = FDC->active_sector->CHRN[SECTOR_CHRN_C];
  FDC->result[RES_H]   = FDC->active_sector->CHRN[SECTOR_CHRN_H];
  FDC->result[RES_R]   = FDC->active_sector->CHRN[SECTOR_CHRN_R];
  FDC->result[RES_N]   = FDC->active_sector->CHRN[SECTOR_CHRN_N];
  
#ifdef ENABLE_METROLOGY
  FDC->signal_updated++;
  FDC->signal_command_result++;
  FDC->signal_phase = FDC->phase;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tVoid fdc_scan_abort(tFDC* FDC)
/***********************************************************************
 *
 *  fdc_scan_abort
 *
 ***********************************************************************/
{
  // Update status
  FDC->ST0_register |= (ST0_ATR);

  // Terminate operation
  fdc_read_result(FDC);
}
/*----------------------------------------------------------------------------*/


//
// TAPE
//

#ifdef ENABLE_TAPE

tVoid Tape_GetCycleCount(tTape* Tape)
/***********************************************************************
 *
 *  Tape_GetCycleCount
 *
 ***********************************************************************/
{
  Tape->dwTapePulseCycles = CYCLE_ADJUST(*Tape->pwTapePulseTablePtr++);
#ifdef USE_TAPE_SPEED_ADJUST
  Tape->dwTapePulseCycles = Tape->dwTapePulseCycles * 100 / Tape->dwTapeSpeedAdjust;
#endif /* USE_TAPE_SPEED_ADJUST */

  // Back to begin of pulse table
  if (Tape->pwTapePulseTablePtr >= Tape->pwTapePulseTableEnd)
  {
    Tape->pwTapePulseTablePtr = Tape->pwTapePulseTable;
  }
}
/*----------------------------------------------------------------------------*/


tLong Tape_ReadDataBit(tTape* Tape)
/***********************************************************************
 *
 *  Tape_ReadDataBit
 *
 ***********************************************************************/
{
  // no more data ?
  if (!Tape->dwTapeDataCount) return 0;

  if (!Tape->dwTapeBitsToShift)
  {
    Tape->bTapeData = *(Tape->pbTapeBlockData++); // get the next data byte
    Tape->dwTapeBitsToShift = 8;
  }

  if (Tape->bTapeData & 0x80)
  {
    Tape->dwTapePulseCycles = Tape->dwTapeOnePulseCycles;
  }
  else
  {
    Tape->dwTapePulseCycles = Tape->dwTapeZeroPulseCycles;
  }
#ifdef USE_TAPE_SPEED_ADJUST
  Tape->dwTapePulseCycles = Tape->dwTapePulseCycles * 100 / Tape->dwTapeSpeedAdjust;
#endif /* USE_TAPE_SPEED_ADJUST */

  Tape->bTapeData <<= 1;
  Tape->dwTapeBitsToShift--;
  Tape->dwTapeDataCount--;

  Tape->dwTapePulseCount = 2; // two pulses = one bit

  return 1;
}
/*----------------------------------------------------------------------------*/


tLong Tape_ReadSampleDataBit(tTape* Tape)
/***********************************************************************
 *
 *  Tape_ReadSampleDataBit
 *
 ***********************************************************************/
{
  // no more data ?
  if (!Tape->dwTapeDataCount) return 0;

  if (!Tape->dwTapeBitsToShift)
  {
    Tape->bTapeData = *(Tape->pbTapeBlockData++); // get the next data byte
    Tape->dwTapeBitsToShift = 8;
  }

  if (Tape->bTapeData & 0x80)
  {
    Tape->bTapeLevel = TAPE_LEVEL_HIGH; // set high level
  }
  else
  {
    Tape->bTapeLevel = TAPE_LEVEL_LOW; // set low level
  }

  Tape->bTapeData <<= 1;
  Tape->dwTapeBitsToShift--;
  Tape->dwTapeDataCount--;

  Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level

  return 1;
}
/*----------------------------------------------------------------------------*/

static tVoid Tape_BlockDone(tTape* Tape)
/***********************************************************************
 *
 *  Tape_BlockDone
 *
 ***********************************************************************/
{
  if (Tape->pbTapeBlock < Tape->pbTapeImageEnd)
  {
    // Pass current block
    switch (*Tape->pbTapeBlock)
    {
      case 0x10: // standard speed data block
      {
        Tape->pbTapeBlock += *(tUShort*)(Tape->pbTapeBlock+0x01+0x02) + 0x04 + 1;
      }
      break;

      case 0x11: // turbo loading data block
      {
        Tape->pbTapeBlock += (*(tULong*)(Tape->pbTapeBlock+0x01+0x0f) & 0x00ffffff) + 0x12 + 1;
      }
      break;

      case 0x12: // pure tone
      {
        Tape->pbTapeBlock += 4 + 1;
      }
      break;

      case 0x13: // sequence of pulses of different length
      {
        Tape->pbTapeBlock += *(Tape->pbTapeBlock+0x01) * 2 + 1 + 1;
      }
      break;

      case 0x14: // pure data block
      {
        Tape->pbTapeBlock += (*(tULong*)(Tape->pbTapeBlock+0x01+0x07) & 0x00ffffff) + 0x0a + 1;
      }
      break;

      case 0x15: // direct recording
      {
        Tape->pbTapeBlock += (*(tULong*)(Tape->pbTapeBlock+0x01+0x05) & 0x00ffffff) + 0x08 + 1;
      }
      break;

      case 0x20: // pause
      {
        Tape->pbTapeBlock += 2 + 1;
      }
      break;
    }

    Tape->current_block++;

    // No more blocks ?
    if (!Engine_TapeGetNextBlock(Tape))
    {
      Tape->dwTapeStage = TAPE_END;
      Tape->tape_play_button = 0;
    }
  }
}
/*----------------------------------------------------------------------------*/


static tVoid Tape_UpdateLevel(tTape* Tape)
/***********************************************************************
 *
 *  Tape_UpdateLevel
 *
 ***********************************************************************/
{
  switch (Tape->dwTapeStage)
  {
    case TAPE_PILOT_STAGE:
    {
      // Switch Level
      Tape->bTapeLevel = Tape->bTapeLevel ? TAPE_LEVEL_LOW : TAPE_LEVEL_HIGH;
      Tape->dwTapePulseCount--;
      if (Tape->dwTapePulseCount > 0) // is the pilot tone still playing?
      {
        Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
      }
      else // finished with the pilot tone
      {
        switch (*Tape->pbTapeBlock)
        {
          case 0x10: // standard speed data block
          {
            Tape->dwTapeStage = TAPE_SYNC_STAGE;
            Tape->wCycleTable[0] = 667;
            Tape->wCycleTable[1] = 735;
            Tape->pwTapePulseTable =
            Tape->pwTapePulseTablePtr = &Tape->wCycleTable[0];
            Tape->pwTapePulseTableEnd = &Tape->wCycleTable[2];
            Tape_GetCycleCount(Tape);
            Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
            Tape->dwTapePulseCount = 2;
          }
          break;

          case 0x11: // turbo loading data block
          {
            Tape->dwTapeStage = TAPE_SYNC_STAGE;
            Tape->pwTapePulseTable =
            Tape->pwTapePulseTablePtr = (tUShort*)(Tape->pbTapeBlock+0x01+0x02);
            Tape->pwTapePulseTableEnd = (tUShort*)(Tape->pbTapeBlock+0x01+0x06);
            Tape_GetCycleCount(Tape);
            Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
            Tape->dwTapePulseCount = 2;
          }
          break;

          case 0x12: // pure tone
          {
            Tape_BlockDone(Tape);
          }
          break;
        }
      }
    }
    break;

    case TAPE_SYNC_STAGE:
    {
      // Switch Level
      Tape->bTapeLevel = Tape->bTapeLevel ? TAPE_LEVEL_LOW : TAPE_LEVEL_HIGH;
      Tape->dwTapePulseCount--;
      if (Tape->dwTapePulseCount > 0)
      {
        Tape_GetCycleCount(Tape);
        Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
      }
      else
      {
        switch (*Tape->pbTapeBlock)
        {
          case 0x10: // standard speed data block
          {
            Tape->dwTapeStage = TAPE_DATA_STAGE;
            Tape->dwTapeZeroPulseCycles = CYCLE_ADJUST(855); // pulse length for a zero bit
            Tape->dwTapeOnePulseCycles = CYCLE_ADJUST(1710); // pulse length for a one bit
            Tape->dwTapeDataCount = *(tUShort*)(Tape->pbTapeBlock+0x01+0x02) << 3; // byte count * 8 bits;
            Tape->pbTapeBlockData = Tape->pbTapeBlock+0x01+0x04; // pointer to the tape data
            Tape->dwTapeBitsToShift = 0;
            Tape_ReadDataBit(Tape);
            Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
          }
          break;

          case 0x11: // turbo loading data block
					{
						Tape->dwTapeStage = TAPE_DATA_STAGE;
						Tape->dwTapeZeroPulseCycles = CYCLE_ADJUST(*(tUShort*)(Tape->pbTapeBlock+0x01+0x06)); // pulse length for a zero bit
						Tape->dwTapeOnePulseCycles = CYCLE_ADJUST(*(tUShort*)(Tape->pbTapeBlock+0x01+0x08)); // pulse length for a one bit
						Tape->dwTapeDataCount = (*(tULong*)(Tape->pbTapeBlock+0x01+0x0f) & 0x00ffffff);
						if (Tape->dwTapeDataCount)
						{
							Tape->dwTapeDataCount = (Tape->dwTapeDataCount - 1) << 3; // (byte count - 1) * 8 bits;
							Tape->dwTapeDataCount += *(Tape->pbTapeBlock+0x01+0x0c); // add the number of bits in the last data byte
						}
						Tape->pbTapeBlockData = Tape->pbTapeBlock+0x01+0x12; // pointer to the tape data
						Tape->dwTapeBitsToShift = 0;
						Tape_ReadDataBit(Tape);
						Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
					}
          break;

          case 0x13: // sequence of pulses of different length
          {
            Tape_BlockDone(Tape);
          }
          break;
        }
      }
    }
    break;

    case TAPE_DATA_STAGE:
    {
      // Switch Level
      Tape->bTapeLevel = Tape->bTapeLevel ? TAPE_LEVEL_LOW : TAPE_LEVEL_HIGH;
      if (Tape->dwTapePulseCount)
      {
        Tape->dwTapePulseCount--;
      }
      if (Tape->dwTapePulseCount > 0)
      {
        Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
      }
      else
      {
        if (Tape_ReadDataBit(Tape))
        {
          Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
        }
        else
        {
          switch (*Tape->pbTapeBlock)
          {
            case 0x10: // standard speed data block
            {
              if (*(tUShort*)(Tape->pbTapeBlock+0x01)) // was a pause requested?
              {
                Tape->dwTapeStage = TAPE_PAUSE_STAGE;
                Tape->dwTapePulseCycles = TAPE_PAUSE_DELAY; // start with a level opposite to the one last played
#ifdef USE_TAPE_SPEED_ADJUST
                Tape->dwTapePulseCycles = Tape->dwTapePulseCycles * 100 / Tape->dwTapeSpeedAdjust;
#endif /* USE_TAPE_SPEED_ADJUST */
                Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
                Tape->dwTapePulseCycles = MS_TO_CYCLES(*(tUShort*)(Tape->pbTapeBlock+0x01) - 1); // pause in ms
#ifdef USE_TAPE_SPEED_ADJUST
                Tape->dwTapePulseCycles = Tape->dwTapePulseCycles * 100 / Tape->dwTapeSpeedAdjust;
#endif /* USE_TAPE_SPEED_ADJUST */
                Tape->dwTapePulseCount = 2; // just one pulse
              }
              else
              {
                Tape_BlockDone(Tape);
              }
            }
            break;

            case 0x11: // turbo loading data block
            {
              if (*(tUShort*)(Tape->pbTapeBlock+0x01+0x0d)) // was a pause requested?
              {
                Tape->dwTapeStage = TAPE_PAUSE_STAGE;
                Tape->dwTapePulseCycles = TAPE_PAUSE_DELAY; // start with a level opposite to the one last played
#ifdef USE_TAPE_SPEED_ADJUST
                Tape->dwTapePulseCycles = Tape->dwTapePulseCycles * 100 / Tape->dwTapeSpeedAdjust;
#endif /* USE_TAPE_SPEED_ADJUST */
                Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
                Tape->dwTapePulseCycles = MS_TO_CYCLES(*(tUShort*)(Tape->pbTapeBlock+0x01+0x0d) - 1); // pause in ms
#ifdef USE_TAPE_SPEED_ADJUST
                Tape->dwTapePulseCycles = Tape->dwTapePulseCycles * 100 / Tape->dwTapeSpeedAdjust;
#endif /* USE_TAPE_SPEED_ADJUST */
                Tape->dwTapePulseCount = 2; // just one pulse
              }
              else
              {
                Tape_BlockDone(Tape);
              }
            }
            break;

            case 0x14: // pure data block
            {
              if (*(tUShort*)(Tape->pbTapeBlock+0x01+0x05)) // was a pause requested?
              {
                Tape->dwTapeStage = TAPE_PAUSE_STAGE;
                Tape->dwTapePulseCycles = TAPE_PAUSE_DELAY; // start with a level opposite to the one last played
#ifdef USE_TAPE_SPEED_ADJUST
                Tape->dwTapePulseCycles = Tape->dwTapePulseCycles * 100 / Tape->dwTapeSpeedAdjust;
#endif /* USE_TAPE_SPEED_ADJUST */
                Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
                Tape->dwTapePulseCycles = MS_TO_CYCLES(*(tUShort*)(Tape->pbTapeBlock+0x01+0x05) - 1); // pause in ms
#ifdef USE_TAPE_SPEED_ADJUST
                Tape->dwTapePulseCycles = Tape->dwTapePulseCycles * 100 / Tape->dwTapeSpeedAdjust;
#endif /* USE_TAPE_SPEED_ADJUST */
                Tape->dwTapePulseCount = 2; // just one pulse
              }
              else
              {
                Tape_BlockDone(Tape);
              }
            }
            break;

            default:
            {
              Tape_BlockDone(Tape);
            }
            break;
          }
        }
      }
    }
    break;

    case TAPE_SAMPLE_DATA_STAGE:
    {
      if (!Tape_ReadSampleDataBit(Tape))
      {
        if (*(tUShort*)(Tape->pbTapeBlock+0x01+0x02)) // was a pause requested?
        {
          Tape->dwTapeStage = TAPE_PAUSE_STAGE;
          Tape->dwTapePulseCycles = TAPE_PAUSE_DELAY; // start with a level opposite to the one last played
#ifdef USE_TAPE_SPEED_ADJUST
          Tape->dwTapePulseCycles = Tape->dwTapePulseCycles * 100 / Tape->dwTapeSpeedAdjust;
#endif /* USE_TAPE_SPEED_ADJUST */
          Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
          Tape->dwTapePulseCycles = MS_TO_CYCLES(*(tUShort*)(Tape->pbTapeBlock+0x01+0x02) - 1); // pause in ms
#ifdef USE_TAPE_SPEED_ADJUST
          Tape->dwTapePulseCycles = Tape->dwTapePulseCycles * 100 / Tape->dwTapeSpeedAdjust;
#endif /* USE_TAPE_SPEED_ADJUST */
          Tape->dwTapePulseCount = 2; // just one pulse
        }
        else
        {
          Tape_BlockDone(Tape);
        }
      }
    }
    break;

    case TAPE_PAUSE_STAGE:
    {
      Tape->dwTapePulseCount--;

      if (Tape->dwTapePulseCount)
      {
        Tape->bTapeLevel = TAPE_LEVEL_LOW;
      }
#ifndef TAPE_PATCH_1
      else
#else /* TAPE_PATCH_1 */
      else if (Tape->bPPICommand)
#endif /* TAPE_PATCH_1 */
      {
        // Invert polarity
        Tape->bTapeLevel = Tape->bTapeLevel ? TAPE_LEVEL_LOW : TAPE_LEVEL_HIGH;
      }

      if (Tape->dwTapePulseCount)
      {
        Tape->iTapeCycleCount += (tLong)Tape->dwTapePulseCycles; // set cycle count for current level
      }
      else
      {
        Tape_BlockDone(Tape);
      }
    }
    break;

    case TAPE_END:
    {
      Tape->tape_play_button = 0;
    }
    break;
  }
}
/*----------------------------------------------------------------------------*/


static tVoid Tape_UpdateCSWLevel(tTape* Tape)
/***********************************************************************
 *
 *  Tape_UpdateCSWLevel
 *
 ***********************************************************************/
{
  // Switch Level
  Tape->bTapeLevel = Tape->bTapeLevel ? TAPE_LEVEL_LOW : TAPE_LEVEL_HIGH;

  // Next pulse ?
  if (Tape->pbCSWData < Tape->pbCSWEnd)
  {
    Tape->csw_nbsamples = *Tape->pbCSWData++;
    
    // Too many pulses
    if (!Tape->csw_nbsamples)
    {
      if ((Tape->pbCSWEnd - Tape->pbCSWData) >= 4)
      {
        // Read 4 bytes sample count.
        Tape->csw_nbsamples =   ((*Tape->pbCSWData++) << 0)
                              + ((*Tape->pbCSWData++) << 8)
                              + ((*Tape->pbCSWData++) << 16)
                              + ((*Tape->pbCSWData++) << 24);
      }
      else
      {
        // Stop Play
        Tape->tape_play_button = 0;
      }
    }
  }
  else
  {
    // Stop Play
    Tape->tape_play_button = 0;
  }
}
/*----------------------------------------------------------------------------*/

#endif /* ENABLE_TAPE */


//
// TMPI SPEECH SYNTHESIS
//

#ifdef ENABLE_TMPI_SPEECH

/**********************************************************************

  Copyright (C) Antoine Mine' 2006

  Philips / Signetics MEA 8000 emulation.

  The MEA 8000 is a speech synthesis chip.
  The French company TMPI (Techni-musique & parole informatique) provided
  speech extensions for several 8-bit computers (Thomson, Amstrad, Oric).
  It was quite popular in France because of its ability to spell 'u' 
  (unlike the more widespread SPO 296 chip).

  The synthesis is based on a 4-formant model.
  First, an initial sawtooth noise signal is generated.
  The signal passes through a cascade of 4 filters of increasing frequency.
  Each filter is a second order digital filter with a programmable
  frequency and bandwidth.
  All parameters, including filter parameters, are smoothly interpolated
  for the duration of a frame (8ms, 16ms, 32ms, or 64 ms).

  IMPORTANT NOTE:
  Emulation is not satisfactory and needs some work!
  Control logics & quantisation tables seem OK.
  However, the voice is terrible, you can barely recognise the words.
  It may be a problem with the interpolation or the filtering logics.

  TODO:
  - improve accuracy and sound quality!!!
  - REQ output pin
  - optimize mea8000_compute_sample
  - should we accept new frames in slow-stop mode ?

**********************************************************************/


static tULong tmpi_read_status(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_read_status
 *
 ***********************************************************************/
{
tULong Status;

  if ( (TMPISpeech->state == MEA8000_STOPPED)
       || (TMPISpeech->state == MEA8000_WAIT_FIRST)
       || ( (TMPISpeech->state == MEA8000_STARTED) && (TMPISpeech->bufpos < 4) ) )
  {
    // Only D7
    Status = 0x80;
  }
  else
  {
    Status = 0;
  }

#ifdef ENABLE_METROLOGY
  TMPISpeech->signal_Read_Status++;
  TMPISpeech->signal_Read_Status_Value = Status;
#endif /* ENABLE_METROLOGY */

  return (Status);
}
/*----------------------------------------------------------------------------*/


static tVoid tmpi_write_data(tTMPISpeech* TMPISpeech, tULong Val)
/***********************************************************************
 *
 *  tmpi_write_data
 *
 ***********************************************************************/
{
  if (TMPISpeech->state == MEA8000_STOPPED)
	{
		/* got pitch byte before first frame */
		TMPISpeech->pitch = 2 * Val;
		TMPISpeech->state = MEA8000_WAIT_FIRST;
		TMPISpeech->bufpos = 0;

#ifdef ENABLE_METROLOGY
    TMPISpeech->signal_Write_Pitch++;
    TMPISpeech->signal_Write_Pitch_Value = Val;
#endif /* ENABLE_METROLOGY */
	}
	else if (TMPISpeech->bufpos == 4)
	{
		/* overflow */
#ifdef ENABLE_METROLOGY
    TMPISpeech->signal_Write_Overflow++;
#endif /* ENABLE_METROLOGY */
	}
	else
	{
		/* enqueue frame byte */
		TMPISpeech->buf[TMPISpeech->bufpos] = (tUChar)Val;
		TMPISpeech->bufpos++;

#ifdef ENABLE_METROLOGY
    if (TMPISpeech->bufpos == 4)
    {
      TMPISpeech->signal_Write_Frame++;
    }
#endif /* ENABLE_METROLOGY */

		if ( (TMPISpeech->bufpos == 4) && (TMPISpeech->state == MEA8000_WAIT_FIRST) )
		{
			/* fade-in first frame */
			int old_pitch = TMPISpeech->pitch;
			TMPISpeech->last_pitch = old_pitch;
			tmpi_decode_frame(TMPISpeech);
			tmpi_shift_frame(TMPISpeech);
			TMPISpeech->last_pitch = old_pitch;
			TMPISpeech->ampl = 0;
			tmpi_start_frame(TMPISpeech);
			TMPISpeech->state = MEA8000_STARTED;
		}
	}
}
/*----------------------------------------------------------------------------*/


static tVoid tmpi_write_command(tTMPISpeech* TMPISpeech, tULong Val)
/***********************************************************************
 *
 *  tmpi_write_command
 *
 ***********************************************************************/
{
  // Bit 7 : Not used
  // Bit 6 : Not used
  // Bit 5 : Not used
  // Bit 4 : STOP
  // Bit 3 : CONT Enable
  // Bit 2 : CONT
  // Bit 1 : ROE Enable
  // Bit 0 : ROE

  // ROE Enable ?
	if (Val & 0x02)
  {
    // Update ROE
		TMPISpeech->roe = Val & 1;
  }

  // CONT Enable ?
	if (Val & 0x08)
  {
    // Update CONT
		TMPISpeech->cont = (Val >> 2) & 1;
  }

  // STOP ?
	if (Val & 0x10)
  {
		tmpi_stop_frame(TMPISpeech);
  }

#ifdef ENABLE_METROLOGY
  TMPISpeech->signal_Write_Command++;
  TMPISpeech->signal_Write_Command_Value = Val;
#endif /* ENABLE_METROLOGY */
}
/*----------------------------------------------------------------------------*/


static tLong tmpi_interp_i(tTMPISpeech* TMPISpeech, tLong org, tLong dst)
/***********************************************************************
 *
 *  tmpi_interp_i
 *  linear interpolation
 *
 ***********************************************************************/
{
	return (org + (((dst-org) * TMPISpeech->framepos) >> TMPISpeech->framelog));
}
/*----------------------------------------------------------------------------*/


#ifndef TMPI_SPEECH_FLOAT_MODE

static tLong tmpi_filter_step(tTMPISpeech* TMPISpeech, tULong i, tLong input)
/***********************************************************************
 *
 *  tmpi_filter_step
 *  apply second order digital filter, sampling at F0
 *
 ***********************************************************************/
{
	/* frequency */
	tLong fm = tmpi_interp_i(TMPISpeech, TMPISpeech->f[i].last_fm, TMPISpeech->f[i].fm);
	/* bandwidth */
	tLong bw = tmpi_interp_i(TMPISpeech, TMPISpeech->f[i].last_bw, TMPISpeech->f[i].bw);
	/* filter coefficients */
	tLong b = (TMPISpeech->cos_table[fm] * TMPISpeech->exp_table[bw]) / TMPI_QUANT;
	tLong c = TMPISpeech->exp2_table[bw];
  
	/* transfer function */
	tLong next_output = input + ((b * TMPISpeech->f[i].output - c * TMPISpeech->f[i].last_output) / TMPI_QUANT);
	TMPISpeech->f[i].last_output = TMPISpeech->f[i].output;
	TMPISpeech->f[i].output = next_output;
  
	return next_output;
}
/*----------------------------------------------------------------------------*/


static tLong tmpi_noise_gen(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_noise_gen
 *  random waveform, in [-QUANT,QUANT]
 *
 ***********************************************************************/
{
	TMPISpeech->phi = (TMPISpeech->phi + 1) % TMPI_NOISE_LEN;
	return TMPISpeech->noise_table[TMPISpeech->phi];
}
/*----------------------------------------------------------------------------*/


static tLong tmpi_freq_gen(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_freq_gen
 *  sawtooth waveform at F0, in [-QUANT,QUANT]
 *
 ***********************************************************************/
{
	tLong pitch = tmpi_interp_i(TMPISpeech, TMPISpeech->last_pitch, TMPISpeech->pitch);
	TMPISpeech->phi = (TMPISpeech->phi + pitch) % TMPI_F0;
	return ((((TMPISpeech->phi % TMPI_F0) * TMPI_QUANT * 2) / TMPI_F0) - TMPI_QUANT);
}
/*----------------------------------------------------------------------------*/


static tLong tmpi_compute_sample(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_compute_sample
 *  sample in [-32768,32767], at F0
 *
 ***********************************************************************/
{
tULong i;
tLong sample;
tLong ampl = tmpi_interp_i(TMPISpeech, TMPISpeech->last_ampl, TMPISpeech->ampl);

	if (TMPISpeech->noise)
		sample = tmpi_noise_gen(TMPISpeech);
	else
		sample = tmpi_freq_gen(TMPISpeech);
	sample = (sample * ampl) >> 10; // div by 1024

	for (i=0; i<4; i++)
		sample = tmpi_filter_step(TMPISpeech, i, sample);

	return sample;
}
/*----------------------------------------------------------------------------*/

#else /* !TMPI_SPEECH_FLOAT_MODE */

static tDouble tmpi_interp_f(tTMPISpeech* TMPISpeech, tDouble org, tDouble dst)
/***********************************************************************
 *
 *  tmpi_interp_f
 *  linear interpolation
 *
 ***********************************************************************/
{
	return (org + (((dst-org) * (tDouble)TMPISpeech->framepos) / (tDouble)TMPISpeech->framelength));
}
/*----------------------------------------------------------------------------*/


static tDouble tmpi_filter_step(tTMPISpeech* TMPISpeech, tULong i, tDouble input)
/***********************************************************************
 *
 *  tmpi_filter_step
 *
 ***********************************************************************/
{
double fm = tmpi_interp_f(TMPISpeech, TMPISpeech->f[i].last_fm, TMPISpeech->f[i].fm);
double bw = tmpi_interp_f(TMPISpeech, TMPISpeech->f[i].last_bw, TMPISpeech->f[i].bw);
double b = 2.*math_cos(2.*M_PI*fm/TMPI_F0);
double c = -math_exp(-M_PI*bw/TMPI_F0);
double next_output =
		input -
		(c * (b * TMPISpeech->f[i].output + c * TMPISpeech->f[i].last_output));
	TMPISpeech->f[i].last_output = TMPISpeech->f[i].output;
	TMPISpeech->f[i].output = next_output;
	return next_output;
}
/*----------------------------------------------------------------------------*/


static tDouble tmpi_noise_gen(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_noise_gen
 *  noise, in [-1,1]
 *
 ***********************************************************************/
{
	TMPISpeech->phi++;
	return ((tDouble) TMPISpeech->noise_table[TMPISpeech->phi % TMPI_NOISE_LEN] / TMPI_QUANT);
}
/*----------------------------------------------------------------------------*/


static tDouble tmpi_freq_gen(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_freq_gen
 *  sawtooth waveform at F0, in [-1,1]
 *
 ***********************************************************************/
{
	tLong pitch = tmpi_interp_i(TMPISpeech, TMPISpeech->last_pitch, TMPISpeech->pitch);
	TMPISpeech->phi += pitch;
	return ((tDouble) (TMPISpeech->phi % TMPI_F0) / (TMPI_F0/2.) - 1.);
}
/*----------------------------------------------------------------------------*/


static tLong tmpi_compute_sample(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_compute_sample
 *
 ***********************************************************************/
{
tULong i;
tDouble sample;
tDouble ampl = tmpi_interp_f(TMPISpeech, TMPISpeech->last_ampl, TMPISpeech->ampl);

	if (TMPISpeech->noise)
		sample = tmpi_noise_gen(TMPISpeech);
	else
		sample = tmpi_freq_gen(TMPISpeech);

	sample *= ampl / 1000.;

	for (i=0; i<4; i++)
	{
		sample = tmpi_filter_step(TMPISpeech, i, sample);
	}

	return (sample * TMPI_QUANT);
} 
/*----------------------------------------------------------------------------*/

#endif /* !TMPI_SPEECH_FLOAT_MODE */

static tVoid tmpi_shift_frame(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_shift_frame
 *  shift frame parameters from current to last
 *
 ***********************************************************************/
{
tUChar i;

	TMPISpeech->last_pitch = TMPISpeech->pitch;
	for (i=0; i<4; i++)
	{
		TMPISpeech->f[i].last_bw = TMPISpeech->f[i].bw;
		TMPISpeech->f[i].last_fm = TMPISpeech->f[i].fm;
	}
	TMPISpeech->last_ampl = TMPISpeech->ampl;
}
/*----------------------------------------------------------------------------*/


static tVoid tmpi_decode_frame(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_decode_frame
 *  decode fields from buffer to current frame
 *
 ***********************************************************************/
{
int fd = (TMPISpeech->buf[3] >> 5) & 3; /* 0=8ms, 1=16ms, 2=32ms, 3=64ms */
int pi = TMPI_pi_table[TMPISpeech->buf[3] & 0x1f] << fd;

	TMPISpeech->noise = (TMPISpeech->buf[3] & 0x1f) == 16;
	TMPISpeech->pitch = TMPISpeech->last_pitch + pi;
	TMPISpeech->f[0].bw = TMPI_bw_table[TMPISpeech->buf[0] >> 6];
	TMPISpeech->f[1].bw = TMPI_bw_table[(TMPISpeech->buf[0] >> 4) & 3];
	TMPISpeech->f[2].bw = TMPI_bw_table[(TMPISpeech->buf[0] >> 2) & 3];
	TMPISpeech->f[3].bw = TMPI_bw_table[TMPISpeech->buf[0] & 3];
	TMPISpeech->f[3].fm = TMPI_fm4_table[0];
	TMPISpeech->f[2].fm = TMPI_fm3_table[TMPISpeech->buf[1] >> 5];
	TMPISpeech->f[1].fm = TMPI_fm2_table[TMPISpeech->buf[1] & 0x1f];
	TMPISpeech->f[0].fm = TMPI_fm1_table[TMPISpeech->buf[2] >> 3];
	TMPISpeech->ampl = TMPI_ampl_table[((TMPISpeech->buf[2] & 7) << 1) | (TMPISpeech->buf[3] >> 7)];
	TMPISpeech->framelog = fd + 3 /* 8 samples / ms */ + 3;
	TMPISpeech->framelength = 1 << TMPISpeech->framelog;
	TMPISpeech->bufpos = 0;
}
/*----------------------------------------------------------------------------*/


static tVoid tmpi_start_frame(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_start_frame
 *
 ***********************************************************************/
{
	/* enter or stay in active mode */
	TMPISpeech->framepos = 0;
}
/*----------------------------------------------------------------------------*/


static tVoid tmpi_stop_frame(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_stop_frame
 *
 ***********************************************************************/
{
tUChar i;

	/* enter stop mode */
	TMPISpeech->state = MEA8000_STOPPED;
  TMPISpeech->cycle_count = TMPI_CYCLE_INIT;

  // Reset filters
  TMPISpeech->sample = 0;
  TMPISpeech->lastsample = 0;
	for (i=0; i<4; i++)
	{
		TMPISpeech->f[i].last_output = 0;
		TMPISpeech->f[i].output = 0;
	}
}
/*----------------------------------------------------------------------------*/


static tVoid tmpi_sampling(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_sampling
 *  next sample in frame, sampling at 8 KHz
 *
 ***********************************************************************/
{
  // Compute new sample
	TMPISpeech->lastsample = TMPISpeech->sample;
	TMPISpeech->sample = tmpi_compute_sample(TMPISpeech);
#ifdef ENABLE_METROLOGY
  TMPISpeech->signal_Sample_Ready++;
#endif /* ENABLE_METROLOGY */

  TMPISpeech->framepos++;
	if (TMPISpeech->framepos == TMPISpeech->framelength)
	{
	  tmpi_shift_frame(TMPISpeech);
    
		/* end of frame */
		if (TMPISpeech->bufpos == 4)
		{
		  /* we have a successor */
			tmpi_decode_frame(TMPISpeech);
			tmpi_start_frame(TMPISpeech);
		}
		else if (TMPISpeech->cont)
		{
		  /* repeat mode */
		  tmpi_start_frame(TMPISpeech);
    }
		/* slow stop */
		else if (TMPISpeech->state == MEA8000_STARTED)
		{
		  TMPISpeech->ampl = 0;
		  tmpi_start_frame(TMPISpeech);
		  TMPISpeech->state = MEA8000_SLOWING;
		}
		else if (TMPISpeech->state == MEA8000_SLOWING)
		{
		  tmpi_stop_frame(TMPISpeech);
		}
	}
}
/*----------------------------------------------------------------------------*/


static tVoid tmpi_output(tTMPISpeech* TMPISpeech)
/***********************************************************************
 *
 *  tmpi_output
 *
 ***********************************************************************/
{
  if (TMPISpeech->FilledBufferSize < TMPISpeech->BufferSize)
  {
    TMPISpeech->FilledBufferSize += 2; // 16 Bits Mono

    // Add current sample
    *(TMPISpeech->bufferptr++) = TMPISpeech->lastsample * 16; // Gain adjust to [-32768,32767]

    if ((void*)TMPISpeech->bufferptr >= (void*)TMPISpeech->pbBufferEnd)
    {
      TMPISpeech->bufferptr = (tShort*)TMPISpeech->pbBufferStart;
    }
  }
}
/*----------------------------------------------------------------------------*/

#endif /* ENABLE_TMPI_SPEECH */


//
// PRINTER
//

#ifdef ENABLE_PRINTER

static tULong printer_getbusy(tPrinter* PrinterP)
/***********************************************************************
 *
 *  printer_getbusy
 *
 ***********************************************************************/
{
	// Printer not connected ?
	if (!PrinterP->connected)
		// Pullup
		return 1;

	return (PrinterP->busy);
}
/*----------------------------------------------------------------------------*/


static tVoid printer_receivedata(tPrinter* PrinterP, tUChar data)
/***********************************************************************
 *
 *  printer_receivedata
 *
 ***********************************************************************/
{
  // Printer offline ?
  if (!PrinterP->online)
    return;

  // /Strobe active, take data into account
  if ((data & 0x80) == 0)
  {
    if (PrinterP->data_counter < PRINTER_DATA_BUFFER_SIZE)
    {
      PrinterP->data_buffer[PrinterP->data_counter++] = data & 0x7f; // 7bits data
    }

    // Printer becomes busy
		PrinterP->busy = 1;
    PrinterP->busy_counter = PRINTER_BUSY_PRINT_CHAR_TIME;
  }
}
/*----------------------------------------------------------------------------*/

#endif /* ENABLE_PRINTER */




//

// DIGIBLASTER
//


#ifdef ENABLE_DIGIBLASTER


static tVoid digiblaster_write_data(tDigiblaster* Digiblaster, tULong Val)
/***********************************************************************
 *
 *  digiblaster_write_data
 *
 ***********************************************************************/
{
  if (Digiblaster->enabled)
  {
    // Prevent cracks
    if ( (Val > 0) && (Val < 255) )
    {
      Digiblaster->sample = (tUChar)Val;
    }
  }
}


static tVoid digiblaster_output(tDigiblaster* Digiblaster)
/***********************************************************************
 *
 *  digiblaster_output
 *
 ***********************************************************************/
{
  if (Digiblaster->FilledBufferSize < Digiblaster->BufferSize)
  {
    Digiblaster->FilledBufferSize += 2; // 8 Bits Stereo

    // Add current sample
    *(Digiblaster->bufferptr++) = Digiblaster->sample;
    *(Digiblaster->bufferptr++) = Digiblaster->sample;
  }
}

#endif /* ENABLE_DIGIBLASTER */



#ifdef USE_XMEM


//

// X-MEM
//
// Winbond W29C040

//

static tVoid XMEM_Winbond_Detect_Preamble(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_Winbond_Detect_Preamble
 *
 ***********************************************************************/
{

	// Address 5555h = AAh

	if ( (NativeCPC->memwrite_addr == 0xd555)

			 && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

			 && (NativeCPC->memdata == 0xaa) )

	{

		// Next step

		NativeCPC->XMEMPtr = XMEM_Winbond_Detect_Preamble_End;

	}

}

static tVoid XMEM_Winbond_Detect_Preamble_End(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_Winbond_Detect_Preamble_End
 *
 ***********************************************************************/
{

	// Address 2AAAh = 55h

	if ( (NativeCPC->memwrite_addr == 0xeaaa)

			 && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

			 && (NativeCPC->memdata == 0x55) ) // Data = 0xAA

	{

		// Next step

		NativeCPC->XMEMPtr = XMEM_Winbond_Detect_Command;

	}

	else

	{

		// Reset

		NativeCPC->XMEMPtr = XMEM_Winbond_Detect_Preamble;

	}

}

static tVoid XMEM_Winbond_Detect_Command(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_Winbond_Detect_Command
 *
 ***********************************************************************/
{

	// Address 5555h

	if ( (NativeCPC->memwrite_addr == 0xd555)

			 && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM) )

	{

		// Command = Disable Protection

		if (NativeCPC->memdata == 0x20)

		{

			// Start write operation

			//NativeCPC->XMEM_write_timeout = NativeCPC->timer_from_reset + XMEM_WRITE_TIMEOUT;

			NativeCPC->XMEM_write_timeout = 0U;

			NativeCPC->XMEMPtr = XMEM_Winbond_Write_Data;

		}

		// Command = Enable Protection

		else if (NativeCPC->memdata == 0xa0)

		{

			// Start write operation

			//NativeCPC->XMEM_write_timeout = NativeCPC->timer_from_reset + XMEM_WRITE_TIMEOUT;

			NativeCPC->XMEM_write_timeout = 0U;

			NativeCPC->XMEMPtr = XMEM_Winbond_Write_Data;

		}

		// Command = Identification

		else if (NativeCPC->memdata == 0x90)

		{

			// Start read identification

			NativeCPC->XMEMPtr = XMEM_Winbond_Detect_Preamble;

			NativeCPC->XMEM_read_id = 1;

			if (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

			{
				NativeCPC->membank_read[3] = NativeCPC->pbXMEMID;
			}
		}

		// Command = Identification Exit

		else if (NativeCPC->memdata == 0xf0)

		{

			// Stop read identification

			NativeCPC->XMEMPtr = XMEM_Winbond_Detect_Preamble;

			NativeCPC->XMEM_read_id = 0;

			NativeCPC->membank_read[3] = NativeCPC->pbExpansionROM;

		}

		else

		{

			// Reset

			NativeCPC->XMEMPtr = XMEM_Winbond_Detect_Preamble;

		}

	}

	else

	{

		// Reset

		NativeCPC->XMEMPtr = XMEM_Winbond_Detect_Preamble;

	}

}

static tVoid XMEM_Winbond_Write_Data(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_Winbond_Write_Data
 *
 ***********************************************************************/
{

	// Address 5555h = AAh

	if ( (NativeCPC->memwrite_addr == 0xd555)

			 && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

			 && (NativeCPC->memdata == 0xaa) )

	{

		// Next step

		NativeCPC->XMEMPtr = XMEM_Winbond_Detect_Preamble_End;

	}

	// Timeout elapsed ?

	else if ( (NativeCPC->XMEM_write_timeout)

						&& (NativeCPC->timer_from_reset >= NativeCPC->XMEM_write_timeout) )

	{

		// Stop write

		NativeCPC->XMEMPtr = XMEM_Winbond_Detect_Preamble;

	}

	else if ( (NativeCPC->memwrite_addr >= 0xc000)

	          && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM) )

	{

		// Translate address

		tULong address = NativeCPC->memwrite_addr - 0xc000;

		address += (NativeCPC->GateArray->upper_ROM & 0x1f) * ROM_SIZE;

		// Write into XMEM Flash area

		*(NativeCPC->pbXMEMROM + address) = NativeCPC->memdata;

		// Signal XMEM ROM have been updated

		NativeCPC->XMEM_modified = 1;

		NativeCPC->XMEM_write_timeout = NativeCPC->timer_from_reset + XMEM_WRITE_TIMEOUT;

	}

	else

	{

		// Wait for timeout !!

	}

}



//

// SST39SF040

//

static tVoid XMEM_SST_Detect_Preamble(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_SST_Detect_Preamble
 *
 ***********************************************************************/
{

	// Address 5555h = AAh

	if ( (NativeCPC->memwrite_addr == 0xd555)

			 && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

			 && (NativeCPC->memdata == 0xaa) )

	{

		// Next step

		NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble_End;

	}

}

static tVoid XMEM_SST_Detect_Preamble_End(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_SST_Detect_Preamble_End
 *
 ***********************************************************************/
{

	// Address 2AAAh = 55h

	if ( (NativeCPC->memwrite_addr == 0xeaaa)

			 && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

			 && (NativeCPC->memdata == 0x55) )

	{

		// Next step

		NativeCPC->XMEMPtr = XMEM_SST_Detect_Command;

	}

	else

	{

		// Reset

		NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble;

	}

}

static tVoid XMEM_SST_Detect_Command(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_SST_Detect_Command
 *
 ***********************************************************************/
{

	// Address 5555h

	if ( (NativeCPC->memwrite_addr == 0xd555)

			 && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM) )

	{

		// Command = Byte Program

		if (NativeCPC->memdata == 0xa0)

		{

			// Next step

			NativeCPC->XMEMPtr = XMEM_SST_Byte_Program;

		}

		// Command = Erase

		else if (NativeCPC->memdata == 0x80)

		{

			// Next step

			NativeCPC->XMEMPtr = XMEM_SST_Detect_Erase_Preamble;

		}

		// Command = Identification

		else if (NativeCPC->memdata == 0x90)

		{

			// Start read identification

			NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble;

			NativeCPC->XMEM_read_id = 1;

			if (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

			{
				NativeCPC->membank_read[3] = NativeCPC->pbXMEMID;
			}
		}

		// Command = Identification Exit

		else if (NativeCPC->memdata == 0xf0)

		{

			// Stop read identification

			NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble;

			NativeCPC->XMEM_read_id = 0;

			NativeCPC->membank_read[3] = NativeCPC->pbExpansionROM;

		}

		else

		{

			// Reset

			NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble;

		}

	}

	else

	{

		// Reset

		NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble;

	}

}

static tVoid XMEM_SST_Detect_Erase_Preamble(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_SST_Detect_Erase_Preamble
 *
 ***********************************************************************/
{

	// Address 5555h = AAh

	if ( (NativeCPC->memwrite_addr == 0xd555)

			 && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

			 && (NativeCPC->memdata == 0xaa) )

	{

		// Next step

		NativeCPC->XMEMPtr = XMEM_SST_Detect_Erase_Preamble_End;

	}

	else

	{

		// Reset

		NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble;

	}

}

static tVoid XMEM_SST_Detect_Erase_Preamble_End(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_SST_Detect_Erase_Preamble_End
 *
 ***********************************************************************/
{

	// Address 2AAAh = 55h

	if ( (NativeCPC->memwrite_addr == 0xeaaa)

			 && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

			 && (NativeCPC->memdata == 0x55) )

	{

		// Next step

		NativeCPC->XMEMPtr = XMEM_SST_Detect_Erase_Command;

	}

	else

	{

		// Reset

		NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble;

	}

}

static tVoid XMEM_SST_Detect_Erase_Command(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_SST_Detect_Erase_Command
 *
 ***********************************************************************/
{

	// Command = Erase Sector

	if (NativeCPC->memdata == 0x30)

	{

		tULong Loop;

		// Translate address

		tULong address = (NativeCPC->memwrite_addr - 0xc000) & 0xf000; // 4K base address

		address += (NativeCPC->GateArray->upper_ROM & 0x1f) * ROM_SIZE;

		// Erase sector

		for (Loop=0U; Loop < 4096U; Loop++)

		{

			*(NativeCPC->pbXMEMROM + address + Loop) = 0xff;

		}

		// Signal XMEM ROM have been updated

		NativeCPC->XMEM_modified = 1;

	}

	// Command = Erase Chip

	else if ( (NativeCPC->memwrite_addr == 0xd555)

						&& (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

						&& (NativeCPC->memdata == 0x10) ) // Data = 0x10

	{

		tULong Loop;

		// Erase chip

		for (Loop=0U; Loop < (XMEM_NB_ROMS * ROM_SIZE); Loop++)

		{

			*(NativeCPC->pbXMEMROM + Loop) = 0xff;

		}

		// Signal XMEM ROM have been updated

		NativeCPC->XMEM_modified = 1;

	}


	// Next step

	NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble;

}

static tVoid XMEM_SST_Byte_Program(tNativeCPC* NativeCPC)

/***********************************************************************

 *
 *  XMEM_SST_Byte_Program
 *
 ***********************************************************************/
{

	// Address 5555h = AAh

	if ( (NativeCPC->memwrite_addr == 0xd555)

			 && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM)

			 && (NativeCPC->GateArray->upper_ROM == 0x03)

			 && (NativeCPC->memdata == 0xAA) ) // Data = 0xAA

	{

		// Next step

		NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble_End;

	}

	else if ( (NativeCPC->memwrite_addr >= 0xc000)

	          && (NativeCPC->membank_read[3] == NativeCPC->pbExpansionROM) )

	{

		// Translate address

		tULong address = NativeCPC->memwrite_addr - 0xc000;

		address += (NativeCPC->GateArray->upper_ROM & 0x1f) * ROM_SIZE;

		// Write into XMEM Flash area

		*(NativeCPC->pbXMEMROM + address) = NativeCPC->memdata;

		// Signal XMEM ROM have been updated

		NativeCPC->XMEM_modified = 1;

		// Next step

		NativeCPC->XMEMPtr = XMEM_SST_Detect_Preamble;

	}

}


#endif /* USE_XMEM */


//==============================================================================
//
// Routines
//
//==============================================================================
static INLINE tVoid MemMoveByte(tUChar* destP,
                                const tUChar* sourceP,
                                tULong numBytes)
/***********************************************************************
 *
 *  MemMoveByte
 *
 ***********************************************************************/
{
  while (numBytes--)
  {
    *(destP++) = *(sourceP++);
  }
}
/*----------------------------------------------------------------------------*/


static INLINE tVoid MemSetByte(tUChar* destP,
                               tULong numBytes,
                               tULong value)
/***********************************************************************
 *
 *  MemSetByte
 *
 ***********************************************************************/
{
  while (numBytes--)
  {
    *(destP++) = value;
  }
}
/*----------------------------------------------------------------------------*/


static INLINE tVoid MemSetLong(tULong* destP,
                               tULong numLongs,
                               tULong value)
/***********************************************************************
 *
 *  MemSetLong
 *
 ***********************************************************************/
{
  while (numLongs--)
  {
    *(destP++) = value;
  }
}
/*----------------------------------------------------------------------------*/



//==============================================================================
//
// Safety
//
//==============================================================================
#ifdef _SAFETY

static tBool CheckRAMMarkers(tNativeCPC* NativeCPC)
{
tBool CheckOK = cFalse;

	do
	{
		if (NativeCPC->magic != NATIVECPC_MAGIC)
			continue;
		if (NativeCPC->Z80->magic != Z80_MAGIC)
			continue;
		if (NativeCPC->PPI->magic != PPI_MAGIC)
			continue;
		if (NativeCPC->PSG->magic != PSG_MAGIC)
			continue;
		if (NativeCPC->FDC->magic != FDC_MAGIC)
			continue;
		/*if (NativeCPC->FDC->DriveA->magic != DRIVE_MAGIC)
			continue;
		if (NativeCPC->FDC->DriveB->magic != DRIVE_MAGIC)
			continue; */
		if (NativeCPC->CRTC->magic != CRTC_MAGIC)
			continue;
		if (NativeCPC->VDU->magic != VDU_MAGIC)
			continue;
		if (NativeCPC->GateArray->magic != GA_MAGIC)
			continue;
		if (NativeCPC->Printer->magic != PRINTER_MAGIC)
			continue;
		if (NativeCPC->Tape->magic != TAPE_MAGIC)
			continue;
		if (NativeCPC->TMPISpeech->magic != TMPI_SPEECH_MAGIC)
			continue;
		if (NativeCPC->PlayCity->magic != PLAYCITY_MAGIC)
			continue;
		if (NativeCPC->Digiblaster->magic != DIGIBLASTER_MAGIC)
			continue;

		// Check OK
		CheckOK = cTrue;
	}
	while (0);

	return (CheckOK);
}

#endif /* _SAFETY */



//==============================================================================
//
// Unitary Tests
//
//==============================================================================
#ifdef _TESTU

// Prototypes of TestU fonctions
static tULong TestU_MemSetByte_1(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_MemSetLong_1(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_MemMoveByte_1(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_Compiler_1(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_Compiler_2(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_Compiler_3(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_Compiler_4(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_ADC(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_ADC_MIX(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_ADC16(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_ADD(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_ADD16(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_AND(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_BIT(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_CALL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_CCF(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_CP(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_CPD(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_DEC(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_DEC_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_DEC_MIX(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_EX_SP(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_EXX(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_INC(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_INC_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_INC_MIX(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_JP(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_JR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_LD16_MEM(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_LDMEM_16(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_OR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_POP(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_PUSH(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RES(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RES_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RES_REG_ADDR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RET(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RL_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RLA(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RLC(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RLC_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RLC_REG_ADDR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RLCA(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RLD(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RR_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RRA(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RRC(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RRC_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RRC_REG_ADDR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RRCA(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RRD(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_RST(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SBC(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SBC16(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SET(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SET_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SET_REG_ADDR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SLA(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SLA_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SLA_REG_ADDR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SLL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SLL_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SLL_REG_ADDR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SRA(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SRA_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SRA_REG_ADDR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SRL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SRL_MHL(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SRL_REG_ADDR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_SUB(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_XOR(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_math_fabs(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_math_cos(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_math_log10(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_math_float(tNativeCPC* NativeCPC, tULong NoTest);
static tULong TestU_PlayCity_YMZFreq(tNativeCPC* NativeCPC, tULong NoTest);


static tULong PerformTestU(tNativeCPC* NativeCPC)
/***********************************************************************
 *
 *  PerformTestU
 *
 ***********************************************************************/
{
tZ80* Z80 = NativeCPC->Z80;
tULong Result = errNone;

  if (Result == errNone) Result = TestU_MemSetByte_1(NativeCPC, 1);
  if (Result == errNone) Result = TestU_MemSetLong_1(NativeCPC, 2);
  if (Result == errNone) Result = TestU_MemMoveByte_1(NativeCPC, 3);
  if (Result == errNone) Result = TestU_Compiler_1(NativeCPC, 4);
  if (Result == errNone) Result = TestU_Compiler_2(NativeCPC, 5);
  if (Result == errNone) Result = TestU_Compiler_3(NativeCPC, 6);
  if (Result == errNone) Result = TestU_Compiler_4(NativeCPC, 7);

  /* Z80 Instructions */
  if (Result == errNone) Result = TestU_ADC(NativeCPC, 20);
  if (Result == errNone) Result = TestU_ADC_MIX(NativeCPC, 21);
  if (Result == errNone) Result = TestU_ADC16(NativeCPC, 22);
  if (Result == errNone) Result = TestU_ADD(NativeCPC, 23);
  if (Result == errNone) Result = TestU_ADD16(NativeCPC, 24);
  if (Result == errNone) Result = TestU_AND(NativeCPC, 25);
  if (Result == errNone) Result = TestU_BIT(NativeCPC, 26);
  if (Result == errNone) Result = TestU_CALL(NativeCPC, 27);
  if (Result == errNone) Result = TestU_CCF(NativeCPC, 28);
  if (Result == errNone) Result = TestU_CP(NativeCPC, 29);
  if (Result == errNone) Result = TestU_CPD(NativeCPC, 30);
  if (Result == errNone) Result = TestU_DEC(NativeCPC, 31);
  if (Result == errNone) Result = TestU_DEC_MHL(NativeCPC, 32);
  if (Result == errNone) Result = TestU_DEC_MIX(NativeCPC, 33);
  if (Result == errNone) Result = TestU_EX_SP(NativeCPC, 34);
  if (Result == errNone) Result = TestU_EXX(NativeCPC, 35);
  if (Result == errNone) Result = TestU_INC(NativeCPC, 36);
  if (Result == errNone) Result = TestU_INC_MHL(NativeCPC, 37);
  if (Result == errNone) Result = TestU_INC_MIX(NativeCPC, 38);
  if (Result == errNone) Result = TestU_JP(NativeCPC, 39);
  if (Result == errNone) Result = TestU_JR(NativeCPC, 40);
  if (Result == errNone) Result = TestU_LD16_MEM(NativeCPC, 41);
  if (Result == errNone) Result = TestU_LDMEM_16(NativeCPC, 42);
  if (Result == errNone) Result = TestU_OR(NativeCPC, 43);
  if (Result == errNone) Result = TestU_POP(NativeCPC, 44);
  if (Result == errNone) Result = TestU_PUSH(NativeCPC, 45);
  if (Result == errNone) Result = TestU_RES(NativeCPC, 46);
  if (Result == errNone) Result = TestU_RES_MHL(NativeCPC, 47);
  if (Result == errNone) Result = TestU_RES_REG_ADDR(NativeCPC, 48);
  if (Result == errNone) Result = TestU_RET(NativeCPC, 49);
  if (Result == errNone) Result = TestU_RL(NativeCPC, 50);
  if (Result == errNone) Result = TestU_RL_MHL(NativeCPC, 51);
  if (Result == errNone) Result = TestU_RLA(NativeCPC, 52);
  if (Result == errNone) Result = TestU_RLC(NativeCPC, 53);
  if (Result == errNone) Result = TestU_RLC_MHL(NativeCPC, 54);
  if (Result == errNone) Result = TestU_RLC_REG_ADDR(NativeCPC, 55);
  if (Result == errNone) Result = TestU_RLCA(NativeCPC, 56);
  if (Result == errNone) Result = TestU_RLD(NativeCPC, 57);
  if (Result == errNone) Result = TestU_RR(NativeCPC, 58);
  if (Result == errNone) Result = TestU_RR_MHL(NativeCPC, 59);
  if (Result == errNone) Result = TestU_RRA(NativeCPC, 60);
  if (Result == errNone) Result = TestU_RRC(NativeCPC, 61);
  if (Result == errNone) Result = TestU_RRC_MHL(NativeCPC, 62);
  if (Result == errNone) Result = TestU_RRC_REG_ADDR(NativeCPC, 63);
  if (Result == errNone) Result = TestU_RRCA(NativeCPC, 64);
  if (Result == errNone) Result = TestU_RRD(NativeCPC, 65);
  if (Result == errNone) Result = TestU_RST(NativeCPC, 66);
  if (Result == errNone) Result = TestU_SBC(NativeCPC, 67);
  if (Result == errNone) Result = TestU_SBC16(NativeCPC, 68);
  if (Result == errNone) Result = TestU_SET(NativeCPC, 69);
  if (Result == errNone) Result = TestU_SET_MHL(NativeCPC, 70);
  if (Result == errNone) Result = TestU_SET_REG_ADDR(NativeCPC, 71);
  if (Result == errNone) Result = TestU_SLA(NativeCPC, 72);
  if (Result == errNone) Result = TestU_SLA_MHL(NativeCPC, 73);
  if (Result == errNone) Result = TestU_SLA_REG_ADDR(NativeCPC, 74);
  if (Result == errNone) Result = TestU_SLL(NativeCPC, 75);
  if (Result == errNone) Result = TestU_SLL_MHL(NativeCPC, 76);
  if (Result == errNone) Result = TestU_SLL_REG_ADDR(NativeCPC, 77);
  if (Result == errNone) Result = TestU_SRA(NativeCPC, 78);
  if (Result == errNone) Result = TestU_SRA_MHL(NativeCPC, 79);
  if (Result == errNone) Result = TestU_SRA_REG_ADDR(NativeCPC, 80);
  if (Result == errNone) Result = TestU_SRL(NativeCPC, 81);
  if (Result == errNone) Result = TestU_SRL_MHL(NativeCPC, 82);
  if (Result == errNone) Result = TestU_SRL_REG_ADDR(NativeCPC, 83);
  if (Result == errNone) Result = TestU_SUB(NativeCPC, 84);
  if (Result == errNone) Result = TestU_XOR(NativeCPC, 85);

  // MathLib
  if (Result == errNone) Result = TestU_math_fabs(NativeCPC, 100);
  if (Result == errNone) Result = TestU_math_cos(NativeCPC, 101);
  if (Result == errNone) Result = TestU_math_log10(NativeCPC, 102);
  if (Result == errNone) Result = TestU_math_float(NativeCPC, 103);

  // PlayCity
  if (Result == errNone) Result = TestU_PlayCity_YMZFreq(NativeCPC, 104);

  //
  // Z80 Instructions Cycle count
  //
  // Initialise motor conditions
#ifdef __PALMOS__
  NativeCPC->FirstInitToPerform = 0;
#endif /* __PALMOS__ */
  fdc_init(NativeCPC);
  video_init(NativeCPC);
  audio_init(NativeCPC);
  ga_init(NativeCPC);
#if defined(__WIN32__) || defined(_WIN64)
	NativeCPC->Z80OUTHandlerPtr = z80_OUT_handler;
	NativeCPC->SetAYRegisterPtr = (tSetAYRegisterPtr)audio_set_AY_Register;
#endif /* __WIN32__ || _WIN64 */


  // All tests OK. Reset CPU */
  //if (Result == errNone)
  {
    // Reset Z80 registers (see Z80Reset)
    MemSetByte((tUChar*)&Z80->Regs,
               sizeof(tZ80Regs),
               0); // clear result codes buffer

    _IX = _IY = 0xffff; // IX and IY are FFFF after a reset!
    _F = Zflag; // set zero flag

#ifdef __PALMOS__
    NativeCPC->FirstInitToPerform = 1;
#endif /* __PALMOS__ */
  }

  return (Result << 24);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_MemSetByte_1(tNativeCPC* NativeCPC,
                                 tULong NoTest)
/***********************************************************************
 *
 *  TestU_MemSetByte_1
 *
 ***********************************************************************/
{
tULong TestResult = errNone;
tUChar ArrayA[100];
tUChar Loop;

  NOT_USED(NativeCPC);

  // Prepare conditions
  for (Loop=0; Loop<100; Loop++)
    ArrayA[Loop] = 0x55;

  // Perform operation
  MemSetByte(ArrayA,
             95,
             0xAA);

  // Check Result
  for (Loop=0; Loop<95; Loop++)
  {
    if (ArrayA[Loop] != 0xAA)
      TestResult=NoTest;
  }
  for (Loop=95; Loop<100; Loop++)
  {
    if (ArrayA[Loop] != 0x55)
      TestResult=NoTest;
  }

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_MemSetLong_1(tNativeCPC* NativeCPC,
                                 tULong NoTest)
/***********************************************************************
 *
 *  TestU_MemSetLong_1
 *
 ***********************************************************************/
{
tULong TestResult = errNone;
tULong ArrayA[100];
tUChar Loop;

  NOT_USED(NativeCPC);

  // Prepare conditions
  for (Loop=0; Loop<100; Loop++)
    ArrayA[Loop] = 0x55;

  // Perform operation
  MemSetLong(ArrayA,
             95,
             0xAA);

  // Check Result
  for (Loop=0; Loop<95; Loop++)
  {
    if (ArrayA[Loop] != 0xAA)
      TestResult=NoTest;
  }
  for (Loop=95; Loop<100; Loop++)
  {
    if (ArrayA[Loop] != 0x55)
      TestResult=NoTest;
  }

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_MemMoveByte_1(tNativeCPC* NativeCPC,
                                  tULong NoTest)
/***********************************************************************
 *
 *  TestU_MemMoveByte_1
 *
 ***********************************************************************/
{
tULong TestResult = errNone;
tUChar SrcArrayA[100];
tUChar DstArrayA[100];
tUChar Loop;

  NOT_USED(NativeCPC);

  // Prepare conditions
  for (Loop=0; Loop<100; Loop++)
  {
    SrcArrayA[Loop] = 0x55;
    DstArrayA[Loop] = 0xAA;
  }

  // Perform operation
  MemMoveByte(DstArrayA,
							(const tUChar*)SrcArrayA,
              95);

  // Check Result
  for (Loop=0; Loop<100; Loop++)
  {
    if (SrcArrayA[Loop] != 0x55)
      TestResult=NoTest;
  }
  for (Loop=0; Loop<95; Loop++)
  {
    if (DstArrayA[Loop] != 0x55)
      TestResult=NoTest;
  }
  for (Loop=95; Loop<100; Loop++)
  {
    if (DstArrayA[Loop] != 0xAA)
      TestResult=NoTest;
  }

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_Compiler_1(tNativeCPC* NativeCPC,
                               tULong NoTest)
/***********************************************************************
 *
 *  TestU_Compiler_1
 *
 ***********************************************************************/
{
tULong TestResult = errNone;
tLong longValue;
tChar charValue;

  NOT_USED(NativeCPC);

  // Test 1 : signed char => signed long

  // Prepare conditions
  charValue = -116;

  // Perform operation
  longValue = (tLong)charValue;

  // Check Result
  if (longValue != -116)
  {
    TestResult=NoTest;
  }

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_Compiler_2(tNativeCPC* NativeCPC,
                               tULong NoTest)
/***********************************************************************
 *
 *  TestU_Compiler_2
 *
 ***********************************************************************/
{
tULong TestResult = errNone;
tULong ulongValue;
tUChar ucharValue;

  NOT_USED(NativeCPC);

  // Test 2 : unsigned char => unsigned long

  // Prepare conditions
  ucharValue = 140;

  // Perform operation
  ulongValue = (tULong)ucharValue;

  // Check Result
  if (ulongValue != 140)
  {
    TestResult=NoTest;
  }

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_Compiler_3(tNativeCPC* NativeCPC,
                               tULong NoTest)
/***********************************************************************
 *
 *  TestU_Compiler_3
 *
 ***********************************************************************/
{
tULong TestResult = errNone;
tLong longValue;
tUChar ucharValue;

  NOT_USED(NativeCPC);

  // Test 3 : unsigned char => signed long
  //
  // ATTENTION : unsigned char <> signed long
  //

  // Prepare conditions
  ucharValue = (tUChar)-116;

  // Perform operation
  longValue = (tLong)ucharValue;

  // Check Result
  if (longValue != 140)
  {
    TestResult=NoTest;
  }

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_Compiler_4(tNativeCPC* NativeCPC,
                               tULong NoTest)
/***********************************************************************
 *
 *  TestU_Compiler_4
 *
 ***********************************************************************/
{
tULong TestResult = errNone;
tULong ulongValue;
tChar charValue;

  NOT_USED(NativeCPC);

  // Test 4 : signed char => unsigned long
  //
  // ATTENTION : signed char <> unsigned long
  //

  // Prepare conditions
  charValue = (tChar)0x8c;

  // Perform operation
  ulongValue = (tULong)charValue;

  // Check Result
  if (ulongValue != 0xffffff8c)
  {
    TestResult=NoTest;
  }

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


//
// Z80 Instructions Unitary Tests
//
// Flags:
// ======
// - Sflag : The  sign  flag is used to indicate whether  in  twos  complement
//           notation  a number is negative or positive.  (see chapter 16  for
//           more  about two's complement) Thus if the number is  negative  (-
//           128 to -1)  then  the  sign flag is set to 1.  If the  number  is
//           positive (0 to 127) then the sign flag is reset to 0.
// - Zflag : The Z flag is set to 1 of the result of an operation is 0. If the
//           result  is other than 0 then the Z flag is reset to 0.
// - Hflag : Half carry. The H flag indicates a  carry  from  bit 3 in addition,
//           and a borrow from  bit  4  in subtraction in 8 bit operations.
//           A carry from bit 11 in addition and a borrow from bit 12 in
//           16 bit operations. Only used by DAA
// - Pflag : Parity.  The  parity of a byte is the number of 1's the byte  has
//           when  it  is  represented  in binary.  43 decimal  in  binary  is
//           00101011,  which  has  4  1's.  If  the number  of  1's  is  even
//           (including 0) then the byte has even parity and the P flag is set
//           to  1.  59  in binary is 00111110 which has 5 1's and thus the  P
//           flag  is  set to 0.  The instructions which use the P flag  as  a
//           parity flag are indicated in the table.
// - Vflag : Overflow. This is the other major use for the P flag. Most of the
//           arithmetic instructions use the P flag as an overflow  flag,  and
//           this is why the flag is sometimes written as P/V. Overflow occurs
//           when,  during  a  two's complement addition the result  in  two's
//           complement is >127 or <-128. If this error condition occurs the
//           P flag is set to 1, otherwise it is set to 0.
// - Nflag : Negative. The N flag is 0 after an add and 1 after a subtract.
// - Cflag : The  carry  flag  is used to indicate whether the  result  of  an
//           operation (in decimal) was greater than 255 or less than 0.
//
// ----------------------------------------------------------------
// | F        |-*01? |Flag unaffected/affected/reset/set/unknown  |
// | S        |S     |Sign flag (Bit 7)                           |
// | Z        | Z    |Zero flag (Bit 6)                           |
// | HC       |  H   |Half Carry flag (Bit 4)                     |
// | P/V      |   P  |Parity/Overflow flag (Bit 2, V=overflow)    |
// | N        |    N |Add/Subtract flag (Bit 1)                   |
// | CY       |     C|Carry flag (Bit 0)                          |
// |----------+------+--------------------------------------------|
// | n               |Immediate addressing                        |
// | nn              |Immediate extended addressing               |
// | e               |Relative addressing (PC=PC+2+offset)        |
// | [nn]            |Extended addressing                         |
// | [xx+d]          |Indexed addressing                          |
// | r               |Register addressing                         |
// | [rr]            |Register indirect addressing                |
// |                 |Implied addressing                          |
// | b               |Bit addressing                              |
// | p               |Modified page zero addressing (see RST)     |
// |-----------------+--------------------------------------------|
// |DEFB n(,...)     |Define Byte(s)                              |
// |DEFB 'str'(,...) |Define Byte ASCII string(s)                 |
// |DEFS nn          |Define Storage Block                        |
// |DEFW nn(,...)    |Define Word(s)                              |
// |-----------------+--------------------------------------------|
// | A  B  C  D  E   |Registers (8-bit)                           |
// | AF  BC  DE  HL  |Register pairs (16-bit)                     |
// | F               |Flag register (8-bit)                       |
// | I               |Interrupt page address register (8-bit)     |
// | IX IY           |Index registers (16-bit)                    |
// | PC              |Program Counter register (16-bit)           |
// | R               |Memory Refresh register                     |
// | SP              |Stack Pointer register (16-bit)             |
// |-----------------+--------------------------------------------|
// | b               |One bit (0 to 7)                            |
// | cc              |Condition (C,M,NC,NZ,P,PE,PO,Z)             |
// | d               |One-byte expression (-128 to +127)          |
// | dst             |Destination s, ss, [BC], [DE], [HL], [nn]   |
// | e               |One-byte expression (-126 to +129)          |
// | m               |Any register r, [HL] or [xx+d]              |
// | n               |One-byte expression (0 to 255)              |
// | nn              |Two-byte expression (0 to 65535)            |
// | pp              |Register pair BC, DE, IX or SP              |
// | qq              |Register pair AF, BC, DE or HL              |
// | qq'             |Alternative register pair AF, BC, DE or HL  |
// | r               |Register A, B, C, D, E, H or L              |
// | rr              |Register pair BC, DE, IY or SP              |
// | s               |Any register r, value n, [HL] or [xx+d]     |
// | src             |Source s, ss, [BC], [DE], [HL], nn, [nn]    |
// | ss              |Register pair BC, DE, HL or SP              |
// | xx              |Index register IX or IY                     |
// |-----------------+--------------------------------------------|
// | +  -  *  /  ^   |Add/subtract/multiply/divide/exponent       |
// | &  ~  v  x      |Logical AND/NOT/inclusive OR/exclusive OR   |
// | <-  ->          |Rotate left/right                           |
// | [ ]             |Indirect addressing                         |
// | [ ]+  -[ ]      |Indirect addressing auto-increment/decrement|
// | { }             |Combination of operands                     |
// | #               |Also BC=BC-1,DE=DE-1                        |
// | ##              |Only lower 4 bits of accumulator A used     |
// ----------------------------------------------------------------
//


static tULong TestU_ADC(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_ADC
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |ADC A,s   |***V0*|Add with Carry       |A=A+s+CY              |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _F = Cflag | Nflag;
    _A = 0x18;
    _BCdword = 0x0001A000;

    // Perform operation
    ADC(_B);

    // Check Result
    if ( (_A != 0xB9) ||
         (_B != 0xA0) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _F = 0;
    _A = 0xfc;
    _BCdword = 0x0001A000;

    // Perform operation
    ADC(_B);

    // Check Result
    if ( (_A != 0x9C) ||
         (_B != 0xA0) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _L = 3; // # test
    _F = Cflag;
    _A = 0xE0;
    _BCdword = 0x00011F00;

    // Perform operation
    ADC(_B);

    // Check Result
    if ( (_A != 0x00) ||
         (_B != 0x1F) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _L = 4; // # test
    _F = Cflag;
    _A = 0x6f;
    _BCdword = 0x00011000;

    // Perform operation
    ADC(_B);

    // Check Result
    if ( (_A != 0x80) ||
         (_B != 0x10) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_ADC_MIX(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_ADC_MIX
 *
 * ------------------------------------------------------------------
 * |Mnemonic    |SZHPNC|Description          |Notes                 |
 * |------------+------+---------------------+----------------------|
 * |ADC A,[IX+d]|***V0*|Add with Carry       |A=A+[IX+d]+CY         |
 * |ADC A,[IY+d]|***V0*|Add with Carry       |A=A+[IY+d]+CY         |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _F = 0;
    _A = 0x00;
    _IXdword = 0x00015000;
    _PCdword = 0x00014000;
    *(NativeCPC->pbRAM + _PC) = 0x40;
    *(NativeCPC->pbRAM + _IX + 0x40) = 0x20;

    // Perform operation
    ADC_MIX;

    // Check Result
    if ( (_A != 0x20) ||
         (_IX != 0x5000) ||
         (_PC != 0x4001) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _F = 0;
    _A = 0x60;
    _IYdword = 0x00015100;
    _PCdword = 0x00014000;
    *(NativeCPC->pbRAM + _PC) = 0xE0; /* 0xE0 = -20 */
    *(NativeCPC->pbRAM + _IY - 0x20) = 0x40;

    // Perform operation
    ADC_MIY;

    // Check Result
    if ( (_A != 0xA0) ||
         (_IY != 0x5100) ||
         (_PC != 0x4001) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_ADC16(tNativeCPC* NativeCPC,
                          tULong NoTest)
/***********************************************************************
 *
 *  TestU_ADC16
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |ADC HL,ss |***V0*|Add with Carry       |HL=HL+ss+CY           |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _HLdword = 0x00010F00;
    _DEdword = 0x0000A100;

    // Perform operation
    ADC16(DE);

    // Check Result
    if ( (_HL != 0xB000) ||
         (_DE != 0xA100) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Cflag;
    _HLdword = 0x00016000;
    _DEdword = 0x00019fff;

    // Perform operation
    ADC16(DE);

    // Check Result
    if ( (_HL != 0x0000) ||
         (_DE != 0x9fff) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 3; // # test
    _F = Nflag;
    _HLdword = 0x00017000;
    _DEdword = 0x00002000;

    // Perform operation
    ADC16(DE);

    // Check Result
    if ( (_HL != 0x9000) ||
         (_DE != 0x2000) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 4; // # test
    _F = Cflag | Nflag;
    _HLdword = 0x0000A000;
    _DEdword = 0x0000A000;

    // Perform operation
    ADC16(DE);

    // Check Result
    if ( (_HL != 0x4001) ||
         (_DE != 0xA000) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_ADD(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_ADD
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |ADD A,s   |***V0*|Add                  |A=A+s                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _F = Nflag;
    _A = 0x11;
    _DEdword = 0x00012200;

    // Perform operation
    ADD(_D);

    // Check Result
    if ( (_A != 0x33) ||
         (_D != 0x22) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _F = Nflag;
    _A = 0x80;
    _DEdword = 0x00018000;

    // Perform operation
    ADD(_D);

    // Check Result
    if ( (_A != 0x00) ||
         (_D != 0x80) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _L = 3; // # test
    _F = Nflag;
    _A = 0x7F;
    _DEdword = 0x00010100;

    // Perform operation
    ADD(_D);

    // Check Result
    if ( (_A != 0x80) ||
         (_D != 0x01) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 4; // # test
    _F = Nflag;
    _A = 0xFF;
    _DEdword = 0x00010100;

    // Perform operation
    ADD(_D);

    // Check Result
    if ( (_A != 0x00) ||
         (_D != 0x01) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_ADD16(tNativeCPC* NativeCPC,
                          tULong NoTest)
/***********************************************************************
 *
 *  TestU_ADD16
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |ADD HL,ss |--*-0*|Add                  |HL=HL+ss              |
 * |ADD IX,pp |--*-0*|Add                  |IX=IX+pp              |
 * |ADD IY,rr |--*-0*|Add                  |IY=IY+rr              |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Nflag | Hflag;
    _BCdword = 0x00010000;
    _HLdword = 0x00008000;

    // Perform operation
    ADD16(HL, BC);

    // Check Result
    if ( (_HL != 0x8000) ||
         (_BC != 0x0000) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }


    // Prepare conditions
    _A = 2; // # test
    _F = Sflag | Zflag | Pflag;
    _BCdword = 0x00018000;
    _HLdword = 0x00008000;

    // Perform operation
    ADD16(HL, BC);

    // Check Result
    if ( (_HL != 0x0000) ||
         (_BC != 0x8000) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 3; // # test
    _F = Nflag;
    _BCdword = 0x00010800;
    _HLdword = 0x00000800;

    // Perform operation
    ADD16(HL, BC);

    // Check Result
    if ( (_HL != 0x1000) ||
         (_BC != 0x0800) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_AND(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_AND
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |AND s     |**1P00|Logical AND          |A=A&s                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _F = 0;
    _A = 0xA5;
    _BCdword = 0x0001A000;

    // Perform operation
    AND(_B);

    // Check Result
    if ( (_A != 0xA0) ||
         (_B != 0xA0) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _F = Nflag | Cflag;
    _A = 0x5F;
    _BCdword = 0x00011300;

    // Perform operation
    AND(_B);

    // Check Result
    if ( (_A != 0x13) ||
         (_B != 0x13) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 3; // # test
    _F = Nflag | Cflag;
    _A = 0x00;
    _BCdword = 0x00015300;

    // Perform operation
    AND(_B);

    // Check Result
    if ( (_A != 0x00) ||
         (_B != 0x53) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_BIT(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_BIT
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |BIT b,m   |?*1?0-|Test Bit             |m&{2^b}               |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Zflag | Cflag;
    _BCdword = 0x00014000;

    // Perform operation
    BIT(6, _B);

    // Check Result
    if ( (_B != 0x40) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0;
    _BCdword = 0x00014000;

    // Perform operation
    BIT(5, _B);

    // Check Result
    if ( (_B != 0x40) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_CALL(tNativeCPC* NativeCPC,
                         tULong NoTest)
/***********************************************************************
 *
 *  TestU_CALL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |CALL nn   |------|Unconditional Call   |-[SP]=PC,PC=nn        |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _PCdword = 0x00014000;
    _SPdword = 0x00015800;
    *(NativeCPC->pbRAM + _PC + 0) = 0xFF;
    *(NativeCPC->pbRAM + _PC + 1) = 0x42;
    *(NativeCPC->pbRAM + _SP - 1) = 0xAA;
    *(NativeCPC->pbRAM + _SP - 2) = 0x55;

    // Perform operation
    CALL;

    // Check Result
    if ( (_PC != 0x42FF) ||
         (_SP != 0x57FE) ||
         (*(NativeCPC->pbRAM + _SP + 0) != 0x02) ||
         (*(NativeCPC->pbRAM + _SP + 1) != 0x40) ||
         (_F != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _PCdword = 0x00014001;
    _SPdword = 0x00012FFF;
    *(NativeCPC->pbRAM + _PC + 0) = 0xFF;
    *(NativeCPC->pbRAM + _PC + 1) = 0x42;
    *(NativeCPC->pbRAM + _SP - 1) = 0xAA;
    *(NativeCPC->pbRAM + _SP - 2) = 0x55;

    // Perform operation
    CALL;

    // Check Result
    if ( (_PC != 0x42FF) ||
         (_SP != 0x2FFD) ||
         (*(NativeCPC->pbRAM + _SP + 0) != 0x03) ||
         (*(NativeCPC->pbRAM + _SP + 1) != 0x40) ||
         (_F != 0x55) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_CCF(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_CCF
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |CCF       |--?-0*|Complement Carry Flag|CY=~CY                |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Sflag | Zflag | Pflag | Nflag;

    // Perform operation
    CCF;

    // Check Result
    if ( ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Nflag | Cflag;

    // Perform operation
    CCF;

    // Check Result
    if ( ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_CP(tNativeCPC* NativeCPC,
                       tULong NoTest)
/***********************************************************************
 *
 *  TestU_CP
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |CP s      |***V1*|Compare              |A-s                   |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _A = 0x00;
    _BCdword = 0x0001A000;

    // Perform operation
    CP(_B);

    // Check Result
    if ( (_A != 0x00) ||
         (_B != 0xA0) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _A = 0xA0;
    _BCdword = 0x0001A000;

    // Perform operation
    CP(_B);

    // Check Result
    if ( (_A != 0xA0) ||
         (_B != 0xA0) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 3; // # test
    _A = 0xA0;
    _BCdword = 0x00018000;

    // Perform operation
    CP(_B);

    // Check Result
    if ( (_A != 0xA0) ||
         (_B != 0x80) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 4; // # test
    _A = 0xF0;
    _BCdword = 0x00013800;

    // Perform operation
    CP(_B);

    // Check Result
    if ( (_A != 0xF0) ||
         (_B != 0x38) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 5; // # test
    _A = 0x90;
    _BCdword = 0x00013000;

    // Perform operation
    CP(_B);

    // Check Result
    if ( (_A != 0x90) ||
         (_B != 0x30) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_CPD(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_CPD
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |CPD       |****1-|Compare and Decrement|A-[HL],HL=HL-1,BC=BC-1|
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _D = 1; // # test
    _F = 0;
    _A = 0xF0;
    _BCdword = 0x00011000;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL + 0) = 0xE1;

    // Perform operation
    CPD;

    // Check Result
    if ( (_A != 0xF0) ||
         (_BC != 0x0FFF) ||
         (_HL != 0x41FF) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _D = 2; // # test
    _F = Cflag;
    _A = 0xAA;
    _BCdword = 0x00010001;
    _HLdword = 0x00014100;
    *(NativeCPC->pbRAM + _HL + 0) = 0xAA;

    // Perform operation
    CPD;

    // Check Result
    if ( (_A != 0xAA) ||
         (_BC != 0x0000) ||
         (_HL != 0x40FF) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_DEC(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_DEC
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |DEC s     |***V1-|Decrement            |s=s-1                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _BCdword = 0x00011000;

    // Perform operation
    DEC(_B);

    // Check Result
    if ( (_B != 0x0F) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Cflag;
    _BCdword = 0x00010000;

    // Perform operation
    DEC(_B);

    // Check Result
    if ( (_B != 0xFF) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 3; // # test
    _F = 0;
    _BCdword = 0x00010100;

    // Perform operation
    DEC(_B);

    // Check Result
    if ( (_B != 0x00) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 4; // # test
    _F = Cflag;
    _BCdword = 0x00018000;

    // Perform operation
    DEC(_B);

    // Check Result
    if ( (_B != 0x7F) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_DEC_MHL(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_DEC_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |DEC [HL]  |***V1-|Decrement            |[HL]=[HL]-1           |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0x0;
    _HLdword = 0x00014455;
    *(NativeCPC->pbRAM + _HL) = 0x11;

    // Perform operation
    DEC_MHL;

    // Check Result
    if ( (_HL != 0x4455) ||
         (*(NativeCPC->pbRAM + _HL) != 0x10) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_DEC_MIX(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_DEC_MIX
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |DEC [IX+d]|***V1-|Decrement            |[IX+d]=[IX+d]-1       |
 * |DEC [IY+d]|***V1-|Decrement            |[IY+d]=[IY+d]-1       |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0x0;
    _IXdword = 0x00015000;
    _PCdword = 0x00014000;
    *(NativeCPC->pbRAM + _PC) = 0x40;
    *(NativeCPC->pbRAM + _IX + 0x40) = 0x20;

    // Perform operation
    DEC_MIX;

    // Check Result
    if ( (_PC != 0x4001) ||
         (_IX != 0x5000) ||
         (*(NativeCPC->pbRAM + _IX + 0x40) != 0x1F) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x0;
    _IYdword = 0x00015000;
    _PCdword = 0x00014000;
    *(NativeCPC->pbRAM + _PC) = 0xE0; /* 0xE0 = -20 */
    *(NativeCPC->pbRAM + _IY - 0x20) = 0x20;

    // Perform operation
    DEC_MIY;

    // Check Result
    if ( (_PC != 0x4001) ||
         (_IY != 0x5000) ||
         (*(NativeCPC->pbRAM + _IY - 0x20) != 0x1F) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_EX_SP(tNativeCPC* NativeCPC,
                          tULong NoTest)
/***********************************************************************
 *
 *  TestU_EX_SP
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |EX [SP],HL|------|Exchange             |[SP]<->HL             |
 * |EX [SP],xx|------|Exchange             |[SP]<->xx             |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _SPdword = 0x000157FE;
    _HLdword = 0x00014455;
    *(NativeCPC->pbRAM + _SP + 0) = 0x11;
    *(NativeCPC->pbRAM + _SP + 1) = 0x22;

    // Perform operation
    EX_SP(HL);

    // Check Result
    if ( (_SP != 0x57FE) ||
         (_HL != 0x2211) ||
         (*(NativeCPC->pbRAM + _SP + 0) != 0x55) ||
         (*(NativeCPC->pbRAM + _SP + 1) != 0x44) ||
         (_F != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _SPdword = 0x000157FF;
    _HLdword = 0x00014455;
    *(NativeCPC->pbRAM + _SP + 0) = 0x11;
    *(NativeCPC->pbRAM + _SP + 1) = 0x22;

    // Perform operation
    EX_SP(HL);

    // Check Result
    if ( (_SP != 0x57FF) ||
         (_HL != 0x2211) ||
         (*(NativeCPC->pbRAM + _SP + 0) != 0x55) ||
         (*(NativeCPC->pbRAM + _SP + 1) != 0x44) ||
         (_F != 0x55) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_EXX(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_EXX
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |EXX       |------|Exchange             |qq<->qq'   (except AF)|
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _AFdword = 0x000155AA;
    _BCdword = 0x00010123;
    _DEdword = 0x00014567;
    _HLdword = 0x000189AB;
    Z80->Regs.AFx.UShort.l = 0x1111;
    Z80->Regs.BCx.UShort.l = 0x2222;
    Z80->Regs.DEx.UShort.l = 0x3333;
    Z80->Regs.HLx.UShort.l = 0x4444;

    // Perform operation
    EXX;

    // Check Result
    if ( (_AF != 0x55AA) ||
         (Z80->Regs.AFx.UShort.l != 0x1111) ||
         (_BC != 0x2222) ||
         (Z80->Regs.BCx.UShort.l != 0x0123) ||
         (_DE != 0x3333) ||
         (Z80->Regs.DEx.UShort.l != 0x4567) ||
         (_HL != 0x4444) ||
         (Z80->Regs.HLx.UShort.l != 0x89AB) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_INC(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_INC
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |INC r     |***V0-|Increment            |r=r+1                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _BCdword = 0x00010F00;

    // Perform operation
    INC(_B);

    // Check Result
    if ( (_B != 0x10) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Cflag;
    _BCdword = 0x0001FF00;

    // Perform operation
    INC(_B);

    // Check Result
    if ( (_B != 0x00) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 3; // # test
    _F = 0;
    _BCdword = 0x00017F00;

    // Perform operation
    INC(_B);

    // Check Result
    if ( (_B != 0x80) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 4; // # test
    _F = Cflag;
    _BCdword = 0x00010500;

    // Perform operation
    INC(_B);

    // Check Result
    if ( (_B != 0x06) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_INC_MHL(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_INC_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |INC [HL]  |***V0-|Increment            |[HL]=[HL]+1           |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Nflag;
    _HLdword = 0x00014455;
    *(NativeCPC->pbRAM + _HL) = 0x11;

    // Perform operation
    INC_MHL;

    // Check Result
    if ( (_HL != 0x4455) ||
         (*(NativeCPC->pbRAM + _HL) != 0x12) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_INC_MIX(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_INC_MIX
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |INC [IX+d]|***V0-|Decrement            |[IX+d]=[IX+d]+1       |
 * |INC [IY+d]|***V0-|Decrement            |[IY+d]=[IY+d]+1       |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Nflag;
    _IXdword = 0x00015000;
    _PCdword = 0x00014000;
    *(NativeCPC->pbRAM + _PC) = 0x40;
    *(NativeCPC->pbRAM + _IX + 0x40) = 0x20;

    // Perform operation
    INC_MIX;

    // Check Result
    if ( (_PC != 0x4001) ||
         (_IX != 0x5000) ||
         (*(NativeCPC->pbRAM + _IX + 0x40) != 0x21) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Nflag;
    _IYdword = 0x00015000;
    _PCdword = 0x00014000;
    *(NativeCPC->pbRAM + _PC) = 0xE0; /* 0xE0 = -20 */
    *(NativeCPC->pbRAM + _IY - 0x20) = 0x20;

    // Perform operation
    INC_MIY;

    // Check Result
    if ( (_PC != 0x4001) ||
         (_IY != 0x5000) ||
         (*(NativeCPC->pbRAM + _IY - 0x20) != 0x21) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_JP(tNativeCPC* NativeCPC,
                       tULong NoTest)
/***********************************************************************
 *
 *  TestU_JP
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |JP nn     |------|Unconditional Jump   |PC=nn                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _PCdword = 0x00014000;
    *(NativeCPC->pbRAM + _PC + 0) = 0x10;
    *(NativeCPC->pbRAM + _PC + 1) = 0x20;


    // Perform operation
    JP;

    // Check Result
    if ( (_PC != 0x2010) ||
         (_F != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _PCdword = 0x00014001;
    *(NativeCPC->pbRAM + _PC + 0) = 0x11;
    *(NativeCPC->pbRAM + _PC + 1) = 0x22;

    // Perform operation
    JP;

    // Check Result
    if ( (_PC != 0x2211) ||
         (_F != 0x55) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_JR(tNativeCPC* NativeCPC,
                       tULong NoTest)
/***********************************************************************
 *
 *  TestU_JR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |JR e      |------|Unconditional Jump   |PC=PC+e               |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _PCdword = 0x00014000;
    *(NativeCPC->pbRAM + _PC + 0) = 0x10;

    // Perform operation
    JR;

    // Check Result
    if ( (_PC != 0x4011) ||
         (_F != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _PCdword = 0x00014009;
    *(NativeCPC->pbRAM + _PC + 0) = 0xF6;

    // Perform operation
    JR;

    // Check Result
    if ( (_PC != 0x4000) ||
         (_F != 0x55) )
    {
      continue;
    }

    // Prepare conditions
    _A = 3; // # test
    _F = 0xAA;
    _PCdword = 0x00014000;
    *(NativeCPC->pbRAM + _PC + 0) = 0x7F;

    // Perform operation
    JR;

    // Check Result
    if ( (_PC != 0x4080) ||
         (_F != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 4; // # test
    _F = 0x55;
    _PCdword = 0x0001407F;
    *(NativeCPC->pbRAM + _PC + 0) = 0x80;

    // Perform operation
    JR;

    // Check Result
    if ( (_PC != 0x4000) ||
         (_F != 0x55) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_LD16_MEM(tNativeCPC* NativeCPC,
                             tULong NoTest)
/***********************************************************************
 *
 *  TestU_LD16_MEM
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |LD dst,src|------|Load                 |dst=src               |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _PCdword = 0x00014000;
    _BCdword = 0x00018899;
    *(NativeCPC->pbRAM + _PC + 0) = 0x00;
    *(NativeCPC->pbRAM + _PC + 1) = 0x42;
    *(NativeCPC->pbRAM + 0x4200 + 0) = 0x55;
    *(NativeCPC->pbRAM + 0x4200 + 1) = 0xAA;

    // Perform operation
    LD16_MEM(BC);

    // Check Result
    if ( (_PC != 0x4002) ||
         (_BC != 0xAA55) ||
         (_F != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _PCdword = 0x000141FF;
    _DEdword = 0x00018899;
    *(NativeCPC->pbRAM + _PC + 0) = 0xFF;
    *(NativeCPC->pbRAM + _PC + 1) = 0x42;
    *(NativeCPC->pbRAM + 0x42FF + 0) = 0x22;
    *(NativeCPC->pbRAM + 0x42FF + 1) = 0x33;

    // Perform operation
    LD16_MEM(DE);

    // Check Result
    if ( (_PC != 0x4201) ||
         (_DE != 0x3322) ||
         (_F != 0x55) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_LDMEM_16(tNativeCPC* NativeCPC,
                              tULong NoTest)
/***********************************************************************
 *
 *  TestU_LDMEM_16
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |LD dst,src|------|Load                 |dst=src               |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _PCdword = 0x00014000;
    _BCdword = 0x00018899;
    *(NativeCPC->pbRAM + _PC + 0) = 0x00;
    *(NativeCPC->pbRAM + _PC + 1) = 0x42;
    *(NativeCPC->pbRAM + 0x4200 + 0) = 0x55;
    *(NativeCPC->pbRAM + 0x4200 + 1) = 0xAA;

    // Perform operation
    LDMEM_16(BC);

    // Check Result
    if ( (_PC != 0x4002) ||
         (*(NativeCPC->pbRAM + 0x4200 + 0) != 0x99) ||
         (*(NativeCPC->pbRAM + 0x4200 + 1) != 0x88) ||
         (_F != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _PCdword = 0x000141FF;
    _BCdword = 0x00018899;
    *(NativeCPC->pbRAM + _PC + 0) = 0xFF;
    *(NativeCPC->pbRAM + _PC + 1) = 0x43;
    *(NativeCPC->pbRAM + 0x43FF + 0) = 0x55;
    *(NativeCPC->pbRAM + 0x43FF + 1) = 0xAA;

    // Perform operation
    LDMEM_16(BC);

    // Check Result
    if ( (_PC != 0x4201) ||
         (*(NativeCPC->pbRAM + 0x43FF + 0) != 0x99) ||
         (*(NativeCPC->pbRAM + 0x43FF + 1) != 0x88) ||
         (_F != 0x55) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_OR(tNativeCPC* NativeCPC,
                       tULong NoTest)
/***********************************************************************
 *
 *  TestU_OR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |OR s      |**0P00|Logical inclusive OR |A=Avs                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _F = Nflag | Cflag;
    _A = 0x25;
    _BCdword = 0x00018800;

    // Perform operation
    OR(_B);

    // Check Result
    if ( (_A != 0xAD) ||
         (_B != 0x88) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _F = 0;
    _A = 0x00;
    _BCdword = 0x00010000;

    // Perform operation
    OR(_B);

    // Check Result
    if ( (_A != 0x00) ||
         (_B != 0x00) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_POP(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_POP
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |POP xx    |------|Pop                  |xx=[SP]+              |
 * |POP qq    |------|Pop                  |qq=[SP]+              |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _SPdword = 0x000157FE;
    _BCdword = 0x00018899;
    *(NativeCPC->pbRAM + _SP + 0) = 0x55;
    *(NativeCPC->pbRAM + _SP + 1) = 0xAA;

    // Perform operation
    POP(BC);

    // Check Result
    if ( (_SP != 0x5800) ||
         (_BC != 0xAA55) ||
         (_F != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _SPdword = 0x000157FF;
    _BCdword = 0x00018899;
    *(NativeCPC->pbRAM + _SP + 0) = 0xAA;
    *(NativeCPC->pbRAM + _SP + 1) = 0x55;

    // Perform operation
    POP(BC);

    // Check Result
    if ( (_SP != 0x5801) ||
         (_BC != 0x55AA) ||
         (*(NativeCPC->pbRAM + _SP - 2) != 0xAA) ||
         (*(NativeCPC->pbRAM + _SP - 1) != 0x55) ||
         (_F != 0x55) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_PUSH(tNativeCPC* NativeCPC,
                         tULong NoTest)
/***********************************************************************
 *
 *  TestU_PUSH
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |PUSH xx   |------|Push                 |-[SP]=xx              |
 * |PUSH qq   |------|Push                 |-[SP]=qq              |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _SPdword = 0x00015800;
    _BCdword = 0x00018899;
    *(NativeCPC->pbRAM + _SP - 1) = 0x55;
    *(NativeCPC->pbRAM + _SP - 2) = 0xAA;

    // Perform operation
    PUSH(BC);

    // Check Result
    if ( (_SP != 0x57FE) ||
         (*(NativeCPC->pbRAM + _SP + 1) != 0x88) ||
         (*(NativeCPC->pbRAM + _SP + 0) != 0x99) ||
         (_F != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _SPdword = 0x00015801;
    _BCdword = 0x00018899;
    *(NativeCPC->pbRAM + _SP - 1) = 0xAA;
    *(NativeCPC->pbRAM + _SP - 2) = 0x55;

    // Perform operation
    PUSH(BC);

    // Check Result
    if ( (_BC != 0x8899) ||
         (_SP != 0x57FF) ||
         (*(NativeCPC->pbRAM + _SP + 1) != 0x88) ||
         (*(NativeCPC->pbRAM + _SP + 0) != 0x99) ||
         (_F != 0x55) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RES(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_RES
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RES b,m   |------|Reset bit            |m=m&{~2^b}            |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _BCdword = 0x0001FFAA;

    // Perform operation
    _B = RES(1, _B);

    // Check Result
    if ( (_F != 0xAA) ||
         (_B != 0xFD) ||
         (_C != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _BCdword = 0x0001FFAA;

    // Perform operation
    _C = RES(1, _C);

    // Check Result
    if ( (_F != 0x55) ||
         (_B != 0xFF) ||
         (_C != 0xA8) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RES_MHL(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_RES_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RES b,[HL]|------|Reset bit            |[HL]=[HL]&{~2^b}      |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL) = 0xFF;

    // Perform operation
    RES_MHL(1);

    // Check Result
    if ( (_F != 0) ||
         (_HL != 0x4200) ||
         (*(NativeCPC->pbRAM + _HL) != 0xFD) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RES_REG_ADDR(tNativeCPC* NativeCPC,
                                 tULong NoTest)
/***********************************************************************
 *
 *  TestU_RES_REG_ADDR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RES b,m   |------|Reset bit            |m=m&{~2^b}            |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _BCdword = 0x00014200;
    _IXdword = 0x00015000;
    *(NativeCPC->pbRAM + _IX) = 0xFF;

    // Perform operation
    RES_B_ADDR(_IX, 0);

    // Check Result
    if ( (_F != 0xAA) ||
         (_B != 0xFE) ||
         (_IX != 0x5000) ||
         (*(NativeCPC->pbRAM + _IX) != 0xFE) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _BCdword = 0x0001FF00;
    _IYdword = 0x00015000;
    *(NativeCPC->pbRAM + _IY) = 0xFF;

    // Perform operation
    RES_B_ADDR(_IY, 7);

    // Check Result
    if ( (_F != 0x55) ||
         (_B != 0x7F) ||
         (_IY != 0x5000) ||
         (*(NativeCPC->pbRAM + _IY) != 0x7F) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RET(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_RET
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RET       |------|Return               |PC=[SP]+              |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _PCdword = 0x00014000;
    _SPdword = 0x000157FE;
    *(NativeCPC->pbRAM + _SP + 0) = 0x55;
    *(NativeCPC->pbRAM + _SP + 1) = 0x44;

    // Perform operation
    RET;

    // Check Result
    if ( (_SP != 0x5800) ||
         (_PC != 0x4455) ||
         (_F != 0xAA) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _PCdword = 0x0001FFFF;
    _SPdword = 0x000157FF;
    *(NativeCPC->pbRAM + _SP + 0) = 0x55;
    *(NativeCPC->pbRAM + _SP + 1) = 0x44;

    // Perform operation
    RET;

    // Check Result
    if ( (_SP != 0x5801) ||
         (_PC != 0x4455) ||
         (_F != 0x55) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RL(tNativeCPC* NativeCPC,
                       tULong NoTest)
/***********************************************************************
 *
 *  TestU_RL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RL m      |**0P0*|Rotate Left          |m={CY,m}<-            |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag | Cflag;
    _BCdword = 0x00013C00;

    // Perform operation
    _B = RL(Z80, _B);

    // Check Result
    if ( (_B != 0x79) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Hflag | Nflag;
    _DEdword = 0x0001C100;

    // Perform operation
    _D = RL(Z80, _D);

    // Check Result
    if ( (_D != 0x82) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RL_MHL(tNativeCPC* NativeCPC,
                           tULong NoTest)
/***********************************************************************
 *
 *  TestU_RL_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RL [HL]   |**0P0*|Rotate Left          |[HL]={CY,[HL]}<-      |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag | Cflag;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL) = 0xAA;

    // Perform operation
    RL_MHL;

    // Check Result
    if ( (_HL != 0x4200) ||
         (*(NativeCPC->pbRAM + _HL) != 0x55) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RL_REG_ADDR(tNativeCPC* NativeCPC,
                                tULong NoTest)
/***********************************************************************
 *
 *  TestU_RL_REG_ADDR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RL m      |**0P0*|Rotate Left          |m={CY,m}<-            |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00011122;
    _IXdword = 0x00014200;
    *(NativeCPC->pbRAM + _IX) = 0x44;

    // Perform operation
    RL_B_ADDR(_IX);

    // Check Result
    if ( (_B != 0x88) ||
         (_C != 0x22) ||
         (*(NativeCPC->pbRAM + _IX) != 0x44) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RLA(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_RLA
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RLA       |--0-0*|Rotate Left Acc.     |A={CY,A}<-            |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _A = 0x80;
    _F = Hflag | Nflag;

    // Perform operation
    RLA;

    // Check Result
    if ( (_A != 0x00) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _A = 0x40;
    _F = Sflag | Zflag | Pflag | Cflag;

    // Perform operation
    RLA;

    // Check Result
    if ( (_A != 0x81) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RLC(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_RLC
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RLC m     |**0P0*|Rotate Left Circular |m=m<-                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00018155;

    // Perform operation
    _B = RLC(Z80, _B);

    // Check Result
    if ( (_B != 0x03) ||
         (_C != 0x55) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Hflag | Nflag | Cflag;
    _BCdword = 0x00014355;

    // Perform operation
    _B = RLC(Z80, _B);

    // Check Result
    if ( (_B != 0x86) ||
         (_C != 0x55) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 3; // # test
    _F = Hflag | Nflag | Cflag;
    _BCdword = 0x00010055;

    // Perform operation
    _B = RLC(Z80, _B);

    // Check Result
    if ( (_B != 0x00) ||
         (_C != 0x55) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RLC_MHL(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_RLC_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RLC m     |**0P0*|Rotate Left Circular |m=m<-                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL) = 0xAA;

    // Perform operation
    RLC_MHL;

    // Check Result
    if ( (_HL != 0x4200) ||
         (*(NativeCPC->pbRAM + _HL) != 0x55) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RLC_REG_ADDR(tNativeCPC* NativeCPC,
                                 tULong NoTest)
/***********************************************************************
 *
 *  TestU_RLC_REG_ADDR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RLC m     |**0P0*|Rotate Left Circular |m=m<-                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _BCdword = 0x00015533;
    _IXdword = 0x00015000;
    *(NativeCPC->pbRAM + _IX) = 0x24;

    // Perform operation
    RLC_B_ADDR(_IX);

    // Check Result
    if ( (_B != 0x48) ||
         (_C != 0x33) ||
         (_IX != 0x5000) ||
         (*(NativeCPC->pbRAM + _IX) != 0x48) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RLCA(tNativeCPC* NativeCPC,
                         tULong NoTest)
/***********************************************************************
 *
 *  TestU_RLCA
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RLCA      |--0-0*|Rotate Left Circular |A=A<-                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _A = 0x88;
    _F = 0;

    // Perform operation
    RLCA;

    // Check Result
    if ( (_A != 0x11) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _A = 0x11;
    _F = Sflag | Zflag | Hflag | Pflag | Nflag | Cflag;

    // Perform operation
    RLCA;

    // Check Result
    if ( (_A != 0x22) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RLD(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_RLD
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RLD       |**0P0-|Rotate Left 4 bits   |{A,[HL]}={A,[HL]}<- ##|
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _D = 1; // # test
    _F = Hflag | Nflag;
    _A = 0x12;
    _HLdword = 0x00015000;
    *(NativeCPC->pbRAM + _HL) = 0x34;

    // Perform operation
    RLD;

    // Check Result
    if ( (_A != 0x13) ||
         (_HL != 0x5000) ||
         (*(NativeCPC->pbRAM + _HL) != 0x42) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _D = 2; // # test
    _F = Hflag | Nflag | Cflag;
    _A = 0x0F;
    _HLdword = 0x00015000;
    *(NativeCPC->pbRAM + _HL) = 0x0F;

    // Perform operation
    RLD;

    // Check Result
    if ( (_A != 0x00) ||
         (_HL != 0x5000) ||
         (*(NativeCPC->pbRAM + _HL) != 0xFF) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _D = 3; // # test
    _F = Hflag | Nflag | Cflag;
    _A = 0x8F;
    _HLdword = 0x00015000;
    *(NativeCPC->pbRAM + _HL) = 0x0F;

    // Perform operation
    RLD;

    // Check Result
    if ( (_A != 0x80) ||
         (_HL != 0x5000) ||
         (*(NativeCPC->pbRAM + _HL) != 0xFF) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }
    
    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RR(tNativeCPC* NativeCPC,
                       tULong NoTest)
/***********************************************************************
 *
 *  TestU_RR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RR m      |**0P0*|Rotate Right         |m=->{CY,m}            |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00018155;

    // Perform operation
    _B = RR(Z80, _B);

    // Check Result
    if ( (_B != 0x40) ||
         (_C != 0x55) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Hflag | Nflag | Cflag;
    _BCdword = 0x00018255;

    // Perform operation
    _B = RR(Z80, _B);

    // Check Result
    if ( (_B != 0xC1) ||
         (_C != 0x55) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }
    
    // Prepare conditions
    _A = 3; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00010155;

    // Perform operation
    _B = RR(Z80, _B);

    // Check Result
    if ( (_B != 0x00) ||
         (_C != 0x55) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }
    
    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RR_MHL(tNativeCPC* NativeCPC,
                           tULong NoTest)
/***********************************************************************
 *
 *  TestU_RR_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RR m      |**0P0*|Rotate Right         |m=->{CY,m}            |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL) = 0x12;

    // Perform operation
    RR_MHL;

    // Check Result
    if ( (_HL != 0x4200) ||
         (*(NativeCPC->pbRAM + _HL) != 0x09) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RRA(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_RRA
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RRA       |--0-0*|Rotate Right Acc.    |A=->{CY,A}            |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _A = 0x11;
    _F = 0;

    // Perform operation
    RRA;

    // Check Result
    if ( (_A != 0x08) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _A = 0x12;
    _F = Sflag | Zflag | Hflag | Pflag | Nflag | Cflag;

    // Perform operation
    RRA;

    // Check Result
    if ( (_A != 0x89) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RRC(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_RRC
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RRC m     |**0P0*|Rotate Right Circular|m=->m                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00014155;

    // Perform operation
    _B = RRC(Z80, _B);

    // Check Result
    if ( (_B != 0xA0) ||
         (_C != 0x55) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0;
    _BCdword = 0x00010055;

    // Perform operation
    _B = RRC(Z80, _B);

    // Check Result
    if ( (_B != 0x00) ||
         (_C != 0x55) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }
    
    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RRC_MHL(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_RRC_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RRC m     |**0P0*|Rotate Right Circular|m=->m                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL) = 0x11;

    // Perform operation
    RRC_MHL;

    // Check Result
    if ( (_HL != 0x4200) ||
         (*(NativeCPC->pbRAM + _HL) != 0x88) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RRC_REG_ADDR(tNativeCPC* NativeCPC,
                                 tULong NoTest)
/***********************************************************************
 *
 *  TestU_RRC_REG_ADDR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RRC m     |**0P0*|Rotate Right Circular|m=->m                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _BCdword = 0x00015533;
    _IXdword = 0x00015000;
    *(NativeCPC->pbRAM + _IX) = 0x11;

    // Perform operation
    RRC_B_ADDR(_IX);

    // Check Result
    if ( (_B != 0x88) ||
         (_C != 0x33) ||
         (_IX != 0x5000) ||
         (*(NativeCPC->pbRAM + _IX) != 0x88) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RRCA(tNativeCPC* NativeCPC,
                         tULong NoTest)
/***********************************************************************
 *
 *  TestU_RRCA
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RRCA      |--0-0*|Rotate Right Circular|A=->A                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _A = 0x11;
    _F = 0;

    // Perform operation
    RRCA;

    // Check Result
    if ( (_A != 0x88) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _A = 0x22;
    _F = Sflag | Zflag | Hflag | Pflag | Nflag | Cflag;

    // Perform operation
    RRCA;

    // Check Result
    if ( (_A != 0x11) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RRD(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_RRD
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RRD       |**0P0-|Rotate Right 4 bits  |{A,[HL]}=->{A,[HL]} ##|
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _D = 1; // # test
    _F = Hflag | Nflag;
    _A = 0x92;
    _HLdword = 0x00015000;
    *(NativeCPC->pbRAM + _HL) = 0x34;

    // Perform operation
    RRD;

    // Check Result
    if ( (_A != 0x94) ||
         (_HL != 0x5000) ||
         (*(NativeCPC->pbRAM + _HL) != 0x23) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _D = 2; // # test
    _F = Hflag | Nflag | Cflag;
    _A = 0x02;
    _HLdword = 0x00015000;
    *(NativeCPC->pbRAM + _HL) = 0x30;

    // Perform operation
    RRD;

    // Check Result
    if ( (_A != 0x00) ||
         (_HL != 0x5000) ||
         (*(NativeCPC->pbRAM + _HL) != 0x23) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_RST(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_RST
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |RST p     |------|Restart              | (p=0H,8H,10H,...,38H)|
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xFF;
    _PCdword = 0x00014321;
    _SPdword = 0x00015800;
    *(NativeCPC->pbRAM + _SP - 1) = 0xAA;
    *(NativeCPC->pbRAM + _SP - 2) = 0x55;

    // Perform operation
    RST(0xABCD);

    // Check Result
    if ( (_PC != 0xABCD) ||
         (_SP != 0x57FE) ||
         (*(NativeCPC->pbRAM + _SP + 1) != 0x43) ||
         (*(NativeCPC->pbRAM + _SP + 0) != 0x21) ||
         (_F != 0xFF) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x00;
    _PCdword = 0x00018765;
    _SPdword = 0x000157FF;
    *(NativeCPC->pbRAM + _SP - 1) = 0xAA;
    *(NativeCPC->pbRAM + _SP - 2) = 0x55;

    // Perform operation
    RST(0x4321);

    // Check Result
    if ( (_PC != 0x4321) ||
         (_SP != 0x57FD) ||
         (*(NativeCPC->pbRAM + _SP + 1) != 0x87) ||
         (*(NativeCPC->pbRAM + _SP + 0) != 0x65) ||
         (_F != 0x00) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SBC(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_SBC
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SBC A,s   |***V1*|Subtract with Carry  |A=A-s-CY              |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _F = Cflag;
    _A = 0xF0;
    _BCdword = 0x0001EF00;

    // Perform operation
    SBC(_B);

    // Check Result
    if ( (_A != 0x00) ||
         (_B != 0xEF) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _F = Cflag;
    _A = 0x10;
    _BCdword = 0x0001EF00;

    // Perform operation
    SBC(_B);

    // Check Result
    if ( (_A != 0x20) ||
         (_B != 0xEF) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _L = 3; // # test
    _F = 0;
    _A = 0xEF;
    _BCdword = 0x00011000;

    // Perform operation
    SBC(_B);

    // Check Result
    if ( (_A != 0xDF) ||
         (_B != 0x10) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 4; // # test
    _F = 0;
    _A = 0x80;
    _BCdword = 0x00011000;

    // Perform operation
    SBC(_B);

    // Check Result
    if ( (_A != 0x70) ||
         (_B != 0x10) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SBC16(tNativeCPC* NativeCPC,
                          tULong NoTest)
/***********************************************************************
 *
 *  TestU_SBC16
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SBC HL,ss |**?V1*|Subtract with Carry  |HL=HL-ss-CY           |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Cflag;
    _HLdword = 0x0001F000;
    _DEdword = 0x0001EFFF;

    // Perform operation
    SBC16(DE);

    // Check Result
    if ( (_HL != 0x0000) ||
         (_DE != 0xEFFF) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0;
    _HLdword = 0x00011000;
    _DEdword = 0x0000A000;

    // Perform operation
    SBC16(DE);

    // Check Result
    if ( (_HL != 0x7000) ||
         (_DE != 0xA000) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 3; // # test
    _F = Cflag;
    _HLdword = 0x0001F000;
    _DEdword = 0x00008FFF;

    // Perform operation
    SBC16(DE);

    // Check Result
    if ( (_HL != 0x6000) ||
         (_DE != 0x8FFF) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 4; // # test
    _F = Cflag;
    _HLdword = 0x00018000;
    _DEdword = 0x00001FFF;

    // Perform operation
    SBC16(DE);

    // Check Result
    if ( (_HL != 0x6000) ||
         (_DE != 0x1FFF) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 5; // # test
    _F = Cflag;
    _HLdword = 0x0001F000;
    _DEdword = 0x00001FFF;

    // Perform operation
    SBC16(DE);

    // Check Result
    if ( (_HL != 0xD000) ||
         (_DE != 0x1FFF) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SET(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_SET
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SET b,m   |------|Set bit              |m=mv{2^b}             |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _BCdword = 0x00010000;

    // Perform operation
    _B = SET(0, _B);

    // Check Result
    if ( (_F != 0xAA) ||
         (_B != 0x01) ||
         (_C != 0x00) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _BCdword = 0x00010000;

    // Perform operation
    _B = SET(7, _B);

    // Check Result
    if ( (_F != 0x55) ||
         (_B != 0x80) ||
         (_C != 0x00) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SET_MHL(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_SET_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SET b,m   |------|Set bit              |m=mv{2^b}             |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL) = 0x00;

    // Perform operation
    SET_MHL(1);

    // Check Result
    if ( (_F != 0) ||
         (_HL != 0x4200) ||
         (*(NativeCPC->pbRAM + _HL) != 0x02) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SET_REG_ADDR(tNativeCPC* NativeCPC,
                                 tULong NoTest)
/***********************************************************************
 *
 *  TestU_SET_REG_ADDR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SET b,m   |------|Set bit              |m=mv{2^b}             |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0xAA;
    _BCdword = 0x00014200;
    _IXdword = 0x00015000;
    *(NativeCPC->pbRAM + _IX) = 0x00;

    // Perform operation
    SET_B_ADDR(_IX, 0);

    // Check Result
    if ( (_F != 0xAA) ||
         (_B != 0x01) ||
         (_IX != 0x5000) ||
         (*(NativeCPC->pbRAM + _IX) != 0x01) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = 0x55;
    _BCdword = 0x0001FF00;
    _IYdword = 0x00015000;
    *(NativeCPC->pbRAM + _IY) = 0x00;

    // Perform operation
    SET_B_ADDR(_IY, 7);

    // Check Result
    if ( (_F != 0x55) ||
         (_B != 0x80) ||
         (_IY != 0x5000) ||
         (*(NativeCPC->pbRAM + _IY) != 0x80) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SLA(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_SLA
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SLA m     |**0P0*|Shift Left Arithmetic|m=m*2                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00014211;

    // Perform operation
    _B = SLA(Z80, _B);

    // Check Result
    if ( (_B != 0x84) ||
         (_C != 0x11) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00018011;

    // Perform operation
    _B = SLA(Z80, _B);

    // Check Result
    if ( (_B != 0x00) ||
         (_C != 0x11) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 3; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x0001C011;

    // Perform operation
    _B = SLA(Z80, _B);

    // Check Result
    if ( (_B != 0x80) ||
         (_C != 0x11) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SLA_MHL(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_SLA_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SLA m     |**0P0*|Shift Left Arithmetic|m=m*2                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL) = 0x88;

    // Perform operation
    SLA_MHL;

    // Check Result
    if ( (_HL != 0x4200) ||
         (*(NativeCPC->pbRAM + _HL) != 0x10) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SLA_REG_ADDR(tNativeCPC* NativeCPC,
                                 tULong NoTest)
/***********************************************************************
 *
 *  TestU_SLA_REG_ADDR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SLA m     |**0P0*|Shift Left Arithmetic|m=m*2                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _BCdword = 0x00015533;
    _IXdword = 0x00015000;
    *(NativeCPC->pbRAM + _IX) = 0x88;

    // Perform operation
    SLA_B_ADDR(_IX);

    // Check Result
    if ( (_B != 0x10) ||
         (_C != 0x33) ||
         (_IX != 0x5000) ||
         (*(NativeCPC->pbRAM + _IX) != 0x10) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SLL(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_SLL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SLL m     |**0P0*|Logical Shift Left   |m=m*2+1               |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00018111;

    // Perform operation
    _B = SLL(Z80, _B);

    // Check Result
    if ( (_B != 0x03) ||
         (_C != 0x11) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Hflag | Nflag | Cflag;
    _BCdword = 0x00014411;

    // Perform operation
    _B = SLL(Z80, _B);

    // Check Result
    if ( (_B != 0x89) ||
         (_C != 0x11) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SLL_MHL(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_SLL_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SLL m     |**0P0*|Logical Shift Left   |m=m*2+1               |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL) = 0x44;

    // Perform operation
    SLL_MHL;

    // Check Result
    if ( (_HL != 0x4200) ||
         (*(NativeCPC->pbRAM + _HL) != 0x89) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SLL_REG_ADDR(tNativeCPC* NativeCPC,
                                 tULong NoTest)
/***********************************************************************
 *
 *  TestU_SLL_REG_ADDR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SLL m     |**0P0*|Logical Shift Left   |m=m*2+1               |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _BCdword = 0x00015533;
    _IXdword = 0x00015000;
    *(NativeCPC->pbRAM + _IX) = 0x44;

    // Perform operation
    SLL_B_ADDR(_IX);

    // Check Result
    if ( (_B != 0x89) ||
         (_C != 0x33) ||
         (_IX != 0x5000) ||
         (*(NativeCPC->pbRAM + _IX) != 0x89) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SRA(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_SRA
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SRA m     |**0P0*|Shift Right Arith.   |m=m/2                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00018211;

    // Perform operation
    _B = SRA(Z80, _B);

    // Check Result
    if ( (_B != 0xC1) ||
         (_C != 0x11) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00010111;

    // Perform operation
    _B = SRA(Z80, _B);

    // Check Result
    if ( (_B != 0x00) ||
         (_C != 0x11) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }
    
    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SRA_MHL(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_SRA_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SRA m     |**0P0*|Shift Right Arith.   |m=m/2                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL) = 0x88;

    // Perform operation
    SRA_MHL;

    // Check Result
    if ( (_HL != 0x4200) ||
         (*(NativeCPC->pbRAM + _HL) != 0xC4) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SRA_REG_ADDR(tNativeCPC* NativeCPC,
                                 tULong NoTest)
/***********************************************************************
 *
 *  TestU_SRA_REG_ADDR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SRA m     |**0P0*|Shift Right Arith.   |m=m/2                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _BCdword = 0x00015533;
    _IXdword = 0x00015000;
    *(NativeCPC->pbRAM + _IX) = 0x88;

    // Perform operation
    SRA_B_ADDR(_IX);

    // Check Result
    if ( (_B != 0xC4) ||
         (_C != 0x33) ||
         (_IX != 0x5000) ||
         (*(NativeCPC->pbRAM + _IX) != 0xC4) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SRL(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_SRL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SRL m     |**0P0*|Shift Right Logical  |m=->{0,m,CY}          |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _BCdword = 0x00010111;

    // Perform operation
    _B = SRL(Z80, _B);

    // Check Result
    if ( (_B != 0x00) ||
         (_C != 0x11) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _A = 2; // # test
    _F = Hflag | Nflag | Cflag;
    _BCdword = 0x00018611;

    // Perform operation
    _B = SRL(Z80, _B);

    // Check Result
    if ( (_B != 0x43) ||
         (_C != 0x11) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SRL_MHL(tNativeCPC* NativeCPC,
                            tULong NoTest)
/***********************************************************************
 *
 *  TestU_SRL_MHL
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SRL m     |**0P0*|Shift Right Logical  |m=->{0,m,CY}          |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = Hflag | Nflag;
    _HLdword = 0x00014200;
    *(NativeCPC->pbRAM + _HL) = 0x86;

    // Perform operation
    SRL_MHL;

    // Check Result
    if ( (_HL != 0x4200) ||
         (*(NativeCPC->pbRAM + _HL) != 0x43) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SRL_REG_ADDR(tNativeCPC* NativeCPC,
                                 tULong NoTest)
/***********************************************************************
 *
 *  TestU_SRL_REG_ADDR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SRL m     |**0P0*|Shift Right Logical  |m=->{0,m,CY}          |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _A = 1; // # test
    _F = 0;
    _BCdword = 0x00015533;
    _IXdword = 0x00015000;
    *(NativeCPC->pbRAM + _IX) = 0x86;

    // Perform operation
    SRL_B_ADDR(_IX);

    // Check Result
    if ( (_B != 0x43) ||
         (_C != 0x33) ||
         (_IX != 0x5000) ||
         (*(NativeCPC->pbRAM + _IX) != 0x43) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_SUB(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_SUB
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |SUB s     |***V1*|Subtract             |A=A-s                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _A = 0x00;
    _BCdword = 0x0001A000;

    // Perform operation
    SUB(_B);

    // Check Result
    if ( (_A != 0x60) ||
         (_B != 0xA0) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != Cflag) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _A = 0x10;
    _BCdword = 0x00010100;

    // Perform operation
    SUB(_B);

    // Check Result
    if ( (_A != 0x0F) ||
         (_B != 0x01) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != Hflag) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 3; // # test
    _A = 0xA0;
    _BCdword = 0x00014000;

    // Perform operation
    SUB(_B);

    // Check Result
    if ( (_A != 0x60) ||
         (_B != 0x40) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != Vflag) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 4; // # test
    _A = 0xE0;
    _BCdword = 0x00011000;

    // Perform operation
    SUB(_B);

    // Check Result
    if ( (_A != 0xD0) ||
         (_B != 0x10) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 5; // # test
    _A = 0x40;
    _BCdword = 0x00014000;

    // Perform operation
    SUB(_B);

    // Check Result
    if ( (_A != 0x00) ||
         (_B != 0x40) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Vflag) != 0) ||
         ((_F & Nflag) != Nflag) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


static tULong TestU_XOR(tNativeCPC* NativeCPC,
                        tULong NoTest)
/***********************************************************************
 *
 *  TestU_XOR
 *
 * ----------------------------------------------------------------
 * |Mnemonic  |SZHPNC|Description          |Notes                 |
 * |----------+------+---------------------+----------------------|
 * |XOR s     |**0P00|Logical Exclusive OR |A=Axs                 |
 * ----------------------------------------------------------------
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tZ80* Z80 = NativeCPC->Z80;

  do
  {
    // Prepare conditions
    _L = 1; // # test
    _F = Nflag | Cflag;
    _A = 0xE9;
    _BCdword = 0x00017800;

    // Perform operation
    XOR(_B);

    // Check Result
    if ( (_A != 0x91) ||
         (_B != 0x78) ||
         ((_F & Sflag) != Sflag) ||
         ((_F & Zflag) != 0) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != 0) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Prepare conditions
    _L = 2; // # test
    _F = 0;
    _A = 0x55;
    _BCdword = 0x00015500;

    // Perform operation
    XOR(_B);

    // Check Result
    if ( (_A != 0x00) ||
         (_B != 0x55) ||
         ((_F & Sflag) != 0) ||
         ((_F & Zflag) != Zflag) ||
         ((_F & Hflag) != 0) ||
         ((_F & Pflag) != Pflag) ||
         ((_F & Nflag) != 0) ||
         ((_F & Cflag) != 0) )
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/


//
// Mathlib Unitary Tests
//
static tULong TestU_math_fabs(tNativeCPC* NativeCPC, tULong NoTest)
/***********************************************************************
 *
 *  TestU_math_fabs
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tLong   VarLong;

	NOT_USED(NativeCPC);

  do
  {
    // Prepare conditions
    VarLong = 1024;
    // Perform operation
    VarLong = (tLong)math_fabs(VarLong);
    // Check Result
    if (VarLong != 1024)
    {
      continue;
    }

    // Prepare conditions
    VarLong = -1024;
    // Perform operation
    VarLong = (tLong)math_fabs(VarLong);
    // Check Result
    if (VarLong != 1024)
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/

static tULong TestU_math_cos(tNativeCPC* NativeCPC, tULong NoTest)
/***********************************************************************
 *
 *  TestU_math_cos
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tDouble Angle;
tLong VarLong;

	NOT_USED(NativeCPC);

  do
  {
    // Prepare conditions
    Angle = 0.;
    // Perform operation
    VarLong = (tLong)(math_cos(Angle) * 1024.);
    // Check Result
    if (VarLong != 1024)
    {
      continue;
    }

    // Prepare conditions
    Angle = M_PI / 4;
    // Perform operation
    VarLong = (tLong)(math_cos(Angle) * 1024.);
    // Check Result
    if (VarLong != 724)
    {
      continue;
    }

    // Prepare conditions
    Angle = M_PI / 2;
    // Perform operation
    VarLong = (tLong)(math_cos(Angle) * 1024.);
    // Check Result
    if (VarLong != 0)
    {
      continue;
    }

    // Prepare conditions
    Angle = 3 * M_PI / 4;
    // Perform operation
    VarLong = (tLong)(math_cos(Angle) * 1024.);
    // Check Result
    if (VarLong != -724)
    {
      continue;
    }

    // Prepare conditions
    Angle = M_PI;
    // Perform operation
    VarLong = (tLong)(math_cos(Angle) * 1024.);
    // Check Result
    if (VarLong != -1024)
    {
      continue;
    }

    // Prepare conditions
    Angle = M_PI * 2;
    // Perform operation
    VarLong = (tLong)(math_cos(Angle) * 1024.);
    // Check Result
    if (VarLong != 1024)
    {
      continue;
    }

    // Prepare conditions
    Angle = M_PI * 4;
    // Perform operation
    VarLong = (tLong)(math_cos(Angle) * 1024.);
    // Check Result
    if (VarLong != 1024)
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/

static tULong TestU_math_log10(tNativeCPC* NativeCPC, tULong NoTest)
/***********************************************************************
 *
 *  TestU_math_log10
 *
 ***********************************************************************/
{
tRawDouble RawDouble;
tULong TestResult = NoTest;

	NOT_USED(NativeCPC);

  do
  {
    // Prepare conditions
    // Perform operation
    RawDouble.d = math_log10(9.0);
    // Check Result
    if (((tLong)RawDouble.d) != 0)
    {
      continue;
    }

    // Prepare conditions
    // Perform operation
    RawDouble.d = math_log10(99.0);
    // Check Result
    if (((tLong)RawDouble.d) != 1)
    {
      continue;
    }

    // Prepare conditions
    // Perform operation
    RawDouble.d = math_log10(999999999.0);
    // Check Result
    if (((tLong)RawDouble.d) != 8)
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/

static tULong TestU_math_float(tNativeCPC* NativeCPC, tULong NoTest)
/***********************************************************************
 *
 *  TestU_math_float
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tUChar BASICFloat[5];
tChar  CurrentString[20];

	NOT_USED(NativeCPC);

  do
  {
    // BASIC: a=32768
    // Prepare conditions
    BASICFloat[0] = 0x00;
    BASICFloat[1] = 0x00;
    BASICFloat[2] = 0x00;
    BASICFloat[3] = 0x00;
    BASICFloat[4] = 0x90;
    // Perform operation
    (void)BASIC_FloatToString(BASICFloat, CurrentString);
    // Check Result
    if (StringCompare(CurrentString, "32768"))
    {
      continue;
    }

    // BASIC: a=48621
    // Prepare conditions
    BASICFloat[0] = 0x00;
    BASICFloat[1] = 0x00;
    BASICFloat[2] = 0xED;
    BASICFloat[3] = 0x3D;
    BASICFloat[4] = 0x90;
    // Perform operation
    (void)BASIC_FloatToString(BASICFloat, CurrentString);
    // Check Result
    if (StringCompare(CurrentString, "48621"))
    {
      continue;
    }

    // BASIC: a=3.14159265359
    // Prepare conditions
    BASICFloat[0] = 0x36;
    BASICFloat[1] = 0xDA;
    BASICFloat[2] = 0x0F;
    BASICFloat[3] = 0x49;
    BASICFloat[4] = 0x82;
    // Perform operation
    (void)BASIC_FloatToString(BASICFloat, CurrentString);
    // Check Result
    if (StringCompare(CurrentString, "3.141592552"))
    {
      continue;
    }

    // BASIC: a=123456789
    // Prepare conditions
    BASICFloat[0] = 0xA0;
    BASICFloat[1] = 0xA2;
    BASICFloat[2] = 0x79;
    BASICFloat[3] = 0x6B;
    BASICFloat[4] = 0x9B;
    // Perform operation
    (void)BASIC_FloatToString(BASICFloat, CurrentString);
    // Check Result
    if (StringCompare(CurrentString, "123456789"))
    {
      continue;
    }

    // BASIC: a=999999999
    // Prepare conditions
    BASICFloat[0] = 0xFC;
    BASICFloat[1] = 0x27;
    BASICFloat[2] = 0x6B;
    BASICFloat[3] = 0x6E;
    BASICFloat[4] = 0x9E;
    // Perform operation
    (void)BASIC_FloatToString(BASICFloat, CurrentString);
    // Check Result
    if (StringCompare(CurrentString, "999999999"))
    {
      continue;
    }

    // BASIC: a=1000000000
    // Prepare conditions
    BASICFloat[0] = 0x00;
    BASICFloat[1] = 0x28;
    BASICFloat[2] = 0x6B;
    BASICFloat[3] = 0x6E;
    BASICFloat[4] = 0x9E;
    // Perform operation
    (void)BASIC_FloatToString(BASICFloat, CurrentString);
    // Check Result
    if (StringCompare(CurrentString, "1E+09"))
    {
      continue;
    }

    // BASIC: a=0.12345E+24
    // Prepare conditions
    BASICFloat[0] = 0x3D;
    BASICFloat[1] = 0xE7;
    BASICFloat[2] = 0x21;
    BASICFloat[3] = 0x51;
    BASICFloat[4] = 0xCD;
    // Perform operation
    (void)BASIC_FloatToString(BASICFloat, CurrentString);
    // Check Result
    if (StringCompare(CurrentString, "1.234499999E+23"))
    {
      continue;
    }

    // BASIC: a=0.12345E-24
    // Prepare conditions
    BASICFloat[0] = 0xDD;
    BASICFloat[1] = 0xDD;
    BASICFloat[2] = 0xD2;
    BASICFloat[3] = 0x18;
    BASICFloat[4] = 0x2E;
    // Perform operation
    (void)BASIC_FloatToString(BASICFloat, CurrentString);
    // Check Result
    if (StringCompare(CurrentString, "1.2345E-25"))
    {
      continue;
    }

    // BASIC: a=0.1E-8
    // Prepare conditions
    BASICFloat[0] = 0x41;
    BASICFloat[1] = 0x5F;
    BASICFloat[2] = 0x70;
    BASICFloat[3] = 0x09;
    BASICFloat[4] = 0x63;
    // Perform operation
    (void)BASIC_FloatToString(BASICFloat, CurrentString);
    // Check Result
    if (StringCompare(CurrentString, "0.0000000009"))
    {
      continue;
    }

    // BASIC: a=0.11E-8
    // Prepare conditions
    BASICFloat[0] = 0x2E;
    BASICFloat[1] = 0xCF;
    BASICFloat[2] = 0x2E;
    BASICFloat[3] = 0x17;
    BASICFloat[4] = 0x63;
    // Perform operation
    (void)BASIC_FloatToString(BASICFloat, CurrentString);
    // Check Result
    if (StringCompare(CurrentString, "0.000000001")) // Should be 1.1E-09
    {
      continue;
    }

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/

#ifdef ENABLE_PLAYCITY

//
// PlayCity Unitary Tests
//

static const tULong PlayCity_Freq[16] =
{
  3992187, // 0
  2000000, // 1
  3000000, // 2
  3333333, // 3
  3500000, // 4
  3600000, // 5
  3666666, // 6
  3714285, // 7
  3750000, // 8
  3777777, // 9
  3800000, // A
  3818181, // B
  3833333, // C
  3846153, // D
  3857142, // E
  3866666, // F
};

static tULong TestU_PlayCity_YMZFreq(tNativeCPC* NativeCPC, tULong NoTest)
/***********************************************************************
 *
 *  TestU_PlayCity_YMZFreq
 *
 ***********************************************************************/
{
tULong TestResult = NoTest;
tUChar Loop;
tULong TimeContant;
tULong Freq;

	NOT_USED(NativeCPC);

  do
  {
    for (Loop=0; Loop < 0x10; Loop++)
    {
      TimeContant = !Loop ? 256 : Loop;

      Freq = CLOCK_Z80 * ((TimeContant*2)-1) / (TimeContant*2);

      if (Freq != PlayCity_Freq[Loop])
        break;
    }
    if (Loop < 0x10) continue;

    // Test is OK
    TestResult = errNone;
  }
  while (0);

  return (TestResult);
}
/*----------------------------------------------------------------------------*/

#endif /* ENABLE_PLAYCITY */


#endif /* _TESTU */
//==============================================================================


#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif


