;
; Union presents a small production for Speccy.pl classic demo competition 2015
;
; Programmed and designed by Dr Piotr - back on Amstrad after 25 years!
; Music by Ben Daglish - ripped from Masters Of Universe game (c) 1987 Gremlin Graphics

; Code & Graphics - copyright 2015 Union, free for noncommercial use
; Music -  copyright 1987 Gremlin Graphics

; This intro features:
; - Unlimited bobs effect, using triple screen buffering, fixed point math
; - Split screen mode, with mode 0 (16 colors) in 256x148 in the upper part of the screen
;   and with mode 1 (4 colors) in 400x50 
; - Overscan with 400 pixels for the scroll at the bottom of the screen
; - Raster effect on the scroll text
; - Shake screen effect, synced with music
; - No firmware calls used, pure hardware banging

; Maxam or WinAPE compatible assembler required. 

org &800
run &800
nolist

; screen dimensions
ScreenWidth equ  128
ScreenHeight equ 148
; screen mode 0 - 160x200
ScreenMode equ  0
; screen bytes per pixels - calculation based on Screenmode
ScreenBytesPerPixel equ  ScreenMode*ScreenMode+ScreenMode+2
; max count of chars i aa font
MaxCharsInFont equ  52
; define sprite x & y dimensions i pixels
SpriteXsize  equ  16
SpriteYSize  equ  16
; scroll width in bytes
ScrollWidth  equ  50
; scroll height
ScrollHeight  equ 25
; scroll screen mode
ScrollScreenMode equ  1
; scroll char width in pixels
ScrollCharWidth  equ  16
; scroll char height in pixels
ScrollCharHeight  equ  25
; deleay between changes of parameters for bobs drawing
BobsDelay equ  1800

; macro - calculate next screen lined address input and result in hl
macro scr_next_line_hl bytes_per_line
ld  a,h ; 1
add  a,8 ; 2
ld  h,a ; 1
and  &38 ; 2
jr  nz,@scr_next_line_end ; 2
ld  a,l ; 1
add  a, bytes_per_line ; 2
ld  l,a ; 1
ld  a,h ; 1
adc  a,&c0 ; 2
ld  h,a ; 1
@scr_next_line_end:
mend

; macro - calculate next screen lined address input and result in hl
macro scr_next_line_de bytes_per_line
ld  a,d ; 1
add  a,8 ; 2
ld  d,a ; 1
and  &38 ; 2
jr  nz,@scr_next_line_end ; 2
ld  a,e ; 1
add  a, bytes_per_line ; 2
ld  e,a ; 1
ld  a,d ; 1
adc  a,&c0 ; 2
ld  d,a ; 1
@scr_next_line_end:
mend

; macro - wait for vertcal refresh
macro WaitVBL
ld  bc,&f500
@WaitVBLLoop: 
in  a,(c)
rra
jr  nc,@WaitVBLLoop
mend

; macro - multiply 16 bit value by 8 bit value
; DE - 16 bit number to multiply by
;  A - 8 bit number (multiplier)
; HL - 16 bit result
macro Mul_DExA
ld  b,8
ld  hl,0
@loop: add  hl,hl
rlca
jr  nc,@skip
add  hl,de
@skip: djnz  @loop
mend

; macro - multiply 8 bit value by 8 bit value
; H,E - 8 bit numbers to multiply
; HL  - 16 bit result
macro Mul_HxE
ld  d,0
ld  l,d
ld  b,8
@loop: add  hl,hl
jr  nc,@skip
add hl,de
@skip: djnz  @loop
mend

; macro - get sin value from the table
macro cos  
ld  l,a
ld  h,0
ld  de, cosTable
add  hl,de
ld  a,(hl)
mend

; macro - get sin value from the table
macro sin 
sub  64
cos
mend

; macro - draw a character on screen
; b - char height, c - char width, hl- ptr to char data to draw, de - destination screen address 
macro DrawChar bytes_per_line
@charloop:
push  hl
push  bc
@lineloop:
ld  a,(de)
inc  de
ld  (hl),a
inc  hl
dec  c
jr  nz, @lineloop
pop  bc
pop  hl
scr_next_line_hl bytes_per_line
djnz  @charloop
mend

; ----  start of the program  ----

; setup interrupt handler
di
im  1
ld  hl,InterruptHandler
ld  (#0039),hl
ld  a,#c3
ld  (#0038),a

; change stack to free area - the original location is taken by the 2nd sceen buffer
ld  sp,#3fff
; initialize screen address lookup table
call  BuildLookupTable
; initialize font character table
ld  hl, ScrollFont
ld  a,50
call  InitFontCharOffsetTable
;initialize music
call  music_init
; set crtc vertival height (disp characters) to 26
ld  bc,&bc06
out  (c),c
ld  bc,&bd1a
out  (c),c
ld  bc,&bc07
out  (c),c
ld  bc,&bd1f
out  (c),c

call  ClearVideoMemory
ld  hl,Colors
ld  e, 8
call  SetColors
WaitVBL
; enable interrupts
ei
; main loop starts here
Loop:
; wait for vertical blanking sync
WaitVBL
ld a,(UnlimitedBobsEffectEnabled)
or  a
jr nz, init_bobs
call Scroll
jr  loop
init_bobs:
ld hl,(CurrSpritePosPtr)
ld a,l
or h
jr nz, do_bobs
ld  hl,SpritePos
jp set_pos
do_bobs
WaitVBL
; get current screen buffer offset and calculate next 
ld  a, (current_scr_offset+1)
add  #10
cp  #40
jr  nz, update_screen_offset
ld  a,#10
update_screen_offset:
ld  (current_scr_offset+1),a
; set a new hardware screen buffer offset
ld  bc,&bc0c
out  (c),c
inc  b
out  (c),a
; update sprite position
call  SpriteUpdatePos
ld hl, (CallCnt)
ld a,l
or h
jr nz, next
ld hl,BobsDelay
ld (CallCnt),hl
ld hl,(CurrSpritePosPtr)
inc hl
inc  hl
inc hl
inc  hl
inc hl
ld a,(hl)
or a
jr  nz,set_pos
ld  hl,SpritePos
set_pos:
ld (CurrSpritePosPtr),hl
ld de,CurrSpritePos
ld a,(hl)
ld (de),a
inc hl
inc de

ld a,(hl)
ld (de),a
inc hl
inc de

ld a,(hl)
ld (CurrSpriteAngle+1),a
inc hl

ld a,(hl)
ld (de),a
inc hl
inc de

ld a,(hl)
ld (de),a
inc hl
inc de

; clear 3 buffers with upper part of the screen
ld  a, (current_scr_offset+1)
push af
ld a,#10
ld  (current_scr_offset+1),a

ld h,ScreenHeight
ld l,ScreenWidth/2
ld de,0
ld bc,0
xor a
call ClearScreen

ld a,#20
ld  (current_scr_offset+1),a
ld h,ScreenHeight
ld l,ScreenWidth/2
ld de,0
ld bc,0
xor a
call ClearScreen

ld a,#30
ld  (current_scr_offset+1),a
ld h,ScreenHeight
ld l,ScreenWidth/2
ld de,0
ld bc,0
xor a
call ClearScreen

pop af
ld  (current_scr_offset+1),a
next: 
; wait a bit
ld hl,#6e0
@waitloop:
dec hl
ld a,l
or h
jr  nz, @waitloop
; back to main loop
jp  loop

; clear screen 
ClearScreen:
; h,l -ysize,xsize 
; bc - xstart
; de - ystart
;  a - a value to fill in
push af
push  hl
call  CalcScreenAddress
pop de
pop af
ld b,d
ld d,a
@ClearHeightLoop:
push  hl
ld c,e
@ClearWidthLoop:
ld  (hl),d
inc  hl
dec  c
jr  nz, @ClearWidthLoop
pop  hl
scr_next_line_hl &40
djnz  @ClearHeightLoop
ret


; clear screen buffers memory
ClearVideoMemory:
ld  hl, &4000
ld  de, &bfff
@fill:
xor  a
ld  (hl),a
inc  hl
dec  de
ld  a,e
or  d
jr  nz, @fill
ret

; set hardware screen address offset
SetScreenAddress:
ld  a, (current_scr_offset+1)
ld  bc,&bc0c
out  (c),c
out  (c),a
xor  a
ld  bc,&bc0d
out  (c),c
out  (c),a
ret

CalcScreenAddress:
; de - ypos
; bc - x pos
; out- hl - current screen address
sla  e
rl  d
ld  hl,screen_lines_table
add  hl,de
; get memory offset for y coord
ld  e, (hl)
inc  hl
ld  d, (hl)
; account for current video screen address
ex  de,hl
ld  de,(current_scr_offset)
sla  d
sla  d
add  hl,de
; calc x offset - for mode 0 divide by 2
ld  a,c
srl  b
rr  c
; calc final screen memory address
add  hl,bc
ret

; sprite related variables
CurrSpriteAngle:
db  0,  6
CurrSpritePos:
db  96,96, 2, 2

CurrSpritePosPtr:
dw 0

SpritePos:
db  20,30, 6, 6, 4
db  10,10,-3, 2, 1
db  78,66, 4,-1, 4
db  44,35, 3, 2, 6
db  88,30, 4, 1, 5
db  96,96, 6, 2, 2
db  50,50, 4, 3, 3
db 0

; update sprite position
SpriteUpdatePos:
; update sprite angle
ld ix,CurrSpriteAngle
ld  a,(ix)
add  (ix+1)
ld  (ix),a
jr  nc, @skip_update
; update sprite x position
ld ix,CurrSpritePos
ld a,(ix)
add  (ix+2)
cp  ScreenWidth-SpriteXSize
jr  c, @updatenext
ld  a,ScreenHeight-SpriteYSize
@updatenext:
ld (ix),a
; update sprite y position
ld a,(ix+1)
add  (ix+3)
cp  ScreenHeight-SpriteYSize
jr  c, @updatenext2
ld  a, ScreenHeight-SpriteYSize
@updatenext2:
ld (ix+1),a
ld  a,(CurrSpriteAngle)
@skip_update: 
; get sinus for a given angle, returns value in fp 8.8 format
call  sinFP
ex  de,hl
ld a,(CurrSpritePos)
; calculate curent x position
Mul_DExA
push  hl
; get cosinus for a given angle, returns value in fp 8.8 format
ld  a,(CurrSpriteAngle)
call cosFP
ex  de,hl
ld a,(CurrSpritePos+1)
; calculate curent y position
Mul_DExA
ex  de,hl
pop  bc
; draw a sprite
;draw a sprite on a screen
; bc - xpos in fp 8.8
; de - ypox in fp 8.8
DrawSpriteFP:
xor  a
ld  c,b
ld  e,d
ld  b,a
ld  d,a
;draw a sprite on a screen
; bc - xpos
; de - ypox
DrawSprite:
; x clipping
ld  a,c
cp  0
jp  p, @ClipXNext
ld  c,0
jr  @ClipXnext2
@ClipXnext:
cp  ScreenWidth-SpriteXsize
jp  c, @ClipXnext2
ld  c,ScreenWidth-SpriteXsize
@ClipXnext2:
; y clipping
ld  a,e
cp  1
jp  nc, @ClipYNext
ld  e,0
jr  @ClipYnext2
@ClipYnext:
cp  ScreenHeight-SpriteYsize
jp  c, @ClipYnext2
ld  e,ScreenHeight-SpriteYsize
@ClipYnext2:
; calc target memory address for a given screen position
call  CalcScreenAddress
ex  de,hl
; de now contains destination screen memory pointer
ld  hl, SpriteData
ld  b, SpriteYSize
; draw a sprite
@DrawHeightLoop:
ld  c, SpriteXSize/ScreenBytesPerPixel
push  de
@DrawWidthLoop:
; get sprite data
ld  a,(de)
; apply sprite mask
and  (hl)
inc  hl
; copy in image data
  or  (hl)
inc  hl
; write to screen
ld  (de),a
inc  de
dec  c
jr  nz, @DrawWidthLoop
pop  de
; calculate address of next screen line
scr_next_line_de &40
djnz  @DrawHeightLoop
ret

; clear sprite on screen
; bc - xpos
; de - ypox
;  a - a value to fill in
SpriteClear:
call  CalcScreenAddress
ld  b, SpriteYSize-1
@SpriteClearHeightLoop:
ld  c, SpriteXSize/ScreenBytesPerPixel
push  hl
@SpriteClearWidthLoop:
ld  (hl),a
inc  hl
dec  c
jr  nz, @SpriteClearWidthLoop
pop hl
scr_next_line_hl &40
djnz  @SpriteClearHeightLoop
ret

; scroll routines


; scroll text
Scroll:
ld a,(scroll_delay)
or  a
jr z, @skip_delay
dec a
ld (scroll_delay),a
jr @skip_write_char
@skip_delay:
ld  hl,(current_scr_offset)
call  ScrollText
ld  a,(scroll_cnt)
dec  a
ld  (scroll_cnt),a
or  a
jr  nz,@skip_write_char
call  WriteChar
ld  a,ScrollCharWidth/4+1
ld  (scroll_cnt),a
@skip_write_char:
ret

ScrollText:
; hl - hardware screen address
; calculate final screen memory offset
ld bc, #1e0e
sla  h
sla  h
add hl, bc
ex de, hl
ld  b, ScrollHeight
@scroll_loop:
ld  a,b
push  de
ld  h,d
ld  l,e
inc  hl
ld  bc, ScrollWidth*2
ldir
ld  b,a
pop  de
scr_next_line_de ScrollWidth*2 
djnz  @scroll_loop
ret

; restart scroll text
RestartScrollText:
ld  hl, scroll_text
ld  (scrolltext_ptr), hl

WriteChar:
; get a char from text scroll, check if reached end
  ld  hl, (scrolltext_ptr)
ld  a, (hl)
or  a
jr  z, RestartScrollText
inc hl
ld  (scrolltext_ptr), hl
cp 1
jr nz, @write_skip_delay
ld a, (hl)
inc  hl
ld  (scrolltext_ptr), hl
ld (scroll_delay), a
ld a,64
@write_skip_delay:
cp 2
jr nz, @write_skip_trigger_bobs

; copy scroll area to remaining 2 screen buffers
ld hl,#c000
ld de,#4000
ld bc,#3fff
ldir
ld hl,#c000
ld de,#8000
ld bc,#3fff
ldir
ld hl,BobsDelay
ld (CallCnt),hl
ld (UnlimitedBobsEffectEnabled),a
ret
@write_skip_trigger_bobs:
cp  32
jr  nz, @write_skip_space
add  32
jr @skip
@write_skip_space:
cp  63
jr nc, @skip
add 16+26
@skip:
; calculate offset to the char in the font
sub  64
sla  a
ld  de,0
ld  e,a
ld  hl, charoffset_table
add  hl,de
ld  e,(hl)
inc  hl
ld  d,(hl)

; calculate final screen memory offset
ld  hl,(current_scr_offset)
sla  h
sla  h
ld  bc,#1e0e+96
add  hl,bc
ld  b, ScrollCharHeight
ld  c, 4
; draw a char
; DrawChar ScrollWidth*2

;DrawChar:
; b - char height, hl-char data to draw, de - destination address 
charloop:
push hl

ld a,(de)
inc de
ld (hl),a
inc hl

ld a,(de)
inc de
ld (hl),a
inc hl

ld a,(de)
inc de
ld (hl),a
inc hl

ld a,(de)
inc de
ld (hl),a

pop hl
scr_next_line_hl ScrollWidth*2 
djnz charloop
ret

; initialize table, containing font offsets for each character
; hl - new font data
; a  - number of chars
InitFontCharOffsetTable:
push af
ld  e, (hl)
inc  hl
ld  a, (hl)
inc  hl
push  hl
ld h,a
Mul_HxE
push  hl
pop  bc
pop  hl
pop af
ld  de,charoffset_table
@fonttableloop:
ex  de,hl
ld  (hl), e
inc  hl
ld  (hl), d
inc  hl
ex  de,hl
add  hl,bc
dec  a
or  a
jr  nz, @fonttableloop
ret
; screen related variables
; current screen offset in crtc values
current_scr_offset:
dw  &3000
scroll_delay:
db 0
scroll_cnt
db  4
scrolltext_ptr;
dw scroll_text
scroll_text:
db ' FIRST      ',1,120
db ' THERE WAS GFORCE ',1,100
db '        NOW        ',1,100
db ' AFTER 25 YEARS  ',1,120
db  '   WE ARE BACK   ',1,140
db '       UNION PRESENTS  ',1,240
db  ' A PRODUCTION FOR SPECCY PL 2O15 CLASSIC DEMO COMPETITION      '
db  ' PROGRAM GRAPHICS AND DESIGN BY DR PIOTR        '
db ' UNLIMITED BOBS  ',2,0
db  'MUSIC BY BEN DAGLISH RIPPED FROM MASTERS OF UNIVERSE GAME BY GREMLIN GRAPHICS',2,0

; interupt im 1 handler routine, called every 300th of second

InterruptHandler:
di
; store used registers on the stack
push  af
push  bc
push  de
push  hl
push  ix
; find if an interrupt was triggered in vertical blanking
ld  b,&f5
in  a,(c)
rra
jr nc,@UpdateIntCnt
; if so, reset interrupt counter
xor  a
jr  @SkipCnt
@UpdateIntCnt:
ld  a,(IntCnt)
inc  a
@skipcnt:
;  handle various operations depending on screen vertical position
ld  (IntCnt),a
or a
jr  nz, @next1
; change video mode to 0
ld  bc,&7f8c
out  (c),c
; set screen size to 256 pixels (32 crtc chars)
ld  bc,&bc01
out  (c),c
ld  bc,&bd20
out  (c),c
ld  bc,&bc02
out  (c),c
ld  bc,&bd2a
out  (c),c
; on every 50th of second call music player
call  music_play
ld hl,(CallCnt)
ld a,l
or h
jr z,IntExit
dec hl
ld (CallCnt),hl
jp  ShakeEffect
@next1:

cp  #3
jr nz,@next2 
jr  IntExit

@next2: cp  #4
jr nz,IntExit 

ld bc,#bc02
out  (c),c
ld bc,#bd2a
out  (c),c
; in lower part of the screenm change the screen mode to 1
ld  bc,&7f8d
out  (c),c
; set overscan width = 400 pixels (50 crtc chars)
ld  bc,&bc01
out  (c),c
ld  bc,&bd32
out  (c),c

ld  bc,&bc02
out  (c),c
ld  bc,&bd33
out  (c),c

ld  bc,&7f00
ld  a,#54
out  (c),c
out  (c),a
; call raster effect
jr  RasterEffect

IntExit:
; reset colors to default after raster effect
ld  hl,Colors
ld  e, 8
call  SetColors
; restore registers from stack
pop  ix
pop  hl
pop  de
pop  bc
pop  af
; return from interrupt
ei
reti

; this keeps track of the section of the screen we are currenty in
IntCnt:
db  0
CallCnt:
dw 0
ShakeEffectEnabled:
db  0
RasterEffectEnabled:
db  1
UnlimitedBobsEffectEnabled:
db 0

; various effects called from interrupt routine

; Raster effect - make stripes on the scroll font
RasterEffect:
; check if raster effect is enabled
ld  a, (RasterEffectEnabled)
or  a
jr  z, IntExit

defs 19,0
ld  b,12
@raster_effect_loop:
push bc
; set colors to a color from the table
ld hl,RasterColorsTable
ld  bc,&7f01
out  (c),c
ld  a,4
@raster_effect_loop2:
outi
; nops to exactly sync to the next raster line
defs  54,0
inc  b
dec  a
jr  nz,@raster_effect_loop2
; move colors in the table to get the moving raster effect
ld hl,RasterColorsTable+1
ld de,RasterColorsTable
ld a,(de)
ld bc,12
ldir
ld (RasterColorsTable+12),a
defs 20,0
pop bc
djnz  @raster_effect_loop
jp  IntExit

RasterColorsTable: 
db  #5c,#4c,#4e,#4a,#4b,#4a,#4e,#4c,#54,#54,#54,#54,#54

; shake effect - shakes the screen by changing position of horizontal sync. pulse
ShakeEffect:
; check if the effect is enabled
ld  a,(ShakeEffectEnabled)
or  a
jp  z, IntExit
; wait a bit
ld b,20
@wait_loop:
djnz @wait_loop
ld  hl,(WavePtr)
ld b,5
@shake_loop:
push  bc
; change the position of horizontal sync. pulse crtc register
  ld  bc,#bc02
out  (c),c
inc  b
ld a,(hl)
inc hl
sub #3
out (c),a
pop bc
djnz @shake_loop
; change the offset to the table to get the moving shake effect
ld hl,(WavePtr)
inc  hl
ld a, (ShakeCnt)
dec a
jr  nz,@shake_skip
; reset table offsets and counters
xor a
ld  (ShakeEffectEnabled),a
ld a,28
ld hl, waves
@shake_skip:
ld (ShakeCnt),a
ld (WavePtr),hl
shake_reset:
jp  IntExit

ShakeCnt:
db 28
WavePtr:
dw waves

waves:
db 42,43,44,45,46,47,48,49,48,47,46,45,44,43,42,43,44,45,46,47,48,49
db 45,44,43,42,43,44
db 45,46,47,48,49,49,49,48,47,46,45,44,43,42,43,44,45,45

; get sinus value from the table - also available as a macro
sin:  
sub  64
; get cosinus value from the table - also available as a macro
cos:  
ld  l,a
ld  h,0
ld  de, CosTable
add  hl,de
ld  a,(hl)
ret  
; get sin value from the table in fixed point 8.8
sinFP:  
sub  64  
; get cossin value from the table in fixed point 8.8
cosFP:
ld  l,a
ld  h,0
ld  de, CosTable
add  hl,de
ld  a,(hl)
add 64
ld  l,a
rla
; sbc  a,a
; ld  h,a
ld h,0
; add  hl, hl
add  hl, hl
ret  

; 256 values of cosinus, kept between -64,64 range - calculated as cos(2*PI*a/256)/64
CosTable:
defb  64, 64, 64, 64, 64, 64, 63, 63
defb  63, 62, 62, 62, 61, 61, 60, 60
defb  59, 59, 58, 57, 56, 56, 55, 54
defb  53, 52, 51, 50, 49, 48, 47, 46
defb  45, 44, 43, 42, 41, 39, 38, 37
defb  36, 34, 33, 32, 30, 29, 27, 26
defb  24, 23, 22, 20, 19, 17, 16, 14
defb  12, 11,  9,  8,  6,  5,  3,  2
defb   0, -2, -3, -5, -6, -8, -9,-11
defb -12,-14,-16,-17,-19,-20,-22,-23
defb -24,-26,-27,-29,-30,-32,-33,-34
defb -36,-37,-38,-39,-41,-42,-43,-44
defb -45,-46,-47,-48,-49,-50,-51,-52
defb -53,-54,-55,-56,-56,-57,-58,-59
defb -59,-60,-60,-61,-61,-62,-62,-62
defb -63,-63,-63,-64,-64,-64,-64,-64
defb -64,-64,-64,-64,-64,-64,-63,-63
defb -63,-62,-62,-62,-61,-61,-60,-60
defb -59,-59,-58,-57,-56,-56,-55,-54
defb -53,-52,-51,-50,-49,-48,-47,-46
defb -45,-44,-43,-42,-41,-39,-38,-37
defb -36,-34,-33,-32,-30,-29,-27,-26
defb -24,-23,-22,-20,-19,-17,-16,-14
defb -12,-11, -9, -8, -6, -5, -3, -2
defb   0,  2,  3,  5,  6,  8,  9, 11
defb  12, 14, 16, 17, 19, 20, 22, 23
defb  24, 26, 27, 29, 30, 32, 33, 34
defb  36, 37, 38, 39, 41, 42, 43, 44
defb  45, 46, 47, 48, 49, 50, 51, 52
defb  53, 54, 55, 56, 56, 57, 58, 59
defb  59, 60, 60, 61, 61, 62, 62, 62
defb  63, 63, 63, 64, 64, 64, 64, 64


;16x16
SpriteData:
defb &FF,&00,&FF,&00,&FF,&00,&00,&0C,&00,&0C,&FF,&00,&FF,&00,&FF,&00; line 0
defb &FF,&00,&FF,&00,&00,&0C,&00,&00,&00,&00,&00,&0C,&FF,&00,&FF,&00; line 1
defb &FF,&00,&AA,&04,&00,&78,&00,&E4,&00,&C0,&00,&C0,&55,&08,&FF,&00; line 2
defb &FF,&00,&00,&1C,&00,&F0,&00,&A0,&00,&30,&00,&30,&00,&84,&FF,&00; line 3
defb &AA,&04,&00,&D8,&00,&F0,&00,&40,&00,&30,&00,&30,&00,&60,&55,&08; line 4
defb &AA,&04,&00,&78,&00,&E4,&00,&90,&00,&30,&00,&30,&00,&60,&55,&08; line 5
defb &00,&4C,&00,&78,&00,&A0,&00,&30,&00,&30,&00,&30,&00,&C0,&00,&04; line 6
defb &00,&08,&00,&78,&00,&A0,&00,&30,&00,&30,&00,&60,&00,&00,&00,&8C; line 7
defb &00,&08,&00,&44,&00,&C8,&00,&30,&00,&30,&00,&80,&00,&00,&00,&8C; line 8
defb &00,&08,&00,&C0,&00,&90,&00,&60,&00,&C0,&00,&00,&00,&44,&00,&8C; line 9
defb &AA,&04,&00,&C0,&00,&30,&00,&C0,&00,&80,&00,&44,&00,&6C,&55,&08; line 10
defb &AA,&04,&00,&40,&00,&90,&00,&C0,&00,&00,&00,&9C,&00,&B4,&55,&08; line 11
defb &FF,&00,&00,&08,&00,&C0,&00,&80,&00,&00,&00,&78,&00,&A4,&FF,&00; line 12
defb &FF,&00,&AA,&04,&00,&40,&00,&80,&00,&00,&00,&3C,&55,&08,&FF,&00; line 13
defb &FF,&00,&FF,&00,&00,&0C,&00,&00,&00,&00,&00,&0C,&FF,&00,&FF,&00; line 14
defb &FF,&00,&FF,&00,&FF,&00,&00,&0C,&00,&0C,&FF,&00,&FF,&00,&FF,&00; line 15

; a table of pointers to screen memory lines
screen_lines_table:
ds 200*2,0

SetColors:
; hl - color table of hardware values
; e  - number of colors to set
ld  bc,&7f00
@loop: ld  a,(hl)
inc  hl
out  (c),c
out  (c),a
inc  c
dec  e
jr  nz, @loop
ld  bc,&7f10
ld  a,(hl)
out  (c),c
out  (c),a
ret

Colors:
db #57,#55,#54,#5f,#44,#4b,#5b,#54,#57

; include music player and data
read "MastersOfUniverseMusic.asm"


; build screen lines lookup tables
BuildLookupTable:
ld  de,&0000
ld  b,200
ld  hl, screen_lines_table
build_table_loop:
ld  (hl),e
inc hl
ld  (hl),d
inc  hl
scr_next_line_de &40 
dec  b
jr  nz,build_table_loop
ret


; table of character offsets in a font
charoffset_table:
ds  MaxCharsInFont*2,0

; include compiled fonts
ScrollFont:
incbin "ScrollFont.bin"