1 /* 2 * QEMU PowerMac CUDA device support 3 * 4 * Copyright (c) 2004-2007 Fabrice Bellard 5 * Copyright (c) 2007 Jocelyn Mayer 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 * THE SOFTWARE. 24 */ 25 #include "qemu/osdep.h" 26 #include "hw/hw.h" 27 #include "hw/ppc/mac.h" 28 #include "hw/input/adb.h" 29 #include "hw/misc/mos6522.h" 30 #include "qemu/timer.h" 31 #include "sysemu/sysemu.h" 32 #include "qemu/cutils.h" 33 #include "qemu/log.h" 34 35 /* debug CUDA packets */ 36 //#define DEBUG_CUDA_PACKET 37 38 /* debug CUDA packets */ 39 //#define DEBUG_CUDA_PACKET 40 41 #ifdef DEBUG_CUDA 42 #define CUDA_DPRINTF(fmt, ...) \ 43 do { printf("CUDA: " fmt , ## __VA_ARGS__); } while (0) 44 #else 45 #define CUDA_DPRINTF(fmt, ...) 46 #endif 47 48 /* Bits in B data register: all active low */ 49 #define TREQ 0x08 /* Transfer request (input) */ 50 #define TACK 0x10 /* Transfer acknowledge (output) */ 51 #define TIP 0x20 /* Transfer in progress (output) */ 52 53 /* commands (1st byte) */ 54 #define ADB_PACKET 0 55 #define CUDA_PACKET 1 56 #define ERROR_PACKET 2 57 #define TIMER_PACKET 3 58 #define POWER_PACKET 4 59 #define MACIIC_PACKET 5 60 #define PMU_PACKET 6 61 62 #define CUDA_TIMER_FREQ (4700000 / 6) 63 64 /* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */ 65 #define RTC_OFFSET 2082844800 66 67 static void cuda_receive_packet_from_host(CUDAState *s, 68 const uint8_t *data, int len); 69 70 /* MacOS uses timer 1 for calibration on startup, so we use 71 * the timebase frequency and cuda_get_counter_value() with 72 * cuda_get_load_time() to steer MacOS to calculate calibrate its timers 73 * correctly for both TCG and KVM (see commit b981289c49 "PPC: Cuda: Use cuda 74 * timer to expose tbfreq to guest" for more information) */ 75 76 static uint64_t cuda_get_counter_value(MOS6522State *s, MOS6522Timer *ti) 77 { 78 MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); 79 CUDAState *cs = mcs->cuda; 80 81 /* Reverse of the tb calculation algorithm that Mac OS X uses on bootup */ 82 uint64_t tb_diff = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 83 cs->tb_frequency, NANOSECONDS_PER_SECOND) - 84 ti->load_time; 85 86 return (tb_diff * 0xBF401675E5DULL) / (cs->tb_frequency << 24); 87 } 88 89 static uint64_t cuda_get_load_time(MOS6522State *s, MOS6522Timer *ti) 90 { 91 MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); 92 CUDAState *cs = mcs->cuda; 93 94 uint64_t load_time = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 95 cs->tb_frequency, NANOSECONDS_PER_SECOND); 96 return load_time; 97 } 98 99 static void cuda_set_sr_int(void *opaque) 100 { 101 CUDAState *s = opaque; 102 MOS6522CUDAState *mcs = s->mos6522_cuda; 103 MOS6522State *ms = MOS6522(mcs); 104 MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); 105 106 mdc->set_sr_int(ms); 107 } 108 109 static void cuda_delay_set_sr_int(CUDAState *s) 110 { 111 MOS6522CUDAState *mcs = s->mos6522_cuda; 112 MOS6522State *ms = MOS6522(mcs); 113 MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); 114 int64_t expire; 115 116 if (ms->dirb == 0xff || s->sr_delay_ns == 0) { 117 /* Disabled or not in Mac OS, fire the IRQ directly */ 118 mdc->set_sr_int(ms); 119 return; 120 } 121 122 CUDA_DPRINTF("CUDA: %s:%d\n", __func__, __LINE__); 123 124 expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->sr_delay_ns; 125 timer_mod(s->sr_delay_timer, expire); 126 } 127 128 /* NOTE: TIP and TREQ are negated */ 129 static void cuda_update(CUDAState *s) 130 { 131 MOS6522CUDAState *mcs = s->mos6522_cuda; 132 MOS6522State *ms = MOS6522(mcs); 133 int packet_received, len; 134 135 packet_received = 0; 136 if (!(ms->b & TIP)) { 137 /* transfer requested from host */ 138 139 if (ms->acr & SR_OUT) { 140 /* data output */ 141 if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { 142 if (s->data_out_index < sizeof(s->data_out)) { 143 CUDA_DPRINTF("send: %02x\n", ms->sr); 144 s->data_out[s->data_out_index++] = ms->sr; 145 cuda_delay_set_sr_int(s); 146 } 147 } 148 } else { 149 if (s->data_in_index < s->data_in_size) { 150 /* data input */ 151 if ((ms->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) { 152 ms->sr = s->data_in[s->data_in_index++]; 153 CUDA_DPRINTF("recv: %02x\n", ms->sr); 154 /* indicate end of transfer */ 155 if (s->data_in_index >= s->data_in_size) { 156 ms->b = (ms->b | TREQ); 157 } 158 cuda_delay_set_sr_int(s); 159 } 160 } 161 } 162 } else { 163 /* no transfer requested: handle sync case */ 164 if ((s->last_b & TIP) && (ms->b & TACK) != (s->last_b & TACK)) { 165 /* update TREQ state each time TACK change state */ 166 if (ms->b & TACK) { 167 ms->b = (ms->b | TREQ); 168 } else { 169 ms->b = (ms->b & ~TREQ); 170 } 171 cuda_delay_set_sr_int(s); 172 } else { 173 if (!(s->last_b & TIP)) { 174 /* handle end of host to cuda transfer */ 175 packet_received = (s->data_out_index > 0); 176 /* always an IRQ at the end of transfer */ 177 cuda_delay_set_sr_int(s); 178 } 179 /* signal if there is data to read */ 180 if (s->data_in_index < s->data_in_size) { 181 ms->b = (ms->b & ~TREQ); 182 } 183 } 184 } 185 186 s->last_acr = ms->acr; 187 s->last_b = ms->b; 188 189 /* NOTE: cuda_receive_packet_from_host() can call cuda_update() 190 recursively */ 191 if (packet_received) { 192 len = s->data_out_index; 193 s->data_out_index = 0; 194 cuda_receive_packet_from_host(s, s->data_out, len); 195 } 196 } 197 198 static void cuda_send_packet_to_host(CUDAState *s, 199 const uint8_t *data, int len) 200 { 201 #ifdef DEBUG_CUDA_PACKET 202 { 203 int i; 204 printf("cuda_send_packet_to_host:\n"); 205 for(i = 0; i < len; i++) 206 printf(" %02x", data[i]); 207 printf("\n"); 208 } 209 #endif 210 memcpy(s->data_in, data, len); 211 s->data_in_size = len; 212 s->data_in_index = 0; 213 cuda_update(s); 214 cuda_delay_set_sr_int(s); 215 } 216 217 static void cuda_adb_poll(void *opaque) 218 { 219 CUDAState *s = opaque; 220 uint8_t obuf[ADB_MAX_OUT_LEN + 2]; 221 int olen; 222 223 olen = adb_poll(&s->adb_bus, obuf + 2, s->adb_poll_mask); 224 if (olen > 0) { 225 obuf[0] = ADB_PACKET; 226 obuf[1] = 0x40; /* polled data */ 227 cuda_send_packet_to_host(s, obuf, olen + 2); 228 } 229 timer_mod(s->adb_poll_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 230 (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); 231 } 232 233 /* description of commands */ 234 typedef struct CudaCommand { 235 uint8_t command; 236 const char *name; 237 bool (*handler)(CUDAState *s, 238 const uint8_t *in_args, int in_len, 239 uint8_t *out_args, int *out_len); 240 } CudaCommand; 241 242 static bool cuda_cmd_autopoll(CUDAState *s, 243 const uint8_t *in_data, int in_len, 244 uint8_t *out_data, int *out_len) 245 { 246 int autopoll; 247 248 if (in_len != 1) { 249 return false; 250 } 251 252 autopoll = (in_data[0] != 0); 253 if (autopoll != s->autopoll) { 254 s->autopoll = autopoll; 255 if (autopoll) { 256 timer_mod(s->adb_poll_timer, 257 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 258 (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); 259 } else { 260 timer_del(s->adb_poll_timer); 261 } 262 } 263 return true; 264 } 265 266 static bool cuda_cmd_set_autorate(CUDAState *s, 267 const uint8_t *in_data, int in_len, 268 uint8_t *out_data, int *out_len) 269 { 270 if (in_len != 1) { 271 return false; 272 } 273 274 /* we don't want a period of 0 ms */ 275 /* FIXME: check what real hardware does */ 276 if (in_data[0] == 0) { 277 return false; 278 } 279 280 s->autopoll_rate_ms = in_data[0]; 281 if (s->autopoll) { 282 timer_mod(s->adb_poll_timer, 283 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 284 (NANOSECONDS_PER_SECOND / (1000 / s->autopoll_rate_ms))); 285 } 286 return true; 287 } 288 289 static bool cuda_cmd_set_device_list(CUDAState *s, 290 const uint8_t *in_data, int in_len, 291 uint8_t *out_data, int *out_len) 292 { 293 if (in_len != 2) { 294 return false; 295 } 296 297 s->adb_poll_mask = (((uint16_t)in_data[0]) << 8) | in_data[1]; 298 return true; 299 } 300 301 static bool cuda_cmd_powerdown(CUDAState *s, 302 const uint8_t *in_data, int in_len, 303 uint8_t *out_data, int *out_len) 304 { 305 if (in_len != 0) { 306 return false; 307 } 308 309 qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); 310 return true; 311 } 312 313 static bool cuda_cmd_reset_system(CUDAState *s, 314 const uint8_t *in_data, int in_len, 315 uint8_t *out_data, int *out_len) 316 { 317 if (in_len != 0) { 318 return false; 319 } 320 321 qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 322 return true; 323 } 324 325 static bool cuda_cmd_set_file_server_flag(CUDAState *s, 326 const uint8_t *in_data, int in_len, 327 uint8_t *out_data, int *out_len) 328 { 329 if (in_len != 1) { 330 return false; 331 } 332 333 qemu_log_mask(LOG_UNIMP, 334 "CUDA: unimplemented command FILE_SERVER_FLAG %d\n", 335 in_data[0]); 336 return true; 337 } 338 339 static bool cuda_cmd_set_power_message(CUDAState *s, 340 const uint8_t *in_data, int in_len, 341 uint8_t *out_data, int *out_len) 342 { 343 if (in_len != 1) { 344 return false; 345 } 346 347 qemu_log_mask(LOG_UNIMP, 348 "CUDA: unimplemented command SET_POWER_MESSAGE %d\n", 349 in_data[0]); 350 return true; 351 } 352 353 static bool cuda_cmd_get_time(CUDAState *s, 354 const uint8_t *in_data, int in_len, 355 uint8_t *out_data, int *out_len) 356 { 357 uint32_t ti; 358 359 if (in_len != 0) { 360 return false; 361 } 362 363 ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) 364 / NANOSECONDS_PER_SECOND); 365 out_data[0] = ti >> 24; 366 out_data[1] = ti >> 16; 367 out_data[2] = ti >> 8; 368 out_data[3] = ti; 369 *out_len = 4; 370 return true; 371 } 372 373 static bool cuda_cmd_set_time(CUDAState *s, 374 const uint8_t *in_data, int in_len, 375 uint8_t *out_data, int *out_len) 376 { 377 uint32_t ti; 378 379 if (in_len != 4) { 380 return false; 381 } 382 383 ti = (((uint32_t)in_data[0]) << 24) + (((uint32_t)in_data[1]) << 16) 384 + (((uint32_t)in_data[2]) << 8) + in_data[3]; 385 s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) 386 / NANOSECONDS_PER_SECOND); 387 return true; 388 } 389 390 static const CudaCommand handlers[] = { 391 { CUDA_AUTOPOLL, "AUTOPOLL", cuda_cmd_autopoll }, 392 { CUDA_SET_AUTO_RATE, "SET_AUTO_RATE", cuda_cmd_set_autorate }, 393 { CUDA_SET_DEVICE_LIST, "SET_DEVICE_LIST", cuda_cmd_set_device_list }, 394 { CUDA_POWERDOWN, "POWERDOWN", cuda_cmd_powerdown }, 395 { CUDA_RESET_SYSTEM, "RESET_SYSTEM", cuda_cmd_reset_system }, 396 { CUDA_FILE_SERVER_FLAG, "FILE_SERVER_FLAG", 397 cuda_cmd_set_file_server_flag }, 398 { CUDA_SET_POWER_MESSAGES, "SET_POWER_MESSAGES", 399 cuda_cmd_set_power_message }, 400 { CUDA_GET_TIME, "GET_TIME", cuda_cmd_get_time }, 401 { CUDA_SET_TIME, "SET_TIME", cuda_cmd_set_time }, 402 }; 403 404 static void cuda_receive_packet(CUDAState *s, 405 const uint8_t *data, int len) 406 { 407 uint8_t obuf[16] = { CUDA_PACKET, 0, data[0] }; 408 int i, out_len = 0; 409 410 for (i = 0; i < ARRAY_SIZE(handlers); i++) { 411 const CudaCommand *desc = &handlers[i]; 412 if (desc->command == data[0]) { 413 CUDA_DPRINTF("handling command %s\n", desc->name); 414 out_len = 0; 415 if (desc->handler(s, data + 1, len - 1, obuf + 3, &out_len)) { 416 cuda_send_packet_to_host(s, obuf, 3 + out_len); 417 } else { 418 qemu_log_mask(LOG_GUEST_ERROR, 419 "CUDA: %s: wrong parameters %d\n", 420 desc->name, len); 421 obuf[0] = ERROR_PACKET; 422 obuf[1] = 0x5; /* bad parameters */ 423 obuf[2] = CUDA_PACKET; 424 obuf[3] = data[0]; 425 cuda_send_packet_to_host(s, obuf, 4); 426 } 427 return; 428 } 429 } 430 431 qemu_log_mask(LOG_GUEST_ERROR, "CUDA: unknown command 0x%02x\n", data[0]); 432 obuf[0] = ERROR_PACKET; 433 obuf[1] = 0x2; /* unknown command */ 434 obuf[2] = CUDA_PACKET; 435 obuf[3] = data[0]; 436 cuda_send_packet_to_host(s, obuf, 4); 437 } 438 439 static void cuda_receive_packet_from_host(CUDAState *s, 440 const uint8_t *data, int len) 441 { 442 #ifdef DEBUG_CUDA_PACKET 443 { 444 int i; 445 printf("cuda_receive_packet_from_host:\n"); 446 for(i = 0; i < len; i++) 447 printf(" %02x", data[i]); 448 printf("\n"); 449 } 450 #endif 451 switch(data[0]) { 452 case ADB_PACKET: 453 { 454 uint8_t obuf[ADB_MAX_OUT_LEN + 3]; 455 int olen; 456 olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1); 457 if (olen > 0) { 458 obuf[0] = ADB_PACKET; 459 obuf[1] = 0x00; 460 cuda_send_packet_to_host(s, obuf, olen + 2); 461 } else { 462 /* error */ 463 obuf[0] = ADB_PACKET; 464 obuf[1] = -olen; 465 obuf[2] = data[1]; 466 olen = 0; 467 cuda_send_packet_to_host(s, obuf, olen + 3); 468 } 469 } 470 break; 471 case CUDA_PACKET: 472 cuda_receive_packet(s, data + 1, len - 1); 473 break; 474 } 475 } 476 477 static uint64_t mos6522_cuda_read(void *opaque, hwaddr addr, unsigned size) 478 { 479 CUDAState *s = opaque; 480 MOS6522CUDAState *mcs = s->mos6522_cuda; 481 MOS6522State *ms = MOS6522(mcs); 482 483 addr = (addr >> 9) & 0xf; 484 return mos6522_read(ms, addr, size); 485 } 486 487 static void mos6522_cuda_write(void *opaque, hwaddr addr, uint64_t val, 488 unsigned size) 489 { 490 CUDAState *s = opaque; 491 MOS6522CUDAState *mcs = s->mos6522_cuda; 492 MOS6522State *ms = MOS6522(mcs); 493 494 addr = (addr >> 9) & 0xf; 495 mos6522_write(ms, addr, val, size); 496 } 497 498 static const MemoryRegionOps mos6522_cuda_ops = { 499 .read = mos6522_cuda_read, 500 .write = mos6522_cuda_write, 501 .endianness = DEVICE_BIG_ENDIAN, 502 .valid = { 503 .min_access_size = 1, 504 .max_access_size = 1, 505 }, 506 }; 507 508 static const VMStateDescription vmstate_cuda = { 509 .name = "cuda", 510 .version_id = 4, 511 .minimum_version_id = 4, 512 .fields = (VMStateField[]) { 513 VMSTATE_UINT8(last_b, CUDAState), 514 VMSTATE_UINT8(last_acr, CUDAState), 515 VMSTATE_INT32(data_in_size, CUDAState), 516 VMSTATE_INT32(data_in_index, CUDAState), 517 VMSTATE_INT32(data_out_index, CUDAState), 518 VMSTATE_UINT8(autopoll, CUDAState), 519 VMSTATE_UINT8(autopoll_rate_ms, CUDAState), 520 VMSTATE_UINT16(adb_poll_mask, CUDAState), 521 VMSTATE_BUFFER(data_in, CUDAState), 522 VMSTATE_BUFFER(data_out, CUDAState), 523 VMSTATE_UINT32(tick_offset, CUDAState), 524 VMSTATE_TIMER_PTR(adb_poll_timer, CUDAState), 525 VMSTATE_TIMER_PTR(sr_delay_timer, CUDAState), 526 VMSTATE_END_OF_LIST() 527 } 528 }; 529 530 static void cuda_reset(DeviceState *dev) 531 { 532 CUDAState *s = CUDA(dev); 533 534 s->data_in_size = 0; 535 s->data_in_index = 0; 536 s->data_out_index = 0; 537 s->autopoll = 0; 538 } 539 540 static void cuda_realize(DeviceState *dev, Error **errp) 541 { 542 CUDAState *s = CUDA(dev); 543 SysBusDevice *sbd; 544 MOS6522State *ms; 545 DeviceState *d; 546 struct tm tm; 547 548 d = qdev_create(NULL, TYPE_MOS6522_CUDA); 549 object_property_set_link(OBJECT(d), OBJECT(s), "cuda", errp); 550 qdev_init_nofail(d); 551 s->mos6522_cuda = MOS6522_CUDA(d); 552 553 /* Pass IRQ from 6522 */ 554 ms = MOS6522(d); 555 sbd = SYS_BUS_DEVICE(s); 556 sysbus_pass_irq(sbd, SYS_BUS_DEVICE(ms)); 557 558 qemu_get_timedate(&tm, 0); 559 s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; 560 561 s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s); 562 s->sr_delay_ns = 300 * SCALE_US; 563 564 s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s); 565 s->adb_poll_mask = 0xffff; 566 s->autopoll_rate_ms = 20; 567 } 568 569 static void cuda_init(Object *obj) 570 { 571 CUDAState *s = CUDA(obj); 572 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 573 574 memory_region_init_io(&s->mem, obj, &mos6522_cuda_ops, s, "cuda", 0x2000); 575 sysbus_init_mmio(sbd, &s->mem); 576 577 qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, 578 DEVICE(obj), "adb.0"); 579 } 580 581 static Property cuda_properties[] = { 582 DEFINE_PROP_UINT64("timebase-frequency", CUDAState, tb_frequency, 0), 583 DEFINE_PROP_END_OF_LIST() 584 }; 585 586 static void cuda_class_init(ObjectClass *oc, void *data) 587 { 588 DeviceClass *dc = DEVICE_CLASS(oc); 589 590 dc->realize = cuda_realize; 591 dc->reset = cuda_reset; 592 dc->vmsd = &vmstate_cuda; 593 dc->props = cuda_properties; 594 set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); 595 } 596 597 static const TypeInfo cuda_type_info = { 598 .name = TYPE_CUDA, 599 .parent = TYPE_SYS_BUS_DEVICE, 600 .instance_size = sizeof(CUDAState), 601 .instance_init = cuda_init, 602 .class_init = cuda_class_init, 603 }; 604 605 static void mos6522_cuda_portB_write(MOS6522State *s) 606 { 607 MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); 608 609 cuda_update(mcs->cuda); 610 } 611 612 static void mos6522_cuda_realize(DeviceState *dev, Error **errp) 613 { 614 MOS6522State *ms = MOS6522(dev); 615 MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); 616 617 mdc->parent_realize(dev, errp); 618 619 ms->timers[0].frequency = CUDA_TIMER_FREQ; 620 ms->timers[1].frequency = (SCALE_US * 6000) / 4700; 621 } 622 623 static void mos6522_cuda_init(Object *obj) 624 { 625 MOS6522CUDAState *s = MOS6522_CUDA(obj); 626 627 object_property_add_link(obj, "cuda", TYPE_CUDA, 628 (Object **) &s->cuda, 629 qdev_prop_allow_set_link_before_realize, 630 0, NULL); 631 } 632 633 static void mos6522_cuda_class_init(ObjectClass *oc, void *data) 634 { 635 DeviceClass *dc = DEVICE_CLASS(oc); 636 MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); 637 638 dc->realize = mos6522_cuda_realize; 639 mdc->portB_write = mos6522_cuda_portB_write; 640 mdc->get_timer1_counter_value = cuda_get_counter_value; 641 mdc->get_timer2_counter_value = cuda_get_counter_value; 642 mdc->get_timer1_load_time = cuda_get_load_time; 643 mdc->get_timer2_load_time = cuda_get_load_time; 644 } 645 646 static const TypeInfo mos6522_cuda_type_info = { 647 .name = TYPE_MOS6522_CUDA, 648 .parent = TYPE_MOS6522, 649 .instance_size = sizeof(MOS6522CUDAState), 650 .instance_init = mos6522_cuda_init, 651 .class_init = mos6522_cuda_class_init, 652 }; 653 654 static void cuda_register_types(void) 655 { 656 type_register_static(&mos6522_cuda_type_info); 657 type_register_static(&cuda_type_info); 658 } 659 660 type_init(cuda_register_types) 661