src/mess/devices/ti99cart.c
| r9500 | r9501 | |
| 1 | | /********************************************************************** |
| 2 | | |
| 3 | | ti99cart.c |
| 4 | | |
| 5 | | TI99 family cartridge management |
| 6 | | |
| 7 | | Michael Zapf, 2009 |
| 8 | | |
| 9 | | Console cartridge port |
| 10 | | |
| 11 | | Vss 36||35 GND |
| 12 | | ROMG* 34||33 Vss ROMG* is low if addr in >6000-7FFF |
| 13 | | WE* 32||31 GR High = GROM ready |
| 14 | | A4 30||29 -5V |
| 15 | | A5 28||27 GRC GROMCLK (or CPUCLK) from the VDP 9918A |
| 16 | | A6 26||25 DBIN |
| 17 | | A3 24||23 A14 |
| 18 | | A7 22||21 GS* Low if addr in >9800-9FFF |
| 19 | | A8 20||19 +5V |
| 20 | | A9 18||17 D0 |
| 21 | | A10 16||15 D1 |
| 22 | | A11 14||13 D2 |
| 23 | | A12 12||11 D3 |
| 24 | | A13 10||9 D4 |
| 25 | | A15/OUT 8||7 D5 |
| 26 | | CRUIN 6||5 D6 |
| 27 | | CRUCLK* 4||3 D7 |
| 28 | | GND 2||1 RESET (Active high) |
| 29 | | |
| 30 | | 1 RESET < Resets the system (active high) |
| 31 | | D0 ... D7: Data bus (D0 = most significant bit) |
| 32 | | A3 ... A15: Address bus (A15 shared with CRUOUT signal, A3 most significant) |
| 33 | | 4 CRUCLK* > Inversion of TMS9900 CRUCLOCK pin |
| 34 | | 6 CRUIN < CRU input to TMS9900 |
| 35 | | 21 GS* > Grom select. Active low is addr in >9800-9FFF. Internal 2.2K pull-up to +5V. |
| 36 | | 25 DBIN > Active high = read memory. Internal 3.3K pull-up to +5V |
| 37 | | 27 GRC > GROM clock: 447.7 kHz signal from the VPD (or 3.5 MHz, jumper-selected). 1K pull-up to +5V. |
| 38 | | 29 VDD -5 Volts power supply |
| 39 | | 31 GR < Active high = GROM ready. 4.7K pull-up. Masked by GS* in the console before it reaches the CPU. |
| 40 | | 32 WE* > Active low = write enable (derived from TMS9900 WE*) |
| 41 | | 33 VSS Pseudo-ground (-0.72 V; purpose unclear) |
| 42 | | 34 ROMG* > Active low if addr in >6000-7FFF |
| 43 | | |
| 44 | | Notes: |
| 45 | | |
| 46 | | * There is no RESET line from the console to the cartridges. The RESET line used |
| 47 | | here is intended to reset the console when a cartridge is plugged in |
| 48 | | * The cartridge port's data bus must be connected to the 16/8 databus multiplex |
| 49 | | * GR is connected to the console's READY* line and is used by GROMs which are |
| 50 | | comparably slow devices. During their processing time the main CPU is |
| 51 | | suspended. (This is not yet implemented). |
| 52 | | * A3-A15 offer a space for 8 KiB ROM. The ROMG* line gets active (low) when |
| 53 | | A0-A2 is 011 (bin), so we get addresses 0x6000 - 0x7fff. |
| 54 | | * Some cartridges use special circuitry which allows for more than 8 KiB by |
| 55 | | switching banks. |
| 56 | | * A15 is shared with the CRUOUT line. During CRUCLK* being active, the line is |
| 57 | | CRUOUT, else A15. |
| 58 | | * A15 is a constructed signal that is created by the 16/8 databus multiplexer. |
| 59 | | The 9900 CPU only has a word-oriented addressing and does not offer an A15 |
| 60 | | line. |
| 61 | | |
| 62 | | *********************************************************************/ |
| 63 | | #include "emu.h" |
| 64 | | #include "ti99cart.h" |
| 65 | | #include "cartslot.h" |
| 66 | | #include "multcart.h" |
| 67 | | |
| 68 | | typedef int assmfct(running_device *); |
| 69 | | |
| 70 | | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_NONE, ti99_cartridge_pcb_none); |
| 71 | | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_STD, ti99_cartridge_pcb_std); |
| 72 | | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGED, ti99_cartridge_pcb_paged); |
| 73 | | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_MINIMEM, ti99_cartridge_pcb_minimem); |
| 74 | | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_SUPER, ti99_cartridge_pcb_super); |
| 75 | | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_MBX, ti99_cartridge_pcb_mbx); |
| 76 | | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGED379I, ti99_cartridge_pcb_paged379i); |
| 77 | | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGEDCRU, ti99_cartridge_pcb_pagedcru); |
| 78 | | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_GRAMKRACKER, ti99_cartridge_pcb_gramkracker); |
| 79 | | |
| 80 | | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_NONE, ti99_cartridge_pcb_none); |
| 81 | | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_STD, ti99_cartridge_pcb_std); |
| 82 | | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGED, ti99_cartridge_pcb_paged); |
| 83 | | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_MINIMEM, ti99_cartridge_pcb_minimem); |
| 84 | | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_SUPER, ti99_cartridge_pcb_super); |
| 85 | | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_MBX, ti99_cartridge_pcb_mbx); |
| 86 | | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGED379I, ti99_cartridge_pcb_paged379i); |
| 87 | | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGEDCRU, ti99_cartridge_pcb_pagedcru); |
| 88 | | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_GRAMKRACKER, ti99_cartridge_pcb_gramkracker); |
| 89 | | |
| 90 | | /* Generic TI99 cartridge structure. */ |
| 91 | | struct _cartridge_t |
| 92 | | { |
| 93 | | // PCB device associated to this cartridge. If NULL, the slot is empty. |
| 94 | | running_device *pcb; |
| 95 | | |
| 96 | | // GROM buffer size. |
| 97 | | int grom_size; |
| 98 | | |
| 99 | | // ROM page. |
| 100 | | int rom_page; |
| 101 | | |
| 102 | | // RAM page. |
| 103 | | int ram_page; |
| 104 | | |
| 105 | | // ROM buffer size. All banks have equal sizes. |
| 106 | | int rom_size; |
| 107 | | // RAM buffer size. All banks have equal sizes. |
| 108 | | int ram_size; |
| 109 | | |
| 110 | | // pointer to GROM data. |
| 111 | | UINT8 *grom_ptr; |
| 112 | | |
| 113 | | // ROM buffer. We are using this for both 16 bit (99/4a) and 8 bit |
| 114 | | // (99/8) access. |
| 115 | | void *rom_ptr; |
| 116 | | |
| 117 | | // ROM buffer for the second bank of paged cartridges. Other cartridges |
| 118 | | // usually store their ROM in one large file. |
| 119 | | void *rom2_ptr; |
| 120 | | |
| 121 | | // RAM buffer. The persistence service is done by the cartridge system. |
| 122 | | // The RAM space is a consecutive space; all banks are in one buffer. |
| 123 | | void *ram_ptr; |
| 124 | | }; |
| 125 | | typedef struct _cartridge_t cartridge_t; |
| 126 | | |
| 127 | | enum |
| 128 | | { |
| 129 | | TI99CARTINFO_FCT_6000_R = DEVINFO_FCT_DEVICE_SPECIFIC, /* read16_device_handler */ |
| 130 | | TI99CARTINFO_FCT_6000_W, /* write16_device_handler */ |
| 131 | | TI99CARTINFO_FCT_6000_R8, /* read8_device_handler */ |
| 132 | | TI99CARTINFO_FCT_6000_W8, /* write16_device_handler */ |
| 133 | | TI99CARTINFO_FCT_CRU_R, |
| 134 | | TI99CARTINFO_FCT_CRU_W, |
| 135 | | TI99CART_FCT_ASSM, |
| 136 | | TI99CART_FCT_DISASSM |
| 137 | | }; |
| 138 | | |
| 139 | | typedef enum _legacy_cart_t |
| 140 | | { |
| 141 | | LEG_CART_NONE, |
| 142 | | LEG_CART_STD, |
| 143 | | LEG_CART_EXB, |
| 144 | | LEG_CART_MINI, |
| 145 | | LEG_CART_MBX |
| 146 | | } legacy_cart_t; |
| 147 | | |
| 148 | | struct _ti99_multicart_state |
| 149 | | { |
| 150 | | // Reserves space for all cartridges. This is also used in the legacy |
| 151 | | // cartridge system, but only for slot 0. |
| 152 | | cartridge_t cartridge[NUMBER_OF_CARTRIDGE_SLOTS]; |
| 153 | | |
| 154 | | // Determines which slot is currently active. This value is changed when |
| 155 | | // there are accesses to other GROM base addresses. |
| 156 | | int active_slot; |
| 157 | | |
| 158 | | // Used in order to enforce a special slot. This value is retrieved |
| 159 | | // from the dipswitch setting. A value of -1 means automatic, that is, |
| 160 | | // the grom base switch is used. Values 0 .. max refer to the |
| 161 | | // respective slot. |
| 162 | | int fixed_slot; |
| 163 | | |
| 164 | | // Contains the slot number of the GRAM Kracker |
| 165 | | int gk_slot; |
| 166 | | |
| 167 | | // Contains the slot number of the GK guest (which is the cartridge |
| 168 | | // plugged into the GK) |
| 169 | | int gk_guest_slot; |
| 170 | | |
| 171 | | // Holds the highest index of a cartridge being plugged in plus one. |
| 172 | | // If we only have one cartridge inserted, we don't want to get a |
| 173 | | // selection option, so we just mirror the memory contents. |
| 174 | | int next_free_slot; |
| 175 | | |
| 176 | | // Counts the number of slots which currently contain legacy format |
| 177 | | // cartridge images. |
| 178 | | int legacy_slots; |
| 179 | | |
| 180 | | // Counts the number of slots which currently contain new format |
| 181 | | // cartridge images. |
| 182 | | int multi_slots; |
| 183 | | |
| 184 | | // Legacy mode. Stores the slot number where the cartridge part is |
| 185 | | // mounted. Index is from slotc_type_t. |
| 186 | | int legacy_slotnumber[5]; |
| 187 | | |
| 188 | | // Current address pointer for the active GROM in port (16 bits) |
| 189 | | UINT16 grom_address; |
| 190 | | |
| 191 | | // GROM data buffer |
| 192 | | UINT8 grom_buffer; |
| 193 | | |
| 194 | | // Hack for GRAM Kracker. Flag to remember whether we switched the |
| 195 | | // loader area. (The GK utility assumes that it is not important whether |
| 196 | | // the GROM address is set before or after the loader bank has been |
| 197 | | // manually switched, but this will fail here.) |
| 198 | | int loader_on; |
| 199 | | |
| 200 | | // Internal flip-flops that are set after the first access to the GROM |
| 201 | | // address so that next access is mapped to the LSB, and cleared after |
| 202 | | // each data access |
| 203 | | char raddr_LSB, waddr_LSB; |
| 204 | | }; |
| 205 | | typedef struct _ti99_multicart_state ti99_multicart_state; |
| 206 | | |
| 207 | | #define AUTO -1 |
| 208 | | |
| 209 | | typedef enum _slotc_type_t |
| 210 | | { |
| 211 | | SLOTC_EMPTY = -1, |
| 212 | | SLOTC_GROM = 0, |
| 213 | | SLOTC_CROM = 1, |
| 214 | | SLOTC_DROM = 2, |
| 215 | | SLOTC_MINIMEM = 3, |
| 216 | | SLOTC_MBX = 4 |
| 217 | | } slotc_type_t; |
| 218 | | |
| 219 | | /* Function declaration; the function itself is in the later part of this file. */ |
| 220 | | static UINT8 cartridge_grom_read_legacy(running_device *cartsys); |
| 221 | | static void unload_legacy(device_image_interface &image); |
| 222 | | static int load_legacy(device_image_interface &image); |
| 223 | | |
| 224 | | static int cartridge_gram_kracker_readg(running_device *cartsys); |
| 225 | | static void cartridge_gram_kracker_writeg(running_device *cartsys, UINT8 data); |
| 226 | | |
| 227 | | /* Access to the pcb. Contained in the token of the pcb instance. */ |
| 228 | | struct _ti99_pcb_t |
| 229 | | { |
| 230 | | /* Read function for this cartridge. */ |
| 231 | | read16_device_func read; |
| 232 | | |
| 233 | | /* Write function for this cartridge. */ |
| 234 | | write16_device_func write; |
| 235 | | |
| 236 | | /* Read function for this cartridge, TI-99/8. */ |
| 237 | | read8_device_func read8; |
| 238 | | |
| 239 | | /* Write function for this cartridge, TI-99/8. */ |
| 240 | | write8_device_func write8; |
| 241 | | |
| 242 | | /* Read function for this cartridge, CRU. */ |
| 243 | | read8_device_func cruread; |
| 244 | | |
| 245 | | /* Write function for this cartridge, CRU. */ |
| 246 | | write8_device_func cruwrite; |
| 247 | | |
| 248 | | /* Link up to the cartridge structure which contains this pcb. */ |
| 249 | | cartridge_t *cartridge; |
| 250 | | |
| 251 | | /* Function to assemble this cartridge. */ |
| 252 | | assmfct *assemble; |
| 253 | | |
| 254 | | /* Function to disassemble this cartridge. */ |
| 255 | | assmfct *disassemble; |
| 256 | | }; |
| 257 | | typedef struct _ti99_pcb_t ti99_pcb_t; |
| 258 | | |
| 259 | | static int in_legacy_mode(running_device *device) |
| 260 | | { |
| 261 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 262 | | if ((cartslots->legacy_slots>0) && (cartslots->multi_slots==0)) |
| 263 | | return TRUE; |
| 264 | | return FALSE; |
| 265 | | } |
| 266 | | |
| 267 | | /* |
| 268 | | Activates a slot in the multi-cartridge extender. |
| 269 | | Setting the slot is done by accessing the GROM ports using a |
| 270 | | specific address: |
| 271 | | |
| 272 | | slot n: read data (read address, write data, write address) |
| 273 | | |
| 274 | | slot 0: 0x9800 (0x9802, 0x9c00, 0x9c02) : cartridge1 |
| 275 | | slot 1: 0x9804 (0x9806, 0x9c04, 0x9c06) : cartridge2 |
| 276 | | ... |
| 277 | | slot 15: 0x983c (0x983e, 0x9c3c, 0x9c3e) : cartridge16 |
| 278 | | |
| 279 | | The following addresses are theoretically available, but the |
| 280 | | built-in OS does not use them; i.e. cartridges will not be |
| 281 | | included in the selection list, and their features will not be |
| 282 | | found by lookup, but they could be accessed directly by user |
| 283 | | programs. |
| 284 | | slot 16: 0x9840 (0x9842, 0x9c40, 0x9c42) |
| 285 | | ... |
| 286 | | slot 255: 0x9bfc (0x9bfe, 0x9ffc, 0x9ffe) |
| 287 | | |
| 288 | | Setting the GROM base should select one cartridge, but the ROMs in the |
| 289 | | CPU space must also be switched. As there is no known special mechanism |
| 290 | | we assume that by switching the GROM base, the ROM is automatically |
| 291 | | switched. |
| 292 | | |
| 293 | | Caution: This means that cartridges which do not have at least one |
| 294 | | GROM cannot be switched with this mechanism. |
| 295 | | |
| 296 | | We assume that the slot number is already calculated in the caller: |
| 297 | | slotnumber>=0 && slotnumber<=255 |
| 298 | | |
| 299 | | NOTE: The OS will stop searching when it finds slots 1 and 2 empty. |
| 300 | | Interestingly, cartridge subroutines are found nevertheless, even when |
| 301 | | the cartridge is plugged into a higher slot. |
| 302 | | */ |
| 303 | | static void cartridge_slot_set(running_device *cartsys, int slotnumber) |
| 304 | | { |
| 305 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 306 | | assert(slotnumber>=0 && slotnumber<=255); |
| 307 | | // if (cartslots->active_slot != slotnumber) printf("Setting cartslot to %d\n", slotnumber); |
| 308 | | if (cartslots->fixed_slot==AUTO) |
| 309 | | cartslots->active_slot = slotnumber; |
| 310 | | else |
| 311 | | cartslots->active_slot = cartslots->fixed_slot; |
| 312 | | } |
| 313 | | |
| 314 | | static int slot_is_empty(running_device *cartsys, int slotnumber) |
| 315 | | { |
| 316 | | cartridge_t *cart; |
| 317 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 318 | | |
| 319 | | cart = &cartslots->cartridge[slotnumber]; |
| 320 | | |
| 321 | | if ((cart->rom_size==0) && (cart->ram_size==0) && (cart->grom_size==0)) |
| 322 | | return TRUE; |
| 323 | | return FALSE; |
| 324 | | } |
| 325 | | |
| 326 | | static void clear_slot(running_device *cartsys, int slotnumber) |
| 327 | | { |
| 328 | | cartridge_t *cart; |
| 329 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 330 | | |
| 331 | | cart = &cartslots->cartridge[slotnumber]; |
| 332 | | |
| 333 | | cart->rom_size = cart->ram_size = cart->grom_size = 0; |
| 334 | | cart->rom_ptr = NULL; |
| 335 | | cart->rom2_ptr = NULL; |
| 336 | | cart->ram_ptr = NULL; |
| 337 | | cart->grom_ptr = NULL; |
| 338 | | |
| 339 | | if (slotnumber==cartslots->gk_guest_slot) |
| 340 | | cartslots->gk_guest_slot = -1; |
| 341 | | } |
| 342 | | |
| 343 | | static void grom_write_address(ti99_multicart_state *cartslots, UINT8 data) |
| 344 | | { |
| 345 | | /* We are writing; the read states will automatically fall back to default. */ |
| 346 | | cartslots->raddr_LSB = FALSE; |
| 347 | | |
| 348 | | /* Fore more comments, see machine/ti99_4x.c */ |
| 349 | | if (cartslots->waddr_LSB) |
| 350 | | { |
| 351 | | /* Accept low byte (2nd write) */ |
| 352 | | cartslots->grom_address = (cartslots->grom_address & 0xff00) | (data & 0x00ff); |
| 353 | | cartslots->waddr_LSB = FALSE; |
| 354 | | } |
| 355 | | else |
| 356 | | { |
| 357 | | /* Accept high byte (1st write) */ |
| 358 | | cartslots->grom_address = ((data<<8) & 0xff00) | (cartslots->grom_address & 0x00ff); |
| 359 | | cartslots->waddr_LSB = TRUE; |
| 360 | | } |
| 361 | | } |
| 362 | | |
| 363 | | /* |
| 364 | | Set the address pointer value or write to GRAM (if available). |
| 365 | | */ |
| 366 | | WRITE8_DEVICE_HANDLER(ti99_cartridge_grom_w) |
| 367 | | { |
| 368 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 369 | | |
| 370 | | // Set the cart slot. Note that the port must be adjusted for 16-bit |
| 371 | | // systems, i.e. it should be shifted left by 1. |
| 372 | | // 1001 1wbb bbbb bbr0 |
| 373 | | |
| 374 | | cartridge_slot_set(device, (offset>>2) & 0x00ff); |
| 375 | | |
| 376 | | if (offset & 0x0002) |
| 377 | | grom_write_address(cartslots, data); |
| 378 | | else |
| 379 | | { |
| 380 | | cartslots->raddr_LSB = cartslots->waddr_LSB = FALSE; |
| 381 | | |
| 382 | | // Handle the GRAM Kracker |
| 383 | | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 384 | | cartridge_gram_kracker_writeg(device,data); |
| 385 | | |
| 386 | | cartslots->grom_address = ((cartslots->grom_address + 1) & 0x1FFF) | (cartslots->grom_address & 0xE000); |
| 387 | | |
| 388 | | /* Reset the read and write address flipflops. */ |
| 389 | | cartslots->raddr_LSB = cartslots->waddr_LSB = FALSE; |
| 390 | | } |
| 391 | | } |
| 392 | | |
| 393 | | /* |
| 394 | | Deliver the local address pointer value. Note that this must be |
| 395 | | in sync with the console. |
| 396 | | */ |
| 397 | | static UINT8 grom_read_address(ti99_multicart_state *cartslots) |
| 398 | | { |
| 399 | | UINT8 reply; |
| 400 | | // When reading, reset the hi/lo flag byte for writing. |
| 401 | | // TODO: Verify this with a real machine. |
| 402 | | cartslots->waddr_LSB = FALSE; |
| 403 | | |
| 404 | | // Address reading is done in two steps; first, the high byte |
| 405 | | // is transferred, then the low byte. |
| 406 | | if (cartslots->raddr_LSB) |
| 407 | | { |
| 408 | | /* second pass */ |
| 409 | | reply = cartslots->grom_address & 0x00ff; |
| 410 | | cartslots->raddr_LSB = FALSE; |
| 411 | | } |
| 412 | | else |
| 413 | | { |
| 414 | | /* first pass */ |
| 415 | | reply = (cartslots->grom_address>>8) & 0x00ff; |
| 416 | | cartslots->raddr_LSB = TRUE; |
| 417 | | } |
| 418 | | return reply; |
| 419 | | } |
| 420 | | |
| 421 | | /* |
| 422 | | GROM access. Accesses the GROMs in the cartridge. GROM memory is |
| 423 | | accessed serially via a set of ports. The GRMWA port (bank 0: 0x9c02) |
| 424 | | takes two consecutive byte writes which represent the high and low byte |
| 425 | | of the memory address in the GROM space. Once set, bytes can be read |
| 426 | | by repeated read accesses on the GRMRD port (bank 0: 0x9800). The |
| 427 | | address pointer advances on each read. The pointer wraps at the end |
| 428 | | of each GROM chip memory space, i.e. every 8KiB. |
| 429 | | This function takes the current GROM address minus 0x6000 which is the |
| 430 | | lowest address for cartridge GROMs. Lower addresses access GROMs in the |
| 431 | | console only, which appear in all banks. |
| 432 | | Note that the GROM access mechanism is identical for all cartridge |
| 433 | | types. |
| 434 | | */ |
| 435 | | READ8_DEVICE_HANDLER(ti99_cartridge_grom_r) |
| 436 | | { |
| 437 | | UINT8 value; |
| 438 | | int slot; |
| 439 | | cartridge_t *cartridge; |
| 440 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 441 | | |
| 442 | | // Set the cart slot. Note that the port must be adjusted for 16-bit |
| 443 | | // systems, i.e. it should be shifted left by 1. |
| 444 | | // 1001 1wbb bbbb bbr0 |
| 445 | | |
| 446 | | cartridge_slot_set(device, (offset>>2) & 0x00ff); |
| 447 | | |
| 448 | | if (offset & 0x0002) |
| 449 | | return grom_read_address(cartslots); |
| 450 | | |
| 451 | | // Handle the GRAM Kracker |
| 452 | | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 453 | | return cartridge_gram_kracker_readg(device); |
| 454 | | |
| 455 | | /* Handle the legacy mode. */ |
| 456 | | if (in_legacy_mode(device)) |
| 457 | | return cartridge_grom_read_legacy(device); |
| 458 | | |
| 459 | | slot = cartslots->active_slot; |
| 460 | | |
| 461 | | // This fixes a minor issue: The selection mechanism of the console |
| 462 | | // concludes that there are multiple cartridges plugged in when it |
| 463 | | // checks the different GROM bases and finds different data. Empty |
| 464 | | // slots should return 0 at all addresses, but when we have only |
| 465 | | // one cartridge, this entails that the second cartridge is |
| 466 | | // different (all zeros), and so we get a meaningless selection |
| 467 | | // option between cartridge 1 and 1 and 1 ... |
| 468 | | // |
| 469 | | // So if next_free_slot==1, we have one cartridge in slot 0. |
| 470 | | // In that case we trick the OS to believe that the addressed |
| 471 | | // cartridge appears at all locations which causes it to assume a |
| 472 | | // standard single cartslot. |
| 473 | | |
| 474 | | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 475 | | slot=0; |
| 476 | | |
| 477 | | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 478 | | value = 0; |
| 479 | | else |
| 480 | | { |
| 481 | | cartridge = &cartslots->cartridge[slot]; |
| 482 | | if ((cartridge->grom_size>0) && (cartridge->pcb!=NULL)) |
| 483 | | { |
| 484 | | // This is the reason why we need the image size: Read |
| 485 | | // access beyond the image size must be detected. */ |
| 486 | | /* TODO: replace this using a bitmap of the available GROMs */ |
| 487 | | if ((cartslots->grom_address < 0x6000) || (cartslots->grom_address > (cartridge->grom_size+0x6000))) |
| 488 | | value = 0; |
| 489 | | else |
| 490 | | { |
| 491 | | /* Buffered GROM access. We use a (virtual) common buffer to avoid implementing this for each cartridge. */ |
| 492 | | value = (UINT8)cartslots->grom_buffer; |
| 493 | | /* read ahead */ |
| 494 | | cartslots->grom_buffer = cartridge->grom_ptr[cartslots->grom_address-0x6000]; |
| 495 | | } |
| 496 | | } |
| 497 | | else |
| 498 | | { |
| 499 | | /* We assume that empty sockets return 0 */ |
| 500 | | value = 0; |
| 501 | | //printf("Empty socket at address G(%d)>%04x\n", slot, cartslots->grom_address); |
| 502 | | } |
| 503 | | } |
| 504 | | /* The program counter wraps at each GROM chip size (8K), */ |
| 505 | | /* so 0x5fff + 1 = 0x4000. */ |
| 506 | | /* The address must be increased even if there is no cartridge slot */ |
| 507 | | cartslots->grom_address = ((cartslots->grom_address + 1) & 0x1FFF) | (cartslots->grom_address & 0xE000); |
| 508 | | |
| 509 | | /* Reset the read and write address flipflops. */ |
| 510 | | cartslots->raddr_LSB = cartslots->waddr_LSB = FALSE; |
| 511 | | |
| 512 | | return value; |
| 513 | | } |
| 514 | | |
| 515 | | /* |
| 516 | | Find the index of the cartridge name. We assume the format |
| 517 | | <name><number>, i.e. the number is the longest string from the right |
| 518 | | which can be interpreted as a number. |
| 519 | | */ |
| 520 | | static int get_index_from_tagname(running_device *image) |
| 521 | | { |
| 522 | | const char *tag = image->tag(); |
| 523 | | int maxlen = strlen(tag); |
| 524 | | int i; |
| 525 | | for (i=maxlen-1; i >=0; i--) |
| 526 | | if (tag[i] < 48 || tag[i] > 57) break; |
| 527 | | |
| 528 | | return atoi(tag+i+1); |
| 529 | | } |
| 530 | | |
| 531 | | /* |
| 532 | | Common routine to assemble cartridges from resources. |
| 533 | | */ |
| 534 | | static cartridge_t *assemble_common(running_device *cartslot) |
| 535 | | { |
| 536 | | /* Pointer to the cartridge structure. */ |
| 537 | | cartridge_t *cartridge; |
| 538 | | running_device *cartsys = cartslot->owner(); |
| 539 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 540 | | |
| 541 | | void *socketcont; |
| 542 | | int reslength; |
| 543 | | int i; |
| 544 | | |
| 545 | | int slotnumber = get_index_from_tagname(cartslot)-1; |
| 546 | | assert(slotnumber>=0 && slotnumber<NUMBER_OF_CARTRIDGE_SLOTS); |
| 547 | | |
| 548 | | /* There is a cartridge in this slot, check the maximum slot number. */ |
| 549 | | if (cartslots->next_free_slot <= slotnumber) |
| 550 | | { |
| 551 | | cartslots->next_free_slot = slotnumber+1; |
| 552 | | } |
| 553 | | cartridge = &cartslots->cartridge[slotnumber]; |
| 554 | | |
| 555 | | // Get the socket which is retrieved by the multicart instance in |
| 556 | | // the state of the cartslot instance. |
| 557 | | socketcont = cartslot_get_socket(cartslot, "grom_socket"); |
| 558 | | reslength = cartslot_get_resource_length(cartslot, "grom_socket"); |
| 559 | | if (socketcont != NULL) |
| 560 | | { |
| 561 | | cartridge->grom_ptr = (UINT8 *)socketcont; |
| 562 | | cartridge->grom_size = reslength; |
| 563 | | } |
| 564 | | |
| 565 | | socketcont = cartslot_get_socket(cartslot, "rom_socket"); |
| 566 | | reslength = cartslot_get_resource_length(cartslot, "rom_socket"); |
| 567 | | if (socketcont != NULL) |
| 568 | | { |
| 569 | | cartridge->rom_ptr = socketcont; |
| 570 | | cartridge->rom_size = reslength; |
| 571 | | if (!ti99_is_99_8()) |
| 572 | | { |
| 573 | | /* Big-endianize it for 16 bit access. */ |
| 574 | | UINT16 *cont16b = (UINT16 *)socketcont; |
| 575 | | for (i = 0; i < cartridge->rom_size/2; i++) |
| 576 | | cont16b[i] = BIG_ENDIANIZE_INT16(cont16b[i]); |
| 577 | | } |
| 578 | | } |
| 579 | | |
| 580 | | socketcont = cartslot_get_socket(cartslot, "rom2_socket"); |
| 581 | | // reslength = cartslot_get_resource_length(cartslot, "rom2_socket"); /* cannot differ from rom_socket */ |
| 582 | | if (socketcont != NULL) |
| 583 | | { |
| 584 | | cartridge->rom2_ptr = (UINT16 *)socketcont; |
| 585 | | assert(cartridge->rom2_ptr != NULL); |
| 586 | | if (!ti99_is_99_8()) |
| 587 | | { |
| 588 | | /* Big-endianize it for 16 bit access. */ |
| 589 | | UINT16 *cont16b = (UINT16 *)socketcont; |
| 590 | | for (i = 0; i < cartridge->rom_size/2; i++) |
| 591 | | cont16b[i] = BIG_ENDIANIZE_INT16(cont16b[i]); |
| 592 | | } |
| 593 | | } |
| 594 | | |
| 595 | | socketcont = cartslot_get_socket(cartslot, "ram_socket"); |
| 596 | | reslength = cartslot_get_resource_length(cartslot, "ram_socket"); |
| 597 | | if (socketcont != NULL) |
| 598 | | { |
| 599 | | cartridge->ram_ptr = socketcont; |
| 600 | | cartridge->ram_size = reslength; |
| 601 | | if (!ti99_is_99_8()) |
| 602 | | { |
| 603 | | /* Big-endianize it for 16 bit access. */ |
| 604 | | UINT16 *cont16b = (UINT16 *)socketcont; |
| 605 | | for (i = 0; i < cartridge->ram_size/2; i++) |
| 606 | | cont16b[i] = BIG_ENDIANIZE_INT16(cont16b[i]); |
| 607 | | } |
| 608 | | } |
| 609 | | |
| 610 | | // If the GRAM Kracker is plugged in, this cartridge may become the |
| 611 | | // guest |
| 612 | | if (cartslots->gk_slot != -1) |
| 613 | | { |
| 614 | | for (i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) |
| 615 | | { |
| 616 | | if (!slot_is_empty(cartsys, i) && (i != cartslots->gk_slot)) |
| 617 | | { |
| 618 | | cartslots->gk_guest_slot = i; |
| 619 | | break; |
| 620 | | } |
| 621 | | } |
| 622 | | } |
| 623 | | return cartridge; |
| 624 | | } |
| 625 | | |
| 626 | | static void set_pointers(running_device *pcb, int index) |
| 627 | | { |
| 628 | | running_device *cartsys = pcb->owner()->owner(); |
| 629 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 630 | | ti99_pcb_t *pcb_def = (ti99_pcb_t *)downcast<legacy_device_base *>(pcb)->token(); |
| 631 | | |
| 632 | | pcb_def->read = (read16_device_func)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CARTINFO_FCT_6000_R); |
| 633 | | pcb_def->write = (write16_device_func)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CARTINFO_FCT_6000_W); |
| 634 | | pcb_def->read8 = (read8_device_func)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CARTINFO_FCT_6000_R8); |
| 635 | | pcb_def->write8 = (write8_device_func)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CARTINFO_FCT_6000_W8); |
| 636 | | pcb_def->cruread = (read8_device_func)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CARTINFO_FCT_CRU_R); |
| 637 | | pcb_def->cruwrite = (write8_device_func)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CARTINFO_FCT_CRU_W); |
| 638 | | |
| 639 | | pcb_def->assemble = (assmfct *)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CART_FCT_ASSM); |
| 640 | | pcb_def->disassemble = (assmfct *)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CART_FCT_DISASSM); |
| 641 | | |
| 642 | | pcb_def->cartridge = &cartslots->cartridge[index]; |
| 643 | | pcb_def->cartridge->pcb = pcb; |
| 644 | | } |
| 645 | | |
| 646 | | /***************************************************************************** |
| 647 | | Cartridge type: None |
| 648 | | This PCB device is just a pseudo device; the legacy mode is handled |
| 649 | | by dedicated functions. |
| 650 | | ******************************************************************************/ |
| 651 | | static DEVICE_START(ti99_pcb_none) |
| 652 | | { |
| 653 | | /* device is ti99_cartslot:cartridge:pcb */ |
| 654 | | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 655 | | } |
| 656 | | |
| 657 | | /***************************************************************************** |
| 658 | | Cartridge type: Standard |
| 659 | | Most cartridges are built in this type. Every cartridge may contain |
| 660 | | GROM between 0 and 40 KiB, and ROM with 8 KiB length, no banking. |
| 661 | | ******************************************************************************/ |
| 662 | | |
| 663 | | static DEVICE_START(ti99_pcb_std) |
| 664 | | { |
| 665 | | /* device is ti99_cartslot:cartridge:pcb */ |
| 666 | | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 667 | | } |
| 668 | | |
| 669 | | /* |
| 670 | | Read handler for the CPU address space of the cartridge |
| 671 | | Images for this area are found in the rom_sockets. |
| 672 | | */ |
| 673 | | static READ16_DEVICE_HANDLER( read_cart_std ) |
| 674 | | { |
| 675 | | /* device is pcb, owner is cartslot */ |
| 676 | | UINT16 value; |
| 677 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 678 | | cartridge_t *cartridge = pcb->cartridge; |
| 679 | | |
| 680 | | if (cartridge->rom_ptr==NULL) |
| 681 | | { |
| 682 | | value = 0; |
| 683 | | } |
| 684 | | else |
| 685 | | { |
| 686 | | value = ((UINT16 *)cartridge->rom_ptr)[offset]; |
| 687 | | } |
| 688 | | return value; |
| 689 | | } |
| 690 | | |
| 691 | | static WRITE16_DEVICE_HANDLER( write_cart_std ) |
| 692 | | { |
| 693 | | logerror("Write access to cartridge ROM at address %04x ignored\n", 0x6000 + 2*offset); |
| 694 | | } |
| 695 | | |
| 696 | | /* |
| 697 | | TI-99/8 support: |
| 698 | | Read handler for the CPU address space of the cartridge |
| 699 | | Images for this area are found in the rom_sockets. |
| 700 | | For comments see the READ16 handler. |
| 701 | | */ |
| 702 | | static READ8_DEVICE_HANDLER( read_cart_std8 ) |
| 703 | | { |
| 704 | | UINT8 value; |
| 705 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 706 | | cartridge_t *cartridge = pcb->cartridge; |
| 707 | | |
| 708 | | if (cartridge->rom_ptr==NULL) |
| 709 | | value = 0; |
| 710 | | else |
| 711 | | value = ((UINT8 *)cartridge->rom_ptr)[offset]; |
| 712 | | return value; |
| 713 | | } |
| 714 | | |
| 715 | | /* |
| 716 | | TI-99/8 support: |
| 717 | | Write handler. No writes are allowed to standard cartridges. |
| 718 | | */ |
| 719 | | static WRITE8_DEVICE_HANDLER( write_cart_std8 ) |
| 720 | | { |
| 721 | | logerror("Write access to cartridge ROM at address %04x ignored", 0x6000 + offset); |
| 722 | | } |
| 723 | | |
| 724 | | /* |
| 725 | | The standard cartridge assemble routine. We just call the common |
| 726 | | function here. |
| 727 | | */ |
| 728 | | static int assemble_std(running_device *image) |
| 729 | | { |
| 730 | | assemble_common(image); |
| 731 | | return IMAGE_INIT_PASS; |
| 732 | | } |
| 733 | | |
| 734 | | /* |
| 735 | | Removes pointers and restores the state before plugging in the |
| 736 | | cartridge. |
| 737 | | The pointer to the location after the last cartridge is adjusted. |
| 738 | | As it seems, we can use the same function for the disassembling of all |
| 739 | | cartridge types. |
| 740 | | */ |
| 741 | | static int disassemble_std(running_device *image) |
| 742 | | { |
| 743 | | int slotnumber; |
| 744 | | int i; |
| 745 | | cartridge_t *cart; |
| 746 | | running_device *cartsys = image->owner(); |
| 747 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 748 | | |
| 749 | | slotnumber = get_index_from_tagname(image)-1; |
| 750 | | |
| 751 | | /* Search the highest remaining cartridge. */ |
| 752 | | cartslots->next_free_slot = 0; |
| 753 | | for (i=NUMBER_OF_CARTRIDGE_SLOTS-1; i >= 0; i--) |
| 754 | | { |
| 755 | | if (i != slotnumber) |
| 756 | | { |
| 757 | | if (!slot_is_empty(cartsys, i)) |
| 758 | | { |
| 759 | | cartslots->next_free_slot = i+1; |
| 760 | | break; |
| 761 | | } |
| 762 | | } |
| 763 | | } |
| 764 | | |
| 765 | | /* Do we have RAM? If so, swap the bytes (undo the BIG_ENDIANIZE) */ |
| 766 | | cart = &cartslots->cartridge[slotnumber]; |
| 767 | | if (!ti99_is_99_8()) |
| 768 | | { |
| 769 | | for (i = 0; i < cart->ram_size/2; i++) |
| 770 | | ((UINT16 *)cart->ram_ptr)[i] = BIG_ENDIANIZE_INT16(((UINT16 *)cart->ram_ptr)[i]); |
| 771 | | } |
| 772 | | |
| 773 | | clear_slot(cartsys, slotnumber); |
| 774 | | |
| 775 | | return IMAGE_INIT_PASS; |
| 776 | | } |
| 777 | | |
| 778 | | /***************************************************************************** |
| 779 | | Cartridge type: Paged (Extended Basic) |
| 780 | | This cartridge consists of GROM memory and 2 pages of standard ROM. |
| 781 | | The page is set by writing any value to a location in |
| 782 | | the address area, where an even word offset sets the page to 0 and an |
| 783 | | odd word offset sets the page to 1 (e.g. 6000 = bank 0, and |
| 784 | | 6002 = bank 1). |
| 785 | | ******************************************************************************/ |
| 786 | | |
| 787 | | static DEVICE_START(ti99_pcb_paged) |
| 788 | | { |
| 789 | | /* device is ti99_cartslot:cartridge:pcb */ |
| 790 | | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 791 | | } |
| 792 | | |
| 793 | | /* |
| 794 | | Read handler for the CPU address space of the cartridge |
| 795 | | Images for this area are found in the rom_sockets. |
| 796 | | */ |
| 797 | | static READ16_DEVICE_HANDLER( read_cart_paged ) |
| 798 | | { |
| 799 | | /* device is pcb, owner is cartslot */ |
| 800 | | UINT16 value; |
| 801 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 802 | | cartridge_t *cartridge = pcb->cartridge; |
| 803 | | UINT16 *rombank; |
| 804 | | |
| 805 | | if (cartridge->rom_page==0) |
| 806 | | rombank = (UINT16*)cartridge->rom_ptr; |
| 807 | | else |
| 808 | | rombank = (UINT16*)cartridge->rom2_ptr; |
| 809 | | |
| 810 | | if (rombank==NULL) |
| 811 | | { |
| 812 | | /* TODO: Check for consistency with the GROM memory handling. */ |
| 813 | | value = 0; |
| 814 | | } |
| 815 | | else |
| 816 | | { |
| 817 | | value = rombank[offset]; |
| 818 | | } |
| 819 | | return value; |
| 820 | | } |
| 821 | | |
| 822 | | |
| 823 | | /* |
| 824 | | Handle paging. Extended Basic switches between ROM banks by |
| 825 | | using the value of the LSB of the address where any value |
| 826 | | is written to. |
| 827 | | */ |
| 828 | | static WRITE16_DEVICE_HANDLER( write_cart_paged ) |
| 829 | | { |
| 830 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 831 | | cartridge_t *cartridge = pcb->cartridge; |
| 832 | | cartridge->rom_page = offset & 1; |
| 833 | | } |
| 834 | | |
| 835 | | /* |
| 836 | | TI-99/8 support: |
| 837 | | Read handler for the CPU address space of the cartridge. |
| 838 | | Images for this area are found in the rom_sockets. |
| 839 | | */ |
| 840 | | static READ8_DEVICE_HANDLER( read_cart_paged8 ) |
| 841 | | { |
| 842 | | UINT8 value; |
| 843 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 844 | | cartridge_t *cartridge = pcb->cartridge; |
| 845 | | UINT8 *rombank; |
| 846 | | |
| 847 | | if (cartridge->rom_page==0) |
| 848 | | rombank = (UINT8 *)cartridge->rom_ptr; |
| 849 | | else |
| 850 | | rombank = (UINT8 *)cartridge->rom2_ptr; |
| 851 | | |
| 852 | | if (rombank==NULL) |
| 853 | | value = 0; |
| 854 | | else |
| 855 | | value = rombank[offset]; |
| 856 | | |
| 857 | | return value; |
| 858 | | } |
| 859 | | |
| 860 | | |
| 861 | | /* |
| 862 | | TI-99/8 support: |
| 863 | | Handle paging. Extended Basic switches between ROM banks by |
| 864 | | using the value of the LSB of the address where any value |
| 865 | | is written to. This is the TI-99/8 support. |
| 866 | | Note that we need to check whether offset/2 is even or odd. |
| 867 | | |
| 868 | | FIXME: Extended Basic crashes with TI-99/8 when changing the rom page. |
| 869 | | This may be an incompatibility of Extended Basic and TI-99/8. |
| 870 | | */ |
| 871 | | static WRITE8_DEVICE_HANDLER( write_cart_paged8 ) |
| 872 | | { |
| 873 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 874 | | cartridge_t *cartridge = pcb->cartridge; |
| 875 | | cartridge->rom_page = (offset>>1) & 1; |
| 876 | | } |
| 877 | | |
| 878 | | /* |
| 879 | | We require paged modules to have at least those two rom banks. |
| 880 | | */ |
| 881 | | static int assemble_paged(running_device *image) |
| 882 | | { |
| 883 | | cartridge_t *cart; |
| 884 | | |
| 885 | | cart = assemble_common(image); |
| 886 | | if (cart->rom_ptr==NULL) |
| 887 | | { |
| 888 | | logerror("Missing ROM for paged cartridge"); |
| 889 | | return IMAGE_INIT_FAIL; |
| 890 | | } |
| 891 | | if (cart->rom2_ptr==NULL) |
| 892 | | { |
| 893 | | logerror("Missing second ROM for paged cartridge"); |
| 894 | | return IMAGE_INIT_FAIL; |
| 895 | | } |
| 896 | | |
| 897 | | return IMAGE_INIT_PASS; |
| 898 | | } |
| 899 | | |
| 900 | | /***************************************************************************** |
| 901 | | Cartridge type: Mini Memory |
| 902 | | GROM: 6 KiB (occupies G>6000 to G>7800) |
| 903 | | ROM: 4 KiB (romfile is actually 8 K long, half with zeros, 0x6000-0x6fff) |
| 904 | | persistent RAM: 4 KiB (0x7000-0x7fff) |
| 905 | | ******************************************************************************/ |
| 906 | | |
| 907 | | static DEVICE_START(ti99_pcb_minimem) |
| 908 | | { |
| 909 | | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 910 | | } |
| 911 | | |
| 912 | | static READ16_DEVICE_HANDLER( read_cart_minimem ) |
| 913 | | { |
| 914 | | /* device is pcb, owner is cartslot */ |
| 915 | | UINT16 value; |
| 916 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 917 | | cartridge_t *cartridge = pcb->cartridge; |
| 918 | | |
| 919 | | if (offset < 0x800) |
| 920 | | { |
| 921 | | if (cartridge->rom_ptr==NULL) |
| 922 | | { |
| 923 | | value = 0; |
| 924 | | } |
| 925 | | else |
| 926 | | { |
| 927 | | value = ((UINT16 *)cartridge->rom_ptr)[offset]; |
| 928 | | } |
| 929 | | } |
| 930 | | else |
| 931 | | { |
| 932 | | if (cartridge->ram_ptr==NULL) |
| 933 | | { |
| 934 | | value = 0; |
| 935 | | } |
| 936 | | else |
| 937 | | { |
| 938 | | value = ((UINT16 *)cartridge->ram_ptr)[offset-0x800]; |
| 939 | | } |
| 940 | | } |
| 941 | | return value; |
| 942 | | } |
| 943 | | |
| 944 | | /* |
| 945 | | Mini Memory cartridge write operation. RAM is located at offset + 4 KiB |
| 946 | | */ |
| 947 | | static WRITE16_DEVICE_HANDLER( write_cart_minimem ) |
| 948 | | { |
| 949 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 950 | | cartridge_t *cartridge = pcb->cartridge; |
| 951 | | UINT16 *pos; |
| 952 | | |
| 953 | | if (offset < 0x800) |
| 954 | | { |
| 955 | | logerror("Write access to cartridge ROM at address %04x ignored", 0x6000 + 2*offset); |
| 956 | | } |
| 957 | | else |
| 958 | | { |
| 959 | | if (cartridge->ram_ptr==NULL) |
| 960 | | { |
| 961 | | logerror("No cartridge RAM at address %04x", 0x6000 + 2*offset); |
| 962 | | /* TODO: Check for consistency with the GROM memory handling. */ |
| 963 | | } |
| 964 | | else |
| 965 | | { |
| 966 | | pos = ((UINT16 *)cartridge->ram_ptr) + offset - 0x800; |
| 967 | | COMBINE_DATA(pos); |
| 968 | | } |
| 969 | | } |
| 970 | | } |
| 971 | | |
| 972 | | /* |
| 973 | | Read handler for MiniMemory in TI-99/8. |
| 974 | | */ |
| 975 | | static READ8_DEVICE_HANDLER( read_cart_minimem8 ) |
| 976 | | { |
| 977 | | /* device is pcb, owner is cartslot */ |
| 978 | | UINT8 value; |
| 979 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 980 | | cartridge_t *cartridge = pcb->cartridge; |
| 981 | | |
| 982 | | if (offset < 0x1000) |
| 983 | | { |
| 984 | | if (cartridge->rom_ptr==NULL) |
| 985 | | { |
| 986 | | value = 0; |
| 987 | | } |
| 988 | | else |
| 989 | | { |
| 990 | | value = ((UINT8 *)cartridge->rom_ptr)[offset]; |
| 991 | | } |
| 992 | | } |
| 993 | | else |
| 994 | | { |
| 995 | | if (cartridge->ram_ptr==NULL) |
| 996 | | { |
| 997 | | value = 0; |
| 998 | | } |
| 999 | | else |
| 1000 | | { |
| 1001 | | value = ((UINT8 *)cartridge->ram_ptr)[offset-0x1000]; |
| 1002 | | } |
| 1003 | | } |
| 1004 | | return value; |
| 1005 | | } |
| 1006 | | |
| 1007 | | /* |
| 1008 | | Mini Memory cartridge write operation. RAM is located at offset + 4 KiB. |
| 1009 | | This is the TI-99/8 support. |
| 1010 | | */ |
| 1011 | | static WRITE8_DEVICE_HANDLER( write_cart_minimem8 ) |
| 1012 | | { |
| 1013 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1014 | | cartridge_t *cartridge = pcb->cartridge; |
| 1015 | | |
| 1016 | | if (offset < 0x1000) |
| 1017 | | { |
| 1018 | | logerror("Write access to cartridge ROM at address %04x ignored", 0x6000 + offset); |
| 1019 | | } |
| 1020 | | else |
| 1021 | | { |
| 1022 | | if (cartridge->ram_ptr==NULL) |
| 1023 | | { |
| 1024 | | logerror("No cartridge RAM at address %04x", 0x6000 + offset); |
| 1025 | | /* TODO: Check for consistency with the GROM memory handling. */ |
| 1026 | | } |
| 1027 | | else |
| 1028 | | { |
| 1029 | | ((UINT8 *)cartridge->ram_ptr)[offset-0x1000] = data; |
| 1030 | | } |
| 1031 | | } |
| 1032 | | } |
| 1033 | | |
| 1034 | | |
| 1035 | | static int assemble_minimem(running_device *image) |
| 1036 | | { |
| 1037 | | cartridge_t *cart; |
| 1038 | | |
| 1039 | | cart = assemble_common(image); |
| 1040 | | if (cart->grom_size==0) |
| 1041 | | { |
| 1042 | | logerror("Missing GROM for Mini Memory"); |
| 1043 | | // should not fail here because there may be variations of |
| 1044 | | // cartridges which do not use all parts |
| 1045 | | // return IMAGE_INIT_FAIL; |
| 1046 | | } |
| 1047 | | if (cart->rom_size==0) |
| 1048 | | { |
| 1049 | | logerror("Missing ROM for Mini Memory"); |
| 1050 | | // return IMAGE_INIT_FAIL; |
| 1051 | | } |
| 1052 | | if (cart->ram_size==0) |
| 1053 | | { |
| 1054 | | logerror("Missing RAM for Mini Memory"); |
| 1055 | | // return IMAGE_INIT_FAIL; |
| 1056 | | } |
| 1057 | | |
| 1058 | | return IMAGE_INIT_PASS; |
| 1059 | | } |
| 1060 | | |
| 1061 | | |
| 1062 | | /***************************************************************************** |
| 1063 | | Cartridge type: SuperSpace II |
| 1064 | | SuperSpace cartridge type. SuperSpace is intended as a user-definable |
| 1065 | | blank cartridge containing buffered RAM |
| 1066 | | It has an Editor/Assembler GROM which helps the user to load |
| 1067 | | the user program into the cartridge. If the user program has a suitable |
| 1068 | | header, the console recognizes the cartridge as runnable, and |
| 1069 | | assigns a number in the selection screen. |
| 1070 | | Switching the RAM banks in this cartridge is achieved by setting |
| 1071 | | CRU bits (the system serial interface). |
| 1072 | | |
| 1073 | | GROM: Editor/Assembler GROM |
| 1074 | | ROM: none |
| 1075 | | persistent RAM: 32 KiB (0x6000-0x7fff, 4 banks) |
| 1076 | | Switching the bank is done via CRU write |
| 1077 | | ******************************************************************************/ |
| 1078 | | |
| 1079 | | static DEVICE_START(ti99_pcb_super) |
| 1080 | | { |
| 1081 | | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 1082 | | } |
| 1083 | | |
| 1084 | | /* |
| 1085 | | The CRU read handler. The CRU is a serial interface in the console. |
| 1086 | | Using the CRU we can switch the banks in the SuperSpace cartridge. |
| 1087 | | */ |
| 1088 | | static READ8_DEVICE_HANDLER( read_cart_cru ) |
| 1089 | | { |
| 1090 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1091 | | cartridge_t *cartridge = pcb->cartridge; |
| 1092 | | // offset is the bit number. The CRU base address is already divided by 2. |
| 1093 | | int reply = 0; |
| 1094 | | |
| 1095 | | // ram_page contains the bank number. We have a maximum of |
| 1096 | | // 4 banks; the Super Space II manual says: |
| 1097 | | // |
| 1098 | | // Banks are selected by writing a bit pattern to CRU address >0800: |
| 1099 | | // |
| 1100 | | // Bank # Value |
| 1101 | | // 0 >02 = 0000 0010 |
| 1102 | | // 1 >08 = 0000 1000 |
| 1103 | | // 2 >20 = 0010 0000 |
| 1104 | | // 3 >80 = 1000 0000 |
| 1105 | | // |
| 1106 | | // With the bank number (0, 1, 2, or 3) in R0: |
| 1107 | | // |
| 1108 | | // BNKSW LI R12,>0800 Set CRU address |
| 1109 | | // LI R1,2 Load Shift Bit |
| 1110 | | // SLA R0,1 Align Bank Number |
| 1111 | | // JEQ BNKS1 Skip shift if Bank 0 |
| 1112 | | // SLA R1,0 Align Shift Bit |
| 1113 | | // BNKS1 LDCR R1,0 Switch Banks |
| 1114 | | // SRL R0,1 Restore Bank Number (optional) |
| 1115 | | // RT |
| 1116 | | |
| 1117 | | if ((offset & 1) == 0 || offset > 7) |
| 1118 | | reply = 0; |
| 1119 | | |
| 1120 | | // CRU addresses are only 1 bit wide. Bytes are transferred from LSB |
| 1121 | | // to MSB. That is, 8 bit are eight consecutive addresses. */ |
| 1122 | | reply = (cartridge->ram_page == (offset-1)/2); |
| 1123 | | |
| 1124 | | return reply; |
| 1125 | | } |
| 1126 | | |
| 1127 | | static WRITE8_DEVICE_HANDLER( write_cart_cru ) |
| 1128 | | { |
| 1129 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1130 | | cartridge_t *cartridge = pcb->cartridge; |
| 1131 | | // data is bit |
| 1132 | | // offset is address |
| 1133 | | if (offset < 8) |
| 1134 | | { |
| 1135 | | if (data != 0) |
| 1136 | | cartridge->ram_page = (offset-1)/2; |
| 1137 | | } |
| 1138 | | } |
| 1139 | | |
| 1140 | | /* |
| 1141 | | SuperSpace RAM read operation. RAM is located at 0x6000 to 0x7fff |
| 1142 | | as 4 banks of 8 KiB each |
| 1143 | | */ |
| 1144 | | static READ16_DEVICE_HANDLER( read_cart_super ) |
| 1145 | | { |
| 1146 | | /* device is pcb, owner is cartslot */ |
| 1147 | | UINT16 value; |
| 1148 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1149 | | cartridge_t *cartridge = pcb->cartridge; |
| 1150 | | offs_t boffset; |
| 1151 | | |
| 1152 | | if (cartridge->ram_ptr==NULL) |
| 1153 | | { |
| 1154 | | value = 0; |
| 1155 | | } |
| 1156 | | else |
| 1157 | | { |
| 1158 | | boffset = cartridge->ram_page * 0x1000 + offset; |
| 1159 | | value = ((UINT16 *)cartridge->ram_ptr)[boffset]; |
| 1160 | | } |
| 1161 | | return value; |
| 1162 | | } |
| 1163 | | |
| 1164 | | /* |
| 1165 | | SuperSpace RAM write operation. RAM is located at 0x6000 to 0x7fff |
| 1166 | | as 4 banks of 8 KiB each |
| 1167 | | */ |
| 1168 | | static WRITE16_DEVICE_HANDLER( write_cart_super ) |
| 1169 | | { |
| 1170 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1171 | | cartridge_t *cartridge = pcb->cartridge; |
| 1172 | | UINT16 *pos; |
| 1173 | | offs_t boffset; |
| 1174 | | |
| 1175 | | if (cartridge->ram_ptr==NULL) |
| 1176 | | { |
| 1177 | | logerror("No cartridge RAM at address %04x", 0x6000 + 2*offset); |
| 1178 | | } |
| 1179 | | else |
| 1180 | | { |
| 1181 | | boffset = cartridge->ram_page * 0x1000 + offset; |
| 1182 | | pos = ((UINT16 *)cartridge->ram_ptr) + boffset; |
| 1183 | | COMBINE_DATA(pos); |
| 1184 | | } |
| 1185 | | } |
| 1186 | | |
| 1187 | | /* |
| 1188 | | SuperSpace RAM read operation. RAM is located at 0x6000 to 0x7fff |
| 1189 | | as 4 banks of 8 KiB each. This is the TI-99/8 support. |
| 1190 | | */ |
| 1191 | | static READ8_DEVICE_HANDLER( read_cart_super8 ) |
| 1192 | | { |
| 1193 | | /* device is pcb, owner is cartslot */ |
| 1194 | | UINT8 value; |
| 1195 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1196 | | cartridge_t *cartridge = pcb->cartridge; |
| 1197 | | offs_t boffset; |
| 1198 | | |
| 1199 | | if (cartridge->ram_ptr==NULL) |
| 1200 | | { |
| 1201 | | value = 0; |
| 1202 | | } |
| 1203 | | else |
| 1204 | | { |
| 1205 | | boffset = cartridge->ram_page * 0x2000 + offset; |
| 1206 | | value = ((UINT8 *)cartridge->ram_ptr)[boffset]; |
| 1207 | | } |
| 1208 | | return value; |
| 1209 | | } |
| 1210 | | |
| 1211 | | /* |
| 1212 | | SuperSpace RAM write operation. RAM is located at 0x6000 to 0x7fff |
| 1213 | | as 4 banks of 8 KiB each. This is the TI-99/8 support. |
| 1214 | | */ |
| 1215 | | static WRITE8_DEVICE_HANDLER( write_cart_super8 ) |
| 1216 | | { |
| 1217 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1218 | | cartridge_t *cartridge = pcb->cartridge; |
| 1219 | | offs_t boffset; |
| 1220 | | |
| 1221 | | if (cartridge->ram_ptr==NULL) |
| 1222 | | { |
| 1223 | | logerror("No cartridge RAM at address %04x", 0x6000 + offset); |
| 1224 | | } |
| 1225 | | else |
| 1226 | | { |
| 1227 | | boffset = cartridge->ram_page * 0x2000 + offset; |
| 1228 | | ((UINT8 *)cartridge->ram_ptr)[boffset] = data; |
| 1229 | | } |
| 1230 | | } |
| 1231 | | |
| 1232 | | |
| 1233 | | static int assemble_super(running_device *image) |
| 1234 | | { |
| 1235 | | cartridge_t *cart; |
| 1236 | | |
| 1237 | | cart = assemble_common(image); |
| 1238 | | if (cart->ram_size==0) |
| 1239 | | { |
| 1240 | | logerror("Missing RAM for SuperSpace"); |
| 1241 | | return IMAGE_INIT_FAIL; |
| 1242 | | } |
| 1243 | | return IMAGE_INIT_PASS; |
| 1244 | | } |
| 1245 | | |
| 1246 | | /***************************************************************************** |
| 1247 | | Cartridge type: MBX |
| 1248 | | GROM: up to 40 KiB |
| 1249 | | ROM: up to 16 KiB (in up to 2 banks of 8KiB each) |
| 1250 | | RAM: 1022 B (0x6c00-0x6ffd, overrides ROM in that area) |
| 1251 | | ROM mapper: 6ffe |
| 1252 | | |
| 1253 | | Note that some MBX cartridges assume the presence of the MBX system |
| 1254 | | (additional console) and will not run without it. The MBX hardware is |
| 1255 | | not emulated yet. |
| 1256 | | ******************************************************************************/ |
| 1257 | | |
| 1258 | | static DEVICE_START(ti99_pcb_mbx) |
| 1259 | | { |
| 1260 | | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 1261 | | } |
| 1262 | | |
| 1263 | | static READ16_DEVICE_HANDLER( read_cart_mbx ) |
| 1264 | | { |
| 1265 | | /* device is pcb, owner is cartslot */ |
| 1266 | | UINT16 value = 0; |
| 1267 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1268 | | cartridge_t *cartridge = pcb->cartridge; |
| 1269 | | |
| 1270 | | if ((offset >= 0x0600) && (offset <= 0x07fe)) |
| 1271 | | { |
| 1272 | | // This is the RAM area which overrides any ROM. There is no |
| 1273 | | // known banking behavior for the RAM, so we must assume that |
| 1274 | | // there is only one bank. |
| 1275 | | if (cartridge->ram_ptr != NULL) |
| 1276 | | value = ((UINT16 *)cartridge->ram_ptr)[offset - 0x0600]; |
| 1277 | | } |
| 1278 | | else |
| 1279 | | { |
| 1280 | | if (cartridge->rom_ptr != NULL) |
| 1281 | | value = ((UINT16 *)cartridge->rom_ptr)[offset + cartridge->rom_page * 0x1000]; |
| 1282 | | } |
| 1283 | | return value; |
| 1284 | | } |
| 1285 | | |
| 1286 | | /* |
| 1287 | | MBX write operation. |
| 1288 | | */ |
| 1289 | | static WRITE16_DEVICE_HANDLER( write_cart_mbx ) |
| 1290 | | { |
| 1291 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1292 | | cartridge_t *cartridge = pcb->cartridge; |
| 1293 | | UINT16 *pos; |
| 1294 | | |
| 1295 | | if ((offset >= 0x0600) && (offset <= 0x07fe)) |
| 1296 | | { |
| 1297 | | if (cartridge->ram_ptr == NULL) |
| 1298 | | return; |
| 1299 | | pos = ((UINT16 *)cartridge->ram_ptr) + offset - 0x0600; |
| 1300 | | COMBINE_DATA(pos); |
| 1301 | | } |
| 1302 | | else if ((offset == 0x07ff) && ACCESSING_BITS_8_15) |
| 1303 | | { |
| 1304 | | if (cartridge->rom_ptr==NULL) |
| 1305 | | return; |
| 1306 | | cartridge->rom_page = ((data >> 8) & 1); |
| 1307 | | } |
| 1308 | | } |
| 1309 | | |
| 1310 | | /* |
| 1311 | | TI-99/8 support |
| 1312 | | */ |
| 1313 | | static READ8_DEVICE_HANDLER( read_cart_mbx8 ) |
| 1314 | | { |
| 1315 | | /* device is pcb, owner is cartslot */ |
| 1316 | | UINT8 value = 0; |
| 1317 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1318 | | cartridge_t *cartridge = pcb->cartridge; |
| 1319 | | |
| 1320 | | if ((offset >= 0x0c00) && (offset <= 0x0ffd)) |
| 1321 | | { |
| 1322 | | // This is the RAM area which overrides any ROM. There is no |
| 1323 | | // known banking behavior for the RAM, so we must assume that |
| 1324 | | // there is only one bank. |
| 1325 | | if (cartridge->ram_ptr != NULL) |
| 1326 | | value = ((UINT8 *)cartridge->ram_ptr)[offset - 0x0c00]; |
| 1327 | | } |
| 1328 | | else |
| 1329 | | { |
| 1330 | | if (cartridge->rom_ptr != NULL) |
| 1331 | | value = ((UINT8 *)cartridge->rom_ptr)[offset + cartridge->rom_page * 0x2000]; |
| 1332 | | } |
| 1333 | | return value; |
| 1334 | | } |
| 1335 | | |
| 1336 | | /* |
| 1337 | | MBX write operation (TI-99/8) |
| 1338 | | */ |
| 1339 | | static WRITE8_DEVICE_HANDLER( write_cart_mbx8 ) |
| 1340 | | { |
| 1341 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1342 | | cartridge_t *cartridge = pcb->cartridge; |
| 1343 | | |
| 1344 | | if ((offset >= 0x0c00) && (offset <= 0x0ffd)) |
| 1345 | | { |
| 1346 | | if (cartridge->ram_ptr == NULL) |
| 1347 | | return; |
| 1348 | | ((UINT8 *)cartridge->ram_ptr)[offset - 0x0c00] = data; |
| 1349 | | } |
| 1350 | | else if (offset == 0x0ffe || offset == 0x0fff) |
| 1351 | | { |
| 1352 | | if (cartridge->rom_ptr==NULL) |
| 1353 | | return; |
| 1354 | | cartridge->rom_page = (data & 1); /* TODO: Check */ |
| 1355 | | } |
| 1356 | | } |
| 1357 | | |
| 1358 | | |
| 1359 | | static int assemble_mbx(running_device *image) |
| 1360 | | { |
| 1361 | | assemble_common(image); |
| 1362 | | return IMAGE_INIT_PASS; |
| 1363 | | } |
| 1364 | | |
| 1365 | | /***************************************************************************** |
| 1366 | | Cartridge type: paged379i |
| 1367 | | This cartridge consists of one 16 KiB, 32 KiB, 64 KiB, or 128 KiB EEPROM |
| 1368 | | which is organised in 2, 4, 8, or 16 pages of 8 KiB each. The complete |
| 1369 | | memory contents must be stored in one dump file. |
| 1370 | | The pages are selected by writing a value to some memory locations. Due to |
| 1371 | | using the inverted outputs of the LS379 latch, setting the inputs of the |
| 1372 | | latch to all 0 selects the highest bank, while setting to all 1 selects the |
| 1373 | | lowest. There are some cartridges (16 KiB) which are using this scheme, and |
| 1374 | | there are new hardware developments mainly relying on this scheme. |
| 1375 | | |
| 1376 | | Writing to selects page (16K/32K/64K/128K) |
| 1377 | | >6000 1 / 3 / 7 / 15 |
| 1378 | | >6002 0 / 2 / 6 / 14 |
| 1379 | | >6004 1 / 1 / 5 / 13 |
| 1380 | | >6006 0 / 0 / 4 / 12 |
| 1381 | | >6008 1 / 3 / 3 / 11 |
| 1382 | | >600A 0 / 2 / 2 / 10 |
| 1383 | | >600C 1 / 1 / 1 / 9 |
| 1384 | | >600E 0 / 0 / 0 / 8 |
| 1385 | | >6010 1 / 3 / 7 / 7 |
| 1386 | | >6012 0 / 2 / 6 / 6 |
| 1387 | | >6014 1 / 1 / 5 / 5 |
| 1388 | | >6016 0 / 0 / 4 / 4 |
| 1389 | | >6018 1 / 3 / 3 / 3 |
| 1390 | | >601A 0 / 2 / 2 / 2 |
| 1391 | | >601C 1 / 1 / 1 / 1 |
| 1392 | | >601E 0 / 0 / 0 / 0 |
| 1393 | | |
| 1394 | | ******************************************************************************/ |
| 1395 | | |
| 1396 | | static DEVICE_START(ti99_pcb_paged379i) |
| 1397 | | { |
| 1398 | | /* device is ti99_cartslot:cartridge:pcb */ |
| 1399 | | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 1400 | | } |
| 1401 | | |
| 1402 | | /* |
| 1403 | | Read handler for the CPU address space of the cartridge |
| 1404 | | Images for this area are found in the rom_sockets. |
| 1405 | | */ |
| 1406 | | static READ16_DEVICE_HANDLER( read_cart_paged379i ) |
| 1407 | | { |
| 1408 | | /* device is pcb, owner is cartslot */ |
| 1409 | | UINT16 value; |
| 1410 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1411 | | cartridge_t *cartridge = pcb->cartridge; |
| 1412 | | offs_t boffset; |
| 1413 | | |
| 1414 | | if (cartridge->rom_ptr==NULL) |
| 1415 | | { |
| 1416 | | value = 0; |
| 1417 | | } |
| 1418 | | else |
| 1419 | | { |
| 1420 | | // rom_page is the stored latch value |
| 1421 | | // 16bit access -> multiply by 0x1000 |
| 1422 | | boffset = cartridge->rom_page * 0x1000 + offset; |
| 1423 | | value = ((UINT16 *)cartridge->rom_ptr)[boffset]; |
| 1424 | | } |
| 1425 | | return value; |
| 1426 | | } |
| 1427 | | |
| 1428 | | static void set_paged379i_bank(cartridge_t *cartridge, int rompage) |
| 1429 | | { |
| 1430 | | int mask = 0; |
| 1431 | | if (cartridge->rom_size > 16384) |
| 1432 | | { |
| 1433 | | if (cartridge->rom_size > 32768) |
| 1434 | | { |
| 1435 | | if (cartridge->rom_size > 65536) |
| 1436 | | mask = 15; |
| 1437 | | else |
| 1438 | | mask = 7; |
| 1439 | | } |
| 1440 | | else |
| 1441 | | mask = 3; |
| 1442 | | } |
| 1443 | | else |
| 1444 | | mask = 1; |
| 1445 | | |
| 1446 | | cartridge->rom_page = rompage & mask; |
| 1447 | | } |
| 1448 | | |
| 1449 | | /* |
| 1450 | | Handle paging. We use a LS379 latch for storing the page number. On this |
| 1451 | | PCB, the inverted output of the latch is used, so the order of pages |
| 1452 | | is reversed. (No problem as long as the memory dump is kept exactly |
| 1453 | | in the way it is stored in the EPROM.) |
| 1454 | | The latch can store a value of 4 bits. We adjust the number of |
| 1455 | | significant bits by the size of the memory dump (16K, 32K, 64K). |
| 1456 | | */ |
| 1457 | | static WRITE16_DEVICE_HANDLER( write_cart_paged379i ) |
| 1458 | | { |
| 1459 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1460 | | cartridge_t *cartridge = pcb->cartridge; |
| 1461 | | |
| 1462 | | // Bits: 011x xxxx xxxb bbbx |
| 1463 | | // x = don't care, bbbb = bank |
| 1464 | | // 16bit access -> drop rightmost bit |
| 1465 | | set_paged379i_bank(cartridge, 15 - (offset & 15)); |
| 1466 | | } |
| 1467 | | |
| 1468 | | /* |
| 1469 | | TI-99/8 support: |
| 1470 | | Read handler for the CPU address space of the cartridge. |
| 1471 | | Images for this area are found in the rom_sockets. |
| 1472 | | */ |
| 1473 | | static READ8_DEVICE_HANDLER( read_cart_paged379i8 ) |
| 1474 | | { |
| 1475 | | UINT8 value; |
| 1476 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1477 | | cartridge_t *cartridge = pcb->cartridge; |
| 1478 | | offs_t boffset; |
| 1479 | | |
| 1480 | | if (cartridge->rom_ptr==NULL) |
| 1481 | | { |
| 1482 | | value = 0; |
| 1483 | | } |
| 1484 | | else |
| 1485 | | { |
| 1486 | | // rom_page is the stored latch value |
| 1487 | | // 8bit access -> multiply by 0x2000 |
| 1488 | | boffset = cartridge->rom_page * 0x2000 + offset; |
| 1489 | | value = ((UINT8 *)cartridge->rom_ptr)[boffset]; |
| 1490 | | } |
| 1491 | | return value; |
| 1492 | | |
| 1493 | | } |
| 1494 | | |
| 1495 | | /* |
| 1496 | | TI-99/8 support: Handle paging. |
| 1497 | | */ |
| 1498 | | static WRITE8_DEVICE_HANDLER( write_cart_paged379i8 ) |
| 1499 | | { |
| 1500 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1501 | | cartridge_t *cartridge = pcb->cartridge; |
| 1502 | | |
| 1503 | | // Bits: 011x xxxx xxxb bbbx |
| 1504 | | // x = don't care, bbbb = bank |
| 1505 | | set_paged379i_bank(cartridge, 15 - ((offset>>1) & 15)); |
| 1506 | | } |
| 1507 | | |
| 1508 | | /* |
| 1509 | | Paged379i modules have one EPROM dump. |
| 1510 | | */ |
| 1511 | | static int assemble_paged379i(running_device *image) |
| 1512 | | { |
| 1513 | | cartridge_t *cart; |
| 1514 | | |
| 1515 | | cart = assemble_common(image); |
| 1516 | | if (cart->rom_ptr==NULL) |
| 1517 | | { |
| 1518 | | logerror("Missing ROM for paged cartridge"); |
| 1519 | | return IMAGE_INIT_FAIL; |
| 1520 | | } |
| 1521 | | set_paged379i_bank(cart, 15); |
| 1522 | | return IMAGE_INIT_PASS; |
| 1523 | | } |
| 1524 | | |
| 1525 | | /***************************************************************************** |
| 1526 | | Cartridge type: pagedcru |
| 1527 | | This cartridge consists of one 16 KiB, 32 KiB, or 64 KiB EEPROM which is |
| 1528 | | organised in 2, 4, or 8 pages of 8 KiB each. We assume there is only one |
| 1529 | | dump file of the respective size. |
| 1530 | | The pages are selected by writing a value to the CRU. This scheme is |
| 1531 | | similar to the one used for the SuperSpace cartridge, with the exception |
| 1532 | | that we are using ROM only, and we can have up to 8 pages. |
| 1533 | | |
| 1534 | | Bank Value written to CRU>0800 |
| 1535 | | 0 >0002 = 0000 0000 0000 0010 |
| 1536 | | 1 >0008 = 0000 0000 0000 1000 |
| 1537 | | 2 >0020 = 0000 0000 0010 0000 |
| 1538 | | 3 >0080 = 0000 0000 1000 0000 |
| 1539 | | 4 >0200 = 0000 0010 0000 0000 |
| 1540 | | 5 >0800 = 0000 1000 0000 0000 |
| 1541 | | 6 >2000 = 0010 0000 0000 0000 |
| 1542 | | 7 >8000 = 1000 0000 0000 0000 |
| 1543 | | |
| 1544 | | ******************************************************************************/ |
| 1545 | | |
| 1546 | | static DEVICE_START(ti99_pcb_pagedcru) |
| 1547 | | { |
| 1548 | | /* device is ti99_cartslot:cartridge:pcb */ |
| 1549 | | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 1550 | | } |
| 1551 | | |
| 1552 | | /* |
| 1553 | | Read handler for the CPU address space of the cartridge |
| 1554 | | Images for this area are found in the rom_sockets. |
| 1555 | | */ |
| 1556 | | static READ16_DEVICE_HANDLER( read_cart_pagedcru ) |
| 1557 | | { |
| 1558 | | /* device is pcb, owner is cartslot */ |
| 1559 | | UINT16 value; |
| 1560 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1561 | | cartridge_t *cartridge = pcb->cartridge; |
| 1562 | | offs_t boffset; |
| 1563 | | |
| 1564 | | if (cartridge->rom_ptr==NULL) |
| 1565 | | { |
| 1566 | | value = 0; |
| 1567 | | } |
| 1568 | | else |
| 1569 | | { |
| 1570 | | // rom_page is the stored latch value |
| 1571 | | // 16bit access -> multiply by 0x1000 |
| 1572 | | boffset = cartridge->rom_page * 0x1000 + offset; |
| 1573 | | value = ((UINT16 *)cartridge->rom_ptr)[boffset]; |
| 1574 | | } |
| 1575 | | return value; |
| 1576 | | } |
| 1577 | | |
| 1578 | | /* |
| 1579 | | This type of cartridge does not support writing to the EPROM address |
| 1580 | | space. |
| 1581 | | */ |
| 1582 | | static WRITE16_DEVICE_HANDLER( write_cart_pagedcru ) |
| 1583 | | { |
| 1584 | | return; |
| 1585 | | } |
| 1586 | | |
| 1587 | | /* |
| 1588 | | TI-99/8 support: |
| 1589 | | Read handler for the CPU address space of the cartridge. |
| 1590 | | Images for this area are found in the rom_sockets. |
| 1591 | | */ |
| 1592 | | static READ8_DEVICE_HANDLER( read_cart_pagedcru8 ) |
| 1593 | | { |
| 1594 | | UINT8 value; |
| 1595 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1596 | | cartridge_t *cartridge = pcb->cartridge; |
| 1597 | | offs_t boffset; |
| 1598 | | |
| 1599 | | if (cartridge->rom_ptr==NULL) |
| 1600 | | { |
| 1601 | | value = 0; |
| 1602 | | } |
| 1603 | | else |
| 1604 | | { |
| 1605 | | // rom_page is the stored latch value |
| 1606 | | // 8bit access -> multiply by 0x2000 |
| 1607 | | boffset = cartridge->rom_page * 0x2000 + offset; |
| 1608 | | value = ((UINT8 *)cartridge->rom_ptr)[boffset]; |
| 1609 | | } |
| 1610 | | return value; |
| 1611 | | } |
| 1612 | | |
| 1613 | | /* |
| 1614 | | TI-99/8 support: This type of cartridge does not support writing to the |
| 1615 | | EPROM address space. |
| 1616 | | */ |
| 1617 | | static WRITE8_DEVICE_HANDLER( write_cart_pagedcru8 ) |
| 1618 | | { |
| 1619 | | return; |
| 1620 | | } |
| 1621 | | |
| 1622 | | /* |
| 1623 | | The CRU read handler. The CRU is a serial interface in the console. |
| 1624 | | Using the CRU we can switch the banks in the SuperSpace cartridge. |
| 1625 | | For documentation see the corresponding cru function above. Note we are |
| 1626 | | using rom_page here. |
| 1627 | | */ |
| 1628 | | static READ8_DEVICE_HANDLER( read_cart_cru_paged ) |
| 1629 | | { |
| 1630 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1631 | | cartridge_t *cartridge = pcb->cartridge; |
| 1632 | | int reply = 0; |
| 1633 | | |
| 1634 | | if ((offset & 1) == 0 || offset > 15) |
| 1635 | | reply = 0; |
| 1636 | | |
| 1637 | | reply = (cartridge->rom_page == (offset-1)/2); |
| 1638 | | |
| 1639 | | return reply; |
| 1640 | | } |
| 1641 | | |
| 1642 | | static WRITE8_DEVICE_HANDLER( write_cart_cru_paged ) |
| 1643 | | { |
| 1644 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 1645 | | cartridge_t *cartridge = pcb->cartridge; |
| 1646 | | // data is bit |
| 1647 | | // offset is address |
| 1648 | | |
| 1649 | | //Note that CRU >0F00 also gets in here ... should check |
| 1650 | | // whether it is intended to go somewhere else |
| 1651 | | if (offset < 16) |
| 1652 | | { |
| 1653 | | if (data != 0) |
| 1654 | | { |
| 1655 | | cartridge->rom_page = (offset-1)/2; |
| 1656 | | } |
| 1657 | | } |
| 1658 | | } |
| 1659 | | |
| 1660 | | /* |
| 1661 | | Pagedcru modules have one EPROM dump. |
| 1662 | | */ |
| 1663 | | static int assemble_pagedcru(running_device *image) |
| 1664 | | { |
| 1665 | | cartridge_t *cart; |
| 1666 | | |
| 1667 | | cart = assemble_common(image); |
| 1668 | | if (cart->rom_ptr==NULL) |
| 1669 | | { |
| 1670 | | logerror("Missing ROM for pagedcru cartridge"); |
| 1671 | | return IMAGE_INIT_FAIL; |
| 1672 | | } |
| 1673 | | cart->rom_page = 0; |
| 1674 | | return IMAGE_INIT_PASS; |
| 1675 | | } |
| 1676 | | |
| 1677 | | /***************************************************************************** |
| 1678 | | Cartridge type: gramkracker |
| 1679 | | ******************************************************************************/ |
| 1680 | | |
| 1681 | | static DEVICE_START(ti99_pcb_gramkracker) |
| 1682 | | { |
| 1683 | | /* device is ti99_cartslot:cartridge:pcb */ |
| 1684 | | // printf("DEVICE_START(ti99_pcb_pagedcru), tag of device=%s\n", device->tag()); |
| 1685 | | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 1686 | | } |
| 1687 | | |
| 1688 | | /* |
| 1689 | | The GRAM Kracker contains one GROM and several buffered RAM chips |
| 1690 | | */ |
| 1691 | | static int assemble_gramkracker(running_device *image) |
| 1692 | | { |
| 1693 | | cartridge_t *cart; |
| 1694 | | int id,i; |
| 1695 | | |
| 1696 | | running_device *cartsys = image->owner(); |
| 1697 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 1698 | | |
| 1699 | | cart = assemble_common(image); |
| 1700 | | |
| 1701 | | if (cart->grom_ptr==NULL) |
| 1702 | | { |
| 1703 | | logerror("Missing loader GROM for GRAM Kracker system"); |
| 1704 | | return IMAGE_INIT_FAIL; |
| 1705 | | } |
| 1706 | | |
| 1707 | | if (cart->ram_size < 81920) |
| 1708 | | { |
| 1709 | | logerror("Missing or insufficient RAM for GRAM Kracker system"); |
| 1710 | | return IMAGE_INIT_FAIL; |
| 1711 | | } |
| 1712 | | |
| 1713 | | // Caution: We are using the ram_socket for both grom and direct access. |
| 1714 | | // So we get the GROM (first 64 KiB) contents swapped. Need to undo. |
| 1715 | | if (!ti99_is_99_8()) |
| 1716 | | { |
| 1717 | | /* Undo the bigendianization for the GROM area. */ |
| 1718 | | UINT16 *cont16b = (UINT16 *)cart->ram_ptr; |
| 1719 | | for (i = 0; i < 32768; i++) |
| 1720 | | cont16b[i] = BIG_ENDIANIZE_INT16(cont16b[i]); |
| 1721 | | } |
| 1722 | | |
| 1723 | | // Get slot number |
| 1724 | | id = get_index_from_tagname(image); |
| 1725 | | cartslots->gk_slot = id-1; |
| 1726 | | cartslots->gk_guest_slot = -1; |
| 1727 | | // The first cartridge that is still plugged in is the "guest" of the |
| 1728 | | // GRAM Kracker |
| 1729 | | for (i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) |
| 1730 | | { |
| 1731 | | if (!slot_is_empty(cartsys, i) && (i != cartslots->gk_slot)) |
| 1732 | | { |
| 1733 | | cartslots->gk_guest_slot = i; |
| 1734 | | break; |
| 1735 | | } |
| 1736 | | } |
| 1737 | | return IMAGE_INIT_PASS; |
| 1738 | | } |
| 1739 | | |
| 1740 | | /* |
| 1741 | | Unplugs the GRAM Kracker. The "guest" is still plugged in, but will be |
| 1742 | | treated as normal. |
| 1743 | | */ |
| 1744 | | static int disassemble_gramkracker(running_device *cartslot) |
| 1745 | | { |
| 1746 | | running_device *cartsys = cartslot->owner(); |
| 1747 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 1748 | | int i; |
| 1749 | | |
| 1750 | | int slotnumber = get_index_from_tagname(cartslot)-1; |
| 1751 | | assert(slotnumber>=0 && slotnumber<NUMBER_OF_CARTRIDGE_SLOTS); |
| 1752 | | cartridge_t *cart = &cartslots->cartridge[slotnumber]; |
| 1753 | | |
| 1754 | | // We'll undo the undo of the bigendianization for saving (only for |
| 1755 | | // the GROM part) |
| 1756 | | if (!ti99_is_99_8()) |
| 1757 | | { |
| 1758 | | /* Undo the bigendianization for the GROM area. */ |
| 1759 | | UINT16 *cont16b = (UINT16 *)cart->ram_ptr; |
| 1760 | | for (i = 0; i < 32768; i++) |
| 1761 | | cont16b[i] = BIG_ENDIANIZE_INT16(cont16b[i]); |
| 1762 | | } |
| 1763 | | |
| 1764 | | cartslots->gk_slot = -1; |
| 1765 | | cartslots->gk_guest_slot = -1; |
| 1766 | | |
| 1767 | | return disassemble_std(cartslot); |
| 1768 | | } |
| 1769 | | |
| 1770 | | |
| 1771 | | /***************************************************************************** |
| 1772 | | Device metadata |
| 1773 | | ******************************************************************************/ |
| 1774 | | |
| 1775 | | static DEVICE_GET_INFO(ti99_cart_common) |
| 1776 | | { |
| 1777 | | switch(state) |
| 1778 | | { |
| 1779 | | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 1780 | | case DEVINFO_INT_TOKEN_BYTES: |
| 1781 | | info->i = sizeof(ti99_pcb_t); |
| 1782 | | break; |
| 1783 | | case DEVINFO_INT_INLINE_CONFIG_BYTES: |
| 1784 | | info->i = 0; |
| 1785 | | break; |
| 1786 | | /* --- the following bits of info are returned as pointers to functions --- */ |
| 1787 | | case DEVINFO_FCT_START: |
| 1788 | | info->start = DEVICE_START_NAME(ti99_pcb_std); |
| 1789 | | break; |
| 1790 | | case DEVINFO_FCT_STOP: |
| 1791 | | /* Nothing */ |
| 1792 | | break; |
| 1793 | | case DEVINFO_FCT_RESET: |
| 1794 | | /* Nothing */ |
| 1795 | | break; |
| 1796 | | case TI99CARTINFO_FCT_6000_R: |
| 1797 | | info->f = (genf *) read_cart_std; break; |
| 1798 | | |
| 1799 | | case TI99CARTINFO_FCT_6000_W: |
| 1800 | | info->f = (genf *) write_cart_std; break; |
| 1801 | | |
| 1802 | | case TI99CARTINFO_FCT_6000_R8: |
| 1803 | | info->f = (genf *) read_cart_std8; break; |
| 1804 | | |
| 1805 | | case TI99CARTINFO_FCT_6000_W8: |
| 1806 | | info->f = (genf *) write_cart_std8; break; |
| 1807 | | |
| 1808 | | case TI99CARTINFO_FCT_CRU_R: |
| 1809 | | /* Nothing */ |
| 1810 | | break; |
| 1811 | | |
| 1812 | | case TI99CARTINFO_FCT_CRU_W: |
| 1813 | | /* Nothing */ |
| 1814 | | break; |
| 1815 | | |
| 1816 | | case TI99CART_FCT_ASSM: |
| 1817 | | info->f = (genf *) assemble_std; |
| 1818 | | break; |
| 1819 | | |
| 1820 | | case TI99CART_FCT_DISASSM: |
| 1821 | | info->f = (genf *) disassemble_std; |
| 1822 | | break; |
| 1823 | | |
| 1824 | | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 1825 | | case DEVINFO_STR_NAME: |
| 1826 | | strcpy(info->s, "TI99 standard cartridge pcb"); |
| 1827 | | break; |
| 1828 | | case DEVINFO_STR_FAMILY: |
| 1829 | | strcpy(info->s, "TI99 cartridge pcb"); |
| 1830 | | break; |
| 1831 | | case DEVINFO_STR_VERSION: |
| 1832 | | strcpy(info->s, "1.0"); |
| 1833 | | break; |
| 1834 | | case DEVINFO_STR_SOURCE_FILE: |
| 1835 | | strcpy(info->s, __FILE__); |
| 1836 | | break; |
| 1837 | | case DEVINFO_STR_CREDITS: |
| 1838 | | /* Nothing */ |
| 1839 | | break; |
| 1840 | | } |
| 1841 | | } |
| 1842 | | |
| 1843 | | DEVICE_GET_INFO(ti99_cartridge_pcb_none) |
| 1844 | | { |
| 1845 | | switch(state) |
| 1846 | | { |
| 1847 | | case DEVINFO_FCT_START: |
| 1848 | | info->start = DEVICE_START_NAME(ti99_pcb_none); |
| 1849 | | break; |
| 1850 | | |
| 1851 | | case DEVINFO_STR_NAME: |
| 1852 | | strcpy(info->s, "TI99 empty cartridge"); |
| 1853 | | break; |
| 1854 | | |
| 1855 | | default: |
| 1856 | | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1857 | | break; |
| 1858 | | } |
| 1859 | | } |
| 1860 | | |
| 1861 | | DEVICE_GET_INFO(ti99_cartridge_pcb_std) |
| 1862 | | { |
| 1863 | | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1864 | | } |
| 1865 | | |
| 1866 | | DEVICE_GET_INFO(ti99_cartridge_pcb_paged) |
| 1867 | | { |
| 1868 | | switch(state) |
| 1869 | | { |
| 1870 | | case DEVINFO_FCT_START: |
| 1871 | | info->start = DEVICE_START_NAME(ti99_pcb_paged); |
| 1872 | | break; |
| 1873 | | |
| 1874 | | case TI99CART_FCT_ASSM: |
| 1875 | | info->f = (genf *) assemble_paged; break; |
| 1876 | | |
| 1877 | | case TI99CARTINFO_FCT_6000_R: |
| 1878 | | info->f = (genf *) read_cart_paged; |
| 1879 | | break; |
| 1880 | | |
| 1881 | | case TI99CARTINFO_FCT_6000_W: |
| 1882 | | info->f = (genf *) write_cart_paged; |
| 1883 | | break; |
| 1884 | | |
| 1885 | | case TI99CARTINFO_FCT_6000_R8: |
| 1886 | | info->f = (genf *) read_cart_paged8; |
| 1887 | | break; |
| 1888 | | |
| 1889 | | case TI99CARTINFO_FCT_6000_W8: |
| 1890 | | info->f = (genf *) write_cart_paged8; |
| 1891 | | break; |
| 1892 | | |
| 1893 | | case DEVINFO_STR_NAME: |
| 1894 | | strcpy(info->s, "TI99 paged cartridge pcb"); |
| 1895 | | break; |
| 1896 | | |
| 1897 | | default: |
| 1898 | | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1899 | | break; |
| 1900 | | } |
| 1901 | | } |
| 1902 | | |
| 1903 | | DEVICE_GET_INFO(ti99_cartridge_pcb_minimem) |
| 1904 | | { |
| 1905 | | switch(state) |
| 1906 | | { |
| 1907 | | case DEVINFO_FCT_START: |
| 1908 | | info->start = DEVICE_START_NAME(ti99_pcb_minimem); |
| 1909 | | break; |
| 1910 | | |
| 1911 | | case TI99CART_FCT_ASSM: |
| 1912 | | info->f = (genf *) assemble_minimem; |
| 1913 | | break; |
| 1914 | | case TI99CARTINFO_FCT_6000_R: |
| 1915 | | info->f = (genf *) read_cart_minimem; |
| 1916 | | break; |
| 1917 | | case TI99CARTINFO_FCT_6000_W: |
| 1918 | | info->f = (genf *) write_cart_minimem; |
| 1919 | | break; |
| 1920 | | case TI99CARTINFO_FCT_6000_R8: |
| 1921 | | info->f = (genf *) read_cart_minimem8; |
| 1922 | | break; |
| 1923 | | case TI99CARTINFO_FCT_6000_W8: |
| 1924 | | info->f = (genf *) write_cart_minimem8; |
| 1925 | | break; |
| 1926 | | |
| 1927 | | case DEVINFO_STR_NAME: |
| 1928 | | strcpy(info->s, "TI99 MiniMemory cartridge pcb"); |
| 1929 | | break; |
| 1930 | | |
| 1931 | | default: |
| 1932 | | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1933 | | break; |
| 1934 | | } |
| 1935 | | } |
| 1936 | | |
| 1937 | | DEVICE_GET_INFO(ti99_cartridge_pcb_super) |
| 1938 | | { |
| 1939 | | switch(state) |
| 1940 | | { |
| 1941 | | case DEVINFO_FCT_START: |
| 1942 | | info->start = DEVICE_START_NAME(ti99_pcb_super); |
| 1943 | | break; |
| 1944 | | |
| 1945 | | case TI99CART_FCT_ASSM: |
| 1946 | | info->f = (genf *) assemble_super; |
| 1947 | | break; |
| 1948 | | case TI99CARTINFO_FCT_6000_R: |
| 1949 | | info->f = (genf *) read_cart_super; |
| 1950 | | break; |
| 1951 | | case TI99CARTINFO_FCT_6000_W: |
| 1952 | | info->f = (genf *) write_cart_super; |
| 1953 | | break; |
| 1954 | | case TI99CARTINFO_FCT_6000_R8: |
| 1955 | | info->f = (genf *) read_cart_super8; |
| 1956 | | break; |
| 1957 | | case TI99CARTINFO_FCT_6000_W8: |
| 1958 | | info->f = (genf *) write_cart_super8; |
| 1959 | | break; |
| 1960 | | case TI99CARTINFO_FCT_CRU_R: |
| 1961 | | info->f = (genf *) read_cart_cru; |
| 1962 | | break; |
| 1963 | | case TI99CARTINFO_FCT_CRU_W: |
| 1964 | | info->f = (genf *) write_cart_cru; |
| 1965 | | break; |
| 1966 | | |
| 1967 | | case DEVINFO_STR_NAME: |
| 1968 | | strcpy(info->s, "TI99 SuperSpace cartridge pcb"); |
| 1969 | | break; |
| 1970 | | |
| 1971 | | default: |
| 1972 | | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1973 | | break; |
| 1974 | | } |
| 1975 | | } |
| 1976 | | |
| 1977 | | DEVICE_GET_INFO(ti99_cartridge_pcb_mbx) |
| 1978 | | { |
| 1979 | | switch(state) |
| 1980 | | { |
| 1981 | | case DEVINFO_FCT_START: |
| 1982 | | info->start = DEVICE_START_NAME(ti99_pcb_mbx); |
| 1983 | | break; |
| 1984 | | |
| 1985 | | case TI99CART_FCT_ASSM: |
| 1986 | | info->f = (genf *) assemble_mbx; |
| 1987 | | break; |
| 1988 | | case TI99CARTINFO_FCT_6000_R: |
| 1989 | | info->f = (genf *) read_cart_mbx; |
| 1990 | | break; |
| 1991 | | case TI99CARTINFO_FCT_6000_W: |
| 1992 | | info->f = (genf *) write_cart_mbx; |
| 1993 | | break; |
| 1994 | | case TI99CARTINFO_FCT_6000_R8: |
| 1995 | | info->f = (genf *) read_cart_mbx8; |
| 1996 | | break; |
| 1997 | | case TI99CARTINFO_FCT_6000_W8: |
| 1998 | | info->f = (genf *) write_cart_mbx8; |
| 1999 | | break; |
| 2000 | | |
| 2001 | | |
| 2002 | | case DEVINFO_STR_NAME: |
| 2003 | | strcpy(info->s, "TI99 MBX cartridge pcb"); |
| 2004 | | break; |
| 2005 | | |
| 2006 | | default: |
| 2007 | | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 2008 | | break; |
| 2009 | | } |
| 2010 | | } |
| 2011 | | |
| 2012 | | DEVICE_GET_INFO(ti99_cartridge_pcb_paged379i) |
| 2013 | | { |
| 2014 | | switch(state) |
| 2015 | | { |
| 2016 | | case DEVINFO_FCT_START: |
| 2017 | | info->start = DEVICE_START_NAME(ti99_pcb_paged379i); |
| 2018 | | break; |
| 2019 | | |
| 2020 | | case TI99CART_FCT_ASSM: |
| 2021 | | info->f = (genf *) assemble_paged379i; break; |
| 2022 | | |
| 2023 | | case TI99CARTINFO_FCT_6000_R: |
| 2024 | | info->f = (genf *) read_cart_paged379i; |
| 2025 | | break; |
| 2026 | | |
| 2027 | | case TI99CARTINFO_FCT_6000_W: |
| 2028 | | info->f = (genf *) write_cart_paged379i; |
| 2029 | | break; |
| 2030 | | |
| 2031 | | case TI99CARTINFO_FCT_6000_R8: |
| 2032 | | info->f = (genf *) read_cart_paged379i8; |
| 2033 | | break; |
| 2034 | | |
| 2035 | | case TI99CARTINFO_FCT_6000_W8: |
| 2036 | | info->f = (genf *) write_cart_paged379i8; |
| 2037 | | break; |
| 2038 | | |
| 2039 | | case DEVINFO_STR_NAME: |
| 2040 | | strcpy(info->s, "TI99 paged379i cartridge pcb"); |
| 2041 | | break; |
| 2042 | | |
| 2043 | | default: |
| 2044 | | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 2045 | | break; |
| 2046 | | } |
| 2047 | | } |
| 2048 | | |
| 2049 | | DEVICE_GET_INFO(ti99_cartridge_pcb_pagedcru) |
| 2050 | | { |
| 2051 | | switch(state) |
| 2052 | | { |
| 2053 | | case DEVINFO_FCT_START: |
| 2054 | | info->start = DEVICE_START_NAME(ti99_pcb_pagedcru); |
| 2055 | | break; |
| 2056 | | |
| 2057 | | case TI99CART_FCT_ASSM: |
| 2058 | | info->f = (genf *) assemble_pagedcru; |
| 2059 | | break; |
| 2060 | | |
| 2061 | | case TI99CARTINFO_FCT_6000_R: |
| 2062 | | info->f = (genf *) read_cart_pagedcru; |
| 2063 | | break; |
| 2064 | | |
| 2065 | | case TI99CARTINFO_FCT_6000_W: |
| 2066 | | info->f = (genf *) write_cart_pagedcru; |
| 2067 | | break; |
| 2068 | | |
| 2069 | | case TI99CARTINFO_FCT_6000_R8: |
| 2070 | | info->f = (genf *) read_cart_pagedcru8; |
| 2071 | | break; |
| 2072 | | |
| 2073 | | case TI99CARTINFO_FCT_6000_W8: |
| 2074 | | info->f = (genf *) write_cart_pagedcru8; |
| 2075 | | break; |
| 2076 | | |
| 2077 | | case TI99CARTINFO_FCT_CRU_R: |
| 2078 | | info->f = (genf *) read_cart_cru_paged; |
| 2079 | | break; |
| 2080 | | case TI99CARTINFO_FCT_CRU_W: |
| 2081 | | info->f = (genf *) write_cart_cru_paged; |
| 2082 | | break; |
| 2083 | | |
| 2084 | | case DEVINFO_STR_NAME: |
| 2085 | | strcpy(info->s, "TI99 pagedcru cartridge pcb"); |
| 2086 | | break; |
| 2087 | | |
| 2088 | | default: |
| 2089 | | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 2090 | | break; |
| 2091 | | } |
| 2092 | | } |
| 2093 | | |
| 2094 | | DEVICE_GET_INFO(ti99_cartridge_pcb_gramkracker) |
| 2095 | | { |
| 2096 | | switch(state) |
| 2097 | | { |
| 2098 | | case DEVINFO_FCT_START: |
| 2099 | | info->start = DEVICE_START_NAME(ti99_pcb_gramkracker); |
| 2100 | | break; |
| 2101 | | |
| 2102 | | case TI99CART_FCT_ASSM: |
| 2103 | | info->f = (genf *) assemble_gramkracker; |
| 2104 | | break; |
| 2105 | | |
| 2106 | | case TI99CART_FCT_DISASSM: |
| 2107 | | info->f = (genf *) disassemble_gramkracker; |
| 2108 | | break; |
| 2109 | | |
| 2110 | | case DEVINFO_STR_NAME: |
| 2111 | | strcpy(info->s, "TI99 GRAM Kracker system"); |
| 2112 | | break; |
| 2113 | | |
| 2114 | | default: |
| 2115 | | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 2116 | | break; |
| 2117 | | } |
| 2118 | | } |
| 2119 | | |
| 2120 | | |
| 2121 | | /***************************************************************************** |
| 2122 | | The cartridge handling of the multi-cartridge system. |
| 2123 | | Every cartridge contains a PCB device. The memory handlers delegate the calls |
| 2124 | | to the respective handlers of the cartridges. |
| 2125 | | ******************************************************************************/ |
| 2126 | | /* |
| 2127 | | Initialize a cartridge. Each cartridge contains a PCB device. |
| 2128 | | */ |
| 2129 | | static DEVICE_START( ti99_cartridge ) |
| 2130 | | { |
| 2131 | | cartslot_t *cart = (cartslot_t *)downcast<legacy_device_base *>(device)->token(); |
| 2132 | | |
| 2133 | | /* find the PCB device */ |
| 2134 | | cart->pcb_device = device->subdevice(TAG_PCB); |
| 2135 | | } |
| 2136 | | |
| 2137 | | /* |
| 2138 | | Load the cartridge image files. Apart from reading, we set pointers |
| 2139 | | to the image files so that during runtime we do not need search |
| 2140 | | operations. |
| 2141 | | */ |
| 2142 | | static DEVICE_IMAGE_LOAD( ti99_cartridge ) |
| 2143 | | { |
| 2144 | | running_device *pcbdev = cartslot_get_pcb(&image.device()); |
| 2145 | | running_device *cartsys = image.device().owner(); |
| 2146 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 2147 | | |
| 2148 | | int result; |
| 2149 | | |
| 2150 | | result = load_legacy(image); |
| 2151 | | |
| 2152 | | if (result != IMAGE_INIT_FAIL) |
| 2153 | | cartslots->legacy_slots++; |
| 2154 | | else |
| 2155 | | { |
| 2156 | | if (pcbdev != NULL) |
| 2157 | | { |
| 2158 | | /* If we are here, we have a multicart. */ |
| 2159 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(pcbdev)->token(); |
| 2160 | | cartslot_t *cart = (cartslot_t *)downcast<legacy_device_base *>(&image.device())->token(); |
| 2161 | | |
| 2162 | | /* try opening this as a multicart */ |
| 2163 | | // This line requires that cartslot_t be included in cartslot.h, |
| 2164 | | // otherwise one cannot make use of multicart handling within such a |
| 2165 | | // custom LOAD function. |
| 2166 | | multicart_open_error me = multicart_open(image.filename(), image.device().machine->gamedrv->name, MULTICART_FLAGS_LOAD_RESOURCES, &cart->mc); |
| 2167 | | |
| 2168 | | // Now that we have loaded the image files, let the PCB put them all |
| 2169 | | // together. This means we put the images in a structure which allows |
| 2170 | | // for a quick access by the memory handlers. Every PCB defines an |
| 2171 | | // own assembly method. |
| 2172 | | if (me == MCERR_NONE) |
| 2173 | | result = pcb->assemble(image); |
| 2174 | | else |
| 2175 | | fatalerror("Error loading multicart: %s", multicart_error_text(me)); |
| 2176 | | |
| 2177 | | // This is for legacy support. If we have no multicart left |
| 2178 | | // but there are still legacy dumps, we switch to legacy mode. |
| 2179 | | if (result != IMAGE_INIT_FAIL) |
| 2180 | | cartslots->multi_slots++; |
| 2181 | | } |
| 2182 | | else |
| 2183 | | { |
| 2184 | | fatalerror("Error loading multicart: no pcb found."); |
| 2185 | | } |
| 2186 | | } |
| 2187 | | return result; |
| 2188 | | } |
| 2189 | | |
| 2190 | | /* |
| 2191 | | This is called when the cartridge is unplugged (or the emulator is |
| 2192 | | stopped). |
| 2193 | | */ |
| 2194 | | static DEVICE_IMAGE_UNLOAD( ti99_cartridge ) |
| 2195 | | { |
| 2196 | | running_device *pcbdev; |
| 2197 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(image.device().owner())->token(); |
| 2198 | | |
| 2199 | | if (downcast<legacy_device_base *>(&image.device())->token() == NULL) |
| 2200 | | { |
| 2201 | | // This means something went wrong during the pcb |
| 2202 | | // identification (e.g. one of the cartridge files was not |
| 2203 | | // found). We do not need to (and cannot) unload the cartridge. |
| 2204 | | return; |
| 2205 | | } |
| 2206 | | pcbdev = cartslot_get_pcb(image); |
| 2207 | | |
| 2208 | | if (pcbdev != NULL) |
| 2209 | | { |
| 2210 | | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(pcbdev)->token(); |
| 2211 | | cartslot_t *cart = (cartslot_t *)downcast<legacy_device_base *>(&image.device())->token(); |
| 2212 | | |
| 2213 | | if (cart->mc != NULL) |
| 2214 | | { |
| 2215 | | /* Remove pointers and de-big-endianize RAM contents. */ |
| 2216 | | pcb->disassemble(image); |
| 2217 | | |
| 2218 | | // Close the multicart; all RAM resources will be |
| 2219 | | // written to disk |
| 2220 | | multicart_close(cart->mc); |
| 2221 | | cart->mc = NULL; |
| 2222 | | |
| 2223 | | /* See above: Legacy support. */ |
| 2224 | | cartslots->multi_slots--; |
| 2225 | | } |
| 2226 | | // else |
| 2227 | | // fatalerror("Lost pointer to multicart in cartridge. Report bug."); |
| 2228 | | } |
| 2229 | | else |
| 2230 | | { |
| 2231 | | unload_legacy(image); |
| 2232 | | cartslots->legacy_slots--; |
| 2233 | | } |
| 2234 | | } |
| 2235 | | |
| 2236 | | /***************************************************************************** |
| 2237 | | The legacy cartridge slot system. |
| 2238 | | |
| 2239 | | This is the system which was used for the TI family until MESS 0.130. |
| 2240 | | Here, plain ROM dumps were mounted on the slots, e.g. the GROM on slot 1, |
| 2241 | | ROM on slot 2. No multiple cartridges were supported. |
| 2242 | | |
| 2243 | | This functionality is deprecated. |
| 2244 | | |
| 2245 | | In order to allow for a smooth transition, this feature is included in this |
| 2246 | | version. It is strongly recommmended to create suitable multicart packs. |
| 2247 | | |
| 2248 | | This implementation tolerates both cartridge systems in the following way: |
| 2249 | | - If there are only single ROM dumps, the system switched to legacy mode. |
| 2250 | | - If there is at least one multicart pack in any slot, the system switches |
| 2251 | | to the multicart mode. All plain dumps become invisible; the slots appear |
| 2252 | | empty to the system |
| 2253 | | - Both modes may change on plugging and unplugging. |
| 2254 | | ******************************************************************************/ |
| 2255 | | |
| 2256 | | static UINT8 cartridge_grom_read_legacy(running_device *cartsys) |
| 2257 | | { |
| 2258 | | int slot; |
| 2259 | | cartridge_t *cartridge; |
| 2260 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 2261 | | |
| 2262 | | UINT8 value; |
| 2263 | | |
| 2264 | | /* Get the GROM slot number. */ |
| 2265 | | slot = cartslots->legacy_slotnumber[SLOTC_GROM]; |
| 2266 | | if (slot<0) |
| 2267 | | /* No GROM available. */ |
| 2268 | | return 0; |
| 2269 | | |
| 2270 | | cartridge = &cartslots->cartridge[slot]; |
| 2271 | | |
| 2272 | | if (cartridge->grom_size>0) |
| 2273 | | { |
| 2274 | | // This is the reason why we need the image size: Read |
| 2275 | | // access beyond the image size must be detected. |
| 2276 | | if ((cartslots->grom_address < 0x6000) || (cartslots->grom_address > (cartridge->grom_size+0x6000))) |
| 2277 | | return 0; |
| 2278 | | |
| 2279 | | value = (UINT8)cartslots->grom_buffer; |
| 2280 | | /* read ahead */ |
| 2281 | | cartslots->grom_buffer = cartridge->grom_ptr[cartslots->grom_address-0x6000]; |
| 2282 | | // The program counter wraps at each GROM chip size (8K), |
| 2283 | | // so 0x5fff + 1 = 0x4000. |
| 2284 | | cartslots->grom_address = ((cartslots->grom_address + 1) & 0x1FFF) | (cartslots->grom_address & 0xE000); |
| 2285 | | |
| 2286 | | /* Reset the read and write address flipflops. */ |
| 2287 | | cartslots->raddr_LSB = cartslots->waddr_LSB = FALSE; |
| 2288 | | } |
| 2289 | | else |
| 2290 | | { |
| 2291 | | /* We assume that empty sockets return 0 */ |
| 2292 | | value = 0; |
| 2293 | | logerror("Empty socket at address G(%d)>%04x\n", slot, cartslots->grom_address); |
| 2294 | | } |
| 2295 | | return value; |
| 2296 | | } |
| 2297 | | |
| 2298 | | static READ16_DEVICE_HANDLER( ti99_cart_r_legacy ) |
| 2299 | | { |
| 2300 | | int slotmbx, slotmini, slotebr2, slotrom; |
| 2301 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 2302 | | |
| 2303 | | slotmbx = cartslots->legacy_slotnumber[SLOTC_MBX]; |
| 2304 | | |
| 2305 | | if ((slotmbx != -1) && (offset >= 0x0600) && (offset <= 0x07fe)) |
| 2306 | | { |
| 2307 | | /* We are in the RAM area of MBX cartridges. */ |
| 2308 | | return ((UINT16 *)(cartslots->cartridge[slotmbx].ram_ptr))[offset-0x0600]; |
| 2309 | | } |
| 2310 | | |
| 2311 | | slotmini = cartslots->legacy_slotnumber[SLOTC_MINIMEM]; |
| 2312 | | if ((slotmini != -1) && (offset >= 0x0800) && (offset <= 0x0fff)) |
| 2313 | | { |
| 2314 | | /* We are in the RAM area of the Mini Memory cartridge. */ |
| 2315 | | return ((UINT16 *)(cartslots->cartridge[slotmini].ram_ptr))[offset-0x0800]; |
| 2316 | | } |
| 2317 | | |
| 2318 | | /* Standard */ |
| 2319 | | slotrom = cartslots->legacy_slotnumber[SLOTC_CROM]; |
| 2320 | | if (slotrom < 0) |
| 2321 | | return 0; |
| 2322 | | |
| 2323 | | slotebr2 = cartslots->legacy_slotnumber[SLOTC_DROM]; |
| 2324 | | if (slotebr2 < 0) |
| 2325 | | return (((UINT16 *)cartslots->cartridge[slotrom].rom_ptr)[offset]); |
| 2326 | | else |
| 2327 | | { |
| 2328 | | /* Extended Basic or other paged cartridge */ |
| 2329 | | if (cartslots->cartridge[slotrom].rom_page!=0) |
| 2330 | | return (((UINT16 *)cartslots->cartridge[slotebr2].rom2_ptr)[offset]); |
| 2331 | | else |
| 2332 | | return (((UINT16 *)cartslots->cartridge[slotrom].rom_ptr)[offset]); |
| 2333 | | } |
| 2334 | | |
| 2335 | | /* SuperSpace, paged379i, and pagedcru are not supported in legacy mode. */ |
| 2336 | | } |
| 2337 | | |
| 2338 | | static WRITE16_DEVICE_HANDLER( ti99_cart_w_legacy ) |
| 2339 | | { |
| 2340 | | int slotmbx, slotmini, slotebr2, slotrom; |
| 2341 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 2342 | | |
| 2343 | | slotmbx = cartslots->legacy_slotnumber[SLOTC_MBX]; |
| 2344 | | slotrom = cartslots->legacy_slotnumber[SLOTC_CROM]; |
| 2345 | | slotebr2 = cartslots->legacy_slotnumber[SLOTC_DROM]; |
| 2346 | | slotmini = cartslots->legacy_slotnumber[SLOTC_MINIMEM]; |
| 2347 | | |
| 2348 | | if (slotmbx != -1) |
| 2349 | | { |
| 2350 | | if ((offset >= 0x0600) && (offset <= 0x07fe)) |
| 2351 | | { |
| 2352 | | /* We are in the RAM area of MBX cartridges. */ |
| 2353 | | /* RAM in 0x6c00-0x6ffd (presumably non-paged) */ |
| 2354 | | /* mapper at 0x6ffe */ |
| 2355 | | COMBINE_DATA(((UINT16 *)cartslots->cartridge[slotmbx].ram_ptr) + offset - 0x0600); |
| 2356 | | } |
| 2357 | | else |
| 2358 | | { |
| 2359 | | if ((offset == 0x07ff) && ACCESSING_BITS_8_15) |
| 2360 | | { |
| 2361 | | if (slotebr2 < 0 || slotrom < 0) |
| 2362 | | return; /* just ignore if the cartridge is not paged. */ |
| 2363 | | else |
| 2364 | | cartslots->cartridge[slotrom].rom_page = ((data >> 8) & 1); |
| 2365 | | } |
| 2366 | | } |
| 2367 | | return; |
| 2368 | | } |
| 2369 | | if (slotmini != -1) |
| 2370 | | { |
| 2371 | | if ((offset >= 0x0800) && (offset <= 0x0fff)) |
| 2372 | | /* We are in the RAM area of a MiniMemory cartridge. */ |
| 2373 | | COMBINE_DATA(((UINT16 *)cartslots->cartridge[slotmini].ram_ptr) + offset - 0x0800); |
| 2374 | | return; |
| 2375 | | } |
| 2376 | | if (slotebr2 != -1 && slotrom != -1) |
| 2377 | | { |
| 2378 | | cartslots->cartridge[slotrom].rom_page = (offset & 1); |
| 2379 | | } |
| 2380 | | } |
| 2381 | | |
| 2382 | | /* |
| 2383 | | TI-99/8 support. |
| 2384 | | */ |
| 2385 | | static READ8_DEVICE_HANDLER( ti99_cart_r_legacy8 ) |
| 2386 | | { |
| 2387 | | int slotmbx, slotmini, slotebr2, slotrom; |
| 2388 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 2389 | | |
| 2390 | | slotmbx = cartslots->legacy_slotnumber[SLOTC_MBX]; |
| 2391 | | |
| 2392 | | if ((slotmbx != -1) && (offset >= 0x0c00) && (offset <= 0x0ffd)) |
| 2393 | | { |
| 2394 | | /* We are in the RAM area of MBX cartridges. */ |
| 2395 | | return ((UINT8 *)(cartslots->cartridge[slotmbx].ram_ptr))[offset-0x0c00]; |
| 2396 | | } |
| 2397 | | |
| 2398 | | slotmini = cartslots->legacy_slotnumber[SLOTC_MINIMEM]; |
| 2399 | | if ((slotmini != -1) && (offset >= 0x1000) && (offset <= 0x1fff)) |
| 2400 | | { |
| 2401 | | /* We are in the RAM area of the Mini Memory cartridge. */ |
| 2402 | | return ((UINT8 *)(cartslots->cartridge[slotmini].ram_ptr))[offset-0x1000]; |
| 2403 | | } |
| 2404 | | |
| 2405 | | /* Standard */ |
| 2406 | | slotrom = cartslots->legacy_slotnumber[SLOTC_CROM]; |
| 2407 | | if (slotrom < 0) |
| 2408 | | return 0; |
| 2409 | | |
| 2410 | | slotebr2 = cartslots->legacy_slotnumber[SLOTC_DROM]; |
| 2411 | | if (slotebr2 < 0) |
| 2412 | | return (((UINT8 *)cartslots->cartridge[slotrom].rom_ptr)[offset]); |
| 2413 | | else |
| 2414 | | { |
| 2415 | | /* Extended Basic or other paged cartridge */ |
| 2416 | | if (cartslots->cartridge[slotrom].rom_page!=0) |
| 2417 | | return (((UINT8 *)cartslots->cartridge[slotebr2].rom2_ptr)[offset]); |
| 2418 | | else |
| 2419 | | return (((UINT8 *)cartslots->cartridge[slotrom].rom_ptr)[offset]); |
| 2420 | | } |
| 2421 | | /* Super Space, paged379i, and pagedcru are not supported in legacy mode. */ |
| 2422 | | } |
| 2423 | | |
| 2424 | | /* |
| 2425 | | TI-99/8 support. |
| 2426 | | */ |
| 2427 | | static WRITE8_DEVICE_HANDLER( ti99_cart_w_legacy8 ) |
| 2428 | | { |
| 2429 | | int slotmbx, slotmini, slotebr2, slotrom; |
| 2430 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 2431 | | |
| 2432 | | slotmbx = cartslots->legacy_slotnumber[SLOTC_MBX]; |
| 2433 | | slotrom = cartslots->legacy_slotnumber[SLOTC_CROM]; |
| 2434 | | slotebr2 = cartslots->legacy_slotnumber[SLOTC_DROM]; |
| 2435 | | slotmini = cartslots->legacy_slotnumber[SLOTC_MINIMEM]; |
| 2436 | | |
| 2437 | | if (slotmbx != -1) |
| 2438 | | { |
| 2439 | | if ((offset >= 0x0c00) && (offset <= 0x0ffd)) |
| 2440 | | { |
| 2441 | | /* We are in the RAM area of MBX cartridges. */ |
| 2442 | | /* RAM in 0x6c00-0x6ffd (presumably non-paged) */ |
| 2443 | | /* mapper at 0x6ffe */ |
| 2444 | | ((UINT8 *)cartslots->cartridge[slotmbx].ram_ptr)[offset - 0x0c00] = data; |
| 2445 | | } |
| 2446 | | else |
| 2447 | | { |
| 2448 | | if (offset == 0x0ffe || offset == 0x0fff) |
| 2449 | | { |
| 2450 | | if (slotebr2 < 0 || slotrom < 0) |
| 2451 | | return; /* just ignore if the cartridge is not paged. */ |
| 2452 | | else |
| 2453 | | cartslots->cartridge[slotrom].rom_page = ((data >> 7) & 1); |
| 2454 | | } |
| 2455 | | } |
| 2456 | | return; |
| 2457 | | } |
| 2458 | | if (slotmini != -1) |
| 2459 | | { |
| 2460 | | if ((offset >= 0x1000) && (offset <= 0x1fff)) |
| 2461 | | /* We are in the RAM area of a MiniMemory cartridge. */ |
| 2462 | | ((UINT8 *)cartslots->cartridge[slotmini].ram_ptr)[offset - 0x1000] = data; |
| 2463 | | return; |
| 2464 | | } |
| 2465 | | if (slotebr2 != -1 && slotrom != -1) |
| 2466 | | { |
| 2467 | | cartslots->cartridge[slotrom].rom_page = (offset & 1); |
| 2468 | | } |
| 2469 | | } |
| 2470 | | |
| 2471 | | |
| 2472 | | static int load_legacy(device_image_interface &image) |
| 2473 | | { |
| 2474 | | running_device *cartsys = image.device().owner(); |
| 2475 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 2476 | | |
| 2477 | | /* Some comments originally in machine/ti99_4x.c */ |
| 2478 | | |
| 2479 | | /* We identify file types according to their extension */ |
| 2480 | | // Note that if we do not recognize the extension, we revert to the |
| 2481 | | // slot location <-> type scheme. |
| 2482 | | int id = 0; |
| 2483 | | int i; |
| 2484 | | int filesize; |
| 2485 | | int maxrom = 0x2000; |
| 2486 | | slotc_type_t type; |
| 2487 | | int savembx; |
| 2488 | | |
| 2489 | | const char *ch, *ch2; |
| 2490 | | const char *name = image.filename(); |
| 2491 | | |
| 2492 | | UINT8 *grom = memory_region(image.device().machine, region_grom) + 0x6000; |
| 2493 | | UINT16 *cartrom = (UINT16 *) (memory_region(image.device().machine, "maincpu") + offset_cart); |
| 2494 | | UINT16 *cartrom2 = (UINT16 *) (memory_region(image.device().machine, "maincpu") + offset_cart + 0x2000); |
| 2495 | | UINT8 *cart8rom = memory_region(image.device().machine, "maincpu") + offset_cart_8; |
| 2496 | | UINT8 *cart8rom2 = memory_region(image.device().machine, "maincpu") + offset_cart_8 + 0x2000; |
| 2497 | | |
| 2498 | | UINT16 *cartmbx; |
| 2499 | | UINT8 *cartmbx8; |
| 2500 | | |
| 2501 | | type = SLOTC_EMPTY; |
| 2502 | | |
| 2503 | | ch = strrchr(name, '.'); |
| 2504 | | ch2 = (ch-1 >= name) ? ch-1 : ""; |
| 2505 | | |
| 2506 | | if (ch) |
| 2507 | | { |
| 2508 | | if (! mame_stricmp(ch2, "g.bin")) |
| 2509 | | { |
| 2510 | | /* grom */ |
| 2511 | | type = SLOTC_GROM; |
| 2512 | | } |
| 2513 | | else if (! mame_stricmp(ch2, "c.bin")) |
| 2514 | | { |
| 2515 | | /* rom first page */ |
| 2516 | | type = SLOTC_CROM; |
| 2517 | | } |
| 2518 | | else if (! mame_stricmp(ch2, "d.bin")) |
| 2519 | | { |
| 2520 | | /* rom second page */ |
| 2521 | | type = SLOTC_DROM; |
| 2522 | | } |
| 2523 | | else if (! mame_stricmp(ch2, "m.bin")) |
| 2524 | | { |
| 2525 | | /* rom minimemory */ |
| 2526 | | type = SLOTC_MINIMEM; |
| 2527 | | } |
| 2528 | | else if (! mame_stricmp(ch2, "b.bin")) |
| 2529 | | { |
| 2530 | | /* rom MBX */ |
| 2531 | | type = SLOTC_MBX; |
| 2532 | | } |
| 2533 | | } |
| 2534 | | |
| 2535 | | if (type == SLOTC_EMPTY) |
| 2536 | | { |
| 2537 | | return IMAGE_INIT_FAIL; |
| 2538 | | } |
| 2539 | | |
| 2540 | | id = get_index_from_tagname(image)-1; |
| 2541 | | |
| 2542 | | /* Remember where the cartridge part is plugged in. */ |
| 2543 | | cartslots->legacy_slotnumber[type] = id; |
| 2544 | | |
| 2545 | | savembx = FALSE; |
| 2546 | | |
| 2547 | | // We will use the already allocated buffers in the memory regions, |
| 2548 | | // and the pointers in the cartridge structure will point to them. |
| 2549 | | switch (type) |
| 2550 | | { |
| 2551 | | case SLOTC_EMPTY: |
| 2552 | | break; |
| 2553 | | |
| 2554 | | case SLOTC_GROM: |
| 2555 | | filesize = image.fread(grom, 0xA000); |
| 2556 | | cartslots->cartridge[id].grom_ptr = grom; |
| 2557 | | cartslots->cartridge[id].grom_size = filesize; |
| 2558 | | // printf("loaded GROM, size = %d\n", filesize); |
| 2559 | | break; |
| 2560 | | |
| 2561 | | case SLOTC_CROM: |
| 2562 | | maxrom = 0x2000; |
| 2563 | | if (cartslots->legacy_slotnumber[SLOTC_MINIMEM] != -1) |
| 2564 | | { |
| 2565 | | // This means the NVRAM has already been loaded. |
| 2566 | | // So we just load 4K of ROM to avoid |
| 2567 | | // overwriting the NVRAM. |
| 2568 | | maxrom = 0x1000; |
| 2569 | | } |
| 2570 | | |
| 2571 | | if (cartslots->legacy_slotnumber[SLOTC_MBX] != -1) |
| 2572 | | { |
| 2573 | | // This means the NVRAM has already been loaded. |
| 2574 | | // We need to temporarily save the RAM contents |
| 2575 | | // and write them back because the RAM overrides |
| 2576 | | // the ROM in an intermediate area. |
| 2577 | | savembx = TRUE; |
| 2578 | | } |
| 2579 | | |
| 2580 | | if (ti99_is_99_8()) |
| 2581 | | { |
| 2582 | | // printf("** is 99/8\n"); |
| 2583 | | if (savembx==TRUE) |
| 2584 | | { |
| 2585 | | cartmbx8 = (UINT8*)malloc(0x400); |
| 2586 | | memcpy(cartmbx8, cart8rom + 0x0c00, 0x400); |
| 2587 | | filesize = image.fread(cart8rom, maxrom); |
| 2588 | | memcpy(cart8rom + 0xc00, cartmbx8, 0x400); |
| 2589 | | free(cartmbx8); |
| 2590 | | } |
| 2591 | | else |
| 2592 | | { |
| 2593 | | filesize = image.fread(cart8rom, maxrom); |
| 2594 | | } |
| 2595 | | cartslots->cartridge[id].rom_ptr = cart8rom; |
| 2596 | | cartslots->cartridge[id].rom_size = filesize; |
| 2597 | | } |
| 2598 | | else |
| 2599 | | { |
| 2600 | | if (savembx==TRUE) |
| 2601 | | { |
| 2602 | | cartmbx = (UINT16*)malloc(0x400); |
| 2603 | | memcpy(cartmbx, cartrom + 0x0600, 0x400); |
| 2604 | | filesize = image.fread(cartrom, maxrom); |
| 2605 | | memcpy(cartrom + 0x0600, cartmbx, 0x400); |
| 2606 | | free(cartmbx); |
| 2607 | | } |
| 2608 | | filesize = image.fread(cartrom, maxrom); |
| 2609 | | for (i = 0; i < maxrom/2; i++) |
| 2610 | | ((UINT16 *)cartrom)[i] = BIG_ENDIANIZE_INT16(((UINT16 *)cartrom)[i]); |
| 2611 | | cartslots->cartridge[id].rom_ptr = cartrom; |
| 2612 | | cartslots->cartridge[id].rom_size = filesize; |
| 2613 | | } |
| 2614 | | break; |
| 2615 | | |
| 2616 | | case SLOTC_MINIMEM: |
| 2617 | | // Load the NVRAM. Need to BIG_ENDIANIZE it. |
| 2618 | | // MiniMemory has only one cartridge page. |
| 2619 | | if (ti99_is_99_8()) |
| 2620 | | { |
| 2621 | | // printf("** is 99/8\n"); |
| 2622 | | image.battery_load(cart8rom+0x1000,0x1000, 0x00); |
| 2623 | | cartslots->cartridge[id].ram_ptr = cart8rom + 0x1000; |
| 2624 | | } |
| 2625 | | else |
| 2626 | | { |
| 2627 | | image.battery_load(cartrom+0x800,0x1000, 0x00); |
| 2628 | | for (i = 0x800; i < 0x1000; i++) |
| 2629 | | cartrom[i] = BIG_ENDIANIZE_INT16(cartrom[i]); |
| 2630 | | cartslots->cartridge[id].ram_ptr = (UINT16 *)cartrom + 0x800; |
| 2631 | | } |
| 2632 | | cartslots->cartridge[id].ram_size = 0x1000; |
| 2633 | | break; |
| 2634 | | |
| 2635 | | case SLOTC_MBX: |
| 2636 | | /* Load the NVRAM. Need to BIG_ENDIANIZE it. */ |
| 2637 | | if (ti99_is_99_8()) |
| 2638 | | { |
| 2639 | | // printf("** is 99/8\n"); |
| 2640 | | image.battery_load(cart8rom+0x0c00, 0x0400, 0x00); |
| 2641 | | cartslots->cartridge[id].ram_ptr = cart8rom + 0xc00; |
| 2642 | | } |
| 2643 | | else |
| 2644 | | { |
| 2645 | | image.battery_load(cartrom+0x0600, 0x0400, 0x00); |
| 2646 | | for (i = 0x0600; i < 0x0800; i++) |
| 2647 | | cartrom[i] = BIG_ENDIANIZE_INT16(cartrom[i]); |
| 2648 | | cartslots->cartridge[id].ram_ptr = (UINT16 *)cartrom + 0x600; |
| 2649 | | } |
| 2650 | | cartslots->cartridge[id].ram_size = 0x0400; |
| 2651 | | // printf("loaded NVRAM\n"); |
| 2652 | | break; |
| 2653 | | |
| 2654 | | case SLOTC_DROM: |
| 2655 | | maxrom = 0x2000; |
| 2656 | | if (ti99_is_99_8()) |
| 2657 | | { |
| 2658 | | // printf("** is 99/8\n"); |
| 2659 | | filesize = image.fread(cart8rom2, maxrom); |
| 2660 | | cartslots->cartridge[id].rom2_ptr = cart8rom2; |
| 2661 | | } |
| 2662 | | else |
| 2663 | | { |
| 2664 | | filesize = image.fread(cartrom2, maxrom); |
| 2665 | | for (i = 0; i < maxrom/2; i++) |
| 2666 | | cartrom2[i] = BIG_ENDIANIZE_INT16(cartrom2[i]); |
| 2667 | | cartslots->cartridge[id].rom2_ptr = cartrom2; |
| 2668 | | } |
| 2669 | | cartslots->cartridge[id].rom_size = filesize; |
| 2670 | | break; |
| 2671 | | } |
| 2672 | | return IMAGE_INIT_PASS; |
| 2673 | | } |
| 2674 | | |
| 2675 | | static void unload_legacy(device_image_interface &image) |
| 2676 | | { |
| 2677 | | int i; |
| 2678 | | running_device *cartsys = image.device().owner(); |
| 2679 | | |
| 2680 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 2681 | | |
| 2682 | | slotc_type_t type = SLOTC_EMPTY; |
| 2683 | | int slot = get_index_from_tagname(image)-1; |
| 2684 | | |
| 2685 | | // printf("non-rpk unload\n"); |
| 2686 | | |
| 2687 | | for (i=0; (i < 5) && (type==SLOTC_EMPTY); i++) |
| 2688 | | { |
| 2689 | | if (cartslots->legacy_slotnumber[i]==slot) |
| 2690 | | { |
| 2691 | | cartslots->legacy_slotnumber[i] = -1; |
| 2692 | | type = (slotc_type_t)i; |
| 2693 | | } |
| 2694 | | } |
| 2695 | | |
| 2696 | | switch (type) |
| 2697 | | { |
| 2698 | | case SLOTC_EMPTY: |
| 2699 | | break; |
| 2700 | | |
| 2701 | | case SLOTC_GROM: |
| 2702 | | memset(cartslots->cartridge[slot].grom_ptr, 0, 0xA000); |
| 2703 | | break; |
| 2704 | | |
| 2705 | | case SLOTC_MINIMEM: |
| 2706 | | if (ti99_is_99_8()) |
| 2707 | | { |
| 2708 | | // printf("** is 99/8\n"); |
| 2709 | | image.battery_save(cartslots->cartridge[slot].ram_ptr, 0x1000); |
| 2710 | | memset(cartslots->cartridge[slot].ram_ptr, 0, 0x1000); |
| 2711 | | } |
| 2712 | | else |
| 2713 | | { |
| 2714 | | // We BIG_ENDIANIZE before saving. This is |
| 2715 | | // consistent with the cartridge save format. |
| 2716 | | UINT16 *ramcont = (UINT16 *)cartslots->cartridge[slot].ram_ptr; |
| 2717 | | for (i = 0; i < 0x0800; i++) |
| 2718 | | ramcont[i] = BIG_ENDIANIZE_INT16(ramcont[i]); |
| 2719 | | image.battery_save(ramcont, 0x1000); |
| 2720 | | memset(ramcont, 0, 0x1000); |
| 2721 | | } |
| 2722 | | break; |
| 2723 | | case SLOTC_MBX: |
| 2724 | | if (ti99_is_99_8()) |
| 2725 | | { |
| 2726 | | // printf("** is 99/8\n"); |
| 2727 | | image.battery_save(cartslots->cartridge[slot].ram_ptr, 0x0400); |
| 2728 | | memset(cartslots->cartridge[slot].ram_ptr, 0, 0x0400); |
| 2729 | | } |
| 2730 | | else |
| 2731 | | { |
| 2732 | | // We BIG_ENDIANIZE before saving. This is |
| 2733 | | // consistent with the cartridge save format. |
| 2734 | | UINT16 *ramcont = (UINT16 *)cartslots->cartridge[slot].ram_ptr; |
| 2735 | | for (i = 0; i < 0x0200; i++) |
| 2736 | | ramcont[i] = BIG_ENDIANIZE_INT16(ramcont[i]); |
| 2737 | | image.battery_save(ramcont, 0x0400); |
| 2738 | | memset(ramcont, 0, 0x0400); |
| 2739 | | } |
| 2740 | | break; |
| 2741 | | case SLOTC_CROM: |
| 2742 | | if (cartslots->legacy_slotnumber[SLOTC_MINIMEM]!=-1) |
| 2743 | | /* Don't wipe the RAM before it is saved. */ |
| 2744 | | memset(cartslots->cartridge[slot].rom_ptr, 0, 0x1000); |
| 2745 | | else |
| 2746 | | memset(cartslots->cartridge[slot].rom_ptr, 0, 0x2000); |
| 2747 | | break; |
| 2748 | | |
| 2749 | | case SLOTC_DROM: |
| 2750 | | cartslots->cartridge[slot].rom_page = 0; |
| 2751 | | memset(cartslots->cartridge[slot].rom2_ptr, 0, 0x2000); |
| 2752 | | break; |
| 2753 | | } |
| 2754 | | |
| 2755 | | clear_slot(cartsys, slot); |
| 2756 | | } |
| 2757 | | |
| 2758 | | /***************************************************************************** |
| 2759 | | The overall multi-cartridge slot system. It contains instances of |
| 2760 | | cartridges which contain PCB devices. The memory handlers delegate the calls |
| 2761 | | to the respective handlers of the cartridges. |
| 2762 | | |
| 2763 | | Note that the term "multi-cartridge system" and "multicart" are not the same: |
| 2764 | | A "multicart" may contain multiple resources, organized on a PCB. The multi- |
| 2765 | | cart system may thus host multiple multicarts. |
| 2766 | | |
| 2767 | | Actually, the name of the device should be changed (however, the device name |
| 2768 | | length is limited) |
| 2769 | | ******************************************************************************/ |
| 2770 | | |
| 2771 | | /* GRAM Kracker support */ |
| 2772 | | static WRITE16_DEVICE_HANDLER( ti99_cart_w_gk ); |
| 2773 | | static READ16_DEVICE_HANDLER( ti99_cart_r_gk ); |
| 2774 | | static WRITE16_DEVICE_HANDLER( ti99_cart_w_gk ); |
| 2775 | | static READ8_DEVICE_HANDLER( ti99_cart_crur_gk ); |
| 2776 | | static WRITE8_DEVICE_HANDLER( ti99_cart_cruw_gk ); |
| 2777 | | static READ8_DEVICE_HANDLER( ti99_cart_r_gk8 ); |
| 2778 | | static WRITE8_DEVICE_HANDLER( ti99_cart_w_gk8 ); |
| 2779 | | |
| 2780 | | /* |
| 2781 | | Instantiation of a multicart system for the TI family. |
| 2782 | | */ |
| 2783 | | static DEVICE_START(ti99_multicart) |
| 2784 | | { |
| 2785 | | int i; |
| 2786 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 2787 | | |
| 2788 | | cartslots->active_slot = 0; |
| 2789 | | cartslots->next_free_slot = 0; |
| 2790 | | |
| 2791 | | for (i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) |
| 2792 | | { |
| 2793 | | cartslots->cartridge[i].pcb = NULL; |
| 2794 | | clear_slot(device, i); |
| 2795 | | } |
| 2796 | | |
| 2797 | | /* Initialize the legacy system. */ |
| 2798 | | cartslots->legacy_slotnumber[SLOTC_GROM] = -1; |
| 2799 | | cartslots->legacy_slotnumber[SLOTC_CROM] = -1; |
| 2800 | | cartslots->legacy_slotnumber[SLOTC_DROM] = -1; |
| 2801 | | cartslots->legacy_slotnumber[SLOTC_MINIMEM] = -1; |
| 2802 | | cartslots->legacy_slotnumber[SLOTC_MBX] = -1; |
| 2803 | | |
| 2804 | | cartslots->legacy_slots = 0; |
| 2805 | | cartslots->multi_slots = 0; |
| 2806 | | |
| 2807 | | cartslots->gk_slot = -1; |
| 2808 | | cartslots->gk_guest_slot = -1; |
| 2809 | | |
| 2810 | | // The cartslot system is initialized now. The cartridges themselves |
| 2811 | | // need to check whether their parts are available. |
| 2812 | | } |
| 2813 | | |
| 2814 | | static DEVICE_STOP(ti99_multicart) |
| 2815 | | { |
| 2816 | | // printf("DEVICE_STOP(ti99_multicart)\n"); |
| 2817 | | } |
| 2818 | | |
| 2819 | | static DEVICE_RESET(ti99_multicart) |
| 2820 | | { |
| 2821 | | // Consider to propagate RESET to cartridges. |
| 2822 | | // However, schematics do not reveal any pin for resetting a cartridge; |
| 2823 | | // the reset line is an input used when plugging in a cartridge. |
| 2824 | | |
| 2825 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 2826 | | int slotnumber = input_port_read(device->machine, "CARTSLOT"); |
| 2827 | | assert(slotnumber>=0 && slotnumber<=255); |
| 2828 | | cartslots->fixed_slot = slotnumber-1; /* auto = -1 */ |
| 2829 | | cartslots->grom_address = 0x0000; |
| 2830 | | cartslots->grom_buffer = 0x00; |
| 2831 | | cartslots->loader_on = FALSE; |
| 2832 | | cartslots->raddr_LSB = cartslots->waddr_LSB = FALSE; |
| 2833 | | } |
| 2834 | | |
| 2835 | | /* |
| 2836 | | Accesses the ROM regions of the cartridge for reading. |
| 2837 | | This is the area from 0x6000 to 0x7fff. Each cartridge is responsible |
| 2838 | | for all kinds of magic which is done inside this area, like swapping |
| 2839 | | banks or doing other control actions. These activities beyond simple |
| 2840 | | reading are cartridge-specific. Each kind of cartridge requires a |
| 2841 | | new type. |
| 2842 | | The currently set GROM bank determines which cartridge is actually |
| 2843 | | accessed, also for ROM reads. |
| 2844 | | */ |
| 2845 | | READ16_DEVICE_HANDLER( ti99_multicart_r ) |
| 2846 | | { |
| 2847 | | ti99_multicart_state *cartslots = (ti99_multicart_state *) downcast<legacy_device_base *>(device)->token(); |
| 2848 | | int slot = cartslots->active_slot; |
| 2849 | | cartridge_t *cart; |
| 2850 | | |
| 2851 | | /* Idle the CPU */ |
| 2852 | | cpu_adjust_icount(device->machine->device("maincpu"),-4); |
| 2853 | | // Alternatively: |
| 2854 | | // cpu_spinuntil_time(device->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 2855 | | |
| 2856 | | // Handle GRAM Kracker |
| 2857 | | // We could also consider to use a flag, but input_port_read uses |
| 2858 | | // a hashtable, so this should not be too slow |
| 2859 | | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 2860 | | return ti99_cart_r_gk(device, offset, mem_mask); |
| 2861 | | |
| 2862 | | /* Sanity check. Higher slots are always empty. */ |
| 2863 | | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 2864 | | return 0; |
| 2865 | | |
| 2866 | | // Same as above (GROM read): If there is only one cartridge there |
| 2867 | | // is no use of trying other banks. There is one issue with the |
| 2868 | | // selection mechanism: When the console probes the GROM banks and |
| 2869 | | // we have a ROM-only cartridge plugged in, it checks bank 0 and bank 1, |
| 2870 | | // finds no GROM, and then fails to dummy-check bank 0 again which would |
| 2871 | | // have set our cartridge expander to slot 0. But in slot 1 there is no |
| 2872 | | // ROM cartridge, so nothing shows up in the selection list. We work |
| 2873 | | // around this with the following line. |
| 2874 | | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 2875 | | slot=0; |
| 2876 | | |
| 2877 | | cart = &cartslots->cartridge[slot]; |
| 2878 | | |
| 2879 | | /* Handle legacy mode. */ |
| 2880 | | if (in_legacy_mode(device)) |
| 2881 | | { |
| 2882 | | return ti99_cart_r_legacy(device, offset, mem_mask); |
| 2883 | | } |
| 2884 | | |
| 2885 | | if (!slot_is_empty(device, slot)) |
| 2886 | | { |
| 2887 | | // printf("accessing cartridge in slot %d\n", slot); |
| 2888 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cart->pcb)->token();; |
| 2889 | | // printf("address=%lx, offset=%lx\n", pcbdef, offset); |
| 2890 | | return (*pcbdef->read)(cart->pcb, offset, mem_mask); |
| 2891 | | } |
| 2892 | | else |
| 2893 | | { |
| 2894 | | // printf("No cartridge in slot %d\n", slot); |
| 2895 | | return 0; |
| 2896 | | } |
| 2897 | | } |
| 2898 | | |
| 2899 | | /* |
| 2900 | | Accesses the ROM regions of the cartridge for writing. |
| 2901 | | This is the area from 0x6000 to 0x7fff. Each cartridge is responsible |
| 2902 | | for all kinds of magic which is done inside this area. Specifically, |
| 2903 | | writing is often used to swap banks for subsequent reads. The actual |
| 2904 | | effect of writing (beyond setting values in a RAM chip) are |
| 2905 | | cartridge-specific and must be handled within the cartridge pcb. |
| 2906 | | The currently set GROM bank determines which cartridge is actually |
| 2907 | | accessed, also for ROM reads. |
| 2908 | | */ |
| 2909 | | WRITE16_DEVICE_HANDLER( ti99_multicart_w ) |
| 2910 | | { |
| 2911 | | ti99_multicart_state *cartslots = (ti99_multicart_state *) downcast<legacy_device_base *>(device)->token(); |
| 2912 | | int slot = cartslots->active_slot; |
| 2913 | | cartridge_t *cart; |
| 2914 | | |
| 2915 | | /* Idle the CPU */ |
| 2916 | | cpu_adjust_icount(device->machine->device("maincpu"),-4); |
| 2917 | | // cpu_spinuntil_time(device->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 2918 | | |
| 2919 | | // Handle GRAM Kracker |
| 2920 | | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 2921 | | ti99_cart_w_gk(device, offset, data, mem_mask); |
| 2922 | | |
| 2923 | | /* Sanity check. Higher slots are always empty. */ |
| 2924 | | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 2925 | | return; |
| 2926 | | |
| 2927 | | /* Same as above (READ16). */ |
| 2928 | | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 2929 | | slot=0; |
| 2930 | | |
| 2931 | | cart = &cartslots->cartridge[slot]; |
| 2932 | | |
| 2933 | | /* Handle legacy mode. */ |
| 2934 | | if (in_legacy_mode(device)) |
| 2935 | | { |
| 2936 | | ti99_cart_w_legacy(device, offset, data, mem_mask); |
| 2937 | | } |
| 2938 | | else |
| 2939 | | { |
| 2940 | | if (!slot_is_empty(device, slot)) |
| 2941 | | { |
| 2942 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cart->pcb)->token();; |
| 2943 | | (*pcbdef->write)(cart->pcb, offset, data, mem_mask); |
| 2944 | | } |
| 2945 | | } |
| 2946 | | } |
| 2947 | | |
| 2948 | | /* |
| 2949 | | CRU interface to cartridges, used only by SuperSpace and Pagedcru |
| 2950 | | style cartridges. |
| 2951 | | The SuperSpace has a CRU-based memory mapper. It is accessed via the |
| 2952 | | cartridge slot on CRU base >0800. |
| 2953 | | */ |
| 2954 | | READ8_DEVICE_HANDLER( ti99_multicart_cru_r ) |
| 2955 | | { |
| 2956 | | ti99_multicart_state *cartslots = (ti99_multicart_state *) downcast<legacy_device_base *>(device)->token(); |
| 2957 | | cartridge_t *cart; |
| 2958 | | |
| 2959 | | int slot = cartslots->active_slot; |
| 2960 | | |
| 2961 | | /* We don't support this cartridge type in the legacy mode. */ |
| 2962 | | if (in_legacy_mode(device)) |
| 2963 | | return 0; |
| 2964 | | |
| 2965 | | // Handle GRAM Kracker |
| 2966 | | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 2967 | | return ti99_cart_crur_gk(device, offset); |
| 2968 | | |
| 2969 | | /* Sanity check. Higher slots are always empty. */ |
| 2970 | | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 2971 | | return 0; |
| 2972 | | |
| 2973 | | /* Same as above (READ16). */ |
| 2974 | | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 2975 | | slot=0; |
| 2976 | | |
| 2977 | | cart = &cartslots->cartridge[slot]; |
| 2978 | | |
| 2979 | | if (!slot_is_empty(device, slot)) |
| 2980 | | { |
| 2981 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cart->pcb)->token();; |
| 2982 | | if (pcbdef->cruread != NULL) |
| 2983 | | return (*pcbdef->cruread)(cart->pcb, offset); |
| 2984 | | } |
| 2985 | | return 0; |
| 2986 | | } |
| 2987 | | |
| 2988 | | /* |
| 2989 | | Write cartridge mapper CRU interface (SuperSpace, Pagedcru) |
| 2990 | | */ |
| 2991 | | WRITE8_DEVICE_HANDLER( ti99_multicart_cru_w ) |
| 2992 | | { |
| 2993 | | ti99_multicart_state *cartslots = (ti99_multicart_state *) downcast<legacy_device_base *>(device)->token(); |
| 2994 | | cartridge_t *cart; |
| 2995 | | |
| 2996 | | int slot = cartslots->active_slot; |
| 2997 | | |
| 2998 | | /* We don't support this cartridge type in the legacy mode. */ |
| 2999 | | if (in_legacy_mode(device)) |
| 3000 | | return; |
| 3001 | | |
| 3002 | | // Handle GRAM Kracker |
| 3003 | | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 3004 | | ti99_cart_cruw_gk(device, offset, data); |
| 3005 | | |
| 3006 | | /* Sanity check. Higher slots are always empty. */ |
| 3007 | | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 3008 | | return; |
| 3009 | | |
| 3010 | | /* Same as above (READ16). */ |
| 3011 | | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 3012 | | slot=0; |
| 3013 | | |
| 3014 | | cart = &cartslots->cartridge[slot]; |
| 3015 | | |
| 3016 | | if (!slot_is_empty(device, slot)) |
| 3017 | | { |
| 3018 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cart->pcb)->token();; |
| 3019 | | if (pcbdef->cruwrite != NULL) |
| 3020 | | (*pcbdef->cruwrite)(cart->pcb, offset, data); |
| 3021 | | } |
| 3022 | | } |
| 3023 | | |
| 3024 | | /* |
| 3025 | | TI-99/8 support. We have only 8 bit memory access, so we adapt all |
| 3026 | | functions to this width. |
| 3027 | | */ |
| 3028 | | READ8_DEVICE_HANDLER(ti99_multicart8_r) |
| 3029 | | { |
| 3030 | | ti99_multicart_state *cartslots = (ti99_multicart_state *) downcast<legacy_device_base *>(device)->token(); |
| 3031 | | int slot = cartslots->active_slot; |
| 3032 | | cartridge_t *cart; |
| 3033 | | |
| 3034 | | /* Idle the CPU. TODO: Check whether this is correct for this machine. */ |
| 3035 | | cpu_adjust_icount(device->machine->device("maincpu"),-4); |
| 3036 | | |
| 3037 | | // Handle GRAM Kracker |
| 3038 | | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 3039 | | return ti99_cart_r_gk8(device, offset); |
| 3040 | | |
| 3041 | | /* Sanity check. Higher slots are always empty. */ |
| 3042 | | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 3043 | | return 0; |
| 3044 | | |
| 3045 | | /* Same as above (READ16). */ |
| 3046 | | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 3047 | | slot=0; |
| 3048 | | |
| 3049 | | cart = &cartslots->cartridge[slot]; |
| 3050 | | |
| 3051 | | /* Handle legacy mode. */ |
| 3052 | | if (in_legacy_mode(device)) |
| 3053 | | { |
| 3054 | | return ti99_cart_r_legacy8(device, offset); |
| 3055 | | } |
| 3056 | | |
| 3057 | | if (!slot_is_empty(device, slot)) |
| 3058 | | { |
| 3059 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cart->pcb)->token();; |
| 3060 | | return (*pcbdef->read8)(cart->pcb, offset); |
| 3061 | | } |
| 3062 | | else |
| 3063 | | { |
| 3064 | | return 0; |
| 3065 | | } |
| 3066 | | } |
| 3067 | | |
| 3068 | | WRITE8_DEVICE_HANDLER(ti99_multicart8_w) |
| 3069 | | { |
| 3070 | | ti99_multicart_state *cartslots = (ti99_multicart_state *) downcast<legacy_device_base *>(device)->token(); |
| 3071 | | cartridge_t *cart; |
| 3072 | | int slot = cartslots->active_slot; |
| 3073 | | |
| 3074 | | /* Idle the CPU. TODO: Check whether this is correct for this machine. */ |
| 3075 | | cpu_adjust_icount(device->machine->device("maincpu"),-4); |
| 3076 | | |
| 3077 | | // Handle GRAM Kracker |
| 3078 | | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 3079 | | ti99_cart_w_gk8(device, offset, data); |
| 3080 | | |
| 3081 | | /* Sanity check. Higher slots are always empty. */ |
| 3082 | | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 3083 | | return; |
| 3084 | | |
| 3085 | | /* Same as above (READ16). */ |
| 3086 | | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 3087 | | slot=0; |
| 3088 | | |
| 3089 | | cart = &cartslots->cartridge[slot]; |
| 3090 | | |
| 3091 | | /* Handle legacy mode. */ |
| 3092 | | if (in_legacy_mode(device)) |
| 3093 | | { |
| 3094 | | ti99_cart_w_legacy8(device, offset, data); |
| 3095 | | } |
| 3096 | | else |
| 3097 | | { |
| 3098 | | if (!slot_is_empty(device, slot)) |
| 3099 | | { |
| 3100 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cart->pcb)->token();; |
| 3101 | | (*pcbdef->write8)(cart->pcb, offset, data); |
| 3102 | | } |
| 3103 | | } |
| 3104 | | } |
| 3105 | | |
| 3106 | | /***************************************************************************** |
| 3107 | | GramKracker support |
| 3108 | | The 80 KiB of the GK are allocated as follows |
| 3109 | | 00000 - 01fff: GRAM 0 |
| 3110 | | 02000 - 03fff: GRAM 1 (alternatively, the loader GROM appears here) |
| 3111 | | 04000 - 05fff: GRAM 2 |
| 3112 | | 06000 - 0ffff: GRAM 3-7 (alternatively, the guest GROMs appear here) |
| 3113 | | 10000 - 11fff: RAM 1 |
| 3114 | | 12000 - 13fff: RAM 2 |
| 3115 | | ******************************************************************************/ |
| 3116 | | /* |
| 3117 | | Reads from the GRAM space of the GRAM Kracker. |
| 3118 | | */ |
| 3119 | | static int cartridge_gram_kracker_readg(running_device *cartsys) |
| 3120 | | { |
| 3121 | | // gk_slot is the slot where the GK module is plugged in |
| 3122 | | // gk_guest_slot is the slot where the cartridge is plugged in |
| 3123 | | UINT8 value; |
| 3124 | | cartridge_t *gkcartridge, *gkguestcart; |
| 3125 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 3126 | | |
| 3127 | | // This is only called when there is really a GK module |
| 3128 | | gkcartridge = &cartslots->cartridge[cartslots->gk_slot]; |
| 3129 | | |
| 3130 | | // Buffered value |
| 3131 | | value = (UINT8)cartslots->grom_buffer; |
| 3132 | | |
| 3133 | | if (cartslots->grom_address >= 0x6000) |
| 3134 | | { |
| 3135 | | // When a cartridge is installed, it overrides the GK contents |
| 3136 | | // but only if it has GROMs |
| 3137 | | // So we first get the values and then have them replaced if applicable |
| 3138 | | if (get_gk_switch(1)==GK_NORMAL) |
| 3139 | | { |
| 3140 | | // Read ahead |
| 3141 | | cartslots->grom_buffer = ((UINT8 *)gkcartridge->ram_ptr)[cartslots->grom_address]; |
| 3142 | | } |
| 3143 | | else |
| 3144 | | { |
| 3145 | | // GK is turned off |
| 3146 | | cartslots->grom_buffer = 0; |
| 3147 | | } |
| 3148 | | if (cartslots->gk_guest_slot != -1) |
| 3149 | | { |
| 3150 | | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 3151 | | // So if the cartridge has GROM, have it overwrite the value |
| 3152 | | // if it is in its range |
| 3153 | | if ((gkguestcart->grom_ptr != NULL) |
| 3154 | | && (cartslots->grom_address < (0x6000 + gkguestcart->grom_size))) |
| 3155 | | cartslots->grom_buffer = gkguestcart->grom_ptr[cartslots->grom_address-0x6000]; |
| 3156 | | } |
| 3157 | | } |
| 3158 | | else |
| 3159 | | { |
| 3160 | | if ((cartslots->grom_address >= 0x2000) && (cartslots->grom_address < 0x4000)) |
| 3161 | | { |
| 3162 | | // Loader area |
| 3163 | | if (get_gk_switch(5)==GK_LDOFF) |
| 3164 | | { |
| 3165 | | // Loader off |
| 3166 | | cartslots->loader_on = FALSE; |
| 3167 | | cartslots->grom_buffer = ((UINT8 *)gkcartridge->ram_ptr)[cartslots->grom_address]; |
| 3168 | | } |
| 3169 | | else |
| 3170 | | { |
| 3171 | | // Loader on |
| 3172 | | if (!cartslots->loader_on) |
| 3173 | | { |
| 3174 | | // Hack for GRAM Kracker: The device tells the user to |
| 3175 | | // turn on the loader, but it sets the GROM address before! |
| 3176 | | // So we have read the value from the earlier setting. |
| 3177 | | value = gkcartridge->grom_ptr[cartslots->grom_address-0x2001]; |
| 3178 | | cartslots->loader_on = TRUE; |
| 3179 | | } |
| 3180 | | cartslots->grom_buffer = gkcartridge->grom_ptr[cartslots->grom_address-0x2000]; |
| 3181 | | } |
| 3182 | | } |
| 3183 | | else |
| 3184 | | { |
| 3185 | | // GRAM 0 or GRAM 2. Note that we deliver a value, but the console may |
| 3186 | | // ignore it according to the switch setting. |
| 3187 | | cartslots->grom_buffer = ((UINT8 *)gkcartridge->ram_ptr)[cartslots->grom_address]; |
| 3188 | | } |
| 3189 | | } |
| 3190 | | |
| 3191 | | // The program counter wraps at each GROM chip size (8K), |
| 3192 | | // so 0x5fff + 1 = 0x4000. |
| 3193 | | // The address must be increased even if there is no cartridge slot |
| 3194 | | cartslots->grom_address = ((cartslots->grom_address + 1) & 0x1FFF) | (cartslots->grom_address & 0xE000); |
| 3195 | | |
| 3196 | | /* Reset the read and write address flipflops. */ |
| 3197 | | cartslots->raddr_LSB = cartslots->waddr_LSB = FALSE; |
| 3198 | | |
| 3199 | | return value; |
| 3200 | | } |
| 3201 | | |
| 3202 | | /* |
| 3203 | | Writes to the GRAM space of the GRAM Kracker. |
| 3204 | | FIXME: The real GK seems to not wrap at the GROM boundary (at least for |
| 3205 | | writing). Also check for reading. |
| 3206 | | */ |
| 3207 | | static void cartridge_gram_kracker_writeg(running_device *cartsys, UINT8 data) |
| 3208 | | { |
| 3209 | | // gk_slot is the slot where the GK module is plugged in |
| 3210 | | // gk_guest_slot is the slot where the cartridge is plugged in |
| 3211 | | cartridge_t *gkcartridge; |
| 3212 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(cartsys)->token(); |
| 3213 | | |
| 3214 | | // This is only called when there is really a GK module |
| 3215 | | gkcartridge = &cartslots->cartridge[cartslots->gk_slot]; |
| 3216 | | |
| 3217 | | // We need to rewind by 1 because the read address has already advanced. |
| 3218 | | UINT16 write_addr = ((cartslots->grom_address + 0x1fff) & 0x1FFF) | (cartslots->grom_address & 0xE000); |
| 3219 | | |
| 3220 | | if (get_gk_switch(4)!=GK_WP) |
| 3221 | | { |
| 3222 | | // Write protection off |
| 3223 | | if (((write_addr < 0x2000) |
| 3224 | | && (get_gk_switch(2)==GK_GRAM0)) |
| 3225 | | || ((write_addr >= 0x2000) && (write_addr < 0x4000) |
| 3226 | | && (get_gk_switch(3)==GK_GRAM12) |
| 3227 | | && (get_gk_switch(5)==GK_LDOFF)) |
| 3228 | | || ((write_addr >= 0x4000) && (write_addr < 0x6000) |
| 3229 | | && (get_gk_switch(3)==GK_GRAM12)) |
| 3230 | | || ((write_addr >= 0x6000) && (cartslots->gk_guest_slot==-1) |
| 3231 | | && (get_gk_switch(1)==GK_NORMAL))) |
| 3232 | | { |
| 3233 | | ((UINT8 *)gkcartridge->ram_ptr)[write_addr] = data; |
| 3234 | | } |
| 3235 | | // There is no GRAM in cartridges |
| 3236 | | } |
| 3237 | | } |
| 3238 | | |
| 3239 | | /* |
| 3240 | | Reads from the RAM space of the GRAM Kracker. |
| 3241 | | */ |
| 3242 | | static READ16_DEVICE_HANDLER( ti99_cart_r_gk ) |
| 3243 | | { |
| 3244 | | // gk_slot is the slot where the GK module is plugged in |
| 3245 | | // gk_guest_slot is the slot where the cartridge is plugged in |
| 3246 | | cartridge_t *gkcartridge, *gkguestcart; |
| 3247 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 3248 | | |
| 3249 | | gkcartridge = &cartslots->cartridge[cartslots->gk_slot]; |
| 3250 | | |
| 3251 | | if (cartslots->gk_guest_slot != -1) |
| 3252 | | { |
| 3253 | | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 3254 | | // Legacy mode is not supported with the GK |
| 3255 | | if (in_legacy_mode(device)) |
| 3256 | | { |
| 3257 | | return 0; |
| 3258 | | } |
| 3259 | | // printf("accessing cartridge in slot %d\n", slot); |
| 3260 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 3261 | | // printf("address=%lx, offset=%lx\n", pcbdef, offset); |
| 3262 | | return (*pcbdef->read)(gkguestcart->pcb, offset, mem_mask); |
| 3263 | | } |
| 3264 | | else |
| 3265 | | { |
| 3266 | | if (get_gk_switch(1)==GK_OFF) |
| 3267 | | return 0; |
| 3268 | | |
| 3269 | | if (get_gk_switch(4)==GK_BANK1) |
| 3270 | | return ((UINT16 *)gkcartridge->ram_ptr)[offset+0x8000]; |
| 3271 | | else |
| 3272 | | { |
| 3273 | | if (get_gk_switch(4)==GK_BANK2) |
| 3274 | | return ((UINT16 *)gkcartridge->ram_ptr)[offset+0x9000]; |
| 3275 | | else |
| 3276 | | { |
| 3277 | | // Write protection on |
| 3278 | | if (gkcartridge->ram_page==0) |
| 3279 | | { |
| 3280 | | return ((UINT16 *)gkcartridge->ram_ptr)[offset+0x8000]; |
| 3281 | | } |
| 3282 | | else |
| 3283 | | { |
| 3284 | | return ((UINT16 *)gkcartridge->ram_ptr)[offset+0x9000]; |
| 3285 | | } |
| 3286 | | } |
| 3287 | | } |
| 3288 | | } |
| 3289 | | } |
| 3290 | | |
| 3291 | | /* |
| 3292 | | Writes to the RAM space of the GRAM Kracker. |
| 3293 | | */ |
| 3294 | | static WRITE16_DEVICE_HANDLER( ti99_cart_w_gk ) |
| 3295 | | { |
| 3296 | | // gk_slot is the slot where the GK module is plugged in |
| 3297 | | // gk_guest_slot is the slot where the cartridge is plugged in |
| 3298 | | cartridge_t *gkcartridge, *gkguestcart; |
| 3299 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 3300 | | |
| 3301 | | gkcartridge = &cartslots->cartridge[cartslots->gk_slot]; |
| 3302 | | |
| 3303 | | if (cartslots->gk_guest_slot != -1) |
| 3304 | | { |
| 3305 | | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 3306 | | // Legacy mode is not supported with the GK |
| 3307 | | if (in_legacy_mode(device)) |
| 3308 | | { |
| 3309 | | return; |
| 3310 | | } |
| 3311 | | |
| 3312 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 3313 | | (*pcbdef->write)(gkguestcart->pcb, offset, data, mem_mask); |
| 3314 | | } |
| 3315 | | else |
| 3316 | | { |
| 3317 | | if (get_gk_switch(1)==GK_OFF) |
| 3318 | | return; |
| 3319 | | |
| 3320 | | if (get_gk_switch(4)==GK_BANK1) |
| 3321 | | { |
| 3322 | | UINT16 *pos = ((UINT16 *)gkcartridge->ram_ptr) + offset + 0x8000; |
| 3323 | | COMBINE_DATA(pos); |
| 3324 | | } |
| 3325 | | else |
| 3326 | | { |
| 3327 | | if (get_gk_switch(4)==GK_BANK2) |
| 3328 | | { |
| 3329 | | UINT16 *pos = ((UINT16 *)gkcartridge->ram_ptr) + offset + 0x9000; |
| 3330 | | COMBINE_DATA(pos); |
| 3331 | | } |
| 3332 | | else |
| 3333 | | { |
| 3334 | | // Write protection on |
| 3335 | | // This is handled like in Extended Basic |
| 3336 | | gkcartridge->ram_page = offset & 1; |
| 3337 | | } |
| 3338 | | } |
| 3339 | | } |
| 3340 | | } |
| 3341 | | |
| 3342 | | /* |
| 3343 | | Read cartridge mapper CRU interface (Guest in GRAM Kracker) |
| 3344 | | */ |
| 3345 | | static READ8_DEVICE_HANDLER( ti99_cart_crur_gk ) |
| 3346 | | { |
| 3347 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 3348 | | cartridge_t *gkguestcart; |
| 3349 | | |
| 3350 | | if (cartslots->gk_guest_slot != -1) |
| 3351 | | { |
| 3352 | | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 3353 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 3354 | | if (pcbdef->cruread != NULL) |
| 3355 | | return (*pcbdef->cruread)(gkguestcart->pcb, offset); |
| 3356 | | } |
| 3357 | | return 0; |
| 3358 | | } |
| 3359 | | |
| 3360 | | /* |
| 3361 | | Write cartridge mapper CRU interface (Guest in GRAM Kracker) |
| 3362 | | */ |
| 3363 | | static WRITE8_DEVICE_HANDLER( ti99_cart_cruw_gk ) |
| 3364 | | { |
| 3365 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 3366 | | cartridge_t *gkguestcart; |
| 3367 | | |
| 3368 | | if (cartslots->gk_guest_slot != -1) |
| 3369 | | { |
| 3370 | | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 3371 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 3372 | | if (pcbdef->cruwrite != NULL) |
| 3373 | | (*pcbdef->cruwrite)(gkguestcart->pcb, offset, data); |
| 3374 | | } |
| 3375 | | } |
| 3376 | | |
| 3377 | | /* |
| 3378 | | TI-99/8 support |
| 3379 | | */ |
| 3380 | | static READ8_DEVICE_HANDLER( ti99_cart_r_gk8 ) |
| 3381 | | { |
| 3382 | | // gk_slot is the slot where the GK module is plugged in |
| 3383 | | // gk_guest_slot is the slot where the cartridge is plugged in |
| 3384 | | cartridge_t *gkcartridge, *gkguestcart; |
| 3385 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 3386 | | |
| 3387 | | gkcartridge = &cartslots->cartridge[cartslots->gk_slot]; |
| 3388 | | |
| 3389 | | if (cartslots->gk_guest_slot != -1) |
| 3390 | | { |
| 3391 | | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 3392 | | // Legacy mode is not supported with the GK |
| 3393 | | if (in_legacy_mode(device)) |
| 3394 | | { |
| 3395 | | return 0; |
| 3396 | | } |
| 3397 | | // printf("accessing cartridge in slot %d\n", slot); |
| 3398 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 3399 | | // printf("address=%lx, offset=%lx\n", pcbdef, offset); |
| 3400 | | return (*pcbdef->read8)(gkguestcart->pcb, offset); |
| 3401 | | } |
| 3402 | | else |
| 3403 | | { |
| 3404 | | if (get_gk_switch(1)==GK_OFF) |
| 3405 | | return 0; |
| 3406 | | |
| 3407 | | if (get_gk_switch(4)==GK_BANK1) |
| 3408 | | return ((UINT8 *)gkcartridge->ram_ptr)[offset+0x10000]; |
| 3409 | | else |
| 3410 | | { |
| 3411 | | if (get_gk_switch(4)==GK_BANK2) |
| 3412 | | return ((UINT8 *)gkcartridge->ram_ptr)[offset+0x12000]; |
| 3413 | | else |
| 3414 | | { |
| 3415 | | // Write protection on |
| 3416 | | if (gkcartridge->ram_page==0) |
| 3417 | | return ((UINT8 *)gkcartridge->ram_ptr)[offset+0x10000]; |
| 3418 | | else |
| 3419 | | return ((UINT8 *)gkcartridge->ram_ptr)[offset+0x12000]; |
| 3420 | | } |
| 3421 | | } |
| 3422 | | } |
| 3423 | | } |
| 3424 | | |
| 3425 | | static WRITE8_DEVICE_HANDLER( ti99_cart_w_gk8 ) |
| 3426 | | { |
| 3427 | | // gk_slot is the slot where the GK module is plugged in |
| 3428 | | // gk_guest_slot is the slot where the cartridge is plugged in |
| 3429 | | cartridge_t *gkcartridge, *gkguestcart; |
| 3430 | | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 3431 | | |
| 3432 | | gkcartridge = &cartslots->cartridge[cartslots->gk_slot]; |
| 3433 | | |
| 3434 | | if (cartslots->gk_guest_slot != -1) |
| 3435 | | { |
| 3436 | | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 3437 | | // Legacy mode is not supported with the GK |
| 3438 | | if (in_legacy_mode(device)) |
| 3439 | | { |
| 3440 | | return; |
| 3441 | | } |
| 3442 | | |
| 3443 | | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 3444 | | (*pcbdef->write8)(gkguestcart->pcb, offset, data); |
| 3445 | | } |
| 3446 | | else |
| 3447 | | { |
| 3448 | | if (get_gk_switch(1)==GK_OFF) |
| 3449 | | return; |
| 3450 | | |
| 3451 | | if (get_gk_switch(4)==GK_BANK1) |
| 3452 | | ((UINT8 *)gkcartridge->ram_ptr)[offset+0x10000] = data; |
| 3453 | | else |
| 3454 | | { |
| 3455 | | if (get_gk_switch(4)==GK_BANK2) |
| 3456 | | ((UINT8 *)gkcartridge->ram_ptr)[offset+0x12000] = data; |
| 3457 | | else |
| 3458 | | { |
| 3459 | | // Write protection on |
| 3460 | | // This is handled like in Extended Basic |
| 3461 | | gkcartridge->ram_page = (offset>>1) & 1; |
| 3462 | | } |
| 3463 | | } |
| 3464 | | } |
| 3465 | | } |
| 3466 | | |
| 3467 | | /*****************************************************************************/ |
| 3468 | | #define TI99_CARTRIDGE_SLOT(p) MDRV_CARTSLOT_ADD(p) \ |
| 3469 | | MDRV_CARTSLOT_EXTENSION_LIST("rpk,bin") \ |
| 3470 | | MDRV_CARTSLOT_PCBTYPE(0, "none", TI99_CARTRIDGE_PCB_NONE) \ |
| 3471 | | MDRV_CARTSLOT_PCBTYPE(1, "standard", TI99_CARTRIDGE_PCB_STD) \ |
| 3472 | | MDRV_CARTSLOT_PCBTYPE(2, "paged", TI99_CARTRIDGE_PCB_PAGED) \ |
| 3473 | | MDRV_CARTSLOT_PCBTYPE(3, "minimem", TI99_CARTRIDGE_PCB_MINIMEM) \ |
| 3474 | | MDRV_CARTSLOT_PCBTYPE(4, "super", TI99_CARTRIDGE_PCB_SUPER) \ |
| 3475 | | MDRV_CARTSLOT_PCBTYPE(5, "mbx", TI99_CARTRIDGE_PCB_MBX) \ |
| 3476 | | MDRV_CARTSLOT_PCBTYPE(6, "paged379i", TI99_CARTRIDGE_PCB_PAGED379I) \ |
| 3477 | | MDRV_CARTSLOT_PCBTYPE(7, "pagedcru", TI99_CARTRIDGE_PCB_PAGEDCRU) \ |
| 3478 | | MDRV_CARTSLOT_PCBTYPE(8, "gramkracker", TI99_CARTRIDGE_PCB_GRAMKRACKER) \ |
| 3479 | | MDRV_CARTSLOT_START(ti99_cartridge) \ |
| 3480 | | MDRV_CARTSLOT_LOAD(ti99_cartridge) \ |
| 3481 | | MDRV_CARTSLOT_UNLOAD(ti99_cartridge) |
| 3482 | | |
| 3483 | | static MACHINE_CONFIG_FRAGMENT(ti99_multicart) |
| 3484 | | TI99_CARTRIDGE_SLOT("cartridge1") |
| 3485 | | TI99_CARTRIDGE_SLOT("cartridge2") |
| 3486 | | TI99_CARTRIDGE_SLOT("cartridge3") |
| 3487 | | TI99_CARTRIDGE_SLOT("cartridge4") |
| 3488 | | MACHINE_CONFIG_END |
| 3489 | | |
| 3490 | | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 3491 | | |
| 3492 | | #define DEVTEMPLATE_ID(p,s) p##ti99_multicart##s |
| 3493 | | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_MACHINE_CONFIG |
| 3494 | | #define DEVTEMPLATE_NAME "TI99 Multi-Cartridge Extender" |
| 3495 | | #define DEVTEMPLATE_FAMILY "Multi-cartridge system" |
| 3496 | | #include "devtempl.h" |
| 3497 | | |
| 3498 | | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_MULTICART, ti99_multicart); |
src/mess/devices/ti99_peb.c
| r9500 | r9501 | |
| 1 | | /* |
| 2 | | Peripheral expansion bus support. |
| 3 | | |
| 4 | | The ti-99/4, ti-99/4a, ti computer 99/8, myarc geneve, and snug sgcpu |
| 5 | | 99/4p systems all feature a bus connector that enables the connection of |
| 6 | | extension cards. (Although the hexbus is the preferred bus to add |
| 7 | | additional peripherals to a ti-99/8, ti-99/8 is believed to be compatible |
| 8 | | with the older PEB system.) In the case of the TI consoles, this bus |
| 9 | | connector is located on the right side of the console. |
| 10 | | |
| 11 | | While a few extension cards connect to the side bus connector of the |
| 12 | | ti-99/4(a) console directly, most extension cards were designed to be |
| 13 | | inserted in a PEB instead. The PEB (Peripheral Expansion Box) is a big box |
| 14 | | with an alimentation, a few bus drivers, and several card slots, that |
| 15 | | connects to the ti-99/4(a) side port. The reason for using a PEB is that |
| 16 | | daisy-chaining many modules caused the system to be unreliable due to the |
| 17 | | noise produced by the successive contacts. (As a matter of fact, TI |
| 18 | | initially released most of its extension cards as side bus units, but when |
| 19 | | the design proved to be unreliable, the PEB was introduced. The TI speech |
| 20 | | synthesizer was the only TI extension that remained on the side bus after |
| 21 | | the introduction of the PEB, probably because TI wanted the speech |
| 22 | | synthesizer to be a cheap extension, and the PEB was not cheap.) |
| 23 | | |
| 24 | | The third-party myarc geneve and snug sgcpu computers are actually cards |
| 25 | | to be inserted into a PEB: they have a PC keyboard connector, and replace |
| 26 | | the original ti-99 console completely. |
| 27 | | |
| 28 | | Each expansion card is assigned to one of 16 CRU address ranges. |
| 29 | | I appended the names of known TI peripherals that use each port (I appended |
| 30 | | '???' when I don't know what the peripheral is :-) ). |
| 31 | | * 0x1000-0x10FE "For test equipment use on production line" |
| 32 | | * 0x1100-0x11FE disk controller |
| 33 | | * 0x1200-0x12FE modem??? |
| 34 | | * 0x1300-0x13FE RS232 1&2, PIO 1 |
| 35 | | * 0x1400-0x14FE unassigned |
| 36 | | * 0x1500-0x15FE RS232 3&4, PIO 2 |
| 37 | | * 0x1600-0x16FE unassigned |
| 38 | | * 0x1700-0x17FE hex-bus (prototypes) |
| 39 | | * 0x1800-0x18FE thermal printer (early peripheral) |
| 40 | | * 0x1900-0x19FE EPROM programmer??? (Mezzanine board in July 1981's TI99/7 |
| 41 | | prototype) |
| 42 | | * 0x1A00-0x1AFE unassigned |
| 43 | | * 0x1B00-0x1BFE TI GPL debugger card |
| 44 | | * 0x1C00-0x1CFE Video Controller Card (Possibly the weirdest device. This |
| 45 | | card is connected to the video output of the computer, to a VCR, and a |
| 46 | | video monitor. It can control the VCR, connect the display to either |
| 47 | | VCR output or computer video output, and it can read or save binary |
| 48 | | data to video tape. I think it can act as a genlock interface (i.e. |
| 49 | | TMS9918 transparent background shows the video signal), too, but I am |
| 50 | | not sure about this.) |
| 51 | | * 0x1D00-0x1DFE IEEE 488 Controller Card ('intelligent' parallel bus, |
| 52 | | schematics on ftp.whtech.com) |
| 53 | | * 0x1E00-0x1EFE unassigned |
| 54 | | * 0x1F00-0x1FFE P-code card (part of a complete development system) |
| 55 | | |
| 56 | | The ti-99/8 implements 16 extra ports: |
| 57 | | * 0x2000-0x26ff and 0x2800-0x2fff: reserved for future expansion? |
| 58 | | * 0x2700-0x27ff: internal DSR |
| 59 | | |
| 60 | | The snug sgcpu 99/4p implements 12 extra ports: |
| 61 | | * 0x0400-0x0eff: free |
| 62 | | * 0x0f00-0x0fff: internal DSR |
| 63 | | |
| 64 | | Known mappings for 3rd party cards: |
| 65 | | * Horizon RAMdisk: any ports from 0 to 7 (port 0 is most common). |
| 66 | | * Myarc 128k/512k (RAM and DSR ROM): port 0 (0x1000-0x11FE) |
| 67 | | * Corcomp 512k (RAM and DSR ROM): port unknown |
| 68 | | * Super AMS 128k/512k: port 14 (0x1E00-0x1EFE) |
| 69 | | * Foundation 128k/512k: port 14 (0x1E00-0x1EFE) |
| 70 | | * Gram Karte: any port (0-15) |
| 71 | | * EVPC (video card): 0x1400-0x14FE |
| 72 | | * HSGPL (GROM replacement): 0x1B00-0x1BFE |
| 73 | | |
| 74 | | Of course, these devices additionally need some support routines, and |
| 75 | | possibly memory-mapped I/O registers. To do so, memory range 0x4000-5FFF |
| 76 | | is shared by all cards. The system enables each card as needed by writing |
| 77 | | a 1 to the first CRU bit: when this happens the card can safely enable its |
| 78 | | ROM, RAM, memory-mapped registers, etc. Provided the ROM uses the proper |
| 79 | | ROM header, the system will recognize and call peripheral I/O functions, |
| 80 | | interrupt service routines, and startup routines. (All these routines are |
| 81 | | generally called DSR = Device Service Routine.) |
| 82 | | |
| 83 | | Also, the cards can trigger level-1 ("INTA") interrupts. A LOAD interrupt |
| 84 | | is present on the side port of the ti-99/4(a), too, but it was never used |
| 85 | | by TI, AFAIK. Also, an interrupt line called "INTB" is found on several |
| 86 | | cards built by TI, but it is not connected: I am quite unsure whether it |
| 87 | | was intended to be connected to the LOAD line, to the INT2 line of TMS9985, |
| 88 | | or it was only to be used with the ti-99/4(a) development system running on |
| 89 | | a TI990/10. This is not the only unused feature: cards built by TI support |
| 90 | | sensing of an interrupt status register and 19-bit addressing, even though |
| 91 | | ti-99/4(A) does not take advantage of these features. TI documentation |
| 92 | | tells that some of these features are "defined by the Personal Computer PCC |
| 93 | | at Texas Instrument", which is not very explicit (What on earth does "PCC" |
| 94 | | stand for?). Maybe it refers to the ti-99/4(a) development system running |
| 95 | | on a TI990/10, or to the ti-99/7 and ti-99/8 prototypes. |
| 96 | | |
| 97 | | Note that I use 8-bit RAM handlers. Obviously, using 16-bit handlers would |
| 98 | | be equivalent and faster on a ti-99/4(a), but we need to interface the |
| 99 | | extension cards not only to the ti-99/4(a), but to the geneve and ti-99/8 |
| 100 | | emulators as well. |
| 101 | | |
| 102 | | June 2010: Reimplemented using device structure (MZ) (obsoletes 99_peb.c) |
| 103 | | */ |
| 104 | | |
| 105 | | #include "emu.h" |
| 106 | | #include "ti99_peb.h" |
| 107 | | |
| 108 | | enum width_t { |
| 109 | | width_8bit = 0, |
| 110 | | width_16bit |
| 111 | | }; |
| 112 | | /* expansion card structure for the snug sgcpu 99/4p system, which supports |
| 113 | | dynamical 16-bit accesses (TI did not design this!) */ |
| 114 | | typedef struct ti99_4p_peb_card_handlers_t |
| 115 | | { |
| 116 | | cru_read_handler cru_read; /* card CRU read handler */ |
| 117 | | cru_write_handler cru_write; /* card CRU handler */ |
| 118 | | |
| 119 | | width_t width; |
| 120 | | union |
| 121 | | { |
| 122 | | struct |
| 123 | | { |
| 124 | | read8_space_func mem_read; /* card mem read handler (8 bits) */ |
| 125 | | write8_space_func mem_write; /* card mem write handler (8 bits) */ |
| 126 | | } width_8bit; |
| 127 | | struct |
| 128 | | { |
| 129 | | read16_space_func mem_read; /* card mem read handler (16 bits) */ |
| 130 | | write16_space_func mem_write; /* card mem write handler (16 bits) */ |
| 131 | | } width_16bit; |
| 132 | | } w; |
| 133 | | } ti99_4p_peb_card_handlers_t; |
| 134 | | |
| 135 | | typedef struct _ti99_peb_state |
| 136 | | { |
| 137 | | /* TRUE if we are using the snug sgcpu 99/4p 16-bit extensions */ |
| 138 | | int has_16bit_peb; |
| 139 | | |
| 140 | | /* handlers for each of 28 slots */ |
| 141 | | ti99_4p_peb_card_handlers_t ti99_4p_expansion_ports[28]; |
| 142 | | |
| 143 | | /* handlers for each of 16 slots + 16 extra slots for ti-99/8 */ |
| 144 | | ti99_peb_card_handlers_t expansion_ports[16+16]; |
| 145 | | |
| 146 | | /* index of the currently active card (-1 if none) */ |
| 147 | | int active_card; |
| 148 | | |
| 149 | | /* index of the currently active card, overridden by the mapper */ |
| 150 | | int active_card_tmp; |
| 151 | | |
| 152 | | /* Mapper override. */ |
| 153 | | int mapper_override; |
| 154 | | |
| 155 | | // ila: inta status register (not actually used on ti-99/4(a), but nevertheless |
| 156 | | // present) |
| 157 | | int ila; |
| 158 | | |
| 159 | | // ilb: intb status register (completely pointless on ti-99/4(a), as neither |
| 160 | | // INTB nor SENILB are connected; OTOH, INTB can trigger interrupts on a Geneve, |
| 161 | | // and snug sgcpu 99/4p can sense ILB) |
| 162 | | int ilb; |
| 163 | | |
| 164 | | /* only used by the snug sgcpu 99/4p */ |
| 165 | | int senila, senilb; |
| 166 | | |
| 167 | | /* hack to simulate TMS9900 byte write */ |
| 168 | | int tmp_buffer; |
| 169 | | |
| 170 | | /* inta/intb handlers */ |
| 171 | | void (*inta_callback)(running_machine *machine, int state); |
| 172 | | void (*intb_callback)(running_machine *machine, int state); |
| 173 | | |
| 174 | | address_space *space; |
| 175 | | |
| 176 | | } ti99_peb_state; |
| 177 | | |
| 178 | | INLINE ti99_peb_state *get_safe_token(running_device *device) |
| 179 | | { |
| 180 | | assert(device != NULL); |
| 181 | | assert(downcast<legacy_device_base *>(device)->token() != NULL); |
| 182 | | return (ti99_peb_state *)downcast<legacy_device_base *>(device)->token(); |
| 183 | | } |
| 184 | | |
| 185 | | INLINE const ti99_peb_config *get_config(running_device *device) |
| 186 | | { |
| 187 | | assert(device != NULL); |
| 188 | | assert(device->type() == PBOX); |
| 189 | | return (const ti99_peb_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config(); |
| 190 | | } |
| 191 | | |
| 192 | | /* |
| 193 | | Sets the handlers for one expansion port (normal 8-bit card) |
| 194 | | |
| 195 | | cru_base: CRU base address (any of 0x1000, 0x1100, 0x1200, ..., 0x1F00) |
| 196 | | (snug sgcpu 99/4p accepts 0x0400, ..., 0x1F00, ti-99/8 accepts 0x1000, |
| 197 | | ..., 0x2F00) |
| 198 | | handler: handler structure for the given card |
| 199 | | */ |
| 200 | | void ti99_peb_set_card_handlers(running_device *box, int cru_base, const ti99_peb_card_handlers_t *handler) |
| 201 | | { |
| 202 | | int port; |
| 203 | | ti99_peb_state *peb = get_safe_token(box); |
| 204 | | |
| 205 | | if (cru_base & 0xff) |
| 206 | | return; |
| 207 | | |
| 208 | | if (peb->has_16bit_peb) |
| 209 | | { |
| 210 | | port = (cru_base - 0x0400) >> 8; |
| 211 | | |
| 212 | | if ((port>=0) && (port<28)) |
| 213 | | { |
| 214 | | peb->ti99_4p_expansion_ports[port].cru_read = handler->cru_read; |
| 215 | | peb->ti99_4p_expansion_ports[port].cru_write = handler->cru_write; |
| 216 | | peb->ti99_4p_expansion_ports[port].width = width_8bit; |
| 217 | | peb->ti99_4p_expansion_ports[port].w.width_8bit.mem_read = handler->mem_read; |
| 218 | | peb->ti99_4p_expansion_ports[port].w.width_8bit.mem_write = handler->mem_write; |
| 219 | | } |
| 220 | | } |
| 221 | | else |
| 222 | | { |
| 223 | | port = (cru_base - 0x1000) >> 8; |
| 224 | | |
| 225 | | if ((port>=0) && (port</*16*/32)) |
| 226 | | { |
| 227 | | peb->expansion_ports[port] = *handler; |
| 228 | | } |
| 229 | | } |
| 230 | | } |
| 231 | | |
| 232 | | /* |
| 233 | | Sets the handlers for one expansion port (special 16-bit card for snug |
| 234 | | sgcpu 99/4p) |
| 235 | | |
| 236 | | cru_base: CRU base address (any of 0x1000, 0x1100, 0x1200, ..., 0x1F00) |
| 237 | | (snug sgcpu 99/4p accepts 0x0400, ..., 0x1F00, ti-99/8 accepts 0x1000, |
| 238 | | ..., 0x2F00) |
| 239 | | handler: handler structure for the given card |
| 240 | | */ |
| 241 | | void ti99_peb_set_16bit_card_handlers(running_device *box, int cru_base, const ti99_peb_16bit_card_handlers_t *handler) |
| 242 | | { |
| 243 | | int port; |
| 244 | | ti99_peb_state *peb = get_safe_token(box); |
| 245 | | |
| 246 | | if (cru_base & 0xff) |
| 247 | | return; |
| 248 | | |
| 249 | | if (peb->has_16bit_peb) |
| 250 | | { |
| 251 | | port = (cru_base - 0x0400) >> 8; |
| 252 | | |
| 253 | | if ((port>=0) && (port<28)) |
| 254 | | { |
| 255 | | peb->ti99_4p_expansion_ports[port].cru_read = handler->cru_read; |
| 256 | | peb->ti99_4p_expansion_ports[port].cru_write = handler->cru_write; |
| 257 | | peb->ti99_4p_expansion_ports[port].width = width_16bit; |
| 258 | | peb->ti99_4p_expansion_ports[port].w.width_16bit.mem_read = handler->mem_read; |
| 259 | | peb->ti99_4p_expansion_ports[port].w.width_16bit.mem_write = handler->mem_write; |
| 260 | | } |
| 261 | | } |
| 262 | | } |
| 263 | | |
| 264 | | /* |
| 265 | | Update ila status register and assert or clear INTA interrupt line |
| 266 | | accordingly. |
| 267 | | |
| 268 | | bit: bit number ([0,7] for original INTA register, [8,15] are extra |
| 269 | | "virtual" bits for devices that assert the INTA line without setting a |
| 270 | | bit of the ILA register) |
| 271 | | state: 1 to assert bit, 0 to clear |
| 272 | | */ |
| 273 | | void ti99_peb_set_ila_bit(running_device *box, int bit, int state) |
| 274 | | { |
| 275 | | ti99_peb_state *peb = get_safe_token(box); |
| 276 | | if (state) |
| 277 | | { |
| 278 | | peb->ila |= 1 << bit; |
| 279 | | if (peb->inta_callback) |
| 280 | | (*peb->inta_callback)(box->machine, 1); |
| 281 | | } |
| 282 | | else |
| 283 | | { |
| 284 | | peb->ila &= ~(1 << bit); |
| 285 | | if ((!peb->ila) && peb->inta_callback) |
| 286 | | (*peb->inta_callback)(box->machine, 0); |
| 287 | | } |
| 288 | | } |
| 289 | | |
| 290 | | /* |
| 291 | | Update ilb status register and assert or clear INTB interrupt line |
| 292 | | accordingly. |
| 293 | | |
| 294 | | bit: bit number ([0,7] for original INTAB register, [8,15] are extra |
| 295 | | "virtual" bits for devices that assert the INTB line without setting a |
| 296 | | bit of the ILB register) |
| 297 | | state: 1 to assert bit, 0 to clear |
| 298 | | */ |
| 299 | | void ti99_peb_set_ilb_bit(running_device *box, int bit, int state) |
| 300 | | { |
| 301 | | ti99_peb_state *peb = get_safe_token(box); |
| 302 | | |
| 303 | | if (state) |
| 304 | | { |
| 305 | | peb->ilb |= 1 << bit; |
| 306 | | if (peb->intb_callback) |
| 307 | | (*peb->intb_callback)(box->machine, 1); |
| 308 | | } |
| 309 | | else |
| 310 | | { |
| 311 | | peb->ilb &= ~(1 << bit); |
| 312 | | if ((!peb->ilb) && peb->intb_callback) |
| 313 | | (*peb->intb_callback)(box->machine, 0); |
| 314 | | } |
| 315 | | } |
| 316 | | |
| 317 | | |
| 318 | | /* |
| 319 | | Read CRU in range >1000->1ffe (>800->fff) (ti-99/4(a)) |
| 320 | | */ |
| 321 | | READ8_DEVICE_HANDLER( ti99_4x_peb_cru_r ) |
| 322 | | { |
| 323 | | int port; |
| 324 | | cru_read_handler handler; |
| 325 | | int reply; |
| 326 | | ti99_peb_state *peb = get_safe_token(device); |
| 327 | | |
| 328 | | port = (offset & 0x00f0) >> 4; |
| 329 | | handler = peb->expansion_ports[port].cru_read; |
| 330 | | |
| 331 | | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0; |
| 332 | | |
| 333 | | return reply; |
| 334 | | } |
| 335 | | |
| 336 | | /* |
| 337 | | Write CRU in range >1000->1ffe (>800->fff) (ti-99/4(a)) |
| 338 | | */ |
| 339 | | WRITE8_DEVICE_HANDLER( ti99_4x_peb_cru_w ) |
| 340 | | { |
| 341 | | int port; |
| 342 | | cru_write_handler handler; |
| 343 | | ti99_peb_state *peb = get_safe_token(device); |
| 344 | | |
| 345 | | port = (offset & 0x0780) >> 7; |
| 346 | | handler = peb->expansion_ports[port].cru_write; |
| 347 | | |
| 348 | | if (handler) |
| 349 | | (*handler)(device->machine, offset & 0x7f, data); |
| 350 | | |
| 351 | | /* expansion card enable? */ |
| 352 | | if ((offset & 0x7f) == 0) |
| 353 | | { |
| 354 | | if (data & 1) |
| 355 | | { |
| 356 | | /* enable */ |
| 357 | | peb->active_card = port; |
| 358 | | } |
| 359 | | else |
| 360 | | { |
| 361 | | if (port == peb->active_card) /* geez... who cares? */ |
| 362 | | { |
| 363 | | peb->active_card = -1; /* no port selected */ |
| 364 | | } |
| 365 | | } |
| 366 | | } |
| 367 | | } |
| 368 | | |
| 369 | | /* |
| 370 | | Read mem in range >4000->5ffe (ti-99/4(a)) |
| 371 | | */ |
| 372 | | READ16_DEVICE_HANDLER( ti99_4x_peb_r ) |
| 373 | | { |
| 374 | | int reply = 0; |
| 375 | | read8_space_func handler; |
| 376 | | ti99_peb_state *peb = get_safe_token(device); |
| 377 | | |
| 378 | | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 379 | | |
| 380 | | if (peb->active_card != -1) |
| 381 | | { |
| 382 | | handler = peb->expansion_ports[peb->active_card].mem_read; |
| 383 | | if (handler) |
| 384 | | { |
| 385 | | reply = (*handler)(peb->space, (offset << 1) + 1); |
| 386 | | reply |= ((unsigned) (*handler)(peb->space, offset << 1)) << 8; |
| 387 | | } |
| 388 | | } |
| 389 | | // printf("[%04x:%04x] = %04x\n", (active_card<<8)+0x1000, (offset<<1)+0x4000, reply); |
| 390 | | |
| 391 | | return peb->tmp_buffer = reply; |
| 392 | | } |
| 393 | | |
| 394 | | /* |
| 395 | | Write mem in range >4000->5ffe (ti-99/4(a)) |
| 396 | | */ |
| 397 | | WRITE16_DEVICE_HANDLER( ti99_4x_peb_w ) |
| 398 | | { |
| 399 | | write8_space_func handler; |
| 400 | | ti99_peb_state *peb = get_safe_token(device); |
| 401 | | |
| 402 | | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 403 | | |
| 404 | | /* simulate byte write */ |
| 405 | | data = (peb->tmp_buffer & ~mem_mask) | (data & mem_mask); |
| 406 | | |
| 407 | | if (peb->active_card != -1) |
| 408 | | { |
| 409 | | handler = peb->expansion_ports[peb->active_card].mem_write; |
| 410 | | if (handler) |
| 411 | | { |
| 412 | | (*handler)(peb->space, (offset << 1) + 1, data & 0xff); |
| 413 | | (*handler)(peb->space, offset << 1, (data >> 8) & 0xff); |
| 414 | | } |
| 415 | | } |
| 416 | | } |
| 417 | | |
| 418 | | |
| 419 | | /* |
| 420 | | Read CRU in range >1000->1ffe (>800->fff) (Geneve) |
| 421 | | */ |
| 422 | | READ8_DEVICE_HANDLER( geneve_peb_cru_r ) |
| 423 | | { |
| 424 | | int port; |
| 425 | | cru_read_handler handler; |
| 426 | | int reply; |
| 427 | | ti99_peb_state *peb = get_safe_token(device); |
| 428 | | |
| 429 | | port = (offset & 0x00f0) >> 4; |
| 430 | | handler = peb->expansion_ports[port].cru_read; |
| 431 | | |
| 432 | | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0; |
| 433 | | |
| 434 | | return reply; |
| 435 | | } |
| 436 | | |
| 437 | | /* |
| 438 | | Write CRU in range >1000->1ffe (>800->fff) (Geneve) |
| 439 | | */ |
| 440 | | WRITE8_DEVICE_HANDLER( geneve_peb_cru_w ) |
| 441 | | { |
| 442 | | int port; |
| 443 | | cru_write_handler handler; |
| 444 | | ti99_peb_state *peb = get_safe_token(device); |
| 445 | | |
| 446 | | port = (offset & 0x0780) >> 7; |
| 447 | | handler = peb->expansion_ports[port].cru_write; |
| 448 | | |
| 449 | | if (handler) |
| 450 | | (*handler)(device->machine, offset & 0x7f, data); |
| 451 | | |
| 452 | | /* expansion card enable? */ |
| 453 | | if ((offset & 0x7f) == 0) |
| 454 | | { |
| 455 | | if (data & 1) |
| 456 | | { |
| 457 | | /* enable */ |
| 458 | | peb->active_card = port; |
| 459 | | } |
| 460 | | else |
| 461 | | { |
| 462 | | if (port == peb->active_card) /* geez... who cares? */ |
| 463 | | { |
| 464 | | peb->active_card = -1; /* no port selected */ |
| 465 | | } |
| 466 | | } |
| 467 | | } |
| 468 | | } |
| 469 | | |
| 470 | | /* |
| 471 | | Read mem in range >4000->5fff (Geneve) |
| 472 | | */ |
| 473 | | READ8_DEVICE_HANDLER( geneve_peb_r ) |
| 474 | | { |
| 475 | | int reply = 0; |
| 476 | | read8_space_func handler; |
| 477 | | ti99_peb_state *peb = get_safe_token(device); |
| 478 | | |
| 479 | | cpu_adjust_icount(device->machine->firstcpu,-8); |
| 480 | | |
| 481 | | if (peb->active_card != -1) |
| 482 | | { |
| 483 | | handler = peb->expansion_ports[peb->active_card].mem_read; |
| 484 | | if (handler) |
| 485 | | reply = (*handler)(peb->space, offset); |
| 486 | | } |
| 487 | | |
| 488 | | return reply; |
| 489 | | } |
| 490 | | |
| 491 | | /* |
| 492 | | Write mem in range >4000->5fff (Geneve) |
| 493 | | */ |
| 494 | | WRITE8_DEVICE_HANDLER( geneve_peb_w ) |
| 495 | | { |
| 496 | | write8_space_func handler; |
| 497 | | ti99_peb_state *peb = get_safe_token(device); |
| 498 | | |
| 499 | | cpu_adjust_icount(device->machine->firstcpu,-8); |
| 500 | | |
| 501 | | if (peb->active_card != -1) |
| 502 | | { |
| 503 | | handler = peb->expansion_ports[peb->active_card].mem_write; |
| 504 | | if (handler) |
| 505 | | (*handler)(peb->space, offset, data); |
| 506 | | } |
| 507 | | } |
| 508 | | |
| 509 | | /* |
| 510 | | Read CRU in range >1000->2ffe (>0800->17ff) (ti-99/8) |
| 511 | | */ |
| 512 | | READ8_DEVICE_HANDLER( ti99_8_peb_cru_r ) |
| 513 | | { |
| 514 | | int port; |
| 515 | | cru_read_handler handler; |
| 516 | | int reply; |
| 517 | | ti99_peb_state *peb = get_safe_token(device); |
| 518 | | |
| 519 | | port = (offset & 0x01f0) >> 4; |
| 520 | | handler = peb->expansion_ports[port].cru_read; |
| 521 | | |
| 522 | | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0; |
| 523 | | |
| 524 | | return reply; |
| 525 | | } |
| 526 | | |
| 527 | | /* |
| 528 | | Write CRU in range >1000->2ffe (>0800->17ff) (ti-99/8) |
| 529 | | */ |
| 530 | | WRITE8_DEVICE_HANDLER( ti99_8_peb_cru_w ) |
| 531 | | { |
| 532 | | int port; |
| 533 | | cru_write_handler handler; |
| 534 | | ti99_peb_state *peb = get_safe_token(device); |
| 535 | | |
| 536 | | port = (offset & 0x0f80) >> 7; |
| 537 | | handler = peb->expansion_ports[port].cru_write; |
| 538 | | |
| 539 | | if (handler) |
| 540 | | (*handler)(device->machine, offset & 0x7f, data); |
| 541 | | |
| 542 | | /* expansion card enable? */ |
| 543 | | if ((offset & 0x7f) == 0) |
| 544 | | { |
| 545 | | if (data & 1) |
| 546 | | { |
| 547 | | /* enable */ |
| 548 | | peb->active_card = port; |
| 549 | | } |
| 550 | | else |
| 551 | | { |
| 552 | | if (port == peb->active_card) /* geez... who cares? */ |
| 553 | | { |
| 554 | | peb->active_card = -1; /* no port selected */ |
| 555 | | } |
| 556 | | } |
| 557 | | } |
| 558 | | } |
| 559 | | |
| 560 | | /* |
| 561 | | Read mem in range >4000->5fff (ti-99/8) |
| 562 | | */ |
| 563 | | READ8_DEVICE_HANDLER( ti99_8_peb_r ) |
| 564 | | { |
| 565 | | int reply = 0; |
| 566 | | read8_space_func handler; |
| 567 | | ti99_peb_state *peb = get_safe_token(device); |
| 568 | | |
| 569 | | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 570 | | |
| 571 | | if (peb->active_card != -1) |
| 572 | | { |
| 573 | | handler = peb->expansion_ports[peb->active_card].mem_read; |
| 574 | | if (handler) |
| 575 | | reply = (*handler)(peb->space, offset); |
| 576 | | } |
| 577 | | |
| 578 | | return reply; |
| 579 | | } |
| 580 | | |
| 581 | | /* |
| 582 | | Write mem in range >4000->5fff (ti-99/8) |
| 583 | | */ |
| 584 | | WRITE8_DEVICE_HANDLER( ti99_8_peb_w ) |
| 585 | | { |
| 586 | | write8_space_func handler; |
| 587 | | ti99_peb_state *peb = get_safe_token(device); |
| 588 | | |
| 589 | | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 590 | | |
| 591 | | if (peb->active_card != -1) |
| 592 | | { |
| 593 | | handler = peb->expansion_ports[peb->active_card].mem_write; |
| 594 | | if (handler) |
| 595 | | (*handler)(peb->space, offset, data); |
| 596 | | } |
| 597 | | } |
| 598 | | |
| 599 | | /* |
| 600 | | Read CRU in range >0400->1ffe (>200->fff) (snug sgcpu 99/4p) |
| 601 | | */ |
| 602 | | READ8_DEVICE_HANDLER( ti99_4p_peb_cru_r ) |
| 603 | | { |
| 604 | | int port; |
| 605 | | cru_read_handler handler; |
| 606 | | int reply; |
| 607 | | ti99_peb_state *peb = get_safe_token(device); |
| 608 | | |
| 609 | | port = offset >> 4; |
| 610 | | handler = peb->ti99_4p_expansion_ports[port].cru_read; |
| 611 | | |
| 612 | | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0; |
| 613 | | |
| 614 | | return reply; |
| 615 | | } |
| 616 | | |
| 617 | | /* |
| 618 | | Write CRU in range >0400->1ffe (>200->fff) (snug sgcpu 99/4p) |
| 619 | | */ |
| 620 | | WRITE8_DEVICE_HANDLER( ti99_4p_peb_cru_w ) |
| 621 | | { |
| 622 | | int port; |
| 623 | | cru_write_handler handler; |
| 624 | | ti99_peb_state *peb = get_safe_token(device); |
| 625 | | |
| 626 | | int cru_address = (offset + 0x200)<<1; |
| 627 | | |
| 628 | | port = offset >> 7; |
| 629 | | handler = peb->ti99_4p_expansion_ports[port].cru_write; |
| 630 | | |
| 631 | | if (handler) |
| 632 | | (*handler)(device->machine, offset & 0x7f, data); |
| 633 | | |
| 634 | | /* expansion card enable? */ |
| 635 | | if ((cru_address & 0xfe) == 0) |
| 636 | | { |
| 637 | | if (data & 1) |
| 638 | | { |
| 639 | | /* enable */ |
| 640 | | if (cru_address == 0x1e00) |
| 641 | | { |
| 642 | | // The internal mapper has precedence over the external bus. |
| 643 | | // It may be turned on although there is another device currently turned on |
| 644 | | peb->active_card_tmp = peb->active_card; |
| 645 | | peb->mapper_override = TRUE; |
| 646 | | } |
| 647 | | peb->active_card = port; |
| 648 | | } |
| 649 | | else |
| 650 | | { |
| 651 | | if ((cru_address == 0x1e00) && peb->mapper_override) |
| 652 | | { |
| 653 | | peb->active_card = peb->active_card_tmp; |
| 654 | | peb->mapper_override = FALSE; |
| 655 | | } |
| 656 | | else |
| 657 | | { |
| 658 | | if (port == peb->active_card) /* geez... who cares? */ |
| 659 | | { |
| 660 | | peb->active_card = -1; /* no port selected */ |
| 661 | | } |
| 662 | | } |
| 663 | | } |
| 664 | | } |
| 665 | | } |
| 666 | | |
| 667 | | /* |
| 668 | | Read mem in range >4000->5ffe (snug sgcpu 99/4p) |
| 669 | | */ |
| 670 | | READ16_DEVICE_HANDLER( ti99_4p_peb_r ) |
| 671 | | { |
| 672 | | int reply = 0; |
| 673 | | read8_space_func handler; |
| 674 | | read16_space_func handler16; |
| 675 | | ti99_peb_state *peb = get_safe_token(device); |
| 676 | | |
| 677 | | if (peb->active_card == -1) |
| 678 | | cpu_adjust_icount(device->machine->firstcpu,-4); /* ??? */ |
| 679 | | else |
| 680 | | { |
| 681 | | if (peb->ti99_4p_expansion_ports[peb->active_card].width == width_8bit) |
| 682 | | { |
| 683 | | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 684 | | |
| 685 | | handler = peb->ti99_4p_expansion_ports[peb->active_card].w.width_8bit.mem_read; |
| 686 | | if (handler) |
| 687 | | { |
| 688 | | reply = (*handler)(peb->space, (offset << 1) + 1); |
| 689 | | reply |= ((unsigned) (*handler)(peb->space, offset << 1)) << 8; |
| 690 | | } |
| 691 | | } |
| 692 | | else |
| 693 | | { |
| 694 | | cpu_adjust_icount(device->machine->firstcpu,-1); /* ??? */ |
| 695 | | |
| 696 | | handler16 = peb->ti99_4p_expansion_ports[peb->active_card].w.width_16bit.mem_read; |
| 697 | | if (handler16) |
| 698 | | reply = (*handler16)(peb->space, offset, /*mem_mask*/0xffff); |
| 699 | | } |
| 700 | | } |
| 701 | | |
| 702 | | if (peb->senila || peb->senilb) |
| 703 | | { |
| 704 | | if ((peb->active_card != -1) || (peb->senila && peb->senilb)) |
| 705 | | /* ah, the smell of burnt silicon... */ |
| 706 | | logerror("<Scrrrrr>: your computer has just burnt (maybe).\n"); |
| 707 | | |
| 708 | | if (peb->senila) |
| 709 | | reply = (peb->ila & 0xff) | (peb->ila << 8); |
| 710 | | else if (peb->senilb) |
| 711 | | reply = (peb->ilb & 0xff) | (peb->ilb << 8); |
| 712 | | } |
| 713 | | |
| 714 | | // printf("[%04x:%04x] = %04x\n", (active_card<<8)+0x400, (offset<<1)+0x4000, reply); |
| 715 | | return peb->tmp_buffer = reply; |
| 716 | | } |
| 717 | | |
| 718 | | /* |
| 719 | | Write mem in range >4000->5ffe (snug sgcpu 99/4p) |
| 720 | | */ |
| 721 | | WRITE16_DEVICE_HANDLER( ti99_4p_peb_w ) |
| 722 | | { |
| 723 | | write8_space_func handler; |
| 724 | | write16_space_func handler16; |
| 725 | | ti99_peb_state *peb = get_safe_token(device); |
| 726 | | |
| 727 | | if (peb->active_card == -1) |
| 728 | | cpu_adjust_icount(device->machine->firstcpu,-4); /* ??? */ |
| 729 | | else |
| 730 | | { |
| 731 | | /* simulate byte write */ |
| 732 | | data = (peb->tmp_buffer & ~mem_mask) | (data & mem_mask); |
| 733 | | |
| 734 | | if (peb->ti99_4p_expansion_ports[peb->active_card].width == width_8bit) |
| 735 | | { |
| 736 | | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 737 | | |
| 738 | | handler = peb->ti99_4p_expansion_ports[peb->active_card].w.width_8bit.mem_write; |
| 739 | | if (handler) |
| 740 | | { |
| 741 | | (*handler)(peb->space, (offset << 1) + 1, data & 0xff); |
| 742 | | (*handler)(peb->space, offset << 1, (data >> 8) & 0xff); |
| 743 | | } |
| 744 | | } |
| 745 | | else |
| 746 | | { |
| 747 | | cpu_adjust_icount(device->machine->firstcpu,-1); /* ??? */ |
| 748 | | |
| 749 | | handler16 = peb->ti99_4p_expansion_ports[peb->active_card].w.width_16bit.mem_write; |
| 750 | | if (handler16) |
| 751 | | (*handler16)(peb->space, offset, data, /*mem_mask*/0xffff); |
| 752 | | } |
| 753 | | } |
| 754 | | } |
| 755 | | |
| 756 | | /* |
| 757 | | Set the state of the SENILA line (snug sgcpu 99/4p) |
| 758 | | */ |
| 759 | | void ti99_4p_peb_set_senila(running_device *box, int state) |
| 760 | | { |
| 761 | | ti99_peb_state *peb = get_safe_token(box); |
| 762 | | peb->senila = (state != 0); |
| 763 | | } |
| 764 | | |
| 765 | | /* |
| 766 | | Set the state of the SENILB line (snug sgcpu 99/4p) |
| 767 | | */ |
| 768 | | void ti99_4p_peb_set_senilb(running_device *box, int state) |
| 769 | | { |
| 770 | | ti99_peb_state *peb = get_safe_token(box); |
| 771 | | peb->senilb = (state != 0); |
| 772 | | } |
| 773 | | |
| 774 | | |
| 775 | | /*************************************************************************** |
| 776 | | DEVICE FUNCTIONS |
| 777 | | ***************************************************************************/ |
| 778 | | |
| 779 | | static DEVICE_START( ti99_peb ) |
| 780 | | { |
| 781 | | } |
| 782 | | |
| 783 | | static DEVICE_STOP( ti99_peb ) |
| 784 | | { |
| 785 | | } |
| 786 | | |
| 787 | | static DEVICE_RESET( ti99_peb ) |
| 788 | | { |
| 789 | | ti99_peb_state *peb = get_safe_token(device); |
| 790 | | |
| 791 | | peb->has_16bit_peb = get_config(device)->mode16; |
| 792 | | peb->inta_callback = get_config(device)->inta_callback; |
| 793 | | peb->intb_callback = get_config(device)->intb_callback; |
| 794 | | |
| 795 | | peb->active_card = -1; |
| 796 | | peb->active_card_tmp = -1; |
| 797 | | |
| 798 | | peb->mapper_override = FALSE; // for ti99_4p |
| 799 | | |
| 800 | | peb->ila = 0; |
| 801 | | peb->ilb = 0; |
| 802 | | |
| 803 | | peb->space = cputag_get_address_space(device->machine, "maincpu", ADDRESS_SPACE_PROGRAM); |
| 804 | | |
| 805 | | // Remove all card handlers; must be reinstalled |
| 806 | | memset(peb->expansion_ports, 0, sizeof(peb->expansion_ports)); |
| 807 | | memset(peb->ti99_4p_expansion_ports, 0, sizeof(peb->ti99_4p_expansion_ports)); |
| 808 | | } |
| 809 | | |
| 810 | | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 811 | | |
| 812 | | #define DEVTEMPLATE_ID(p,s) p##ti99_peb##s |
| 813 | | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_INLINE_CONFIG |
| 814 | | #define DEVTEMPLATE_NAME "TI99 Peripheral Expansion System" |
| 815 | | #define DEVTEMPLATE_FAMILY "External devices" |
| 816 | | #include "devtempl.h" |
| 817 | | |
| 818 | | DEFINE_LEGACY_DEVICE( PBOX, ti99_peb ); |
src/mess/machine/99_dsk.c
| r9500 | r9501 | |
| 1 | | /* |
| 2 | | TI-99/4 and /4a disk controller emulation. |
| 3 | | |
| 4 | | Known disk controllers: |
| 5 | | * TI original SD disk controller. First sold as a side port module |
| 6 | | (PHP1800), later as a card to be inserted in the PE-box (PHP1240). |
| 7 | | Uses a WD1771 FDC, and supports a format of 40 tracks of 9 sectors. |
| 8 | | * TI DD disk controller prototype. Uses some NEC FDC and supports 16 |
| 9 | | sectors per track. (Not emulated) |
| 10 | | * CorComp FDC. Specs unknown, but it supports DD. (Not emulated) |
| 11 | | * SNUG BwG. A nice DD disk controller. Supports 18 sectors per track. |
| 12 | | * Myarc HFDC. Extremely elaborate, supports DD (and even HD, provided that |
| 13 | | a) the data separator is a 9216B and not a 9216, and b) that the OS |
| 14 | | overrides the hfdc DSR, whose HD support is buggy) and MFM harddisks. |
| 15 | | |
| 16 | | An alternative was installing the hexbus controller (which was just a |
| 17 | | prototype, and is not emulated) and connecting a hexbus floppy disk |
| 18 | | controller. The integrated hexbus port was the only supported way to |
| 19 | | attach a floppy drive to a TI-99/2 or a TI-99/5. The integrated hexbus |
| 20 | | port was the recommended way to attach a floppy drive to the TI-99/8, |
| 21 | | but I think this computer supported the TI-99/4(a) disk controllers, too. |
| 22 | | |
| 23 | | Raphael Nabet, 1999-2004. |
| 24 | | |
| 25 | | CHANGES |
| 26 | | |
| 27 | | * Removed format definition and put it in separate file (ti99_dsk) |
| 28 | | Michael Zapf, Feb 2010 |
| 29 | | */ |
| 30 | | |
| 31 | | #include "emu.h" |
| 32 | | #include "wd17xx.h" |
| 33 | | #include "smc92x4.h" |
| 34 | | #include "ti99_4x.h" /* required for memory region offsets */ |
| 35 | | #include "devices/ti99_peb.h" |
| 36 | | #include "99_dsk.h" |
| 37 | | #include "mm58274c.h" |
| 38 | | #include "devices/flopdrv.h" |
| 39 | | #include "devices/harddriv.h" |
| 40 | | #include "devices/ti99_hd.h" |
| 41 | | #include "harddisk.h" |
| 42 | | #include "formats/ti99_dsk.h" |
| 43 | | |
| 44 | | #define MAX_FLOPPIES 4 |
| 45 | | |
| 46 | | /* defines for DRQ_IRQ_status */ |
| 47 | | enum |
| 48 | | { |
| 49 | | fdc_IRQ = 1, |
| 50 | | fdc_DRQ = 2 |
| 51 | | }; |
| 52 | | |
| 53 | | /* current state of the DRQ and IRQ lines */ |
| 54 | | static int DRQ_IRQ_status; |
| 55 | | |
| 56 | | /* when TRUE the CPU is halted while DRQ/IRQ are true */ |
| 57 | | static int DSKhold; |
| 58 | | /* disk selection bits */ |
| 59 | | static int DSEL; |
| 60 | | /* currently selected disk unit */ |
| 61 | | static int DSKnum; |
| 62 | | /* current side */ |
| 63 | | static int DSKside; |
| 64 | | /* 1 if motor is on */ |
| 65 | | static int DVENA; |
| 66 | | /* on rising edge, sets DVENA for 4.23 seconds on rising edge */ |
| 67 | | static int motor_on; |
| 68 | | /* count 4.23s from rising edge of motor_on */ |
| 69 | | static emu_timer *motor_on_timer; |
| 70 | | |
| 71 | | static running_device *expansion_box; |
| 72 | | |
| 73 | | /* |
| 74 | | Resets the drive geometry. This is required because the heuristic of |
| 75 | | the default implementation sets the drive geometry to the geometry |
| 76 | | of the medium. |
| 77 | | */ |
| 78 | | static void set_geometry(running_device *drive, floppy_type_t type) |
| 79 | | { |
| 80 | | if (drive!=NULL) |
| 81 | | floppy_drive_set_geometry(drive, type); |
| 82 | | else |
| 83 | | logerror("Drive not found\n"); |
| 84 | | } |
| 85 | | |
| 86 | | static void set_all_geometries(running_machine *machine, floppy_type_t type) |
| 87 | | { |
| 88 | | set_geometry(machine->device(FLOPPY_0), type); |
| 89 | | set_geometry(machine->device(FLOPPY_1), type); |
| 90 | | set_geometry(machine->device(FLOPPY_2), type); |
| 91 | | set_geometry(machine->device(FLOPPY_3), type); |
| 92 | | } |
| 93 | | |
| 94 | | /* |
| 95 | | call this when the state of DSKhold or DRQ/IRQ or DVENA change |
| 96 | | |
| 97 | | Emulation is faulty because the CPU is actually stopped in the midst of |
| 98 | | instruction, at the end of the memory access |
| 99 | | */ |
| 100 | | static void fdc_handle_hold(running_machine *machine) |
| 101 | | { |
| 102 | | if (DSKhold && (!DRQ_IRQ_status) && DVENA) |
| 103 | | cputag_set_input_line(machine, "maincpu", INPUT_LINE_HALT, ASSERT_LINE); |
| 104 | | else |
| 105 | | cputag_set_input_line(machine, "maincpu", INPUT_LINE_HALT, CLEAR_LINE); |
| 106 | | } |
| 107 | | |
| 108 | | /* |
| 109 | | callback called at the end of DVENA pulse |
| 110 | | */ |
| 111 | | static TIMER_CALLBACK(motor_on_timer_callback) |
| 112 | | { |
| 113 | | DVENA = 0; |
| 114 | | fdc_handle_hold(machine); |
| 115 | | } |
| 116 | | |
| 117 | | /* |
| 118 | | callback called whenever DRQ/IRQ state change |
| 119 | | */ |
| 120 | | static WRITE_LINE_DEVICE_HANDLER( ti99_fdc_intrq_w ) |
| 121 | | { |
| 122 | | if (state) |
| 123 | | { |
| 124 | | DRQ_IRQ_status |= fdc_IRQ; |
| 125 | | ti99_peb_set_ilb_bit(expansion_box, intb_fdc_bit, 1); |
| 126 | | } |
| 127 | | else |
| 128 | | { |
| 129 | | DRQ_IRQ_status &= ~fdc_IRQ; |
| 130 | | ti99_peb_set_ilb_bit(expansion_box, intb_fdc_bit, 0); |
| 131 | | } |
| 132 | | |
| 133 | | fdc_handle_hold(device->machine); |
| 134 | | } |
| 135 | | |
| 136 | | static WRITE_LINE_DEVICE_HANDLER( ti99_fdc_drq_w ) |
| 137 | | { |
| 138 | | if (state) |
| 139 | | DRQ_IRQ_status |= fdc_DRQ; |
| 140 | | else |
| 141 | | DRQ_IRQ_status &= ~fdc_DRQ; |
| 142 | | |
| 143 | | fdc_handle_hold(device->machine); |
| 144 | | } |
| 145 | | |
| 146 | | const wd17xx_interface ti99_wd17xx_interface = |
| 147 | | { |
| 148 | | DEVCB_NULL, |
| 149 | | DEVCB_LINE(ti99_fdc_intrq_w), |
| 150 | | DEVCB_LINE(ti99_fdc_drq_w), |
| 151 | | {FLOPPY_0, FLOPPY_1, FLOPPY_2, FLOPPY_3} |
| 152 | | }; |
| 153 | | |
| 154 | | /* |
| 155 | | Initializes all available controllers. This routine is only used in the |
| 156 | | init state of the emulator. During the normal operation, only the |
| 157 | | reset routines are used. |
| 158 | | */ |
| 159 | | // TODO: Check that: floppy_mon_w(w->drive, CLEAR_LINE); |
| 160 | | |
| 161 | | void ti99_floppy_controllers_init_all(running_machine *machine) |
| 162 | | { |
| 163 | | motor_on_timer = timer_alloc(machine, motor_on_timer_callback, NULL); |
| 164 | | expansion_box = machine->device("per_exp_box"); |
| 165 | | } |
| 166 | | |
| 167 | | /*===========================================================================*/ |
| 168 | | /* |
| 169 | | TI99/4(a) Floppy disk controller card emulation |
| 170 | | */ |
| 171 | | |
| 172 | | /* prototypes */ |
| 173 | | static TIMER_CALLBACK(motor_on_timer_callback); |
| 174 | | static int fdc_cru_r(running_machine *machine, int offset); |
| 175 | | static void fdc_cru_w(running_machine *machine, int offset, int data); |
| 176 | | static READ8_HANDLER(fdc_mem_r); |
| 177 | | static WRITE8_HANDLER(fdc_mem_w); |
| 178 | | |
| 179 | | /* pointer to the fdc ROM data */ |
| 180 | | static UINT8 *ti99_disk_DSR; |
| 181 | | |
| 182 | | static const ti99_peb_card_handlers_t fdc_handlers = |
| 183 | | { |
| 184 | | fdc_cru_r, |
| 185 | | fdc_cru_w, |
| 186 | | fdc_mem_r, |
| 187 | | fdc_mem_w |
| 188 | | }; |
| 189 | | |
| 190 | | /* |
| 191 | | Reset fdc card, set up handlers |
| 192 | | */ |
| 193 | | void ti99_fdc_reset(running_machine *machine) |
| 194 | | { |
| 195 | | running_device *fdc = machine->device("wd179x"); |
| 196 | | ti99_disk_DSR = memory_region(machine, region_dsr) + offset_fdc_dsr; |
| 197 | | DSEL = 0; |
| 198 | | DSKnum = -1; |
| 199 | | DSKside = 0; |
| 200 | | |
| 201 | | DVENA = 0; |
| 202 | | motor_on = 0; |
| 203 | | |
| 204 | | ti99_set_80_track_drives(FALSE); |
| 205 | | |
| 206 | | floppy_type_t flop = FLOPPY_STANDARD_5_25_DSDD_40; |
| 207 | | set_all_geometries(machine, flop); |
| 208 | | |
| 209 | | ti99_peb_set_card_handlers(expansion_box, 0x1100, &fdc_handlers); |
| 210 | | if (fdc) { |
| 211 | | wd17xx_reset(fdc); /* resets the fdc */ |
| 212 | | wd17xx_dden_w(fdc, ASSERT_LINE); |
| 213 | | } |
| 214 | | } |
| 215 | | |
| 216 | | /* |
| 217 | | Read disk CRU interface |
| 218 | | |
| 219 | | bit 0: HLD pin |
| 220 | | bit 1-3: drive n active |
| 221 | | bit 4: 0: motor strobe on |
| 222 | | bit 5: always 0 |
| 223 | | bit 6: always 1 |
| 224 | | bit 7: selected side |
| 225 | | */ |
| 226 | | static int fdc_cru_r(running_machine *machine, int offset) |
| 227 | | { |
| 228 | | int reply; |
| 229 | | |
| 230 | | switch (offset) |
| 231 | | { |
| 232 | | case 0: |
| 233 | | reply = 0x40; |
| 234 | | if (DSKside) |
| 235 | | reply |= 0x80; |
| 236 | | if (DVENA) |
| 237 | | reply |= (DSEL << 1); |
| 238 | | if (! DVENA) |
| 239 | | reply |= 0x10; |
| 240 | | break; |
| 241 | | |
| 242 | | default: |
| 243 | | reply = 0; |
| 244 | | break; |
| 245 | | } |
| 246 | | |
| 247 | | return reply; |
| 248 | | } |
| 249 | | |
| 250 | | /* |
| 251 | | Write disk CRU interface |
| 252 | | */ |
| 253 | | static void fdc_cru_w(running_machine *machine, int offset, int data) |
| 254 | | { |
| 255 | | running_device *fdc = machine->device("wd179x"); |
| 256 | | |
| 257 | | switch (offset) |
| 258 | | { |
| 259 | | case 0: |
| 260 | | /* WRITE to DISK DSR ROM bit (bit 0) */ |
| 261 | | /* handled in xxx_peb_cru_w() */ |
| 262 | | break; |
| 263 | | |
| 264 | | case 1: |
| 265 | | /* Strobe motor (motor is on for 4.23 sec) */ |
| 266 | | if (data && !motor_on) |
| 267 | | { /* on rising edge, set DVENA for 4.23s */ |
| 268 | | DVENA = 1; |
| 269 | | fdc_handle_hold(machine); |
| 270 | | timer_adjust_oneshot(motor_on_timer, ATTOTIME_IN_MSEC(4230), 0); |
| 271 | | } |
| 272 | | motor_on = data; |
| 273 | | break; |
| 274 | | |
| 275 | | case 2: |
| 276 | | /* Set disk ready/hold (bit 2) |
| 277 | | 0: ignore IRQ and DRQ |
| 278 | | 1: TMS9900 is stopped until IRQ or DRQ are set (OR the motor stops rotating - rotates |
| 279 | | for 4.23s after write to revelant CRU bit, this is not emulated and could cause |
| 280 | | the TI99 to lock...) */ |
| 281 | | DSKhold = data; |
| 282 | | fdc_handle_hold(machine); |
| 283 | | break; |
| 284 | | |
| 285 | | case 3: |
| 286 | | /* Load disk heads (HLT pin) (bit 3) */ |
| 287 | | /* ... */ |
| 288 | | break; |
| 289 | | |
| 290 | | case 4: |
| 291 | | case 5: |
| 292 | | case 6: |
| 293 | | /* Select drive X (bits 4-6) */ |
| 294 | | { |
| 295 | | int drive = offset-4; /* drive # (0-2) */ |
| 296 | | |
| 297 | | if (data) |
| 298 | | { |
| 299 | | DSEL |= 1 << drive; |
| 300 | | |
| 301 | | if (drive != DSKnum) /* turn on drive... already on ? */ |
| 302 | | { |
| 303 | | DSKnum = drive; |
| 304 | | |
| 305 | | wd17xx_set_drive(fdc,DSKnum); |
| 306 | | /*wd17xx_set_side(DSKside);*/ |
| 307 | | } |
| 308 | | } |
| 309 | | else |
| 310 | | { |
| 311 | | DSEL &= ~ (1 << drive); |
| 312 | | |
| 313 | | if (drive == DSKnum) /* geez... who cares? */ |
| 314 | | { |
| 315 | | DSKnum = -1; /* no drive selected */ |
| 316 | | } |
| 317 | | } |
| 318 | | } |
| 319 | | break; |
| 320 | | |
| 321 | | case 7: |
| 322 | | /* Select side of disk (bit 7) */ |
| 323 | | DSKside = data; |
| 324 | | wd17xx_set_side(fdc,DSKside); |
| 325 | | break; |
| 326 | | } |
| 327 | | } |
| 328 | | |
| 329 | | |
| 330 | | /* |
| 331 | | read a byte in disk DSR space |
| 332 | | */ |
| 333 | | static READ8_HANDLER(fdc_mem_r) |
| 334 | | { |
| 335 | | running_device *fdc = space->machine->device("wd179x"); |
| 336 | | |
| 337 | | /* only use the even addresses from 1ff0 to 1ff6. |
| 338 | | Note that data is inverted. */ |
| 339 | | if (offset >= 0x1ff0 && (offset & 0x09)==0) |
| 340 | | return wd17xx_r(fdc, (offset >> 1)&0x03) ^ 0xff; |
| 341 | | else |
| 342 | | return ti99_disk_DSR[offset]; |
| 343 | | } |
| 344 | | |
| 345 | | /* |
| 346 | | write a byte in disk DSR space |
| 347 | | */ |
| 348 | | static WRITE8_HANDLER(fdc_mem_w) |
| 349 | | { |
| 350 | | running_device *fdc = space->machine->device("wd179x"); |
| 351 | | |
| 352 | | /* only use the even addresses from 1ff8 to 1ffe. |
| 353 | | Note that data is inverted. */ |
| 354 | | if (offset >= 0x1ff0 && (offset & 0x09)==0x08) |
| 355 | | wd17xx_w(fdc, (offset >> 1)&0x03, data ^ 0xff); |
| 356 | | } |
| 357 | | |
| 358 | | |
| 359 | | #if HAS_99CCFDC |
| 360 | | /*===========================================================================*/ |
| 361 | | /* |
| 362 | | Alternate fdc: CorcComp FDC |
| 363 | | |
| 364 | | Advantages: |
| 365 | | * this card supports Double Density. |
| 366 | | * this card support an additional floppy drive, for a total of 4 floppies. |
| 367 | | |
| 368 | | References: |
| 369 | | * ??? |
| 370 | | */ |
| 371 | | |
| 372 | | /* prototypes */ |
| 373 | | static int ccfdc_cru_r(running_machine *machine, int offset); |
| 374 | | static void ccfdc_cru_w(running_machine *machine, int offset, int data); |
| 375 | | static READ8_HANDLER(ccfdc_mem_r); |
| 376 | | static WRITE8_HANDLER(ccfdc_mem_w); |
| 377 | | |
| 378 | | static const ti99_peb_card_handlers_t ccfdc_handlers = |
| 379 | | { |
| 380 | | ccfdc_cru_r, |
| 381 | | ccfdc_cru_w, |
| 382 | | ccfdc_mem_r, |
| 383 | | ccfdc_mem_w |
| 384 | | }; |
| 385 | | |
| 386 | | #if HAS_99CCFDC |
| 387 | | void ti99_ccfdc_reset(running_machine *machine) |
| 388 | | { |
| 389 | | running_device *fdc = machine->device("wd179x"); |
| 390 | | |
| 391 | | ti99_disk_DSR = memory_region(machine, region_dsr) + offset_ccfdc_dsr; |
| 392 | | DSEL = 0; |
| 393 | | DSKnum = -1; |
| 394 | | DSKside = 0; |
| 395 | | |
| 396 | | DVENA = 0; |
| 397 | | motor_on = 0; |
| 398 | | |
| 399 | | ti99_set_80_track_drives(FALSE); |
| 400 | | |
| 401 | | floppy_type_t flop = FLOPPY_STANDARD_5_25_DSDD_40; |
| 402 | | set_all_geometries(machine, flop); |
| 403 | | |
| 404 | | ti99_peb_set_card_handlers(expansion_box, 0x1100, &ccfdc_handlers); |
| 405 | | |
| 406 | | wd17xx_reset(fdc); /* initialize the floppy disk controller */ |
| 407 | | wd17xx_dden(fdc, CLEAR_LINE); |
| 408 | | } |
| 409 | | #endif |
| 410 | | |
| 411 | | |
| 412 | | /* |
| 413 | | Read disk CRU interface |
| 414 | | |
| 415 | | bit 0: drive 4 active (not emulated) |
| 416 | | bit 1-3: drive n active |
| 417 | | bit 4-7: dip switches 1-4 |
| 418 | | */ |
| 419 | | static int ccfdc_cru_r(int offset) |
| 420 | | { |
| 421 | | int reply; |
| 422 | | |
| 423 | | switch (offset) |
| 424 | | { |
| 425 | | case 0: |
| 426 | | /* CRU bits: beware, logic levels of DIP-switches are inverted */ |
| 427 | | reply = 0x50; |
| 428 | | if (DVENA) |
| 429 | | reply |= ((DSEL << 1) | (DSEL >> 3)) & 0x0f; |
| 430 | | break; |
| 431 | | |
| 432 | | default: |
| 433 | | reply = 0; |
| 434 | | break; |
| 435 | | } |
| 436 | | |
| 437 | | return reply; |
| 438 | | } |
| 439 | | |
| 440 | | |
| 441 | | /* |
| 442 | | Write disk CRU interface |
| 443 | | */ |
| 444 | | static void ccfdc_cru_w(running_machine *machine, int offset, int data) |
| 445 | | { |
| 446 | | running_device *fdc = machine->device("wd179x"); |
| 447 | | |
| 448 | | switch (offset) |
| 449 | | { |
| 450 | | case 0: |
| 451 | | /* WRITE to DISK DSR ROM bit (bit 0) */ |
| 452 | | /* handled in xxx_peb_cru_w() */ |
| 453 | | break; |
| 454 | | |
| 455 | | case 1: |
| 456 | | /* Strobe motor (motor is on for 4.23 sec) */ |
| 457 | | if (data && !motor_on) |
| 458 | | { /* on rising edge, set DVENA for 4.23s */ |
| 459 | | DVENA = 1; |
| 460 | | fdc_handle_hold(machine); |
| 461 | | timer_adjust_oneshot(motor_on_timer, ATTOTIME_IN_MSEC(4230), 0); |
| 462 | | } |
| 463 | | motor_on = data; |
| 464 | | break; |
| 465 | | |
| 466 | | case 2: |
| 467 | | /* Set disk ready/hold (bit 2) |
| 468 | | 0: ignore IRQ and DRQ |
| 469 | | 1: TMS9900 is stopped until IRQ or DRQ are set (OR the motor stops rotating - rotates |
| 470 | | for 4.23s after write to revelant CRU bit, this is not emulated and could cause |
| 471 | | the TI99 to lock...) */ |
| 472 | | DSKhold = data; |
| 473 | | fdc_handle_hold(machine); |
| 474 | | break; |
| 475 | | |
| 476 | | case 4: |
| 477 | | case 5: |
| 478 | | case 6: |
| 479 | | case 8: |
| 480 | | /* Select drive 0-2 (DSK1-DSK3) (bits 4-6) */ |
| 481 | | /* Select drive 3 (DSK4) (bit 8) */ |
| 482 | | { |
| 483 | | int drive = (offset == 8) ? 3 : (offset-4); /* drive # (0-3) */ |
| 484 | | |
| 485 | | if (data) |
| 486 | | { |
| 487 | | DSEL |= 1 << drive; |
| 488 | | |
| 489 | | if (drive != DSKnum) /* turn on drive... already on ? */ |
| 490 | | { |
| 491 | | DSKnum = drive; |
| 492 | | |
| 493 | | wd17xx_set_drive(fdc,DSKnum); |
| 494 | | /*wd17xx_set_side(fdc,DSKside);*/ |
| 495 | | } |
| 496 | | } |
| 497 | | else |
| 498 | | { |
| 499 | | DSEL &= ~ (1 << drive); |
| 500 | | |
| 501 | | if (drive == DSKnum) /* geez... who cares? */ |
| 502 | | { |
| 503 | | DSKnum = -1; /* no drive selected */ |
| 504 | | } |
| 505 | | } |
| 506 | | } |
| 507 | | break; |
| 508 | | |
| 509 | | case 7: |
| 510 | | /* Select side of disk (bit 7) */ |
| 511 | | DSKside = data; |
| 512 | | wd17xx_set_side(fdc,DSKside); |
| 513 | | break; |
| 514 | | |
| 515 | | case 10: |
| 516 | | /* double density enable (active low) */ |
| 517 | | wd17xx_dden(fdc, data ? ASSERT_LINE : CLEAR_LINE); |
| 518 | | break; |
| 519 | | |
| 520 | | case 11: |
| 521 | | /* EPROM A13 */ |
| 522 | | break; |
| 523 | | |
| 524 | | case 13: |
| 525 | | /* RAM A10 */ |
| 526 | | break; |
| 527 | | |
| 528 | | case 14: |
| 529 | | /* override FDC with RTC (active high) */ |
| 530 | | break; |
| 531 | | |
| 532 | | case 15: |
| 533 | | /* EPROM A14 */ |
| 534 | | break; |
| 535 | | |
| 536 | | case 3: |
| 537 | | case 9: |
| 538 | | case 12: |
| 539 | | /* Unused (bit 3, 9 & 12) */ |
| 540 | | break; |
| 541 | | } |
| 542 | | } |
| 543 | | |
| 544 | | |
| 545 | | /* |
| 546 | | read a byte in disk DSR space |
| 547 | | */ |
| 548 | | static READ8_HANDLER(ccfdc_mem_r) |
| 549 | | { |
| 550 | | int reply = 0; |
| 551 | | |
| 552 | | reply = ti99_disk_DSR[offset]; |
| 553 | | |
| 554 | | return reply; |
| 555 | | } |
| 556 | | |
| 557 | | /* |
| 558 | | write a byte in disk DSR space |
| 559 | | */ |
| 560 | | static WRITE8_HANDLER(ccfdc_mem_w) |
| 561 | | { |
| 562 | | } |
| 563 | | #endif |
| 564 | | |
| 565 | | |
| 566 | | /*===========================================================================*/ |
| 567 | | /* |
| 568 | | Alternate fdc: BwG card from SNUG |
| 569 | | |
| 570 | | Advantages: |
| 571 | | * this card supports Double Density. |
| 572 | | * as this card includes its own RAM, it does not need to allocate a portion |
| 573 | | of VDP RAM to store I/O buffers. |
| 574 | | * this card includes a MM58274C RTC. |
| 575 | | * this card support an additional floppy drive, for a total of 4 floppies. |
| 576 | | |
| 577 | | Reference: |
| 578 | | * BwG Disketten-Controller: Beschreibung der DSR |
| 579 | | <http://home.t-online.de/home/harald.glaab/snug/bwg.pdf> |
| 580 | | */ |
| 581 | | |
| 582 | | /* prototypes */ |
| 583 | | static int bwg_cru_r(running_machine *machine, int offset); |
| 584 | | static void bwg_cru_w(running_machine *machine, int offset, int data); |
| 585 | | static READ8_HANDLER(bwg_mem_r); |
| 586 | | static WRITE8_HANDLER(bwg_mem_w); |
| 587 | | |
| 588 | | static const ti99_peb_card_handlers_t bwg_handlers = |
| 589 | | { |
| 590 | | bwg_cru_r, |
| 591 | | bwg_cru_w, |
| 592 | | bwg_mem_r, |
| 593 | | bwg_mem_w |
| 594 | | }; |
| 595 | | |
| 596 | | static int bwg_rtc_enable; |
| 597 | | static int bwg_ram_offset; |
| 598 | | static int bwg_rom_offset; |
| 599 | | static UINT8 *bwg_ram; |
| 600 | | |
| 601 | | |
| 602 | | /* |
| 603 | | Reset fdc card, set up handlers |
| 604 | | */ |
| 605 | | void ti99_bwg_reset(running_machine *machine) |
| 606 | | { |
| 607 | | running_device *fdc = machine->device("wd179x"); |
| 608 | | |
| 609 | | ti99_disk_DSR = memory_region(machine, region_dsr) + offset_bwg_dsr; |
| 610 | | bwg_ram = memory_region(machine, region_dsr) + offset_bwg_ram; |
| 611 | | bwg_ram_offset = 0; |
| 612 | | bwg_rom_offset = 0; |
| 613 | | bwg_rtc_enable = 0; |
| 614 | | |
| 615 | | DSEL = 0; |
| 616 | | DSKnum = -1; |
| 617 | | DSKside = 0; |
| 618 | | |
| 619 | | DVENA = 0; |
| 620 | | motor_on = 0; |
| 621 | | |
| 622 | | ti99_set_80_track_drives(FALSE); |
| 623 | | |
| 624 | | floppy_type_t flop = FLOPPY_STANDARD_5_25_DSDD_40; |
| 625 | | set_all_geometries(machine, flop); |
| 626 | | |
| 627 | | ti99_peb_set_card_handlers(expansion_box, 0x1100, & bwg_handlers); |
| 628 | | |
| 629 | | wd17xx_reset(fdc); /* initialize the floppy disk controller */ |
| 630 | | wd17xx_dden_w(fdc, CLEAR_LINE); |
| 631 | | } |
| 632 | | |
| 633 | | /* |
| 634 | | Read disk CRU interface |
| 635 | | |
| 636 | | bit 0: drive 4 active (not emulated) |
| 637 | | bit 1-3: drive n active |
| 638 | | bit 4-7: dip switches 1-4 |
| 639 | | */ |
| 640 | | static int bwg_cru_r(running_machine *machine, int offset) |
| 641 | | { |
| 642 | | int reply; |
| 643 | | |
| 644 | | switch (offset) |
| 645 | | { |
| 646 | | case 0: |
| 647 | | /* CRU bits: beware, logic levels of DIP-switches are inverted */ |
| 648 | | reply = 0x50; |
| 649 | | if (DVENA) |
| 650 | | reply |= ((DSEL << 1) | (DSEL >> 3)) & 0x0f; |
| 651 | | break; |
| 652 | | |
| 653 | | default: |
| 654 | | reply = 0; |
| 655 | | break; |
| 656 | | } |
| 657 | | |
| 658 | | return reply; |
| 659 | | } |
| 660 | | |
| 661 | | |
| 662 | | /* |
| 663 | | Write disk CRU interface |
| 664 | | */ |
| 665 | | static void bwg_cru_w(running_machine *machine, int offset, int data) |
| 666 | | { |
| 667 | | running_device *fdc = machine->device("wd179x"); |
| 668 | | |
| 669 | | switch (offset) |
| 670 | | { |
| 671 | | case 0: |
| 672 | | /* WRITE to DISK DSR ROM bit (bit 0) */ |
| 673 | | /* handled in xxx_peb_cru_w() */ |
| 674 | | break; |
| 675 | | |
| 676 | | case 1: |
| 677 | | /* Strobe motor (motor is on for 4.23 sec) */ |
| 678 | | if (data && !motor_on) |
| 679 | | { /* on rising edge, set DVENA for 4.23s */ |
| 680 | | DVENA = 1; |
| 681 | | fdc_handle_hold(machine); |
| 682 | | timer_adjust_oneshot(motor_on_timer, ATTOTIME_IN_MSEC(4230), 0); |
| 683 | | } |
| 684 | | motor_on = data; |
| 685 | | break; |
| 686 | | |
| 687 | | case 2: |
| 688 | | /* Set disk ready/hold (bit 2) |
| 689 | | 0: ignore IRQ and DRQ |
| 690 | | 1: TMS9900 is stopped until IRQ or DRQ are set (OR the motor stops rotating - rotates |
| 691 | | for 4.23s after write to revelant CRU bit, this is not emulated and could cause |
| 692 | | the TI99 to lock...) */ |
| 693 | | DSKhold = data; |
| 694 | | fdc_handle_hold(machine); |
| 695 | | break; |
| 696 | | |
| 697 | | case 4: |
| 698 | | case 5: |
| 699 | | case 6: |
| 700 | | case 8: |
| 701 | | /* Select drive 0-2 (DSK1-DSK3) (bits 4-6) */ |
| 702 | | /* Select drive 3 (DSK4) (bit 8) */ |
| 703 | | { |
| 704 | | int drive = (offset == 8) ? 3 : (offset-4); /* drive # (0-3) */ |
| 705 | | |
| 706 | | if (data) |
| 707 | | { |
| 708 | | DSEL |= 1 << drive; |
| 709 | | |
| 710 | | if (drive != DSKnum) /* turn on drive... already on ? */ |
| 711 | | { |
| 712 | | DSKnum = drive; |
| 713 | | |
| 714 | | wd17xx_set_drive(fdc,DSKnum); |
| 715 | | /*wd17xx_set_side(fdc,DSKside);*/ |
| 716 | | } |
| 717 | | } |
| 718 | | else |
| 719 | | { |
| 720 | | DSEL &= ~ (1 << drive); |
| 721 | | |
| 722 | | if (drive == DSKnum) /* geez... who cares? */ |
| 723 | | { |
| 724 | | DSKnum = -1; /* no drive selected */ |
| 725 | | } |
| 726 | | } |
| 727 | | } |
| 728 | | break; |
| 729 | | |
| 730 | | case 7: |
| 731 | | /* Select side of disk (bit 7) */ |
| 732 | | DSKside = data; |
| 733 | | wd17xx_set_side(fdc,DSKside); |
| 734 | | break; |
| 735 | | |
| 736 | | case 10: |
| 737 | | /* double density enable (active low) */ |
| 738 | | wd17xx_dden_w(fdc, data ? ASSERT_LINE : CLEAR_LINE); |
| 739 | | break; |
| 740 | | |
| 741 | | case 11: |
| 742 | | /* EPROM A13 */ |
| 743 | | if (data) |
| 744 | | bwg_rom_offset |= 0x2000; |
| 745 | | else |
| 746 | | bwg_rom_offset &= ~0x2000; |
| 747 | | break; |
| 748 | | |
| 749 | | case 13: |
| 750 | | /* RAM A10 */ |
| 751 | | if (data) |
| 752 | | bwg_ram_offset = 0x0400; |
| 753 | | else |
| 754 | | bwg_ram_offset = 0x0000; |
| 755 | | break; |
| 756 | | |
| 757 | | case 14: |
| 758 | | /* override FDC with RTC (active high) */ |
| 759 | | bwg_rtc_enable = data; |
| 760 | | break; |
| 761 | | |
| 762 | | case 15: |
| 763 | | /* EPROM A14 */ |
| 764 | | if (data) |
| 765 | | bwg_rom_offset |= 0x4000; |
| 766 | | else |
| 767 | | bwg_rom_offset &= ~0x4000; |
| 768 | | break; |
| 769 | | |
| 770 | | case 3: |
| 771 | | case 9: |
| 772 | | case 12: |
| 773 | | /* Unused (bit 3, 9 & 12) */ |
| 774 | | break; |
| 775 | | } |
| 776 | | } |
| 777 | | |
| 778 | | |
| 779 | | /* |
| 780 | | read a byte in disk DSR space |
| 781 | | */ |
| 782 | | static READ8_HANDLER(bwg_mem_r) |
| 783 | | { |
| 784 | | running_device *fdc = space->machine->device("wd179x"); |
| 785 | | |
| 786 | | int reply = 0; |
| 787 | | |
| 788 | | if (offset < 0x1c00) |
| 789 | | reply = ti99_disk_DSR[bwg_rom_offset+offset]; |
| 790 | | else if (offset < 0x1fe0) |
| 791 | | reply = bwg_ram[bwg_ram_offset+(offset-0x1c00)]; |
| 792 | | else if (bwg_rtc_enable) |
| 793 | | { |
| 794 | | if (! (offset & 1)) |
| 795 | | reply = mm58274c_r(space->machine->device("mm58274c_floppy"), (offset - 0x1FE0) >> 1); |
| 796 | | } |
| 797 | | else |
| 798 | | { |
| 799 | | if (offset < 0x1ff0) |
| 800 | | reply = bwg_ram[bwg_ram_offset+(offset-0x1c00)]; |
| 801 | | else |
| 802 | | switch (offset) |
| 803 | | { |
| 804 | | case 0x1FF0: /* Status register */ |
| 805 | | reply = wd17xx_status_r(fdc, offset); |
| 806 | | break; |
| 807 | | case 0x1FF2: /* Track register */ |
| 808 | | reply = wd17xx_track_r(fdc, offset); |
| 809 | | break; |
| 810 | | case 0x1FF4: /* Sector register */ |
| 811 | | reply = wd17xx_sector_r(fdc, offset); |
| 812 | | break; |
| 813 | | case 0x1FF6: /* Data register */ |
| 814 | | reply = wd17xx_data_r(fdc, offset); |
| 815 | | break; |
| 816 | | default: |
| 817 | | reply = 0; |
| 818 | | break; |
| 819 | | } |
| 820 | | } |
| 821 | | return reply; |
| 822 | | } |
| 823 | | |
| 824 | | /* |
| 825 | | write a byte in disk DSR space |
| 826 | | */ |
| 827 | | static WRITE8_HANDLER(bwg_mem_w) |
| 828 | | { |
| 829 | | running_device *fdc = space->machine->device("wd179x"); |
| 830 | | |
| 831 | | if (offset < 0x1c00) |
| 832 | | ; |
| 833 | | else if (offset < 0x1fe0) |
| 834 | | bwg_ram[bwg_ram_offset+(offset-0x1c00)] = data; |
| 835 | | else if (bwg_rtc_enable) |
| 836 | | { |
| 837 | | if (! (offset & 1)) |
| 838 | | mm58274c_w(space->machine->device("mm58274c_floppy"), (offset - 0x1FE0) >> 1, data); |
| 839 | | } |
| 840 | | else |
| 841 | | { |
| 842 | | if (offset < 0x1ff0) |
| 843 | | { |
| 844 | | bwg_ram[bwg_ram_offset+(offset-0x1c00)] = data; |
| 845 | | } |
| 846 | | else |
| 847 | | { |
| 848 | | switch (offset) |
| 849 | | { |
| 850 | | case 0x1FF8: /* Command register */ |
| 851 | | wd17xx_command_w(fdc, offset, data); |
| 852 | | break; |
| 853 | | case 0x1FFA: /* Track register */ |
| 854 | | wd17xx_track_w(fdc, offset, data); |
| 855 | | break; |
| 856 | | case 0x1FFC: /* Sector register */ |
| 857 | | wd17xx_sector_w(fdc, offset, data); |
| 858 | | break; |
| 859 | | case 0x1FFE: /* Data register */ |
| 860 | | wd17xx_data_w(fdc, offset, data); |
| 861 | | break; |
| 862 | | } |
| 863 | | } |
| 864 | | } |
| 865 | | } |
| 866 | | |
| 867 | | |
| 868 | | /*===========================================================================*/ |
| 869 | | /* |
| 870 | | Alternate fdc: HFDC card built by Myarc |
| 871 | | |
| 872 | | Advantages: same as BwG, plus: |
| 873 | | * high density support (only on upgraded cards, I think) |
| 874 | | * hard disk support (only prehistoric mfm hard disks are supported, though) |
| 875 | | * DMA support (I think the DMA controller can only access the on-board RAM) |
| 876 | | |
| 877 | | This card includes a MM58274C RTC and a 9234 HFDC with its various support |
| 878 | | chips. |
| 879 | | |
| 880 | | Reference: |
| 881 | | * hfdc manual |
| 882 | | <ftp://ftp.whtech.com//datasheets & manuals/Hardware manuals/hfdc manual.max> |
| 883 | | |
| 884 | | For smc92x4 (HFDC-internal DSR) |
| 885 | | FM: gap0=3, gap1=26, gap2=11, gap3=27, gap4=42, sync=6, count=9, size=2, fm=16, gap_byte=ff |
| 886 | | MFM: gap0=2, gap1=30, gap2=22, gap3=20, gap4=42, sync=12, count=18, size=2, fm=0, gap_byte=4e |
| 887 | | */ |
| 888 | | |
| 889 | | /* prototypes */ |
| 890 | | static int hfdc_cru_r(running_machine *machine, int offset); |
| 891 | | static void hfdc_cru_w(running_machine *machine, int offset, int data); |
| 892 | | static READ8_HANDLER(hfdc_mem_r); |
| 893 | | static WRITE8_HANDLER(hfdc_mem_w); |
| 894 | | |
| 895 | | static const ti99_peb_card_handlers_t hfdc_handlers = |
| 896 | | { |
| 897 | | hfdc_cru_r, |
| 898 | | hfdc_cru_w, |
| 899 | | hfdc_mem_r, |
| 900 | | hfdc_mem_w |
| 901 | | }; |
| 902 | | |
| 903 | | #define HFDC_MAX_FLOPPY 4 |
| 904 | | #define HFDC_MAX_HARD 4 |
| 905 | | |
| 906 | | static int hfdc_rtc_enable; |
| 907 | | static int hfdc_ram_offset[3]; |
| 908 | | static int hfdc_rom_offset; |
| 909 | | static int cru_sel; |
| 910 | | static UINT8 *hfdc_ram; |
| 911 | | |
| 912 | | static int hfdc_irq_state; |
| 913 | | static int hfdc_dip_state; |
| 914 | | |
| 915 | | static running_device *floppy_unit[4]; |
| 916 | | static running_device *harddisk_unit[4]; |
| 917 | | |
| 918 | | static UINT8 output1_latch; |
| 919 | | static UINT8 output2_latch; |
| 920 | | |
| 921 | | static int dma_address; |
| 922 | | |
| 923 | | static int line_to_index(UINT8 line, int max) |
| 924 | | { |
| 925 | | int index = 0; |
| 926 | | while (index < max) |
| 927 | | { |
| 928 | | if ((line & 0x01)!=0) |
| 929 | | break; |
| 930 | | else |
| 931 | | { |
| 932 | | line = line >> 1; |
| 933 | | index++; |
| 934 | | } |
| 935 | | } |
| 936 | | if (index==max) return -1; |
| 937 | | return index; |
| 938 | | } |
| 939 | | |
| 940 | | /* |
| 941 | | Select the HFDC hard disk unit |
| 942 | | This function implements the selection logic on the PCB (outside the |
| 943 | | controller chip) |
| 944 | | The HDC9234 allows up to four drives to be selected by the select lines. |
| 945 | | This is not enough for controlling four floppy drives and three hard |
| 946 | | drives, so the user programmable outputs are used to select the floppy |
| 947 | | drives. This selection happens completely outside of the controller, |
| 948 | | so we must implement it here. |
| 949 | | */ |
| 950 | | |
| 951 | | static running_device *hfdc_current_harddisk(void) |
| 952 | | { |
| 953 | | int disk_unit = -1; |
| 954 | | disk_unit = line_to_index((output1_latch>>4)&0x0f, HFDC_MAX_HARD); |
| 955 | | |
| 956 | | /* The HFDC does not use disk_unit=0. The first HD has index 1. */ |
| 957 | | /* if (disk_unit>=0) printf("Select hard disk %d\n", disk_unit); |
| 958 | | else printf("No hard disk selected\n"); |
| 959 | | */ |
| 960 | | return harddisk_unit[disk_unit]; |
| 961 | | } |
| 962 | | |
| 963 | | /* |
| 964 | | Select the HFDC floppy disk unit |
| 965 | | This function implements the selection logic on the PCB (outside the |
| 966 | | controller chip) |
| 967 | | */ |
| 968 | | |
| 969 | | static running_device *hfdc_current_floppy(void) |
| 970 | | { |
| 971 | | int disk_unit = -1; |
| 972 | | disk_unit = line_to_index(output1_latch & 0x0f, HFDC_MAX_FLOPPY); |
| 973 | | |
| 974 | | if (disk_unit<0) |
| 975 | | { |
| 976 | | // printf("No unit selected\n"); |
| 977 | | return NULL; |
| 978 | | } |
| 979 | | |
| 980 | | // printf("Select floppy disk %d (output1=%02x)\n", index, output1_latch); |
| 981 | | return floppy_unit[disk_unit]; |
| 982 | | } |
| 983 | | |
| 984 | | /* |
| 985 | | Called whenever the state of the sms9234 interrupt pin changes. |
| 986 | | */ |
| 987 | | static WRITE_LINE_DEVICE_HANDLER( ti99_hfdc_intrq_w ) |
| 988 | | { |
| 989 | | hfdc_irq_state = state; |
| 990 | | |
| 991 | | /* Set INTA */ |
| 992 | | if (state) |
| 993 | | { |
| 994 | | ti99_peb_set_ila_bit(expansion_box, intb_fdc_bit, 1); |
| 995 | | } |
| 996 | | else |
| 997 | | { |
| 998 | | ti99_peb_set_ila_bit(expansion_box, intb_fdc_bit, 0); |
| 999 | | } |
| 1000 | | |
| 1001 | | /* TODO: Check */ |
| 1002 | | fdc_handle_hold(device->machine); |
| 1003 | | } |
| 1004 | | |
| 1005 | | /* |
| 1006 | | Called whenever the state of the sms9234 DMA in progress changes. |
| 1007 | | */ |
| 1008 | | static WRITE_LINE_DEVICE_HANDLER( ti99_hfdc_dip_w ) |
| 1009 | | { |
| 1010 | | hfdc_dip_state = state; |
| 1011 | | } |
| 1012 | | |
| 1013 | | static WRITE8_DEVICE_HANDLER( ti99_hfdc_auxbus_out ) |
| 1014 | | { |
| 1015 | | switch (offset) |
| 1016 | | { |
| 1017 | | case INPUT_STATUS: |
| 1018 | | logerror("99_dsk: Invalid operation: S0=S1=0, but tried to write (expected: read drive status)\n"); |
| 1019 | | return; |
| 1020 | | case OUTPUT_DMA_ADDR: |
| 1021 | | /* Value is dma address byte. Shift previous contents to the left. */ |
| 1022 | | dma_address = ((dma_address << 8) + (data&0xff))&0xffffff; |
| 1023 | | break; |
| 1024 | | case OUTPUT_OUTPUT1: |
| 1025 | | /* value is output1 */ |
| 1026 | | output1_latch = data; |
| 1027 | | break; |
| 1028 | | case OUTPUT_OUTPUT2: |
| 1029 | | /* value is output2 */ |
| 1030 | | output2_latch = data; |
| 1031 | | break; |
| 1032 | | } |
| 1033 | | } |
| 1034 | | |
| 1035 | | static READ_LINE_DEVICE_HANDLER( ti99_hfdc_auxbus_in ) |
| 1036 | | { |
| 1037 | | running_device *drive; |
| 1038 | | UINT8 reply = 0; |
| 1039 | | |
| 1040 | | if (output1_latch==0) |
| 1041 | | { |
| 1042 | | // printf("Neither floppy nor hard disk selected.\n"); |
| 1043 | | return 0; /* is that the correct default value? */ |
| 1044 | | } |
| 1045 | | |
| 1046 | | /* If a floppy is selected, we have one line set among the four programmable outputs. */ |
| 1047 | | if ((output1_latch & 0x0f)!=0) |
| 1048 | | { |
| 1049 | | drive = hfdc_current_floppy(); |
| 1050 | | |
| 1051 | | /* Get floppy status. */ |
| 1052 | | if (floppy_drive_get_flag_state(drive, FLOPPY_DRIVE_INDEX) == FLOPPY_DRIVE_INDEX) |
| 1053 | | reply |= DS_INDEX; |
| 1054 | | if (floppy_tk00_r(drive) == CLEAR_LINE) |
| 1055 | | reply |= DS_TRK00; |
| 1056 | | if (floppy_wpt_r(drive) == CLEAR_LINE) |
| 1057 | | reply |= DS_WRPROT; |
| 1058 | | |
| 1059 | | /* if (image_exists(disk_img)) */ |
| 1060 | | |
| 1061 | | reply |= DS_READY; /* Floppies don't have a READY line; line is pulled up */ |
| 1062 | | reply |= DS_SKCOM; /* Same here. */ |
| 1063 | | } |
| 1064 | | else /* one of the first four lines must be selected */ |
| 1065 | | { |
| 1066 | | UINT8 state; |
| 1067 | | drive = hfdc_current_harddisk(); |
| 1068 | | state = ti99_mfm_harddisk_status(drive); |
| 1069 | | if (state & MFMHD_TRACK00) |
| 1070 | | reply |= DS_TRK00; |
| 1071 | | if (state & MFMHD_SEEKCOMP) |
| 1072 | | reply |= DS_SKCOM; |
| 1073 | | if (state & MFMHD_WRFAULT) |
| 1074 | | reply |= DS_WRFAULT; |
| 1075 | | if (state & MFMHD_INDEX) |
| 1076 | | reply |= DS_INDEX; |
| 1077 | | if (state & MFMHD_READY) |
| 1078 | | reply |= DS_READY; |
| 1079 | | } |
| 1080 | | return reply; |
| 1081 | | } |
| 1082 | | |
| 1083 | | /* |
| 1084 | | Read a byte from buffer in DMA mode |
| 1085 | | */ |
| 1086 | | static UINT8 hfdc_dma_read_callback(void) |
| 1087 | | { |
| 1088 | | UINT8 value = hfdc_ram[dma_address & 0x7fff]; |
| 1089 | | dma_address++; |
| 1090 | | return value; |
| 1091 | | } |
| 1092 | | |
| 1093 | | /* |
| 1094 | | Write a byte to buffer in DMA mode |
| 1095 | | */ |
| 1096 | | static void hfdc_dma_write_callback(UINT8 data) |
| 1097 | | { |
| 1098 | | hfdc_ram[dma_address & 0x7fff] = data; |
| 1099 | | dma_address++; |
| 1100 | | } |
| 1101 | | |
| 1102 | | /* |
| 1103 | | Preliminary MFM harddisk interface. |
| 1104 | | */ |
| 1105 | | static void hfdc_harddisk_get_next_id(int head, chrn_id_hd *id) |
| 1106 | | { |
| 1107 | | running_device *harddisk = hfdc_current_harddisk(); |
| 1108 | | ti99_mfm_harddisk_get_next_id(harddisk, head, id); |
| 1109 | | } |
| 1110 | | |
| 1111 | | static void hfdc_harddisk_seek(int direction) |
| 1112 | | { |
| 1113 | | running_device *harddisk = hfdc_current_harddisk(); |
| 1114 | | ti99_mfm_harddisk_seek(harddisk, direction); |
| 1115 | | } |
| 1116 | | |
| 1117 | | static void hfdc_harddisk_read_sector(int cylinder, int head, int sector, UINT8 **buf, int *sector_length) |
| 1118 | | { |
| 1119 | | running_device *harddisk = hfdc_current_harddisk(); |
| 1120 | | if (harddisk != NULL) |
| 1121 | | ti99_mfm_harddisk_read_sector(harddisk, cylinder, head, sector, buf, sector_length); |
| 1122 | | } |
| 1123 | | |
| 1124 | | static void hfdc_harddisk_write_sector(int cylinder, int head, int sector, UINT8 *buf, int sector_length) |
| 1125 | | { |
| 1126 | | running_device *harddisk = hfdc_current_harddisk(); |
| 1127 | | if (harddisk != NULL) |
| 1128 | | ti99_mfm_harddisk_write_sector(harddisk, cylinder, head, sector, buf, sector_length); |
| 1129 | | } |
| 1130 | | |
| 1131 | | static void hfdc_harddisk_read_track(int head, UINT8 **buffer, int *data_count) |
| 1132 | | { |
| 1133 | | running_device *harddisk = hfdc_current_harddisk(); |
| 1134 | | if (harddisk != NULL) |
| 1135 | | ti99_mfm_harddisk_read_track(harddisk, head, buffer, data_count); |
| 1136 | | } |
| 1137 | | |
| 1138 | | static void hfdc_harddisk_write_track(int head, UINT8 *buffer, int data_count) |
| 1139 | | { |
| 1140 | | running_device *harddisk = hfdc_current_harddisk(); |
| 1141 | | if (harddisk != NULL) |
| 1142 | | ti99_mfm_harddisk_write_track(harddisk, head, buffer, data_count); |
| 1143 | | } |
| 1144 | | |
| 1145 | | /* |
| 1146 | | Reset fdc card, set up handlers |
| 1147 | | */ |
| 1148 | | void ti99_hfdc_reset(running_machine *machine) |
| 1149 | | { |
| 1150 | | running_device *device = machine->device("smc92x4"); |
| 1151 | | const char *flopname[] = {FLOPPY_0, FLOPPY_1, FLOPPY_2, FLOPPY_3}; |
| 1152 | | const char *hardname[] = {MFMHD_0, MFMHD_1, MFMHD_2}; |
| 1153 | | |
| 1154 | | int i; |
| 1155 | | |
| 1156 | | ti99_disk_DSR = memory_region(machine, region_dsr) + offset_hfdc_dsr; |
| 1157 | | hfdc_ram = memory_region(machine, region_dsr) + offset_hfdc_ram; |
| 1158 | | hfdc_ram_offset[0] = hfdc_ram_offset[1] = hfdc_ram_offset[2] = 0; |
| 1159 | | hfdc_rom_offset = 0; |
| 1160 | | hfdc_rtc_enable = 0; |
| 1161 | | |
| 1162 | | DSEL = 0; |
| 1163 | | DSKnum = -1; |
| 1164 | | DSKside = 0; |
| 1165 | | |
| 1166 | | DVENA = 0; |
| 1167 | | motor_on = 0; |
| 1168 | | |
| 1169 | | ti99_peb_set_card_handlers(expansion_box, 0x1100, & hfdc_handlers); |
| 1170 | | |
| 1171 | | if ((input_port_read(machine, "DISKCTRL") & DISK_HFDC) |
| 1172 | | && (input_port_read(machine, "HFDCDIP")&0x55)) |
| 1173 | | ti99_set_80_track_drives(TRUE); |
| 1174 | | else |
| 1175 | | ti99_set_80_track_drives(FALSE); |
| 1176 | | |
| 1177 | | smc92x4_set_timing(device, input_port_read(machine, "DRVSPD")); |
| 1178 | | |
| 1179 | | floppy_type_t flop = FLOPPY_STANDARD_5_25_DSHD; |
| 1180 | | set_all_geometries(machine, flop); |
| 1181 | | |
| 1182 | | /* Connect floppy drives to controller. Note that *this* is the |
| 1183 | | controller, not the controller chip. The pcb contains a select |
| 1184 | | logic. |
| 1185 | | TODO: Turn this implementation into a device so that this is |
| 1186 | | correctly modelled. |
| 1187 | | */ |
| 1188 | | for (i = 0; i < 4; i++) |
| 1189 | | { |
| 1190 | | if (device->owner() != NULL) |
| 1191 | | { |
| 1192 | | floppy_unit[i] = device->owner()->subdevice(flopname[i]); |
| 1193 | | if (floppy_unit[i]==NULL) |
| 1194 | | { |
| 1195 | | floppy_unit[i] = device->machine->device(flopname[i]); |
| 1196 | | } |
| 1197 | | } |
| 1198 | | else |
| 1199 | | { |
| 1200 | | floppy_unit[i] = device->machine->device(flopname[i]); |
| 1201 | | } |
| 1202 | | |
| 1203 | | if (floppy_unit[i]!=NULL) |
| 1204 | | { |
| 1205 | | floppy_drive_set_controller(floppy_unit[i], device); |
| 1206 | | // floppy_drive_set_index_pulse_callback(floppy_unit[i], smc92x4_index_pulse_callback); |
| 1207 | | floppy_drive_set_rpm(floppy_unit[i], 300.); |
| 1208 | | } |
| 1209 | | else |
| 1210 | | { |
| 1211 | | logerror("99_dsk: Image %s is null\n", flopname[i]); |
| 1212 | | } |
| 1213 | | } |
| 1214 | | |
| 1215 | | /* In the HFDC ROM, WDSx selects drive x; drive 0 is not used */ |
| 1216 | | for (i = 1; i < 4; i++) |
| 1217 | | { |
| 1218 | | harddisk_unit[i] = device->machine->device(hardname[i-1]); |
| 1219 | | } |
| 1220 | | harddisk_unit[0] = NULL; |
| 1221 | | |
| 1222 | | if (device) { |
| 1223 | | smc92x4_reset(device); |
| 1224 | | } |
| 1225 | | } |
| 1226 | | |
| 1227 | | const smc92x4_interface ti99_smc92x4_interface = |
| 1228 | | { |
| 1229 | | FALSE, /* do not use the full track layout */ |
| 1230 | | DEVCB_LINE(ti99_hfdc_intrq_w), |
| 1231 | | DEVCB_LINE(ti99_hfdc_dip_w), |
| 1232 | | DEVCB_HANDLER(ti99_hfdc_auxbus_out), |
| 1233 | | DEVCB_LINE(ti99_hfdc_auxbus_in), |
| 1234 | | |
| 1235 | | hfdc_current_floppy, |
| 1236 | | hfdc_dma_read_callback, |
| 1237 | | hfdc_dma_write_callback, |
| 1238 | | |
| 1239 | | /* Preliminary MFM harddisk interface. */ |
| 1240 | | hfdc_harddisk_get_next_id, |
| 1241 | | hfdc_harddisk_seek, |
| 1242 | | hfdc_harddisk_read_sector, |
| 1243 | | hfdc_harddisk_write_sector, |
| 1244 | | hfdc_harddisk_read_track, |
| 1245 | | hfdc_harddisk_write_track |
| 1246 | | }; |
| 1247 | | |
| 1248 | | int mycheck = 0; |
| 1249 | | |
| 1250 | | /* |
| 1251 | | Read disk CRU interface |
| 1252 | | HFDC manual p. 44 |
| 1253 | | |
| 1254 | | CRU Select=0 |
| 1255 | | CRU rel. address Definition |
| 1256 | | 00 Disk controller interrupt |
| 1257 | | 02 Motor status |
| 1258 | | 04 DMA in progress |
| 1259 | | |
| 1260 | | Note that the cru line settings on the board do not match the |
| 1261 | | description. 02 and 04 switch positions. Similarly, the dip switches |
| 1262 | | for the drive configurations are not correctly enumerated in the manual. |
| 1263 | | |
| 1264 | | CRU Select=1 |
| 1265 | | CRU rel. address Definition |
| 1266 | | 00 Switch 4 status \_ drive 2 config |
| 1267 | | 02 Switch 3 status / |
| 1268 | | 04 Switch 2 status \_ drive 1 config |
| 1269 | | 06 Switch 1 status / |
| 1270 | | 08 Switch 8 status \_ drive 4 config |
| 1271 | | 0A Switch 7 status / |
| 1272 | | 0C Switch 6 status \_ drive 3 config |
| 1273 | | 0E Switch 5 status / |
| 1274 | | |
| 1275 | | config: 00 = 9/16/18 sectors, 40 tracks, 16 msec |
| 1276 | | 10 = 9/16/18 sectors, 40 tracks, 8 msec |
| 1277 | | 01 = 9/16/18 sectors, 80 tracks, 2 msec |
| 1278 | | 11 = 36 sectors, 80 tracks, 2 msec |
| 1279 | | */ |
| 1280 | | static int hfdc_cru_r(running_machine *machine, int offset) |
| 1281 | | { |
| 1282 | | int reply; |
| 1283 | | if (offset==0) /* CRU bits 0-7 */ |
| 1284 | | { |
| 1285 | | /* CRU bits */ |
| 1286 | | if (cru_sel) |
| 1287 | | { |
| 1288 | | /* DIP switches. Logic levels are inverted (on->0, off->1). CRU |
| 1289 | | bit order is the reverse of DIP-switch order, too (dip 1 -> bit 7, |
| 1290 | | dip 8 -> bit 0). |
| 1291 | | |
| 1292 | | MZ: 00 should not be used since there is a bug in the |
| 1293 | | DSR of the HFDC which causes problems with SD disks |
| 1294 | | (controller tries DD and then fails to fall back to SD) */ |
| 1295 | | |
| 1296 | | reply = ~(input_port_read(machine, "HFDCDIP")); |
| 1297 | | // printf("hfdc cru read 7-0 = %02x\n", reply); |
| 1298 | | } |
| 1299 | | else |
| 1300 | | { |
| 1301 | | reply = 0; |
| 1302 | | /* if (hfdc_irq_state) |
| 1303 | | reply |= 1; |
| 1304 | | if (motor_on) |
| 1305 | | reply |= 2; |
| 1306 | | if (hfdc_dma_in_progress) |
| 1307 | | reply |= 4;*/ |
| 1308 | | |
| 1309 | | if (hfdc_irq_state) |
| 1310 | | reply |= 0x01; |
| 1311 | | if (hfdc_dip_state) |
| 1312 | | reply |= 0x02; |
| 1313 | | if (motor_on) |
| 1314 | | reply |= 0x04; |
| 1315 | | // printf("hfdc cru read 7-0 = %02x\n", reply); |
| 1316 | | |
| 1317 | | } |
| 1318 | | } |
| 1319 | | else /* CRU bits 8+ */ |
| 1320 | | reply = 0; |
| 1321 | | // printf("hfdc cru read %d = %d\n", offset, reply); |
| 1322 | | |
| 1323 | | return reply; |
| 1324 | | } |
| 1325 | | |
| 1326 | | |
| 1327 | | /* |
| 1328 | | Write disk CRU interface |
| 1329 | | HFDC manual p. 43 |
| 1330 | | |
| 1331 | | CRU rel. address Definition Active |
| 1332 | | 00 Device Service Routine Select HIGH |
| 1333 | | 02 Reset to controller chip LOW |
| 1334 | | 04 Floppy Motor on / CD0 HIGH |
| 1335 | | 06 ROM page select 0 / CD1 |
| 1336 | | 08 ROM page select 1 / CRU input select |
| 1337 | | 12/4/6/8/A RAM page select at 0x5400 |
| 1338 | | 1C/E/0/2/4 RAM page select at 0x5800 |
| 1339 | | 26/8/A/C/E RAM page select at 0x5C00 |
| 1340 | | |
| 1341 | | CD0 and CD1 are Clock Divider selections for the Floppy Data Separator (FDC9216) |
| 1342 | | */ |
| 1343 | | static void hfdc_cru_w(running_machine *machine, int offset, int data) |
| 1344 | | { |
| 1345 | | switch (offset) |
| 1346 | | { |
| 1347 | | case 0: |
| 1348 | | /* WRITE to DISK DSR ROM bit (bit 0) */ |
| 1349 | | /* handled in xxx_peb_cru_w() */ |
| 1350 | | break; |
| 1351 | | |
| 1352 | | case 1: |
| 1353 | | /* reset fdc (active low) */ |
| 1354 | | if (!data) |
| 1355 | | { |
| 1356 | | running_device *device = machine->device("smc92x4"); |
| 1357 | | smc92x4_reset(device); |
| 1358 | | } |
| 1359 | | break; |
| 1360 | | |
| 1361 | | case 2: |
| 1362 | | /* Strobe motor and Clock Divider Bit 0 (CD0) */ |
| 1363 | | if (data && !motor_on) |
| 1364 | | { |
| 1365 | | DVENA = 1; |
| 1366 | | fdc_handle_hold(machine); |
| 1367 | | timer_adjust_oneshot(motor_on_timer, ATTOTIME_IN_MSEC(4230), 0); |
| 1368 | | } |
| 1369 | | motor_on = data; |
| 1370 | | //printf("99dsk set motor on / clock divider 0 to %d\n", data); |
| 1371 | | break; |
| 1372 | | |
| 1373 | | case 3: |
| 1374 | | /* rom page select 0 and Clock Divider Bit 1 (CD1) */ |
| 1375 | | if (data) |
| 1376 | | hfdc_rom_offset |= 0x2000; |
| 1377 | | else |
| 1378 | | hfdc_rom_offset &= ~0x2000; |
| 1379 | | //printf("99dsk set rom page 0 / clock divider 1 to %d\n", data); |
| 1380 | | break; |
| 1381 | | |
| 1382 | | case 4: |
| 1383 | | /* rom page select 1 + cru sel */ |
| 1384 | | if (data) |
| 1385 | | hfdc_rom_offset |= 0x1000; |
| 1386 | | else |
| 1387 | | hfdc_rom_offset &= ~0x1000; |
| 1388 | | cru_sel = data; |
| 1389 | | break; |
| 1390 | | |
| 1391 | | case 9: |
| 1392 | | case 10: |
| 1393 | | case 11: |
| 1394 | | case 12: |
| 1395 | | case 13: |
| 1396 | | case 14: |
| 1397 | | case 15: |
| 1398 | | case 16: |
| 1399 | | case 17: |
| 1400 | | case 18: |
| 1401 | | case 19: |
| 1402 | | case 20: |
| 1403 | | case 21: |
| 1404 | | case 22: |
| 1405 | | case 23: |
| 1406 | | /* ram page select */ |
| 1407 | | if (data) |
| 1408 | | hfdc_ram_offset[(offset-9)/5] |= 0x400 << ((offset-9)%5); |
| 1409 | | else |
| 1410 | | hfdc_ram_offset[(offset-9)/5] &= ~(0x400 << ((offset-9)%5)); |
| 1411 | | break; |
| 1412 | | } |
| 1413 | | // printf("hfdc cru write bit %d = %d\n", offset, data); |
| 1414 | | } |
| 1415 | | |
| 1416 | | |
| 1417 | | /* |
| 1418 | | read a byte in disk DSR space |
| 1419 | | HFDC manual, p. 44 |
| 1420 | | Memory map as seen by the 99/4A PEB |
| 1421 | | |
| 1422 | | 0x4000 - 0x4fbf one of four possible ROM pages |
| 1423 | | 0x4fc0 - 0x4fcf tape select |
| 1424 | | 0x4fd0 - 0x4fdf disk controller select |
| 1425 | | 0x4fe0 - 0x4fff time chip select |
| 1426 | | |
| 1427 | | 0x5000 - 0x53ff static RAM page 0x10 |
| 1428 | | 0x5400 - 0x57ff static RAM page any of 32 pages |
| 1429 | | 0x5800 - 0x5bff static RAM page any of 32 pages |
| 1430 | | 0x5c00 - 0x5fff static RAM page any of 32 pages |
| 1431 | | */ |
| 1432 | | static READ8_HANDLER(hfdc_mem_r) |
| 1433 | | { |
| 1434 | | running_device *device = space->machine->device("smc92x4"); |
| 1435 | | |
| 1436 | | if (offset >= 0x0000 && offset <= 0x0fbf) |
| 1437 | | { |
| 1438 | | /* dsr ROM */ |
| 1439 | | return ti99_disk_DSR[hfdc_rom_offset+offset]; |
| 1440 | | } |
| 1441 | | |
| 1442 | | if (offset >= 0xfc0 && offset <= 0x0fcf) |
| 1443 | | { |
| 1444 | | /* tape controller */ |
| 1445 | | logerror("99_dsk: Tape support not available (access to offset %04x)\n", offset); |
| 1446 | | return 0; |
| 1447 | | } |
| 1448 | | |
| 1449 | | if (offset >= 0xfd0 && offset <= 0x0fdf) |
| 1450 | | { |
| 1451 | | /* Disk controller. Note that the odd addresses are ignored and |
| 1452 | | the addresses used for the write ports are also masked away |
| 1453 | | since the write operations in the ti99 system are always |
| 1454 | | accompanied by read operations before. |
| 1455 | | */ |
| 1456 | | if ((offset & 3) == 0x00) |
| 1457 | | { |
| 1458 | | return smc92x4_r(device, (offset>>2)&1); |
| 1459 | | } |
| 1460 | | else return 0; |
| 1461 | | } |
| 1462 | | if (offset >= 0x0fe0 && offset <= 0x0fff) |
| 1463 | | { |
| 1464 | | /* Real-time clock. Again, ignore odd addresses. */ |
| 1465 | | if ((offset & 1) == 0) |
| 1466 | | { |
| 1467 | | running_device *clock = space->machine->device("mm58274c_floppy"); |
| 1468 | | return mm58274c_r(clock, (offset - 0x0fe0) >> 1); |
| 1469 | | } |
| 1470 | | else return 0; |
| 1471 | | } |
| 1472 | | |
| 1473 | | if (offset >= 0x1000 && offset <= 0x13ff) |
| 1474 | | { |
| 1475 | | /* ram page >08 (hfdc manual says >10) */ |
| 1476 | | return hfdc_ram[0x2000+(offset-0x1000)]; |
| 1477 | | } |
| 1478 | | |
| 1479 | | /* The remaining space is ram with mapper: |
| 1480 | | 1400, 1800, 1c00 are the three banks; get the pointer |
| 1481 | | plus the offset within the bank |
| 1482 | | */ |
| 1483 | | return hfdc_ram[hfdc_ram_offset[(offset-0x1400) >> 10] + (offset & 0x3ff)]; |
| 1484 | | } |
| 1485 | | |
| 1486 | | /* |
| 1487 | | write a byte in disk DSR space |
| 1488 | | */ |
| 1489 | | static WRITE8_HANDLER(hfdc_mem_w) |
| 1490 | | { |
| 1491 | | running_device *diskcnt = space->machine->device("smc92x4"); |
| 1492 | | |
| 1493 | | if (offset >= 0x0000 && offset <= 0x0fbf) |
| 1494 | | { |
| 1495 | | /* dsr ROM */ |
| 1496 | | logerror("99_dsk: Writing to HFDC ROM ignored (access to offset %04x)\n", offset); |
| 1497 | | return; |
| 1498 | | } |
| 1499 | | |
| 1500 | | if (offset >= 0xfc0 && offset <= 0x0fcf) |
| 1501 | | { |
| 1502 | | /* tape controller */ |
| 1503 | | logerror("99_dsk: Tape support not available (access to offset %04x)\n", offset); |
| 1504 | | return; |
| 1505 | | } |
| 1506 | | |
| 1507 | | if (offset >= 0xfd0 && offset <= 0x0fdf) |
| 1508 | | { |
| 1509 | | /* Disk controller. Note that the odd addresses are ignored. */ |
| 1510 | | if ((offset & 3) == 0x02) /* 0x0fd2 and 0x0fd6 */ |
| 1511 | | { |
| 1512 | | smc92x4_w(diskcnt, (offset>>2)&1, data); |
| 1513 | | } |
| 1514 | | return; |
| 1515 | | } |
| 1516 | | |
| 1517 | | if (offset >= 0x0fe0 && offset <= 0x0fff) |
| 1518 | | { |
| 1519 | | /* Real-time clock. Again, ignore odd addresses. */ |
| 1520 | | if ((offset & 1) == 0) |
| 1521 | | { |
| 1522 | | running_device *clock = space->machine->device("mm58274c_floppy"); |
| 1523 | | mm58274c_w(clock, (offset - 0x0fe0) >> 1, data); |
| 1524 | | } |
| 1525 | | return; |
| 1526 | | } |
| 1527 | | |
| 1528 | | if (offset >= 0x1000 && offset <= 0x13ff) |
| 1529 | | { |
| 1530 | | /* ram page >08 (hfdc manual says >10) */ |
| 1531 | | hfdc_ram[0x2000+(offset-0x1000)] = data; |
| 1532 | | return; |
| 1533 | | } |
| 1534 | | |
| 1535 | | /* The remaining space is ram with mapper: |
| 1536 | | 1400, 1800, 1c00 are the three banks; get the pointer |
| 1537 | | plus the offset within the bank |
| 1538 | | */ |
| 1539 | | // if ((offset&0x0f)==0) printf("\n"); |
| 1540 | | // printf("%02x ", data); |
| 1541 | | |
| 1542 | | hfdc_ram[hfdc_ram_offset[(offset-0x1400) >> 10] + (offset & 0x3ff)] = data; |
| 1543 | | } |
src/mess/machine/99_peb.c
| r9500 | r9501 | |
| 1 | | /************************************************************************ |
| 2 | | |
| 3 | | Obsoleted by devices/ti99_peb.c |
| 4 | | |
| 5 | | ************************************************************************/ |
| 6 | | |
| 7 | | /* |
| 8 | | Peripheral expansion bus support. |
| 9 | | |
| 10 | | The ti-99/4, ti-99/4a, ti computer 99/8, myarc geneve, and snug sgcpu |
| 11 | | 99/4p systems all feature a bus connector that enables the connection of |
| 12 | | extension cards. (Although the hexbus is the preferred bus to add |
| 13 | | additional peripherals to a ti-99/8, ti-99/8 is believed to be compatible |
| 14 | | with the older PEB system.) In the case of the TI consoles, this bus |
| 15 | | connector is located on the right side of the console. |
| 16 | | |
| 17 | | While a few extension cards connect to the side bus connector of the |
| 18 | | ti-99/4(a) console directly, most extension cards were designed to be |
| 19 | | inserted in a PEB instead. The PEB (Peripheral Expansion Box) is a big box |
| 20 | | with an alimentation, a few bus drivers, and several card slots, that |
| 21 | | connects to the ti-99/4(a) side port. The reason for using a PEB is that |
| 22 | | daisy-chaining many modules caused the system to be unreliable due to the |
| 23 | | noise produced by the successive contacts. (As a matter of fact, TI |
| 24 | | initially released most of its extension cards as side bus units, but when |
| 25 | | the design proved to be unreliable, the PEB was introduced. The TI speech |
| 26 | | synthesizer was the only TI extension that remained on the side bus after |
| 27 | | the introduction of the PEB, probably because TI wanted the speech |
| 28 | | synthesizer to be a cheap extension, and the PEB was not cheap.) |
| 29 | | |
| 30 | | The third-party myarc geneve and snug sgcpu computers are actually cards |
| 31 | | to be inserted into a PEB: they have a PC keyboard connector, and replace |
| 32 | | the original ti-99 console completely. |
| 33 | | |
| 34 | | Each expansion card is assigned to one of 16 CRU address ranges. |
| 35 | | I appended the names of known TI peripherals that use each port (I appended |
| 36 | | '???' when I don't know what the peripheral is :-) ). |
| 37 | | * 0x1000-0x10FE "For test equipment use on production line" |
| 38 | | * 0x1100-0x11FE disk controller |
| 39 | | * 0x1200-0x12FE modem??? |
| 40 | | * 0x1300-0x13FE RS232 1&2, PIO 1 |
| 41 | | * 0x1400-0x14FE unassigned |
| 42 | | * 0x1500-0x15FE RS232 3&4, PIO 2 |
| 43 | | * 0x1600-0x16FE unassigned |
| 44 | | * 0x1700-0x17FE hex-bus (prototypes) |
| 45 | | * 0x1800-0x18FE thermal printer (early peripheral) |
| 46 | | * 0x1900-0x19FE EPROM programmer??? (Mezzanine board in July 1981's TI99/7 |
| 47 | | prototype) |
| 48 | | * 0x1A00-0x1AFE unassigned |
| 49 | | * 0x1B00-0x1BFE TI GPL debugger card |
| 50 | | * 0x1C00-0x1CFE Video Controller Card (Possibly the weirdest device. This |
| 51 | | card is connected to the video output of the computer, to a VCR, and a |
| 52 | | video monitor. It can control the VCR, connect the display to either |
| 53 | | VCR output or computer video output, and it can read or save binary |
| 54 | | data to video tape. I think it can act as a genlock interface (i.e. |
| 55 | | TMS9918 transparent background shows the video signal), too, but I am |
| 56 | | not sure about this.) |
| 57 | | * 0x1D00-0x1DFE IEEE 488 Controller Card ('intelligent' parallel bus, |
| 58 | | schematics on ftp.whtech.com) |
| 59 | | * 0x1E00-0x1EFE unassigned |
| 60 | | * 0x1F00-0x1FFE P-code card (part of a complete development system) |
| 61 | | |
| 62 | | The ti-99/8 implements 16 extra ports: |
| 63 | | * 0x2000-0x26ff and 0x2800-0x2fff: reserved for future expansion? |
| 64 | | * 0x2700-0x27ff: internal DSR |
| 65 | | |
| 66 | | The snug sgcpu 99/4p implements 12 extra ports: |
| 67 | | * 0x0400-0x0eff: free |
| 68 | | * 0x0f00-0x0fff: internal DSR |
| 69 | | |
| 70 | | Known mappings for 3rd party cards: |
| 71 | | * Horizon RAMdisk: any ports from 0 to 7 (port 0 is most common). |
| 72 | | * Myarc 128k/512k (RAM and DSR ROM): port 0 (0x1000-0x11FE) |
| 73 | | * Corcomp 512k (RAM and DSR ROM): port unknown |
| 74 | | * Super AMS 128k/512k: port 14 (0x1E00-0x1EFE) |
| 75 | | * Foundation 128k/512k: port 14 (0x1E00-0x1EFE) |
| 76 | | * Gram Karte: any port (0-15) |
| 77 | | * EVPC (video card): 0x1400-0x14FE |
| 78 | | * HSGPL (GROM replacement): 0x1B00-0x1BFE |
| 79 | | |
| 80 | | Of course, these devices additionally need some support routines, and |
| 81 | | possibly memory-mapped I/O registers. To do so, memory range 0x4000-5FFF |
| 82 | | is shared by all cards. The system enables each card as needed by writing |
| 83 | | a 1 to the first CRU bit: when this happens the card can safely enable its |
| 84 | | ROM, RAM, memory-mapped registers, etc. Provided the ROM uses the proper |
| 85 | | ROM header, the system will recognize and call peripheral I/O functions, |
| 86 | | interrupt service routines, and startup routines. (All these routines are |
| 87 | | generally called DSR = Device Service Routine.) |
| 88 | | |
| 89 | | Also, the cards can trigger level-1 ("INTA") interrupts. A LOAD interrupt |
| 90 | | is present on the side port of the ti-99/4(a), too, but it was never used |
| 91 | | by TI, AFAIK. Also, an interrupt line called "INTB" is found on several |
| 92 | | cards built by TI, but it is not connected: I am quite unsure whether it |
| 93 | | was intended to be connected to the LOAD line, to the INT2 line of TMS9985, |
| 94 | | or it was only to be used with the ti-99/4(a) development system running on |
| 95 | | a TI990/10. This is not the only unused feature: cards built by TI support |
| 96 | | sensing of an interrupt status register and 19-bit addressing, even though |
| 97 | | ti-99/4(A) does not take advantage of these features. TI documentation |
| 98 | | tells that some of these features are "defined by the Personal Computer PCC |
| 99 | | at Texas Instrument", which is not very explicit (What on earth does "PCC" |
| 100 | | stand for?). Maybe it refers to the ti-99/4(a) development system running |
| 101 | | on a TI990/10, or to the ti-99/7 and ti-99/8 prototypes. |
| 102 | | |
| 103 | | Note that I use 8-bit RAM handlers. Obviously, using 16-bit handlers would |
| 104 | | be equivalent and faster on a ti-99/4(a), but we need to interface the |
| 105 | | extension cards not only to the ti-99/4(a), but to the geneve and ti-99/8 |
| 106 | | emulators as well. |
| 107 | | */ |
| 108 | | |
| 109 | | #include "emu.h" |
| 110 | | #include "99_peb.h" |
| 111 | | |
| 112 | | /* TRUE if we are using the snug sgcpu 99/4p 16-bit extensions */ |
| 113 | | static int has_16bit_peb; |
| 114 | | |
| 115 | | /* handlers for each of 16 slots + 16 extra slots for ti-99/8 */ |
| 116 | | static ti99_peb_card_handlers_t expansion_ports[16+16]; |
| 117 | | |
| 118 | | enum width_t { |
| 119 | | width_8bit = 0, |
| 120 | | width_16bit |
| 121 | | }; |
| 122 | | /* expansion card structure for the snug sgcpu 99/4p system, which supports |
| 123 | | dynamical 16-bit accesses (TI did not design this!) */ |
| 124 | | typedef struct ti99_4p_peb_card_handlers_t |
| 125 | | { |
| 126 | | cru_read_handler cru_read; /* card CRU read handler */ |
| 127 | | cru_write_handler cru_write; /* card CRU handler */ |
| 128 | | |
| 129 | | width_t width; |
| 130 | | union |
| 131 | | { |
| 132 | | struct |
| 133 | | { |
| 134 | | read8_space_func mem_read; /* card mem read handler (8 bits) */ |
| 135 | | write8_space_func mem_write; /* card mem write handler (8 bits) */ |
| 136 | | } width_8bit; |
| 137 | | struct |
| 138 | | { |
| 139 | | read16_space_func mem_read; /* card mem read handler (16 bits) */ |
| 140 | | write16_space_func mem_write; /* card mem write handler (16 bits) */ |
| 141 | | } width_16bit; |
| 142 | | } w; |
| 143 | | } ti99_4p_peb_card_handlers_t; |
| 144 | | |
| 145 | | /* handlers for each of 28 slots */ |
| 146 | | static ti99_4p_peb_card_handlers_t ti99_4p_expansion_ports[28]; |
| 147 | | |
| 148 | | /* index of the currently active card (-1 if none) */ |
| 149 | | static int active_card; |
| 150 | | |
| 151 | | /* index of the currently active card, overridden by the mapper */ |
| 152 | | static int active_card_tmp; |
| 153 | | |
| 154 | | /* Mapper override. */ |
| 155 | | static int mapper_override; |
| 156 | | |
| 157 | | /* ila: inta status register (not actually used on ti-99/4(a), but nevertheless |
| 158 | | present) */ |
| 159 | | static int ila; |
| 160 | | /* ilb: intb status register (completely pointless on ti-99/4(a), as neither |
| 161 | | INTB nor SENILB are connected; OTOH, INTB can trigger interrupts on a Geneve, |
| 162 | | and snug sgcpu 99/4p can sense ILB) */ |
| 163 | | static int ilb; |
| 164 | | |
| 165 | | /* only used by the snug sgcpu 99/4p */ |
| 166 | | static int senila, senilb; |
| 167 | | |
| 168 | | /* hack to simulate TMS9900 byte write */ |
| 169 | | static int tmp_buffer; |
| 170 | | |
| 171 | | /* inta/intb handlers */ |
| 172 | | static void (*inta_callback)(running_machine *machine, int state); |
| 173 | | static void (*intb_callback)(running_machine *machine, int state); |
| 174 | | |
| 175 | | |
| 176 | | /* |
| 177 | | Initializes the expansion card handlers |
| 178 | | |
| 179 | | */ |
| 180 | | void ti99_peb_init() |
| 181 | | { |
| 182 | | memset(expansion_ports, 0, sizeof(expansion_ports)); |
| 183 | | memset(ti99_4p_expansion_ports, 0, sizeof(ti99_4p_expansion_ports)); |
| 184 | | } |
| 185 | | |
| 186 | | |
| 187 | | /* |
| 188 | | Resets the expansion card handlers |
| 189 | | |
| 190 | | in_has_16bit_peb: TRUE if we are using the snug sgcpu 99/4p 16-bit |
| 191 | | extensions |
| 192 | | in_inta_callback: callback called when the state of INTA changes (may be |
| 193 | | NULL) |
| 194 | | in_intb_callback: callback called when the state of INTB changes (may be |
| 195 | | NULL) |
| 196 | | */ |
| 197 | | void ti99_peb_reset(int in_has_16bit_peb, void (*in_inta_callback)(running_machine *machine, int state), void (*in_intb_callback)(running_machine *machine, int state)) |
| 198 | | { |
| 199 | | has_16bit_peb = in_has_16bit_peb; |
| 200 | | inta_callback = in_inta_callback; |
| 201 | | intb_callback = in_intb_callback; |
| 202 | | |
| 203 | | active_card = -1; |
| 204 | | active_card_tmp = -1; |
| 205 | | |
| 206 | | mapper_override = FALSE; // for ti99_4p |
| 207 | | |
| 208 | | ila = 0; |
| 209 | | ilb = 0; |
| 210 | | } |
| 211 | | |
| 212 | | /* |
| 213 | | Sets the handlers for one expansion port (normal 8-bit card) |
| 214 | | |
| 215 | | cru_base: CRU base address (any of 0x1000, 0x1100, 0x1200, ..., 0x1F00) |
| 216 | | (snug sgcpu 99/4p accepts 0x0400, ..., 0x1F00, ti-99/8 accepts 0x1000, |
| 217 | | ..., 0x2F00) |
| 218 | | handler: handler structure for the given card |
| 219 | | */ |
| 220 | | void ti99_peb_set_card_handlers(int cru_base, const ti99_peb_card_handlers_t *handler) |
| 221 | | { |
| 222 | | int port; |
| 223 | | |
| 224 | | if (cru_base & 0xff) |
| 225 | | return; |
| 226 | | |
| 227 | | if (has_16bit_peb) |
| 228 | | { |
| 229 | | port = (cru_base - 0x0400) >> 8; |
| 230 | | |
| 231 | | if ((port>=0) && (port<28)) |
| 232 | | { |
| 233 | | ti99_4p_expansion_ports[port].cru_read = handler->cru_read; |
| 234 | | ti99_4p_expansion_ports[port].cru_write = handler->cru_write; |
| 235 | | ti99_4p_expansion_ports[port].width = width_8bit; |
| 236 | | ti99_4p_expansion_ports[port].w.width_8bit.mem_read = handler->mem_read; |
| 237 | | ti99_4p_expansion_ports[port].w.width_8bit.mem_write = handler->mem_write; |
| 238 | | } |
| 239 | | } |
| 240 | | else |
| 241 | | { |
| 242 | | port = (cru_base - 0x1000) >> 8; |
| 243 | | |
| 244 | | if ((port>=0) && (port</*16*/32)) |
| 245 | | { |
| 246 | | expansion_ports[port] = *handler; |
| 247 | | } |
| 248 | | } |
| 249 | | } |
| 250 | | |
| 251 | | /* |
| 252 | | Sets the handlers for one expansion port (special 16-bit card for snug |
| 253 | | sgcpu 99/4p) |
| 254 | | |
| 255 | | cru_base: CRU base address (any of 0x1000, 0x1100, 0x1200, ..., 0x1F00) |
| 256 | | (snug sgcpu 99/4p accepts 0x0400, ..., 0x1F00, ti-99/8 accepts 0x1000, |
| 257 | | ..., 0x2F00) |
| 258 | | handler: handler structure for the given card |
| 259 | | */ |
| 260 | | void ti99_peb_set_16bit_card_handlers(int cru_base, const ti99_peb_16bit_card_handlers_t *handler) |
| 261 | | { |
| 262 | | int port; |
| 263 | | |
| 264 | | if (cru_base & 0xff) |
| 265 | | return; |
| 266 | | |
| 267 | | if (has_16bit_peb) |
| 268 | | { |
| 269 | | port = (cru_base - 0x0400) >> 8; |
| 270 | | |
| 271 | | if ((port>=0) && (port<28)) |
| 272 | | { |
| 273 | | ti99_4p_expansion_ports[port].cru_read = handler->cru_read; |
| 274 | | ti99_4p_expansion_ports[port].cru_write = handler->cru_write; |
| 275 | | ti99_4p_expansion_ports[port].width = width_16bit; |
| 276 | | ti99_4p_expansion_ports[port].w.width_16bit.mem_read = handler->mem_read; |
| 277 | | ti99_4p_expansion_ports[port].w.width_16bit.mem_write = handler->mem_write; |
| 278 | | } |
| 279 | | } |
| 280 | | } |
| 281 | | |
| 282 | | /* |
| 283 | | Update ila status register and assert or clear INTA interrupt line |
| 284 | | accordingly. |
| 285 | | |
| 286 | | bit: bit number ([0,7] for original INTA register, [8,15] are extra |
| 287 | | "virtual" bits for devices that assert the INTA line without setting a |
| 288 | | bit of the ILA register) |
| 289 | | state: 1 to assert bit, 0 to clear |
| 290 | | */ |
| 291 | | void ti99_peb_set_ila_bit(running_machine *machine, int bit, int state) |
| 292 | | { |
| 293 | | if (state) |
| 294 | | { |
| 295 | | ila |= 1 << bit; |
| 296 | | if (inta_callback) |
| 297 | | (*inta_callback)(machine, 1); |
| 298 | | } |
| 299 | | else |
| 300 | | { |
| 301 | | ila &= ~(1 << bit); |
| 302 | | if ((! ila) && inta_callback) |
| 303 | | (*inta_callback)(machine, 0); |
| 304 | | } |
| 305 | | } |
| 306 | | |
| 307 | | /* |
| 308 | | Update ilb status register and assert or clear INTB interrupt line |
| 309 | | accordingly. |
| 310 | | |
| 311 | | bit: bit number ([0,7] for original INTAB register, [8,15] are extra |
| 312 | | "virtual" bits for devices that assert the INTB line without setting a |
| 313 | | bit of the ILB register) |
| 314 | | state: 1 to assert bit, 0 to clear |
| 315 | | */ |
| 316 | | void ti99_peb_set_ilb_bit(running_machine *machine, int bit, int state) |
| 317 | | { |
| 318 | | if (state) |
| 319 | | { |
| 320 | | ilb |= 1 << bit; |
| 321 | | if (intb_callback) |
| 322 | | (*intb_callback)(machine, 1); |
| 323 | | } |
| 324 | | else |
| 325 | | { |
| 326 | | ilb &= ~(1 << bit); |
| 327 | | if ((! ilb) && intb_callback) |
| 328 | | (*intb_callback)(machine, 0); |
| 329 | | } |
| 330 | | } |
| 331 | | |
| 332 | | |
| 333 | | /* |
| 334 | | Read CRU in range >1000->1ffe (>800->fff) (ti-99/4(a)) |
| 335 | | */ |
| 336 | | READ8_HANDLER ( ti99_4x_peb_cru_r ) |
| 337 | | { |
| 338 | | int port; |
| 339 | | cru_read_handler handler; |
| 340 | | int reply; |
| 341 | | |
| 342 | | port = (offset & 0x00f0) >> 4; |
| 343 | | handler = expansion_ports[port].cru_read; |
| 344 | | |
| 345 | | reply = (handler) ? (*handler)(space->machine, offset & 0xf) : 0; |
| 346 | | |
| 347 | | return reply; |
| 348 | | } |
| 349 | | |
| 350 | | /* |
| 351 | | Write CRU in range >1000->1ffe (>800->fff) (ti-99/4(a)) |
| 352 | | */ |
| 353 | | WRITE8_HANDLER ( ti99_4x_peb_cru_w ) |
| 354 | | { |
| 355 | | int port; |
| 356 | | cru_write_handler handler; |
| 357 | | |
| 358 | | port = (offset & 0x0780) >> 7; |
| 359 | | handler = expansion_ports[port].cru_write; |
| 360 | | |
| 361 | | if (handler) |
| 362 | | (*handler)(space->machine, offset & 0x7f, data); |
| 363 | | |
| 364 | | /* expansion card enable? */ |
| 365 | | if ((offset & 0x7f) == 0) |
| 366 | | { |
| 367 | | if (data & 1) |
| 368 | | { |
| 369 | | /* enable */ |
| 370 | | active_card = port; |
| 371 | | } |
| 372 | | else |
| 373 | | { |
| 374 | | if (port == active_card) /* geez... who cares? */ |
| 375 | | { |
| 376 | | active_card = -1; /* no port selected */ |
| 377 | | } |
| 378 | | } |
| 379 | | } |
| 380 | | } |
| 381 | | |
| 382 | | /* |
| 383 | | Read mem in range >4000->5ffe (ti-99/4(a)) |
| 384 | | */ |
| 385 | | READ16_HANDLER ( ti99_4x_peb_r ) |
| 386 | | { |
| 387 | | int reply = 0; |
| 388 | | read8_space_func handler; |
| 389 | | |
| 390 | | cpu_adjust_icount(space->machine->firstcpu,-4); |
| 391 | | |
| 392 | | if (active_card != -1) |
| 393 | | { |
| 394 | | handler = expansion_ports[active_card].mem_read; |
| 395 | | if (handler) |
| 396 | | { |
| 397 | | reply = (*handler)(space, (offset << 1) + 1); |
| 398 | | reply |= ((unsigned) (*handler)(space, offset << 1)) << 8; |
| 399 | | } |
| 400 | | } |
| 401 | | // printf("[%04x:%04x] = %04x\n", (active_card<<8)+0x1000, (offset<<1)+0x4000, reply); |
| 402 | | |
| 403 | | return tmp_buffer = reply; |
| 404 | | } |
| 405 | | |
| 406 | | /* |
| 407 | | Write mem in range >4000->5ffe (ti-99/4(a)) |
| 408 | | */ |
| 409 | | WRITE16_HANDLER ( ti99_4x_peb_w ) |
| 410 | | { |
| 411 | | write8_space_func handler; |
| 412 | | |
| 413 | | cpu_adjust_icount(space->machine->firstcpu,-4); |
| 414 | | |
| 415 | | /* simulate byte write */ |
| 416 | | data = (tmp_buffer & ~mem_mask) | (data & mem_mask); |
| 417 | | |
| 418 | | if (active_card != -1) |
| 419 | | { |
| 420 | | handler = expansion_ports[active_card].mem_write; |
| 421 | | if (handler) |
| 422 | | { |
| 423 | | (*handler)(space, (offset << 1) + 1, data & 0xff); |
| 424 | | (*handler)(space, offset << 1, (data >> 8) & 0xff); |
| 425 | | } |
| 426 | | } |
| 427 | | } |
| 428 | | |
| 429 | | |
| 430 | | /* |
| 431 | | Read CRU in range >1000->1ffe (>800->fff) (Geneve) |
| 432 | | */ |
| 433 | | READ8_HANDLER ( geneve_peb_cru_r ) |
| 434 | | { |
| 435 | | int port; |
| 436 | | cru_read_handler handler; |
| 437 | | int reply; |
| 438 | | |
| 439 | | port = (offset & 0x00f0) >> 4; |
| 440 | | handler = expansion_ports[port].cru_read; |
| 441 | | |
| 442 | | reply = (handler) ? (*handler)(space->machine, offset & 0xf) : 0; |
| 443 | | |
| 444 | | return reply; |
| 445 | | } |
| 446 | | |
| 447 | | /* |
| 448 | | Write CRU in range >1000->1ffe (>800->fff) (Geneve) |
| 449 | | */ |
| 450 | | WRITE8_HANDLER ( geneve_peb_cru_w ) |
| 451 | | { |
| 452 | | int port; |
| 453 | | cru_write_handler handler; |
| 454 | | |
| 455 | | port = (offset & 0x0780) >> 7; |
| 456 | | handler = expansion_ports[port].cru_write; |
| 457 | | |
| 458 | | if (handler) |
| 459 | | (*handler)(space->machine, offset & 0x7f, data); |
| 460 | | |
| 461 | | /* expansion card enable? */ |
| 462 | | if ((offset & 0x7f) == 0) |
| 463 | | { |
| 464 | | if (data & 1) |
| 465 | | { |
| 466 | | /* enable */ |
| 467 | | active_card = port; |
| 468 | | } |
| 469 | | else |
| 470 | | { |
| 471 | | if (port == active_card) /* geez... who cares? */ |
| 472 | | { |
| 473 | | active_card = -1; /* no port selected */ |
| 474 | | } |
| 475 | | } |
| 476 | | } |
| 477 | | } |
| 478 | | |
| 479 | | /* |
| 480 | | Read mem in range >4000->5fff (Geneve) |
| 481 | | */ |
| 482 | | READ8_HANDLER ( geneve_peb_r ) |
| 483 | | { |
| 484 | | int reply = 0; |
| 485 | | read8_space_func handler; |
| 486 | | |
| 487 | | cpu_adjust_icount(space->machine->firstcpu,-8); |
| 488 | | |
| 489 | | if (active_card != -1) |
| 490 | | { |
| 491 | | handler = expansion_ports[active_card].mem_read; |
| 492 | | if (handler) |
| 493 | | reply = (*handler)(space, offset); |
| 494 | | } |
| 495 | | |
| 496 | | return reply; |
| 497 | | } |
| 498 | | |
| 499 | | /* |
| 500 | | Write mem in range >4000->5fff (Geneve) |
| 501 | | */ |
| 502 | | WRITE8_HANDLER ( geneve_peb_w ) |
| 503 | | { |
| 504 | | write8_space_func handler; |
| 505 | | |
| 506 | | cpu_adjust_icount(space->machine->firstcpu,-8); |
| 507 | | |
| 508 | | if (active_card != -1) |
| 509 | | { |
| 510 | | handler = expansion_ports[active_card].mem_write; |
| 511 | | if (handler) |
| 512 | | (*handler)(space, offset, data); |
| 513 | | } |
| 514 | | } |
| 515 | | |
| 516 | | /* |
| 517 | | Read CRU in range >1000->2ffe (>0800->17ff) (ti-99/8) |
| 518 | | */ |
| 519 | | READ8_HANDLER ( ti99_8_peb_cru_r ) |
| 520 | | { |
| 521 | | int port; |
| 522 | | cru_read_handler handler; |
| 523 | | int reply; |
| 524 | | |
| 525 | | port = (offset & 0x01f0) >> 4; |
| 526 | | handler = expansion_ports[port].cru_read; |
| 527 | | |
| 528 | | reply = (handler) ? (*handler)(space->machine, offset & 0xf) : 0; |
| 529 | | |
| 530 | | return reply; |
| 531 | | } |
| 532 | | |
| 533 | | /* |
| 534 | | Write CRU in range >1000->2ffe (>0800->17ff) (ti-99/8) |
| 535 | | */ |
| 536 | | WRITE8_HANDLER ( ti99_8_peb_cru_w ) |
| 537 | | { |
| 538 | | int port; |
| 539 | | cru_write_handler handler; |
| 540 | | |
| 541 | | port = (offset & 0x0f80) >> 7; |
| 542 | | handler = expansion_ports[port].cru_write; |
| 543 | | |
| 544 | | if (handler) |
| 545 | | (*handler)(space->machine, offset & 0x7f, data); |
| 546 | | |
| 547 | | /* expansion card enable? */ |
| 548 | | if ((offset & 0x7f) == 0) |
| 549 | | { |
| 550 | | if (data & 1) |
| 551 | | { |
| 552 | | /* enable */ |
| 553 | | active_card = port; |
| 554 | | } |
| 555 | | else |
| 556 | | { |
| 557 | | if (port == active_card) /* geez... who cares? */ |
| 558 | | { |
| 559 | | active_card = -1; /* no port selected */ |
| 560 | | } |
| 561 | | } |
| 562 | | } |
| 563 | | } |
| 564 | | |
| 565 | | /* |
| 566 | | Read mem in range >4000->5fff (ti-99/8) |
| 567 | | */ |
| 568 | | READ8_HANDLER ( ti99_8_peb_r ) |
| 569 | | { |
| 570 | | int reply = 0; |
| 571 | | read8_space_func handler; |
| 572 | | |
| 573 | | cpu_adjust_icount(space->machine->firstcpu,-4); |
| 574 | | |
| 575 | | if (active_card != -1) |
| 576 | | { |
| 577 | | handler = expansion_ports[active_card].mem_read; |
| 578 | | if (handler) |
| 579 | | reply = (*handler)(space, offset); |
| 580 | | } |
| 581 | | |
| 582 | | return reply; |
| 583 | | } |
| 584 | | |
| 585 | | /* |
| 586 | | Write mem in range >4000->5fff (ti-99/8) |
| 587 | | */ |
| 588 | | WRITE8_HANDLER ( ti99_8_peb_w ) |
| 589 | | { |
| 590 | | write8_space_func handler; |
| 591 | | |
| 592 | | cpu_adjust_icount(space->machine->firstcpu,-4); |
| 593 | | |
| 594 | | if (active_card != -1) |
| 595 | | { |
| 596 | | handler = expansion_ports[active_card].mem_write; |
| 597 | | if (handler) |
| 598 | | (*handler)(space, offset, data); |
| 599 | | } |
| 600 | | } |
| 601 | | |
| 602 | | /* |
| 603 | | Read CRU in range >0400->1ffe (>200->fff) (snug sgcpu 99/4p) |
| 604 | | */ |
| 605 | | READ8_HANDLER( ti99_4p_peb_cru_r ) |
| 606 | | { |
| 607 | | int port; |
| 608 | | cru_read_handler handler; |
| 609 | | int reply; |
| 610 | | |
| 611 | | port = offset >> 4; |
| 612 | | handler = ti99_4p_expansion_ports[port].cru_read; |
| 613 | | |
| 614 | | reply = (handler) ? (*handler)(space->machine, offset & 0xf) : 0; |
| 615 | | |
| 616 | | return reply; |
| 617 | | } |
| 618 | | |
| 619 | | /* |
| 620 | | Write CRU in range >0400->1ffe (>200->fff) (snug sgcpu 99/4p) |
| 621 | | */ |
| 622 | | WRITE8_HANDLER( ti99_4p_peb_cru_w ) |
| 623 | | { |
| 624 | | int port; |
| 625 | | cru_write_handler handler; |
| 626 | | |
| 627 | | int cru_address = (offset + 0x200)<<1; |
| 628 | | |
| 629 | | port = offset >> 7; |
| 630 | | handler = ti99_4p_expansion_ports[port].cru_write; |
| 631 | | |
| 632 | | if (handler) |
| 633 | | (*handler)(space->machine, offset & 0x7f, data); |
| 634 | | |
| 635 | | /* expansion card enable? */ |
| 636 | | if ((cru_address & 0xfe) == 0) |
| 637 | | { |
| 638 | | if (data & 1) |
| 639 | | { |
| 640 | | /* enable */ |
| 641 | | if (cru_address == 0x1e00) |
| 642 | | { |
| 643 | | // The internal mapper has precedence over the external bus. |
| 644 | | // It may be turned on although there is another device currently turned on |
| 645 | | active_card_tmp = active_card; |
| 646 | | mapper_override = TRUE; |
| 647 | | } |
| 648 | | active_card = port; |
| 649 | | } |
| 650 | | else |
| 651 | | { |
| 652 | | if ((cru_address == 0x1e00) && mapper_override) |
| 653 | | { |
| 654 | | active_card = active_card_tmp; |
| 655 | | mapper_override = FALSE; |
| 656 | | } |
| 657 | | else |
| 658 | | { |
| 659 | | if (port == active_card) /* geez... who cares? */ |
| 660 | | { |
| 661 | | active_card = -1; /* no port selected */ |
| 662 | | } |
| 663 | | } |
| 664 | | } |
| 665 | | } |
| 666 | | } |
| 667 | | |
| 668 | | /* |
| 669 | | Read mem in range >4000->5ffe (snug sgcpu 99/4p) |
| 670 | | */ |
| 671 | | READ16_HANDLER ( ti99_4p_peb_r ) |
| 672 | | { |
| 673 | | int reply = 0; |
| 674 | | read8_space_func handler; |
| 675 | | read16_space_func handler16; |
| 676 | | |
| 677 | | |
| 678 | | if (active_card == -1) |
| 679 | | cpu_adjust_icount(space->machine->firstcpu,-4); /* ??? */ |
| 680 | | else |
| 681 | | { |
| 682 | | |
| 683 | | if (ti99_4p_expansion_ports[active_card].width == width_8bit) |
| 684 | | { |
| 685 | | cpu_adjust_icount(space->machine->firstcpu,-4); |
| 686 | | |
| 687 | | handler = ti99_4p_expansion_ports[active_card].w.width_8bit.mem_read; |
| 688 | | if (handler) |
| 689 | | { |
| 690 | | reply = (*handler)(space, (offset << 1) + 1); |
| 691 | | reply |= ((unsigned) (*handler)(space, offset << 1)) << 8; |
| 692 | | } |
| 693 | | } |
| 694 | | else |
| 695 | | { |
| 696 | | cpu_adjust_icount(space->machine->firstcpu,-1); /* ??? */ |
| 697 | | |
| 698 | | handler16 = ti99_4p_expansion_ports[active_card].w.width_16bit.mem_read; |
| 699 | | if (handler16) |
| 700 | | reply = (*handler16)(space, offset, /*mem_mask*/0xffff); |
| 701 | | } |
| 702 | | } |
| 703 | | |
| 704 | | if (senila || senilb) |
| 705 | | { |
| 706 | | if ((active_card != -1) || (senila && senilb)) |
| 707 | | /* ah, the smell of burnt silicon... */ |
| 708 | | logerror("<Scrrrrr>: your computer has just burnt (maybe).\n"); |
| 709 | | |
| 710 | | if (senila) |
| 711 | | reply = (ila & 0xff) | (ila << 8); |
| 712 | | else if (senilb) |
| 713 | | reply = (ilb & 0xff) | (ilb << 8); |
| 714 | | } |
| 715 | | |
| 716 | | // printf("[%04x:%04x] = %04x\n", (active_card<<8)+0x400, (offset<<1)+0x4000, reply); |
| 717 | | return tmp_buffer = reply; |
| 718 | | } |
| 719 | | |
| 720 | | /* |
| 721 | | Write mem in range >4000->5ffe (snug sgcpu 99/4p) |
| 722 | | */ |
| 723 | | WRITE16_HANDLER ( ti99_4p_peb_w ) |
| 724 | | { |
| 725 | | write8_space_func handler; |
| 726 | | write16_space_func handler16; |
| 727 | | |
| 728 | | if (active_card == -1) |
| 729 | | cpu_adjust_icount(space->machine->firstcpu,-4); /* ??? */ |
| 730 | | else |
| 731 | | { |
| 732 | | /* simulate byte write */ |
| 733 | | data = (tmp_buffer & ~mem_mask) | (data & mem_mask); |
| 734 | | |
| 735 | | if (ti99_4p_expansion_ports[active_card].width == width_8bit) |
| 736 | | { |
| 737 | | cpu_adjust_icount(space->machine->firstcpu,-4); |
| 738 | | |
| 739 | | handler = ti99_4p_expansion_ports[active_card].w.width_8bit.mem_write; |
| 740 | | if (handler) |
| 741 | | { |
| 742 | | (*handler)(space, (offset << 1) + 1, data & 0xff); |
| 743 | | (*handler)(space, offset << 1, (data >> 8) & 0xff); |
| 744 | | } |
| 745 | | } |
| 746 | | else |
| 747 | | { |
| 748 | | cpu_adjust_icount(space->machine->firstcpu,-1); /* ??? */ |
| 749 | | |
| 750 | | handler16 = ti99_4p_expansion_ports[active_card].w.width_16bit.mem_write; |
| 751 | | if (handler16) |
| 752 | | (*handler16)(space, offset, data, /*mem_mask*/0xffff); |
| 753 | | } |
| 754 | | } |
| 755 | | } |
| 756 | | |
| 757 | | /* |
| 758 | | Set the state of the SENILA line (snug sgcpu 99/4p) |
| 759 | | */ |
| 760 | | void ti99_4p_peb_set_senila(int state) |
| 761 | | { |
| 762 | | senila = (state != 0); |
| 763 | | } |
| 764 | | |
| 765 | | /* |
| 766 | | Set the state of the SENILB line (snug sgcpu 99/4p) |
| 767 | | */ |
| 768 | | void ti99_4p_peb_set_senilb(int state) |
| 769 | | { |
| 770 | | senilb = (state != 0); |
| 771 | | } |
src/mess/machine/geneve.c
| r9500 | r9501 | |
| 1 | | /* |
| 2 | | Machine code for Myarc Geneve. |
| 3 | | Raphael Nabet, 2003. |
| 4 | | */ |
| 5 | | |
| 6 | | #include <math.h> |
| 7 | | #include "emu.h" |
| 8 | | #include "tms9901.h" |
| 9 | | #include "mm58274c.h" |
| 10 | | #include "video/v9938.h" |
| 11 | | #include "audio/spchroms.h" |
| 12 | | #include "ti99_4x.h" |
| 13 | | #include "includes/geneve.h" |
| 14 | | #include "devices/ti99_peb.h" |
| 15 | | #include "994x_ser.h" |
| 16 | | #include "99_dsk.h" |
| 17 | | #include "99_ide.h" |
| 18 | | #include "99_usbsm.h" |
| 19 | | |
| 20 | | #include "sound/tms5220.h" /* for tms5220_set_variant() */ |
| 21 | | #include "sound/sn76496.h" |
| 22 | | |
| 23 | | static void read_key_if_possible(running_machine *machine); |
| 24 | | static void poll_keyboard(running_machine *machine); |
| 25 | | static void poll_mouse(running_machine *machine); |
| 26 | | |
| 27 | | /* |
| 28 | | pointers to memory areas |
| 29 | | */ |
| 30 | | /* pointer to boot ROM */ |
| 31 | | static UINT8 *ROM_ptr; |
| 32 | | /* pointer to alternative boot ROM */ |
| 33 | | static UINT8 *AROM_ptr; |
| 34 | | /* pointer to static RAM */ |
| 35 | | static UINT8 *SRAM_ptr; |
| 36 | | /* pointer to dynamic RAM */ |
| 37 | | static UINT8 *DRAM_ptr; |
| 38 | | |
| 39 | | /* |
| 40 | | Configuration |
| 41 | | */ |
| 42 | | /* TRUE if speech synthesizer present */ |
| 43 | | static char has_speech; |
| 44 | | |
| 45 | | /* TRUE if ide card present */ |
| 46 | | static char has_ide; |
| 47 | | /* TRUE if rs232 card present */ |
| 48 | | static char has_rs232; |
| 49 | | /* TRUE if genmod extension present */ |
| 50 | | /*static char has_genmod;*/ |
| 51 | | /* TRUE if usb-sm card present */ |
| 52 | | static char has_usb_sm; |
| 53 | | |
| 54 | | /* keyboard interface */ |
| 55 | | static int JoySel; |
| 56 | | enum |
| 57 | | { |
| 58 | | KeyQueueSize = 256, |
| 59 | | MaxKeyMessageLen = 10, |
| 60 | | KeyAutoRepeatDelay = 30, |
| 61 | | KeyAutoRepeatRate = 6 |
| 62 | | }; |
| 63 | | static UINT8 KeyQueue[KeyQueueSize]; |
| 64 | | static int KeyQueueHead; |
| 65 | | static int KeyQueueLen; |
| 66 | | static int KeyInBuf; |
| 67 | | static int KeyReset; |
| 68 | | static UINT32 KeyStateSave[4]; |
| 69 | | static int KeyNumLockState; |
| 70 | | static int KeyCtrlState; |
| 71 | | static int KeyAltState; |
| 72 | | static int KeyRealShiftState; |
| 73 | | static int KeyFakeShiftState; |
| 74 | | static int KeyFakeUnshiftState; |
| 75 | | static int KeyAutoRepeatKey; |
| 76 | | static int KeyAutoRepeatTimer; |
| 77 | | |
| 78 | | static running_device *expansion_box; |
| 79 | | |
| 80 | | /* |
| 81 | | GROM support. |
| 82 | | |
| 83 | | The Geneve does not include any GROM, but it features a simple GROM emulator. |
| 84 | | |
| 85 | | One oddity is that, since the Geneve does not use a look-ahead buffer, GPL |
| 86 | | data is always off one byte, i.e. GPL bytes 0, 1, 2... are stored in bytes |
| 87 | | 1, 2, 3... of pages >38->3f (byte 0 of page >38 is has GPL address >ffff). |
| 88 | | |
| 89 | | I think that I have once read that the geneve GROM emulator does not |
| 90 | | emulate wrap-around within a GROM, i.e. address >1fff is followed by >2000 |
| 91 | | (instead of >0000 with a real GROM). |
| 92 | | */ |
| 93 | | static struct |
| 94 | | { |
| 95 | | unsigned int addr; |
| 96 | | char raddr_LSB, waddr_LSB; |
| 97 | | } GPL_port; |
| 98 | | |
| 99 | | /* |
| 100 | | Cartridge support |
| 101 | | |
| 102 | | The Geneve does not have a cartridge port, but it has cartridge emulation. |
| 103 | | */ |
| 104 | | static int cartridge_page; |
| 105 | | |
| 106 | | |
| 107 | | static int mode_flags; |
| 108 | | |
| 109 | | static UINT8 page_lookup[8]; |
| 110 | | |
| 111 | | enum |
| 112 | | { |
| 113 | | mf_PAL = 0x001, /* bit 5 */ |
| 114 | | mf_keycaps = 0x004, /* bit 7 - exact meaning unknown */ |
| 115 | | mf_keyclock = 0x008, /* bit 8 */ |
| 116 | | mf_keyclear = 0x010, /* bit 9 */ |
| 117 | | mf_genevemode = 0x020, /* bit 10 */ |
| 118 | | mf_mapmode = 0x040, /* bit 11 */ |
| 119 | | mf_cartpaging = 0x080, /* bit 12 */ |
| 120 | | mf_cart6protect = 0x100, /* bit 13 */ |
| 121 | | mf_cart7protect = 0x200, /* bit 14 */ |
| 122 | | mf_waitstate = 0x400 /* bit 15 - exact meaning unknown */ |
| 123 | | }; |
| 124 | | |
| 125 | | /* tms9995_ICount: used to implement memory waitstates (hack) */ |
| 126 | | /* NPW 23-Feb-2004 - externs no longer needed because we now use cpu_adjust_icount(space->machine->device("maincpu"),) */ |
| 127 | | |
| 128 | | enum |
| 129 | | { |
| 130 | | BOOT_V09 = 0, |
| 131 | | BOOT_V10 = 1 |
| 132 | | }; |
| 133 | | |
| 134 | | /*===========================================================================*/ |
| 135 | | /* |
| 136 | | General purpose code: |
| 137 | | initialization, cart loading, etc. |
| 138 | | */ |
| 139 | | |
| 140 | | DRIVER_INIT( geneve ) |
| 141 | | { |
| 142 | | /*has_genmod = FALSE;*/ |
| 143 | | } |
| 144 | | |
| 145 | | DRIVER_INIT( genmod ) |
| 146 | | { |
| 147 | | /*has_genmod = TRUE;*/ |
| 148 | | } |
| 149 | | |
| 150 | | MACHINE_START( geneve ) |
| 151 | | { |
| 152 | | /* Initialize all. Actually, at this point, we don't know |
| 153 | | how the switches are set. Later we use the configuration switches to |
| 154 | | determine which one to use. */ |
| 155 | | ti99_floppy_controllers_init_all(machine); |
| 156 | | ti99_ide_init(machine); |
| 157 | | ti99_usbsm_init(machine); |
| 158 | | |
| 159 | | /* set up RAM pointers */ |
| 160 | | ROM_ptr = memory_region(machine, "maincpu") + offset_rom_geneve; |
| 161 | | AROM_ptr = memory_region(machine, "maincpu") + offset_altrom_geneve; |
| 162 | | SRAM_ptr = memory_region(machine, "maincpu") + offset_sram_geneve; |
| 163 | | DRAM_ptr = memory_region(machine, "maincpu") + offset_dram_geneve; |
| 164 | | } |
| 165 | | |
| 166 | | MACHINE_RESET( geneve ) |
| 167 | | { |
| 168 | | mode_flags = /*0*/mf_mapmode; |
| 169 | | /* initialize page lookup */ |
| 170 | | memset(page_lookup, 0, sizeof(page_lookup)); |
| 171 | | |
| 172 | | /* Initialize GROMs */ |
| 173 | | memset(& GPL_port, 0, sizeof(GPL_port)); |
| 174 | | |
| 175 | | /* reset cartridge mapper */ |
| 176 | | cartridge_page = 0; |
| 177 | | |
| 178 | | expansion_box = machine->device("per_exp_box"); |
| 179 | | |
| 180 | | v9938_reset(0); |
| 181 | | |
| 182 | | /* clear keyboard interface state (probably overkill, but can't harm) */ |
| 183 | | JoySel = 0; |
| 184 | | KeyQueueHead = KeyQueueLen = 0; |
| 185 | | memset(KeyStateSave, 0, sizeof(KeyStateSave)); |
| 186 | | KeyNumLockState = 0; |
| 187 | | KeyCtrlState = 0; |
| 188 | | KeyAltState = 0; |
| 189 | | KeyRealShiftState = 0; |
| 190 | | KeyFakeShiftState = 0; |
| 191 | | KeyFakeUnshiftState = 0; |
| 192 | | KeyAutoRepeatKey = 0; |
| 193 | | KeyInBuf = 0; |
| 194 | | KeyReset = 1; |
| 195 | | |
| 196 | | /* read config */ |
| 197 | | has_speech = input_port_read(machine, "SPEECH"); |
| 198 | | has_ide = input_port_read(machine, "HDCTRL") & HD_IDE; |
| 199 | | has_rs232 = input_port_read(machine, "SERIAL") & SERIAL_TI; |
| 200 | | has_usb_sm = input_port_read(machine, "HDCTRL") & HD_USB; |
| 201 | | |
| 202 | | /* set up optional expansion hardware */ |
| 203 | | |
| 204 | | if (has_speech) |
| 205 | | { |
| 206 | | static const spchroms_interface speech_intf = { region_speech_rom }; |
| 207 | | spchroms_config(machine, &speech_intf); |
| 208 | | } |
| 209 | | |
| 210 | | switch (input_port_read(machine, "DISKCTRL")) |
| 211 | | { |
| 212 | | case DISK_TIFDC: |
| 213 | | ti99_fdc_reset(machine); |
| 214 | | break; |
| 215 | | #if HAS_99CCFDC |
| 216 | | case DISK_CC: |
| 217 | | ti99_ccfdc_reset(machine); |
| 218 | | break; |
| 219 | | #endif |
| 220 | | case DISK_BWG: |
| 221 | | ti99_bwg_reset(machine); |
| 222 | | break; |
| 223 | | case DISK_HFDC: |
| 224 | | ti99_hfdc_reset(machine); |
| 225 | | break; |
| 226 | | case DISK_NONE: |
| 227 | | break; |
| 228 | | } |
| 229 | | |
| 230 | | if (has_ide) |
| 231 | | { |
| 232 | | ti99_ide_reset(machine, TRUE); |
| 233 | | } |
| 234 | | |
| 235 | | if (has_usb_sm) |
| 236 | | ti99_usbsm_reset(machine, TRUE); |
| 237 | | |
| 238 | | /* reset CPU */ |
| 239 | | machine->device("maincpu")->reset(); |
| 240 | | } |
| 241 | | |
| 242 | | /* |
| 243 | | video initialization. |
| 244 | | */ |
| 245 | | VIDEO_START(geneve) |
| 246 | | { |
| 247 | | VIDEO_START_CALL(generic_bitmapped); |
| 248 | | v9938_init(machine, 0, *machine->primary_screen, machine->generic.tmpbitmap, MODEL_V9938, /*0x20000*/0x30000, tms9901_set_int2); /* v38 with 128 kb of video RAM */ |
| 249 | | } |
| 250 | | |
| 251 | | /* |
| 252 | | scanline interrupt |
| 253 | | */ |
| 254 | | INTERRUPT_GEN( geneve_hblank_interrupt ) |
| 255 | | { |
| 256 | | static int line_count; |
| 257 | | v9938_interrupt(device->machine, 0); |
| 258 | | if (++line_count == 262) |
| 259 | | { |
| 260 | | line_count = 0; |
| 261 | | poll_keyboard(device->machine); |
| 262 | | poll_mouse(device->machine); |
| 263 | | } |
| 264 | | } |
| 265 | | |
| 266 | | /* |
| 267 | | inta is connected to both tms9901 IRQ1 line and to tms9995 INT4/EC line. |
| 268 | | */ |
| 269 | | void inta_callback(running_machine *machine, int state) |
| 270 | | { |
| 271 | | tms9901_set_single_int(machine->device("tms9901"), 1, state); |
| 272 | | cputag_set_input_line(machine, "maincpu", 1, state ? ASSERT_LINE : CLEAR_LINE); |
| 273 | | } |
| 274 | | |
| 275 | | /* |
| 276 | | intb is connected to tms9901 IRQ12 line. |
| 277 | | */ |
| 278 | | void intb_callback(running_machine *machine, int state) |
| 279 | | { |
| 280 | | tms9901_set_single_int(machine->device("tms9901"), 12, state); |
| 281 | | } |
| 282 | | |
| 283 | | |
| 284 | | /*===========================================================================*/ |
| 285 | | #if 0 |
| 286 | | #pragma mark - |
| 287 | | #pragma mark MEMORY HANDLERS |
| 288 | | #endif |
| 289 | | /* |
| 290 | | Memory handlers. |
| 291 | | */ |
| 292 | | |
| 293 | | /* |
| 294 | | TMS5200 speech chip read |
| 295 | | */ |
| 296 | | static READ8_HANDLER ( geneve_speech_r ) |
| 297 | | { |
| 298 | | cpu_adjust_icount(space->machine->device("maincpu"),-8); /* this is just a minimum, it can be more */ |
| 299 | | |
| 300 | | return tms5220_status_r(space->machine->device("tmc0285"), offset); |
| 301 | | } |
| 302 | | |
| 303 | | #if 0 |
| 304 | | |
| 305 | | static void speech_kludge_callback(int dummy) |
| 306 | | { |
| 307 | | if (! tms5220_readyq_r(space->machine->device("tmc0285"))) |
| 308 | | { |
| 309 | | /* Weirdly enough, we are always seeing some problems even though |
| 310 | | everything is working fine. */ |
| 311 | | #if 0 |
| 312 | | attotime time_to_ready = double_to_attotime(tms5220_time_to_ready()); |
| 313 | | logerror("ti99/4a speech says aaargh!\n"); |
| 314 | | logerror("(time to ready: %f -> %d)\n", time_to_ready, (int) ceil(3000000*time_to_ready)); |
| 315 | | #endif |
| 316 | | } |
| 317 | | } |
| 318 | | |
| 319 | | #endif |
| 320 | | |
| 321 | | /* |
| 322 | | TMS5200 speech chip write |
| 323 | | */ |
| 324 | | static WRITE8_HANDLER ( geneve_speech_w ) |
| 325 | | { |
| 326 | | cpu_adjust_icount(space->machine->device("maincpu"),-32*4); /* this is just an approx. minimum, it can be much more */ |
| 327 | | |
| 328 | | #if 1 |
| 329 | | /* the stupid design of the tms5220 core means that ready is cleared when |
| 330 | | there are 15 bytes in FIFO. It should be 16. Of course, if it were the |
| 331 | | case, we would need to store the value on the bus, which would be more |
| 332 | | complex. */ |
| 333 | | if (! tms5220_readyq_r(space->machine->device("tmc0285"))) |
| 334 | | { |
| 335 | | attotime time_to_ready = double_to_attotime(tms5220_time_to_ready(space->machine->device("tmc0285"))); |
| 336 | | int cycles_to_ready = space->machine->device<cpu_device>("maincpu")->attotime_to_cycles(time_to_ready); |
| 337 | | |
| 338 | | logerror("time to ready: %f -> %d\n", attotime_to_double(time_to_ready), (int) cycles_to_ready); |
| 339 | | |
| 340 | | cpu_adjust_icount(space->machine->device("maincpu"),-cycles_to_ready); |
| 341 | | timer_set(space->machine, attotime_zero, NULL, 0, /*speech_kludge_callback*/NULL); |
| 342 | | } |
| 343 | | #endif |
| 344 | | |
| 345 | | tms5220_data_w(space->machine->device("tmc0285"), offset, data); |
| 346 | | } |
| 347 | | |
| 348 | | READ8_HANDLER ( geneve_r ) |
| 349 | | { |
| 350 | | int page; |
| 351 | | |
| 352 | | /* 03-Apr-2007 - hack because this seems to be called on startup */ |
| 353 | | if (!DRAM_ptr) |
| 354 | | return 0; |
| 355 | | |
| 356 | | if (mode_flags & mf_genevemode) |
| 357 | | { |
| 358 | | /* geneve mode */ |
| 359 | | if ((offset >= 0xf100) && (offset < 0xf140)) |
| 360 | | { |
| 361 | | /* memory-mapped registers */ |
| 362 | | /* VDP ports 2 & 3 and sound port are write-only */ |
| 363 | | switch (offset) |
| 364 | | { |
| 365 | | case 0xf100: |
| 366 | | case 0xf108: /* mirror? */ |
| 367 | | return v9938_0_vram_r(space, 0); |
| 368 | | |
| 369 | | case 0xf102: |
| 370 | | case 0xf10a: /* mirror? */ |
| 371 | | return v9938_0_status_r(space, 0); |
| 372 | | |
| 373 | | case 0xf110: |
| 374 | | case 0xf111: |
| 375 | | case 0xf112: |
| 376 | | case 0xf113: |
| 377 | | case 0xf114: |
| 378 | | case 0xf115: |
| 379 | | case 0xf116: |
| 380 | | case 0xf117: |
| 381 | | return page_lookup[offset-0xf110]; |
| 382 | | |
| 383 | | case 0xf118: |
| 384 | | return KeyInBuf ? KeyQueue[KeyQueueHead] : 0; |
| 385 | | |
| 386 | | case 0xf130: |
| 387 | | case 0xf131: |
| 388 | | case 0xf132: |
| 389 | | case 0xf133: |
| 390 | | case 0xf134: |
| 391 | | case 0xf135: |
| 392 | | case 0xf136: |
| 393 | | case 0xf137: |
| 394 | | case 0xf138: |
| 395 | | case 0xf139: |
| 396 | | case 0xf13a: |
| 397 | | case 0xf13b: |
| 398 | | case 0xf13c: |
| 399 | | case 0xf13d: |
| 400 | | case 0xf13e: |
| 401 | | case 0xf13f: |
| 402 | | return mm58274c_r(space->machine->device("mm58274c"), offset-0xf130); |
| 403 | | |
| 404 | | default: |
| 405 | | logerror("unmapped read offs=%d\n", (int) offset); |
| 406 | | return 0; |
| 407 | | } |
| 408 | | } |
| 409 | | } |
| 410 | | else |
| 411 | | { |
| 412 | | /* ti99 mode */ |
| 413 | | if ((offset >= 0x8000) && (offset < 0x8020)) |
| 414 | | { |
| 415 | | /* 0x8000-0x801f */ |
| 416 | | /* geneve memory-mapped registers */ |
| 417 | | switch (offset) |
| 418 | | { |
| 419 | | case 0x8000: |
| 420 | | case 0x8001: |
| 421 | | case 0x8002: |
| 422 | | case 0x8003: |
| 423 | | case 0x8004: |
| 424 | | case 0x8005: |
| 425 | | case 0x8006: |
| 426 | | case 0x8007: |
| 427 | | return page_lookup[offset-0x8000]; |
| 428 | | |
| 429 | | case 0x8008: |
| 430 | | return KeyInBuf ? KeyQueue[KeyQueueHead] : 0; |
| 431 | | |
| 432 | | case 0x8010: |
| 433 | | case 0x8011: |
| 434 | | case 0x8012: |
| 435 | | case 0x8013: |
| 436 | | case 0x8014: |
| 437 | | case 0x8015: |
| 438 | | case 0x8016: |
| 439 | | case 0x8017: |
| 440 | | case 0x8018: |
| 441 | | case 0x8019: |
| 442 | | case 0x801a: |
| 443 | | case 0x801b: |
| 444 | | case 0x801c: |
| 445 | | case 0x801d: |
| 446 | | case 0x801e: |
| 447 | | case 0x801f: |
| 448 | | return mm58274c_r(space->machine->device("mm58274c"), offset-0xf130); |
| 449 | | |
| 450 | | default: |
| 451 | | logerror("unmapped read offs=%d\n", (int) offset); |
| 452 | | return 0; |
| 453 | | } |
| 454 | | } |
| 455 | | else if ((offset >= 0x8400) && (offset < 0xa000)) |
| 456 | | { |
| 457 | | /* 0x8400-0x9fff */ |
| 458 | | /* ti99 memory-mapped registers */ |
| 459 | | switch ((offset - 0x8000) >> 10) |
| 460 | | { |
| 461 | | case 2: |
| 462 | | /* VDP read */ |
| 463 | | if (! (offset & 1)) |
| 464 | | { |
| 465 | | if (offset & 2) |
| 466 | | { /* read VDP status */ |
| 467 | | return v9938_0_status_r(space, 0); |
| 468 | | } |
| 469 | | else |
| 470 | | { /* read VDP RAM */ |
| 471 | | return v9938_0_vram_r(space, 0); |
| 472 | | } |
| 473 | | } |
| 474 | | return 0; |
| 475 | | |
| 476 | | case 4: |
| 477 | | /* speech read */ |
| 478 | | if ((! (offset & 1)) && has_speech) |
| 479 | | { |
| 480 | | return geneve_speech_r(space, 0); |
| 481 | | } |
| 482 | | return 0; |
| 483 | | |
| 484 | | case 6: |
| 485 | | /* GPL read */ |
| 486 | | if (! (offset & 1)) |
| 487 | | { |
| 488 | | if (offset & 2) |
| 489 | | { /* read GPL address */ |
| 490 | | int reply; |
| 491 | | |
| 492 | | GPL_port.waddr_LSB = FALSE; /* right??? */ |
| 493 | | |
| 494 | | if (GPL_port.raddr_LSB) |
| 495 | | { |
| 496 | | reply = GPL_port.addr & 0xff; |
| 497 | | GPL_port.raddr_LSB = FALSE; |
| 498 | | } |
| 499 | | else |
| 500 | | { |
| 501 | | reply = GPL_port.addr >> 8; |
| 502 | | GPL_port.raddr_LSB = TRUE; |
| 503 | | } |
| 504 | | return reply; |
| 505 | | } |
| 506 | | else |
| 507 | | { /* read GPL data */ |
| 508 | | int reply; |
| 509 | | |
| 510 | | reply = DRAM_ptr[0x38*0x2000 + GPL_port.addr]; |
| 511 | | GPL_port.addr = (GPL_port.addr + 1) & 0xffff; |
| 512 | | GPL_port.raddr_LSB = GPL_port.waddr_LSB = FALSE; |
| 513 | | |
| 514 | | return reply; |
| 515 | | } |
| 516 | | } |
| 517 | | return 0; |
| 518 | | |
| 519 | | default: |
| 520 | | logerror("unmapped read offs=%d\n", (int) offset); |
| 521 | | return 0; |
| 522 | | } |
| 523 | | } |
| 524 | | } |
| 525 | | |
| 526 | | page = offset >> 13; |
| 527 | | |
| 528 | | if (! (mode_flags & mf_mapmode)) |
| 529 | | { |
| 530 | | if ((mode_flags & mf_genevemode) || (page != 3)) |
| 531 | | page = page_lookup[page]; |
| 532 | | else |
| 533 | | /* Cartridge page under ti99 emulation */ |
| 534 | | page = ((mode_flags & mf_cartpaging) || ! cartridge_page) ? 0x36 : 0x37; |
| 535 | | } |
| 536 | | else |
| 537 | | page = page + 0xf0; |
| 538 | | |
| 539 | | offset &= 0x1fff; |
| 540 | | |
| 541 | | /* Although we could have 128K boot ROM (page f0...ff), the stock |
| 542 | | Geneve only has 16K. The address is incompletely decoded, so page |
| 543 | | f0 is mirrored on all even pages up to fe, and f1 is mirrored on |
| 544 | | f3, f5, ... ff. |
| 545 | | Currently, MESS is delivered with the 16K ROM only. So for the time |
| 546 | | being, we won't lose much if we mask the page bits. |
| 547 | | Michael Zapf, 2008-01-23 |
| 548 | | */ |
| 549 | | if (page > 0xf1) page &= 0xf1; |
| 550 | | |
| 551 | | switch (page) |
| 552 | | { |
| 553 | | case 0xf0: |
| 554 | | case 0xf1: |
| 555 | | /* Boot ROM */ |
| 556 | | if (input_port_read(space->machine, "BOOTROM")==BOOT_V09) |
| 557 | | return AROM_ptr[(page-0xf0)*0x2000 + offset]; |
| 558 | | else |
| 559 | | return ROM_ptr[(page-0xf0)*0x2000 + offset]; |
| 560 | | |
| 561 | | case 0xe8: |
| 562 | | case 0xe9: |
| 563 | | case 0xea: |
| 564 | | case 0xeb: |
| 565 | | case 0xec: |
| 566 | | case 0xed: |
| 567 | | case 0xee: |
| 568 | | case 0xef: |
| 569 | | /* SRAM */ |
| 570 | | return SRAM_ptr[(page-0xe8)*0x2000 + offset]; |
| 571 | | |
| 572 | | #if 0 |
| 573 | | case 0x7a: /* mirror of 0xba??? */ |
| 574 | | return 0; |
| 575 | | #endif |
| 576 | | case 0xba: |
| 577 | | /* DSR space */ |
| 578 | | return geneve_peb_r(expansion_box, offset); |
| 579 | | |
| 580 | | case 0xbc: |
| 581 | | /* speech space */ |
| 582 | | if (has_speech) |
| 583 | | { |
| 584 | | if ((offset >= 0x1000) && (offset < 0x1400) && (! (offset & 1))) |
| 585 | | return geneve_speech_r(space, 0); |
| 586 | | else |
| 587 | | return 0; |
| 588 | | } |
| 589 | | |
| 590 | | default: |
| 591 | | /* DRAM */ |
| 592 | | return DRAM_ptr[page*0x2000 + offset]; |
| 593 | | } |
| 594 | | |
| 595 | | logerror("unmapped read page=%d offs=%d\n", (int) page, (int) offset); |
| 596 | | return 0; |
| 597 | | } |
| 598 | | |
| 599 | | WRITE8_HANDLER ( geneve_w ) |
| 600 | | { |
| 601 | | int page; |
| 602 | | |
| 603 | | if (mode_flags & mf_genevemode) |
| 604 | | { |
| 605 | | /* geneve mode */ |
| 606 | | if ((offset >= 0xf100) && (offset < 0xf140)) |
| 607 | | { |
| 608 | | /* memory-mapped registers */ |
| 609 | | /* VDP ports 2 & 3 and sound port are write-only */ |
| 610 | | switch (offset) |
| 611 | | { |
| 612 | | case 0xf100: |
| 613 | | case 0xf108: /* mirror? */ |
| 614 | | v9938_0_vram_w(space, 0, data); |
| 615 | | return; |
| 616 | | |
| 617 | | case 0xf102: |
| 618 | | case 0xf10a: /* mirror? */ |
| 619 | | v9938_0_command_w(space, 0, data); |
| 620 | | return; |
| 621 | | |
| 622 | | case 0xf104: |
| 623 | | case 0xf10c: /* mirror? */ |
| 624 | | v9938_0_palette_w(space, 0, data); |
| 625 | | return; |
| 626 | | |
| 627 | | case 0xf106: |
| 628 | | case 0xf10e: /* mirror? */ |
| 629 | | v9938_0_register_w(space, 0, data); |
| 630 | | return; |
| 631 | | |
| 632 | | case 0xf110: |
| 633 | | case 0xf111: |
| 634 | | case 0xf112: |
| 635 | | case 0xf113: |
| 636 | | case 0xf114: |
| 637 | | case 0xf115: |
| 638 | | case 0xf116: |
| 639 | | case 0xf117: |
| 640 | | page_lookup[offset-0xf110] = data; |
| 641 | | return; |
| 642 | | |
| 643 | | #if 0 |
| 644 | | case 0xf118: // read-only register??? |
| 645 | | key_buf = data; |
| 646 | | return; |
| 647 | | #endif |
| 648 | | |
| 649 | | case 0xf120: |
| 650 | | sn76496_w(space->machine->device("sn76496"), 0, data); |
| 651 | | break; |
| 652 | | |
| 653 | | case 0xf130: |
| 654 | | case 0xf131: |
| 655 | | case 0xf132: |
| 656 | | case 0xf133: |
| 657 | | case 0xf134: |
| 658 | | case 0xf135: |
| 659 | | case 0xf136: |
| 660 | | case 0xf137: |
| 661 | | case 0xf138: |
| 662 | | case 0xf139: |
| 663 | | case 0xf13a: |
| 664 | | case 0xf13b: |
| 665 | | case 0xf13c: |
| 666 | | case 0xf13d: |
| 667 | | case 0xf13e: |
| 668 | | case 0xf13f: |
| 669 | | mm58274c_w(space->machine->device("mm58274c"), offset-0xf130, data); |
| 670 | | return; |
| 671 | | |
| 672 | | default: |
| 673 | | logerror("unmapped write offs=%d data=%d\n", (int) offset, (int) data); |
| 674 | | return; |
| 675 | | } |
| 676 | | } |
| 677 | | } |
| 678 | | else |
| 679 | | { |
| 680 | | /* ti99 mode */ |
| 681 | | if ((offset >= 0x8000) && (offset < 0x8020)) |
| 682 | | { |
| 683 | | /* 0x8000-0x801f */ |
| 684 | | /* geneve memory-mapped registers */ |
| 685 | | switch (offset) |
| 686 | | { |
| 687 | | case 0x8000: |
| 688 | | case 0x8001: |
| 689 | | case 0x8002: |
| 690 | | case 0x8003: |
| 691 | | case 0x8004: |
| 692 | | case 0x8005: |
| 693 | | case 0x8006: |
| 694 | | case 0x8007: |
| 695 | | page_lookup[offset-0x8000] = data; |
| 696 | | return; |
| 697 | | |
| 698 | | #if 0 |
| 699 | | case 0x8008: // read-only register??? |
| 700 | | key_buf = data; |
| 701 | | return |
| 702 | | #endif |
| 703 | | |
| 704 | | case 0x8010: |
| 705 | | case 0x8011: |
| 706 | | case 0x8012: |
| 707 | | case 0x8013: |
| 708 | | case 0x8014: |
| 709 | | case 0x8015: |
| 710 | | case 0x8016: |
| 711 | | case 0x8017: |
| 712 | | case 0x8018: |
| 713 | | case 0x8019: |
| 714 | | case 0x801a: |
| 715 | | case 0x801b: |
| 716 | | case 0x801c: |
| 717 | | case 0x801d: |
| 718 | | case 0x801e: |
| 719 | | case 0x801f: |
| 720 | | mm58274c_w(space->machine->device("mm58274c"), offset-0xf130, data); |
| 721 | | return; |
| 722 | | |
| 723 | | default: |
| 724 | | logerror("unmapped write offs=%d data=%d\n", (int) offset, (int) data); |
| 725 | | return; |
| 726 | | } |
| 727 | | } |
| 728 | | else if ((offset >= 0x8400) && (offset < 0xa000)) |
| 729 | | { |
| 730 | | /* 0x8400-0x9fff */ |
| 731 | | /* ti99 memory-mapped registers */ |
| 732 | | switch ((offset - 0x8000) >> 10) |
| 733 | | { |
| 734 | | case 1: |
| 735 | | /* sound write */ |
| 736 | | sn76496_w(space->machine->device("sn76496"), 0, data); |
| 737 | | return; |
| 738 | | |
| 739 | | case 3: |
| 740 | | /* VDP write */ |
| 741 | | if (! (offset & 1)) |
| 742 | | { |
| 743 | | |
| 744 | | switch ((offset >> 1) & 3) |
| 745 | | { |
| 746 | | case 0: |
| 747 | | /* write VDP RAM */ |
| 748 | | v9938_0_vram_w(space, 0, data); |
| 749 | | break; |
| 750 | | case 1: |
| 751 | | /* write VDP address */ |
| 752 | | v9938_0_command_w(space, 0, data); |
| 753 | | break; |
| 754 | | case 2: |
| 755 | | /* write palette */ |
| 756 | | v9938_0_palette_w(space, 0, data); |
| 757 | | break; |
| 758 | | case 3: |
| 759 | | /* write register */ |
| 760 | | v9938_0_register_w(space, 0, data); |
| 761 | | break; |
| 762 | | } |
| 763 | | } |
| 764 | | return; |
| 765 | | |
| 766 | | case 5: |
| 767 | | /* speech write */ |
| 768 | | if ((! (offset & 1)) && has_speech) |
| 769 | | { |
| 770 | | geneve_speech_w(space, 0, data); |
| 771 | | } |
| 772 | | return; |
| 773 | | |
| 774 | | case 7: |
| 775 | | /* GPL write */ |
| 776 | | if (! (offset & 1)) |
| 777 | | { |
| 778 | | if (offset & 2) |
| 779 | | { /* write GPL address */ |
| 780 | | GPL_port.raddr_LSB = FALSE; |
| 781 | | |
| 782 | | if (GPL_port.waddr_LSB) |
| 783 | | { |
| 784 | | GPL_port.addr = (GPL_port.addr & 0xFF00) | data; |
| 785 | | |
| 786 | | /* increment */ |
| 787 | | GPL_port.addr = (GPL_port.addr + 1) & 0xffff; |
| 788 | | |
| 789 | | GPL_port.waddr_LSB = FALSE; |
| 790 | | } |
| 791 | | else |
| 792 | | { |
| 793 | | GPL_port.addr = ((int) data << 8) | (GPL_port.addr & 0xFF); |
| 794 | | |
| 795 | | GPL_port.waddr_LSB = TRUE; |
| 796 | | } |
| 797 | | } |
| 798 | | else |
| 799 | | { /* write GPL data: probably not implemented */ |
| 800 | | GPL_port.addr = (GPL_port.addr + 1) & 0xffff; |
| 801 | | GPL_port.raddr_LSB = GPL_port.waddr_LSB = FALSE; |
| 802 | | } |
| 803 | | } |
| 804 | | return; |
| 805 | | |
| 806 | | default: |
| 807 | | logerror("unmapped write offs=%d data=%d\n", (int) offset, (int) data); |
| 808 | | return; |
| 809 | | } |
| 810 | | } |
| 811 | | } |
| 812 | | |
| 813 | | page = offset >> 13; |
| 814 | | |
| 815 | | if (! (mode_flags & mf_mapmode)) |
| 816 | | { |
| 817 | | if ((mode_flags & mf_genevemode) || (page != 3)) |
| 818 | | page = page_lookup[page]; |
| 819 | | else |
| 820 | | { |
| 821 | | /* Cartridge page under ti99 emulation */ |
| 822 | | if (! (mode_flags & mf_cartpaging)) |
| 823 | | { /* bankswitching */ |
| 824 | | cartridge_page = (offset & 2) != 0; |
| 825 | | return; |
| 826 | | } |
| 827 | | else |
| 828 | | { |
| 829 | | /* writing to (non-bankswitched) cart -> page 0x36 */ |
| 830 | | page = 0x36; |
| 831 | | if ((offset < 0x7000) ? (! (mode_flags & mf_cart6protect)) : (! (mode_flags & mf_cart7protect))) |
| 832 | | { |
| 833 | | /* Trying to write a write-protected cartridge */ |
| 834 | | logerror("Ignoring write to a write-protected cartridge\n"); |
| 835 | | return; |
| 836 | | } |
| 837 | | } |
| 838 | | } |
| 839 | | } |
| 840 | | else |
| 841 | | page = page + 0xf0; |
| 842 | | |
| 843 | | offset &= 0x1fff; |
| 844 | | |
| 845 | | /* ROM is incompletely decoded. Pages f0, f2, f4 ... fe all map to the |
| 846 | | first 8K Boot ROM; f1, ..., ff to the second 8K. See above. */ |
| 847 | | if (page > 0xf1) page &= 0xf1; |
| 848 | | |
| 849 | | switch (page) |
| 850 | | { |
| 851 | | case 0xf0: |
| 852 | | case 0xf1: |
| 853 | | /* Boot ROM */ |
| 854 | | return; |
| 855 | | |
| 856 | | case 0xe8: |
| 857 | | case 0xe9: |
| 858 | | case 0xea: |
| 859 | | case 0xeb: |
| 860 | | case 0xec: |
| 861 | | case 0xed: |
| 862 | | case 0xee: |
| 863 | | case 0xef: |
| 864 | | /* SRAM */ |
| 865 | | SRAM_ptr[(page-0xe8)*0x2000 + offset] = data; |
| 866 | | return; |
| 867 | | |
| 868 | | #if 0 |
| 869 | | case 0x7a: /* mirror of 0xba??? */ |
| 870 | | return; |
| 871 | | #endif |
| 872 | | case 0xba: |
| 873 | | /* DSR space */ |
| 874 | | geneve_peb_w(expansion_box, offset, data); |
| 875 | | return; |
| 876 | | |
| 877 | | case 0xbc: |
| 878 | | /* speech space */ |
| 879 | | if (has_speech) |
| 880 | | { |
| 881 | | if ((offset >= 0x1400) && (offset < 0x1800) && (! (offset & 1))) |
| 882 | | geneve_speech_w(space, 0, data); |
| 883 | | return; |
| 884 | | } |
| 885 | | |
| 886 | | default: |
| 887 | | /* DRAM */ |
| 888 | | DRAM_ptr[page*0x2000 + offset] = data; |
| 889 | | return; |
| 890 | | } |
| 891 | | |
| 892 | | logerror("unmapped write page=%d offs=%d data=%d\n", (int) page, (int) offset, (int) data); |
| 893 | | return; |
| 894 | | } |
| 895 | | |
| 896 | | |
| 897 | | /*===========================================================================*/ |
| 898 | | #if 0 |
| 899 | | #pragma mark - |
| 900 | | #pragma mark CRU HANDLERS |
| 901 | | #endif |
| 902 | | |
| 903 | | WRITE8_HANDLER ( geneve_peb_mode_cru_w ) |
| 904 | | { |
| 905 | | if ((offset >= /*0x770*/0x775) && (offset < 0x780)) |
| 906 | | { |
| 907 | | int old_flags = mode_flags; |
| 908 | | |
| 909 | | /* tms9995 user flags */ |
| 910 | | if (data) |
| 911 | | mode_flags |= 1 << (offset - 0x775); |
| 912 | | else |
| 913 | | mode_flags &= ~(1 << (offset - 0x775)); |
| 914 | | |
| 915 | | if ((offset == 0x778) && data && (! (old_flags & mf_keyclock))) |
| 916 | | { |
| 917 | | /* set mf_keyclock */ |
| 918 | | read_key_if_possible(space->machine); |
| 919 | | } |
| 920 | | if (offset == 0x779) |
| 921 | | { |
| 922 | | if (data && (! (old_flags & mf_keyclear))) |
| 923 | | { |
| 924 | | /* set mf_keyclear: enable key input */ |
| 925 | | /* shift in new key immediately if possible */ |
| 926 | | read_key_if_possible(space->machine); |
| 927 | | } |
| 928 | | else if ((! data) && (old_flags & mf_keyclear)) |
| 929 | | { |
| 930 | | /* clear mf_keyclear: clear key input */ |
| 931 | | if (KeyQueueLen) |
| 932 | | { |
| 933 | | KeyQueueHead = (KeyQueueHead + 1) % KeyQueueSize; |
| 934 | | KeyQueueLen--; |
| 935 | | } |
| 936 | | /* clear keyboard interrupt */ |
| 937 | | tms9901_set_single_int(space->machine->device("tms9901"), 8, 0); |
| 938 | | KeyInBuf = 0; |
| 939 | | } |
| 940 | | } |
| 941 | | } |
| 942 | | |
| 943 | | geneve_peb_cru_w(expansion_box, offset, data); |
| 944 | | } |
| 945 | | |
| 946 | | /*===========================================================================*/ |
| 947 | | #if 0 |
| 948 | | #pragma mark - |
| 949 | | #pragma mark KEYBOARD INTERFACE |
| 950 | | #endif |
| 951 | | |
| 952 | | static void read_key_if_possible(running_machine *machine) |
| 953 | | { |
| 954 | | /* if keyboard reset is not asserted, and key clock is enabled, and key |
| 955 | | buffer clear is disabled, and key queue is not empty. */ |
| 956 | | if ((! KeyReset) && (mode_flags & mf_keyclock) && (mode_flags & mf_keyclear) && KeyQueueLen) |
| 957 | | { |
| 958 | | tms9901_set_single_int(machine->device("tms9901"), 8, 1); |
| 959 | | KeyInBuf = 1; |
| 960 | | } |
| 961 | | } |
| 962 | | |
| 963 | | INLINE void post_in_KeyQueue(int keycode) |
| 964 | | { |
| 965 | | KeyQueue[(KeyQueueHead+KeyQueueLen) % KeyQueueSize] = keycode; |
| 966 | | KeyQueueLen++; |
| 967 | | } |
| 968 | | |
| 969 | | static void poll_keyboard(running_machine *machine) |
| 970 | | { |
| 971 | | UINT32 keystate; |
| 972 | | UINT32 key_transitions; |
| 973 | | int i, j; |
| 974 | | int keycode; |
| 975 | | int pressed; |
| 976 | | static const char *const keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5", "KEY6", "KEY7" }; |
| 977 | | |
| 978 | | static const UINT8 keyboard_mf1_code[0xe] = |
| 979 | | { |
| 980 | | /* extended keys that are equivalent to non-extended keys */ |
| 981 | | 0x1c, /* keypad enter */ |
| 982 | | 0x1d, /* right control */ |
| 983 | | 0x38, /* alt gr */ |
| 984 | | /* extra codes are 0x5b for Left Windows, 0x5c for Right Windows, 0x5d |
| 985 | | for Menu, 0x5e for power, 0x5f for sleep, 0x63 for wake, but I doubt |
| 986 | | any Geneve program would take advantage of these. */ |
| 987 | | |
| 988 | | /* extended key that is equivalent to a non-extended key |
| 989 | | with shift off */ |
| 990 | | 0x35, /* pad slash */ |
| 991 | | |
| 992 | | /* extended keys that are equivalent to non-extended keys |
| 993 | | with numlock off */ |
| 994 | | 0x47, /* home */ |
| 995 | | 0x48, /* up */ |
| 996 | | 0x49, /* page up */ |
| 997 | | 0x4b, /* left */ |
| 998 | | 0x4d, /* right */ |
| 999 | | 0x4f, /* end */ |
| 1000 | | 0x50, /* down */ |
| 1001 | | 0x51, /* page down */ |
| 1002 | | 0x52, /* insert */ |
| 1003 | | 0x53 /* delete */ |
| 1004 | | }; |
| 1005 | | |
| 1006 | | |
| 1007 | | if (KeyReset) |
| 1008 | | return; |
| 1009 | | |
| 1010 | | /* Poll keyboard */ |
| 1011 | | for (i = 0; (i < 4) && (KeyQueueLen <= (KeyQueueSize-MaxKeyMessageLen)); i++) |
| 1012 | | { |
| 1013 | | keystate = input_port_read(machine, keynames[2*i]) | (input_port_read(machine, keynames[2*i + 1]) << 16); |
| 1014 | | key_transitions = keystate ^ KeyStateSave[i]; |
| 1015 | | if (key_transitions) |
| 1016 | | { |
| 1017 | | for (j = 0; (j < 32) && (KeyQueueLen <= (KeyQueueSize-MaxKeyMessageLen)); j++) |
| 1018 | | { |
| 1019 | | if ((key_transitions >> j) & 1) |
| 1020 | | { |
| 1021 | | keycode = (i << 5) | j; |
| 1022 | | pressed = ((keystate >> j) & 1); |
| 1023 | | if (pressed) |
| 1024 | | KeyStateSave[i] |= (1 << j); |
| 1025 | | else |
| 1026 | | KeyStateSave[i] &= ~ (1 << j); |
| 1027 | | |
| 1028 | | /* Update auto-repeat */ |
| 1029 | | if (pressed) |
| 1030 | | { |
| 1031 | | KeyAutoRepeatKey = keycode; |
| 1032 | | KeyAutoRepeatTimer = KeyAutoRepeatDelay+1; |
| 1033 | | } |
| 1034 | | else /*if (keycode == KeyAutoRepeatKey)*/ |
| 1035 | | KeyAutoRepeatKey = 0; |
| 1036 | | |
| 1037 | | /* Release Fake Shift/Unshift if another key is pressed */ |
| 1038 | | /* We do so if a key is released, though it is actually |
| 1039 | | required only if it is a modifier key */ |
| 1040 | | /*if (pressed)*/ |
| 1041 | | { |
| 1042 | | if (KeyFakeShiftState) |
| 1043 | | { |
| 1044 | | /* Fake shift release */ |
| 1045 | | post_in_KeyQueue(0xe0); |
| 1046 | | post_in_KeyQueue(0xaa); |
| 1047 | | KeyFakeShiftState = 0; |
| 1048 | | } |
| 1049 | | if (KeyFakeUnshiftState) |
| 1050 | | { |
| 1051 | | /* Fake shift press */ |
| 1052 | | post_in_KeyQueue(0xe0); |
| 1053 | | post_in_KeyQueue(0x2a); |
| 1054 | | KeyFakeUnshiftState = 0; |
| 1055 | | } |
| 1056 | | } |
| 1057 | | |
| 1058 | | /* update shift and numlock state */ |
| 1059 | | if ((keycode == 0x2a) || (keycode == 0x36)) |
| 1060 | | KeyRealShiftState = KeyRealShiftState + (pressed ? +1 : -1); |
| 1061 | | if ((keycode == 0x1d) || (keycode == 0x61)) |
| 1062 | | KeyCtrlState = KeyCtrlState + (pressed ? +1 : -1); |
| 1063 | | if ((keycode == 0x38) || (keycode == 0x62)) |
| 1064 | | KeyAltState = KeyAltState + (pressed ? +1 : -1); |
| 1065 | | if ((keycode == 0x45) && pressed) |
| 1066 | | KeyNumLockState = ! KeyNumLockState; |
| 1067 | | |
| 1068 | | if ((keycode >= 0x60) && (keycode < 0x6e)) |
| 1069 | | { /* simpler extended keys */ |
| 1070 | | /* these keys are emulated */ |
| 1071 | | |
| 1072 | | if ((keycode >= 0x63) && pressed) |
| 1073 | | { |
| 1074 | | /* Handle shift state */ |
| 1075 | | if (keycode == 0x63) |
| 1076 | | { /* non-shifted key */ |
| 1077 | | if (KeyRealShiftState) |
| 1078 | | /* Fake shift unpress */ |
| 1079 | | KeyFakeUnshiftState = 1; |
| 1080 | | } |
| 1081 | | else /*if (keycode >= 0x64)*/ |
| 1082 | | { /* non-numlock mode key */ |
| 1083 | | if (KeyNumLockState & ! KeyRealShiftState) |
| 1084 | | /* Fake shift press if numlock is active */ |
| 1085 | | KeyFakeShiftState = 1; |
| 1086 | | else if ((! KeyNumLockState) & KeyRealShiftState) |
| 1087 | | /* Fake shift unpress if shift is down */ |
| 1088 | | KeyFakeUnshiftState = 1; |
| 1089 | | } |
| 1090 | | |
| 1091 | | if (KeyFakeShiftState) |
| 1092 | | { |
| 1093 | | post_in_KeyQueue(0xe0); |
| 1094 | | post_in_KeyQueue(0x2a); |
| 1095 | | } |
| 1096 | | |
| 1097 | | if (KeyFakeUnshiftState) |
| 1098 | | { |
| 1099 | | post_in_KeyQueue(0xe0); |
| 1100 | | post_in_KeyQueue(0xaa); |
| 1101 | | } |
| 1102 | | } |
| 1103 | | |
| 1104 | | keycode = keyboard_mf1_code[keycode-0x60]; |
| 1105 | | if (! pressed) |
| 1106 | | keycode |= 0x80; |
| 1107 | | post_in_KeyQueue(0xe0); |
| 1108 | | post_in_KeyQueue(keycode); |
| 1109 | | } |
| 1110 | | else if (keycode == 0x6e) |
| 1111 | | { /* emulate Print Screen / System Request (F13) key */ |
| 1112 | | /* this is a bit complex, as Alt+PrtScr -> SysRq */ |
| 1113 | | /* Additionally, Ctrl+PrtScr involves no fake shift press */ |
| 1114 | | if (KeyAltState) |
| 1115 | | { |
| 1116 | | /* SysRq */ |
| 1117 | | keycode = 0x54; |
| 1118 | | if (! pressed) |
| 1119 | | keycode |= 0x80; |
| 1120 | | post_in_KeyQueue(keycode); |
| 1121 | | } |
| 1122 | | else |
| 1123 | | { |
| 1124 | | /* Handle shift state */ |
| 1125 | | if (pressed && (! KeyRealShiftState) && (! KeyCtrlState)) |
| 1126 | | { /* Fake shift press */ |
| 1127 | | post_in_KeyQueue(0xe0); |
| 1128 | | post_in_KeyQueue(0x2a); |
| 1129 | | KeyFakeShiftState = 1; |
| 1130 | | } |
| 1131 | | |
| 1132 | | keycode = 0x37; |
| 1133 | | if (! pressed) |
| 1134 | | keycode |= 0x80; |
| 1135 | | post_in_KeyQueue(0xe0); |
| 1136 | | post_in_KeyQueue(keycode); |
| 1137 | | } |
| 1138 | | } |
| 1139 | | else if (keycode == 0x6f) |
| 1140 | | { /* emulate pause (F15) key */ |
| 1141 | | /* this is a bit complex, as Pause -> Ctrl+NumLock and |
| 1142 | | Ctrl+Pause -> Ctrl+ScrLock. Furthermore, there is no |
| 1143 | | repeat or release. */ |
| 1144 | | if (pressed) |
| 1145 | | { |
| 1146 | | if (KeyCtrlState) |
| 1147 | | { |
| 1148 | | post_in_KeyQueue(0xe0); |
| 1149 | | post_in_KeyQueue(0x46); |
| 1150 | | post_in_KeyQueue(0xe0); |
| 1151 | | post_in_KeyQueue(0xc6); |
| 1152 | | } |
| 1153 | | else |
| 1154 | | { |
| 1155 | | post_in_KeyQueue(0xe1); |
| 1156 | | post_in_KeyQueue(0x1d); |
| 1157 | | post_in_KeyQueue(0x45); |
| 1158 | | post_in_KeyQueue(0xe1); |
| 1159 | | post_in_KeyQueue(0x9d); |
| 1160 | | post_in_KeyQueue(0xc5); |
| 1161 | | } |
| 1162 | | } |
| 1163 | | } |
| 1164 | | else |
| 1165 | | { |
| 1166 | | if (! pressed) |
| 1167 | | keycode |= 0x80; |
| 1168 | | post_in_KeyQueue(keycode); |
| 1169 | | } |
| 1170 | | read_key_if_possible(machine); |
| 1171 | | } |
| 1172 | | } |
| 1173 | | } |
| 1174 | | } |
| 1175 | | |
| 1176 | | /* Handle auto-repeat */ |
| 1177 | | if ((KeyQueueLen <= (KeyQueueSize-MaxKeyMessageLen)) && KeyAutoRepeatKey && (--KeyAutoRepeatTimer == 0)) |
| 1178 | | { |
| 1179 | | if ((KeyAutoRepeatKey >= 0x60) && (KeyAutoRepeatKey < 0x6e)) |
| 1180 | | { |
| 1181 | | post_in_KeyQueue(0xe0); |
| 1182 | | post_in_KeyQueue(keyboard_mf1_code[KeyAutoRepeatKey-0x60]); |
| 1183 | | } |
| 1184 | | else if (KeyAutoRepeatKey == 0x6e) |
| 1185 | | { |
| 1186 | | if (KeyAltState) |
| 1187 | | post_in_KeyQueue(0x54); |
| 1188 | | else |
| 1189 | | { |
| 1190 | | post_in_KeyQueue(0xe0); |
| 1191 | | post_in_KeyQueue(0x37); |
| 1192 | | } |
| 1193 | | } |
| 1194 | | else if (KeyAutoRepeatKey == 0x6f) |
| 1195 | | ; |
| 1196 | | else |
| 1197 | | { |
| 1198 | | post_in_KeyQueue(KeyAutoRepeatKey); |
| 1199 | | } |
| 1200 | | read_key_if_possible(machine); |
| 1201 | | KeyAutoRepeatTimer = KeyAutoRepeatRate; |
| 1202 | | } |
| 1203 | | } |
| 1204 | | |
| 1205 | | static void poll_mouse(running_machine *machine) |
| 1206 | | { |
| 1207 | | static int last_mx = 0, last_my = 0; |
| 1208 | | int new_mx, new_my; |
| 1209 | | int delta_x, delta_y, buttons; |
| 1210 | | |
| 1211 | | buttons = input_port_read(machine, "MOUSE0"); |
| 1212 | | new_mx = input_port_read(machine, "MOUSEX"); |
| 1213 | | new_my = input_port_read(machine, "MOUSEY"); |
| 1214 | | |
| 1215 | | /* compute x delta */ |
| 1216 | | delta_x = new_mx - last_mx; |
| 1217 | | |
| 1218 | | /* check for wrap */ |
| 1219 | | if (delta_x > 0x80) |
| 1220 | | delta_x = 0x100-delta_x; |
| 1221 | | if (delta_x < -0x80) |
| 1222 | | delta_x = -0x100-delta_x; |
| 1223 | | |
| 1224 | | last_mx = new_mx; |
| 1225 | | |
| 1226 | | /* compute y delta */ |
| 1227 | | delta_y = new_my - last_my; |
| 1228 | | |
| 1229 | | /* check for wrap */ |
| 1230 | | if (delta_y > 0x80) |
| 1231 | | delta_y = 0x100-delta_y; |
| 1232 | | if (delta_y < -0x80) |
| 1233 | | delta_y = -0x100-delta_y; |
| 1234 | | |
| 1235 | | last_my = new_my; |
| 1236 | | |
| 1237 | | v9938_update_mouse_state(0, delta_x, delta_y, buttons & 3); |
| 1238 | | } |
| 1239 | | |
| 1240 | | |
| 1241 | | /*===========================================================================*/ |
| 1242 | | #if 0 |
| 1243 | | #pragma mark - |
| 1244 | | #pragma mark TMS9901 INTERFACE |
| 1245 | | #endif |
| 1246 | | /* |
| 1247 | | Geneve-specific tms9901 I/O handlers |
| 1248 | | |
| 1249 | | See mess/machine/tms9901.c for generic tms9901 CRU handlers. |
| 1250 | | */ |
| 1251 | | /* |
| 1252 | | TMS9901 interrupt handling on a Geneve. |
| 1253 | | |
| 1254 | | Geneve uses the following interrupts: |
| 1255 | | INT1: external interrupt (used by RS232 controller, for instance) - |
| 1256 | | connected to tms9995 int4, too. |
| 1257 | | INT2: VDP interrupt |
| 1258 | | TMS9901 timer interrupt? (overrides INT3) |
| 1259 | | INT8: keyboard interrupt??? |
| 1260 | | INT10: mouse interrupt??? |
| 1261 | | INT11: clock interrupt??? |
| 1262 | | INT12: INTB interrupt from PE-bus |
| 1263 | | */ |
| 1264 | | |
| 1265 | | /* |
| 1266 | | set the state of int2 (called by tms9928 core) |
| 1267 | | */ |
| 1268 | | #ifdef UNUSED_FUNCTION |
| 1269 | | void tms9901_set_int2(int state) |
| 1270 | | { |
| 1271 | | tms9901_set_single_int(machine->device("tms9901"), 2, state); |
| 1272 | | } |
| 1273 | | #endif |
| 1274 | | |
| 1275 | | /* |
| 1276 | | Called by the 9901 core whenever the state of INTREQ and IC0-3 changes |
| 1277 | | */ |
| 1278 | | static TMS9901_INT_CALLBACK( tms9901_interrupt_callback ) |
| 1279 | | { |
| 1280 | | /* INTREQ is connected to INT1 (IC0-3 are not connected) */ |
| 1281 | | cputag_set_input_line(device->machine, "maincpu", 0, intreq ? ASSERT_LINE : CLEAR_LINE); |
| 1282 | | } |
| 1283 | | |
| 1284 | | /* |
| 1285 | | Read pins INT3*-INT7* of Geneve 9901. |
| 1286 | | |
| 1287 | | signification: |
| 1288 | | (bit 1: INT1 status) |
| 1289 | | (bit 2: INT2 status) |
| 1290 | | bit 3-7: joystick status |
| 1291 | | */ |
| 1292 | | static READ8_DEVICE_HANDLER( R9901_0 ) |
| 1293 | | { |
| 1294 | | int answer; |
| 1295 | | |
| 1296 | | answer = input_port_read(device->machine, "JOY") >> (JoySel * 8); |
| 1297 | | |
| 1298 | | return (answer); |
| 1299 | | } |
| 1300 | | |
| 1301 | | /* |
| 1302 | | Read pins INT8*-INT15* of Geneve 9901. |
| 1303 | | |
| 1304 | | signification: |
| 1305 | | (bit 0: keyboard interrupt) |
| 1306 | | bit 1: unused |
| 1307 | | bit 2: mouse right button |
| 1308 | | (bit 3: clock interrupt) |
| 1309 | | (bit 4: INTB from PE-bus) |
| 1310 | | bit 5 & 7: used as output |
| 1311 | | bit 6: unused |
| 1312 | | */ |
| 1313 | | static READ8_DEVICE_HANDLER( R9901_1 ) |
| 1314 | | { |
| 1315 | | int answer; |
| 1316 | | |
| 1317 | | answer = (input_port_read(device->machine, "MOUSE0") & 4) ^ 4; |
| 1318 | | |
| 1319 | | return answer; |
| 1320 | | } |
| 1321 | | |
| 1322 | | /* |
| 1323 | | Read pins P0-P7 of Geneve 9901. |
| 1324 | | */ |
| 1325 | | static READ8_DEVICE_HANDLER( R9901_2 ) |
| 1326 | | { |
| 1327 | | return 0; |
| 1328 | | } |
| 1329 | | |
| 1330 | | /* |
| 1331 | | Read pins P8-P15 of Geneve 9901. |
| 1332 | | bit 4: mouse right button |
| 1333 | | */ |
| 1334 | | static READ8_DEVICE_HANDLER( R9901_3 ) |
| 1335 | | { |
| 1336 | | int answer = 0; |
| 1337 | | |
| 1338 | | if (! (input_port_read(device->machine, "MOUSE0") & 4)) |
| 1339 | | answer |= 0x10; |
| 1340 | | |
| 1341 | | return answer; |
| 1342 | | } |
| 1343 | | |
| 1344 | | |
| 1345 | | /* |
| 1346 | | Write PE bus reset line |
| 1347 | | */ |
| 1348 | | static WRITE8_DEVICE_HANDLER( W9901_PE_bus_reset ) |
| 1349 | | { |
| 1350 | | } |
| 1351 | | |
| 1352 | | /* |
| 1353 | | Write VDP reset line |
| 1354 | | */ |
| 1355 | | static WRITE8_DEVICE_HANDLER( W9901_VDP_reset ) |
| 1356 | | { |
| 1357 | | } |
| 1358 | | |
| 1359 | | /* |
| 1360 | | Write joystick select line |
| 1361 | | */ |
| 1362 | | static WRITE8_DEVICE_HANDLER( W9901_JoySel ) |
| 1363 | | { |
| 1364 | | JoySel = data; |
| 1365 | | } |
| 1366 | | |
| 1367 | | static WRITE8_DEVICE_HANDLER( W9901_KeyboardReset ) |
| 1368 | | { |
| 1369 | | KeyReset = ! data; |
| 1370 | | if (KeyReset) |
| 1371 | | { |
| 1372 | | /* reset -> clear keyboard key queue, but not geneve key buffer */ |
| 1373 | | KeyQueueLen = KeyInBuf ? 1 : 0; |
| 1374 | | memset(KeyStateSave, 0, sizeof(KeyStateSave)); |
| 1375 | | KeyNumLockState = 0; |
| 1376 | | KeyCtrlState = 0; |
| 1377 | | KeyAltState = 0; |
| 1378 | | KeyRealShiftState = 0; |
| 1379 | | KeyFakeShiftState = 0; |
| 1380 | | KeyFakeUnshiftState = 0; |
| 1381 | | KeyAutoRepeatKey = 0; |
| 1382 | | } |
| 1383 | | /*else |
| 1384 | | poll_keyboard(space->machine);*/ |
| 1385 | | } |
| 1386 | | |
| 1387 | | /* |
| 1388 | | Write external mem cycles (0=long, 1=short) |
| 1389 | | */ |
| 1390 | | static WRITE8_DEVICE_HANDLER( W9901_ext_mem_wait_states ) |
| 1391 | | { |
| 1392 | | } |
| 1393 | | |
| 1394 | | /* |
| 1395 | | Write vdp wait cycles (1=add 15 cycles, 0=add none) |
| 1396 | | */ |
| 1397 | | static WRITE8_DEVICE_HANDLER( W9901_VDP_wait_states ) |
| 1398 | | { |
| 1399 | | } |
| 1400 | | |
| 1401 | | /* tms9901 setup */ |
| 1402 | | const tms9901_interface tms9901reset_param_ti99 = |
| 1403 | | { |
| 1404 | | TMS9901_INT1 | TMS9901_INT2 | TMS9901_INT8 | TMS9901_INTB | TMS9901_INTC, /* only input pins whose state is always known */ |
| 1405 | | |
| 1406 | | { /* read handlers */ |
| 1407 | | R9901_0, |
| 1408 | | R9901_1, |
| 1409 | | R9901_2, |
| 1410 | | R9901_3 |
| 1411 | | }, |
| 1412 | | |
| 1413 | | { /* write handlers */ |
| 1414 | | W9901_PE_bus_reset, |
| 1415 | | W9901_VDP_reset, |
| 1416 | | W9901_JoySel, |
| 1417 | | NULL, |
| 1418 | | NULL, |
| 1419 | | NULL, |
| 1420 | | W9901_KeyboardReset, |
| 1421 | | W9901_ext_mem_wait_states, |
| 1422 | | NULL, |
| 1423 | | W9901_VDP_wait_states, |
| 1424 | | NULL, |
| 1425 | | NULL, |
| 1426 | | NULL, |
| 1427 | | NULL, |
| 1428 | | NULL, |
| 1429 | | NULL |
| 1430 | | }, |
| 1431 | | |
| 1432 | | /* interrupt handler */ |
| 1433 | | tms9901_interrupt_callback, |
| 1434 | | |
| 1435 | | /* clock rate = 3MHz */ |
| 1436 | | 3000000. |
| 1437 | | }; |
| 1438 | | |
| 1439 | | |
src/mess/machine/ti99_4x.c
| r9500 | r9501 | |
| 1 | | /* |
| 2 | | Machine code for TI99/4, TI-99/4A, TI-99/8, and SNUG SGCPU (a.k.a. 99/4P). |
| 3 | | Raphael Nabet, 1999-2003. |
| 4 | | Some code was originally derived from Ed Swartz's V9T9. |
| 5 | | |
| 6 | | References: |
| 7 | | * The TI-99/4A Tech Pages. Great site with complete technical description |
| 8 | | of the TI99/4a. |
| 9 | | <http://www.nouspikel.com/ti99/titech.htm> |
| 10 | | * ftp.whtech.com has schematics and hardware documentations. The documents |
| 11 | | of most general interest are: |
| 12 | | <ftp://ftp.whtech.com//datasheets/Hardware manuals/99-4 console specification and schematic.pdf> |
| 13 | | <ftp://ftp.whtech.com//datasheets/Hardware manuals/99-4A Console and peripheral Expansion System Technical Data.pdf> |
| 14 | | Schematics for various extension cards are available in the same directory. |
| 15 | | * V9T9 source code. This older emulator was often fairly accurate. |
| 16 | | * Harald Glaab's site has software and documentation for the various SNUG |
| 17 | | cards: 99/4P (nicknamed "SGCPU"), EVPC, BwG. |
| 18 | | <http://home.t-online.de/home/harald.glaab/snug/> |
| 19 | | * The TI-99/4 CPU ROM is the only source of information for the IR remote |
| 20 | | handset controller protocol. I have written a document that explains |
| 21 | | this protocol. |
| 22 | | * TI-99/8 ROM source code (6 PC99 disk images): |
| 23 | | <ftp://ftp.whtech.com//source/998Source Disk1.DSK> |
| 24 | | ... |
| 25 | | <ftp://ftp.whtech.com//source/998Source Disk6.DSK> |
| 26 | | * TI-99/8 schematics. This is a valuable source of information, BUT the |
| 27 | | schematics describe an early prototype (no Extended Basic, etc.). |
| 28 | | <ftp://ftp.whtech.com//datasheets & manuals/99-8 Computer/99-8 schematic.max> |
| 29 | | I have written an index for the signals on these schematics. |
| 30 | | |
| 31 | | Emulated: |
| 32 | | * All TI99 basic console hardware, except a few tricks in TMS9901 emulation. |
| 33 | | * Cartridges with ROM (either non-paged or paged), RAM (minimemory) and |
| 34 | | GROM. Supercart and MBX support are not finished. |
| 35 | | * Speech Synthesizer, with standard speech ROM (no speech ROM expansion). |
| 36 | | * Disk emulation (SSSD disk images with TI fdc, both SSSD and DSDD with |
| 37 | | SNUG's BwG and HFDC fdcs). |
| 38 | | * Hard disk (HFDC and IDE). |
| 39 | | * Mechatronics mouse. |
| 40 | | * save minimemory contents |
| 41 | | |
| 42 | | Compatibility looks quite good. |
| 43 | | |
| 44 | | Issues: |
| 45 | | * floppy disk timings are not emulated (general issue) |
| 46 | | |
| 47 | | TODO: |
| 48 | | * support for other peripherals and DSRs as documentation permits |
| 49 | | * implement the EVPC palette chip |
| 50 | | * finish 99/4p support: ROM6, HSGPL (implemented, but not fully debugged) |
| 51 | | */ |
| 52 | | |
| 53 | | #include <math.h> |
| 54 | | #include "emu.h" |
| 55 | | #include "tms9901.h" |
| 56 | | #include "video/v9938.h" |
| 57 | | #include "audio/spchroms.h" |
| 58 | | #include "devices/cassette.h" |
| 59 | | #include "ti99_4x.h" |
| 60 | | #include "devices/ti99_peb.h" |
| 61 | | #include "994x_ser.h" |
| 62 | | #include "99_dsk.h" |
| 63 | | #include "99_ide.h" |
| 64 | | #include "99_hsgpl.h" |
| 65 | | #include "99_usbsm.h" |
| 66 | | #include "ti99pcod.h" |
| 67 | | |
| 68 | | #include "sound/tms5220.h" /* for tms5220_set_variant() */ |
| 69 | | #include "sound/sn76496.h" |
| 70 | | |
| 71 | | #include "devices/harddriv.h" /* for device_init_mess_hd() */ |
| 72 | | #include "smc92x4.h" /* for smc92x4_hd_load()/smc92x4_hd_unload() */ |
| 73 | | |
| 74 | | /* prototypes */ |
| 75 | | static READ16_HANDLER ( ti99_rspeech_r ); |
| 76 | | static WRITE16_HANDLER ( ti99_wspeech_w ); |
| 77 | | |
| 78 | | static void ti99_handset_task(running_machine *machine); |
| 79 | | static void mecmouse_poll(running_machine *machine); |
| 80 | | |
| 81 | | static void ti99_8_internal_dsr_reset(running_machine *machine); |
| 82 | | |
| 83 | | static void ti99_4p_internal_dsr_reset(running_machine *machine); |
| 84 | | static void ti99_TIxram_init(running_machine *machine); |
| 85 | | static void ti99_sAMSxram_init(running_machine *machine); |
| 86 | | static void ti99_4p_mapper_init(running_machine *machine); |
| 87 | | static void ti99_myarcxram_init(running_machine *machine); |
| 88 | | static void ti99_evpc_reset(running_machine *machine); |
| 89 | | static void ti99_evpc_load_novram(running_machine *machine, const char *filename); |
| 90 | | static void ti99_evpc_save_novram(running_machine *machine, const char *filename); |
| 91 | | |
| 92 | | /* |
| 93 | | pointer to extended RAM area |
| 94 | | */ |
| 95 | | static UINT16 *xRAM_ptr; |
| 96 | | |
| 97 | | /* |
| 98 | | Configuration |
| 99 | | */ |
| 100 | | /* model of ti99 in emulation */ |
| 101 | | static enum |
| 102 | | { |
| 103 | | model_99_4, |
| 104 | | model_99_4a, |
| 105 | | model_99_8, |
| 106 | | model_99_4p |
| 107 | | } ti99_model; |
| 108 | | |
| 109 | | /* TRUE if speech synthesizer present */ |
| 110 | | static char has_speech; |
| 111 | | |
| 112 | | /* TRUE if ide card present */ |
| 113 | | static char has_ide; |
| 114 | | /* TRUE if evpc card present */ |
| 115 | | static char has_evpc; |
| 116 | | /* TRUE if rs232 card present */ |
| 117 | | static char has_rs232; |
| 118 | | /* TRUE if ti99/4 IR remote handset present */ |
| 119 | | static char has_handset; |
| 120 | | /* TRUE if hsgpl card present */ |
| 121 | | static char has_hsgpl; |
| 122 | | static char hsgpl_flash_mode; |
| 123 | | |
| 124 | | /* TRUE if mechatronics mouse present */ |
| 125 | | static char has_mecmouse; |
| 126 | | /* TRUE if usb-sm card present */ |
| 127 | | static char has_usb_sm; |
| 128 | | |
| 129 | | /* TRUE if p-code card present */ |
| 130 | | static char has_pcode; |
| 131 | | |
| 132 | | /* handset interface */ |
| 133 | | static int handset_buf; |
| 134 | | static int handset_buflen; |
| 135 | | static int handset_clock; |
| 136 | | static int handset_ack; |
| 137 | | |
| 138 | | /* Expansion RAM type */ |
| 139 | | static int xRAM_kind; |
| 140 | | |
| 141 | | enum |
| 142 | | { |
| 143 | | max_handsets = 4 |
| 144 | | }; |
| 145 | | |
| 146 | | /* mechatronics */ |
| 147 | | static int mecmouse_sel; |
| 148 | | static int mecmouse_read_y; |
| 149 | | static int mecmouse_x, mecmouse_y; |
| 150 | | static int mecmouse_x_buf, mecmouse_y_buf; |
| 151 | | |
| 152 | | /* keyboard interface */ |
| 153 | | static int KeyCol; |
| 154 | | static int AlphaLockLine; |
| 155 | | |
| 156 | | /* |
| 157 | | GROM support. |
| 158 | | |
| 159 | | In short: |
| 160 | | |
| 161 | | TI99/4x hardware supports GROMs. GROMs are slow ROM devices, which are |
| 162 | | interfaced via a 8-bit data bus, and include an internal address pointer |
| 163 | | which is incremented after each read. This implies that accesses are |
| 164 | | faster when reading consecutive bytes, although the address pointer can be |
| 165 | | read and written at any time. |
| 166 | | |
| 167 | | They are generally used to store programs in GPL (Graphic Programming |
| 168 | | Language: a proprietary, interpreted language - the interpreter takes most |
| 169 | | of a TI99/4(a) CPU ROMs). They can used to store large pieces of data, |
| 170 | | too. |
| 171 | | |
| 172 | | Both TI-99/4 and TI-99/4a include three GROMs, with some start-up code, |
| 173 | | system routines and TI-Basic. TI99/4 includes an additional Equation |
| 174 | | Editor. According to the preliminary schematics found on ftp.whtech.com, |
| 175 | | TI-99/8 includes the three standard GROMs and 16 GROMs for the UCSD |
| 176 | | p-system. TI99/2 does not include GROMs at all, and was not designed to |
| 177 | | support any, although it should be relatively easy to create an expansion |
| 178 | | card with the GPL interpreter and a /4a cartridge port. |
| 179 | | |
| 180 | | The simple way: |
| 181 | | |
| 182 | | Each GROM is logically 8kb long. |
| 183 | | |
| 184 | | Communication with GROM is done with 4 memory-mapped locations. You can read or write |
| 185 | | a 16-bit address pointer, and read data from GROMs. One register allows to write data, too, |
| 186 | | which would support some GRAMs, but AFAIK TI never built such GRAMs (although docs from TI |
| 187 | | do refer to this possibility). |
| 188 | | |
| 189 | | Since address are 16-bit long, you can have up to 8 GROMs. So, a cartridge may |
| 190 | | include up to 5 GROMs. (Actually, there is a way more GROMs can be used - see below...) |
| 191 | | |
| 192 | | The address pointer is incremented after each GROM operation, but it will always remain |
| 193 | | within the bounds of the currently selected GROM (e.g. after 0x3fff comes 0x2000). |
| 194 | | |
| 195 | | Some details: |
| 196 | | |
| 197 | | Original TI-built GROM are 6kb long, but take 8kb in address space. The extra 2kb can be |
| 198 | | read, and follow the following formula: |
| 199 | | GROM[0x1800+offset] = GROM[0x0800+offset] | GROM[0x1000+offset]; |
| 200 | | (sounds like address decoding is incomplete - we are lucky we don't burn any silicon when |
| 201 | | doing so...) |
| 202 | | |
| 203 | | Needless to say, some hackers simulated 8kb GRAMs and GROMs with normal RAM/PROM chips and |
| 204 | | glue logic. |
| 205 | | |
| 206 | | GPL ports: |
| 207 | | |
| 208 | | When accessing the GROMs registers, 8 address bits (cpu_addr & 0x03FC) may |
| 209 | | be used as a port number, which permits the use of up to 256 independant |
| 210 | | GROM ports, with 64kb of address space in each. TI99/4(a) ROMs can take |
| 211 | | advantage of the first 16 ports: it will look for GPL programs in every |
| 212 | | GROM of the 16 first ports. Additionally, while the other 240 ports cannot |
| 213 | | contain standard GROMs with GPL code, they may still contain custom GROMs |
| 214 | | with data. |
| 215 | | |
| 216 | | Note, however, that the TI99/4(a) console does not decode the page number, so console GROMs |
| 217 | | occupy the first 24kb of EVERY port, and cartridge GROMs occupy the next 40kb of EVERY port |
| 218 | | (with the exception of one demonstration from TI that implements several distinct ports). |
| 219 | | (Note that the TI99/8 console does have the required decoder.) Fortunately, GROM drivers |
| 220 | | have a relatively high impedance, and therefore extension cards can use TTL drivers to impose |
| 221 | | another value on the data bus with no risk of burning the console GROMs. This hack permits |
| 222 | | the addition of additionnal GROMs in other ports, with the only side effect that whenever |
| 223 | | the address pointer in port N is altered, the address pointer of console GROMs in port 0 |
| 224 | | is altered, too. Overriding the system GROMs with custom code is possible, too (some hackers |
| 225 | | have done so), but I would not recommended it, as connecting such a device to a TI99/8 might |
| 226 | | burn some drivers. |
| 227 | | |
| 228 | | The p-code card (-> UCSD Pascal system) contains 8 GROMs, all in port 16. This port is not |
| 229 | | in the range recognized by the TI ROMs (0-15), and therefore it is used by the p-code DSR ROMs |
| 230 | | as custom data. Additionally, some hackers used the extra ports to implement "GRAM" devices. |
| 231 | | |
| 232 | | The handling of cartridge GROMs has moved to ti99cart.c. |
| 233 | | */ |
| 234 | | |
| 235 | | /* descriptor for console GROMs */ |
| 236 | | static GROM_port_t console_GROMs; |
| 237 | | |
| 238 | | /* true if hsgpl is enabled (i.e. has_hsgpl is true and hsgpl cru bit crdena is |
| 239 | | set) */ |
| 240 | | static char hsgpl_crdena; |
| 241 | | |
| 242 | | /* Used to cache the switch settings. */ |
| 243 | | static UINT8 gk_switch[8]; |
| 244 | | |
| 245 | | /* DIP switches of EVPC */ |
| 246 | | static UINT8 evpc_switch[4]; |
| 247 | | |
| 248 | | /* Pointer to the cartridge system for the 99/8 */ |
| 249 | | static running_device *multicart8; |
| 250 | | |
| 251 | | /* ti99/8 hardware */ |
| 252 | | static UINT8 ti99_8_enable_rom_and_ports; |
| 253 | | |
| 254 | | static UINT32 ti99_8_mapper_regs[16]; |
| 255 | | static UINT8 ti99_8_mapper_status; |
| 256 | | |
| 257 | | static UINT8 *xRAM_ptr_8; |
| 258 | | static UINT8 *ROM0_ptr_8; |
| 259 | | static UINT8 *ROM1_ptr_8; |
| 260 | | static UINT8 *ROM2_ptr_8; |
| 261 | | static UINT8 *ROM3_ptr_8; |
| 262 | | static UINT8 *sRAM_ptr_8; |
| 263 | | |
| 264 | | /* tms9900_ICount: used to implement memory waitstates (hack) */ |
| 265 | | /* tms9995_ICount: used to implement memory waitstates (hack) */ |
| 266 | | /* NPW 23-Feb-2004 - externs no longer needed because we now use cpu_adjust_icount(space->machine->device("maincpu"),) */ |
| 267 | | |
| 268 | | /* Link to the cartridge system. */ |
| 269 | | static running_device *cartslots; |
| 270 | | |
| 271 | | /* Link to the Peripheral Expansion Box */ |
| 272 | | static running_device *expansion_box; |
| 273 | | |
| 274 | | /*===========================================================================*/ |
| 275 | | /* |
| 276 | | General purpose code: |
| 277 | | initialization, cart loading, etc. |
| 278 | | */ |
| 279 | | |
| 280 | | static void create_grom_high2k(int first, int last) |
| 281 | | { |
| 282 | | /* Generate missing chunk of each console GROMs. Although a GROM */ |
| 283 | | /* occupies 8 KiB of address space, it only has 6 KiB capacity, and */ |
| 284 | | /* the last 2 KiB mirror previous content as shown above. */ |
| 285 | | int base; |
| 286 | | for (base=(first&0xe000); base<((last+1)&0xe000); base+=0x2000) |
| 287 | | { |
| 288 | | int j; |
| 289 | | for (j=0; j<0x800; j++) |
| 290 | | { |
| 291 | | console_GROMs.data_ptr[base+0x1800+j] = |
| 292 | | console_GROMs.data_ptr[base+0x0800+j] |
| 293 | | | console_GROMs.data_ptr[base+0x1000+j]; |
| 294 | | } |
| 295 | | } |
| 296 | | } |
| 297 | | |
| 298 | | int ti99_is_99_8() |
| 299 | | { |
| 300 | | return ti99_model == model_99_8; |
| 301 | | } |
| 302 | | |
| 303 | | DRIVER_INIT( ti99_4 ) |
| 304 | | { |
| 305 | | ti99_model = model_99_4; |
| 306 | | has_evpc = FALSE; |
| 307 | | |
| 308 | | /* set up memory pointers */ |
| 309 | | xRAM_ptr = (UINT16 *) (memory_region(machine, "maincpu") + offset_xram); |
| 310 | | console_GROMs.data_ptr = memory_region(machine, region_grom); |
| 311 | | create_grom_high2k(0x0000, 0x5fff); |
| 312 | | } |
| 313 | | |
| 314 | | DRIVER_INIT( ti99_4a ) |
| 315 | | { |
| 316 | | ti99_model = model_99_4a; |
| 317 | | has_evpc = FALSE; |
| 318 | | |
| 319 | | /* set up memory pointers */ |
| 320 | | xRAM_ptr = (UINT16 *) (memory_region(machine, "maincpu") + offset_xram); |
| 321 | | console_GROMs.data_ptr = memory_region(machine, region_grom); |
| 322 | | create_grom_high2k(0x0000, 0x5fff); |
| 323 | | } |
| 324 | | |
| 325 | | DRIVER_INIT( ti99_4ev ) |
| 326 | | { |
| 327 | | UINT8 *mem = memory_region(machine, "maincpu"); |
| 328 | | ti99_model = model_99_4a; |
| 329 | | has_evpc = TRUE; |
| 330 | | |
| 331 | | /* set up memory pointers */ |
| 332 | | xRAM_ptr = (UINT16 *) (mem + offset_xram); |
| 333 | | console_GROMs.data_ptr = memory_region(machine, region_grom); |
| 334 | | create_grom_high2k(0x0000, 0x5fff); |
| 335 | | } |
| 336 | | |
| 337 | | static const TMS9928a_interface tms9918_interface = |
| 338 | | { |
| 339 | | TMS99x8, |
| 340 | | 0x4000, |
| 341 | | 15, 15, |
| 342 | | tms9901_set_int2 |
| 343 | | }; |
| 344 | | |
| 345 | | MACHINE_START( ti99_4_60hz ) |
| 346 | | { |
| 347 | | ti99_common_init(machine, &tms9918_interface); |
| 348 | | } |
| 349 | | |
| 350 | | static const TMS9928a_interface tms9929_interface = |
| 351 | | { |
| 352 | | TMS9929, |
| 353 | | 0x4000, |
| 354 | | 13, 13, |
| 355 | | tms9901_set_int2 |
| 356 | | }; |
| 357 | | |
| 358 | | MACHINE_START( ti99_4_50hz ) |
| 359 | | { |
| 360 | | ti99_common_init(machine, &tms9929_interface); |
| 361 | | } |
| 362 | | |
| 363 | | static const TMS9928a_interface tms9918a_interface = |
| 364 | | { |
| 365 | | TMS99x8A, |
| 366 | | 0x4000, |
| 367 | | 15, 15, |
| 368 | | tms9901_set_int2 |
| 369 | | }; |
| 370 | | |
| 371 | | MACHINE_START( ti99_4a_60hz ) |
| 372 | | { |
| 373 | | ti99_common_init(machine, &tms9918a_interface); |
| 374 | | } |
| 375 | | |
| 376 | | static const TMS9928a_interface tms9929a_interface = |
| 377 | | { |
| 378 | | TMS9929A, |
| 379 | | 0x4000, |
| 380 | | 13, 13, |
| 381 | | tms9901_set_int2 |
| 382 | | }; |
| 383 | | |
| 384 | | MACHINE_START( ti99_4a_50hz ) |
| 385 | | { |
| 386 | | ti99_common_init(machine, &tms9929a_interface); |
| 387 | | } |
| 388 | | |
| 389 | | MACHINE_START( ti99_4ev_60hz) |
| 390 | | { |
| 391 | | /* has an own VDP, so skip initing the VDP */ |
| 392 | | ti99_common_init(machine, 0); |
| 393 | | } |
| 394 | | |
| 395 | | void ti99_common_init(running_machine *machine, const TMS9928a_interface *gfxparm) |
| 396 | | { |
| 397 | | if (gfxparm != 0) |
| 398 | | TMS9928A_configure(gfxparm); |
| 399 | | |
| 400 | | /* Initialize all. Actually, at this point, we don't know */ |
| 401 | | /* how the switches are set. Later we use the configuration switches */ |
| 402 | | /* to determine which one to use. */ |
| 403 | | ti99_floppy_controllers_init_all(machine); |
| 404 | | ti99_ide_init(machine); |
| 405 | | ti99_hsgpl_init(machine); |
| 406 | | ti99_usbsm_init(machine); |
| 407 | | |
| 408 | | /* Find the cartslot device and cache it. This is a string search, */ |
| 409 | | /* and we don't want to repeat it on each memory access. */ |
| 410 | | cartslots = machine->device("ti99_multicart"); |
| 411 | | assert(cartslots != NULL); |
| 412 | | } |
| 413 | | |
| 414 | | |
| 415 | | /* |
| 416 | | ti99_init_machine(); called before ti99_load_rom... |
| 417 | | */ |
| 418 | | MACHINE_RESET( ti99 ) |
| 419 | | { |
| 420 | | address_space *space = cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM); |
| 421 | | /*console_GROMs.data_ptr = memory_region(machine, region_grom);*/ |
| 422 | | console_GROMs.addr = 0; |
| 423 | | console_GROMs.buf = 0; |
| 424 | | console_GROMs.raddr_LSB = FALSE; |
| 425 | | console_GROMs.waddr_LSB = FALSE; |
| 426 | | |
| 427 | | expansion_box = machine->device("per_exp_box"); |
| 428 | | |
| 429 | | /* set up scratch pad pointer */ |
| 430 | | memory_set_bankptr(machine, "bank1", memory_region(machine, "maincpu") + offset_sram); |
| 431 | | |
| 432 | | if (!has_evpc) TMS9928A_reset(); |
| 433 | | else v9938_reset(0); |
| 434 | | |
| 435 | | /* clear keyboard interface state (probably overkill, but can't harm) */ |
| 436 | | KeyCol = 0; |
| 437 | | AlphaLockLine = 0; |
| 438 | | |
| 439 | | /* reset handset */ |
| 440 | | handset_buf = 0; |
| 441 | | handset_buflen = 0; |
| 442 | | handset_clock = 0; |
| 443 | | handset_ack = 0; |
| 444 | | tms9901_set_single_int(machine->device("tms9901"), 12, 0); |
| 445 | | |
| 446 | | /* read config */ |
| 447 | | xRAM_kind = input_port_read(machine, "RAM"); |
| 448 | | |
| 449 | | has_speech = input_port_read(machine, "SPEECH"); |
| 450 | | has_ide = input_port_read(machine, "HDCTRL") & HD_IDE; |
| 451 | | has_rs232 = input_port_read(machine, "SERIAL") & SERIAL_TI; |
| 452 | | has_handset = (ti99_model == model_99_4)? (input_port_read(machine, "HCI") & HCI_IR) : FALSE; |
| 453 | | has_mecmouse = input_port_read(machine, "HCI") & HCI_MECMOUSE; |
| 454 | | has_hsgpl = input_port_read(machine, "EXTCARD") & (EXT_HSGPL_ON | EXT_HSGPL_FLASH); |
| 455 | | has_usb_sm = input_port_read(machine, "HDCTRL") & HD_USB; |
| 456 | | has_pcode = input_port_read(machine, "EXTCARD") & EXT_PCODE; |
| 457 | | |
| 458 | | hsgpl_flash_mode = input_port_read(machine, "EXTCARD") & EXT_HSGPL_FLASH; |
| 459 | | |
| 460 | | /* Get the GK settings once */ |
| 461 | | gk_switch[1] = input_port_read(machine, "GKSWITCH1"); |
| 462 | | gk_switch[2] = input_port_read(machine, "GKSWITCH2"); |
| 463 | | gk_switch[3] = input_port_read(machine, "GKSWITCH3"); |
| 464 | | gk_switch[4] = input_port_read(machine, "GKSWITCH4"); |
| 465 | | gk_switch[5] = input_port_read(machine, "GKSWITCH5"); |
| 466 | | |
| 467 | | // set up optional expansion hardware |
| 468 | | if (has_speech) |
| 469 | | { |
| 470 | | static const spchroms_interface speech_intf = { region_speech_rom }; |
| 471 | | |
| 472 | | spchroms_config(machine, &speech_intf); |
| 473 | | |
| 474 | | memory_install_read16_handler(space, 0x9000, 0x93ff, 0, 0, ti99_rspeech_r); |
| 475 | | memory_install_write16_handler(space, 0x9400, 0x97ff, 0, 0, ti99_wspeech_w); |
| 476 | | } |
| 477 | | else |
| 478 | | { |
| 479 | | memory_install_read16_handler(space, 0x9000, 0x93ff, 0, 0, ti99_nop_8_r); |
| 480 | | memory_install_write16_handler(space, 0x9400, 0x97ff, 0, 0, ti99_nop_8_w); |
| 481 | | } |
| 482 | | |
| 483 | | switch (xRAM_kind) |
| 484 | | { |
| 485 | | case RAM_TI32: |
| 486 | | ti99_TIxram_init(machine); |
| 487 | | break; |
| 488 | | case RAM_SUPERAMS1024: |
| 489 | | ti99_sAMSxram_init(machine); |
| 490 | | break; |
| 491 | | case RAM_FOUNDATION128: |
| 492 | | case RAM_FOUNDATION512: |
| 493 | | case RAM_MYARC128: |
| 494 | | case RAM_MYARC512: |
| 495 | | ti99_myarcxram_init(machine); |
| 496 | | break; |
| 497 | | case RAM_NONE: |
| 498 | | default: |
| 499 | | memory_install_read16_handler(space, 0x2000, 0x3fff, 0, 0, ti99_nop_8_r); |
| 500 | | memory_install_write16_handler(space, 0x2000, 0x3fff, 0, 0, ti99_nop_8_w); |
| 501 | | memory_install_read16_handler(space, 0xa000, 0xffff, 0, 0, ti99_nop_8_r); |
| 502 | | memory_install_write16_handler(space, 0xa000, 0xffff, 0, 0, ti99_nop_8_w); |
| 503 | | break; |
| 504 | | } |
| 505 | | |
| 506 | | switch (input_port_read(machine, "DISKCTRL")) |
| 507 | | { |
| 508 | | case DISK_TIFDC: |
| 509 | | ti99_fdc_reset(machine); |
| 510 | | break; |
| 511 | | #if HAS_99CCFDC |
| 512 | | case DISK_CC: |
| 513 | | ti99_ccfdc_reset(machine); |
| 514 | | break; |
| 515 | | #endif |
| 516 | | case DISK_BWG: |
| 517 | | ti99_bwg_reset(machine); |
| 518 | | break; |
| 519 | | case DISK_HFDC: |
| 520 | | ti99_hfdc_reset(machine); |
| 521 | | break; |
| 522 | | case DISK_NONE: |
| 523 | | break; |
| 524 | | } |
| 525 | | |
| 526 | | if (has_ide) |
| 527 | | ti99_ide_reset(machine, FALSE); |
| 528 | | |
| 529 | | if (has_hsgpl) |
| 530 | | ti99_hsgpl_reset(machine); |
| 531 | | else |
| 532 | | hsgpl_crdena = 0; |
| 533 | | |
| 534 | | if (has_usb_sm) |
| 535 | | ti99_usbsm_reset(machine, FALSE); |
| 536 | | |
| 537 | | if (has_evpc) |
| 538 | | { |
| 539 | | /* Get the EVPC settings once */ |
| 540 | | evpc_switch[0] = input_port_read(machine, "EVPC-SW1"); |
| 541 | | evpc_switch[1] = input_port_read(machine, "EVPC-SW3"); |
| 542 | | evpc_switch[2] = input_port_read(machine, "EVPC-SW4"); |
| 543 | | evpc_switch[3] = input_port_read(machine, "EVPC-SW8"); |
| 544 | | ti99_evpc_reset(machine); |
| 545 | | } |
| 546 | | |
| 547 | | if (has_pcode) |
| 548 | | ti99_pcode_reset(machine); |
| 549 | | |
| 550 | | /* initialize mechatronics mouse */ |
| 551 | | mecmouse_sel = 0; |
| 552 | | mecmouse_read_y = 0; |
| 553 | | mecmouse_x = 0; |
| 554 | | mecmouse_y = 0; |
| 555 | | } |
| 556 | | |
| 557 | | #ifdef UNUSED_FUNCTION |
| 558 | | void machine_stop_ti99(void) |
| 559 | | { |
| 560 | | if (has_hsgpl) |
| 561 | | ti99_hsgpl_save_memcard(); |
| 562 | | |
| 563 | | if (has_rs232) |
| 564 | | ti99_rs232_cleanup(); |
| 565 | | |
| 566 | | tms9901_cleanup(0); |
| 567 | | } |
| 568 | | #endif |
| 569 | | |
| 570 | | NVRAM_HANDLER( ti99 ) |
| 571 | | { |
| 572 | | // called from generic.c |
| 573 | | // (running_machine *machine, mame_file *file, int read_or_write) |
| 574 | | |
| 575 | | astring *hsname = astring_assemble_3(astring_alloc(), machine->gamedrv->name, PATH_SEPARATOR, "hsgpl.nv"); |
| 576 | | astring *evname = astring_assemble_3(astring_alloc(), machine->gamedrv->name, PATH_SEPARATOR, "evpc.nv"); |
| 577 | | |
| 578 | | // We always try to read the HSGPL, even when the config switch is |
| 579 | | // set to off. Otherwise, when the system is started without HSGPL |
| 580 | | // and we later switch on the card and do a reset, the console locks |
| 581 | | // up as it does not find any content. Likewise, we save the contents |
| 582 | | // even when the card has been turned off during runtime. |
| 583 | | if (read_or_write==0) |
| 584 | | { |
| 585 | | ti99_hsgpl_load_flashroms(machine, astring_c(hsname)); |
| 586 | | if (has_evpc) ti99_evpc_load_novram(machine, astring_c(evname)); |
| 587 | | } |
| 588 | | else |
| 589 | | { |
| 590 | | ti99_hsgpl_save_flashroms(machine, astring_c(hsname)); |
| 591 | | if (has_evpc) ti99_evpc_save_novram(machine, astring_c(evname)); |
| 592 | | } |
| 593 | | |
| 594 | | astring_free(hsname); |
| 595 | | astring_free(evname); |
| 596 | | } |
| 597 | | |
| 598 | | INPUT_CHANGED( hsgpl_changed ) |
| 599 | | { |
| 600 | | hsgpl_flash_mode = newval & EXT_HSGPL_FLASH; |
| 601 | | } |
| 602 | | |
| 603 | | INPUT_CHANGED( gk_changed ) |
| 604 | | { |
| 605 | | gk_switch[(UINT64)param&0x07] = newval; |
| 606 | | } |
| 607 | | |
| 608 | | UINT8 get_gk_switch(int index) |
| 609 | | { |
| 610 | | return gk_switch[index]; |
| 611 | | } |
| 612 | | |
| 613 | | /* |
| 614 | | VBL interrupt (mmm... actually, it happens when the beam enters the lower |
| 615 | | border, so it is not a genuine VBI, but who cares ?) |
| 616 | | */ |
| 617 | | INTERRUPT_GEN( ti99_vblank_interrupt ) |
| 618 | | { |
| 619 | | TMS9928A_interrupt(device->machine); |
| 620 | | if (has_handset) |
| 621 | | ti99_handset_task(device->machine); |
| 622 | | if (has_mecmouse) |
| 623 | | mecmouse_poll(device->machine); |
| 624 | | } |
| 625 | | |
| 626 | | /* |
| 627 | | Backdoor function that is called by hsgpl, so that the machine code knows |
| 628 | | whether hsgpl or console GROMs are active |
| 629 | | Called from 99_hsgpl.c |
| 630 | | */ |
| 631 | | void ti99_set_hsgpl_crdena(int data) |
| 632 | | { |
| 633 | | hsgpl_crdena = (data != 0); |
| 634 | | } |
| 635 | | |
| 636 | | /*===========================================================================*/ |
| 637 | | #if 0 |
| 638 | | #pragma mark - |
| 639 | | #pragma mark MEMORY HANDLERS |
| 640 | | #endif |
| 641 | | /* |
| 642 | | Memory handlers. |
| 643 | | |
| 644 | | TODO: |
| 645 | | * actually implement GRAM support and GPL port support |
| 646 | | */ |
| 647 | | |
| 648 | | /* |
| 649 | | Same as MRA16_NOP, but with an additionnal delay caused by the 16->8 bit |
| 650 | | bus multiplexer. |
| 651 | | */ |
| 652 | | READ16_HANDLER ( ti99_nop_8_r ) |
| 653 | | { |
| 654 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 655 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 656 | | |
| 657 | | return (0); |
| 658 | | } |
| 659 | | |
| 660 | | WRITE16_HANDLER ( ti99_nop_8_w ) |
| 661 | | { |
| 662 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 663 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 664 | | } |
| 665 | | |
| 666 | | /* |
| 667 | | Cartridge space (>6000->7FFF). This is usually connected to the cartridge |
| 668 | | port, but is also used by the HSGPL. So we need to check this here and then |
| 669 | | delegate to the respective component. |
| 670 | | */ |
| 671 | | READ16_HANDLER ( ti99_cart_r ) |
| 672 | | { |
| 673 | | if (hsgpl_crdena && !hsgpl_flash_mode) |
| 674 | | /* hsgpl is enabled */ |
| 675 | | return ti99_hsgpl_rom6_r(space, offset, mem_mask); |
| 676 | | return ti99_multicart_r(cartslots, offset, mem_mask); |
| 677 | | } |
| 678 | | |
| 679 | | /* |
| 680 | | Cartridge space (>6000->7FFF). This is usually connected to the cartridge |
| 681 | | port, but is also used by the HSGPL. So we need to check this here and then |
| 682 | | delegate to the respective component. |
| 683 | | */ |
| 684 | | WRITE16_HANDLER ( ti99_cart_w ) |
| 685 | | { |
| 686 | | // Writing always means that we access the cartridges (and possibly the HGSPL) |
| 687 | | // Except when we use the SGCPU. |
| 688 | | if (ti99_model != model_99_4p) |
| 689 | | ti99_multicart_w(cartslots, offset, data, mem_mask); |
| 690 | | |
| 691 | | if (hsgpl_crdena && !hsgpl_flash_mode) |
| 692 | | /* hsgpl is enabled */ |
| 693 | | ti99_hsgpl_rom6_w(space, offset, data, mem_mask); |
| 694 | | } |
| 695 | | |
| 696 | | /*---------------------------------------------------------------------------*/ |
| 697 | | /* |
| 698 | | memory-mapped register handlers. |
| 699 | | |
| 700 | | Theory: |
| 701 | | |
| 702 | | These are registers to communicate with several peripherals. |
| 703 | | These registers are all 8-bit-wide, and are located at even adresses between |
| 704 | | 0x8400 and 0x9FFE. |
| 705 | | The registers are identified by (addr & 1C00), and, for VDP and GPL access, |
| 706 | | by (addr & 2). These registers are either read-only or write-only. (Or, |
| 707 | | more accurately, (addr & 4000) disables register read, whereas |
| 708 | | !(addr & 4000) disables register write.) |
| 709 | | |
| 710 | | Memory mapped registers list: |
| 711 | | - write sound chip. (8400-87FE) |
| 712 | | - read VDP memory. (8800-8BFC), (addr&2) == 0 |
| 713 | | - read VDP status. (8802-8BFE), (addr&2) == 2 |
| 714 | | - write VDP memory. (8C00-8FFC), (addr&2) == 0 |
| 715 | | - write VDP address and VDP register. (8C02-8FFE), (addr&2) == 2 |
| 716 | | - read speech synthesis chip. (9000-93FE) |
| 717 | | - write speech synthesis chip. (9400-97FE) |
| 718 | | - read GPL memory. (9800-9BFC), (addr&2) == 0 (1) |
| 719 | | - read GPL adress. (9802-9BFE), (addr&2) == 2 (1) |
| 720 | | - write GPL memory. (9C00-9FFC), (addr&2) == 0 (1) |
| 721 | | - write GPL adress. (9C02-9FFE), (addr&2) == 2 (1) |
| 722 | | |
| 723 | | (1) on some hardware, (addr & 0x3FC) provides a GPL page number. |
| 724 | | */ |
| 725 | | |
| 726 | | /* |
| 727 | | TMS9919 sound chip write |
| 728 | | */ |
| 729 | | WRITE16_HANDLER ( ti99_wsnd_w ) |
| 730 | | { |
| 731 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 732 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 733 | | |
| 734 | | sn76496_w(space->machine->device("sn76496"), offset, (data >> 8) & 0xff); |
| 735 | | } |
| 736 | | |
| 737 | | /* |
| 738 | | TMS9918(A)/9928(A)/9929(A) VDP read |
| 739 | | */ |
| 740 | | READ16_HANDLER ( ti99_rvdp_r ) |
| 741 | | { |
| 742 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 743 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 744 | | |
| 745 | | if (offset & 1) |
| 746 | | { /* read VDP status */ |
| 747 | | return ((int) TMS9928A_register_r(space, 0)) << 8; |
| 748 | | } |
| 749 | | else |
| 750 | | { /* read VDP RAM */ |
| 751 | | return ((int) TMS9928A_vram_r(space, 0)) << 8; |
| 752 | | } |
| 753 | | } |
| 754 | | |
| 755 | | /* |
| 756 | | TMS9918(A)/9928(A)/9929(A) vdp write |
| 757 | | */ |
| 758 | | WRITE16_HANDLER ( ti99_wvdp_w ) |
| 759 | | { |
| 760 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 761 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 762 | | |
| 763 | | if (offset & 1) |
| 764 | | { /* write VDP address */ |
| 765 | | TMS9928A_register_w(space, 0, (data >> 8) & 0xff); |
| 766 | | } |
| 767 | | else |
| 768 | | { /* write VDP data */ |
| 769 | | TMS9928A_vram_w(space, 0, (data >> 8) & 0xff); |
| 770 | | } |
| 771 | | } |
| 772 | | |
| 773 | | /* |
| 774 | | TMS0285 speech chip read |
| 775 | | */ |
| 776 | | static READ16_HANDLER ( ti99_rspeech_r ) |
| 777 | | { |
| 778 | | cpu_adjust_icount(space->machine->device("maincpu"),-(18+3)); /* this is just a minimum, it can be more */ |
| 779 | | |
| 780 | | return ((int) tms5220_status_r(space->machine->device("tmc0285"), offset)) << 8; |
| 781 | | } |
| 782 | | |
| 783 | | #if 0 |
| 784 | | |
| 785 | | static void speech_kludge_callback(int dummy) |
| 786 | | { |
| 787 | | if (! tms5220_readyq_r()) |
| 788 | | { |
| 789 | | /* Weirdly enough, we are always seeing some problems even */ |
| 790 | | /* though everything is working fine. */ |
| 791 | | attotime time_to_ready = double_to_attotime(tms5220_time_to_ready()); |
| 792 | | logerror("ti99/4a speech says aaargh!\n"); |
| 793 | | logerror("(time to ready: %f -> %d)\n", time_to_ready, (int) ceil(3000000*time_to_ready)); |
| 794 | | } |
| 795 | | } |
| 796 | | |
| 797 | | #endif |
| 798 | | |
| 799 | | /* |
| 800 | | TMS0285 speech chip write |
| 801 | | */ |
| 802 | | static WRITE16_HANDLER ( ti99_wspeech_w ) |
| 803 | | { |
| 804 | | cpu_adjust_icount(space->machine->device("maincpu"),-(54+3)); /* this is just an approx. minimum, it can be much more */ |
| 805 | | |
| 806 | | #if 1 |
| 807 | | /* the stupid design of the tms5220 core means that ready is cleared */ |
| 808 | | /* when there are 15 bytes in FIFO. It should be 16. Of course, if */ |
| 809 | | /* it were the case, we would need to store the value on the bus, */ |
| 810 | | /* which would be more complex. */ |
| 811 | | if (! tms5220_readyq_r(space->machine->device("tmc0285"))) |
| 812 | | { |
| 813 | | attotime time_to_ready = double_to_attotime(tms5220_time_to_ready(space->machine->device("tmc0285"))); |
| 814 | | int cycles_to_ready = space->machine->device<cpu_device>("maincpu")->attotime_to_cycles(time_to_ready); |
| 815 | | |
| 816 | | logerror("time to ready: %f -> %d\n", attotime_to_double(time_to_ready), (int) cycles_to_ready); |
| 817 | | |
| 818 | | cpu_adjust_icount(space->machine->device("maincpu"),-cycles_to_ready); |
| 819 | | timer_set(space->machine, attotime_zero, NULL, 0, /*speech_kludge_callback*/NULL); |
| 820 | | } |
| 821 | | #endif |
| 822 | | |
| 823 | | tms5220_data_w(space->machine->device("tmc0285"), offset, (data >> 8) & 0xff); |
| 824 | | } |
| 825 | | |
| 826 | | static UINT8 GROM_dataread(running_machine *machine, offs_t offset) |
| 827 | | { |
| 828 | | UINT8 reply = 0, replycart; |
| 829 | | int override_console = FALSE; |
| 830 | | |
| 831 | | // Operation of GRAMKracker |
| 832 | | if (input_port_read(machine, "CARTSLOT")==CART_GK) |
| 833 | | { |
| 834 | | if (((console_GROMs.addr < 0x2000) && (gk_switch[2]==GK_GRAM0)) |
| 835 | | || ((console_GROMs.addr >= 0x2000) && (console_GROMs.addr < 0x4000) && |
| 836 | | ((gk_switch[3]==GK_GRAM12) || (gk_switch[5]==GK_LDON))) |
| 837 | | || ((console_GROMs.addr >= 0x4000) && (console_GROMs.addr < 0x6000) && |
| 838 | | (gk_switch[3]==GK_GRAM12))) |
| 839 | | { |
| 840 | | override_console = TRUE; |
| 841 | | } |
| 842 | | } |
| 843 | | |
| 844 | | if (console_GROMs.addr < 0x6000) |
| 845 | | { |
| 846 | | /* GROMs are buffered. Data is retrieved from a buffer, */ |
| 847 | | /* while the buffer is replaced with the next cell content. */ |
| 848 | | reply = console_GROMs.buf; |
| 849 | | |
| 850 | | /* Get next value, put it in buffer. Note that the GROM */ |
| 851 | | /* wraps at 8K boundaries. */ |
| 852 | | console_GROMs.buf = console_GROMs.data_ptr[console_GROMs.addr]; |
| 853 | | } |
| 854 | | |
| 855 | | // The address pointer in the cartridge system is updated by this call |
| 856 | | replycart = ti99_cartridge_grom_r(cartslots, offset); |
| 857 | | |
| 858 | | if (console_GROMs.addr >= 0x6000 || override_console) |
| 859 | | { |
| 860 | | reply = replycart; |
| 861 | | } |
| 862 | | |
| 863 | | /* Each GROM chip has its own counter, so we update all of them even */ |
| 864 | | /* when we do not access them. */ |
| 865 | | /* The program counter wraps at each GROM chip size (8K), */ |
| 866 | | /* so 0x5fff + 1 = 0x4000. */ |
| 867 | | console_GROMs.addr = ((console_GROMs.addr + 1) & 0x1FFF) | (console_GROMs.addr & 0xE000); |
| 868 | | |
| 869 | | /* Reset the read and write address flipflops. */ |
| 870 | | console_GROMs.raddr_LSB = console_GROMs.waddr_LSB = FALSE; |
| 871 | | |
| 872 | | return reply; |
| 873 | | } |
| 874 | | |
| 875 | | static void GRAM_datawrite(offs_t offset, UINT8 data) |
| 876 | | { |
| 877 | | // The console does not contain RAM at these locations. |
| 878 | | // Writing has only effect when there is a GRAM Kracker |
| 879 | | ti99_cartridge_grom_w(cartslots, offset, data); |
| 880 | | |
| 881 | | /* Each GROM chip has its own counter, so we update all of them even */ |
| 882 | | /* when we do not access them. */ |
| 883 | | /* The program counter wraps at each GROM chip size (8K), */ |
| 884 | | /* so 0x5fff + 1 = 0x4000. */ |
| 885 | | console_GROMs.addr = ((console_GROMs.addr + 1) & 0x1FFF) | (console_GROMs.addr & 0xE000); |
| 886 | | |
| 887 | | /* Reset the read and write address flipflops. */ |
| 888 | | console_GROMs.raddr_LSB = console_GROMs.waddr_LSB = FALSE; |
| 889 | | } |
| 890 | | |
| 891 | | |
| 892 | | /* |
| 893 | | GPL read |
| 894 | | */ |
| 895 | | READ16_HANDLER ( ti99_grom_r ) |
| 896 | | { |
| 897 | | UINT16 reply, replycart; |
| 898 | | |
| 899 | | cpu_adjust_icount(space->machine->device("maincpu"),-4 /*20+3*/); /* from 4 to 23? */ |
| 900 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 901 | | |
| 902 | | /* This implementation features a multislot cartridge system which */ |
| 903 | | /* is based on multiple GROM base addresses. The standard base */ |
| 904 | | /* is >9800 (slot 0). When we access the port >9804, we switch to */ |
| 905 | | /* slot 1, >9808 is slot 2, ... >983C is slot 15. Although */ |
| 906 | | /* theoretically we could address up to 256 banks, the TI operating */ |
| 907 | | /* system does not check more than 16 banks. */ |
| 908 | | /* Cartridges may also contain ROMs which need to be banked */ |
| 909 | | /* simultaneously. We use the cartridge slot number to swap the ROM */ |
| 910 | | /* at >6000, or RAM if available. I don't know whether this is the */ |
| 911 | | /* way Texas Instruments envisaged it in the OS, but it is the only */ |
| 912 | | /* plausible way to allow for GROM+ROM cartridges. */ |
| 913 | | /* Note that some cartridges may have programming flaws which only */ |
| 914 | | /* appear when the cartridge is used in a higher-numbered slot. */ |
| 915 | | /* Parsec is one example which only runs in slot 0 as the */ |
| 916 | | /* programmers hard-coded an access to GROM port 0. Note: This *may* */ |
| 917 | | /* be worked around if we make sure that switching only occurs */ |
| 918 | | /* when the address is set, not when data is read or written. But */ |
| 919 | | /* it's not clear whether this has unwanted side effects. */ |
| 920 | | |
| 921 | | if (offset & 1) |
| 922 | | { /* Read GROM address */ |
| 923 | | /* We only have one GROM program counter for the console */ |
| 924 | | /* GROMs and all cartridge GROMs in all banks(!). Hardware */ |
| 925 | | /* implementations must take this into account as well. Thus, */ |
| 926 | | /* we use a structure here in the console, rather than */ |
| 927 | | /* separate counters in each GROM chip. */ |
| 928 | | |
| 929 | | /* We call the cartridge system here, but we ignore the */ |
| 930 | | /* value. This is done to sync the flags. */ |
| 931 | | replycart = ti99_cartridge_grom_r(cartslots, (offset<<1))<<8; |
| 932 | | |
| 933 | | /* When reading, reset the hi/lo flag byte for writing. */ |
| 934 | | /* TODO: Verify this with a real machine. */ |
| 935 | | console_GROMs.waddr_LSB = FALSE; |
| 936 | | |
| 937 | | /* Address reading is done in two steps; first, the high byte */ |
| 938 | | /* is transferred, then the low byte. */ |
| 939 | | if (console_GROMs.raddr_LSB) |
| 940 | | { |
| 941 | | /* second pass */ |
| 942 | | reply = (console_GROMs.addr << 8) & 0xff00; |
| 943 | | console_GROMs.raddr_LSB = FALSE; |
| 944 | | } |
| 945 | | else |
| 946 | | { |
| 947 | | /* first pass */ |
| 948 | | reply = console_GROMs.addr & 0xff00; |
| 949 | | console_GROMs.raddr_LSB = TRUE; |
| 950 | | } |
| 951 | | /* GROM address values must be globally unique. */ |
| 952 | | assert (replycart == reply); |
| 953 | | } |
| 954 | | else |
| 955 | | { |
| 956 | | /* Read GROM data */ |
| 957 | | /* Note that we shift the GROM port address to accomodate for */ |
| 958 | | /* 8-bit and 16-bit systems. */ |
| 959 | | reply = ((UINT16)GROM_dataread(space->machine, offset<<1)) << 8; |
| 960 | | } |
| 961 | | |
| 962 | | if (hsgpl_crdena) |
| 963 | | { |
| 964 | | /* We must prevent the HSGPL to override the console GROMs */ |
| 965 | | /* when it is not yet set up - or the console will get stuck */ |
| 966 | | /* in a black screen. The "flash mode" allows to read GROMs */ |
| 967 | | /* only in higher banks; writing is enabled for all banks. */ |
| 968 | | int bank = (offset>>1)&0xff; |
| 969 | | if (!hsgpl_flash_mode || (bank>=16)) |
| 970 | | { |
| 971 | | /* hsgpl buffers always override console GROM buffers */ |
| 972 | | reply = ti99_hsgpl_gpl_r(space, offset, mem_mask); |
| 973 | | } |
| 974 | | } |
| 975 | | |
| 976 | | return reply; |
| 977 | | } |
| 978 | | |
| 979 | | /* |
| 980 | | GPL write |
| 981 | | */ |
| 982 | | WRITE16_HANDLER ( ti99_grom_w ) |
| 983 | | { |
| 984 | | cpu_adjust_icount(space->machine->device("maincpu"),-4/*20+3*/); /* from 4 to 23? */ |
| 985 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 986 | | |
| 987 | | if (offset & 1) |
| 988 | | { |
| 989 | | ti99_cartridge_grom_w(cartslots, (offset<<1), (data>>8)&0xff); |
| 990 | | |
| 991 | | /* write GROM address */ |
| 992 | | /* see comments above */ |
| 993 | | console_GROMs.raddr_LSB = FALSE; |
| 994 | | |
| 995 | | /* Implements the internal flipflop. */ |
| 996 | | /* The Editor/Assembler manuals says that the current address */ |
| 997 | | /* plus one is returned. This effect is properly emulated */ |
| 998 | | /* by using a read-ahead buffer. */ |
| 999 | | if (console_GROMs.waddr_LSB) |
| 1000 | | { |
| 1001 | | /* Accept low byte (2nd write) */ |
| 1002 | | console_GROMs.addr = (console_GROMs.addr & 0xFF00) | ((data >> 8) & 0xFF); |
| 1003 | | /* Setting the address causes a new read, putting the */ |
| 1004 | | /* value into the buffer. We don't need the value here. */ |
| 1005 | | GROM_dataread(space->machine, (offset<<1)&0xfffc); |
| 1006 | | console_GROMs.waddr_LSB = FALSE; |
| 1007 | | } |
| 1008 | | else |
| 1009 | | { |
| 1010 | | /* Accept high byte (1st write) */ |
| 1011 | | console_GROMs.addr = (data & 0xFF00) | (console_GROMs.addr & 0xFF); |
| 1012 | | console_GROMs.waddr_LSB = TRUE; |
| 1013 | | } |
| 1014 | | } |
| 1015 | | else |
| 1016 | | { |
| 1017 | | /* write GRAM data */ |
| 1018 | | GRAM_datawrite(offset<<1, (data>>8)&0xff); |
| 1019 | | console_GROMs.raddr_LSB = console_GROMs.waddr_LSB = FALSE; |
| 1020 | | } |
| 1021 | | |
| 1022 | | if (hsgpl_crdena) |
| 1023 | | { |
| 1024 | | /* If the HSGPL card is used, we always attempt to write to */ |
| 1025 | | /* its memory, even in flash mode. Writing does no harm to */ |
| 1026 | | /* the internal GROMs. */ |
| 1027 | | ti99_hsgpl_gpl_w(space, offset, data, mem_mask); |
| 1028 | | } |
| 1029 | | } |
| 1030 | | |
| 1031 | | /*===========================================================================*/ |
| 1032 | | #if 0 |
| 1033 | | #pragma mark - |
| 1034 | | #pragma mark TI-99/4 HANDSETS |
| 1035 | | #endif |
| 1036 | | /* |
| 1037 | | Handset support (TI99/4 only) |
| 1038 | | |
| 1039 | | The ti99/4 was intended to support some so-called "IR remote handsets". |
| 1040 | | This feature was canceled at the last minute (reportedly ten minutes before |
| 1041 | | the introductory press conference in June 1979), but the first thousands of |
| 1042 | | 99/4 units had the required port, and the support code was seemingly not |
| 1043 | | deleted from ROMs until the introduction of the ti99/4a in 1981. You could |
| 1044 | | connect up to 4 20-key keypads, and up to 4 joysticks with a maximum |
| 1045 | | resolution of 15 levels on each axis. |
| 1046 | | |
| 1047 | | The keyboard DSR was able to couple two 20-key keypads together to emulate |
| 1048 | | a full 40-key keyboard. Keyboard modes 0, 1 and 2 would select either the |
| 1049 | | console keyboard with its two wired remote controllers (i.e. joysticks), or |
| 1050 | | remote handsets 1 and 2 with their associated IR remote controllers (i.e. |
| 1051 | | joysticks), according to which was currently active. |
| 1052 | | */ |
| 1053 | | |
| 1054 | | /* |
| 1055 | | ti99_handset_poll_bus() |
| 1056 | | |
| 1057 | | Poll the current state of the 4-bit data bus that goes from the I/R |
| 1058 | | receiver to the tms9901. |
| 1059 | | */ |
| 1060 | | static int ti99_handset_poll_bus(void) |
| 1061 | | { |
| 1062 | | return (has_handset) ? (handset_buf & 0xf) : 0; |
| 1063 | | } |
| 1064 | | |
| 1065 | | /* |
| 1066 | | ti99_handset_ack_callback() |
| 1067 | | |
| 1068 | | Handle data acknowledge sent by the ti-99/4 handset ISR (through tms9901 |
| 1069 | | line P0). This function is called by a delayed timer 30us after the state |
| 1070 | | of P0 is changed, because, in one occasion, the ISR asserts the line before |
| 1071 | | it reads the data, so we need to delay the acknowledge process. |
| 1072 | | */ |
| 1073 | | static TIMER_CALLBACK(ti99_handset_ack_callback) |
| 1074 | | { |
| 1075 | | handset_clock = ! handset_clock; |
| 1076 | | handset_buf >>= 4; |
| 1077 | | handset_buflen--; |
| 1078 | | tms9901_set_single_int(machine->device("tms9901"), 12, 0); |
| 1079 | | |
| 1080 | | if (handset_buflen == 1) |
| 1081 | | { |
| 1082 | | // Unless I am missing something, the third and last nybble of the |
| 1083 | | // message is not acknowledged by the DSR in any way, and the first nybble |
| 1084 | | // of next message is not requested for either, so we need to decide on |
| 1085 | | // our own when we can post a new event. Currently, we wait for 1000us |
| 1086 | | // after the DSR acknowledges the second nybble. |
| 1087 | | timer_set(machine, ATTOTIME_IN_USEC(1000), NULL, 0, ti99_handset_ack_callback); |
| 1088 | | } |
| 1089 | | |
| 1090 | | if (handset_buflen == 0) |
| 1091 | | /* See if we need to post a new event */ |
| 1092 | | ti99_handset_task(machine); |
| 1093 | | } |
| 1094 | | |
| 1095 | | /* |
| 1096 | | ti99_handset_set_ack() |
| 1097 | | Handler for tms9901 P0 pin (handset data acknowledge) |
| 1098 | | */ |
| 1099 | | static WRITE8_DEVICE_HANDLER( ti99_handset_set_ack ) |
| 1100 | | { |
| 1101 | | if (has_handset && handset_buflen && (data != handset_ack)) |
| 1102 | | { |
| 1103 | | handset_ack = data; |
| 1104 | | if (data == handset_clock) |
| 1105 | | /* I don't know what the real delay is, but 30us apears to be enough */ |
| 1106 | | timer_set(device->machine, ATTOTIME_IN_USEC(30), NULL, 0, ti99_handset_ack_callback); |
| 1107 | | } |
| 1108 | | } |
| 1109 | | |
| 1110 | | /* |
| 1111 | | ti99_handset_post_message() |
| 1112 | | |
| 1113 | | Post a 12-bit message: trigger an interrupt on the tms9901, and store the |
| 1114 | | message in the I/R receiver buffer so that the handset ISR will read this |
| 1115 | | message. |
| 1116 | | |
| 1117 | | message: 12-bit message to post (only the 12 LSBits are meaningful) |
| 1118 | | */ |
| 1119 | | static void ti99_handset_post_message(running_machine *machine, int message) |
| 1120 | | { |
| 1121 | | /* post message and assert interrupt */ |
| 1122 | | handset_clock = 1; |
| 1123 | | handset_buf = ~ message; |
| 1124 | | handset_buflen = 3; |
| 1125 | | tms9901_set_single_int(machine->device("tms9901"), 12, 1); |
| 1126 | | } |
| 1127 | | |
| 1128 | | /* |
| 1129 | | ti99_handset_poll_keyboard() |
| 1130 | | Poll the current state of one given handset keypad. |
| 1131 | | num: number of the keypad to poll (0-3) |
| 1132 | | Returns TRUE if the handset state has changed and a message was posted. |
| 1133 | | */ |
| 1134 | | static int ti99_handset_poll_keyboard(running_machine *machine, int num) |
| 1135 | | { |
| 1136 | | static UINT8 previous_key[max_handsets]; |
| 1137 | | |
| 1138 | | UINT32 key_buf; |
| 1139 | | UINT8 current_key; |
| 1140 | | int i; |
| 1141 | | static const char *const keynames[] = { "KP0", "KP1", "KP2", "KP3", "KP4" }; |
| 1142 | | |
| 1143 | | /* read current key state */ |
| 1144 | | key_buf = ( input_port_read(machine, keynames[num]) | (input_port_read(machine, keynames[num + 1]) << 16) ) >> (4*num); |
| 1145 | | |
| 1146 | | // If a key was previously pressed, this key was not shift, and this key is |
| 1147 | | // still down, then don't change the current key press. |
| 1148 | | if (previous_key[num] && (previous_key[num] != 0x24) |
| 1149 | | && (key_buf & (1 << (previous_key[num] & 0x1f)))) |
| 1150 | | { |
| 1151 | | /* check the shift modifier state */ |
| 1152 | | if (((previous_key[num] & 0x20) != 0) == ((key_buf & 0x0008) != 0)) |
| 1153 | | /* the shift modifier state has not changed */ |
| 1154 | | return FALSE; |
| 1155 | | else |
| 1156 | | { |
| 1157 | | // The shift modifier state has changed: we need to update the |
| 1158 | | // keyboard state |
| 1159 | | if (key_buf & 0x0008) |
| 1160 | | { /* shift has been pressed down */ |
| 1161 | | previous_key[num] = current_key = previous_key[num] | 0x20; |
| 1162 | | } |
| 1163 | | else |
| 1164 | | { /* shift has been pressed down */ |
| 1165 | | previous_key[num] = current_key = previous_key[num] & ~0x20; |
| 1166 | | } |
| 1167 | | /* post message */ |
| 1168 | | ti99_handset_post_message(machine, (((unsigned) current_key) << 4) | (num << 1)); |
| 1169 | | |
| 1170 | | return TRUE; |
| 1171 | | } |
| 1172 | | |
| 1173 | | } |
| 1174 | | |
| 1175 | | current_key = 0; /* default value if no key is down */ |
| 1176 | | for (i=0; i<20; i++) |
| 1177 | | { |
| 1178 | | if (key_buf & (1 << i)) |
| 1179 | | { |
| 1180 | | current_key = i + 1; |
| 1181 | | if (key_buf & 0x0008) |
| 1182 | | current_key |= 0x20; /* set shift flag */ |
| 1183 | | |
| 1184 | | if (current_key != 0x24) |
| 1185 | | // If this is the shift key, any other key we may find will |
| 1186 | | // have higher priority; otherwise, we may exit the loop and keep |
| 1187 | | // the key we have just found. |
| 1188 | | break; |
| 1189 | | } |
| 1190 | | } |
| 1191 | | |
| 1192 | | if (current_key != previous_key[num]) |
| 1193 | | { |
| 1194 | | previous_key[num] = current_key; |
| 1195 | | |
| 1196 | | /* post message */ |
| 1197 | | ti99_handset_post_message(machine, (((unsigned) current_key) << 4) | (num << 1)); |
| 1198 | | |
| 1199 | | return TRUE; |
| 1200 | | } |
| 1201 | | |
| 1202 | | return FALSE; |
| 1203 | | } |
| 1204 | | |
| 1205 | | /* |
| 1206 | | ti99_handset_poll_joystick() |
| 1207 | | |
| 1208 | | Poll the current state of one given handset joystick. |
| 1209 | | num: number of the joystick to poll (0-3) |
| 1210 | | Returns TRUE if the handset state has changed and a message was posted. |
| 1211 | | */ |
| 1212 | | static int ti99_handset_poll_joystick(running_machine *machine, int num) |
| 1213 | | { |
| 1214 | | static UINT8 previous_joy[max_handsets]; |
| 1215 | | UINT8 current_joy; |
| 1216 | | int current_joy_x, current_joy_y; |
| 1217 | | int message; |
| 1218 | | static const char *const joynames[2][4] = |
| 1219 | | { |
| 1220 | | { "JOY0", "JOY2", "JOY4", "JOY6" }, // X axis |
| 1221 | | { "JOY1", "JOY3", "JOY5", "JOY7" } // Y axis |
| 1222 | | }; |
| 1223 | | |
| 1224 | | /* read joystick position */ |
| 1225 | | current_joy_x = input_port_read(machine, joynames[0][num]); |
| 1226 | | current_joy_y = input_port_read(machine, joynames[1][num]); |
| 1227 | | /* compare with last saved position */ |
| 1228 | | current_joy = current_joy_x | (current_joy_y << 4); |
| 1229 | | if (current_joy != previous_joy[num]) |
| 1230 | | { |
| 1231 | | /* save position */ |
| 1232 | | previous_joy[num] = current_joy; |
| 1233 | | |
| 1234 | | /* transform position to signed quantity */ |
| 1235 | | current_joy_x -= 7; |
| 1236 | | current_joy_y -= 7; |
| 1237 | | |
| 1238 | | message = 0; |
| 1239 | | |
| 1240 | | /* set sign */ |
| 1241 | | // note that we set the sign if the joystick position is 0 to work |
| 1242 | | // around a bug in the ti99/4 ROMs |
| 1243 | | if (current_joy_x <= 0) |
| 1244 | | { |
| 1245 | | message |= 0x040; |
| 1246 | | current_joy_x = - current_joy_x; |
| 1247 | | } |
| 1248 | | |
| 1249 | | if (current_joy_y <= 0) |
| 1250 | | { |
| 1251 | | message |= 0x400; |
| 1252 | | current_joy_y = - current_joy_y; |
| 1253 | | } |
| 1254 | | |
| 1255 | | /* convert absolute values to Gray code and insert in message */ |
| 1256 | | if (current_joy_x & 4) |
| 1257 | | current_joy_x ^= 3; |
| 1258 | | if (current_joy_x & 2) |
| 1259 | | current_joy_x ^= 1; |
| 1260 | | message |= current_joy_x << 3; |
| 1261 | | |
| 1262 | | if (current_joy_y & 4) |
| 1263 | | current_joy_y ^= 3; |
| 1264 | | if (current_joy_y & 2) |
| 1265 | | current_joy_y ^= 1; |
| 1266 | | message |= current_joy_y << 7; |
| 1267 | | |
| 1268 | | /* set joystick address */ |
| 1269 | | message |= (num << 1) | 0x1; |
| 1270 | | |
| 1271 | | /* post message */ |
| 1272 | | ti99_handset_post_message(machine, message); |
| 1273 | | |
| 1274 | | return TRUE; |
| 1275 | | } |
| 1276 | | |
| 1277 | | return FALSE; |
| 1278 | | } |
| 1279 | | |
| 1280 | | /* |
| 1281 | | ti99_handset_task() |
| 1282 | | Manage handsets, posting an event if the state of any handset has changed. |
| 1283 | | */ |
| 1284 | | static void ti99_handset_task(running_machine *machine) |
| 1285 | | { |
| 1286 | | int i; |
| 1287 | | |
| 1288 | | if (handset_buflen == 0) |
| 1289 | | { /* poll every handset */ |
| 1290 | | for (i=0; i<max_handsets; i++) |
| 1291 | | if (ti99_handset_poll_joystick(machine, i)) |
| 1292 | | return; |
| 1293 | | for (i=0; i<max_handsets; i++) |
| 1294 | | if (ti99_handset_poll_keyboard(machine, i)) |
| 1295 | | return; |
| 1296 | | } |
| 1297 | | else if (handset_buflen == 3) |
| 1298 | | { /* update messages after they have been posted */ |
| 1299 | | if (handset_buf & 1) |
| 1300 | | { /* keyboard */ |
| 1301 | | ti99_handset_poll_keyboard(machine, (~ (handset_buf >> 1)) & 0x3); |
| 1302 | | } |
| 1303 | | else |
| 1304 | | { /* joystick */ |
| 1305 | | ti99_handset_poll_joystick(machine, (~ (handset_buf >> 1)) & 0x3); |
| 1306 | | } |
| 1307 | | } |
| 1308 | | } |
| 1309 | | |
| 1310 | | |
| 1311 | | /*===========================================================================*/ |
| 1312 | | #if 0 |
| 1313 | | #pragma mark - |
| 1314 | | #pragma mark MECHATRONICS MOUSE |
| 1315 | | #endif |
| 1316 | | |
| 1317 | | static void mecmouse_select(int selnow, int stick1, int stick2) |
| 1318 | | { |
| 1319 | | /* The Mechatronic mouse is connected to the joystick port and occupies |
| 1320 | | both joystick select lines and the switch lines. From these five |
| 1321 | | lines, left/right/down are used for the motion (i.e. 3 motion steps |
| 1322 | | for positive and four for negative motion and one for rest), |
| 1323 | | the fire line is used for the secondary mouse button, and the up |
| 1324 | | line is used for the primary button. |
| 1325 | | The mouse motion is delivered by the same lines for both directions; |
| 1326 | | this requires swapping the axes. According to the source code of |
| 1327 | | the accompanying mouse driver, the readout of the current axis is |
| 1328 | | done by selecting joystick 1, then joystick 2. The axis swapping is |
| 1329 | | achieved by selecting stick 1 again. When selecting stick 2, the |
| 1330 | | second axis is seen on the input lines. |
| 1331 | | Interrupting this sequence will lead to swapped axes. This is |
| 1332 | | prevented by resetting the toggle when the mouse is deselected |
| 1333 | | (neither 1 nor 2 are selected). |
| 1334 | | |
| 1335 | | The joystick lines are selected as follows: |
| 1336 | | TI-99/4: Stick 1: P4=1, P3=0, P2=1 (5) |
| 1337 | | Stick 2: P4=1, P3=1, P2=0 (6) |
| 1338 | | |
| 1339 | | TI-99/4A: Stick 1: P4=1, P3=1, P2=0 (6) |
| 1340 | | Stick 2: P4=1, P3=1, P2=1 (7) |
| 1341 | | |
| 1342 | | TI-99/8: Stick 1: P3=1, P2=1, P1=1, P0=0 (14) |
| 1343 | | Stick 2: P3=1, P2=1, P1=1, P0=1 (15) |
| 1344 | | |
| 1345 | | Geneve: n/a, has own mouse handling via v9938 |
| 1346 | | |
| 1347 | | As we can only deliver at max 3 steps positive and 4 steps negative, |
| 1348 | | we split the delta so that subsequent queries add up to the actual |
| 1349 | | delta. That is, one delta of +10 yields a 3+3+3+1. |
| 1350 | | |
| 1351 | | mecmouse_x holds the current delta to be counted down for x |
| 1352 | | (y accordingly) |
| 1353 | | |
| 1354 | | mecmouse_x_buf is the current step count reported to CRU |
| 1355 | | |
| 1356 | | Michael Zapf, 2008-01-22 |
| 1357 | | */ |
| 1358 | | |
| 1359 | | if (selnow==stick2) { |
| 1360 | | if (mecmouse_sel==stick1) { |
| 1361 | | if (! mecmouse_read_y) |
| 1362 | | { |
| 1363 | | /* Sample x motion. */ |
| 1364 | | if (mecmouse_x < -4) |
| 1365 | | mecmouse_x_buf = -4; |
| 1366 | | else if (mecmouse_x > 3) |
| 1367 | | mecmouse_x_buf = 3; |
| 1368 | | else |
| 1369 | | mecmouse_x_buf = mecmouse_x; |
| 1370 | | mecmouse_x -= mecmouse_x_buf; |
| 1371 | | mecmouse_x_buf = (mecmouse_x_buf-1) & 7; |
| 1372 | | } |
| 1373 | | else |
| 1374 | | { |
| 1375 | | /* Sample y motion. */ |
| 1376 | | if (mecmouse_y < -4) |
| 1377 | | mecmouse_y_buf = -4; |
| 1378 | | else if (mecmouse_y > 3) |
| 1379 | | mecmouse_y_buf = 3; |
| 1380 | | else |
| 1381 | | mecmouse_y_buf = mecmouse_y; |
| 1382 | | mecmouse_y -= mecmouse_y_buf; |
| 1383 | | mecmouse_y_buf = (mecmouse_y_buf-1) & 7; |
| 1384 | | } |
| 1385 | | } |
| 1386 | | mecmouse_sel = selnow; |
| 1387 | | } |
| 1388 | | else if (selnow==stick1) |
| 1389 | | { |
| 1390 | | if (mecmouse_sel==stick2) |
| 1391 | | { |
| 1392 | | /* Swap the axes. */ |
| 1393 | | mecmouse_read_y = ! mecmouse_read_y; |
| 1394 | | } |
| 1395 | | mecmouse_sel = selnow; |
| 1396 | | } |
| 1397 | | else |
| 1398 | | { |
| 1399 | | /* Reset the axis toggle when the mouse is deselected */ |
| 1400 | | mecmouse_read_y = 0; |
| 1401 | | } |
| 1402 | | } |
| 1403 | | |
| 1404 | | static void mecmouse_poll(running_machine *machine) |
| 1405 | | { |
| 1406 | | static int last_mx = 0, last_my = 0; |
| 1407 | | int new_mx, new_my; |
| 1408 | | int delta_x, delta_y; |
| 1409 | | |
| 1410 | | new_mx = input_port_read(machine, "MOUSEX"); |
| 1411 | | new_my = input_port_read(machine, "MOUSEY"); |
| 1412 | | |
| 1413 | | /* compute x delta */ |
| 1414 | | delta_x = new_mx - last_mx; |
| 1415 | | |
| 1416 | | /* check for wrap */ |
| 1417 | | if (delta_x > 0x80) |
| 1418 | | delta_x = -0x100+delta_x; |
| 1419 | | if (delta_x < -0x80) |
| 1420 | | delta_x = 0x100+delta_x; |
| 1421 | | |
| 1422 | | /* Prevent unplausible values at startup. */ |
| 1423 | | if (delta_x > 100 || delta_x<-100) delta_x = 0; |
| 1424 | | |
| 1425 | | last_mx = new_mx; |
| 1426 | | |
| 1427 | | /* compute y delta */ |
| 1428 | | delta_y = new_my - last_my; |
| 1429 | | |
| 1430 | | /* check for wrap */ |
| 1431 | | if (delta_y > 0x80) |
| 1432 | | delta_y = -0x100+delta_y; |
| 1433 | | if (delta_y < -0x80) |
| 1434 | | delta_y = 0x100+delta_y; |
| 1435 | | |
| 1436 | | if (delta_y > 100 || delta_y<-100) delta_y = 0; |
| 1437 | | |
| 1438 | | last_my = new_my; |
| 1439 | | |
| 1440 | | /* update state */ |
| 1441 | | mecmouse_x += delta_x; |
| 1442 | | mecmouse_y += delta_y; |
| 1443 | | } |
| 1444 | | |
| 1445 | | /*===========================================================================*/ |
| 1446 | | #if 0 |
| 1447 | | #pragma mark - |
| 1448 | | #pragma mark TMS9901 INTERFACE |
| 1449 | | #endif |
| 1450 | | /* |
| 1451 | | TI99/4x-specific tms9901 I/O handlers |
| 1452 | | |
| 1453 | | See mess/machine/tms9901.c for generic tms9901 CRU handlers. |
| 1454 | | |
| 1455 | | BTW, although TMS9900 is generally big-endian, it is little endian as far as CRU is |
| 1456 | | concerned. (i.e. bit 0 is the least significant) |
| 1457 | | |
| 1458 | | KNOWN PROBLEMS: |
| 1459 | | * a read or write to bits 16-31 causes TMS9901 to quit timer mode. The problem is: |
| 1460 | | on a TI99/4A, any memory access causes a dummy CRU read. Therefore, TMS9901 can quit |
| 1461 | | timer mode even though the program did not explicitely ask... and THIS is impossible |
| 1462 | | to emulate efficiently (we would have to check every memory operation). |
| 1463 | | */ |
| 1464 | | /* |
| 1465 | | TMS9901 interrupt handling on a TI99/4(a). |
| 1466 | | |
| 1467 | | TI99/4(a) uses the following interrupts: |
| 1468 | | INT1: external interrupt (used by RS232 controller, for instance) |
| 1469 | | INT2: VDP interrupt |
| 1470 | | TMS9901 timer interrupt (overrides INT3) |
| 1471 | | INT12: handset interrupt (only on a TI-99/4 with the handset prototypes) |
| 1472 | | |
| 1473 | | Three (occasionally four) interrupts are used by the system (INT1, INT2, |
| 1474 | | timer, and INT12 on a TI-99/4 with remote handset prototypes), out of 15/16 |
| 1475 | | possible interrupts. Keyboard pins can be used as interrupt pins, too, but |
| 1476 | | this is not emulated (it's a trick, anyway, and I don't know any program |
| 1477 | | which uses it). |
| 1478 | | |
| 1479 | | When an interrupt line is set (and the corresponding bit in the interrupt mask is set), |
| 1480 | | a level 1 interrupt is requested from the TMS9900. This interrupt request lasts as long as |
| 1481 | | the interrupt pin and the revelant bit in the interrupt mask are set. |
| 1482 | | |
| 1483 | | TIMER interrupts are kind of an exception, since they are not associated with an external |
| 1484 | | interrupt pin, and I guess it takes a write to the 9901 CRU bit 3 ("SBO 3") to clear |
| 1485 | | the pending interrupt (or am I wrong once again ?). |
| 1486 | | |
| 1487 | | nota: |
| 1488 | | All interrupt routines notify (by software) the TMS9901 of interrupt |
| 1489 | | recognition (with a "SBO n"). However, unless I am missing something, this |
| 1490 | | has absolutely no consequence on the TMS9901 (except for the TIMER |
| 1491 | | interrupt routine), and interrupt routines would work fine without this |
| 1492 | | SBO instruction. This is quite weird. Maybe the interrupt recognition |
| 1493 | | notification is needed on TMS9985, or any other weird variant of TMS9900 |
| 1494 | | (how about the TI-99 development system connected to a TI990/10?). |
| 1495 | | */ |
| 1496 | | |
| 1497 | | /* |
| 1498 | | set the state of int1 (called by the peb core) |
| 1499 | | */ |
| 1500 | | void tms9901_set_int1(running_machine *machine, int state) |
| 1501 | | { |
| 1502 | | tms9901_set_single_int(machine->device("tms9901"), 1, state); |
| 1503 | | } |
| 1504 | | |
| 1505 | | /* |
| 1506 | | set the state of int2 (called by the tms9928 core) |
| 1507 | | */ |
| 1508 | | void tms9901_set_int2(running_machine *machine, int state) |
| 1509 | | { |
| 1510 | | tms9901_set_single_int(machine->device("tms9901"), 2, state); |
| 1511 | | } |
| 1512 | | |
| 1513 | | /* |
| 1514 | | Called by the 9901 core whenever the state of INTREQ and IC0-3 changes |
| 1515 | | */ |
| 1516 | | static TMS9901_INT_CALLBACK( tms9901_interrupt_callback ) |
| 1517 | | { |
| 1518 | | if (intreq) |
| 1519 | | { |
| 1520 | | // On TI99, TMS9900 IC0-3 lines are not connected to TMS9901, |
| 1521 | | // but hard-wired to force level 1 interrupts |
| 1522 | | cputag_set_input_line_and_vector(device->machine, "maincpu", 0, ASSERT_LINE, 1); /* interrupt it, baby */ |
| 1523 | | } |
| 1524 | | else |
| 1525 | | { |
| 1526 | | cputag_set_input_line(device->machine, "maincpu", 0, CLEAR_LINE); |
| 1527 | | } |
| 1528 | | } |
| 1529 | | |
| 1530 | | /* |
| 1531 | | Read pins INT3*-INT7* of TI99's 9901. |
| 1532 | | |
| 1533 | | signification: |
| 1534 | | (bit 1: INT1 status) |
| 1535 | | (bit 2: INT2 status) |
| 1536 | | bit 3-7: keyboard status bits 0 to 4 |
| 1537 | | */ |
| 1538 | | static READ8_DEVICE_HANDLER( ti99_R9901_0 ) |
| 1539 | | { |
| 1540 | | int answer; |
| 1541 | | static const char *const keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3" }; |
| 1542 | | |
| 1543 | | if ((ti99_model == model_99_4) && (KeyCol == 7)) |
| 1544 | | answer = (ti99_handset_poll_bus() << 3) | 0x80; |
| 1545 | | else if (has_mecmouse && (KeyCol == ((ti99_model == model_99_4) ? 6 : 7))) |
| 1546 | | { |
| 1547 | | int buttons = input_port_read(device->machine, "MOUSE0") & 3; |
| 1548 | | |
| 1549 | | answer = (mecmouse_read_y ? mecmouse_y_buf : mecmouse_x_buf) << 4; |
| 1550 | | |
| 1551 | | if (! (buttons & 1)) |
| 1552 | | /* action button */ |
| 1553 | | answer |= 0x08; |
| 1554 | | if (! (buttons & 2)) |
| 1555 | | /* home button */ |
| 1556 | | answer |= 0x80; |
| 1557 | | } |
| 1558 | | else |
| 1559 | | { |
| 1560 | | answer = ((input_port_read(device->machine, keynames[KeyCol >> 1]) >> ((KeyCol & 1) * 8)) << 3) & 0xF8; |
| 1561 | | } |
| 1562 | | |
| 1563 | | if ((ti99_model == model_99_4a) || (ti99_model == model_99_4p)) |
| 1564 | | { |
| 1565 | | if (! AlphaLockLine) |
| 1566 | | answer &= ~ (input_port_read(device->machine, "ALPHA") << 3); |
| 1567 | | } |
| 1568 | | |
| 1569 | | return answer; |
| 1570 | | } |
| 1571 | | |
| 1572 | | /* |
| 1573 | | Read pins INT8*-INT15* of TI99's 9901. |
| 1574 | | |
| 1575 | | signification: |
| 1576 | | bit 0-2: keyboard status bits 5 to 7 |
| 1577 | | bit 3: tape input mirror |
| 1578 | | (bit 4: IR remote handset interrupt) |
| 1579 | | bit 5-7: weird, not emulated |
| 1580 | | */ |
| 1581 | | static READ8_DEVICE_HANDLER( ti99_R9901_1 ) |
| 1582 | | { |
| 1583 | | int answer; |
| 1584 | | static const char *const keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3" }; |
| 1585 | | |
| 1586 | | if (/*(ti99_model == model_99_4) &&*/ (KeyCol == 7)) |
| 1587 | | answer = 0x07; |
| 1588 | | else |
| 1589 | | { |
| 1590 | | answer = ((input_port_read(device->machine, keynames[KeyCol >> 1]) >> ((KeyCol & 1) * 8)) >> 5) & 0x07; |
| 1591 | | } |
| 1592 | | |
| 1593 | | /* we don't take CS2 into account, as CS2 is a write-only unit */ |
| 1594 | | #if 0 |
| 1595 | | if (cassette_input(device->machine->device("cassette1")) > 0) |
| 1596 | | answer |= 8; |
| 1597 | | #endif |
| 1598 | | |
| 1599 | | return answer; |
| 1600 | | } |
| 1601 | | |
| 1602 | | /* |
| 1603 | | Read pins P0-P7 of TI99's 9901. |
| 1604 | | |
| 1605 | | bit 1: handset data clock pin |
| 1606 | | */ |
| 1607 | | static READ8_DEVICE_HANDLER( ti99_R9901_2 ) |
| 1608 | | { |
| 1609 | | return (has_handset && handset_clock) ? 2 : 0; |
| 1610 | | } |
| 1611 | | |
| 1612 | | /* |
| 1613 | | Read pins P8-P15 of TI99's 9901. |
| 1614 | | |
| 1615 | | bit 26: IR handset interrupt |
| 1616 | | bit 27: tape input |
| 1617 | | */ |
| 1618 | | static READ8_DEVICE_HANDLER( ti99_R9901_3 ) |
| 1619 | | { |
| 1620 | | int answer; |
| 1621 | | |
| 1622 | | if (has_handset && (handset_buflen == 3)) |
| 1623 | | answer = 0; |
| 1624 | | else |
| 1625 | | answer = 4; /* on systems without handset, the pin is pulled up to avoid spurious interrupts */ |
| 1626 | | |
| 1627 | | /* we don't take CS2 into account, as CS2 is a write-only unit */ |
| 1628 | | if ( (ti99_model != model_99_8) && (ti99_model != model_99_4p)) |
| 1629 | | { |
| 1630 | | if (cassette_input(device->machine->device("cassette1")) > 0) |
| 1631 | | answer |= 8; |
| 1632 | | } |
| 1633 | | else |
| 1634 | | { |
| 1635 | | if (cassette_input(device->machine->device("cassette")) > 0) |
| 1636 | | answer |= 8; |
| 1637 | | } |
| 1638 | | |
| 1639 | | return answer; |
| 1640 | | } |
| 1641 | | |
| 1642 | | |
| 1643 | | /* |
| 1644 | | WRITE key column select (P2-P4) |
| 1645 | | */ |
| 1646 | | static WRITE8_DEVICE_HANDLER( ti99_KeyC ) |
| 1647 | | { |
| 1648 | | if (data) |
| 1649 | | KeyCol |= 1 << (offset-2); |
| 1650 | | else |
| 1651 | | KeyCol &= ~ (1 << (offset-2)); |
| 1652 | | |
| 1653 | | if (has_mecmouse) |
| 1654 | | mecmouse_select(KeyCol, (ti99_model == model_99_4) ? 5 : 6, (ti99_model == model_99_4) ? 6 : 7); |
| 1655 | | } |
| 1656 | | |
| 1657 | | /* |
| 1658 | | WRITE alpha lock line - TI99/4a only (P5) |
| 1659 | | */ |
| 1660 | | static WRITE8_DEVICE_HANDLER( ti99_AlphaW ) |
| 1661 | | { |
| 1662 | | if ((ti99_model == model_99_4a) || (ti99_model == model_99_4p)) |
| 1663 | | AlphaLockLine = data; |
| 1664 | | } |
| 1665 | | |
| 1666 | | /* |
| 1667 | | Read pins INT8*-INT15* of TI99's 9901. |
| 1668 | | |
| 1669 | | signification: |
| 1670 | | bit 0-2: keyboard status bits 2 to 4 |
| 1671 | | bit 3: tape input mirror |
| 1672 | | (bit 4: IR remote handset interrupt) |
| 1673 | | bit 5-7: weird, not emulated |
| 1674 | | */ |
| 1675 | | static READ8_DEVICE_HANDLER( ti99_8_R9901_1 ) |
| 1676 | | { |
| 1677 | | int answer; |
| 1678 | | static const char *const keynames[] = { |
| 1679 | | "KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5", "KEY6", "KEY7", |
| 1680 | | "KEY8", "KEY9", "KEY10", "KEY11", "KEY12", "KEY13", "KEY14", "KEY15" |
| 1681 | | }; |
| 1682 | | |
| 1683 | | if (has_mecmouse && (KeyCol == 15)) |
| 1684 | | { |
| 1685 | | int buttons = input_port_read(device->machine, "MOUSE0") & 3; |
| 1686 | | |
| 1687 | | answer = ((mecmouse_read_y ? mecmouse_y_buf : mecmouse_x_buf) << 1) & 0x03; |
| 1688 | | |
| 1689 | | if (! (buttons & 2)) |
| 1690 | | /* home button */ |
| 1691 | | answer |= 0x04; |
| 1692 | | } |
| 1693 | | else |
| 1694 | | { |
| 1695 | | answer = (input_port_read(device->machine, keynames[KeyCol]) >> 2) & 0x07; |
| 1696 | | } |
| 1697 | | |
| 1698 | | /* we don't take CS2 into account, as CS2 is a write-only unit */ |
| 1699 | | #if 0 |
| 1700 | | if (cassette_input(machine->device("cassette")) > 0) |
| 1701 | | answer |= 8; |
| 1702 | | #endif |
| 1703 | | |
| 1704 | | return answer; |
| 1705 | | } |
| 1706 | | |
| 1707 | | /* |
| 1708 | | command CS1/CS2 tape unit motor (P6-P7) |
| 1709 | | */ |
| 1710 | | static WRITE8_DEVICE_HANDLER( ti99_CS_motor ) |
| 1711 | | { |
| 1712 | | running_device *img; |
| 1713 | | |
| 1714 | | if ( (ti99_model != model_99_8) && (ti99_model != model_99_4p)) |
| 1715 | | { |
| 1716 | | img = device->machine->device((offset-6 ) ? "cassette2" : "cassette1" ); |
| 1717 | | } |
| 1718 | | else |
| 1719 | | { |
| 1720 | | img = device->machine->device("cassette"); |
| 1721 | | } |
| 1722 | | cassette_change_state(img, data ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR); |
| 1723 | | } |
| 1724 | | |
| 1725 | | /* |
| 1726 | | audio gate (P8) |
| 1727 | | |
| 1728 | | Set to 1 before using tape: this enables the mixing of tape input sound |
| 1729 | | with computer sound. |
| 1730 | | |
| 1731 | | We do not really need to emulate this as the tape recorder generates sound |
| 1732 | | on its own. |
| 1733 | | */ |
| 1734 | | static WRITE8_DEVICE_HANDLER( ti99_audio_gate ) |
| 1735 | | { |
| 1736 | | } |
| 1737 | | |
| 1738 | | /* |
| 1739 | | tape output (P9) |
| 1740 | | I think polarity is correct, but don't take my word for it. |
| 1741 | | */ |
| 1742 | | static WRITE8_DEVICE_HANDLER( ti99_CS_output ) |
| 1743 | | { |
| 1744 | | if ((ti99_model != model_99_8) && (ti99_model != model_99_4p)) /* 99/8 only has one tape port!!! */ |
| 1745 | | { |
| 1746 | | cassette_output(device->machine->device("cassette1"), data ? +1 : -1); |
| 1747 | | cassette_output(device->machine->device("cassette2"), data ? +1 : -1); |
| 1748 | | } |
| 1749 | | else |
| 1750 | | { |
| 1751 | | cassette_output(device->machine->device("cassette"), data ? +1 : -1); |
| 1752 | | } |
| 1753 | | } |
| 1754 | | |
| 1755 | | /* tms9901 setup */ |
| 1756 | | const tms9901_interface tms9901reset_param_ti99_4x = |
| 1757 | | { |
| 1758 | | TMS9901_INT1 | TMS9901_INT2 | TMS9901_INTC, /* only input pins whose state is always known */ |
| 1759 | | |
| 1760 | | { /* read handlers */ |
| 1761 | | ti99_R9901_0, |
| 1762 | | ti99_R9901_1, |
| 1763 | | ti99_R9901_2, |
| 1764 | | ti99_R9901_3 |
| 1765 | | }, |
| 1766 | | |
| 1767 | | { /* write handlers */ |
| 1768 | | ti99_handset_set_ack, |
| 1769 | | NULL, |
| 1770 | | ti99_KeyC, |
| 1771 | | ti99_KeyC, |
| 1772 | | ti99_KeyC, |
| 1773 | | ti99_AlphaW, |
| 1774 | | ti99_CS_motor, |
| 1775 | | ti99_CS_motor, |
| 1776 | | ti99_audio_gate, |
| 1777 | | ti99_CS_output, |
| 1778 | | NULL, |
| 1779 | | NULL, |
| 1780 | | NULL, |
| 1781 | | NULL, |
| 1782 | | NULL, |
| 1783 | | NULL |
| 1784 | | }, |
| 1785 | | |
| 1786 | | /* interrupt handler */ |
| 1787 | | tms9901_interrupt_callback, |
| 1788 | | |
| 1789 | | /* clock rate = 3MHz */ |
| 1790 | | 3000000. |
| 1791 | | }; |
| 1792 | | |
| 1793 | | /*===========================================================================*/ |
| 1794 | | /* |
| 1795 | | TI memory extension support. |
| 1796 | | |
| 1797 | | Simple 8-bit DRAM: 32kb in two chunks of 8kb and 24kb repectively. |
| 1798 | | Since the RAM is on the 8-bit bus, there is an additionnal delay. |
| 1799 | | */ |
| 1800 | | |
| 1801 | | static READ16_HANDLER ( ti99_TIxramlow_r ); |
| 1802 | | static WRITE16_HANDLER ( ti99_TIxramlow_w ); |
| 1803 | | static READ16_HANDLER ( ti99_TIxramhigh_r ); |
| 1804 | | static WRITE16_HANDLER ( ti99_TIxramhigh_w ); |
| 1805 | | |
| 1806 | | static void ti99_TIxram_init(running_machine *machine) |
| 1807 | | { |
| 1808 | | address_space *space = cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM); |
| 1809 | | |
| 1810 | | memory_install_read16_handler(space, 0x2000, 0x3fff, 0, 0, ti99_TIxramlow_r); |
| 1811 | | memory_install_write16_handler(space, 0x2000, 0x3fff, 0, 0, ti99_TIxramlow_w); |
| 1812 | | memory_install_read16_handler(space, 0xa000, 0xffff, 0, 0, ti99_TIxramhigh_r); |
| 1813 | | memory_install_write16_handler(space, 0xa000, 0xffff, 0, 0, ti99_TIxramhigh_w); |
| 1814 | | } |
| 1815 | | |
| 1816 | | /* low 8 kb: 0x2000-0x3fff */ |
| 1817 | | static READ16_HANDLER ( ti99_TIxramlow_r ) |
| 1818 | | { |
| 1819 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 1820 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 1821 | | |
| 1822 | | return xRAM_ptr[offset]; |
| 1823 | | } |
| 1824 | | |
| 1825 | | static WRITE16_HANDLER ( ti99_TIxramlow_w ) |
| 1826 | | { |
| 1827 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 1828 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 1829 | | COMBINE_DATA(xRAM_ptr + offset); |
| 1830 | | } |
| 1831 | | |
| 1832 | | /* high 24 kb: 0xa000-0xffff */ |
| 1833 | | static READ16_HANDLER ( ti99_TIxramhigh_r ) |
| 1834 | | { |
| 1835 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 1836 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 1837 | | return xRAM_ptr[offset+0x1000]; |
| 1838 | | } |
| 1839 | | |
| 1840 | | static WRITE16_HANDLER ( ti99_TIxramhigh_w ) |
| 1841 | | { |
| 1842 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 1843 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 1844 | | |
| 1845 | | COMBINE_DATA(xRAM_ptr + offset+0x1000); |
| 1846 | | } |
| 1847 | | |
| 1848 | | |
| 1849 | | /*===========================================================================*/ |
| 1850 | | /* |
| 1851 | | Super AMS memory extension support. |
| 1852 | | |
| 1853 | | Up to 1Mb of SRAM. Straightforward mapper, works with 4kb chunks. The mapper was designed |
| 1854 | | to be extendable with other RAM areas. |
| 1855 | | */ |
| 1856 | | |
| 1857 | | /* prototypes */ |
| 1858 | | static void sAMS_cru_w(running_machine *machine, int offset, int data); |
| 1859 | | static READ8_HANDLER(sAMS_mapper_r); |
| 1860 | | static WRITE8_HANDLER(sAMS_mapper_w); |
| 1861 | | |
| 1862 | | static READ16_HANDLER ( ti99_sAMSxramlow_r ); |
| 1863 | | static WRITE16_HANDLER ( ti99_sAMSxramlow_w ); |
| 1864 | | static READ16_HANDLER ( ti99_sAMSxramhigh_r ); |
| 1865 | | static WRITE16_HANDLER ( ti99_sAMSxramhigh_w ); |
| 1866 | | |
| 1867 | | |
| 1868 | | static const ti99_peb_card_handlers_t sAMS_expansion_handlers = |
| 1869 | | { |
| 1870 | | NULL, |
| 1871 | | sAMS_cru_w, |
| 1872 | | sAMS_mapper_r, |
| 1873 | | sAMS_mapper_w |
| 1874 | | }; |
| 1875 | | |
| 1876 | | |
| 1877 | | static int sAMS_mapper_on; |
| 1878 | | static int sAMSlookup[16]; |
| 1879 | | |
| 1880 | | /* set up super AMS handlers, and set initial state */ |
| 1881 | | static void ti99_sAMSxram_init(running_machine *machine) |
| 1882 | | { |
| 1883 | | int i; |
| 1884 | | |
| 1885 | | address_space *space = cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM); |
| 1886 | | |
| 1887 | | memory_install_read16_handler(space, 0x2000, 0x3fff, 0, 0, ti99_sAMSxramlow_r); |
| 1888 | | memory_install_write16_handler(space, 0x2000, 0x3fff, 0, 0, ti99_sAMSxramlow_w); |
| 1889 | | memory_install_read16_handler(space, 0xa000, 0xffff, 0, 0, ti99_sAMSxramhigh_r); |
| 1890 | | memory_install_write16_handler(space, 0xa000, 0xffff, 0, 0, ti99_sAMSxramhigh_w); |
| 1891 | | |
| 1892 | | ti99_peb_set_card_handlers(expansion_box, 0x1e00, & sAMS_expansion_handlers); |
| 1893 | | |
| 1894 | | sAMS_mapper_on = 0; |
| 1895 | | |
| 1896 | | for (i=0; i<16; i++) |
| 1897 | | sAMSlookup[i] = i << 11; |
| 1898 | | } |
| 1899 | | |
| 1900 | | /* write CRU bit: |
| 1901 | | bit0: enable/disable mapper registers in DSR space, |
| 1902 | | bit1: enable/disable address mapping */ |
| 1903 | | static void sAMS_cru_w(running_machine *machine, int offset, int data) |
| 1904 | | { |
| 1905 | | if (offset == 1) |
| 1906 | | sAMS_mapper_on = data; |
| 1907 | | } |
| 1908 | | |
| 1909 | | /* read a mapper register */ |
| 1910 | | static READ8_HANDLER(sAMS_mapper_r) |
| 1911 | | { |
| 1912 | | return (sAMSlookup[(offset >> 1) & 0xf] >> 11); |
| 1913 | | } |
| 1914 | | |
| 1915 | | /* write a mapper register */ |
| 1916 | | static WRITE8_HANDLER(sAMS_mapper_w) |
| 1917 | | { |
| 1918 | | sAMSlookup[(offset >> 1) & 0xf] = ((int) data) << 11; |
| 1919 | | } |
| 1920 | | |
| 1921 | | /* low 8 kb: 0x2000-0x3fff */ |
| 1922 | | static READ16_HANDLER ( ti99_sAMSxramlow_r ) |
| 1923 | | { |
| 1924 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 1925 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 1926 | | |
| 1927 | | if (sAMS_mapper_on) |
| 1928 | | return xRAM_ptr[(offset&0x7ff)+sAMSlookup[(0x1000+offset)>>11]]; |
| 1929 | | else |
| 1930 | | return xRAM_ptr[offset+0x1000]; |
| 1931 | | } |
| 1932 | | |
| 1933 | | static WRITE16_HANDLER ( ti99_sAMSxramlow_w ) |
| 1934 | | { |
| 1935 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 1936 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 1937 | | |
| 1938 | | if (sAMS_mapper_on) |
| 1939 | | COMBINE_DATA(xRAM_ptr + (offset&0x7ff)+sAMSlookup[(0x1000+offset)>>11]); |
| 1940 | | else |
| 1941 | | COMBINE_DATA(xRAM_ptr + offset+0x1000); |
| 1942 | | } |
| 1943 | | |
| 1944 | | /* high 24 kb: 0xa000-0xffff */ |
| 1945 | | static READ16_HANDLER ( ti99_sAMSxramhigh_r ) |
| 1946 | | { |
| 1947 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 1948 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 1949 | | |
| 1950 | | if (sAMS_mapper_on) |
| 1951 | | return xRAM_ptr[(offset&0x7ff)+sAMSlookup[(0x5000+offset)>>11]]; |
| 1952 | | else |
| 1953 | | return xRAM_ptr[offset+0x5000]; |
| 1954 | | } |
| 1955 | | |
| 1956 | | static WRITE16_HANDLER ( ti99_sAMSxramhigh_w ) |
| 1957 | | { |
| 1958 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 1959 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 1960 | | |
| 1961 | | if (sAMS_mapper_on) |
| 1962 | | COMBINE_DATA(xRAM_ptr + (offset&0x7ff)+sAMSlookup[(0x5000+offset)>>11]); |
| 1963 | | else |
| 1964 | | COMBINE_DATA(xRAM_ptr + offset+0x5000); |
| 1965 | | } |
| 1966 | | |
| 1967 | | /*===========================================================================*/ |
| 1968 | | /* |
| 1969 | | myarc-ish memory extension support (foundation, and myarc clones). |
| 1970 | | |
| 1971 | | Up to 512kb of RAM. Straightforward mapper, works with 32kb chunks. |
| 1972 | | */ |
| 1973 | | |
| 1974 | | static int myarc_cru_r(running_machine *machine, int offset); |
| 1975 | | static void myarc_cru_w(running_machine *machine, int offset, int data); |
| 1976 | | |
| 1977 | | static READ16_HANDLER ( ti99_myarcxramlow_r ); |
| 1978 | | static WRITE16_HANDLER ( ti99_myarcxramlow_w ); |
| 1979 | | static READ16_HANDLER ( ti99_myarcxramhigh_r ); |
| 1980 | | static WRITE16_HANDLER ( ti99_myarcxramhigh_w ); |
| 1981 | | |
| 1982 | | |
| 1983 | | static const ti99_peb_card_handlers_t myarc_expansion_handlers = |
| 1984 | | { |
| 1985 | | myarc_cru_r, |
| 1986 | | myarc_cru_w, |
| 1987 | | NULL, |
| 1988 | | NULL |
| 1989 | | }; |
| 1990 | | |
| 1991 | | static int myarc_cur_page_offset; |
| 1992 | | static int myarc_page_offset_mask; |
| 1993 | | |
| 1994 | | /* set up myarc handlers, and set initial state */ |
| 1995 | | static void ti99_myarcxram_init(running_machine *machine) |
| 1996 | | { |
| 1997 | | address_space *space = cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM); |
| 1998 | | |
| 1999 | | memory_install_read16_handler(space, 0x2000, 0x3fff, 0, 0, ti99_myarcxramlow_r); |
| 2000 | | memory_install_write16_handler(space, 0x2000, 0x3fff, 0, 0, ti99_myarcxramlow_w); |
| 2001 | | memory_install_read16_handler(space, 0xa000, 0xffff, 0, 0, ti99_myarcxramhigh_r); |
| 2002 | | memory_install_write16_handler(space, 0xa000, 0xffff, 0, 0, ti99_myarcxramhigh_w); |
| 2003 | | |
| 2004 | | switch (xRAM_kind) |
| 2005 | | { |
| 2006 | | case RAM_FOUNDATION128: /* 128kb foundation */ |
| 2007 | | case RAM_MYARC128: /* 128kb myarc clone */ |
| 2008 | | myarc_page_offset_mask = 0x0c000; |
| 2009 | | break; |
| 2010 | | case RAM_FOUNDATION512: /* 512kb foundation */ |
| 2011 | | case RAM_MYARC512: /* 512kb myarc clone */ |
| 2012 | | myarc_page_offset_mask = 0x3c000; |
| 2013 | | break; |
| 2014 | | default: |
| 2015 | | break; /* let's just keep GCC's big mouth shut */ |
| 2016 | | } |
| 2017 | | |
| 2018 | | switch (xRAM_kind) |
| 2019 | | { |
| 2020 | | case RAM_FOUNDATION128: /* 128kb foundation */ |
| 2021 | | case RAM_FOUNDATION512: /* 512kb foundation */ |
| 2022 | | ti99_peb_set_card_handlers(expansion_box, 0x1e00, & myarc_expansion_handlers); |
| 2023 | | break; |
| 2024 | | case RAM_MYARC128: /* 128kb myarc clone */ |
| 2025 | | case RAM_MYARC512: /* 512kb myarc clone */ |
| 2026 | | ti99_peb_set_card_handlers(expansion_box, 0x1000, & myarc_expansion_handlers); |
| 2027 | | ti99_peb_set_card_handlers(expansion_box, 0x1900, & myarc_expansion_handlers); |
| 2028 | | break; |
| 2029 | | default: |
| 2030 | | break; /* let's just keep GCC's big mouth shut */ |
| 2031 | | } |
| 2032 | | |
| 2033 | | myarc_cur_page_offset = 0; |
| 2034 | | } |
| 2035 | | |
| 2036 | | /* read CRU bit: |
| 2037 | | bit 1-2 (128kb) or 1-4 (512kb): read current map offset */ |
| 2038 | | static int myarc_cru_r(running_machine *machine, int offset) |
| 2039 | | { |
| 2040 | | /*if (offset == 0)*/ /* right??? */ |
| 2041 | | { |
| 2042 | | return (myarc_cur_page_offset >> 14); |
| 2043 | | } |
| 2044 | | } |
| 2045 | | |
| 2046 | | /* write CRU bit: |
| 2047 | | bit 1-2 (128kb) or 1-4 (512kb): write map offset */ |
| 2048 | | static void myarc_cru_w(running_machine *machine, int offset, int data) |
| 2049 | | { |
| 2050 | | offset &= 0x7; /* right??? */ |
| 2051 | | if (offset >= 1) |
| 2052 | | { |
| 2053 | | int mask = 1 << (offset-1+14); |
| 2054 | | |
| 2055 | | if (data) |
| 2056 | | myarc_cur_page_offset |= mask; |
| 2057 | | else |
| 2058 | | myarc_cur_page_offset &= ~mask; |
| 2059 | | |
| 2060 | | myarc_cur_page_offset &= myarc_page_offset_mask; |
| 2061 | | } |
| 2062 | | } |
| 2063 | | |
| 2064 | | /* low 8 kb: 0x2000-0x3fff */ |
| 2065 | | static READ16_HANDLER ( ti99_myarcxramlow_r ) |
| 2066 | | { |
| 2067 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 2068 | | return xRAM_ptr[myarc_cur_page_offset + offset]; |
| 2069 | | } |
| 2070 | | |
| 2071 | | static WRITE16_HANDLER ( ti99_myarcxramlow_w ) |
| 2072 | | { |
| 2073 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 2074 | | COMBINE_DATA(xRAM_ptr + myarc_cur_page_offset + offset); |
| 2075 | | } |
| 2076 | | |
| 2077 | | /* high 24 kb: 0xa000-0xffff */ |
| 2078 | | static READ16_HANDLER ( ti99_myarcxramhigh_r ) |
| 2079 | | { |
| 2080 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 2081 | | return xRAM_ptr[myarc_cur_page_offset + offset+0x1000]; |
| 2082 | | } |
| 2083 | | |
| 2084 | | static WRITE16_HANDLER ( ti99_myarcxramhigh_w ) |
| 2085 | | { |
| 2086 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 2087 | | COMBINE_DATA(xRAM_ptr + myarc_cur_page_offset + offset+0x1000); |
| 2088 | | } |
| 2089 | | |
| 2090 | | |
| 2091 | | /************************************************************************** |
| 2092 | | EVPC (Enhanced Video Processor Card) from SNUG |
| 2093 | | based on v9938 (may also be equipped with v9958) |
| 2094 | | Can be used with TI-99/4A as an add-on card; internal VDP must be removed |
| 2095 | | |
| 2096 | | The SGCPU ("TI-99/4P") only runs with EVPC. |
| 2097 | | ***************************************************************************/ |
| 2098 | | |
| 2099 | | #include "emuopts.h" |
| 2100 | | |
| 2101 | | /* prototypes */ |
| 2102 | | static int evpc_cru_r(running_machine *machine, int offset); |
| 2103 | | static void evpc_cru_w(running_machine *machine, int offset, int data); |
| 2104 | | static READ8_HANDLER(evpc_mem_r); |
| 2105 | | static WRITE8_HANDLER(evpc_mem_w); |
| 2106 | | |
| 2107 | | /* pointer to the evpc DSR area */ |
| 2108 | | static UINT8 *ti99_evpc_DSR; |
| 2109 | | |
| 2110 | | static int RAMEN; |
| 2111 | | static int evpc_dsr_page; |
| 2112 | | |
| 2113 | | /* |
| 2114 | | NOVRAM area |
| 2115 | | */ |
| 2116 | | static UINT8 ti99_evpc_novram[256]; |
| 2117 | | |
| 2118 | | static const ti99_peb_card_handlers_t evpc_handlers = |
| 2119 | | { |
| 2120 | | evpc_cru_r, |
| 2121 | | evpc_cru_w, |
| 2122 | | evpc_mem_r, |
| 2123 | | evpc_mem_w |
| 2124 | | }; |
| 2125 | | |
| 2126 | | static struct |
| 2127 | | { |
| 2128 | | UINT8 read_index, write_index, mask; |
| 2129 | | int read; |
| 2130 | | int state; |
| 2131 | | struct { UINT8 red, green, blue; } color[0x100]; |
| 2132 | | //int dirty; |
| 2133 | | } evpc_palette; |
| 2134 | | |
| 2135 | | /* |
| 2136 | | NOVRAM handling |
| 2137 | | */ |
| 2138 | | static void ti99_evpc_load_novram(running_machine *machine, const char *filename) |
| 2139 | | { |
| 2140 | | file_error filerr; |
| 2141 | | mame_file *file; |
| 2142 | | |
| 2143 | | filerr = mame_fopen(SEARCHPATH_NVRAM, filename, OPEN_FLAG_READ, &file); |
| 2144 | | if (filerr == FILERR_NONE) |
| 2145 | | { |
| 2146 | | if (mame_fread(file, ti99_evpc_novram, 256) != 256) |
| 2147 | | logerror("ti99_4ev: NOVRAM load error\n"); |
| 2148 | | mame_fclose(file); |
| 2149 | | } |
| 2150 | | } |
| 2151 | | |
| 2152 | | static void ti99_evpc_save_novram(running_machine *machine, const char *filename) |
| 2153 | | { |
| 2154 | | file_error filerr; |
| 2155 | | mame_file *file; |
| 2156 | | |
| 2157 | | filerr = mame_fopen(SEARCHPATH_NVRAM, filename, |
| 2158 | | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &file); |
| 2159 | | |
| 2160 | | if (filerr == FILERR_NONE) |
| 2161 | | { |
| 2162 | | if (mame_fwrite(file, ti99_evpc_novram, 256) != 256) |
| 2163 | | logerror("ti99_4ev: NOVRAM save error\n"); |
| 2164 | | mame_fclose(file); |
| 2165 | | } |
| 2166 | | } |
| 2167 | | |
| 2168 | | INPUT_CHANGED( evpc_changed ) |
| 2169 | | { |
| 2170 | | evpc_switch[(UINT64)param&0x03] = newval; |
| 2171 | | } |
| 2172 | | |
| 2173 | | INTERRUPT_GEN( ti99_4ev_hblank_interrupt ) |
| 2174 | | { |
| 2175 | | // static int line_count; |
| 2176 | | v9938_interrupt(device->machine, 0); |
| 2177 | | |
| 2178 | | // No Mechatronic Mouse with v9938 (has own mouse) |
| 2179 | | /* if (++line_count == 262) |
| 2180 | | { |
| 2181 | | line_count = 0; |
| 2182 | | if (has_mecmouse) |
| 2183 | | mecmouse_poll(device->machine); |
| 2184 | | } */ |
| 2185 | | } |
| 2186 | | |
| 2187 | | // still unused |
| 2188 | | TIMER_DEVICE_CALLBACK( ti99_4ev_scanline_interrupt ) |
| 2189 | | { |
| 2190 | | v9938_interrupt(timer.machine, 0); |
| 2191 | | } |
| 2192 | | |
| 2193 | | /* |
| 2194 | | Video initialization. (also used by SGCPU) |
| 2195 | | */ |
| 2196 | | VIDEO_START( ti99_4ev ) |
| 2197 | | { |
| 2198 | | VIDEO_START_CALL(generic_bitmapped); |
| 2199 | | v9938_init(machine, 0, *machine->primary_screen, machine->generic.tmpbitmap, |
| 2200 | | MODEL_V9938, 0x20000, tms9901_set_int2); /* v38 with 128 kb of video RAM */ |
| 2201 | | } |
| 2202 | | |
| 2203 | | /* |
| 2204 | | V38 VDP read |
| 2205 | | */ |
| 2206 | | READ16_HANDLER ( ti99_rv38_r ) |
| 2207 | | { |
| 2208 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 2209 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 2210 | | |
| 2211 | | if (offset & 1) |
| 2212 | | { /* read VDP status */ |
| 2213 | | return ((int) v9938_0_status_r(space, 0)) << 8; |
| 2214 | | } |
| 2215 | | else |
| 2216 | | { /* read VDP RAM */ |
| 2217 | | return ((int) v9938_0_vram_r(space, 0)) << 8; |
| 2218 | | } |
| 2219 | | } |
| 2220 | | |
| 2221 | | /* |
| 2222 | | V38 vdp write |
| 2223 | | */ |
| 2224 | | WRITE16_HANDLER ( ti99_wv38_w ) |
| 2225 | | { |
| 2226 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 2227 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 2228 | | |
| 2229 | | switch (offset & 3) |
| 2230 | | { |
| 2231 | | case 0: |
| 2232 | | /* write VDP data */ |
| 2233 | | v9938_0_vram_w(space, 0, (data >> 8) & 0xff); |
| 2234 | | break; |
| 2235 | | case 1: |
| 2236 | | /* write VDP address */ |
| 2237 | | v9938_0_command_w(space, 0, (data >> 8) & 0xff); |
| 2238 | | break; |
| 2239 | | case 2: |
| 2240 | | /* write VDP palette */ |
| 2241 | | v9938_0_palette_w(space, 0, (data >> 8) & 0xff); |
| 2242 | | break; |
| 2243 | | case 3: |
| 2244 | | /* write VDP register pointer (indirect access) */ |
| 2245 | | v9938_0_register_w(space, 0, (data >> 8) & 0xff); |
| 2246 | | break; |
| 2247 | | } |
| 2248 | | } |
| 2249 | | |
| 2250 | | /* |
| 2251 | | Reset evpc card, set up handlers |
| 2252 | | */ |
| 2253 | | static void ti99_evpc_reset(running_machine *machine) |
| 2254 | | { |
| 2255 | | ti99_evpc_DSR = memory_region(machine, region_dsr) + offset_evpc_dsr; |
| 2256 | | running_device *box = machine->device("per_exp_box"); |
| 2257 | | |
| 2258 | | RAMEN = 0; |
| 2259 | | evpc_dsr_page = 0; |
| 2260 | | |
| 2261 | | ti99_peb_set_card_handlers(box, 0x1400, & evpc_handlers); |
| 2262 | | } |
| 2263 | | |
| 2264 | | /* |
| 2265 | | Read EVPC CRU interface (dip switches) |
| 2266 | | 0: Video timing (PAL/NTSC) |
| 2267 | | 1: - |
| 2268 | | 2: charset |
| 2269 | | 3: RAM shift |
| 2270 | | 4: - |
| 2271 | | 5: - |
| 2272 | | 6: - |
| 2273 | | 7: DIP or NOVRAM |
| 2274 | | Logic is inverted |
| 2275 | | */ |
| 2276 | | static int evpc_cru_r(running_machine *machine, int offset) |
| 2277 | | { |
| 2278 | | UINT8 value = 0; |
| 2279 | | |
| 2280 | | if (offset == 0) |
| 2281 | | value = ~(evpc_switch[0] | (evpc_switch[1]<<2) | (evpc_switch[2]<<3) | (evpc_switch[3]<<7)); |
| 2282 | | |
| 2283 | | return value; |
| 2284 | | } |
| 2285 | | |
| 2286 | | /* |
| 2287 | | Write evpc CRU interface |
| 2288 | | */ |
| 2289 | | static void evpc_cru_w(running_machine *machine, int offset, int data) |
| 2290 | | { |
| 2291 | | switch (offset) |
| 2292 | | { |
| 2293 | | case 0: |
| 2294 | | break; |
| 2295 | | |
| 2296 | | case 1: |
| 2297 | | if (data) |
| 2298 | | evpc_dsr_page |= 1; |
| 2299 | | else |
| 2300 | | evpc_dsr_page &= ~1; |
| 2301 | | break; |
| 2302 | | |
| 2303 | | case 2: |
| 2304 | | break; |
| 2305 | | |
| 2306 | | case 3: |
| 2307 | | RAMEN = data; |
| 2308 | | break; |
| 2309 | | |
| 2310 | | case 4: |
| 2311 | | if (data) |
| 2312 | | evpc_dsr_page |= 4; |
| 2313 | | else |
| 2314 | | evpc_dsr_page &= ~4; |
| 2315 | | break; |
| 2316 | | |
| 2317 | | case 5: |
| 2318 | | if (data) |
| 2319 | | evpc_dsr_page |= 2; |
| 2320 | | else |
| 2321 | | evpc_dsr_page &= ~2; |
| 2322 | | break; |
| 2323 | | |
| 2324 | | case 6: |
| 2325 | | break; |
| 2326 | | |
| 2327 | | case 7: |
| 2328 | | break; |
| 2329 | | } |
| 2330 | | } |
| 2331 | | |
| 2332 | | /* |
| 2333 | | read a byte in evpc DSR space |
| 2334 | | */ |
| 2335 | | static READ8_HANDLER(evpc_mem_r) |
| 2336 | | { |
| 2337 | | UINT8 reply = 0; |
| 2338 | | |
| 2339 | | if (offset < 0x1f00) |
| 2340 | | { |
| 2341 | | reply = ti99_evpc_DSR[offset+evpc_dsr_page*0x2000]; |
| 2342 | | } |
| 2343 | | else if (offset < 0x1ff0) |
| 2344 | | { |
| 2345 | | if (RAMEN) |
| 2346 | | { |
| 2347 | | reply = ti99_evpc_novram[offset-0x1f00]; |
| 2348 | | } |
| 2349 | | else |
| 2350 | | { |
| 2351 | | reply = ti99_evpc_DSR[offset+evpc_dsr_page*0x2000]; |
| 2352 | | } |
| 2353 | | } |
| 2354 | | else |
| 2355 | | { /* PALETTE */ |
| 2356 | | switch (offset - 0x1ff0) |
| 2357 | | { |
| 2358 | | case 0: |
| 2359 | | /* Palette Read Address Register */ |
| 2360 | | reply = evpc_palette.write_index; |
| 2361 | | break; |
| 2362 | | |
| 2363 | | case 2: |
| 2364 | | /* Palette Read Color Value */ |
| 2365 | | if (evpc_palette.read) |
| 2366 | | { |
| 2367 | | switch (evpc_palette.state) |
| 2368 | | { |
| 2369 | | case 0: |
| 2370 | | reply = evpc_palette.color[evpc_palette.read_index].red; |
| 2371 | | break; |
| 2372 | | case 1: |
| 2373 | | reply = evpc_palette.color[evpc_palette.read_index].green; |
| 2374 | | break; |
| 2375 | | case 2: |
| 2376 | | reply = evpc_palette.color[evpc_palette.read_index].blue; |
| 2377 | | break; |
| 2378 | | } |
| 2379 | | evpc_palette.state++; |
| 2380 | | if (evpc_palette.state == 3) |
| 2381 | | { |
| 2382 | | evpc_palette.state = 0; |
| 2383 | | evpc_palette.read_index++; |
| 2384 | | } |
| 2385 | | } |
| 2386 | | break; |
| 2387 | | |
| 2388 | | case 4: |
| 2389 | | /* Palette Read Pixel Mask */ |
| 2390 | | reply = evpc_palette.mask; |
| 2391 | | break; |
| 2392 | | case 6: |
| 2393 | | /* Palette Read Address Register for Color Value */ |
| 2394 | | if (evpc_palette.read) |
| 2395 | | reply = 0; |
| 2396 | | else |
| 2397 | | reply = 3; |
| 2398 | | break; |
| 2399 | | } |
| 2400 | | } |
| 2401 | | |
| 2402 | | return reply; |
| 2403 | | } |
| 2404 | | |
| 2405 | | /* |
| 2406 | | write a byte in evpc DSR space |
| 2407 | | */ |
| 2408 | | static WRITE8_HANDLER(evpc_mem_w) |
| 2409 | | { |
| 2410 | | if ((offset >= 0x1f00) && (offset < 0x1ff0) && RAMEN) |
| 2411 | | { |
| 2412 | | // NOVRAM |
| 2413 | | ti99_evpc_novram[offset-0x1f00] = data; |
| 2414 | | } |
| 2415 | | else if (offset >= 0x1ff0) |
| 2416 | | { /* PALETTE */ |
| 2417 | | logerror("palette write, offset=%d\n, data=%d", offset-0x1ff0, data); |
| 2418 | | switch (offset - 0x1ff0) |
| 2419 | | { |
| 2420 | | case 8: |
| 2421 | | /* Palette Write Address Register */ |
| 2422 | | logerror("EVPC palette address write (for write access)\n"); |
| 2423 | | evpc_palette.write_index = data; |
| 2424 | | evpc_palette.state = 0; |
| 2425 | | evpc_palette.read = 0; |
| 2426 | | break; |
| 2427 | | |
| 2428 | | case 10: |
| 2429 | | /* Palette Write Color Value */ |
| 2430 | | logerror("EVPC palette color write\n"); |
| 2431 | | if (! evpc_palette.read) |
| 2432 | | { |
| 2433 | | switch (evpc_palette.state) |
| 2434 | | { |
| 2435 | | case 0: |
| 2436 | | evpc_palette.color[evpc_palette.write_index].red = data; |
| 2437 | | break; |
| 2438 | | case 1: |
| 2439 | | evpc_palette.color[evpc_palette.write_index].green = data; |
| 2440 | | break; |
| 2441 | | case 2: |
| 2442 | | evpc_palette.color[evpc_palette.write_index].blue = data; |
| 2443 | | break; |
| 2444 | | } |
| 2445 | | evpc_palette.state++; |
| 2446 | | if (evpc_palette.state == 3) |
| 2447 | | { |
| 2448 | | evpc_palette.state = 0; |
| 2449 | | evpc_palette.write_index++; |
| 2450 | | } |
| 2451 | | //evpc_palette.dirty = 1; |
| 2452 | | } |
| 2453 | | break; |
| 2454 | | |
| 2455 | | case 12: |
| 2456 | | /* Palette Write Pixel Mask */ |
| 2457 | | logerror("EVPC palette mask write\n"); |
| 2458 | | evpc_palette.mask = data; |
| 2459 | | break; |
| 2460 | | |
| 2461 | | case 14: |
| 2462 | | /* Palette Write Address Register for Color Value */ |
| 2463 | | logerror("EVPC palette address write (for read access)\n"); |
| 2464 | | evpc_palette.read_index=data; |
| 2465 | | evpc_palette.state=0; |
| 2466 | | evpc_palette.read=1; |
| 2467 | | break; |
| 2468 | | } |
| 2469 | | } |
| 2470 | | } |
| 2471 | | |
| 2472 | | /************************************************************************** |
| 2473 | | TI-99/8 |
| 2474 | | ***************************************************************************/ |
| 2475 | | |
| 2476 | | DRIVER_INIT( ti99_8 ) |
| 2477 | | { |
| 2478 | | UINT8 *mem = memory_region(machine, "maincpu"); |
| 2479 | | ti99_model = model_99_8; |
| 2480 | | has_evpc = FALSE; |
| 2481 | | |
| 2482 | | /* set up memory pointers */ |
| 2483 | | xRAM_ptr_8 = mem + offset_xram_8; |
| 2484 | | ROM0_ptr_8 = mem + offset_rom0_8; |
| 2485 | | ROM1_ptr_8 = ROM0_ptr_8 + 0x2000; |
| 2486 | | ROM2_ptr_8 = ROM1_ptr_8 + 0x2000; |
| 2487 | | ROM3_ptr_8 = ROM2_ptr_8 + 0x2000; |
| 2488 | | sRAM_ptr_8 = mem + offset_sram_8; |
| 2489 | | |
| 2490 | | /* Pull this one up here. */ |
| 2491 | | ti99_8_enable_rom_and_ports = 1; |
| 2492 | | |
| 2493 | | console_GROMs.data_ptr = memory_region(machine, region_grom); |
| 2494 | | create_grom_high2k(0x0000, 0x5fff); |
| 2495 | | |
| 2496 | | multicart8 = machine->device("ti99_multicart"); |
| 2497 | | assert (multicart8 != NULL); |
| 2498 | | } |
| 2499 | | |
| 2500 | | /* |
| 2501 | | ti99_init_machine(); called before ti99_load_rom... |
| 2502 | | */ |
| 2503 | | MACHINE_RESET( ti99_8 ) |
| 2504 | | { |
| 2505 | | /*console_GROMs.data_ptr = memory_region(machine, region_grom);*/ |
| 2506 | | console_GROMs.addr = 0; |
| 2507 | | console_GROMs.buf = 0; |
| 2508 | | console_GROMs.raddr_LSB = FALSE; |
| 2509 | | console_GROMs.waddr_LSB = FALSE; |
| 2510 | | |
| 2511 | | expansion_box = machine->device("per_exp_box"); |
| 2512 | | |
| 2513 | | ti99_8_enable_rom_and_ports = 1; /* ??? maybe there is a pull-up */ |
| 2514 | | /* MZ: already setting this in driver init, otherwise machine locks up */ |
| 2515 | | |
| 2516 | | TMS9928A_reset(); |
| 2517 | | |
| 2518 | | /* clear keyboard interface state (probably overkill, but can't harm) */ |
| 2519 | | KeyCol = 0; |
| 2520 | | AlphaLockLine = 0; |
| 2521 | | |
| 2522 | | tms9901_set_single_int(machine->device("tms9901"), 12, 0); |
| 2523 | | |
| 2524 | | xRAM_kind = RAM_99_8; |
| 2525 | | has_speech = TRUE; |
| 2526 | | has_ide = input_port_read(machine, "HDCTRL") & HD_IDE; |
| 2527 | | has_rs232 = input_port_read(machine, "SERIAL") & SERIAL_TI; |
| 2528 | | has_handset = FALSE; |
| 2529 | | |
| 2530 | | has_mecmouse = input_port_read(machine, "HCI") & HCI_MECMOUSE; |
| 2531 | | |
| 2532 | | has_hsgpl = input_port_read(machine, "EXTCARD") & (EXT_HSGPL_ON | EXT_HSGPL_FLASH); |
| 2533 | | hsgpl_flash_mode = input_port_read(machine, "EXTCARD") & EXT_HSGPL_FLASH; |
| 2534 | | |
| 2535 | | has_usb_sm = input_port_read(machine, "HDCTRL") & HD_USB; |
| 2536 | | has_pcode = input_port_read(machine, "EXTCARD") & EXT_PCODE; |
| 2537 | | |
| 2538 | | /* Get the GK settings once */ |
| 2539 | | gk_switch[1] = input_port_read(machine, "GKSWITCH1"); |
| 2540 | | gk_switch[2] = input_port_read(machine, "GKSWITCH2"); |
| 2541 | | gk_switch[3] = input_port_read(machine, "GKSWITCH3"); |
| 2542 | | gk_switch[4] = input_port_read(machine, "GKSWITCH4"); |
| 2543 | | gk_switch[5] = input_port_read(machine, "GKSWITCH5"); |
| 2544 | | |
| 2545 | | // Removes all card handlers |
| 2546 | | // ti99_peb_init(); |
| 2547 | | |
| 2548 | | // set up optional expansion hardware |
| 2549 | | |
| 2550 | | ti99_8_internal_dsr_reset(machine); |
| 2551 | | |
| 2552 | | static const spchroms_interface speech_intf = { region_speech_rom }; |
| 2553 | | spchroms_config(machine, &speech_intf); |
| 2554 | | |
| 2555 | | switch (input_port_read(machine, "DISKCTRL")) |
| 2556 | | { |
| 2557 | | case DISK_TIFDC: |
| 2558 | | ti99_fdc_reset(machine); |
| 2559 | | break; |
| 2560 | | #if HAS_99CCFDC |
| 2561 | | case DISK_CC: |
| 2562 | | ti99_ccfdc_reset(machine); |
| 2563 | | break; |
| 2564 | | #endif |
| 2565 | | case DISK_BWG: |
| 2566 | | ti99_bwg_reset(machine); |
| 2567 | | break; |
| 2568 | | case DISK_HFDC: |
| 2569 | | ti99_hfdc_reset(machine); |
| 2570 | | break; |
| 2571 | | case DISK_NONE: |
| 2572 | | break; |
| 2573 | | } |
| 2574 | | |
| 2575 | | if (has_ide) |
| 2576 | | ti99_ide_reset(machine, TRUE); |
| 2577 | | |
| 2578 | | if (has_hsgpl) |
| 2579 | | ti99_hsgpl_reset(machine); |
| 2580 | | else |
| 2581 | | hsgpl_crdena = 0; |
| 2582 | | |
| 2583 | | if (has_usb_sm) |
| 2584 | | ti99_usbsm_reset(machine, TRUE); |
| 2585 | | |
| 2586 | | if (has_pcode) |
| 2587 | | ti99_pcode_reset(machine); |
| 2588 | | |
| 2589 | | /* initialize mechatronics mouse */ |
| 2590 | | mecmouse_sel = 0; |
| 2591 | | mecmouse_read_y = 0; |
| 2592 | | mecmouse_x = 0; |
| 2593 | | mecmouse_y = 0; |
| 2594 | | } |
| 2595 | | |
| 2596 | | /* |
| 2597 | | GROM read for TI-99/8 |
| 2598 | | For comments, see the handler for TI-99/4A |
| 2599 | | */ |
| 2600 | | static READ8_HANDLER ( ti99_grom_r8 ) |
| 2601 | | { |
| 2602 | | UINT8 reply, replycart; |
| 2603 | | |
| 2604 | | cpu_adjust_icount(space->machine->firstcpu,-4); |
| 2605 | | |
| 2606 | | if (offset & 2) |
| 2607 | | { |
| 2608 | | replycart = ti99_cartridge_grom_r(cartslots, offset); |
| 2609 | | console_GROMs.waddr_LSB = FALSE; |
| 2610 | | |
| 2611 | | if (console_GROMs.raddr_LSB) |
| 2612 | | { |
| 2613 | | reply = console_GROMs.addr & 0x00ff; |
| 2614 | | console_GROMs.raddr_LSB = FALSE; |
| 2615 | | } |
| 2616 | | else |
| 2617 | | { |
| 2618 | | reply = (console_GROMs.addr>>8) & 0x00ff; |
| 2619 | | console_GROMs.raddr_LSB = TRUE; |
| 2620 | | } |
| 2621 | | /* GROM address values must be globally unique. */ |
| 2622 | | assert (replycart == reply); |
| 2623 | | } |
| 2624 | | else |
| 2625 | | { |
| 2626 | | reply = GROM_dataread(space->machine, offset); |
| 2627 | | } |
| 2628 | | return reply; |
| 2629 | | } |
| 2630 | | |
| 2631 | | /* |
| 2632 | | GROM write for TI-99/8 |
| 2633 | | For comments, see the handler for TI-99/4A |
| 2634 | | */ |
| 2635 | | static WRITE8_HANDLER ( ti99_grom_w8 ) |
| 2636 | | { |
| 2637 | | cpu_adjust_icount(space->machine->firstcpu,-4/*20+3*/); /* from 4 to 23? */ |
| 2638 | | |
| 2639 | | if (offset & 2) |
| 2640 | | { |
| 2641 | | ti99_cartridge_grom_w(cartslots, offset, data); |
| 2642 | | console_GROMs.raddr_LSB = FALSE; |
| 2643 | | if (console_GROMs.waddr_LSB) |
| 2644 | | { |
| 2645 | | console_GROMs.addr = (console_GROMs.addr & 0xFF00) | (data & 0xFF); |
| 2646 | | |
| 2647 | | GROM_dataread(space->machine, offset&0xfffc); |
| 2648 | | console_GROMs.waddr_LSB = FALSE; |
| 2649 | | } |
| 2650 | | else |
| 2651 | | { |
| 2652 | | console_GROMs.addr = ((data<<8) & 0xFF00) | (console_GROMs.addr & 0xFF); |
| 2653 | | console_GROMs.waddr_LSB = TRUE; |
| 2654 | | } |
| 2655 | | |
| 2656 | | } |
| 2657 | | else |
| 2658 | | { |
| 2659 | | GRAM_datawrite(offset, data); |
| 2660 | | console_GROMs.raddr_LSB = console_GROMs.waddr_LSB = FALSE; |
| 2661 | | } |
| 2662 | | } |
| 2663 | | |
| 2664 | | READ8_HANDLER( ti99_8_r ) |
| 2665 | | { |
| 2666 | | /* Set the page. Page size is 4 KiB; page number are the high 4 bits. */ |
| 2667 | | int page = offset >> 12; |
| 2668 | | UINT32 mapper_reg; |
| 2669 | | UINT8 reply = 0; |
| 2670 | | |
| 2671 | | if (ti99_8_enable_rom_and_ports) |
| 2672 | | { |
| 2673 | | // This is only important for address access 0000-1fff and |
| 2674 | | // 8000-9fff. Outside, the mapper mode is used. |
| 2675 | | if ((offset >= 0x0000) && (offset < 0x2000)) |
| 2676 | | { |
| 2677 | | /* ROM */ |
| 2678 | | reply = ROM0_ptr_8[offset & 0x1fff]; |
| 2679 | | return reply; |
| 2680 | | } |
| 2681 | | else if ((offset >= 0x8000) && (offset < 0xa000)) |
| 2682 | | { |
| 2683 | | /* ti99 scratch pad and memory-mapped registers */ |
| 2684 | | /* 0x8000-0x9fff */ |
| 2685 | | switch ((offset - 0x8000) >> 10) |
| 2686 | | { |
| 2687 | | case 0: |
| 2688 | | /* RAM: >8000 - >83ff */ |
| 2689 | | reply = sRAM_ptr_8[offset & 0x1fff]; |
| 2690 | | break; |
| 2691 | | |
| 2692 | | case 1: |
| 2693 | | // >8400 - >840f: sound (cannot read) |
| 2694 | | // >8410 - >87ff: SRAM |
| 2695 | | if (offset >= 0x8410) |
| 2696 | | reply = sRAM_ptr_8[offset & 0x1fff]; |
| 2697 | | break; |
| 2698 | | |
| 2699 | | case 2: |
| 2700 | | /* VDP read + mapper status */ |
| 2701 | | if (offset < 0x8810) |
| 2702 | | { |
| 2703 | | if (! (offset & 1)) |
| 2704 | | { |
| 2705 | | if (offset & 2) |
| 2706 | | /* read VDP status >8802 */ |
| 2707 | | reply = TMS9928A_register_r(space, 0); |
| 2708 | | else |
| 2709 | | /* read VDP RAM >8800 */ |
| 2710 | | reply = TMS9928A_vram_r(space, 0); |
| 2711 | | } |
| 2712 | | } |
| 2713 | | else |
| 2714 | | { |
| 2715 | | /* Read mapper >8810 */ |
| 2716 | | reply = ti99_8_mapper_status; |
| 2717 | | ti99_8_mapper_status = 0; |
| 2718 | | } |
| 2719 | | break; |
| 2720 | | |
| 2721 | | case 4: |
| 2722 | | /* speech read: >9000 */ |
| 2723 | | if (! (offset & 1)) |
| 2724 | | { |
| 2725 | | cpu_adjust_icount(space->machine->device("maincpu"),-16*4); /* this is just a minimum, it can be more */ |
| 2726 | | reply = tms5220_status_r(space->machine->device("tms5220"), 0); |
| 2727 | | } |
| 2728 | | break; |
| 2729 | | |
| 2730 | | case 6: |
| 2731 | | /* GROM read: >9800 */ |
| 2732 | | if (! (offset & 1)) |
| 2733 | | reply = ti99_grom_r8(space, offset); |
| 2734 | | break; |
| 2735 | | |
| 2736 | | default: |
| 2737 | | logerror("unmapped read offs=%d\n", (int) offset); |
| 2738 | | break; |
| 2739 | | } |
| 2740 | | return reply; |
| 2741 | | } |
| 2742 | | } |
| 2743 | | |
| 2744 | | mapper_reg = ti99_8_mapper_regs[page]; |
| 2745 | | offset = (mapper_reg + (offset & 0x0fff)) & 0x00ffffff; |
| 2746 | | |
| 2747 | | if (offset < 0x010000) { |
| 2748 | | UINT8 *mem = memory_region(space->machine, "maincpu")+ offset_xram_8; |
| 2749 | | /* Read RAM */ |
| 2750 | | reply = mem[offset]; |
| 2751 | | } |
| 2752 | | |
| 2753 | | if (offset >= 0xff0000) |
| 2754 | | { |
| 2755 | | switch ((offset >> 13) & 7) |
| 2756 | | { |
| 2757 | | default: |
| 2758 | | /* should never happen */ |
| 2759 | | case 0: |
| 2760 | | /* ff0000 - ff1fff: unassigned? */ |
| 2761 | | case 1: |
| 2762 | | /* ff2000 - ff3fff: ? */ |
| 2763 | | case 4: |
| 2764 | | /* ff8000 - ff9fff: internal DSR ROM? (normally enabled with a write to CRU >2700) */ |
| 2765 | | case 7: |
| 2766 | | /* ffe000 - ffffff: ? */ |
| 2767 | | logerror("unmapped read page=%d offs=%d\n", (int) page, (int) offset); |
| 2768 | | break; |
| 2769 | | |
| 2770 | | case 2: |
| 2771 | | /* ff4000 - ff5fff: DSR space */ |
| 2772 | | reply = ti99_8_peb_r(expansion_box, offset & 0x1fff); |
| 2773 | | break; |
| 2774 | | |
| 2775 | | case 3: |
| 2776 | | /* ff6000 - ff7fff: cartridge ROM space */ |
| 2777 | | offset &= 0x1fff; |
| 2778 | | |
| 2779 | | // We do not support hsgpl here. Just check the cartridges. |
| 2780 | | reply = ti99_multicart8_r(multicart8, offset); |
| 2781 | | break; |
| 2782 | | |
| 2783 | | case 5: |
| 2784 | | /* ffa000 - ffbfff: >2000 ROM (ROM1) */ |
| 2785 | | reply = ROM1_ptr_8[offset & 0x1fff]; |
| 2786 | | break; |
| 2787 | | |
| 2788 | | case 6: |
| 2789 | | /* ffc000 - ffdfff: >6000 ROM */ |
| 2790 | | reply = ROM3_ptr_8[offset & 0x1fff]; |
| 2791 | | break; |
| 2792 | | } |
| 2793 | | } |
| 2794 | | |
| 2795 | | return reply; |
| 2796 | | } |
| 2797 | | |
| 2798 | | WRITE8_HANDLER ( ti99_8_w ) |
| 2799 | | { |
| 2800 | | int page = offset >> 12; |
| 2801 | | UINT32 mapper_reg; |
| 2802 | | |
| 2803 | | if (ti99_8_enable_rom_and_ports) |
| 2804 | | { |
| 2805 | | /* memory mapped ports enabled */ |
| 2806 | | if ((offset >= 0x0000) && (offset < 0x2000)) |
| 2807 | | { |
| 2808 | | /* ROM? */ |
| 2809 | | return; |
| 2810 | | } |
| 2811 | | else if ((offset >= 0x8000) && (offset < 0xa000)) |
| 2812 | | { |
| 2813 | | /* ti99 scratch pad and memory-mapped registers */ |
| 2814 | | /* 0x8000-0x9fff */ |
| 2815 | | switch ((offset - 0x8000) >> 10) |
| 2816 | | { |
| 2817 | | case 0: |
| 2818 | | /* RAM */ |
| 2819 | | sRAM_ptr_8[offset & 0x1fff] = data; |
| 2820 | | break; |
| 2821 | | |
| 2822 | | case 1: |
| 2823 | | /* sound write + RAM */ |
| 2824 | | if (offset < 0x8410) |
| 2825 | | sn76496_w(space->machine->device("sn76496"), offset, data); |
| 2826 | | else |
| 2827 | | sRAM_ptr_8[offset & 0x1fff] = data; |
| 2828 | | break; |
| 2829 | | |
| 2830 | | case 2: |
| 2831 | | /* VDP read + mapper control */ |
| 2832 | | if (offset >= 0x8810) |
| 2833 | | { |
| 2834 | | int file = (data >> 1) & 3; |
| 2835 | | int i; |
| 2836 | | |
| 2837 | | if (data & 1) |
| 2838 | | { /* load */ |
| 2839 | | for (i=0; i<16; i++) |
| 2840 | | { |
| 2841 | | ti99_8_mapper_regs[i] = (sRAM_ptr_8[(file << 6) + (i << 2)] << 24) |
| 2842 | | | (sRAM_ptr_8[(file << 6) + (i << 2) + 1] << 16) |
| 2843 | | | (sRAM_ptr_8[(file << 6) + (i << 2) + 2] << 8) |
| 2844 | | | sRAM_ptr_8[(file << 6) + (i << 2) + 3]; |
| 2845 | | } |
| 2846 | | } |
| 2847 | | else |
| 2848 | | { /* save */ |
| 2849 | | for (i=0; i<16; i++) |
| 2850 | | { |
| 2851 | | sRAM_ptr_8[(file << 6) + (i << 2)] = ti99_8_mapper_regs[i] >> 24; |
| 2852 | | sRAM_ptr_8[(file << 6) + (i << 2) + 1] = ti99_8_mapper_regs[i] >> 16; |
| 2853 | | sRAM_ptr_8[(file << 6) + (i << 2) + 2] = ti99_8_mapper_regs[i] >> 8; |
| 2854 | | sRAM_ptr_8[(file << 6) + (i << 2) + 3] = ti99_8_mapper_regs[i]; |
| 2855 | | } |
| 2856 | | } |
| 2857 | | } |
| 2858 | | break; |
| 2859 | | |
| 2860 | | case 3: |
| 2861 | | /* VDP write */ |
| 2862 | | if (! (offset & 1)) |
| 2863 | | { |
| 2864 | | if (offset & 2) |
| 2865 | | /* read VDP status */ |
| 2866 | | TMS9928A_register_w(space, 0, data); |
| 2867 | | else |
| 2868 | | /* read VDP RAM */ |
| 2869 | | TMS9928A_vram_w(space, 0, data); |
| 2870 | | } |
| 2871 | | break; |
| 2872 | | |
| 2873 | | case 5: |
| 2874 | | /* speech write */ |
| 2875 | | if (! (offset & 1)) |
| 2876 | | { |
| 2877 | | cpu_adjust_icount(space->machine->device("maincpu"),-48*4); /* this is just an approx. minimum, it can be much more */ |
| 2878 | | |
| 2879 | | // the stupid design of the tms5220 core means that ready is cleared when |
| 2880 | | // there are 15 bytes in FIFO. It should be 16. Of course, if it were the |
| 2881 | | // case, we would need to store the value on the bus, which would be more |
| 2882 | | // complex. |
| 2883 | | if (! tms5220_readyq_r(space->machine->device("tms5220"))) |
| 2884 | | { |
| 2885 | | attotime time_to_ready = double_to_attotime(tms5220_time_to_ready(space->machine->device("tms5220"))); |
| 2886 | | double d = space->machine->device<cpu_device>("maincpu")->attotime_to_cycles(time_to_ready); |
| 2887 | | int cycles_to_ready = ((int) (d + 3)) & ~3; |
| 2888 | | |
| 2889 | | logerror("time to ready: %f -> %d\n", attotime_to_double(time_to_ready) |
| 2890 | | , (int) cycles_to_ready); |
| 2891 | | |
| 2892 | | cpu_adjust_icount(space->machine->device("maincpu"),-cycles_to_ready); |
| 2893 | | timer_set(space->machine, attotime_zero, NULL, 0, /*speech_kludge_callback*/NULL); |
| 2894 | | } |
| 2895 | | |
| 2896 | | tms5220_data_w(space->machine->device("tms5220"), offset, data); |
| 2897 | | } |
| 2898 | | break; |
| 2899 | | |
| 2900 | | case 7: |
| 2901 | | /* GPL write */ |
| 2902 | | if (! (offset & 1)) |
| 2903 | | ti99_grom_w8(space, offset, data); |
| 2904 | | break; |
| 2905 | | |
| 2906 | | default: |
| 2907 | | logerror("unmapped write offs=%d data=%d\n", (int) offset, (int) data); |
| 2908 | | break; |
| 2909 | | } |
| 2910 | | return; |
| 2911 | | } |
| 2912 | | } |
| 2913 | | |
| 2914 | | mapper_reg = ti99_8_mapper_regs[page]; |
| 2915 | | |
| 2916 | | /* 4 KiB page size */ |
| 2917 | | offset = (mapper_reg + (offset & 0x0fff)) & 0x00ffffff; |
| 2918 | | |
| 2919 | | #if 0 |
| 2920 | | /* test write protect */ |
| 2921 | | if (mapper_reg & 0x80000000) |
| 2922 | | ; |
| 2923 | | #endif |
| 2924 | | |
| 2925 | | if (offset < 0x010000) |
| 2926 | | { /* Write RAM */ |
| 2927 | | xRAM_ptr_8[offset] = data; |
| 2928 | | } |
| 2929 | | else if (offset >= 0xff0000) |
| 2930 | | { |
| 2931 | | switch ((offset >> 13) & 7) |
| 2932 | | { |
| 2933 | | default: |
| 2934 | | /* should never happen */ |
| 2935 | | case 0: |
| 2936 | | /* unassigned??? */ |
| 2937 | | case 1: |
| 2938 | | /* ??? */ |
| 2939 | | case 4: |
| 2940 | | /* internal DSR ROM??? (normally enabled with a write to CRU >2700) */ |
| 2941 | | case 7: |
| 2942 | | /* ??? */ |
| 2943 | | break; |
| 2944 | | |
| 2945 | | case 2: |
| 2946 | | /* DSR space */ |
| 2947 | | ti99_8_peb_w(expansion_box, offset & 0x1fff, data); |
| 2948 | | break; |
| 2949 | | |
| 2950 | | case 3: |
| 2951 | | /* cartridge space */ |
| 2952 | | /* We do not support HSGPL for the 99/8 at this time. */ |
| 2953 | | ti99_multicart8_w(multicart8, offset & 0x1fff, data); |
| 2954 | | break; |
| 2955 | | |
| 2956 | | case 5: |
| 2957 | | /* >2000 ROM (ROM1) */ |
| 2958 | | break; |
| 2959 | | |
| 2960 | | case 6: |
| 2961 | | /* >6000 ROM */ |
| 2962 | | break; |
| 2963 | | } |
| 2964 | | } |
| 2965 | | else |
| 2966 | | { |
| 2967 | | logerror("unmapped write page=%d offs=%d\n", (int) page, (int) offset); |
| 2968 | | } |
| 2969 | | } |
| 2970 | | |
| 2971 | | /* |
| 2972 | | WRITE key column select (P0-P3) |
| 2973 | | */ |
| 2974 | | static WRITE8_DEVICE_HANDLER( ti99_8_KeyC ) |
| 2975 | | { |
| 2976 | | if (data) |
| 2977 | | KeyCol |= 1 << offset; |
| 2978 | | else |
| 2979 | | KeyCol &= ~ (1 << offset); |
| 2980 | | |
| 2981 | | if (has_mecmouse) |
| 2982 | | mecmouse_select(KeyCol, 14, 15); |
| 2983 | | } |
| 2984 | | |
| 2985 | | static WRITE8_DEVICE_HANDLER( ti99_8_WCRUS ) |
| 2986 | | { |
| 2987 | | ti99_8_enable_rom_and_ports = data; |
| 2988 | | } |
| 2989 | | |
| 2990 | | static WRITE8_DEVICE_HANDLER( ti99_8_PTGEN ) |
| 2991 | | { |
| 2992 | | /* ... */ |
| 2993 | | } |
| 2994 | | |
| 2995 | | /* |
| 2996 | | TI 99/8 internal DSR support. |
| 2997 | | Includes a few specific signals, and an extra ROM. |
| 2998 | | */ |
| 2999 | | |
| 3000 | | /* prototypes */ |
| 3001 | | static void ti99_8_internal_dsr_cru_w(running_machine *machine, int offset, int data); |
| 3002 | | static READ8_HANDLER(ti99_8_internal_dsr_r); |
| 3003 | | |
| 3004 | | |
| 3005 | | static const ti99_peb_card_handlers_t ti99_8_internal_dsr_handlers = |
| 3006 | | { |
| 3007 | | NULL, |
| 3008 | | ti99_8_internal_dsr_cru_w, |
| 3009 | | ti99_8_internal_dsr_r, |
| 3010 | | NULL |
| 3011 | | }; |
| 3012 | | |
| 3013 | | /* pointer to the internal DSR ROM data */ |
| 3014 | | static UINT8 *ti99_8_internal_DSR; |
| 3015 | | |
| 3016 | | |
| 3017 | | /* set up handlers, and set initial state */ |
| 3018 | | static void ti99_8_internal_dsr_reset(running_machine *machine) |
| 3019 | | { |
| 3020 | | ti99_8_internal_DSR = memory_region(machine, "maincpu") + offset_rom0_8 + 0x4000; |
| 3021 | | ti99_peb_set_card_handlers(expansion_box, 0x2700, & ti99_8_internal_dsr_handlers); |
| 3022 | | } |
| 3023 | | |
| 3024 | | /* write CRU bit: |
| 3025 | | bit0: enable/disable internal DSR ROM, |
| 3026 | | bit1: hard reset */ |
| 3027 | | static void ti99_8_internal_dsr_cru_w(running_machine *machine, int offset, int data) |
| 3028 | | { |
| 3029 | | switch (offset) |
| 3030 | | { |
| 3031 | | case 1: |
| 3032 | | /* hard reset -- not emulated */ |
| 3033 | | break; |
| 3034 | | } |
| 3035 | | } |
| 3036 | | |
| 3037 | | /* read internal DSR ROM */ |
| 3038 | | static READ8_HANDLER(ti99_8_internal_dsr_r) |
| 3039 | | { |
| 3040 | | return ti99_8_internal_DSR[offset]; |
| 3041 | | } |
| 3042 | | |
| 3043 | | /* |
| 3044 | | Read pins INT3*-INT7* of TI99's 9901. |
| 3045 | | |
| 3046 | | signification: |
| 3047 | | (bit 1: INT1 status) |
| 3048 | | (bit 2: INT2 status) |
| 3049 | | bits 3-4: unused? |
| 3050 | | bit 5: ??? |
| 3051 | | bit 6-7: keyboard status bits 0 through 1 |
| 3052 | | */ |
| 3053 | | static READ8_DEVICE_HANDLER( ti99_8_R9901_0 ) |
| 3054 | | { |
| 3055 | | int answer; |
| 3056 | | static const char *const keynames[] = { |
| 3057 | | "KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5", "KEY6", "KEY7", |
| 3058 | | "KEY8", "KEY9", "KEY10", "KEY11", "KEY12", "KEY13", "KEY14", "KEY15" |
| 3059 | | }; |
| 3060 | | |
| 3061 | | if (has_mecmouse && (KeyCol == 15)) |
| 3062 | | { |
| 3063 | | int buttons = input_port_read(device->machine, "MOUSE0") & 3; |
| 3064 | | |
| 3065 | | answer = ((mecmouse_read_y ? mecmouse_y_buf : mecmouse_x_buf) << 7) & 0x80; |
| 3066 | | |
| 3067 | | if (! (buttons & 1)) |
| 3068 | | /* action button */ |
| 3069 | | answer |= 0x40; |
| 3070 | | } |
| 3071 | | else |
| 3072 | | { |
| 3073 | | answer = (input_port_read(device->machine, keynames[KeyCol]) << 6) & 0xC0; |
| 3074 | | } |
| 3075 | | |
| 3076 | | return answer; |
| 3077 | | } |
| 3078 | | |
| 3079 | | const tms9901_interface tms9901reset_param_ti99_8 = |
| 3080 | | { |
| 3081 | | TMS9901_INT1 | TMS9901_INT2 | TMS9901_INTC, /* only input pins whose state is always known */ |
| 3082 | | |
| 3083 | | { /* read handlers */ |
| 3084 | | ti99_8_R9901_0, |
| 3085 | | ti99_8_R9901_1, |
| 3086 | | ti99_R9901_2, |
| 3087 | | ti99_R9901_3 |
| 3088 | | }, |
| 3089 | | |
| 3090 | | { /* write handlers */ |
| 3091 | | ti99_8_KeyC, |
| 3092 | | ti99_8_KeyC, |
| 3093 | | ti99_8_KeyC, |
| 3094 | | ti99_8_KeyC, |
| 3095 | | ti99_8_WCRUS, |
| 3096 | | ti99_8_PTGEN, |
| 3097 | | ti99_CS_motor, |
| 3098 | | NULL, |
| 3099 | | ti99_audio_gate, |
| 3100 | | ti99_CS_output, |
| 3101 | | NULL, |
| 3102 | | NULL, |
| 3103 | | NULL, |
| 3104 | | NULL, |
| 3105 | | NULL, |
| 3106 | | NULL |
| 3107 | | }, |
| 3108 | | |
| 3109 | | /* interrupt handler */ |
| 3110 | | tms9901_interrupt_callback, |
| 3111 | | |
| 3112 | | /* clock rate = 3.58MHz??? 2.68MHz??? */ |
| 3113 | | /*3579545.*/2684658.75 |
| 3114 | | }; |
| 3115 | | |
| 3116 | | |
| 3117 | | /************************************************************************** |
| 3118 | | SGCPU (aka "TI-99/4P") |
| 3119 | | Requires EVPC and HSGPL (with content) |
| 3120 | | ***************************************************************************/ |
| 3121 | | |
| 3122 | | /* True if SGCPU rom6 is enabled */ |
| 3123 | | static int ti99_4p_internal_rom6_enable; |
| 3124 | | |
| 3125 | | /* Pointer to the memory area. */ |
| 3126 | | static UINT16 *ti99_4p_mem; |
| 3127 | | |
| 3128 | | /* pointer to the internal DSR ROM data */ |
| 3129 | | static UINT16 *ti99_4p_internal_DSR; |
| 3130 | | |
| 3131 | | /* Offset to the ROM6 bank. */ |
| 3132 | | static offs_t rom6_bank_offset; |
| 3133 | | |
| 3134 | | static void ti99_4p_mapper_cru_w(running_machine *machine, int offset, int data); |
| 3135 | | static READ16_HANDLER(ti99_4p_mapper_r); |
| 3136 | | static WRITE16_HANDLER(ti99_4p_mapper_w); |
| 3137 | | static void ti99_4p_internal_dsr_cru_w(running_machine *machine, int offset, int data); |
| 3138 | | static READ16_HANDLER(ti99_4p_internal_dsr_r); |
| 3139 | | |
| 3140 | | static const ti99_peb_16bit_card_handlers_t ti99_4p_internal_dsr_handlers = |
| 3141 | | { |
| 3142 | | NULL, |
| 3143 | | ti99_4p_internal_dsr_cru_w, |
| 3144 | | ti99_4p_internal_dsr_r, |
| 3145 | | NULL |
| 3146 | | }; |
| 3147 | | |
| 3148 | | static const ti99_peb_16bit_card_handlers_t ti99_4p_mapper_handlers = |
| 3149 | | { |
| 3150 | | NULL, |
| 3151 | | ti99_4p_mapper_cru_w, |
| 3152 | | ti99_4p_mapper_r, |
| 3153 | | ti99_4p_mapper_w |
| 3154 | | }; |
| 3155 | | |
| 3156 | | static int ti99_4p_mapper_on; |
| 3157 | | static int ti99_4p_mapper_lookup[16]; |
| 3158 | | |
| 3159 | | DRIVER_INIT( ti99_4p ) |
| 3160 | | { |
| 3161 | | ti99_model = model_99_4p; |
| 3162 | | has_evpc = TRUE; |
| 3163 | | |
| 3164 | | /* set up memory pointers */ |
| 3165 | | xRAM_ptr = (UINT16 *) (memory_region(machine, "maincpu") + offset_xram_4p); |
| 3166 | | } |
| 3167 | | |
| 3168 | | MACHINE_START( ti99_4p ) |
| 3169 | | { |
| 3170 | | UINT8* mem = memory_region(machine, "maincpu"); |
| 3171 | | |
| 3172 | | // Set up system ROM pointers. Must be set up here |
| 3173 | | // so that the system can successfully reset (otherwise the reset |
| 3174 | | // vector would be missing in 0x0000). |
| 3175 | | memory_set_bankptr(machine, "bank1", mem + offset_rom0_4p); // system ROM |
| 3176 | | |
| 3177 | | ti99_floppy_controllers_init_all(machine); |
| 3178 | | ti99_ide_init(machine); |
| 3179 | | ti99_hsgpl_init(machine); |
| 3180 | | ti99_usbsm_init(machine); |
| 3181 | | } |
| 3182 | | |
| 3183 | | /* |
| 3184 | | Reset the machine. |
| 3185 | | */ |
| 3186 | | MACHINE_RESET( ti99_4p ) |
| 3187 | | { |
| 3188 | | address_space *space = cputag_get_address_space(machine, "maincpu", ADDRESS_SPACE_PROGRAM); |
| 3189 | | |
| 3190 | | UINT8* mem = memory_region(machine, "maincpu"); |
| 3191 | | |
| 3192 | | expansion_box = machine->device("per_exp_box"); |
| 3193 | | |
| 3194 | | /* set up system ROM and scratch pad pointers */ |
| 3195 | | memory_set_bankptr(machine, "bank1", mem + offset_rom0_4p); /* system ROM */ |
| 3196 | | memory_set_bankptr(machine, "bank2", mem + offset_sram_4p); /* scratch pad */ |
| 3197 | | memory_set_bankptr(machine, "bank11", mem + offset_dram_4p); /* extra RAM for debugger */ |
| 3198 | | |
| 3199 | | v9938_reset(0); |
| 3200 | | |
| 3201 | | /* clear keyboard interface state (probably overkill, but can't harm) */ |
| 3202 | | KeyCol = 0; |
| 3203 | | AlphaLockLine = 0; |
| 3204 | | |
| 3205 | | tms9901_set_single_int(machine->device("tms9901"), 12, 0); |
| 3206 | | |
| 3207 | | xRAM_kind = RAM_99_4P; |
| 3208 | | has_speech = input_port_read(machine, "SPEECH"); |
| 3209 | | has_ide = input_port_read(machine, "HDCTRL") & HD_IDE; |
| 3210 | | has_rs232 = input_port_read(machine, "SERIAL") & SERIAL_TI; |
| 3211 | | |
| 3212 | | has_handset = FALSE; |
| 3213 | | has_mecmouse = FALSE; // mouse is connected to v9938 |
| 3214 | | has_hsgpl = TRUE; |
| 3215 | | has_usb_sm = input_port_read(machine, "HDCTRL") & HD_USB; |
| 3216 | | has_pcode = input_port_read(machine, "EXTCARD") & EXT_PCODE; |
| 3217 | | |
| 3218 | | // set up optional expansion hardware |
| 3219 | | ti99_4p_internal_dsr_reset(machine); |
| 3220 | | ti99_4p_mapper_init(machine); |
| 3221 | | ti99_hsgpl_reset(machine); |
| 3222 | | ti99_evpc_reset(machine); |
| 3223 | | |
| 3224 | | if (has_speech) |
| 3225 | | { |
| 3226 | | static const spchroms_interface speech_intf = { region_speech_rom }; |
| 3227 | | spchroms_config(machine, &speech_intf); |
| 3228 | | memory_install_read16_handler(space, 0x9000, 0x93ff, 0, 0, ti99_rspeech_r); |
| 3229 | | memory_install_write16_handler(space, 0x9400, 0x97ff, 0, 0, ti99_wspeech_w); |
| 3230 | | } |
| 3231 | | else |
| 3232 | | { |
| 3233 | | memory_install_read16_handler(space, 0x9000, 0x93ff, 0, 0, ti99_nop_8_r); |
| 3234 | | memory_install_write16_handler(space, 0x9400, 0x97ff, 0, 0, ti99_nop_8_w); |
| 3235 | | } |
| 3236 | | |
| 3237 | | |
| 3238 | | switch (input_port_read(machine, "DISKCTRL")) |
| 3239 | | { |
| 3240 | | case DISK_TIFDC: |
| 3241 | | ti99_fdc_reset(machine); |
| 3242 | | break; |
| 3243 | | #if HAS_99CCFDC |
| 3244 | | case DISK_CC: |
| 3245 | | ti99_ccfdc_reset(machine); |
| 3246 | | break; |
| 3247 | | #endif |
| 3248 | | case DISK_BWG: |
| 3249 | | ti99_bwg_reset(machine); |
| 3250 | | break; |
| 3251 | | case DISK_HFDC: |
| 3252 | | ti99_hfdc_reset(machine); |
| 3253 | | break; |
| 3254 | | case DISK_NONE: |
| 3255 | | break; |
| 3256 | | } |
| 3257 | | |
| 3258 | | if (has_ide) |
| 3259 | | ti99_ide_reset(machine, FALSE); |
| 3260 | | |
| 3261 | | if (has_usb_sm) |
| 3262 | | ti99_usbsm_reset(machine, FALSE); |
| 3263 | | |
| 3264 | | if (has_pcode) |
| 3265 | | ti99_pcode_reset(machine); |
| 3266 | | } |
| 3267 | | |
| 3268 | | |
| 3269 | | /* |
| 3270 | | SNUG SGCPU (a.k.a. 99/4p) Super AMS clone support. |
| 3271 | | Compatible with Super AMS, but uses a 16-bit bus. |
| 3272 | | |
| 3273 | | Up to 1Mb of SRAM. Straightforward mapper, works with 4kb chunks. |
| 3274 | | */ |
| 3275 | | |
| 3276 | | /* set up handlers, and set initial state */ |
| 3277 | | static void ti99_4p_mapper_init(running_machine *machine) |
| 3278 | | { |
| 3279 | | int i; |
| 3280 | | char bank_name[10]; |
| 3281 | | /* Not required at run-time */ |
| 3282 | | ti99_peb_set_16bit_card_handlers(expansion_box, 0x1e00, & ti99_4p_mapper_handlers); |
| 3283 | | |
| 3284 | | ti99_4p_mapper_on = 0; |
| 3285 | | |
| 3286 | | for (i=0; i<16; i++) |
| 3287 | | { |
| 3288 | | ti99_4p_mapper_lookup[i] = i << 11; |
| 3289 | | |
| 3290 | | /* update bank base */ |
| 3291 | | switch (i) |
| 3292 | | { |
| 3293 | | case 2: |
| 3294 | | case 3: |
| 3295 | | sprintf(bank_name,"bank%d",3+(i-2)); |
| 3296 | | memory_set_bankptr(machine,bank_name, xRAM_ptr + (i<<11)); |
| 3297 | | break; |
| 3298 | | |
| 3299 | | case 10: |
| 3300 | | case 11: |
| 3301 | | case 12: |
| 3302 | | case 13: |
| 3303 | | case 14: |
| 3304 | | case 15: |
| 3305 | | sprintf(bank_name,"bank%d",5+(i-10)); |
| 3306 | | memory_set_bankptr(machine,bank_name, xRAM_ptr + (i<<11)); |
| 3307 | | break; |
| 3308 | | } |
| 3309 | | } |
| 3310 | | } |
| 3311 | | |
| 3312 | | /* |
| 3313 | | Write CRU bit: |
| 3314 | | bit0: enable/disable mapper registers in DSR space, |
| 3315 | | bit1: enable/disable address mapping |
| 3316 | | */ |
| 3317 | | static void ti99_4p_mapper_cru_w(running_machine *machine, int offset, int data) |
| 3318 | | { |
| 3319 | | int i; |
| 3320 | | char bank_name[10]; |
| 3321 | | |
| 3322 | | if (offset == 1) |
| 3323 | | { |
| 3324 | | if (ti99_4p_mapper_on != data) |
| 3325 | | { |
| 3326 | | ti99_4p_mapper_on = data; |
| 3327 | | |
| 3328 | | for (i=0; i<16; i++) |
| 3329 | | { |
| 3330 | | /* update bank base */ |
| 3331 | | switch (i) |
| 3332 | | { |
| 3333 | | case 2: |
| 3334 | | case 3: |
| 3335 | | sprintf(bank_name,"bank%d",3+(i-2)); |
| 3336 | | memory_set_bankptr(machine,bank_name, xRAM_ptr + (ti99_4p_mapper_on ? (ti99_4p_mapper_lookup[i]) : (i<<11))); |
| 3337 | | break; |
| 3338 | | |
| 3339 | | case 10: |
| 3340 | | case 11: |
| 3341 | | case 12: |
| 3342 | | case 13: |
| 3343 | | case 14: |
| 3344 | | case 15: |
| 3345 | | sprintf(bank_name,"bank%d",5+(i-10)); |
| 3346 | | memory_set_bankptr(machine,bank_name, xRAM_ptr + (ti99_4p_mapper_on ? (ti99_4p_mapper_lookup[i]) : (i<<11))); |
| 3347 | | break; |
| 3348 | | } |
| 3349 | | } |
| 3350 | | } |
| 3351 | | } |
| 3352 | | } |
| 3353 | | |
| 3354 | | /* read a mapper register */ |
| 3355 | | static READ16_HANDLER(ti99_4p_mapper_r) |
| 3356 | | { |
| 3357 | | return (ti99_4p_mapper_lookup[offset & 0xf] >> 3); |
| 3358 | | } |
| 3359 | | |
| 3360 | | /* write a mapper register */ |
| 3361 | | static WRITE16_HANDLER(ti99_4p_mapper_w) |
| 3362 | | { |
| 3363 | | int page = offset & 0xf; |
| 3364 | | char bank_name[10]; |
| 3365 | | ti99_4p_mapper_lookup[page] = (data & 0xff00) << 3; |
| 3366 | | |
| 3367 | | if (ti99_4p_mapper_on) |
| 3368 | | { |
| 3369 | | /* update bank base */ |
| 3370 | | switch (page) |
| 3371 | | { |
| 3372 | | case 2: |
| 3373 | | case 3: |
| 3374 | | sprintf(bank_name,"bank%d",3+(page-2)); |
| 3375 | | memory_set_bankptr(space->machine,bank_name, xRAM_ptr+ti99_4p_mapper_lookup[page]); |
| 3376 | | break; |
| 3377 | | |
| 3378 | | case 10: |
| 3379 | | case 11: |
| 3380 | | case 12: |
| 3381 | | case 13: |
| 3382 | | case 14: |
| 3383 | | case 15: |
| 3384 | | sprintf(bank_name,"bank%d",5+(page-10)); |
| 3385 | | memory_set_bankptr(space->machine,bank_name, xRAM_ptr+ti99_4p_mapper_lookup[page]); |
| 3386 | | break; |
| 3387 | | } |
| 3388 | | } |
| 3389 | | } |
| 3390 | | |
| 3391 | | /* |
| 3392 | | Internal DSR support. |
| 3393 | | Includes a few specific signals, and an extra ROM. |
| 3394 | | */ |
| 3395 | | static void ti99_4p_internal_dsr_reset(running_machine *machine) |
| 3396 | | { |
| 3397 | | // set up handlers, and set initial state |
| 3398 | | UINT8* mem = memory_region(machine, "maincpu"); |
| 3399 | | |
| 3400 | | ti99_4p_internal_DSR = (UINT16 *) (mem + offset_rom4_4p); |
| 3401 | | rom6_bank_offset = offset_rom6_4p; |
| 3402 | | ti99_4p_mem = (UINT16 *)mem; |
| 3403 | | |
| 3404 | | ti99_peb_set_16bit_card_handlers(expansion_box, 0x0f00, &ti99_4p_internal_dsr_handlers); |
| 3405 | | |
| 3406 | | ti99_4p_internal_rom6_enable = 0; |
| 3407 | | ti99_4p_peb_set_senila(expansion_box, 0); |
| 3408 | | ti99_4p_peb_set_senilb(expansion_box, 0); |
| 3409 | | } |
| 3410 | | |
| 3411 | | /* |
| 3412 | | Write CRU bit: |
| 3413 | | bit0: enable/disable internal DSR ROM, |
| 3414 | | bit1: enable/disable internal cartridge ROM |
| 3415 | | bit2: set/clear senila |
| 3416 | | bit3: set/clear senilb |
| 3417 | | */ |
| 3418 | | static void ti99_4p_internal_dsr_cru_w(running_machine *machine, int offset, int data) |
| 3419 | | { |
| 3420 | | switch (offset) |
| 3421 | | { |
| 3422 | | case 1: |
| 3423 | | ti99_4p_internal_rom6_enable = data; |
| 3424 | | break; |
| 3425 | | case 2: |
| 3426 | | ti99_4p_peb_set_senila(expansion_box, data); |
| 3427 | | break; |
| 3428 | | case 3: |
| 3429 | | ti99_4p_peb_set_senilb(expansion_box, data); |
| 3430 | | break; |
| 3431 | | case 4: |
| 3432 | | /* 0: 16-bit (fast) memory timings */ |
| 3433 | | /* 1: 8-bit memory timings */ |
| 3434 | | break; |
| 3435 | | case 5: |
| 3436 | | /* if 0: "KBENA" mode (enable keyboard?) */ |
| 3437 | | /* if 1: "KBINH" mode (inhibit keyboard?) */ |
| 3438 | | break; |
| 3439 | | } |
| 3440 | | } |
| 3441 | | |
| 3442 | | /* |
| 3443 | | Read internal DSR ROM. |
| 3444 | | Addresses 4000-5fff |
| 3445 | | */ |
| 3446 | | static READ16_HANDLER(ti99_4p_internal_dsr_r) |
| 3447 | | { |
| 3448 | | return ti99_4p_internal_DSR[offset]; |
| 3449 | | } |
| 3450 | | |
| 3451 | | /* |
| 3452 | | SGCPU cartridge space: mapped either to internal ROM6 or to HSGPL. |
| 3453 | | Addresses 6000-7fff |
| 3454 | | */ |
| 3455 | | READ16_HANDLER ( ti99_4p_cart_r ) |
| 3456 | | { |
| 3457 | | UINT16 value = 0; |
| 3458 | | if (ti99_4p_internal_rom6_enable) |
| 3459 | | { |
| 3460 | | value = ti99_4p_mem[(rom6_bank_offset>>1) + offset]; |
| 3461 | | } |
| 3462 | | else |
| 3463 | | { |
| 3464 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 3465 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 3466 | | |
| 3467 | | if (hsgpl_crdena) |
| 3468 | | /* hsgpl is enabled */ |
| 3469 | | value = ti99_hsgpl_rom6_r(space, offset, mem_mask); |
| 3470 | | } |
| 3471 | | return value; |
| 3472 | | } |
| 3473 | | |
| 3474 | | WRITE16_HANDLER ( ti99_4p_cart_w ) |
| 3475 | | { |
| 3476 | | if (ti99_4p_internal_rom6_enable) |
| 3477 | | { |
| 3478 | | // Switch ROMs |
| 3479 | | if (offset & 1) |
| 3480 | | rom6_bank_offset = offset_rom6b_4p; |
| 3481 | | else |
| 3482 | | rom6_bank_offset = offset_rom6_4p; |
| 3483 | | } |
| 3484 | | else |
| 3485 | | { |
| 3486 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 3487 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 3488 | | |
| 3489 | | if (hsgpl_crdena) |
| 3490 | | /* hsgpl is enabled */ |
| 3491 | | ti99_hsgpl_rom6_w(space, offset, data, mem_mask); |
| 3492 | | } |
| 3493 | | } |
| 3494 | | |
| 3495 | | /* |
| 3496 | | GROM read for TI-99/4p |
| 3497 | | */ |
| 3498 | | READ16_HANDLER ( ti99_4p_grom_r ) |
| 3499 | | { |
| 3500 | | UINT16 value = 0; |
| 3501 | | // HSGPL is located on 8-bit bus |
| 3502 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 3503 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 3504 | | |
| 3505 | | // When crdena is false, we must still be able to query the address |
| 3506 | | // counter (offset & 1) |
| 3507 | | if (hsgpl_crdena || (offset & 1)) |
| 3508 | | value = ti99_hsgpl_gpl_r(space, offset, mem_mask); |
| 3509 | | |
| 3510 | | return value; |
| 3511 | | } |
| 3512 | | |
| 3513 | | /* |
| 3514 | | GROM write |
| 3515 | | */ |
| 3516 | | WRITE16_HANDLER ( ti99_4p_grom_w ) |
| 3517 | | { |
| 3518 | | // HSGPL is located on 8-bit bus |
| 3519 | | cpu_adjust_icount(space->machine->device("maincpu"),-4); |
| 3520 | | // cpu_spinuntil_time(space->machine->firstcpu, ATTOTIME_IN_USEC(6)); |
| 3521 | | |
| 3522 | | if (hsgpl_crdena || (offset & 1)) |
| 3523 | | ti99_hsgpl_gpl_w(space, offset, data, mem_mask); |
| 3524 | | } |
| 3525 | | |