diff --git a/Readme-BSD.txt b/Readme-BSD.txt new file mode 100644 index 0000000..54bd090 --- /dev/null +++ b/Readme-BSD.txt @@ -0,0 +1,31 @@ +PCem v14 BSD supplement + + +You will need the following libraries : + +SDL2 +wxWidgets 3.x +OpenAL + +and their dependencies. + +Open a terminal window, navigate to the PCem directory then enter + +./configure --enable-release-build +gmake + +then ./pcem to run. + +The BSD version stores BIOS ROM images, configuration files, and other data in ~/.pcem + +configure options are : + --enable-release-build : Generate release build. Recommended for regular use. + --enable-debug : Compile with debugging enabled. + --enable-networking : Build with networking support. + --enable-alsa : Build with support for MIDI output through ALSA. Requires libasound. + + +The menu is a pop-up menu in the BSD port. Right-click on the main window when mouse is not +captured. + +CD-ROM support currently only accesses /dev/cdrom. It has not been heavily tested. diff --git a/configure.ac b/configure.ac index e4a6932..0e1b3d2 100644 --- a/configure.ac +++ b/configure.ac @@ -160,6 +160,18 @@ case "$host" in exit -1]) build_linux="yes" ;; + *-*-freebsd*) + CFLAGS="$CFLAGS -I/usr/local/include" + CXXFLAGS="$CXXFLAGS -I/usr/local/include" + LDFLAGS="$LDFLAGS -L/usr/local/lib" + AC_CHECK_LIB([GL], [glGetError], [], \ + [echo "You need to install the OpenGL library." + exit -1]) + AC_CHECK_LIB([openal], [alGetError], [], \ + [echo "You need to install the OpenAL library." + exit -1]) + build_other="yes" + ;; *) AC_CHECK_LIB([GL], [glGetError], [], \ [echo "You need to install the OpenGL library." diff --git a/install-sh b/install-sh old mode 100644 new mode 100755 diff --git a/src/Makefile.am b/src/Makefile.am index ada57f6..a9e94d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -37,8 +37,8 @@ sound_speaker.c sound_ssi2001.c sound_wss.c sound_ym7128.c soundopenal.c sst39sf t1000.c t3100e.c \ timer.c um8669f.c um8881f.c vid_ati_eeprom.c vid_ati_mach64.c vid_ati18800.c vid_ati28800.c \ vid_ati68860_ramdac.c vid_cga.c vid_cl5429.c vid_colorplus.c vid_compaq_cga.c vid_ega.c vid_et4000.c vid_et4000w32.c vid_genius.c vid_hercules.c \ -vid_ht216.c vid_icd2061.c vid_ics2595.c vid_incolor.c vid_mda.c vid_olivetti_m24.c vid_oti037.c vid_oti067.c vid_paradise.c vid_pc200.c \ -vid_pc1512.c vid_pc1640.c vid_pcjr.c vid_ps1_svga.c vid_s3.c vid_s3_virge.c vid_sdac_ramdac.c vid_sigma.c \ +vid_ht216.c vid_icd2061.c vid_ics2595.c vid_im1024.c vid_incolor.c vid_mda.c vid_olivetti_m24.c vid_oti037.c vid_oti067.c vid_paradise.c vid_pc200.c \ +vid_pc1512.c vid_pc1640.c vid_pcjr.c vid_pgc.c vid_ps1_svga.c vid_s3.c vid_s3_virge.c vid_sdac_ramdac.c vid_sigma.c \ vid_stg_ramdac.c vid_svga.c vid_svga_render.c vid_t1000.c vid_t3100e.c vid_tandy.c vid_tandysl.c vid_tgui9440.c \ vid_tkd8001_ramdac.c vid_tvga.c vid_unk_ramdac.c vid_vga.c vid_voodoo.c video.c wd76c10.c vid_wy700.c vt82c586b.c \ w83877tf.c x86seg.c x87.c xi8088.c xtide.c sound_dbopl.cc sound_resid.cc \ diff --git a/src/Makefile.linux32-wx-sdl2 b/src/Makefile.linux32-wx-sdl2 index 4ab2d80..f57456a 100644 --- a/src/Makefile.linux32-wx-sdl2 +++ b/src/Makefile.linux32-wx-sdl2 @@ -16,8 +16,8 @@ sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_sb. sound_speaker.o sound_ssi2001.o sound_wss.o sound_ym7128.o soundopenal.o t1000.o t3100e.o tandy_eeprom.o tandy_rom.o \ timer.o um8669f.o um8881f.o vid_ati_eeprom.o vid_ati_mach64.o vid_ati18800.o vid_ati28800.o \ vid_ati68860_ramdac.o vid_cga.o vid_cl5429.o vid_colorplus.o vid_compaq_cga.o vid_ega.o vid_et4000.o vid_et4000w32.o vid_genius.o vid_hercules.o \ -vid_ht216.o vid_icd2061.o vid_ics2595.o vid_incolor.o vid_mda.o vid_olivetti_m24.o vid_oti037.c vid_oti067.o vid_paradise.o vid_pc200.o \ -vid_pc1512.o vid_pc1640.o vid_pcjr.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_sigma.o \ +vid_ht216.o vid_icd2061.o vid_ics2595.o vid_im1024.o vid_incolor.o vid_mda.o vid_olivetti_m24.o vid_oti037.c vid_oti067.o vid_paradise.o vid_pc200.o \ +vid_pc1512.o vid_pc1640.o vid_pcjr.o vid_pgc.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_sigma.o \ vid_stg_ramdac.o vid_svga.o vid_svga_render.o vid_t1000.o vid_t3100e.o vid_tandy.o vid_tandysl.o vid_tgui9440.o \ vid_tkd8001_ramdac.o vid_tvga.o vid_unk_ramdac.o vid_vga.o vid_voodoo.o vid_wy700.o video.o wd76c10.o \ x86seg.o x87.o xi8088.c xtide.o zenith.o sound_dbopl.o sound_resid.o \ diff --git a/src/Makefile.linux64-wx-sdl2 b/src/Makefile.linux64-wx-sdl2 index 64e4e3c..6489ce6 100644 --- a/src/Makefile.linux64-wx-sdl2 +++ b/src/Makefile.linux64-wx-sdl2 @@ -16,8 +16,8 @@ sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_sb. sound_speaker.o sound_ssi2001.o sound_wss.o sound_ym7128.o soundopenal.o t1000.o t3100e.o tandy_eeprom.o tandy_rom.o \ timer.o um8669f.o um8881f.o vid_ati_eeprom.o vid_ati_mach64.o vid_ati18800.o vid_ati28800.o \ vid_ati68860_ramdac.o vid_cga.o vid_cl5429.o vid_colorplus.o vid_compaq_cga.o vid_ega.o vid_et4000.o vid_et4000w32.o vid_genius.o vid_hercules.o \ -vid_ht216.o vid_icd2061.o vid_ics2595.o vid_incolor.o vid_mda.o vid_olivetti_m24.o vid_oti037.c vid_oti067.o vid_paradise.o vid_pc200.o \ -vid_pc1512.o vid_pc1640.o vid_pcjr.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_sigma.o \ +vid_ht216.o vid_icd2061.o vid_ics2595.o vid_im1024.o vid_incolor.o vid_mda.o vid_olivetti_m24.o vid_oti037.c vid_oti067.o vid_paradise.o vid_pc200.o \ +vid_pc1512.o vid_pc1640.o vid_pcjr.o vid_pgc.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_sigma.o \ vid_stg_ramdac.o vid_svga.o vid_svga_render.o vid_t1000.o vid_t3100e.o vid_tandy.o vid_tandysl.o vid_tgui9440.o \ vid_tkd8001_ramdac.o vid_tvga.o vid_unk_ramdac.o vid_vga.o vid_voodoo.o vid_wy700.o video.o wd76c10.o \ x86seg.o x87.o xi8088.c xtide.o zenith.o sound_dbopl.o sound_resid.o \ diff --git a/src/Makefile.mingw-wx-sdl2 b/src/Makefile.mingw-wx-sdl2 index a1aa7ed..1f36ced 100644 --- a/src/Makefile.mingw-wx-sdl2 +++ b/src/Makefile.mingw-wx-sdl2 @@ -34,7 +34,7 @@ OBJ = 386.o 386_common.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acer38 vid_cl5429.o vid_colorplus.o vid_compaq_cga.o vid_ega.o vid_et4000.o vid_et4000w32.o \ vid_et4000w32i.o vid_genius.o vid_hercules.o vid_icd2061.o vid_ics2595.o vid_incolor.o vid_mda.o \ vid_olivetti_m24.o vid_oti037.c vid_oti067.o vid_paradise.o vid_pc1512.o vid_pc1640.o vid_pc200.o \ - vid_pcjr.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_sigma.o vid_stg_ramdac.o vid_svga.o \ + vid_pcjr.o vid_pgc.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_sigma.o vid_stg_ramdac.o vid_svga.o \ vid_svga_render.o vid_t1000.o vid_t3100e.o vid_tandy.o vid_tandysl.o vid_tgui9440.o \ vid_tkd8001_ramdac.o vid_tvga.o vid_unk_ramdac.o vid_vga.o vid_voodoo.o vid_wy700.o video.o \ vt82c586b.o w83877tf.o wd76c10.o x86seg.o x87.o xi8088.c xtide.o win-midi.o wx-main.o \ diff --git a/src/Makefile.mingw-wx-sdl2-network b/src/Makefile.mingw-wx-sdl2-network index 0b2a993..6d80726 100644 --- a/src/Makefile.mingw-wx-sdl2-network +++ b/src/Makefile.mingw-wx-sdl2-network @@ -17,9 +17,9 @@ OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o sound_sb.o sound_sb_dsp.o sound_sn76489.o sound_speaker.o sound_ssi2001.o sound_wss.o \ sound_ym7128.o soundopenal.o t1000.o t3100e.o tandy_eeprom.o tandy_rom.o timer.o um8881f.o um8669f.o vid_ati_eeprom.o vid_ati_mach64.o vid_ati18800.o \ vid_ati28800.o vid_ati68860_ramdac.o vid_cga.o vid_cl5429.o vid_colorplus.o vid_compaq_cga.o vid_ega.o vid_et4000.o \ - vid_et4000w32.o vid_et4000w32i.o vid_genius.o vid_hercules.o vid_ht216.o vid_icd2061.o vid_ics2595.o vid_incolor.o vid_mda.o \ + vid_et4000w32.o vid_et4000w32i.o vid_genius.o vid_hercules.o vid_ht216.o vid_icd2061.o vid_ics2595.o vid_im1024.o vid_incolor.o vid_mda.o \ vid_olivetti_m24.o vid_oti037.c vid_oti067.o vid_paradise.o vid_pc1512.o vid_pc1640.o vid_pc200.o \ - vid_pcjr.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_sigma.o vid_stg_ramdac.o vid_svga.o \ + vid_pcjr.o vid_pgc.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_sigma.o vid_stg_ramdac.o vid_svga.o \ vid_svga_render.o vid_t1000.o vid_t3100e.o vid_tandy.o vid_tandysl.o vid_tgui9440.o vid_tkd8001_ramdac.o vid_tvga.o vid_unk_ramdac.o \ vid_vga.o vid_voodoo.o vid_wy700.o video.o wd76c10.o x86seg.o x87.o xi8088.c xtide.o zenith.o win-midi.o \ wx-main.o wx-config_sel.o wx-dialogbox.o wx-utils.o wx-app.o wx-sdl2-joystick.o wx-sdl2-mouse.o wx-sdl2-keyboard.o wx-sdl2-video.o \ diff --git a/src/Makefile.osx64-wx-sdl2 b/src/Makefile.osx64-wx-sdl2 index 44169d3..536cfb3 100644 --- a/src/Makefile.osx64-wx-sdl2 +++ b/src/Makefile.osx64-wx-sdl2 @@ -15,8 +15,8 @@ sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_sb. sound_speaker.o sound_ssi2001.o sound_wss.o sound_ym7128.o soundopenal.o t1000.o t3100e.o tandy_eeprom.o tandy_rom.o \ timer.o um8669f.o um8881f.o vid_ati_eeprom.o vid_ati_mach64.o vid_ati18800.o vid_ati28800.o \ vid_ati68860_ramdac.o vid_cga.o vid_cl5429.o vid_colorplus.o vid_compaq_cga.o vid_ega.o vid_et4000.o vid_et4000w32.o vid_genius.o vid_hercules.o \ -vid_ht216.o vid_icd2061.o vid_ics2595.o vid_incolor.o vid_mda.o vid_olivetti_m24.o vid_oti037.c vid_oti067.o vid_paradise.o vid_pc200.o \ -vid_pc1512.o vid_pc1640.o vid_pcjr.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_sigma.o \ +vid_ht216.o vid_icd2061.o vid_ics2595.o vid_im1024.o vid_incolor.o vid_mda.o vid_olivetti_m24.o vid_oti037.c vid_oti067.o vid_paradise.o vid_pc200.o \ +vid_pc1512.o vid_pc1640.o vid_pcjr.o vid_pgc.o vid_ps1_svga.o vid_s3.o vid_s3_virge.o vid_sdac_ramdac.o vid_sigma.o \ vid_stg_ramdac.o vid_svga.o vid_svga_render.o vid_t1000.o vid_t3100e.o vid_tandy.o vid_tandysl.o vid_tgui9440.o \ vid_tkd8001_ramdac.o vid_tvga.o vid_unk_ramdac.o vid_vga.o vid_voodoo.o vid_wy700.o video.o wd76c10.o \ x86seg.o x87.o xi8088.c xtide.o zenith.o sound_dbopl.o sound_resid.o \ diff --git a/src/ibm.h b/src/ibm.h index 950122f..8bc82ea 100644 --- a/src/ibm.h +++ b/src/ibm.h @@ -250,6 +250,7 @@ enum ROM_ZD_SUPERS, /* [8088] Zenith Data Systems SupersPort */ ROM_PB410A, ROM_PPC512, + ROM_BULL_MICRAL_45, ROM_FIC_VA503P, ROM_MAX @@ -297,6 +298,8 @@ enum GFX_OTI037, /*Oak OTI-037*/ GFX_COMPAQ_CGA, /*Compaq CGA*/ GFX_SIGMA400, /*Sigma Designs Color 400 */ + GFX_PGC, /*Professional Graphics Controller */ + GFX_IM1024, /*Vermont Microsystems IM1024 */ GFX_MAX }; diff --git a/src/mem_bios.c b/src/mem_bios.c index b12887a..8f787ea 100644 --- a/src/mem_bios.c +++ b/src/mem_bios.c @@ -62,6 +62,7 @@ int loadbios() loadfont("mda.rom", FONT_MDA); loadfont("wy700.rom", FONT_WY700); loadfont("8x12.bin", FONT_MDSI); + loadfont("im1024font.bin", FONT_IM1024); biosmask = 0xffff; @@ -986,6 +987,19 @@ int loadbios() fclose(f); biosmask = 0x1ffff; return 1; + + case ROM_BULL_MICRAL_45: + f = romfopen("bull_micral_45/even.fil", "rb"); + ff = romfopen("bull_micral_45/odd.fil", "rb"); + if (!f || !ff) break; + for (c = 0x0000; c < 0x10000; c += 2) + { + rom[c] = getc(f); + rom[c+1] = getc(ff); + } + fclose(ff); + fclose(f); + return 1; case ROM_FIC_VA503P: f = romfopen("fic_va503p/je4333.bin","rb"); diff --git a/src/model.c b/src/model.c index d370533..8c364dc 100644 --- a/src/model.c +++ b/src/model.c @@ -160,6 +160,7 @@ MODEL models[] = {"[286] AMI 286 clone", ROM_AMI286, "ami286", { {"", cpus_286}, {"", NULL}, {"", NULL}}, MODEL_GFX_NONE|MODEL_AT|MODEL_HAS_IDE, 512,16384, 128, at_neat_init, NULL}, {"[286] Award 286 clone", ROM_AWARD286, "award286", { {"", cpus_286}, {"", NULL}, {"", NULL}}, MODEL_GFX_NONE|MODEL_AT|MODEL_HAS_IDE, 512,16384, 128, at_scat_init, NULL}, + {"[286] Bull Micral 45", ROM_BULL_MICRAL_45, "bull_micral_45", { {"", cpus_286}, {"", NULL}, {"", NULL}}, MODEL_GFX_NONE|MODEL_AT|MODEL_HAS_IDE, 1024, 6144, 128, ibm_at_init, NULL}, {"[286] Commodore PC 30 III", ROM_CMDPC30, "cmdpc30", { {"", cpus_286}, {"", NULL}, {"", NULL}}, MODEL_GFX_NONE|MODEL_AT|MODEL_HAS_IDE, 640,16384, 128, at_cbm_init, NULL}, {"[286] Compaq Portable II", ROM_COMPAQ_PII, "compaq_pii", { {"", cpus_286}, {"", NULL}, {"", NULL}}, MODEL_GFX_NONE|MODEL_AT|MODEL_HAS_IDE, 256,15872, 128, ibm_at_init, NULL}, {"[286] DELL System 200", ROM_DELL200, "dells200", { {"", cpus_286}, {"", NULL}, {"", NULL}}, MODEL_GFX_NONE|MODEL_AT, 640,16384, 128, dells200_init, NULL}, diff --git a/src/nvr.c b/src/nvr.c index 4fe085a..3c68768 100644 --- a/src/nvr.c +++ b/src/nvr.c @@ -322,6 +322,7 @@ void loadnvr() case ROM_IBMPS2_M70_TYPE4: f = nvrfopen("ibmps2_m70_type4.nvr","rb"); break; case ROM_TULIP_TC7: f = nvrfopen("tulip_tc7.nvr", "rb"); break; case ROM_PB410A: f = nvrfopen("pb410a.nvr", "rb"); nvrmask = 127; break; + case ROM_BULL_MICRAL_45: f = nvrfopen("bull_micral_45.nvr", "rb"); break; case ROM_FIC_VA503P: f = nvrfopen("fic_va503p.nvr", "rb"); nvrmask = 127; break; default: return; @@ -421,6 +422,7 @@ void savenvr() case ROM_IBMPS2_M70_TYPE4: f = nvrfopen("ibmps2_m70_type4.nvr","wb"); break; case ROM_TULIP_TC7: f = nvrfopen("tulip_tc7.nvr", "wb"); break; case ROM_PB410A: f = nvrfopen("pb410a.nvr", "wb"); break; + case ROM_BULL_MICRAL_45: f = nvrfopen("bull_micral_45.nvr", "wb"); break; case ROM_FIC_VA503P: f = nvrfopen("fic_va503p.nvr", "wb"); break; default: return; diff --git a/src/pgc_palettes.h b/src/pgc_palettes.h new file mode 100644 index 0000000..2d3d0dd --- /dev/null +++ b/src/pgc_palettes.h @@ -0,0 +1,1554 @@ +/* Palette 0: Default */ + { + makecol(0x00,0x00,0x00), + makecol(0x11,0x11,0x11), + makecol(0x22,0x22,0x22), + makecol(0x33,0x33,0x33), + makecol(0x44,0x44,0x44), + makecol(0x55,0x55,0x55), + makecol(0x66,0x66,0x66), + makecol(0x77,0x77,0x77), + makecol(0x88,0x88,0x88), + makecol(0x99,0x99,0x99), + makecol(0xaa,0xaa,0xaa), + makecol(0xbb,0xbb,0xbb), + makecol(0xcc,0xcc,0xcc), + makecol(0xdd,0xdd,0xdd), + makecol(0xee,0xee,0xee), + makecol(0xff,0xff,0xff), + makecol(0x00,0x00,0x00), + makecol(0x00,0x22,0x00), + makecol(0x00,0x44,0x00), + makecol(0x00,0x66,0x00), + makecol(0x00,0x88,0x00), + makecol(0x00,0xaa,0x00), + makecol(0x00,0xcc,0x00), + makecol(0x00,0xee,0x00), + makecol(0x00,0xff,0x00), + makecol(0x22,0xff,0x22), + makecol(0x44,0xff,0x44), + makecol(0x66,0xff,0x66), + makecol(0x88,0xff,0x88), + makecol(0xaa,0xff,0xaa), + makecol(0xcc,0xff,0xcc), + makecol(0xee,0xff,0xee), + makecol(0x00,0x00,0x00), + makecol(0x00,0x22,0x11), + makecol(0x00,0x44,0x22), + makecol(0x00,0x66,0x33), + makecol(0x00,0x88,0x44), + makecol(0x00,0xaa,0x55), + makecol(0x00,0xcc,0x66), + makecol(0x00,0xee,0x77), + makecol(0x00,0xff,0x88), + makecol(0x22,0xff,0x99), + makecol(0x44,0xff,0xaa), + makecol(0x66,0xff,0xbb), + makecol(0x88,0xff,0xcc), + makecol(0xaa,0xff,0xdd), + makecol(0xcc,0xff,0xee), + makecol(0xee,0xff,0xff), + makecol(0x00,0x00,0x00), + makecol(0x00,0x22,0x22), + makecol(0x00,0x44,0x44), + makecol(0x00,0x66,0x66), + makecol(0x00,0x88,0x88), + makecol(0x00,0xaa,0xaa), + makecol(0x00,0xcc,0xcc), + makecol(0x00,0xee,0xee), + makecol(0x00,0xff,0xff), + makecol(0x22,0xff,0xff), + makecol(0x44,0xff,0xff), + makecol(0x66,0xff,0xff), + makecol(0x88,0xff,0xff), + makecol(0xaa,0xff,0xff), + makecol(0xcc,0xff,0xff), + makecol(0xee,0xff,0xff), + makecol(0x00,0x00,0x00), + makecol(0x00,0x11,0x22), + makecol(0x00,0x22,0x44), + makecol(0x00,0x33,0x66), + makecol(0x00,0x44,0x88), + makecol(0x00,0x55,0xaa), + makecol(0x00,0x66,0xcc), + makecol(0x00,0x77,0xee), + makecol(0x00,0x88,0xff), + makecol(0x22,0x99,0xff), + makecol(0x44,0xaa,0xff), + makecol(0x66,0xbb,0xff), + makecol(0x88,0xcc,0xff), + makecol(0xaa,0xdd,0xff), + makecol(0xcc,0xee,0xff), + makecol(0xee,0xff,0xff), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x22), + makecol(0x00,0x00,0x44), + makecol(0x00,0x00,0x66), + makecol(0x00,0x00,0x88), + makecol(0x00,0x00,0xaa), + makecol(0x00,0x00,0xcc), + makecol(0x00,0x00,0xee), + makecol(0x00,0x00,0xff), + makecol(0x22,0x22,0xff), + makecol(0x44,0x44,0xff), + makecol(0x66,0x66,0xff), + makecol(0x88,0x88,0xff), + makecol(0xaa,0xaa,0xff), + makecol(0xcc,0xcc,0xff), + makecol(0xee,0xee,0xff), + makecol(0x00,0x00,0x00), + makecol(0x11,0x00,0x22), + makecol(0x22,0x00,0x44), + makecol(0x33,0x00,0x66), + makecol(0x44,0x00,0x88), + makecol(0x55,0x00,0xaa), + makecol(0x66,0x00,0xcc), + makecol(0x77,0x00,0xee), + makecol(0x88,0x00,0xff), + makecol(0x99,0x22,0xff), + makecol(0xaa,0x44,0xff), + makecol(0xbb,0x66,0xff), + makecol(0xcc,0x88,0xff), + makecol(0xdd,0xaa,0xff), + makecol(0xee,0xcc,0xff), + makecol(0xff,0xee,0xff), + makecol(0x00,0x00,0x00), + makecol(0x22,0x00,0x22), + makecol(0x44,0x00,0x44), + makecol(0x66,0x00,0x66), + makecol(0x88,0x00,0x88), + makecol(0xaa,0x00,0xaa), + makecol(0xcc,0x00,0xcc), + makecol(0xee,0x00,0xee), + makecol(0xff,0x00,0xff), + makecol(0xff,0x22,0xff), + makecol(0xff,0x44,0xff), + makecol(0xff,0x66,0xff), + makecol(0xff,0x88,0xff), + makecol(0xff,0xaa,0xff), + makecol(0xff,0xcc,0xff), + makecol(0xff,0xee,0xff), + makecol(0x00,0x00,0x00), + makecol(0x22,0x00,0x11), + makecol(0x44,0x00,0x22), + makecol(0x66,0x00,0x33), + makecol(0x88,0x00,0x44), + makecol(0xaa,0x00,0x55), + makecol(0xcc,0x00,0x66), + makecol(0xee,0x00,0x77), + makecol(0xff,0x00,0x88), + makecol(0xff,0x22,0x99), + makecol(0xff,0x44,0xaa), + makecol(0xff,0x66,0xbb), + makecol(0xff,0x88,0xcc), + makecol(0xff,0xaa,0xdd), + makecol(0xff,0xcc,0xee), + makecol(0xff,0xee,0xff), + makecol(0x00,0x00,0x00), + makecol(0x22,0x00,0x00), + makecol(0x44,0x00,0x00), + makecol(0x66,0x00,0x00), + makecol(0x88,0x00,0x00), + makecol(0xaa,0x00,0x00), + makecol(0xcc,0x00,0x00), + makecol(0xee,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x22,0x22), + makecol(0xff,0x44,0x44), + makecol(0xff,0x66,0x66), + makecol(0xff,0x88,0x88), + makecol(0xff,0xaa,0xaa), + makecol(0xff,0xcc,0xcc), + makecol(0xff,0xee,0xee), + makecol(0x00,0x00,0x00), + makecol(0x22,0x11,0x00), + makecol(0x44,0x22,0x00), + makecol(0x66,0x33,0x00), + makecol(0x88,0x44,0x00), + makecol(0xaa,0x55,0x00), + makecol(0xcc,0x66,0x00), + makecol(0xee,0x77,0x00), + makecol(0xff,0x88,0x00), + makecol(0xff,0x99,0x22), + makecol(0xff,0xaa,0x44), + makecol(0xff,0xbb,0x66), + makecol(0xff,0xcc,0x88), + makecol(0xff,0xdd,0xaa), + makecol(0xff,0xee,0xcc), + makecol(0xff,0xff,0xee), + makecol(0x00,0x00,0x00), + makecol(0x22,0x22,0x00), + makecol(0x44,0x44,0x00), + makecol(0x66,0x66,0x00), + makecol(0x88,0x88,0x00), + makecol(0xaa,0xaa,0x00), + makecol(0xcc,0xcc,0x00), + makecol(0xee,0xee,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x22), + makecol(0xff,0xff,0x44), + makecol(0xff,0xff,0x66), + makecol(0xff,0xff,0x88), + makecol(0xff,0xff,0xaa), + makecol(0xff,0xff,0xcc), + makecol(0xff,0xff,0xee), + makecol(0x00,0x00,0x00), + makecol(0x11,0x22,0x00), + makecol(0x22,0x44,0x00), + makecol(0x33,0x66,0x00), + makecol(0x44,0x88,0x00), + makecol(0x55,0xaa,0x00), + makecol(0x66,0xcc,0x00), + makecol(0x77,0xee,0x00), + makecol(0x88,0xff,0x00), + makecol(0x99,0xff,0x22), + makecol(0xaa,0xff,0x44), + makecol(0xbb,0xff,0x66), + makecol(0xcc,0xff,0x88), + makecol(0xdd,0xff,0xaa), + makecol(0xee,0xff,0xcc), + makecol(0xff,0xff,0xee), + makecol(0x00,0x00,0x00), + makecol(0x00,0x11,0x00), + makecol(0x11,0x33,0x11), + makecol(0x11,0x44,0x11), + makecol(0x22,0x66,0x22), + makecol(0x22,0x77,0x22), + makecol(0x33,0x99,0x33), + makecol(0x33,0xaa,0x33), + makecol(0x44,0xcc,0x44), + makecol(0x55,0xcc,0x55), + makecol(0x77,0xdd,0x77), + makecol(0x88,0xdd,0x88), + makecol(0xaa,0xee,0xaa), + makecol(0xbb,0xee,0xbb), + makecol(0xdd,0xff,0xdd), + makecol(0xee,0xff,0xee), + makecol(0x00,0x00,0x00), + makecol(0x11,0x00,0x00), + makecol(0x33,0x11,0x11), + makecol(0x44,0x11,0x11), + makecol(0x66,0x22,0x22), + makecol(0x77,0x22,0x22), + makecol(0x99,0x33,0x33), + makecol(0xaa,0x33,0x33), + makecol(0xcc,0x44,0x44), + makecol(0xcc,0x55,0x55), + makecol(0xdd,0x77,0x77), + makecol(0xdd,0x88,0x88), + makecol(0xee,0xaa,0xaa), + makecol(0xee,0xbb,0xbb), + makecol(0xff,0xdd,0xdd), + makecol(0xff,0xee,0xee), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x11), + makecol(0x11,0x11,0x33), + makecol(0x11,0x11,0x44), + makecol(0x22,0x22,0x66), + makecol(0x22,0x22,0x77), + makecol(0x33,0x33,0x99), + makecol(0x33,0x33,0xaa), + makecol(0x44,0x44,0xcc), + makecol(0x55,0x55,0xcc), + makecol(0x77,0x77,0xdd), + makecol(0x88,0x88,0xdd), + makecol(0xaa,0xaa,0xee), + makecol(0xbb,0xbb,0xee), + makecol(0xdd,0xdd,0xff), + makecol(0xee,0xee,0xff), + }, +/* Palette 1: 16-colour palette */ + { + makecol(0x88,0x66,0xdd), + makecol(0x00,0x00,0x00), + makecol(0x44,0x77,0x22), + makecol(0x77,0xaa,0x44), + makecol(0x00,0x77,0x00), + makecol(0x00,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x99,0xee,0x66), + makecol(0x77,0x77,0x77), + makecol(0xff,0xff,0xff), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x44,0x77,0x22), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x77,0xaa,0x44), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x77), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x99,0xee,0x66), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0x77,0x77,0x77), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + makecol(0xff,0xff,0xff), + }, +/* Palette 2: 2-3-3 truecolour */ + { + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x33), + makecol(0x00,0x00,0x55), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x99), + makecol(0x00,0x00,0xbb), + makecol(0x00,0x00,0xdd), + makecol(0x00,0x00,0xff), + makecol(0x33,0x00,0x00), + makecol(0x33,0x00,0x33), + makecol(0x33,0x00,0x55), + makecol(0x33,0x00,0x77), + makecol(0x33,0x00,0x99), + makecol(0x33,0x00,0xbb), + makecol(0x33,0x00,0xdd), + makecol(0x33,0x00,0xff), + makecol(0x55,0x00,0x00), + makecol(0x55,0x00,0x33), + makecol(0x55,0x00,0x55), + makecol(0x55,0x00,0x77), + makecol(0x55,0x00,0x99), + makecol(0x55,0x00,0xbb), + makecol(0x55,0x00,0xdd), + makecol(0x55,0x00,0xff), + makecol(0x77,0x00,0x00), + makecol(0x77,0x00,0x33), + makecol(0x77,0x00,0x55), + makecol(0x77,0x00,0x77), + makecol(0x77,0x00,0x99), + makecol(0x77,0x00,0xbb), + makecol(0x77,0x00,0xdd), + makecol(0x77,0x00,0xff), + makecol(0x99,0x00,0x00), + makecol(0x99,0x00,0x33), + makecol(0x99,0x00,0x55), + makecol(0x99,0x00,0x77), + makecol(0x99,0x00,0x99), + makecol(0x99,0x00,0xbb), + makecol(0x99,0x00,0xdd), + makecol(0x99,0x00,0xff), + makecol(0xbb,0x00,0x00), + makecol(0xbb,0x00,0x33), + makecol(0xbb,0x00,0x55), + makecol(0xbb,0x00,0x77), + makecol(0xbb,0x00,0x99), + makecol(0xbb,0x00,0xbb), + makecol(0xbb,0x00,0xdd), + makecol(0xbb,0x00,0xff), + makecol(0xdd,0x00,0x00), + makecol(0xdd,0x00,0x33), + makecol(0xdd,0x00,0x55), + makecol(0xdd,0x00,0x77), + makecol(0xdd,0x00,0x99), + makecol(0xdd,0x00,0xbb), + makecol(0xdd,0x00,0xdd), + makecol(0xdd,0x00,0xff), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x33), + makecol(0xff,0x00,0x55), + makecol(0xff,0x00,0x77), + makecol(0xff,0x00,0x99), + makecol(0xff,0x00,0xbb), + makecol(0xff,0x00,0xdd), + makecol(0xff,0x00,0xff), + makecol(0x00,0x55,0x00), + makecol(0x00,0x55,0x33), + makecol(0x00,0x55,0x55), + makecol(0x00,0x55,0x77), + makecol(0x00,0x55,0x99), + makecol(0x00,0x55,0xbb), + makecol(0x00,0x55,0xdd), + makecol(0x00,0x55,0xff), + makecol(0x33,0x55,0x00), + makecol(0x33,0x55,0x33), + makecol(0x33,0x55,0x55), + makecol(0x33,0x55,0x77), + makecol(0x33,0x55,0x99), + makecol(0x33,0x55,0xbb), + makecol(0x33,0x55,0xdd), + makecol(0x33,0x55,0xff), + makecol(0x55,0x55,0x00), + makecol(0x55,0x55,0x33), + makecol(0x55,0x55,0x55), + makecol(0x55,0x55,0x77), + makecol(0x55,0x55,0x99), + makecol(0x55,0x55,0xbb), + makecol(0x55,0x55,0xdd), + makecol(0x55,0x55,0xff), + makecol(0x77,0x55,0x00), + makecol(0x77,0x55,0x33), + makecol(0x77,0x55,0x55), + makecol(0x77,0x55,0x77), + makecol(0x77,0x55,0x99), + makecol(0x77,0x55,0xbb), + makecol(0x77,0x55,0xdd), + makecol(0x77,0x55,0xff), + makecol(0x99,0x55,0x00), + makecol(0x99,0x55,0x33), + makecol(0x99,0x55,0x55), + makecol(0x99,0x55,0x77), + makecol(0x99,0x55,0x99), + makecol(0x99,0x55,0xbb), + makecol(0x99,0x55,0xdd), + makecol(0x99,0x55,0xff), + makecol(0xbb,0x55,0x00), + makecol(0xbb,0x55,0x33), + makecol(0xbb,0x55,0x55), + makecol(0xbb,0x55,0x77), + makecol(0xbb,0x55,0x99), + makecol(0xbb,0x55,0xbb), + makecol(0xbb,0x55,0xdd), + makecol(0xbb,0x55,0xff), + makecol(0xdd,0x55,0x00), + makecol(0xdd,0x55,0x33), + makecol(0xdd,0x55,0x55), + makecol(0xdd,0x55,0x77), + makecol(0xdd,0x55,0x99), + makecol(0xdd,0x55,0xbb), + makecol(0xdd,0x55,0xdd), + makecol(0xdd,0x55,0xff), + makecol(0xff,0x55,0x00), + makecol(0xff,0x55,0x33), + makecol(0xff,0x55,0x55), + makecol(0xff,0x55,0x77), + makecol(0xff,0x55,0x99), + makecol(0xff,0x55,0xbb), + makecol(0xff,0x55,0xdd), + makecol(0xff,0x55,0xff), + makecol(0x00,0xaa,0x00), + makecol(0x00,0xaa,0x33), + makecol(0x00,0xaa,0x55), + makecol(0x00,0xaa,0x77), + makecol(0x00,0xaa,0x99), + makecol(0x00,0xaa,0xbb), + makecol(0x00,0xaa,0xdd), + makecol(0x00,0xaa,0xff), + makecol(0x33,0xaa,0x00), + makecol(0x33,0xaa,0x33), + makecol(0x33,0xaa,0x55), + makecol(0x33,0xaa,0x77), + makecol(0x33,0xaa,0x99), + makecol(0x33,0xaa,0xbb), + makecol(0x33,0xaa,0xdd), + makecol(0x33,0xaa,0xff), + makecol(0x55,0xaa,0x00), + makecol(0x55,0xaa,0x33), + makecol(0x55,0xaa,0x55), + makecol(0x55,0xaa,0x77), + makecol(0x55,0xaa,0x99), + makecol(0x55,0xaa,0xbb), + makecol(0x55,0xaa,0xdd), + makecol(0x55,0xaa,0xff), + makecol(0x77,0xaa,0x00), + makecol(0x77,0xaa,0x33), + makecol(0x77,0xaa,0x55), + makecol(0x77,0xaa,0x77), + makecol(0x77,0xaa,0x99), + makecol(0x77,0xaa,0xbb), + makecol(0x77,0xaa,0xdd), + makecol(0x77,0xaa,0xff), + makecol(0x99,0xaa,0x00), + makecol(0x99,0xaa,0x33), + makecol(0x99,0xaa,0x55), + makecol(0x99,0xaa,0x77), + makecol(0x99,0xaa,0x99), + makecol(0x99,0xaa,0xbb), + makecol(0x99,0xaa,0xdd), + makecol(0x99,0xaa,0xff), + makecol(0xbb,0xaa,0x00), + makecol(0xbb,0xaa,0x33), + makecol(0xbb,0xaa,0x55), + makecol(0xbb,0xaa,0x77), + makecol(0xbb,0xaa,0x99), + makecol(0xbb,0xaa,0xbb), + makecol(0xbb,0xaa,0xdd), + makecol(0xbb,0xaa,0xff), + makecol(0xdd,0xaa,0x00), + makecol(0xdd,0xaa,0x33), + makecol(0xdd,0xaa,0x55), + makecol(0xdd,0xaa,0x77), + makecol(0xdd,0xaa,0x99), + makecol(0xdd,0xaa,0xbb), + makecol(0xdd,0xaa,0xdd), + makecol(0xdd,0xaa,0xff), + makecol(0xff,0xaa,0x00), + makecol(0xff,0xaa,0x33), + makecol(0xff,0xaa,0x55), + makecol(0xff,0xaa,0x77), + makecol(0xff,0xaa,0x99), + makecol(0xff,0xaa,0xbb), + makecol(0xff,0xaa,0xdd), + makecol(0xff,0xaa,0xee), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x33), + makecol(0x00,0xff,0x55), + makecol(0x00,0xff,0x77), + makecol(0x00,0xff,0x99), + makecol(0x00,0xff,0xbb), + makecol(0x00,0xff,0xdd), + makecol(0x00,0xff,0xff), + makecol(0x33,0xff,0x00), + makecol(0x33,0xff,0x33), + makecol(0x33,0xff,0x55), + makecol(0x33,0xff,0x77), + makecol(0x33,0xff,0x99), + makecol(0x33,0xff,0xbb), + makecol(0x33,0xff,0xdd), + makecol(0x33,0xff,0xff), + makecol(0x55,0xff,0x00), + makecol(0x55,0xff,0x33), + makecol(0x55,0xff,0x55), + makecol(0x55,0xff,0x77), + makecol(0x55,0xff,0x99), + makecol(0x55,0xff,0xbb), + makecol(0x55,0xff,0xdd), + makecol(0x55,0xff,0xff), + makecol(0x77,0xff,0x00), + makecol(0x77,0xff,0x33), + makecol(0x77,0xff,0x55), + makecol(0x77,0xff,0x77), + makecol(0x77,0xff,0x99), + makecol(0x77,0xff,0xbb), + makecol(0x77,0xff,0xdd), + makecol(0x77,0xff,0xff), + makecol(0x99,0xff,0x00), + makecol(0x99,0xff,0x33), + makecol(0x99,0xff,0x55), + makecol(0x99,0xff,0x77), + makecol(0x99,0xff,0x99), + makecol(0x99,0xff,0xbb), + makecol(0x99,0xff,0xdd), + makecol(0x99,0xff,0xff), + makecol(0xbb,0xff,0x00), + makecol(0xbb,0xff,0x33), + makecol(0xbb,0xff,0x55), + makecol(0xbb,0xff,0x77), + makecol(0xbb,0xff,0x99), + makecol(0xbb,0xff,0xbb), + makecol(0xbb,0xff,0xdd), + makecol(0xbb,0xff,0xff), + makecol(0xdd,0xff,0x00), + makecol(0xdd,0xff,0x33), + makecol(0xdd,0xff,0x55), + makecol(0xdd,0xff,0x77), + makecol(0xdd,0xff,0x99), + makecol(0xdd,0xff,0xbb), + makecol(0xdd,0xff,0xdd), + makecol(0xdd,0xff,0xff), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x33), + makecol(0xff,0xff,0x55), + makecol(0xff,0xff,0x77), + makecol(0xff,0xff,0x99), + makecol(0xff,0xff,0xbb), + makecol(0xff,0xff,0xdd), + makecol(0xff,0xff,0xff), + }, +/* Palette 3: 3-2-3 truecolour */ + { + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x33), + makecol(0x00,0x00,0x55), + makecol(0x00,0x00,0x77), + makecol(0x00,0x00,0x99), + makecol(0x00,0x00,0xbb), + makecol(0x00,0x00,0xdd), + makecol(0x00,0x00,0xff), + makecol(0x55,0x00,0x00), + makecol(0x55,0x00,0x33), + makecol(0x55,0x00,0x55), + makecol(0x55,0x00,0x77), + makecol(0x55,0x00,0x99), + makecol(0x55,0x00,0xbb), + makecol(0x55,0x00,0xdd), + makecol(0x55,0x00,0xff), + makecol(0xaa,0x00,0x00), + makecol(0xaa,0x00,0x33), + makecol(0xaa,0x00,0x55), + makecol(0xaa,0x00,0x77), + makecol(0xaa,0x00,0x99), + makecol(0xaa,0x00,0xbb), + makecol(0xaa,0x00,0xdd), + makecol(0xaa,0x00,0xff), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x33), + makecol(0xff,0x00,0x55), + makecol(0xff,0x00,0x77), + makecol(0xff,0x00,0x99), + makecol(0xff,0x00,0xbb), + makecol(0xff,0x00,0xdd), + makecol(0xff,0x00,0xff), + makecol(0x00,0x33,0x00), + makecol(0x00,0x33,0x33), + makecol(0x00,0x33,0x55), + makecol(0x00,0x33,0x77), + makecol(0x00,0x33,0x99), + makecol(0x00,0x33,0xbb), + makecol(0x00,0x33,0xdd), + makecol(0x00,0x33,0xff), + makecol(0x55,0x33,0x00), + makecol(0x55,0x33,0x33), + makecol(0x55,0x33,0x55), + makecol(0x55,0x33,0x77), + makecol(0x55,0x33,0x99), + makecol(0x55,0x33,0xbb), + makecol(0x55,0x33,0xdd), + makecol(0x55,0x33,0xff), + makecol(0xaa,0x33,0x00), + makecol(0xaa,0x33,0x33), + makecol(0xaa,0x33,0x55), + makecol(0xaa,0x33,0x77), + makecol(0xaa,0x33,0x99), + makecol(0xaa,0x33,0xbb), + makecol(0xaa,0x33,0xdd), + makecol(0xaa,0x33,0xff), + makecol(0xff,0x33,0x00), + makecol(0xff,0x33,0x33), + makecol(0xff,0x33,0x55), + makecol(0xff,0x33,0x77), + makecol(0xff,0x33,0x99), + makecol(0xff,0x33,0xbb), + makecol(0xff,0x33,0xdd), + makecol(0xff,0x33,0xff), + makecol(0x00,0x55,0x00), + makecol(0x00,0x55,0x33), + makecol(0x00,0x55,0x55), + makecol(0x00,0x55,0x77), + makecol(0x00,0x55,0x99), + makecol(0x00,0x55,0xbb), + makecol(0x00,0x55,0xdd), + makecol(0x00,0x55,0xff), + makecol(0x55,0x55,0x00), + makecol(0x55,0x55,0x33), + makecol(0x55,0x55,0x55), + makecol(0x55,0x55,0x77), + makecol(0x55,0x55,0x99), + makecol(0x55,0x55,0xbb), + makecol(0x55,0x55,0xdd), + makecol(0x55,0x55,0xff), + makecol(0xaa,0x55,0x00), + makecol(0xaa,0x55,0x33), + makecol(0xaa,0x55,0x55), + makecol(0xaa,0x55,0x77), + makecol(0xaa,0x55,0x99), + makecol(0xaa,0x55,0xbb), + makecol(0xaa,0x55,0xdd), + makecol(0xaa,0x55,0xff), + makecol(0xff,0x55,0x00), + makecol(0xff,0x55,0x33), + makecol(0xff,0x55,0x55), + makecol(0xff,0x55,0x77), + makecol(0xff,0x55,0x99), + makecol(0xff,0x55,0xbb), + makecol(0xff,0x55,0xdd), + makecol(0xff,0x55,0xff), + makecol(0x00,0x77,0x00), + makecol(0x00,0x77,0x33), + makecol(0x00,0x77,0x55), + makecol(0x00,0x77,0x77), + makecol(0x00,0x77,0x99), + makecol(0x00,0x77,0xbb), + makecol(0x00,0x77,0xdd), + makecol(0x00,0x77,0xff), + makecol(0x55,0x77,0x00), + makecol(0x55,0x77,0x33), + makecol(0x55,0x77,0x55), + makecol(0x55,0x77,0x77), + makecol(0x55,0x77,0x99), + makecol(0x55,0x77,0xbb), + makecol(0x55,0x77,0xdd), + makecol(0x55,0x77,0xff), + makecol(0xaa,0x77,0x00), + makecol(0xaa,0x77,0x33), + makecol(0xaa,0x77,0x55), + makecol(0xaa,0x77,0x77), + makecol(0xaa,0x77,0x99), + makecol(0xaa,0x77,0xbb), + makecol(0xaa,0x77,0xdd), + makecol(0xaa,0x77,0xff), + makecol(0xff,0x77,0x00), + makecol(0xff,0x77,0x33), + makecol(0xff,0x77,0x55), + makecol(0xff,0x77,0x77), + makecol(0xff,0x77,0x99), + makecol(0xff,0x77,0xbb), + makecol(0xff,0x77,0xdd), + makecol(0xff,0x77,0xff), + makecol(0x00,0x99,0x00), + makecol(0x00,0x99,0x33), + makecol(0x00,0x99,0x55), + makecol(0x00,0x99,0x77), + makecol(0x00,0x99,0x99), + makecol(0x00,0x99,0xbb), + makecol(0x00,0x99,0xdd), + makecol(0x00,0x99,0xff), + makecol(0x55,0x99,0x00), + makecol(0x55,0x99,0x33), + makecol(0x55,0x99,0x55), + makecol(0x55,0x99,0x77), + makecol(0x55,0x99,0x99), + makecol(0x55,0x99,0xbb), + makecol(0x55,0x99,0xdd), + makecol(0x55,0x99,0xff), + makecol(0xaa,0x99,0x00), + makecol(0xaa,0x99,0x33), + makecol(0xaa,0x99,0x55), + makecol(0xaa,0x99,0x77), + makecol(0xaa,0x99,0x99), + makecol(0xaa,0x99,0xbb), + makecol(0xaa,0x99,0xdd), + makecol(0xaa,0x99,0xff), + makecol(0xff,0x99,0x00), + makecol(0xff,0x99,0x33), + makecol(0xff,0x99,0x55), + makecol(0xff,0x99,0x77), + makecol(0xff,0x99,0x99), + makecol(0xff,0x99,0xbb), + makecol(0xff,0x99,0xdd), + makecol(0xff,0x99,0xff), + makecol(0x00,0xbb,0x00), + makecol(0x00,0xbb,0x33), + makecol(0x00,0xbb,0x55), + makecol(0x00,0xbb,0x77), + makecol(0x00,0xbb,0x99), + makecol(0x00,0xbb,0xbb), + makecol(0x00,0xbb,0xdd), + makecol(0x00,0xbb,0xff), + makecol(0x55,0xbb,0x00), + makecol(0x55,0xbb,0x33), + makecol(0x55,0xbb,0x55), + makecol(0x55,0xbb,0x77), + makecol(0x55,0xbb,0x99), + makecol(0x55,0xbb,0xbb), + makecol(0x55,0xbb,0xdd), + makecol(0x55,0xbb,0xff), + makecol(0xaa,0xbb,0x00), + makecol(0xaa,0xbb,0x33), + makecol(0xaa,0xbb,0x55), + makecol(0xaa,0xbb,0x77), + makecol(0xaa,0xbb,0x99), + makecol(0xaa,0xbb,0xbb), + makecol(0xaa,0xbb,0xdd), + makecol(0xaa,0xbb,0xff), + makecol(0xff,0xbb,0x00), + makecol(0xff,0xbb,0x33), + makecol(0xff,0xbb,0x55), + makecol(0xff,0xbb,0x77), + makecol(0xff,0xbb,0x99), + makecol(0xff,0xbb,0xbb), + makecol(0xff,0xbb,0xdd), + makecol(0xff,0xbb,0xff), + makecol(0x00,0xdd,0x00), + makecol(0x00,0xdd,0x33), + makecol(0x00,0xdd,0x55), + makecol(0x00,0xdd,0x77), + makecol(0x00,0xdd,0x99), + makecol(0x00,0xdd,0xbb), + makecol(0x00,0xdd,0xdd), + makecol(0x00,0xdd,0xff), + makecol(0x55,0xdd,0x00), + makecol(0x55,0xdd,0x33), + makecol(0x55,0xdd,0x55), + makecol(0x55,0xdd,0x77), + makecol(0x55,0xdd,0x99), + makecol(0x55,0xdd,0xbb), + makecol(0x55,0xdd,0xdd), + makecol(0x55,0xdd,0xff), + makecol(0xaa,0xdd,0x00), + makecol(0xaa,0xdd,0x33), + makecol(0xaa,0xdd,0x55), + makecol(0xaa,0xdd,0x77), + makecol(0xaa,0xdd,0x99), + makecol(0xaa,0xdd,0xbb), + makecol(0xaa,0xdd,0xdd), + makecol(0xaa,0xdd,0xff), + makecol(0xff,0xdd,0x00), + makecol(0xff,0xdd,0x33), + makecol(0xff,0xdd,0x55), + makecol(0xff,0xdd,0x77), + makecol(0xff,0xdd,0x99), + makecol(0xff,0xdd,0xbb), + makecol(0xff,0xdd,0xdd), + makecol(0xff,0xdd,0xff), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x33), + makecol(0x00,0xff,0x55), + makecol(0x00,0xff,0x77), + makecol(0x00,0xff,0x99), + makecol(0x00,0xff,0xbb), + makecol(0x00,0xff,0xdd), + makecol(0x00,0xff,0xff), + makecol(0x55,0xff,0x00), + makecol(0x55,0xff,0x33), + makecol(0x55,0xff,0x55), + makecol(0x55,0xff,0x77), + makecol(0x55,0xff,0x99), + makecol(0x55,0xff,0xbb), + makecol(0x55,0xff,0xdd), + makecol(0x55,0xff,0xff), + makecol(0xaa,0xff,0x00), + makecol(0xaa,0xff,0x33), + makecol(0xaa,0xff,0x55), + makecol(0xaa,0xff,0x77), + makecol(0xaa,0xff,0x99), + makecol(0xaa,0xff,0xbb), + makecol(0xaa,0xff,0xdd), + makecol(0xaa,0xff,0xff), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x33), + makecol(0xff,0xff,0x55), + makecol(0xff,0xff,0x77), + makecol(0xff,0xff,0x99), + makecol(0xff,0xff,0xbb), + makecol(0xff,0xff,0xdd), + makecol(0xff,0xff,0xff), + }, +/* Palette 4: 3-3-2 truecolour */ + { + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x55), + makecol(0x00, 0x00, 0xaa), + makecol(0x00, 0x00, 0xff), + makecol(0x00, 0x33, 0x00), + makecol(0x00, 0x33, 0x55), + makecol(0x00, 0x33, 0xaa), + makecol(0x00, 0x33, 0xff), + makecol(0x00, 0x55, 0x00), + makecol(0x00, 0x55, 0x55), + makecol(0x00, 0x55, 0xaa), + makecol(0x00, 0x55, 0xff), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x55), + makecol(0x00, 0x77, 0xaa), + makecol(0x00, 0x77, 0xff), + makecol(0x00, 0x99, 0x00), + makecol(0x00, 0x99, 0x55), + makecol(0x00, 0x99, 0xaa), + makecol(0x00, 0x99, 0xff), + makecol(0x00, 0xbb, 0x00), + makecol(0x00, 0xbb, 0x55), + makecol(0x00, 0xbb, 0xaa), + makecol(0x00, 0xbb, 0xff), + makecol(0x00, 0xdd, 0x00), + makecol(0x00, 0xdd, 0x55), + makecol(0x00, 0xdd, 0xaa), + makecol(0x00, 0xdd, 0xff), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x55), + makecol(0x00, 0xff, 0xaa), + makecol(0x00, 0xff, 0xff), + makecol(0x33, 0x00, 0x00), + makecol(0x33, 0x00, 0x55), + makecol(0x33, 0x00, 0xaa), + makecol(0x33, 0x00, 0xff), + makecol(0x33, 0x33, 0x00), + makecol(0x33, 0x33, 0x55), + makecol(0x33, 0x33, 0xaa), + makecol(0x33, 0x33, 0xff), + makecol(0x33, 0x55, 0x00), + makecol(0x33, 0x55, 0x55), + makecol(0x33, 0x55, 0xaa), + makecol(0x33, 0x55, 0xff), + makecol(0x33, 0x77, 0x00), + makecol(0x33, 0x77, 0x55), + makecol(0x33, 0x77, 0xaa), + makecol(0x33, 0x77, 0xff), + makecol(0x33, 0x99, 0x00), + makecol(0x33, 0x99, 0x55), + makecol(0x33, 0x99, 0xaa), + makecol(0x33, 0x99, 0xff), + makecol(0x33, 0xbb, 0x00), + makecol(0x33, 0xbb, 0x55), + makecol(0x33, 0xbb, 0xaa), + makecol(0x33, 0xbb, 0xff), + makecol(0x33, 0xdd, 0x00), + makecol(0x33, 0xdd, 0x55), + makecol(0x33, 0xdd, 0xaa), + makecol(0x33, 0xdd, 0xff), + makecol(0x33, 0xff, 0x00), + makecol(0x33, 0xff, 0x55), + makecol(0x33, 0xff, 0xaa), + makecol(0x33, 0xff, 0xff), + makecol(0x55, 0x00, 0x00), + makecol(0x55, 0x00, 0x55), + makecol(0x55, 0x00, 0xaa), + makecol(0x55, 0x00, 0xff), + makecol(0x55, 0x33, 0x00), + makecol(0x55, 0x33, 0x55), + makecol(0x55, 0x33, 0xaa), + makecol(0x55, 0x33, 0xff), + makecol(0x55, 0x55, 0x00), + makecol(0x55, 0x55, 0x55), + makecol(0x55, 0x55, 0xaa), + makecol(0x55, 0x55, 0xff), + makecol(0x55, 0x77, 0x00), + makecol(0x55, 0x77, 0x55), + makecol(0x55, 0x77, 0xaa), + makecol(0x55, 0x77, 0xff), + makecol(0x55, 0x99, 0x00), + makecol(0x55, 0x99, 0x55), + makecol(0x55, 0x99, 0xaa), + makecol(0x55, 0x99, 0xff), + makecol(0x55, 0xbb, 0x00), + makecol(0x55, 0xbb, 0x55), + makecol(0x55, 0xbb, 0xaa), + makecol(0x55, 0xbb, 0xff), + makecol(0x55, 0xdd, 0x00), + makecol(0x55, 0xdd, 0x55), + makecol(0x55, 0xdd, 0xaa), + makecol(0x55, 0xdd, 0xff), + makecol(0x55, 0xff, 0x00), + makecol(0x55, 0xff, 0x55), + makecol(0x55, 0xff, 0xaa), + makecol(0x55, 0xff, 0xff), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x55), + makecol(0x77, 0x00, 0xaa), + makecol(0x77, 0x00, 0xff), + makecol(0x77, 0x33, 0x00), + makecol(0x77, 0x33, 0x55), + makecol(0x77, 0x33, 0xaa), + makecol(0x77, 0x33, 0xff), + makecol(0x77, 0x55, 0x00), + makecol(0x77, 0x55, 0x55), + makecol(0x77, 0x55, 0xaa), + makecol(0x77, 0x55, 0xff), + makecol(0x77, 0x77, 0x00), + makecol(0x77, 0x77, 0x55), + makecol(0x77, 0x77, 0xaa), + makecol(0x77, 0x77, 0xff), + makecol(0x77, 0x99, 0x00), + makecol(0x77, 0x99, 0x55), + makecol(0x77, 0x99, 0xaa), + makecol(0x77, 0x99, 0xff), + makecol(0x77, 0xbb, 0x00), + makecol(0x77, 0xbb, 0x55), + makecol(0x77, 0xbb, 0xaa), + makecol(0x77, 0xbb, 0xff), + makecol(0x77, 0xdd, 0x00), + makecol(0x77, 0xdd, 0x55), + makecol(0x77, 0xdd, 0xaa), + makecol(0x77, 0xdd, 0xff), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x55), + makecol(0x77, 0xff, 0xaa), + makecol(0x77, 0xff, 0xff), + makecol(0x99, 0x00, 0x00), + makecol(0x99, 0x00, 0x55), + makecol(0x99, 0x00, 0xaa), + makecol(0x99, 0x00, 0xff), + makecol(0x99, 0x33, 0x00), + makecol(0x99, 0x33, 0x55), + makecol(0x99, 0x33, 0xaa), + makecol(0x99, 0x33, 0xff), + makecol(0x99, 0x55, 0x00), + makecol(0x99, 0x55, 0x55), + makecol(0x99, 0x55, 0xaa), + makecol(0x99, 0x55, 0xff), + makecol(0x99, 0x77, 0x00), + makecol(0x99, 0x77, 0x55), + makecol(0x99, 0x77, 0xaa), + makecol(0x99, 0x77, 0xff), + makecol(0x99, 0x99, 0x00), + makecol(0x99, 0x99, 0x55), + makecol(0x99, 0x99, 0xaa), + makecol(0x99, 0x99, 0xff), + makecol(0x99, 0xbb, 0x00), + makecol(0x99, 0xbb, 0x55), + makecol(0x99, 0xbb, 0xaa), + makecol(0x99, 0xbb, 0xff), + makecol(0x99, 0xdd, 0x00), + makecol(0x99, 0xdd, 0x55), + makecol(0x99, 0xdd, 0xaa), + makecol(0x99, 0xdd, 0xff), + makecol(0x99, 0xff, 0x00), + makecol(0x99, 0xff, 0x55), + makecol(0x99, 0xff, 0xaa), + makecol(0x99, 0xff, 0xff), + makecol(0xbb, 0x00, 0x00), + makecol(0xbb, 0x00, 0x55), + makecol(0xbb, 0x00, 0xaa), + makecol(0xbb, 0x00, 0xff), + makecol(0xbb, 0x33, 0x00), + makecol(0xbb, 0x33, 0x55), + makecol(0xbb, 0x33, 0xaa), + makecol(0xbb, 0x33, 0xff), + makecol(0xbb, 0x55, 0x00), + makecol(0xbb, 0x55, 0x55), + makecol(0xbb, 0x55, 0xaa), + makecol(0xbb, 0x55, 0xff), + makecol(0xbb, 0x77, 0x00), + makecol(0xbb, 0x77, 0x55), + makecol(0xbb, 0x77, 0xaa), + makecol(0xbb, 0x77, 0xff), + makecol(0xbb, 0x99, 0x00), + makecol(0xbb, 0x99, 0x55), + makecol(0xbb, 0x99, 0xaa), + makecol(0xbb, 0x99, 0xff), + makecol(0xbb, 0xbb, 0x00), + makecol(0xbb, 0xbb, 0x55), + makecol(0xbb, 0xbb, 0xaa), + makecol(0xbb, 0xbb, 0xff), + makecol(0xbb, 0xdd, 0x00), + makecol(0xbb, 0xdd, 0x55), + makecol(0xbb, 0xdd, 0xaa), + makecol(0xbb, 0xdd, 0xff), + makecol(0xbb, 0xff, 0x00), + makecol(0xbb, 0xff, 0x55), + makecol(0xbb, 0xff, 0xaa), + makecol(0xbb, 0xff, 0xff), + makecol(0xdd, 0x00, 0x00), + makecol(0xdd, 0x00, 0x55), + makecol(0xdd, 0x00, 0xaa), + makecol(0xdd, 0x00, 0xff), + makecol(0xdd, 0x33, 0x00), + makecol(0xdd, 0x33, 0x55), + makecol(0xdd, 0x33, 0xaa), + makecol(0xdd, 0x33, 0xff), + makecol(0xdd, 0x55, 0x00), + makecol(0xdd, 0x55, 0x55), + makecol(0xdd, 0x55, 0xaa), + makecol(0xdd, 0x55, 0xff), + makecol(0xdd, 0x77, 0x00), + makecol(0xdd, 0x77, 0x55), + makecol(0xdd, 0x77, 0xaa), + makecol(0xdd, 0x77, 0xff), + makecol(0xdd, 0x99, 0x00), + makecol(0xdd, 0x99, 0x55), + makecol(0xdd, 0x99, 0xaa), + makecol(0xdd, 0x99, 0xff), + makecol(0xdd, 0xbb, 0x00), + makecol(0xdd, 0xbb, 0x55), + makecol(0xdd, 0xbb, 0xaa), + makecol(0xdd, 0xbb, 0xff), + makecol(0xdd, 0xdd, 0x00), + makecol(0xdd, 0xdd, 0x55), + makecol(0xdd, 0xdd, 0xaa), + makecol(0xdd, 0xdd, 0xff), + makecol(0xdd, 0xff, 0x00), + makecol(0xdd, 0xff, 0x55), + makecol(0xdd, 0xff, 0xaa), + makecol(0xdd, 0xff, 0xff), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x55), + makecol(0xff, 0x00, 0xaa), + makecol(0xff, 0x00, 0xff), + makecol(0xff, 0x33, 0x00), + makecol(0xff, 0x33, 0x55), + makecol(0xff, 0x33, 0xaa), + makecol(0xff, 0x33, 0xff), + makecol(0xff, 0x55, 0x00), + makecol(0xff, 0x55, 0x55), + makecol(0xff, 0x55, 0xaa), + makecol(0xff, 0x55, 0xff), + makecol(0xff, 0x77, 0x00), + makecol(0xff, 0x77, 0x55), + makecol(0xff, 0x77, 0xaa), + makecol(0xff, 0x77, 0xff), + makecol(0xff, 0x99, 0x00), + makecol(0xff, 0x99, 0x55), + makecol(0xff, 0x99, 0xaa), + makecol(0xff, 0x99, 0xff), + makecol(0xff, 0xbb, 0x00), + makecol(0xff, 0xbb, 0x55), + makecol(0xff, 0xbb, 0xaa), + makecol(0xff, 0xbb, 0xff), + makecol(0xff, 0xdd, 0x00), + makecol(0xff, 0xdd, 0x55), + makecol(0xff, 0xdd, 0xaa), + makecol(0xff, 0xdd, 0xff), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x55), + makecol(0xff, 0xff, 0xaa), + makecol(0xff, 0xff, 0xff), + }, +/* Palette 5: 6x6x6 colour cube */ + { + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x33), + makecol(0x00,0x00,0x66), + makecol(0x00,0x00,0x99), + makecol(0x00,0x00,0xcc), + makecol(0x00,0x00,0xff), + makecol(0x33,0x00,0x00), + makecol(0x33,0x00,0x33), + makecol(0x33,0x00,0x66), + makecol(0x33,0x00,0x99), + makecol(0x33,0x00,0xcc), + makecol(0x33,0x00,0xff), + makecol(0x66,0x00,0x00), + makecol(0x66,0x00,0x33), + makecol(0x66,0x00,0x66), + makecol(0x66,0x00,0x99), + makecol(0x66,0x00,0xcc), + makecol(0x66,0x00,0xff), + makecol(0x99,0x00,0x00), + makecol(0x99,0x00,0x33), + makecol(0x99,0x00,0x66), + makecol(0x99,0x00,0x99), + makecol(0x99,0x00,0xcc), + makecol(0x99,0x00,0xff), + makecol(0xcc,0x00,0x00), + makecol(0xcc,0x00,0x33), + makecol(0xcc,0x00,0x66), + makecol(0xcc,0x00,0x99), + makecol(0xcc,0x00,0xcc), + makecol(0xcc,0x00,0xff), + makecol(0xff,0x00,0x00), + makecol(0xff,0x00,0x33), + makecol(0xff,0x00,0x66), + makecol(0xff,0x00,0x99), + makecol(0xff,0x00,0xcc), + makecol(0xff,0x00,0xff), + makecol(0x00,0x33,0x00), + makecol(0x00,0x33,0x33), + makecol(0x00,0x33,0x66), + makecol(0x00,0x33,0x99), + makecol(0x00,0x33,0xcc), + makecol(0x00,0x33,0xff), + makecol(0x33,0x33,0x00), + makecol(0x33,0x33,0x33), + makecol(0x33,0x33,0x66), + makecol(0x33,0x33,0x99), + makecol(0x33,0x33,0xcc), + makecol(0x33,0x33,0xff), + makecol(0x66,0x33,0x00), + makecol(0x66,0x33,0x33), + makecol(0x66,0x33,0x66), + makecol(0x66,0x33,0x99), + makecol(0x66,0x33,0xcc), + makecol(0x66,0x33,0xff), + makecol(0x99,0x33,0x00), + makecol(0x99,0x33,0x33), + makecol(0x99,0x33,0x66), + makecol(0x99,0x33,0x99), + makecol(0x99,0x33,0xcc), + makecol(0x99,0x33,0xff), + makecol(0xcc,0x33,0x00), + makecol(0xcc,0x33,0x33), + makecol(0xcc,0x33,0x66), + makecol(0xcc,0x33,0x99), + makecol(0xcc,0x33,0xcc), + makecol(0xcc,0x33,0xff), + makecol(0xff,0x33,0x00), + makecol(0xff,0x33,0x33), + makecol(0xff,0x33,0x66), + makecol(0xff,0x33,0x99), + makecol(0xff,0x33,0xcc), + makecol(0xff,0x33,0xff), + makecol(0x00,0x66,0x00), + makecol(0x00,0x66,0x33), + makecol(0x00,0x66,0x66), + makecol(0x00,0x66,0x99), + makecol(0x00,0x66,0xcc), + makecol(0x00,0x66,0xff), + makecol(0x33,0x66,0x00), + makecol(0x33,0x66,0x33), + makecol(0x33,0x66,0x66), + makecol(0x33,0x66,0x99), + makecol(0x33,0x66,0xcc), + makecol(0x33,0x66,0xff), + makecol(0x66,0x66,0x00), + makecol(0x66,0x66,0x33), + makecol(0x66,0x66,0x66), + makecol(0x66,0x66,0x99), + makecol(0x66,0x66,0xcc), + makecol(0x66,0x66,0xff), + makecol(0x99,0x66,0x00), + makecol(0x99,0x66,0x33), + makecol(0x99,0x66,0x66), + makecol(0x99,0x66,0x99), + makecol(0x99,0x66,0xcc), + makecol(0x99,0x66,0xff), + makecol(0xcc,0x66,0x00), + makecol(0xcc,0x66,0x33), + makecol(0xcc,0x66,0x66), + makecol(0xcc,0x66,0x99), + makecol(0xcc,0x66,0xcc), + makecol(0xcc,0x66,0xff), + makecol(0xff,0x66,0x00), + makecol(0xff,0x66,0x33), + makecol(0xff,0x66,0x66), + makecol(0xff,0x66,0x99), + makecol(0xff,0x66,0xcc), + makecol(0xff,0x66,0xff), + makecol(0x00,0x99,0x00), + makecol(0x00,0x99,0x33), + makecol(0x00,0x99,0x66), + makecol(0x00,0x99,0x99), + makecol(0x00,0x99,0xcc), + makecol(0x00,0x99,0xff), + makecol(0x33,0x99,0x00), + makecol(0x33,0x99,0x33), + makecol(0x33,0x99,0x66), + makecol(0x33,0x99,0x99), + makecol(0x33,0x99,0xcc), + makecol(0x33,0x99,0xff), + makecol(0x66,0x99,0x00), + makecol(0x66,0x99,0x33), + makecol(0x66,0x99,0x66), + makecol(0x66,0x99,0x99), + makecol(0x66,0x99,0xcc), + makecol(0x66,0x99,0xff), + makecol(0x99,0x99,0x00), + makecol(0x99,0x99,0x33), + makecol(0x99,0x99,0x66), + makecol(0x99,0x99,0x99), + makecol(0x99,0x99,0xcc), + makecol(0x99,0x99,0xff), + makecol(0xcc,0x99,0x00), + makecol(0xcc,0x99,0x33), + makecol(0xcc,0x99,0x66), + makecol(0xcc,0x99,0x99), + makecol(0xcc,0x99,0xcc), + makecol(0xcc,0x99,0xff), + makecol(0xff,0x99,0x00), + makecol(0xff,0x99,0x33), + makecol(0xff,0x99,0x66), + makecol(0xff,0x99,0x99), + makecol(0xff,0x99,0xcc), + makecol(0xff,0x99,0xff), + makecol(0x00,0xcc,0x00), + makecol(0x00,0xcc,0x33), + makecol(0x00,0xcc,0x66), + makecol(0x00,0xcc,0x99), + makecol(0x00,0xcc,0xcc), + makecol(0x00,0xcc,0xff), + makecol(0x33,0xcc,0x00), + makecol(0x33,0xcc,0x33), + makecol(0x33,0xcc,0x66), + makecol(0x33,0xcc,0x99), + makecol(0x33,0xcc,0xcc), + makecol(0x33,0xcc,0xff), + makecol(0x66,0xcc,0x00), + makecol(0x66,0xcc,0x33), + makecol(0x66,0xcc,0x66), + makecol(0x66,0xcc,0x99), + makecol(0x66,0xcc,0xcc), + makecol(0x66,0xcc,0xff), + makecol(0x99,0xcc,0x00), + makecol(0x99,0xcc,0x33), + makecol(0x99,0xcc,0x66), + makecol(0x99,0xcc,0x99), + makecol(0x99,0xcc,0xcc), + makecol(0x99,0xcc,0xff), + makecol(0xcc,0xcc,0x00), + makecol(0xcc,0xcc,0x33), + makecol(0xcc,0xcc,0x66), + makecol(0xcc,0xcc,0x99), + makecol(0xcc,0xcc,0xcc), + makecol(0xcc,0xcc,0xff), + makecol(0xff,0xcc,0x00), + makecol(0xff,0xcc,0x33), + makecol(0xff,0xcc,0x66), + makecol(0xff,0xcc,0x99), + makecol(0xff,0xcc,0xcc), + makecol(0xff,0xcc,0xff), + makecol(0x00,0xff,0x00), + makecol(0x00,0xff,0x33), + makecol(0x00,0xff,0x66), + makecol(0x00,0xff,0x99), + makecol(0x00,0xff,0xcc), + makecol(0x00,0xff,0xff), + makecol(0x33,0xff,0x00), + makecol(0x33,0xff,0x33), + makecol(0x33,0xff,0x66), + makecol(0x33,0xff,0x99), + makecol(0x33,0xff,0xcc), + makecol(0x33,0xff,0xff), + makecol(0x66,0xff,0x00), + makecol(0x66,0xff,0x33), + makecol(0x66,0xff,0x66), + makecol(0x66,0xff,0x99), + makecol(0x66,0xff,0xcc), + makecol(0x66,0xff,0xff), + makecol(0x99,0xff,0x00), + makecol(0x99,0xff,0x33), + makecol(0x99,0xff,0x66), + makecol(0x99,0xff,0x99), + makecol(0x99,0xff,0xcc), + makecol(0x99,0xff,0xff), + makecol(0xcc,0xff,0x00), + makecol(0xcc,0xff,0x33), + makecol(0xcc,0xff,0x66), + makecol(0xcc,0xff,0x99), + makecol(0xcc,0xff,0xcc), + makecol(0xcc,0xff,0xff), + makecol(0xff,0xff,0x00), + makecol(0xff,0xff,0x33), + makecol(0xff,0xff,0x66), + makecol(0xff,0xff,0x99), + makecol(0xff,0xff,0xcc), + makecol(0xff,0xff,0xff), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + makecol(0x00,0x00,0x00), + }, diff --git a/src/vid_im1024.c b/src/vid_im1024.c new file mode 100644 index 0000000..386059a --- /dev/null +++ b/src/vid_im1024.c @@ -0,0 +1,906 @@ +#include +#include +#include +#include "ibm.h" +#include "device.h" +#include "io.h" +#include "mem.h" +#include "thread.h" +#include "video.h" +#include "vid_pgc.h" +#include "vid_im1024.h" + +/* This implements just enough of the Vermont Microsystems IM-1024 to + * support the Windows 1.03 driver. Functions are partially implemented + * or hardwired to the behaviour expected by the Windows driver. + * + * One major difference seems to be that in hex mode, coordinates are + * passed as 2-byte integer words rather than 4-byte fixed-point fractions. + * I don't know what triggers this, so for now it's always on. + */ + +extern uint8_t fontdat12x18[256][36]; + +typedef struct im1024_t +{ + pgc_core_t pgc; + unsigned char fontx[256]; + unsigned char fonty[256]; + unsigned char font[256][128]; + + unsigned char *fifo; + unsigned fifo_len, fifo_wrptr, fifo_rdptr; +} im1024_t; + +/* As well as the usual PGC ring buffer at 0xC6000, the IM1024 appears to + * have an alternate method of passing commands. This is enabled by setting + * 0xC6330 to 1, and then: + * + * CX = count to write + * SI -> bytes to write + * + * Set pending bytes to 0 + * Read [C6331]. This gives number of bytes that can be written: + * 0xFF => 0, 0xFE => 1, 0xFD => 2 etc. + * Write that number of bytes to C6000. + * If there are more to come, go back to reading [C6331]. + * As far as I can see, at least one byte is always written; there's no + * provision to pause if the queue is full. + * + * I am implementing this by holding a FIFO of unlimited depth in the + * IM1024 to receive the data. + * + */ + +static void fifo_write(im1024_t *im1024, unsigned char val) +{ +/* PGCLOG(("fifo_write: %02x [rd=%04x wr=%04x]\n", val, + im1024->fifo_rdptr, im1024->fifo_wrptr)); */ + if (((im1024->fifo_wrptr + 1) % im1024->fifo_len) == im1024->fifo_rdptr) + { +/* FIFO is full. Double its size. */ + unsigned char *buf; + + PGCLOG(("fifo_resize: %d to %d\n", + im1024->fifo_len, 2 * im1024->fifo_len)); + + buf = realloc(im1024->fifo, 2 * im1024->fifo_len); + if (!buf) return; +/* Move the [0..wrptr] range to the newly-allocated area [len..len+wrptr] */ + memmove(buf + im1024->fifo_len, buf, im1024->fifo_wrptr); + im1024->fifo = buf; + im1024->fifo_wrptr += im1024->fifo_len; + im1024->fifo_len *= 2; + } +/* Append to the queue */ + im1024->fifo[im1024->fifo_wrptr] = val; + ++im1024->fifo_wrptr; + +/* Wrap if end of buffer reached */ + if (im1024->fifo_wrptr >= im1024->fifo_len) + { + im1024->fifo_wrptr = 0; + } +} + + +static int fifo_read(im1024_t *im1024) +{ + uint8_t result; + + if (im1024->fifo_wrptr == im1024->fifo_rdptr) + { + return -1; /* FIFO empty */ + } + result = im1024->fifo[im1024->fifo_rdptr]; + ++im1024->fifo_rdptr; + if (im1024->fifo_rdptr >= im1024->fifo_len) + { + im1024->fifo_rdptr = 0; + } +/* + PGCLOG(("fifo_read: %02x\n", result)); +*/ + return result; + +} + +/* Where a normal PGC would just read from the ring buffer at 0xC6300, the + * IM-1024 can read either from this or from its internal FIFO. The internal + * FIFO has priority. */ +int im1024_input_byte(pgc_core_t *pgc, uint8_t *result) +{ + im1024_t *im1024 = (im1024_t *)pgc; + + /* If input buffer empty, wait for it to fill */ + while ((im1024->fifo_wrptr == im1024->fifo_rdptr) && + (pgc->mapram[0x300] == pgc->mapram[0x301])) + { + pgc->waiting_input_fifo = 1; + pgc_sleep(pgc); + } + if (pgc->mapram[0x3FF]) /* Reset triggered */ + { + pgc_reset(pgc); + return 0; + } + if (im1024->fifo_wrptr == im1024->fifo_rdptr) + { + *result = pgc->mapram[pgc->mapram[0x301]]; + ++pgc->mapram[0x301]; + } + else + { + *result = fifo_read(im1024); + } + return 1; +} + +/* Macros to disable clipping and save clip state */ +#define PUSHCLIP { \ + uint16_t vp_x1, vp_x2, vp_y1, vp_y2; \ + vp_x1 = pgc->vp_x1; \ + vp_y1 = pgc->vp_y1; \ + vp_x2 = pgc->vp_x2; \ + vp_y2 = pgc->vp_y2; \ + pgc->vp_x1 = 0; \ + pgc->vp_y1 = 0; \ + pgc->vp_x2 = pgc->maxw - 1; \ + pgc->vp_y2 = pgc->maxh - 1; \ + +/* And to restore clip state */ +#define POPCLIP \ + pgc->vp_x1 = vp_x1; \ + pgc->vp_y1 = vp_y1; \ + pgc->vp_x2 = vp_x2; \ + pgc->vp_y2 = vp_y2; \ + } + + +/* Override memory read to return FIFO space */ +uint8_t im1024_read(uint32_t addr, void *p) +{ + im1024_t *im1024 = (im1024_t *)p; + + if (addr == 0xC6331 && im1024->pgc.mapram[0x330] == 1) + { + return 0x80; /* Hardcode that there are 128 bytes + * free */ + } + return pgc_read(addr, &im1024->pgc); +} + +/* Override memory write to handle writes to the FIFO */ +void im1024_write(uint32_t addr, uint8_t val, void *p) +{ + im1024_t *im1024 = (im1024_t *)p; + + /* If we are in 'fast' input mode, send all writes to the internal + * FIFO */ + if (addr >= 0xC6000 && addr < 0xC6100 && im1024->pgc.mapram[0x330] == 1) + { + fifo_write(im1024, val); +/* + PGCLOG(("im1024_write(%02x)\n", val)); +*/ + if (im1024->pgc.waiting_input_fifo) + { + im1024->pgc.waiting_input_fifo = 0; + pgc_wake(&im1024->pgc); + } + return; + } + pgc_write(addr, val, &im1024->pgc); +} + + + +/* I don't know what the IMGSIZ command does, only that the Windows driver + * issues it. So just parse and ignore it */ +void hndl_imgsiz(pgc_core_t *pgc) +{ + int16_t w,h; + uint8_t a,b; +// im1024_t *im1024 = (im1024_t *)pgc; + + if (!pgc_param_word(pgc, &w)) return; + if (!pgc_param_word(pgc, &h)) return; + if (!pgc_param_byte(pgc, &a)) return; + if (!pgc_param_byte(pgc, &b)) return; + + PGCLOG(("IMGSIZ %d,%d,%d,%d\n", w,h,a,b)); +} + + +/* I don't know what the IPREC command does, only that the Windows driver + * issues it. So just parse and ignore it */ +void hndl_iprec(pgc_core_t *pgc) +{ + uint8_t param; +// im1024_t *im1024 = (im1024_t *)pgc; + + if (!pgc_param_byte(pgc, ¶m)) return; + + PGCLOG(("IPREC %d\n", param)); +} + +/* I think PAN controls which part of the 1024x1024 framebuffer is displayed + * in the 1024x800 visible screen. */ +void hndl_pan(pgc_core_t *pgc) +{ + int16_t x,y; + + if (!pgc_param_word(pgc, &x)) return; + if (!pgc_param_word(pgc, &y)) return; + + PGCLOG(("PAN %d,%d\n", x, y)); + + pgc->pan_x = x; + pgc->pan_y = y; +} + +/* PLINE draws a non-filled polyline at a fixed position */ +void hndl_pline(pgc_core_t *pgc) +{ + int16_t x[257]; + int16_t y[257]; + uint8_t count; + unsigned n; + uint16_t linemask = pgc->line_pattern; + + if (!pgc_param_byte(pgc, &count)) return; + + PGCLOG(("PLINE (%d) ", count)); + for (n = 0; n < count; n++) + { + if (!pgc_param_word(pgc, &x[n])) return; + if (!pgc_param_word(pgc, &y[n])) return; + PGCLOG((" (%d,%d)\n", x[n], y[n])); + } + for (n = 1; n < count; n++) + { + linemask = pgc_draw_line(pgc, x[n - 1] << 16, y[n - 1] << 16, + x[n] << 16, y[n] << 16, linemask); + } +} + + +/* Blit a single row of pixels from one location to another. To avoid + * difficulties if the two overlap, read both rows into memory, process them + * there, and write the result back. */ +void blkmov_row(pgc_core_t *pgc, int16_t x0, int16_t x1, int16_t x2, + int16_t sy, int16_t ty) +{ + int16_t x; + uint8_t src[1024]; + uint8_t dst[1024]; + + for (x = x0; x <= x1; x++) + { + src[x - x0] = pgc_read_pixel(pgc, x, sy); + dst[x - x0] = pgc_read_pixel(pgc, x - x0 + x2, ty); + } + for (x = x0; x <= x1; x++) + { + switch (pgc->draw_mode) + { + default: + case 0: pgc_write_pixel(pgc, (x - x0 + x2), ty, src[x - x0]); break; + case 1: pgc_write_pixel(pgc, (x - x0 + x2), ty, dst[x - x0] ^ 0xFF); break; + case 2: pgc_write_pixel(pgc, (x - x0 + x2), ty, src[x - x0] ^ dst[x - x0]); break; + case 3: pgc_write_pixel(pgc, (x - x0 + x2), ty, src[x - x0] & dst[x - x0]); break; + } + } + +} + + +/* BLKMOV blits a rectangular area from one location to another, with no + * clipping. */ +void hndl_blkmov(pgc_core_t *pgc) +{ + int16_t x0,y0; + int16_t x1,y1; + int16_t x2,y2; + int16_t y; +// im1024_t *im1024 = (im1024_t *)pgc; + + if (!pgc_param_word(pgc, &x0)) return; + if (!pgc_param_word(pgc, &y0)) return; + if (!pgc_param_word(pgc, &x1)) return; + if (!pgc_param_word(pgc, &y1)) return; + if (!pgc_param_word(pgc, &x2)) return; + if (!pgc_param_word(pgc, &y2)) return; + + PGCLOG(("BLKMOV %d,%d,%d,%d,%d,%d\n", x0,y0,x1,y1,x2,y2)); + + /* Disable clipping */ + PUSHCLIP + + /* Either go down from the top, or up from the bottom, depending + * whether areas might overlap */ + if (y2 <= y0) + { + for (y = y0; y <= y1; y++) + { + blkmov_row(pgc, x0, x1, x2, y, y - y0 + y2); + } + } + else + { + for (y = y1; y >= y0; y--) + { + blkmov_row(pgc, x0, x1, x2, y, y - y0 + y2); + } + } + /* Restore clipping */ + POPCLIP +} + + +/* This overrides the PGC ELIPSE command to parse its parameters as words + * rather than coordinates */ +static void hndl_ellipse(pgc_core_t *pgc) +{ + int16_t x, y; + + if (!pgc_param_word(pgc, &x)) return; + if (!pgc_param_word(pgc, &y)) return; + + PGCLOG(("ELLIPSE %d,%d @ %d,%d\n", x,y, + pgc->x >> 16, pgc->y >> 16)); + pgc_draw_ellipse(pgc, x << 16, y << 16); +} + + + +/* This overrides the PGC MOVE command to parse its parameters as words + * rather than coordinates */ +static void hndl_move(pgc_core_t *pgc) +{ + int16_t x, y; + + if (!pgc_param_word(pgc, &x)) return; + if (!pgc_param_word(pgc, &y)) return; + + pgc->x = x << 16; + pgc->y = y << 16; + PGCLOG(("MOVE %d,%d\n", x,y)); +} + +/* This overrides the PGC DRAW command to parse its parameters as words + * rather than coordinates */ +static void hndl_draw(pgc_core_t *pgc) +{ + int16_t x, y; + + if (!pgc_param_word(pgc, &x)) return; + if (!pgc_param_word(pgc, &y)) return; + + PGCLOG(("DRAW %d,%d to %d,%d\n", + pgc->x >> 16, pgc->y >> 16, x, y)); + pgc_draw_line(pgc, pgc->x, pgc->y, x << 16, y << 16, pgc->line_pattern); + pgc->x = x << 16; + pgc->y = y << 16; +} + + + + +/* This overrides the PGC POLY command to parse its parameters as words + * rather than coordinates */ +static void hndl_poly(pgc_core_t *pgc) +{ + int32_t *x, *y; + int32_t n; + int16_t xw, yw, mask; + unsigned realcount = 0; + int parsing = 1; + int as = 256; + + x = malloc(as * sizeof(int32_t)); + y = malloc(as * sizeof(int32_t)); + + if (!x || !y) + { + PGCLOG(("hndl_poly: malloc failed\n")); + return; + } + while (parsing) + { + uint8_t count; + if (!pgc_param_byte(pgc, &count)) return; + + if (count + realcount >= as) + { + int32_t *nx, *ny; + + nx = realloc(x, 2 * as * sizeof(int32_t)); + ny = realloc(y, 2 * as * sizeof(int32_t)); + if (!x || !y) + { + PGCLOG(("hndl_poly: realloc failed\n")); + break; + } + x = nx; + y = ny; + as *= 2; + } + + for (n = 0; n < count; n++) + { + if (!pgc_param_word(pgc, &xw)) return; + if (!pgc_param_word(pgc, &yw)) return; + + /* Skip degenerate line segments */ + if (realcount > 0 && + (xw << 16) == x[realcount - 1] && + (yw << 16) == y[realcount - 1]) + { + continue; + } + x[realcount] = xw << 16; + y[realcount] = yw << 16; + ++realcount; + } +/* If we're in a command list, peek ahead to see if the next command is + * also POLY. If so, that's a continuation of this polygon! */ + parsing = 0; + if (pgc->clcur && (pgc->clcur->rdptr+1) < pgc->clcur->wrptr && + pgc->clcur->list[pgc->clcur->rdptr] == 0x30) + { + PGCLOG(("hndl_poly: POLY continues!\n")); + parsing = 1; + /* Swallow the POLY */ + ++pgc->clcur->rdptr; + } + + } + + PGCLOG(("POLY (%d) fill_mode=%d\n", realcount, pgc->fill_mode)); + for (n = 0; n < realcount; n++) + { + PGCLOG((" (%d,%d)\n", x[n] >> 16, y[n] >> 16)); + } + if (pgc->fill_mode) + { + pgc_fill_polygon(pgc, realcount, x, y); + } + /* Now draw borders */ + mask = pgc->line_pattern; + for (n = 1; n < realcount; n++) + { + mask = pgc_draw_line(pgc, x[n - 1], y[n - 1], x[n], y[n], mask); + } + pgc_draw_line(pgc, x[realcount - 1], y[realcount - 1], x[0], y[0], mask); + free(y); + free(x); +} + + +static int parse_poly(pgc_core_t *pgc, pgc_commandlist_t *cl, int c) +{ + uint8_t count; + + PGCLOG(("parse_poly\n")); + if (!pgc_param_byte(pgc, &count)) return 0; + + PGCLOG(("parse_poly: count=%02x\n", count)); + if (!pgc_commandlist_append(cl, count)) + { + pgc_error(pgc, PGC_ERROR_OVERFLOW); + return 0; + } + PGCLOG(("parse_poly: parse %d words\n", 2 * count)); + return pgc_parse_words(pgc, cl, count * 2); +} + + + +/* This overrides the PGC RECT command to parse its parameters as words + * rather than coordinates */ +static void hndl_rect(pgc_core_t *pgc) +{ + int16_t x0, y0, x1, y1, p, q; + + x0 = pgc->x >> 16; + y0 = pgc->y >> 16; + + if (!pgc_param_word(pgc, &x1)) return; + if (!pgc_param_word(pgc, &y1)) return; + + /* Convert to raster coords */ + pgc_sto_raster(pgc, &x0, &y0); + pgc_sto_raster(pgc, &x1, &y1); + + if (x0 > x1) { p = x0; x0 = x1; x1 = p; } + if (y0 > y1) { q = y0; y0 = y1; y1 = q; } + + PGCLOG(("RECT (%d,%d) -> (%d,%d)\n", x0, y0, x1, y1)); + + if (pgc->fill_mode) + { + for (p = y0; p <= y1; p++) + { + pgc_fill_line_r(pgc, x0, x1, p); + } + } + else /* Outline: 4 lines */ + { + p = pgc->line_pattern; + p = pgc_draw_line_r(pgc, x0, y0, x1, y0, p); + p = pgc_draw_line_r(pgc, x1, y0, x1, y1, p); + p = pgc_draw_line_r(pgc, x1, y1, x0, y1, p); + p = pgc_draw_line_r(pgc, x0, y1, x0, y0, p); + } +} + + +/* TODO: Text drawing should probably be implemented in vid_pgc.c rather + * than vid_im1024.c */ +static void hndl_tdefin(pgc_core_t *pgc) +{ + unsigned char ch, bt; + unsigned char rows, cols; + unsigned len, n; +// unsigned x = 0; + im1024_t * im1024 = (im1024_t *)pgc; + + if (!pgc_param_byte(pgc, &ch)) return; + if (!pgc_param_byte(pgc, &cols)) return; + if (!pgc_param_byte(pgc, &rows)) return; + + PGCLOG(("TDEFIN (%d,%d,%d) 0x%02x 0x%02x\n", ch, rows, cols, + pgc->mapram[0x300], pgc->mapram[0x301])); + + len = ((cols + 7) / 8) * rows; + for (n = 0; n < len; n++) + { +// char buf[10]; +// unsigned char mask; + + if (!pgc_param_byte(pgc, &bt)) return; + +// buf[0] = 0; +// for (mask = 0x80; mask != 0; mask >>= 1) +// { +// if (bt & mask) strcat(buf, "#"); +// else strcat(buf, "-"); +// ++x; +// if (x == cols) { strcat(buf, "\n"); x = 0; } +// } +// PGCLOG((buf)); + + if (n < sizeof(im1024->font[ch])) im1024->font[ch][n] = bt; + } + im1024->fontx[ch] = cols; + im1024->fonty[ch] = rows; +} + +static void hndl_tsize(pgc_core_t *pgc) +{ + int16_t size; + + if (!pgc_param_word(pgc, &size)) return; + PGCLOG(("TSIZE(%d)\n", size)); + + pgc->tsize = size << 16; +} + + + +static void hndl_twrite(pgc_core_t *pgc) +{ + unsigned char count; + unsigned char mask; + unsigned char *row; + int x, y, wb, n; + im1024_t * im1024 = (im1024_t *)pgc; + int16_t x0 = pgc->x >> 16; + int16_t y0 = pgc->y >> 16; + unsigned char buf[256]; +/* unsigned char rbuf[256];*/ + + if (!pgc_param_byte(pgc, &count)) return; + + for (n = 0; n < count; n++) + { + if (!pgc_param_byte(pgc, &buf[n])) return; + } + buf[count] = 0; +/* for (n = 0; n <= count; n++) + { + if (isprint(buf[n]) || 0 == buf[n]) + { + rbuf[n] = buf[n]; + } + else rbuf[n] = '?'; + }*/ + pgc_sto_raster(pgc, &x0, &y0); + PGCLOG(("TWRITE (%d,%-*.*s) x0=%d y0=%d\n", count, count, count, rbuf, x0, y0)); + + for (n = 0; n < count; n++) + { + wb = (im1024->fontx[buf[n]] + 7) / 8; + PGCLOG(("ch=0x%02x w=%d h=%d wb=%d\n", + buf[n], im1024->fontx[buf[n]], + im1024->fonty[buf[n]], wb)); + for (y = 0; y < im1024->fonty[buf[n]]; y++) + { + mask = 0x80; + row = &im1024->font[buf[n]][y * wb]; + for (x = 0; x < im1024->fontx[buf[n]]; x++) + { +/* rbuf[x] = (row[0] & mask) ? '#' : '-';*/ + if (row[0] & mask) pgc_plot(pgc, x + x0, y0 - y); + mask = mask >> 1; + if (mask == 0) { mask = 0x80; ++row; } + } +/* rbuf[x++] = '\n'; + rbuf[x++] = 0;*/ +// PGCLOG((rbuf); + } + x0 += im1024->fontx[buf[n]]; + } +} + + + +static void hndl_txt88(pgc_core_t *pgc) +{ + unsigned char count; + unsigned char mask; + unsigned char *row; + int x, y, /*wb, */n; + int16_t x0 = pgc->x >> 16; + int16_t y0 = pgc->y >> 16; + unsigned char buf[256]; +/* unsigned char rbuf[256];*/ + + if (!pgc_param_byte(pgc, &count)) return; + + for (n = 0; n < count; n++) + { + if (!pgc_param_byte(pgc, &buf[n])) return; + } + buf[count] = 0; +/* for (n = 0; n <= count; n++) + { + if (isprint(buf[n]) || 0 == buf[n]) + { + rbuf[n] = buf[n]; + } + else rbuf[n] = '?'; + }*/ + pgc_sto_raster(pgc, &x0, &y0); + PGCLOG(("TXT88 (%d,%-*.*s) x0=%d y0=%d\n", count, count, count, rbuf, x0, y0)); + + for (n = 0; n < count; n++) + { +/* wb = 2;*/ + PGCLOG(("ch=0x%02x w=%d h=%d wb=%d\n", buf[n], 12, 18, wb)); + for (y = 0; y < 18; y++) + { + mask = 0x80; + row = &fontdat12x18[buf[n]][y * 2]; + for (x = 0; x < 12; x++) + { +/* rbuf[x] = (row[0] & mask) ? '#' : '-';*/ + if (row[0] & mask) pgc_plot(pgc, x + x0, y0 - y); + mask = mask >> 1; + if (mask == 0) { mask = 0x80; ++row; } + } +/* rbuf[x++] = '\n'; + rbuf[x++] = 0;*/ +// PGCLOG((rbuf); + } + x0 += 12; + } +} + + + + +static void hndl_imagew(pgc_core_t *pgc) +{ + int16_t row1, col1, col2; + uint8_t v1, v2; + int16_t vp_x1, vp_y1, vp_x2, vp_y2; + + if (!pgc_param_word(pgc, &row1)) return; + if (!pgc_param_word(pgc, &col1)) return; + if (!pgc_param_word(pgc, &col2)) return; + + /* IMAGEW already uses raster coordinates so there is no need to + * convert it */ + PGCLOG(("IMAGEW (row=%d,col1=%d,col2=%d)\n", row1, col1, col2)); + + vp_x1 = pgc->vp_x1; + vp_y1 = pgc->vp_y1; + vp_x2 = pgc->vp_x2; + vp_y2 = pgc->vp_y2; + /* Disable clipping */ + pgc->vp_x1 = 0; + pgc->vp_y1 = 0; + pgc->vp_x2 = pgc->maxw - 1; + pgc->vp_y2 = pgc->maxh - 1; + + /* In ASCII mode, what is written is a stream of bytes */ + if (pgc->ascii_mode) + { + while (col1 <= col2) + { + if (!pgc_param_byte(pgc, &v1)) return; + pgc_write_pixel(pgc, col1, row1, v1); + ++col1; + } + return; + } + else /* In hex mode, it's RLE compressed */ + { + while (col1 <= col2) + { + if (!pgc_param_byte(pgc, &v1)) return; + + if (v1 & 0x80) /* Literal run */ + { + v1 -= 0x7F; + while (col1 <= col2 && v1 != 0) + { + if (!pgc_param_byte(pgc, &v2)) return; + pgc_write_pixel(pgc, col1, row1, v2); + ++col1; + --v1; + } + } + else /* Repeated run */ + { + if (!pgc_param_byte(pgc, &v2)) return; + + ++v1; + while (col1 <= col2 && v1 != 0) + { + pgc_write_pixel(pgc, col1, row1, v2); + ++col1; + --v1; + } + } + } + } + /* Restore clipping */ + pgc->vp_x1 = vp_x1; + pgc->vp_y1 = vp_y1; + pgc->vp_x2 = vp_x2; + pgc->vp_y2 = vp_y2; +} + +/* I have called this command DOT - I don't know its proper name. Draws a + * single pixel at the current location */ +static void hndl_dot(pgc_core_t *pgc) +{ + int16_t x = pgc->x >> 16, y = pgc->y >> 16; + + pgc_sto_raster(pgc, &x, &y); + + PGCLOG(("Dot @ %d,%d ink=%d mode=%d\n", x, y, pgc->colour, pgc->draw_mode)); + pgc_plot(pgc, x, y); +} + +/* This command (which I have called IMAGEX, since I don't know its real + * name) is a screen-to-memory blit. It reads a rectangle of bytes, rather + * than the single row read by IMAGER, and does not attempt to compress + * the result */ +static void hndl_imagex(pgc_core_t *pgc) +{ + int16_t x0, x1, y0, y1; + int16_t p,q; + + if (!pgc_param_word(pgc, &x0)) return; + if (!pgc_param_word(pgc, &y0)) return; + if (!pgc_param_word(pgc, &x1)) return; + if (!pgc_param_word(pgc, &y1)) return; + + /* IMAGEX already uses raster coordinates so don't convert */ + PGCLOG(("IMAGEX (%d,%d,%d,%d)\n", x0,y0,x1,y1)); + + for (p = y0; p <= y1; p++) + { + for (q = x0; q <= x1; q++) + { + if (!pgc_result_byte(pgc, pgc_read_pixel(pgc, q, p))) + return; + } + } +} + +/* Commands implemented by the IM-1024. + * + * TODO: A lot of commands need commandlist parsers. + * TODO: The IM-1024 has a lot more commands that are not included here + * (BLINK, BUTRD, COPROC, RBAND etc) because the Windows 1.03 driver + * does not use them. + */ +static const pgc_command_t im1024_commands[] = +{ + { "BLKMOV", 0xDF, hndl_blkmov, pgc_parse_words, 6 }, + { "DRAW", 0x28, hndl_draw, pgc_parse_words, 2 }, + { "D", 0x28, hndl_draw, pgc_parse_words, 2 }, + { "DOT", 0x08, hndl_dot }, + { "ELIPSE", 0x39, hndl_ellipse, pgc_parse_words, 2 }, + { "EL", 0x39, hndl_ellipse, pgc_parse_words, 2 }, + { "IMAGEW", 0xD9, hndl_imagew }, + { "IW", 0xD9, hndl_imagew }, + { "IMAGEX", 0xDA, hndl_imagex }, + { "TXT88", 0x88, hndl_txt88 }, + { "TWRITE", 0x8B, hndl_twrite }, + { "TDEFIN", 0x84, hndl_tdefin }, + { "TD", 0x84, hndl_tdefin }, + { "TSIZE", 0x81, hndl_tsize }, + { "TS", 0x81, hndl_tsize }, + { "IPREC", 0xE4, hndl_iprec }, + { "IMGSIZ", 0x4E, hndl_imgsiz }, + { "LUT8", 0xE6, pgc_hndl_lut8 }, + { "L8", 0xE6, pgc_hndl_lut8 }, + { "LUT8RD", 0x53, pgc_hndl_lut8rd }, + { "L8RD", 0x53, pgc_hndl_lut8rd }, + { "PAN", 0xB7, hndl_pan }, + { "POLY", 0x30, hndl_poly, parse_poly }, + { "P", 0x30, hndl_poly, parse_poly }, + { "PLINE", 0x36, hndl_pline }, + { "PL", 0x37, hndl_pline }, + { "MOVE", 0x10, hndl_move, pgc_parse_words, 2 }, + { "M", 0x10, hndl_move, pgc_parse_words, 2 }, + { "RECT", 0x34, hndl_rect }, + { "R", 0x34, hndl_rect }, + { "******", 0x00, NULL } +}; + + +void *im1024_init() +{ + im1024_t *im1024 = malloc(sizeof(im1024_t)); + memset(im1024, 0, sizeof(im1024_t)); + + im1024->fifo = malloc(4096); + im1024->fifo_len = 4096; + im1024->fifo_wrptr = 0; + im1024->fifo_rdptr = 0; + + /* 1024x1024 framebuffer with 1024x800 visible + Pixel clock is a guess */ + pgc_core_init(&im1024->pgc, 1024, 1024, 1024, 800, im1024_input_byte, 65000000.0); + + mem_mapping_set_handler(&im1024->pgc.mapping, + im1024_read, NULL, NULL, + im1024_write, NULL, NULL); + + im1024->pgc.pgc_commands = im1024_commands; + return im1024; +} + +void im1024_close(void *p) +{ + im1024_t *im1024 = (im1024_t *)p; + + pgc_close(&im1024->pgc); +} + +void im1024_speed_changed(void *p) +{ + im1024_t *im1024 = (im1024_t *)p; + + pgc_speed_changed(&im1024->pgc); +} + +device_t im1024_device = +{ + "Image Manager 1024", + 0, + im1024_init, + im1024_close, + NULL, + im1024_speed_changed, + NULL, + NULL, + NULL +}; diff --git a/src/vid_im1024.h b/src/vid_im1024.h new file mode 100644 index 0000000..145401e --- /dev/null +++ b/src/vid_im1024.h @@ -0,0 +1 @@ +extern device_t im1024_device; diff --git a/src/vid_pgc.c b/src/vid_pgc.c new file mode 100644 index 0000000..311e2f2 --- /dev/null +++ b/src/vid_pgc.c @@ -0,0 +1,2477 @@ +/*PGC emulation*/ +#include +#include +#include +#include "ibm.h" +#include "device.h" +#include "io.h" +#include "mem.h" +#include "timer.h" +#include "thread.h" +#include "video.h" +#include "vid_pgc.h" +#include + +/* This implements just enough of the Professional Graphics Controller to + * act as a basis for the Vermont Microsystems IM-1024. + * + * PGC features implemented include: + * > The CGA-compatible display modes + * > Switching to and from native mode + * > Communicating with the host PC + * + * Numerous features are implemented partially or not at all, such as: + * > 2D drawing + * > 3D drawing + * > Command lists + * Some of these are marked TODO. + * + * The PGC has two display modes: CGA (in which it appears in the normal CGA + * memory and I/O ranges) and native (in which all functions are accessed + * through reads and writes to 1k of memory at 0xC6000). The PGC's 8088 + * processor monitors this buffer and executes instructions left there + * for it. We simulate this behaviour with a separate thread. + */ + +#define PGC_CGA_WIDTH 640 +#define PGC_CGA_HEIGHT 400 + +static int pgc_parse_command(pgc_core_t *pgc, const pgc_command_t **pcmd); +int pgc_clist_byte(pgc_core_t *pgc, uint8_t *val); + + + + +static const char *pgc_err_msgs[] = +{ + "Range \r", + "Integer \r", + "Memory \r", + "Overflow\r", + "Digit \r", + "Opcode \r", + "Running \r", + "Stack \r", + "Too long\r", + "Area \r", + "Missing \r" +}; + +#define HWORD(u) ((u) >> 16) +#define LWORD(u) ((u) & 0xFFFF) + + +/* Initial palettes */ +uint32_t init_palette[6][256] = +{ +#include "pgc_palettes.h" +}; + + + +/* When idle, the PGC drawing thread sleeps. pgc_wake() awakens it - but + * not immediately. Like the Voodoo, it has a short delay so that writes + * can be batched */ +#define WAKE_DELAY (TIMER_USEC * 500) +void pgc_wake(pgc_core_t *pgc) +{ + if (!timer_is_enabled(&pgc->wake_timer)) + { + timer_set_delay_u64(&pgc->wake_timer, WAKE_DELAY); + } +} + + +/* When the wake timer expires, that's when the drawing thread is actually + * woken */ +static void pgc_wake_timer(void *p) +{ + pgc_core_t *pgc = (pgc_core_t *)p; + + PGCLOG(("pgc: Waking up\n")); + thread_set_event(pgc->pgc_wake_thread); +} + +/* This is called by the drawing thread when it's waiting for the host + * to put more input in its input FIFO, or drain output from its output + * FIFO. */ +void pgc_sleep(pgc_core_t *pgc) +{ + + PGCLOG(("pgc: Sleeping on %d %d %d 0x%02x 0x%02x\n", + pgc->waiting_input_fifo, + pgc->waiting_output_fifo, + pgc->waiting_error_fifo, + pgc->mapram[0x300], pgc->mapram[0x301])); + /* Race condition: If host wrote to the PGC during the pclog() that + * won't be noticed */ + if (pgc->waiting_input_fifo && pgc->mapram[0x300] != pgc->mapram[0x301]) + { + pgc->waiting_input_fifo = 0; + return; + } + /* Same if they read */ + if (pgc->waiting_output_fifo && pgc->mapram[0x302] != (uint8_t)(pgc->mapram[0x303] - 1)) + { + pgc->waiting_output_fifo = 0; + return; + } + thread_wait_event(pgc->pgc_wake_thread, -1); + thread_reset_event(pgc->pgc_wake_thread); + +} + +/* Switch between CGA mode (DISPLAY 1) and native mode (DISPLAY 0) */ +void pgc_setdisplay(pgc_core_t *pgc, int cga) +{ + PGCLOG(("pgc_setdisplay(%d): cga_selected=%d cga_enabled=%d\n", + cga, pgc->cga_selected, pgc->cga_enabled)); + if (pgc->cga_selected != (pgc->cga_enabled && cga)) + { + pgc->cga_selected = (pgc->cga_enabled && cga); + + if (pgc->cga_selected) + { + mem_mapping_enable(&pgc->cga_mapping); + pgc->screenw = PGC_CGA_WIDTH; + pgc->screenh = PGC_CGA_HEIGHT; + } + else + { + mem_mapping_disable(&pgc->cga_mapping); + pgc->screenw = pgc->visw; + pgc->screenh = pgc->vish; + } + pgc_recalctimings(pgc); + } +} + + + + + +/* Convert coordinates based on the current window / viewport to raster + * coordinates. */ +void pgc_dto_raster(pgc_core_t *pgc, double *x, double *y) +{ +/* double x0 = *x, y0 = *y; */ + + *x += (pgc->vp_x1 - pgc->win_x1); + *y += (pgc->vp_y1 - pgc->win_y1); + +/* PGCLOG(("Coords to raster: (%f, %f) -> (%f, %f)\n", x0, y0, *x, *y)); */ +} + + +/* Overloads that take ints */ +void pgc_sto_raster(pgc_core_t *pgc, int16_t *x, int16_t *y) +{ + double xd = *x, yd = *y; + + pgc_dto_raster(pgc, &xd, &yd); + *x = (int16_t)xd; + *y = (int16_t)yd; +} + +void pgc_ito_raster(pgc_core_t *pgc, int32_t *x, int32_t *y) +{ + double xd = *x, yd = *y; + + pgc_dto_raster(pgc, &xd, &yd); + *x = (int32_t)xd; + *y = (int32_t)yd; +} + + +/* Add a byte to a command list. We allow command lists to be + * arbitrarily large */ +int pgc_commandlist_append(pgc_commandlist_t *list, uint8_t v) +{ + if (list->listmax == 0 || list->list == NULL) + { + list->list = malloc(4096); + if (!list->list) + { + PGCLOG(("Out of memory initialising command list\n")); + return 0; + } + list->listmax = 4096; + } + while (list->wrptr >= list->listmax) + { + uint8_t *buf = realloc(list->list, 2 * list->listmax); + if (!buf) + { + PGCLOG(("Out of memory growing command list\n")); + return 0; + } + list->list = buf; + list->listmax *= 2; + } + list->list[list->wrptr++] = v; + return 1; +} + +/* Beginning of a command list. Parse commands up to the next CLEND, + * storing them (in hex form) in the named command list. */ +static void hndl_clbeg(pgc_core_t *pgc) +{ + uint8_t param; + pgc_commandlist_t cl; + const pgc_command_t *cmd; + + memset(&cl, 0, sizeof(cl)); + if (!pgc_param_byte(pgc, ¶m)) return; + + PGCLOG(("CLBEG(%d)\n", param)); + while (1) + { + if (!pgc_parse_command(pgc, &cmd)) + { + /* PGC has been reset */ + return; + } + if (!cmd) + { + pgc_error(pgc, PGC_ERROR_OPCODE); + return; + } + else if (pgc->hex_command == 0x71) /* CLEND */ + { + pgc->clist[param] = cl; + return; + } + else + { + if (!pgc_commandlist_append(&cl, pgc->hex_command)) + { + pgc_error(pgc, PGC_ERROR_OVERFLOW); + return; + } + if (cmd->parser) + { + if (!(*cmd->parser)(pgc, &cl, cmd->p)) + { + return; + } + } + } + } +} + +static void hndl_clend(pgc_core_t *pgc) +{ + /* Shouldn't happen outside a CLBEG */ +} + +/* Execute a command list. If one was already executing, remember it so + * we can return to it afterwards. */ +static void hndl_clrun(pgc_core_t *pgc) +{ + uint8_t param; + pgc_commandlist_t *clprev = pgc->clcur; + + if (!pgc_param_byte(pgc, ¶m)) return; + + pgc->clcur = &pgc->clist[param]; + pgc->clcur->rdptr = 0; + pgc->clcur->repeat = 1; + pgc->clcur->chain = clprev; +} + +/* Execute a command list multiple times. */ +static void hndl_cloop(pgc_core_t *pgc) +{ + uint8_t param; + int16_t repeat; + pgc_commandlist_t *clprev = pgc->clcur; + + if (!pgc_param_byte(pgc, ¶m)) return; + if (!pgc_param_word(pgc, &repeat)) return; + + pgc->clcur = &pgc->clist[param]; + pgc->clcur->rdptr = 0; + pgc->clcur->repeat = repeat; + pgc->clcur->chain = clprev; +} + + +/* Read back a command list */ +static void hndl_clread(pgc_core_t *pgc) +{ + uint8_t param; + int n; + + if (!pgc_param_byte(pgc, ¶m)) return; + + for (n = 0; n < pgc->clist[param].wrptr; n++) + { + if (!pgc_result_byte(pgc, pgc->clist[param].list[n])) + return; + } +} + + +/* Delete a command list */ +static void hndl_cldel(pgc_core_t *pgc) +{ + uint8_t param; + + if (!pgc_param_byte(pgc, ¶m)) return; + memset(&pgc->clist[param], 0, sizeof(pgc_commandlist_t)); +} + + + +/* Clear the screen to a specified colour */ +static void hndl_clears(pgc_core_t *pgc) +{ + uint8_t param; + int y; + + if (!pgc_param_byte(pgc, ¶m)) return; + + for (y = 0; y < pgc->screenh; y++) + memset(pgc->vram + y * pgc->maxw, param, pgc->screenw); +} + +/* Select drawing colour */ +static void hndl_color(pgc_core_t *pgc) +{ + uint8_t param; + + if (!pgc_param_byte(pgc, ¶m)) return; + + pgc->colour = param; + PGCLOG(("COLOR(%d)\n", param)); +} + + +/* Set drawing mode. + * + * 0 => Draw + * 1 => Invert + * 2 => XOR (IM-1024) + * 3 => AND (IM-1024) + * + */ +static void hndl_linfun(pgc_core_t *pgc) +{ + uint8_t param; + + if (!pgc_param_byte(pgc, ¶m)) return; + + /* TODO: Not range-checked. Strictly speaking we should limit to 0-1 + * for the PGC and 0-3 for the IM-1024. */ + pgc->draw_mode = param; + PGCLOG(("LINFUN(%d)\n", param)); +} + +/* Set the line drawing pattern */ +static void hndl_linpat(pgc_core_t *pgc) +{ + uint16_t param; + + if (!pgc_param_word(pgc, (int16_t *)¶m)) return; + + pgc->line_pattern = param; + PGCLOG(("LINPAT(0x%04x)\n", param)); +} + +/* Set the polygon fill mode (0=hollow, 1=filled, 2=fast fill) */ +static void hndl_prmfil(pgc_core_t *pgc) +{ + uint8_t param; + + if (!pgc_param_byte(pgc, ¶m)) return; + + PGCLOG(("PRMFIL(%d)\n", param)); + if (param < 3) + { + pgc->fill_mode = param; + } + else + { + pgc_error(pgc, PGC_ERROR_RANGE); + } +} + + +/* Set the 2D drawing position */ +static void hndl_move(pgc_core_t *pgc) +{ + int32_t x = 0, y = 0; + + if (!pgc_param_coord(pgc, &x)) return; + if (!pgc_param_coord(pgc, &y)) return; + + pgc->x = x; + pgc->y = y; + PGCLOG(("MOVE %x.%04x,%x.%04x\n", HWORD(x), LWORD(x), HWORD(y), LWORD(y))); +} + +/* Set the 3D drawing position */ +static void hndl_move3(pgc_core_t *pgc) +{ + int32_t x = 0, y = 0, z = 0; + + if (!pgc_param_coord(pgc, &x)) return; + if (!pgc_param_coord(pgc, &y)) return; + if (!pgc_param_coord(pgc, &z)) return; + + pgc->x = x; + pgc->y = y; + pgc->z = z; +} + +/* Relative move (2D) */ +static void hndl_mover(pgc_core_t *pgc) +{ + int32_t x = 0, y = 0; + + if (!pgc_param_coord(pgc, &x)) return; + if (!pgc_param_coord(pgc, &y)) return; + + pgc->x += x; + pgc->y += y; +} + +/* Relative move (3D) */ +static void hndl_mover3(pgc_core_t *pgc) +{ + int32_t x = 0, y = 0, z = 0; + + if (!pgc_param_coord(pgc, &x)) return; + if (!pgc_param_coord(pgc, &y)) return; + if (!pgc_param_coord(pgc, &z)) return; + + pgc->x += x; + pgc->y += y; + pgc->z += z; +} + + + +/* Draw a line (using PGC fixed-point coordinates) */ +uint16_t pgc_draw_line(pgc_core_t *pgc, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint16_t linemask) +{ + + PGCLOG(("pgc_draw_line: (%d,%d) to (%d,%d)\n", x0 >> 16, y0 >> 16, + x1 >> 16, y1 >> 16)); + /* Convert from PGC fixed-point to device coordinates */ + x0 >>= 16; + x1 >>= 16; + y0 >>= 16; + y1 >>= 16; + + pgc_ito_raster(pgc, &x0, &y0); + pgc_ito_raster(pgc, &x1, &y1); + + return pgc_draw_line_r(pgc, x0, y0, x1, y1, linemask); +} + +/* Draw a line (using raster coordinates) + Bresenham's Algorithm from + * + * The line pattern mask to use is passed in. The return value is the line + * pattern mask rotated by the number of points drawn. + */ +uint16_t pgc_draw_line_r(pgc_core_t *pgc, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint16_t linemask) +{ + int32_t dx, dy, sx, sy, err, e2; + + dx = abs(x1 - x0); + dy = abs(y1 - y0); + sx = (x0 < x1) ? 1 : -1; + sy = (y0 < y1) ? 1 : -1; + err = (dx > dy ? dx : -dy) / 2; + + for(;;) + { + if (linemask & 0x8000) + { + pgc_plot(pgc, x0, y0); + linemask = (linemask << 1) | 1; + } + else + { + linemask = (linemask << 1); + } + if (x0 == x1 && y0 == y1) break; + e2 = err; + if (e2 > -dx) { err -= dy; x0 += sx; } + if (e2 < dy) { err += dx; y0 += sy; } + } + return linemask; +} + +/* Draw a horizontal line in the current fill pattern + * (using raster coordinates) */ +void pgc_fill_line_r(pgc_core_t *pgc, int32_t x0, int32_t x1, int32_t y0) +{ + int32_t x; + int32_t mask = 0x8000 >> (x0 & 0x0F); + + if (x0 > x1) { x = x1; x1 = x0; x0 = x; } + + for (x = x0; x <= x1; x++) + { + if (pgc->fill_pattern[y0 & 0x0F] & mask) + pgc_plot(pgc, x, y0); + mask = mask >> 1; + if (mask == 0) mask = 0x8000; + } +} + +/* For sorting polygon nodes */ +static int compare_double(const void *a, const void *b) +{ + const double *da = (const double *)a; + const double *db = (const double *)b; + + if (*da > *db) return 1; + if (*da < *db) return -1; + return 0; +} + +/* Draw a filled polygon (using PGC fixed-point coordinates) */ +void pgc_fill_polygon(pgc_core_t *pgc, + unsigned corners, int32_t *x, int32_t *y) +{ + double *nodex; + double *dx; + double *dy; + unsigned n, nodes, i, j; + double ymin, ymax, ypos; + + PGCLOG(("pgc_fill_polygon(%d corners)\n", corners)); + + if (corners < 2) return; /* Degenerate polygon */ + nodex = malloc(corners * sizeof(double)); + dx = malloc(corners * sizeof(double)); + dy = malloc(corners * sizeof(double)); + if (!nodex || !dx || !dy) return; + + ymin = ymax = y[0] / 65536.0; + for (n = 0; n < corners; n++) + { + /* Convert from PGC fixed-point to native floating-point */ + dx[n] = x[n] / 65536.0; + dy[n] = y[n] / 65536.0; + + if (dy[n] < ymin) ymin = dy[n]; + if (dy[n] > ymax) ymax = dy[n]; + } + /* Polygon fill. Based on */ + /* For each row, work out where the polygon lines intersect with + * that row. */ + for (ypos = ymin; ypos <= ymax; ypos++) + { + nodes = 0; + j = corners - 1; + for (i = 0; i < corners; i++) + { + if ((dy[i] < ypos && dy[j] >= ypos) + || (dy[j] < ypos && dy[i] >= ypos)) /* Line crosses */ + { + nodex[nodes++] = dx[i] + (ypos-dy[i])/(dy[j]-dy[i]) * (dx[j] - dx[i]); + } + j = i; + } + + /* Sort the intersections */ + if (nodes) qsort(nodex, nodes, sizeof(double), compare_double); +/* + PGCLOG(("pgc_fill_polygon ypos=%f nodes=%d ", ypos, nodes)); + for (i = 0; i < nodes; i++) + { + PGCLOG(("%f;", nodex[i])); + } + PGCLOG(("\n")); +*/ + /* And fill between them */ + for (i = 0; i < nodes; i += 2) + { + int16_t x1 = nodex[i], x2 = nodex[i + 1], + y1 = ypos, y2 = ypos; + pgc_sto_raster(pgc, &x1, &y1); + pgc_sto_raster(pgc, &x2, &y2); +/* PGCLOG(("pgc_fill_polygon raster %d,%d to %d,%d\n", + x1, y1, x2, y2)); */ + pgc_fill_line_r(pgc, x1, x2, y1); + } + } + free(nodex); + free(dx); + free(dy); +} + + + +/* Draw a filled ellipse (using PGC fixed-point coordinates) */ +void pgc_draw_ellipse(pgc_core_t *pgc, int32_t x, int32_t y) +{ + /* Convert from PGC fixed-point to native floating-point */ + double h = y / 65536.0; + double w = x / 65536.0; + double y0 = pgc->y / 65536.0; + double x0 = pgc->x / 65536.0; + double ypos = 0.0, xpos = 0.0; + double x1; + double xlast = 0.0; + int16_t linemask = pgc->line_pattern; + + pgc_dto_raster(pgc, &x0, &y0); + PGCLOG(("Ellipse: Colour=%d Drawmode=%d fill=%d\n", pgc->colour, + pgc->draw_mode, pgc->fill_mode)); + for (ypos = 0; ypos <= h; ypos++) + { + if (ypos == 0) + { + if (pgc->fill_mode) + { + pgc_fill_line_r(pgc, x0 - w, x0 + w, y0); + } + if (linemask & 0x8000) + { + pgc_plot(pgc, x0 + w, y0); + pgc_plot(pgc, x0 - w, y0); + linemask = (linemask << 1) | 1; + } + else + { + linemask = linemask << 1; + } + xlast = w; + } + else + { + x1 = sqrt((h * h) - (ypos * ypos)) * w / h; + + if (pgc->fill_mode) + { + pgc_fill_line_r(pgc, x0 - x1, x0 + x1, y0 + ypos); + pgc_fill_line_r(pgc, x0 - x1, x0 + x1, y0 - ypos); + } + + /* Draw border */ + for (xpos = xlast; xpos >= x1; xpos--) + { + if (linemask & 0x8000) + { + pgc_plot(pgc, x0 + xpos, y0 + ypos); + pgc_plot(pgc, x0 - xpos, y0 + ypos); + pgc_plot(pgc, x0 + xpos, y0 - ypos); + pgc_plot(pgc, x0 - xpos, y0 - ypos); + linemask = (linemask << 1) | 1; + } + else + { + linemask = linemask << 1; + } + + } + xlast = x1; + } + } +} + +/* Handle the ELIPSE (sic) command */ +static void hndl_ellipse(pgc_core_t *pgc) +{ + int32_t x = 0, y = 0; + + if (!pgc_param_coord(pgc, &x)) return; + if (!pgc_param_coord(pgc, &y)) return; + + pgc_draw_ellipse(pgc, x, y); +} + + + + +/* Handle the POLY command */ +static void hndl_poly(pgc_core_t *pgc) +{ + uint8_t count; + int32_t x[256]; + int32_t y[256]; + int32_t n; + + if (!pgc_param_byte(pgc, &count)) return; + + for (n = 0; n < count; n++) + { + if (!pgc_param_coord(pgc, &x[n])) return; + if (!pgc_param_coord(pgc, &y[n])) return; + } + PGCLOG(("POLY (%d)\n", count)); +} + +/* Parse but don't execute a POLY command (for adding to a command list) */ +static int parse_poly(pgc_core_t *pgc, pgc_commandlist_t *cl, int c) +{ + uint8_t count; + + PGCLOG(("parse_poly\n")); + if (!pgc_param_byte(pgc, &count)) return 0; + PGCLOG(("parse_poly: count=%02x\n", count)); + + if (!pgc_commandlist_append(cl, count)) + { + pgc_error(pgc, PGC_ERROR_OVERFLOW); + return 0; + } + PGCLOG(("parse_poly: parse %d coords\n", 2 * count)); + return pgc_parse_coords(pgc, cl, 2 * count); +} + + +/* Parse but don't execute a command with a fixed number of byte parameters */ +int pgc_parse_bytes(pgc_core_t *pgc, pgc_commandlist_t *cl, int count) +{ + uint8_t *param = malloc(count); + int n; + + if (!param) + { + pgc_error(pgc, PGC_ERROR_OVERFLOW); + return 0; + } + for (n = 0; n < count; n++) + { + if (!pgc_param_byte(pgc, ¶m[n])) + { + free(param); + return 0; + } + } + for (n = 0; n < count; n++) + { + if (!pgc_commandlist_append(cl, param[n])) + { + pgc_error(pgc, PGC_ERROR_OVERFLOW); + free(param); + return 0; + } + } + free(param); + return 1; +} + + +/* Parse but don't execute a command with a fixed number of word parameters */ +int pgc_parse_words(pgc_core_t *pgc, pgc_commandlist_t *cl, int count) +{ + int16_t *param = malloc(count * sizeof(int16_t)); + int n; + + if (!param) + { + pgc_error(pgc, PGC_ERROR_OVERFLOW); + return 0; + } + for (n = 0; n < count; n++) + { + if (!pgc_param_word(pgc, ¶m[n])) return 0; + } + for (n = 0; n < count; n++) + { + if (!pgc_commandlist_append(cl, param[n] & 0xFF) || + !pgc_commandlist_append(cl, param[n] >> 8)) + { + pgc_error(pgc, PGC_ERROR_OVERFLOW); + free(param); + return 0; + } + } + return 1; +} + + + +/* Parse but don't execute a command with a fixed number of coord parameters */ +int pgc_parse_coords(pgc_core_t *pgc, pgc_commandlist_t *cl, int count) +{ + int32_t *param = malloc(count * sizeof(int32_t)); + int n; + + if (!param) + { + pgc_error(pgc, PGC_ERROR_OVERFLOW); + return 0; + } + for (n = 0; n < count; n++) + { + if (!pgc_param_coord(pgc, ¶m[n])) return 0; + } +/* Here's how the real PGC serialises coords: + * + * 100.5 -> 64 00 00 80 ie 0064.8000 + * 100.3 -> 64 00 CD 4C ie 0064.4CCD + * + */ + for (n = 0; n < count; n++) + { + /* Serialise integer part */ + if (!pgc_commandlist_append(cl, (param[n] >> 16) & 0xFF) || + !pgc_commandlist_append(cl, (param[n] >> 24) & 0xFF) || + /* Serialise fraction part */ + !pgc_commandlist_append(cl, (param[n] ) & 0xFF) || + !pgc_commandlist_append(cl, (param[n] >> 8) & 0xFF)) + { + pgc_error(pgc, PGC_ERROR_OVERFLOW); + free(param); + return 0; + } + } + return 1; +} + + + +/* Handle the DISPLAY command */ +static void hndl_display(pgc_core_t *pgc) +{ + uint8_t param; + + if (!pgc_param_byte(pgc, ¶m)) return; + + PGCLOG(("DISPLAY(%d)\n", param)); + if (param > 1) + { + pgc_error(pgc, PGC_ERROR_RANGE); + } + else + { + pgc_setdisplay(pgc, param); + } +} + + +/* Handle the IMAGEW command (memory to screen blit) */ +static void hndl_imagew(pgc_core_t *pgc) +{ + int16_t row, col1, col2; + uint8_t v1, v2; + + if (!pgc_param_word(pgc, &row)) return; + if (!pgc_param_word(pgc, &col1)) return; + if (!pgc_param_word(pgc, &col2)) return; + + if (row >= pgc->screenh || col1 >= pgc->maxw || col2 >= pgc->maxw) + { + pgc_error(pgc, PGC_ERROR_RANGE); + return; + } + + /* In ASCII mode, what is written is a stream of bytes */ + if (pgc->ascii_mode) + { + while (col1 <= col2) + { + if (!pgc_param_byte(pgc, &v1)) return; + pgc_write_pixel(pgc, col1, row, v1); + ++col1; + } + return; + } + else /* In hex mode, it's RLE compressed */ + { + while (col1 <= col2) + { + if (!pgc_param_byte(pgc, &v1)) return; + + if (v1 & 0x80) /* Literal run */ + { + v1 -= 0x7F; + while (col1 <= col2 && v1 != 0) + { + if (!pgc_param_byte(pgc, &v2)) return; + pgc_write_pixel(pgc, col1, row, v2); + ++col1; + --v1; + } + } + else /* Repeated run */ + { + if (!pgc_param_byte(pgc, &v2)) return; + + ++v1; + while (col1 <= col2 && v1 != 0) + { + pgc_write_pixel(pgc, col1, row, v2); + ++col1; + --v1; + } + } + } + } +} + + +/* Select one of the built-in palettes */ +static void pgc_init_lut(pgc_core_t *pgc, int param) +{ + if (param >= 0 && param < 6) + { + memcpy(pgc->palette, init_palette[param], sizeof(pgc->palette)); + } + else if (param == 0xFF) + { + memcpy(pgc->palette, pgc->userpal, sizeof(pgc->palette)); + } + else + { + pgc_error(pgc, PGC_ERROR_RANGE); + } +} + +/* Save the current palette */ +static void hndl_lutsav(pgc_core_t *pgc) +{ + memcpy(pgc->userpal, pgc->palette, sizeof(pgc->palette)); +} + + +/* Handle LUTINT (select palette) */ +static void hndl_lutint(pgc_core_t *pgc) +{ + uint8_t param; + + if (!pgc_param_byte(pgc, ¶m)) return; + + pgc_init_lut(pgc, param); +} + + +/* Handle LUTRD (read palette register) */ +static void hndl_lutrd(pgc_core_t *pgc) +{ + uint8_t param; + uint32_t col; + + if (!pgc_param_byte(pgc, ¶m)) return; + + col = pgc->palette[param]; + + pgc_result_byte(pgc, (col >> 20) & 0x0F); + pgc_result_byte(pgc, (col >> 12) & 0x0F); + pgc_result_byte(pgc, (col >> 4) & 0x0F); +} + +/* Handle LUT (write palette register) */ +static void hndl_lut(pgc_core_t *pgc) +{ + uint8_t param[4]; + int n; + + for (n = 0; n < 4; n++) + { + if (!pgc_param_byte(pgc, ¶m[n])) return; + if (n > 0 && param[n] > 15) + { + pgc_error(pgc, PGC_ERROR_RANGE); + param[n] &= 0x0F; + } + } + pgc->palette[param[0]] = makecol((param[1] * 0x11), + (param[2] * 0x11), + (param[3] * 0x11)); +} + +/* LUT8RD and LUT8 are extensions implemented by several PGC clones, so + * here are functions that implement them even though they aren't + * used by the PGC */ +void pgc_hndl_lut8rd(pgc_core_t *pgc) +{ + uint8_t param; + uint32_t col; + + if (!pgc_param_byte(pgc, ¶m)) return; + + col = pgc->palette[param]; + + pgc_result_byte(pgc, (col >> 16) & 0xFF); + pgc_result_byte(pgc, (col >> 8) & 0xFF); + pgc_result_byte(pgc, col & 0xFF); +} + +void pgc_hndl_lut8(pgc_core_t *pgc) +{ + uint8_t param[4]; + int n; + + for (n = 0; n < 4; n++) + { + if (!pgc_param_byte(pgc, ¶m[n])) return; + } + pgc->palette[param[0]] = makecol((param[1]), (param[2]), (param[3])); +} + + +/* Handle AREAPT (set 16x16 fill pattern) */ +static void hndl_areapt(pgc_core_t *pgc) +{ + int16_t pattern[16]; + int n; + + for (n = 0; n < 16; n++) + { + if (!pgc_param_word(pgc, &pattern[n])) return; + } + memcpy(pgc->fill_pattern, pattern, sizeof(pgc->fill_pattern)); + PGCLOG(("AREAPT(%04x %04x %04x %04x...)\n", + pattern[0] & 0xFFFF, pattern[1] & 0xFFFF, + pattern[2] & 0xFFFF, pattern[3] & 0xFFFF)); +} + + +/* Handle CA (select ASCII mode) */ +static void hndl_ca(pgc_core_t *pgc) +{ + pgc->ascii_mode = 1; +} + +/* Handle CX (select hex mode) */ +static void hndl_cx(pgc_core_t *pgc) +{ + pgc->ascii_mode = 0; +} + +/* CA and CX remain valid in hex mode; they are handled as command 0x43 ('C') + * with a one-byte parameter */ +static void hndl_c(pgc_core_t *pgc) +{ + uint8_t param; + + if (!pgc->inputbyte(pgc, ¶m)) return; + + if (param == 'A') pgc->ascii_mode = 1; + if (param == 'X') pgc->ascii_mode = 0; +} + +/* RESETF resets the PGC */ +static void hndl_resetf(pgc_core_t *pgc) +{ + pgc_reset(pgc); +} + + + +/* TJUST sets text justify settings */ +static void hndl_tjust(pgc_core_t *pgc) +{ + uint8_t param[2]; + + if (!pgc->inputbyte(pgc, ¶m[0])) return; + if (!pgc->inputbyte(pgc, ¶m[1])) return; + + if (param[0] >= 1 && param[0] <= 3 && + param[1] >= 1 && param[1] <= 3) + { + pgc->tjust_h = param[0]; + pgc->tjust_v = param[1]; + } + else + { + pgc_error(pgc, PGC_ERROR_RANGE); + } +} + +/* TSIZE controls text horizontal spacing */ +static void hndl_tsize(pgc_core_t *pgc) +{ + int32_t param = 0; + + if (!pgc_param_coord(pgc, ¶m)) return; + + pgc->tsize = param; + PGCLOG(("TSIZE %d\n", param)); +} + + +/* VWPORT sets up the viewport (roughly, the clip rectangle) in raster + * coordinates, measured from the bottom left of the screen */ +static void hndl_vwport(pgc_core_t *pgc) +{ + int16_t x1, x2, y1, y2; + + if (!pgc_param_word(pgc, &x1)) return; + if (!pgc_param_word(pgc, &x2)) return; + if (!pgc_param_word(pgc, &y1)) return; + if (!pgc_param_word(pgc, &y2)) return; + + PGCLOG(("VWPORT %d,%d,%d,%d\n", x1,x2,y1,y2)); + pgc->vp_x1 = x1; + pgc->vp_x2 = x2; + pgc->vp_y1 = y1; + pgc->vp_y2 = y2; +} + +/* WINDOW defines the coordinate system in use */ +static void hndl_window(pgc_core_t *pgc) +{ + int16_t x1, x2, y1, y2; + + if (!pgc_param_word(pgc, &x1)) return; + if (!pgc_param_word(pgc, &x2)) return; + if (!pgc_param_word(pgc, &y1)) return; + if (!pgc_param_word(pgc, &y2)) return; + + PGCLOG(("WINDOW %d,%d,%d,%d\n", x1,x2,y1,y2)); + pgc->win_x1 = x1; + pgc->win_x2 = x2; + pgc->win_y1 = y1; + pgc->win_y2 = y2; +} + + + +/* The list of commands implemented by this mini-PGC. In order to support + * the original PGC and clones, we support two lists; core commands (listed + * below) and subclass commands (listed in the clone). + * + * Each row has five parameters: + * ASCII-mode command + * Hex-mode command + * Function that executes this command + * Function that parses this command when building a command list + * Parameter for the parse function + * + * TODO: This list omits numerous commands present in a genuine PGC + * (ARC, AREA, AREABC, BUFFER, CIRCLE etc etc). + * TODO: Some commands don't have a parse function (for example, IMAGEW) + * + * The following ASCII entries have special meaning: + * ~~~~~~ command is valid only in hex mode + * ****** end of subclass command list, now process core command list + * @@@@@@ end of core command list + * + */ +static const pgc_command_t pgc_core_commands[] = +{ + { "AREAPT", 0xE7, hndl_areapt, pgc_parse_words, 16 }, + { "AP", 0xE7, hndl_areapt, pgc_parse_words, 16 }, + { "~~~~~~", 0x43, hndl_c }, /* Handle CA / CX in hex mode */ + { "CA", 0xD2, hndl_ca }, + { "CLBEG", 0x70, hndl_clbeg }, + { "CB", 0x70, hndl_clbeg }, + { "CLDEL", 0x74, hndl_cldel, pgc_parse_bytes, 1 }, + { "CD", 0x74, hndl_cldel, pgc_parse_bytes, 1 }, + { "CLEND", 0x71, hndl_clend }, + { "CLRUN", 0x72, hndl_clrun, pgc_parse_bytes, 1 }, + { "CR", 0x72, hndl_clrun, pgc_parse_bytes, 1 }, + { "CLRD", 0x75, hndl_clread, pgc_parse_bytes, 1 }, + { "CRD", 0x75, hndl_clread, pgc_parse_bytes, 1 }, + { "CLOOP", 0x73, hndl_cloop }, + { "CL", 0x73, hndl_cloop }, + { "CLEARS", 0x0F, hndl_clears, pgc_parse_bytes, 1 }, + { "CLS", 0x0F, hndl_clears, pgc_parse_bytes, 1 }, + { "COLOR", 0x06, hndl_color, pgc_parse_bytes, 1 }, + { "C", 0x06, hndl_color, pgc_parse_bytes, 1 }, + { "CX", 0xD1, hndl_cx }, + { "DISPLA", 0xD0, hndl_display, pgc_parse_bytes, 1 }, + { "DI", 0xD0, hndl_display, pgc_parse_bytes, 1 }, + { "ELIPSE", 0x39, hndl_ellipse, pgc_parse_coords, 2 }, + { "EL", 0x39, hndl_ellipse, pgc_parse_coords, 2 }, + { "IMAGEW", 0xD9, hndl_imagew }, + { "IW", 0xD9, hndl_imagew }, + { "LINFUN", 0xEB, hndl_linfun, pgc_parse_bytes, 1 }, + { "LF", 0xEB, hndl_linfun, pgc_parse_bytes, 1 }, + { "LINPAT", 0xEA, hndl_linpat, pgc_parse_words, 1 }, + { "LP", 0xEA, hndl_linpat, pgc_parse_words, 1 }, + { "LUTINT", 0xEC, hndl_lutint, pgc_parse_bytes, 1 }, + { "LI", 0xEC, hndl_lutint, pgc_parse_bytes, 1 }, + { "LUTRD", 0x50, hndl_lutrd, pgc_parse_bytes, 1 }, + { "LUTSAV", 0xED, hndl_lutsav, NULL, 0 }, + { "LUT", 0xEE, hndl_lut, pgc_parse_bytes, 4 }, + { "MOVE", 0x10, hndl_move, pgc_parse_coords, 2 }, + { "M", 0x10, hndl_move, pgc_parse_coords, 2 }, + { "MOVE3", 0x12, hndl_move3, pgc_parse_coords, 3 }, + { "M3", 0x12, hndl_move3, pgc_parse_coords, 3 }, + { "MOVER", 0x11, hndl_mover, pgc_parse_coords, 2 }, + { "MR", 0x11, hndl_mover, pgc_parse_coords, 2 }, + { "MOVER3", 0x13, hndl_mover3, pgc_parse_coords, 3 }, + { "MR3", 0x13, hndl_mover3, pgc_parse_coords, 3 }, + { "PRMFIL", 0xE9, hndl_prmfil, pgc_parse_bytes, 1 }, + { "PF", 0xE9, hndl_prmfil, pgc_parse_bytes, 1 }, + { "POLY", 0x30, hndl_poly, parse_poly }, + { "P", 0x30, hndl_poly, parse_poly }, + { "RESETF", 0x04, hndl_resetf, NULL, 0 }, + { "RF", 0x04, hndl_resetf, NULL, 0 }, + { "TJUST", 0x85, hndl_tjust, pgc_parse_bytes, 2 }, + { "TJ", 0x85, hndl_tjust, pgc_parse_bytes, 2 }, + { "TSIZE", 0x81, hndl_tsize, pgc_parse_coords, 1 }, + { "TS", 0x81, hndl_tsize, pgc_parse_coords, 1 }, + { "VWPORT", 0xB2, hndl_vwport, pgc_parse_words, 4 }, + { "VWP", 0xB2, hndl_vwport, pgc_parse_words, 4 }, + { "WINDOW", 0xB3, hndl_window, pgc_parse_words, 4 }, + { "WI", 0xB3, hndl_window, pgc_parse_words, 4 }, + + { "@@@@@@", 0x00, NULL } +}; + + +/* Writes to CGA registers are copied into the transfer memory buffer */ +void pgc_out(uint16_t addr, uint8_t val, void *p) +{ + pgc_core_t *pgc = (pgc_core_t *)p; + + switch(addr) + { + case 0x3D0: case 0x3D2: case 0x3D4: case 0x3D6: + pgc->mapram[0x3D0] = val; break; + case 0x3D1: case 0x3D3: case 0x3D5: case 0x3D7: + if (pgc->mapram[0x3D0] < 18) + { + pgc->mapram[0x3E0 + pgc->mapram[0x3D0]] = val; + } + break; + case 0x3D8: pgc->mapram[0x3D8] = val; break; + case 0x3D9: pgc->mapram[0x3D9] = val; break; + } + +} + +/* Read back the CGA registers */ +uint8_t pgc_in(uint16_t addr, void *p) +{ + pgc_core_t *pgc = (pgc_core_t *)p; + + switch(addr) + { + case 0x3D0: case 0x3D2: case 0x3D4: case 0x3D6: + return pgc->mapram[0x3D0]; + case 0x3D1: case 0x3D3: case 0x3D5: case 0x3D7: + if (pgc->mapram[0x3D0] < 18) + { + return pgc->mapram[0x3E0 + pgc->mapram[0x3D0]]; + } + return 0xFF; + + case 0x3D8: return pgc->mapram[0x3D8]; + case 0x3D9: return pgc->mapram[0x3D9]; + case 0x3DA: return pgc->mapram[0x3DA]; + } + return 0xFF; +} + +/* Memory write to the transfer buffer */ +void pgc_write(uint32_t addr, uint8_t val, void *p) +{ + pgc_core_t *pgc = (pgc_core_t *)p; + + /* It seems variable whether the PGC maps 1k or 2k at 0xC6000. + * Map 2k here in case a clone requires it */ + if (addr >= 0xC6000 && addr < 0xC6800) + { + addr &= 0x7FF; + + /* If one of the FIFOs has been updated, this may cause + * the drawing thread to be woken */ + + if (pgc->mapram[addr] != val) + { + pgc->mapram[addr] = val; + switch (addr) + { + case 0x300: /* Input write pointer */ + if (pgc->waiting_input_fifo && + pgc->mapram[0x300] != pgc->mapram[0x301]) + { + pgc->waiting_input_fifo = 0; + pgc_wake(pgc); + } + break; + + case 0x303: /* Output read pointer */ + if (pgc->waiting_output_fifo && + pgc->mapram[0x302] != (uint8_t)(pgc->mapram[0x303] - 1)) + { + pgc->waiting_output_fifo = 0; + pgc_wake(pgc); + } + break; + + case 0x305: /* Error read pointer */ + if (pgc->waiting_error_fifo && + pgc->mapram[0x304] != (uint8_t)(pgc->mapram[0x305] - 1)) + { + pgc->waiting_error_fifo = 0; + pgc_wake(pgc); + } + break; + + case 0x306: /* Cold start flag */ + /* XXX This should be in IM-1024 specific code */ + pgc->mapram[0x306] = 0; + break; + + case 0x030C: /* Display type */ + pgc_setdisplay(p, pgc->mapram[0x30C]); + pgc->mapram[0x30D] = pgc->mapram[0x30C]; + break; + + + case 0x3FF: /* Reboot the PGC + (handled on core thread) */ + pgc_wake(pgc); + break; + } // end switch (addr) + } + } + if (addr >= 0xB8000 && addr < 0xBC000 && pgc->cga_selected) + { + addr &= 0x3FFF; + pgc->cga_vram[addr] = val; + } +} + + +uint8_t pgc_read(uint32_t addr, void *p) +{ + pgc_core_t *pgc = (pgc_core_t *)p; + + if (addr >= 0xC6000 && addr < 0xC6800) + { + addr &= 0x7FF; + + return pgc->mapram[addr]; + } + if (addr >= 0xB8000 && addr < 0xBC000 && pgc->cga_selected) + { + addr &= 0x3FFF; + return pgc->cga_vram[addr]; + } + return 0xFF; +} + +/* Called by the drawing thread to read the next byte from the input + * buffer. If no byte available will sleep until one is. Returns 0 if + * a PGC reset has been triggered by a write to 0xC63FF */ +int pgc_input_byte(pgc_core_t *pgc, uint8_t *result) +{ + /* If input buffer empty, wait for it to fill */ + while (pgc->mapram[0x300] == pgc->mapram[0x301]) + { + pgc->waiting_input_fifo = 1; + pgc_sleep(pgc); + } + if (pgc->mapram[0x3FF]) /* Reset triggered */ + { + pgc_reset(pgc); + return 0; + } + + *result = pgc->mapram[pgc->mapram[0x301]]; + ++pgc->mapram[0x301]; + return 1; +} + +/* Called by the drawing thread to write a byte to the output buffer. + * If buffer is full will sleep until it is not. Returns 0 if + * a PGC reset has been triggered by a write to 0xC63FF */ +int pgc_output_byte(pgc_core_t *pgc, uint8_t val) +{ + /* If output buffer full, wait for it to empty */ + while (pgc->mapram[0x302] == (uint8_t)(pgc->mapram[0x303] - 1)) + { + PGCLOG(("Output buffer state: %02x %02x Sleeping\n", + pgc->mapram[0x302], pgc->mapram[0x303])); + pgc->waiting_output_fifo = 1; + pgc_sleep(pgc); + } + if (pgc->mapram[0x3FF]) /* Reset triggered */ + { + pgc_reset(pgc); + return 0; + } + pgc->mapram[0x100 + pgc->mapram[0x302]] = val; + ++pgc->mapram[0x302]; +/* + PGCLOG(("Output %02x: new state: %02x %02x\n", val, + pgc->mapram[0x302], pgc->mapram[0x303])); */ + return 1; +} + +/* Helper to write an entire string to the output buffer */ +int pgc_output_string(pgc_core_t *pgc, const char *s) +{ + while (*s) + { + if (!pgc_output_byte(pgc, *s)) return 0; + ++s; + } + return 1; +} + +/* As pgc_output_byte, for the error buffer */ +int pgc_error_byte(pgc_core_t *pgc, uint8_t val) +{ + /* If error buffer full, wait for it to empty */ + while (pgc->mapram[0x304] == pgc->mapram[0x305] - 1) + { + pgc->waiting_error_fifo = 1; + pgc_sleep(pgc); + } + if (pgc->mapram[0x3FF]) /* Reset triggered */ + { + pgc_reset(pgc); + return 0; + } + pgc->mapram[0x200 + pgc->mapram[0x304]] = val; + ++pgc->mapram[0x304]; + return 1; +} + +/* As pgc_output_string, for the error buffer */ +int pgc_error_string(pgc_core_t *pgc, const char *s) +{ + while (*s) + { + if (!pgc_error_byte(pgc, *s)) return 0; + ++s; + } + return 1; +} + + +/* Report an error, either in ASCII or in hex */ +int pgc_error(pgc_core_t *pgc, int err) +{ + if (pgc->mapram[0x307]) /* Errors enabled? */ + { + if (pgc->ascii_mode) + { + if (err >= PGC_ERROR_RANGE && err <= PGC_ERROR_MISSING) + return pgc_error_string(pgc, pgc_err_msgs[err]); + return pgc_error_string(pgc, "Unknown error\r"); + } + else + { + return pgc_error_byte(pgc, err); + } + } + return 1; +} + + +static inline int is_whitespace(char ch) +{ + return (ch != 0 && strchr(" \r\n\t,;()+-", ch) != NULL); +} + + +/* Read a byte and interpret as ASCII: ignore control characters other than + * CR, LF or tab. */ +int pgc_input_char(pgc_core_t *pgc, char *result) +{ + uint8_t ch; + + while (1) + { + if (!pgc->inputbyte(pgc, &ch)) return 0; + + ch &= 0x7F; + if (ch == '\r' || ch == '\n' || ch == '\t' || ch >= ' ') + { + *result = toupper(ch); + return 1; + } + } +} + + +/* Parameter passed is not a number: abort */ +static int err_digit(pgc_core_t *pgc) +{ + uint8_t asc; + + do /* Swallow everything until the next separator */ + { + if (!pgc->inputbyte(pgc, &asc)) return 0; + } + while (!is_whitespace(asc)); + pgc_error(pgc, PGC_ERROR_DIGIT); + return 0; +} + + +typedef enum parse_state_t +{ + PS_MAIN, + PS_FRACTION, + PS_EXPONENT +} parse_state_t; + +/* Read in a PGC coordinate, either as hex (4 bytes) or ASCII (xxxx.yyyyEeee) + * Returns 0 if PGC reset detected while the value is being read */ +int pgc_param_coord(pgc_core_t *pgc, int32_t *value) +{ + uint8_t asc; + int sign = 1; + int esign = 1; + int n; + uint16_t dp = 1; + uint16_t integer = 0; + uint16_t frac = 0; + uint16_t exponent = 0; + uint32_t res; + parse_state_t state = PS_MAIN; + uint8_t encoded[4]; + + /* If there is a command list running, pull the bytes out of that + * command list */ + if (pgc->clcur) + { + for (n = 0; n < 4; n++) + { + if (!pgc_clist_byte(pgc, &encoded[n])) return 0; + } + integer = (((int16_t)encoded[1]) << 8) | encoded[0]; + frac = (((int16_t)encoded[3]) << 8) | encoded[2]; + + *value = (((int32_t)integer) << 16) | frac; + return 1; + } + /* If in hex mode, read in the encoded integer and fraction parts + * from the hex stream */ + if (!pgc->ascii_mode) + { + for (n = 0; n < 4; n++) + { + if (!pgc->inputbyte(pgc, &encoded[n])) return 0; + } + integer = (((int16_t)encoded[1]) << 8) | encoded[0]; + frac = (((int16_t)encoded[3]) << 8) | encoded[2]; + + *value = (((int32_t)integer) << 16) | frac; + return 1; + } + /* Parsing an ASCII value */ + + /* Skip separators */ + do + { + if (!pgc->inputbyte(pgc, &asc)) return 0; + if (asc == '-') sign = -1; + } + while (is_whitespace(asc)); + + /* There had better be a digit next */ + if (!isdigit(asc)) + { + pgc_error(pgc, PGC_ERROR_MISSING); + return 0; + } + do + { + switch (asc) + { +/* Decimal point is acceptable in 'main' state (start of fraction) + * not otherwise */ + case '.': + if (state == PS_MAIN) + { + if (!pgc->inputbyte(pgc, &asc)) return 0; + state = PS_FRACTION; + continue; + } + else + { + pgc_error(pgc, PGC_ERROR_MISSING); + return err_digit(pgc); + } + break; + /* Scientific notation */ + case 'd': case 'D': case 'e': case 'E': + esign = 1; + if (!pgc->inputbyte(pgc, &asc)) return 0; + if (asc == '-') + { + sign = -1; + if (!pgc->inputbyte(pgc, &asc)) return 0; + } + state = PS_EXPONENT; + continue; + /* Should be a number or a separator */ + default: + if (is_whitespace(asc)) break; + if (!isdigit(asc)) + { + pgc_error(pgc, PGC_ERROR_MISSING); + return err_digit(pgc); + } + asc -= '0'; /* asc is digit */ + switch (state) + { + case PS_MAIN: + integer = (integer * 10)+asc; + if (integer & 0x8000) /* Overflow */ + { + pgc_error(pgc, PGC_ERROR_RANGE); + integer = 0x7FFF; + } + break; + case PS_FRACTION: + frac = (frac * 10)+asc; + dp *= 10; + break; + case PS_EXPONENT: + exponent = (exponent * 10)+asc; + break; + } + + } + if (!pgc->inputbyte(pgc, &asc)) return 0; + } + while (!is_whitespace(asc)); + + + res = (frac << 16) / dp; + PGCLOG(("integer=%u frac=%u exponent=%u dp=%d res=0x%08x\n", + integer, frac, exponent, dp, res)); + + res = (res & 0xFFFF) | (integer << 16); + if (exponent) + { + for (n = 0; n < exponent; n++) + { + if (esign > 0) res *= 10; + else res /= 10; + } + } + *value = sign*res; + + return 1; +} + +/* Pull the next byte from the current command list */ +int pgc_clist_byte(pgc_core_t *pgc, uint8_t *val) +{ + if (pgc->clcur == NULL) return 0; + + if (pgc->clcur->rdptr < pgc->clcur->wrptr) + { + *val = pgc->clcur->list[pgc->clcur->rdptr++]; + } + else + { + *val = 0; + } + /* If we've reached the end, reset to the beginning and + * (if repeating) run the repeat */ + if (pgc->clcur->rdptr >= pgc->clcur->wrptr) + { + pgc->clcur->rdptr = 0; + --pgc->clcur->repeat; + if (pgc->clcur->repeat == 0) + { + pgc->clcur = pgc->clcur->chain; + } + } + return 1; +} + + +/* Read in a byte, either as hex (1 byte) or ASCII (decimal). + * Returns 0 if PGC reset detected while the value is being read */ +int pgc_param_byte(pgc_core_t *pgc, uint8_t *val) +{ + int32_t c; + + if (pgc->clcur) return pgc_clist_byte(pgc, val); + + if (!pgc->ascii_mode) return pgc->inputbyte(pgc, val); + + if (!pgc_param_coord(pgc, &c)) return 0; + c = (c >> 16); /* Drop fractional part */ + + if (c > 255) + { + pgc_error(pgc, PGC_ERROR_RANGE); + return 0; + } + *val = (uint8_t)c; + return 1; +} + +/* Read in a word, either as hex (2 bytes) or ASCII (decimal). + * Returns 0 if PGC reset detected while the value is being read */ +int pgc_param_word(pgc_core_t *pgc, int16_t *val) +{ + int32_t c; + + if (pgc->clcur) + { + uint8_t lo, hi; + + if (!pgc_clist_byte(pgc, &lo)) return 0; + if (!pgc_clist_byte(pgc, &hi)) return 0; + *val = (((int16_t)hi) << 8) | lo; + return 1; + } + + if (!pgc->ascii_mode) + { + uint8_t lo, hi; + + if (!pgc->inputbyte(pgc, &lo)) return 0; + if (!pgc->inputbyte(pgc, &hi)) return 0; + *val = (((int16_t)hi) << 8) | lo; + return 1; + } + + if (!pgc_param_coord(pgc, &c)) return 0; + + c = (c >> 16); + if (c > 0x7FFF || c < -0x7FFF) + { + pgc_error(pgc, PGC_ERROR_RANGE); + return 0; + } + *val = (int16_t)c; + return 1; +} + + +/* Output a byte, either as hex or ASCII depending on the mode */ +int pgc_result_byte(pgc_core_t *pgc, uint8_t val) +{ + char buf[20]; + + if (!pgc->ascii_mode) return pgc_output_byte(pgc, val); + + if (pgc->result_count) + { + if (!pgc_output_byte(pgc, ',')) return 0; + } + sprintf(buf, "%d", val); + ++pgc->result_count; + return pgc_output_string(pgc, buf); +} + + +/* Output a word, either as hex or ASCII depending on the mode */ +int pgc_result_word(pgc_core_t *pgc, int16_t val) +{ + char buf[20]; + + if (!pgc->ascii_mode) + { + if (!pgc_output_byte(pgc, val & 0xFF)) return 0; + return pgc_output_byte(pgc, val >> 8); + } + + if (pgc->result_count) + { + if (!pgc_output_byte(pgc, ',')) return 0; + } + sprintf(buf, "%d", val); + ++pgc->result_count; + return pgc_output_string(pgc, buf); +} + + +/* Write a screen pixel (x and y are raster coordinates, ink is the + * value to write) */ +void pgc_write_pixel(pgc_core_t *pgc, uint16_t x, uint16_t y, uint8_t ink) +{ + uint8_t *vram; + + /* Suppress out-of-range writes; clip to viewport */ + if (x < pgc->vp_x1 || x > pgc->vp_x2 || x >= pgc->maxw || + y < pgc->vp_y1 || y > pgc->vp_y2 || y >= pgc->maxh) + { + PGCLOG(("pgc_write_pixel clipped: (%d,%d) " + "vp_x1=%d vp_y1=%d vp_x2=%d vp_y2=%d " + "ink=0x%02x\n", x, y, + pgc->vp_x1, pgc->vp_y1, + pgc->vp_x2, pgc->vp_y2, ink)); + return; + } + vram = pgc_vram_addr(pgc, x, y); + if (vram) *vram = ink; +} + +/* Read a screen pixel (x and y are raster coordinates) */ +uint8_t pgc_read_pixel(pgc_core_t *pgc, uint16_t x, uint16_t y) +{ + uint8_t *vram; + + /* Suppress out-of-range reads */ + if (x >= pgc->maxw || y >= pgc->maxh) + { + return 0; + } + vram = pgc_vram_addr(pgc, x, y); + if (vram) return *vram; + return 0; +} + + + + +/* Plot a point in the current colour and draw mode. Raster coordinates. */ +void pgc_plot(pgc_core_t *pgc, uint16_t x, uint16_t y) +{ + uint8_t *vram; + + /* Only allow plotting within the current viewport. */ + if (x < pgc->vp_x1 || x > pgc->vp_x2 || x >= pgc->maxw || + y < pgc->vp_y1 || y > pgc->vp_y2 || y >= pgc->maxh) + { + PGCLOG(("pgc_plot clipped: (%d,%d) %d <= x <= %d; %d <= y <= %d; " + "mode=%d ink=0x%02x\n", x, y, + pgc->vp_x1, pgc->vp_x2, pgc->vp_y1, pgc->vp_y2, + pgc->draw_mode, pgc->colour)); + return; + } + vram = pgc_vram_addr(pgc, x, y); + if (!vram) return; + + /* TODO: Does not implement the PGC plane mask (set by MASK) */ + switch (pgc->draw_mode) + { + default: + case 0: *vram = pgc->colour; break; /* Write */ + case 1: *vram ^= 0xFF; break; /* Invert */ + case 2: *vram ^= pgc->colour; break; /* XOR colour */ + case 3: *vram &= pgc->colour; break; /* AND */ + } +} + + +/* Given raster coordinates, find the matching address in PGC video RAM */ +uint8_t *pgc_vram_addr(pgc_core_t *pgc, int16_t x, int16_t y) +{ + int offset; + +/* We work from the bottom left-hand corner */ + if (y < 0 || y >= pgc->maxh || x < 0 || x >= pgc->maxw) return NULL; + + offset = (pgc->maxh - 1 -y) * (pgc->maxw) + x; +// PGCLOG(("pgc_vram_addr x=%d y=%d offset=%d\n", x, y, offset); + + if (offset < 0 || offset >= (pgc->maxw * pgc->maxh)) + { + return NULL; + } + return &pgc->vram[offset]; +} + + + +/* Read in the next command, either as hex (1 byte) or ASCII (up to 6 + * characters) */ +int pgc_read_command(pgc_core_t *pgc) +{ + if (pgc->clcur) + { + return pgc_clist_byte(pgc, &pgc->hex_command); + } + else if (pgc->ascii_mode) + { + char ch; + int count = 0; + + while (count < 7) + { + if (!pgc_input_char(pgc, &ch)) return 0; + if (is_whitespace(ch)) + { + /* Pad to 6 characters */ + while (count < 6) + pgc->asc_command[count++] = ' '; + pgc->asc_command[6] = 0; + return 1; + } + pgc->asc_command[count++] = toupper(ch); + } + return 1; + } + else + { + return pgc->inputbyte(pgc, &pgc->hex_command); + } +} + + +/* Read in the next command and parse it */ +static int pgc_parse_command(pgc_core_t *pgc, const pgc_command_t **pcmd) +{ + const pgc_command_t *cmd; + char match[7]; + + *pcmd = NULL; + pgc->hex_command = 0; + memset(pgc->asc_command, ' ', 6); + pgc->asc_command[6] = 0; + if (!pgc_read_command(pgc)) + { + /* PGC has been reset */ + return 0; + } +/* Scan the list of valid commands. pgc->pgc_commands may be a subclass + * list (terminated with '*') or the core list (terminated with '@') */ + for (cmd = pgc->pgc_commands; cmd->ascii[0] != '@'; cmd++) + { + /* End of subclass command list, chain to core */ + if (cmd->ascii[0] == '*') cmd = pgc_core_commands; + + /* If in ASCII mode match on the ASCII command */ + if (pgc->ascii_mode && !pgc->clcur) + { + sprintf(match, "%-6.6s", cmd->ascii); + if (!strncmp(match, pgc->asc_command, 6)) + { + *pcmd = cmd; + pgc->hex_command = cmd->hex; + break; + } + } + else /* Otherwise match on the hex command */ + { + if (cmd->hex == pgc->hex_command) + { + sprintf(pgc->asc_command, "%-6.6s", + cmd->ascii); + *pcmd = cmd; + break; + } + } + } + return 1; +} + + +/* The PGC drawing thread main loop. Read in commands and execute them ad + * infinitum */ +void pgc_core_thread(void *p) +{ + pgc_core_t *pgc = (pgc_core_t *)p; + const pgc_command_t *cmd; + + PGCLOG(("pgc_core_thread begins")); + while (1) + { + if (!pgc_parse_command(pgc, &cmd)) + { + /* PGC has been reset */ + continue; + } + PGCLOG(("PGC command: [%02x] '%s' found=%d\n", + pgc->hex_command, pgc->asc_command, (cmd != NULL))); + + if (cmd) + { + pgc->result_count = 0; + (*cmd->handler)(pgc); + } + else + { + pgc_error(pgc, PGC_ERROR_OPCODE); + } + } +} + +void pgc_recalctimings(pgc_core_t *pgc) +{ + double disptime, _dispontime, _dispofftime; + double pixel_clock = (cpuclock * (double)(1ull << 32)) / (pgc->cga_selected ? 25175000.0 : pgc->native_pixel_clock); + + /* Use a fixed 640 columns, like the T3100e */ + disptime = pgc->screenw + 11; + _dispontime = pgc->screenw * pixel_clock; + _dispofftime = (disptime - _dispontime) * pixel_clock; + pgc->dispontime = (uint64_t)_dispontime; + pgc->dispofftime = (uint64_t)_dispofftime; +} + +/* Draw the display in CGA (640x400) text mode */ +void pgc_cga_text(pgc_core_t *pgc, int w) +{ + int x, c; + uint8_t chr, attr; + int drawcursor = 0; + uint32_t cols[2]; + int pitch = (pgc->mapram[0x3E9] + 1) * 2; + uint16_t sc = (pgc->displine & 0x0F) % pitch; + uint16_t ma = (pgc->mapram[0x3ED] | (pgc->mapram[0x3EC] << 8)) & 0x3fff; + uint16_t ca = (pgc->mapram[0x3EF] | (pgc->mapram[0x3EE] << 8)) & 0x3fff; + uint8_t *addr; + int cw = (w == 80) ? 8 : 16; + + addr = &pgc->cga_vram[((ma << 1) + (((pgc->displine / pitch)*w)) * 2) & 0x3ffe]; + ma += (pgc->displine / pitch) * w; + + for (x = 0; x < w; x++) + { + chr = addr[0]; + attr = addr[1]; + addr += 2; + + /* Cursor enabled? */ + if (ma == ca && (pgc->cgablink & 8) && + (pgc->mapram[0x3EA] & 0x60) != 0x20) + { + drawcursor = ((pgc->mapram[0x3EA] & 0x1F) <= (sc >> 1)) + && ((pgc->mapram[0x3EB] & 0x1F) >= (sc >> 1)); + } + else drawcursor = 0; + if (pgc->mapram[0x3D8] & 0x20) + { + cols[1] = attr & 15; + cols[0] = (attr >> 4) & 7; + if ((pgc->cgablink & 8) && (attr & 0x80) && !drawcursor) + cols[1] = cols[0]; + } + else + { + cols[1] = attr & 15; + cols[0] = attr >> 4; + } + if (drawcursor) + { + for (c = 0; c < cw; c++) + { + if (w == 80) + ((uint32_t *)buffer32->line[pgc->displine & 2047])[(x << 3) + c] = cols[(fontdatm[chr + pgc->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ 0xffffff; + else + ((uint32_t *)buffer32->line[pgc->displine & 2047])[(x << 4) + c] = cols[(fontdatm[chr + pgc->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ 0xffffff; + } + } + else + { + for (c = 0; c < cw; c++) + { + if (w == 80) + ((uint32_t *)buffer32->line[pgc->displine & 2047])[(x << 3) + c] = cols[(fontdatm[chr + pgc->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0]; + else + ((uint32_t *)buffer32->line[pgc->displine & 2047])[(x << 4) + c] = cols[(fontdatm[chr + pgc->fontbase][sc] & (1 << ((c >> 1) ^ 7))) ? 1 : 0]; + } + } + ma++; + } +} + + +/* Draw the display in CGA (320x200) graphics mode */ +void pgc_cga_gfx40(pgc_core_t *pgc) +{ + int x, c; + uint32_t cols[4]; + int col; + uint16_t ma = (pgc->mapram[0x3ED] | (pgc->mapram[0x3EC] << 8)) & 0x3fff; + uint8_t *addr; + uint16_t dat; + + cols[0] = pgc->mapram[0x3D9] & 15; + col = (pgc->mapram[0x3D9] & 16) ? 8 : 0; + + if (pgc->mapram[0x3D8] & 4) + { + cols[1] = col | 3; + cols[2] = col | 4; + cols[3] = col | 7; + } + else if (pgc->mapram[0x3D9] & 32) + { + cols[1] = col | 3; + cols[2] = col | 5; + cols[3] = col | 7; + } + else + { + cols[1] = col | 2; + cols[2] = col | 4; + cols[3] = col | 6; + } + for (x = 0; x < 40; x++) + { + addr = &pgc->cga_vram[(ma + 2 * x + 80 * (pgc->displine >> 2) + 0x2000 * ((pgc->displine >> 1) & 1)) & 0x3FFF]; + dat = (addr[0] << 8) | addr[1]; + pgc->ma++; + for (c = 0; c < 8; c++) + { + ((uint32_t *)buffer32->line[pgc->displine & 2047])[(x << 4) + (c << 1)] = + ((uint32_t *)buffer32->line[pgc->displine & 2047])[(x << 4) + (c << 1) + 1] = cols[dat >> 14]; + dat <<= 2; + } + } +} + + +/* Draw the display in CGA (640x200) graphics mode */ +void pgc_cga_gfx80(pgc_core_t *pgc) +{ + int x, c; + uint32_t cols[2]; + uint16_t ma = (pgc->mapram[0x3ED] | (pgc->mapram[0x3EC] << 8)) & 0x3fff; + uint8_t *addr; + uint16_t dat; + + cols[0] = 0; + cols[1] = pgc->mapram[0x3D9] & 15; + + for (x = 0; x < 40; x++) + { + addr = &pgc->cga_vram[(ma + 2 * x + 80 * (pgc->displine >> 2) + 0x2000 * ((pgc->displine >> 1) & 1)) & 0x3FFF]; + dat = (addr[0] << 8) | addr[1]; + pgc->ma++; + for (c = 0; c < 16; c++) + { + ((uint32_t *)buffer32->line[pgc->displine & 2047])[(x << 4) + c] = cols[dat >> 15]; + dat <<= 1; + } + } +} + + + +/* Draw the screen in CGA mode. Based on the simplified WY700 renderer + * rather than the original CGA, since the PGC doesn't have a real 6845 */ +void pgc_cga_poll(pgc_core_t *pgc) +{ + int c; + uint32_t cols[2]; + + if (!pgc->linepos) + { + timer_advance_u64(&pgc->timer, pgc->dispofftime); + pgc->mapram[0x3DA] |= 1; + pgc->linepos = 1; + if (pgc->cgadispon) + { + if (pgc->displine == 0) + { + video_wait_for_buffer(); + } + + if ((pgc->mapram[0x3D8] & 0x12) == 0x12) + { + pgc_cga_gfx80(pgc); + } + else if (pgc->mapram[0x3D8] & 0x02) + { + pgc_cga_gfx40(pgc); + } + else if (pgc->mapram[0x3D8] & 0x01) + { + pgc_cga_text(pgc, 80); + } + else + { + pgc_cga_text(pgc, 40); + } + } + else + { + cols[0] = ((pgc->mapram[0x3D8] & 0x12) == 0x12) ? 0 : (pgc->mapram[0x3D9] & 15); + hline(buffer32, 0, pgc->displine & 2047, PGC_CGA_WIDTH, cols[0]); + } + + for (c = 0; c < PGC_CGA_WIDTH; c++) + ((uint32_t *)buffer32->line[pgc->displine & 2047])[c] = cgapal[((uint32_t *)buffer32->line[pgc->displine & 2047])[c] & 0xf]; + + pgc->displine++; + if (pgc->displine == PGC_CGA_HEIGHT) + { + pgc->mapram[0x3DA] |= 8; + pgc->cgadispon = 0; + } + if (pgc->displine == PGC_CGA_HEIGHT + 32) + { + pgc->mapram[0x3DA] &= ~8; + pgc->cgadispon = 1; + pgc->displine = 0; + } + } + else + { + if (pgc->cgadispon) + { + pgc->mapram[0x3DA] &= ~1; + } + timer_advance_u64(&pgc->timer, pgc->dispontime); + pgc->linepos = 0; + + if (pgc->displine == PGC_CGA_HEIGHT) + { + if (PGC_CGA_WIDTH != xsize || PGC_CGA_HEIGHT != ysize) + { + xsize = PGC_CGA_WIDTH; + ysize = PGC_CGA_HEIGHT; + updatewindowsize(xsize, ysize); + } + video_blit_memtoscreen(0, 0, 0, ysize, xsize, ysize); + frames++; + + /* We have a fixed 640x400 screen for + * CGA modes */ + video_res_x = PGC_CGA_WIDTH; + video_res_y = PGC_CGA_HEIGHT; + switch (pgc->mapram[0x3D8] & 0x12) + { + case 0x12: video_bpp = 1; break; + case 0x02: video_bpp = 2; break; + default: video_bpp = 0; break; + } + pgc->cgablink++; + } + } +} + + + +/* Draw the screen in CGA or native mode. */ +void pgc_poll(void *p) +{ + pgc_core_t *pgc = (pgc_core_t *)p; + int x, y; + + if (pgc->cga_selected) + { + pgc_cga_poll(pgc); + return; + } +/* Not CGA, so must be native mode */ + if (!pgc->linepos) + { + timer_advance_u64(&pgc->timer, pgc->dispofftime); + pgc->mapram[0x3DA] |= 1; + pgc->linepos = 1; + if (pgc->cgadispon && pgc->displine < pgc->maxh) + { + if (pgc->displine == 0) + { + video_wait_for_buffer(); + } + /* Don't know why pan needs to be multiplied by -2, but + * the IM1024 driver uses PAN -112 for an offset of + * 224. */ + y = pgc->displine - 2 * pgc->pan_y; + for (x = 0; x < pgc->screenw; x++) + { + if (x + pgc->pan_x < pgc->maxw) + { + ((uint32_t *)buffer32->line[pgc->displine & 2047])[x] = pgc->palette[pgc->vram[y * pgc->maxw + x]]; + } + else + { + ((uint32_t *)buffer32->line[pgc->displine & 2047])[x] = pgc->palette[0]; + } + } + } + else + { + hline(buffer32, 0, pgc->displine & 2047, pgc->screenw, pgc->palette[0]); + } + pgc->displine++; + if (pgc->displine == pgc->screenh) + { + pgc->mapram[0x3DA] |= 8; + pgc->cgadispon = 0; + } + if (pgc->displine == pgc->screenh + 32) + { + pgc->mapram[0x3DA] &= ~8; + pgc->cgadispon = 1; + pgc->displine = 0; + } + } + else + { + if (pgc->cgadispon) + { + pgc->mapram[0x3DA] &= ~1; + } + timer_advance_u64(&pgc->timer, pgc->dispontime); + pgc->linepos = 0; + + if (pgc->displine == pgc->screenh) + { + if (pgc->screenw != xsize || pgc->screenh != ysize) + { + xsize = pgc->screenw; + ysize = pgc->screenh; + updatewindowsize(xsize, ysize); + } + video_blit_memtoscreen(0, 0, 0, ysize, xsize, ysize); + frames++; + + video_res_x = pgc->screenw; + video_res_y = pgc->screenh; + video_bpp = 8; + pgc->cgablink++; + } + } +} + + +/* Initialise RAM and registers to default values */ +void pgc_reset(pgc_core_t *pgc) +{ + int n; + + memset(pgc->mapram, 0, sizeof(pgc->mapram)); +/* There is no point in emulating the 'CGA disable' jumper as this is only + * appropriate for a dual-head system, not emulated by PCEM */ + pgc->mapram[0x30B] = pgc->cga_enabled = 1; + pgc->mapram[0x3F8] = 0x03; /* Minor version */ + pgc->mapram[0x3F9] = 0x01; /* Minor version */ + pgc->mapram[0x3FB] = 0xA5; /* } */ + pgc->mapram[0x3FC] = 0x5A; /* PGC self-test passed */ + pgc->mapram[0x3FD] = 0x55; /* } */ + pgc->mapram[0x3FE] = 0x5A; /* } */ + + pgc->ascii_mode = 1; /* Start off in ASCII mode */ + pgc->line_pattern = 0xFFFF; + memset(pgc->fill_pattern, 0xFF, sizeof(pgc->fill_pattern)); + pgc->colour = 0xFF; + pgc->tjust_h = 1; + pgc->tjust_v = 1; + + /* Reset panning */ + pgc->pan_x = 0; + pgc->pan_y = 0; + + /* Reset clipping */ + pgc->vp_x1 = 0; + pgc->vp_y1 = 0; + pgc->vp_x2 = pgc->visw - 1; + pgc->vp_y2 = pgc->vish - 1; + + /* Empty command lists */ + for (n = 0; n < 256; n++) + { + pgc->clist[n].wrptr = 0; + pgc->clist[n].rdptr = 0; + pgc->clist[n].repeat = 0; + pgc->clist[n].chain = 0; + } + pgc->clcur = NULL; + /* Select CGA display */ + pgc->cga_selected = -1; + pgc_setdisplay(pgc, pgc->cga_enabled); + pgc->mapram[0x30C] = pgc->cga_enabled; + pgc->mapram[0x30D] = pgc->cga_enabled; + /* Default palette is 0 */ + pgc_init_lut(pgc, 0); + hndl_lutsav(pgc); +} + + +/* Initialisation code common to the PGC and its subclasses. + * + * Pass the 'input byte' function in since this is overridden in + * the IM-1024, and needs to be set before the drawing thread is + * launched */ +void pgc_core_init(pgc_core_t *pgc, int maxw, int maxh, int visw, int vish, + int (*inpbyte)(struct pgc_core_t *pgc, uint8_t *result), double native_pixel_clock) +{ + int n; + + mem_mapping_add(&pgc->mapping, 0xC6000, 0x800, + pgc_read, NULL, NULL, + pgc_write, NULL, NULL, NULL, MEM_MAPPING_EXTERNAL, pgc); + mem_mapping_add(&pgc->cga_mapping, 0xB8000, 0x8000, + pgc_read, NULL, NULL, + pgc_write, NULL, NULL, NULL, MEM_MAPPING_EXTERNAL, pgc); + io_sethandler(0x3D0, 0x0010, pgc_in, NULL, NULL, pgc_out, NULL, NULL, + pgc); + + pgc->maxw = maxw; + pgc->maxh = maxh; + pgc->visw = visw; + pgc->vish = vish; + pgc->vram = malloc(maxw * maxh); + pgc->cga_vram = malloc(0x4000); + pgc->clist = calloc(256, sizeof(pgc_commandlist_t)); + pgc->clcur = NULL; + pgc->native_pixel_clock = native_pixel_clock; + + memset(pgc->vram, 0, maxw * maxh); + memset(pgc->cga_vram, 0, 0x4000); + /* Empty command lists */ + for (n = 0; n < 256; n++) + { + pgc->clist[n].list = NULL; + pgc->clist[n].listmax = 0; + pgc->clist[n].wrptr = 0; + pgc->clist[n].rdptr = 0; + pgc->clist[n].repeat = 0; + pgc->clist[n].chain = NULL; + } + + pgc_reset(pgc); + pgc->inputbyte = inpbyte; + pgc->pgc_commands = pgc_core_commands; + pgc->pgc_wake_thread = thread_create_event(); + pgc->pgc_thread = thread_create(pgc_core_thread, pgc); + + timer_add(&pgc->timer, pgc_poll, (void *)pgc, 1); + + timer_add(&pgc->wake_timer, pgc_wake_timer, (void *)pgc, 0); +} + + +/* Initialisation code specific to the PGC */ +void *pgc_standalone_init() +{ + pgc_core_t *pgc = malloc(sizeof(pgc_core_t)); + memset(pgc, 0, sizeof(pgc_core_t)); + + /* Framebuffer and screen are both 640x480 */ + pgc_core_init(pgc, 640, 480, 640, 480, pgc_input_byte, 25175000.0); + return pgc; +} + + +void pgc_close(void *p) +{ + pgc_core_t *pgc = (pgc_core_t *)p; + + thread_kill(pgc->pgc_thread); + thread_destroy_event(pgc->pgc_wake_thread); + + if (pgc->cga_vram) + { + free(pgc->cga_vram); + } + if (pgc->vram) + { + free(pgc->vram); + } + free(pgc); +} + +void pgc_speed_changed(void *p) +{ + pgc_core_t *pgc = (pgc_core_t *)p; + + pgc_recalctimings(pgc); +} + + +device_config_t pgc_config[] = +{ + { + .type = -1 + } +}; + +device_t pgc_device = +{ + "PGC", + 0, + pgc_standalone_init, + pgc_close, + NULL, + pgc_speed_changed, + NULL, + NULL, + pgc_config +}; + diff --git a/src/vid_pgc.h b/src/vid_pgc.h new file mode 100644 index 0000000..621f4a3 --- /dev/null +++ b/src/vid_pgc.h @@ -0,0 +1,164 @@ +#include "timer.h" + +struct pgc_core_t; + +typedef struct pgc_commandlist_t +{ + uint8_t *list; + uint32_t listmax; + uint32_t wrptr; + uint32_t rdptr; + uint32_t repeat; + struct pgc_commandlist_t *chain; +} pgc_commandlist_t; + +int pgc_commandlist_append(pgc_commandlist_t *list, uint8_t v); + +typedef struct pgc_command_t +{ + char ascii[6]; + uint8_t hex; + void (*handler)(struct pgc_core_t *pgc); + int (*parser) (struct pgc_core_t *pgc, pgc_commandlist_t *cl, + int p); + int p; +} pgc_command_t; + + +typedef struct pgc_core_t +{ + mem_mapping_t mapping; + mem_mapping_t cga_mapping; + + pgc_commandlist_t *clist; + pgc_commandlist_t *clcur; + uint8_t mapram[2048]; /* Host <--> PGC communication buffer */ + uint8_t *cga_vram; + uint8_t *vram; + char asc_command[7]; + uint8_t hex_command; + uint32_t palette[256]; + uint32_t userpal[256]; + uint32_t maxw, maxh; /* Maximum framebuffer size */ + uint32_t visw, vish; /* Maximum screen size */ + uint32_t screenw, screenh; + int16_t pan_x, pan_y; + uint16_t win_x1, win_x2, win_y1, win_y2; + uint16_t vp_x1, vp_x2, vp_y1, vp_y2; + int16_t fill_pattern[16]; + int16_t line_pattern; + uint8_t draw_mode; + uint8_t fill_mode; + uint8_t colour; + uint8_t tjust_h; /* Horizontal alignment 1=left 2=centre 3=right*/ + uint8_t tjust_v; /* Vertical alignment 1=bottom 2=centre 3=top*/ + int32_t tsize; /* Horizontal spacing */ + + int32_t x, y, z;/* Drawing position */ + + const pgc_command_t *pgc_commands; + thread_t *pgc_thread; + event_t *pgc_wake_thread; + pc_timer_t wake_timer; +// int wake_timer; + + int waiting_input_fifo; + int waiting_output_fifo; + int waiting_error_fifo; + int cga_enabled; + int cga_selected; + int ascii_mode; + int result_count; + + int fontbase; + int linepos, displine; + int vc; + int cgadispon; + int con, coff, cursoron, cgablink; + int vsynctime, vadj; + uint16_t ma, maback; + int oddeven; + + uint64_t dispontime, dispofftime; + pc_timer_t timer; +// int vidtime; + double native_pixel_clock; + + int drawcursor; + + int (*inputbyte)(struct pgc_core_t *pgc, uint8_t *result); + +} pgc_core_t; + +void pgc_init(pgc_core_t *pgc); +void pgc_out(uint16_t addr, uint8_t val, void *p); +uint8_t pgc_in(uint16_t addr, void *p); +void pgc_write(uint32_t addr, uint8_t val, void *p); +uint8_t pgc_read(uint32_t addr, void *p); +void pgc_recalctimings(pgc_core_t *pgc); +void pgc_poll(void *p); +void pgc_reset(pgc_core_t *pgc); +void pgc_wake(pgc_core_t *pgc); +/* Plot a pixel (raster coordinates, current ink) */ +void pgc_plot(pgc_core_t *pgc, uint16_t x, uint16_t y); +void pgc_write_pixel(pgc_core_t *pgc, uint16_t x, uint16_t y, uint8_t ink); +uint8_t pgc_read_pixel(pgc_core_t *pgc, uint16_t x, uint16_t y); +uint8_t *pgc_vram_addr(pgc_core_t *pgc, int16_t x, int16_t y); +/* Draw line (window coordinates) */ +uint16_t pgc_draw_line(pgc_core_t *pgc, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t linemask); +/* Draw line (raster coordinates) */ +uint16_t pgc_draw_line_r(pgc_core_t *pgc, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t linemask); +void pgc_draw_ellipse(pgc_core_t *pgc, int32_t x, int32_t y); +void pgc_fill_polygon(pgc_core_t *pgc, unsigned corners, int32_t *x, int32_t *y); +/* Horizontal line in fill pattern (raster coordinates) */ +void pgc_fill_line_r(pgc_core_t *pgc, int32_t x0, int32_t x1, int32_t y); + +/* Convert to raster coordinates */ +void pgc_sto_raster(pgc_core_t *pgc, int16_t *x, int16_t *y); +void pgc_ito_raster(pgc_core_t *pgc, int32_t *x, int32_t *y); +void pgc_dto_raster(pgc_core_t *pgc, double *x, double *y); + +/* Read/write to the PGC's FIFOs */ +int pgc_input_byte(pgc_core_t *pgc, uint8_t *val); +int pgc_output_byte(pgc_core_t *pgc, uint8_t val); +int pgc_output_string(pgc_core_t *pgc, const char *val); +int pgc_error_byte(pgc_core_t *pgc, uint8_t val); +int pgc_error_string(pgc_core_t *pgc, const char *val); + +int pgc_param_byte(pgc_core_t *pgc, uint8_t *val); +int pgc_param_word(pgc_core_t *pgc, int16_t *val); +int pgc_param_coord(pgc_core_t *pgc, int32_t *val); +int pgc_result_byte(pgc_core_t *pgc, uint8_t val); +int pgc_result_word(pgc_core_t *pgc, int16_t val); +int pgc_result_coord(pgc_core_t *pgc, int32_t val); + +void pgc_sleep(pgc_core_t *pgc); +int pgc_error(pgc_core_t *pgc, int err); +void pgc_core_init(pgc_core_t *pgc, int maxw, int maxh, int visw, int vish, + int (*inpbyte)(struct pgc_core_t *pgc, uint8_t *result), double native_pixel_clock); +void pgc_speed_changed(void *p); +void pgc_close(void *p); + +void pgc_hndl_lut8(pgc_core_t *pgc); +void pgc_hndl_lut8rd(pgc_core_t *pgc); + +int pgc_parse_bytes(pgc_core_t *pgc, pgc_commandlist_t *cl, int p); +int pgc_parse_words(pgc_core_t *pgc, pgc_commandlist_t *cl, int p); +int pgc_parse_coords(pgc_core_t *pgc, pgc_commandlist_t *cl, int p); + +extern device_t pgc_device; + +#define PGC_ERROR_RANGE 0x01 +#define PGC_ERROR_INTEGER 0x02 +#define PGC_ERROR_MEMORY 0x03 +#define PGC_ERROR_OVERFLOW 0x04 +#define PGC_ERROR_DIGIT 0x05 +#define PGC_ERROR_OPCODE 0x06 +#define PGC_ERROR_RUNNING 0x07 +#define PGC_ERROR_STACK 0x08 +#define PGC_ERROR_TOOLONG 0x09 +#define PGC_ERROR_AREA 0x0A +#define PGC_ERROR_MISSING 0x0B + +/* #define PGCLOG(x) pclog x */ +#define PGCLOG(x) diff --git a/src/vid_voodoo_codegen_x86-64.h b/src/vid_voodoo_codegen_x86-64.h index d729231..c8bb5f8 100644 --- a/src/vid_voodoo_codegen_x86-64.h +++ b/src/vid_voodoo_codegen_x86-64.h @@ -3354,17 +3354,7 @@ static void voodoo_codegen_init(voodoo_t *voodoo) #if WIN64 voodoo->codegen_data = VirtualAlloc(NULL, sizeof(voodoo_x86_data_t) * BLOCK_NUM * 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE); #else - voodoo->codegen_data = malloc(sizeof(voodoo_x86_data_t) * BLOCK_NUM * 2); -#endif - -#if defined(__linux__) || defined(__APPLE__) - start = (void *)((long)voodoo->codegen_data & pagemask); - len = ((sizeof(voodoo_x86_data_t) * BLOCK_NUM * 2) + pagesize) & pagemask; - if (mprotect(start, len, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) - { - perror("mprotect"); - exit(-1); - } + voodoo->codegen_data = mmap(0, sizeof(voodoo_x86_data_t) * BLOCK_NUM*2, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0); #endif for (c = 0; c < 256; c++) @@ -3394,7 +3384,7 @@ static void voodoo_codegen_close(voodoo_t *voodoo) #if WIN64 VirtualFree(voodoo->codegen_data, 0, MEM_RELEASE); #else - free(voodoo->codegen_data); + munmap(voodoo->codegen_data, sizeof(voodoo_x86_data_t) * BLOCK_NUM*2); #endif } diff --git a/src/vid_voodoo_codegen_x86.h b/src/vid_voodoo_codegen_x86.h index 48b0cca..b09ed0e 100644 --- a/src/vid_voodoo_codegen_x86.h +++ b/src/vid_voodoo_codegen_x86.h @@ -3292,17 +3292,7 @@ static void voodoo_codegen_init(voodoo_t *voodoo) #if defined WIN32 || defined _WIN32 || defined _WIN32 voodoo->codegen_data = VirtualAlloc(NULL, sizeof(voodoo_x86_data_t) * BLOCK_NUM*2, MEM_COMMIT, PAGE_EXECUTE_READWRITE); #else - voodoo->codegen_data = malloc(sizeof(voodoo_x86_data_t) * BLOCK_NUM*2); -#endif - -#if defined(__linux__) || defined(__APPLE__) - start = (void *)((long)voodoo->codegen_data & pagemask); - len = ((sizeof(voodoo_x86_data_t) * BLOCK_NUM*2) + pagesize) & pagemask; - if (mprotect(start, len, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) - { - perror("mprotect"); - exit(-1); - } + voodoo->codegen_data = mmap(0, sizeof(voodoo_x86_data_t) * BLOCK_NUM*2, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0); #endif for (c = 0; c < 256; c++) @@ -3332,6 +3322,6 @@ static void voodoo_codegen_close(voodoo_t *voodoo) #if defined WIN32 || defined _WIN32 || defined _WIN32 VirtualFree(voodoo->codegen_data, 0, MEM_RELEASE); #else - free(voodoo->codegen_data); + munmap(voodoo->codegen_data, sizeof(voodoo_x86_data_t) * BLOCK_NUM*2); #endif } diff --git a/src/video.c b/src/video.c index b416594..7eb151d 100644 --- a/src/video.c +++ b/src/video.c @@ -24,6 +24,7 @@ #include "vid_genius.h" #include "vid_hercules.h" #include "vid_ht216.h" +#include "vid_im1024.h" #include "vid_incolor.h" #include "vid_colorplus.h" #include "vid_mda.h" @@ -35,6 +36,7 @@ #include "vid_pc1640.h" #include "vid_pc200.h" #include "vid_pcjr.h" +#include "vid_pgc.h" #include "vid_ps1_svga.h" #include "vid_s3.h" #include "vid_s3_virge.h" @@ -94,6 +96,7 @@ static VIDEO_CARD video_cards[] = {"EGA", "ega", &ega_device, GFX_EGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}}, {"Hercules", "hercules", &hercules_device, GFX_HERCULES, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}}, {"Hercules InColor", "incolor", &incolor_device, GFX_INCOLOR, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}}, + {"Image Manager 1024", "im1024", &im1024_device, GFX_IM1024, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}}, {"MDA", "mda", &mda_device, GFX_MDA, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}}, {"MDSI Genius", "genius", &genius_device, GFX_GENIUS, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}}, {"Number Nine 9FX (S3 Trio64)", "n9_9fx", &s3_9fx_device, GFX_N9_9FX, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}}, @@ -101,6 +104,9 @@ static VIDEO_CARD video_cards[] = {"OAK OTI-067", "oti067", &oti067_device, GFX_OTI067, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 6, 8, 16, 6, 8, 16}}, {"Olivetti GO481 (Paradise PVGA1A)", "olivetti_go481", ¶dise_pvga1a_oli_go481_device, GFX_OLIVETTI_GO481, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 6, 8, 16, 6, 8, 16}}, {"Paradise Bahamas 64 (S3 Vision864)", "bahamas64", &s3_bahamas64_device, GFX_BAHAMAS64, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 5, 20, 20, 35}}, +/* Not offered as the emulation is very incomplete + {"Professional Graphics Controller", "pgc", &pgc_device, GFX_PGC, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}}, +*/ {"Phoenix S3 Trio32", "px_trio32", &s3_phoenix_trio32_device, GFX_PHOENIX_TRIO32, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}}, {"Phoenix S3 Trio64", "px_trio64", &s3_phoenix_trio64_device, GFX_PHOENIX_TRIO64, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}}, {"Plantronics ColorPlus", "plantronics", &colorplus_device, GFX_COLORPLUS, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}}, @@ -817,6 +823,7 @@ uint8_t fontdat[2048][8]; uint8_t fontdatm[2048][16]; uint8_t fontdatw[512][32]; /* Wyse700 font */ uint8_t fontdat8x12[256][16]; /* MDSI Genius font */ +uint8_t fontdat12x18[256][36]; /* IM1024 font */ uint8_t fontdatksc5601[16384][32]; /* Korean KSC-5601 font */ uint8_t fontdatksc5601_user[192][32]; /* Korean KSC-5601 user defined font */ @@ -952,6 +959,13 @@ void loadfont(char *s, fontformat_t format) fread(&fontdatm[c][0], 1, 16, f); } break; + case FONT_IM1024: /* Image Manager 1024 native font */ + for (c=0;c<256;c++) + { + fread(&fontdat12x18[c][0], 1, 36, f); + } + break; + } fclose(f); } diff --git a/src/video.h b/src/video.h index 4194b7a..459abcb 100644 --- a/src/video.h +++ b/src/video.h @@ -93,7 +93,7 @@ typedef enum FONT_T3100E, /* Toshiba T3100e, four fonts */ FONT_KSC5601, /* Korean KSC-5601 */ FONT_SIGMA400, /* Sigma Color 400, 8x8 and 8x16 */ - + FONT_IM1024, /* Image Manager 1024 */ } fontformat_t; void loadfont(char *s, fontformat_t format);