Home Page

Battlezone fun

Posted By: Golden Child

Battlezone fun - 02/02/20 12:01 PM

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)


Code

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]

A few more sine waves and I could be at the Outer Limits.

[Linked Image from i.imgur.com]

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=80s

I 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]
Posted By: Golden Child

Re: Battlezone fun - 02/23/20 04:38 PM


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.html

which pointed me to the avg docs:

http://www.ionpool.net/arcade/atari_docs/avg.pdf
http://arcarc.xmission.com/Tech/neilw_xy.txt

The 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]

[Linked Image from i.imgur.com]

the moon

[Linked Image from i.imgur.com]



Code

-- 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

Posted By: Golden Child

Re: Battlezone fun - 03/12/20 05:52 PM

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]

Code
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.

Code
-- 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]
Posted By: R. Belmont

Re: Battlezone fun - 03/12/20 11:22 PM

That's really cool!
Posted By: Golden Child

Re: Battlezone fun - 03/14/20 04:26 PM

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]

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.

Code
[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).

Code
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:

Code
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:
Code
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.

Code
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.
Code
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)


Code
/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...

Code
  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:
Code
? 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:

Code
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

Code
[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
Code
[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.
Posted By: Golden Child

Re: Battlezone fun - 03/14/20 06:02 PM


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
Code
  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:

Code
?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:

Code
[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:
Code
[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.
Code
[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:
Code
[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.

Code
[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.



[Linked Image from i.imgur.com]

-> change 0x3c77 from 0xffd8 to 0 -> (changing the z coordinate from -40 to 0)

[Linked Image from i.imgur.com]

and you can see the vertex move right up to the horizon line.

[Linked Image from i.imgur.com]
[Linked Image from i.imgur.com]
Posted By: Golden Child

Re: Battlezone fun - 03/14/20 09:26 PM

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:
Code
  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.
Code
  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:
Code
[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:
Code
[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)
Code
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

Code
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


Code
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
Posted By: Golden Child

Re: Battlezone fun - 03/15/20 03:28 AM

Once we know the points and the vertexes, let's see if we can draw the 3d objects.

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

Code
--
-- 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()



Posted By: Golden Child

Re: Battlezone fun - 03/22/20 03:42 PM

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]

Code
#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]
Posted By: Golden Child

Re: Battlezone fun - 03/23/20 04:06 AM

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
[Linked Image from i.imgur.com]

bzonea
[Linked Image from i.imgur.com]

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:

Code
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...|


Code
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:

Code
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:
Code
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]
Posted By: Al Kossow

Re: Battlezone fun - 03/23/20 11:51 AM

There is a bug that gives you a lot of tanks. Maybe it is documented somewhere.
I saw someone do it in an arcade in the 80s, but I could never catch the guy using it (he always did it when no one was watching)
Posted By: Golden Child

Re: Battlezone fun - 03/24/20 01:01 AM

I've been reading about the highscore bug on a few forums.

According to biltronix at http://www.biltronix.com/battlezone.html
Quote

Hey, check out that wild high score eh? I know what you're thinking, it's a photoshop trick or something right? No, it's quite real alright. While it may not actually be an impossible score to achieve, I know I'll never even come close. I got the 4,537,000 score while playing but some sort of game program bug produced it. I took a shot at a saucer and while the shot was still in flight, a missile came at me. I kept clicking the fire button hoping it would shoot again before I got nailed. It did shoot again and I got the saucer and the missile so close to being at the same time that the sounds of the hits interfered with each other and suddenly the crazy high score appeared. I have a hi-score save module installed in my machine so it faithfully saved and restored it so I guess it's not going anywhere unless I decide to reset the scores. Nah.


(Scoring Millions of Points in Battlezone in Seconds)
https://forums.arcade-museum.com/showthread.php?t=244380

(Battlezone - 7 million points!)
http://www.classicarcadegaming.com/forums/index.php/topic,3302.0.html

(Scoring Millions in seconds in Upright Arcade BattleZone)
https://www.mameworld.info/ubbthreads/showflat.php?Cat=1&Number=292881


I've been looking at the screenshots of the high scores and it's interesting that none of them have the diagonally shifted high score screen of bzone (with the single tank icon for scores 100k+.)

https://forums.arcade-museum.com/showthread.php?t=244380&page=8


The player's score is at $b8 and $b9 so something must be stomping on them.



https://www.retroinjection.com/post...cade-courtesy-of-the-best-snowstorm-ever
Quote
Battlezone, a first-person perspective shooter, is one of my favorite vector games. How can you not love looking through that viewfinder? Kent's daughter managed to trigger a notoriously-difficult glitch in the game, awarding her this astronomical high score.
Posted By: Golden Child

Re: Battlezone fun - 03/24/20 01:15 AM

More silly fun, let's draw the start led on the screen (startled in the upper left corner)
[Linked Image from i.imgur.com]


Code
dispatch_list = { }

function frame_dispatcher()
for index,my_func in pairs(dispatch_list) do  my_func() end
end

function cld()
dispatch_list = {}
end

if not alreadyregisteredframedone then
emu.register_frame_done(frame_dispatcher)
alreadyregisteredframedone=true
end

function drawstartled() 
if manager:machine():outputs():get_value("startled")==1 then 
  manager:machine().screens[":screen"]:draw_text(5,5,"startled")  
end 
inp = manager:machine():input() if inp:code_pressed(inp:code_from_token("KEYCODE_S")) then dispatch_list={} end
end

table.insert(dispatch_list,drawstartled)



I tried the same thing with foodfight, it has them named "led0" and "led1".

Code
function drawled(ledname,x,y)
if manager:machine():outputs():get_value(ledname)==1 then 
 manager:machine().screens[":screen"]:draw_text(x,y,ledname)  
end
end

function drawstartled() 
drawled("led0",5,5)
drawled("led1",5,15)
inp = manager:machine():input() if inp:code_pressed(inp:code_from_token("KEYCODE_S")) then dispatch_list={} end
end

Posted By: Golden Child

Re: Battlezone fun - 03/27/20 05:29 PM

If we know where the locations for our position and direction are in memory, let's see if we can move things around under lua's control.

Moving the player under the control of a lua script:

[Linked Image from i.imgur.com]

Player position is stored at 2d/2e for x position and 31/32 for y position.
Tank position is stored at 2f/30 for x position and 33/34 for y position.
Tank direction is stored at 29/2c 29=lsb and 2c=msb.

The player's direction is stored at 27 = lsb and 2a = msb. When 27/2a are set to 0000 that precisely lines up on the right side of the moon with the target.

Interestingly, only the msb of the direction gets reset on the start of the game, when the game starts it will be very close to the edge of the moon.

[Linked Image from i.imgur.com]

This script will move the tank around, spin it, and move the player around also. It's kind of fun to "trap" the missile with the script and watch it rotate.

Code
mem=manager:machine().devices[":maincpu"].spaces["program"]
mem2=manager:machine():memory().regions[":maincpu"]
function hex(a,digits) digits = digits or 2 return string.format("%0"..digits.."x",a) end

function setlives(l) l=int(l) mem:write_u8(0xcc,l) end
function setscore(s) s=int(s) mem:write_u16(0xb8,s) end

function setmypos(x,y) mem:write_u16(0x2d,int(x)) mem:write_u16(0x31,int(y)) end
function setmydir(d) d=int(d) mem:write_u8(0x27,d&0xff) mem:write_u8(0x2a,(d&0xff00)>>8)  end

function settankposx(x) mem:write_u16(0x2f,int(x)) end
function settankposy(y) mem:write_u16(0x33,int(y)) end
function settankpos(x,y) settankposx(x) settankposy(y) end
function settankdir(d) d=int(d) mem:write_u8(0x29,d&0xff) mem:write_u8(0x2c,(d&0xff00)>>8)  end 

function delay(delaytime)  delaytime = delaytime or 4 emu.wait(delaytime * 1/60) end

function int(i) return math.floor(i) end

function movetank2()
-- move enemy tank in a triangle
movetable = { 10,0,  50,0,  50,10, 10,0, 50,-20, 10,0,}
for pos=1,#movetable-2,2 do
   x1=movetable[pos+0] y1=movetable[pos+1] 
   x2=movetable[pos+2] y2=movetable[pos+3]
   dist=math.sqrt((x2-x1)^2 + (y2-y1)^2)
   vel=20/60
   t=dist/vel
   frames=t
   --print(pos,x1,y1,x2,y2)
   for f=1,frames do
       x=(x2-x1)/frames * f + x1
       y=(y2-y1)/frames * f + y1
       settankpos(x*256,y*256)
       setmypos(0,0)
       setmydir(0)
       print(f,frames,x,y) 
       delay(1)
  end
end
-- spin the enemy tank or missile around 3 times
for i=1,3 do
for d=0,255 do
  setmypos(0,0)
  setmydir(0)
  settankdir(d*256)
  settankpos(0x600,0)
  setlives(4)
  setscore(d)
  delay(2)
end
end
-- move me and fix the enemy tank right in front of me
movetable = { 10,0,  50,0,  50,10, 10,0, 50,-20, 10,0,}
for pos=1,#movetable-2,2 do
   x1=movetable[pos+0] y1=movetable[pos+1] 
   x2=movetable[pos+2] y2=movetable[pos+3]
   dist=math.sqrt((x2-x1)^2 + (y2-y1)^2)
   vel=20/60
   t=dist/vel
   frames=t
   --print(pos,x1,y1,x2,y2)
   for f=1,frames do
       x=(x2-x1)/frames * f + x1
       y=(y2-y1)/frames * f + y1
       setmypos(x*256,y*256)
       settankpos(x*256+0x401,y*256)
       setmydir(0)
       setlives(4)
       setscore(f)
       print(f,frames,x,y) 
       delay(1)
    end
end
end

co1=coroutine.create(movetank2) coroutine.resume(co1)
Posted By: Golden Child

Re: Battlezone fun - 04/04/20 04:59 PM

Not exactly battlezone specifically related, but I recently came across Steven Hugg's 8bitworkshop site and it has on-line compilers and emulators for atari vector-like hardware. That site is amazing for the instantaneous assembly and compilation. Super cool.

[Linked Image from i.imgur.com]

https://8bitworkshop.com/v3.5.0/?platform=vector-z80color&file=threed.c
Posted By: Dullaron

Re: Battlezone fun - 04/05/20 06:39 AM

There scan lines in there? I see something there. Good effect.
Posted By: R. Belmont

Re: Battlezone fun - 04/05/20 12:04 PM

I hope not, because vector monitors don't have scan lines.
Posted By: Golden Child

Re: Battlezone fun - 04/05/20 10:01 PM

Being fascinated by vector graphics I was looking for stuff on the Star Wars trench sequence and found a few articles and videos.


https://www.chicagotribune.com/ente...rs-evl-lab-ent-0524-20170523-column.html
https://www.evl.uic.edu/entry.php?id=2297

Making of the Computer Graphics for Star Wars (Episode IV)
[video:youtube]https://www.youtube.com/watch?v=yMeSw00n3Ac[/video]

Aura Technologies Revives Real Death Star "Main Computer"
[video:youtube]https://www.youtube.com/watch?v=VedPXjAQT8Q[/video]


Here's a video about the restoration of the Vector General 3DR and the star wars animation: (54 minutes of pure awesomeness)

Restoring the Machine that Destroyed the Death Star | VCFMW 2019
[video:youtube]https://www.youtube.com/watch?v=_mtLUws1bOk[/video]

You could hook up to 4 monitors and 4 sets of dials. Wow.
Posted By: fadden

Re: Battlezone fun - 05/23/20 05:59 PM

Amazing work!

I recently completed a full disassembly and analysis of Battlezone. I wish I'd found this thread back in April; it would have saved me a bit of fumbling around (e.g. I couldn't figure out why it was looking at "unused" I/O bits to decide if it should make a sound; somebody on a different forum figured out that it was the slam switch).

I've posted the disassembly at https://6502disassembly.com/va-battlezone/ . Among other things, it explains why the 3D meshes look squashed in your renderings.

I haven't figured out what might cause the multi-million-point score bug. The rev1/rev2 change added the diagonal offset to the high score display. I suspect the reason you don't see it in some of the screen shots is because people are using a "high score save kit", which replaces the code in question. Otherwise, you should either see the staggered list or a row of tank icons extending off the screen.
Posted By: Golden Child

Re: Battlezone fun - 05/24/20 07:07 AM

Super cool, brother! Your disassembly is awesome! I like how you integrated the pictures of the shapes. Genius!


My disassembly technique was to take the dasm output and copy it into MS Word, set up 5 columns wide with no margins and print it out on 20 pages. 8-)


One of the things I'd been fiddling with lately was figuring out the 3d models from Stellar 7, you might have a gander at https://www.calormen.com/jsbasic/ and look in the sample programs.
Posted By: fadden

Re: Battlezone fun - 05/24/20 02:45 PM

Originally Posted by Golden Child
I like how you integrated the pictures of the shapes.


I think we had similar goals, but wildly different approaches. :-) I'm new to MAME, and had no idea the debugger could do the things you showed.

Originally Posted by Golden Child
One of the things I'd been fiddling with lately was figuring out the 3d models from Stellar 7, you might have a gander at https://www.calormen.com/jsbasic/ and look in the sample programs.


Stellar 7 is pretty straightforward once you figure out that
it's using polar coordinates
, though even figuring out which of the various files holds the data takes some digging. Elite was a bit more twisty... between the backface culling and the subtle bugs nobody noticed, it took a while to sort out, even with a commented BBC Micro disassembly.
Posted By: Dullaron

Re: Battlezone fun - 05/24/20 03:55 PM

There used to be a iOS Tank game base off of the Battlezone. Atari made the dev removed it. https://www.pocketgamer.com/article...i-onto-ios-under-the-name-panzerkampf-3/ That long time ago. Here is a video. https://www.youtube.com/watch?v=73htOPb6bEw Too bad I didn't get it.
Posted By: R. Belmont

Re: Battlezone fun - 05/24/20 04:58 PM

Originally Posted by fadden
I'm new to MAME, and had no idea the debugger could do the things you showed.


The debugger's quite powerful (conditional breakpoints, for instance) and supports driver-specific extensions. 8-bit Apple IIs show Monitor entry points, zero page locations, $C0XX soft switches, and ProDOS 8 calls by name in the disassembly, for example. For Battlezone it'd be possible to have the disassembler show the variable names and add a custom command to disassemble the display lists.

But most of what Golden Child did was actually through the Lua script engine. Scripts have pretty complete access to the emulated system (including being able to draw overlays on top of the emulated system's video), and can actually play the games.
Posted By: fadden

Re: Battlezone fun - 05/24/20 09:24 PM

Originally Posted by R. Belmont
For Battlezone it'd be possible to have the disassembler show the variable names and add a custom command to disassemble the display lists.


Are the symbol lists dynamic? I see "symlist" but no "symload" in the help text. One of the things I want to add to SourceGen is symbol table export, so you can annotate the code you're playing with in the emulator.

There are multiple types of symbols, some easier to handle than others:

  • global labels for variables and code
  • non-unique local labels for code ("@loop")
  • local variables (used for zero-page variables)
  • constants


All of the above can be converted to unique global symbols. Some people have been running assemblers in a mode that generates a symbol table (e.g. this), and then parsing that table, but that approach limits you to whatever the assembler wants to support.
Posted By: R. Belmont

Re: Battlezone fun - 05/25/20 01:43 AM

The symbol list shown by symlist is MAME-internal symbols. The current infrastructure for showing labels for memory locations in the debugger is a bit too low-level (you get the opcodes and operands and either pass back the disassembled instruction text with the label included or tell the debugger to handle it as usual) and it'd be nice if there was a cleaner way to handle that stuff, including loadable symbol tables. (On the other hand, I'm not sure how you'd do things like ProDOS calls or 68000 A-line traps without that lower-level interface).

AJR had a commit yesterday hinting at a debugger refactoring; we'll see what that involves.
Posted By: Golden Child

Re: Battlezone fun - 05/26/20 10:34 AM

Wow, the battlezone disassembly is really comprehensive! And all the others too. Awesome! That's exactly why I've been looking for. I'm just trying to absorb all the greatness...

The 6502bench SourceGen tool looks really interesting. That screenshot of crossrunner? looked really cool. I watched the SourceGen youtube videos (and I didn't think they were boring).


It'd be really slick if the mame debugger could display source code and sync with assembler listings like the c64 debugger in this video:
https://www.youtube.com/watch?v=ZU6TQN5Gi5g (KickAssembler + C64 Debugger integration)

Or something like ICU64 where you could have alternate views of memory:
https://www.youtube.com/watch?v=tjcvR5McmSg

I was thinking of trying to open up an additional screen where I could draw things with lua, that could do things like visualize bitmap data in memory.
© 2020 Forums