// license:BSD-3-Clause // copyright-holders:Robbbert,FSanches and unknown others /*************************************************************************** machine.c Functions to emulate general aspects of the machine (RAM, ROM, interrupts, I/O ports) MAX_LUMPS 192 crude storage units - don't know much about it MAX_GRANULES 8 lumps consisted of granules.. aha MAX_SECTORS 5 and granules of sectors ***************************************************************************/ #include "emu.h" #include "includes/trs80.h" #define IRQ_M1_RTC 0x80 /* RTC on Model I */ #define IRQ_M1_FDC 0x40 /* FDC on Model I */ #define IRQ_M4_RTC 0x04 /* RTC on Model 4 */ #define CASS_RISE 0x01 /* high speed cass on Model III/4) */ #define CASS_FALL 0x02 /* high speed cass on Model III/4) */ #define MODEL4_MASTER_CLOCK 20275200 TIMER_CALLBACK_MEMBER(trs80_state::cassette_data_callback) { /* This does all baud rates. 250 baud (trs80), and 500 baud (all others) set bit 7 of "cassette_data". 1500 baud (trs80m3, trs80m4) is interrupt-driven and uses bit 0 of "cassette_data" */ double new_val = (m_cassette->input()); /* Check for HI-LO transition */ if ( m_old_cassette_val > -0.2 && new_val < -0.2 ) { m_cassette_data |= 0x80; /* 500 baud */ if (m_mask & CASS_FALL) /* see if 1500 baud */ { m_cassette_data = 0; m_irq |= CASS_FALL; m_maincpu->set_input_line(0, HOLD_LINE); } } else if ( m_old_cassette_val < -0.2 && new_val > -0.2 ) { if (m_mask & CASS_RISE) /* 1500 baud */ { m_cassette_data = 1; m_irq |= CASS_RISE; m_maincpu->set_input_line(0, HOLD_LINE); } } m_old_cassette_val = new_val; } /************************************* * * Port handlers. * *************************************/ READ8_MEMBER( trs80_state::trs80m4_e0_r ) { /* Indicates which devices are interrupting - d6..d3 not emulated. Whenever an interrupt occurs, this port is immediately read to find out which device requires service. Lowest-numbered bit takes precedence. We take this opportunity to clear the cpu INT line. d6 RS232 Error (Any of {FE, PE, OR} errors has occurred) d5 RS232 Rcv (DAV indicates a char ready to be picked up from uart) d4 RS232 Xmit (TBMT indicates ready to accept another char from cpu) d3 I/O Bus d2 RTC d1 Cass 1500 baud Falling d0 Cass 1500 baud Rising */ m_maincpu->set_input_line(0, CLEAR_LINE); return ~(m_mask & m_irq); } READ8_MEMBER( trs80_state::trs80m4_e4_r ) { /* Indicates which devices are interrupting - d6..d5 not emulated. Whenever an NMI occurs, this port is immediately read to find out which device requires service. Lowest-numbered bit takes precedence. We take this opportunity to clear the cpu NMI line. d7 status of FDC INTREQ (0=true) d6 status of Motor Timeout (0=true) d5 status of Reset signal (0=true - this will reboot the computer) */ m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); return ~(m_nmi_mask & m_nmi_data); } READ8_MEMBER( trs80_state::trs80m4_e8_r ) { /* not emulated d7 Clear-to-Send (CTS), Pin 5 d6 Data-Set-Ready (DSR), pin 6 d5 Carrier Detect (CD), pin 8 d4 Ring Indicator (RI), pin 22 d3,d2,d0 Not used d1 UART Receiver Input, pin 20 (pin 20 is also DTR) */ return 0; } READ8_MEMBER( trs80_state::trs80m4_ea_r ) { /* UART Status Register d7 Data Received ('1'=condition true) d6 Transmitter Holding Register empty ('1'=condition true) d5 Overrun Error ('1'=condition true) d4 Framing Error ('1'=condition true) d3 Parity Error ('1'=condition true) d2..d0 Not used */ uint8_t data=7; m_uart->write_swe(0); data |= m_uart->tbmt_r() ? 0x40 : 0; data |= m_uart->dav_r( ) ? 0x80 : 0; data |= m_uart->or_r( ) ? 0x20 : 0; data |= m_uart->fe_r( ) ? 0x10 : 0; data |= m_uart->pe_r( ) ? 0x08 : 0; m_uart->write_swe(1); return data; } READ8_MEMBER( trs80_state::trs80m4_ec_r ) { /* Reset the RTC interrupt */ m_irq &= ~IRQ_M4_RTC; return 0; } READ8_MEMBER( trs80_state::sys80_f9_r ) { /* UART Status Register - d6..d4 not emulated d7 Transmit buffer empty (inverted) d6 CTS pin d5 DSR pin d4 CD pin d3 Parity Error d2 Framing Error d1 Overrun d0 Data Available */ uint8_t data = 70; m_uart->write_swe(0); data |= m_uart->tbmt_r() ? 0 : 0x80; data |= m_uart->dav_r( ) ? 0x01 : 0; data |= m_uart->or_r( ) ? 0x02 : 0; data |= m_uart->fe_r( ) ? 0x04 : 0; data |= m_uart->pe_r( ) ? 0x08 : 0; m_uart->write_swe(1); return data; } READ8_MEMBER( trs80_state::lnw80_fe_r ) { return ((m_mode & 0x78) >> 3) | 0xf0; } READ8_MEMBER( trs80_state::trs80_ff_r ) { /* ModeSel and cassette data d7 cassette data from tape d2 modesel setting */ uint8_t data = (~m_mode & 1) << 5; return data | m_cassette_data; } READ8_MEMBER( trs80_state::trs80m4_ff_r ) { /* Return of cassette data stream from tape d7 Low-speed data d6..d1 info from write of port EC d0 High-speed data */ m_irq &= 0xfc; /* clear cassette interrupts */ return m_port_ec | m_cassette_data; } READ8_MEMBER( trs80_state::cp500_a11_flipflop_toggle ) { /* The A11 flipflop is used for enabling access to the system monitor code at the EPROM address range 3800-3fff */ uint8_t *rom = memregion("maincpu")->base(); uint8_t *bootrom = memregion("bootrom")->base(); int block; m_a11_flipflop ^= 1; //toggle the flip-flop at every read at io addresses 0xf4-f7 for (block=0; block<8; block++){ memcpy(&rom[block * 0x800], &bootrom[(block | m_a11_flipflop) * 0x800], 0x800); } return 0x00; //really?! } WRITE8_MEMBER( trs80_state::trs80m4_84_w ) { /* Hi-res graphics control - d6..d4 not emulated d7 Page Control d6 Fix upper memory d5 Memory bit 1 d4 Memory bit 0 d3 Invert Video d2 80/64 width d1 Select bit 1 d0 Select bit 0 */ /* get address space instead of io space */ address_space &mem = m_maincpu->space(AS_PROGRAM); uint8_t *base = m_region_maincpu->base(); m_mode = (m_mode & 0x73) | (data & 0x8c); m_model4 &= 0xce; m_model4 |= (data & 3) << 4; switch (data & 3) { case 0: /* normal operation */ if (m_model4 & 4) /* Model 4P gets RAM while Model 4 gets ROM */ { if (m_model4 & 8) m_bank1->set_base(base); else m_bank1->set_base(base + 0x10000); m_bank2->set_base(base + 0x11000); m_bank4->set_base(base + 0x137ea); } else { m_bank1->set_base(base); m_bank2->set_base(base + 0x01000); m_bank4->set_base(base + 0x037ea); } m_bank7->set_base(base + 0x14000); m_bank8->set_base(base + 0x1f400); m_bank9->set_base(base + 0x1f800); m_bank11->set_base(base + 0x05000); m_bank12->set_base(base + 0x06000); m_bank14->set_base(base + 0x09000); m_bank15->set_base(base + 0x0a000); m_bank17->set_base(base + 0x14000); m_bank18->set_base(base + 0x1f400); m_bank19->set_base(base + 0x1f800); mem.install_readwrite_handler (0x37e8, 0x37e9, read8_delegate(FUNC(trs80_state::trs80_printer_r), this), write8_delegate(FUNC(trs80_state::trs80_printer_w), this)); /* 3 & 13 */ mem.install_read_handler (0x3800, 0x3bff, read8_delegate(FUNC(trs80_state::trs80_keyboard_r), this)); /* 5 */ mem.install_readwrite_handler (0x3c00, 0x3fff, read8_delegate(FUNC(trs80_state::trs80_videoram_r), this), write8_delegate(FUNC(trs80_state::trs80_videoram_w), this)); /* 6 & 16 */ break; case 1: /* write-only ram backs up the rom */ if (m_model4 & 4) /* Model 4P gets RAM while Model 4 gets ROM */ { if (m_model4 & 8) m_bank1->set_base(base); else m_bank1->set_base(base + 0x10000); m_bank2->set_base(base + 0x11000); m_bank3->set_base(base + 0x137e8); m_bank4->set_base(base + 0x137ea); } else { m_bank1->set_base(base); m_bank2->set_base(base + 0x01000); m_bank3->set_base(base + 0x037e8); m_bank4->set_base(base + 0x037ea); } m_bank7->set_base(base + 0x14000); m_bank8->set_base(base + 0x1f400); m_bank9->set_base(base + 0x1f800); m_bank11->set_base(base + 0x10000); m_bank12->set_base(base + 0x11000); m_bank13->set_base(base + 0x137e8); m_bank14->set_base(base + 0x137ea); m_bank15->set_base(base + 0x0a000); m_bank17->set_base(base + 0x14000); m_bank18->set_base(base + 0x1f400); m_bank19->set_base(base + 0x1f800); mem.install_read_handler (0x3800, 0x3bff, read8_delegate(FUNC(trs80_state::trs80_keyboard_r), this)); /* 5 */ mem.install_readwrite_handler (0x3c00, 0x3fff, read8_delegate(FUNC(trs80_state::trs80_videoram_r), this), write8_delegate(FUNC(trs80_state::trs80_videoram_w), this)); /* 6 & 16 */ break; case 2: /* keyboard and video are moved to high memory, and the rest is ram */ m_bank1->set_base(base + 0x10000); m_bank2->set_base(base + 0x11000); m_bank3->set_base(base + 0x137e8); m_bank4->set_base(base + 0x137ea); m_bank5->set_base(base + 0x13800); m_bank6->set_base(base + 0x13c00); m_bank7->set_base(base + 0x14000); m_bank11->set_base(base + 0x10000); m_bank12->set_base(base + 0x11000); m_bank13->set_base(base + 0x137e8); m_bank14->set_base(base + 0x137ea); m_bank15->set_base(base + 0x13800); m_bank16->set_base(base + 0x13c00); m_bank17->set_base(base + 0x14000); m_bank18->set_base(base + 0x0a000); mem.install_read_handler (0xf400, 0xf7ff, read8_delegate(FUNC(trs80_state::trs80_keyboard_r), this)); /* 8 */ mem.install_readwrite_handler (0xf800, 0xffff, read8_delegate(FUNC(trs80_state::trs80_videoram_r), this), write8_delegate(FUNC(trs80_state::trs80_videoram_w), this)); /* 9 & 19 */ m_model4++; break; case 3: /* 64k of ram */ m_bank1->set_base(base + 0x10000); m_bank2->set_base(base + 0x11000); m_bank3->set_base(base + 0x137e8); m_bank4->set_base(base + 0x137ea); m_bank5->set_base(base + 0x13800); m_bank6->set_base(base + 0x13c00); m_bank7->set_base(base + 0x14000); m_bank8->set_base(base + 0x1f400); m_bank9->set_base(base + 0x1f800); m_bank11->set_base(base + 0x10000); m_bank12->set_base(base + 0x11000); m_bank13->set_base(base + 0x137e8); m_bank14->set_base(base + 0x137ea); m_bank15->set_base(base + 0x13800); m_bank16->set_base(base + 0x13c00); m_bank17->set_base(base + 0x14000); m_bank18->set_base(base + 0x1f400); m_bank19->set_base(base + 0x1f800); break; } } WRITE8_MEMBER( trs80_state::trs80m4_90_w ) { m_speaker->level_w(~data & 1); } WRITE8_MEMBER( trs80_state::trs80m4p_9c_w ) /* model 4P only - swaps the ROM with read-only RAM */ { /* Meaning of model4 variable: d5..d4 memory mode (as described in section above) d3 rom switch (1=enabled) only effective in mode0 and 1 d2 this is a Model 4P d1 this is a Model 4 d0 Video banking exists yes/no (1=not banked) */ m_model4 &= 0xf7; m_model4 |= (data << 3); if ((m_model4) && (~m_model4 & 0x20)) { switch (m_model4 & 8) { case 0: /* Read-only RAM replaces rom */ m_bank1->set_base(m_region_maincpu->base() + 0x10000); break; case 8: /* Normal setup - rom enabled */ m_bank1->set_base(m_region_maincpu->base()); break; } } } WRITE8_MEMBER( trs80_state::trs80m4_e0_w ) { /* Interrupt settings - which devices are allowed to interrupt - bits align with read of E0 d6 Enable Rec Err d5 Enable Rec Data d4 Enable Xmit Emp d3 Enable I/O int d2 Enable RT int d1 C fall Int d0 C Rise Int */ m_mask = data; } WRITE8_MEMBER( trs80_state::trs80m4_e4_w ) { /* Disk to NMI interface d7 1=enable disk INTRQ to generate NMI d6 1=enable disk Motor Timeout to generate NMI */ m_nmi_mask = data; } WRITE8_MEMBER( trs80_state::trs80m4_e8_w ) { /* d1 when '1' enables control register load (see below) */ m_reg_load = data & 2; } WRITE8_MEMBER( trs80_state::trs80m4_e9_w ) { /* UART set baud rate. Rx = bits 0..3, Tx = bits 4..7 00h 50 11h 75 22h 110 33h 134.5 44h 150 55h 300 66h 600 77h 1200 88h 1800 99h 2000 AAh 2400 BBh 3600 CCh 4800 DDh 7200 EEh 9600 FFh 19200 Note: this may be a COM5016 dual baud rate generator, or may be an equivalent circuit embedded in the gate array ASIC on the trs80 model 4 */ static const int baud_clock[]={ 800, 1200, 1760, 2152, 2400, 4800, 9600, 19200, 28800, 32000, 38400, 57600, 76800, 115200, 153600, 307200 }; m_uart->set_receiver_clock(baud_clock[data & 0x0f]); m_uart->set_transmitter_clock(baud_clock[data >> 4]); } WRITE8_MEMBER( trs80_state::trs80m4_ea_w ) { if (m_reg_load) /* d2..d0 not emulated d7 Even Parity Enable ('1'=even, '0'=odd) d6='1',d5='1' for 8 bits d6='0',d5='1' for 7 bits d6='1',d5='0' for 6 bits d6='0',d5='0' for 5 bits d4 Stop Bit Select ('1'=two stop bits, '0'=one stop bit) d3 Parity Inhibit ('1'=disable; No parity, '0'=parity enabled) d2 Break ('0'=disable transmit data; continuous RS232 'SPACE' condition) d1 Request-to-Send (RTS), pin 4 d0 Data-Terminal-Ready (DTR), pin 20 */ { m_uart->write_cs(0); m_uart->write_nb1(BIT(data, 6)); m_uart->write_nb2(BIT(data, 5)); m_uart->write_tsb(BIT(data, 4)); m_uart->write_eps(BIT(data, 7)); m_uart->write_np(BIT(data, 3)); m_uart->write_cs(1); } else { /* not emulated d7,d6 Not used d5 Secondary Unassigned, pin 18 d4 Secondary Transmit Data, pin 14 d3 Secondary Request-to-Send, pin 19 d2 Break ('0'=disable transmit data; continuous RS232 'SPACE' condition) d1 Data-Terminal-Ready (DTR), pin 20 d0 Request-to-Send (RTS), pin 4 */ } } WRITE8_MEMBER( trs80_state::trs80m4_ec_w ) { /* Hardware settings - d5..d4 not emulated d6 CPU fast (1=4MHz, 0=2MHz) d5 1=Enable Video Wait d4 1=Enable External I/O bus d3 1=Enable Alternate Character Set d2 Mode Select (0=64 chars, 1=32chars) d1 Cassette Motor (1=On) */ m_maincpu->set_unscaled_clock(data & 0x40 ? MODEL4_MASTER_CLOCK/5 : MODEL4_MASTER_CLOCK/10); m_mode = (m_mode & 0xde) | ((data & 4) ? 1 : 0) | ((data & 8) ? 0x20 : 0); m_cassette->change_state(( data & 2 ) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR ); m_port_ec = data & 0x7e; } /* Selection of drive and parameters - d6..d5 not emulated. A write also causes the selected drive motor to turn on for about 3 seconds. When the motor turns off, the drive is deselected. d7 1=MFM, 0=FM d6 1=Wait d5 1=Write Precompensation enabled d4 0=Side 0, 1=Side 1 d3 1=select drive 3 d2 1=select drive 2 d1 1=select drive 1 d0 1=select drive 0 */ WRITE8_MEMBER( trs80_state::trs80m4_f4_w ) { floppy_image_device *floppy = nullptr; if (BIT(data, 0)) floppy = m_floppy0->get_device(); if (BIT(data, 1)) floppy = m_floppy1->get_device(); if (BIT(data, 2)) floppy = m_floppy2->get_device(); if (BIT(data, 3)) floppy = m_floppy3->get_device(); m_fdc->set_floppy(floppy); if (floppy) { floppy->mon_w(0); floppy->ss_w(BIT(data, 4)); } m_fdc->dden_w(!BIT(data, 7)); } WRITE8_MEMBER( trs80_state::sys80_f8_w ) { /* not emulated d2 reset UART (XR pin) d1 DTR d0 RTS */ } WRITE8_MEMBER( trs80_state::sys80_fe_w ) { /* not emulated d4 select internal or external cassette player */ m_tape_unit = (data & 0x10) ? 2 : 1; } /* lnw80 can switch out all the devices, roms and video ram to be replaced by graphics ram. */ WRITE8_MEMBER( trs80_state::lnw80_fe_w ) { /* lnw80 video options d3 bankswitch lower 16k between roms and hires ram (1=hires) d2 enable colour \ d1 hres / these 2 are the bits from the MODE command of LNWBASIC d0 inverse video (entire screen) */ /* get address space instead of io space */ address_space &mem = m_maincpu->space(AS_PROGRAM); m_mode = (m_mode & 0x87) | ((data & 0x0f) << 3); if (data & 8) { mem.unmap_readwrite (0x0000, 0x3fff); mem.install_readwrite_handler (0x0000, 0x3fff, read8_delegate(FUNC(trs80_state::trs80_gfxram_r), this), write8_delegate(FUNC(trs80_state::trs80_gfxram_w), this)); } else { mem.unmap_readwrite (0x0000, 0x3fff); mem.install_read_bank (0x0000, 0x2fff, "bank1"); membank("bank1")->set_base(m_region_maincpu->base()); mem.install_readwrite_handler (0x37e0, 0x37e3, read8_delegate(FUNC(trs80_state::trs80_irq_status_r), this), write8_delegate(FUNC(trs80_state::trs80_motor_w), this)); mem.install_readwrite_handler (0x37e8, 0x37eb, read8_delegate(FUNC(trs80_state::trs80_printer_r), this), write8_delegate(FUNC(trs80_state::trs80_printer_w), this)); mem.install_read_handler (0x37ec, 0x37ec, read8_delegate(FUNC(trs80_state::trs80_wd179x_r), this)); mem.install_write_handler (0x37ec, 0x37ec, write8_delegate(FUNC(fd1793_device::cmd_w),(fd1793_device*)m_fdc)); mem.install_readwrite_handler (0x37ed, 0x37ed, read8_delegate(FUNC(fd1793_device::track_r),(fd1793_device*)m_fdc), write8_delegate(FUNC(fd1793_device::track_w),(fd1793_device*)m_fdc)); mem.install_readwrite_handler (0x37ee, 0x37ee, read8_delegate(FUNC(fd1793_device::sector_r),(fd1793_device*)m_fdc), write8_delegate(FUNC(fd1793_device::sector_w),(fd1793_device*)m_fdc)); mem.install_readwrite_handler (0x37ef, 0x37ef, read8_delegate(FUNC(fd1793_device::data_r),(fd1793_device*)m_fdc),write8_delegate( FUNC(fd1793_device::data_w),(fd1793_device*)m_fdc)); mem.install_read_handler (0x3800, 0x38ff, 0, 0x0300, 0, read8_delegate(FUNC(trs80_state::trs80_keyboard_r), this)); mem.install_readwrite_handler (0x3c00, 0x3fff, read8_delegate(FUNC(trs80_state::trs80_videoram_r), this), write8_delegate(FUNC(trs80_state::trs80_videoram_w), this)); } } WRITE8_MEMBER( trs80_state::trs80_ff_w ) { /* Standard output port of Model I d3 ModeSel bit d2 Relay d1, d0 Cassette output */ static const double levels[4] = { 0.0, -1.0, 0.0, 1.0 }; static int init = 0; m_cassette->change_state(( data & 4 ) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR ); m_cassette->output(levels[data & 3]); m_cassette_data &= ~0x80; m_mode = (m_mode & 0xfe) | ((data & 8) >> 3); if (!init) { init = 1; static int16_t speaker_levels[4] = { 0, -32768, 0, 32767 }; m_speaker->set_levels(4, speaker_levels); } /* Speaker for System-80 MK II - only sounds if relay is off */ if (~data & 4) m_speaker->level_w(data & 3); } WRITE8_MEMBER( trs80_state::trs80m4_ff_w ) { /* Cassette port d1, d0 Cassette output */ static const double levels[4] = { 0.0, -1.0, 0.0, 1.0 }; m_cassette->output(levels[data & 3]); m_cassette_data &= ~0x80; } /************************************* * * Interrupt handlers. * *************************************/ INTERRUPT_GEN_MEMBER(trs80_state::trs80_rtc_interrupt) { /* This enables the processing of interrupts for the clock and the flashing cursor. The OS counts one tick for each interrupt. The Model I has 40 ticks per second, while the Model III/4 has 30. */ if (m_model4) // Model 4 { if (m_mask & IRQ_M4_RTC) { m_irq |= IRQ_M4_RTC; device.execute().set_input_line(0, HOLD_LINE); } } else // Model 1 { m_irq |= IRQ_M1_RTC; device.execute().set_input_line(0, HOLD_LINE); } } void trs80_state::trs80_fdc_interrupt_internal() { if (m_model4) { if (m_nmi_mask & 0x80) // Model 4 does a NMI { m_nmi_data = 0x80; m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); } } else // Model 1 does a IRQ { m_irq |= IRQ_M1_FDC; m_maincpu->set_input_line(0, HOLD_LINE); } } INTERRUPT_GEN_MEMBER(trs80_state::trs80_fdc_interrupt)/* not used - should it be? */ { trs80_fdc_interrupt_internal(); } WRITE_LINE_MEMBER(trs80_state::trs80_fdc_intrq_w) { if (state) { trs80_fdc_interrupt_internal(); } else { if (m_model4) m_nmi_data = 0; else m_irq &= ~IRQ_M1_FDC; } } /************************************* * * * Memory handlers * * * *************************************/ READ8_MEMBER( trs80_state::trs80_wd179x_r ) { uint8_t data = 0xff; if (BIT(m_io_config->read(), 7)) data = m_fdc->status_r(space, offset); return data; } READ8_MEMBER( trs80_state::trs80_printer_r ) { return m_cent_status_in->read(); } WRITE8_MEMBER( trs80_state::trs80_printer_w ) { m_cent_data_out->write(space, 0, data); m_centronics->write_strobe(0); m_centronics->write_strobe(1); } WRITE8_MEMBER( trs80_state::trs80_cassunit_w ) { /* not emulated 01 for unit 1 (default) 02 for unit 2 */ m_tape_unit = data; } READ8_MEMBER( trs80_state::trs80_irq_status_r ) { /* (trs80l2) Whenever an interrupt occurs, 37E0 is read to see what devices require service. d7 = RTC d6 = FDC d2 = Communications (not emulated) All interrupting devices are serviced in a single interrupt. There is a mask byte, which is dealt with by the DOS. We take the opportunity to reset the cpu INT line. */ int result = m_irq; m_maincpu->set_input_line(0, CLEAR_LINE); m_irq = 0; return result; } WRITE8_MEMBER( trs80_state::trs80_motor_w ) { floppy_image_device *floppy = nullptr; if (BIT(data, 0)) floppy = m_floppy0->get_device(); if (BIT(data, 1)) floppy = m_floppy1->get_device(); if (BIT(data, 2)) floppy = m_floppy2->get_device(); if (BIT(data, 3)) floppy = m_floppy3->get_device(); m_fdc->set_floppy(floppy); if (floppy) { floppy->mon_w(0); floppy->ss_w(BIT(data, 4)); } // switch to fm m_fdc->dden_w(1); } /************************************* * Keyboard * *************************************/ READ8_MEMBER( trs80_state::trs80_keyboard_r ) { u8 i, result = 0; for (i = 0; i < 8; i++) if (BIT(offset, i)) result |= m_io_keyboard[i]->read(); return result; } /************************************* * Machine * *************************************/ void trs80_state::machine_start() { m_tape_unit=1; m_reg_load=1; m_nmi_data=0xff; m_cassette_data_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(trs80_state::cassette_data_callback),this)); m_cassette_data_timer->adjust( attotime::zero, 0, attotime::from_hz(11025) ); } void trs80_state::machine_reset() { m_cassette_data = 0; } MACHINE_RESET_MEMBER(trs80_state,trs80m4) { address_space &mem = m_maincpu->space(AS_PROGRAM); m_cassette_data = 0; mem.install_read_bank (0x0000, 0x0fff, "bank1"); m_bank1 = membank("bank1"); mem.install_read_bank (0x1000, 0x37e7, "bank2"); m_bank2 = membank("bank2"); mem.install_read_bank (0x37e8, 0x37e9, "bank3"); m_bank3 = membank("bank3"); mem.install_read_bank (0x37ea, 0x37ff, "bank4"); m_bank4 = membank("bank4"); mem.install_read_bank (0x3800, 0x3bff, "bank5"); m_bank5 = membank("bank5"); mem.install_read_bank (0x3c00, 0x3fff, "bank6"); m_bank6 = membank("bank6"); mem.install_read_bank (0x4000, 0xf3ff, "bank7"); m_bank7 = membank("bank7"); mem.install_read_bank (0xf400, 0xf7ff, "bank8"); m_bank8 = membank("bank8"); mem.install_read_bank (0xf800, 0xffff, "bank9"); m_bank9 = membank("bank9"); mem.install_write_bank (0x0000, 0x0fff, "bank11"); m_bank11 = membank("bank11"); mem.install_write_bank (0x1000, 0x37e7, "bank12"); m_bank12 = membank("bank12"); mem.install_write_bank (0x37e8, 0x37e9, "bank13"); m_bank13 = membank("bank13"); mem.install_write_bank (0x37ea, 0x37ff, "bank14"); m_bank14 = membank("bank14"); mem.install_write_bank (0x3800, 0x3bff, "bank15"); m_bank15 = membank("bank15"); mem.install_write_bank (0x3c00, 0x3fff, "bank16"); m_bank16 = membank("bank16"); mem.install_write_bank (0x4000, 0xf3ff, "bank17"); m_bank17 = membank("bank17"); mem.install_write_bank (0xf400, 0xf7ff, "bank18"); m_bank18 = membank("bank18"); mem.install_write_bank (0xf800, 0xffff, "bank19"); m_bank19 = membank("bank19"); trs80m4p_9c_w(mem, 0, 1); /* Enable the ROM */ trs80m4_84_w(mem, 0, 0); /* switch in devices at power-on */ } MACHINE_RESET_MEMBER(trs80_state,lnw80) { address_space &space = m_maincpu->space(AS_PROGRAM); m_cassette_data = 0; m_reg_load = 1; lnw80_fe_w(space, 0, 0); } MACHINE_RESET_MEMBER(trs80_state,cp500) { m_a11_flipflop = 0; MACHINE_RESET_CALL_MEMBER( trs80m4 ); } /*************************************************************************** PARAMETERS ***************************************************************************/ #define LOG 1 #define CMD_TYPE_OBJECT_CODE 0x01 #define CMD_TYPE_TRANSFER_ADDRESS 0x02 #define CMD_TYPE_END_OF_PARTITIONED_DATA_SET_MEMBER 0x04 #define CMD_TYPE_LOAD_MODULE_HEADER 0x05 #define CMD_TYPE_PARTITIONED_DATA_SET_HEADER 0x06 #define CMD_TYPE_PATCH_NAME_HEADER 0x07 #define CMD_TYPE_ISAM_DIRECTORY_ENTRY 0x08 #define CMD_TYPE_END_OF_ISAM_DIRECTORY_ENTRY 0x0a #define CMD_TYPE_PDS_DIRECTORY_ENTRY 0x0c #define CMD_TYPE_END_OF_PDS_DIRECTORY_ENTRY 0x0e #define CMD_TYPE_YANKED_LOAD_BLOCK 0x10 #define CMD_TYPE_COPYRIGHT_BLOCK 0x1f /*************************************************************************** IMPLEMENTATION ***************************************************************************/ QUICKLOAD_LOAD_MEMBER( trs80_state, trs80_cmd ) { address_space &program = m_maincpu->space(AS_PROGRAM); uint8_t type, length; uint8_t data[0x100]; uint8_t addr[2]; void *ptr; while (!image.image_feof()) { image.fread( &type, 1); image.fread( &length, 1); length -= 2; int block_length = length ? length : 256; switch (type) { case CMD_TYPE_OBJECT_CODE: { image.fread( &addr, 2); uint16_t address = (addr[1] << 8) | addr[0]; if (LOG) logerror("/CMD object code block: address %04x length %u\n", address, block_length); ptr = program.get_write_ptr(address); image.fread( ptr, block_length); } break; case CMD_TYPE_TRANSFER_ADDRESS: { image.fread( &addr, 2); uint16_t address = (addr[1] << 8) | addr[0]; if (LOG) logerror("/CMD transfer address %04x\n", address); m_maincpu->set_state_int(Z80_PC, address); } break; case CMD_TYPE_LOAD_MODULE_HEADER: image.fread( &data, block_length); if (LOG) logerror("/CMD load module header '%s'\n", data); break; case CMD_TYPE_COPYRIGHT_BLOCK: image.fread( &data, block_length); if (LOG) logerror("/CMD copyright block '%s'\n", data); break; default: image.fread( &data, block_length); logerror("/CMD unsupported block type %u!\n", type); } } return image_init_result::PASS; }