More Interesting Code Snippets

Ask anything your want about Megadrive/Genesis programming.

Moderator: BigEvilCorporation

Post Reply
walker7
Interested
Posts: 45
Joined: Tue Jul 24, 2012 6:27 am

More Interesting Code Snippets

Post by walker7 » Thu Oct 22, 2015 7:09 pm

In this thread, I'll post some interesting snippets of code that I've come up with.

The first one takes a set of colors that are compressed, and decompresses them. This is useful when you have large color arrays.

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
Example:
Say that you have bytes at $20000 that read 72 FF F8 90 D4. You want to decompress 3 colors to $FF1000 in RAM. Then, use this input:
  • Set d1 to 0, because you are decompressing from the beginning of the list.
  • Set d0 to 2 for 3 colors (this is a loop counter adjusted for DBRA).
  • Set a0 (source) to $020000.
  • Set a1 (destination) to $FF1000.
Now, to explain what will happen here, convert the bytes above to binary.
72 = 01110010
FF = 11111111
F8 = 11111000
90 = 10010000
D4 = 11010100

Now, concatenate the bytes into one long string.
0111001011111111111110001001000011010100

Then, break it into sets of 9 bits.
011100101 111111111 111000100 100001101 0100xxxxx

Next, for each 9-bit block, map these bits to the 9 meaningful bits for a color:
011 100 101 becomes 0000011010001010, or color $068A, which is a medium brown.
111 111 111 becomes color $EEE, which is white.
111 000 100 becomes color $E08, or purple.
100 001 101 becomes color $82A, or plum.

With the input above, the bytes output at $FF1000 will be 06 8A 0E EE 0E 08.

If the input in d1 was 1, you would start at the second color in the list instead of the first. The result would then be 0E EE 0E 08 08 2A.

---------------------------------------------------------------------------

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
---------------------------------------------------------------------------

This one does the same thing, but the data is immediate as opposed to being in registers. You can use it for drawing rectangles whose parameters are always constant. The immediate data 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
Example:
If the data following the call to this routine is 0003 03 05 08, it checks the value in d0, and returns accordingly:
  • If d0 is from 00 to 02, it returns 0.
  • If d0 is from 03 to 04, it returns 1.
  • If d0 is from 05 to 07, it returns 2.
  • If d0 is from 08 or higher, it returns 3.
Note that this routine uses only byte-size values. Also, the data must be padded to an even number of bytes. If you want to do a large number of comparisons, this one is for you.

---------------------------------------------------------------------------

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
Again, output is immediate, and there are two bytes that determine how many bytes follow. For example, say that the bytes were 0006 01 07 05 00 0A 13. Depending on the value of d0, here is what is returned:
  • If d0 is 00, 01 is returned.
  • If d0 is 01, 07 is returned.
  • If d0 is 02, 05 is returned.
  • If d0 is 03, 00 is returned.
  • If d0 is 04, 0A is returned.
  • If d0 is 05, 13 is returned.
When programming, you can do it if you put your mind to it.

greatkreator
Very interested
Posts: 158
Joined: Sat May 12, 2012 7:37 pm
Location: Ukraine

Re: More Interesting Code Snippets

Post by greatkreator » Tue Nov 17, 2015 3:13 pm

Sad there is not like-button as Facebook has.
I would use it with a pleasure.
LIKE VERY MUCH.

Post Reply