Tutorials
create a table that will hold 65536 entries, (the size is the number of colors squared)
sequence table
table = repeat(0, 65536)
sequence globalpal, -- the global palette
pal1, -- the palette for bitmap source1
pal2 -- the palette for bitmap source2
mix the two palettes together... (if the same palette is used for both images, most likely if you use only one global palette, pal1 and pal2 can be replaced by globalpal.)
for i = 1 to 256 do
for j = 1 to 256 do
table[i+(j-1)*256] = mix(pal1[i], pal2[j]) - 1
end for
end for
where the mix function averages the rgb components and then searches the palette for the closest match to that average, then returns that index. (subtract one because palette indexing starts at 1 and the bitmap colors start at 0.)
function mix(sequence c1, sequence c2)
integer idx, diff, x
c1 = floor((c1 + c2) / 2) -- take the average (change this to mix with different weights)
c1 = repeat(c1, 256) - globalpal -- find the difference from the global palette
c1 = c1*c1 -- square the difference
idx = 1
diff = 100000 -- start with a huge difference
for i = 1 to 256 do
x = c1[i][1]+c1[i][2]+c1[i][3]
if x = 0 then
return i -- exact match
elsif x < diff then
diff = x -- save closest so far
idx = i -- save index
end if
end for
return idx
end function
now that you have the table (after a very long wait), you can save it to
disk...
integer fn
-- save it
fn = open("mixtable.dat", wb)
if fn != -1 then
puts(fn, table)
close(fn)
end if
-- load it
table = repeat(0, 65536)
fn = open("mixtable.dat", rb)
if fn != -1 then
for i = 1 to 65536 do
table[i] = getc(fn)
end for
close(fn)
end if
Since loading the table from disk is much faster than calculating it every
time, people will not have to wait every time your program starts (unlike
modex demos 2 and 3). To actually mix two bitmaps in realtime, use the
table to find the new color.
for y=y1 to y2 do
for x=x1 to x2 do
dest[y][x] = table[source1[y][x] + source2[y][x]*256 + 1]
end for
end for
for modex I wrote the mix table calculations in asm for extra speed, the mixtable was stored in memory instead of a sequence, and the mixcopy command was optimized to quickly read the lookup table.
(I haven't tested the code presented here... let me know if you have any problems)
I made a nifty case procedure that I think is cool:
procedure case(atom key, sequence cases, integer default_proc)
integer i
i = find({1,0}, (cases = key))
if not i then
i = find({1,1}, (cases = key))
end if
if i then
call_proc(cases[i][2], {})
elsif default_proc then
call_proc(default_proc, {})
end if
end procedure
procedure case1()
puts(1, "Case 1\n")
end procedure
procedure case2()
puts(1, "Case 2\n")
end procedure
procedure case3()
puts(1, "Case 3\n")
end procedure
procedure default()
puts(1, "default\n")
end procedure
constant case_seq = {
{1, routine_id("case1")},
{2, routine_id("case2")},
{3, routine_id("case3")}
}
constant def_proc = routine_id("default")
case(1, case_seq, def_proc)
case(2, case_seq, def_proc)
case(3, case_seq, def_proc)
case(4, case_seq, def_proc)
case(5, case_seq, 0)
For flicker-free graphics you should wait until the monitor isn't refreshing before drawing to the display. Note: since the monitor refreshes around 60hz, you must completely update the screen within the 1/60th of a second interval. So it might be a good idea to build the next screen in memory, then copy it to the real screen using mem_copy. (Btw, the mode 19 video memory lies at address #A0000.)
global constant wait_retrace = allocate(20)
poke(wait_retrace, {
#50, -- PUSH EAX
#52, -- PUSH EDX
#BA,#DA,3,0,0,-- MOV EDX, 0x03DA
#EC, -- IN AL, DX
#A8,#08, -- TEST AL, 0x08
#75,#FB, -- JNZ -5
#EC, -- IN AL, DX
#A8,#08, -- TEST AL, 0x08
#74,#FB, -- JZ -5
#5A, -- POP EDX
#58, -- POP EAX
#C3 } ) -- RET
use "call(wait_retrace)" before copying your virtual screen...
function abs(object x)
object temp
if atom(x) then
if x<0 then return -x else return x end if
end if
for i=1 to length(x) do
temp = x[i]
if atom(temp) then
if temp<0 then
x[i] = -temp
end if
else
x[i] = abs(temp)
end if
end for
return x
end function
function reverse(sequence s)
object temp
integer lower, n
n = length(s)
lower = 1
for upper = n to floor(n/2)+1 by -1 do
temp = s[lower]
s[lower] = s[upper]
s[upper] = temp
lower = lower + 1
end for
return s
end function
Mode 19 usually sets the border color to palette entry 16, which is a pain when you want it to be 0. Here's two ways to change that:
include machine.e
procedure border_color(integer color)
-- set border & background color
sequence regs
regs=repeat(0,10)
regs[REG_AX]=#0B00
regs[REG_BX]=color
regs=dos_interrupt(#10,regs)
end procedure
-- Graeme.
You can reprogram the vga card overscan (the border) color by doing the following:
{#60, -- 0: pusha
#66,#BA,#DA,#03, -- 1: mov dx, #3DA
#EC, -- 5: in al, dx
#B2,#C0, -- 6: mov dl, #C0
#B0,#31, -- 8: mov al, #31
#EE, -- A: out dx, al
#B0,#00, -- B: mov al, yourcolor (12)
#EE, -- D: out dx, al
#61, -- E: popa
#C3} -- F: ret