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 ]
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;
}
Works nicely 🙂