Page 1 of 1

[Source] Execute Digital Vector Generator (DVG) Instructions in PB

Posted: Sat Aug 09, 2025 1:38 pm
by Mijikai
Recently 8-Bit Show And Tell made a video about a C64 Asteroids Emulator mentioning the DVG.
Long story short: Executing DVG instructions in PB sounded like a fun litte project. :D

Note that the project is still WIP and unfinished, it needs more testing & functions,
anyway i thought its ok enough to release for others to play around. 8)

Links:
Youtube - 8-Bit Show And Tell: C64 Asteroids Emulator? How It Works + SuperCPU Gameplay
Computer Archeology: Asteriods DVG
GitHub - nmikstas: Asteroids Vector Rom

The include:

Code: Select all

EnableExplicit

;-------------------------------------------------------------------------
; Project: Execute Digital Vector Generator (DVG) Instructions
;-------------------------------------------------------------------------
; Inspired by 8-Bit Show And Tell (youtube):
; - https://www.youtube.com/watch?v=5f15S1vVCSk
; Based on information found here:
; - https://computerarcheology.com/Arcade/Asteroids/DVG.html
; - https://github.com/nmikstas/asteroids-disassembly/blob/master/
;   AsteroidsSource/asteroids_vector_rom.asm
;-------------------------------------------------------------------------
; Version: dev.1.20 (WIP)
; Author:  Mijikai
;-------------------------------------------------------------------------

Structure _DVG_WORDS
  y.w
  x.w
EndStructure

Structure _DVG_BYTES
  b.b
  a.b
  d.b
  c.b
EndStructure

Structure _DVG_OPCODE
  StructureUnion
    bytes._DVG_BYTES
    words._DVG_WORDS
  EndStructureUnion
EndStructure

Structure _DVG
  *program
  *offset
  *opcode._DVG_OPCODE
  *stack[4]
  scale_index.i
  scale_global.d
  scale_local.d
  scale_table_global.d[16]
  scale_table_local.d[16]
  brightness.i
  offset_x.i
  offset_y.i
  delta_x.i
  delta_y.i
  *callback
EndStructure

Enumeration DVG_OPCODE
  #DVG_OPCODE_VEC_0
  #DVG_OPCODE_VEC_1
  #DVG_OPCODE_VEC_2
  #DVG_OPCODE_VEC_3
  #DVG_OPCODE_VEC_4
  #DVG_OPCODE_VEC_5
  #DVG_OPCODE_VEC_6
  #DVG_OPCODE_VEC_7
  #DVG_OPCODE_VEC_8
  #DVG_OPCODE_VEC_9
  #DVG_OPCODE_CUR
  #DVG_OPCODE_HALT
  #DVG_OPCODE_JSR
  #DVG_OPCODE_RTS
  #DVG_OPCODE_JMP
  #DVG_OPCODE_SVEC
EndEnumeration

Procedure.i dvg_callback(x1.i,y1.i,x2.i,y2.i,brightness.i)
  ProcedureReturn #Null
EndProcedure

Procedure.i dvg_reset(*dvg._DVG)
  With *dvg
    ResetStructure(*dvg,_DVG)
    \scale_table_global[0] = 1
    \scale_table_global[1] = 2
    \scale_table_global[2] = 4
    \scale_table_global[3] = 8
    \scale_table_global[4] = 16
    \scale_table_global[5] = 32
    \scale_table_global[6] = 64
    \scale_table_global[7] = 128
    \scale_table_global[8] = 1 / 256
    \scale_table_global[9] = 1 / 128
    \scale_table_global[10] = 1 / 64
    \scale_table_global[11] = 1 / 32
    \scale_table_global[12] = 1 / 16
    \scale_table_global[13] = 1 / 8
    \scale_table_global[14] = 1 / 4
    \scale_table_global[15] = 1 / 2
    \scale_table_local[0] = 1 / 512
    \scale_table_local[1] = 1 / 256
    \scale_table_local[2] = 1 / 128
    \scale_table_local[3] = 1 / 64
    \scale_table_local[4] = 1 / 32
    \scale_table_local[5] = 1 / 16
    \scale_table_local[6] = 1 / 8
    \scale_table_local[7] = 1 / 4
    \scale_table_local[8] = 1 / 2
    \scale_table_local[9] = 1
    \scale_table_local[10] = 1
    \scale_table_local[11] = 1
    \scale_table_local[12] = 1
    \scale_table_local[13] = 1
    \scale_table_local[14] = 1
    \scale_table_local[15] = 1
    ProcedureReturn #Null
  EndWith
EndProcedure

Procedure.i dvg_program(*dvg._DVG,*program,*callback)
  With *dvg
    dvg_reset(*dvg)
    \program = *program
    If *callback
      \callback = *callback
    Else
      \callback = @dvg_callback()
    EndIf
    ProcedureReturn #Null
  EndWith
EndProcedure

Procedure.i dvg_execute(*dvg._DVG)
  With *dvg
    \opcode = \program + \offset
    If \opcode\words\y
      Select ((\opcode\bytes\a >> 4) & %00001111)
        Case #DVG_OPCODE_VEC_0 To #DVG_OPCODE_VEC_9
          \scale_index = ((\opcode\bytes\a >> 4) & %00001111)
          \scale_local = \scale_table_local[\scale_index] * \scale_global
          \delta_x = (\opcode\words\x & %0000001111111111)
          \delta_y = (\opcode\words\y & %0000001111111111)
          \delta_x - (((\opcode\bytes\c >> 2) & 1) * (\delta_x << 1))
          \delta_y - (((\opcode\bytes\a >> 2) & 1) * (\delta_y << 1))
          \delta_x * \scale_local
          \delta_y * \scale_local
          \delta_x + \offset_x
          \delta_y + \offset_y
          \brightness = ((\opcode\bytes\c >> 4) & %00001111)
          If \brightness
            CallCFunctionFast(\callback,\offset_x,\offset_y,\delta_x,\delta_y,\brightness)
          EndIf
          \offset_x = \delta_x
          \offset_y = \delta_y
          \offset + 4
          ProcedureReturn #True
        Case #DVG_OPCODE_CUR
          \scale_index = ((\opcode\bytes\c >> 4) & %11111111)
          If \scale_index < 0
            \scale_index - (\scale_index << 1)
          EndIf
          \scale_global = \scale_table_global[\scale_index]
          \offset_x = (\opcode\words\x & %0000001111111111)
          \offset_y = (\opcode\words\y & %0000001111111111)
          \offset + 4
          ProcedureReturn #True
        Case #DVG_OPCODE_HALT
          ProcedureReturn #False
        Case #DVG_OPCODE_JSR
          \stack[3] = \stack[2]
          \stack[2] = \stack[1]
          \stack[1] = \stack[0]
          \stack[0] = \offset + 2
          \offset + ((((\opcode\words\y & %0000111111111111) - $800) << 1) + $800);<- need testcode for this!
          ProcedureReturn #True
        Case #DVG_OPCODE_RTS
          \offset = \stack[0]
          \stack[0] = \stack[1]
          \stack[1] = \stack[2]
          \stack[2] = \stack[3]
          \stack[3] = #Null
          ProcedureReturn Bool(\offset <> #Null)
        Case #DVG_OPCODE_JMP
          \offset + ((((\opcode\words\y & %0000111111111111) - $800) << 1) + $800);<- need testcode for this!
          ProcedureReturn #True
        Case #DVG_OPCODE_SVEC
          \scale_index = ((((\opcode\bytes\b >> 3) & 1) << 1) + ((\opcode\bytes\a >> 3) & 1)) + 1
          \scale_local = \scale_table_global[\scale_index] * \scale_global
          \delta_x = (\opcode\bytes\b & %00000011)
          \delta_y = (\opcode\bytes\a & %00000011)
          \delta_x = \delta_x - (((\opcode\bytes\b >> 2) & 1) * (\delta_x << 1))
          \delta_y = \delta_y - (((\opcode\bytes\a >> 2) & 1) * (\delta_y << 1))
          \delta_x * \scale_local
          \delta_y * \scale_local
          \brightness = ((\opcode\bytes\b >> 4) & %00001111)
          \delta_x + \offset_x
          \delta_y + \offset_y
          If \brightness
            CallCFunctionFast(\callback,\offset_x,\offset_y,\delta_x,\delta_y,\brightness)
          EndIf
          \offset_x = \delta_x
          \offset_y = \delta_y
          \offset + 2
          ProcedureReturn #True
      EndSelect
    EndIf
    ProcedureReturn #False
  EndWith
EndProcedure

Procedure.i dvg_alloc()
  ProcedureReturn AllocateStructure(_DVG)
EndProcedure

Procedure.i dvg_free(*dvg._DVG)
  FreeStructure(*dvg)
  ProcedureReturn #Null
EndProcedure

Test code:

Code: Select all

EnableExplicit

;DVG Test

XIncludeFile "dvg.pbi"

Procedure.i draw_start()
  If StartVectorDrawing(CanvasVectorOutput(0))
    VectorSourceColor(RGBA(10,10,10,255))
    FillVectorOutput()
    ProcedureReturn #True
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure.i draw_stop()
  StopVectorDrawing()
  ProcedureReturn #Null
EndProcedure

Procedure.i draw_line(x1.i,y1.i,x2.i,y2.i,brightness.i)
  MovePathCursor(x1,y1)
  AddPathLine(x2,y2)
  VectorSourceColor(RGBA(0,brightness << 4,0,200))
  StrokePath(2)
  ProcedureReturn #Null
EndProcedure

Procedure.i main()
  Protected *dvg
  Protected.i exit,draw
  *dvg = dvg_alloc()
  If *dvg
    dvg_program(*dvg,?dvg_test_program,@draw_line())
    If OpenWindow(0,0,0,1024,1024,"DVG Test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
      If CanvasGadget(0,0,0,WindowWidth(0),WindowHeight(0))
        AddWindowTimer(0,0,20)
        Repeat
          Repeat
            Select WindowEvent()
              Case #PB_Event_Timer
                draw = #True
              Case #PB_Event_None
                Break
              Case #PB_Event_CloseWindow
                exit = #True
            EndSelect
          ForEver
          If draw
            If draw_start()
              While dvg_execute(*dvg)
              Wend
              draw = draw_stop()
            EndIf
          EndIf
        Until exit
      EndIf
      CloseWindow(0)  
    EndIf
    dvg_free(*dvg)
  EndIf
  ProcedureReturn #Null
EndProcedure

End main()

DataSection
  ;Test Pattern. Diamond pattern across screen with a parallel line pattern in the center. 
  dvg_test_program:
  Data.w $A080, $0000      ;CUR  scale=0(/512) x=0     y=128  
  Data.w $7000, $0000      ;VEC  scale=7(/4)   x=0     y=0     b=0
  Data.w $9000, $73FF      ;VEC  scale=9(/1)   x=1023  y=0     b=7
  Data.w $92FF, $7000      ;VEC  scale=9(/1)   x=0     y=767   b=7
  Data.w $9000, $77FF      ;VEC  scale=9(/1)   x=-1023 y=0     b=7
  Data.w $96FF, $7000      ;VEC  scale=9(/1)   x=0     y=-767  b=7
  Data.w $92FF, $72FF      ;VEC  scale=9(/1)   x=767   y=767   b=7
  Data.w $8600, $7200      ;VEC  scale=8(/2)   x=512   y=-512  b=7
  Data.w $87FE, $77FE      ;VEC  scale=8(/2)   x=-1022 y=-1022 b=7
  Data.w $9200, $7600      ;VEC  scale=9(/1)   x=-512  y=512   b=7
  Data.w $81FE, $7200      ;VEC  scale=8(/2)   x=512   y=510   b=7
  Data.w $96FF, $72FF      ;VEC  scale=9(/1)   x=767   y=-767  b=7
  Data.w $A37F, $03FF      ;CUR  scale=0(/512) x=1023  y=895  
  Data.w $7000, $0000      ;VEC  scale=7(/4)   x=0     y=0     b=0
  Data.w $96FF, $76FF      ;VEC  scale=9(/1)   x=-767  y=-767  b=7
  Data.w $81FE, $7600      ;VEC  scale=8(/2)   x=-512  y=510   b=7
  Data.w $9200, $7200      ;VEC  scale=9(/1)   x=512   y=512   b=7
  Data.w $87FE, $73FE      ;VEC  scale=8(/2)   x=1022  y=-1022 b=7
  Data.w $8600, $7600      ;VEC  scale=8(/2)   x=-512  y=-512  b=7
  Data.w $92FF, $76FF      ;VEC  scale=9(/1)   x=-767  y=767   b=7
  Data.w $A1FC, $01F4      ;CUR  scale=0(/512) x=500   y=508  
  Data.w $7000, $0000      ;VEC  scale=7(/4)   x=0     y=0     b=0
  Data.w $F0DB             ;SVEC scale=2(/32)  x=3     y=0     b=13
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F0CF             ;SVEC scale=2(/32)  x=-3    y=0     b=12
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F0BB             ;SVEC scale=2(/32)  x=3     y=0     b=11
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F0AF             ;SVEC scale=2(/32)  x=-3    y=0     b=10
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F09B             ;SVEC scale=2(/32)  x=3     y=0     b=9
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F08F             ;SVEC scale=2(/32)  x=-3    y=0     b=8
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F07B             ;SVEC scale=2(/32)  x=3     y=0     b=7
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F06F             ;SVEC scale=2(/32)  x=-3    y=0     b=6
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F05B             ;SVEC scale=2(/32)  x=3     y=0     b=5
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F04F             ;SVEC scale=2(/32)  x=-3    y=0     b=4
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F03B             ;SVEC scale=2(/32)  x=3     y=0     b=3
  Data.w $F900             ;SVEC scale=1(/64)  x=0     y=1     b=0
  Data.w $F02F             ;SVEC scale=2(/32)  x=-3    y=0     b=2
  Data.w $D07C             ;RTS
EndDataSection

Re: [Source] Execute Digital Vector Generator (DVG) Instructions in PB

Posted: Sat Aug 09, 2025 2:05 pm
by threedslider
Nice for starting your new project !

Thanks for sharing @Mijikai :D

Re: [Source] Execute Digital Vector Generator (DVG) Instructions in PB

Posted: Sun Aug 10, 2025 10:17 pm
by idle
Interesting. Thanks

Re: [Source] Execute Digital Vector Generator (DVG) Instructions in PB

Posted: Mon Aug 11, 2025 6:03 am
by miso
Thanks, cool. ;)

Re: [Source] Execute Digital Vector Generator (DVG) Instructions in PB

Posted: Thu Aug 14, 2025 6:29 pm
by infratec
On Win10 x64 with PB 6.21 x86 I got an illegal memory access at ProcedureReturn #Null in the callback procedure.

A short insepction: CallCFunctionFast() in Case #DVG_OPCODE_VEC_0 To #DVG_OPCODE_VEC_9 is not correct. You don't call a C function.

So I modified your code:

Code: Select all

Prototype.i dvg_callback(x1.i,y1.i,x2.i,y2.i,brightness.i)

Structure _DVG
  *program
  *offset
  *opcode._DVG_OPCODE
  *stack[4]
  scale_index.i
  scale_global.d
  scale_local.d
  scale_table_global.d[16]
  scale_table_local.d[16]
  brightness.i
  offset_x.i
  offset_y.i
  delta_x.i
  delta_y.i
  callback.dvg_callback
EndStructure
2 times:

Code: Select all

If \brightness
  \callback(\offset_x,\offset_y,\delta_x,\delta_y,\brightness)
EndIf
Now it works also in x86 :wink:
It looks 'cleaner' and it is the prefered PB way.