]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
CD32 FMV cartridge
authorToni Wilen <twilen@winuae.net>
Thu, 10 Jul 2014 14:16:41 +0000 (17:16 +0300)
committerToni Wilen <twilen@winuae.net>
Thu, 10 Jul 2014 14:16:41 +0000 (17:16 +0300)
12 files changed:
ar.cpp
cd32_fmv.cpp
cd32_fmv_genlock.cpp [new file with mode: 0644]
cfgfile.cpp
custom.cpp
drawing.cpp
expansion.cpp
include/cd32_fmv.h
include/custom.h
include/options.h
main.cpp
od-win32/mman.cpp

diff --git a/ar.cpp b/ar.cpp
index 53fbf162603d0c185b97171e88676b74306c41c0..8fe0f15a611c47a2f9aeddcdbfdc33df0cf91fd7 100644 (file)
--- a/ar.cpp
+++ b/ar.cpp
@@ -1643,6 +1643,8 @@ int action_replay_load (void)
 
        if (_tcslen (currprefs.cartfile) == 0)
                return 0;
+       if (currprefs.cs_cd32fmv)
+               return 0;
        rd = getromdatabypath (currprefs.cartfile);
        if (rd) {
                if (rd->id == 62)
index c882bf2d594330c302ecf567127741ec79b4f0a9..b3813f077237d5cc222ee99c0b718f66171dde33 100644 (file)
@@ -3,7 +3,7 @@
 *
 * CD32 FMV cartridge
 *
-* Copyright 2008-2010 Toni Wilen
+* Copyright 2008-2014 Toni Wilen
 *
 */
 
 #include "zfile.h"
 #include "cd32_fmv.h"
 #include "uae.h"
+#include "debug.h"
+#include "custom.h"
+
+#include "cda_play.h"
+#include "archivers\mp2\kjmp2.h"
+#include "mpeg2.h"
+#include "mpeg2convert.h"
 
-#define FMV_DEBUG 1
+
+#define FMV_DEBUG 0
+static int fmv_audio_debug = 0;
+static int fmv_video_debug = 0;
+#define DUMP_VIDEO 0
 
 /*
- 0x200000 - 0x23FFFF ROM
- 0x240000 io/status (single word register?)
+ 0x200000 - 0x23FFFF ROM (256k)
+ 0x240000 io/status (word)
  0x2500xx L64111 audio decoder (word registers)
+ 0x260000 CL450 data port (word)
  0x2700xx CL450 video decoder (word registers)
- 0x280000 - 0x2FFFFF RAM
+ 0x280000 - 0x2FFFFF RAM (512k)
 */
 
+#define ROM_BASE               0x000000
 #define IO_BASE                        0x040000
 #define L64111_BASE            0x050000
+#define CL450_DATA             0x060000
 #define CL450_BASE             0x070000
-#define VRAM_BASE              0x080000
+#define RAM_BASE               0x080000
 
 #define BANK_MASK              0x0F0000
 
-#define IO_IRQ_L641111 0x4000
-#define IO_IRQ_CL450   0x8000
-
-// L64111 registers (from datasheet)
-#define A_DATA                  0      //0
-#define A_CONTROL1              1      //2
-#define A_CONTROL2              2      //4
-#define A_CONTROL3              3      //6
-#define A_INT1                  4      //8
-#define A_INT2                  5      //10
-#define A_TCR                   6      //12
-#define A_TORH                  7      //14
-#define A_TORL                  8      //16
-#define A_PARAM1                9      //18
-#define A_PARAM2               10      //20
-#define A_PARAM3               11      //22
-#define A_PRESENT1             12      //24
-#define A_PRESENT2             13      //26
-#define A_PRESENT3             14      //28
-#define A_PRESENT4             15      //30
-#define A_PRESENT5             16      //32
-#define A_FIFO                 17      //34
-#define A_CB_STATUS            18      //36
-#define A_CB_WRITE             19      //38
-#define A_CB_READ              20      //40
-
-static int fmv_mask;
-static uae_u8 *rom;
-static int fmv_rom_size = 262144;
-static uaecptr fmv_start = 0x00200000;
-static int fmv_size = 1048576;
-
-static uae_u16 l64111regs[32];
-static uae_u16 l64111intmask1, l64111intmask2, l64111intstatus1, l64111intstatus2;
+// IO_BASE bits (read)
+#define IO_CL450_IRQ                   0x8000 // CL450INT_
+#define IO_L64111_IRQ                  0x4000 // L64111INT_
+#define IO_CL450_FIFO_STATUS   0x0800 // CL450CFLEVEL
+#define IO_L64111_FALF                 0x0400 // L64111FALF
+#define IO_L64111_FALE                 0x0400 // L64111FALE
+#define IO_L64111_FEMP                 0x0400 // L64111FEMP
+
+// IO_BASE bits (write)
+#define IO_CL450_VIDEO                 0x4000
+#define IO_UNK2                                        0x2000
+#define IO_UNK3                                        0x1000
+// above three are set/cleared by ROM code
+#define IO_L64111_MUTE                 0x0200
+
+// L64111 registers
+#define A_DATA                  0
+#define A_CONTROL1              1
+#define A_CONTROL2              2
+#define A_CONTROL3              3
+#define A_INT1                  4
+#define A_INT2                  5
+#define A_TCR                   6
+#define A_TORH                  7
+#define A_TORL                  8
+#define A_PARAM1                9
+#define A_PARAM2               10
+#define A_PARAM3               11
+#define A_PRESENT1             12
+#define A_PRESENT2             13
+#define A_PRESENT3             14
+#define A_PRESENT4             15
+#define A_PRESENT5             16
+#define A_FIFO                 17
+#define A_CB_STATUS            18
+#define A_CB_WRITE             19
+#define A_CB_READ              20
+// L641111 status register 1
+#define ANC_DATA_VALID         0x80
+#define ANC_DATA_FIFO_OVFL     0x40
+#define ANC_DATA_FIO_HFF       0x10
+#define ERR_BUF_OVFL           0x08
+// L641111 status register 2
+#define SYNTAX_ERR_DET 0x40
+#define PTS_AVAILABLE  0x20
+#define SYNC_AUD               0x10
+#define SYNC_SYS               0x08
+#define FRAME_DETECT_IN        0x04
+#define CRC_ERR                        0x02
+#define NEWLAST_FRAME  0x01
+#define NEW_FRAME_S            0x01
+#define LAST_FRAME_S   0x80
+// status register 2 mask bits
+#define NEW_FRAME              0x02
+#define LAST_FRAME             0x01
+
+// CL450 direct access registers
+#define CMEM_control   0x80
+#define CMEM_data              0x02
+#define CMEM_dmactrl   0x84
+#define CMEM_status            0x82
+#define CPU_control            0x20
+#define CPU_iaddr              0x3e
+#define CPU_imem               0x42
+#define CPU_int                        0x54
+#define CPU_intenb             0x26
+#define CPU_pc                 0x22
+#define CPU_taddr              0x38
+#define CPU_tmem               0x46
+#define DRAM_refcnt            0xac
+#define HOST_control   0x90
+#define HOST_intvecr   0x9c
+#define HOST_intvecw   0x98
+#define HOST_newcmd            0x56
+#define HOST_raddr             0x88
+#define HOST_rdata             0x8c
+#define HOST_scr0              0x92
+#define HOST_scr1              0x94
+#define HOST_scr2              0x96
+#define VID_control            0xec
+#define VID_regdata            0xee
+#define VID_chrom              0x0a
+#define VID_y                  0x00
+// CL450 indirect access registers
+#define VID_sela               0x0
+#define VID_selactive  0x8
+#define VID_selaux             0xc
+#define VID_selb               0x1
+#define VID_selbor             0x9
+#define VID_selGB              0xb
+#define VID_selmode            0x7
+#define VID_selR               0xA
+// CL450 commands
+#define CL_SetBlank                            0x030f
+#define CL_SetBorder                   0x0407
+#define CL_SetColorMode                        0x0111
+#define CL_SetInterruptMask            0x0104
+#define CL_SetThresHold                        0x0103
+#define CL_SetVideoFormat              0x0105
+#define CL_SetWindow                   0x0406
+#define CL_DisplayStill                        0x000c
+#define CL_Pause                               0x000e
+#define CL_Play                                        0x000d
+#define CL_Scan                                        0x000a
+#define CL_SingleStep                  0x000b
+#define CL_SlowMotion                  0x0109
+#define CL_AccessSCR                   0x8312
+#define CL_FlushBitStream              0x8102
+#define CL_InquireBufferFullness 0x8001
+#define CL_NewPacket                   0x0408
+#define CL_Reset                               0x8000
+// CL450 interrupts
+#define CL_INT_RDY             (1 << 10)
+#define CL_INT_END_D   (1 <<  5)
+#define CL_INT_ERR             (1 <<  0)
+#define CL_INT_PIC_D   (1 <<  6)
+#define CL_INT_SEQ_D   (1 <<  9)
+#define CL_INT_SCN             (1 << 11)
+#define CL_INT_UND             (1 <<  8)
+#define CL_INT_END_V   (1 <<  4)
+#define CL_INT_GOP             (1 <<  2)
+#define CL_INT_PIC_V   (1 <<  1)
+#define CL_INT_SEQ_V   (1 <<  3)
+// CL450 DRAM resident variabels (WORD address)
+#define CL_DRAM_SEQ_SEM                        0x10
+#define CL_DRAM_SEQ_CONTROL            0x11
+#define CL_DRAM_H_SIZE                 0x12
+#define CL_DRAM_V_SIZE                 0x13
+#define CL_DRAM_PICTURE_RATE   0x14
+#define CL_DRAM_FLAGS                  0x15
+#define CL_DRAM_PIC_SEM                        0x16
+#define CL_DRAM_TIME_CODE_0            0x17
+#define CL_DRAM_TIME_CODE_1            0x18
+#define CL_DRAM_TEMPORAL_REF   0x19
+#define CL_DRAM_VER                            0xa0 // 0x0200
+#define CL_DRAM_PID                            0xa1 // 0x0002
+#define CL_DRAM_CPU_PC                 0xa2
+
+static uae_u8 *rom, *ram, *audioram;
+static const int fmv_rom_size = 262144;
+static const int fmv_ram_size = 524288;
+static const uaecptr fmv_start = 0x00200000;
+static const int fmv_board_size = 1048576;
+
+#define CL_HMEM_INT_STATUS             0x0a
+#define CL450_MPEG_BUFFER              0x10000
+#define CL450_MPEG_BUFFER_SIZE  65536
+#define CL450_MPEG_DECODE_BUFFER  (CL450_MPEG_BUFFER + CL450_MPEG_BUFFER_SIZE)
+#define CL450_MPEG_DECODE_BUFFER_SIZE (fmv_ram_size - CL450_MPEG_DECODE_BUFFER)
+
+#define CL450_VIDEO_BUFFERS 8
+#define CL450_VIDEO_BUFFER_SIZE (352 * 288 * 4)
+
+static uae_u16 cl450_regs[256];
+static double cl450_scr;
+#define CL450_IMEM_WORDS (2 * 512)
+#define CL450_TMEM_WORDS 128
+#define CL450_HMEM_WORDS 16
+static uae_u16 cl450_imem[CL450_IMEM_WORDS];
+static uae_u16 cl450_tmem[CL450_TMEM_WORDS];
+static uae_u16 cl450_hmem[CL450_HMEM_WORDS];
+#define CL450_VID_REGS 16
+static uae_u16 cl450_vid[CL450_VID_REGS];
+static int cl450_play;
+static int cl450_blank;
+static uae_u32 cl450_border_color;
+static uae_u16 cl450_interruptmask;
+static uae_u16 cl450_pending_interrupts;
+static uae_u16 cl450_threshold;
+static int cl450_buffer_offset;
+static int cl450_buffer_empty_cnt;
+static int libmpeg_offset;
+
+struct cl450_videoram
+{
+       int width;
+       int height;
+       uae_u8 data[CL450_VIDEO_BUFFER_SIZE];
+};
+static struct cl450_videoram *videoram;
+
+static bool cl450_newpacket_mode;
+// Real CL450 has command buffer but we don't need to care,
+// non-NewPacket commands can be emulated immediately.
+#define CL450_NEWPACKET_BUFFER_SIZE 32
+struct cl450_newpacket
+{
+       uae_u16 length;
+       uae_u64 pts;
+       bool pts_valid;
+};
+static struct cl450_newpacket cl450_newpacket_buffer[CL450_NEWPACKET_BUFFER_SIZE];
+static int cl450_newpacket_offset_write;
+static int cl450_newpacket_offset_read;
+
+static int cl450_frame_rate;
+static int cl450_video_hsync_wait;
+static int cl450_videoram_read;
+static int cl450_videoram_write;
+static int cl450_videoram_cnt;
+static int cl450_frame_cnt;
+
+static uae_u16 l64111_regs[32];
+static uae_u16 l64111intmask[2], l64111intstatus[2];
+#define L64111_CHANNEL_BUFFERS 128
+
 static uae_u16 io_reg;
 
-static int isdebug (uaecptr addr)
+static mpeg2dec_t *mpeg_decoder;
+static const mpeg2_info_t *mpeg_info;
+
+extern addrbank fmv_bank;
+extern addrbank fmv_rom_bank;
+extern addrbank fmv_ram_bank;
+
+#if FMV_DEBUG
+static int isdebug(uaecptr addr)
 {
 #if FMV_DEBUG > 2
        if (M68K_GETPC >= 0x200100)
                return 1;
        return 0;
 #endif
-#if (FMV_DEBUG == 2)
+#if FMV_DEBUG == 2
        if (M68K_GETPC >= 0x200100 && (addr & fmv_mask) >= VRAM_BASE)
                return 1;
        return 0;
 #endif
        return 0;
 }
+#endif
 
-static uae_u8 io_bget (uaecptr addr)
+static void do_irq(void)
 {
-       addr &= 0xffff;
-       write_log (_T("FMV: IO byte read access %08x!\n"), addr);
-       return 0;
+       if (!(intreq & 8)) {
+               INTREQ_0(0x8000 | 0x0008);
+       }
 }
-static uae_u16 io_wget (uaecptr addr)
+
+static bool l64111_checkint(bool enabled)
 {
-       addr &= 0xffff;
-       if (addr != 0)
-               return 0;
-       return io_reg;
+       bool irq = false;
+       if (l64111intstatus[0] & l64111intmask[0])
+               irq = true;
+       if (((l64111intstatus[1] << 1) | (l64111intstatus[1] >> 7)) & l64111intmask[1])
+               irq = true;
+       if (irq && enabled)
+               do_irq();
+       return irq;
 }
-static void io_bput (uaecptr addr, uae_u8 v)
+
+static bool cl450_checkint(bool enabled)
 {
-       addr &= 0xffff;
-       write_log (_T("FMV: IO byte write access %08x!\n"), addr);
+       bool irq = false;
+       if (!(cl450_regs[CPU_control] & 1))
+               return false;
+       if (!(cl450_regs[HOST_control] & 0x80))
+               irq = true;
+       if (irq && enabled)
+               do_irq();
+       return irq;
 }
-static void io_wput (uaecptr addr, uae_u16 v)
+
+void rethink_cd32fmv(void)
 {
-       addr &= 0xffff;
-       if (addr != 0)
+       if (!ram)
                return;
-       write_log (_T("FMV: IO=%04x\n"), v);
-       io_reg = v;
+       cl450_checkint(true);
+       l64111_checkint(true);
 }
 
-static uae_u8 l64111_bget (uaecptr addr)
+
+#define L64111_FIFO_LOOKUP 96
+#define L64111_FIFO_BYTES 128
+static uae_u8 l64111_fifo[L64111_FIFO_BYTES];
+static int l64111_fifo_cnt;
+static int audio_frame_cnt, audio_frame_size;
+static int audio_frame_detect, audio_head_detect;
+static int l64111_cb_mask;
+#define L64111_CHANNEL_BUFFER_SIZE 2048
+
+static kjmp2_context_t mp2;
+#define PCM_SECTORS 4
+static cda_audio *cda;
+static int audio_data_remaining;
+static int audio_skip_size;
+
+struct zfile *fdump;
+
+struct fmv_pcmaudio
 {
-       write_log (_T("FMV: L64111 byte read access %08x!\n"), addr);
-       return 0;
+       bool ready;
+       signed short pcm[KJMP2_SAMPLES_PER_FRAME * 2];
+};
+static struct fmv_pcmaudio *pcmaudio;
+
+static void l64111_set_status(int num, uae_u16 mask)
+{
+       num--;
+       l64111intstatus[num] |= mask;
+       l64111_checkint(true);
+}
+
+static void l64111_setvolume(void)
+{
+       int volume = 32768;
+       if (l64111_regs[A_CONTROL2] & (1 << 5) || (io_reg & IO_L64111_MUTE))
+               volume = 0;
+       write_log(_T("L64111 mute %d\n"), volume ? 0 : 1);
+       if (cda)
+               cda->setvolume(currprefs.sound_volume_cd >= 0 ? currprefs.sound_volume_cd : currprefs.sound_volume, volume, volume);
 }
-static void l64111_bput (uaecptr addr, uae_u8 v)
+
+static int l64111_get_frame(uae_u8 *data, int remaining)
 {
-       write_log (_T("FMV: L64111 byte write access %08x!\n"), addr);
+       int size, offset;
+       uae_u8 *memdata;
+
+       if (!audio_frame_size || !audio_data_remaining || !remaining)
+               return 0;
+       size = audio_frame_size - audio_frame_cnt > remaining ? remaining : audio_frame_size - audio_frame_cnt;
+       if (audio_data_remaining >= 0 && size > audio_data_remaining)
+               size = audio_data_remaining;
+       offset = l64111_regs[A_CB_WRITE] & l64111_cb_mask;
+       memdata = audioram + offset * L64111_CHANNEL_BUFFER_SIZE;
+       memcpy(memdata + audio_frame_cnt, data, size);
+       audio_frame_cnt += size;
+       if (audio_data_remaining >= 0)
+               audio_data_remaining -= size;
+       if (audio_frame_cnt == audio_frame_size) {
+               int bytes;
+
+               if (pcmaudio[offset].ready) {
+                       write_log(_T("L64111 buffer overflow!\n"));
+               }
+#if 0
+               if (!fdump)
+                       fdump = zfile_fopen(_T("c:\\temp\\1.mp2"), _T("wb"));
+
+               zfile_fwrite(memdata, 1, audio_frame_size, fdump);
+#endif
+               bytes = kjmp2_decode_frame(&mp2, memdata, pcmaudio[offset].pcm);
+               if (bytes < 4 || bytes > KJMP2_MAX_FRAME_SIZE) {
+                       write_log(_T("mp2 decoding error\n"));
+                       memset(pcmaudio[offset].pcm, 0, KJMP2_SAMPLES_PER_FRAME * 4);
+               }
+               pcmaudio[offset].ready = true;
+
+               audio_frame_size = 0;
+               audio_frame_cnt = 0;
+               l64111_set_status(2, NEW_FRAME_S);
+               //write_log(_T("Audio frame %d (%d %d)\n"), offset, l64111_regs[A_CB_STATUS], bytes);
+               offset++;
+               l64111_regs[A_CB_WRITE] = offset & l64111_cb_mask;
+               l64111_regs[A_CB_STATUS]++;
+
+       }
+       return size;
+}
+
+static const int mpa_bitrates[] = {
+       -1, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1,
+       -1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1,
+       -1, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1,
+       -1, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1,
+       -1, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1
+};
+static const int mpa_frequencies[] = {
+       44100, 48000, 32000, 0,
+       22050, 24000, 16000, 0,
+       11025, 12000, 8000, 0
+};
+static const int mpa_samplesperframe[] = {
+       384, 384, 384,
+       1152, 1152, 1152,
+       1152, 576, 576
+};
+
+static bool parse_mp2_frame(uae_u8 *header)
+{
+       int ver, layer, bitrate, freq, padding, bitindex, iscrc;
+       int samplerate, bitrateidx, channelmode;
+       int isstereo;
+
+       audio_frame_cnt = 0;
+       audio_frame_size = 0;
+
+       ver = (header[1] >> 3) & 3;
+       if (ver != 3) // not MPEG-1
+               return false;
+       ver = 0;
+       layer = 4 - ((header[1] >> 1) & 3);
+       if (layer != 2) // not layer-2
+               return false;
+       iscrc = ((header[1] >> 0) & 1) ? 0 : 2;
+       bitrateidx = (header[2] >> 4) & 15;
+       if (bitrateidx == 0 || bitrateidx == 15)
+               return false; // invalid value
+       freq = mpa_frequencies[(header[2] >> 2) & 3];
+       if (!freq)
+               return false; // invalid value
+       channelmode = (header[3] >> 6) & 3;
+       isstereo = channelmode != 3;
+       if (ver == 0) {
+               bitindex = layer - 1;
+       } else {
+               if (layer == 1)
+                       bitindex = 3;
+               else
+                       bitindex = 4;
+       }
+       bitrate = mpa_bitrates[bitindex * 16 + bitrateidx] * 1000;
+       if (bitrate <= 0) // invalid value
+               return false;
+       padding = (header[2] >> 1) & 1;
+       samplerate = mpa_samplesperframe[(layer - 1) * 3 + ver];
+       audio_frame_size = ((samplerate / 8 * bitrate) / freq) + padding;
+       audio_frame_cnt = 0;
+       l64111_cb_mask = layer == 2 ? 63 : 127;
+
+       audio_frame_detect++;
+
+       l64111_regs[A_PARAM1] = ((header[1] << 4) | (header[2] >> 4)) & 0xff;
+       l64111_regs[A_PARAM2] = ((header[2] << 4) | (header[3] >> 4)) & 0xff;
+       l64111_regs[A_PARAM3] = ((header[3] << 4)) & 0xff;
+
+       l64111_set_status(2, FRAME_DETECT_IN | (audio_frame_detect == 3 ? SYNC_AUD : 0));
+
+       return true;
+}
+
+static void l64111_init(void)
+{
+       audio_head_detect = 0;
+       audio_frame_detect = 0;
+       audio_data_remaining = 0;
+       audio_skip_size = 0;
+       audio_frame_cnt = 0;
+       audio_frame_size = 0;
+       l64111_fifo_cnt = 0;
+}
+
+static void l64111_reset(void)
+{
+       memset(l64111_regs, 0, sizeof l64111_regs);
+       l64111intmask[0] = l64111intmask[1] = 0;
+       l64111intstatus[0] = l64111intstatus[1] = 0;
+       l64111_regs[A_CONTROL3] = 1 << 7; // AUDIO STREAM_ID_IGNORE=1
+       l64111_init();
+       l64111_setvolume();
+       if (pcmaudio)
+               memset(pcmaudio, 0, sizeof(struct fmv_pcmaudio) * L64111_CHANNEL_BUFFERS);
+       write_log(_T("L64111 reset\n"));
+}
+
+static uae_u8 *parse_audio_header(uae_u8 *p)
+{
+       bool ptsdts;
+       int cnt;
+       uae_u16 psize;
+       uae_u64 pts = 0;
+
+       p += 4;
+       psize = (p[0] << 8) | p[1];
+       //write_log(_T("audio stream header size %d\n"), psize);
+       p += 2;
+       if (audio_head_detect < 3) {
+               audio_head_detect++;
+               if (audio_head_detect == 3)
+                       l64111_set_status(2, SYNC_SYS);
+       }
+       cnt = 16;
+       while (p[0] == 0xff) {
+               cnt--;
+               if (cnt < 0)
+                       return p;
+               p++;
+               psize--;
+       }
+       if (p[0] == 0x0f) {
+               p++;
+               psize--;
+       } else {
+               if ((p[0] & 0xc0) == 0x40) {
+                       // STD
+                       p += 2;
+                       psize -= 2;
+               }
+               ptsdts = false;
+               if ((p[0] & 0xf0) == 0x20 || (p[0] & 0xf0) == 0x30) {
+                       if ((p[0] & 0xf0) == 0x30)
+                               ptsdts = true;
+                       // PTS
+                       pts = (((uae_u64)(p[0] >> 1) & 7) << 30);
+                       pts |= ((p[1] >> 0) << 22);
+                       pts |= ((p[2] >> 1) << 15);
+                       pts |= ((p[3] >> 0) << 7);
+                       pts |= ((p[4] >> 1) << 0);
+                       p += 5;
+                       psize -= 5;
+                       if (audio_head_detect >= 3) {
+                               l64111_regs[A_PRESENT1] = pts >> 0;
+                               l64111_regs[A_PRESENT2] = pts >> 8;
+                               l64111_regs[A_PRESENT3] = pts >> 16;
+                               l64111_regs[A_PRESENT4] = pts >> 24;
+                               l64111_regs[A_PRESENT5] = pts >> 32;
+                               //write_log(_T("audio PTS %09llx SCR %09llx\n"), pts, (uae_u64)cl450_scr);
+                               l64111_set_status(2, PTS_AVAILABLE);
+                       }
+               }
+               if (ptsdts && (p[0] & 0xf0) == 0x10) {
+                       // DTS
+                       p += 5;
+                       psize -= 5;
+               }
+       }
+       audio_data_remaining = psize;
+       return p;
+}
+
+static void l64111_parse(void)
+{
+       bool audio_only = (l64111_regs[A_CONTROL2] & 0x08) != 0;
+       uae_u8 *p = l64111_fifo;
+
+       if (!(l64111_regs[A_CONTROL1] & 1)) { // START bit set?
+               l64111_fifo_cnt = 0;
+               return;
+       }
+
+       if (audio_only) {
+               audio_data_remaining = -1;
+               audio_skip_size = 0;
+       }
+
+       if (audio_skip_size) {
+               int size = audio_skip_size > L64111_FIFO_BYTES ? L64111_FIFO_BYTES : audio_skip_size;
+               p += size;
+               audio_skip_size -= size;
+       }
+
+       if (audio_frame_size && audio_data_remaining)
+               p += l64111_get_frame(p, L64111_FIFO_BYTES - (p - l64111_fifo));
+
+       while (p - l64111_fifo < L64111_FIFO_LOOKUP || ((p - l64111_fifo) & 1)) {
+               uae_u8 *op = p;
+               int size = 0;
+
+               if (!audio_only) {
+                       // check system stream packets
+                       uae_u8 marker = p[3];
+                       if (p[0] == 0 && p[1] == 0 && p[2] == 1 && (marker & 0x80)) {
+                               int size = (p[4] << 8) | p[5];
+                               if (marker >= 0xc0 && marker <= 0xdf) {
+                                       // audio stream 0 to 31
+                                       bool ignore_stream_id = (l64111_regs[A_CONTROL3] & 0x80) != 0;
+                                       if (ignore_stream_id || (marker - 0xc0) == (l64111_regs[A_CONTROL3] & 31))
+                                               p = parse_audio_header(p);
+                                       else
+                                               p += 4;
+                               } else if (marker == 0xba) {
+                                       //write_log(_T("L64111: Pack header\n"));
+                                       p += 12;
+                               } else if (marker == 0xbb) {
+                                       write_log(_T("L64111: System header, size %d\n"), size);
+                                       p += 6;
+                                       audio_skip_size = size;
+                               } else if (marker == 0xbe) {
+                                       //write_log(_T("L64111: Padding packet size %d\n"), size);
+                                       p += 6;
+                                       audio_skip_size = size;
+                               } else {
+                                       write_log(_T("L64111: Packet %02X, size %d\n"), marker, size);
+                                       p += 6;
+                               }
+                       }
+               }
+
+               if (audio_skip_size) {
+                       int size = audio_skip_size > L64111_FIFO_BYTES - (p - l64111_fifo) ? L64111_FIFO_BYTES - (p - l64111_fifo) : audio_skip_size;
+                       p += size;
+                       audio_skip_size -= size;
+               }
+               if (L64111_FIFO_BYTES - (p - l64111_fifo) > 0 && audio_data_remaining) {
+                       if (audio_frame_size) {
+                               p += l64111_get_frame(p, L64111_FIFO_BYTES - (p - l64111_fifo));
+                       } else if (p[0] == 0xff && (p[1] & (0x80 | 0x40 | 0x20)) == (0x80 | 0x40 | 0x20)) {
+                               if (parse_mp2_frame(p))
+                                       p += l64111_get_frame(p, L64111_FIFO_BYTES - (p - l64111_fifo));
+                       }
+               }
+
+               if (p == op) {
+                       p++;
+                       if (audio_data_remaining > 0)
+                               audio_data_remaining--;
+               }
+       }
+       l64111_fifo_cnt = L64111_FIFO_BYTES - (p - l64111_fifo);
+       if (l64111_fifo_cnt < 0)
+               l64111_fifo_cnt = 0;
+       if (l64111_fifo_cnt > 0)
+               memmove(l64111_fifo, p, l64111_fifo_cnt);
 }
 
 static uae_u16 l64111_wget (uaecptr addr)
 {
+       uae_u16 v;
+
        addr >>= 1;
        addr &= 31;
-#if FMV_DEBUG > 0
-       write_log (_T("FMV: L64111 read reg %d -> %04x\n"), addr, l64111regs[addr]);
-#endif
-       if (addr == 4)
-               return l64111intstatus1;
-       if (addr == 5)
-               return l64111intstatus1;
 
-       return l64111regs[addr];
+       if (fmv_audio_debug) {
+               if (!(addr == A_INT1 || addr == A_INT2 || addr == A_CB_STATUS))
+                       write_log(_T("L64111 read reg %d -> %04x\n"), addr, l64111_regs[addr]);
+       }
+       v = l64111_regs[addr];
+       if (addr == A_INT1) {
+               v = l64111intstatus[0];
+               l64111intstatus[0] = 0;
+       } else if (addr == A_INT2) {
+               v = l64111intstatus[1] & 0x7f;
+               l64111intstatus[1] = 0;
+       }
+       return v;
 }
 static void l64111_wput (uaecptr addr, uae_u16 v)
 {
        addr >>= 1;
        addr &= 31;
 
-#if FMV_DEBUG > 0
-       write_log (_T("FMV: L64111 write reg %d = %04x\n"), addr, v);
-#endif
+       if (fmv_audio_debug) {
+               if (addr != A_DATA)
+                       write_log(_T("L64111 write reg %d = %04x\n"), addr, v);
+       }
 
-       if (addr == 4) {
-               l64111intmask1 = v;
+       switch (addr)
+       {
+       case A_CONTROL1:
+               if ((v & 1) != (l64111_regs[addr] & 1))
+                       l64111_init();
+               if ((v & 2))
+                       l64111_reset();
+               if (v & 4) {
+                       l64111_regs[A_CB_WRITE] = 0;
+                       l64111_regs[A_CB_READ] = 0;
+                       l64111_regs[A_CB_STATUS] = 0;
+                       memset(pcmaudio, 0, sizeof(struct fmv_pcmaudio) * L64111_CHANNEL_BUFFERS);
+                       write_log(_T("L64111 buffer reset\n"));
+               }
+               break;
+       case A_CONTROL2:
+               l64111_setvolume();
+               break;
+       case A_DATA:
+               if (l64111_fifo_cnt < 0 || l64111_fifo_cnt > L64111_FIFO_BYTES) {
+                       write_log(_T("L641111 fifo overflow!\n"));
+                       l64111_fifo_cnt = 0;
+                       return;
+               }
+               l64111_fifo[l64111_fifo_cnt++] = v >> 8;
+               l64111_fifo[l64111_fifo_cnt++] = v;
+               if (l64111_fifo_cnt == L64111_FIFO_BYTES) {
+                       l64111_parse();
+               }
+               break;
+       case A_INT1:
+               l64111intmask[0] = v;
                return;
-       }
-       if (addr == 5) {
-               l64111intmask2 = v;
+       case A_INT2:
+               l64111intmask[1] = v;
                return;
        }
 
-       l64111regs[addr] = v;
+       l64111_regs[addr] = v;
 
 }
 
-static uae_u8 cl450_bget (uaecptr addr)
+static uae_u8 l64111_bget(uaecptr addr)
 {
-       addr &= 0xff;
-       write_log (_T("FMV: CL450 byte read access %08x!\n"), addr);
-       return 0;
+       return l64111_wget(addr);
 }
-static uae_u16 cl450_wget (uaecptr addr)
+static void l64111_bput(uaecptr addr, uae_u8 v)
 {
-       addr &= 0xff;
-       addr >>= 1;
-       write_log (_T("FMV: CL450 read reg %d\n"), addr);
-       return 0;
+       l64111_wput(addr, v);
 }
-static void cl450_bput (uaecptr addr, uae_u8 v)
+
+static void cl450_set_status(uae_u16 mask)
 {
-       addr &= 0xff;
-       write_log (_T("FMV: CL450 byte write access %08x!\n"), addr);
+       cl450_pending_interrupts |= mask & cl450_interruptmask;
+       if (cl450_hmem[CL_HMEM_INT_STATUS] == 0) {
+               cl450_hmem[CL_HMEM_INT_STATUS] = cl450_pending_interrupts;
+               cl450_pending_interrupts = 0;
+               cl450_regs[HOST_control] &= ~0x80;
+               cl450_checkint(true);
+       }
 }
-static void cl450_wput (uaecptr addr, uae_u16 v)
+
+static void cl450_write_dram(int addr, uae_u16 w)
 {
-       addr &= 0xff;
-       write_log (_T("FMV: CL450 write reg %d = %04x\n"), addr, v);
+       if (!ram)
+               return;
+       ram[addr * 2 + 0] = w >> 8;
+       ram[addr * 2 + 1] = w;
 }
 
-static uae_u8 romram_bget (uaecptr addr)
+#if DUMP_VIDEO
+static struct zfile *videodump;
+#endif
+
+static void cl450_parse_frame(void)
 {
-#ifdef FMV_DEBUG
-       if (isdebug (addr))
-               write_log (_T("romram_bget %08X PC=%08X\n"), addr, M68K_GETPC);
+       for (;;) {
+               mpeg2_state_t mpeg_state = mpeg2_parse(mpeg_decoder);
+               switch (mpeg_state)
+               {
+                       case STATE_BUFFER:
+                       {
+                               int bufsize = cl450_buffer_offset;
+                               if (bufsize == 0)
+                                       return;
+                               while (bufsize > 0 && cl450_newpacket_mode) {
+                                       struct cl450_newpacket *np = &cl450_newpacket_buffer[cl450_newpacket_offset_read];
+                                       if (cl450_newpacket_offset_read == cl450_newpacket_offset_write)
+                                               return;
+                                       int size = np->length > bufsize ? bufsize : np->length;
+
+                                       if (np->length == 0) {
+                                               write_log(_T("CL450 no matching newpacket!?\n"));
+                                               return;
+                                       }
+
+                                       np->length -= size;
+                                       bufsize -= size;
+                                       if (np->length > 0)
+                                               break;
+                                       //write_log(_T("CL450: NewPacket %d done\n"), cl450_newpacket_offset_read);
+                                       cl450_newpacket_offset_read++;
+                                       cl450_newpacket_offset_read &= CL450_NEWPACKET_BUFFER_SIZE - 1;
+                               }
+#if DUMP_VIDEO
+                               if (!videodump)
+                                       videodump = zfile_fopen(_T("c:\\temp\\1.mpg"), _T("wb"));
+                               zfile_fwrite(&ram[CL450_MPEG_BUFFER], 1, cl450_buffer_offset, videodump);
 #endif
-       if (addr >= IO_BASE && addr < VRAM_BASE)
-               return 0;
-       return rom[addr];
+                               memcpy(&ram[CL450_MPEG_DECODE_BUFFER] + libmpeg_offset, &ram[CL450_MPEG_BUFFER], cl450_buffer_offset);
+                               mpeg2_buffer(mpeg_decoder, &ram[CL450_MPEG_DECODE_BUFFER] + libmpeg_offset, &ram[CL450_MPEG_DECODE_BUFFER] + libmpeg_offset + cl450_buffer_offset);
+                               libmpeg_offset += cl450_buffer_offset;
+                               if (libmpeg_offset >= CL450_MPEG_DECODE_BUFFER_SIZE - CL450_MPEG_BUFFER_SIZE)
+                                       libmpeg_offset = 0;
+                               cl450_buffer_offset = 0;
+                       }
+                       break;
+                       case STATE_SEQUENCE:
+                               mpeg2_convert(mpeg_decoder, mpeg2convert_rgb32, NULL);
+                               cl450_set_status(CL_INT_SEQ_V);
+                               cl450_frame_rate = mpeg_info->sequence->frame_period ? 27000000 / mpeg_info->sequence->frame_period : 0;
+                               cl450_write_dram(CL_DRAM_PICTURE_RATE, cl450_frame_rate);
+                               cl450_write_dram(CL_DRAM_H_SIZE, mpeg_info->sequence->width);
+                               cl450_write_dram(CL_DRAM_V_SIZE, mpeg_info->sequence->height);
+                               break;
+                       case STATE_PICTURE:
+                               break;
+                       case STATE_GOP:
+                               cl450_write_dram(CL_DRAM_TIME_CODE_0, (mpeg_info->gop->hours << 6) | (mpeg_info->gop->minutes));
+                               cl450_write_dram(CL_DRAM_TIME_CODE_1, (mpeg_info->gop->seconds << 6) | (mpeg_info->gop->pictures));
+                               break;
+                       case STATE_SLICE:
+                       case STATE_END:
+                               if (mpeg_info->display_fbuf) {
+                                       memcpy(videoram[cl450_videoram_write].data, mpeg_info->display_fbuf->buf[0], CL450_VIDEO_BUFFER_SIZE);
+                                       videoram[cl450_videoram_write].width = mpeg_info->sequence->width;
+                                       videoram[cl450_videoram_write].height = mpeg_info->sequence->height;
+                                       cl450_videoram_write++;
+                                       cl450_videoram_write &= CL450_VIDEO_BUFFERS - 1;
+                                       cl450_videoram_cnt++;
+                                       //write_log(_T("%d\n"), cl450_videoram_cnt);
+                               }
+                               return;
+                       default:
+                               break;
+               }
+       }
 }
-static uae_u16 romram_wget (uaecptr addr)
+
+static void cl450_reset(void)
 {
-#ifdef FMV_DEBUG
-       if (isdebug (addr))
-               write_log (_T("romram_wget %08X PC=%08X\n"), addr, M68K_GETPC);
-#endif
-       if (addr >= IO_BASE && addr < VRAM_BASE)
-               return 0;
-       return (rom[addr] << 8) | (rom[addr + 1] << 0);
+       write_log(_T("CL450 reset\n"));
+       cl450_play = 0;
+       cl450_pending_interrupts = 0;
+       cl450_interruptmask = 0;
+       cl450_blank = 0;
+       cl450_border_color = 0;
+       cl450_threshold = 4096;
+       cl450_buffer_offset = 0;
+       cl450_buffer_empty_cnt = 0;
+       libmpeg_offset = 0;
+       cl450_newpacket_mode = false;
+       cl450_newpacket_offset_write = 0;
+       cl450_newpacket_offset_read = 0;
+       cl450_videoram_write = 0;
+       cl450_videoram_read = 0;
+       cl450_videoram_cnt = 0;
+       memset(cl450_regs, 0, sizeof cl450_regs);
+       if (mpeg_decoder)
+               mpeg2_reset(mpeg_decoder, 1);
+       if (ram)
+               memset(ram, 0, 0x100);
+       cl450_write_dram(CL_DRAM_VER, 0x0200);
+       cl450_write_dram(CL_DRAM_PID, 0x0002);
 }
-static void ram_bput (uaecptr addr, uae_u8 v)
+
+static void cl450_init(void)
 {
-       if (addr < VRAM_BASE)
-               return;
-       rom[addr] = v;
-       if (isdebug (addr)) {
-               write_log (_T("ram_bput %08X=%02X PC=%08X\n"), addr, v & 0xff, M68K_GETPC);
+       write_log(_T("CL450 CPU enabled\n"));
+       cl450_hmem[15] = 0;
+       cl450_regs[HOST_newcmd] = 0;
+       cl450_regs[CMEM_control] = 2;
+       cl450_regs[HOST_control] = 0x0080 | 0x0001;
+       cl450_regs[VID_sela] = 0x8000 | 0x2000 | 0x0800 | 0x00c0 | 0x0006;
+       cl450_regs[VID_selb] = 0x4000 | 0x0900 | 0x0060 | 0x0007;
+       cl450_regs[HOST_scr2] = 0x1000 | 0x0d00 | 0x00e0;
+       cl450_regs[HOST_scr1] = 0;
+       cl450_regs[HOST_scr0] = 0;
+       cl450_scr = 0;
+       cl450_write_dram(CL_DRAM_VER, 0x0200);
+       cl450_write_dram(CL_DRAM_PID, 0x0002);
+       memset(ram + 0x10, 0, 0x100 - 0x10);
+}
+
+static void cl450_newpacket(void)
+{
+       struct cl450_newpacket *np = &cl450_newpacket_buffer[cl450_newpacket_offset_write];
+
+       cl450_newpacket_mode = true;
+
+       np->length = cl450_hmem[1];
+       np->pts = 0;
+       np->pts_valid = false;
+       if (cl450_hmem[2] & 0x8000) {
+               uae_u64 v;
+               v = ((uae_u64)cl450_regs[HOST_scr0] & 7) << 30;
+               v |= (cl450_regs[HOST_scr1] & 0x7fff) << 15;
+               v |= cl450_regs[HOST_scr2] & 0x7fff;
+               np->pts = v;
+               np->pts_valid = true;
        }
+
+       if (fmv_video_debug & 1)
+               write_log(_T("CL450 NewPacket %d: size=%d pts=%09llx v=%d\n"), cl450_newpacket_offset_write, np->length, np->pts, np->pts_valid);
+
+       cl450_newpacket_offset_write++;
+       cl450_newpacket_offset_write &= CL450_NEWPACKET_BUFFER_SIZE - 1;
 }
-static void ram_wput (uaecptr addr, uae_u16 v)
+
+static void cl450_from_scr(void)
+{
+       uae_u64 v = (uae_u64)cl450_scr;
+       cl450_regs[HOST_scr0] &= ~7;
+       cl450_regs[HOST_scr0] |= (v >> 30) & 7;
+       cl450_regs[HOST_scr1] = (v >> 15) & 0x7fff;
+       cl450_regs[HOST_scr2] = v & 0x7fff;
+}
+
+static void cl450_to_scr(void)
 {
-       if (addr < VRAM_BASE)
+       uae_u64 v;
+       v = ((uae_u64)cl450_regs[HOST_scr0] & 7) << 30;
+       v |= (cl450_regs[HOST_scr1] & 0x7fff) << 15;
+       v |= cl450_regs[HOST_scr2] & 0x7fff;
+       cl450_scr = v;
+}
+
+static void cl450_reset_cmd(void)
+{
+       cl450_blank = 1;
+       cl450_play = 0;
+       cl450_newpacket_offset_read = 0;
+       cl450_newpacket_offset_write = 0;
+       cl450_newpacket_mode = false;
+       cl450_interruptmask = 0;
+       cl450_buffer_offset = 0;
+       cl450_buffer_empty_cnt = 0;
+}
+
+static void cl450_newcmd(void)
+{
+//     write_log(_T("* CL450 Command %04x\n"), cl450_hmem[0]);
+//     for (int i = 1; i <= 4; i++)
+//             write_log(_T("%02d: %04x\n"), i, cl450_hmem[i]);
+       switch (cl450_hmem[0])
+       {
+               case CL_Play:
+                       cl450_play = 1;
+                       write_log(_T("CL450 PLAY\n"));
+                       break;
+               case CL_Pause:
+                       if (cl450_play > 0) {
+                               // pause clears SCR
+                               cl450_scr = 0;
+                               cl450_to_scr();
+                       }
+                       cl450_play = -cl450_play;
+                       write_log(_T("CL450 PAUSE\n"));
+                       break;
+               case CL_NewPacket:
+                       cl450_newpacket();
+                       break;
+               case CL_InquireBufferFullness:
+                       cl450_hmem[0x0b] = cl450_buffer_offset;
+                       if (fmv_video_debug & 1)
+                               write_log(_T("CL450 InquireBufferFullness (%d)\n"), cl450_buffer_offset);
+                       break;
+               case CL_SetBlank:
+                       cl450_blank = cl450_hmem[1] & 1;
+                       write_log(_T("CL450 blank = %d\n"), cl450_blank);
+                       break;
+               case CL_SetBorder:
+                       cl450_border_color = ((cl450_hmem[3] & 0xff) << 16) | cl450_hmem[4];
+                       write_log(_T("CL450 SetBorder %08x\n"), cl450_border_color);
+                       cd32_fmv_new_border_color(cl450_border_color);
+                       break;
+               case CL_SetColorMode:
+                       write_log(_T("CL450 SetColorMode\n"));
+                       break;
+               case CL_SetInterruptMask:
+                       cl450_interruptmask = cl450_hmem[1];
+                       write_log(_T("CL450 SetInterruptMask %04x\n"), cl450_interruptmask);
+                       break;
+               case CL_SetThresHold:
+                       cl450_threshold = cl450_hmem[1];
+                       write_log(_T("CL450 SetThresHold %d\n"), cl450_threshold);
+                       break;
+               case CL_SetVideoFormat:
+                       write_log(_T("CL450 SetVideoFormat\n"));
+                       break;
+               case CL_SetWindow:
+                       write_log(_T("CL450 SetWindow\n"));
+                       break;
+               case CL_AccessSCR:
+                       if (cl450_hmem[1] & 0x8000) {
+                               cl450_from_scr();
+                               cl450_hmem[1] = 0x8000 | (cl450_regs[HOST_scr0] & 7);
+                               cl450_hmem[2] = cl450_regs[HOST_scr1] & 0x7fff;
+                               cl450_hmem[3] = cl450_regs[HOST_scr2] & 0x7fff;
+                       } else {
+                               cl450_regs[HOST_scr0] = cl450_hmem[1] & 7;
+                               cl450_regs[HOST_scr1] = cl450_hmem[2] & 0x7fff;
+                               cl450_regs[HOST_scr2] = cl450_hmem[3] & 0x7fff;
+                               cl450_to_scr();
+                       }
+                       if (fmv_video_debug & 1)
+                               write_log(_T("CL450 AccessSCR %c %09llx (%04x %04x %04x)\n"),
+                                       (cl450_hmem[1] & 0x8000) ? 'R' : 'W', (uae_u64)cl450_scr,
+                                       cl450_regs[HOST_scr0], cl450_regs[HOST_scr1], cl450_regs[HOST_scr2]);
+                       break;
+               case CL_Reset:
+                       write_log(_T("CL450 Reset\n"));
+                       cl450_reset_cmd();
+                       break;
+               case CL_FlushBitStream:
+                       write_log(_T("CL450 CL_FlushBitStream\n"));
+                       cl450_buffer_offset = 0;
+                       memset(cl450_newpacket_buffer, 0, sizeof cl450_newpacket_buffer);
+                       cl450_newpacket_offset_read = cl450_newpacket_offset_write = 0;
+                       break;
+               default:
+                       write_log(_T("CL450 unsupported command %04x\n"), cl450_hmem[0]);
+                       break;
+       }
+
+       cl450_regs[HOST_newcmd] = 0;
+}
+
+static uae_u16 cl450_wget (uaecptr addr)
+{
+       uae_u16 v = 0;
+       addr &= 0xfe;
+
+       switch (addr)
+       {
+       case CMEM_dmactrl:
+               write_log(_T("CL450 CMEM_dmactrl\n"));
+               break;
+       case HOST_intvecr:
+               v = cl450_regs[HOST_intvecr];
+               break;
+       case HOST_control:
+               v = cl450_regs[HOST_control];
+               break;
+       case HOST_raddr:
+               v = cl450_regs[HOST_raddr];
+               break;
+       case HOST_rdata:
+               v = cl450_hmem[cl450_regs[HOST_raddr]];
+               break;
+       case HOST_newcmd:
+               v = cl450_regs[HOST_newcmd];
+               break;
+       case CPU_iaddr:
+               v = cl450_regs[CPU_iaddr];
+               break;
+       case CPU_taddr:
+               v = cl450_regs[CPU_taddr];
+               break;
+       case CPU_pc:
+               v = cl450_regs[CPU_pc];
+               break;
+       case CPU_control:
+               v = cl450_regs[CPU_control];
+               break;
+       case VID_control:
+               v = cl450_regs[VID_control];
+               break;
+       case VID_regdata:
+               v = cl450_vid[cl450_regs[VID_control] >> 1];
+               break;
+       default:
+               write_log(_T("CL450 unknown register %02x read\n"), addr);
+               return v;
+       }
+
+       if (fmv_video_debug & 2)
+               write_log (_T("CL450 read reg %02x %04x\n"), addr, v);
+       return v;
+}
+
+static void cl450_data_wput(uae_u16 v)
+{
+       ram[CL450_MPEG_BUFFER + cl450_buffer_offset + 0] = v >> 8;
+       ram[CL450_MPEG_BUFFER + cl450_buffer_offset + 1] = v;
+       if (cl450_buffer_offset < CL450_MPEG_BUFFER_SIZE - 2)
+               cl450_buffer_offset += 2;
+}
+
+static void cl450_wput(uaecptr addr, uae_u16 v)
+{
+       addr &= 0xfe;
+
+       if (fmv_video_debug & 2)
+               write_log(_T("CL450 write reg %02x %04x\n"), addr, v);
+
+       switch (addr)
+       {
+       case CMEM_data:
+               cl450_data_wput(v);
+               break;
+       case CMEM_control:
+               cl450_regs[CMEM_control] = v;
+               if (v & 0x40)
+                       cl450_reset();
+               write_log(_T("CL450 CMEM_control %04x\n"), v);
+               break;
+       case CMEM_dmactrl:
+               cl450_regs[CMEM_dmactrl] = v;
+               write_log(_T("CL450 CMEM_dmactrl %04x\n"), v);
+               break;
+       case HOST_intvecw:
+               cl450_regs[HOST_intvecr] = v;
+               write_log(_T("CL450 HOST_intvecw %04x\n"), v);
+               break;
+       case HOST_control:
+               cl450_regs[HOST_control] = v;
+               //write_log(_T("CL450 HOST_control %04x\n"), v);
+               break;
+       case HOST_raddr:
+               cl450_regs[HOST_raddr] = v & 15;
+               //write_log(_T("CL450 HOST_raddr %04x\n"), v);
+               break;
+       case HOST_rdata:
+               cl450_hmem[cl450_regs[HOST_raddr]] = v;
+               //write_log(_T("CL450 HOST_rdata %d %04x\n"), cl450_regs[HOST_raddr], v);
+               cl450_regs[HOST_raddr]++;
+               cl450_regs[HOST_raddr] &= CL450_HMEM_WORDS - 1;
+               break;
+       case HOST_newcmd:
+               cl450_regs[HOST_newcmd] = v;
+               cl450_newcmd();
+               break;
+       case CPU_pc:
+               cl450_regs[CPU_pc] = v;
+               write_log(_T("CL450 CPU_pc %04x\n"), v);
+               break;
+       case CPU_control:
+               write_log(_T("CL450 CPU_control %04x\n"), v);
+               if (!(cl450_regs[CPU_control] & 1) && (v & 1)) {
+                       cl450_init();
+               }
+               cl450_regs[CPU_control] = v & 1;
+               break;
+       case DRAM_refcnt:
+               cl450_regs[DRAM_refcnt] = v;
+               write_log(_T("CL450 DRAM_refcnt %04x\n"), v);
+               break;
+       case CPU_imem:
+               cl450_regs[CPU_iaddr] &= CL450_IMEM_WORDS - 1;
+               cl450_imem[CPU_iaddr] = v;
+               cl450_regs[CPU_iaddr]++;
+               cl450_regs[CPU_iaddr] &= CL450_IMEM_WORDS - 1;
+               break;
+       case CPU_iaddr:
+               cl450_regs[CPU_iaddr] = v & (CL450_IMEM_WORDS - 1);
+               write_log(_T("CL450 CPU_iaddr %04x\n"), v);
+               break;
+       case CPU_tmem:
+               cl450_regs[CPU_taddr] &= CL450_TMEM_WORDS - 1;
+               cl450_tmem[CPU_taddr] = v;
+               cl450_regs[CPU_taddr]++;
+               cl450_regs[CPU_taddr] &= CL450_TMEM_WORDS - 1;
+               break;
+       case CPU_taddr:
+               cl450_regs[CPU_taddr] = v & (CL450_TMEM_WORDS - 1);
+               write_log(_T("CL450 CPU_taddr %04x\n"), v);
+               break;
+       case VID_control:
+               cl450_regs[VID_control] = v & ((CL450_VID_REGS - 1) << 1);
+               break;
+       case VID_regdata:
+               cl450_vid[cl450_regs[VID_control] >> 1] = v;
+               write_log(_T("CL450 vid reg %02x = %04x\n"), cl450_regs[VID_control] >> 1, v);
+               break;
+       case HOST_scr0:
+               cl450_regs[HOST_scr0] = v;
+               break;
+       case HOST_scr1:
+               cl450_regs[HOST_scr1] = v;
+               break;
+       case HOST_scr2:
+               cl450_regs[HOST_scr2] = v;
+               break;
+       default:
+               write_log(_T("CL450 write unknown register %02x = %04x\n"), addr, v);
                return;
-       rom[addr + 0] = v >> 8;
-       rom[addr + 1] = v >> 0;
-       if (isdebug (addr)) {
-               write_log (_T("ram_wput %08X=%04X PC=%08X\n"), addr, v & 0xffff, M68K_GETPC);
        }
 }
 
+static uae_u8 cl450_bget(uaecptr addr)
+{
+       return cl450_wget(addr);
+}
+static void cl450_bput(uaecptr addr, uae_u8 v)
+{
+       cl450_wput(addr, v);
+}
+
+static uae_u8 io_bget(uaecptr addr)
+{
+       addr &= 0xffff;
+       write_log(_T("FMV: IO byte read access %08x!\n"), addr);
+       return 0;
+}
+static uae_u16 io_wget(uaecptr addr)
+{
+       uae_u16 v = 0;
+       addr &= 0xffff;
+       if (addr != 0)
+               return 0;
+       v |= IO_CL450_IRQ | IO_L64111_IRQ | IO_CL450_FIFO_STATUS;
+       if (cl450_checkint(false))
+               v &= ~IO_CL450_IRQ;
+       if (l64111_checkint(false))
+               v &= ~IO_L64111_IRQ;
+       return v;
+}
+static void io_bput(uaecptr addr, uae_u8 v)
+{
+       addr &= 0xffff;
+       write_log(_T("FMV: IO byte write access %08x!\n"), addr);
+}
+static void io_wput(uaecptr addr, uae_u16 v)
+{
+       addr &= 0xffff;
+       if (addr != 0)
+               return;
+       write_log(_T("FMV: IO=%04x\n"), v);
+       io_reg = v;
+       l64111_setvolume();
+       cd32_fmv_state((io_reg & IO_CL450_VIDEO) ? 1 : 0);
+}
+
 static uae_u32 REGPARAM2 fmv_wget (uaecptr addr)
 {
        uae_u32 v;
-       addr -= fmv_start & fmv_mask;
-       addr &= fmv_mask;
+       addr -= fmv_start & fmv_bank.mask;
+       addr &= fmv_bank.mask;
        int mask = addr & BANK_MASK;
        if (mask == L64111_BASE)
                v = l64111_wget (addr);
@@ -236,10 +1242,8 @@ static uae_u32 REGPARAM2 fmv_wget (uaecptr addr)
                v = cl450_wget (addr);
        else if (mask == IO_BASE)
                v = io_wget (addr);
-       else
-               v = romram_wget (addr);
 
-#ifdef FMV_DEBUG
+#if FMV_DEBUG
        if (isdebug (addr))
                write_log (_T("fmv_wget %08X=%04X PC=%08X\n"), addr, v, M68K_GETPC);
 #endif
@@ -250,7 +1254,7 @@ static uae_u32 REGPARAM2 fmv_lget (uaecptr addr)
 {
        uae_u32 v;
        v = (fmv_wget (addr) << 16) | (fmv_wget (addr + 2) << 0);
-#ifdef FMV_DEBUG
+#if FMV_DEBUG
        if (isdebug (addr))
                write_log (_T("fmv_lget %08X=%08X PC=%08X\n"), addr, v, M68K_GETPC);
 #endif
@@ -260,8 +1264,8 @@ static uae_u32 REGPARAM2 fmv_lget (uaecptr addr)
 static uae_u32 REGPARAM2 fmv_bget (uaecptr addr)
 {
        uae_u32 v;
-       addr -= fmv_start & fmv_mask;
-       addr &= fmv_mask;
+       addr -= fmv_start & fmv_bank.mask;
+       addr &= fmv_bank.mask;
        int mask = addr & BANK_MASK;
        if (mask == L64111_BASE)
                v = l64111_bget (addr);
@@ -269,16 +1273,14 @@ static uae_u32 REGPARAM2 fmv_bget (uaecptr addr)
                v = cl450_bget (addr);
        else if (mask == IO_BASE)
                v = io_bget (addr);
-       else
-               v = romram_bget (addr);
        return v;
 }
 
 static void REGPARAM2 fmv_wput (uaecptr addr, uae_u32 w)
 {
-       addr -= fmv_start & fmv_mask;
-       addr &= fmv_mask;
-#ifdef FMV_DEBUG
+       addr -= fmv_start & fmv_bank.mask;
+       addr &= fmv_bank.mask;
+#if FMV_DEBUG
        if (isdebug (addr))
                write_log (_T("fmv_wput %04X=%04X PC=%08X\n"), addr, w & 65535, M68K_GETPC);
 #endif
@@ -288,14 +1290,14 @@ static void REGPARAM2 fmv_wput (uaecptr addr, uae_u32 w)
        else if (mask == CL450_BASE)
                cl450_wput (addr, w);
        else if (mask == IO_BASE)
-               io_wput (addr, w);
-       else
-               ram_wput (addr, w);
+               io_wput(addr, w);
+       else if (mask == CL450_DATA)
+               cl450_data_wput(w);
 }
 
 static void REGPARAM2 fmv_lput (uaecptr addr, uae_u32 w)
 {
-#ifdef FMV_DEBUG
+#if FMV_DEBUG
        if (isdebug (addr))
                write_log (_T("fmv_lput %08X=%08X PC=%08X\n"), addr, w, M68K_GETPC);
 #endif
@@ -305,8 +1307,8 @@ static void REGPARAM2 fmv_lput (uaecptr addr, uae_u32 w)
 
 static void REGPARAM2 fmv_bput (uaecptr addr, uae_u32 w)
 {
-       addr -= fmv_start & fmv_mask;
-       addr &= fmv_mask;
+       addr -= fmv_start & fmv_bank.mask;
+       addr &= fmv_bank.mask;
        int mask = addr & BANK_MASK;
        if (mask == L64111_BASE)
                l64111_bput (addr, w);
@@ -314,91 +1316,234 @@ static void REGPARAM2 fmv_bput (uaecptr addr, uae_u32 w)
                cl450_bput (addr, w);
        else if (mask == IO_BASE)
                io_bput (addr, w);
-       else
-               ram_bput (addr, w);
 }
 
-static uae_u32 REGPARAM2 fmv_wgeti (uaecptr addr)
-{
-       uae_u32 v = 0;
-       uae_u8 *m;
-#ifdef JIT
-       special_mem |= S_READ;
-#endif
-       addr -= fmv_start & fmv_mask;
-       addr &= fmv_mask;
-       m = rom + addr;
-       if (addr < fmv_rom_size)
-               return do_get_mem_word ((uae_u16 *)m);
-#ifdef FMV_DEBUG
-       write_log (_T("fmv_wgeti %08X %08X PC=%08X\n"), addr, v, M68K_GETPC);
-#endif
-       return v;
-}
+static double max_sync_vpos;
+static double remaining_sync_vpos;
 
-static uae_u32 REGPARAM2 fmv_lgeti (uaecptr addr)
+void cd32_fmv_set_sync(double svpos)
 {
-       uae_u32 v = 0;
-       uae_u8 *m;
-#ifdef JIT
-       special_mem |= S_READ;
-#endif
-       addr -= fmv_start & fmv_mask;
-       addr &= fmv_mask;
-       m = rom + addr;
-       if (addr < fmv_rom_size)
-               return do_get_mem_long ((uae_u32 *)m);
-#ifdef FMV_DEBUG
-       write_log (_T("fmv_lgeti %08X %08X PC=%08X\n"), addr, v, M68K_GETPC);
-#endif
-       return v;
+       max_sync_vpos = svpos;
 }
 
-static int REGPARAM2 fmv_check (uaecptr addr, uae_u32 size)
+void cd32_fmv_hsync_handler(void)
 {
-       addr -= fmv_start & fmv_mask;
-       addr &= fmv_mask;
-       return (addr + size) <= fmv_size;
+       if (!ram)
+               return;
+
+       if (cl450_play > 0)
+               cl450_scr += 90000.0 / hblank_hz;
+
+       if (cl450_video_hsync_wait > 0)
+               cl450_video_hsync_wait--;
+       if (cl450_video_hsync_wait == 0) {
+               cl450_set_status(CL_INT_PIC_D);
+               if (cl450_videoram_cnt > 0) {
+                       cd32_fmv_new_image(videoram[cl450_videoram_read].width, videoram[cl450_videoram_read].height, cl450_blank ? NULL : videoram[cl450_videoram_read].data);
+                       cl450_videoram_read++;
+                       cl450_videoram_read &= CL450_VIDEO_BUFFERS - 1;
+                       cl450_videoram_cnt--;
+               }
+               cl450_video_hsync_wait = max_sync_vpos;
+               while (remaining_sync_vpos >= 1.0) {
+                       cl450_video_hsync_wait++;
+                       remaining_sync_vpos -= 1.0;
+               }
+               remaining_sync_vpos += max_sync_vpos - cl450_video_hsync_wait;
+               if (cl450_frame_rate < 40)
+                       cl450_video_hsync_wait *= 2;
+       }
+
+       if (vpos & 7)
+               return;
+
+       if (cl450_play > 0) {
+               if (cl450_newpacket_mode && cl450_buffer_offset < cl450_threshold) {
+                       int newpacket_len = 0;
+                       for (int i = 0; i < CL450_NEWPACKET_BUFFER_SIZE; i++)
+                               newpacket_len += cl450_newpacket_buffer[i].length;
+                       if (cl450_buffer_offset >= newpacket_len - 6)
+                               cl450_set_status(CL_INT_RDY);
+               }
+
+               if (cl450_buffer_offset >= 512 && cl450_videoram_cnt < CL450_VIDEO_BUFFERS - 1) {
+                       cl450_parse_frame();
+               }
+       }
 }
 
-static uae_u8 *REGPARAM2 fmv_xlate (uaecptr addr)
+void cd32_fmv_vsync_handler(void)
 {
-       addr -= fmv_start & fmv_mask;
-       addr &= fmv_mask;
-       return rom + addr;
+       int bufnum;
+       int offset, needsectors;
+       bool play0, play1;
+
+       if (!ram)
+               return;
+
+       if (cl450_buffer_offset == 0) {
+               if (cl450_buffer_empty_cnt >= 2)
+                       cl450_set_status(CL_INT_UND);
+               else
+                       cl450_buffer_empty_cnt++;
+       } else {
+               cl450_buffer_empty_cnt = 0;
+       }
+
+
+       if (!cda || !(l64111_regs[A_CONTROL1] & 1))
+               return;
+       play0 = cda->isplaying(0);
+       play1 = cda->isplaying(1);
+       needsectors = PCM_SECTORS;
+       if (!play0 && !play1) {
+               needsectors *= 2;
+               write_log(_T("L64111 buffer underflow\n"));
+       }
+       offset = l64111_regs[A_CB_READ] & l64111_cb_mask;
+       for (int i = 0; i < needsectors; i++) {
+               int offset2 = (offset + i) & l64111_cb_mask;
+               if (!pcmaudio[offset2].ready)
+                       return;
+       }
+
+       bufnum = 0;
+       if (play0) {
+               if (play1)
+                       return;
+               bufnum = 1;
+       }
+       for (int i = 0; i < PCM_SECTORS; i++) {
+               int offset2 = (offset + i) & l64111_cb_mask;
+               memcpy(cda->buffers[bufnum] + i * KJMP2_SAMPLES_PER_FRAME * 4, pcmaudio[offset2].pcm, KJMP2_SAMPLES_PER_FRAME * 4);
+               pcmaudio[offset2].ready = false;
+       }
+       cda->play(bufnum);
+       offset += PCM_SECTORS;
+       offset &= l64111_cb_mask;
+       l64111_regs[A_CB_READ] = offset;
+       l64111_regs[A_CB_STATUS] -= PCM_SECTORS;
 }
 
 static addrbank fmv_bank = {
        fmv_lget, fmv_wget, fmv_bget,
        fmv_lput, fmv_wput, fmv_bput,
-       fmv_xlate, fmv_check, NULL, _T("CD32 FMV module"),
-       fmv_lgeti, fmv_wgeti, ABFLAG_ROM | ABFLAG_IO
+       default_xlate, default_check, NULL, _T("CD32 FMV IO"),
+       fmv_lget, fmv_wget, ABFLAG_IO
 };
 
+MEMORY_FUNCTIONS_NOJIT(fmv_rom);
+
+static addrbank fmv_rom_bank = {
+       fmv_rom_lget, fmv_rom_wget, fmv_rom_bget,
+       fmv_rom_lput, fmv_rom_wput, fmv_rom_bput,
+       fmv_rom_xlate, fmv_rom_check, NULL, _T("CD32 FMV ROM"),
+       fmv_rom_lget, fmv_rom_wget, ABFLAG_ROM
+};
 
 
+MEMORY_FUNCTIONS_NOJIT(fmv_ram);
+
+static addrbank fmv_ram_bank = {
+       fmv_ram_lget, fmv_ram_wget, fmv_ram_bget,
+       fmv_ram_lput, fmv_ram_wput, fmv_ram_bput,
+       fmv_ram_xlate, fmv_ram_check, NULL, _T("CD32 FMV RAM"),
+       fmv_ram_lget, fmv_ram_wget, ABFLAG_RAM
+};
+
+void cd32_fmv_reset(void)
+{
+       if (ram)
+               memset(ram, 0, fmv_ram_size);
+       cd32_fmv_state(0);
+}
+
+void cd32_fmv_free(void)
+{
+       mapped_free(rom);
+       rom = NULL;
+       mapped_free(ram);
+       ram = NULL;
+       xfree(audioram);
+       audioram = NULL;
+       xfree(videoram);
+       videoram = NULL;
+       if (cda) {
+               cda->wait(0);
+               cda->wait(1);
+               delete cda;
+       }
+       cda = NULL;
+       xfree(pcmaudio);
+       pcmaudio = NULL;
+       if (mpeg_decoder)
+               mpeg2_close(mpeg_decoder);
+       mpeg_decoder = NULL;
+       cl450_reset();
+       l64111_reset();
+}
+
 void cd32_fmv_init (uaecptr start)
 {
-       int ids[] = { 23, 74, -1 };
-       struct romlist *rl = getromlistbyids (ids);
-       struct romdata *rd;
        struct zfile *z;
 
-       write_log (_T("CD32 FMV mapped @$%lx\n"), start);
-       if (start != fmv_start)
-               return;
-       if (!rl)
+       cd32_fmv_free();
+       write_log (_T("CD32 FMV mapped @$%x\n"), start);
+       if (start != fmv_start) {
+               write_log(_T("CD32 FMV invalid base address!\n"));
                return;
-       rd = rl->rd;
-       z = read_rom (rd);
+       }
+       z = read_rom_name(currprefs.cartfile);
+       if (!z) {
+               int ids[] = { 74, 23, -1 };
+               struct romdata *rd;
+               struct romlist *rl = getromlistbyids (ids);
+               if (rl) {
+                       rd = rl->rd;
+                       write_log (_T("CD32 FMV ROM %d.%d\n"), rd->ver, rd->rev);
+                       z = read_rom (rd);
+               }
+       }
        if (z) {
-               write_log (_T("CD32 FMV ROM %d.%d\n"), rd->ver, rd->rev);
-               rom = mapped_malloc (fmv_size, _T("fast"));
-               if (rom)
-                       zfile_fread (rom, rd->size, 1, z);
+               rom = mapped_malloc(fmv_rom_size, _T("fmv_rom"));
+               if (rom) {
+                       int size = zfile_size(z);
+                       zfile_fread (rom, size > fmv_rom_size ? fmv_rom_size : size, 1, z);
+               }
                zfile_fclose (z);
        }
-       fmv_mask = fmv_size - 1;
-       fmv_bank.baseaddr = rom;
-       map_banks (&fmv_bank, start >> 16, fmv_size >> 16, 0);
+       if (!rom) {
+               write_log(_T("CD32 FMV without ROM is not supported.\n"));
+               return;
+       }
+       if (!audioram)
+               audioram = xmalloc(uae_u8, 262144);
+       if (!videoram)
+               videoram = xmalloc(struct cl450_videoram, CL450_VIDEO_BUFFERS);
+       if (!ram)
+               ram = mapped_malloc(fmv_ram_size, _T("fmv_ram"));
+       if (!pcmaudio)
+               pcmaudio = xcalloc(struct fmv_pcmaudio, L64111_CHANNEL_BUFFERS);
+
+       kjmp2_init(&mp2);
+       if (!cda) {
+               cda = new cda_audio(PCM_SECTORS, KJMP2_SAMPLES_PER_FRAME * 4);
+               l64111_setvolume();
+       }
+       if (!mpeg_decoder) {
+               mpeg_decoder = mpeg2_init();
+               mpeg_info = mpeg2_info(mpeg_decoder);
+       }
+
+       fmv_rom_bank.mask = fmv_rom_size - 1;
+       fmv_rom_bank.baseaddr = rom;
+       fmv_rom_bank.allocated = fmv_rom_size;
+       fmv_ram_bank.mask = fmv_ram_size - 1;
+       fmv_ram_bank.baseaddr = ram;
+       fmv_ram_bank.allocated = fmv_ram_size;
+       fmv_bank.mask = fmv_board_size - 1;
+       map_banks(&fmv_rom_bank, (fmv_start + ROM_BASE) >> 16, fmv_rom_size >> 16, 0);
+       map_banks(&fmv_ram_bank, (fmv_start + RAM_BASE) >> 16, fmv_ram_size >> 16, 0);
+       map_banks(&fmv_bank, (fmv_start + IO_BASE) >> 16, (RAM_BASE - IO_BASE) >> 16, 0);
+       cd32_fmv_reset();
 }
diff --git a/cd32_fmv_genlock.cpp b/cd32_fmv_genlock.cpp
new file mode 100644 (file)
index 0000000..c49e4b9
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+* UAE - The Un*x Amiga Emulator
+*
+* CD32 FMV video genlock
+*
+* Copyright 2014 Toni Wilen
+*
+*/
+
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include "options.h"
+#include "cd32_fmv.h"
+#include "xwin.h"
+
+static uae_u8 *mpeg_out_buffer;
+static int mpeg_width, mpeg_height;
+static uae_u32 fmv_border_color;
+int cd32_fmv_active;
+
+// According to schematics there is at least 3 (possible more)
+// "genlock modes" but they seem to be unused, at least ROM
+// mpeg player software never sets any genlock bits.
+
+// this is probably not how it actually works.
+#define GENLOCK_VAL 0x20
+
+#define MPEG_PIXBYTES 4
+#define MAX_MPEG_WIDTH 352
+#define MAX_MPEG_HEIGHT 288
+
+void cd32_fmv_new_border_color(uae_u32 bordercolor)
+{
+       fmv_border_color = bordercolor;
+}
+
+void cd32_fmv_new_image(int w, int h, uae_u8 *s)
+{
+       if (!mpeg_out_buffer)
+               mpeg_out_buffer = xmalloc(uae_u8, MAX_MPEG_WIDTH * MAX_MPEG_HEIGHT * MPEG_PIXBYTES);
+       if (s == NULL || w > MAX_MPEG_WIDTH || h > MAX_MPEG_HEIGHT) {
+               memset(mpeg_out_buffer, 0, MAX_MPEG_WIDTH * MAX_MPEG_HEIGHT * MPEG_PIXBYTES);
+               mpeg_width = MAX_MPEG_WIDTH;
+               mpeg_height = MAX_MPEG_HEIGHT;
+               return;
+       }
+       memcpy(mpeg_out_buffer, s, w * h * MPEG_PIXBYTES);
+       mpeg_width = w;
+       mpeg_height = h;
+}
+
+void cd32_fmv_state(int state)
+{
+       cd32_fmv_active = state;
+}
+
+// slow software method but who cares.
+
+void cd32_fmv_genlock(struct vidbuffer *vbin, struct vidbuffer *vbout)
+{
+       int hoffset, voffset, mult;
+       int w = vbin->outwidth;
+       int h = vbin->outheight;
+       int d = vbin->pixbytes;
+
+       mult = 1;
+       for (;;) {
+               if (mult < 4 && mpeg_width * (mult << 1) <= w && mpeg_height * (mult << 1) <= h) {
+                       mult <<= 1;
+               }
+               else {
+                       break;
+               }
+       }
+
+       hoffset = (w / mult - mpeg_width) / 2;
+       voffset = (h / mult - mpeg_height) / 2;
+
+       if (hoffset < 0)
+               hoffset = 0;
+       if (voffset < 0)
+               voffset = 0;
+
+       for (int hh = 0, sh = -voffset; hh < h; sh++, hh += mult) {
+               for (int h2 = 0; h2 < mult; h2++) {
+                       uae_u8 *d8 = vbout->bufmem + vbout->rowbytes * (hh + h2 + voffset);
+                       uae_u32 *d32 = (uae_u32*)d8;
+                       uae_u8 *s8 = vbin->bufmem + vbin->rowbytes * (hh + h2 + voffset) ;
+                       uae_u32 *srcp = NULL;
+                       if (sh >= 0 && sh < mpeg_height)
+                               srcp = (uae_u32*)(mpeg_out_buffer + sh * mpeg_width * MPEG_PIXBYTES);
+                       for (int ww = 0, sw = -hoffset; ww < w; sw++, ww += mult) {
+                               uae_u32 sv = fmv_border_color;
+                               if (sw >= 0 && sw < mpeg_width && srcp)
+                                       sv = srcp[sw];
+                               for (int w2 = 0; w2 < mult; w2++) {
+                                       uae_u32 v;
+                                       if (s8[0] >= GENLOCK_VAL) {
+                                               v = *((uae_u32*)s8);
+                                       } else {
+                                               v = sv;
+                                       }
+                                       *d32++ = v;
+                                       s8 += d;
+                               }
+                       }
+               }
+       }
+}
index df1dadb7aa6265baebd8a8f2825e44fb83130a10..5f051d284653eb58c0668b69ac85a449586e39ec 100644 (file)
@@ -180,7 +180,7 @@ static const TCHAR *magiccursors[] = { _T("both"), _T("native"), _T("host"), 0 }
 static const TCHAR *autoscale[] = { _T("none"), _T("auto"), _T("standard"), _T("max"), _T("scale"), _T("resize"), _T("center"), _T("manual"), _T("integer"), _T("integer_auto"), 0 };
 static const TCHAR *autoscale_rtg[] = { _T("resize"), _T("scale"), _T("center"), _T("integer"), 0 };
 static const TCHAR *joyportmodes[] = { _T(""), _T("mouse"), _T("mousenowheel"), _T("djoy"), _T("gamepad"), _T("ajoy"), _T("cdtvjoy"), _T("cd32joy"), _T("lightpen"), 0 };
-static const TCHAR *joyaf[] = { _T("none"), _T("normal"), _T("toggle"), 0 };
+static const TCHAR *joyaf[] = { _T("none"), _T("normal"), _T("toggle"), _T("always"), 0 };
 static const TCHAR *epsonprinter[] = { _T("none"), _T("ascii"), _T("epson_matrix_9pin"), _T("epson_matrix_24pin"), _T("epson_matrix_48pin"), 0 };
 static const TCHAR *aspects[] = { _T("none"), _T("vga"), _T("tv"), 0 };
 static const TCHAR *vsyncmodes[] = { _T("false"), _T("true"), _T("autoswitch"), 0 };
@@ -1391,8 +1391,9 @@ void cfgfile_save_options (struct zfile *f, struct uae_prefs *p, int type)
        cfgfile_dwrite_bool (f, _T("ksmirror_a8"), p->cs_ksmirror_a8);
        cfgfile_dwrite_bool (f, _T("cd32cd"), p->cs_cd32cd);
        cfgfile_dwrite_bool (f, _T("cd32c2p"), p->cs_cd32c2p);
-       cfgfile_dwrite_bool (f, _T("cd32nvram"), p->cs_cd32nvram);
-       cfgfile_dwrite_bool (f, _T("cdtvcd"), p->cs_cdtvcd);
+       cfgfile_dwrite_bool(f, _T("cd32nvram"), p->cs_cd32nvram);
+       cfgfile_dwrite_bool(f, _T("cd32fmv"), p->cs_cd32fmv);
+       cfgfile_dwrite_bool(f, _T("cdtvcd"), p->cs_cdtvcd);
        cfgfile_dwrite_bool (f, _T("cdtvram"), p->cs_cdtvram);
        cfgfile_dwrite (f, _T("cdtvramcard"), _T("%d"), p->cs_cdtvcard);
        cfgfile_dwrite_str (f, _T("ide"), p->cs_ide == IDE_A600A1200 ? _T("a600/a1200") : (p->cs_ide == IDE_A4000 ? _T("a4000") : _T("none")));
@@ -3514,8 +3515,9 @@ static int cfgfile_parse_hardware (struct uae_prefs *p, const TCHAR *option, TCH
                || cfgfile_yesno (option, value, _T("cpu_no_unimplemented"), &p->int_no_unimplemented)
                || cfgfile_yesno (option, value, _T("cd32cd"), &p->cs_cd32cd)
                || cfgfile_yesno (option, value, _T("cd32c2p"), &p->cs_cd32c2p)
-               || cfgfile_yesno (option, value, _T("cd32nvram"), &p->cs_cd32nvram)
-               || cfgfile_yesno (option, value, _T("cdtvcd"), &p->cs_cdtvcd)
+               || cfgfile_yesno(option, value, _T("cd32nvram"), &p->cs_cd32nvram)
+               || cfgfile_yesno(option, value, _T("cd32fmv"), &p->cs_cd32fmv)
+               || cfgfile_yesno(option, value, _T("cdtvcd"), &p->cs_cdtvcd)
                || cfgfile_yesno (option, value, _T("cdtvram"), &p->cs_cdtvram)
                || cfgfile_yesno (option, value, _T("a1000ram"), &p->cs_a1000ram)
                || cfgfile_yesno (option, value, _T("pcmcia"), &p->cs_pcmcia)
@@ -5308,7 +5310,7 @@ void default_prefs (struct uae_prefs *p, int type)
        p->cs_mbdmac = 0;
        p->a2091 = 0;
        p->a4091 = 0;
-       p->cs_cd32c2p = p->cs_cd32cd = p->cs_cd32nvram = false;
+       p->cs_cd32c2p = p->cs_cd32cd = p->cs_cd32nvram = p->cs_cd32fmv = false;
        p->cs_cdtvcd = p->cs_cdtvram = false;
        p->cs_cdtvcard = 0;
        p->cs_pcmcia = 0;
@@ -5562,7 +5564,7 @@ static void buildin_default_prefs (struct uae_prefs *p)
        p->cs_mbdmac = 0;
        p->a2091 = false;
        p->a4091 = false;
-       p->cs_cd32c2p = p->cs_cd32cd = p->cs_cd32nvram = false;
+       p->cs_cd32c2p = p->cs_cd32cd = p->cs_cd32nvram = p->cs_cd32fmv = false;
        p->cs_cdtvcd = p->cs_cdtvram = p->cs_cdtvcard = false;
        p->cs_ide = 0;
        p->cs_pcmcia = 0;
@@ -5810,10 +5812,10 @@ static int bip_cdtv (struct uae_prefs *p, int config, int compa, int romcheck)
 
 static int bip_cd32 (struct uae_prefs *p, int config, int compa, int romcheck)
 {
-       int roms[2];
+       int roms[3];
 
        buildin_default_prefs_68020 (p);
-       p->cs_cd32c2p = p->cs_cd32cd = p->cs_cd32nvram = 1;
+       p->cs_cd32c2p = p->cs_cd32cd = p->cs_cd32nvram = true;
        p->nr_floppies = 0;
        p->floppyslots[0].dfxtype = DRV_NONE;
        p->floppyslots[1].dfxtype = DRV_NONE;
@@ -5834,7 +5836,10 @@ static int bip_cd32 (struct uae_prefs *p, int config, int compa, int romcheck)
                        return 0;
        }
        if (config > 0) {
-               roms[0] = 23;
+               p->cs_cd32fmv = true;
+               roms[0] = 74;
+               roms[1] = 23;
+               roms[2] = -1;
                if (!configure_rom (p, roms, romcheck))
                        return 0;
        }
@@ -6135,7 +6140,7 @@ int built_in_chipset_prefs (struct uae_prefs *p)
                p->cs_ksmirror_e0 = 0;
                break;
        case CP_CD32: // CD32
-               p->cs_cd32c2p = p->cs_cd32cd = p->cs_cd32nvram = 1;
+               p->cs_cd32c2p = p->cs_cd32cd = p->cs_cd32nvram = true;
                p->cs_ksmirror_e0 = 0;
                p->cs_ksmirror_a8 = 1;
                p->cs_ciaoverlay = 0;
index 7456672551b458480c4e579b4a5a399b8fa6c278..78a19687f97cca47917a59349ef150919ffaca22 100644 (file)
@@ -44,6 +44,7 @@
 #endif
 #include "debug.h"
 #include "akiko.h"
+#include "cd32_fmv.h"
 #include "cdtv.h"
 #if defined(ENFORCER)
 #include "enforcer.h"
@@ -202,6 +203,7 @@ int firstblankedline;
 static int equ_vblank_endline = EQU_ENDLINE_PAL;
 static bool equ_vblank_toggle = true;
 double vblank_hz = VBLANK_HZ_PAL, fake_vblank_hz, vblank_hz_stored, vblank_hz_nom;
+double hblank_hz;
 static float vblank_hz_lof, vblank_hz_shf, vblank_hz_lace;
 static int vblank_hz_mult, vblank_hz_state;
 static struct chipset_refresh *stored_chipset_refresh;
@@ -3504,6 +3506,9 @@ int vsynctimebase_orig;
 
 void compute_vsynctime (void)
 {
+       double svpos = maxvpos_nom;
+       double shpos = maxhpos_short;
+
        fake_vblank_hz = 0;
        vblank_hz_mult = 0;
        vblank_hz_state = 1;
@@ -3529,21 +3534,20 @@ void compute_vsynctime (void)
                updatedisplayarea ();
        }
 #endif
+       if (islinetoggle ()) {
+               shpos += 0.5;
+       }
+       if (interlace_seen) {
+               svpos += 0.5;
+       } else if (lof_current) {
+               svpos += 1.0;
+       }
        if (currprefs.produce_sound > 1) {
-               double svpos = maxvpos_nom;
-               double shpos = maxhpos_short;
-               if (islinetoggle ()) {
-                       shpos += 0.5;
-               }
-               if (interlace_seen) {
-                       svpos += 0.5;
-               } else if (lof_current) {
-                       svpos += 1.0;
-               }
                double clk = svpos * shpos * fake_vblank_hz;
                //write_log (_T("SNDRATE %.1f*%.1f*%.6f=%.6f\n"), svpos, shpos, fake_vblank_hz, clk);
                update_sound (clk);
        }
+       cd32_fmv_set_sync(svpos);
 }
 
 
@@ -3770,12 +3774,14 @@ void compute_framesync (void)
 
        compute_vsynctime ();
 
+       hblank_hz = (double)(currprefs.ntscmode ? CHIPSET_CLOCK_NTSC : CHIPSET_CLOCK_PAL) / (maxhpos + (islinetoggle() ? 0.5 : 0));
+
        write_log (_T("%s mode%s%s V=%.4fHz H=%0.4fHz (%dx%d+%d) IDX=%d (%s) D=%d RTG=%d/%d\n"),
                isntsc ? _T("NTSC") : _T("PAL"),
                islace ? _T(" lace") : (lof_lace ? _T(" loflace") : _T("")),
                doublescan > 0 ? _T(" dblscan") : _T(""),
                vblank_hz,
-               (double)(currprefs.ntscmode ? CHIPSET_CLOCK_NTSC : CHIPSET_CLOCK_PAL) / (maxhpos + (islinetoggle () ? 0.5 : 0)),
+               hblank_hz,
                maxhpos, maxvpos, lof_store ? 1 : 0,
                cr ? cr->index : -1,
                cr != NULL && cr->label != NULL ? cr->label : _T("<?>"),
@@ -4611,6 +4617,7 @@ static void rethink_intreq (void)
 #endif
 #ifdef CD32
        rethink_akiko ();
+       rethink_cd32fmv();
 #endif
        rethink_gayle ();
 }
@@ -7064,6 +7071,9 @@ static void vsync_handler_pre (void)
 #ifdef RETROPLATFORM
        rp_vsync ();
 #endif
+#ifdef CD32
+       cd32_fmv_vsync_handler();
+#endif
 
        if (!vsync_rendered) {
                frame_time_t start, end;
@@ -7483,6 +7493,7 @@ static void hsync_handler_pre (bool onvsync)
 #endif
 #ifdef CD32
        AKIKO_hsync_handler ();
+       cd32_fmv_hsync_handler();
 #endif
 #ifdef CDTV
        CDTV_hsync_handler ();
index 7456e8caaec81a39c0b51876a4a840dccd02fc82..e9a1d48756cd0e5d8197478f16adcb316bc2f75c 100644 (file)
@@ -49,6 +49,7 @@ happening, all ports should restrict window widths to be multiples of 16 pixels.
 #include "statusline.h"
 #include "inputdevice.h"
 #include "debug.h"
+#include "cd32_fmv.h"
 
 extern bool emulate_specialmonitors (struct vidbuffer*, struct vidbuffer*);
 
@@ -3125,6 +3126,16 @@ bool draw_frame (struct vidbuffer *vb)
        return true;
 }
 
+static void setnativeposition(struct vidbuffer *vb)
+{
+       vb->inwidth = gfxvidinfo.drawbuffer.inwidth;
+       vb->inheight = gfxvidinfo.drawbuffer.inheight;
+       vb->inwidth2 = gfxvidinfo.drawbuffer.inwidth2;
+       vb->inheight2 = gfxvidinfo.drawbuffer.inheight2;
+       vb->outwidth = gfxvidinfo.drawbuffer.outwidth;
+       vb->outheight = gfxvidinfo.drawbuffer.outheight;
+}
+
 static void finish_drawing_frame (void)
 {
        int i;
@@ -3172,14 +3183,8 @@ static void finish_drawing_frame (void)
        if (currprefs.monitoremu && gfxvidinfo.tempbuffer.bufmem_allocated) {
                if (emulate_specialmonitors (vb, &gfxvidinfo.tempbuffer)) {
                        vb = gfxvidinfo.outbuffer = &gfxvidinfo.tempbuffer;
-                       if (vb->nativepositioning) {
-                               vb->inwidth = gfxvidinfo.drawbuffer.inwidth;
-                               vb->inheight = gfxvidinfo.drawbuffer.inheight;
-                               vb->inwidth2 = gfxvidinfo.drawbuffer.inwidth2;
-                               vb->inheight2 = gfxvidinfo.drawbuffer.inheight2;
-                               vb->outwidth = gfxvidinfo.drawbuffer.outwidth;
-                               vb->outheight = gfxvidinfo.drawbuffer.outheight;
-                       }
+                       if (vb->nativepositioning)
+                               setnativeposition(vb);
                        gfxvidinfo.drawbuffer.tempbufferinuse = true;
                        if (!specialmonitoron)
                                compute_framesync ();
@@ -3194,6 +3199,19 @@ static void finish_drawing_frame (void)
                }
        }
 
+       if (!currprefs.monitoremu && gfxvidinfo.tempbuffer.bufmem_allocated && currprefs.cs_cd32fmv) {
+               if (cd32_fmv_active) {
+                       cd32_fmv_genlock(vb, &gfxvidinfo.tempbuffer);
+                       vb = gfxvidinfo.outbuffer = &gfxvidinfo.tempbuffer;
+                       setnativeposition(vb);
+                       gfxvidinfo.drawbuffer.tempbufferinuse = true;
+                       do_flush_screen(vb, 0, vb->outheight);
+                       didflush = true;
+               } else {
+                       gfxvidinfo.drawbuffer.tempbufferinuse = false;
+               }
+       }
+
        if (!didflush)
                do_flush_screen (vb, first_drawn_line, last_drawn_line);
 }
index 2c5edb0ee3431e9868e8d3bb1823dd0419c26551..25d3dc5cc2cd0f041fc039bb0774063856b0d84c 100644 (file)
@@ -1520,15 +1520,11 @@ void expamem_reset (void)
        }
 #endif
 #ifdef CD32
-       if (currprefs.cs_cd32cd && currprefs.fastmem_size == 0 && currprefs.chipmem_size <= 0x200000) {
-               int ids[] = { 23, -1 };
-               struct romlist *rl = getromlistbyids (ids);
-               if (rl && !_tcscmp (rl->path, currprefs.cartfile)) {
-                       card_flags[cardno] = 0;
-                       card_name[cardno] = _T("CD32MPEG");
-                       card_init[cardno] = expamem_init_cd32fmv;
-                       card_map[cardno++] = expamem_map_cd32fmv;
-               }
+       if (currprefs.cs_cd32cd && currprefs.fastmem_size == 0 && currprefs.chipmem_size <= 0x200000 && currprefs.cs_cd32fmv) {
+               card_flags[cardno] = 0;
+               card_name[cardno] = _T("CD32MPEG");
+               card_init[cardno] = expamem_init_cd32fmv;
+               card_map[cardno++] = expamem_map_cd32fmv;
        }
 #endif
 #ifdef A2065
index 458f849ddabbf2580953f0d5a5022515f5ca1d38..3343ab7f53dcf19e9e0c6dedc02b304322b4da1e 100644 (file)
@@ -1,2 +1,15 @@
 
 extern void cd32_fmv_init (uaecptr);
+extern void cd32_fmv_reset(void);
+extern void cd32_fmv_free(void);
+extern void rethink_cd32fmv(void);
+extern void cd32_fmv_hsync_handler(void);
+extern void cd32_fmv_vsync_handler(void);
+
+extern void cd32_fmv_state(int state);
+extern void cd32_fmv_new_image(int, int, uae_u8*);
+extern void cd32_fmv_genlock(struct vidbuffer*, struct vidbuffer*);
+extern void cd32_fmv_new_border_color(uae_u32);
+extern void cd32_fmv_set_sync(double svpos);
+
+extern int cd32_fmv_active;
index 660fba93aeefc0f5bcc5ebe0949dc2cd4228a276..9e212ddb8ce14289b1ff87cc6152ea45535373e4 100644 (file)
@@ -128,6 +128,7 @@ extern int maxvpos, maxvpos_nom, maxvpos_display;
 extern int hsyncstartpos, hsyncendpos;
 extern int minfirstline, vblank_endline, numscrlines;
 extern double vblank_hz, fake_vblank_hz;
+extern double hblank_hz;
 extern int vblank_skip, doublescan;
 extern bool programmedmode;
 
index a8ce1e5fdf49bc8a13f6dfa736c668ba569ee207..2f5223959bf82b13eb1a2d91cf706d2d1b02b052 100644 (file)
@@ -176,7 +176,7 @@ struct uaedev_config_data
        int unitnum; // scsi unit number (if tape currently)
 };
 
-enum { CP_GENERIC = 1, CP_CDTV, CP_CD32, CP_A500, CP_A500P, CP_A600, CP_A1000,
+enum { CP_GENERIC = 1, CP_CDTV, CP_CD32, CP_CD32FMV, CP_A500, CP_A500P, CP_A600, CP_A1000,
        CP_A1200, CP_A2000, CP_A3000, CP_A3000T, CP_A4000, CP_A4000T };
 
 #define IDE_A600A1200 1
@@ -420,6 +420,7 @@ struct uae_prefs {
        bool cs_cd32cd;
        bool cs_cd32c2p;
        bool cs_cd32nvram;
+       bool cs_cd32fmv;
        bool cs_cdtvcd;
        bool cs_cdtvram;
        int cs_cdtvcard;
index 6484e86dbcc373a732166666e74296efbc852bd2..b886b577de9c5cfd3ebf8c2d537034e2c70865af 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -38,6 +38,7 @@
 #include "scsidev.h"
 #include "uaeserial.h"
 #include "akiko.h"
+#include "cd32_fmv.h"
 #include "cdtv.h"
 #include "savestate.h"
 #include "filesys.h"
@@ -205,9 +206,15 @@ void fixup_prefs_dimensions (struct uae_prefs *prefs)
                if (prefs->gf[i].gfx_filter == 0 && ((prefs->gf[i].gfx_filter_autoscale && !prefs->gfx_api) || (prefs->gfx_apmode[APMODE_NATIVE].gfx_vsyncmode))) {
                        prefs->gf[i].gfx_filter = 1;
                }
-               if (i == 0 && prefs->gf[i].gfx_filter == 0 && prefs->monitoremu) {
-                       error_log (_T("A2024 and Graffiti require at least null filter enabled."));
-                       prefs->gf[i].gfx_filter = 1;
+               if (i == 0) {
+                       if (prefs->gf[i].gfx_filter == 0 && prefs->monitoremu) {
+                               error_log(_T("A2024 and Graffiti require at least null filter enabled."));
+                               prefs->gf[i].gfx_filter = 1;
+                       }
+                       if (prefs->gf[i].gfx_filter == 0 && prefs->cs_cd32fmv) {
+                               error_log(_T("CD32 MPEG module overlay support require at least null filter enabled."));
+                               prefs->gf[i].gfx_filter = 1;
+                       }
                }
        }
 }
@@ -962,6 +969,7 @@ void do_leave_program (void)
 #endif
 #ifdef CD32
        akiko_free ();
+       cd32_fmv_free();
 #endif
        if (! no_gui)
                gui_exit ();
index 4fd0daa6818ff28e96de11bd257fe7f0c17112b3..e3d3c71ead97c73eee16e84a16c9f407cedbad86 100644 (file)
@@ -538,11 +538,17 @@ void *shmat (int shmid, void *shmaddr, int shmflg)
                        shmaddr=natmem_offset + 0xf00000;
                        got = TRUE;
                        readonly = TRUE;
-               } else if(!_tcscmp (shmids[shmid].name, _T("rtarea"))) {
-                       shmaddr=natmem_offset + rtarea_base;
+               } else if (!_tcscmp(shmids[shmid].name, _T("rtarea"))) {
+                       shmaddr = natmem_offset + rtarea_base;
                        got = TRUE;
                        readonly = TRUE;
                        readonlysize = RTAREA_TRAPS;
+               } else if (!_tcscmp(shmids[shmid].name, _T("fmv_rom"))) {
+                       got = TRUE;
+                       shmaddr = natmem_offset + 0x200000;
+               } else if (!_tcscmp(shmids[shmid].name, _T("fmv_ram"))) {
+                       got = TRUE;
+                       shmaddr = natmem_offset + 0x280000;
                } else if(!_tcscmp (shmids[shmid].name, _T("fast"))) {
                        got = TRUE;
                        if (size < 524288) {