onsdag 22 februari 2017

The prodigy #10 - the BCD display part 4

Now when the segments works we need to select the right digit to lit. MAME has a layout system where it is possible to create simple layouts from textual descriptions. We will create a small layout and show how to manipulate this from the driver. The BCD digit is defined like this:
<led7seg> <color blue="0.0" green="0.0" red="0.75"></color>
</led7seg>

Which allows us to make it redish. The actual BCD display should look something like the picture to the right. To actually place out some digits we use the bezel
<bezel element="digit" name="digit0">
<bounds bottom="885" left="70" right="100" top="840">
</bounds>
This defines "digit0" which can be manipulated from the driver by including the layout, or rather the compiled version which is compressed during build time.

#include "prodigy.lh"


We need to synchronize the update of the UI digit element with the ROM software so we know when to present the segments and to which digit. Fortunately there is an interrupt firing between the VIA shift register updates so we can create yes another trampoline in the driver code for this

MCFG_VIA6522_IRQ_HANDLER(WRITELINE(prodigy_state, irq_handler));

This handler is now responsible for interrupting the CPU but we also do the update of the BCD display

WRITE_LINE_MEMBER(prodigy_state::irq_handler)
{
    m_maincpu->set_input_line(M6502_IRQ_LINE, state);
    update_bcd();
}


We need also to know which digit to lit so we need to feed the 74145 from the VIA Port B bit 0 and 1. In fact Bit 2 and 3 are also connected but used for something else that we will explore later.

MCFG_VIA6522_WRITEPB_HANDLER(WRITE8(prodigy_state, via_pb_w))
...
WRITE8_MEMBER( prodigy_state::via_pb_w )
{
    m_74145->write( data & 0x0f ); // Write PB0-PB3 to the 74145
}

Viola, so when the interrupt fires the 74145 can be read to know the digit:

void prodigy_state::update_bcd()
{
 LOGBCD("%s\n", FUNCNAME);
 uint8_t ttl74145_data;
 uint8_t digit_nbr = 4;

 ttl74145_data = m_74145->read();

 if ((ttl74145_data & 0x0f) != 0x00)
 {
  switch (ttl74145_data & 0x0f)
  {
  case 0x01: digit_nbr = 0; break;
  case 0x02: digit_nbr = 1; break;
  case 0x04: digit_nbr = 2; break;
  case 0x08: digit_nbr = 3; break;
  }

  m_segments = m_digit;
  output().set_digit_value( digit_nbr, m_segments);
 }
}

The last statement will form the tag = "digit%c", '0' + digit_number; eg digit0 to digit3, and then lit the segments for that digit in the layout. Running the final code will render this in MAME:


Next step will be to trace out the keypad and get the software running, stay tuned!

tisdag 21 februari 2017

The prodigy #9 - the BCD display part 3


Netlist is a CPU device in MAME that slices the time at a higher rate than the clocks on the other devices and is because of this able to calculate levels in analog as well as in digital circuits between the clock cycles. This is important as it can also span multiple clock domains and synchronize independently running sub circuits including CPU:s.

The drawback is of course that it takes more host CPU power to emulate things this way so the trick is to find a good mid ground. In the prodigy project we need a 74164 device that is implemented as part of the Netlist framework in order to contribute to this better emulation.

In the previous blog we checked out how to emulate the 74164 the usual way. To do the same thing using MAME Netlists we need to write a netlist device and it is also quite small however the way of connecting it the Prodigy driver is more complex.

If we start with the device configuration it looks like this:

MCFG_DEVICE_ADD(NETLIST_TAG, NETLIST_CPU, XTAL_2MHz * 30)
MCFG_NETLIST_SETUP(prodigy)

MCFG_NETLIST_LOGIC_INPUT(NETLIST_TAG, "cb1", "cb1.IN", 0)
MCFG_NETLIST_LOGIC_INPUT(NETLIST_TAG, "cb2", "cb2.IN", 0)
MCFG_NETLIST_LOGIC_OUTPUT(NETLIST_TAG, "bcd_bit0",  "bcd_bit0",  prodigy_state, bcd_bit0_cb, "")
MCFG_NETLIST_LOGIC_OUTPUT(NETLIST_TAG, "bcd_bit1",  "bcd_bit1",  prodigy_state, bcd_bit1_cb, "")
MCFG_NETLIST_LOGIC_OUTPUT(NETLIST_TAG, "bcd_bit2",  "bcd_bit2",  prodigy_state, bcd_bit2_cb, "")
MCFG_NETLIST_LOGIC_OUTPUT(NETLIST_TAG, "bcd_bit3",  "bcd_bit3",  prodigy_state, bcd_bit3_cb, "")
MCFG_NETLIST_LOGIC_OUTPUT(NETLIST_TAG, "bcd_bit4",  "bcd_bit4",  prodigy_state, bcd_bit4_cb, "")
MCFG_NETLIST_LOGIC_OUTPUT(NETLIST_TAG, "bcd_bit5",  "bcd_bit5",  prodigy_state, bcd_bit5_cb, "")
MCFG_NETLIST_LOGIC_OUTPUT(NETLIST_TAG, "bcd_bit6",  "bcd_bit6",  prodigy_state, bcd_bit6_cb, "")
MCFG_NETLIST_LOGIC_OUTPUT(NETLIST_TAG, "bcd_bit7",  "bcd_bit7",  prodigy_state, bcd_bit7_cb, "")

As can be seen the NETLIST_CPU runs at 30 times the 2MHz of the CPU and VIA devices. This is not a tuned value at this point as we may need to add more things ot the Netlist later. The second thing that strikes the eye is that there is no sign of the 74164 itself but instead a netlist called 'prodigy'. This is because the circuit itself is a sub device put in separate file, the setup of the 74164 looks like this:

 TTL_INPUT(cb1, 0)
 TTL_INPUT(cb2, 0)

 TTL_74164(SHIFT, cb2, cb2, high, cb1)
 ALIAS(bcd_bit0, SHIFT.QA)
 ALIAS(bcd_bit1, SHIFT.QB)
 ALIAS(bcd_bit2, SHIFT.QC)
 ALIAS(bcd_bit3, SHIFT.QD)
 ALIAS(bcd_bit4, SHIFT.QE)
 ALIAS(bcd_bit5, SHIFT.QF)
 ALIAS(bcd_bit6, SHIFT.QG)
 ALIAS(bcd_bit7, SHIFT.QH)


To actually hook up all this to the VIA requires some more glue, first we need to tell MAME that we need the netlist circuit and the inputs to it.


 required_device<netlist_mame_device_t> m_bcd;
 required_device<netlist_mame_logic_input_t> m_cb1;
 required_device<netlist_mame_logic_input_t> m_cb2;


We also the callbacks where the Netlist communicates back the changes in signal states:


NETDEV_LOGIC_CALLBACK_MEMBER(prodigy_state::bcd_bit0_cb) 
{ 
 if (data != 0) 
  m_digit |= 0x01;
 else 
  m_digit &= ~(0x01);
}

Lastly we use some trampoline methods to route the VIA output into our Netlist:


MCFG_VIA6522_CB1_HANDLER(WRITELINE(prodigy_state, via_cb1_w))
MCFG_VIA6522_CB2_HANDLER(WRITELINE(prodigy_state, via_cb2_w))

WRITE_LINE_MEMBER(prodigy_state::via_cb1_w)
{
 LOGCLK("%s: %d\n", FUNCNAME, state);
 m_cb1->write(state);
}

WRITE_LINE_MEMBER(prodigy_state::via_cb2_w)
{
 LOGCLK("%s: %d\n", FUNCNAME, state);
 m_cb2->write(state);
}

To summarize the chain of events that takes place:

  • The CB1 and CB2 trampoline handlers, via_cb1_w and via_cb2_w respectivelly, are called when the VIA device generates changes on these outputs. 
  • The  handlers calls the write method on the netlist input devices m_cb1 and m_cb2. 
  • The netlist is crunched at 30 times the CPU clock, at an emulated 60MHz, and are able to calculate the logical signals at this rate. 
  • When the 74164 output signals enters a TTL low or high levels the bcd_bitx_cb() callback routine corresponding to the bit that has changed is called with the logical state. 
  • The callbacks finally compiles all the changes into a 8 bit pattern which are the segment values to be lit!
Next thing is to lit the correct BCD display! 

The prodigy #8 - the BCD display part 2

In order to emulate the Prodigy BCD display we need to write a 74164 device as MAME currently not has it. This should be straight forward as the chip is quite simple.


In MAME there are plenty of devices already emulated that can be configured and connected to other devices in a specific board driver using some MCFG macros. The MCFG macros are a loosely standardized way of configure and setting up relationships between devices in MAME.

To connect devices to the executed ROM code these are either mapped onto the address map using AM macros where a specific function in the device driver is called when accessing the address range. Which one however varies from device to device and you need to study how other have done it or just read the device source code.

In our case the 74164 is connected through the VIA device which we mapped up earlier using the following lines of macros:

MCFG_VIA6522_CB1_HANDLER(DEVWRITELINE("shift", ttl74164_device, a_w)) 
MCFG_VIA6522_CB2_HANDLER(DEVWRITELINE("shift", ttl74164_device, clock_w))
MCFG_74164_ADD("shift")

These macros connects the newly written device 'ttl74164_device' instance called 'shift' with clock input from VIA CB2 output and the A input from the VIA CB1 output. Last rows adds the actual 74164 itself and names it "shift" so MAME can look it up in runtime when putting the pieces together.

Now the missing piece is the 74164 driver itself. I wrote it up the classical way and submitted a PR to MAME, check it out here, but it was rejected as will all synchronous devices going forward. Here is why:

The way MAME works each CPU gets a certain amount of time to execute. When accessing devices the state of the signals are always adjusted according to the input in a chained manner which works pretty well as long as there is only one CPU/device talking. As soon as there is another source of changes such as a clocked circuitry there is a risk that the one part of the circuit is not up to date with the other clock.

This was revealed in a parallel work in MAME by Ryan Holtz with the Hazeltine driver, where multiple devices were supposed to deliver a unified bit field on an edge of a clock and in fact only the first part of the circuit held the expected data at the time as the latter part had not yet been updated. This is a common problem with logic emulation in general why there is a technique using oversampling so that sub cycle changes can be emulated at a higher precision than just connecting the signals.

This is accomplished in MAME by using the Netlist CPU contributed by Couriersud. It is not a traditional CPU but a device that runs at a higher clock rate than the emulated clocks in the system and constantly updates a circuit described as a Netlist This is what is used in the Pong game MAME emulation.


The Pong game had no CPU but only discrete components. The MAME Netlist system can describe both analog and logic circuits even mixed together at a decent performance.

Lets see how we can use a Netlist to solve the multiple clock alignment problem in MAME for the 74164 next!

The prodigy #7 - the BCD display part 1





Tracing the VIA towards the BCD display revealed an interesting circuit to emulate. The 6522 VIA has a poor mans UART builtin in the form of a 8 bit shift register that is very versatile. It can shift both in and out clocked by internal timers, the VIA clock or an external clock. It also has a continuous mode so all together there are numerous ways to use it. In the prodigy it is used to drive the segments of the BCD display.



The BCD display is of common anode type where two bits on VIA port B selects one of the four anodes to light up through a TTL 74145 chip. The shift register is set up by the Prodigy ROM to put the internal VIA clock on CB1 and data on CB2 which is connected to a 74164 chip which translates the bits shifted in to it to an 8 bit output which drives the segments for one of the 4 BCD displays selected by the 74145. There is a need for more current than the TTL circuits can drive to lit up the segments and why there is also 4 x PN2907 transistors and 2 x 75491 chips to boost the current.

Now, how would we emulate this in MAME? First we need to check what chips are already emulated and a quick check in the source code repository reveals that there was no 74164 device.

So the next thing to do is writing a 74164 device and add that to MAME.