Aggregating Code Snippets
Posted: Sat Mar 04, 2017 4:16 am
				
				In response to Mask of Destiny's post about aggregating community research, I'd like to aggregate all the interesting code snippets I've posted.
The one takes a set of colors (in 9-bit strings, bbbgggrrr format) that are compressed, and decompresses them.
---------------------------------------------------------------------------
This piece of code draws a rectangle of VRAM tiles, all of which are the same. It assumes you have a 64*64-tile plane size.
---------------------------------------------------------------------------
Same as above, but the data is immediate as opposed to being in registers. It requires immediate data, which is 8 bytes long:
---------------------------------------------------------------------------
Another rectangle-drawing snippet, except this time, the VRAM tile increments by 1 after every tile.
---------------------------------------------------------------------------
Same as above, but with immediate data:
---------------------------------------------------------------------------
Here's another rectangle-drawing method, but this time, using a pointer to determine the tile mappings. In this case, the input in d5 determines what to add to the data before displaying it.
---------------------------------------------------------------------------
Same as above, but the data is immediate. In addition to the usual 8 bytes of data, there are an additional 4 bytes that tell where to find the mappings.
---------------------------------------------------------------------------
Still the same as above, this piece of code uses immediate data, but instead of compressed data, it uses a pointer to Enigma-compressed mapping data.
---------------------------------------------------------------------------
This piece of code converts a number to a range index. It uses immediate data for input.
---------------------------------------------------------------------------
This one does an immediate table lookup.
---------------------------------------------------------------------------
This one converts a 32-bit number a converts it to a 10-digit string. Each digit in the string is represented using one byte which ranges from $00-$09.
---------------------------------------------------------------------------
This one does the same thing, except it produces an ASCII string (bytes range from $30-$39):
---------------------------------------------------------------------------
Multiply a 32-bit number by 10:
NOTE:  LSL.L #2 is faster than two ADD.L's.
---------------------------------------------------------------------------
Copy the C flag to the X flag:
---------------------------------------------------------------------------
Signum of a 32-bit integer (posted by ehaliewicz):
I might post some more later on. Coming up:
			The one takes a set of colors (in 9-bit strings, bbbgggrrr format) that are compressed, and decompresses them.
Code: Select all
Load_CompColors:
;==============================================================================
; INPUT:	a0	= POINTS TO: List of colors
;		a1	= POINTS TO: Destination
;		d0.w	= # of Colors - 1
;		d1.w	= Offset
;==============================================================================
	;----------------------------------------------------------------------
	; Initialize Registers.
	;----------------------------------------------------------------------
	movem.l	d0-d5/a0,-(a7)		; -- Save Registers.
	mulu	#9,d1
	moveq	#7,d2			; Initialize Bit Count.
	and.w	d1,d2
	neg.w	d2
	addq.w	#8,d2
	lsr.w	#3,d1
	add.w	d1,a0
	lsl.l	#8,d3
	move.b	(a0)+,d3
	;----------------------------------------------------------------------
	; If there are at least 9 bits in the Accumulator, unpack the color.
	; Otherwise, read the next byte.
	;----------------------------------------------------------------------
.2:	cmpi.w	#9,d2
	bcc.b	.1
	lsl.l	#8,d3
	move.b	(a0)+,d3
	addq.w	#8,d2
	bra.b	.2
.1:	;----------------------------------------------------------------------
	; Unpack a color.
	;----------------------------------------------------------------------
	move.l	d3,d4
	move.w	d2,d5
	subi.w	#9,d5
	move.w	d5,d2
	lsr.l	d5,d4			; d4.w = %-------bbbgggrrr
	lsl.w	#2,d4			; d4.w = %-----bbbgggrrr..
	lsr.b	#1,d4			; d4.w = %-----bbb.gggrrr.
	lsl.w	#4,d4			; d4.w = %-bbb.gggrrr.....
	lsr.b	#1,d4			; d4.w = %-bbb.ggg.rrr....
	lsr.w	#3,d4			; d4.w = %...-bbb.ggg.rrr.
	andi.w	#$EEE,d4		; d4.w = %....bbb.ggg.rrr.
	move.w	d4,(a1)+		; Store color.
	dbra	d0,.2			; Loop for next color.
	movem.l	(a7)+,d0-d5/a0		; ++ Restore Registers.
	rts
This piece of code draws a rectangle of VRAM tiles, all of which are the same. It assumes you have a 64*64-tile plane size.
Code: Select all
Rectangle:
;==============================================================================
; INPUT:	d0.5-0	= X Size
;		d1.5-0	= Y Size
;		d2.5-0	= X Starting Position
;		d3.5-0	= Y Starting Position
;		d4.2-0	= VRAM Bloc ($2000 boundaries)
;		d5.w	= Starting tile
; OUTPUT:	A rectangle of a certain tile onscreen.
; NOTES:	This is best used for rectangles whose parameters may be
; variable.
;==============================================================================
	movem.l	d0-d6/a0-a1,-(a7)	
	lea	$C00004,a0
	lea	-4(a0),a1
	moveq	#$3F,d6
	and.w	d6,d0
	and.w	d6,d1
	and.w	d6,d2
	and.w	d6,d3
	moveq	#$07,d6
	and.l	d6,d4
	lsl.w	#6,d4
	or.w	d3,d4
	lsl.w	#6,d4
	or.w	d2,d4
	lsl.l	#3,d4
	lsr.w	#2,d4
	swap	d4
	bset	#30,d4		
.2:	move.w	d0,-(a7)
	move.l	d4,(a0)
.1:	move.w	d5,(a1)
	dbra	d0,.1
	move.w	(a7)+,d0
	addi.l	#1<<23,d4
	dbra	d1,.2
	movem.l	(a7)+,d0-d6/a0-a1
	rts
Same as above, but the data is immediate as opposed to being in registers. It requires immediate data, which is 8 bytes long:
- One byte for number of tiles across, minus 1
- One byte for number of tiles down, minus 1
- One byte for starting X position, minus 1
- One byte for starting Y position, minus 1
- Two bytes for VRAM bloc. It can range from 0-7 (0 = $0000, 1 = $2000, etc.). Even with this range, it's still two bytes because the next value needs to be aligned to an even address.
- Two bytes for VRAM tile.
Code: Select all
Rectangle_Imm:
;==============================================================================
; INPUT:	Immediate Data
; OUTPUT:	A rectangle of a certain tile onscreen.
; NOTES:	The immediate data is in this format:
;	Byte  #1	X Size (0-63, for 1-64 tiles)
;	Byte  #2	Y Size (0-63, for 1-64 tiles)
;	Byte  #3	X Position
;	Byte  #4	Y Position
;	Bytes #5-#6	VRAM Bloc (0-7; is 2 bytes for byte-alignment reasons)
;	Bytes #7-#8	VRAM Tile
; NOTES:	This is best used for rectangles whose parameters are constant.
;==============================================================================
	move.l	a0,-(a7)	; -- Save Register.
	move.l	4(a7),a0	; Load JSR address.
	move.b	(a0)+,d0	; Load X Size in d0.
	move.b	(a0)+,d1	; Load Y Size in d1.
	move.b	(a0)+,d2	; Load X Position in d2.
	move.b	(a0)+,d3	; Load Y Position in d3.
	move.w	(a0)+,d4	; Load VRAM Bloc in d4.
	move.w	(a0)+,d5	; Load VRAM Tile in d5.
	bsr.b	Rectangle	; Draw Rectangle.
	move.l	(a7)+,a0	; ++ Restore Register.
	addq.l	#8,(a7)		; Adjust return address.
	rts
Another rectangle-drawing snippet, except this time, the VRAM tile increments by 1 after every tile.
Code: Select all
Rectangle_Inc:
;==============================================================================
; INPUT:	d0.5-0	= X Size
;		d1.5-0	= Y Size
;		d2.5-0	= X Starting Position
;		d3.5-0	= Y Starting Position
;		d4.2-0	= VRAM Section ($2000 boundaries)
;		d5.w	= VRAM Tile Offset
; OUTPUT:	A rectangle of a certain tile onscreen, with the tile
;		incrementing.
; NOTES:	This is best used for rectangles whose parameters may be
; variable.
;==============================================================================
	movem.l	d0-d6/a0-a1,-(a7)
	lea	$C00004,a0
	lea	-4(a0),a1
	moveq	#$3F,d6
	and.w	d6,d0
	and.w	d6,d1
	and.w	d6,d2
	and.w	d6,d3
	moveq	#$07,d6
	and.l	d6,d4
	lsl.w	#6,d4
	or.w	d3,d4
	lsl.w	#6,d4
	or.w	d2,d4
	lsl.l	#3,d4
	lsr.w	#2,d4
	swap	d4
	bset	#30,d4	
.2:	move.w	d0,-(a7)
	move.l	d4,(a0)
.1:	move.w	d5,(a1)
	addq.w	#1,d5
	dbra	d0,.1
	move.w	(a7)+,d0
	addi.l	#1<<23,d4
	dbra	d1,.2
	movem.l	(a7)+,d0-d6/a0-a1
	rts
Same as above, but with immediate data:
Code: Select all
Rectangle_IncI:
;==============================================================================
; INPUT:	Immediate Data
; OUTPUT:	A rectangle of a certain tile onscreen, with the tile
;		incrementing.
; NOTES:	The immediate data is in this format:
;	Byte  #1	X Size (0-63, for 1-64 tiles)
;	Byte  #2	Y Size (0-63, for 1-64 tiles)
;	Byte  #3	X Position
;	Byte  #4	Y Position
;	Bytes #5-#6	VRAM Bloc (0-7; is 2 bytes for byte-alignment reasons)
;	Bytes #7-#8	VRAM Tile
; This is best used for rectangles whose parameters are constant.
;==============================================================================
	move.l	a0,-(a7)	; -- Save Register.
	move.l	4(a7),a0	; Load JSR address.
	move.b	(a0)+,d0	; Load X Size in d0.
	move.b	(a0)+,d1	; Load Y Size in d1.
	move.b	(a0)+,d2	; Load X Position in d2.
	move.b	(a0)+,d3	; Load Y Position in d3.
	move.w	(a0)+,d4	; Load VRAM Bloc in d4.
	move.w	(a0)+,d5	; Load VRAM Tile in d5.
	bsr.b	Rectangle_Inc	; Draw Rectangle.
	move.l	(a7)+,a0	; ++ Restore Register.
	addq.l	#8,(a7)		; Adjust return address.
	rts
Here's another rectangle-drawing method, but this time, using a pointer to determine the tile mappings. In this case, the input in d5 determines what to add to the data before displaying it.
Code: Select all
Rectangle_Mapping:
;==============================================================================
; INPUT:	a2	= POINTER: Mappings
;		d0.5-0	= X Size
;		d1.5-0	= Y Size
;		d2.5-0	= X Starting Position
;		d3.5-0	= Y Starting Position
;		d4.2-0	= VRAM Section ($2000 boundaries)
;		d5.w	= Starting tile
; OUTPUT:	A rectangle of a certain tile onscreen, with the tiles based
;		on 16-bit mappings.
;==============================================================================
	movem.l	d0-d6/a0-a2,-(a7)
	lea	$C00004,a0
	lea	-4(a0),a1
	moveq	#$3F,d6
	and.w	d6,d0
	and.w	d6,d1
	and.w	d6,d2
	and.w	d6,d3
	moveq	#$07,d6
	and.l	d6,d4
	lsl.w	#6,d4
	or.w	d3,d4
	lsl.w	#6,d4
	or.w	d2,d4
	lsl.l	#3,d4
	lsr.w	#2,d4
	swap	d4
	bset	#30,d4	
.2:	move.w	d0,-(a7)
	move.l	d4,(a0)
.1:	move.w	(a2)+,d6
	add.w	d5,d6
	move.w	d6,(a1)
	dbra	d0,.1
	move.w	(a7)+,d0
	addi.l	#1<<23,d4
	dbra	d1,.2
	movem.l	(a7)+,d0-d6/a0-a2
	rts
Same as above, but the data is immediate. In addition to the usual 8 bytes of data, there are an additional 4 bytes that tell where to find the mappings.
Code: Select all
Rectangle_MapI:
;==============================================================================
; INPUT:	Immediate Data
; OUTPUT:	A rectangle of a certain tile onscreen, with the tiles based
;		on 16-bit mappings.
; NOTES:	The immediate data is in this format:
;	Byte  #1	X Size (0-63, for 1-64 tiles)
;	Byte  #2	Y Size (0-63, for 1-64 tiles)
;	Byte  #3	X Position
;	Byte  #4	Y Position
;	Bytes #5-#6	VRAM Bloc (0-7; is 2 bytes for byte-alignment reasons)
;	Bytes #7-#8	VRAM Tile Offset
; 	Bytes #9-#12	Mapping Address	
; This is best used for rectangles whose parameters are constant, and the
; mappings are uncompressed.
;==============================================================================
	move.l	a0,-(a7)		; -- Save Register.
	move.l	4(a7),a0		; Load JSR address.
	move.b	(a0)+,d0		; Load X Size in d0.
	move.b	(a0)+,d1		; Load Y Size in d1.
	move.b	(a0)+,d2		; Load X Position in d2.
	move.b	(a0)+,d3		; Load Y Position in d3.
	move.w	(a0)+,d4		; Load VRAM Bloc in d4.
	move.w	(a0)+,d5		; Load VRAM Tile Offset in d5.
	move.l	(a0)+,a2		; Load Mapping Address in a2.
	bsr.b	Rectangle_Mapping	; Draw Rectangle.
	move.l	(a7)+,a0		; ++ Restore Register.
	addq.l	#8,(a7)			; Adjust return address.
	addq.l	#4,(a7)			;
	rts
Still the same as above, this piece of code uses immediate data, but instead of compressed data, it uses a pointer to Enigma-compressed mapping data.
Code: Select all
Rectangle_MapEI:
;==============================================================================
; INPUT:	Immediate Data
; OUTPUT:	A rectangle of a certain tile onscreen, with the tiles based
;		on 16-bit mappings.
; NOTES:	The immediate data is in this format:
;	Byte  #1	X Size (0-63, for 1-64 tiles)
;	Byte  #2	Y Size (0-63, for 1-64 tiles)
;	Byte  #3	X Position
;	Byte  #4	Y Position
;	Bytes #5-#6	VRAM Bloc (0-7; is 2 bytes for byte-alignment reasons)
;	Bytes #7-#8	VRAM Tile Offset
; 	Bytes #9-#12	Enigma-Compressed Mapping Address	
; This is best used for rectangles whose parameters are constant, and the
; mappings are Enigma-compressed.
;==============================================================================
	movem.l	a0-a1/a3,-(a7)		; -- Save Register.
	move.l	12(a7),a3		; Load JSR address.	
	move.l	8(a3),a0		; Load Mapping Address.
	lea	__TileBuffer,a1		; POINT TO: Tile Buffer.
	move.l	a1,a2			; Copy Mapping Address.
	move.w	6(a3),d0		; Load VRAM Tile for Enigma Decompression.
	jsr	EniDec			; Decompress the Mappings.		
	move.b	(a3)+,d0		; Load X Size in d0.
	move.b	(a3)+,d1		; Load Y Size in d1.
	move.b	(a3)+,d2		; Load X Position in d2.
	move.b	(a3)+,d3		; Load Y Position in d3.
	move.w	(a3)+,d4		; Load VRAM Bloc in d4.
	moveq	#0,d5			; Set VRAM Tile Offset to 0.
	bsr.b	Rectangle_Mapping	; Draw Rectangle.
	movem.l	(a7)+,a0-a1/a3		; ++ Restore Register.
	addq.l	#8,(a7)			; Adjust RTS address.
	addq.l	#4,(a7)			;
	rts
This piece of code converts a number to a range index. It uses immediate data for input.
- The first two bytes tell how many bytes follow.
- The remaining bytes are simply the data.
Code: Select all
Range_Check:
;==============================================================================
; INPUT:	d0.b	= Number
; OUTPUT:	d0	= Range
;==============================================================================
	movem.l	d1-d2/a0,-(a7)		; -- Save Registers.
	move.l	12(a7),a0		; Load JSR address from stack.
	move.w	(a0)+,d1		; Load Range count as Loop Counter.
	moveq	#0,d2			; Initialize Output.
	bra.b	.3			; Skip loop if Loop Counter is 0.
.2:	cmp.b	(a0,d2.w),d0		; Compare to next Range.
	bcs.b	.1			; If the Input is >= Range...
	addq.w	#1,d2			; ...increment Output by 1.
.3:	dbra	d1,.2			; Loop for next Range.
.1:	move.l	d2,d0			; Arrange Output.
	add.w	-2(a0),a0		; Add Range Count to JSR address.
	move.w	a0,d1			; Load bit 0 of JSR address into a register.
	lsr.w	#1,d1			; Shift it right one bit.
	bcc.b	.4			; If C is set (JSR address was odd)...
	addq.l	#1,a0			; ...increment JSR address by 1.
.4:	move.l	a0,12(a7)		; Update JSR address on stack.
	movem.l	(a7)+,d1-d2/a0		; ++ Restore Registers.
	rts
This one does an immediate table lookup.
Code: Select all
Indexed_Mapping:
	movem.l	d1/a0,-(a7)		; -- Save Registers.
	move.l	8(a7),a0		; Load JSR address from stack.
	move.w	(a0)+,d1		; Load Element count.
	cmp.w	d1,d0			; Compare Index to Element count.
	bcs.b	.1			; If Index >= Element count:
	move.w	d1,d0			; Make Index = Element count...
	subq.w	#1,d0			; ...and subtract 1 from it.
.1:	move.b	0(a0,d0.w),d0		; Load the appropriate Element from the list.
	andi.l	#$FF,d0			; CONVERT: 8-bit Output --> 32-bit Output.
	add.w	d1,a0			; Add Element count to JSR Address.
	move.w	a0,d1			; Load bit 0 of JSR address into a register.
	lsr.w	#1,d1			; Shift it right one bit.
	bcc.b	.2			; If C is set (JSR address was odd)...
	addq.l	#1,a0			; ...increment JSR address by 1.
.2:	move.l	a0,8(a7)		; Update JSR address on stack.
	movem.l	(a7)+,d1/a0		; ++ Restore Registers.
	rts
This one converts a 32-bit number a converts it to a 10-digit string. Each digit in the string is represented using one byte which ranges from $00-$09.
Code: Select all
Num_to_UBCD:
;==============================================================================
; INPUT:	d0	= Number
; OUTPUT:	a0	= POINTS TO: Output
; NOTES:	This takes a 32-bit number and converts it to a 10-digit
; number in unpacked BCD. Each digit is one byte, and each byte ranges from
; $00-$09.
;==============================================================================
	movem.l	d1-d2/a0-a1,-(a7)		; -- Save registers.
	lea	Log10_TABLE(pc),a1		; Point to power-of-10 table.
	moveq	#9,d3				; Initialize Loop Counter.
.2:	move.l	(a1)+,d2
	moveq	#-1,d1
.1:	addq.b	#1,d1
	sub.l	d2,d0
	bcc.b	.1
	move.b	d1,(a0)+
	add.l	d2,d0
	dbra	d3,.2
	movem.l	(a7)+,d1-d2/a0-a1		; ++ Restore registers.
	rts
Log10_TABLE:
;==============================================================================
; This is a power-of-10 table.
;==============================================================================
	dc.l	1000000000, 100000000, 10000000, 1000000, 100000
	dc.l	10000, 1000, 100, 10, 1
This one does the same thing, except it produces an ASCII string (bytes range from $30-$39):
Code: Select all
Num_to_ASCII:
;==============================================================================
; INPUT:	d0	= Number
; OUTPUT:	a0	= POINTS TO: Output
; NOTES:	This takes a 32-bit number and converts it to a 10-digit
; number in ASCII. Each digit is one byte, and each byte ranges from $30-$39.
;==============================================================================
	move.l	d3,-(a7)		; -- Save register.
	bsr.b	Num_to_UBCD		; Convert number to unpacked BCD.
	moveq	#9,d3			; Initialize Loop Counter.
.1:	addi.b	#'0',0(a0,d3.w)		; Adjust digit for ASCII.
	dbra	d3,.1			; Loop for next Digit.
	move.l	(a7)+,d3		; ++ Restore register.
	rts
Multiply a 32-bit number by 10:
Code: Select all
add.l	d0,d0
move.l	d0,d1
lsl.l	#2,d0
add.l	d1,d0---------------------------------------------------------------------------
Copy the C flag to the X flag:
Code: Select all
scs	d0
add.b	d0,d0Signum of a 32-bit integer (posted by ehaliewicz):
Code: Select all
add.l	d0,d0
subx.l	d1,d1
negx.l	d0
addx.l	d1,d1
I might post some more later on. Coming up:
- A routine that acts like several MOVEQ's.
- A routine that does an LFSR iteration (several sizes).
- A routine that uses the CCR to index into a jump table.
