;
; File: atari2600.library.asm
; Author: Neil Cafferkey
; Copyright (C) 1999-2001 Neil Cafferkey
;
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation; either version 2
; of the License, or (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 59 Temple Place - Suite 330, Boston,
; MA 02111-1307, USA.
;

; This shared library provides an emulated Atari 2600 environment to
; translated Atari 2600 programs.


	; Specify that this library contains code that requires at least
	; a 68020 processor.

	machine	68020


	; Includes

	include "exec/types.i"
	include	"exec/macros.i"
	include	"exec/nodes.i"
	include "exec/initializers.i"
	include "exec/memory.i"
	include "exec/resident.i"
	include	"intuition/screens.i"
	include	"libraries/gadtools.i"
	include	"graphics/text.i"
	include	"graphics/displayinfo.i"
	include	"utility/tagitem.i"
	include	"workbench/workbench.i"
	include	"workbench/startup.i"
	include	"workbench/icon.i"
	include	"devices/audio.i"
	include	"exec/io.i"
	include	"libraries/asl.i"

	include	"libraries/atari2600.i"

	include	"atari2600base.i"
	include	"atari2600.library_rev.i"


	; Constant definitions

SCREEN_BUFFER_COUNT	equ	3

SCREEN_DEPTH		equ	7
X_MAG			equ	2

VSYNC_SPEED_FACTOR	equ	259

DEFAULT_START_LINE	equ	30	; officially 37

TV_WIDTH		equ	160
DEFAULT_TV_HEIGHT	equ	230	; officially 192
TV_HEIGHT		equ	DEFAULT_TV_HEIGHT
MENU_BAR_HEIGHT		equ	11

BYTES_PER_LINE	equ	TV_WIDTH/8
SUMMARY_SIZE	equ	((DEFAULT_TV_HEIGHT-1)>>2+1)<<2
PATTERN_SIZE	equ	SUMMARY_SIZE*BYTES_PER_LINE
LIST_SIZE	equ	DEFAULT_TV_HEIGHT*3*8

	BITDEF	SWCHB,NORESET,0
	BITDEF	SWCHB,NOSELECT,1
	BITDEF	SWCHB,COLOUR,3
	BITDEF	SWCHB,P0PRO,6
	BITDEF	SWCHB,P1PRO,7

	BITDEF	SWCHA,P1UP,0
	BITDEF	SWCHA,P1DOWN,1
	BITDEF	SWCHA,P1LEFT,2
	BITDEF	SWCHA,P1RIGHT,3
	BITDEF	SWCHA,P0UP,4
	BITDEF	SWCHA,P0DOWN,5
	BITDEF	SWCHA,P0LEFT,6
	BITDEF	SWCHA,P0RIGHT,7

JOY_CODE	equ	$7f
PADDLE_CHARGE_THRESHOLD	equ	4000000


I4_LATCHES_MASK		equ	$40

	; Enumerated constants for graphics objects

	ENUM
	EITEM	PF
	EITEM	P0
	EITEM	P1
	EITEM	BL
	EITEM	M0
	EITEM	M1
	EITEM	OBJECT_COUNT

REDUCED_OBJECT_COUNT	equ	OBJECT_COUNT/2

	BITDEF	OBJECT,PF,PF
	BITDEF	OBJECT,P0,P0
	BITDEF	OBJECT,P1,P1
	BITDEF	OBJECT,BL,BL
	BITDEF	OBJECT,M0,M0
	BITDEF	OBJECT,M1,M1

	ENUM
	EITEM	PF_COLOUR
	EITEM	P0_COLOUR
	EITEM	P1_COLOUR
	EITEM	BK_COLOUR
	EITEM	PEN_COUNT

PATTERN_COUNT		equ	OBJECT_COUNT+PEN_COUNT*SCREEN_DEPTH+1
REDUCED_PATTERN_COUNT	equ	REDUCED_OBJECT_COUNT+PEN_COUNT*SCREEN_DEPTH+1

OBJO_UNDELAYEDGRAPHICS	EQU	24
OBJW_UNDELAYEDGRAPHICS	EQU	8
OBJO_DELAYEDGRAPHICS	EQU	16
OBJW_DELAYEDGRAPHICS	EQU	8
OBJO_POSITION		EQU	8
OBJW_POSITION		EQU	8
	BITDEF	OBJ,REFLECTION,24
	BITDEF	OBJ,VDELAY,25
	BITDEF	OBJ,VBLANK,26
	BITDEF	OBJ,LOCKED,27
OBJO_NUMBERSIZE		EQU	0
OBJW_NUMBERSIZE		EQU	3
OBJO_WIDTH		EQU	0
OBJW_WIDTH		EQU	2
	BITDEF	OBJ,UNDELAYEDENABLE,0
	BITDEF	OBJ,DELAYEDENABLE,8
OBJO_GRAPHICS0		EQU	8
OBJW_GRAPHICS0		EQU	8
OBJO_GRAPHICS1		EQU	16
OBJW_GRAPHICS1		EQU	8
OBJO_GRAPHICS2		EQU	24
OBJW_GRAPHICS2		EQU	8

PENO_LEFTCOLOUR		EQU	8
PENW_LEFTCOLOUR		EQU	8
PENO_RIGHTCOLOUR	EQU	16
PENW_RIGHTCOLOUR	EQU	8
PENO_NORMALCOLOUR	EQU	24
PENW_NORMALCOLOUR	EQU	8
	BITDEF	PEN,VBLANK,26
	BITDEF	PEN,SCOREMODE,25
	BITDEF	PEN,NORMALENABLE,0
	BITDEF	PEN,RIGHTENABLE,8
	BITDEF	PEN,LEFTENABLE,16

	BITDEF	PRI,ON,0

SOUND_FREQUENCY		equ	31400
SOUND_PERIOD		equ	(1000000000/280)/SOUND_FREQUENCY
SOUND_PRIORITY		equ	70
AUDIO_CHANNEL_COUNT	equ	2
AUDIO_BUFFER_COUNT	equ	2

FOUR_BIT_POLY_SIZE	equ	$f
FIVE_BIT_POLY_SIZE	equ	$1f
NINE_BIT_POLY_SIZE	equ	$1ff
SLOW_PURE_SIZE		equ	6

FIVE_BIT_POLY_PATTERN	equ	%0010110011111000110111010100001<<1
DIV_31_PATTERN		equ	%0100000000000000000100000000000<<1

SOUND_0_MULTIPLE	equ	4	;256	; Must be multiple of four
SOUND_1_MULTIPLE	equ	2	;16	; Must be even
SOUND_2_MULTIPLE	equ	2	; Must be even
SOUND_3_MULTIPLE	equ	2	; Must be even
SOUND_4_MULTIPLE	equ	4	;256	; Must be multiple of four
SOUND_6_MULTIPLE	equ	2	; Must be even
SOUND_7_MULTIPLE	equ	2	; Must be even
SOUND_8_MULTIPLE	equ	2	; Must be even
SOUND_9_MULTIPLE	equ	2	; Must be even
SOUND_12_MULTIPLE	equ	1	;42
SOUND_14_MULTIPLE	equ	2	; Must be even
SOUND_15_MULTIPLE	equ	2	; Must be even


	ENUM
	EITEM	NOCONTROLLER
	EITEM	JOYSTICK
	EITEM	PADDLES
	EITEM	DRIVING
	EITEM	KEYPAD
	EITEM	CONTROLLER_COUNT


SCREENMODE_STR_SIZE	equ	12


	; The structure representing an Atari 2600 environment

	STRUCTURE	Atari2600Env,0
	APTR	env_ProgramName
	APTR	env_OriginalStackPtr
	FPTR	env_ExitFunction
	APTR	env_DiskObject
	APTR	env_ProgramFileName
	ULONG	env_HomeDirLock
	APTR	env_ScreenTags
	APTR	env_Screen
	APTR	env_WindowTags
	APTR	env_ScreenReq
	APTR	env_ScreenReqTags
	ULONG	env_ScreenModeID
	APTR	env_ScreenModeString
	APTR	env_Window
	APTR	env_Menus
	APTR	env_VisualInfo
	APTR	env_UserPort
	STRUCT	env_ScreenBitMaps,SCREEN_BUFFER_COUNT*4
	STRUCT	env_ScreenBuffers,SCREEN_BUFFER_COUNT*4
	APTR	env_SafeMsgPort
	APTR	env_DispMsgPort
	APTR	env_AboutRequester
	ULONG	env_FrameCount
	ULONG	env_PrevFrameCount
	ULONG	env_SecondCount
	ULONG	env_MillisecondCount
	UWORD	env_FramesPerSecond
	BOOL	env_VerticalSync
	ULONG	env_ClockAtVSyncOn
	ULONG	env_ClockAtPotDumpDisable
	ULONG	env_ClockAtPaddleUpdate
	STRUCT	env_PaddleCharges,4*4
	STRUCT	env_PaddlesCharged,4*2
	APTR	env_Data
	ULONG	env_ClockOffset
	BOOL	env_VerticalBlank
	UWORD	env_StartingLineNo
	UWORD	env_TVHeight
	UWORD	env_TVHeightPad
	ULONG	env_LineOffset
	APTR	env_PlanePtrs
	STRUCT	env_TempPlanePtrs,SCREEN_DEPTH*4
	APTR	env_DifferencePattern
	APTR	env_ObjectDifferencePattern
	ULONG	env_Timer
	UWORD	env_TimerShift
	BOOL	env_I4LatchEnable
	STRUCT	env_Settings,PATTERN_COUNT*4
	APTR	env_PatternLine
	STRUCT	env_Patterns,PATTERN_COUNT*4
	STRUCT	env_MergedPatterns,REDUCED_PATTERN_COUNT*(SCREEN_BUFFER_COUNT+1)*4
	STRUCT	env_PatternLists,PATTERN_COUNT*(SCREEN_BUFFER_COUNT+1)*4
	STRUCT	env_PatternListPtrs,PATTERN_COUNT*4
	STRUCT	env_PatternListLimits,PATTERN_COUNT*4
	STRUCT	env_DifferencePatterns,REDUCED_PATTERN_COUNT*4
	STRUCT	env_SummaryPatterns,OBJECT_COUNT*4
	STRUCT	env_CollisionPatterns,OBJECT_COUNT*(SCREEN_BUFFER_COUNT+1)*4
	STRUCT	env_MotionRates,OBJECT_COUNT*2
	UWORD	env_FrameSkipRatio
	UWORD	env_VSyncOffAttempts
	UWORD	env_IdealFrameRate
	UWORD	env_SkipMenuOffset
	ULONG	env_PatternSize
	ULONG	env_ScreenHeight
	ULONG	env_RoundedTVHeight
	APTR	env_AudioIO
	APTR	env_AudioMsgPort
	BOOL	env_AudioOpen
	WORD	env_AudioKey
	STRUCT	env_AudioChannelMasks,AUDIO_CHANNEL_COUNT*4
	STRUCT	env_PerVolIO,AUDIO_CHANNEL_COUNT*4
	STRUCT	env_ToneTypeIO,AUDIO_CHANNEL_COUNT*AUDIO_BUFFER_COUNT*4
	STRUCT	env_ToneTypeNo,2
	STRUCT	env_FrequencyNo,2
	STRUCT	env_VolumeNo,2
	BOOL	env_SoundOn
	ULONG	env_ScreenSwapWaitTime
	STRUCT	env_ControllerTypes,2
	BOOL	env_WBStart
	BOOL	env_PotsDumped
	UWORD	env_MouseX
	IFD	PROFILE
	STRUCT	env_ProfileData,1024
	ENDC
	LABEL	env_SIZEOF


	code


Start:

	; Exit with an error if an attempt is made to run the library as a
	; program.

	moveq	#-1,d0
	rts

rom_tag:
				; STRUCTURE RT,0
	dc.w	RTC_MATCHWORD	; UWORD RT_MATCHWORD
	dc.l	rom_tag		; APTR  RT_MATCHTAG
	dc.l	end_of_library	; APTR  RT_ENDSKIP
	dc.b	RTF_AUTOINIT	; UBYTE RT_FLAGS
	dc.b	VERSION		; UBYTE RT_VERSION
	dc.b	NT_LIBRARY	; UBYTE RT_TYPE
	dc.b	0		; BYTE  RT_PRI
	dc.l	library_name	; APTR  RT_NAME
	dc.l	id_string	; APTR  RT_IDSTRING
	dc.l	init_table	; APTR  RT_INIT


	; Declare the library's name

library_name:

	ATARI2600NAME


	; Incorporate the version string

id_string:

	VSTRING


	; Declare the names of other libraries used

intuition_name:

	dc.b	"intuition.library",0

gadtools_name:

	dc.b	"gadtools.library",0

graphics_name:

	dc.b	"graphics.library",0

utility_name:

	dc.b	"utility.library",0

icon_name:

	ICONNAME

dos_name:

	dc.b	"dos.library",0

asl_name:

	AslName

audio_name:

	AUDIONAME


	cnop	0,2

init_table:

	; Declare the auto-initialisation table referenced in the rom tag

	dc.l	Atari2600Base_SIZEOF	; size of library base data space
	dc.l	function_table		; pointer to function initializers
	dc.l	data_table		; pointer to data initializers
	dc.l	Initialise		; routine to run


function_table:

	; Standard system routines

	dc.l	Open
	dc.l	Close
	dc.l	Expunge
	dc.l	Null

	; Stella functions ($0-$3f)

	dc.l	VSync
	dc.l	VBlank
	dc.l	WSync
	dc.l	NoFunction
	dc.l	NuSiz0
	dc.l	NuSiz1
	dc.l	CoLuP0
	dc.l	CoLuP1
	dc.l	CoLuPF
	dc.l	CoLuBk
	dc.l	CtrlPF
	dc.l	RefP0
	dc.l	RefP1
	dc.l	PF0
	dc.l	PF1
	dc.l	PF2
	dc.l	ResP0
	dc.l	ResP1
	dc.l	ResM0
	dc.l	ResM1
	dc.l	ResBl
	dc.l	AudC0
	dc.l	AudC1
	dc.l	AudF0
	dc.l	AudF1
	dc.l	AudV0
	dc.l	AudV1
	dc.l	GrP0
	dc.l	GrP1
	dc.l	EnaM0
	dc.l	EnaM1
	dc.l	EnaBl
	dc.l	HMP0
	dc.l	HMP1
	dc.l	HMM0
	dc.l	HMM1
	dc.l	HMBl
	dc.l	VDelP0
	dc.l	VDelP1
	dc.l	VDelBl
	dc.l	ResMP0
	dc.l	ResMP1
	dc.l	HMove
	dc.l	HMClr
	dc.l	CxClr
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction

	; Stella function duplicates ($40-$7f)

	dc.l	VSync
	dc.l	VBlank
	dc.l	WSync
	dc.l	NoFunction
	dc.l	NuSiz0
	dc.l	NuSiz1
	dc.l	CoLuP0
	dc.l	CoLuP1
	dc.l	CoLuPF
	dc.l	CoLuBk
	dc.l	CtrlPF
	dc.l	RefP0
	dc.l	RefP1
	dc.l	PF0
	dc.l	PF1
	dc.l	PF2
	dc.l	ResP0
	dc.l	ResP1
	dc.l	ResM0
	dc.l	ResM1
	dc.l	ResBl
	dc.l	AudC0
	dc.l	AudC1
	dc.l	AudF0
	dc.l	AudF1
	dc.l	AudV0
	dc.l	AudV1
	dc.l	GrP0
	dc.l	GrP1
	dc.l	EnaM0
	dc.l	EnaM1
	dc.l	EnaBl
	dc.l	HMP0
	dc.l	HMP1
	dc.l	HMM0
	dc.l	HMM1
	dc.l	HMBl
	dc.l	VDelP0
	dc.l	VDelP1
	dc.l	VDelBl
	dc.l	ResMP0
	dc.l	ResMP1
	dc.l	HMove
	dc.l	HMClr
	dc.l	CxClr
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction

	; Other functions

	dc.l	OpenAtari2600
	dc.l	CloseAtari2600
	dc.l	InstallROM

	; No functions ($83-$100)

	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction

	; Stella function duplicates ($100-$13f)

	dc.l	VSync
	dc.l	VBlank
	dc.l	WSync
	dc.l	NoFunction
	dc.l	NuSiz0
	dc.l	NuSiz1
	dc.l	CoLuP0
	dc.l	CoLuP1
	dc.l	CoLuPF
	dc.l	CoLuBk
	dc.l	CtrlPF
	dc.l	RefP0
	dc.l	RefP1
	dc.l	PF0
	dc.l	PF1
	dc.l	PF2
	dc.l	ResP0
	dc.l	ResP1
	dc.l	ResM0
	dc.l	ResM1
	dc.l	ResBl
	dc.l	AudC0
	dc.l	AudC1
	dc.l	AudF0
	dc.l	AudF1
	dc.l	AudV0
	dc.l	AudV1
	dc.l	GrP0
	dc.l	GrP1
	dc.l	EnaM0
	dc.l	EnaM1
	dc.l	EnaBl
	dc.l	HMP0
	dc.l	HMP1
	dc.l	HMM0
	dc.l	HMM1
	dc.l	HMBl
	dc.l	VDelP0
	dc.l	VDelP1
	dc.l	VDelBl
	dc.l	ResMP0
	dc.l	ResMP1
	dc.l	HMove
	dc.l	HMClr
	dc.l	CxClr
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction

	; Stella function duplicates ($140-$17f)

	dc.l	VSync
	dc.l	VBlank
	dc.l	WSync
	dc.l	NoFunction
	dc.l	NuSiz0
	dc.l	NuSiz1
	dc.l	CoLuP0
	dc.l	CoLuP1
	dc.l	CoLuPF
	dc.l	CoLuBk
	dc.l	CtrlPF
	dc.l	RefP0
	dc.l	RefP1
	dc.l	PF0
	dc.l	PF1
	dc.l	PF2
	dc.l	ResP0
	dc.l	ResP1
	dc.l	ResM0
	dc.l	ResM1
	dc.l	ResBl
	dc.l	AudC0
	dc.l	AudC1
	dc.l	AudF0
	dc.l	AudF1
	dc.l	AudV0
	dc.l	AudV1
	dc.l	GrP0
	dc.l	GrP1
	dc.l	EnaM0
	dc.l	EnaM1
	dc.l	EnaBl
	dc.l	HMP0
	dc.l	HMP1
	dc.l	HMM0
	dc.l	HMM1
	dc.l	HMBl
	dc.l	VDelP0
	dc.l	VDelP1
	dc.l	VDelBl
	dc.l	ResMP0
	dc.l	ResMP1
	dc.l	HMove
	dc.l	HMClr
	dc.l	CxClr
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction

	; No functions ($180-$27f)

	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction

	; Riot functions		; ($280-$2a0)

	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	InTim
	dc.l	Tim1T
	dc.l	Tim8T
	dc.l	Tim64T
	dc.l	Tim1024T

	; Skip to the $380th custom function

	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction

	; Riot function duplicates

	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	NoFunction
	dc.l	InTim
	dc.l	Tim1T
	dc.l	Tim8T
	dc.l	Tim64T
	dc.l	Tim1024T

	; Function table end marker

	dc.l	-1

	; Initialise the library's data table

data_table:

	INITBYTE	LN_TYPE,NT_LIBRARY
	INITLONG	LN_NAME,library_name
	INITBYTE	LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
	INITWORD	LIB_VERSION,VERSION
	INITWORD	LIB_REVISION,REVISION
	INITLONG	LIB_IDSTRING,id_string
	dc.l		0


	even


;****i* atari2600.library/Initialise ***
;
;   NAME
;	Initialise
;
;   SYNOPSIS
;	success = Initialise(library,exec,seg_list)
;	                     d0      a6   a0
;
;***
;
;

Initialise:

	; Push registers that should be preserved onto stack

	movem.l	d2-d4/a2/a4-a5,-(sp)

	; Store parameters

	move.l	d0,a5
	move.l	a6,(ab_SysLib,a5)
	move.l	a0,(ab_SegList,a5)

	; Check exec.library is v36+

	cmpi.w	#36,(LIB_VERSION,a6)
	blo	clean_up$

	; Open intuition.library v39+

	lea	intuition_name,a1
	moveq	#39,d0
	JSRLIB	OpenLibrary
	move.l	d0,ab_IntuitionLib(a5)
	beq	clean_up$

	; Open gadtools.library v39+

	lea	gadtools_name,a1
	moveq	#39,d0
	JSRLIB	OpenLibrary
	move.l	d0,ab_GadtoolsLib(a5)
	beq	clean_up$

	; Open graphics.library v39+

	lea	graphics_name,a1
	moveq	#39,d0
	JSRLIB	OpenLibrary
	move.l	d0,ab_GraphicsLib(a5)
	beq	clean_up$

	; Open utility.library v39+

	lea	utility_name,a1
	moveq	#39,d0
	JSRLIB	OpenLibrary
	move.l	d0,(ab_UtilityLib,a5)
	beq	clean_up$

	; Open any version of icon.library

	lea	icon_name,a1
	moveq	#0,d0
	JSRLIB	OpenLibrary
	move.l	d0,(ab_IconLib,a5)
	beq	clean_up$

	; Open dos.library v36+

	lea	dos_name,a1
	moveq	#36,d0
	JSRLIB	OpenLibrary
	move.l	d0,(ab_DosLib,a5)
	beq	clean_up$

	; Open asl.library v38+

	lea	asl_name,a1
	moveq	#38,d0
	JSRLIB	OpenLibrary
	move.l	d0,(ab_AslLib,a5)
	beq	clean_up$

	; Allocate memory for word-to-long stretch table

	move.l	#(1<<16)*4,d0
	move.l	#MEMF_PUBLIC,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_WordToLongTable,a5)
	beq	clean_up$

	; Fill in word-to-long stretch table

	movea.l	(ab_WordToLongTable,a5),a2

	move.w	#256-1,d1
	movea.l	#word_stretch_table,a1

upper_loop$:

	move.w	(a1)+,d2
	swap.w	d2

	move.w	#256-1,d0
	movea.l	#word_stretch_table,a0

lower_loop$:

	move.w	(a0)+,d2
	move.l	d2,(a2)+
	dbra	d0,lower_loop$

	dbra	d1,upper_loop$

	; Allocate memory for sound 0

	move.l	#SOUND_0_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds,a5)
	move.l	d0,(ab_Sounds+11*4,a5)
	beq	clean_up$

	; Create sound 0

	moveq	#SOUND_0_MULTIPLE>>2-1,d2
	movea.l	d0,a0

sound_0_loop$:

	move.l	#$7f7f7f7f,(a0)+
	dbra	d2,sound_0_loop$

	; Allocate memory for sound 1

	move.l	#FOUR_BIT_POLY_SIZE*SOUND_1_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+4,a5)
	beq	clean_up$

	; Create sound 1

	moveq	#SOUND_1_MULTIPLE-1,d2
	movea.l	d0,a2

sound_1_loop$:

	movea.l	#four_bit_poly_pattern,a0
	movea.l	a2,a1
	move.l	#FOUR_BIT_POLY_SIZE,d0
	JSRLIB	CopyMem

	adda.l	#FOUR_BIT_POLY_SIZE,a2
	dbra	d2,sound_1_loop$

	; Allocate memory for sound 2

	move.l	#FIVE_BIT_POLY_SIZE*FOUR_BIT_POLY_SIZE*SOUND_2_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+2*4,a5)
	beq	clean_up$

	; Create sound 2

	moveq	#SOUND_2_MULTIPLE-1,d2
	movea.l	d0,a0
	moveq	#-128,d0

sound_2_loop$:

	move.l	#DIV_31_PATTERN,d1
	moveq	#FOUR_BIT_POLY_SIZE-1,d4
	movea.l	#four_bit_poly_pattern,a1
	moveq	#0,d3

sound_2_inner_loop$:

	rol.l	#1,d1
	bcc	sound_2_no_change$
	move.b	(a1)+,d0
	dbra	d4,sound_2_no_change$
	moveq	#FOUR_BIT_POLY_SIZE-1,d4
	movea.l	#four_bit_poly_pattern,a1

sound_2_no_change$:

	cmpi.l	#DIV_31_PATTERN>>1,d1
	bne	sound_2_no_adjust$
	rol.l	#1,d1
	addq.b	#1,d3

sound_2_no_adjust$:

	move.b	d0,(a0)+

	cmpi.b	#FOUR_BIT_POLY_SIZE,d3
	bne	sound_2_inner_loop$
	dbra	d2,sound_2_loop$

	; Allocate memory for sound 3

	move.l	#FIVE_BIT_POLY_SIZE*FOUR_BIT_POLY_SIZE*SOUND_3_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+3*4,a5)
	beq	clean_up$

	; Create sound 3

	moveq	#SOUND_3_MULTIPLE-1,d2
	movea.l	d0,a0
	moveq	#-128,d0

sound_3_loop$:

	move.l	#FIVE_BIT_POLY_PATTERN,d1
	moveq	#FOUR_BIT_POLY_SIZE-1,d4
	movea.l	#four_bit_poly_pattern,a1
	moveq	#0,d3

sound_3_inner_loop$:

	rol.l	#1,d1
	bcc	sound_3_no_change$
	move.b	(a1)+,d0
	dbra	d4,sound_3_no_change$
	moveq	#FOUR_BIT_POLY_SIZE-1,d4
	movea.l	#four_bit_poly_pattern,a1

sound_3_no_change$:

	cmpi.l	#FIVE_BIT_POLY_PATTERN>>1,d1
	bne	sound_3_no_adjust$
	rol.l	#1,d1
	addq.b	#1,d3

sound_3_no_adjust$:

	move.b	d0,(a0)+

	cmpi.b	#FOUR_BIT_POLY_SIZE,d3
	bne	sound_3_inner_loop$
	dbra	d2,sound_3_loop$

	; Allocate memory for sound 4

	move.l	#SOUND_4_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+4*4,a5)
	move.l	d0,(ab_Sounds+5*4,a5)
	beq	clean_up$

	; Create sound 4

	moveq	#SOUND_4_MULTIPLE>>2-1,d2
	movea.l	d0,a0

sound_4_loop$:

	move.l	#$7f807f80,(a0)+
	dbra	d2,sound_4_loop$

	; Allocate memory for sound 6

	move.l	#31*SOUND_6_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+6*4,a5)
	move.l	d0,(ab_Sounds+10*4,a5)
	beq	clean_up$

	; Create sound 6

	moveq	#SOUND_6_MULTIPLE-1,d2
	movea.l	d0,a2

sound_6_loop$:

	movea.l	#div_31_pattern,a0
	movea.l	a2,a1
	move.l	#31,d0
	JSRLIB	CopyMem

	adda.l	#31,a2
	dbra	d2,sound_6_loop$

	; Allocate memory for sound 7

	move.l	#FIVE_BIT_POLY_SIZE*SOUND_7_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+7*4,a5)
	beq	clean_up$

	; Create sound 7

	moveq	#SOUND_7_MULTIPLE-1,d2
	movea.l	d0,a0
	moveq	#-128,d0

sound_7_loop$:

	moveq	#FIVE_BIT_POLY_SIZE-1,d3
	move.l	#FIVE_BIT_POLY_PATTERN,d1

sound_7_inner_loop$:

	lsl.l	#1,d1
	bcc	sound_7_no_change$
	not.b	d0

sound_7_no_change$:

	move.b	d0,(a0)+
	dbra	d3,sound_7_inner_loop$
	dbra	d2,sound_7_loop$

	; Allocate memory for sound 8

	move.l	#NINE_BIT_POLY_SIZE*SOUND_8_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+8*4,a5)
	beq	clean_up$

	; Create sound 8

	moveq	#SOUND_8_MULTIPLE-1,d2
	movea.l	d0,a2

sound_8_loop$:

	movea.l	#nine_bit_poly_pattern,a0
	movea.l	a2,a1
	move.l	#NINE_BIT_POLY_SIZE,d0
	JSRLIB	CopyMem

	adda.l	#NINE_BIT_POLY_SIZE,a2
	dbra	d2,sound_8_loop$

	; Allocate memory for sound 9

	move.l	#FIVE_BIT_POLY_SIZE*SOUND_9_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+9*4,a5)
	beq	clean_up$

	; Create sound 9

	moveq	#SOUND_9_MULTIPLE-1,d2
	movea.l	d0,a2

sound_9_loop$:

	movea.l	#five_bit_poly_pattern,a0
	movea.l	a2,a1
	move.l	#FIVE_BIT_POLY_SIZE,d0
	JSRLIB	CopyMem

	adda.l	#FIVE_BIT_POLY_SIZE,a2
	dbra	d2,sound_9_loop$

	; Allocate memory for sound 12

	move.l	#SLOW_PURE_SIZE*SOUND_12_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+12*4,a5)
	move.l	d0,(ab_Sounds+13*4,a5)
	beq	clean_up$

	; Create sound 12

	moveq	#SOUND_12_MULTIPLE-1,d2
	movea.l	d0,a2

sound_12_loop$:

	movea.l	#slow_pure_pattern,a0
	movea.l	a2,a1
	move.l	#SLOW_PURE_SIZE,d0
	JSRLIB	CopyMem

	adda.l	#SLOW_PURE_SIZE,a2
	dbra	d2,sound_12_loop$

	; Allocate memory for sound 14

	move.l	#93*SOUND_14_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+14*4,a5)
	beq	clean_up$

	; Create sound 14

	moveq	#SOUND_14_MULTIPLE-1,d2
	movea.l	d0,a2

sound_14_loop$:

	movea.l	#div_93_pattern,a0
	movea.l	a2,a1
	move.l	#93,d0
	JSRLIB	CopyMem

	adda.l	#93,a2
	dbra	d2,sound_14_loop$

	; Allocate memory for sound 15

	move.l	#FIVE_BIT_POLY_SIZE*3*SOUND_15_MULTIPLE,d0
	move.l	#MEMF_PUBLIC|MEMF_CHIP,d1
	JSRLIB	AllocMem
	move.l	d0,(ab_Sounds+15*4,a5)
	beq	clean_up$

	; Create sound 15

	moveq	#SOUND_15_MULTIPLE-1,d2
	movea.l	d0,a0
	moveq	#-128,d0

sound_15_loop$:

	moveq	#FIVE_BIT_POLY_SIZE-1,d3
	move.l	#FIVE_BIT_POLY_PATTERN,d1

sound_15_inner_loop$:

	lsl.l	#1,d1
	bcc	sound_15_no_change$
	not.b	d0

sound_15_no_change$:

	move.b	d0,(a0)+
	move.b	d0,(a0)+
	move.b	d0,(a0)+
	dbra	d3,sound_15_inner_loop$
	dbra	d2,sound_15_loop$

	; Set library pointer as return value

	move.l	a5,d0
	bra	skip_clean_up$

clean_up$:

	; Deallocate resources and set return value to nought

	bsr	CleanUp
	moveq	#0,d0

skip_clean_up$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d4/a2/a4-a5
	rts



;****i* atari2600.library/Open ***
;
;   NAME
;	Open
;
;   SYNOPSIS
;	library_pointer = Open(library,version)
;	d0                     a6      d0
;
;***
;
;

Open:

	; Mark the library as having another user

	addq.w	#1,(LIB_OPENCNT,a6)

	; Prevent delayed expunges

	bclr	#LIBB_DELEXP,(LIB_FLAGS,a6)

	; Return with library pointer as result

	move.l	a6,d0
	rts



;****i* atari2600.library/Close ***
;
;   NAME
;	Close
;
;   SYNOPSIS
;	Close(library)
;	      a6
;
;***
;
;

Close:

	; Set the default return value

	moveq	#0,d0

	; Mark the library as having one fewer users

	subq.w	#1,(LIB_OPENCNT,a6)

	; See if anyone still has the library open

	bne	skip_expunge$

	; See if there is a delayed expunge pending

	btst	#LIBB_DELEXP,(LIB_FLAGS,a6)
	beq	skip_expunge$

	; Expunge the library

	bsr	Expunge

skip_expunge$:

	; Return

	rts



;****i* atari2600.library/Expunge ***
;
;   NAME
;	Expunge
;
;   SYNOPSIS
;	Expunge(library)
;	        a6
;
;***
;
;

Expunge:

	; Push registers that should be preserved onto stack

	movem.l	d2/a5/a6,-(sp)

	; Store parameters

	move.l	a6,a5

	; Check if anyone has this library open

	tst.w	(LIB_OPENCNT,a5)
	beq	skip_set_delayed_expunge$

	; Set the delayed-expunge flag, set return value to null and skip
	; the rest of this function.

	bset	#LIBB_DELEXP,(LIB_FLAGS,a5)
	moveq	#0,d0
	bra	end$

skip_set_delayed_expunge$:

	; Get the library's segment list

	move.l	(ab_SegList,a5),d2

	; Unlink this library from the library list

	move.l	a5,a1
	move.l	(ab_SysLib,a5),a6
	JSRLIB	Remove

	; Deallocate the library's resources

	bsr	CleanUp

	; Set the library's segment list to be the return value

	move.l	d2,d0

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2/a5/a6
	rts



;****i* atari2600.library/Null ***
;
;   NAME
;	Null
;
;   SYNOPSIS
;	Null(library)
;	     a6
;
;***
;
;

Null:

	moveq	#0,d0
	rts



;****** atari2600.library/A2600_VSync ***
;
;   NAME
;	A2600_VSync -- Start or end an Atari 2600 video frame.
;
;   SYNOPSIS
;	A2600_VSync(new_state,clock,env)
;	            d0        d1    a1
;
;	VOID A2600_VSync(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Signals the start or end of the emulated vertical retrace period,
;	which occurs between every pair of consecutive frames.
;
;   INPUTS
;	new_state - Frame ends if bit 1 is set; otherwise a new frame is
;	    begun.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;   SEE ALSO
;	A2600_VBlank()
;
;***
;
;

VSync:
;   rts
	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a6,-(sp)

	; Store parameters

	movea.l	a6,a5
	movea.l	a1,a3
	move.l	d0,d2
	move.l	d1,d3

	; See what the new state is

	btst	#1,d2
	beq	off$

on$:

	; Calculate current colour clock

	mulu.w	#A2600_CLR_CLK_FACTOR,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update all pattern lists

	moveq	#PATTERN_COUNT-1,d2

pattern_update_loop$:

	move.l	d3,d1
	move.l	d2,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	dbra	d2,pattern_update_loop$

	; Check vsync isn't already on, then put it on

	tst.w	(env_VerticalSync,a3)
	bne	end$
	move.w	#1,(env_VerticalSync,a3)

	; Finish the current frame

	movea.l	a5,a6
	movea.l	a3,a1
	bsr	FinishFrame

	; Repaint areas of screen buffer that should change

	movea.l	a5,a6
	movea.l	a3,a1
	bsr	UpdateScreen

	; Exchange hidden bitmap with visible one

	movea.l	a5,a6
	movea.l	a3,a1
	bsr	ChangeDisplay

	bra	end$

off$:

	; Check vsync isn't already off

	tst.w	(env_VerticalSync,a3)
	beq	end$

	; Check if next frame should be skipped

	tst.w	(env_VSyncOffAttempts,a3)
	beq	reset_attempt_count$

	subq.w	#1,(env_VSyncOffAttempts,a3)
	bra	end$

reset_attempt_count$:

	move.w	(env_FrameSkipRatio,a3),d0
	subq.w	#1,d0
	move.w	d0,(env_VSyncOffAttempts,a3)

	; Turn vsync off

	clr.w	(env_VerticalSync,a3)

	; Go back to first line

	move.l	d3,d1
	divu.w	#A2600_LINE_CYCLES/3,d1
	clr.w	d1
	swap.w	d1
	sub.l	d1,d3
	add.l	d3,(env_ClockOffset,a3)
	sub.l	d3,(env_Timer,a3)
	sub.l	d3,(env_ClockAtPotDumpDisable,a3)
	sub.l	d3,(env_ClockAtPaddleUpdate,a3)

;	; Calculate how many lines we've gone up during vsync

;	move.w	d4,d0
;	sub.w	(env_ClockAtVSyncOn,a3),d0
;	mulu.w	#VSYNC_SPEED_FACTOR,d0
;	divu.w	#A2600_LINE_CYCLES/3*3,d0

;	; Correct clock cycle counter to be less than one scanline

;	move.l	d4,d0
;	divu.w	#TV_WIDTH,d0
;	clr.w	d0
;	swap.w	d0
;	move.l	d4,d1
;	sub.l	d0,d1
;	add.l	d1,(env_ClockOffset,a3)

	; Increment frame counter

	addq.l	#1,(env_FrameCount,a3)

	; Get and respond to input from menus, joysticks etc.

	bsr	GetUserInput

	; Initialise pattern lists for upcoming frame

	moveq	#PATTERN_COUNT-1,d2

pattern_init_loop$:

	movea.l	(env_PatternListPtrs,a3,d2*4),a0
	move.l	(env_Settings,a3,d2*4),(a0)+
	move.l	#0,(a0)

	dbra	d2,pattern_init_loop$

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a6

	rts



;****** atari2600.library/A2600_VBlank ***
;
;   NAME
;	A2600_VBlank -- Turn Atari 2600 vertical blanking on or off.
;
;   SYNOPSIS
;	A2600_VBlank(new_state,clock,env)
;	             d0        d1    a1
;
;	VOID A2600_VBlank(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Begins or ends the emulated vertical blanking period.
;
;   INPUTS
;	new_state - Vertical blanking begins if bit 1 is set; otherwise it
;	    ends.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

VBlank:
;   rts
	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	movem.l	d2-d7/a2-a6,-(sp)

	; Store parameters

	movea.l	a6,a5
	movea.l	a1,a3
	move.l	d0,d5
	move.l	d1,d4

	; Calculate current colour clock

	mulu.w	#A2600_CLR_CLK_FACTOR,d1
	move.l	d1,d3

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update all pattern lists

	moveq	#PATTERN_COUNT-1,d2

pattern_update_loop$:

	move.l	d3,d1
	move.l	d2,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	dbra	d2,pattern_update_loop$

	; See if new vblank state is on or off

	btst.b	#1,d5
	beq	off$

on$:

	; Record that vertical blanking is on

	move.w	#1,(env_VerticalBlank,a3)

	moveq	#PATTERN_COUNT-1,d0

on_loop$:

	move.l	(env_Settings,a3,d0*4),d1
	bset.l	#OBJB_VBLANK,d1
	move.l	d1,(env_Settings,a3,d0*4)
	dbra	d0,on_loop$

	bra	skip_off$

off$:

	; Record that vertical blanking is off

	clr.w	(env_VerticalBlank,a3)

	moveq	#PATTERN_COUNT-1,d0

off_loop$:

	move.l	(env_Settings,a3,d0*4),d1
	bclr.l	#OBJB_VBLANK,d1
	move.l	d1,(env_Settings,a3,d0*4)
	dbra	d0,off_loop$

skip_off$:

	; Update paddle charge

	move.l	d4,d1
	movea.l	a3,a1
	bsr	UpdatePaddleCharge

	; Check new pot dump state

	btst.b	#7,d5
	beq	dump_disabled$

	; Check pot dump isn't already enabled

	tst.w	(env_PotsDumped,a3)
	bne	no_dump_change$

	move.w	#1,(env_PotsDumped,a3)
	clr.l	(env_PaddleCharges,a3)
	clr.w	(env_PaddlesCharged,a3)

	; Clear all pot bits

	movea.l	(env_Data,a3),a0
	moveq	#$70,d0

pot_dump_enable_loop$:

	move.l	#$7f7f7f7f,(A2600_INPT0,a0,d0)
	subi.l	#$10,d0
	bpl	pot_dump_enable_loop$

	bra	no_dump_change$

dump_disabled$:

	clr.w	(env_PotsDumped,a3)

no_dump_change$:

;	move.w	d5,d0
;	and.w	#I4_LATCHES_MASK,d5
;	move.w	d0,env_I4LatchEnable(a3)
;
;	tst.w	d0
;	beq	skip$
;	move.b.w	#INPT4
;	move.b	#$80,([env_Data,a3],INPT4)
;	move.b	#$80,([env_Data,a3],INPT5)
;skip$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a6
	rts



;****** atari2600.library/A2600_WSync ***
;
;   NAME
;	A2600_WSync -- Wait for Atari 2600 horizontal synchronisation.
;
;   SYNOPSIS
;	A2600_WSync(clock,env)
;	            d1    a1
;
;	VOID A2600_WSync(ULONG,APTR);
;
;   FUNCTION
;	Moves the emulated clock ahead to the end of the current scanline.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

WSync:
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$2<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Store parameters

	move.l	d1,a0

	; Calculate number of clock cycles since start of current line

	divu.w	#A2600_LINE_CYCLES/3,d1

	; Check if we're already at end of line

	clr.w	d1
	swap.w	d1
	beq	at_eol$

	; Get number of cycles until end of line

	moveq	#A2600_LINE_CYCLES/3,d0
	sub.w	d1,d0

	; Adjust clock-cycle counter

	sub.l	d0,(env_ClockOffset,a1)

at_eol$:

	; Update paddle charge

	move.l	a0,d1
	bsr	UpdatePaddleCharge

	; Return

	rts



;****** atari2600.library/A2600_NuSiz0 ***
;
;   NAME
;	A2600_NuSiz0 -- Set size and duplication for first player & missile.
;
;   SYNOPSIS
;	A2600_NuSiz0(num_size,clock,env)
;	             d0       d1    a1
;
;	VOID A2600_NuSiz0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets size and duplication modes for first player and its associated
;	missile.
;
;   INPUTS
;	num_size - the number and size of the first player and missile.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

NuSiz0:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$4<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock plus delay

	move.l	d1,d0
	lsl.l	#1,d0
	add.l	d0,d1

;	mulu.w	#A2600_CLR_CLK_FACTOR,d1
	add.l	#12,d1

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update first player's pattern list

	move.l	d3,d1
	moveq	#P0,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Update first missile's pattern list

	move.l	d3,d1
	moveq	#M0,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Set missile width

	move.w	d2,d0
	lsr.w	#4,d0

	move.l	(env_Settings+M0*4,a3),d1
	bfins	d0,d1{OBJO_WIDTH:OBJW_WIDTH}
	move.l	d1,(env_Settings+M0*4,a3)

	; Store first player's new number and size

	move.l	(env_Settings+P0*4,a3),d0
	bfins	d2,d0{OBJO_NUMBERSIZE:OBJW_NUMBERSIZE}
	move.l	d0,(env_Settings+P0*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_NuSiz1 ***
;
;   NAME
;	A2600_NuSiz1 -- Set size and duplication for second player & missile.
;
;   SYNOPSIS
;	A2600_NuSiz1(num_size,clock,env)
;	             d0       d1    a1
;
;	VOID A2600_NuSiz1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets size and duplication modes for first player and its associated
;	missile.
;
;   INPUTS
;	num_size - the number and size of the second player and missile.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

NuSiz1:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$5<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock plus delay

	move.l	d1,d0
	lsl.l	#1,d0
	add.l	d0,d1

;	mulu.w	#A2600_CLR_CLK_FACTOR,d1
	add.l	#12,d1

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update first player's pattern list

	move.l	d3,d1
	moveq	#P1,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Update first missile's pattern list

	move.l	d3,d1
	moveq	#M1,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Set missile width

	move.w	d2,d0
	lsr.w	#4,d0

	move.l	(env_Settings+M1*4,a3),d1
	bfins	d0,d1{OBJO_WIDTH:OBJW_WIDTH}
	move.l	d1,(env_Settings+M1*4,a3)

	; Store first player's new number and size

	move.l	(env_Settings+P1*4,a3),d0
	bfins	d2,d0{OBJO_NUMBERSIZE:OBJW_NUMBERSIZE}
	move.l	d0,(env_Settings+P1*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_CoLuP0 ***
;
;   NAME
;	A2600_CoLuP0 -- Set Atari 2600 player 0 colour.
;
;   SYNOPSIS
;	A2600_CoLuP0(colour,clock,env)
;	             d0     d1    a1
;
;	VOID A2600_CoLuP0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets a new colour for an Atari 2600 environment's first player and
;	its associated missile.
;
;   INPUTS
;	colour - A colour-luminescence value in the format that would be
;	    sent to the Stella chip.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

CoLuP0:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$6<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d4/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	move.l	d1,d3
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

;	mulu.w	#A2600_CLR_CLK_FACTOR,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Get new colour

	move.b	(colour_remap_table.l,d2),d2

	; Initialise loop

	moveq	#SCREEN_DEPTH-1,d4
	rol.b	#1,d2

colour_loop$:

	; Update first player colour's pattern lists

	move.l	d3,d1
	moveq	#OBJECT_COUNT+P0_COLOUR*SCREEN_DEPTH,d0
	add.l	d4,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Update playfield colour's pattern lists

	move.l	d3,d1
	moveq	#OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH,d0
	add.l	d4,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Store one bit of new player 0 colour and playfield left colour

	rol.b	#1,d2

	move.l	(env_Settings+(OBJECT_COUNT+P0_COLOUR*SCREEN_DEPTH)*4,a3,d4*4),d0
	bfins	d2,d0{31-PENB_NORMALENABLE:1}
	move.l	d0,(env_Settings+(OBJECT_COUNT+P0_COLOUR*SCREEN_DEPTH)*4,a3,d4*4)

	move.l	(env_Settings+(OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH)*4,a3,d4*4),d0
	bfins	d2,d0{31-PENB_LEFTENABLE:1}
	move.l	d0,(env_Settings+(OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH)*4,a3,d4*4)

	; Loop

	dbra	d4,colour_loop$

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d4/a3

	rts



;****** atari2600.library/A2600_CoLuP1 ***
;
;   NAME
;	A2600_CoLuP1 -- Set Atari 2600 player 1 colour.
;
;   SYNOPSIS
;	A2600_CoLuP1(colour,clock,env)
;	             d0     d1    a1
;
;	VOID A2600_CoLuP1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets a new colour for an Atari 2600 environment's second player and
;	its associated missile.
;
;   INPUTS
;	colour - A colour-luminescence value in the format that would be
;	    sent to the Stella chip.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

CoLuP1:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$7<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d4/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	move.l	d1,d3
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

;	mulu.w	#A2600_CLR_CLK_FACTOR,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Get new colour

	move.b	(colour_remap_table.l,d2),d2

	; Initialise loop

	moveq	#SCREEN_DEPTH-1,d4
	rol.b	#1,d2

colour_loop$:

	; Update second player colour's pattern lists

	move.l	d3,d1
	moveq	#OBJECT_COUNT+P1_COLOUR*SCREEN_DEPTH,d0
	add.l	d4,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Update playfield colour's pattern lists

	move.l	d3,d1
	moveq	#OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH,d0
	add.l	d4,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Store one bit of new player 0 colour and playfield right colour

	rol.b	#1,d2

	move.l	(env_Settings+(OBJECT_COUNT+P1_COLOUR*SCREEN_DEPTH)*4,a3,d4*4),d0
	bfins	d2,d0{31-PENB_NORMALENABLE:1}
	move.l	d0,(env_Settings+(OBJECT_COUNT+P1_COLOUR*SCREEN_DEPTH)*4,a3,d4*4)

	move.l	(env_Settings+(OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH)*4,a3,d4*4),d0
	bfins	d2,d0{31-PENB_RIGHTENABLE:1}
	move.l	d0,(env_Settings+(OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH)*4,a3,d4*4)

	; Loop

	dbra	d4,colour_loop$

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d4/a3

	rts



;****** atari2600.library/A2600_CoLuPF ***
;
;   NAME
;	A2600_CoLuPF -- Set Atari 2600 playfield colour.
;
;   SYNOPSIS
;	A2600_CoLuPF(colour,clock,env)
;	             d0     d1    a1
;
;	VOID A2600_CoLuPF(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets a new colour for an Atari 2600 environment's playfield and
;	ball.
;
;   INPUTS
;	colour - A colour-luminescence value in the format that would be
;	    sent to the Stella chip.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

CoLuPF:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$8<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d4/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	move.l	d1,d3
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Get new colour

	move.b	(colour_remap_table.l,d2),d2

	; Initialise playfield colour loop

	moveq	#SCREEN_DEPTH-1,d4
	rol.b	#1,d2

pf_loop$:

	; Update playfield colour's pattern lists

	move.l	d3,d1
	moveq	#OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH,d0
	add.l	d4,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Store one bit of new colour

	rol.b	#1,d2

	move.l	(env_Settings+(OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH)*4,a3,d4*4),d0
	bfins	d2,d0{31-PENB_NORMALENABLE:1}
	move.l	d0,(env_Settings+(OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH)*4,a3,d4*4)

	; Loop

	dbra	d4,pf_loop$

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d4/a3

	rts



;****** atari2600.library/A2600_CoLuBk ***
;
;   NAME
;	A2600_CoLuBk -- Set Atari 2600 background colour.
;
;   SYNOPSIS
;	A2600_CoLuBk(colour,clock,env)
;	             d0     d1    a1
;
;	VOID A2600_CoLuBk(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets a new colour for an Atari 2600 environment's background.
;
;   INPUTS
;	colour - A colour-luminescence value in the format that would be
;	    sent to the Stella chip.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

CoLuBk:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$9<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d4/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	move.l	d1,d3
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Get new colour

	move.b	(colour_remap_table.l,d2),d2

	; Initialise background colour loop

	moveq	#SCREEN_DEPTH-1,d4
	rol.b	#1,d2

bk_loop$:

	; Update background colour's pattern lists

	move.l	d3,d1
	moveq	#OBJECT_COUNT+BK_COLOUR*SCREEN_DEPTH,d0
	add.l	d4,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Store one bit of new colour

	rol.b	#1,d2

	move.l	(env_Settings+(OBJECT_COUNT+BK_COLOUR*SCREEN_DEPTH)*4,a3,d4*4),d0
	bfins	d2,d0{31-PENB_NORMALENABLE:1}
	move.l	d0,(env_Settings+(OBJECT_COUNT+BK_COLOUR*SCREEN_DEPTH)*4,a3,d4*4)

	; Loop

	dbra	d4,bk_loop$

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d4/a3
	rts



;****** atari2600.library/A2600_CtrlPF ***
;
;   NAME
;	A2600_CtrlPF -- Set various playfield and ball modes.
;
;   SYNOPSIS
;	A2600_CtrlPF(modes,clock,env)
;	             d0    d1    a1
;
;	VOID A2600_CtrlPF(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Determines the playfield's reflection mode, score mode and priority,
;	    and the ball's width. Details are provided below.
;
;   INPUTS
;	modes - The meaning of this parameter's bits is as follows:
;
;	    Bit 0 determines playfield reflection (1=on, 0=off).
;
;	    If bit 1 is set, the left-hand side of the playfield will have
;	    the first player's colour and the right-hand side will have the
;	    second player's colour.
;
;	    If bit 2 is set, the playfield and ball graphics will have
;	    priority over all other objects' graphics. Otherwise, the
;	    playfield and ball graphics will have least priority.
;
;	    The 5th and 4th bits form a two-bit number that sets the ball's
;	    width according to the following table:
;
;	        00 - 1 colour clock
;	        01 - 2 colour clocks
;	        10 - 4 colour clocks
;	        11 - 8 colour clocks
;
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

CtrlPF:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$a<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d4/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	move.l	d1,d3
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Initialise loop

	moveq	#SCREEN_DEPTH-1,d4

colour_loop$:

	; Update playfield colour's pattern lists

	move.l	d3,d1
	moveq	#OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH,d0
	add.l	d4,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Set playfield score mode

	move.w	d2,d0
	lsr.w	#1,d0

	move.l	(env_Settings+(OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH)*4,a3,d4*4),d1
	bfins	d0,d1{31-PENB_SCOREMODE:1}
	move.l	d1,(env_Settings+(OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH)*4,a3,d4*4)

	; Loop

	dbra	d4,colour_loop$

	; Update playfield's pattern list

	move.l	d3,d1
	moveq	#PF,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Update ball's pattern list

	move.l	d3,d1
	moveq	#BL,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Update playfield priority pattern list

	move.l	d3,d1
	moveq	#PATTERN_COUNT-1,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Set playfield copy/reflection mode

	move.l	(env_Settings+PF*4,a3),d0
	bfins	d2,d0{31-OBJB_REFLECTION:1}
	move.l	d0,(env_Settings+PF*4,a3)

	; Set playfield priority

	move.w	d2,d0
	lsr.b	#2,d0

	move.l	(env_Settings+(PATTERN_COUNT-1)*4,a3),d1
	bfins	d0,d1{31-PRIB_ON:1}
	move.l	d1,(env_Settings+(PATTERN_COUNT-1)*4,a3)

	; Set ball width

	move.w	d2,d0
	lsr.w	#4,d0

	move.l	(env_Settings+BL*4,a3),d1
	bfins	d0,d1{OBJO_WIDTH:OBJW_WIDTH}
	move.l	d1,(env_Settings+BL*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d4/a3

	rts



;****** atari2600.library/A2600_RefP0 ***
;
;   NAME
;	A2600_RefP0 -- Set the reflection mode for player 0.
;
;   SYNOPSIS
;	A2600_RefP0(mode,clock,env)
;	            d0   d1    a1
;
;	VOID A2600_RefP0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Turns the first player's reflection on or off after a delay of one
;	colour clock.
;
;   INPUTS
;	mode - Bit 3 of this specifies the reflection mode: 0 for off, 1 for
;	    on.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

RefP0:
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$b<<2,a1)
	ENDC
;   rts
	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock plus delay

	move.l	d1,d0
	lsl.l	#1,d0
	add.l	d0,d1
	addq.l	#1,d1

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update first player's pattern list

	move.l	d3,d1
	moveq	#P0,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Set player's reflection mode

	lsr.b	#3,d2

	move.l	(env_Settings+P0*4,a3),d0
	bfins	d2,d0{31-OBJB_REFLECTION:1}
	move.l	d0,(env_Settings+P0*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_RefP1 ***
;
;   NAME
;	A2600_RefP1 -- Set the reflection mode for player 1.
;
;   SYNOPSIS
;	A2600_RefP1(mode,clock,env)
;	            d0   d1    a1
;
;	VOID A2600_RefP1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Turns the second player's reflection on or off after a delay of one
;	colour clock.
;
;   INPUTS
;	mode - Bit 3 of this specifies the reflection mode: 0 for off, 1 for
;	    on.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

RefP1:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$c<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock plus delay

	move.l	d1,d0
	lsl.l	#1,d0
	add.l	d0,d1
	addq.l	#1,d1

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update first player's pattern list

	move.l	d3,d1
	moveq	#P1,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Set player's reflection mode

	lsr.b	#3,d2

	move.l	(env_Settings+P1*4,a3),d0
	bfins	d2,d0{31-OBJB_REFLECTION:1}
	move.l	d0,(env_Settings+P1*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_PF0 ***
;
;   NAME
;	A2600_PF0 -- Set the first section of playfield graphics pattern.
;
;   SYNOPSIS
;	A2600_PF0(graphics,clock,env)
;	          d0       d1    a1
;
;	VOID A2600_PF0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the leftmost four bits of the 20-bit playfield pattern. The
;	new setting takes effect after a delay of between two and five
;	colour clock cycles.
;
;   INPUTS
;	graphics - The upper nibble of the lowest byte is the first section
;	    of the playfield pattern. Bit four is leftmost while bit seven
;	    is rightmost.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

PF0:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$d<<2,a1)
	ENDC

	; Check if pattern is changing

	move.b	(byte_reverse_table.l,d0),d0
	andi.b	#$f,d0
	cmp.b	(env_Settings+PF*4+1,a1),d0
	bne.s	change$
	rts

change$:

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	move.l	d1,d3
	movea.l	a1,a3

	; Calculate delay

	divu.w	#A2600_LINE_CYCLES/3,d1
	swap.w	d1
	moveq	#0,d0
	move.b	(playfield_delay_table.l,d1),d0

	; Calculate current colour clock plus delay

	move.l	d3,d1
	lsl.l	#1,d1
	add.l	d1,d3
	add.l	d0,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update playfield's pattern list

	move.l	d3,d1
	moveq	#PF,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

;	; Load address of byte reverse table

;	lea.l	byte_reverse_table,a0

	; Write to first section of playfield graphics

;	move.b	(a0,d2),(env_Settings+PF*4+1,a3)
	move.b	d2,(env_Settings+PF*4+1,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_PF1 ***
;
;   NAME
;	A2600_PF1 -- Set the second section of playfield graphics pattern.
;
;   SYNOPSIS
;	A2600_PF1(graphics,clock,env)
;	          d0       d1    a1
;
;	VOID A2600_PF1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets eight bits of the 20-bit playfield pattern, beginning with the
;	fifth from the left. The new setting takes effect after a delay of
;	between two and five colour clock cycles.
;
;   INPUTS
;	graphics - The lowest byte is the second section of the playfield
;	    pattern. Bit seven is leftmost while bit zero is rightmost.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

PF1:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$e<<2,a1)
	ENDC

	; Check if pattern is changing

	cmp.b	(env_Settings+PF*4+2,a1),d0
	bne.s	change$
	rts

change$:

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	move.l	d1,d3
	movea.l	a1,a3

	; Calculate delay

	divu.w	#A2600_LINE_CYCLES/3,d1
	swap.w	d1
	moveq	#0,d0
	move.b	(playfield_delay_table.l,d1),d0

	; Calculate current colour clock plus delay

	move.l	d3,d1
	lsl.l	#1,d1
	add.l	d1,d3
	add.l	d0,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update playfield's pattern list

	move.l	d3,d1
	moveq	#PF,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Write to second section of playfield graphics

	move.b	d2,(env_Settings+PF*4+2,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_PF2 ***
;
;   NAME
;	A2600_PF2 -- Set the third section of playfield graphics pattern.
;
;   SYNOPSIS
;	A2600_PF2(graphics,clock,env)
;	          d0       d1    a1
;
;	VOID A2600_PF2(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the rightmost eight bits of the 20-bit playfield pattern. The
;	new setting takes effect after a delay of between two and five
;	colour clock cycles.
;
;   INPUTS
;	graphics - The lowest byte is the third section of the playfield
;	    pattern. Bit zero is leftmost while bit seven is rightmost.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

PF2:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$f<<2,a1)
	ENDC

	; Check if pattern is changing

	move.b	(byte_reverse_table.l,d0),d0
	cmp.b	(env_Settings+PF*4+3,a1),d0
	bne.s	change$
	rts

change$:

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	move.l	d1,d3
	movea.l	a1,a3

	; Calculate delay

	divu.w	#A2600_LINE_CYCLES/3,d1
	swap.w	d1
	moveq	#0,d0
	move.b	(playfield_delay_table.l,d1),d0

	; Calculate current colour clock plus delay

	move.l	d3,d1
	lsl.l	#1,d1
	add.l	d1,d3
	add.l	d0,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update playfield's pattern list

	move.l	d3,d1
	moveq	#PF,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

;	; Load address of byte reverse table

;	lea.l	byte_reverse_table,a0

	; Write to third section of playfield graphics

;	move.b	(a0,d2),(env_Settings+PF*4+3,a3)
	move.b	d2,(env_Settings+PF*4+3,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_ResP0 ***
;
;   NAME
;	A2600_ResP0 -- Reset the position of player 0.
;
;   SYNOPSIS
;	A2600_ResP0(clock,env)
;	            d1    a1
;
;	VOID A2600_ResP0(ULONG,APTR);
;
;   FUNCTION
;	Sets the first player's horizontal position to the emulated video
;	beam's current horizontal position plus five colour clocks. If this
;	function is called during the emulated horizontal blank period, the
;	player's position will be set to the third colour clock of the line.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

ResP0:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$10<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d1,d3
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

;	mulu.w	#A2600_CLR_CLK_FACTOR,d3
	move.l	d3,d2

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update first player's pattern list

	move.l	d3,d1
	moveq	#P0,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Calculate first player's new position

	divu.w	#A2600_LINE_CYCLES,d2

	swap.w	d2
	sub.w	#A2600_H_BLANK_CYCLES,d2
	bhs	not_in_hblank$

	moveq	#-2,d2

not_in_hblank$:

	addq.w	#5,d2
	cmpi.w	#TV_WIDTH,d2
	blo	skip_wrap_around$

	subi.w	#TV_WIDTH,d2

skip_wrap_around$:

	; Store first player's new position

	move.b	d2,(env_Settings+P0*4+1,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_ResP1 ***
;
;   NAME
;	A2600_ResP1 -- Reset the position of player 1.
;
;   SYNOPSIS
;	A2600_ResP1(clock,env)
;	            d1    a1
;
;	VOID A2600_ResP1(ULONG,APTR);
;
;   FUNCTION
;	Sets the second player's horizontal position to the emulated video
;	beam's current horizontal position plus five colour clocks. If this
;	function is called during the emulated horizontal blank period, the
;	player's position will be set to the third colour clock of the line.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

ResP1:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$11<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d1,d3
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

;	mulu.w	#A2600_CLR_CLK_FACTOR,d3
	move.l	d3,d2

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Update second player's pattern list

	move.l	d3,d1
	moveq	#P1,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Calculate second player's new position

	divu.w	#A2600_LINE_CYCLES,d2

	swap.w	d2
	subi.w	#A2600_H_BLANK_CYCLES,d2
	bhs	not_in_hblank$

	moveq	#-2,d2

not_in_hblank$:

	addq.w	#5,d2
	cmpi.w	#TV_WIDTH,d2
	blo	skip_wrap_around$

	subi.w	#TV_WIDTH,d2

skip_wrap_around$:

	; Store second player's new position

	move.b	d2,(env_Settings+P1*4+1,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_ResM0 ***
;
;   NAME
;	A2600_ResM0 -- Reset the position of missile 0.
;
;   SYNOPSIS
;	A2600_ResM0(clock,env)
;	            d1    a1
;
;	VOID A2600_ResM0(ULONG,APTR);
;
;   FUNCTION
;	Sets the first missile's horizontal position to the emulated video
;	beam's current horizontal position plus four colour clocks. If this
;	function is called during the emulated horizontal blank period, the
;	missile's position will be set to the second colour clock of the
;	line.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

ResM0:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$12<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d3/a3,-(sp)

	; Store parameters

	move.l	d1,d3
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

;	mulu.w	#A2600_CLR_CLK_FACTOR,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update first missile's pattern list

	move.l	d0,d1
	moveq	#M0,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Store missile's new position

	divu.w	#A2600_LINE_CYCLES,d3

	swap.w	d3
	sub.w	#A2600_H_BLANK_CYCLES,d3
	bhs	not_in_hblank$

	moveq	#-2,d3

not_in_hblank$:

	addq.w	#4,d3
	cmpi.w	#TV_WIDTH,d3
	blo	skip_wrap_around$

	subi.w	#TV_WIDTH,d3

skip_wrap_around$:

	; Store first missile's new position

	move.b	d3,(env_Settings+M0*4+1,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d3/a3

	rts



;****** atari2600.library/A2600_ResM1 ***
;
;   NAME
;	A2600_ResM1 -- Reset the position of missile 1.
;
;   SYNOPSIS
;	A2600_ResM1(clock,env)
;	            d1    a1
;
;	VOID A2600_ResM1(ULONG,APTR);
;
;   FUNCTION
;	Sets the second missile's horizontal position to the emulated video
;	beam's current horizontal position plus four colour clocks. If this
;	function is called during the emulated horizontal blank period, the
;	missile's position will be set to the second colour clock of the
;	line.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

ResM1:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$13<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d3/a3,-(sp)

	; Store parameters

	move.l	d1,d3
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

;	mulu.w	#A2600_CLR_CLK_FACTOR,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update missile's pattern list

	move.l	d0,d1
	moveq	#M1,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Store missile's new position

	divu.w	#A2600_LINE_CYCLES,d3

	swap.w	d3
	sub.w	#A2600_H_BLANK_CYCLES,d3
	bhs	not_in_hblank$

	moveq	#-2,d3

not_in_hblank$:

	addq.w	#4,d3
	cmpi.w	#TV_WIDTH,d3
	blo	skip_wrap_around$

	subi.w	#TV_WIDTH,d3

skip_wrap_around$:

	; Store missile's new position

	move.b	d3,(env_Settings+M1*4+1,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d3/a3

	rts



;****** atari2600.library/A2600_ResBl ***
;
;   NAME
;	A2600_ResBl -- Reset the position of the ball.
;
;   SYNOPSIS
;	A2600_ResBl(clock,env)
;	            d1    a1
;
;	VOID A2600_ResBl(ULONG,APTR);
;
;   FUNCTION
;	Sets the ball's horizontal position to the emulated video beam's
;	current horizontal position plus four colour clocks. If this
;	function is called during the emulated horizontal blank period, the
;	ball's position will be set to the second colour clock of the line.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

ResBl:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$14<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d3/a3,-(sp)

	; Store parameters

	move.l	d1,d3
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

;	mulu.w	#A2600_CLR_CLK_FACTOR,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update ball's pattern list

	move.l	d0,d1
	moveq	#BL,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Calculate ball's new position

	divu.w	#A2600_LINE_CYCLES,d3

	swap.w	d3
	sub.w	#A2600_H_BLANK_CYCLES,d3
	bhs	not_in_hblank$

	moveq	#-2,d3

not_in_hblank$:

	addq.w	#4,d3
	cmpi.w	#TV_WIDTH,d3
	blo	skip_wrap_around$

	subi.w	#TV_WIDTH,d3

skip_wrap_around$:

	; Store ball's new position

	move.b	d3,(env_Settings+BL*4+1,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d3/a3

	rts



;****** atari2600.library/A2600_AudC0 ***
;
;   NAME
;	A2600_AudC0 -- Set the tone type for audio channel 0. (V2)
;
;   SYNOPSIS
;	A2600_AudC0(tone_num,clock,env)
;	            d0       d1    a1
;
;	VOID A2600_AudC0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the tone type for an Atari 2600 environment's first audio
;	channel.
;
;   INPUTS
;	tone_num - A four-bit number to select the tone type to use.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

AudC0:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2/a3/a5-a6,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3
	movea.l	a6,a5

	; Check if sound is on

	tst.w	(env_SoundOn,a1)
	beq.s	end$

	; Check if tone type is unchanged

	andi.b	#$f,d2
	cmp.b	(env_ToneTypeNo,a1),d2
	beq	end$
	move.b	d2,(env_ToneTypeNo,a1)

	; Ensure that current IORequest is not in use

	movea.l	(ab_SysLib,a5),a6
	movea.l	(env_ToneTypeIO,a3),a1
	JSRLIB	WaitIO

	; Start new sound

	movea.l	(env_ToneTypeIO,a3),a1
	move.l	(ab_Sounds,a5,d2*4),(ioa_Data,a1)
	move.l	(sound_length_table.l,d2*4),(ioa_Length,a1)
	JSRLIB	SendIO

	; Rotate IORequests for this channel

	movea.l	(env_ToneTypeIO+4,a3),a1
	move.l	(env_ToneTypeIO,a3),(env_ToneTypeIO+4,a3)
	movea.l	a1,(env_ToneTypeIO,a3)

	; Stop old sound

	JSRLIB	AbortIO

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2/a3/a5-a6
	rts



;****** atari2600.library/A2600_AudC1 ***
;
;   NAME
;	A2600_AudC1 -- Set the tone type for audio channel 1. (V2)
;
;   SYNOPSIS
;	A2600_AudC1(tone_num,clock,env)
;	            d0       d1    a1
;
;	VOID A2600_AudC1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the tone type for an Atari 2600 environment's second audio
;	channel.
;
;   INPUTS
;	tone_num - A four-bit number to select the tone type to use.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

AudC1:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2/a3/a5-a6,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3
	movea.l	a6,a5

	; Check if sound is on

	tst.w	(env_SoundOn,a1)
	beq.s	end$

	; Check if tone type is unchanged

	andi.b	#$f,d2
	cmp.b	(env_ToneTypeNo+1,a1),d2
	beq	end$
	move.b	d2,(env_ToneTypeNo+1,a1)

	; Ensure that current IORequest is not in use

	movea.l	(ab_SysLib,a5),a6
	movea.l	(env_ToneTypeIO+AUDIO_BUFFER_COUNT*4,a3),a1
	JSRLIB	WaitIO

	; Start new sound

	movea.l	(env_ToneTypeIO+AUDIO_BUFFER_COUNT*4,a3),a1
	move.l	(ab_Sounds,a5,d2*4),(ioa_Data,a1)
	move.l	(sound_length_table.l,d2*4),(ioa_Length,a1)
	JSRLIB	SendIO

	; Rotate IORequests for this channel

	movea.l	(env_ToneTypeIO+AUDIO_BUFFER_COUNT*4+4,a3),a1
	move.l	(env_ToneTypeIO+AUDIO_BUFFER_COUNT*4,a3),(env_ToneTypeIO+AUDIO_BUFFER_COUNT*4+4,a3)
	movea.l	a1,(env_ToneTypeIO+AUDIO_BUFFER_COUNT*4,a3)

	; Stop old sound

	JSRLIB	AbortIO

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2/a3/a5-a6
	rts



;****** atari2600.library/A2600_AudF0 ***
;
;   NAME
;	A2600_AudF0 -- Set the frequency for audio channel 0. (V2)
;
;   SYNOPSIS
;	A2600_AudF0(freq_num,clock,env)
;	            d0       d1    a1
;
;	VOID A2600_AudF0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the sound frequency for the first audio channel. The new
;	frequency is approximately 31KHz divided by one more than freq_num.
;
;   INPUTS
;	freq_num - One less than the divisor of the basic frequency. A five-
;	    bit number.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

AudF0:
;   rts
	; Push library pointer onto stack and load exec pointer

	move.l	a6,-(sp)
	movea.l	(ab_SysLib,a6),a6

	; Check if sound is on

	tst.w	(env_SoundOn,a1)
	beq.s	end$

	; Check if frequency is unchanged

	andi.b	#$1f,d0
	cmp.b	(env_FrequencyNo,a1),d0
	beq	end$
	move.b	d0,(env_FrequencyNo,a1)

	; Change frequency

	movea.l	(env_PerVolIO,a1),a1
	move.w	(period_table.l,d0*2),(ioa_Period,a1)
	JSRLIB	DoIO

end$:

	; Pop library pointer off stack and return

	movea.l	(sp)+,a6
	rts



;****** atari2600.library/A2600_AudF1 ***
;
;   NAME
;	A2600_AudF1 -- Set the frequency for audio channel 1. (V2)
;
;   SYNOPSIS
;	A2600_AudF1(freq_num,clock,env)
;	            d0       d1    a1
;
;	VOID A2600_AudF1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the sound frequency for the first audio channel. The new
;	frequency is approximately 31KHz divided by one more than freq_num.
;
;   INPUTS
;	freq_num - One less than the divisor of the basic frequency. A five-
;	    bit number.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

AudF1:
;   rts
	; Push library pointer onto stack and load exec pointer

	move.l	a6,-(sp)
	movea.l	(ab_SysLib,a6),a6

	; Check if sound is on

	tst.w	(env_SoundOn,a1)
	beq.s	end$

	; Check if frequency is unchanged

	andi.b	#$1f,d0
	cmp.b	(env_FrequencyNo+1,a1),d0
	beq	end$
	move.b	d0,(env_FrequencyNo+1,a1)

	; Change frequency

	movea.l	(env_PerVolIO+4,a1),a1
	move.w	(period_table.l,d0*2),(ioa_Period,a1)
	JSRLIB	DoIO

end$:

	; Pop library pointer off stack and return

	movea.l	(sp)+,a6
	rts



;****** atari2600.library/A2600_AudV0 ***
;
;   NAME
;	A2600_AudV0 -- Set the volume for audio channel 0. (V2)
;
;   SYNOPSIS
;	A2600_AudV0(volume,clock,env)
;	            d0     d1    a1
;
;	VOID A2600_AudV0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the volume for audio channel 0.
;
;   INPUTS
;	volume - A four-bit number to specify the new volume. 0 is quietest,
;	    15 is loudest.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

AudV0:
;   rts

	; Push library pointer onto stack and load exec pointer

	move.l	a6,-(sp)
	movea.l	(ab_SysLib,a6),a6

	; Check if sound is on

	tst.w	(env_SoundOn,a1)
	beq.s	end$

	; Check if sound volume is unchanged

	andi.b	#$f,d0
	cmp.b	(env_VolumeNo,a1),d0
	beq	end$
	move.b	d0,(env_VolumeNo,a1)

	; Change frequency

	movea.l	(env_PerVolIO,a1),a1
	lsl.b	#2,d0
	move.w	d0,(ioa_Volume,a1)
	JSRLIB	DoIO

end$:

	; Pop library pointer off stack and return

	movea.l	(sp)+,a6
	rts



;****** atari2600.library/A2600_AudV1 ***
;
;   NAME
;	A2600_AudV1 -- Set the volume for audio channel 1. (V2)
;
;   SYNOPSIS
;	A2600_AudV1(volume,clock,env)
;	            d0     d1    a1
;
;	VOID A2600_AudV1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the volume for audio channel 1.
;
;   INPUTS
;	volume - A four-bit number to specify the new volume. 0 is quietest,
;	    15 is loudest.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

AudV1:
;   rts

	; Push library pointer onto stack and load exec pointer

	move.l	a6,-(sp)
	movea.l	(ab_SysLib,a6),a6

	; Check if sound is on

	tst.w	(env_SoundOn,a1)
	beq.s	end$

	; Check if sound volume is unchanged

	andi.b	#$f,d0
	cmp.b	(env_VolumeNo+1,a1),d0
	beq	end$
	move.b	d0,(env_VolumeNo+1,a1)

	; Change frequency

	movea.l	(env_PerVolIO+4,a1),a1
	lsl.b	#2,d0
	move.w	d0,(ioa_Volume,a1)
	JSRLIB	DoIO

end$:

	; Pop library pointer off stack and return

	movea.l	(sp)+,a6
	rts



;****** atari2600.library/A2600_GrP0 ***
;
;   NAME
;	A2600_GrP0 -- Set the graphics pattern for player 0.
;
;   SYNOPSIS
;	A2600_GrP0(graphics,clock,env)
;	           d0       d1    a1
;
;	VOID A2600_GrP0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the first player's undelayed graphics pattern, and copies the
;	second player's undelayed graphics pattern to the same player's
;	delayed graphics pattern.
;
;   INPUTS
;	graphics - Lowest byte is the new graphics pattern for player 0.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

GrP0:

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	move.l	d2,-(sp)
	move.l	a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock plus delay

	move.l	d1,d0
	add.l	d1,d0
	add.l	d0,d1
	addq.l	#1,d1

	; Get update position

	bsr	CalculateCurrentPosition

	; Update first player's pattern list

	move.l	d0,d1
	moveq	#P0,d0
	bsr	UpdatePatternList

	; Update second player's pattern list

	moveq	#P1,d0
	bsr	UpdatePatternList

	; Store first player's new graphics

	move.b	d2,(env_Settings+P0*4+3,a3)

	; Copy second player's undelayed graphics to its delayed graphics

	move.b	(env_Settings+P1*4+3,a3),(env_Settings+P1*4+2,a3)

	; Pop preserved registers off stack and return

	move.l	(sp)+,a3
	move.l	(sp)+,d2

	rts



;****** atari2600.library/A2600_GrP1 ***
;
;   NAME
;	A2600_GrP1 -- Set the graphics pattern for player 1.
;
;   SYNOPSIS
;	A2600_GrP1(graphics,clock,env)
;	           d0       d1    a1
;
;	VOID A2600_GrP1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the second player's undelayed graphics pattern, and copies the
;	first player's undelayed graphics pattern to the same player's
;	delayed graphics pattern. In addition, the ball's delayed enable
;	flag is set to the same value as its undelayed enable flag.
;
;   INPUTS
;	graphics - Lowest byte is the new graphics pattern for player 1.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

GrP1:
	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	move.l	d2,-(sp)
	move.l	a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock plus delay

	move.l	d1,d0
	add.l	d1,d0
	add.l	d0,d1
	add.l	#1,d1

	; Get update position

	bsr	CalculateCurrentPosition
	move.l	d0,d1

	; Update ball's pattern list

	moveq	#BL,d0
	bsr	UpdatePatternList

	; Update first player's pattern list

	moveq	#P0,d0
	bsr	UpdatePatternList

	; Update second player's pattern list

	moveq	#P1,d0
	bsr	UpdatePatternList

	; Store second player's new graphics

	move.b	d2,(env_Settings+P1*4+3,a3)

	; Copy first player's undelayed graphics to its delayed graphics

	move.b	(env_Settings+P0*4+3,a3),(env_Settings+P0*4+2,a3)

	; Copy ball's undelayed enable flag to its delayed enable flag

	move.b	(env_Settings+BL*4+3,a3),(env_Settings+BL*4+2,a3)

	; Pop preserved registers off stack and return

	move.l	(sp)+,a3
	move.l	(sp)+,d2

	rts



;****** atari2600.library/A2600_EnaM0 ***
;
;   NAME
;	A2600_EnaM0 -- Enable or disable missile 0.
;
;   SYNOPSIS
;	A2600_EnaM0(mode,clock,env)
;	            d0   d1    a1
;
;	VOID A2600_EnaM0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Determines whether the first missile is displayed or not.
;
;   INPUTS
;	mode - If bit 1 is set then the missile is enabled. It is disabled
;	    otherwise.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

EnaM0:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$1d<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d1,d0
	lsl.l	#1,d0
	add.l	d0,d1

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update first missile's pattern list

	move.l	d0,d1
	moveq	#M0,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Enable or disable missile graphics

	lsr.l	#1,d2

	move.l	(env_Settings+M0*4,a3),d0
	bfins	d2,d0{31-OBJB_UNDELAYEDENABLE:1}
	move.l	d0,(env_Settings+M0*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2/a3

	rts



;****** atari2600.library/A2600_EnaM1 ***
;
;   NAME
;	A2600_EnaM1 -- Enable or disable missile 1.
;
;   SYNOPSIS
;	A2600_EnaM1(mode,clock,env)
;	            d0   d1    a1
;
;	VOID A2600_EnaM1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Determines whether the second missile is displayed or not.
;
;   INPUTS
;	mode - If bit 1 is set then the missile is enabled. It is disabled
;	    otherwise.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

EnaM1:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$1e<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d1,d0
	lsl.l	#1,d0
	add.l	d0,d1

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update first missile's pattern list

	move.l	d0,d1
	moveq	#M1,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Enable or disable missile graphics

	lsr.l	#1,d2

	move.l	(env_Settings+M1*4,a3),d0
	bfins	d2,d0{31-OBJB_UNDELAYEDENABLE:1}
	move.l	d0,(env_Settings+M1*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2/a3

	rts



;****** atari2600.library/A2600_EnaBl ***
;
;   NAME
;	A2600_EnaBl -- Enable or disable the ball.
;
;   SYNOPSIS
;	A2600_EnaBl(mode,clock,env)
;	            d0   d1    a1
;
;	VOID A2600_EnaBl(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Determines whether the ball is displayed or not.
;
;   INPUTS
;	mode - If bit 1 is set then the ball is enabled. It is disabled
;	    otherwise.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

EnaBl:
;    rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$1f<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d1,d0
	lsl.l	#1,d0
	add.l	d0,d1

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update ball's pattern list

	move.l	d0,d1
	moveq	#BL,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Enable or disable ball graphics

	lsr.l	#1,d2

	move.l	(env_Settings+BL*4,a3),d0
	bfins	d2,d0{31-OBJB_UNDELAYEDENABLE:1}
	move.l	d0,(env_Settings+BL*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2/a3

	rts



;****** atari2600.library/A2600_HMP0 ***
;
;   NAME
;	A2600_HMP0 -- Set the horizontal movement rate for player 0.
;
;   SYNOPSIS
;	A2600_HMP0(rate,clock,env)
;	           d0   d1    a1
;
;	VOID A2600_HMP0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the horizontal movement rate for the first player. The player's
;	horizontal position will only actually change when HMove() is
;	called.
;
;   INPUTS
;	rate - the upper nibble of the lowest byte is a two's-complement
;	    value that is subtracted from the the first player's horizontal
;	    position on the next call to HMove().
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

HMP0:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$20<<2,a1)
	ENDC

	; Store player's new horizontal movement rate

	lsr.b	#4,d0
	move.w	d0,(env_MotionRates+P0*2,a1)

	; Return

	rts



;****** atari2600.library/A2600_HMP1 ***
;
;   NAME
;	A2600_HMP1 -- Set the horizontal movement rate for player 1.
;
;   SYNOPSIS
;	A2600_HMP1(rate,clock,env)
;	           d0   d1    a1
;
;	VOID A2600_HMP1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the horizontal movement rate for the second player. The
;	player's horizontal position will only actually change when HMove()
;	is called.
;
;   INPUTS
;	rate - the upper nibble of the lowest byte is a two's-complement
;	    value that is subtracted from the the second player's horizontal
;	    position on the next call to HMove().
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

HMP1:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$21<<2,a1)
	ENDC

	; Store player's new horizontal movement rate

	lsr.b	#4,d0
	move.w	d0,(env_MotionRates+P1*2,a1)

	; Return

	rts



;****** atari2600.library/A2600_HMM0 ***
;
;   NAME
;	A2600_HMM0 -- Set the horizontal movement rate for missile 0.
;
;   SYNOPSIS
;	A2600_HMM0(rate,clock,env)
;	           d0   d1    a1
;
;	VOID A2600_HMM0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the horizontal movement rate for the first missile. The
;	missile's horizontal position will only actually change when HMove()
;	is called.
;
;   INPUTS
;	rate - the upper nibble of the lowest byte is a two's-complement
;	    value that is subtracted from the the first missile's horizontal
;	    position on the next call to HMove().
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

HMM0:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$22<<2,a1)
	ENDC

	; Store missile's new horizontal movement rate

	lsr.b	#4,d0
	move.w	d0,(env_MotionRates+M0*2,a1)

	; Return

	rts



;****** atari2600.library/A2600_HMM1 ***
;
;   NAME
;	A2600_HMM1 -- Set the horizontal movement rate for missile 1.
;
;   SYNOPSIS
;	A2600_HMM1(rate,clock,env)
;	           d0   d1    a1
;
;	VOID A2600_HMM1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the horizontal movement rate for the second missile. The
;	missile's horizontal position will only actually change when HMove()
;	is called.
;
;   INPUTS
;	rate - the upper nibble of the lowest byte is a two's-complement
;	    value that is subtracted from the the second missile's
;	    horizontal position on the next call to HMove().
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

HMM1:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$23<<2,a1)
	ENDC

	; Store missile's new horizontal movement rate

	lsr.b	#4,d0
	move.w	d0,(env_MotionRates+M1*2,a1)

	; Return

	rts



;****** atari2600.library/A2600_HMBl ***
;
;   NAME
;	A2600_HMBl -- Set the horizontal movement rate for the ball.
;
;   SYNOPSIS
;	A2600_HMBl(rate,clock,env)
;	           d0   d1    a1
;
;	VOID A2600_HMBl(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the horizontal movement rate for the ball. The ball's
;	horizontal position will only actually change when HMove() is
;	called.
;
;   INPUTS
;	rate - the upper nibble of the lowest byte is a two's-complement
;	    value that is subtracted from the the ball's horizontal
;	    position on the next call to HMove().
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

HMBl:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$24<<2,a1)
	ENDC

	; Store ball's new horizontal movement rate

	lsr.b	#4,d0
	move.w	d0,(env_MotionRates+BL*2,a1)

	; Return

	rts



;****** atari2600.library/A2600_VDelP0 ***
;
;   NAME
;	A2600_VDelP0 -- Set vertical delay mode for player 0.
;
;   SYNOPSIS
;	A2600_VDelP0(mode,clock,env)
;	             d0   d1    a1
;
;	VOID A2600_VDelP0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Determines whether the first player's delayed or undelayed graphics
;	pattern will be used.
;
;   INPUTS
;	mode - Bit 0 of this specifies the vertical delay mode: 0 for off, 1
;	    for on.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

VDelP0:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$25<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d1,d0
	lsl.l	#1,d0
	add.l	d0,d1

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update first player's pattern list

	move.l	d0,d1
	moveq	#P0,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Set first player's vertical delay mode

	move.l	(env_Settings+P0*4,a3),d0
	bfins	d2,d0{31-OBJB_VDELAY:1}
	move.l	d0,(env_Settings+P0*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2/a3

	rts



;****** atari2600.library/A2600_VDelP1 ***
;
;   NAME
;	A2600_VDelP1 -- Set vertical delay mode for player 1.
;
;   SYNOPSIS
;	A2600_VDelP1(mode,clock,env)
;	             d0   d1    a1
;
;	VOID A2600_VDelP1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Determines whether the second player's delayed or undelayed graphics
;	pattern will be used.
;
;   INPUTS
;	mode - Bit 0 of this specifies the vertical delay mode: 0 for off, 1
;	    for on.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

VDelP1:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$26<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d1,d0
	lsl.l	#1,d0
	add.l	d0,d1

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update second player's pattern list

	move.l	d0,d1
	moveq	#P1,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Set second player's vertical delay mode

	move.l	(env_Settings+P1*4,a3),d0
	bfins	d2,d0{31-OBJB_VDELAY:1}
	move.l	d0,(env_Settings+P1*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2/a3

	rts



;****** atari2600.library/A2600_VDelBl ***
;
;   NAME
;	A2600_VDelBl -- Set vertical delay mode for the ball.
;
;   SYNOPSIS
;	A2600_VDelBl(mode,clock,env)
;	             d0   d1    a1
;
;	VOID A2600_VDelBl(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Determines whether the ball's delayed or undelayed enable flag will
;	be used.
;
;   INPUTS
;	mode - Bit 0 of this specifies the vertical delay mode: 0 for off, 1
;	    for on.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

VDelBl:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$27<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Calculate current colour clock

	move.l	d1,d0
	lsl.l	#1,d0
	add.l	d0,d1

	; Get update position

	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update ball's pattern list

	move.l	d0,d1
	moveq	#BL,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Set ball's vertical delay mode

	move.l	(env_Settings+BL*4,a3),d0
	bfins	d2,d0{31-OBJB_VDELAY:1}
	move.l	d0,(env_Settings+BL*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2/a3

	rts



;****** atari2600.library/A2600_ResMP0 ***
;
;   NAME
;	A2600_ResMP0 -- Lock missile 0 to its player. (V2)
;
;   SYNOPSIS
;	A2600_ResMP0(mode,clock,env)
;	             d0   d1    a1
;
;	VOID A2600_ResMP0(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Either locks the first missile's horizontal position to the centre
;	of its player or unlocks it so that it can move freely. When the
;	missile is locked, its horizontal position is always the same as
;	that of its player.
;
;   INPUTS
;	mode - Bit 1 should be set to lock the missile, cleared to unlock
;	    it.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

ResMP0:
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$28<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	move.l	d1,d3
	movea.l	a1,a3

	; Load missile's existing settings

	move.l	(env_Settings+M0*4,a3),d2

	; See if missile is being locked or unlocked

	btst.l	#1,d0
	beq	unlock$

	; Check if missile is already locked

	btst.l	#OBJB_LOCKED,d2
	bne	end$

	; Record missile as being locked

	bset.l	#OBJB_LOCKED,d2

	; Get corresponding player's central position

	move.l	(env_Settings+P0*4,a3),d1
	bfextu	d1{OBJO_NUMBERSIZE:OBJW_NUMBERSIZE},d0

	move.b	(player_centre_table.l,d0),d0

	bfextu	d1{OBJO_POSITION:OBJW_POSITION},d1

	add.l	d0,d1
	cmpi.w	#TV_WIDTH,d1
	blo	skip_wrap_around$

	subi.w	#TV_WIDTH,d1

skip_wrap_around$:

	; Set missile's new position

	bfins	d1,d2{OBJO_POSITION:OBJW_POSITION}

	bra	skip_unlock$

unlock$:

	; Check if missile is already unlocked

	btst.l	#OBJB_LOCKED,d2
	beq	end$

	; Record missile as being unlocked

	bclr.l	#OBJB_LOCKED,d2

skip_unlock$:

	; Calculate current colour clock

	move.l	d3,d0
	add.l	d0,d0
	add.l	d0,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update missile's pattern list

	move.l	d0,d1
	moveq	#M0,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Store new settings for missile

	move.l	d2,(env_Settings+M0*4,a3)

	bra	end$

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_ResMP1 ***
;
;   NAME
;	A2600_ResMP1 -- Lock missile 1 to its player. (V2)
;
;   SYNOPSIS
;	A2600_ResMP1(mode,clock,env)
;	             d0   d1    a1
;
;	VOID A2600_ResMP1(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Either locks the second missile's horizontal position to the centre
;	of its player or unlocks it so that it can move freely. When the
;	missile is locked, its horizontal position is always the same as
;	that of its player.
;
;   INPUTS
;	mode - Bit 1 should be set to lock the missile, cleared to unlock
;	    it.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

ResMP1:
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$29<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a3,-(sp)

	; Store parameters

	move.l	d0,d2
	move.l	d1,d3
	movea.l	a1,a3

	; Load missile's existing settings

	move.l	(env_Settings+M1*4,a3),d2

	; See if missile is being locked or unlocked

	btst.l	#1,d0
	beq	unlock$

	; Check if missile is already locked

	btst.l	#OBJB_LOCKED,d2
	bne	end$

	; Record missile as being locked

	bset.l	#OBJB_LOCKED,d2

	; Get corresponding player's central position

	move.l	(env_Settings+P1*4,a3),d1
	bfextu	d1{OBJO_NUMBERSIZE:OBJW_NUMBERSIZE},d0

	move.b	(player_centre_table.l,d0),d0

	bfextu	d1{OBJO_POSITION:OBJW_POSITION},d1

	add.l	d0,d1
	cmpi.w	#TV_WIDTH,d1
	blo	skip_wrap_around$

	subi.w	#TV_WIDTH,d1

skip_wrap_around$:

	; Set missile's new position

	bfins	d1,d2{OBJO_POSITION:OBJW_POSITION}

	bra	skip_unlock$

unlock$:

	; Check if missile is already unlocked

	btst.l	#OBJB_LOCKED,d2
	beq	end$

	; Record missile as being unlocked

	bclr.l	#OBJB_LOCKED,d2

skip_unlock$:

	; Calculate current colour clock

	move.l	d3,d0
	add.l	d0,d0
	add.l	d0,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition

	; Update missile's pattern list

	move.l	d0,d1
	moveq	#M1,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Store new settings for missile

	move.l	d2,(env_Settings+M1*4,a3)

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a3

	rts



;****** atari2600.library/A2600_HMove ***
;
;   NAME
;	A2600_HMove -- Move all Atari 2600 objects horizontally.
;
;   SYNOPSIS
;	A2600_HMove(clock,env)
;	            d1    a1
;
;	VOID A2600_HMove(ULONG,APTR);
;
;   FUNCTION
;	Moves all objects according to their current horizontal movement
;	rates.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;   BUGS
;	Prior to V2, this function gave incorrect movement if not called
;	directly after WSync().
;
;***
;
;

HMove:
;   rts
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$2a<<2,a1)
	ENDC

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Push registers that should be preserved onto stack

	movem.l	d2-d4/a2-a4,-(sp)

	; Store parameters

	move.l	d1,d3
	movea.l	a1,a3

	; Get address of motion array appropriate to the current position

	divu.w	#A2600_LINE_CYCLES/3,d1
	swap.w	d1
	lsl.w	#4+1,d1
	lea.l	(horizontal_motion_table.l,d1),a2

	; Calculate current colour clock

	move.l	d3,d0
	lsl.l	#1,d0
	add.l	d0,d3

	; Get update position

	move.l	d3,d1
	movea.l	a3,a1
	bsr	CalculateCurrentPosition
	move.l	d0,d3

	; Initialise object loop

	moveq	#OBJECT_COUNT-1,d2
	lea.l	(env_MotionRates+OBJECT_COUNT*2,a3),a4

next_object$:

	; Get object's actual movement rate

	move.w	-(a4),d4
	move.w	(a2,d4*2),d4

	; See if object should move

	beq	skip_object$

	; Update object's pattern list

	move.l	d3,d1
	move.l	d2,d0
	movea.l	a3,a1
	bsr	UpdatePatternList

	; Adjust object's position by its motion value

	move.b	(env_Settings+1,a3,d2*4),d1
	andi.w	#$ff,d1

	sub.w	d4,d1

	bpl	check_upper_bound$
	add.w	#TV_WIDTH,d1
	bra	save_position$

check_upper_bound$:

	cmpi.w	#TV_WIDTH,d1
	blo	save_position$
	sub.w	#TV_WIDTH,d1

save_position$:

	; Store object's new position

	move.b	d1,(env_Settings+1,a3,d2*4)

skip_object$:

	dbra	d2,next_object$

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d4/a2-a4

	rts



;****** atari2600.library/A2600_HMClr ***
;
;   NAME
;	A2600_HMClr -- Reset Atari 2600 objects' horizontal movement rates.
;
;   SYNOPSIS
;	A2600_HMClr(clock,env)
;	            d1    a1
;
;	VOID A2600_HMClr(ULONG,APTR);
;
;   FUNCTION
;	Sets the movement rates of all objects to stationary.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

HMClr:
	IFD	PROFILE
	addq.l	#1,(env_ProfileData+$2b<<2,a1)
	ENDC

	; Reset all objects' horizontal movement rates to (officially)
	; stationary.

	lea.l	(env_MotionRates,a1),a0

	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+

	; Return

	rts



;****** atari2600.library/A2600_CxClr ***
;
;   NAME
;	A2600_CxClr -- Reset collisions between Atari 2600 objects. (V2)
;
;   SYNOPSIS
;	A2600_CxClr(clock,env)
;	            d1    a1
;
;	VOID A2600_CxClr(ULONG,APTR);
;
;   FUNCTION
;	Resets all emulated collision registers to a state of no collision.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

CxClr:
   rts
	; Reset collision registers to no-collision while preserving the
	; mysterious undocumented bits in those locations.

	movea.l	(env_Data,a1),a0
	moveq	#$70,d0

loop$:

	move.l	#$00010203,(a0,d0)
	move.l	#$04050600,(4,a0,d0)
	subi.l	#$10,d0
	bpl	loop$

	; Return

	rts



;****** atari2600.library/A2600_OpenAtari2600 ***
;
;   NAME
;	A2600_OpenAtari2600 -- Create a new Atari 2600 environment.
;
;   SYNOPSIS
;	env = A2600_OpenAtari2600(tag_items,orig_sp,mem_base)
;	d0                        a0        d0      a1
;
;	APTR A2600_OpenAtari2600(struct TagItem *,APTR,APTR);
;
;   FUNCTION
;	Opens a new Atari 2600 environment to emulate that machine's custom
;	hardware functions.
;
;   INPUTS
;	tag_items - Pointer to a taglist of parameters.
;	orig_sp - The program's starting stack pointer.
;	mem_base - Pointer to the program's Atari 2600 memory.
;
;   TAGS
;	A2600TAG_ProgramName (STRPTR) - The name of the program that will
;	    use the environment. The string is not copied and must be
;	    preserved for the lifetime of the environment.
;	A2600TAG_ExitFunction (FPTR) - Pointer to a function that will be
;	    called when the human user chooses to exit the program. This
;	    function must close the environment and this library, deallocate
;	    any of the program's own resources etc. and then set a DOS
;	    return code and return.
;	A2600TAG_WBMsg (struct WBStartup *) - The program's Workbench
;	    start up message. This is used to access the program icon's Tool
;	    Types. (V2)
;
;   RESULT
;	env - A new Atari 2600 environment.
;
;   SEE ALSO
;	A2600_CloseAtari2600()
;
;***
;
;

OpenAtari2600:

	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a6,-(sp)

	; Store parameters

	movea.l	a0,a2
	move.l	d0,d2
	movea.l	a1,a4

	movea.l	a6,a5

	; Allocate memory for a new environment structure

	move.l	(ab_SysLib,a5),a6
	move.l	#env_SIZEOF,d0
	move.l	#MEMF_CLEAR,d1
	JSRLIB	AllocMem
	movea.l	d0,a3
	tst.l	d0
	beq	clean_up$

	; Store program's name

	movea.l	(ab_UtilityLib,a5),a6

	move.l	#A2600TAG_ProgramName,d0
	move.l	#default_prog_name,d1
	movea.l	a2,a0
	JSRLIB	GetTagData

	move.l	d0,(env_ProgramName,a3)

	; Store original stack pointer for use when quitting

	move.l	d2,(env_OriginalStackPtr,a3)

	; Store address of Atari 2600 address space

	move.l	a4,(env_Data,a3)

	; Store address of client program's exit function

	move.l	#A2600TAG_ExitFunction,d0
	move.l	#0,d1
	movea.l	a2,a0
	JSRLIB	GetTagData

	move.l	d0,(env_ExitFunction,a3)
	beq	clean_up$

	; Store default values for tool type variables

	move.w	#1,(env_FrameSkipRatio,a3)
	move.w	#DEFAULT_START_LINE,(env_StartingLineNo,a3)
	move.w	#60,(env_IdealFrameRate,a3)
	move.w	#DEFAULT_TV_HEIGHT,(env_TVHeight,a3)
	move.w	#1,(env_SoundOn,a3)
	move.w	#JOYSTICK<<8+NOCONTROLLER,(env_ControllerTypes,a3)
	move.l	#INVALID_ID,(env_ScreenModeID,a3)

	; Get WB startup message

	move.l	#A2600TAG_WBMsg,d0
	move.l	#0,d1
	movea.l	a2,a0
	JSRLIB	GetTagData
	tst.l	d0
	bne	wb_msg$

	; Get lock for program's directory

	movea.l	(ab_DosLib,a5),a6
	JSRLIB	GetProgramDir
	move.l	d0,(env_HomeDirLock,a3)
	beq	no_tooltypes$

	; Get program's file name

	movea.l	(ab_SysLib,a5),a6
	move.l	#256,d2
	move.l	d2,d0
	moveq	#0,d1
	JSRLIB	AllocMem
	move.l	d0,(env_ProgramFileName,a3)
	beq	no_tooltypes$

	movea.l	(ab_DosLib,a5),a6
	move.l	d0,d1
	JSRLIB	GetProgramName
	tst.l	d0
	beq	no_tooltypes$

	movea.l	(env_ProgramFileName,a3),a2
	bra	skip_wb_msg$

wb_msg$:

	; Record a workbench start for the program

	move.w	#1,(env_WBStart,a3)

	; Get directory lock and program name from start-up message

	movea.l	d0,a0
	movea.l	(sm_ArgList,a0),a2
	move.l	(wa_Lock,a2),(env_HomeDirLock,a3)
	movea.l	(wa_Name,a2),a2
	move.l	a2,(env_ProgramFileName,a3)

skip_wb_msg$:

	; Get program's tool types

	movea.l	(ab_DosLib,a5),a6
	move.l	(env_HomeDirLock,a3),d1
	JSRLIB	CurrentDir
	move.l	d0,d2

	movea.l	(ab_IconLib,a5),a6
	movea.l	a2,a0
	JSRLIB	GetDiskObject
	move.l	d0,(env_DiskObject,a3)

	movea.l	(ab_DosLib,a5),a6
	move.l	d2,d1
	JSRLIB	CurrentDir

	move.l	(env_DiskObject,a3),d0
	beq	no_tooltypes$
	movea.l	(do_ToolTypes.w,d0.l),a2

	; Parse frame-skip tool type

	movea.l	(ab_IconLib,a5),a6

	movea.l	a2,a0
	movea.l	#frameskip_tt_name,a1
	JSRLIB	FindToolType
	tst.l	d0
	beq	no_frameskip_tt$

	movea.l	(ab_DosLib,a5),a6
	move.l	d0,d1
	subq.l	#4,sp
	move.l	sp,d2
	JSRLIB	StrToLong
	move.l	(sp)+,d0
	beq	no_frameskip_tt$
	bmi	no_frameskip_tt$
	move.w	d0,(env_FrameSkipRatio,a3)
	subq.w	#1,d0
	move.w	d0,(env_VSyncOffAttempts,a3)

no_frameskip_tt$:

	; Parse starting-line tool type

	movea.l	(ab_IconLib,a5),a6

	movea.l	a2,a0
	movea.l	#startingline_tt_name,a1
	JSRLIB	FindToolType
	tst.l	d0
	beq	no_startingline_tt$

	movea.l	(ab_DosLib,a5),a6
	move.l	d0,d1
	subq.l	#4,sp
	move.l	sp,d2
	JSRLIB	StrToLong
	move.l	(sp)+,d1
	bmi	no_startingline_tt$
	tst.l	d0
	bmi	no_startingline_tt$
	move.w	d1,(env_StartingLineNo,a3)

no_startingline_tt$:

	; Parse TV lines tool type

	movea.l	(ab_IconLib,a5),a6

	movea.l	a2,a0
	movea.l	#tvlines_tt_name,a1
	JSRLIB	FindToolType
	tst.l	d0
	beq	no_tvlines_tt$

	movea.l	(ab_DosLib,a5),a6
	move.l	d0,d1
	subq.l	#4,sp
	move.l	sp,d2
	JSRLIB	StrToLong
	move.l	(sp)+,d0
	bmi	no_tvlines_tt$
	beq	no_tvlines_tt$
	move.w	d0,(env_TVHeight,a3)

no_tvlines_tt$:

	; Parse frame-rate tool type

	movea.l	(ab_IconLib,a5),a6

	movea.l	a2,a0
	movea.l	#framerate_tt_name,a1
	JSRLIB	FindToolType
	tst.l	d0
	beq	no_framerate_tt$

	movea.l	(ab_DosLib,a5),a6
	move.l	d0,d1
	subq.l	#4,sp
	move.l	sp,d2
	JSRLIB	StrToLong
	move.l	(sp)+,d0
	beq	no_framerate_tt$
	bmi	no_framerate_tt$
	move.w	d0,(env_IdealFrameRate,a3)

no_framerate_tt$:

	; Parse no-sound tool type

	movea.l	(ab_IconLib,a5),a6

	movea.l	a2,a0
	movea.l	#nosound_tt_name,a1
	JSRLIB	FindToolType
	tst.l	d0
	beq	no_nosound_tt$

	clr.w	(env_SoundOn,a3)

no_nosound_tt$:

	; Parse left and right controller tool types

	movea.l	(ab_IconLib,a5),a6

	moveq	#2-1,d2

next_controller_tt$:

	movea.l	a2,a0
	movea.l	(controller_tt_name_table.l,d2*4),a1
	JSRLIB	FindToolType
	tst.l	d0
	beq	no_controller_tt$

	moveq	#CONTROLLER_COUNT-1,d3
	movea.l	d0,a4

next_controller_type$:

	movea.l	a4,a0
	movea.l	(controller_tt_value_table.l,d3*4),a1
	JSRLIB	MatchToolValue
	tst.l	d0
	beq.s	controller_not_matched$
	move.b	d3,(env_ControllerTypes,a3,d2)

controller_not_matched$:

	dbra	d3,next_controller_type$

no_controller_tt$:

	dbra	d2,next_controller_tt$

	; Parse screen-mode tool type

	movea.l	(ab_IconLib,a5),a6

	movea.l	a2,a0
	movea.l	#screenmode_tt_name,a1
	JSRLIB	FindToolType
	tst.l	d0
	beq	no_screenmode_tt$

	cmpi.w	#'0x',(d0.l)
	bne	decimal_id$
	move.l	d0,a0
	addq.l	#2,a0
	subq.l	#4,sp
	movea.l	sp,a1
	bsr	HexToULong
	move.l	(sp)+,d1
	tst.l	d0
	beq	no_screenmode_tt$
	move.l	d1,(env_ScreenModeID,a3)
	bra	skip_decimal_id$

decimal_id$:

	movea.l	(ab_DosLib,a5),a6
	move.l	d0,d1
	subq.l	#4,sp
	move.l	sp,d2
	JSRLIB	StrToLong
	move.l	(sp)+,d1
	bmi	no_screenmode_tt$
	tst.l	d0
	bmi	no_screenmode_tt$
	move.l	d1,(env_ScreenModeID,a3)

skip_decimal_id$:

no_screenmode_tt$:

no_tooltypes$:

	; Calculate TV height rounded-up to nearest multiple of four

	moveq	#0,d0
	move.w	(env_TVHeight,a3),d0
	subq.w	#1,d0
	lsr.w	#2,d0
	addq.w	#1,d0
	lsl.w	#2,d0
	move.l	d0,(env_RoundedTVHeight,a3)

	; Calculate the size of a screen pattern

	move.l	(env_RoundedTVHeight,a3),d0
	mulu.w	#BYTES_PER_LINE,d0
	move.l	d0,(env_PatternSize,a3)

	; Calculate the height of the intuition screen

	moveq	#MENU_BAR_HEIGHT,d0
	add.w	(env_TVHeight,a3),d0
	move.l	d0,(env_ScreenHeight,a3)

	; Check if a screen mode has already been specified in a tooltype

	cmpi.l	#INVALID_ID,(env_ScreenModeID,a3)
	bne	no_screen_req$

	; Make a copy of the screen mode requester tag list

	movea.l	(ab_UtilityLib,a5),a6
	lea	screen_req_tags,a0
	JSRLIB	CloneTagItems
	move.l	d0,(env_ScreenReqTags,a3)
	beq	clean_up$

	; Set minimum height for screen mode

	move.l	#ASLSM_MinHeight,d0
	move.l	(env_ScreenReqTags,a3),a0
	JSRLIB	FindTagItem
	move.l	(env_ScreenHeight,a3),(ti_Data.w,d0.l)

	; Set window title for screen mode requester

	move.l	#ASLSM_TitleText,d0
	move.l	(env_ScreenReqTags,a3),a0
	JSRLIB	FindTagItem
	move.l	(env_ProgramName,a3),(ti_Data.w,d0.l)

	; Allocate screen mode requester

	movea.l	(ab_AslLib,a5),a6
	moveq	#ASL_ScreenModeRequest,d0
	move.l	(env_ScreenReqTags,a3),a0
	JSRLIB	AllocAslRequest
	move.l	d0,(env_ScreenReq,a3)
	beq	clean_up$

	; Ask the user for a screen mode

	move.l	(env_ScreenReq,a3),a2
	movea.l	a2,a0
	suba.l	a1,a1
	JSRLIB	AslRequest
	tst.l	d0
	beq	clean_up$

	; Store screen mode

	move.l	(sm_DisplayID,a2),(env_ScreenModeID,a3)

	; Check if the icon file has been read

	tst.l	(env_DiskObject,a3)
	beq	no_screen_req$

	; Allocate memory for a screen mode tooltype string

	movea.l	(ab_SysLib,a5),a6
	moveq	#SCREENMODE_STR_SIZE+11+1,d0
	moveq	#0,d1
	JSRLIB	AllocMem
	move.l	d0,(env_ScreenModeString,a3)
	beq	clean_up$

	; Create screen mode tooltype string

	movea.l	d0,a2
	movea.l	#screenmode_tt_name,a0
	movea.l	d0,a1
	moveq	#SCREENMODE_STR_SIZE,d0
	JSRLIB	CopyMem

	movea.l	a2,a0
	adda.l	#SCREENMODE_STR_SIZE,a0
	move.b	#'=',(a0)+
	move.w	#'0x',(a0)+
	move.l	(env_ScreenModeID,a3),d0
	bsr	ULongToHex

	; Count tooltypes plus two

	movea.l	([env_DiskObject,a3],do_ToolTypes),a4
	movea.l	a4,a2
	moveq	#1,d2

count_tooltypes$:

	addq.l	#1,d2
	move.l	(a2)+,d0
	beq	count_tooltypes_end$
	movea.l	a5,a6
	movea.l	d0,a0
	movea.l	#screenmode_tt_name,a1
	bsr	Substring
	tst.l	d0
	bne	no_screen_req$
	bra	count_tooltypes$

count_tooltypes_end$:

	; Create new tooltype array

	movea.l	(ab_SysLib,a5),a6
	move.l	d2,d0
	lsl.l	#2,d0
	moveq	#0,d1
	JSRLIB	AllocMem
	move.l	d0,a2
	tst.l	d0
	beq	clean_up$

	movea.l	a4,a0
	movea.l	a2,a1
	move.l	d2,d1
	subq.l	#2,d1
	bra	copy_tooltypes_start$

copy_tooltypes$:

	movea.l	(a0)+,(a1)+

copy_tooltypes_start$:

	dbra	d1,copy_tooltypes$

	; Add screen mode string onto end of array and terminate it with a
	; null pointer.

	move.l	(env_ScreenModeString,a3),(a1)+
	clr.l	(a1)

	; Write icon file to disk

	movea.l	(ab_DosLib,a5),a6
	move.l	(env_HomeDirLock,a3),d1
	JSRLIB	CurrentDir
	move.l	d0,d3

	movea.l	(ab_IconLib,a5),a6
	movea.l	(env_DiskObject,a3),a1
	move.l	a2,(do_ToolTypes,a1)
	movea.l	(env_ProgramFileName,a3),a0
	JSRLIB	PutDiskObject

	movea.l	(ab_DosLib,a5),a6
	move.l	d3,d1
	JSRLIB	CurrentDir

	; Restore original tooltype array

	movea.l	(env_DiskObject,a3),a1
	move.l	a4,(do_ToolTypes,a1)

	; Free memory for new tooltype array

	movea.l	(ab_SysLib,a5),a6
	move.l	d2,d0
	lsl.l	#2,d0
	movea.l	a2,a1
	JSRLIB	FreeMem

no_screen_req$:

	; Check if sound should be used

	tst.w	(env_SoundOn,a3)
	beq	no_sound$

	; Allocate sound

	clr.w	(env_SoundOn,a3)

	movea.l	a5,a6
	movea.l	a3,a1
	moveq	#1,d0
	bsr	AllocateSound

	tst.l	d0
	beq	no_sound$

	move.w	#1,(env_SoundOn,a3)

no_sound$:

	; Create a series of bitmaps to contain the display

	movea.l	(ab_GraphicsLib,a5),a6
	moveq	#SCREEN_BUFFER_COUNT-1,d4

bitmap_loop$:

	move.l	#TV_WIDTH*X_MAG,d0
	move.l	(env_ScreenHeight,a3),d1
	move.l	#SCREEN_DEPTH,d2
	move.l	#(BMF_CLEAR|BMF_DISPLAYABLE),d3
	move.l	#0,a0
	JSRLIB	AllocBitMap
	move.l	d0,(env_ScreenBitMaps,a3,d4*4)
	beq	clean_up$

	dbra	d4,bitmap_loop$

	; Make a copy of the screen tag list

	movea.l	(ab_UtilityLib,a5),a6
	lea	screen_tags,a0
	JSRLIB	CloneTagItems
	move.l	d0,(env_ScreenTags,a3)
	beq	clean_up$

	; Set screen height

	move.l	#SA_Height,d0
	move.l	(env_ScreenTags,a3),a0
	JSRLIB	FindTagItem

	movea.l	d0,a0
	move.l	(env_ScreenHeight,a3),(ti_Data,a0)

	; Set screen bitmap

	move.l	#SA_BitMap,d0
	move.l	(env_ScreenTags,a3),a0
	JSRLIB	FindTagItem

	movea.l	d0,a0
	move.l	(env_ScreenBitMaps,a3),(ti_Data,a0)

	; Set screen's screen mode

	movea.l	(ab_UtilityLib,a5),a6
	move.l	#SA_DisplayID,d0
	move.l	(env_ScreenTags,a3),a0
	JSRLIB	FindTagItem

	movea.l	d0,a0
	move.l	(env_ScreenModeID,a3),(ti_Data,a0)

	; Set screen title

	move.l	#SA_Title,d0
	move.l	(env_ScreenTags,a3),a0
	JSRLIB	FindTagItem

	movea.l	d0,a0
	move.l	(env_ProgramName,a3),(ti_Data,a0)

	; Open the screen

	movea.l	(ab_IntuitionLib,a5),a6
	movea.l	#0,a0
	movea.l	(env_ScreenTags,a3),a1
	JSRLIB	OpenScreenTagList
	move.l	d0,(env_Screen,a3)
	beq	clean_up$

	; Make a copy of the window tag list

	movea.l	(ab_UtilityLib,a5),a6
	lea	window_tags,a0
	JSRLIB	CloneTagItems
	move.l	d0,(env_WindowTags,a3)
	beq	clean_up$

	; Set window's height

	move.l	#WA_Height,d0
	move.l	(env_WindowTags,a3),a0
	JSRLIB	FindTagItem

	movea.l	d0,a0
	moveq	#0,d0
	move.w	(env_TVHeight,a3),d0
	move.l	d0,(ti_Data,a0)

	; Set window's screen

	move.l	#WA_CustomScreen,d0
	move.l	(env_WindowTags,a3),a0
	JSRLIB	FindTagItem

	movea.l	d0,a0
	move.l	(env_Screen,a3),(ti_Data,a0)

	; Open main window

	movea.l	ab_IntuitionLib(a5),a6
	movea.l	#0,a0
	movea.l	(env_WindowTags,a3),a1
	JSRLIB	OpenWindowTagList
	move.l	d0,(env_Window,a3)
	beq	clean_up$

	; Get window's UserPort

	movea.l	env_Window(a3),a0
	move.l	wd_UserPort(a0),env_UserPort(a3)

	; Create menus structure

	movea.l	ab_GadtoolsLib(a5),a6
	lea	new_menus,a0
	movea.l	#0,a1
	JSRLIB	CreateMenusA
	move.l	d0,env_Menus(a3)
	beq	clean_up$

	; Get screen info needed for displaying menus

	movea.l	ab_GadtoolsLib(a5),a6
	movea.l	env_Screen(a3),a0
	movea.l	#0,a1
	JSRLIB	GetVisualInfoA
	move.l	d0,env_VisualInfo(a3)
	beq	clean_up$

	; Prepare menus for display

	movea.l	ab_GadtoolsLib(a5),a6
	movea.l	env_Menus(a3),a0
	movea.l	env_VisualInfo(a3),a1
	lea.l	menu_layout_tags,a2
	JSRLIB	LayoutMenusA
	tst.l	d0
	beq	clean_up$

	; Display menus

	movea.l	ab_IntuitionLib(a5),a6
	movea.l	env_Window(a3),a0
	movea.l	env_Menus(a3),a1
	JSRLIB	SetMenuStrip

	; Create a series of screen buffer structures for multi-buffering

	movea.l	(ab_IntuitionLib,a5),a6

	movea.l	(env_Screen,a3),a0
	movea.l	(env_ScreenBitMaps+0*4,a3),a1
	moveq	#0,d0
	JSRLIB	AllocScreenBuffer
	move.l	d0,(env_ScreenBuffers+0*4,a3)
	beq	clean_up$

	movea.l	(env_Screen,a3),a0
	movea.l	(env_ScreenBitMaps+1*4,a3),a1
	moveq	#SB_COPY_BITMAP,d0
	JSRLIB	AllocScreenBuffer
	move.l	d0,(env_ScreenBuffers+1*4,a3)
	beq	clean_up$

	movea.l	(env_Screen,a3),a0
	movea.l	(env_ScreenBitMaps+2*4,a3),a1
	moveq	#SB_COPY_BITMAP,d0
	JSRLIB	AllocScreenBuffer
	move.l	d0,(env_ScreenBuffers+2*4,a3)
	beq	clean_up$

	; Create message ports for the library to use in receiving
	; information that ensures a smooth display.

	movea.l	ab_SysLib(a5),a6

	JSRLIB	CreateMsgPort
	move.l	d0,env_SafeMsgPort(a3)
	beq	clean_up$

	JSRLIB	CreateMsgPort
	move.l	d0,env_DispMsgPort(a3)
	beq	clean_up$

	; Put message ports into multi-buffering structures

	moveq	#SCREEN_BUFFER_COUNT-1,d2

buffer_ports_loop$:

	movea.l	([env_ScreenBuffers,a3,d2*4],sb_DBufInfo),a0
	move.l	(env_SafeMsgPort,a3),(dbi_SafeMessage+MN_REPLYPORT,a0)
	move.l	(env_DispMsgPort,a3),(dbi_DispMessage+MN_REPLYPORT,a0)

	dbra	d2,buffer_ports_loop$

	; Generate initial screen-buffering messages

	movea.l	(ab_SysLib,a5),a6

	movea.l	([env_ScreenBuffers,a3],sb_DBufInfo),a0
	lea.l	(dbi_SafeMessage,a0),a1
	JSRLIB	ReplyMsg

	movea.l	([env_ScreenBuffers,a3],sb_DBufInfo),a0
	lea.l	(dbi_DispMessage,a0),a1
	JSRLIB	ReplyMsg

	movea.l	([env_ScreenBuffers,a3],sb_DBufInfo),a0
	lea.l	(dbi_DispMessage,a0),a1
	JSRLIB	ReplyMsg

	movea.l	([env_ScreenBuffers,a3],sb_DBufInfo),a0
	lea.l	(dbi_DispMessage,a0),a1
	JSRLIB	ReplyMsg

	; Allocate memory for all-pattern difference pattern

	moveq	#0,d0
	move.w	(env_TVHeight,a3),d0
	move.l	#MEMF_CLEAR,d1
	JSRLIB	AllocMem
	move.l	d0,(env_DifferencePattern,a3)
	beq	clean_up$

	; Allocate memory for all-object difference pattern

	moveq	#0,d0
	move.w	(env_TVHeight,a3),d0
	move.l	#MEMF_CLEAR,d1
	JSRLIB	AllocMem
	move.l	d0,(env_ObjectDifferencePattern,a3)
	beq	clean_up$

	; Allocate memory for pattern lists and initialise them

	moveq	#PATTERN_COUNT-1,d3

pattern_list_loop$:

	lea.l	(env_PatternLists,a3),a2
	moveq	#(SCREEN_BUFFER_COUNT+1)*4,d0
	mulu.w	d3,d0
	adda.l	d0,a2

	moveq	#SCREEN_BUFFER_COUNT,d2

inner_pattern_list_loop$:

	move.l	#LIST_SIZE,d0
	moveq	#0,d1
	JSRLIB	AllocMem
	move.l	d0,(a2,d2*4)
	beq	clean_up$

	movea.l	d0,a1
	move.l	#OBJF_VBLANK,(a1)+
	move.l	(env_TVHeight,a3),(a1)

	dbra	d2,inner_pattern_list_loop$

	move.l	(a2),d0
	move.l	d0,(env_PatternListPtrs,a3,d3*4)
	add.l	#LIST_SIZE-2*8,d0
	move.l	d0,(env_PatternListLimits,a3,d3*4)

	dbra	d3,pattern_list_loop$

	; Allocate memory for patterns

	moveq	#PATTERN_COUNT-1,d2

pattern_loop$:

	move.l	(env_PatternSize,a3),d0
	move.l	#MEMF_CLEAR,d1
	JSRLIB	AllocMem
	move.l	d0,(env_Patterns,a3,d2*4)
	beq	clean_up$

	dbra	d2,pattern_loop$

	; Allocate memory for merged patterns

	moveq	#REDUCED_PATTERN_COUNT*(SCREEN_BUFFER_COUNT+1)-1,d2

merged_pattern_loop$:

	move.l	(env_PatternSize,a3),d0
	move.l	#MEMF_CLEAR,d1
	JSRLIB	AllocMem
	move.l	d0,(env_MergedPatterns,a3,d2*4)
	beq	clean_up$

	dbra	d2,merged_pattern_loop$

	; Allocate memory for difference patterns

	moveq	#REDUCED_PATTERN_COUNT-1,d2

difference_patterns_loop$:

	move.l	(env_RoundedTVHeight,a3),d0
	move.l	#MEMF_CLEAR,d1
	JSRLIB	AllocMem
	move.l	d0,(env_DifferencePatterns,a3,d2*4)
	beq	clean_up$

	dbra	d2,difference_patterns_loop$

	; Allocate memory for summary patterns

	moveq	#OBJECT_COUNT-1,d2

summary_loop$:

	move.l	(env_RoundedTVHeight,a3),d0
	move.l	#MEMF_CLEAR,d1
	JSRLIB	AllocMem
	move.l	d0,(env_SummaryPatterns,a3,d2*4)
	beq	clean_up$

	dbra	d2,summary_loop$

	; Allocate memory for pattern line

	move.l	#BYTES_PER_LINE+4*4,d0
	moveq	#0,d1
	JSRLIB	AllocMem
	move.l	d0,(env_PatternLine,a3)
	beq	clean_up$

	; Allocate memory for the "About" requester structure

	move.l	#EasyStruct_SIZEOF,d0
	moveq	#0,d1
	JSRLIB	AllocMem
	move.l	d0,(env_AboutRequester,a3)
	beq	clean_up$

	; Fill in "About" requester structure

	movea.l	(env_AboutRequester,a3),a0
	move.l	#EasyStruct_SIZEOF,(es_StructSize,a0)
	move.l	(env_ProgramName,a3),(es_Title,a0)
	move.l	#about_req_body_text,(es_TextFormat,a0)
	move.l	#about_req_button_text,(es_GadgetFormat,a0)

	; Set offset of part of bit planes that should be written to

	move.w	#MENU_BAR_HEIGHT*TV_WIDTH*2/8,env_SkipMenuOffset(a3)

	; Set emulated system variables to default values

	move.b	#%11001011,([env_Data,a3],A2600_SWCHB)	; pro, colour, no select or reset
	move.b	#%11001011,([env_Data,a3],A2600_SWCHB+$100)
	move.b	#%11111111,([env_Data,a3],A2600_SWCHA)	; no joystick activity
	move.b	#%11111111,([env_Data,a3],A2600_SWCHA+$100)

	move.w	#1,(env_MouseX,a3)
;	move.b	#$80,([env_Data,a3],$285)

	; Initialise read registers

	movea.l	(env_Data,a3),a0
	moveq	#8-1,d0

read_register_loop$:

	move.l	#$00010203,(a0)+
	move.l	#$04050600,(a0)+
	move.l	#$7f7f7f7f,(a0)+
	move.l	#$ffff0e0f,(a0)+
	dbra	d0,read_register_loop$

	; Get and store current time in milliseconds

	movea.l	a5,a6
	bsr	GetTime
	move.l	d0,(env_MillisecondCount,a3)

	; Initialise plane pointers

	movea.l	a3,a1
	bsr	GetHiddenBitPlanes
	move.l	d0,(env_PlanePtrs,a3)

	; Set environment pointer as return value

	movea.l	a3,d0
	bra	skip_clean_up$

clean_up$:

	; Deallocate environment resources and set return value to null

	movea.l	a5,a6
	movea.l	a3,a1
	bsr	CloseAtari2600
	moveq	#0,d0

skip_clean_up$:

	; Return new environment or null

	movem.l	(sp)+,d2-d7/a2-a6
	rts



;****** atari2600.library/A2600_CloseAtari2600 ***
;
;   NAME
;	A2600_CloseAtari2600 -- Close an Atari 2600 environment.
;
;   SYNOPSIS
;	A2600_CloseAtari2600(env)
;	                     a1
;
;	VOID A2600_CloseAtari2600(APTR);
;
;   FUNCTION
;	Closes an Atari 2600 environment and all of its resources.
;
;   INPUTS
;	env - The Atari 2600 environment to be closed. May be NULL.
;
;   RESULT
;	None.
;
;   SEE ALSO
;	A2600_OpenAtari2600()
;
;***
;
;

CloseAtari2600:

;FIX LEAK AT BOTTOM OF THIS FUNCTION!
	IFD	PROFILE
	lea.l	(env_ProfileData,a1),a0
	move.l	a0,8
	ENDC

	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a6,-(sp)

	; Move library and environment pointers to safe registers

	movea.l	a6,a5
	movea.l	a1,a3

	; See if environment is null

	cmpa.l	#0,a3
	beq	end$

	; Free memory for the "About" requester structure

	movea.l	(ab_SysLib,a5),a6

	move.l	#EasyStruct_SIZEOF,d0
	move.l	(env_AboutRequester,a3),d1
	beq	skip_about$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_about$:

	; Free memory for all-pattern difference pattern

	moveq	#0,d0
	move.w	(env_TVHeight,a3),d0
	movea.l	(env_DifferencePattern,a3),d1
	beq	skip_diff$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_diff$:

	; Free memory for all-object difference pattern

	moveq	#0,d0
	move.w	(env_TVHeight,a3),d0
	movea.l	(env_ObjectDifferencePattern,a3),d1
	beq	skip_object_diff$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_object_diff$:

	; Free memory for patterns

	moveq	#PATTERN_COUNT-1,d2

pattern_loop$:

	move.l	(env_PatternSize,a3),d0
	move.l	(env_Patterns,a3,d2*4),d1
	beq	skip_pattern$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_pattern$:

	dbra	d2,pattern_loop$

	; Free memory for merged patterns

	moveq	#REDUCED_PATTERN_COUNT*(SCREEN_BUFFER_COUNT+1)-1,d2

merged_pattern_loop$:

	move.l	(env_PatternSize,a3),d0
	move.l	(env_MergedPatterns,a3,d2*4),d1
	beq	skip_merged_pattern$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_merged_pattern$:

	dbra	d2,merged_pattern_loop$

	; Free memory for pattern lists

	moveq	#PATTERN_COUNT-1,d3

pattern_list_loop$:

	lea.l	(env_PatternLists,a3),a2
	moveq	#(SCREEN_BUFFER_COUNT+1)*4,d0
	mulu.w	d3,d0
	adda.l	d0,a2

	moveq	#SCREEN_BUFFER_COUNT,d2

inner_pattern_list_loop$:

	move.l	#LIST_SIZE,d0
	move.l	#MEMF_CLEAR,d1
	move.l	(a2,d2*4),d1
	beq	skip_pattern_list$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_pattern_list$:

	dbra	d2,inner_pattern_list_loop$

	dbra	d3,pattern_list_loop$

	; Free memory for difference patterns

	moveq	#REDUCED_PATTERN_COUNT-1,d2

difference_patterns_loop$:

	move.l	(env_RoundedTVHeight,a3),d0
	movea.l	(env_DifferencePatterns,a3,d2*4),d1
	beq	skip_difference_pattern$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_difference_pattern$:

	dbra	d2,difference_patterns_loop$

	; Free memory for summary patterns

	moveq	#OBJECT_COUNT-1,d2

summary_loop$:

	move.l	(env_RoundedTVHeight,a3),d0
	movea.l	(env_SummaryPatterns,a3,d2*4),d1
	beq	skip_summary_pattern$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_summary_pattern$:

	dbra	d2,summary_loop$

	; Free memory for pattern line

	move.l	#BYTES_PER_LINE+4*4,d0
	movea.l	(env_PatternLine,a3),d1
	beq	skip_pattern_line$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_pattern_line$:

	; See if window is open

	tst.l	(env_Window,a3)
	beq	no_window$

	; Remove menus

	movea.l	ab_IntuitionLib(a5),a6
	movea.l	env_Window(a3),a0
	JSRLIB	ClearMenuStrip

	; Free visual info structure

	movea.l	ab_GadtoolsLib(a5),a6
	movea.l	env_VisualInfo(a3),a0
	JSRLIB	FreeVisualInfo

	; Free menus

	movea.l	ab_GadtoolsLib(a5),a6
	movea.l	env_Menus(a3),a0
	JSRLIB	FreeMenus

	; Close main window

	move.l	ab_IntuitionLib(a5),a6
	movea.l	env_Window(a3),a0
	JSRLIB	CloseWindow

no_window$:

	; Free copy of window tag list

	move.l	(ab_UtilityLib,a5),a6
	move.l	(env_WindowTags,a3),a0
	JSRLIB	FreeTagItems

	; See if the screen is open

	tst.l	(env_Screen,a3)
	beq	no_screen$

	; Free screen buffers as soon as they are no longer in use

	move.l	(ab_GraphicsLib,a5),a6
	JSRLIB	WaitBlit

	move.l	(ab_IntuitionLib,a5),a6
	moveq	#SCREEN_BUFFER_COUNT-1,d2

buffer_loop$:

	movea.l	env_Screen(a3),a0
	movea.l	(env_ScreenBuffers,a3,d2*4),a1
	JSRLIB	FreeScreenBuffer

	dbra	d2,buffer_loop$

	; Close the screen

	move.l	ab_IntuitionLib(a5),a6

try_again$:

	movea.l	env_Screen(a3),a0
	JSRLIB	CloseScreen

	; Keep trying to close until successful

	cmp.l	#0,d0
	beq	try_again$

no_screen$:

	; Free copy of screen tag list

	move.l	(ab_UtilityLib,a5),a6
	move.l	(env_ScreenTags,a3),a0
	JSRLIB	FreeTagItems

	; Free bitmaps as soon as they are no longer in use

	move.l	(ab_GraphicsLib,a5),a6
	JSRLIB	WaitBlit
	moveq	#SCREEN_BUFFER_COUNT-1,d2

bitmap_loop$:

	movea.l	(env_ScreenBitMaps,a3,d2*4),a0
	JSRLIB	FreeBitMap

	dbra	d2,bitmap_loop$

	; Free double buffering message ports

	movea.l	ab_SysLib(a5),a6
	movea.l	env_SafeMsgPort(a3),a0
	JSRLIB	DeleteMsgPort
	movea.l	env_DispMsgPort(a3),a0
	JSRLIB	DeleteMsgPort

	; Deallocate sound

	movea.l	a5,a6
	movea.l	a3,a1
	moveq	#0,d0
	bsr	AllocateSound

	; Free memory for screen mode tooltype string

	movea.l	(ab_SysLib,a5),a6
	moveq	#SCREENMODE_STR_SIZE+11+1,d0
	move.l	(env_ScreenModeString,a3),d1
	beq	skip_screenmode_string$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_screenmode_string$:

	; Free disk object

	movea.l	(ab_IconLib,a5),a6
	move.l	(env_DiskObject,a3),d0
	beq	skip_disk_object$
	movea.l	d0,a0
	JSRLIB	FreeDiskObject

skip_disk_object$:

	; Free screen mode requester

	movea.l	(ab_AslLib,a5),a6
	movea.l	(env_ScreenReq,a3),a0
	JSRLIB	FreeAslRequest

	; Free copy of screen mode requester tag list

	move.l	(ab_UtilityLib,a5),a6
	move.l	(env_ScreenReqTags,a3),a0
	JSRLIB	FreeTagItems

	; Free memory for program file name if not started from Workbench

	movea.l	(ab_SysLib,a5),a6
	tst.w	(env_WBStart,a3)
	bne	skip_prog_name$

	move.l	#256,d0
	move.l	(env_ProgramFileName,a3),d1
	beq	skip_prog_name$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_prog_name$:

	; Free environment memory

	IFND	PROFILE
	movea.l	a3,a1
	move.l	#env_SIZEOF,d0
	JSRLIB	FreeMem
	ENDC

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a6
	rts



;****** atari2600.library/A2600_InstallROM ***
;
;   NAME
;	A2600_InstallROM -- Copy a ROM image into Atari 2600 address space.
;
;   SYNOPSIS
;	A2600_InstallROM(source,dest,size)
;	                 a0     a1   d0
;
;	VOID A2600_InstallROM(APTR,APTR,ULONG);
;
;   FUNCTION
;	Just copies a block of data from one longword-aligned location to
;	another.
;
;   INPUTS
;	source - Pointer to the source ROM image.
;	dest - The destination address.
;	size - The ROM image's size in bytes.
;
;   RESULT
;	None.
;
;***
;
;

InstallROM:

	; Move ROM image into Atari 2600's address space

	move.l	a5,-(sp)
	movea.l	a6,a5

	movea.l	ab_SysLib(a5),a6
	JSRLIB	CopyMemQuick

	movea.l	a5,a6
	movea.l	(sp)+,a5

	; Return

	rts



;****** atari2600.library/A2600_InTim ***
;
;   NAME
;	A2600_InTim -- See if the timer has timed out.
;
;   SYNOPSIS
;	result = A2600_InTim(clock,env)
;	d0                   d1    a1
;
;	ULONG A2600_InTim(ULONG,APTR);
;
;   FUNCTION
;	Checks if the timer has timed out.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	result - The number of emulated clock cycles since the timer timed
;	    out.
;
;***
;
;

InTim:

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Check if this timer run has timed out.

	move.l	(env_Timer,a1),d0
	sub.l	d1,d0
	blo.s	timed_out$

	; Divide time difference by size of timer interval.

	move.w	(env_TimerShift,a1),d1
	lsr.l	d1,d0

	bra	end$

timed_out$:

	; Return the number of processor clocks since the time-out occurred.
	; This value is modulo 256 since only a byte is returned.

	andi.l	#$ff,d0

end$:

	; Return

	rts



;****** atari2600.library/A2600_Tim1T ***
;
;   NAME
;	A2600_Tim1T -- Set timer in units of 1 clock cycle.
;
;   SYNOPSIS
;	A2600_Tim1T(units,clock,env)
;	            d0    d1    a1
;
;	VOID A2600_Tim1T(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the Riot chip's timer in units of 1 clock cycle.
;
;   INPUTS
;	units - The number of 1 clock cycle units.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

Tim1T:

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Record the clock value for time-out

	add.l	d0,d1
	move.l	d1,(env_Timer,a1)

	; Record the shift value for this function's interval

	move.w	#0,(env_TimerShift,a1)

	; Return

	rts



;****** atari2600.library/A2600_Tim8T ***
;
;   NAME
;	A2600_Tim8T -- Set timer in units of 8 clock cycles.
;
;   SYNOPSIS
;	A2600_Tim8T(units,clock,env)
;	            d0    d1    a1
;
;	VOID A2600_Tim8T(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the Riot chip's timer in units of 8 clock cycles.
;
;   INPUTS
;	units - The number of 8 clock cycle units.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

Tim8T:

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Record the clock value for time-out

	lsl.l	#3,d0
	add.l	d0,d1
	move.l	d1,(env_Timer,a1)

	; Record the shift value for this function's interval

	move.w	#3,(env_TimerShift,a1)

	; Return

	rts



;****** atari2600.library/A2600_Tim64T ***
;
;   NAME
;	A2600_Tim64T -- Set timer in units of 64 clock cycles.
;
;   SYNOPSIS
;	A2600_Tim64T(units,clock,env)
;	             d0    d1    a1
;
;	VOID A2600_Tim64T(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the Riot chip's timer in units of 64 clock cycles.
;
;   INPUTS
;	units - The number of 64 clock cycle units.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

Tim64T:

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Record the clock value for time-out

	lsl.l	#6,d0
	add.l	d0,d1
	move.l	d1,(env_Timer,a1)

	; Record the shift value for this function's interval

	move.w	#6,(env_TimerShift,a1)

	; Return

	rts



;****** atari2600.library/A2600_Tim1024T ***
;
;   NAME
;	A2600_Tim1024T -- Set timer in units of 1024 clock cycles.
;
;   SYNOPSIS
;	A2600_Tim1024T(units,clock,env)
;	               d0    d1    a1
;
;	VOID A2600_Tim1024T(ULONG,ULONG,APTR);
;
;   FUNCTION
;	Sets the Riot chip's timer in units of 1024 clock cycles.
;
;   INPUTS
;	units - The number of 1024 clock cycle units.
;	clock - The number of emulated clock cycles since the program began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

Tim1024T:

	; Correct clock parameter

	sub.l	(env_ClockOffset,a1),d1

	; Record the clock value for time-out

	swap.w	d0
	lsr.l	#6,d0
	add.l	d0,d1
	move.l	d1,(env_Timer,a1)

	; Record the shift value for this function's interval

	move.w	#10,(env_TimerShift,a1)

	; Return

	rts



;****i* atari2600.library/NoFunction ***
;
;   NAME
;	NoFunction
;
;   SYNOPSIS
;	NoFunction()
;
;***
;
;

NoFunction:

	moveq	#0,d0
	rts



;****i* atari2600.library/AllocateSound ***
;
;   NAME
;	AllocateSound
;
;   SYNOPSIS
;	AllocateSound(on,env)
;	              d0 a1
;
;   FUNCTION
;	Allocates or deallocates sound.
;
;   INPUTS
;	on - True for sound to be switched on, false for sound to be
;	    switched off.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	new_state - True for on, false for off.
;
;***
;
;

AllocateSound:

	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a2-a3/a5/a6,-(sp)

	; Store parameters

	movea.l	a1,a3
	movea.l	a6,a5

	; Find desired new state

	tst.l	d0
	beq	off$

	; Open audio.device and allocate any two audio channels

	movea.l	(ab_SysLib,a5),a6

	JSRLIB	CreateMsgPort
	move.l	d0,(env_AudioMsgPort,a3)
	beq	off$

	movea.l	d0,a0
	move.l	#ioa_SIZEOF,d0
	JSRLIB	CreateIORequest
	move.l	d0,(env_AudioIO,a3)
	beq	off$

	lea.l	audio_name,a0
	movea.l	(env_AudioIO,a3),a1
	move.b	#ADALLOC_MAXPREC,(LN_PRI,a1)
	move.w	#0,(ioa_AllocKey,a1)
	move.l	#channel_combinations,(ioa_Data,a1)
	move.l	#6,(ioa_Length,a1)
	move.b	#ADIOF_NOWAIT,(IO_FLAGS,a1)
	JSRLIB	OpenDevice
	tst.l	d0
	bne	off$
	move.w	#1,(env_AudioOpen,a3)

	; Reset audio channels

	movea.l	(env_AudioIO,a3),a1
	move.w	#CMD_RESET,(IO_COMMAND,a1)
	move.w	period_table,(ioa_Period,a1)
	move.w	#0,(ioa_Volume,a1)
	JSRLIB	DoIO

	; Turn off sound filter

	move.b	#2,$bfe001

	; Get mask for first channel

	movea.l	(env_AudioIO,a3),a1
	move.l	(IO_UNIT,a1),d0

	bfffo	d0{24:8},d1
	moveq	#0,d2
	bfset.l	d2{d1:1}
	move.l	d2,(env_AudioChannelMasks,a3)

	; Get mask for second channel

	bfclr.l	d0{d1:1}
	bfffo	d0{24:8},d1
	moveq	#0,d2
	bfset.l	d2{d1:1}
	move.l	d2,(env_AudioChannelMasks+4,a3)

	; Initialise loop for making IORequests for volume and pitch changes

	moveq	#AUDIO_CHANNEL_COUNT-1,d2

pervol_on_loop$:

	; Allocate memory for IORequest

	movea.l	(env_AudioMsgPort,a3),a0
	move.l	#ioa_SIZEOF,d0
	JSRLIB	CreateIORequest
	move.l	d0,(env_PerVolIO,a3,d2*4)
	beq	off$

	; Copy the contents of the original IORequest used to open the audio
	; device.

	movea.l	(env_AudioIO,a3),a0
	movea.l	d0,a1
	move.l	#ioa_SIZEOF,d0
	JSRLIB	CopyMem

	; Initialise IORequest for changing period and volume

	movea.l	(env_PerVolIO,a3,d2*4),a1
	move.w	#ADCMD_PERVOL,(IO_COMMAND,a1)
	move.l	(env_AudioChannelMasks,a3,d2*4),(IO_UNIT,a1)

	; Loop back

	dbra	d2,pervol_on_loop$

	; Initialise loop for making IORequests for tone type changes

	moveq	#AUDIO_CHANNEL_COUNT*AUDIO_BUFFER_COUNT-1,d2

tone_on_loop$:

	; Allocate memory for IORequest

	movea.l	(env_AudioMsgPort,a3),a0
	move.l	#ioa_SIZEOF,d0
	JSRLIB	CreateIORequest
	movea.l	d0,a2
	move.l	d0,(env_ToneTypeIO,a3,d2*4)
	beq	off$

	; Copy the contents of the original IORequest used to open the audio
	; device.

	movea.l	(env_AudioIO,a3),a0
	movea.l	d0,a1
	move.l	#ioa_SIZEOF,d0
	JSRLIB	CopyMem

	; Initialise IORequest for changing tone type

	movea.l	a2,a1
	move.w	#CMD_WRITE,(IO_COMMAND,a1)
	move.l	d2,d3
	divu.w	#AUDIO_BUFFER_COUNT,d3
	move.l	(env_AudioChannelMasks.w,a3,d3*4),(IO_UNIT,a1)
	move.w	#0,(ioa_Cycles,a1)

	; Start initial sound

	move.l	(ab_Sounds,a5),(ioa_Data,a1)
	move.l	sound_length_table,(ioa_Length,a1)
	JSRLIB	SendIO

	; Abort sound for this IORequest if it is the current one for its
	; channel.

	swap.w	d3
	tst.w	d3
	bne	on_no_abort$

	movea.l	a2,a1
	JSRLIB	AbortIO

on_no_abort$:

	; Loop back

	dbra	d2,tone_on_loop$

	; Set result as on

	moveq	#-1,d0

	bra	end$

off$:

	; Check if audio device was opened

	movea.l	(ab_SysLib,a5),a6

	tst.w	(env_AudioOpen,a3)
	beq	skip_close_audio$

	; Initialise tone type loop

	moveq	#AUDIO_CHANNEL_COUNT*AUDIO_BUFFER_COUNT-1,d2

tone_off_loop$:

	; Check if this IORequest was allocated

	movea.l	(env_ToneTypeIO,a3,d2*4),d0
	beq	skip_tone_type_io$
	movea.l	d0,a2

	; Cancel any use of this IORequest

	btst.l	#0,d2
	beq	off_no_abort$
	movea.l	a2,a1
	JSRLIB	AbortIO

off_no_abort$:

	movea.l	a2,a1
	JSRLIB	WaitIO

	; Delete tone type IORequest

	movea.l	a2,a0
	JSRLIB	DeleteIORequest

skip_tone_type_io$:

	; Loop

	dbra	d2,tone_off_loop$

	; Initialise period/volume loop

	moveq	#AUDIO_CHANNEL_COUNT-1,d2

pervol_off_loop$:

	; Delete period/volume IORequest

	movea.l	(env_PerVolIO,a3,d2*4),a0
	JSRLIB	DeleteIORequest

	; Loop

	dbra	d2,pervol_off_loop$

	; Close audio.device

	movea.l	(env_AudioIO,a3),a1
	JSRLIB	CloseDevice

skip_close_audio$:

	; Delete audio's IORequest

	movea.l	(env_AudioIO,a3),a0
	JSRLIB	DeleteIORequest

	; Delete audio's message port

	movea.l	(env_AudioMsgPort,a3),a0
	JSRLIB	DeleteMsgPort

	; Set result as off

	moveq	#0,d0

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a2-a3/a5/a6
	rts



;****i* atari2600.library/GetHiddenBitPlanes ***
;
;   NAME
;	GetHiddenBitPlanes -- Get address of table of hidden bitplanes.
;
;   SYNOPSIS
;	bit_planes = GetHiddenBitPlanes(env)
;	d0                              a1
;
;   FUNCTION
;
;   INPUTS
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	A table of the addresses of the currently hidden bitmap's planes.
;
;***
;
;

GetHiddenBitPlanes:

	; Set result as address of bitplane list within bitmap structure

	movea.l	(env_ScreenBitMaps+4,a1),a0
	lea.l	(bm_Planes,a0),a1
	move.l	a1,d0

	; Return

	rts



;****i* atari2600.library/MakePlayfieldPatternLine ***
;
;   NAME
;	MakePlayfieldPatternLine -- Construct the current playfield TV line.
;
;   SYNOPSIS
;	MakePlayfieldPatternLine(settings,env)
;	                         d1       a1
;
;   FUNCTION
;
;   INPUTS
;	settings -
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

MakePlayfieldPatternLine:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a2-a3,-(sp)

	; Get playfield graphics and pattern line and transformation tables

	movea.l	(env_PatternLine,a1),a1
	movea.l	a1,a3
	move.l	d1,d0
	lea.l	long_stretch_table,a0
	lea.l	byte_reverse_table,a2

	; Make empty pattern line if in vblank

	btst.l	#OBJB_VBLANK,d0
	beq	skip_vblank$

	clr.l	(a1)+
	clr.l	(a1)+
	clr.l	(a1)+
	clr.l	(a1)+
	clr.l	(a1)+

	bra	end$

skip_vblank$:

	; Fill left hand side of playfield pattern line

	moveq	#12,d2
	moveq	#8,d3

	bfextu	d0{d2:d3},d1
	move.l	(a0,d1*4),(a1)+
	addq.b	#8,d2

	bfextu	d0{d2:d3},d1
	move.l	(a0,d1*4),(a1)+
	addq.b	#4,d2

	bfextu	d0{d2:d3},d1
	move.w	(2,a0,d1*4),(a1)+

	; See if copy or relection mode should be used for remainder of line

	btst.l	#OBJB_REFLECTION,d0
	bne	reflected$

	; Fill right hand side of playfield pattern line in copy mode

	move.l	(a3)+,(a1)+
	move.l	(a3)+,(a1)+
	move.w	(a3)+,(a1)+

	bra	skip_reflected$

reflected$:

	; Fill right hand side of playfield pattern line in reflection mode

	moveq	#24,d2
	moveq	#8,d3

	bfextu	d0{d2:d3},d1
	move.b	(a2,d1),d1
	move.l	(a0,d1*4),(a1)+
	subq.b	#8,d2

	bfextu	d0{d2:d3},d1
	move.b	(a2,d1),d1
	move.l	(a0,d1*4),(a1)+
	subq.b	#4,d2

	bfextu	d0{d2:d3},d1
	move.b	(a2,d1),d1
	move.w	(2,a0,d1*4),(a1)+

skip_reflected$:

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a2-a3

	rts



;****i* atari2600.library/MakeBallPatternLine ***
;
;   NAME
;	MakeBallPatternLine -- Construct the current ball TV line.
;
;   SYNOPSIS
;	MakeBallPatternLine(settings,env)
;	                    d1       a1
;
;   FUNCTION
;
;   INPUTS
;	settings -
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

MakeBallPatternLine:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2-d4/a2-a4,-(sp)

	; Store parameters

	move.l	d1,d4
	movea.l	a1,a3

	; Clear the line buffer

	movea.l	(env_PatternLine,a3),a0
	movea.l	a0,a1

	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+

	clr.l	(a0)+

	; Check if vertical blanking is on

	btst.l	#OBJB_VBLANK,d4
	bne	end$

	; Get ball position and separate it into a word and a bit

	bfextu	d4{OBJO_POSITION:OBJW_POSITION},d0
	move.w	d0,d1
	andi.w	#$f,d1
	lsr.w	#4,d0

	; Create a longword that contains the ball's image pattern

	bfextu	d4{OBJO_WIDTH:OBJW_WIDTH},d3
	moveq	#1,d2
	lsl.w	d3,d2

	moveq	#0,d3
	bfset	d3{d1:d2}

	; See whether undelayed or delayed enable flag should be used

	btst.w	#OBJB_VDELAY,d4
	bne	delayed$

	btst.l	#OBJB_UNDELAYEDENABLE,d4
	bra	skip_delayed$

delayed$:

	btst.l	#OBJB_DELAYEDENABLE,d4

skip_delayed$:

	; Write image pattern to line buffer if ball is enabled

	beq	disabled$
	move.l	d3,(a1,d0*2)

disabled$:

	; Wrap around graphics that extend past end of line

	cmpi.w	#BYTES_PER_LINE/2/2,d0
	blo	no_wrap$

	move.l	(BYTES_PER_LINE,a1),(a1)

no_wrap$:

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d4/a2-a4

	rts



;****i* atari2600.library/MakePlayerPatternLine ***
;
;   NAME
;	MakePlayerPatternLine -- Construct a player 0 TV line.
;
;   SYNOPSIS
;	MakePlayerPatternLine(settings,env)
;	                      d1       a1
;
;   FUNCTION
;
;   INPUTS
;	settings -
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

MakePlayerPatternLine:

	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a3,-(sp)

	; Store parameters

	move.l	d1,d6
	movea.l	a1,a3

	; Clear the line buffer

	movea.l	(env_PatternLine,a3),a1
	movea.l	a1,a0

	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+

	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+

	; Check if vertical blanking is on

	btst.l	#OBJB_VBLANK,d6
	bne	end$

	; Get player's position and separate it into byte and bit
	; offsets.

	bfextu	d6{OBJO_POSITION:OBJW_POSITION},d0
	move.w	d0,d1
	andi.w	#$7,d1
	lsr.w	#3,d0

	; Store position's byte number

	move.w	d0,d7

	; Prepare a register to hold graphics and see if delayed graphics
	; should be used.

	moveq	#0,d2

	btst.w	#OBJB_VDELAY,d6
	bne	delayed$

	; Get undelayed player graphics

	move.b	d6,d2
	bra	undelayed$

	; Get delayed player graphics

delayed$:

	bfextu	d6{OBJO_DELAYEDGRAPHICS:OBJW_DELAYEDGRAPHICS},d2

undelayed$:

	; Reverse graphics if reflection is on for this player

	btst.w	#OBJB_REFLECTION,d6
	beq	no_reflect$
	lea.l	byte_reverse_table,a0
	move.b	(a0,d2),d2

no_reflect$:

	; Get repetition/stretch setting

	bfextu	d6{OBJO_NUMBERSIZE:OBJW_NUMBERSIZE},d3

	; Check if either double or quad size is specified

	cmpi.b	#$5,d3
	beq	double_width$
	cmpi.b	#$7,d3
	beq	quad_width$

	; Get normal-width repetition pattern

	lea.l	num_size_table,a0
	move.b	(a0,d3),d3

	; Place graphics in left byte of word

	lsl.w	#8,d2

	; Shift graphics right by bit component of player's position

	lsr.w	d1,d2

	; Get address at which to start writing graphics

	lea.l	(a1,d0.w),a0

	; Initialise loop counter with the number of normal width "columns"
	; to be written.

	moveq	#5-1,d1

normal_width_loop$:

	; Save graphics to player's pattern if the player should be present
	; in the next word.

	lsr.w	#1,d3
	bcc	no_draw$

	move.w	d2,(a0)

no_draw$:

	; Move on to next column

	addq.l	#2,a0
	dbra	d1,normal_width_loop$

	bra	end$

double_width$:

	; One emulated pixel must be added to the bit offset in double-width
	; mode.

	addq.w	#1,d1

	; Adjust byte and bit offsets if bit offset now exceeds seven

	btst.w	#3,d1
	beq	double_no_adjust$
	addq.w	#1,d0
	bclr.w	#3,d1

double_no_adjust$:

	; Stretch graphics byte over a word.

	lea.l	word_stretch_table,a0
	move.w	(a0,d2*2),d2

	; Left-justify the graphics within their register.

	swap.w	d2

	; Shift graphics right by bit offset.

	lsr.l	d1,d2

	; Write the graphics to the line buffer at the appropriate offset.

	move.l	d2,(a1,d0.w)

	bra	end$

quad_width$:

	; One emulated pixel must be added to the bit offset in quad-width
	; mode.

	addq.w	#1,d1

	; Adjust byte and bit offsets if bit offset now exceeds seven

	btst.w	#3,d1
	beq	quad_no_adjust$
	addq.w	#1,d0
	bclr.w	#3,d1

quad_no_adjust$:

	; Stretch graphics byte over a longword

	lea.l	long_stretch_table,a0
	move.l	(a0,d2*4),d2

	; Load address within line pattern to start writing at

	lea.l	(a1,d0.w),a0

	; Set a register to the value that will complete a 16-bit shift
	; after a shift by the bit offset.

	moveq	#16,d0
	sub.w	d1,d0

	; Clear temporary register to hold graphics

	moveq	#0,d3

	; Move the left word of the graphics into the temporary register,
	; rotate it right by the bit offset, and write the lower word of the
	; result to the line pattern.

	swap.w	d2
	move.w	d2,d3
	ror.l	d1,d3
	move.w	d3,(a0)+

	; Right-shift the wrapped-around portion of the temporary register
	; so that it is right-justified within the upper word.

	lsr.l	d0,d3

	; Move the right word of the graphics into the lower word of the
	; temporary register.

	swap.w	d2
	move.w	d2,d3

	; Rotate the temporary register right by the bit offset as
	; before, which this time brings the unwritten portion of the left
	; graphics word into the the lower half of the register.

	ror.l	d1,d3

	; Write this combination of the left and right graphics words to the
	; line pattern.

	move.w	d3,(a0)+

	; Write the remaining portion of the right graphics word to the line
	; pattern.

	swap.w	d3
	move.w	d3,(a0)+

end$:

	; Wrap around graphics that extend past end of line

	cmpi.w	#10,d7
	blo	no_wrap$

	lea.l	(BYTES_PER_LINE,a1),a0
	move.l	(a0)+,(a1)+
	move.l	(a0)+,(a1)+
	move.w	(a0)+,(a1)+

no_wrap$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a3

	rts



;****i* atari2600.library/MakeMissilePatternLine ***
;
;   NAME
;	MakeMissilePatternLine -- Construct a missile TV line.
;
;   SYNOPSIS
;	MakeMissilePatternLine(settings,env)
;	                       d1       a1
;
;   FUNCTION
;
;   INPUTS
;	settings -
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

MakeMissilePatternLine:

	; Push registers that should be preserved onto stack

	movem.l	d2-d4/a3,-(sp)

	; Store parameters

	move.l	d1,d4
	movea.l	a1,a3

	; Clear the line buffer

	movea.l	(env_PatternLine,a3),a1
	movea.l	a1,a0

	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+

	clr.l	(a0)+

	; Check if vertical blanking is on

	btst.l	#OBJB_VBLANK,d4
	bne	end$

	; Check if the missile is locked

	btst.l	#OBJB_LOCKED,d4
	bne	end$

	; Get missile position and separate it into a word and a bit

	bfextu	d4{OBJO_POSITION:OBJW_POSITION},d0
	move.w	d0,d1
	andi.w	#$f,d1
	lsr.w	#4,d0

	; Create a longword that contains the missile's image pattern

	bfextu	d4{OBJO_WIDTH:OBJW_WIDTH},d3
	moveq	#1,d2
	lsl.w	d3,d2

	moveq	#0,d3
	bfset	d3{d1:d2}

	; Write image pattern to line buffer if missile is enabled

	btst.w	#OBJB_UNDELAYEDENABLE,d4
	beq	disabled$
	move.l	d3,(a1,d0*2)

disabled$:

	; Wrap around graphics that extend past end of line

	cmpi.w	#BYTES_PER_LINE/2/2,d0
	blo	no_wrap$

	move.l	(BYTES_PER_LINE,a1),(a1)

no_wrap$:

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d4/a3

	rts



;****i* atari2600.library/MakeBitPlanePatternLine ***
;
;   NAME
;	MakeBitPlanePatternLine -- Make a TV line for a colour's bitplane.
;
;   SYNOPSIS
;	MakeBitPlanePatternLine(settings,env)
;	                        d1       a1
;
;   FUNCTION
;
;   INPUTS
;	settings -
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

MakeBitPlanePatternLine:

	; Get address of pattern line

	movea.l	(env_PatternLine,a1),a0

	; See whether the line should be filled with ones or zeros

	btst.l	#PENB_VBLANK,d1
	bne	zeros$

	btst.l	#PENB_NORMALENABLE,d1
	beq	zeros$

	; Set the line buffer

	move.l	#-1,(a0)+
	move.l	#-1,(a0)+
	move.l	#-1,(a0)+
	move.l	#-1,(a0)+
	move.l	#-1,(a0)+

	bra	end$

zeros$:

	; Clear the line buffer

	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+

end$:

	; Return

	rts



;****i* atari2600.library/MakePlayfieldBitPlanePatternLine ***
;
;   NAME
;	MakePlayfieldBitPlanePatternLine -- Make playfield colour's TV line.
;
;   SYNOPSIS
;	MakePlayfieldBitPlanePatternLine(settings,env)
;	                                 d1       a1
;
;   FUNCTION
;
;   INPUTS
;	settings -
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

MakePlayfieldBitPlanePatternLine:

	; Get address of pattern line

	movea.l	(env_PatternLine,a1),a0

	; See whether the first half of the line should be filled with ones
	; or zeros.

	btst.l	#PENB_VBLANK,d1
	bne	zeros_left$

	btst.l	#PENB_SCOREMODE,d1
	bne	use_left_colour$

	btst.l	#PENB_NORMALENABLE,d1

	bra	skip_use_left_colour$

use_left_colour$:

	btst.l	#PENB_LEFTENABLE,d1

skip_use_left_colour$:

	beq	zeros_left$

	; Set half the line buffer

	move.l	#-1,(a0)+
	move.l	#-1,(a0)+
	move.w	#-1,(a0)+

	bra	skip_zeros_left$

zeros_left$:

	; Clear half the line buffer

	clr.l	(a0)+
	clr.l	(a0)+
	clr.w	(a0)+

skip_zeros_left$:

	; See whether the second half of the line should be filled with ones
	; or zeros.

	btst.l	#PENB_VBLANK,d1
	bne	zeros_right$

	btst.l	#PENB_SCOREMODE,d1
	bne	use_right_colour$

	btst.l	#PENB_NORMALENABLE,d1

	bra	skip_use_right_colour$

use_right_colour$:

	btst.l	#PENB_RIGHTENABLE,d1

skip_use_right_colour$:

	beq	zeros_right$

	; Set half the line buffer

	move.w	#-1,(a0)+
	move.l	#-1,(a0)+
	move.l	#-1,(a0)

	bra	skip_zeros_right$

zeros_right$:

	; Clear half the line buffer

	clr.w	(a0)+
	clr.l	(a0)+
	clr.l	(a0)

skip_zeros_right$:

	; Return

	rts



;****i* atari2600.library/MakePriorityPatternLine ***
;
;   NAME
;	MakePriorityPatternLine --
;
;   SYNOPSIS
;	MakePriorityPatternLine(settings,env)
;	                        d1       a1
;
;   FUNCTION
;
;   INPUTS
;	settings -
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

MakePriorityPatternLine:

	; Get address of pattern line

	movea.l	(env_PatternLine,a1),a0

	; See whether the line should be filled with ones or zeros

	btst.l	#PRIB_ON,d1
	beq	zeros$

	; Set the line buffer

	move.l	#-1,(a0)+
	move.l	#-1,(a0)+
	move.l	#-1,(a0)+
	move.l	#-1,(a0)+
	move.l	#-1,(a0)+

	bra	end$

zeros$:

	; Clear the line buffer

	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+
	clr.l	(a0)+

end$:

	; Return

	rts



;****i* atari2600.library/FillPatternSection ***
;
;   NAME
;	FillPatternSection -- Fill part of an object pattern.
;
;   SYNOPSIS
;	FillPatternSection(object,start_point,end_point,env)
;	                   d0     d1          d2        a1
;
;   FUNCTION
;
;   INPUTS
;	object - The object whose pattern is to be updated.
;	start_point - A valid display position.
;	end_point - A valid display position.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

FillPatternSection:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a4,-(sp)

	; Get address of starting longword in object pattern

	movea.l	(env_Patterns,a1,d0*4),a3

	move.w	d1,d6
	lsr.w	#5,d6
	lea.l	(a3,d6.w*4),a3

	move.l	d1,d6
	swap.w	d6
	mulu.w	#BYTES_PER_LINE,d6
	adda.l	d6,a3

	; Load address of object's pattern line

	movea.l	(env_PatternLine,a1),a2

	; See if the new update point is on a later line than the old one

	move.l	d2,d4
	swap.w	d4
	move.l	d1,d3
	swap.w	d3
	cmp.w	d3,d4
	bne	multiple_lines$

	; See if fill starts and stops on the same longword

	move.w	d2,d4
	lsr.w	#5,d4
	move.w	d1,d3
	lsr.w	#5,d3
	cmp.w	d3,d4

	bne	multiple_longwords$

	; See if start and end points are the same

	move.w	d2,d4
	sub.w	d1,d4
	beq	end$

	; Get longword containing the bits to be inserted into pattern

	move.w	d1,d3
	lsr.w	#5,d3
	move.l	(a2,d3.w*4),d5

	; Extract the relevant bits from the longword

	bfextu	d5{d1:d4},d5

	; Replace bits in old longword

	move.l	(a3),d6
	bfins	d5,d6{d1:d4}
	move.l	d6,(a3)

	bra	end$

multiple_longwords$:

	; Get first longword to be used from pattern line

	move.w	d1,d3
	lsr.w	#5,d3
	lea.l	(a2,d3.w*4),a0
	move.l	(a0)+,d5

	; Extract the relevant bits from the longword

	moveq	#32,d4
	move.w	d1,d3
	andi.w	#$1f,d3
	sub.w	d3,d4
	bfextu	d5{d1:d4},d5

	; Replace bits in starting longword

	move.l	(a3),d6
	bfins	d5,d6{d1:d4}
	move.l	d6,(a3)+

	; Fill any complete longwords in the range

	move.w	d2,d4
	lsr.w	#5,d4
	move.w	d1,d3
	lsr.w	#5,d3
	sub.w	d3,d4
	subq.w	#1,d4

	bra	loop_start$

next_longword$:

	move.l	(a0)+,(a3)+

loop_start$:

	dbra	d4,next_longword$

	; Fill part of ending longword

	move.w	d2,d3
	andi.w	#$1f,d3
	beq	end$

	move.l	(a0),d5
	bfextu	d5{0:d2},d5

	move.l	(a3),d6
	bfins	d5,d6{0:d2}
	move.l	d6,(a3)

	bra	end$

multiple_lines$:

	; Get first longword to be used from pattern line

	move.w	d1,d3
	lsr.w	#5,d3
	lea.l	(a2,d3.w*4),a0
	move.l	(a0)+,d5

	; Extract the relevant bits from the longword

	moveq	#32,d4
	move.w	d1,d3
	andi.w	#$1f,d3
	sub.w	d3,d4
	bfextu	d5{d1:d4},d5

	; Replace bits in starting longword

	move.l	(a3),d6
	bfins	d5,d6{d1:d4}
	move.l	d6,(a3)+

	; Fill the rest of the first line

	moveq	#(TV_WIDTH>>5)-1,d3
	move.w	d1,d4
	lsr.w	#5,d4
	sub.w	d4,d3

	bra	loop_start_2$

next_longword_2$:

	move.l	(a0)+,(a3)+

loop_start_2$:

	dbra	d3,next_longword_2$

	; Fill any complete lines in the range

	move.l	d2,d3
	swap.w	d3
	move.l	d1,d4
	swap.w	d4

	sub.w	d4,d3
	subq.w	#1,d3

	bra	line_loop_start$

next_line$:

	movea.l	a2,a0

	move.l	(a0)+,(a3)+
	move.l	(a0)+,(a3)+
	move.l	(a0)+,(a3)+
	move.l	(a0)+,(a3)+
	move.l	(a0)+,(a3)+

line_loop_start$:

	dbra	d3,next_line$

	; Fill complete longwords in final line of the fill

	move.w	d2,d3
	lsr.w	#5,d3

	movea.l	a2,a0
	bra	loop_start_3$

next_longword_3$:

	move.l	(a0)+,(a3)+

loop_start_3$:

	dbra	d3,next_longword_3$

	; Fill part of ending longword

	andi.w	#$1f,d2
	beq	end$

	move.l	(a0),d5
	bfextu	d5{0:d2},d5
	move.l	(a3),d6
	bfins	d5,d6{0:d2}
	move.l	d6,(a3)

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a4

	rts



;****i* atari2600.library/MakeSummaryPattern ***
;
;   NAME
;	MakeSummaryPattern -- Summarise an object's pattern.
;
;   SYNOPSIS
;	MakeSummaryPattern(object,env)
;	                   d0     a1
;
;   FUNCTION
;
;   INPUTS
;	object - The object whose pattern is to be summarised.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

MakeSummaryPattern:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a2-a3,-(sp)

	; Store parameters

	move.l	d0,d2
	movea.l	a1,a3

	; Load addresses of patterns used

	movea.l	(env_Patterns,a3,d2*4),a0
	movea.l	(env_SummaryPatterns,a3,d2*4),a1

	; Initialise outer loop counter

	move.l	(env_RoundedTVHeight,a3),d0
	lsr.w	#2,d0
	subq.w	#1,d0

next_line$:

	; Initialise inner loop

	moveq	#0,d3
	move.w	#BYTES_PER_LINE-1,d1

next_longword$:

	; Check if longword is empty

	tst.l	(a0)+
	bne	something_there$

	; Clear bit to indicate that longword is empty

	bclr.l	d1,d3
	bra	skip_something_there$

something_there$:

	; Set bit to indicate that longword is non-empty

	bset.l	d1,d3

skip_something_there$:

	dbra	d1,next_longword$

	; Store longword representing four lines

	move.l	d3,(a1)+

	dbra	d0,next_line$

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a2-a3

	rts



;****i* atari2600.library/FindCollision ***
;
;   NAME
;	FindCollision
;
;   SYNOPSIS
;	FindCollision(obj_0,obj_1,result_offset,result_bit,env)
;	              d0    d1    d2            d3         a1
;
;   FUNCTION
;
;   INPUTS
;	obj_0 - The first object.
;	obj_1 - The second object.
;	result_offset - The offset in Atari 2600 memory where the result
;	    should be written.
;	result_bit - The bit of the result byte that represents the
;	    collision.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

FindCollision:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a6,-(sp)

	; Store parameters

	move.l	d0,d6
	move.l	d1,d7
	movea.l	a1,a3

	; Load addresses of patterns used

	movea.l	(env_SummaryPatterns,a3,d6*4),a0
	movea.l	(env_SummaryPatterns,a3,d7*4),a1
	movea.l	(env_Patterns,a3,d6*4),a4
	movea.l	(env_Patterns,a3,d7*4),a5

	; Move pointers on to end of each pattern

	adda.l	(env_RoundedTVHeight,a3),a0
	adda.l	(env_RoundedTVHeight,a3),a1
	adda.l	(env_PatternSize,a3),a4
	adda.l	(env_PatternSize,a3),a5

	; Initialise loop counter

	move.l	(env_RoundedTVHeight,a3),d0
	lsr.w	#2,d0
	subq.w	#1,d0

next_line$:

	move.l	-(a0),d1
	and.l	-(a1),d1
	bne	detailed_line_search$

continue_summary_search$:

	suba.l	#BYTES_PER_LINE<<2,a4
	suba.l	#BYTES_PER_LINE<<2,a5

	dbra	d0,next_line$

	; Set result as not found

	moveq	#0,d5
	bra	store_result$

detailed_line_search$:

	movea.l	a4,a2
	movea.l	a5,a6

	; Initialise loop counter

	move.w	#BYTES_PER_LINE-1,d4

next_longword$:

	move.l	-(a2),d1
	and.l	-(a6),d1
	bne	collision_found$
	dbra	d4,next_longword$

	bra	continue_summary_search$

collision_found$:

	moveq	#1,d5

store_result$:

	; Test whether a collision was found

	tst.w	d5
	bne	store_collision$

	; Record a non-collision

	bclr.b	d3,([env_Data,a3],d2)
	bclr.b	d3,([env_Data,a3],d2,$10)
	bclr.b	d3,([env_Data,a3],d2,$20)
	bclr.b	d3,([env_Data,a3],d2,$30)
	bclr.b	d3,([env_Data,a3],d2,$40)
	bclr.b	d3,([env_Data,a3],d2,$50)
	bclr.b	d3,([env_Data,a3],d2,$60)
	bclr.b	d3,([env_Data,a3],d2,$70)

	bra	skip_store_collision$

store_collision$:

	; Record a collision

	bset.b	d3,([env_Data,a3],d2)
	bset.b	d3,([env_Data,a3],d2,$10)
	bset.b	d3,([env_Data,a3],d2,$20)
	bset.b	d3,([env_Data,a3],d2,$30)
	bset.b	d3,([env_Data,a3],d2,$40)
	bset.b	d3,([env_Data,a3],d2,$50)
	bset.b	d3,([env_Data,a3],d2,$60)
	bset.b	d3,([env_Data,a3],d2,$70)

skip_store_collision$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a6

	rts



;****i* atari2600.library/CleanUp ***
;
;   NAME
;	CleanUp -- Deallocate the library's resources.
;
;   SYNOPSIS
;	CleanUp(library,exec)
;	        a5      a6
;
;   FUNCTION
;
;   INPUTS
;
;   RESULT
;	None.
;
;***
;
;

CleanUp:

	; Free memory for sound 0

	move.l	#SOUND_0_MULTIPLE,d0
	movea.l	(ab_Sounds,a5),d1
	beq	skip_sound_0$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_0$:

	; Free memory for sound 1

	move.l	#FOUR_BIT_POLY_SIZE*SOUND_1_MULTIPLE,d0
	movea.l	(ab_Sounds+4,a5),d1
	beq	skip_sound_1$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_1$:

	; Free memory for sound 2

	move.l	#FIVE_BIT_POLY_SIZE*FOUR_BIT_POLY_SIZE*SOUND_2_MULTIPLE,d0
	movea.l	(ab_Sounds+2*4,a5),d1
	beq	skip_sound_2$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_2$:

	; Free memory for sound 3

	move.l	#FIVE_BIT_POLY_SIZE*FOUR_BIT_POLY_SIZE*SOUND_3_MULTIPLE,d0
	movea.l	(ab_Sounds+3*4,a5),d1
	beq	skip_sound_3$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_3$:

	; Free memory for sound 4

	move.l	#SOUND_4_MULTIPLE,d0
	movea.l	(ab_Sounds+4*4,a5),d1
	beq	skip_sound_4$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_4$:

	; Free memory for sound 6

	move.l	#31*SOUND_6_MULTIPLE,d0
	movea.l	(ab_Sounds+6*4,a5),d1
	beq	skip_sound_6$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_6$:

	; Free memory for sound 7

	move.l	#FIVE_BIT_POLY_SIZE*SOUND_7_MULTIPLE,d0
	movea.l	(ab_Sounds+7*4,a5),d1
	beq	skip_sound_7$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_7$:

	; Free memory for sound 8

	move.l	#NINE_BIT_POLY_SIZE*SOUND_8_MULTIPLE,d0
	movea.l	(ab_Sounds+8*4,a5),d1
	beq	skip_sound_8$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_8$:

	; Free memory for sound 9

	move.l	#FIVE_BIT_POLY_SIZE*SOUND_9_MULTIPLE,d0
	movea.l	(ab_Sounds+9*4,a5),d1
	beq	skip_sound_9$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_9$:

	; Free memory for sound 12

	move.l	#SLOW_PURE_SIZE*SOUND_12_MULTIPLE,d0
	movea.l	(ab_Sounds+12*4,a5),d1
	beq	skip_sound_12$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_12$:

	; Free memory for sound 14

	move.l	#93*SOUND_14_MULTIPLE,d0
	movea.l	(ab_Sounds+14*4,a5),d1
	beq	skip_sound_14$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_14$:

	; Free memory for sound 15

	move.l	#FIVE_BIT_POLY_SIZE*3*SOUND_15_MULTIPLE,d0
	movea.l	(ab_Sounds+15*4,a5),d1
	beq	skip_sound_15$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_sound_15$:

	; Free memory for word-to-long stretch table

	move.l	#(1<<16)*4,d0
	movea.l	(ab_WordToLongTable,a5),d1
	beq	skip_stretch_table$
	movea.l	d1,a1
	JSRLIB	FreeMem

skip_stretch_table$:

	; Close asl library if it's been opened

	move.l	(ab_AslLib,a5),d0
	beq	skip_close_asl$
	move.l	d0,a1
	JSRLIB	CloseLibrary

skip_close_asl$:

	; Close dos library if it's been opened

	move.l	(ab_DosLib,a5),d0
	beq	skip_close_dos$
	move.l	d0,a1
	JSRLIB	CloseLibrary

skip_close_dos$:

	; Close icon library if it's been opened

	move.l	(ab_IconLib,a5),d0
	beq	skip_close_icon$
	move.l	d0,a1
	JSRLIB	CloseLibrary

skip_close_icon$:

	; Close utility library if it's been opened

	move.l	(ab_UtilityLib,a5),d0
	beq	skip_close_utility$
	move.l	d0,a1
	JSRLIB	CloseLibrary

skip_close_utility$:

	; Close graphics library if it's been opened

	move.l	(ab_GraphicsLib,a5),d0
	beq	skip_close_graphics$
	move.l	d0,a1
	JSRLIB	CloseLibrary

skip_close_graphics$:

	; Close gadtools library if it's been opened

	move.l	(ab_GadtoolsLib,a5),d0
	beq	skip_close_gadtools$
	move.l	d0,a1
	JSRLIB	CloseLibrary

skip_close_gadtools$:

	; Close intuition library if it's been opened

	move.l	(ab_IntuitionLib,a5),d0
	beq	skip_close_intuition$
	move.l	d0,a1
	JSRLIB	CloseLibrary

skip_close_intuition$:

	; Free the library's memory

	move.l	a5,a1
	moveq	#0,d0
	move.w	(LIB_NEGSIZE,a5),d0

	sub.l	d0,a1
	add.w	(LIB_POSSIZE,a5),d0

	JSRLIB	FreeMem

	; Return

	rts



;****i* atari2600.library/FinishFrame ***
;
;   NAME
;	FinishFrame -- Finish current frame's patterns and get collisions.
;
;   SYNOPSIS
;	FinishFrame(env,lib_ptr)
;	            a1  a6
;
;   FUNCTION
;
;   INPUTS
;
;   RESULT
;	None.
;
;***
;
;

FinishFrame:

	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a6,-(sp)

	; Store parameters

	movea.l	a1,a3
	movea.l	a6,a5

	; Fill in patterns

	moveq	#PATTERN_COUNT-1,d2

fill_patterns_loop$

	move.l	d2,d0
	movea.l	a3,a1
	bsr	FillPattern

	dbra	d2,fill_patterns_loop$

	; Merge the patterns of associated object pairs

	moveq	#REDUCED_OBJECT_COUNT-1,d2

object_merge_loop$:

	move.w	d2,d0
	mulu.w	#SCREEN_BUFFER_COUNT+1,d0
	movea.l	(env_MergedPatterns,a3,d0*4),a0
	movea.l	(env_Patterns,a3,d2*4),a1
	movea.l	(env_Patterns+REDUCED_OBJECT_COUNT*4,a3,d2*4),a2
	move.l	(env_PatternSize,a3),d0
	lsr.l	#2,d0
	subq.l	#1,d0

merge_next_longword$:

	move.l	(a1)+,d1
	or.l	(a2)+,d1
	move.l	d1,(a0)+

	dbra	d0,merge_next_longword$

	dbra	d2,object_merge_loop$

	; Construct summary patterns for all objects

	moveq	#OBJECT_COUNT-1,d2

summary_loop$:

	move.l	d2,d0
	movea.l	a3,a1
	bsr	MakeSummaryPattern

	dbra	d2,summary_loop$

	; Find collisions between every pair of objects

	moveq	#M0,d0
	moveq	#P1,d1
	moveq	#A2600_CXM0P,d2
	moveq	#7,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#M0,d0
	moveq	#P0,d1
	moveq	#A2600_CXM0P,d2
	moveq	#6,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#M1,d0
	moveq	#P0,d1
	moveq	#A2600_CXM1P,d2
	moveq	#7,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#M1,d0
	moveq	#P1,d1
	moveq	#A2600_CXM1P,d2
	moveq	#6,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#P0,d0
	moveq	#PF,d1
	moveq	#A2600_CXP0FB,d2
	moveq	#7,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#P0,d0
	moveq	#BL,d1
	moveq	#A2600_CXP0FB,d2
	moveq	#6,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#P1,d0
	moveq	#PF,d1
	moveq	#A2600_CXP1FB,d2
	moveq	#7,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#P1,d0
	moveq	#BL,d1
	moveq	#A2600_CXP1FB,d2
	moveq	#6,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#M0,d0
	moveq	#PF,d1
	moveq	#A2600_CXM0FB,d2
	moveq	#7,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#M0,d0
	moveq	#BL,d1
	moveq	#A2600_CXM0FB,d2
	moveq	#6,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#M1,d0
	moveq	#PF,d1
	moveq	#A2600_CXM1FB,d2
	moveq	#7,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#M1,d0
	moveq	#BL,d1
	moveq	#A2600_CXM1FB,d2
	moveq	#6,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#BL,d0
	moveq	#PF,d1
	moveq	#A2600_CXBLPF,d2
	moveq	#7,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#P0,d0
	moveq	#P1,d1
	moveq	#A2600_CXPPMM,d2
	moveq	#7,d3
	movea.l	a3,a1
	bsr	FindCollision

	moveq	#M0,d0
	moveq	#M1,d1
	moveq	#A2600_CXPPMM,d2
	moveq	#6,d3
	movea.l	a3,a1
	bsr	FindCollision

	; Update address of bitplane table

	movea.l	a3,a1
	bsr	GetHiddenBitPlanes
	move.l	d0,env_PlanePtrs(a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a6
	rts



;****i* atari2600.library/ChangeDisplay ***
;
;   NAME
;	ChangeDisplay -- Display newly written bitmap and hide visible one.
;
;   SYNOPSIS
;	ChangeDisplay(env,lib_ptr)
;	              a1  a6
;
;   FUNCTION
;
;   INPUTS
;
;   RESULT
;	None.
;
;***
;
;

ChangeDisplay:

	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a6,-(sp)

	; Store parameters

	movea.l	a1,a3
	movea.l	a6,a5

	; Wait for notification that the previous frame has been displayed

	movea.l	(ab_SysLib,a5),a6

	movea.l	(env_DispMsgPort,a3),a0
	JSRLIB	WaitPort
	movea.l	(env_DispMsgPort,a3),a0
	JSRLIB	GetMsg

	; Wait until there is no screen-drawing taking place

	movea.l	(ab_GraphicsLib,a5),a6
	JSRLIB	WaitBlit

	; Hide currently visible bitmap and display newly written bitmap

	movea.l	(ab_IntuitionLib,a5),a6

	movea.l	(env_Screen,a3),a0
	movea.l	(env_ScreenBuffers+4,a3),a1
	JSRLIB	ChangeScreenBuffer
	tst.l	d0
	bne	no_delay$

	; Get current time in milliseconds

	movea.l	a5,a6
	bsr	GetTime
	move.l	d0,d2

	; Stop sound if it's on

	tst.w	(env_SoundOn,a3)
	beq	no_sound_off$

	movea.l	(ab_SysLib,a5),a6
	movea.l	(env_AudioIO,a3),a1
	move.w	#CMD_STOP,(IO_COMMAND,a1)
	JSRLIB	DoIO

no_sound_off$:

	; Try again to change bitmaps

	movea.l	(ab_IntuitionLib,a5),a6

try_again$:

	movea.l	(env_Screen,a3),a0
	movea.l	(env_ScreenBuffers+4,a3),a1
	JSRLIB	ChangeScreenBuffer
	tst.l	d0
	beq	try_again$

	; Restart sound if it should be on

	tst.w	(env_SoundOn,a3)
	beq	no_sound_on$

	movea.l	(ab_SysLib,a5),a6
	movea.l	(env_AudioIO,a3),a1
	move.w	#CMD_START,(IO_COMMAND,a1)
	JSRLIB	DoIO

no_sound_on$:

	; Get length of delay

	movea.l	a5,a6
	bsr	GetTime
	sub.l	d2,d0

	; Update the recorded amount of time spent waiting to change bitmaps

	add.l	d0,(env_ScreenSwapWaitTime,a3)

no_delay$:

	; Rotate array of screen buffer pointers

	movea.l	(env_ScreenBuffers+0*4,a3),a0
	movea.l	(env_ScreenBuffers+1*4,a3),(env_ScreenBuffers+0*4,a3)
	movea.l	(env_ScreenBuffers+2*4,a3),(env_ScreenBuffers+1*4,a3)
	movea.l	a0,(env_ScreenBuffers+2*4,a3)

	; Rotate array of bitmap pointers

	movea.l	(env_ScreenBitMaps+0*4,a3),a0
	movea.l	(env_ScreenBitMaps+1*4,a3),(env_ScreenBitMaps+0*4,a3)
	movea.l	(env_ScreenBitMaps+2*4,a3),(env_ScreenBitMaps+1*4,a3)
	movea.l	a0,(env_ScreenBitMaps+2*4,a3)

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a6
	rts



;****i* atari2600.library/UpdateScreen ***
;
;   NAME
;	UpdateScreen -- Redraw areas of the screen that have changed.
;
;   SYNOPSIS
;	UpdateScreen(env,lib_ptr)
;	             a1  a6
;
;   FUNCTION
;
;   INPUTS
;
;   RESULT
;	None.
;
;***
;
;

UpdateScreen:

	; Preserve all register values

	movem.l	d0-d7/a0-a6,-(sp)

	; Make room on stack for local variables

	suba.l	#16,sp

	; Store library and environment pointers

	move.l	a6,(12,sp)
	move.l	a1,(8,sp)
	movea.l	a1,a3

	; Wait for a message that means it's safe to write to the hidden
	; bitmap

	movea.l	(ab_SysLib,a6),a6

	movea.l	(env_SafeMsgPort,a3),a0
	JSRLIB	WaitPort
	movea.l	(env_SafeMsgPort,a3),a0
	JSRLIB	GetMsg

	; Find areas of each object pattern that have changed since the last
	; time the current screen buffer was used.

	moveq	#REDUCED_OBJECT_COUNT-1,d2

diff_loop$:

	movea.l	a3,a1
	move.l	d2,d0
	bsr	FindDifferences

	dbra	d2,diff_loop$

	; Find areas of each colour and priority pattern that have changed
	; since the last time the current screen buffer was used.

	moveq	#PATTERN_COUNT-OBJECT_COUNT-1,d2

colour_diff_loop$:

	movea.l	a3,a1
	move.l	d2,d0
	add.l	#OBJECT_COUNT,d0
	bsr	FindColourDifferences

	dbra	d2,colour_diff_loop$

	; Combine object and priority difference patterns

	movea.l	(env_ObjectDifferencePattern,a3),a6

	movea.l	(env_DifferencePatterns+PF*4,a3),a0
	movea.l	(env_DifferencePatterns+P0*4,a3),a2
	movea.l	(env_DifferencePatterns+P1*4,a3),a4
	movea.l	(env_DifferencePatterns+(REDUCED_PATTERN_COUNT-1)*4,a3),a5

	move.w	(env_TVHeight,a3),d0
	move.w	d0,d1
	lsr.w	#2,d0
	subq.w	#1,d0
	andi.w	#3,d1

obj_diff_longword_loop$:

	move.l	(a0)+,d2
	or.l	(a2)+,d2
	or.l	(a4)+,d2
	or.l	(a5)+,d2
	move.l	d2,(a6)+

	dbra	d0,obj_diff_longword_loop$

	bra	obj_diff_byte_loop_start$

obj_diff_byte_loop$:

	move.b	(a0)+,d2
	or.b	(a2)+,d2
	or.b	(a4)+,d2
	or.b	(a5)+,d2
	move.b	d2,(a6)+

obj_diff_byte_loop_start$:

	dbra	d1,obj_diff_byte_loop$

	; Initialise bit plane loop

	moveq	#SCREEN_DEPTH-1,d2

next_plane$:

	; Combine object differences with differences for this bit plane

	movea.l	(env_DifferencePattern,a3),a6
	movea.l	(env_ObjectDifferencePattern,a3),a5

	movea.l	(env_DifferencePatterns+(REDUCED_OBJECT_COUNT+BK_COLOUR*SCREEN_DEPTH)*4,a3,d2*4),a0
	movea.l	(env_DifferencePatterns+(REDUCED_OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH)*4,a3,d2*4),a1
	movea.l	(env_DifferencePatterns+(REDUCED_OBJECT_COUNT+P0_COLOUR*SCREEN_DEPTH)*4,a3,d2*4),a2
	movea.l	(env_DifferencePatterns+(REDUCED_OBJECT_COUNT+P1_COLOUR*SCREEN_DEPTH)*4,a3,d2*4),a4

	move.w	(env_TVHeight,a3),d0
	move.w	d0,d1
	lsr.w	#2,d0
	subq.w	#1,d0
	andi.w	#3,d1

plane_diff_longword_loop$:

	move.l	(a0)+,d4
	or.l	(a1)+,d4
	or.l	(a2)+,d4
	or.l	(a4)+,d4
	or.l	(a5)+,d4
	move.l	d4,(a6)+

	dbra	d0,plane_diff_longword_loop$

	bra	plane_diff_byte_loop_start$

plane_diff_byte_loop$:

	move.b	(a0)+,d4
	or.b	(a1)+,d4
	or.b	(a2)+,d4
	or.b	(a4)+,d4
	or.b	(a5)+,d4
	move.b	d4,(a6)+

plane_diff_byte_loop_start$:

	dbra	d1,plane_diff_byte_loop$

	; Load address of current bitplane

	movea.l	([env_PlanePtrs,a3],d2*4),a6

	moveq	#0,d0
	move.w	env_SkipMenuOffset(a3),d0
	adda.l	d0,a6

	; Save environment pointer and plane counter

	move.l	d2,(4,sp)
	move.l	a3,(8,sp)

	; Load addresses of patterns

	movea.l	(env_DifferencePattern,a3),a5

	move.l	(env_Patterns+(PATTERN_COUNT-1)*4,a3),a1

	move.l	(env_Patterns+(OBJECT_COUNT+BK_COLOUR*SCREEN_DEPTH)*4,a3,d2*4),d4
	move.l	(env_Patterns+(OBJECT_COUNT+PF_COLOUR*SCREEN_DEPTH)*4,a3,d2*4),d5
	move.l	(env_Patterns+(OBJECT_COUNT+P0_COLOUR*SCREEN_DEPTH)*4,a3,d2*4),d6
	move.l	(env_Patterns+(OBJECT_COUNT+P1_COLOUR*SCREEN_DEPTH)*4,a3,d2*4),d7

	movea.l	(env_MergedPatterns+PF*(SCREEN_BUFFER_COUNT+1)*4,a3),a0
	movea.l	(env_MergedPatterns+P0*(SCREEN_BUFFER_COUNT+1)*4,a3),a2
	movea.l	(env_MergedPatterns+P1*(SCREEN_BUFFER_COUNT+1)*4,a3),a4

	; Initialise line loop

	moveq	#0,d2
	move.w	(env_TVHeight,a3),d2
	subq	#1,d2

	movea.l	([12,sp],ab_WordToLongTable),a3

next_line$:

	; Store line counter

	move.w	d2,(sp)

	; Get differences for this line

	move.b	(a5)+,d2
	bne	line_start$

	; Move all pointers on by a line

	add.l	#BYTES_PER_LINE,d4
	add.l	#BYTES_PER_LINE,d5
	add.l	#BYTES_PER_LINE,d6
	add.l	#BYTES_PER_LINE,d7

	adda.l	#BYTES_PER_LINE,a0
	adda.l	#BYTES_PER_LINE,a1
	adda.l	#BYTES_PER_LINE,a2
	adda.l	#BYTES_PER_LINE,a4

	adda.l	#BYTES_PER_LINE*X_MAG,a6

	bra	skip_line$

line_start$:

	; Initialise longword loop

	swap.w	d2
	move.w	#BYTES_PER_LINE/4-1,d2

next_longword$:

	; Check if the current longword needs to be redrawn

	move.w	d2,d0
	add.w	#16,d0
	btst.l	d0,d2

	bne	longword_start$

	; Move pointers on to next longword

	addq.l	#4,a0
	addq.l	#4,a1
	addq.l	#4,a2
	addq.l	#4,a4
	addq.l	#8,a6

	bra	skip_longword$

longword_start$:

	; Get background

	move.l	(d4.l),d3

	; Apply playfield and ball

	move.l	(d5.l),d1
	move.l	(a0),d0
	and.l	d0,d1
	not.l	d0
	and.l	d0,d3
	or.l	d1,d3

	; Apply second player and missile

	move.l	(d7.l),d1
	move.l	(a4)+,d0
	and.l	d0,d1
	not.l	d0
	and.l	d0,d3
	or.l	d1,d3

	; Apply first player and missile

	move.l	(d6.l),d1
	move.l	(a2)+,d0
	and.l	d0,d1
	not.l	d0
	and.l	d0,d3
	or.l	d1,d3

	; Reapply parts of playfield and ball that have priority

	move.l	(d5.l),d1
	move.l	(a0)+,d0
	and.l	(a1)+,d0
	and.l	d0,d1
	not.l	d0
	and.l	d0,d3
	or.l	d1,d3

	; Stretch combined longword to a quadword and write to bitplane

	move.l	d3,d0
	clr.w	d0
	swap.w	d0
	move.l	(a3,d0.l*4),(a6)+
	andi.l	#$ffff,d3
	move.l	(a3,d3.l*4),(a6)+

skip_longword$:

	; Move colour pattern pointers on to next longword

	addq.l	#4,d4
	addq.l	#4,d5
	addq.l	#4,d6
	addq.l	#4,d7

	; Loop back

	dbra	d2,next_longword$

skip_line$:

	; Restore line counter and loop back

	move.w	(sp),d2
	dbra	d2,next_line$

	; Restore environment pointer and plane counter and loop back

	movea.l	(8,sp),a3
	move.l	(4,sp),d2
	dbra	d2,next_plane$

	; "Rotate" all pattern lists and reset list pointers for next frame

	moveq	#PATTERN_COUNT-1,d2

pattern_list_loop$:

	lea.l	(env_PatternLists,a3),a2
	moveq	#(SCREEN_BUFFER_COUNT+1)*4,d0
	mulu.w	d2,d0
	adda.l	d0,a2

	move.l	(3*4,a2),d0
	move.l	(2*4,a2),(3*4,a2)
	move.l	(1*4,a2),(2*4,a2)
	move.l	(0*4,a2),(1*4,a2)
	move.l	d0,(0*4,a2)

	move.l	d0,(env_PatternListPtrs,a3,d2*4)
	add.l	#LIST_SIZE-2*8,d0
	move.l	d0,(env_PatternListLimits,a3,d2*4)

	dbra	d2,pattern_list_loop$

	; "Rotate" all merged patterns

	moveq	#REDUCED_PATTERN_COUNT-1,d2

merged_pattern_loop$:

	lea.l	(env_MergedPatterns,a3),a2
	moveq	#(SCREEN_BUFFER_COUNT+1)*4,d0
	mulu.w	d2,d0
	adda.l	d0,a2

	move.l	(3*4,a2),d0
	move.l	(2*4,a2),(3*4,a2)
	move.l	(1*4,a2),(2*4,a2)
	move.l	(0*4,a2),(1*4,a2)
	move.l	d0,(0*4,a2)

	dbra	d2,merged_pattern_loop$

	; Readjust stack pointer to remove local variable space

	adda.l	#16,sp

	; Restore all registers from stack and return

	movem.l	(sp)+,d0-d7/a0-a6
	rts



;****i* atari2600.library/GetUserInput ***
;
;   NAME
;	GetUserInput -- Check for user input and respond to it.
;
;   SYNOPSIS
;	GetUserInput()
;
;   FUNCTION
;
;   INPUTS
;
;   RESULT
;	None.
;
;***
;
;

GetUserInput:

	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a6,-(sp)

	; Turn off Select and Reset

	or.b	#SWCHBF_NOSELECT|SWCHBF_NORESET,([env_Data,a3],A2600_SWCHB)
	or.b	#SWCHBF_NOSELECT|SWCHBF_NORESET,([env_Data,a3],A2600_SWCHB+$100)

next_msg$:

	; See if there is an incoming message

	movea.l	ab_GadtoolsLib(a5),a6
	movea.l	env_UserPort(a3),a0
	JSRLIB	GT_GetIMsg
	tst.l	d0
	beq	end$

	; Extract relevant details from message and reply to it

	movea.l	d0,a1
	move.l	(im_Class,a1),d3
	move.w	(im_Code,a1),d2
	move.w	(im_Qualifier,a1),d4
	move.w	(im_MouseX,a1),d5
	JSRLIB	GT_ReplyIMsg

	; Check which type of user-input event occurred

	cmp.l	#IDCMP_MENUPICK,d3
	beq	menu_selection$

	cmp.l	#IDCMP_RAWKEY,d3
	beq	joystick_action$

	cmp.l	#IDCMP_MOUSEBUTTONS,d3
	beq	mouse_button_action$

	cmp.l	#IDCMP_MOUSEMOVE,d3
	beq	new_mouse_position$

	; Skip user input code if message type is not recognised

	bra	next_msg$

menu_selection$:

	; Check that this is a real menu selection

	cmp.w	#MENUNULL,d2
	beq	next_msg$

	; Store MenuNumber

	moveq	#0,d3
	move.w	d2,d3

	; Parse MenuNumber

	moveq	#$1f,d0
	and.w	d2,d0		; d0 is now menu number (natural meaning)

	lsr.w	#5,d2
	moveq	#$3f,d1
	and.w	d2,d1		; d1 is now item number

	lsr.w	#6,d2		; d2 is now subitem number

	; See which menu contains the selected menu item

	cmpi.w	#0,d0
	beq	game_menu$

	cmpi.w	#1,d0
	beq	console_menu$

	bra	next_msg$

game_menu$:

	; See which item from this menu was picked

	cmpi.w	#0,d1
	beq	about$

	cmpi.w	#2,d1
	beq	quit$

	bra	next_msg$

console_menu$:

	; See which item from this menu was picked

	cmpi.w	#0,d1
	beq	select$

	cmpi.w	#1,d1
	beq	reset$

	cmpi.w	#3,d1
	beq	colour$

	cmpi.w	#4,d1
	beq	p0_difficulty$

	bra	next_msg$

about$:

	; Stop sound if it's on

	tst.w	(env_SoundOn,a3)
	beq	no_sound_off$

	movea.l	(ab_SysLib,a5),a6
	movea.l	(env_AudioIO,a3),a1
	move.w	#CMD_STOP,(IO_COMMAND,a1)
	JSRLIB	DoIO

no_sound_off$:

	; Get current time in milliseconds

	movea.l	a5,a6
	bsr	GetTime
	move.l	d0,d1

	; Get number of milliseconds since last report

	sub.l	(env_MillisecondCount,a3),d1

	; Compensate for time spent waiting to change bitmaps

	sub.l	(env_ScreenSwapWaitTime,a3),d1
	bne	skip_nought_milliseconds$
	moveq	#1,d1			; avoid divide-by-zero

skip_nought_milliseconds$:

	; Reset record of time spent waiting to change bitmaps

	clr.l	(env_ScreenSwapWaitTime,a3)

	; Get number of frames since last report

	move.l	(env_FrameCount,a3),d0
	sub.l	(env_PrevFrameCount,a3),d0
	move.l	(env_FrameCount,a3),(env_PrevFrameCount,a3)

	; Convert frame count to "milliframes"

	mulu.l	#1000,d0

	; Calculate and store number of frames per second

	divu.l	d1,d0
	move.w	d0,(env_FramesPerSecond,a3)

	; Display the "About" requester

	movem.l	d0-d7/a0-a6,-(sp)

	movea.l	(env_Window,a3),a0
	movea.l	(env_AboutRequester,a3),a1
	movea.l	#0,a2

	move.w	(env_FrameSkipRatio,a3),-(sp)

	move.w	(env_FramesPerSecond,a3),-(sp)

	move.w	(env_FramesPerSecond,a3),d0
	mulu.w	#100,d0
	mulu.w	(env_FrameSkipRatio,a3),d0
	divu.w	(env_IdealFrameRate,a3),d0
	move.w	d0,-(sp)

	movea.l	sp,a3

	movea.l	(ab_IntuitionLib,a5),a6
	JSRLIB	EasyRequestArgs
	add.w	#6,sp

	movem.l	(sp)+,d0-d7/a0-a6

	; Restart sound if it should be on

	tst.w	(env_SoundOn,a3)
	beq	no_sound_on$

	movea.l	(ab_SysLib,a5),a6
	movea.l	(env_AudioIO,a3),a1
	move.w	#CMD_START,(IO_COMMAND,a1)
	JSRLIB	DoIO

no_sound_on$:

	; Get and store current time in milliseconds

	movea.l	a5,a6
	bsr	GetTime

	move.l	d0,(env_MillisecondCount,a3)

	bra	next_msg$

quit$:

	; Set return code to be used by exit function to "success"

	moveq	#0,d2

	; Restore client program's original stack pointer and jump to its
	; exit function which deallocates its resources and closes this
	; library.

	movea.l	(env_OriginalStackPtr,a3),sp
	jmp	([env_ExitFunction,a3])

select$:

	; This menu option emulates the Select button from the Atari 2600
	; console. It usually selects game options before play begins.

	bclr.b	#SWCHBB_NOSELECT,([env_Data,a3],A2600_SWCHB)
	bclr.b	#SWCHBB_NOSELECT,([env_Data,a3],A2600_SWCHB+$100)
	bra	next_msg$

reset$:

	; This menu option emulates the Reset button from the Atari 2600
	; console. It usually starts the game's play mode.

	bclr.b	#SWCHBB_NORESET,([env_Data,a3],A2600_SWCHB)
	bclr.b	#SWCHBB_NORESET,([env_Data,a3],A2600_SWCHB+$100)
	bra	next_msg$

colour$:

	; Get the address of this menu item, which emulates the Colour/B&W
	; switch.

	movea.l	(ab_IntuitionLib,a5),a6
	move.l	d3,d0
	movea.l	(env_Menus,a3),a0
	JSRLIB	ItemAddress
	movea.l	d0,a0

	; See whether new state is checked or unchecked

	move.w	(mi_Flags,a0),d0
	andi.w	#CHECKED,d0
	beq	black_and_white$

	bset.b	#SWCHBB_COLOUR,([env_Data,a3],A2600_SWCHB)
	bset.b	#SWCHBB_COLOUR,([env_Data,a3],A2600_SWCHB+$100)
	bra	next_msg$

black_and_white$:

	bclr.b	#SWCHBB_COLOUR,([env_Data,a3],A2600_SWCHB)
	bclr.b	#SWCHBB_COLOUR,([env_Data,a3],A2600_SWCHB+$100)
	bra	next_msg$

p0_difficulty$:

	; Get the address of this menu item, which emulates the first
	; player's difficulty switch.

	movea.l	(ab_IntuitionLib,a5),a6
	move.l	d3,d0
	movea.l	(env_Menus,a3),a0
	JSRLIB	ItemAddress
	movea.l	d0,a0

	; See whether new state is checked or unchecked

	move.w	(mi_Flags,a0),d0
	andi.w	#CHECKED,d0
	beq	difficult_p0$

	bclr.b	#SWCHBB_P0PRO,([env_Data,a3],A2600_SWCHB)
	bclr.b	#SWCHBB_P0PRO,([env_Data,a3],A2600_SWCHB+$100)
	bra	next_msg$

difficult_p0$:

	bset.b	#SWCHBB_P0PRO,([env_Data,a3],A2600_SWCHB)
	bset.b	#SWCHBB_P0PRO,([env_Data,a3],A2600_SWCHB+$100)
	bra	next_msg$

joystick_action$:

	cmpi.w	#JOY_CODE,d2
	bne	next_msg$

	cmpi.b	#JOYSTICK,(env_ControllerTypes,a3)
	bne	next_msg$

	moveq	#0,d0

	btst.w	#IEQUALIFIERB_RSHIFT,d4
	bne	left$
	bset.b	#SWCHAB_P0RIGHT,d0

left$:
	btst.w	#IEQUALIFIERB_LSHIFT,d4
	bne	down$
	bset.b	#SWCHAB_P0LEFT,d0

down$:
	btst.w	#IEQUALIFIERB_RALT,d4
	bne	up$
	bset.b	#SWCHAB_P0DOWN,d0

up$:
	btst.w	#IEQUALIFIERB_LALT,d4
	bne	save_joystick$
	bset.b	#SWCHAB_P0UP,d0

save_joystick$:

	ori.b	#$0f,d0
	move.b	d0,([env_Data,a3],A2600_SWCHA)
	move.b	d0,([env_Data,a3],A2600_SWCHA+$100)

	btst.w	#IEQUALIFIERB_LEFTBUTTON,d4
	bne	firing$

	movea.l	(env_Data,a3),a0
	moveq	#$70,d0

no_fire_loop$:

	move.b	#$ff,(A2600_INPT4,a0,d0)
	subi.l	#$10,d0
	bpl	no_fire_loop$

	bra	not_firing$

firing$:

	movea.l	(env_Data,a3),a0
	moveq	#$70,d0

fire_loop$:

	move.b	#$7f,(A2600_INPT4,a0,d0)
	subi.l	#$10,d0
	bpl	fire_loop$

not_firing$:

	bra	next_msg$

mouse_button_action$:

	; Check if this is a paddles game

	cmpi.b	#PADDLES,(env_ControllerTypes,a3)
	bne	next_msg$

	; Record any change in state of the left mouse button

	movea.l	(env_Data,a3),a0

	cmpi.w	#SELECTDOWN,d2
	bne.s	no_mouse_down$
	move.b	#$7f,d0
	move.b	#$7f,(A2600_SWCHA,a0)
	move.b	#$7f,(A2600_SWCHA+$100,a0)

no_mouse_down$:

	cmpi.w	#SELECTUP,d2
	bne.s	no_mouse_up$
	move.b	#$ff,(A2600_SWCHA,a0)
	move.b	#$ff,(A2600_SWCHA+$100,a0)

no_mouse_up$:

	bra	next_msg$

new_mouse_position$:

	; Record new horizontal mouse position

	tst.w	d5
	bpl	mouse_high_enough$
	clr.w	d5

mouse_high_enough$:

	cmpi.w	#A2600_TV_WIDTH*X_MAG,d5
	blo	mouse_low_enough$
	move.w	#A2600_TV_WIDTH*X_MAG-1,d5

mouse_low_enough$:

	move.w	#A2600_TV_WIDTH*X_MAG,d0
	sub.w	d5,d0
	move.w	d0,(env_MouseX,a3)

	bra	next_msg$

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a6
	rts



;****i* atari2600.library/UpdatePatternList ***
;
;   NAME
;	UpdatePatternList -- .
;
;   SYNOPSIS
;	UpdatePatternList(object,position,env)
;	                  d0     d1       a1
;
;   FUNCTION
;
;   INPUTS
;	object - The object whose pattern is to be updated.
;	position - The current position in the pattern.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;   NOTES
;	Preserves d0, d1 and a1.
;
;***
;
;

UpdatePatternList:
;   rts
	; Push registers that should be preserved onto stack

	move.l	d2,-(sp)

	; Check if vertical sync is on

	tst.w	(env_VerticalSync,a1)
	bne	end$

	; Check if new settings are different from previous settings

	movea.l	(env_PatternListPtrs,a1,d0*4),a0

	move.l	(env_Settings,a1,d0*4),d2
	cmp.l	(a0),d2
	beq.s	replace$

	; Check new settings don't come into effect before or at the same
	; time as old settings.

	cmp.l	(4,a0),d1
	bhi.s	new_time_is_later$
	move.l	d1,(4,a0)
	bra.s	end$

new_time_is_later$:

	; Check if list is full

	cmpa.l	(env_PatternListLimits,a1,d0*4),a0
	bhs	end$

	; Move on to next position in list

	addq.l	#8,(env_PatternListPtrs,a1,d0*4)
	addq.l	#8,a0

replace$:

	; Store object-settings and update-point pair

	move.l	d2,(a0)+
	move.l	d1,(a0)

end$:

	; Pop preserved registers off stack and return

	move.l	(sp)+,d2
	rts



;****i* atari2600.library/CalculateCurrentPosition ***
;
;   NAME
;	CalculateCurrentPosition -- .
;
;   SYNOPSIS
;	position = CalculateCurrentPosition(colour_clock,env)
;	d0                                  d1           a1
;
;   FUNCTION
;
;   INPUTS
;	colour_clock - The number of emulated colour clock cycles since the
;	    previous call to WSync().
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	position - The current position in the pattern.
;
;   NOTES
;	Preserves a1.
;
;***
;
;

CalculateCurrentPosition:

	; Calculate current line number

	divu.w	#A2600_LINE_CYCLES,d1

	; Calculate visible line number

	sub.w	(env_StartingLineNo,a1),d1

	; Ensure visible line number isn't negative

	bhs	high_enough$

	moveq	#0,d1
	bra	end$

high_enough$:

	; Ensure visible line number isn't too big

	cmp.w	(env_TVHeight,a1),d1
	bmi	low_enough$

	move.l	(env_TVHeight,a1),d1
	bra	end$

low_enough$:

	; Calculate current horizontal position

	swap.w	d1
	move.b	(horizontal_position_table.l,d1.w),d1

end$:

	; Return position

	move.l	d1,d0

	rts



;****i* atari2600.library/FillPattern ***
;
;   NAME
;	FillPattern -- .
;
;   SYNOPSIS
;	FillPattern(object,env)
;	            d0     a1
;
;   FUNCTION
;
;   INPUTS
;	object - the number of the object whose pattern is to be filled.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

FillPattern:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a6,-(sp)

	; Store parameters

	move.l	d0,d5
	movea.l	a1,a3

	; Add final entry to current pattern list

	movea.l	(env_PatternListPtrs,a3,d5*4),a1
	addq.l	#8,a1
	move.l	#OBJF_VBLANK,(a1)+
	move.l	(env_TVHeight,a3),(a1)

	; Load starting address of old and new pattern lists

	lea.l	(env_PatternLists,a3),a0
	move.l	d5,d0
	lsl.l	#4,d0
	adda.l	d0,a0

	movea.l	(a0)+,a2
	movea.l	(a0),a4

	; Initialise loop

	moveq	#0,d3

loop$:

	; Get the current patterns from both lists

	move.l	(a2),d1
	move.l	(a4),d4

	; See whether the next change is in the old list or the new list

	move.l	(4,a2),d6
	move.l	(4,a4),d7

	cmp.l	d7,d6
	bmi	use_new_element$
	bne	not_same$

	; Move new list on to next element

	addq.l	#8,a2

not_same$:

	; Consume an element from the old list

	move.l	d7,d2
	addq.l	#8,a4

	bra	skip_use_new_element$

use_new_element$:

	; Consume an element from the new list

	move.l	d6,d2
	addq.l	#8,a2

skip_use_new_element$:

	; See if the new pattern for the current section is different from
	; the old one.

	cmp.l	d4,d1
	beq	do_not_fill$

	; Construct the pattern line for the next section

	movea.l	a3,a1
	movea.l	(pattern_line_function_table.l,d5*4),a0
	jsr	(a0)

	; Fill in the current section of the pattern

	move.l	d3,d1

	move.l	d5,d0
	movea.l	a3,a1
	bsr	FillPatternSection

do_not_fill$:

	; Store new base position

	move.l	d2,d3

	; Check that new base is less than or equal to the maximum allowable

	cmp.l	(env_TVHeight,a3),d3
	bmi	loop$

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a6

	rts



;****i* atari2600.library/FindColourDifferences ***
;
;   NAME
;	FindColourDifferences -- .
;
;   SYNOPSIS
;	FindColourDifferences(pattern_no,env)
;	                      d0         a1
;
;   FUNCTION
;
;   INPUTS
;	pattern_no - the number of the pattern whose difference pattern is
;	    to be filled.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

FindColourDifferences:

	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a6,-(sp)

	; Store parameters

	move.l	d0,d5
	movea.l	a1,a3

	; Clear difference pattern

	movea.l	(env_DifferencePatterns-REDUCED_OBJECT_COUNT*4,a3,d5*4),a0

	move.w	(env_TVHeight,a3),d0
	move.w	d0,d1
	lsr.w	#2,d0
	andi.w	#3,d1

	bra	longword_loop_start$

longword_loop$:

	clr.l	(a0)+

longword_loop_start$:

	dbra	d0,longword_loop$

	bra	byte_loop_start$

byte_loop$:

	clr.b	(a0)+

byte_loop_start$:

	dbra	d1,byte_loop$

	; Load starting address of old and new pattern lists

	lea.l	(env_PatternLists,a3),a0
	move.l	d5,d0
	lsl.l	#4,d0
	adda.l	d0,a0

	movea.l	(a0),a2
	movea.l	(SCREEN_BUFFER_COUNT*4,a0),a4

	; Initialise loop

	moveq	#0,d3

loop$:

	; Get the current patterns from both lists

	move.l	(a2),d1
	move.l	(a4),d4

	; See whether the next change is in the old list or the new list

	move.l	(4,a2),d6
	move.l	(4,a4),d7

	cmp.l	d7,d6
	bmi	use_new_element$
	bne	not_same$

	; Move new list forward to next element

	addq.l	#8,a2

not_same$:

	; Consume an element from the old list

	move.l	d7,d2
	addq.l	#8,a4

	bra	skip_use_new_element$

use_new_element$:

	; Consume an element from the new list

	move.l	d6,d2
	addq.l	#8,a2

skip_use_new_element$:

	; See if the new pattern for the current section is different from
	; the old one.

	cmp.l	d4,d1
	beq	do_not_fill$

	; Fill in the current section of the difference pattern

	move.l	d3,d1

	move.l	d5,d0
	movea.l	a3,a1
	bsr	FillDifferenceSection

do_not_fill$:

	; Store new base position

	move.l	d2,d3

	; Check that new base is less than or equal to the maximum allowable

	cmp.l	(env_TVHeight,a3),d3
	bmi	loop$

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a6

	rts



;****i* atari2600.library/FindDifferences ***
;
;   NAME
;	FindDifferences -- .
;
;   SYNOPSIS
;	FindDifferences(object,env)
;	                d0     a1
;
;   FUNCTION
;
;   INPUTS
;	object - the number of the object whose pattern is to be filled.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

FindDifferences:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2-d7/a2-a6,-(sp)

	; Store parameters

	move.l	d0,d5
	movea.l	a1,a3

	; Load starting address of old and new patterns

	lea.l	(env_MergedPatterns,a3),a2
	move.l	d5,d0
	lsl.l	#4,d0
	adda.l	d0,a2

	movea.l	(a2),a1
	movea.l	(SCREEN_BUFFER_COUNT*4,a2),a0

	; Load address of difference pattern

	movea.l	(env_DifferencePatterns,a3,d5*4),a2

	; Initialise line loop

	move.w	(env_TVHeight,a3),d3
	subq.w	#1,d3

next_line$:

	; Initialise longword loop

	moveq	#BYTES_PER_LINE/4-1,d5
	moveq	#0,d2

next_longword$:

	; Check if the old pattern segment is different to the new one

	cmpm.l	(a0)+,(a1)+
	beq	same_pattern$

	bset.l	d5,d2

same_pattern$:

	dbra	d5,next_longword$

	move.b	d2,(a2)+
	dbra	d3,next_line$

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d7/a2-a6

	rts



;****i* atari2600.library/FillDifferenceSection ***
;
;   NAME
;	FillDifferenceSection -- Fill part of an object pattern.
;
;   SYNOPSIS
;	FillDifferenceSection(object,start_point,end_point,env)
;	                      d0     d1          d2        a1
;
;   FUNCTION
;
;   INPUTS
;	object - The object whose pattern is to be updated.
;	start_point - A valid display position.
;	end_point - A valid display position.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

FillDifferenceSection:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2-d3,-(sp)

	; See if start and end points are the same

	cmp.l	d1,d2
	beq	end$

	; Get address of starting byte in difference pattern

	movea.l	(env_DifferencePatterns-REDUCED_OBJECT_COUNT*4,a1,d0*4),a0

	move.l	d1,d0
	clr.w	d0
	swap.w	d0
	adda.l	d0,a0

	; See if the new update point is on a later line than the old one

	move.l	d2,d3
	swap.w	d3
	move.l	d1,d0
	swap.w	d0
	cmp.w	d0,d3
	bne	multiple_lines$

	; Reduce horizontal coordinates from pixels to longwords

	lsr.w	#5,d1	
	lsr.w	#5,d2

	; See if fill starts and stops on the same longword

	cmp.w	d1,d2
	bne	multiple_longwords$

	; Set relevant bit in the line's byte

	moveq	#4,d0
	sub.w	d1,d0
	bset.b	d0,(a0)

	bra	end$

multiple_longwords$:

	; Set bits representing longwords in the range

	sub.w	d1,d2
	addq.w	#1,d2
	add.w	#27,d1
	move.b	(a0),d0
	bfset.l	d0{d1:d2}
	move.b	d0,(a0)

	bra	end$

multiple_lines$:

	; Set relevant bits representing longwords on first line

	moveq	#5,d0
	lsr.w	#5,d1
	sub.w	d1,d0
	add.w	#27,d1
	move.b	(a0),d3
	bfset.l	d3{d1:d0}
	move.b	d3,(a0)+

	; Fill any complete lines in the range

	move.l	d2,d0
	swap.w	d0
	swap.w	d1

	sub.w	d1,d0
	subq.w	#1,d0

	bra	line_loop_start$

next_line$:

	move.b	#%11111,(a0)+

line_loop_start$:

	dbra	d0,next_line$

	; Check if there are any bits to be filled in last line

	tst.w	d2
	beq.s	end$

	; Set bits for part of last line in the range

	lsr.w	#5,d2
	addq.w	#1,d2
	move.b	(a0),d0
	bfset	d0{27:d2}
	move.b	d0,(a0)

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3

	rts



;****i* atari2600.library/GetTime ***
;
;   NAME
;	GetTime -- Get current time in milliseconds.
;
;   SYNOPSIS
;	milliseconds = GetTime()
;	d0
;
;   FUNCTION
;
;   INPUTS
;
;   RESULT
;	milliseconds - The current time in milliseconds.
;
;***
;
;

GetTime:

	; Push registers that should be preserved onto stack

	move.l	a6,-(sp)

	; Get current time in milliseconds

	movea.l	(ab_IntuitionLib,a6),a6
	subq.l	#4,sp
	movea.l	sp,a0
	subq.l	#4,sp
	movea.l	sp,a1
	JSRLIB	CurrentTime

	move.l	(sp)+,d0
	move.l	(sp)+,d1

	divu.w	#1000,d0
	andi.l	#$ffff,d0
	mulu.l	#1000,d1
	add.l	d0,d1

	; Restore preserved registers and return time in milliseconds

	move.l	d1,d0
	movea.l	(sp)+,a6
	rts



;****i* atari2600.library/HexToULong ***
;
;   NAME
;	HexToULong -- Convert a hexadecimal string to a ULONG.
;
;   SYNOPSIS
;	success = HexToULong(string,value)
;	d0                   a0     a1
;
;   FUNCTION
;
;   INPUTS
;	string - The string to be converted.
;	value - Pointer to a ULONG in which the result should be placed.
;
;   RESULT
;	success - A boolean value to indicate whether a conversion could be
;	    made.
;
;***
;
;

HexToULong:

	; Initialise loop

	moveq	#0,d0
	moveq	#0,d1

	; Check for empty string

	tst.b	(a0)
	beq	failure$

loop$:

	; Check for end of string

	move.b	(a0)+,d1
	beq	success$


	; Check for lowercase hex digit

	cmpi.b	#'f',d1
	bhi.s	failure$
	cmpi.b	#'a',d1
	blo.s	skip_lower$

	subi.b	#'a'-10,d1
	bra.s	add_on$

skip_lower$:

	; Check for uppercase hex digit

	cmp.b	#'F',d1
	bhi.s	failure$
	cmp.b	#'A',d1
	blo.s	skip_upper$

	subi.b	#'A'-10,d1
	bra.s	add_on$

skip_upper$:

	; Check for decimal digit

	cmp.b	#'9',d1
	bhi.s	failure$
	cmp.b	#'0',d1
	blo.s	failure$

	subi.b	#'0',d1

add_on$:

	; Add on current digit and loop to get next digit

	lsl.l	#4,d0
	add.l	d1,d0
	bra	loop$

failure$:

	; Indicate failure

	moveq	#0,d0
	bra	end$

success$:

	; Store converted value and indicate success

	move.l	d0,(a1)
	moveq	#1,d0

end$:

	; Return

	rts



;****i* atari2600.library/ULongToHex ***
;
;   NAME
;	ULongToHex -- Convert a ULONG to a hexadecimal string.
;
;   SYNOPSIS
;	ULongToHex(value,string)
;	           d0    a0
;
;   FUNCTION
;
;   INPUTS
;	value - The number to be converted to a string.
;	string - A pointer to at least 9 bytes to store the resultant string
;	    in.
;
;   RESULT
;	None.
;
;***
;
;

ULongToHex:

	; Zero is a special case

	tst.l	d0
	bne.s	not_zero$

	move.b	#'0',(a0)+
	bra	end$

not_zero$:

	; Initialise loop and discard leading zeros

	bfffo	d0{0:32},d1
	andi.b	#~%11,d1
	ror.l	#4,d0
	rol.l	d1,d0
	lsr.b	#2,d1
	movea.l	d0,a1
	move.b	d1,d0
	moveq	#8-1,d1
	sub.b	d0,d1

loop$:

	; Output digits

	move.l	a1,d0
	rol.l	#4,d0
	movea.l	d0,a1
	bfextu	d0{0:4},d0
	move.b	(hex_digit_table.l,d0),(a0)+
	dbra	d1,loop$

end$:

	; Terminate string

	move.b	#'\0',(a0)+

	; Return

	rts



;****i* atari2600.library/Substring ***
;
;   NAME
;	Substring -- Determine if a string is a substring of another.
;
;   SYNOPSIS
;	found = Substring(string,substring)
;	d0                a0     a1
;
;   FUNCTION
;	Determines if a string is a case-insensitive substring of another.
;
;   INPUTS
;	string - The string to be searched.
;	substring - The possible substring.
;
;   RESULT
;	found - A boolean value indicating whether one string was a
;	    substring.
;
;***
;
;

Substring:
;   rts
	; Push registers that should be preserved onto stack

	movem.l	d2-d3/a2-a3/a6,-(sp)

	; Store parameters

	movea.l	a0,a2
	movea.l	a1,a3

	; Get length of string

	moveq	#-1,d2

get_string_length$:

	addq.l	#1,d2
	tst.b	(a0)+
	bne	get_string_length$

	; Get length of substring

	moveq	#-1,d3

get_substring_length$:

	addq.l	#1,d3
	tst.b	(a1)+
	bne	get_substring_length$

	; Check if substring is longer than string

	sub.l	d3,d2
	bmi	not_found$

	; Search for the substring

	movea.l	(ab_UtilityLib,a6),a6

search$:

	movea.l	a2,a0
	movea.l	a3,a1
	move.l	d3,d0
	JSRLIB	Strnicmp
	tst.l	d0
	beq	found$
	addq.l	#1,a2
	dbra	d2,search$

not_found$:

	moveq	#0,d0
	bra	end$

found$:

	moveq	#1,d0

end$:

	; Pop preserved registers off stack and return

	movem.l	(sp)+,d2-d3/a2-a3/a6
	rts



;****i* atari2600.library/UpdatePaddleCharge ***
;
;   NAME
;	UpdatePaddleCharge -- Update the charge of the paddles.
;
;   SYNOPSIS
;	UpdatePaddleCharge(clock,env)
;	                   d1    a1
;
;   FUNCTION
;	Sets highest bit in each paddle register if the paddle's capacitor
;	should have charged up by now.
;
;   INPUTS
;	clock - The number of emulated clock cycles since the frame began.
;	env - The Atari 2600 environment involved.
;
;   RESULT
;	None.
;
;***
;
;

UpdatePaddleCharge:

	; Check if this is a paddles game

	cmpi.b	#PADDLES,(env_ControllerTypes,a1)
	bne	end$

	; Do nothing if paddle is already charged

	tst.w	(env_PaddlesCharged,a1)
	bne	end$

	; Get clock cycles since last update and store clock cycles for this
	; update.

	move.l	d1,d0
	sub.l	(env_ClockAtPaddleUpdate,a1),d1
	move.l	d0,(env_ClockAtPaddleUpdate,a1)

	; Check if pots are dumped to ground

	tst.w	(env_PotsDumped,a1)
	bne	end$

	; Add product of time and current to charge

	move.l	#$ffff,d0
	divu.w	(env_MouseX,a1),d0
	andi.l	#$ffff,d0

	mulu.l	d0,d1
	move.l	(env_PaddleCharges,a1),d0
	add.l	d1,d0
	move.l	d0,(env_PaddleCharges,a1)

	; Check if paddle has charged up

	cmpi.l	#PADDLE_CHARGE_THRESHOLD,d0
	blo	end$

	; Record paddle as having charged up

	move.w	#1,(env_PaddlesCharged,a1)

	movea.l	(env_Data,a1),a0
	moveq	#$70,d0

paddle_charged_loop$:

	move.b	#$ff,(A2600_INPT0,a0,d0)
	subi.l	#$10,d0
	bpl	paddle_charged_loop$

end$:

	; Return

	rts



; Data:
; =====
; Static data used by library.

;	data

screen_tags:

	dc.l	SA_Width,TV_WIDTH*X_MAG
	dc.l	SA_Height,0
	dc.l	SA_Depth,SCREEN_DEPTH
	dc.l	SA_DetailPen,5
	dc.l	SA_BlockPen,4
	dc.l	SA_SysFont,1
	dc.l	SA_Font,font_spec
	dc.l	SA_Type,CUSTOMSCREEN|CUSTOMBITMAP
	dc.l	SA_BitMap,0
	dc.l	SA_DisplayID,0
	dc.l	SA_ShowTitle,1
	dc.l	SA_Pens,pen_list
	dc.l	SA_Colors32,colour_table
	dc.l	SA_Title,0
	dc.l	TAG_END,0

pen_list:

	dc.w	0	; DETAILPEN
	dc.w	7	; BLOCKPEN
	dc.w	0	; TEXTPEN
	dc.w	7	; SHINEPEN
	dc.w	0	; SHADOWPEN
	dc.w	$0e	; FILLPEN
	dc.w	0	; FILLTEXTPEN
	dc.w	$03	; BACKGROUNDPEN
	dc.w	2	; HIGHLIGHTTEXTPEN
	dc.w	0	; BARDETAILPEN
	dc.w	7	; BARBLOCKPEN
	dc.w	0	; BARTRIMPEN
	dc.w	-1	; end of list

font_spec:

	dc.l	font_name
	dc.w	8
	dc.b	FS_NORMAL
	dc.b	FPF_ROMFONT

font_name:

	dc.b	"topaz.font",0


	cnop	0,4

colour_table:
ntsc_colours:

	dc.w	128,0
	dc.l	$00000000,$00000000,$00000000
	dc.l	$c8888888,$51111111,$22222222	;$39999999,$39999999,$39999999
	dc.l	$79999999,$79999999,$79999999
	dc.l	$abbbbbbb,$abbbbbbb,$abbbbbbb
	dc.l	$cddddddd,$cddddddd,$cddddddd
	dc.l	$ffffffff,$81111111,$1eeeeeee	;$e6666666,$e6666666,$e6666666
	dc.l	$f2222222,$f2222222,$f2222222
	dc.l	$ffffffff,$ffffffff,$ffffffff
	dc.l	$39999999,$17777777,$01111111
	dc.l	$83333333,$30000000,$08888888
	dc.l	$c8888888,$5fffffff,$24444444
	dc.l	$ffffffff,$91111111,$1ddddddd
	dc.l	$ffffffff,$c5555555,$1ddddddd
	dc.l	$ffffffff,$d8888888,$4ccccccc
	dc.l	$ffffffff,$f4444444,$56666666
	dc.l	$ffffffff,$ffffffff,$98888888
	dc.l	$45555555,$19999999,$04444444
	dc.l	$faaaaaaa,$52222222,$55555555	;$9fffffff,$24444444,$1eeeeeee
	dc.l	$39999999,$39999999,$39999999	;$c8888888,$51111111,$22222222
	dc.l	$e6666666,$e6666666,$e6666666	;$ffffffff,$81111111,$1eeeeeee
	dc.l	$ffffffff,$98888888,$2ccccccc
	dc.l	$ffffffff,$c5555555,$45555555
	dc.l	$ffffffff,$c6666666,$6ddddddd
	dc.l	$ffffffff,$e4444444,$a1111111
	dc.l	$4aaaaaaa,$17777777,$04444444
	dc.l	$b2222222,$1ddddddd,$17777777
	dc.l	$dfffffff,$25555555,$1ccccccc
	dc.l	$9fffffff,$24444444,$1eeeeeee	;$faaaaaaa,$52222222,$55555555
	dc.l	$ffffffff,$70000000,$6eeeeeee
	dc.l	$ffffffff,$8fffffff,$8fffffff
	dc.l	$ffffffff,$abbbbbbb,$addddddd
	dc.l	$ffffffff,$c7777777,$ceeeeeee
	dc.l	$05555555,$05555555,$68888888
	dc.l	$71111111,$22222222,$72222222
	dc.l	$a5555555,$32222222,$a6666666
	dc.l	$cddddddd,$3eeeeeee,$cfffffff
	dc.l	$eaaaaaaa,$51111111,$ebbbbbbb
	dc.l	$feeeeeee,$6ddddddd,$ffffffff
	dc.l	$ffffffff,$87777777,$fbbbbbbb
	dc.l	$ffffffff,$a4444444,$ffffffff
	dc.l	$28888888,$04444444,$79999999
	dc.l	$59999999,$0fffffff,$90000000
	dc.l	$88888888,$39999999,$aaaaaaaa
	dc.l	$c0000000,$4aaaaaaa,$dccccccc
	dc.l	$e0000000,$5eeeeeee,$ffffffff
	dc.l	$f2222222,$7ccccccc,$ffffffff
	dc.l	$ffffffff,$98888888,$ffffffff
	dc.l	$feeeeeee,$abbbbbbb,$ffffffff
	dc.l	$35555555,$08888888,$8aaaaaaa
	dc.l	$50000000,$0ccccccc,$d0000000
	dc.l	$79999999,$45555555,$d0000000
	dc.l	$a2222222,$51111111,$d9999999
	dc.l	$beeeeeee,$60000000,$ffffffff
	dc.l	$cccccccc,$77777777,$ffffffff
	dc.l	$d7777777,$90000000,$ffffffff
	dc.l	$dfffffff,$aaaaaaaa,$ffffffff
	dc.l	$05555555,$1eeeeeee,$81111111
	dc.l	$08888888,$2fffffff,$caaaaaaa
	dc.l	$44444444,$4ccccccc,$deeeeeee
	dc.l	$5aaaaaaa,$68888888,$ffffffff
	dc.l	$71111111,$83333333,$ffffffff
	dc.l	$90000000,$a0000000,$ffffffff
	dc.l	$9fffffff,$b2222222,$ffffffff
	dc.l	$c0000000,$cbbbbbbb,$ffffffff
	dc.l	$0ccccccc,$04444444,$8bbbbbbb
	dc.l	$38888888,$2ddddddd,$b5555555
	dc.l	$58888888,$4fffffff,$daaaaaaa
	dc.l	$6bbbbbbb,$64444444,$ffffffff
	dc.l	$8aaaaaaa,$84444444,$ffffffff
	dc.l	$99999999,$98888888,$ffffffff
	dc.l	$b1111111,$aeeeeeee,$ffffffff
	dc.l	$c0000000,$c2222222,$ffffffff
	dc.l	$1ddddddd,$29999999,$5aaaaaaa
	dc.l	$1ddddddd,$48888888,$92222222
	dc.l	$1ccccccc,$71111111,$c6666666
	dc.l	$48888888,$9bbbbbbb,$d9999999
	dc.l	$55555555,$b6666666,$ffffffff
	dc.l	$8ccccccc,$d8888888,$ffffffff
	dc.l	$9bbbbbbb,$dfffffff,$ffffffff
	dc.l	$c3333333,$e9999999,$ffffffff
	dc.l	$2fffffff,$43333333,$02222222
	dc.l	$44444444,$61111111,$03333333
	dc.l	$3eeeeeee,$94444444,$21111111
	dc.l	$57777777,$abbbbbbb,$3bbbbbbb
	dc.l	$61111111,$d0000000,$70000000
	dc.l	$72222222,$f5555555,$84444444
	dc.l	$87777777,$ffffffff,$97777777
	dc.l	$addddddd,$ffffffff,$b6666666
	dc.l	$0aaaaaaa,$41111111,$08888888
	dc.l	$10000000,$68888888,$0ddddddd
	dc.l	$16666666,$92222222,$12222222
	dc.l	$1ccccccc,$b9999999,$17777777
	dc.l	$21111111,$d9999999,$1bbbbbbb
	dc.l	$6eeeeeee,$f0000000,$40000000
	dc.l	$83333333,$ffffffff,$5bbbbbbb
	dc.l	$b2222222,$ffffffff,$9aaaaaaa
	dc.l	$04444444,$41111111,$0bbbbbbb
	dc.l	$06666666,$66666666,$11111111
	dc.l	$08888888,$88888888,$17777777
	dc.l	$0bbbbbbb,$afffffff,$1ddddddd
	dc.l	$86666666,$d9999999,$22222222
	dc.l	$99999999,$f9999999,$27777777
	dc.l	$b7777777,$ffffffff,$5bbbbbbb
	dc.l	$dccccccc,$ffffffff,$81111111
	dc.l	$02222222,$35555555,$0fffffff
	dc.l	$0ccccccc,$4aaaaaaa,$1ccccccc
	dc.l	$4fffffff,$74444444,$20000000
	dc.l	$64444444,$92222222,$28888888
	dc.l	$a1111111,$b0000000,$34444444
	dc.l	$b2222222,$d2222222,$41111111
	dc.l	$d6666666,$e1111111,$49999999
	dc.l	$f2222222,$ffffffff,$53333333
	dc.l	$26666666,$30000000,$01111111
	dc.l	$23333333,$40000000,$05555555
	dc.l	$80000000,$69999999,$31111111
	dc.l	$afffffff,$99999999,$3aaaaaaa
	dc.l	$d5555555,$b5555555,$43333333
	dc.l	$e1111111,$cbbbbbbb,$38888888
	dc.l	$e3333333,$e5555555,$34444444
	dc.l	$fbbbbbbb,$ffffffff,$7ddddddd
	dc.l	$40000000,$1aaaaaaa,$02222222
	dc.l	$70000000,$24444444,$08888888
	dc.l	$abbbbbbb,$51111111,$1fffffff
	dc.l	$bfffffff,$77777777,$30000000
	dc.l	$e1111111,$93333333,$44444444
	dc.l	$f9999999,$addddddd,$58888888
	dc.l	$ffffffff,$c1111111,$60000000
	dc.l	$ffffffff,$cbbbbbbb,$83333333
	dc.w	0

copyright_notice:

	dc.b	"Copyright © 1999-2001 Neil Cafferkey",0

	cnop	0,4

pal_colours:

	dc.w	128,0
	dc.l	$00000000,$00000000,$00000000
	dc.l	$24444444,$24444444,$24444444
	dc.l	$48888888,$48888888,$48888888
	dc.l	$6ddddddd,$6ddddddd,$6ddddddd
	dc.l	$91111111,$91111111,$91111111
	dc.l	$b6666666,$b6666666,$b6666666
	dc.l	$daaaaaaa,$daaaaaaa,$daaaaaaa
	dc.l	$ffffffff,$ffffffff,$ffffffff
	dc.l	$00000000,$00000000,$00000000
	dc.l	$24444444,$24444444,$24444444
	dc.l	$48888888,$48888888,$48888888
	dc.l	$6ddddddd,$6ddddddd,$6ddddddd
	dc.l	$91111111,$91111111,$91111111
	dc.l	$b6666666,$b6666666,$b6666666
	dc.l	$daaaaaaa,$daaaaaaa,$daaaaaaa
	dc.l	$ffffffff,$ffffffff,$ffffffff
	dc.l	$4aaaaaaa,$37777777,$00000000
	dc.l	$70000000,$58888888,$13333333
	dc.l	$8ccccccc,$73333333,$2aaaaaaa
	dc.l	$a6666666,$8ddddddd,$46666666
	dc.l	$beeeeeee,$a7777777,$67777777
	dc.l	$d4444444,$c1111111,$8bbbbbbb
	dc.l	$eaaaaaaa,$dccccccc,$b3333333
	dc.l	$ffffffff,$f6666666,$deeeeeee
	dc.l	$28888888,$4aaaaaaa,$00000000
	dc.l	$44444444,$70000000,$0fffffff
	dc.l	$5ccccccc,$8ccccccc,$21111111
	dc.l	$74444444,$a6666666,$38888888
	dc.l	$8ccccccc,$beeeeeee,$51111111
	dc.l	$a6666666,$d4444444,$6eeeeeee
	dc.l	$c0000000,$eaaaaaaa,$8eeeeeee
	dc.l	$dbbbbbbb,$ffffffff,$b0000000
	dc.l	$4aaaaaaa,$13333333,$00000000
	dc.l	$70000000,$28888888,$0fffffff
	dc.l	$8ccccccc,$3ddddddd,$21111111
	dc.l	$a6666666,$54444444,$38888888
	dc.l	$beeeeeee,$6ddddddd,$51111111
	dc.l	$d4444444,$88888888,$6eeeeeee
	dc.l	$eaaaaaaa,$a5555555,$8eeeeeee
	dc.l	$ffffffff,$c4444444,$b0000000
	dc.l	$00000000,$4aaaaaaa,$22222222
	dc.l	$0fffffff,$70000000,$3bbbbbbb
	dc.l	$21111111,$8ccccccc,$52222222
	dc.l	$38888888,$a6666666,$6aaaaaaa
	dc.l	$51111111,$beeeeeee,$83333333
	dc.l	$6eeeeeee,$d4444444,$9ddddddd
	dc.l	$8eeeeeee,$eaaaaaaa,$b8888888
	dc.l	$b0000000,$ffffffff,$d4444444
	dc.l	$4aaaaaaa,$00000000,$28888888
	dc.l	$70000000,$0fffffff,$44444444
	dc.l	$8ccccccc,$21111111,$5ccccccc
	dc.l	$a6666666,$38888888,$74444444
	dc.l	$beeeeeee,$51111111,$8ccccccc
	dc.l	$d4444444,$6eeeeeee,$a6666666
	dc.l	$eaaaaaaa,$8eeeeeee,$c0000000
	dc.l	$ffffffff,$b0000000,$dbbbbbbb
	dc.l	$00000000,$40000000,$4aaaaaaa
	dc.l	$0fffffff,$63333333,$70000000
	dc.l	$21111111,$7eeeeeee,$8ccccccc
	dc.l	$38888888,$97777777,$a6666666
	dc.l	$51111111,$afffffff,$beeeeeee
	dc.l	$6eeeeeee,$c7777777,$d4444444
	dc.l	$8eeeeeee,$deeeeeee,$eaaaaaaa
	dc.l	$b0000000,$f4444444,$ffffffff
	dc.l	$43333333,$00000000,$2ccccccc
	dc.l	$65555555,$0fffffff,$4bbbbbbb
	dc.l	$7eeeeeee,$21111111,$65555555
	dc.l	$95555555,$38888888,$80000000
	dc.l	$a6666666,$51111111,$9aaaaaaa
	dc.l	$bfffffff,$6eeeeeee,$b7777777
	dc.l	$d3333333,$8eeeeeee,$d3333333
	dc.l	$e5555555,$b0000000,$f1111111
	dc.l	$00000000,$1ddddddd,$4aaaaaaa
	dc.l	$0fffffff,$38888888,$70000000
	dc.l	$21111111,$53333333,$8ccccccc
	dc.l	$38888888,$6eeeeeee,$a6666666
	dc.l	$51111111,$8ddddddd,$beeeeeee
	dc.l	$6eeeeeee,$a8888888,$d4444444
	dc.l	$8eeeeeee,$c8888888,$eaaaaaaa
	dc.l	$b0000000,$e9999999,$ffffffff
	dc.l	$37777777,$00000000,$4aaaaaaa
	dc.l	$57777777,$0fffffff,$70000000
	dc.l	$70000000,$21111111,$8ccccccc
	dc.l	$89999999,$38888888,$a6666666
	dc.l	$a1111111,$51111111,$beeeeeee
	dc.l	$baaaaaaa,$6eeeeeee,$d4444444
	dc.l	$d2222222,$8eeeeeee,$eaaaaaaa
	dc.l	$eaaaaaaa,$b0000000,$ffffffff
	dc.l	$00000000,$18888888,$4aaaaaaa
	dc.l	$0fffffff,$2eeeeeee,$70000000
	dc.l	$21111111,$44444444,$8ccccccc
	dc.l	$38888888,$5bbbbbbb,$a6666666
	dc.l	$51111111,$74444444,$beeeeeee
	dc.l	$6eeeeeee,$8fffffff,$d4444444
	dc.l	$8eeeeeee,$abbbbbbb,$eaaaaaaa
	dc.l	$b0000000,$c9999999,$ffffffff
	dc.l	$13333333,$00000000,$4aaaaaaa
	dc.l	$28888888,$0fffffff,$70000000
	dc.l	$3ddddddd,$21111111,$8ccccccc
	dc.l	$54444444,$38888888,$a6666666
	dc.l	$6ddddddd,$51111111,$beeeeeee
	dc.l	$88888888,$6eeeeeee,$d4444444
	dc.l	$a5555555,$8eeeeeee,$eaaaaaaa
	dc.l	$c4444444,$b0000000,$ffffffff
	dc.l	$00000000,$01111111,$4aaaaaaa
	dc.l	$0fffffff,$11111111,$70000000
	dc.l	$21111111,$24444444,$8ccccccc
	dc.l	$38888888,$3aaaaaaa,$a6666666
	dc.l	$51111111,$53333333,$beeeeeee
	dc.l	$6eeeeeee,$70000000,$d4444444
	dc.l	$8eeeeeee,$8fffffff,$eaaaaaaa
	dc.l	$b0000000,$b2222222,$ffffffff
	dc.l	$00000000,$00000000,$00000000
	dc.l	$24444444,$24444444,$24444444
	dc.l	$48888888,$48888888,$48888888
	dc.l	$6ddddddd,$6ddddddd,$6ddddddd
	dc.l	$91111111,$91111111,$91111111
	dc.l	$b6666666,$b6666666,$b6666666
	dc.l	$daaaaaaa,$daaaaaaa,$daaaaaaa
	dc.l	$ffffffff,$ffffffff,$ffffffff
	dc.l	$00000000,$00000000,$00000000
	dc.l	$24444444,$24444444,$24444444
	dc.l	$48888888,$48888888,$48888888
	dc.l	$6ddddddd,$6ddddddd,$6ddddddd
	dc.l	$91111111,$91111111,$91111111
	dc.l	$b6666666,$b6666666,$b6666666
	dc.l	$daaaaaaa,$daaaaaaa,$daaaaaaa
	dc.l	$ffffffff,$ffffffff,$ffffffff
	dc.w	0

colour_remap_table:

	dc.b	$00,$00,$12,$12,$02,$02,$03,$03,$04,$04,$13,$13,$06,$06,$07,$07
	dc.b	$08,$08,$09,$09,$0a,$0a,$0b,$0b,$0c,$0c,$0d,$0d,$0e,$0e,$0f,$0f
	dc.b	$10,$10,$1b,$1b,$01,$01,$05,$05,$14,$14,$15,$15,$16,$16,$17,$17
	dc.b	$18,$18,$19,$19,$1a,$1a,$11,$11,$1c,$1c,$1d,$1d,$1e,$1e,$1f,$1f
	dc.b	$20,$20,$21,$21,$22,$22,$23,$23,$24,$24,$25,$25,$26,$26,$27,$27
	dc.b	$28,$28,$29,$29,$2a,$2a,$2b,$2b,$2c,$2c,$2d,$2d,$2e,$2e,$2f,$2f
	dc.b	$30,$30,$31,$31,$32,$32,$33,$33,$34,$34,$35,$35,$36,$36,$37,$37
	dc.b	$38,$38,$39,$39,$3a,$3a,$3b,$3b,$3c,$3c,$3d,$3d,$3e,$3e,$3f,$3f
	dc.b	$40,$40,$41,$41,$42,$42,$43,$43,$44,$44,$45,$45,$46,$46,$47,$47
	dc.b	$48,$48,$49,$49,$4a,$4a,$4b,$4b,$4c,$4c,$4d,$4d,$4e,$4e,$4f,$4f
	dc.b	$50,$50,$51,$51,$52,$52,$53,$53,$54,$54,$55,$55,$56,$56,$57,$57
	dc.b	$58,$58,$59,$59,$5a,$5a,$5b,$5b,$5c,$5c,$5d,$5d,$5e,$5e,$5f,$5f
	dc.b	$60,$60,$61,$61,$62,$62,$63,$63,$64,$64,$65,$65,$66,$66,$67,$67
	dc.b	$68,$68,$69,$69,$6a,$6a,$6b,$6b,$6c,$6c,$6d,$6d,$6e,$6e,$6f,$6f
	dc.b	$70,$70,$71,$71,$72,$72,$73,$73,$74,$74,$75,$75,$76,$76,$77,$77
	dc.b	$78,$78,$79,$79,$7a,$7a,$7b,$7b,$7c,$7c,$7d,$7d,$7e,$7e,$7f,$7f

byte_reverse_table:

	incbin	"byte_reverse_table"

	cnop	0,2

word_stretch_table:

	incbin	"word_stretch_table"

	cnop	0,4

long_stretch_table:

	incbin	"long_stretch_table"

	cnop	0,4

nibble_stretch_table:

	incbin	"nibble_stretch_table"

nibble_reverse_table:

	incbin	"nibble_reverse_table"

num_size_table:

	dc.b	%00000001,%00000011,%00000101,%00000111
	dc.b	%00010001,%00000001,%00010101,%00000001

player_centre_table:

	dc.b	4,4,4,4,4,8,4,16

playfield_delay_table:

	dc.b	4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3
	dc.b	4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3
	dc.b	4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3,4,5,2,3

window_tags:

	dc.l	WA_Top,MENU_BAR_HEIGHT
	dc.l	WA_Height,0
	dc.l	WA_IDCMP,IDCMP_MENUPICK|IDCMP_RAWKEY|IDCMP_MOUSEBUTTONS|IDCMP_MOUSEMOVE
	dc.l	WA_CustomScreen,0
	dc.l	WA_Backdrop,1
	dc.l	WA_ReportMouse,1
	dc.l	WA_NoCareRefresh,1
	dc.l	WA_Borderless,1
	dc.l	WA_Activate,1
	dc.l	WA_NewLookMenus,1
	dc.l	TAG_END,0

menu_layout_tags:

	dc.l	GTMN_NewLookMenus,1
	dc.l	TAG_END,0

new_menus:

	dc.b	NM_TITLE,0
	dc.l	game_menu_name
	dcb.b	14,0
	dc.b	NM_ITEM,0
	dc.l	about_item_name
	dc.l	about_item_key
	dcb.b	10,0
	dc.b	NM_ITEM,0
	dc.l	NM_BARLABEL
	dcb.b	14,0
	dc.b	NM_ITEM,0
	dc.l	quit_item_name
	dc.l	quit_item_key
	dcb.b	10,0

	dc.b	NM_TITLE,0
	dc.l	console_menu_name
	dcb.b	14,0
	dc.b	NM_ITEM,0
	dc.l	select_item_name
	dc.l	select_item_key
	dcb.b	10,0
	dc.b	NM_ITEM,0
	dc.l	reset_item_name
	dc.l	reset_item_key
	dcb.b	10,0
	dc.b	NM_ITEM,0
	dc.l	NM_BARLABEL
	dcb.b	14,0
	dc.b	NM_ITEM,0
	dc.l	colour_item_name
	dc.l	0
	dc.w	CHECKIT|MENUTOGGLE|CHECKED
	dcb.b	8,0
	dc.b	NM_ITEM,0
	dc.l	easy_item_name
	dc.l	0
	dc.w	CHECKIT|MENUTOGGLE
	dcb.b	8,0
	dc.b	NM_END,0
	dcb.b	18,0

game_menu_name:

	dc.b	"Game",0

about_item_name:

	dc.b	"About...",0

about_item_key:

	dc.b	"?",0

quit_item_name:

	dc.b	"Quit",0

quit_item_key:

	dc.b	"Q",0

console_menu_name:

	dc.b	"Control",0

select_item_name:

	dc.b	"Select",0

select_item_key:

	dc.b	"S",0

reset_item_name:

	dc.b	"Reset",0

reset_item_key:

	dc.b	"R",0

colour_item_name:

	dc.b	"Colour",0

easy_item_name:

	dc.b	"Easy",0


about_req_body_text:

	dc.b	"Speed:              %3u%%\n\n"
	dc.b	"Frames per second:   %3u\n\n"
	dc.b	"Frame skip ratio:    1:%u",0

about_req_button_text:

	dc.b	"OK",0

default_prog_name:

	dc.b	"Atari 2600 Program",0

	cnop	0,4

pattern_line_function_table:

	dc.l	MakePlayfieldPatternLine
	dc.l	MakePlayerPatternLine
	dc.l	MakePlayerPatternLine
	dc.l	MakeBallPatternLine
	dc.l	MakeMissilePatternLine
	dc.l	MakeMissilePatternLine

	dc.l	MakePlayfieldBitPlanePatternLine
	dc.l	MakePlayfieldBitPlanePatternLine
	dc.l	MakePlayfieldBitPlanePatternLine
	dc.l	MakePlayfieldBitPlanePatternLine
	dc.l	MakePlayfieldBitPlanePatternLine
	dc.l	MakePlayfieldBitPlanePatternLine
	dc.l	MakePlayfieldBitPlanePatternLine

	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine

	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine

	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine
	dc.l	MakeBitPlanePatternLine

	dc.l	MakePriorityPatternLine

horizontal_position_table:

	dc.b	0,0,0,0,0,0,0,0,0,0
	dc.b	0,0,0,0,0,0,0,0,0,0
	dc.b	0,0,0,0,0,0,0,0,0,0
	dc.b	0,0,0,0,0,0,0,0,0,0
	dc.b	0,0,0,0,0,0,0,0,0,0
	dc.b	0,0,0,0,0,0,0,0,0,0
	dc.b	0,0,0,0,0,0,0,0,0,1
	dc.b	2,3,4,5,6,7,8,9,10,11
	dc.b	12,13,14,15,16,17,18,19,20,21
	dc.b	22,23,24,25,26,27,28,29,30,31
	dc.b	32,33,34,35,36,37,38,39,40,41
	dc.b	42,43,44,45,46,47,48,49,50,51
	dc.b	52,53,54,55,56,57,58,59,60,61
	dc.b	62,63,64,65,66,67,68,69,70,71
	dc.b	72,73,74,75,76,77,78,79,80,81
	dc.b	82,83,84,85,86,87,88,89,90,91
	dc.b	92,93,94,95,96,97,98,99,100,101
	dc.b	102,103,104,105,106,107,108,109,110,111
	dc.b	112,113,114,115,116,117,118,119,120,121
	dc.b	122,123,124,125,126,127,128,129,130,131
	dc.b	132,133,134,135,136,137,138,139,140,141
	dc.b	142,143,144,145,146,147,148,149,150,151
	dc.b	152,153,154,155,156,157,158,159

	cnop	0,4

horizontal_motion_table:

	dc.w	0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,2,3,4,5,6,6,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,2,3,4,5,5,5,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,2,3,4,5,5,5,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,2,3,4,4,4,4,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,2,3,3,3,3,3,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,2,2,2,2,2,2,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,2,2,2,2,2,2,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,1,1,1,1,1,1,1,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	0,0,0,0,0,0,0,0,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	-1,-1,-1,-1,-1,-1,-1,-1,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	-1,-1,-1,-1,-1,-1,-1,-1,-8,-7,-6,-5,-4,-3,-2,-1
	dc.w	-2,-2,-2,-2,-2,-2,-2,-2,-8,-7,-6,-5,-4,-3,-2,-2
	dc.w	-3,-3,-3,-3,-3,-3,-3,-3,-8,-7,-6,-5,-4,-3,-3,-3
	dc.w	-4,-4,-4,-4,-4,-4,-4,-4,-8,-7,-6,-5,-4,-4,-4,-4
	dc.w	-4,-4,-4,-4,-4,-4,-4,-4,-8,-7,-6,-5,-4,-4,-4,-4
	dc.w	-5,-5,-5,-5,-5,-5,-5,-5,-8,-7,-6,-5,-5,-5,-5,-5
	dc.w	-6,-6,-6,-6,-6,-6,-6,-6,-8,-7,-6,-6,-6,-6,-6,-6
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,1,2,3,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,0,1,2,3,0,0,0,0,0,0,0,0
	dc.w	0,0,0,0,1,2,3,4,0,0,0,0,0,0,0,0
	dc.w	0,0,0,1,2,3,4,5,0,0,0,0,0,0,0,0
	dc.w	0,0,1,2,3,4,5,6,0,0,0,0,0,0,0,0
	dc.w	0,0,1,2,3,4,5,6,0,0,0,0,0,0,0,0
	dc.w	0,1,2,3,4,5,6,7,0,0,0,0,0,0,0,0
	dc.w	1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,0
	dc.w	2,3,4,5,6,7,8,9,0,0,0,0,0,0,0,1
	dc.w	2,3,4,5,6,7,8,9,0,0,0,0,0,0,0,1
	dc.w	3,4,5,6,7,8,9,10,0,0,0,0,0,0,1,2
	dc.w	4,5,6,7,8,9,10,11,0,0,0,0,0,1,2,3
	dc.w	5,6,7,8,9,10,11,12,0,0,0,0,1,2,3,4
	dc.w	5,6,7,8,9,10,11,12,0,0,0,0,1,2,3,4
	dc.w	6,7,8,9,10,11,12,13,0,0,0,1,2,3,4,5
	dc.w	7,8,9,10,11,12,13,14,0,0,1,2,3,4,5,6
	dc.w	8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7
	dc.w	8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7
	dc.w	0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1

frameskip_tt_name:

	dc.b	"FRAMESKIPRATIO",0

startingline_tt_name:

	dc.b	"STARTLINE",0

tvlines_tt_name:

	dc.b	"TVLINES",0

framerate_tt_name:

	dc.b	"IDEALFRAMERATE",0

nosound_tt_name:

	dc.b	"NOSOUND",0

	cnop	0,4

controller_tt_name_table:

	dc.l	leftcontroller_tt_name
	dc.l	rightcontroller_tt_name

leftcontroller_tt_name:

	dc.b	"LEFTCONTROLLER",0

rightcontroller_tt_name:

	dc.b	"RIGHTCONTROLLER",0

	cnop	0,4

controller_tt_value_table:

	dc.l	nocontroller_name
	dc.l	joystick_name
	dc.l	paddles_name
	dc.l	driving_name
	dc.l	keypad_name

nocontroller_name:

	dc.b	"NONE",0

joystick_name:

	dc.b	"JOYSTICK",0

paddles_name:

	dc.b	"PADDLES",0

driving_name:

	dc.b	"DRIVING",0

keypad_name:

	dc.b	"KEYPAD",0

screenmode_tt_name:

	dc.b	"SCREENMODEID",0

channel_combinations:

	dc.b	1|2,1|4,1|8,2|4,2|8,4|8

slow_pure_pattern:

	dc.b	127,127,127,-128,-128,-128

div_31_pattern:

	dc.b	 127,-128,-128,-128,-128,-128,-128,-128,-128,-128
	dc.b	-128,-128,-128,-128,-128,-128,-128,-128,-128, 127
	dc.b	 127, 127, 127, 127, 127, 127, 127, 127, 127, 127
	dc.b	 127

div_93_pattern:

	dc.b	 127, 127, 127,-128,-128,-128,-128,-128,-128,-128
	dc.b	-128,-128,-128,-128,-128,-128,-128,-128,-128,-128
	dc.b	-128,-128,-128,-128,-128,-128,-128,-128,-128,-128
	dc.b	-128,-128,-128,-128,-128,-128,-128,-128,-128,-128
	dc.b	-128,-128,-128,-128,-128,-128,-128,-128,-128,-128
	dc.b	-128,-128,-128,-128,-128,-128,-128, 127, 127, 127
	dc.b	 127, 127, 127, 127, 127, 127, 127, 127, 127, 127
	dc.b	 127, 127, 127, 127, 127, 127, 127, 127, 127, 127
	dc.b	 127, 127, 127, 127, 127, 127, 127, 127, 127, 127
	dc.b	 127, 127, 127


four_bit_poly_pattern:

	dc.b	127,127,-128,127,127,127,-128,-128
	dc.b	-128,-128,127,-128,127,-128,-128

five_bit_poly_pattern:

	dc.b	-128,-128, 127,-128, 127, 127,-128,-128, 127, 127
	dc.b	 127, 127, 127,-128,-128,-128, 127, 127,-128, 127
	dc.b	 127, 127,-128, 127,-128, 127,-128,-128,-128,-128
	dc.b	 127

nine_bit_poly_pattern:

	dc.b	 127,-128, 127,-128, 127,-128, 127,-128, 127, 127
	dc.b	 127, 127, 127,-128, 127,-128, 127,-128, 127,-128
	dc.b	 127,-128, 127,-128,-128, 127,-128, 127,-128, 127
	dc.b	-128, 127, 127,-128, 127,-128, 127,-128, 127,-128
	dc.b	 127,-128, 127,-128,-128,-128,-128, 127,-128,-128
	dc.b	-128,-128, 127,-128,-128, 127,-128, 127,-128, 127
	dc.b	-128, 127,-128,-128, 127, 127, 127, 127,-128, 127
	dc.b	-128, 127,-128,-128, 127, 127, 127,-128, 127,-128
	dc.b	 127,-128,-128, 127,-128, 127,-128, 127,-128, 127
	dc.b	 127, 127,-128, 127,-128, 127,-128, 127,-128, 127
	dc.b	-128, 127,-128, 127,-128, 127,-128, 127,-128, 127
	dc.b	-128,-128, 127, 127, 127,-128, 127,-128,-128, 127
	dc.b	-128, 127, 127, 127,-128,-128, 127, 127,-128,-128
	dc.b	 127, 127,-128,-128, 127, 127,-128,-128, 127, 127
	dc.b	 127,-128,-128, 127, 127,-128,-128, 127, 127,-128
	dc.b	-128, 127, 127,-128, 127, 127,-128,-128, 127, 127
	dc.b	-128, 127, 127,-128,-128, 127,-128, 127,-128, 127
	dc.b	-128, 127,-128, 127, 127, 127,-128,-128, 127, 127
	dc.b	 127, 127,-128,-128, 127, 127,-128,-128,-128, 127
	dc.b	 127,-128,-128, 127, 127, 127,-128,-128,-128, 127
	dc.b	 127, 127,-128,-128, 127, 127, 127,-128,-128, 127
	dc.b	 127,-128, 127,-128, 127,-128, 127, 127,-128, 127
	dc.b	-128, 127,-128, 127, 127,-128, 127,-128,-128,-128
	dc.b	 127, 127, 127,-128, 127,-128, 127,-128, 127,-128
	dc.b	 127,-128, 127, 127,-128, 127,-128, 127, 127,-128
	dc.b	-128, 127,-128, 127,-128, 127,-128,-128, 127,-128
	dc.b	-128,-128, 127,-128, 127,-128, 127,-128,-128, 127
	dc.b	-128,-128,-128, 127,-128, 127,-128, 127,-128, 127
	dc.b	 127, 127, 127,-128, 127,-128, 127,-128,-128,-128
	dc.b	 127,-128,-128,-128, 127,-128, 127,-128, 127,-128
	dc.b	 127,-128, 127,-128, 127,-128, 127,-128, 127,-128
	dc.b	-128, 127, 127,-128,-128,-128, 127,-128, 127, 127
	dc.b	 127, 127,-128, 127,-128, 127,-128, 127,-128,-128
	dc.b	 127,-128, 127,-128, 127,-128, 127, 127, 127,-128
	dc.b	-128,-128, 127, 127, 127,-128,-128, 127, 127,-128
	dc.b	 127, 127, 127,-128, 127,-128, 127,-128, 127,-128
	dc.b	 127,-128, 127,-128, 127, 127, 127,-128, 127,-128
	dc.b	 127,-128,-128,-128, 127,-128,-128, 127,-128, 127
	dc.b	-128, 127,-128, 127,-128,-128,-128,-128,-128,-128
	dc.b	 127, 127,-128,-128, 127,-128, 127,-128, 127, 127
	dc.b	-128, 127, 127,-128,-128, 127, 127,-128,-128, 127
	dc.b	-128, 127,-128,-128, 127,-128, 127,-128,-128, 127
	dc.b	-128, 127,-128,-128,-128, 127,-128, 127,-128, 127
	dc.b	-128,-128, 127,-128, 127,-128, 127,-128, 127, 127
	dc.b	-128, 127,-128, 127,-128,-128, 127, 127, 127,-128
	dc.b	-128, 127, 127,-128,-128, 127, 127,-128, 127, 127
	dc.b	-128, 127, 127,-128,-128, 127, 127,-128, 127,-128
	dc.b	 127,-128,-128, 127,-128,-128, 127, 127,-128, 127
	dc.b	-128, 127,-128,-128, 127,-128, 127,-128,-128, 127
	dc.b	 127,-128, 127,-128, 127,-128, 127, 127, 127,-128
	dc.b	-128, 127,-128, 127,-128,-128, 127, 127,-128,-128
	dc.b	 127

	cnop	0,4

sound_length_table:

	dc.l	SOUND_0_MULTIPLE
	dc.l	FOUR_BIT_POLY_SIZE*SOUND_1_MULTIPLE
	dc.l	FIVE_BIT_POLY_SIZE*FOUR_BIT_POLY_SIZE*SOUND_2_MULTIPLE
	dc.l	FIVE_BIT_POLY_SIZE*FOUR_BIT_POLY_SIZE*SOUND_3_MULTIPLE
	dc.l	SOUND_4_MULTIPLE
	dc.l	SOUND_4_MULTIPLE
	dc.l	31*SOUND_6_MULTIPLE
	dc.l	FIVE_BIT_POLY_SIZE*SOUND_7_MULTIPLE
	dc.l	NINE_BIT_POLY_SIZE*SOUND_8_MULTIPLE
	dc.l	FIVE_BIT_POLY_SIZE*SOUND_9_MULTIPLE
	dc.l	31*SOUND_6_MULTIPLE
	dc.l	SOUND_0_MULTIPLE
	dc.l	SLOW_PURE_SIZE*SOUND_12_MULTIPLE
	dc.l	SLOW_PURE_SIZE*SOUND_12_MULTIPLE
	dc.l	93*SOUND_14_MULTIPLE
	dc.l	FIVE_BIT_POLY_SIZE*3*SOUND_15_MULTIPLE

period_table:

	dc.w	124
	dc.w	SOUND_PERIOD*2
	dc.w	SOUND_PERIOD*3
	dc.w	SOUND_PERIOD*4
	dc.w	SOUND_PERIOD*5
	dc.w	SOUND_PERIOD*6
	dc.w	SOUND_PERIOD*7
	dc.w	SOUND_PERIOD*8
	dc.w	SOUND_PERIOD*9
	dc.w	SOUND_PERIOD*10
	dc.w	SOUND_PERIOD*11
	dc.w	SOUND_PERIOD*12
	dc.w	SOUND_PERIOD*13
	dc.w	SOUND_PERIOD*14
	dc.w	SOUND_PERIOD*15
	dc.w	SOUND_PERIOD*16
	dc.w	SOUND_PERIOD*17
	dc.w	SOUND_PERIOD*18
	dc.w	SOUND_PERIOD*19
	dc.w	SOUND_PERIOD*20
	dc.w	SOUND_PERIOD*21
	dc.w	SOUND_PERIOD*22
	dc.w	SOUND_PERIOD*23
	dc.w	SOUND_PERIOD*24
	dc.w	SOUND_PERIOD*25
	dc.w	SOUND_PERIOD*26
	dc.w	SOUND_PERIOD*27
	dc.w	SOUND_PERIOD*28
	dc.w	SOUND_PERIOD*29
	dc.w	SOUND_PERIOD*30
	dc.w	SOUND_PERIOD*31
	dc.w	SOUND_PERIOD*32

	cnop	0,4

screen_req_tags:

	dc.l	ASLSM_TitleText,0
	dc.l	ASLSM_NegativeText,screen_req_cancel_text
	dc.l	ASLSM_PropertyFlags,DIPF_IS_DBUFFER
	dc.l	ASLSM_PropertyMask,DIPF_IS_DBUFFER
	dc.l	ASLSM_MinWidth,A2600_TV_WIDTH*X_MAG
	dc.l	ASLSM_MinHeight,0
	dc.l	ASLSM_MinDepth,SCREEN_DEPTH
	dc.l	ASLSM_MaxDepth,SCREEN_DEPTH
	dc.l	TAG_END,0

screen_req_cancel_text:

	dc.b	"Quit",0

hex_digit_table:

	dc.b	'0','1','2','3','4','5','6','7'
	dc.b	'8','9','A','B','C','D','E','F'



end_of_library:


	end


