src/mess/devices/ti99_peb.c
| r8681 | r8682 | |
| 1 | | /*
|
| 2 | | Peripheral expansion bus support.
|
| 3 | | |
| 4 | | The ti-99/4, ti-99/4a, ti computer 99/8, myarc geneve, and snug sgcpu
|
| 5 | | 99/4p systems all feature a bus connector that enables the connection of
|
| 6 | | extension cards. (Although the hexbus is the preferred bus to add
|
| 7 | | additional peripherals to a ti-99/8, ti-99/8 is believed to be compatible
|
| 8 | | with the older PEB system.) In the case of the TI consoles, this bus
|
| 9 | | connector is located on the right side of the console.
|
| 10 | | |
| 11 | | While a few extension cards connect to the side bus connector of the
|
| 12 | | ti-99/4(a) console directly, most extension cards were designed to be
|
| 13 | | inserted in a PEB instead. The PEB (Peripheral Expansion Box) is a big box
|
| 14 | | with an alimentation, a few bus drivers, and several card slots, that
|
| 15 | | connects to the ti-99/4(a) side port. The reason for using a PEB is that
|
| 16 | | daisy-chaining many modules caused the system to be unreliable due to the
|
| 17 | | noise produced by the successive contacts. (As a matter of fact, TI
|
| 18 | | initially released most of its extension cards as side bus units, but when
|
| 19 | | the design proved to be unreliable, the PEB was introduced. The TI speech
|
| 20 | | synthesizer was the only TI extension that remained on the side bus after
|
| 21 | | the introduction of the PEB, probably because TI wanted the speech
|
| 22 | | synthesizer to be a cheap extension, and the PEB was not cheap.)
|
| 23 | | |
| 24 | | The third-party myarc geneve and snug sgcpu computers are actually cards
|
| 25 | | to be inserted into a PEB: they have a PC keyboard connector, and replace
|
| 26 | | the original ti-99 console completely.
|
| 27 | | |
| 28 | | Each expansion card is assigned to one of 16 CRU address ranges.
|
| 29 | | I appended the names of known TI peripherals that use each port (I appended
|
| 30 | | '???' when I don't know what the peripheral is :-) ).
|
| 31 | | * 0x1000-0x10FE "For test equipment use on production line"
|
| 32 | | * 0x1100-0x11FE disk controller
|
| 33 | | * 0x1200-0x12FE modem???
|
| 34 | | * 0x1300-0x13FE RS232 1&2, PIO 1
|
| 35 | | * 0x1400-0x14FE unassigned
|
| 36 | | * 0x1500-0x15FE RS232 3&4, PIO 2
|
| 37 | | * 0x1600-0x16FE unassigned
|
| 38 | | * 0x1700-0x17FE hex-bus (prototypes)
|
| 39 | | * 0x1800-0x18FE thermal printer (early peripheral)
|
| 40 | | * 0x1900-0x19FE EPROM programmer??? (Mezzanine board in July 1981's TI99/7
|
| 41 | | prototype)
|
| 42 | | * 0x1A00-0x1AFE unassigned
|
| 43 | | * 0x1B00-0x1BFE TI GPL debugger card
|
| 44 | | * 0x1C00-0x1CFE Video Controller Card (Possibly the weirdest device. This
|
| 45 | | card is connected to the video output of the computer, to a VCR, and a
|
| 46 | | video monitor. It can control the VCR, connect the display to either
|
| 47 | | VCR output or computer video output, and it can read or save binary
|
| 48 | | data to video tape. I think it can act as a genlock interface (i.e.
|
| 49 | | TMS9918 transparent background shows the video signal), too, but I am
|
| 50 | | not sure about this.)
|
| 51 | | * 0x1D00-0x1DFE IEEE 488 Controller Card ('intelligent' parallel bus,
|
| 52 | | schematics on ftp.whtech.com)
|
| 53 | | * 0x1E00-0x1EFE unassigned
|
| 54 | | * 0x1F00-0x1FFE P-code card (part of a complete development system)
|
| 55 | | |
| 56 | | The ti-99/8 implements 16 extra ports:
|
| 57 | | * 0x2000-0x26ff and 0x2800-0x2fff: reserved for future expansion?
|
| 58 | | * 0x2700-0x27ff: internal DSR
|
| 59 | | |
| 60 | | The snug sgcpu 99/4p implements 12 extra ports:
|
| 61 | | * 0x0400-0x0eff: free
|
| 62 | | * 0x0f00-0x0fff: internal DSR
|
| 63 | | |
| 64 | | Known mappings for 3rd party cards:
|
| 65 | | * Horizon RAMdisk: any ports from 0 to 7 (port 0 is most common).
|
| 66 | | * Myarc 128k/512k (RAM and DSR ROM): port 0 (0x1000-0x11FE)
|
| 67 | | * Corcomp 512k (RAM and DSR ROM): port unknown
|
| 68 | | * Super AMS 128k/512k: port 14 (0x1E00-0x1EFE)
|
| 69 | | * Foundation 128k/512k: port 14 (0x1E00-0x1EFE)
|
| 70 | | * Gram Karte: any port (0-15)
|
| 71 | | * EVPC (video card): 0x1400-0x14FE
|
| 72 | | * HSGPL (GROM replacement): 0x1B00-0x1BFE
|
| 73 | | |
| 74 | | Of course, these devices additionally need some support routines, and
|
| 75 | | possibly memory-mapped I/O registers. To do so, memory range 0x4000-5FFF
|
| 76 | | is shared by all cards. The system enables each card as needed by writing
|
| 77 | | a 1 to the first CRU bit: when this happens the card can safely enable its
|
| 78 | | ROM, RAM, memory-mapped registers, etc. Provided the ROM uses the proper
|
| 79 | | ROM header, the system will recognize and call peripheral I/O functions,
|
| 80 | | interrupt service routines, and startup routines. (All these routines are
|
| 81 | | generally called DSR = Device Service Routine.)
|
| 82 | | |
| 83 | | Also, the cards can trigger level-1 ("INTA") interrupts. A LOAD interrupt
|
| 84 | | is present on the side port of the ti-99/4(a), too, but it was never used
|
| 85 | | by TI, AFAIK. Also, an interrupt line called "INTB" is found on several
|
| 86 | | cards built by TI, but it is not connected: I am quite unsure whether it
|
| 87 | | was intended to be connected to the LOAD line, to the INT2 line of TMS9985,
|
| 88 | | or it was only to be used with the ti-99/4(a) development system running on
|
| 89 | | a TI990/10. This is not the only unused feature: cards built by TI support
|
| 90 | | sensing of an interrupt status register and 19-bit addressing, even though
|
| 91 | | ti-99/4(A) does not take advantage of these features. TI documentation
|
| 92 | | tells that some of these features are "defined by the Personal Computer PCC
|
| 93 | | at Texas Instrument", which is not very explicit (What on earth does "PCC"
|
| 94 | | stand for?). Maybe it refers to the ti-99/4(a) development system running
|
| 95 | | on a TI990/10, or to the ti-99/7 and ti-99/8 prototypes.
|
| 96 | | |
| 97 | | Note that I use 8-bit RAM handlers. Obviously, using 16-bit handlers would
|
| 98 | | be equivalent and faster on a ti-99/4(a), but we need to interface the
|
| 99 | | extension cards not only to the ti-99/4(a), but to the geneve and ti-99/8
|
| 100 | | emulators as well.
|
| 101 | | |
| 102 | | June 2010: Reimplemented using device structure (MZ) (obsoletes 99_peb.c)
|
| 103 | | */
|
| 104 | | |
| 105 | | #include "emu.h"
|
| 106 | | #include "ti99_peb.h"
|
| 107 | | |
| 108 | | enum width_t {
|
| 109 | | width_8bit = 0,
|
| 110 | | width_16bit
|
| 111 | | };
|
| 112 | | /* expansion card structure for the snug sgcpu 99/4p system, which supports
|
| 113 | | dynamical 16-bit accesses (TI did not design this!) */
|
| 114 | | typedef struct ti99_4p_peb_card_handlers_t
|
| 115 | | {
|
| 116 | | cru_read_handler cru_read; /* card CRU read handler */
|
| 117 | | cru_write_handler cru_write; /* card CRU handler */
|
| 118 | | |
| 119 | | width_t width;
|
| 120 | | union
|
| 121 | | {
|
| 122 | | struct
|
| 123 | | {
|
| 124 | | read8_space_func mem_read; /* card mem read handler (8 bits) */
|
| 125 | | write8_space_func mem_write; /* card mem write handler (8 bits) */
|
| 126 | | } width_8bit;
|
| 127 | | struct
|
| 128 | | {
|
| 129 | | read16_space_func mem_read; /* card mem read handler (16 bits) */
|
| 130 | | write16_space_func mem_write; /* card mem write handler (16 bits) */
|
| 131 | | } width_16bit;
|
| 132 | | } w;
|
| 133 | | } ti99_4p_peb_card_handlers_t;
|
| 134 | | |
| 135 | | typedef struct _ti99_peb_state
|
| 136 | | {
|
| 137 | | /* TRUE if we are using the snug sgcpu 99/4p 16-bit extensions */
|
| 138 | | int has_16bit_peb;
|
| 139 | | |
| 140 | | /* handlers for each of 28 slots */
|
| 141 | | ti99_4p_peb_card_handlers_t ti99_4p_expansion_ports[28];
|
| 142 | | |
| 143 | | /* handlers for each of 16 slots + 16 extra slots for ti-99/8 */
|
| 144 | | ti99_peb_card_handlers_t expansion_ports[16+16];
|
| 145 | | |
| 146 | | /* index of the currently active card (-1 if none) */
|
| 147 | | int active_card;
|
| 148 | | |
| 149 | | /* index of the currently active card, overridden by the mapper */
|
| 150 | | int active_card_tmp;
|
| 151 | | |
| 152 | | /* Mapper override. */
|
| 153 | | int mapper_override;
|
| 154 | | |
| 155 | | // ila: inta status register (not actually used on ti-99/4(a), but nevertheless
|
| 156 | | // present)
|
| 157 | | int ila;
|
| 158 | | |
| 159 | | // ilb: intb status register (completely pointless on ti-99/4(a), as neither
|
| 160 | | // INTB nor SENILB are connected; OTOH, INTB can trigger interrupts on a Geneve,
|
| 161 | | // and snug sgcpu 99/4p can sense ILB)
|
| 162 | | int ilb;
|
| 163 | | |
| 164 | | /* only used by the snug sgcpu 99/4p */
|
| 165 | | int senila, senilb;
|
| 166 | | |
| 167 | | /* hack to simulate TMS9900 byte write */
|
| 168 | | int tmp_buffer;
|
| 169 | | |
| 170 | | /* inta/intb handlers */
|
| 171 | | void (*inta_callback)(running_machine *machine, int state);
|
| 172 | | void (*intb_callback)(running_machine *machine, int state);
|
| 173 | | |
| 174 | | const address_space *space;
|
| 175 | | |
| 176 | | } ti99_peb_state;
|
| 177 | | |
| 178 | | INLINE ti99_peb_state *get_safe_token(running_device *device)
|
| 179 | | {
|
| 180 | | assert(device != NULL);
|
| 181 | | assert(downcast<legacy_device_base *>(device)->token() != NULL);
|
| 182 | | return (ti99_peb_state *)downcast<legacy_device_base *>(device)->token();
|
| 183 | | }
|
| 184 | | |
| 185 | | INLINE const ti99_peb_config *get_config(running_device *device)
|
| 186 | | {
|
| 187 | | assert(device != NULL);
|
| 188 | | assert(device->type() == PBOX);
|
| 189 | | return (const ti99_peb_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config();
|
| 190 | | }
|
| 191 | | |
| 192 | | /*
|
| 193 | | Sets the handlers for one expansion port (normal 8-bit card)
|
| 194 | | |
| 195 | | cru_base: CRU base address (any of 0x1000, 0x1100, 0x1200, ..., 0x1F00)
|
| 196 | | (snug sgcpu 99/4p accepts 0x0400, ..., 0x1F00, ti-99/8 accepts 0x1000,
|
| 197 | | ..., 0x2F00)
|
| 198 | | handler: handler structure for the given card
|
| 199 | | */
|
| 200 | | void ti99_peb_set_card_handlers(running_device *box, int cru_base, const ti99_peb_card_handlers_t *handler)
|
| 201 | | {
|
| 202 | | int port;
|
| 203 | | ti99_peb_state *peb = get_safe_token(box);
|
| 204 | | |
| 205 | | if (cru_base & 0xff)
|
| 206 | | return;
|
| 207 | | |
| 208 | | if (peb->has_16bit_peb)
|
| 209 | | {
|
| 210 | | port = (cru_base - 0x0400) >> 8;
|
| 211 | | |
| 212 | | if ((port>=0) && (port<28))
|
| 213 | | {
|
| 214 | | peb->ti99_4p_expansion_ports[port].cru_read = handler->cru_read;
|
| 215 | | peb->ti99_4p_expansion_ports[port].cru_write = handler->cru_write;
|
| 216 | | peb->ti99_4p_expansion_ports[port].width = width_8bit;
|
| 217 | | peb->ti99_4p_expansion_ports[port].w.width_8bit.mem_read = handler->mem_read;
|
| 218 | | peb->ti99_4p_expansion_ports[port].w.width_8bit.mem_write = handler->mem_write;
|
| 219 | | }
|
| 220 | | }
|
| 221 | | else
|
| 222 | | {
|
| 223 | | port = (cru_base - 0x1000) >> 8;
|
| 224 | | |
| 225 | | if ((port>=0) && (port</*16*/32))
|
| 226 | | {
|
| 227 | | peb->expansion_ports[port] = *handler;
|
| 228 | | }
|
| 229 | | }
|
| 230 | | }
|
| 231 | | |
| 232 | | /*
|
| 233 | | Sets the handlers for one expansion port (special 16-bit card for snug
|
| 234 | | sgcpu 99/4p)
|
| 235 | | |
| 236 | | cru_base: CRU base address (any of 0x1000, 0x1100, 0x1200, ..., 0x1F00)
|
| 237 | | (snug sgcpu 99/4p accepts 0x0400, ..., 0x1F00, ti-99/8 accepts 0x1000,
|
| 238 | | ..., 0x2F00)
|
| 239 | | handler: handler structure for the given card
|
| 240 | | */
|
| 241 | | void ti99_peb_set_16bit_card_handlers(running_device *box, int cru_base, const ti99_peb_16bit_card_handlers_t *handler)
|
| 242 | | {
|
| 243 | | int port;
|
| 244 | | ti99_peb_state *peb = get_safe_token(box);
|
| 245 | | |
| 246 | | if (cru_base & 0xff)
|
| 247 | | return;
|
| 248 | | |
| 249 | | if (peb->has_16bit_peb)
|
| 250 | | {
|
| 251 | | port = (cru_base - 0x0400) >> 8;
|
| 252 | | |
| 253 | | if ((port>=0) && (port<28))
|
| 254 | | {
|
| 255 | | peb->ti99_4p_expansion_ports[port].cru_read = handler->cru_read;
|
| 256 | | peb->ti99_4p_expansion_ports[port].cru_write = handler->cru_write;
|
| 257 | | peb->ti99_4p_expansion_ports[port].width = width_16bit;
|
| 258 | | peb->ti99_4p_expansion_ports[port].w.width_16bit.mem_read = handler->mem_read;
|
| 259 | | peb->ti99_4p_expansion_ports[port].w.width_16bit.mem_write = handler->mem_write;
|
| 260 | | }
|
| 261 | | }
|
| 262 | | }
|
| 263 | | |
| 264 | | /*
|
| 265 | | Update ila status register and assert or clear INTA interrupt line
|
| 266 | | accordingly.
|
| 267 | | |
| 268 | | bit: bit number ([0,7] for original INTA register, [8,15] are extra
|
| 269 | | "virtual" bits for devices that assert the INTA line without setting a
|
| 270 | | bit of the ILA register)
|
| 271 | | state: 1 to assert bit, 0 to clear
|
| 272 | | */
|
| 273 | | void ti99_peb_set_ila_bit(running_device *box, int bit, int state)
|
| 274 | | {
|
| 275 | | ti99_peb_state *peb = get_safe_token(box);
|
| 276 | | if (state)
|
| 277 | | {
|
| 278 | | peb->ila |= 1 << bit;
|
| 279 | | if (peb->inta_callback)
|
| 280 | | (*peb->inta_callback)(box->machine, 1);
|
| 281 | | }
|
| 282 | | else
|
| 283 | | {
|
| 284 | | peb->ila &= ~(1 << bit);
|
| 285 | | if ((!peb->ila) && peb->inta_callback)
|
| 286 | | (*peb->inta_callback)(box->machine, 0);
|
| 287 | | }
|
| 288 | | }
|
| 289 | | |
| 290 | | /*
|
| 291 | | Update ilb status register and assert or clear INTB interrupt line
|
| 292 | | accordingly.
|
| 293 | | |
| 294 | | bit: bit number ([0,7] for original INTAB register, [8,15] are extra
|
| 295 | | "virtual" bits for devices that assert the INTB line without setting a
|
| 296 | | bit of the ILB register)
|
| 297 | | state: 1 to assert bit, 0 to clear
|
| 298 | | */
|
| 299 | | void ti99_peb_set_ilb_bit(running_device *box, int bit, int state)
|
| 300 | | {
|
| 301 | | ti99_peb_state *peb = get_safe_token(box);
|
| 302 | | |
| 303 | | if (state)
|
| 304 | | {
|
| 305 | | peb->ilb |= 1 << bit;
|
| 306 | | if (peb->intb_callback)
|
| 307 | | (*peb->intb_callback)(box->machine, 1);
|
| 308 | | }
|
| 309 | | else
|
| 310 | | {
|
| 311 | | peb->ilb &= ~(1 << bit);
|
| 312 | | if ((!peb->ilb) && peb->intb_callback)
|
| 313 | | (*peb->intb_callback)(box->machine, 0);
|
| 314 | | }
|
| 315 | | }
|
| 316 | | |
| 317 | | |
| 318 | | /*
|
| 319 | | Read CRU in range >1000->1ffe (>800->fff) (ti-99/4(a))
|
| 320 | | */
|
| 321 | | READ8_DEVICE_HANDLER( ti99_4x_peb_cru_r )
|
| 322 | | {
|
| 323 | | int port;
|
| 324 | | cru_read_handler handler;
|
| 325 | | int reply;
|
| 326 | | ti99_peb_state *peb = get_safe_token(device);
|
| 327 | | |
| 328 | | port = (offset & 0x00f0) >> 4;
|
| 329 | | handler = peb->expansion_ports[port].cru_read;
|
| 330 | | |
| 331 | | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0;
|
| 332 | | |
| 333 | | return reply;
|
| 334 | | }
|
| 335 | | |
| 336 | | /*
|
| 337 | | Write CRU in range >1000->1ffe (>800->fff) (ti-99/4(a))
|
| 338 | | */
|
| 339 | | WRITE8_DEVICE_HANDLER( ti99_4x_peb_cru_w )
|
| 340 | | {
|
| 341 | | int port;
|
| 342 | | cru_write_handler handler;
|
| 343 | | ti99_peb_state *peb = get_safe_token(device);
|
| 344 | | |
| 345 | | port = (offset & 0x0780) >> 7;
|
| 346 | | handler = peb->expansion_ports[port].cru_write;
|
| 347 | | |
| 348 | | if (handler)
|
| 349 | | (*handler)(device->machine, offset & 0x7f, data);
|
| 350 | | |
| 351 | | /* expansion card enable? */
|
| 352 | | if ((offset & 0x7f) == 0)
|
| 353 | | {
|
| 354 | | if (data & 1)
|
| 355 | | {
|
| 356 | | /* enable */
|
| 357 | | peb->active_card = port;
|
| 358 | | }
|
| 359 | | else
|
| 360 | | {
|
| 361 | | if (port == peb->active_card) /* geez... who cares? */
|
| 362 | | {
|
| 363 | | peb->active_card = -1; /* no port selected */
|
| 364 | | }
|
| 365 | | }
|
| 366 | | }
|
| 367 | | }
|
| 368 | | |
| 369 | | /*
|
| 370 | | Read mem in range >4000->5ffe (ti-99/4(a))
|
| 371 | | */
|
| 372 | | READ16_DEVICE_HANDLER( ti99_4x_peb_r )
|
| 373 | | {
|
| 374 | | int reply = 0;
|
| 375 | | read8_space_func handler;
|
| 376 | | ti99_peb_state *peb = get_safe_token(device);
|
| 377 | | |
| 378 | | cpu_adjust_icount(device->machine->firstcpu,-4);
|
| 379 | | |
| 380 | | if (peb->active_card != -1)
|
| 381 | | {
|
| 382 | | handler = peb->expansion_ports[peb->active_card].mem_read;
|
| 383 | | if (handler)
|
| 384 | | {
|
| 385 | | reply = (*handler)(peb->space, (offset << 1) + 1);
|
| 386 | | reply |= ((unsigned) (*handler)(peb->space, offset << 1)) << 8;
|
| 387 | | }
|
| 388 | | }
|
| 389 | | // printf("[%04x:%04x] = %04x\n", (active_card<<8)+0x1000, (offset<<1)+0x4000, reply);
|
| 390 | | |
| 391 | | return peb->tmp_buffer = reply;
|
| 392 | | }
|
| 393 | | |
| 394 | | /*
|
| 395 | | Write mem in range >4000->5ffe (ti-99/4(a))
|
| 396 | | */
|
| 397 | | WRITE16_DEVICE_HANDLER( ti99_4x_peb_w )
|
| 398 | | {
|
| 399 | | write8_space_func handler;
|
| 400 | | ti99_peb_state *peb = get_safe_token(device);
|
| 401 | | |
| 402 | | cpu_adjust_icount(device->machine->firstcpu,-4);
|
| 403 | | |
| 404 | | /* simulate byte write */
|
| 405 | | data = (peb->tmp_buffer & ~mem_mask) | (data & mem_mask);
|
| 406 | | |
| 407 | | if (peb->active_card != -1)
|
| 408 | | {
|
| 409 | | handler = peb->expansion_ports[peb->active_card].mem_write;
|
| 410 | | if (handler)
|
| 411 | | {
|
| 412 | | (*handler)(peb->space, (offset << 1) + 1, data & 0xff);
|
| 413 | | (*handler)(peb->space, offset << 1, (data >> 8) & 0xff);
|
| 414 | | }
|
| 415 | | }
|
| 416 | | }
|
| 417 | | |
| 418 | | |
| 419 | | /*
|
| 420 | | Read CRU in range >1000->1ffe (>800->fff) (Geneve)
|
| 421 | | */
|
| 422 | | READ8_DEVICE_HANDLER( geneve_peb_cru_r )
|
| 423 | | {
|
| 424 | | int port;
|
| 425 | | cru_read_handler handler;
|
| 426 | | int reply;
|
| 427 | | ti99_peb_state *peb = get_safe_token(device);
|
| 428 | | |
| 429 | | port = (offset & 0x00f0) >> 4;
|
| 430 | | handler = peb->expansion_ports[port].cru_read;
|
| 431 | | |
| 432 | | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0;
|
| 433 | | |
| 434 | | return reply;
|
| 435 | | }
|
| 436 | | |
| 437 | | /*
|
| 438 | | Write CRU in range >1000->1ffe (>800->fff) (Geneve)
|
| 439 | | */
|
| 440 | | WRITE8_DEVICE_HANDLER( geneve_peb_cru_w )
|
| 441 | | {
|
| 442 | | int port;
|
| 443 | | cru_write_handler handler;
|
| 444 | | ti99_peb_state *peb = get_safe_token(device);
|
| 445 | | |
| 446 | | port = (offset & 0x0780) >> 7;
|
| 447 | | handler = peb->expansion_ports[port].cru_write;
|
| 448 | | |
| 449 | | if (handler)
|
| 450 | | (*handler)(device->machine, offset & 0x7f, data);
|
| 451 | | |
| 452 | | /* expansion card enable? */
|
| 453 | | if ((offset & 0x7f) == 0)
|
| 454 | | {
|
| 455 | | if (data & 1)
|
| 456 | | {
|
| 457 | | /* enable */
|
| 458 | | peb->active_card = port;
|
| 459 | | }
|
| 460 | | else
|
| 461 | | {
|
| 462 | | if (port == peb->active_card) /* geez... who cares? */
|
| 463 | | {
|
| 464 | | peb->active_card = -1; /* no port selected */
|
| 465 | | }
|
| 466 | | }
|
| 467 | | }
|
| 468 | | }
|
| 469 | | |
| 470 | | /*
|
| 471 | | Read mem in range >4000->5fff (Geneve)
|
| 472 | | */
|
| 473 | | READ8_DEVICE_HANDLER( geneve_peb_r )
|
| 474 | | {
|
| 475 | | int reply = 0;
|
| 476 | | read8_space_func handler;
|
| 477 | | ti99_peb_state *peb = get_safe_token(device);
|
| 478 | | |
| 479 | | cpu_adjust_icount(device->machine->firstcpu,-8);
|
| 480 | | |
| 481 | | if (peb->active_card != -1)
|
| 482 | | {
|
| 483 | | handler = peb->expansion_ports[peb->active_card].mem_read;
|
| 484 | | if (handler)
|
| 485 | | reply = (*handler)(peb->space, offset);
|
| 486 | | }
|
| 487 | | |
| 488 | | return reply;
|
| 489 | | }
|
| 490 | | |
| 491 | | /*
|
| 492 | | Write mem in range >4000->5fff (Geneve)
|
| 493 | | */
|
| 494 | | WRITE8_DEVICE_HANDLER( geneve_peb_w )
|
| 495 | | {
|
| 496 | | write8_space_func handler;
|
| 497 | | ti99_peb_state *peb = get_safe_token(device);
|
| 498 | | |
| 499 | | cpu_adjust_icount(device->machine->firstcpu,-8);
|
| 500 | | |
| 501 | | if (peb->active_card != -1)
|
| 502 | | {
|
| 503 | | handler = peb->expansion_ports[peb->active_card].mem_write;
|
| 504 | | if (handler)
|
| 505 | | (*handler)(peb->space, offset, data);
|
| 506 | | }
|
| 507 | | }
|
| 508 | | |
| 509 | | /*
|
| 510 | | Read CRU in range >1000->2ffe (>0800->17ff) (ti-99/8)
|
| 511 | | */
|
| 512 | | READ8_DEVICE_HANDLER( ti99_8_peb_cru_r )
|
| 513 | | {
|
| 514 | | int port;
|
| 515 | | cru_read_handler handler;
|
| 516 | | int reply;
|
| 517 | | ti99_peb_state *peb = get_safe_token(device);
|
| 518 | | |
| 519 | | port = (offset & 0x01f0) >> 4;
|
| 520 | | handler = peb->expansion_ports[port].cru_read;
|
| 521 | | |
| 522 | | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0;
|
| 523 | | |
| 524 | | return reply;
|
| 525 | | }
|
| 526 | | |
| 527 | | /*
|
| 528 | | Write CRU in range >1000->2ffe (>0800->17ff) (ti-99/8)
|
| 529 | | */
|
| 530 | | WRITE8_DEVICE_HANDLER( ti99_8_peb_cru_w )
|
| 531 | | {
|
| 532 | | int port;
|
| 533 | | cru_write_handler handler;
|
| 534 | | ti99_peb_state *peb = get_safe_token(device);
|
| 535 | | |
| 536 | | port = (offset & 0x0f80) >> 7;
|
| 537 | | handler = peb->expansion_ports[port].cru_write;
|
| 538 | | |
| 539 | | if (handler)
|
| 540 | | (*handler)(device->machine, offset & 0x7f, data);
|
| 541 | | |
| 542 | | /* expansion card enable? */
|
| 543 | | if ((offset & 0x7f) == 0)
|
| 544 | | {
|
| 545 | | if (data & 1)
|
| 546 | | {
|
| 547 | | /* enable */
|
| 548 | | peb->active_card = port;
|
| 549 | | }
|
| 550 | | else
|
| 551 | | {
|
| 552 | | if (port == peb->active_card) /* geez... who cares? */
|
| 553 | | {
|
| 554 | | peb->active_card = -1; /* no port selected */
|
| 555 | | }
|
| 556 | | }
|
| 557 | | }
|
| 558 | | }
|
| 559 | | |
| 560 | | /*
|
| 561 | | Read mem in range >4000->5fff (ti-99/8)
|
| 562 | | */
|
| 563 | | READ8_DEVICE_HANDLER( ti99_8_peb_r )
|
| 564 | | {
|
| 565 | | int reply = 0;
|
| 566 | | read8_space_func handler;
|
| 567 | | ti99_peb_state *peb = get_safe_token(device);
|
| 568 | | |
| 569 | | cpu_adjust_icount(device->machine->firstcpu,-4);
|
| 570 | | |
| 571 | | if (peb->active_card != -1)
|
| 572 | | {
|
| 573 | | handler = peb->expansion_ports[peb->active_card].mem_read;
|
| 574 | | if (handler)
|
| 575 | | reply = (*handler)(peb->space, offset);
|
| 576 | | }
|
| 577 | | |
| 578 | | return reply;
|
| 579 | | }
|
| 580 | | |
| 581 | | /*
|
| 582 | | Write mem in range >4000->5fff (ti-99/8)
|
| 583 | | */
|
| 584 | | WRITE8_DEVICE_HANDLER( ti99_8_peb_w )
|
| 585 | | {
|
| 586 | | write8_space_func handler;
|
| 587 | | ti99_peb_state *peb = get_safe_token(device);
|
| 588 | | |
| 589 | | cpu_adjust_icount(device->machine->firstcpu,-4);
|
| 590 | | |
| 591 | | if (peb->active_card != -1)
|
| 592 | | {
|
| 593 | | handler = peb->expansion_ports[peb->active_card].mem_write;
|
| 594 | | if (handler)
|
| 595 | | (*handler)(peb->space, offset, data);
|
| 596 | | }
|
| 597 | | }
|
| 598 | | |
| 599 | | /*
|
| 600 | | Read CRU in range >0400->1ffe (>200->fff) (snug sgcpu 99/4p)
|
| 601 | | */
|
| 602 | | READ8_DEVICE_HANDLER( ti99_4p_peb_cru_r )
|
| 603 | | {
|
| 604 | | int port;
|
| 605 | | cru_read_handler handler;
|
| 606 | | int reply;
|
| 607 | | ti99_peb_state *peb = get_safe_token(device);
|
| 608 | | |
| 609 | | port = offset >> 4;
|
| 610 | | handler = peb->ti99_4p_expansion_ports[port].cru_read;
|
| 611 | | |
| 612 | | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0;
|
| 613 | | |
| 614 | | return reply;
|
| 615 | | }
|
| 616 | | |
| 617 | | /*
|
| 618 | | Write CRU in range >0400->1ffe (>200->fff) (snug sgcpu 99/4p)
|
| 619 | | */
|
| 620 | | WRITE8_DEVICE_HANDLER( ti99_4p_peb_cru_w )
|
| 621 | | {
|
| 622 | | int port;
|
| 623 | | cru_write_handler handler;
|
| 624 | | ti99_peb_state *peb = get_safe_token(device);
|
| 625 | | |
| 626 | | int cru_address = (offset + 0x200)<<1;
|
| 627 | | |
| 628 | | port = offset >> 7;
|
| 629 | | handler = peb->ti99_4p_expansion_ports[port].cru_write;
|
| 630 | | |
| 631 | | if (handler)
|
| 632 | | (*handler)(device->machine, offset & 0x7f, data);
|
| 633 | | |
| 634 | | /* expansion card enable? */
|
| 635 | | if ((cru_address & 0xfe) == 0)
|
| 636 | | {
|
| 637 | | if (data & 1)
|
| 638 | | {
|
| 639 | | /* enable */
|
| 640 | | if (cru_address == 0x1e00)
|
| 641 | | {
|
| 642 | | // The internal mapper has precedence over the external bus.
|
| 643 | | // It may be turned on although there is another device currently turned on
|
| 644 | | peb->active_card_tmp = peb->active_card;
|
| 645 | | peb->mapper_override = TRUE;
|
| 646 | | }
|
| 647 | | peb->active_card = port;
|
| 648 | | }
|
| 649 | | else
|
| 650 | | {
|
| 651 | | if ((cru_address == 0x1e00) && peb->mapper_override)
|
| 652 | | {
|
| 653 | | peb->active_card = peb->active_card_tmp;
|
| 654 | | peb->mapper_override = FALSE;
|
| 655 | | }
|
| 656 | | else
|
| 657 | | {
|
| 658 | | if (port == peb->active_card) /* geez... who cares? */
|
| 659 | | {
|
| 660 | | peb->active_card = -1; /* no port selected */
|
| 661 | | }
|
| 662 | | }
|
| 663 | | }
|
| 664 | | }
|
| 665 | | }
|
| 666 | | |
| 667 | | /*
|
| 668 | | Read mem in range >4000->5ffe (snug sgcpu 99/4p)
|
| 669 | | */
|
| 670 | | READ16_DEVICE_HANDLER( ti99_4p_peb_r )
|
| 671 | | {
|
| 672 | | int reply = 0;
|
| 673 | | read8_space_func handler;
|
| 674 | | read16_space_func handler16;
|
| 675 | | ti99_peb_state *peb = get_safe_token(device);
|
| 676 | | |
| 677 | | if (peb->active_card == -1)
|
| 678 | | cpu_adjust_icount(device->machine->firstcpu,-4); /* ??? */
|
| 679 | | else
|
| 680 | | {
|
| 681 | | if (peb->ti99_4p_expansion_ports[peb->active_card].width == width_8bit)
|
| 682 | | {
|
| 683 | | cpu_adjust_icount(device->machine->firstcpu,-4);
|
| 684 | | |
| 685 | | handler = peb->ti99_4p_expansion_ports[peb->active_card].w.width_8bit.mem_read;
|
| 686 | | if (handler)
|
| 687 | | {
|
| 688 | | reply = (*handler)(peb->space, (offset << 1) + 1);
|
| 689 | | reply |= ((unsigned) (*handler)(peb->space, offset << 1)) << 8;
|
| 690 | | }
|
| 691 | | }
|
| 692 | | else
|
| 693 | | {
|
| 694 | | cpu_adjust_icount(device->machine->firstcpu,-1); /* ??? */
|
| 695 | | |
| 696 | | handler16 = peb->ti99_4p_expansion_ports[peb->active_card].w.width_16bit.mem_read;
|
| 697 | | if (handler16)
|
| 698 | | reply = (*handler16)(peb->space, offset, /*mem_mask*/0xffff);
|
| 699 | | }
|
| 700 | | }
|
| 701 | | |
| 702 | | if (peb->senila || peb->senilb)
|
| 703 | | {
|
| 704 | | if ((peb->active_card != -1) || (peb->senila && peb->senilb))
|
| 705 | | /* ah, the smell of burnt silicon... */
|
| 706 | | logerror("<Scrrrrr>: your computer has just burnt (maybe).\n");
|
| 707 | | |
| 708 | | if (peb->senila)
|
| 709 | | reply = (peb->ila & 0xff) | (peb->ila << 8);
|
| 710 | | else if (peb->senilb)
|
| 711 | | reply = (peb->ilb & 0xff) | (peb->ilb << 8);
|
| 712 | | }
|
| 713 | | |
| 714 | | // printf("[%04x:%04x] = %04x\n", (active_card<<8)+0x400, (offset<<1)+0x4000, reply);
|
| 715 | | return peb->tmp_buffer = reply;
|
| 716 | | }
|
| 717 | | |
| 718 | | /*
|
| 719 | | Write mem in range >4000->5ffe (snug sgcpu 99/4p)
|
| 720 | | */
|
| 721 | | WRITE16_DEVICE_HANDLER( ti99_4p_peb_w )
|
| 722 | | {
|
| 723 | | write8_space_func handler;
|
| 724 | | write16_space_func handler16;
|
| 725 | | ti99_peb_state *peb = get_safe_token(device);
|
| 726 | | |
| 727 | | if (peb->active_card == -1)
|
| 728 | | cpu_adjust_icount(device->machine->firstcpu,-4); /* ??? */
|
| 729 | | else
|
| 730 | | {
|
| 731 | | /* simulate byte write */
|
| 732 | | data = (peb->tmp_buffer & ~mem_mask) | (data & mem_mask);
|
| 733 | | |
| 734 | | if (peb->ti99_4p_expansion_ports[peb->active_card].width == width_8bit)
|
| 735 | | {
|
| 736 | | cpu_adjust_icount(device->machine->firstcpu,-4);
|
| 737 | | |
| 738 | | handler = peb->ti99_4p_expansion_ports[peb->active_card].w.width_8bit.mem_write;
|
| 739 | | if (handler)
|
| 740 | | {
|
| 741 | | (*handler)(peb->space, (offset << 1) + 1, data & 0xff);
|
| 742 | | (*handler)(peb->space, offset << 1, (data >> 8) & 0xff);
|
| 743 | | }
|
| 744 | | }
|
| 745 | | else
|
| 746 | | {
|
| 747 | | cpu_adjust_icount(device->machine->firstcpu,-1); /* ??? */
|
| 748 | | |
| 749 | | handler16 = peb->ti99_4p_expansion_ports[peb->active_card].w.width_16bit.mem_write;
|
| 750 | | if (handler16)
|
| 751 | | (*handler16)(peb->space, offset, data, /*mem_mask*/0xffff);
|
| 752 | | }
|
| 753 | | }
|
| 754 | | }
|
| 755 | | |
| 756 | | /*
|
| 757 | | Set the state of the SENILA line (snug sgcpu 99/4p)
|
| 758 | | */
|
| 759 | | void ti99_4p_peb_set_senila(running_device *box, int state)
|
| 760 | | {
|
| 761 | | ti99_peb_state *peb = get_safe_token(box);
|
| 762 | | peb->senila = (state != 0);
|
| 763 | | }
|
| 764 | | |
| 765 | | /*
|
| 766 | | Set the state of the SENILB line (snug sgcpu 99/4p)
|
| 767 | | */
|
| 768 | | void ti99_4p_peb_set_senilb(running_device *box, int state)
|
| 769 | | {
|
| 770 | | ti99_peb_state *peb = get_safe_token(box);
|
| 771 | | peb->senilb = (state != 0);
|
| 772 | | }
|
| 773 | | |
| 774 | | |
| 775 | | /***************************************************************************
|
| 776 | | DEVICE FUNCTIONS
|
| 777 | | ***************************************************************************/
|
| 778 | | |
| 779 | | static DEVICE_START( ti99_peb )
|
| 780 | | {
|
| 781 | | }
|
| 782 | | |
| 783 | | static DEVICE_STOP( ti99_peb )
|
| 784 | | {
|
| 785 | | }
|
| 786 | | |
| 787 | | static DEVICE_RESET( ti99_peb )
|
| 788 | | {
|
| 789 | | ti99_peb_state *peb = get_safe_token(device);
|
| 790 | | |
| 791 | | peb->has_16bit_peb = get_config(device)->mode16;
|
| 792 | | peb->inta_callback = get_config(device)->inta_callback;
|
| 793 | | peb->intb_callback = get_config(device)->intb_callback;
|
| 794 | | |
| 795 | | peb->active_card = -1;
|
| 796 | | peb->active_card_tmp = -1;
|
| 797 | | |
| 798 | | peb->mapper_override = FALSE; // for ti99_4p
|
| 799 | | |
| 800 | | peb->ila = 0;
|
| 801 | | peb->ilb = 0;
|
| 802 | | |
| 803 | | peb->space = cputag_get_address_space(device->machine, "maincpu", ADDRESS_SPACE_PROGRAM);
|
| 804 | | |
| 805 | | // Remove all card handlers; must be reinstalled
|
| 806 | | memset(peb->expansion_ports, 0, sizeof(peb->expansion_ports));
|
| 807 | | memset(peb->ti99_4p_expansion_ports, 0, sizeof(peb->ti99_4p_expansion_ports));
|
| 808 | | }
|
| 809 | | |
| 810 | | static const char DEVTEMPLATE_SOURCE[] = __FILE__;
|
| 811 | | |
| 812 | | #define DEVTEMPLATE_ID(p,s) p##ti99_peb##s
|
| 813 | | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_INLINE_CONFIG
|
| 814 | | #define DEVTEMPLATE_NAME "TI99 Peripheral Expansion System"
|
| 815 | | #define DEVTEMPLATE_FAMILY "External devices"
|
| 816 | | #include "devtempl.h"
|
| 817 | | |
| 818 | | DEFINE_LEGACY_DEVICE( PBOX, ti99_peb );
|
| 1 | /* |
| 2 | Peripheral expansion bus support. |
| 3 | |
| 4 | The ti-99/4, ti-99/4a, ti computer 99/8, myarc geneve, and snug sgcpu |
| 5 | 99/4p systems all feature a bus connector that enables the connection of |
| 6 | extension cards. (Although the hexbus is the preferred bus to add |
| 7 | additional peripherals to a ti-99/8, ti-99/8 is believed to be compatible |
| 8 | with the older PEB system.) In the case of the TI consoles, this bus |
| 9 | connector is located on the right side of the console. |
| 10 | |
| 11 | While a few extension cards connect to the side bus connector of the |
| 12 | ti-99/4(a) console directly, most extension cards were designed to be |
| 13 | inserted in a PEB instead. The PEB (Peripheral Expansion Box) is a big box |
| 14 | with an alimentation, a few bus drivers, and several card slots, that |
| 15 | connects to the ti-99/4(a) side port. The reason for using a PEB is that |
| 16 | daisy-chaining many modules caused the system to be unreliable due to the |
| 17 | noise produced by the successive contacts. (As a matter of fact, TI |
| 18 | initially released most of its extension cards as side bus units, but when |
| 19 | the design proved to be unreliable, the PEB was introduced. The TI speech |
| 20 | synthesizer was the only TI extension that remained on the side bus after |
| 21 | the introduction of the PEB, probably because TI wanted the speech |
| 22 | synthesizer to be a cheap extension, and the PEB was not cheap.) |
| 23 | |
| 24 | The third-party myarc geneve and snug sgcpu computers are actually cards |
| 25 | to be inserted into a PEB: they have a PC keyboard connector, and replace |
| 26 | the original ti-99 console completely. |
| 27 | |
| 28 | Each expansion card is assigned to one of 16 CRU address ranges. |
| 29 | I appended the names of known TI peripherals that use each port (I appended |
| 30 | '???' when I don't know what the peripheral is :-) ). |
| 31 | * 0x1000-0x10FE "For test equipment use on production line" |
| 32 | * 0x1100-0x11FE disk controller |
| 33 | * 0x1200-0x12FE modem??? |
| 34 | * 0x1300-0x13FE RS232 1&2, PIO 1 |
| 35 | * 0x1400-0x14FE unassigned |
| 36 | * 0x1500-0x15FE RS232 3&4, PIO 2 |
| 37 | * 0x1600-0x16FE unassigned |
| 38 | * 0x1700-0x17FE hex-bus (prototypes) |
| 39 | * 0x1800-0x18FE thermal printer (early peripheral) |
| 40 | * 0x1900-0x19FE EPROM programmer??? (Mezzanine board in July 1981's TI99/7 |
| 41 | prototype) |
| 42 | * 0x1A00-0x1AFE unassigned |
| 43 | * 0x1B00-0x1BFE TI GPL debugger card |
| 44 | * 0x1C00-0x1CFE Video Controller Card (Possibly the weirdest device. This |
| 45 | card is connected to the video output of the computer, to a VCR, and a |
| 46 | video monitor. It can control the VCR, connect the display to either |
| 47 | VCR output or computer video output, and it can read or save binary |
| 48 | data to video tape. I think it can act as a genlock interface (i.e. |
| 49 | TMS9918 transparent background shows the video signal), too, but I am |
| 50 | not sure about this.) |
| 51 | * 0x1D00-0x1DFE IEEE 488 Controller Card ('intelligent' parallel bus, |
| 52 | schematics on ftp.whtech.com) |
| 53 | * 0x1E00-0x1EFE unassigned |
| 54 | * 0x1F00-0x1FFE P-code card (part of a complete development system) |
| 55 | |
| 56 | The ti-99/8 implements 16 extra ports: |
| 57 | * 0x2000-0x26ff and 0x2800-0x2fff: reserved for future expansion? |
| 58 | * 0x2700-0x27ff: internal DSR |
| 59 | |
| 60 | The snug sgcpu 99/4p implements 12 extra ports: |
| 61 | * 0x0400-0x0eff: free |
| 62 | * 0x0f00-0x0fff: internal DSR |
| 63 | |
| 64 | Known mappings for 3rd party cards: |
| 65 | * Horizon RAMdisk: any ports from 0 to 7 (port 0 is most common). |
| 66 | * Myarc 128k/512k (RAM and DSR ROM): port 0 (0x1000-0x11FE) |
| 67 | * Corcomp 512k (RAM and DSR ROM): port unknown |
| 68 | * Super AMS 128k/512k: port 14 (0x1E00-0x1EFE) |
| 69 | * Foundation 128k/512k: port 14 (0x1E00-0x1EFE) |
| 70 | * Gram Karte: any port (0-15) |
| 71 | * EVPC (video card): 0x1400-0x14FE |
| 72 | * HSGPL (GROM replacement): 0x1B00-0x1BFE |
| 73 | |
| 74 | Of course, these devices additionally need some support routines, and |
| 75 | possibly memory-mapped I/O registers. To do so, memory range 0x4000-5FFF |
| 76 | is shared by all cards. The system enables each card as needed by writing |
| 77 | a 1 to the first CRU bit: when this happens the card can safely enable its |
| 78 | ROM, RAM, memory-mapped registers, etc. Provided the ROM uses the proper |
| 79 | ROM header, the system will recognize and call peripheral I/O functions, |
| 80 | interrupt service routines, and startup routines. (All these routines are |
| 81 | generally called DSR = Device Service Routine.) |
| 82 | |
| 83 | Also, the cards can trigger level-1 ("INTA") interrupts. A LOAD interrupt |
| 84 | is present on the side port of the ti-99/4(a), too, but it was never used |
| 85 | by TI, AFAIK. Also, an interrupt line called "INTB" is found on several |
| 86 | cards built by TI, but it is not connected: I am quite unsure whether it |
| 87 | was intended to be connected to the LOAD line, to the INT2 line of TMS9985, |
| 88 | or it was only to be used with the ti-99/4(a) development system running on |
| 89 | a TI990/10. This is not the only unused feature: cards built by TI support |
| 90 | sensing of an interrupt status register and 19-bit addressing, even though |
| 91 | ti-99/4(A) does not take advantage of these features. TI documentation |
| 92 | tells that some of these features are "defined by the Personal Computer PCC |
| 93 | at Texas Instrument", which is not very explicit (What on earth does "PCC" |
| 94 | stand for?). Maybe it refers to the ti-99/4(a) development system running |
| 95 | on a TI990/10, or to the ti-99/7 and ti-99/8 prototypes. |
| 96 | |
| 97 | Note that I use 8-bit RAM handlers. Obviously, using 16-bit handlers would |
| 98 | be equivalent and faster on a ti-99/4(a), but we need to interface the |
| 99 | extension cards not only to the ti-99/4(a), but to the geneve and ti-99/8 |
| 100 | emulators as well. |
| 101 | |
| 102 | June 2010: Reimplemented using device structure (MZ) (obsoletes 99_peb.c) |
| 103 | */ |
| 104 | |
| 105 | #include "emu.h" |
| 106 | #include "ti99_peb.h" |
| 107 | |
| 108 | enum width_t { |
| 109 | width_8bit = 0, |
| 110 | width_16bit |
| 111 | }; |
| 112 | /* expansion card structure for the snug sgcpu 99/4p system, which supports |
| 113 | dynamical 16-bit accesses (TI did not design this!) */ |
| 114 | typedef struct ti99_4p_peb_card_handlers_t |
| 115 | { |
| 116 | cru_read_handler cru_read; /* card CRU read handler */ |
| 117 | cru_write_handler cru_write; /* card CRU handler */ |
| 118 | |
| 119 | width_t width; |
| 120 | union |
| 121 | { |
| 122 | struct |
| 123 | { |
| 124 | read8_space_func mem_read; /* card mem read handler (8 bits) */ |
| 125 | write8_space_func mem_write; /* card mem write handler (8 bits) */ |
| 126 | } width_8bit; |
| 127 | struct |
| 128 | { |
| 129 | read16_space_func mem_read; /* card mem read handler (16 bits) */ |
| 130 | write16_space_func mem_write; /* card mem write handler (16 bits) */ |
| 131 | } width_16bit; |
| 132 | } w; |
| 133 | } ti99_4p_peb_card_handlers_t; |
| 134 | |
| 135 | typedef struct _ti99_peb_state |
| 136 | { |
| 137 | /* TRUE if we are using the snug sgcpu 99/4p 16-bit extensions */ |
| 138 | int has_16bit_peb; |
| 139 | |
| 140 | /* handlers for each of 28 slots */ |
| 141 | ti99_4p_peb_card_handlers_t ti99_4p_expansion_ports[28]; |
| 142 | |
| 143 | /* handlers for each of 16 slots + 16 extra slots for ti-99/8 */ |
| 144 | ti99_peb_card_handlers_t expansion_ports[16+16]; |
| 145 | |
| 146 | /* index of the currently active card (-1 if none) */ |
| 147 | int active_card; |
| 148 | |
| 149 | /* index of the currently active card, overridden by the mapper */ |
| 150 | int active_card_tmp; |
| 151 | |
| 152 | /* Mapper override. */ |
| 153 | int mapper_override; |
| 154 | |
| 155 | // ila: inta status register (not actually used on ti-99/4(a), but nevertheless |
| 156 | // present) |
| 157 | int ila; |
| 158 | |
| 159 | // ilb: intb status register (completely pointless on ti-99/4(a), as neither |
| 160 | // INTB nor SENILB are connected; OTOH, INTB can trigger interrupts on a Geneve, |
| 161 | // and snug sgcpu 99/4p can sense ILB) |
| 162 | int ilb; |
| 163 | |
| 164 | /* only used by the snug sgcpu 99/4p */ |
| 165 | int senila, senilb; |
| 166 | |
| 167 | /* hack to simulate TMS9900 byte write */ |
| 168 | int tmp_buffer; |
| 169 | |
| 170 | /* inta/intb handlers */ |
| 171 | void (*inta_callback)(running_machine *machine, int state); |
| 172 | void (*intb_callback)(running_machine *machine, int state); |
| 173 | |
| 174 | const address_space *space; |
| 175 | |
| 176 | } ti99_peb_state; |
| 177 | |
| 178 | INLINE ti99_peb_state *get_safe_token(running_device *device) |
| 179 | { |
| 180 | assert(device != NULL); |
| 181 | assert(downcast<legacy_device_base *>(device)->token() != NULL); |
| 182 | return (ti99_peb_state *)downcast<legacy_device_base *>(device)->token(); |
| 183 | } |
| 184 | |
| 185 | INLINE const ti99_peb_config *get_config(running_device *device) |
| 186 | { |
| 187 | assert(device != NULL); |
| 188 | assert(device->type() == PBOX); |
| 189 | return (const ti99_peb_config *) downcast<const legacy_device_config_base &>(device->baseconfig()).inline_config(); |
| 190 | } |
| 191 | |
| 192 | /* |
| 193 | Sets the handlers for one expansion port (normal 8-bit card) |
| 194 | |
| 195 | cru_base: CRU base address (any of 0x1000, 0x1100, 0x1200, ..., 0x1F00) |
| 196 | (snug sgcpu 99/4p accepts 0x0400, ..., 0x1F00, ti-99/8 accepts 0x1000, |
| 197 | ..., 0x2F00) |
| 198 | handler: handler structure for the given card |
| 199 | */ |
| 200 | void ti99_peb_set_card_handlers(running_device *box, int cru_base, const ti99_peb_card_handlers_t *handler) |
| 201 | { |
| 202 | int port; |
| 203 | ti99_peb_state *peb = get_safe_token(box); |
| 204 | |
| 205 | if (cru_base & 0xff) |
| 206 | return; |
| 207 | |
| 208 | if (peb->has_16bit_peb) |
| 209 | { |
| 210 | port = (cru_base - 0x0400) >> 8; |
| 211 | |
| 212 | if ((port>=0) && (port<28)) |
| 213 | { |
| 214 | peb->ti99_4p_expansion_ports[port].cru_read = handler->cru_read; |
| 215 | peb->ti99_4p_expansion_ports[port].cru_write = handler->cru_write; |
| 216 | peb->ti99_4p_expansion_ports[port].width = width_8bit; |
| 217 | peb->ti99_4p_expansion_ports[port].w.width_8bit.mem_read = handler->mem_read; |
| 218 | peb->ti99_4p_expansion_ports[port].w.width_8bit.mem_write = handler->mem_write; |
| 219 | } |
| 220 | } |
| 221 | else |
| 222 | { |
| 223 | port = (cru_base - 0x1000) >> 8; |
| 224 | |
| 225 | if ((port>=0) && (port</*16*/32)) |
| 226 | { |
| 227 | peb->expansion_ports[port] = *handler; |
| 228 | } |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | /* |
| 233 | Sets the handlers for one expansion port (special 16-bit card for snug |
| 234 | sgcpu 99/4p) |
| 235 | |
| 236 | cru_base: CRU base address (any of 0x1000, 0x1100, 0x1200, ..., 0x1F00) |
| 237 | (snug sgcpu 99/4p accepts 0x0400, ..., 0x1F00, ti-99/8 accepts 0x1000, |
| 238 | ..., 0x2F00) |
| 239 | handler: handler structure for the given card |
| 240 | */ |
| 241 | void ti99_peb_set_16bit_card_handlers(running_device *box, int cru_base, const ti99_peb_16bit_card_handlers_t *handler) |
| 242 | { |
| 243 | int port; |
| 244 | ti99_peb_state *peb = get_safe_token(box); |
| 245 | |
| 246 | if (cru_base & 0xff) |
| 247 | return; |
| 248 | |
| 249 | if (peb->has_16bit_peb) |
| 250 | { |
| 251 | port = (cru_base - 0x0400) >> 8; |
| 252 | |
| 253 | if ((port>=0) && (port<28)) |
| 254 | { |
| 255 | peb->ti99_4p_expansion_ports[port].cru_read = handler->cru_read; |
| 256 | peb->ti99_4p_expansion_ports[port].cru_write = handler->cru_write; |
| 257 | peb->ti99_4p_expansion_ports[port].width = width_16bit; |
| 258 | peb->ti99_4p_expansion_ports[port].w.width_16bit.mem_read = handler->mem_read; |
| 259 | peb->ti99_4p_expansion_ports[port].w.width_16bit.mem_write = handler->mem_write; |
| 260 | } |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | /* |
| 265 | Update ila status register and assert or clear INTA interrupt line |
| 266 | accordingly. |
| 267 | |
| 268 | bit: bit number ([0,7] for original INTA register, [8,15] are extra |
| 269 | "virtual" bits for devices that assert the INTA line without setting a |
| 270 | bit of the ILA register) |
| 271 | state: 1 to assert bit, 0 to clear |
| 272 | */ |
| 273 | void ti99_peb_set_ila_bit(running_device *box, int bit, int state) |
| 274 | { |
| 275 | ti99_peb_state *peb = get_safe_token(box); |
| 276 | if (state) |
| 277 | { |
| 278 | peb->ila |= 1 << bit; |
| 279 | if (peb->inta_callback) |
| 280 | (*peb->inta_callback)(box->machine, 1); |
| 281 | } |
| 282 | else |
| 283 | { |
| 284 | peb->ila &= ~(1 << bit); |
| 285 | if ((!peb->ila) && peb->inta_callback) |
| 286 | (*peb->inta_callback)(box->machine, 0); |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | /* |
| 291 | Update ilb status register and assert or clear INTB interrupt line |
| 292 | accordingly. |
| 293 | |
| 294 | bit: bit number ([0,7] for original INTAB register, [8,15] are extra |
| 295 | "virtual" bits for devices that assert the INTB line without setting a |
| 296 | bit of the ILB register) |
| 297 | state: 1 to assert bit, 0 to clear |
| 298 | */ |
| 299 | void ti99_peb_set_ilb_bit(running_device *box, int bit, int state) |
| 300 | { |
| 301 | ti99_peb_state *peb = get_safe_token(box); |
| 302 | |
| 303 | if (state) |
| 304 | { |
| 305 | peb->ilb |= 1 << bit; |
| 306 | if (peb->intb_callback) |
| 307 | (*peb->intb_callback)(box->machine, 1); |
| 308 | } |
| 309 | else |
| 310 | { |
| 311 | peb->ilb &= ~(1 << bit); |
| 312 | if ((!peb->ilb) && peb->intb_callback) |
| 313 | (*peb->intb_callback)(box->machine, 0); |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | |
| 318 | /* |
| 319 | Read CRU in range >1000->1ffe (>800->fff) (ti-99/4(a)) |
| 320 | */ |
| 321 | READ8_DEVICE_HANDLER( ti99_4x_peb_cru_r ) |
| 322 | { |
| 323 | int port; |
| 324 | cru_read_handler handler; |
| 325 | int reply; |
| 326 | ti99_peb_state *peb = get_safe_token(device); |
| 327 | |
| 328 | port = (offset & 0x00f0) >> 4; |
| 329 | handler = peb->expansion_ports[port].cru_read; |
| 330 | |
| 331 | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0; |
| 332 | |
| 333 | return reply; |
| 334 | } |
| 335 | |
| 336 | /* |
| 337 | Write CRU in range >1000->1ffe (>800->fff) (ti-99/4(a)) |
| 338 | */ |
| 339 | WRITE8_DEVICE_HANDLER( ti99_4x_peb_cru_w ) |
| 340 | { |
| 341 | int port; |
| 342 | cru_write_handler handler; |
| 343 | ti99_peb_state *peb = get_safe_token(device); |
| 344 | |
| 345 | port = (offset & 0x0780) >> 7; |
| 346 | handler = peb->expansion_ports[port].cru_write; |
| 347 | |
| 348 | if (handler) |
| 349 | (*handler)(device->machine, offset & 0x7f, data); |
| 350 | |
| 351 | /* expansion card enable? */ |
| 352 | if ((offset & 0x7f) == 0) |
| 353 | { |
| 354 | if (data & 1) |
| 355 | { |
| 356 | /* enable */ |
| 357 | peb->active_card = port; |
| 358 | } |
| 359 | else |
| 360 | { |
| 361 | if (port == peb->active_card) /* geez... who cares? */ |
| 362 | { |
| 363 | peb->active_card = -1; /* no port selected */ |
| 364 | } |
| 365 | } |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | /* |
| 370 | Read mem in range >4000->5ffe (ti-99/4(a)) |
| 371 | */ |
| 372 | READ16_DEVICE_HANDLER( ti99_4x_peb_r ) |
| 373 | { |
| 374 | int reply = 0; |
| 375 | read8_space_func handler; |
| 376 | ti99_peb_state *peb = get_safe_token(device); |
| 377 | |
| 378 | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 379 | |
| 380 | if (peb->active_card != -1) |
| 381 | { |
| 382 | handler = peb->expansion_ports[peb->active_card].mem_read; |
| 383 | if (handler) |
| 384 | { |
| 385 | reply = (*handler)(peb->space, (offset << 1) + 1); |
| 386 | reply |= ((unsigned) (*handler)(peb->space, offset << 1)) << 8; |
| 387 | } |
| 388 | } |
| 389 | // printf("[%04x:%04x] = %04x\n", (active_card<<8)+0x1000, (offset<<1)+0x4000, reply); |
| 390 | |
| 391 | return peb->tmp_buffer = reply; |
| 392 | } |
| 393 | |
| 394 | /* |
| 395 | Write mem in range >4000->5ffe (ti-99/4(a)) |
| 396 | */ |
| 397 | WRITE16_DEVICE_HANDLER( ti99_4x_peb_w ) |
| 398 | { |
| 399 | write8_space_func handler; |
| 400 | ti99_peb_state *peb = get_safe_token(device); |
| 401 | |
| 402 | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 403 | |
| 404 | /* simulate byte write */ |
| 405 | data = (peb->tmp_buffer & ~mem_mask) | (data & mem_mask); |
| 406 | |
| 407 | if (peb->active_card != -1) |
| 408 | { |
| 409 | handler = peb->expansion_ports[peb->active_card].mem_write; |
| 410 | if (handler) |
| 411 | { |
| 412 | (*handler)(peb->space, (offset << 1) + 1, data & 0xff); |
| 413 | (*handler)(peb->space, offset << 1, (data >> 8) & 0xff); |
| 414 | } |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | |
| 419 | /* |
| 420 | Read CRU in range >1000->1ffe (>800->fff) (Geneve) |
| 421 | */ |
| 422 | READ8_DEVICE_HANDLER( geneve_peb_cru_r ) |
| 423 | { |
| 424 | int port; |
| 425 | cru_read_handler handler; |
| 426 | int reply; |
| 427 | ti99_peb_state *peb = get_safe_token(device); |
| 428 | |
| 429 | port = (offset & 0x00f0) >> 4; |
| 430 | handler = peb->expansion_ports[port].cru_read; |
| 431 | |
| 432 | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0; |
| 433 | |
| 434 | return reply; |
| 435 | } |
| 436 | |
| 437 | /* |
| 438 | Write CRU in range >1000->1ffe (>800->fff) (Geneve) |
| 439 | */ |
| 440 | WRITE8_DEVICE_HANDLER( geneve_peb_cru_w ) |
| 441 | { |
| 442 | int port; |
| 443 | cru_write_handler handler; |
| 444 | ti99_peb_state *peb = get_safe_token(device); |
| 445 | |
| 446 | port = (offset & 0x0780) >> 7; |
| 447 | handler = peb->expansion_ports[port].cru_write; |
| 448 | |
| 449 | if (handler) |
| 450 | (*handler)(device->machine, offset & 0x7f, data); |
| 451 | |
| 452 | /* expansion card enable? */ |
| 453 | if ((offset & 0x7f) == 0) |
| 454 | { |
| 455 | if (data & 1) |
| 456 | { |
| 457 | /* enable */ |
| 458 | peb->active_card = port; |
| 459 | } |
| 460 | else |
| 461 | { |
| 462 | if (port == peb->active_card) /* geez... who cares? */ |
| 463 | { |
| 464 | peb->active_card = -1; /* no port selected */ |
| 465 | } |
| 466 | } |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | /* |
| 471 | Read mem in range >4000->5fff (Geneve) |
| 472 | */ |
| 473 | READ8_DEVICE_HANDLER( geneve_peb_r ) |
| 474 | { |
| 475 | int reply = 0; |
| 476 | read8_space_func handler; |
| 477 | ti99_peb_state *peb = get_safe_token(device); |
| 478 | |
| 479 | cpu_adjust_icount(device->machine->firstcpu,-8); |
| 480 | |
| 481 | if (peb->active_card != -1) |
| 482 | { |
| 483 | handler = peb->expansion_ports[peb->active_card].mem_read; |
| 484 | if (handler) |
| 485 | reply = (*handler)(peb->space, offset); |
| 486 | } |
| 487 | |
| 488 | return reply; |
| 489 | } |
| 490 | |
| 491 | /* |
| 492 | Write mem in range >4000->5fff (Geneve) |
| 493 | */ |
| 494 | WRITE8_DEVICE_HANDLER( geneve_peb_w ) |
| 495 | { |
| 496 | write8_space_func handler; |
| 497 | ti99_peb_state *peb = get_safe_token(device); |
| 498 | |
| 499 | cpu_adjust_icount(device->machine->firstcpu,-8); |
| 500 | |
| 501 | if (peb->active_card != -1) |
| 502 | { |
| 503 | handler = peb->expansion_ports[peb->active_card].mem_write; |
| 504 | if (handler) |
| 505 | (*handler)(peb->space, offset, data); |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | /* |
| 510 | Read CRU in range >1000->2ffe (>0800->17ff) (ti-99/8) |
| 511 | */ |
| 512 | READ8_DEVICE_HANDLER( ti99_8_peb_cru_r ) |
| 513 | { |
| 514 | int port; |
| 515 | cru_read_handler handler; |
| 516 | int reply; |
| 517 | ti99_peb_state *peb = get_safe_token(device); |
| 518 | |
| 519 | port = (offset & 0x01f0) >> 4; |
| 520 | handler = peb->expansion_ports[port].cru_read; |
| 521 | |
| 522 | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0; |
| 523 | |
| 524 | return reply; |
| 525 | } |
| 526 | |
| 527 | /* |
| 528 | Write CRU in range >1000->2ffe (>0800->17ff) (ti-99/8) |
| 529 | */ |
| 530 | WRITE8_DEVICE_HANDLER( ti99_8_peb_cru_w ) |
| 531 | { |
| 532 | int port; |
| 533 | cru_write_handler handler; |
| 534 | ti99_peb_state *peb = get_safe_token(device); |
| 535 | |
| 536 | port = (offset & 0x0f80) >> 7; |
| 537 | handler = peb->expansion_ports[port].cru_write; |
| 538 | |
| 539 | if (handler) |
| 540 | (*handler)(device->machine, offset & 0x7f, data); |
| 541 | |
| 542 | /* expansion card enable? */ |
| 543 | if ((offset & 0x7f) == 0) |
| 544 | { |
| 545 | if (data & 1) |
| 546 | { |
| 547 | /* enable */ |
| 548 | peb->active_card = port; |
| 549 | } |
| 550 | else |
| 551 | { |
| 552 | if (port == peb->active_card) /* geez... who cares? */ |
| 553 | { |
| 554 | peb->active_card = -1; /* no port selected */ |
| 555 | } |
| 556 | } |
| 557 | } |
| 558 | } |
| 559 | |
| 560 | /* |
| 561 | Read mem in range >4000->5fff (ti-99/8) |
| 562 | */ |
| 563 | READ8_DEVICE_HANDLER( ti99_8_peb_r ) |
| 564 | { |
| 565 | int reply = 0; |
| 566 | read8_space_func handler; |
| 567 | ti99_peb_state *peb = get_safe_token(device); |
| 568 | |
| 569 | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 570 | |
| 571 | if (peb->active_card != -1) |
| 572 | { |
| 573 | handler = peb->expansion_ports[peb->active_card].mem_read; |
| 574 | if (handler) |
| 575 | reply = (*handler)(peb->space, offset); |
| 576 | } |
| 577 | |
| 578 | return reply; |
| 579 | } |
| 580 | |
| 581 | /* |
| 582 | Write mem in range >4000->5fff (ti-99/8) |
| 583 | */ |
| 584 | WRITE8_DEVICE_HANDLER( ti99_8_peb_w ) |
| 585 | { |
| 586 | write8_space_func handler; |
| 587 | ti99_peb_state *peb = get_safe_token(device); |
| 588 | |
| 589 | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 590 | |
| 591 | if (peb->active_card != -1) |
| 592 | { |
| 593 | handler = peb->expansion_ports[peb->active_card].mem_write; |
| 594 | if (handler) |
| 595 | (*handler)(peb->space, offset, data); |
| 596 | } |
| 597 | } |
| 598 | |
| 599 | /* |
| 600 | Read CRU in range >0400->1ffe (>200->fff) (snug sgcpu 99/4p) |
| 601 | */ |
| 602 | READ8_DEVICE_HANDLER( ti99_4p_peb_cru_r ) |
| 603 | { |
| 604 | int port; |
| 605 | cru_read_handler handler; |
| 606 | int reply; |
| 607 | ti99_peb_state *peb = get_safe_token(device); |
| 608 | |
| 609 | port = offset >> 4; |
| 610 | handler = peb->ti99_4p_expansion_ports[port].cru_read; |
| 611 | |
| 612 | reply = (handler) ? (*handler)(device->machine, offset & 0xf) : 0; |
| 613 | |
| 614 | return reply; |
| 615 | } |
| 616 | |
| 617 | /* |
| 618 | Write CRU in range >0400->1ffe (>200->fff) (snug sgcpu 99/4p) |
| 619 | */ |
| 620 | WRITE8_DEVICE_HANDLER( ti99_4p_peb_cru_w ) |
| 621 | { |
| 622 | int port; |
| 623 | cru_write_handler handler; |
| 624 | ti99_peb_state *peb = get_safe_token(device); |
| 625 | |
| 626 | int cru_address = (offset + 0x200)<<1; |
| 627 | |
| 628 | port = offset >> 7; |
| 629 | handler = peb->ti99_4p_expansion_ports[port].cru_write; |
| 630 | |
| 631 | if (handler) |
| 632 | (*handler)(device->machine, offset & 0x7f, data); |
| 633 | |
| 634 | /* expansion card enable? */ |
| 635 | if ((cru_address & 0xfe) == 0) |
| 636 | { |
| 637 | if (data & 1) |
| 638 | { |
| 639 | /* enable */ |
| 640 | if (cru_address == 0x1e00) |
| 641 | { |
| 642 | // The internal mapper has precedence over the external bus. |
| 643 | // It may be turned on although there is another device currently turned on |
| 644 | peb->active_card_tmp = peb->active_card; |
| 645 | peb->mapper_override = TRUE; |
| 646 | } |
| 647 | peb->active_card = port; |
| 648 | } |
| 649 | else |
| 650 | { |
| 651 | if ((cru_address == 0x1e00) && peb->mapper_override) |
| 652 | { |
| 653 | peb->active_card = peb->active_card_tmp; |
| 654 | peb->mapper_override = FALSE; |
| 655 | } |
| 656 | else |
| 657 | { |
| 658 | if (port == peb->active_card) /* geez... who cares? */ |
| 659 | { |
| 660 | peb->active_card = -1; /* no port selected */ |
| 661 | } |
| 662 | } |
| 663 | } |
| 664 | } |
| 665 | } |
| 666 | |
| 667 | /* |
| 668 | Read mem in range >4000->5ffe (snug sgcpu 99/4p) |
| 669 | */ |
| 670 | READ16_DEVICE_HANDLER( ti99_4p_peb_r ) |
| 671 | { |
| 672 | int reply = 0; |
| 673 | read8_space_func handler; |
| 674 | read16_space_func handler16; |
| 675 | ti99_peb_state *peb = get_safe_token(device); |
| 676 | |
| 677 | if (peb->active_card == -1) |
| 678 | cpu_adjust_icount(device->machine->firstcpu,-4); /* ??? */ |
| 679 | else |
| 680 | { |
| 681 | if (peb->ti99_4p_expansion_ports[peb->active_card].width == width_8bit) |
| 682 | { |
| 683 | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 684 | |
| 685 | handler = peb->ti99_4p_expansion_ports[peb->active_card].w.width_8bit.mem_read; |
| 686 | if (handler) |
| 687 | { |
| 688 | reply = (*handler)(peb->space, (offset << 1) + 1); |
| 689 | reply |= ((unsigned) (*handler)(peb->space, offset << 1)) << 8; |
| 690 | } |
| 691 | } |
| 692 | else |
| 693 | { |
| 694 | cpu_adjust_icount(device->machine->firstcpu,-1); /* ??? */ |
| 695 | |
| 696 | handler16 = peb->ti99_4p_expansion_ports[peb->active_card].w.width_16bit.mem_read; |
| 697 | if (handler16) |
| 698 | reply = (*handler16)(peb->space, offset, /*mem_mask*/0xffff); |
| 699 | } |
| 700 | } |
| 701 | |
| 702 | if (peb->senila || peb->senilb) |
| 703 | { |
| 704 | if ((peb->active_card != -1) || (peb->senila && peb->senilb)) |
| 705 | /* ah, the smell of burnt silicon... */ |
| 706 | logerror("<Scrrrrr>: your computer has just burnt (maybe).\n"); |
| 707 | |
| 708 | if (peb->senila) |
| 709 | reply = (peb->ila & 0xff) | (peb->ila << 8); |
| 710 | else if (peb->senilb) |
| 711 | reply = (peb->ilb & 0xff) | (peb->ilb << 8); |
| 712 | } |
| 713 | |
| 714 | // printf("[%04x:%04x] = %04x\n", (active_card<<8)+0x400, (offset<<1)+0x4000, reply); |
| 715 | return peb->tmp_buffer = reply; |
| 716 | } |
| 717 | |
| 718 | /* |
| 719 | Write mem in range >4000->5ffe (snug sgcpu 99/4p) |
| 720 | */ |
| 721 | WRITE16_DEVICE_HANDLER( ti99_4p_peb_w ) |
| 722 | { |
| 723 | write8_space_func handler; |
| 724 | write16_space_func handler16; |
| 725 | ti99_peb_state *peb = get_safe_token(device); |
| 726 | |
| 727 | if (peb->active_card == -1) |
| 728 | cpu_adjust_icount(device->machine->firstcpu,-4); /* ??? */ |
| 729 | else |
| 730 | { |
| 731 | /* simulate byte write */ |
| 732 | data = (peb->tmp_buffer & ~mem_mask) | (data & mem_mask); |
| 733 | |
| 734 | if (peb->ti99_4p_expansion_ports[peb->active_card].width == width_8bit) |
| 735 | { |
| 736 | cpu_adjust_icount(device->machine->firstcpu,-4); |
| 737 | |
| 738 | handler = peb->ti99_4p_expansion_ports[peb->active_card].w.width_8bit.mem_write; |
| 739 | if (handler) |
| 740 | { |
| 741 | (*handler)(peb->space, (offset << 1) + 1, data & 0xff); |
| 742 | (*handler)(peb->space, offset << 1, (data >> 8) & 0xff); |
| 743 | } |
| 744 | } |
| 745 | else |
| 746 | { |
| 747 | cpu_adjust_icount(device->machine->firstcpu,-1); /* ??? */ |
| 748 | |
| 749 | handler16 = peb->ti99_4p_expansion_ports[peb->active_card].w.width_16bit.mem_write; |
| 750 | if (handler16) |
| 751 | (*handler16)(peb->space, offset, data, /*mem_mask*/0xffff); |
| 752 | } |
| 753 | } |
| 754 | } |
| 755 | |
| 756 | /* |
| 757 | Set the state of the SENILA line (snug sgcpu 99/4p) |
| 758 | */ |
| 759 | void ti99_4p_peb_set_senila(running_device *box, int state) |
| 760 | { |
| 761 | ti99_peb_state *peb = get_safe_token(box); |
| 762 | peb->senila = (state != 0); |
| 763 | } |
| 764 | |
| 765 | /* |
| 766 | Set the state of the SENILB line (snug sgcpu 99/4p) |
| 767 | */ |
| 768 | void ti99_4p_peb_set_senilb(running_device *box, int state) |
| 769 | { |
| 770 | ti99_peb_state *peb = get_safe_token(box); |
| 771 | peb->senilb = (state != 0); |
| 772 | } |
| 773 | |
| 774 | |
| 775 | /*************************************************************************** |
| 776 | DEVICE FUNCTIONS |
| 777 | ***************************************************************************/ |
| 778 | |
| 779 | static DEVICE_START( ti99_peb ) |
| 780 | { |
| 781 | } |
| 782 | |
| 783 | static DEVICE_STOP( ti99_peb ) |
| 784 | { |
| 785 | } |
| 786 | |
| 787 | static DEVICE_RESET( ti99_peb ) |
| 788 | { |
| 789 | ti99_peb_state *peb = get_safe_token(device); |
| 790 | |
| 791 | peb->has_16bit_peb = get_config(device)->mode16; |
| 792 | peb->inta_callback = get_config(device)->inta_callback; |
| 793 | peb->intb_callback = get_config(device)->intb_callback; |
| 794 | |
| 795 | peb->active_card = -1; |
| 796 | peb->active_card_tmp = -1; |
| 797 | |
| 798 | peb->mapper_override = FALSE; // for ti99_4p |
| 799 | |
| 800 | peb->ila = 0; |
| 801 | peb->ilb = 0; |
| 802 | |
| 803 | peb->space = cputag_get_address_space(device->machine, "maincpu", ADDRESS_SPACE_PROGRAM); |
| 804 | |
| 805 | // Remove all card handlers; must be reinstalled |
| 806 | memset(peb->expansion_ports, 0, sizeof(peb->expansion_ports)); |
| 807 | memset(peb->ti99_4p_expansion_ports, 0, sizeof(peb->ti99_4p_expansion_ports)); |
| 808 | } |
| 809 | |
| 810 | static const char DEVTEMPLATE_SOURCE[] = __FILE__; |
| 811 | |
| 812 | #define DEVTEMPLATE_ID(p,s) p##ti99_peb##s |
| 813 | #define DEVTEMPLATE_FEATURES DT_HAS_START | DT_HAS_STOP | DT_HAS_RESET | DT_HAS_INLINE_CONFIG |
| 814 | #define DEVTEMPLATE_NAME "TI99 Peripheral Expansion System" |
| 815 | #define DEVTEMPLATE_FAMILY "External devices" |
| 816 | #include "devtempl.h" |
| 817 | |
| 818 | DEFINE_LEGACY_DEVICE( PBOX, ti99_peb ); |
src/mess/tools/imgtool/modules/cybikoxt.c
| r8681 | r8682 | |
| 1 | | /*
|
| 2 | | |
| 3 | | Cybiko Xtreme File System
|
| 4 | | |
| 5 | | (c) 2010 Tim Schuerewegen
|
| 6 | | |
| 7 | | */
|
| 8 | | |
| 9 | | #include "imgtool.h"
|
| 10 | | |
| 11 | | #include <zlib.h>
|
| 12 | | |
| 13 | | typedef struct _cybiko_file_system cybiko_file_system;
|
| 14 | | struct _cybiko_file_system
|
| 15 | | {
|
| 16 | | imgtool_stream *stream;
|
| 17 | | UINT32 page_count, page_size, block_count_boot, block_count_file;
|
| 18 | | UINT16 write_count;
|
| 19 | | };
|
| 20 | | |
| 21 | | typedef struct _cybiko_iter cybiko_iter;
|
| 22 | | struct _cybiko_iter
|
| 23 | | {
|
| 24 | | UINT16 block;
|
| 25 | | };
|
| 26 | | |
| 27 | | typedef struct _cfs_file cfs_file;
|
| 28 | | struct _cfs_file
|
| 29 | | {
|
| 30 | | char name[64]; // name of the file
|
| 31 | | UINT32 date; // date/time of the file (seconds since 1900/01/01)
|
| 32 | | UINT32 size; // size of the file
|
| 33 | | UINT32 blocks; // number of blocks occupied by the file
|
| 34 | | };
|
| 35 | | |
| 36 | | enum
|
| 37 | | {
|
| 38 | | BLOCK_TYPE_INVALID,
|
| 39 | | BLOCK_TYPE_BOOT,
|
| 40 | | BLOCK_TYPE_FILE
|
| 41 | | };
|
| 42 | | |
| 43 | | #define MAX_PAGE_SIZE (264 * 2)
|
| 44 | | |
| 45 | | #define INVALID_FILE_ID 0xFFFF
|
| 46 | | |
| 47 | | #define BLOCK_USED(x) (x[0] & 0x80)
|
| 48 | | #define BLOCK_FILE_ID(x) buffer_read_16_be( x + 2)
|
| 49 | | #define BLOCK_PART_ID(x) buffer_read_16_be( x + 4)
|
| 50 | | #define BLOCK_FILENAME(x) (char*)(x + 7)
|
| 51 | | |
| 52 | | #define FILE_HEADER_SIZE 0x48
|
| 53 | | |
| 54 | | // 2208988800 is the number of seconds between 1900/01/01 and 1970/01/01
|
| 55 | | |
| 56 | | static time_t time_crack( UINT32 cfs_time)
|
| 57 | | {
|
| 58 | | return (time_t)(cfs_time - 2208988800UL);
|
| 59 | | }
|
| 60 | | |
| 61 | | static UINT32 time_setup( time_t ansi_time)
|
| 62 | | {
|
| 63 | | return (UINT32)(ansi_time + 2208988800UL);
|
| 64 | | }
|
| 65 | | |
| 66 | | static UINT32 buffer_read_32_be( UINT8 *buffer)
|
| 67 | | {
|
| 68 | | return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3] << 0);
|
| 69 | | }
|
| 70 | | |
| 71 | | static UINT16 buffer_read_16_be( UINT8 *buffer)
|
| 72 | | {
|
| 73 | | return (buffer[0] << 8) | (buffer[1] << 0);
|
| 74 | | }
|
| 75 | | |
| 76 | | static void buffer_write_32_be( UINT8 *buffer, UINT32 data)
|
| 77 | | {
|
| 78 | | buffer[0] = (data >> 24) & 0xFF;
|
| 79 | | buffer[1] = (data >> 16) & 0xFF;
|
| 80 | | buffer[2] = (data >> 8) & 0xFF;
|
| 81 | | buffer[3] = (data >> 0) & 0xFF;
|
| 82 | | }
|
| 83 | | |
| 84 | | static void buffer_write_16_be( UINT8 *buffer, UINT16 data)
|
| 85 | | {
|
| 86 | | buffer[0] = (data >> 8) & 0xFF;
|
| 87 | | buffer[1] = (data >> 0) & 0xFF;
|
| 88 | | }
|
| 89 | | |
| 90 | | // page = crc (2) + data (x)
|
| 91 | | |
| 92 | | static UINT16 page_buffer_calc_checksum( UINT8 *data, UINT32 size)
|
| 93 | | {
|
| 94 | | int i;
|
| 95 | | UINT32 val = 0;
|
| 96 | | for (i = 0; i < size; i++)
|
| 97 | | {
|
| 98 | | val = (val ^ data[i] ^ i) << 1;
|
| 99 | | val = val | ((val >> 16) & 0x0001);
|
| 100 | | }
|
| 101 | | return val;
|
| 102 | | }
|
| 103 | | |
| 104 | | static int page_buffer_verify( UINT8 *buffer, UINT32 size, int block_type)
|
| 105 | | {
|
| 106 | | // checksum
|
| 107 | | if (block_type == BLOCK_TYPE_FILE)
|
| 108 | | {
|
| 109 | | UINT32 checksum_page, checksum_calc;
|
| 110 | | checksum_calc = page_buffer_calc_checksum( buffer + 2, size - 2);
|
| 111 | | checksum_page = buffer_read_16_be( buffer + 0);
|
| 112 | | if (checksum_calc != checksum_page) return FALSE;
|
| 113 | | }
|
| 114 | | // ok
|
| 115 | | return TRUE;
|
| 116 | | }
|
| 117 | | |
| 118 | | static int cfs_block_to_page( cybiko_file_system *cfs, int block_type, UINT32 block, UINT32 *page)
|
| 119 | | {
|
| 120 | | switch (block_type)
|
| 121 | | {
|
| 122 | | case BLOCK_TYPE_BOOT : if (page) *page = block; return TRUE;
|
| 123 | | case BLOCK_TYPE_FILE : if (page) *page = block + cfs->block_count_boot; return TRUE;
|
| 124 | | default : return FALSE;
|
| 125 | | }
|
| 126 | | }
|
| 127 | | |
| 128 | | static int cfs_page_to_block( cybiko_file_system *cfs, UINT32 page, int *block_type, UINT32 *block)
|
| 129 | | {
|
| 130 | | UINT32 tmp = page;
|
| 131 | | // boot block
|
| 132 | | if (tmp < cfs->block_count_boot)
|
| 133 | | {
|
| 134 | | if (block_type) *block_type = BLOCK_TYPE_BOOT;
|
| 135 | | if (block) *block = tmp;
|
| 136 | | return TRUE;
|
| 137 | | }
|
| 138 | | tmp -= cfs->block_count_boot;
|
| 139 | | // file block
|
| 140 | | if (tmp < cfs->block_count_file)
|
| 141 | | {
|
| 142 | | if (block_type) *block_type = BLOCK_TYPE_FILE;
|
| 143 | | if (block) *block = tmp;
|
| 144 | | return TRUE;
|
| 145 | | }
|
| 146 | | tmp -= cfs->block_count_file;
|
| 147 | | // error
|
| 148 | | return FALSE;
|
| 149 | | }
|
| 150 | | |
| 151 | | static int cfs_page_read( cybiko_file_system *cfs, UINT8 *buffer, UINT32 page)
|
| 152 | | {
|
| 153 | | if (page >= cfs->page_count) return FALSE;
|
| 154 | | stream_seek( cfs->stream, page * cfs->page_size, SEEK_SET);
|
| 155 | | stream_read( cfs->stream, buffer, cfs->page_size);
|
| 156 | | return TRUE;
|
| 157 | | }
|
| 158 | | |
| 159 | | static int cfs_page_write( cybiko_file_system *cfs, UINT8 *buffer, UINT32 page)
|
| 160 | | {
|
| 161 | | if (page >= cfs->page_count) return FALSE;
|
| 162 | | stream_seek( cfs->stream, page * cfs->page_size, SEEK_SET);
|
| 163 | | stream_write( cfs->stream, buffer, cfs->page_size);
|
| 164 | | return TRUE;
|
| 165 | | }
|
| 166 | | |
| 167 | | static int cfs_block_read( cybiko_file_system *cfs, UINT8 *buffer, int block_type, UINT32 block)
|
| 168 | | {
|
| 169 | | UINT8 buffer_page[MAX_PAGE_SIZE];
|
| 170 | | UINT32 page;
|
| 171 | | if (!cfs_block_to_page( cfs, block_type, block, &page)) return FALSE;
|
| 172 | | if (!cfs_page_read( cfs, buffer_page, page)) return FALSE;
|
| 173 | | memcpy( buffer, buffer_page + 2, cfs->page_size - 2);
|
| 174 | | return TRUE;
|
| 175 | | }
|
| 176 | | |
| 177 | | static int cfs_block_write( cybiko_file_system *cfs, UINT8 *buffer, int block_type, UINT32 block)
|
| 178 | | {
|
| 179 | | UINT8 buffer_page[MAX_PAGE_SIZE];
|
| 180 | | UINT32 page;
|
| 181 | | UINT16 checksum;
|
| 182 | | memcpy( buffer_page + 2, buffer, cfs->page_size - 2);
|
| 183 | | if (block_type == BLOCK_TYPE_BOOT)
|
| 184 | | {
|
| 185 | | checksum = 0xFFFF;
|
| 186 | | }
|
| 187 | | else
|
| 188 | | {
|
| 189 | | checksum = page_buffer_calc_checksum( buffer_page + 2, cfs->page_size - 2);
|
| 190 | | }
|
| 191 | | buffer_write_16_be( buffer_page + 0, checksum);
|
| 192 | | if (!cfs_block_to_page( cfs, block_type, block, &page)) return FALSE;
|
| 193 | | if (!cfs_page_write( cfs, buffer_page, page)) return FALSE;
|
| 194 | | return TRUE;
|
| 195 | | }
|
| 196 | | |
| 197 | | static int cfs_file_delete( cybiko_file_system *cfs, UINT16 file_id)
|
| 198 | | {
|
| 199 | | UINT8 buffer[MAX_PAGE_SIZE];
|
| 200 | | int i;
|
| 201 | | for (i=0;i<cfs->block_count_file;i++)
|
| 202 | | {
|
| 203 | | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return FALSE;
|
| 204 | | if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id))
|
| 205 | | {
|
| 206 | | buffer[0] &= ~0x80;
|
| 207 | | if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return FALSE;
|
| 208 | | }
|
| 209 | | }
|
| 210 | | return TRUE;
|
| 211 | | }
|
| 212 | | |
| 213 | | static int cfs_file_info( cybiko_file_system *cfs, UINT16 file_id, cfs_file *file)
|
| 214 | | {
|
| 215 | | UINT8 buffer[MAX_PAGE_SIZE];
|
| 216 | | int i;
|
| 217 | | file->blocks = file->size = 0;
|
| 218 | | for (i=0;i<cfs->block_count_file;i++)
|
| 219 | | {
|
| 220 | | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return FALSE;
|
| 221 | | if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id))
|
| 222 | | {
|
| 223 | | if (BLOCK_PART_ID(buffer) == 0)
|
| 224 | | {
|
| 225 | | strcpy( file->name, BLOCK_FILENAME(buffer));
|
| 226 | | file->date = buffer_read_32_be( buffer + 6 + FILE_HEADER_SIZE - 4);
|
| 227 | | }
|
| 228 | | file->size += buffer[1];
|
| 229 | | file->blocks++;
|
| 230 | | }
|
| 231 | | }
|
| 232 | | return (file->blocks > 0) ? TRUE : FALSE;
|
| 233 | | }
|
| 234 | | |
| 235 | | static int cfs_file_find( cybiko_file_system *cfs, const char *filename, UINT16 *file_id)
|
| 236 | | {
|
| 237 | | UINT8 buffer[MAX_PAGE_SIZE];
|
| 238 | | int i;
|
| 239 | | for (i=0;i<cfs->block_count_file;i++)
|
| 240 | | {
|
| 241 | | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return FALSE;
|
| 242 | | if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0) && (strcmp( filename, BLOCK_FILENAME(buffer)) == 0))
|
| 243 | | {
|
| 244 | | *file_id = i;
|
| 245 | | return TRUE;
|
| 246 | | }
|
| 247 | | }
|
| 248 | | return FALSE;
|
| 249 | | }
|
| 250 | | |
| 251 | | static int cfs_verify( cybiko_file_system *cfs)
|
| 252 | | {
|
| 253 | | UINT8 buffer[MAX_PAGE_SIZE];
|
| 254 | | int i, block_type;
|
| 255 | | for (i=0;i<cfs->page_count;i++)
|
| 256 | | {
|
| 257 | | if (!cfs_page_read( cfs, buffer, i)) return FALSE;
|
| 258 | | if (!cfs_page_to_block( cfs, i, &block_type, NULL)) return FALSE;
|
| 259 | | if (!page_buffer_verify( buffer, cfs->page_size, block_type)) return FALSE;
|
| 260 | | }
|
| 261 | | return TRUE;
|
| 262 | | }
|
| 263 | | |
| 264 | | static int cfs_init( cybiko_file_system *cfs, imgtool_stream *stream)
|
| 265 | | {
|
| 266 | | cfs->stream = stream;
|
| 267 | | cfs->page_count = 2005;
|
| 268 | | cfs->page_size = 258;
|
| 269 | | cfs->block_count_boot = 5;
|
| 270 | | cfs->block_count_file = cfs->page_count - cfs->block_count_boot;
|
| 271 | | cfs->write_count = 0;
|
| 272 | | return TRUE;
|
| 273 | | }
|
| 274 | | |
| 275 | | static int cfs_format( cybiko_file_system *cfs)
|
| 276 | | {
|
| 277 | | UINT8 buffer[MAX_PAGE_SIZE];
|
| 278 | | int i;
|
| 279 | | // boot blocks
|
| 280 | | memset( buffer, 0xFF, sizeof( buffer));
|
| 281 | | for (i=0;i<cfs->block_count_boot;i++)
|
| 282 | | {
|
| 283 | | if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_BOOT, i)) return FALSE;
|
| 284 | | }
|
| 285 | | // file blocks
|
| 286 | | memset( buffer, 0xFF, sizeof( buffer));
|
| 287 | | buffer[0] &= ~0x80;
|
| 288 | | for (i=0;i<cfs->block_count_file;i++)
|
| 289 | | {
|
| 290 | | if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return FALSE;
|
| 291 | | }
|
| 292 | | // padding
|
| 293 | | buffer[0] = 0xFF;
|
| 294 | | for (i=0;i<0x1B56;i++)
|
| 295 | | {
|
| 296 | | stream_write( cfs->stream, buffer, 1);
|
| 297 | | }
|
| 298 | | // ok
|
| 299 | | return TRUE;
|
| 300 | | }
|
| 301 | | |
| 302 | | static UINT16 cfs_calc_free_blocks( cybiko_file_system *cfs)
|
| 303 | | {
|
| 304 | | UINT8 buffer[MAX_PAGE_SIZE];
|
| 305 | | int i;
|
| 306 | | UINT16 blocks = 0;
|
| 307 | | for (i=0;i<cfs->block_count_file;i++)
|
| 308 | | {
|
| 309 | | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return 0;
|
| 310 | | if (!BLOCK_USED(buffer)) blocks++;
|
| 311 | | }
|
| 312 | | return blocks;
|
| 313 | | }
|
| 314 | | |
| 315 | | static UINT32 cfs_calc_free_space( cybiko_file_system *cfs, UINT16 blocks)
|
| 316 | | {
|
| 317 | | UINT32 free_space;
|
| 318 | | free_space = blocks * ((cfs->page_size - 2) - 6);
|
| 319 | | if (free_space > 0) free_space -= FILE_HEADER_SIZE;
|
| 320 | | return free_space;
|
| 321 | | }
|
| 322 | | |
| 323 | | static imgtoolerr_t cybiko_image_open( imgtool_image *image, imgtool_stream *stream)
|
| 324 | | {
|
| 325 | | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image);
|
| 326 | | // init
|
| 327 | | if (!cfs_init( cfs, stream)) return IMGTOOLERR_CORRUPTIMAGE;
|
| 328 | | // verify
|
| 329 | | if (!cfs_verify( cfs)) return IMGTOOLERR_CORRUPTIMAGE;
|
| 330 | | // ok
|
| 331 | | return IMGTOOLERR_SUCCESS;
|
| 332 | | }
|
| 333 | | |
| 334 | | static void cybiko_image_close( imgtool_image *image)
|
| 335 | | {
|
| 336 | | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image);
|
| 337 | | stream_close( cfs->stream);
|
| 338 | | }
|
| 339 | | |
| 340 | | static imgtoolerr_t cybiko_image_create( imgtool_image *image, imgtool_stream *stream, option_resolution *opts)
|
| 341 | | {
|
| 342 | | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image);
|
| 343 | | // init
|
| 344 | | if (!cfs_init( cfs, stream)) return IMGTOOLERR_CORRUPTIMAGE;
|
| 345 | | // format
|
| 346 | | if (!cfs_format( cfs)) return IMGTOOLERR_CORRUPTIMAGE;
|
| 347 | | // ok
|
| 348 | | return IMGTOOLERR_SUCCESS;
|
| 349 | | }
|
| 350 | | |
| 351 | | static imgtoolerr_t cybiko_image_begin_enum( imgtool_directory *enumeration, const char *path)
|
| 352 | | {
|
| 353 | | cybiko_iter *iter = (cybiko_iter*)imgtool_directory_extrabytes( enumeration);
|
| 354 | | iter->block = 0;
|
| 355 | | return IMGTOOLERR_SUCCESS;
|
| 356 | | }
|
| 357 | | |
| 358 | | static imgtoolerr_t cybiko_image_next_enum( imgtool_directory *enumeration, imgtool_dirent *ent)
|
| 359 | | {
|
| 360 | | imgtool_image *image = imgtool_directory_image( enumeration);
|
| 361 | | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image);
|
| 362 | | cybiko_iter *iter = (cybiko_iter*)imgtool_directory_extrabytes( enumeration);
|
| 363 | | UINT8 buffer[MAX_PAGE_SIZE];
|
| 364 | | UINT16 file_id = INVALID_FILE_ID;
|
| 365 | | cfs_file file;
|
| 366 | | // find next file
|
| 367 | | while (iter->block < cfs->block_count_file)
|
| 368 | | {
|
| 369 | | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, iter->block++)) return IMGTOOLERR_READERROR;
|
| 370 | | if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0))
|
| 371 | | {
|
| 372 | | file_id = BLOCK_FILE_ID(buffer);
|
| 373 | | break;
|
| 374 | | }
|
| 375 | | }
|
| 376 | | // get file information
|
| 377 | | if ((file_id != INVALID_FILE_ID) && cfs_file_info( cfs, file_id, &file))
|
| 378 | | {
|
| 379 | | strcpy( ent->filename, file.name);
|
| 380 | | ent->filesize = file.size;
|
| 381 | | ent->lastmodified_time = time_crack( file.date);
|
| 382 | | ent->filesize = file.size;
|
| 383 | | }
|
| 384 | | else
|
| 385 | | {
|
| 386 | | ent->eof = 1;
|
| 387 | | }
|
| 388 | | // ok
|
| 389 | | return IMGTOOLERR_SUCCESS;
|
| 390 | | }
|
| 391 | | |
| 392 | | static void cybiko_image_close_enum( imgtool_directory *enumeration)
|
| 393 | | {
|
| 394 | | // nothing
|
| 395 | | }
|
| 396 | | |
| 397 | | static imgtoolerr_t cybiko_image_free_space( imgtool_partition *partition, UINT64 *size)
|
| 398 | | {
|
| 399 | | imgtool_image *image = imgtool_partition_image( partition);
|
| 400 | | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image);
|
| 401 | | if (size) *size = cfs_calc_free_space( cfs, cfs_calc_free_blocks( cfs));
|
| 402 | | return IMGTOOLERR_SUCCESS;
|
| 403 | | }
|
| 404 | | |
| 405 | | static imgtoolerr_t cybiko_image_read_file( imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *destf)
|
| 406 | | {
|
| 407 | | imgtool_image *image = imgtool_partition_image( partition);
|
| 408 | | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image);
|
| 409 | | UINT8 buffer[MAX_PAGE_SIZE];
|
| 410 | | UINT16 file_id, part_id = 0, old_part_id;
|
| 411 | | int i;
|
| 412 | | // check filename
|
| 413 | | if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME;
|
| 414 | | // find file
|
| 415 | | if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND;
|
| 416 | | // read file
|
| 417 | | do
|
| 418 | | {
|
| 419 | | old_part_id = part_id;
|
| 420 | | for (i=0;i<cfs->block_count_file;i++)
|
| 421 | | {
|
| 422 | | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR;
|
| 423 | | if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id) && (BLOCK_PART_ID(buffer) == part_id))
|
| 424 | | {
|
| 425 | | stream_write( destf, buffer + 6 + ((part_id == 0) ? FILE_HEADER_SIZE : 0), buffer[1]);
|
| 426 | | part_id++;
|
| 427 | | }
|
| 428 | | }
|
| 429 | | } while (old_part_id != part_id);
|
| 430 | | // ok
|
| 431 | | return IMGTOOLERR_SUCCESS;
|
| 432 | | }
|
| 433 | | |
| 434 | | static imgtoolerr_t cybiko_image_write_file( imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *sourcef, option_resolution *opts)
|
| 435 | | {
|
| 436 | | imgtool_image *image = imgtool_partition_image( partition);
|
| 437 | | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image);
|
| 438 | | UINT8 buffer[MAX_PAGE_SIZE];
|
| 439 | | UINT16 file_id, part_id = 0, free_blocks;
|
| 440 | | UINT64 bytes_left;
|
| 441 | | cfs_file file;
|
| 442 | | int i;
|
| 443 | | // check filename
|
| 444 | | if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME;
|
| 445 | | // find file
|
| 446 | | if (!cfs_file_find( cfs, filename, &file_id)) file_id = INVALID_FILE_ID;
|
| 447 | | // check free space
|
| 448 | | free_blocks = cfs_calc_free_blocks( cfs);
|
| 449 | | if (file_id != INVALID_FILE_ID)
|
| 450 | | {
|
| 451 | | if (!cfs_file_info( cfs, file_id, &file)) return IMGTOOLERR_UNEXPECTED;
|
| 452 | | free_blocks += file.blocks;
|
| 453 | | }
|
| 454 | | if (cfs_calc_free_space( cfs, free_blocks) < stream_size( sourcef)) return IMGTOOLERR_NOSPACE;
|
| 455 | | // delete file
|
| 456 | | if (file_id != INVALID_FILE_ID)
|
| 457 | | {
|
| 458 | | if (!cfs_file_delete( cfs, file_id)) return IMGTOOLERR_UNEXPECTED;
|
| 459 | | }
|
| 460 | | // create/write destination file
|
| 461 | | bytes_left = stream_size( sourcef);
|
| 462 | | i = 0;
|
| 463 | | while (i < cfs->block_count_file)
|
| 464 | | {
|
| 465 | | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR;
|
| 466 | | if (!BLOCK_USED(buffer))
|
| 467 | | {
|
| 468 | | if (part_id == 0) file_id = i;
|
| 469 | | memset( buffer, 0xFF, cfs->page_size - 0x02);
|
| 470 | | buffer[0] = 0x80;
|
| 471 | | buffer[1] = (cfs->page_size - 2) - 6 - ((part_id == 0) ? FILE_HEADER_SIZE : 0);
|
| 472 | | if (bytes_left < buffer[1]) buffer[1] = bytes_left;
|
| 473 | | buffer_write_16_be( buffer + 2, file_id);
|
| 474 | | buffer_write_16_be( buffer + 4, part_id);
|
| 475 | | if (part_id == 0)
|
| 476 | | {
|
| 477 | | buffer[6] = 0x20;
|
| 478 | | strcpy( BLOCK_FILENAME(buffer), filename);
|
| 479 | | buffer_write_32_be( buffer + 6 + FILE_HEADER_SIZE - 4, time_setup( time( NULL)));
|
| 480 | | stream_read( sourcef, buffer + 6 + FILE_HEADER_SIZE, buffer[1]);
|
| 481 | | }
|
| 482 | | else
|
| 483 | | {
|
| 484 | | stream_read( sourcef, buffer + 6, buffer[1]);
|
| 485 | | }
|
| 486 | | if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_WRITEERROR;
|
| 487 | | bytes_left -= buffer[1];
|
| 488 | | if (bytes_left == 0) break;
|
| 489 | | part_id++;
|
| 490 | | }
|
| 491 | | i++;
|
| 492 | | }
|
| 493 | | // ok
|
| 494 | | return IMGTOOLERR_SUCCESS;
|
| 495 | | }
|
| 496 | | |
| 497 | | static imgtoolerr_t cybiko_image_delete_file( imgtool_partition *partition, const char *filename)
|
| 498 | | {
|
| 499 | | imgtool_image *image = imgtool_partition_image( partition);
|
| 500 | | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image);
|
| 501 | | UINT16 file_id;
|
| 502 | | // check filename
|
| 503 | | if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME;
|
| 504 | | // find file
|
| 505 | | if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND;
|
| 506 | | // delete file
|
| 507 | | if (!cfs_file_delete( cfs, file_id)) return IMGTOOLERR_UNEXPECTED;
|
| 508 | | // ok
|
| 509 | | return IMGTOOLERR_SUCCESS;
|
| 510 | | }
|
| 511 | | |
| 512 | | void cybikoxt_get_info( const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
|
| 513 | | {
|
| 514 | | switch (state)
|
| 515 | | {
|
| 516 | | // --- the following bits of info are returned as 64-bit signed integers ---
|
| 517 | | case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES : info->i = sizeof( cybiko_file_system); break;
|
| 518 | | case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES : info->i = sizeof( cybiko_iter); break;
|
| 519 | | // case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME : info->i = 1; break;
|
| 520 | | case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME : info->i = 1; break;
|
| 521 | | // case IMGTOOLINFO_INT_BLOCK_SIZE : info->i = 264; break;
|
| 522 | | // --- the following bits of info are returned as pointers to data or functions ---
|
| 523 | | case IMGTOOLINFO_PTR_OPEN : info->open = cybiko_image_open; break;
|
| 524 | | case IMGTOOLINFO_PTR_CREATE : info->create = cybiko_image_create; break;
|
| 525 | | case IMGTOOLINFO_PTR_CLOSE : info->close = cybiko_image_close; break;
|
| 526 | | case IMGTOOLINFO_PTR_BEGIN_ENUM : info->begin_enum = cybiko_image_begin_enum; break;
|
| 527 | | case IMGTOOLINFO_PTR_NEXT_ENUM : info->next_enum = cybiko_image_next_enum; break;
|
| 528 | | case IMGTOOLINFO_PTR_CLOSE_ENUM : info->close_enum = cybiko_image_close_enum; break;
|
| 529 | | case IMGTOOLINFO_PTR_FREE_SPACE : info->free_space = cybiko_image_free_space; break;
|
| 530 | | case IMGTOOLINFO_PTR_READ_FILE : info->read_file = cybiko_image_read_file; break;
|
| 531 | | case IMGTOOLINFO_PTR_WRITE_FILE : info->write_file = cybiko_image_write_file; break;
|
| 532 | | case IMGTOOLINFO_PTR_DELETE_FILE : info->delete_file = cybiko_image_delete_file; break;
|
| 533 | | // --- the following bits of info are returned as NULL-terminated strings ---
|
| 534 | | case IMGTOOLINFO_STR_NAME : strcpy( info->s = imgtool_temp_str(), "cybikoxt"); break;
|
| 535 | | case IMGTOOLINFO_STR_DESCRIPTION : strcpy( info->s = imgtool_temp_str(), "Cybiko Xtreme File System"); break;
|
| 536 | | case IMGTOOLINFO_STR_FILE : strcpy( info->s = imgtool_temp_str(), __FILE__); break;
|
| 537 | | case IMGTOOLINFO_STR_FILE_EXTENSIONS : strcpy( info->s = imgtool_temp_str(), "bin,nv"); break;
|
| 538 | | case IMGTOOLINFO_STR_EOLN : strcpy( info->s = imgtool_temp_str(), "\r\n"); break;
|
| 539 | | }
|
| 540 | | }
|
| 1 | /* |
| 2 | |
| 3 | Cybiko Xtreme File System |
| 4 | |
| 5 | (c) 2010 Tim Schuerewegen |
| 6 | |
| 7 | */ |
| 8 | |
| 9 | #include "imgtool.h" |
| 10 | |
| 11 | #include <zlib.h> |
| 12 | |
| 13 | typedef struct _cybiko_file_system cybiko_file_system; |
| 14 | struct _cybiko_file_system |
| 15 | { |
| 16 | imgtool_stream *stream; |
| 17 | UINT32 page_count, page_size, block_count_boot, block_count_file; |
| 18 | UINT16 write_count; |
| 19 | }; |
| 20 | |
| 21 | typedef struct _cybiko_iter cybiko_iter; |
| 22 | struct _cybiko_iter |
| 23 | { |
| 24 | UINT16 block; |
| 25 | }; |
| 26 | |
| 27 | typedef struct _cfs_file cfs_file; |
| 28 | struct _cfs_file |
| 29 | { |
| 30 | char name[64]; // name of the file |
| 31 | UINT32 date; // date/time of the file (seconds since 1900/01/01) |
| 32 | UINT32 size; // size of the file |
| 33 | UINT32 blocks; // number of blocks occupied by the file |
| 34 | }; |
| 35 | |
| 36 | enum |
| 37 | { |
| 38 | BLOCK_TYPE_INVALID, |
| 39 | BLOCK_TYPE_BOOT, |
| 40 | BLOCK_TYPE_FILE |
| 41 | }; |
| 42 | |
| 43 | #define MAX_PAGE_SIZE (264 * 2) |
| 44 | |
| 45 | #define INVALID_FILE_ID 0xFFFF |
| 46 | |
| 47 | #define BLOCK_USED(x) (x[0] & 0x80) |
| 48 | #define BLOCK_FILE_ID(x) buffer_read_16_be( x + 2) |
| 49 | #define BLOCK_PART_ID(x) buffer_read_16_be( x + 4) |
| 50 | #define BLOCK_FILENAME(x) (char*)(x + 7) |
| 51 | |
| 52 | #define FILE_HEADER_SIZE 0x48 |
| 53 | |
| 54 | // 2208988800 is the number of seconds between 1900/01/01 and 1970/01/01 |
| 55 | |
| 56 | static time_t time_crack( UINT32 cfs_time) |
| 57 | { |
| 58 | return (time_t)(cfs_time - 2208988800UL); |
| 59 | } |
| 60 | |
| 61 | static UINT32 time_setup( time_t ansi_time) |
| 62 | { |
| 63 | return (UINT32)(ansi_time + 2208988800UL); |
| 64 | } |
| 65 | |
| 66 | static UINT32 buffer_read_32_be( UINT8 *buffer) |
| 67 | { |
| 68 | return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3] << 0); |
| 69 | } |
| 70 | |
| 71 | static UINT16 buffer_read_16_be( UINT8 *buffer) |
| 72 | { |
| 73 | return (buffer[0] << 8) | (buffer[1] << 0); |
| 74 | } |
| 75 | |
| 76 | static void buffer_write_32_be( UINT8 *buffer, UINT32 data) |
| 77 | { |
| 78 | buffer[0] = (data >> 24) & 0xFF; |
| 79 | buffer[1] = (data >> 16) & 0xFF; |
| 80 | buffer[2] = (data >> 8) & 0xFF; |
| 81 | buffer[3] = (data >> 0) & 0xFF; |
| 82 | } |
| 83 | |
| 84 | static void buffer_write_16_be( UINT8 *buffer, UINT16 data) |
| 85 | { |
| 86 | buffer[0] = (data >> 8) & 0xFF; |
| 87 | buffer[1] = (data >> 0) & 0xFF; |
| 88 | } |
| 89 | |
| 90 | // page = crc (2) + data (x) |
| 91 | |
| 92 | static UINT16 page_buffer_calc_checksum( UINT8 *data, UINT32 size) |
| 93 | { |
| 94 | int i; |
| 95 | UINT32 val = 0; |
| 96 | for (i = 0; i < size; i++) |
| 97 | { |
| 98 | val = (val ^ data[i] ^ i) << 1; |
| 99 | val = val | ((val >> 16) & 0x0001); |
| 100 | } |
| 101 | return val; |
| 102 | } |
| 103 | |
| 104 | static int page_buffer_verify( UINT8 *buffer, UINT32 size, int block_type) |
| 105 | { |
| 106 | // checksum |
| 107 | if (block_type == BLOCK_TYPE_FILE) |
| 108 | { |
| 109 | UINT32 checksum_page, checksum_calc; |
| 110 | checksum_calc = page_buffer_calc_checksum( buffer + 2, size - 2); |
| 111 | checksum_page = buffer_read_16_be( buffer + 0); |
| 112 | if (checksum_calc != checksum_page) return FALSE; |
| 113 | } |
| 114 | // ok |
| 115 | return TRUE; |
| 116 | } |
| 117 | |
| 118 | static int cfs_block_to_page( cybiko_file_system *cfs, int block_type, UINT32 block, UINT32 *page) |
| 119 | { |
| 120 | switch (block_type) |
| 121 | { |
| 122 | case BLOCK_TYPE_BOOT : if (page) *page = block; return TRUE; |
| 123 | case BLOCK_TYPE_FILE : if (page) *page = block + cfs->block_count_boot; return TRUE; |
| 124 | default : return FALSE; |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | static int cfs_page_to_block( cybiko_file_system *cfs, UINT32 page, int *block_type, UINT32 *block) |
| 129 | { |
| 130 | UINT32 tmp = page; |
| 131 | // boot block |
| 132 | if (tmp < cfs->block_count_boot) |
| 133 | { |
| 134 | if (block_type) *block_type = BLOCK_TYPE_BOOT; |
| 135 | if (block) *block = tmp; |
| 136 | return TRUE; |
| 137 | } |
| 138 | tmp -= cfs->block_count_boot; |
| 139 | // file block |
| 140 | if (tmp < cfs->block_count_file) |
| 141 | { |
| 142 | if (block_type) *block_type = BLOCK_TYPE_FILE; |
| 143 | if (block) *block = tmp; |
| 144 | return TRUE; |
| 145 | } |
| 146 | tmp -= cfs->block_count_file; |
| 147 | // error |
| 148 | return FALSE; |
| 149 | } |
| 150 | |
| 151 | static int cfs_page_read( cybiko_file_system *cfs, UINT8 *buffer, UINT32 page) |
| 152 | { |
| 153 | if (page >= cfs->page_count) return FALSE; |
| 154 | stream_seek( cfs->stream, page * cfs->page_size, SEEK_SET); |
| 155 | stream_read( cfs->stream, buffer, cfs->page_size); |
| 156 | return TRUE; |
| 157 | } |
| 158 | |
| 159 | static int cfs_page_write( cybiko_file_system *cfs, UINT8 *buffer, UINT32 page) |
| 160 | { |
| 161 | if (page >= cfs->page_count) return FALSE; |
| 162 | stream_seek( cfs->stream, page * cfs->page_size, SEEK_SET); |
| 163 | stream_write( cfs->stream, buffer, cfs->page_size); |
| 164 | return TRUE; |
| 165 | } |
| 166 | |
| 167 | static int cfs_block_read( cybiko_file_system *cfs, UINT8 *buffer, int block_type, UINT32 block) |
| 168 | { |
| 169 | UINT8 buffer_page[MAX_PAGE_SIZE]; |
| 170 | UINT32 page; |
| 171 | if (!cfs_block_to_page( cfs, block_type, block, &page)) return FALSE; |
| 172 | if (!cfs_page_read( cfs, buffer_page, page)) return FALSE; |
| 173 | memcpy( buffer, buffer_page + 2, cfs->page_size - 2); |
| 174 | return TRUE; |
| 175 | } |
| 176 | |
| 177 | static int cfs_block_write( cybiko_file_system *cfs, UINT8 *buffer, int block_type, UINT32 block) |
| 178 | { |
| 179 | UINT8 buffer_page[MAX_PAGE_SIZE]; |
| 180 | UINT32 page; |
| 181 | UINT16 checksum; |
| 182 | memcpy( buffer_page + 2, buffer, cfs->page_size - 2); |
| 183 | if (block_type == BLOCK_TYPE_BOOT) |
| 184 | { |
| 185 | checksum = 0xFFFF; |
| 186 | } |
| 187 | else |
| 188 | { |
| 189 | checksum = page_buffer_calc_checksum( buffer_page + 2, cfs->page_size - 2); |
| 190 | } |
| 191 | buffer_write_16_be( buffer_page + 0, checksum); |
| 192 | if (!cfs_block_to_page( cfs, block_type, block, &page)) return FALSE; |
| 193 | if (!cfs_page_write( cfs, buffer_page, page)) return FALSE; |
| 194 | return TRUE; |
| 195 | } |
| 196 | |
| 197 | static int cfs_file_delete( cybiko_file_system *cfs, UINT16 file_id) |
| 198 | { |
| 199 | UINT8 buffer[MAX_PAGE_SIZE]; |
| 200 | int i; |
| 201 | for (i=0;i<cfs->block_count_file;i++) |
| 202 | { |
| 203 | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return FALSE; |
| 204 | if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id)) |
| 205 | { |
| 206 | buffer[0] &= ~0x80; |
| 207 | if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return FALSE; |
| 208 | } |
| 209 | } |
| 210 | return TRUE; |
| 211 | } |
| 212 | |
| 213 | static int cfs_file_info( cybiko_file_system *cfs, UINT16 file_id, cfs_file *file) |
| 214 | { |
| 215 | UINT8 buffer[MAX_PAGE_SIZE]; |
| 216 | int i; |
| 217 | file->blocks = file->size = 0; |
| 218 | for (i=0;i<cfs->block_count_file;i++) |
| 219 | { |
| 220 | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return FALSE; |
| 221 | if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id)) |
| 222 | { |
| 223 | if (BLOCK_PART_ID(buffer) == 0) |
| 224 | { |
| 225 | strcpy( file->name, BLOCK_FILENAME(buffer)); |
| 226 | file->date = buffer_read_32_be( buffer + 6 + FILE_HEADER_SIZE - 4); |
| 227 | } |
| 228 | file->size += buffer[1]; |
| 229 | file->blocks++; |
| 230 | } |
| 231 | } |
| 232 | return (file->blocks > 0) ? TRUE : FALSE; |
| 233 | } |
| 234 | |
| 235 | static int cfs_file_find( cybiko_file_system *cfs, const char *filename, UINT16 *file_id) |
| 236 | { |
| 237 | UINT8 buffer[MAX_PAGE_SIZE]; |
| 238 | int i; |
| 239 | for (i=0;i<cfs->block_count_file;i++) |
| 240 | { |
| 241 | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return FALSE; |
| 242 | if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0) && (strcmp( filename, BLOCK_FILENAME(buffer)) == 0)) |
| 243 | { |
| 244 | *file_id = i; |
| 245 | return TRUE; |
| 246 | } |
| 247 | } |
| 248 | return FALSE; |
| 249 | } |
| 250 | |
| 251 | static int cfs_verify( cybiko_file_system *cfs) |
| 252 | { |
| 253 | UINT8 buffer[MAX_PAGE_SIZE]; |
| 254 | int i, block_type; |
| 255 | for (i=0;i<cfs->page_count;i++) |
| 256 | { |
| 257 | if (!cfs_page_read( cfs, buffer, i)) return FALSE; |
| 258 | if (!cfs_page_to_block( cfs, i, &block_type, NULL)) return FALSE; |
| 259 | if (!page_buffer_verify( buffer, cfs->page_size, block_type)) return FALSE; |
| 260 | } |
| 261 | return TRUE; |
| 262 | } |
| 263 | |
| 264 | static int cfs_init( cybiko_file_system *cfs, imgtool_stream *stream) |
| 265 | { |
| 266 | cfs->stream = stream; |
| 267 | cfs->page_count = 2005; |
| 268 | cfs->page_size = 258; |
| 269 | cfs->block_count_boot = 5; |
| 270 | cfs->block_count_file = cfs->page_count - cfs->block_count_boot; |
| 271 | cfs->write_count = 0; |
| 272 | return TRUE; |
| 273 | } |
| 274 | |
| 275 | static int cfs_format( cybiko_file_system *cfs) |
| 276 | { |
| 277 | UINT8 buffer[MAX_PAGE_SIZE]; |
| 278 | int i; |
| 279 | // boot blocks |
| 280 | memset( buffer, 0xFF, sizeof( buffer)); |
| 281 | for (i=0;i<cfs->block_count_boot;i++) |
| 282 | { |
| 283 | if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_BOOT, i)) return FALSE; |
| 284 | } |
| 285 | // file blocks |
| 286 | memset( buffer, 0xFF, sizeof( buffer)); |
| 287 | buffer[0] &= ~0x80; |
| 288 | for (i=0;i<cfs->block_count_file;i++) |
| 289 | { |
| 290 | if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return FALSE; |
| 291 | } |
| 292 | // padding |
| 293 | buffer[0] = 0xFF; |
| 294 | for (i=0;i<0x1B56;i++) |
| 295 | { |
| 296 | stream_write( cfs->stream, buffer, 1); |
| 297 | } |
| 298 | // ok |
| 299 | return TRUE; |
| 300 | } |
| 301 | |
| 302 | static UINT16 cfs_calc_free_blocks( cybiko_file_system *cfs) |
| 303 | { |
| 304 | UINT8 buffer[MAX_PAGE_SIZE]; |
| 305 | int i; |
| 306 | UINT16 blocks = 0; |
| 307 | for (i=0;i<cfs->block_count_file;i++) |
| 308 | { |
| 309 | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return 0; |
| 310 | if (!BLOCK_USED(buffer)) blocks++; |
| 311 | } |
| 312 | return blocks; |
| 313 | } |
| 314 | |
| 315 | static UINT32 cfs_calc_free_space( cybiko_file_system *cfs, UINT16 blocks) |
| 316 | { |
| 317 | UINT32 free_space; |
| 318 | free_space = blocks * ((cfs->page_size - 2) - 6); |
| 319 | if (free_space > 0) free_space -= FILE_HEADER_SIZE; |
| 320 | return free_space; |
| 321 | } |
| 322 | |
| 323 | static imgtoolerr_t cybiko_image_open( imgtool_image *image, imgtool_stream *stream) |
| 324 | { |
| 325 | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image); |
| 326 | // init |
| 327 | if (!cfs_init( cfs, stream)) return IMGTOOLERR_CORRUPTIMAGE; |
| 328 | // verify |
| 329 | if (!cfs_verify( cfs)) return IMGTOOLERR_CORRUPTIMAGE; |
| 330 | // ok |
| 331 | return IMGTOOLERR_SUCCESS; |
| 332 | } |
| 333 | |
| 334 | static void cybiko_image_close( imgtool_image *image) |
| 335 | { |
| 336 | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image); |
| 337 | stream_close( cfs->stream); |
| 338 | } |
| 339 | |
| 340 | static imgtoolerr_t cybiko_image_create( imgtool_image *image, imgtool_stream *stream, option_resolution *opts) |
| 341 | { |
| 342 | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image); |
| 343 | // init |
| 344 | if (!cfs_init( cfs, stream)) return IMGTOOLERR_CORRUPTIMAGE; |
| 345 | // format |
| 346 | if (!cfs_format( cfs)) return IMGTOOLERR_CORRUPTIMAGE; |
| 347 | // ok |
| 348 | return IMGTOOLERR_SUCCESS; |
| 349 | } |
| 350 | |
| 351 | static imgtoolerr_t cybiko_image_begin_enum( imgtool_directory *enumeration, const char *path) |
| 352 | { |
| 353 | cybiko_iter *iter = (cybiko_iter*)imgtool_directory_extrabytes( enumeration); |
| 354 | iter->block = 0; |
| 355 | return IMGTOOLERR_SUCCESS; |
| 356 | } |
| 357 | |
| 358 | static imgtoolerr_t cybiko_image_next_enum( imgtool_directory *enumeration, imgtool_dirent *ent) |
| 359 | { |
| 360 | imgtool_image *image = imgtool_directory_image( enumeration); |
| 361 | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image); |
| 362 | cybiko_iter *iter = (cybiko_iter*)imgtool_directory_extrabytes( enumeration); |
| 363 | UINT8 buffer[MAX_PAGE_SIZE]; |
| 364 | UINT16 file_id = INVALID_FILE_ID; |
| 365 | cfs_file file; |
| 366 | // find next file |
| 367 | while (iter->block < cfs->block_count_file) |
| 368 | { |
| 369 | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, iter->block++)) return IMGTOOLERR_READERROR; |
| 370 | if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0)) |
| 371 | { |
| 372 | file_id = BLOCK_FILE_ID(buffer); |
| 373 | break; |
| 374 | } |
| 375 | } |
| 376 | // get file information |
| 377 | if ((file_id != INVALID_FILE_ID) && cfs_file_info( cfs, file_id, &file)) |
| 378 | { |
| 379 | strcpy( ent->filename, file.name); |
| 380 | ent->filesize = file.size; |
| 381 | ent->lastmodified_time = time_crack( file.date); |
| 382 | ent->filesize = file.size; |
| 383 | } |
| 384 | else |
| 385 | { |
| 386 | ent->eof = 1; |
| 387 | } |
| 388 | // ok |
| 389 | return IMGTOOLERR_SUCCESS; |
| 390 | } |
| 391 | |
| 392 | static void cybiko_image_close_enum( imgtool_directory *enumeration) |
| 393 | { |
| 394 | // nothing |
| 395 | } |
| 396 | |
| 397 | static imgtoolerr_t cybiko_image_free_space( imgtool_partition *partition, UINT64 *size) |
| 398 | { |
| 399 | imgtool_image *image = imgtool_partition_image( partition); |
| 400 | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image); |
| 401 | if (size) *size = cfs_calc_free_space( cfs, cfs_calc_free_blocks( cfs)); |
| 402 | return IMGTOOLERR_SUCCESS; |
| 403 | } |
| 404 | |
| 405 | static imgtoolerr_t cybiko_image_read_file( imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *destf) |
| 406 | { |
| 407 | imgtool_image *image = imgtool_partition_image( partition); |
| 408 | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image); |
| 409 | UINT8 buffer[MAX_PAGE_SIZE]; |
| 410 | UINT16 file_id, part_id = 0, old_part_id; |
| 411 | int i; |
| 412 | // check filename |
| 413 | if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME; |
| 414 | // find file |
| 415 | if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND; |
| 416 | // read file |
| 417 | do |
| 418 | { |
| 419 | old_part_id = part_id; |
| 420 | for (i=0;i<cfs->block_count_file;i++) |
| 421 | { |
| 422 | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR; |
| 423 | if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id) && (BLOCK_PART_ID(buffer) == part_id)) |
| 424 | { |
| 425 | stream_write( destf, buffer + 6 + ((part_id == 0) ? FILE_HEADER_SIZE : 0), buffer[1]); |
| 426 | part_id++; |
| 427 | } |
| 428 | } |
| 429 | } while (old_part_id != part_id); |
| 430 | // ok |
| 431 | return IMGTOOLERR_SUCCESS; |
| 432 | } |
| 433 | |
| 434 | static imgtoolerr_t cybiko_image_write_file( imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *sourcef, option_resolution *opts) |
| 435 | { |
| 436 | imgtool_image *image = imgtool_partition_image( partition); |
| 437 | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image); |
| 438 | UINT8 buffer[MAX_PAGE_SIZE]; |
| 439 | UINT16 file_id, part_id = 0, free_blocks; |
| 440 | UINT64 bytes_left; |
| 441 | cfs_file file; |
| 442 | int i; |
| 443 | // check filename |
| 444 | if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME; |
| 445 | // find file |
| 446 | if (!cfs_file_find( cfs, filename, &file_id)) file_id = INVALID_FILE_ID; |
| 447 | // check free space |
| 448 | free_blocks = cfs_calc_free_blocks( cfs); |
| 449 | if (file_id != INVALID_FILE_ID) |
| 450 | { |
| 451 | if (!cfs_file_info( cfs, file_id, &file)) return IMGTOOLERR_UNEXPECTED; |
| 452 | free_blocks += file.blocks; |
| 453 | } |
| 454 | if (cfs_calc_free_space( cfs, free_blocks) < stream_size( sourcef)) return IMGTOOLERR_NOSPACE; |
| 455 | // delete file |
| 456 | if (file_id != INVALID_FILE_ID) |
| 457 | { |
| 458 | if (!cfs_file_delete( cfs, file_id)) return IMGTOOLERR_UNEXPECTED; |
| 459 | } |
| 460 | // create/write destination file |
| 461 | bytes_left = stream_size( sourcef); |
| 462 | i = 0; |
| 463 | while (i < cfs->block_count_file) |
| 464 | { |
| 465 | if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR; |
| 466 | if (!BLOCK_USED(buffer)) |
| 467 | { |
| 468 | if (part_id == 0) file_id = i; |
| 469 | memset( buffer, 0xFF, cfs->page_size - 0x02); |
| 470 | buffer[0] = 0x80; |
| 471 | buffer[1] = (cfs->page_size - 2) - 6 - ((part_id == 0) ? FILE_HEADER_SIZE : 0); |
| 472 | if (bytes_left < buffer[1]) buffer[1] = bytes_left; |
| 473 | buffer_write_16_be( buffer + 2, file_id); |
| 474 | buffer_write_16_be( buffer + 4, part_id); |
| 475 | if (part_id == 0) |
| 476 | { |
| 477 | buffer[6] = 0x20; |
| 478 | strcpy( BLOCK_FILENAME(buffer), filename); |
| 479 | buffer_write_32_be( buffer + 6 + FILE_HEADER_SIZE - 4, time_setup( time( NULL))); |
| 480 | stream_read( sourcef, buffer + 6 + FILE_HEADER_SIZE, buffer[1]); |
| 481 | } |
| 482 | else |
| 483 | { |
| 484 | stream_read( sourcef, buffer + 6, buffer[1]); |
| 485 | } |
| 486 | if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_WRITEERROR; |
| 487 | bytes_left -= buffer[1]; |
| 488 | if (bytes_left == 0) break; |
| 489 | part_id++; |
| 490 | } |
| 491 | i++; |
| 492 | } |
| 493 | // ok |
| 494 | return IMGTOOLERR_SUCCESS; |
| 495 | } |
| 496 | |
| 497 | static imgtoolerr_t cybiko_image_delete_file( imgtool_partition *partition, const char *filename) |
| 498 | { |
| 499 | imgtool_image *image = imgtool_partition_image( partition); |
| 500 | cybiko_file_system *cfs = (cybiko_file_system*)imgtool_image_extra_bytes( image); |
| 501 | UINT16 file_id; |
| 502 | // check filename |
| 503 | if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME; |
| 504 | // find file |
| 505 | if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND; |
| 506 | // delete file |
| 507 | if (!cfs_file_delete( cfs, file_id)) return IMGTOOLERR_UNEXPECTED; |
| 508 | // ok |
| 509 | return IMGTOOLERR_SUCCESS; |
| 510 | } |
| 511 | |
| 512 | void cybikoxt_get_info( const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info) |
| 513 | { |
| 514 | switch (state) |
| 515 | { |
| 516 | // --- the following bits of info are returned as 64-bit signed integers --- |
| 517 | case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES : info->i = sizeof( cybiko_file_system); break; |
| 518 | case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES : info->i = sizeof( cybiko_iter); break; |
| 519 | // case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME : info->i = 1; break; |
| 520 | case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME : info->i = 1; break; |
| 521 | // case IMGTOOLINFO_INT_BLOCK_SIZE : info->i = 264; break; |
| 522 | // --- the following bits of info are returned as pointers to data or functions --- |
| 523 | case IMGTOOLINFO_PTR_OPEN : info->open = cybiko_image_open; break; |
| 524 | case IMGTOOLINFO_PTR_CREATE : info->create = cybiko_image_create; break; |
| 525 | case IMGTOOLINFO_PTR_CLOSE : info->close = cybiko_image_close; break; |
| 526 | case IMGTOOLINFO_PTR_BEGIN_ENUM : info->begin_enum = cybiko_image_begin_enum; break; |
| 527 | case IMGTOOLINFO_PTR_NEXT_ENUM : info->next_enum = cybiko_image_next_enum; break; |
| 528 | case IMGTOOLINFO_PTR_CLOSE_ENUM : info->close_enum = cybiko_image_close_enum; break; |
| 529 | case IMGTOOLINFO_PTR_FREE_SPACE : info->free_space = cybiko_image_free_space; break; |
| 530 | case IMGTOOLINFO_PTR_READ_FILE : info->read_file = cybiko_image_read_file; break; |
| 531 | case IMGTOOLINFO_PTR_WRITE_FILE : info->write_file = cybiko_image_write_file; break; |
| 532 | case IMGTOOLINFO_PTR_DELETE_FILE : info->delete_file = cybiko_image_delete_file; break; |
| 533 | // --- the following bits of info are returned as NULL-terminated strings --- |
| 534 | case IMGTOOLINFO_STR_NAME : strcpy( info->s = imgtool_temp_str(), "cybikoxt"); break; |
| 535 | case IMGTOOLINFO_STR_DESCRIPTION : strcpy( info->s = imgtool_temp_str(), "Cybiko Xtreme File System"); break; |
| 536 | case IMGTOOLINFO_STR_FILE : strcpy( info->s = imgtool_temp_str(), __FILE__); break; |
| 537 | case IMGTOOLINFO_STR_FILE_EXTENSIONS : strcpy( info->s = imgtool_temp_str(), "bin,nv"); break; |
| 538 | case IMGTOOLINFO_STR_EOLN : strcpy( info->s = imgtool_temp_str(), "\r\n"); break; |
| 539 | } |
| 540 | } |
src/mess/drivers/prestige.c
| r8681 | r8682 | |
| 1 | | /*
|
| 2 | | |
| 3 | | VTech PreComputer Prestige Elite
|
| 4 | | |
| 5 | | PCB Layout
|
| 6 | | ----------
|
| 7 | | |
| 8 | | |-------------------------------------------|
|
| 9 | | | |-------CN1-------| CN2 |
|
| 10 | | | |
|
| 11 | | | CN3 |
|
| 12 | | | |
|
| 13 | | |CN10 |
|
| 14 | | |CN9 RAM --|
|
| 15 | | |CN8 |
|
| 16 | | | |
|
| 17 | | | Z80 ROM |
|
| 18 | | | 04010 CN4 |
|
| 19 | | |CN7 |
|
| 20 | | |CN6 |
|
| 21 | | | |------CN5------| --|
|
| 22 | | |-------------------------------------------|
|
| 23 | | |
| 24 | | Notes:
|
| 25 | | All IC's shown.
|
| 26 | | |
| 27 | | ROM - VTech LH5S8R14, labeled "1998 27-6020-02" (dumped as 1Mx8)
|
| 28 | | Z80 - Z80 family SOC?
|
| 29 | | RAM - LG Semicon GM76C256CLLFW55 32Kx8 Static RAM
|
| 30 | | 04010 - ?
|
| 31 | | CN1 - Centronics connector
|
| 32 | | CN2 - mouse connector
|
| 33 | | CN3 - LCD ribbon cable
|
| 34 | | CN4 - expansion connector
|
| 35 | | CN5 - keyboard ribbon cable
|
| 36 | | CN6 - speaker wire
|
| 37 | | CN7 - volume wire
|
| 38 | | CN8 - ? wire
|
| 39 | | CN9 - power wire
|
| 40 | | CN10 - NVRAM battery wire
|
| 41 | | */
|
| 42 | | |
| 43 | | /*
|
| 44 | |
|
| 45 | | Undumped cartridges:
|
| 46 | | |
| 47 | | 80-1410 Super Science
|
| 48 | | 80-1533 Famous Things & Places
|
| 49 | | 80-0989 Bible Knowledge
|
| 50 | | 80-1001 Fantasy Trivia
|
| 51 | | 80-1002 General Knowledge II
|
| 52 | | 80-1003 Sports History
|
| 53 | | 80-2314 Familiar Faces
|
| 54 | | 80-2315 Historical Happenings
|
| 55 | | 80-2333 Arts, Entertainment & More
|
| 56 | | 80-2334 Customs & Cultures
|
| 57 | | 80-1531 32K RAM Memory Expansion Cartridge
|
| 58 | | 80-12051 Space Scholar
|
| 59 | | 80-12053 Frenzy of Facts
|
| 60 | | 80-12052 Spreadsheet Success
|
| 61 | | |
| 62 | | */
|
| 63 | | |
| 64 | | /*
|
| 65 | | |
| 66 | | TODO:
|
| 67 | | |
| 68 | | - identify unknown chips
|
| 69 | | - boot animation won't play
|
| 70 | | - keyboard
|
| 71 | | - mouse
|
| 72 | | - sound
|
| 73 | | - cartridges
|
| 74 | | |
| 75 | | */
|
| 76 | | |
| 77 | | #include "emu.h"
|
| 78 | | #include "cpu/z80/z80.h"
|
| 79 | | #include "devices/messram.h"
|
| 80 | | #include "devices/cartslot.h"
|
| 81 | | |
| 82 | | static UINT8 bank[6];
|
| 83 | | |
| 84 | | static READ8_HANDLER( bankswitch_r )
|
| 85 | | {
|
| 86 | | return bank[offset];
|
| 87 | | }
|
| 88 | | |
| 89 | | static WRITE8_HANDLER( bankswitch_w )
|
| 90 | | {
|
| 91 | | const address_space *program = cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM);
|
| 92 | | |
| 93 | | switch (offset)
|
| 94 | | {
|
| 95 | | case 0:
|
| 96 | | memory_set_bank(space->machine, "bank1", data & 0x3f);
|
| 97 | | break;
|
| 98 | | |
| 99 | | case 1:
|
| 100 | | memory_set_bank(space->machine, "bank2", data & 0x3f);
|
| 101 | | break;
|
| 102 | | |
| 103 | | case 2:
|
| 104 | | if (bank[5] == 0x0c)
|
| 105 | | {
|
| 106 | | memory_set_bank(space->machine, "bank3", 0x40 + BIT(data, 7));
|
| 107 | | }
|
| 108 | | else
|
| 109 | | {
|
| 110 | | memory_set_bank(space->machine, "bank3", data & 0x3f);
|
| 111 | | }
|
| 112 | | break;
|
| 113 | | |
| 114 | | case 3:
|
| 115 | | memory_set_bank(space->machine, "bank4", data & 0x03);
|
| 116 | | break;
|
| 117 | | |
| 118 | | case 4:
|
| 119 | | memory_set_bank(space->machine, "bank5", data & 0x03);
|
| 120 | | break;
|
| 121 | | |
| 122 | | case 5:
|
| 123 | | if (data == 0x0c)
|
| 124 | | {
|
| 125 | | memory_install_readwrite_bank(program, 0x8000, 0xbfff, 0, 0, "bank3");
|
| 126 | | }
|
| 127 | | else
|
| 128 | | {
|
| 129 | | memory_unmap_write(program, 0x8000, 0xbfff, 0, 0);
|
| 130 | | }
|
| 131 | | }
|
| 132 | | |
| 133 | | bank[offset] = data;
|
| 134 | | }
|
| 135 | | |
| 136 | | static ADDRESS_MAP_START(prestige_mem, ADDRESS_SPACE_PROGRAM, 8)
|
| 137 | | AM_RANGE(0x0000, 0x3fff) AM_ROMBANK("bank1")
|
| 138 | | AM_RANGE(0x4000, 0x7fff) AM_ROMBANK("bank2")
|
| 139 | | AM_RANGE(0x8000, 0xbfff) AM_ROMBANK("bank3")
|
| 140 | | AM_RANGE(0xc000, 0xdfff) AM_RAMBANK("bank4")
|
| 141 | | AM_RANGE(0xe000, 0xffff) AM_RAMBANK("bank5")
|
| 142 | | ADDRESS_MAP_END
|
| 143 | | |
| 144 | | static ADDRESS_MAP_START( prestige_io , ADDRESS_SPACE_IO, 8)
|
| 145 | | ADDRESS_MAP_UNMAP_HIGH
|
| 146 | | ADDRESS_MAP_GLOBAL_MASK(0xff)
|
| 147 | | AM_RANGE(0x50, 0x55) AM_READWRITE(bankswitch_r, bankswitch_w)
|
| 148 | | ADDRESS_MAP_END
|
| 149 | | |
| 150 | | /* Input ports */
|
| 151 | | INPUT_PORTS_START( prestige )
|
| 152 | | INPUT_PORTS_END
|
| 153 | | |
| 154 | | |
| 155 | | static MACHINE_START(prestige)
|
| 156 | | {
|
| 157 | | UINT8 *ram = messram_get_ptr(machine->device("messram"));
|
| 158 | | |
| 159 | | memory_configure_bank(machine, "bank1", 0, 64, memory_region(machine, "maincpu"), 0x4000);
|
| 160 | | memory_configure_bank(machine, "bank2", 0, 64, memory_region(machine, "maincpu"), 0x4000);
|
| 161 | | memory_configure_bank(machine, "bank3", 0, 64, memory_region(machine, "maincpu"), 0x4000);
|
| 162 | | memory_configure_bank(machine, "bank3", 64, 2, ram + 0x8000, 0x4000);
|
| 163 | | memory_configure_bank(machine, "bank4", 0, 4, ram, 0x2000);
|
| 164 | | memory_configure_bank(machine, "bank5", 0, 4, ram, 0x2000);
|
| 165 | | |
| 166 | | memory_set_bank(machine, "bank1", 0);
|
| 167 | | memory_set_bank(machine, "bank2", 0);
|
| 168 | | memory_set_bank(machine, "bank3", 0);
|
| 169 | | memory_set_bank(machine, "bank4", 0);
|
| 170 | | memory_set_bank(machine, "bank5", 0);
|
| 171 | | }
|
| 172 | | |
| 173 | | static PALETTE_INIT( prestige )
|
| 174 | | {
|
| 175 | | palette_set_color(machine, 0, MAKE_RGB(39, 108, 51));
|
| 176 | | palette_set_color(machine, 1, MAKE_RGB(16, 37, 84));
|
| 177 | | }
|
| 178 | | |
| 179 | | static VIDEO_START( prestige )
|
| 180 | | {
|
| 181 | | }
|
| 182 | | |
| 183 | | static VIDEO_UPDATE( prestige )
|
| 184 | | {
|
| 185 | | const address_space *program = cputag_get_address_space(screen->machine, "maincpu", ADDRESS_SPACE_PROGRAM);
|
| 186 | | UINT16 addr = 0xe000;
|
| 187 | | |
| 188 | | for (int y = 0; y < 100; y++)
|
| 189 | | {
|
| 190 | | for (int sx = 0; sx < 30; sx++)
|
| 191 | | {
|
| 192 | | UINT8 data = memory_read_byte(program, addr);
|
| 193 | | |
| 194 | | for (int x = 0; x < 8; x++)
|
| 195 | | {
|
| 196 | | *BITMAP_ADDR16(bitmap, y, (sx * 8) + x) = BIT(data, 7);
|
| 197 | | |
| 198 | | data <<= 1;
|
| 199 | | }
|
| 200 | | |
| 201 | | addr++;
|
| 202 | | }
|
| 203 | | }
|
| 204 | | |
| 205 | | return 0;
|
| 206 | | }
|
| 207 | | |
| 208 | | static DEVICE_IMAGE_LOAD( prestige_cart )
|
| 209 | | {
|
| 210 | | return IMAGE_INIT_FAIL;
|
| 211 | | }
|
| 212 | | |
| 213 | | static MACHINE_DRIVER_START( prestige )
|
| 214 | | /* basic machine hardware */
|
| 215 | | MDRV_CPU_ADD("maincpu",Z80, XTAL_4MHz)
|
| 216 | | MDRV_CPU_PROGRAM_MAP(prestige_mem)
|
| 217 | | MDRV_CPU_IO_MAP(prestige_io)
|
| 218 | | |
| 219 | | MDRV_MACHINE_START(prestige)
|
| 220 | | |
| 221 | | /* video hardware */
|
| 222 | | MDRV_SCREEN_ADD("screen", LCD)
|
| 223 | | MDRV_SCREEN_REFRESH_RATE(50)
|
| 224 | | MDRV_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */
|
| 225 | | MDRV_SCREEN_FORMAT(BITMAP_FORMAT_INDEXED16)
|
| 226 | | MDRV_SCREEN_SIZE( 240, 100 )
|
| 227 | | MDRV_SCREEN_VISIBLE_AREA( 0, 240-1, 0, 100-1 )
|
| 228 | | MDRV_DEFAULT_LAYOUT( layout_lcd )
|
| 229 | | |
| 230 | | MDRV_PALETTE_LENGTH(2)
|
| 231 | | MDRV_PALETTE_INIT(prestige)
|
| 232 | | |
| 233 | | MDRV_VIDEO_START(prestige)
|
| 234 | | MDRV_VIDEO_UPDATE(prestige)
|
| 235 | | |
| 236 | | /* cartridge */
|
| 237 | | MDRV_CARTSLOT_ADD("cart")
|
| 238 | | MDRV_CARTSLOT_EXTENSION_LIST("bin")
|
| 239 | | MDRV_CARTSLOT_INTERFACE("prestige_cart")
|
| 240 | | MDRV_CARTSLOT_LOAD(prestige_cart)
|
| 241 | | |
| 242 | | /* internal ram */
|
| 243 | | MDRV_RAM_ADD("messram")
|
| 244 | | MDRV_RAM_DEFAULT_SIZE("32K")
|
| 245 | | MDRV_RAM_EXTRA_OPTIONS("64K")
|
| 246 | | MACHINE_DRIVER_END
|
| 247 | | |
| 248 | | /* ROM definition */
|
| 249 | | ROM_START( prestige )
|
| 250 | | ROM_REGION( 0x100000, "maincpu", 0 )
|
| 251 | | ROM_LOAD( "27-6020-02.u2", 0x00000, 0x100000, CRC(6bb6db14) SHA1(5d51fc3fd799e7f01ee99c453f9005fb07747b1e) )
|
| 252 | | ROM_END
|
| 253 | | |
| 254 | | /* Driver */
|
| 255 | | |
| 256 | | /* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */
|
| 257 | | COMP( 1998, prestige, 0, 0, prestige, prestige, 0, "VTech", "PC Prestige Elite", GAME_NOT_WORKING | GAME_NO_SOUND)
|
| 1 | /* |
| 2 | |
| 3 | VTech PreComputer Prestige Elite |
| 4 | |
| 5 | PCB Layout |
| 6 | ---------- |
| 7 | |
| 8 | |-------------------------------------------| |
| 9 | | |-------CN1-------| CN2 | |
| 10 | | | |
| 11 | | CN3 | |
| 12 | | | |
| 13 | |CN10 | |
| 14 | |CN9 RAM --| |
| 15 | |CN8 | |
| 16 | | | |
| 17 | | Z80 ROM | |
| 18 | | 04010 CN4 | |
| 19 | |CN7 | |
| 20 | |CN6 | |
| 21 | | |------CN5------| --| |
| 22 | |-------------------------------------------| |
| 23 | |
| 24 | Notes: |
| 25 | All IC's shown. |
| 26 | |
| 27 | ROM - VTech LH5S8R14, labeled "1998 27-6020-02" (dumped as 1Mx8) |
| 28 | Z80 - Z80 family SOC? |
| 29 | RAM - LG Semicon GM76C256CLLFW55 32Kx8 Static RAM |
| 30 | 04010 - ? |
| 31 | CN1 - Centronics connector |
| 32 | CN2 - mouse connector |
| 33 | CN3 - LCD ribbon cable |
| 34 | CN4 - expansion connector |
| 35 | CN5 - keyboard ribbon cable |
| 36 | CN6 - speaker wire |
| 37 | CN7 - volume wire |
| 38 | CN8 - ? wire |
| 39 | CN9 - power wire |
| 40 | CN10 - NVRAM battery wire |
| 41 | */ |
| 42 | |
| 43 | /* |
| 44 | |
| 45 | Undumped cartridges: |
| 46 | |
| 47 | 80-1410 Super Science |
| 48 | 80-1533 Famous Things & Places |
| 49 | 80-0989 Bible Knowledge |
| 50 | 80-1001 Fantasy Trivia |
| 51 | 80-1002 General Knowledge II |
| 52 | 80-1003 Sports History |
| 53 | 80-2314 Familiar Faces |
| 54 | 80-2315 Historical Happenings |
| 55 | 80-2333 Arts, Entertainment & More |
| 56 | 80-2334 Customs & Cultures |
| 57 | 80-1531 32K RAM Memory Expansion Cartridge |
| 58 | 80-12051 Space Scholar |
| 59 | 80-12053 Frenzy of Facts |
| 60 | 80-12052 Spreadsheet Success |
| 61 | |
| 62 | */ |
| 63 | |
| 64 | /* |
| 65 | |
| 66 | TODO: |
| 67 | |
| 68 | - identify unknown chips |
| 69 | - boot animation won't play |
| 70 | - keyboard |
| 71 | - mouse |
| 72 | - sound |
| 73 | - cartridges |
| 74 | |
| 75 | */ |
| 76 | |
| 77 | #include "emu.h" |
| 78 | #include "cpu/z80/z80.h" |
| 79 | #include "devices/messram.h" |
| 80 | #include "devices/cartslot.h" |
| 81 | |
| 82 | static UINT8 bank[6]; |
| 83 | |
| 84 | static READ8_HANDLER( bankswitch_r ) |
| 85 | { |
| 86 | return bank[offset]; |
| 87 | } |
| 88 | |
| 89 | static WRITE8_HANDLER( bankswitch_w ) |
| 90 | { |
| 91 | const address_space *program = cputag_get_address_space(space->machine, "maincpu", ADDRESS_SPACE_PROGRAM); |
| 92 | |
| 93 | switch (offset) |
| 94 | { |
| 95 | case 0: |
| 96 | memory_set_bank(space->machine, "bank1", data & 0x3f); |
| 97 | break; |
| 98 | |
| 99 | case 1: |
| 100 | memory_set_bank(space->machine, "bank2", data & 0x3f); |
| 101 | break; |
| 102 | |
| 103 | case 2: |
| 104 | if (bank[5] == 0x0c) |
| 105 | { |
| 106 | memory_set_bank(space->machine, "bank3", 0x40 + BIT(data, 7)); |
| 107 | } |
| 108 | else |
| 109 | { |
| 110 | memory_set_bank(space->machine, "bank3", data & 0x3f); |
| 111 | } |
| 112 | break; |
| 113 | |
| 114 | case 3: |
| 115 | memory_set_bank(space->machine, "bank4", data & 0x03); |
| 116 | break; |
| 117 | |
| 118 | case 4: |
| 119 | memory_set_bank(space->machine, "bank5", data & 0x03); |
| 120 | break; |
| 121 | |
| 122 | case 5: |
| 123 | if (data == 0x0c) |
| 124 | { |
| 125 | memory_install_readwrite_bank(program, 0x8000, 0xbfff, 0, 0, "bank3"); |
| 126 | } |
| 127 | else |
| 128 | { |
| 129 | memory_unmap_write(program, 0x8000, 0xbfff, 0, 0); |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | bank[offset] = data; |
| 134 | } |
| 135 | |
| 136 | static ADDRESS_MAP_START(prestige_mem, ADDRESS_SPACE_PROGRAM, 8) |
| 137 | AM_RANGE(0x0000, 0x3fff) AM_ROMBANK("bank1") |
| 138 | AM_RANGE(0x4000, 0x7fff) AM_ROMBANK("bank2") |
| 139 | AM_RANGE(0x8000, 0xbfff) AM_ROMBANK("bank3") |
| 140 | AM_RANGE(0xc000, 0xdfff) AM_RAMBANK("bank4") |
| 141 | AM_RANGE(0xe000, 0xffff) AM_RAMBANK("bank5") |
| 142 | ADDRESS_MAP_END |
| 143 | |
| 144 | static ADDRESS_MAP_START( prestige_io , ADDRESS_SPACE_IO, 8) |
| 145 | ADDRESS_MAP_UNMAP_HIGH |
| 146 | ADDRESS_MAP_GLOBAL_MASK(0xff) |
| 147 | AM_RANGE(0x50, 0x55) AM_READWRITE(bankswitch_r, bankswitch_w) |
| 148 | ADDRESS_MAP_END |
| 149 | |
| 150 | /* Input ports */ |
| 151 | INPUT_PORTS_START( prestige ) |
| 152 | INPUT_PORTS_END |
| 153 | |
| 154 | |
| 155 | static MACHINE_START(prestige) |
| 156 | { |
| 157 | UINT8 *ram = messram_get_ptr(machine->device("messram")); |
| 158 | |
| 159 | memory_configure_bank(machine, "bank1", 0, 64, memory_region(machine, "maincpu"), 0x4000); |
| 160 | memory_configure_bank(machine, "bank2", 0, 64, memory_region(machine, "maincpu"), 0x4000); |
| 161 | memory_configure_bank(machine, "bank3", 0, 64, memory_region(machine, "maincpu"), 0x4000); |
| 162 | memory_configure_bank(machine, "bank3", 64, 2, ram + 0x8000, 0x4000); |
| 163 | memory_configure_bank(machine, "bank4", 0, 4, ram, 0x2000); |
| 164 | memory_configure_bank(machine, "bank5", 0, 4, ram, 0x2000); |
| 165 | |
| 166 | memory_set_bank(machine, "bank1", 0); |
| 167 | memory_set_bank(machine, "bank2", 0); |
| 168 | memory_set_bank(machine, "bank3", 0); |
| 169 | memory_set_bank(machine, "bank4", 0); |
| 170 | memory_set_bank(machine, "bank5", 0); |
| 171 | } |
| 172 | |
| 173 | static PALETTE_INIT( prestige ) |
| 174 | { |
| 175 | palette_set_color(machine, 0, MAKE_RGB(39, 108, 51)); |
| 176 | palette_set_color(machine, 1, MAKE_RGB(16, 37, 84)); |
| 177 | } |
| 178 | |
| 179 | static VIDEO_START( prestige ) |
| 180 | { |
| 181 | } |
| 182 | |
| 183 | static VIDEO_UPDATE( prestige ) |
| 184 | { |
| 185 | const address_space *program = cputag_get_address_space(screen->machine, "maincpu", ADDRESS_SPACE_PROGRAM); |
| 186 | UINT16 addr = 0xe000; |
| 187 | |
| 188 | for (int y = 0; y < 100; y++) |
| 189 | { |
| 190 | for (int sx = 0; sx < 30; sx++) |
| 191 | { |
| 192 | UINT8 data = memory_read_byte(program, addr); |
| 193 | |
| 194 | for (int x = 0; x < 8; x++) |
| 195 | { |
| 196 | *BITMAP_ADDR16(bitmap, y, (sx * 8) + x) = BIT(data, 7); |
| 197 | |
| 198 | data <<= 1; |
| 199 | } |
| 200 | |
| 201 | addr++; |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | return 0; |
| 206 | } |
| 207 | |
| 208 | static DEVICE_IMAGE_LOAD( prestige_cart ) |
| 209 | { |
| 210 | return IMAGE_INIT_FAIL; |
| 211 | } |
| 212 | |
| 213 | static MACHINE_DRIVER_START( prestige ) |
| 214 | /* basic machine hardware */ |
| 215 | MDRV_CPU_ADD("maincpu",Z80, XTAL_4MHz) |
| 216 | MDRV_CPU_PROGRAM_MAP(prestige_mem) |
| 217 | MDRV_CPU_IO_MAP(prestige_io) |
| 218 | |
| 219 | MDRV_MACHINE_START(prestige) |
| 220 | |
| 221 | /* video hardware */ |
| 222 | MDRV_SCREEN_ADD("screen", LCD) |
| 223 | MDRV_SCREEN_REFRESH_RATE(50) |
| 224 | MDRV_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */ |
| 225 | MDRV_SCREEN_FORMAT(BITMAP_FORMAT_INDEXED16) |
| 226 | MDRV_SCREEN_SIZE( 240, 100 ) |
| 227 | MDRV_SCREEN_VISIBLE_AREA( 0, 240-1, 0, 100-1 ) |
| 228 | MDRV_DEFAULT_LAYOUT( layout_lcd ) |
| 229 | |
| 230 | MDRV_PALETTE_LENGTH(2) |
| 231 | MDRV_PALETTE_INIT(prestige) |
| 232 | |
| 233 | MDRV_VIDEO_START(prestige) |
| 234 | MDRV_VIDEO_UPDATE(prestige) |
| 235 | |
| 236 | /* cartridge */ |
| 237 | MDRV_CARTSLOT_ADD("cart") |
| 238 | MDRV_CARTSLOT_EXTENSION_LIST("bin") |
| 239 | MDRV_CARTSLOT_INTERFACE("prestige_cart") |
| 240 | MDRV_CARTSLOT_LOAD(prestige_cart) |
| 241 | |
| 242 | /* internal ram */ |
| 243 | MDRV_RAM_ADD("messram") |
| 244 | MDRV_RAM_DEFAULT_SIZE("32K") |
| 245 | MDRV_RAM_EXTRA_OPTIONS("64K") |
| 246 | MACHINE_DRIVER_END |
| 247 | |
| 248 | /* ROM definition */ |
| 249 | ROM_START( prestige ) |
| 250 | ROM_REGION( 0x100000, "maincpu", 0 ) |
| 251 | ROM_LOAD( "27-6020-02.u2", 0x00000, 0x100000, CRC(6bb6db14) SHA1(5d51fc3fd799e7f01ee99c453f9005fb07747b1e) ) |
| 252 | ROM_END |
| 253 | |
| 254 | /* Driver */ |
| 255 | |
| 256 | /* YEAR NAME PARENT COMPAT MACHINE INPUT INIT COMPANY FULLNAME FLAGS */ |
| 257 | COMP( 1998, prestige, 0, 0, prestige, prestige, 0, "VTech", "PC Prestige Elite", GAME_NOT_WORKING | GAME_NO_SOUND) |