455 lines
11 KiB
Lua
455 lines
11 KiB
Lua
-- thomson.lua : lots of utility for handling
|
||
-- thomson screen.
|
||
--
|
||
-- Version: 02-jan-2017
|
||
--
|
||
-- Copyright 2016-2017 by Samuel Devulder
|
||
--
|
||
-- This program is free software; you can redistribute
|
||
-- it and/or modify it under the terms of the GNU
|
||
-- General Public License as published by the Free
|
||
-- Software Foundation; version 2 of the License.
|
||
-- See <http://www.gnu.org/licenses/>
|
||
|
||
if not thomson then
|
||
|
||
run("color.lua") -- optionnal
|
||
|
||
thomson = {optiMAP=true}
|
||
|
||
-- RAM banks
|
||
thomson.ramA = {}
|
||
thomson.ramB = {}
|
||
|
||
function thomson.clear()
|
||
for i=1,8000 do
|
||
thomson.ramA[i] = 0
|
||
thomson.ramB[i] = 0
|
||
end
|
||
end
|
||
|
||
-- color levels
|
||
thomson.levels = {
|
||
-- in pc-space (0-255):
|
||
pc = {0,100,127,142,163,179,191,203,215,223,231,239,
|
||
243,247,251,255},
|
||
-- in linear space (0-255):
|
||
linear = {},
|
||
-- maps pc-levels (0-255) to thomson levels (1-16)
|
||
pc2to={},
|
||
-- maps linear-levels (0-255) to thomson levels (1-16)
|
||
linear2to={}
|
||
};
|
||
|
||
-- pc space to linear space
|
||
local function toLinear(val)
|
||
-- use the version from Color library
|
||
if not Color then
|
||
val = val/255
|
||
if val<=0.081 then
|
||
val = val/4.5;
|
||
else
|
||
val = ((val+0.099)/1.099)^2.2;
|
||
end
|
||
val = val*255
|
||
return val;
|
||
else
|
||
return Color:new(val,0,0):toLinear().r
|
||
end
|
||
end
|
||
|
||
for i=1,16 do
|
||
thomson.levels.linear[i] = toLinear(thomson.levels.pc[i])
|
||
end
|
||
for i=0,255 do
|
||
local r,cm,dm;
|
||
r,cm,dm = toLinear(i),0,1e30
|
||
for c,v in ipairs(thomson.levels.linear) do
|
||
local d = math.abs(v-r);
|
||
if d<dm then cm,dm = c,d; end
|
||
end
|
||
thomson.levels.pc2to[i] = cm;
|
||
r,cm,dm = i,0,1e30
|
||
for c,v in ipairs(thomson.levels.linear) do
|
||
local d = math.abs(v-r);
|
||
if d<dm then cm,dm = c,d; end
|
||
end
|
||
thomson.levels.linear2to[i] = cm;
|
||
end
|
||
|
||
-- palette stuff
|
||
function thomson.palette(i, pal)
|
||
-- returns palette #i if pal is missing (nil)
|
||
-- if pal is a number, sets palette #i
|
||
-- if pal is an array, sets the palette #i, #i+1, ...
|
||
if type(pal)=='table' then
|
||
for j,v in ipairs(pal) do
|
||
thomson.palette(i+j-1,v)
|
||
end
|
||
elseif pal and i>=0 and i<thomson._palette.max then
|
||
thomson._palette[i+1] = pal
|
||
elseif not pal and i>=0 and i<thomson._palette.max then
|
||
return thomson._palette[i+1]
|
||
end
|
||
end;
|
||
thomson._palette = {offset = 0, max=16}
|
||
thomson.default_palette = {0,15,240,255,3840,3855,4080,4095,
|
||
1911,826,931,938,2611,2618,3815,123}
|
||
|
||
-- border color
|
||
function thomson.border(c)
|
||
if c then
|
||
thomson._border = c;
|
||
else
|
||
return thomson._border
|
||
end
|
||
end
|
||
thomson.border(0)
|
||
|
||
-- helper to appen tables to tables
|
||
function thomson._append(result, ...)
|
||
for _,tab in ipairs({...}) do
|
||
for _,v in ipairs(tab) do
|
||
table.insert(result,v)
|
||
end
|
||
end
|
||
end
|
||
|
||
-- RLE compression of data into result
|
||
function thomson._compress(result,data)
|
||
local partial,p,pmax={},1,#data
|
||
local function addCarToPartial(car)
|
||
partial[2] = partial[2]+1
|
||
partial[2+partial[2]] = car
|
||
end
|
||
while p<=pmax do
|
||
local num,car = 1,data[p]
|
||
while num<255 and p<pmax and data[p+1]==car do
|
||
num,p = num+1,p+1
|
||
end
|
||
local default=true
|
||
if partial[1] then
|
||
-- 01 aa 01 bb ==> 00 02 aa bb
|
||
if default and num==1 and partial[1]==1 then
|
||
partial = {0,2,partial[2],car}
|
||
default = false
|
||
end
|
||
-- 00 n xx xx xx 01 bb ==> 00 n+1 xx xx xx bb
|
||
if default and num==1 and partial[1]==0 and partial[2]<255 then
|
||
addCarToPartial(car)
|
||
default = false
|
||
end
|
||
-- 00 n xx xx xx 02 bb ==> 00 n+2 xx xx xx bb bb (pas utile mais sert quand combin<69> <20> la regle ci-dessus)
|
||
if default and num==2 and partial[1]==0 and partial[2]<254 then
|
||
addCarToPartial(car)
|
||
addCarToPartial(car)
|
||
default = false
|
||
end
|
||
end
|
||
if default then
|
||
thomson._append(result, partial)
|
||
partial = {num,car}
|
||
end
|
||
p=p+1
|
||
end
|
||
thomson._append(result, partial)
|
||
return result
|
||
end
|
||
|
||
-- save a map file corresponging to the current file
|
||
-- if a map file already exist, a confirmation is
|
||
-- prompted to the user
|
||
local function save_current_file()
|
||
local function exist(file)
|
||
local f=io.open(file,'rb')
|
||
if not f then return false else io.close(f); return true; end
|
||
end
|
||
local name,path = getfilename()
|
||
local mapname = string.gsub(name,"%.%w*$","") .. ".map"
|
||
local fullname = path .. '/' .. mapname
|
||
local ok = not exist(fullname)
|
||
if not ok then
|
||
selectbox("Ovr " .. mapname .. "?", "Yes", function() ok = true; end, "No", function() ok = false; end)
|
||
end
|
||
if ok then thomson.savep(fullname) end
|
||
end
|
||
|
||
-- saves the thomson screen into a MAP file
|
||
function thomson.savep(name)
|
||
if not name then return save_current_file() end
|
||
|
||
wait(0) -- allow for key handling
|
||
local data = thomson._get_map_data()
|
||
local tmp = {0, math.floor(#data/256), #data%256,0,0}
|
||
thomson._append(tmp,data,{255,0,0,0,0})
|
||
local function save(name, buf)
|
||
local out = io.open(name,"wb")
|
||
out:write(buf)
|
||
out:close()
|
||
end
|
||
save(name, string.char(unpack(tmp)))
|
||
|
||
-- save raw data as well ?
|
||
local moved, key, mx, my, mb = waitinput(0.01)
|
||
if key==4123 then -- shift-ESC ==> save raw files as well
|
||
save(name .. ".rama", string.char(unpack(thomson.ramA)))
|
||
save(name .. ".ramb", string.char(unpack(thomson.ramB)))
|
||
local pal = ""
|
||
for i=0,15 do
|
||
local val = thomson.palette(i)
|
||
pal=pal..string.char(math.floor(val/256),val%256)
|
||
end
|
||
save(name .. ".pal", pal)
|
||
messagebox('Saved MAP + RAMA/RAMB/PAL files.')
|
||
end
|
||
end
|
||
waitbreak(0.01)
|
||
|
||
function thomson.info(...)
|
||
local txt = ""
|
||
for _,t in ipairs({...}) do txt = txt .. t end
|
||
statusmessage(txt);
|
||
if waitbreak(0)==1 then
|
||
local ok=false
|
||
selectbox("Abort ?", "Yes", function() ok = true end, "No", function() ok = false end)
|
||
if ok then error('Operation aborted') end
|
||
end
|
||
end
|
||
|
||
-- copy ramA/B onto GrafX2 screen
|
||
function thomson.updatescreen()
|
||
-- back out
|
||
for i=0,255 do
|
||
setcolor(i,0,0,0)
|
||
end
|
||
-- refresh screen content
|
||
clearpicture(thomson._palette.offset + thomson.border())
|
||
for y=0,thomson.h-1 do
|
||
for x=0,thomson.w-1 do
|
||
local p = thomson.point(x,y)
|
||
if p<0 then p=-p-1 end
|
||
thomson._putpixel(x,y,thomson._palette.offset + p)
|
||
end
|
||
end
|
||
-- refresh palette
|
||
for i=1,thomson._palette.max do
|
||
local v=thomson._palette[i]
|
||
local r=v % 16
|
||
local g=math.floor(v/16) % 16
|
||
local b=math.floor(v/256) % 16
|
||
setcolor(i+thomson._palette.offset-1,
|
||
thomson.levels.pc[r+1],
|
||
thomson.levels.pc[g+1],
|
||
thomson.levels.pc[b+1])
|
||
end
|
||
updatescreen()
|
||
end
|
||
|
||
-- bitmap 16 mode
|
||
function thomson.setBM16()
|
||
-- put a pixel onto real screen
|
||
function thomson._putpixel(x,y,c)
|
||
putpicturepixel(x*2+0,y,c)
|
||
putpicturepixel(x*2+1,y,c)
|
||
end
|
||
-- put a pixel in thomson screen
|
||
function thomson.pset(x,y,c)
|
||
local bank = x%4<2 and thomson.ramA or thomson.ramB
|
||
local offs = math.floor(x/4)+y*40+1
|
||
if x%2==0 then
|
||
bank[offs] = (bank[offs]%16)+c*16
|
||
else
|
||
bank[offs] = math.floor(bank[offs]/16)*16+c
|
||
end
|
||
-- c=c+thomson._palette.offset
|
||
-- putpicturepixel(x*2+0,y,c)
|
||
-- putpicturepixel(x*2+1,y,c)
|
||
end
|
||
-- get thomson pixel at (x,y)
|
||
function thomson.point(x,y)
|
||
local bank = x%4<2 and thomson.ramA or thomson.ramB
|
||
local offs = math.floor(x/4)+y*40+1
|
||
if x%2==0 then
|
||
return math.floor(bank[offs]/16)
|
||
else
|
||
return bank[offs]%16
|
||
end
|
||
end
|
||
-- return internal MAP file
|
||
function thomson._get_map_data()
|
||
local tmp = {}
|
||
for x=1,40 do
|
||
for y=x,x+7960,40 do
|
||
table.insert(tmp, thomson.ramA[y])
|
||
end
|
||
for y=x,x+7960,40 do
|
||
table.insert(tmp, thomson.ramB[y])
|
||
end
|
||
wait(0) -- allow for key handling
|
||
end
|
||
local pal = {}
|
||
for i=1,16 do
|
||
pal[2*i-1] = math.floor(thomson._palette[i]/256)
|
||
pal[2*i+0] = thomson._palette[i]%256
|
||
end
|
||
-- build data
|
||
local data={
|
||
-- BM16
|
||
0x40,
|
||
-- ncols-1
|
||
79,
|
||
-- nlines-1
|
||
24
|
||
};
|
||
thomson._compress(data, tmp)
|
||
thomson._append(data,{0,0})
|
||
-- padd to word
|
||
if #data%2==1 then table.insert(data,0); end
|
||
-- tosnap
|
||
thomson._append(data,{0,128,0,thomson.border(),0,3})
|
||
thomson._append(data, pal)
|
||
thomson._append(data,{0xa5,0x5a})
|
||
return data
|
||
end
|
||
|
||
thomson.w = 160
|
||
thomson.h = 200
|
||
thomson.palette(0,thomson.default_palette)
|
||
thomson.border(0)
|
||
thomson.clear()
|
||
end
|
||
|
||
-- mode MO5
|
||
function thomson.setMO5()
|
||
-- put a pixel onto real screen
|
||
thomson._putpixel = putpicturepixel
|
||
-- helpers
|
||
local function bittst(val,mask)
|
||
-- return bit32.btest(val,mask)
|
||
return (val % (2*mask))>=mask;
|
||
end
|
||
local function bitset(val,mask)
|
||
-- return bit32.bor(val, mask)
|
||
return bittst(val,mask) and val or (val+mask)
|
||
end
|
||
local function bitclr(val,mask)
|
||
-- return bit32.band(val,255-mask)
|
||
return bittst(val,mask) and (val-mask) or val
|
||
end
|
||
-- put a pixel in thomson screen
|
||
function thomson.pset(x,y,c)
|
||
local offs = math.floor(x/8)+y*40+1
|
||
local mask = 2^(7-(x%8))
|
||
if c>=0 then
|
||
thomson.ramB[offs] = (thomson.ramB[offs]%16)+c*16
|
||
thomson.ramA[offs] = bitset(thomson.ramA[offs],mask)
|
||
else
|
||
c=-c-1
|
||
thomson.ramB[offs] = math.floor(thomson.ramB[offs]/16)*16+c
|
||
thomson.ramA[offs] = bitclr(thomson.ramA[offs],mask)
|
||
end
|
||
end
|
||
-- get thomson pixel at (x,y)
|
||
function thomson.point(x,y)
|
||
local offs = math.floor(x/8)+y*40+1
|
||
local mask = 2^(7-(x%8))
|
||
if bittst(thomson.ramA[offs],mask) then
|
||
return math.floor(thomson.ramB[offs]/16)
|
||
else
|
||
return -(thomson.ramB[offs]%16)-1
|
||
end
|
||
end
|
||
-- convert color from MO5 to TO7 (MAP requires TO7 encoding)
|
||
local function mo5to7(val)
|
||
-- MO5: DCBA 4321
|
||
-- __
|
||
-- TO7: 4DCB A321
|
||
local t=((val%16)>=8) and 0 or 128
|
||
val = math.floor(val/16)*8 + (val%8)
|
||
val = (val>=64 and val-64 or val+64) + t
|
||
return val
|
||
end
|
||
-- return internal MAP file
|
||
function thomson._get_map_data()
|
||
-- create columnwise data
|
||
local tmpA,tmpB={},{};
|
||
for x=1,40 do
|
||
for y=x,x+7960,40 do
|
||
table.insert(tmpA, thomson.ramA[y])
|
||
table.insert(tmpB, thomson.ramB[y])
|
||
end
|
||
wait(0) -- allow for key handling
|
||
end
|
||
if thomson.optiMAP then
|
||
-- optimize
|
||
for i=2,8000 do
|
||
local c1,c2 = math.floor(tmpB[i-0]/16),tmpB[i-0]%16
|
||
local d1,d2 = math.floor(tmpB[i-1]/16),tmpB[i-1]%16
|
||
|
||
if tmpA[i-1]==255-tmpA[i] or c1==d2 and c2==c1 then
|
||
tmpA[i] = 255-tmpA[i]
|
||
tmpB[i] = c2*16+c1
|
||
elseif tmpA[i]==255 and c1==d1 or tmpA[i]==0 and c2==d2 then
|
||
tmpB[i] = tmpB[i-1]
|
||
end
|
||
end
|
||
else
|
||
for i=1,8000 do
|
||
local c1,c2 = math.floor(tmpB[i]/16),tmpB[i]%16
|
||
|
||
if tmpA[i]==255 or c1<c2 then
|
||
tmpA[i] = 255-tmpA[i]
|
||
tmpB[i] = c2*16+c1
|
||
end
|
||
end
|
||
end
|
||
-- convert into to7 encoding
|
||
for i=1,#tmpB do tmpB[i] = mo5to7(tmpB[i]); end
|
||
-- build data
|
||
local data={
|
||
-- BM40
|
||
0x00,
|
||
-- ncols-1
|
||
39,
|
||
-- nlines-1
|
||
24
|
||
};
|
||
thomson._compress(data, tmpA); tmpA=nil;
|
||
thomson._append(data,{0,0})
|
||
thomson._compress(data, tmpB); tmpB=nil;
|
||
thomson._append(data,{0,0})
|
||
-- padd to word (for compatibility with basic)
|
||
if #data%2==1 then table.insert(data,0); end
|
||
|
||
-- tosnap
|
||
local orig_palette = true
|
||
for i=0,15 do
|
||
if thomson.default_palette[i+1]~=thomson.palette(i) then
|
||
orig_palette = false
|
||
break
|
||
end
|
||
end
|
||
if not orig_palette then
|
||
local pal = {}
|
||
for i=0,15 do
|
||
local v = thomson.palette(i)
|
||
pal[2*i+1] = math.floor(v/256)
|
||
pal[2*i+2] = v%256
|
||
end
|
||
thomson._append(data,{0,0,0,thomson.border(),0,0})
|
||
thomson._append(data, pal)
|
||
thomson._append(data,{0xa5,0x5a})
|
||
end
|
||
|
||
return data
|
||
end
|
||
|
||
thomson.w = 320
|
||
thomson.h = 200
|
||
thomson.palette(0,thomson.default_palette)
|
||
thomson.border(0)
|
||
thomson.clear()
|
||
end
|
||
|
||
end -- thomson
|