src/mess/machine/ti99/gromport.c
| r0 | r9485 | |
| 1 | /********************************************************************** |
| 2 | |
| 3 | gromport.c |
| 4 | |
| 5 | *********************************************************************/ |
| 6 | |
| 7 | #include "emu.h" |
| 8 | #include "gromport.h" |
| 9 | #include "devices/cartslot.h" |
| 10 | #include "devices/multcart.h" |
| 11 | #include "grom.h" |
| 12 | |
| 13 | typedef UINT8 (*read8z_device_func) (ATTR_UNUSED device_t *device, ATTR_UNUSED offs_t offset, UINT8 *value); |
| 14 | |
| 15 | /* We set the number of slots to 8, although we may have up to 16. From a |
| 16 | logical point of view we could have 256, but the operating system only checks |
| 17 | the first 16 banks. */ |
| 18 | #define NUMBER_OF_CARTRIDGE_SLOTS 8 |
| 19 | |
| 20 | typedef int assmfct(running_device *); |
| 21 | |
| 22 | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_NONE, ti99_cartridge_pcb_none); |
| 23 | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_STD, ti99_cartridge_pcb_std); |
| 24 | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGED, ti99_cartridge_pcb_paged); |
| 25 | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_MINIMEM, ti99_cartridge_pcb_minimem); |
| 26 | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_SUPER, ti99_cartridge_pcb_super); |
| 27 | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_MBX, ti99_cartridge_pcb_mbx); |
| 28 | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGED379I, ti99_cartridge_pcb_paged379i); |
| 29 | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGEDCRU, ti99_cartridge_pcb_pagedcru); |
| 30 | DECLARE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_GRAMKRACKER, ti99_cartridge_pcb_gramkracker); |
| 31 | |
| 32 | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_NONE, ti99_cartridge_pcb_none); |
| 33 | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_STD, ti99_cartridge_pcb_std); |
| 34 | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGED, ti99_cartridge_pcb_paged); |
| 35 | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_MINIMEM, ti99_cartridge_pcb_minimem); |
| 36 | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_SUPER, ti99_cartridge_pcb_super); |
| 37 | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_MBX, ti99_cartridge_pcb_mbx); |
| 38 | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGED379I, ti99_cartridge_pcb_paged379i); |
| 39 | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_PAGEDCRU, ti99_cartridge_pcb_pagedcru); |
| 40 | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_CARTRIDGE_PCB_GRAMKRACKER, ti99_cartridge_pcb_gramkracker); |
| 41 | |
| 42 | /* Generic TI99 cartridge structure. */ |
| 43 | struct _cartridge_t |
| 44 | { |
| 45 | // PCB device associated to this cartridge. If NULL, the slot is empty. |
| 46 | running_device *pcb; |
| 47 | |
| 48 | // ROM page. |
| 49 | int rom_page; |
| 50 | |
| 51 | // RAM page. |
| 52 | int ram_page; |
| 53 | |
| 54 | // GROM buffer size. |
| 55 | int grom_size; |
| 56 | |
| 57 | // ROM buffer size. All banks have equal sizes. |
| 58 | int rom_size; |
| 59 | |
| 60 | // RAM buffer size. All banks have equal sizes. |
| 61 | int ram_size; |
| 62 | |
| 63 | // GROM buffer. |
| 64 | UINT8 *grom_ptr; |
| 65 | |
| 66 | // ROM buffer. |
| 67 | UINT8 *rom_ptr; |
| 68 | |
| 69 | // ROM buffer for the second bank of paged cartridges. |
| 70 | UINT8 *rom2_ptr; |
| 71 | |
| 72 | // RAM buffer. The persistence service is done by the cartridge system. |
| 73 | // The RAM space is a consecutive space; all banks are in one buffer. |
| 74 | UINT8 *ram_ptr; |
| 75 | |
| 76 | /* GK support. */ |
| 77 | UINT16 grom_address; |
| 78 | int waddr_LSB; |
| 79 | }; |
| 80 | typedef struct _cartridge_t cartridge_t; |
| 81 | |
| 82 | enum |
| 83 | { |
| 84 | TI99CARTINFO_FCT_6000_R = DEVINFO_FCT_DEVICE_SPECIFIC, |
| 85 | TI99CARTINFO_FCT_6000_W, |
| 86 | TI99CARTINFO_FCT_CRU_R, |
| 87 | TI99CARTINFO_FCT_CRU_W, |
| 88 | TI99CART_FCT_ASSM, |
| 89 | TI99CART_FCT_DISASSM |
| 90 | }; |
| 91 | |
| 92 | typedef struct _ti99_multicart_state |
| 93 | { |
| 94 | // Reserves space for all cartridges. This is also used in the legacy |
| 95 | // cartridge system, but only for slot 0. |
| 96 | cartridge_t cartridge[NUMBER_OF_CARTRIDGE_SLOTS]; |
| 97 | |
| 98 | // Determines which slot is currently active. This value is changed when |
| 99 | // there are accesses to other GROM base addresses. |
| 100 | int active_slot; |
| 101 | |
| 102 | // Used in order to enforce a special slot. This value is retrieved |
| 103 | // from the dipswitch setting. A value of -1 means automatic, that is, |
| 104 | // the grom base switch is used. Values 0 .. max refer to the |
| 105 | // respective slot. |
| 106 | int fixed_slot; |
| 107 | |
| 108 | // Holds the highest index of a cartridge being plugged in plus one. |
| 109 | // If we only have one cartridge inserted, we don't want to get a |
| 110 | // selection option, so we just mirror the memory contents. |
| 111 | int next_free_slot; |
| 112 | |
| 113 | /* GK support. */ |
| 114 | int gk_slot; |
| 115 | int gk_guest_slot; |
| 116 | |
| 117 | /* Legacy support. */ |
| 118 | // Counts the number of slots which currently contain legacy format |
| 119 | // cartridge images. |
| 120 | int legacy_slots; |
| 121 | |
| 122 | // Counts the number of slots which currently contain new format |
| 123 | // cartridge images. |
| 124 | int multi_slots; |
| 125 | |
| 126 | /* Used to cache the switch settings. */ |
| 127 | UINT8 gk_switch[8]; |
| 128 | |
| 129 | /* Ready line */ |
| 130 | devcb_resolved_write_line ready; |
| 131 | |
| 132 | } ti99_multicart_state; |
| 133 | |
| 134 | #define AUTO -1 |
| 135 | |
| 136 | /* Access to the pcb. Contained in the token of the pcb instance. */ |
| 137 | struct _ti99_pcb_t |
| 138 | { |
| 139 | /* Read function for this cartridge. */ |
| 140 | read8z_device_func read; |
| 141 | |
| 142 | /* Write function for this cartridge. */ |
| 143 | write8_device_func write; |
| 144 | |
| 145 | /* Read function for this cartridge, CRU. */ |
| 146 | read8z_device_func cruread; |
| 147 | |
| 148 | /* Write function for this cartridge, CRU. */ |
| 149 | write8_device_func cruwrite; |
| 150 | |
| 151 | /* Link up to the cartridge structure which contains this pcb. */ |
| 152 | cartridge_t *cartridge; |
| 153 | |
| 154 | /* Function to assemble this cartridge. */ |
| 155 | assmfct *assemble; |
| 156 | |
| 157 | /* Function to disassemble this cartridge. */ |
| 158 | assmfct *disassemble; |
| 159 | |
| 160 | /* Points to the (max 5) GROM devices. */ |
| 161 | running_device *grom[5]; |
| 162 | }; |
| 163 | typedef struct _ti99_pcb_t ti99_pcb_t; |
| 164 | |
| 165 | INLINE ti99_multicart_state *get_safe_token(running_device *device) |
| 166 | { |
| 167 | assert(device != NULL); |
| 168 | return (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 169 | } |
| 170 | |
| 171 | INLINE const ti99_multicart_config *get_config(running_device *device) |
| 172 | { |
| 173 | assert(device != NULL); |
| 174 | assert(device->type() == TI99_MULTICART); |
| 175 | return (const ti99_multicart_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config(); |
| 176 | } |
| 177 | |
| 178 | INLINE ti99_pcb_t *get_safe_pcb_token(running_device *device) |
| 179 | { |
| 180 | assert(device != NULL); |
| 181 | return (ti99_pcb_t *)downcast<legacy_device_base *>(device)->token(); |
| 182 | } |
| 183 | |
| 184 | static WRITE_LINE_DEVICE_HANDLER( cart_grom_ready ) |
| 185 | { |
| 186 | running_device *cartdev = device->owner()->owner(); |
| 187 | ti99_multicart_state *cartslots = get_safe_token(cartdev); |
| 188 | devcb_call_write_line( &cartslots->ready, state ); |
| 189 | } |
| 190 | |
| 191 | /* |
| 192 | The console writes the values in this array to cache them. |
| 193 | */ |
| 194 | void set_gk_switches(running_device *cartsys, int number, int value) |
| 195 | { |
| 196 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 197 | cartslots->gk_switch[number] = value; |
| 198 | } |
| 199 | |
| 200 | static int get_gk_switch(running_device *cartsys, int number) |
| 201 | { |
| 202 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 203 | return cartslots->gk_switch[number]; |
| 204 | } |
| 205 | |
| 206 | static int in_legacy_mode(running_device *device) |
| 207 | { |
| 208 | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 209 | return ((cartslots->legacy_slots>0) && (cartslots->multi_slots==0)); |
| 210 | } |
| 211 | |
| 212 | /* |
| 213 | Activates a slot in the multi-cartridge extender. |
| 214 | */ |
| 215 | static void cartridge_slot_set(running_device *cartsys, UINT8 slotnumber) |
| 216 | { |
| 217 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 218 | // if (cartslots->active_slot != slotnumber) printf("Setting cartslot to %d\n", slotnumber); |
| 219 | if (cartslots->fixed_slot==AUTO) |
| 220 | cartslots->active_slot = slotnumber; |
| 221 | else |
| 222 | cartslots->active_slot = cartslots->fixed_slot; |
| 223 | } |
| 224 | |
| 225 | static int slot_is_empty(running_device *cartsys, int slotnumber) |
| 226 | { |
| 227 | cartridge_t *cart; |
| 228 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 229 | |
| 230 | cart = &cartslots->cartridge[slotnumber]; |
| 231 | |
| 232 | if ((cart->rom_size==0) && (cart->ram_size==0) && (cart->grom_size==0)) |
| 233 | return TRUE; |
| 234 | return FALSE; |
| 235 | } |
| 236 | |
| 237 | static void clear_slot(running_device *cartsys, int slotnumber) |
| 238 | { |
| 239 | cartridge_t *cart; |
| 240 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 241 | |
| 242 | cart = &cartslots->cartridge[slotnumber]; |
| 243 | |
| 244 | cart->rom_size = cart->ram_size = cart->grom_size = 0; |
| 245 | cart->rom_ptr = NULL; |
| 246 | cart->rom2_ptr = NULL; |
| 247 | cart->ram_ptr = NULL; |
| 248 | } |
| 249 | |
| 250 | /* |
| 251 | Find the index of the cartridge name. We assume the format |
| 252 | <name><number>, i.e. the number is the longest string from the right |
| 253 | which can be interpreted as a number. |
| 254 | */ |
| 255 | static int get_index_from_tagname(running_device *image) |
| 256 | { |
| 257 | const char *tag = image->tag(); |
| 258 | int maxlen = strlen(tag); |
| 259 | int i; |
| 260 | for (i=maxlen-1; i >=0; i--) |
| 261 | if (tag[i] < 48 || tag[i] > 57) break; |
| 262 | |
| 263 | return atoi(tag+i+1); |
| 264 | } |
| 265 | |
| 266 | /* |
| 267 | Common routine to assemble cartridges from resources. |
| 268 | */ |
| 269 | static cartridge_t *assemble_common(running_device *cartslot) |
| 270 | { |
| 271 | /* Pointer to the cartridge structure. */ |
| 272 | cartridge_t *cartridge; |
| 273 | running_device *cartsys = cartslot->owner(); |
| 274 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 275 | running_device *pcb = cartslot->subdevice("pcb"); |
| 276 | ti99_pcb_t *pcb_def; |
| 277 | |
| 278 | void *socketcont; |
| 279 | int reslength; |
| 280 | // int i; |
| 281 | |
| 282 | int slotnumber = get_index_from_tagname(cartslot)-1; |
| 283 | assert(slotnumber>=0 && slotnumber<NUMBER_OF_CARTRIDGE_SLOTS); |
| 284 | |
| 285 | /* There is a cartridge in this slot, check the maximum slot number. */ |
| 286 | if (cartslots->next_free_slot <= slotnumber) |
| 287 | { |
| 288 | cartslots->next_free_slot = slotnumber+1; |
| 289 | } |
| 290 | cartridge = &cartslots->cartridge[slotnumber]; |
| 291 | |
| 292 | // Get the socket which is retrieved by the multicart instance in |
| 293 | // the state of the cartslot instance. |
| 294 | socketcont = cartslot_get_socket(cartslot, "grom_socket"); |
| 295 | reslength = cartslot_get_resource_length(cartslot, "grom_socket"); |
| 296 | if (socketcont != NULL) |
| 297 | { |
| 298 | cartridge->grom_ptr = (UINT8 *)socketcont; |
| 299 | // printf("grom_socket = %lx\n", cartridge->grom_ptr); |
| 300 | cartridge->grom_size = reslength; |
| 301 | } |
| 302 | |
| 303 | socketcont = cartslot_get_socket(cartslot, "rom_socket"); |
| 304 | reslength = cartslot_get_resource_length(cartslot, "rom_socket"); |
| 305 | if (socketcont != NULL) |
| 306 | { |
| 307 | cartridge->rom_ptr = (UINT8 *)socketcont; |
| 308 | cartridge->rom_size = reslength; |
| 309 | // printf("set rom ptr = %lx, romlength = %04x, data = %02x %02x\n", cartridge->rom_ptr, cartridge->rom_size, cartridge->rom_ptr[0], cartridge->rom_ptr[1]); |
| 310 | } |
| 311 | |
| 312 | socketcont = cartslot_get_socket(cartslot, "rom2_socket"); |
| 313 | // reslength = cartslot_get_resource_length(cartslot, "rom2_socket"); /* cannot differ from rom_socket */ |
| 314 | if (socketcont != NULL) |
| 315 | { |
| 316 | cartridge->rom2_ptr = (UINT8 *)socketcont; |
| 317 | } |
| 318 | |
| 319 | socketcont = cartslot_get_socket(cartslot, "ram_socket"); |
| 320 | reslength = cartslot_get_resource_length(cartslot, "ram_socket"); |
| 321 | if (socketcont != NULL) |
| 322 | { |
| 323 | cartridge->ram_ptr = (UINT8 *)socketcont; |
| 324 | cartridge->ram_size = reslength; |
| 325 | } |
| 326 | |
| 327 | // Register the GROMs |
| 328 | pcb_def = (ti99_pcb_t *)downcast<legacy_device_base *>(pcb)->token(); |
| 329 | for (int i=0; i < 5; i++) pcb_def->grom[i]= NULL; |
| 330 | if (cartridge->grom_size > 0) pcb_def->grom[0] = pcb->subdevice("grom_3"); |
| 331 | if (cartridge->grom_size > 0x2000) pcb_def->grom[1] = pcb->subdevice("grom_4"); |
| 332 | if (cartridge->grom_size > 0x4000) pcb_def->grom[2] = pcb->subdevice("grom_5"); |
| 333 | if (cartridge->grom_size > 0x6000) pcb_def->grom[3] = pcb->subdevice("grom_6"); |
| 334 | if (cartridge->grom_size > 0x8000) pcb_def->grom[4] = pcb->subdevice("grom_7"); |
| 335 | |
| 336 | return cartridge; |
| 337 | } |
| 338 | |
| 339 | static void set_pointers(running_device *pcb, int index) |
| 340 | { |
| 341 | running_device *cartsys = pcb->owner()->owner(); |
| 342 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 343 | ti99_pcb_t *pcb_def = (ti99_pcb_t *)downcast<legacy_device_base *>(pcb)->token(); |
| 344 | |
| 345 | pcb_def->read = (read8z_device_func)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CARTINFO_FCT_6000_R); |
| 346 | pcb_def->write = (write8_device_func)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CARTINFO_FCT_6000_W); |
| 347 | pcb_def->cruread = (read8z_device_func)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CARTINFO_FCT_CRU_R); |
| 348 | pcb_def->cruwrite = (write8_device_func)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CARTINFO_FCT_CRU_W); |
| 349 | |
| 350 | pcb_def->assemble = (assmfct *)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CART_FCT_ASSM); |
| 351 | pcb_def->disassemble = (assmfct *)downcast<const legacy_cart_slot_device_config_base *>(&pcb->baseconfig())->get_config_fct(TI99CART_FCT_DISASSM); |
| 352 | |
| 353 | pcb_def->cartridge = &cartslots->cartridge[index]; |
| 354 | pcb_def->cartridge->pcb = pcb; |
| 355 | } |
| 356 | |
| 357 | /***************************************************************************** |
| 358 | Cartridge type: None |
| 359 | This PCB device is just a pseudo device; the legacy mode is handled |
| 360 | by dedicated functions. |
| 361 | ******************************************************************************/ |
| 362 | static DEVICE_START(ti99_pcb_none) |
| 363 | { |
| 364 | /* device is ti99_cartslot:cartridge:pcb */ |
| 365 | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 366 | } |
| 367 | |
| 368 | /***************************************************************************** |
| 369 | Cartridge type: Standard |
| 370 | Most cartridges are built in this type. Every cartridge may contain |
| 371 | GROM between 0 and 40 KiB, and ROM with 8 KiB length, no banking. |
| 372 | ******************************************************************************/ |
| 373 | |
| 374 | static DEVICE_START(ti99_pcb_std) |
| 375 | { |
| 376 | /* device is ti99_cartslot:cartridge:pcb */ |
| 377 | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 378 | } |
| 379 | |
| 380 | /* |
| 381 | Read handler for the CPU address space of the cartridge |
| 382 | Images for this area are found in the rom_sockets. |
| 383 | */ |
| 384 | static READ8Z_DEVICE_HANDLER( read_cart_std ) |
| 385 | { |
| 386 | /* device is pcb, owner is cartslot (cartridge is only a part of the state of cartslot) */ |
| 387 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 388 | cartridge_t *cartridge = pcb->cartridge; |
| 389 | if (cartridge->rom_ptr!=NULL) |
| 390 | { |
| 391 | // For TI-99/8 we should plan for 16K cartridges. However, none was produced. Well, just ignore this thought, |
| 392 | *value = (cartridge->rom_ptr)[offset & 0x1fff]; |
| 393 | // printf("read cartridge rom space (length %04x) %04x = %02x\n", cartridge->rom_size, offset, *value); |
| 394 | } |
| 395 | } |
| 396 | |
| 397 | static WRITE8_DEVICE_HANDLER( write_cart_std ) |
| 398 | { |
| 399 | logerror("Write access to cartridge ROM at address %04x ignored\n", offset); |
| 400 | } |
| 401 | |
| 402 | /* |
| 403 | The standard cartridge assemble routine. We just call the common |
| 404 | function here. |
| 405 | */ |
| 406 | static int assemble_std(running_device *image) |
| 407 | { |
| 408 | assemble_common(image); |
| 409 | return IMAGE_INIT_PASS; |
| 410 | } |
| 411 | |
| 412 | /* |
| 413 | Removes pointers and restores the state before plugging in the |
| 414 | cartridge. |
| 415 | The pointer to the location after the last cartridge is adjusted. |
| 416 | As it seems, we can use the same function for the disassembling of all |
| 417 | cartridge types. |
| 418 | */ |
| 419 | static int disassemble_std(running_device *image) |
| 420 | { |
| 421 | int slotnumber; |
| 422 | int i; |
| 423 | // cartridge_t *cart; |
| 424 | running_device *cartsys = image->owner(); |
| 425 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 426 | |
| 427 | slotnumber = get_index_from_tagname(image)-1; |
| 428 | |
| 429 | /* Search the highest remaining cartridge. */ |
| 430 | cartslots->next_free_slot = 0; |
| 431 | for (i=NUMBER_OF_CARTRIDGE_SLOTS-1; i >= 0; i--) |
| 432 | { |
| 433 | if (i != slotnumber) |
| 434 | { |
| 435 | if (!slot_is_empty(cartsys, i)) |
| 436 | { |
| 437 | cartslots->next_free_slot = i+1; |
| 438 | break; |
| 439 | } |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | clear_slot(cartsys, slotnumber); |
| 444 | |
| 445 | return IMAGE_INIT_PASS; |
| 446 | } |
| 447 | |
| 448 | /***************************************************************************** |
| 449 | Cartridge type: Paged (Extended Basic) |
| 450 | This cartridge consists of GROM memory and 2 pages of standard ROM. |
| 451 | The page is set by writing any value to a location in |
| 452 | the address area, where an even word offset sets the page to 0 and an |
| 453 | odd word offset sets the page to 1 (e.g. 6000 = bank 0, and |
| 454 | 6002 = bank 1). |
| 455 | ******************************************************************************/ |
| 456 | |
| 457 | static DEVICE_START(ti99_pcb_paged) |
| 458 | { |
| 459 | /* device is ti99_cartslot:cartridge:pcb */ |
| 460 | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 461 | } |
| 462 | |
| 463 | /* |
| 464 | Read handler for the CPU address space of the cartridge |
| 465 | Images for this area are found in the rom_sockets. |
| 466 | */ |
| 467 | static READ8Z_DEVICE_HANDLER( read_cart_paged ) |
| 468 | { |
| 469 | /* device is pcb, owner is cartslot */ |
| 470 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 471 | cartridge_t *cartridge = pcb->cartridge; |
| 472 | UINT8 *rombank; |
| 473 | |
| 474 | if (cartridge->rom_page==0) |
| 475 | rombank = (UINT8*)cartridge->rom_ptr; |
| 476 | else |
| 477 | rombank = (UINT8*)cartridge->rom2_ptr; |
| 478 | |
| 479 | if (rombank==NULL) |
| 480 | { |
| 481 | /* TODO: Check for consistency with the GROM memory handling. */ |
| 482 | *value = 0; |
| 483 | } |
| 484 | else |
| 485 | { |
| 486 | *value = rombank[offset & 0x1fff]; |
| 487 | } |
| 488 | } |
| 489 | |
| 490 | /* |
| 491 | Handle paging. Extended Basic switches between ROM banks by |
| 492 | using the value of the LSB of the address where any value |
| 493 | is written to. |
| 494 | */ |
| 495 | static WRITE8_DEVICE_HANDLER( write_cart_paged ) |
| 496 | { |
| 497 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 498 | cartridge_t *cartridge = pcb->cartridge; |
| 499 | cartridge->rom_page = (offset >> 1) & 1; |
| 500 | } |
| 501 | |
| 502 | /* |
| 503 | We require paged modules to have at least those two rom banks. |
| 504 | */ |
| 505 | static int assemble_paged(running_device *image) |
| 506 | { |
| 507 | cartridge_t *cart; |
| 508 | |
| 509 | cart = assemble_common(image); |
| 510 | if (cart->rom_ptr==NULL) |
| 511 | { |
| 512 | logerror("Missing ROM for paged cartridge"); |
| 513 | return IMAGE_INIT_FAIL; |
| 514 | } |
| 515 | if (cart->rom2_ptr==NULL) |
| 516 | { |
| 517 | logerror("Missing second ROM for paged cartridge"); |
| 518 | return IMAGE_INIT_FAIL; |
| 519 | } |
| 520 | |
| 521 | return IMAGE_INIT_PASS; |
| 522 | } |
| 523 | |
| 524 | /***************************************************************************** |
| 525 | Cartridge type: Mini Memory |
| 526 | GROM: 6 KiB (occupies G>6000 to G>7800) |
| 527 | ROM: 4 KiB (romfile is actually 8 K long, second half with zeros, 0x6000-0x6fff) |
| 528 | persistent RAM: 4 KiB (0x7000-0x7fff) |
| 529 | ******************************************************************************/ |
| 530 | |
| 531 | static DEVICE_START(ti99_pcb_minimem) |
| 532 | { |
| 533 | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 534 | } |
| 535 | |
| 536 | static READ8Z_DEVICE_HANDLER( read_cart_minimem ) |
| 537 | { |
| 538 | /* device is pcb, owner is cartslot */ |
| 539 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 540 | cartridge_t *cartridge = pcb->cartridge; |
| 541 | |
| 542 | if ((offset & 0xf000)==0x6000) |
| 543 | { |
| 544 | *value = (cartridge->rom_ptr==NULL)? 0 : (cartridge->rom_ptr)[offset & 0x0fff]; |
| 545 | } |
| 546 | else |
| 547 | { |
| 548 | *value = (cartridge->ram_ptr==NULL)? 0 : (cartridge->ram_ptr)[offset & 0x0fff]; |
| 549 | } |
| 550 | } |
| 551 | |
| 552 | /* |
| 553 | Mini Memory cartridge write operation. RAM is located at 0x7000. |
| 554 | */ |
| 555 | static WRITE8_DEVICE_HANDLER( write_cart_minimem ) |
| 556 | { |
| 557 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 558 | cartridge_t *cartridge = pcb->cartridge; |
| 559 | |
| 560 | if (offset < 0x7000) |
| 561 | { |
| 562 | logerror("Write access to cartridge ROM at address %04x ignored", offset); |
| 563 | } |
| 564 | else |
| 565 | { |
| 566 | if (cartridge->ram_ptr==NULL) |
| 567 | { |
| 568 | logerror("No cartridge RAM at address %04x", offset); |
| 569 | /* TODO: Check for consistency with the GROM memory handling. */ |
| 570 | } |
| 571 | else |
| 572 | { |
| 573 | (cartridge->ram_ptr)[offset-0x7000] = data; |
| 574 | } |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | static int assemble_minimem(running_device *image) |
| 579 | { |
| 580 | cartridge_t *cart; |
| 581 | |
| 582 | cart = assemble_common(image); |
| 583 | if (cart->grom_size==0) |
| 584 | { |
| 585 | logerror("Missing GROM for Mini Memory"); |
| 586 | // should not fail here because there may be variations of |
| 587 | // cartridges which do not use all parts |
| 588 | // return IMAGE_INIT_FAIL; |
| 589 | } |
| 590 | if (cart->rom_size==0) |
| 591 | { |
| 592 | logerror("Missing ROM for Mini Memory"); |
| 593 | // return IMAGE_INIT_FAIL; |
| 594 | } |
| 595 | if (cart->ram_size==0) |
| 596 | { |
| 597 | logerror("Missing RAM for Mini Memory"); |
| 598 | // return IMAGE_INIT_FAIL; |
| 599 | } |
| 600 | |
| 601 | return IMAGE_INIT_PASS; |
| 602 | } |
| 603 | |
| 604 | /***************************************************************************** |
| 605 | Cartridge type: SuperSpace II |
| 606 | SuperSpace cartridge type. SuperSpace is intended as a user-definable |
| 607 | blank cartridge containing buffered RAM |
| 608 | It has an Editor/Assembler GROM which helps the user to load |
| 609 | the user program into the cartridge. If the user program has a suitable |
| 610 | header, the console recognizes the cartridge as runnable, and |
| 611 | assigns a number in the selection screen. |
| 612 | Switching the RAM banks in this cartridge is achieved by setting |
| 613 | CRU bits (the system serial interface). |
| 614 | |
| 615 | GROM: Editor/Assembler GROM |
| 616 | ROM: none |
| 617 | persistent RAM: 32 KiB (0x6000-0x7fff, 4 banks) |
| 618 | Switching the bank is done via CRU write |
| 619 | ******************************************************************************/ |
| 620 | |
| 621 | static DEVICE_START(ti99_pcb_super) |
| 622 | { |
| 623 | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 624 | } |
| 625 | |
| 626 | /* |
| 627 | The CRU read handler. The CRU is a serial interface in the console. |
| 628 | Using the CRU we can switch the banks in the SuperSpace cartridge. |
| 629 | */ |
| 630 | static READ8Z_DEVICE_HANDLER( read_cart_cru ) |
| 631 | { |
| 632 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 633 | cartridge_t *cartridge = pcb->cartridge; |
| 634 | // offset is the bit number. The CRU base address is already divided by 2. |
| 635 | |
| 636 | // ram_page contains the bank number. We have a maximum of |
| 637 | // 4 banks; the Super Space II manual says: |
| 638 | // |
| 639 | // Banks are selected by writing a bit pattern to CRU address >0800: |
| 640 | // |
| 641 | // Bank # Value |
| 642 | // 0 >02 = 0000 0010 |
| 643 | // 1 >08 = 0000 1000 |
| 644 | // 2 >20 = 0010 0000 |
| 645 | // 3 >80 = 1000 0000 |
| 646 | // |
| 647 | // With the bank number (0, 1, 2, or 3) in R0: |
| 648 | // |
| 649 | // BNKSW LI R12,>0800 Set CRU address |
| 650 | // LI R1,2 Load Shift Bit |
| 651 | // SLA R0,1 Align Bank Number |
| 652 | // JEQ BNKS1 Skip shift if Bank 0 |
| 653 | // SLA R1,0 Align Shift Bit |
| 654 | // BNKS1 LDCR R1,0 Switch Banks |
| 655 | // SRL R0,1 Restore Bank Number (optional) |
| 656 | // RT |
| 657 | printf("Superspace: CRU accessed at %04x\n", offset); |
| 658 | if ((offset & 1) == 0 || offset > 7) |
| 659 | *value = 0; |
| 660 | |
| 661 | // CRU addresses are only 1 bit wide. Bytes are transferred from LSB |
| 662 | // to MSB. That is, 8 bit are eight consecutive addresses. */ |
| 663 | *value = (cartridge->ram_page == (offset-1)/2); |
| 664 | } |
| 665 | |
| 666 | static WRITE8_DEVICE_HANDLER( write_cart_cru ) |
| 667 | { |
| 668 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 669 | cartridge_t *cartridge = pcb->cartridge; |
| 670 | // data is bit |
| 671 | // offset is address |
| 672 | printf("Superspace: CRU accessed at %04x\n", offset); |
| 673 | if (offset < 8) |
| 674 | { |
| 675 | if (data != 0) |
| 676 | cartridge->ram_page = (offset-1)/2; |
| 677 | } |
| 678 | } |
| 679 | |
| 680 | /* |
| 681 | SuperSpace RAM read operation. RAM is located at 0x6000 to 0x7fff |
| 682 | as 4 banks of 8 KiB each |
| 683 | */ |
| 684 | static READ8Z_DEVICE_HANDLER( read_cart_super ) |
| 685 | { |
| 686 | /* device is pcb, owner is cartslot */ |
| 687 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 688 | cartridge_t *cartridge = pcb->cartridge; |
| 689 | offs_t boffset; |
| 690 | |
| 691 | if (cartridge->ram_ptr==NULL) |
| 692 | { |
| 693 | *value = 0; |
| 694 | } |
| 695 | else |
| 696 | { |
| 697 | boffset = cartridge->ram_page * 0x2000 + (offset & 0x1fff); |
| 698 | *value = (cartridge->ram_ptr)[boffset]; |
| 699 | } |
| 700 | } |
| 701 | |
| 702 | /* |
| 703 | SuperSpace RAM write operation. RAM is located at 0x6000 to 0x7fff |
| 704 | as 4 banks of 8 KiB each |
| 705 | */ |
| 706 | static WRITE8_DEVICE_HANDLER( write_cart_super ) |
| 707 | { |
| 708 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 709 | cartridge_t *cartridge = pcb->cartridge; |
| 710 | offs_t boffset; |
| 711 | |
| 712 | if (cartridge->ram_ptr==NULL) |
| 713 | { |
| 714 | logerror("No cartridge RAM at address %04x", offset); |
| 715 | } |
| 716 | else |
| 717 | { |
| 718 | boffset = cartridge->ram_page * 0x2000 + (offset & 0x1fff); |
| 719 | (cartridge->ram_ptr)[boffset] = data; |
| 720 | } |
| 721 | } |
| 722 | |
| 723 | static int assemble_super(running_device *image) |
| 724 | { |
| 725 | cartridge_t *cart; |
| 726 | |
| 727 | cart = assemble_common(image); |
| 728 | if (cart->ram_size==0) |
| 729 | { |
| 730 | logerror("Missing RAM for SuperSpace"); |
| 731 | return IMAGE_INIT_FAIL; |
| 732 | } |
| 733 | return IMAGE_INIT_PASS; |
| 734 | } |
| 735 | |
| 736 | /***************************************************************************** |
| 737 | Cartridge type: MBX |
| 738 | GROM: up to 40 KiB |
| 739 | ROM: up to 16 KiB (in up to 2 banks of 8KiB each) |
| 740 | RAM: 1022 B (0x6c00-0x6ffd, overrides ROM in that area) |
| 741 | ROM mapper: 6ffe |
| 742 | |
| 743 | Note that some MBX cartridges assume the presence of the MBX system |
| 744 | (additional console) and will not run without it. The MBX hardware is |
| 745 | not emulated yet. |
| 746 | ******************************************************************************/ |
| 747 | |
| 748 | static DEVICE_START(ti99_pcb_mbx) |
| 749 | { |
| 750 | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 751 | } |
| 752 | |
| 753 | static READ8Z_DEVICE_HANDLER( read_cart_mbx ) |
| 754 | { |
| 755 | /* device is pcb, owner is cartslot */ |
| 756 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 757 | cartridge_t *cartridge = pcb->cartridge; |
| 758 | |
| 759 | if ((offset & 0x1c00)==0x0c00) |
| 760 | { |
| 761 | // This is the RAM area which overrides any ROM. There is no |
| 762 | // known banking behavior for the RAM, so we must assume that |
| 763 | // there is only one bank. |
| 764 | if (cartridge->ram_ptr != NULL) |
| 765 | *value = (cartridge->ram_ptr)[offset & 0x03ff]; |
| 766 | } |
| 767 | else |
| 768 | { |
| 769 | if (cartridge->rom_ptr != NULL) |
| 770 | *value = (cartridge->rom_ptr)[(offset & 0x1fff)+ cartridge->rom_page*0x2000]; |
| 771 | } |
| 772 | } |
| 773 | |
| 774 | /* |
| 775 | MBX write operation. |
| 776 | */ |
| 777 | static WRITE8_DEVICE_HANDLER( write_cart_mbx ) |
| 778 | { |
| 779 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 780 | cartridge_t *cartridge = pcb->cartridge; |
| 781 | |
| 782 | if (offset == 0x6ffe) |
| 783 | { |
| 784 | cartridge->rom_page = data & 1; |
| 785 | return; |
| 786 | } |
| 787 | |
| 788 | if ((offset & 0x1c00)==0x0c00) |
| 789 | { |
| 790 | if (cartridge->ram_ptr == NULL) return; |
| 791 | cartridge->ram_ptr[offset - 0x6c00] = data; |
| 792 | } |
| 793 | } |
| 794 | |
| 795 | |
| 796 | static int assemble_mbx(running_device *image) |
| 797 | { |
| 798 | assemble_common(image); |
| 799 | return IMAGE_INIT_PASS; |
| 800 | } |
| 801 | |
| 802 | /***************************************************************************** |
| 803 | Cartridge type: paged379i |
| 804 | This cartridge consists of one 16 KiB, 32 KiB, 64 KiB, or 128 KiB EEPROM |
| 805 | which is organised in 2, 4, 8, or 16 pages of 8 KiB each. The complete |
| 806 | memory contents must be stored in one dump file. |
| 807 | The pages are selected by writing a value to some memory locations. Due to |
| 808 | using the inverted outputs of the LS379 latch, setting the inputs of the |
| 809 | latch to all 0 selects the highest bank, while setting to all 1 selects the |
| 810 | lowest. There are some cartridges (16 KiB) which are using this scheme, and |
| 811 | there are new hardware developments mainly relying on this scheme. |
| 812 | |
| 813 | Writing to selects page (16K/32K/64K/128K) |
| 814 | >6000 1 / 3 / 7 / 15 |
| 815 | >6002 0 / 2 / 6 / 14 |
| 816 | >6004 1 / 1 / 5 / 13 |
| 817 | >6006 0 / 0 / 4 / 12 |
| 818 | >6008 1 / 3 / 3 / 11 |
| 819 | >600A 0 / 2 / 2 / 10 |
| 820 | >600C 1 / 1 / 1 / 9 |
| 821 | >600E 0 / 0 / 0 / 8 |
| 822 | >6010 1 / 3 / 7 / 7 |
| 823 | >6012 0 / 2 / 6 / 6 |
| 824 | >6014 1 / 1 / 5 / 5 |
| 825 | >6016 0 / 0 / 4 / 4 |
| 826 | >6018 1 / 3 / 3 / 3 |
| 827 | >601A 0 / 2 / 2 / 2 |
| 828 | >601C 1 / 1 / 1 / 1 |
| 829 | >601E 0 / 0 / 0 / 0 |
| 830 | |
| 831 | ******************************************************************************/ |
| 832 | |
| 833 | static DEVICE_START(ti99_pcb_paged379i) |
| 834 | { |
| 835 | /* device is ti99_cartslot:cartridge:pcb */ |
| 836 | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 837 | } |
| 838 | |
| 839 | /* |
| 840 | Read handler for the CPU address space of the cartridge |
| 841 | Images for this area are found in the rom_sockets. |
| 842 | */ |
| 843 | static READ8Z_DEVICE_HANDLER( read_cart_paged379i ) |
| 844 | { |
| 845 | /* device is pcb, owner is cartslot */ |
| 846 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 847 | cartridge_t *cartridge = pcb->cartridge; |
| 848 | |
| 849 | if (cartridge->rom_ptr!=NULL) |
| 850 | { |
| 851 | *value = (cartridge->rom_ptr)[cartridge->rom_page*0x2000 + (offset & 0x1fff)]; |
| 852 | } |
| 853 | } |
| 854 | |
| 855 | /* |
| 856 | Determines which bank to set, depending on the size of the ROM. This is |
| 857 | some magic code that actually represents different PCB versions. |
| 858 | */ |
| 859 | static void set_paged379i_bank(cartridge_t *cartridge, int rompage) |
| 860 | { |
| 861 | int mask = 0; |
| 862 | if (cartridge->rom_size > 16384) |
| 863 | { |
| 864 | if (cartridge->rom_size > 32768) |
| 865 | { |
| 866 | if (cartridge->rom_size > 65536) |
| 867 | mask = 15; |
| 868 | else |
| 869 | mask = 7; |
| 870 | } |
| 871 | else |
| 872 | mask = 3; |
| 873 | } |
| 874 | else |
| 875 | mask = 1; |
| 876 | |
| 877 | cartridge->rom_page = rompage & mask; |
| 878 | } |
| 879 | |
| 880 | /* |
| 881 | Handle paging. We use a LS379 latch for storing the page number. On this |
| 882 | PCB, the inverted output of the latch is used, so the order of pages |
| 883 | is reversed. (No problem as long as the memory dump is kept exactly |
| 884 | in the way it is stored in the EPROM.) |
| 885 | The latch can store a value of 4 bits. We adjust the number of |
| 886 | significant bits by the size of the memory dump (16K, 32K, 64K). |
| 887 | */ |
| 888 | static WRITE8_DEVICE_HANDLER( write_cart_paged379i ) |
| 889 | { |
| 890 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 891 | cartridge_t *cartridge = pcb->cartridge; |
| 892 | |
| 893 | // Bits: 011x xxxx xxxb bbbx |
| 894 | // x = don't care, bbbb = bank |
| 895 | set_paged379i_bank(cartridge, 15 - ((offset>>1) & 15)); |
| 896 | } |
| 897 | |
| 898 | /* |
| 899 | Paged379i modules have one EPROM dump. |
| 900 | */ |
| 901 | static int assemble_paged379i(running_device *image) |
| 902 | { |
| 903 | cartridge_t *cart; |
| 904 | |
| 905 | cart = assemble_common(image); |
| 906 | if (cart->rom_ptr==NULL) |
| 907 | { |
| 908 | logerror("Missing ROM for paged cartridge"); |
| 909 | return IMAGE_INIT_FAIL; |
| 910 | } |
| 911 | set_paged379i_bank(cart, 15); |
| 912 | return IMAGE_INIT_PASS; |
| 913 | } |
| 914 | |
| 915 | /***************************************************************************** |
| 916 | Cartridge type: pagedcru |
| 917 | This cartridge consists of one 16 KiB, 32 KiB, or 64 KiB EEPROM which is |
| 918 | organised in 2, 4, or 8 pages of 8 KiB each. We assume there is only one |
| 919 | dump file of the respective size. |
| 920 | The pages are selected by writing a value to the CRU. This scheme is |
| 921 | similar to the one used for the SuperSpace cartridge, with the exception |
| 922 | that we are using ROM only, and we can have up to 8 pages. |
| 923 | |
| 924 | Bank Value written to CRU>0800 |
| 925 | 0 >0002 = 0000 0000 0000 0010 |
| 926 | 1 >0008 = 0000 0000 0000 1000 |
| 927 | 2 >0020 = 0000 0000 0010 0000 |
| 928 | 3 >0080 = 0000 0000 1000 0000 |
| 929 | 4 >0200 = 0000 0010 0000 0000 |
| 930 | 5 >0800 = 0000 1000 0000 0000 |
| 931 | 6 >2000 = 0010 0000 0000 0000 |
| 932 | 7 >8000 = 1000 0000 0000 0000 |
| 933 | |
| 934 | ******************************************************************************/ |
| 935 | |
| 936 | static DEVICE_START(ti99_pcb_pagedcru) |
| 937 | { |
| 938 | /* device is ti99_cartslot:cartridge:pcb */ |
| 939 | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 940 | } |
| 941 | |
| 942 | /* |
| 943 | Read handler for the CPU address space of the cartridge |
| 944 | Images for this area are found in the rom_sockets. |
| 945 | */ |
| 946 | static READ8Z_DEVICE_HANDLER( read_cart_pagedcru ) |
| 947 | { |
| 948 | /* device is pcb, owner is cartslot */ |
| 949 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 950 | cartridge_t *cartridge = pcb->cartridge; |
| 951 | |
| 952 | if (cartridge->rom_ptr!=NULL) |
| 953 | { |
| 954 | *value = (cartridge->rom_ptr)[cartridge->rom_page*0x2000 + (offset & 0x1fff)]; |
| 955 | } |
| 956 | } |
| 957 | |
| 958 | /* |
| 959 | This type of cartridge does not support writing to the EPROM address |
| 960 | space. |
| 961 | */ |
| 962 | static WRITE8_DEVICE_HANDLER( write_cart_pagedcru ) |
| 963 | { |
| 964 | return; |
| 965 | } |
| 966 | |
| 967 | /* |
| 968 | The CRU read handler. The CRU is a serial interface in the console. |
| 969 | Using the CRU we can switch the banks in the SuperSpace cartridge. |
| 970 | For documentation see the corresponding cru function above. Note we are |
| 971 | using rom_page here. |
| 972 | */ |
| 973 | static READ8Z_DEVICE_HANDLER( read_cart_cru_paged ) |
| 974 | { |
| 975 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 976 | cartridge_t *cartridge = pcb->cartridge; |
| 977 | |
| 978 | int page = cartridge->rom_page; |
| 979 | if ((offset & 0xf800)==0x0800) |
| 980 | { |
| 981 | int bit = (offset & 0x001e)/2; |
| 982 | if (bit != 0) |
| 983 | { |
| 984 | page = page-(bit/2); // 4 page flags per 8 bits |
| 985 | } |
| 986 | *value = 1 << (page*2+1); |
| 987 | } |
| 988 | } |
| 989 | |
| 990 | static WRITE8_DEVICE_HANDLER( write_cart_cru_paged ) |
| 991 | { |
| 992 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 993 | cartridge_t *cartridge = pcb->cartridge; |
| 994 | |
| 995 | if ((offset & 0xf800)==0x0800) |
| 996 | { |
| 997 | int bit = (offset & 0x001e)/2; |
| 998 | if (data != 0 && bit > 0) |
| 999 | { |
| 1000 | cartridge->rom_page = (bit-1)/2; |
| 1001 | } |
| 1002 | } |
| 1003 | } |
| 1004 | |
| 1005 | /* |
| 1006 | Pagedcru modules have one EPROM dump. |
| 1007 | */ |
| 1008 | static int assemble_pagedcru(running_device *image) |
| 1009 | { |
| 1010 | cartridge_t *cart; |
| 1011 | |
| 1012 | cart = assemble_common(image); |
| 1013 | if (cart->rom_ptr==NULL) |
| 1014 | { |
| 1015 | logerror("Missing ROM for pagedcru cartridge"); |
| 1016 | return IMAGE_INIT_FAIL; |
| 1017 | } |
| 1018 | cart->rom_page = 0; |
| 1019 | return IMAGE_INIT_PASS; |
| 1020 | } |
| 1021 | |
| 1022 | /***************************************************************************** |
| 1023 | Cartridge type: gramkracker |
| 1024 | ******************************************************************************/ |
| 1025 | |
| 1026 | static DEVICE_START(ti99_pcb_gramkracker) |
| 1027 | { |
| 1028 | /* device is ti99_cartslot:cartridge:pcb */ |
| 1029 | // printf("DEVICE_START(ti99_pcb_pagedcru), tag of device=%s\n", device->tag()); |
| 1030 | set_pointers(device, get_index_from_tagname(device->owner())-1); |
| 1031 | } |
| 1032 | |
| 1033 | /* |
| 1034 | The GRAM Kracker contains one GROM and several buffered RAM chips |
| 1035 | */ |
| 1036 | static int assemble_gramkracker(running_device *image) |
| 1037 | { |
| 1038 | cartridge_t *cart; |
| 1039 | int id,i; |
| 1040 | |
| 1041 | running_device *cartsys = image->owner(); |
| 1042 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 1043 | |
| 1044 | cart = assemble_common(image); |
| 1045 | |
| 1046 | if (cart->grom_ptr==NULL) |
| 1047 | { |
| 1048 | logerror("Missing loader GROM for GRAM Kracker system"); |
| 1049 | return IMAGE_INIT_FAIL; |
| 1050 | } |
| 1051 | |
| 1052 | if (cart->ram_size < 81920) |
| 1053 | { |
| 1054 | logerror("Missing or insufficient RAM for GRAM Kracker system"); |
| 1055 | return IMAGE_INIT_FAIL; |
| 1056 | } |
| 1057 | |
| 1058 | // Get slot number |
| 1059 | id = get_index_from_tagname(image); |
| 1060 | cartslots->gk_slot = id-1; |
| 1061 | cartslots->gk_guest_slot = -1; |
| 1062 | // The first cartridge that is still plugged in is the "guest" of the |
| 1063 | // GRAM Kracker |
| 1064 | for (i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) |
| 1065 | { |
| 1066 | if (!slot_is_empty(cartsys, i) && (i != cartslots->gk_slot)) |
| 1067 | { |
| 1068 | cartslots->gk_guest_slot = i; |
| 1069 | break; |
| 1070 | } |
| 1071 | } |
| 1072 | return IMAGE_INIT_PASS; |
| 1073 | } |
| 1074 | |
| 1075 | /* |
| 1076 | Unplugs the GRAM Kracker. The "guest" is still plugged in, but will be |
| 1077 | treated as normal. |
| 1078 | */ |
| 1079 | static int disassemble_gramkracker(running_device *cartslot) |
| 1080 | { |
| 1081 | running_device *cartsys = cartslot->owner(); |
| 1082 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 1083 | |
| 1084 | int slotnumber = get_index_from_tagname(cartslot)-1; |
| 1085 | assert(slotnumber>=0 && slotnumber<NUMBER_OF_CARTRIDGE_SLOTS); |
| 1086 | |
| 1087 | cartslots->gk_slot = -1; |
| 1088 | cartslots->gk_guest_slot = -1; |
| 1089 | |
| 1090 | return disassemble_std(cartslot); |
| 1091 | } |
| 1092 | |
| 1093 | |
| 1094 | /***************************************************************************** |
| 1095 | Device metadata |
| 1096 | ******************************************************************************/ |
| 1097 | |
| 1098 | /* |
| 1099 | Get the pointer to the GROM data from the cartridge. Called by the GROM. |
| 1100 | */ |
| 1101 | static UINT8 *get_grom_ptr(running_device *device) |
| 1102 | { |
| 1103 | ti99_pcb_t *pcb = get_safe_pcb_token(device); |
| 1104 | return pcb->cartridge->grom_ptr; |
| 1105 | } |
| 1106 | |
| 1107 | static MACHINE_CONFIG_FRAGMENT(ti99_cart_common) |
| 1108 | MDRV_GROM_ADD_P( "grom_3", 3, get_grom_ptr, 0x0000, 0x1800, cart_grom_ready ) |
| 1109 | MDRV_GROM_ADD_P( "grom_4", 4, get_grom_ptr, 0x2000, 0x1800, cart_grom_ready ) |
| 1110 | MDRV_GROM_ADD_P( "grom_5", 5, get_grom_ptr, 0x4000, 0x1800, cart_grom_ready ) |
| 1111 | MDRV_GROM_ADD_P( "grom_6", 6, get_grom_ptr, 0x6000, 0x1800, cart_grom_ready ) |
| 1112 | MDRV_GROM_ADD_P( "grom_7", 7, get_grom_ptr, 0x8000, 0x1800, cart_grom_ready ) |
| 1113 | MACHINE_CONFIG_END |
| 1114 | |
| 1115 | static DEVICE_GET_INFO(ti99_cart_common) |
| 1116 | { |
| 1117 | switch(state) |
| 1118 | { |
| 1119 | /* --- the following bits of info are returned as 64-bit signed integers --- */ |
| 1120 | case DEVINFO_INT_TOKEN_BYTES: |
| 1121 | info->i = sizeof(ti99_pcb_t); |
| 1122 | break; |
| 1123 | case DEVINFO_INT_INLINE_CONFIG_BYTES: |
| 1124 | info->i = 0; |
| 1125 | break; |
| 1126 | |
| 1127 | case DEVINFO_PTR_MACHINE_CONFIG: |
| 1128 | info->machine_config = MACHINE_CONFIG_NAME(ti99_cart_common); |
| 1129 | break; |
| 1130 | |
| 1131 | /* --- the following bits of info are returned as pointers to functions --- */ |
| 1132 | case DEVINFO_FCT_START: |
| 1133 | info->start = DEVICE_START_NAME(ti99_pcb_std); |
| 1134 | break; |
| 1135 | case DEVINFO_FCT_STOP: |
| 1136 | /* Nothing */ |
| 1137 | break; |
| 1138 | case DEVINFO_FCT_RESET: |
| 1139 | /* Nothing */ |
| 1140 | break; |
| 1141 | case TI99CARTINFO_FCT_6000_R: |
| 1142 | info->f = (genf *) read_cart_std; break; |
| 1143 | |
| 1144 | case TI99CARTINFO_FCT_6000_W: |
| 1145 | info->f = (genf *) write_cart_std; break; |
| 1146 | |
| 1147 | case TI99CARTINFO_FCT_CRU_R: |
| 1148 | /* Nothing */ |
| 1149 | break; |
| 1150 | |
| 1151 | case TI99CARTINFO_FCT_CRU_W: |
| 1152 | /* Nothing */ |
| 1153 | break; |
| 1154 | |
| 1155 | case TI99CART_FCT_ASSM: |
| 1156 | info->f = (genf *) assemble_std; |
| 1157 | break; |
| 1158 | |
| 1159 | case TI99CART_FCT_DISASSM: |
| 1160 | info->f = (genf *) disassemble_std; |
| 1161 | break; |
| 1162 | |
| 1163 | /* --- the following bits of info are returned as NULL-terminated strings --- */ |
| 1164 | case DEVINFO_STR_NAME: |
| 1165 | strcpy(info->s, "TI99 standard cartridge pcb"); |
| 1166 | break; |
| 1167 | case DEVINFO_STR_FAMILY: |
| 1168 | strcpy(info->s, "TI99 cartridge pcb"); |
| 1169 | break; |
| 1170 | case DEVINFO_STR_VERSION: |
| 1171 | strcpy(info->s, "1.0"); |
| 1172 | break; |
| 1173 | case DEVINFO_STR_SOURCE_FILE: |
| 1174 | strcpy(info->s, __FILE__); |
| 1175 | break; |
| 1176 | case DEVINFO_STR_CREDITS: |
| 1177 | /* Nothing */ |
| 1178 | break; |
| 1179 | } |
| 1180 | } |
| 1181 | |
| 1182 | DEVICE_GET_INFO(ti99_cartridge_pcb_none) |
| 1183 | { |
| 1184 | switch(state) |
| 1185 | { |
| 1186 | case DEVINFO_FCT_START: |
| 1187 | info->start = DEVICE_START_NAME(ti99_pcb_none); |
| 1188 | break; |
| 1189 | |
| 1190 | case DEVINFO_STR_NAME: |
| 1191 | strcpy(info->s, "TI99 empty cartridge"); |
| 1192 | break; |
| 1193 | |
| 1194 | // Don't create GROMs |
| 1195 | case DEVINFO_PTR_MACHINE_CONFIG: |
| 1196 | break; |
| 1197 | |
| 1198 | default: |
| 1199 | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1200 | break; |
| 1201 | } |
| 1202 | } |
| 1203 | |
| 1204 | DEVICE_GET_INFO(ti99_cartridge_pcb_std) |
| 1205 | { |
| 1206 | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1207 | } |
| 1208 | |
| 1209 | DEVICE_GET_INFO(ti99_cartridge_pcb_paged) |
| 1210 | { |
| 1211 | switch(state) |
| 1212 | { |
| 1213 | case DEVINFO_FCT_START: |
| 1214 | info->start = DEVICE_START_NAME(ti99_pcb_paged); |
| 1215 | break; |
| 1216 | |
| 1217 | case TI99CART_FCT_ASSM: |
| 1218 | info->f = (genf *) assemble_paged; |
| 1219 | break; |
| 1220 | |
| 1221 | case TI99CARTINFO_FCT_6000_R: |
| 1222 | info->f = (genf *) read_cart_paged; |
| 1223 | break; |
| 1224 | |
| 1225 | case TI99CARTINFO_FCT_6000_W: |
| 1226 | info->f = (genf *) write_cart_paged; |
| 1227 | break; |
| 1228 | |
| 1229 | case DEVINFO_STR_NAME: |
| 1230 | strcpy(info->s, "TI99 paged cartridge pcb"); |
| 1231 | break; |
| 1232 | |
| 1233 | default: |
| 1234 | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1235 | break; |
| 1236 | } |
| 1237 | } |
| 1238 | |
| 1239 | DEVICE_GET_INFO(ti99_cartridge_pcb_minimem) |
| 1240 | { |
| 1241 | switch(state) |
| 1242 | { |
| 1243 | case DEVINFO_FCT_START: |
| 1244 | info->start = DEVICE_START_NAME(ti99_pcb_minimem); |
| 1245 | break; |
| 1246 | |
| 1247 | case TI99CART_FCT_ASSM: |
| 1248 | info->f = (genf *) assemble_minimem; |
| 1249 | break; |
| 1250 | case TI99CARTINFO_FCT_6000_R: |
| 1251 | info->f = (genf *) read_cart_minimem; |
| 1252 | break; |
| 1253 | case TI99CARTINFO_FCT_6000_W: |
| 1254 | info->f = (genf *) write_cart_minimem; |
| 1255 | break; |
| 1256 | |
| 1257 | case DEVINFO_STR_NAME: |
| 1258 | strcpy(info->s, "TI99 MiniMemory cartridge pcb"); |
| 1259 | break; |
| 1260 | |
| 1261 | default: |
| 1262 | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1263 | break; |
| 1264 | } |
| 1265 | } |
| 1266 | |
| 1267 | DEVICE_GET_INFO(ti99_cartridge_pcb_super) |
| 1268 | { |
| 1269 | switch(state) |
| 1270 | { |
| 1271 | case DEVINFO_FCT_START: |
| 1272 | info->start = DEVICE_START_NAME(ti99_pcb_super); |
| 1273 | break; |
| 1274 | |
| 1275 | case TI99CART_FCT_ASSM: |
| 1276 | info->f = (genf *) assemble_super; |
| 1277 | break; |
| 1278 | case TI99CARTINFO_FCT_6000_R: |
| 1279 | info->f = (genf *) read_cart_super; |
| 1280 | break; |
| 1281 | case TI99CARTINFO_FCT_6000_W: |
| 1282 | info->f = (genf *) write_cart_super; |
| 1283 | break; |
| 1284 | case TI99CARTINFO_FCT_CRU_R: |
| 1285 | info->f = (genf *) read_cart_cru; |
| 1286 | break; |
| 1287 | case TI99CARTINFO_FCT_CRU_W: |
| 1288 | info->f = (genf *) write_cart_cru; |
| 1289 | break; |
| 1290 | |
| 1291 | // Don't create GROMs |
| 1292 | case DEVINFO_PTR_MACHINE_CONFIG: |
| 1293 | break; |
| 1294 | |
| 1295 | case DEVINFO_STR_NAME: |
| 1296 | strcpy(info->s, "TI99 SuperSpace cartridge pcb"); |
| 1297 | break; |
| 1298 | |
| 1299 | default: |
| 1300 | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1301 | break; |
| 1302 | } |
| 1303 | } |
| 1304 | |
| 1305 | DEVICE_GET_INFO(ti99_cartridge_pcb_mbx) |
| 1306 | { |
| 1307 | switch(state) |
| 1308 | { |
| 1309 | case DEVINFO_FCT_START: |
| 1310 | info->start = DEVICE_START_NAME(ti99_pcb_mbx); |
| 1311 | break; |
| 1312 | |
| 1313 | case TI99CART_FCT_ASSM: |
| 1314 | info->f = (genf *) assemble_mbx; |
| 1315 | break; |
| 1316 | case TI99CARTINFO_FCT_6000_R: |
| 1317 | info->f = (genf *) read_cart_mbx; |
| 1318 | break; |
| 1319 | case TI99CARTINFO_FCT_6000_W: |
| 1320 | info->f = (genf *) write_cart_mbx; |
| 1321 | break; |
| 1322 | |
| 1323 | case DEVINFO_STR_NAME: |
| 1324 | strcpy(info->s, "TI99 MBX cartridge pcb"); |
| 1325 | break; |
| 1326 | |
| 1327 | default: |
| 1328 | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1329 | break; |
| 1330 | } |
| 1331 | } |
| 1332 | |
| 1333 | DEVICE_GET_INFO(ti99_cartridge_pcb_paged379i) |
| 1334 | { |
| 1335 | switch(state) |
| 1336 | { |
| 1337 | case DEVINFO_FCT_START: |
| 1338 | info->start = DEVICE_START_NAME(ti99_pcb_paged379i); |
| 1339 | break; |
| 1340 | |
| 1341 | case TI99CART_FCT_ASSM: |
| 1342 | info->f = (genf *) assemble_paged379i; break; |
| 1343 | |
| 1344 | case TI99CARTINFO_FCT_6000_R: |
| 1345 | info->f = (genf *) read_cart_paged379i; |
| 1346 | break; |
| 1347 | |
| 1348 | case TI99CARTINFO_FCT_6000_W: |
| 1349 | info->f = (genf *) write_cart_paged379i; |
| 1350 | break; |
| 1351 | |
| 1352 | case DEVINFO_STR_NAME: |
| 1353 | strcpy(info->s, "TI99 paged379i cartridge pcb"); |
| 1354 | break; |
| 1355 | |
| 1356 | default: |
| 1357 | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1358 | break; |
| 1359 | } |
| 1360 | } |
| 1361 | |
| 1362 | DEVICE_GET_INFO(ti99_cartridge_pcb_pagedcru) |
| 1363 | { |
| 1364 | switch(state) |
| 1365 | { |
| 1366 | case DEVINFO_FCT_START: |
| 1367 | info->start = DEVICE_START_NAME(ti99_pcb_pagedcru); |
| 1368 | break; |
| 1369 | |
| 1370 | case TI99CART_FCT_ASSM: |
| 1371 | info->f = (genf *) assemble_pagedcru; |
| 1372 | break; |
| 1373 | |
| 1374 | case TI99CARTINFO_FCT_6000_R: |
| 1375 | info->f = (genf *) read_cart_pagedcru; |
| 1376 | break; |
| 1377 | |
| 1378 | case TI99CARTINFO_FCT_6000_W: |
| 1379 | info->f = (genf *) write_cart_pagedcru; |
| 1380 | break; |
| 1381 | |
| 1382 | case TI99CARTINFO_FCT_CRU_R: |
| 1383 | info->f = (genf *) read_cart_cru_paged; |
| 1384 | break; |
| 1385 | case TI99CARTINFO_FCT_CRU_W: |
| 1386 | info->f = (genf *) write_cart_cru_paged; |
| 1387 | break; |
| 1388 | |
| 1389 | case DEVINFO_STR_NAME: |
| 1390 | strcpy(info->s, "TI99 pagedcru cartridge pcb"); |
| 1391 | break; |
| 1392 | |
| 1393 | default: |
| 1394 | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1395 | break; |
| 1396 | } |
| 1397 | } |
| 1398 | |
| 1399 | DEVICE_GET_INFO(ti99_cartridge_pcb_gramkracker) |
| 1400 | { |
| 1401 | switch(state) |
| 1402 | { |
| 1403 | case DEVINFO_FCT_START: |
| 1404 | info->start = DEVICE_START_NAME(ti99_pcb_gramkracker); |
| 1405 | break; |
| 1406 | |
| 1407 | case TI99CART_FCT_ASSM: |
| 1408 | info->f = (genf *) assemble_gramkracker; |
| 1409 | break; |
| 1410 | |
| 1411 | case TI99CART_FCT_DISASSM: |
| 1412 | info->f = (genf *) disassemble_gramkracker; |
| 1413 | break; |
| 1414 | |
| 1415 | case DEVINFO_STR_NAME: |
| 1416 | strcpy(info->s, "TI99 GRAM Kracker system"); |
| 1417 | break; |
| 1418 | |
| 1419 | default: |
| 1420 | DEVICE_GET_INFO_CALL(ti99_cart_common); |
| 1421 | break; |
| 1422 | } |
| 1423 | } |
| 1424 | |
| 1425 | /***************************************************************************** |
| 1426 | The cartridge handling of the multi-cartridge system. |
| 1427 | Every cartridge contains a PCB device. The memory handlers delegate the calls |
| 1428 | to the respective handlers of the cartridges. |
| 1429 | ******************************************************************************/ |
| 1430 | /* |
| 1431 | Initialize a cartridge. Each cartridge contains a PCB device. |
| 1432 | */ |
| 1433 | static DEVICE_START( ti99_cartridge ) |
| 1434 | { |
| 1435 | cartslot_t *cart = (cartslot_t *)downcast<legacy_device_base *>(device)->token(); |
| 1436 | |
| 1437 | /* find the PCB device */ |
| 1438 | cart->pcb_device = device->subdevice(TAG_PCB); |
| 1439 | } |
| 1440 | |
| 1441 | /* |
| 1442 | Load the cartridge image files. Apart from reading, we set pointers |
| 1443 | to the image files so that during runtime we do not need search |
| 1444 | operations. |
| 1445 | */ |
| 1446 | static DEVICE_IMAGE_LOAD( ti99_cartridge ) |
| 1447 | { |
| 1448 | running_device *pcbdev = cartslot_get_pcb(&image.device()); |
| 1449 | // running_device *cartsys = image.device().owner(); |
| 1450 | // ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 1451 | |
| 1452 | int result; |
| 1453 | |
| 1454 | if (pcbdev != NULL) |
| 1455 | { |
| 1456 | /* If we are here, we have a multicart. */ |
| 1457 | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(pcbdev)->token(); |
| 1458 | cartslot_t *cart = (cartslot_t *)downcast<legacy_device_base *>(&image.device())->token(); |
| 1459 | |
| 1460 | /* try opening this as a multicart */ |
| 1461 | // This line requires that cartslot_t be included in cartslot.h, |
| 1462 | // otherwise one cannot make use of multicart handling within such a |
| 1463 | // custom LOAD function. |
| 1464 | multicart_open_error me = multicart_open(image.filename(), image.device().machine->gamedrv->name, MULTICART_FLAGS_LOAD_RESOURCES, &cart->mc); |
| 1465 | |
| 1466 | // Now that we have loaded the image files, let the PCB put them all |
| 1467 | // together. This means we put the images in a structure which allows |
| 1468 | // for a quick access by the memory handlers. Every PCB defines an |
| 1469 | // own assembly method. |
| 1470 | if (me == MCERR_NONE) |
| 1471 | result = pcb->assemble(image); |
| 1472 | else |
| 1473 | fatalerror("Error loading multicart: %s", multicart_error_text(me)); |
| 1474 | } |
| 1475 | else |
| 1476 | { |
| 1477 | fatalerror("Error loading multicart: no pcb found."); |
| 1478 | } |
| 1479 | return result; |
| 1480 | } |
| 1481 | |
| 1482 | /* |
| 1483 | This is called when the cartridge is unplugged (or the emulator is |
| 1484 | stopped). |
| 1485 | */ |
| 1486 | static DEVICE_IMAGE_UNLOAD( ti99_cartridge ) |
| 1487 | { |
| 1488 | running_device *pcbdev; |
| 1489 | // ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(image.device().owner())->token(); |
| 1490 | |
| 1491 | if (downcast<legacy_device_base *>(&image.device())->token() == NULL) |
| 1492 | { |
| 1493 | // This means something went wrong during the pcb |
| 1494 | // identification (e.g. one of the cartridge files was not |
| 1495 | // found). We do not need to (and cannot) unload the cartridge. |
| 1496 | return; |
| 1497 | } |
| 1498 | pcbdev = cartslot_get_pcb(image); |
| 1499 | |
| 1500 | if (pcbdev != NULL) |
| 1501 | { |
| 1502 | ti99_pcb_t *pcb = (ti99_pcb_t *)downcast<legacy_device_base *>(pcbdev)->token(); |
| 1503 | cartslot_t *cart = (cartslot_t *)downcast<legacy_device_base *>(&image.device())->token(); |
| 1504 | |
| 1505 | if (cart->mc != NULL) |
| 1506 | { |
| 1507 | /* Remove pointers and de-big-endianize RAM contents. */ |
| 1508 | pcb->disassemble(image); |
| 1509 | |
| 1510 | // Close the multicart; all RAM resources will be |
| 1511 | // written to disk |
| 1512 | multicart_close(cart->mc); |
| 1513 | cart->mc = NULL; |
| 1514 | |
| 1515 | } |
| 1516 | } |
| 1517 | } |
| 1518 | |
| 1519 | /***************************************************************************** |
| 1520 | The overall multi-cartridge slot system. It contains instances of |
| 1521 | cartridges which contain PCB devices. The memory handlers delegate the calls |
| 1522 | to the respective handlers of the cartridges. |
| 1523 | |
| 1524 | Note that the term "multi-cartridge system" and "multicart" are not the same: |
| 1525 | A "multicart" may contain multiple resources, organized on a PCB. The multi- |
| 1526 | cart system may thus host multiple multicarts. |
| 1527 | |
| 1528 | Actually, the name of the device should be changed (however, the device name |
| 1529 | length is limited) |
| 1530 | ******************************************************************************/ |
| 1531 | |
| 1532 | static READ8Z_DEVICE_HANDLER( ti99_cart_gk_rz ); |
| 1533 | static WRITE8_DEVICE_HANDLER( ti99_cart_gk_w ); |
| 1534 | static void cartridge_gram_kracker_writeg(running_device *cartsys, int offset, UINT8 data); |
| 1535 | static void cartridge_gram_kracker_readg(running_device *device, UINT8 *value); |
| 1536 | static READ8Z_DEVICE_HANDLER( ti99_cart_cru_gk_rz ); |
| 1537 | static WRITE8_DEVICE_HANDLER( ti99_cart_cru_gk_w ); |
| 1538 | |
| 1539 | /* |
| 1540 | Instantiation of a multicart system for the TI family. |
| 1541 | */ |
| 1542 | static DEVICE_START(ti99_multicart) |
| 1543 | { |
| 1544 | int i; |
| 1545 | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 1546 | const ti99_multicart_config* cartconf = (const ti99_multicart_config*)get_config(device); |
| 1547 | |
| 1548 | cartslots->active_slot = 0; |
| 1549 | cartslots->next_free_slot = 0; |
| 1550 | |
| 1551 | for (i=0; i < NUMBER_OF_CARTRIDGE_SLOTS; i++) |
| 1552 | { |
| 1553 | cartslots->cartridge[i].pcb = NULL; |
| 1554 | clear_slot(device, i); |
| 1555 | } |
| 1556 | |
| 1557 | devcb_write_line ready = DEVCB_LINE(cartconf->ready); |
| 1558 | |
| 1559 | devcb_resolve_write_line(&cartslots->ready, &ready, device); |
| 1560 | |
| 1561 | // The cartslot system is initialized now. The cartridges themselves |
| 1562 | // need to check whether their parts are available. |
| 1563 | } |
| 1564 | |
| 1565 | static DEVICE_STOP(ti99_multicart) |
| 1566 | { |
| 1567 | // printf("DEVICE_STOP(ti99_multicart)\n"); |
| 1568 | } |
| 1569 | |
| 1570 | static DEVICE_RESET(ti99_multicart) |
| 1571 | { |
| 1572 | // Consider to propagate RESET to cartridges. |
| 1573 | // However, schematics do not reveal any pin for resetting a cartridge; |
| 1574 | // the reset line is an input used when plugging in a cartridge. |
| 1575 | |
| 1576 | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 1577 | int slotnumber = input_port_read(device->machine, "CARTSLOT"); |
| 1578 | cartslots->fixed_slot = slotnumber-1; /* auto = -1 */ |
| 1579 | } |
| 1580 | |
| 1581 | /* |
| 1582 | Accesses the ROM regions of the cartridge for reading. |
| 1583 | */ |
| 1584 | READ8Z_DEVICE_HANDLER( gromportr_rz ) |
| 1585 | { |
| 1586 | ti99_multicart_state *cartslots = (ti99_multicart_state *) downcast<legacy_device_base *>(device)->token(); |
| 1587 | int slot = cartslots->active_slot; |
| 1588 | cartridge_t *cart; |
| 1589 | |
| 1590 | // Handle GRAM Kracker |
| 1591 | // We could also consider to use a flag, but input_port_read uses |
| 1592 | // a hashtable, so this should not be too slow |
| 1593 | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 1594 | { |
| 1595 | ti99_cart_gk_rz(device, offset, value); |
| 1596 | return; |
| 1597 | } |
| 1598 | |
| 1599 | /* Sanity check. Higher slots are always empty. */ |
| 1600 | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 1601 | return; |
| 1602 | |
| 1603 | // Same as above (GROM read) |
| 1604 | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 1605 | slot=0; |
| 1606 | |
| 1607 | cart = &cartslots->cartridge[slot]; |
| 1608 | |
| 1609 | if (!slot_is_empty(device, slot)) |
| 1610 | { |
| 1611 | // printf("try it on slot %d\n", slot); |
| 1612 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cart->pcb)->token(); |
| 1613 | (*pcbdef->read)(cart->pcb, offset, value); |
| 1614 | } |
| 1615 | } |
| 1616 | |
| 1617 | /* |
| 1618 | Accesses the ROM regions of the cartridge for writing. |
| 1619 | */ |
| 1620 | WRITE8_DEVICE_HANDLER( gromportr_w ) |
| 1621 | { |
| 1622 | ti99_multicart_state *cartslots = (ti99_multicart_state *) downcast<legacy_device_base *>(device)->token(); |
| 1623 | int slot = cartslots->active_slot; |
| 1624 | cartridge_t *cart; |
| 1625 | |
| 1626 | // Handle GRAM Kracker |
| 1627 | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 1628 | ti99_cart_gk_w(device, offset, data); |
| 1629 | |
| 1630 | /* Sanity check. Higher slots are always empty. */ |
| 1631 | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 1632 | return; |
| 1633 | |
| 1634 | /* Same as above (READ16). */ |
| 1635 | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 1636 | slot=0; |
| 1637 | |
| 1638 | cart = &cartslots->cartridge[slot]; |
| 1639 | |
| 1640 | if (!slot_is_empty(device, slot)) |
| 1641 | { |
| 1642 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cart->pcb)->token(); |
| 1643 | (*pcbdef->write)(cart->pcb, offset, data); |
| 1644 | } |
| 1645 | } |
| 1646 | |
| 1647 | /* |
| 1648 | Set the address pointer value or write to GRAM (if available). |
| 1649 | */ |
| 1650 | WRITE8_DEVICE_HANDLER(gromportg_w) |
| 1651 | { |
| 1652 | int slot; |
| 1653 | cartridge_t *cartridge; |
| 1654 | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 1655 | |
| 1656 | // Set the cart slot. |
| 1657 | // 1001 1wbb bbbb bbr0 |
| 1658 | |
| 1659 | cartridge_slot_set(device, (UINT8)((offset>>2) & 0x00ff)); |
| 1660 | slot = cartslots->active_slot; |
| 1661 | |
| 1662 | // The selection mechanism of the console checks the different GROM address |
| 1663 | // bases, and when it finds different data, it concludes that there is |
| 1664 | // indeed a multi-cartridge facility. Empty slots return 0 at all |
| 1665 | // addresses. |
| 1666 | // Unfortunately, TI's cartridge selection mechanism has an undesirable |
| 1667 | // side-effect which we work around in this implementation. |
| 1668 | // When we have only one cartridge we would like to get only the option to |
| 1669 | // select this cartridge and not an option to switch to the next one (which |
| 1670 | // would immediately cycle back to cartridge 1). However, the console |
| 1671 | // routines always show the switch option when any two slots have different |
| 1672 | // content, and this is the case with only one cartridge (slot 2 will then |
| 1673 | // be filled with zeros). |
| 1674 | // So if next_free_slot==1, we have one cartridge in slot 0. |
| 1675 | // In that case we trick the OS to believe that the addressed |
| 1676 | // cartridge appears at all locations which causes it to assume a |
| 1677 | // standard single cartslot. |
| 1678 | |
| 1679 | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 1680 | slot=0; |
| 1681 | |
| 1682 | // Handle the GRAM Kracker |
| 1683 | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 1684 | { |
| 1685 | cartridge_gram_kracker_writeg(device, offset, data); |
| 1686 | return; |
| 1687 | } |
| 1688 | |
| 1689 | if (slot < NUMBER_OF_CARTRIDGE_SLOTS) |
| 1690 | { |
| 1691 | cartridge = &cartslots->cartridge[slot]; |
| 1692 | // Send the request to all mounted GROMs |
| 1693 | if (cartridge->pcb!=NULL) |
| 1694 | { |
| 1695 | for (int i=0; i < 5; i++) |
| 1696 | { |
| 1697 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cartridge->pcb)->token(); |
| 1698 | if (pcbdef->grom[i]!=NULL) |
| 1699 | ti99grom_w(pcbdef->grom[i], offset, data); |
| 1700 | } |
| 1701 | } |
| 1702 | } |
| 1703 | } |
| 1704 | |
| 1705 | /* |
| 1706 | GROM access. Nota bene: We only react to the request if there is a GROM |
| 1707 | at the selected bank with the currently selected ID. If not, just do |
| 1708 | nothing; do not return a value. |
| 1709 | (We must not return a value since all GROMs are connected in parallel, so |
| 1710 | returning a 0, for instance, will spoil the processing of other GROMs.) |
| 1711 | */ |
| 1712 | READ8Z_DEVICE_HANDLER(gromportg_rz) |
| 1713 | { |
| 1714 | int slot; |
| 1715 | cartridge_t *cartridge; |
| 1716 | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 1717 | |
| 1718 | // Set the cart slot. Note that the port must be adjusted for 16-bit |
| 1719 | // systems, i.e. it should be shifted left by 1. |
| 1720 | // 1001 1wbb bbbb bbr0 |
| 1721 | |
| 1722 | cartridge_slot_set(device, (UINT8)((offset>>2) & 0x00ff)); |
| 1723 | |
| 1724 | // Handle the GRAM Kracker |
| 1725 | // BTW, the GK does not have a readable address counter, but the console |
| 1726 | // GROMs will keep our addess counter up to date. That is |
| 1727 | // exactly what happens in the real machine. |
| 1728 | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 1729 | { |
| 1730 | if ((offset & 0x0002)==0) cartridge_gram_kracker_readg(device, value); |
| 1731 | return; |
| 1732 | } |
| 1733 | |
| 1734 | slot = cartslots->active_slot; |
| 1735 | |
| 1736 | // This fixes a minor issue: The selection mechanism of the console |
| 1737 | // concludes that there are multiple cartridges plugged in when it |
| 1738 | // checks the different GROM bases and finds different data. Empty |
| 1739 | // slots should return 0 at all addresses, but when we have only |
| 1740 | // one cartridge, this entails that the second cartridge is |
| 1741 | // different (all zeros), and so we get a meaningless selection |
| 1742 | // option between cartridge 1 and 1 and 1 ... |
| 1743 | // |
| 1744 | // So if next_free_slot==1, we have one cartridge in slot 0. |
| 1745 | // In that case we trick the OS to believe that the addressed |
| 1746 | // cartridge appears at all locations which causes it to assume a |
| 1747 | // standard single cartslot. |
| 1748 | |
| 1749 | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 1750 | slot=0; |
| 1751 | |
| 1752 | if (slot < NUMBER_OF_CARTRIDGE_SLOTS) |
| 1753 | { |
| 1754 | // Select the cartridge which the multicart system is currently pointing at |
| 1755 | cartridge = &cartslots->cartridge[slot]; |
| 1756 | // Send the request to all mounted GROMs on this PCB |
| 1757 | if (cartridge->pcb!=NULL) |
| 1758 | { |
| 1759 | for (int i=0; i < 5; i++) |
| 1760 | { |
| 1761 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cartridge->pcb)->token(); |
| 1762 | if (pcbdef->grom[i]!=NULL) |
| 1763 | ti99grom_rz(pcbdef->grom[i], offset, value); |
| 1764 | } |
| 1765 | } |
| 1766 | } |
| 1767 | } |
| 1768 | |
| 1769 | /* |
| 1770 | CRU interface to cartridges, used only by SuperSpace and Pagedcru |
| 1771 | style cartridges. |
| 1772 | The SuperSpace has a CRU-based memory mapper. It is accessed via the |
| 1773 | cartridge slot on CRU base >0800. |
| 1774 | */ |
| 1775 | READ8Z_DEVICE_HANDLER( gromportc_rz ) |
| 1776 | { |
| 1777 | ti99_multicart_state *cartslots = (ti99_multicart_state *) downcast<legacy_device_base *>(device)->token(); |
| 1778 | cartridge_t *cart; |
| 1779 | |
| 1780 | int slot = cartslots->active_slot; |
| 1781 | |
| 1782 | // Handle GRAM Kracker |
| 1783 | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 1784 | { |
| 1785 | ti99_cart_cru_gk_rz(device, offset, value); |
| 1786 | return; |
| 1787 | } |
| 1788 | |
| 1789 | /* Sanity check. Higher slots are always empty. */ |
| 1790 | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 1791 | return; |
| 1792 | |
| 1793 | /* Same as above (READ16). */ |
| 1794 | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 1795 | slot=0; |
| 1796 | |
| 1797 | cart = &cartslots->cartridge[slot]; |
| 1798 | |
| 1799 | if (!slot_is_empty(device, slot)) |
| 1800 | { |
| 1801 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cart->pcb)->token();; |
| 1802 | if (pcbdef->cruread != NULL) |
| 1803 | (*pcbdef->cruread)(cart->pcb, offset, value); |
| 1804 | } |
| 1805 | } |
| 1806 | |
| 1807 | /* |
| 1808 | Write cartridge mapper CRU interface (SuperSpace, Pagedcru) |
| 1809 | */ |
| 1810 | WRITE8_DEVICE_HANDLER( gromportc_w ) |
| 1811 | { |
| 1812 | ti99_multicart_state *cartslots = (ti99_multicart_state *) downcast<legacy_device_base *>(device)->token(); |
| 1813 | cartridge_t *cart; |
| 1814 | |
| 1815 | int slot = cartslots->active_slot; |
| 1816 | |
| 1817 | // Handle GRAM Kracker |
| 1818 | if ((cartslots->gk_slot != -1) && (input_port_read(device->machine, "CARTSLOT")==CART_GK)) |
| 1819 | { |
| 1820 | ti99_cart_cru_gk_w(device, offset, data); |
| 1821 | return; |
| 1822 | } |
| 1823 | |
| 1824 | /* Sanity check. Higher slots are always empty. */ |
| 1825 | if (slot >= NUMBER_OF_CARTRIDGE_SLOTS) |
| 1826 | return; |
| 1827 | |
| 1828 | /* Same as above (READ16). */ |
| 1829 | if (cartslots->fixed_slot==AUTO && cartslots->next_free_slot==1) |
| 1830 | slot=0; |
| 1831 | |
| 1832 | cart = &cartslots->cartridge[slot]; |
| 1833 | |
| 1834 | if (!slot_is_empty(device, slot)) |
| 1835 | { |
| 1836 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(cart->pcb)->token();; |
| 1837 | if (pcbdef->cruwrite != NULL) |
| 1838 | (*pcbdef->cruwrite)(cart->pcb, offset, data); |
| 1839 | } |
| 1840 | } |
| 1841 | |
| 1842 | /***************************************************************************** |
| 1843 | GramKracker support |
| 1844 | The 80 KiB of the GK are allocated as follows |
| 1845 | 00000 - 01fff: GRAM 0 |
| 1846 | 02000 - 03fff: GRAM 1 (alternatively, the loader GROM appears here) |
| 1847 | 04000 - 05fff: GRAM 2 |
| 1848 | 06000 - 0ffff: GRAM 3-7 (alternatively, the guest GROMs appear here) |
| 1849 | 10000 - 11fff: RAM 1 |
| 1850 | 12000 - 13fff: RAM 2 |
| 1851 | |
| 1852 | The GK emulates GROMs, but without a readable address counter. Thus it |
| 1853 | relies on the console GROMs being still in place (compare with the HSGPL). |
| 1854 | Also, the GK does not implement a GROM buffer. |
| 1855 | ******************************************************************************/ |
| 1856 | |
| 1857 | /* |
| 1858 | Reads from the GRAM space of the GRAM Kracker. |
| 1859 | */ |
| 1860 | static void cartridge_gram_kracker_readg(running_device *cartsys, UINT8 *value) |
| 1861 | { |
| 1862 | // gk_slot is the slot where the GK module is plugged in |
| 1863 | // gk_guest_slot is the slot where the cartridge is plugged in |
| 1864 | |
| 1865 | cartridge_t *gromkrackerbox, *gkguestcart; |
| 1866 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 1867 | |
| 1868 | // Not null, since this is only called when there is really a GK module |
| 1869 | gromkrackerbox = &cartslots->cartridge[cartslots->gk_slot]; |
| 1870 | |
| 1871 | if ((gromkrackerbox->grom_address & 0xe000) == 0x0000) |
| 1872 | { |
| 1873 | // GRAM 0. Only return a value if switch 2 is in GRAM0 position. |
| 1874 | if (get_gk_switch(cartsys, 2)==GK_GRAM0) |
| 1875 | *value = (gromkrackerbox->ram_ptr)[gromkrackerbox->grom_address]; |
| 1876 | } |
| 1877 | |
| 1878 | if ((gromkrackerbox->grom_address & 0xe000) == 0x2000) |
| 1879 | { |
| 1880 | // If the loader is turned on, return loader contents. |
| 1881 | if (get_gk_switch(cartsys, 5)==GK_LDON) |
| 1882 | { |
| 1883 | // The only ROM contained in the GK box is the loader |
| 1884 | *value = gromkrackerbox->grom_ptr[gromkrackerbox->grom_address & 0x1fff]; |
| 1885 | } |
| 1886 | else |
| 1887 | { |
| 1888 | // Loader off |
| 1889 | // GRAM 1. Only return a value if switch 3 is in GRAM12 position. |
| 1890 | if (get_gk_switch(cartsys, 3)==GK_GRAM12) |
| 1891 | *value = (gromkrackerbox->ram_ptr)[gromkrackerbox->grom_address]; |
| 1892 | } |
| 1893 | } |
| 1894 | |
| 1895 | if ((gromkrackerbox->grom_address & 0xe000) == 0x4000) |
| 1896 | { |
| 1897 | // GRAM 2. Only return a value if switch 3 is in GRAM12 position. |
| 1898 | if (get_gk_switch(cartsys, 3)==GK_GRAM12) |
| 1899 | *value = (gromkrackerbox->ram_ptr)[gromkrackerbox->grom_address]; |
| 1900 | } |
| 1901 | |
| 1902 | if (((gromkrackerbox->grom_address & 0xe000) == 0x6000) || |
| 1903 | ((gromkrackerbox->grom_address & 0x8000) == 0x8000)) |
| 1904 | { |
| 1905 | // Cartridge space |
| 1906 | // When a cartridge is installed, it overrides the GK contents |
| 1907 | // but only if it has GROMs |
| 1908 | int cartwithgrom = FALSE; |
| 1909 | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 1910 | |
| 1911 | // Send the request to all mounted GROMs |
| 1912 | if (gkguestcart->pcb!=NULL) |
| 1913 | { |
| 1914 | for (int i=0; i < 5; i++) |
| 1915 | { |
| 1916 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 1917 | if (pcbdef->grom[i]!=NULL) |
| 1918 | { |
| 1919 | ti99grom_rz(pcbdef->grom[i], 0x9800, value); |
| 1920 | cartwithgrom = TRUE; |
| 1921 | } |
| 1922 | } |
| 1923 | } |
| 1924 | if (!cartwithgrom && get_gk_switch(cartsys, 1)==GK_NORMAL) |
| 1925 | { |
| 1926 | *value = (gromkrackerbox->ram_ptr)[gromkrackerbox->grom_address]; |
| 1927 | } |
| 1928 | } |
| 1929 | |
| 1930 | // The GK GROM emulation does not wrap at 8K boundaries. |
| 1931 | gromkrackerbox->grom_address = (gromkrackerbox->grom_address+1) & 0xffff; |
| 1932 | |
| 1933 | /* Reset the write address flipflop. */ |
| 1934 | gromkrackerbox->waddr_LSB = FALSE; |
| 1935 | } |
| 1936 | |
| 1937 | /* |
| 1938 | Writes to the GRAM space of the GRAM Kracker. |
| 1939 | */ |
| 1940 | static void cartridge_gram_kracker_writeg(running_device *cartsys, int offset, UINT8 data) |
| 1941 | { |
| 1942 | // gk_slot is the slot where the GK module is plugged in |
| 1943 | // gk_guest_slot is the slot where the cartridge is plugged in |
| 1944 | cartridge_t *gromkrackerbox; |
| 1945 | ti99_multicart_state *cartslots = get_safe_token(cartsys); |
| 1946 | |
| 1947 | // This is only called when there is really a GK module |
| 1948 | gromkrackerbox = &cartslots->cartridge[cartslots->gk_slot]; |
| 1949 | |
| 1950 | if ((offset & 0x0002)==0x0002) |
| 1951 | { |
| 1952 | // Set address |
| 1953 | if (gromkrackerbox->waddr_LSB) |
| 1954 | { |
| 1955 | /* Accept low byte (2nd write) */ |
| 1956 | gromkrackerbox->grom_address = (gromkrackerbox->grom_address & 0xff00) | data; |
| 1957 | gromkrackerbox->waddr_LSB = FALSE; |
| 1958 | } |
| 1959 | else |
| 1960 | { |
| 1961 | /* Accept high byte (1st write) */ |
| 1962 | gromkrackerbox->grom_address = ((data<<8) & 0xff00) | (gromkrackerbox->grom_address & 0x00ff); |
| 1963 | gromkrackerbox->waddr_LSB = TRUE; |
| 1964 | } |
| 1965 | } |
| 1966 | else |
| 1967 | { |
| 1968 | if (get_gk_switch(cartsys, 4) != GK_WP) |
| 1969 | { |
| 1970 | // Write protection off |
| 1971 | if ( (((gromkrackerbox->grom_address & 0xe000)==0x0000) && get_gk_switch(cartsys, 2)==GK_GRAM0) |
| 1972 | || (((gromkrackerbox->grom_address & 0xe000)==0x2000) && get_gk_switch(cartsys, 3)==GK_GRAM12 && (get_gk_switch(cartsys, 5)==GK_LDOFF)) |
| 1973 | || (((gromkrackerbox->grom_address & 0xe000)==0x4000) && get_gk_switch(cartsys, 3)==GK_GRAM12) |
| 1974 | || (((gromkrackerbox->grom_address & 0xe000)==0x6000) && get_gk_switch(cartsys, 1)==GK_NORMAL && (cartslots->gk_guest_slot==-1)) |
| 1975 | || (((gromkrackerbox->grom_address & 0x8000)==0x8000) && get_gk_switch(cartsys, 1)==GK_NORMAL && (cartslots->gk_guest_slot==-1))) |
| 1976 | { |
| 1977 | (gromkrackerbox->ram_ptr)[gromkrackerbox->grom_address] = data; |
| 1978 | } |
| 1979 | } |
| 1980 | // The GK GROM emulation does not wrap at 8K boundaries. |
| 1981 | gromkrackerbox->grom_address = (gromkrackerbox->grom_address+1) & 0xffff; |
| 1982 | |
| 1983 | /* Reset the write address flipflop. */ |
| 1984 | gromkrackerbox->waddr_LSB = FALSE; |
| 1985 | } |
| 1986 | } |
| 1987 | |
| 1988 | /* |
| 1989 | Reads from the RAM space of the GRAM Kracker. |
| 1990 | */ |
| 1991 | static READ8Z_DEVICE_HANDLER( ti99_cart_gk_rz ) |
| 1992 | { |
| 1993 | // gk_slot is the slot where the GK module is plugged in |
| 1994 | // gk_guest_slot is the slot where the cartridge is plugged in |
| 1995 | cartridge_t *gromkrackerbox, *gkguestcart; |
| 1996 | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 1997 | |
| 1998 | gromkrackerbox = &cartslots->cartridge[cartslots->gk_slot]; |
| 1999 | |
| 2000 | if (cartslots->gk_guest_slot != -1) |
| 2001 | { |
| 2002 | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 2003 | // Legacy mode is not supported with the GK |
| 2004 | if (in_legacy_mode(device)) |
| 2005 | { |
| 2006 | return; |
| 2007 | } |
| 2008 | // printf("accessing cartridge in slot %d\n", slot); |
| 2009 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 2010 | // printf("address=%lx, offset=%lx\n", pcbdef, offset); |
| 2011 | (*pcbdef->read)(gkguestcart->pcb, offset, value); |
| 2012 | return; |
| 2013 | } |
| 2014 | else |
| 2015 | { |
| 2016 | if (get_gk_switch(device, 1)==GK_OFF) return; |
| 2017 | if (get_gk_switch(device, 4)==GK_BANK1) |
| 2018 | { |
| 2019 | *value = (gromkrackerbox->ram_ptr)[offset+0x10000]; |
| 2020 | } |
| 2021 | else |
| 2022 | { |
| 2023 | if (get_gk_switch(device, 4)==GK_BANK2) |
| 2024 | { |
| 2025 | *value = (gromkrackerbox->ram_ptr)[offset+0x12000]; |
| 2026 | } |
| 2027 | else |
| 2028 | { |
| 2029 | // Write protection on; auto-bank |
| 2030 | if (gromkrackerbox->ram_page==0) |
| 2031 | { |
| 2032 | *value = (gromkrackerbox->ram_ptr)[offset+0x10000]; |
| 2033 | } |
| 2034 | else |
| 2035 | { |
| 2036 | *value = (gromkrackerbox->ram_ptr)[offset+0x12000]; |
| 2037 | } |
| 2038 | } |
| 2039 | } |
| 2040 | } |
| 2041 | } |
| 2042 | |
| 2043 | /* |
| 2044 | Writes to the RAM space of the GRAM Kracker. |
| 2045 | */ |
| 2046 | static WRITE8_DEVICE_HANDLER( ti99_cart_gk_w ) |
| 2047 | { |
| 2048 | // gk_slot is the slot where the GK module is plugged in |
| 2049 | // gk_guest_slot is the slot where the cartridge is plugged in |
| 2050 | cartridge_t *gromkrackerbox, *gkguestcart; |
| 2051 | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 2052 | |
| 2053 | gromkrackerbox = &cartslots->cartridge[cartslots->gk_slot]; |
| 2054 | |
| 2055 | if (cartslots->gk_guest_slot != -1) |
| 2056 | { |
| 2057 | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 2058 | // Legacy mode is not supported with the GK |
| 2059 | if (in_legacy_mode(device)) |
| 2060 | { |
| 2061 | return; |
| 2062 | } |
| 2063 | |
| 2064 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 2065 | (*pcbdef->write)(gkguestcart->pcb, offset, data); |
| 2066 | } |
| 2067 | else |
| 2068 | { |
| 2069 | if (get_gk_switch(device, 1)==GK_OFF) |
| 2070 | return; |
| 2071 | |
| 2072 | if (get_gk_switch(device, 4)==GK_BANK1) |
| 2073 | { |
| 2074 | (gromkrackerbox->ram_ptr)[offset + 0x10000] = data; |
| 2075 | } |
| 2076 | else |
| 2077 | { |
| 2078 | if (get_gk_switch(device, 4)==GK_BANK2) |
| 2079 | { |
| 2080 | (gromkrackerbox->ram_ptr)[offset + 0x12000] = data; |
| 2081 | } |
| 2082 | else |
| 2083 | { |
| 2084 | // Write protection on |
| 2085 | // This is handled like in Extended Basic |
| 2086 | gromkrackerbox->ram_page = (offset & 2)>>1; |
| 2087 | } |
| 2088 | } |
| 2089 | } |
| 2090 | } |
| 2091 | |
| 2092 | /* |
| 2093 | Read cartridge mapper CRU interface (Guest in GRAM Kracker) |
| 2094 | */ |
| 2095 | static READ8Z_DEVICE_HANDLER( ti99_cart_cru_gk_rz ) |
| 2096 | { |
| 2097 | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 2098 | cartridge_t *gkguestcart; |
| 2099 | |
| 2100 | if (cartslots->gk_guest_slot != -1) |
| 2101 | { |
| 2102 | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 2103 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 2104 | if (pcbdef->cruread != NULL) |
| 2105 | (*pcbdef->cruread)(gkguestcart->pcb, offset, value); |
| 2106 | } |
| 2107 | } |
| 2108 | |
| 2109 | /* |
| 2110 | Write cartridge mapper CRU interface (Guest in GRAM Kracker) |
| 2111 | */ |
| 2112 | static WRITE8_DEVICE_HANDLER( ti99_cart_cru_gk_w ) |
| 2113 | { |
| 2114 | ti99_multicart_state *cartslots = (ti99_multicart_state *)downcast<legacy_device_base *>(device)->token(); |
| 2115 | cartridge_t *gkguestcart; |
| 2116 | |
| 2117 | if (cartslots->gk_guest_slot != -1) |
| 2118 | { |
| 2119 | gkguestcart = &cartslots->cartridge[cartslots->gk_guest_slot]; |
| 2120 | ti99_pcb_t *pcbdef = (ti99_pcb_t *)downcast<legacy_device_base *>(gkguestcart->pcb)->token(); |
| 2121 | if (pcbdef->cruwrite != NULL) |
| 2122 | (*pcbdef->cruwrite)(gkguestcart->pcb, offset, data); |
| 2123 | } |
| 2124 | } |
| 2125 | |
| 2126 | /*****************************************************************************/ |
| 2127 | #define TI99_CARTRIDGE_SLOT(p) MDRV_CARTSLOT_ADD(p) \ |
| 2128 | MDRV_CARTSLOT_EXTENSION_LIST("rpk,bin") \ |
| 2129 | MDRV_CARTSLOT_PCBTYPE(0, "none", TI99_CARTRIDGE_PCB_NONE) \ |
| 2130 | MDRV_CARTSLOT_PCBTYPE(1, "standard", TI99_CARTRIDGE_PCB_STD) \ |
| 2131 | MDRV_CARTSLOT_PCBTYPE(2, "paged", TI99_CARTRIDGE_PCB_PAGED) \ |
| 2132 | MDRV_CARTSLOT_PCBTYPE(3, "minimem", TI99_CARTRIDGE_PCB_MINIMEM) \ |
| 2133 | MDRV_CARTSLOT_PCBTYPE(4, "super", TI99_CARTRIDGE_PCB_SUPER) \ |
| 2134 | MDRV_CARTSLOT_PCBTYPE(5, "mbx", TI99_CARTRIDGE_PCB_MBX) \ |
| 2135 | MDRV_CARTSLOT_PCBTYPE(6, "paged379i", TI99_CARTRIDGE_PCB_PAGED379I) \ |
| 2136 | MDRV_CARTSLOT_PCBTYPE(7, "pagedcru", TI99_CARTRIDGE_PCB_PAGEDCRU) \ |
| 2137 | MDRV_CARTSLOT_PCBTYPE(8, "gramkracker", TI99_CARTRIDGE_PCB_GRAMKRACKER) \ |
| 2138 | MDRV_CARTSLOT_START(ti99_cartridge) \ |
| 2139 | MDRV_CARTSLOT_LOAD(ti99_cartridge) \ |
| 2140 | MDRV_CARTSLOT_UNLOAD(ti99_cartridge) |
| 2141 | |
| 2142 | static MACHINE_CONFIG_FRAGMENT(ti99_multicart) |
| 2143 | TI99_CARTRIDGE_SLOT("cartridge1") |
| 2144 | TI99_CARTRIDGE_SLOT("cartridge2") |
| 2145 | TI99_CARTRIDGE_SLOT("cartridge3") |
| 2146 | TI99_CARTRIDGE_SLOT("cartridge4") |
| 2147 | MACHINE_CONFIG_END |
| 2148 | |
| 2149 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 2150 | |
| 2151 | #define DEVTEMPLATE_ID(p,s) p##ti99_multicart##s |
| 2152 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_INLINE_CONFIG | DT_HAS_MACHINE_CONFIG |
| 2153 | #define DEVTEMPLATE_NAME "TI99 Multi-Cartridge Extender" |
| 2154 | #define DEVTEMPLATE_FAMILY "Multi-cartridge system" |
| 2155 | #include "devtempl.h" |
| 2156 | |
| 2157 | DEFINE_LEGACY_CART_SLOT_DEVICE(TI99_MULTICART, ti99_multicart); |
src/mess/machine/ti99/hsgpl.c
| r0 | r9485 | |
| 1 | /* |
| 2 | SNUG HSGPL card emulation. |
| 3 | Raphael Nabet, 2003. |
| 4 | |
| 5 | Rewritten as device |
| 6 | Michael Zapf, October 2010 |
| 7 | */ |
| 8 | |
| 9 | #include "emu.h" |
| 10 | #include "emuopts.h" |
| 11 | #include "peribox.h" |
| 12 | #include "hsgpl.h" |
| 13 | #include "machine/at29040a.h" |
| 14 | #include "ti99defs.h" |
| 15 | |
| 16 | /* |
| 17 | Supports 16 banks of 8 GROMs (8kbytes each) with 16 associated banks of |
| 18 | 32kbytes (8kbytes*4) of module ROM, 2 banks of 8 GRAMs with 2 associated |
| 19 | banks of 32 kbytes of RAM, and 512kbytes of DSR. Roms are implemented with |
| 20 | 512kbyte EEPROMs (1 for DSR, 2 for GROMs, 1 for cartridge ROM). RAM is |
| 21 | implemented with 128kbyte SRAMs (1 for GRAM, 1 for cartridge RAM - only the |
| 22 | first 64kbytes of the cartridge RAM chip is used). |
| 23 | |
| 24 | CRU bits: |
| 25 | Name Equates Meaning |
| 26 | >0 DEN DSRENA DSR Enable |
| 27 | >1 GRMENA GRMENA Enable GRAM instead of GROM in banks 0 and 1 |
| 28 | >2 BNKINH* BNKENA Disable banking |
| 29 | >3 PG0 PG0 |
| 30 | >4 PG1 PG1 |
| 31 | >5 PG2 PG2 Paging-Bits for DSR-area |
| 32 | >6 PG3 PG3 |
| 33 | >7 PG4 PG4 |
| 34 | >8 PG5 PG5 |
| 35 | >9 CRDENA CRDENA Activate memory areas of HSGPL (i.e. enable HSGPL GROM and ROM6 ports) |
| 36 | >A WRIENA WRIENA write enable for RAM and GRAM (and flash GROM!) |
| 37 | >B SCENA SCARTE Activate SuperCart-banking |
| 38 | >C LEDENA LEDENA turn LED on |
| 39 | >D - - free |
| 40 | >E MBXENA MBXENA Activate MBX-Banking |
| 41 | >F RAMENA RAMENA Enable RAM6000 instead of ROM6000 in banks 0 and 1 |
| 42 | |
| 43 | |
| 44 | Direct access ports for all memory areas (the original manual gives |
| 45 | >9880->989C and >9C80->9C9C for ROM6000, but this is incorrect): |
| 46 | |
| 47 | Module bank Read Write Read ROM6000 Write ROM6000 |
| 48 | GROM GROM |
| 49 | 0 >9800 >9C00 >9860 Offset >0000 >9C60 Offset >0000 |
| 50 | 1 >9804 >9C04 >9860 Offset >8000 >9C60 Offset >8000 |
| 51 | 2 >9808 >9C08 >9864 Offset >0000 >9C64 Offset >0000 |
| 52 | 3 >980C >9C0C >9864 Offset >8000 >9C64 Offset >8000 |
| 53 | 4 >9810 >9C10 >9868 Offset >0000 >9C68 Offset >0000 |
| 54 | 5 >9814 >9C14 >9868 Offset >8000 >9C68 Offset >8000 |
| 55 | 6 >9818 >9C18 >986C Offset >0000 >9C6C Offset >0000 |
| 56 | 7 >981C >9C1C >986C Offset >8000 >9C6C Offset >8000 |
| 57 | 8 >9820 >9C20 >9870 Offset >0000 >9C70 Offset >0000 |
| 58 | 9 >9824 >9C24 >9870 Offset >8000 >9C70 Offset >8000 |
| 59 | 10 >9828 >9C28 >9874 Offset >0000 >9C74 Offset >0000 |
| 60 | 11 >982C >9C2C >9874 Offset >8000 >9C74 Offset >8000 |
| 61 | 12 >9830 >9C30 >9878 Offset >0000 >9C78 Offset >0000 |
| 62 | 13 >9834 >9C34 >9878 Offset >8000 >9C78 Offset >8000 |
| 63 | 14 >9838 >9C38 >987C Offset >0000 >9C7C Offset >0000 |
| 64 | 15 >983C >9C3C >987C Offset >8000 >9C7C Offset >8000 |
| 65 | |
| 66 | Module bank Read Write Read RAM6000 Write RAM6000 |
| 67 | GRAM GRAM |
| 68 | 16 (Ram) >9880 >9C80 >98C0 Offset >0000 >9CC0 Offset >0000 |
| 69 | 17 (Ram) >9884 >9C84 >98C0 Offset >8000 >9CC0 Offset >8000 |
| 70 | |
| 71 | DSR bank Read Write |
| 72 | 0 - 7 >9840 >9C40 |
| 73 | 8 - 15 >9844 >9C44 |
| 74 | 16 - 23 >9848 >9C48 |
| 75 | 24 - 31 >984C >9C4C |
| 76 | 32 - 39 >9850 >9C50 |
| 77 | 40 - 47 >9854 >9C54 |
| 78 | 48 - 55 >9858 >9C58 |
| 79 | 56 - 63 >985C >9C5C |
| 80 | |
| 81 | Note: Writing only works for areas set up as RAM. To write to the |
| 82 | FEEPROMs, you must used the algorithm specified by their respective |
| 83 | manufacturer. |
| 84 | |
| 85 | FIXME: Crashes when a cartridge is plugged in |
| 86 | */ |
| 87 | |
| 88 | #define CRU_BASE 0x1B00 |
| 89 | |
| 90 | #define RAMSIZE 0x020000 |
| 91 | #define GRAMSIZE 0x020000 |
| 92 | #define FEEPROM_SIZE 0x80000 |
| 93 | #define FLROMSIZE 4*(FEEPROM_SIZE+2) + 1 |
| 94 | |
| 95 | #define NVVERSION 0 |
| 96 | |
| 97 | typedef ti99_pebcard_config hsgpl_config; |
| 98 | |
| 99 | typedef struct _hsgpl_state |
| 100 | { |
| 101 | running_device *dsr, *rom6, *groma, *gromb; |
| 102 | UINT8 *ram6; |
| 103 | UINT8 *gram; |
| 104 | UINT8 *flashrom; |
| 105 | |
| 106 | int current_grom_port; |
| 107 | |
| 108 | int dsr_enabled; |
| 109 | int gram_enabled; |
| 110 | int bank_enabled; |
| 111 | int dsr_page; |
| 112 | int card_enabled; |
| 113 | int write_enabled; |
| 114 | int supercart_enabled; |
| 115 | int led_on; |
| 116 | int mbx_enabled; |
| 117 | int ram_enabled; |
| 118 | |
| 119 | int flash_mode; |
| 120 | |
| 121 | int current_bank; |
| 122 | |
| 123 | /* GROM emulation */ |
| 124 | int raddr_LSB, waddr_LSB; |
| 125 | int grom_address; |
| 126 | |
| 127 | } hsgpl_state; |
| 128 | |
| 129 | INLINE hsgpl_state *get_safe_token(running_device *device) |
| 130 | { |
| 131 | assert(device != NULL); |
| 132 | return (hsgpl_state *)downcast<legacy_device_base *>(device)->token(); |
| 133 | } |
| 134 | |
| 135 | /* |
| 136 | Write hsgpl CRU interface |
| 137 | */ |
| 138 | static WRITE8_DEVICE_HANDLER( cru_w ) |
| 139 | { |
| 140 | hsgpl_state *card = get_safe_token(device); |
| 141 | |
| 142 | if ((offset & 0xff00)==CRU_BASE) |
| 143 | { |
| 144 | int bit = (offset >> 1) & 0x0f; |
| 145 | switch (bit) |
| 146 | { |
| 147 | case 0: |
| 148 | card->dsr_enabled = data; |
| 149 | logerror("hsgpl: Set dsr_enabled=%x\n", data); |
| 150 | break; |
| 151 | case 1: |
| 152 | card->gram_enabled = data; |
| 153 | logerror("hsgpl: Set gram_enabled=%x\n", data); |
| 154 | break; |
| 155 | case 2: |
| 156 | card->bank_enabled = data; |
| 157 | logerror("hsgpl: Set bank_enabled=%x\n", data); |
| 158 | break; |
| 159 | case 3: |
| 160 | case 4: |
| 161 | case 5: |
| 162 | case 6: |
| 163 | case 7: |
| 164 | case 8: |
| 165 | if (data) |
| 166 | card->dsr_page |= (1 << (bit-3)); |
| 167 | else |
| 168 | card->dsr_page &= ~(1 << (bit-3)); |
| 169 | logerror("hsgpl: Set dsr_page=%d\n", card->dsr_page); |
| 170 | break; |
| 171 | case 9: |
| 172 | card->card_enabled = data; |
| 173 | logerror("hsgpl: Set card_enabled=%x\n", data); |
| 174 | break; |
| 175 | case 10: |
| 176 | card->write_enabled = data; |
| 177 | logerror("hsgpl: Set write_enabled=%x\n", data); |
| 178 | break; |
| 179 | case 11: |
| 180 | card->supercart_enabled = data; |
| 181 | logerror("hsgpl: Set supercart_enabled=%x\n", data); |
| 182 | break; |
| 183 | case 12: |
| 184 | card->led_on = data; |
| 185 | logerror("hsgpl: Set led_on=%x\n", data); |
| 186 | break; |
| 187 | case 13: |
| 188 | break; |
| 189 | case 14: |
| 190 | card->mbx_enabled = data; |
| 191 | logerror("hsgpl: Set mbx_enabled=%x\n", data); |
| 192 | break; |
| 193 | case 15: |
| 194 | card->ram_enabled = data; |
| 195 | logerror("hsgpl: Set ram_enabled=%x\n", data); |
| 196 | break; |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | /* |
| 202 | GROM read. This is somewhat different to the original TI GROMs, so we |
| 203 | partly reimplement it here. Anyway, the card does not use GROMs but makes |
| 204 | use of a special decoder chip (MACH) which emulates the GROM functions. |
| 205 | */ |
| 206 | static READ8Z_DEVICE_HANDLER ( hsgpl_grom_rz ) |
| 207 | { |
| 208 | int port; |
| 209 | hsgpl_state *card = get_safe_token(device); |
| 210 | |
| 211 | //activecpu_adjust_icount(-4); |
| 212 | |
| 213 | // 1001 10bb bbbb bba0 |
| 214 | port = card->current_grom_port = (offset & 0x3fc) >> 2; |
| 215 | |
| 216 | if (offset & 2) |
| 217 | { // Read GPL address. This must be available even when the rest |
| 218 | // of the card is offline (card_enabled=0). |
| 219 | card->waddr_LSB = FALSE; |
| 220 | |
| 221 | if (card->raddr_LSB) |
| 222 | { |
| 223 | *value = ((card->grom_address + 1) & 0xff); |
| 224 | card->raddr_LSB = FALSE; |
| 225 | } |
| 226 | else |
| 227 | { |
| 228 | *value = ((card->grom_address + 1) >> 8) & 0xff; |
| 229 | card->raddr_LSB = TRUE; |
| 230 | } |
| 231 | } |
| 232 | else |
| 233 | { /* read GPL data */ |
| 234 | if (card->card_enabled) |
| 235 | { |
| 236 | if ((port < 2) && (card->gram_enabled)) |
| 237 | { |
| 238 | *value = card->gram[card->grom_address + 0x10000*port]; |
| 239 | } |
| 240 | else |
| 241 | { |
| 242 | if (port < 8) |
| 243 | { |
| 244 | if (!card->flash_mode) |
| 245 | { |
| 246 | *value = at29c040a_r(card->groma, card->grom_address + 0x10000*port); |
| 247 | } |
| 248 | } |
| 249 | else |
| 250 | { |
| 251 | if (port < 16) |
| 252 | { |
| 253 | *value = at29c040a_r(card->gromb, card->grom_address + 0x10000*(port-8)); |
| 254 | } |
| 255 | else |
| 256 | { |
| 257 | if (port < 24) |
| 258 | { |
| 259 | *value = at29c040a_r(card->dsr, card->grom_address + 0x10000*(port-16)); |
| 260 | } |
| 261 | else |
| 262 | { |
| 263 | if (port < 32) |
| 264 | { |
| 265 | /* The HSGPL manual says 32-47, but this is incorrect */ |
| 266 | *value = at29c040a_r(card->rom6, card->grom_address + 0x10000*(port-24)); |
| 267 | } |
| 268 | else |
| 269 | { |
| 270 | if (port==32 || port==33) |
| 271 | { |
| 272 | *value = card->gram[card->grom_address + 0x10000*(port-32)]; |
| 273 | } |
| 274 | else |
| 275 | { |
| 276 | if (port==48 || port==49) |
| 277 | { |
| 278 | *value = card->ram6[card->grom_address]; |
| 279 | } |
| 280 | else |
| 281 | { |
| 282 | logerror("hsgpl: Attempt to read from undefined port 0x%0x; ignored.\n", port); |
| 283 | } |
| 284 | } |
| 285 | } |
| 286 | } |
| 287 | } |
| 288 | } |
| 289 | } |
| 290 | } |
| 291 | // The address auto-increment should be done even when the card is |
| 292 | // offline |
| 293 | card->grom_address++; |
| 294 | card->raddr_LSB = card->waddr_LSB = FALSE; |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | /* |
| 299 | GROM write |
| 300 | */ |
| 301 | static WRITE8_DEVICE_HANDLER ( hsgpl_grom_w ) |
| 302 | { |
| 303 | int port; |
| 304 | hsgpl_state *card = get_safe_token(device); |
| 305 | |
| 306 | //activecpu_adjust_icount(-4); |
| 307 | |
| 308 | // 1001 11bb bbbb bba0 |
| 309 | port = card->current_grom_port = (offset & 0x3fc) >> 2; |
| 310 | |
| 311 | if (offset & 2) |
| 312 | { // Write GPL address. This must be available even when the rest |
| 313 | // of the card is offline (card_enabled=0). |
| 314 | card->raddr_LSB = FALSE; |
| 315 | |
| 316 | if (card->waddr_LSB) |
| 317 | { |
| 318 | card->grom_address = (card->grom_address & 0xFF00) | data; |
| 319 | card->waddr_LSB = FALSE; |
| 320 | } |
| 321 | else |
| 322 | { |
| 323 | card->grom_address = (data << 8) | (card->grom_address & 0xFF); |
| 324 | card->waddr_LSB = TRUE; |
| 325 | } |
| 326 | } |
| 327 | else |
| 328 | { |
| 329 | if (card->card_enabled) |
| 330 | { |
| 331 | /* write GPL data */ |
| 332 | if (card->write_enabled) |
| 333 | { |
| 334 | if ((port < 2) && (card->gram_enabled)) |
| 335 | { |
| 336 | card->gram[card->grom_address + 0x10000*port] = data; |
| 337 | } |
| 338 | else |
| 339 | { |
| 340 | if (port < 8) |
| 341 | { |
| 342 | at29c040a_w(card->groma, card->grom_address + 0x10000*port, data); |
| 343 | } |
| 344 | else |
| 345 | { |
| 346 | if (port < 16) |
| 347 | { |
| 348 | at29c040a_w(card->gromb, card->grom_address + 0x10000*(port-8), data); |
| 349 | } |
| 350 | else |
| 351 | { |
| 352 | if (port < 24) |
| 353 | { |
| 354 | at29c040a_w(card->dsr, card->grom_address + 0x10000*(port-16), data); |
| 355 | } |
| 356 | else |
| 357 | { |
| 358 | if (port < 32) |
| 359 | { |
| 360 | /* The HSGPL manual says 32-47, but this is incorrect */ |
| 361 | at29c040a_w(card->rom6, card->grom_address + 0x10000*(port-24), data); |
| 362 | } |
| 363 | else |
| 364 | { |
| 365 | if (port==32 || port==33) |
| 366 | { |
| 367 | card->gram[card->grom_address + 0x10000*(port-32)] = data; |
| 368 | } |
| 369 | else |
| 370 | { |
| 371 | if (port==48 || port==49) |
| 372 | { |
| 373 | card->ram6[card->grom_address] = data; |
| 374 | } |
| 375 | else |
| 376 | { |
| 377 | logerror("hsgpl: Attempt to write to undefined port; ignored.\n"); |
| 378 | } |
| 379 | } |
| 380 | } |
| 381 | } |
| 382 | } |
| 383 | } |
| 384 | } |
| 385 | } |
| 386 | } |
| 387 | // The address auto-increment should be done even when the card is |
| 388 | // offline |
| 389 | card->grom_address++; |
| 390 | card->raddr_LSB = card->waddr_LSB = FALSE; |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | static READ8Z_DEVICE_HANDLER ( hsgpl_dsrspace_rz ) |
| 395 | { |
| 396 | hsgpl_state *card = get_safe_token(device); |
| 397 | if (card->dsr_enabled) |
| 398 | { |
| 399 | *value = at29c040a_r(card->dsr, (offset-0x4000) + 0x2000 * card->dsr_page); |
| 400 | // logerror("hsgpl: read dsr %04x[%02x] -> %02x\n", offset, card->dsr_page, *value); |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | static READ8Z_DEVICE_HANDLER ( hsgpl_cartspace_rz ) |
| 405 | { |
| 406 | hsgpl_state *card = get_safe_token(device); |
| 407 | |
| 408 | if (!card->card_enabled || card->flash_mode) |
| 409 | { |
| 410 | // logerror("hsgpl cart read ignored (enable=%02x)\n", card->card_enabled); |
| 411 | return; |
| 412 | } |
| 413 | |
| 414 | int port = card->current_grom_port; |
| 415 | |
| 416 | if ((port < 2) && (card->ram_enabled)) |
| 417 | { |
| 418 | *value = card->ram6[(offset-0x6000) + 0x2000*card->current_bank + 0x8000*port]; |
| 419 | // logerror("hsgpl cart ram read %04x -> %02x\n", offset, *value); |
| 420 | return; |
| 421 | } |
| 422 | |
| 423 | if (port < 16) |
| 424 | { |
| 425 | *value = at29c040a_r(card->rom6, (offset-0x6000) + 0x2000*card->current_bank + 0x8000*port); |
| 426 | // logerror("hsgpl cart read %04x -> %02x\n", offset, *value); |
| 427 | } |
| 428 | else |
| 429 | { |
| 430 | if (port==32 || port==33) |
| 431 | { |
| 432 | *value = card->ram6[(offset-0x6000) + 0x2000*card->current_bank + 0x8000*(port-32)]; |
| 433 | } |
| 434 | else |
| 435 | { |
| 436 | logerror("unknown 0x6000 port\n"); |
| 437 | } |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | static WRITE8_DEVICE_HANDLER ( hsgpl_cartspace_w ) |
| 442 | { |
| 443 | hsgpl_state *card = get_safe_token(device); |
| 444 | |
| 445 | if (!card->card_enabled || card->flash_mode) |
| 446 | { |
| 447 | logerror("hsgpl cart write ignored: card_enabled=%02x\n", card->card_enabled); |
| 448 | return; |
| 449 | } |
| 450 | |
| 451 | int port = card->current_grom_port; |
| 452 | // logerror("hsgpl cart write %04x -> %02x\n", offset, data); |
| 453 | |
| 454 | if (card->bank_enabled) |
| 455 | { |
| 456 | card->current_bank = (offset>>1) & 3; |
| 457 | // logerror("hsgpl cart select bank %02x\n", card->current_bank); |
| 458 | return; /* right??? */ |
| 459 | } |
| 460 | |
| 461 | if ((card->mbx_enabled) && (offset==0x6ffe)) |
| 462 | { /* MBX: mapper at 0x6ffe */ |
| 463 | card->current_bank = data & 0x03; |
| 464 | return; |
| 465 | } |
| 466 | |
| 467 | // MBX: RAM in 0x6c00-0x6ffd (it is unclear whether the MBX RAM area is |
| 468 | // enabled/disabled by the wriena bit). I guess RAM is unpaged, but it is |
| 469 | // not implemented |
| 470 | if ((card->write_enabled) || ((card->mbx_enabled) && ((offset & 0xfc00)==0x6c00))) |
| 471 | { |
| 472 | if ((port < 2) && (card->ram_enabled)) |
| 473 | { |
| 474 | card->ram6[(offset-0x6000) + 0x2000*card->current_bank + 0x8000*port ] = data; |
| 475 | } |
| 476 | else |
| 477 | { // keep in mind that these lines are also reached for port < 2 |
| 478 | // and !ram_enabled |
| 479 | if (port < 16) |
| 480 | { |
| 481 | // feeprom is normally written to using GPL ports, and I don't know |
| 482 | // writing through >6000 page is enabled |
| 483 | /* |
| 484 | at29c040a_w(feeprom_rom6, 1 + 2*offset + 0x2000*hsgpl.cur_bank + 0x8000*port, data); |
| 485 | at29c040a_w(feeprom_rom6, 2*offset + 0x2000*hsgpl.cur_bank + 0x8000*port, data >> 8); |
| 486 | */ |
| 487 | } |
| 488 | else |
| 489 | { |
| 490 | if (port==32 || port==33) |
| 491 | { |
| 492 | card->ram6[(offset-0x6000) + 0x2000*card->current_bank + 0x8000*(port-32)] = data; |
| 493 | } |
| 494 | else |
| 495 | { |
| 496 | logerror("unknown 0x6000 port\n"); |
| 497 | } |
| 498 | } |
| 499 | } |
| 500 | } |
| 501 | } |
| 502 | |
| 503 | /* |
| 504 | Memory read |
| 505 | */ |
| 506 | static READ8Z_DEVICE_HANDLER ( data_rz ) |
| 507 | { |
| 508 | if ((offset & 0xe000)==0x4000) |
| 509 | { |
| 510 | hsgpl_dsrspace_rz(device, offset, value); |
| 511 | } |
| 512 | |
| 513 | if ((offset & 0xe000)==0x6000) |
| 514 | { |
| 515 | hsgpl_cartspace_rz(device, offset, value); |
| 516 | } |
| 517 | |
| 518 | // 1001 1wbb bbbb bba0 |
| 519 | if ((offset & 0xfc01)==0x9800) |
| 520 | { |
| 521 | hsgpl_grom_rz(device, offset, value); |
| 522 | } |
| 523 | } |
| 524 | |
| 525 | /* |
| 526 | Memory write |
| 527 | */ |
| 528 | static WRITE8_DEVICE_HANDLER ( data_w ) |
| 529 | { |
| 530 | if ((offset & 0xe000)==0x6000) |
| 531 | { |
| 532 | hsgpl_cartspace_w(device, offset, data); |
| 533 | } |
| 534 | |
| 535 | // 1001 1wbb bbbb bba0 |
| 536 | if ((offset & 0xfc01)==0x9c00) |
| 537 | { |
| 538 | hsgpl_grom_w(device, offset, data); |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | static ti99_peb_card hsgpl_card = |
| 543 | { |
| 544 | data_rz, |
| 545 | data_w, |
| 546 | NULL, |
| 547 | cru_w, |
| 548 | NULL, NULL, NULL, NULL |
| 549 | }; |
| 550 | |
| 551 | static DEVICE_START( hsgpl ) |
| 552 | { |
| 553 | logerror("HSGPL start\n"); |
| 554 | hsgpl_state *card = get_safe_token(device); |
| 555 | card->dsr = device->subdevice("u9_dsr"); |
| 556 | card->rom6 = device->subdevice("u6_rom6"); |
| 557 | card->groma = device->subdevice("u4_grom"); |
| 558 | card->gromb = device->subdevice("u1_grom"); |
| 559 | |
| 560 | /* We need this for the NVRAM; at this point we cannot query the settings yet. */ |
| 561 | card->flashrom = (UINT8*)malloc(FLROMSIZE); |
| 562 | } |
| 563 | |
| 564 | static DEVICE_STOP( hsgpl ) |
| 565 | { |
| 566 | hsgpl_state *card = get_safe_token(device); |
| 567 | logerror("hsgpl: stop\n"); |
| 568 | free(card->flashrom); |
| 569 | if (card->ram6) |
| 570 | { |
| 571 | free(card->ram6); |
| 572 | free(card->gram); |
| 573 | } |
| 574 | } |
| 575 | |
| 576 | static DEVICE_RESET( hsgpl ) |
| 577 | { |
| 578 | logerror("hsgpl: reset\n"); |
| 579 | hsgpl_state *card = get_safe_token(device); |
| 580 | |
| 581 | /* If the card is selected in the menu, register the card */ |
| 582 | if (input_port_read(device->machine, "EXTCARD") & (EXT_HSGPL_ON | EXT_HSGPL_FLASH)) |
| 583 | { |
| 584 | running_device *peb = device->owner(); |
| 585 | int success = mount_card(peb, device, &hsgpl_card, get_pebcard_config(device)->slot); |
| 586 | if (!success) |
| 587 | { |
| 588 | logerror("hsgpl: Failed to mount.\n"); |
| 589 | return; |
| 590 | } |
| 591 | |
| 592 | card->grom_address = 0; |
| 593 | card->raddr_LSB = 0; |
| 594 | card->waddr_LSB = 0; |
| 595 | card->current_grom_port = 0; |
| 596 | card->current_bank = 0; |
| 597 | |
| 598 | card->dsr_enabled = FALSE; |
| 599 | card->gram_enabled = FALSE; |
| 600 | card->bank_enabled = TRUE; // important, assumed to be enabled by default |
| 601 | card->dsr_page = 0; |
| 602 | card->card_enabled = TRUE; // important, assumed to be enabled by default |
| 603 | card->write_enabled = FALSE; |
| 604 | card->supercart_enabled = FALSE; |
| 605 | card->led_on = FALSE; |
| 606 | card->mbx_enabled = FALSE; |
| 607 | card->ram_enabled = FALSE; |
| 608 | |
| 609 | card->flash_mode = (input_port_read(device->machine, "EXTCARD") & EXT_HSGPL_FLASH); |
| 610 | if (card->flash_mode) logerror("hsgpl: flash mode\n"); |
| 611 | else logerror("hsgpl: full mode\n"); |
| 612 | |
| 613 | if (card->ram6==NULL) |
| 614 | { |
| 615 | card->ram6 = (UINT8*)malloc(RAMSIZE); |
| 616 | card->gram = (UINT8*)malloc(GRAMSIZE); |
| 617 | } |
| 618 | } |
| 619 | } |
| 620 | |
| 621 | static DEVICE_NVRAM( hsgpl ) |
| 622 | { |
| 623 | // Called between START and RESET |
| 624 | hsgpl_state *card = get_safe_token(device); |
| 625 | astring *hsname = astring_assemble_3(astring_alloc(), device->machine->gamedrv->name, PATH_SEPARATOR, "hsgpl.nv"); |
| 626 | file_error filerr; |
| 627 | mame_file *nvfile; |
| 628 | |
| 629 | if (read_or_write==0) |
| 630 | { |
| 631 | logerror("hsgpl: device nvram load %s\n", astring_c(hsname)); |
| 632 | |
| 633 | filerr = mame_fopen(SEARCHPATH_NVRAM, astring_c(hsname), OPEN_FLAG_READ, &nvfile); |
| 634 | if (filerr != FILERR_NONE) |
| 635 | { |
| 636 | logerror("hsgpl: Could not restore NVRAM\n"); |
| 637 | return; |
| 638 | } |
| 639 | |
| 640 | if (mame_fread(nvfile, card->flashrom, FLROMSIZE) != FLROMSIZE) |
| 641 | { |
| 642 | logerror("hsgpl: Error loading NVRAM; unexpected EOF\n"); |
| 643 | mame_fclose(nvfile); |
| 644 | return; |
| 645 | } |
| 646 | |
| 647 | if (card->flashrom[0] != NVVERSION) |
| 648 | { |
| 649 | logerror("hsgpl: Wrong NVRAM image version: %d\n", card->flashrom[0]); |
| 650 | mame_fclose(nvfile); |
| 651 | return; |
| 652 | } |
| 653 | mame_fclose(nvfile); |
| 654 | } |
| 655 | else |
| 656 | { |
| 657 | logerror("hsgpl: device nvram save %s\n", astring_c(hsname)); |
| 658 | // Check dirty flags |
| 659 | if (at29c040a_is_dirty(card->dsr) || |
| 660 | at29c040a_is_dirty(card->rom6) || |
| 661 | at29c040a_is_dirty(card->groma) || |
| 662 | at29c040a_is_dirty(card->gromb)) |
| 663 | { |
| 664 | card->flashrom[0] = NVVERSION; |
| 665 | filerr = mame_fopen(SEARCHPATH_NVRAM, astring_c(hsname), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &nvfile); |
| 666 | |
| 667 | if (filerr != FILERR_NONE) |
| 668 | { |
| 669 | logerror("hsgpl: Could not save NVRAM\n"); |
| 670 | } |
| 671 | else |
| 672 | { |
| 673 | if (mame_fwrite(nvfile, card->flashrom, FLROMSIZE) != FLROMSIZE) |
| 674 | { |
| 675 | logerror("hsgpl: Error while saving contents of NVRAM.\n"); |
| 676 | } |
| 677 | } |
| 678 | mame_fclose(nvfile); |
| 679 | return; |
| 680 | } |
| 681 | else |
| 682 | { |
| 683 | logerror("hsgpl: No changes on card, leaving nvram file unchanged.\n"); |
| 684 | } |
| 685 | } |
| 686 | } |
| 687 | |
| 688 | /* |
| 689 | Get the pointer to the memory data from the HSGPL card. Called by the FEEPROM. |
| 690 | */ |
| 691 | static UINT8 *get_mem_ptr(running_device *device) |
| 692 | { |
| 693 | hsgpl_state *card = get_safe_token(device); |
| 694 | return card->flashrom; |
| 695 | } |
| 696 | |
| 697 | MACHINE_CONFIG_FRAGMENT( hsgpl ) |
| 698 | MDRV_AT29C040_ADD_P( "u9_dsr", get_mem_ptr, 0x000001) |
| 699 | MDRV_AT29C040_ADD_P( "u4_grom", get_mem_ptr, 0x080003) |
| 700 | MDRV_AT29C040_ADD_P( "u1_grom", get_mem_ptr, 0x100005) |
| 701 | MDRV_AT29C040_ADD_P( "u6_rom6", get_mem_ptr, 0x180007) |
| 702 | MACHINE_CONFIG_END |
| 703 | |
| 704 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 705 | |
| 706 | #define DEVTEMPLATE_ID(p,s) p##hsgpl##s |
| 707 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_INLINE_CONFIG | DT_HAS_MACHINE_CONFIG | DT_HAS_NVRAM |
| 708 | #define DEVTEMPLATE_NAME "SNUG High-Speed GPL card" |
| 709 | #define DEVTEMPLATE_FAMILY "Peripheral expansion" |
| 710 | #include "devtempl.h" |
| 711 | |
| 712 | DEFINE_LEGACY_NVRAM_DEVICE( HSGPL, hsgpl ); |
src/mess/machine/ti99/genboard.c
| r0 | r9485 | |
| 1 | /*************************************************************************** |
| 2 | Geneve 9640 system board |
| 3 | |
| 4 | Michael Zapf, October 2010 |
| 5 | ***************************************************************************/ |
| 6 | |
| 7 | #include "emu.h" |
| 8 | #include "genboard.h" |
| 9 | #include "video/v9938.h" |
| 10 | #include "videowrp.h" |
| 11 | #include "sound/sn76496.h" |
| 12 | #include "machine/mm58274c.h" |
| 13 | #include "peribox.h" |
| 14 | #include "includes/geneve.h" |
| 15 | |
| 16 | #define KEYQUEUESIZE 256 |
| 17 | #define MAXKEYMSGLENGTH 10 |
| 18 | #define KEYAUTOREPEATDELAY 30 |
| 19 | #define KEYAUTOREPEATRATE 6 |
| 20 | |
| 21 | #define SRAM_SIZE 64*1024 |
| 22 | #define DRAM_SIZE 512*1024 |
| 23 | |
| 24 | typedef struct _genboard_state |
| 25 | { |
| 26 | /* Mouse support */ |
| 27 | int last_mx; |
| 28 | int last_my; |
| 29 | |
| 30 | /* Joystick support */ |
| 31 | int joySel; |
| 32 | |
| 33 | /* Keyboard support */ |
| 34 | UINT8 keyQueue[KEYQUEUESIZE]; |
| 35 | int keyQueueHead; |
| 36 | int keyQueueLen; |
| 37 | int keyInBuf; |
| 38 | int keyReset; |
| 39 | UINT32 keyStateSave[4]; |
| 40 | int keyNumLockState; |
| 41 | int keyCtrlState; |
| 42 | int keyAltState; |
| 43 | int keyRealShiftState; |
| 44 | int keyFakeShiftState; |
| 45 | int keyFakeUnshiftState; |
| 46 | int keyAutoRepeatKey; |
| 47 | int keyAutoRepeatTimer; |
| 48 | |
| 49 | /* Mode flags */ |
| 50 | int palvideo; // bit 5 + |
| 51 | int capslock; // bit 7 + |
| 52 | int keyboard_clock; // bit 8 + |
| 53 | int keep_keybuf; // bit 9 keyclear - |
| 54 | int zero_wait; // bit 15 |
| 55 | |
| 56 | /* GROM simulation */ |
| 57 | int grom_address; |
| 58 | int gromraddr_LSB; |
| 59 | int gromwaddr_LSB; |
| 60 | |
| 61 | /* Memory */ |
| 62 | UINT8 *sram; |
| 63 | UINT8 *dram; |
| 64 | UINT8 *eprom; |
| 65 | |
| 66 | /* Mapper */ |
| 67 | int geneve_mode; // + |
| 68 | int direct_mode; // mapmode - |
| 69 | int cartridge_size_8K; // + |
| 70 | int cartridge_secondpage; // + |
| 71 | int cartridge6_writable; // protect - |
| 72 | int cartridge7_writable; // protect - |
| 73 | int map[8]; |
| 74 | |
| 75 | int line_count; |
| 76 | |
| 77 | /* Devices */ |
| 78 | running_device *cpu; |
| 79 | running_device *video; |
| 80 | running_device *tms9901; |
| 81 | running_device *clock; |
| 82 | running_device *peribox; |
| 83 | running_device *sound; |
| 84 | |
| 85 | } genboard_state; |
| 86 | |
| 87 | static const UINT8 keyboard_mf1_code[0xe] = |
| 88 | { |
| 89 | /* extended keys that are equivalent to non-extended keys */ |
| 90 | 0x1c, /* keypad enter */ |
| 91 | 0x1d, /* right control */ |
| 92 | 0x38, /* alt gr */ |
| 93 | /* extra codes are 0x5b for Left Windows, 0x5c for Right Windows, 0x5d |
| 94 | for Menu, 0x5e for power, 0x5f for sleep, 0x63 for wake, but I doubt |
| 95 | any Geneve program would take advantage of these. */ |
| 96 | |
| 97 | /* extended key that is equivalent to a non-extended key |
| 98 | with shift off */ |
| 99 | 0x35, /* pad slash */ |
| 100 | |
| 101 | /* extended keys that are equivalent to non-extended keys |
| 102 | with numlock off */ |
| 103 | 0x47, /* home */ |
| 104 | 0x48, /* up */ |
| 105 | 0x49, /* page up */ |
| 106 | 0x4b, /* left */ |
| 107 | 0x4d, /* right */ |
| 108 | 0x4f, /* end */ |
| 109 | 0x50, /* down */ |
| 110 | 0x51, /* page down */ |
| 111 | 0x52, /* insert */ |
| 112 | 0x53 /* delete */ |
| 113 | }; |
| 114 | |
| 115 | static void poll_keyboard(running_device *device); |
| 116 | static void poll_mouse(running_device *device); |
| 117 | static void read_key_if_possible(genboard_state *board); |
| 118 | static UINT8 get_recent_key(running_device *device); |
| 119 | |
| 120 | static const char *keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5", "KEY6", "KEY7" }; |
| 121 | |
| 122 | INLINE genboard_state *get_safe_token(running_device *device) |
| 123 | { |
| 124 | assert(device != NULL); |
| 125 | assert(downcast<legacy_device_base *>(device)->token() != NULL); |
| 126 | return (genboard_state *)downcast<legacy_device_base *>(device)->token(); |
| 127 | } |
| 128 | |
| 129 | /* |
| 130 | scanline interrupt |
| 131 | */ |
| 132 | INTERRUPT_GEN( geneve_hblank_interrupt ) |
| 133 | { |
| 134 | running_device *dev = device->machine->device("geneve_board"); |
| 135 | genboard_state *board = get_safe_token(dev); |
| 136 | v9938_interrupt(device->machine, 0); |
| 137 | board->line_count++; |
| 138 | if (board->line_count == 262) |
| 139 | { |
| 140 | board->line_count = 0; |
| 141 | poll_keyboard(dev); |
| 142 | poll_mouse(dev); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | /**************************************************************************** |
| 147 | GROM simulation. The Geneve board simulated GROM circuits within its gate |
| 148 | array. |
| 149 | *****************************************************************************/ |
| 150 | |
| 151 | /* |
| 152 | Simulates GROM. The real Geneve does not use GROMs but simulates them |
| 153 | within the gate array. Unlike with real GROMs, no address wrapping occurs, |
| 154 | and the complete 64K space is available. |
| 155 | */ |
| 156 | static READ8_DEVICE_HANDLER( read_grom ) |
| 157 | { |
| 158 | genboard_state *board = get_safe_token(device); |
| 159 | UINT8 reply; |
| 160 | if (offset & 0x0002) |
| 161 | { |
| 162 | // GROM address handling |
| 163 | board->gromwaddr_LSB = FALSE; |
| 164 | |
| 165 | if (board->gromraddr_LSB) |
| 166 | { |
| 167 | reply = board->grom_address & 0xff; |
| 168 | board->gromraddr_LSB = FALSE; |
| 169 | } |
| 170 | else |
| 171 | { |
| 172 | reply = (board->grom_address >> 8) & 0xff; |
| 173 | board->gromraddr_LSB = TRUE; |
| 174 | } |
| 175 | } |
| 176 | else |
| 177 | { |
| 178 | // GROM data handling |
| 179 | // GROMs are stored in pages 38..3f |
| 180 | int page = 0x38; |
| 181 | reply = board->dram[page*0x2000 + board->grom_address]; |
| 182 | board->grom_address = (board->grom_address + 1) & 0xffff; |
| 183 | board->gromraddr_LSB = board->gromwaddr_LSB = FALSE; |
| 184 | } |
| 185 | return reply; |
| 186 | } |
| 187 | |
| 188 | /* |
| 189 | Simulates GROM. The real Geneve does not use GROMs but simulates them |
| 190 | within the gate array. |
| 191 | */ |
| 192 | static WRITE8_DEVICE_HANDLER( write_grom ) |
| 193 | { |
| 194 | genboard_state *board = get_safe_token(device); |
| 195 | if (offset & 0x0002) |
| 196 | { |
| 197 | // set address |
| 198 | board->gromraddr_LSB = FALSE; |
| 199 | if (board->gromwaddr_LSB) |
| 200 | { |
| 201 | board->grom_address = (board->grom_address & 0xff00) | data; |
| 202 | board->grom_address = (board->grom_address + 1) & 0xffff; |
| 203 | board->gromwaddr_LSB = FALSE; |
| 204 | } |
| 205 | else |
| 206 | { |
| 207 | board->grom_address = (board->grom_address & 0x00ff) | ((UINT16)data<<8); |
| 208 | board->gromwaddr_LSB = TRUE; |
| 209 | } |
| 210 | } |
| 211 | else |
| 212 | { // write GPL data |
| 213 | // TODO: Check whether available in the real hardware. |
| 214 | board->grom_address = (board->grom_address + 1) & 0xffff; |
| 215 | board->gromraddr_LSB = board->gromwaddr_LSB = FALSE; |
| 216 | logerror("Geneve: GROM write ignored\n"); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | /**************************************************************************** |
| 221 | Mapper |
| 222 | *****************************************************************************/ |
| 223 | |
| 224 | READ8_DEVICE_HANDLER( geneve_r ) |
| 225 | { |
| 226 | genboard_state *board = get_safe_token(device); |
| 227 | UINT8 value = 0; |
| 228 | int page; |
| 229 | |
| 230 | // Premature access. The CPU reads the start vector before RESET. |
| 231 | if (board->peribox==NULL) return 0; |
| 232 | |
| 233 | if (!board->zero_wait) |
| 234 | cpu_adjust_icount(board->cpu, -4); |
| 235 | |
| 236 | UINT32 physaddr; |
| 237 | |
| 238 | if (board->geneve_mode) |
| 239 | { |
| 240 | // TODO: shortcut offset & 0xffc0 = 0xf100 |
| 241 | if ((offset & 0xfff5)==0xf100) |
| 242 | { |
| 243 | // video |
| 244 | // ++++ ++++ ++++ -+-+ |
| 245 | // 1111 0001 0000 0000 |
| 246 | // 1111 0001 0000 0010 |
| 247 | // 1111 0001 0000 1000 |
| 248 | // 1111 0001 0000 1010 |
| 249 | gen_v9938_rz(board->video, offset, &value); |
| 250 | return value; |
| 251 | } |
| 252 | if ((offset & 0xfff8)==0xf110) |
| 253 | { |
| 254 | // mapper |
| 255 | value = board->map[offset & 0x0007]; |
| 256 | return value; |
| 257 | } |
| 258 | if (offset == 0xf118) |
| 259 | { |
| 260 | // key |
| 261 | value = get_recent_key(device); |
| 262 | return value; |
| 263 | } |
| 264 | if ((offset & 0xfff0)==0xf130) |
| 265 | { |
| 266 | // clock |
| 267 | value = mm58274c_r(board->clock, offset & 0x00f); |
| 268 | return value; |
| 269 | } |
| 270 | } |
| 271 | else // TI mode |
| 272 | { |
| 273 | if ((offset & 0xfff8)==0x8000) |
| 274 | { |
| 275 | // mapper |
| 276 | value = board->map[offset & 0x0007]; |
| 277 | return value; |
| 278 | } |
| 279 | if (offset == 0x8008) |
| 280 | { |
| 281 | // key |
| 282 | value = get_recent_key(device); |
| 283 | return value; |
| 284 | } |
| 285 | if ((offset & 0xfff0)==0x8010) |
| 286 | { |
| 287 | // clock |
| 288 | value = mm58274c_r(board->clock, offset & 0x00f); |
| 289 | return value; |
| 290 | } |
| 291 | if ((offset & 0xfc01)==0x8800) |
| 292 | { |
| 293 | // video |
| 294 | // ++++ ++-- ---- ---+ |
| 295 | // 1000 1000 0000 00x0 |
| 296 | gen_v9938_rz(board->video, offset, &value); |
| 297 | return value; |
| 298 | } |
| 299 | if ((offset & 0xfc01)==0x9000) |
| 300 | { |
| 301 | // speech |
| 302 | // ++++ ++-- ---- ---+ |
| 303 | // 1001 0000 0000 0000 |
| 304 | // 0x17000 represents the proper AMA/B/C setting; will be masked away currently |
| 305 | ti99_peb_data_rz(board->peribox, offset | 0x17000, &value); |
| 306 | return value; |
| 307 | } |
| 308 | if ((offset & 0xfc01)==0x9800) |
| 309 | { |
| 310 | // grom simulation |
| 311 | // ++++ ++-- ---- ---+ |
| 312 | // 1001 1000 0000 00x0 |
| 313 | value = read_grom(device, offset); |
| 314 | return value; |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | page = (offset & 0xe000) >> 13; |
| 319 | |
| 320 | if (board->direct_mode) |
| 321 | { |
| 322 | physaddr = 0x1e0000; // points to boot eprom |
| 323 | } |
| 324 | else |
| 325 | { // mf_mapmode=0 (bit 11) |
| 326 | if (!board->geneve_mode && page==3) |
| 327 | { |
| 328 | // Cartridge paging in TI mode |
| 329 | // See also cartridge type "paged" in gromport.h |
| 330 | // value 0x36 = 0 0110 110x xxxx xxxx xxxx (page 1) |
| 331 | // value 0x37 = 0 0110 111x xxxx xxxx xxxx (page 2) |
| 332 | // Only use this if there are 2*8 KiB cartridge ROM |
| 333 | if (!board->cartridge_size_8K && board->cartridge_secondpage) physaddr = 0x06e000; |
| 334 | else physaddr = 0x06c000; |
| 335 | } |
| 336 | else |
| 337 | { |
| 338 | physaddr = (board->map[page] << 13); |
| 339 | } |
| 340 | } |
| 341 | |
| 342 | physaddr |= (offset & 0x1fff); |
| 343 | |
| 344 | if ((physaddr & 0x180000)==0x000000) |
| 345 | { |
| 346 | // DRAM. One wait state. |
| 347 | cpu_adjust_icount(board->cpu, -4); |
| 348 | value = board->dram[physaddr & 0x07ffff]; |
| 349 | return value; |
| 350 | } |
| 351 | if ((physaddr & 0x180000)==0x080000) |
| 352 | { |
| 353 | // Memex in PEB |
| 354 | // 0 1aaa xxxx xxxx xxxx xxxx |
| 355 | ti99_peb_data_rz(board->peribox, physaddr, &value); |
| 356 | return value; |
| 357 | } |
| 358 | if ((physaddr & 0x180000)==0x100000) |
| 359 | { |
| 360 | // 1 0aaa xxxx xxxx xxxx xxxx p-box address block |
| 361 | // Cards in PEB (aaa = AMC, AMB, AMA) |
| 362 | ti99_peb_data_rz(board->peribox, physaddr, &value); |
| 363 | return value; |
| 364 | } |
| 365 | if ((physaddr & 0x1f0000)==0x1d0000) |
| 366 | { |
| 367 | // 1 1101 xxxx xxxx xxxx xxxx on-board sram (64K) |
| 368 | value = board->sram[physaddr & 0x00ffff]; |
| 369 | return value; |
| 370 | } |
| 371 | if ((physaddr & 0x1e0000)==0x1e0000) |
| 372 | { |
| 373 | // 1 111. ..xx xxxx xxxx xxxx on-board eprom (16K) |
| 374 | // mirrored for f0, f2, f4, ...; f1, f3, f5, ... |
| 375 | value = board->eprom[physaddr & 0x003fff]; |
| 376 | return value; |
| 377 | } |
| 378 | |
| 379 | return 0; |
| 380 | } |
| 381 | /* |
| 382 | Geneve mode: |
| 383 | f100 / fff5: v9938_r |
| 384 | f110 / fff8: mapper_r |
| 385 | f118 / ffff: key_r |
| 386 | f130 / fff0: clock_r |
| 387 | |
| 388 | TI mode: |
| 389 | 8000 / fff8: mapper_r |
| 390 | 8008 / ffff: key_r |
| 391 | 8010 / fff0: clock_r |
| 392 | 8800 / fffd: v9938_r |
| 393 | 9000 / ffc0: speech_r |
| 394 | 9800 / ffc0: grom_r |
| 395 | |
| 396 | */ |
| 397 | |
| 398 | WRITE8_DEVICE_HANDLER( geneve_w ) |
| 399 | { |
| 400 | genboard_state *board = get_safe_token(device); |
| 401 | UINT32 physaddr; |
| 402 | int page; |
| 403 | |
| 404 | if (board->peribox==NULL) return; |
| 405 | |
| 406 | if (!board->zero_wait) |
| 407 | cpu_adjust_icount(board->cpu, -4); |
| 408 | |
| 409 | if (board->geneve_mode) |
| 410 | { |
| 411 | if ((offset & 0xfff1)==0xf100) |
| 412 | { |
| 413 | // video |
| 414 | // ++++ ++++ ++++ ---+ |
| 415 | // 1111 0001 0000 .cc0 |
| 416 | gen_v9938_w(board->video, offset, data); |
| 417 | return; |
| 418 | } |
| 419 | if ((offset & 0xfff8)==0xf110) |
| 420 | { |
| 421 | // mapper |
| 422 | board->map[offset & 0x0007] = data; |
| 423 | return; |
| 424 | } |
| 425 | if ((offset & 0xfff1)==0xf120) |
| 426 | { |
| 427 | // sound |
| 428 | // ++++ ++++ ++++ ---+ |
| 429 | sn76496_w(board->sound, 0, data); |
| 430 | return; |
| 431 | } |
| 432 | if ((offset & 0xfff0)==0xf130) |
| 433 | { |
| 434 | // clock |
| 435 | // ++++ ++++ ++++ ---- |
| 436 | mm58274c_w(board->clock, offset & 0x00f, data); |
| 437 | return; |
| 438 | } |
| 439 | } |
| 440 | else |
| 441 | { |
| 442 | // TI mode |
| 443 | if ((offset & 0xfff8)==0x8000) |
| 444 | { |
| 445 | // mapper |
| 446 | board->map[offset & 0x0007] = data; |
| 447 | return; |
| 448 | } |
| 449 | // No key write at 8008 |
| 450 | if ((offset & 0xfff0)==0x8010) |
| 451 | { |
| 452 | // clock |
| 453 | mm58274c_w(board->clock, offset & 0x00f, data); |
| 454 | return; |
| 455 | } |
| 456 | if ((offset & 0xfc01)==0x8400) |
| 457 | { |
| 458 | // sound |
| 459 | // ++++ ++-- ---- ---+ |
| 460 | // 1000 0100 0000 0000 |
| 461 | sn76496_w(board->sound, 0, data); |
| 462 | return; |
| 463 | } |
| 464 | if ((offset & 0xfc01)==0x8c00) |
| 465 | { |
| 466 | // video |
| 467 | // ++++ ++-- ---- ---+ |
| 468 | // 1000 1100 0000 00c0 |
| 469 | gen_v9938_w(board->video, offset, data); |
| 470 | return; |
| 471 | } |
| 472 | if ((offset & 0xfc01)==0x9400) |
| 473 | { |
| 474 | // speech |
| 475 | // ++++ ++-- ---- ---+ |
| 476 | // 1001 0100 0000 0000 |
| 477 | ti99_peb_data_w(board->peribox, offset | 0x17000, data); |
| 478 | return; |
| 479 | } |
| 480 | if ((offset & 0xfc01)==0x9c00) |
| 481 | { |
| 482 | // grom simulation |
| 483 | // ++++ ++-- ---- ---+ |
| 484 | // 1001 1100 0000 00c0 |
| 485 | write_grom(device, offset, data); |
| 486 | return; |
| 487 | } |
| 488 | } |
| 489 | |
| 490 | page = (offset & 0xe000) >> 13; |
| 491 | if (board->direct_mode) |
| 492 | { |
| 493 | physaddr = 0x1e0000; // points to boot eprom |
| 494 | } |
| 495 | else |
| 496 | { |
| 497 | if (!board->geneve_mode && page==3) |
| 498 | { |
| 499 | if (!board->cartridge_size_8K) |
| 500 | { |
| 501 | // Writing to 0x6000 selects page 1, |
| 502 | // writing to 0x6002 selects page 2 |
| 503 | board->cartridge_secondpage = ((offset & 0x0002)!=0); |
| 504 | return; |
| 505 | } |
| 506 | else |
| 507 | { |
| 508 | // writing into cartridge rom space (no bankswitching) |
| 509 | if ((((offset & 0x1000)==0x0000) && !board->cartridge6_writable) |
| 510 | || (((offset & 0x1000)==0x1000) && !board->cartridge7_writable)) |
| 511 | { |
| 512 | logerror("Geneve: Writing to protected cartridge space %04x ignored\n", offset); |
| 513 | return; |
| 514 | } |
| 515 | else |
| 516 | // TODO: Check whether secondpage is really ignored |
| 517 | physaddr = 0x06c000; |
| 518 | } |
| 519 | } |
| 520 | else |
| 521 | physaddr = (board->map[page] << 13); |
| 522 | } |
| 523 | |
| 524 | physaddr |= offset & 0x1fff; |
| 525 | |
| 526 | if ((physaddr & 0x180000)==0x000000) |
| 527 | { |
| 528 | // DRAM write. One wait state. |
| 529 | cpu_adjust_icount(board->cpu, -4); |
| 530 | board->dram[physaddr & 0x07ffff] = data; |
| 531 | return; |
| 532 | } |
| 533 | if ((physaddr & 0x180000)==0x080000) |
| 534 | { |
| 535 | // Memex in PEB |
| 536 | ti99_peb_data_w(board->peribox, physaddr, data); |
| 537 | return; |
| 538 | } |
| 539 | if ((physaddr & 0x180000)==0x100000) |
| 540 | { |
| 541 | // Classic cards in PEB |
| 542 | ti99_peb_data_w(board->peribox, physaddr, data); |
| 543 | return; |
| 544 | } |
| 545 | if ((physaddr & 0x1f0000)==0x1d0000) |
| 546 | { |
| 547 | // SRAM write |
| 548 | board->sram[physaddr & 0x00ffff] = data; |
| 549 | return; |
| 550 | } |
| 551 | // Ignore EPROM write |
| 552 | } |
| 553 | |
| 554 | /* |
| 555 | Geneve mode: |
| 556 | f100 / fff1: v9938_w |
| 557 | f110 / fff8: mapper_w |
| 558 | f120 / fff0: sound_w |
| 559 | f130 / fff0: clock_w |
| 560 | |
| 561 | TI mode: |
| 562 | 8000 / fff8: mapper_w |
| 563 | 8010 / fff0: clock_w |
| 564 | 8400 / fc00: sound_w |
| 565 | 8c00 / fff9: v9938_w |
| 566 | 9400 / fc00: speech_w |
| 567 | 9c00 / fffd: grom_w |
| 568 | |
| 569 | |
| 570 | Address: (max 2 MB) |
| 571 | b bbbb bbbc cccc cccc cccc |
| 572 | |
| 573 | 0 0... .... .... .... .... on-board dram 512 KiB |
| 574 | |
| 575 | 0 1... .... .... .... .... p-box optional memex 512 KiB |
| 576 | 1 00.. .... .... .... .... p-box AMA=0 (256 KiB) |
| 577 | 1 010. .... .... .... .... p-box AMA=1 AMB=0 (128 KiB) |
| 578 | 1 0110 .... .... .... .... p-box AMA=1 AMB=1 AMC=0 (64 KiB) |
| 579 | |
| 580 | 1 0111 00.. .... .... .... p-box address block 0xxx, 2xxx |
| 581 | 1 0111 010. .... .... .... p-box address block 4xxx (DSR) |
| 582 | 1 0111 011. .... .... .... p-box address block 6xxx |
| 583 | 1 0111 100. .... .... .... p-box address block 8xxx |
| 584 | 1 0111 101. .... .... .... p-box address block axxx |
| 585 | 1 0111 11.. .... .... .... p-box address block cxxx, exxx |
| 586 | |
| 587 | 1 1101 .... .... .... .... on-board sram (64K) |
| 588 | 1 111. ..0. .... .... .... on-board boot1 |
| 589 | 1 111. ..1. .... .... .... on-board boot2 |
| 590 | |
| 591 | |
| 592 | */ |
| 593 | |
| 594 | /**************************************************************************** |
| 595 | CRU handling |
| 596 | *****************************************************************************/ |
| 597 | |
| 598 | #define CRU_CONTROL_BASE 0x1ee0 |
| 599 | |
| 600 | WRITE8_DEVICE_HANDLER ( geneve_cru_w ) |
| 601 | { |
| 602 | genboard_state *board = get_safe_token(device); |
| 603 | |
| 604 | int addroff = (offset + 0x0800) << 1; |
| 605 | int rising_edge = FALSE; |
| 606 | int falling_edge = FALSE; |
| 607 | |
| 608 | if ((addroff & 0x1fe0) == CRU_CONTROL_BASE) |
| 609 | { |
| 610 | int bit = (addroff & 0x0001e)>>1; |
| 611 | switch (bit) |
| 612 | { |
| 613 | case 5: |
| 614 | board->palvideo = data; |
| 615 | break; |
| 616 | case 7: |
| 617 | board->capslock = data; |
| 618 | break; |
| 619 | case 8: |
| 620 | rising_edge = (!board->keyboard_clock && data); |
| 621 | board->keyboard_clock = data; |
| 622 | if (rising_edge) |
| 623 | read_key_if_possible(board); |
| 624 | break; |
| 625 | case 9: |
| 626 | rising_edge = (!board->keep_keybuf && data); |
| 627 | falling_edge = (board->keep_keybuf && !data); |
| 628 | board->keep_keybuf = data; |
| 629 | |
| 630 | if (rising_edge) |
| 631 | read_key_if_possible(board); |
| 632 | else |
| 633 | { |
| 634 | if (falling_edge) |
| 635 | { |
| 636 | if (board->keyQueueLen != 0) |
| 637 | { |
| 638 | board->keyQueueHead = (board->keyQueueHead + 1) % KEYQUEUESIZE; |
| 639 | board->keyQueueLen--; |
| 640 | } |
| 641 | /* clear keyboard interrupt */ |
| 642 | tms9901_set_single_int(board->tms9901, 8, 0); |
| 643 | board->keyInBuf = 0; |
| 644 | } |
| 645 | } |
| 646 | break; |
| 647 | case 10: |
| 648 | board->geneve_mode = data; |
| 649 | break; |
| 650 | case 11: |
| 651 | board->direct_mode = data; |
| 652 | break; |
| 653 | case 12: |
| 654 | board->cartridge_size_8K = data; |
| 655 | break; |
| 656 | case 13: |
| 657 | board->cartridge6_writable = data; |
| 658 | break; |
| 659 | case 14: |
| 660 | board->cartridge7_writable = data; |
| 661 | break; |
| 662 | case 15: |
| 663 | board->zero_wait = data; |
| 664 | break; |
| 665 | default: |
| 666 | logerror("geneve: set CRU address %04x=%02x ignored\n", addroff, data); |
| 667 | break; |
| 668 | } |
| 669 | } |
| 670 | else |
| 671 | ti99_peb_cru_w(board->peribox, addroff, data); |
| 672 | } |
| 673 | |
| 674 | |
| 675 | READ8_DEVICE_HANDLER ( geneve_cru_r ) |
| 676 | { |
| 677 | UINT8 value = 0; |
| 678 | genboard_state *board = get_safe_token(device); |
| 679 | int addroff = (offset + 0x0100) << 4; |
| 680 | |
| 681 | // TMS9995-internal CRU locations (1ee0-1efe) are handled within the 99xxcore |
| 682 | // implementation (read_single_cru), so we don't arrive here |
| 683 | ti99_peb_cru_rz(board->peribox, addroff, &value); |
| 684 | return value; |
| 685 | } |
| 686 | |
| 687 | /**************************************************************************** |
| 688 | Keyboard support |
| 689 | *****************************************************************************/ |
| 690 | |
| 691 | UINT8 get_recent_key(running_device *device) |
| 692 | { |
| 693 | genboard_state *board = get_safe_token(device); |
| 694 | if (board->keyInBuf) |
| 695 | return board->keyQueue[board->keyQueueHead]; |
| 696 | else |
| 697 | return 0; |
| 698 | } |
| 699 | |
| 700 | static void read_key_if_possible(genboard_state *board) |
| 701 | { |
| 702 | // if keyboard reset is not asserted, and key clock is enabled, and key |
| 703 | // buffer clear is disabled, and key queue is not empty. */ |
| 704 | if ((!board->keyReset) && (board->keyboard_clock) && (board->keep_keybuf) && (board->keyQueueLen != 0)) |
| 705 | { |
| 706 | tms9901_set_single_int(board->tms9901, 8, 1); |
| 707 | board->keyInBuf = 1; |
| 708 | } |
| 709 | } |
| 710 | |
| 711 | INLINE void post_in_keyQueue(genboard_state *board, int keycode) |
| 712 | { |
| 713 | board->keyQueue[(board->keyQueueHead + board->keyQueueLen) % KEYQUEUESIZE] = keycode; |
| 714 | board->keyQueueLen++; |
| 715 | } |
| 716 | |
| 717 | static void poll_keyboard(running_device *device) |
| 718 | { |
| 719 | genboard_state *board = get_safe_token(device); |
| 720 | |
| 721 | UINT32 keystate; |
| 722 | UINT32 key_transitions; |
| 723 | int i, j; |
| 724 | int keycode; |
| 725 | int pressed; |
| 726 | |
| 727 | if (board->keyReset) |
| 728 | return; |
| 729 | |
| 730 | /* Poll keyboard */ |
| 731 | for (i = 0; (i < 4) && (board->keyQueueLen <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)); i++) |
| 732 | { |
| 733 | keystate = input_port_read(device->machine, keynames[2*i]) | (input_port_read(device->machine, keynames[2*i + 1]) << 16); |
| 734 | key_transitions = keystate ^ board->keyStateSave[i]; |
| 735 | if (key_transitions) |
| 736 | { |
| 737 | for (j = 0; (j < 32) && (board->keyQueueLen <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)); j++) |
| 738 | { |
| 739 | if ((key_transitions >> j) & 1) |
| 740 | { |
| 741 | keycode = (i << 5) | j; |
| 742 | pressed = ((keystate >> j) & 1); |
| 743 | if (pressed) |
| 744 | board->keyStateSave[i] |= (1 << j); |
| 745 | else |
| 746 | board->keyStateSave[i] &= ~ (1 << j); |
| 747 | |
| 748 | /* Update auto-repeat */ |
| 749 | if (pressed) |
| 750 | { |
| 751 | board->keyAutoRepeatKey = keycode; |
| 752 | board->keyAutoRepeatTimer = KEYAUTOREPEATDELAY+1; |
| 753 | } |
| 754 | else /*if (keycode == board->keyAutoRepeatKey)*/ |
| 755 | board->keyAutoRepeatKey = 0; |
| 756 | |
| 757 | // Release Fake Shift/Unshift if another key is pressed |
| 758 | // We do so if a key is released, though it is actually |
| 759 | // required only if it is a modifier key |
| 760 | /*if (pressed)*/ |
| 761 | //{ |
| 762 | if (board->keyFakeShiftState) |
| 763 | { |
| 764 | /* Fake shift release */ |
| 765 | post_in_keyQueue(board, 0xe0); |
| 766 | post_in_keyQueue(board, 0xaa); |
| 767 | board->keyFakeShiftState = 0; |
| 768 | } |
| 769 | if (board->keyFakeUnshiftState) |
| 770 | { |
| 771 | /* Fake shift press */ |
| 772 | post_in_keyQueue(board, 0xe0); |
| 773 | post_in_keyQueue(board, 0x2a); |
| 774 | board->keyFakeUnshiftState = 0; |
| 775 | } |
| 776 | //} |
| 777 | |
| 778 | /* update shift and numlock state */ |
| 779 | if ((keycode == 0x2a) || (keycode == 0x36)) |
| 780 | board->keyRealShiftState = board->keyRealShiftState + (pressed ? +1 : -1); |
| 781 | if ((keycode == 0x1d) || (keycode == 0x61)) |
| 782 | board->keyCtrlState = board->keyCtrlState + (pressed ? +1 : -1); |
| 783 | if ((keycode == 0x38) || (keycode == 0x62)) |
| 784 | board->keyAltState = board->keyAltState + (pressed ? +1 : -1); |
| 785 | if ((keycode == 0x45) && pressed) |
| 786 | board->keyNumLockState = ! board->keyNumLockState; |
| 787 | |
| 788 | if ((keycode >= 0x60) && (keycode < 0x6e)) |
| 789 | { /* simpler extended keys */ |
| 790 | /* these keys are emulated */ |
| 791 | |
| 792 | if ((keycode >= 0x63) && pressed) |
| 793 | { |
| 794 | /* Handle shift state */ |
| 795 | if (keycode == 0x63) |
| 796 | { /* non-shifted key */ |
| 797 | if (board->keyRealShiftState) |
| 798 | /* Fake shift unpress */ |
| 799 | board->keyFakeUnshiftState = 1; |
| 800 | } |
| 801 | else /*if (keycode >= 0x64)*/ |
| 802 | { /* non-numlock mode key */ |
| 803 | if (board->keyNumLockState & ! board->keyRealShiftState) |
| 804 | /* Fake shift press if numlock is active */ |
| 805 | board->keyFakeShiftState = 1; |
| 806 | else if ((! board->keyNumLockState) & board->keyRealShiftState) |
| 807 | /* Fake shift unpress if shift is down */ |
| 808 | board->keyFakeUnshiftState = 1; |
| 809 | } |
| 810 | |
| 811 | if (board->keyFakeShiftState) |
| 812 | { |
| 813 | post_in_keyQueue(board, 0xe0); |
| 814 | post_in_keyQueue(board, 0x2a); |
| 815 | } |
| 816 | |
| 817 | if (board->keyFakeUnshiftState) |
| 818 | { |
| 819 | post_in_keyQueue(board, 0xe0); |
| 820 | post_in_keyQueue(board, 0xaa); |
| 821 | } |
| 822 | } |
| 823 | |
| 824 | keycode = keyboard_mf1_code[keycode-0x60]; |
| 825 | if (! pressed) |
| 826 | keycode |= 0x80; |
| 827 | post_in_keyQueue(board, 0xe0); |
| 828 | post_in_keyQueue(board, keycode); |
| 829 | } |
| 830 | else if (keycode == 0x6e) |
| 831 | { /* emulate Print Screen / System Request (F13) key */ |
| 832 | /* this is a bit complex, as Alt+PrtScr -> SysRq */ |
| 833 | /* Additionally, Ctrl+PrtScr involves no fake shift press */ |
| 834 | if (board->keyAltState) |
| 835 | { |
| 836 | /* SysRq */ |
| 837 | keycode = 0x54; |
| 838 | if (! pressed) |
| 839 | keycode |= 0x80; |
| 840 | post_in_keyQueue(board, keycode); |
| 841 | } |
| 842 | else |
| 843 | { |
| 844 | /* Handle shift state */ |
| 845 | if (pressed && (! board->keyRealShiftState) && (! board->keyCtrlState)) |
| 846 | { /* Fake shift press */ |
| 847 | post_in_keyQueue(board, 0xe0); |
| 848 | post_in_keyQueue(board, 0x2a); |
| 849 | board->keyFakeShiftState = 1; |
| 850 | } |
| 851 | |
| 852 | keycode = 0x37; |
| 853 | if (! pressed) |
| 854 | keycode |= 0x80; |
| 855 | post_in_keyQueue(board, 0xe0); |
| 856 | post_in_keyQueue(board, keycode); |
| 857 | } |
| 858 | } |
| 859 | else if (keycode == 0x6f) |
| 860 | { // emulate pause (F15) key |
| 861 | // this is a bit complex, as Pause -> Ctrl+NumLock and |
| 862 | // Ctrl+Pause -> Ctrl+ScrLock. Furthermore, there is no |
| 863 | // repeat or release. |
| 864 | if (pressed) |
| 865 | { |
| 866 | if (board->keyCtrlState) |
| 867 | { |
| 868 | post_in_keyQueue(board, 0xe0); |
| 869 | post_in_keyQueue(board, 0x46); |
| 870 | post_in_keyQueue(board, 0xe0); |
| 871 | post_in_keyQueue(board, 0xc6); |
| 872 | } |
| 873 | else |
| 874 | { |
| 875 | post_in_keyQueue(board, 0xe1); |
| 876 | post_in_keyQueue(board, 0x1d); |
| 877 | post_in_keyQueue(board, 0x45); |
| 878 | post_in_keyQueue(board, 0xe1); |
| 879 | post_in_keyQueue(board, 0x9d); |
| 880 | post_in_keyQueue(board, 0xc5); |
| 881 | } |
| 882 | } |
| 883 | } |
| 884 | else |
| 885 | { |
| 886 | if (! pressed) |
| 887 | keycode |= 0x80; |
| 888 | post_in_keyQueue(board, keycode); |
| 889 | } |
| 890 | read_key_if_possible(board); |
| 891 | } |
| 892 | } |
| 893 | } |
| 894 | } |
| 895 | |
| 896 | /* Handle auto-repeat */ |
| 897 | if ((board->keyQueueLen <= (KEYQUEUESIZE-MAXKEYMSGLENGTH)) && board->keyAutoRepeatKey && (--board->keyAutoRepeatTimer == 0)) |
| 898 | { |
| 899 | if ((board->keyAutoRepeatKey >= 0x60) && (board->keyAutoRepeatKey < 0x6e)) |
| 900 | { |
| 901 | post_in_keyQueue(board, 0xe0); |
| 902 | post_in_keyQueue(board, keyboard_mf1_code[board->keyAutoRepeatKey-0x60]); |
| 903 | } |
| 904 | else if (board->keyAutoRepeatKey == 0x6e) |
| 905 | { |
| 906 | if (board->keyAltState) |
| 907 | post_in_keyQueue(board, 0x54); |
| 908 | else |
| 909 | { |
| 910 | post_in_keyQueue(board, 0xe0); |
| 911 | post_in_keyQueue(board, 0x37); |
| 912 | } |
| 913 | } |
| 914 | else if (board->keyAutoRepeatKey == 0x6f) |
| 915 | ; |
| 916 | else |
| 917 | { |
| 918 | post_in_keyQueue(board, board->keyAutoRepeatKey); |
| 919 | } |
| 920 | read_key_if_possible(board); |
| 921 | board->keyAutoRepeatTimer = KEYAUTOREPEATRATE; |
| 922 | } |
| 923 | } |
| 924 | |
| 925 | /**************************************************************************** |
| 926 | Mouse support |
| 927 | ****************************************************************************/ |
| 928 | |
| 929 | static void poll_mouse(running_device *device) |
| 930 | { |
| 931 | genboard_state *board = get_safe_token(device); |
| 932 | |
| 933 | int new_mx, new_my; |
| 934 | int delta_x, delta_y, buttons; |
| 935 | |
| 936 | buttons = input_port_read(device->machine, "MOUSE0"); |
| 937 | new_mx = input_port_read(device->machine, "MOUSEX"); |
| 938 | new_my = input_port_read(device->machine, "MOUSEY"); |
| 939 | |
| 940 | /* compute x delta */ |
| 941 | delta_x = new_mx - board->last_mx; |
| 942 | |
| 943 | /* check for wrap */ |
| 944 | if (delta_x > 0x80) |
| 945 | delta_x = 0x100-delta_x; |
| 946 | if (delta_x < -0x80) |
| 947 | delta_x = -0x100-delta_x; |
| 948 | |
| 949 | board->last_mx = new_mx; |
| 950 | |
| 951 | /* compute y delta */ |
| 952 | delta_y = new_my - board->last_my; |
| 953 | |
| 954 | /* check for wrap */ |
| 955 | if (delta_y > 0x80) |
| 956 | delta_y = 0x100-delta_y; |
| 957 | if (delta_y < -0x80) |
| 958 | delta_y = -0x100-delta_y; |
| 959 | |
| 960 | board->last_my = new_my; |
| 961 | |
| 962 | video_update_mouse(board->video, delta_x, delta_y, buttons & 3); |
| 963 | } |
| 964 | |
| 965 | /**************************************************************************** |
| 966 | TMS9901 and attached devices handling |
| 967 | ****************************************************************************/ |
| 968 | |
| 969 | /* |
| 970 | Called by the 9901 core whenever the state of INTREQ and IC0-3 changes |
| 971 | */ |
| 972 | static TMS9901_INT_CALLBACK( tms9901_interrupt_callback ) |
| 973 | { |
| 974 | /* INTREQ is connected to INT1 (IC0-3 are not connected) */ |
| 975 | cputag_set_input_line(device->machine, "maincpu", 0, intreq? ASSERT_LINE : CLEAR_LINE); |
| 976 | } |
| 977 | |
| 978 | /* |
| 979 | Read pins INT3*-INT7* of Geneve 9901. |
| 980 | |
| 981 | signification: |
| 982 | (bit 1: INT1 status) |
| 983 | (bit 2: INT2 status) |
| 984 | bit 3-7: joystick status |
| 985 | */ |
| 986 | static READ8_DEVICE_HANDLER( R9901_0 ) |
| 987 | { |
| 988 | running_device *dev = device->machine->device("geneve_board"); |
| 989 | genboard_state *board = get_safe_token(dev); |
| 990 | int answer; |
| 991 | answer = input_port_read(device->machine, "JOY") >> (board->joySel * 8); |
| 992 | return answer; |
| 993 | } |
| 994 | |
| 995 | /* |
| 996 | Read pins INT8*-INT15* of Geneve 9901. |
| 997 | |
| 998 | signification: |
| 999 | (bit 0: keyboard interrupt) |
| 1000 | bit 1: unused |
| 1001 | bit 2: mouse right button |
| 1002 | (bit 3: clock interrupt) |
| 1003 | (bit 4: INTB from PE-bus) |
| 1004 | bit 5 & 7: used as output |
| 1005 | bit 6: unused |
| 1006 | */ |
| 1007 | static READ8_DEVICE_HANDLER( R9901_1 ) |
| 1008 | { |
| 1009 | int answer; |
| 1010 | answer = (input_port_read(device->machine, "MOUSE0") & 4) ^ 4; |
| 1011 | return answer; |
| 1012 | } |
| 1013 | |
| 1014 | /* |
| 1015 | Read pins P0-P7 of Geneve 9901. |
| 1016 | */ |
| 1017 | static READ8_DEVICE_HANDLER( R9901_2 ) |
| 1018 | { |
| 1019 | return 0; |
| 1020 | } |
| 1021 | |
| 1022 | /* |
| 1023 | Read pins P8-P15 of Geneve 9901. |
| 1024 | bit 4: mouse right button |
| 1025 | */ |
| 1026 | static READ8_DEVICE_HANDLER( R9901_3 ) |
| 1027 | { |
| 1028 | int answer = 0; |
| 1029 | |
| 1030 | if (! (input_port_read(device->machine, "MOUSE0") & 4)) |
| 1031 | answer |= 0x10; |
| 1032 | |
| 1033 | return answer; |
| 1034 | } |
| 1035 | |
| 1036 | /* |
| 1037 | Write PE bus reset line |
| 1038 | */ |
| 1039 | static WRITE8_DEVICE_HANDLER( W9901_PE_bus_reset ) |
| 1040 | { |
| 1041 | } |
| 1042 | |
| 1043 | /* |
| 1044 | Write VDP reset line |
| 1045 | */ |
| 1046 | static WRITE8_DEVICE_HANDLER( W9901_VDP_reset ) |
| 1047 | { |
| 1048 | } |
| 1049 | |
| 1050 | /* |
| 1051 | Write joystick select line |
| 1052 | */ |
| 1053 | static WRITE8_DEVICE_HANDLER( W9901_joySel ) |
| 1054 | { |
| 1055 | running_device *dev = device->machine->device("geneve_board"); |
| 1056 | genboard_state *board = get_safe_token(dev); |
| 1057 | board->joySel = data; |
| 1058 | } |
| 1059 | |
| 1060 | static WRITE8_DEVICE_HANDLER( W9901_keyboardReset ) |
| 1061 | { |
| 1062 | running_device *dev = device->machine->device("geneve_board"); |
| 1063 | genboard_state *board = get_safe_token(dev); |
| 1064 | |
| 1065 | board->keyReset = !data; |
| 1066 | |
| 1067 | if (board->keyReset) |
| 1068 | { |
| 1069 | /* reset -> clear keyboard key queue, but not geneve key buffer */ |
| 1070 | board->keyQueueLen = board->keyInBuf ? 1 : 0; |
| 1071 | memset(board->keyStateSave, 0, sizeof(board->keyStateSave)); |
| 1072 | board->keyNumLockState = 0; |
| 1073 | board->keyCtrlState = 0; |
| 1074 | board->keyAltState = 0; |
| 1075 | board->keyRealShiftState = 0; |
| 1076 | board->keyFakeShiftState = 0; |
| 1077 | board->keyFakeUnshiftState = 0; |
| 1078 | board->keyAutoRepeatKey = 0; |
| 1079 | } |
| 1080 | /*else |
| 1081 | poll_keyboard(space->machine);*/ |
| 1082 | } |
| 1083 | |
| 1084 | /* |
| 1085 | Write external mem cycles (0=long, 1=short) |
| 1086 | */ |
| 1087 | static WRITE8_DEVICE_HANDLER( W9901_ext_mem_wait_states ) |
| 1088 | { |
| 1089 | } |
| 1090 | |
| 1091 | /* |
| 1092 | Write vdp wait cycles (1=add 15 cycles, 0=add none) |
| 1093 | */ |
| 1094 | static WRITE8_DEVICE_HANDLER( W9901_VDP_wait_states ) |
| 1095 | { |
| 1096 | } |
| 1097 | |
| 1098 | /* tms9901 setup */ |
| 1099 | const tms9901_interface tms9901_wiring_geneve = |
| 1100 | { |
| 1101 | TMS9901_INT1 | TMS9901_INT2 | TMS9901_INT8 | TMS9901_INTB | TMS9901_INTC, /* only input pins whose state is always known */ |
| 1102 | |
| 1103 | { /* read handlers */ |
| 1104 | R9901_0, // INTA, VDPint, Joystick |
| 1105 | R9901_1, // Keyb, Mouse, Clock, INTB |
| 1106 | R9901_2, // - |
| 1107 | R9901_3 // Mouse right button |
| 1108 | }, |
| 1109 | |
| 1110 | { /* write handlers */ |
| 1111 | W9901_PE_bus_reset, |
| 1112 | W9901_VDP_reset, |
| 1113 | W9901_joySel, |
| 1114 | NULL, |
| 1115 | NULL, |
| 1116 | NULL, |
| 1117 | W9901_keyboardReset, |
| 1118 | W9901_ext_mem_wait_states, |
| 1119 | NULL, |
| 1120 | W9901_VDP_wait_states, |
| 1121 | NULL, |
| 1122 | NULL, |
| 1123 | NULL, |
| 1124 | NULL, |
| 1125 | NULL, |
| 1126 | NULL |
| 1127 | }, |
| 1128 | |
| 1129 | /* interrupt handler */ |
| 1130 | tms9901_interrupt_callback, |
| 1131 | |
| 1132 | /* clock rate = 3MHz */ |
| 1133 | 3000000. |
| 1134 | }; |
| 1135 | |
| 1136 | /* |
| 1137 | inta is connected to both tms9901 IRQ1 line and to tms9995 INT4/EC line. |
| 1138 | */ |
| 1139 | WRITE_LINE_DEVICE_HANDLER( board_inta ) |
| 1140 | { |
| 1141 | tms9901_set_single_int(device->machine->device("tms9901"), 1, state); |
| 1142 | cputag_set_input_line(device->machine, "maincpu", 1, state ? ASSERT_LINE : CLEAR_LINE); |
| 1143 | } |
| 1144 | |
| 1145 | /* |
| 1146 | intb is connected to tms9901 IRQ12 line. |
| 1147 | */ |
| 1148 | WRITE_LINE_DEVICE_HANDLER( board_intb ) |
| 1149 | { |
| 1150 | tms9901_set_single_int(device->machine->device("tms9901"), 12, state); |
| 1151 | |
| 1152 | } |
| 1153 | |
| 1154 | WRITE_LINE_DEVICE_HANDLER( board_ready ) |
| 1155 | { |
| 1156 | logerror("READY line set ... not yet connected, level=%02x\n", state); |
| 1157 | } |
| 1158 | |
| 1159 | /* |
| 1160 | set the state of int2 (called by the v9938 core) |
| 1161 | */ |
| 1162 | void tms9901_gen_set_int2(running_machine *machine, int state) |
| 1163 | { |
| 1164 | tms9901_set_single_int(machine->device("tms9901"), 2, state); |
| 1165 | } |
| 1166 | |
| 1167 | /*************************************************************************** |
| 1168 | DEVICE LIFECYCLE FUNCTIONS |
| 1169 | ***************************************************************************/ |
| 1170 | |
| 1171 | static const mm58274c_interface geneve_mm58274c_interface = |
| 1172 | { |
| 1173 | 1, /* mode 24*/ |
| 1174 | 0 /* first day of week */ |
| 1175 | }; |
| 1176 | |
| 1177 | static MACHINE_CONFIG_FRAGMENT( genboard ) |
| 1178 | MDRV_MM58274C_ADD("mm58274c", geneve_mm58274c_interface) |
| 1179 | MACHINE_CONFIG_END |
| 1180 | |
| 1181 | static DEVICE_START( genboard ) |
| 1182 | { |
| 1183 | logerror("Starting Geneve board\n"); |
| 1184 | genboard_state *board = get_safe_token(device); |
| 1185 | board->video = device->siblingdevice("video"); |
| 1186 | board->tms9901 = device->siblingdevice("tms9901"); |
| 1187 | board->peribox = device->siblingdevice("peribox"); |
| 1188 | board->cpu = device->siblingdevice("maincpu"); |
| 1189 | board->sound = device->siblingdevice("soundgen"); |
| 1190 | |
| 1191 | board->clock = device->subdevice("mm58274c"); |
| 1192 | |
| 1193 | board->sram = (UINT8*)malloc(SRAM_SIZE); |
| 1194 | board->dram = (UINT8*)malloc(DRAM_SIZE); |
| 1195 | board->eprom = memory_region(device->machine, "maincpu"); |
| 1196 | assert(board->video && board->tms9901); |
| 1197 | assert(board->peribox && board->cpu); |
| 1198 | assert(board->sound && board->clock); |
| 1199 | assert(board->eprom); |
| 1200 | board->geneve_mode = 0; |
| 1201 | board->direct_mode = 1; |
| 1202 | |
| 1203 | memset(board->sram, 0x00, SRAM_SIZE); |
| 1204 | memset(board->dram, 0x00, DRAM_SIZE); |
| 1205 | } |
| 1206 | |
| 1207 | static DEVICE_STOP( genboard ) |
| 1208 | { |
| 1209 | logerror("Stopping Geneve board\n"); |
| 1210 | genboard_state *board = get_safe_token(device); |
| 1211 | free(board->sram); |
| 1212 | free(board->dram); |
| 1213 | } |
| 1214 | |
| 1215 | static DEVICE_RESET( genboard ) |
| 1216 | { |
| 1217 | genboard_state *board = get_safe_token(device); |
| 1218 | logerror("Resetting Geneve board\n"); |
| 1219 | board->joySel = 0; |
| 1220 | board->keyQueueHead = board->keyQueueLen = 0; |
| 1221 | memset(board->keyStateSave, 0, sizeof(board->keyStateSave)); |
| 1222 | board->keyNumLockState = 0; |
| 1223 | board->keyCtrlState = 0; |
| 1224 | board->keyAltState = 0; |
| 1225 | board->keyRealShiftState = 0; |
| 1226 | board->keyFakeShiftState = 0; |
| 1227 | board->keyFakeUnshiftState = 0; |
| 1228 | board->keyAutoRepeatKey = 0; |
| 1229 | board->keyInBuf = 0; |
| 1230 | board->keyReset = 1; |
| 1231 | board->last_mx = 0; |
| 1232 | board->last_my = 0; |
| 1233 | board->line_count = 0; |
| 1234 | |
| 1235 | board->palvideo = 0; |
| 1236 | board->capslock = 0; |
| 1237 | board->keyboard_clock = 0; |
| 1238 | board->keep_keybuf = 0; |
| 1239 | board->zero_wait = 1; |
| 1240 | |
| 1241 | board->geneve_mode = 0; |
| 1242 | board->direct_mode = 1; |
| 1243 | board->cartridge_size_8K = 0; |
| 1244 | board->cartridge_secondpage = 0; |
| 1245 | board->cartridge6_writable = 0; |
| 1246 | board->cartridge7_writable = 0; |
| 1247 | for (int i=0; i < 8; i++) board->map[i] = 0; |
| 1248 | |
| 1249 | if (input_port_read(device->machine, "BOOTROM")==0) |
| 1250 | { |
| 1251 | board->eprom = memory_region(device->machine, "maincpu") + offset_altrom_geneve; |
| 1252 | } |
| 1253 | } |
| 1254 | |
| 1255 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 1256 | |
| 1257 | #define DEVTEMPLATE_ID(p,s) p##genboard##s |
| 1258 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_MACHINE_CONFIG |
| 1259 | #define DEVTEMPLATE_NAME "Geneve system board" |
| 1260 | #define DEVTEMPLATE_FAMILY "Internal component" |
| 1261 | #include "devtempl.h" |
| 1262 | |
| 1263 | DEFINE_LEGACY_DEVICE( GENBOARD, genboard ); |
src/mess/machine/ti99/sgcpu.c
| r0 | r9485 | |
| 1 | /*************************************************************************** |
| 2 | |
| 3 | SGCPU board |
| 4 | including decoder/mapper |
| 5 | |
| 6 | This component implements the address decoder and mapper logic from the |
| 7 | SGCPU expansion card (inofficially called TI-99/4P). |
| 8 | |
| 9 | Michael Zapf, October 2010 |
| 10 | |
| 11 | TODO: Check for wait states |
| 12 | ***************************************************************************/ |
| 13 | |
| 14 | #include "emu.h" |
| 15 | #include "peribox.h" |
| 16 | #include "sgcpu.h" |
| 17 | #include "videowrp.h" |
| 18 | #include "sound/sn76496.h" |
| 19 | #include "devices/cassette.h" |
| 20 | |
| 21 | typedef struct _sgcpu_state |
| 22 | { |
| 23 | /* Pointer to ROM0 */ |
| 24 | UINT16 *rom0; |
| 25 | |
| 26 | /* Pointer to DSR ROM */ |
| 27 | UINT16 *dsr; |
| 28 | |
| 29 | /* Pointer to ROM6, first bank */ |
| 30 | UINT16 *rom6a; |
| 31 | |
| 32 | /* Pointer to ROM6, second bank */ |
| 33 | UINT16 *rom6b; |
| 34 | |
| 35 | /* AMS RAM (1 Mib) */ |
| 36 | UINT16 *ram; |
| 37 | |
| 38 | /* Scratch pad ram (1 KiB) */ |
| 39 | UINT16 *scratchpad; |
| 40 | |
| 41 | /* True if SGCPU DSR is enabled */ |
| 42 | int internal_dsr; |
| 43 | |
| 44 | /* True if SGCPU rom6 is enabled */ |
| 45 | int internal_rom6; |
| 46 | |
| 47 | /* Offset to the ROM6 bank. */ |
| 48 | int rom6_bank; |
| 49 | |
| 50 | /* TRUE when mapper is active */ |
| 51 | int map_mode; |
| 52 | |
| 53 | /* TRUE when mapper registers are accessible */ |
| 54 | int access_mapper; |
| 55 | |
| 56 | UINT8 lowbyte; |
| 57 | UINT8 highbyte; |
| 58 | UINT8 latch; |
| 59 | |
| 60 | /* Mapper registers */ |
| 61 | UINT8 mapper[16]; |
| 62 | |
| 63 | running_device *cpu; |
| 64 | running_device *soundchip; |
| 65 | running_device *video; |
| 66 | running_device *cassette; |
| 67 | running_device *peribox; |
| 68 | |
| 69 | int keyCol; |
| 70 | int alphaLockLine; |
| 71 | |
| 72 | } sgcpu_state; |
| 73 | |
| 74 | |
| 75 | INLINE sgcpu_state *get_safe_token(running_device *device) |
| 76 | { |
| 77 | assert(device != NULL); |
| 78 | assert(downcast<legacy_device_base *>(device)->token() != NULL); |
| 79 | return (sgcpu_state *)downcast<legacy_device_base *>(device)->token(); |
| 80 | } |
| 81 | |
| 82 | |
| 83 | /*************************************************************************** |
| 84 | AMS Memory implementation |
| 85 | ***************************************************************************/ |
| 86 | |
| 87 | #define MAP_CRU_BASE 0x0f00 |
| 88 | #define SAMS_CRU_BASE 0x1e00 |
| 89 | |
| 90 | /* |
| 91 | CRU write (there is no read here) |
| 92 | */ |
| 93 | WRITE8_DEVICE_HANDLER( sgcpu_cru_w ) |
| 94 | { |
| 95 | sgcpu_state *sgcpu = get_safe_token(device); |
| 96 | int addroff = (offset + 0x0400)<<1; |
| 97 | |
| 98 | if ((addroff & 0xff00)==MAP_CRU_BASE) |
| 99 | { |
| 100 | if ((addroff & 0x000e)==0) sgcpu->internal_dsr = data; |
| 101 | if ((addroff & 0x000e)==2) sgcpu->internal_rom6 = data; |
| 102 | if ((addroff & 0x000e)==4) |
| 103 | { |
| 104 | ti99_peb_senila(sgcpu->peribox, data); |
| 105 | } |
| 106 | if ((addroff & 0x000e)==6) |
| 107 | { |
| 108 | ti99_peb_senilb(sgcpu->peribox, data); |
| 109 | } |
| 110 | // TODO: more CRU bits? 8=Fast timing / a=KBENA |
| 111 | return; |
| 112 | } |
| 113 | if ((addroff & 0xff00)==SAMS_CRU_BASE) |
| 114 | { |
| 115 | if ((addroff & 0x000e)==0) sgcpu->access_mapper = data; |
| 116 | if ((addroff & 0x000e)==2) sgcpu->map_mode = data; |
| 117 | return; |
| 118 | } |
| 119 | |
| 120 | // No match - pass to peribox |
| 121 | ti99_peb_cru_w(sgcpu->peribox, addroff, data); |
| 122 | } |
| 123 | |
| 124 | READ8_DEVICE_HANDLER( sgcpu_cru_r ) |
| 125 | { |
| 126 | sgcpu_state *sgcpu = get_safe_token(device); |
| 127 | UINT8 value = 0; |
| 128 | int addroff = (offset + 0x0080)<<4; |
| 129 | |
| 130 | ti99_peb_cru_rz(sgcpu->peribox, addroff, &value); |
| 131 | return value; |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | Memory read. The SAMS card has two address areas: The memory is at locations |
| 136 | 0x2000-0x3fff and 0xa000-0xffff, and the mapper area is at 0x4000-0x401e |
| 137 | (only even addresses). |
| 138 | */ |
| 139 | static READ16_DEVICE_HANDLER( sgcpu_samsmem_r ) |
| 140 | { |
| 141 | sgcpu_state *sgcpu = get_safe_token(device); |
| 142 | UINT32 address = 0; |
| 143 | int addroff = offset << 1; |
| 144 | |
| 145 | // select memory expansion |
| 146 | if (sgcpu->map_mode) |
| 147 | address = (sgcpu->mapper[(addroff>>12) & 0x000f] << 12) + (addroff & 0x0fff); |
| 148 | else // transparent mode |
| 149 | address = addroff; |
| 150 | |
| 151 | return sgcpu->ram[address>>1]; |
| 152 | } |
| 153 | |
| 154 | /* |
| 155 | Memory write |
| 156 | */ |
| 157 | static WRITE16_DEVICE_HANDLER( sgcpu_samsmem_w ) |
| 158 | { |
| 159 | sgcpu_state *sgcpu = get_safe_token(device); |
| 160 | UINT32 address = 0; |
| 161 | int addroff = offset << 1; |
| 162 | |
| 163 | // select memory expansion |
| 164 | if (sgcpu->map_mode) |
| 165 | address = (sgcpu->mapper[(addroff>>12) & 0x000f] << 12) + (addroff & 0x0fff); |
| 166 | else // transparent mode |
| 167 | address = addroff; |
| 168 | |
| 169 | COMBINE_DATA(&sgcpu->ram[address>>1]); |
| 170 | } |
| 171 | |
| 172 | /*************************************************************************** |
| 173 | Internal datamux; similar to TI-99/4A |
| 174 | ***************************************************************************/ |
| 175 | |
| 176 | static READ16_DEVICE_HANDLER( sgcpu_datamux_r ) |
| 177 | { |
| 178 | sgcpu_state *sgcpu = get_safe_token(device); |
| 179 | |
| 180 | UINT8 hbyte = 0; |
| 181 | UINT16 addroff = (offset << 1); |
| 182 | |
| 183 | ti99_peb_data_rz(sgcpu->peribox, addroff+1, &sgcpu->latch); |
| 184 | sgcpu->lowbyte = sgcpu->latch; |
| 185 | |
| 186 | // Takes three cycles |
| 187 | cpu_adjust_icount(sgcpu->cpu, -3); |
| 188 | |
| 189 | ti99_peb_data_rz(sgcpu->peribox, addroff, &hbyte); |
| 190 | sgcpu->highbyte = hbyte; |
| 191 | |
| 192 | // Takes three cycles |
| 193 | cpu_adjust_icount(sgcpu->cpu, -3); |
| 194 | |
| 195 | // use the latch and the currently read byte and put it on the 16bit bus |
| 196 | // printf("read address = %04x, value = %04x, memmask = %4x\n", addroff, (hbyte<<8) | sgcpu->latch, mem_mask); |
| 197 | return (hbyte<<8) | sgcpu->latch ; |
| 198 | } |
| 199 | |
| 200 | /* |
| 201 | Write access. |
| 202 | TODO: use the 16-bit expansion in the box for suitable cards |
| 203 | */ |
| 204 | static WRITE16_DEVICE_HANDLER( sgcpu_datamux_w ) |
| 205 | { |
| 206 | sgcpu_state *sgcpu = get_safe_token(device); |
| 207 | UINT16 addroff = (offset << 1); |
| 208 | // printf("write address = %04x, value = %04x, memmask = %4x\n", addroff, data, mem_mask); |
| 209 | |
| 210 | // read more about the datamux in datamux.c |
| 211 | |
| 212 | // byte-only transfer, high byte |
| 213 | // we use the previously read low byte to complete |
| 214 | if (mem_mask == 0xff00) |
| 215 | data = data | sgcpu->lowbyte; |
| 216 | |
| 217 | // byte-only transfer, low byte |
| 218 | // we use the previously read high byte to complete |
| 219 | if (mem_mask == 0x00ff) |
| 220 | data = data | (sgcpu->highbyte << 8); |
| 221 | |
| 222 | // Write to the PEB |
| 223 | ti99_peb_data_w(sgcpu->peribox, addroff+1, data & 0xff); |
| 224 | |
| 225 | // Takes three cycles |
| 226 | cpu_adjust_icount(sgcpu->cpu,-3); |
| 227 | |
| 228 | // Write to the PEB |
| 229 | ti99_peb_data_w(sgcpu->peribox, addroff, (data>>8) & 0xff); |
| 230 | |
| 231 | // Takes three cycles |
| 232 | cpu_adjust_icount(sgcpu->cpu,-3); |
| 233 | } |
| 234 | |
| 235 | /* |
| 236 | Read access. |
| 237 | */ |
| 238 | READ16_DEVICE_HANDLER( sgcpu_r ) |
| 239 | { |
| 240 | sgcpu_state *sgcpu = get_safe_token(device); |
| 241 | if (sgcpu->cpu == NULL) return 0; |
| 242 | |
| 243 | int addroff = offset << 1; |
| 244 | UINT16 zone = addroff & 0xe000; |
| 245 | UINT16 value = 0; |
| 246 | |
| 247 | if (zone==0x0000) |
| 248 | { |
| 249 | // ROM0 |
| 250 | value = sgcpu->rom0[(addroff & 0x1fff)>>1]; |
| 251 | return value; |
| 252 | } |
| 253 | if (zone==0x2000 || zone==0xa000 || zone==0xc000 || zone==0xe000) |
| 254 | { |
| 255 | value = sgcpu_samsmem_r(device, offset, mem_mask); |
| 256 | return value; |
| 257 | } |
| 258 | |
| 259 | if (zone==0x4000) |
| 260 | { |
| 261 | if (sgcpu->internal_dsr) |
| 262 | { |
| 263 | value = sgcpu->dsr[(addroff & 0x1fff)>>1]; |
| 264 | return value; |
| 265 | } |
| 266 | else |
| 267 | { |
| 268 | if (sgcpu->access_mapper && ((addroff & 0xffe0)==0x4000)) |
| 269 | { |
| 270 | value = sgcpu->mapper[offset & 0x000f]<<8; |
| 271 | return value; |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | if (zone==0x6000 && sgcpu->internal_rom6) |
| 277 | { |
| 278 | if (sgcpu->rom6_bank==0) |
| 279 | value = sgcpu->rom6a[(addroff & 0x1fff)>>1]; |
| 280 | else |
| 281 | value = sgcpu->rom6b[(addroff & 0x1fff)>>1]; |
| 282 | |
| 283 | return value; |
| 284 | } |
| 285 | |
| 286 | // Scratch pad RAM and sound |
| 287 | // speech is in peribox |
| 288 | // groms are in hsgpl in peribox |
| 289 | if (zone==0x8000) |
| 290 | { |
| 291 | if ((addroff & 0xfff0)==0x8400) |
| 292 | { |
| 293 | value = 0; |
| 294 | return value; |
| 295 | } |
| 296 | if ((addroff & 0xfc00)==0x8000) |
| 297 | { |
| 298 | value = sgcpu->scratchpad[(addroff & 0x03ff)>>1]; |
| 299 | return value; |
| 300 | } |
| 301 | // Video: 8800, 8802 |
| 302 | if ((addroff & 0xfffd)==0x8800) |
| 303 | { |
| 304 | value = ti_v9938_r16(sgcpu->video, offset, mem_mask); |
| 305 | return value; |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | // If we are here, check the peribox via the datamux |
| 310 | // catch-all for unmapped zones |
| 311 | value = sgcpu_datamux_r(device, offset, mem_mask); |
| 312 | return value; |
| 313 | } |
| 314 | |
| 315 | /* |
| 316 | Write access. |
| 317 | */ |
| 318 | WRITE16_DEVICE_HANDLER( sgcpu_w ) |
| 319 | { |
| 320 | sgcpu_state *sgcpu = get_safe_token(device); |
| 321 | if (sgcpu->cpu == NULL) return; |
| 322 | |
| 323 | // cpu_adjust_icount(sgcpu->cpu, -4); |
| 324 | |
| 325 | int addroff = offset << 1; |
| 326 | UINT16 zone = addroff & 0xe000; |
| 327 | |
| 328 | if (zone==0x0000) |
| 329 | { |
| 330 | // ROM0 |
| 331 | logerror("SGCPU: ignoring ROM write access at %04x\n", addroff); |
| 332 | return; |
| 333 | } |
| 334 | |
| 335 | if (zone==0x2000 || zone==0xa000 || zone==0xc000 || zone==0xe000) |
| 336 | { |
| 337 | sgcpu_samsmem_w(device, offset, data, mem_mask); |
| 338 | return; |
| 339 | } |
| 340 | |
| 341 | if (zone==0x4000) |
| 342 | { |
| 343 | if (sgcpu->internal_dsr) |
| 344 | { |
| 345 | logerror("SGCPU: ignoring DSR write access at %04x\n", addroff); |
| 346 | return; |
| 347 | } |
| 348 | else |
| 349 | { |
| 350 | if (sgcpu->access_mapper && ((addroff & 0xffe0)==0x4000)) |
| 351 | { |
| 352 | COMBINE_DATA(&sgcpu->mapper[offset & 0x000f]); |
| 353 | return; |
| 354 | } |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | if (zone==0x6000 && sgcpu->internal_rom6) |
| 359 | { |
| 360 | sgcpu->rom6_bank = offset & 0x0001; |
| 361 | return; |
| 362 | } |
| 363 | |
| 364 | // Scratch pad RAM and sound |
| 365 | // speech is in peribox |
| 366 | // groms are in hsgpl in peribox |
| 367 | if (zone==0x8000) |
| 368 | { |
| 369 | if ((addroff & 0xfff0)==0x8400) |
| 370 | { |
| 371 | sn76496_w(sgcpu->soundchip, 0, (data >> 8) & 0xff); |
| 372 | return; |
| 373 | } |
| 374 | if ((addroff & 0xfc00)==0x8000) |
| 375 | { |
| 376 | COMBINE_DATA(&sgcpu->scratchpad[(addroff & 0x03ff)>>1]); |
| 377 | return; |
| 378 | } |
| 379 | // Video: 8C00, 8C02 |
| 380 | if ((addroff & 0xfffd)==0x8c00) |
| 381 | { |
| 382 | ti_v9938_w16(sgcpu->video, offset, data, mem_mask); |
| 383 | return; |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | // If we are here, check the peribox via the datamux |
| 388 | // catch-all for unmapped zones |
| 389 | sgcpu_datamux_w(device, offset, data, mem_mask); |
| 390 | } |
| 391 | |
| 392 | /*************************************************************************** |
| 393 | Control lines |
| 394 | ****************************************************************************/ |
| 395 | /* |
| 396 | Handles the EXTINT line. This one is directly connected to the 9901. |
| 397 | */ |
| 398 | WRITE_LINE_DEVICE_HANDLER( card_extint ) |
| 399 | { |
| 400 | tms9901_set_single_int(device->machine->device("tms9901"), 1, state); |
| 401 | } |
| 402 | |
| 403 | WRITE_LINE_DEVICE_HANDLER( card_notconnected ) |
| 404 | { |
| 405 | logerror("Not connected line set ... sorry\n"); |
| 406 | } |
| 407 | |
| 408 | WRITE_LINE_DEVICE_HANDLER( card_ready ) |
| 409 | { |
| 410 | logerror("READY line set ... not yet connected, level=%02x\n", state); |
| 411 | } |
| 412 | |
| 413 | /* |
| 414 | set the state of int2 (called by the v9938) |
| 415 | */ |
| 416 | void tms9901_sg_set_int2(running_machine *machine, int state) |
| 417 | { |
| 418 | tms9901_set_single_int(machine->device("tms9901"), 2, state); |
| 419 | } |
| 420 | |
| 421 | /*************************************************************************** |
| 422 | Keyboard/tape control |
| 423 | ****************************************************************************/ |
| 424 | static const char *keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3" }; |
| 425 | |
| 426 | /* |
| 427 | Called by the 9901 core whenever the state of INTREQ and IC0-3 changes |
| 428 | */ |
| 429 | static TMS9901_INT_CALLBACK( tms9901_interrupt_callback ) |
| 430 | { |
| 431 | if (intreq) |
| 432 | { |
| 433 | // On TI99, TMS9900 IC0-3 lines are not connected to TMS9901, |
| 434 | // but hard-wired to force level 1 interrupts |
| 435 | cputag_set_input_line_and_vector(device->machine, "maincpu", 0, ASSERT_LINE, 1); |
| 436 | } |
| 437 | else |
| 438 | { |
| 439 | cputag_set_input_line(device->machine, "maincpu", 0, CLEAR_LINE); |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | |
| 444 | /* |
| 445 | Read pins INT3*-INT7* of TI99's 9901. |
| 446 | |
| 447 | signification: |
| 448 | (bit 1: INT1 status) |
| 449 | (bit 2: INT2 status) |
| 450 | bit 3-7: keyboard status bits 0 to 4 |
| 451 | */ |
| 452 | static READ8_DEVICE_HANDLER( sgcpu_R9901_0 ) |
| 453 | { |
| 454 | running_device *dev = device->machine->device("sgcpu_board"); |
| 455 | sgcpu_state *sgcpu = get_safe_token(dev); |
| 456 | int answer; |
| 457 | |
| 458 | answer = ((input_port_read(device->machine, keynames[sgcpu->keyCol >> 1]) >> ((sgcpu->keyCol & 1) * 8)) << 3) & 0xF8; |
| 459 | |
| 460 | if (sgcpu->alphaLockLine == FALSE) |
| 461 | answer &= ~(input_port_read(device->machine, "ALPHA") << 3); |
| 462 | |
| 463 | return answer; |
| 464 | } |
| 465 | |
| 466 | /* |
| 467 | Read pins INT8*-INT15* of TI99's 9901. |
| 468 | bit 0-2: keyboard status bits 5 to 7 |
| 469 | bit 3: tape input mirror |
| 470 | bit 5-7: weird, not emulated |
| 471 | */ |
| 472 | static READ8_DEVICE_HANDLER( sgcpu_R9901_1 ) |
| 473 | { |
| 474 | running_device *dev = device->machine->device("sgcpu_board"); |
| 475 | sgcpu_state *sgcpu = get_safe_token(dev); |
| 476 | int answer; |
| 477 | |
| 478 | if (sgcpu->keyCol == 7) |
| 479 | answer = 0x07; |
| 480 | else |
| 481 | { |
| 482 | answer = ((input_port_read(device->machine, keynames[sgcpu->keyCol >> 1]) >> ((sgcpu->keyCol & 1) * 8)) >> 5) & 0x07; |
| 483 | } |
| 484 | return answer; |
| 485 | } |
| 486 | |
| 487 | |
| 488 | /* |
| 489 | Read pins P8-P15 of TI99's 9901. |
| 490 | bit 26: high |
| 491 | bit 27: tape input |
| 492 | */ |
| 493 | static READ8_DEVICE_HANDLER( sgcpu_R9901_3 ) |
| 494 | { |
| 495 | running_device *dev = device->machine->device("sgcpu_board"); |
| 496 | sgcpu_state *sgcpu = get_safe_token(dev); |
| 497 | int answer = 4; /* on systems without handset, the pin is pulled up to avoid spurious interrupts */ |
| 498 | if (cassette_input(sgcpu->cassette) > 0) |
| 499 | answer |= 8; |
| 500 | return answer; |
| 501 | } |
| 502 | |
| 503 | /* |
| 504 | WRITE key column select (P2-P4) |
| 505 | */ |
| 506 | static WRITE8_DEVICE_HANDLER( sgcpu_KeyC ) |
| 507 | { |
| 508 | running_device *dev = device->machine->device("sgcpu_board"); |
| 509 | sgcpu_state *sgcpu = get_safe_token(dev); |
| 510 | if (data) |
| 511 | sgcpu->keyCol |= 1 << (offset-2); |
| 512 | else |
| 513 | sgcpu->keyCol &= ~(1 << (offset-2)); |
| 514 | } |
| 515 | |
| 516 | /* |
| 517 | WRITE alpha lock line - TI99/4a only (P5) |
| 518 | */ |
| 519 | static WRITE8_DEVICE_HANDLER( sgcpu_AlphaW ) |
| 520 | { |
| 521 | running_device *dev = device->machine->device("sgcpu_board"); |
| 522 | sgcpu_state *sgcpu = get_safe_token(dev); |
| 523 | sgcpu->alphaLockLine = data; |
| 524 | } |
| 525 | |
| 526 | /* |
| 527 | command CS1 (only) tape unit motor (P6) |
| 528 | */ |
| 529 | static WRITE8_DEVICE_HANDLER( sgcpu_CS_motor ) |
| 530 | { |
| 531 | running_device *dev = device->machine->device("sgcpu_board"); |
| 532 | sgcpu_state *sgcpu = get_safe_token(dev); |
| 533 | cassette_change_state(sgcpu->cassette, data ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR); |
| 534 | } |
| 535 | |
| 536 | /* |
| 537 | audio gate (P8) |
| 538 | |
| 539 | Set to 1 before using tape: this enables the mixing of tape input sound |
| 540 | with computer sound. |
| 541 | |
| 542 | We do not really need to emulate this as the tape recorder generates sound |
| 543 | on its own. |
| 544 | */ |
| 545 | static WRITE8_DEVICE_HANDLER( sgcpu_audio_gate ) |
| 546 | { |
| 547 | } |
| 548 | |
| 549 | /* |
| 550 | tape output (P9) |
| 551 | I think polarity is correct, but don't take my word for it. |
| 552 | */ |
| 553 | static WRITE8_DEVICE_HANDLER( sgcpu_CS_output ) |
| 554 | { |
| 555 | running_device *dev = device->machine->device("sgcpu_board"); |
| 556 | sgcpu_state *sgcpu = get_safe_token(dev); |
| 557 | cassette_output(sgcpu->cassette, data ? +1 : -1); |
| 558 | } |
| 559 | |
| 560 | /* TMS9901 setup. The callback functions pass a reference to the TMS9901 as device. */ |
| 561 | const tms9901_interface tms9901_wiring_ti99_4p = |
| 562 | { |
| 563 | TMS9901_INT1 | TMS9901_INT2 | TMS9901_INTC, /* only input pins whose state is always known */ |
| 564 | |
| 565 | { /* read handlers */ |
| 566 | sgcpu_R9901_0, |
| 567 | sgcpu_R9901_1, |
| 568 | NULL, |
| 569 | sgcpu_R9901_3 |
| 570 | }, |
| 571 | |
| 572 | { /* write handlers */ |
| 573 | NULL, |
| 574 | NULL, |
| 575 | sgcpu_KeyC, |
| 576 | sgcpu_KeyC, |
| 577 | sgcpu_KeyC, |
| 578 | sgcpu_AlphaW, |
| 579 | sgcpu_CS_motor, |
| 580 | NULL, |
| 581 | sgcpu_audio_gate, |
| 582 | sgcpu_CS_output, |
| 583 | NULL, |
| 584 | NULL, |
| 585 | NULL, |
| 586 | NULL, |
| 587 | NULL, |
| 588 | NULL |
| 589 | }, |
| 590 | |
| 591 | /* interrupt handler */ |
| 592 | tms9901_interrupt_callback, |
| 593 | |
| 594 | /* clock rate = 3MHz */ |
| 595 | 3000000. |
| 596 | }; |
| 597 | |
| 598 | |
| 599 | /*************************************************************************** |
| 600 | DEVICE LIFECYCLE FUNCTIONS |
| 601 | ***************************************************************************/ |
| 602 | |
| 603 | static DEVICE_START( sgcpu ) |
| 604 | { |
| 605 | // // printf("Starting mapper\n"); |
| 606 | sgcpu_state *sgcpu = get_safe_token(device); |
| 607 | sgcpu->cpu = device->machine->device("maincpu"); |
| 608 | sgcpu->peribox = device->machine->device("peribox"); |
| 609 | sgcpu->soundchip = device->machine->device("soundgen"); |
| 610 | sgcpu->video = device->machine->device("video"); |
| 611 | sgcpu->cassette = device->machine->device("cassette"); |
| 612 | |
| 613 | assert(sgcpu->cpu && sgcpu->peribox && sgcpu->soundchip && sgcpu->video && sgcpu->cassette); |
| 614 | |
| 615 | sgcpu->ram = (UINT16*)malloc(0x080000); |
| 616 | sgcpu->scratchpad = (UINT16*)malloc(0x0400); |
| 617 | |
| 618 | UINT16 *rom = (UINT16*)memory_region(device->machine, "maincpu"); |
| 619 | sgcpu->rom0 = rom + 0x2000; |
| 620 | sgcpu->dsr = rom + 0x6000; |
| 621 | sgcpu->rom6a = rom + 0x3000; |
| 622 | sgcpu->rom6b = rom + 0x7000; |
| 623 | } |
| 624 | |
| 625 | static DEVICE_STOP( sgcpu ) |
| 626 | { |
| 627 | // printf("Stopping SGCPU\n"); |
| 628 | sgcpu_state *sgcpu = get_safe_token(device); |
| 629 | free(sgcpu->ram); |
| 630 | free(sgcpu->scratchpad); |
| 631 | } |
| 632 | |
| 633 | static DEVICE_RESET( sgcpu ) |
| 634 | { |
| 635 | logerror("Resetting SGCPU\n"); |
| 636 | } |
| 637 | |
| 638 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 639 | |
| 640 | #define DEVTEMPLATE_ID(p,s) p##sgcpu##s |
| 641 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET |
| 642 | #define DEVTEMPLATE_NAME "SGCPU Board" |
| 643 | #define DEVTEMPLATE_FAMILY "Peripheral Expansion" |
| 644 | #include "devtempl.h" |
| 645 | |
| 646 | DEFINE_LEGACY_DEVICE( SGCPU, sgcpu ); |
src/mess/machine/ti99/datamux.c
| r0 | r9485 | |
| 1 | /*************************************************************************** |
| 2 | |
| 3 | TI-99/4(A) databus multiplexer circuit |
| 4 | |
| 5 | The DMUX is used to convert the 16-bit databus of the TMS9900 into |
| 6 | an 8-bit databus. The processor writes a 16 bit word which is split |
| 7 | by this circuit into two bytes that are sent serially over the 8-bit bus. |
| 8 | In the opposite direction, one 16-bit read request from the CPU is |
| 9 | translated into two 8-bit read requests (odd address / even address) from |
| 10 | this datamux. Its 8-bit latch (LS373) holds the first byte, while the |
| 11 | datamux puts the CPU on hold, gets the second byte, and routes that second |
| 12 | byte to the D0-D7 lines, while the latch now puts the first byte on D8-D15. |
| 13 | Since we get two memory accesses each time, there are twice as many |
| 14 | wait states than for a direct 16-bit access (order LSB, MSB). |
| 15 | |
| 16 | In addition, since the TMS 9900 also supports byte operations, every write |
| 17 | operation is automatically preceded by a read operation, so this adds even |
| 18 | more delays. |
| 19 | |
| 20 | Within the TI-99/4(A) console, only the internal ROM and the small internal |
| 21 | RAM ("scratch pad RAM") are directly connected to the 16-bit bus. All other |
| 22 | devices (video, audio, speech, GROM, and the complete P-Box system are |
| 23 | connected to the datamux. |
| 24 | |
| 25 | The TMS9995 which is used in the Geneve has an internal multiplex, and |
| 26 | the byte order is reversed: MSB, LSB |
| 27 | |
| 28 | ROM = 4K * 16 bit (8 KiB) system ROM (kind of BIOS, plus the GPL interpreter) |
| 29 | RAM = 128 * 16 bit (256 byte) system RAM ("scratch pad") |
| 30 | |
| 31 | Many users (me too) used to solder a 16K * 16 bit (32 KiB) SRAM circuit into |
| 32 | the console, before the datamux, decoded to 0x2000-0x3fff and 0xa000-0xffff. |
| 33 | (This expansion was also called 0-waitstate, since it could be accessed |
| 34 | with the full databus width, and the datamux did not create waitstates.) |
| 35 | |
| 36 | +---+ +-------+ |
| 37 | | |===##========##== D0-D7 ==========##===============|TMS9918| Video |
| 38 | | | || || || +-------+ |
| 39 | | T | +-----+ +-----+ LS245 +----+ |
| 40 | | M | | ROM | | RAM | +----+ |
| 41 | | S | +-----+ +-----+ || | : |
| 42 | | |---||-||-----||-||----------------||-|---------------------: |
| 43 | | 9 | || || A0 - A14 || | A0 : Sound |
| 44 | | 9 |---||--------||-------------------||-|----------+ -A15 : GROM |
| 45 | | 0 | || || LS373 +-+ || | +----A15-+----------: Cartridges |
| 46 | | 0 | || || ##========|<|===## | | : Speech |
| 47 | | | || || || +-+ +-+ || | | : Expansion |
| 48 | | |===## D8-D15 ##===##==|>|=====|===##=|=|=========== D0-D7 =: cards |
| 49 | +---+ +-+ | | | : |
| 50 | ^ LS244| | | | |
| 51 | | | +--+---+-++ |
| 52 | | +--------| DMUX | |
| 53 | +--- READY -------------------------+------+ |
| 54 | |
| 55 | Databus width |
| 56 | :------------- 16 bit ---------------|---------- 8 bit -----: |
| 57 | |
| 58 | A0=MSB; A15=LSB |
| 59 | D0=MSB; D15=LSB |
| 60 | |
| 61 | We integrate the 16 bit memory expansion in this datamux component |
| 62 | (pretending that the memory expansion was soldered on top of the datamux) |
| 63 | ***************************************************************************/ |
| 64 | |
| 65 | #include "emu.h" |
| 66 | #include "peribox.h" |
| 67 | #include "datamux.h" |
| 68 | |
| 69 | #define MAXDEV 10 |
| 70 | |
| 71 | typedef struct _attached_device |
| 72 | { |
| 73 | /* The device. */ |
| 74 | running_device *device; |
| 75 | |
| 76 | /* Address bits involved */ |
| 77 | UINT16 address_mask; |
| 78 | |
| 79 | /* Value of the address bits which are involved for selecting for read. */ |
| 80 | UINT16 select; |
| 81 | |
| 82 | /* Value of the address bits which are involved for selecting for write. */ |
| 83 | UINT16 write_select; |
| 84 | |
| 85 | /* Read access. */ |
| 86 | databus_read_function read_byte; |
| 87 | |
| 88 | /* Write access. */ |
| 89 | databus_write_function write_byte; |
| 90 | |
| 91 | /* Setting which determined if this device is mounted or not. */ |
| 92 | const char *setting; |
| 93 | |
| 94 | /* Bits which must be set in the setting. */ |
| 95 | UINT8 set; |
| 96 | |
| 97 | /* Bits which must not be set in the setting. */ |
| 98 | UINT8 unset; |
| 99 | } attached_device; |
| 100 | |
| 101 | typedef struct _datamux_state |
| 102 | { |
| 103 | /* All devices that are attached to the 8-bit bus. */ |
| 104 | attached_device component[MAXDEV]; |
| 105 | |
| 106 | /* Latch which stores the first byte */ |
| 107 | UINT8 latch; |
| 108 | |
| 109 | // Intermediate storage which holds the low byte of the previous read cycle. |
| 110 | // In reality, the TMS9900 adds the low byte to the 16 bit memory word |
| 111 | // from the previous read-before-write cycle, but this is not yet handled |
| 112 | // by the TMS9900 emulation. TODO: rewrite TMS9900 |
| 113 | UINT8 lowbyte; |
| 114 | UINT8 highbyte; |
| 115 | |
| 116 | /* Memory expansion (internal, 16 bit). */ |
| 117 | UINT16 *ram16b; |
| 118 | |
| 119 | /* Use the memory expansion? */ |
| 120 | int use32k; |
| 121 | |
| 122 | /* Management function */ |
| 123 | int devindex; |
| 124 | } datamux_state; |
| 125 | |
| 126 | |
| 127 | INLINE datamux_state *get_safe_token(running_device *device) |
| 128 | { |
| 129 | assert(device != NULL); |
| 130 | assert(downcast<legacy_device_base *>(device)->token() != NULL); |
| 131 | return (datamux_state *)downcast<legacy_device_base *>(device)->token(); |
| 132 | } |
| 133 | |
| 134 | static void dmux_mount_device(running_device *dmuxdev, running_device *busdevice, UINT16 address_mask, UINT16 address_bits, UINT16 write_sel, databus_read_function read, databus_write_function write) |
| 135 | { |
| 136 | datamux_state *dmux = get_safe_token(dmuxdev); |
| 137 | int index = dmux->devindex++; |
| 138 | if (index < MAXDEV) |
| 139 | { |
| 140 | if (dmux->component[index].device == NULL) |
| 141 | { |
| 142 | dmux->component[index].device = busdevice; |
| 143 | dmux->component[index].address_mask = address_mask; |
| 144 | dmux->component[index].select = address_bits; |
| 145 | dmux->component[index].write_select = write_sel; |
| 146 | dmux->component[index].read_byte = read; |
| 147 | dmux->component[index].write_byte = write; |
| 148 | // printf("registering the device at position %d\n", index); |
| 149 | } |
| 150 | } |
| 151 | else |
| 152 | { |
| 153 | logerror("ti99_dmux: Tried to mount too many devices to the DMUX. (Just a bug.)\n"); |
| 154 | } |
| 155 | } |
| 156 | /* |
| 157 | static void dmux_unmount_device(running_device *dmuxdev, running_device *busdevice) |
| 158 | { |
| 159 | datamux_state *dmux = get_safe_token(dmuxdev); |
| 160 | for (int index = 0; index < MAXDEV; index++) |
| 161 | { |
| 162 | if (dmux->component[index].device == busdevice) |
| 163 | { |
| 164 | dmux->component[index].device = NULL; |
| 165 | return; |
| 166 | } |
| 167 | } |
| 168 | printf("Failed to uninstall device from dmux\n"); |
| 169 | } |
| 170 | */ |
| 171 | /*************************************************************************** |
| 172 | DEVICE ACCESSOR FUNCTIONS |
| 173 | ***************************************************************************/ |
| 174 | |
| 175 | /* |
| 176 | Read access. We are using two loops because the delay between both |
| 177 | accesses must not occur within the loop. So we have one access on the bus, |
| 178 | a delay, and then the second access (each one with possibly many attached |
| 179 | devices) |
| 180 | */ |
| 181 | READ16_DEVICE_HANDLER( ti99_dmux_r ) |
| 182 | { |
| 183 | UINT8 hbyte = 0; |
| 184 | datamux_state *dmux = get_safe_token(device); |
| 185 | UINT16 addr = (offset << 1); |
| 186 | |
| 187 | // Looks ugly, but this is close to the real thing. If the 16bit |
| 188 | // memory expansion is installed in the console, and the access hits its |
| 189 | // space, just respond to the memory access and don't bother the |
| 190 | // datamux in any way. In particular, do not make the datamux insert wait |
| 191 | // states. |
| 192 | if (dmux->use32k) |
| 193 | { |
| 194 | UINT16 base = 0; |
| 195 | if ((addr & 0xe000)==0x2000) base = 0x1000; |
| 196 | if (((addr & 0xe000)==0xa000) || ((addr & 0xc000)==0xc000)) base = 0x4000; |
| 197 | |
| 198 | if (base != 0) |
| 199 | { |
| 200 | UINT16 reply = dmux->ram16b[offset-base]; |
| 201 | // Store the prefetch |
| 202 | dmux->lowbyte = reply & 0x00ff; |
| 203 | dmux->highbyte = (reply & 0xff00)>>8; |
| 204 | return reply; |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | for (int i=0; i < MAXDEV; i++) |
| 209 | { |
| 210 | attached_device *adev = &dmux->component[i]; |
| 211 | if (dmux->component[i].device != NULL) |
| 212 | { |
| 213 | if (adev->read_byte != NULL) |
| 214 | { |
| 215 | if (((addr+1) & adev->address_mask)==adev->select) |
| 216 | { |
| 217 | // put on the latch |
| 218 | adev->read_byte(adev->device, addr+1, &dmux->latch); |
| 219 | dmux->lowbyte = dmux->latch; |
| 220 | } |
| 221 | } |
| 222 | // hope we don't have two devices answering... |
| 223 | // consider something like a logical OR and maybe some artificial smoke |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | // Takes three cycles |
| 228 | cpu_adjust_icount(device->machine->device("maincpu"),-3); |
| 229 | |
| 230 | for (int i=0; i < MAXDEV; i++) |
| 231 | { |
| 232 | attached_device *adev = &dmux->component[i]; |
| 233 | if (dmux->component[i].device != NULL) |
| 234 | { |
| 235 | if (adev->read_byte != NULL) |
| 236 | { |
| 237 | if ((addr & adev->address_mask)==adev->select) |
| 238 | { |
| 239 | adev->read_byte(adev->device, addr, &hbyte); |
| 240 | dmux->highbyte = hbyte; |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | // Takes three cycles |
| 247 | cpu_adjust_icount(device->machine->device("maincpu"),-3); |
| 248 | |
| 249 | // use the latch and the currently read byte and put it on the 16bit bus |
| 250 | return (hbyte<<8) | dmux->latch ; |
| 251 | } |
| 252 | |
| 253 | /* |
| 254 | Write access. |
| 255 | */ |
| 256 | WRITE16_DEVICE_HANDLER( ti99_dmux_w ) |
| 257 | { |
| 258 | datamux_state *dmux = get_safe_token(device); |
| 259 | UINT16 addr = (offset << 1); |
| 260 | |
| 261 | // The handling of byte writing is done in MESS using mem_mask. |
| 262 | // If the mem_mask is ff00, the CPU is about to write the high byte. This |
| 263 | // is not the true story, since the real CPU never writes half a word. |
| 264 | // In reality, the TMS9900 performs a read-before-write, stores the word |
| 265 | // internally, and replaces the high or low byte when it is about to write |
| 266 | // a byte to an even or odd address, respectively. Then it writes the |
| 267 | // complete new word. |
| 268 | // Example: Memory locations 0x6000 contains the word 0x1234 (BE). If we |
| 269 | // write a 0x56 to 0x6000, the CPU first reads 0x6000, stores the 0x1234, |
| 270 | // replaces the first byte with 0x56, and then writes 0x5634 to 0x6000. |
| 271 | |
| 272 | // Until the TMS9900 emulation is corrected in this way, we are using |
| 273 | // the DMUX in place of the internal buffer. |
| 274 | |
| 275 | // printf("write addr=%04x memmask=%04x value=%04x\n", addr, mem_mask, data); |
| 276 | if (mem_mask == 0xff00) |
| 277 | data = data | dmux->lowbyte; |
| 278 | |
| 279 | if (mem_mask == 0x00ff) |
| 280 | data = data | (dmux->highbyte << 8); |
| 281 | |
| 282 | // Handle the internal 32K expansion |
| 283 | if (dmux->use32k) |
| 284 | { |
| 285 | if (addr>=0x2000 && addr<0x4000) |
| 286 | { |
| 287 | dmux->ram16b[offset-0x1000] = data; // index 0000 - 0fff |
| 288 | return; |
| 289 | } |
| 290 | if (addr>=0xa000) |
| 291 | { |
| 292 | dmux->ram16b[offset-0x4000] = data; // index 1000 - 4fff |
| 293 | return; |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | for (int i=0; i < MAXDEV; i++) |
| 298 | { |
| 299 | attached_device *adev = &dmux->component[i]; |
| 300 | if (dmux->component[i].device != NULL) |
| 301 | { |
| 302 | if (adev->write_byte != NULL) |
| 303 | { |
| 304 | if (((addr+1) & adev->address_mask)==(adev->select|adev->write_select)) |
| 305 | { |
| 306 | // write the byte from the lower 8 lines |
| 307 | adev->write_byte(adev->device, addr+1, data & 0xff); |
| 308 | } |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | // Takes three cycles |
| 314 | cpu_adjust_icount(device->machine->device("maincpu"),-3); |
| 315 | |
| 316 | for (int i=0; i < MAXDEV; i++) |
| 317 | { |
| 318 | attached_device *adev = &dmux->component[i]; |
| 319 | if (dmux->component[i].device != NULL) |
| 320 | { |
| 321 | if (adev->write_byte != NULL) |
| 322 | { |
| 323 | if ((addr & adev->address_mask)==(adev->select|adev->write_select)) |
| 324 | { |
| 325 | // write the byte from the upper 8 lines |
| 326 | adev->write_byte(adev->device, addr, (data>>8) & 0xff); |
| 327 | } |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | // Takes three cycles |
| 333 | cpu_adjust_icount(device->machine->device("maincpu"),-3); |
| 334 | } |
| 335 | |
| 336 | /* CRU space is not involved in the dmux. */ |
| 337 | |
| 338 | /*************************************************************************** |
| 339 | DEVICE LIFECYCLE FUNCTIONS |
| 340 | ***************************************************************************/ |
| 341 | |
| 342 | static DEVICE_START( datamux ) |
| 343 | { |
| 344 | datamux_state *dmux = get_safe_token(device); |
| 345 | dmux->ram16b = NULL; |
| 346 | } |
| 347 | |
| 348 | static DEVICE_STOP( datamux ) |
| 349 | { |
| 350 | datamux_state *dmux = get_safe_token(device); |
| 351 | if (dmux->ram16b) free(dmux->ram16b); |
| 352 | } |
| 353 | |
| 354 | static DEVICE_RESET( datamux ) |
| 355 | { |
| 356 | datamux_state *dmux = get_safe_token(device); |
| 357 | dmux->use32k = (input_port_read(device->machine, "RAM")==RAM_TI32_INT)? TRUE : FALSE; |
| 358 | |
| 359 | dmux->devindex = 0; |
| 360 | int i = 0; |
| 361 | int done=FALSE; |
| 362 | |
| 363 | for (int index = 0; index < MAXDEV; index++) |
| 364 | { |
| 365 | dmux->component[index].device = NULL; |
| 366 | } |
| 367 | |
| 368 | if (dmux->ram16b==NULL) dmux->ram16b = (UINT16*)malloc(32768); |
| 369 | bus_device *cons = (bus_device*)device->baseconfig().static_config(); |
| 370 | while (!done) |
| 371 | { |
| 372 | if (cons[i].name != NULL) |
| 373 | { |
| 374 | running_device *dev = device->machine->device(cons[i].name); |
| 375 | if (dev!=NULL) |
| 376 | { |
| 377 | UINT32 set = 0; |
| 378 | if (cons[i].setting!=NULL) |
| 379 | set = input_port_read(device->machine, cons[i].setting); |
| 380 | |
| 381 | if (((set & cons[i].set)==cons[i].set)&&((set & cons[i].unset)==0)) |
| 382 | { |
| 383 | dmux_mount_device(device, dev, cons[i].address_mask, cons[i].select_pattern, cons[i].write_select, cons[i].read, cons[i].write); |
| 384 | logerror("ti99_datamux: Device %s mounted at index %d.\n", cons[i].name, i); |
| 385 | } |
| 386 | else |
| 387 | { |
| 388 | logerror("ti99_datamux: Device %s not mounted due to configuration setting %s.\n", cons[i].name, cons[i].setting); |
| 389 | } |
| 390 | } |
| 391 | else |
| 392 | logerror("ti99_datamux: Device %s not found.\n", cons[i].name); |
| 393 | } |
| 394 | else done=TRUE; |
| 395 | i++; |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 400 | |
| 401 | #define DEVTEMPLATE_ID(p,s) p##datamux##s |
| 402 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET |
| 403 | #define DEVTEMPLATE_NAME "TI-99 Databus 16/8 Multiplexer" |
| 404 | #define DEVTEMPLATE_FAMILY "Internal devices" |
| 405 | #include "devtempl.h" |
| 406 | |
| 407 | DEFINE_LEGACY_DEVICE( DMUX, datamux ); |
src/mess/machine/ti99/hfdc.c
| r0 | r9485 | |
| 1 | /* |
| 2 | Myarc Hard and Floppy Disk Controller |
| 3 | Michael Zapf |
| 4 | */ |
| 5 | #include "emu.h" |
| 6 | #include "peribox.h" |
| 7 | #include "ti_fdc.h" |
| 8 | #include "machine/wd17xx.h" |
| 9 | #include "formats/ti99_dsk.h" |
| 10 | #include "devices/flopdrv.h" |
| 11 | #include "devices/ti99_hd.h" |
| 12 | #include "machine/mm58274c.h" |
| 13 | #include "hfdc.h" |
| 14 | |
| 15 | #define CRU_BASE 0x1100 |
| 16 | |
| 17 | #define hfdc_region "hfdc_region" |
| 18 | |
| 19 | #define HFDC_MAX_FLOPPY 4 |
| 20 | #define HFDC_MAX_HARD 4 |
| 21 | |
| 22 | #define TAPE_ADDR 0x0fc0 |
| 23 | #define HDC_R_ADDR 0x0fd0 |
| 24 | #define HDC_W_ADDR 0x0fd2 |
| 25 | #define CLK_ADDR 0x0fe0 |
| 26 | #define RAM_ADDR 0x1000 |
| 27 | |
| 28 | typedef ti99_pebcard_config ti99_hfdc_config; |
| 29 | |
| 30 | typedef struct _ti99_hfdc_state |
| 31 | { |
| 32 | /* When TRUE, card is accessible. Indicated by a LED. */ |
| 33 | int selected; |
| 34 | |
| 35 | /* When TRUE, triggers motor monoflop. */ |
| 36 | int strobe_motor; |
| 37 | |
| 38 | /* Clock divider bit 0. Unused in this emulation. */ |
| 39 | int CD0; |
| 40 | |
| 41 | /* Clock divider bit 1. Unused in this emulation. */ |
| 42 | int CD1; |
| 43 | |
| 44 | /* count 4.23s from rising edge of motor_on */ |
| 45 | emu_timer *motor_on_timer; |
| 46 | |
| 47 | /* Link to the HDC9234 controller on the board. */ |
| 48 | running_device *controller; |
| 49 | |
| 50 | /* Link to the clock chip on the board. */ |
| 51 | running_device *clock; |
| 52 | |
| 53 | /* Determines whether we have access to the CRU bits. */ |
| 54 | int cru_select; |
| 55 | |
| 56 | /* IRQ state */ |
| 57 | int irq; |
| 58 | |
| 59 | /* DMA in Progress state */ |
| 60 | int dip; |
| 61 | |
| 62 | /* Output 1 latch */ |
| 63 | UINT8 output1_latch; |
| 64 | |
| 65 | /* Output 2 latch */ |
| 66 | UINT8 output2_latch; |
| 67 | |
| 68 | /* Connected floppy drives. */ |
| 69 | running_device *floppy_unit[HFDC_MAX_FLOPPY]; |
| 70 | |
| 71 | /* Connected harddisk drives. */ |
| 72 | running_device *harddisk_unit[HFDC_MAX_HARD]; |
| 73 | |
| 74 | /* DMA address latch */ |
| 75 | UINT32 dma_address; |
| 76 | |
| 77 | /* DSR ROM */ |
| 78 | UINT8 *rom; |
| 79 | |
| 80 | /* ROM banks. */ |
| 81 | int rom_offset; |
| 82 | |
| 83 | /* HFDC RAM */ |
| 84 | UINT8 *ram; |
| 85 | |
| 86 | /* RAM banks */ |
| 87 | int ram_offset[4]; |
| 88 | |
| 89 | /* Callback lines to the main system. */ |
| 90 | ti99_peb_connect lines; |
| 91 | |
| 92 | } ti99_hfdc_state; |
| 93 | |
| 94 | INLINE ti99_hfdc_state *get_safe_token(running_device *device) |
| 95 | { |
| 96 | assert(device != NULL); |
| 97 | return (ti99_hfdc_state *)downcast<legacy_device_base *>(device)->token(); |
| 98 | } |
| 99 | |
| 100 | /* |
| 101 | Resets the drive geometry. This is required because the heuristic of |
| 102 | the default implementation sets the drive geometry to the geometry |
| 103 | of the medium. |
| 104 | */ |
| 105 | static void set_geometry(running_device *drive, floppy_type_t type) |
| 106 | { |
| 107 | if (drive!=NULL) |
| 108 | floppy_drive_set_geometry(drive, type); |
| 109 | else |
| 110 | logerror("ti99/HFDC: Drive not found\n"); |
| 111 | } |
| 112 | |
| 113 | static void set_all_geometries(running_device *device, floppy_type_t type) |
| 114 | { |
| 115 | set_geometry(device->siblingdevice(FLOPPY_0), type); |
| 116 | set_geometry(device->siblingdevice(FLOPPY_1), type); |
| 117 | set_geometry(device->siblingdevice(FLOPPY_2), type); |
| 118 | } |
| 119 | |
| 120 | /*************************************************************************/ |
| 121 | |
| 122 | /* |
| 123 | Read disk CRU interface |
| 124 | HFDC manual p. 44 |
| 125 | |
| 126 | CRU Select=0 |
| 127 | xx00 Disk controller interrupt |
| 128 | xx02 Motor status |
| 129 | xx04 DMA in progress |
| 130 | |
| 131 | Note that the cru line settings on the board do not match the |
| 132 | description in the manual. 02 and 04 swap their positions (checked with |
| 133 | board and schematics). Similarly, the dip switches |
| 134 | for the drive configurations are not correctly enumerated in the manual. |
| 135 | |
| 136 | CRU Select=1 |
| 137 | xx00 Switch 4 status \_ drive 2 config |
| 138 | xx02 Switch 3 status / |
| 139 | xx04 Switch 2 status \_ drive 1 config |
| 140 | xx06 Switch 1 status / |
| 141 | xx08 Switch 8 status \_ drive 4 config |
| 142 | xx0A Switch 7 status / |
| 143 | xx0C Switch 6 status \_ drive 3 config |
| 144 | xx0E Switch 5 status / |
| 145 | |
| 146 | config: 00 = 9/16/18 sectors, 40 tracks, 16 msec |
| 147 | 10 = 9/16/18 sectors, 40 tracks, 8 msec |
| 148 | 01 = 9/16/18 sectors, 80 tracks, 2 msec |
| 149 | 11 = 36 sectors, 80 tracks, 2 msec |
| 150 | */ |
| 151 | static READ8Z_DEVICE_HANDLER( cru_r ) |
| 152 | { |
| 153 | ti99_hfdc_state *card = get_safe_token(device); |
| 154 | UINT8 reply; |
| 155 | |
| 156 | if ((offset & 0xff00)==CRU_BASE) |
| 157 | { |
| 158 | if ((offset & 0x00ff)==0) /* CRU bits 0-7 */ |
| 159 | { |
| 160 | /* CRU bits */ |
| 161 | if (card->cru_select) |
| 162 | { |
| 163 | /* DIP switches. Logic levels are inverted (on->0, off->1). CRU |
| 164 | bit order is the reverse of DIP-switch order, too (dip 1 -> bit 7, |
| 165 | dip 8 -> bit 0). |
| 166 | |
| 167 | MZ: 00 should not be used since there is a bug in the |
| 168 | DSR of the HFDC which causes problems with SD disks |
| 169 | (controller tries DD and then fails to fall back to SD) */ |
| 170 | reply = ~(input_port_read(device->machine, "HFDCDIP")); |
| 171 | } |
| 172 | else |
| 173 | { |
| 174 | reply = 0; |
| 175 | |
| 176 | if (card->irq) reply |= 0x01; |
| 177 | if (card->dip) reply |= 0x02; |
| 178 | if (card->strobe_motor) reply |= 0x04; |
| 179 | } |
| 180 | *value = reply; |
| 181 | } |
| 182 | else /* CRU bits 8+ */ |
| 183 | *value = 0; |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | /* |
| 188 | Write disk CRU interface |
| 189 | HFDC manual p. 43 |
| 190 | |
| 191 | CRU rel. address Definition Active |
| 192 | 00 Device Service Routine Select HIGH |
| 193 | 02 Reset to controller chip LOW |
| 194 | 04 Floppy Motor on / CD0 HIGH |
| 195 | 06 ROM page select 0 / CD1 |
| 196 | 08 ROM page select 1 / CRU input select |
| 197 | 12/4/6/8/A RAM page select at 0x5400 |
| 198 | 1C/E/0/2/4 RAM page select at 0x5800 |
| 199 | 26/8/A/C/E RAM page select at 0x5C00 |
| 200 | |
| 201 | RAM bank select: bank 0..31; 12 = LSB (accordingly for other two areas) |
| 202 | ROM bank select: bank 0..3; 06 = MSB, 07 = LSB |
| 203 | Bit number = (CRU_rel_address - base_address)/2 |
| 204 | CD0 and CD1 are Clock Divider selections for the Floppy Data Separator (FDC9216) |
| 205 | */ |
| 206 | static WRITE8_DEVICE_HANDLER( cru_w ) |
| 207 | { |
| 208 | ti99_hfdc_state *card = get_safe_token(device); |
| 209 | |
| 210 | if ((offset & 0xff00)==CRU_BASE) |
| 211 | { |
| 212 | int bit = (offset >> 1) & 0x1f; |
| 213 | |
| 214 | if (bit >= 9 && bit < 24) |
| 215 | { |
| 216 | if (data) |
| 217 | // we leave index 0 unchanged; modify indexes 1-3 |
| 218 | card->ram_offset[(bit-4)/5] |= 0x0400 << ((bit-9)%5); |
| 219 | else |
| 220 | card->ram_offset[(bit-4)/5] &= ~(0x0400 << ((bit-9)%5)); |
| 221 | |
| 222 | return; |
| 223 | } |
| 224 | |
| 225 | switch (bit) |
| 226 | { |
| 227 | case 0: |
| 228 | card->selected = data; |
| 229 | break; |
| 230 | |
| 231 | case 1: |
| 232 | if (!data) smc92x4_reset(card->controller); // active low |
| 233 | break; |
| 234 | |
| 235 | case 2: |
| 236 | card->CD0 = data; |
| 237 | if (data && !card->strobe_motor) |
| 238 | { |
| 239 | timer_adjust_oneshot(card->motor_on_timer, ATTOTIME_IN_MSEC(4230), 0); |
| 240 | } |
| 241 | card->strobe_motor = data; |
| 242 | break; |
| 243 | |
| 244 | case 3: |
| 245 | card->CD1 = data; |
| 246 | if (data) card->rom_offset |= 0x2000; |
| 247 | else card->rom_offset &= ~0x2000; |
| 248 | break; |
| 249 | |
| 250 | case 4: |
| 251 | card->cru_select = data; |
| 252 | if (data) card->rom_offset |= 0x1000; |
| 253 | else card->rom_offset &= ~0x1000; |
| 254 | break; |
| 255 | |
| 256 | default: |
| 257 | logerror("ti99/HFDC: Attempt to set undefined CRU bit %d\n", bit); |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | /* |
| 263 | read a byte in disk DSR space |
| 264 | HFDC manual, p. 44 |
| 265 | Memory map as seen by the 99/4A PEB |
| 266 | |
| 267 | 0x4000 - 0x4fbf one of four possible ROM pages |
| 268 | 0x4fc0 - 0x4fcf tape select |
| 269 | 0x4fd0 - 0x4fdf disk controller select |
| 270 | 0x4fe0 - 0x4fff time chip select |
| 271 | |
| 272 | 0x5000 - 0x53ff static RAM page 0x10 |
| 273 | 0x5400 - 0x57ff static RAM page any of 32 pages |
| 274 | 0x5800 - 0x5bff static RAM page any of 32 pages |
| 275 | 0x5c00 - 0x5fff static RAM page any of 32 pages |
| 276 | */ |
| 277 | |
| 278 | static READ8Z_DEVICE_HANDLER( data_r ) |
| 279 | { |
| 280 | ti99_hfdc_state *card = get_safe_token(device); |
| 281 | |
| 282 | if (card->selected && (offset & 0xe000)==0x4000) |
| 283 | { |
| 284 | /* DSR region */ |
| 285 | if ((offset & 0x1000)==0x0000) |
| 286 | { |
| 287 | if ((offset & 0x0fc0)==0x0fc0) |
| 288 | { |
| 289 | // Tape: 4fc0...4fcf |
| 290 | if ((offset & 0x1ff0)==TAPE_ADDR) |
| 291 | { |
| 292 | logerror("ti99/HFDC: Tape support not available (access to address %04x)\n", offset); |
| 293 | return; |
| 294 | } |
| 295 | |
| 296 | // HDC9234: 4fd0..4fdf / read: 4fd0,4 (mirror 8,c) |
| 297 | // read: 0100 1111 1101 xx00 |
| 298 | if ((offset & 0x1ff3)==HDC_R_ADDR) |
| 299 | { |
| 300 | *value = smc92x4_r(card->controller, (offset>>2)&1); |
| 301 | return; |
| 302 | } |
| 303 | |
| 304 | if ((offset & 0x1fe1)==CLK_ADDR) |
| 305 | { |
| 306 | *value = mm58274c_r(card->clock, (offset & 0x001e) >> 1); |
| 307 | return; |
| 308 | } |
| 309 | } |
| 310 | else |
| 311 | { |
| 312 | // ROM |
| 313 | *value = card->rom[card->rom_offset + (offset & 0x0fff)]; |
| 314 | return; |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | // RAM: 0101 xxxx xxxx xxxx |
| 319 | if ((offset & 0x1000)==RAM_ADDR) |
| 320 | { |
| 321 | // 0101 00xx xxxx xxxx static 0x08 |
| 322 | // 0101 01xx xxxx xxxx bank 1 |
| 323 | // 0101 10xx xxxx xxxx bank 2 |
| 324 | // 0101 11xx xxxx xxxx bank 3 |
| 325 | int bank = (offset & 0x0c00) >> 10; |
| 326 | *value = card->ram[card->ram_offset[bank] + (offset & 0x03ff)]; |
| 327 | } |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | /* |
| 332 | Write a byte to the controller. |
| 333 | */ |
| 334 | static WRITE8_DEVICE_HANDLER( data_w ) |
| 335 | { |
| 336 | ti99_hfdc_state *card = get_safe_token(device); |
| 337 | if (card->selected && (offset & 0xe000)==0x4000) |
| 338 | { |
| 339 | // Tape: 4fc0...4fcf |
| 340 | if ((offset & 0x1ff0)==TAPE_ADDR) |
| 341 | { |
| 342 | logerror("ti99/HFDC: Tape support not available (access to address %04x)\n", offset); |
| 343 | return; |
| 344 | } |
| 345 | |
| 346 | // HDC9234: 4fd0..4fdf / write: 4fd2,6 (mirror a,e) |
| 347 | // write: 0100 1111 1101 xx10 |
| 348 | if ((offset & 0x1ff3)==HDC_W_ADDR) |
| 349 | { |
| 350 | smc92x4_w(card->controller, (offset>>2)&1, data); |
| 351 | return; |
| 352 | } |
| 353 | |
| 354 | if ((offset & 0x1fe1)==CLK_ADDR) |
| 355 | { |
| 356 | mm58274c_w(card->clock, (offset & 0x001e) >> 1, data); |
| 357 | return; |
| 358 | } |
| 359 | |
| 360 | // RAM: 0101 xxxx xxxx xxxx |
| 361 | if ((offset & 0x1000)==RAM_ADDR) |
| 362 | { |
| 363 | // 0101 00xx xxxx xxxx static 0x08 |
| 364 | // 0101 01xx xxxx xxxx bank 1 |
| 365 | // 0101 10xx xxxx xxxx bank 2 |
| 366 | // 0101 11xx xxxx xxxx bank 3 |
| 367 | int bank = (offset & 0x0c00) >> 10; |
| 368 | card->ram[card->ram_offset[bank] + (offset & 0x03ff)] = data; |
| 369 | } |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | /* |
| 374 | Called whenever the state of the sms9234 interrupt pin changes. |
| 375 | */ |
| 376 | static WRITE_LINE_DEVICE_HANDLER( intrq_w ) |
| 377 | { |
| 378 | // Note that the callback functions deliver the calling device, which is |
| 379 | // the controller in this case. |
| 380 | ti99_hfdc_state *card = get_safe_token(device->owner()); |
| 381 | card->irq = state; |
| 382 | |
| 383 | /* Set INTA */ |
| 384 | if (state) |
| 385 | { |
| 386 | devcb_call_write_line(&card->lines.inta, ASSERT_LINE); |
| 387 | } |
| 388 | else |
| 389 | { |
| 390 | devcb_call_write_line(&card->lines.inta, CLEAR_LINE); |
| 391 | } |
| 392 | } |
| 393 | |
| 394 | /* |
| 395 | Called whenever the state of the sms9234 DMA in progress changes. |
| 396 | */ |
| 397 | static WRITE_LINE_DEVICE_HANDLER( dip_w ) |
| 398 | { |
| 399 | ti99_hfdc_state *card = get_safe_token(device->owner()); |
| 400 | card->dip = state; |
| 401 | } |
| 402 | |
| 403 | // device ok |
| 404 | static WRITE8_DEVICE_HANDLER( auxbus_out ) |
| 405 | { |
| 406 | ti99_hfdc_state *card = get_safe_token(device->owner()); |
| 407 | switch (offset) |
| 408 | { |
| 409 | case INPUT_STATUS: |
| 410 | logerror("ti99/HFDC: Invalid operation: S0=S1=0, but tried to write (expected: read drive status)\n"); |
| 411 | break; |
| 412 | |
| 413 | case OUTPUT_DMA_ADDR: |
| 414 | /* Value is dma address byte. Shift previous contents to the left. */ |
| 415 | card->dma_address = ((card->dma_address << 8) + (data&0xff))&0xffffff; |
| 416 | break; |
| 417 | |
| 418 | case OUTPUT_OUTPUT1: |
| 419 | /* value is output1 */ |
| 420 | card->output1_latch = data; |
| 421 | break; |
| 422 | |
| 423 | case OUTPUT_OUTPUT2: |
| 424 | /* value is output2 */ |
| 425 | card->output2_latch = data; |
| 426 | break; |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | static int line_to_index(UINT8 line, int max) |
| 431 | { |
| 432 | // Calculate the binary logarithm of the given 2-multiple |
| 433 | int index = 0; |
| 434 | while (index < max) |
| 435 | { |
| 436 | if ((line & 0x01)!=0) |
| 437 | break; |
| 438 | else |
| 439 | { |
| 440 | line = line >> 1; |
| 441 | index++; |
| 442 | } |
| 443 | } |
| 444 | if (index==max) return -1; |
| 445 | return index; |
| 446 | } |
| 447 | |
| 448 | /* |
| 449 | Select the HFDC floppy disk unit |
| 450 | This function implements the selection logic on the PCB (outside the |
| 451 | controller chip) |
| 452 | (Callback function) |
| 453 | */ |
| 454 | |
| 455 | static running_device *current_floppy(running_device *controller) |
| 456 | { |
| 457 | int disk_unit = -1; |
| 458 | ti99_hfdc_state *card = (ti99_hfdc_state*)downcast<legacy_device_base *>(controller->owner())->token(); |
| 459 | |
| 460 | disk_unit = line_to_index(card->output1_latch & 0x0f, HFDC_MAX_FLOPPY); |
| 461 | |
| 462 | if (disk_unit<0) |
| 463 | { |
| 464 | logerror("No unit selected\n"); |
| 465 | return NULL; |
| 466 | } |
| 467 | |
| 468 | return card->floppy_unit[disk_unit]; |
| 469 | } |
| 470 | |
| 471 | /* |
| 472 | Select the HFDC hard disk unit |
| 473 | This function implements the selection logic on the PCB (outside the |
| 474 | controller chip) |
| 475 | The HDC9234 allows up to four drives to be selected by the select lines. |
| 476 | This is not enough for controlling four floppy drives and three hard |
| 477 | drives, so the user programmable outputs are used to select the floppy |
| 478 | drives. This selection happens completely outside of the controller, |
| 479 | so we must implement it here. |
| 480 | */ |
| 481 | |
| 482 | static running_device *current_harddisk(running_device *controller) |
| 483 | { |
| 484 | ti99_hfdc_state *card = (ti99_hfdc_state*)downcast<legacy_device_base *>(controller->owner())->token(); |
| 485 | |
| 486 | int disk_unit = -1; |
| 487 | disk_unit = line_to_index((card->output1_latch>>4)&0x0f, HFDC_MAX_HARD); |
| 488 | |
| 489 | /* The HFDC does not use disk_unit=0. The first HD has index 1. */ |
| 490 | |
| 491 | if (disk_unit>=0) |
| 492 | return card->harddisk_unit[disk_unit]; |
| 493 | else |
| 494 | return NULL; |
| 495 | } |
| 496 | |
| 497 | static READ_LINE_DEVICE_HANDLER( auxbus_in ) |
| 498 | { |
| 499 | ti99_hfdc_state *card = get_safe_token(device->owner()); |
| 500 | running_device *drive; |
| 501 | UINT8 reply = 0; |
| 502 | |
| 503 | if (card->output1_latch==0) |
| 504 | { |
| 505 | return 0; /* is that the correct default value? */ |
| 506 | } |
| 507 | |
| 508 | /* If a floppy is selected, we have one line set among the four programmable outputs. */ |
| 509 | if ((card->output1_latch & 0x0f)!=0) |
| 510 | { |
| 511 | drive = current_floppy(device); |
| 512 | |
| 513 | /* Get floppy status. */ |
| 514 | if (floppy_drive_get_flag_state(drive, FLOPPY_DRIVE_INDEX) == FLOPPY_DRIVE_INDEX) |
| 515 | reply |= DS_INDEX; |
| 516 | if (floppy_tk00_r(drive) == CLEAR_LINE) |
| 517 | reply |= DS_TRK00; |
| 518 | if (floppy_wpt_r(drive) == CLEAR_LINE) |
| 519 | reply |= DS_WRPROT; |
| 520 | |
| 521 | /* if (image_exists(disk_img)) */ |
| 522 | |
| 523 | reply |= DS_READY; /* Floppies don't have a READY line; line is pulled up */ |
| 524 | reply |= DS_SKCOM; /* Same here. */ |
| 525 | } |
| 526 | else /* one of the first four lines must be selected */ |
| 527 | { |
| 528 | UINT8 state; |
| 529 | drive = current_harddisk(device); |
| 530 | state = ti99_mfm_harddisk_status(drive); |
| 531 | if (state & MFMHD_TRACK00) reply |= DS_TRK00; |
| 532 | if (state & MFMHD_SEEKCOMP) reply |= DS_SKCOM; |
| 533 | if (state & MFMHD_WRFAULT) reply |= DS_WRFAULT; |
| 534 | if (state & MFMHD_INDEX) reply |= DS_INDEX; |
| 535 | if (state & MFMHD_READY) reply |= DS_READY; |
| 536 | } |
| 537 | |
| 538 | return reply; |
| 539 | } |
| 540 | |
| 541 | /* |
| 542 | Read a byte from buffer in DMA mode |
| 543 | */ |
| 544 | static UINT8 hfdc_dma_read_callback(running_device *device) |
| 545 | { |
| 546 | ti99_hfdc_state *card = get_safe_token(device->owner()); |
| 547 | UINT8 value = card->ram[card->dma_address & 0x7fff]; |
| 548 | card->dma_address++; |
| 549 | return value; |
| 550 | } |
| 551 | |
| 552 | /* |
| 553 | Write a byte to buffer in DMA mode |
| 554 | */ |
| 555 | static void hfdc_dma_write_callback(running_device *device, UINT8 data) |
| 556 | { |
| 557 | ti99_hfdc_state *card = get_safe_token(device->owner()); |
| 558 | card->ram[card->dma_address & 0x7fff] = data; |
| 559 | card->dma_address++; |
| 560 | } |
| 561 | |
| 562 | /* |
| 563 | callback called at the end of the motor movement |
| 564 | */ |
| 565 | static TIMER_CALLBACK(motor_on_timer_callback) |
| 566 | { |
| 567 | // currently no activity here |
| 568 | } |
| 569 | |
| 570 | /* |
| 571 | Preliminary MFM harddisk interface. |
| 572 | */ |
| 573 | static void hfdc_harddisk_get_next_id(running_device *controller, int head, chrn_id_hd *id) |
| 574 | { |
| 575 | running_device *harddisk = current_harddisk(controller); |
| 576 | if (harddisk != NULL) |
| 577 | ti99_mfm_harddisk_get_next_id(harddisk, head, id); |
| 578 | } |
| 579 | |
| 580 | static void hfdc_harddisk_seek(running_device *controller, int direction) |
| 581 | { |
| 582 | running_device *harddisk = current_harddisk(controller); |
| 583 | if (harddisk != NULL) |
| 584 | ti99_mfm_harddisk_seek(harddisk, direction); |
| 585 | } |
| 586 | |
| 587 | static void hfdc_harddisk_read_sector(running_device *controller, int cylinder, int head, int sector, UINT8 **buf, int *sector_length) |
| 588 | { |
| 589 | running_device *harddisk = current_harddisk(controller); |
| 590 | if (harddisk != NULL) |
| 591 | ti99_mfm_harddisk_read_sector(harddisk, cylinder, head, sector, buf, sector_length); |
| 592 | } |
| 593 | |
| 594 | static void hfdc_harddisk_write_sector(running_device *controller, int cylinder, int head, int sector, UINT8 *buf, int sector_length) |
| 595 | { |
| 596 | running_device *harddisk = current_harddisk(controller); |
| 597 | if (harddisk != NULL) |
| 598 | ti99_mfm_harddisk_write_sector(harddisk, cylinder, head, sector, buf, sector_length); |
| 599 | } |
| 600 | |
| 601 | static void hfdc_harddisk_read_track(running_device *controller, int head, UINT8 **buffer, int *data_count) |
| 602 | { |
| 603 | running_device *harddisk = current_harddisk(controller); |
| 604 | if (harddisk != NULL) |
| 605 | ti99_mfm_harddisk_read_track(harddisk, head, buffer, data_count); |
| 606 | } |
| 607 | |
| 608 | static void hfdc_harddisk_write_track(running_device *controller, int head, UINT8 *buffer, int data_count) |
| 609 | { |
| 610 | running_device *harddisk = current_harddisk(controller); |
| 611 | if (harddisk != NULL) |
| 612 | ti99_mfm_harddisk_write_track(harddisk, head, buffer, data_count); |
| 613 | } |
| 614 | |
| 615 | const smc92x4_interface ti99_smc92x4_interface = |
| 616 | { |
| 617 | FALSE, /* do not use the full track layout */ |
| 618 | DEVCB_LINE(intrq_w), |
| 619 | DEVCB_LINE(dip_w), |
| 620 | DEVCB_HANDLER(auxbus_out), |
| 621 | DEVCB_LINE(auxbus_in), |
| 622 | |
| 623 | current_floppy, |
| 624 | hfdc_dma_read_callback, |
| 625 | hfdc_dma_write_callback, |
| 626 | |
| 627 | /* Preliminary MFM harddisk interface. */ |
| 628 | hfdc_harddisk_get_next_id, |
| 629 | hfdc_harddisk_seek, |
| 630 | hfdc_harddisk_read_sector, |
| 631 | hfdc_harddisk_write_sector, |
| 632 | hfdc_harddisk_read_track, |
| 633 | hfdc_harddisk_write_track |
| 634 | }; |
| 635 | |
| 636 | static ti99_peb_card hfdc_card = |
| 637 | { |
| 638 | data_r, |
| 639 | data_w, |
| 640 | cru_r, |
| 641 | cru_w, |
| 642 | |
| 643 | NULL, NULL, NULL, NULL |
| 644 | }; |
| 645 | |
| 646 | static DEVICE_START( ti99_hfdc ) |
| 647 | { |
| 648 | ti99_hfdc_state *card = (ti99_hfdc_state*)downcast<legacy_device_base *>(device)->token(); |
| 649 | |
| 650 | /* Resolve the callbacks to the PEB */ |
| 651 | peb_callback_if *topeb = (peb_callback_if *)device->baseconfig().static_config(); |
| 652 | devcb_resolve_write_line(&card->lines.inta, &topeb->inta, device); |
| 653 | // The HFDC does not use READY; it has on-board RAM for DMA |
| 654 | |
| 655 | card->motor_on_timer = timer_alloc(device->machine, motor_on_timer_callback, (void *)device); |
| 656 | card->ram = NULL; |
| 657 | card->ram_offset[0] = 0x2000; // static bank |
| 658 | card->controller = device->subdevice("smc92x4"); |
| 659 | card->clock = device->subdevice("mm58274c"); |
| 660 | astring *region = new astring(); |
| 661 | astring_assemble_3(region, device->tag(), ":", hfdc_region); |
| 662 | card->rom = memory_region(device->machine, astring_c(region)); |
| 663 | } |
| 664 | |
| 665 | static DEVICE_STOP( ti99_hfdc ) |
| 666 | { |
| 667 | ti99_hfdc_state *card = (ti99_hfdc_state*)downcast<legacy_device_base *>(device)->token(); |
| 668 | if (card->ram) free(card->ram); |
| 669 | } |
| 670 | |
| 671 | static DEVICE_RESET( ti99_hfdc ) |
| 672 | { |
| 673 | ti99_hfdc_state *card = (ti99_hfdc_state*)downcast<legacy_device_base *>(device)->token(); |
| 674 | const char *flopname[] = {FLOPPY_0, FLOPPY_1, FLOPPY_2, FLOPPY_3}; |
| 675 | const char *hardname[] = {MFMHD_0, MFMHD_1, MFMHD_2}; |
| 676 | |
| 677 | /* If the card is selected in the menu, register the card */ |
| 678 | if (input_port_read(device->machine, "DISKCTRL") == DISK_HFDC) |
| 679 | { |
| 680 | running_device *peb = device->owner(); |
| 681 | int success = mount_card(peb, device, &hfdc_card, get_pebcard_config(device)->slot); |
| 682 | if (!success) return; |
| 683 | |
| 684 | // Allocate 32 KiB for on-board buffer memory |
| 685 | if (card->ram==NULL) |
| 686 | { |
| 687 | card->ram = (UINT8*)malloc(32768); |
| 688 | // memset(card->ram, 0, 32768); |
| 689 | } |
| 690 | |
| 691 | if (input_port_read(device->machine, "HFDCDIP")&0x55) |
| 692 | ti99_set_80_track_drives(TRUE); |
| 693 | else |
| 694 | ti99_set_80_track_drives(FALSE); |
| 695 | |
| 696 | smc92x4_set_timing(card->controller, input_port_read(device->machine, "DRVSPD")); |
| 697 | |
| 698 | // Connect floppy drives to controller. Note that *this* is the |
| 699 | // controller, not the controller chip. The pcb contains a select |
| 700 | // logic. |
| 701 | int i; |
| 702 | for (i = 0; i < 4; i++) |
| 703 | { |
| 704 | card->floppy_unit[i] = device->siblingdevice(flopname[i]); |
| 705 | |
| 706 | if (card->floppy_unit[i]!=NULL) |
| 707 | { |
| 708 | floppy_drive_set_controller(card->floppy_unit[i], device); |
| 709 | // floppy_drive_set_index_pulse_callback(floppy_unit[i], smc92x4_index_pulse_callback); |
| 710 | floppy_drive_set_rpm(card->floppy_unit[i], 300.); |
| 711 | } |
| 712 | else |
| 713 | { |
| 714 | logerror("hfdc: Image %s is null\n", flopname[i]); |
| 715 | } |
| 716 | } |
| 717 | |
| 718 | /* In the HFDC ROM, WDSx selects drive x; drive 0 is not used */ |
| 719 | for (i = 1; i < 4; i++) |
| 720 | { |
| 721 | card->harddisk_unit[i] = device->siblingdevice(hardname[i-1]); |
| 722 | } |
| 723 | card->harddisk_unit[0] = NULL; |
| 724 | |
| 725 | floppy_type_t flop = FLOPPY_STANDARD_5_25_DSHD; |
| 726 | set_all_geometries(device, flop); |
| 727 | |
| 728 | card->strobe_motor = 0; |
| 729 | card->ram_offset[1] = card->ram_offset[2] = card->ram_offset[3] = 0; |
| 730 | card->rom_offset = 0; |
| 731 | } |
| 732 | // TODO: Check how to make use of floppy_mon_w(w->drive, CLEAR_LINE); |
| 733 | } |
| 734 | |
| 735 | static const mm58274c_interface floppy_mm58274c_interface = |
| 736 | { |
| 737 | 1, /* mode 24*/ |
| 738 | 0 /* first day of week */ |
| 739 | }; |
| 740 | |
| 741 | MACHINE_CONFIG_FRAGMENT( ti99_hfdc ) |
| 742 | MDRV_SMC92X4_ADD("smc92x4", ti99_smc92x4_interface ) |
| 743 | MDRV_MM58274C_ADD("mm58274c", floppy_mm58274c_interface) |
| 744 | MACHINE_CONFIG_END |
| 745 | |
| 746 | ROM_START( ti99_hfdc ) |
| 747 | ROM_REGION(0x4000, hfdc_region, 0) |
| 748 | ROM_LOAD_OPTIONAL("hfdc.bin", 0x0000, 0x4000, CRC(66fbe0ed) SHA1(11df2ecef51de6f543e4eaf8b2529d3e65d0bd59)) /* HFDC disk DSR ROM */ |
| 749 | ROM_END |
| 750 | |
| 751 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 752 | |
| 753 | #define DEVTEMPLATE_ID(p,s) p##ti99_hfdc##s |
| 754 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_ROM_REGION | DT_HAS_INLINE_CONFIG | DT_HAS_MACHINE_CONFIG |
| 755 | #define DEVTEMPLATE_NAME "Myarc Hard and Floppy Disk Controller Card" |
| 756 | #define DEVTEMPLATE_FAMILY "Peripheral expansion" |
| 757 | #include "devtempl.h" |
| 758 | |
| 759 | DEFINE_LEGACY_DEVICE( HFDC, ti99_hfdc ); |
| 760 | |
src/mess/machine/ti99/ti_rs232.c
| r0 | r9485 | |
| 1 | /* |
| 2 | TI-99 RS232 and Parallel interface card |
| 3 | Currently this emulation does not interact with the serial interface |
| 4 | on the host computer. Planned for a later release. |
| 5 | As most serial applications require a feedback from the receiver's side |
| 6 | the serial interface implementation will usually *not* work. |
| 7 | |
| 8 | TI99 RS232 card ('rs232') |
| 9 | TMS9902 ('rs232:tms9902_0') |
| 10 | TMS9902 ('rs232:tms9902_1') |
| 11 | TI99 RS232 attached serial device ('rs232:serdev0') |
| 12 | TI99 RS232 attached serial device ('rs232:serdev1') |
| 13 | TI99 PIO attached parallel device ('rs232:piodev') |
| 14 | |
| 15 | */ |
| 16 | #include "emu.h" |
| 17 | #include "peribox.h" |
| 18 | #include "machine/tms9902.h" |
| 19 | #include "ti_rs232.h" |
| 20 | |
| 21 | #define CRU_BASE 0x1300 |
| 22 | #define SENILA_0_BIT 0x80 |
| 23 | #define SENILA_1_BIT 0x40 |
| 24 | |
| 25 | #define ser_region "ser_region" |
| 26 | |
| 27 | // Second card: Change CRU base to 0x1500 |
| 28 | // #define CRU_BASE 0x1500 |
| 29 | |
| 30 | typedef ti99_pebcard_config ti_rs232_config; |
| 31 | |
| 32 | typedef struct _ti_rs232_state |
| 33 | { |
| 34 | running_device *uart0, *uart1, *serdev0, *serdev1, *piodev; |
| 35 | |
| 36 | int selected; |
| 37 | |
| 38 | // PIO flags |
| 39 | int pio_direction; // a.k.a. PIOOC pio in output mode if 0 |
| 40 | int pio_handshakeout; |
| 41 | int pio_handshakein; |
| 42 | int pio_spareout; |
| 43 | int pio_sparein; |
| 44 | int flag0; // spare |
| 45 | int cts1; // a.k.a. flag1 |
| 46 | int cts2; // a.k.a. flag2 |
| 47 | int led; // a.k.a. flag3 |
| 48 | int pio_out_buffer; |
| 49 | int pio_in_buffer; |
| 50 | int pio_readable; |
| 51 | int pio_writable; |
| 52 | int pio_write; // 1 if image is to be written to |
| 53 | |
| 54 | UINT8 *rom; |
| 55 | |
| 56 | /* Keeps the state of the SENILA line. */ |
| 57 | UINT8 senila; |
| 58 | |
| 59 | /* Keeps the value put on the bus when SENILA becomes active. */ |
| 60 | UINT8 ila; |
| 61 | |
| 62 | /* Callback lines to the main system. */ |
| 63 | ti99_peb_connect lines; |
| 64 | |
| 65 | } ti_rs232_state; |
| 66 | |
| 67 | INLINE ti_rs232_state *get_safe_token(running_device *device) |
| 68 | { |
| 69 | assert(device != NULL); |
| 70 | return (ti_rs232_state *)downcast<legacy_device_base *>(device)->token(); |
| 71 | } |
| 72 | |
| 73 | /**************************************************************************/ |
| 74 | /* Ports */ |
| 75 | |
| 76 | static DEVICE_START( ti99_rs232_serdev ) |
| 77 | { |
| 78 | } |
| 79 | |
| 80 | static DEVICE_START( ti99_piodev ) |
| 81 | { |
| 82 | } |
| 83 | |
| 84 | /* |
| 85 | Find the index of the image name. We assume the format |
| 86 | <name><number>, i.e. the number is the longest string from the right |
| 87 | which can be interpreted as a number. |
| 88 | */ |
| 89 | static int get_index_from_tagname(running_device *image) |
| 90 | { |
| 91 | const char *tag = image->tag(); |
| 92 | int maxlen = strlen(tag); |
| 93 | int i; |
| 94 | for (i=maxlen-1; i >=0; i--) |
| 95 | if (tag[i] < 48 || tag[i] > 57) break; |
| 96 | |
| 97 | return atoi(tag+i+1); |
| 98 | } |
| 99 | |
| 100 | /* |
| 101 | Initialize rs232 unit and open image |
| 102 | */ |
| 103 | static DEVICE_IMAGE_LOAD( ti99_rs232_serdev ) |
| 104 | { |
| 105 | running_device *tms9902 = NULL; |
| 106 | |
| 107 | // TODO: strcmp does not work this way (wrong constant or wrong function) |
| 108 | int devnumber = get_index_from_tagname(&image.device()); |
| 109 | if (devnumber==0) |
| 110 | { |
| 111 | tms9902 = image.device().siblingdevice("tms9902_0"); |
| 112 | } |
| 113 | else if (devnumber==1) |
| 114 | { |
| 115 | tms9902 = image.device().siblingdevice("tms9902_1"); |
| 116 | } |
| 117 | else |
| 118 | { |
| 119 | logerror("ti99/rs232: Could not find device tag number\n"); |
| 120 | return IMAGE_INIT_FAIL; |
| 121 | } |
| 122 | |
| 123 | if (image) |
| 124 | { |
| 125 | tms9902_set_cts(tms9902, 1); |
| 126 | tms9902_set_dsr(tms9902, 1); |
| 127 | } |
| 128 | else |
| 129 | { |
| 130 | tms9902_set_cts(tms9902, 0); |
| 131 | tms9902_set_dsr(tms9902, 0); |
| 132 | } |
| 133 | |
| 134 | return IMAGE_INIT_PASS; |
| 135 | } |
| 136 | |
| 137 | /* |
| 138 | Initialize pio unit and open image |
| 139 | */ |
| 140 | static DEVICE_IMAGE_LOAD( ti99_piodev ) |
| 141 | { |
| 142 | running_device *carddev = image.device().owner(); |
| 143 | ti_rs232_state *card = get_safe_token(carddev); |
| 144 | |
| 145 | /* tell whether the image is writable */ |
| 146 | card->pio_readable = !image.has_been_created(); |
| 147 | /* tell whether the image is writable */ |
| 148 | card->pio_writable = image.is_writable(); |
| 149 | |
| 150 | if (card->pio_write && card->pio_writable) |
| 151 | card->pio_handshakein = 0; /* receiver ready */ |
| 152 | else |
| 153 | card->pio_handshakein = 1; |
| 154 | |
| 155 | return IMAGE_INIT_PASS; |
| 156 | } |
| 157 | |
| 158 | /* |
| 159 | close a pio image |
| 160 | */ |
| 161 | static DEVICE_IMAGE_UNLOAD( ti99_piodev ) |
| 162 | { |
| 163 | running_device *carddev = image.device().owner(); |
| 164 | ti_rs232_state *card = get_safe_token(carddev); |
| 165 | |
| 166 | card->pio_writable = 0; |
| 167 | card->pio_handshakein = 1; /* receiver not ready */ |
| 168 | card->pio_sparein = 0; |
| 169 | } |
| 170 | |
| 171 | /****************************************************************************/ |
| 172 | |
| 173 | /* |
| 174 | CRU read |
| 175 | */ |
| 176 | static READ8Z_DEVICE_HANDLER( cru_rz ) |
| 177 | { |
| 178 | ti_rs232_state *card = get_safe_token(device); |
| 179 | |
| 180 | if ((offset & 0xff00)==CRU_BASE) |
| 181 | { |
| 182 | if ((offset & 0x00c0)==0x0000) |
| 183 | { |
| 184 | UINT8 reply = 0x00; |
| 185 | if (card->pio_direction) |
| 186 | reply |= 0x02; |
| 187 | if (card->pio_handshakein) |
| 188 | reply |= 0x04; |
| 189 | if (card->pio_sparein) |
| 190 | reply |= 0x08; |
| 191 | if (card->flag0) |
| 192 | reply |= 0x10; |
| 193 | if (card->cts1) |
| 194 | reply |= 0x20; |
| 195 | if (card->cts2) |
| 196 | reply |= 0x40; |
| 197 | if (card->led) |
| 198 | reply |= 0x80; |
| 199 | *value = reply; |
| 200 | return; |
| 201 | } |
| 202 | if ((offset & 0x00c0)==0x0040) |
| 203 | { |
| 204 | *value = tms9902_cru_r(card->uart0, offset>>4); |
| 205 | return; |
| 206 | } |
| 207 | if ((offset & 0x00c0)==0x0080) |
| 208 | { |
| 209 | *value = tms9902_cru_r(card->uart1, offset>>1); |
| 210 | return; |
| 211 | } |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | /* |
| 216 | CRU write |
| 217 | */ |
| 218 | static WRITE8_DEVICE_HANDLER( cru_w ) |
| 219 | { |
| 220 | ti_rs232_state *card = get_safe_token(device); |
| 221 | |
| 222 | if ((offset & 0xff00)==CRU_BASE) |
| 223 | { |
| 224 | if ((offset & 0x00c0)==0x0040) |
| 225 | { |
| 226 | tms9902_cru_w(card->uart0, offset>>1, data); |
| 227 | return; |
| 228 | } |
| 229 | if ((offset & 0x00c0)==0x0080) |
| 230 | { |
| 231 | tms9902_cru_w(card->uart1, offset, data); |
| 232 | return; |
| 233 | } |
| 234 | |
| 235 | device_image_interface *image = dynamic_cast<device_image_interface *>(card->piodev); |
| 236 | int bit = (offset & 0x00ff)>>1; |
| 237 | switch (bit) |
| 238 | { |
| 239 | case 0: |
| 240 | card->selected = data; |
| 241 | break; |
| 242 | |
| 243 | case 1: |
| 244 | card->pio_direction = data; |
| 245 | break; |
| 246 | |
| 247 | case 2: |
| 248 | if (data != card->pio_handshakeout) |
| 249 | { |
| 250 | card->pio_handshakeout = data; |
| 251 | if (card->pio_write && card->pio_writable && (!card->pio_direction)) |
| 252 | { /* PIO in output mode */ |
| 253 | if (!card->pio_handshakeout) |
| 254 | { /* write data strobe */ |
| 255 | /* write data and acknowledge */ |
| 256 | UINT8 buf = card->pio_out_buffer; |
| 257 | int ret = image->fwrite(&buf, 1); |
| 258 | if (ret) |
| 259 | card->pio_handshakein = 1; |
| 260 | } |
| 261 | else |
| 262 | { |
| 263 | /* end strobe */ |
| 264 | /* we can write some data: set receiver ready */ |
| 265 | card->pio_handshakein = 0; |
| 266 | } |
| 267 | } |
| 268 | if ((!card->pio_write) && card->pio_readable /*&& pio_direction*/) |
| 269 | { /* PIO in input mode */ |
| 270 | if (!card->pio_handshakeout) |
| 271 | { /* receiver ready */ |
| 272 | /* send data and strobe */ |
| 273 | UINT8 buf; |
| 274 | if (image->fread(&buf, 1)) |
| 275 | card->pio_in_buffer = buf; |
| 276 | card->pio_handshakein = 0; |
| 277 | } |
| 278 | else |
| 279 | { |
| 280 | /* data acknowledge */ |
| 281 | /* we can send some data: set transmitter ready */ |
| 282 | card->pio_handshakein = 1; |
| 283 | } |
| 284 | } |
| 285 | } |
| 286 | break; |
| 287 | |
| 288 | case 3: |
| 289 | card->pio_spareout = data; |
| 290 | break; |
| 291 | |
| 292 | case 4: |
| 293 | card->flag0 = data; |
| 294 | break; |
| 295 | |
| 296 | case 5: |
| 297 | card->cts1 = data; |
| 298 | break; |
| 299 | |
| 300 | case 6: |
| 301 | card->cts2 = data; |
| 302 | break; |
| 303 | |
| 304 | case 7: |
| 305 | card->led = data; |
| 306 | break; |
| 307 | } |
| 308 | return; |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | Memory read |
| 314 | */ |
| 315 | static READ8Z_DEVICE_HANDLER( data_rz ) |
| 316 | { |
| 317 | ti_rs232_state *card = get_safe_token(device); |
| 318 | |
| 319 | if (card->senila) |
| 320 | { |
| 321 | logerror("ti99/rs232: Sensing ILA\n"); |
| 322 | *value = card->ila; |
| 323 | // hope that the card rom was *not* selected, or we get two values |
| 324 | // on the address bus |
| 325 | |
| 326 | // Not sure whether this is right. Unfortunately, this is difficult |
| 327 | // to verify since the system does not make use of it, although the |
| 328 | // lines are there. |
| 329 | card->ila = 0; |
| 330 | } |
| 331 | if (((offset & 0xe000)==0x4000) && card->selected) |
| 332 | { |
| 333 | if ((offset & 0x1000)==0x0000) |
| 334 | { |
| 335 | *value = card->rom[offset&0x0fff]; |
| 336 | } |
| 337 | else |
| 338 | { |
| 339 | *value = card->pio_direction ? card->pio_in_buffer : card->pio_out_buffer; |
| 340 | } |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | /* |
| 345 | Memory write |
| 346 | */ |
| 347 | static WRITE8_DEVICE_HANDLER( data_w ) |
| 348 | { |
| 349 | ti_rs232_state *card = get_safe_token(device); |
| 350 | |
| 351 | if (((offset & 0xe000)==0x4000) && card->selected) |
| 352 | { |
| 353 | if ((offset & 0x1001)==0x1000) |
| 354 | { |
| 355 | card->pio_out_buffer = data; |
| 356 | } |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | /**************************************************************************/ |
| 361 | |
| 362 | /* |
| 363 | rs232 interrupt handler |
| 364 | */ |
| 365 | static TMS9902_INT_CALLBACK( int_callback_0 ) |
| 366 | { |
| 367 | running_device *carddev = device->owner(); |
| 368 | ti_rs232_state *card = get_safe_token(carddev); |
| 369 | if (INT) |
| 370 | card->ila |= SENILA_0_BIT; |
| 371 | else |
| 372 | card->ila &= ~SENILA_0_BIT; |
| 373 | |
| 374 | devcb_call_write_line(&card->lines.inta, INT); |
| 375 | } |
| 376 | |
| 377 | static TMS9902_INT_CALLBACK( int_callback_1 ) |
| 378 | { |
| 379 | running_device *carddev = device->owner(); |
| 380 | ti_rs232_state *card = get_safe_token(carddev); |
| 381 | if (INT) |
| 382 | card->ila |= SENILA_1_BIT; |
| 383 | else |
| 384 | card->ila &= ~SENILA_1_BIT; |
| 385 | |
| 386 | devcb_call_write_line(&card->lines.inta, INT); |
| 387 | } |
| 388 | |
| 389 | static TMS9902_XMIT_CALLBACK( xmit_callback_0 ) |
| 390 | { |
| 391 | UINT8 buf = data; |
| 392 | device_image_interface *rs232_fp = dynamic_cast<device_image_interface *>(device->siblingdevice("serdev0")); |
| 393 | |
| 394 | if (rs232_fp) |
| 395 | rs232_fp->fwrite(&buf, 1); |
| 396 | else |
| 397 | logerror("ti99/rs232: serdev0 not found\n"); |
| 398 | } |
| 399 | |
| 400 | static TMS9902_XMIT_CALLBACK( xmit_callback_1 ) |
| 401 | { |
| 402 | UINT8 buf = data; |
| 403 | device_image_interface *rs232_fp = dynamic_cast<device_image_interface *>(device->siblingdevice("serdev1")); |
| 404 | |
| 405 | if (rs232_fp) |
| 406 | rs232_fp->fwrite(&buf, 1); |
| 407 | else |
| 408 | logerror("ti99/rs232: serdev1 not found\n"); |
| 409 | } |
| 410 | |
| 411 | WRITE_LINE_DEVICE_HANDLER( senila ) |
| 412 | { |
| 413 | // put the value on the data bus. We store it in a state variable. |
| 414 | ti_rs232_state *card = (ti_rs232_state*)downcast<legacy_device_base *>(device)->token(); |
| 415 | card->senila = state; |
| 416 | } |
| 417 | |
| 418 | static const tms9902_interface tms9902_params_0 = |
| 419 | { |
| 420 | 3000000., /* clock rate (3MHz) */ |
| 421 | int_callback_0, /* called when interrupt pin state changes */ |
| 422 | NULL,//rts_callback, /* called when Request To Send pin state changes */ |
| 423 | NULL,//brk_callback, /* called when BReaK state changes */ |
| 424 | xmit_callback_0 /* called when a character is transmitted */ |
| 425 | }; |
| 426 | |
| 427 | static const tms9902_interface tms9902_params_1 = |
| 428 | { |
| 429 | 3000000., /* clock rate (3MHz) */ |
| 430 | int_callback_1, /* called when interrupt pin state changes */ |
| 431 | NULL,//rts_callback, /* called when Request To Send pin state changes */ |
| 432 | NULL,//brk_callback, /* called when BReaK state changes */ |
| 433 | xmit_callback_1 /* called when a character is transmitted */ |
| 434 | }; |
| 435 | |
| 436 | |
| 437 | static ti99_peb_card tirs232_card = |
| 438 | { |
| 439 | data_rz, data_w, // memory access read/write |
| 440 | cru_rz, cru_w, // CRU access |
| 441 | senila, NULL, // SENILA/B access |
| 442 | NULL, NULL // 16 bit access (none here) |
| 443 | }; |
| 444 | |
| 445 | /* |
| 446 | Defines the serial serdev. |
| 447 | */ |
| 448 | DEVICE_GET_INFO( ti99_rs232_serdev ) |
| 449 | { |
| 450 | switch ( state ) |
| 451 | { |
| 452 | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; |
| 453 | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_SERIAL; break; |
| 454 | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 455 | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break; |
| 456 | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break; |
| 457 | |
| 458 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ti99_rs232_serdev ); break; |
| 459 | // case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( ti99_rs232_serdev ); break; |
| 460 | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME( ti99_rs232_serdev ); break; |
| 461 | case DEVINFO_STR_NAME: strcpy( info->s, "TI99 RS232 attached serial device"); break; |
| 462 | case DEVINFO_STR_FAMILY: strcpy(info->s, "Peripheral expansion"); break; |
| 463 | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, ""); break; |
| 464 | case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; |
| 465 | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 466 | case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright the MESS Team"); break; |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | /* |
| 471 | Defines the PIO (parallel IO) serdev |
| 472 | */ |
| 473 | DEVICE_GET_INFO( ti99_piodev ) |
| 474 | { |
| 475 | switch ( state ) |
| 476 | { |
| 477 | case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break; |
| 478 | case DEVINFO_INT_IMAGE_TYPE: info->i = IO_PARALLEL; break; |
| 479 | case DEVINFO_INT_IMAGE_READABLE: info->i = 1; break; |
| 480 | case DEVINFO_INT_IMAGE_WRITEABLE: info->i = 1; break; |
| 481 | case DEVINFO_INT_IMAGE_CREATABLE: info->i = 1; break; |
| 482 | |
| 483 | case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( ti99_piodev ); break; |
| 484 | case DEVINFO_FCT_IMAGE_LOAD: info->f = (genf *) DEVICE_IMAGE_LOAD_NAME( ti99_piodev ); break; |
| 485 | case DEVINFO_FCT_IMAGE_UNLOAD: info->f = (genf *) DEVICE_IMAGE_UNLOAD_NAME(ti99_piodev ); break; |
| 486 | case DEVINFO_STR_NAME: strcpy( info->s, "TI99 PIO attached device"); break; |
| 487 | case DEVINFO_STR_FAMILY: strcpy(info->s, "Peripheral expansion"); break; |
| 488 | case DEVINFO_STR_IMAGE_FILE_EXTENSIONS: strcpy(info->s, ""); break; |
| 489 | case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; |
| 490 | case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; |
| 491 | case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright the MESS Team"); break; |
| 492 | } |
| 493 | } |
| 494 | |
| 495 | DECLARE_LEGACY_IMAGE_DEVICE(TI99_RS232, ti99_rs232_serdev); |
| 496 | DEFINE_LEGACY_IMAGE_DEVICE(TI99_RS232, ti99_rs232_serdev); |
| 497 | DECLARE_LEGACY_IMAGE_DEVICE(TI99_PIO, ti99_piodev); |
| 498 | DEFINE_LEGACY_IMAGE_DEVICE(TI99_PIO, ti99_piodev); |
| 499 | |
| 500 | static DEVICE_START( ti_rs232 ) |
| 501 | { |
| 502 | ti_rs232_state *card = (ti_rs232_state*)downcast<legacy_device_base *>(device)->token(); |
| 503 | peb_callback_if *topeb = (peb_callback_if *)device->baseconfig().static_config(); |
| 504 | |
| 505 | astring *region = new astring(); |
| 506 | astring_assemble_3(region, device->tag(), ":", ser_region); |
| 507 | card->rom = memory_region(device->machine, astring_c(region)); |
| 508 | devcb_resolve_write_line(&card->lines.inta, &topeb->inta, device); |
| 509 | // READY and INTB are not used |
| 510 | card->uart0 = device->subdevice("tms9902_0"); |
| 511 | card->uart1 = device->subdevice("tms9902_1"); |
| 512 | card->serdev0 = device->subdevice("serdev0"); |
| 513 | card->serdev1 = device->subdevice("serdev1"); |
| 514 | card->piodev = device->subdevice("piodev"); |
| 515 | } |
| 516 | |
| 517 | static DEVICE_STOP( ti_rs232 ) |
| 518 | { |
| 519 | } |
| 520 | |
| 521 | static DEVICE_RESET( ti_rs232 ) |
| 522 | { |
| 523 | ti_rs232_state *card = (ti_rs232_state*)downcast<legacy_device_base *>(device)->token(); |
| 524 | /* Register the card */ |
| 525 | running_device *peb = device->owner(); |
| 526 | |
| 527 | if (input_port_read(device->machine, "SERIAL")==SERIAL_TI) |
| 528 | { |
| 529 | int success = mount_card(peb, device, &tirs232_card, get_pebcard_config(device)->slot); |
| 530 | if (!success) return; |
| 531 | |
| 532 | card->pio_direction = 0; |
| 533 | card->pio_handshakeout = 0; |
| 534 | card->pio_spareout = 0; |
| 535 | card->flag0 = 0; |
| 536 | card->cts1 = 0; |
| 537 | card->cts2 = 0; |
| 538 | card->led = 0; |
| 539 | card->pio_write = 1; |
| 540 | } |
| 541 | } |
| 542 | |
| 543 | static MACHINE_CONFIG_FRAGMENT( ti_rs232 ) |
| 544 | MDRV_TMS9902_ADD("tms9902_0", tms9902_params_0) |
| 545 | MDRV_TMS9902_ADD("tms9902_1", tms9902_params_1) |
| 546 | MDRV_DEVICE_ADD("serdev0", TI99_RS232, 0) |
| 547 | MDRV_DEVICE_ADD("serdev1", TI99_RS232, 0) |
| 548 | MDRV_DEVICE_ADD("piodev", TI99_PIO, 0) |
| 549 | MACHINE_CONFIG_END |
| 550 | |
| 551 | ROM_START( ti_rs232 ) |
| 552 | ROM_REGION(0x1000, ser_region, 0) |
| 553 | ROM_LOAD_OPTIONAL("rs232.bin", 0x0000, 0x1000, CRC(eab382fb) SHA1(ee609a18a21f1a3ddab334e8798d5f2a0fcefa91)) /* TI rs232 DSR ROM */ |
| 554 | ROM_END |
| 555 | |
| 556 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 557 | |
| 558 | #define DEVTEMPLATE_ID(p,s) p##ti_rs232##s |
| 559 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_INLINE_CONFIG | DT_HAS_ROM_REGION | DT_HAS_MACHINE_CONFIG |
| 560 | #define DEVTEMPLATE_NAME "TI99 RS232/PIO interface card" |
| 561 | #define DEVTEMPLATE_FAMILY "Peripheral expansion" |
| 562 | #include "devtempl.h" |
| 563 | |
| 564 | DEFINE_LEGACY_DEVICE( TIRS232, ti_rs232 ); |
| 565 | |
src/mess/machine/ti99/peribox.c
| r0 | r9485 | |
| 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 | June 2010: Reimplemented using device structure (MZ) (obsoletes 99_peb.c)
|
| 25 | |
| 26 | Slots:
|
| 27 |
|
| 28 | REAR
|
| 29 | +8V 1||2 +8V
|
| 30 | GND 3||4 READY
|
| 31 | GND 5||6 RESET*
|
| 32 | GND 7||8 SCLK
|
| 33 | BOOTPG* 9||10 AUDIO
|
| 34 | RDBENA* 11||12 PCBEN
|
| 35 | HOLD* 13||14 IAQHDA
|
| 36 | SENILA* 15||16 SENILB*
|
| 37 | INTA* 17||18 INTB*
|
| 38 | D7 19||20 GND |
| 39 | D5 21||22 D6
|
| 40 | D3 23||24 D4
|
| 41 | D1 25||26 D2
|
| 42 | GND 27||28 D0
|
| 43 | A14 29||30 A15/CRUOUT
|
| 44 | A12 31||32 A13
|
| 45 | A10 33||34 A11
|
| 46 | A8 35||36 A9
|
| 47 | A6 37||38 A7
|
| 48 | A4 39||40 A5
|
| 49 | A2 41||42 A3
|
| 50 | A0 43||44 A1
|
| 51 | AMB 45||46 AMA |
| 52 | GND 47||48 AMC
|
| 53 | GND 49||50 CLKOUT*
|
| 54 | CRUCLK* 51||52 DBIN
|
| 55 | GND 53||54 WE*
|
| 56 | CRUIN 55||56 MEMEN*
|
| 57 | -16V 57||58 -16V
|
| 58 | +16V 59||60 +16V
|
| 59 | FRONT
|
| 60 | |
| 61 | < from box to console
|
| 62 | > from console into box
|
| 63 |
|
| 64 | READYA < System ready (goes to READY, 10K pull-up to +5V) A low level puts the cpu on hold.
|
| 65 | RESET* > System reset (active low) |
| 66 | SCLK nc System clock (not connected in interface card) |
| 67 | LCP* nc CPU indicator 1=TI99 0=2nd generation (not connected in interface card) |
| 68 | BOOTPG* nc ?
|
| 69 | AUDIO < Input audio (to AUDIOIN in console) |
| 70 | RDBENA* < Active low: enable flex cable data bus drivers (1K pull-up) |
| 71 | PCBEN H PCB enable for burn-in (always High) |
| 72 | HOLD* H Active low CPU hold request (always High) |
| 73 | IAQHDA nc IAQ [or] HOLDA (logical or) |
| 74 | SENILA* H(>) Interrupt level A sense enable (always High) |
| 75 | SENILB* H(>) Interrupt level B sense enable (always High) |
| 76 | INTA* < Interrupt level A (active low, goes to EXTINT*) |
| 77 | INTB* nc Interrupt level B (not used) |
| 78 | LOAD* nc Unmaskable interrupt (not carried by interface cable/card) |
| 79 | D0-D7 <> Data bus (D0 most significant) |
| 80 | A0-A15 > Address bus (A0 most sig; A15 also used as CRUOUT)
|
| 81 | AMA/B/C H Extra address bits (always high for TI-99/4x)
|
| 82 | CLKOUT* > Inverted PHI3 clock, from TIM9904 clock generator
|
| 83 | CRUCLK* > Inverted CRU clock, from TMS9900 CRUCLK pin
|
| 84 | DBIN > Active high = read memory. Drives the data bus buffers.
|
| 85 | WE* > Write Enable pulse (derived from TMS9900 WE* pin)
|
| 86 | CRUIN < CRU input bit to TMS9900
|
| 87 | MEMEN* > Memory access enable (active low) |
| 88 | |
| 89 | |
| 90 | The obscure SENILx lines:
|
| 91 | |
| 92 | With SENILA* going low, a value shall be put on the data bus, |
| 93 | representing the interrupt status bits. It can also be used to determine |
| 94 | the source of the interrupt: The RS232 card (in its standard configuration) |
| 95 | uses the data bus bits 0 and 1 for its two UARTs, while in the second |
| 96 | configuration, it uses bits 4 and 5. Still note that the TI-99/4x does not make |
| 97 | use of SENILA*.
|
| 98 | |
| 99 | SENILB* / INTB* was planned to be used with disk controllers. The PHP1240 disk
|
| 100 | controller puts the value of INTB* on D0 when SENILB* gets active (low) which
|
| 101 | reflects the INTRQ output pin of the WD1771. This signal is not used, however. |
| 102 | Instead, the disk controller combines DRQ and IRQ and makes use of a READY/HOLD
|
| 103 | control of the CPU.
|
| 104 | |
| 105 | Obviously, SENILA* and SENILB* should never be active at the same time, and |
| 106 | neither should any memory access to a card be active at the same time, for in |
| 107 | both cases, data bus lines may be set to different levels simultaneously. One
|
| 108 | possible application case is to turn off all cards in the box, lower SENILA*, |
| 109 | and then do a read access in the memory area of any card in the P-Box (e.g.
|
| 110 | 0x4000-0x5fff). Another possiblity is that the currently active card simply
|
| 111 | does not respond to a certain memory access, and in this case the status bits
|
| 112 | can be read.
|
| 113 | |
| 114 | Also note that the SENILx lines access all cards in parallel, meaning that there
|
| 115 | must be an agreement which cards may use which bits on the data bus. The lines
|
| 116 | do not depend on the card being active at that time. |
| 117 | */
|
| 118 | |
| 119 | #include "emu.h"
|
| 120 | #include "peribox.h"
|
| 121 | |
| 122 | /* Expansion cards */
|
| 123 | #include "p_code.h"
|
| 124 | #include "ti_fdc.h"
|
| 125 | #include "hfdc.h"
|
| 126 | #include "bwg.h"
|
| 127 | #include "ti_rs232.h"
|
| 128 | #include "tn_usbsm.h"
|
| 129 | #include "tn_ide.h"
|
| 130 | #include "hsgpl.h"
|
| 131 | #include "spchsyn.h"
|
| 132 | #include "evpc.h"
|
| 133 | |
| 134 | #include "devices/flopdrv.h"
|
| 135 | #include "devices/harddriv.h"
|
| 136 | #include "formats/ti99_dsk.h"
|
| 137 | #include "devices/ti99_hd.h"
|
| 138 | |
| 139 | static const floppy_config ti99_4_floppy_config =
|
| 140 | {
|
| 141 | DEVCB_NULL,
|
| 142 | DEVCB_NULL,
|
| 143 | DEVCB_NULL,
|
| 144 | DEVCB_NULL,
|
| 145 | DEVCB_NULL,
|
| 146 | FLOPPY_STANDARD_5_25_DSHD,
|
| 147 | FLOPPY_OPTIONS_NAME(ti99),
|
| 148 | NULL
|
| 149 | };
|
| 150 | |
| 151 | typedef struct _ti99_peb_slot
|
| 152 | {
|
| 153 | running_device *card;
|
| 154 | ti99_peb_card *intf;
|
| 155 | } ti99_peb_slot;
|
| 156 | |
| 157 | typedef struct _ti99_peb_state
|
| 158 | {
|
| 159 | ti99_peb_slot slot[MAXSLOTS];
|
| 160 | |
| 161 | // The next three members are used to emulate the collective lines
|
| 162 | // If any card goes L, L is propagated. We are using flags.
|
| 163 | UINT32 inta_state;
|
| 164 | UINT32 intb_state;
|
| 165 | UINT32 ready_state;
|
| 166 | |
| 167 | int highest;
|
| 168 |
|
| 169 | // Callback to the main system
|
| 170 | ti99_peb_connect lines;
|
| 171 |
|
| 172 | } ti99_peb_state;
|
| 173 | |
| 174 | INLINE ti99_peb_state *get_safe_token(running_device *device)
|
| 175 | {
|
| 176 | assert(device != NULL);
|
| 177 | return (ti99_peb_state *)downcast<legacy_device_base *>(device)->token();
|
| 178 | }
|
| 179 | |
| 180 | INLINE const ti99_peb_config *get_config(running_device *device)
|
| 181 | {
|
| 182 | assert(device != NULL);
|
| 183 | assert(device->type() == PBOX4 || device->type() == PBOX4A || device->type() == PBOX8 || device->type() == PBOXEV || device->type() == PBOXSG || device->type() == PBOXGEN);
|
| 184 | return (const ti99_peb_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config();
|
| 185 | }
|
| 186 | |
| 187 | /* Callbacks */
|
| 188 | static WRITE_LINE_DEVICE_HANDLER( inta )
|
| 189 | {
|
| 190 | int slot = get_pebcard_config(device)->slot;
|
| 191 | ti99_peb_state *peb = (ti99_peb_state*)downcast<legacy_device_base *>(device->owner())->token();
|
| 192 | if (state==TRUE)
|
| 193 | {
|
| 194 | // The flags are stored as inverted (inta_state=0 if all lines are H)
|
| 195 | peb->inta_state &= ~(1 << slot);
|
| 196 | }
|
| 197 | else
|
| 198 | {
|
| 199 | // 1 means: The card set it to L
|
| 200 | peb->inta_state |= (1 << slot);
|
| 201 | }
|
| 202 | // Call back the main system, setting the line to L if any PEB card set it to L
|
| 203 | // This is the case when inta_state is not 0
|
| 204 | devcb_call_write_line( &peb->lines.inta, (peb->inta_state == 0) );
|
| 205 | }
|
| 206 | |
| 207 | static WRITE_LINE_DEVICE_HANDLER( intb )
|
| 208 | {
|
| 209 | int slot = get_pebcard_config(device)->slot;
|
| 210 | ti99_peb_state *peb = (ti99_peb_state*)downcast<legacy_device_base *>(device->owner())->token();
|
| 211 | if (state==TRUE)
|
| 212 | peb->intb_state &= ~(1 << slot);
|
| 213 | else
|
| 214 | peb->intb_state |= (1 << slot);
|
| 215 | devcb_call_write_line( &peb->lines.intb, (peb->intb_state == 0) );
|
| 216 | }
|
| 217 | |
| 218 | static WRITE_LINE_DEVICE_HANDLER( ready )
|
| 219 | {
|
| 220 | int slot = get_pebcard_config(device)->slot;
|
| 221 | ti99_peb_state *peb = (ti99_peb_state*)downcast<legacy_device_base *>(device->owner())->token();
|
| 222 | if (state==TRUE)
|
| 223 | peb->ready_state &= ~(1 << slot);
|
| 224 | else
|
| 225 | peb->ready_state |= (1 << slot);
|
| 226 | devcb_call_write_line( &peb->lines.ready, (peb->ready_state == 0) );
|
| 227 | }
|
| 228 | |
| 229 | /*
|
| 230 | Declares the callbacks above that are called from the cards. This instance
|
| 231 | is passed to the cards with MDRV_PBOXCARD_ADD and must be handled as a
|
| 232 | static_config.
|
| 233 | */
|
| 234 | const peb_callback_if peb_callback = |
| 235 | { |
| 236 | DEVCB_LINE( inta ), |
| 237 | DEVCB_LINE( intb ), |
| 238 | DEVCB_LINE( ready ) |
| 239 | };
|
| 240 | |
| 241 | /*
|
| 242 | Mount the PEB card. Although possible, this is not done by the box itself;
|
| 243 | instead, the card must call this function on the box. The advantage is
|
| 244 | that we can assign the same slot to different cards which are selected
|
| 245 | by dip switches. Only the card which is selected will register itself; the
|
| 246 | other cards are stillborn.
|
| 247 | Returns TRUE when the card could be mounted.
|
| 248 | */
|
| 249 | int mount_card(running_device *device, running_device *cardptr, ti99_peb_card *cardintf, int slotindex)
|
| 250 | {
|
| 251 | ti99_peb_state *peb = get_safe_token(device);
|
| 252 | if ((slotindex > 0) && (slotindex < MAXSLOTS))
|
| 253 | {
|
| 254 | if (peb->slot[slotindex].card == NULL)
|
| 255 | {
|
| 256 | peb->slot[slotindex].card = cardptr;
|
| 257 | peb->slot[slotindex].intf = cardintf;
|
| 258 | if (slotindex > peb->highest) peb->highest = slotindex;
|
| 259 | logerror("ti99_peb: registering the card at slot index %d\n", slotindex);
|
| 260 | return TRUE;
|
| 261 | }
|
| 262 | else
|
| 263 | {
|
| 264 | logerror("ti99_peb: Tried to mount a second card in slot %d\n", slotindex);
|
| 265 | }
|
| 266 | }
|
| 267 | else
|
| 268 | logerror("ti99_peb: Invalid slot number %d\n", slotindex); |
| 269 | return FALSE;
|
| 270 | }
|
| 271 | |
| 272 | void unmount_card(running_device *device, int slotindex)
|
| 273 | {
|
| 274 | ti99_peb_state *peb = get_safe_token(device);
|
| 275 | peb->slot[slotindex].card = NULL;
|
| 276 | }
|
| 277 | |
| 278 | /***************************************************************************
|
| 279 | Memory and CRU access
|
| 280 | ***************************************************************************/
|
| 281 | |
| 282 | READ8Z_DEVICE_HANDLER( ti99_peb_data_rz )
|
| 283 | {
|
| 284 | ti99_peb_state *peb = get_safe_token(device);
|
| 285 | int i;
|
| 286 | for (i=1; i <= peb->highest; i++)
|
| 287 | {
|
| 288 | if ((peb->slot[i].card != NULL) && (*peb->slot[i].intf->card_read_data))
|
| 289 | (*peb->slot[i].intf->card_read_data)(peb->slot[i].card, offset, value);
|
| 290 | }
|
| 291 | }
|
| 292 | |
| 293 | WRITE8_DEVICE_HANDLER( ti99_peb_data_w )
|
| 294 | {
|
| 295 | ti99_peb_state *peb = get_safe_token(device);
|
| 296 | int i;
|
| 297 | for (i=1; i <= peb->highest; i++)
|
| 298 | {
|
| 299 | if ((peb->slot[i].card != NULL) && (*peb->slot[i].intf->card_write_data))
|
| 300 | (*peb->slot[i].intf->card_write_data)(peb->slot[i].card, offset, data);
|
| 301 | }
|
| 302 | }
|
| 303 | |
| 304 | READ8Z_DEVICE_HANDLER( ti99_peb_cru_rz )
|
| 305 | {
|
| 306 | ti99_peb_state *peb = get_safe_token(device);
|
| 307 | int i;
|
| 308 | for (i=1; i <= peb->highest; i++)
|
| 309 | {
|
| 310 | if ((peb->slot[i].card != NULL) && (*peb->slot[i].intf->card_read_cru))
|
| 311 | (*peb->slot[i].intf->card_read_cru)(peb->slot[i].card, offset, value);
|
| 312 | }
|
| 313 | }
|
| 314 | |
| 315 | WRITE8_DEVICE_HANDLER( ti99_peb_cru_w )
|
| 316 | {
|
| 317 | ti99_peb_state *peb = get_safe_token(device);
|
| 318 | int i;
|
| 319 | for (i=1; i <= peb->highest; i++)
|
| 320 | {
|
| 321 | if ((peb->slot[i].card != NULL) && (*peb->slot[i].intf->card_write_cru))
|
| 322 | (*peb->slot[i].intf->card_write_cru)(peb->slot[i].card, offset, data);
|
| 323 | }
|
| 324 | }
|
| 325 | |
| 326 | /*
|
| 327 | For SGCPU
|
| 328 | */
|
| 329 | READ16Z_DEVICE_HANDLER( ti99_peb_data16_rz )
|
| 330 | {
|
| 331 | ti99_peb_state *peb = get_safe_token(device);
|
| 332 | int i;
|
| 333 | for (i=1; i <= peb->highest; i++)
|
| 334 | {
|
| 335 | if ((peb->slot[i].card != NULL) && (*peb->slot[i].intf->card_read_data16))
|
| 336 | (*peb->slot[i].intf->card_read_data16)(peb->slot[i].card, offset, value);
|
| 337 | }
|
| 338 | }
|
| 339 | |
| 340 | /*
|
| 341 | For SGCPU
|
| 342 | */
|
| 343 | WRITE16_DEVICE_HANDLER( ti99_peb_data16_w )
|
| 344 | {
|
| 345 | ti99_peb_state *peb = get_safe_token(device);
|
| 346 | int i;
|
| 347 | for (i=1; i <= peb->highest; i++)
|
| 348 | {
|
| 349 | if ((peb->slot[i].card != NULL) && (*peb->slot[i].intf->card_write_data16))
|
| 350 | (*peb->slot[i].intf->card_write_data16)(peb->slot[i].card, offset, data);
|
| 351 | }
|
| 352 | }
|
| 353 | |
| 354 | /*
|
| 355 | SENILx
|
| 356 | TODO: Check whether device is the correct pointer (seems to be wrong here,
|
| 357 | the device is usually the caller)
|
| 358 | */
|
| 359 | WRITE_LINE_DEVICE_HANDLER( ti99_peb_senila )
|
| 360 | {
|
| 361 | ti99_peb_state *peb = get_safe_token(device);
|
| 362 | int i;
|
| 363 | for (i=1; i <= peb->highest; i++)
|
| 364 | {
|
| 365 | if ((peb->slot[i].card != NULL) && (*peb->slot[i].intf->senila))
|
| 366 | (*peb->slot[i].intf->senila)(peb->slot[i].card, state);
|
| 367 | }
|
| 368 | }
|
| 369 | |
| 370 | WRITE_LINE_DEVICE_HANDLER( ti99_peb_senilb)
|
| 371 | {
|
| 372 | ti99_peb_state *peb = get_safe_token(device);
|
| 373 | int i;
|
| 374 | for (i=1; i <= peb->highest; i++)
|
| 375 | {
|
| 376 | if ((peb->slot[i].card != NULL) && (*peb->slot[i].intf->senilb))
|
| 377 | (*peb->slot[i].intf->senilb)(peb->slot[i].card, state);
|
| 378 | }
|
| 379 | }
|
| 380 | |
| 381 | /***************************************************************************
|
| 382 | DEVICE FUNCTIONS
|
| 383 | ***************************************************************************/
|
| 384 | |
| 385 | static DEVICE_START( ti99_peb )
|
| 386 | {
|
| 387 | ti99_peb_state *peb = get_safe_token(device);
|
| 388 | const ti99_peb_config* pebconf = (const ti99_peb_config*)get_config(device);
|
| 389 | |
| 390 | for (int slotindex=0; slotindex < MAXSLOTS; slotindex++)
|
| 391 | unmount_card(device, slotindex);
|
| 392 |
|
| 393 | /* Resolve the callbacks to the console */
|
| 394 | devcb_write_line inta = DEVCB_LINE(pebconf->inta);
|
| 395 | devcb_write_line intb = DEVCB_LINE(pebconf->intb);
|
| 396 | devcb_write_line ready = DEVCB_LINE(pebconf->ready);
|
| 397 | |
| 398 | devcb_resolve_write_line(&peb->lines.ready, &ready, device);
|
| 399 | devcb_resolve_write_line(&peb->lines.inta, &inta, device);
|
| 400 | devcb_resolve_write_line(&peb->lines.intb, &intb, device);
|
| 401 | }
|
| 402 | |
| 403 | static DEVICE_STOP( ti99_peb )
|
| 404 | {
|
| 405 | logerror("ti99_peb: stop\n");
|
| 406 | }
|
| 407 | |
| 408 | static DEVICE_RESET( ti99_peb )
|
| 409 | {
|
| 410 | ti99_peb_state *peb = get_safe_token(device);
|
| 411 | logerror("ti99_peb: reset\n");
|
| 412 | peb->inta_state = 0;
|
| 413 | peb->intb_state = 0;
|
| 414 | peb->ready_state = 0;
|
| 415 | peb->highest = 0;
|
| 416 | for (int i=0; i < MAXSLOTS; i++) unmount_card(device, i);
|
| 417 | }
|
| 418 | |
| 419 | #define MDRV_PBOXCARD_ADD( _tag, _type, _slot ) \
|
| 420 | MDRV_DEVICE_ADD(_tag, _type, 0) \
|
| 421 | MDRV_DEVICE_CONFIG_DATA32( ti99_pebcard_config, slot, _slot ) \
|
| 422 | MDRV_DEVICE_CONFIG( peb_callback )
|
| 423 | |
| 424 | /*
|
| 425 | We're emulating a "super box" with more than 8 slots. The original
|
| 426 | box had 8 slots, but that's a bit too limiting.
|
| 427 | This may become organized differently in future versions; for now
|
| 428 | we'll go this way.
|
| 429 |
|
| 430 | Slot 0 is the "Flex cable interface" / SGCPU / Geneve
|
| 431 | We assign the same slot number for some cards that cannot be used
|
| 432 | in parallel anyway. The config switch will determine which one to use.
|
| 433 | Future works:
|
| 434 | // WHTech SCSI controller |
| 435 | // MDRV_PBOXCARD_ADD("SCSI", 4, scsi_intf)
|
| 436 | |
| 437 | // Horizon Ramdisk
|
| 438 | // MDRV_PBOXCARD_ADD("HRD", 10, ramdisk_intf)
|
| 439 | */ |
| 440 | |
| 441 | static MACHINE_CONFIG_FRAGMENT( ti99_peb )
|
| 442 | MDRV_PBOXCARD_ADD( "mem_ti32k", TI32KMEM, 1 ) |
| 443 | MDRV_PBOXCARD_ADD( "mem_sams1m", SAMSMEM, 1 )
|
| 444 | MDRV_PBOXCARD_ADD( "mem_myarc512", MYARCMEM, 1 ) |
| 445 | MDRV_PBOXCARD_ADD( "speech", TISPEECH, 2 ) |
| 446 | MDRV_PBOXCARD_ADD( "usbsmart", USBSMART, 3 )
|
| 447 | MDRV_PBOXCARD_ADD( "ide", TNIDE, 4 )
|
| 448 | MDRV_PBOXCARD_ADD( "hsgpl", HSGPL, 5 ) |
| 449 | MDRV_PBOXCARD_ADD( "rs232_card", TIRS232, 7 )
|
| 450 | MDRV_PBOXCARD_ADD( "ti_fdc", TIFDC, 8 )
|
| 451 | MDRV_PBOXCARD_ADD( "hfdc", HFDC, 8 )
|
| 452 | MDRV_PBOXCARD_ADD( "bwg", BWG, 8 )
|
| 453 |
|
| 454 | MDRV_FLOPPY_4_DRIVES_ADD(ti99_4_floppy_config)
|
| 455 | MDRV_MFMHD_3_DRIVES_ADD()
|
| 456 | MACHINE_CONFIG_END
|
| 457 | |
| 458 | static MACHINE_CONFIG_FRAGMENT( ti994a_peb )
|
| 459 | MDRV_PBOXCARD_ADD( "mem_ti32k", TI32KMEM, 1 ) |
| 460 | MDRV_PBOXCARD_ADD( "mem_sams1m", SAMSMEM, 1 )
|
| 461 | MDRV_PBOXCARD_ADD( "mem_myarc512", MYARCMEM, 1 ) |
| 462 | MDRV_PBOXCARD_ADD( "speech", TISPEECH, 2 ) |
| 463 | MDRV_PBOXCARD_ADD( "usbsmart", USBSMART, 3 )
|
| 464 | MDRV_PBOXCARD_ADD( "ide", TNIDE, 4 )
|
| 465 | MDRV_PBOXCARD_ADD( "hsgpl", HSGPL, 5 ) |
| 466 | MDRV_PBOXCARD_ADD( "p_code_card", PCODEN, 6 )
|
| 467 | MDRV_PBOXCARD_ADD( "rs232_card", TIRS232, 7 )
|
| 468 | MDRV_PBOXCARD_ADD( "ti_fdc", TIFDC, 8 )
|
| 469 | MDRV_PBOXCARD_ADD( "hfdc", HFDC, 8 )
|
| 470 | MDRV_PBOXCARD_ADD( "bwg", BWG, 8 )
|
| 471 |
|
| 472 | MDRV_FLOPPY_4_DRIVES_ADD(ti99_4_floppy_config)
|
| 473 | MDRV_MFMHD_3_DRIVES_ADD()
|
| 474 | MACHINE_CONFIG_END
|
| 475 | |
| 476 | static MACHINE_CONFIG_FRAGMENT( ti99ev_peb )
|
| 477 | MDRV_PBOXCARD_ADD( "mem_ti32k", TI32KMEM, 1 ) |
| 478 | MDRV_PBOXCARD_ADD( "mem_sams1m", SAMSMEM, 1 )
|
| 479 | MDRV_PBOXCARD_ADD( "mem_myarc512", MYARCMEM, 1 ) |
| 480 | MDRV_PBOXCARD_ADD( "speech", TISPEECH, 2 ) |
| 481 | MDRV_PBOXCARD_ADD( "usbsmart", USBSMART, 3 )
|
| 482 | MDRV_PBOXCARD_ADD( "ide", TNIDE, 4 )
|
| 483 | MDRV_PBOXCARD_ADD( "hsgpl", HSGPL, 5 ) |
| 484 | MDRV_PBOXCARD_ADD( "p_code_card", PCODEN, 6 )
|
| 485 | MDRV_PBOXCARD_ADD( "rs232_card", TIRS232, 7 )
|
| 486 | MDRV_PBOXCARD_ADD( "ti_fdc", TIFDC, 8 )
|
| 487 | MDRV_PBOXCARD_ADD( "hfdc", HFDC, 8 )
|
| 488 | MDRV_PBOXCARD_ADD( "bwg", BWG, 8 )
|
| 489 | MDRV_PBOXCARD_ADD( "evpc", EVPC, 9 )
|
| 490 |
|
| 491 | MDRV_FLOPPY_4_DRIVES_ADD(ti99_4_floppy_config)
|
| 492 | MDRV_MFMHD_3_DRIVES_ADD()
|
| 493 | MACHINE_CONFIG_END
|
| 494 | |
| 495 | |
| 496 | static MACHINE_CONFIG_FRAGMENT( ti998_peb )
|
| 497 | MDRV_PBOXCARD_ADD( "usbsmart", USBSMART, 1 )
|
| 498 | MDRV_PBOXCARD_ADD( "ide", TNIDE, 2 )
|
| 499 | MDRV_PBOXCARD_ADD( "rs232_card", TIRS232, 3 )
|
| 500 | MDRV_PBOXCARD_ADD( "ti_fdc", TIFDC, 8 )
|
| 501 | MDRV_PBOXCARD_ADD( "hfdc", HFDC, 8 )
|
| 502 | MDRV_PBOXCARD_ADD( "bwg", BWG, 8 )
|
| 503 |
|
| 504 | MDRV_FLOPPY_4_DRIVES_ADD(ti99_4_floppy_config)
|
| 505 | MDRV_MFMHD_3_DRIVES_ADD()
|
| 506 | MACHINE_CONFIG_END
|
| 507 | |
| 508 | static MACHINE_CONFIG_FRAGMENT( ti99sg_peb )
|
| 509 | MDRV_PBOXCARD_ADD( "speech", TISPEECH, 1 ) |
| 510 | MDRV_PBOXCARD_ADD( "usbsmart", USBSMART, 2 )
|
| 511 | MDRV_PBOXCARD_ADD( "ide", TNIDE, 3 )
|
| 512 | MDRV_PBOXCARD_ADD( "hsgpl", HSGPL, 4 ) |
| 513 | MDRV_PBOXCARD_ADD( "rs232_card", TIRS232, 6 )
|
| 514 | MDRV_PBOXCARD_ADD( "ti_fdc", TIFDC, 7 )
|
| 515 | MDRV_PBOXCARD_ADD( "hfdc", HFDC, 7 )
|
| 516 | MDRV_PBOXCARD_ADD( "bwg", BWG, 7 )
|
| 517 | MDRV_PBOXCARD_ADD( "evpc", EVPC, 8 )
|
| 518 | |
| 519 | MDRV_FLOPPY_4_DRIVES_ADD(ti99_4_floppy_config)
|
| 520 | MDRV_MFMHD_3_DRIVES_ADD()
|
| 521 | MACHINE_CONFIG_END
|
| 522 | |
| 523 | static MACHINE_CONFIG_FRAGMENT( geneve_peb )
|
| 524 | MDRV_PBOXCARD_ADD( "speech", TISPEECH, 1 ) |
| 525 | MDRV_PBOXCARD_ADD( "usbsmart", USBSMART, 2 )
|
| 526 | MDRV_PBOXCARD_ADD( "ide", TNIDE, 3 )
|
| 527 | MDRV_PBOXCARD_ADD( "rs232_card", TIRS232, 6 )
|
| 528 | MDRV_PBOXCARD_ADD( "ti_fdc", TIFDC, 7 )
|
| 529 | MDRV_PBOXCARD_ADD( "hfdc", HFDC, 7 )
|
| 530 | MDRV_PBOXCARD_ADD( "bwg", BWG, 7 )
|
| 531 | |
| 532 | MDRV_FLOPPY_4_DRIVES_ADD(ti99_4_floppy_config)
|
| 533 | MDRV_MFMHD_3_DRIVES_ADD()
|
| 534 | MACHINE_CONFIG_END
|
| 535 | |
| 536 | static const char DEVTEMPLATE_SOURCE[] = __FILE__;
|
| 537 | |
| 538 | #define DEVTEMPLATE_ID(p,s) p##ti99_peb##s
|
| 539 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_MACHINE_CONFIG | DT_HAS_INLINE_CONFIG
|
| 540 | #define DEVTEMPLATE_NAME "TI-99 Peripheral Expansion System (99/4)"
|
| 541 | #define DEVTEMPLATE_FAMILY "External devices"
|
| 542 | #include "devtempl.h"
|
| 543 | |
| 544 | #define DEVTEMPLATE_DERIVED_ID(p,s) p##ti994a_peb##s
|
| 545 | #define DEVTEMPLATE_DERIVED_FEATURES DT_HAS_MACHINE_CONFIG
|
| 546 | #define DEVTEMPLATE_DERIVED_NAME "TI-99 Peripheral Expansion System (99/4A)"
|
| 547 | #include "devtempl.h"
|
| 548 | |
| 549 | #define DEVTEMPLATE_DERIVED_ID(p,s) p##ti998_peb##s
|
| 550 | #define DEVTEMPLATE_DERIVED_FEATURES DT_HAS_MACHINE_CONFIG
|
| 551 | #define DEVTEMPLATE_DERIVED_NAME "TI-99 Peripheral Expansion System (99/8)"
|
| 552 | #include "devtempl.h"
|
| 553 | |
| 554 | #define DEVTEMPLATE_DERIVED_ID(p,s) p##ti99ev_peb##s
|
| 555 | #define DEVTEMPLATE_DERIVED_FEATURES DT_HAS_MACHINE_CONFIG
|
| 556 | #define DEVTEMPLATE_DERIVED_NAME "TI-99 Peripheral Expansion System (99/4a with EVPC)"
|
| 557 | #include "devtempl.h"
|
| 558 | |
| 559 | #define DEVTEMPLATE_DERIVED_ID(p,s) p##ti99sg_peb##s
|
| 560 | #define DEVTEMPLATE_DERIVED_FEATURES DT_HAS_MACHINE_CONFIG
|
| 561 | #define DEVTEMPLATE_DERIVED_NAME "TI99 Peripheral Expansion System (SGCPU)"
|
| 562 | #include "devtempl.h"
|
| 563 | |
| 564 | #define DEVTEMPLATE_DERIVED_ID(p,s) p##geneve_peb##s
|
| 565 | #define DEVTEMPLATE_DERIVED_FEATURES DT_HAS_MACHINE_CONFIG
|
| 566 | #define DEVTEMPLATE_DERIVED_NAME "TI99 Peripheral Expansion System (Geneve)"
|
| 567 | #include "devtempl.h"
|
| 568 | |
| 569 | DEFINE_LEGACY_DEVICE( PBOX4, ti99_peb );
|
| 570 | DEFINE_LEGACY_DEVICE( PBOX4A, ti994a_peb );
|
| 571 | DEFINE_LEGACY_DEVICE( PBOXEV, ti99ev_peb );
|
| 572 | DEFINE_LEGACY_DEVICE( PBOX8, ti998_peb );
|
| 573 | DEFINE_LEGACY_DEVICE( PBOXSG, ti99sg_peb );
|
| 574 | DEFINE_LEGACY_DEVICE( PBOXGEN, geneve_peb );
|
src/mess/machine/ti99/mapper8.c
| r0 | r9485 | |
| 1 | /*************************************************************************** |
| 2 | |
| 3 | TI-99/8 Address decoder and mapper |
| 4 | |
| 5 | This component implements the address decoder and mapper logic from the |
| 6 | TI-99/8 console. |
| 7 | |
| 8 | The TI-99/8 defines a "logical address map" with 64 KiB (according to the |
| 9 | 16 address bits) and a "physical address map" with 16 MiB (according to the |
| 10 | 24 address bits of the mapper). Note that the mapper only uses 16 outgoing |
| 11 | address lines and multiplexes the address bytes. |
| 12 | |
| 13 | Note: The TI-99/8's internal codename was "Armadillo" |
| 14 | |
| 15 | Initial setting of mapper (as defined in the power-up routine, TI-99/4A mode) |
| 16 | |
| 17 | 0 00ff0000 -> Unmapped; logical address 0000...0fff = ROM0 |
| 18 | 1 00ff0000 -> Unmapped; logical address 1000...1fff = ROM0 |
| 19 | 2 00000800 -> DRAM; 2000 = 000800, 2fff = 0017ff |
| 20 | 3 00001800 -> DRAM; 3000 = 001800, 3fff = 0027ff |
| 21 | 4 00ff4000 -> DSR space (internal / ioport) |
| 22 | 5 00ff5000 -> DSR space (internal / ioport) |
| 23 | 6 00ff6000 -> Cartridge space (6000..6fff) |
| 24 | 7 00ff7000 -> Cartridge space (7000..7fff) |
| 25 | 8 00ff0000 -> Unmapped; device ports (VDP) and SRAM |
| 26 | 9 00ff0000 -> Unmapped; device ports (Speech, GROM) |
| 27 | A 00002800 -> DRAM; a000 = 002800, afff = 0037ff |
| 28 | B 00003800 -> DRAM; b000 = 003800, bfff = 0047ff |
| 29 | C 00004800 -> DRAM; c000 = 004800, cfff = 0057ff |
| 30 | D 00005800 -> DRAM; d000 = 005800, dfff = 0067ff |
| 31 | E 00006800 -> DRAM; e000 = 006800, efff = 0077ff |
| 32 | F 00007800 -> DRAM; f000 = 007800, ffff = 0087ff |
| 33 | |
| 34 | Informations taken from |
| 35 | [1] ARMADILLO PRODUCT SPECIFICATIONS |
| 36 | [2] TI-99/8 Graphics Programming Language interpreter |
| 37 | |
| 38 | Michael Zapf, October 2010 |
| 39 | |
| 40 | ***************************************************************************/ |
| 41 | |
| 42 | #include "emu.h" |
| 43 | #include "peribox.h" |
| 44 | #include "mapper8.h" |
| 45 | |
| 46 | #define MAXLOGDEV 20 |
| 47 | #define MAXPHYSDEV 10 |
| 48 | |
| 49 | #define SRAM_SIZE 2048 |
| 50 | |
| 51 | #define DRAM_SIZE 65536 |
| 52 | |
| 53 | #define MAPPER_CRU_BASE 0x2700 |
| 54 | |
| 55 | #define MAP8_SRAM (void*)1 |
| 56 | #define MAP8_ROM0 (void*)2 |
| 57 | #define MAP8_ROM1 (void*)3 |
| 58 | #define MAP8_ROM1A (void*)4 |
| 59 | #define MAP8_DRAM (void*)5 |
| 60 | #define MAP8_INTS (void*)6 |
| 61 | |
| 62 | typedef struct _log_addressed_device |
| 63 | { |
| 64 | /* The device. */ |
| 65 | running_device *device; |
| 66 | |
| 67 | /* Mode. */ |
| 68 | int mode; |
| 69 | |
| 70 | /* Stop. If true, stop the address decoder here and do not look for parallel devices. */ |
| 71 | int stop; |
| 72 | |
| 73 | /* Address bits involved */ |
| 74 | UINT16 address_mask; |
| 75 | |
| 76 | /* Value of the address bits which are involved for selecting for read. */ |
| 77 | UINT16 select; |
| 78 | |
| 79 | /* Value of the address bits which are involved for selecting for write. */ |
| 80 | UINT16 write_select; |
| 81 | |
| 82 | /* Read access. */ |
| 83 | mapper_read_function read_byte; |
| 84 | |
| 85 | /* Write access. */ |
| 86 | mapper_write_function write_byte; |
| 87 | |
| 88 | } log_addressed_device; |
| 89 | |
| 90 | typedef struct _phys_addressed_device |
| 91 | { |
| 92 | /* The device. */ |
| 93 | running_device *device; |
| 94 | |
| 95 | /* Stop. If true, stop the address decoder here and do not look for parallel devices. */ |
| 96 | int stop; |
| 97 | |
| 98 | /* Address bits involved */ |
| 99 | UINT32 address_mask; |
| 100 | |
| 101 | /* Value of the address bits which are involved for selecting for read. */ |
| 102 | UINT32 select; |
| 103 | |
| 104 | /* Read access. */ |
| 105 | mapper_read_function read_byte; |
| 106 | |
| 107 | /* Write access. */ |
| 108 | mapper_write_function write_byte; |
| 109 | |
| 110 | } phys_addressed_device; |
| 111 | |
| 112 | typedef struct _mapper8_state |
| 113 | { |
| 114 | /* All devices that are attached to the 16-bit address bus. */ |
| 115 | log_addressed_device logcomp[MAXLOGDEV]; |
| 116 | |
| 117 | /* All devices that are attached to the 24-bit mapped address bus. */ |
| 118 | phys_addressed_device physcomp[MAXPHYSDEV]; |
| 119 | |
| 120 | /* The I/O port. */ |
| 121 | running_device *ioport; |
| 122 | |
| 123 | /* Select bit for the internal DSR. */ |
| 124 | int dsr_selected; |
| 125 | |
| 126 | /* 99/4A compatibility mode. Name is taken from the spec. If 1, 99/4A compatibility is active. */ |
| 127 | int CRUS; |
| 128 | |
| 129 | /* P-Code mode. Name is taken from the spec. If 0, P-Code libraries are available. */ |
| 130 | // May be read as "Pascal and Text-to-speech GROM libraries ENable" |
| 131 | // Note: this is negative logic. GROMs are present only for PTGEN=0 |
| 132 | int PTGEN; |
| 133 | |
| 134 | /* Address mapper registers. Each offset is selected by the first 4 bits |
| 135 | of the logical address. */ |
| 136 | UINT32 pas_offset[16]; |
| 137 | |
| 138 | /* SRAM area of the system. Directly connected to the address decoder. */ |
| 139 | UINT8 *sram; |
| 140 | |
| 141 | /* DRAM area of the system. Connected to the mapped address bus. */ |
| 142 | UINT8 *dram; |
| 143 | |
| 144 | /* ROM area of the system. Directly connected to the address decoder. */ |
| 145 | UINT8 *rom; |
| 146 | |
| 147 | /* Points to the next free slot in the list. */ |
| 148 | int logindex, physindex; |
| 149 | |
| 150 | } mapper8_state; |
| 151 | |
| 152 | |
| 153 | INLINE mapper8_state *get_safe_token(running_device *device) |
| 154 | { |
| 155 | assert(device != NULL); |
| 156 | assert(downcast<legacy_device_base *>(device)->token() != NULL); |
| 157 | return (mapper8_state *)downcast<legacy_device_base *>(device)->token(); |
| 158 | } |
| 159 | |
| 160 | static void mapper_mount_logical_device(running_device *mapperdev, running_device *busdevice, int mode, int stop, UINT16 address_mask, UINT16 address_bits, UINT16 write_sel, mapper_read_function read, mapper_write_function write) |
| 161 | { |
| 162 | mapper8_state *mapper = get_safe_token(mapperdev); |
| 163 | int index = mapper->logindex++; |
| 164 | if (index < MAXLOGDEV) |
| 165 | { |
| 166 | if (mapper->logcomp[index].device == NULL) |
| 167 | { |
| 168 | mapper->logcomp[index].device = busdevice; |
| 169 | mapper->logcomp[index].mode = mode; |
| 170 | mapper->logcomp[index].stop = stop; |
| 171 | |
| 172 | mapper->logcomp[index].address_mask = address_mask; |
| 173 | mapper->logcomp[index].select = address_bits; |
| 174 | mapper->logcomp[index].write_select = write_sel; |
| 175 | mapper->logcomp[index].read_byte = read; |
| 176 | mapper->logcomp[index].write_byte = write; |
| 177 | // printf("registering the device at logical position %d, device %lx\n", index, busdevice); |
| 178 | } |
| 179 | } |
| 180 | else |
| 181 | { |
| 182 | logerror("ti99_8_mapper: Tried to mount too many logical devices to the mapper. (Actually a bug.)\n"); |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | static void mapper_mount_physical_device(running_device *mapperdev, running_device *busdevice, int stop, UINT32 address_mask, UINT32 address_bits, mapper_read_function read, mapper_write_function write) |
| 187 | { |
| 188 | mapper8_state *mapper = get_safe_token(mapperdev); |
| 189 | int index = mapper->physindex++; |
| 190 | if (index < MAXPHYSDEV) |
| 191 | { |
| 192 | if (mapper->physcomp[index].device == NULL) |
| 193 | { |
| 194 | mapper->physcomp[index].device = busdevice; |
| 195 | mapper->physcomp[index].stop = stop; |
| 196 | mapper->physcomp[index].address_mask = address_mask; |
| 197 | mapper->physcomp[index].select = address_bits; |
| 198 | mapper->physcomp[index].read_byte = read; |
| 199 | mapper->physcomp[index].write_byte = write; |
| 200 | // printf("registering the device at physical position %d, device %lx\n", index, busdevice); |
| 201 | } |
| 202 | } |
| 203 | else |
| 204 | { |
| 205 | logerror("ti99_8_mapper: Tried to mount too many phys devices to the mapper. (Actually a bug.)\n"); |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | /*************************************************************************** |
| 210 | DEVICE ACCESSOR FUNCTIONS |
| 211 | ***************************************************************************/ |
| 212 | |
| 213 | static int check_logically_addressed_r( device_t *device, offs_t offset, UINT8 *value) |
| 214 | { |
| 215 | mapper8_state *mapper = get_safe_token(device); |
| 216 | int found = FALSE; |
| 217 | // printf("ti99_8: read access %04x ... ", offset); |
| 218 | // Look for components responding to the logical address |
| 219 | for (int i=0; i < MAXLOGDEV; i++) |
| 220 | { |
| 221 | log_addressed_device *adev = &mapper->logcomp[i]; |
| 222 | if (adev->device != NULL) |
| 223 | { |
| 224 | // Check for the mode |
| 225 | if (adev->mode == mapper->CRUS || (mapper->CRUS==0 && adev->mode==1 && mapper->PTGEN==FALSE)) |
| 226 | { |
| 227 | if ((offset & adev->address_mask)==adev->select) |
| 228 | { |
| 229 | if (adev->device == MAP8_SRAM) |
| 230 | { |
| 231 | *value = mapper->sram[offset & ~adev->address_mask]; |
| 232 | // printf("sram"); |
| 233 | found = TRUE; |
| 234 | } |
| 235 | else |
| 236 | { |
| 237 | if (adev->device == MAP8_ROM0) |
| 238 | { |
| 239 | // Starts at 0000 |
| 240 | *value = mapper->rom[offset & ~adev->address_mask]; |
| 241 | // printf("rom0"); |
| 242 | found = TRUE; |
| 243 | } |
| 244 | else |
| 245 | { |
| 246 | // device |
| 247 | if (adev->read_byte != NULL) |
| 248 | { |
| 249 | // printf("device "); |
| 250 | adev->read_byte(adev->device, offset, value); |
| 251 | found = TRUE; |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | } |
| 256 | } |
| 257 | } |
| 258 | if (found && adev->stop) break; |
| 259 | } |
| 260 | return found; |
| 261 | } |
| 262 | |
| 263 | static int check_logically_addressed_w( device_t *device, offs_t offset, UINT8 data) |
| 264 | { |
| 265 | mapper8_state *mapper = get_safe_token(device); |
| 266 | int found = FALSE; |
| 267 | |
| 268 | // printf("ti99_8: write access %04x ... ", offset); |
| 269 | for (int i=0; i < MAXLOGDEV; i++) |
| 270 | { |
| 271 | log_addressed_device *adev = &mapper->logcomp[i]; |
| 272 | if (adev->device != NULL) |
| 273 | { |
| 274 | // Check for the mode |
| 275 | if (adev->mode == mapper->CRUS || (mapper->CRUS==0 && adev->mode==1 && mapper->PTGEN==FALSE)) |
| 276 | { |
| 277 | if ((offset & adev->address_mask)==(adev->select | adev->write_select)) |
| 278 | { |
| 279 | if (adev->device == MAP8_SRAM) |
| 280 | { |
| 281 | mapper->sram[offset & ~adev->address_mask] = data; |
| 282 | // printf("sram"); |
| 283 | found = TRUE; |
| 284 | } |
| 285 | else |
| 286 | { |
| 287 | if (adev->device == MAP8_ROM0) |
| 288 | { |
| 289 | // printf("rom0 (ignored)"); |
| 290 | found = TRUE; |
| 291 | } |
| 292 | else |
| 293 | { |
| 294 | if (adev->write_byte != NULL) |
| 295 | { |
| 296 | // printf("device "); |
| 297 | adev->write_byte(adev->device, offset, data); |
| 298 | found = TRUE; |
| 299 | } |
| 300 | } |
| 301 | } |
| 302 | } |
| 303 | } |
| 304 | } |
| 305 | if (found && adev->stop) break; |
| 306 | } |
| 307 | return found; |
| 308 | } |
| 309 | |
| 310 | /* |
| 311 | Read access. |
| 312 | */ |
| 313 | READ8_DEVICE_HANDLER( ti99_mapper8_r ) |
| 314 | { |
| 315 | UINT8 value = 0; |
| 316 | mapper8_state *mapper = get_safe_token(device); |
| 317 | int found = FALSE; |
| 318 | |
| 319 | if (mapper->sram==NULL) // not initialized yet |
| 320 | return 0; |
| 321 | |
| 322 | found = check_logically_addressed_r(device, offset, &value); |
| 323 | |
| 324 | if (!found) |
| 325 | { |
| 326 | // printf(" not logical ..."); |
| 327 | // In that case, the address decoder could not find a suitable device. |
| 328 | // This means the logical address is transformed by the mapper. |
| 329 | UINT32 pas_address = mapper->pas_offset[(offset & 0xf000)>>12] + (offset & 0xfff); |
| 330 | // printf("%08x ", pas_address); |
| 331 | |
| 332 | // So now let's do the same as above with physical addresses |
| 333 | for (int i=0; i < MAXPHYSDEV; i++) |
| 334 | { |
| 335 | phys_addressed_device *adev = &mapper->physcomp[i]; |
| 336 | if (adev->device != NULL) |
| 337 | { |
| 338 | if ((pas_address & adev->address_mask)==adev->select) |
| 339 | { |
| 340 | if (adev->device == MAP8_DRAM) |
| 341 | { |
| 342 | value = mapper->dram[pas_address & ~adev->address_mask]; |
| 343 | found = TRUE; |
| 344 | // printf("dram"); |
| 345 | } |
| 346 | else |
| 347 | { |
| 348 | if (adev->device == MAP8_ROM1) |
| 349 | { |
| 350 | // Starts at 2000 in the image, 8K |
| 351 | value = mapper->rom[0x2000 + (pas_address & 0x1fff)]; |
| 352 | found = TRUE; |
| 353 | // printf("rom1"); |
| 354 | } |
| 355 | else |
| 356 | { |
| 357 | if (adev->device == MAP8_ROM1A) |
| 358 | { |
| 359 | // Starts at 6000 in the image, 8K |
| 360 | value = mapper->rom[0x6000 + (pas_address & 0x1fff)]; |
| 361 | found = TRUE; |
| 362 | // printf("rom1a"); |
| 363 | } |
| 364 | else |
| 365 | { |
| 366 | if (adev->device == MAP8_INTS) |
| 367 | { |
| 368 | // Interrupt sense |
| 369 | // printf("ti99_8: ILSENSE not implemented.\n"); |
| 370 | // printf("sense"); |
| 371 | found = TRUE; |
| 372 | } |
| 373 | else |
| 374 | { |
| 375 | // only one option left: cartridge port (aka grom port) |
| 376 | if (adev->read_byte != NULL) |
| 377 | { |
| 378 | // printf("pdevice "); |
| 379 | adev->read_byte(adev->device, pas_address, &value); |
| 380 | found = TRUE; |
| 381 | } |
| 382 | } |
| 383 | } |
| 384 | } |
| 385 | } |
| 386 | } |
| 387 | } |
| 388 | if (found && adev->stop) break; |
| 389 | } |
| 390 | |
| 391 | if (!found) |
| 392 | { |
| 393 | // Still not found? Then we route the physical address to the I/O port |
| 394 | // which has the peribox attached |
| 395 | // printf("not mapped ... ioport"); |
| 396 | if (mapper->ioport!=NULL) |
| 397 | ti99_peb_data_rz(mapper->ioport, pas_address, &value); |
| 398 | // printf("ti99_8: io read %04x = %02x\n", offset, value); |
| 399 | } |
| 400 | } |
| 401 | // printf(" %02x\n", value); |
| 402 | |
| 403 | return value; |
| 404 | } |
| 405 | |
| 406 | /* |
| 407 | Write access. |
| 408 | */ |
| 409 | WRITE8_DEVICE_HANDLER( ti99_mapper8_w ) |
| 410 | { |
| 411 | mapper8_state *mapper = get_safe_token(device); |
| 412 | int found = FALSE; |
| 413 | if (mapper->sram==NULL) |
| 414 | return; |
| 415 | // // printf("ti99_8: write %04x = %02x\n", offset, data); |
| 416 | |
| 417 | // Look for components responding to the logical address |
| 418 | found = check_logically_addressed_w(device, offset, data); |
| 419 | |
| 420 | if (!found) |
| 421 | { |
| 422 | // printf(" not logical ..."); |
| 423 | // In that case, the address decoder could not find a suitable device. |
| 424 | // This means the logical address is transformed by the mapper. |
| 425 | UINT32 pas_address = mapper->pas_offset[(offset & 0xf000)>>12] + (offset & 0xfff); |
| 426 | |
| 427 | // So now let's do the same as above with physical addresses |
| 428 | for (int i=0; i < MAXPHYSDEV; i++) |
| 429 | { |
| 430 | phys_addressed_device *adev = &mapper->physcomp[i]; |
| 431 | if (adev->device != NULL) |
| 432 | { |
| 433 | if ((pas_address & adev->address_mask)==adev->select) |
| 434 | { |
| 435 | if (adev->device == MAP8_DRAM) |
| 436 | { |
| 437 | mapper->dram[pas_address & ~adev->address_mask] = data; |
| 438 | // printf("dram base %08x", pas_address); |
| 439 | found = TRUE; |
| 440 | } |
| 441 | else |
| 442 | { |
| 443 | if (adev->device == MAP8_ROM1 || adev->device == MAP8_ROM1A) |
| 444 | { |
| 445 | // printf("rom1 (ignored)"); |
| 446 | found = TRUE; |
| 447 | } |
| 448 | else |
| 449 | { |
| 450 | if (adev->device == MAP8_INTS) |
| 451 | { |
| 452 | // Interrupt sense |
| 453 | // printf("ilsense (ignored)"); |
| 454 | found = TRUE; |
| 455 | } |
| 456 | else |
| 457 | { |
| 458 | // only one option left: cartridge port (aka grom port) |
| 459 | if (adev->write_byte != NULL) |
| 460 | { |
| 461 | // printf("pdevice "); |
| 462 | adev->write_byte(adev->device, pas_address, data); |
| 463 | found = TRUE; |
| 464 | } |
| 465 | } |
| 466 | } |
| 467 | } |
| 468 | } |
| 469 | } |
| 470 | if (found && adev->stop) break; |
| 471 | } |
| 472 | |
| 473 | if (!found) |
| 474 | { |
| 475 | // Still not found? Then we route the physical address to the I/O port |
| 476 | // which has the peribox attached |
| 477 | // printf("not mapped ... ioport"); |
| 478 | if (mapper->ioport!=NULL) |
| 479 | ti99_peb_data_w(mapper->ioport, pas_address, data); |
| 480 | // printf("ti99_8: io write %04x = %02x\n", offset, data); |
| 481 | } |
| 482 | } |
| 483 | // printf(" %02x\n", data); |
| 484 | } |
| 485 | |
| 486 | /* |
| 487 | Reconfigure mapper. Writing to this address copies the values in the |
| 488 | SRAM into the mapper and vice versa. |
| 489 | Format: |
| 490 | 0000 bbbl; bbb=bank, l=load |
| 491 | */ |
| 492 | WRITE8_DEVICE_HANDLER( ti99_mapreg_w ) |
| 493 | { |
| 494 | mapper8_state *mapper = get_safe_token(device); |
| 495 | |
| 496 | if ((data & 0xf0)==0) |
| 497 | { |
| 498 | int bankindx = (data & 0x0e)>>1; |
| 499 | if (data & 1) |
| 500 | { |
| 501 | // printf("mapper8: load from sram bank %02x", bankindx); |
| 502 | // Load from SRAM |
| 503 | for (int i=0; i < 16; i++) |
| 504 | { |
| 505 | int ptr = (bankindx << 6); |
| 506 | mapper->pas_offset[i] = |
| 507 | (mapper->sram[i*4 + ptr] << 24) |
| 508 | + (mapper->sram[i*4 + ptr+1] << 16) |
| 509 | + (mapper->sram[i*4 + ptr+2] << 8) |
| 510 | + (mapper->sram[i*4 + ptr+3]); |
| 511 | // // printf(" values (%04x) %02x %02x %02x %02x, ", i*4+ptr, mapper->sram[i*4 + ptr], mapper->sram[i*4 + ptr+1], mapper->sram[i*4 + ptr+2], mapper->sram[i*4 + ptr+3]); |
| 512 | } |
| 513 | } |
| 514 | else |
| 515 | { |
| 516 | // Store in SRAM |
| 517 | // printf("mapper8: copy to sram bank %02x", bankindx); |
| 518 | for (int i=0; i < 16; i++) |
| 519 | { |
| 520 | int ptr = (bankindx << 6); |
| 521 | mapper->sram[i*4 + ptr] = (mapper->pas_offset[i] >> 24)& 0xff; |
| 522 | mapper->sram[i*4 + ptr +1] = (mapper->pas_offset[i] >> 16)& 0xff; |
| 523 | mapper->sram[i*4 + ptr +2] = (mapper->pas_offset[i] >> 8)& 0xff; |
| 524 | mapper->sram[i*4 + ptr +3] = (mapper->pas_offset[i])& 0xff; |
| 525 | } |
| 526 | } |
| 527 | } |
| 528 | } |
| 529 | |
| 530 | /************************************************************************/ |
| 531 | |
| 532 | /* |
| 533 | Internal DSR |
| 534 | */ |
| 535 | READ8Z_DEVICE_HANDLER( ti998dsr_rz ) |
| 536 | { |
| 537 | mapper8_state *mapper = get_safe_token(device); |
| 538 | if (mapper->dsr_selected) |
| 539 | { |
| 540 | // Starts at 0x4000 in the image |
| 541 | *value = mapper->rom[0x4000 + (offset & 0x1fff)]; |
| 542 | } |
| 543 | else |
| 544 | { |
| 545 | if (mapper->ioport!=NULL) |
| 546 | ti99_peb_data_rz(mapper->ioport, 0x4000 + (offset & 0x1fff), value); |
| 547 | // printf("ti99_8: io1 read %04x = %02x\n", offset, *value); |
| 548 | } |
| 549 | } |
| 550 | |
| 551 | |
| 552 | /* |
| 553 | CRU handling. We handle the internal device at CRU address 0x2700 via |
| 554 | this mapper component. |
| 555 | */ |
| 556 | WRITE8_DEVICE_HANDLER( mapper8c_w ) |
| 557 | { |
| 558 | mapper8_state *mapper = get_safe_token(device); |
| 559 | if ((offset & 0xff00)==MAPPER_CRU_BASE) |
| 560 | { |
| 561 | int addr = offset & 0x00ff; |
| 562 | if (addr==0) |
| 563 | mapper->dsr_selected = data; |
| 564 | if (addr==2) |
| 565 | { |
| 566 | // TODO |
| 567 | // printf("ti-99/8: Hardware reset via CRU\n"); |
| 568 | } |
| 569 | } |
| 570 | } |
| 571 | |
| 572 | WRITE8_DEVICE_HANDLER( mapper8_CRUS_w ) |
| 573 | { |
| 574 | mapper8_state *mapper = get_safe_token(device); |
| 575 | mapper->CRUS = data; |
| 576 | // printf("ti99_8: CRUS = %02x\n", data); |
| 577 | } |
| 578 | |
| 579 | WRITE8_DEVICE_HANDLER( mapper8_PTGEN_w ) |
| 580 | { |
| 581 | mapper8_state *mapper = get_safe_token(device); |
| 582 | mapper->PTGEN = data; |
| 583 | // printf("ti99_8: PTGEN = %02x\n", data); |
| 584 | } |
| 585 | |
| 586 | /*************************************************************************** |
| 587 | DEVICE LIFECYCLE FUNCTIONS |
| 588 | ***************************************************************************/ |
| 589 | |
| 590 | static DEVICE_START( mapper8 ) |
| 591 | { |
| 592 | // // printf("Starting mapper\n"); |
| 593 | mapper8_state *mapper = get_safe_token(device); |
| 594 | mapper->sram = (UINT8*)malloc(SRAM_SIZE); |
| 595 | mapper->dram = (UINT8*)malloc(DRAM_SIZE); |
| 596 | mapper->rom = memory_region(device->machine, "maincpu"); |
| 597 | |
| 598 | int i = 0; |
| 599 | int done=FALSE; |
| 600 | |
| 601 | for (int index = 0; index < MAXLOGDEV; index++) |
| 602 | { |
| 603 | mapper->logcomp[index].device = NULL; |
| 604 | } |
| 605 | |
| 606 | for (int index = 0; index < MAXPHYSDEV; index++) |
| 607 | { |
| 608 | mapper->physcomp[index].device = NULL; |
| 609 | } |
| 610 | |
| 611 | // Need to connect the callbacks |
| 612 | mapper->ioport = device->siblingdevice("peribox"); |
| 613 | assert(mapper->ioport != NULL); |
| 614 | |
| 615 | mapper8_dev_config *cons = (mapper8_dev_config*)device->baseconfig().static_config(); |
| 616 | |
| 617 | mapper->logindex = 0; |
| 618 | mapper->physindex = 0; |
| 619 | |
| 620 | const char *list[6] = { SRAMNAME, ROM0NAME, ROM1NAME, ROM1ANAME, DRAMNAME, INTSNAME }; |
| 621 | while (!done) |
| 622 | { |
| 623 | void *dev = NULL; |
| 624 | if (cons[i].name==NULL) |
| 625 | done = TRUE; |
| 626 | else |
| 627 | { |
| 628 | for (int j=1; (j < 7) && (dev == NULL); j++) |
| 629 | { |
| 630 | // printf("%s, %s\n", cons[i].name, list[j-1]); |
| 631 | if (strcmp(cons[i].name, list[j-1])==0) |
| 632 | dev = (void *)j; |
| 633 | } |
| 634 | if (dev==NULL) |
| 635 | dev = device->machine->device(cons[i].name); |
| 636 | |
| 637 | if (dev!=NULL) |
| 638 | { |
| 639 | if (cons[i].mode != 3) |
| 640 | { |
| 641 | mapper_mount_logical_device(device, (running_device*)dev, cons[i].mode, cons[i].stop, (UINT16)cons[i].address_mask, (UINT16)cons[i].select_pattern, (UINT16)cons[i].write_select, cons[i].read, cons[i].write); |
| 642 | } |
| 643 | else |
| 644 | { |
| 645 | mapper_mount_physical_device(device, (running_device*)dev, cons[i].stop, cons[i].address_mask, cons[i].select_pattern, cons[i].read, cons[i].write); |
| 646 | } |
| 647 | } |
| 648 | else |
| 649 | logerror("mapper8: Device %s not found.\n", cons[i].name); |
| 650 | i++; |
| 651 | } |
| 652 | } |
| 653 | |
| 654 | mapper->dsr_selected = FALSE; |
| 655 | mapper->CRUS = TRUE; |
| 656 | mapper->PTGEN = TRUE; |
| 657 | } |
| 658 | |
| 659 | static DEVICE_STOP( mapper8 ) |
| 660 | { |
| 661 | // printf("Stopping mapper\n"); |
| 662 | mapper8_state *mapper = get_safe_token(device); |
| 663 | free(mapper->sram); |
| 664 | free(mapper->dram); |
| 665 | } |
| 666 | |
| 667 | static DEVICE_RESET( mapper8 ) |
| 668 | { |
| 669 | // printf("Resetting mapper\n"); |
| 670 | mapper8_state *mapper = get_safe_token(device); |
| 671 | |
| 672 | mapper->dsr_selected = FALSE; |
| 673 | mapper->CRUS = TRUE; |
| 674 | mapper->PTGEN = TRUE; |
| 675 | } |
| 676 | |
| 677 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 678 | |
| 679 | #define DEVTEMPLATE_ID(p,s) p##mapper8##s |
| 680 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET |
| 681 | #define DEVTEMPLATE_NAME "TI-99/8 Address Decoder and Mapper" |
| 682 | #define DEVTEMPLATE_FAMILY "Internal devices" |
| 683 | #include "devtempl.h" |
| 684 | |
| 685 | DEFINE_LEGACY_DEVICE( MAPPER8, mapper8 ); |
src/mess/machine/ti99/bwg.c
| r0 | r9485 | |
| 1 | /* |
| 2 | SNUG BwG Disk Controller |
| 3 | Based on WD1773 |
| 4 | Double Density, Double-sided |
| 5 | |
| 6 | * Supports Double Density. |
| 7 | * As this card includes its own RAM, it does not need to allocate a portion |
| 8 | of VDP RAM to store I/O buffers. |
| 9 | * Includes a MM58274C RTC. |
| 10 | * Support an additional floppy drive, for a total of 4 floppies. |
| 11 | |
| 12 | Reference: |
| 13 | * BwG Disketten-Controller: Beschreibung der DSR |
| 14 | <http://home.t-online.de/home/harald.glaab/snug/bwg.pdf> |
| 15 | |
| 16 | Michael Zapf, September 2010 |
| 17 | |
| 18 | FIXME: IO error 50, CALLs ok |
| 19 | */ |
| 20 | |
| 21 | #include "emu.h" |
| 22 | #include "peribox.h" |
| 23 | #include "bwg.h" |
| 24 | #include "machine/wd17xx.h" |
| 25 | #include "formats/ti99_dsk.h" |
| 26 | #include "devices/flopdrv.h" |
| 27 | #include "machine/mm58274c.h" |
| 28 | |
| 29 | #define CRU_BASE 0x1100 |
| 30 | |
| 31 | #define bwg_IRQ 1 |
| 32 | #define bwg_DRQ 2 |
| 33 | |
| 34 | #define bwg_region "bwg_region" |
| 35 | |
| 36 | typedef ti99_pebcard_config ti99_bwg_config; |
| 37 | |
| 38 | typedef struct _ti99_bwg_state |
| 39 | { |
| 40 | /* Holds the status of the DRQ and IRQ lines. */ |
| 41 | int DRQ_IRQ_status; |
| 42 | |
| 43 | /* When TRUE, card is accessible. Indicated by a LED. */ |
| 44 | int selected; |
| 45 | |
| 46 | /* When TRUE, keeps DVENA high. */ |
| 47 | int strobe_motor; |
| 48 | |
| 49 | /* Signal DVENA. When TRUE, makes some drive turning. */ |
| 50 | int DVENA; |
| 51 | |
| 52 | /* When TRUE the CPU is halted while DRQ/IRQ are true. */ |
| 53 | int hold; |
| 54 | |
| 55 | /* Indicates which drive has been selected. Values are 0, 1, 2, and 4. */ |
| 56 | // 000 = no drive |
| 57 | // 001 = drive 1 |
| 58 | // 010 = drive 2 |
| 59 | // 100 = drive 3 |
| 60 | int DSEL; |
| 61 | |
| 62 | /* Signal SIDSEL. 0 or 1, indicates the selected head. */ |
| 63 | int SIDE; |
| 64 | |
| 65 | /* ROM and RAM offset. */ |
| 66 | int rom_offset, ram_offset; |
| 67 | |
| 68 | /* Indicates whether the clock is mapped into the address space. */ |
| 69 | int rtc_enabled; |
| 70 | |
| 71 | /* count 4.23s from rising edge of motor_on */ |
| 72 | emu_timer *motor_on_timer; |
| 73 | |
| 74 | /* Link to the FDC1771 controller on the board. */ |
| 75 | running_device *controller; |
| 76 | |
| 77 | /* Link to the real-time clock on the board. */ |
| 78 | running_device *clock; |
| 79 | |
| 80 | /* DSR ROM */ |
| 81 | UINT8 *rom; |
| 82 | |
| 83 | /* On-board RAM */ |
| 84 | UINT8 *ram; |
| 85 | |
| 86 | /* DIP swiches */ |
| 87 | UINT8 dip1, dip2, dip34; |
| 88 | |
| 89 | /* Callback lines to the main system. */ |
| 90 | ti99_peb_connect lines; |
| 91 | |
| 92 | } ti99_bwg_state; |
| 93 | |
| 94 | INLINE ti99_bwg_state *get_safe_token(running_device *device) |
| 95 | { |
| 96 | assert(device != NULL); |
| 97 | return (ti99_bwg_state *)downcast<legacy_device_base *>(device)->token(); |
| 98 | } |
| 99 | |
| 100 | /* |
| 101 | Call this when the state of DSKhold or DRQ/IRQ or DVENA change |
| 102 | |
| 103 | Emulation is faulty because the CPU is actually stopped in the midst of |
| 104 | instruction, at the end of the memory access |
| 105 | |
| 106 | TODO: This has to be replaced by the proper READY handling that is already |
| 107 | prepared here. (Requires READY handling by the CPU.) |
| 108 | */ |
| 109 | static void bwg_handle_hold(running_device *device) |
| 110 | { |
| 111 | ti99_bwg_state *card = get_safe_token(device); |
| 112 | line_state state; |
| 113 | |
| 114 | if (card->hold && (!card->DRQ_IRQ_status) && card->DVENA) |
| 115 | state = ASSERT_LINE; |
| 116 | else |
| 117 | state = CLEAR_LINE; |
| 118 | |
| 119 | // TODO: use READY |
| 120 | cputag_set_input_line(device->machine, "maincpu", INPUT_LINE_HALT, state); |
| 121 | } |
| 122 | |
| 123 | /* |
| 124 | Resets the drive geometry. This is required because the heuristic of |
| 125 | the default implementation sets the drive geometry to the geometry |
| 126 | of the medium. |
| 127 | */ |
| 128 | static void set_geometry(running_device *drive, floppy_type_t type) |
| 129 | { |
| 130 | if (drive!=NULL) |
| 131 | floppy_drive_set_geometry(drive, type); |
| 132 | else |
| 133 | logerror("ti99/BwG: Drive not found\n"); |
| 134 | } |
| 135 | |
| 136 | /* Those defines are required because the WD17xx DEVICE_START implementation |
| 137 | assumes that the floppy devices are either at root level or at the parent |
| 138 | level. Our floppy devices, however, are at the grandparent level as seen from |
| 139 | the controller. */ |
| 140 | |
| 141 | #define PFLOPPY_0 "peribox:floppy0" |
| 142 | #define PFLOPPY_1 "peribox:floppy1" |
| 143 | #define PFLOPPY_2 "peribox:floppy2" |
| 144 | #define PFLOPPY_3 "peribox:floppy3" |
| 145 | |
| 146 | static void set_all_geometries(running_device *device, floppy_type_t type) |
| 147 | { |
| 148 | set_geometry(device->machine->device(PFLOPPY_0), type); |
| 149 | set_geometry(device->machine->device(PFLOPPY_1), type); |
| 150 | set_geometry(device->machine->device(PFLOPPY_2), type); |
| 151 | set_geometry(device->machine->device(PFLOPPY_3), type); |
| 152 | } |
| 153 | |
| 154 | |
| 155 | /*************************************************************************/ |
| 156 | |
| 157 | /* |
| 158 | Callback, called from the controller chip whenever DRQ/IRQ state change |
| 159 | */ |
| 160 | static WRITE_LINE_DEVICE_HANDLER( ti_bwg_intrq_w ) |
| 161 | { |
| 162 | running_device *carddev = device->owner(); |
| 163 | ti99_bwg_state *card = get_safe_token(carddev); |
| 164 | |
| 165 | if (state) |
| 166 | { |
| 167 | card->DRQ_IRQ_status |= bwg_IRQ; |
| 168 | // Note that INTB is actually not used in the TI-99 family. But the |
| 169 | // controller asserts the line nevertheless |
| 170 | devcb_call_write_line(&card->lines.intb, TRUE); |
| 171 | } |
| 172 | else |
| 173 | { |
| 174 | card->DRQ_IRQ_status &= ~bwg_IRQ; |
| 175 | devcb_call_write_line(&card->lines.intb, FALSE); |
| 176 | } |
| 177 | bwg_handle_hold(carddev); |
| 178 | } |
| 179 | |
| 180 | static WRITE_LINE_DEVICE_HANDLER( ti_bwg_drq_w ) |
| 181 | { |
| 182 | running_device *carddev = device->owner(); |
| 183 | ti99_bwg_state *card = get_safe_token(carddev); |
| 184 | |
| 185 | if (state) |
| 186 | card->DRQ_IRQ_status |= bwg_DRQ; |
| 187 | else |
| 188 | card->DRQ_IRQ_status &= ~bwg_DRQ; |
| 189 | |
| 190 | bwg_handle_hold(carddev); |
| 191 | } |
| 192 | |
| 193 | /* |
| 194 | callback called at the end of DVENA pulse |
| 195 | */ |
| 196 | static TIMER_CALLBACK(motor_on_timer_callback) |
| 197 | { |
| 198 | running_device *device = (running_device *)ptr; |
| 199 | ti99_bwg_state *card = get_safe_token(device); |
| 200 | card->DVENA = 0; |
| 201 | bwg_handle_hold(device); |
| 202 | } |
| 203 | |
| 204 | #if 0 |
| 205 | static WRITE_LINE_DEVICE_HANDLER( ti99_bwg_ready ) |
| 206 | { |
| 207 | // Caution: The device pointer passed to this function is the calling |
| 208 | // device. That is, if we want *this* device, we need to take the owner. |
| 209 | ti99_bwg_state *card = (ti99_bwg_state*)downcast<legacy_device_base *>(device->owner())->token(); |
| 210 | devcb_call_write_line( &card->lines.ready, state ); |
| 211 | } |
| 212 | #endif |
| 213 | |
| 214 | /* |
| 215 | CRU read handler. *=inverted. |
| 216 | bit 0: DSK4 connected* |
| 217 | bit 1: DSK1 connected* |
| 218 | bit 2: DSK2 connected* |
| 219 | bit 3: DSK3 connected* |
| 220 | bit 4: Dip 1 |
| 221 | bit 5: Dip 2 |
| 222 | bit 6: Dip 3 |
| 223 | bit 7: Dip 4 |
| 224 | */ |
| 225 | static READ8Z_DEVICE_HANDLER( cru_r ) |
| 226 | { |
| 227 | ti99_bwg_state *card = get_safe_token(device); |
| 228 | UINT8 reply = 0; |
| 229 | |
| 230 | if ((offset & 0xff00)==CRU_BASE) |
| 231 | { |
| 232 | if ((offset & 0x00ff)==0) |
| 233 | { |
| 234 | // Assume that we have 4 drives connected |
| 235 | // If we want to do that properly, we need to check the actually |
| 236 | // available drives (not the images!). But why should we connect less? |
| 237 | reply = 0x00; |
| 238 | |
| 239 | // DIP switches. Note that a closed switch means 0 |
| 240 | // xx01 1111 11 = nur dsk1; 10 = 1+2, 01=1/2/3, 00=1-4 |
| 241 | if (card->dip1) reply |= 0x10; |
| 242 | if (card->dip2) reply |= 0x20; |
| 243 | reply |= (card->dip34 << 6); |
| 244 | *value = ~reply; |
| 245 | } |
| 246 | else |
| 247 | *value = 0; |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | static WRITE8_DEVICE_HANDLER( cru_w ) |
| 252 | { |
| 253 | ti99_bwg_state *card = get_safe_token(device); |
| 254 | int drive, drivebit; |
| 255 | |
| 256 | if ((offset & 0xff00)==CRU_BASE) |
| 257 | { |
| 258 | int bit = (offset >> 1) & 0x0f; |
| 259 | switch (bit) |
| 260 | { |
| 261 | case 0: |
| 262 | /* (De)select the card. Indicated by a LED on the board. */ |
| 263 | card->selected = data; |
| 264 | break; |
| 265 | |
| 266 | case 1: |
| 267 | /* Activate motor */ |
| 268 | if (data && !card->strobe_motor) |
| 269 | { /* on rising edge, set motor_running for 4.23s */ |
| 270 | card->DVENA = TRUE; |
| 271 | bwg_handle_hold(device); |
| 272 | timer_adjust_oneshot(card->motor_on_timer, ATTOTIME_IN_MSEC(4230), 0); |
| 273 | } |
| 274 | card->strobe_motor = data; |
| 275 | break; |
| 276 | |
| 277 | case 2: |
| 278 | /* Set disk ready/hold (bit 2) */ |
| 279 | // 0: ignore IRQ and DRQ |
| 280 | // 1: TMS9900 is stopped until IRQ or DRQ are set |
| 281 | // OR the motor stops rotating - rotates for 4.23s after write |
| 282 | // to CRU bit 1 |
| 283 | // This is not emulated and could cause the TI99 to lock up |
| 284 | card->hold = data; |
| 285 | bwg_handle_hold(device); |
| 286 | break; |
| 287 | |
| 288 | case 4: |
| 289 | case 5: |
| 290 | case 6: |
| 291 | case 8: |
| 292 | /* Select drive 0-2 (DSK1-DSK3) (bits 4-6) */ |
| 293 | /* Select drive 3 (DSK4) (bit 8) */ |
| 294 | drive = (bit == 8) ? 3 : (bit - 4); /* drive # (0-3) */ |
| 295 | drivebit = 1<<drive; |
| 296 | |
| 297 | if (data) |
| 298 | { |
| 299 | if (!(card->DSEL & drivebit)) /* select drive */ |
| 300 | { |
| 301 | if (card->DSEL != 0) |
| 302 | logerror("ti99/BwG: Multiple drives selected, %02x\n", card->DSEL); |
| 303 | card->DSEL |= drivebit; |
| 304 | wd17xx_set_drive(card->controller, drive); |
| 305 | } |
| 306 | } |
| 307 | else |
| 308 | card->DSEL &= ~drivebit; |
| 309 | break; |
| 310 | |
| 311 | case 7: |
| 312 | /* Select side of disk (bit 7) */ |
| 313 | card->SIDE = data; |
| 314 | wd17xx_set_side(card->controller, card->SIDE); |
| 315 | break; |
| 316 | |
| 317 | case 10: |
| 318 | /* double density enable (active low) */ |
| 319 | wd17xx_dden_w(card->controller, data ? ASSERT_LINE : CLEAR_LINE); |
| 320 | break; |
| 321 | |
| 322 | case 11: |
| 323 | /* EPROM A13 */ |
| 324 | if (data) |
| 325 | card->rom_offset |= 0x2000; |
| 326 | else |
| 327 | card->rom_offset &= ~0x2000; |
| 328 | break; |
| 329 | |
| 330 | case 13: |
| 331 | /* RAM A10 */ |
| 332 | if (data) |
| 333 | card->ram_offset = 0x0400; |
| 334 | else |
| 335 | card->ram_offset = 0x0000; |
| 336 | break; |
| 337 | |
| 338 | case 14: |
| 339 | /* Override FDC with RTC (active high) */ |
| 340 | card->rtc_enabled = data; |
| 341 | break; |
| 342 | |
| 343 | case 15: |
| 344 | /* EPROM A14 */ |
| 345 | if (data) |
| 346 | card->rom_offset |= 0x4000; |
| 347 | else |
| 348 | card->rom_offset &= ~0x4000; |
| 349 | break; |
| 350 | |
| 351 | case 3: |
| 352 | case 9: |
| 353 | case 12: |
| 354 | /* Unused (bit 3, 9 & 12) */ |
| 355 | break; |
| 356 | } |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | /* |
| 361 | Read a byte |
| 362 | 4000 - 5bff: ROM (4 banks) |
| 363 | |
| 364 | rtc disabled: |
| 365 | 5c00 - 5fef: RAM |
| 366 | 5ff0 - 5fff: Controller (f0 = status, f2 = track, f4 = sector, f6 = data) |
| 367 | |
| 368 | rtc enabled: |
| 369 | 5c00 - 5fdf: RAM |
| 370 | 5fe0 - 5fff: Clock (even addr) |
| 371 | */ |
| 372 | |
| 373 | static READ8Z_DEVICE_HANDLER( data_r ) |
| 374 | { |
| 375 | ti99_bwg_state *card = get_safe_token(device); |
| 376 | |
| 377 | if (card->selected) |
| 378 | { |
| 379 | if ((offset & 0xe000)==0x4000) |
| 380 | { |
| 381 | // 010x xxxx xxxx xxxx |
| 382 | if ((offset & 0x1c00)==0x1c00) |
| 383 | { |
| 384 | // ...1 11xx xxxx xxxx |
| 385 | if (card->rtc_enabled) |
| 386 | { |
| 387 | if ((offset & 0x03e1)==0x03e0) |
| 388 | { |
| 389 | // .... ..11 111x xxx0 |
| 390 | *value = mm58274c_r(card->clock, (offset & 0x001e) >> 1); |
| 391 | } |
| 392 | else |
| 393 | { |
| 394 | *value = card->ram[card->ram_offset + (offset&0x03ff)]; |
| 395 | } |
| 396 | } |
| 397 | else |
| 398 | { |
| 399 | if ((offset & 0x03f9)==0x03f0) |
| 400 | { |
| 401 | // .... ..11 1111 0xx0 |
| 402 | // Note that the value is inverted again on the board, |
| 403 | // so we can drop the inversion |
| 404 | *value = wd17xx_r(card->controller, (offset >> 1)&0x03); |
| 405 | } |
| 406 | else |
| 407 | { |
| 408 | *value = card->ram[card->ram_offset + (offset&0x03ff)]; |
| 409 | } |
| 410 | } |
| 411 | } |
| 412 | else |
| 413 | { |
| 414 | *value = card->rom[card->rom_offset + (offset & 0x1fff)]; |
| 415 | } |
| 416 | } |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | /* |
| 421 | Write a byte |
| 422 | 4000 - 5bff: ROM, ignore write (4 banks) |
| 423 | |
| 424 | rtc disabled: |
| 425 | 5c00 - 5fef: RAM |
| 426 | 5ff0 - 5fff: Controller (f8 = command, fa = track, fc = sector, fe = data) |
| 427 | |
| 428 | rtc enabled: |
| 429 | 5c00 - 5fdf: RAM |
| 430 | 5fe0 - 5fff: Clock (even addr) |
| 431 | */ |
| 432 | static WRITE8_DEVICE_HANDLER( data_w ) |
| 433 | { |
| 434 | ti99_bwg_state *card = get_safe_token(device); |
| 435 | |
| 436 | if (card->selected) |
| 437 | { |
| 438 | if ((offset & 0xe000)==0x4000) |
| 439 | { |
| 440 | // 010x xxxx xxxx xxxx |
| 441 | if ((offset & 0x1c00)==0x1c00) |
| 442 | { |
| 443 | // ...1 11xx xxxx xxxx |
| 444 | if (card->rtc_enabled) |
| 445 | { |
| 446 | if ((offset & 0x03e1)==0x03e0) |
| 447 | { |
| 448 | // .... ..11 111x xxx0 |
| 449 | mm58274c_w(card->clock, (offset & 0x001e) >> 1, data); |
| 450 | } |
| 451 | else |
| 452 | { |
| 453 | card->ram[card->ram_offset + (offset&0x03ff)] = data; |
| 454 | } |
| 455 | } |
| 456 | else |
| 457 | { |
| 458 | if ((offset & 0x03f9)==0x03f8) |
| 459 | { |
| 460 | // .... ..11 1111 1xx0 |
| 461 | // Note that the value is inverted again on the board, |
| 462 | // so we can drop the inversion |
| 463 | wd17xx_w(card->controller, (offset >> 1)&0x03, data); |
| 464 | } |
| 465 | else |
| 466 | { |
| 467 | card->ram[card->ram_offset + (offset&0x03ff)] = data; |
| 468 | } |
| 469 | } |
| 470 | } |
| 471 | } |
| 472 | } |
| 473 | } |
| 474 | |
| 475 | static DEVICE_START( ti99_bwg ) |
| 476 | { |
| 477 | ti99_bwg_state *card = (ti99_bwg_state*)downcast<legacy_device_base *>(device)->token(); |
| 478 | |
| 479 | /* Resolve the callbacks to the PEB */ |
| 480 | peb_callback_if *topeb = (peb_callback_if *)device->baseconfig().static_config(); |
| 481 | devcb_resolve_write_line(&card->lines.ready, &topeb->ready, device); |
| 482 | |
| 483 | card->motor_on_timer = timer_alloc(device->machine, motor_on_timer_callback, (void *)device); |
| 484 | card->controller = device->subdevice("wd1773"); |
| 485 | card->clock = device->subdevice("mm58274c"); |
| 486 | astring *region = new astring(); |
| 487 | astring_assemble_3(region, device->tag(), ":", bwg_region); |
| 488 | card->rom = memory_region(device->machine, astring_c(region)); |
| 489 | card->ram = NULL; |
| 490 | } |
| 491 | |
| 492 | static DEVICE_STOP( ti99_bwg ) |
| 493 | { |
| 494 | logerror("ti99_bwg: stop\n"); |
| 495 | ti99_bwg_state *card = (ti99_bwg_state*)downcast<legacy_device_base *>(device)->token(); |
| 496 | if (card->ram) free(card->ram); |
| 497 | } |
| 498 | |
| 499 | static ti99_peb_card bwg_card = |
| 500 | { |
| 501 | data_r, |
| 502 | data_w, |
| 503 | cru_r, |
| 504 | cru_w, |
| 505 | NULL, NULL, NULL, NULL |
| 506 | }; |
| 507 | |
| 508 | static DEVICE_RESET( ti99_bwg ) |
| 509 | { |
| 510 | ti99_bwg_state *card = (ti99_bwg_state*)downcast<legacy_device_base *>(device)->token(); |
| 511 | |
| 512 | /* If the card is selected in the menu, register the card */ |
| 513 | if (input_port_read(device->machine, "DISKCTRL") == DISK_BWG) |
| 514 | { |
| 515 | running_device *peb = device->owner(); |
| 516 | int success = mount_card(peb, device, &bwg_card, get_pebcard_config(device)->slot); |
| 517 | if (!success) return; |
| 518 | |
| 519 | // Allocate 2 KiB for on-board buffer memory (6116 chip) |
| 520 | if (card->ram==NULL) |
| 521 | { |
| 522 | card->ram = (UINT8*)malloc(2048); |
| 523 | memset(card->ram, 0, 2048); |
| 524 | } |
| 525 | |
| 526 | card->dip1 = input_port_read(device->machine, "BWGDIP1"); |
| 527 | card->dip2 = input_port_read(device->machine, "BWGDIP2"); |
| 528 | card->dip34 = input_port_read(device->machine, "BWGDIP34"); |
| 529 | |
| 530 | card->DSEL = 0; |
| 531 | card->SIDE = 0; |
| 532 | card->DVENA = 0; |
| 533 | card->strobe_motor = 0; |
| 534 | |
| 535 | ti99_set_80_track_drives(FALSE); |
| 536 | |
| 537 | floppy_type_t flop = FLOPPY_STANDARD_5_25_DSDD_40; |
| 538 | set_all_geometries(device, flop); |
| 539 | |
| 540 | card->strobe_motor = 0; |
| 541 | card->ram_offset = 0; |
| 542 | card->rom_offset = 0; |
| 543 | card->rtc_enabled = 0; |
| 544 | |
| 545 | wd17xx_dden_w(card->controller, CLEAR_LINE); // Correct? |
| 546 | } |
| 547 | } |
| 548 | |
| 549 | const wd17xx_interface ti_wd17xx_interface = |
| 550 | { |
| 551 | DEVCB_NULL, |
| 552 | DEVCB_LINE(ti_bwg_intrq_w), |
| 553 | DEVCB_LINE(ti_bwg_drq_w), |
| 554 | { PFLOPPY_0, PFLOPPY_1, PFLOPPY_2, PFLOPPY_3 } |
| 555 | }; |
| 556 | |
| 557 | static const mm58274c_interface floppy_mm58274c_interface = |
| 558 | { |
| 559 | 1, /* mode 24*/ |
| 560 | 0 /* first day of week */ |
| 561 | }; |
| 562 | |
| 563 | MACHINE_CONFIG_FRAGMENT( ti99_bwg ) |
| 564 | MDRV_WD1773_ADD("wd1773", ti_wd17xx_interface ) |
| 565 | MDRV_MM58274C_ADD("mm58274c", floppy_mm58274c_interface) |
| 566 | MACHINE_CONFIG_END |
| 567 | |
| 568 | ROM_START( ti99_bwg ) |
| 569 | ROM_REGION(0x8000, bwg_region, 0) |
| 570 | ROM_LOAD_OPTIONAL("bwg.bin", 0x0000, 0x8000, CRC(06f1ec89) SHA1(6ad77033ed268f986d9a5439e65f7d391c4b7651)) /* BwG disk DSR ROM */ |
| 571 | ROM_END |
| 572 | |
| 573 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 574 | |
| 575 | #define DEVTEMPLATE_ID(p,s) p##ti99_bwg##s |
| 576 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_ROM_REGION | DT_HAS_INLINE_CONFIG | DT_HAS_MACHINE_CONFIG |
| 577 | #define DEVTEMPLATE_NAME "SNUG BwG Floppy Disk Controller Card" |
| 578 | #define DEVTEMPLATE_FAMILY "Peripheral expansion" |
| 579 | #include "devtempl.h" |
| 580 | |
| 581 | DEFINE_LEGACY_DEVICE( BWG, ti99_bwg ); |
| 582 | |
src/mess/machine/ti99/tiboard.c
| r0 | r9485 | |
| 1 | /*************************************************************************** |
| 2 | TI-99/4(A) and TI-99/8 main board |
| 3 | |
| 4 | Michael Zapf, October 2010 |
| 5 | TODO: Find out whether device can be determined more easily in CRU functions |
| 6 | ***************************************************************************/ |
| 7 | |
| 8 | #include "emu.h" |
| 9 | #include "tiboard.h" |
| 10 | #include "gromport.h" |
| 11 | #include "video/tms9928a.h" |
| 12 | #include "handset.h" |
| 13 | #include "mecmouse.h" |
| 14 | #include "devices/cassette.h" |
| 15 | #include "mapper8.h" |
| 16 | |
| 17 | typedef struct _tiboard_state |
| 18 | { |
| 19 | /* Mode: Which console do we have? */ |
| 20 | int mode; |
| 21 | |
| 22 | /* Keyboard support */ |
| 23 | int keyCol; |
| 24 | int alphaLockLine; |
| 25 | |
| 26 | /* Devices */ |
| 27 | running_device *cpu; |
| 28 | running_device *video; |
| 29 | running_device *tms9901; |
| 30 | running_device *peribox; |
| 31 | running_device *sound; |
| 32 | |
| 33 | running_device *handset; |
| 34 | running_device *mecmouse; |
| 35 | running_device *gromport; |
| 36 | |
| 37 | } tiboard_state; |
| 38 | |
| 39 | static const char *keynames[] = { "KEY0", "KEY1", "KEY2", "KEY3" }; |
| 40 | static const char *keynames8[] = { |
| 41 | "KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5", "KEY6", "KEY7", |
| 42 | "KEY8", "KEY9", "KEY10", "KEY11", "KEY12", "KEY13", "KEY14", "KEY15" |
| 43 | }; |
| 44 | |
| 45 | INLINE tiboard_state *get_safe_token(running_device *device) |
| 46 | { |
| 47 | assert(device != NULL); |
| 48 | assert(downcast<legacy_device_base *>(device)->token() != NULL); |
| 49 | return (tiboard_state *)downcast<legacy_device_base *>(device)->token(); |
| 50 | } |
| 51 | |
| 52 | INLINE const tiboard_config *get_config(running_device *device) |
| 53 | { |
| 54 | assert(device != NULL); |
| 55 | assert(device->type()==TIBOARD); |
| 56 | return (const tiboard_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config(); |
| 57 | } |
| 58 | |
| 59 | /**************************************************************************** |
| 60 | TMS9901 and attached devices handling |
| 61 | ****************************************************************************/ |
| 62 | /* |
| 63 | VBL interrupt (mmm... actually, it happens when the beam enters the lower |
| 64 | border, so it is not a genuine VBI, but who cares ?) |
| 65 | */ |
| 66 | INTERRUPT_GEN( ti99_vblank_interrupt ) |
| 67 | { |
| 68 | running_device *dev = device->machine->device("ti_board"); |
| 69 | tiboard_state *board = get_safe_token(dev); |
| 70 | |
| 71 | TMS9928A_interrupt(device->machine); |
| 72 | |
| 73 | if (board->handset != NULL) |
| 74 | ti99_handset_task(board->handset); |
| 75 | if (board->mecmouse != NULL) |
| 76 | mecmouse_poll(board->mecmouse); |
| 77 | } |
| 78 | |
| 79 | /* |
| 80 | TI99/4x-specific tms9901 I/O handlers |
| 81 | |
| 82 | See mess/machine/tms9901.c for generic tms9901 CRU handlers. |
| 83 | |
| 84 | BTW, although TMS9900 is generally big-endian, it is little endian as far as CRU is |
| 85 | concerned. (i.e. bit 0 is the least significant) |
| 86 | |
| 87 | KNOWN PROBLEMS: |
| 88 | * a read or write to bits 16-31 causes TMS9901 to quit timer mode. The problem is: |
| 89 | on a TI99/4A, any memory access causes a dummy CRU read. Therefore, TMS9901 can quit |
| 90 | timer mode even though the program did not explicitely ask... and THIS is impossible |
| 91 | to emulate efficiently (we would have to check every memory operation). |
| 92 | */ |
| 93 | /* |
| 94 | TMS9901 interrupt handling on a TI99/4(a). |
| 95 | |
| 96 | TI99/4(a) uses the following interrupts: |
| 97 | INT1: external interrupt (used by RS232 controller, for instance) |
| 98 | INT2: VDP interrupt |
| 99 | TMS9901 timer interrupt (overrides INT3) |
| 100 | INT12: handset interrupt (only on a TI-99/4 with the handset prototypes) |
| 101 | |
| 102 | Three (occasionally four) interrupts are used by the system (INT1, INT2, |
| 103 | timer, and INT12 on a TI-99/4 with remote handset prototypes), out of 15/16 |
| 104 | possible interrupts. Keyboard pins can be used as interrupt pins, too, but |
| 105 | this is not emulated (it's a trick, anyway, and I don't know any program |
| 106 | which uses it). |
| 107 | |
| 108 | When an interrupt line is set (and the corresponding bit in the interrupt mask is set), |
| 109 | a level 1 interrupt is requested from the TMS9900. This interrupt request lasts as long as |
| 110 | the interrupt pin and the revelant bit in the interrupt mask are set. |
| 111 | |
| 112 | TIMER interrupts are kind of an exception, since they are not associated with an external |
| 113 | interrupt pin, and I guess it takes a write to the 9901 CRU bit 3 ("SBO 3") to clear |
| 114 | the pending interrupt (or am I wrong once again ?). |
| 115 | |
| 116 | nota: |
| 117 | All interrupt routines notify (by software) the TMS9901 of interrupt |
| 118 | recognition (with a "SBO n"). However, unless I am missing something, this |
| 119 | has absolutely no consequence on the TMS9901 (except for the TIMER |
| 120 | interrupt routine), and interrupt routines would work fine without this |
| 121 | SBO instruction. This is quite weird. Maybe the interrupt recognition |
| 122 | notification is needed on TMS9985, or any other weird variant of TMS9900 |
| 123 | (how about the TI-99 development system connected to a TI990/10?). |
| 124 | */ |
| 125 | |
| 126 | /* |
| 127 | set the state of int2 (called by the tms9928 core) |
| 128 | */ |
| 129 | void tms9901_set_int2(running_machine *machine, int state) |
| 130 | { |
| 131 | tms9901_set_single_int(machine->device("tms9901"), 2, state); |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | Called by the 9901 core whenever the state of INTREQ and IC0-3 changes |
| 136 | */ |
| 137 | static TMS9901_INT_CALLBACK( tms9901_interrupt_callback ) |
| 138 | { |
| 139 | if (intreq) |
| 140 | { |
| 141 | // On TI99, TMS9900 IC0-3 lines are not connected to TMS9901, |
| 142 | // but hard-wired to force level 1 interrupts |
| 143 | cputag_set_input_line_and_vector(device->machine, "maincpu", 0, ASSERT_LINE, 1); /* interrupt it, baby */ |
| 144 | } |
| 145 | else |
| 146 | { |
| 147 | cputag_set_input_line(device->machine, "maincpu", 0, CLEAR_LINE); |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | /* |
| 152 | ti99_handset_set_ack() |
| 153 | Handler for tms9901 P0 pin (handset data acknowledge) |
| 154 | */ |
| 155 | static WRITE8_DEVICE_HANDLER( ti99_handset_set_ack ) |
| 156 | { |
| 157 | running_device *dev = device->machine->device("ti_board"); |
| 158 | tiboard_state *board = get_safe_token(dev); |
| 159 | |
| 160 | if (board->handset != NULL) |
| 161 | ti99_handset_set_acknowledge(board->handset, data); |
| 162 | } |
| 163 | |
| 164 | /* |
| 165 | Read pins INT3*-INT7* of TI99's 9901. |
| 166 | |
| 167 | signification: |
| 168 | (bit 1: INT1 status) |
| 169 | (bit 2: INT2 status) |
| 170 | bit 3-7: keyboard status bits 0 to 4 |
| 171 | */ |
| 172 | static READ8_DEVICE_HANDLER( ti99_R9901_0 ) |
| 173 | { |
| 174 | int answer = 0; |
| 175 | running_device *dev = device->machine->device("ti_board"); |
| 176 | tiboard_state *board = get_safe_token(dev); |
| 177 | |
| 178 | if (board->keyCol == 7) |
| 179 | { |
| 180 | if (board->handset != NULL) answer = (ti99_handset_poll_bus(board->handset) << 3); |
| 181 | answer |= 0x80; |
| 182 | } |
| 183 | else |
| 184 | { |
| 185 | if ((board->mecmouse != NULL) && (board->keyCol == 6)) |
| 186 | { |
| 187 | answer = mecmouse_get_values(board->mecmouse); |
| 188 | } |
| 189 | else |
| 190 | answer = ((input_port_read(device->machine, keynames[board->keyCol >> 1]) >> ((board->keyCol & 1) * 8)) << 3) & 0xF8; |
| 191 | } |
| 192 | return answer; |
| 193 | } |
| 194 | |
| 195 | /* |
| 196 | Read pins INT3*-INT7* of TI99's 9901. |
| 197 | |
| 198 | signification: |
| 199 | (bit 1: INT1 status) |
| 200 | (bit 2: INT2 status) |
| 201 | bit 3-7: keyboard status bits 0 to 4 |
| 202 | */ |
| 203 | static READ8_DEVICE_HANDLER( ti99_R9901_0a ) |
| 204 | { |
| 205 | int answer; |
| 206 | running_device *dev = device->machine->device("ti_board"); |
| 207 | tiboard_state *board = get_safe_token(dev); |
| 208 | |
| 209 | if ((board->mecmouse != NULL) && (board->keyCol == 7)) |
| 210 | answer = mecmouse_get_values(board->mecmouse); |
| 211 | else |
| 212 | answer = ((input_port_read(device->machine, keynames[board->keyCol >> 1]) >> ((board->keyCol & 1) * 8)) << 3) & 0xF8; |
| 213 | |
| 214 | if (board->alphaLockLine == FALSE) |
| 215 | answer &= ~(input_port_read(device->machine, "ALPHA") << 3); |
| 216 | |
| 217 | return answer; |
| 218 | } |
| 219 | |
| 220 | |
| 221 | /* |
| 222 | Read pins INT8*-INT15* of TI99's 9901. |
| 223 | |
| 224 | signification: |
| 225 | bit 0-2: keyboard status bits 5 to 7 |
| 226 | bit 3: tape input mirror |
| 227 | (bit 4: IR remote handset interrupt) |
| 228 | bit 5-7: weird, not emulated |
| 229 | */ |
| 230 | static READ8_DEVICE_HANDLER( ti99_R9901_1 ) |
| 231 | { |
| 232 | int answer; |
| 233 | running_device *dev = device->machine->device("ti_board"); |
| 234 | tiboard_state *board = get_safe_token(dev); |
| 235 | |
| 236 | if (board->keyCol == 7) |
| 237 | answer = 0x07; |
| 238 | else |
| 239 | { |
| 240 | answer = ((input_port_read(device->machine, keynames[board->keyCol >> 1]) >> ((board->keyCol & 1) * 8)) >> 5) & 0x07; |
| 241 | } |
| 242 | |
| 243 | /* we don't take CS2 into account, as CS2 is a write-only unit */ |
| 244 | // if (cassette_input(device->machine->device("cassette1")) > 0) |
| 245 | // answer |= 8; |
| 246 | |
| 247 | return answer; |
| 248 | } |
| 249 | |
| 250 | /* |
| 251 | Read pins P0-P7 of TI99's 9901. |
| 252 | |
| 253 | bit 1: handset data clock pin |
| 254 | */ |
| 255 | static READ8_DEVICE_HANDLER( ti99_R9901_2 ) |
| 256 | { |
| 257 | running_device *dev = device->machine->device("ti_board"); |
| 258 | tiboard_state *board = get_safe_token(dev); |
| 259 | |
| 260 | if (board->handset != NULL) |
| 261 | if (ti99_handset_get_clock(board->handset)) return 2; |
| 262 | return 0; |
| 263 | } |
| 264 | |
| 265 | /* |
| 266 | Read pins P8-P15 of TI99's 9901. (99/4) |
| 267 | |
| 268 | bit 26: IR handset interrupt |
| 269 | bit 27: tape input |
| 270 | */ |
| 271 | static READ8_DEVICE_HANDLER( ti99_R9901_3 ) |
| 272 | { |
| 273 | int answer = 4; |
| 274 | running_device *dev = device->machine->device("ti_board"); |
| 275 | tiboard_state *board = get_safe_token(dev); |
| 276 | |
| 277 | /* on systems without handset, the pin is pulled up to avoid spurious interrupts */ |
| 278 | if (board->handset != NULL) |
| 279 | if (ti99_handset_get_int(board->handset)) answer = 0; |
| 280 | |
| 281 | /* we don't take CS2 into account, as CS2 is a write-only unit */ |
| 282 | if (cassette_input(device->machine->device("cassette1")) > 0) |
| 283 | answer |= 8; |
| 284 | |
| 285 | return answer; |
| 286 | } |
| 287 | |
| 288 | /* |
| 289 | Read pins P8-P15 of TI99's 9901. (TI-99/8) |
| 290 | |
| 291 | bit 26: high |
| 292 | bit 27: tape input |
| 293 | */ |
| 294 | static READ8_DEVICE_HANDLER( ti99_8_R9901_3 ) |
| 295 | { |
| 296 | int answer = 4; /* on systems without handset, the pin is pulled up to avoid spurious interrupts */ |
| 297 | if (cassette_input(device->machine->device("cassette")) > 0) |
| 298 | answer |= 8; |
| 299 | return answer; |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | WRITE key column select (P2-P4), TI-99/4 |
| 304 | */ |
| 305 | static WRITE8_DEVICE_HANDLER( ti99_KeyC ) |
| 306 | { |
| 307 | running_device *dev = device->machine->device("ti_board"); |
| 308 | tiboard_state *board = get_safe_token(dev); |
| 309 | |
| 310 | int index=5; |
| 311 | |
| 312 | if (board->mode==TI994A) |
| 313 | index=6; |
| 314 | |
| 315 | if (data) |
| 316 | board->keyCol |= 1 << (offset-2); |
| 317 | else |
| 318 | board->keyCol &= ~ (1 << (offset-2)); |
| 319 | |
| 320 | if (board->mecmouse != NULL) |
| 321 | mecmouse_select(board->mecmouse, board->keyCol, index , index+1); |
| 322 | } |
| 323 | |
| 324 | /* |
| 325 | WRITE alpha lock line - TI99/4a only (P5) |
| 326 | */ |
| 327 | static WRITE8_DEVICE_HANDLER( ti99_AlphaW ) |
| 328 | { |
| 329 | running_device *dev = device->machine->device("ti_board"); |
| 330 | tiboard_state *board = get_safe_token(dev); |
| 331 | board->alphaLockLine = data; |
| 332 | } |
| 333 | |
| 334 | /* |
| 335 | command CS1 tape unit motor (P6) |
| 336 | */ |
| 337 | static WRITE8_DEVICE_HANDLER( ti99_CS1_motor ) |
| 338 | { |
| 339 | running_device *img = device->machine->device("cassette1"); |
| 340 | cassette_change_state(img, data ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR); |
| 341 | } |
| 342 | |
| 343 | /* |
| 344 | command CS2 tape unit motor (P7) |
| 345 | */ |
| 346 | static WRITE8_DEVICE_HANDLER( ti99_CS2_motor ) |
| 347 | { |
| 348 | running_device *img = device->machine->device("cassette2"); |
| 349 | cassette_change_state(img, data ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR); |
| 350 | } |
| 351 | |
| 352 | /* |
| 353 | command CS1 (only) tape unit motor (P6) |
| 354 | */ |
| 355 | static WRITE8_DEVICE_HANDLER( ti99_CS_motor ) |
| 356 | { |
| 357 | running_device *img = device->machine->device("cassette"); |
| 358 | cassette_change_state(img, data ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR); |
| 359 | } |
| 360 | |
| 361 | /* |
| 362 | audio gate (P8) |
| 363 | |
| 364 | Set to 1 before using tape: this enables the mixing of tape input sound |
| 365 | with computer sound. |
| 366 | |
| 367 | We do not really need to emulate this as the tape recorder generates sound |
| 368 | on its own. |
| 369 | */ |
| 370 | static WRITE8_DEVICE_HANDLER( ti99_audio_gate ) |
| 371 | { |
| 372 | } |
| 373 | |
| 374 | /* |
| 375 | tape output (P9) |
| 376 | I think polarity is correct, but don't take my word for it. |
| 377 | */ |
| 378 | static WRITE8_DEVICE_HANDLER( ti99_CS12_output ) |
| 379 | { |
| 380 | cassette_output(device->machine->device("cassette1"), data ? +1 : -1); |
| 381 | cassette_output(device->machine->device("cassette2"), data ? +1 : -1); |
| 382 | } |
| 383 | |
| 384 | /* |
| 385 | tape output (P9) |
| 386 | I think polarity is correct, but don't take my word for it. |
| 387 | */ |
| 388 | static WRITE8_DEVICE_HANDLER( ti99_CS_output ) |
| 389 | { |
| 390 | cassette_output(device->machine->device("cassette"), data ? +1 : -1); |
| 391 | } |
| 392 | |
| 393 | /* |
| 394 | WRITE key column select (P0-P3) |
| 395 | */ |
| 396 | static WRITE8_DEVICE_HANDLER( ti99_8_KeyC ) |
| 397 | { |
| 398 | running_device *dev = device->machine->device("ti_board"); |
| 399 | tiboard_state *board = get_safe_token(dev); |
| 400 | |
| 401 | if (data) |
| 402 | board->keyCol |= 1 << offset; |
| 403 | else |
| 404 | board->keyCol &= ~ (1 << offset); |
| 405 | |
| 406 | if (board->mecmouse != NULL) |
| 407 | mecmouse_select(board->mecmouse, board->keyCol, 14, 15); |
| 408 | } |
| 409 | |
| 410 | static WRITE8_DEVICE_HANDLER( ti99_8_CRUS ) |
| 411 | { |
| 412 | running_device *mapper = device->machine->device("mapper"); |
| 413 | mapper8_CRUS_w(mapper, 0, data); |
| 414 | } |
| 415 | |
| 416 | static WRITE8_DEVICE_HANDLER( ti99_8_PTGEN ) |
| 417 | { |
| 418 | running_device *mapper = device->machine->device("mapper"); |
| 419 | mapper8_PTGEN_w(mapper, 0, data); |
| 420 | } |
| 421 | |
| 422 | /* |
| 423 | Read pins INT3*-INT7* of TI99's 9901. |
| 424 | |
| 425 | signification: |
| 426 | (bit 1: INT1 status) |
| 427 | (bit 2: INT2 status) |
| 428 | bits 3-4: unused? |
| 429 | bit 5: ??? |
| 430 | bit 6-7: keyboard status bits 0 through 1 |
| 431 | */ |
| 432 | static READ8_DEVICE_HANDLER( ti99_8_R9901_0 ) |
| 433 | { |
| 434 | int answer; |
| 435 | running_device *dev = device->machine->device("ti_board"); |
| 436 | tiboard_state *board = get_safe_token(dev); |
| 437 | |
| 438 | if (board->mecmouse != NULL && (board->keyCol == 15)) |
| 439 | { |
| 440 | answer = mecmouse_get_values8(board->mecmouse, 0); |
| 441 | } |
| 442 | else |
| 443 | { |
| 444 | answer = (input_port_read(device->machine, keynames8[board->keyCol]) << 6) & 0xC0; |
| 445 | } |
| 446 | |
| 447 | return answer; |
| 448 | } |
| 449 | |
| 450 | |
| 451 | /* |
| 452 | Read pins INT8*-INT15* of TI99's 9901. |
| 453 | |
| 454 | signification: |
| 455 | bit 0-2: keyboard status bits 2 to 4 |
| 456 | bit 3: tape input mirror |
| 457 | (bit 4: IR remote handset interrupt) |
| 458 | bit 5-7: weird, not emulated |
| 459 | */ |
| 460 | static READ8_DEVICE_HANDLER( ti99_8_R9901_1 ) |
| 461 | { |
| 462 | running_device *dev = device->machine->device("ti_board"); |
| 463 | tiboard_state *board = get_safe_token(dev); |
| 464 | int answer; |
| 465 | |
| 466 | if (board->mecmouse != NULL && (board->keyCol == 15)) |
| 467 | { |
| 468 | answer = mecmouse_get_values8(board->mecmouse, 1); |
| 469 | } |
| 470 | else |
| 471 | { |
| 472 | answer = (input_port_read(device->machine, keynames8[board->keyCol]) >> 2) & 0x07; |
| 473 | } |
| 474 | |
| 475 | return answer; |
| 476 | } |
| 477 | |
| 478 | /* |
| 479 | Read pins P0-P7 of TI99's 9901. |
| 480 | |
| 481 | bit 1: handset data clock pin |
| 482 | */ |
| 483 | static READ8_DEVICE_HANDLER( ti99_8_R9901_2 ) |
| 484 | { |
| 485 | return 0; |
| 486 | } |
| 487 | |
| 488 | |
| 489 | /* TMS9901 setup. The callback functions pass a reference to the TMS9901 as device. */ |
| 490 | const tms9901_interface tms9901_wiring_ti99_4 = |
| 491 | { |
| 492 | TMS9901_INT1 | TMS9901_INT2 | TMS9901_INTC, /* only input pins whose state is always known */ |
| 493 | |
| 494 | { /* read handlers */ |
| 495 | ti99_R9901_0, |
| 496 | ti99_R9901_1, |
| 497 | ti99_R9901_2, |
| 498 | ti99_R9901_3 |
| 499 | }, |
| 500 | |
| 501 | { /* write handlers */ |
| 502 | ti99_handset_set_ack, |
| 503 | NULL, |
| 504 | ti99_KeyC, |
| 505 | ti99_KeyC, |
| 506 | ti99_KeyC, |
| 507 | NULL, |
| 508 | ti99_CS1_motor, |
| 509 | ti99_CS2_motor, |
| 510 | ti99_audio_gate, |
| 511 | ti99_CS12_output, |
| 512 | NULL, |
| 513 | NULL, |
| 514 | NULL, |
| 515 | NULL, |
| 516 | NULL, |
| 517 | NULL |
| 518 | }, |
| 519 | |
| 520 | /* interrupt handler */ |
| 521 | tms9901_interrupt_callback, |
| 522 | |
| 523 | /* clock rate = 3MHz */ |
| 524 | 3000000. |
| 525 | }; |
| 526 | |
| 527 | /* TMS9901 setup. The callback functions pass a reference to the TMS9901 as device. */ |
| 528 | const tms9901_interface tms9901_wiring_ti99_4a = |
| 529 | { |
| 530 | TMS9901_INT1 | TMS9901_INT2 | TMS9901_INTC, /* only input pins whose state is always known */ |
| 531 | |
| 532 | { /* read handlers */ |
| 533 | ti99_R9901_0a, |
| 534 | ti99_R9901_1, |
| 535 | ti99_R9901_2, |
| 536 | ti99_R9901_3 |
| 537 | }, |
| 538 | |
| 539 | { /* write handlers */ |
| 540 | NULL, |
| 541 | NULL, |
| 542 | ti99_KeyC, |
| 543 | ti99_KeyC, |
| 544 | ti99_KeyC, |
| 545 | ti99_AlphaW, |
| 546 | ti99_CS1_motor, |
| 547 | ti99_CS2_motor, |
| 548 | ti99_audio_gate, |
| 549 | ti99_CS12_output, |
| 550 | NULL, |
| 551 | NULL, |
| 552 | NULL, |
| 553 | NULL, |
| 554 | NULL, |
| 555 | NULL |
| 556 | }, |
| 557 | |
| 558 | /* interrupt handler */ |
| 559 | tms9901_interrupt_callback, |
| 560 | |
| 561 | /* clock rate = 3MHz */ |
| 562 | 3000000. |
| 563 | }; |
| 564 | |
| 565 | const tms9901_interface tms9901_wiring_ti99_8 = |
| 566 | { |
| 567 | TMS9901_INT1 | TMS9901_INT2 | TMS9901_INTC, /* only input pins whose state is always known */ |
| 568 | |
| 569 | { /* read handlers */ |
| 570 | ti99_8_R9901_0, |
| 571 | ti99_8_R9901_1, |
| 572 | ti99_8_R9901_2, |
| 573 | ti99_8_R9901_3 |
| 574 | }, |
| 575 | |
| 576 | { /* write handlers */ |
| 577 | ti99_8_KeyC, |
| 578 | ti99_8_KeyC, |
| 579 | ti99_8_KeyC, |
| 580 | ti99_8_KeyC, |
| 581 | ti99_8_CRUS, |
| 582 | ti99_8_PTGEN, |
| 583 | ti99_CS_motor, |
| 584 | NULL, |
| 585 | ti99_audio_gate, |
| 586 | ti99_CS_output, |
| 587 | NULL, |
| 588 | NULL, |
| 589 | NULL, |
| 590 | NULL, |
| 591 | NULL, |
| 592 | NULL |
| 593 | }, |
| 594 | |
| 595 | /* interrupt handler */ |
| 596 | tms9901_interrupt_callback, |
| 597 | |
| 598 | /* clock rate = 3.58MHz??? 2.68MHz??? */ |
| 599 | /*3579545.*/2684658.75 |
| 600 | }; |
| 601 | |
| 602 | /* |
| 603 | Handles the EXTINT line. This one is directly connected to the 9901. |
| 604 | */ |
| 605 | WRITE_LINE_DEVICE_HANDLER( console_extint ) |
| 606 | { |
| 607 | tms9901_set_single_int(device->machine->device("tms9901"), 1, state); |
| 608 | } |
| 609 | |
| 610 | WRITE_LINE_DEVICE_HANDLER( console_notconnected ) |
| 611 | { |
| 612 | logerror("Not connected line set ... sorry\n"); |
| 613 | } |
| 614 | |
| 615 | WRITE_LINE_DEVICE_HANDLER( console_ready ) |
| 616 | { |
| 617 | logerror("READY line set ... not yet connected, level=%02x\n", state); |
| 618 | } |
| 619 | |
| 620 | /*************************************************************************** |
| 621 | DEVICE LIFECYCLE FUNCTIONS |
| 622 | ***************************************************************************/ |
| 623 | |
| 624 | static DEVICE_START( tiboard ) |
| 625 | { |
| 626 | tiboard_state *board = get_safe_token(device); |
| 627 | logerror("Starting TI-99 board\n"); |
| 628 | board->tms9901 = device->siblingdevice("tms9901"); |
| 629 | board->gromport = device->siblingdevice("gromport"); |
| 630 | } |
| 631 | |
| 632 | static DEVICE_STOP( tiboard ) |
| 633 | { |
| 634 | logerror("Stopping TI-99 board\n"); |
| 635 | } |
| 636 | |
| 637 | static DEVICE_RESET( tiboard ) |
| 638 | { |
| 639 | tiboard_state *board = get_safe_token(device); |
| 640 | const tiboard_config* conf = (const tiboard_config*)get_config(device); |
| 641 | board->mode = conf->mode; |
| 642 | |
| 643 | /* clear keyboard interface state (probably overkill, but can't harm) */ |
| 644 | board->keyCol = 0; |
| 645 | board->alphaLockLine = 0; |
| 646 | board->handset = NULL; |
| 647 | board->mecmouse = NULL; |
| 648 | |
| 649 | if (board->mode == TI994) |
| 650 | { |
| 651 | /* reset handset */ |
| 652 | tms9901_set_single_int(board->tms9901, 12, 0); |
| 653 | |
| 654 | if (input_port_read(device->machine, "HCI") & HCI_IR) |
| 655 | board->handset = device->siblingdevice("handset"); |
| 656 | } |
| 657 | |
| 658 | if (input_port_read(device->machine, "HCI") & HCI_MECMOUSE) |
| 659 | board->mecmouse = device->siblingdevice("mecmouse"); |
| 660 | |
| 661 | if (board->mode != TI998) |
| 662 | { |
| 663 | set_gk_switches(board->gromport, 1, input_port_read(device->machine, "GKSWITCH1")); |
| 664 | set_gk_switches(board->gromport, 2, input_port_read(device->machine, "GKSWITCH2")); |
| 665 | set_gk_switches(board->gromport, 3, input_port_read(device->machine, "GKSWITCH3")); |
| 666 | set_gk_switches(board->gromport, 4, input_port_read(device->machine, "GKSWITCH4")); |
| 667 | set_gk_switches(board->gromport, 5, input_port_read(device->machine, "GKSWITCH5")); |
| 668 | } |
| 669 | } |
| 670 | |
| 671 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 672 | |
| 673 | #define DEVTEMPLATE_ID(p,s) p##tiboard##s |
| 674 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_INLINE_CONFIG |
| 675 | #define DEVTEMPLATE_NAME "TI-99 system board" |
| 676 | #define DEVTEMPLATE_FAMILY "Internal component" |
| 677 | #include "devtempl.h" |
| 678 | |
| 679 | DEFINE_LEGACY_DEVICE( TIBOARD, tiboard ); |