agat: basic emulation of agat9 (video, apple compat mode, LLE floppy)

also included: MX floppy format (nw)
This commit is contained in:
Sergey Svishchev 2019-07-01 23:56:11 +03:00
parent 35af77fcfa
commit a152d1125f
21 changed files with 2709 additions and 185 deletions

View File

@ -2115,6 +2115,8 @@ if (BUSES["A2BUS"]~=null) then
MAME_DIR .. "src/devices/bus/a2bus/agat7ram.h",
MAME_DIR .. "src/devices/bus/a2bus/agat840k_hle.cpp",
MAME_DIR .. "src/devices/bus/a2bus/agat840k_hle.h",
MAME_DIR .. "src/devices/bus/a2bus/agat_fdc.cpp",
MAME_DIR .. "src/devices/bus/a2bus/agat_fdc.h",
MAME_DIR .. "src/devices/bus/a2bus/ssprite.cpp",
MAME_DIR .. "src/devices/bus/a2bus/ssprite.h",
MAME_DIR .. "src/devices/bus/a2bus/ssbapple.cpp",

View File

@ -197,6 +197,18 @@ if (FORMATS["AGAT840K_HLE_DSK"]~=null or _OPTIONS["with-tools"]) then
}
end
--------------------------------------------------
--
--@src/lib/formats/aim_dsk.h,FORMATS["AIM_DSK"] = true
--------------------------------------------------
if (FORMATS["AIM_DSK"]~=null or _OPTIONS["with-tools"]) then
files {
MAME_DIR.. "src/lib/formats/aim_dsk.cpp",
MAME_DIR.. "src/lib/formats/aim_dsk.h",
}
end
--------------------------------------------------
--
--@src/lib/formats/ami_dsk.h,FORMATS["AMI_DSK"] = true
@ -689,6 +701,18 @@ if (FORMATS["DMK_DSK"]~=null or _OPTIONS["with-tools"]) then
}
end
--------------------------------------------------
--
--@src/lib/formats/ds9_dsk.h,FORMATS["DS9_DSK"] = true
--------------------------------------------------
if (FORMATS["DS9_DSK"]~=null or _OPTIONS["with-tools"]) then
files {
MAME_DIR.. "src/lib/formats/ds9_dsk.cpp",
MAME_DIR.. "src/lib/formats/ds9_dsk.h",
}
end
--------------------------------------------------
--
--@src/lib/formats/sdf_dsk.h,FORMATS["SDF_DSK"] = true
@ -725,6 +749,18 @@ if (FORMATS["DMV_DSK"]~=null or _OPTIONS["with-tools"]) then
}
end
--------------------------------------------------
--
--@src/lib/formats/dvk_mx_dsk.h,FORMATS["DVK_MX_DSK"] = true
--------------------------------------------------
if (FORMATS["DVK_MX_DSK"]~=null or _OPTIONS["with-tools"]) then
files {
MAME_DIR.. "src/lib/formats/dvk_mx_dsk.cpp",
MAME_DIR.. "src/lib/formats/dvk_mx_dsk.h",
}
end
--------------------------------------------------
--
--@src/lib/formats/esq16_dsk.h,FORMATS["ESQ16_DSK"] = true

View File

@ -883,6 +883,7 @@ FORMATS["ADAM_CAS"] = true
FORMATS["ADAM_DSK"] = true
FORMATS["AFS_DSK"] = true
FORMATS["AGAT840K_HLE_DSK"] = true
FORMATS["AIM_DSK"] = true
FORMATS["AMI_DSK"] = true
FORMATS["AP2_DSK"] = true
FORMATS["APD_DSK"] = true
@ -925,6 +926,7 @@ FORMATS["DCP_DSK"] = true
FORMATS["DIM_DSK"] = true
FORMATS["DIP_DSK"] = true
FORMATS["DMK_DSK"] = true
FORMATS["DS9_DSK"] = true
FORMATS["SDF_DSK"] = true
FORMATS["EP64_DSK"] = true
FORMATS["DMV_DSK"] = true
@ -1523,6 +1525,8 @@ files {
MAME_DIR .. "src/mame/includes/apple2.h",
MAME_DIR .. "src/mame/video/agat7.cpp",
MAME_DIR .. "src/mame/video/agat7.h",
MAME_DIR .. "src/mame/video/agat9.cpp",
MAME_DIR .. "src/mame/video/agat9.h",
}
createMESSProjects(_target, _subtarget, "akai")

View File

@ -26,6 +26,7 @@
DEFINE_DEVICE_TYPE(A2BUS_DISKII, a2bus_diskii_device, "a2diskii", "Apple Disk II controller")
DEFINE_DEVICE_TYPE(A2BUS_IWM_FDC, a2bus_iwmflop_device, "a2iwm_flop", "Apple IWM floppy card")
DEFINE_DEVICE_TYPE(A2BUS_AGAT7_FDC, a2bus_agat7flop_device, "agat7_flop", "Agat-7 140K floppy card")
DEFINE_DEVICE_TYPE(A2BUS_AGAT9_FDC, a2bus_agat9flop_device, "agat9_flop", "Agat-9 140K floppy card")
#define DISKII_ROM_REGION "diskii_rom"
#define FDC_TAG "diskii_fdc"
@ -57,6 +58,11 @@ ROM_START( agat7 )
ROM_LOAD( "shugart7.rom", 0x0000, 0x0100, CRC(c6e4850c) SHA1(71626d3d2d4bbeeac2b77585b45a5566d20b8d34) )
ROM_END
ROM_START( agat9 )
ROM_REGION(0x100, DISKII_ROM_REGION, 0)
ROM_LOAD( "shugart9.rom", 0x0000, 0x0100, CRC(964a0ce2) SHA1(bf955189ebffe874c20ef649a3db8177dc16af61) )
ROM_END
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
@ -89,6 +95,11 @@ const tiny_rom_entry *a2bus_agat7flop_device::device_rom_region() const
return ROM_NAME( agat7 );
}
const tiny_rom_entry *a2bus_agat9flop_device::device_rom_region() const
{
return ROM_NAME( agat9 );
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
@ -115,6 +126,11 @@ a2bus_agat7flop_device::a2bus_agat7flop_device(const machine_config &mconfig, co
{
}
a2bus_agat9flop_device::a2bus_agat9flop_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
a2bus_floppy_device(mconfig, A2BUS_AGAT9_FDC, tag, owner, clock)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------

View File

@ -67,9 +67,18 @@ public:
virtual const tiny_rom_entry *device_rom_region() const override;
};
class a2bus_agat9flop_device: public a2bus_floppy_device
{
public:
a2bus_agat9flop_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
virtual const tiny_rom_entry *device_rom_region() const override;
};
// device type definition
DECLARE_DEVICE_TYPE(A2BUS_DISKII, a2bus_diskii_device)
DECLARE_DEVICE_TYPE(A2BUS_IWM_FDC, a2bus_iwmflop_device)
DECLARE_DEVICE_TYPE(A2BUS_AGAT7_FDC, a2bus_agat7flop_device)
DECLARE_DEVICE_TYPE(A2BUS_AGAT9_FDC, a2bus_agat9flop_device)
#endif // MAME_BUS_A2BUS_A2DISKII_H

View File

@ -326,24 +326,28 @@ uint8_t a2bus_agat840k_hle_device::read_cnxx(uint8_t offset)
legacy_floppy_image_device *a2bus_agat840k_hle_device::floppy_image(int drive)
{
const char *floppy_name = nullptr;
switch (drive)
{
case 0:
floppy_name = FLOPPY_0;
break;
case 1:
floppy_name = FLOPPY_1;
break;
switch(drive) {
case 0 : return subdevice<legacy_floppy_image_device>(FLOPPY_0);
case 1 : return subdevice<legacy_floppy_image_device>(FLOPPY_1);
}
return subdevice<legacy_floppy_image_device>(floppy_name);
return nullptr;
}
// all signals active low. write support not implemented; WPT is always active.
/*
* all signals active low. write support not implemented; WPT is always active.
*
* b0-b1 type of drive 2: 00 - ES 5323.01 "1000 KB", 01 - "500 KB", 10 - "250 KB", 11 - not present
* b2-b3 type of drive 1: -""-
* b4 INDEX/SECTOR
* b5 WRITE PROTECT
* b6 TRACK 0
* b7 READY
*
* C0x1
*/
READ8_MEMBER(a2bus_agat840k_hle_device::d14_i_b)
{
u8 data = 0x03; // one drive present, because drive select is broken
u8 data = 0x3;
m_floppy->floppy_drive_set_ready_state(FLOPPY_DRIVE_READY, 1);
@ -373,7 +377,6 @@ READ8_MEMBER(a2bus_agat840k_hle_device::d14_i_b)
*/
WRITE8_MEMBER(a2bus_agat840k_hle_device::d14_o_c)
{
// drive select is broken in legacy flopdrv.cpp -- floppy_get_drive
m_unit = BIT(data, 3);
m_floppy = floppy_image(m_unit);
if (m_unit)
@ -402,6 +405,8 @@ WRITE8_MEMBER(a2bus_agat840k_hle_device::d14_o_c)
data, m_unit, m_side, !BIT(data, 2), !BIT(data, 6), !BIT(data, 7));
}
// C0x4
//
// data are latched in by write to PC4
READ8_MEMBER(a2bus_agat840k_hle_device::d15_i_a)
{

View File

@ -0,0 +1,488 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/*********************************************************************
agat_fdc.c
Implementation of the Agat 840K floppy controller card
Technical manual:
http://agatcomp.ru/Reading/serkov/hainfo/023-01to.shtml
http://agatcomp.ru/Reading/serkov/hainfo/023-01to1.shtml
Schematic:
http://agatcomp.ru/Reading/fl800k/FD840/TEAC_023-adj-HI.jpg
On-disk format:
https://github.com/sintech/AGAT/blob/master/docs/agat-840k-format.txt
http://www.torlus.com/floppy/forum/viewtopic.php?f=19&t=1385
*********************************************************************/
#include "emu.h"
#include "agat_fdc.h"
#include "formats/aim_dsk.h"
#include "formats/ds9_dsk.h"
#define LOG_LSS (1U << 1) // Show warnings
#define LOG_SHIFT (1U << 2) // Shows shift register contents
//#define VERBOSE (LOG_GENERAL | LOG_SHIFT | LOG_LSS)
//#define VERBOSE (LOG_GENERAL)
#include "logmacro.h"
#define LOGLSS(...) LOGMASKED(LOG_LSS, __VA_ARGS__)
#define LOGSHIFT(...) LOGMASKED(LOG_SHIFT, __VA_ARGS__)
/***************************************************************************
PARAMETERS
***************************************************************************/
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
DEFINE_DEVICE_TYPE(A2BUS_AGAT_FDC, a2bus_agat_fdc_device, "agat_fdc", "Agat 840K floppy card")
#define AGAT_FDC_ROM_REGION "agat_fdc_rom"
#define AGAT_FDC_ROM_D6_REGION "agat_fdc_d6_rom"
ROM_START( agat9 )
ROM_REGION(0x100, AGAT_FDC_ROM_REGION, 0)
// zagorsk
ROM_LOAD( "teac.rom", 0x0000, 0x0100, CRC(94266928) SHA1(5d369bad6cdd6a70b0bb16480eba69640de87a2e) )
ROM_REGION(0x200, AGAT_FDC_ROM_D6_REGION, 0)
ROM_LOAD( "d6encdec.bin", 0x0000, 0x0200, CRC(66e7e896) SHA1(b5305e82c81240a6fdc932a559b5493c59f302c6) )
ROM_END
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
FLOPPY_FORMATS_MEMBER( a2bus_agat_fdc_device::floppy_formats )
FLOPPY_DS9_FORMAT,
FLOPPY_AIM_FORMAT
FLOPPY_FORMATS_END
static void agat_floppies(device_slot_interface &device)
{
device.option_add("525dsqd", FLOPPY_525_QD);
}
void a2bus_agat_fdc_device::device_add_mconfig (machine_config &config)
{
FLOPPY_CONNECTOR(config, floppy0, agat_floppies, "525dsqd", a2bus_agat_fdc_device::floppy_formats);
FLOPPY_CONNECTOR(config, floppy1, agat_floppies, "525dsqd", a2bus_agat_fdc_device::floppy_formats);
I8255(config, m_d14);
// PA not connected
m_d14->in_pb_callback().set(FUNC(a2bus_agat_fdc_device::d14_i_b)); // status signals from drive
m_d14->out_pc_callback().set(FUNC(a2bus_agat_fdc_device::d14_o_c)); // control
I8255(config, m_d15);
m_d15->in_pa_callback().set(FUNC(a2bus_agat_fdc_device::d15_i_a)); // read data
// m_d15->out_pb_callback().set(FUNC(a2bus_agat_fdc_device::d15_o_b)); // write data
m_d15->in_pc_callback().set(FUNC(a2bus_agat_fdc_device::d15_i_c));
m_d15->out_pc_callback().set(FUNC(a2bus_agat_fdc_device::d15_o_c));
}
//-------------------------------------------------
// rom_region - device-specific ROM region
//-------------------------------------------------
const tiny_rom_entry *a2bus_agat_fdc_device::device_rom_region() const
{
return ROM_NAME( agat9 );
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
a2bus_agat_fdc_device::a2bus_agat_fdc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock)
, device_a2bus_card_interface(mconfig, *this)
, m_d14(*this, "d14")
, m_d15(*this, "d15")
, floppy0(*this, "0")
, floppy1(*this, "1")
, m_rom(nullptr)
, m_rom_d6(nullptr)
{
}
a2bus_agat_fdc_device::a2bus_agat_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
a2bus_agat_fdc_device(mconfig, A2BUS_AGAT_FDC, tag, owner, clock)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void a2bus_agat_fdc_device::device_start()
{
set_unscaled_clock((XTAL(14'300'000) / 14.0) * 4.0);
m_rom = device().machine().root_device().memregion(this->subtag(AGAT_FDC_ROM_REGION).c_str())->base();
m_rom_d6 = device().machine().root_device().memregion(this->subtag(AGAT_FDC_ROM_D6_REGION).c_str())->base();
floppy = nullptr;
if (floppy0)
{
floppy = floppy0->get_device();
}
m_mxcs = MXCSR_SYNC;
m_timer_lss = timer_alloc(TIMER_ID_LSS);
m_timer_motor = timer_alloc(TIMER_ID_MOTOR);
m_seektime = 6; // ms, per es5323.txt
m_waittime = 32; // us - 16 bits x 2 us
}
void a2bus_agat_fdc_device::device_reset()
{
active = 0;
cycles = time_to_cycles(machine().time());
data_reg = 0x00;
address = 0xff;
m_mxcs |= MXCSR_SYNC;
m_mxcs &= ~MXCSR_TR;
// Just a timer to be sure that the lss is updated from time to
// time, so that there's no hiccup when it's talked to again.
m_timer_lss->adjust(attotime::from_msec(10), 0, attotime::from_msec(10));
}
void a2bus_agat_fdc_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
switch (id)
{
case TIMER_ID_LSS:
lss_sync();
break;
case TIMER_ID_MOTOR:
active = 0;
floppy->mon_w(1);
break;
}
}
uint64_t a2bus_agat_fdc_device::time_to_cycles(const attotime &tm)
{
// Clock is falling edges of the ~4Mhz clock
uint64_t cycles = tm.as_ticks(clock()*2);
cycles = (cycles+1) >> 1;
return cycles;
}
attotime a2bus_agat_fdc_device::cycles_to_time(uint64_t cycles)
{
return attotime::from_ticks(cycles*2+1, clock()*2);
}
void a2bus_agat_fdc_device::lss_start()
{
cycles = time_to_cycles(machine().time()) + 1;
data_reg = 0x00;
address = 0xff;
bits = 8;
}
void a2bus_agat_fdc_device::lss_sync()
{
if(!active)
return;
attotime next_flux = floppy ? floppy->get_next_transition(cycles_to_time(cycles-1)) : attotime::never;
uint64_t cycles_limit = time_to_cycles(machine().time());
uint64_t cycles_next_flux = next_flux != attotime::never ? time_to_cycles(next_flux) : uint64_t(-1);
uint64_t cycles_next_flux_down = cycles_next_flux != uint64_t(-1) ? cycles_next_flux+1 : uint64_t(-1);
LOGLSS("LSS at %11.6f: %d (limit %d next %d) in %02x\n",
machine().time().as_double(), cycles, cycles_limit, cycles_next_flux, address);
if(cycles >= cycles_next_flux && cycles < cycles_next_flux_down)
address &= ~0x40;
else
address |= 0x40;
while (cycles < cycles_limit) {
uint64_t cycles_next_trans = cycles_limit;
if(cycles_next_trans > cycles_next_flux && cycles < cycles_next_flux)
{
cycles_next_trans = cycles_next_flux;
LOGLSS("lss: next_trans up (%d < %d; %d)\n", cycles, cycles_next_flux, cycles_limit);
}
if(cycles_next_trans > cycles_next_flux_down && cycles < cycles_next_flux_down)
{
cycles_next_trans = cycles_next_flux_down;
LOGLSS("lss: next_trans down (%d < %d; %d)\n", cycles, cycles_next_flux_down, cycles_limit);
}
while (cycles < cycles_next_trans) {
uint8_t opcode = m_rom_d6[address];
if (cycles_next_flux != uint64_t(-1))
{
LOGLSS("lss: %d (limit %d next %d) in %03x out %02x (addr %02x end %d bit %d sync %d)\n",
cycles, cycles_limit, cycles_next_flux, address, opcode,
opcode & 0x3f, BIT(opcode, 6), BIT(opcode, 5), BIT(opcode, 7));
}
if (!BIT(opcode, 6)) // end bit
{
data_reg <<= 1;
data_reg |= !BIT(opcode, 5);
LOGSHIFT("lss shift: %d (to %02x, %2d bits) at %d%s\n", BIT(opcode, 5), data_reg, bits, cycles,
BIT(opcode, 7) ? " (sync)":"");
if (BIT(opcode, 7) == BIT(opcode, 5)) // hack
{
bits++;
}
else
{
m_mxcs &= ~MXCSR_SYNC;
bits = 0;
}
if (bits == 16)
{
address |= 0x80;
m_d15->pc4_w(0);
m_d15->pc4_w(1);
bits = 8;
LOGSHIFT("lss data: %02x\n", data_reg);
}
else
{
address &= ~0x80;
}
}
address &= ~0x3f;
address |= (opcode & 0x3f);
cycles++;
}
if(cycles == cycles_next_flux)
address &= ~0x40;
else if(cycles == cycles_next_flux_down) {
address |= 0x40;
next_flux = floppy ? floppy->get_next_transition(cycles_to_time(cycles)) : attotime::never;
if (next_flux != attotime::never) {
cycles_next_flux = time_to_cycles(next_flux);
LOGLSS("lss next: %d cycles\n", cycles_next_flux+1-cycles_next_flux_down);
cycles_next_flux_down = cycles_next_flux+1;
} else {
cycles_next_flux = uint64_t(-1);
cycles_next_flux_down = uint64_t(-1);
}
}
}
}
/*-------------------------------------------------
read_c0nx - called for reads from this card's c0nx space
-------------------------------------------------*/
uint8_t a2bus_agat_fdc_device::read_c0nx(uint8_t offset)
{
u8 data;
lss_sync();
switch (offset)
{
case 0: case 1: case 2: case 3:
data = m_d14->read(offset);
break;
case 4: case 5: case 6: case 7:
data = m_d15->read(offset - 4);
break;
default:
data = 0xff;
break;
}
return data;
}
/*-------------------------------------------------
write_c0nx - called for writes to this card's c0nx space
-------------------------------------------------*/
void a2bus_agat_fdc_device::write_c0nx(uint8_t offset, uint8_t data)
{
lss_sync();
switch (offset)
{
case 0: case 1: case 2: case 3:
m_d14->write(offset, data);
break;
case 4: case 5: case 6: case 7:
m_d15->write(offset - 4, data);
break;
case 8: // D9.15 - write desync
break;
case 9: // D9.14 - step
LOG("step at %11.6f\n", machine().time().as_double());
if (floppy && active)
{
floppy->stp_w(1);
floppy->stp_w(0);
}
break;
case 10: // D9.13 - reset desync flipflop
m_mxcs |= MXCSR_SYNC;
break;
default:
break;
}
}
/*-------------------------------------------------
read_cnxx - called for reads from this card's c0nx space
-------------------------------------------------*/
uint8_t a2bus_agat_fdc_device::read_cnxx(uint8_t offset)
{
return m_rom[offset];
}
/*
* all signals active low. write support not implemented; WPT is always active.
*
* b0-b1 type of drive 2: 00 - ES 5323.01 "1000 KB", 01 - "500 KB", 10 - "250 KB", 11 - not present
* b2-b3 type of drive 1: -""-
* b4 INDEX/SECTOR
* b5 WRITE PROTECT
* b6 TRACK 0
* b7 READY
*
* C0x1
*/
READ8_MEMBER(a2bus_agat_fdc_device::d14_i_b)
{
u8 data = 0x3;
// all signals active low
if (floppy)
{
data |= (floppy->idx_r() << 4) ^ 0x10;
// data |= floppy->wpt_r() << 5;
data |= floppy->trk00_r() << 6;
data |= floppy->ready_r() << 7;
}
else
{
data |= 0xf0;
}
LOG("status A: %s %s (t %d) %s %s\n", BIT(data, 7) ? "ready" : "READY", BIT(data, 6) ? "tk00" : "TK00",
floppy ? floppy->get_cyl() : -1, BIT(data, 5) ? "wpt" : "WPT", BIT(data, 4) ? "index" : "INDEX");
return data;
}
/*
* b0 AH strong write precomp
* b1 -- NC
* b2 -- step direction (1 - inward, 0 - outward)
* b3 -- drive select (0 - drive 1, 1 - drive 2)
* b4 -- head select (0 - bottom, 1 - top)
* b5 AH write precomp off
* b6 AH write enable
* b7 AH motor on
*
* C0x2
*/
WRITE8_MEMBER(a2bus_agat_fdc_device::d14_o_c)
{
m_unit = BIT(data, 3);
if (floppy)
{
floppy->dir_w(!BIT(data, 2));
floppy->ss_w(BIT(data, 4));
// floppy->wtg_w(!BIT(data, 6));
// floppy->mon_w(!BIT(data, 7)); // tied to 'drive select', 'motor on' and 'head load'
}
if (BIT(data, 7))
{
m_d15->pc4_w(0);
m_d15->pc4_w(1);
floppy->mon_w(0);
if (!active)
{
active = 1;
lss_start();
}
}
#if 0
else
{
m_timer_motor->adjust(attotime::from_msec(1000));
}
#endif
LOG("D14 C <- %02X (unit %d side %d drtn %d wtg %d mon %d)\n",
data, m_unit, BIT(data, 4), !BIT(data, 2), !BIT(data, 6), !BIT(data, 7));
}
// data are latched in by write to PC4
READ8_MEMBER(a2bus_agat_fdc_device::d15_i_a)
{
return data_reg;
}
// C0x6
//
// b6 AL desync detected
// b7 AH read or write data ready
READ8_MEMBER(a2bus_agat_fdc_device::d15_i_c)
{
LOG("status B: @ %4d %s %s\n", 0,
BIT(m_mxcs, 7) ? "ready" : "READY", BIT(m_mxcs, 6) ? "SYNC" : "sync");
return m_mxcs;
}
// C0x7
//
// b0 -- connected to b7, set if m_intr[PORT_B]
// b2 AH b7 = ready for write data
// b3 -- connected to b7, set if m_intr[PORT_A]
// b4 AH b7 = read data ready
WRITE8_MEMBER(a2bus_agat_fdc_device::d15_o_c)
{
if (BIT(data, 0) || BIT(data, 3))
{
m_mxcs |= MXCSR_TR;
}
else
{
m_mxcs &= ~MXCSR_TR;
}
}

View File

@ -0,0 +1,105 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/*********************************************************************
agat_fdc.h
Implementation of the Agat 840K floppy controller card
*********************************************************************/
#ifndef MAME_BUS_A2BUS_AGAT_FDC_H
#define MAME_BUS_A2BUS_AGAT_FDC_H
#pragma once
#include "a2bus.h"
#include "imagedev/floppy.h"
#include "machine/i8255.h"
#define MXCSR_SYNC 0x40
#define MXCSR_TR 0x80
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
class a2bus_agat_fdc_device:
public device_t,
public device_a2bus_card_interface
{
public:
// construction/destruction
a2bus_agat_fdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8_MEMBER(d14_i_b);
DECLARE_READ8_MEMBER(d15_i_a);
DECLARE_READ8_MEMBER(d15_i_c);
DECLARE_WRITE8_MEMBER(d14_o_c);
DECLARE_WRITE8_MEMBER(d15_o_b);
DECLARE_WRITE8_MEMBER(d15_o_c);
DECLARE_FLOPPY_FORMATS(floppy_formats);
protected:
// construction/destruction
a2bus_agat_fdc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
virtual void device_add_mconfig(machine_config &config) override;
virtual const tiny_rom_entry *device_rom_region() const override;
// overrides of standard a2bus slot functions
virtual uint8_t read_c0nx(uint8_t offset) override;
virtual void write_c0nx(uint8_t offset, uint8_t data) override;
virtual uint8_t read_cnxx(uint8_t offset) override;
enum
{
TIMER_ID_LSS = 0,
TIMER_ID_SEEK,
TIMER_ID_MOTOR
};
required_device<i8255_device> m_d14;
required_device<i8255_device> m_d15;
private:
required_device<floppy_connector> floppy0;
required_device<floppy_connector> floppy1;
uint64_t time_to_cycles(const attotime &tm);
attotime cycles_to_time(uint64_t cycles);
void lss_start();
void lss_sync();
floppy_image_device *floppy;
int active, bits;
uint8_t data_reg;
uint16_t address;
uint64_t cycles;
u8 m_mxcs;
int m_unit;
int m_state;
int m_seektime;
int m_waittime;
emu_timer *m_timer_lss;
emu_timer *m_timer_seek;
emu_timer *m_timer_motor;
uint8_t *m_rom;
uint8_t *m_rom_d6;
};
// device type definition
DECLARE_DEVICE_TYPE(A2BUS_AGAT_FDC, a2bus_agat_fdc_device)
#endif // MAME_BUS_A2BUS_AGAT_FDC_H

108
src/lib/formats/aim_dsk.cpp Normal file
View File

@ -0,0 +1,108 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/*********************************************************************
formats/aim_dsk.h
AIM disk images
References:
- http://www.torlus.com/floppy/forum/viewtopic.php?f=19&t=1385
- http://agatcomp.ru/Soft/agat.shtml
*********************************************************************/
#include <assert.h>
#include "aim_dsk.h"
aim_format::aim_format()
{
}
const char *aim_format::name() const
{
return "aim";
}
const char *aim_format::description() const
{
return "AIM disk image";
}
const char *aim_format::extensions() const
{
return "aim";
}
int aim_format::identify(io_generic *io, uint32_t form_factor)
{
if (io_generic_size(io) == 2068480)
{
return 100;
}
return 0;
}
bool aim_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
image->set_variant(floppy_image::DSQD);
const int tracks = 80;
const int track_size = 6464 * 2;
const int heads = 2;
for (int track = 0; track < tracks; track++)
{
for (int head = 0; head < heads; head++)
{
std::vector<uint8_t> track_data(track_size);
std::vector<uint32_t> raw_track_data;
int data_count = 0;
bool header = false;
// Read track
io_generic_read(io, &track_data[0], ( heads * track + head ) * track_size, track_size);
for (int offset = 0; offset < track_size; offset += 2)
{
switch (track_data[offset + 1] & 1)
{
case 0:
if (data_count == 0)
header = (track_data[offset] == 0x95) ? true : false;
data_count++;
mfm_w(raw_track_data, 8, track_data[offset]);
break;
case 1:
if (header && data_count < 11) // XXX hack
{
for (; data_count < 12; data_count++)
{
mfm_w(raw_track_data, 8, 0xaa);
}
}
raw_w(raw_track_data, 16, 0x8924);
raw_w(raw_track_data, 16, 0x5555);
data_count = 0;
break;
}
}
generate_track_from_levels(track, head, raw_track_data, 0, image);
}
}
return true;
}
const floppy_format_type FLOPPY_AIM_FORMAT = &floppy_image_format_creator<aim_format>;

34
src/lib/formats/aim_dsk.h Normal file
View File

@ -0,0 +1,34 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/*********************************************************************
formats/aim_dsk.h
AIM disk images
*********************************************************************/
#ifndef AIM_DSK_H
#define AIM_DSK_H
#include "flopimg.h"
/**************************************************************************/
class aim_format : public floppy_image_format_t
{
public:
aim_format();
virtual int identify(io_generic *io, uint32_t form_factor) override;
virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
virtual const char *name() const override;
virtual const char *description() const override;
virtual const char *extensions() const override;
virtual bool supports_save() const override { return false; }
};
extern const floppy_format_type FLOPPY_AIM_FORMAT;
#endif /* AIM_DSK_H */

156
src/lib/formats/ds9_dsk.cpp Normal file
View File

@ -0,0 +1,156 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/**********************************************************************
formats/ds9_dsk.cpp
Floppies used by Agat-9 840KB controller
http://agatcomp.ru/Reading/docs/es5323.txt
https://github.com/sintech/AGAT/blob/master/docs/agat-840k-format.txt
http://www.torlus.com/floppy/forum/viewtopic.php?f=19&t=1385
************************************************************************/
#include <assert.h>
#include "formats/ds9_dsk.h"
static FLOPPY_IDENTIFY(ds9_dsk_identify)
{
switch (floppy_image_size(floppy))
{
case (80 * 2 * 21 * 256):
case 860164:
case 860288:
*vote = 100;
break;
default:
*vote = 0;
break;
}
return FLOPPY_ERROR_SUCCESS;
}
static FLOPPY_CONSTRUCT(ds9_dsk_construct)
{
struct basicdsk_geometry geometry;
memset(&geometry, 0, sizeof(geometry));
geometry.heads = 2;
geometry.first_sector_id = 0;
geometry.sector_length = 256;
geometry.tracks = 80;
geometry.sectors = 21;
return basicdsk_construct(floppy, &geometry);
}
LEGACY_FLOPPY_OPTIONS_START( ds9 )
LEGACY_FLOPPY_OPTION( ds9_dsk, "ds9,dsk,raw", "Agat 840K DSK image",
ds9_dsk_identify, ds9_dsk_construct, nullptr, nullptr)
LEGACY_FLOPPY_OPTIONS_END
// exactly 6500 bytes
const floppy_image_format_t::desc_e ds9_format::ds9_desc[] = {
/* 01 */ { MFM, 0xaa, 32 }, // GAP1
/* 02 */ { SECTOR_LOOP_START, 0, 20 }, // 21 sectors
/* 03 */ { RAWBITS, 0x8924, 16 }, // sync mark: xA4, 2 us zero level interval, 0xFF
/* 04 */ { RAWBITS, 0x5555, 16 },
/* 05 */ { MFM, 0x95, 1 }, // address field prologue
/* 06 */ { MFM, 0x6a, 1 },
/* 07 */ { MFM, 0xfe, 1 }, // volume number
/* 08 */ { OFFSET_ID },
/* 09 */ { SECTOR_ID },
/* 10 */ { MFM, 0x5a, 1 }, // address field epilogue
/* 11 */ { MFM, 0xaa, 5 }, // GAP2 (min 4 bytes)
/* 12 */ { RAWBITS, 0x8924, 16 }, // sync mark
/* 13 */ { RAWBITS, 0x5555, 16 },
/* 14 */ { MFM, 0x6a, 1 }, // data field prologue
/* 15 */ { MFM, 0x95, 1 },
/* 16 */ { SECTOR_DATA_DS9, -1 },
/* 17 */ { MFM, 0x5a, 1 }, // data field epilogue
/* 18 */ { MFM, 0xaa, 33 }, // GAP3
/* 19 */ { SECTOR_LOOP_END },
/* 20 */ { END }
};
ds9_format::ds9_format()
{
}
const char *ds9_format::name() const
{
return "a9dsk";
}
const char *ds9_format::description() const
{
return "Agat-9 840K floppy image";
}
const char *ds9_format::extensions() const
{
return "ds9";
}
void ds9_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
{
uint32_t expected_size = 0;
uint64_t size = io_generic_size(io);
head_count = 2;
track_count = 80;
sector_count = 21;
expected_size = 256 * track_count * head_count * sector_count;
if (size >= expected_size) // standard format has 860160 bytes
return;
track_count = head_count = sector_count = 0;
}
int ds9_format::identify(io_generic *io, uint32_t form_factor)
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
if (track_count) return 50;
return 0;
}
bool ds9_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
if (track_count == 0) return false;
uint8_t sectdata[21 * 256];
desc_s sectors[21];
for (int i = 0; i < sector_count; i++)
{
sectors[i].data = sectdata + 256 * i;
sectors[i].size = 256;
sectors[i].sector_id = i;
}
int track_size = sector_count * 256;
for (int track = 0; track < track_count; track++)
{
for (int head = 0; head < head_count; head++)
{
io_generic_read(io, sectdata, (track * head_count + head) * track_size, track_size);
generate_track(ds9_desc, track, head, sectors, sector_count, 104000, image);
}
}
image->set_variant(floppy_image::DSQD);
return true;
}
const floppy_format_type FLOPPY_DS9_FORMAT = &floppy_image_format_creator<ds9_format>;

38
src/lib/formats/ds9_dsk.h Normal file
View File

@ -0,0 +1,38 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/*********************************************************************
formats/ds9_dsk.h
*********************************************************************/
#ifndef DS9_DSK_H_
#define DS9_DSK_H_
#include "flopimg.h"
#include "formats/basicdsk.h"
LEGACY_FLOPPY_OPTIONS_EXTERN(ds9);
class ds9_format : public floppy_image_format_t
{
public:
ds9_format();
virtual int identify(io_generic *io, uint32_t form_factor) override;
virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
virtual const char *name() const override;
virtual const char *description() const override;
virtual const char *extensions() const override;
virtual bool supports_save() const override { return false; }
static const desc_e ds9_desc[];
private:
void find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
};
extern const floppy_format_type FLOPPY_DS9_FORMAT;
#endif /* DS9_DSK_H_ */

View File

@ -0,0 +1,177 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/**********************************************************************
formats/dvk_mx_dsk.cpp
Floppies used by DVK MX: controller
http://torlus.com/floppy/forum/viewtopic.php?f=19&t=1384
Track format is almost entirely driver-dependent (only sync word
0x00f3 is mandatory), because hardware always reads or writes
entire track. 'old' format is used by stock driver, 'new' --
by 3rd party one. Formatting tools produce yet other variants...
Floppy drives were 40- and 80-track, double-sided. 'new' driver
also supports single-sided floppies.
************************************************************************/
#include <assert.h>
#include "flopimg.h"
#include "formats/dvk_mx_dsk.h"
const floppy_image_format_t::desc_e dvk_mx_format::dvk_mx_new_desc[] = {
/* 01 */ { FM, 0x00, 8*2 }, // eight 0x0000 words
/* 03 */ { FM, 0x00, 1 },
/* 02 */ { FM, 0xf3, 1 }, // word 0x00f3
/* 05 */ { FM, 0x00, 1 },
/* 04 */ { TRACK_ID_FM }, // track number word
/* 05 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors
/* 07 */ { SECTOR_DATA_MX, -1 },
/* 10 */ { SECTOR_LOOP_END },
/* 13 */ { FM, 0x83, 1 },
/* 12 */ { OFFSET_ID_FM },
/* 15 */ { FM, 0x83, 1 },
/* 14 */ { OFFSET_ID_FM },
/* 17 */ { FM, 0x83, 1 },
/* 16 */ { OFFSET_ID_FM },
/* 18 */ { END }
};
const floppy_image_format_t::desc_e dvk_mx_format::dvk_mx_old_desc[] = {
/* 01 */ { FM, 0x00, 30*2 },
/* 03 */ { FM, 0x00, 1 },
/* 02 */ { FM, 0xf3, 1 }, // word 0x00f3
/* 05 */ { FM, 0x00, 1 },
/* 04 */ { TRACK_ID_FM }, // track number word
/* 06 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors
/* 07 */ { SECTOR_DATA_MX, -1 },
/* 10 */ { SECTOR_LOOP_END },
/* 13 */ { FM, 0x83, 1 },
/* 11 */ { FM, 0x01, 1 },
/* 15 */ { FM, 0x83, 1 },
/* 14 */ { FM, 0x01, 1 },
/* 16 */ { END }
};
dvk_mx_format::dvk_mx_format()
{
}
const char *dvk_mx_format::name() const
{
return "mx";
}
const char *dvk_mx_format::description() const
{
return "DVK MX: floppy image";
}
const char *dvk_mx_format::extensions() const
{
return "mx";
}
bool dvk_mx_format::supports_save() const
{
return false;
}
void dvk_mx_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
{
uint64_t size = io_generic_size(io);
switch (size)
{
case 112640:
track_count = 40;
sector_count = 11;
head_count = 1;
break;
case 225280:
track_count = 40;
sector_count = 11;
head_count = 2;
break;
case 450560:
track_count = 80;
sector_count = 11;
head_count = 2;
break;
default:
track_count = head_count = sector_count = 0;
break;
}
}
int dvk_mx_format::identify(io_generic *io, uint32_t form_factor)
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
if (track_count)
{
uint8_t sectdata[512];
io_generic_read(io, sectdata, 512, 512);
// check value in RT-11 home block. see src/tools/imgtool/modules/rt11.cpp
if (pick_integer_le(sectdata, 0724, 2) == 6)
return 100;
else
return 75;
}
return 0;
}
bool dvk_mx_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
if (track_count == 0) return false;
uint8_t sectdata[11 * 256];
desc_s sectors[11];
for (int i = 0; i < sector_count; i++)
{
sectors[i].data = sectdata + 256 * i;
sectors[i].size = 256;
sectors[i].sector_id = i;
}
int track_size = sector_count * 256;
for (int track = 0; track < track_count; track++)
{
for (int head = 0; head < head_count; head++)
{
io_generic_read(io, sectdata, (track * head_count + head) * track_size, track_size);
generate_track(dvk_mx_new_desc, track, head, sectors, sector_count, 45824, image);
}
}
if (head_count == 1)
{
image->set_variant(floppy_image::SSDD);
}
else
{
if (track_count > 40)
{
image->set_variant(floppy_image::DSQD);
}
else
{
image->set_variant(floppy_image::DSDD);
}
}
return true;
}
const floppy_format_type FLOPPY_DVK_MX_FORMAT = &floppy_image_format_creator<dvk_mx_format>;

View File

@ -0,0 +1,39 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/*********************************************************************
formats/dvk_mx_dsk.h
*********************************************************************/
#ifndef DVK_MX_DSK_H_
#define DVK_MX_DSK_H_
#pragma once
#include "flopimg.h"
#include "imageutl.h"
class dvk_mx_format : public floppy_image_format_t
{
public:
dvk_mx_format();
virtual int identify(io_generic *io, uint32_t form_factor) override;
virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
virtual const char *name() const override;
virtual const char *description() const override;
virtual const char *extensions() const override;
virtual bool supports_save() const override;
static const desc_e dvk_mx_old_desc[];
static const desc_e dvk_mx_new_desc[];
private:
void find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
};
extern const floppy_format_type FLOPPY_DVK_MX_FORMAT;
#endif /* DVK_MX_DSK_H_ */

View File

@ -1038,6 +1038,7 @@ bool floppy_image_format_t::type_data_mfm(int type, int p1, const gen_crc_info *
type == SIZE_ID ||
type == OFFSET_ID_O ||
type == OFFSET_ID_E ||
type == OFFSET_ID_FM ||
type == SECTOR_ID_O ||
type == SECTOR_ID_E ||
type == REMAIN_O ||
@ -1521,6 +1522,14 @@ void floppy_image_format_t::generate_track(const desc_e *desc, int track, int he
mfm_half_w(buffer, 6, track*2+head);
break;
case OFFSET_ID_FM:
fm_w(buffer, 8, track*2+head);
break;
case OFFSET_ID:
mfm_w(buffer, 8, track*2+head);
break;
case SECTOR_ID_O:
mfm_half_w(buffer, 7, sector_idx);
break;
@ -1650,6 +1659,37 @@ void floppy_image_format_t::generate_track(const desc_e *desc, int track, int he
break;
}
case SECTOR_DATA_MX: {
const desc_s *csect = sect + (desc[index].p1 >= 0 ? desc[index].p1 : sector_idx);
uint16_t cksum = 0, data;
for(int i=0; i < csect->size; i+=2)
{
data = csect->data[i+1];
fm_w(buffer, 8, data);
data = (data << 8) | csect->data[i];
fm_w(buffer, 8, csect->data[i]);
cksum += data;
}
fm_w(buffer, 16, cksum);
break;
}
case SECTOR_DATA_DS9: {
const desc_s *csect = sect + (desc[index].p1 >= 0 ? desc[index].p1 : sector_idx);
uint8_t data;
int cksum = 0;
for(int i=0; i != csect->size; i++)
{
if (cksum > 255) { cksum++; cksum &= 255; }
data = csect->data[i];
mfm_w(buffer, 8, data);
cksum += data;
}
cksum &= 255;
mfm_w(buffer, 8, cksum);
break;
}
default:
printf("%d.%d.%d (%d) unhandled\n", desc[index].type, desc[index].p1, desc[index].p2, index);
break;

View File

@ -333,6 +333,8 @@ protected:
SECTOR_INFO_GCR6, //!< Sector info byte, gcr6-encoded
OFFSET_ID_O, //!< Offset (track*2+head) byte, odd bits, mfm-encoded
OFFSET_ID_E, //!< Offset (track*2+head) byte, even bits, mfm-encoded
OFFSET_ID_FM, //!< Offset (track*2+head) byte, fm-encoded
OFFSET_ID, //!< Offset (track*2+head) byte, mfm-encoded
SECTOR_ID_O, //!< Sector id byte, odd bits, mfm-encoded
SECTOR_ID_E, //!< Sector id byte, even bits, mfm-encoded
REMAIN_O, //!< Remaining sector count, odd bits, mfm-encoded, total sector count in p1
@ -345,6 +347,8 @@ protected:
SECTOR_DATA_GCR5, //!< Sector data to gcr5-encode, which in p1, -1 for the current one per the sector id
SECTOR_DATA_MAC, //!< Transformed sector data + checksum, mac style, id in p1, -1 for the current one per the sector id
SECTOR_DATA_8N1, //!< Sector data to 8N1-encode, which in p1, -1 for the current one per the sector id
SECTOR_DATA_MX, //!< Sector data to MX-encode, which in p1, -1 for the current one per the sector id
SECTOR_DATA_DS9, //!< Sector data to DS9-encode, which in p1, -1 for the current one per the sector id
CRC_CCITT_START, //!< Start a CCITT CRC calculation, with the usual x^16 + x^12 + x^5 + 1 (11021) polynomial, p1 = crc id
CRC_CCITT_FM_START, //!< Start a CCITT CRC calculation, with the usual x^16 + x^12 + x^5 + 1 (11021) polynomial, p1 = crc id

File diff suppressed because it is too large Load Diff

View File

@ -94,7 +94,9 @@ void agat7video_device::device_reset()
READ8_MEMBER(agat7video_device::read)
{
do_io(offset);
if(!machine().side_effects_disabled())
do_io(offset);
return 0;
}
@ -110,7 +112,7 @@ void agat7video_device::do_io(int offset)
{
case 0:
m_video_mode = GRAPHICS_LORES;
m_start_address = (offset) << 9;
m_start_address = (offset & 0x30) << 9;
logerror("offset %04X, video mode 0 (GRAPHICS_LORES)\n", m_start_address);
break;
@ -134,7 +136,7 @@ void agat7video_device::do_io(int offset)
case 3:
m_video_mode = GRAPHICS_MONO;
m_start_address = ((offset & 0x3f) - 0x03) << 9;
m_start_address = ((offset - 0x03) & 0x30) << 9;
logerror("offset %04X, video mode 3 (GRAPHICS_MONO)\n", m_start_address);
break;
}
@ -184,14 +186,12 @@ void agat7video_device::text_update_lores(screen_device &screen, bitmap_ind16 &b
address = m_start_address + (col * 2) + (row * 8);
ch = m_ram_dev->read(address);
attr = m_ram_dev->read(address + 1);
fg = bitswap<8>(attr,7,6,5,3,4,2,1,0) & 15;
if (BIT(attr, 5)) {
fg = bitswap<8>(attr,7,6,5,3,4,2,1,0) & 15;
bg = 0;
plot_text_character(bitmap, col * 16, row, 2, ch, m_char_ptr, m_char_size, fg, bg);
} else {
fg = 0;
bg = bitswap<8>(attr,7,6,5,3,4,2,1,0) & 15;
plot_text_character(bitmap, col * 16, row, 2, ch, m_char_ptr, m_char_size, bg, fg);
}
plot_text_character(bitmap, col * 16, row, 2, ch, m_char_ptr, m_char_size, fg, bg);
}
}
}
@ -352,25 +352,3 @@ uint32_t agat7video_device::screen_update(screen_device &screen, bitmap_ind16 &b
return 0;
}
#if 0
static const rgb_t agat7_palette[] =
{
rgb_t::black(),
rgb_t(0xFF, 0x00, 0x00), /* White */
rgb_t(0x00, 0xFF, 0x00), /* White */
rgb_t(0xFF, 0xFF, 0x00), /* White */
rgb_t(0x00, 0x00, 0xFF), /* White */
rgb_t(0xFF, 0x00, 0xFF), /* White */
rgb_t(0xFF, 0xFF, 0x00), /* White */
rgb_t(0xFF, 0xFF, 0xFF), /* White */
rgb_t::black(),
rgb_t(0x7F, 0x00, 0x00), /* White */
rgb_t(0x00, 0x7F, 0x00), /* White */
rgb_t(0x7F, 0x7F, 0x00), /* White */
rgb_t(0x00, 0x00, 0x7F), /* White */
rgb_t(0x7F, 0x00, 0x7F), /* White */
rgb_t(0x7F, 0x7F, 0x00), /* White */
rgb_t(0x7F, 0x7F, 0x7F) /* White */
};
#endif

715
src/mame/video/agat9.cpp Normal file
View File

@ -0,0 +1,715 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/*********************************************************************
agat9video.cpp
Implementation of Agat-9 onboard video.
6 native video modes:
- 32x32 color text
- 64x32 mono text with reverse video
- 128x128 16 color graphics
- 256x256 4 color graphics
- 256x256 and 512x256 mono graphics
2 apple video modes: 40col text and HGR.
C7xx: video mode select
*********************************************************************/
#include "emu.h"
#include "video/agat9.h"
#include "screen.h"
#define BLACK 0
#define RED 1
#define GREEN 2
#define YELLOW 3
#define BLUE 4
#define PURPLE 5
#define CYAN 6
#define WHITE 7
/***************************************************************************
PARAMETERS
***************************************************************************/
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
DEFINE_DEVICE_TYPE(AGAT9VIDEO, agat9video_device, "agat9video", "Agat-9 Video")
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
void agat9video_device::device_add_mconfig(machine_config &config)
{
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
m_screen->set_raw(XTAL(10'500'000), 672, 0, 512, 312, 0, 256);
m_screen->set_screen_update(FUNC(agat9video_device::screen_update));
m_screen->set_palette(DEVICE_SELF);
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
agat9video_device::agat9video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, AGAT9VIDEO, tag, owner, clock),
device_palette_interface(mconfig, *this),
m_screen(*this, "a9screen"),
m_ram_dev(*this, finder_base::DUMMY_TAG),
m_char_region(*this, finder_base::DUMMY_TAG),
m_char_ptr(nullptr),
m_char_size(0),
m_start_address(0)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void agat9video_device::device_start()
{
int i, j;
uint16_t c;
static const uint8_t hires_artifact_color_table[] =
{
BLACK, PURPLE, GREEN, WHITE,
BLACK, BLUE, RED, WHITE
};
m_char_ptr = m_char_region->base();
m_char_size = m_char_region->bytes();
/* 2^3 dependent pixels * 2 color sets * 2 offsets */
m_hires_artifact_map = std::make_unique<uint16_t[]>(8 * 2 * 2);
/* build hires artifact map */
for (i = 0; i < 8; i++)
{
for (j = 0; j < 2; j++)
{
if (i & 0x02)
{
if ((i & 0x05) != 0)
c = 3;
else
c = j ? 2 : 1;
}
else
{
c = 0;
}
m_hires_artifact_map[0 + j * 8 + i] = hires_artifact_color_table[(c + 0) % 8];
m_hires_artifact_map[16 + j * 8 + i] = hires_artifact_color_table[(c + 4) % 8];
}
}
// per http://agatcomp.ru/Reading/IiO/87-2-077.djvu
for (int i = 0; 8 > i; ++i)
{
set_pen_color(i + 0, rgb_t(BIT(i, 0) ? 0xff : 0, BIT(i, 1) ? 0xff : 0, BIT(i, 2) ? 0xff : 0));
set_pen_color(i + 8, rgb_t(BIT(i, 0) ? 0x7f : 0, BIT(i, 1) ? 0x7f : 0, BIT(i, 2) ? 0x7f : 0));
}
save_item(NAME(m_start_address));
save_item(NAME(m_page2));
save_item(NAME(m_flash));
save_item(NAME(m_mix));
save_item(NAME(m_graphics));
}
void agat9video_device::device_reset()
{
// XXX to be confirmed
m_video_mode = TEXT_LORES;
m_start_address = 0x7800;
m_mode = palette_index = 0;
// apple
m_page2 = false;
m_flash = false;
m_mix = false;
m_graphics = false;
}
READ8_MEMBER(agat9video_device::read)
{
if(!machine().side_effects_disabled())
do_io(offset);
// XXX only 'Moscow' revision
return m_mode;
}
WRITE8_MEMBER(agat9video_device::write)
{
do_io(offset);
}
READ8_MEMBER(agat9video_device::apple_read)
{
logerror("%s: %04x read (%s)\n", machine().describe_context(), 0xc050 + offset, offset<8?"apple":"palette");
if(!machine().side_effects_disabled())
do_apple_io(offset);
// XXX
return m_mode;
}
WRITE8_MEMBER(agat9video_device::apple_write)
{
logerror("%s: %04x write (%s)\n", machine().describe_context(), 0xc050 + offset, offset<8?"apple":"palette");
do_apple_io(offset);
}
void agat9video_device::do_apple_io(int offset)
{
if (offset < 0x8)
{
m_video_mode = APPLE;
m_screen->set_visible_area(0, 280-1, 0, 192-1);
}
switch (offset)
{
case 0x0:
m_graphics = true;
break;
case 0x1:
m_graphics = false;
break;
case 0x2:
m_mix = false;
break;
case 0x3:
m_mix = true;
break;
case 0x4:
m_page2 = false;
break;
case 0x5:
m_page2 = true;
break;
case 0x8:
palette_index &= 0xfe;
break;
case 0x9:
palette_index |= 0x01;
break;
case 0xa:
palette_index &= 0xfd;
break;
case 0xb:
palette_index |= 0x02;
break;
}
}
void agat9video_device::do_io(int offset)
{
logerror("%s: %04x access, ", machine().describe_context(), 0xc700 + offset);
m_mode = offset;
m_screen->set_visible_area(0, 2*256-1, 0, 256-1);
switch (offset & 3)
{
case 0:
// 256x256, 4 colors, 64 bytes per scanline, 0x4000 page length, odd scanlines at 0x2000
m_video_mode = GRAPHICS_COLOR_HIRES;
m_start_address = ((offset & 0x60) << 9) + ((offset & 0x08) << 13);
logerror("offset %04X, video mode 0 (COLOR_HIRES)\n", m_start_address);
break;
case 1:
// 128x128, 16 colors, 64 bytes per scanline, 0x2000 page length, linear
m_video_mode = GRAPHICS_COLOR_LORES;
m_start_address = ((offset & 0x70) << 9) + ((offset & 0x08) << 13);
logerror("offset %04X, video mode 1 (COLOR_LORES)\n", m_start_address);
break;
// b6..b4 == page number, b3..b2 == subpage number
case 2:
// 64x32, 0x1000 page length
if (offset > 0x80)
{
m_video_mode = TEXT_HIRES;
m_start_address = (offset - 0x82) << 9;
logerror("offset %04X, video mode 2 (TEXT_HIRES)\n", m_start_address);
}
// 32x32, 0x1000 page length
else
{
m_video_mode = TEXT_LORES;
m_start_address = (offset - 0x02) << 9;
logerror("offset %04X, video mode 2 (TEXT_LORES)\n", m_start_address);
}
break;
case 3:
// 512x256, 64 bytes per scanline, 0x4000 page length, odd scanlines at 0x2000
if (offset > 0x80)
{
m_video_mode = GRAPHICS_MONO_HIRES;
m_start_address = ((offset & 0x60) << 9) + ((offset & 0x08) << 13);
logerror("offset %04X, video mode 3 (MONO_HIRES)\n", m_start_address);
}
// 256x256, 32 bytes per scanline, 0x2000 page length, linear
else
{
m_video_mode = GRAPHICS_MONO_LORES;
m_start_address = ((offset & 0x70) << 9) + ((offset & 0x08) << 13);
logerror("offset %04X, video mode 3 (MONO_LORES)\n", m_start_address);
}
break;
}
}
void agat9video_device::plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code,
const uint8_t *textgfx_data, uint32_t textgfx_datalen, int fg, int bg)
{
int x, y, i;
const uint8_t *chardata;
uint16_t color;
/* look up the character data */
chardata = &textgfx_data[(code * 8)];
for (y = 0; y < 8; y++)
{
for (x = 0; x < 8; x++)
{
color = (chardata[y] & (1 << (7 - x))) ? fg : bg;
for (i = 0; i < xscale; i++)
{
bitmap.pix16(ypos + y, xpos + (x * xscale) + i) = color;
}
}
}
}
int color_1_p[4] =
{
0, 4, 0, 5
};
int color_2_p[4][2] =
{
{ 0, 7 },
{ 7, 0 },
{ 0, 2 },
{ 2, 0 },
};
void agat9video_device::text_update_lores(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col;
uint32_t address;
uint8_t ch, attr;
int fg = 0;
int bg = color_1_p[palette_index];
beginrow = std::max(beginrow, cliprect.min_y - (cliprect.min_y % 8));
endrow = std::min(endrow, cliprect.max_y - (cliprect.max_y % 8) + 7);
for (row = beginrow; row <= endrow; row += 8)
{
for (col = 0; col < 32; col++)
{
/* calculate address */
address = m_start_address + (col * 2) + (row * 8);
ch = m_ram_dev->read(address);
attr = m_ram_dev->read(address + 1);
fg = bitswap<8>(attr,7,6,5,3,4,2,1,0) & 15;
if (BIT(attr, 5)) {
plot_text_character(bitmap, col * 16, row, 2, ch, m_char_ptr, m_char_size, fg, bg);
} else {
plot_text_character(bitmap, col * 16, row, 2, ch, m_char_ptr, m_char_size, bg, fg);
}
}
}
}
void agat9video_device::text_update_hires(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col;
uint32_t address;
uint8_t ch;
int fg, bg;
beginrow = std::max(beginrow, cliprect.min_y - (cliprect.min_y % 8));
endrow = std::min(endrow, cliprect.max_y - (cliprect.max_y % 8) + 7);
if (m_start_address & 0x800)
{
fg = color_2_p[palette_index][1];
bg = color_2_p[palette_index][0];
}
else
{
fg = color_2_p[palette_index][0];
bg = color_2_p[palette_index][1];
}
for (row = beginrow; row <= endrow; row += 8)
{
for (col = 0; col < 64; col++)
{
/* calculate address */
address = m_start_address + col + (row * 8);
ch = m_ram_dev->read(address);
plot_text_character(bitmap, col * 8, row, 1, ch, m_char_ptr, m_char_size, fg, bg);
}
}
}
void agat9video_device::graph_update_mono_lores(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col, b;
uint32_t address;
uint16_t *p;
uint8_t gfx, v;
int fg = 7, bg = 0;
beginrow = std::max(beginrow, cliprect.min_y - (cliprect.min_y % 8));
endrow = std::min(endrow, cliprect.max_y - (cliprect.max_y % 8) + 7);
for (row = beginrow; row <= endrow; row++)
{
p = &bitmap.pix16(row);
for (col = 0; col < 32; col++)
{
address = m_start_address + col + (row * 0x20);
gfx = m_ram_dev->read(address);
for (b = 0; b < 8; b++)
{
v = (gfx & 0x80) >> 7;
v = color_2_p[palette_index][v];
gfx <<= 1;
*(p++) = v ? fg : bg;
*(p++) = v ? fg : bg;
}
}
}
}
void agat9video_device::graph_update_mono_hires(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col, b;
uint32_t address;
uint16_t *p;
uint8_t gfx, v;
int fg = 7, bg = 0;
beginrow = std::max(beginrow, cliprect.min_y - (cliprect.min_y % 8));
endrow = std::min(endrow, cliprect.max_y - (cliprect.max_y % 8) + 7);
for (row = beginrow; row <= endrow; row++)
{
p = &bitmap.pix16(row);
for (col = 0; col < 64; col++)
{
address = m_start_address + col + ((row / 2) * 0x40) + 0x2000 * (row & 1);
gfx = m_ram_dev->read(address);
for (b = 0; b < 8; b++)
{
v = (gfx & 0x80) >> 7;
v = color_2_p[palette_index][v];
gfx <<= 1;
*(p++) = v ? fg : bg;
}
}
}
}
int color_4_p[4][4] =
{
{ 0, 1, 2, 4 },
{ 7, 1, 2, 4 },
{ 0, 0, 2, 4 },
{ 0, 1, 0, 4 }
};
void agat9video_device::graph_update_color_hires(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col, b;
uint32_t address;
uint16_t *p;
uint8_t gfx, v;
beginrow = std::max(beginrow, cliprect.min_y - (cliprect.min_y % 8));
endrow = std::min(endrow, cliprect.max_y - (cliprect.max_y % 8) + 7);
for (row = beginrow; row <= endrow; row++)
{
p = &bitmap.pix16(row);
for (col = 0; col < 0x40; col++)
{
address = m_start_address + col + ((row / 2) * 0x40) + 0x2000 * (row & 1);
gfx = m_ram_dev->read(address);
for (b = 0; b < 4; b++)
{
v = (gfx & 0xc0) >> 6;
v = color_4_p[palette_index][v];
gfx <<= 2;
*(p++) = v;
*(p++) = v;
}
}
}
}
void agat9video_device::graph_update_color_lores(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col, b;
uint32_t address;
uint16_t *p;
uint8_t gfx, v;
beginrow = std::max(beginrow, cliprect.min_y - (cliprect.min_y % 8));
endrow = std::min(endrow, cliprect.max_y - (cliprect.max_y % 8) + 7);
for (row = beginrow; row <= endrow; row++)
{
p = &bitmap.pix16(row);
for (col = 0; col < 0x40; col++)
{
address = m_start_address + col + ((row / 2) * 0x40);
gfx = m_ram_dev->read(address);
for (b = 0; b < 2; b++)
{
v = (gfx & 0xf0) >> 4;
gfx <<= 4;
*(p++) = v;
*(p++) = v;
*(p++) = v;
*(p++) = v;
}
}
}
}
// Apple ][ video modes
void agat9video_device::plot_text_character_apple(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code,
const uint8_t *textgfx_data, uint32_t textgfx_datalen, int fg, int bg)
{
int x, y, i;
const uint8_t *chardata;
uint16_t color;
if ((code >= 0x40) && (code <= 0x7f))
{
code &= 0x3f;
if (m_flash)
{
i = fg;
fg = bg;
bg = i;
}
}
/* look up the character data */
chardata = &textgfx_data[(code * 8)];
for (y = 0; y < 8; y++)
{
for (x = 0; x < 7; x++)
{
color = (chardata[y] & (1 << (6 - x))) ? fg : bg;
for (i = 0; i < xscale; i++)
{
bitmap.pix16(ypos + y, xpos + (x * xscale) + i) = color;
}
}
}
}
void agat9video_device::text_update_apple(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col;
uint32_t start_address;
uint32_t address;
int fg, bg;
start_address = m_page2 ? 0x800 : 0x400;
fg = color_2_p[palette_index][1];
bg = color_2_p[palette_index][0];
beginrow = std::max(beginrow, cliprect.min_y - (cliprect.min_y % 8));
endrow = std::min(endrow, cliprect.max_y - (cliprect.max_y % 8) + 7);
for (row = beginrow; row <= endrow; row += 8)
{
for (col = 0; col < 40; col++)
{
/* calculate address */
address = start_address + ((((row/8) & 0x07) << 7) | (((row/8) & 0x18) * 5 + col));
plot_text_character_apple(bitmap, col * 7, row, 1, m_ram_dev->read(address),
m_char_ptr, m_char_size, fg, bg);
}
}
}
void agat9video_device::hgr_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow)
{
int row, col, b, va;
int offset;
uint8_t vram_row[42];
uint16_t v;
uint16_t *p;
uint32_t w;
uint16_t *artifact_map_ptr;
int begincol = 0, endcol = 40;
/* sanity checks */
if (beginrow < cliprect.min_y)
beginrow = cliprect.min_y;
if (endrow > cliprect.max_y)
endrow = cliprect.max_y;
if (endrow < beginrow)
return;
// we generate 2 pixels per "column" so adjust
if (begincol < (cliprect.min_x/7))
begincol = (cliprect.min_x/7);
if (endcol > (cliprect.max_x/7))
endcol = (cliprect.max_x/7);
if (cliprect.max_x > 39*7)
endcol = 40;
if (endcol < begincol)
return;
va = m_page2 ? 0x4000 : 0x2000;
vram_row[0] = 0;
vram_row[41] = 0;
for (row = beginrow; row <= endrow; row++)
{
for (col = begincol; col < endcol; col++)
{
offset = ((((row / 8) & 0x07) << 7) | (((row / 8) & 0x18) * 5 + col)) | ((row & 7) << 10);
vram_row[1 + col] = m_ram_dev->read(va + offset);
}
p = &bitmap.pix16(row);
/*
* p. 50 of technical manual:
*
* 1. a 'zero' bit is always drawn as 'black' pixel
* 2. two consecutive 'one' bits are always drawn as 'white' pixel, even bits from different bytes
* 3. even pixels can be 'black', 'purple' (5) or 'blue' (4)
* 4. odd pixels can be 'black', 'green' (2) or 'red' (1)
* 5. bit 7 affects color of pixels in this byte. zero = 'blue' or 'red', one = 'purple' or 'green'.
*/
for (col = 0; col < 40; col++)
{
w = (((uint32_t) vram_row[col+0] & 0x7f) << 0)
| (((uint32_t) vram_row[col+1] & 0x7f) << 7)
| (((uint32_t) vram_row[col+2] & 0x7f) << 14);
artifact_map_ptr = &m_hires_artifact_map[((vram_row[col + 1] & 0x80) >> 7) * 16];
for (b = 0; b < 7; b++)
{
v = artifact_map_ptr[((w >> (b + 7 - 1)) & 0x07) | (((b ^ col) & 0x01) << 3)];
*(p++) = v;
}
}
}
}
uint32_t agat9video_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
switch (m_video_mode)
{
case APPLE: // from apple2e.cpp
// always update the flash timer here so it's smooth regardless of mode switches
m_flash = ((machine().time() * 4).seconds() & 1) ? true : false;
if (m_graphics)
{
if (m_mix)
{
hgr_update(screen, bitmap, cliprect, 0, 159);
text_update_apple(screen, bitmap, cliprect, 160, 191);
}
else
{
hgr_update(screen, bitmap, cliprect, 0, 191);
}
}
else
{
text_update_apple(screen, bitmap, cliprect, 0, 191);
}
break;
case TEXT_LORES:
text_update_lores(screen, bitmap, cliprect, 0, 255);
break;
case TEXT_HIRES:
text_update_hires(screen, bitmap, cliprect, 0, 255);
break;
case GRAPHICS_MONO_LORES:
graph_update_mono_lores(screen, bitmap, cliprect, 0, 255);
break;
case GRAPHICS_MONO_HIRES:
graph_update_mono_hires(screen, bitmap, cliprect, 0, 255);
break;
case GRAPHICS_COLOR_LORES:
graph_update_color_lores(screen, bitmap, cliprect, 0, 255);
break;
case GRAPHICS_COLOR_HIRES:
graph_update_color_hires(screen, bitmap, cliprect, 0, 255);
break;
default:
graph_update_mono_lores(screen, bitmap, cliprect, 0, 255);
break;
}
return 0;
}

96
src/mame/video/agat9.h Normal file
View File

@ -0,0 +1,96 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/*********************************************************************
agat9video.h
Implementation of Agat-9 onboard video.
*********************************************************************/
#ifndef MAME_VIDEO_AGAT9_H
#define MAME_VIDEO_AGAT9_H
#pragma once
#include "machine/ram.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
class agat9video_device : public device_t, public device_palette_interface
{
public:
template <typename T, typename U>
agat9video_device(const machine_config &mconfig, const char *tag, device_t *owner, T &&ram_tag, U &&char_tag)
: agat9video_device(mconfig, tag, owner, (uint32_t)0)
{
m_ram_dev.set_tag(std::forward<T>(ram_tag));
m_char_region.set_tag(std::forward<U>(char_tag));
}
agat9video_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_READ8_MEMBER(read);
DECLARE_WRITE8_MEMBER(write);
DECLARE_READ8_MEMBER(apple_read);
DECLARE_WRITE8_MEMBER(apple_write);
bool m_page2;
bool m_flash;
bool m_mix;
bool m_graphics;
std::unique_ptr<uint16_t[]> m_hires_artifact_map;
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
virtual u32 palette_entries() const override { return 16; }
void text_update_lores(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);
void text_update_hires(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);
void graph_update_mono_lores(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);
void graph_update_mono_hires(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);
void graph_update_color_lores(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);
void graph_update_color_hires(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);
void text_update_apple(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);
void hgr_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int beginrow, int endrow);
private:
void do_io(int offset);
void do_apple_io(int offset);
void plot_text_character(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code, const uint8_t *textgfx_data, uint32_t textgfx_datalen, int fg, int bg);
void plot_text_character_apple(bitmap_ind16 &bitmap, int xpos, int ypos, int xscale, uint32_t code, const uint8_t *textgfx_data, uint32_t textgfx_datalen, int fg, int bg);
uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
required_device<screen_device> m_screen;
required_device<ram_device> m_ram_dev;
required_memory_region m_char_region;
uint8_t *m_char_ptr;
int m_char_size;
uint32_t m_start_address;
enum {
TEXT_LORES = 0,
TEXT_HIRES,
GRAPHICS_COLOR_LORES,
GRAPHICS_COLOR_HIRES,
GRAPHICS_MONO_LORES,
GRAPHICS_MONO_HIRES,
APPLE
} m_video_mode;
int m_mode;
int palette_index;
};
// device type definition
DECLARE_DEVICE_TYPE(AGAT9VIDEO, agat9video_device)
#endif // MAME_VIDEO_AGAT9_H

View File

@ -51,6 +51,10 @@
#include "formats/hpi_dsk.h"
#include "formats/dvk_mx_dsk.h"
#include "formats/aim_dsk.h"
static floppy_format_type floppy_formats[] = {
FLOPPY_MFI_FORMAT,
FLOPPY_DFI_FORMAT,
@ -89,7 +93,10 @@ static floppy_format_type floppy_formats[] = {
FLOPPY_APPLIX_FORMAT,
FLOPPY_HPI_FORMAT
FLOPPY_HPI_FORMAT,
FLOPPY_DVK_MX_FORMAT,
FLOPPY_AIM_FORMAT
};
void CLIB_DECL ATTR_PRINTF(1,2) logerror(const char *format, ...)