|
Joined: Feb 2014
Posts: 521
Senior Member
|
OP
Senior Member
Joined: Feb 2014
Posts: 521 |
Hi guys, I was always curious about Battlezone and I've been trying to figure out how it all works. My main goal is to find out where the 3d models for the tanks and objects are stored. But first, I wanted to find the sin/cos table, since it's got to have one. Sure enough, there's one at 5e77 to 5ef7. A little bit of lua to visualize it: (we'll render it 3 times to make it brighter)
function hex(a) return string.format("%x",a) end
mem=manager:machine().devices[":maincpu"].spaces["program"]
emu.pause()
for brightloop=1,3 do
xo=0 yo=200 xpos=xo
for wave=1,2 do
--middle to under
ibits=7 for i=0x5e77,0x5ef7,2 do val=mem:read_u16(i) x=math.floor(val/0x7fff*100) print(hex(i),hex(val),hex(x)) manager:machine().screens[":screen"]:draw_line(xpos,yo+x,xpos,yo+x,0xff000000 | (math.floor((ibits/11)*0xff)<<8)) xpos=xpos+1 end
--under to middle
for i=0x5ef7-2,0x5e77,-2 do val=mem:read_u16(i) x=math.floor(val/0x7fff*100) print(hex(i),hex(val),hex(x)) manager:machine().screens[":screen"]:draw_line(xpos,yo+x,xpos,yo+x,0xff000000 | (math.floor((ibits/11)*0xff)<<8)) xpos=xpos+1 end
-- middle to top
for i=0x5e77+2,0x5ef7,2 do val=mem:read_u16(i) x=math.floor(val/0x7fff*100) print(hex(i),hex(val),hex(x)) manager:machine().screens[":screen"]:draw_line(xpos,yo-x,xpos,yo-x,0xff000000 | (math.floor((ibits/11)*0xff)<<8)) xpos=xpos+1 end
--top to middle
for i=0x5ef7-2,0x5e77+2,-2 do val=mem:read_u16(i) x=math.floor(val/0x7fff*100) print(hex(i),hex(val),hex(x)) manager:machine().screens[":screen"]:draw_line(xpos,yo-x,xpos,yo-x,0xff000000 | (math.floor((ibits/11)*0xff)<<8)) xpos=xpos+1 end
print("wave"..wave.."done")
end -- wave
end --brightloop
![[Linked Image from i.imgur.com]](https://i.imgur.com/U6LTkp4.png) A few more sine waves and I could be at the Outer Limits. ![[Linked Image from i.imgur.com]](https://i.imgur.com/RFXECxs.jpg) Just out of curiosity, I noticed that the vectors don't line up perfectly at the bottom on the self test screen. A video that shows the self test screen close up on a real machine where the vectors don't line up exactly: https://www.youtube.com/watch?v=YGMdVn0sN80&t=80sI looked at the self test for a few of the other vector games and the one for quantum doesn't line up perfectly (I overlaid 2 of the pattern screenshots using gimp) ![[Linked Image from i.imgur.com]](https://i.imgur.com/DmowDN3.png)
|
|
|
|
Joined: Feb 2014
Posts: 521
Senior Member
|
OP
Senior Member
Joined: Feb 2014
Posts: 521 |
I was looking for a battlezone disassembly and didn't find one, but did find this interesting thread: https://forums.arcade-museum.com/archive/index.php/t-88714.htmlwhich pointed me to the avg docs: http://www.ionpool.net/arcade/atari_docs/avg.pdfhttp://arcarc.xmission.com/Tech/neilw_xy.txtThe avg is pretty interesting. Let's see if we can draw some vectors from the data in rom: I was having trouble getting the moon to draw properly until I figured out that short vector numbers represent "one-half" of the actual value (in other words, multiply by 2). If you can read "half" below your eyes (or monitor) is better than mine. ![[Linked Image from i.imgur.com]](https://i.imgur.com/qje0Qw6.png) ![[Linked Image from i.imgur.com]](https://i.imgur.com/mEywgPy.png) the moon ![[Linked Image from i.imgur.com]](https://i.imgur.com/PmcJw4O.png)
-- pause the emulator with p or emu.pause() or you won't see anything on screen
-- unpause with p or emu.unpause() to remove the drawing
function printt(a) for i,j in pairs(a) do print ("ITEM "..i,hex(j)) end end
function maskbits(a,b) local r = 0,i for i=a,b do r=r|(2^i) end return r end
function getbits(n,a,b) return (n & maskbits(a,b))>>a end
function hex(a) return string.format("%x",a) end
function iif(a,b,c) if a then return b else return c end end
mem=manager:machine().devices[":maincpu"].spaces["program"]
function cmdprocess(i)
print("ADDRESS="..hex(i))
cmd=mem:read_u16(i) i=i+2 cmdbits=getbits(cmd,13,15)
if (cmdbits<<1) == 0x4 then
xbits=getbits(cmd,0,4) if getbits(xbits,4,4)==1 then xbits=xbits-32 end
ibits=getbits(cmd,5,7)
ybits=getbits(cmd,8,12) if getbits(ybits,4,4)==1 then ybits=ybits-32 end
print(hex(cmd),hex(cmdbits<<1),xbits,ybits,ibits)
elseif (cmdbits<<1) == 0x0 then
io.write("MEMORY"..hex(i-2)..":")for k=i-2,i-2+3 do io.write(hex(mem:read_u8(k)).." ") end print()
cmd=(cmd<<16) | mem:read_u16(i) i=i+2
xbits=getbits(cmd,0,12)
if getbits(xbits,12,12)==1 then xbits=xbits-2^13 end
ibits=getbits(cmd,13,15)
ybits=getbits(cmd,16,16+8+5-1)
if getbits(ybits,12,12)==1 then ybits=ybits-2^13 end
print(hex(cmd),hex(cmdbits<<1).." x="..xbits.." y="..ybits.." i="..ibits)
elseif ((cmdbits<<1) == 0xe or ((cmdbits<<1) == 0xa)) then
jumpbits=getbits(cmd,0,12) print(hex(cmd),hex(cmdbits<<1).." JUMP="..hex(jumpbits))
jumpaddress=jumpbits*2+0x2000
--beforejump = i i=jumpbits*2+0x2000
if (cmdbits<<1)==0xa then print("ADD TO STACK"..hex(i)) table.insert(callstack,i) end
beforejump=i
i=jumpaddress
print("JUMP ADDRESS="..hex(jumpaddress))
elseif ((cmdbits<<1) == 0xc) then
print("RETURN FROM SUBROUTINE",hex(cmd),hex(cmdbits<<1))
printt(callstack)
if #callstack==0 then print("RETURN STACK EMPTY")
else i=table.remove(callstack) print ("RETURN STACK ADDRESS="..hex(i)) cmd=cmd<<8 end
elseif ((cmdbits<<1) == 0x70) then print("NEW SCALE ***************")
else
print(hex(cmd),hex(cmdbits<<1))
end
return i
end
-- draw the alphabet
xo=0 yo=100 xs=0.3 ys=xs stuckmax=1500 stuckcount=0 i=0x3282 callstack={} for count=1,40 do x=xo y=yo print(manager:machine().screens[":screen"]:draw_text(x,y-20,hex(i))) print ("COUNT="..count) x=xo y=yo cmd=0 beforejump=0 while not(cmd == 0xc000 and #callstack == 0) and stuckcount < stuckmax do stuckcount=stuckcount + 1 i=cmdprocess(i) if ((cmdbits<<1) == 0x4) or ((cmdbits<<1) == 0) then x1=x+xbits*iif((cmdbits<<1)==0,1,2)*xs y1=y-ybits*iif((cmdbits<<1)==0,1,2)*ys print(manager:machine().screens[":screen"]:draw_line(x,y,x1,y1,0xff000000 | (math.floor((ibits/11)*0xff)<<8))) x=x1 y=y1 end end if beforejump ~= 0 then i=beforejump end xo=xo+60 if xo > 440 then xo=0 yo=yo+60 end end
-- draw the moon
xo=100 xs=2.0 ys=xs stuckmax=500 stuckcount=0 i=0x3058 callstack={} for count=1,1 do x=xo y=yo print(manager:machine().screens[":screen"]:draw_text(x,y-20,hex(i))) print ("COUNT="..count) x=xo y=yo cmd=0 beforejump=0 while not(cmd == 0xc000 and #callstack == 0) and stuckcount < stuckmax do stuckcount=stuckcount + 1 i=cmdprocess(i) if ((cmdbits<<1) == 0x4) or ((cmdbits<<1) == 0) then x1=x+xbits*iif((cmdbits<<1)==0,1,2)*xs y1=y-ybits*iif((cmdbits<<1)==0,1,2)*ys print(manager:machine().screens[":screen"]:draw_line(x,y,x1,y1,0xff000000 | (math.floor((ibits/11)*0xff)<<8))) x=x1 y=y1 end end if beforejump ~= 0 then i=beforejump end xo=xo+20 if xo > 400 then xo=0 yo=yo+20 end end
|
|
|
|
Joined: Feb 2014
Posts: 521
Senior Member
|
OP
Senior Member
Joined: Feb 2014
Posts: 521 |
Ok, to make sense of what the AVG is doing, let's write a little disassembler for the instructions because it's so hard to do it by hand. Not impossible, but really difficult. So after writing some decoding routines, it's much easier to see what's going on. Taking an attract mode screen, let's see what it decodes as: ![[Linked Image from i.imgur.com]](https://i.imgur.com/hUr80JI.png)
disavg(0x2000,300)disavg(0x2000,300)
DISAVG: 2000 for 300 instructions
2000: e401 CMD=e e401 0e JUMP=401 JMPL 2802 character *
2002: 7100 CMD=6 SCAL 0100
2004: 8040 CMD=8 CNTR
2006: 1e04 CMD=0 2006:04 1e 04 1e VCTR 1e041e04 0 x=-508 y=-508 i=0 (-508,-508)
200a: 6003 CMD=6 STAT
200c: 8040 CMD=8 CNTR
200e: 00c0 CMD=0 200e:c0 00 fc 01 VCTR 00c001fc 0 x= 508 y= 192 i=0 ( 508, 192)
2012: 6203 CMD=6 STAT
2014: 8040 CMD=8 CNTR
2016: 1f10 CMD=0 2016:10 1f 58 1f VCTR 1f101f58 0 x=-168 y=-240 i=0 (-168,-240)
201a: 7100 CMD=6 SCAL 0100
201c: aa2a CMD=a aa2a 0a JUMP=a2a JSRL 3454 character Character39 Char=c
201e: aa33 CMD=a aa33 0a JUMP=a33 JSRL 3466 character Character40 Char=p
2020: a993 CMD=a a993 0a JUMP=993 JSRL 3326 character Character_0 Char=
2022: a993 CMD=a a993 0a JUMP=993 JSRL 3326 character Character_0 Char=
2024: a941 CMD=a a941 0a JUMP=941 JSRL 3282 character Character11 Char=A
2026: a9af CMD=a a9af 0a JUMP=9af JSRL 335e character Character30 Char=T
2028: a941 CMD=a a941 0a JUMP=941 JSRL 3282 character Character11 Char=A
202a: a9a2 CMD=a a9a2 0a JUMP=9a2 JSRL 3344 character Character28 Char=R
202c: a974 CMD=a a974 0a JUMP=974 JSRL 32e8 character Character19 Char=I
202e: a993 CMD=a a993 0a JUMP=993 JSRL 3326 character Character_0 Char=
2030: a9cf CMD=a a9cf 0a JUMP=9cf JSRL 339e character Character_2 Char=1
2032: a9f4 CMD=a a9f4 0a JUMP=9f4 JSRL 33e8 character Character10 Char=9
2034: a9f2 CMD=a a9f2 0a JUMP=9f2 JSRL 33e4 character Character_9 Char=8
2036: a98f CMD=a a98f 0a JUMP=98f JSRL 331e character Character_1 Char=0
2038: 7100 CMD=6 SCAL 0100
203a: 8040 CMD=8 CNTR
203c: 0000 CMD=0 203c:00 00 a8 03 VCTR 000003a8 0 x= 936 y= 0 i=0 ( 936, 0)
2040: a800 CMD=a a800 0a JUMP=800 JSRL 3000 character Item Left Horizontal Line
2042: a80b CMD=a a80b 0a JUMP=80b JSRL 3016 character Mountain_0
2044: a865 CMD=a a865 0a JUMP=865 JSRL 30ca character Mountain_1
2046: a882 CMD=a a882 0a JUMP=882 JSRL 3104 character Mountain_2
2048: 8040 CMD=8 CNTR
Soooo much easier to see what's going on.
-- drawvect() and disavg()
--
-- dofile("avg.lua")
function printthex(a) for i,j in pairs(a) do print ("ITEM "..i,hex(j)) end end
function printt(a) for i,j in pairs(a) do print ("ITEM "..i,j) end end
function maskbits(a,b) local r = 0,i for i=a,b do r=r|(2^i) end return r end
function getbits(n,a,b) return (n & maskbits(a,b))>>a end
function iif(a,b,c) if a then return b else return c end end
function hex(a,digits) digits = digits or 2 return string.format("%0"..digits.."x",a) end
function table16lookup(item,base) return mem:read_u16(base+item*2) end
function int(i) return math.floor(i) end
function avgjumpadr(i) jumpbits=getbits(i,0,12) jumpaddress=jumpbits*2+0x2000 return jumpaddress end
mem=manager:machine().devices[":maincpu"].spaces["program"]
charstr=" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ _cp"
function lookup_character(adr) local chartableentry,jumpbits,jumpaddress for i=1,charstr:len() do if avgjumpadr(mem:read_u16((i-1)*2+0x33f0))==adr then return charstr:sub(i,i) end end for i=0,7 do if avgjumpadr(mem:read_u16(i*2+0x3006))==adr then return "Mountains"..i end end for i=0,7 do if avgjumpadr(mem:read_u16(i*2+0x36f8))==adr then return "ScreenCrack"..i end end for i=1,#drawitemtable do if adr==drawitemtable[i][1] then return "Item "..drawitemtable[i][2] end end return "*" end
function lookup_character(adr)
for j=1,#drawtablestable do
for i=1,drawtablestable[j][4] do
if avgjumpadr(mem:read_u16((i-1)*2+drawtablestable[j][1]))==adr then return drawtablestable[j][3]..string.gsub(string.format("%2d",i-1)," ","_") ..iif(j==1," Char="..charstr:sub(i,i),"")
end end end
for i=1,#drawitemtable do if adr==drawitemtable[i][1] then return "Item "..drawitemtable[i][2] end end
return "*" end
drawtablestable=
{
--address, tablename, itemdescription, tablelength
{0x33f0, "CHARTABLE", "Character", charstr:len()},
{0x3006, "MOUNTAINTABLE", "Mountain", 8},
{0x36f8 ,"SCREENCRACKTABLE", "ScreenCrack", 8}
}
drawitemtable=
{
{0x3058, "Mac Tonight"},
{0x3000, "Left Horizontal Line"},
{0x33de, "Downstroke"},
{0x33f0, "CHARTABLE"},
{0x3006, "MOUNTAINTABLE"},
{0x3444, "CIRCLE DIAMOND"},
{0x347a, "BRIGHT DOTS"},
{0x34cc, "TARGET SQUARE"},
{0x3500, "TARGET LOCK ON"},
{0x353c, "RADAR"},
{0x3568, "TANK ICON"},
{0x36f8, "SCREENCRACKTABLE"},
{0x370a, "SELF TEST GRID"},
}
for i=1,#drawtablestable do printt(drawtablestable[i]) end
function formatxyi(x,y,i) return string.format ("x=% 3d y=% 3d i=%d (% 3d,% 3d)",x,y,i,x,y) end
function cmdprocess(i,addtostack)
if addtostack==nil then addtostack=true end
if lookup_character(i) ~= "*" then print(string.rep("-",40)) print(hex(i,4)..": "..lookup_character(i)) end
io.write(hex(i,4)..": ") cmd=mem:read_u16(i) i=i+2 cmdbits=getbits(cmd,13,15)
io.write(hex(cmd,4).." ".."CMD="..hex(cmdbits<<1,1).." ")
if (cmdbits<<1) == 0x4 then
xbits=getbits(cmd,0,4) if getbits(xbits,4,4)==1 then xbits=xbits-32 end
ibits=getbits(cmd,5,7)
ybits=getbits(cmd,8,12) if getbits(ybits,4,4)==1 then ybits=ybits-32 end
print("SVEC "..hex(cmd),hex(cmdbits<<1).." "..formatxyi(xbits,ybits,ibits))
elseif (cmdbits<<1) == 0x0 then
io.write(" "..hex(i-2)..":")for k=i-2,i-2+3 do io.write(hex(mem:read_u8(k)).." ") end
cmd=(cmd<<16) | mem:read_u16(i) i=i+2
xbits=getbits(cmd,0,12)
if getbits(xbits,12,12)==1 then xbits=xbits-2^13 end
ibits=getbits(cmd,13,15)
ybits=getbits(cmd,16,16+8+5-1)
if getbits(ybits,12,12)==1 then ybits=ybits-2^13 end
print("VCTR "..hex(cmd,8).." "..hex(cmdbits<<1,1).." "..formatxyi(xbits,ybits,ibits))
elseif ((cmdbits<<1) == 0xe or ((cmdbits<<1) == 0xa)) then
jumpbits=getbits(cmd,0,12) io.write(hex(cmd).." "..hex(cmdbits<<1).." JUMP="..hex(jumpbits).." ")
jumpaddress=jumpbits*2+0x2000
--beforejump = i i=jumpbits*2+0x2000
if (cmdbits<<1)==0xa then if addtostack then io.write("ADD TO STACK: "..hex(i).." ") table.insert(callstack,i) end end
beforejump=i
i=jumpaddress
print(iif((cmdbits<<1)==0xa,"JSRL ","JMPL ")..hex(jumpaddress).." character "..lookup_character(jumpaddress))
elseif ((cmdbits<<1) == 0xc) then
print("RTSL")
printt(callstack)
if #callstack==0 then print("RETURN STACK EMPTY") rtsemptyflag=true
else i=table.remove(callstack) print ("RETURN STACK ADDRESS="..hex(i)) cmd=cmd<<8 end
elseif (cmdbits<<1) == 0x6 then io.write (iif(getbits(cmd,12,12)==0,"STAT ","SCAL ")) if getbits(cmd,12,12)==1 then print( hex(getbits(cmd,0,10),4)) else print () end
elseif (cmdbits<<1) == 0x8 then print("CNTR ")
elseif (cmdbits<<1) == 0x2 then print("HALT ")
else
print(hex(cmd),hex(cmdbits<<1))
end
return i
end
function drawvect(adr,xo,yo,scale,textoffset) xo=xo or 150 yo=yo or 250 scale=scale or 0.4 textoffset=textoffset or -20 rtsemptyflag=false xs=scale ys=xs stuckmax=968 stuckcount=0 i=adr callstack={} for count=1,1 do x=xo y=yo print(manager:machine().screens[":screen"]:draw_text(x,y+textoffset,hex(i)..":"..lookup_character(i))) print ("COUNT="..count) x=xo y=yo cmd=0 beforejump=0 cmdbits=0 while not(((cmdbits<<1) == 0xc) and rtsemptyflag) and stuckcount < stuckmax and not ((cmdbits<<1)==0x2) do stuckcount=stuckcount + 1 i=cmdprocess(i) if ((cmdbits<<1) == 0x8) then x=xo y=yo end if ((cmdbits<<1) == 0x4) or ((cmdbits<<1) == 0) then x1=x+xbits*iif((cmdbits<<1)==0,1,2)*xs y1=y-ybits*iif((cmdbits<<1)==0,1,2)*ys manager:machine().screens[":screen"]:draw_line(x,y,x1,y1,0xff000000 | (math.floor((ibits/11)*0xff)<<8)) x=x1 y=y1 end end if beforejump ~= 0 then i=beforejump end xo=xo+20 end end
function disavg(adr,count) print("DISAVG: "..hex(adr).." for "..count.." instructions") i=adr callstack={} for counter=1,count do callstack={} originali=i cmdprocess(i,false) if cmdbits==0x0 then i=originali+4 else i=originali + 2 end end end
function drawmountains() mtntable=0x3006 for i=0,7 do drawvect(avgjumpadr(table16lookup(i,mtntable)),15+270*(i%2),180+65*int(i/2),0.5,-60) end end
print(manager:machine():debugger():command("go")) emu.pause()
for i=1,3 do drawmountains() end
print(manager:machine():debugger():command("snap"))
disavg(0x3000,20)
And let's draw some mountains while we're at it 8-) ![[Linked Image from i.imgur.com]](https://i.imgur.com/cNQywML.png)
|
|
|
|
Joined: Mar 2001
Posts: 16,652 Likes: 1
Very Senior Member
|
Very Senior Member
Joined: Mar 2001
Posts: 16,652 Likes: 1 |
|
|
|
|
Joined: Feb 2014
Posts: 521
Senior Member
|
OP
Senior Member
Joined: Feb 2014
Posts: 521 |
Thanks, RB! ==================================== Now we've got an AVG decoder let's see if we can figure out where those vectors are coming from. The attract screen has some objects, and the cube to the right of center that's just below the horizon line could be useful to study. ![[Linked Image from i.imgur.com]](https://i.imgur.com/BWOLmq6.png) We'll pause mame with "p" and take a look at the AVG display list disassembly. Through process of elimination, looking at the first VCTR after a CNTR, we home in on 0x20e4. There's 8 possibilities and this one is most likely due to it being to the right of center and below the horizon. [MAME]> disavg(0x2000,260)
20e4: 8040 CMD=8 CNTR
20e6: 1ff6 CMD=0 20e6:f6 1f 87 00 VCTR 1ff60087 0 x= 135 y=-10 i=0 ( 135,-10)
20ea: 6430 CMD=6 STAT
20ec: 0000 CMD=0 20ec:00 00 11 20 VCTR 00002011 0 x= 17 y= 0 i=1 ( 17, 0)
20f0: 0000 CMD=0 20f0:00 00 0d 20 VCTR 0000200d 0 x= 13 y= 0 i=1 ( 13, 0)
20f4: 0000 CMD=0 20f4:00 00 ef 3f VCTR 00003fef 0 x=-17 y= 0 i=1 (-17, 0)
20f8: 0000 CMD=0 20f8:00 00 f3 3f VCTR 00003ff3 0 x=-13 y= 0 i=1 (-13, 0)
20fc: 0009 CMD=0 20fc:09 00 00 20 VCTR 00092000 0 x= 0 y= 9 i=1 ( 0, 9)
2100: 0000 CMD=0 2100:00 00 11 20 VCTR 00002011 0 x= 17 y= 0 i=1 ( 17, 0)
2104: 0000 CMD=0 2104:00 00 0d 20 VCTR 0000200d 0 x= 13 y= 0 i=1 ( 13, 0)
2108: 0000 CMD=0 2108:00 00 ef 3f VCTR 00003fef 0 x=-17 y= 0 i=1 (-17, 0)
210c: 0000 CMD=0 210c:00 00 f3 3f VCTR 00003ff3 0 x=-13 y= 0 i=1 (-13, 0)
2110: 0000 CMD=0 2110:00 00 11 00 VCTR 00000011 0 x= 17 y= 0 i=0 ( 17, 0)
2114: 1ff7 CMD=0 2114:f7 1f 00 20 VCTR 1ff72000 0 x= 0 y= -9 i=1 ( 0, -9)
2118: 0000 CMD=0 2118:00 00 0d 00 VCTR 0000000d 0 x= 13 y= 0 i=0 ( 13, 0)
211c: 0009 CMD=0 211c:09 00 00 20 VCTR 00092000 0 x= 0 y= 9 i=1 ( 0, 9)
2120: 0000 CMD=0 2120:00 00 ef 1f VCTR 00001fef 0 x=-17 y= 0 i=0 (-17, 0)
2124: 1ff7 CMD=0 2124:f7 1f 00 20 VCTR 1ff72000 0 x= 0 y= -9 i=1 ( 0, -9)
2128: 8040 CMD=8 CNTR
Notice that all of the y moves are either 9 or -9. After the y coord goes -10 at 20e6, then at 20fc it comes back up +9, then goes -9 at 2114, +9 at 211c, and -9 at 2124. The y coordinate stays below the horizon at y=0, going back between y=-10 and y=-1. So let's do a trace. First I'll set a watchpoint on writes to 0x2002 since that's where the displaylist starts (the displaylist alternates between 0x2002 and 0x2802). wp 2002,1,w
Watchpoint 1 set In the debugger I'll enter these commands (or do a "source" from a file) This is a good trace command because it shows you the status of the A,X,Y,P registers along with the status flags: trace bzone.tracenoloop2002.txt,0,noloop,{tracelog "A=%02x X=%02x Y=%02x P=%02x NV__DIZC\n %1x%1x__%1x%1x%1x%1x\n",A,X,Y,P,((p&0x80)!=0),((p&0x40)!=0),((p&0x8)!=0),((p&0x4)!=0),((p&0x2)!=0),((p&0x1)!=0);}
Let's reformat the tracelog statement a little bit so it's easier to digest:
tracelog
"A=%02x X=%02x Y=%02x P=%02x NV__DIZC\n
%1x%1x__%1x%1x%1x%1x\n",
A,X,Y,P,
((p&0x80)!=0),((p&0x40)!=0),((p&0x8)!=0),((p&0x4)!=0),((p&0x2)!=0),((p&0x1)!=0);}
The noloop is a useful parameter because we want to see all of the instructions executed. I like to instrument all of the READ and WRITE activity which helps to show the actual data moving around in the system so we'll set some watchpoints to do it. wp 0,8000,r,1,{tracelog "READ addr=%02x data=%02x\n",wpaddr,wpdata;g}
wp 0,8000,w,1,{tracelog "WRITE addr=%02x data=%02x\n",wpaddr,wpdata;g}
Once we have these in place, we'll issue a "g" to get things started.
Stopped at watchpoint 1 writing 00 to 00002002 (PC=7A70)
[MAME]>
Stopped at watchpoint 1 writing 00 to 00002002 (PC=7A70)
[MAME]> It won't actually stop when it hits watchpoint 1 since there's another watchpoint that issues a "g" so we'll let it run after it hits watchpoint 1 a couple of times and then stop it with the enter key and then "trace off". So our cube begins drawing at the location 20e4, let's search in our trace for a write to that location. Using less on our trace file "less -n bzone.tracenoloop2002.txt" (-n for line numbers)
/WRITE addr=20E4
50378 7A6E: ldy #$00
50381 A=40 X=80 Y=00 P=36 NV__DIZC
50382 00__0110
50383 7A70: sta ($02), y
50388 WRITE addr=20E4 data=40 <<<<<<<<<<<<< WRITE TO 20E4
Scanning backwards a little bit we can see an LDA ($3b), y which looks interesting... 50235 5C68: ldy #$00
50240 5C6A: lda ($3b), y
50242 READ addr=3B data=D7
50243 READ addr=3C data=74
50244 READ addr=74D7 data=03 So let's see if we can find where $3b gets written: Search backwards with less: ? WRITE addr=3B
50183 50B6: lda $0270, x
50190 50B9: jsr $5c5c
50199 5C5C: asl a
50209 5C5E: lda $7472, y
50212 READ addr=7490 data=D7
50214 A=D7 X=06 Y=1E P=B4 NV__DIZC
50216 5C61: sta $3b
50218 WRITE addr=3B data=D7 <<<< write to 3B
50222 5C63: lda $7473, y
50225 READ addr=7491 data=74
50229 5C66: sta $3c
50231 WRITE addr=3C data=74 <<<<< write to 3C so there's a lookup table at 7472 and the offset is 1E, the lookup address is 74D7 and let's do a hexdump on it using a couple of lua functions to hexdump: mem=manager:machine().devices[":maincpu"].spaces["program"]
function hexdump(adr,len,peritem,perrow) peritem=peritem or 1 perrow=perrow or 8 print("HEXDUMP addr="..hex(adr,4).." len="..hex(len).." peritem="..peritem) local rowcount=0 for i=adr,adr+(len-1)*peritem,peritem do if rowcount%perrow ==0 then io.write(hex(i,4)..": ") end local memval=0 for n=0,peritem-1 do memval=memval|(mem:read_u8(i+n)<<((n)*8)) end io.write(hex(memval,2*peritem).." ") if ((rowcount)%perrow) == (perrow-1) then print() end rowcount=rowcount+1 end print() end
function hexdump16(adr,len,perrow) hexdump(adr,len,2,perrow) end
[MAME]> hexdump16(0x7472,16)
HEXDUMP16 addr=7472 len=10
7472: 74cb 74d7 74e9 7519 7525 7525 7525 7525
7482: 7525 7525 7525 7525 74cb 752d 753b 74d7 <<< 74d7 at offset 1E
and let's look at 74d7
[MAME]> hexdump(0x74d7,18)
HEXDUMP addr=74d7 len=12
74d7: 03 a1 0c 14 1c 04 24 2c
74df: 34 3c 24 2a 0c 12 34 3a
74e7: 1c ff What does this table do? I figured out what it does, it is the vertex table. We'll analyze the vertex table later, but first I want to find the actual point data. So we'll leave that for the next installment.
|
|
|
|
Joined: Feb 2014
Posts: 521
Senior Member
|
OP
Senior Member
Joined: Feb 2014
Posts: 521 |
Finding the Points Table ======================== So let's look a little further back in the trace, it looks like there's some math being done with the mathbox. There's writes to $1871 which is a mathbox address, and it's accessing data at 3C88, using LDA ($3b), y where 3b=3C5A and y=2e
49839 5DC1: lda ($3b), y
49840 READ addr=5DC2 data=3B
49841 READ addr=3B data=5A
49842 READ addr=3C data=3C
49843 READ addr=3C88 data=FD
49845 A=FD X=1C Y=2E P=B4 NV__DIZC
49847 5DC3: sta $1871 so let's scan backwards for where 3b gets set: and that leads us to this table lookup at $388e: ?WRITE addr=3B
46690 5D76: lda $0270, x <<<<< loads the object # from $0270, x
46693 READ addr=276 data=0F <<<< number is 0xF
46695 A=0F X=06 Y=00 P=34 NV__DIZC
46697 5D79: asl a <<< multiply by 2
46702 5D7A: tay <<<< transfer into y
46705 A=1E X=06 Y=1E P=34 NV__DIZC
46707 5D7B: lda $388e, y <<<<< lookup table at 388e
46710 READ addr=38AC data=5A <<<< get the low byte
46712 A=5A X=06 Y=1E P=34 NV__DIZC
46714 5D7E: sta $3b
46716 WRITE addr=3B data=5A <<<<< 3b gets written here
46718 A=5A X=06 Y=1E P=34 NV__DIZC
46720 5D80: lda $388f, y <<<<< now get the high byte
46723 READ addr=38AD data=3C
46727 5D83: sta $3c
46729 WRITE addr=3C data=3C <<<<< 3c gets written here
46731 A=3C X=06 Y=1E P=34 NV__DIZC full address is $3c5a
Taking a hexdump of the 0x388e table: [MAME]> hexdump16(0x388e,15)
HEXDUMP16 addr=388e len=0f
388e: 38e6 3905 3955 3936 3a55 3a30 3a0b 39e6
389e: 3a7a 3a9f 3ac4 3ae9 3c3b 3b0e 3b3f 3c5a <<<<<<<< 3c5a at offset 1E
and the entry for item 0xf is $3c5a so let's have a look:
[MAME]> hexdump(0x3c5a,48)
HEXDUMP addr=3c5a len=30
3c5a: 31 80 fd 80 fd c0 fe 80
3c62: fd 80 02 c0 fe 80 02 80
3c6a: 02 c0 fe 80 02 80 fd c0
3c72: fe 80 fd 80 fd d8 ff 80
3c7a: fd 80 02 d8 ff 80 02 80
3c82: 02 d8 ff 80 02 80 fd d8 The first number must be the # of bytes in the point table plus 1 (since it includes the first byte), 0x31=49. So 48 bytes of points, each point is 2 bytes, 3 points for (x,y,z) so there's 48/2/3=8 points.
[MAME]> hexdump16(0x3c5a+1,8*3,3)
HEXDUMP16 addr=3c5b len=18
3c5b: fd80 fd80 fec0
3c61: fd80 0280 fec0
3c67: 0280 0280 fec0
3c6d: 0280 fd80 fec0
3c73: fd80 fd80 ffd8
3c79: fd80 0280 ffd8
3c7f: 0280 0280 ffd8
3c85: 0280 fd80 ffd8
Hex is hard to decode so let's look at it in decimal:
[MAME]> a=mem:read_u8(0x3c5a)
[MAME]> print(a)
49
[MAME]> for i=0x3c5a+1,0x3c5a+1+a-1-1,6 do x=mem:read_i16(i) y=mem:read_i16(i+2) z=mem:read_i16(i+4) print(hex(i,4),"("..int(x)..","..int(y)..","..int(z)..")") end
3c5b (-640,-640,-320)
3c61 (-640,640,-320)
3c67 (640,640,-320)
3c6d (640,-640,-320)
3c73 (-640,-640,-40)
3c79 (-640,640,-40)
3c7f (640,640,-40)
3c85 (640,-640,-40) They all seem to be multiples of 40, so let's divide by 40 to make the numbers a little simpler for us to understand. [MAME]> for i=0x3c5a+1,0x3c5a+1+a-1-1,6 do x=mem:read_i16(i) y=mem:read_i16(i+2) z=mem:read_i16(i+4) print(hex(i,4),"("..int(x/40)..","..int(y/40)..","..int(z/40)..")") end
3c5b (-16,-16,-8)
3c61 (-16,16,-8)
3c67 (16,16,-8)
3c6d (16,-16,-8)
3c73 (-16,-16,-1)
3c79 (-16,16,-1)
3c7f (16,16,-1)
3c85 (16,-16,-1)
[MAME]> If you draw the (x,y) components you get a square, and there's one square at z=-8 and another at z=-1 so definitely a cube, and it's the cube we're looking for. So now let's see if we can change a point and see it visibly change on screen: We'll open up a memory viewer in the debugger and go to our table at 0x3c5b. Note that we are in region :maincpu as the default region won't let you change the memory. -> change 0x3c77 from 0xffd8 to 0 -> (changing the z coordinate from -40 to 0) ![[Linked Image from i.imgur.com]](https://i.imgur.com/sGcdlVO.png) and you can see the vertex move right up to the horizon line. ![[Linked Image from i.imgur.com]](https://i.imgur.com/96UGKkD.png) ![[Linked Image from i.imgur.com]](https://i.imgur.com/iPxr2ws.png)
|
|
|
|
Joined: Feb 2014
Posts: 521
Senior Member
|
OP
Senior Member
Joined: Feb 2014
Posts: 521 |
Finding the Vertex Table =========================== Okay, so now we've got a point table at $388e, which points to a set of points. The length of the point table comes first (size of table in bytes including length byte) followed by the 16 bit point triples, each triple entry being 6 bytes. Now the vertex array lookup table is at $7472, which points to a set of vertexes. This vertex list is terminated with an FF byte. So what's the format of this vertex table? We lookup an address for our vertex table at $7472, put it into $3b and then start getting values from ($3b),y:
50209 5C5E: lda $7472, y
50216 5C61: sta $3b
50222 5C63: lda $7473, y
50229 5C66: sta $3c
50235 5C68: ldy #$00
50240 5C6A: lda ($3b), y
...
50264 5C72: and #$f8
...
50280 5C77: and #$07 there's an and #$f8 which is bits 11111000 and an and #$07 which is bits 00000111 These low 3 bits get multiplied by 2 and used as an offset in a lookup table at $5c84. The address of the routine gets pushed on the stack and then an rts jumps to the routine address + 1.
50295 5C7B: lda $5c85, x
50302 5C7E: pha
50308 5C7F: lda $5c84, x
50315 5C82: pha
50321 5C83: rts The high 5 bits are the vertex number or a parameter for the routine in the lookup table. The lookup table at $5c84:
[MAME]> hexdump16(0x5c84,7,1)
HEXDUMP16 addr=5c84 len=07
5c84: 5c91 (address is +1, so $5c92 00)
5c86: 5c9e (address is +1, so $5c9f 01)
5c88: 5ce0 (address is +1, so $5ce1 02)
5c8a: 5cd3 (address is +1, so $5cd4 03)
5c8c: 5ce4 (address is +1, so $5ce5 04)
5c8e: 5cec (address is +1, so $5ced 05)
5c90: 5d0d (address is +1, so $5d0e 06)
5c92: (first routine in jump table)
Let's try to decode the data:
[MAME]> hexdump(0x3c5a,16)
HEXDUMP addr=3c5a len=10
3c5a: 31 80 fd 80 fd c0 fe 80
3c62: fd 80 02 c0 fe 80 02 80
3c6a: 02
stuckcount=0 i=0x74d7 while mem:read_u8(i)~=0xff do m=mem:read_u8(i) print(hex(i,4)..":"..hex(m).." "..hex(getbits(m,3,7)).." "..hex(getbits(m,0,2))) i=i+1 stuckcount=stuckcount+1 if stuckcount>50 then break end end print(hex(i,4)..":"..hex(mem:read_u8(i)))
param routine
(high 5) (low 3 bits)
| |
74d7:03 00 03
74d8:a1 14 01
74d9:0c 01 04
74da:14 02 04
74db:1c 03 04
74dc:04 00 04
74dd:24 04 04
74de:2c 05 04
74df:34 06 04
74e0:3c 07 04
74e1:24 04 04
74e2:2a 05 02
74e3:0c 01 04
74e4:12 02 02
74e5:34 06 04
74e6:3a 07 02
74e7:1c 03 04
74e8:ff In the last column we have the routine that gets called, a number from 0 to 5. 03 starts the object, moves to a new location (MoveToStart) 01 sets the intensity 04 continues the vector (DrawTo) 02 ends the current line, then moves to a new location (MoveTo) So if we put our generated AVG commands next to the vertex table decode, it's easy to see the pattern (you may need to widen the window or scroll to see all of it): All DrawTos have (i=1) All MoveTos have (i=0)
20e4: 8040 CMD=8 CNTR
20e6: 1ff6 CMD=0 20e6:f6 1f 87 00 VCTR 1ff60087 0 x= 135 y=-10 i=0 ( 135,-10) 74d7:03 00 03 Start New Object (point 0)
20ea: 6430 CMD=6 STAT 74d8:a1 14 01 Stat (set brightness)
20ec: 0000 CMD=0 20ec:00 00 11 20 VCTR 00002011 0 x= 17 y= 0 i=1 ( 17, 0) 74d9:0c 01 04 DrawTo (point 1)
20f0: 0000 CMD=0 20f0:00 00 0d 20 VCTR 0000200d 0 x= 13 y= 0 i=1 ( 13, 0) 74da:14 02 04 DrawTo (point 2)
20f4: 0000 CMD=0 20f4:00 00 ef 3f VCTR 00003fef 0 x=-17 y= 0 i=1 (-17, 0) 74db:1c 03 04 DrawTo (point 3)
20f8: 0000 CMD=0 20f8:00 00 f3 3f VCTR 00003ff3 0 x=-13 y= 0 i=1 (-13, 0) 74dc:04 00 04 DrawTo (point 0)
20fc: 0009 CMD=0 20fc:09 00 00 20 VCTR 00092000 0 x= 0 y= 9 i=1 ( 0, 9) 74dd:24 04 04 DrawTo (point 4)
2100: 0000 CMD=0 2100:00 00 11 20 VCTR 00002011 0 x= 17 y= 0 i=1 ( 17, 0) 74de:2c 05 04 DrawTo (point 5)
2104: 0000 CMD=0 2104:00 00 0d 20 VCTR 0000200d 0 x= 13 y= 0 i=1 ( 13, 0) 74df:34 06 04 DrawTo (point 6)
2108: 0000 CMD=0 2108:00 00 ef 3f VCTR 00003fef 0 x=-17 y= 0 i=1 (-17, 0) 74e0:3c 07 04 DrawTo (point 7)
210c: 0000 CMD=0 210c:00 00 f3 3f VCTR 00003ff3 0 x=-13 y= 0 i=1 (-13, 0) 74e1:24 04 04 DrawTo (point 4)
2110: 0000 CMD=0 2110:00 00 11 00 VCTR 00000011 0 x= 17 y= 0 i=0 ( 17, 0) 74e2:2a 05 02 MoveTo (point 5)
2114: 1ff7 CMD=0 2114:f7 1f 00 20 VCTR 1ff72000 0 x= 0 y= -9 i=1 ( 0, -9) 74e3:0c 01 04 DrawTo (point 1)
2118: 0000 CMD=0 2118:00 00 0d 00 VCTR 0000000d 0 x= 13 y= 0 i=0 ( 13, 0) 74e4:12 02 02 MoveTo (point 2)
211c: 0009 CMD=0 211c:09 00 00 20 VCTR 00092000 0 x= 0 y= 9 i=1 ( 0, 9) 74e5:34 06 04 DrawTo (point 6)
2120: 0000 CMD=0 2120:00 00 ef 1f VCTR 00001fef 0 x=-17 y= 0 i=0 (-17, 0) 74e6:3a 07 02 MoveTo (point 7)
2124: 1ff7 CMD=0 2124:f7 1f 00 20 VCTR 1ff72000 0 x= 0 y= -9 i=1 ( 0, -9) 74e7:1c 03 04 DrawTo (point 3)
2128: 8040 CMD=8 CNTR
So let's write some routines to print out the vertex table and points table
mem=manager:machine().devices[":maincpu"].spaces["program"]
mem2=manager:machine():memory().regions[":maincpu"] -- alternate way to access memory
function hex(a,digits) digits = digits or 2 return string.format("%0"..digits.."x",a) end
function int(i) return math.floor(i) end
function tablelookup16(item,base) return mem:read_u16(base+item*2) end
function maskbits(a,b) local r = 0,i for i=a,b do r=r|(2^i) end return r end
function getbits(n,a,b) return (n & maskbits(a,b))>>a end
function iif(a,b,c) if a then return b else return c end end
function printvertextable(i) stuckcount=0 while mem:read_u8(i)~=0xff do m=mem:read_u8(i) print(hex(i,4)..":"..hex(m).." "..hex(getbits(m,3,7)).." "..hex(getbits(m,0,2))) i=i+1 stuckcount=stuckcount+1 if stuckcount>100 then print("STUCK LOOP="..stuckcount) break end end print(hex(i,4)..":"..hex(mem:read_u8(i))) end
function printpointstable(adr) a=mem:read_u8(adr) local count=0 for i=adr+1,adr+1+a-1-1,6 do x=mem:read_i16(i) y=mem:read_i16(i+2) z=mem:read_i16(i+4) print(hex(i,4).." vertex "..count.." ("..int(x)..","..int(y)..","..int(z)..")") count=count+1 end end
function printline() print(string.rep("-",40)) end
battlezoneobjecttable={
"pyramid",
"cube",
"enemy tank",
"shot",
"rear tread 1",
"rear tread 2",
"rear tread 3",
"rear tread 4",
"front tread 1",
"front tread 2",
"front tread 3",
"front tread 4",
"pyramid wide",
"radar1",
"dot",
"short cube",
"fragment 1",
"fragment 2",
"fragment 3",
"fragment 4",
"fragment 5",
"fragment 6",
"missile",
"BA",
"fragment 7",
"fragment 8",
"fragment 9",
"fragment 10",
"fragment 11",
"fragment 12",
"TTLE",
"ZONE",
"saucer",
"supertank"
}
function printobject(obj)
printline() print("OBJECT "..obj.." "..battlezoneobjecttable[obj+1]) printline()
printpointstable(tablelookup16(obj,0x388e))
printvertextable(tablelookup16(obj,0x7472))
end
printobject(0xf)
----------------------------------------
OBJECT 15 short cube
----------------------------------------
3c5b vertex 0 (-640,-640,-320)
3c61 vertex 1 (-640,640,-320)
3c67 vertex 2 (640,640,-320)
3c6d vertex 3 (640,-640,-320)
3c73 vertex 4 (-640,-640,-40)
3c79 vertex 5 (-640,640,-40)
3c7f vertex 6 (640,640,-40)
3c85 vertex 7 (640,-640,-40)
74d7:03 00 03
74d8:a1 14 01
74d9:0c 01 04
74da:14 02 04
74db:1c 03 04
74dc:04 00 04
74dd:24 04 04
74de:2c 05 04
74df:34 06 04
74e0:3c 07 04
74e1:24 04 04
74e2:2a 05 02
74e3:0c 01 04
74e4:12 02 02
74e5:34 06 04
74e6:3a 07 02
74e7:1c 03 04
74e8:ff
for i=0,33 do printobject(i) end
|
|
|
|
Joined: Feb 2014
Posts: 521
Senior Member
|
OP
Senior Member
Joined: Feb 2014
Posts: 521 |
Once we know the points and the vertexes, let's see if we can draw the 3d objects. ![[Linked Image from i.imgur.com]](https://i.imgur.com/fjfbbIi.png) ![[Linked Image from i.imgur.com]](https://i.imgur.com/gaX4gNK.png)
--
-- drawbz.lua
-- dofile("drawbz.lua") or -autoboot_script drawbz.lua
--
function int(i) return math.floor(i) end
mem=manager:machine().devices[":maincpu"].spaces["program"]
mem2=manager:machine():memory().regions[":maincpu"] -- alternate way to access memory
function hex(a,digits) digits = digits or 2 return string.format("%0"..digits.."x",a) end
function int(i) return math.floor(i) end
function tablelookup16(item,base) return mem:read_u16(base+item*2) end
function maskbits(a,b) local r = 0,i for i=a,b do r=r|(2^i) end return r end
function getbits(n,a,b) return (n & maskbits(a,b))>>a end
function iif(a,b,c) if a then return b else return c end end
function printt(a) for i,j in pairs(a) do print ("ITEM "..i,j) end end
function makevertextable(i) local v local v1 local vertextable={} stuckcount=0 printt(vertextable) while mem:read_u8(i)~=0xff do m=mem:read_u8(i) param=getbits(m,3,7) routine=getbits(m,0,2) if routine==0x3 or routine==0x2 then v=param elseif routine == 0x4 or routine == 0x5 then v1=v v=param vertextable[#vertextable+1]={v1,v} end -- 0x5 for the dot object
--print(hex(i,4)..":"..hex(m).." "..hex(getbits(m,3,7)).." "..hex(getbits(m,0,2)))
i=i+1 stuckcount=stuckcount+1 if stuckcount>50 then break end end
--print(hex(i,4)..":"..hex(mem:read_u8(i)))
return vertextable end
function makepointstable(adr) local pointstable={} a=mem:read_u8(adr) for i=adr+1,adr+1+a-1-1,6 do x=mem:read_i16(i) y=mem:read_i16(i+2) z=mem:read_i16(i+4)
--print(hex(i,4),"("..int(x)..","..int(y)..","..int(z)..")")
pointstable[#pointstable+1]={int(x),int(y),int(z)} end return pointstable end
function printpoint(j) for k=1,#j do io.write(j[k]..iif(k<#j,", ","")) end end
function printvt(v) for i,j in pairs(v) do io.write(i..": ") for k=1,#j do io.write(j[k]..iif(k<#j,", ","")) end print() end end
function printvt(v) for i,j in pairs(v) do io.write(i..": ") printpoint(j) end print() end
function rotatexyz(x,y,z,a,axis) if axis=="x" then y,z=rotatexy(y,z,a) elseif axis=="y" then x,z=rotatexy(x,z,a) else x,y=rotatexy(x,y,a) end return x,y,z end
function rotatexy(x,y,a) local x1=x*math.cos(a)-y*math.sin(a) local y1=y*math.cos(a)+x*math.sin(a) return x1,y1 end
function translatexyz(x,y,z,x1,y1,z1) return x+x1,y+y1,z+z1 end
function scalexyz(x,y,z,sx,sy,sz) return x*sx,y*sy,z*sz end
function projectpoint(x,y,z,d) return project(x,z,d),project(y,z,d) end
function project(x,z,d) return x/z*d end
ztrans=2550 xo=200 yo=200 d=400
function drawobject(obj,rotx,roty,rotz,s)
s=s or 1.0
rotx=rotx or 0.0
roty=roty or 0.0
rotz=rotz or 0.0
pt=makepointstable(tablelookup16(obj,0x388e)) vt=makevertextable(tablelookup16(obj,0x7472))
rotx=math.pi*rotx/180.0
roty=math.pi*roty/180.0
rotz=math.pi*rotz/180.0
--print("rot",rotx,roty,rotz)
for a,b in pairs(vt) do
--io.write(a..": ("..b[1]..", "..b[2]..") ") printpoint(pt[b[1]+1]) io.write(" ") printpoint(pt[b[2]+1]) print()
pt1=pt[b[1]+1] pt2=pt[b[2]+1] x1,y1,z1=pt1[1],pt1[2],pt1[3] x2,y2,z2=pt2[1],pt2[2],pt2[3]
x1,y1,z1=scalexyz(x1,y1,z1,s,s,s)
x2,y2,z2=scalexyz(x2,y2,z2,s,s,s)
--print(x1,y1,z1) print(x2,y2,z2)
x1,y1,z1=rotatexyz(x1,y1,z1,rotx,"x")
x1,y1,z1=rotatexyz(x1,y1,z1,roty,"y")
x1,y1,z1=rotatexyz(x1,y1,z1,rotz,"z")
x2,y2,z2=rotatexyz(x2,y2,z2,rotx,"x")
x2,y2,z2=rotatexyz(x2,y2,z2,roty,"y")
x2,y2,z2=rotatexyz(x2,y2,z2,rotz,"z")
--print("rotatexyz")
--print(x1,y1,z1) print(x2,y2,z2)
x1,y1,z1=translatexyz(x1,y1,z1,0,0,ztrans*iif(obj==23 or obj==30 or obj==31,3.0,1.0))
x2,y2,z2=translatexyz(x2,y2,z2,0,0,ztrans*iif(obj==23 or obj==30 or obj==31,3.0,1.0))
x1,y1=projectpoint(x1,y1,z1,d)
x2,y2=projectpoint(x2,y2,z2,d)
--print("project",x1,y1,x2,y2)
ibits=11 manager:machine().screens[":screen"]:draw_line(x1+xo,y1+yo,x2+xo,y2+yo,0xff0000ff | (math.floor((ibits/11)*0xff)<<8)| (math.floor((ibits/11)*0xff)<<16))
end
manager:machine().screens[":screen"]:draw_text(xo-30,yo+24,obj.." "..battlezoneobjecttable[obj+1],0xff0000ff | (math.floor((ibits/11)*0xff)<<8)| (math.floor((ibits/11)*0xff)<<16))
end
battlezoneobjecttable={
"pyramid",
"cube",
"enemy tank",
"shot",
"rear tread 1",
"rear tread 2",
"rear tread 3",
"rear tread 4",
"front tread 1",
"front tread 2",
"front tread 3",
"front tread 4",
"pyramid wide",
"radar1",
"dot",
"short cube",
"fragment 1",
"fragment 2",
"fragment 3",
"fragment 4",
"fragment 5",
"fragment 6",
"missile",
"BA",
"fragment 7",
"fragment 8",
"fragment 9",
"fragment 10",
"fragment 11",
"fragment 12",
"TTLE",
"ZONE",
"saucer",
"supertank"
}
--ztrans=2550 xo=200 yo=200 d=400
--drawobject(2,45,5,90)
for obj=0,33 do ztrans=19550 xo=50+85*(obj%6) yo=50+60*int(obj/6) d=600 drawobject(obj,0,0,0, 2.0) end -- overhead
for obj=0,33 do ztrans=19550 xo=50+85*(obj%6) yo=50+60*int(obj/6) d=600 drawobject(obj,90,0,0,2.0) end -- sideways
for obj=0,33 do ztrans=19550 xo=50+85*(obj%6) yo=50+60*int(obj/6) d=600 drawobject(obj,90,90,0,2.0) end -- at you
function drawbz()
counter=counter or 0
counter=counter+1
rotx=rotx or 0
roty=roty or 0
rotz=rotz or 0
spinmode = spinmode or 0
inp = manager:machine():input() if inp:code_pressed(inp:code_from_token("KEYCODE_SPACE")) then print("X") end
inp = manager:machine():input() if inp:code_pressed(inp:code_from_token("KEYCODE_C")) then dispatch_list={} end
if spinmode == 0 then
if int(counter/360)%3 == 0 then
rotx=rotx+1
elseif int(counter/360)%3 == 1 then
roty=roty+1
elseif int(counter/360)%3 == 2 then
rotz=rotz+1
end
end
inp = manager:machine():input()
if inp:code_pressed(inp:code_from_token("KEYCODE_DOWN")) or
inp:code_pressed(inp:code_from_token("KEYCODE_UP")) or
inp:code_pressed(inp:code_from_token("KEYCODE_LEFT")) or
inp:code_pressed(inp:code_from_token("KEYCODE_RIGHT")) or
inp:code_pressed(inp:code_from_token("KEYCODE_Z")) or
inp:code_pressed(inp:code_from_token("KEYCODE_X")) then spinmode = 1
end
if spinmode == 1 then
if inp:code_pressed(inp:code_from_token("KEYCODE_DOWN")) then rotx=rotx+1
elseif inp:code_pressed(inp:code_from_token("KEYCODE_UP")) then rotx=rotx-1 end
if inp:code_pressed(inp:code_from_token("KEYCODE_LEFT")) then rotz=rotz+1
elseif inp:code_pressed(inp:code_from_token("KEYCODE_RIGHT")) then rotz=rotz-1 end
if inp:code_pressed(inp:code_from_token("KEYCODE_Z")) then roty=roty+1
elseif inp:code_pressed(inp:code_from_token("KEYCODE_X")) then roty=roty-1 end
end
delaykey=delaykey or 0
if inp:code_pressed(inp:code_from_token("KEYCODE_S")) and delaykey==0 then spinmode=1-spinmode delaykey=10 end
if delaykey>0 then delaykey=delaykey - 1 end
for obj=0,33 do ztrans=19550 xo=50+85*(obj%6) yo=50+60*int(obj/6) d=600 drawobject(obj,rotx,roty,rotz,2.0) end
manager:machine().screens[":screen"]:draw_text(20,150,"PRESS C TO STOP RENDERING, ARROW KEYS AND Z/X TO SPIN, S TO SPIN",0xff0000ff | (math.floor((ibits/11)*0xff)<<8)| (math.floor((ibits/11)*0xff)<<16))
end -- drawbz
dispatch_list = { }
function frame_dispatcher()
for index,my_func in pairs(dispatch_list) do my_func() end
end
function dispatch_list_remove(a_func)
for my_index,my_func in pairs(dispatch_list) do if my_func == a_func then table.remove(dispatch_list,my_index) end end
end
function print_dispatch_list() for my_index,my_func in pairs(dispatch_list) do print(my_func) end end
function cld()
dispatch_list = {}
end
table.insert(dispatch_list,drawbz)
if not alreadyregisteredframedone then
emu.register_frame_done(frame_dispatcher)
alreadyregisteredframedone=true
end
function printvertextable(i) stuckcount=0 while mem:read_u8(i)~=0xff do m=mem:read_u8(i) print(hex(i,4)..":"..hex(m).." "..hex(getbits(m,3,7)).." "..hex(getbits(m,0,2))) i=i+1 stuckcount=stuckcount+1 if stuckcount>100 then print("STUCK LOOP="..stuckcount) break end end print(hex(i,4)..":"..hex(mem:read_u8(i))) end
function printpointstable(adr) a=mem:read_u8(adr) local count=0 for i=adr+1,adr+1+a-1-1,6 do x=mem:read_i16(i) y=mem:read_i16(i+2) z=mem:read_i16(i+4) print(hex(i,4).." vertex "..count.." ("..int(x)..","..int(y)..","..int(z)..")") count=count+1 end end
--printpointstable(0x3c5a)
--printvertextable(0x74d7)
function printline() print(string.rep("-",40)) end
function printobject(obj)
printline() print("OBJECT "..obj.." "..battlezoneobjecttable[obj+1]) printline()
printpointstable(tablelookup16(obj,0x388e,obj))
printvertextable(tablelookup16(obj,0x7472))
end
printobject(0xf)
-- disable game rendering by setting the displaylist to HALT
print(manager:machine():debugger():command("wp 2000,4,w,1,{w@2000=2020;g}"))
emu.pause()
print(manager:machine():debugger():command("go"))
emu.unpause()
|
|
|
|
Joined: Feb 2014
Posts: 521
Senior Member
|
OP
Senior Member
Joined: Feb 2014
Posts: 521 |
I was looking at the schematics and saw that there's a SLAM switch as well as a coin switch for left, center and right, so let's see if we can hook that IPT_COIN3 and IPT_TILT up: ![[Linked Image from i.imgur.com]](https://i.imgur.com/PTLQ4c2.png) #define BZONEIN0\
PORT_START("IN0")\
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 )\
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN2 )\
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_COIN3 )\
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_TILT )\
PORT_SERVICE( 0x10, IP_ACTIVE_LOW )\
PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("Diagnostic Step") \
/* bit 6 is the VG HALT bit. We set it to "low" */\
/* per default (busy vector processor). */\
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("avg", avg_bzone_device, done_r)\
/* bit 7 is tied to a 3kHz clock */\
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(bzone_state, clock_r)
void bzone_state::bzone_coin_counter_w(offs_t offset, uint8_t data)
{
// machine().bookkeeping().coin_counter_w(offset,data);
for (int i=0; i<3; i++) machine().bookkeeping().coin_counter_w(i,BIT(data,i));
} And now coin B and C will get incremented: (if you want to see it increment, bring up the bookkeeping, take it off with TAB, then hit 567 and hit TAB again to bring back the bookkeeping. Do it fast enough and you'll see the number increment) Interestingly, hitting 567 simultaneously doesn't register, but 567 quickly in sequence registers. Hitting the T for TILT causes the game to screech and ignore the coin input. (No hitting the coinbox to get free plays!) ![[Linked Image from i.imgur.com]](https://i.imgur.com/cy9H1Zo.png)
|
|
|
|
Joined: Feb 2014
Posts: 521
Senior Member
|
OP
Senior Member
Joined: Feb 2014
Posts: 521 |
One of the things that's puzzled me is why the hi-score screen for bzone is diagonal and shifted down onto the words BONUS TANK. It's odd because the hi-score screen for bzonea is perfectly aligned. bzone bzonea ![[Linked Image from i.imgur.com]](https://i.imgur.com/9NSIdhV.png) Apparently, the only difference between bzone and bzonea is the rendering of the hi-score screen text and how many tank icons are displayed. For bzone, the text is rendered each line as an offset of the line above it. For bzonea, each line of text has a CNTR command followed by a move. Any high score above 100K will draw a tank icon in bzone. Bzonea will draw up to 10 tanks per 100K of hi score, going off the screen to do so. (6 1/2 are visible) If you have too many hi scores and tanks onscreen it will clip the rendering of the BONUS TANK line as it overloads the AVG. http://www.vectorlist.org/Vectorlist/2003/07/0046.html Re: bzone and bzone2 roms - whats the diff http://www.vectorlist.org/Vectorlist/2003/07/0046.html Re: Bug: Battlezone high score rendering I took a hexdump and diff'ed it and got this: diff bzone_orighex.txt bzonea_orighex.txt
55c55
< 00005340 67 45 44 52 a2 00 86 d2 8e 3c 03 bc 00 03 c4 b8 |gEDR.....<......|
---
> 00005340 70 45 44 52 a2 00 86 d2 8e 3c 03 bc 00 03 c4 b8 |pEDR.....<......|
77,86c77,86
< 000054a0 20 98 6c 20 6a 7a a0 00 a9 e0 a2 1a 20 8e 7a a2 | .l jz...... .z.|
< 000054b0 00 8e 40 03 ae 40 03 bd 00 03 85 0a 1d 01 03 f0 |..@..@..........|
< 000054c0 7c bd 01 03 85 0b a9 0a 20 9e 7b a2 1c 20 98 6c ||....... .{.. .l|
< 000054d0 ae 40 03 bd 1e 03 20 6a 55 ae 40 03 bd 1f 03 20 |.@.... jU.@.... |
< 000054e0 6a 55 ae 40 03 bd 20 03 20 6a 55 a0 00 ae 40 03 |jU.@.. . jU...@.|
< 000054f0 bd 01 03 f0 1a ad f0 33 91 02 c8 ad f1 33 91 02 |.......3.....3..|
< 00005500 c8 ad 90 35 91 02 c8 ad 91 35 91 02 20 76 7a ae |...5.....5.. vz.|
< 00005510 40 03 e0 1b b0 27 e8 e8 e8 8e 40 03 a9 d8 85 06 |@....'....@.....|
< 00005520 a9 ff 85 07 a9 fe 85 05 98 f0 04 a9 a3 d0 02 a9 |................|
< 00005530 f4 85 04 a9 00 85 01 20 ab 7a 4c b4 54 ad 00 0a |....... .zL.T...|
---
> 000054a0 20 98 6c 20 6a 7a a0 00 8c 40 03 a9 e0 a2 1e 8e | .l jz...@......|
> 000054b0 71 03 20 8e 7a ae 40 03 bd 00 03 85 0a 1d 01 03 |q. .z.@.........|
> 000054c0 f0 7b bd 01 03 85 0b a9 0a 20 9e 7b a2 1c 20 98 |.{....... .{.. .|
> 000054d0 6c ae 40 03 bd 1e 03 20 6a 55 ae 40 03 bd 1f 03 |l.@.... jU.@....|
> 000054e0 20 6a 55 ae 40 03 bd 20 03 20 6a 55 a0 00 ae 40 | jU.@.. . jU...@|
> 000054f0 03 bd 01 03 f0 24 aa ad f0 33 91 02 c8 ad f1 33 |.....$...3.....3|
> 00005500 91 02 e0 0a 90 02 a2 0a c8 ad 90 35 91 02 c8 ad |...........5....|
> 00005510 91 35 91 02 ca d0 f1 20 76 7a ae 40 03 e0 1b b0 |.5..... vz.@....|
> 00005520 1c e8 e8 e8 8e 40 03 20 6a 7a ad 71 03 38 e9 0a |.....@. jz.q.8..|
> 00005530 8d 71 03 aa a9 e0 a0 00 4c b2 54 45 52 ad 00 0a |.q......L.TER...|
cat bzone_dasm_diff.txt
54A0: 20 98 6C jsr $6c98 54A0: 20 98 6C jsr $6c98
54A3: 20 6A 7A jsr $7a6a 54A3: 20 6A 7A jsr $7a6a
54A6: A0 00 ldy #$00 54A6: A0 00 ldy #$00
54A8: A9 E0 lda #$e0 | 54A8: 8C 40 03 sty $0340
54AA: A2 1A ldx #$1a | 54AB: A9 E0 lda #$e0
54AC: 20 8E 7A jsr $7a8e | 54AD: A2 1E ldx #$1e
54AF: A2 00 ldx #$00 | 54AF: 8E 71 03 stx $0371
54B1: 8E 40 03 stx $0340 | 54B2: 20 8E 7A jsr $7a8e
54B4: AE 40 03 ldx $0340 | 54B5: AE 40 03 ldx $0340
54B7: BD 00 03 lda $0300, x | 54B8: BD 00 03 lda $0300, x
54BA: 85 0A sta $0a | 54BB: 85 0A sta $0a
54BC: 1D 01 03 ora $0301, x | 54BD: 1D 01 03 ora $0301, x
54BF: F0 7C beq $553d | 54C0: F0 7B beq $553d
54C1: BD 01 03 lda $0301, x | 54C2: BD 01 03 lda $0301, x
54C4: 85 0B sta $0b | 54C5: 85 0B sta $0b
54C6: A9 0A lda #$0a | 54C7: A9 0A lda #$0a
54C8: 20 9E 7B jsr $7b9e | 54C9: 20 9E 7B jsr $7b9e
54CB: A2 1C ldx #$1c | 54CC: A2 1C ldx #$1c
54CD: 20 98 6C jsr $6c98 | 54CE: 20 98 6C jsr $6c98
54D0: AE 40 03 ldx $0340 | 54D1: AE 40 03 ldx $0340
54D3: BD 1E 03 lda $031e, x | 54D4: BD 1E 03 lda $031e, x
54D6: 20 6A 55 jsr $556a | 54D7: 20 6A 55 jsr $556a
54D9: AE 40 03 ldx $0340 | 54DA: AE 40 03 ldx $0340
54DC: BD 1F 03 lda $031f, x | 54DD: BD 1F 03 lda $031f, x
54DF: 20 6A 55 jsr $556a | 54E0: 20 6A 55 jsr $556a
54E2: AE 40 03 ldx $0340 | 54E3: AE 40 03 ldx $0340
54E5: BD 20 03 lda $0320, x | 54E6: BD 20 03 lda $0320, x
54E8: 20 6A 55 jsr $556a | 54E9: 20 6A 55 jsr $556a
54EB: A0 00 ldy #$00 | 54EC: A0 00 ldy #$00
54ED: AE 40 03 ldx $0340 | 54EE: AE 40 03 ldx $0340
54F0: BD 01 03 lda $0301, x | 54F1: BD 01 03 lda $0301, x
54F3: F0 1A beq $550f | 54F4: F0 24 beq $551a
54F5: AD F0 33 lda $33f0 | 54F6: AA tax
54F8: 91 02 sta ($02), y | 54F7: AD F0 33 lda $33f0
54FA: C8 iny | 54FA: 91 02 sta ($02), y
54FB: AD F1 33 lda $33f1 | 54FC: C8 iny
54FE: 91 02 sta ($02), y | 54FD: AD F1 33 lda $33f1
5500: C8 iny | 5500: 91 02 sta ($02), y
5501: AD 90 35 lda $3590 | 5502: E0 0A cpx #$0a
5504: 91 02 sta ($02), y | 5504: 90 02 bcc $5508
5506: C8 iny | 5506: A2 0A ldx #$0a
5507: AD 91 35 lda $3591 | 5508: C8 iny
550A: 91 02 sta ($02), y | 5509: AD 90 35 lda $3590
550C: 20 76 7A jsr $7a76 | 550C: 91 02 sta ($02), y
550F: AE 40 03 ldx $0340 | 550E: C8 iny
5512: E0 1B cpx #$1b | 550F: AD 91 35 lda $3591
5514: B0 27 bcs $553d | 5512: 91 02 sta ($02), y
5516: E8 inx | 5514: CA dex
5517: E8 inx | 5515: D0 F1 bne $5508
5518: E8 inx | 5517: 20 76 7A jsr $7a76
5519: 8E 40 03 stx $0340 | 551A: AE 40 03 ldx $0340
551C: A9 D8 lda #$d8 | 551D: E0 1B cpx #$1b
551E: 85 06 sta $06 | 551F: B0 1C bcs $553d
5520: A9 FF lda #$ff | 5521: E8 inx
5522: 85 07 sta $07 | 5522: E8 inx
5524: A9 FE lda #$fe | 5523: E8 inx
5526: 85 05 sta $05 | 5524: 8E 40 03 stx $0340
5528: 98 tya | 5527: 20 6A 7A jsr $7a6a
5529: F0 04 beq $552f | 552A: AD 71 03 lda $0371
552B: A9 A3 lda #$a3 | 552D: 38 sec
552D: D0 02 bne $5531 | 552E: E9 0A sbc #$0a
552F: A9 F4 lda #$f4 | 5530: 8D 71 03 sta $0371
5531: 85 04 sta $04 | 5533: AA tax
5533: A9 00 lda #$00 | 5534: A9 E0 lda #$e0
5535: 85 01 sta $01 | 5536: A0 00 ldy #$00
5537: 20 AB 7A jsr $7aab | 5538: 4C B2 54 jmp $54b2
553A: 4C B4 54 jmp $54b4 | 553B: 45 52 eor $52
553D: AD 00 0A lda $0a00 553D: AD 00 0A lda $0a00
5540: 4A lsr a 5540: 4A lsr a
5541: 4A lsr a 5541: 4A lsr a
5542: 4A lsr a 5542: 4A lsr a
5543: 4A lsr a 5543: 4A lsr a
5544: 29 03 and #$03 5544: 29 03 and #$03
5546: F0 1C beq $5564 5546: F0 1C beq $5564
5548: AA tax 5548: AA tax
5549: BD 86 38 lda $3886, x 5549: BD 86 38 lda $3886, x
554C: 18 clc 554C: 18 clc
554D: F8 sed 554D: F8 sed
554E: 69 01 adc #$01 554E: 69 01 adc #$01
So if we change a few memory locations we can get the hiscore of bzone to perfectly match the output of bzonea but there will still be only one tank rendered. Here's a little lua routine to mimic the self-test routine of bzone: function hex(a,digits) digits = digits or 2 return string.format("%0"..digits.."x",a) end
mem2=manager:machine():memory().regions[":maincpu"]
mem=mem2
function bzself()
-- self test initializes to 0xff and then xors every byte in each 0x800 block, after each block the value should be zero, skipping blocks 0x4000-0x47ff and 0x4800-0x4fff
for j=0x0,0x9 do o=0xff for i=0x3000+j*0x800,0x3000+j*0x800+0x7ff do o=o~mem:read_u8(i) end print(j,hex(0x3000+j*0x800),o) end -- self test at $7d01
bzmemlocs={0x5530,0x54a9,0x54ab,0x552c,0x5340}
bzmemlocsdesc={
"hiscore horiz move for next line",
"hiscore first line x pos",
"hiscore first line y pos",
"hiscore horiz move next line w/tank icon",
"self test eor checksum fixer"}
print("-------------")
for i=1,#bzmemlocs do print(hex(i),hex(bzmemlocs[i]),hex(mem2:read_u8(bzmemlocs[i])),bzmemlocsdesc[i]) end
end
On initial start we get:
bzself()
0 3000 0
1 3800 0
2 4000 255
3 4800 255
4 5000 0
5 5800 0
6 6000 0
7 6800 0
8 7000 0
9 7800 0
-------------
01 5530 f4 hiscore horiz move for next line
02 54a9 e0 hiscore first line x pos
03 54ab 1a hiscore first line y pos
04 552c a3 hiscore horiz move next line w/tank icon
05 5340 67 self test eor checksum fixer
mem2:write_u8(0x5530,0xf8) -- f4 to f8 0100 to 1000
mem2:write_u8(0x54ab,0x1e) -- 1a to 1e 1010 to 1110
mem2:write_u8(0x552c,0xa7) -- a3 to a7 0011 to 0111
mem2:write_u8(0x5340,0x6b) -- fix the self test checksum
bzself()
0 3000 0
1 3800 0
2 4000 255
3 4800 255
4 5000 0
5 5800 0
6 6000 0
7 6800 0
8 7000 0
9 7800 0
-------------
01 5530 f8 hiscore horiz move for next line
02 54a9 e0 hiscore first line x pos
03 54ab 1e hiscore first line y pos
04 552c a7 hiscore horiz move next line w/tank icon
05 5340 6b self test eor checksum fixer
If you didn't care about it matching bzonea exactly, you could probably find some values that don't change the eor checksum and look reasonable on screen. Then we get a high score screen that matches: Loaded the fixed bzone and the bzonea and overlaid it with gimp and modified the opacity to see how it lines up. ![[Linked Image from i.imgur.com]](https://i.imgur.com/ABB6hnS.png)
|
|
|
0 members (),
42
guests, and
3
robots. |
Key:
Admin,
Global Mod,
Mod
|
|
Forums9
Topics8,854
Posts116,541
Members4,927
|
Most Online890 Jan 17th, 2020
|
|
|
|