|
Joined: Mar 2001
Posts: 17,217 Likes: 234
Very Senior Member
|
Very Senior Member
Joined: Mar 2001
Posts: 17,217 Likes: 234 |
That's really cool! Apple II text display in modern fonts 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
Very Senior Member
|
Very Senior Member
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.
-- 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
Very Senior Member
|
Very Senior Member
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
|
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
Very Senior Member
|
Very Senior Member
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 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:
[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
[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 [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
[MAME]> printt(gettslist(0x12,0xf))
{{0x12 18, 0xe 14},
{0x12 18, 0xd 13}}
or you can do textdumps: [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:
bload("BRIEF",0x2000)
hgr() and a neat-o function to print tables:
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: [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: [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
Very Senior Member
|
Very Senior Member
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)
-- 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
Very Senior Member
|
OP
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
NCR DMV- DEC Rainbow- Siemens PCD- ITT 3030-Oly People- Acorn A5000- Olivetti M20
|
|
|
|
Joined: Feb 2014
Posts: 1,102 Likes: 173
Very Senior Member
|
Very Senior Member
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.
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:
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>
================================================================================
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)
|
|
|
|
Joined: Feb 2014
Posts: 1,102 Likes: 173
Very Senior Member
|
Very Senior Member
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_KitSo 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! So let's reverse it and shift it a little bit, that looks better. and look at the 2e rom: and the 2c rom: (hey look, it's the running man!) 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: Atari 800: C64: The coleco adam, pet2001 and the cpc300 char roms look pretty good too.
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
-- 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. 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.
|
|
|
|
Joined: Feb 2014
Posts: 1,102 Likes: 173
Very Senior Member
|
Very Senior Member
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. 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.
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)
coming soon: drawing Print Shop and Fontrix fonts
|
|
|
3 members (Olivier Galibert, 2 invisible),
380
guests, and
5
robots. |
Key:
Admin,
Global Mod,
Mod
|
|
Forums9
Topics9,320
Posts121,944
Members5,074
|
Most Online1,283 Dec 21st, 2022
|
|
These forums are sponsored by Superior Solitaire, an ad-free card game collection for macOS and iOS. Download it today!
|
|
|
|