summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/drivers/coleco.cpp
blob: 031ce2dd0371b675bf77310d0e34b6aaca8bc1c6 (plain) (tree)
1
2
3
4
5
6
7
                       
                                                                             



                                                                                                        
                                                      























































                                                                                                             



                                                      
 

  
                
                            
                   
                     
                    
                     
 



                                        
                                                                                            



                                        
                                                                                            











                                           

                                            




                                                 



                                            




                                                 






                                                          
                             










                                                                                    
                                                       

         
 

                 






                                                              








                                                                                        


                                                  
                                                                         
                                                                                                               
                                                                        
                                                                                 

                                                                       

 









                                                                                                               
                                                                                                                                      















                                                                                                                             





                                                              
 














                                                                                                                
                                                                                                               

























                                                                          



















































































                                                                                                  
 

                
                                                     
 
                             
                                       
                                                                            
 
                                 

 
                                                            
 

                                      

 
                                                             
 
                                   
 

                                                                       

 
                                                          
 
                                       
         
                                                                       

                                                                                                                  

                                                                                                         

                                                                                                     


                                                                                                         

                               
                                                                                   


         
                                                                  

                                                      
                                                                              


                                            
                                              

                                                    
                                                            



                                                                                     
                                                        
                                                                                                                    



                 

                                    
                                                            

 
                                                                                     
 
                                                  


                                                                             
                                                         
                                                                        
                                                          

                                                                             
                                                         
                                                                        
                                                          






                                                                                                   

                                                       





                                           
                                                                                    
 

                                                  





                                                   

                                      





























                                                                                                            
                                    


















                                                                                    

                                  

                                                             


                                            


                                                                                                                                                     




                                             
 
                             
                                                                                                                                     
 





                                             

 






                                                           


                                  

 





                                      
                                                                                     
   
                                           


                                                                                  
                                           
                                                                                                                                                      
                                           



                  
 

                     

                                                 
                                    


                                                                      
 
                            




                                                                             
 
                            
                                               

                                                                                              
                                                                                                          
                                                                                

                       
                                                                                      

                            
                                                                  
 

                                                                                                                              
 

                                                  
                       
 
                            




                                                                                       
 





























                                                                                                                              

                                                
                       
 
                                    

                                                                                                                                         
 

                                               
                      

                            




                                                                                       
 
 


                  
                                           
                                                    
                                                                                                                                          
                                                         
                                                                                   
                                                                                                                             

       



                                                                                                        
                                                                                       






                                                                                                            















                                           
                                                                                                                   

       
                  
                                           






                                                                                                             





















                                                        

                  



                                                
                                           







                                                                                                                           
       


                    






                                                                                                                                               

                                                                                                                                             

 
// license:BSD-3-Clause
// copyright-holders:Mike Balfour, Ben Bruscella, Sean Young, Frank Palazzolo
/*******************************************************************************************************

  coleco.c

  Driver file to handle emulation of the ColecoVision.

  Marat Fayzullin (ColEm source)
  Marcel de Kogel (AdamEm source)
  Mike Balfour
  Ben Bruscella
  Sean Young

  NEWS:
    - Modified memory map, now it has only 1k of RAM mapped on 8k Slot
    - Modified I/O map, now it is handled as on a real ColecoVision:
        The I/O map is broken into 4 write and 4 read ports:
            80-9F (W) = Set both controllers to keypad mode
            80-9F (R) = Not Connected

            A0-BF (W) = Video Chip (TMS9928A), A0=0 -> Write Register 0 , A0=1 -> Write Register 1
            A0-BF (R) = Video Chip (TMS9928A), A0=0 -> Read Register 0 , A0=1 -> Read Register 1

            C0-DF (W) = Set both controllers to joystick mode
            C0-DF (R) = Not Connected

            E0-FF (W) = Sound Chip (SN76489A)
            E0-FF (R) = Read Controller data, A1=0 -> read controller 1, A1=1 -> read controller 2

    - Modified paddle handler, now it is handled as on a real ColecoVision
    - Added support for a Roller Controller (Trackball), enabled via category
    - Added support for two Super Action Controller, enabled via category

    EXTRA CONTROLLERS INFO:

    -Driving Controller (Expansion Module #2). It consist of a steering wheel and a gas pedal. Only one
    can be used on a real ColecoVision. The gas pedal is not analog, internally it is just a switch.
    On a real ColecoVision, when the Driving Controller is enabled, the controller 1 do not work because
    have been replaced by the Driving Controller, and controller 2 have to be used to start game, gear
    shift, etc.
    Driving Controller is just a spinner on controller 1 socket similar to the one on Roller Controller
    and Super Action Controllers so you can use Roller Controller or Super Action Controllers to play
    games requiring Driving Controller.

    -Roller Controller. Basically a trackball with four buttons (the two fire buttons from player 1 and
    the two fire buttons from player 2). Only one Roller Controller can be used on a real ColecoVision.
    Roller Controller is connected to both controller sockets and both controllers are conected to the Roller
    Controller, it uses the spinner pins of both sockets to generate the X and Y signals (X from controller 1
    and the Y from controller 2)

    -Super Action Controllers. It is a hand controller with a keypad, four buttons (the two from
    the player pad and two more), and a spinner. This was made primarily for two player sport games, but
    will work for every other ColecoVision game.

*******************************************************************************************************/

/*

    TODO:

    - Dina SG-1000 mode

    - Bit90:
        Add support for memory expansion (documented)
        Add support for printer interface (documented)
        Add tape Support

*/

#include "emu.h"
#include "includes/coleco.h"
#include "screen.h"
#include "softlist.h"
#include "speaker.h"
#include "logmacro.h"

/* Read/Write Handlers */

READ8_MEMBER( coleco_state::paddle_1_r )
{
	return m_joy_d7_state[0] | coleco_paddle_read(0, m_joy_mode, m_joy_analog_state[0]);
}

READ8_MEMBER( coleco_state::paddle_2_r )
{
	return m_joy_d7_state[1] | coleco_paddle_read(1, m_joy_mode, m_joy_analog_state[1]);
}

WRITE8_MEMBER( coleco_state::paddle_off_w )
{
	m_joy_mode = 0;
}

WRITE8_MEMBER( coleco_state::paddle_on_w )
{
	m_joy_mode = 1;
}

READ8_MEMBER( bit90_state::bankswitch_u4_r )
{
	if (!machine().side_effects_disabled()) {
		LOG("Bankswitch to u4\n");
		m_bank->set_entry(0);
	}
	return space.unmap();
}

READ8_MEMBER( bit90_state::bankswitch_u3_r )
{
	if (!machine().side_effects_disabled()) {
		LOG("Bankswitch to u3\n");
		m_bank->set_entry(1);
	}
	return space.unmap();
}

READ8_MEMBER( bit90_state::keyboard_r )
{
	if (m_keyselect < 8) {
		return m_io_keyboard[m_keyselect]->read();
	}
	return space.unmap();
}

WRITE8_MEMBER( bit90_state::u32_w )
{
	// Write to a 74LS174 at u32
	// Bits 4-7 are connected for keyboard scanning (actually only 4-6 are used)
	// Bits 0-1 unknown (probably tape?)
	m_keyselect = (data >> 4) & 0x0f;
	uint8_t temp = data & 0x03;
	if (m_unknown != temp) {
		m_unknown = temp;
		LOG("m_unknown -> 0x%02x\n",m_unknown);
	}
}

/* Memory Maps */

void coleco_state::coleco_map(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x6000, 0x63ff).ram().mirror(0x1c00).share("ram");
	map(0x8000, 0xffff).rom();
}

void bit90_state::bit90_map(address_map &map)
{
	map(0x0000, 0x1fff).bankr("bank");
	map(0x2000, 0x3fff).rom();
	map(0x4000, 0x5fff).rom();  // Decoded through pin 5 of the Bit90 expansion port
	map(0x6000, 0x67ff).ram().mirror(0x1800).share("ram");
	map(0x8000, 0xffff).rom();  // More RAM could also appear here (TBD)
}

void coleco_state::coleco_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x80, 0x80).mirror(0x1f).w(FUNC(coleco_state::paddle_off_w));
	map(0xa0, 0xa1).mirror(0x1e).rw("tms9928a", FUNC(tms9928a_device::read), FUNC(tms9928a_device::write));
	map(0xc0, 0xc0).mirror(0x1f).w(FUNC(coleco_state::paddle_on_w));
	map(0xe0, 0xe0).mirror(0x1f).w("sn76489a", FUNC(sn76489a_device::write));
	map(0xe0, 0xe0).mirror(0x1d).r(FUNC(coleco_state::paddle_1_r));
	map(0xe2, 0xe2).mirror(0x1d).r(FUNC(coleco_state::paddle_2_r));
}

void bit90_state::bit90_io_map(address_map &map)
{
	map.global_mask(0xff);
	map(0x80, 0x80).mirror(0x17).r(FUNC(bit90_state::bankswitch_u4_r));
	map(0x88, 0x88).mirror(0x17).r(FUNC(bit90_state::bankswitch_u3_r));
	map(0x80, 0x80).mirror(0x1f).w(FUNC(coleco_state::paddle_off_w));
	map(0xa0, 0xa1).mirror(0x1e).rw("tms9928a", FUNC(tms9928a_device::read), FUNC(tms9928a_device::write));
	map(0xc0, 0xc0).mirror(0x1f).r(FUNC(bit90_state::keyboard_r));
	map(0xc0, 0xc0).mirror(0x1f).w(FUNC(coleco_state::paddle_on_w));
	map(0xe0, 0xe0).mirror(0x1d).r(FUNC(coleco_state::paddle_1_r));
	map(0xe0, 0xe0).mirror(0x1b).w(FUNC(bit90_state::u32_w));        // bits7-4 for keyscan, (to bcd decoder) and bits1-0 tape out
	map(0xe2, 0xe2).mirror(0x1d).r(FUNC(coleco_state::paddle_2_r));  // also, bit7 is tape read?
	map(0xe4, 0xe4).mirror(0x1b).w("sn76489a", FUNC(sn76489a_device::write));

	// IORQ goes to pin 55 of the Bit90 expansion port,
	// So ports < 0x80 can be decoded there

	// External Printer Interface
	//map(0x48, 0x48).w(FUNC(bit90_state::printer_data_w));  // data to latch here
	//map(0x4a, 0x4a).r(FUNC(bit90_state::printer_control_r));  // bit0 is busy bit
	//map(0x4a, 0x4a).w(FUNC(bit90_state::printer_control_w));  // bit0 is reset (active low), bit1 is latch (active low)

	// External/(Internal?) RAM Interface
	//map(0x4e, 0x4f).w(FUNC(bit90_state::external_ram_control_w)); // 0x4e enable, 0x4f disable
	// RAM can appear, starting at 0x8000 up to 0xffff
}

void coleco_state::czz50_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	map(0x6000, 0x63ff).ram().mirror(0x1c00).share("ram");
	map(0x8000, 0xffff).rom();
}

/* Input Ports */

static INPUT_PORTS_START( czz50 )
	PORT_START("STD_KEYPAD1")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0')
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1')
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("2") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2')
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("3") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3')
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("4") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4')
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5')
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("6") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6')
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7')
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("8") PORT_CODE(KEYCODE_8_PAD) PORT_CHAR('8')
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("9") PORT_CODE(KEYCODE_9_PAD) PORT_CHAR('9')
	PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("#") PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHAR('#')
	PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("*") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHAR('*')
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0xb000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("STD_JOY1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0xb0, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("STD_KEYPAD2")
	PORT_BIT( 0x00ff, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x0f00, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0xb000, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("STD_JOY2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0xb0, IP_ACTIVE_LOW, IPT_UNKNOWN )
INPUT_PORTS_END

static INPUT_PORTS_START( bit90 )
	PORT_INCLUDE( coleco )

	PORT_START("ROW0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7") PORT_CODE(KEYCODE_7)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3") PORT_CODE(KEYCODE_3)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INS") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1") PORT_CODE(KEYCODE_1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5") PORT_CODE(KEYCODE_5)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9") PORT_CODE(KEYCODE_9)

	PORT_START("ROW1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^") PORT_CODE(KEYCODE_TILDE)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8") PORT_CODE(KEYCODE_8)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4") PORT_CODE(KEYCODE_4)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BS") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL") PORT_CODE(KEYCODE_DEL)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2") PORT_CODE(KEYCODE_2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6") PORT_CODE(KEYCODE_6)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0") PORT_CODE(KEYCODE_0)

	PORT_START("ROW2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CODE(KEYCODE_P)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CODE(KEYCODE_Y)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CODE(KEYCODE_W)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("]") PORT_CODE(KEYCODE_CLOSEBRACE)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("UP") PORT_CODE(KEYCODE_UP)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESC") PORT_CODE(KEYCODE_ESC)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CODE(KEYCODE_R)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CODE(KEYCODE_I)

	PORT_START("ROW3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CODE(KEYCODE_U)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CODE(KEYCODE_E)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RUBOUT") PORT_CODE(KEYCODE_HOME)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RIGHT") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CODE(KEYCODE_Q)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CODE(KEYCODE_T)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CODE(KEYCODE_O)

	PORT_START("ROW4")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CODE(KEYCODE_H)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CODE(KEYCODE_S)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@") PORT_CODE(KEYCODE_END)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CODE(KEYCODE_F)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CODE(KEYCODE_K)

	PORT_START("ROW5")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(":") PORT_CODE(KEYCODE_QUOTE)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CODE(KEYCODE_J)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CODE(KEYCODE_D)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RETURN") PORT_CODE(KEYCODE_ENTER)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(".") PORT_CODE(KEYCODE_STOP)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CODE(KEYCODE_A)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CODE(KEYCODE_G)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CODE(KEYCODE_L)

	PORT_START("ROW6")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CODE(KEYCODE_N)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CODE(KEYCODE_V)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CODE(KEYCODE_X)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CODE(KEYCODE_M)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(",") PORT_CODE(KEYCODE_COMMA)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CODE(KEYCODE_C)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CODE(KEYCODE_B)

	PORT_START("ROW7")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("META") PORT_CODE(KEYCODE_RALT)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("BASIC") PORT_CODE(KEYCODE_LALT)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LEFT") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DOWN") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FCTN") PORT_CODE(KEYCODE_RCONTROL)

INPUT_PORTS_END

/* Interrupts */

WRITE_LINE_MEMBER(coleco_state::coleco_vdp_interrupt)
{
	// NMI on rising edge
	if (state && !m_last_nmi_state)
		m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);

	m_last_nmi_state = state;
}

TIMER_CALLBACK_MEMBER(coleco_state::paddle_d7reset_callback)
{
	m_joy_d7_state[param] = 0;
	m_joy_analog_state[param] = 0;
}

TIMER_CALLBACK_MEMBER(coleco_state::paddle_irqreset_callback)
{
	m_joy_irq_state[param] = 0;

	if (!m_joy_irq_state[param ^ 1])
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
}

TIMER_CALLBACK_MEMBER(coleco_state::paddle_pulse_callback)
{
	if (m_joy_analog_reload[param])
	{
		m_joy_analog_state[param] = m_joy_analog_reload[param];

		// on movement, controller port d7 is set for a short period and an irq is fired on d7 rising edge
		m_joy_d7_state[param] = 0x80;
		m_joy_d7_timer[param]->adjust(attotime::from_usec(500), param); // TODO: measure duration

		// irq on rising edge, PULSE_LINE is not supported in this case, so clear it manually
		m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
		m_joy_irq_timer[param]->adjust(attotime::from_usec(11), param); // TODO: measure duration
		m_joy_irq_state[param] = 1;

		// reload timer
		m_joy_pulse_timer[param]->adjust(m_joy_pulse_reload[param], param);
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(coleco_state::paddle_update_callback)
{
	// arbitrary timer for reading analog controls
	coleco_scan_paddles(&m_joy_analog_reload[0], &m_joy_analog_reload[1]);

	for (int port = 0; port < 2; port++)
	{
		if (m_joy_analog_reload[port])
		{
			const int sensitivity = 500;
			int ipt = m_joy_analog_reload[port];
			if (ipt & 0x80) ipt = 0x100 - ipt;
			attotime freq = attotime::from_msec(sensitivity / ipt);

			// change pulse intervals relative to spinner/trackball speed
			m_joy_pulse_reload[port] = freq;
			m_joy_pulse_timer[port]->adjust(std::min(freq, m_joy_pulse_timer[port]->remaining()), port);
		}
	}
}

READ8_MEMBER( coleco_state::cart_r )
{
	return m_cart->bd_r(offset & 0x7fff, 0, 0, 0, 0, 0);
}

uint8_t coleco_state::coleco_scan_paddles(uint8_t *joy_status0, uint8_t *joy_status1)
{
	uint8_t ctrl_sel = m_ctrlsel.read_safe(0);

	/* which controller shall we read? */
	if ((ctrl_sel & 0x07) == 0x02)          // Super Action Controller P1
		*joy_status0 = m_sac_slide1.read_safe(0);
	else if ((ctrl_sel & 0x07) == 0x03)     // Driving Controller P1
		*joy_status0 = m_driv_wheel1.read_safe(0);

	if ((ctrl_sel & 0x70) == 0x20)          // Super Action Controller P2
		*joy_status1 = m_sac_slide2.read_safe(0);
	else if ((ctrl_sel & 0x70) == 0x30)     // Driving Controller P2
		*joy_status1 = m_driv_wheel2.read_safe(0);

	/* In principle, even if not supported by any game, I guess we could have two Super
	   Action Controllers plugged into the Roller controller ports. Since I found no info
	   about the behavior of sliders in such a configuration, we overwrite SAC sliders with
	   the Roller trackball inputs and actually use the latter ones, when both are selected. */
	if (ctrl_sel & 0x80)                    // Roller controller
	{
		*joy_status0 = m_roller_x.read_safe(0);
		*joy_status1 = m_roller_y.read_safe(0);
	}

	return *joy_status0 | *joy_status1;
}


uint8_t coleco_state::coleco_paddle_read(int port, int joy_mode, uint8_t joy_status)
{
	uint8_t ctrl_sel = m_ctrlsel.read_safe(0);
	uint8_t ctrl_extra = ctrl_sel & 0x80;
	ctrl_sel = ctrl_sel >> (port*4) & 7;

	/* Keypad and fire 1 (SAC Yellow Button) */
	if (joy_mode == 0)
	{
		/* No key pressed by default */
		uint8_t data = 0x0f;
		uint16_t ipt = 0xffff;

		if (ctrl_sel == 0)          // ColecoVision Controller
			ipt = port ? m_std_keypad2->read() : m_std_keypad1->read();
		else if (ctrl_sel == 2)     // Super Action Controller
			ipt = port ? m_sac_keypad2->read() : m_sac_keypad1->read();

		/* Numeric pad buttons are not independent on a real ColecoVision, if you push more
		   than one, a real ColecoVision think that it is a third button, so we are going to emulate
		   the right behaviour */
		/* Super Action Controller additional buttons are read in the same way */
		if (!(ipt & 0x0001)) data &= 0x0a; /* 0 */
		if (!(ipt & 0x0002)) data &= 0x0d; /* 1 */
		if (!(ipt & 0x0004)) data &= 0x07; /* 2 */
		if (!(ipt & 0x0008)) data &= 0x0c; /* 3 */
		if (!(ipt & 0x0010)) data &= 0x02; /* 4 */
		if (!(ipt & 0x0020)) data &= 0x03; /* 5 */
		if (!(ipt & 0x0040)) data &= 0x0e; /* 6 */
		if (!(ipt & 0x0080)) data &= 0x05; /* 7 */
		if (!(ipt & 0x0100)) data &= 0x01; /* 8 */
		if (!(ipt & 0x0200)) data &= 0x0b; /* 9 */
		if (!(ipt & 0x0400)) data &= 0x06; /* # */
		if (!(ipt & 0x0800)) data &= 0x09; /* * */
		if (!(ipt & 0x1000)) data &= 0x04; /* Blue Action Button */
		if (!(ipt & 0x2000)) data &= 0x08; /* Purple Action Button */

		return ((ipt & 0x4000) >> 8) | 0x30 | data;
	}
	/* Joystick and fire 2 (SAC Red Button) */
	else
	{
		uint8_t data = 0x7f;

		if (ctrl_sel == 0)          // ColecoVision Controller
			data = port ? m_std_joy2->read() : m_std_joy1->read();
		else if (ctrl_sel == 2)     // Super Action Controller
			data = port ? m_sac_joy2->read() : m_sac_joy1->read();
		else if (ctrl_sel == 3)     // Driving Controller
			data = port ? m_driv_pedal2->read() : m_driv_pedal1->read();

		/* If any extra analog contoller enabled */
		if (ctrl_extra || ctrl_sel == 2 || ctrl_sel == 3)
		{
			if (joy_status & 0x80) data ^= 0x30;
			else if (joy_status) data ^= 0x10;
		}

		return data & 0x7f;
	}
}

void coleco_state::machine_start()
{
	memset(m_ram, 0xff, m_ram.bytes()); // initialize RAM

	// init paddles
	for (int port = 0; port < 2; port++)
	{
		m_joy_pulse_timer[port] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(coleco_state::paddle_pulse_callback),this));
		m_joy_d7_timer[port] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(coleco_state::paddle_d7reset_callback),this));
		m_joy_irq_timer[port] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(coleco_state::paddle_irqreset_callback),this));

		m_joy_irq_state[port] = 0;
		m_joy_d7_state[port] = 0;
		m_joy_analog_state[port] = 0;
	}

	if (m_cart->exists())
		m_maincpu->space(AS_PROGRAM).install_read_handler(0x8000, 0xffff, read8_delegate(*this, FUNC(coleco_state::cart_r)));

	save_item(NAME(m_joy_mode));
	save_item(NAME(m_last_nmi_state));
	save_item(NAME(m_joy_irq_state));
	save_item(NAME(m_joy_d7_state));
	save_item(NAME(m_joy_analog_state));
	save_item(NAME(m_joy_analog_reload));
}

void bit90_state::machine_start()
{
	coleco_state::machine_start();
	uint8_t *banked = memregion("banked")->base();
	m_bank->configure_entries(0, 0x02, banked, 0x2000);
}

void coleco_state::machine_reset()
{
	m_last_nmi_state = 0;
}

void bit90_state::machine_reset()
{
	coleco_state::machine_reset();
	m_bank->set_entry(0);
}

//static image_verify_result coleco_cart_verify(const uint8_t *cartdata, size_t size)
//{
//  int retval = image_verify_result::FAIL;
//
//  /* Verify the file is in Colecovision format */
//  if ((cartdata[0] == 0xAA) && (cartdata[1] == 0x55)) /* Production Cartridge */
//      retval = image_verify_result::PASS;
//  if ((cartdata[0] == 0x55) && (cartdata[1] == 0xAA)) /* "Test" Cartridge. Some games use this method to skip ColecoVision title screen and delay */
//      retval = image_verify_result::PASS;
//
//  return retval;
//}


/* Machine Drivers */

void coleco_state::coleco(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(7'159'090)/2); // 3.579545 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &coleco_state::coleco_map);
	m_maincpu->set_addrmap(AS_IO, &coleco_state::coleco_io_map);

	/* video hardware */
	tms9928a_device &vdp(TMS9928A(config, "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(FUNC(coleco_state::coleco_vdp_interrupt));
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	sn76489a_device &psg(SN76489A(config, "sn76489a", XTAL(7'159'090)/2)); // 3.579545 MHz
	psg.add_route(ALL_OUTPUTS, "mono", 1.00);
	// TODO: enable when Z80 has better WAIT pin emulation, this currently breaks pitfall2 for example
	//psg.ready_cb().set_inputline("maincpu", Z80_INPUT_LINE_WAIT).invert();

	/* cartridge */
	COLECOVISION_CARTRIDGE_SLOT(config, m_cart, colecovision_cartridges, nullptr);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("coleco");

	TIMER(config, "paddle_timer").configure_periodic(FUNC(coleco_state::paddle_update_callback), attotime::from_msec(20));
}

void coleco_state::colecop(machine_config &config)
{
	coleco(config);

	/* video hardware */
	tms9929a_device &vdp(TMS9929A(config.replace(), "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(FUNC(coleco_state::coleco_vdp_interrupt));
}

void bit90_state::bit90(machine_config &config)
{
	/* basic machine hardware */
	Z80(config, m_maincpu, XTAL(7'159'090)/2); // 3.579545 MHz
	m_maincpu->set_addrmap(AS_PROGRAM, &bit90_state::bit90_map);
	m_maincpu->set_addrmap(AS_IO, &bit90_state::bit90_io_map);

	/* video hardware */
	tms9929a_device &vdp(TMS9929A(config, "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(FUNC(coleco_state::coleco_vdp_interrupt));
	SCREEN(config, "screen", SCREEN_TYPE_RASTER);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	sn76489a_device &psg(SN76489A(config, "sn76489a", XTAL(7'159'090)/2)); // 3.579545 MHz
	psg.add_route(ALL_OUTPUTS, "mono", 1.00);
	// TODO: enable when Z80 has better WAIT pin emulation, this currently breaks pitfall2 for example
	//psg.ready_cb().set_inputline("maincpu", Z80_INPUT_LINE_WAIT).invert();

	/* cartridge */
	COLECOVISION_CARTRIDGE_SLOT(config, m_cart, colecovision_cartridges, nullptr);

	/* software lists */
	SOFTWARE_LIST(config, "cart_list").set_original("coleco");

	TIMER(config, "paddle_timer").configure_periodic(FUNC(coleco_state::paddle_update_callback), attotime::from_msec(20));
}

void coleco_state::czz50(machine_config &config)
{
	coleco(config);

	/* basic machine hardware */
	m_maincpu->set_addrmap(AS_PROGRAM, &coleco_state::czz50_map); // note: cpu speed unverified, assume it's the same as ColecoVision
}

void coleco_state::dina(machine_config &config)
{
	czz50(config);

	/* video hardware */
	tms9929a_device &vdp(TMS9929A(config.replace(), "tms9928a", XTAL(10'738'635)));
	vdp.set_screen("screen");
	vdp.set_vram_size(0x4000);
	vdp.int_callback().set(FUNC(coleco_state::coleco_vdp_interrupt));
}


/* ROMs */

ROM_START (coleco)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_SYSTEM_BIOS( 0, "original", "Original" )
	ROMX_LOAD( "313 10031-4005 73108a.u2", 0x0000, 0x2000, CRC(3aa93ef3) SHA1(45bedc4cbdeac66c7df59e9e599195c778d86a92), ROM_BIOS(0) )
	ROM_SYSTEM_BIOS( 1, "thick", "Thick characters" )
	// differences to 0x3aa93ef3 modified characters, added a pad 2 related fix
	ROMX_LOAD( "colecoa.rom", 0x0000, 0x2000, CRC(39bb16fc) SHA1(99ba9be24ada3e86e5c17aeecb7a2d68c5edfe59), ROM_BIOS(1) )
ROM_END

/*  ONYX (Prototype)
    Unreleased Brazilian Colecovision clone by Microdigital.

    It was never released and the only known prototypes were uncovered by an ex-employee of Microdigital
    called Cl??udio Cassens who donated it to collectors (Eduardo Luccas) in June 2015.
    -- Felipe Sanches
*/
ROM_START (onyx)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "onyx.rom", 0x0000, 0x2000, CRC(011c32e7) SHA1(f44263221e330b2590dffc1a6f43ed2591fe19be) )
ROM_END

/*  PAL Colecovision BIOS

Country: Italy
Serial number: C0039036
Model number: 240020
Circuit board: (C) 1983 91162 rev D

Information about the chip

Motorola logo
(C)1983 COLECO
R72114A
8317   */

ROM_START (colecop)
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "r72114a_8317.u2", 0x0000, 0x2000, CRC(d393c0cc) SHA1(160077afb139943725c634d6539898db59f33657) )
ROM_END

ROM_START( czz50 )
	ROM_REGION( 0x10000, "maincpu", 0 )
	ROM_LOAD( "czz50.rom", 0x0000, 0x2000, CRC(4999abc6) SHA1(96aecec3712c94517103d894405bc98a7dafa440) )
	ROM_CONTINUE( 0x8000, 0x2000 )
ROM_END

#define rom_dina rom_czz50
#define rom_prsarcde rom_czz50

/* BIT90 BIOS

Circuit board is labelled: BIT90C-PAL-90002

Units have 2764-compatible pinouts at U2,U3, and U4
Some units have 2764 EPROMS, Mask ROMs, or a combination

Mask ROMs are labelled:

@U4:
D32351E
BIT-99C1

@U3:
D32521E
MONITOR2

@U2:
D32522E
MONITOR3

*/

ROM_START( bit90 )
	ROM_DEFAULT_BIOS( "3.0" )
	ROM_SYSTEM_BIOS( 0, "3.0", "BASIC 3.0" )
	ROM_SYSTEM_BIOS( 1, "3.1", "BASIC 3.1" )

	ROM_REGION( 0x10000, "maincpu", 0 )
	ROMX_LOAD("bit90b3.u2",  0x2000, 0x2000, CRC(b992b940) SHA1(c7dd96a1944fac40cbae20630f303a69de7e6313), ROM_BIOS(0))
	ROMX_LOAD("d32522e.u2",  0x2000, 0x2000, NO_DUMP, ROM_BIOS(1)) // MONITOR3

	ROM_REGION( 0x4000, "banked", 0 )
	ROMX_LOAD("bit90b3.u4", 0x0000, 0x2000, CRC(06d21fc2) SHA1(6d296b09b661babd4c2ef6993f8e768a67932388), ROM_BIOS(0))
	ROMX_LOAD("bit90b3.u3", 0x2000, 0x2000, CRC(61fdccbb) SHA1(25cac13627c0916d3ed2b92f0b2218b405de5be4), ROM_BIOS(0))
	ROMX_LOAD("d32351e.u4", 0x0000, 0x2000, NO_DUMP, ROM_BIOS(1)) // BIT-99C1
	ROMX_LOAD("d32521e.u3", 0x2000, 0x2000, NO_DUMP, ROM_BIOS(1)) // MONITOR2
ROM_END

/* System Drivers */

//    YEAR  NAME      PARENT  COMPAT  MACHINE   INPUT   CLASS         INIT        COMPANY             FULLNAME                            FLAGS
CONS( 1982, coleco,   0,      0,      coleco,   coleco, coleco_state, empty_init, "Coleco",           "ColecoVision (NTSC)",              0 )
CONS( 1982, onyx,     coleco, 0,      coleco,   coleco, coleco_state, empty_init, "Microdigital",     "Onyx (Brazil/Prototype)",          0 )
CONS( 1983, colecop,  coleco, 0,      colecop,  coleco, coleco_state, empty_init, "Coleco",           "ColecoVision (PAL)",               0 )
CONS( 1986, czz50,    0,      coleco, czz50,    czz50,  coleco_state, empty_init, "Bit Corporation",  "Chuang Zao Zhe 50",                0 )
CONS( 1988, dina,     czz50,  0,      dina,     czz50,  coleco_state, empty_init, "Telegames",        "Dina",                             0 )
CONS( 1988, prsarcde, czz50,  0,      czz50,    czz50,  coleco_state, empty_init, "Telegames",        "Personal Arcade",                  0 )
COMP( 1983, bit90,    0,      coleco, bit90,    bit90,  bit90_state,  empty_init, "Bit Corporation",  "Bit90",                            0 )