src/mess/machine/ay31015.c
| r0 | r2802 | |
| 1 | /****************************************************************************
|
| 2 | |
| 3 | ay31015.c by Robbbert, May 2008
|
| 4 | |
| 5 | Code for the AY3-1015 UART
|
| 6 | |
| 7 | This is cycle-accurate according to the specifications.
|
| 8 | |
| 9 | It supports independent receive and transmit clocks,
|
| 10 | and transmission and reception can occur simultaneously if desired.
|
| 11 | |
| 12 | Note to any MESSDEVs who might happen to stray here:
|
| 13 | |
| 14 | - If you find bugs, please fix them yourself.
|
| 15 | - If you want to convert it to a DEVICE, please do so.
|
| 16 | |
| 17 | ****************************************************************************/
|
| 18 | |
| 19 | /* It is not clear in the documentation as to which settings will reset the device.
|
| 20 | To be safe, we will always reset whenever the control register changes.
|
| 21 | |
| 22 | Also, it not clear exactly what happens under various error conditions.
|
| 23 | What we do is:
|
| 24 | 1. During receive the stop bit processing is cut short, so we can better
|
| 25 | synchronise the next byte.
|
| 26 | 2. If a framing error occurs, we do not signal DAV, but instead skip
|
| 27 | the bad byte and start looking for the next.
|
| 28 | |
| 29 | This device has only been tested successfully at 300 baud, with 8 bits,
|
| 30 | 2 stop bits and no parity.
|
| 31 | |
| 32 | The bit order of the status and control registers is set up to suit the
|
| 33 | Exidy driver. If you wish to use it for some other driver, use the BITSWAP8
|
| 34 | macro in the driver.
|
| 35 | |
| 36 | Device Data:
|
| 37 | |
| 38 | * Common Controls:
|
| 39 | -- Pin 1 - Vcc - 5 volts
|
| 40 | -- Pin 2 - not used
|
| 41 | -- Pin 3 - Gnd - 0 volts
|
| 42 | -- Pin 21 - XR - External Reset - resets all registers to initial state except for the control register
|
| 43 | -- Pin 35 - NP - No Parity - "1" will kill any parity processing
|
| 44 | -- Pin 36 - TSB - Number of Stop Bits - "0" = 1 stop bit; "1" = 2 stop bits. If "1", and 5 bits per character, then we have 1.5 stop bits
|
| 45 | -- pin 37 - NB1
|
| 46 | -- pin 38 - NB2 - Number of bits per character = NB1 + (NB2 * 2) + 5
|
| 47 | -- pin 39 - EPS - Odd or Even Parity Select - "0" = Odd parity; "1" = Even parity. Has no effect if NP is high.
|
| 48 | -- Pin 34 - CS - Control Strobe - Read NP, TSB, EPS, NB1, NB2 into the control register.
|
| 49 | |
| 50 | Format of data stream:
|
| 51 | Start bit (low), Bit 0, Bit 1... highest bit, Parity bit (if enabled), 1-2 stop bits (high)
|
| 52 | |
| 53 | |
| 54 | * Receiver Controls:
|
| 55 | -- Pin 17 - RCP - Clock which is 16x the desired baud rate
|
| 56 | -- Pin 20 - SI - Serial input stream - "1" = Mark (waiting for input), "0" = Space (Start bit) initiates the transfer of a byte
|
| 57 | -- Pin 4 - RDE - "0" causes the received data to appear on RD1 to RD8.
|
| 58 | -- Pins 5 to 12 - RD8 to RD1 - These are the data lines (bits 7 to 0). Data is right-justified.
|
| 59 | -- Pin 16 - SWE - Status word enable - causes the status bits (PE, FE, OR, DAV, TBMT) to appear at the pins.
|
| 60 | -- Pin 19 - DAV - "1" indicates that a byte has been received by the UART, and should now be accepted by the computer
|
| 61 | -- Pin 18 - RDAV - "0" will force DAV low.
|
| 62 | -- Pin 13 - PE - Parity error - "1" indicates that a parity error occured
|
| 63 | -- Pin 14 - FE - Framing error - "1" Indicates that the stop bit was missing
|
| 64 | -- Pin 15 - OR - overrun - "1" indicates that a new character has become available before the computer had accepted the previous character
|
| 65 | |
| 66 | * Transmitter controls:
|
| 67 | -- Pin 40 - TCP - Clock which is 16x the desired baud rate
|
| 68 | -- Pin 25 - SO - Serial output stream - it will stay at "1" while no data is being transmitted
|
| 69 | -- Pins 26 to 33 - DB1 to DB8 - These are the data lines containing the byte to be sent
|
| 70 | -- Pin 23 - DS - Data Strobe - "0" will copy DB1 to DB8 into the transmit buffer
|
| 71 | -- Pin 22 - TBMT - Transmit buffer Empty - "1" indicates to the computer that another byte may be sent to the UART
|
| 72 | -- Pin 24 - EOC - End of Character - "0" means that a character is being sent.
|
| 73 | |
| 74 | ******************************************* COMMON CONTROLS ********************************************************/
|
| 75 | |
| 76 | #include "driver.h"
|
| 77 | #include "ay31015.h"
|
| 78 | |
| 79 | //typedef struct _ay31015_token ay31015_token;
|
| 80 | static struct // _ay31015_token
|
| 81 | {
|
| 82 | UINT8 control_reg;
|
| 83 | UINT8 status_reg;
|
| 84 | UINT16 second_stop_bit; // 0, 8, 16
|
| 85 | UINT16 total_pulses; // bits * 16
|
| 86 | UINT8 internal_sample;
|
| 87 | |
| 88 | UINT8 rx_state;
|
| 89 | UINT8 rx_data; // byte being received
|
| 90 | UINT8 rx_buffer; // received byte waiting to be accepted by computer
|
| 91 | UINT8 rx_bit_count;
|
| 92 | UINT8 rx_parity;
|
| 93 | UINT16 rx_pulses; // total pulses left
|
| 94 | |
| 95 | UINT8 tx_state;
|
| 96 | UINT8 tx_data; // byte being sent
|
| 97 | UINT8 tx_buffer; // next byte to send
|
| 98 | UINT8 tx_parity;
|
| 99 | UINT16 tx_pulses; // total pulses left
|
| 100 | UINT8 si;
|
| 101 | } ay;
|
| 102 | |
| 103 | //static struct _ay31015_token ay;
|
| 104 | |
| 105 | /* state - must only be altered by the timer callbacks */
|
| 106 | |
| 107 | #define IDLE 0
|
| 108 | #define START_BIT 1
|
| 109 | #define PROCESSING 2
|
| 110 | #define PARITY_BIT 3
|
| 111 | #define FIRST_STOP_BIT 4
|
| 112 | #define SECOND_STOP_BIT 5
|
| 113 | #define PREP_TIME 6
|
| 114 | |
| 115 | #if 0
|
| 116 | /*-------------------------------------------------
|
| 117 | get_token - safely gets the data
|
| 118 | -------------------------------------------------*/
|
| 119 | |
| 120 | INLINE ay31015_token *get_token(const device_config *device)
|
| 121 | {
|
| 122 | assert(device != NULL);
|
| 123 | assert(device->token != NULL);
|
| 124 | assert(device->type == AY31015);
|
| 125 | return (ay31015_token *) device->token;
|
| 126 | }
|
| 127 | |
| 128 | |
| 129 | /*-------------------------------------------------
|
| 130 | get_config - safely gets the config
|
| 131 | -------------------------------------------------*/
|
| 132 | |
| 133 | INLINE const ay31015_config *get_config(const device_config *device)
|
| 134 | {
|
| 135 | assert(device != NULL);
|
| 136 | assert(device->token != NULL);
|
| 137 | assert(device->type == ay31015);
|
| 138 | return (const ay31015_config *) device->static_config;
|
| 139 | }
|
| 140 | |
| 141 | |
| 142 | static DEVICE_START(ay31015)
|
| 143 | {
|
| 144 | ay31015_token *ay;
|
| 145 | // const ay31015_config *config = get_config(device);
|
| 146 | |
| 147 | ay = get_token(device);
|
| 148 | }
|
| 149 | #endif
|
| 150 | |
| 151 | |
| 152 | /*-------------------------------------------------
|
| 153 | ay31015_xr - the reset pin
|
| 154 | -------------------------------------------------*/
|
| 155 | static void ay31015_xr( void )
|
| 156 | {
|
| 157 | /* total pulses = 16 * data-bits */
|
| 158 | UINT8 t1;
|
| 159 | t1 = (ay.control_reg & 3) + 5; /* data bits */
|
| 160 | ay.total_pulses = t1 << 4; /* total clock pulses to load a byte */
|
| 161 | ay.second_stop_bit = ((ay.control_reg & AY31015_TSB) ? 1 : 0) << 4; /* 2nd stop bit */
|
| 162 | if ((t1 == 5) && (ay.second_stop_bit == 16))
|
| 163 | ay.second_stop_bit = 8; /* 5 data bits and 2 stop bits = 1.5 stop bits */
|
| 164 | ay.status_reg = AY31015_EOC | AY31015_TBMT | AY31015_SO;
|
| 165 | ay.rx_data = 0;
|
| 166 | ay.tx_data = 0;
|
| 167 | ay.rx_state = PREP_TIME;
|
| 168 | ay.tx_state = IDLE;
|
| 169 | ay.si = 1;
|
| 170 | }
|
| 171 | |
| 172 | |
| 173 | /*-------------------------------------------------
|
| 174 | ay31015_init - drivers should call this at
|
| 175 | machine reset time
|
| 176 | -------------------------------------------------*/
|
| 177 | void ay31015_init( void )
|
| 178 | {
|
| 179 | ay.control_reg = 0;
|
| 180 | ay31015_xr();
|
| 181 | }
|
| 182 | |
| 183 | |
| 184 | /*-------------------------------------------------
|
| 185 | ay31015_cs - The entire control register is
|
| 186 | updated at once.
|
| 187 | -------------------------------------------------*/
|
| 188 | void ay31015_cs( UINT8 data )
|
| 189 | {
|
| 190 | UINT8 t1 = ay.control_reg;
|
| 191 | ay.control_reg = data;
|
| 192 | if (ay.control_reg != t1) ay31015_xr();
|
| 193 | }
|
| 194 | |
| 195 | |
| 196 | /*************************************************** RECEIVE CONTROLS *************************************************/
|
| 197 | |
| 198 | |
| 199 | /*-------------------------------------------------
|
| 200 | ay31015_rx_process - convert serial to parallel
|
| 201 | -------------------------------------------------*/
|
| 202 | static TIMER_CALLBACK( ay31015_rx_process )
|
| 203 | {
|
| 204 | switch (ay.rx_state)
|
| 205 | {
|
| 206 | case PREP_TIME: // assist sync by ensuring high bit occurs
|
| 207 | ay.rx_pulses--;
|
| 208 | if (ay.si)
|
| 209 | ay.rx_state = IDLE;
|
| 210 | return;
|
| 211 | |
| 212 | case IDLE:
|
| 213 | ay.rx_pulses--;
|
| 214 | if (!ay.si)
|
| 215 | {
|
| 216 | ay.rx_state = START_BIT;
|
| 217 | ay.rx_pulses = 16;
|
| 218 | }
|
| 219 | return;
|
| 220 | |
| 221 | case START_BIT:
|
| 222 | ay.rx_pulses--;
|
| 223 | if ((ay.rx_pulses == 8) && (ay.si)) // start bit must be low at sample time
|
| 224 | ay.rx_state = IDLE;
|
| 225 | else
|
| 226 | if (!ay.rx_pulses) // end of start bit
|
| 227 | {
|
| 228 | ay.rx_state = PROCESSING;
|
| 229 | ay.rx_pulses = ay.total_pulses;
|
| 230 | ay.rx_bit_count = 0;
|
| 231 | ay.rx_parity = 0;
|
| 232 | ay.rx_data = 0;
|
| 233 | }
|
| 234 | return;
|
| 235 | |
| 236 | case PROCESSING:
|
| 237 | ay.rx_pulses--;
|
| 238 | if (!ay.rx_pulses) // end of a byte
|
| 239 | {
|
| 240 | ay.rx_pulses = 16;
|
| 241 | if (ay.control_reg & AY31015_NP) // see if we need to get a parity bit
|
| 242 | ay.rx_state = FIRST_STOP_BIT;
|
| 243 | else
|
| 244 | ay.rx_state = PARITY_BIT;
|
| 245 | }
|
| 246 | else
|
| 247 | if (!(ay.rx_pulses & 15)) // end of a bit
|
| 248 | ay.rx_bit_count++;
|
| 249 | else
|
| 250 | if ((ay.rx_pulses & 15) == 8) // sample input stream
|
| 251 | {
|
| 252 | ay.internal_sample = ay.si;
|
| 253 | ay.rx_parity ^= ay.internal_sample; // calculate cumulative parity
|
| 254 | ay.rx_data |= ay.internal_sample << ay.rx_bit_count;
|
| 255 | }
|
| 256 | return;
|
| 257 | |
| 258 | case PARITY_BIT:
|
| 259 | ay.rx_pulses--;
|
| 260 | |
| 261 | if (ay.rx_pulses == 8) // sample input stream
|
| 262 | ay.rx_parity ^= ay.si; // calculate cumulative parity
|
| 263 | else
|
| 264 | if (!ay.rx_pulses) // end of a byte
|
| 265 | {
|
| 266 | ay.rx_pulses = 16;
|
| 267 | ay.rx_state = FIRST_STOP_BIT;
|
| 268 | |
| 269 | if ((ay.status_reg & AY31015_EPS) && (ay.rx_parity))
|
| 270 | ay.rx_parity = 0; // odd parity, ok
|
| 271 | else
|
| 272 | if ((!(ay.status_reg & AY31015_EPS)) && (!ay.rx_parity))
|
| 273 | ay.rx_parity = 0; // even parity, ok
|
| 274 | else
|
| 275 | ay.rx_parity = 1; // parity error
|
| 276 | }
|
| 277 | return;
|
| 278 | |
| 279 | case FIRST_STOP_BIT:
|
| 280 | ay.rx_pulses--;
|
| 281 | if (ay.rx_pulses == 8) // sample input stream
|
| 282 | ay.internal_sample = ay.si;
|
| 283 | else
|
| 284 | if (ay.rx_pulses == 7) // set error flags
|
| 285 | {
|
| 286 | if (!ay.internal_sample)
|
| 287 | {
|
| 288 | ay.status_reg |= AY31015_FE; // framing error - the stop bit not high
|
| 289 | ay.rx_state = PREP_TIME; // lost sync - start over
|
| 290 | // return;
|
| 291 | }
|
| 292 | else
|
| 293 | ay.status_reg &= ~AY31015_FE;
|
| 294 | |
| 295 | if ((ay.rx_parity) && (!(ay.control_reg & AY31015_NP)))
|
| 296 | ay.status_reg |= AY31015_PE; // parity error
|
| 297 | else
|
| 298 | ay.status_reg &= ~AY31015_PE;
|
| 299 | |
| 300 | if (ay.status_reg & AY31015_DAV)
|
| 301 | ay.status_reg |= AY31015_OR; // overrun error - previous byte still in buffer
|
| 302 | else
|
| 303 | ay.status_reg &= ~AY31015_OR;
|
| 304 | |
| 305 | ay.rx_buffer = ay.rx_data; // bring received byte out for computer to read
|
| 306 | }
|
| 307 | else
|
| 308 | if (ay.rx_pulses == 6)
|
| 309 | ay.status_reg |= AY31015_DAV; // tell computer that new byte is ready
|
| 310 | else
|
| 311 | if (ay.rx_pulses == 4)
|
| 312 | |
| 313 | /* According to the specs, this commented-out code is what happens.
|
| 314 | However, it is not particularly effective at keeping sync.
|
| 315 | Therefore, since the input is HIGH, we simply jump to PREP_TIME and
|
| 316 | wait for the next start bit to begin. */
|
| 317 | |
| 318 | // if (!ay.rx_pulses) // end of first stop bit
|
| 319 | // {
|
| 320 | // if (ay.second_stop_bit)
|
| 321 | // {
|
| 322 | // ay.rx_state = SECOND_STOP_BIT;
|
| 323 | // ay.rx_pulses = ay.second_stop_bit - 2;
|
| 324 | // }
|
| 325 | // else
|
| 326 | // ay.rx_state = PREP_TIME;
|
| 327 | // }
|
| 328 | // return;
|
| 329 | //
|
| 330 | // case SECOND_STOP_BIT:
|
| 331 | // ay.rx_pulses--;
|
| 332 | // if (!ay.rx_pulses)
|
| 333 | ay.rx_state = PREP_TIME;
|
| 334 | return;
|
| 335 | }
|
| 336 | }
|
| 337 | |
| 338 | |
| 339 | /*-------------------------------------------------
|
| 340 | ay31015_set_rx_clock_speed - set receive clock
|
| 341 | -------------------------------------------------*/
|
| 342 | void ay31015_set_rx_clock_speed( UINT32 data )
|
| 343 | {
|
| 344 | // if (data)
|
| 345 | timer_pulse(ATTOTIME_IN_HZ(data),NULL,0,ay31015_rx_process);
|
| 346 | // else
|
| 347 | // turn timer off;
|
| 348 | }
|
| 349 | |
| 350 | /*-------------------------------------------------
|
| 351 | ay31015_swe - The entire status register is
|
| 352 | returned at once.
|
| 353 | -------------------------------------------------*/
|
| 354 | UINT8 ay31015_swe( void )
|
| 355 | {
|
| 356 | return ay.status_reg & 0x3f;
|
| 357 | }
|
| 358 | |
| 359 | |
| 360 | /*-------------------------------------------------
|
| 361 | ay31015_rde - return a byte to the computer
|
| 362 | -------------------------------------------------*/
|
| 363 | UINT8 ay31015_rde( void )
|
| 364 | {
|
| 365 | return ay.rx_buffer;
|
| 366 | }
|
| 367 | |
| 368 | |
| 369 | /*-------------------------------------------------
|
| 370 | ay31015_rdav - tell uart computer has retrieved
|
| 371 | the above byte.
|
| 372 | -------------------------------------------------*/
|
| 373 | void ay31015_rdav( void )
|
| 374 | {
|
| 375 | ay.status_reg &= ~AY31015_DAV; /* set dav low */
|
| 376 | }
|
| 377 | |
| 378 | |
| 379 | /*-------------------------------------------------
|
| 380 | ay31015_si - send a serial bit to the uart
|
| 381 | -------------------------------------------------*/
|
| 382 | void ay31015_si( UINT8 data )
|
| 383 | {
|
| 384 | ay.si = (data) ? 1 : 0;
|
| 385 | }
|
| 386 | |
| 387 | |
| 388 | /*************************************************** TRANSMIT CONTROLS *************************************************/
|
| 389 | |
| 390 | |
| 391 | /*-------------------------------------------------
|
| 392 | ay31015_tx_process - convert parallel to serial
|
| 393 | -------------------------------------------------*/
|
| 394 | static TIMER_CALLBACK( ay31015_tx_process )
|
| 395 | {
|
| 396 | UINT8 t1;
|
| 397 | switch (ay.tx_state)
|
| 398 | {
|
| 399 | case IDLE:
|
| 400 | if (!(ay.status_reg & AY31015_TBMT))
|
| 401 | {
|
| 402 | ay.tx_state = PREP_TIME; // When idle, see if a byte has been sent to us
|
| 403 | ay.tx_pulses = 1;
|
| 404 | }
|
| 405 | return;
|
| 406 | |
| 407 | case PREP_TIME: // This phase lets the transmitter regain sync after an idle period
|
| 408 | ay.tx_pulses--;
|
| 409 | if (!ay.tx_pulses)
|
| 410 | {
|
| 411 | ay.tx_state = START_BIT;
|
| 412 | ay.tx_pulses = 16;
|
| 413 | }
|
| 414 | return;
|
| 415 | |
| 416 | case START_BIT:
|
| 417 | if (ay.tx_pulses == 16) // beginning of start bit
|
| 418 | {
|
| 419 | ay.tx_data = ay.tx_buffer; // load the shift register
|
| 420 | ay.status_reg |= AY31015_TBMT; // tell computer that another byte can be sent to uart
|
| 421 | ay.status_reg &= ~AY31015_SO; // start bit begins now (we are "spacing")
|
| 422 | ay.status_reg &= ~AY31015_EOC; // we are no longer idle
|
| 423 | ay.tx_parity = 0;
|
| 424 | }
|
| 425 | |
| 426 | ay.tx_pulses--;
|
| 427 | if (!ay.tx_pulses) // end of start bit
|
| 428 | {
|
| 429 | ay.tx_state = PROCESSING;
|
| 430 | ay.tx_pulses = ay.total_pulses;
|
| 431 | }
|
| 432 | return;
|
| 433 | |
| 434 | case PROCESSING:
|
| 435 | if (!(ay.tx_pulses & 15)) // beginning of a data bit
|
| 436 | {
|
| 437 | if (ay.tx_data & 1)
|
| 438 | {
|
| 439 | ay.status_reg |= AY31015_SO;
|
| 440 | ay.tx_parity++; // calculate cumulative parity
|
| 441 | }
|
| 442 | else
|
| 443 | ay.status_reg &= ~AY31015_SO;
|
| 444 | |
| 445 | ay.tx_data >>= 1; // adjust the shift register
|
| 446 | }
|
| 447 | |
| 448 | ay.tx_pulses--;
|
| 449 | if (!ay.tx_pulses) // all data bits sent
|
| 450 | {
|
| 451 | ay.tx_pulses = 16;
|
| 452 | if (ay.control_reg & AY31015_NP) // see if we need to make a parity bit
|
| 453 | ay.tx_state = FIRST_STOP_BIT;
|
| 454 | else
|
| 455 | ay.tx_state = PARITY_BIT;
|
| 456 | }
|
| 457 | |
| 458 | return;
|
| 459 | |
| 460 | case PARITY_BIT:
|
| 461 | if (ay.tx_pulses == 16)
|
| 462 | {
|
| 463 | t1 = (ay.control_reg & AY31015_EPS) ? 0 : 1;
|
| 464 | t1 ^= (ay.tx_parity & 1);
|
| 465 | if (t1)
|
| 466 | ay.status_reg |= AY31015_SO; // extra bit to set the correct parity
|
| 467 | else
|
| 468 | ay.status_reg &= ~AY31015_SO; // it was already correct
|
| 469 | }
|
| 470 | |
| 471 | ay.tx_pulses--;
|
| 472 | if (!ay.tx_pulses)
|
| 473 | {
|
| 474 | ay.tx_state = FIRST_STOP_BIT;
|
| 475 | ay.tx_pulses = 16;
|
| 476 | }
|
| 477 | return;
|
| 478 | |
| 479 | case FIRST_STOP_BIT:
|
| 480 | if (ay.tx_pulses == 16)
|
| 481 | ay.status_reg |= AY31015_SO; // create a stop bit (marking and soon idle)
|
| 482 | ay.tx_pulses--;
|
| 483 | if (!ay.tx_pulses)
|
| 484 | {
|
| 485 | ay.status_reg |= AY31015_EOC; // character is completely sent
|
| 486 | if (ay.second_stop_bit)
|
| 487 | {
|
| 488 | ay.tx_state = SECOND_STOP_BIT;
|
| 489 | ay.tx_pulses = ay.second_stop_bit;
|
| 490 | }
|
| 491 | else
|
| 492 | if (ay.status_reg & AY31015_TBMT)
|
| 493 | ay.tx_state = IDLE; // if nothing to send, go idle
|
| 494 | else
|
| 495 | {
|
| 496 | ay.tx_pulses = 16;
|
| 497 | ay.tx_state = START_BIT; // otherwise immediately start next byte
|
| 498 | }
|
| 499 | }
|
| 500 | return;
|
| 501 | |
| 502 | case SECOND_STOP_BIT:
|
| 503 | ay.tx_pulses--;
|
| 504 | if (!ay.tx_pulses)
|
| 505 | {
|
| 506 | if (ay.status_reg & AY31015_TBMT)
|
| 507 | ay.tx_state = IDLE; // if nothing to send, go idle
|
| 508 | else
|
| 509 | {
|
| 510 | ay.tx_pulses = 16;
|
| 511 | ay.tx_state = START_BIT; // otherwise immediately start next byte
|
| 512 | }
|
| 513 | }
|
| 514 | return;
|
| 515 | |
| 516 | }
|
| 517 | }
|
| 518 | |
| 519 | |
| 520 | /*-------------------------------------------------
|
| 521 | ay31015_set_tx_clock_speed - set transmit clock
|
| 522 | -------------------------------------------------*/
|
| 523 | void ay31015_set_tx_clock_speed( UINT32 data )
|
| 524 | {
|
| 525 | // if (data)
|
| 526 | timer_pulse(ATTOTIME_IN_HZ(data),NULL,0,ay31015_tx_process);
|
| 527 | // else
|
| 528 | // turn timer off;
|
| 529 | }
|
| 530 | |
| 531 | |
| 532 | /*-------------------------------------------------
|
| 533 | ay31015_so - return a serial bit to calling
|
| 534 | driver.
|
| 535 | -------------------------------------------------*/
|
| 536 | UINT8 ay31015_so( void )
|
| 537 | {
|
| 538 | return (ay.status_reg & AY31015_SO) ? 1 : 0;
|
| 539 | }
|
| 540 | |
| 541 | |
| 542 | /*-------------------------------------------------
|
| 543 | ay31015_ds - accept a byte to transmit, if able
|
| 544 | -------------------------------------------------*/
|
| 545 | void ay31015_ds( UINT8 data )
|
| 546 | {
|
| 547 | if (ay.status_reg & AY31015_TBMT)
|
| 548 | {
|
| 549 | ay.tx_buffer = data;
|
| 550 | ay.status_reg &= ~AY31015_TBMT;
|
| 551 | }
|
| 552 | }
|
| 553 | |
| 554 | |
| 555 | |
| 556 | #if 0
|
| 557 | |
| 558 | void |
| 559 | ay31015_token *ay = get_token(device);
|
| 560 | const ay31015_config *config = get_config(device);
|
| 561 | |
| 562 | |
| 563 | /*-------------------------------------------------
|
| 564 | DEVICE_GET_INFO(ay31015) - device getinfo
|
| 565 | function
|
| 566 | -------------------------------------------------*/
|
| 567 | |
| 568 | DEVICE_GET_INFO(ay31015)
|
| 569 | {
|
| 570 | switch(state)
|
| 571 | {
|
| 572 | /* --- the following bits of info are returned as 64-bit signed integers --- */
|
| 573 | case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(ay31015_token); break;
|
| 574 | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
|
| 575 | case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_PERIPHERAL; break;
|
| 576 | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_PRINTER; break;
|
| 577 | case DEVINFO_INT_IMAGE_READABLE: info->i = 0; break;
|
| 578 | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break;
|
| 579 | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break;
|
| 580 | |
| 581 | /* --- the following bits of info are returned as pointers to data or functions --- */
|
| 582 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(ay31015); break;
|
| 583 | |
| 584 | /* --- the following bits of info are returned as NULL-terminated strings --- */
|
| 585 | case DEVINFO_STR_NAME: info->s = "ay31015"; break;
|
| 586 | case DEVINFO_STR_FAMILY: info->s = "ay31015"; break;
|
| 587 | case DEVINFO_STR_SOURCE_FILE: info->s = __FILE__; break;
|
| 588 | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: info->s = "prn"; break;
|
| 589 | }
|
| 590 | }
|
| 591 | #endif
|
| 592 | |
src/mess/drivers/exidy.c
| r2801 | r2802 | |
| 8 | 8 | port fc: |
| 9 | 9 | ======== |
| 10 | 10 | input/output: |
| 11 | | hd6402 uart data |
| 11 | uart data |
| 12 | 12 | |
| 13 | 13 | port fd: |
| 14 | 14 | ======== |
| 15 | | input: hd6402 uart status |
| 15 | input: uart status |
| 16 | 16 | bit 4: parity error (RPE) |
| 17 | 17 | bit 3: framing error (RFE) |
| 18 | 18 | bit 2: over-run (RDP) |
| r2801 | r2802 | |
| 55 | 55 | |
| 56 | 56 | The cassette interface hardware converts square-wave pulses into bits which the uart receives. |
| 57 | 57 | |
| 58 | 1. the cassette format: "frequency shift" is converted |
| 59 | into the uart data format "non-return to zero" |
| 58 | 60 | |
| 61 | 2. on cassette a 1 data bit is stored as a high frequency |
| 62 | and a 0 data bit as a low frequency |
| 63 | - At 1200 baud, a logic 1 is 1 cycle of 1200hz and a logic 0 is 1/2 cycle of 600hz. |
| 64 | - At 300 baud, a logic 1 is 8 cycles of 2400hz and a logic 0 is 4 cycles of 1200hz. |
| 65 | |
| 66 | Attenuation is applied to the signal and the square wave edges are rounded. |
| 67 | |
| 68 | A manchester encoder is used. A flip-flop synchronises input |
| 69 | data on the positive-edge of the clock pulse. |
| 70 | |
| 71 | Interestingly the data on cassette is stored in xmodem-checksum. |
| 72 | |
| 73 | |
| 74 | Due to bugs in the hardware and software of a real Sorcerer, the serial |
| 75 | interface misbehaves. |
| 76 | 1. Sorcerer I had a hardware problem causing rs232 idle to be a space (+9v) |
| 77 | instead of mark (-9v). Fixed in Sorcerer II. |
| 78 | 2. When you select a different baud for rs232, it was "remembered" but not |
| 79 | sent to port fe. It only gets sent when motor on was requested. Motor on is |
| 80 | only meaningful in a cassette operation. |
| 81 | 3. The monitor software always resets the device to cassette whenever the |
| 82 | keyboard is scanned, motors altered, or an error occurred. |
| 83 | 4. The above problems make rs232 communication impractical unless you write |
| 84 | your own routines or create a corrected monitor rom. |
| 85 | |
| 59 | 86 | Sound: |
| 60 | 87 | |
| 61 | 88 | external speaker connected to the parallel port |
| r2801 | r2802 | |
| 66 | 93 | |
| 67 | 94 | ****************************************************************************** |
| 68 | 95 | |
| 69 | | The Sorcerer comes with a non-standard Serial Port with connections for |
| 70 | | rs232 and the 2 cassette players. The connections for cassette 1 are duplicated |
| 71 | | on a set of phono plugs. |
| 72 | | |
| 73 | 96 | The CPU clock speed is 2.106mhz, which was increased to 4.0mhz on the last production runs. |
| 74 | 97 | |
| 75 | 98 | The Sorcerer has a bus connection for S100 equipment. This allows the connection |
| r2801 | r2802 | |
| 79 | 102 | |
| 80 | 103 | The driver "exidyd" emulates the most common form, a cassette-based system with 48k of ram. |
| 81 | 104 | |
| 105 | ******************************************************************************* |
| 82 | 106 | |
| 107 | |
| 108 | Emulation status: |
| 109 | |
| 110 | exidy: 32k disk-based system. Disks not tested. Optional S-100 interface not implemented. |
| 111 | exidyd: 48k cassette-based system. Disks and S-100 are not options on this system. |
| 112 | |
| 113 | Real machines had optional RAM sizes of 4k, 16k, 32k, 48k (officially). |
| 114 | Unofficially, the cart could hold another 8k of static RAM (4x 6116), giving 56k total. |
| 115 | |
| 116 | On the back of the machine is a 50-pin expansion port, which could be hooked to the |
| 117 | expansion unit. |
| 118 | |
| 119 | Also is a 25-pin parallel port, allowing inwards and outwards communication. |
| 120 | It was often hooked to a printer, a joystick, a music card, or a speaker. |
| 121 | |
| 122 | We emulate the printer and the speaker. |
| 123 | |
| 124 | Another 25-pin port provided two-way serial communications. Only two speeds are |
| 125 | available - 300 baud and 1200 baud. There is no handshaking. This protocol is |
| 126 | currently not emulated. |
| 127 | |
| 128 | Other pins on this connector provided for two cassette players. The connections |
| 129 | for cassette unit 1 are duplicated on a set of phono plugs. |
| 130 | |
| 131 | We emulate the use of two cassette units. An option allows you to hear the sound |
| 132 | of the tape during playback. |
| 133 | |
| 134 | The sorcerer has a UART device used by the serial interface and the cassette system. |
| 135 | So far, it has been tested to work at 300 baud. |
| 136 | |
| 83 | 137 | ********************************************************************************/ |
| 84 | 138 | #include "driver.h" |
| 85 | 139 | #include "includes/exidy.h" |
| 86 | 140 | #include "machine/centroni.h" |
| 87 | | #include "machine/hd6402.h" |
| 88 | 141 | #include "cpu/z80/z80.h" |
| 89 | 142 | #include "machine/wd17xx.h" |
| 90 | 143 | #include "devices/basicdsk.h" |
| r2801 | r2802 | |
| 94 | 147 | #include "devices/printer.h" |
| 95 | 148 | #include "devices/z80bin.h" |
| 96 | 149 | #include "sound/speaker.h" |
| 150 | #include "machine/ay31015.h" |
| 97 | 151 | |
| 98 | 152 | |
| 99 | 153 | static Z80BIN_EXECUTE( exidy ); |
| r2801 | r2802 | |
| 111 | 165 | } |
| 112 | 166 | |
| 113 | 167 | |
| 114 | | static unsigned char exidy_fe; |
| 115 | | static int exidy_keyboard_line; |
| 116 | | static unsigned long exidy_hd6402_state; |
| 168 | static UINT8 exidy_fe = 0xff; |
| 169 | static UINT8 exidy_keyboard_line; |
| 117 | 170 | |
| 118 | 171 | static WRITE8_HANDLER(exidy_fe_port_w); |
| 119 | 172 | |
| 173 | #if 0 |
| 174 | |
| 175 | The serial code (which was never connected to the outside) is disabled for now. |
| 176 | |
| 120 | 177 | /* timer for exidy serial chip transmit and receive */ |
| 121 | | static emu_timer *serial_timer; |
| 178 | //static emu_timer *serial_timer; |
| 122 | 179 | |
| 123 | | static TIMER_CALLBACK(exidy_serial_timer_callback) |
| 124 | | { |
| 180 | //static TIMER_CALLBACK(exidy_serial_timer_callback) |
| 181 | //{ |
| 125 | 182 | /* if rs232 is enabled, uart is connected to clock defined by bit6 of port fe. |
| 126 | 183 | Transmit and receive clocks are connected to the same clock */ |
| 127 | 184 | |
| 128 | 185 | /* if rs232 is disabled, receive clock is linked to cassette hardware */ |
| 129 | | if (exidy_fe & 0x080) |
| 130 | | { |
| 131 | | /* trigger transmit clock on hd6402 */ |
| 132 | | hd6402_transmit_clock(); |
| 133 | | /* trigger receive clock on hd6402 */ |
| 134 | | hd6402_receive_clock(); |
| 135 | | } |
| 136 | | } |
| 186 | // if (exidy_fe & 0x80) |
| 187 | // { |
| 188 | connect to rs232 |
| 189 | // } |
| 190 | //} |
| 191 | #endif |
| 137 | 192 | |
| 138 | 193 | |
| 139 | | |
| 140 | 194 | /* timer to read cassette waveforms */ |
| 141 | | static void *cassette_timer; |
| 195 | static emu_timer *cassette_timer; |
| 142 | 196 | |
| 143 | | /* cassette data is converted into bits which are clocked into serial chip */ |
| 144 | | static struct serial_connection cassette_serial_connection; |
| 145 | 197 | |
| 146 | | /* two flip-flops used to store state of bit coming from cassette */ |
| 147 | | static int cassette_input_ff[2]; |
| 148 | | /* state of clock, used to clock in bits */ |
| 149 | | static int cassette_clock_state; |
| 150 | | /* a up-counter. when this reaches a fixed value, the cassette clock state is changed */ |
| 151 | | static int cassette_clock_counter; |
| 198 | static const device_config *cassette_device_image(void) |
| 199 | { |
| 200 | if (exidy_fe & 0x20) |
| 201 | return image_from_devtype_and_index(IO_CASSETTE, 1); |
| 202 | else |
| 203 | return image_from_devtype_and_index(IO_CASSETTE, 0); |
| 204 | } |
| 152 | 205 | |
| 153 | | /* 1. the cassette format: "frequency shift" is converted |
| 154 | | into the uart data format "non-return to zero" |
| 206 | static UINT8 cass_data[]={ 0, 0, 0, 0, 0, 0, 0 }; |
| 155 | 207 | |
| 156 | | 2. on cassette a 1 data bit is stored as a high frequency |
| 157 | | and a 0 data bit as a low frequency |
| 158 | | - At 1200 baud, a logic 1 is 1 cycle of 1200hz and a logic 0 is 1/2 cycle of 600hz. |
| 159 | | - At 300 baud, a logic 1 is 8 cycles of 2400hz and a logic 0 is 4 cycles of 1200hz. |
| 208 | static TIMER_CALLBACK(exidy_cassette_tc) |
| 209 | { |
| 210 | UINT8 cass_ws = 0; |
| 211 | switch (exidy_fe & 0xc0) // bit 7 low indicates cassette |
| 212 | { |
| 213 | case 0x00: // cassette 300 baud - works |
| 160 | 214 | |
| 161 | | Attenuation is applied to the signal and the square wave edges are rounded. |
| 215 | /* loading a tape - this is basically the same as the super80. |
| 216 | We convert the 1200/2400 Hz signal to a 0 or 1, and send it to the uart. */ |
| 162 | 217 | |
| 163 | | A manchester encoder is used. A flip-flop synchronises input |
| 164 | | data on the positive-edge of the clock pulse. |
| 218 | cass_data[1]++; |
| 165 | 219 | |
| 166 | | Interestingly the data on cassette is stored in xmodem-checksum. |
| 220 | cass_ws = (cassette_input(cassette_device_image()) > +0.02) ? 1 : 0; |
| 167 | 221 | |
| 222 | if (cass_ws != cass_data[0]) |
| 223 | { |
| 224 | cass_data[0] = cass_ws; |
| 225 | cass_data[2] = ((cass_data[1] < 0x6) || (cass_data[1] > 0x20)) ? 1 : 0; |
| 226 | cass_data[1] = 0; |
| 227 | if (cass_data[2] != cass_data[3]) |
| 228 | { |
| 229 | ay31015_si(cass_data[2]); |
| 230 | cass_data[3] = cass_data[2]; |
| 231 | } |
| 232 | } |
| 168 | 233 | |
| 169 | | */ |
| 234 | /* saving a tape - convert the serial stream from the uart, into 1200 and 2400 Hz frequencies. |
| 235 | Synchronisation of the frequency pulses to the uart is extremely important. */ |
| 170 | 236 | |
| 171 | | /* only works for one cassette so far */ |
| 172 | | static TIMER_CALLBACK(exidy_cassette_timer_callback) |
| 173 | | { |
| 174 | | cassette_clock_counter++; |
| 237 | cass_data[5]++; |
| 238 | if (!(cass_data[5] & 0x1f)) |
| 239 | { |
| 240 | cass_ws = ay31015_so(); |
| 241 | if (cass_ws != cass_data[4]) |
| 242 | { |
| 243 | cass_data[4] = cass_ws; |
| 244 | cass_data[5]=0; |
| 245 | } |
| 246 | } |
| 175 | 247 | |
| 176 | | if (cassette_clock_counter==(4800/1200)) |
| 177 | | { |
| 178 | | /* reset counter */ |
| 179 | | cassette_clock_counter = 0; |
| 248 | if (!(cass_data[5] & 3)) |
| 249 | { |
| 250 | if (!((cass_data[4] == 0) && (cass_data[5] & 4))) |
| 251 | { |
| 252 | cass_data[6] ^= 1; // toggle output state, except on 2nd half of low bit |
| 253 | cassette_output(cassette_device_image(), cass_data[6] ? -1.0 : +1.0); |
| 254 | } |
| 255 | } |
| 256 | return; |
| 180 | 257 | |
| 181 | | /* toggle state of clock */ |
| 182 | | cassette_clock_state^=0x0ffffffff; |
| 258 | case 0x40: // cassette 1200 baud - not working at this time |
| 259 | /* loading a tape */ |
| 260 | cass_data[1]++; |
| 183 | 261 | |
| 184 | | /* previously was 0, now gone 1 */ |
| 185 | | /* +ve edge detected */ |
| 186 | | if (cassette_clock_state) |
| 187 | | { |
| 188 | | int bit; |
| 262 | cass_ws = (cassette_input(cassette_device_image()) > +0.02) ? 1 : 0; |
| 189 | 263 | |
| 190 | | /* clock bits into cassette flip flops */ |
| 191 | | /* bit is inverted! */ |
| 264 | if (cass_ws != cass_data[0]) |
| 265 | { |
| 266 | cass_data[0] = cass_ws; |
| 267 | cass_data[2] = ((cass_data[1] < 0xc) || (cass_data[1] > 0x20)) ? 1 : 0; |
| 268 | cass_data[1] = 0; |
| 269 | if (cass_data[2] != cass_data[3]) |
| 270 | { |
| 271 | ay31015_si(cass_data[2]); |
| 272 | cass_data[3] = cass_data[2]; |
| 273 | } |
| 274 | cass_data[2] = 0; |
| 275 | } |
| 192 | 276 | |
| 193 | | /* detect level */ |
| 194 | | bit = 1; |
| 195 | | if (cassette_input(image_from_devtype_and_index(IO_CASSETTE, 0)) > 0.0038) |
| 196 | | bit = 0; |
| 197 | | cassette_input_ff[0] = bit; |
| 198 | | /* detect level */ |
| 199 | | bit = 1; |
| 200 | | if (cassette_input(image_from_devtype_and_index(IO_CASSETTE, 1)) > 0.0038) |
| 201 | | bit = 0; |
| 277 | /* saving a tape - convert the serial stream from the uart, into 600 and 1200 Hz frequencies. */ |
| 202 | 278 | |
| 203 | | cassette_input_ff[1] = bit; |
| 279 | cass_data[5]++; |
| 280 | if (!(cass_data[5] & 0x07)) |
| 281 | { |
| 282 | cass_ws = ay31015_so(); |
| 283 | if (cass_ws != cass_data[4]) |
| 284 | { |
| 285 | cass_data[4] = cass_ws; |
| 286 | cass_data[5]=0; |
| 287 | } |
| 288 | } |
| 204 | 289 | |
| 205 | | logerror("cassette bit: %0d\n",bit); |
| 206 | | |
| 207 | | /* set out data bit */ |
| 208 | | set_out_data_bit(cassette_serial_connection.State, cassette_input_ff[0]); |
| 209 | | /* output through serial connection */ |
| 210 | | serial_connection_out(&cassette_serial_connection); |
| 211 | | |
| 212 | | /* update hd6402 receive clock */ |
| 213 | | hd6402_receive_clock(); |
| 214 | | } |
| 290 | if (!(cass_data[5] & 7)) |
| 291 | { |
| 292 | if (!((cass_data[4] == 0) && (cass_data[5] & 8))) |
| 293 | { |
| 294 | cass_data[6] ^= 1; // toggle output state, except on 2nd half of low bit |
| 295 | cassette_output(cassette_device_image(), cass_data[6] ? -1.0 : +1.0); |
| 296 | } |
| 297 | } |
| 298 | return; |
| 215 | 299 | } |
| 216 | 300 | } |
| 217 | 301 | |
| 218 | | static void exidy_hd6402_callback(int mask, int data) |
| 219 | | { |
| 220 | | exidy_hd6402_state &=~mask; |
| 221 | | exidy_hd6402_state |= (data & mask); |
| 222 | | |
| 223 | | logerror("hd6402 state: %04x %04x\n",mask,data); |
| 224 | | } |
| 225 | | |
| 226 | | /* after the first 4 bytes have been read from ROM, switch the ram back in */ |
| 227 | | static TIMER_CALLBACK( exidy_reset ) |
| 228 | | { |
| 229 | | memory_set_bank(1, 0); |
| 230 | | } |
| 231 | | |
| 232 | 302 | static void exidy_printer_handshake_in(int number, int data, int mask) |
| 233 | 303 | { |
| 234 | 304 | if (mask & CENTRONICS_ACKNOWLEDGE) |
| r2801 | r2802 | |
| 246 | 316 | }, |
| 247 | 317 | }; |
| 248 | 318 | |
| 249 | | static void cassette_serial_in(int id, unsigned long state) |
| 319 | |
| 320 | /* after the first 4 bytes have been read from ROM, switch the ram back in */ |
| 321 | static TIMER_CALLBACK( exidy_reset ) |
| 250 | 322 | { |
| 251 | | cassette_serial_connection.input_state = state; |
| 323 | memory_set_bank(1, 0); |
| 252 | 324 | } |
| 253 | 325 | |
| 254 | 326 | static MACHINE_START( exidyd ) |
| 255 | 327 | { |
| 256 | | serial_timer = timer_alloc(exidy_serial_timer_callback, NULL); |
| 257 | | cassette_timer = timer_alloc(exidy_cassette_timer_callback, NULL); |
| 328 | // serial_timer = timer_alloc(exidy_serial_timer_callback, NULL); |
| 329 | cassette_timer = timer_alloc(exidy_cassette_tc, NULL); |
| 258 | 330 | } |
| 259 | 331 | |
| 260 | 332 | static MACHINE_START( exidy ) |
| r2801 | r2802 | |
| 265 | 337 | |
| 266 | 338 | static MACHINE_RESET( exidyd ) |
| 267 | 339 | { |
| 268 | | hd6402_init(); |
| 269 | | hd6402_set_callback(exidy_hd6402_callback); |
| 270 | | hd6402_reset(); |
| 340 | ay31015_init(); |
| 271 | 341 | |
| 272 | 342 | centronics_config(0, exidy_cent_config); |
| 273 | 343 | /* assumption: select is tied low */ |
| 274 | 344 | centronics_write_handshake(0, CENTRONICS_SELECT | CENTRONICS_NO_RESET, CENTRONICS_SELECT| CENTRONICS_NO_RESET); |
| 275 | | |
| 276 | | serial_connection_init(&cassette_serial_connection); |
| 277 | | serial_connection_set_in_callback(&cassette_serial_connection, cassette_serial_in); |
| 278 | | |
| 279 | 345 | exidy_fe_port_w(machine, 0, 0); |
| 280 | 346 | |
| 281 | 347 | timer_set(ATTOTIME_IN_USEC(10), NULL, 0, exidy_reset); |
| r2801 | r2802 | |
| 330 | 396 | |
| 331 | 397 | static WRITE8_HANDLER(exidy_fc_port_w) |
| 332 | 398 | { |
| 333 | | logerror("exidy fc w: %04x %02x\n",offset,data); |
| 334 | | |
| 335 | | hd6402_set_input(HD6402_INPUT_TBRL, HD6402_INPUT_TBRL); |
| 336 | | hd6402_data_w(machine, offset, data); |
| 399 | ay31015_ds( data ); |
| 337 | 400 | } |
| 338 | 401 | |
| 339 | 402 | |
| 340 | 403 | static WRITE8_HANDLER(exidy_fd_port_w) |
| 341 | 404 | { |
| 342 | | logerror("exidy fd w: %04x %02x\n",offset,data); |
| 343 | | |
| 344 | | /* bit 0,1: char length select */ |
| 345 | | if (data & 1) |
| 346 | | hd6402_set_input(HD6402_INPUT_CLS1, HD6402_INPUT_CLS1); |
| 347 | | else |
| 348 | | hd6402_set_input(HD6402_INPUT_CLS1, 0); |
| 349 | | |
| 350 | | if (data & 2) |
| 351 | | hd6402_set_input(HD6402_INPUT_CLS2, HD6402_INPUT_CLS2); |
| 352 | | else |
| 353 | | hd6402_set_input(HD6402_INPUT_CLS2, 0); |
| 354 | | |
| 355 | | /* bit 2: stop bit count */ |
| 356 | | if (data & 4) |
| 357 | | hd6402_set_input(HD6402_INPUT_SBS, HD6402_INPUT_SBS); |
| 358 | | else |
| 359 | | hd6402_set_input(HD6402_INPUT_SBS, 0); |
| 360 | | |
| 361 | | /* bit 3: parity type select */ |
| 362 | | if (data & 8) |
| 363 | | hd6402_set_input(HD6402_INPUT_EPE, HD6402_INPUT_EPE); |
| 364 | | else |
| 365 | | hd6402_set_input(HD6402_INPUT_EPE, 0); |
| 366 | | |
| 367 | | /* bit 4: inhibit parity (no parity) */ |
| 368 | | if (data & 16) |
| 369 | | hd6402_set_input(HD6402_INPUT_PI, HD6402_INPUT_PI); |
| 370 | | else |
| 371 | | hd6402_set_input(HD6402_INPUT_PI, 0); |
| 405 | ay31015_cs( data ); |
| 372 | 406 | } |
| 373 | 407 | |
| 374 | 408 | #define EXIDY_CASSETTE_MOTOR_MASK ((1<<4)|(1<<5)) |
| r2801 | r2802 | |
| 376 | 410 | |
| 377 | 411 | static WRITE8_HANDLER(exidy_fe_port_w) |
| 378 | 412 | { |
| 379 | | int changed_bits; |
| 413 | UINT8 changed_bits = (exidy_fe ^ data) & 0xf0; |
| 414 | exidy_fe = data; |
| 415 | |
| 380 | 416 | /* bits 0..3 */ |
| 381 | 417 | exidy_keyboard_line = data & 0x0f; |
| 382 | 418 | |
| 383 | | changed_bits = exidy_fe^data; |
| 419 | if (!changed_bits) return; |
| 420 | |
| 384 | 421 | /* bits 4..5 */ |
| 385 | | /* either cassette motor state changed */ |
| 386 | | if ((changed_bits & EXIDY_CASSETTE_MOTOR_MASK)!=0) |
| 422 | /* does user want to hear the sound? */ |
| 423 | if ((input_port_read(machine, "CONFIG") & 8) && (data & EXIDY_CASSETTE_MOTOR_MASK)) |
| 387 | 424 | { |
| 388 | | /* cassette 1 motor */ |
| 389 | | cassette_change_state(image_from_devtype_and_index(IO_CASSETTE, 0), |
| 390 | | (data & 0x10) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, |
| 391 | | CASSETTE_MASK_MOTOR); |
| 392 | | |
| 393 | | /* cassette 2 motor */ |
| 394 | | cassette_change_state(image_from_devtype_and_index(IO_CASSETTE, 1), |
| 395 | | (data & 0x20) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, |
| 396 | | CASSETTE_MASK_MOTOR); |
| 397 | | |
| 398 | | if ((data & EXIDY_CASSETTE_MOTOR_MASK)==0) |
| 425 | if (data & 0x20) |
| 399 | 426 | { |
| 400 | | /* both are now off */ |
| 401 | | |
| 402 | | /* stop timer */ |
| 403 | | timer_adjust_oneshot(cassette_timer, attotime_zero, 0); |
| 427 | cassette_change_state(image_from_devtype_and_index(IO_CASSETTE, 0), CASSETTE_SPEAKER_MUTED, CASSETTE_MASK_SPEAKER); |
| 428 | cassette_change_state(image_from_devtype_and_index(IO_CASSETTE, 1), CASSETTE_SPEAKER_ENABLED, CASSETTE_MASK_SPEAKER); |
| 404 | 429 | } |
| 405 | 430 | else |
| 406 | 431 | { |
| 407 | | /* if both motors were off previously, at least one motor has been switched on */ |
| 408 | | if ((exidy_fe & EXIDY_CASSETTE_MOTOR_MASK)==0) |
| 409 | | { |
| 410 | | cassette_clock_counter = 0; |
| 411 | | cassette_clock_state = 0; |
| 412 | | /* start timer */ |
| 413 | | /* the correct baud rate should be being used here (see bit 6 below) */ |
| 414 | | timer_adjust_periodic(cassette_timer, attotime_zero, 0, ATTOTIME_IN_HZ(4800)); |
| 415 | | } |
| 432 | cassette_change_state(image_from_devtype_and_index(IO_CASSETTE, 1), CASSETTE_SPEAKER_MUTED, CASSETTE_MASK_SPEAKER); |
| 433 | cassette_change_state(image_from_devtype_and_index(IO_CASSETTE, 0), CASSETTE_SPEAKER_ENABLED, CASSETTE_MASK_SPEAKER); |
| 416 | 434 | } |
| 417 | 435 | } |
| 418 | | /* bit 7 */ |
| 419 | | if (data & 0x080) |
| 436 | else |
| 420 | 437 | { |
| 421 | | /* connect to serial device (not yet emulated) */ |
| 422 | | /* Due to bugs in the hardware and software of a real Sorcerer, the serial |
| 423 | | interface misbehaves. |
| 424 | | 1. Sorcerer I had a hardware problem causing rs232 idle to be a space (+9v) |
| 425 | | instead of mark (-9v). Fixed in Sorcerer II. |
| 426 | | 2. When you select a different baud for rs232, it was "remembered" but not |
| 427 | | sent to port fe. It only gets sent when motor on was requested. Motor on is |
| 428 | | only meaningful in a cassette operation. |
| 429 | | 3. The monitor software always resets the device to cassette whenever the |
| 430 | | keyboard is scanned, motors altered, or an error occurred. |
| 431 | | 4. The above problems make rs232 communication impractical unless you write |
| 432 | | your own routines or create a corrected monitor rom. */ |
| 438 | cassette_change_state(image_from_devtype_and_index(IO_CASSETTE, 1), CASSETTE_SPEAKER_MUTED, CASSETTE_MASK_SPEAKER); |
| 439 | cassette_change_state(image_from_devtype_and_index(IO_CASSETTE, 0), CASSETTE_SPEAKER_MUTED, CASSETTE_MASK_SPEAKER); |
| 433 | 440 | } |
| 441 | |
| 442 | /* cassette 1 motor */ |
| 443 | cassette_change_state(image_from_devtype_and_index(IO_CASSETTE, 0), |
| 444 | (data & 0x10) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR); |
| 445 | |
| 446 | /* cassette 2 motor */ |
| 447 | cassette_change_state(image_from_devtype_and_index(IO_CASSETTE, 1), |
| 448 | (data & 0x20) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR); |
| 449 | |
| 450 | if ((data & EXIDY_CASSETTE_MOTOR_MASK) && (~data & 0x80)) |
| 451 | timer_adjust_periodic(cassette_timer, attotime_zero, 0, ATTOTIME_IN_HZ(19200)); |
| 434 | 452 | else |
| 453 | timer_adjust_oneshot(cassette_timer, attotime_zero, 0); |
| 454 | |
| 455 | /* bit 6 */ |
| 456 | if (changed_bits & 0x40) |
| 435 | 457 | { |
| 436 | | /* connect to tape */ |
| 437 | | hd6402_connect(&cassette_serial_connection); |
| 458 | ay31015_set_rx_clock_speed ((data & 0x40) ? 19200 : 4800); |
| 459 | ay31015_set_tx_clock_speed ((data & 0x40) ? 14400 : 4800); |
| 438 | 460 | } |
| 439 | | /* bit 6 */ |
| 440 | | if ((changed_bits & (1<<6))!=0) |
| 461 | |
| 462 | /* bit 7 */ |
| 463 | if (changed_bits & 0x80) |
| 441 | 464 | { |
| 442 | | int baud_rate; |
| 443 | | |
| 444 | | baud_rate = 300; |
| 445 | | if (data & (1<<6)) |
| 465 | if (data & 0x80) |
| 446 | 466 | { |
| 447 | | baud_rate = 1200; |
| 467 | /* connect to serial device (not yet emulated) */ |
| 468 | // timer_adjust_periodic(serial_timer, attotime_zero, 0, ATTOTIME_IN_HZ(baud_rate)); |
| 448 | 469 | } |
| 449 | | |
| 450 | | timer_adjust_periodic(serial_timer, attotime_zero, 0, ATTOTIME_IN_HZ(baud_rate)); |
| 470 | else |
| 471 | { |
| 472 | /* connect to tape */ |
| 473 | // hd6402_connect(&cassette_serial_connection); |
| 474 | } |
| 451 | 475 | } |
| 452 | | |
| 453 | | exidy_fe = data; |
| 454 | 476 | } |
| 455 | 477 | |
| 456 | 478 | static WRITE8_HANDLER(exidy_ff_port_w) |
| r2801 | r2802 | |
| 473 | 495 | |
| 474 | 496 | static READ8_HANDLER(exidy_fc_port_r) |
| 475 | 497 | { |
| 476 | | UINT8 data; |
| 477 | | |
| 478 | | hd6402_set_input(HD6402_INPUT_DRR, HD6402_INPUT_DRR); |
| 479 | | data = hd6402_data_r(machine, offset); |
| 480 | | |
| 481 | | logerror("exidy fc r: %04x %02x\n",offset,data); |
| 482 | | |
| 498 | UINT8 data = ay31015_rde(); |
| 499 | ay31015_rdav(); |
| 483 | 500 | return data; |
| 484 | 501 | } |
| 485 | 502 | |
| 486 | 503 | static READ8_HANDLER(exidy_fd_port_r) |
| 487 | 504 | { |
| 488 | 505 | /* set unused bits high */ |
| 489 | | UINT8 data = 0xe0; |
| 490 | | |
| 491 | | /* bit 4: parity error */ |
| 492 | | if (exidy_hd6402_state & HD6402_OUTPUT_PE) data |= 16; |
| 493 | | |
| 494 | | /* bit 3: framing error */ |
| 495 | | if (exidy_hd6402_state & HD6402_OUTPUT_FE) data |= 8; |
| 496 | | |
| 497 | | /* bit 2: over-run error */ |
| 498 | | if (exidy_hd6402_state & HD6402_OUTPUT_OE) data |= 4; |
| 499 | | |
| 500 | | /* bit 1: data receive ready - data ready in receive reg */ |
| 501 | | if (exidy_hd6402_state & HD6402_OUTPUT_DR) data |= 2; |
| 502 | | |
| 503 | | /* bit 0: transmitter buffer receive empty */ |
| 504 | | /* if this BIT is forced high, then the cassette save routine does not hang */ |
| 505 | | if (exidy_hd6402_state & HD6402_OUTPUT_TBRE) data |= 1; |
| 506 | | |
| 507 | | logerror("exidy fd r: %04x %02x\n",offset,data); |
| 508 | | |
| 509 | | return data; |
| 506 | return ay31015_swe() | 0xe0; |
| 510 | 507 | } |
| 511 | 508 | |
| 512 | | static READ8_HANDLER(exidy_fe_port_r) |
| 509 | static READ8_HANDLER(exidy_fe_port_r) |
| 513 | 510 | { |
| 514 | 511 | /* bits 6..7 |
| 515 | 512 | - hardware handshakes from user port |
| r2801 | r2802 | |
| 573 | 570 | static ADDRESS_MAP_START( exidyd_mem , ADDRESS_SPACE_PROGRAM, 8) |
| 574 | 571 | ADDRESS_MAP_UNMAP_HIGH |
| 575 | 572 | AM_RANGE(0x0000, 0x07ff) AM_RAMBANK(1) |
| 576 | | AM_RANGE(0x0800, 0xbfff) AM_RAM /* ram 48k diskless machine */ |
| 573 | AM_RANGE(0x0800, 0xbfff) AM_RAM /* ram 48k cassette-based machine */ |
| 577 | 574 | AM_RANGE(0xc000, 0xefff) AM_ROM /* rom pac and bios */ |
| 578 | 575 | AM_RANGE(0xf000, 0xf7ff) AM_RAM /* screen ram */ |
| 579 | 576 | AM_RANGE(0xf800, 0xfbff) AM_ROM /* char rom */ |
| r2801 | r2802 | |
| 716 | 713 | PORT_CONFNAME( 0x02, 0x00, "Parallel port" ) |
| 717 | 714 | PORT_CONFSETTING( 0x00, "Speaker" ) |
| 718 | 715 | PORT_CONFSETTING( 0x02, "Printer" ) |
| 719 | | // PORT_CONFNAME( 0x08, 0x08, "Cassette Speaker") |
| 720 | | // PORT_CONFSETTING( 0x08, DEF_STR(On)) |
| 721 | | // PORT_CONFSETTING( 0x00, DEF_STR(Off)) |
| 716 | PORT_CONFNAME( 0x08, 0x08, "Cassette Speaker") |
| 717 | PORT_CONFSETTING( 0x08, DEF_STR(On)) |
| 718 | PORT_CONFSETTING( 0x00, DEF_STR(Off)) |
| 722 | 719 | INPUT_PORTS_END |
| 723 | 720 | |
| 724 | 721 | /**********************************************************************************************************/ |
| r2801 | r2802 | |
| 746 | 743 | |
| 747 | 744 | /* sound hardware */ |
| 748 | 745 | MDRV_SPEAKER_STANDARD_MONO("mono") |
| 746 | MDRV_SOUND_ADD(WAVE, 0) |
| 747 | MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) // cass1 speaker |
| 748 | MDRV_SOUND_ADD(WAVE, 1) |
| 749 | MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) // cass2 speaker |
| 749 | 750 | MDRV_SOUND_ADD(SPEAKER, 0) |
| 750 | | MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) |
| 751 | MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) // speaker on parallel port |
| 751 | 752 | |
| 752 | 753 | /* printer */ |
| 753 | 754 | MDRV_DEVICE_ADD("printer", PRINTER) |
| r2801 | r2802 | |
| 770 | 771 | { |
| 771 | 772 | UINT8 *RAM = memory_region(REGION_CPU1); |
| 772 | 773 | memory_configure_bank(1, 0, 2, &RAM[0x0000], 0xe000); |
| 773 | | // timer_pulse(ATTOTIME_IN_HZ(200000),NULL,0,exidy_timer); /* timer for cassette */ |
| 774 | 774 | } |
| 775 | 775 | |
| 776 | 776 | /*************************************************************************** |
| r2801 | r2802 | |
| 851 | 851 | switch(state) |
| 852 | 852 | { |
| 853 | 853 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 854 | | case MESS_DEVINFO_INT_COUNT: info->i = 4; break; |
| 854 | case MESS_DEVINFO_INT_COUNT: info->i = 4; break; |
| 855 | 855 | |
| 856 | 856 | /* --- the following bits of info are returned as pointers to data or functions --- */ |
| 857 | | case MESS_DEVINFO_PTR_LOAD: info->load = DEVICE_IMAGE_LOAD_NAME(exidy_floppy); break; |
| 857 | case MESS_DEVINFO_PTR_LOAD: info->load = DEVICE_IMAGE_LOAD_NAME(exidy_floppy); break; |
| 858 | 858 | |
| 859 | 859 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 860 | | case MESS_DEVINFO_STR_FILE_EXTENSIONS: strcpy(info->s = device_temp_str(), "dsk"); break; |
| 860 | case MESS_DEVINFO_STR_FILE_EXTENSIONS: strcpy(info->s = device_temp_str(), "dsk"); break; |
| 861 | 861 | |
| 862 | | default: legacybasicdsk_device_getinfo(devclass, state, info); break; |
| 862 | default: legacybasicdsk_device_getinfo(devclass, state, info); break; |
| 863 | 863 | } |
| 864 | 864 | } |
| 865 | 865 | |
| r2801 | r2802 | |
| 869 | 869 | switch(state) |
| 870 | 870 | { |
| 871 | 871 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 872 | | case MESS_DEVINFO_INT_COUNT: info->i = 2; break; |
| 872 | case MESS_DEVINFO_INT_COUNT: info->i = 2; break; |
| 873 | case MESS_DEVINFO_INT_CASSETTE_DEFAULT_STATE: info->i = CASSETTE_STOPPED | CASSETTE_MOTOR_DISABLED | CASSETTE_SPEAKER_ENABLED; break; |
| 873 | 874 | |
| 874 | | default: cassette_device_getinfo(devclass, state, info); break; |
| 875 | default: cassette_device_getinfo(devclass, state, info); break; |
| 875 | 876 | } |
| 876 | 877 | } |
| 877 | 878 | |
| 878 | 879 | SYSTEM_CONFIG_START(exidy) |
| 879 | 880 | CONFIG_DEVICE(exidy_floppy_getinfo) |
| 880 | 881 | CONFIG_DEVICE(cartslot_device_getinfo) |
| 881 | | CONFIG_DEVICE(exidy_cassette_getinfo) // use of cassette causes a hang |
| 882 | CONFIG_DEVICE(exidy_cassette_getinfo) |
| 882 | 883 | SYSTEM_CONFIG_END |
| 883 | 884 | |
| 884 | 885 | SYSTEM_CONFIG_START(exidyd) |
| 885 | 886 | CONFIG_DEVICE(cartslot_device_getinfo) |
| 886 | | CONFIG_DEVICE(exidy_cassette_getinfo) // use of cassette causes a hang |
| 887 | CONFIG_DEVICE(exidy_cassette_getinfo) |
| 887 | 888 | SYSTEM_CONFIG_END |
| 888 | 889 | |
| 889 | 890 | |