Well, fast forward 30 years later I still can't shake that feeling and urge to do exactly that again. For the past 3months or so I have started reading my huge collection of almost 5000 computer magazines from the early 80's and into the 90's. Starting of A through Z. At this point in time, I'm still at letter A :-)
Anyways, I picked out some of the type-ins that appeared very interresting for me, either based on the fact of stuff I wanted to try back in the days, or actual code snippets that can help me in whatever projects I'm doing on C64 or Amiga these days.
Enjoy a trip into the world of typing matters. Only this time, naturally everything is simply copied as OCR'd text from PDF, double-checked, notes added and code presented below.
I have referenced the issue and page number the original type-in was published in, so if you have the magazine available, read on for more details there!
Vector Graphics - Machine Code / AmigaBasic code
This one was discovered in a Danish magazine called "Amiga Interface" Issue 3-1989 on page 37-38. In fact they had code listings for AmigaBasic and Assembly language in many of their magazines. Download link for magazine at bottom of page.
I am familiar with most languages, but assembly code was never my favourite topic, it was just too darn hard to understand. But still, back in the days I managed to cook together snippets of minimalstic code or just assemble random sources found on various utilities disks and see what happen and kind-of learn something.
30 years later, I decided to give it a go. Only to find my lack of memory regarding what assembler to use, how to use it and understand today how to get it to work. Since I love old-school vector graphics, I spend almost 2 days gathering the following research and successfull results below :-)
I started out thinking I have to rely on the available assembler software used back in 1989. During some research in those Danish magazines, I noticed they used SEKA, and most likely V1.5 by Kuma (not those other MasterSeka hacks, or even SEKA hacks that circulated back then). Further, knowing its 1989, we could assume its was coded (and working) on a plain stock Amiga 500 v1.3 workbench with 0.5m chip and probably 0.5m fast memory.
I loaded up my "AmigaFilesDatabase" in my searcher software (SOTDS) and found a couple of hundred filenames with "seka" in them.
My research showed that there was no difference in compiling the source code in the original SEKA v1.5 by Kuma until the latest version, including MasterSeka v1.80. ASMOne/Devpac/ASMOneTurbo all failed on something.
Oh well, here's a complete gathering of my findings and don't forget to check out the attached links and files at the bottom of this longer post.
First up, the AmigaBasic code which will generate Sin/Cos tables "RAM:TABLES" that where mentioned in the magazine to include inside the assembly source code.
REM From Amiga Interface Magazine Issue 3-1989 (page 37-38)
REM Typed/Cleaned up by Stone Oakvalley January 2018
REM /////////////////////////////////////
OPEN "ram:TABLES" FOR OUTPUT AS #1
pi=3.141592654#
s=360/512
d=0
PRINT #1,"sin_table:"
FOR a=0 TO 511
b=16384&*SIN(d*pi/180)
PRINT #1,CHR$(9)"dc.w";CHR$(9);INT(b)
d=d+s
NEXT a
d=0
PRINT #1,CHR$(13);"cos_table:"
FOR a=0 TO 511
b=16384&*COS(d*pi/180)
PRINT #1,CHR$(9)"dc.w";CHR$(9);INT(b)
d=d+s
NEXT a
CLOSE#1
STOP
Next, the assembly code to be compiled with SEKA V1.5-V3.2 (excluding v3.0) including MasterSekaV1.80 (at least). For clarity and to remember for myself, I included HOW-TO assemble and save the executable file to disk. It isn't easy when SEKA V1.5 don't have menus or I managed to find any decent user manual, heck, even the original 2 disk-set from Kuma with manual wasn't even found in my gadzillion of files gathered from Internet for the past 10 years either!
It should be noted that the code listed below the sin/cos tables are removed. However, the attached "Realtime_VectorGraphics.s" source file at the bottom is complete with sin/cos tables.
;//////////////////////////////////////
; Title: VGR (Vector Graphics Routines)
; By : Kenneth Bernholm
; For : Amiga Interface
; From Amiga Interface Magazine Issue 3-1989 (page 37-38)
; Typed/Cleaned up by Stone Oakvalley - 14/15 January 2018
; To assemble (instructions by Stone Oakvalley):
; Run SEKA V1.5 (Kuma) and set 300 as memory
; 'r' for loading file from disk (this source)
; 'a' for assemble
; 'OPTIONS>' just leave blank and press return
; 'wo' press return (for writing the executable)
; 'FILENAME>' = enter a filename, example ram:test
; and file is successfully saved as stand-alone executable?!
; The screen will boot up brown, but no spinning vector disk, mouse button
; to exit works, so does anybody know why it don't execute properly?!!
; Or if you want to run it at once directly from SEKA V1.5
; Simple type "a" for assemble, press return to ignore "OPTIONS>"
; and then "jj" (j=jump) and press enter. It will then execute from
; where the "j" label begins and show a nice floppy disk with the
; Amiga checkmark logo rotating in wireframe!
;
; A good user manual for SEKA (K-SEKA Assembler) found here:
; http://wiki.amigaspirit.hu/index.php/Amiga_Machine_Language_(Chapter_3) and section 3.3
;//////////////////////////////////////
; [[[[[[ memory reservations ]]]]]]
screen0 = $60000
screen1 = screen0+8000
progstart = screen1+8000
org progstart
load progstart
j:
jsr init_interrupts ;call : init _interrupts
jsr init_screen ;call : init_screen
test_left_button:
btst #6,$bfe001 ;is left button pressed
bne.s test_left_button ;if yes, jump
jsr restore_screen ;call : restore_screen
jsr restore_interrupts ;call : restore_interrupts
clr.l d0 ;clear exit-vector
rts ;return to dos
; ###### subprogram: init_interrupts #####
init_interrupts:
move.w $dff01e,sys_intreq ;store system intreq
move.w $dff01c,sys_intena ;store system intena
move.w #$7fff,$dff09c ;kill all requests
move.w #$7fff,$dff09a ;disable all interrupts
move.l $6c,sys_level3 ;store system level3
move.l #my_level3,$6c ;set my level3
move.w #$8020,$dff09a ;enable vb-interrupt
move.w #$c000,$dff09a ;master enable
rts ;return to main
; ###### subprogram: restore_interrupts ######
restore_interrupts:
move.l sys_level3,$6c ;restore system level3
or.w #$8000,sys_intena ;set enable bit
or.w #$8000,sys_intreq ;set enable bit
move.w sys_intreq,$dff09c ;restore system inrreq
move.w sys_intena,$dff09a ;restore system intena
rts ;return to main
; ###### subprogram: init_screen ######
init_screen:
move.l 4,a6 ;a6=adr. of exec-library
move.l 156(a6),a1 ;a1=adr. of gfx-lib.
move.l 38(a1),sys_copper ;store adr. of system copper
move.l a7,sys_stack ;store system stack
move.l #copper_list,$dff080 ;start my copper_list
move.w #$0020,$dff096 ;stop sprites
rts ;return to main
; ##### subprogram: restore_screen ######
restore_screen:
move.l 4,a6 ;a6=adr. of exec-library
move.l sys_copper,$dff080 ;start system coppe
move.l sys_stack,a7 ;restore system stack
move.w #$8020,$dff096 ;start sprites
rts ;return to main
; ###### my level3 interrupts ######
my_level3:
btst #5,$dff01f ;is it a vb-interrupt
beq.L my_level3_exit ;if not, jump
jsr draw_figure ;call : draw_figure
move.w #0800,$dff180
jsr flip_screen ;call : flip_screen
move.w #$0020,$dff09c ;kill all vb-requests
my_level3_exit:
rte ;return
; ###### subprogram: flip_screen ######
flip_screen:
cmp.l #screen1,screen_adress ;are we showing screen1
beq.s flip_to_screen1 ;if yes, jump
;---- flip to screen()
lea.l bpl_pointers,a0 ;a0=adr. of bpl_pointers
move.l #screen0,d0 ;d0=adr. of screen0
move.w d0,6(a0) ;set low word
swap d0 ;swap high/low words
move.w d0,2(a0) ;set high word
move.l #screen1,screen_adress ;set screen_adress
bra.s flip_screen_exit ;jump
;---- flip to screen1
flip_to_screen1:
lea.l bpl_pointers,a0 ;a0=adr. of bpl_pointers
move.l #screen1,d0 ;d0=adr. of screen1
move.w d0,6(a0) ;set low word
swap d0 ;swap high/low words
move.w d0,2(a0) ;set high word
move.l #screen0,screen_adress ;set screen_adress
flip_screen_exit:
rts ;return to main
; ##### subprogram : draw_figure #####
draw_figure:
btst #14,$dff002 ;is blitter busy
bne.s draw_figure ;if yes, jump
move.l #$01000000,$dff040 ;set control()
move.l screen_adress,$dff054 ;set destination
move.w #0,$dff066 ;set modulo d
move.w #[200*64]+20,$dff058 ;set size (start blitter)
move.l #$00008000,$dff072 ;sel blitter source data
move.l #$ffffffff,$dff044 ;set first mask for a
move.w #40,$dff060 ;modulo c=width
;---- calculate new degrees
move.l xdelta,d0 ;d0=xdelta
add.l d0,xgrad ;set new xgrad
and.l #$000001ff,xgrad ;kill all over 511
move.l ygrad,d6 ;d6=ygrad
add.l ydelta,d6 ;d6=ygrad+ydelta
and.l #$000001ff,d6 ;kill all over 511
move.l d6,ygrad ;store ygrad
lsl.w #1,d6 ;d6=ygrad*4
move.l zgrad,d7 ;d7=zgrad
add.l zdelta,d7 ;d7=zgrad+zdelta
and.l #$000001ff,d7 ;kill all over 511
move.l d7,zgrad ;store zgrad
lsl.w #1,d7 ;d7=zgrad*4
;---- rotate figure
move.l screen_adress,a0 ;a0=adr. of screen
lea.l figure1,a1 ;a1=adr. of figure1
lea.l sin_table,a2 ;a2=adr. of sin_table
lea.l cos_table,a3 ;a3=adr. of cos_lable
lea.l octant_table,a4 ;a4=adr. of octant_table
draw_figure2:
cmp.w #555,(a1) ;is this the end
beq.L draw_figure_exit ;if yes, jump
move.l xgrad,d5 ;d5=xgrad
lsl.w #1,d5 ;d5=xgrad*4
;---- rotate around x-axis
move.w (a3,d5),d0 ;d0=cos(xgrad)
muls 4(a1),d0 ;d0=y*cos(xgrad)
move.w (a2,d5),d1 ;d1=sin(xgrad)
muls 6(a1),d1 ;d1=z*sin(xgrad)
sub.l d1,d0 ;d0=y*cos(xgrad)-z*sin(xgrad)
lsl.l #2,d0 ;multiply d0 with 4
move.l d0,y1 ;set yl
move.w (a2,d5),d0 ;d0=sin(xgrad)
muls 4(a1),d0 ;d0=y*sin(xgrad)
move.w (a3,d5),d1 ;d1=cos(xgrad)
muls 6(a1),d1 ;d1=z*cos(xgrad)
add.l d1,d0 ;d0=y*sin(xgrad)+z*cos(xgrad)
lsl.l #2,d0 ;multiply d0 with 4
move.l d0,z1 ;set zl
;---- rotate around y-axis
move.w (a3,d6),d0 ;d0=cos(ygrad)
muls 2(a1),d0 ;d0=x*cos(ygrad)
move.w (a2,d6),d1 ;d1=sin(ygrad)
muls z1,d1 ;d1=z*sin(ygrad)
sub.l d1,d0 ;d0=x*cos(ygrad)-z1*sin(ygrad)
lsl.l #2,d0 ;multiply d0 with 4
move.l d0,x2 ;set x2
move.w (a2,d6),d0 ;d0=sin(ygrad)
muls 2(a1),d0 ;d0=x*sin(ygrad)
move.w (a3,d6),d1 ;d1=cos(ygrad)
muls z1,d1 ;d1=z1*cos(ygrad)
add.l d1,d0 ;d0=x*sin(ygrad)+z1*cos(ygrad)
lsl.l #2,d0 ;multiply d0 with 4
swap d0 ;divide d0 with 65535
ext.l d0 ;extend to longword
move.l d0,z2 ;set z2
;---- rotate around z-axis
move.w (a3,d7),d0 ;d0=cos(zgrad)
muls x2,d0 ;d0=x2*cos(zgrad)
move.w (a2,d7),d1 ;d1=sin(zgrad)
muls y1,d1 ;dl=y1*sin(zgrad)
sub.l d1,d0 ;d3=x2*cos(zgrad)-y2*sin(zgrad)
lsl.l #2,d0 ;multiply d0 with 4
swap d0 ;divide d0 with 65535
ext.l d0 ;extend to longword
move.w (a2,d7),d1 ;d1=sin(zgrad)
muls x2,d1 ;d1=x2*sin(zgrad)
move.w (a3,d7),d2 ;d2=cos(zgrad)
muls y1,d2 ;d2=y1*cos(zgrad)
add.l d2,d1 ;d1=x2*sin(zgrad)+y2*cos(zgrad)
lsl.l #2,d1 ;multiply d1 with 4
swap d1 ;divide d1 wilh 65535
ext.l d1 ;extend to longword
;---- translate from 3d to 2d
move.l yviewpoint,d2 ;d2=yviewpoint
muls d0,d2 ;d2=yviewpoint*x3
move.l viewdistance,d4 ;d4=viewdistance
add.l d1,d4 ;d4=viewdistance+y3
divs d4,d2 ;d2=d2/d4
ext.l d2 ;extend to longword
add.l xorigo,d2 ;add xorigo to x-position
move.l last_x_point,d0 ;d0=x1
move.l d2,last_x_point ;store x-position
move.l d1,d3 ;d3=y3
add.l yorigo,d3 ;d3=y3+yorigo
move.l zviewpoint,d4 ;d4=zviewpoint
sub.l z2,d4 ;d4=zviewpoint-z2
muls d3,d4 ;d4=d4*d3
add.l viewdistance,d1 ;d1=viewdistance+y3
divs d1,d4 ;d4=d4/d1
ext.l d4 ;extend to longword
add.l z2,d4 ;add z2 to d4
lsr.l #1,d4 ;d4=y4/2
move.l zorigo,d3 ;d3=zorigo
sub.l d4,d3 ;d3=zorigo-y4/2 = y2
move.l last_y_point,d1 ;d1=yl
move.l d3,last_y_point ;store y-position
;---- draw line
tst.w (a1) ;move?
beq.L draw_figure3 ;if yes, jump
cmp.l d0,d2 ;compare x-coords
bne.l draw1 ;if not equal, jump
cmp.l d1,d3 ;compare y-coords
bne.L draw1 ;if not equal, jump
bra.l draw_figure3 ;jump
;---- d4=start-adress for line
draw1:
move.l d1,d4 ;d4=yl
move.l d4,d5 ;d5=yl
lsl.w #5,d4 ;d4=yl*32
lsl.w #3,d5 ;d5=yl*8
add.l d5,d4 ;d4=y1*40
move.l d0,d5 ;d5=xl
lsr.w #3,d5 ;d5=xl/8
add.l d5,d4 ;add d5 to d4
add.l a0,d4 ;add bitplane-adress to d4
;---- calculate octant & delta
move.l #0,d5 ;clear d5
sub.w d1,d3 ;d3=deltay (y2-y1)
roxl.b #1,d5 ;scroll neg-sign in deltay
tst.w d3 ;restore n-flag ?
bge.s y2gy1 ;jump if deltay is positive
neg.w d3 ;else invert deltay
y2gy1: sub.w d0,d2 ;d2=deltax (x2-x1)
roxl.b #1,d5 ;scroll neg-sign in deltax
tst.w d2 ;restore n-flag
bge.s x2gx1 ;jump if deltax is positive
neg.w d2 ;else invert deltax
x2gx1:
move.w d3,d1 ;d1=deltay
sub.w d2,d1 ;dl=deltay-deltax
bge.s dygdx ;jump if deltay>deltax
exg d2,d3 ;d2=smallest data
dygdx: roxl.b #1,d5 ;d5=number of actual octant
move.b (a4,d5),d5 ;d5=octant-code
lsl.l #1,d2 ;d2=smallest data*2
and.w #$000f,d0 ;first 4 bits of x1
ror.w #4,d0 ;set shift-value
or.w #$0bfa,d0 ;set use/miniterms
move.l d3,d1 ;d1=d3
lsl.w #6,d3 ;d3=length*64
addq.w #2,d3 ;d3=length*64+2
draw99: btst #14,$dff002 ;test blitter-busy bit
bne.s draw99 ;if not set, loop
move.w d2,$dff062 ;set modulo b
sub.w d1,d2 ;d2=d2-greatest data
bge.s signn1 ;jump if 2*kdelta>gdelta
or.b #$40,d5 ;set signed-flag
signn1: move.w d2,$dff052 ;set pointer a (low word)
sub.w d1,d2 ;d2=d2-d3
move.w d2,$dff064 ;set modulo a
;---- initialize other blitter-registers
move.w d0,$dff040 ;set shift and minterms
move.w d5,$dff042 ;set octant no.
move.l d4,$dff048 ;set start-adress af line
move.l d4,$dff054 ;set start-adress of line
move.w #40,$dff066 ;modulo d=width
move.w d3,$dff058 ;start blitter
draw_figure3:
addq.l #8,a1 ;move to next 3d-data
bra.L draw_figure2 ;jump
draw_figure_exit:
rts ;return to main
;---- interrupt reservations ----
even
sys_intreq: dc.w 0 ;systems intreq
sys_intena: dc.w 0 ;systems intena
sys_level3: dc.l 0 ;systems level3 interrupt
;---- system reservations ----
even
sys_copper: dc.l 0 ;adr. of system copper
sys_stack: dc.l 0 ;adr.of system stack
;---- screen reservations ----
even
screen_adress: dc.l screen0
;---- vector reservations -----
even
octant_table:
dc.b %00000001 ;y1
dc.b %00010001 ;y1dy = octant7
dc.b %00001001 ;y1x2, dx
dc.b %00010101 ;y1x2, dx>dy = octant4
dc.b %00000101 ;y1>y2, x1
dc.b %00011001 ;y1>y2, x1dy = octant0
dc.b %00001101 ;y1>y2, xl>x2, dx
dc.b %00011101 ;y1>y2, x1>x2, dx>dy = octant3
xorigo: dc.l 160
yorigo: dc.l -100
zorigo: dc.l 100
yviewpoint: dc.l 250
zviewpoint: dc.l 0
viewdistance: dc.l 300
xgrad: dc.l 0
ygrad: dc.l 0
zgrad: dc.l 0
xdelta: dc.l 5
ydelta: dc.l 3
zdelta: dc.l 1
y1: dc.l 0
z1: dc.l 0
x2: dc.l 0
z2: dc.l 0
last_x_point: dc.l 0
last_y_point: dc.l 0
; command(0=move,1=draw),x,z,y
figure1:
dc.w 0,-65,0,65,1,65,0,65,1,65,0,-80,1,-57,0,-80,1,-65,0,-72,1,-65,0,65,0,-53,0,65
dc.w 1,-53,0,-20,1,53,0,-20,1,53,0,65,0,55,0,55,1,61,0,55,1,61,0,49,1,55,0,49
dc.w 1,55,0,55,0,-34,0,-80,1,-34,0,-34,1,53,0,-34,1,53,0,-80,1,32,0,-80,1,32,0,-34
dc.w 0,-24,0,-73,1,-24,0,-37,1,-8,0,-37,1,-8,0,-73,1,-24,0,-73,0,-40,0,30,1,-15,0,0
dc.w 1,25,0,55,0,-30,0,30,1,-5,0,0,1,35,0,55,0,-20,0,30,1,5,0,0,1,45,0,55,0,-65,50,65
dc.w 1,65,50,65,1,65,50,-80,1,-57,50,-80,1,-65,50,-72,1,-65,50,65,0,-53,50,65
dc.w 1,-53,50,-20,1,53,50,-20,1,53,50,65,0,55,50,55,1,61,50,55,1,61,50,49,1,55,50,49
dc.w 1,55,50,55,0,-34,50,-80,1,-34,50,-34,1,53,50,-34,1,53,50,-80,1,32,50,-80
dc.w 1,32,50,-34,555,555,555,555
;---- my copper_list ----
even
copper_list:
dc.w $0100,$1200 ;bitplane control
dc.w $0104,$0024 ;video priority
dc.w $0102,$0000 ;bitplane scroll-value
dc.w $0108,$0000 ;bitplane modulo odd
dc.w $010a,$0000 ;bilplane modulo even
dc.w $008e,$2c81 ;window upper-left corner
dc.w $0090,$f4c1 ;window bottom-right corner
dc.w $0092,$0038 ;datafeteh start
dc.w $0094,$00d0 ;datafeteh stop
bpl_pointers:
dc.w $00e0,$0007 ;bitplane 1 high
dc.w $00e2,$5000 ;bitplane 1 low
dc.w $0180,$0000 ;ink 0
dc.w $0182,$00f0 ;ink 1
dc.w $ffff,$fffe ;wait for vbi
; ##### table of sinus-values from 0 to 511 ####
; inject the "RAM:TABLES" file created with the AmigaBasic source here
thend:
dc.b 'the end',0
end
Finally, to top it all off, I included every SEKA version I could find, created a bootable V1.3 disk with minor CLI commands for you, both source code listed above and also some instructions of what on the disk. Kind of a cover-disk to this story! :-)
I also included which is probably the only suitable manual for SEKA (K-SEKA) and other assemblers on Amiga "http://wiki.amigaspirit.hu/index.php/Amiga_Machine_Language_(Chapter_3)".
Since I don't trust other sites to be "out there" as long as I have intended, I took the liberty of copy/paste the SEKA section and uploaded that file to my server too!
In fact, I get so frustrated that there isn't any "love" for K-SEKA. Everybody seems to hate it. For this matter, I found a Danish/English Machine Code course that uses K-SEKA as their reference. At least something!