Name all the shapes, begin analysis of which shapes are drawn where
authorNick Downing <nick@ndcode.org>
Tue, 28 Jun 2022 17:17:45 +0000 (03:17 +1000)
committerNick Downing <nick@ndcode.org>
Tue, 28 Jun 2022 17:21:58 +0000 (03:21 +1000)
emu_65c02/emu_65c02.c
galaxian/galaxian.txt
galaxian/shape_extract.py
galaxian/shape_extract_png.py
utils/disasm.py

index 7533c49..a0eaa83 100644 (file)
 
 #define RESET_VECTOR 0xfffc
 
-#define TRACE 1
+#define TRACE 0
 #define MEM_TRACE 0
 
 extern char **environ;
index c552b21..fddad13 100644 (file)
@@ -31,6 +31,18 @@ items
 # also y coordinate temporary storage
 0x0017,0x0001,draw_temp,byte
 0x0020,0x0001,draw_x_save,byte
+# high score in 4-digit BCD
+0x0023,0x0002,high_score,byte
+# cleared when keyboard input occurs
+# I think it controls demo mode, 0 for play mode
+0x0025,0x0001,demo_mode,byte
+# score in 4-digit BCD
+0x0029,0x0002,score,byte
+0x0036,0x0001,starfield_count,byte
+# ships in 2-digit BCD
+0x0069,0x0001,ships,byte
+# wins in 2-digit BCD
+0x0072,0x0001,wins,byte
 0x2000,0x0028,VIDEO_LINE_00,byte
 0x2028,0x0028,VIDEO_LINE_40,byte
 0x2050,0x0028,VIDEO_LINE_80,byte
@@ -223,25 +235,51 @@ items
 0x3f80,0x0028,VIDEO_LINE_3F,byte
 0x3fa8,0x0028,VIDEO_LINE_7F,byte
 0x3fd0,0x0028,VIDEO_LINE_BF,byte
-0x486e,0x0001,velocity_y,byte
-0x4870,0x0001,velocity_x_lo,byte
+0x4854,0x0001,wins_y,byte
+0x4856,0x0001,wins_x,byte
+0x4858,0x0001,ships_y,byte
+0x485a,0x0001,ships_x,byte
+0x485c,0x0001,high_score_y,byte
+0x485e,0x0001,high_score_x,byte
+0x4860,0x0001,score_y,byte
+0x4862,0x0001,score_x,byte
+0x486e,0x0001,y_velocity,byte
+0x4870,0x0001,x_velocity_lo,byte
+0x4886,0x0001,high_score_init,byte
+0x48aa,0x0001,starfield_count_init,byte
+# used for clearing the screen in the beginning
+# seems to be x0, x1, y0, y1
+# it will be offset by the centre values x = 0x6c, y = 0x20
+0x48b2,0x0004,screen_size,byte
+0x48de,0x0010,title_display_delay,byte
+0x48ee,0x0010,title_display_y,byte
+0x48fe,0x0010,title_display_x,byte
 0x7280,0x0380,shape_data_ptr_lo,byte
 0x7600,0x0380,shape_data_ptr_hi,byte
 0x7980,0x0380,shape_width_bytes,byte
 0x7d80,0x0080,shape_height,byte
-0x7e80,0x0001,draw_misc_data,byte
-0x8a80,0x0080,draw_misc_data_ptr_lo,byte
-0x8b00,0x0080,draw_misc_data_ptr_hi,byte
-0x8b80,0x0080,draw_misc_width_bytes,byte
-0x8c00,0x0080,draw_misc_height,byte
+0x7e80,0x0001,misc_shape_data,byte
+0x8a80,0x0080,misc_shape_data_ptr_lo,byte
+0x8b00,0x0080,misc_shape_data_ptr_hi,byte
+0x8b80,0x0080,misc_shape_width_bytes,byte
+0x8c00,0x0080,misc_shape_height,byte
+0x8c81,0x0028,star_x,byte
 0x8cf1,0x0008,y_8cf1,byte
 0x8d01,0x0008,velocity_hi_8d01,byte
 0x8d11,0x0008,x_hi_8d11,byte
 0x8d21,0x0008,x_lo_8d21,byte
+0x8d31,0x0028,star_y,byte
+0x8df1,0x0028,star_shape_left,byte
+0x8e51,0x0028,star_shape_right,byte
+0x8eb1,0x0028,star_y_velocity,byte
 0x8f71,0x0001,y_8f71,byte
+0x8f79,0x0028,move_starfield_table,byte
 0x8fe1,0x0001,x_8fe1,byte
 0x9028,0x0012,y_9028,byte
 0x90a8,0x0012,x_90a8,byte
+0x937c,0x0001,init_bvar_0019,byte
+# see screen_size
+0x93a4,0x0004,screen_offset,byte
 # initialize clip_rect, clear screen, set hires
 0x93a8,0x0001,video_init,code
 0x93be,0x0001,,code_ign # self-modifying code assembled with garbage word
@@ -284,7 +322,7 @@ items
 0x94e5,0x0001,,code_ign # accessing video_line_table before clipping y
 0x94fe,0x0001,,code_ign # self-modifying code assembled with garbage word
 0x94ff,0x0002,,word
-0x951c,0x0002,draw_misc_data_ptr_base,word
+0x951c,0x0002,misc_shape_data_ptr_base,word
 # draws over previous video contents
 # a = shape to draw
 # draw_x1 = x0 for drawing and is advanced by routine
@@ -294,6 +332,16 @@ items
 0x9577,0x00c0,video_line_table_lo,byte,byte,10000
 0x9637,0x00c0,video_line_table_hi,byte,byte,10000
 0x9707,0x0001,start,code
+# initializes zpage, and probably other init, and draws something
+0x9780,0x0001,zpage_init,code
+0x9784,0x0001,,code_ign # zeroing zpage, don't merge cleared variables
+# used for colouring the starfield
+0x983e,0x0001,star_mask_table_left,byte
+0x9842,0x0001,star_mask_table_right,byte
+# called when star goes off screen at y = 0xe0
+# randomize the x position for restart at y = 0x20
+0x9846,0x0001,regenerate_star,byte
+0x989e,0x0001,move_starfield,code
 0x98e0,0x0001,,code_ign # accessing video_line_table before clipping y
 0x98e5,0x0001,,code_ign # accessing video_line_table before clipping y
 0x990a,0x0001,,code_ign # accessing video_line_table before clipping y
@@ -302,12 +350,12 @@ items
 0x9ef7,0x0001,physics_9ef7,code
 0x9f22,0x0001,physics_9f22,code
 0x9f5b,0x0001,add_velocity_to_position,code
-0x9f7d,0x0001,add_velocity_x_to_position,code
+0x9f7d,0x0001,add_x_velocity_to_position,code
 0x9f88,0x0001,move_shape_9f88,code
-0x9fcf,0x0001,move_shape_01,code
-0xa018,0x0001,move_shape_02,code
+0x9fcf,0x0001,move_missile,code
+0xa018,0x0001,move_shell,code
 0xa0f4,0x0001,clip_xy,code
-0xa10d,0x0001,move_shape_00,code
+0xa10d,0x0001,move_ship,code
 0xa12e,0x0001,move_shape_a12e,code
 # returns a = x, y = y
 0xa156,0x0001,get_new_position,code
@@ -317,16 +365,27 @@ items
 0xa205,0x0001,get_old_position,code
 0xa22e,0x0001,move_shape_a22e,code
 0xa264,0x0001,erase_shape_a264,code
-0xa281,0x0001,erase_shape_01,code
-0xa2a6,0x0001,erase_shape_02,code
-0xa2cb,0x0001,draw_misc_a2cb,code
-0xa332,0x0001,draw_misc_a332,code
-0xa354,0x0001,draw_misc_a345,code
-0xa376,0x0001,do_draw_digit_pair,code
+0xa281,0x0001,erase_missile,code
+0xa2a6,0x0001,erase_shell,code
+# draws "score" followed by 4 digits followed by "0"
+# also updates high score if new score exceeds high score
+0xa2cb,0x0001,draw_score,code
+# draws "high score" followed by 4 digits followed by "0"
+0xa30c,0x0001,draw_high_score,code
+0xa332,0x0001,draw_ships,code
+0xa354,0x0001,draw_wins,code
+0xa376,0x0001,set_base_draw_digit_pair,code
 0xa379,0x0001,draw_digit_pair,code
-0xa389,0x0001,do_draw_digit,code
+0xa385,0x0001,set_base_draw_hi_digit,code
+0xa389,0x0001,set_base_draw_digit,code
 0xa38c,0x0001,draw_digit,code
 0xa390,0x0001,draw_digit_base,byte
+# a = x (saved to draw_x1), y = y (saved to draw_erase_y0)
+0xa394,0x0001,set_cursor,byte
+# draws shapes draw_misc_50 through 5f at title_display_x, title_display_y
+0xa399,0x0001,title_display,byte
+# does x * 0x1000 loops, can be cut short by keyboard input
+0xa3db,0x0001,delay_x,byte
 0xc000,0x0001,HW_KBD,byte
 0xc010,0x0001,HW_KBDSTRB,byte
 0xc030,0x0001,HW_SPKR,byte
@@ -338,3 +397,112 @@ items
 0xc062,0x0001,HW_PB1,byte
 0xc065,0x0001,HW_PADDL1,byte
 0xc070,0x0001,HW_PTRIG,byte
+
+shape_names
+0x00,ship
+0x01,missile
+0x02,shell
+0x04,ship_explosion
+0x05,ship_explosion
+0x06,ship_explosion
+0x07,ship_explosion
+0x08,alien_foot
+0x09,alien_foot
+0x0a,alien_foot
+0x0b,alien_foot
+0x0c,alien_foot
+0x0d,alien_foot
+0x0e,alien_foot
+0x0f,alien_foot
+0x10,alien_explosion
+0x11,alien_explosion
+0x12,alien_explosion
+0x13,alien_explosion
+0x14,alien_foot
+0x15,alien_foot
+0x16,alien_foot
+0x17,alien_foot
+0x18,text_100
+0x19,text_200
+0x1a,text_300
+0x1b,text_500
+0x28,alien_head
+0x29,alien_head
+0x2a,alien_head
+0x3c,alien_head
+0x3d,alien_head
+0x3e,alien_head
+0x50,flagship
+0x51,flagship
+0x52,flagship
+
+misc_shape_names
+0x00,signature
+0x02,text_score
+0x03,text_score_0
+0x04,text_hiscore
+0x05,text_hiscore_0
+0x06,text_ships
+0x07,text_ships_blank
+0x08,text_wins
+0x09,text_wins_blank
+0x14,text_0
+0x15,text_1
+0x16,text_2
+0x17,text_3
+0x18,text_4
+0x19,text_5
+0x1a,text_6
+0x1b,text_7
+0x1c,text_8
+0x1d,text_9
+0x1e,text_0
+0x1f,text_1
+0x20,text_2
+0x21,text_3
+0x22,text_4
+0x23,text_5
+0x24,text_6
+0x25,text_7
+0x26,text_8
+0x27,text_9
+0x28,ships_0
+0x29,ships_1
+0x2a,ships_2
+0x2b,ships_3
+0x32,blank
+0x3c,flags_0
+0x3d,flags_1
+0x3e,flags_2
+0x3f,flags_3
+0x40,flags_4
+0x41,flags_5
+0x42,flags_6
+0x43,flags_7
+0x44,flags_8
+0x45,flags_9
+0x46,flags_00
+0x47,flags_10
+0x48,flags_20
+0x49,flags_30
+0x4a,flags_40
+0x4b,flags_50
+0x4c,flags_60
+0x4d,flags_70
+0x4e,flags_80
+0x4f,flags_90
+0x50,text_galaxian
+0x51,text_by
+0x52,text_tony_suzuki
+0x53,text_copyright_1980
+0x54,text_star_craft
+0x55,text_tokyo
+0x56,text_points
+0x57,flagship
+0x58,text_random_or_50
+0x59,alien
+0x5a,text_50_or_30
+0x5b,alien
+0x5c,text_30_or_10
+0x5d,text_special_add_1_ship_at_3000_pts
+0x64,bullet
index 3293ca0..e908e82 100755 (executable)
@@ -18,9 +18,10 @@ out_json = sys.argv[3]
 print('reading addrs')
 shape_tables = {}
 shape_data_ptr_base = -1
-draw_misc_tables = {}
-draw_misc_data_ptr_base = -1
-shapes = {}
+misc_shape_tables = {}
+misc_shape_data_ptr_base = -1
+shape_names = {}
+misc_shape_names = {}
 with open(addrs_txt) as fin:
   def get_line():
     while True:
@@ -52,23 +53,33 @@ with open(addrs_txt) as fin:
         if _type == 'byte':
           if name[:6] == 'shape_':
             shape_tables[name[6:]] = (addr, size // 0x80)
-          elif name[:10] == 'draw_misc_':
-            draw_misc_tables[name[10:]] = (addr, size // 0x80)
+          elif name[:11] == 'misc_shape_':
+            misc_shape_tables[name[11:]] = (addr, size // 0x80)
         elif _type == 'word':
           if name == 'shape_data_ptr_base':
             shape_data_ptr_base = addr
-          elif name == 'draw_misc_data_ptr_base':
-            draw_misc_data_ptr_base = addr
+          elif name == 'misc_shape_data_ptr_base':
+            misc_shape_data_ptr_base = addr
         fields = get_line()
       continue
 
-    if section == 'shapes':
+    if section == 'shape_names':
       fields = get_line()
       while len(fields) >= 2:
         assert len(fields) == 2
         index = int(fields[0], 0)
         name = fields[1]
-        shapes[index] = name
+        shape_names[index] = name
+        fields = get_line()
+      continue
+
+    if section == 'misc_shape_names':
+      fields = get_line()
+      while len(fields) >= 2:
+        assert len(fields) == 2
+        index = int(fields[0], 0)
+        name = fields[1]
+        misc_shape_names[index] = name
         fields = get_line()
       continue
 
@@ -77,7 +88,7 @@ with open(addrs_txt) as fin:
     while len(fields) >= 2:
       fields = get_line()
 assert shape_data_ptr_base != -1
-assert draw_misc_data_ptr_base != -1
+assert misc_shape_data_ptr_base != -1
 
 print('reading ihx')
 intelhex = IntelHex(in_ihx)
@@ -86,14 +97,21 @@ for i in range(0, len(segments), 2):
   print(f'[{segments[i]:04x}, {segments[i + 1]:04x})')
 
 print('extracting')
-data = []
-for i in range(0x100):
-  name = f'shape_{i:02x}' if i < 0x80 else f'draw_misc_{i - 0x80:02x}'
-  if i in shapes:
-    name += '_' + shapes[i]
-  data.append({'name': name})
+shapes = []
+for base, prefix, names in [
+  (0, 'shape', shape_names),
+  (0x80, 'misc_shape', misc_shape_names)
+]:
+  for i in range(0x80):
+    name = f'{prefix:s}_{i:02x}'
+    if i in names:
+      name += '_' + names[i]
+    shapes.append({'name': name})
 
-for base, tables in [(0, shape_tables), (0x80, draw_misc_tables)]:
+for base, tables in [
+  (0, shape_tables),
+  (0x80, misc_shape_tables)
+]:
   i = 0
   tables1 = list(tables.items())
   while i < len(tables1):
@@ -111,7 +129,7 @@ for base, tables in [(0, shape_tables), (0x80, draw_misc_tables)]:
             (intelhex[data_ptr_hi + j + k * 0x80] << 8)
           for k in range(shifts)
         ]
-        data[base + j][table] = [f'0x{k:04x}' for k in value]
+        shapes[base + j][table] = [f'0x{k:04x}' for k in value]
       i += 2
     else:
       table, (data_ptr, shifts) = tables1[i]
@@ -120,7 +138,7 @@ for base, tables in [(0, shape_tables), (0x80, draw_misc_tables)]:
           intelhex[data_ptr + j + k * 0x80]
           for k in range(shifts)
         ]
-        data[base + j][table] = [str(k) for k in value]
+        shapes[base + j][table] = [str(k) for k in value]
       i += 1
 
 for base, data_ptr_base in [
@@ -131,24 +149,24 @@ for base, data_ptr_base in [
   ),
   (
     0x80,
-    intelhex[draw_misc_data_ptr_base] |
-      (intelhex[draw_misc_data_ptr_base + 1] << 8)
+    intelhex[misc_shape_data_ptr_base] |
+      (intelhex[misc_shape_data_ptr_base + 1] << 8)
   )
 ]:
   for i in range(0x80):
-    height = int(data[base + i]['height'][0], 0)
-    width_bytes = [int(j, 0) for j in data[base + i]['width_bytes']]
-    data_ptr = [int(j, 0) for j in data[base + i]['data_ptr']]
+    height = int(shapes[base + i]['height'][0], 0)
+    width_bytes = [int(j, 0) for j in shapes[base + i]['width_bytes']]
+    data_ptr = [int(j, 0) for j in shapes[base + i]['data_ptr']]
 
     # only try to extract if data is sensible
-    data1 = None
+    data = None
     if (
       height >= 1 and
         height < 46 and
         all([j >= 1 and j < 40 for j in width_bytes]) and
         all([j < 0x1000 for j in data_ptr])
     ):
-      data1 = [
+      data = [
         [
           [
             f'0x{intelhex[data_ptr_base + data_ptr[j] + k * width_bytes[j] + l]:02x}'
@@ -158,8 +176,8 @@ for base, data_ptr_base in [
         ]
         for j in range(len(width_bytes))
       ]
-    data[base + i]['data'] = data1
+    shapes[base + i]['data'] = data
 
 print('writing json')
 with open(out_json, 'w') as fout:
-  json.dump(data, fout, indent = 2)
+  json.dump(shapes, fout, indent = 2)
index f84a743..2115270 100755 (executable)
@@ -42,7 +42,7 @@ out_png = sys.argv[2]
 
 print('reading json')
 with open(in_json) as fin:
-  data = json.load(fin)
+  shapes = json.load(fin)
 
 image_out = numpy.zeros((PITCH_Y * 32, PITCH_X * 8), numpy.uint8)
 
@@ -54,10 +54,10 @@ for i in range(0x100):
   bg = 0xa if (j ^ k) & 1 else 5
   image_out[y:y + PITCH_Y, x:x + PITCH_X] = bg
 
-  data1 = data[i]['data']
-  if data1 is not None:
+  data = shapes[i]['data']
+  if data is not None:
     shape = numpy.array(
-      [[int(k, 0) for k in j] for j in data1[0]],
+      [[int(k, 0) for k in j] for j in data[0]],
       numpy.uint8
     )
     ys = shape.shape[0]
@@ -80,9 +80,9 @@ for i in range(0x100):
     ).astype(bool).reshape((ys, xs))
 
     # check remaining (shifted) shapes are as we expect
-    for j in range(1, len(data1)):
+    for j in range(1, len(data)):
       shape1 = numpy.array(
-        [[int(l, 0) for l in k] for k in data1[j]],
+        [[int(l, 0) for l in k] for k in data[j]],
         numpy.uint8
       )
       ys1 = shape1.shape[0]
index 072d602..a824f31 100755 (executable)
@@ -209,6 +209,8 @@ with open(addrs_txt) as fin:
           mate = tuple([int(i, 0) for i in fields[7].split()])
           assert len(mate) == 2
 
+        # enable this to see which address is out of order
+        #print(hex(addr))
         assert (
           len(item_addr) == 0 or
             addr >= item_addr[-1] + item_info[-1].size