I added a memory lookup as well using mem:read_u8() and noticed that it doesn't account for which hi-res page is displayed, so let's look that up in an item called ":a2video/0/m_page2".
Code
function rem(a) print(a) end
function remp(a,b,c,d) print(a,b,c,d) end
function rem(a) end
function float(f) return string.format("%6.2f",f) end
function hex(a) return string.format("0x%02x",a) end
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 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 myframedispatcher() if myframedispatchlist ~= nil then for i,j in pairs(myframedispatchlist) do j() end end end
function get_page() return emu.item(manager:machine().devices[":a2video"].items["0/m_page2"]):read(0) end
function draw_crosshairs()
x,y,button,target=manager:machine():uiinput():find_mouse()
if target==nil then print ("TARGET=nil") return end
manager:machine().screens[":screen"]:draw_line(0,0,x/target:width()*559,y/target:height()*191,0xffffffff)
boxsize=4
manager:machine().screens[":screen"]:draw_box(x/target:width()*559-boxsize*2,y/target:height()*191-boxsize,x/target:width()*559+boxsize*2,y/target:height()*191+boxsize,0x22ffffff,0xffff0000)
xsize=target:width()
ysize=target:height()
textx=math.min(math.floor(x/xsize*560+10),400)
texty=math.min(math.floor(y/ysize*192+10),160)
cpu = manager:machine().devices[":maincpu"];mem = cpu.spaces["program"]
page=get_page()
screenaddr=calc(math.floor(y/ysize*192),get_page())+math.floor(x/xsize*279/7)
screenvalue=mem:read_u8(screenaddr)
manager:machine().screens[":screen"]:draw_text(textx,texty,"x="..float(x/xsize*280).." y="..float(y/ysize*192).."\naddr="..hex(screenaddr).."\nvideo page= "..page.."\nvalue="..hex(screenvalue).." "..bin(screenvalue),0xffffffff,0xffff0000)
if button then
myframedispatchlist={}
end
end
myframedispatchlist={} table.insert(myframedispatchlist,draw_crosshairs)
if alreadyregisteredmyframedispatcher==nil then
emu.register_frame_done(myframedispatcher)
alreadyregisteredmyframedispatcher=true
end
function clrd() myframedispatchlist={} end
And you can flip the hi-res pages with the following command in the debugger: b!c054=1 (for page 1) b!c055=1 (for page 2)
or if you're adventurous: emu.item(manager:machine().devices[":a2video"].items["0/m_page2"]):write(0,0) for page 1 emu.item(manager:machine().devices[":a2video"].items["0/m_page2"]):write(0,1) for page 2
So if I hover over the character A in the opening menu screen and work downward, I find that the bytes are 7c,44,44,7c,46,46,46 and if I do a search with "find 0,c000,7c,44,44,7c,46,46,46" it tells that it's found at 7A08, and further study finds the text font starting at 0x7900.
I've added cross hairs that will show the byte boundaries and the line boundaries, and if you press the 1 or 2 keys you can switch hi-res pages.
Code
function rem(a) print(a) end
function remp(a,b,c,d) print(a,b,c,d) end
function rem(a) end
function float(f) return string.format("%6.2f",f) end
function hex(a) return string.format("0x%02x",a) end
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 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 myframedispatcher() if myframedispatchlist ~= nil then for i,j in pairs(myframedispatchlist) do j() end end end
function get_page()
page = emu.item(manager:machine().devices[":a2video"].items["0/m_page2"]):read(0)
return page
end
function iskeypressed(keycode)
inp = manager:machine():input() return inp:code_pressed(inp:code_from_token(keycode))
end
function draw_crosshairs()
x,y,button,target=manager:machine():uiinput():find_mouse()
if target==nil then print ("TARGET=nil") return end
manager:machine().screens[":screen"]:draw_line(0,0,x/target:width()*559,y/target:height()*191,0xffffffff)
boxsize=4
manager:machine().screens[":screen"]:draw_box(x/target:width()*559-boxsize*2,y/target:height()*191-boxsize,x/target:width()*559+boxsize*2,y/target:height()*191+boxsize,0x22ffffff,0xffff0000)
xsize=target:width()
ysize=target:height()
textx=math.min(math.floor(x/xsize*560+10),400)
texty=math.min(math.floor(y/ysize*192+10),160)
if texty>159 then texty=texty-40 end
x1=math.floor(x/xsize*279/7)*7*2
x2=(math.floor(x/xsize*279/7)+1)*7*2
y1=math.floor(y/ysize*191)
y2=math.floor(y/ysize*191)+1
manager:machine().screens[":screen"]:draw_box(x1,0,x2,y/target:height()*191,0x44ffff00,0xffff0000)
manager:machine().screens[":screen"]:draw_box(0,y1,x/xsize*559,y2,0x44ffff00,0xffff0000)
cpu = manager:machine().devices[":maincpu"];mem = cpu.spaces["program"]
page=get_page()
screenaddr=calc(math.floor(y/ysize*191),get_page())+math.floor(x/xsize*279/7)
screenvalue=mem:read_u8(screenaddr)
manager:machine().screens[":screen"]:draw_text(textx,texty,"x="..float(x/xsize*280).." y="..float(y/ysize*192).."\naddr="..hex(screenaddr).."\nvideo page= "..page.."\nvalue="..hex(screenvalue).." "..bin(screenvalue),0xffffffff,0xffff0000)
if button then
myframedispatchlist={}
end
if iskeypressed("KEYCODE_1") then emu.item(manager:machine().devices[":a2video"].items["0/m_page2"]):write(0,0)
elseif iskeypressed("KEYCODE_2") then emu.item(manager:machine().devices[":a2video"].items["0/m_page2"]):write(0,1)
end
end
myframedispatchlist={} table.insert(myframedispatchlist,draw_crosshairs)
if alreadyregisteredmyframedispatcher==nil then
emu.register_frame_done(myframedispatcher)
alreadyregisteredmyframedispatcher=true
end
function clrd() myframedispatchlist={} end
R.Belmont, thanks for explaining, makes sense now why it wasn't working the way I expected. I did convert my sysutils1.3 dsk to po, and it also did not work, so had me confused a bit.
Looks like there are a couple of other A3 SOS boot sectors that it might be good to add in support for. One looks like an older Apple one, and one is used on the BOS operating system disks written by Bob Consorti. This second one looks like it supports the 512k ram option. Here is a link to the disk images for these:
That disk image that did not work was just my attempt at putting atomic-defense back together from my disassembled source. The boot sector for it uses the block io inbuilt into the apple3 firmware, so it was easier to just use a prodos ordered disk to just add the code parts together to make an image. I don't think there is much point adding this one in at the moment, i'll just convert to dsk and work with that for now.
And still on Atomic-defense, I worked out why the text screen disappears during the game play on some machines. The Alpha Lock key is checked by the code! If the key is down, then the text displaying the score is kept at the top. If the key is released, then the code times 240 VBL periods (4 seconds) and then removes the text from the top. It Mame it works ok once you toggle it in the game, although Mame does not seem to carry in the current state of the capslock key into the emulator on startup.
I thought I'd try and see if I could create a memory viewer that writes into the hi-res graphics page #1.
It's kind of cool, you can see that Stellar 7's menus will "fight" over drawing into the hi-res page.
There's also a really neat effect where if you scroll into hi-res page #1 memory range that the bytes will shimmer and disappear.
Code
function float(f) return string.format("%6.2f",f) end
function hex(a) return string.format("0x%02x",a) end
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 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 myframedispatcher() if myframedispatchlist ~= nil then for i,j in pairs(myframedispatchlist) do j() end end end
function between(a,b,c) return (a >= b) and (a <= c) end
function memtovideo(start,stride)
cpu = manager:machine().devices[":maincpu"];mem = cpu.spaces["program"]
screenwidth=40
mempos=start
for i=0,screenwidth+stride,stride do
for j=0,191 do
for k=0,stride-1 do
xpos=i+k
ypos=j
if between(xpos,0,screenwidth-1) and between(ypos,0,191) then
memaddress=calc(ypos)+xpos
if between(mempos,0,0xc000) then valuetowrite=mem:read_u8(mempos)
else valuetowrite = iif(mempos%1==0,0xaa,0x55)
end
-- iif evaluates all operands so this won't work since it does the mem:read_u8
-- valuetowrite=iif(between(mempos,0,0xc000),mem:read_u8(mempos),iif(mempos%1==0,0xaa,0x55))
mem:write_u8(memaddress,valuetowrite)
end
mempos=mempos+1
end
end
end
end
function iskeypressed(keycode)
inp = manager:machine():input() return inp:code_pressed(inp:code_from_token(keycode))
end
mempos0=0
memorystep=192
blockwidth=1
framecounter=0
keydelay=0
function memviewer()
if keydelay > 0 then keydelay=keydelay-1 end
framecounter=framecounter+1
if framecounter % 5 == 0 then
framecounter=0
memtovideo(mempos0,blockwidth)
if iskeypressed("KEYCODE_1") then
mempos0=mempos0 - (iif(iskeypressed("KEYCODE_LSHIFT"),8*blockwidth,iif(iskeypressed("KEYCODE_LCONTROL"),1,memorystep*blockwidth)))
mempos0=math.max(0,mempos0)
elseif iskeypressed("KEYCODE_2") then
mempos0=mempos0 + (iif(iskeypressed("KEYCODE_LSHIFT"),8*blockwidth,iif(iskeypressed("KEYCODE_LCONTROL"),1,memorystep*blockwidth)))
mempos0=math.min(0xc0000,mempos0)
elseif iskeypressed("KEYCODE_BACKSPACE") then myframedispatchlist={}
elseif iskeypressed("KEYCODE_3") and keydelay==0 then blockwidth=blockwidth-1 blockwidth=math.max(blockwidth,1) keydelay=20
elseif iskeypressed("KEYCODE_4") and keydelay==0 then blockwidth=blockwidth+1 blockwidth=math.min(blockwidth,128) keydelay=20
end
end
manager:machine().screens[":screen"]:draw_text(350,0,"Address="..hex(mempos0).." Blockwidth="..blockwidth.."\n\nPress 1,2 = move by column\nshift+1,2 = move by 8 * blocksize\nctrl+1,2 = move by 1\n3,4 = increase/decrease blockwidth\nBackspace = quit",0xffffffff,0xffff0000)
end
myframedispatchlist={} table.insert(myframedispatchlist,memviewer)
if alreadyregisteredmyframedispatcher==nil then
emu.register_frame_done(myframedispatcher)
alreadyregisteredmyframedispatcher=true
end
function clrd() myframedispatchlist={} end
but for some reason, when I read it, it doesn't seem to change it right away.
So let's see what happens if we set a value, and read it right back:
function portsetvalue(x) manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"]:set_value(x) print(manager:machine():ioport().ports[":a2_config"]:read()) end
function portwrite(x,mask) manager:machine():ioport().ports[":a2_config"]:write(x,mask) print(manager:machine():ioport().ports[":a2_config"]:read()) end
and what's weird is that it the port's read() value seems to lag where it changes to a value that I've set awhile back.
I can see the monitor type change at different points but it seems very disconnected from what I'm trying to write to the port. It seems to be sensitive to actually sending a different number to set_value.
Edit: I wrote a little function that will call setvalue(0) and setvalue(1) and have an emu:wait() in between.
What's strange is that it seems to cycle the values for the monitor type 0,1,2,3 and you can see the monitor type change, cycling through (color,b&w,green,amber) in the apple2 display which doesn't make sense to me.
Code
function co1()
for i=1,20 do
print("cycle="..i)
manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"]:set_value(0)
print("set_value(0)")
print(":a2_config="..manager:machine():ioport().ports[":a2_config"]:read())
emu.wait(60/60)
manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"]:set_value(1)
print("set_value(1)")
print(":a2_config="..manager:machine():ioport().ports[":a2_config"]:read())
emu.wait(30/60)
end
end
co1co = coroutine.create(co1)
coroutine.resume(co1co)
I think that the ioport values update every frame so if I wait a little bit, and set_value again, I could get it to cycle on purpose.
Code
function portsetvalue(x)
print ("set_value="..x)
manager:machine():ioport().ports[":a2_config"].fields["Composite monitor type"]:set_value(x) print(manager:machine():ioport().ports[":a2_config"]:read())
end
function keepcyclingport(wanted)
print(wanted)
for i=0,30 do
if (manager:machine():ioport().ports[":a2_config"]:read() & 0x03) ~= wanted then
portsetvalue(0)
emu.wait(2/60)
portsetvalue(1)
emu.wait(2/60)
else
return
end end end
keepco =coroutine.create(keepcyclingport) coroutine.resume(keepco,2)
keepco=coroutine.create(keepcyclingport) coroutine.resume(keepco,0)
So I was perusing miner 2049er main memory looking for where it draws the words PLAYER 1 and PLAYER2 but I couldn't find it. I finally figured out it was loading the screens from disk.
How to view them? Why just rewrite my memory viewer to load the data from a .dsk file instead. Just read a .dsk file into a lua string and use that as the data source.
I just let the miner 2049er program crash, since it loads some of its code into the first hi-res page and then it won't be competing for the hi-res page 0. I set up the "9" key to re initialize graphics mode.
It's neat to scroll through the disk stepping by blocks of 256 bytes, when it comes across a hi-res screen it's like it's "tuning" into view like a television.
Code
function rem(a) print(a) end
function remp(a,b,c,d) print(a,b,c,d) end
function rem(a) end
function float(f) return string.format("%6.2f",f) end
function hex(a) return string.format("0x%02x",a) end
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 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 myframedispatcher() if myframedispatchlist ~= nil then for i,j in pairs(myframedispatchlist) do j() end end end
function between(a,b,c) return (a >= b) and (a <= c) end
disksize=143360
function disktovideointerleave(start)
if mydisk==nil then return end
cpu = manager:machine().devices[":maincpu"];mem = cpu.spaces["program"]
for i=0,screenwidth do
for j=0,191 do
xpos = i
ypos = j
if between(xpos,0,screenwidth-1) and between(ypos,0,191) then
memaddress=calc(ypos)+xpos
mempos=start+(memaddress-8192)
if between(mempos,0,disksize-1) then valuetowrite=mydisk:byte(mempos+1)
else valuetowrite = iif(mempos%1==0,0xaa,0x55)
end
mem:write_u8(memaddress,valuetowrite)
end
end
end
end
function disktovideo(start,stride)
if mydisk==nil then return end
cpu = manager:machine().devices[":maincpu"];mem = cpu.spaces["program"]
screenwidth=40
mempos=start
for i=0,screenwidth+stride,stride do
for j=0,191 do
for k=0,stride-1 do
xpos=i+k
ypos=j
if between(xpos,0,screenwidth-1) and between(ypos,0,191) then
memaddress=calc(ypos)+xpos
if between(mempos,0,disksize-1) then valuetowrite=mydisk:byte(mempos+1)
else valuetowrite = iif(mempos%1==0,0xaa,0x55)
end
mem:write_u8(memaddress,valuetowrite)
end
mempos=mempos+1
end
end
end
end
function setgfx()
cpu = manager:machine().devices[":maincpu"];mem = cpu.spaces["program"]
dummy=mem:read_u8(0xc050) -- display graphics
dummy=mem:read_u8(0xc052) -- full screen
dummy=mem:read_u8(0xc054) -- page 1
dummy=mem:read_u8(0xc057) -- hi res
end
function iskeypressed(keycode)
inp = manager:machine():input() return inp:code_pressed(inp:code_from_token(keycode))
end
mempos0=0
memorystep=192
blockwidth=1
framecounter=0
keydelay=0
interleavemode=0
function diskviewer()
if keydelay > 0 then keydelay=keydelay-1 end
framecounter=framecounter+1
if framecounter % 5 == 0 then
framecounter=0
if interleavemode == 0 then disktovideo(mempos0,blockwidth) else disktovideointerleave(mempos0) end
if iskeypressed("KEYCODE_1") then
if interleavemode == 0 then
mempos0=mempos0 - (iif(iskeypressed("KEYCODE_LSHIFT"),8*blockwidth,iif(iskeypressed("KEYCODE_LCONTROL"),1,memorystep*blockwidth)))
else
mempos0=mempos0 - 256 mempos0 = math.floor(mempos0 / 256) * 256
end
mempos0=math.max(0,mempos0)
elseif iskeypressed("KEYCODE_2") then
if interleavemode == 0 then
mempos0=mempos0 + (iif(iskeypressed("KEYCODE_LSHIFT"),8*blockwidth,iif(iskeypressed("KEYCODE_LCONTROL"),1,memorystep*blockwidth)))
else
mempos0=mempos0 + 256 mempos0 = math.floor(mempos0 / 256) * 256
end
mempos0=math.min(disksize-1,mempos0)
elseif iskeypressed("KEYCODE_BACKSPACE") then myframedispatchlist={}
elseif iskeypressed("KEYCODE_3") and keydelay==0 then blockwidth=blockwidth-1 blockwidth=math.max(blockwidth,1) keydelay=20
elseif iskeypressed("KEYCODE_4") and keydelay==0 then blockwidth=blockwidth+1 blockwidth=math.min(blockwidth,128) keydelay=20
elseif iskeypressed("KEYCODE_9") then setgfx()
elseif iskeypressed("KEYCODE_7") then interleavemode=0
elseif iskeypressed("KEYCODE_8") then interleavemode=1
end
end
manager:machine().screens[":screen"]:draw_text(350,0,"Disk Viewer File="..iif(diskname==nil,"NO DISK",diskname).."\nAddress="..hex(mempos0).." Blockwidth="..blockwidth.."\n"..iif(interleavemode==0,"","Hi-res interleave mode\n").."\n\nPress 1,2 = move by column\nshift+1,2 = move by 8 * blocksize\nctrl+1,2 = move by 1\n3,4 = increase/decrease blockwidth\n7 = Hi res interleave mode off\n8= Hi res interleave mode on\n9= Reset Graphics mode\nBackspace = quit",0xffffffff,0xffff0000)
if mydisk==nil then manager:machine().screens[":screen"]:draw_text(0,0,"No Disk Loaded") end
end
myframedispatchlist={} table.insert(myframedispatchlist,diskviewer)
if alreadyregisteredmyframedispatcher==nil then
emu.register_frame_done(myframedispatcher)
alreadyregisteredmyframedispatcher=true
end
function clrd() myframedispatchlist={} end
function loaddisk(filename)
myfile=assert(io.open(filename,"r"))
mydisk = myfile:read("*all")
myfile:close()
diskname=filename
end
function viewdisk(filename)
loaddisk(filename)
myframedispatchlist={} table.insert(myframedispatchlist,diskviewer)
end
-- dofile("../../mame205_extract/mame/memviewer_diskviewer_lua.txt")
-- loaddisk("miner 2049er (1982)(micro fun)(clean crack).dsk")