CPC Plotting pixels

Post programming questions related to the CPC Here!
Post Reply
Indyuk2000
Posts: 9
Joined: Wed Apr 24, 2019 11:59 pm

CPC Plotting pixels

Post by Indyuk2000 » Fri Apr 26, 2019 8:12 pm

Hi Keith,

I'd first like to say how glad I am you setup this forum. I think it's really going to help users out a lot. And also my personal thanks to you for giving up so much of your free time to teach assembly - greatly appreciated. It's a shame you weren't teaching 30 years ago when I was much younger. I would have loved to have created some games.

Now onto the query. I'm having quite a bit of trouble working our how to convert pixel x,y to screen address. I know you have covered this in one or two of your tutorials but, for some reason I still can't seem to find the logic pattern. I have a very simple asm program in which I move a pixel around the screen but, that's easy because I start at addrss &C000 and using some register logic and xpos and ypos variables I control it's movement. However, I want to be able to plot at certain areas of the screens and this is where I need some help.

Thanks in advance.

User avatar
akuyou
Posts: 192
Joined: Mon Apr 22, 2019 3:19 am

Re: CPC Plotting pixels

Post by akuyou » Sat Apr 27, 2019 8:11 am

well the reason I wasn't doing this 30 years ago is I was ten then!... but seriously, remember i only started learning Assembly 3 years ago... In a lot of ways I'm really documenting what I'm learning, and making it available to others...

Anyway, in my tutorial I used a lookup table to calculate addresses, but I've come up with something cleverer - and saving a lot of memory!

Try running this from Winape:

Code: Select all

org &8000

	ld c,199
	ld b,0
Again:
	call GetScreenPos
	ld (hl),255
	halt
	dec c
	jr nz,Again
	ret



GetScreenPos:
;	; Input  BC= XY (x=bytes - so 80 across)
;	; output HL= screen mem pos
;	; de is wiped
;

;Looking at Y line (in C) - we need to take each set of bits, and work with them separately:
;YYYYYYYY	Y line number (0-200)
;-----YYY	(0-7)  - this part needs to be multiplied by &0800 and added to the total
;YYYYY---	(0-31) - This part needs to be multiplied by 80 (&50)



;YYYYY---	(0-31) - This part needs to be multiplied by 80 (&50)

	;screen is 80 bytes wide = -------- %01010000
	ld a,C		; 00000000 01010000
	and %11111000 	; -------- YYYYY---
	ld h,0
	ld l,a
			; 00000000 01010000
	
	sla l		; -------Y YYYY----
	rl h
	
	ld d,h		;value is in first bit position -add it
	ld e,l	
			; 00000000 01010000
	sla e		; ------YY YYY-----
	rl d		;	    
	sla e		; -----YYY YY------
	rl d
	
	add hl,de	;value is in 2nd bit position - add it
	;We've now effectively multiplied by 80 (&50)

;-----YYY	(0-7)  - this part needs to be multiplied by &0800 and added to the total
	ld a,C
	and %00000111	
	
	rlca		;X8
	rlca
	rlca

	ld d,a		;Load into top byte, and add as 16 bit
	ld e,0

	add hl,de
	;We've now effectively multiplied by 8

;Screen Base
	ld a,&C0	;Add the screen Base &C000
	ld d,a

;X position
	ld e,B		;Add the X pos 
	add hl,de
	ret 		;return memory location in hl

Effectively we're now using the 'rules' of calculating the start of a line in memory, and calculating the memory position... we effect multiplication via bit shifts, and repeated adds where necessary

This is an improved version of the code in the 1st chibiakumas game, which used parital lookup tables... the reason I didn't use this at that time, is I simply wasn't good enough at ASM maths to manage it...
That said... the lookup table IS faster if you can spare the 400 bytes

Maybe this will help you? if not, I'll try something else, and maybe make a video about this new 'lookup table-less' version
Chibi Akuma(s) Comedy-Horror 8-bit Bullet Hell shooter! // 「チビ悪魔」可笑しいゴシックSTG ! // Work in Progress: ChibiAliens

Interested in CPU's :Z80,6502,68000,6809,ARM,8086,RISC-V
Learning: 65816,ARM,8086,6809

Indyuk2000
Posts: 9
Joined: Wed Apr 24, 2019 11:59 pm

Re: CPC Plotting pixels

Post by Indyuk2000 » Mon Apr 29, 2019 8:06 am

Hi Keith,

Thanks for the example code. I've been looking through it and have started to understand how you are doing things. It's really puzzling why the Amstrad screen (and Spectrum) RAM was never designed to be linear. Using C on the PC I used to find it much easier to work with the screen memory. I used to use a formula to calculate the screen position by entering the x,y into it and I would get the screen RAM position. I suppose I was naive to think that all system would use a simple screen layout.

BTW - I noticed in your sample code you used the "HALT" command. Was that to slow it down or is that another trick of a quick less memory expensive method of V-SYNC?

I've got another quick query for you. Using your pixel plotting code, do you think it could be re-used to plot sprites considering sprites are plotted pixels at a time?

Thanks.

User avatar
akuyou
Posts: 192
Joined: Mon Apr 22, 2019 3:19 am

Re: CPC Plotting pixels

Post by akuyou » Tue Apr 30, 2019 4:13 am

The halt command was just to slow things down enough to see what was going on,

If the code was working correctly the lines should fill in order, but without the halt it would be hard to see what order they were filling.

The GetNextLine and GetScreenPos functions here were taken from ChibiAkumas, and used in GrimeZ80 and ChibiAliens, they definately can be used as the starting point for Sprites!

Now you may need to do some clipping or transparency, but these functions can be the first stage in that, working out what address in memory you need to write to (or read for transparency)
Chibi Akuma(s) Comedy-Horror 8-bit Bullet Hell shooter! // 「チビ悪魔」可笑しいゴシックSTG ! // Work in Progress: ChibiAliens

Interested in CPU's :Z80,6502,68000,6809,ARM,8086,RISC-V
Learning: 65816,ARM,8086,6809

User avatar
akuyou
Posts: 192
Joined: Mon Apr 22, 2019 3:19 am

Re: CPC Plotting pixels

Post by akuyou » Tue Apr 30, 2019 4:16 am

A youtube user has left the following comment:
I've been trying to follow this, but the example code is just way too complicated in the way it branches off to so many external files and functions. I get that it's efficient to use prewritten code with these techniques but for a beginner just trying to get a grip on what's happening it's far too confusing and it makes it very hard to visualise what's meant to be going on. All I want to do is draw a simple 8x8 sprite to the screen.
It's a fair comment, so I've made the following example using the same functions above, this should just paste into Winape and run with Call &8000, and it should show an 8x8 sprite onscreen

Code: Select all

	ORG &8000
	ld b,2			;Xpos in bytes
	ld c,4			;Ypos in lines
	call getScreenPos
	ex hl,de
	ld hl,sourceBitmap	;Source Bitmap

	ld b,8	;height
NextLine:
	ld c,2	;width (in bytes)
	push de
NextByte:
		ld a,(hl)	;Move a byte to screen
		ld (de),a
		inc hl
		inc de
		dec c
		jr nz,NextByte	;repeat for next byte
	pop de
	ex de,hl
		call GetNextLine ;Move down a line
	ex de,hl
	djnz NextLine		;repeat for next line
	
	ret

sourceBitmap:
	db &FF,&FF
	db 0,0
	db &F0,&F0
	db &F0,&F0
	db &0F,&0F
	db &0F,&0F
	db 0,0
	db &FF,&FF

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


GetScreenPos:	;return memory pos in HL of screen co-ord B,C (X,Y)
;	; Input  BC= XY (x=bytes - so 80 across)
;	; output HL= screen mem pos
;	; de is wiped
;

;Looking at Y line (in C) - we need to take each set of bits, and work with them separately:
;YYYYYYYY	Y line number (0-200)
;-----YYY	(0-7)  - this part needs to be multiplied by &0800 and added to the total
;YYYYY---	(0-31) - This part needs to be multiplied by 80 (&50)



;YYYYY---	(0-31) - This part needs to be multiplied by 80 (&50)
	push de
		;screen is 80 bytes wide = -------- %01010000
		ld a,C		; 00000000 01010000
		and %11111000 	; -------- -YYYY---
		ld h,0
		ld l,a
				; 00000000 01010000
		
		sla l		; -------- YYYY----
		rl h
		
		ld d,h		;value is in first bit position -add it
		ld e,l	
				; 00000000 01010000
		sla e		; -------Y YYY-----
		rl d		;	    
		sla e		; ------YY YY------
		rl d
		
		add hl,de	;value is in 2nd bit position - add it
		;We've now effectively multiplied by 80 (&50)

	;-----YYY	(0-7)  - this part needs to be multiplied by &0800 and added to the total
		ld a,C
		and %00000111	
		
		rlca		;X8
		rlca
		rlca

		ld d,a		;Load into top byte, and add as 16 bit
		ld e,0

		add hl,de
		;We've now effectively multiplied by 8

	;Screen Base
		ld a,&C0	;Add the screen Base &C000
		ld d,a

	;X position
		ld e,B		;Add the X pos 
		add hl,de
	pop de
	ret 		;return memory location in hl
	
GetNextLine:
	push af
		ld a,h		;Add &08 to H (each CPC line is &0800 bytes below the last
		add &08
		ld h,a
			;Every 8 lines we need to jump back to the top of the memory range to get the correct line
			;The code below will check if we need to do this - yes it's annoying but that's just the way the CPC screen is!
		bit 7,h		;Change this to bit 6,h if your screen is at &8000!
		jp nz,GetNextLineDone
		push bc
			ld bc,&c050	;if we got here we need to jump back to the top of the screen - the command here will do that
			add hl,bc
		pop bc
	GetNextLineDone:
	pop af
	ret
Chibi Akuma(s) Comedy-Horror 8-bit Bullet Hell shooter! // 「チビ悪魔」可笑しいゴシックSTG ! // Work in Progress: ChibiAliens

Interested in CPU's :Z80,6502,68000,6809,ARM,8086,RISC-V
Learning: 65816,ARM,8086,6809

Indyuk2000
Posts: 9
Joined: Wed Apr 24, 2019 11:59 pm

Re: CPC Plotting pixels

Post by Indyuk2000 » Tue May 07, 2019 4:29 pm

Hi Keith

I was browsing through the CPCWiki forum the other day and came across a post in which someone showed a firmware function which calculated the screen address based on x/y coordinates. The call was to &BC1D – Have you ever come across this? I immediately looked it up in the firmware documentation to see how it can be used. Knocked up some basic assembler code to see how it worked and I was quite impressed. I then immediately thought, hold on, this is probably going to be slow because it uses the firmware but, to my surprise it wasn’t at all.

I put together some code which mimicked your example and ran a very crude benchmark (using a stopwatch app) and I was very impressed how fast it was. In fact it I couldn’t really see any speed difference between your version and the firmware one.

Here’s the code I used;

Code: Select all

org &8000

	ld de,100	;xpos start
	ld hl,199	;ypos start

Again:
	
	push de		;save xpos
	push hl		;save ypos
	
	call &bc1d	;convert x/y to screen address and put destination address in HL
	
	ld (hl),255	;plot pixel to screen address

	call &BD19 	;wait for v-sync
		
	pop hl		;get current ypos
	pop de		;get current xpos

	dec l		;decrease ypos (by one)

	jr nz,Again	;check if zero set. if not repeat..

	ret		;..else return to BASIC
In your experience where would you think the firmware version will be lacking?

User avatar
akuyou
Posts: 192
Joined: Mon Apr 22, 2019 3:19 am

Re: CPC Plotting pixels

Post by akuyou » Tue May 07, 2019 9:55 pm

I've not used that particular function, so I will have to talk more generally

1. Chibi Akumas used to use the firmware text functions to get the 'highscore' onscreen... they were slow... horrendously so - I moved to my unoptimized sprite routines and the speedup was amazing - that's not to say they won't be OK for a simple game, but for an arcade game like ChibiAkumas it was a no-go... with Chibiakumas I had to do a lot of optimization, and every single command counted, so if not using the firmware saved just a few ticks, then that was what I did - that's not the case for other projects

2. if you use any firmware functions, then you have to play by the firmaware rules - ChibiAkumas uses ALL the registers and a custom interrupt handler - the firmware requires the shadow BC to be unaltered, otherwise it will crash in most cases - the firware also uses ram from around &A500-BFFF for various system variables - ChibiAkumas uses that area for the second screen buffer.

What I'm trying to say is, You should certainly use the firmware functions if they help you, but once you do then you have to work within the firmware limits... not that that's a problem, as you can always write your own replacement routines if you find the firmware is too limited later on... that's what I did with ChibiAkumas.
Chibi Akuma(s) Comedy-Horror 8-bit Bullet Hell shooter! // 「チビ悪魔」可笑しいゴシックSTG ! // Work in Progress: ChibiAliens

Interested in CPU's :Z80,6502,68000,6809,ARM,8086,RISC-V
Learning: 65816,ARM,8086,6809

Post Reply

Return to “Amstrad CPC Assembly Programming”