Tutorials

  • a case statement using routine_id...

  • transparency effects with a lookup table...
  • everyone needs to know how to wait for the vertical retrace...
  • fastest abs
  • fastest reverse
  • changing the border color in mode 19
  • easyasm.html - a asm tutorial for Euphoria programmers (work in progress)
    Transparency effects using a lookup table:

    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...



    Fastest abs() function: Wars have been waged on the listserv over something as trivial as this...
    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
    



    Fastest reverse() function: This is a minor tweak on Rob Craig's fine algorithm, just a teensy bit faster due to swapping values instead of copying to a new sequence.
    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:

  • 1. read a byte from the input status register 1 (port #3DA) you don't have to do anything with this value; it just resets some flags in the vga hardware so we can do the next step.
  • 2. send the byte #31 to the attribute register (port #3C0)
  • 3. send the border color you want to the attribute register (port #3C0)
    You will need Jacques Deschenes ports.e to read and write ports. or you could do it with the following machine code:
       {#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