// license:BSD-3-Clause // copyright-holders:Wilbert Pol /*************************************************************************** PHUNSY (Philipse Universal System) 2010-11-04 Skeleton driver. 2012-05-24 Cassette added. 2014-01-13 Quickload added. http://www.tubedata.info/phunsy/index.html Baud Rate ~ 6000 baud W command to save data, eg 800-8FFW R command to read data, eg 1100R to load the file at 1100, or R to load the file where it came from. The tape must already be playing the leader when you press the Enter key, or it errors immediately. Rom banking (in U bank): 0U: RAM 1U: MDCR program 2U: Disassembler 3U: Label handler ****************************************************************************/ #include "emu.h" #include "cpu/s2650/s2650.h" #include "imagedev/cassette.h" #include "imagedev/snapquik.h" #include "machine/keyboard.h" #include "sound/spkrdev.h" #include "sound/wave.h" #include "screen.h" #include "speaker.h" #define LOG 1 class phunsy_state : public driver_device { public: phunsy_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) , m_maincpu(*this, "maincpu") , m_speaker(*this, "speaker") , m_cass(*this, "cassette") , m_p_videoram(*this, "videoram") , m_p_chargen(*this, "chargen") { } void init_phunsy(); DECLARE_READ8_MEMBER(phunsy_data_r); DECLARE_WRITE8_MEMBER(phunsy_ctrl_w); DECLARE_WRITE8_MEMBER(phunsy_data_w); void kbd_put(u8 data); DECLARE_READ_LINE_MEMBER(cass_r); DECLARE_WRITE_LINE_MEMBER(cass_w); DECLARE_QUICKLOAD_LOAD_MEMBER(phunsy); DECLARE_PALETTE_INIT(phunsy); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); void phunsy(machine_config &config); void phunsy_data(address_map &map); void phunsy_io(address_map &map); void phunsy_mem(address_map &map); private: uint8_t m_data_out; uint8_t m_keyboard_input; virtual void machine_reset() override; required_device m_maincpu; required_device m_speaker; required_device m_cass; required_shared_ptr m_p_videoram; required_region_ptr m_p_chargen; }; WRITE_LINE_MEMBER( phunsy_state::cass_w ) { m_cass->output(state ? -1.0 : +1.0); } READ_LINE_MEMBER(phunsy_state::cass_r) { return (m_cass->input() > 0.03) ? 0 : 1; } void phunsy_state::phunsy_mem(address_map &map) { map(0x0000, 0x07ff).rom().region("roms", 0); map(0x0800, 0x0fff).ram(); map(0x1000, 0x17ff).ram().share("videoram"); // Video RAM map(0x1800, 0x1fff).bankr("bankru").bankw("bankwu"); // Banked RAM/ROM map(0x2000, 0x3fff).ram(); map(0x4000, 0x7fff).bankrw("bankq"); // Banked RAM } void phunsy_state::phunsy_io(address_map &map) { map.unmap_value_high(); } void phunsy_state::phunsy_data(address_map &map) { map.unmap_value_high(); map(S2650_CTRL_PORT, S2650_CTRL_PORT).w(this, FUNC(phunsy_state::phunsy_ctrl_w)); map(S2650_DATA_PORT, S2650_DATA_PORT).rw(this, FUNC(phunsy_state::phunsy_data_r), FUNC(phunsy_state::phunsy_data_w)); } WRITE8_MEMBER( phunsy_state::phunsy_ctrl_w ) { if (LOG) logerror("%s: phunsy_ctrl_w %02x\n", machine().describe_context(), data); // Q-bank membank("bankq")->set_entry(data & 15); // U-bank data >>= 4; if (data < 4) membank("bankru")->set_entry(data); } WRITE8_MEMBER( phunsy_state::phunsy_data_w ) { if (LOG) logerror("%s: phunsy_data_w %02x\n", machine().describe_context(), data); m_data_out = data; /* b0 - TTY out */ /* b1 - select MDCR / keyboard */ /* b2 - Clear keyboard strobe signal */ if ( data & 0x04 ) { m_keyboard_input |= 0x80; } /* b3 - speaker output (manual says it is bit 1)*/ m_speaker->level_w(BIT(data, 1)); /* b4 - -REV MDCR output */ /* b5 - -FWD MDCR output */ /* b6 - -WCD MDCR output */ /* b7 - WDA MDCR output */ } READ8_MEMBER( phunsy_state::phunsy_data_r ) { uint8_t data = 0xff; //if (LOG) //logerror("%s: phunsy_data_r\n", machine().describe_context()); if ( m_data_out & 0x02 ) { /* MDCR selected */ /* b0 - TTY input */ /* b1 - SK1 switch input */ /* b2 - SK2 switch input */ /* b3 - -WEN MDCR input */ /* b4 - -CIP MDCR input */ /* b5 - -BET MDCR input */ /* b6 - RDA MDCR input */ /* b7 - RDC MDCR input */ data = 0xFF; } else { /* Keyboard selected */ /* b0-b6 - ASCII code from keyboard */ /* b7 - strobe signal */ data = m_keyboard_input; } return data; } /* Input ports */ static INPUT_PORTS_START( phunsy ) INPUT_PORTS_END void phunsy_state::kbd_put(u8 data) { if (data) m_keyboard_input = data; } void phunsy_state::machine_reset() { membank("bankru")->set_entry(0); // point at ram membank("bankq" )->set_base( memregion("ram_4000")->base() ); m_keyboard_input = 0xFF; m_data_out = 0; } PALETTE_INIT_MEMBER(phunsy_state, phunsy) { for ( int i = 0; i < 8; i++ ) { int j = ( i << 5 ) | ( i << 2 ) | ( i >> 1 ); palette.set_pen_color( i, j, j, j ); } } uint32_t phunsy_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { uint8_t y,ra,chr,gfx,col; uint16_t sy=0,ma=0,x; for (y = 0; y < 32; y++) { for (ra = 0; ra < 8; ra++) { uint16_t *p = &bitmap.pix16(sy++); for (x = ma; x < ma+64; x++) { chr = m_p_videoram[x]; if (BIT(chr, 7)) { /* Graphics mode */ gfx = 0; col = ( chr >> 4 ) & 7; if ( (BIT(chr, 0) && (!BIT(ra, 2))) || (BIT(chr, 2) && (BIT(ra, 2))) ) gfx = 0x38; if ( (BIT(chr, 1) && (!BIT(ra, 2))) || (BIT(chr, 3) && (BIT(ra, 2))) ) gfx |= 7; } else { /* ASCII mode */ gfx = m_p_chargen[(chr<<3) | ra]; col = 7; } /* Display a scanline of a character (6 pixels) */ *p++ = BIT( gfx, 5 ) ? col : 0; *p++ = BIT( gfx, 4 ) ? col : 0; *p++ = BIT( gfx, 3 ) ? col : 0; *p++ = BIT( gfx, 2 ) ? col : 0; *p++ = BIT( gfx, 1 ) ? col : 0; *p++ = BIT( gfx, 0 ) ? col : 0; } } ma+=64; } return 0; } /* F4 Character Displayer */ static const gfx_layout phunsy_charlayout = { 5, 7, /* 6 x 8 characters */ 128, /* 128 characters */ 1, /* 1 bits per pixel */ { 0 }, /* no bitplanes */ /* x offsets */ { 3, 4, 5, 6, 7 }, /* y offsets */ { 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 }, 8*8 /* every char takes 8 bytes */ }; static GFXDECODE_START( gfx_phunsy ) GFXDECODE_ENTRY( "chargen", 0x0000, phunsy_charlayout, 1, 3 ) GFXDECODE_END // quickloads can start from various addresses, and the files have no header. QUICKLOAD_LOAD_MEMBER( phunsy_state, phunsy ) { address_space &space = m_maincpu->space(AS_PROGRAM); uint16_t i; uint16_t quick_addr = 0x1800; std::vector quick_data; image_init_result result = image_init_result::FAIL; int quick_length = image.length(); if (quick_length > 0x4000) { image.seterror(IMAGE_ERROR_INVALIDIMAGE, "File too long"); image.message(" File too long"); } else { quick_data.resize(quick_length); membank("bankru")->set_entry(0); // point at ram uint16_t exec_addr = quick_addr + 2; for (i = 0; i < quick_length; i++) space.write_byte(i+quick_addr, quick_data[i]); /* display a message about the loaded quickload */ image.message(" Quickload: size=%04X : exec=%04X",quick_length,exec_addr); // Start the quickload m_maincpu->set_state_int(S2650_R0, exec_addr>>8); m_maincpu->set_state_int(S2650_R1, 0x08); m_maincpu->set_state_int(S2650_R2, 0xe0); m_maincpu->set_state_int(S2650_R3, 0x83); m_maincpu->set_state_int(S2650_PC, exec_addr); result = image_init_result::PASS; } return result; } void phunsy_state::init_phunsy() { uint8_t *main = memregion("maincpu")->base(); uint8_t *roms = memregion("roms")->base(); uint8_t *ram = memregion("ram_4000")->base(); membank("bankru")->configure_entry(0, &main[0x1800]); membank("bankwu")->configure_entry(0, &main[0x1800]); membank("bankru")->configure_entries(1, 3, &roms[0x800], 0x800); membank("bankq")->configure_entries(0, 16, &ram[0], 0x4000); membank("bankru")->set_entry(0); // point at ram membank("bankwu")->set_entry(0); // always write to ram membank("bankq")->set_entry(0); } MACHINE_CONFIG_START(phunsy_state::phunsy) /* basic machine hardware */ MCFG_DEVICE_ADD("maincpu",S2650, XTAL(1'000'000)) MCFG_DEVICE_PROGRAM_MAP(phunsy_mem) MCFG_DEVICE_IO_MAP(phunsy_io) MCFG_DEVICE_DATA_MAP(phunsy_data) MCFG_S2650_SENSE_INPUT(READLINE(*this, phunsy_state, cass_r)) MCFG_S2650_FLAG_OUTPUT(WRITELINE(*this, phunsy_state, cass_w)) /* video hardware */ MCFG_SCREEN_ADD("screen", RASTER) /* Display (page 12 of pdf) - 8Mhz clock - 64 6 pixel characters on a line. - 16us not active, 48us active: ( 64 * 6 ) * 60 / 48 => 480 pixels wide - 313 line display of which 256 are displayed. */ MCFG_SCREEN_RAW_PARAMS(XTAL(8'000'000), 480, 0, 64*6, 313, 0, 256) MCFG_SCREEN_UPDATE_DRIVER(phunsy_state, screen_update) MCFG_SCREEN_PALETTE("palette") MCFG_DEVICE_ADD("gfxdecode", GFXDECODE, "palette", gfx_phunsy) MCFG_PALETTE_ADD("palette", 8) MCFG_PALETTE_INIT_OWNER(phunsy_state, phunsy) /* sound hardware */ SPEAKER(config, "mono").front_center(); WAVE(config, "wave", "cassette").add_route(ALL_OUTPUTS, "mono", 0.25); SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50); /* Devices */ MCFG_DEVICE_ADD("keyboard", GENERIC_KEYBOARD, 0) MCFG_GENERIC_KEYBOARD_CB(PUT(phunsy_state, kbd_put)) MCFG_CASSETTE_ADD( "cassette" ) /* quickload */ MCFG_QUICKLOAD_ADD("quickload", phunsy_state, phunsy, "bin", 2) MACHINE_CONFIG_END /* ROM definition */ ROM_START( phunsy ) ROM_REGION( 0x4000, "maincpu", ROMREGION_ERASE00 ) ROM_REGION( 0x2000, "roms", 0 ) ROM_LOAD( "phunsy_bios.bin", 0x0000, 0x0800, CRC(a789e82e) SHA1(b1c130ab2b3c139fd16ddc5dc7bdcaf7a9957d02)) ROM_LOAD( "pdcr.bin", 0x0800, 0x0800, CRC(74bf9d0a) SHA1(8d2f673615215947f033571f1221c6aa99c537e9)) ROM_LOAD( "dass.bin", 0x1000, 0x0800, CRC(13380140) SHA1(a999201cb414abbf1e10a7fcc1789e3e000a5ef1)) ROM_LOAD( "labhnd.bin", 0x1800, 0x0800, CRC(1d5a106b) SHA1(a20d09e32e21cf14db8254cbdd1d691556b473f0)) ROM_REGION( 0x0400, "chargen", 0 ) ROM_LOAD( "ph_char1.bin", 0x0200, 0x0100, CRC(a7e567fc) SHA1(b18aae0a2d4f92f5a7e22640719bbc4652f3f4ee)) ROM_CONTINUE(0x0100, 0x0100) ROM_LOAD( "ph_char2.bin", 0x0000, 0x0100, CRC(3d5786d3) SHA1(8cf87d83be0b5e4becfa9fd6e05b01250a2feb3b)) ROM_CONTINUE(0x0300, 0x0100) /* 16 x 16KB RAM banks */ ROM_REGION( 0x40000, "ram_4000", ROMREGION_ERASEFF ) ROM_END /* Driver */ /* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ COMP( 1980, phunsy, 0, 0, phunsy, phunsy, phunsy_state, init_phunsy, "J.F.P. Philipse", "PHUNSY", MACHINE_NOT_WORKING )