Beating the C compiler...

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
User avatar
ParadigmShifter
Manic Miner
Posts: 521
Joined: Sat Sep 09, 2023 4:55 am

Re: Beating the C compiler...

Post by ParadigmShifter »

Anyway I hope you get the idea.

For sprites you may have to draw them at just the right time (i.e. draw them ordered according to where they are on the y axis, intermingled with drawing the attribute rows) to beat the raster which will complicate the code quite a bit I expect.

If you want to reduce the code size you can do it in a loop and self modify the code based on the src and dest pointers as I hinted at earlier... all of those methods will be slower than the code I posted though which is max unrolled.

EDIT: There should really be a DI and an EI around my code which abuses the stack (and a save/restore SP as well).

EDIT2: So this is safer. Stack would be pointing at attribute memory when the interrupt goes off (since I have not changed from the ROM interrupt). I guess that worked out ok since it was pointing at RAM not ROM and even if the attribs got corrupted I redraw them all before the raster reached any corruption ;)

In my actual code I do more stuff with the SP (I erase and draw all my sprites without using SP for anything other than data transfer, i.e. I can't call any functions).

Code: Select all

	; TEST
.testagain
	ld a, 2
	out (#fe), a

	di ; about to mess with SP, best disable interrupts
	ld (.restoresp+1), sp

	REPT 48, idx
	MEMCPY16 ATTRIBS_ADDR + idx*16, (attribptrs + idx*2)
	ENDR

.restoresp
	ld sp, 0 ; restore SP before turning on interrupts
	ei ; we're done abusing the stack now. Safe to call subroutines and for interrupts to go off

	ld a, 5
	out (#fe), a

	; update lower bytes of all 48 src addresses for next frame
	ld b, 48
	ld hl, attribptrs
.updateptrs
	inc (hl)
	inc hl
	inc hl
	djnz .updateptrs

	ld a, 7
	out (#fe), a

	halt


	jp .testagain
rothers
Drutt
Posts: 34
Joined: Sat Dec 30, 2023 2:50 pm

Re: Beating the C compiler...

Post by rothers »

Thank you! I've finished hard coding the sprites now with ASM lookup tables and I think they are as fast as they can be, so now it's just this attribute copy to get working as fast as possible.

The rest of the code is really fast, using the attributes as a tile map is speedy!

I'm also coding a super compressed way to store levels, and that should be it, ready to release. You can actually load super mario bros levels in to it, but I'm only using that to test speed vs the NES.

I'll probably then look at that C64 Sonic port and see what I can do on the 48k. Really enjoying this, it's like my morning crossword puzzle every day.
User avatar
ParadigmShifter
Manic Miner
Posts: 521
Joined: Sat Sep 09, 2023 4:55 am

Re: Beating the C compiler...

Post by ParadigmShifter »

You probably want to use Einar's ZX0 for compression unless your level format is really simple to run-length encode.

I run length encoded the levels for my Manic Miner remake, here is an example so you get the idea of what I did. This was my first attempt at ASM programming so probably could do a lot better. I could compress a lot more by packing the row/column into 9 bits and the repeat count into the high bits of a 16 bit number rather than using a byte for each.

Code: Select all


; some macros to pack pointers to graphics into 8 bits rather than 16
	MACRO celltype gfx
	db (gfx - gfx_platform0) / 8
	ENDM

	MACRO keytype gfx
	db (gfx - gfx_key0) / 8
	ENDM
	
	; macro for ink, paper, bright
	MACRO IPB ink, paper, bright
	db ink|(paper<<3)|(bright<<6)
	ENDM


; Central Cavern (Spectrum Version)
Central_Cavern:
	dc "Central Cavern"

	; border/paper
	db 2

	; cell graphics
	celltype gfx_platform0
	celltype gfx_wall0
	celltype gfx_spiky0
	celltype gfx_crumbly0
	celltype gfx_platform0
	celltype gfx_platform0
	celltype gfx_spiky1
	celltype gfx_conveyor0
	celltype gfx_conveyor0

	; cell attribs
	IPB 2, 0, 1
	IPB 6, 2, 0
	IPB 4, 0, 1
	IPB 2, 0, 0
	IPB 0, 0, 0 
	IPB 0, 0, 0 
	IPB 5, 0, 0
	IPB 4, 0, 0
	IPB 4, 0, 0

	; willy start position x, y. bit 4 of y is set if facing left
	db 2, 13

	; exit position
	db 29, 13
	; exit colour
	IPB 6, 1, 0

	; keytype
	keytype gfx_key0

	db 5	; number of keys
	; position of keys
	db 9, 0
	db 29, 0
	db 16, 1
	db 24, 4
	db 30, 6

	; guardians
	db 1		; number of guardians
	; something like start position, end position of patrol path and some other stuff I can't remember ;) Obvs the attribs (64+6) here too.
	; seems to be terminated with a 0 since some enemies need extra data
	db gfx_robot0/256, 0, 8, 7, 8, 15, 64+6, 0

	; single blocks
	db SPIKY_A, 23, 4
	db SPIKY_A, 27, 4
	db SPIKY_A, 21, 8
	db SPIKY_A, 12, 12
	db SPIKY_B, 11, 0
	db SPIKY_B, 16, 0

	; repeat blocks: platforms
	db HORZ_REPEAT|PLATFORM_A, 1, 5, 30
	db HORZ_REPEAT|PLATFORM_A, 1, 7, 3
	db HORZ_REPEAT|PLATFORM_A, 1, 9, 4
	db HORZ_REPEAT|PLATFORM_A, 29, 10, 2
	db HORZ_REPEAT|PLATFORM_A, 28, 12, 3
	db HORZ_REPEAT|PLATFORM_A, 5, 13, 15
	db HORZ_REPEAT|PLATFORM_A, 1, 15, 30
	; crumbly platforms
	db HORZ_REPEAT|CRUMBLY, 14, 5, 4
	db HORZ_REPEAT|CRUMBLY, 19, 5, 4
	db HORZ_REPEAT|CRUMBLY, 23, 12, 5
	; walls
	db HORZ_REPEAT|WALL_A, 17, 8, 3
	db HORZ_REPEAT|WALL_A, 20, 12, 3
	; conveyor
	db HORZ_REPEAT|CONVEYOR_L, 8, 9, 20
	db #ff ; terminator

	ENDIF

; The Cold Room
The_Cold_Room:
	dc "The Cold Room"

	; border/paper
	IPB 2, 1, 0

	; cell graphics
	celltype gfx_platform0
	celltype gfx_wall0
	celltype gfx_spiky0
	celltype gfx_crumbly0
	celltype gfx_platform0
	celltype gfx_platform0
	celltype gfx_spiky4
	celltype gfx_conveyor0
	celltype gfx_conveyor0

	; cell attribs
	IPB 3, 1, 1
	IPB 6, 2, 0
	IPB 0, 0, 0
	IPB 3, 1, 0
	IPB 0, 0, 0 
	IPB 0, 0, 0 
	IPB 5, 1, 0
	IPB 6, 1, 0
	IPB 6, 1, 0

	; willy start position x, y. bit 4 of y is set if facing left
	db 2, 13

	; exit position
	db 29, 13
	; exit colour
	IPB 3, 2, 1

	; keytype
	keytype gfx_key2

	db 5|(1<<4)	; number of keys/paper colour
	db 7, 1
	db 25, 1
	db 26, 7
	db 3, 9
	db 19, 12

	; guardians
	db 2		; number of guardians
	db gfx_penguin0/256, 7, 18, 3, 1, 18
	IPB 6, 1, 0
	db 0
	db gfx_penguin0/256, 7, 29, 13, 12, 29
	IPB 5, 1, 0
	db 0
	
	; single blocks
	db SPIKY_B, 30, 1
	db PLATFORM_A, 25, 3
	db PLATFORM_A, 1, 7
	
	; repeat blocks
	db HORZ_REPEAT|WALL_A, 19, 0, 12
	db HORZ_REPEAT|PLATFORM_A, 1, 5, 19
	db HORZ_REPEAT|CRUMBLY, 21, 3, 4
	db HORZ_REPEAT|PLATFORM_A, 21, 6, 4
	db HORZ_REPEAT|CRUMBLY, 26, 6, 2
	db HORZ_REPEAT|CRUMBLY, 2, 7, 5
	db HORZ_REPEAT|PLATFORM_A, 9, 9, 7
	db HORZ_REPEAT|CRUMBLY, 19, 10, 4
	db HORZ_REPEAT|CONVEYOR_R, 3, 11, 4
	db HORZ_REPEAT|PLATFORM_A, 14, 12, 4
	db HORZ_REPEAT|CRUMBLY, 8, 13, 4
	db HORZ_REPEAT|PLATFORM_A, 1, 15, 30
	db VERT_REPEAT|WALL_A, 25, 6, 7
	db VERT_REPEAT|WALL_A, 28, 5, 8
	db VERT_REPEAT|CRUMBLY, 26, 8, 5
	db VERT_REPEAT|CRUMBLY, 27, 8, 5
	db #ff
So I had horizontal and vertical repeating cells in the level. Could also add rectangles instead of just horizontal/vertical repeat.

Guardian sprite data starts on a 256 byte aligned boundary so I can just use 8 bits for those as well.
Post Reply