summaryrefslogblamecommitdiffstatshomepage
path: root/src/emu/tilemap.c
blob: ecbd59b322051349794e8fa1139324a227cc6ca2 (plain) (tree)
1
2
3
4
5
6
7
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
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248





                                                                            
                                                






                                                                            
                     











                                                                            

                                                           



















                                                                            

                                                                                                                                             





















                                                                                                                     










































                                                                                                                                          


































                                                                                                                                                 








                                                                                                                                                                             











                                                                                                                                                               




























































































                                                                                                   







                                                    
 
                                                                                                         

                                                         











                                                                            
                                                                                                                                                








                                              






























                                                                                        
                                                                                         
                                                                                                                  
























































































































































                                                                                                          
                                                                     












                                                                                       
                                               




























































































































































                                                                                                        



























                                                   


















































































































































































































































































































































































































































































































































































                                                                                                                                         
                                                                                                                                                                        

















                                                                                                                                                
                                                                            



































                                                                                             
                                                                 


















































                                                                   































                                                                                                                        
                                                                 
























































































































                                                                                                                                                                            
                                   
                                   
                                 

















                                                                     
                                                                    














                                                                                                   
                                                            



























































                                                                                                                                                                 
                                                                           

















                                                                                                                                          
                                                                                     

























































                                                                                                                                                                          
                                                                              

















                                                          
                   
                          
                             





































                                                                              
                                                                            
                                                                      
                                                                        








                                                                                                                   
                                                                                           




























                                                                                                           
                                                                    




                                                               
                                                                                                                      

                                                                                                                          
                                                                                   





























                                                                                                           
                                                                    





                                                                                           
                                                                                                          

                                                                                                              
                                                                                           




























                                                                            
                                                                                                            






                                                    
                                                                 








                                                   
                                                                                                                                                       







                                                         
                                                                         









                                                   
                                                                                                              













                                                   
                                                                         








                                                  
                                                                 
















                                                   
                                                                                                                                                         











                                                          
                                                                         


















                                                          
                                                                                                              
 
                                                        








                                                  
                                                                 
















                                                   
                                                                                                                                                         
 
                                                        









                                                          
                                                                         

















                                                          
                                                                                                                    
 
                                                        








                                                                          
                                                                 

















                                                                          
                                                                                                                                                               
 
                                                        









                                                                                  
                                                                         

















                                                                                  
                                                                                                              
 
                                                        








                                                  
                                                                 
















                                                   
                                                                                                                                                         
 
                                                        









                                                          
                                                                         

















                                                          
                                                                                                                    
 
                                                        








                                                                          
                                                                 

















                                                                          
                                                                                                                                                               
 
                                                        









                                                                                  
                                                                         










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

    tilemap.c

    Generic tilemap management system.

    Copyright Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

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

#include "driver.h"
#include "tilemap.h"
#include "profiler.h"
#include "deprecat.h"


/***************************************************************************
    CONSTANTS
***************************************************************************/

/* internal usage to mark tiles dirty */
#define TILE_FLAG_DIRTY					0xff

/* invalid logical index */
#define INVALID_LOGICAL_INDEX			((tilemap_logical_index)~0)

/* maximum index in each array */
#define MAX_PEN_TO_FLAGS				256


/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

/* logical index */
typedef UINT32 tilemap_logical_index;


/* internal set of transparency states for rendering */
typedef enum
{
	WHOLLY_TRANSPARENT,
	WHOLLY_OPAQUE,
	MASKED
} trans_t;


/* internal blitting callbacks */
typedef void (*blitmask_t)(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode);
typedef void (*blitopaque_t)(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode);


/* blitting parameters for rendering */
typedef struct _blit_parameters blit_parameters;
struct _blit_parameters
{
	mame_bitmap *		bitmap;
	rectangle			cliprect;
	blitmask_t 			draw_masked;
	blitopaque_t 		draw_opaque;
	UINT32 				tilemap_priority_code;
	UINT8				mask;
	UINT8				value;
};


/* core tilemap structure */
struct _tilemap
{
	tilemap *					next;				/* pointer to next tilemap */

	/* basic tilemap metrics */
	UINT32						rows;				/* number of tile rows */
	UINT32						cols;				/* number of tile columns */
	UINT32						tilewidth;			/* width of a single tile in pixels */
	UINT32						tileheight;			/* height of a single tile in pixels */
	UINT32						width;				/* width of the full tilemap in pixels */
	UINT32						height;				/* height of the full tilemap in pixels */

	/* logical <-> memory mappings */
	tilemap_mapper_callback		mapper;				/* callback to map a row/column to a memory index */
	tilemap_logical_index *		memory_to_logical;	/* map from memory index to logical index */
	tilemap_logical_index		max_logical_index;	/* maximum valid logical index */
	tilemap_memory_index *		logical_to_memory;	/* map from logical index to memory index */
	tilemap_memory_index		max_memory_index;	/* maximum valid memory index */

	/* callback to interpret video RAM for the tilemap */
	tile_get_info_callback		tile_get_info;		/* callback to get information about a tile */
	tile_data					tileinfo;			/* structure to hold the data for a tile */
	void *						user_data;			/* user data value passed to the callback */

	/* global tilemap states */
	UINT8						enable;				/* true if we are enabled */
	UINT8						attributes;			/* global attributes (flipx/y) */
	UINT8						all_tiles_dirty;	/* true if all tiles are dirty */
	UINT8						all_tiles_clean;	/* true if all tiles are clean */
	UINT32						palette_offset;		/* palette offset */

	/* scroll information */
	UINT32						scrollrows;			/* number of independently scrolled rows */
	UINT32						scrollcols;			/* number of independently scrolled colums */
	INT32 *						rowscroll;			/* array of rowscroll values */
	INT32 *						colscroll;			/* array of colscroll values */
	INT32 						dx;					/* global horizontal scroll offset */
	INT32						dx_flipped;			/* global horizontal scroll offset when flipped */
	INT32 						dy;					/* global vertical scroll offset */
	INT32						dy_flipped;			/* global vertical scroll offset when flipped */

	/* pixel data */
	mame_bitmap *				pixmap;				/* cached pixel data */

	/* transparency mapping */
	mame_bitmap *				flagsmap;			/* per-pixel flags */
	UINT8 *						tileflags;			/* per-tile flags */
	UINT8 *						pen_to_flags; 		/* mapping of pens to flags */
};



/***************************************************************************
    GLOBAL VARIABLES
***************************************************************************/

mame_bitmap *			priority_bitmap;

static tilemap *		tilemap_list;
static tilemap **		tilemap_tailptr;
static int				tilemap_instance;

static UINT32			screen_width, screen_height;



/***************************************************************************
    FUNCTION PROTOTYPES
***************************************************************************/

/* system management helpers */
static void tilemap_exit(running_machine *machine);
static void tilemap_postload(void *param);
static void tilemap_dispose(tilemap *tmap);

/* logical <-> memory index mapping */
static void mappings_create(tilemap *tmap);
static void mappings_update(tilemap *tmap);

/* tile rendering */
static void pixmap_update(tilemap *tmap, const rectangle *cliprect);
static void tile_update(tilemap *tmap, tilemap_logical_index logindex, UINT32 cached_col, UINT32 cached_row);
static UINT8 tile_draw(tilemap *tmap, const UINT8 *pendata, UINT32 x0, UINT32 y0, UINT32 palette_base, UINT8 category, UINT8 group, UINT8 flags);
static UINT8 tile_apply_bitmask(tilemap *tmap, const UINT8 *maskdata, UINT32 x0, UINT32 y0, UINT8 category, UINT8 flags);

/* drawing helpers */
static void configure_blit_parameters(blit_parameters *blit, tilemap *tmap, mame_bitmap *dest, const rectangle *cliprect, UINT32 flags, UINT8 priority, UINT8 priority_mask);
static void tilemap_draw_instance(tilemap *tmap, const blit_parameters *blit, int xpos, int ypos);
static void tilemap_draw_roz_core(tilemap *tmap, const blit_parameters *blit,
		UINT32 startx, UINT32 starty, int incxx, int incxy, int incyx, int incyy, int wraparound);

/* scanline rasterizers for drawing to the pixmap */
static void scanline_draw_opaque_null(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_masked_null(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_opaque_ind16(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_masked_ind16(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_opaque_rgb16(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_masked_rgb16(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_opaque_rgb16_alpha(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_masked_rgb16_alpha(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_opaque_rgb32(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_masked_rgb32(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_opaque_rgb32_alpha(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode);
static void scanline_draw_masked_rgb32_alpha(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode);



/***************************************************************************
    INLINE FUNCTIONS
***************************************************************************/

/*-------------------------------------------------
    effective_rowscroll - return the effective
    rowscroll value for a given index, taking into
    account tilemap flip states
-------------------------------------------------*/

INLINE INT32 effective_rowscroll(tilemap *tmap, int index)
{
	INT32 value;

	/* if we're flipping vertically, adjust the row number */
	if (tmap->attributes & TILEMAP_FLIPY)
		index = tmap->scrollrows - 1 - index;

	/* adjust final result based on the horizontal flip and dx values */
	if (!(tmap->attributes & TILEMAP_FLIPX))
		value = tmap->dx - tmap->rowscroll[index];
	else
		value = screen_width - tmap->width - (tmap->dx_flipped - tmap->rowscroll[index]);

	/* clamp to 0..width */
	if (value < 0)
		value = tmap->width - (-value) % tmap->width;
	else
		value %= tmap->width;
	return value;
}


/*-------------------------------------------------
    effective_colscroll - return the effective
    colscroll value for a given index, taking into
    account tilemap flip states
-------------------------------------------------*/

INLINE INT32 effective_colscroll(tilemap *tmap, int index)
{
	INT32 value;

	/* if we're flipping horizontally, adjust the column number */
	if (tmap->attributes & TILEMAP_FLIPX)
		index = tmap->scrollcols - 1 - index;

	/* adjust final result based on the vertical flip and dx values */
	if (!(tmap->attributes & TILEMAP_FLIPY))
		value = tmap->dy - tmap->colscroll[index];
	else
		value = screen_height - tmap->height - (tmap->dy_flipped - tmap->colscroll[index]);

	/* clamp to 0..height */
	if (value < 0)
		value = tmap->height - (-value) % tmap->height;
	else
		value %= tmap->height;
	return value;
}


/*-------------------------------------------------
    indexed_tilemap - return a tilemap by index
-------------------------------------------------*/

INLINE tilemap *indexed_tilemap(int index)
{
	tilemap *tmap;

	/* find by the tilemap index */
	for (tmap = tilemap_list; tmap != NULL; tmap = tmap->next)
		if (index-- == 0)
			return tmap;

	return NULL;
}



/***************************************************************************
    SYSTEM-WIDE MANAGEMENT
***************************************************************************/

/*-------------------------------------------------
    tilemap_init - initialize the tilemap system
-------------------------------------------------*/

void tilemap_init(running_machine *machine)
{
	screen_width	= machine->screen[0].width;
	screen_height	= machine->screen[0].height;

	if (screen_width != 0 && screen_height != 0)
	{
		tilemap_list	 = NULL;
		tilemap_tailptr  = &tilemap_list;
		tilemap_instance = 0;

		priority_bitmap = auto_bitmap_alloc(screen_width, screen_height, BITMAP_FORMAT_INDEXED8);
		add_exit_callback(machine, tilemap_exit);
	}
}



/***************************************************************************
    TILEMAP CREATION AND CONFIGURATION
***************************************************************************/

/*-------------------------------------------------
    tilemap_create - allocate a new tilemap
-------------------------------------------------*/

tilemap *tilemap_create(tile_get_info_callback tile_get_info, tilemap_mapper_callback mapper, int tilewidth, int tileheight, int cols, int rows)
{
	tilemap *tmap;
	int group;

	/* allocate the tilemap itself */
	tmap = malloc_or_die(sizeof(tilemap));
	memset(tmap, 0, sizeof(tilemap));

	/* fill in the basic metrics */
	tmap->rows = rows;
	tmap->cols = cols;
	tmap->tilewidth = tilewidth;
	tmap->tileheight = tileheight;
	tmap->width = cols * tilewidth;
	tmap->height = rows * tileheight;

	/* set up the logical <-> memory mappings */
	tmap->mapper = mapper;
	mappings_create(tmap);

	/* set up the tile map callbacks */
	tmap->tile_get_info = tile_get_info;

	/* initialize global states */
	tmap->enable = TRUE;
	tmap->all_tiles_dirty = TRUE;

	/* initialize scroll information */
	tmap->scrollrows = 1;
	tmap->scrollcols = 1;
	tmap->rowscroll = malloc_or_die(tmap->height * sizeof(*tmap->rowscroll));
	memset(tmap->rowscroll, 0, tmap->height * sizeof(*tmap->rowscroll));
	tmap->colscroll = malloc_or_die(tmap->width * sizeof(*tmap->colscroll));
	memset(tmap->colscroll, 0, tmap->width * sizeof(*tmap->colscroll));

	/* allocate the pixel data cache */
	tmap->pixmap = bitmap_alloc(tmap->width, tmap->height, BITMAP_FORMAT_INDEXED16);

	/* allocate transparency mapping data */
	tmap->tileflags = malloc_or_die(tmap->max_logical_index);
	tmap->flagsmap = bitmap_alloc(tmap->width, tmap->height, BITMAP_FORMAT_INDEXED8);
	tmap->pen_to_flags = malloc_or_die(sizeof(tmap->pen_to_flags[0]) * MAX_PEN_TO_FLAGS * TILEMAP_NUM_GROUPS);
	for (group = 0; group < TILEMAP_NUM_GROUPS; group++)
		tilemap_map_pens_to_layer(tmap, group, 0, 0, TILEMAP_PIXEL_LAYER0);

	/* add us to the end of the list of tilemaps */
	*tilemap_tailptr = tmap;
	tilemap_tailptr = &tmap->next;

	/* save relevant state */
	state_save_register_item("tilemap", tilemap_instance, tmap->enable);
	state_save_register_item("tilemap", tilemap_instance, tmap->attributes);
	state_save_register_item("tilemap", tilemap_instance, tmap->palette_offset);
	state_save_register_item("tilemap", tilemap_instance, tmap->scrollrows);
	state_save_register_item("tilemap", tilemap_instance, tmap->scrollcols);
	state_save_register_item_pointer("tilemap", tilemap_instance, tmap->rowscroll, rows * tileheight);
	state_save_register_item_pointer("tilemap", tilemap_instance, tmap->colscroll, cols * tilewidth);
	state_save_register_item("tilemap", tilemap_instance, tmap->dx);
	state_save_register_item("tilemap", tilemap_instance, tmap->dx_flipped);
	state_save_register_item("tilemap", tilemap_instance, tmap->dy);
	state_save_register_item("tilemap", tilemap_instance, tmap->dy_flipped);
	tilemap_instance++;

	/* reset everything after a load */
	state_save_register_func_postload_ptr(tilemap_postload, tmap);
	return tmap;
}


/*-------------------------------------------------
    tilemap_set_user_data - specify a parameter
    to be passed into the tile_get_info callback
-------------------------------------------------*/

void tilemap_set_user_data(tilemap *tmap, void *user_data)
{
	tmap->user_data = user_data;
}


/*-------------------------------------------------
    tilemap_set_palette_offset - specify an offset
    to be added to each pixel before looking up
    the palette
-------------------------------------------------*/

void tilemap_set_palette_offset(tilemap *tmap, UINT32 offset)
{
	if (tmap->palette_offset != offset)
	{
		tmap->palette_offset = offset;
		tilemap_mark_all_tiles_dirty(tmap);
	}
}


/*-------------------------------------------------
    tilemap_set_enable - set an enable flag for
    the tilemap; if 0, requests to draw the
    tilemap are ignored
-------------------------------------------------*/

void tilemap_set_enable(tilemap *tmap, int enable)
{
	tmap->enable = (enable != 0);
}


/*-------------------------------------------------
    tilemap_set_flip - set a global flip for the
    tilemap; ALL_TILEMAPS can be passed here as
    well
-------------------------------------------------*/

void tilemap_set_flip(tilemap *tmap, UINT32 attributes)
{
	/* handle ALL_TILEMAPS */
	if (tmap == ALL_TILEMAPS)
	{
		for (tmap = tilemap_list; tmap != NULL; tmap = tmap->next)
			tilemap_set_flip(tmap, attributes);
	}

	/* if we're changing things, force a refresh of the mappings and mark it all dirty */
	else if (tmap->attributes != attributes)
	{
		tmap->attributes = attributes;
		mappings_update(tmap);
	}
}



/***************************************************************************
    DIRTY TILE MARKING
***************************************************************************/

/*-------------------------------------------------
    tilemap_mark_tile_dirty - mark a single tile
    dirty based on its memory index
-------------------------------------------------*/

void tilemap_mark_tile_dirty(tilemap *tmap, tilemap_memory_index memindex)
{
	/* only mark if within range */
	if (memindex < tmap->max_memory_index)
	{
		tilemap_logical_index logindex = tmap->memory_to_logical[memindex];

		/* there may be no logical index for a given memory index */
		if (logindex != INVALID_LOGICAL_INDEX)
		{
			tmap->tileflags[logindex] = TILE_FLAG_DIRTY;
			tmap->all_tiles_clean = FALSE;
		}
	}
}


/*-------------------------------------------------
    tilemap_mark_all_tiles_dirty - mark all the
    tiles in a tilemap dirty
-------------------------------------------------*/

void tilemap_mark_all_tiles_dirty(tilemap *tmap)
{
	/* handle ALL_TILEMAPS */
	if (tmap == ALL_TILEMAPS)
	{
		for (tmap = tilemap_list; tmap != NULL; tmap = tmap->next)
			tilemap_mark_all_tiles_dirty(tmap);
	}

	/* mark all tiles dirty and clear the clean flag */
	else
	{
		tmap->all_tiles_dirty = TRUE;
		tmap->all_tiles_clean = FALSE;
	}
}



/***************************************************************************
    PEN-TO-LAYER MAPPING
***************************************************************************/

/*-------------------------------------------------
    tilemap_map_pens_to_layer - specify the
    mapping of one or more pens (where
    (<pen> & mask) == pen) to a layer
-------------------------------------------------*/

void tilemap_map_pens_to_layer(tilemap *tmap, int group, pen_t pen, pen_t mask, UINT8 layermask)
{
	UINT8 *array = tmap->pen_to_flags + group * MAX_PEN_TO_FLAGS;
	pen_t start, stop, cur;
	UINT8 changed = FALSE;

	assert(group < TILEMAP_NUM_GROUPS);
	assert((layermask & TILEMAP_PIXEL_CATEGORY_MASK) == 0);

	/* we start at the index where (pen & mask) == pen, and all other bits are 0 */
	start = pen & mask;

	/* we stop at the index where (pen & mask) == pen, and all other bits are 1 */
	stop = start | ~mask;

	/* clamp to the number of entries actually there */
	stop = MIN(stop, MAX_PEN_TO_FLAGS - 1);

	/* iterate and set */
	for (cur = start; cur <= stop; cur++)
		if ((cur & mask) == pen && array[cur] != layermask)
		{
			changed = TRUE;
			array[cur] = layermask;
		}

	/* everything gets dirty if anything changed */
	if (changed)
		tilemap_mark_all_tiles_dirty(tmap);
}


/*-------------------------------------------------
    tilemap_set_transparent_pen - set a single
    transparent pen into the tilemap, mapping
    all other pens to layer 0
-------------------------------------------------*/

void tilemap_set_transparent_pen(tilemap *tmap, pen_t pen)
{
	/* reset the whole pen map to opaque */
	tilemap_map_pens_to_layer(tmap, 0, 0, 0, TILEMAP_PIXEL_LAYER0);

	/* set the single pen to transparent */
    tilemap_map_pen_to_layer(tmap, 0, pen, TILEMAP_PIXEL_TRANSPARENT);
}


/*-------------------------------------------------
    tilemap_set_transmask - set up the first 32
    pens using a foreground mask (mapping to
    layer 0) and a background mask (mapping to
    layer 1)
-------------------------------------------------*/

void tilemap_set_transmask(tilemap *tmap, int group, UINT32 fgmask, UINT32 bgmask)
{
	pen_t pen;

	/* iterate over all 32 pens specified */
	for (pen = 0; pen < 32; pen++)
	{
		UINT8 fgbits = ((fgmask >> pen) & 1) ? TILEMAP_PIXEL_TRANSPARENT : TILEMAP_PIXEL_LAYER0;
		UINT8 bgbits = ((bgmask >> pen) & 1) ? TILEMAP_PIXEL_TRANSPARENT : TILEMAP_PIXEL_LAYER1;
		tilemap_map_pen_to_layer(tmap, group, pen, fgbits | bgbits);
	}
}



/***************************************************************************
    TILEMAP SCROLLING
***************************************************************************/

/*-------------------------------------------------
    tilemap_set_scroll_rows - specify the number of
    independently scrollable row units; each unit
    covers height/scroll_rows pixels
-------------------------------------------------*/

void tilemap_set_scroll_rows(tilemap *tmap, UINT32 scroll_rows)
{
	assert(scroll_rows <= tmap->height);
	tmap->scrollrows = scroll_rows;
}


/*-------------------------------------------------
    tilemap_set_scroll_cols - specify the number of
    independently scrollable column units; each
    unit covers width/scroll_cols pixels
-------------------------------------------------*/

void tilemap_set_scroll_cols(tilemap *tmap, UINT32 scroll_cols)
{
	assert(scroll_cols <= tmap->width);
	tmap->scrollcols = scroll_cols;
}


/*-------------------------------------------------
    tilemap_set_scrolldx - specify global
    horizontal scroll offset, for non-flipped and
    flipped cases
-------------------------------------------------*/

void tilemap_set_scrolldx(tilemap *tmap, int dx, int dx_flipped)
{
	tmap->dx = dx;
	tmap->dx_flipped = dx_flipped;
}


/*-------------------------------------------------
    tilemap_set_scrolldy - specify global
    vertical scroll offset, for non-flipped and
    flipped cases
-------------------------------------------------*/

void tilemap_set_scrolldy(tilemap *tmap, int dy, int dy_flipped)
{
	tmap->dy = dy;
	tmap->dy_flipped = dy_flipped;
}


/*-------------------------------------------------
    tilemap_get_scrolldx - return the global
    horizontal scroll offset, based on current
    flip state
-------------------------------------------------*/

int tilemap_get_scrolldx(tilemap *tmap)
{
	return (tmap->attributes & TILEMAP_FLIPX) ? tmap->dx_flipped : tmap->dx;
}


/*-------------------------------------------------
    tilemap_get_scrolldy - return the global
    vertical scroll offset, based on current
    flip state
-------------------------------------------------*/

int tilemap_get_scrolldy(tilemap *tmap)
{
	return (tmap->attributes & TILEMAP_FLIPY) ? tmap->dy_flipped : tmap->dy;
}


/*-------------------------------------------------
    tilemap_set_scrollx - specify the scroll value
    for a row unit
-------------------------------------------------*/

void tilemap_set_scrollx(tilemap *tmap, int which, int value)
{
	if (which < tmap->scrollrows)
		tmap->rowscroll[which] = value;
}


/*-------------------------------------------------
    tilemap_set_scrolly - specify the scroll value
    for a column unit
-------------------------------------------------*/

void tilemap_set_scrolly(tilemap *tmap, int which, int value)
{
	if (which < tmap->scrollcols)
		tmap->colscroll[which] = value;
}


/*-------------------------------------------------
    tilemap_get_scrollx - return the scroll value
    for a row unit
-------------------------------------------------*/

int tilemap_get_scrollx(tilemap *tmap, int which)
{
	if (which < tmap->scrollrows)
		return tmap->rowscroll[which];
	else
		return 0;
}


/*-------------------------------------------------
    tilemap_get_scrolly - return the scroll value
    for a column unit
-------------------------------------------------*/

int tilemap_get_scrolly(tilemap *tmap, int which)
{
	if (which < tmap->scrollcols)
		return tmap->colscroll[which];
	else
		return 0;
}



/***************************************************************************
    INTERNAL MAP ACCESS
***************************************************************************/

/*-------------------------------------------------
    tilemap_get_pixmap - return a pointer to the
    (updated) internal pixmap for a tilemap
-------------------------------------------------*/

mame_bitmap *tilemap_get_pixmap(tilemap *tmap)
{
	/* ensure all the tiles are up-to-date and then return the pixmap */
	pixmap_update(tmap, NULL);
	return tmap->pixmap;
}


/*-------------------------------------------------
    tilemap_get_flagsmap - return a pointer to the
    (updated) internal flagsmap for a tilemap
-------------------------------------------------*/

mame_bitmap *tilemap_get_flagsmap(tilemap *tmap)
{
	/* ensure all the tiles are up-to-date and then return the pixmap */
	pixmap_update(tmap, NULL);
	return tmap->flagsmap;
}


/*-------------------------------------------------
    tilemap_get_pixmap - return a pointer to the
    (updated) internal per-tile flags for a tilemap
-------------------------------------------------*/

UINT8 *tilemap_get_tile_flags(tilemap *tmap)
{
	/* ensure all the tiles are up-to-date and then return the pixmap */
	pixmap_update(tmap, NULL);
	return tmap->tileflags;
}



/***************************************************************************
    TILEMAP RENDERING
***************************************************************************/

/*-------------------------------------------------
    tilemap_draw_primask - draw a tilemap to the
    destination with clipping; pixels apply
    priority/priority_mask to the priority bitmap
-------------------------------------------------*/

void tilemap_draw_primask(mame_bitmap *dest, const rectangle *cliprect, tilemap *tmap, UINT32 flags, UINT8 priority, UINT8 priority_mask)
{
	rectangle original_cliprect;
	blit_parameters blit;
	int xpos, ypos;

	/* skip if disabled */
	if (!tmap->enable)
		return;

profiler_mark(PROFILER_TILEMAP_DRAW);
	/* configure the blit parameters based on the input parameters */
	configure_blit_parameters(&blit, tmap, dest, cliprect, flags, priority, priority_mask);
	original_cliprect = blit.cliprect;

	/* if the whole map is dirty, mark it as such */
	if (tmap->all_tiles_dirty)
	{
		memset(tmap->tileflags, TILE_FLAG_DIRTY, tmap->max_logical_index);
		tmap->all_tiles_dirty = FALSE;
	}

	/* XY scrolling playfield */
	if (tmap->scrollrows == 1 && tmap->scrollcols == 1)
	{
		int scrollx = effective_rowscroll(tmap, 0);
		int scrolly = effective_colscroll(tmap, 0);

		/* iterate to handle wraparound */
		for (ypos = scrolly - tmap->height; ypos <= blit.cliprect.max_y; ypos += tmap->height)
			for (xpos = scrollx - tmap->width; xpos <= blit.cliprect.max_x; xpos += tmap->width)
				tilemap_draw_instance(tmap, &blit, xpos, ypos);
	}

	/* scrolling rows + vertical scroll */
	else if (tmap->scrollcols == 1)
	{
		const rectangle original_cliprect = blit.cliprect;
		int rowheight = tmap->height / tmap->scrollrows;
		int scrolly = effective_colscroll(tmap, 0);
		int currow, nextrow;

		/* iterate over rows in the tilemap */
		for (currow = 0; currow < tmap->scrollrows; currow = nextrow)
		{
			int scrollx = effective_rowscroll(tmap, currow);

			/* scan forward until we find a non-matching row */
			for (nextrow = currow + 1; nextrow < tmap->scrollrows; nextrow++)
				if (effective_rowscroll(tmap, nextrow) != scrollx)
					break;

 			/* skip if disabled */
			if (scrollx == TILE_LINE_DISABLED)
				continue;

			/* iterate over Y to handle wraparound */
			for (ypos = scrolly - tmap->height; ypos <= original_cliprect.max_y; ypos += tmap->height)
			{
				/* update the cliprect just for this set of rows */
				blit.cliprect.min_y = currow * rowheight + ypos;
				blit.cliprect.max_y = nextrow * rowheight - 1 + ypos;
				sect_rect(&blit.cliprect, &original_cliprect);

				/* iterate over X to handle wraparound */
				for (xpos = scrollx - tmap->width; xpos <= original_cliprect.max_x; xpos += tmap->width)
					tilemap_draw_instance(tmap, &blit, xpos, ypos);
			}
		}
	}

	/* scrolling columns + horizontal scroll */
	else if (tmap->scrollrows == 1)
	{
		const rectangle original_cliprect = blit.cliprect;
		int colwidth = tmap->width / tmap->scrollcols;
		int scrollx = effective_rowscroll(tmap, 0);
		int curcol, nextcol;

		/* iterate over columns in the tilemap */
		for (curcol = 0; curcol < tmap->scrollcols; curcol = nextcol)
		{
			int scrolly	= effective_colscroll(tmap, curcol);

			/* scan forward until we find a non-matching column */
			for (nextcol = curcol + 1; nextcol < tmap->scrollcols; nextcol++)
				if (effective_colscroll(tmap, nextcol) != scrolly)
					break;

 			/* skip if disabled */
			if (scrolly == TILE_LINE_DISABLED)
				continue;

			/* iterate over X to handle wraparound */
			for (xpos = scrollx - tmap->width; xpos <= original_cliprect.max_x; xpos += tmap->width)
			{
				/* update the cliprect just for this set of columns */
				blit.cliprect.min_x = curcol * colwidth + xpos;
				blit.cliprect.max_x = nextcol * colwidth - 1 + xpos;
				sect_rect(&blit.cliprect, &original_cliprect);

				/* iterate over Y to handle wraparound */
				for (ypos = scrolly - tmap->height; ypos <= original_cliprect.max_y; ypos += tmap->height)
					tilemap_draw_instance(tmap, &blit, xpos, ypos);
			}
		}
	}
profiler_mark(PROFILER_END);
}


/*-------------------------------------------------
    tilemap_draw_primask - draw a tilemap to the
    destination with clipping and arbitrary
    rotate/zoom; pixels apply priority/
    priority_mask to the priority bitmap
-------------------------------------------------*/

void tilemap_draw_roz_primask(mame_bitmap *dest, const rectangle *cliprect, tilemap *tmap,
		UINT32 startx, UINT32 starty, int incxx, int incxy, int incyx, int incyy,
		int wraparound, UINT32 flags, UINT8 priority, UINT8 priority_mask)
{
	blit_parameters blit;

/* notes:
   - startx and starty MUST be UINT32 for calculations to work correctly
   - srcbitmap->width and height are assumed to be a power of 2 to speed up wraparound
   */

	/* skip if disabled */
	if (!tmap->enable)
		return;

	/* see if this is just a regular render and if so, do a regular render */
	if (incxx == (1 << 16) && incxy == 0 && incyx == 0 && incyy == (1 << 16) && wraparound)
	{
		tilemap_set_scrollx(tmap, 0, startx >> 16);
		tilemap_set_scrolly(tmap, 0, starty >> 16);
		tilemap_draw(dest, cliprect, tmap, flags, priority);
		return;
	}

profiler_mark(PROFILER_TILEMAP_DRAW_ROZ);
	/* configure the blit parameters */
	configure_blit_parameters(&blit, tmap, dest, cliprect, flags, priority, priority_mask);

	/* get the full pixmap for the tilemap */
	tilemap_get_pixmap(tmap);

	/* then do the roz copy */
	tilemap_draw_roz_core(tmap, &blit, startx, starty, incxx, incxy, incyx, incyy, wraparound);
profiler_mark(PROFILER_END);
}



/***************************************************************************
    INDEXED TILEMAP HANDLING
***************************************************************************/

/*-------------------------------------------------
    tilemap_count - return the number of tilemaps
-------------------------------------------------*/

int tilemap_count(void)
{
	tilemap *tmap;
	int count = 0;

	/* find by the tilemap index */
	for (tmap = tilemap_list; tmap != NULL; tmap = tmap->next)
		count++;
	return count;
}


/*-------------------------------------------------
    tilemap_size_by_index - return the size of an
    indexed tilemap
-------------------------------------------------*/

void tilemap_size_by_index(int number, UINT32 *width, UINT32 *height)
{
	tilemap *tmap = indexed_tilemap(number);
	*width = tmap->width;
	*height = tmap->height;
}


/*-------------------------------------------------
    tilemap_draw_by_index - render an indexed
    tilemap with fixed characteristics (no
    priority)
-------------------------------------------------*/

void tilemap_draw_by_index(mame_bitmap *dest, int number, UINT32 scrollx, UINT32 scrolly)
{
	tilemap *tmap = indexed_tilemap(number);
	blit_parameters blit;
	int xpos,ypos;

	/* set up for the blit, using hard-coded parameters (no priority, etc) */
	configure_blit_parameters(&blit, tmap, dest, NULL, TILEMAP_DRAW_OPAQUE, 0, 0xff);

	/* compute the effective scroll positions */
	scrollx = tmap->width  - scrollx % tmap->width;
	scrolly = tmap->height - scrolly % tmap->height;

	/* if the whole map is dirty, mark it as such */
	if (tmap->all_tiles_dirty)
	{
		memset(tmap->tileflags, TILE_FLAG_DIRTY, tmap->max_logical_index);
		tmap->all_tiles_dirty = FALSE;
	}

	/* iterate to handle wraparound */
	for (ypos = scrolly - tmap->height; ypos <= blit.cliprect.max_y; ypos += tmap->height)
		for (xpos = scrollx - tmap->width; xpos <= blit.cliprect.max_x; xpos += tmap->width)
			tilemap_draw_instance(tmap, &blit, xpos, ypos);
}



/***************************************************************************
    COMMON LOGICAL-TO-MEMORY MAPPERS
***************************************************************************/

/*-------------------------------------------------
    tilemap_scan_rows
    tilemap_scan_rows_flip_x
    tilemap_scan_rows_flip_y
    tilemap_scan_rows_flip_xy - scan in row-major
    order with optional flipping
-------------------------------------------------*/

TILEMAP_MAPPER( tilemap_scan_rows )
{
	return row * num_cols + col;
}

TILEMAP_MAPPER( tilemap_scan_rows_flip_x )
{
	return row * num_cols + (num_cols - 1 - col);
}

TILEMAP_MAPPER( tilemap_scan_rows_flip_y )
{
	return (num_rows - 1 - row) * num_cols + col;
}

TILEMAP_MAPPER( tilemap_scan_rows_flip_xy )
{
	return (num_rows - 1 - row) * num_cols + (num_cols - 1 - col);
}


/*-------------------------------------------------
    tilemap_scan_cols
    tilemap_scan_cols_flip_x
    tilemap_scan_cols_flip_y
    tilemap_scan_cols_flip_xy - scan in column-
    major order with optional flipping
-------------------------------------------------*/

TILEMAP_MAPPER( tilemap_scan_cols )
{
	return col * num_rows + row;
}

TILEMAP_MAPPER( tilemap_scan_cols_flip_x )
{
	return (num_cols - 1 - col) * num_rows + row;
}

TILEMAP_MAPPER( tilemap_scan_cols_flip_y )
{
	return col * num_rows + (num_rows - 1 - row);
}

TILEMAP_MAPPER( tilemap_scan_cols_flip_xy )
{
	return (num_cols - 1 - col) * num_rows + (num_rows - 1 - row);
}



/***************************************************************************
    SYSTEM MANAGEMENT HELPERS
***************************************************************************/

/*-------------------------------------------------
    tilemap_exit - free memory allocated by the
    tilemap system
-------------------------------------------------*/

static void tilemap_exit(running_machine *machine)
{
	/* free all the tilemaps in the list */
	while (tilemap_list != NULL)
	{
		tilemap *next = tilemap_list->next;
		tilemap_dispose(tilemap_list);
		tilemap_list = next;
	}
	tilemap_tailptr = NULL;
}


/*-------------------------------------------------
    tilemap_postload - after loading a save state
    invalidate everything
-------------------------------------------------*/

static void tilemap_postload(void *param)
{
	/* recompute the mappings for this tilemap */
	tilemap *tmap = param;
	mappings_update(tmap);
}


/*-------------------------------------------------
    tilemap_dispose - dispose of a tilemap
-------------------------------------------------*/

static void tilemap_dispose(tilemap *tmap)
{
	tilemap **tmapptr;

	/* walk the list of tilemaps; when we find ourself, remove it */
	for (tmapptr = &tilemap_list; *tmapptr != NULL; tmapptr = &(*tmapptr)->next)
		if (*tmapptr == tmap)
		{
			*tmapptr = tmap->next;
			break;
		}

	/* free allocated memory */
	free(tmap->pen_to_flags);
	free(tmap->tileflags);
	bitmap_free(tmap->flagsmap);
	bitmap_free(tmap->pixmap);
	free(tmap->colscroll);
	free(tmap->rowscroll);
	free(tmap->logical_to_memory);
	free(tmap->memory_to_logical);
	free(tmap);
}



/***************************************************************************
    LOGICAL <-> MEMORY INDEX MAPPING
***************************************************************************/

/*-------------------------------------------------
    mappings_create - allocate memory for the
    mapping tables and compute their extents
-------------------------------------------------*/

static void mappings_create(tilemap *tmap)
{
	UINT32 col, row;

	/* compute the maximum logical index */
	tmap->max_logical_index = tmap->rows * tmap->cols;

	/* compute the maximum memory index */
	tmap->max_memory_index = 0;
	for (row = 0; row < tmap->rows; row++)
		for (col = 0; col < tmap->cols; col++)
		{
			tilemap_memory_index memindex = (*tmap->mapper)(col, row, tmap->cols, tmap->rows);
			tmap->max_memory_index = MAX(tmap->max_memory_index, memindex);
		}
	tmap->max_memory_index++;

	/* allocate the necessary mappings */
	tmap->memory_to_logical = malloc_or_die(tmap->max_memory_index * sizeof(*tmap->memory_to_logical));
	tmap->logical_to_memory = malloc_or_die(tmap->max_logical_index * sizeof(*tmap->logical_to_memory));

	/* update the mappings */
	mappings_update(tmap);
}


/*-------------------------------------------------
    mappings_update - update the mappings after
    a major change (flip x/y changes)
-------------------------------------------------*/

static void mappings_update(tilemap *tmap)
{
	tilemap_logical_index logindex;
	tilemap_memory_index memindex;

	/* initialize all the mappings to invalid values */
	for (memindex = 0; memindex < tmap->max_memory_index; memindex++)
		tmap->memory_to_logical[memindex] = -1;

	/* now iterate over all logical indexes and populate the memory index */
	for (logindex = 0; logindex < tmap->max_logical_index; logindex++)
	{
		UINT32 logical_col = logindex % tmap->cols;
		UINT32 logical_row = logindex / tmap->cols;
		tilemap_memory_index memindex = (*tmap->mapper)(logical_col, logical_row, tmap->cols, tmap->rows);
		UINT32 flipped_logindex;

		/* apply tilemap flip to get the final location to store */
		if (tmap->attributes & TILEMAP_FLIPX)
			logical_col = (tmap->cols - 1) - logical_col;
		if (tmap->attributes & TILEMAP_FLIPY)
			logical_row = (tmap->rows - 1) - logical_row;
		flipped_logindex = logical_row * tmap->cols + logical_col;

		/* fill in entries in both arrays */
		tmap->memory_to_logical[memindex] = flipped_logindex;
		tmap->logical_to_memory[flipped_logindex] = memindex;
	}

	/* mark the whole tilemap dirty */
	tilemap_mark_all_tiles_dirty(tmap);
}



/***************************************************************************
    TILE RENDERING
***************************************************************************/

/*-------------------------------------------------
    pixmap_update - update the portion of the
    pixmap described by the cliprect
-------------------------------------------------*/

static void pixmap_update(tilemap *tmap, const rectangle *cliprect)
{
	int mincol, maxcol, minrow, maxrow;
	int row, col;

	/* if everything is clean, do nothing */
	if (tmap->all_tiles_clean)
		return;

profiler_mark(PROFILER_TILEMAP_DRAW);

	/* compute which columns and rows to update */
	if (cliprect != NULL)
	{
		mincol = cliprect->min_x / tmap->tilewidth;
		maxcol = cliprect->max_x / tmap->tilewidth;
		minrow = cliprect->min_y / tmap->tileheight;
		maxrow = cliprect->max_y / tmap->tileheight;
	}
	else
	{
		mincol = minrow = 0;
		maxcol = tmap->cols - 1;
		maxrow = tmap->rows - 1;
	}

	/* if the whole map is dirty, mark it as such */
	if (tmap->all_tiles_dirty)
	{
		memset(tmap->tileflags, TILE_FLAG_DIRTY, tmap->max_logical_index);
		tmap->all_tiles_dirty = FALSE;
	}

	/* iterate over rows */
	for (row = minrow; row <= maxrow; row++)
	{
		tilemap_logical_index logindex = row * tmap->cols;

		/* iterate over colums */
		for (col = mincol; col <= maxcol; col++)
			if (tmap->tileflags[logindex + col] == TILE_FLAG_DIRTY)
				tile_update(tmap, logindex + col, col, row);
	}

	/* mark it all clean */
	if (mincol == 0 && minrow == 0 && maxcol == tmap->cols - 1 && maxcol == tmap->rows - 1)
		tmap->all_tiles_clean = TRUE;

profiler_mark(PROFILER_END);
}


/*-------------------------------------------------
    tile_update - update a single dirty tile
-------------------------------------------------*/

static void tile_update(tilemap *tmap, tilemap_logical_index logindex, UINT32 col, UINT32 row)
{
	UINT32 x0 = tmap->tilewidth * col;
	UINT32 y0 = tmap->tileheight * row;
	tilemap_memory_index memindex;
	UINT32 flags;

profiler_mark(PROFILER_TILEMAP_UPDATE);

	/* call the get info callback for the associated memory index */
	memindex = tmap->logical_to_memory[logindex];
	(*tmap->tile_get_info)(Machine, &tmap->tileinfo, memindex, tmap->user_data);

	/* apply the global tilemap flip to the returned flip flags */
	flags = tmap->tileinfo.flags ^ (tmap->attributes & 0x03);

	/* draw the tile, using either direct or transparent */
	tmap->tileflags[logindex] = tile_draw(tmap, tmap->tileinfo.pen_data, x0, y0, tmap->tileinfo.palette_base, tmap->tileinfo.category, tmap->tileinfo.group, flags);

	/* if mask data is specified, apply it */
	if ((flags & (TILE_FORCE_LAYER0 | TILE_FORCE_LAYER1 | TILE_FORCE_LAYER2)) == 0 && tmap->tileinfo.mask_data != NULL)
		tmap->tileflags[logindex] = tile_apply_bitmask(tmap, tmap->tileinfo.mask_data, x0, y0, tmap->tileinfo.category, flags);

profiler_mark(PROFILER_END);
}


/*-------------------------------------------------
    tile_draw - draw a single tile to the
    tilemap's internal pixmap, using the pen as
    the pen_to_flags lookup value, and adding
    the palette_base
-------------------------------------------------*/

static UINT8 tile_draw(tilemap *tmap, const UINT8 *pendata, UINT32 x0, UINT32 y0, UINT32 palette_base, UINT8 category, UINT8 group, UINT8 flags)
{
	const UINT8 *penmap = tmap->pen_to_flags + group * MAX_PEN_TO_FLAGS;
	mame_bitmap *flagsmap = tmap->flagsmap;
	mame_bitmap *pixmap = tmap->pixmap;
	int height = tmap->tileheight;
	int width = tmap->tilewidth;
	UINT8 andmask = ~0, ormask = 0;
	int dx0 = 1, dy0 = 1;
	int tx, ty;

	/* OR in the force layer flags */
	category |= flags & (TILE_FORCE_LAYER0 | TILE_FORCE_LAYER1 | TILE_FORCE_LAYER2);

	/* if we're vertically flipped, point to the bottom row and work backwards */
	if (flags & TILE_FLIPY)
	{
		y0 += height - 1;
		dy0 = -1;
	}

	/* if we're horizontally flipped, point to the rightmost column and work backwards */
	if (flags & TILE_FLIPX)
	{
		x0 += width - 1;
		dx0 = -1;
	}

	/* in 4bpp mode, we draw in groups of 2 pixels, so halve the width now */
	if (flags & TILE_4BPP)
	{
		assert(width % 2 == 0);
		width /= 2;
	}

	/* iterate over rows */
	for (ty = 0; ty < height; ty++)
	{
		UINT16 *pixptr = BITMAP_ADDR16(pixmap, y0, x0);
		UINT8 *flagsptr = BITMAP_ADDR8(flagsmap, y0, x0);
		int xoffs = 0;

		/* pre-advance to the next row */
		y0 += dy0;

		/* 8bpp data */
		if (!(flags & TILE_4BPP))
		{
			for (tx = 0; tx < width; tx++)
			{
				UINT8 pen = *pendata++;
				UINT8 map = penmap[pen];
				pixptr[xoffs] = palette_base + pen;
				flagsptr[xoffs] = map | category;
				andmask &= map;
				ormask |= map;
				xoffs += dx0;
			}
		}

		/* 4bpp data */
		else
		{
			for (tx = 0; tx < width; tx++)
			{
				UINT8 data = *pendata++;
				UINT8 pen, map;

				pen = data & 0x0f;
				map = penmap[pen];
				pixptr[xoffs] = palette_base + pen;
				flagsptr[xoffs] = map | category;
				andmask &= map;
				ormask |= map;
				xoffs += dx0;

				pen = data >> 4;
				map = penmap[pen];
				pixptr[xoffs] = palette_base + pen;
				flagsptr[xoffs] = map | category;
				andmask &= map;
				ormask |= map;
				xoffs += dx0;
			}
		}
	}
	return andmask ^ ormask;
}


/*-------------------------------------------------
    tile_apply_bitmask - apply a bitmask to an
    already-rendered tile by modifying the
    flagsmap appropriately
-------------------------------------------------*/

static UINT8 tile_apply_bitmask(tilemap *tmap, const UINT8 *maskdata, UINT32 x0, UINT32 y0, UINT8 category, UINT8 flags)
{
	mame_bitmap *flagsmap = tmap->flagsmap;
	int height = tmap->tileheight;
	int width = tmap->tilewidth;
	UINT8 andmask = ~0, ormask = 0;
	int dx0 = 1, dy0 = 1;
	int bitoffs = 0;
	int tx, ty;

	/* if we're vertically flipped, point to the bottom row and work backwards */
	if (flags & TILE_FLIPY)
	{
		y0 += height - 1;
		dy0 = -1;
	}

	/* if we're horizontally flipped, point to the rightmost column and work backwards */
	if (flags & TILE_FLIPX)
	{
		x0 += width - 1;
		dx0 = -1;
	}

	/* iterate over rows */
	for (ty = 0; ty < height; ty++)
	{
		UINT8 *flagsptr = BITMAP_ADDR8(flagsmap, y0, x0);
		int xoffs = 0;

		/* pre-advance to the next row */
		y0 += dy0;

		/* anywhere the bitmask is 0 should be transparent */
		for (tx = 0; tx < width; tx++)
		{
			UINT8 map = flagsptr[xoffs];

			if ((maskdata[bitoffs / 8] & (0x80 >> (bitoffs & 7))) == 0)
				map = flagsptr[xoffs] = TILEMAP_PIXEL_TRANSPARENT | category;
			andmask &= map;
			ormask |= map;
			xoffs += dx0;
			bitoffs++;
		}
	}
	return andmask ^ ormask;
}



/***************************************************************************
    DRAWING HELPERS
***************************************************************************/

/*-------------------------------------------------
    configure_blit_parameters - fill in the
    standard blit parameters based on the input
    data; this code is shared by normal, roz,
    and indexed drawing code
-------------------------------------------------*/

static void configure_blit_parameters(blit_parameters *blit, tilemap *tmap, mame_bitmap *dest, const rectangle *cliprect, UINT32 flags, UINT8 priority, UINT8 priority_mask)
{
	/* start with nothing */
	memset(blit, 0, sizeof(*blit));

	/* set the target bitmap */
	blit->bitmap = dest;

	/* if we have a cliprect, copy */
	if (cliprect != NULL)
		blit->cliprect = *cliprect;

	/* otherwise, make one up */
	else
	{
		blit->cliprect.min_x = blit->cliprect.min_y = 0;
		blit->cliprect.max_x = dest->width - 1;
		blit->cliprect.max_y = dest->height - 1;
	}

	/* set the priority code */
	blit->tilemap_priority_code = priority | (priority_mask << 8) | (tmap->palette_offset << 16);

	/* if no destination, just render priority */
	if (dest == NULL)
	{
		blit->draw_masked = scanline_draw_masked_null;
		blit->draw_opaque = scanline_draw_opaque_null;
	}

	/* otherwise get the appropriate callbacks for the format and flags */
	else
	{
		switch (dest->format)
		{
			case BITMAP_FORMAT_RGB32:
				blit->draw_masked = (flags & TILEMAP_DRAW_ALPHA) ? scanline_draw_masked_rgb32_alpha : scanline_draw_masked_rgb32;
				blit->draw_opaque = (flags & TILEMAP_DRAW_ALPHA) ? scanline_draw_opaque_rgb32_alpha : scanline_draw_opaque_rgb32;
				break;

			case BITMAP_FORMAT_RGB15:
				blit->draw_masked = (flags & TILEMAP_DRAW_ALPHA) ? scanline_draw_masked_rgb16_alpha : scanline_draw_masked_rgb16;
				blit->draw_opaque = (flags & TILEMAP_DRAW_ALPHA) ? scanline_draw_opaque_rgb16_alpha : scanline_draw_opaque_rgb16;
				break;

			case BITMAP_FORMAT_INDEXED16:
				blit->draw_masked = scanline_draw_masked_ind16;
				blit->draw_opaque = scanline_draw_opaque_ind16;
				break;

			default:
				fatalerror("tilemap_draw_primask: Invalid bitmap format");
				break;
		}
	}

	/* tile priority; unless otherwise specified, draw anything in layer 0 */
	blit->mask = TILEMAP_PIXEL_CATEGORY_MASK;
	blit->value	= flags & TILEMAP_PIXEL_CATEGORY_MASK;

	/* if no layers specified, draw layer 0 */
	if ((flags & (TILEMAP_DRAW_LAYER0 | TILEMAP_DRAW_LAYER1 | TILEMAP_DRAW_LAYER2)) == 0)
		flags |= TILEMAP_DRAW_LAYER0;

	/* OR in the bits from the draw masks */
	blit->mask |= flags & (TILEMAP_DRAW_LAYER0 | TILEMAP_DRAW_LAYER1 | TILEMAP_DRAW_LAYER2);
	blit->value |= flags & (TILEMAP_DRAW_LAYER0 | TILEMAP_DRAW_LAYER1 | TILEMAP_DRAW_LAYER2);

	/* for all-opaque rendering, don't check any of the layer bits */
	if (flags & TILEMAP_DRAW_OPAQUE)
	{
		blit->mask &= ~(TILEMAP_PIXEL_LAYER0 | TILEMAP_PIXEL_LAYER1 | TILEMAP_PIXEL_LAYER2);
		blit->value &= ~(TILEMAP_PIXEL_LAYER0 | TILEMAP_PIXEL_LAYER1 | TILEMAP_PIXEL_LAYER2);
	}
}


/*-------------------------------------------------
    tilemap_draw_instance - draw a single
    instance of the tilemap to the internal
    pixmap at the given xpos,ypos
-------------------------------------------------*/

static void tilemap_draw_instance(tilemap *tmap, const blit_parameters *blit, int xpos, int ypos)
{
	mame_bitmap *dest = blit->bitmap;
	const UINT16 *source_baseaddr;
	const UINT8 *mask_baseaddr;
	void *dest_baseaddr = NULL;
	UINT8 *priority_baseaddr;
	int dest_line_pitch_bytes = 0;
	int dest_bytespp = 0;
	int mincol, maxcol;
	int x1, y1, x2, y2;
	int y, nexty;

	/* clip destination coordinates to the tilemap */
	/* note that x2/y2 are exclusive, not inclusive */
	x1 = MAX(xpos, blit->cliprect.min_x);
	x2 = MIN(xpos + (int)tmap->width, blit->cliprect.max_x + 1);
	y1 = MAX(ypos, blit->cliprect.min_y);
	y2 = MIN(ypos + (int)tmap->height, blit->cliprect.max_y + 1);

	/* if totally clipped, stop here */
	if (x1 >= x2 || y1 >= y2)
		return;

	/* look up priority and destination base addresses for y1 */
	priority_baseaddr = BITMAP_ADDR8(priority_bitmap, y1, xpos);
	if (dest != NULL)
	{
		dest_bytespp = dest->bpp / 8;
		dest_line_pitch_bytes = dest->rowpixels * dest_bytespp;
		dest_baseaddr = (UINT8 *)dest->base + (y1 * dest->rowpixels + xpos) * dest_bytespp;
	}

	/* convert screen coordinates to source tilemap coordinates */
	x1 -= xpos;
	y1 -= ypos;
	x2 -= xpos;
	y2 -= ypos;

	/* get tilemap pixels */
	source_baseaddr = BITMAP_ADDR16(tmap->pixmap, y1, 0);
	mask_baseaddr = BITMAP_ADDR8(tmap->flagsmap, y1, 0);

	/* get start/stop columns, rounding outward */
	mincol = x1 / tmap->tilewidth;
	maxcol = (x2 + tmap->tilewidth - 1) / tmap->tilewidth;

	/* set up row counter */
	y = y1;
	nexty = tmap->tileheight * (y1 / tmap->tileheight) + tmap->tileheight;
	nexty = MIN(nexty, y2);

	/* loop over tilemap rows */
	for (;;)
	{
		int row = y / tmap->tileheight;
		trans_t prev_trans = WHOLLY_TRANSPARENT;
		trans_t cur_trans;
		int x_start = x1;
		int column;

		/* iterate across the applicable tilemap columns */
		for (column = mincol; column <= maxcol; column++)
		{
			int x_end;

			/* we don't actually render the last column; it is always just used for flushing */
			if (column == maxcol)
				cur_trans = WHOLLY_TRANSPARENT;

			/* for other columns we look up the transparency information */
			else
			{
				tilemap_logical_index logindex = row * tmap->cols + column;

				/* if the current tile is dirty, fix it */
				if (tmap->tileflags[logindex] == TILE_FLAG_DIRTY)
					tile_update(tmap, logindex, column, row);

				/* if the current summary data is non-zero, we must draw masked */
				if ((tmap->tileflags[logindex] & blit->mask) != 0)
					cur_trans = MASKED;

				/* otherwise, our transparency state is constant across the tile; fetch it */
				else
					cur_trans = ((mask_baseaddr[column * tmap->tilewidth] & blit->mask) == blit->value) ? WHOLLY_OPAQUE : WHOLLY_TRANSPARENT;
			}

			/* if the transparency state is the same as last time, don't render yet */
			if (cur_trans == prev_trans)
				continue;

			/* compute the end of this run, in pixels */
			x_end = column * tmap->tilewidth;
			x_end = MAX(x_end, x1);
			x_end = MIN(x_end, x2);

			/* if we're rendering something, compute the pointers */
			if (prev_trans != WHOLLY_TRANSPARENT)
			{
				const UINT16 *source0 = source_baseaddr + x_start;
				void *dest0 = (UINT8 *)dest_baseaddr + x_start * dest_bytespp;
				UINT8 *pmap0 = priority_baseaddr + x_start;
				int cury;

				/* if we were opaque, use the opaque renderer */
				if (prev_trans == WHOLLY_OPAQUE)
				{
					for (cury = y; cury < nexty; cury++)
					{
						(*blit->draw_opaque)(dest0, source0, x_end - x_start, pmap0, blit->tilemap_priority_code);

						dest0 = (UINT8 *)dest0 + dest_line_pitch_bytes;
						source0 += tmap->pixmap->rowpixels;
						pmap0 += priority_bitmap->rowpixels;
					}
				}

				/* otherwise use the masked renderer */
				else
				{
					const UINT8 *mask0 = mask_baseaddr + x_start;
					for (cury = y; cury < nexty; cury++)
					{
						(*blit->draw_masked)(dest0, source0, mask0, blit->mask, blit->value, x_end - x_start, pmap0, blit->tilemap_priority_code);

						dest0 = (UINT8 *)dest0 + dest_line_pitch_bytes;
						source0 += tmap->pixmap->rowpixels;
						mask0 += tmap->flagsmap->rowpixels;
						pmap0 += priority_bitmap->rowpixels;
					}
				}
			}

			/* the new start is the end */
			x_start = x_end;
			prev_trans = cur_trans;
		}

		/* if this was the last row, stop */
		if (nexty == y2)
			break;

		/* advance to the next row on all our bitmaps */
		priority_baseaddr += priority_bitmap->rowpixels * (nexty - y);
		source_baseaddr += tmap->pixmap->rowpixels * (nexty - y);
		mask_baseaddr += tmap->flagsmap->rowpixels * (nexty - y);
		dest_baseaddr = (UINT8 *)dest_baseaddr + dest_line_pitch_bytes * (nexty - y);

		/* increment the Y counter */
		y = nexty;
		nexty += tmap->tileheight;
		nexty = MIN(nexty, y2);
	}
}


/*-------------------------------------------------
    tilemap_draw_roz_core - render the tilemap's
    pixmap to the destination with rotation
    and zoom
-------------------------------------------------*/

#define ROZ_PLOT_PIXEL(INPUT_VAL)											\
do {																		\
	if (blit->draw_masked == scanline_draw_masked_ind16)					\
		*(UINT16 *)dest = (INPUT_VAL) + (priority >> 16);					\
	else if (blit->draw_masked == scanline_draw_masked_rgb32)				\
		*(UINT32 *)dest = clut[INPUT_VAL];									\
	else if (blit->draw_masked == scanline_draw_masked_rgb16)				\
		*(UINT16 *)dest = clut[INPUT_VAL];									\
	else if (blit->draw_masked == scanline_draw_masked_rgb32_alpha)			\
		*(UINT32 *)dest = alpha_blend32(*(UINT32 *)dest, clut[INPUT_VAL]);	\
	else if (blit->draw_masked == scanline_draw_masked_rgb16_alpha)			\
		*(UINT16 *)dest = alpha_blend16(*(UINT16 *)dest, clut[INPUT_VAL]);	\
} while (0)

static void tilemap_draw_roz_core(tilemap *tmap, const blit_parameters *blit,
		UINT32 startx, UINT32 starty, int incxx, int incxy, int incyx, int incyy, int wraparound)
{
	const pen_t *clut = &Machine->pens[blit->tilemap_priority_code >> 16];
	mame_bitmap *destbitmap = blit->bitmap;
	mame_bitmap *srcbitmap = tmap->pixmap;
	mame_bitmap *flagsmap = tmap->flagsmap;
	const int xmask = srcbitmap->width-1;
	const int ymask = srcbitmap->height-1;
	const int widthshifted = srcbitmap->width << 16;
	const int heightshifted = srcbitmap->height << 16;
	UINT32 priority = blit->tilemap_priority_code;
	UINT8 mask = blit->mask;
	UINT8 value = blit->value;
	UINT32 cx;
	UINT32 cy;
	int x;
	int sx;
	int sy;
	int ex;
	int ey;
	void *dest;
	UINT8 *pri;
	const UINT16 *src;
	const UINT8 *maskptr;
	int destadvance = destbitmap->bpp / 8;

	/* pre-advance based on the cliprect */
	startx += blit->cliprect.min_x * incxx + blit->cliprect.min_y * incyx;
	starty += blit->cliprect.min_x * incxy + blit->cliprect.min_y * incyy;

	/* extract start/end points */
	sx = blit->cliprect.min_x;
	sy = blit->cliprect.min_y;
	ex = blit->cliprect.max_x;
	ey = blit->cliprect.max_y;

	/* optimized loop for the not rotated case */
	if (incxy == 0 && incyx == 0 && !wraparound)
	{
		/* skip without drawing until we are within the bitmap */
		while (startx >= widthshifted && sx <= ex)
		{
			startx += incxx;
			sx++;
		}

		/* early exit if we're done already */
		if (sx > ex)
			return;

		/* loop over rows */
		while (sy <= ey)
		{
			/* only draw if Y is within range */
			if (starty < heightshifted)
			{
				/* initialize X counters */
				x = sx;
				cx = startx;
				cy = starty >> 16;

				/* get source and priority pointers */
				pri = BITMAP_ADDR8(priority_bitmap, sy, sx);
				src = BITMAP_ADDR16(srcbitmap, cy, 0);
				maskptr = BITMAP_ADDR8(flagsmap, cy, 0);
				dest = (UINT8 *)destbitmap->base + (destbitmap->rowpixels * sy + sx) * destadvance;

				/* loop over columns */
				while (x <= ex && cx < widthshifted)
				{
					/* plot if we match the mask */
					if ((maskptr[cx >> 16] & mask) == value)
					{
						ROZ_PLOT_PIXEL(src[cx >> 16]);
						*pri = (*pri & (priority >> 8)) | priority;
					}

					/* advance in X */
					cx += incxx;
					x++;
					dest = (UINT8 *)dest + destadvance;
					pri++;
				}
			}

			/* advance in Y */
			starty += incyy;
			sy++;
		}
	}

	/* wraparound case */
	else if (wraparound)
	{
		/* loop over rows */
		while (sy <= ey)
		{
			/* initialize X counters */
			x = sx;
			cx = startx;
			cy = starty;

			/* get dest and priority pointers */
			dest = (UINT8 *)destbitmap->base + (destbitmap->rowpixels * sy + sx) * destadvance;
			pri = BITMAP_ADDR8(priority_bitmap, sy, sx);

			/* loop over columns */
			while (x <= ex)
			{
				/* plot if we match the mask */
				if ((*BITMAP_ADDR8(flagsmap, (cy >> 16) & ymask, (cx >> 16) & xmask) & mask) == value)
				{
					ROZ_PLOT_PIXEL(*BITMAP_ADDR16(srcbitmap, (cy >> 16) & ymask, (cx >> 16) & xmask));
					*pri = (*pri & (priority >> 8)) | priority;
				}

				/* advance in X */
				cx += incxx;
				cy += incxy;
				x++;
				dest = (UINT8 *)dest + destadvance;
				pri++;
			}

			/* advance in Y */
			startx += incyx;
			starty += incyy;
			sy++;
		}
	}

	/* non-wraparound case */
	else
	{
		/* loop over rows */
		while (sy <= ey)
		{
			/* initialize X counters */
			x = sx;
			cx = startx;
			cy = starty;

			/* get dest and priority pointers */
			dest = (UINT8 *)destbitmap->base + (destbitmap->rowpixels * sy + sx) * destadvance;
			pri = BITMAP_ADDR8(priority_bitmap, sy, sx);

			/* loop over columns */
			while (x <= ex)
			{
				/* plot if we're within the bitmap and we match the mask */
				if (cx < widthshifted && cy < heightshifted)
					if ((*BITMAP_ADDR8(flagsmap, cy >> 16, cx >> 16) & mask) == value)
					{
						ROZ_PLOT_PIXEL(*BITMAP_ADDR16(srcbitmap, cy >> 16, cx >> 16));
						*pri = (*pri & (priority >> 8)) | priority;
					}

				/* advance in X */
				cx += incxx;
				cy += incxy;
				x++;
				dest = (UINT8 *)dest + destadvance;
				pri++;
			}

			/* advance in Y */
			startx += incyx;
			starty += incyy;
			sy++;
		}
	}
}



/***************************************************************************
    SCANLINE RASTERIZERS
***************************************************************************/

/*-------------------------------------------------
    scanline_draw_opaque_null - draw to a NULL
    bitmap, setting priority only
-------------------------------------------------*/

static void scanline_draw_opaque_null(void *dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode)
{
	int i;

	/* skip entirely if not changing priority */
	if (pcode != 0xff00)
	{
		for (i = 0; i < count; i++)
			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
	}
}


/*-------------------------------------------------
    scanline_draw_masked_null - draw to a NULL
    bitmap using a mask, setting priority only
-------------------------------------------------*/

static void scanline_draw_masked_null(void *dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode)
{
	int i;

	/* skip entirely if not changing priority */
	if (pcode != 0xff00)
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
				pri[i] = (pri[i] & (pcode >> 8)) | pcode;
	}
}



/*-------------------------------------------------
    scanline_draw_opaque_ind16 - draw to a 16bpp
    indexed bitmap
-------------------------------------------------*/

static void scanline_draw_opaque_ind16(void *_dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode)
{
	UINT16 *dest = _dest;
	int pal = pcode >> 16;
	int i;

	/* special case for no palette offset */
	if (pal == 0)
	{
		memcpy(dest, source, count * 2);

		/* priority if necessary */
		if (pcode != 0xff00)
		{
			for (i = 0; i < count; i++)
				pri[i] = (pri[i] & (pcode >> 8)) | pcode;
		}
	}

	/* priority case */
	else if ((pcode & 0xffff) != 0xff00)
	{
		for (i = 0; i < count; i++)
		{
			dest[i] = source[i] + pal;
			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
		}
	}

	/* no priority case */
	else
	{
		for (i = 0; i < count; i++)
			dest[i] = source[i] + pal;
	}
}


/*-------------------------------------------------
    scanline_draw_masked_ind16 - draw to a 16bpp
    indexed bitmap using a mask
-------------------------------------------------*/

static void scanline_draw_masked_ind16(void *_dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode)
{
	UINT16 *dest = _dest;
	int pal = pcode >> 16;
	int i;

	/* priority case */
	if ((pcode & 0xffff) != 0xff00)
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
			{
				dest[i] = source[i] + pal;
				pri[i] = (pri[i] & (pcode >> 8)) | pcode;
			}
	}

	/* no priority case */
	else
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
				dest[i] = source[i] + pal;
	}
}



/*-------------------------------------------------
    scanline_draw_opaque_rgb16 - draw to a 16bpp
    RGB bitmap
-------------------------------------------------*/

static void scanline_draw_opaque_rgb16(void *_dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode)
{
	const pen_t *clut = &Machine->pens[pcode >> 16];
	UINT16 *dest = _dest;
	int i;

	/* priority case */
	if ((pcode & 0xffff) != 0xff00)
	{
		for (i = 0; i < count; i++)
		{
			dest[i] = clut[source[i]];
			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
		}
	}

	/* no priority case */
	else
	{
		for (i = 0; i < count; i++)
			dest[i] = clut[source[i]];
	}
}


/*-------------------------------------------------
    scanline_draw_masked_rgb16 - draw to a 16bpp
    RGB bitmap using a mask
-------------------------------------------------*/

static void scanline_draw_masked_rgb16(void *_dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode)
{
	const pen_t *clut = &Machine->pens[pcode >> 16];
	UINT16 *dest = _dest;
	int i;

	/* priority case */
	if ((pcode & 0xffff) != 0xff00)
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
			{
				dest[i] = clut[source[i]];
				pri[i] = (pri[i] & (pcode >> 8)) | pcode;
			}
	}

	/* no priority case */
	else
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
				dest[i] = clut[source[i]];
	}
}


/*-------------------------------------------------
    scanline_draw_opaque_rgb16_alpha - draw to a
    16bpp RGB bitmap with alpha blending
-------------------------------------------------*/

static void scanline_draw_opaque_rgb16_alpha(void *_dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode)
{
	const pen_t *clut = &Machine->pens[pcode >> 16];
	UINT16 *dest = _dest;
	int i;

	/* priority case */
	if ((pcode & 0xffff) != 0xff00)
	{
		for (i = 0; i < count; i++)
		{
			dest[i] = alpha_blend16(dest[i], clut[source[i]]);
			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
		}
	}

	/* no priority case */
	else
	{
		for (i = 0; i < count; i++)
			dest[i] = alpha_blend16(dest[i], clut[source[i]]);
	}
}


/*-------------------------------------------------
    scanline_draw_masked_rgb16_alpha - draw to a
    16bpp RGB bitmap using a mask and alpha
    blending
-------------------------------------------------*/

static void scanline_draw_masked_rgb16_alpha(void *_dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode)
{
	const pen_t *clut = &Machine->pens[pcode >> 16];
	UINT16 *dest = _dest;
	int i;

	/* priority case */
	if ((pcode & 0xffff) != 0xff00)
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
			{
				dest[i] = alpha_blend16(dest[i], clut[source[i]]);
				pri[i] = (pri[i] & (pcode >> 8)) | pcode;
			}
	}

	/* no priority case */
	else
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
				dest[i] = alpha_blend16(dest[i], clut[source[i]]);
	}
}


/*-------------------------------------------------
    scanline_draw_opaque_rgb32 - draw to a 32bpp
    RGB bitmap
-------------------------------------------------*/

static void scanline_draw_opaque_rgb32(void *_dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode)
{
	const pen_t *clut = &Machine->pens[pcode >> 16];
	UINT32 *dest = _dest;
	int i;

	/* priority case */
	if ((pcode & 0xffff) != 0xff00)
	{
		for (i = 0; i < count; i++)
		{
			dest[i] = clut[source[i]];
			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
		}
	}

	/* no priority case */
	else
	{
		for (i = 0; i < count; i++)
			dest[i] = clut[source[i]];
	}
}


/*-------------------------------------------------
    scanline_draw_masked_rgb32 - draw to a 32bpp
    RGB bitmap using a mask
-------------------------------------------------*/

static void scanline_draw_masked_rgb32(void *_dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode)
{
	const pen_t *clut = &Machine->pens[pcode >> 16];
	UINT32 *dest = _dest;
	int i;

	/* priority case */
	if ((pcode & 0xffff) != 0xff00)
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
			{
				dest[i] = clut[source[i]];
				pri[i] = (pri[i] & (pcode >> 8)) | pcode;
			}
	}

	/* no priority case */
	else
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
				dest[i] = clut[source[i]];
	}
}


/*-------------------------------------------------
    scanline_draw_opaque_rgb32_alpha - draw to a
    32bpp RGB bitmap with alpha blending
-------------------------------------------------*/

static void scanline_draw_opaque_rgb32_alpha(void *_dest, const UINT16 *source, int count, UINT8 *pri, UINT32 pcode)
{
	const pen_t *clut = &Machine->pens[pcode >> 16];
	UINT32 *dest = _dest;
	int i;

	/* priority case */
	if ((pcode & 0xffff) != 0xff00)
	{
		for (i = 0; i < count; i++)
		{
			dest[i] = alpha_blend32(dest[i], clut[source[i]]);
			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
		}
	}

	/* no priority case */
	else
	{
		for (i = 0; i < count; i++)
			dest[i] = alpha_blend32(dest[i], clut[source[i]]);
	}
}


/*-------------------------------------------------
    scanline_draw_masked_rgb32_alpha - draw to a
    32bpp RGB bitmap using a mask and alpha
    blending
-------------------------------------------------*/

static void scanline_draw_masked_rgb32_alpha(void *_dest, const UINT16 *source, const UINT8 *maskptr, int mask, int value, int count, UINT8 *pri, UINT32 pcode)
{
	const pen_t *clut = &Machine->pens[pcode >> 16];
	UINT32 *dest = _dest;
	int i;

	/* priority case */
	if ((pcode & 0xffff) != 0xff00)
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
			{
				dest[i] = alpha_blend32(dest[i], clut[source[i]]);
				pri[i] = (pri[i] & (pcode >> 8)) | pcode;
			}
	}

	/* no priority case */
	else
	{
		for (i = 0; i < count; i++)
			if ((maskptr[i] & mask) == value)
				dest[i] = alpha_blend32(dest[i], clut[source[i]]);
	}
}