// license:BSD-3-Clause // copyright-holders:Curt Coder #include "p2000t_cas.h" #include "coretmpl.h" // BIT #include // This code will reproduce the timing of a P2000 mini cassette tape. constexpr double P2000_CLOCK_PERIOD = 0.000084; constexpr double P2000_BOT_GAP = 1; constexpr double P2000_BOB_GAP = 0.515; constexpr double P2000_MARK_GAP = 0.085; constexpr double P2000_END_GAP = 0.155; constexpr double P2000_EOT_GAP = 1.8; constexpr int P2000_HIGH = 0x7FFFFFFF; constexpr int P2000_LOW = -1 * 0x7FFFFFFF; #define CHR(x) \ err = (x); \ if (err != cassette_image::error::SUCCESS) \ return err; /* Here's a description on how the P2000t stores data on tape. ## Tape Format Each track (or side) of a cassette is divided into 40 block of information. A file may comprise between 1 and 40 blocks, depending on its length. ## Blocks, Gaps, Marks and Records At the start of the tape is an area of clear (erased) tape, the BOT (beginning of tape) gap; to read pas this gap takes approximately 1 second. After this, the first block starts, followed directly by the second, third and so on. After the last block on the track comes the EOT (end of tape) gap; if all 40 blocks on the tracks are used, this area of erased tape has a length equivalent to 1.8 seconds of reading time. | BOT | BLOCK 1 | BLOCK 2 | BLOCK ... | EOT | ### BLOCK Each tape block is made up of five sections of tape: | START GAP | MARK | MARK GAP | DATA RECORD | END GAP | - Start gap: A section of erased tape separating the start of one block from the end of the previous block. This takes approx. 515ms to read over. - Mark: Four bytes of recorded information (described below) - Mark Gap: A section of erased tape separating the mark from the data record; length about 85ms. - Data Record: 1056 bytes of recorded data (described below) - End Gap: A section of erased tape of around 155 ms. ### MARK The mark is made up of four bytes with the following bit patterns: - preamble syncronization pattern (0xAA) - 0000 0000 (0x00) - 0000 0000 (0x00) - postamble syncronization pattern (0xAA) The function of the synchronisation byte is to make sure the RDC clock is set properly. ### DATA RECORD The data record contains the information which has been written onto the tape. It compromises five sections: | sync byte | header | data | check sum | post sync | - sync byte: Preamble synchronisation pattern 0xAA - header: 32 bytes which specify the contents and type of the data section. (See below) - data section: 1024 bytes of information. - checksum: 2 bytes that contain checksum - post sync: 0xAA ### HEADER See the struct declaration below for details on what is contained. ### DATA The data section consists of 1024 bytes written in serial form, least significant byte first. ### Checksum The checksum is only calculated for the header and data section. The algorithm is not documented, an implementation can be found below. */ // Specifies the type of data stored in the tape block enum P2000_File_Type : uint8_t { Basic = 'B', Program = 'P', Viewdata = 'V', WordProcessing = 'W', Other = 'O', }; // Represents the internal code for data, differrent codes are used in different countries // only relevant when P2000_File_Type is Program. enum P2000_Data_Type : uint8_t { German = 'D', Swedish = 'S', Dutch_English = 'U', }; // This is the 32 byte header definition used by the P2000, it is mainly // here for documentation. struct P2000T_Header { // Starting address in ram where data should go. This address is supplied by // the application program when calling the monitor cassette routine to write // the data on cassette in the first place. uint16_t data_transfer_address; // Total # of bytes which make up the file (can be spread over many blocks). // The monitor uses this to determine how many blocks to read. uint16_t file_length; // # bytes in this record that are actually used. For example if this is 256 // only 256 bytes will be loaded in ram uint16_t data_section_length; // The eight character file name identifies the file to which the record // belongs; it will be the same in all records making up the file. Each record // except the first is considered an extension. char file_name[8]; // Addition file extension. char ext[3]; // This file type specifies the type of data stored. P2000_File_Type file_type; // Code and region information. P2000_Data_Type data_code; // Start address where program should start. (if type = Program) uint16_t start_addr; // Address in ram where the program should load (if type = Program) uint16_t load_addr; // Unused. char reserved[8]; // Record number (i.e. which block) uint8_t rec_nr; }; std::ostream &operator<<(std::ostream &os, P2000T_Header const &hdr) { return os << "File: " << std::string(hdr.file_name, 8) << '.' << std::string(hdr.ext, 3) << " " << hdr.file_length; } static cassette_image::error p2000t_cas_identify(cassette_image *cass, cassette_image::Options *opts) { opts->bits_per_sample = 32; opts->channels = 1; opts->sample_frequency = 44100; return cassette_image::error::SUCCESS; } uint16_t rotr16a(uint16_t x, uint16_t n) { return (x >> n) | (x << (16 - n)); } void update_chksum(uint16_t *de, bool bit) { // Reverse engineered from monitor.rom // code is at: [0x07ac, 0x07c5] uint8_t e = *de & 0xff; uint8_t d = (*de >> 8) & 0xff; e = e ^ (bit ? 1 : 0); if (e & 0x01) { e = e ^ 2; d = d ^ 0x40; } else { d = d ^ 0x00; } *de = rotr16a((d << 8) | e, 1); } /* A transition on a clock boundary from low to high is a 1. A transition on a clock boundary from high to low is a 0 An intermediate transition halfway between the clock boundary can occur when there are consecutive 0s or 1s. See the example below where the clock is marked by a | 1 0 1 1 0 0 RDA: _|----|____|--__|----|__--|__-- RDC: _|-___|-___|-___|-___|-___|-___ ^ ^ |-- clock signal |-- intermediate transition. This signal can be written by a simple algorithm where the first bit is always false (transition to low, half clock). Now only one bit is needed to determine what the next partial clock should look like. This works because we are always guaranteed that a block starts with 0xAA, and hence will ALWAYS find a signal like this on tape: _-- (low, high, high) after a gap. This is guaranteed when the tape is moving forward as well as backwards. */ cassette_image::error p2000t_put_bit(cassette_image *cass, double *time_index, bool bit) { const int channel = 0; cassette_image::error err = cassette_image::error::SUCCESS; CHR(cass->put_sample(channel, *time_index, P2000_CLOCK_PERIOD, bit ? P2000_HIGH : P2000_LOW)); *time_index += P2000_CLOCK_PERIOD; CHR(cass->put_sample(channel, *time_index, P2000_CLOCK_PERIOD, bit ? P2000_LOW : P2000_HIGH)); *time_index += P2000_CLOCK_PERIOD; return err; } // Store byte of data, updating the checksum cassette_image::error p2000t_put_byte(cassette_image *cass, double *time_index, uint16_t *chksum, uint8_t byte) { cassette_image::error err = cassette_image::error::SUCCESS; for (int i = 0; i < 8 && err == cassette_image::error::SUCCESS; i++) { update_chksum(chksum, util::BIT(byte, i)); CHR(p2000t_put_bit(cass, time_index, util::BIT(byte, i))); } return err; } // Store a sequence of bytes, updating the checksum cassette_image::error p2000t_put_bytes(cassette_image *cass, double *time_index, uint16_t *chksum, const uint8_t *bytes, const uint16_t cByte) { cassette_image::error err = cassette_image::error::SUCCESS; for (int i = 0; i < cByte && err == cassette_image::error::SUCCESS; i++) { CHR(p2000t_put_byte(cass, time_index, chksum, bytes[i])); } return err; } // Insert time seconds of silence. cassette_image::error p2000t_silence(cassette_image *cassette, double *time_index, double time) { auto err = cassette->put_sample(0, *time_index, time, 0); *time_index += time; return err; } static cassette_image::error p2000t_cas_load(cassette_image *cassette) { cassette_image::error err = cassette_image::error::SUCCESS; uint64_t image_size = cassette->image_size(); constexpr int CAS_BLOCK = 1280; /* The cas format is pretty simple. it consists of a sequence of blocks, where a block consists of the following: [0-256] P2000 memory address 0x6000 - 0x6100 .... Nonsense (keyboard status etc.) 0x30 P200T_Header 0x50 ... Nonsense.. [0-1024] Data block This means that one block gets stored in 1280 bytes. */ if (image_size % CAS_BLOCK != 0) { return cassette_image::error::INVALID_IMAGE; } uint8_t block[CAS_BLOCK]; constexpr uint8_t BLOCK_MARK[4] = { 0xAA, 0x00, 0x00, 0xAA }; auto blocks = image_size / CAS_BLOCK; double time_idx = 0; // Beginning of tape marker CHR(p2000t_silence(cassette, &time_idx, P2000_BOT_GAP)); for (int i = 0; i < blocks; i++) { uint16_t crc = 0, unused = 0; cassette->image_read(&block, CAS_BLOCK * i, CAS_BLOCK); // Insert sync header.. 0xAA, 0x00, 0x00, 0xAA CHR(p2000t_silence(cassette, &time_idx, P2000_BOB_GAP)); CHR(p2000t_put_bytes(cassette, &time_idx, &unused, BLOCK_MARK, std::size(BLOCK_MARK))); CHR(p2000t_silence(cassette, &time_idx, P2000_MARK_GAP)); // Insert data block CHR(p2000t_put_byte(cassette, &time_idx, &unused, 0xAA)); CHR(p2000t_put_bytes(cassette, &time_idx, &crc, block + 0x30, 32)); CHR(p2000t_put_bytes(cassette, &time_idx, &crc, block + 256, 1024)); CHR(p2000t_put_bytes(cassette, &time_idx, &unused, ( uint8_t * )&crc, 2)); CHR(p2000t_put_byte(cassette, &time_idx, &unused, 0xAA)); // Block finished. CHR(p2000t_silence(cassette, &time_idx, P2000_END_GAP)); } // End of tape marker return p2000t_silence(cassette, &time_idx, P2000_EOT_GAP); } static const cassette_image::Format p2000t_cas = { "cas", p2000t_cas_identify, p2000t_cas_load, nullptr /* no save */ }; CASSETTE_FORMATLIST_START(p2000t_cassette_formats) CASSETTE_FORMAT(p2000t_cas) CASSETTE_FORMATLIST_END n160'>160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
/***************************************************************************

  video.c

  Functions to emulate the video hardware of the machine.

***************************************************************************/

#include "driver.h"
#include "video/resnet.h"
#include "includes/cclimber.h"


UINT8 *cclimber_bsvideoram;
size_t cclimber_bsvideoram_size;
UINT8 *cclimber_bigspriteram;
UINT8 *cclimber_column_scroll;
UINT8 *swimmer_bgcolor;
UINT8 *swimmer_sidepanel_enabled;
UINT8 *swimmer_palettebank;


/***************************************************************************

  Convert the color PROMs into a more useable format.

  Crazy Climber has three 32x8 palette PROMs.
  The palette PROMs are connected to the RGB output this way:

  bit 7 -- 220 ohm resistor  -- BLUE
        -- 470 ohm resistor  -- BLUE
        -- 220 ohm resistor  -- GREEN
        -- 470 ohm resistor  -- GREEN
        -- 1  kohm resistor  -- GREEN
        -- 220 ohm resistor  -- RED
        -- 470 ohm resistor  -- RED
  bit 0 -- 1  kohm resistor  -- RED

***************************************************************************/
PALETTE_INIT( cclimber )
{
	static const int resistances_rg[3] = { 1000, 470, 220 };
	static const int resistances_b [2] = { 470, 220 };
	double rweights[3], gweights[3], bweights[2];
	int i;

	/* compute the color output resistor weights */
	compute_resistor_weights(0,	255, -1.0,
			3, &resistances_rg[0], rweights, 0, 0,
			3, &resistances_rg[0], gweights, 0, 0,
			2, &resistances_b[0],  bweights, 0, 0);

	for (i = 0;i < machine->config->total_colors; i++)
	{
		UINT8 data;
		int bit0, bit1, bit2;
		int r, g, b;

		if (i & 0x07)
			data = color_prom[i];
		else
			/* River Patrol shows that background is pen 0 */
			data = color_prom[0];

		/* red component */
		bit0 = (data >> 0) & 0x01;
		bit1 = (data >> 1) & 0x01;
		bit2 = (data >> 2) & 0x01;
		r = combine_3_weights(rweights, bit0, bit1, bit2);

		/* green component */
		bit0 = (data >> 3) & 0x01;
		bit1 = (data >> 4) & 0x01;
		bit2 = (data >> 5) & 0x01;
		g = combine_3_weights(gweights, bit0, bit1, bit2);

		/* blue component */
		bit0 = (data >> 6) & 0x01;
		bit1 = (data >> 7) & 0x01;
		b = combine_2_weights(bweights, bit0, bit1);

		palette_set_color(machine, i, MAKE_RGB(r, g, b));
	}
}


/***************************************************************************

  Convert the color PROMs into a more useable format.

  Swimmer has two 256x4 char/sprite palette PROMs and one 32x8 big sprite
  palette PROM.
  The palette PROMs are connected to the RGB output this way:
  (the 500 and 250 ohm resistors are made of 1 kohm resistors in parallel)

  bit 3 -- 250 ohm resistor  -- BLUE
        -- 500 ohm resistor  -- BLUE
        -- 250 ohm resistor  -- GREEN
  bit 0 -- 500 ohm resistor  -- GREEN
  bit 3 -- 1  kohm resistor  -- GREEN
        -- 250 ohm resistor  -- RED
        -- 500 ohm resistor  -- RED
  bit 0 -- 1  kohm resistor  -- RED

  bit 7 -- 250 ohm resistor  -- BLUE
        -- 500 ohm resistor  -- BLUE
        -- 250 ohm resistor  -- GREEN
        -- 500 ohm resistor  -- GREEN
        -- 1  kohm resistor  -- GREEN
        -- 250 ohm resistor  -- RED
        -- 500 ohm resistor  -- RED
  bit 0 -- 1  kohm resistor  -- RED

  Additionally, the background color of the score panel is determined by
  these resistors:

                  /--- tri-state --  470 -- BLUE
  +5V -- 1kohm ------- tri-state --  390 -- GREEN
                  \--- tri-state -- 1000 -- RED

***************************************************************************/

PALETTE_INIT( swimmer )
{
	int i;

	for (i = 0; i < 0x200; i++)
	{
		rgb_t color;

		/* non-backgrond pens */
		if (i & 0x07)
		{
			int bit0, bit1, bit2;
			int r, g, b;

			/* red component */
			bit0 = (color_prom[(i & 0xff) + 0x000] >> 0) & 0x01;
			bit1 = (color_prom[(i & 0xff) + 0x000] >> 1) & 0x01;
			bit2 = (color_prom[(i & 0xff) + 0x000] >> 2) & 0x01;
			r = 0x20 * bit0 + 0x40 * bit1 + 0x80 * bit2;

			/* green component */
			bit0 = (color_prom[(i & 0xff) + 0x000] >> 3) & 0x01;
			bit1 = (color_prom[(i & 0xff) + 0x100] >> 0) & 0x01;
			bit2 = (color_prom[(i & 0xff) + 0x100] >> 1) & 0x01;
			g = 0x20 * bit0 + 0x40 * bit1 + 0x80 * bit2;

			/* blue component */
			bit0 = 0;
			bit1 = (color_prom[(i & 0xff) + 0x100] >> 2) & 0x01;
			bit2 = (color_prom[(i & 0xff) + 0x100] >> 3) & 0x01;
			b = 0x20 * bit0 + 0x40 * bit1 + 0x80 * bit2;

			color = MAKE_RGB(r,g,b);
		}

		/* backgrond pen */
		else
		{
			if (i & 0x100)
				/* side panel */
#if 0
				// values calculated from the resistors don't seem to match the real board
				color = MAKE_RGB(0x24, 0x5d, 0x4e);
#endif
				color = MAKE_RGB(0x20, 0x98, 0x79);
			else
				/* 'water' background -- will be modified dynamically later */
				color = RGB_BLACK;
		}

		palette_set_color(machine, i, color);
	}

	color_prom += 0x200;

	/* big sprite */
	for (i = 0; i < 0x20; i++)
	{
		int bit0, bit1, bit2;
		int r, g, b;

		/* red component */
		bit0 = (color_prom[i] >> 0) & 0x01;
		bit1 = (color_prom[i] >> 1) & 0x01;
		bit2 = (color_prom[i] >> 2) & 0x01;
		r = 0x20 * bit0 + 0x40 * bit1 + 0x80 * bit2;

		/* green component */
		bit0 = (color_prom[i] >> 3) & 0x01;
		bit1 = (color_prom[i] >> 4) & 0x01;
		bit2 = (color_prom[i] >> 5) & 0x01;
		g = 0x20 * bit0 + 0x40 * bit1 + 0x80 * bit2;

		/* blue component */
		bit0 = 0;
		bit1 = (color_prom[i] >> 6) & 0x01;
		bit2 = (color_prom[i] >> 7) & 0x01;
		b = 0x20 * bit0 + 0x40 * bit1 + 0x80 * bit2;

		palette_set_color(machine, i + 0x200, MAKE_RGB(r, g, b));
	}
}



/***************************************************************************

  Swimmer can directly set the background color.
  The latch is connected to the RGB output this way:
  (the 500 and 250 ohm resistors are made of 1 kohm resistors in parallel)

  bit 7 -- 250 ohm resistor  -- RED
        -- 500 ohm resistor  -- RED
        -- 250 ohm resistor  -- GREEN
        -- 500 ohm resistor  -- GREEN
        -- 1  kohm resistor  -- GREEN
        -- 250 ohm resistor  -- BLUE
        -- 500 ohm resistor  -- BLUE
  bit 0 -- 1  kohm resistor  -- BLUE

***************************************************************************/

static void swimmer_set_background_pen(running_machine *machine)
{
	int i;
	pen_t color;
	int bit0, bit1, bit2;
	int r, g, b;

	/* red component */
	bit0 = 0;
	bit1 = (*swimmer_bgcolor >> 6) & 0x01;
	bit2 = (*swimmer_bgcolor >> 7) & 0x01;
	r = 0x20 * bit0 + 0x40 * bit1 + 0x80 * bit2;

	/* green component */
	bit0 = (*swimmer_bgcolor >> 3) & 0x01;
	bit1 = (*swimmer_bgcolor >> 4) & 0x01;
	bit2 = (*swimmer_bgcolor >> 5) & 0x01;
	g = 0x20 * bit0 + 0x40 * bit1 + 0x80 * bit2;

	/* blue component */
	bit0 = (*swimmer_bgcolor >> 0) & 0x01;
	bit1 = (*swimmer_bgcolor >> 1) & 0x01;
	bit2 = (*swimmer_bgcolor >> 2) & 0x01;
	b = 0x20 * bit0 + 0x40 * bit1 + 0x80 * bit2;

	color = MAKE_RGB(r, g, b);

	/* set pen 0 if each color code */
	for (i = 0; i < 0x100; i += 8)
		palette_set_color(machine, i, color);
}



WRITE8_HANDLER( cclimber_colorram_w )
{
	/* bit 5 of the address is not used for color memory. There is just */
	/* 512 bytes of memory; every two consecutive rows share the same memory */
	/* region. */
	colorram[offset] = data;
	colorram[offset ^ 0x20] = data;
}



static void cclimber_draw_big_sprite(running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect)
{
	int offs;
	int ox,oy,sx,sy,flipx,flipy;
	int color;


	ox = 136 - cclimber_bigspriteram[3];
	oy = 128 - cclimber_bigspriteram[2];
	flipx = cclimber_bigspriteram[1] & 0x10;
	flipy = cclimber_bigspriteram[1] & 0x20;
	if (flip_screen_y)      /* only the Y direction has to be flipped */
	{
		oy = 128 - oy;
		flipy = !flipy;
	}
	color = cclimber_bigspriteram[1] & 0x07;	/* cclimber */
//  color = cclimber_bigspriteram[1] & 0x03;    /* swimmer */

	for (offs = cclimber_bsvideoram_size - 1;offs >= 0;offs--)
	{
		sx = offs % 16;
		sy = offs / 16;
		if (flipx) sx = 15 - sx;
		if (flipy) sy = 15 - sy;

		drawgfx(bitmap,machine->gfx[2],
//              cclimber_bsvideoram[offs],  /* cclimber */
				cclimber_bsvideoram[offs] + ((cclimber_bigspriteram[1] & 0x08) << 5),	/* swimmer */
				color,
				flipx,flipy,
				(ox+8*sx) & 0xff,(oy+8*sy) & 0xff,
				cliprect,TRANSPARENCY_PEN,0);

		/* wraparound */
		drawgfx(bitmap,machine->gfx[2],
//              cclimber_bsvideoram[offs],  /* cclimber */
				cclimber_bsvideoram[offs] + ((cclimber_bigspriteram[1] & 0x08) << 5),	/* swimmer */
				color,
				flipx,flipy,
				((ox+8*sx) & 0xff) - 256,(oy+8*sy) & 0xff,
				cliprect,TRANSPARENCY_PEN,0);
	}
}


VIDEO_UPDATE( cclimber )
{
	int offs;

	for (offs = videoram_size - 1;offs >= 0;offs--)
	{
		int sx,sy,flipx,flipy;

		sx = offs % 32;
		sy = offs / 32;
		flipx = colorram[offs] & 0x40;
		flipy = colorram[offs] & 0x80;
		/* vertical flipping flips two adjacent characters */
		if (flipy) sy ^= 1;

		if (flip_screen_x)
		{
			sx = 31 - sx;
			flipx = !flipx;
		}
		if (flip_screen_y)
		{
			sy = 31 - sy;
			flipy = !flipy;
		}

		drawgfx(tmpbitmap,machine->gfx[(colorram[offs] & 0x10) ? 1 : 0],
				videoram[offs] + 8 * (colorram[offs] & 0x20),
				colorram[offs] & 0x0f,
				flipx,flipy,
				8*sx,8*sy,
				0,TRANSPARENCY_NONE,0);
	}


	/* copy the temporary bitmap to the screen */
	{
		int scroll[32];


		if (flip_screen_x)
		{
			for (offs = 0;offs < 32;offs++)
			{
				scroll[offs] = -cclimber_column_scroll[31 - offs];
				if (flip_screen_y) scroll[offs] = -scroll[offs];
			}
		}
		else
		{
			for (offs = 0;offs < 32;offs++)
			{
				scroll[offs] = -cclimber_column_scroll[offs];
				if (flip_screen_y) scroll[offs] = -scroll[offs];
			}
		}

		copyscrollbitmap(bitmap,tmpbitmap,0,0,32,scroll,cliprect);
	}


	if (cclimber_bigspriteram[0] & 1)
		/* draw the "big sprite" below sprites */
		cclimber_draw_big_sprite(machine, bitmap, cliprect);


	/* Draw the sprites. Note that it is important to draw them exactly in this */
	/* order, to have the correct priorities. */
	for (offs = spriteram_size - 4;offs >= 0;offs -= 4)
	{
		int sx,sy,flipx,flipy;


		sx = spriteram[offs + 3];
		sy = 240 - spriteram[offs + 2];
		flipx = spriteram[offs] & 0x40;
		flipy = spriteram[offs] & 0x80;
		if (flip_screen_x)
		{
			sx = 240 - sx;
			flipx = !flipx;
		}
		if (flip_screen_y)
		{
			sy = 240 - sy;
			flipy = !flipy;
		}

		drawgfx(bitmap,machine->gfx[spriteram[offs + 1] & 0x10 ? 4 : 3],
				(spriteram[offs] & 0x3f) + 2 * (spriteram[offs + 1] & 0x20),
				spriteram[offs + 1] & 0x0f,
				flipx,flipy,
				sx,sy,
				cliprect,TRANSPARENCY_PEN,0);
	}


	if ((cclimber_bigspriteram[0] & 1) == 0)
		/* draw the "big sprite" over sprites */
		cclimber_draw_big_sprite(machine, bitmap, cliprect);
	return 0;
}


VIDEO_UPDATE( cannonb )
{
	int offs;

	for (offs = videoram_size - 1;offs >= 0;offs--)
	{
		int attr,code,color,sx,sy,flipx,flipy;

		code = videoram[offs];
		attr = colorram[offs] & 0x20;
		color = colorram[offs] & 0x0f;
		sx = offs % 32;
		sy = offs / 32;
		flipx = colorram[offs] & 0x40;
		flipy = colorram[offs] & 0x80;
		/* vertical flipping flips two adjacent characters */
		if (flipy) sy ^= 1;

		if (flip_screen_x)
		{
			sx = 31 - sx;
			flipx = !flipx;
		}
		if (flip_screen_y)
		{
			sy = 31 - sy;
			flipy = !flipy;
		}

		drawgfx(tmpbitmap,machine->gfx[0],
				code + 8 * attr,
				color,
				flipx,flipy,
				8*sx,8*sy,
				0,TRANSPARENCY_NONE,0);
	}


	/* copy the temporary bitmap to the screen */
	{
		int scroll[32];


		if (flip_screen_x)
		{
			for (offs = 0;offs < 32;offs++)
			{
				scroll[offs] = -cclimber_column_scroll[31 - offs];
				if (flip_screen_y) scroll[offs] = -scroll[offs];
			}
		}
		else
		{
			for (offs = 0;offs < 32;offs++)
			{
				scroll[offs] = -cclimber_column_scroll[offs];
				if (flip_screen_y) scroll[offs] = -scroll[offs];
			}
		}

		copyscrollbitmap(bitmap,tmpbitmap,0,0,32,scroll,cliprect);
	}


	if (cclimber_bigspriteram[0] & 1)
		/* draw the "big sprite" below sprites */
		cclimber_draw_big_sprite(machine, bitmap, cliprect);


	/* Draw the sprites. Note that it is important to draw them exactly in this */
	/* order, to have the correct priorities. */
	for (offs = spriteram_size - 4;offs >= 0;offs -= 4)
	{
		int code,color,sx,sy,flipx,flipy;

		code = spriteram[offs] & 0x3f;
		color = spriteram[offs + 1] & 0x0f;
		sx = spriteram[offs + 3];
		sy = 240 - spriteram[offs + 2];
		flipx = spriteram[offs] & 0x40;
		flipy = spriteram[offs] & 0x80;
		if (flip_screen_x)
		{
			sx = 240 - sx;
			flipx = !flipx;
		}
		if (flip_screen_y)
		{
			sy = 240 - sy;
			flipy = !flipy;
		}

		drawgfx(bitmap,machine->gfx[3],
				code + 0x40,
				color,
				flipx,flipy,
				sx,sy,
				cliprect,TRANSPARENCY_PEN,0);
	}

	if ((cclimber_bigspriteram[0] & 1) == 0)
		/* draw the "big sprite" over sprites */
		cclimber_draw_big_sprite(machine, bitmap, cliprect);
	return 0;
}


VIDEO_UPDATE( swimmer )
{
	int offs;

	swimmer_set_background_pen(machine);

	for (offs = videoram_size - 1;offs >= 0;offs--)
	{
		int sx,sy,flipx,flipy,color;

		sx = offs % 32;
		sy = offs / 32;
		flipx = colorram[offs] & 0x40;
		flipy = colorram[offs] & 0x80;
		/* vertical flipping flips two adjacent characters */
		if (flipy) sy ^= 1;

		color = (colorram[offs] & 0x0f) + 0x10 * (*swimmer_palettebank & 0x01);
		if (sx >= 24 && (*swimmer_sidepanel_enabled & 0x01))
		{
			color += 32;
		}

		if (flip_screen_x)
		{
			sx = 31 - sx;
			flipx = !flipx;
		}
		if (flip_screen_y)
		{
			sy = 31 - sy;
			flipy = !flipy;
		}

		drawgfx(tmpbitmap,machine->gfx[0],
				videoram[offs] + ((colorram[offs] & 0x10) << 4),
				color,
				flipx,flipy,
				8*sx,8*sy,
				0,TRANSPARENCY_NONE,0);
	}


	/* copy the temporary bitmap to the screen */
	{
		int scroll[32];


		if (flip_screen_y)
		{
			for (offs = 0;offs < 32;offs++)
				scroll[offs] = cclimber_column_scroll[31 - offs];
		}
		else
		{
			for (offs = 0;offs < 32;offs++)
				scroll[offs] = -cclimber_column_scroll[offs];
		}

		copyscrollbitmap(bitmap,tmpbitmap,0,0,32,scroll,cliprect);
	}


	if (cclimber_bigspriteram[0] & 1)
		/* draw the "big sprite" below sprites */
		cclimber_draw_big_sprite(machine, bitmap, cliprect);


	/* Draw the sprites. Note that it is important to draw them exactly in this */
	/* order, to have the correct priorities. */
	for (offs = spriteram_size - 4;offs >= 0;offs -= 4)
	{
		int sx,sy,flipx,flipy;


		sx = spriteram[offs + 3];
		sy = 240 - spriteram[offs + 2];
		flipx = spriteram[offs] & 0x40;
		flipy = spriteram[offs] & 0x80;
		if (flip_screen_x)
		{
			sx = 240 - sx;
			flipx = !flipx;
		}
		if (flip_screen_y)
		{
			sy = 240 - sy;
			flipy = !flipy;
		}

		drawgfx(bitmap,machine->gfx[1],
				(spriteram[offs] & 0x3f) | (spriteram[offs + 1] & 0x10) << 2,
				(spriteram[offs + 1] & 0x0f) + 0x10 * (*swimmer_palettebank & 0x01),
				flipx,flipy,
				sx,sy,
				cliprect,TRANSPARENCY_PEN,0);
	}


	if ((cclimber_bigspriteram[0] & 1) == 0)
		/* draw the "big sprite" over sprites */
		cclimber_draw_big_sprite(machine, bitmap, cliprect);
	return 0;
}

VIDEO_UPDATE( yamato )
{
	int offs;
	int i,j;

	/* bg gradient */
	fillbitmap(bitmap, 0, cliprect);

	for(i=8;i<256;i++)
	{
		pen_t pen = 16*4+8*4 + memory_region(REGION_USER1)[(flip_screen_x?0x1280:0x1200)+(i>>1)];

		for(j=0;j<256;j++)
		{
			*BITMAP_ADDR16(bitmap, j, i-8) = pen;
		}
	}


	fillbitmap(tmpbitmap, 0, NULL);

	for (offs = videoram_size - 1;offs >= 0;offs--)
	{
		int sx,sy,flipx,flipy;

		sx = offs % 32;
		sy = offs / 32;
		flipx = colorram[offs] & 0x40;
		flipy = colorram[offs] & 0x80;
		/* vertical flipping flips two adjacent characters */
		if (flipy) sy ^= 1;

		if (flip_screen_x)
		{
			sx = 31 - sx;
			flipx = !flipx;
		}
		if (flip_screen_y)
		{
			sy = 31 - sy;
			flipy = !flipy;
		}

		drawgfx(tmpbitmap,machine->gfx[(colorram[offs] & 0x10) ? 1 : 0],
				videoram[offs] + 8 * (colorram[offs] & 0x20),
				colorram[offs] & 0x0f,
				flipx,flipy,
				8*sx,8*sy,
				NULL,TRANSPARENCY_PEN,0);
	}


	/* copy the temporary bitmap to the screen */
	{
		int scroll[32];


		if (flip_screen_x)
		{
			for (offs = 0;offs < 32;offs++)
			{
				scroll[offs] = -cclimber_column_scroll[31 - offs];
				if (flip_screen_y) scroll[offs] = -scroll[offs];
			}
		}
		else
		{
			for (offs = 0;offs < 32;offs++)
			{
				scroll[offs] = -cclimber_column_scroll[offs];
				if (flip_screen_y) scroll[offs] = -scroll[offs];
			}
		}

		copyscrollbitmap_trans(bitmap,tmpbitmap,0,0,32,scroll,cliprect,0);
	}


	if (cclimber_bigspriteram[0] & 1)
		/* draw the "big sprite" below sprites */
		cclimber_draw_big_sprite(machine, bitmap, cliprect);


	/* Draw the sprites. Note that it is important to draw them exactly in this */
	/* order, to have the correct priorities. */
	for (offs = spriteram_size - 4;offs >= 0;offs -= 4)
	{
		int sx,sy,flipx,flipy;


		sx = spriteram[offs + 3];
		sy = 240 - spriteram[offs + 2];
		flipx = spriteram[offs] & 0x40;
		flipy = spriteram[offs] & 0x80;
		if (flip_screen_x)
		{
			sx = 240 - sx;
			flipx = !flipx;
		}
		if (flip_screen_y)
		{
			sy = 240 - sy;
			flipy = !flipy;
		}

		drawgfx(bitmap,machine->gfx[spriteram[offs + 1] & 0x10 ? 4 : 3],
				(spriteram[offs] & 0x3f) + 2 * (spriteram[offs + 1] & 0x20),
				spriteram[offs + 1] & 0x0f,
				flipx,flipy,
				sx,sy,
				cliprect,TRANSPARENCY_PEN,0);
	}


	if ((cclimber_bigspriteram[0] & 1) == 0)
		/* draw the "big sprite" over sprites */
		cclimber_draw_big_sprite(machine, bitmap, cliprect);
	return 0;
}


/* Top Roller */

#define PRIORITY_OVER  	0x20
#define PRIORITY_UNDER 	0x00
#define PRIORITY_MASK 	0x20

static tilemap *bg_tilemap;

UINT8 *toprollr_videoram2;
UINT8 *toprollr_videoram3;
UINT8 *toprollr_videoram4;


static TILE_GET_INFO( get_tile_info_bg )
{
	int code = toprollr_videoram3[tile_index];
	int attr = toprollr_videoram4[tile_index];
	int flipx,flipy,bank,palette;

	bank=(attr&0x40)<<2;





	flipx=1;
	flipy=0;

	code+=bank;

	palette=(attr&0xf)+0x18 ;

	SET_TILE_INFO(1, code, palette, (flipx?TILE_FLIPX:0)|(flipy?TILE_FLIPY:0));
}

VIDEO_START( toprollr )
{
	bg_tilemap = tilemap_create(get_tile_info_bg,tilemap_scan_rows,8,8,32,32);
}

static void toprollr_draw_big_sprite(running_machine *machine, mame_bitmap *bitmap,const rectangle *cliprect,int priority)
{
	if((cclimber_bigspriteram[1]&PRIORITY_MASK)==priority)
	{
		int code,xs,ys,palette,bank,x,y;
		int flipx=0;
		int flipy=0;

		xs=136-cclimber_bigspriteram[3];
		ys=128-cclimber_bigspriteram[2];


		if(xs==0)
		{
			return;
		}

		if (flip_screen_x)
		{
			flipx^=1;
		}

		palette=cclimber_bigspriteram[1]&0x7;

		bank=(cclimber_bigspriteram[1]>>3)&3;


		for(y=0;y<16;y++)
			for(x=0;x<16;x++)
			{
				int sx=x;
				int sy=y;
				if (flipx) sx = 15 - x;
				if (flipy) sy = 15 - y;

				code=cclimber_bsvideoram[y*16+x]+bank*256;

				drawgfx(bitmap, machine->gfx[3], code, palette, flipx, flipy,(sx*8+xs)&0xff,(sy*8+ys)&0xff, cliprect, TRANSPARENCY_PEN, 0);
				drawgfx(bitmap, machine->gfx[3], code, palette, flipx, flipy,((sx*8+xs)&0xff)-256,((sy*8+ys)&0xff)-256, cliprect, TRANSPARENCY_PEN, 0);
			}
	}
}

VIDEO_UPDATE( toprollr )
{

	UINT32 x,y;
	int offs;
	rectangle myclip=*cliprect;
	myclip.min_x=4*8;
	myclip.max_x=29*8-1;

	fillbitmap(bitmap, 0, cliprect);

	tilemap_set_scrollx(bg_tilemap,0,toprollr_videoram3[0]+8);
	tilemap_mark_all_tiles_dirty(bg_tilemap);
	tilemap_draw(bitmap, &myclip,bg_tilemap,0,0);

	toprollr_draw_big_sprite(machine, bitmap, &myclip, PRIORITY_UNDER);

	for (offs = spriteram_size - 4;offs >= 0;offs -= 4)
	{
		int sx,sy,flipx,flipy,palette;

		sx = spriteram[offs + 3]-8;
		sy = 240 - spriteram[offs + 2];
		flipx = spriteram[offs] & 0x40;
		flipy = spriteram[offs] & 0x80;
		if (flip_screen_x)
		{
			sx = 240 - sx;
			flipx = !flipx;
		}
		if (flip_screen_y)
		{
			sy = 240 - sy;
			flipy = !flipy;
		}

		palette=0x08+(spriteram[offs + 1] & 0x0f);

		drawgfx(bitmap,machine->gfx[2],
			(spriteram[offs] & 0x3f) + 2 * (spriteram[offs + 1] & 0x20)+8*(spriteram[offs + 1] & 0x10),
				palette,
				flipx,flipy,
				sx,sy,
				&myclip,TRANSPARENCY_PEN,0);
	}

	toprollr_draw_big_sprite(machine, bitmap, &myclip, PRIORITY_OVER);

	for(y=0;y<32;y++)
		for(x=0;x<32;x++)
		{
			int sx=x*8;
			int sy=y*8;
			int flipx=0;
			int flipy=0;

			int code=videoram[y*32+x];
			int attr=(x>16)?(toprollr_videoram2[(y&0xfe)*32+x]):(toprollr_videoram2[y*32+x]);
			int palette;

			if (flip_screen_x)
			{
				sx = 240 - sx;
				flipx^=1;
			}
			if (flip_screen_y)
			{
				sy = 240 - sy;
				flipy^=1;
			}

			palette=8+(attr&0xf);
			drawgfx(bitmap, machine->gfx[0], code+((attr&0xf0)<<4),palette, flipx, flipy, sx, sy, cliprect, TRANSPARENCY_PEN, 0);

		}
	return 0;
}