Vectrex: Curved Lines

Bildschirmfoto 2015-12-26 um 12.16.06

Nowadays, “Curved lines” on the vectrex are – at least programmers wise – widely known. But as far as I know they were “forgotten” for quite some time. 1998 I dug thru old sources I found on the internet, and discovered the sources called “RUMCHIP.ASM”.

These were sources from the original vectrex developers and within was a routine  labeled “draw_curved_line”. I was quite intrigued by the idea, took those sources, fitted and “corrected” them and did a small demo, drawing a “handwritten” Chris on the screen.

Here my original posting:
(BIN: CLINES.BIN)

Hi!

I hope you don't mind a somewhat lengthier post today!
For quite some time now I was thinking about doing a vectrex program
using curved lines.

OK... Here it is a small demo... including source and binary!

Malban

PS
It sort of works on the emulator, but only looks really well on a
vectrex machine, there you can see the 'curved' lines!


###############
Following is the source:

; this is a small demo, how curved lines can be drawn with vectrex
; idea by myself, implementation mainly by unkown (whoever did the vectrex
; BIOS)
; source of draw_curved_line routine taken from "RUMCHIP.ASM",
; modified, corrected and commented by me... Malban MAY/20/1998
;
; I place this routine in the public domain (as far as I am able to,
; since I didn't program the 'draw_curved_line' myself).
;
; comments and vectrex talk are welcome
; my email: xxxx@aol.com
;
; following command line was used to assemble:
;
; C:>as09.exe -w200 -h0 -l -mcti clines.asm >error
;
; I used the 6809 assembler:
; as09 [1.11].
; Copyright 1990-1994, Frank A. Vorstenbosch, Kingswood Software.
; Available at:
; http://www.falstaff.demon.co.uk/cross.html
;
 INCLUDE "VECTREX.I" ; vectrex function includes
; user variable definitions
; $c880
user_ram EQU $c880 ; well start of our ram space
user_ram_start EQU user_ram

;***************************************************************************
 ORG 0
; start of vectrex memory with cartridge name...
 DB "g GCE 1998", $80 ; 'g' is copyright sign
 DW music7 ; music from the rom
 DB $F8, $50, $20, -$3a; hight, width, rel y, rel x (from 0,0)
 DB "CURVED LINES", $80; some game information, ending with $80
 DB 0 ; end of game header
;***************************************************************************
; here the cartridge program starts off

entry_point:
 JSR Wait_Recal          ; sets dp to d0, and pos at 0, 0
                         ; vector calibration
 JSR Intensity_3F        ; intensity to $3f, gets destroyed
                         ; by recalibration
 LDA #$7f                ; scale factor
 STA VIA_t1_cnt_lo       ; move to time 1 lo, this means scaling
                         ; only for Moveto_d
                         ; curved lines can only be
                         ; scaled via values of line updates
 LDD #((lo($20))*256+(lo(-$1c))) ; position on screen
 JSR Moveto_d            ; and go to position
 LDU #curved_line        ; load address of curved lines
                         ; vector list
 JSR draw_curved_line    ; and draw the thing
 BRA entry_point         ; repeat forever!

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; draw_curved_line
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; taken from "RUMCHIP.ASM" file, found somewhere...
; corrected, commented and renamed labels (by Malban)
;
; subroutine for printing curved lines
; expects pointer to update table in U
; expects DP pointing to $D0
;
; update table has following format:
; DB Y_update, VIDEO_enable, X_update, Xupdate,... ,0
; DB Y_update, VIDEO_enable, X_update, Xupdate,... ,0
; DB ...
; DB 0 ; 0 end of update list
;
; VIDEO_enable is a SHIFTREG poke
; this means $ff == solid line
; $00 == invisible line
; everything else == somewhat dotted line
;
; Note: smoothest curved lines with X slopes
; Y slopes tend to have some edges (since the integration must be
; stopped for Y updates)
;
draw_curved_line:
 LDD #$1881             ; load D with VIA pokes
 STB VIA_port_b         ; poke $81 to port B
                        ; disable MUX
                        ; disable ~RAMP
 STA VIA_aux_cntl       ; poke $18 to AUX
                        ; shift mode 4
                        ; PB7 not timer controlled
                        ; PB7 is ~RAMP
 BRA next_update_round  ; jump to entry of loop

x_update_loop_init:
 STB VIA_port_b         ; MUX disable, ~RAMP enable
 STA VIA_shift_reg      ; poke the enable byte (A) found to
                        ; shift, that enables/disables ~BLANK

x_update_loop:
 LDA ,U+                ; load next X_update value
 BEQ finnish_x_update   ; if zero, we are done with this
                        ; X_update
 STA VIA_port_a         ; otherwise put the found value to
                        ; DAC and thus to X integration
 BRA x_update_loop      ; go on, look if another X_update
                        ; value is there...

finnish_x_update:
 LDB #$81               ; load value for ramp off, MUX off
 STB VIA_port_b         ; poke $81, ramp off, MUX off

 NOP                    ; these NOP's seem to be neccessary
 NOP                    ; since the delay between VIA and
 NOP                    ; integration hardware
 NOP                    ; otherwise, there is a space
                        ; between Y_updates... Malban
 STA VIA_shift_reg      ; A == %00000000

next_update_round:
 LDA ,U+                ; load next Y_update
 BEQ done_curved_line   ; go to done if 0
 STA VIA_port_a         ; poke to DAC
 DECB                   ; B now $80
 STB VIA_port_b         ; enable MUX, that means put
                        ; DAC to Y integrator S/H
 LDD ,U++               ; A=VIDEO_enable, B=X_update
 INC VIA_port_b         ; MUX off, only X on DAC now
 STB VIA_port_a         ; store B (X_update) to DAC
 LDB #$01               ; load poke for MUX disable,
                        ; ~RAMP enable
 BRA x_update_loop_init ; goto x update loop

done_curved_line:
 LDA #$98               ; load AUX setting
 STA VIA_aux_cntl       ; restore usual AUX setting
                        ; (enable PB7 timer, SHIFT mode 4)
 RTS                    ; and out of here

;***************************************************************************
; DB Y_update, VIDEO_enable, X_update, Xupdate,... ,0
; DB Y_update, VIDEO_enable, X_update, Xupdate,... ,0
; DB ...
; DB 0 ; 0 end of update list
curved_line:
 ; C
 DB $10, $ff, -$03, -$06, -$0c, -$14, $00
 DB -$10, $ff, -$18, -$0c, -$08, -$04, -$02, -$02, -$01, $00
 DB -$10, $ff, $01, $02, $02, $04 , $08, $0c, $10, $18
 DB $20, $00
 DB $10, $ff, $20, $18, $10, $0c, $08, $08, $04, $02
 DB $02, -$02 -$02, -$04, -$04, -$08, -$0c, -$0c
 DB $00

 ; h
 DB -$10, $ff, -$18, -$0c, -$08, -$04, -$02, -$02, -$01, $00
 DB -$10, $ff, -$01, -$01, -$01, -$01, -$01, -$01, -$01, $00
 DB $10, $ff, $01, $02, $03, $04, $05, $07, $0a, $00
 DB $04, $ff, $04, $06, $08, $0c, $00
 DB -$04, $ff, $0c, $08, $06, $04, $00
 DB -$10, $ff, $07, $05, $01, $01, $01, $01, $01, $00
 DB -$04, $ff, $02, $04, $06, $08, $00

 ;r
 DB $04, $ff, $08, $06, $04, $02, $00
 DB $10, $ff, $01, $01, $01, $01, $05, $07, $0a, $00
 DB $04, $ff, $04, $06, $08, $0c, $00
 DB -$04, $ff, $0c, $08, $06, $04, $00
 DB -$10, $ff, $07, $05, $01, $01, $01, $01, $01
 DB $01, $00
 DB $1c, $ff, $01, $01, $01, $01, $01, $00
 DB -$0c, $ff, $05, $0a, $15, $00

 ; i
 DB $0c, $ff, $15, $0a, $05, $00
 DB $0c, $00, $01, $01, $00
 DB $0c, $01, $01, $00
 DB -$0c, $00, -$01, -$01, -$01, $00
 DB -$10, $ff, $01, $01, $01, $01, $01, $01, $01, $01
 DB $00
 DB -$04, $ff, $02, $04, $06, $08, $00

 ; s
 DB $04, $ff, $08, $06, $04, $02, $00
 DB $10, $ff, $06, $06, $06, $06, $06, $06, $06, $06
 DB $00
 DB -$0c, $ff, $05, $0a, $15, $00
 DB -$06, $ff, $0c, $08, $07, $05, $03, $00
 DB -$0e, $ff, -$01, -$02, -$03, -$08, -$0a, -$0c, $00

 DB 0
;***************************************************************************
 END entry_point
;***************************************************************************



Following is the binary image of the demo...

section 1/1 file clines.bin [ Wincode 2.7.3 ]

begin 644 clines.bin
M9R!'0T4@,3DY.(#^QOA0(,9#55)6140@3$E.15.``+WQDKWRH89_M]`$S"#D
MO?,2S@![O0`X(.?,&('WT`"WT`L@&_?0`+?0"J;`)P6WT`$@]\:!]]``$A(2
M$K?0"J;`)Q.WT`%:]]``[,%\T`#WT`'&`2#.AIBWT`LY$/_]^O3L`/#_Z/3X
M_/[^_P#P_P$"`@0(#!`8(``0_R`8$`P("`0"`OS\_/CT]`#P_^CT^/S^_O\`
M\/__________`!#_`0(#!`4'"@`$_P0&"`P`_/\,"`8$`/#_!P4!`0$!`0#\
M_P($!@@`!/\(!@0"`!#_`0$!`04'"@`$_P0&"`P`_/\,"`8$`/#_!P4!`0$!
M`0$`'/\!`0$!`0#T_P4*%0`,_Q4*!0`,``$!``P!`0#T`/___P#P_P$!`0$!
M`0$!`/S_`@0&"``$_P@&!`(`$/\&!@8&!@8&!@#T_P4*%0#Z_PP(!P4#`/+_
(__[]^/;T``#T
`
end
sum -r/size 45087/368

section 1/1 file clines.bin [ Wincode 2.7.3 ]


2 thoughts on “Vectrex: Curved Lines

  1. Graham Toal

    Thought you might like this… I was seeing if I could use my sbt code to convert asm to C 🙂 (lots of manual help but the idea sort of works…)

    #include

    // Malban’s “clines” demo converted from asm to C. (semi-automatically using “6809sbt”)

    const unsigned int curved_lines[] = {
    // Y_update, VIDEO_enable, X_update, Xupdate,… ,0
    // C
    0x10,0xFF,
    0xFD,0xFA,0xF4,0xEC,0x00,0xF0,0xFF,0xE8,0xF4,0xF8,0xFC,0xFE,0xFE,0xFF,0x00,
    0xF0,0xFF,0x01,0x02,0x02,0x04,0x08,0x0C,0x10,0x18,0x20,0x00,0x10,0xFF,0x20,
    0x18,0x10,0x0C,0x08,0x08,0x04,0x02,0x02,0xFC,0xFC,0xFC,0xF8,0xF4,0xF4,0x00,
    // H
    0xF0,0xFF,
    0xE8,0xF4,0xF8,0xFC,0xFE,0xFE,0xFF,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
    0xFF,0xFF,0x00,0x10,0xFF,0x01,0x02,0x03,0x04,0x05,0x07,0x0A,0x00,0x04,0xFF,
    0x04,0x06,0x08,0x0C,0x00,0xFC,0xFF,0x0C,0x08,0x06,0x04,0x00,0xF0,0xFF,0x07,
    0x05,0x01,0x01,0x01,0x01,0x01,0x00,0xFC,0xFF,0x02,0x04,0x06,0x08,0x00,
    // R
    0x04,0xFF,
    0x08,0x06,0x04,0x02,0x00,0x10,0xFF,0x01,0x01,0x01,0x01,0x05,0x07,0x0A,0x00,
    0x04,0xFF,0x04,0x06,0x08,0x0C,0x00,0xFC,0xFF,0x0C,0x08,0x06,0x04,0x00,0xF0,
    0xFF,0x07,0x05,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x1C,0xFF,0x01,0x01,0x01,
    0x01,0x01,0x00,0xF4,0xFF,0x05,0x0A,0x15,0x00,
    // I
    0x0C,0xFF,
    0x15,0x0A,0x05,0x00,0x0C,0x00,0x01,0x01,0x00,0x0C,0x01,0x01,0x00,0xF4,0x00,
    0xFF,0xFF,0xFF,0x00,0xF0,0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
    0xFC,0xFF,0x02,0x04,0x06,0x08,0x00,
    // S
    0x04,0xFF,
    0x08,0x06,0x04,0x02,0x00,0x10,0xFF,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
    0x00,0xF4,0xFF,0x05,0x0A,0x15,0x00,0xFA,0xFF,0x0C,0x08,0x07,0x05,0x03,0x00,
    0xF2,0xFF,0xFF,0xFE,0xFD,0xF8,0xF6,0xF4,0x00,
    // end of update list
    0x00,
    };

    void draw_curved_lines(register const unsigned int *data) {
    register unsigned int A;
    register unsigned int B;

    B = 0x81;
    VIA_port_b = (int)B; //disable MUX, disable ~RAMP
    VIA_aux_cntl = A = 0x18; // shift mode 4, PB7 not timer controlled, PB7 is ~RAMP

    for (;;) {
    // next update round

    A = *data++; // load next X_update value

    if (!A) {
    VIA_aux_cntl = 0x98; // restore usual AUX setting (enable PB7 timer, SHIFT mode 4)
    return;
    }
    VIA_port_a = (int)A; // poke to DAC
    VIA_port_b = (int)–B; // (B now $80) enable MUX, that means put DAC to Y integrator S/H
    A = *data++; // A=VIDEO_enable
    B = *data++; // B=X_update
    VIA_port_b++; // MUX off, only X on DAC now
    VIA_port_a = (int)B; // store B (X_update) to DAC
    B = 1; // load poke for MUX disable, ~RAMP enable

    VIA_port_b = (int)B; // MUX disable, ~RAMP enable
    VIA_shift_reg = A; // poke the enable byte (A) found to shift, that enables/disables ~BLANK

    for (;;) {
    A = *data++; // load next X_update value
    if (A==0) break; // if zero, we are done with this
    VIA_port_a = (int)A; // X_update
    }

    B = 0x81; // load value for ramp off, MUX off
    VIA_port_b = (int)B; //
    //asm volatile (” NOP “); // these NOP’s seem to be neccessary
    //asm volatile (” NOP “); // since the delay between VIA and
    //asm volatile (” NOP “); // integration hardware
    //asm volatile (” NOP “); // otherwise, there is a space
    // // between Y_updates… Chris
    VIA_shift_reg = A; // A == %00000000
    }
    }

    int main(void)
    {
    for (;;) {
    Wait_Recal();
    Intensity_3F();
    VIA_t1_cnt_lo = 0x7f; // scale for initial move
    Moveto_d(0x20, -0x5C); // position the graphics
    draw_curved_lines(curved_lines);
    }
    // if return value is <= 0, then a warm reset will be performed,
    // otherwise a cold reset will be performed
    return 0;
    }

Leave a Reply

Your email address will not be published. Required fields are marked *