summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/machine/3dom2.cpp
blob: b984da8b1ab3f58b204a86d222b1c9329e77e033 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953



                                                                            
                       




                                                                            
 
                                




















                                                                                            












                                    



                                                   



                                          



                                                   






                                          



                                                   




                                          



                                                   






                                          



                                                   








                                          







                                                   


                                          




                                                   










                                          



                                                   














                                          



                                                   











                                          



                                                   











                                          




                                                   
                                          






                                                                            
                                                                         


                     


                                                      
                        
                                                                                      
















































































                                                                                                               
                                                 














                                                                                                          
                                                                                                













































































































































































                                                                                                                






                                                                                                                                                                                                                                               
 
                                                                                                                                                                                                                                               




                                                                                
                 

                                                                            
                                                                                                                                                                                                         
















































































































































































































































































































































































































































































                                                                                                                         
                                                                       
 
                                                                                                       
















































































































































































                                                                                                                                     

                                                                     





















































                                                                                             

                                                                     














































































































































                                                                                                             
                                                                          














































































































































































































































































                                                                                                                       
                                                                                                  
















































































































































                                                                                                                               

                                                                       






                                   
                                                                                                                 


















































































                                                                                                                            
                             































































































                                                                                                                                           
                                                           




                                              
                                                                                                                   















                                                                                                                                                            



                                                                                                                                    













































                                                                                                        

                                                                                                    











































































                                                                                                                 
// license:BSD-3-Clause
// copyright-holders:Philip Bennett
/***************************************************************************

    3DO M2 Bulldog ASIC

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

#include "emu.h"
#include "3dom2.h"

#include <algorithm> // std::min


//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

// Device type definitions
DEFINE_DEVICE_TYPE(M2_BDA, m2_bda_device, "m2bda", "3DO M2 Bulldog ASIC")
DEFINE_DEVICE_TYPE(M2_POWERBUS, m2_powerbus_device, "m2powerbus", "BDA PowerBus Controller")
DEFINE_DEVICE_TYPE(M2_MEMCTL, m2_memctl_device, "m2memctl", "BDA Memory Controller")
DEFINE_DEVICE_TYPE(M2_VDU, m2_vdu_device, "m2vdu", "BDA VDU")
DEFINE_DEVICE_TYPE(M2_CTRLPORT, m2_ctrlport_device, "m2ctrlport", "BDA Control Ports")
DEFINE_DEVICE_TYPE(M2_MPEG, m2_mpeg_device, "m2mpeg", "3DO MPEG Decoder")
DEFINE_DEVICE_TYPE(M2_CDE, m2_cde_device, "m2cde", "3DO M2 CDE ASIC")



//**************************************************************************
//  VDU REGISTER DEFINITIONS
//**************************************************************************

#define VDU_VLOC                0x00
#define VDU_VINT                0x04
#define VDU_VDC0                0x08
#define VDU_VDC1                0x0c
#define VDU_FV0A                0x10
#define VDU_FV1A                0x14
#define VDU_AVDI                0x1c
#define VDU_VDLI                0x20
#define VDU_VCFG                0x24
#define VDU_DMT0                0x28
#define VDU_DMT1                0x2c
#define VDU_LFSR                0x30
#define VDU_VRST                0x34

//-------------------------------------------------
//  VLOC
//-------------------------------------------------
#define VDU_VLOC_VCOUNT_MASK    0x00003FF8
#define VDU_VLOC_VCOUNT_SHIFT   3
#define VDU_VLOC_VIDEOFIELD     0x00004000
#define VDU_VLOC_RESERVED       0xFFFF8007

//-------------------------------------------------
//  VINT
//-------------------------------------------------
#define VDU_VINT_VINT0          0x80000000
#define VDU_VINT_VLINE0_MASK    0x7FF00000
#define VDU_VINT_VLINE0_SHIFT   20
#define VDU_VINT_VINT1          0x00008000
#define VDU_VINT_VLINE1_MASK    0x00007FF0
#define VDU_VINT_VLINE1_SHIFT   4
#define VDU_VINT_RESERVED       0x000F000F

//-------------------------------------------------
//  VDC0/VDC1
//-------------------------------------------------
#define VDU_VDC_HINT            0x02000000
#define VDU_VDC_VINT            0x01000000
#define VDU_VDC_DITHER          0x00400000
#define VDU_VDC_MTXBYP          0x00200000
#define VDU_VDC_RESERVED        0xFC9FFFFF

//-------------------------------------------------
//  AVDI
//-------------------------------------------------
#define VDU_AVDI_HSTART_MASK    0xFFE00000
#define VDU_AVDI_HWIDTH_MASK    0x0003FF80
#define VDU_AVDI_HDOUBLE        0x00000008
#define VDU_AVDI_VDOUBLE        0x00000004
#define VDU_AVDI_RESERVED       0x001C0073
#define VDU_AVDI_HSTART_SHIFT   21
#define VDU_AVDI_HWIDTH_SHIFT   7

//-------------------------------------------------
//  VDLI
//-------------------------------------------------
#define VDU_VDLI_BYPASSTYPE     0x10000000
#define VDU_VDLI_FBFORMAT       0x04000000
#define VDU_VDLI_ONEVINTDIS     0x00400000
#define VDU_VDLI_RANDOMDITHER   0x00200000
#define VDU_VDLI_RESERVED       0xEB9FFFFF
#define VDU_VDLI_BYPASSTYPE_MSB 0
#define VDU_VDLI_BYPASSTYPE_LSB 0x10000000
#define VDU_VDLI_FBFORMAT_16    0
#define VDU_VDLI_FBFORMAT_32    0x04000000

//-------------------------------------------------
//  VCFG
//-------------------------------------------------

//-------------------------------------------------
//  VRST
//-------------------------------------------------
#define VDU_VRST_DVERESET       0x00000002
#define VDU_VRST_VIDRESET       0x00000001
#define VDU_VRST_RESERVED       0xFFFFFFFC


//-------------------------------------------------
//  VDL DMA CONTROL WORD
//-------------------------------------------------
#define VDL_DMA_MOD_MASK        0xFF000000
#define VDL_DMA_ENABLE          0x00200000
#define VDL_DMA_NOBUCKET        0x00020000
#define VDL_DMA_LDLOWER         0x00010000
#define VDL_DMA_LDUPPER         0x00008000
#define VDL_DMA_NWORDS_MASK     0x00007E00
#define VDL_DMA_NLINES_MASK     0x000001FF
#define VDL_DMA_RESERVED        0x00DC0000
#define VDL_DMA_NWORDS_SHIFT    9
#define VDL_DMA_MOD_SHIFT       24
#define VDL_DMA_NLINES_SHIFT    0

//-------------------------------------------------
//  VDL DC CONTROL WORD
//-------------------------------------------------
#define VDL_DC                  0x80000000
#define VDL_DC_0                0x00000000
#define VDL_DC_1                0x10000000
#define VDL_DC_HINTCTL_MASK     0x00060000
#define VDL_DC_HINTCTL_SHIFT    17
#define VDL_DC_VINTCTL_MASK     0x00018000
#define VDL_DC_VINTCTL_SHIFT    15
#define VDL_DC_DITHERCTL_MASK   0x00001800
#define VDL_DC_DITHERCTL_SHIFT  11
#define VDL_DC_MTXBYPCTL_MASK   0x00000600
#define VDL_DC_MTXBYPCTL_SHIFT  9
#define VDL_DC_RESERVED         0x0FF861FF
#define VDL_CTL_DISABLE         0
#define VDL_CTL_ENABLE          1
#define VDL_CTL_NOP             2

//-------------------------------------------------
//  VDL AV CONTROL WORD
//-------------------------------------------------
#define VDL_AV                  0xA0000000
#define VDL_AV_HSTART_MASK      0x1FFC0000
#define VDL_AV_HSTART_SHIFT     18
#define VDL_AV_LD_HSTART        0x00020000
#define VDL_AV_HWIDTH_MASK      0x0001FFC0
#define VDL_AV_HWIDTH_SHIFT     6
#define VDL_AV_LD_HWIDTH        0x00000020
#define VDL_AV_HDOUBLE          0x00000010
#define VDL_AV_VDOUBLE          0x00000008
#define VDL_AV_LD_HDOUBLE       0x00000004
#define VDL_AV_LD_VDOUBLE       0x00000002
#define VDL_AV_RESERVED         0x00000001

//-------------------------------------------------
//  VDL LC CONTROL WORD
//-------------------------------------------------
#define VDL_LC                  0xC0000000
#define VDL_LC_BYPASSTYPE       0x02000000
#define VDL_LC_FBFORMAT         0x00800000
#define VDL_LC_ONEVINTDIS       0x00080000
#define VDL_LC_RANDOMDITHER     0x00040000
#define VDL_LC_LD_BYPASSTYPE    0x00002000
#define VDL_LC_LD_FBFORMAT      0x00001000
#define VDL_LC_RESERVED         0x1D73CFFF
#define VDL_LC_BYPASSTYPE_MSB   0x00000000
#define VDL_LC_BYPASSTYPE_LSB   0x02000000
#define VDL_LC_FBFORMAT_16      0x00000000
#define VDL_LC_FBFORMAT_32      0x00800000

//-------------------------------------------------
//  VDL DMA CONTROL WORD
//-------------------------------------------------

#define VDL_NOP                 0xe1000000



/***************************************************************************
    SUPPORT FUNCTIONS
***************************************************************************/

static void write_m2_reg(uint32_t &reg, uint32_t data, m2_reg_wmode mode)
{
	switch (mode)
	{
		case REG_WRITE: reg = data;     break;
		case REG_SET:   reg |= data;    break;
		case REG_CLEAR: reg &= ~data;   break;
		default:
			throw emu_fatalerror("write_m2_reg: Bad register write mode");
	}
}



//**************************************************************************
//  BDA DEVICE
//**************************************************************************

//-------------------------------------------------
//  m2_bda_device - constructor
//-------------------------------------------------

m2_bda_device::m2_bda_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, M2_BDA, tag, owner, clock),
	m_cpu1(*this, finder_base::DUMMY_TAG),
	m_cpu2(*this, finder_base::DUMMY_TAG),
	m_videores_in(*this),
	m_memctl(*this, "memctl"),
	m_powerbus(*this, "powerbus"),
	m_vdu(*this, "vdu"),
	m_ctrlport(*this, "ctrlport"),
	m_dspp(*this, "dspp"),
	m_mpeg(*this, "mpeg"),
	m_te(*this, "te"),
	m_dac_l(*this),
	m_dac_r(*this)
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void m2_bda_device::device_start()
{
	// Resolve callbacks
	m_videores_in.resolve_safe(0);
	m_dac_l.resolve_safe();
	m_dac_r.resolve_safe();

	// Allocate RAM
	uint32_t ram_size = (m_rambank_size[0] + m_rambank_size[1]) * 1024 * 1024;
	m_ram = auto_alloc_array(machine(), uint32_t, ram_size / sizeof(uint32_t));
	m_ram_mask = ram_size - 1;

	// Install RAM and handlers into the CPU address spaces
	configure_ppc_address_map(m_cpu1->space(AS_PROGRAM));
	configure_ppc_address_map(m_cpu2->space(AS_PROGRAM));

	// Register state for saving
	save_pointer(NAME(m_ram), ram_size / sizeof(uint32_t));

	// Set a timer to pull data from the DSPP FIFO into the DACs
	m_dac_timer = timer_alloc(0);
	m_dac_timer->adjust(attotime::from_hz(16.9345));
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void m2_bda_device::device_reset()
{

}


//-------------------------------------------------
//  device_post_load - device-specific post-load
//-------------------------------------------------

void m2_bda_device::device_post_load()
{

}


//-------------------------------------------------
//  machine_config_fragment - declare sub-devices
//-------------------------------------------------

void m2_bda_device::device_add_mconfig(machine_config &config)
{
	M2_MEMCTL(config, m_memctl, DERIVED_CLOCK(1, 1));

	M2_POWERBUS(config, m_powerbus, DERIVED_CLOCK(1, 1));

	M2_VDU(config, m_vdu, DERIVED_CLOCK(1, 1));
	m_vdu->vint0_int_handler().set(m_powerbus, FUNC(m2_powerbus_device::int_line<BDAINT_VINT0_LINE>));
	m_vdu->vint1_int_handler().set(m_powerbus, FUNC(m2_powerbus_device::int_line<BDAINT_VINT1_LINE>));

	M2_CTRLPORT(config, m_ctrlport, DERIVED_CLOCK(1, 1));

	M2_MPEG(config, m_mpeg, DERIVED_CLOCK(1, 1));
//  m_mpeg->int_handler().set(m_powerbus, FUNC(m2_powerbus_device::int_line<BDAINT_MPEG_LINE>));

	DSPP(config, m_dspp, DERIVED_CLOCK(1, 1));
	m_dspp->int_handler().set(m_powerbus, FUNC(m2_powerbus_device::int_line<BDAINT_DSP_LINE>));
	m_dspp->dma_read_handler().set(FUNC(m2_bda_device::read_bus8));
	m_dspp->dma_write_handler().set(FUNC(m2_bda_device::write_bus8));

	M2_TE(config, m_te, DERIVED_CLOCK(1, 1));
	m_te->general_int_handler().set(m_powerbus, FUNC(m2_powerbus_device::int_line<BDAINT_TRIGEN_LINE>));
	m_te->dfinstr_int_handler().set(m_powerbus, FUNC(m2_powerbus_device::int_line<BDAINT_TRIDFINST_LINE>));
	m_te->iminstr_int_handler().set(m_powerbus, FUNC(m2_powerbus_device::int_line<BDAINT_TRIDMINST_LINE>));
	m_te->listend_int_handler().set(m_powerbus, FUNC(m2_powerbus_device::int_line<BDAINT_TRILISTEND_LINE>));
	m_te->winclip_int_handler().set(m_powerbus, FUNC(m2_powerbus_device::int_line<BDAINT_TRIWINCLIP_LINE>));
}


//-------------------------------------------------
//  device_timer - device-specific timers
//-------------------------------------------------

void m2_bda_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
		case 0:
		{
			m_dac_l(m_dspp->read_output_fifo());
			m_dac_r(m_dspp->read_output_fifo());
			m_dac_timer->adjust(attotime::from_hz(44100));
			break;
		}
	}
}



/***************************************************************************
    PUBLIC FUNCTIONS
***************************************************************************/

//-------------------------------------------------
//  cpu_id_r - read from CPU ID register
//-------------------------------------------------

READ32_MEMBER( m2_bda_device::cpu_id_r )
{
	uint32_t data = 0;

	// .x...... ........ ........ ........ -  Video type (0 = Arcade, 1 = NTSC/PAL)
	// x....... ........ ........ ........ -  CPU ID     (0 = CPU1, 1 = CPU2)

	if (&space.device() == m_cpu2)
		data |= 0x80000000;

	if (m_videores_in() != 0)
		data |= 0x40000000;

	return data;
}


//-------------------------------------------------
//  cpu_id_w - Write to CPU ID register
//-------------------------------------------------

WRITE32_MEMBER( m2_bda_device::cpu_id_w )
{
	// TODO: How should this work?
	logerror("%s: CPUID: %x\n", machine().describe_context(), data);
}


//-------------------------------------------------
//  read_bus8 - Read 8-bit data from the PowerBus
//-------------------------------------------------

uint8_t m2_bda_device::read_bus8(offs_t offset)
{
	assert(offset >= RAM_BASE && offset <= RAM_BASE + m_ram_mask);

	offset &= m_ram_mask;
	uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_ram[0]) + BYTE8_XOR_BE(offset);
	return *ptr;
}


//-------------------------------------------------
//  read_bus16 - Read 16-bit data from the PowerBus
//-------------------------------------------------

uint16_t m2_bda_device::read_bus16(offs_t offset)
{
	assert(offset >= RAM_BASE && offset <= RAM_BASE + m_ram_mask);

	offset &= m_ram_mask;
	uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_ram[0]) + WORD2_XOR_BE(offset);
	return *reinterpret_cast<uint16_t *>(ptr);
}


//-------------------------------------------------
//  read_bus32 - Read 32-bit data from the PowerBus
//-------------------------------------------------

uint32_t m2_bda_device::read_bus32(offs_t offset)
{
	assert(offset >= RAM_BASE && offset <= RAM_BASE + m_ram_mask);

	offset &= m_ram_mask;

	uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_ram[0]) + DWORD_XOR_BE(offset);
	return *reinterpret_cast<uint32_t *>(ptr);
}


//-------------------------------------------------
//  write_bus8 - Write 8-bit data to the PowerBus
//-------------------------------------------------

void m2_bda_device::write_bus8(offs_t offset, uint8_t data)
{
	assert(offset >= RAM_BASE && offset <= RAM_BASE + m_ram_mask);

	offset &= m_ram_mask;
	uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_ram[0]) + BYTE8_XOR_BE(offset);
	*ptr = data;
}


//-------------------------------------------------
//  write_bus16 - Write 16-bit data to the PowerBus
//-------------------------------------------------

void m2_bda_device::write_bus16(offs_t offset, uint16_t data)
{
	assert(offset >= RAM_BASE && offset <= RAM_BASE + m_ram_mask);

	offset &= m_ram_mask;
	uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_ram[0]) + WORD2_XOR_BE(offset);
	*reinterpret_cast<uint16_t *>(ptr) = data;
}


//-------------------------------------------------
//  write_bus32 - Write 32-bit data to the PowerBus
//-------------------------------------------------

void m2_bda_device::write_bus32(offs_t offset, uint32_t data)
{
	assert(offset >= RAM_BASE && offset <= RAM_BASE + m_ram_mask);

	offset &= m_ram_mask;
	uint8_t *ptr = reinterpret_cast<uint8_t *>(&m_ram[0]) + DWORD_XOR_BE(offset);
	*reinterpret_cast<uint32_t *>(ptr) = data;
}



/***************************************************************************
    PRIVATE FUNCTIONS
***************************************************************************/

//-------------------------------------------------
//  configure_ppc_address_map -
//-------------------------------------------------

void m2_bda_device::configure_ppc_address_map(address_space &space)
{
	// Install shared RAM
	space.install_ram(RAM_BASE, RAM_BASE + m_ram_mask, m_ram);

	// Install TE texture RAM window
	space.install_ram(TE_TRAM_BASE, TE_TRAM_BASE + TE_TRAM_MASK, m_te->tram_ptr());

	// Install BDA sub-devices
	space.install_readwrite_handler(POWERBUS_BASE,  POWERBUS_BASE + DEVICE_MASK,read32_delegate(*m_powerbus, FUNC(m2_powerbus_device::read)),    write32_delegate(*m_powerbus, FUNC(m2_powerbus_device::write)),    0xffffffffffffffffULL);
	space.install_readwrite_handler(MEMCTL_BASE,    MEMCTL_BASE + DEVICE_MASK,  read32_delegate(*m_memctl,   FUNC(m2_memctl_device::read)),      write32_delegate(*m_memctl,   FUNC(m2_memctl_device::write)),      0xffffffffffffffffULL);
	space.install_readwrite_handler(VDU_BASE,       VDU_BASE + DEVICE_MASK,     read32_delegate(*m_vdu,      FUNC(m2_vdu_device::read)),         write32_delegate(*m_vdu,      FUNC(m2_vdu_device::write)),         0xffffffffffffffffULL);
	space.install_readwrite_handler(TE_BASE,        TE_BASE + DEVICE_MASK,      read32_delegate(*m_te,       FUNC(m2_te_device::read)),          write32_delegate(*m_te,       FUNC(m2_te_device::write)),          0xffffffffffffffffULL);
	space.install_readwrite_handler(DSP_BASE,       DSP_BASE + DEVICE_MASK,     read32_delegate(*m_dspp,     FUNC(dspp_device::read)),           write32_delegate(*m_dspp,     FUNC(dspp_device::write)),           0xffffffffffffffffULL);
	space.install_readwrite_handler(CTRLPORT_BASE,  CTRLPORT_BASE + DEVICE_MASK,read32_delegate(*m_ctrlport, FUNC(m2_ctrlport_device::read)),    write32_delegate(*m_ctrlport, FUNC(m2_ctrlport_device::write)),    0xffffffffffffffffULL);
	space.install_readwrite_handler(MPEG_BASE,      MPEG_BASE + DEVICE_MASK,    read32_delegate(*m_mpeg,     FUNC(m2_mpeg_device::read)),        write32_delegate(*m_mpeg,     FUNC(m2_mpeg_device::write)),        0xffffffffffffffffULL);

	space.install_readwrite_handler(CPUID_BASE,     CPUID_BASE + DEVICE_MASK,   read32_delegate(*this,       FUNC(m2_bda_device::cpu_id_r)),     write32_delegate(*this,       FUNC(m2_bda_device::cpu_id_w)),      0xffffffffffffffffULL);


	// Find and install the CDE
	m2_cde_device *cde = downcast<m2_cde_device *>(machine().device("cde"));

	if (!cde)
		throw emu_fatalerror("BDA: Could not find the CDE device!");

	space.install_readwrite_handler(SLOT4_BASE, SLOT4_BASE + SLOT_MASK, read32_delegate(*cde, FUNC(m2_cde_device::read)), write32_delegate(*cde, FUNC(m2_cde_device::write)), 0xffffffffffffffffULL);
}



//**************************************************************************
//  POWERBUS DEVICE
//**************************************************************************


//-------------------------------------------------
//  m2_powerbus_device - constructor
//-------------------------------------------------

m2_powerbus_device::m2_powerbus_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, M2_POWERBUS, tag, owner, clock),
	m_int_handler(*this)
{

}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void m2_powerbus_device::device_start()
{
	// Resolve callbacks
	m_int_handler.resolve();

	// Register state for saving
	save_item(NAME(m_ctrl));
	save_item(NAME(m_int_enable));
	save_item(NAME(m_int_status));
	save_item(NAME(m_err_status));
	save_item(NAME(m_err_address));
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void m2_powerbus_device::device_reset()
{
	m_ctrl = 0;
	m_int_enable = 0;
	m_int_status = 0;
	m_err_status = 0;
	m_err_address = 0;
}


/***************************************************************************
    PUBLIC FUNCTIONS
***************************************************************************/

//-------------------------------------------------
//  read -
//-------------------------------------------------

READ32_MEMBER( m2_powerbus_device::read )
{
	const uint32_t byte_offs = offset << 2;
	uint32_t data = 0;

	switch (byte_offs)
	{
		case BDAPCTL_PBINTENSET:
		{
			data = m_int_enable;
			break;
		}
		case BDAPCTL_PBINTSTAT:
		{
			data = m_int_status;
			break;
		}
		default:
			logerror("%s: POWERBUS R: [%x] %x\n", machine().describe_context(), byte_offs, data);
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

WRITE32_MEMBER( m2_powerbus_device::write )
{
	uint32_t byte_offs = offset << 2;

	switch (byte_offs & ~0x400)
	{
		case BDAPCTL_PBINTENSET:
		{
			write_m2_reg(m_int_enable, data, byte_offs & 0x400 ? REG_CLEAR : REG_SET);
			update_interrupts();
			break;
		}
		case BDAPCTL_ERRSTAT:
		{
#if 1 // TODO
			if (byte_offs & 0x400)
			{
				write_m2_reg(m_int_status, data, REG_CLEAR);
			}
			else
			{
				if (data == 1)
					write_m2_reg(m_int_status, data, REG_SET);
			}
			update_interrupts();
#endif
			break;
		}
		default:
			logerror("%s: POWERBUS W: [%x] %x (PC:%x)\n", machine().describe_context(), byte_offs, data);
	}
}


/***************************************************************************
    PRIVATE FUNCTIONS
***************************************************************************/

//-------------------------------------------------
//  update_interrupts -
//-------------------------------------------------

void m2_powerbus_device::update_interrupts()
{
	m_int_handler(m_int_status & m_int_enable ? ASSERT_LINE : CLEAR_LINE);
}



//**************************************************************************
//  MEMORY CONTROLLER DEVICE
//**************************************************************************


//-------------------------------------------------
//  m2_memctl_device - constructor
//-------------------------------------------------

m2_memctl_device::m2_memctl_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, M2_MEMCTL, tag, owner, clock),
		m_gpio_in{{*this}, {*this}, {*this}, {*this}},
		m_gpio_out{{*this}, {*this}, {*this}, {*this}}
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void m2_memctl_device::device_start()
{
	// Resolve our callbacks
	for (int i = 0; i < 4; i++)
	{
		m_gpio_in[i].resolve_safe(0);
		m_gpio_out[i].resolve_safe();
	}

	// TODO: DELETE ME
	m2_bda_device *m_bda = (m2_bda_device*)owner(); // TEMP

	// Configure the memory configuration register
	uint32_t bank1 = m_bda->get_rambank_size(0);
	uint32_t bank2 = m_bda->get_rambank_size(1);

	m_mcfg = (ramsize_to_mcfg_field(bank2) << MCFG_SS1_SHIFT) | (ramsize_to_mcfg_field(bank1) << MCFG_SS0_SHIFT);

	// Register state for saving
	save_item(NAME(m_mcfg));
	save_item(NAME(m_mref));
	save_item(NAME(m_mcntl));
	save_item(NAME(m_reset));
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void m2_memctl_device::device_reset()
{
	// TODO: Need postload to set GPIO also?
	m_mref = 0;
}


/***************************************************************************
    PUBLIC FUNCTIONS
***************************************************************************/

//-------------------------------------------------
//  read -
//-------------------------------------------------

READ32_MEMBER( m2_memctl_device::read )
{
	uint32_t byte_offs = offset << 2;
	uint32_t data = 0;

	switch (byte_offs)
	{
		case MCTL_MCONFIG:
		{
			data = m_mcfg;
			break;
		}
		case MCTL_MREF:
		{
			if ((m_mref & MREF_GPIO0_GP) && !(m_mref & MREF_GPIO0_OUT))
			{
				if (m_gpio_in[0]())
					m_mref |= MREF_GPIO0_VALUE;
				else
					m_mref &= ~MREF_GPIO0_VALUE;
			}

			if ((m_mref & MREF_GPIO1_GP) && !(m_mref & MREF_GPIO1_OUT))
			{
				if (m_gpio_in[1]())
					m_mref |= MREF_GPIO1_VALUE;
				else
					m_mref &= ~MREF_GPIO1_VALUE;
			}

			if ((m_mref & MREF_GPIO2_GP) && !(m_mref & MREF_GPIO2_OUT))
			{
				if (m_gpio_in[2]())
					m_mref |= MREF_GPIO2_VALUE;
				else
					m_mref &= ~MREF_GPIO2_VALUE;
			}

			if ((m_mref & MREF_GPIO3_GP) && !(m_mref & MREF_GPIO3_OUT))
			{
				if (m_gpio_in[3]())
					m_mref |= MREF_GPIO3_VALUE;
				else
					m_mref &= ~MREF_GPIO3_VALUE;
			}

			data = m_mref;
			break;
		}
		case MCTL_MCNTL:
		case MCTL_MRESET:
			//logerror("%s: MEMCTL READ: %x %x\n", machine().describe_context(), byte_offs, mem_mask);
			break;
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

WRITE32_MEMBER( m2_memctl_device::write )
{
	uint32_t byte_offs = offset << 2;

	switch (byte_offs)
	{
		case MCTL_MCONFIG:
		{
			m_mcfg = data;
			break;
		}
		case MCTL_MREF:
		{
			// Set any general purpose outputs
			if (data & (MREF_GPIO0_GP | MREF_GPIO0_OUT))
				m_gpio_out[0](data & MREF_GPIO0_VALUE ? 1 : 0);

			if (data & (MREF_GPIO1_GP | MREF_GPIO1_OUT))
				m_gpio_out[1](data & MREF_GPIO1_VALUE ? 1 : 0);

			if (data & (MREF_GPIO2_GP | MREF_GPIO2_OUT))
				m_gpio_out[2](data & MREF_GPIO2_VALUE ? 1 : 0);

			if (data & (MREF_GPIO3_GP | MREF_GPIO3_OUT))
				m_gpio_out[3](data & MREF_GPIO3_VALUE ? 1 : 0);

			m_mref = data;
			break;
		}
		case MCTL_MCNTL:
		case MCTL_MRESET:
			//logerror("%s: MEMCTL WRITE: %x %x %x\n", machine().describe_context(), data, byte_offs);
			break;
	}
}



//**************************************************************************
//  VDU DEVICE
//**************************************************************************


//-------------------------------------------------
//  m2_vdu_device - constructor
//-------------------------------------------------

m2_vdu_device::m2_vdu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, M2_VDU, tag, owner, clock),
	m_screen(*this, finder_base::DUMMY_TAG),
	m_vint0_int_handler(*this),
	m_vint1_int_handler(*this)
{

}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void m2_vdu_device::device_start()
{
	// Resolve callbacks
	m_vint0_int_handler.resolve_safe();
	m_vint1_int_handler.resolve_safe();

	// Initialize line interrupt timers
	m_vint0_timer = timer_alloc(TIMER_ID_VINT0);
	m_vint1_timer = timer_alloc(TIMER_ID_VINT1);

	// Calculate H/V count bias values (1 = start of blanking)
	const rectangle visarea = m_screen->visible_area();

	m_hstart = visarea.min_x;
	m_htotal = visarea.max_x + 1;

	m_vstart = visarea.min_y;
	m_vtotal = visarea.max_y + 1;

	// Register state for saving
	save_item(NAME(m_vint));
	save_item(NAME(m_vdc0));
	save_item(NAME(m_vdc1));
	save_item(NAME(m_fv0a));
	save_item(NAME(m_fv1a));
	save_item(NAME(m_avdi));
	save_item(NAME(m_vdli));
	save_item(NAME(m_vcfg));
	save_item(NAME(m_dmt0));
	save_item(NAME(m_dmt1));
	save_item(NAME(m_vrst));
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void m2_vdu_device::device_reset()
{
	m_fv0a = 0;
	m_fv1a = 0;
	m_avdi = 0;
	m_vdli = 0;
	m_vint = 0;
	m_vcfg = 0;
	m_dmt0 = 0;
	m_dmt1 = 0;

	m_vint0_timer->adjust(attotime::never);
	m_vint1_timer->adjust(attotime::never);
}


//-------------------------------------------------
//  device_timer - device-specific timers
//-------------------------------------------------

void m2_vdu_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
		case TIMER_ID_VINT0:
		{
			m_vint |= VDU_VINT_VINT0;
			m_vint0_int_handler(ASSERT_LINE);
			set_vint_timer(0);
			break;
		}
		case TIMER_ID_VINT1:
		{
			m_vint |= VDU_VINT_VINT1;
			m_vint1_int_handler(ASSERT_LINE);
			set_vint_timer(1);
			break;
		}
	}
}



/***************************************************************************
    PUBLIC FUNCTIONS
***************************************************************************/

//-------------------------------------------------
//  read -
//-------------------------------------------------

READ32_MEMBER( m2_vdu_device::read )
{
	uint32_t byte_offs = offset << 2;
	uint32_t data = 0;

	switch (byte_offs)
	{
		case VDU_VLOC:
		{
			// TODO: Check me
			uint32_t mpos = m_screen->vpos();
			uint32_t vpos = ((mpos + m_vstart) % m_vtotal) + 1;
			data = vpos << VDU_VLOC_VCOUNT_SHIFT;
			break;
		}
		case VDU_VINT:
		{
			data = m_vint;
			break;
		}
		case VDU_VDC0:
		case VDU_VDC1:
		case VDU_FV0A:
		case VDU_FV1A:
		case VDU_AVDI:
		case VDU_VDLI:
		case VDU_VCFG:
		case VDU_DMT0:
		case VDU_DMT1:
		case VDU_LFSR:
		{
			//logerror("%s: VDU READ: %x %x\n", machine().describe_context(), byte_offs, mem_mask);
			break;
		}
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

WRITE32_MEMBER( m2_vdu_device::write )
{
	uint32_t byte_offs = offset << 2;
	m2_reg_wmode wmode = byte_offs & 0x400 ? REG_CLEAR : REG_WRITE;

//  logerror("%s: VDU WRITE: %03x %08x %x\n", machine().describe_context(), byte_offs, data, mem_mask);
	byte_offs &= ~0x400;
	switch (byte_offs)
	{
		case VDU_VINT:
		{
			uint32_t old = m_vint;
			write_m2_reg(m_vint, data, wmode);

			// Update line interrupts if changed
			if ((m_vint & VDU_VINT_VLINE0_MASK) != (old & VDU_VINT_VLINE0_MASK))
				set_vint_timer(0);

			if ((m_vint & VDU_VINT_VLINE1_MASK) != (old & VDU_VINT_VLINE1_MASK))
				set_vint_timer(1);

			// Clear interrupt bits
			if ((old & VDU_VINT_VINT0) && !(m_vint & VDU_VINT_VINT0))
				m_vint0_int_handler(CLEAR_LINE);

			if ((old & VDU_VINT_VINT1) && !(m_vint & VDU_VINT_VINT1))
				m_vint1_int_handler(CLEAR_LINE);

			break;
		}
		case VDU_FV0A:
		{
			m_fv0a = data;
			break;
		}
		case VDU_FV1A:
		{
			m_fv1a = data;
			break;
		}
		case VDU_VCFG:
		{
			m_vcfg = data;
			break;
		}
		case VDU_VRST:
		{
			m_vrst = data;
			break;
		}
		default:
		{
			logerror("%s: VDU WRITE: %x %x %x\n", machine().describe_context(), byte_offs, data, mem_mask);
			break;
		}
	}
}


//-------------------------------------------------
//  parse_dc_word -
//-------------------------------------------------

void m2_vdu_device::parse_dc_word(uint32_t cmd)
{
	// Determine the control register
	uint32_t &vdc = cmd & VDL_DC_1 ? m_vdc1 : m_vdc0;

	// Horizontal interpolation
	uint32_t hint = (cmd & VDL_DC_HINTCTL_MASK) >> VDL_DC_HINTCTL_SHIFT;

	if (hint == VDL_CTL_ENABLE)
		vdc |= VDU_VDC_HINT;
	else if (hint == VDL_CTL_DISABLE)
		vdc &= ~VDU_VDC_HINT;


	// Vertical interpolation
	uint32_t vint = (cmd & VDL_DC_VINTCTL_MASK) >> VDL_DC_VINTCTL_SHIFT;

	if (vint == VDL_CTL_ENABLE)
		vdc |= VDU_VDC_VINT;
	else if (vint == VDL_CTL_DISABLE)
		vdc &= ~VDU_VDC_VINT;


	// Dithering
	uint32_t dith = (cmd & VDL_DC_DITHERCTL_MASK) >> VDL_DC_DITHERCTL_SHIFT;

	if (dith == VDL_CTL_ENABLE)
		vdc |= VDU_VDC_DITHER;
	else if (dith == VDL_CTL_DISABLE)
		vdc &= ~VDU_VDC_DITHER;


	// MTXBYP (?)
	uint32_t mtxbyp = (cmd & VDL_DC_MTXBYPCTL_MASK) >> VDL_DC_MTXBYPCTL_SHIFT;

	if (mtxbyp == VDL_CTL_ENABLE)
		vdc |= VDU_VDC_MTXBYP;
	else if (mtxbyp == VDL_CTL_DISABLE)
		vdc &= ~VDU_VDC_MTXBYP;
}


//-------------------------------------------------
//  parse_av_word -
//-------------------------------------------------

void m2_vdu_device::parse_av_word(uint32_t cmd)
{
	if (cmd & VDL_AV_LD_HSTART)
	{
		uint32_t hstart = (cmd & VDL_AV_HSTART_MASK) >> VDL_AV_HSTART_SHIFT;
		m_avdi &= ~VDU_AVDI_HSTART_MASK;
		m_avdi |= hstart << VDU_AVDI_HSTART_SHIFT;
	}
	if (cmd & VDL_AV_LD_HWIDTH)
	{
		uint32_t hwidth = (cmd & VDL_AV_HWIDTH_MASK) >> VDL_AV_HWIDTH_SHIFT;
		m_avdi &= ~VDU_AVDI_HWIDTH_MASK;
		m_avdi |= hwidth << VDU_AVDI_HWIDTH_SHIFT;
	}
	if (cmd & VDL_AV_LD_HDOUBLE)
	{
		if (cmd & VDL_AV_HDOUBLE)
			m_avdi |= VDU_AVDI_HDOUBLE;
		else
			m_avdi &= ~VDU_AVDI_HDOUBLE;
	}
	if (cmd & VDL_AV_LD_VDOUBLE)
	{
		if (cmd & VDL_AV_VDOUBLE)
			m_avdi |= VDU_AVDI_VDOUBLE;
		else
			m_avdi &= ~VDU_AVDI_VDOUBLE;
	}
}


//-------------------------------------------------
//  parse_lc_word -
//-------------------------------------------------

void m2_vdu_device::parse_lc_word(uint32_t cmd)
{
	// TODO: This may not be used
	if (cmd & VDL_LC_LD_BYPASSTYPE)
	{
		m_vdli &= ~VDU_VDLI_BYPASSTYPE;
		m_vdli |= (cmd & VDU_VDLI_BYPASSTYPE) == VDU_VDLI_BYPASSTYPE_MSB ? VDU_VDLI_BYPASSTYPE_MSB : VDU_VDLI_BYPASSTYPE_LSB;
	}
	if (cmd & VDL_LC_LD_FBFORMAT)
	{
		m_vdli &= ~VDU_VDLI_FBFORMAT;
		m_vdli |= (cmd & VDL_LC_FBFORMAT) == VDL_LC_FBFORMAT_32 ? VDU_VDLI_FBFORMAT_32 : VDU_VDLI_FBFORMAT_16;
	}

	// Seems these two are always set by the command word
	if (cmd & VDL_LC_RANDOMDITHER)
		m_vdli |= VDU_VDLI_RANDOMDITHER;
	else
		m_vdli &= ~VDU_VDLI_RANDOMDITHER;

	if (cmd & VDL_LC_ONEVINTDIS)
		m_vdli |= VDU_VDLI_ONEVINTDIS;
	else
		m_vdli &= ~VDU_VDLI_ONEVINTDIS;
}


//-------------------------------------------------
//  draw_scanline - Draw a scanline
//-------------------------------------------------

void m2_vdu_device::draw_scanline(uint32_t *dst, uint32_t srclower, uint32_t srcupper)
{
	m2_bda_device *m_bda = (m2_bda_device*)owner(); // TEMP

	uint32_t hs = (m_avdi & VDU_AVDI_HSTART_MASK) >> VDU_AVDI_HSTART_SHIFT;
	uint32_t hw = (m_avdi & VDU_AVDI_HWIDTH_MASK) >> VDU_AVDI_HWIDTH_SHIFT;

	bool is32bpp = m_vdli & VDU_VDLI_FBFORMAT_32 ? true : false;
//  bool bypassmsb = m_vdli & VDU_VDLI_BYPASSTYPE_MSB ? true : false;
//  bool randomdith = m_vdli & VDU_VDLI_RANDOMDITHER ? true : false;

	uint32_t h = 0;

	// Left border
	while (h < hs)
	{
		*dst++ = rgb_t::black();
		++h;
	}

	// Active video area
	uint32_t vismax = std::min<uint32_t>(h + hw, m_htotal);

	if (is32bpp)
	{
		while (h < vismax)
		{
			*dst++ = m_bda->read_bus32(srclower);
			srclower += 4;
			++h;
		}
	}
	else
	{
		while (h < vismax)
		{
			uint16_t srcdata = m_bda->read_bus16(srclower);
			*dst++ = pal555(srcdata, 10, 5, 0);
			srclower += 2;
			++h;
		}
	}

	// Right border
	while (h < m_htotal)
	{
		*dst++ = rgb_t::black();
		++h;
	}
}


//-------------------------------------------------
//  draw_scanline_double - Draw a pixel-doubled scanline
//-------------------------------------------------

void m2_vdu_device::draw_scanline_double(uint32_t *dst, uint32_t srclower, uint32_t srcupper)
{
	m2_bda_device *m_bda = (m2_bda_device*)owner(); // TEMP

	uint32_t hs = (m_avdi & VDU_AVDI_HSTART_MASK) >> VDU_AVDI_HSTART_SHIFT;
	uint32_t hw = (m_avdi & VDU_AVDI_HWIDTH_MASK) >> VDU_AVDI_HWIDTH_SHIFT;

	bool is32bpp = m_vdli & VDU_VDLI_FBFORMAT_32 ? true : false;
//  bool bypassmsb = m_vdli & VDU_VDLI_BYPASSTYPE_MSB ? true : false;
//  bool randomdith = m_vdli & VDU_VDLI_RANDOMDITHER ? true : false;

	uint32_t h = 0;

	// Left border
	while (h < hs)
	{
		*dst++ = rgb_t::black();
		++h;
	}

	// Active video area
	uint32_t vismax = std::min<uint32_t>(h + hw, m_htotal);

	if (is32bpp)
	{
		while (h < vismax)
		{
			uint32_t srcdata = m_bda->read_bus32(srclower);

			srclower += 4;
			*dst++ = srcdata;
			*dst++ = srcdata;
			++h;
		}
	}
	else
	{
		while (h < vismax)
		{
			uint32_t srcdata = m_bda->read_bus16(srclower);
			srcdata = pal555(srcdata, 10, 5, 0);

			srclower += 2;
			*dst++ = srcdata;
			*dst++ = srcdata;
			++h;
		}
	}

	// Right border
	while (h < m_htotal)
	{
		*dst++ = rgb_t::black();
		++h;
	}
}


//-------------------------------------------------
//  core_update_screen -
//-------------------------------------------------



//-------------------------------------------------
//  screen_update -
//-------------------------------------------------

uint32_t m2_vdu_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	m2_bda_device *m_bda = (m2_bda_device*)owner(); // TEMP

	// TODO: Interlace mode
	uint32_t addr = m_fv0a;//screen.frame_number() & 1 ? m_fv1a : m_fv0a;

	// Fill entire screen with black if disabled
	if ((m_vrst & (VDU_VRST_VIDRESET | VDU_VRST_DVERESET)) || addr == 0)
	{
		bitmap.fill(rgb_t::black());
		return 0;
	}

	// Processing begins at VSYNC
	uint32_t v = 0;

	// Process VDLs until all lines are exhausted
	while (v < m_vtotal)
	{
		// Fetch the 4 header words
		uint32_t dmactl = m_bda->read_bus32(addr);
		uint32_t lower = m_bda->read_bus32(addr + 4);
		uint32_t upper = m_bda->read_bus32(addr + 8);
		uint32_t next = m_bda->read_bus32(addr + 12);

		addr += 16;

		// Word count includes the header
		uint32_t words = (dmactl & VDL_DMA_NWORDS_MASK) >> VDL_DMA_NWORDS_SHIFT;
		words -= 4;

		// Check and adjust the line count
		uint32_t lines = (dmactl & VDL_DMA_NLINES_MASK) >> VDL_DMA_NLINES_SHIFT;

		if (lines > 0)
		{
			uint32_t vend = v + lines;

			if (vend > m_vtotal)
				lines = vend - m_vtotal;
		}
		else
		{
			// A zero count denotes all of the remaining screen lines
			lines = m_vtotal - v;
		}

		// Parse the command list and update video registers accordingly
		while (words-- > 0)
		{
			uint32_t cmd = m_bda->read_bus32(addr);
			addr += 4;

			switch (cmd & 0xe0000000)
			{
				case VDL_DC:
				{
					parse_dc_word(cmd);
					break;
				}
				case VDL_AV:
				{
					parse_av_word(cmd);
					break;
				}
				case VDL_LC:
				{
					parse_lc_word(cmd);
					break;
				}
				default:
				{
					if (cmd != VDL_NOP)
						fatalerror("VDU: Unknown VDL command word\n");
					break;
				}
			}
		}

		// DMA from RAM to the display
		if (dmactl & VDL_DMA_ENABLE)
		{
			bool hdouble = m_avdi & VDU_AVDI_HDOUBLE ? true : false;
			bool vdouble = m_avdi & VDU_AVDI_VDOUBLE ? true : false;
//          bool onevintdis = m_vdli & VDU_VDLI_ONEVINTDIS ? true : false;

			uint32_t srclower = lower;
			uint32_t srcupper = upper;
			uint32_t mod = ((dmactl & VDL_DMA_MOD_MASK) >> VDL_DMA_MOD_SHIFT) << 5;

			// Draw these lines
			while (lines--)
			{
				// Line doubling is easily handled
				for (uint32_t ys = vdouble ? 2 : 1; ys > 0; --ys)
				{
					if (hdouble)
						draw_scanline_double(&bitmap.pix32(v, 0), srclower, srcupper);
					else
						draw_scanline(&bitmap.pix32(v, 0), srclower, srcupper);

					++v;
				}
				// Update the source addresses
				srclower += mod;
				srcupper += mod;
			}
		}
		else
		{
			// Blank this block of lines if DMA is disabled
			while (lines--)
			{
				uint32_t *dst = &bitmap.pix32(v, cliprect.min_x);

				for (uint32_t x = cliprect.min_x; x <= cliprect.max_x; ++x)
					*dst++ = rgb_t::black();

				++v;
			}
		}

		// Jump to the next VDL
		addr = next;
	}

	return 0;
}


//-------------------------------------------------
//  set_vint_timer -
//-------------------------------------------------

void m2_vdu_device::set_vint_timer(uint32_t id)
{
	uint32_t v;
	emu_timer *timer = (id == 0) ? m_vint0_timer : m_vint1_timer;

	if (id == 0)
		v = (m_vint & VDU_VINT_VLINE0_MASK) >> VDU_VINT_VLINE0_SHIFT;
	else
		v = (m_vint & VDU_VINT_VLINE1_MASK) >> VDU_VINT_VLINE1_SHIFT;

	if (v == 0)
	{
		// Apparently 0 is invalid
		timer->adjust(attotime::never);
	}
	else
	{
		// Adjust the count to what the core expects
		uint32_t vadj = (v - 1 + (m_vtotal - m_vstart)) % m_vtotal;
		timer->adjust(m_screen->time_until_pos(vadj));
	}
}



//**************************************************************************
//  CONTROL PORTS DEVICE
//**************************************************************************

//-------------------------------------------------
//  m2_ctrlport_device - constructor
//-------------------------------------------------

m2_ctrlport_device::m2_ctrlport_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, M2_CTRLPORT, tag, owner, clock)
{

}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void m2_ctrlport_device::device_start()
{

}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void m2_ctrlport_device::device_reset()
{

}


/***************************************************************************
    PUBLIC FUNCTIONS
***************************************************************************/

//-------------------------------------------------
//  read -
//-------------------------------------------------

READ32_MEMBER( m2_ctrlport_device::read )
{
	//const uint32_t byte_offs = offset << 2;
	uint32_t data = machine().rand();

	//switch (byte_offs)
	//{
		//default:
			//logerror("%s: CTRLPORT R: [%x] %x\n", machine().describe_context(), byte_offs, data);
	//}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

WRITE32_MEMBER( m2_ctrlport_device::write )
{
	//uint32_t byte_offs = offset << 2;

	//switch (byte_offs)
	//{
		//default:
			//logerror("%s: CTRLPORT W: [%x] %x\n", machine().describe_context(), byte_offs, data);
	//}
}


/***************************************************************************
    PRIVATE FUNCTIONS
***************************************************************************/





//**************************************************************************
//  CDE DEVICE
//**************************************************************************

//-------------------------------------------------
//  m2_cde_device - constructor
//-------------------------------------------------

m2_cde_device::m2_cde_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, M2_CDE, tag, owner, clock),
	m_cpu1(*this, finder_base::DUMMY_TAG),
	m_int_handler(*this),
	m_sdbg_out_handler(*this)
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void m2_cde_device::device_start()
{
	// Find our friend the BDA
	m_bda = downcast<m2_bda_device *>(machine().device("bda"));
	assert(m_bda != NULL);

	if (m_bda == NULL)
		throw device_missing_dependencies();

	// Resolve callbacks
	m_int_handler.resolve_safe();
	m_sdbg_out_handler.resolve_safe();

	// Init DMA
	m_dma[0].m_timer = timer_alloc(TIMER_ID_DMA1);
	m_dma[1].m_timer = timer_alloc(TIMER_ID_DMA2);

	// Register state for saving
	save_item(NAME(m_sdbg_cntl));
	save_item(NAME(m_int_status));
	save_item(NAME(m_int_enable));
	save_item(NAME(m_bblock_en));
	save_item(NAME(m_visa_dis));

	for (uint32_t i = 0; i < 8; ++i)
	{
		save_item(NAME(m_bio_device[i].m_setup), i);
		save_item(NAME(m_bio_device[i].m_cycle_time), i);
	}

	for (uint32_t i = 0; i < 2; ++i)
	{
		save_item(NAME(m_dma[i].m_cntl), i);
		save_item(NAME(m_dma[i].m_cbad), i);
		save_item(NAME(m_dma[i].m_cpad), i);
		save_item(NAME(m_dma[i].m_ccnt), i);
		save_item(NAME(m_dma[i].m_nbad), i);
		save_item(NAME(m_dma[i].m_npad), i);
		save_item(NAME(m_dma[i].m_ncnt), i);
		// timer
	}
}

//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void m2_cde_device::device_reset()
{
	m_sdbg_cntl = 0;
	m_int_status = 0;
	m_int_enable = 0;

	// TODO? Boot block is clear on reset
	m_bblock_en = 1; // ?
	m_visa_dis = 0;

	reset_dma(0);
	reset_dma(1);
}


//-------------------------------------------------
//  device_post_load - device-specific post-load
//-------------------------------------------------

void m2_cde_device::device_post_load()
{

}


//-------------------------------------------------
//  device_timer - a timer
//-------------------------------------------------

void m2_cde_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
		case TIMER_ID_READY:
			// TODO ?
			set_interrupt(CDE_ID_READY);
			break;

		case TIMER_ID_DMA1:
			next_dma(0);
			break;

		case TIMER_ID_DMA2:
			next_dma(1);
			break;

		default:
			throw emu_fatalerror("m2_cde_device::device_timer: Unknown CDE timer ID");
	}
}


//-------------------------------------------------
//  set_interrupt -
//-------------------------------------------------

void m2_cde_device::set_interrupt(uint32_t intmask)
{
	m_int_status |= (uint32_t)intmask;
	update_interrupts();
}


//-------------------------------------------------
//  update_interrupts -
//-------------------------------------------------

void m2_cde_device::update_interrupts()
{
	if (m_int_status & m_int_enable)
		m_int_handler(ASSERT_LINE);
	else
		m_int_handler(CLEAR_LINE);
}



/***************************************************************************
    PUBLIC FUNCTIONS
***************************************************************************/

//-------------------------------------------------
//  read -
//-------------------------------------------------

READ32_MEMBER( m2_cde_device::read )
{
	const uint32_t byte_offs = offset << 2;
	uint32_t data = 0;

	switch (byte_offs)
	{
		case CDE_DEVICE_ID:
		{
			data = 0x00010000;
			break;
		}
		case CDE_VERSION:
		{
			data = 0; // TODO
			break;
		}
		case CDE_SDBG_CNTL:
		{
			data = m_sdbg_cntl;
			break;
		}
		case CDE_SDBG_RD:
		{
			data = m_sdbg_in;
			break;
		}
		case CDE_INT_STS:
		{
			data = m_int_status;
			break;
		}
		case CDE_CD_STS_RD:
		{
			data = 0x000; // Status ready = 0x100
			break;
		}
		case CDE_INT_ENABLE:
		{
			data = m_int_enable;
			break;
		}
		case CDE_DEV_DETECT:
		{
			data = 0x0; // ?
			break;
		};
		case CDE_BBLOCK:
		{
			// 8, 80
			data = 0x80; // Needs to be non-zero
			break;
		}
		case CDE_UNIQ_ID_RD:
		{
			data = 0xffffffff; // ?
			break;
		}
		case CDE_BBLOCK_EN:
		{
			data = m_bblock_en;
			break;
		}
		case CDE_SYSTEM_CONF:
		{
			data = m_syscfg;
			break;
		}
		case CDE_MICRO_STATUS:
		{
			data = 0x20; // TODO
			break;
		}
		case CDE_MICRO_RWS:
		{
			break;
		}
		case CDE_VISA_DIS:
		{
			data = m_visa_dis;
			break;
		}
		case CDE_DMA1_CNTL:
		case CDE_DMA2_CNTL:
		{
			uint32_t ch = (byte_offs & 0x20) ? 1 : 0;
			data = m_dma[ch].m_cntl;
			break;
		}
		default:
		{
			//logerror("%s: CDE_R UNHANDLED: 0x%.8x 0x%.8x\n", machine().describe_context(), byte_offs, mem_mask));
		}
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

WRITE32_MEMBER( m2_cde_device::write )
{
	uint32_t byte_offs = offset << 2;
	uint32_t dmach = byte_offs & 0x20 ? 1 : 0;

	m2_reg_wmode wm_cw = byte_offs & 0x400 ? REG_CLEAR : REG_WRITE;
	m2_reg_wmode wm_cs = byte_offs & 0x400 ? REG_CLEAR : REG_SET;

	byte_offs &= ~0x400;

	switch (byte_offs)
	{
		case CDE_SDBG_CNTL:
		{
			// ........ ........ xxxxxxxx xxxx....      Clock scaler (written with 33MHz/38400 = 868)
			write_m2_reg(m_sdbg_cntl, data, wm_cw);
			break;
		}
		case CDE_SDBG_WRT:
		{
			m_sdbg_out_handler(data);
			set_interrupt(CDE_SDBG_WRT_DONE);
			break;
		}
		case CDE_INT_STS:
		{
			write_m2_reg(m_int_status, data, wm_cw);
			update_interrupts();
			break;
		}
		case CDE_INT_ENABLE:
		{
			write_m2_reg(m_int_enable, data, wm_cs);
			update_interrupts();
			break;
		}
		case CDE_RESET_CNTL:
		{
			if (data & 1)
			{
				// TODO: Should we reset both CPUs?
				downcast<cpu_device *>(&space.device())->pulse_input_line(INPUT_LINE_RESET, attotime::zero);

				// TODO: Is this correct?
				m_bblock_en = 0;
			}
			else if (data & 2)
			{
				// TODO: Hard reset
			}

			break;
		}
		case CDE_CD_CMD_WRT:
		{
			//set_interrupt(CDE_CD_CMD_WRT_DONE); // ?
			//set_interrupt(CDE_CD_STS_FL_DONE); // ?
			break;
		};
		case CDE_UNIQ_ID_CMD:
		{
			// TODO: What is this?
			timer_set(attotime::from_usec(250), TIMER_ID_READY);
			break;
		}
		case CDE_BBLOCK:
		{
			break;
		}

		case CDE_DEV0_SETUP:
		case CDE_DEV1_SETUP:
		case CDE_DEV2_SETUP:
		case CDE_DEV3_SETUP:
		case CDE_DEV4_SETUP:
		case CDE_DEV5_SETUP:
		case CDE_DEV6_SETUP:
		case CDE_DEV7_SETUP:
		{
			uint32_t id = (byte_offs - CDE_DEV0_SETUP) >> 3;
			write_m2_reg(m_bio_device[id].m_setup, data, wm_cw);
			break;
		}

		case CDE_DEV0_CYCLE_TIME:
		case CDE_DEV1_CYCLE_TIME:
		case CDE_DEV2_CYCLE_TIME:
		case CDE_DEV3_CYCLE_TIME:
		case CDE_DEV4_CYCLE_TIME:
		case CDE_DEV5_CYCLE_TIME:
		case CDE_DEV6_CYCLE_TIME:
		case CDE_DEV7_CYCLE_TIME:
		{
			uint32_t id = (byte_offs - CDE_DEV0_CYCLE_TIME) >> 3;
			write_m2_reg(m_bio_device[id].m_cycle_time, data, wm_cw);
			break;
		}

//      case CDE_SYSTEM_CONF:
		case CDE_VISA_DIS:
		{
			write_m2_reg(m_visa_dis, data, wm_cw);
			break;
		}
		case CDE_MICRO_RWS:
		case CDE_MICRO_WI:
		case CDE_MICRO_WOB:
		case CDE_MICRO_WO:
		case CDE_MICRO_STATUS:
		{
			break;
		}

		case CDE_DMA1_CNTL:
		case CDE_DMA2_CNTL:
		{
			uint32_t &ctrl = m_dma[dmach].m_cntl;
			uint32_t old = ctrl;

			write_m2_reg(ctrl, data, wm_cw);

			if (!(old & CDE_DMA_RESET) && (ctrl & CDE_DMA_RESET))
				reset_dma(dmach);

			if (!(old & CDE_DMA_CURR_VALID) && (ctrl & CDE_DMA_CURR_VALID))
				start_dma(dmach);

			break;
		}
		case CDE_DMA1_CBAD:
		case CDE_DMA2_CBAD:
		{
			write_m2_reg(m_dma[dmach].m_cbad, data, wm_cw);
			break;
		}
		case CDE_DMA1_CPAD:
		case CDE_DMA2_CPAD:
		{
			write_m2_reg(m_dma[dmach].m_cpad, data, wm_cw);
			break;
		}
		case CDE_DMA1_CCNT:
		case CDE_DMA2_CCNT:
		{
			write_m2_reg(m_dma[dmach].m_ccnt, data, wm_cw);
			break;
		}
		default:
		{
			//logerror("%s: CDE_W UNHANDLED: 0x%.8x 0x%.8x 0x%.8x\n", machine().describe_context(), byte_offs, data, mem_mask);
		}
	}
}


//-------------------------------------------------
//  sdbg_in -
//-------------------------------------------------

WRITE32_MEMBER( m2_cde_device::sdbg_in )
{
	m_sdbg_in = data;
	set_interrupt(CDE_SDBG_RD_DONE);
}




/***************************************************************************
    PRIVATE FUNCTIONS
***************************************************************************/

//-------------------------------------------------
//  reset_dma - Reset a DMA channel
//-------------------------------------------------

void m2_cde_device::reset_dma(uint32_t ch)
{
	m_dma[ch].m_cntl = 0;
	m_dma[ch].m_timer->adjust(attotime::never);
}


//-------------------------------------------------
//  start_dma - DMA between the PowerBus and BioBus
//-------------------------------------------------

void m2_cde_device::start_dma(uint32_t ch)
{
	dma_channel &dma_ch = m_dma[ch];
	address_space *dma_space = &m_cpu1->space();

	// TODO: DMA timing is probably inaccurate
	attotime delay = attotime::from_nsec(10);// * dma_ch.m_ccnt;

//  attotime delay = clocks_to_attotime(4 * dma_ch.m_ccnt);
	dma_ch.m_timer->adjust(delay);

	if (dma_ch.m_cntl & CDE_DMA_DIRECTION)
	{
		// PowerBus to BioBus
		throw emu_fatalerror("m2_cde_device::start_dma: CDE PowerBus to BioBus DMA currently unsupported");
	}
	else
	{
		// BioBus to PowerBus
#if 0
		logerror("%s: CDE DMA %u: [%.8x] -> [%.8x], 0x%.8x bytes\n", machine().describe_context(), ch, dma_ch.m_cbad, dma_ch.m_cpad, dma_ch.m_ccnt);
#endif
		// Determine the BioBus device from the address
		const uint32_t slot = address_to_biobus_slot(dma_ch.m_cbad);

		// Get the device parameters
		const uint32_t setup = m_bio_device[slot].m_setup;

		if (setup & CDE_DATAWIDTH_16)
		{
			// 16-bit case
			if (dma_ch.m_ccnt & 1)
				throw emu_fatalerror("m2_cde_device::start_dma: 16-bit DMA: Byte count must be even?");
			if (dma_ch.m_cpad & 1)
				throw emu_fatalerror("m2_cde_device::start_dma: 16-bit DMA: DMA destination must be word aligned?");

			const uint32_t srcinc = setup & CDE_READ_SETUP_IO ? 0 : 2;

			while (dma_ch.m_ccnt > 0)
			{
				uint16_t data = dma_space->read_word_unaligned(dma_ch.m_cbad); // FIX ME
				dma_space->write_word(dma_ch.m_cpad, data);

				dma_ch.m_cbad += srcinc;
				dma_ch.m_cpad += 2;
				dma_ch.m_ccnt -= 2;
			}
		}
		else
		{
			// 8-bit case
			const uint32_t srcinc = setup & CDE_READ_SETUP_IO ? 0 : 1;

			fatalerror("8-bit DMA untested\n");

			while (dma_ch.m_ccnt > 0)
			{
				uint8_t data = dma_space->read_byte(dma_ch.m_cbad);
				dma_space->write_byte(dma_ch.m_cpad, data);

				dma_ch.m_cbad += srcinc;
				dma_ch.m_cpad += 1;
				dma_ch.m_ccnt -= 1;
			}
		}
	}
}

//-------------------------------------------------
//  next_dma - Start the next DMA if set
//-------------------------------------------------

void m2_cde_device::next_dma(uint32_t ch)
{
	dma_channel &dma_ch = m_dma[ch];

	// TODO: HACK!
#if 1
	m_cpu1->set_cache_dirty();
#endif

	if (dma_ch.m_ccnt != 0)
		throw emu_fatalerror("m2_cde_device::next_dma: DMA count non-zero during next DMA");

	if (dma_ch.m_cntl & CDE_DMA_NEXT_VALID)
	{
		logerror("NEXT DMA CODE UNTESTED");

		// Update current address and count registers
		dma_ch.m_cbad = dma_ch.m_nbad;
		dma_ch.m_cpad = dma_ch.m_npad;
		dma_ch.m_ccnt = dma_ch.m_ncnt;
		dma_ch.m_cntl |= CDE_DMA_CURR_VALID;

		// Disable looping
		if (!(dma_ch.m_cntl & CDE_DMA_GO_FOREVER))
			dma_ch.m_cntl &= ~CDE_DMA_NEXT_VALID;

		start_dma(ch);
	}
	else
	{
		// DMA complete
		dma_ch.m_cntl &= ~CDE_DMA_CURR_VALID;
		set_interrupt(ch == 0 ? CDE_DMA1_DONE : CDE_DMA2_DONE);
	}
}


/***************************************************************************
 MPEG DEVICE
 ***************************************************************************/

//-------------------------------------------------
//  m2_mpeg_device - constructor
//-------------------------------------------------

m2_mpeg_device::m2_mpeg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, M2_MPEG, tag, owner, clock)
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void m2_mpeg_device::device_start()
{

}

//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void m2_mpeg_device::device_reset()
{

}

//-------------------------------------------------
//  read
//-------------------------------------------------

READ32_MEMBER( m2_mpeg_device::read )
{
	logerror("%s: MPEG READ: %08X\n", machine().describe_context(), offset);
	return 0;
}

//-------------------------------------------------
//  write
//-------------------------------------------------

WRITE32_MEMBER( m2_mpeg_device::write )
{
	logerror("%s: MPEG WRITE: %08X %08X\n", machine().describe_context(), offset, data);
}