1 //////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // Nestopia - NES/Famicom emulator written in C++ 4 // 5 // Copyright (C) 2003-2008 Martin Freij 6 // Copyright (C) 2021 Rupert Carmichael 7 // 8 // This file is part of Nestopia. 9 // 10 // Nestopia is free software; you can redistribute it and/or modify 11 // it under the terms of the GNU General Public License as published by 12 // the Free Software Foundation; either version 2 of the License, or 13 // (at your option) any later version. 14 // 15 // Nestopia is distributed in the hope that it will be useful, 16 // but WITHOUT ANY WARRANTY; without even the implied warranty of 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 // GNU General Public License for more details. 19 // 20 // You should have received a copy of the GNU General Public License 21 // along with Nestopia; if not, write to the Free Software 22 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 // 24 //////////////////////////////////////////////////////////////////////////////////////// 25 26 #include "../NstTimer.hpp" 27 #include "NstBoard.hpp" 28 #include "NstBoardKaiser.hpp" 29 30 namespace Nes 31 { 32 namespace Core 33 { 34 namespace Boards 35 { 36 namespace Kaiser 37 { 38 #ifdef NST_MSVC_OPTIMIZE 39 #pragma optimize("s", on) 40 #endif 41 Ks202(const Context & c)42 Ks202::Ks202(const Context& c) 43 : Board(c), irq(*c.cpu) {} 44 Reset(const bool hard)45 void Ks202::Irq::Reset(const bool hard) 46 { 47 if (hard) 48 { 49 count = 0; 50 latch = 0; 51 ctrl = 0; 52 } 53 } 54 SubReset(const bool hard)55 void Ks202::SubReset(const bool hard) 56 { 57 Map( 0x8000U, 0x8FFFU, &Ks202::Poke_8000 ); 58 Map( 0x9000U, 0x9FFFU, &Ks202::Poke_9000 ); 59 Map( 0xA000U, 0xAFFFU, &Ks202::Poke_A000 ); 60 Map( 0xB000U, 0xBFFFU, &Ks202::Poke_B000 ); 61 Map( 0xC000U, 0xCFFFU, &Ks202::Poke_C000 ); 62 Map( 0xD000U, 0xDFFFU, &Ks202::Poke_D000 ); 63 Map( 0xE000U, 0xEFFFU, &Ks202::Poke_E000 ); 64 Map( 0xF000U, 0xFFFFU, &Ks202::Poke_F000 ); 65 66 if (hard) 67 ctrl = 0; 68 69 irq.Reset( hard, hard ? false : irq.Connected() ); 70 } 71 SubReset(const bool hard)72 void Ks7010::SubReset(const bool hard) 73 { 74 prg.SwapBank<SIZE_16K>( 0x0000, 0x5 ); 75 prg.SwapBank<SIZE_16K>( 0x4000, 0x3 ); 76 77 // At the time of writing, the true mask for bankswitching is unknown 78 Map( 0x6000U, 0x7FFFU, &Ks7010::Peek_6000 ); 79 Map( 0xCAB6U, 0xCAD6U, &Ks7010::Peek_FFFC ); 80 Map( 0xEBE2U, 0xEBE3U, &Ks7010::Peek_FFFC ); 81 Map( 0xEE32U, &Ks7010::Peek_FFFC ); 82 Map( 0xFFFCU, &Ks7010::Peek_FFFC ); 83 84 reg = 0; 85 } 86 SubReset(const bool hard)87 void Ks7013b::SubReset(const bool hard) 88 { 89 prg.SwapBank<SIZE_16K>( 0x4000, 0x7 ); 90 91 Map( 0x6000U, 0x7FFFU, &Ks7013b::Poke_6000 ); 92 Map( 0x8000U, 0xFFFFU, &Ks7013b::Poke_8000 ); 93 } 94 SubReset(const bool hard)95 void Ks7016::SubReset(const bool hard) 96 { 97 reg = 8; 98 99 prg.SwapBank<SIZE_32K>( 0x0000, 0x3 ); 100 101 Map( 0x6000U, 0x7FFFU, &Ks7016::Peek_6000 ); 102 Map( 0x8000U, 0xFFFFU, &Ks7016::Poke_8000 ); 103 } 104 SubReset(const bool hard)105 void Ks7022::SubReset(const bool hard) 106 { 107 reg = 0; 108 109 if (hard) 110 prg.SwapBanks<SIZE_16K,0x0000>( 0, 0 ); 111 112 Map( 0x8000, &Ks7022::Poke_8000 ); 113 Map( 0xA000, &Ks7022::Poke_A000 ); 114 Map( 0xFFFC, &Ks7022::Peek_FFFC ); 115 } 116 SubReset(const bool hard)117 void Ks7031::SubReset(const bool hard) 118 { 119 Map( 0x6000U, 0xFFFEU, &Ks7031::Peek_6000 ); 120 Map( 0x8000U, 0xFFFFU, &Ks7031::Poke_8000 ); 121 122 regs[0] = 0; 123 regs[1] = 0; 124 regs[2] = 0; 125 regs[3] = 0; 126 } 127 SubReset(const bool hard)128 void Ks7032::SubReset(const bool hard) 129 { 130 Ks202::SubReset( hard ); 131 Map( 0x6000U, 0x7FFFU, &Ks7032::Peek_6000 ); 132 } 133 SubReset(const bool hard)134 void Ks7037::SubReset(const bool hard) 135 { 136 if (hard) 137 { 138 regNum = 0; 139 140 for (uint i = 0; i < 8; ++i) 141 regs[i] = 0; 142 } 143 144 Map( 0x6000U, 0x6FFFU, &Ks7037::Peek_6000 ); 145 Map( 0x6000U, 0x6FFFU, &Ks7037::Poke_6000 ); 146 147 Map( 0x7000U, 0x7FFFU, &Ks7037::Peek_7000 ); 148 Map( 0x8000U, 0x9FFFU, &Ks7037::Peek_8000 ); 149 150 for (uint i = 0x0000; i < 0x2000; i += 0x2) 151 { 152 Map( 0x8000 + i, &Ks7037::Poke_8000 ); 153 Map( 0x8001 + i, &Ks7037::Poke_8001 ); 154 } 155 156 Map( 0xA000U, 0xAFFFU, &Ks7037::Peek_A000 ); 157 158 Map( 0xB000U, 0xBFFFU, &Ks7037::Peek_B000 ); 159 Map( 0xB000U, 0xBFFFU, &Ks7037::Poke_B000 ); 160 161 Map( 0xC000U, 0xDFFFU, &Ks7037::Peek_C000 ); 162 Map( 0xE000U, 0xEFFFU, &Ks7037::Peek_E000 ); 163 } 164 SubReset(const bool hard)165 void Ks7057::SubReset(const bool hard) 166 { 167 prg.SwapBank<SIZE_8K>( 0x2000, 0xD ); 168 prg.SwapBank<SIZE_16K>( 0x4000, 0x7 ); 169 170 Map( 0x6000U, 0x9FFFU, &Ks7057::Peek_6000 ); 171 Map( 0x8000U, 0x9FFFU, &Ks7057::Poke_8000 ); 172 Map( 0xB000U, 0xE003U, &Ks7057::Poke_B000 ); 173 174 if (hard) 175 { 176 for (uint i = 0; i < 8; ++i) 177 regs[i] = 0; 178 } 179 } 180 SubReset(bool)181 void Ks7058::SubReset(bool) 182 { 183 for (uint i=0x000; i < 0x1000; i += 0x100) 184 { 185 Map( 0xF000+i, 0xF07F+i, CHR_SWAP_4K_0 ); 186 Map( 0xF080+i, 0xF0FF+i, CHR_SWAP_4K_1 ); 187 } 188 } 189 SubLoad(State::Loader & state,const dword baseChunk)190 void Ks202::SubLoad(State::Loader& state,const dword baseChunk) 191 { 192 NST_VERIFY( (baseChunk == AsciiId<'K','0','2'>::V) ); 193 194 if (baseChunk == AsciiId<'K','0','2'>::V) 195 { 196 while (const dword chunk = state.Begin()) 197 { 198 switch (chunk) 199 { 200 case AsciiId<'R','E','G'>::V: 201 202 ctrl = state.Read8(); 203 break; 204 205 case AsciiId<'I','R','Q'>::V: 206 { 207 State::Loader::Data<5> data( state ); 208 209 irq.unit.ctrl = data[0]; 210 irq.unit.count = data[1] | data[2] << 8; 211 irq.unit.latch = data[3] | data[4] << 8; 212 irq.Connect( data[0] & 0xF ); 213 214 break; 215 } 216 } 217 218 state.End(); 219 } 220 } 221 } 222 SubLoad(State::Loader & state,const dword baseChunk)223 void Ks7010::SubLoad(State::Loader& state,const dword baseChunk) 224 { 225 NST_VERIFY( (baseChunk == AsciiId<'K','7','0'>::V) ); 226 227 if (baseChunk == AsciiId<'K','7','0'>::V) 228 { 229 while (const dword chunk = state.Begin()) 230 { 231 if (chunk == AsciiId<'R','E','G'>::V) 232 reg = state.Read8(); 233 234 state.End(); 235 } 236 } 237 } 238 SubLoad(State::Loader & state,const dword baseChunk)239 void Ks7016::SubLoad(State::Loader& state,const dword baseChunk) 240 { 241 NST_VERIFY( (baseChunk == AsciiId<'K','7','6'>::V) ); 242 243 if (baseChunk == AsciiId<'K','7','6'>::V) 244 { 245 while (const dword chunk = state.Begin()) 246 { 247 if (chunk == AsciiId<'R','E','G'>::V) 248 reg = state.Read8(); 249 250 state.End(); 251 } 252 } 253 } 254 SubLoad(State::Loader & state,const dword baseChunk)255 void Ks7022::SubLoad(State::Loader& state,const dword baseChunk) 256 { 257 NST_VERIFY( (baseChunk == AsciiId<'K','7','2'>::V) ); 258 259 if (baseChunk == AsciiId<'K','7','2'>::V) 260 { 261 while (const dword chunk = state.Begin()) 262 { 263 if (chunk == AsciiId<'R','E','G'>::V) 264 reg = state.Read8(); 265 266 state.End(); 267 } 268 } 269 } 270 SubLoad(State::Loader & state,const dword baseChunk)271 void Ks7031::SubLoad(State::Loader& state,const dword baseChunk) 272 { 273 NST_VERIFY( (baseChunk == AsciiId<'K','7','1'>::V) ); 274 275 if (baseChunk == AsciiId<'K','7','1'>::V) 276 { 277 while (const dword chunk = state.Begin()) 278 { 279 if (chunk == AsciiId<'R','E','G'>::V) 280 { 281 State::Loader::Data<4> data( state ); 282 283 regs[0] = data[0]; 284 regs[1] = data[1]; 285 regs[2] = data[2]; 286 regs[3] = data[3]; 287 } 288 289 state.End(); 290 } 291 } 292 } 293 SubLoad(State::Loader & state,const dword baseChunk)294 void Ks7037::SubLoad(State::Loader& state,const dword baseChunk) 295 { 296 NST_VERIFY( (baseChunk == AsciiId<'K','7','7'>::V) ); 297 298 if (baseChunk == AsciiId<'K','7','7'>::V) 299 { 300 while (const dword chunk = state.Begin()) 301 { 302 if (chunk == AsciiId<'R','E','G'>::V) 303 { 304 State::Loader::Data<9> data( state ); 305 306 regs[0] = data[0]; 307 regs[1] = data[1]; 308 regs[2] = data[2]; 309 regs[3] = data[3]; 310 regs[4] = data[4]; 311 regs[5] = data[5]; 312 regs[6] = data[6]; 313 regs[7] = data[7]; 314 regNum = data[8]; 315 } 316 317 state.End(); 318 } 319 } 320 } 321 SubLoad(State::Loader & state,const dword baseChunk)322 void Ks7057::SubLoad(State::Loader& state,const dword baseChunk) 323 { 324 NST_VERIFY( (baseChunk == AsciiId<'K','5','7'>::V) ); 325 326 if (baseChunk == AsciiId<'K','5','7'>::V) 327 { 328 while (const dword chunk = state.Begin()) 329 { 330 if (chunk == AsciiId<'R','E','G'>::V) 331 { 332 State::Loader::Data<8> data( state ); 333 334 regs[0] = data[0]; 335 regs[1] = data[1]; 336 regs[2] = data[2]; 337 regs[3] = data[3]; 338 regs[4] = data[4]; 339 regs[5] = data[5]; 340 regs[6] = data[6]; 341 regs[7] = data[7]; 342 } 343 344 state.End(); 345 } 346 } 347 } 348 SubSave(State::Saver & state) const349 void Ks202::SubSave(State::Saver& state) const 350 { 351 state.Begin( AsciiId<'K','0','2'>::V ); 352 state.Begin( AsciiId<'R','E','G'>::V ).Write8( ctrl ).End(); 353 354 const byte data[5] = 355 { 356 irq.unit.ctrl, 357 irq.unit.count & 0xFF, 358 irq.unit.count >> 8, 359 irq.unit.latch & 0xFF, 360 irq.unit.latch >> 8 361 }; 362 363 state.Begin( AsciiId<'I','R','Q'>::V ).Write( data ).End(); 364 state.End(); 365 } 366 SubSave(State::Saver & state) const367 void Ks7010::SubSave(State::Saver& state) const 368 { 369 state.Begin( AsciiId<'K','7','0'>::V ).Begin( AsciiId<'R','E','G'>::V ).Write8( reg ).End().End(); 370 } 371 SubSave(State::Saver & state) const372 void Ks7016::SubSave(State::Saver& state) const 373 { 374 state.Begin( AsciiId<'K','7','6'>::V ).Begin( AsciiId<'R','E','G'>::V ).Write8( reg ).End().End(); 375 } 376 SubSave(State::Saver & state) const377 void Ks7022::SubSave(State::Saver& state) const 378 { 379 state.Begin( AsciiId<'K','7','2'>::V ).Begin( AsciiId<'R','E','G'>::V ).Write8( reg ).End().End(); 380 } 381 SubSave(State::Saver & state) const382 void Ks7031::SubSave(State::Saver& state) const 383 { 384 state.Begin( AsciiId<'K','7','1'>::V ); 385 386 state.Begin( AsciiId<'R','E','G'>::V ).Write( regs ).End(); 387 state.End(); 388 } 389 SubSave(State::Saver & state) const390 void Ks7037::SubSave(State::Saver& state) const 391 { 392 state.Begin( AsciiId<'K','7','7'>::V ); 393 394 const byte data[9] = 395 { 396 regs[0], regs[1], regs[2], regs[3], 397 regs[4], regs[5], regs[6], regs[7], 398 regNum 399 }; 400 401 state.Begin( AsciiId<'R','E','G'>::V ).Write( data ).End(); 402 state.End(); 403 } 404 SubSave(State::Saver & state) const405 void Ks7057::SubSave(State::Saver& state) const 406 { 407 state.Begin( AsciiId<'K','5','7'>::V ); 408 409 const byte data[8] = 410 { 411 regs[0], regs[1], regs[2], regs[3], 412 regs[4], regs[5], regs[6], regs[7] 413 }; 414 415 state.Begin( AsciiId<'R','E','G'>::V ).Write( data ).End(); 416 state.End(); 417 } 418 419 #ifdef NST_MSVC_OPTIMIZE 420 #pragma optimize("", on) 421 #endif 422 423 NES_POKE_D(Ks202,8000) 424 { 425 irq.Update(); 426 irq.unit.latch = (irq.unit.latch & 0xFFF0) | (data & 0xF) << 0; 427 } 428 429 NES_POKE_D(Ks202,9000) 430 { 431 irq.Update(); 432 irq.unit.latch = (irq.unit.latch & 0xFF0F) | (data & 0xF) << 4; 433 } 434 NES_POKE_D(Ks202,A000)435 NES_POKE_D(Ks202,A000) 436 { 437 irq.Update(); 438 irq.unit.latch = (irq.unit.latch & 0xF0FF) | (data & 0xF) << 8; 439 } 440 NES_POKE_D(Ks202,B000)441 NES_POKE_D(Ks202,B000) 442 { 443 irq.Update(); 444 irq.unit.latch = (irq.unit.latch & 0x0FFF) | (data & 0xF) << 12; 445 } 446 NES_POKE_D(Ks202,C000)447 NES_POKE_D(Ks202,C000) 448 { 449 irq.Update(); 450 451 irq.unit.ctrl = data; 452 453 if (irq.Connect( data & 0xF )) 454 irq.unit.count = irq.unit.latch; 455 456 irq.ClearIRQ(); 457 } 458 NES_POKE(Ks202,D000)459 NES_POKE(Ks202,D000) 460 { 461 irq.Update(); 462 irq.ClearIRQ(); 463 } 464 NES_POKE_D(Ks202,E000)465 NES_POKE_D(Ks202,E000) 466 { 467 ctrl = data; 468 } 469 NES_POKE_AD(Ks202,F000)470 NES_POKE_AD(Ks202,F000) 471 { 472 { 473 uint offset = (ctrl & 0xF) - 1; 474 475 if (offset < 3) 476 { 477 offset <<= 13; 478 prg.SwapBank<SIZE_8K>( offset, (data & 0x0F) | (prg.GetBank<SIZE_8K>(offset) & 0x10) ); 479 } 480 else if (offset < 4) 481 { 482 wrk.SwapBank<SIZE_8K,0x0000>( data ); 483 } 484 } 485 486 switch (address & 0xC00) 487 { 488 case 0x000: 489 490 address &= 0x3; 491 492 if (address < 3) 493 { 494 address <<= 13; 495 prg.SwapBank<SIZE_8K>( address, (prg.GetBank<SIZE_8K>(address) & 0x0F) | (data & 0x10) ); 496 } 497 break; 498 499 case 0x800: 500 501 ppu.SetMirroring( (data & 0x1) ? Ppu::NMT_V : Ppu::NMT_H ); 502 break; 503 504 case 0xC00: 505 506 ppu.Update(); 507 chr.SwapBank<SIZE_1K>( (address & 0x7) << 10, data ); 508 break; 509 } 510 } 511 Clock()512 bool Ks202::Irq::Clock() 513 { 514 return (count++ == 0xFFFF) ? (count=latch, true) : false; 515 } 516 Sync(Event event,Input::Controllers * controllers)517 void Ks202::Sync(Event event,Input::Controllers* controllers) 518 { 519 if (event == EVENT_END_FRAME) 520 irq.VSync(); 521 522 Board::Sync( event, controllers ); 523 } 524 525 NES_PEEK_A(Ks7010,6000) 526 { 527 return *(prg.Source().Mem(reg * SIZE_8K) + (address & 0x1FFF)); 528 } 529 NES_PEEK_A(Ks7010,FFFC)530 NES_PEEK_A(Ks7010,FFFC) 531 { 532 reg = (address >> 2) & 0xF; 533 chr.SwapBank<SIZE_8K,0x0000>( reg ); 534 ppu.Update(); 535 536 return prg.Peek(address & 0x7FFF); 537 } 538 539 NES_POKE_D(Ks7013b,6000) 540 { 541 prg.SwapBank<SIZE_16K>( 0x0000, data & 0x7 ); 542 } 543 544 NES_POKE_D(Ks7013b,8000) 545 { 546 ppu.SetMirroring( (data & 0x1) ? Ppu::NMT_H : Ppu::NMT_V ); 547 } 548 549 NES_PEEK_A(Ks7016,6000) 550 { 551 return *(prg.Source().Mem(reg * SIZE_8K) + (address & 0x1FFF)); 552 } 553 554 NES_POKE_A(Ks7016,8000) 555 { 556 bool mode = (address & 0x30) == 0x30; 557 558 switch(address & 0xD943) { 559 case 0xD943: 560 reg = mode ? 0xB : (address >> 2) & 0xF; 561 break; 562 563 case 0xD903: 564 reg = mode ? 0x8 | ((address >> 2) & 0x3) : reg = 0xB; 565 break; 566 } 567 } 568 569 NES_POKE_D(Ks7022,8000) 570 { 571 ppu.SetMirroring( (data & 0x4) ? Ppu::NMT_H : Ppu::NMT_V ); 572 } 573 NES_POKE_D(Ks7022,A000)574 NES_POKE_D(Ks7022,A000) 575 { 576 reg = data & 0xF; 577 } 578 NES_PEEK(Ks7022,FFFC)579 NES_PEEK(Ks7022,FFFC) 580 { 581 ppu.Update(); 582 chr.SwapBank<SIZE_8K,0x0000>( reg ); 583 prg.SwapBanks<SIZE_16K,0x0000>( reg, reg ); 584 585 return prg.Peek(0x7FFC); 586 } 587 588 NES_POKE_AD(Ks7031,8000) 589 { 590 regs[(address >> 11) & 0x03] = data; 591 } 592 593 NES_PEEK_A(Ks7031,6000) 594 { 595 int bank, new_addr; 596 597 if (address < 0x8000) 598 bank = regs[(address >> 11) & 0x03]; 599 else 600 bank = 0x0f - ((address >> 11) & 0x0f); 601 602 new_addr = ((bank << 11) % prg.Source(0).Size()) | (address & 0x07ff); 603 604 return prg[0][new_addr]; 605 } 606 607 NES_PEEK_A(Ks7032,6000) 608 { 609 return wrk[0][address - 0x6000]; 610 } 611 612 NES_PEEK_A(Ks7037,6000) 613 { 614 NST_VERIFY( wrk.Readable(0) ); 615 return wrk.Readable(0) ? wrk[0][address - 0x6000] : (address >> 8); 616 } 617 618 NES_POKE_AD(Ks7037,6000) 619 { 620 NST_VERIFY( wrk.Writable(0) ); 621 622 if (wrk.Writable(0)) 623 wrk[0][address- 0x6000] = data; 624 } 625 626 NES_PEEK_A(Ks7037,7000) 627 { 628 return *(prg.Source().Mem(SIZE_4K * 15) + (address & 0xFFF)); 629 } 630 631 NES_PEEK_A(Ks7037,8000) 632 { 633 return *(prg.Source().Mem(regs[6] * SIZE_8K) + (address & 0x1FFF)); 634 } 635 636 NES_POKE_D(Ks7037,8000) 637 { 638 regNum = data & 0x7U; 639 byte mirror[4] = { regs[2], regs[4], regs[3], regs[5] }; 640 ppu.SetMirroring(mirror); 641 } 642 643 NES_POKE_D(Ks7037,8001) 644 { 645 regs[regNum] = data; 646 } 647 NES_PEEK_A(Ks7037,A000)648 NES_PEEK_A(Ks7037,A000) 649 { 650 return *(prg.Source().Mem(SIZE_4K * 28) + (address & 0xFFF)); 651 } 652 NES_PEEK_A(Ks7037,B000)653 NES_PEEK_A(Ks7037,B000) 654 { 655 NST_VERIFY( wrk.Readable(0) ); 656 return wrk.Readable(0) ? wrk[0][address - 0xA000] : (address >> 8); 657 } 658 NES_POKE_AD(Ks7037,B000)659 NES_POKE_AD(Ks7037,B000) 660 { 661 NST_VERIFY( wrk.Writable(0) ); 662 663 if (wrk.Writable(0)) 664 wrk[0][address- 0xA000] = data; 665 } 666 NES_PEEK_A(Ks7037,C000)667 NES_PEEK_A(Ks7037,C000) 668 { 669 return *(prg.Source().Mem(regs[7] * SIZE_8K) + (address & 0x1FFF)); 670 } 671 NES_PEEK_A(Ks7037,E000)672 NES_PEEK_A(Ks7037,E000) 673 { 674 return *(prg.Source().Mem(SIZE_8K * 15) + (address & 0x1FFF)); 675 } 676 677 NES_PEEK_A(Ks7057,6000) 678 { 679 return *(prg.Source().Mem(regs[(address >> 11) - 0xC] * SIZE_2K) + (address & 0x7FF)); 680 } 681 682 NES_POKE_D(Ks7057,8000) 683 { 684 ppu.SetMirroring( (data & 0x1) ? Ppu::NMT_V : Ppu::NMT_H ); 685 } 686 NES_POKE_AD(Ks7057,B000)687 NES_POKE_AD(Ks7057,B000) 688 { 689 switch(address & 0xF003) 690 { 691 case 0xB000: regs[4] = (regs[4] & 0xF0) | (data & 0xF); break; 692 case 0xB001: regs[4] = (regs[4] & 0xF) | (data << 4); break; 693 case 0xB002: regs[5] = (regs[5] & 0xF0) | (data & 0xF); break; 694 case 0xB003: regs[5] = (regs[5] & 0xF) | (data << 4); break; 695 case 0xC000: regs[6] = (regs[6] & 0xF0) | (data & 0xF); break; 696 case 0xC001: regs[6] = (regs[6] & 0xF) | (data << 4); break; 697 case 0xC002: regs[7] = (regs[7] & 0xF0) | (data & 0xF); break; 698 case 0xC003: regs[7] = (regs[7] & 0xF) | (data << 4); break; 699 case 0xD000: regs[0] = (regs[0] & 0xF0) | (data & 0xF); break; 700 case 0xD001: regs[0] = (regs[0] & 0xF) | (data << 4); break; 701 case 0xD002: regs[1] = (regs[1] & 0xF0) | (data & 0xF); break; 702 case 0xD003: regs[1] = (regs[1] & 0xF) | (data << 4); break; 703 case 0xE000: regs[2] = (regs[2] & 0xF0) | (data & 0xF); break; 704 case 0xE001: regs[2] = (regs[2] & 0xF) | (data << 4); break; 705 case 0xE002: regs[3] = (regs[3] & 0xF0) | (data & 0xF); break; 706 case 0xE003: regs[3] = (regs[3] & 0xF) | (data << 4); break; 707 } 708 } 709 } 710 } 711 } 712 } 713