CPCWiki forum

General Category => Programming => Topic started by: Trystan on 08:52, 06 March 23

Title: CPC Plus changing the palette in assembly
Post by: Trystan on 08:52, 06 March 23
I've recently gotten back into BASIC, which led me to z80 assembly (I've done quite a bit of 6502 but the register heavy approach of z80 is new to me) and the CPC of all the 8 bit machines looks to have a good set of capabilities for experimenting with.

I've made a program that changes the palette, draws a pricture to screen and then ends when you press space but to make it so that the firmware didn't overwrite the palette (I believe due to the palette refreshing every now and then to enable flashing colours) seems to need quite a bit of work and I wanted to make sure I hadn't missed a trick.

My current method is to use a basic loader to set himem low so there's space, set the screen mode and put some palette values into memory (at &8000) which the assembly can use. This then runs the .bin file:
  - 1, unlock plus capabilities by writing the 17 magic unlock bytes
  - 2, disable firmware interrupts by writing EI,RET to &0038 (after backing the original data up so they can be restored at the end)
  - 3, page in ASIC RAM
  - 4, write palette data directly to &6400-&641f
  - 5, page out ASIC RAM
  - 6, load picture data to video memory
  - 7, so that the program doesn't end (causing firmware to rewrite the palette) I go into an infinite loop
  - 8, but I want to be able to end the program. Using a firmware call like &BB06 rewrites my palette though so I used the keyboard scanning routine from https://www.cpcwiki.eu/index.php/Programming:Keyboard_scanning to wait for the space key
  - 9, finally restore the interrupt at &0038 after space has been hit or the cpc becomes super unstable and hangs on most commands.

Anyway, it works fine but I wanted to make sure I hadn't missed something that would prevent me from having to gut the firmware interrupts to keep my palette. So an alternative to my points 2, 8 and 9. I know I can do it by putting some B-ASIC in my loader but I'd prefer an assembly solution, if there is one.
Title: Re: CPC Plus changing the palette in assembly
Post by: andycadley on 09:45, 06 March 23
Using the Plus features from BASIC is a bit of a faff, because you essentially have to unsubscribe the firmware ticket event responsible for setting the palette colours on every frame (and managing things like flashing colours).

The easy way is just to use the B-ASIC RSXs which take care of that for you, as well as giving you a bunch of BASIC commands to handle managing all the Plus hardware.

If not, there is some code in AA somewhere I can probably dig out.
Title: Re: CPC Plus changing the palette in assembly
Post by: andycadley on 10:05, 06 March 23
Issue 114:

https://photos.app.goo.gl/mAMbAGDTFPkbKe377

CALL &8000 to disable the firmware interrupt (so Plus colours work) and &800C to restore normal behaviour.
Title: Re: CPC Plus changing the palette in assembly
Post by: roudoudou on 10:37, 06 March 23
could be cool to have this in ASM :)

(translating with mind, maybe i miss some)
enable ld hl,#8017 : ld bc,#FF01 : ld de,#8012 : jp #BCD7
disable ld hl,#8017 : jp #BCDD : xor a : ld (#B7F8),a : ret

is it ok? there is dead code at the end, maybe your picture is wrong, and this is a CALL #BCDD before the xor a
Title: Re: CPC Plus changing the palette in assembly
Post by: andycadley on 10:53, 06 March 23
Not sure, I was thinking the same thing though. Would be good to disassemble and put both versions on the wiki somewhere. Possibly with a better description of the issue than the magazine had (which is kind of wrong)
Title: Re: CPC Plus changing the palette in assembly
Post by: roudoudou on 10:55, 06 March 23
+ the value loaded to HL will probably need to be changed (with a label)
i lost my PDF about system vector so...
Title: Re: CPC Plus changing the palette in assembly
Post by: Trystan on 11:15, 06 March 23
Thanks for the ideas, I'll have a look into it later. I've got a pdf of system vectors. According to that..

&BCD7 -  KL New Frame Fly
Action -  Sets up a frame flyback event block which will be acted on whenever a frame flyback occurs
Entry - HL contains the address of the event block in the central 32K of RAM, B contains the event class. C contains the ROM select address (if any) and DE contains the address of the event routine
Exit - AF,DE and HL are corrupt and all other registers preserved

&BCDD - KL Del Frame Fly
Action - Removes a frame flyback event block from the list of routines which are run when a frame flyback occurs
Entry - HL contains the address of the event block
Exit - AF, DE and HL are corrupt and all other registers are preserved

I'm not sure what's at &B7F8 (where the event is sending xor'd a), in the pdf I have (which is for the standard cpc range, not plus) it's 3 bytes after the second set of inks but it's not labelled
Title: Re: CPC Plus changing the palette in assembly
Post by: andycadley on 14:19, 06 March 23
It disassembles as something like:

KL_NEW_FRAME_FLY equ #BCD7
KL_DEL_FRAME_FLY equ #BCDD

enable:
LD HL, eventblock
LD BC,#81FF
LD DE,routine
JP KL_NEW_FRAME_FLY
disable:
LD HL,eventblock
JP KL_DEL_FRAME_FLY
routine:
XOR A
LD (#B7F8), A
RET
eventblock:
DEFS 8

#B7F8 must be some sort of system variable related to INK flashing (grimware doesn't say what it's used for) or indicating a colour has changed or something, I'm not entirely sure. The code is just setting up a frame flyback event to reset it to zero, so presumably it fools the system into not refreshing the colour palette.
Title: Re: CPC Plus changing the palette in assembly
Post by: Trystan on 15:05, 06 March 23
Thank you both for your input, that assembly code works perfectly. 

Between enabling the new event block and disabling it the plus keeps the palette I give it and I can still use firmware calls like &bb06 to get keyboard input (which I couldn't get working with my old method of completely disabling interrupts at &0038).
Title: Re: CPC Plus changing the palette in assembly
Post by: Animalgril987 on 19:47, 06 March 23
#B7F8 is the OS 1.1 system variable "Colour Time". ( In OS 1.0 it's at #B1FD).
Title: Re: CPC Plus changing the palette in assembly
Post by: Trystan on 09:35, 07 March 23
Just to see if I could I put all of the information here into a set of RSX commands (mainly to see how it works) and it all seems to be working perfectly.

You can change colours with:
|plusenable
|flashdisable
|pageinasicram
poke #6400-601f,colours
|pageoutasicram
It should then keep the palette until you call |flashenable

Pretty sure all of this is mainly recreating B-ASIC but slower and with more rsx calls needed but it was a fun learning exercise. The next step might be looking up how to have arguments for RSX's to have a set palette command that pages in ASIC RAM, sets the colour then pages it out again.

org #8000
.kl_log_ext equ #bcd1
.kl_new_frame_fly equ #bcd7
.kl_del_frame_fly equ #bcdd

;;-------------------------------------------------------------------------------
;; install RSX

ld hl,work_space ;;address of a 4 byte workspace useable by Kernel
ld bc,jump_table ;;address of command name table and routine handlers
jp kl_log_ext ;;Install RSX's

.work_space             ;;Space for kernel to use
defs 4

;;-------------------------------------------------------------------------------
;; RSX definition
.jump_table
defw name_table            ;address pointing to RSX commands
                           ;list of jump commands associated with each command
                          
                           ;The name (in the name_table) and jump instruction
                           ;(in the jump_table), must be in the same
                           ;order.
                           ;i.e. the first name in the name_table refers to the
                           ;first jump in the jump_table, and vice versa.
jp UnlockPlus              ;routine for PLUSENABLE
jp PageInAsicRam           ;routine for PAGEINASICRAM
jp PageOutAsicRam          ;routine for PAGEOUTASICRAM
jp FlashDisable            ;routine for FLASHDISABLE
jp FlashEnable             ;routine for FLASHENABLE

;; the table of RSX function names
;; the names must be in capitals.

.name_table
defb "PLUSENABL","E"+&80     ;the last letter of each RSX name must have bit 7 set to 1.
defb "PAGEINASICRA","M"+&80     ;This is used by the Kernel to identify the end of the name.
defb "PAGEOUTASICRA","M"+&80
defb "FLASHDISABL","E"+&80
defb "FLASHENABL","E"+&80  
defb 0                     ;end of name table marker

; Code for the RSXs

UnlockPlus:
di
ld b,#bc
ld hl,PlusInitSequence
ld e,17

PlusInitLoop:
ld a,(hl)
out (c),a
inc hl
dec e
jr nz,PlusInitLoop
ei
ret

PageInAsicRam:
ld bc,#7fb8
out (c),c
ret

PageOutAsicRam:
ld bc,#7fa0
out (c),c
ret

FlashDisable:
ld hl, EventBlock
ld bc,#81FF
ld de,NoFlashRoutine
jp kl_new_frame_fly
ret

FlashEnable:
ld hl,EventBlock
jp kl_del_frame_fly
ret

NoFlashRoutine:
xor a
ld (#b7f8), a
ret

EventBlock:
def 8

PlusInitSequence:
db #ff,#00,#ff,#77,#b3,#51,#a8,#d4,#62,#39,#9c,#46,#2b,#15,#8a,#cd,#ee




Title: Re: CPC Plus changing the palette in assembly
Post by: andycadley on 09:44, 07 March 23
It probably is recreating B-ASIC, but it's fun to try anyway. And the last version of B-ASIC I saw seemed to have incorporated so much extra functionality that it was doing a bit more than just letting you access the Plus hardware.
Title: Re: CPC Plus changing the palette in assembly
Post by: Trystan on 11:33, 07 March 23
A bit more playing around and I've now made a |setpal command, which pages in ASIC RAM, sets a colour then pages it out again automatically so there's a few less commands needed. 

I think that'll do on this for now. The actual colour setting is probably slower than it needs to be but it seems to work. the format is |setpal, ink (0-31), red (0-15), green (0-15), blue (0-15).

Inks 0-15 set pen inks, ink 16 sets the border and inks 17-31 set sprite colours.

I'm really impressed with the RSX system, it seems super useful.

org #8000

.kl_log_ext equ #bcd1
.kl_new_frame_fly equ #bcd7
.kl_del_frame_fly equ #bcdd

;;-------------------------------------------------------------------------------
;; install RSX

ld hl,work_space ;;address of a 4 byte workspace useable by Kernel
ld bc,jump_table ;;address of command name table and routine handlers
jp kl_log_ext ;;Install RSX's

.work_space             ;;Space for kernel to use
defs 4

;;-------------------------------------------------------------------------------
;; RSX definition

.jump_table
defw name_table            ;address pointing to RSX commands

                           ;list of jump commands associated with each command
                          
                           ;The name (in the name_table) and jump instruction
                           ;(in the jump_table), must be in the same
                           ;order.

                           ;i.e. the first name in the name_table refers to the
                           ;first jump in the jump_table, and vice versa.

jp UnlockPlus            ;routine for PLUSENABLE
jp SetPal            ;routine for SETPAL
jp FlashDisable            ;routine for FLASHDISABLE
jp FlashEnable             ;routine for FLASHENABLE

;; the table of RSX function names
;; the names must be in capitals.

.name_table
defb "PLUSENABL","E"+&80     ;the last letter of each RSX name must have bit 7 set to 1.
defb "SETPA","L"+&80     ;This is used by the Kernel to identify the end of the name.
defb "FLASHDISABL","E"+&80
defb "FLASHENABL","E"+&80  

defb 0                     ;end of name table marker

; Code for the RSX routines

UnlockPlus:
di
ld b,#bc
ld hl,PlusInitSequence
ld e,17
PlusInitLoop:
ld a,(hl)
out (c),a
inc hl
dec e
jr nz,PlusInitLoop
ei
ret

SetPal:
; format |setpal,ink,r,g,b
; ix+0 = b
; ix+1 = -
; ix+2 = g
; ix+3 = -
; ix+4 = r
; ix+5 = -
; ix+6 = ink
; ix+7 = -
; check if we have 4 arguments, if not return (we probably should also print a message)
; we should probably also check arguments are 8 bit but for now just take the lower nibble of each
cp 4
ret nz

; page in ASIC RAM
ld bc,#7fb8
out (c),c

; set palette
; load address
ld h,#64 ;high byte for palette write
ld l,(ix+6) ;ink number
sla l ;double the low byte as two bytes per colour
ld a,(ix+4) ;b
sla a ;shift to upper nibble
sla a
sla a
sla a
add a,(ix+0) ;add r
ld (hl),a ;write
inc l ;next address
ld a,(ix+2) ;g (doesn't matter what's in the upper nibble)
ld (hl),a ;write

; page out ASIC RAM
ld bc,#7fa0
out (c),c
ret

FlashDisable:
LD hl, EventBlock
LD bc,#81FF
LD de,NoFlashRoutine
JP kl_new_frame_fly
ret

FlashEnable:
LD hl,EventBlock
JP kl_del_frame_fly
ret

NoFlashRoutine:
xor a
ld (#b7f8), a
ret

EventBlock:
defs 8

PlusInitSequence:
db #ff,#00,#ff,#77,#b3,#51,#a8,#d4,#62,#39,#9c,#46,#2b,#15,#8a,#cd,#ee


Title: Re: CPC Plus changing the palette in assembly
Post by: Trystan on 14:36, 07 March 23
One final question while I'm here. In my test project I've currently put this in memory at #BF00 (as https://www.cpcwiki.eu/index.php/Amstrad_Whole_Memory_Guide_-_General_system_arrangement says that can be used for small programs)

That article though looks to be for the 464 so I wanted to make sure there weren't going to be any conflicts (as http://www.cpcalive.com/docs/amstrad_cpc_6128_memory_map.html lists it as the machine stack) and it'd be safer to put it at the top of BASIC memory and reduce himem a bit.
Title: Re: CPC Plus changing the palette in assembly
Post by: andycadley on 15:01, 07 March 23
The machine stack probably doesn't go down that low, so it's probably safe. Unless you're planning on running a lot of recursive assembly code.  :laugh:

Lowering HIMEM and putting it there is another option, but then you've still really got to guess what a suitable value of HIMEM is as a machine with a lot of sideways ROMs will have lowered this further than a stock machine anyway. At some point you're always just plucking numbers out of thin air. 
Title: Re: CPC Plus changing the palette in assembly
Post by: BSC on 17:47, 07 March 23
Quote from: Trystan on 14:36, 07 March 23One final question while I'm here. In my test project I've currently put this in memory at #BF00 (as https://www.cpcwiki.eu/index.php/Amstrad_Whole_Memory_Guide_-_General_system_arrangement says that can be used for small programs)
Back in the days, I liked to use the memory area at &afxx very often. Also reset-safe and I think that, at least on my 464, it was more or less not used by the OS/basic.

It seems that you are starting your assembly journey with some RSX commands..? That's nice to see because when I was looking at your first post today, this kl_log_ext thing at the beginning suddenly made me realize that the first assembly code I have ever written was also some RSX commands. Boy was I proud :)
Title: Re: CPC Plus changing the palette in assembly
Post by: Longshot on 10:49, 09 March 23
Quote from: andycadley on 14:19, 06 March 23It disassembles as something like:

KL_NEW_FRAME_FLY equ #BCD7
KL_DEL_FRAME_FLY equ #BCDD

enable:
LD HL, eventblock
LD BC,#81FF
LD DE,routine
JP KL_NEW_FRAME_FLY
disable:
LD HL,eventblock
JP KL_DEL_FRAME_FLY
routine:
XOR A
LD (#B7F8), A
RET
eventblock:
DEFS 8

#B7F8 must be some sort of system variable related to INK flashing (grimware doesn't say what it's used for) or indicating a colour has changed or something, I'm not entirely sure. The code is just setting up a frame flyback event to reset it to zero, so presumably it fools the system into not refreshing the colour palette.

Creating an event to block the "color time" variable (&B7F8 (or &B1F2)) penalizes the CPU.

A better solution is shown here:
https://www.cpcwiki.eu/forum/programming/preventing-ink-blink-by-firmware/msg153776/#msg153776

This solution consists in disabling the Event Block "Set Inks" (&B7F9 (or &B1FE 464)) via KL DEL FRAME FLY (&BCDD)
This disables the BORDER and INK functions :

ld hl, &b7f9 ; &b1fe on cpc 464
call &bcdd
Powered by SMFPacks Menu Editor Mod