// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**********************************************************************
"Craft" demo platform by Linus Akesson
driver by Ryan Holtz
**********************************************************************/
#include "emu.h"
#include "cpu/avr8/avr8.h"
#include "sound/dac.h"
#include "sound/volt_reg.h"
#include "screen.h"
#include "speaker.h"
#define VERBOSE_LEVEL (0)
#define ENABLE_VERBOSE_LOG (0)
#define MASTER_CLOCK 20000000
#define VISIBLE_CYCLES 480
#define HSYNC_CYCLES 155
#define LINE_CYCLES (VISIBLE_CYCLES + HSYNC_CYCLES)
#define VISIBLE_LINES 480
#define VSYNC_LINES 45
#define LINES_PER_FRAME (VISIBLE_LINES + VSYNC_LINES)
#define CYCLES_PER_FRAME (LINES_PER_FRAME * LINE_CYCLES)
#define PIXELS_PER_FRAME (CYCLES_PER_FRAME)
/****************************************************\
* I/O devices *
\****************************************************/
class craft_state : public driver_device
{
public:
craft_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag),
m_maincpu(*this, "maincpu"),
m_dac(*this, "dac")
{
}
void video_update();
virtual void machine_start() override;
uint32_t m_last_cycles;
uint64_t m_frame_start_cycle;
uint8_t m_port_b;
uint8_t m_port_c;
uint8_t m_port_d;
uint8_t m_latched_color;
uint8_t m_pixels[PIXELS_PER_FRAME];
required_device<avr8_device> m_maincpu;
DECLARE_READ8_MEMBER(port_r);
DECLARE_WRITE8_MEMBER(port_w);
void init_craft();
virtual void machine_reset() override;
uint32_t screen_update_craft(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
inline void verboselog(int n_level, const char *s_fmt, ...) ATTR_PRINTF(3,4);
required_device<dac_byte_interface> m_dac;
void craft(machine_config &config);
void craft_data_map(address_map &map);
void craft_io_map(address_map &map);
void craft_prg_map(address_map &map);
};
inline void craft_state::verboselog(int n_level, const char *s_fmt, ...)
{
#if ENABLE_VERBOSE_LOG
if( VERBOSE_LEVEL >= n_level )
{
va_list v;
char buf[ 32768 ];
va_start( v, s_fmt );
vsprintf( buf, s_fmt, v );
va_end( v );
logerror( "%s: %s", machine().describe_context(), buf );
}
#endif
}
void craft_state::machine_start()
{
}
READ8_MEMBER(craft_state::port_r)
{
switch( offset )
{
case 0x00: // Port A
case 0x01: // Port B
case 0x02: // Port C
case 0x03: // Port D
// Unhandled
return 0x00;
}
return 0;
}
WRITE8_MEMBER(craft_state::port_w)
{
switch( offset )
{
case AVR8_IO_PORTA: // Port A
// Unhandled
break;
case AVR8_IO_PORTB: // Port B
{
uint8_t old_port_b = m_port_b;
uint8_t pins = data;
uint8_t changed = pins ^ old_port_b;
if(pins & changed & 0x02)
{
m_frame_start_cycle = m_maincpu->get_elapsed_cycles();
video_update();
}
if(changed & 0x08)
{
video_update();
m_latched_color = (pins & 0x08) ? (m_port_c & 0x3f) : 0x3f;
}
m_port_b = data;
break;
}
case AVR8_IO_PORTC: // Port C
video_update();
m_port_c = data;
m_latched_color = m_port_c;
break;
case AVR8_IO_PORTD: // Port D
{
m_port_d = data;
uint8_t audio_sample = (data & 0x02) | ((data & 0xf4) >> 2);
m_dac->write(audio_sample);
break;
}
}
}
void craft_state::video_update()
{
uint64_t cycles = m_maincpu->get_elapsed_cycles();
uint32_t frame_cycles = (uint32_t)(cycles - m_frame_start_cycle);
if (m_last_cycles < frame_cycles)
{
for (uint32_t pixidx = m_last_cycles; pixidx < frame_cycles && pixidx < PIXELS_PER_FRAME; pixidx++)
{
m_pixels[pixidx] = m_latched_color;
}
}
else
{
uint32_t end_clear = sizeof(m_pixels) - m_last_cycles;
uint32_t start_clear = frame_cycles;
end_clear = (end_clear > PIXELS_PER_FRAME) ? (PIXELS_PER_FRAME - m_last_cycles) : end_clear;
start_clear = (start_clear > PIXELS_PER_FRAME) ? PIXELS_PER_FRAME : start_clear;
if (m_last_cycles < PIXELS_PER_FRAME)
{
memset(m_pixels + m_last_cycles, 0, end_clear);
}
if (start_clear < PIXELS_PER_FRAME)
{
memset(m_pixels, 0, start_clear);
}
}
m_last_cycles = frame_cycles;
}
/****************************************************\
* Address maps *
\****************************************************/
void craft_state::craft_prg_map(address_map &map)
{
map(0x0000, 0x1fff).rom();
}
void craft_state::craft_data_map(address_map &map)
{
map(0x0100, 0x04ff).ram();
}
void craft_state::craft_io_map(address_map &map)
{
map(AVR8_IO_PORTA, AVR8_IO_PORTD).rw(this, FUNC(craft_state::port_r), FUNC(craft_state::port_w));
}
/****************************************************\
* Input ports *
\****************************************************/
static INPUT_PORTS_START( craft )
PORT_START("MAIN")
PORT_BIT(0xff, IP_ACTIVE_HIGH, IPT_UNUSED)
INPUT_PORTS_END
/****************************************************\
* Video hardware *
\****************************************************/
uint32_t craft_state::screen_update_craft(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
for(int y = 0; y < LINES_PER_FRAME; y++)
{
uint32_t *line = &bitmap.pix32(y);
for(int x = 0; x < LINE_CYCLES; x++)
{
uint8_t pixel = m_pixels[y * LINE_CYCLES + x];
uint8_t r = 0x55 * ((pixel & 0x30) >> 4);
uint8_t g = 0x55 * ((pixel & 0x0c) >> 2);
uint8_t b = 0x55 * (pixel & 0x03);
line[x] = 0xff000000 | (r << 16) | (g << 8) | b;
}
}
return 0;
}
/****************************************************\
* Machine definition *
\****************************************************/
void craft_state::init_craft()
{
}
void craft_state::machine_reset()
{
m_frame_start_cycle = 0;
m_last_cycles = 0;
}
MACHINE_CONFIG_START(craft_state::craft)
/* basic machine hardware */
MCFG_DEVICE_ADD("maincpu", ATMEGA88, MASTER_CLOCK)
MCFG_DEVICE_PROGRAM_MAP(craft_prg_map)
MCFG_DEVICE_DATA_MAP(craft_data_map)
MCFG_DEVICE_IO_MAP(craft_io_map)
MCFG_CPU_AVR8_EEPROM("eeprom")
/* video hardware */
MCFG_SCREEN_ADD("screen", RASTER)
MCFG_SCREEN_REFRESH_RATE(59.99)
MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(1429)) /* accurate */
MCFG_SCREEN_SIZE(635, 525)
MCFG_SCREEN_VISIBLE_AREA(47, 526, 36, 515)
MCFG_SCREEN_UPDATE_DRIVER(craft_state, screen_update_craft)
MCFG_PALETTE_ADD("palette", 0x1000)
/* sound hardware */
SPEAKER(config, "avr8").front_center();
MCFG_DEVICE_ADD("dac", DAC_6BIT_R2R, 0) MCFG_SOUND_ROUTE(0, "avr8", 0.25) // pd1/pd2/pd4/pd5/pd6/pd7 + 2k(x7) + 1k(x5)
MCFG_DEVICE_ADD("vref", VOLTAGE_REGULATOR, 0) MCFG_VOLTAGE_REGULATOR_OUTPUT(5.0)
MCFG_SOUND_ROUTE(0, "dac", 1.0, DAC_VREF_POS_INPUT) MCFG_SOUND_ROUTE(0, "dac", -1.0, DAC_VREF_NEG_INPUT)
MACHINE_CONFIG_END
ROM_START( craft )
ROM_REGION( 0x2000, "maincpu", 0 ) /* Main program store */
ROM_LOAD( "craft.bin", 0x0000, 0x2000, CRC(2e6f9ad2) SHA1(75e495bf18395d74289ca7ee2649622fc4010457) )
ROM_REGION( 0x200, "eeprom", 0 ) /* on-die eeprom */
ROM_LOAD( "eeprom.raw", 0x0000, 0x0200, CRC(e18a2af9) SHA1(81fc6f2d391edfd3244870214fac37929af0ac0c) )
ROM_END
/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME */
CONS(2008, craft, 0, 0, craft, craft, craft_state, init_craft, "Linus Akesson", "Craft", MACHINE_NOT_WORKING)