summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/drivers/intellec4.cpp
blob: ed1d554c6d5c82758ae6ef03a20ebe49fdcecad1 (plain) (tree)
1
2
3
                       

                              














                                                                      
 





                                                                         








                                                                        
                                                         
                                                                       
                                                              

      
                              


                

                            
                                    

                            
                            
 
                     

                      


           




                                                                        

       
                                                                            
 



                                        

                                      


                                      



                                        





                                        
                                 


                                                  
                               
                                               
                                                    
                                                                        






                                                    

                                                   
 
          







                                                                                         
                                                     




                                                                      
                                               




                                                                            


         
                                             
                                             
 











                                                        




                                                                      
        


                                 
                              
                             


                                
                             
                                
 
                                 
                                  

                           







                                  



                                      
          
 

                                             
                             





                                                    


                                
 
                                                                                      
                                                                      
                                                          
 

                                                                              
                                                   




                                                      
 
                                             
 
                                
                                                 

                                    


                                                   
                              




                                                                   
 

                                                         
                                                                              





                                                                                        
                                                
                                                            
                                                           
                                 


  
                                    






































                                                                                                                                                                                                        





                                                                                                                                                                                                        
               
 





                                                                                    






                                                                 
                                                                              







                                                            
                                                                                          


























































                                                                                                                                  





                                      
 
                                               
         


                                                                                   
         
 


                                                                                              
 
                                        
 







                                                                                                               

 





                                      
 
                                                         
                                              

 











                                                        
                                      
 

                                         
 
 
                                      
 

                                         

 



























                                                                    




                                                          
 
                                        
 

                                                                 

 
                                        
 

                                                           

 



                                                                               



                                                      

























                                                  




                                   
 
                                                    








                                                              
                                                                        
 


                                                                                                              
 

                              
                                                                                                                             
                 
                                              
                         

                                                                        
                                                                                                    





                                                                 
                                                                    
                 





                                                                                             
         
                                         

 
                                             





                                 
                                                   



                                    
                                              








                                                                 
                                              








                                                                 
                                              








                                                                   
                                                    





                                    
                                                   




                                           









                                                   
 




                                    
 
                                                                                                                              
 





                                    


                                     
 


                                     
                                     



                                          
 


                                         


                                        
                                       
                                     






                                           
                                    
                                         
                                       
                                  
 


                                                    
 
                                    

                                                              
                                                                                        










                                                                                      







                                                                     

                                                            

                                                        

                                                            
                                                                                        
                                                 
                                                                       
 






                                                                                      
                               




                                                                                

 
 
                                    


                                    


                                                               

                              
                                                            



                               
                                                    

                                                                                     
 
 


                                                                

                              





                                                                                                                     





                                  
 





                                   


































                                                                                                      

















                                                                           
                                                             





                                                           
                                                              





                                                           
                                                            
 
                                                                             

                                                                 
 







                                                                                                












                                                                                                       



                                    

                                    
 
                                                     
 




                                                                    




                                                
         

 
 


                                    
 
                                                          


                                                        











                                                      

 
                                                            


                                                         







                                                          

 
                                                   
 



                                             
 
 
                                                          


                                                        







                                                       

 
                                                        


                                                       







                                                     


 


                                    
 





















                                                                                                              
 








                                                                                            
 












                                                                                                      
 
 
 


                                                                        
 






                                                                                    
 

                                            
 


                                                  
 
                                          
 


                                             
 





                                
 
                                                
 
                                                
 

                                   
 

                                            
 
                                                

                                  
 
 
                                    









                                                                                                                                                                                    
 
 


                                    
 













































                                                                                                           


                                    

                                      
                                                         






                                                                 
                                                                                      

                                 
                                                                               





                                           






























































                                                                                                                                    
                                                           












                                                     
                                           
 












                                                   

                                         













                                                         

                                    
 

                               
 



                                                                                                                                                                                     

 


                                    
 














                                                                                   
                                      

                                          
 
 



























                                                                                        
                                




                                                                                                 
 






















                                                                                                           


                                    

                                        
                                                         






                                                                  

                                                                                      

                                 

                                                                                





                                            






                                        

                                   




























                                                                                                                                      
                                      































                                                                                                                                       

                   
                                                     

                                                
                                                                                                                                       



                                                                                                                                       
                                                                                                                                       






                                                                                                                                       




                                                                        


                                                                                                                                                                                       
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*
 Intel INTELLEC® 4

 The MOD 4 system has a 4004/4008/4009 chipset.  It has front panel
 switches for holding or pulsing the CPU's TEST line, and a CPU LED
 driven by clock phase 2.

 The MOD 40 system has a 4040/4289 chipset.  It replaces the TEST
 switches with STOP and SINGLE STEP switches, and replaces the CPU LED
 with a RUN LED that's lit when the CPU isn't acknowledging a STOP or
 HALT condition.

 The MOD 4 and MOD 40 systems use the same monitor PROM.  The monitor
 program doesn't use any 4040-specific features, and the console RAM
 readback feature avoids the need for the RPM instruction by latching
 the data and reading it through the ROM port space.

 Strictly speaking, the INTELLEC® 4 and INTELLEC® 4/MOD 4 are different
 machines.  The former uses an earlier control board without support
 for MOD 40 features; the latter uses the same control board as the
 MOD 40 jumpered to provide TEST line control rather than 4040 STOP
 and SINGLE STEP features.

 The MOD 40 repurposes the pins originally used for ROM 1 input port
 lines to expose stop/interrupt request/acknowledge.  It's pretty
 obvious that the MOD 40 was designed as a minimal modification.  A
 set of X1 display LEDs would have been fantastic, as it would show the
 contents of the accumulator with the 4040 CPU, while the X3 LEDs really
 aren't very useful.  However, since the 4004 just echoes M2 during X1,
 the control board has no provision for latching data during this cycle
 and would have required significant layout changes to cater for this.

 Set the terminal for 110 1/8/N/2 to talk to the monitor.
 It only accepts uppercase letters, digits, comma, and carriage return.
 Paper tape reader run/stop is sent to RTS on the serial port.

 TODO:
 * Expose general-purpose I/O?
 */
#include "emu.h"

#include "machine/imm6_76.h"

#include "bus/intellec4/intellec4.h"
#include "bus/rs232/rs232.h"
#include "cpu/mcs40/mcs40.h"
#include "machine/bankdev.h"

#include "intlc44.lh"
#include "intlc440.lh"


namespace {

/***********************************************************************
    INTELLEC® 4 base driver
***********************************************************************/

class intellec4_state : public driver_device
{
public:
	void bus_cycle(mcs40_cpu_device_base::phase step, u8 sync, u8 data);

	DECLARE_READ8_MEMBER(pm_read);
	DECLARE_WRITE8_MEMBER(pm_write);

	DECLARE_READ8_MEMBER(rom0_in);
	DECLARE_READ8_MEMBER(rom2_in);
	DECLARE_READ8_MEMBER(rom3_in);
	DECLARE_READ8_MEMBER(rome_in);
	DECLARE_READ8_MEMBER(romf_in);

	DECLARE_WRITE8_MEMBER(rom0_out);
	DECLARE_WRITE8_MEMBER(rom1_out);
	DECLARE_WRITE8_MEMBER(rom2_out);
	DECLARE_WRITE8_MEMBER(rom3_out);
	DECLARE_WRITE8_MEMBER(rome_out);
	DECLARE_WRITE8_MEMBER(romf_out);

	DECLARE_WRITE8_MEMBER(ram0_out);
	DECLARE_WRITE8_MEMBER(ram1_out);

	// universal slot outputs
	DECLARE_WRITE_LINE_MEMBER(bus_reset_4002);
	DECLARE_WRITE_LINE_MEMBER(bus_user_reset);

	// front panel switches
	DECLARE_INPUT_CHANGED_MEMBER(sw_reset);
	DECLARE_INPUT_CHANGED_MEMBER(sw_reset_mode);
	template <unsigned N> DECLARE_INPUT_CHANGED_MEMBER(sw_prg_mode);
	DECLARE_INPUT_CHANGED_MEMBER(sw_run);
	DECLARE_INPUT_CHANGED_MEMBER(sw_next_inst);
	DECLARE_INPUT_CHANGED_MEMBER(sw_decr);
	DECLARE_INPUT_CHANGED_MEMBER(sw_incr);
	DECLARE_INPUT_CHANGED_MEMBER(sw_load);
	DECLARE_INPUT_CHANGED_MEMBER(sw_cma_enable);
	DECLARE_INPUT_CHANGED_MEMBER(sw_cma_write);
	DECLARE_INPUT_CHANGED_MEMBER(sw_prgm_pwr);
	DECLARE_INPUT_CHANGED_MEMBER(sw_do_enable);

protected:
	intellec4_state(machine_config const &mconfig, device_type type, char const *tag)
		: driver_device(mconfig, type, tag)
		, m_cpu(*this, "maincpu")
		, m_bus(*this, "bus")
		, m_prg_ram(*this, "ram")
		, m_sw_mode(*this, "MODE")
		, m_program_banks(*this, "prgbank")
		, m_rom_port_banks(*this, "rpbank")
		, m_prom_programmer(*this, "promprg")
		, m_tty(*this, "tty")
		, m_memory(*this, "memory"), m_status(*this, "status")
		, m_sw_control(*this, "CONTROL")
		, m_sw_addr_data(*this, "ADDRDAT")
		, m_sw_passes(*this, "PASSES")
		, m_sw_prom_prgm(*this, "PROM")
		, m_led_address(*this, "led_address_a%u_%u", 1U, 0U)
		, m_led_instruction(*this, "led_instruction_m%u_%u", 1U, 0U)
		, m_led_active_bank(*this, "led_active_bank_%u", 0U)
		, m_led_execution(*this, "led_execution_x%u_%u", 2U, 0U)
		, m_led_last_ptr(*this, "led_last_ptr_x%u_%u", 2U, 0U)
	{
	}

	virtual void driver_start() override;
	virtual void driver_reset() override;

	void intellec4_program_banks(address_map &map);
	void intellec4_rom_port_banks(address_map &map);

	void intellec4_rom(address_map &map);
	void intellec4_ram_memory(address_map &map);
	void intellec4_rom_ports(address_map &map);
	void intellec4_ram_status(address_map &map);
	void intellec4_ram_ports(address_map &map);
	void intellec4_program_memory(address_map &map);

	void intellec4(machine_config &config);

	required_device<mcs40_cpu_device_base>              m_cpu;
	required_device<bus::intellec4::univ_bus_device>    m_bus;
	required_shared_ptr<u8>                             m_prg_ram;
	required_ioport                                     m_sw_mode;

private:
	enum
	{
		BANK_PRG_MON = 0,
		BANK_PRG_PROM,
		BANK_PRG_RAM,
		BANK_PRG_NONE,

		BANK_IO_MON = 0,
		BANK_IO_PROM,
		BANK_IO_NEITHER,

		BIT_SW_RESET = 2,
		BIT_SW_RESET_MODE,
		BIT_SW_MON,
		BIT_SW_RAM,
		BIT_SW_PROM,

		BIT_SW_RUN = 0,
		BIT_SW_NEXT_INST,
		BIT_SW_DECR,
		BIT_SW_INCR,
		BIT_SW_LOAD,
		BIT_SW_CMA_ENABLE,
		BIT_SW_CMA_WRITE,

		BIT_SW_PRGM_PWR = 0,
		BIT_SW_DATA_OUT_ENABLE
	};

	TIMER_CALLBACK_MEMBER(reset_expired);

	// LED update helpers
	void display_address(u16 value, u16 mask);
	void display_instruction(u8 value, u8 mask);
	void display_active_bank(u8 value);
	void display_execution(u8 value, u8 mask);
	void display_pointer(u8 value, u8 mask);

	void trigger_reset();
	void check_4002_reset();
	void reset_panel();

	required_device<address_map_bank_device>    m_program_banks, m_rom_port_banks;
	required_device<intel_imm6_76_device>       m_prom_programmer;
	required_device<rs232_port_device>          m_tty;

	required_shared_ptr<u8>     m_memory, m_status;
	required_ioport             m_sw_control, m_sw_addr_data, m_sw_passes;
	required_ioport             m_sw_prom_prgm;
	output_finder<3, 4>         m_led_address;
	output_finder<2, 4>         m_led_instruction;
	output_finder<4>            m_led_active_bank;
	output_finder<2, 4>         m_led_execution;
	output_finder<2, 4>         m_led_last_ptr;

	emu_timer   *m_reset_timer = nullptr;

	// program memory access
	u8      m_ram_page = 0U, m_ram_data = 0U;
	bool    m_ram_write = false;

	// PROM programmer
	u8      m_prom_addr = 0U, m_prom_data = 0U;

	// control board state
	bool    m_cpu_reset = false;
	bool    m_ff_prg_mode[3] = { false, false, false };

	// current state of signals from bus
	bool    m_bus_reset_4002 = false, m_bus_user_reset = false;

	// front panel state
	u16     m_latched_addr = 0U, m_display_addr = 0U;
	u8      m_display_instr = 0U, m_display_exec = 0U, m_display_ptr = 0U;
	u8      m_pass_counter = 0U;
	bool    m_panel_reset = false;
	bool    m_next_inst = false, m_adr_cmp_latch = false, m_search_complete = false;
	bool    m_src = false, m_pointer_valid = false;
	bool    m_cma_enable = false, m_cma_write = false;

	// current state of front panel switches
	bool    m_sw_reset = false, m_sw_reset_mode = false;
	bool    m_sw_prg_mode[3] = { false, false, false };
	bool    m_sw_run = false;
};


/*----------------------------------
  Common front panel switches
----------------------------------*/

INPUT_PORTS_START(intellec4)
	PORT_START("MODE")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW,  IPT_KEYPAD )             PORT_NAME("RESET")                                         PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_reset,       nullptr)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("RESET MODE")                                    PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_reset_mode,  nullptr)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW,  IPT_KEYPAD )             PORT_NAME("MON")              PORT_CODE(KEYCODE_1_PAD)     PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_prg_mode<0>, nullptr)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW,  IPT_KEYPAD )             PORT_NAME("RAM")              PORT_CODE(KEYCODE_2_PAD)     PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_prg_mode<1>, nullptr)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW,  IPT_KEYPAD )             PORT_NAME("PROM")             PORT_CODE(KEYCODE_3_PAD)     PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_prg_mode<2>, nullptr)

	PORT_START("CONTROL")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("RUN")                                           PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_run,         nullptr)
	PORT_BIT( 0x0002, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("NEXT INST")                                     PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_next_inst,   nullptr)
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("DECR")             PORT_CODE(KEYCODE_MINUS_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_decr,        nullptr)
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("INCR")             PORT_CODE(KEYCODE_PLUS_PAD)  PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_incr,        nullptr)
	PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("LOAD")             PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_load,        nullptr)
	PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("CMA ENABLE")                                    PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_cma_enable,  nullptr)
	PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("CMA WRITE")        PORT_CODE(KEYCODE_SLASH_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_cma_write,   nullptr)

	PORT_START("ADDRDAT")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 0")
	PORT_BIT( 0x0002, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 1")
	PORT_BIT( 0x0004, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 2")
	PORT_BIT( 0x0008, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 3")
	PORT_BIT( 0x0010, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 4")
	PORT_BIT( 0x0020, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 5")
	PORT_BIT( 0x0040, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 6")
	PORT_BIT( 0x0080, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 7")
	PORT_BIT( 0x0100, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 8")
	PORT_BIT( 0x0200, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 9")
	PORT_BIT( 0x0400, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 10")
	PORT_BIT( 0x0800, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("ADDRESS/DATA 11")

	PORT_START("PASSES")
	PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 0")
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 1")
	PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 2")
	PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 3")

	PORT_START("PROM")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PRGM PROM PWR")                                 PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_prgm_pwr,    nullptr)
	PORT_CONFNAME( 0x0002, 0x0002, "PROM PROGRAMMER DATA OUT ENABLE" )                                                    PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_do_enable,   nullptr)
	PORT_CONFSETTING(      0x0000, DEF_STR(Off) )
	PORT_CONFSETTING(      0x0002, DEF_STR(On)  )
INPUT_PORTS_END


/*----------------------------------
  System bus cycle handler
----------------------------------*/

void intellec4_state::bus_cycle(mcs40_cpu_device_base::phase step, u8 sync, u8 data)
{
	switch (step)
	{
	case mcs40_cpu_device_base::phase::A1:
		if (m_cma_enable)
		{
			display_address(m_latched_addr, 0x0fffU);
			display_instruction(m_prg_ram[m_latched_addr], 0xffU);
		}
		else if (!m_search_complete)
		{
			display_address(u16(data), 0x000fU);
			m_src = false;
		}
		if (m_cma_write)
		{
			m_prg_ram[m_latched_addr] = u8(~m_sw_addr_data->read() & 0x00ffU);
			m_cma_write = false;
		}
		break;
	case mcs40_cpu_device_base::phase::A2:
		if (!m_search_complete && !m_cma_enable)
			display_address(u16(data) << 4, 0x00f0U);
		break;
	case mcs40_cpu_device_base::phase::A3:
		if (!m_search_complete && !m_cma_enable)
		{
			display_address(u16(data) << 8, 0x0f00U);
			display_active_bank(~m_cpu->get_cm_ram());
		}
		break;
	case mcs40_cpu_device_base::phase::M1:
		if (!m_search_complete && !m_cma_enable)
			display_instruction(data << 4, 0xf0U);
		break;
	case mcs40_cpu_device_base::phase::M2:
		if (!m_search_complete && !m_cma_enable)
			display_instruction(data, 0x0fU);
		break;
	case mcs40_cpu_device_base::phase::X1:
		// not connected to anything
		break;
	case mcs40_cpu_device_base::phase::X2:
		if (!m_search_complete && !m_cma_enable)
		{
			display_execution(data << 4, 0xf0U);
			m_src = BIT(~m_cpu->get_cm_rom(), 0);
			if (m_src)
				display_pointer(data << 4, 0xf0U);
		}
		break;
	case mcs40_cpu_device_base::phase::X3:
		if (m_search_complete != m_adr_cmp_latch)
			machine().output().set_value("led_status_search", m_search_complete = m_adr_cmp_latch);
		if (!m_search_complete && !m_cma_enable)
		{
			display_execution(data, 0x0fU);
			if (m_src)
			{
				display_pointer(data, 0x0fU);
				if (!m_panel_reset && !m_pointer_valid)
					machine().output().set_value("led_status_ptr_valid", m_pointer_valid = true);
			}
		}
		if (!m_panel_reset && !m_adr_cmp_latch)
			m_adr_cmp_latch = (m_latched_addr == m_display_addr) && !((m_pass_counter ^ m_sw_passes->read()) & 0x0fU);
		if (!m_search_complete && !m_panel_reset && !m_cma_enable && !m_sw_run && (m_latched_addr == m_display_addr))
			m_pass_counter = (m_pass_counter + 1U) & 0x0fU;
		if (m_adr_cmp_latch && !m_next_inst && !m_search_complete)
			machine().output().set_value("led_status_search", m_search_complete = true);
		if (!m_cpu_reset && !m_cma_enable && !m_sw_run)
			m_panel_reset = false;
		break;
	}
}


/*----------------------------------
  Program memory access handlers
----------------------------------*/

READ8_MEMBER(intellec4_state::pm_read)
{
	if (!machine().side_effects_disabled())
	{
		// always causes data to be latched
		u16 const addr((u16(m_ram_page) << 8) | ((offset >> 1) & 0x00ffU));
		m_ram_data = m_prg_ram[addr];
	}

	// the C outputs of the 4289 are always high for RPM/WPM so it's equivalent to romf_in
	return m_ram_data & 0x0fU;
}

WRITE8_MEMBER(intellec4_state::pm_write)
{
	// always causes data to be latched
	u16 const addr((u16(m_ram_page) << 8) | ((offset >> 1) & 0x00ffU));
	m_ram_data = m_prg_ram[addr];
	if (m_ram_write)
	{
		bool const first(BIT(offset, 0));
		m_prg_ram[addr] = (m_ram_data & (first ? 0x0fU : 0xf0U)) | ((data & 0x0fU) << (first ? 4 : 0));
	}
}


/*----------------------------------
  I/O port handlers
----------------------------------*/

READ8_MEMBER(intellec4_state::rom0_in)
{
	// bit 0 of this port is ANDed with the TTY input
	return m_tty->rxd_r() ? 0x0eU : 0x0fU;
}

READ8_MEMBER(intellec4_state::rom2_in)
{
	// lower nybble of PROM programmer data
	return m_prom_programmer->do_r() & 0x0fU;
}

READ8_MEMBER(intellec4_state::rom3_in)
{
	// upper nybble of PROM programmer data
	return (m_prom_programmer->do_r() >> 4) & 0x0fU;
}

READ8_MEMBER(intellec4_state::rome_in)
{
	// upper nybble of RAM data latch
	return (m_ram_data >> 4) & 0x0fU;
}

READ8_MEMBER(intellec4_state::romf_in)
{
	// lower nybble of RAM data latch
	return m_ram_data & 0x0fU;
}

WRITE8_MEMBER(intellec4_state::rom0_out)
{
	// lower nybble of PROM programmer address
	m_prom_addr = (m_prom_addr & 0xf0U) | (data & 0x0fU);
	m_prom_programmer->a_w(m_prom_addr);
}

WRITE8_MEMBER(intellec4_state::rom1_out)
{
	// upper nybble of PROM programmer address
	m_prom_addr = (m_prom_addr & 0x0fU) | ((data << 4) & 0xf0U);
	m_prom_programmer->a_w(m_prom_addr);
}

WRITE8_MEMBER(intellec4_state::rom2_out)
{
	// lower nybble of PROM programmer data
	m_prom_data = (m_prom_data & 0xf0U) | (data & 0x0fU);
	m_prom_programmer->di_w(m_prom_data);
}

WRITE8_MEMBER(intellec4_state::rom3_out)
{
	// upper nybble of PROM programmer data
	m_prom_data = (m_prom_data & 0x0fU) | ((data << 4) & 0xf0U);
	m_prom_programmer->di_w(m_prom_data);
}

WRITE8_MEMBER(intellec4_state::rome_out)
{
	// bit 0 of this port enables program memory write
	m_ram_write = BIT(data, 0);
}

WRITE8_MEMBER(intellec4_state::romf_out)
{
	// sets the program memory page for read/write operations
	m_ram_page = data & 0x0fU;
}

WRITE8_MEMBER(intellec4_state::ram0_out)
{
	// bit 0 of this port controls the TTY current loop
	m_tty->write_txd(BIT(data, 0));
}

WRITE8_MEMBER(intellec4_state::ram1_out)
{
	// bit 0 of this port controls the paper tape motor (0 = stop, 1 = run)
	m_tty->write_rts(BIT(~data, 0));

	// bits 1 and 2 of this port enable PROM write
	m_prom_programmer->r_w_a(BIT(~data, 1));
	m_prom_programmer->r_w(BIT(~data, 2));
}


/*----------------------------------
  Bus signal handlers
----------------------------------*/

WRITE_LINE_MEMBER(intellec4_state::bus_reset_4002)
{
	m_bus_reset_4002 = 0 == state;
	check_4002_reset();
}

WRITE_LINE_MEMBER(intellec4_state::bus_user_reset)
{
	if (!state)
		trigger_reset();
	m_bus_user_reset = 0 == state;
}


/*----------------------------------
  Front panel switch handlers
----------------------------------*/

INPUT_CHANGED_MEMBER(intellec4_state::sw_reset)
{
	if (!newval && oldval)
		trigger_reset();
	m_sw_reset = !bool(newval);
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_reset_mode)
{
	m_sw_reset_mode = bool(newval);
	if (m_cpu_reset)
	{
		m_bus->reset_4002_in(m_sw_reset_mode ? 0 : 1);
		check_4002_reset();
	}
}

template <unsigned N> INPUT_CHANGED_MEMBER(intellec4_state::sw_prg_mode)
{
	static constexpr char const *const mode_leds[3] = { "led_mode_mon", "led_mode_ram", "led_mode_prom" };
	static constexpr int prg_banks[3] = { BANK_PRG_MON, BANK_PRG_RAM, BANK_PRG_PROM };
	static constexpr int io_banks[3] = { BANK_IO_MON, BANK_IO_NEITHER, BANK_IO_PROM };

	if (oldval && !newval)
	{
		if (((0U == N) || !m_sw_prg_mode[0]) && ((1U == N) || !m_sw_prg_mode[1]) && ((2U == N) || !m_sw_prg_mode[2]))
		{
			if (!m_ff_prg_mode[N])
			{
				m_program_banks->set_bank(prg_banks[N]);
				m_rom_port_banks->set_bank(io_banks[N]);
				machine().output().set_value(mode_leds[N], m_ff_prg_mode[N] = true);
			}
			trigger_reset();
		}
		else
		{
			m_program_banks->set_bank(BANK_PRG_NONE);
			m_rom_port_banks->set_bank(BANK_IO_NEITHER);
		}
		if ((0U != N) && m_ff_prg_mode[0])
			machine().output().set_value(mode_leds[0], m_ff_prg_mode[0] = false);
		if ((1U != N) && m_ff_prg_mode[1])
			machine().output().set_value(mode_leds[1], m_ff_prg_mode[1] = false);
		if ((2U != N) && m_ff_prg_mode[2])
			machine().output().set_value(mode_leds[2], m_ff_prg_mode[2] = false);
	}
	m_sw_prg_mode[N] = !bool(newval);
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_run)
{
	m_sw_run = !bool(newval);
	if (m_sw_run)
		reset_panel();
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_next_inst)
{
	m_next_inst = !bool(newval);
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_decr)
{
	// connected to a pulse generator circuit
	if (newval && !oldval)
	{
		m_latched_addr = (m_latched_addr - 1U) & 0x0fffU;
		reset_panel();
	}
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_incr)
{
	// connected to a pulse generator circuit
	if (newval && !oldval)
	{
		m_latched_addr = (m_latched_addr + 1U) & 0x0fffU;
		reset_panel();
	}
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_load)
{
	// connected to a pulse generator circuit
	if (newval && !oldval)
	{
		m_latched_addr = ~m_sw_addr_data->read() & 0x0fffU;
		reset_panel();
	}
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_cma_enable)
{
	m_cma_enable = bool(newval);
	if (m_cma_enable)
		reset_panel();
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_cma_write)
{
	if (newval && !oldval)
		m_cma_write = m_cma_enable;
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_prgm_pwr)
{
	m_prom_programmer->prgm_prom_pwr(newval);
}

INPUT_CHANGED_MEMBER(intellec4_state::sw_do_enable)
{
	m_prom_programmer->data_out_enable(newval);
}


/*----------------------------------
  driver_device implementation
----------------------------------*/

void intellec4_state::driver_start()
{
	m_reset_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(intellec4_state::reset_expired), this));

	m_led_address.resolve();
	m_led_instruction.resolve();
	m_led_active_bank.resolve();
	m_led_execution.resolve();
	m_led_last_ptr.resolve();

	save_item(NAME(m_ram_page));
	save_item(NAME(m_ram_data));
	save_item(NAME(m_ram_write));

	save_item(NAME(m_prom_addr));
	save_item(NAME(m_prom_data));

	save_item(NAME(m_cpu_reset));
	save_item(NAME(m_ff_prg_mode));

	save_item(NAME(m_bus_reset_4002));
	save_item(NAME(m_bus_user_reset));

	save_item(NAME(m_latched_addr));
	save_item(NAME(m_display_addr));
	save_item(NAME(m_display_instr));
	save_item(NAME(m_display_exec));
	save_item(NAME(m_display_ptr));
	save_item(NAME(m_pass_counter));
	save_item(NAME(m_panel_reset));
	save_item(NAME(m_next_inst));
	save_item(NAME(m_adr_cmp_latch));
	save_item(NAME(m_search_complete));
	save_item(NAME(m_src));
	save_item(NAME(m_pointer_valid));
	save_item(NAME(m_cma_enable));
	save_item(NAME(m_cma_write));

	save_item(NAME(m_sw_reset));
	save_item(NAME(m_sw_reset_mode));
	save_item(NAME(m_sw_prg_mode));
	save_item(NAME(m_sw_run));

	m_ff_prg_mode[0] = true;
	m_ff_prg_mode[1] = m_ff_prg_mode[2] = false;
};

void intellec4_state::driver_reset()
{
	// set stuff according to initial state of front panel
	ioport_value const sw_mode(m_sw_mode->read()), sw_control(m_sw_control->read());
	m_sw_reset       = BIT(~sw_mode,    BIT_SW_RESET);
	m_sw_reset_mode  = BIT( sw_mode,    BIT_SW_RESET_MODE);
	m_sw_prg_mode[0] = BIT(~sw_mode,    BIT_SW_MON);
	m_sw_prg_mode[1] = BIT(~sw_mode,    BIT_SW_RAM);
	m_sw_prg_mode[2] = BIT(~sw_mode,    BIT_SW_PROM);
	m_sw_run         = BIT(~sw_control, BIT_SW_RUN);
	m_next_inst      = BIT(~sw_control, BIT_SW_NEXT_INST);
	m_cma_enable     = BIT( sw_control, BIT_SW_CMA_ENABLE);
	m_ff_prg_mode[0] = m_ff_prg_mode[0] && !m_sw_prg_mode[1] && !m_sw_prg_mode[2];
	m_ff_prg_mode[1] = m_ff_prg_mode[1] && !m_sw_prg_mode[0] && !m_sw_prg_mode[2];
	m_ff_prg_mode[2] = m_ff_prg_mode[2] && !m_sw_prg_mode[0] && !m_sw_prg_mode[1];
	m_panel_reset = m_panel_reset || m_cma_enable || m_sw_run;
	if (m_panel_reset)
	{
		m_pass_counter = 0U;
		m_adr_cmp_latch = false;
		m_search_complete = m_search_complete && m_next_inst;
		m_pointer_valid = false;
	}

	// ensure we're consistent with the state of the bus
	m_bus_reset_4002 = 0 == m_bus->reset_4002_out();
	m_bus_user_reset = 0 == m_bus->user_reset_out();

	// ensure device inputs are all in the correct state
	m_cpu->set_input_line(INPUT_LINE_RESET, m_cpu_reset ? ASSERT_LINE : CLEAR_LINE);
	m_bus->cpu_reset_in(m_cpu_reset ? 0 : 1);
	m_bus->reset_4002_in((m_cpu_reset && m_sw_reset_mode) ? 0 : 1);

	// set up the PROM programmer
	ioport_value const sw_prom_prgm(m_sw_prom_prgm->read());
	m_prom_programmer->data_out_enable(BIT(sw_prom_prgm, BIT_SW_DATA_OUT_ENABLE));
	m_prom_programmer->data_in_positive(1); // not jumpered in, onboard pullup
	m_prom_programmer->data_out_positive(0); // jumpered to ground
	m_prom_programmer->prgm_prom_pwr(BIT(sw_prom_prgm, BIT_SW_PRGM_PWR));

	// set front panel LEDs
	machine().output().set_value("led_status_ptr_valid", m_pointer_valid);
	machine().output().set_value("led_status_search",    m_search_complete);
	machine().output().set_value("led_mode_mon",         m_ff_prg_mode[0]);
	machine().output().set_value("led_mode_ram",         m_ff_prg_mode[1]);
	machine().output().set_value("led_mode_prom",        m_ff_prg_mode[2]);
}


/*----------------------------------
  System address spaces
----------------------------------*/

void intellec4_state::intellec4_program_banks(address_map &map)
{
	map.unmap_value_low();

	// 0x0000...0x0fff MON
	map(0x0000, 0x03ff).rom().region("monitor", 0x0000);

	// 0x1000...0x1fff PROM

	// 0x1000...0x1fff RAM
	map(0x2000, 0x2fff).readonly().share("ram");

	// 0x3000...0x3fff unmapped in case someone presses two mode switches at once
}

void intellec4_state::intellec4_rom_port_banks(address_map &map)
{
	map.unmap_value_high();

	// 0x0000...0x07ff MON
	map(0x0000, 0x000f).mirror(0x1f00).rw(this, FUNC(intellec4_state::rom0_in), FUNC(intellec4_state::rom0_out));
	map(0x0010, 0x001f).mirror(0x1f00).w(this, FUNC(intellec4_state::rom1_out));
	map(0x0020, 0x002f).mirror(0x1f00).rw(this, FUNC(intellec4_state::rom2_in), FUNC(intellec4_state::rom2_out));
	map(0x0030, 0x003f).mirror(0x1f00).rw(this, FUNC(intellec4_state::rom3_in), FUNC(intellec4_state::rom3_out));
	map(0x00e0, 0x00ef).mirror(0x1f00).rw(this, FUNC(intellec4_state::rome_in), FUNC(intellec4_state::rome_out));
	map(0x00f0, 0x00ff).mirror(0x1f00).rw(this, FUNC(intellec4_state::romf_in), FUNC(intellec4_state::romf_out));

	// 0x0800...0x0fff PROM

	// 0x1000...0x17ff neither

	// 0x1800...0x1fff unused
}


/*---------------------------------
  CPU views of address spaces
---------------------------------*/

void intellec4_state::intellec4_rom(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x0fff).m(m_program_banks, FUNC(address_map_bank_device::amap8));
}

void intellec4_state::intellec4_ram_memory(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x00ff).ram().share("memory"); // 4 * 4002
}

void intellec4_state::intellec4_rom_ports(address_map &map)
{
	map.unmap_value_high();
	map(0x0000, 0x07ff).m("rpbank", FUNC(address_map_bank_device::amap8));
}

void intellec4_state::intellec4_ram_status(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x003f).ram().share("status"); // 4 * 4002
}

void intellec4_state::intellec4_ram_ports(address_map &map)
{
	map(0x00, 0x00).w(this, FUNC(intellec4_state::ram0_out));
	map(0x01, 0x01).w(this, FUNC(intellec4_state::ram1_out));
}

void intellec4_state::intellec4_program_memory(address_map &map)
{
	map.unmap_value_low();
	map(0x0000, 0x01ff).rw(this, FUNC(intellec4_state::pm_read), FUNC(intellec4_state::pm_write));
}


/*----------------------------------
  Common machine configuration
----------------------------------*/

DEVICE_INPUT_DEFAULTS_START(tty)
	DEVICE_INPUT_DEFAULTS("RS232_TXBAUD",    0x00ff, RS232_BAUD_110)
	DEVICE_INPUT_DEFAULTS("RS232_RXBAUD",    0x00ff, RS232_BAUD_110)
	DEVICE_INPUT_DEFAULTS("RS232_STARTBITS", 0x00ff, RS232_STARTBITS_1)
	DEVICE_INPUT_DEFAULTS("RS232_DATABITS",  0x00ff, RS232_DATABITS_8)
	DEVICE_INPUT_DEFAULTS("RS232_PARITY",    0x00ff, RS232_PARITY_NONE)
	DEVICE_INPUT_DEFAULTS("RS232_STOPBITS",  0x00ff, RS232_STOPBITS_2)
	DEVICE_INPUT_DEFAULTS("TERM_CONF",       0x01c0, 0x0000)
	DEVICE_INPUT_DEFAULTS("FLOW_CONTROL",    0x0001, 0x0000)
DEVICE_INPUT_DEFAULTS_END

MACHINE_CONFIG_START(intellec4_state::intellec4)
	MCFG_DEVICE_ADD(m_program_banks, ADDRESS_MAP_BANK, 0)
	MCFG_DEVICE_PROGRAM_MAP(intellec4_program_banks)
	MCFG_ADDRESS_MAP_BANK_ENDIANNESS(ENDIANNESS_LITTLE)
	MCFG_ADDRESS_MAP_BANK_DATA_WIDTH(8)
	MCFG_ADDRESS_MAP_BANK_ADDR_WIDTH(14)
	MCFG_ADDRESS_MAP_BANK_STRIDE(0x1000)

	MCFG_DEVICE_ADD(m_rom_port_banks, ADDRESS_MAP_BANK, 0)
	MCFG_DEVICE_PROGRAM_MAP(intellec4_rom_port_banks)
	MCFG_ADDRESS_MAP_BANK_ENDIANNESS(ENDIANNESS_LITTLE)
	MCFG_ADDRESS_MAP_BANK_DATA_WIDTH(8)
	MCFG_ADDRESS_MAP_BANK_ADDR_WIDTH(14)
	MCFG_ADDRESS_MAP_BANK_STRIDE(0x1000)

	MCFG_DEVICE_ADD(m_prom_programmer, INTEL_IMM6_76, 0)

	MCFG_DEVICE_ADD(m_tty, RS232_PORT, default_rs232_devices, "terminal")
	MCFG_SLOT_OPTION_DEVICE_INPUT_DEFAULTS("terminal",   tty)
	MCFG_SLOT_OPTION_DEVICE_INPUT_DEFAULTS("null_modem", tty)

	MCFG_DEVICE_ADD(m_bus, INTELLEC4_UNIV_BUS, 518000. / 7)
	MCFG_INTELLEC4_UNIV_BUS_ROM_SPACE(m_program_banks, AS_PROGRAM)
	MCFG_INTELLEC4_UNIV_BUS_ROM_PORTS_SPACE(m_rom_port_banks, AS_PROGRAM)
	MCFG_INTELLEC4_UNIV_BUS_MEMORY_SPACE(m_cpu, mcs40_cpu_device_base::AS_RAM_MEMORY)
	MCFG_INTELLEC4_UNIV_BUS_STATUS_SPACE(m_cpu, mcs40_cpu_device_base::AS_RAM_STATUS)
	MCFG_INTELLEC4_UNIV_BUS_RAM_PORTS_SPACE(m_cpu, mcs40_cpu_device_base::AS_RAM_PORTS)
	MCFG_INTELLEC4_UNIV_BUS_RESET_4002_CB(WRITELINE(*this, intellec4_state, bus_reset_4002))
	MCFG_INTELLEC4_UNIV_BUS_USER_RESET_CB(WRITELINE(*this, intellec4_state, bus_user_reset))
	INTELLEC4_UNIV_SLOT(config, "j7",  5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, "imm4_90");
	INTELLEC4_UNIV_SLOT(config, "j8",  5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, "imm6_26");
	INTELLEC4_UNIV_SLOT(config, "j9",  5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j10", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j11", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j12", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j13", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j14", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j15", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j16", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j17", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j18", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
	INTELLEC4_UNIV_SLOT(config, "j19", 5.185_MHz_XTAL / 7, m_bus, intellec4_univ_cards, nullptr);
MACHINE_CONFIG_END


/*----------------------------------
  Timer handlers
----------------------------------*/

TIMER_CALLBACK_MEMBER(intellec4_state::reset_expired)
{
	if (m_cpu_reset)
	{
		m_cpu_reset = false;
		m_cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
		m_bus->cpu_reset_in(1);
		if (m_sw_reset_mode)
		{
			m_bus->reset_4002_in(1);
			check_4002_reset();
		}
	}
}


/*----------------------------------
  LED update helpers
----------------------------------*/

void intellec4_state::display_address(u16 value, u16 mask)
{
	u16 const diff((value ^ m_display_addr) & mask);
	m_display_addr ^= diff;
	m_led_address[0][0] = BIT(m_display_addr, 0);
	m_led_address[0][1] = BIT(m_display_addr, 1);
	m_led_address[0][2] = BIT(m_display_addr, 2);
	m_led_address[0][3] = BIT(m_display_addr, 3);
	m_led_address[1][0] = BIT(m_display_addr, 4);
	m_led_address[1][1] = BIT(m_display_addr, 5);
	m_led_address[1][2] = BIT(m_display_addr, 6);
	m_led_address[1][3] = BIT(m_display_addr, 7);
	m_led_address[2][0] = BIT(m_display_addr, 8);
	m_led_address[2][1] = BIT(m_display_addr, 9);
	m_led_address[2][2] = BIT(m_display_addr, 10);
	m_led_address[2][3] = BIT(m_display_addr, 11);
}

void intellec4_state::display_instruction(u8 value, u8 mask)
{
	u16 const diff((value ^ m_display_instr) & mask);
	m_display_instr ^= diff;
	m_led_instruction[1][0] = BIT(m_display_instr, 0);
	m_led_instruction[1][1] = BIT(m_display_instr, 1);
	m_led_instruction[1][2] = BIT(m_display_instr, 2);
	m_led_instruction[1][3] = BIT(m_display_instr, 3);
	m_led_instruction[0][0] = BIT(m_display_instr, 4);
	m_led_instruction[0][1] = BIT(m_display_instr, 5);
	m_led_instruction[0][2] = BIT(m_display_instr, 6);
	m_led_instruction[0][3] = BIT(m_display_instr, 7);
}

void intellec4_state::display_active_bank(u8 value)
{
	m_led_active_bank[0] = BIT(value, 0);
	m_led_active_bank[1] = BIT(value, 1);
	m_led_active_bank[2] = BIT(value, 2);
	m_led_active_bank[3] = BIT(value, 3);
}

void intellec4_state::display_execution(u8 value, u8 mask)
{
	u16 const diff((value ^ m_display_exec) & mask);
	m_display_exec ^= diff;
	m_led_execution[1][0] = BIT(m_display_exec, 0);
	m_led_execution[1][1] = BIT(m_display_exec, 1);
	m_led_execution[1][2] = BIT(m_display_exec, 2);
	m_led_execution[1][3] = BIT(m_display_exec, 3);
	m_led_execution[0][0] = BIT(m_display_exec, 4);
	m_led_execution[0][1] = BIT(m_display_exec, 5);
	m_led_execution[0][2] = BIT(m_display_exec, 6);
	m_led_execution[0][3] = BIT(m_display_exec, 7);
}

void intellec4_state::display_pointer(u8 value, u8 mask)
{
	u16 const diff((value ^ m_display_ptr) & mask);
	m_display_ptr ^= diff;
	m_led_last_ptr[1][0] = BIT(m_display_ptr, 0);
	m_led_last_ptr[1][1] = BIT(m_display_ptr, 1);
	m_led_last_ptr[1][2] = BIT(m_display_ptr, 2);
	m_led_last_ptr[1][3] = BIT(m_display_ptr, 3);
	m_led_last_ptr[0][0] = BIT(m_display_ptr, 4);
	m_led_last_ptr[0][1] = BIT(m_display_ptr, 5);
	m_led_last_ptr[0][2] = BIT(m_display_ptr, 6);
	m_led_last_ptr[0][3] = BIT(m_display_ptr, 7);
}


/*----------------------------------
  Internal helpers
----------------------------------*/

void intellec4_state::trigger_reset()
{
	// (re-)trigger the reset monostable
	if (!m_sw_reset && !m_sw_prg_mode[0] && !m_sw_prg_mode[1] && !m_sw_prg_mode[2] && !m_bus_user_reset)
	{
		// 9602 with Rx = 27kΩ, Cx = 0.1µF
		// K * Rx(kΩ) * Cx(pF) * (1 + (1 / Rx(kΩ))) = 0.34 * 27 * 100000 * (1 + (1 / 27)) = 952000ns
		m_reset_timer->adjust(attotime::from_usec(952));
		if (!m_cpu_reset)
		{
			m_cpu_reset = true;
			m_cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
			m_bus->cpu_reset_in(0);
			reset_panel();
			if (m_sw_reset_mode)
			{
				m_bus->reset_4002_in(0);
				check_4002_reset();
			}
		}
	}
}

void intellec4_state::check_4002_reset()
{
	// FIXME: this really takes multiple cycles to erase, and prevents writes while held
	if ((m_cpu_reset && m_sw_reset_mode) || m_bus_reset_4002)
	{
		std::fill_n(&m_memory[0], m_memory.bytes(), 0U);
		std::fill_n(&m_status[0], m_memory.bytes(), 0U);
	}
}

void intellec4_state::reset_panel()
{
	if (!m_panel_reset)
	{
		m_pass_counter = 0U;
		m_panel_reset = true;
		m_adr_cmp_latch = false;
		if (m_search_complete && !m_next_inst)
			machine().output().set_value("led_status_search", m_search_complete = false);
		if (m_pointer_valid)
			machine().output().set_value("led_status_ptr_valid", m_pointer_valid = false);
	}
}



/***********************************************************************
    MOD 4 driver
***********************************************************************/

class mod4_state : public intellec4_state
{
public:
	mod4_state(machine_config const &mconfig, device_type type, char const *tag)
		: intellec4_state(mconfig, type, tag)
	{
	}

	// universal slot outputs
	DECLARE_WRITE_LINE_MEMBER(bus_test);

	// front panel switches
	DECLARE_INPUT_CHANGED_MEMBER(sw_hold);
	DECLARE_INPUT_CHANGED_MEMBER(sw_one_shot);

	void mod4(machine_config &config);

protected:
	virtual void driver_start() override;
	virtual void driver_reset() override;

private:
	enum
	{
		BIT_SW_HOLD = 0,
		BIT_SW_ONE_SHOT
	};

	TIMER_CALLBACK_MEMBER(one_shot_expired);

	emu_timer   *m_one_shot_timer = nullptr;

	// control board state
	bool    m_one_shot = false;

	// current state of signals from bus
	bool    m_bus_test = false;

	// current state of front panel switches
	bool    m_sw_hold = false;
};


/*----------------------------------
  MOD 4-specific switches
----------------------------------*/

INPUT_PORTS_START(mod4)
	PORT_INCLUDE(intellec4)

	PORT_MODIFY("MODE")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("HOLD")     PORT_CODE(KEYCODE_LEFT)  PORT_CHANGED_MEMBER(DEVICE_SELF, mod4_state, sw_hold,     nullptr)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("ONE SHOT") PORT_CODE(KEYCODE_RIGHT) PORT_CHANGED_MEMBER(DEVICE_SELF, mod4_state, sw_one_shot, nullptr)
INPUT_PORTS_END


/*----------------------------------
  Bus signal handlers
----------------------------------*/

WRITE_LINE_MEMBER(mod4_state::bus_test)
{
	if (!m_one_shot && !m_sw_hold)
		m_cpu->set_input_line(I4004_TEST_LINE, state ? CLEAR_LINE : ASSERT_LINE);
	m_bus_test = 0 == state;
}


/*----------------------------------
  Front panel switch handlers
----------------------------------*/

INPUT_CHANGED_MEMBER(mod4_state::sw_hold)
{
	// overridden by the one-shot monostable and the bus test signal
	if (!m_one_shot)
	{
		if (!m_bus_test)
			m_cpu->set_input_line(I4004_TEST_LINE, newval ? CLEAR_LINE : ASSERT_LINE);
		m_bus->test_in(newval ? 1 : 0);
	}
	m_sw_hold = !bool(newval);
}

INPUT_CHANGED_MEMBER(mod4_state::sw_one_shot)
{
	if (newval && !oldval)
	{
		// 9602 with Rx = 20kΩ, Cx = 0.005µF
		// K * Rx(kΩ) * Cx(pF) * (1 + (1 / Rx(kΩ))) = 0.34 * 20 * 5000 * (1 + (1 / 20)) = 35700ns
		m_one_shot_timer->adjust(attotime::from_nsec(35700));
		if (!m_one_shot)
		{
			m_one_shot = true;
			if (!m_sw_hold)
			{
				m_bus->test_in(0);
				if (!m_bus_test)
					m_cpu->set_input_line(I4004_TEST_LINE, ASSERT_LINE);
			}
		}
	}
}


/*----------------------------------
  MOD 4-specific configuration
----------------------------------*/

MACHINE_CONFIG_START(mod4_state::mod4)
	intellec4(config);
	MCFG_DEVICE_ADD(m_cpu, I4004, 5.185_MHz_XTAL / 7)
	MCFG_I4004_ROM_MAP(intellec4_rom)
	MCFG_I4004_RAM_MEMORY_MAP(intellec4_ram_memory)
	MCFG_I4004_ROM_PORTS_MAP(intellec4_rom_ports)
	MCFG_I4004_RAM_STATUS_MAP(intellec4_ram_status)
	MCFG_I4004_RAM_PORTS_MAP(intellec4_ram_ports)
	MCFG_I4004_PROGRAM_MEMORY_MAP(intellec4_program_memory)
	MCFG_I4004_BUS_CYCLE_CB(BUSCYCLE(mod4_state, bus_cycle));
	MCFG_I4004_SYNC_CB(WRITELINE(m_bus, bus::intellec4::univ_bus_device, sync_in))

	MCFG_DEVICE_MODIFY("bus")
	MCFG_INTELLEC4_UNIV_BUS_TEST_CB(WRITELINE(*this, mod4_state, bus_test))

	MCFG_DEFAULT_LAYOUT(layout_intlc44)
MACHINE_CONFIG_END


/*----------------------------------
  driver_device implementation
----------------------------------*/

void mod4_state::driver_start()
{
	intellec4_state::driver_start();

	m_one_shot_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mod4_state::one_shot_expired), this));

	save_item(NAME(m_one_shot));

	save_item(NAME(m_bus_test));

	save_item(NAME(m_sw_hold));

	m_one_shot = false;
}

void mod4_state::driver_reset()
{
	intellec4_state::driver_reset();

	// set stuff according to initial state of front panel
	m_sw_hold = BIT(~m_sw_mode->read(), BIT_SW_HOLD);

	// ensure we're consistent with the state of the bus
	m_bus_test = 0 == m_bus->test_out();

	// ensure device inputs are all in the correct state
	m_cpu->set_input_line(I4004_TEST_LINE, (m_one_shot || m_bus_test || m_sw_hold) ? ASSERT_LINE : CLEAR_LINE);
	m_bus->test_in((m_one_shot || m_sw_hold) ? 0 : 1);

	// set front panel LEDs
	machine().output().set_value("led_status_cpu", true); // actually driven by clock phase 2 - let's assume it's always running
}


/*----------------------------------
  Timer handlers
----------------------------------*/

TIMER_CALLBACK_MEMBER(mod4_state::one_shot_expired)
{
	m_one_shot = false;
	if (!m_sw_hold)
	{
		m_bus->test_in(1);
		if (!m_bus_test)
			m_cpu->set_input_line(I4004_TEST_LINE, CLEAR_LINE);
	}
}



/***********************************************************************
    MOD 40 driver
***********************************************************************/

class mod40_state : public intellec4_state
{
public:
	mod40_state(machine_config const &mconfig, device_type type, char const *tag)
		: intellec4_state(mconfig, type, tag)
		, m_led_status_run(*this, "led_status_run")
	{
	}

	DECLARE_WRITE_LINE_MEMBER(stp_ack);

	// universal slot outputs
	DECLARE_WRITE_LINE_MEMBER(bus_stop);
	DECLARE_WRITE_LINE_MEMBER(bus_test);

	// front panel switches
	DECLARE_INPUT_CHANGED_MEMBER(sw_stop);
	DECLARE_INPUT_CHANGED_MEMBER(sw_single_step);

	void mod40(machine_config &config);

protected:
	virtual void driver_start() override;
	virtual void driver_reset() override;

private:
	enum
	{
		BIT_SW_STOP = 0,
		BIT_SW_SINGLE_STEP
	};

	TIMER_CALLBACK_MEMBER(single_step_expired);

	output_finder<> m_led_status_run;

	emu_timer   *m_single_step_timer = nullptr;

	// control board state
	bool    m_stp_ack = false, m_single_step = false;

	// current state of signals from bus
	bool    m_bus_stop = false;

	// current state of front panel switches
	bool    m_sw_stop = false;
};


/*----------------------------------
  MOD 40-specific switches
----------------------------------*/

INPUT_PORTS_START(mod40)
	PORT_INCLUDE(intellec4)

	PORT_MODIFY("MODE")
	PORT_BIT( 0x0001, IP_ACTIVE_LOW,  IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("STOP")        PORT_CODE(KEYCODE_LEFT)  PORT_CHANGED_MEMBER(DEVICE_SELF, mod40_state, sw_stop,        0)
	PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYPAD )             PORT_NAME("SINGLE STEP") PORT_CODE(KEYCODE_RIGHT) PORT_CHANGED_MEMBER(DEVICE_SELF, mod40_state, sw_single_step, 0)
INPUT_PORTS_END


/*----------------------------------
  CPU output handlers
----------------------------------*/

WRITE_LINE_MEMBER(mod40_state::stp_ack)
{
	// resets the single-step monostable
	if (m_stp_ack && state)
	{
		m_single_step_timer->reset();
		m_single_step = false;
		if (m_sw_stop)
		{
			m_bus->stop_in(0);
			if (!m_bus_stop)
				m_cpu->set_input_line(I4040_STP_LINE, ASSERT_LINE);
		}
	}
	m_stp_ack = 0 == state;
	m_led_status_run = !m_stp_ack;
	m_bus->stop_acknowledge_in(state);
}


WRITE_LINE_MEMBER(mod40_state::bus_stop)
{
	// will not allow the CPU to step/run
	if (m_single_step || !m_sw_stop)
		m_cpu->set_input_line(I4040_STP_LINE, state ? CLEAR_LINE : ASSERT_LINE);
	m_bus_stop = 0 == state;
}


/*----------------------------------
  Bus signal handlers
----------------------------------*/

WRITE_LINE_MEMBER(mod40_state::bus_test)
{
	m_cpu->set_input_line(I4040_TEST_LINE, state ? CLEAR_LINE : ASSERT_LINE);
}


/*----------------------------------
  Front panel switch handlers
----------------------------------*/

INPUT_CHANGED_MEMBER(mod40_state::sw_stop)
{
	// overridden by the single-step monostable and the bus stop signal
	if (!m_single_step)
	{
		if (!m_bus_stop)
			m_cpu->set_input_line(I4040_STP_LINE, newval ? CLEAR_LINE : ASSERT_LINE);
		m_bus->stop_in(newval ? 1 : 0);
	}
	m_sw_stop = !bool(newval);
}

INPUT_CHANGED_MEMBER(mod40_state::sw_single_step)
{
	// (re-)triggers the single-step monostable
	if (m_stp_ack && newval && !oldval)
	{
		// 9602 with Rx = 20kΩ, Cx = 0.005µF
		// K * Rx(kΩ) * Cx(pF) * (1 + (1 / Rx(kΩ))) = 0.34 * 20 * 5000 * (1 + (1 / 20)) = 35700ns
		m_single_step_timer->adjust(attotime::from_nsec(35700));
		if (!m_single_step)
		{
			m_single_step = true;
			if (m_sw_stop)
			{
				m_bus->stop_in(1);
				if (!m_bus_stop)
					m_cpu->set_input_line(I4040_STP_LINE, CLEAR_LINE);
			}
		}
	}
}


/*----------------------------------
  MOD 40-specific configuration
----------------------------------*/

MACHINE_CONFIG_START(mod40_state::mod40)
	intellec4(config);
	MCFG_DEVICE_ADD(m_cpu, I4040, 5.185_MHz_XTAL / 7)
	MCFG_I4040_ROM_MAP(intellec4_rom)
	MCFG_I4040_RAM_MEMORY_MAP(intellec4_ram_memory)
	MCFG_I4040_ROM_PORTS_MAP(intellec4_rom_ports)
	MCFG_I4040_RAM_STATUS_MAP(intellec4_ram_status)
	MCFG_I4040_RAM_PORTS_MAP(intellec4_ram_ports)
	MCFG_I4040_PROGRAM_MEMORY_MAP(intellec4_program_memory)
	MCFG_I4040_BUS_CYCLE_CB(BUSCYCLE(mod40_state, bus_cycle));
	MCFG_I4040_SYNC_CB(WRITELINE(m_bus, bus::intellec4::univ_bus_device, sync_in))
	MCFG_I4040_STP_ACK_CB(WRITELINE(*this, mod40_state, stp_ack))

	MCFG_DEVICE_MODIFY("bus")
	MCFG_INTELLEC4_UNIV_BUS_STOP_CB(WRITELINE(*this, mod40_state, bus_stop))
	MCFG_INTELLEC4_UNIV_BUS_TEST_CB(WRITELINE(*this, mod40_state, bus_test))

	MCFG_DEFAULT_LAYOUT(layout_intlc440)
MACHINE_CONFIG_END


/*----------------------------------
  driver_device implementation
----------------------------------*/

void mod40_state::driver_start()
{
	intellec4_state::driver_start();

	m_led_status_run.resolve();

	m_single_step_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mod40_state::single_step_expired), this));

	save_item(NAME(m_stp_ack));
	save_item(NAME(m_single_step));

	save_item(NAME(m_bus_stop));

	save_item(NAME(m_sw_stop));

	m_stp_ack = m_single_step = false;
}

void mod40_state::driver_reset()
{
	intellec4_state::driver_reset();

	// set stuff according to initial state of front panel
	m_sw_stop = BIT(~m_sw_mode->read(), BIT_SW_STOP);

	// ensure we're consistent with the state of the bus
	m_bus_stop = 0 == m_bus->stop_out();

	// ensure device inputs are all in the correct state
	m_cpu->set_input_line(I4040_TEST_LINE, m_bus->test_out() ? CLEAR_LINE : ASSERT_LINE);
	m_cpu->set_input_line(I4040_STP_LINE, ((m_sw_stop && !m_single_step) || m_bus_stop) ? ASSERT_LINE : CLEAR_LINE);
	m_bus->test_in(1);
	m_bus->stop_in((m_sw_stop && !m_single_step) ? 0 : 1);

	// set front panel LEDs
	m_led_status_run = !m_stp_ack;
}


/*----------------------------------
  Timer handlers
----------------------------------*/

TIMER_CALLBACK_MEMBER(mod40_state::single_step_expired)
{
	m_single_step = false;
	if (m_sw_stop)
	{
		m_bus->stop_in(0);
		if (!m_bus_stop)
			m_cpu->set_input_line(I4040_STP_LINE, ASSERT_LINE);
	}
}


/***********************************************************************
    ROM definitions
***********************************************************************/

ROM_START(intlc44)
	ROM_REGION(0x0400, "monitor", 0) // 4 * 1702A
	ROM_DEFAULT_BIOS("v2.1")
	ROM_SYSTEM_BIOS(0, "v2.1", "MON 4 V2.1")
	ROMX_LOAD("mon_4-000-v_2.1.a1",      0x0000, 0x0100, CRC(8d1f56ff) SHA1(96bc19be9be4e92195fad82d7a3cadb763ab6e3f), ROM_BIOS(1))
	ROMX_LOAD("mon_4-100-v_2.1.a2",      0x0100, 0x0100, CRC(66562a4f) SHA1(040749c45e95dfc39b3397d0c31c8b4c11f0a5fc), ROM_BIOS(1))
	ROMX_LOAD("mon_4-200-v_2.1.a3",      0x0200, 0x0100, CRC(fe039c68) SHA1(1801cfcc7514412865c0fdc7d1800fcf583a2d2a), ROM_BIOS(1))
	ROMX_LOAD("mon_4-300-v_2.1.a4",      0x0300, 0x0100, CRC(3724d5af) SHA1(b764b3bb3541fbda875f7a7655f46aa54b332631), ROM_BIOS(1))
ROM_END

ROM_START(intlc440)
	ROM_REGION(0x0400, "monitor", 0) // 4 * 1702A
	ROM_DEFAULT_BIOS("v2.1")
	ROM_SYSTEM_BIOS(0, "v2.1", "MON 4 V2.1")
	ROMX_LOAD("mon_4-000-v_2.1.a1",      0x0000, 0x0100, CRC(8d1f56ff) SHA1(96bc19be9be4e92195fad82d7a3cadb763ab6e3f), ROM_BIOS(1))
	ROMX_LOAD("mon_4-100-v_2.1.a2",      0x0100, 0x0100, CRC(66562a4f) SHA1(040749c45e95dfc39b3397d0c31c8b4c11f0a5fc), ROM_BIOS(1))
	ROMX_LOAD("mon_4-200-v_2.1.a3",      0x0200, 0x0100, CRC(fe039c68) SHA1(1801cfcc7514412865c0fdc7d1800fcf583a2d2a), ROM_BIOS(1))
	ROMX_LOAD("mon_4-300-v_2.1.a4",      0x0300, 0x0100, CRC(3724d5af) SHA1(b764b3bb3541fbda875f7a7655f46aa54b332631), ROM_BIOS(1))
	ROM_SYSTEM_BIOS(1, "v2.1_1200", "MON 4 V2.1 1200 Baud hack")
	ROMX_LOAD("mon_4-000-v_2.1.a1",      0x0000, 0x0100, CRC(8d1f56ff) SHA1(96bc19be9be4e92195fad82d7a3cadb763ab6e3f), ROM_BIOS(2))
	ROMX_LOAD("i40_mon-1.a2",            0x0100, 0x0100, CRC(cd9fecd6) SHA1(9c4fb85118c881687fd4b324e5089df05d1e63d1), ROM_BIOS(2))
	ROMX_LOAD("i40_mon-2.a3",            0x0200, 0x0100, CRC(037de128) SHA1(3694636e1f4e23688b36ea9ee755a0c5888f4328), ROM_BIOS(2))
	ROMX_LOAD("1200_baud-i40_mon-f3.a4", 0x0300, 0x0100, CRC(f3198d79) SHA1(b7903073b69f487b6f78842c08694f12225d85f0), ROM_BIOS(2))
ROM_END

} // anonymous namespace


/***********************************************************************
    Machine definitions
***********************************************************************/

//    YEAR   NAME      PARENT  COMPAT  MACHINE  INPUT  CLASS        INIT        COMPANY  FULLNAME             FLAGS
COMP( 1973?, intlc44,  0,      0,      mod4,    mod4,  mod4_state,  empty_init, "Intel", "INTELLEC 4/MOD 4",  MACHINE_NO_SOUND_HW | MACHINE_CLICKABLE_ARTWORK | MACHINE_SUPPORTS_SAVE )
COMP( 1974?, intlc440, 0,      0,      mod40,   mod40, mod40_state, empty_init, "Intel", "INTELLEC 4/MOD 40", MACHINE_NO_SOUND_HW | MACHINE_CLICKABLE_ARTWORK | MACHINE_SUPPORTS_SAVE )