trunk/src/mess/drivers/x1.c
| r20272 | r20273 | |
| 38 | 38 | - Might & Magic: uses 0xe80-3 kanji ports, should be a good test case for that; |
| 39 | 39 | - "newtype": trips a z80dma assert, worked around for now; |
| 40 | 40 | - Saziri: doesn't re-initialize the tilemap attribute vram when you start a play, making it to have missing colors if you don't start a play in time; |
| 41 | | - Shilver Ghost: changes the vertical visible area during scrolling, and that doesn't work too well with current mc6845 core. |
| 42 | 41 | - Suikoden: shows a JP message error (DFJustin: "Problem with the disk device !! Please set a floppy disk properly and press the return key. Retrying.") |
| 43 | 42 | - Super Billiards (X1 Pack 14): has a slight PCG timing bug, that happens randomly; |
| 44 | 43 | - Trivia-Q: dunno what to do on the selection screen, missing inputs? |
| r20272 | r20273 | |
| 62 | 61 | 0xff16 - 0xff17 start-up vector |
| 63 | 62 | In theory, you can convert your tape / floppy games into ROM format easily, provided that you know what's the pinout of the |
| 64 | 63 | cartridge slot and it doesn't exceed 64k (0x10000) of size. |
| 65 | | - Gruppe: shows a random bitmap graphic then returns "program load error" ... it wants that the floppy has write protection enabled (btanb) |
| 66 | | - Maidum: you need to load BOTH disk without write protection disabled, otherwise it refuses to run. (btanb) |
| 64 | - Gruppe: shows a random bitmap graphic then returns "program load error" ... it wants that the floppy has write protection enabled (!) (btanb) |
| 65 | - Maidum: you need to load BOTH disk with write protection disabled, otherwise it refuses to run. (btanb) |
| 67 | 66 | - Marvelous: needs write protection disabled (btanb) |
| 68 | 67 | - Chack'n Pop: to load this game, do a files command on the "Jodan Dos" prompt then move the cursor up at the "Chack'n Pop" file. |
| 69 | 68 | Substitute bin with load and press enter. Finally, do a run once that it loaded correctly. |
| r20272 | r20273 | |
| 230 | 229 | m_pal_4096 = auto_alloc_array_clear(machine(), UINT8, 0x1000*3); |
| 231 | 230 | } |
| 232 | 231 | |
| 233 | | static void x1_draw_pixel(running_machine &machine, bitmap_rgb32 &bitmap,int y,int x,UINT16 pen,UINT8 width,UINT8 height) |
| 232 | void x1_state::x1_draw_pixel(running_machine &machine, bitmap_rgb32 &bitmap,int y,int x,UINT16 pen,UINT8 width,UINT8 height) |
| 234 | 233 | { |
| 235 | 234 | if(!machine.primary_screen->visible_area().contains(x, y)) |
| 236 | 235 | return; |
| 237 | 236 | |
| 238 | 237 | if(width && height) |
| 239 | 238 | { |
| 240 | | bitmap.pix32(y+0, x+0) = machine.pens[pen]; |
| 241 | | bitmap.pix32(y+0, x+1) = machine.pens[pen]; |
| 242 | | bitmap.pix32(y+1, x+0) = machine.pens[pen]; |
| 243 | | bitmap.pix32(y+1, x+1) = machine.pens[pen]; |
| 239 | bitmap.pix32(y+0+m_ystart, x+0+m_xstart) = machine.pens[pen]; |
| 240 | bitmap.pix32(y+0+m_ystart, x+1+m_xstart) = machine.pens[pen]; |
| 241 | bitmap.pix32(y+1+m_ystart, x+0+m_xstart) = machine.pens[pen]; |
| 242 | bitmap.pix32(y+1+m_ystart, x+1+m_xstart) = machine.pens[pen]; |
| 244 | 243 | } |
| 245 | 244 | else if(width) |
| 246 | 245 | { |
| 247 | | bitmap.pix32(y, x+0) = machine.pens[pen]; |
| 248 | | bitmap.pix32(y, x+1) = machine.pens[pen]; |
| 246 | bitmap.pix32(y+m_ystart, x+0+m_xstart) = machine.pens[pen]; |
| 247 | bitmap.pix32(y+m_ystart, x+1+m_xstart) = machine.pens[pen]; |
| 249 | 248 | } |
| 250 | 249 | else if(height) |
| 251 | 250 | { |
| 252 | | bitmap.pix32(y+0, x) = machine.pens[pen]; |
| 253 | | bitmap.pix32(y+1, x) = machine.pens[pen]; |
| 251 | bitmap.pix32(y+0+m_ystart, x+m_xstart) = machine.pens[pen]; |
| 252 | bitmap.pix32(y+1+m_ystart, x+m_xstart) = machine.pens[pen]; |
| 254 | 253 | } |
| 255 | 254 | else |
| 256 | | bitmap.pix32(y, x) = machine.pens[pen]; |
| 255 | bitmap.pix32(y+m_ystart, x+m_xstart) = machine.pens[pen]; |
| 257 | 256 | } |
| 258 | 257 | |
| 259 | | #define mc6845_h_char_total (state->m_crtc_vreg[0]) |
| 260 | | #define mc6845_h_display (state->m_crtc_vreg[1]) |
| 261 | | #define mc6845_h_sync_pos (state->m_crtc_vreg[2]) |
| 262 | | #define mc6845_sync_width (state->m_crtc_vreg[3]) |
| 263 | | #define mc6845_v_char_total (state->m_crtc_vreg[4]) |
| 264 | | #define mc6845_v_total_adj (state->m_crtc_vreg[5]) |
| 265 | | #define mc6845_v_display (state->m_crtc_vreg[6]) |
| 266 | | #define mc6845_v_sync_pos (state->m_crtc_vreg[7]) |
| 267 | | #define mc6845_mode_ctrl (state->m_crtc_vreg[8]) |
| 268 | | #define mc6845_tile_height (state->m_crtc_vreg[9]+1) |
| 269 | | #define mc6845_cursor_y_start (state->m_crtc_vreg[0x0a]) |
| 270 | | #define mc6845_cursor_y_end (state->m_crtc_vreg[0x0b]) |
| 271 | | #define mc6845_start_addr (((state->m_crtc_vreg[0x0c]<<8) & 0x3f00) | (state->m_crtc_vreg[0x0d] & 0xff)) |
| 272 | | #define mc6845_cursor_addr (((state->m_crtc_vreg[0x0e]<<8) & 0x3f00) | (state->m_crtc_vreg[0x0f] & 0xff)) |
| 273 | | #define mc6845_light_pen_addr (((state->m_crtc_vreg[0x10]<<8) & 0x3f00) | (state->m_crtc_vreg[0x11] & 0xff)) |
| 274 | | #define mc6845_update_addr (((state->m_crtc_vreg[0x12]<<8) & 0x3f00) | (state->m_crtc_vreg[0x13] & 0xff)) |
| 258 | #define mc6845_h_char_total (m_crtc_vreg[0]) |
| 259 | #define mc6845_h_display (m_crtc_vreg[1]) |
| 260 | #define mc6845_h_sync_pos (m_crtc_vreg[2]) |
| 261 | #define mc6845_sync_width (m_crtc_vreg[3]) |
| 262 | #define mc6845_v_char_total (m_crtc_vreg[4]) |
| 263 | #define mc6845_v_total_adj (m_crtc_vreg[5]) |
| 264 | #define mc6845_v_display (m_crtc_vreg[6]) |
| 265 | #define mc6845_v_sync_pos (m_crtc_vreg[7]) |
| 266 | #define mc6845_mode_ctrl (m_crtc_vreg[8]) |
| 267 | #define mc6845_tile_height (m_crtc_vreg[9]+1) |
| 268 | #define mc6845_cursor_y_start (m_crtc_vreg[0x0a]) |
| 269 | #define mc6845_cursor_y_end (m_crtc_vreg[0x0b]) |
| 270 | #define mc6845_start_addr (((m_crtc_vreg[0x0c]<<8) & 0x3f00) | (m_crtc_vreg[0x0d] & 0xff)) |
| 271 | #define mc6845_cursor_addr (((m_crtc_vreg[0x0e]<<8) & 0x3f00) | (m_crtc_vreg[0x0f] & 0xff)) |
| 272 | #define mc6845_light_pen_addr (((m_crtc_vreg[0x10]<<8) & 0x3f00) | (m_crtc_vreg[0x11] & 0xff)) |
| 273 | #define mc6845_update_addr (((m_crtc_vreg[0x12]<<8) & 0x3f00) | (m_crtc_vreg[0x13] & 0xff)) |
| 275 | 274 | |
| 275 | |
| 276 | 276 | /* adjust tile index when we are under double height condition */ |
| 277 | | static UINT8 check_prev_height(running_machine &machine,int x,int y,int x_size) |
| 277 | UINT8 x1_state::check_prev_height(running_machine &machine,int x,int y,int x_size) |
| 278 | 278 | { |
| 279 | 279 | x1_state *state = machine.driver_data<x1_state>(); |
| 280 | 280 | UINT8 prev_tile = state->m_tvram[(x+((y-1)*x_size)+mc6845_start_addr) & 0x7ff]; |
| r20272 | r20273 | |
| 289 | 289 | } |
| 290 | 290 | |
| 291 | 291 | /* Exoa II - Warroid: if double height isn't enabled on the first tile of the line then double height is disabled on everything else. */ |
| 292 | | static UINT8 check_line_valid_height(running_machine &machine,int y,int x_size,int height) |
| 292 | UINT8 x1_state::check_line_valid_height(running_machine &machine,int y,int x_size,int height) |
| 293 | 293 | { |
| 294 | 294 | x1_state *state = machine.driver_data<x1_state>(); |
| 295 | 295 | UINT8 line_attr = state->m_avram[(0+(y*x_size)+mc6845_start_addr) & 0x7ff]; |
| r20272 | r20273 | |
| 300 | 300 | return height; |
| 301 | 301 | } |
| 302 | 302 | |
| 303 | | static void draw_fgtilemap(running_machine &machine, bitmap_rgb32 &bitmap,const rectangle &cliprect) |
| 303 | void x1_state::draw_fgtilemap(running_machine &machine, bitmap_rgb32 &bitmap,const rectangle &cliprect) |
| 304 | 304 | { |
| 305 | 305 | /* |
| 306 | 306 | attribute table: |
| r20272 | r20273 | |
| 318 | 318 | ---- xxxx Kanji upper 4 bits |
| 319 | 319 | */ |
| 320 | 320 | |
| 321 | | x1_state *state = machine.driver_data<x1_state>(); |
| 322 | 321 | int y,x,res_x,res_y; |
| 323 | 322 | UINT32 tile_offset; |
| 324 | 323 | UINT8 x_size,y_size; |
| r20272 | r20273 | |
| 336 | 335 | { |
| 337 | 336 | for (x=0;x<x_size;x++) |
| 338 | 337 | { |
| 339 | | int tile = state->m_tvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff]; |
| 340 | | int color = state->m_avram[((x+y*x_size)+mc6845_start_addr) & 0x7ff] & 0x1f; |
| 341 | | int width = BIT(state->m_avram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 7); |
| 342 | | int height = BIT(state->m_avram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 6); |
| 343 | | int pcg_bank = BIT(state->m_avram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 5); |
| 338 | int tile = m_tvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff]; |
| 339 | int color = m_avram[((x+y*x_size)+mc6845_start_addr) & 0x7ff] & 0x1f; |
| 340 | int width = BIT(m_avram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 7); |
| 341 | int height = BIT(m_avram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 6); |
| 342 | int pcg_bank = BIT(m_avram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 5); |
| 344 | 343 | UINT8 *gfx_data = machine.root_device().memregion(pcg_bank ? "pcg" : "cgrom")->base(); |
| 345 | 344 | int knj_enable = 0; |
| 346 | 345 | int knj_side = 0; |
| 347 | 346 | int knj_bank = 0; |
| 348 | 347 | int knj_uline = 0; |
| 349 | | if(state->m_is_turbo) |
| 348 | if(m_is_turbo) |
| 350 | 349 | { |
| 351 | | knj_enable = BIT(state->m_kvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 7); |
| 352 | | knj_side = BIT(state->m_kvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 6); |
| 353 | | knj_uline = BIT(state->m_kvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 5); |
| 354 | | //knj_lv2 = BIT(state->m_kvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 4); |
| 355 | | knj_bank = state->m_kvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff] & 0x0f; |
| 350 | knj_enable = BIT(m_kvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 7); |
| 351 | knj_side = BIT(m_kvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 6); |
| 352 | knj_uline = BIT(m_kvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 5); |
| 353 | //knj_lv2 = BIT(m_kvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff], 4); |
| 354 | knj_bank = m_kvram[((x+y*x_size)+mc6845_start_addr) & 0x7ff] & 0x0f; |
| 356 | 355 | if(knj_enable) |
| 357 | 356 | { |
| 358 | | gfx_data = state->memregion("kanji")->base(); |
| 357 | gfx_data = memregion("kanji")->base(); |
| 359 | 358 | tile = ((tile + (knj_bank << 8)) << 1) + (knj_side & 1); |
| 360 | 359 | } |
| 361 | 360 | } |
| r20272 | r20273 | |
| 382 | 381 | if(knj_enable) //kanji select |
| 383 | 382 | { |
| 384 | 383 | tile_offset = tile * 16; |
| 385 | | tile_offset += (yi+dy*(state->m_scrn_reg.v400_mode+1)) >> (height+state->m_scrn_reg.v400_mode); |
| 384 | tile_offset += (yi+dy*(m_scrn_reg.v400_mode+1)) >> (height+m_scrn_reg.v400_mode); |
| 386 | 385 | pen[0] = gfx_data[tile_offset+0x0000]>>(7-xi) & (pen_mask & 1)>>0; |
| 387 | 386 | pen[1] = gfx_data[tile_offset+0x0000]>>(7-xi) & (pen_mask & 2)>>1; |
| 388 | 387 | pen[2] = gfx_data[tile_offset+0x0000]>>(7-xi) & (pen_mask & 4)>>2; |
| r20272 | r20273 | |
| 394 | 393 | pen[2] = (pen_mask & 4)>>2; |
| 395 | 394 | } |
| 396 | 395 | |
| 397 | | if((yi >= 16 && state->m_scrn_reg.v400_mode == 0) || (yi >= 32 && state->m_scrn_reg.v400_mode == 1)) |
| 396 | if((yi >= 16 && m_scrn_reg.v400_mode == 0) || (yi >= 32 && m_scrn_reg.v400_mode == 1)) |
| 398 | 397 | pen[0] = pen[1] = pen[2] = 0; |
| 399 | 398 | } |
| 400 | 399 | else if(pcg_bank) // PCG |
| 401 | 400 | { |
| 402 | 401 | tile_offset = tile * 8; |
| 403 | | tile_offset += (yi+dy*(state->m_scrn_reg.v400_mode+1)) >> (height+state->m_scrn_reg.v400_mode); |
| 402 | tile_offset += (yi+dy*(m_scrn_reg.v400_mode+1)) >> (height+m_scrn_reg.v400_mode); |
| 404 | 403 | |
| 405 | 404 | pen[0] = gfx_data[tile_offset+0x0000]>>(7-xi) & (pen_mask & 1)>>0; |
| 406 | 405 | pen[1] = gfx_data[tile_offset+0x0800]>>(7-xi) & (pen_mask & 2)>>1; |
| 407 | 406 | pen[2] = gfx_data[tile_offset+0x1000]>>(7-xi) & (pen_mask & 4)>>2; |
| 408 | 407 | |
| 409 | | if((yi >= 8 && state->m_scrn_reg.v400_mode == 0) || (yi >= 16 && state->m_scrn_reg.v400_mode == 1)) |
| 408 | if((yi >= 8 && m_scrn_reg.v400_mode == 0) || (yi >= 16 && m_scrn_reg.v400_mode == 1)) |
| 410 | 409 | pen[0] = pen[1] = pen[2] = 0; |
| 411 | 410 | } |
| 412 | 411 | else |
| 413 | 412 | { |
| 414 | | tile_offset = tile * (8*(state->m_scrn_reg.ank_sel+1)); |
| 415 | | tile_offset += (yi+dy*(state->m_scrn_reg.v400_mode+1)) >> (height+state->m_scrn_reg.v400_mode); |
| 413 | tile_offset = tile * (8*(m_scrn_reg.ank_sel+1)); |
| 414 | tile_offset += (yi+dy*(m_scrn_reg.v400_mode+1)) >> (height+m_scrn_reg.v400_mode); |
| 416 | 415 | |
| 417 | | pen[0] = gfx_data[tile_offset+state->m_scrn_reg.ank_sel*0x0800]>>(7-xi) & (pen_mask & 1)>>0; |
| 418 | | pen[1] = gfx_data[tile_offset+state->m_scrn_reg.ank_sel*0x0800]>>(7-xi) & (pen_mask & 2)>>1; |
| 419 | | pen[2] = gfx_data[tile_offset+state->m_scrn_reg.ank_sel*0x0800]>>(7-xi) & (pen_mask & 4)>>2; |
| 416 | pen[0] = gfx_data[tile_offset+m_scrn_reg.ank_sel*0x0800]>>(7-xi) & (pen_mask & 1)>>0; |
| 417 | pen[1] = gfx_data[tile_offset+m_scrn_reg.ank_sel*0x0800]>>(7-xi) & (pen_mask & 2)>>1; |
| 418 | pen[2] = gfx_data[tile_offset+m_scrn_reg.ank_sel*0x0800]>>(7-xi) & (pen_mask & 4)>>2; |
| 420 | 419 | |
| 421 | | if(state->m_scrn_reg.ank_sel) |
| 420 | if(m_scrn_reg.ank_sel) |
| 422 | 421 | { |
| 423 | | if((yi >= 16 && state->m_scrn_reg.v400_mode == 0) || (yi >= 32 && state->m_scrn_reg.v400_mode == 1)) |
| 422 | if((yi >= 16 && m_scrn_reg.v400_mode == 0) || (yi >= 32 && m_scrn_reg.v400_mode == 1)) |
| 424 | 423 | pen[0] = pen[1] = pen[2] = 0; |
| 425 | 424 | } |
| 426 | 425 | else |
| 427 | 426 | { |
| 428 | | if((yi >= 8 && state->m_scrn_reg.v400_mode == 0) || (yi >= 16 && state->m_scrn_reg.v400_mode == 1)) |
| 427 | if((yi >= 8 && m_scrn_reg.v400_mode == 0) || (yi >= 16 && m_scrn_reg.v400_mode == 1)) |
| 429 | 428 | pen[0] = pen[1] = pen[2] = 0; |
| 430 | 429 | } |
| 431 | 430 | } |
| r20272 | r20273 | |
| 441 | 440 | if(color & 8) //revert the used color pen |
| 442 | 441 | pcg_pen^=7; |
| 443 | 442 | |
| 444 | | if((state->m_scrn_reg.blackclip & 8) && (color == (state->m_scrn_reg.blackclip & 7))) |
| 443 | if((m_scrn_reg.blackclip & 8) && (color == (m_scrn_reg.blackclip & 7))) |
| 445 | 444 | pcg_pen = 0; // clip the pen to black |
| 446 | 445 | |
| 447 | 446 | res_x = x*8+xi*(width+1); |
| r20272 | r20273 | |
| 489 | 488 | return pri_mask_calc; |
| 490 | 489 | } |
| 491 | 490 | |
| 492 | | static void draw_gfxbitmap(running_machine &machine, bitmap_rgb32 &bitmap,const rectangle &cliprect, int plane,int pri) |
| 491 | void x1_state::draw_gfxbitmap(running_machine &machine, bitmap_rgb32 &bitmap,const rectangle &cliprect, int plane,int pri) |
| 493 | 492 | { |
| 494 | | x1_state *state = machine.driver_data<x1_state>(); |
| 495 | 493 | int xi,yi,x,y; |
| 496 | 494 | int pen_r,pen_g,pen_b,color; |
| 497 | 495 | int pri_mask_val; |
| r20272 | r20273 | |
| 518 | 516 | for(xi=0;xi<8;xi++) |
| 519 | 517 | { |
| 520 | 518 | gfx_offset = ((x+(y*x_size)) + mc6845_start_addr) & 0x7ff; |
| 521 | | gfx_offset+= ((yi >> state->m_scrn_reg.v400_mode) * 0x800) & 0x3fff; |
| 522 | | pen_b = (state->m_gfx_bitmap_ram[gfx_offset+0x0000+plane*0xc000]>>(7-xi)) & 1; |
| 523 | | pen_r = (state->m_gfx_bitmap_ram[gfx_offset+0x4000+plane*0xc000]>>(7-xi)) & 1; |
| 524 | | pen_g = (state->m_gfx_bitmap_ram[gfx_offset+0x8000+plane*0xc000]>>(7-xi)) & 1; |
| 519 | gfx_offset+= ((yi >> m_scrn_reg.v400_mode) * 0x800) & 0x3fff; |
| 520 | pen_b = (m_gfx_bitmap_ram[gfx_offset+0x0000+plane*0xc000]>>(7-xi)) & 1; |
| 521 | pen_r = (m_gfx_bitmap_ram[gfx_offset+0x4000+plane*0xc000]>>(7-xi)) & 1; |
| 522 | pen_g = (m_gfx_bitmap_ram[gfx_offset+0x8000+plane*0xc000]>>(7-xi)) & 1; |
| 525 | 523 | |
| 526 | 524 | color = (pen_g<<2 | pen_r<<1 | pen_b<<0) | 8; |
| 527 | 525 | |
| 528 | 526 | pri_mask_val = priority_mixer_pri(machine,color); |
| 529 | 527 | if(pri_mask_val & pri) continue; |
| 530 | 528 | |
| 531 | | if((color == 8 && state->m_scrn_reg.blackclip & 0x10) || (color == 9 && state->m_scrn_reg.blackclip & 0x20)) // bitmap color clip to black conditions |
| 529 | if((color == 8 && m_scrn_reg.blackclip & 0x10) || (color == 9 && m_scrn_reg.blackclip & 0x20)) // bitmap color clip to black conditions |
| 532 | 530 | color = 0; |
| 533 | 531 | |
| 534 | 532 | if(y*(mc6845_tile_height)+yi < cliprect.min_y || y*(mc6845_tile_height)+yi > cliprect.max_y) // partial update TODO: optimize |
| r20272 | r20273 | |
| 545 | 543 | { |
| 546 | 544 | bitmap.fill(MAKE_ARGB(0xff,0x00,0x00,0x00), cliprect); |
| 547 | 545 | |
| 546 | /* TODO: correct calculation thru mc6845 regs */ |
| 547 | m_xstart = ((mc6845_h_char_total - mc6845_h_sync_pos) * 8) / 2; |
| 548 | m_ystart = ((mc6845_v_char_total - mc6845_v_sync_pos) * 8) / 2; |
| 549 | |
| 550 | // popmessage("%d %d %d %d",mc6845_h_sync_pos,mc6845_v_sync_pos,mc6845_h_char_total,mc6845_v_char_total); |
| 551 | |
| 548 | 552 | draw_gfxbitmap(machine(),bitmap,cliprect,m_scrn_reg.disp_bank,m_scrn_reg.pri); |
| 549 | 553 | draw_fgtilemap(machine(),bitmap,cliprect); |
| 550 | 554 | draw_gfxbitmap(machine(),bitmap,cliprect,m_scrn_reg.disp_bank,m_scrn_reg.pri^0xff); |
| r20272 | r20273 | |
| 1900 | 1904 | static MC6845_INTERFACE( mc6845_intf ) |
| 1901 | 1905 | { |
| 1902 | 1906 | "screen", /* screen we are acting on */ |
| 1903 | | false, /* show border area */ |
| 1907 | true, /* show border area */ |
| 1904 | 1908 | 8, /* number of pixels per video memory address */ |
| 1905 | 1909 | NULL, /* before pixel update callback */ |
| 1906 | 1910 | NULL, /* row update callback */ |