TETRISK1.A

Assembly source for Tetrisk, 1370 lines. Raw file

;	TETRISK v1.0 - Basic graphics.
;	<c> LKS 1994.
;	History:	Create 2/5/94.	Now 2/5/94.

POINTER_SIZE	equ 11+256*17
POINTER_SPRITE	equ 16
POINTER_MASK	equ 16+11
POINTER_UNDER	equ 16+22
TINYNUM_SIZE	equ 4+256*5
TINYNUM_SPRITE	equ 16+320*27
SMALLNUM_SIZE	equ 5+256*7
SMALLNUM_SPRITE	equ 49+320*10
BIGNUM_SIZE	equ 7+256*9
BIGNUM_SPRITE	equ 49

dseg

filename:	db 'TETRISK.V10', 00h
handle:		dw 0000h

mouseflag:	dw 0000h	;  Contains -1 if mouse present.
oldmode:	db 00h		;  Screen mode before change to graphics.

loadmagic:	dw 0000h
loadver:	dw 0000h
checkmagic:	db 'TR'
checkver:	db 01h,00h

mousex:		dw 0000h
mousey:		dw 0000h

score:		db 00h, 00h, 00h, 00h, 00h, 00h, 00h, 01h
scoreadd:	dd 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h

speed:		db 10

piecenum:	db 00h		;  Number of active pieces (up to 8).

;piece_x:	dq 0000h	;  X co-ordinates of pieces.
;piece_y:	dq 0000h	;  Y co-ordinate.
;piece_type:	dq 0000h	;  Type of piece (0-6).



cseg

    ;  Processor registers.
stor_cs:	dw 0000h
stor_ds:	dw 0000h
stor_gs:	dw 0A000h
stor_ss:	dw 0000h

    ;  Memory allocation registers. (Each one is a free 64k segment).
stor_s1:	dw 0000h	;  A copy of the background screen + pal.
stor_s2:	dw 0000h	;  The character storage space.
stor_s3:	dw 0000h	;  Reserved for future expansion.
stor_s4:	dw 0000h        ;  Reserved for future expansion

public main_
main_:
  call logon
  jc main_end
  call maingame
  call logoff
main_end:
  ret

;----------------------------- Interrupt handler(s) -------------------------

;  Graphics bitblt.
;  CL - Length.
;  CH - Height.
;  DS:SI - Source sprite.
;  ES:DI - Destination sprite.

graph_blit:
  cld
  xor bh, bh
  mov bl, cl
  mov ax, 320
  sub ax, bx
graph_blit_y:
  mov bx, cx
  xor ch, ch
  rep movsb
  mov cx, bx
  add si, ax
  add di, ax
  dec ch
  jnz graph_blit_y
  ret


;  Graphics bitblt on green.
;  CL - Length.
;  CH - Height.
;  DS:SI - Source sprite.
;  ES:DI - Destination sprite.

graph_blitg:
  xor bh, bh
  mov bl, cl
  mov dx, 320
  sub dx, bx
graph_blitg_y:
  mov bx, cx
  xor ch, ch
graph_blitg_x:
  mov al, ds: [si]
  cmp byte es: [di], 224
  jl graph_blitg_no
  cmp byte es: [di], 240
  jge graph_blitg_no
  mov es: [di], al
graph_blitg_no:
  inc di
  inc si
  loop graph_blitg_x
  mov cx, bx
  add si, dx
  add di, dx
  dec ch
  jnz graph_blitg_y
  ret


;  Graphics bitblt special.
;  CL - Length.
;  CH - Height.
;  DS:SI - Source sprite.
;  ES:DI - Destination sprite.

graph_blitspec:
  xor bh, bh
  mov bl, cl
  mov dx, 320
  sub dx, bx
graph_blitspec_y:
  mov bx, cx
  xor ch, ch
graph_blitspec_x:
  mov al, ds: [si]
  db 26h
graph_blitsin:
  db 88h		;  MOV ES:[DI], AL = 26 88 05
  db 05h                ;  AND ES:[DI], AL = 26 20 05
  inc di		;  XOR ES:[DI], AL = 26 30 05
  inc si
  loop graph_blitspec_x
  mov cx, bx
  add si, dx
  add di, dx
  dec ch
  jnz graph_blitspec_y
  ret


;  Score addon.
;  Adds var scoreadd to score (both byte decimals (one byte = one digit)).

score_addon:
  mov cx, 8
  mov bx, offset cs: scoreadd + 7
score_addloop:
  mov al, ds: [bx + 8]
  add al, ds: [bx]
score_testcarry:
  cmp al, 10
  jl score_noverflow
  sub al, 10
  cmp cx, 1
  je score_testcarry
  inc byte ds: [bx - 1]
  jmp score_testcarry
score_noverflow:
  mov ds: [bx], al
  loop score_addloop
  ret



;----------------------------- Game subfunctions ----------------------------

;  Maingame.

maingame:
  call game_logon

maingame_loop:

  mov cx, 200
  mov es, cs: stor_gs
  mov di, 0
l2:
  mov byte es: [di], 0
  add di, 320
  loop l2

  call graphwait

  mov cx, 200
  mov es, cs: stor_gs
  mov di, 0
l1:
  mov byte es: [di], 255
  add di, 320
  loop l1

  call game_mouseoff

;  call score_addon

    ;  REST OF GAME CODE GOES IN HERE!!!

  call game_conveyor

  call game_display1
  call game_display2

  call game_mouseon

  in al, 60h
  cmp al, 129
  jne maingame_loop

  call game_logoff
  ret

;  Game mouse off.

game_mouseoff:
    ;  Turn off mouse pointer.
  mov ax, 320
  mul word ds: mousey
  add ax, word ds: mousex
  mov ds, cs: stor_s2
  mov si, POINTER_UNDER
  mov es, cs: stor_gs
  mov di, ax
  mov cx, POINTER_SIZE
  call graph_blit
  mov ds, cs: stor_ds
  ret

;  Game move conveyor.

game_conveyor:
    ; Move conveyor up.
  mov ds, cs: stor_gs
  mov es, cs: stor_gs
  mov si, 133+320*94
  mov di, 133+320*93
  mov cx, 39+256*28
  call graph_blit	;  Top part.
  mov si, 133+320*140
  mov di, 133+320*139
  mov cx, 39+256*60
  call graph_blit	;  Bottom part.
    ;  Put in extra sections.
  mov ds, cs: stor_s2
  mov si, cs: game_conv_pat
  mov di, 133+320*121
  mov cx, 39+256*1
  call graph_blit
  mov si, cs: game_conv_pat
  mov di, 133+320*199
  mov cx, 39+256*1
  call graph_blit
  mov ax, cs: game_conv_pat
  add ax, 39
  cmp ax, 64000-39
  jl game_conv_nover
  sub ax, 320*12-39
game_conv_nover:
  mov cs: game_conv_pat, ax
  mov ds, cs: stor_ds
    ;  Put on the highlight....
  mov di, 133+320*93
  mov al, 4
game_conv_hiloop_y:
  mov cx, 39
game_conv_hiloop_x:
  inc byte es: [di]
  inc di
  loop game_conv_hiloop_x
  add di, 320-39
  dec al
  jnz game_conv_hiloop_y
    ;  ...And the shadow.
  mov di, 133+320*142
  mov al, 2
game_conv_shadloop_y:
  mov cx, 39
game_conv_shadloop_x:
  cmp byte es: [di], 32
  jge game_conv_nomore
  dec byte es: [di]
game_conv_nomore:
  inc di
  loop game_conv_shadloop_x
  add di, 320-39
  dec al
  jnz game_conv_shadloop_y

  ret

game_conv_pat:	dw 64000-320*12

;  Game display 1.

game_display1:
    ;  Put on flashing 0's and timer :.
  mov es, cs: stor_gs
  mov ax, cs: game_disp1_flash
  inc ax
  mov cs: game_disp1_flash, ax
  cmp al, 45
  jl game_disp1_flashon
  mov ds, cs: stor_s1
  mov es, cs: stor_gs
  mov si, 270+320*88
  mov di, si
  mov cx, 5+256*5
  call graph_blitg
  mov si, 275+320*88
  mov di, si
  mov cx, 16+256*16
  call graph_blit
  mov ds, cs: stor_ds
  mov byte es: [280+320*83], 236
  mov byte es: [280+320*85], 236	;  Draw colon.
  jmp game_disp1_flashoff
game_disp1_flashon:
  mov ds, cs: stor_s2
  mov es, cs: stor_gs
  mov si, TINYNUM_SPRITE
  mov di, 270+320*88
  mov cx, TINYNUM_SIZE
  call graph_blitg
  mov si, TINYNUM_SPRITE
  mov di, 275+320*88
  mov cx, TINYNUM_SIZE
  call graph_blit
  mov si, TINYNUM_SPRITE
  mov di, 282+320*88
  mov cx, TINYNUM_SIZE
  call graph_blit
  mov si, TINYNUM_SPRITE
  mov di, 287+320*88
  mov cx, TINYNUM_SIZE
  call graph_blit
  mov ds, cs: stor_ds
  mov byte es: [280+320*83], 224
  mov byte es: [280+320*85], 224	;  Clear colon.
game_disp1_flashoff:
    ;  Display clock.
  mov ah, 02h
  int 1Ah		;  Read real-time clock.
  mov ds, cs: stor_s2
  mov es, cs: stor_gs
  cmp dh, cs: game_disp1_sec
  je game_disp1_nosec
  mov cs: game_disp1_sec, dh
  mov word cs: game_disp1_flash, 0	;  Reset flash counter if sec passed.
game_disp1_nosec:
    ;  0x:xx
  cmp ch, 00h
  jne game_disp1_no0
  mov ch, 12h
game_disp1_no0:
  cmp ch, 12h
  jle game_disp1_no24
  cmp ch, 20h
  jne game_disp1_not20
  mov ch, 08h
  jmp game_disp1_no24
game_disp1_not20:
  cmp ch, 21h
  jne game_disp1_not21
  mov ch, 09h
  jmp game_disp1_no24
game_disp1_not21:
  sub ch, 12h
game_disp1_no24:
  push cx
  xor cl, cl
  and ch, 0F0h
  shr ch, 1
  shr ch, 1
  xchg cl, ch
  mov si, TINYNUM_SPRITE
  add si, cx
  mov di, 270+320*82
  mov cx, TINYNUM_SIZE
  call graph_blitg
  pop cx
    ;  x0:xx
  push cx
  xor cl, cl
  and ch, 0Fh
  shl ch, 1
  shl ch, 1
  xchg cl, ch
  mov si, TINYNUM_SPRITE
  add si, cx
  mov di, 275+320*82
  mov cx, TINYNUM_SIZE
  call graph_blit
  pop cx
    ; xx:0x
  push cx
  xor ch, ch
  and cl, 0F0h
  shr cl, 1
  shr cl, 1
  mov si, TINYNUM_SPRITE
  add si, cx
  mov di, 282+320*82
  mov cx, TINYNUM_SIZE
  call graph_blit
  pop cx
    ; xx:x0
  push cx
  xor ch, ch
  and cl, 0Fh
  shl cl, 1
  shl cl, 1
  mov si, TINYNUM_SPRITE
  add si, cx
  mov di, 287+320*82
  mov cx, TINYNUM_SIZE
  call graph_blit
  pop cx
  cmp ch, 10			;  Is hour < 10 ?
  jg game_disp1_hour2
  mov ds, cs: stor_s1
  mov si, 270+320*82
  mov di, si
  mov cx, TINYNUM_SIZE
  call graph_blitg
game_disp1_hour2:
  mov ds, cs: stor_ds

  ret

game_disp1_flash:	dw 0000h
game_disp1_sec:		db 00h

;  Game diplay 2.

game_display2:
  ret

;  Game mouse on.

game_mouseon:
    ;  Turn mouse pointer on.
  mov ax, 3
  int 33h		;  Get mouse position.
  mov ds: mousex, cx
  mov ds: mousey, dx
    ;  Get sprite under pointer.
  mov ax, 320
  mul dx
  add ax, cx
  push ax
  mov ds, cs: stor_gs
  mov si, ax
  mov es, cs: stor_s2
  mov di, POINTER_UNDER
  mov cx, POINTER_SIZE
  call graph_blit
  mov ds, cs: stor_ds
    ;  Blit the AND mask.
  mov ds, cs: stor_s2
  mov si, POINTER_MASK
  mov es, cs: stor_gs
  pop di
  push di
  mov cx, POINTER_SIZE
  mov byte cs: graph_blitsin, 20h
  call graph_blitspec
    ;  Blit XOR sprite.
  mov ds, cs: stor_s2
  mov si, POINTER_SPRITE
  mov es, cs: stor_gs
  pop di
  mov cx, POINTER_SIZE
  mov byte cs: graph_blitsin, 30h
  call graph_blitspec
  mov ds, cs: stor_ds
  ret

;  Game logon.

game_logon:
    ;  Fade down text screen.
  mov ax, 1017h
  mov bx, 0
  mov cx, 100h
  mov dx, 64000
  mov es, cs: stor_s2
  int 10h		;  Get old palette.
  xor bx, bx
  mov cx, 100h
  mov dl, 6
  mov si, 64000
  mov ds, cs: stor_s2
  call graphfadedown
  mov ds, cs: stor_ds
    ;  Get old graphics mode and set it to MCGA 320x200x256.
  mov ah, 0Fh
  int 10h
  mov ds: oldmode, al
  mov ax, 13h
  int 10h
    ;  Clear the palette and copy S1 into GS.
  xor bx, bx
  mov cx, 100h
  call graphclearpalette
  cld
  xor di, di
  and si, di
  mov si, 0
  mov ds, cs: stor_s1
  mov es, cs: stor_gs
  mov cx, 32000
  rep movsw
  mov ds, cs: stor_ds
    ;  Set up variables.
  mov ax, 7
  mov cx, 10
  mov dx, 298
  int 33h		;  Set mouse X bounds.
  mov ax, 8
  mov cx, 60
  mov dx, 180
  int 33h		;  Set mouse Y bounds.
    ;  Set mouse position.
  mov ax, 4
  mov cx, 159
  mov dx, 99
  int 33h
  mov ds: mousex, 159
  mov ds: mousey, 99
    ;  Set up sprites.
  call game_mouseon
    ;  Fade up palette.
  xor bx, bx
  mov cx, 100h
  mov dl, 5
  mov si, 64000
  mov ds, cs: stor_s1
  call graphfadeup
  mov ds, cs: stor_ds
  ret

;  Game logoff

game_logoff:
    ;  Fade down graphics screen.
  xor bx, bx
  mov cx, 100h
  mov dl, 5
  mov si, 64000
  mov ds, cs: stor_s1
  call graphfadedown
  mov ds, cs: stor_ds
    ;  Back to text mode.
  mov ah, 0
  mov al, ds: oldmode
  int 10h
  xor bx, bx
  mov cx, 100h
    ;  Get text palette before clearing.
  mov ax, 1017h
  mov bx, 0
  mov cx, 100h
  mov dx, 64000
  mov es, cs: stor_s2
  int 10h
  call graphclearpalette
    ;  Pause one second.
  mov cx, 60
  call graphdelay
  call text
  db 0Dh, 0Ah, 'þ Thank you for playing TETRISK v1.0 þ', 0Dh, 0Ah, 00h
   ;  Fade up.
  xor bx, bx
  mov cx, 100h
  mov dl, 4
  mov si, 64000
  mov ds, cs: stor_s2
  call graphfadeup
  mov ds, cs: stor_ds
  ret



;----------------------------- Main subfunctions ----------------------------

;  Logon.

logon:
  call logon_storesegs
  jc logon_error
  call logon_message
  jc logon_error
  call logon_sysinfo
  jc logon_error
  call logon_memalloc
  jc logon_error
  call logon_testmouse
  jc logon_error
  call logon_openfile
  jc logon_error
  call logon_hookkbd
  jc logon_error
  call text
  db 0Dh, 0Ah, 'þ Press any key...', 00h
  call getkey
  call newline
logon_error:
  ret


;  Logon store segments.

logon_storesegs:
    ;  Store segments.
  mov cs: stor_cs, cs
  mov cs: stor_ds, ds
  mov cs: stor_ss, ss
  clc
  ret

;  Logon message.

logon_message:
    ;  Get screen mode.
  mov ah, 0Fh
  int 10h
  and al, 7Fh			;  Mask out bit 7 (no clear screen).
  mov ds: oldmode, al
    ;  Clear screen.
  xor ah, ah
  int 10h
    ;  Introduction.
  call text
  db 0Dh, 0Ah, 'þ TETRISK v1.0 þ <c> LKS 1994.', 0Dh, 0Ah, 0DH, 0Ah, 00h
  clc
  ret

;  Logon system information.

logon_sysinfo:
    ;  Get and interpret equipment status word.
  int 11h
  push ax
  call text
  db '  Processor: 80x86.', 0Dh, 0Ah, '  Co-processor: ', 00h
  pop ax
  and ax, 1
  jz logon_nocopro
  call text
  db 'Present.', 0Dh, 0Ah, 00h
  jmp logon_aftercopro
logon_nocopro:
  call text
  db 'Not present.', 0Dh, 0Ah, 00h
logon_aftercopro:
    ;  Get DOS info.
  mov ax, 3306h
  int 21h
  push dx
  add cs: logon_dosmax, bl
  add cs: logon_dosmin, bh
  and dl, 11b
  add cs: logon_dosrev, dl
  call text
  db '  DOS info: v'
logon_dosmax:	db '0.'
logon_dosmin:	db '0 r'
logon_dosrev:	db '0 ', 00h
  pop dx
  push dx
  test dh, 1000b
  jnz logon_romdos
  call text
  db 'RAM', 00h
  jmp logon_ramdos
logon_romdos:
  call text
  db 'ROM', 00h
logon_ramdos:
  pop dx
  test dh, 10000b
  jz logon_nohidos
  call text
  db ' HMA', 00h
logon_nohidos:
  call text
  db '.', 0Dh, 0Ah, 00h
    ;  See which disk DOS booted from.
  mov ax, 3305h
  int 21h
  add logon_dosboot, dl
  call text
  db '  DOS boot drive: '
logon_dosboot:	db '@:.', 0Dh, 0Ah, 00h
  clc
  ret

;  Logon memory allocation.

logon_memalloc:
    ;  See how much memory spare by trying to allocate too much.
  mov ah, 48h
  mov bx, 0FFFFh
  int 21h
  push bx
  mov ax, bx
  mov bx, offset cs: logon_memalloc_mem
  mov ch, 4
  mov ds, cs: stor_cs
  call hex2text
  mov ds, cs: stor_ds
  call text
  db '  Free memory: '
logon_memalloc_mem:	db '____h paragraphs.', 0Dh, 0Ah, 00h
  pop bx
  cmp bx, 4010h			;  10h more just to be on the safe side.
  jge logon_memok		;  There is enough memory.
  call text
  db 0Dh, 0Ah, 'þ ERROR þ Not enough memory.', 0Dh, 0Ah, 00h
  stc
  ret
logon_memok:
    ;  Allocate 4000h paragraphs of memory as four 64k chunks, S1-S4.
  mov ah, 48h
  mov bx, 1000h
  int 21h
  jc logon_memalloc_err
  mov cs: stor_s1, ax
  mov ah, 48h
  mov bx, 1000h
  int 21h
  jc logon_memalloc_err
  mov cs: stor_s2, ax
  mov ah, 48h
  mov bx, 1000h
  int 21h
  jc logon_memalloc_err
  mov cs: stor_s3, ax
  mov ah, 48h
  mov bx, 1000h
  int 21h
  jc logon_memalloc_err
  mov cs: stor_s4, ax
    ;  Display error contents of S1-S4.
  mov ds, cs: stor_cs
  mov ax, cs: stor_s1
  mov bx, offset cs: logon_memalloc_s1
  mov ch, 4
  call hex2text
  mov ax, cs: stor_s2
  mov bx, offset cs: logon_memalloc_s2
  mov ch, 4
  call hex2text
  mov ax, cs: stor_s3
  mov bx, offset cs: logon_memalloc_s3
  mov ch, 4
  call hex2text
  mov ax, cs: stor_s4
  mov bx, offset cs: logon_memalloc_s4
  mov ch, 4
  call hex2text
  mov ds, cs: stor_ds
  call text
  db '  þ S1: '
logon_memalloc_s1:	db '____h', 0Dh, 0Ah, '  þ S2: '
logon_memalloc_s2:	db '____h', 0Dh, 0Ah, '  þ S3: '
logon_memalloc_s3:	db '____h', 0Dh, 0Ah, '  þ S4: '
logon_memalloc_s4:	db '____h', 0Dh, 0Ah, 00h
    ;  Check how much memory spare after allocation.
  mov ah, 48h
  mov bx, 0FFFFh
  int 21h
  mov ax, bx
  mov bx, offset cs: logon_memalloc_aftermem
  mov ch, 4
  mov ds, cs: stor_cs
  call hex2text
  mov ds, cs: stor_ds
  call text
  db '  After allocation: '
logon_memalloc_aftermem:	db '____h paragraphs.', 0Dh, 0Ah, 00h
  clc
  ret
logon_memalloc_err:
    ;  Display error message.
  call text
  db 0Dh, 0Ah, 'þ ERROR þ Cannot allocate a 64k segment.', 0Dh, 0Ah, 00h
  stc
  ret

;  Logon test mouse

logon_testmouse:
    ;  Check if mouse driver loaded.
  call text
  db '  Mouse: ', 00h
  xor ax, ax
  int 33h
  mov ds: mouseflag, ax
  or ax, ax
  jnz logon_mouseok
  call text
  db 'Not present.', 0Dh, 0Ah, 00h
  jmp logon_aftermouse
logon_mouseok:
  add cs: logon_mouseb, bl
  call text
  db 'Present ('
logon_mouseb:	db '0 Buttons).', 0Dh, 0Ah, 00h
    ;  Set mouse speed.
  mov ax, 0Fh
  mov cx, 24
  mov dx, 24
  int 33h
logon_aftermouse:
  clc
  ret

;  Logon open data file.

logon_openfile:
  mov ax, 3D00h
  mov dx, offset cs: filename
  mov ds, cs: stor_ds
  int 21h
  jnc logon_fileok
  call text
  db 0Dh, 0Ah, 'þ ERROR þ Cannot open data file TETRISK.V10.', 0Dh, 0Ah, 00h
  stc
  ret
logon_fileok:
  mov ds: handle, ax
  call text
  db '  TETRISK data file: TETRISK.V10', 0Dh, 0Ah, 00h
    ;  Read 4-byte magic and version number pair and check.
  mov ah, 3Fh
  mov bx, ds: handle
  mov cx, 4
  mov dx, offset ds: loadmagic
  int 21h
  jnc logon_filetestok
  call text
  db 0Dh, 0Ah, 'þ ERROR þ Cannot read data file.', 0Dh, 0Ah, 00h
  stc
  ret
logon_filetestok:
  call text
  db '  þ Data magic: ', 00h
  mov ax, ds: loadmagic
  cmp ax, word ds: checkmagic
  je logon_magicok
  call text
  db 'Illegal.', 0Dh, 0Ah, 0Dh, 0Ah, 'þ ERROR þ Illegal data file magic.', 0Dh, 0Ah, 00h
  stc
  ret
logon_magicok:
  mov ax, loadver
  xchg ah, al
  mov bx, offset cs: logon_version
  mov ch, 2
  mov ds, cs: stor_cs
  call hex2text
  inc bx
  mov ch, 2
  call hex2text
  mov ds, cs: stor_ds
  call text
  db 'Accepted.', 0Dh, 0Ah, '  þ Data version: '
logon_version:	db '__.__', 0Dh, 0Ah, '  Loading file: ------------------.', 00h
    ;  Read palette.
  mov ax, 4200h
  mov bx, ds: handle
  xor cx, cx
  mov dx, 10h
  int 21h		;  Position file pointer.
  mov ah, 3Fh
  mov bx, ds: handle
  mov cx, 300h
  mov dx, 64000
  mov ds, stor_s1
  int 21h
  mov ds, cs: stor_ds
    ;  Load background screen into S1.
  mov ax, 4200h
  mov bx, ds: handle
  xor cx, cx
  mov dx, 310h
  int 21h		;  Position file pointer.
  mov bx, offset cs: logon_loadbackbar
  mov dx, 0
logon_loadback:
  push bx
  push dx
  mov ah, 3Fh
  mov bx, ds: handle
  mov cx, 6400
  mov ds, cs: stor_s1
  int 21h
  mov ds, cs: stor_ds
  call text
  db 0Dh, '  Loading file: '
logon_loadbackbar:	db '------------------.', 00h
  pop dx
  pop bx
  mov byte cs: [bx], 254
  inc bx
  add dx, 6400
  cmp dx, 64000
  jne logon_loadback
    ;  Load character screen into S2.
  mov bx, offset cs: logon_loadcharbar
  mov dx, 0
logon_loadchar:
  push bx
  push dx
  mov ah, 3Fh
  mov bx, ds: handle
  mov cx, 6400
  mov ds, cs: stor_s2
  int 21h
  mov ds, cs: stor_ds
  call text
  db 0Dh, '  Loading file: þþþþþþþþþ'
logon_loadcharbar:	db '---------.', 00h
  pop dx
  pop bx
  mov byte cs: [bx], 254
  inc bx
  add dx, 6400
  cmp dx, 64000
  jne logon_loadchar
  call newline
    ;  Close data file.
  mov ah, 3Eh
  mov bx, ds: handle
  int 21h
  clc
  ret

;  Logon hook keyboard IRQ.

logon_hookkbd:
  call text
  db '  Keyboard IRQ: Cannot hook.', 0Dh, 0Ah, 00h
  clc
  ret

;  Logoff.

logoff:
  clc
  ret


;----------------------------- Library Functions ----------------------------

;  Text
;  Usage:	call text
;            	db 'Message... ', 00h	;  Null terminated.
;		<next instruction>

text:
  pop si
textloop:
  push si
  mov ah, 0Eh
  mov al, byte cs: [si]
  mov bx, 7
  int 10h
  pop si
  inc si
  cmp byte cs: [si], 0
  jne textloop
  inc si
  push si
  ret


;  NewLine

newline:
  call text
  db 0Dh, 0Ah, 00h
  ret


;  Delete

delete:
  mov ah, 0Eh
  mov al, 08
  mov bx, 15
  int 10h
  ret


;  Hex2Text
;  Inputs:	AX = Hex number.
;		CH = Number of digits.
;		DS:BX = Pointer to output string.

hex2text:
  mov di, ax
  mov cl, 12
  shr di, cl
  add di, offset cs: hex2textlookup
  mov cl, cs: [di]
  mov ds: [bx], cl
  mov cl, 4
  shl ax, cl
  inc bx
  dec ch
  jne hex2text
  ret
hex2textlookup:
  db '0123456789ABCDEF'



;  GetKey
;  Returns character in AL.

getkey:
  mov ah, 7
  int 21h
  cmp ah, 0
  jne getkey_notext	;  Not an extended keycode.
  mov ah, 7
  int 21h
  mov ah, al
  xor al, al
getkey_notext:
  ret



;  GetKeyExt
;  Returns char in AL, and sets carry if it is an extended keycode.

getkeyext:
  call getkey
  cmp al, 0
  jne getkeyextnot
  call getkey
  stc
  ret
getkeyextnot:
  ret



;  Clear Key Buf

clearkeybuf:
  mov ah, 0Bh
  int 21h			;  Check for character waiting.
  cmp al, 0
  jne clearkeybufget		;  Jump over the RET if there is.
  ret
clearkeybufget:
  call getkey
  jmp clearkeybuf



;  Error
;  Usage:	call error		;  No return from this function.
;		db '<Error message>'	;  Null terminated.

error:
  call text
  db 'ERROR - þ'
    ;  Display error message.
  pop si
errortext:
  push si
  mov ah, 0Eh
  mov al, byte cs: [si]
  mov bx, 15
  int 10h
  pop si
  inc si
  cmp byte cs: [si], 0
  jne errortext
  inc si
  push si
  call newline
    ;  Get extended error information.
  mov ah, 59h
  mov bx, 00h
  int 21h
  push cx
  push bx
  push ax
    ;  Put AX into string.
  pop ax
  mov bx, offset cs: errorax
  mov ch, 4
  mov ds, word cs: stor_cs
  call hex2text
    ;  Put BX into string.
  pop ax
  mov bx, offset cs: errorbx
  mov ch, 4
  mov ds, word cs: stor_cs
  call hex2text
    ;  Put CH into string.
  pop ax
  mov bx, offset cs: errorch
  mov ch, 2
  mov ds, word cs: stor_cs
  call hex2text
  mov ds, word cs: stor_ds
    ;  Write error information.
  call text
  db ' Return codes (21/59) - AX:'
errorax:	db '0000 BX:'
errorbx:	db '0000 CH:'
errorch:	db '00þ'
  call newline
  ret

;----------------------------- Graphics Functions ---------------------------

;  768-byte buffer for temporary storage of palettes.
;  Filled with very misleading text for anyone who looks at the executable.
graphpal:
db 'ERROR - There seems to be a sheep in the computer.$n'
db 'Please contact us immediately on: 0296 613225.þ'
db 'ERROR - This computer is not a PC compatible$nþ'
db 'ERROR - Not enough memory (BATTEROID requires at least 4Gb)$nþ'
db 'FATAL ERROR - No processor detected (BATTEROID needs at least one to run!)$nþ'
db 'ERROR - Fixed disk fault$nþ'
db 'ERROR - Printer not connected$nþ'
db 'WARNING - The %s has %s$nþ'
db 'Initiating self destruct sequence. $nþ10þ09þ08þ07þ06þ05þ04þ03þ02þ01þ00þ'
db '$n$nþ'
db 'DEBUG Mode activated. Please type in your authorization code:þ'
db '%sþ0123675440þPOVþ$n$n$dþ'
db 'ERROR - Indirect maximum payload already sequenced.$nþ'
db 'TEST System: AX:þ BX:þ CX:þ DX:þ $nSegment:%Xh Offset:%Xh$nþ'
db 'This program requires Microsoft Golfþ'
db 'LKSþJDWþTgGWfpOn*íþ'
db 'ERROR - Cannot relocate BOOT sector.$nþ'
db ' Motorcycle Emptyness...$n'



;  Graphics Wait.

graphwait:
  mov dx, 3DAh		;  Vertical reset @ bit 3 of port 3DAh.
graphwaitloop:
  in al, dx             ;  Read the port.
  test al, 08h          ;  Test bit 3.
  jz graphwaitloop      ;  Not set?  Repeat until it is.
  ret



;  Graphics Delay
;  Input:	CX - Number of vertical retraces to wait.

graphdelay:
  push ax             	;  Store registers which are used.
  push dx
graphdelayloop:
  call graphwait
graphdelaywait:
  in al, dx             ;  Read the port.
  and al, 08h           ;  Test bit 3.
  cmp al, 0
  jne graphdelaywait	;  Is it set?  Repeat until it is not set.
  loop graphdelayloop	;  Decrement CX and loop until CX = 0.
  pop dx
  pop ax               	;  Restore the trashed registers.
  ret



;  Graphics Clear Palette.
;  Inputs:	BX - First colour to clear.
;		CX - Number of colours.

graphclearpalette:
  mov ax, cx		;  Multiply CX by three to get the size of the
  add cx, ax            ; palette in bytes (each colour is three bytes).
  add cx, ax
  push ax               ;  Keep a copy of the old CX for after the loop.
  push bx		;  Keep a backup of BX for after the loop.
graphclearpaletteloop:
  mov byte cs: graphpal[bx], 0
  inc bx
  loop graphclearpaletteloop
  mov ax, 1012h
  pop bx
  pop cx
  mov dx, offset cs: graphpal
  mov es, word cs: stor_cs
  int 10h
  ret



;  Graphics Fade up.
;  Inputs:
;
;	BX - First colour register to be faded.
;       CX - Number of colours.
;	DL - Number of steps (2^DL).
;	DS:SI - Destination palette.

graphfade_first:	dw 0000h
graphfade_bytes:	dw 0000h
graphfade_cols:		dw 0000h
graphfade_steps:	db 00h
graphfade_loop:		db 00h

graphfadeup:
    ;  Set up the fade variables.
  mov word cs: graphfade_first, bx		;  Store the params.
  mov word cs: graphfade_cols, cx
  mov byte cs: graphfade_steps, dl
  mov bx, cx				;  Multiply the number of colours
  add bx, cx                            ; by three - the size of the pal
  add bx, cx				; in bytes.  There are three bytes
  mov word cs: graphfade_bytes, bx     	; per colour.
    ;  Copy the dest pal into the pal buffer.
  push si
  cld					;  Copy direction = forward.
  mov cx, bx
  mov es, word cs: stor_cs
  mov di, offset graphpal
  rep movsb
  pop si
    ;  Copy DS into ES now instead of before the palette change to save time.
  mov ax, ds
  mov es, ax
    ;  The dest palette is used as fade workspace as it will end up back in
    ; it's original state after the fade up.
  mov byte cs: graphfade_loop, 0	;  Clear counter.
graphfadeuploop:
  xor bx, bx
graphfadeupcolloop:
  mov al, byte cs: graphfade_loop
  mul byte cs: graphpal[bx]
  mov cl, byte cs: graphfade_steps
  shr ax, cl
  mov byte ds: [si+bx], al
  inc bx
  cmp bx, word cs: graphfade_bytes
  jl graphfadeupcolloop
    ;  Wait for VScan.
  call graphwait
    ;  Set palette.
  mov ax, 1012h
  mov bx, word cs: graphfade_first
  mov cx, word cs: graphfade_cols
  mov dx, si
  int 10h
  inc byte cs: graphfade_loop
  mov bl, byte cs: graphfade_loop
  mov cl, byte cs: graphfade_steps
  shr bl, cl
  cmp bl, 1
  jne graphfadeuploop
    ;  Set the palette to the dest palette.
  mov cx, word cs: graphfade_bytes
  mov ds, word cs: stor_cs
  mov di, si
  mov si, offset cs: graphpal
  rep movsb
    ;  Set palette.
  mov ax, 1012h
  mov bx, word cs: graphfade_first
  mov cx, word cs: graphfade_cols
  mov dx, offset cs: graphpal
  mov es, word cs: stor_cs
  int 10h
  ret



;  Graphics Fade down.
;  Inputs:
;
;	BX - First colour register to be faded.
;       CX - Number of colours.
;	DL - Number of steps (2^DL).
;	DS:SI - Destination palette.

graphfadedown:
    ;  Set up the fade variables.
  mov word cs: graphfade_first, bx		;  Store the params.
  mov word cs: graphfade_cols, cx
  mov byte cs: graphfade_steps, dl
  mov bx, cx				;  Multiply the number of colours
  add bx, cx                            ; by three - the size of the pal
  add bx, cx				; in bytes.  There are three bytes
  mov word cs: graphfade_bytes, bx     	; per colour.
    ;  Copy the dest pal into the pal buffer.
  push si
  cld					;  Copy direction = forward.
  mov cx, bx
  mov es, word cs: stor_cs
  mov di, offset cs: graphpal
  rep movsb
  pop si
    ;  Copy DS into ES now instead of before the palette change to save time.
  mov ax, ds
  mov es, ax
    ;  The dest palette is used as fade workspace and is reset to
    ; it's original state after the fade down.
  mov cl, dl
  mov dl, 1
  shl dl, cl
  mov byte cs: graphfade_loop, dl	;  Set counter to maximum.
graphfadedownloop:
  xor bx, bx
graphfadedowncolloop:
  mov al, byte cs: graphfade_loop
  mul byte cs: graphpal[bx]
  mov cl, byte cs: graphfade_steps
  shr ax, cl
  mov byte ds: [si+bx], al
  inc bx
  cmp bx, word cs: graphfade_bytes
  jne graphfadedowncolloop
    ;  Wait for VScan.
  call graphwait
    ;  Set palette.
  mov ax, 1012h
  mov bx, word cs: graphfade_first
  mov cx, word cs: graphfade_cols
  mov dx, si
  int 10h
    ;  Decrement counter and loop if greater than or equal to zero.
  dec byte cs: graphfade_loop
  cmp byte cs: graphfade_loop, 0
  jge graphfadedownloop
    ;  Reset the source palette to it's original values by copying the
    ; palette buffer into it.
  mov cx, word cs: graphfade_bytes
  mov ax, ds
  mov es, ax
  mov ds, word cs: stor_cs
  mov di, si
  mov si, offset cs: graphpal
  rep movsb
  mov ds, word cs: stor_ds	;  Restore DS.
  ret