Previous Thread
Next Thread
Print Thread
Page 29 of 80 1 2 27 28 29 30 31 79 80
Joined: Mar 2001
Posts: 17,217
Likes: 234
R
Very Senior Member
Offline
Very Senior Member
R
Joined: Mar 2001
Posts: 17,217
Likes: 234
That's really cool! Apple II text display in modern fonts smile

For added perversion, use https://www.kreativekorp.com/software/fonts/apple2.shtml as the MAME UI font.

Joined: Feb 2014
Posts: 1,102
Likes: 173
G
Very Senior Member
Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,102
Likes: 173
Here's a small improvement:

Using emu.item(manager:machine().devices[":ram"].items["0/m_pointer"]):read() will always get main memory so the scrolling is not as strange while the softswitches are getting hit.

Also we do inverse characters by flipping the foreground/background color so you can see the cursor.

Code

-- 80 column text overlay for Apple IIe

function calc(line,page) page=page or 0 return line % 8 * 1024 + math.floor(line / 64) * 40 + math.floor((line%64) / 8) * 128 + 8192* (page + 1) end

function iskeypressed(keycode) 
inp = manager:machine():input() return inp:code_pressed(inp:code_from_token(keycode))
end

function striphi(a) if type(a)=="number" then a=string.char(a) end local b="" for i=1,#a do b=b..string.char(string.byte(a,i)&0x7f) end return b end

mem=manager:machine().devices[":maincpu"].spaces["program"]              -- gets main or aux, depending on softswitches
aux=emu.item(manager:machine().devices[":aux:ext80"].items["0/m_ram"])   -- gets aux memory
scr=manager:machine().screens[":screen"]
main=emu.item(manager:machine().devices[":ram"].items["0/m_pointer"])  -- always gets main memory

function widthpct(c) if c==nil or c==string.char(0) then return 0 end return ((manager:ui():get_string_width(c,1.0)/manager:ui():get_string_width("A",1.0))) end
function widthoffset(c) if c==nil or c==string.char(0) then return 0 end return 3.5*(1-widthpct(c)) end


function between(a,b,c) return (a >= b) and (a <= c) end

function applecharfixinverse(c)
  if type(c)=="string" then c=string.byte(c) end
  if c == 0 then return string.char(32),false end -- looks terrible with @ signs at bootup
  if between(c,0,31) then return string.char(c+64),true
  elseif between (c,32,127) then return string.char(c), true
  elseif between (c,128,159) then return string.char(c-128+64), false
  elseif between (c,160,255) then return string.char(c-128), false
  else return 0, false
  end
end

drawoverlay=true
keydelay=0

function print_80col()
for y=0,23 do for x=0,39 do offset=calc(y*8)-8192+x 
c1=striphi(aux:read(1024+offset)) c0=striphi(mem:read_u8(1024+offset)) 
io.write(c1..c0) end print() end
end

function draw_80col()
if keydelay > 0 then keydelay = keydelay - 1 end
if drawoverlay then 
scr:draw_box(0,0,559,279,0xff000000,0xffffffff)for y=0,23 do for x=0,39 do offset=calc(y*8)-8192+x 
c1=aux:read(1024+offset)
c1,c1invert=applecharfixinverse(c1)
c0=main:read(1024+offset)
c0,c0invert=applecharfixinverse(c0)
fgcolor,bgcolor=0xffffffff,0xff000000
if c1invert then fgcolor,bgcolor=bgcolor,fgcolor end
scr:draw_text((x*2+0)*7+widthoffset(c1),y*8,c1,fgcolor,bgcolor) 
fgcolor,bgcolor=0xffffffff,0xff000000
if c0invert then fgcolor,bgcolor=bgcolor,fgcolor end
scr:draw_text((x*2+1)*7+widthoffset(c0),y*8,c0,fgcolor,bgcolor) end end
end
if iskeypressed("KEYCODE_LSHIFT") and keydelay==0 then drawoverlay=not drawoverlay keydelay=20 end
end

draw80dispatchlist={draw_80col}

function draw80dispatch() for i,j in pairs(draw80dispatchlist) do j() end end

function drawon() draw80dispatchlist={draw_80col} end
function drawoff() draw80dispatchlist={} end

if not alreadyregistereddraw80 then
  emu.register_frame_done(draw80dispatch)
  alreadyregistereddraw80=true
end



oops - I added a check for the 0 character since the aux card gets all 0's on bootup and it makes it hard to read.

Last edited by Golden Child; 03/03/19 12:32 PM.
Joined: Mar 2001
Posts: 17,217
Likes: 234
R
Very Senior Member
Offline
Very Senior Member
R
Joined: Mar 2001
Posts: 17,217
Likes: 234
You could key off the m_80col variable in the a2video device to determine if 80 column video is enabled, and either drop to 40 or don't show the overlay.

Joined: Jul 2007
Posts: 42
Likes: 2
Member
Offline
Member
Joined: Jul 2007
Posts: 42
Likes: 2
Thanks to Golden Child and R. Belmont for fixing the Apple keyboard timings.... Can play HERO wonderfully with MAME 0.207.

A-Noid

Joined: Feb 2014
Posts: 1,102
Likes: 173
G
Very Senior Member
Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,102
Likes: 173
Hi guys,

Just wanted to share my latest lua project for fiddling with Apple disk images.

You can load a disk image into a global "mydisk" variable with:

loaddisk("stellar7.dsk")

then you can do

Code
catalog()
[MAME]> catalog()
(11,0f) 00  *B 003 LETTER                         TS List=(12,0f)
(11,0f) 01  *B 01a ROCK1                          TS List=(12,0c)
(11,0f) 02  *B 04f ROCK2                          TS List=(13,02)
(11,0f) 03  *B 007 ROCK3                          TS List=(18,03)
(11,0f) 04   B 022 TITLE                          TS List=(04,0f)
(11,0f) 05  *B 022 PANEL                          TS List=(1b,0a)
(11,0f) 06  *B 022 BRIEF                          TS List=(1d,08)
(11,0e) 00  *B 008 BRIEF.ST                       TS List=(1f,06)
(11,0e) 01  *B 019 PLAYGAME                       TS List=(20,0e)
(11,0e) 02  *B 00f BRIEFING                       TS List=(21,05)
(11,0e) 03  *B 00e LEV1                           TS List=(22,07)
(11,0e) 04  *B 00e LEV2                           TS List=(10,09)
(11,0e) 05  *B 00e LEV3                           TS List=(0f,0b)
(11,0e) 06  *B 00e LEV4                           TS List=(08,0f)
(11,0d) 00  *B 00e LEV5                           TS List=(0d,0f)
(11,0d) 01  *B 00e LEV6                           TS List=(07,0f)
(11,0d) 02  *B 00e LEV7                           TS List=(06,0f)
(11,0d) 03  *B 002 OBJ1                           TS List=(0b,05)
(11,0d) 04  *B 002 OBJ2                           TS List=(0b,03)
(11,0d) 05  *B 002 OBJ3                           TS List=(0b,01)
(11,0d) 06  *B 002 OBJ4                           TS List=(0a,0f)
(11,0c) 00  *B 002 OBJ5                           TS List=(0a,0d)
(11,0c) 01  *B 002 OBJ6                           TS List=(0a,0b)
(11,0c) 02  *B 002 OBJ7                           TS List=(0a,09)
(11,0c) 03  *B 003 DYN1                           TS List=(0a,07)
(11,0c) 04  *B 003 DYN2                           TS List=(0a,04)
(11,0c) 05  *B 003 DYN3                           TS List=(0a,01)
(11,0c) 06  *B 003 DYN4                           TS List=(09,0e)
(11,0b) 00  *B 003 DYN5                           TS List=(09,0b)
(11,0b) 01  *B 003 DYN6                           TS List=(09,08)
(11,0b) 02  *B 003 DYN7                           TS List=(09,05)
(11,0b) 03   B 002 HIGH                           TS List=(09,02)
(11,0b) 04   A 002 HELLO                          TS List=(19,0c)
(11,0b) 05   A 003 LEVELS VERTAUSCHEN             TS List=(1a,0f)
T=228 out of range
and if you want to load a file:
Code
[MAME]> a=getfileraw("LEVEL")
(11,0f) 00  *B 003 LETTER                         TS List=(12,0f)
...
(11,0b) 04   A 002 HELLO                          TS List=(19,0c)
(11,0b) 05   A 003 LEVELS VERTAUSCHEN             TS List=(1a,0f)
matched "LEVEL" with "LEVELS VERTAUSCHEN            "
You can do basic dumps
Code
[MAME]> basdump(a)
5   PRINT "LEVELS VERTAUSCHEN": PRINT 
10   PRINT 
15   INPUT "LEVEL1, LEVEL2 ZU TAUSCHEN ?";L1,L2
17   IF L1 = 0 OR L2 = 0 THEN 30
18  A$ = "UNLOCK":I = L1: GOSUB 100:I = L2: GOSUB 100
20  I = L1:A$ = "RENAME":J = 99: GOSUB 110:I = L2:J = L1: GOSUB 110:I = 99:J = L2: GOSUB 110
30  A$ = "LOCK":I = L1: GOSUB 100:I = L2: GOSUB 100
40   PRINT "FERTIG.": PRINT : RUN 
99   END 
100   PRINT A$"LEV"I: PRINT A$"OBJ"I: PRINT A$"DYN"I: RETURN 
110   PRINT A$"LEV"I",LEV"J: PRINT A$"OBJ"I",OBJ"J: PRINT A$"DYN"I",DYN"J: RETURN 
or hexdumps

Code
[MAME]> hexdump(a)
00   5c 01 1d 08 05 00 ba 22  |  4c 45 56 45 4c 53 20 56  |  \.....:"LEVELS V
10   45 52 54 41 55 53 43 48  |  45 4e 22 3a ba 00 23 08  |  ERTAUSCHEN"::.#.
20   0a 00 ba 00 4d 08 0f 00  |  84 22 4c 45 56 45 4c 31  |  ..:.M...."LEVEL1
30   2c 20 4c 45 56 45 4c 32  |  20 5a 55 20 54 41 55 53  |  , LEVEL2 ZU TAUS
40   43 48 45 4e 20 3f 22 3b  |  4c 31 2c 4c 32 00 5f 08  |  CHEN ?";L1,L2._.
50   11 00 ad 4c 31 d0 30 ce  |  4c 32 d0 30 c4 33 30 00  |  ..-L1P0NL2P0D30.
60   84 08 12 00 41 24 d0 22  |  04 55 4e 4c 4f 43 4b 22  |  ....A$P".UNLOCK"
70   3a 49 d0 4c 31 3a b0 31  |  30 30 3a 49 d0 4c 32 3a  |  :IPL1:0100:IPL2:
80   b0 31 30 30 00 c2 08 14  |  00 49 d0 4c 31 3a 41 24  |  0100.B...IPL1:A$
90   d0 22 04 52 45 4e 41 4d  |  45 22 3a 4a d0 39 39 3a  |  P".RENAME":JP99:
a0   b0 31 31 30 3a 49 d0 4c  |  32 3a 4a d0 4c 31 3a b0  |  0110:IPL2:JPL1:0
b0   31 31 30 3a 49 d0 39 39  |  3a 4a d0 4c 32 3a b0 31  |  110:IP99:JPL2:01
c0   31 30 00 e5 08 1e 00 41  |  24 d0 22 04 4c 4f 43 4b  |  10.e...A$P".LOCK
d0   22 3a 49 d0 4c 31 3a b0  |  31 30 30 3a 49 d0 4c 32  |  ":IPL1:0100:IPL2
e0   3a b0 31 30 30 00 f8 08  |  28 00 ba 22 46 45 52 54  |  :0100.x.(.:"FERT
f0   49 47 2e 22 3a ba 3a ac  |  00 fe 08 63 00 80 00 22  |  IG.":::,.~.c.�."
100   09 64 00 ba 41 24 22 4c  |  45 56 22 49 3a ba 41 24  |  .d.:A$"LEV"I::A$
110   22 4f 42 4a 22 49 3a ba  |  41 24 22 44 59 4e 22 49  |  "OBJ"I::A$"DYN"I
120   3a b1 00 5b 09 6e 00 ba  |  41 24 22 4c 45 56 22 49  |  :1.[.n.:A$"LEV"I
130   22 2c 4c 45 56 22 4a 3a  |  ba 41 24 22 4f 42 4a 22  |  ",LEV"J::A$"OBJ"
140   49 22 2c 4f 42 4a 22 4a  |  3a ba 41 24 22 44 59 4e  |  I",OBJ"J::A$"DYN
150   22 49 22 2c 44 59 4e 22  |  4a 3a b1 00 00 00 4c 00  |  "I",DYN"J:1...L.
160   00 00 00 00 00 00 00 00  |  00 00 00 00 00 00 00 00  |  ................
...
1f0   00 00 00 00 00 00 00 00  |  00 00 00 00 00 00 00 00  |  ................

DONE

You can get the track sector list

Code
[MAME]> printt(gettslist(0x12,0xf))
{{0x12 18, 0xe 14}, 
{0x12 18, 0xd 13}}
or you can do textdumps:

Code
[MAME]> a=getfile("LET")
(11,0f) 00  *B 003 LETTER                         TS List=(12,0f)
matched "LET" with "LETTER                        "
BLOAD ADDR=(01,09)  0901   DATA LENGTH=(26,01) DATA LENGTH=0126
LEN ORIG:	200
LEN AFTER:	126
[MAME]> textdump(a)
00                STELLAR 7.
38   .
39   YOU SHOULDN'T BE READING THIS, BUT IF.
You can load a picture file:
Code
bload("BRIEF",0x2000)
hgr()

and a neat-o function to print tables:
Code
 printt({"why","lisa",{"you're tearing me apart","oh hi","mark"}})
{"why", 
"lisa", 
{"you're tearing me apart", "oh hi", "mark"}}

printt(manager:machine().devices)
{":a2video" = sol.device_t*: 0x5585abc0fa28, 
":a2bus" = sol.device_t*: 0x5585abc09028, 
":sl6:diskiing:0:525" = sol.device_t*: 0x5585abc08de8, 
":sl4:mockingboard:mockbd_via2" = sol.device_t*: 0x5585abc09268, 
":r40bank" = sol.device_t*: 0x5585abc0f7a8, 
...
":r20bank" = sol.device_t*: 0x5585abc0fb68}

You can bload files:

Code
[MAME]> loaddisk("serpentine_bolocheat_pestpatrol.dsk")
[MAME]> bload("PEST")
(11,0f) 00   A 003 HELLO                          TS List=(05,0f)
(11,0f) 01  *T 002 SERPENTINE*                    TS List=(15,0a)
(11,0f) 02  *B 00a SERPENTINE 00-08               TS List=(15,08)
(11,0f) 03  *B 03a SERPENTINE 08-40               TS List=(16,0e)
(11,0f) 04  *B 052 SERPENTINE 40-90               TS List=(19,04)
(11,0f) 05  *B 02a SERPENTINE 90-B8               TS List=(1e,02)
(11,0f) 06  *B 00a SERPENTINE B8-C0               TS List=(21,08)
(11,0e) 00  *B 022 BOLOSCREEN                     TS List=(05,0c)
(11,0e) 01  *B 012 BOLO                           TS List=(03,0a)
(11,0e) 02  *B 060 BOLO.BINARY                    TS List=(02,08)
(11,0e) 03   A 007 BOLO-CHEATER                   TS List=(10,0c)
(11,0e) 04  *B 08c PEST PATROL                    TS List=(07,0f)
matched "PEST" with "PEST PATROL                   "
BLOAD ADDR=(fd,0c)  0cfd   DATA LENGTH=(03,89) DATA LENGTH=8903
LEN ORIG:	8a00
LEN AFTER:	8903

and you can bload a file and execute it like a brun but you have to stop the cpu first with stop() for synchronization reasons:

Code
[MAME]> stop()
[MAME]> bload("PEST",nil,nil,true)
(11,0f) 00   A 003 HELLO                          TS List=(05,0f)
(11,0f) 01  *T 002 SERPENTINE*                    TS List=(15,0a)
(11,0f) 02  *B 00a SERPENTINE 00-08               TS List=(15,08)
(11,0f) 03  *B 03a SERPENTINE 08-40               TS List=(16,0e)
(11,0f) 04  *B 052 SERPENTINE 40-90               TS List=(19,04)
(11,0f) 05  *B 02a SERPENTINE 90-B8               TS List=(1e,02)
(11,0f) 06  *B 00a SERPENTINE B8-C0               TS List=(21,08)
(11,0e) 00  *B 022 BOLOSCREEN                     TS List=(05,0c)
(11,0e) 01  *B 012 BOLO                           TS List=(03,0a)
(11,0e) 02  *B 060 BOLO.BINARY                    TS List=(02,08)
(11,0e) 03   A 007 BOLO-CHEATER                   TS List=(10,0c)
(11,0e) 04  *B 08c PEST PATROL                    TS List=(07,0f)
matched "PEST" with "PEST PATROL                   "
BLOAD ADDR=(fd,0c)  0cfd   DATA LENGTH=(03,89) DATA LENGTH=8903
LEN ORIG:	8a00
LEN AFTER:	8903
pc=cfd
g

It's a little too large to include in a forum post so you can find it at:

https://github.com/goldnchild/goldenchilds_apple2_lua_stuff/blob/master/diskimage_file.lua

Joined: Feb 2014
Posts: 1,102
Likes: 173
G
Very Senior Member
Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,102
Likes: 173
Good idea RB to check the m_80col to see if it is in 80 column mode.

Also if you want to see how drawing by rows works, just do a

drawbyrow(true)


Code

-- 80 column text overlay for Apple IIe

function calc(line,page) page=page or 0 return line % 8 * 1024 + math.floor(line / 64) * 40 + math.floor((line%64) / 8) * 128 + 8192* (page + 1) end

function iskeypressed(keycode) 
inp = manager:machine():input() return inp:code_pressed(inp:code_from_token(keycode))
end

function striphi(a) if type(a)=="number" then a=string.char(a) end local b="" for i=1,#a do b=b..string.char(string.byte(a,i)&0x7f) end return b end

mem=manager:machine().devices[":maincpu"].spaces["program"]  -- gets main or aux, depending on softswitches
aux=emu.item(manager:machine().devices[":aux:ext80"].items["0/m_ram"])   -- gets aux memory
scr=manager:machine().screens[":screen"]
main=emu.item(manager:machine().devices[":ram"].items["0/m_pointer"])  -- always gets main memory

function widthpct(c) 
  if c==nil or c==string.char(0) then return 0 end 
  return ((manager:ui():get_string_width(c,1.0)/manager:ui():get_string_width("A",1.0))) 
end
function widthoffset(c) if c==nil or c==string.char(0) then return 0 end return 3.5*(1-widthpct(c)) end
-- assume chars have width of 7 pixels, center offset=7*((1-widthpercentage)/2)=3.5*(1-widthpercentage)

precalcwidthoffsettable={}

function precalccharwidthoffset()
  for i=0,255 do precalcwidthoffsettable[i]=widthoffset(string.char(i)) end
end

precalccharwidthoffset()

function widthoffsetpre(c)  if c==nil or c==string.char(0) then return 0 else return precalcwidthoffsettable[string.byte(c)] end end

function between(a,b,c) return (a >= b) and (a <= c) end
-- 80 column text overlay for Apple IIe

function calc(line,page) page=page or 0 return line % 8 * 1024 + math.floor(line / 64) * 40 + math.floor((line%64) / 8) * 128 + 8192* (page + 1) end

function iskeypressed(keycode) 
inp = manager:machine():input() return inp:code_pressed(inp:code_from_token(keycode))
end

function striphi(a) if type(a)=="number" then a=string.char(a) end local b="" for i=1,#a do b=b..string.char(string.byte(a,i)&0x7f) end return b end

mem=manager:machine().devices[":maincpu"].spaces["program"]  -- gets main or aux, depending on softswitches
aux=emu.item(manager:machine().devices[":aux:ext80"].items["0/m_ram"])   -- gets aux memory
scr=manager:machine().screens[":screen"]
main=emu.item(manager:machine().devices[":ram"].items["0/m_pointer"])  -- always gets main memory

function widthpct(c) 
  if c==nil or c==string.char(0) then return 0 end 
  return ((manager:ui():get_string_width(c,1.0)/manager:ui():get_string_width("A",1.0))) 
end
function widthoffset(c) if c==nil or c==string.char(0) then return 0 end return 3.5*(1-widthpct(c)) end
-- assume chars have width of 7 pixels, center offset=7*((1-widthpercentage)/2)=3.5*(1-widthpercentage)

precalcwidthoffsettable={}

function precalccharwidthoffset()
  for i=0,255 do precalcwidthoffsettable[i]=widthoffset(string.char(i)) end
end

precalccharwidthoffset()

function widthoffsetpre(c)  if c==nil or c==string.char(0) then return 0 else return precalcwidthoffsettable[string.byte(c)] end end

function between(a,b,c) return (a >= b) and (a <= c) end

function applecharfixinverse(c)
  if type(c)=="string" then c=string.byte(c) end
  if between(c,0,31) then return string.char(c+64),true
  elseif between (c,32,127) then return string.char(c), true
  elseif between (c,128,159) then return string.char(c-128+64), false
  elseif between (c,160,255) then return string.char(c-128), false
  else return 0, false
  end
end

drawoverlay=true
keydelay=0

function print_80col()
for y=0,23 do for x=0,39 do offset=calc(y*8)-8192+x 
c1=striphi(aux:read(1024+offset)) c0=striphi(mem:read_u8(1024+offset)) 
io.write(c1..c0) end print() end
end

function interleavec1c0(c1row,c0row)
local outrow=""
for i=1,#c1row do
  outrow=outrow..applecharfixinverse(c1row:sub(i,i))..applecharfixinverse(c0row:sub(i,i))
end
return outrow
end

function draw_80colrow()
  if keydelay > 0 then keydelay = keydelay - 1 end
  local m_80col=emu.item(manager:machine().devices[":a2video"].items["0/m_80col"]):read(0)==1
  if drawoverlay then 
    scr:draw_box(0,0,559,279,0xff000000,0xffffffff)
    for y=0,23 do 
      offset=calc(y*8)-8192 
      c1row=aux:read_block(1024+offset,40)
      c0row=main:read_block(1024+offset,40)
      if m_80col then
        c0row=interleavec1c0(c1row,c0row)
      else
        c0row=interleavec1c0(string.rep(" ",40),c0row)
      end
      fgcolor,bgcolor=0xffffffff,0xff000000
      scr:draw_text(0,y*8,c0row,fgcolor,bgcolor) 
    end
  end
  if iskeypressed("KEYCODE_LSHIFT") and keydelay==0 then drawoverlay=not drawoverlay keydelay=20 end
end

m_80col_itemnum=-1

function draw_80col()
  if keydelay > 0 then keydelay = keydelay - 1 end
  -- might be a smidge faster if we didn't have to look up the itemnum for m_80col each time
  if m_80col_itemnum < 0 then m_80col_itemnum = manager:machine().devices[":a2video"].items["0/m_80col"] end
  local m_80col=emu.item(m_80col_itemnum):read(0)==1
  if drawoverlay then 
    scr:draw_box(0,0,559,279,0xff000000,0xffffffff)
    for y=0,23 do 
      offset=calc(y*8)-8192 
      c1row=aux:read_block(1024+offset,40)
      c0row=main:read_block(1024+offset,40)
      for x=0,39 do 
        c1=c1row:byte(x+1)
        c1,c1invert=applecharfixinverse(c1)
        c0=c0row:byte(x+1)
        c0,c0invert=applecharfixinverse(c0)
        fgcolor,bgcolor=0xffffffff,0xff000000
        if c1invert then fgcolor,bgcolor=bgcolor,fgcolor end
        --only draw the 80 columns if 80col is active
        if m_80col then
          scr:draw_text((x*2+0)*7+widthoffsetpre(c1),y*8,c1,fgcolor,bgcolor) 
        end
        fgcolor,bgcolor=0xffffffff,0xff000000
        if c0invert then fgcolor,bgcolor=bgcolor,fgcolor end
        scr:draw_text((x*2+1)*7+widthoffsetpre(c0),y*8,c0,fgcolor,bgcolor) 
      end 
    end
  end
  if iskeypressed("KEYCODE_LSHIFT") and keydelay==0 then drawoverlay=not drawoverlay keydelay=20 end
end

draw80dispatchlist={draw_80col}

function draw80dispatch() for i,j in pairs(draw80dispatchlist) do j() end end

function drawon() draw80dispatchlist={draw_80col} end
function drawoff() draw80dispatchlist={} end

-- Draw by rows by calling drawbyrow(true) or shut off with drawbyrow(false)
-- You end up losing the inverse chars and your ui font should be a monospace font
--  or it won't look right.  Also the fonts end up getting cropped at the right edge of the screen.
-- It's much faster since it can write out a whole 80 character row in one draw_text call
--  instead of 80 separate draw_text calls for each individual character.

function drawbyrow(a)
  if a==nil then a=true end
  if a==true then draw80dispatchlist={draw_80colrow} 
             else draw80dispatchlist={draw_80col}
  end
end

if not alreadyregistereddraw80 then
  emu.register_frame_done(draw80dispatch)
  alreadyregistereddraw80=true
end



Last edited by Golden Child; 03/04/19 09:27 PM. Reason: oops pasted some double text
Joined: Jan 2012
Posts: 1,179
Likes: 17
rfka01 Offline OP
Very Senior Member
OP Offline
Very Senior Member
Joined: Jan 2012
Posts: 1,179
Likes: 17
The "remote control" for the disk images is a great idea ... I might have to learn how to use the LUA console one day after all smile


NCR DMV- DEC Rainbow- Siemens PCD- ITT 3030-Oly People- Acorn A5000- Olivetti M20
Joined: Feb 2014
Posts: 1,102
Likes: 173
G
Very Senior Member
Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,102
Likes: 173
More silly fun: I always loved the different fonts and wanted to use the Beagle Bros fonts from Apple Mechanic which are shape tables. So I wrote a shape table interpreter. It's not fully complete in that it doesn't do scale and rot, but it can write the text to the screen.

Code

function iif(a,b,c) if a then return b else return c end end 
function bool10(a) return iif(a,1,0) end

function bin(x,numbits,gap) if x==nil then return nil end gap=gap or "_" numbits=numbits or 8 str="" for i=numbits-1,0,-1 do str=str..iif((x&(2^i))~=0,"1","0")..iif((i%8==0) and (i>0),gap,"") end return str end

function hex(a,digits,prefix) digits=digits or 2 prefix=prefix or "" if a==nil then return nil end return string.format(prefix.."%0"..digits.."x",a) end
function hexx(a,digits,prefix) prefix=prefix or "0x" return hex(a,digits,"0x") end
function hexpair(a,b) return "("..hex(a)..","..hex(b)..")" end

function max(a,b) if a>b then return a else return b end end 
function min(a,b) if a<b then return a else return b end end
function round(x) if x<0 then return math.floor(x-0.5) else return math.floor (x+0.5) end end

function calc(line,page) if line<0 then line = 0 end if line > 191 then line=191 end page=page or 0 return line % 8 * 1024 + math.floor(line / 64) * 40 + math.floor((line%64) / 8) * 128 + 8192* (page + 1) end

function calcyaddr(line,page) return calc(line,page) end

function plot(x,y,page) page=page or 0 x = max(0,x) x = min(x,279) y = max(0,y) y = min(y,191) mem=applemem() mem:write_u8(calcyaddr(y,page)+math.floor(x/7),mem:read_u8(calcyaddr(y,page)+math.floor(x/7))|(1<<(x%7))) end

function applemem() return manager:machine().devices[":maincpu"].spaces["program"] end

function getbits(a,lobit,hibit) local retval=0 for bit=lobit,hibit do retval=retval|((1<<bit)&a) end return retval end

function getbitsshift(a,lobit,hibit) return getbits(a,lobit,hibit)>>lobit end

stuck=nil
stuckmax=5000

function stuckcheck(stucklimit)
if stuck==nil then stuck =0 end
stuck=stuck+1
stucklimit=stucklimit or stuckmax
if stuck > stucklimit then print("STUCK hit loop "..stucklimit.."times") stuck=nil gonnamakeanerror() end
end

function getdrawcmd(a,cmdindex) return getbitsshift(a,cmdindex*3,cmdindex*3+2) end

function drawshapetable(a,shapenum,xpos,ypos)
local bytepos=2*shapenum+1
if printdebugshapetable then print("drawshapetable shapenum="..shapenum) end
if a:byte(1) < shapenum then 
   if  printdebugshapetable then print ("only have "..a:byte(1).." shapes!  shapenum="..shapenum) end 
   return xpos,ypos end
if printdebugshapetable then print(string.rep("-",80)) end
bytepos=a:byte(bytepos)+a:byte(bytepos+1)*256+1
stuck=nil
while true do
stuckcheck()  -- stop those pesky infinite loops while debugging things, an infinite loop will hang mame
local b=a:byte(bytepos)
local drawcmd={}
for i=0,2 do
  if b==0 or b==nil then return xpos,ypos end
  drawcmd[i+1]=getdrawcmd(b,i)
end
for i=0,2 do
  local dirbits=getbitsshift(drawcmd[i+1],0,1)
  local plotbit=getbitsshift(drawcmd[i+1],2,2)
  dirtable={{x=0,y=-1,dir="up"},{x=1,y=0,dir="right"},{x=0,y=1,dir="down"},{x=-1,y=0,dir="left"}}
  x,y,dir=dirtable[dirbits+1].x,dirtable[dirbits+1].y,dirtable[dirbits+1].dir
  local skip=false
  if i==1 and drawcmd[2]==0 and drawcmd[3]==0 then skip=true end -- if second and third are zero, ignore
  if i==2 and dirbits==0 then skip=true end -- 00 in last command (can't go up in final command)
  if printdebugshapetable then  print("bytepos="..hex(bytepos),"byte="..hex(b),"binary="..bin(b),i+1,bin(drawcmd[i+1],3),"PLOT="..plotbit,"DIR="..dirbits.."  "..dir.." "..iif(skip,"<SKIP>","")..iif(plotbit==1," plot","")) end
  if skip then break end
  newxpos,newypos=xpos+x,ypos+y
  if plotbit == 1 then plot(xpos,ypos) end --plot before moving
  xpos,ypos=newxpos,newypos
end
if printdebugshapetable then print(string.rep("=",80)) end
bytepos=bytepos+1
end -- while
end -- drawshapetable

-- we can just draw the character and ignore the return ypos if we don't want to draw shape #99 between characters

function drawshapetext(a,thisstr,x,y,spacing,xclip,height) for i=1,#thisstr do if thisstr:byte(i)==string.byte("\n") then if x~=0 then x,y=0,y+height end else x=drawshapetable(a,thisstr:byte(i)-32+1,x,y) x=x+spacing if x>xclip then x,y=0,y+height end end end return x,y end

-- Beagle Bros shape table fonts, draw shape #99 between characters to return to baseline and add space

function drawshapetext99(a,thisstr,x,y,spacing,xclip,height) for i=1,#thisstr do if thisstr:byte(i)==string.byte("\n") then if x ~= 0 then x,y=0,y+height end else x,y=drawshapetable(a,thisstr:byte(i)-32+1,x,y) x,y=drawshapetable(a,99,x,y) if x>xclip then x,y=0,y+height end end end return x,y end



printdebugshapetable = true

It's kinda neat to see how the shapes were put together:
Code
drawshapetable shapenum=99
--------------------------------------------------------------------------------
bytepos=10a5	byte=40	binary=01000000	1	000	PLOT=0	DIR=0  up 
bytepos=10a5	byte=40	binary=01000000	2	000	PLOT=0	DIR=0  up 
bytepos=10a5	byte=40	binary=01000000	3	001	PLOT=0	DIR=1  right 
================================================================================
...
bytepos=10ac	byte=08	binary=00001000	1	000	PLOT=0	DIR=0  up 
bytepos=10ac	byte=08	binary=00001000	2	001	PLOT=0	DIR=1  right 
bytepos=10ac	byte=08	binary=00001000	3	000	PLOT=0	DIR=0  up <SKIP>
================================================================================

[Linked Image from i.imgur.com]

Code
loaddisk("../../apple_mechanic_typefaces.dsk") hgr() hgrfull() hgrclr() a=getfile("]COMPUTE") drawshapetext99(a,"I'm gonna download and compile mame! ...and compile mame!\nI'm going to live like modern hardware doesn't exist! ...like it doesn't exist!\nI'm gonna fly like a 6502 in the night!\nFeel the artifacts on my eyes",0,0,0,260,18)

[Linked Image from i.imgur.com]

Joined: Feb 2014
Posts: 1,102
Likes: 173
G
Very Senior Member
Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,102
Likes: 173
I was always jealous of other computers where you could redefine the fonts, so why not see if I can create a custom font rom for the apple 2.

There's an article on reactivemicro where they mention some "custom" character sets like Pigfont and ReActiveText: https://wiki.reactivemicro.com/Apple_IIe_Enhancement_Kit

So let's see what's in the Apple 2 character roms, we can just load them into the hi-res screen.

The apple 2 char rom is "reversed".

No lowercase characters here!


[Linked Image from i.imgur.com]

So let's reverse it and shift it a little bit, that looks better.

[Linked Image from i.imgur.com]

and look at the 2e rom:

[Linked Image from i.imgur.com]

and the 2c rom: (hey look, it's the running man!)

[Linked Image from i.imgur.com]

It's kind of cool to see the bit patterns that make up the lo-res colors. I think I'm finally understanding how the 560 pixel double hi-res works, it's just the same bit patterns that make up the lo-res colors.

and what if we look at some non-native character sets loaded into Apple hi-res:

(I put the apple2 configuration into black and white mode)

Colecovision:

[Linked Image from i.imgur.com]

Atari 800:
[Linked Image from i.imgur.com]

C64:
[Linked Image from i.imgur.com]

The coleco adam, pet2001 and the cpc300 char roms look pretty good too.

Code
stuck=nil
stuckmax=5000
function stuckcheck(stucklimit)
if stuck==nil then stuck =0 end
stuck=stuck+1
stucklimit=stucklimit or stuckmax
if stuck > stucklimit then print("STUCK hit loop "..stucklimit.."times") stuck=nil error("I may be STUCK in an infinite loop.  I've fallen and I can't get up") end
end

function loadfile(filename) if filename==nil then print("NO FILENAME") return nil end local f=assert(io.open(filename,"r")) local a=f:read("*a") f:close() if a==nil then print("NO DATA") else print("Loadfile (\""..filename.."\") = "..#a.." bytes read.") end return a end

function revbits(a) local r=0 for i=0,7 do r=(r<<1)+a%2 a=a>>1 end return r end

function drawblockloop(looplist,dofunc,xwrap)
--reset stuck counter
stuck=nil
looplist=looplist or {"y",8,"x",1}
xwrap = xwrap or 40  -- wrap the x at 40 bytes across, since apple screen is 40 bytes across
-- from left to right, outer --> inner
-- need to track value of each loop index and what type is it, an "x" loop or a "y" loop
-- iterate from inner out, so iterate in reverse order for i=looplevelcount,1,-1 
-- xsize (of all inners multiplied) * currentlevel value
-- and same goes for ycoordinate
-- so initialize all levels of our loop
-- and start from innermost level
-- add 1, if greater than loop size, then revert to zero and propagate the add to the next outer level
-- go up a level, add 1, if greater than bump up to next, rinse, repeat
-- if all levels done then exit
loopstate={}
looplevelcount=0
for i = 1,#looplist,2 do
  looplevelcount=looplevelcount+1
  loopstate[looplevelcount]={}
  loopstate[looplevelcount].axis=looplist[i]
  loopstate[looplevelcount].size=looplist[i+1]
  loopstate[looplevelcount].value=0
end
print(looplevelcount)
printt(loopstate)
print("Size of block should be ")
blocksize=1
for i=looplevelcount,1,-1 do
   blocksize=blocksize*loopstate[i].size 
   io.write(loopstate[i].size.." * ")
end
print("1 = "..blocksize)
index=0
while true do
stuckcheck()
-- now compute the x value
local coord=0
blocksize=1
for i=looplevelcount,1,-1 do
  if loopstate[i].axis=="x" then coord=coord+blocksize*loopstate[i].value
                                 blocksize=blocksize*loopstate[i].size 
end
end
xcoord=coord
coord=0
blocksize=1
for i=looplevelcount,1,-1 do
  if loopstate[i].axis=="y" then coord=coord+blocksize*loopstate[i].value blocksize=blocksize*loopstate[i].size 
end
end
ycoord=coord
xfinal=xcoord%xwrap
yfinal=ycoord+blocksize*math.floor(xcoord/xwrap)
dofunc(xfinal,yfinal,index)
index=index+1
-- now add 1 to inner levels
for i=looplevelcount,1,-1 do
  loopstate[i].value = loopstate[i].value + 1
  if loopstate[i].value <= loopstate[i].size-1 then break end
  loopstate[i].value = 0  -- set to value and propagate up to next level
  if i==1 then return end-- we need to stop everything since we are done
end
end
end --drawblockloop

function applemem() return manager:machine().devices[":maincpu"].spaces["program"] end

function revstring(a) local b="" for i=1,#a do b=b..string.char(revbits(a:byte(i))) end return b end
function shiftstringleft(a,numbits) local b="" for i=1,#a do b=b..string.char((a:byte(i)<<numbits)&0xff) end return b end
function andstring(a,andvalue) local b="" for i=1,#a do b=b..string.char(a:byte(i)&andvalue) end return b end
function invertstring(a) local b="" for i=1,#a do b=b..string.char(255-a:byte(i)) end return b end

Code

-- to draw the coleco font, uses a loadstring(executestr)() to execute some text code

hgr() hgrfull() hgrclr() 
filename="../../CHARSETS/coleco/r72114a_8317.u2" 
a=loadfile(filename) 
executestr="a=revstring(a) a=shiftstringleft(a,0) a=andstring(a,0x7f)" 
loadstring(executestr)()
stuckmax=16384 
startfrom=4096+4+152*8
drawblockloop({"y",16,"x",32,"y",8},function(x,y,index) applemem():write_u8(calc(y)+x,(a:sub(startfrom)):byte(index+1)) end,32) 
drawshapetext(font,filename,0,140,1,270,8) drawshapetext(font,executestr.." a:sub("..startfrom..")",0,150,1,270,10)


And one interesting discovery, though completely trivial: In studying the colecovision font, there's an error in the lowercase x, it doesn't look right.


[Linked Image from i.imgur.com]

and going to the tms9918 manual pdf, there's a picture and hex, but clearly the hex doesn't match the picture.

It should be 00 88 50 20 50 88 00.

[Linked Image from i.imgur.com]

[Linked Image from i.imgur.com]


Joined: Feb 2014
Posts: 1,102
Likes: 173
G
Very Senior Member
Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,102
Likes: 173
I found another program that will render shape table fonts which is Executive Briefing System. It's actually pretty clever in that it "hides" the character width information in the shape table itself. The first two bytes I think are the height and the baseline, then it's a normal shape table, so we can just go a:sub(3) to skip past the first two characters of the a string.

The shape table has a byte to tell you the number of shapes, and the unused next byte is used to point to the offset of the width information.

[Linked Image from i.imgur.com][Linked Image from i.imgur.com]

According to the applesoft disassembly at http://www.txbobsc.com/scsc/scdocumentor/F5BA.html, F605- AA 1650 DRAW1 is the entry point, so putting a breakpoint at F605 told me that it was surely an applesoft shape table.



And now we can generate font samples automatically. Just get a filelist, iterate through the filelist, draw a sample, then wait 2/60 of a second and ask the debugger to take a snapshot.

Since we've got to use emu.wait this must be done with coroutines.

Code

function getebswidth(a,shapenum) return a:sub(1):byte(a:sub(1):byte(2)+shapenum+1) end

function drawshapetextebs(a,thisstr,x,y,spacing,xclip,height) for i=1,#thisstr do if thisstr:byte(i)==string.byte("\n") then if x~=0 then x,y=0,y+height end else
local c=thisstr:byte(i)  if c>=32 and c<=95 then c=c elseif c<32 then c=32 elseif c>=97 and c<=127 then c=c-97+1 else c=32 end 
 drawshapetable(a,c,x,y) x=x+getebswidth(a,c) if x>xclip then x,y=0,y+height end end end return x,y end

function getfilenamepart(s)
local curpos=1
repeat
local newpos=s:find("/",curpos,true)
if newpos then curpos = newpos+1 end  -- if you don't add the +1, keeps getting same match over and over for an infinite loop
until newpos == nil
return s:sub(curpos)
end

function trim2(s) return s:match "^%s*(.-)%s*$" end  -- from lua users wiki

disknamelist={""../../EXEC_BRIEF_SYS_hr.dsk","../../EXEC_BRIEF_SYS_FONTS_hr.dsk")

loaddisk("../../EXEC_BRIEF_SYS_hr.dsk") monospacefont=getfile("MONOSPACE.FONT")

co1=coroutine.create( function()
disknamelist={"../../EXEC_BRIEF_SYS_hr.dsk","../../EXEC_BRIEF_SYS_FONTS_hr.dsk"}
for d=1,#disknamelist do diskname=disknamelist[d] loaddisk(diskname)
filelist=cat()
for i=1,#filelist do print(filelist[i]) 
  if filelist[i]:find(".FONT",1,true) then 
  print("IT'S A FONT so let's render a sample!") 
  a=getfile(filelist[i])
  hgr() hgrfull() hgrclr() xpos=0 ypos=10 for i=1,95 do drawshapetable(a:sub(3),i,xpos,ypos) xpos=xpos+a:sub(3):byte(a:sub(3):byte(2)+i+1) print("width="..a:sub(3):byte(0xc2+i-1))  if xpos>250 or (((i+1) % 16) == 0) then xpos=0 ypos=ypos+20 end end
  drawshapetextebs(monospacefont:sub(3),getfilenamepart(diskname).."\n"..filelist[i],0,170,1,250,10)
  emu.wait(1/60+.01) 
  manager:machine():debugger():command("snap \"" .. sanefilename(getfilenamepart(diskname).."_"..trim2(filelist[i]))..".png\"")
end end end end) 
ok,error=coroutine.resume(co1)
print(ok,error)



[Linked Image from i.imgur.com][Linked Image from i.imgur.com]
[Linked Image from i.imgur.com]


coming soon: drawing Print Shop and Fontrix fonts

Page 29 of 80 1 2 27 28 29 30 31 79 80

Link Copied to Clipboard
Who's Online Now
3 members (Olivier Galibert, 2 invisible), 380 guests, and 5 robots.
Key: Admin, Global Mod, Mod
ShoutChat
Comment Guidelines: Do post respectful and insightful comments. Don't flame, hate, spam.
Forum Statistics
Forums9
Topics9,320
Posts121,944
Members5,074
Most Online1,283
Dec 21st, 2022
Our Sponsor
These forums are sponsored by Superior Solitaire, an ad-free card game collection for macOS and iOS. Download it today!

Superior Solitaire
Forum hosted by www.retrogamesformac.com