mirror of
https://github.com/Oibaf66/uae-wii.git
synced 2024-11-24 19:46:55 +01:00
1498 lines
35 KiB
C
1498 lines
35 KiB
C
|
/*
|
||
|
* UAE - The Un*x Amiga Emulator
|
||
|
*
|
||
|
* CD32 Akiko emulation
|
||
|
*
|
||
|
* - C2P
|
||
|
* - NVRAM
|
||
|
* - CDROM
|
||
|
*
|
||
|
* Copyright 2001, 2002 Toni Wilen
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "sysconfig.h"
|
||
|
#include "sysdeps.h"
|
||
|
|
||
|
#include "options.h"
|
||
|
#include "memory.h"
|
||
|
#include "custom.h"
|
||
|
#include "newcpu.h"
|
||
|
#include "events.h"
|
||
|
#include "savestate.h"
|
||
|
#include "blkdev.h"
|
||
|
#include "zfile.h"
|
||
|
#include "threaddep/thread.h"
|
||
|
#include "akiko.h"
|
||
|
#include "gui.h"
|
||
|
#include "sleep.h"
|
||
|
#include "cdrom.h"
|
||
|
|
||
|
#define AKIKO_DEBUG_NVRAM 0
|
||
|
#define AKIKO_DEBUG_IO 0
|
||
|
#define AKIKO_DEBUG_IO_CMD 0
|
||
|
|
||
|
int cd32_enabled;
|
||
|
|
||
|
//static int m68k_getpc(void) { return 0; }
|
||
|
|
||
|
/*
|
||
|
* CD32 1Kb NVRAM (EEPROM) emulation
|
||
|
*
|
||
|
* NVRAM chip is 24C08 CMOS EEPROM (1024x8 bits = 1Kb)
|
||
|
* Chip interface is I2C (2 wire serial)
|
||
|
* Akiko addresses used:
|
||
|
* 0xb80030: bit 7 = SCL (clock), 6 = SDA (data)
|
||
|
* 0xb80032: 0xb80030 data direction register (0 = input, 1 = output)
|
||
|
*
|
||
|
* Because I don't have any experience on I2C, following code may be
|
||
|
* unnecessarily complex and not 100% correct..
|
||
|
*/
|
||
|
|
||
|
enum i2c { I2C_WAIT, I2C_START, I2C_DEVICEADDR, I2C_WORDADDR, I2C_DATA };
|
||
|
|
||
|
/* size of EEPROM, don't try to change,
|
||
|
* (hardcoded in Kickstart)
|
||
|
*/
|
||
|
#define NVRAM_SIZE 1024
|
||
|
/* max size of one write request */
|
||
|
#define NVRAM_PAGE_SIZE 16
|
||
|
|
||
|
static uae_u8 cd32_nvram[NVRAM_SIZE], nvram_writetmp[NVRAM_PAGE_SIZE];
|
||
|
static int nvram_address, nvram_writeaddr;
|
||
|
static int nvram_rw;
|
||
|
static int bitcounter = -1, direction = -1;
|
||
|
static uae_u8 nvram_byte;
|
||
|
static int scl_out, scl_in, scl_dir, oscl, sda_out, sda_in, sda_dir, osda;
|
||
|
static int sda_dir_nvram;
|
||
|
static int state = I2C_WAIT;
|
||
|
|
||
|
static void nvram_write (unsigned int offset, unsigned int len)
|
||
|
{
|
||
|
struct zfile *f = zfile_fopen (currprefs.flashfile, "rb+");
|
||
|
if (!f) {
|
||
|
f = zfile_fopen (currprefs.flashfile, "wb");
|
||
|
if (!f) return;
|
||
|
zfile_fwrite (cd32_nvram, NVRAM_SIZE, 1, f);
|
||
|
}
|
||
|
zfile_fseek (f, offset, SEEK_SET);
|
||
|
zfile_fwrite (cd32_nvram + offset, len, 1, f);
|
||
|
zfile_fclose (f);
|
||
|
}
|
||
|
|
||
|
static void nvram_read (void)
|
||
|
{
|
||
|
struct zfile *f;
|
||
|
|
||
|
f = zfile_fopen (currprefs.flashfile, "rb");
|
||
|
memset (cd32_nvram, 0, NVRAM_SIZE);
|
||
|
if (!f) return;
|
||
|
zfile_fread (cd32_nvram, NVRAM_SIZE, 1, f);
|
||
|
zfile_fclose (f);
|
||
|
}
|
||
|
|
||
|
static void i2c_do (void)
|
||
|
{
|
||
|
#if AKIKO_DEBUG_NVRAM
|
||
|
int i;
|
||
|
#endif
|
||
|
sda_in = 1;
|
||
|
if (!sda_dir_nvram && scl_out && oscl) {
|
||
|
if (!sda_out && osda) { /* START-condition? */
|
||
|
state = I2C_DEVICEADDR;
|
||
|
bitcounter = 0;
|
||
|
direction = -1;
|
||
|
#if AKIKO_DEBUG_NVRAM
|
||
|
write_log ("START\n");
|
||
|
#endif
|
||
|
return;
|
||
|
} else if(sda_out && !osda) { /* STOP-condition? */
|
||
|
state = I2C_WAIT;
|
||
|
bitcounter = -1;
|
||
|
#if AKIKO_DEBUG_NVRAM
|
||
|
write_log ("STOP\n");
|
||
|
#endif
|
||
|
if (direction > 0) {
|
||
|
memcpy (cd32_nvram + (nvram_address & ~(NVRAM_PAGE_SIZE - 1)), nvram_writetmp, NVRAM_PAGE_SIZE);
|
||
|
nvram_write (nvram_address & ~(NVRAM_PAGE_SIZE - 1), NVRAM_PAGE_SIZE);
|
||
|
direction = -1;
|
||
|
#if AKIKO_DEBUG_NVRAM
|
||
|
write_log ("NVRAM write address %04.4X:", nvram_address & ~(NVRAM_PAGE_SIZE - 1));
|
||
|
for (i = 0; i < NVRAM_PAGE_SIZE; i++)
|
||
|
write_log ("%02.2X", nvram_writetmp[i]);
|
||
|
write_log ("\n");
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
if (bitcounter >= 0) {
|
||
|
if (direction) {
|
||
|
/* Amiga -> NVRAM */
|
||
|
if (scl_out && !oscl) {
|
||
|
if (bitcounter == 8) {
|
||
|
#if AKIKO_DEBUG_NVRAM
|
||
|
write_log ("RB %02.2X ", nvram_byte, m68k_getpc());
|
||
|
#endif
|
||
|
sda_in = 0; /* ACK */
|
||
|
if (direction > 0) {
|
||
|
nvram_writetmp[nvram_writeaddr++] = nvram_byte;
|
||
|
nvram_writeaddr &= 15;
|
||
|
bitcounter = 0;
|
||
|
} else {
|
||
|
bitcounter = -1;
|
||
|
}
|
||
|
} else {
|
||
|
//write_log("NVRAM received bit %d, offset %d\n", sda_out, bitcounter);
|
||
|
nvram_byte <<= 1;
|
||
|
nvram_byte |= sda_out;
|
||
|
bitcounter++;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
/* NVRAM -> Amiga */
|
||
|
if (scl_out && !oscl && bitcounter < 8) {
|
||
|
if (bitcounter == 0)
|
||
|
nvram_byte = cd32_nvram[nvram_address];
|
||
|
sda_dir_nvram = 1;
|
||
|
sda_in = (nvram_byte & 0x80) ? 1 : 0;
|
||
|
//write_log("NVRAM sent bit %d, offset %d\n", sda_in, bitcounter);
|
||
|
nvram_byte <<= 1;
|
||
|
bitcounter++;
|
||
|
if (bitcounter == 8) {
|
||
|
#if AKIKO_DEBUG_NVRAM
|
||
|
write_log ("NVRAM sent byte %02.2X address %04.4X PC=%08.8X\n", cd32_nvram[nvram_address], nvram_address, m68k_getpc());
|
||
|
#endif
|
||
|
nvram_address++;
|
||
|
nvram_address &= NVRAM_SIZE - 1;
|
||
|
sda_dir_nvram = 0;
|
||
|
}
|
||
|
}
|
||
|
if(!sda_out && sda_dir && !scl_out) /* ACK from Amiga */
|
||
|
bitcounter = 0;
|
||
|
}
|
||
|
if (bitcounter >= 0) return;
|
||
|
}
|
||
|
switch (state)
|
||
|
{
|
||
|
case I2C_DEVICEADDR:
|
||
|
if ((nvram_byte & 0xf0) != 0xa0) {
|
||
|
write_log ("WARNING: I2C_DEVICEADDR: device address != 0xA0\n");
|
||
|
state = I2C_WAIT;
|
||
|
return;
|
||
|
}
|
||
|
nvram_rw = (nvram_byte & 1) ? 0 : 1;
|
||
|
if (nvram_rw) {
|
||
|
/* 2 high address bits, only fetched if WRITE = 1 */
|
||
|
nvram_address &= 0xff;
|
||
|
nvram_address |= ((nvram_byte >> 1) & 3) << 8;
|
||
|
state = I2C_WORDADDR;
|
||
|
direction = -1;
|
||
|
} else {
|
||
|
state = I2C_DATA;
|
||
|
direction = 0;
|
||
|
sda_dir_nvram = 1;
|
||
|
}
|
||
|
bitcounter = 0;
|
||
|
#if AKIKO_DEBUG_NVRAM
|
||
|
write_log ("I2C_DEVICEADDR: rw %d, address %02.2Xxx PC=%08.8X\n", nvram_rw, nvram_address >> 8, m68k_getpc());
|
||
|
#endif
|
||
|
break;
|
||
|
case I2C_WORDADDR:
|
||
|
nvram_address &= 0x300;
|
||
|
nvram_address |= nvram_byte;
|
||
|
#if AKIKO_DEBUG_NVRAM
|
||
|
write_log ("I2C_WORDADDR: address %04.4X PC=%08.8X\n", nvram_address, m68k_getpc());
|
||
|
#endif
|
||
|
if (direction < 0) {
|
||
|
memcpy (nvram_writetmp, cd32_nvram + (nvram_address & ~(NVRAM_PAGE_SIZE - 1)), NVRAM_PAGE_SIZE);
|
||
|
nvram_writeaddr = nvram_address & (NVRAM_PAGE_SIZE - 1);
|
||
|
}
|
||
|
state = I2C_DATA;
|
||
|
bitcounter = 0;
|
||
|
direction = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void akiko_nvram_write (int offset, uae_u32 v)
|
||
|
{
|
||
|
int sda;
|
||
|
switch (offset)
|
||
|
{
|
||
|
case 0:
|
||
|
oscl = scl_out;
|
||
|
scl_out = (v & 0x80) ? 1 : 0;
|
||
|
osda = sda_out;
|
||
|
sda_out = (v & 0x40) ? 1 : 0;
|
||
|
break;
|
||
|
case 2:
|
||
|
scl_dir = (v & 0x80) ? 1 : 0;
|
||
|
sda_dir = (v & 0x40) ? 1 : 0;
|
||
|
break;
|
||
|
default:
|
||
|
return;
|
||
|
}
|
||
|
sda = sda_out;
|
||
|
if (oscl != scl_out || osda != sda) {
|
||
|
i2c_do ();
|
||
|
oscl = scl_out;
|
||
|
osda = sda;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static uae_u32 akiko_nvram_read (int offset)
|
||
|
{
|
||
|
uae_u32 v = 0;
|
||
|
switch (offset)
|
||
|
{
|
||
|
case 0:
|
||
|
if (!scl_dir)
|
||
|
v |= scl_in ? 0x80 : 0x00;
|
||
|
else
|
||
|
v |= scl_out ? 0x80 : 0x00;
|
||
|
if (!sda_dir)
|
||
|
v |= sda_in ? 0x40 : 0x00;
|
||
|
else
|
||
|
v |= sda_out ? 0x40 : 0x00;
|
||
|
break;
|
||
|
case 2:
|
||
|
v |= scl_dir ? 0x80 : 0x00;
|
||
|
v |= sda_dir ? 0x40 : 0x00;
|
||
|
break;
|
||
|
}
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
/* CD32 Chunky to Planar hardware emulation
|
||
|
* Akiko addresses used:
|
||
|
* 0xb80038-0xb8003b
|
||
|
*/
|
||
|
|
||
|
static uae_u32 akiko_buffer[8];
|
||
|
static int akiko_read_offset, akiko_write_offset;
|
||
|
static uae_u32 akiko_result[8];
|
||
|
|
||
|
static void akiko_c2p_do (void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < 8; i++) akiko_result[i] = 0;
|
||
|
/* FIXME: better c2p algoritm than this piece of crap.... */
|
||
|
for (i = 0; i < 8 * 32; i++) {
|
||
|
if (akiko_buffer[7 - (i >> 5)] & (1 << (i & 31)))
|
||
|
akiko_result[i & 7] |= 1 << (i >> 3);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void akiko_c2p_write (int offset, uae_u32 v)
|
||
|
{
|
||
|
if (offset == 3) akiko_buffer[akiko_write_offset] = 0;
|
||
|
akiko_buffer[akiko_write_offset] |= v << ( 8 * (3 - offset));
|
||
|
if (offset == 0) {
|
||
|
akiko_write_offset++;
|
||
|
akiko_write_offset &= 7;
|
||
|
}
|
||
|
akiko_read_offset = 0;
|
||
|
}
|
||
|
|
||
|
static uae_u32 akiko_c2p_read (int offset)
|
||
|
{
|
||
|
uae_u32 v;
|
||
|
|
||
|
if (akiko_read_offset == 0 && offset == 3)
|
||
|
akiko_c2p_do ();
|
||
|
akiko_write_offset = 0;
|
||
|
v = akiko_result[akiko_read_offset];
|
||
|
if (offset == 0) {
|
||
|
akiko_read_offset++;
|
||
|
akiko_read_offset &= 7;
|
||
|
}
|
||
|
return v >> (8 * (3 - offset));
|
||
|
}
|
||
|
|
||
|
/* CD32 CDROM hardware emulation
|
||
|
* Akiko addresses used:
|
||
|
* 0xb80004-0xb80028
|
||
|
*
|
||
|
* I can't believe cd.device and custom loaders are fooled to think
|
||
|
* this piece of crap emulates real CD32 CDROM controller and drive :)
|
||
|
*/
|
||
|
|
||
|
#define CDSTATUS_FRAME 0x80000000
|
||
|
#define CDSTATUS_DATA_AVAILABLE 0x10000000
|
||
|
#define CDSTATUS_DATASECTOR_ERROR 0x08000000 /* ?? */
|
||
|
#define CDSTATUS_DATASECTOR 0x04000000
|
||
|
#define CDSTATUS_UNKNOWN 0x02000000
|
||
|
|
||
|
#define CDS_ERROR 0x80
|
||
|
#define CDS_PLAYING 0x08
|
||
|
|
||
|
static uae_u32 cdrom_status1, cdrom_status2;
|
||
|
static uae_u8 cdrom_status3;
|
||
|
static uae_u32 cdrom_address1, cdrom_address2;
|
||
|
static uae_u32 cdrom_longmask;
|
||
|
static uae_u32 cdrom_readmask_r, cdrom_readmask_w;
|
||
|
static uae_u8 cdrom_command_offset_complete; /* 0x19 */
|
||
|
static uae_u8 cdrom_command_offset_todo; /* 0x1d */
|
||
|
static uae_u8 cdrom_result_complete; /* 0x1a */
|
||
|
static uae_u8 cdrom_result_last_pos; /* 0x1f */
|
||
|
static uae_u8 cdrom_result_buffer[32];
|
||
|
static uae_u8 cdrom_command_buffer[32];
|
||
|
static uae_u8 cdrom_command;
|
||
|
|
||
|
#define MAX_TOC_ENTRIES 103 /* tracks 1-99, A0,A1 and A2 */
|
||
|
static int cdrom_toc_entries;
|
||
|
static int cdrom_toc_counter;
|
||
|
static uae_u8 cdrom_toc_buffer[MAX_TOC_ENTRIES*13];
|
||
|
|
||
|
static int cdrom_disk, cdrom_paused, cdrom_playing;
|
||
|
static int cdrom_command_active;
|
||
|
static int cdrom_command_length;
|
||
|
static int cdrom_checksum_error;
|
||
|
static int cdrom_data_offset, cdrom_speed, cdrom_sector_counter;
|
||
|
static int cdrom_current_sector;
|
||
|
static int cdrom_data_end, cdrom_leadout;
|
||
|
static int cdrom_dosomething;
|
||
|
|
||
|
static uae_u8 *sector_buffer_1, *sector_buffer_2;
|
||
|
static int sector_buffer_sector_1, sector_buffer_sector_2;
|
||
|
#define SECTOR_BUFFER_SIZE 64
|
||
|
static uae_u8 *sector_buffer_info_1, *sector_buffer_info_2;
|
||
|
|
||
|
static int unitnum = -1;
|
||
|
|
||
|
static uae_u8 frombcd (uae_u8 v)
|
||
|
{
|
||
|
return (v >> 4) * 10 + (v & 15);
|
||
|
}
|
||
|
|
||
|
static uae_u8 tobcd (uae_u8 v)
|
||
|
{
|
||
|
return ((v / 10) << 4) | (v % 10);
|
||
|
}
|
||
|
|
||
|
static int fromlongbcd (uae_u8 *p)
|
||
|
{
|
||
|
return (frombcd (p[0]) << 16) | (frombcd (p[1]) << 8) | (frombcd (p[2]) << 0);
|
||
|
}
|
||
|
|
||
|
/* convert minutes, seconds and frames -> logical sector number */
|
||
|
static int msf2lsn (int msf)
|
||
|
{
|
||
|
int sector = (((msf >> 16) & 0xff) * 60 * 75 + ((msf >> 8) & 0xff) * 75 + ((msf >> 0) & 0xff)) - 150;
|
||
|
if (sector < 0)
|
||
|
sector = 0;
|
||
|
return sector;
|
||
|
}
|
||
|
|
||
|
/* convert logical sector number -> minutes, seconds and frames */
|
||
|
static int lsn2msf (int sectors)
|
||
|
{
|
||
|
int msf;
|
||
|
sectors += 150;
|
||
|
msf = (sectors / (75 * 60)) << 16;
|
||
|
msf |= ((sectors / 75) % 60) << 8;
|
||
|
msf |= (sectors % 75) << 0;
|
||
|
return msf;
|
||
|
}
|
||
|
|
||
|
static void cdaudiostop (void)
|
||
|
{
|
||
|
cdrom_playing = 0;
|
||
|
cdrom_paused = 0;
|
||
|
if (unitnum < 0)
|
||
|
return;
|
||
|
sys_command_pause (DF_IOCTL, unitnum, 0);
|
||
|
sys_command_stop (DF_IOCTL, unitnum);
|
||
|
sys_command_pause (DF_IOCTL, unitnum, 1);
|
||
|
}
|
||
|
|
||
|
static uae_u32 last_play_end;
|
||
|
static int cd_play_audio (uae_u32 startmsf, uae_u32 endmsf, int scan)
|
||
|
{
|
||
|
if (endmsf == 0xffffffff)
|
||
|
endmsf = last_play_end;
|
||
|
else
|
||
|
last_play_end = endmsf;
|
||
|
return sys_command_play (DF_IOCTL, unitnum,startmsf, endmsf, scan);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* read qcode */
|
||
|
static uae_u32 last_play_pos;
|
||
|
static int cd_qcode (uae_u8 *d)
|
||
|
{
|
||
|
const uae_u8 *buf, *s;
|
||
|
uae_u8 as;
|
||
|
|
||
|
if (d)
|
||
|
memset (d, 0, 11);
|
||
|
last_play_pos = 0;
|
||
|
buf = sys_command_qcode (DF_IOCTL, unitnum);
|
||
|
if (!buf)
|
||
|
return 0;
|
||
|
as = buf[1];
|
||
|
if (as != 0x11 && as != 0x12 && as != 0x13 && as != 0x15) /* audio status ok? */
|
||
|
return 0;
|
||
|
s = buf + 4;
|
||
|
last_play_pos = (s[9] << 16) | (s[10] << 8) | (s[11] << 0);
|
||
|
if (!d)
|
||
|
return 0;
|
||
|
/* ??? */
|
||
|
d[0] = 0;
|
||
|
/* CtlAdr */
|
||
|
d[1] = (s[1] >> 4) | (s[1] << 4);
|
||
|
/* Track */
|
||
|
d[2] = tobcd (s[2]);
|
||
|
/* Index */
|
||
|
d[3] = tobcd (s[3]);
|
||
|
/* TrackPos */
|
||
|
d[4] = tobcd (s[9]);
|
||
|
d[5] = tobcd (s[10]);
|
||
|
d[6] = tobcd (s[11]);
|
||
|
/* DiskPos */
|
||
|
d[7] = 0;
|
||
|
d[8] = tobcd (s[5]);
|
||
|
d[9] = tobcd (s[6]);
|
||
|
d[10] = tobcd (s[7]);
|
||
|
if (as == 0x15) {
|
||
|
/* Make sure end of disc position is passed.
|
||
|
*/
|
||
|
int lsn = msf2lsn ((s[5] << 16) | (s[6] << 8) | (s[7] << 0));
|
||
|
int msf = lsn2msf (cdrom_leadout);
|
||
|
if (lsn >= cdrom_leadout || cdrom_leadout - lsn < 10) {
|
||
|
d[8] = tobcd ((uae_u8)(msf >> 16));
|
||
|
d[9] = tobcd ((uae_u8)(msf >> 8));
|
||
|
d[10] = tobcd ((uae_u8)(msf >> 0));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* read toc */
|
||
|
static int cdrom_toc (void)
|
||
|
{
|
||
|
int i, j;
|
||
|
int datatrack = 0, secondtrack = 0;
|
||
|
uae_u8 *d;
|
||
|
const uae_u8 *buf, *s;
|
||
|
|
||
|
cdrom_toc_counter = -1;
|
||
|
cdrom_toc_entries = 0;
|
||
|
buf = sys_command_toc (DF_IOCTL, unitnum);
|
||
|
if (!buf)
|
||
|
return 1;
|
||
|
i = (buf[0] << 8) | (buf[1] << 0);
|
||
|
i -= 2;
|
||
|
i /= 11;
|
||
|
if (i > MAX_TOC_ENTRIES)
|
||
|
return -1;
|
||
|
memset (cdrom_toc_buffer, 0, MAX_TOC_ENTRIES * 13);
|
||
|
cdrom_data_end = -1;
|
||
|
for (j = 0; j < i; j++) {
|
||
|
s = buf + 4 + j * 11;
|
||
|
d = &cdrom_toc_buffer[j * 13];
|
||
|
d[1] = (s[1] >> 4) | (s[1] << 4);
|
||
|
d[3] = s[3] < 100 ? tobcd(s[3]) : s[3];
|
||
|
d[8] = tobcd (s[8]);
|
||
|
d[9] = tobcd (s[9]);
|
||
|
d[10] = tobcd (s[10]);
|
||
|
if (s[3] == 1 && (s[1] & 0x0f) == 0x04)
|
||
|
datatrack = 1;
|
||
|
if (s[3] == 2)
|
||
|
secondtrack = msf2lsn ((s[8] << 16) | (s[9] << 8) | (s[10] << 0));
|
||
|
if (s[3] == 0xa2)
|
||
|
cdrom_leadout = msf2lsn ((s[8] << 16) | (s[9] << 8) | (s[10] << 0));
|
||
|
}
|
||
|
if (datatrack) {
|
||
|
if (secondtrack)
|
||
|
cdrom_data_end = secondtrack;
|
||
|
else
|
||
|
cdrom_data_end = cdrom_leadout;
|
||
|
}
|
||
|
cdrom_toc_entries = i;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* open device */
|
||
|
static int sys_cddev_open (void)
|
||
|
{
|
||
|
int first = -1;
|
||
|
struct device_info di1, *di2;
|
||
|
int cd32unit = -1;
|
||
|
int audiounit = -1;
|
||
|
|
||
|
for (unitnum = 0; unitnum < MAX_TOTAL_DEVICES; unitnum++) {
|
||
|
if (sys_command_open (DF_IOCTL, unitnum)) {
|
||
|
di2 = sys_command_info (DF_IOCTL, unitnum, &di1);
|
||
|
if (di2 && di2->type == INQ_ROMD) {
|
||
|
write_log ("%s: ", di2->label);
|
||
|
if (first < 0)
|
||
|
first = unitnum;
|
||
|
if (!cdrom_toc ()) {
|
||
|
if (cdrom_data_end > 0) {
|
||
|
const uae_u8 *p = sys_command_read (DF_IOCTL, unitnum, 16);
|
||
|
if (p) {
|
||
|
if (!memcmp (p + 8, "CDTV", 4) || !memcmp (p + 8, "CD32", 4)) {
|
||
|
write_log ("CD32 or CDTV\n");
|
||
|
if (cd32unit < 0)
|
||
|
cd32unit = unitnum;
|
||
|
} else {
|
||
|
write_log ("non CD32/CDTV data CD\n");
|
||
|
}
|
||
|
} else {
|
||
|
write_log ("read error\n");
|
||
|
}
|
||
|
} else {
|
||
|
write_log ("Audio CD\n");
|
||
|
if (audiounit < 0)
|
||
|
audiounit = unitnum;
|
||
|
}
|
||
|
} else {
|
||
|
write_log ("can't read TOC\n");
|
||
|
}
|
||
|
}
|
||
|
sys_command_close (DF_IOCTL, unitnum);
|
||
|
}
|
||
|
}
|
||
|
unitnum = audiounit;
|
||
|
if (cd32unit >= 0)
|
||
|
unitnum = cd32unit;
|
||
|
if (unitnum < 0)
|
||
|
unitnum = first;
|
||
|
if (unitnum < 0)
|
||
|
return 1;
|
||
|
if (!sys_command_open (DF_IOCTL, unitnum))
|
||
|
write_log ("re-opening unit %d failed!\n", unitnum);
|
||
|
di2 = sys_command_info (DF_IOCTL, unitnum, &di1);
|
||
|
if (!di2) {
|
||
|
write_log ("unit %d info failed\n", unitnum);
|
||
|
sys_command_close (DF_IOCTL, unitnum);
|
||
|
return 1;
|
||
|
}
|
||
|
write_log ("using drive %s (unit %d, media %d)\n", di2->label, unitnum, di2->media_inserted);
|
||
|
/* make sure CD audio is not playing */
|
||
|
cdaudiostop ();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* close device */
|
||
|
static void sys_cddev_close (void)
|
||
|
{
|
||
|
cdaudiostop ();
|
||
|
sys_command_close (DF_IOCTL, unitnum);
|
||
|
}
|
||
|
|
||
|
static int command_lengths[] = { 1,2,1,1,12,2,1,1,4,1,-1,-1,-1,-1,-1 };
|
||
|
|
||
|
static void cdrom_return_data (int len)
|
||
|
{
|
||
|
uae_u32 cmd_buf = cdrom_address2;
|
||
|
int i;
|
||
|
uae_u8 checksum;
|
||
|
|
||
|
if (len <= 0) return;
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("OUT:");
|
||
|
#endif
|
||
|
checksum = 0xff;
|
||
|
for (i = 0; i < len; i++) {
|
||
|
checksum -= cdrom_result_buffer[i];
|
||
|
put_byte (cmd_buf + ((cdrom_result_complete + i) & 0xff), cdrom_result_buffer[i]);
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("%02.2X ", cdrom_result_buffer[i]);
|
||
|
#endif
|
||
|
}
|
||
|
put_byte (cmd_buf + ((cdrom_result_complete + len) & 0xff), checksum);
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("%02.2X\n", checksum);
|
||
|
#endif
|
||
|
cdrom_result_complete += len + 1;
|
||
|
cdrom_status1 |= CDSTATUS_DATA_AVAILABLE;
|
||
|
}
|
||
|
|
||
|
static int cdrom_command_something (void)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int cdrom_command_media_status (void)
|
||
|
{
|
||
|
struct device_info di;
|
||
|
|
||
|
cdrom_result_buffer[0] = 10;
|
||
|
cdrom_result_buffer[1] = sys_command_info (DF_IOCTL, unitnum, &di)->media_inserted ? 1 : 0;
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
/* check if cd drive door is open or closed */
|
||
|
static int cdrom_command_door_status (void)
|
||
|
{
|
||
|
struct device_info di;
|
||
|
if (unitnum >= 0 && !sys_command_info (DF_IOCTL, unitnum, &di)->media_inserted) {
|
||
|
cdrom_result_buffer[1] = 0x80;
|
||
|
cdrom_disk = 0;
|
||
|
} else {
|
||
|
cdrom_result_buffer[1] = 1;
|
||
|
cdrom_disk = 1;
|
||
|
}
|
||
|
if (unitnum >= 0)
|
||
|
cdrom_toc ();
|
||
|
cdrom_result_buffer[0] = cdrom_command;
|
||
|
return 20;
|
||
|
}
|
||
|
|
||
|
/* return one TOC entry */
|
||
|
static int cdrom_return_toc_entry (void)
|
||
|
{
|
||
|
cdrom_result_buffer[0] = 6;
|
||
|
if (cdrom_toc_entries == 0) {
|
||
|
cdrom_result_buffer[1] = CDS_ERROR;
|
||
|
return 15;
|
||
|
}
|
||
|
cdrom_result_buffer[1] = 0;
|
||
|
memcpy (cdrom_result_buffer + 2, cdrom_toc_buffer + cdrom_toc_counter * 13, 13);
|
||
|
cdrom_toc_counter++;
|
||
|
if (cdrom_toc_counter >= cdrom_toc_entries)
|
||
|
cdrom_toc_counter = 0;
|
||
|
return 15;
|
||
|
}
|
||
|
|
||
|
/* pause CD audio */
|
||
|
static int cdrom_command_pause (void)
|
||
|
{
|
||
|
cdrom_toc_counter = -1;
|
||
|
cdrom_result_buffer[0] = cdrom_command;
|
||
|
cdrom_result_buffer[1] = cdrom_playing ? CDS_PLAYING : 0;
|
||
|
if (!cdrom_playing)
|
||
|
return 2;
|
||
|
if (cdrom_paused)
|
||
|
return 2;
|
||
|
sys_command_pause (DF_IOCTL, unitnum,1);
|
||
|
cdrom_paused = 1;
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
/* unpause CD audio */
|
||
|
static int cdrom_command_unpause (void)
|
||
|
{
|
||
|
cdrom_result_buffer[0] = cdrom_command;
|
||
|
cdrom_result_buffer[1] = cdrom_playing ? CDS_PLAYING : 0;
|
||
|
if (!cdrom_paused)
|
||
|
return 2;
|
||
|
if (!cdrom_playing)
|
||
|
return 2;
|
||
|
cdrom_paused = 0;
|
||
|
sys_command_pause (DF_IOCTL, unitnum,0);
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
/* seek head/play CD audio/read data sectors */
|
||
|
static int cdrom_command_multi (void)
|
||
|
{
|
||
|
int seekpos = fromlongbcd (cdrom_command_buffer + 1);
|
||
|
int endpos = fromlongbcd (cdrom_command_buffer + 4);
|
||
|
|
||
|
if (cdrom_playing)
|
||
|
cdaudiostop ();
|
||
|
cdrom_speed = (cdrom_command_buffer[8] & 0x40) ? 2 : 1;
|
||
|
cdrom_result_buffer[0] = cdrom_command;
|
||
|
cdrom_result_buffer[1] = 0;
|
||
|
if (!cdrom_disk) {
|
||
|
cdrom_result_buffer[1] |= CDS_ERROR;
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
if (cdrom_command_buffer[7] == 0x80) { /* data read */
|
||
|
int cdrom_data_offset_end = msf2lsn (endpos);
|
||
|
cdrom_data_offset = msf2lsn (seekpos);
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("READ DATA FROM %06.6X (%d) TO %06.6X (%d) SPEED=%dx\n", seekpos, cdrom_data_offset, endpos, cdrom_data_offset_end, cdrom_speed);
|
||
|
#endif
|
||
|
cdrom_result_buffer[1] |= 0x02;
|
||
|
} else if (cdrom_command_buffer[10] & 4) { /* play audio */
|
||
|
int scan = 0;
|
||
|
if (cdrom_command_buffer[7] & 0x04)
|
||
|
scan = 1;
|
||
|
else if (cdrom_command_buffer[7] & 0x08)
|
||
|
scan = -1;
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("PLAY FROM %06.6X to %06.6X SCAN=%d\n", seekpos, endpos, scan);
|
||
|
#endif
|
||
|
if (!cd_play_audio (seekpos, endpos, 0)) {
|
||
|
cdrom_result_buffer[1] = CDS_ERROR;
|
||
|
} else {
|
||
|
cdrom_playing = 1;
|
||
|
cdrom_result_buffer[1] |= CDS_PLAYING;
|
||
|
}
|
||
|
} else {
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("SEEKTO %06.6X\n",seekpos);
|
||
|
#endif
|
||
|
if (seekpos < 150)
|
||
|
cdrom_toc_counter = 0;
|
||
|
else
|
||
|
cdrom_toc_counter = -1;
|
||
|
}
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
/* return subq entry */
|
||
|
static int cdrom_command_subq (void)
|
||
|
{
|
||
|
cdrom_result_buffer[0] = cdrom_command;
|
||
|
cdrom_result_buffer[1] = 0;
|
||
|
if (cd_qcode (cdrom_result_buffer + 2))
|
||
|
cdrom_result_buffer[1] = CDS_ERROR;
|
||
|
return 15;
|
||
|
}
|
||
|
|
||
|
static void cdrom_run_command (void)
|
||
|
{
|
||
|
uae_u32 cmd_buf = cdrom_address2 + 0x200;
|
||
|
int i, cmd_len;
|
||
|
uae_u8 checksum;
|
||
|
|
||
|
for (;;) {
|
||
|
if (cdrom_command_active)
|
||
|
return;
|
||
|
if (cdrom_command_offset_complete == cdrom_command_offset_todo)
|
||
|
return;
|
||
|
cdrom_command = get_byte (cmd_buf + cdrom_command_offset_complete);
|
||
|
if ((cdrom_command & 0xf0) == 0)
|
||
|
return;
|
||
|
cdrom_checksum_error = 0;
|
||
|
cmd_len = command_lengths[cdrom_command & 0x0f];
|
||
|
if (cmd_len < 0) {
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("unknown command\n");
|
||
|
#endif
|
||
|
cmd_len = 1;
|
||
|
}
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("IN:");
|
||
|
#endif
|
||
|
checksum = 0;
|
||
|
for (i = 0; i < cmd_len + 1; i++) {
|
||
|
cdrom_command_buffer[i] = get_byte (cmd_buf + ((cdrom_command_offset_complete + i) & 0xff));
|
||
|
checksum += cdrom_command_buffer[i];
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("%02.2X ", cdrom_command_buffer[i]);
|
||
|
#endif
|
||
|
}
|
||
|
if (checksum!=0xff) {
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log (" checksum error");
|
||
|
#endif
|
||
|
cdrom_checksum_error = 1;
|
||
|
}
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("\n");
|
||
|
#endif
|
||
|
cdrom_command_active = 1;
|
||
|
cdrom_command_length = cmd_len;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void cdrom_run_command_run (void)
|
||
|
{
|
||
|
int len;
|
||
|
|
||
|
cdrom_command_offset_complete = (cdrom_command_offset_complete + cdrom_command_length + 1) & 0xff;
|
||
|
memset (cdrom_result_buffer, 0, sizeof(cdrom_result_buffer));
|
||
|
switch (cdrom_command & 0x0f)
|
||
|
{
|
||
|
case 2:
|
||
|
len = cdrom_command_pause ();
|
||
|
break;
|
||
|
case 3:
|
||
|
len = cdrom_command_unpause ();
|
||
|
break;
|
||
|
case 4:
|
||
|
len = cdrom_command_multi ();
|
||
|
break;
|
||
|
case 5:
|
||
|
cdrom_dosomething = 1;
|
||
|
len = cdrom_command_something ();
|
||
|
break;
|
||
|
case 6:
|
||
|
len = cdrom_command_subq ();
|
||
|
break;
|
||
|
case 7:
|
||
|
len = cdrom_command_door_status ();
|
||
|
break;
|
||
|
default:
|
||
|
len = 0;
|
||
|
break;
|
||
|
}
|
||
|
if (len == 0)
|
||
|
return;
|
||
|
if (cdrom_checksum_error)
|
||
|
cdrom_result_buffer[1] |= 0x80;
|
||
|
cdrom_return_data (len);
|
||
|
}
|
||
|
|
||
|
static uae_sem_t akiko_sem;
|
||
|
|
||
|
/* DMA transfer one CD sector */
|
||
|
static void cdrom_run_read (void)
|
||
|
{
|
||
|
int i, j, sector;
|
||
|
int read = 0;
|
||
|
uae_u8 buf[2352];
|
||
|
int sec;
|
||
|
|
||
|
if (!(cdrom_longmask & 0x04000000))
|
||
|
return;
|
||
|
if (!cdrom_readmask_w)
|
||
|
return;
|
||
|
if (cdrom_data_offset<0)
|
||
|
return;
|
||
|
j = cdrom_sector_counter & 15;
|
||
|
if (unitnum >= 0 && cdrom_readmask_w & (1 << j)) {
|
||
|
uae_sem_wait (&akiko_sem);
|
||
|
sector = cdrom_current_sector = cdrom_data_offset + cdrom_sector_counter;
|
||
|
sec = sector - sector_buffer_sector_1;
|
||
|
if (sector_buffer_sector_1 >= 0 && sec >= 0 && sec < SECTOR_BUFFER_SIZE) {
|
||
|
if (sector_buffer_info_1[sec] != 0xff && sector_buffer_info_1[sec] != 0) {
|
||
|
memcpy (buf + 16, sector_buffer_1 + sec * 2048, 2048);
|
||
|
encode_l2 (buf, sector + 150);
|
||
|
buf[0] = 0;
|
||
|
buf[1] = 0;
|
||
|
buf[2] = 0;
|
||
|
buf[3] = cdrom_sector_counter;
|
||
|
for (i = 0; i < 2352; i++)
|
||
|
put_byte (cdrom_address1 + j * 4096 + i, buf[i]);
|
||
|
cdrom_readmask_r |= 1 << j;
|
||
|
}
|
||
|
if (sector_buffer_info_1[sec] != 0xff)
|
||
|
sector_buffer_info_1[sec]--;
|
||
|
} else {
|
||
|
uae_sem_post (&akiko_sem);
|
||
|
return;
|
||
|
}
|
||
|
uae_sem_post (&akiko_sem);
|
||
|
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log ("read sector=%d, scnt=%d -> %d\n", cdrom_data_offset, cdrom_sector_counter, sector);
|
||
|
#endif
|
||
|
cdrom_readmask_w &= ~(1 << j);
|
||
|
}
|
||
|
cdrom_sector_counter++;
|
||
|
if (cdrom_readmask_w == 0)
|
||
|
cdrom_status1 |= CDSTATUS_DATASECTOR;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void akiko_handler (void)
|
||
|
{
|
||
|
static int mediacheckcnt;
|
||
|
static int lastmediastate = -1;
|
||
|
struct device_info di;
|
||
|
|
||
|
if (unitnum < 0)
|
||
|
return;
|
||
|
if (cdrom_result_complete > cdrom_result_last_pos && cdrom_result_complete - cdrom_result_last_pos < 100) {
|
||
|
cdrom_status1 |= CDSTATUS_DATA_AVAILABLE;
|
||
|
return;
|
||
|
}
|
||
|
if (cdrom_result_last_pos < cdrom_result_complete)
|
||
|
return;
|
||
|
if (mediacheckcnt > 0)
|
||
|
mediacheckcnt--;
|
||
|
if (mediacheckcnt == 0) {
|
||
|
int media = sys_command_info (DF_IOCTL, unitnum, &di)->media_inserted;
|
||
|
mediacheckcnt = 312 * 50 * 2;
|
||
|
if (media != lastmediastate) {
|
||
|
write_log ("media changed = %d\n", media);
|
||
|
lastmediastate = cdrom_disk = media;
|
||
|
cdrom_return_data (cdrom_command_media_status ());
|
||
|
cdrom_toc ();
|
||
|
/* do not remove! first try may fail */
|
||
|
cdrom_toc ();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
if (cdrom_toc_counter >= 0 && !cdrom_command_active && cdrom_dosomething) {
|
||
|
cdrom_return_data (cdrom_return_toc_entry ());
|
||
|
cdrom_dosomething--;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void akiko_internal (void)
|
||
|
{
|
||
|
cdrom_run_command ();
|
||
|
if (cdrom_command_active > 0) {
|
||
|
cdrom_command_active--;
|
||
|
if (!cdrom_command_active)
|
||
|
cdrom_run_command_run ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extern int cd32_enabled;
|
||
|
|
||
|
void AKIKO_hsync_handler (void)
|
||
|
{
|
||
|
static int framecounter;
|
||
|
|
||
|
if (!cd32_enabled)
|
||
|
return;
|
||
|
framecounter--;
|
||
|
if (framecounter <= 0) {
|
||
|
if (cdrom_playing || cdrom_toc_counter > 0)
|
||
|
gui_cd_led (1);
|
||
|
cdrom_run_read ();
|
||
|
framecounter = 1000000 / (74 * 75 * cdrom_speed);
|
||
|
cdrom_status1 |= CDSTATUS_FRAME;
|
||
|
}
|
||
|
akiko_internal ();
|
||
|
akiko_handler ();
|
||
|
}
|
||
|
|
||
|
|
||
|
static volatile int akiko_thread_running;
|
||
|
|
||
|
/* cdrom data buffering thread */
|
||
|
static void *akiko_thread (void *null)
|
||
|
{
|
||
|
int i;
|
||
|
uae_u8 *tmp1;
|
||
|
uae_u8 *tmp2;
|
||
|
int tmp3;
|
||
|
int offset;
|
||
|
int sector;
|
||
|
|
||
|
sys_command_open_thread (DF_IOCTL, unitnum);
|
||
|
while(akiko_thread_running) {
|
||
|
uae_sem_wait (&akiko_sem);
|
||
|
sector = cdrom_current_sector;
|
||
|
for (i = 0; i < SECTOR_BUFFER_SIZE; i++) {
|
||
|
if (sector_buffer_info_1[i] == 0xff) break;
|
||
|
}
|
||
|
if (cdrom_data_end > 0 && sector >= 0 && (sector_buffer_sector_1 < 0 || sector < sector_buffer_sector_1 || sector >= sector_buffer_sector_1 + SECTOR_BUFFER_SIZE * 2 / 3 || i != SECTOR_BUFFER_SIZE)) {
|
||
|
|
||
|
uae_sem_post (&akiko_sem);
|
||
|
|
||
|
memset (sector_buffer_info_2, 0, SECTOR_BUFFER_SIZE);
|
||
|
#if AKIKO_DEBUG_IO_CMD
|
||
|
write_log("filling buffer sector=%d (max=%d)\n", sector, cdrom_data_end);
|
||
|
#endif
|
||
|
sector_buffer_sector_2 = sector;
|
||
|
offset = 0;
|
||
|
while (offset < SECTOR_BUFFER_SIZE) {
|
||
|
const uae_u8 *p = 0;
|
||
|
if (sector < cdrom_data_end)
|
||
|
p = sys_command_read (DF_IOCTL, unitnum, sector);
|
||
|
if (p)
|
||
|
memcpy (sector_buffer_2 + offset * 2048, p, 2048);
|
||
|
sector_buffer_info_2[offset] = p ? 3 : 0;
|
||
|
offset++;
|
||
|
sector++;
|
||
|
}
|
||
|
|
||
|
uae_sem_wait (&akiko_sem);
|
||
|
|
||
|
tmp1 = sector_buffer_info_1;
|
||
|
sector_buffer_info_1 = sector_buffer_info_2;
|
||
|
sector_buffer_info_2 = tmp1;
|
||
|
tmp2 = sector_buffer_1;
|
||
|
sector_buffer_1 = sector_buffer_2;
|
||
|
sector_buffer_2 = tmp2;
|
||
|
tmp3 = sector_buffer_sector_1;
|
||
|
sector_buffer_sector_1 = sector_buffer_sector_2;
|
||
|
sector_buffer_sector_2 = tmp3;
|
||
|
}
|
||
|
uae_sem_post (&akiko_sem);
|
||
|
uae_msleep (10);
|
||
|
}
|
||
|
sys_command_close_thread (DF_IOCTL, unitnum);
|
||
|
akiko_thread_running = -1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
STATIC_INLINE uae_u8 akiko_get_long (uae_u32 v, int offset)
|
||
|
{
|
||
|
return v >> ((3 - offset) * 8);
|
||
|
}
|
||
|
|
||
|
STATIC_INLINE void akiko_put_long (uae_u32 *p, int offset, int v)
|
||
|
{
|
||
|
*p &= ~(0xff << ((3 - offset) * 8));
|
||
|
*p |= v << ((3 - offset) * 8);
|
||
|
}
|
||
|
|
||
|
STATIC_INLINE uae_u32 akiko_bget2 (uaecptr addr, int msg)
|
||
|
{
|
||
|
uae_u8 v;
|
||
|
|
||
|
addr &= 0xffff;
|
||
|
|
||
|
switch (addr)
|
||
|
{
|
||
|
/* "CAFE" = Akiko identification.
|
||
|
* Kickstart ignores Akiko C2P if this ID isn't correct */
|
||
|
case 0x02:
|
||
|
v = 0xCA;
|
||
|
break;
|
||
|
case 0x03:
|
||
|
v = 0xFE;
|
||
|
break;
|
||
|
|
||
|
/* CDROM control */
|
||
|
case 0x04:
|
||
|
case 0x05:
|
||
|
case 0x06:
|
||
|
case 0x07:
|
||
|
v = akiko_get_long (cdrom_status1, addr - 0x04);
|
||
|
break;
|
||
|
case 0x08:
|
||
|
case 0x09:
|
||
|
case 0x0a:
|
||
|
case 0x0b:
|
||
|
v = akiko_get_long (cdrom_status2, addr - 0x08);
|
||
|
break;
|
||
|
case 0x10:
|
||
|
case 0x11:
|
||
|
case 0x12:
|
||
|
case 0x13:
|
||
|
v = akiko_get_long (cdrom_address1, addr - 0x10);
|
||
|
break;
|
||
|
case 0x14:
|
||
|
case 0x15:
|
||
|
case 0x16:
|
||
|
case 0x17:
|
||
|
v = akiko_get_long (cdrom_address2, addr - 0x14);
|
||
|
break;
|
||
|
case 0x18:
|
||
|
v = cdrom_status3;
|
||
|
break;
|
||
|
case 0x19:
|
||
|
v = cdrom_command_offset_complete;
|
||
|
break;
|
||
|
case 0x1a:
|
||
|
v = cdrom_result_complete;
|
||
|
break;
|
||
|
case 0x1f:
|
||
|
v = cdrom_result_last_pos;
|
||
|
break;
|
||
|
case 0x20:
|
||
|
case 0x21:
|
||
|
v = akiko_get_long (cdrom_readmask_r, addr - 0x20 + 2);
|
||
|
break;
|
||
|
case 0x24:
|
||
|
case 0x25:
|
||
|
case 0x26:
|
||
|
case 0x27:
|
||
|
v = akiko_get_long (cdrom_longmask, addr - 0x24);
|
||
|
break;
|
||
|
|
||
|
/* NVRAM */
|
||
|
case 0x30:
|
||
|
case 0x31:
|
||
|
case 0x32:
|
||
|
case 0x33:
|
||
|
v = akiko_nvram_read (addr - 0x30);
|
||
|
break;
|
||
|
|
||
|
/* C2P */
|
||
|
case 0x38:
|
||
|
case 0x39:
|
||
|
case 0x3a:
|
||
|
case 0x3b:
|
||
|
v = akiko_c2p_read (addr - 0x38);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
write_log ("akiko_bget: unknown address %08.8X\n", addr);
|
||
|
v = 0;
|
||
|
break;
|
||
|
}
|
||
|
akiko_internal ();
|
||
|
|
||
|
if (msg && addr < 0x30 && AKIKO_DEBUG_IO)
|
||
|
write_log ("akiko_bget %08.8X: %08.8X %02.2X\n", m68k_getpc (®s), addr, v & 0xff);
|
||
|
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
uae_u32 REGPARAM2 akiko_bget (uaecptr addr)
|
||
|
{
|
||
|
return akiko_bget2 (addr, 1);
|
||
|
}
|
||
|
|
||
|
uae_u32 REGPARAM2 akiko_wget (uaecptr addr)
|
||
|
{
|
||
|
uae_u16 v;
|
||
|
|
||
|
addr &= 0xffff;
|
||
|
v = akiko_bget2 (addr + 1, 0);
|
||
|
v |= akiko_bget2 (addr + 0, 0) << 8;
|
||
|
|
||
|
if (addr < 0x30 && AKIKO_DEBUG_IO)
|
||
|
write_log ("akiko_wget %08.8X: %08.8X %04.4X\n", m68k_getpc (®s), addr, v & 0xffff);
|
||
|
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
uae_u32 REGPARAM2 akiko_lget (uaecptr addr)
|
||
|
{
|
||
|
uae_u32 v;
|
||
|
|
||
|
addr &= 0xffff;
|
||
|
v = akiko_bget2 (addr + 3, 0);
|
||
|
v |= akiko_bget2 (addr + 2, 0) << 8;
|
||
|
v |= akiko_bget2 (addr + 1, 0) << 16;
|
||
|
v |= akiko_bget2 (addr + 0, 0) << 24;
|
||
|
|
||
|
if (addr < 0x30 && (addr != 4 && addr != 8) && AKIKO_DEBUG_IO)
|
||
|
write_log ("akiko_lget %08.8X: %08.8X %08.8X\n", m68k_getpc (®s), addr, v);
|
||
|
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
STATIC_INLINE void REGPARAM2 akiko_bput2 (uaecptr addr, uae_u32 v, int msg)
|
||
|
{
|
||
|
uae_u32 tmp;
|
||
|
|
||
|
addr &= 0xffff;
|
||
|
v &= 0xff;
|
||
|
if(msg && addr < 0x30 && AKIKO_DEBUG_IO)
|
||
|
write_log ("akiko_bput %08.8X: %08.8X=%02.2X\n", m68k_getpc (®s), addr, v & 0xff);
|
||
|
|
||
|
switch (addr)
|
||
|
{
|
||
|
case 0x04:
|
||
|
case 0x05:
|
||
|
case 0x06:
|
||
|
case 0x07:
|
||
|
akiko_put_long (&cdrom_status1, addr - 0x04, v);
|
||
|
break;
|
||
|
case 0x08:
|
||
|
case 0x09:
|
||
|
case 0x0a:
|
||
|
case 0x0b:
|
||
|
akiko_put_long (&cdrom_status2, addr - 0x08, v);
|
||
|
if (addr == 8)
|
||
|
cdrom_status1 &= cdrom_status2;
|
||
|
break;
|
||
|
case 0x10:
|
||
|
case 0x11:
|
||
|
case 0x12:
|
||
|
case 0x13:
|
||
|
akiko_put_long (&cdrom_address1, addr - 0x10, v);
|
||
|
break;
|
||
|
case 0x14:
|
||
|
case 0x15:
|
||
|
case 0x16:
|
||
|
case 0x17:
|
||
|
akiko_put_long (&cdrom_address2, addr - 0x14, v);
|
||
|
break;
|
||
|
case 0x18:
|
||
|
cdrom_status3 = v;
|
||
|
break;
|
||
|
case 0x19:
|
||
|
cdrom_command_offset_complete = v;
|
||
|
break;
|
||
|
case 0x1a:
|
||
|
cdrom_result_complete = v;
|
||
|
break;
|
||
|
case 0x1d:
|
||
|
cdrom_command_offset_todo = v;
|
||
|
break;
|
||
|
case 0x1f:
|
||
|
cdrom_result_last_pos = v;
|
||
|
break;
|
||
|
case 0x20:
|
||
|
cdrom_readmask_w |= (v << 8);
|
||
|
cdrom_readmask_r &= 0x00ff;
|
||
|
break;
|
||
|
case 0x21:
|
||
|
cdrom_readmask_w |= (v << 0);
|
||
|
cdrom_readmask_r &= 0xff00;
|
||
|
break;
|
||
|
case 0x24:
|
||
|
case 0x25:
|
||
|
case 0x26:
|
||
|
case 0x27:
|
||
|
tmp = cdrom_longmask;
|
||
|
akiko_put_long (&cdrom_longmask, addr - 0x24, v);
|
||
|
if ((cdrom_longmask & 0x04000000) && !(tmp & 0x04000000))
|
||
|
cdrom_sector_counter = 0;
|
||
|
break;
|
||
|
|
||
|
case 0x30:
|
||
|
case 0x31:
|
||
|
case 0x32:
|
||
|
case 0x33:
|
||
|
akiko_nvram_write (addr - 0x30, v);
|
||
|
break;
|
||
|
|
||
|
case 0x38:
|
||
|
case 0x39:
|
||
|
case 0x3a:
|
||
|
case 0x3b:
|
||
|
akiko_c2p_write (addr - 0x38, v);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
write_log ("akiko_bput: unknown address %08.8X\n", addr);
|
||
|
break;
|
||
|
}
|
||
|
akiko_internal ();
|
||
|
}
|
||
|
|
||
|
void REGPARAM2 akiko_bput (uaecptr addr, uae_u32 v)
|
||
|
{
|
||
|
akiko_bput2 (addr, v, 1);
|
||
|
}
|
||
|
|
||
|
void REGPARAM2 akiko_wput (uaecptr addr, uae_u32 v)
|
||
|
{
|
||
|
addr &= 0xfff;
|
||
|
|
||
|
if((addr < 0x30 && AKIKO_DEBUG_IO))
|
||
|
write_log("akiko_wput %08.8X: %08.8X=%04.4X\n", m68k_getpc (®s), addr, v & 0xffff);
|
||
|
akiko_bput2 (addr + 1, v & 0xff, 0);
|
||
|
akiko_bput2 (addr + 0, v >> 8, 0);
|
||
|
}
|
||
|
|
||
|
void REGPARAM2 akiko_lput (uaecptr addr, uae_u32 v)
|
||
|
{
|
||
|
addr &= 0xffff;
|
||
|
|
||
|
if(addr < 0x30 && AKIKO_DEBUG_IO)
|
||
|
write_log("akiko_lput %08.8X: %08.8X=%08.8X\n", m68k_getpc (®s), addr, v);
|
||
|
akiko_bput2 (addr + 3, (v >> 0) & 0xff, 0);
|
||
|
akiko_bput2 (addr + 2, (v >> 8) & 0xff, 0);
|
||
|
akiko_bput2 (addr + 1, (v >> 16) & 0xff, 0);
|
||
|
akiko_bput2 (addr + 0, (v >> 24) & 0xff, 0);
|
||
|
}
|
||
|
|
||
|
static uae_thread_id akiko_tid;
|
||
|
|
||
|
|
||
|
static int cdromok = 0;
|
||
|
|
||
|
void akiko_reset (void)
|
||
|
{
|
||
|
cdaudiostop ();
|
||
|
nvram_read ();
|
||
|
state = I2C_WAIT;
|
||
|
bitcounter = -1;
|
||
|
direction = -1;
|
||
|
|
||
|
cdrom_speed = 1;
|
||
|
cdrom_current_sector = -1;
|
||
|
|
||
|
if (akiko_thread_running > 0) {
|
||
|
akiko_thread_running = 0;
|
||
|
while(akiko_thread_running == 0)
|
||
|
uae_msleep (10);
|
||
|
akiko_thread_running = 0;
|
||
|
}
|
||
|
|
||
|
if (cdromok) {
|
||
|
if (unitnum >= 0)
|
||
|
sys_cddev_close ();
|
||
|
unitnum = -1;
|
||
|
free (sector_buffer_1);
|
||
|
free (sector_buffer_2);
|
||
|
free (sector_buffer_info_1);
|
||
|
free (sector_buffer_info_2);
|
||
|
sector_buffer_1 = 0;
|
||
|
sector_buffer_2 = 0;
|
||
|
sector_buffer_info_1 = 0;
|
||
|
sector_buffer_info_2 = 0;
|
||
|
cdromok = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extern uae_u8 *extendedkickmemory;
|
||
|
|
||
|
static const uae_u8 patchdata[] = {0x0c,0x82,0x00,0x00,0x03,0xe8,0x64,0x00,0x00,0x46};
|
||
|
|
||
|
static void patchrom (void)
|
||
|
{
|
||
|
static int patch_done = 0;
|
||
|
unsigned int i;
|
||
|
|
||
|
if (!patch_done) {
|
||
|
uae_u8 *p;
|
||
|
|
||
|
patch_done = 1;
|
||
|
|
||
|
p = (uae_u8*)extendedkickmemory;
|
||
|
|
||
|
for (i = 0; i < 524288 - sizeof (patchdata); i++) {
|
||
|
if (!memcmp (p + i, patchdata, sizeof(patchdata))) {
|
||
|
p[i + 6] = 0x4e;
|
||
|
p[i + 7] = 0x71;
|
||
|
p[i + 8] = 0x4e;
|
||
|
p[i + 9] = 0x71;
|
||
|
write_log ("Extended ROM delay loop patched at 0x%p\n", i + 6 + 0xe00000);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
write_log ("Couldn't patch extended ROM\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void akiko_free (void)
|
||
|
{
|
||
|
akiko_reset ();
|
||
|
}
|
||
|
|
||
|
int akiko_init (void)
|
||
|
{
|
||
|
if (cdromok == 0) {
|
||
|
unitnum = -1;
|
||
|
if (!device_func_init(DEVICE_TYPE_ANY)) {
|
||
|
write_log ("no CDROM support\n");
|
||
|
return 0;
|
||
|
}
|
||
|
if (!sys_cddev_open ()) {
|
||
|
cdromok = 1;
|
||
|
sector_buffer_1 = malloc (SECTOR_BUFFER_SIZE * 2048);
|
||
|
sector_buffer_2 = malloc (SECTOR_BUFFER_SIZE * 2048);
|
||
|
sector_buffer_info_1 = malloc (SECTOR_BUFFER_SIZE);
|
||
|
sector_buffer_info_2 = malloc (SECTOR_BUFFER_SIZE);
|
||
|
sector_buffer_sector_1 = -1;
|
||
|
sector_buffer_sector_2 = -1;
|
||
|
patchrom ();
|
||
|
}
|
||
|
}
|
||
|
if (!savestate_state) {
|
||
|
cdrom_playing = cdrom_paused = 0;
|
||
|
cdrom_data_offset = -1;
|
||
|
uae_sem_init (&akiko_sem, 0, 1);
|
||
|
}
|
||
|
if (cdromok && !akiko_thread_running) {
|
||
|
akiko_thread_running = 1;
|
||
|
uae_start_thread (akiko_thread, 0, &akiko_tid);
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef SAVESTATE
|
||
|
|
||
|
uae_u8 *save_akiko (uae_u32 *len)
|
||
|
{
|
||
|
uae_u8 *dstbak, *dst;
|
||
|
unsigned int i;
|
||
|
|
||
|
dstbak = dst = malloc (1000);
|
||
|
save_u16 (0);
|
||
|
save_u16 (0xCAFE);
|
||
|
save_u32 (cdrom_status1);
|
||
|
save_u32 (cdrom_status2);
|
||
|
save_u32 (0);
|
||
|
save_u32 (cdrom_address1);
|
||
|
save_u32 (cdrom_address2);
|
||
|
save_u8 (cdrom_status3);
|
||
|
save_u8 (cdrom_command_offset_complete);
|
||
|
save_u8 (cdrom_result_complete);
|
||
|
save_u8 (0);
|
||
|
save_u8 (0);
|
||
|
save_u8 (cdrom_command_offset_todo);
|
||
|
save_u8 (0);
|
||
|
save_u8 (cdrom_result_last_pos);
|
||
|
save_u16 ((uae_u16)cdrom_readmask_w);
|
||
|
save_u16 (0);
|
||
|
save_u32 (cdrom_longmask);
|
||
|
save_u32 (0);
|
||
|
save_u32 (0);
|
||
|
save_u32 ((scl_dir ? 0x8000 : 0) | (sda_dir ? 0x4000 : 0));
|
||
|
save_u32 (0);
|
||
|
save_u32 (0);
|
||
|
|
||
|
for (i = 0; i < 8; i++)
|
||
|
save_u32 (akiko_buffer[i]);
|
||
|
save_u8 ((uae_u8)akiko_read_offset);
|
||
|
save_u8 ((uae_u8)akiko_write_offset);
|
||
|
|
||
|
save_u32 ((cdrom_playing ? 1 : 0) | (cdrom_paused ? 2 : 0));
|
||
|
if (cdrom_playing)
|
||
|
cd_qcode (0);
|
||
|
save_u32 (last_play_pos);
|
||
|
save_u32 (last_play_end);
|
||
|
save_u8 ((uae_u8)cdrom_toc_counter);
|
||
|
|
||
|
*len = dst - dstbak;
|
||
|
return dstbak;
|
||
|
}
|
||
|
|
||
|
const uae_u8 *restore_akiko (const uae_u8 *src)
|
||
|
{
|
||
|
uae_u32 v;
|
||
|
unsigned int i;
|
||
|
|
||
|
restore_u16 ();
|
||
|
restore_u16 ();
|
||
|
cdrom_status1 = restore_u32 ();
|
||
|
cdrom_status2 = restore_u32 ();
|
||
|
restore_u32 ();
|
||
|
cdrom_address1 = restore_u32 ();
|
||
|
cdrom_address2 = restore_u32 ();
|
||
|
cdrom_status3 = restore_u8 ();
|
||
|
cdrom_command_offset_complete = restore_u8 ();
|
||
|
cdrom_result_complete = restore_u8 ();
|
||
|
restore_u8 ();
|
||
|
restore_u8 ();
|
||
|
cdrom_command_offset_todo = restore_u8 ();
|
||
|
restore_u8 ();
|
||
|
cdrom_result_last_pos = restore_u8 ();
|
||
|
cdrom_readmask_w = restore_u16 ();
|
||
|
restore_u16 ();
|
||
|
cdrom_longmask = restore_u32 ();
|
||
|
restore_u32 ();
|
||
|
restore_u32 ();
|
||
|
v = restore_u32 ();
|
||
|
scl_dir = (v & 0x8000) ? 1 : 0;
|
||
|
sda_dir = (v & 0x4000) ? 1 : 0;
|
||
|
restore_u32 ();
|
||
|
restore_u32 ();
|
||
|
|
||
|
for (i = 0; i < 8; i++)
|
||
|
akiko_buffer[i] = restore_u32 ();
|
||
|
akiko_read_offset = restore_u8 ();
|
||
|
akiko_write_offset = restore_u8 ();
|
||
|
akiko_c2p_do ();
|
||
|
|
||
|
cdrom_playing = cdrom_paused = 0;
|
||
|
v = restore_u32 ();
|
||
|
if (v & 1)
|
||
|
cdrom_playing = 1;
|
||
|
if (v & 2)
|
||
|
cdrom_paused = 1;
|
||
|
last_play_pos = restore_u32 ();
|
||
|
last_play_end = restore_u32 ();
|
||
|
cdrom_toc_counter = restore_u8 ();
|
||
|
if (cdrom_toc_counter == 255)
|
||
|
cdrom_toc_counter = -1;
|
||
|
if (cdrom_playing)
|
||
|
sys_command_play (DF_IOCTL, unitnum, last_play_pos, last_play_end, 0);
|
||
|
|
||
|
return src;
|
||
|
}
|
||
|
|
||
|
#endif /* SAVESTATE */
|
||
|
|
||
|
|
||
|
void akiko_entergui (void)
|
||
|
{
|
||
|
if (cdrom_playing)
|
||
|
sys_command_pause (DF_IOCTL, unitnum, 1);
|
||
|
}
|
||
|
void akiko_exitgui (void)
|
||
|
{
|
||
|
if (cdrom_playing)
|
||
|
sys_command_pause (DF_IOCTL, unitnum, 0);
|
||
|
}
|