summaryrefslogtreecommitdiffstatshomepage
path: root/docs/source/techspecs/luaengine.rst
blob: fa1c04cdb1049aa3802547d5ed5a556866b4623c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
Scripting MAME via LUA
======================

Introduction
------------

It is now possible to externally drive MAME via LUA scripts. This feature initially appeared in version 0.148, when a minimal
``luaengine`` was implemented. Nowadays, the LUA interface is rich enough to let you inspect and manipulate devices state, access CPU
registers, read and write memory, and draw a custom HUD on screen.

Internally, MAME makes extensive use of ``luabridge`` to implement this feature: the idea is to transparently expose as many of the useful internals as possible.

Finally, a warning: The LUA API is not yet declared stable and may suddenly change without prior notice. However, we expose methods to let you know at runtime which API version you are running against, and you can introspect most of the objects at runtime.

Features
--------

The API is not yet complete, but this is a partial list of capabilities currently available to LUA scripts:

-  machine metadata (app version, current rom, rom details)
-  machine control (starting, pausing, resetting, stopping)
-  machine hooks (on frame painting and on user events)
-  devices introspection (device tree listing, memory and register
   enumeration)
-  screens introspection (screens listing, screen details, frames
   counting)
-  screen HUD drawing (text, lines, boxes on multiple screens)
-  memory read/write (8/16/32/64 bits, signed and unsigned)
-  registers and states control (states enumeration, get and set)

Usage
-----

MAME supports external scripting via LUA (>= 5.3) scripts, either written on the interactive console or loaded as a file. To reach the
console, just run MAME with **-console** and you will be greeted by a naked ``>`` prompt where you can input your script.

To load a whole script at once, store it in a plain text file and pass it via **-autoboot_script**. Please note that script loading may be delayed (few seconds by default), but you can override the default with the **-autoboot_delay** argument.

To control the execution of your code, you can use a loop-based or an event-based approach. The former is not encouraged as it is
resource-intensive and makes control flow unnecessarily complex. Instead, we suggest to register custom hooks to be invoked on specific
events (eg. at each frame rendering).

Walkthrough
-----------

Let's first run MAME in a terminal to reach the LUA console:

::

    $ mame -console YOUR_ROM
         _/      _/    _/_/    _/      _/  _/_/_/_/
       _/_/  _/_/  _/    _/  _/_/  _/_/  _/
      _/  _/  _/  _/_/_/_/  _/  _/  _/  _/_/_/
     _/      _/  _/    _/  _/      _/  _/
    _/      _/  _/    _/  _/      _/  _/_/_/_/
    mame v0.195
    Copyright (C) Nicola Salmoria and the MAME team

    Lua 5.3
    Copyright (C) Lua.org, PUC-Rio

    [MAME]>

At this point, your game is probably running in demo mode, let's pause it:

::

    [MAME]> emu.pause()
    [MAME]>

Even without textual feedback on the console, you'll notice the game is
now paused. In general, commands are quiet and only print back error
messages.

You can check at runtime which version of MAME you are running, with:

::

    [MAME]> print(emu.app_name() .. " " .. emu.app_version())
    mame 0.195

We now start exploring screen related methods. First, let's enumerate available screens:

::

    [MAME]> for i,v in pairs(manager:machine().screens) do print(i) end
    :screen

**manager:machine()** is the root object of your currently running machine: we will be using this often. **screens** is a table with all
available screens; most machines only have one main screen. In our case, the main and only screen is tagged as **:screen**, and we can further inspect it:

::

    [MAME]> -- let's define a shorthand for the main screen
    [MAME]> s = manager:machine().screens[":screen"]
    [MAME]> print(s:width() .. "x" .. s:height())
    320x224

We have several methods to draw on the screen a HUD composed of lines, boxes and text:

::

    [MAME]> -- we define a HUD-drawing function, and then call it
    [MAME]> function draw_hud()
    [MAME]>> s:draw_text(40, 40, "foo"); -- (x0, y0, msg)
    [MAME]>> s:draw_box(20, 20, 80, 80, 0, 0xff00ffff); -- (x0, y0, x1, y1, fill-color, line-color)
    [MAME]>> s:draw_line(20, 20, 80, 80, 0xff00ffff); -- (x0, y0, x1, y1, line-color)
    [MAME]>> end
    [MAME]> draw_hud();

This will draw some useless art on the screen. However, when unpausing the game, your HUD needs to be refreshed otherwise it will just disappear. In order to do this, you have to register your hook to be called on every frame repaint:

::

    [MAME]> emu.register_frame_done(draw_hud, "frame")

All colors are expected in ARGB format (32b unsigned), while screen origin (0,0) normally corresponds to the top-left corner.

Similarly to screens, you can inspect all the devices attached to a machine:

::

    [MAME]> for k,v in pairs(manager:machine().devices) do print(k) end
    :audiocpu
    :maincpu
    :saveram
    :screen
    :palette
    [...]

On some of them, you can also inspect and manipulate memory and state:

::

    [MAME]> cpu = manager:machine().devices[":maincpu"]
    [MAME]> -- enumerate, read and write state registers
    [MAME]> for k,v in pairs(cpu.state) do print(k) end
    D5
    SP
    A4
    A3
    D0
    PC
    [...]
    [MAME]> print(cpu.state["D0"].value)
    303
    [MAME]> cpu.state["D0"].value = 255
    [MAME]> print(cpu.state["D0"].value)
    255

::

    [MAME]> -- inspect memory
    [MAME]> for k,v in pairs(cpu.spaces) do print(k) end
    program
    [MAME]> mem = cpu.spaces["program"]
    [MAME]> print(mem:read_i8(0xC000))
    41
#n557'>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
#include "driver.h"
#include "machine/eeprom.h"
#include "video/konamiic.h"


static int layer_colorbase[3],sprite_colorbase,bg_colorbase;
static int priorityflag;
static int layerpri[3];
static int prmrsocr_sprite_bank;
static int sorted_layer[3];
static int dim_c,dim_v;	/* ssriders, tmnt2 */
static int lastdim,lasten;

static tilemap *roz_tilemap;


static int glfgreat_roz_rom_bank,glfgreat_roz_char_bank,glfgreat_roz_rom_mode;

static TILE_GET_INFO( glfgreat_get_roz_tile_info )
{
	UINT8 *rom = memory_region(REGION_USER1);
	int code;

	tile_index += 0x40000 * glfgreat_roz_rom_bank;

	code = rom[tile_index+0x80000] + 256*rom[tile_index] + 256*256*((rom[tile_index/4+0x100000]>>(2*(tile_index&3)))&3);

	SET_TILE_INFO(0,code & 0x3fff,code >> 14,0);
}

static TILE_GET_INFO( prmrsocr_get_roz_tile_info )
{
	UINT8 *rom = memory_region(REGION_USER1);
	int code = rom[tile_index+0x20000] + 256*rom[tile_index];

	SET_TILE_INFO(0,code & 0x1fff,code >> 13,0);
}



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

  Callbacks for the K052109

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

/* Missing in Action */

static void mia_tile_callback(int layer,int bank,int *code,int *color,int *flags,int *priority)
{
	*flags = (*color & 0x04) ? TILE_FLIPX : 0;
	if (layer == 0)
	{
		*code |= ((*color & 0x01) << 8);
		*color = layer_colorbase[layer] + ((*color & 0x80) >> 5) + ((*color & 0x10) >> 1);
	}
	else
	{
		*code |= ((*color & 0x01) << 8) | ((*color & 0x18) << 6) | (bank << 11);
		*color = layer_colorbase[layer] + ((*color & 0xe0) >> 5);
	}
}

static void cuebrick_tile_callback(int layer,int bank,int *code,int *color,int *flags,int *priority)
{
	if ((K052109_get_RMRD_line() == CLEAR_LINE) && (layer == 0))
	{
		*code |= ((*color & 0x01) << 8);
		*color = layer_colorbase[layer]  + ((*color & 0x80) >> 5) + ((*color & 0x10) >> 1);
	}
	else
	{
		*code |= ((*color & 0xf) << 8);
		*color = layer_colorbase[layer] + ((*color & 0xe0) >> 5);
	}
}

static void tmnt_tile_callback(int layer,int bank,int *code,int *color,int *flags,int *priority)
{
	*code |= ((*color & 0x03) << 8) | ((*color & 0x10) << 6) | ((*color & 0x0c) << 9)
			| (bank << 13);
	*color = layer_colorbase[layer] + ((*color & 0xe0) >> 5);
}

static void ssbl_tile_callback(int layer,int bank,int *code,int *color,int *flags,int *priority)
{
	if (layer == 0)
	{
		*code |= ((*color & 0x03) << 8) | ((*color & 0x10) << 6) | ((*color & 0x0c) << 9)
				| (bank << 13);
	}
	else
	{
		*code |= ((*color & 0x03) << 8) | ((*color & 0x10) << 6) | ((*color & 0x0c) << 9)
				| (bank << 13);
//      mame_printf_debug("L%d: bank %d code %x color %x\n", layer, bank, *code, *color);
	}

	*color = layer_colorbase[layer] + ((*color & 0xe0) >> 5);
}

static int blswhstl_rombank;

static void blswhstl_tile_callback(int layer,int bank,int *code,int *color,int *flags,int *priority)
{
	/* (color & 0x02) is flip y handled internally by the 052109 */
	*code |= ((*color & 0x01) << 8) | ((*color & 0x10) << 5) | ((*color & 0x0c) << 8)
			| (bank << 12) | blswhstl_rombank << 14;
	*color = layer_colorbase[layer] + ((*color & 0xe0) >> 5);
}



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

  Callbacks for the K051960

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

static void mia_sprite_callback(int *code,int *color,int *priority,int *shadow)
{
	*color = sprite_colorbase + (*color & 0x0f);
}

static void tmnt_sprite_callback(int *code,int *color,int *priority,int *shadow)
{
	*code |= (*color & 0x10) << 9;
	*color = sprite_colorbase + (*color & 0x0f);
}

static void punkshot_sprite_callback(int *code,int *color,int *priority_mask,int *shadow)
{
	int pri = 0x20 | ((*color & 0x60) >> 2);
	if (pri <= layerpri[2])								*priority_mask = 0;
	else if (pri > layerpri[2] && pri <= layerpri[1])	*priority_mask = 0xf0;
	else if (pri > layerpri[1] && pri <= layerpri[0])	*priority_mask = 0xf0|0xcc;
	else 												*priority_mask = 0xf0|0xcc|0xaa;

	*code |= (*color & 0x10) << 9;
	*color = sprite_colorbase + (*color & 0x0f);
}

static void thndrx2_sprite_callback(int *code,int *color,int *priority_mask,int *shadow)
{
	int pri = 0x20 | ((*color & 0x60) >> 2);
	if (pri <= layerpri[2])								*priority_mask = 0;
	else if (pri > layerpri[2] && pri <= layerpri[1])	*priority_mask = 0xf0;
	else if (pri > layerpri[1] && pri <= layerpri[0])	*priority_mask = 0xf0|0xcc;
	else 												*priority_mask = 0xf0|0xcc|0xaa;

	*color = sprite_colorbase + (*color & 0x0f);
}


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

  Callbacks for the K053245

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

static void lgtnfght_sprite_callback(int *code,int *color,int *priority_mask)
{
	int pri = 0x20 | ((*color & 0x60) >> 2);
	if (pri <= layerpri[2])								*priority_mask = 0;
	else if (pri > layerpri[2] && pri <= layerpri[1])	*priority_mask = 0xf0;
	else if (pri > layerpri[1] && pri <= layerpri[0])	*priority_mask = 0xf0|0xcc;
	else 												*priority_mask = 0xf0|0xcc|0xaa;

	*color = sprite_colorbase + (*color & 0x1f);
}

static void blswhstl_sprite_callback(int *code,int *color,int *priority_mask)
{
#if 0
if (input_code_pressed(KEYCODE_Q) && (*color & 0x20)) *color = rand();
if (input_code_pressed(KEYCODE_W) && (*color & 0x40)) *color = rand();
if (input_code_pressed(KEYCODE_E) && (*color & 0x80)) *color = rand();
#endif
	int pri = 0x20 | ((*color & 0x60) >> 2);
	if (pri <= layerpri[2])								*priority_mask = 0;
	else if (pri > layerpri[2] && pri <= layerpri[1])	*priority_mask = 0xf0;
	else if (pri > layerpri[1] && pri <= layerpri[0])	*priority_mask = 0xf0|0xcc;
	else 												*priority_mask = 0xf0|0xcc|0xaa;

	*color = sprite_colorbase + (*color & 0x1f);
}

static void prmrsocr_sprite_callback(int *code,int *color,int *priority_mask)
{
	int pri = 0x20 | ((*color & 0x60) >> 2);
	if (pri <= layerpri[2])								*priority_mask = 0;
	else if (pri > layerpri[2] && pri <= layerpri[1])	*priority_mask = 0xf0;
	else if (pri > layerpri[1] && pri <= layerpri[0])	*priority_mask = 0xf0|0xcc;
	else 												*priority_mask = 0xf0|0xcc|0xaa;

	*code |= prmrsocr_sprite_bank << 14;

	*color = sprite_colorbase + (*color & 0x1f);
}



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

  Start the video hardware emulation.

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

VIDEO_START( mia )
{
	layer_colorbase[0] = 0;
	layer_colorbase[1] = 32;
	layer_colorbase[2] = 40;
	sprite_colorbase = 16;
	K052109_vh_start(machine,REGION_GFX1,NORMAL_PLANE_ORDER,mia_tile_callback);
	K051960_vh_start(machine,REGION_GFX2,REVERSE_PLANE_ORDER,mia_sprite_callback);
}

VIDEO_START( cuebrick )
{
	layer_colorbase[0] = 0;
	layer_colorbase[1] = 32;
	layer_colorbase[2] = 40;
	sprite_colorbase = 16;
	K052109_vh_start(machine,REGION_GFX1,NORMAL_PLANE_ORDER,cuebrick_tile_callback);
	K051960_vh_start(machine,REGION_GFX2,REVERSE_PLANE_ORDER,mia_sprite_callback);
}

VIDEO_START( tmnt )
{
	layer_colorbase[0] = 0;
	layer_colorbase[1] = 32;
	layer_colorbase[2] = 40;
	sprite_colorbase = 16;
	K052109_vh_start(machine,REGION_GFX1,NORMAL_PLANE_ORDER,tmnt_tile_callback);
	K051960_vh_start(machine,REGION_GFX2,REVERSE_PLANE_ORDER,tmnt_sprite_callback);
}

VIDEO_START( punkshot )
{
	K053251_vh_start();
	K052109_vh_start(machine,REGION_GFX1,NORMAL_PLANE_ORDER,tmnt_tile_callback);
	K051960_vh_start(machine,REGION_GFX2,NORMAL_PLANE_ORDER,punkshot_sprite_callback);
}

VIDEO_START( lgtnfght )	/* also tmnt2, ssriders */
{
	K053251_vh_start();
	K052109_vh_start(machine,REGION_GFX1,NORMAL_PLANE_ORDER,tmnt_tile_callback);
	K053245_vh_start(machine,0, REGION_GFX2,NORMAL_PLANE_ORDER,lgtnfght_sprite_callback);

	K05324x_set_z_rejection(0);

	dim_c = dim_v = lastdim = lasten = 0;

	state_save_register_global(dim_c);
	state_save_register_global(dim_v);
	state_save_register_global(lastdim);
	state_save_register_global(lasten);
}

VIDEO_START( sunsetbl )
{
	K053251_vh_start();
	K052109_vh_start(machine,REGION_GFX1,NORMAL_PLANE_ORDER,ssbl_tile_callback);
	K053245_vh_start(machine,0, REGION_GFX2,NORMAL_PLANE_ORDER,lgtnfght_sprite_callback);
}

VIDEO_START( blswhstl )
{
	K053251_vh_start();
	K052109_vh_start(machine,REGION_GFX1,NORMAL_PLANE_ORDER,blswhstl_tile_callback);
	K053245_vh_start(machine,0, REGION_GFX2,NORMAL_PLANE_ORDER,blswhstl_sprite_callback);
}

VIDEO_START( glfgreat )
{
	K053251_vh_start();
	K052109_vh_start(machine,REGION_GFX1,NORMAL_PLANE_ORDER,tmnt_tile_callback);
	K053245_vh_start(machine,0, REGION_GFX2,NORMAL_PLANE_ORDER,lgtnfght_sprite_callback);

	roz_tilemap = tilemap_create(glfgreat_get_roz_tile_info,tilemap_scan_rows,TILEMAP_TYPE_PEN,16,16,512,512);

	tilemap_set_transparent_pen(roz_tilemap,0);

	K053936_wraparound_enable(0, 1);
	K053936_set_offset(0, 85, 0);
}

VIDEO_START( thndrx2 )
{
	K053251_vh_start();
	K052109_vh_start(machine,REGION_GFX1,NORMAL_PLANE_ORDER,tmnt_tile_callback);
	K051960_vh_start(machine,REGION_GFX2,NORMAL_PLANE_ORDER,thndrx2_sprite_callback);
}

VIDEO_START( prmrsocr )
{
	K053251_vh_start();
	K052109_vh_start(machine,REGION_GFX1,NORMAL_PLANE_ORDER,tmnt_tile_callback);
	K053245_vh_start(machine,0, REGION_GFX2,NORMAL_PLANE_ORDER,prmrsocr_sprite_callback);

	roz_tilemap = tilemap_create(prmrsocr_get_roz_tile_info,tilemap_scan_rows,TILEMAP_TYPE_PEN,16,16,512,256);

	tilemap_set_transparent_pen(roz_tilemap,0);

	K053936_wraparound_enable(0, 0);
	K053936_set_offset(0, 85, 1);
}



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

  Memory handlers

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

WRITE16_HANDLER( tmnt_paletteram_word_w )
{
	COMBINE_DATA(paletteram16 + offset);
	offset &= ~1;

	data = (paletteram16[offset] << 8) | paletteram16[offset+1];
	palette_set_color_rgb(Machine,offset / 2,pal5bit(data >> 0),pal5bit(data >> 5),pal5bit(data >> 10));
}



WRITE16_HANDLER( tmnt_0a0000_w )
{
	if (ACCESSING_LSB)
	{
		static int last;

		/* bit 0/1 = coin counters */
		coin_counter_w(0,data & 0x01);
		coin_counter_w(1,data & 0x02);	/* 2 players version */

		/* bit 3 high then low triggers irq on sound CPU */
		if (last == 0x08 && (data & 0x08) == 0)
			cpunum_set_input_line_and_vector(1,0,HOLD_LINE,0xff);

		last = data & 0x08;

		/* bit 5 = irq enable */
		interrupt_enable_w(0,data & 0x20);

		/* bit 7 = enable char ROM reading through the video RAM */
		K052109_set_RMRD_line((data & 0x80) ? ASSERT_LINE : CLEAR_LINE);

		/* other bits unused */
	}
}

WRITE16_HANDLER( punkshot_0a0020_w )
{
	if (ACCESSING_LSB)
	{
		static int last;


		/* bit 0 = coin counter */
		coin_counter_w(0,data & 0x01);

		/* bit 2 = trigger irq on sound CPU */
		if (last == 0x04 && (data & 0x04) == 0)
			cpunum_set_input_line_and_vector(1,0,HOLD_LINE,0xff);

		last = data & 0x04;

		/* bit 3 = enable char ROM reading through the video RAM */
		K052109_set_RMRD_line((data & 0x08) ? ASSERT_LINE : CLEAR_LINE);
	}
}

WRITE16_HANDLER( lgtnfght_0a0018_w )
{
	if (ACCESSING_LSB)
	{
		static int last;


		/* bit 0,1 = coin counter */
		coin_counter_w(0,data & 0x01);
		coin_counter_w(1,data & 0x02);

		/* bit 2 = trigger irq on sound CPU */
		if (last == 0x00 && (data & 0x04) == 0x04)
			cpunum_set_input_line_and_vector(1,0,HOLD_LINE,0xff);

		last = data & 0x04;

		/* bit 3 = enable char ROM reading through the video RAM */
		K052109_set_RMRD_line((data & 0x08) ? ASSERT_LINE : CLEAR_LINE);
	}
}

WRITE16_HANDLER( blswhstl_700300_w )
{
	if (ACCESSING_LSB)
	{
		/* bit 0,1 = coin counter */
		coin_counter_w(0,data & 0x01);
		coin_counter_w(1,data & 0x02);

		/* bit 3 = enable char ROM reading through the video RAM */
		K052109_set_RMRD_line((data & 0x08) ? ASSERT_LINE : CLEAR_LINE);

		/* bit 7 = select char ROM bank */
		if (blswhstl_rombank != ((data & 0x80) >> 7))
		{
			blswhstl_rombank = (data & 0x80) >> 7;
			tilemap_mark_all_tiles_dirty(ALL_TILEMAPS);
		}

		/* other bits unknown */
	}
}


READ16_HANDLER( glfgreat_rom_r )
{
	if (glfgreat_roz_rom_mode)
		return memory_region(REGION_GFX3)[glfgreat_roz_char_bank * 0x80000 + offset];
	else if (offset < 0x40000)
		return memory_region(REGION_USER1)[offset + 0x80000 + glfgreat_roz_rom_bank * 0x40000] +
				256 * memory_region(REGION_USER1)[offset + glfgreat_roz_rom_bank * 0x40000];
	else
		return memory_region(REGION_USER1)[((offset & 0x3ffff) >> 2) + 0x100000 + glfgreat_roz_rom_bank * 0x10000];
}

WRITE16_HANDLER( glfgreat_122000_w )
{
	if (ACCESSING_LSB)
	{
		/* bit 0,1 = coin counter */
		coin_counter_w(0,data & 0x01);
		coin_counter_w(1,data & 0x02);

		/* bit 4 = enable char ROM reading through the video RAM */
		K052109_set_RMRD_line((data & 0x10) ? ASSERT_LINE : CLEAR_LINE);

		/* bit 5 = 53596 tile rom bank selection */
		if (glfgreat_roz_rom_bank != (data & 0x20) >> 5)
		{
			glfgreat_roz_rom_bank = (data & 0x20) >> 5;
			tilemap_mark_all_tiles_dirty(roz_tilemap);
		}

		/* bit 6,7 = 53596 char bank selection for ROM test */
		glfgreat_roz_char_bank = (data & 0xc0) >> 6;

		/* other bits unknown */
	}
	if (ACCESSING_MSB)
	{
		/* bit 8 = 53596 char/rom selection for ROM test */
		glfgreat_roz_rom_mode = data & 0x100;
	}
}


WRITE16_HANDLER( ssriders_eeprom_w )
{
	if (ACCESSING_LSB)
	{
		/* bit 0 is data */
		/* bit 1 is cs (active low) */
		/* bit 2 is clock (active high) */
		EEPROM_write_bit(data & 0x01);
		EEPROM_set_cs_line((data & 0x02) ? CLEAR_LINE : ASSERT_LINE);
		EEPROM_set_clock_line((data & 0x04) ? ASSERT_LINE : CLEAR_LINE);

		/* bits 3-4 control palette dimming */
		/* 4 = DIMPOL = when set, negate SHAD */
		/* 3 = DIMMOD = when set, or BRIT with [negated] SHAD */
		dim_c = data & 0x18;

		/* bit 5 selects sprite ROM for testing in TMNT2 (bits 5-7, actually, according to the schematics) */
		K053244_bankselect(0, ((data & 0x20) >> 5) << 2);
	}
}

WRITE16_HANDLER( ssriders_1c0300_w )
{
	if (ACCESSING_LSB)
	{
		/* bit 0,1 = coin counter */
		coin_counter_w(0,data & 0x01);
		coin_counter_w(1,data & 0x02);

		/* bit 3 = enable char ROM reading through the video RAM */
		K052109_set_RMRD_line((data & 0x08) ? ASSERT_LINE : CLEAR_LINE);

		/* bits 4-6 control palette dimming (DIM0-DIM2) */
		dim_v = (data & 0x70) >> 4;
	}
}

WRITE16_HANDLER( prmrsocr_122000_w )
{
	if (ACCESSING_LSB)
	{
		/* bit 0,1 = coin counter */
		coin_counter_w(0,data & 0x01);
		coin_counter_w(1,data & 0x02);

		/* bit 4 = enable char ROM reading through the video RAM */
		K052109_set_RMRD_line((data & 0x10) ? ASSERT_LINE : CLEAR_LINE);

		/* bit 6 = sprite ROM bank */
		prmrsocr_sprite_bank = (data & 0x40) >> 6;
		K053244_bankselect(0, prmrsocr_sprite_bank << 2);

		/* bit 7 = 53596 region selector for ROM test */
		glfgreat_roz_char_bank = (data & 0x80) >> 7;

		/* other bits unknown (unused?) */
	}
}

READ16_HANDLER( prmrsocr_rom_r )
{
	if(glfgreat_roz_char_bank)
		return memory_region(REGION_GFX3)[offset];
	else
		return 256 * memory_region(REGION_USER1)[offset] + memory_region(REGION_USER1)[offset + 0x020000];
}

WRITE16_HANDLER( tmnt_priority_w )
{
	if (ACCESSING_LSB)
	{
		/* bit 2/3 = priority; other bits unused */
		/* bit2 = PRI bit3 = PRI2
              sprite/playfield priority is controlled by these two bits, by bit 3
              of the background tile color code, and by the SHADOW sprite
              attribute bit.
              Priorities are encoded in a PROM (G19 for TMNT). However, in TMNT,
              the PROM only takes into account the PRI and SHADOW bits.
              PRI  Priority
               0   bg fg spr text
               1   bg spr fg text
              The SHADOW bit, when set, torns a sprite into a shadow which makes
              color below it darker (this is done by turning off three resistors
              in parallel with the RGB output).

              Note: the background color (color used when all of the four layers
              are 0) is taken from the *foreground* palette, not the background
              one as would be more intuitive.
        */
		priorityflag = (data & 0x0c) >> 2;
	}
}



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

  Display refresh

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

/* useful function to sort the three tile layers by priority order */
static void sortlayers(int *layer,int *pri)
{
#define SWAP(a,b) \
	if (pri[a] < pri[b]) \
	{ \
		int t; \
		t = pri[a]; pri[a] = pri[b]; pri[b] = t; \
		t = layer[a]; layer[a] = layer[b]; layer[b] = t; \
	}

	SWAP(0,1)
	SWAP(0,2)
	SWAP(1,2)
}

VIDEO_UPDATE( mia )
{
	K052109_tilemap_update();

	tilemap_draw(bitmap,cliprect,K052109_tilemap[2],TILEMAP_DRAW_OPAQUE,0);
	if ((priorityflag & 1) == 1) K051960_sprites_draw(bitmap,cliprect,0,0);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[1],0,0);
	if ((priorityflag & 1) == 0) K051960_sprites_draw(bitmap,cliprect,0,0);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[0],0,0);
	return 0;
}

VIDEO_UPDATE( tmnt )
{
	K052109_tilemap_update();

	tilemap_draw(bitmap,cliprect,K052109_tilemap[2],TILEMAP_DRAW_OPAQUE,0);
	if ((priorityflag & 1) == 1) K051960_sprites_draw(bitmap,cliprect,0,0);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[1],0,0);
	if ((priorityflag & 1) == 0) K051960_sprites_draw(bitmap,cliprect,0,0);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[0],0,0);
	return 0;
}


VIDEO_UPDATE( punkshot )
{
	bg_colorbase       = K053251_get_palette_index(K053251_CI0);
	sprite_colorbase   = K053251_get_palette_index(K053251_CI1);
	layer_colorbase[0] = K053251_get_palette_index(K053251_CI2);
	layer_colorbase[1] = K053251_get_palette_index(K053251_CI4);
	layer_colorbase[2] = K053251_get_palette_index(K053251_CI3);

	K052109_tilemap_update();

	sorted_layer[0] = 0;
	layerpri[0] = K053251_get_priority(K053251_CI2);
	sorted_layer[1] = 1;
	layerpri[1] = K053251_get_priority(K053251_CI4);
	sorted_layer[2] = 2;
	layerpri[2] = K053251_get_priority(K053251_CI3);

	sortlayers(sorted_layer,layerpri);

	fillbitmap(priority_bitmap,0,cliprect);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[0]],TILEMAP_DRAW_OPAQUE,1);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[1]],0,2);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[2]],0,4);

	K051960_sprites_draw(bitmap,cliprect,-1,-1);
	return 0;
}


VIDEO_UPDATE( lgtnfght )
{
	bg_colorbase       = K053251_get_palette_index(K053251_CI0);
	sprite_colorbase   = K053251_get_palette_index(K053251_CI1);
	layer_colorbase[0] = K053251_get_palette_index(K053251_CI2);
	layer_colorbase[1] = K053251_get_palette_index(K053251_CI4);
	layer_colorbase[2] = K053251_get_palette_index(K053251_CI3);

	K052109_tilemap_update();

	sorted_layer[0] = 0;
	layerpri[0] = K053251_get_priority(K053251_CI2);
	sorted_layer[1] = 1;
	layerpri[1] = K053251_get_priority(K053251_CI4);
	sorted_layer[2] = 2;
	layerpri[2] = K053251_get_priority(K053251_CI3);

	sortlayers(sorted_layer,layerpri);

	fillbitmap(priority_bitmap,0,cliprect);
	fillbitmap(bitmap,machine->pens[16 * bg_colorbase],cliprect);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[0]],0,1);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[1]],0,2);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[2]],0,4);

	K053245_sprites_draw(0, bitmap,cliprect);
	return 0;
}

static int glfgreat_pixel;

READ16_HANDLER( glfgreat_ball_r )
{
#ifdef MAME_DEBUG
popmessage("%04x",glfgreat_pixel);
#endif
	/* if out of the ROZ layer palette range, it's in the water - return 0 */
	if (glfgreat_pixel < 0x400 || glfgreat_pixel >= 0x500) return 0;
	else return glfgreat_pixel & 0xff;
}

VIDEO_UPDATE( glfgreat )
{
	K053251_set_tilemaps(NULL,NULL,K052109_tilemap[0],K052109_tilemap[1],K052109_tilemap[2]);

	bg_colorbase       = K053251_get_palette_index(K053251_CI0);
	sprite_colorbase   = K053251_get_palette_index(K053251_CI1);
	layer_colorbase[0] = K053251_get_palette_index(K053251_CI2);
	layer_colorbase[1] = K053251_get_palette_index(K053251_CI3) + 8;	/* weird... */
	layer_colorbase[2] = K053251_get_palette_index(K053251_CI4);

	K052109_tilemap_update();

	sorted_layer[0] = 0;
	layerpri[0] = K053251_get_priority(K053251_CI2);
	sorted_layer[1] = 1;
	layerpri[1] = K053251_get_priority(K053251_CI3);
	sorted_layer[2] = 2;
	layerpri[2] = K053251_get_priority(K053251_CI4);

	sortlayers(sorted_layer,layerpri);

	/* not sure about the 053936 priority, but it seems to work */

	fillbitmap(priority_bitmap,0,cliprect);
	fillbitmap(bitmap,machine->pens[16 * bg_colorbase],cliprect);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[0]],0,1);
	if (layerpri[0] >= 0x30 && layerpri[1] < 0x30)
	{
		K053936_0_zoom_draw(bitmap,cliprect,roz_tilemap,0,1);
		glfgreat_pixel = *BITMAP_ADDR16(bitmap,0x80,0x105);
	}
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[1]],0,2);
	if (layerpri[1] >= 0x30 && layerpri[2] < 0x30)
	{
		K053936_0_zoom_draw(bitmap,cliprect,roz_tilemap,0,1);
		glfgreat_pixel = *BITMAP_ADDR16(bitmap,0x80,0x105);
	}
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[2]],0,4);
	if (layerpri[2] >= 0x30)
	{
		K053936_0_zoom_draw(bitmap,cliprect,roz_tilemap,0,1);
		glfgreat_pixel = *BITMAP_ADDR16(bitmap,0x80,0x105);
	}

	K053245_sprites_draw(0, bitmap,cliprect);
	return 0;
}

VIDEO_UPDATE( tmnt2 )
{
	double brt;
	int i, newdim, newen, cb, ce;

	newdim = dim_v | ((~dim_c & 0x10) >> 1);
	newen  = (K053251_get_priority(5) && K053251_get_priority(5) != 0x3e);

	if (newdim != lastdim || newen != lasten)
	{
		brt = 1.0;
		if (newen) brt -= (1.0-PALETTE_DEFAULT_SHADOW_FACTOR)*newdim/8;
		lastdim = newdim;
		lasten  = newen;

		/*
            Only affect the background and sprites, not text layer.
            Instead of dimming each layer we dim the entire palette
            except text colors because palette bases may change
            anytime and there's no guarantee a dimmed color will be
            reset properly.
        */

		// find the text layer's palette range
		cb = layer_colorbase[sorted_layer[2]] << 4;
		ce = cb + 128;

		// dim all colors before it
		for (i=0; i<cb; i++)
			palette_set_brightness(machine,i,brt);

		// reset all colors in range
		for (i=cb; i<ce; i++)
			palette_set_brightness(machine,i,1.0);

		// dim all colors after it
		for (i=ce; i<2048; i++)
			palette_set_brightness(machine,i,brt);

		// toggle shadow/highlight
		if (~dim_c & 0x10)
			palette_set_shadow_mode(machine, 1);
		else
			palette_set_shadow_mode(machine, 0);
	}

	VIDEO_UPDATE_CALL(lgtnfght);
	return 0;
}


VIDEO_UPDATE( thndrx2 )
{
	bg_colorbase       = K053251_get_palette_index(K053251_CI0);
	sprite_colorbase   = K053251_get_palette_index(K053251_CI1);
	layer_colorbase[0] = K053251_get_palette_index(K053251_CI2);
	layer_colorbase[1] = K053251_get_palette_index(K053251_CI4);
	layer_colorbase[2] = K053251_get_palette_index(K053251_CI3);

	K052109_tilemap_update();

	sorted_layer[0] = 0;
	layerpri[0] = K053251_get_priority(K053251_CI2);
	sorted_layer[1] = 1;
	layerpri[1] = K053251_get_priority(K053251_CI4);
	sorted_layer[2] = 2;
	layerpri[2] = K053251_get_priority(K053251_CI3);

	sortlayers(sorted_layer,layerpri);

	fillbitmap(priority_bitmap,0,cliprect);
	fillbitmap(bitmap,machine->pens[16 * bg_colorbase],cliprect);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[0]],0,1);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[1]],0,2);
	tilemap_draw(bitmap,cliprect,K052109_tilemap[sorted_layer[2]],0,4);

	K051960_sprites_draw(bitmap,cliprect,-1,-1);
	return 0;
}



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

  Housekeeping

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

VIDEO_EOF( blswhstl )
{
	K053245_clear_buffer(0);
}