CPC Pinball Dreams

Previous topic - Next topic

erico

This is superb!
http://www.indieretronews.com/2016/10/pinball-dreams-preview-fantastic-amiga.html

Does anyone here have the original computer still running?

Ian Price

That really is awesome. I got rid of my CPC many years ago. Still miss it from time to time.
I came. I saw. I played.

Paul Smith

Intro music is changing and scoreboard for final release.Only 50℅ of CPU used and 40℅ of it capabilities according to Rhino.
Still look on eBay but most have local pick up or £30 delivery, not tested seen as sold.
It's a shame there's no kickstarter for the CPC like there was for the spectrum. The C64 had one for PCB and case but supply your own chips.
Amstrad CPC 6128, ATARI STE.
Acer  SW5-173 & AMD RYZEN 7,RTX 3060TI

mentalthink

Just I played in WinApe and it's awesome, and you don't know what comes... Street Fighter2 but , take a look to this images... it's real I meet the guys are doing this...

This is a very old video, but seems now is better, the guy tell me this not a fake video, some people said is a fake... really impressive.. !!!

https://www.youtube.com/watch?v=iJXwm4zPQCg

bigsofty

Excellent stuff, would really like a coding post-mortem on this, scrolling was a pretty hard trick on the CPC to pull off.
Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)

Ian Price

Scrolling wasn't difficult on the CPC. Smooth scrolling was though! :P

This would be helped by the reduced screen size, due to black borders on the sides and the high score display.
I came. I saw. I played.

Paul Smith

Quote from: bigsofty on 2016-Oct-30
Excellent stuff, would really like a coding post-mortem on this, scrolling was a pretty hard trick on the CPC to pull off.

It's in Spanish, but this is the code used.
Way to advanced for me.

http://www.cpcwiki.eu/forum/games/pinball-illusions-new-game-from-batman-group!!/?action=dlattach;attach=20854

Code (glbasic) Select

; SCROLL VERTICAL CON MARCADOR ARRIBA
; ===================================

; SUBRUTINAS / FUNCIONES ------------------------------

; * init -> Inicializacion de parametros segun el tipo de CRTC.

; * init_scroll -> Inicializacion del scroll

;; Entrada:

; A = Posicion inicial del scroll
; HL = Rutina a la que se llamara por interrupciones cada 50hz (<3073c o 48 scanlines)

; * scroll -> Hace scroll

;; Entrada:

; A = Numero de pixeles a scrollar, <0 = de arriba abajo, >0 = de abajo arriba

;; Salida:

; Flag C = 1 si necesita dibujarse una nueva fila de scroll
; HL = Puntero a donde se debe dibujar la fila de tiles del scroll cuando flag c = 1
;;      Notas:
; - SCROLL_NEXT_ROW_BASE_PTR define la direccion base de este parametro.
; - Con doble buffer, la fila de tiles hay que dibujarla en ambos buffers.
; - Para evitar saltos, conviene no dibujar toda la fila de tiles a la vez.
;   Por ejemplo, si el scroll es pixel a pixel, se necesita dibujar una
;   fila de 32 tiles cada 8 scrolls, proceso que se puede dividir en 4 tiles
;   por cada nuevo scroll hasta tener que dibujar la siguiente fila. De forma
;   que el consumo de CPU necesario para dibujar toda la fila no recae en un
;   solo frame del juego.

; * toGameScreen -> Cambia del modo de pantalla standar al modo del juego

; * toStandardScreen -> Cambia del modo de pantalla del juego al modo estandar

; VARIABLES ------------------------------------------

; * scroll_y (byte) -> Posicion Y del scroll de 0 a 255, indica donde se produce el loop
;        de la memoria de video para la rutina de sprites

; FORMATO DE VIDEO DEL JUEGO -------------------------

; * Marcador arriba de 256x23 pixeles con memoria lineal direccionada en SCOREBOARD_SCREEN_PTR
;   y ocupando #5c0 bytes
; * 1 scanline invisible
; * Area del juego de 256x256 (256x248 visibles) direccionada en GAME_SCREEN_BUFFER0_PTR
;   y GAME_SCREEN_BUFFER1_PTR con doble buffer.

; PRUEBAS --------------------------------------------

; Hardware real

; - CRTC 0 = OK
; - CRTC 1 = OK
; - CRTC 2 = OK
; - CRTC 3 = OK
; - CRTC 4 = OK

; Emuladores
; - WinAPE = OK en type 0, 1, 2 y 3 (3 con "Plus PPI Emulation")
; - Arnold = OK en type 0
; - WinCPC = OK
; - CapriceR = OK
; - JavaCPC = MAL
; - CPCE = MAL


; CONFIGURACION DE ENSAMBLADO

LIST

WRITE DIRECT -1,-1,#c0

run start;, break

LET DOUBLE_BUFFER = 0 ; si se quiere con doble buffer
LET PINBALL_PREVIEW = 1 ; preview pinball dreams

; CONSTANTES

SCOREBOARD_SCREEN_MODE EQU 1
GAME_SCREEN_MODE EQU 0

SCOREBOARD_SCREEN_PTR EQU #40 ; puntero a la memoria de video del marcador
GAME_SCREEN_BUFFER0_PTR EQU #C000 ; puntero a la memoria de video del juego
GAME_SCREEN_BUFFER1_PTR EQU #8000 ; puntero al doble buffer de la memoria de video del juego

SCROLL_NEXT_ROW_BASE_PTR EQU GAME_SCREEN_BUFFER0_PTR ; base del puntero que se devuelve con la siguiente fila del scroll para dibujar los tiles

SCOREBOARD_SCREEN_REG12 EQU SCOREBOARD_SCREEN_PTR/#400 AND %0110000
SCOREBOARD_SCREEN_REG13 EQU SCOREBOARD_SCREEN_PTR/2 AND #ff
GAME_SCREEN_BUFFER0_REG12 EQU GAME_SCREEN_BUFFER0_PTR/#400 AND %0110000
GAME_SCREEN_BUFFER1_REG12_XOR EQU GAME_SCREEN_BUFFER1_PTR/#400 AND %0110000 XOR GAME_SCREEN_BUFFER0_REG12

NOLIST

; MACROS

read "MACRO_waitCycles.asm"
read "MACRO_setCRTCReg.asm"

LIST

        ORG #3000

__ini

start ; AQUI EMPIEZA LA EJECUCION

NOLIST

di
        LD hl,#C9FB
        LD (#38),hl

ld sp,#3000

IF PINBALL_PREVIEW

SET_CRTC_REG 1,0 ; apaga la pantalla mientras transfiere los datos para que no se vea morralla

ld hl,pinball_board
ld de,GAME_SCREEN_BUFFER0_PTR
ld bc,#4000
ldir

ld hl,pinball_sb
ld de,SCOREBOARD_SCREEN_PTR
ld bc,pinball_board-pinball_sb
ldir

ENDIF

; inicializacion
call init

; inicializacion del scroll
ld a,0 ; posicion y inicial
ld hl,myMusicPlayer ; rutina a la que se llamara por interrupciones cada 50hz (< 3073c o 48 scanlines)
call init_scroll

; pone el modo de pantalla del juego
call toGameScreen

; loop de prueba de scroll
mainLoop
call wVb

; scrolla 1 pixel de arriba abajo
ld a,-1
call scroll
jr nc,mainLoop

; debe dibujar una nueva fila de tiles en hl
; ...

jr mainLoop

; Rutina que se ejecuta por interrupciones cada 50hz, normalmente player de musica (< 3073c o 48 scanlines).

myMusicPlayer

; ...

ret

; Inicializacion (ajustes de parametros segun el tipo de CRTC) -------------------------

init
call getCRTCType ; averigua el tipo de CRTC
or a
jr z,CRTCType0_2 ; type 0
cp 2
jr z,CRTCType0_2 ; type 2

CRTCType1_3_4

; type 1, 3 y 4 (TODOS PROBADOS)

ld a,30+32-1
ld (crtc_param1+1),a
ld hl,myInt1_crtc1
ld (crtc_param2+1),hl
ld a,0
ld (crtc_param3+1),a ; acaba en _y_bottom_adjust
ld a,1;0;1
ld (crtc_param4+1),a ; reg7 al final
ld a,0;8;0
ld (_y_bottom_adjust+1),a ; reg5 abajo
ld a,22-1
ld (crtc_param5+1),a ; reg4
ld a,1
ld (crtc_param6+1),a ; reg5
ld a,9
ld (crtc_param7+1),a

; ld a,0
; ld (vlc_selected+1),a

ret

CRTCType0_2

; type 0, 2 (SOLO PROBADO TYPE 2)

ld a,30+32
ld (crtc_param1+1),a
ld hl,myInt1_crtc0
ld (crtc_param2+1),hl
ld a,8
ld (crtc_param3+1),a
ld a,0
ld (crtc_param4+1),a
ld a,7
ld (_y_bottom_adjust+1),a
ld a,22
ld (crtc_param5+1),a
ld a,0
ld (crtc_param6+1),a
ld a,8
ld (crtc_param7+1),a

; ld a,1;0
; ld (vlc_selected+1),a

ret

; Inicializa el scroll ---------------------

; entrada

; a = posicion inicial del scroll
; hl = rutina a la que se llamara por interrupciones cada 50hz (< 3073c o 48 scanlines)

init_scroll
ld (my50hzRutine_call+1),hl

IF DOUBLE_BUFFER

ld hl,double_buffer_reg12+1
ld (hl),GAME_SCREEN_BUFFER0_REG12

ENDIF

call update_scroll_pos ; establece la posicion inicial del scroll
ret

; Scrolla x pixeles ---------------------

; entrada:

; a = numero de pixeles a scrollar, <0 = de arriba abajo, >0 = de abajo arriba

; salida:

; flag c = 1 si necesita dibujarse una nueva fila de scroll
; hl = puntero a donde se debe dibujar la fila de tiles del scroll si flag c = 1

scroll
or a
ret z ; sin scroll

ld b,a
ld a,(scroll_y)
ld c,a ; guarda la pos y anterior en c
add a,b

update_scroll_pos

; calcula el desplazamiento del scroll

ld (scroll_y),a
ld b,a ; guarda la pos y actual en b
and 7
add a
ld hl,scroll_pixels_ptr
add l
ld l,a
ld (scroll_jump_ptr+1),hl ; guarda el puntero

ld a,b
and %11111000
ld l,a
xor a
sla l
rla
sla l
rla

ld d,a

double_buffer_reg12
ld e,GAME_SCREEN_BUFFER0_REG12 ; pantalla del juego
or e
ld h,a
ld (game_video_regs+1),hl

IF DOUBLE_BUFFER

ld a,GAME_SCREEN_BUFFER1_REG12_XOR ; conmutacion del doble buffer
xor e
ld (double_buffer_reg12+1),a

ENDIF

ld a,b
xor c
and %11111000
ret z

; calcula el puntero a la nueva fila de tiles
ld h,d
add hl,hl
ld de,SCROLL_NEXT_ROW_BASE_PTR+#800-#40
add hl,de
res 3,h

scf
ret

; Cambia del modo de pantalla standar al modo de pantalla del juego -----

; (produce un frame sin verse nada antes de aparecer la pantalla)

toGameScreen
di

ld a,#C3
ld (#38),a
ld hl,toGameScreen_int0
ld (#39),hl

call wVb

SET_CRTC_REG 9,0 ; caracter de 1 pixeles de alto
SET_CRTC_REG 3,#8e
SET_CRTC_REG 2,#2a
SET_CRTC_REG 7,127

ld bc,#bc04
out (c),c
crtc_param1
ld bc,#bd00+62 ; 61 en crtc 1
out (c),c ; reg4

SET_CRTC_REG 1,#20

ei

; pantalla invisble durante la transicion

ld bc,#7f8c + 2
out (c),c ; mode 2

ld a,(color0)
ld de,#1001
out (c),d
out (c),a ; borde
defb #ed,#71 ; 4c OUT (C),0 selecciona reg 0
out (c),a ; color 0
out (c),e
out (c),a ; color 1

; pone el resto de colores

ld hl,color3_game+1
ld a,4
set_colors
out (c),a
inc b
outi
inc a
cp d
jr nz,set_colors

ret

; Int 0 (inicializacion)

toGameScreen_int0
push hl

ld hl,toGameScreen_int1 ; myInt1_crtc1 en crtc 1
ld (&39),hl

pop hl

ei
ret

; Int 1 (inicializacion)

toGameScreen_int1
push af
push bc

SET_CRTC_REG 4,22

ld bc,myInt2
ld (&39),bc

crtc_param2
jp myInt1_crtc0

myInt1_crtc0
WAIT_CYCLES 43
myInt1_crtc1

WAIT_CYCLES 28

SET_CRTC_REG 9,7 ; caracter de 8 pixeles de alto

pop bc
pop af

ei
ret

; Vuelve al modo de pantalla standar

toStandardScreen
call wVb
di

SET_CRTC_REG 7,#1e ; caracter de 1 pixeles de alto
SET_CRTC_REG 5,0
SET_CRTC_REG 2,#2e ; pos x
SET_CRTC_REG 1,#28 ; 40 caracteres
ld bc,#bc04
out (c),c
crtc_param7
ld bc,#bd08
out (c),c ; reg4
SET_CRTC_REG 6,1
SET_CRTC_REG 12,#30
SET_CRTC_REG 13,#0 ; pantalla en #c000

        LD hl,#C9FB
        LD (#38),hl
ei
halt ; halt 0
halt ; halt 1

SET_CRTC_REG 6,#19 ; 25 caracteres alto

halt ; halt 2
di

SET_CRTC_REG 4,#26

ret

; Interrupciones -----------------------------------------------------

; Interrupcion 0 y 1

myInt0
push af
push bc
push hl
push de

SET_CRTC_REG 9,2 ; caracter de 1 pixeles de alto
SET_CRTC_REG 7,127

ld bc,#bc05
out (c),c
crtc_param6
ld bc,#bd00
out (c),c

SET_CRTC_REG 12,SCOREBOARD_SCREEN_REG12
SET_CRTC_REG 13,SCOREBOARD_SCREEN_REG13
SET_CRTC_REG 9,0 ; caracter de 1 pixeles de alto
SET_CRTC_REG 6,23 ; 23 pixeles de alto marcador

; modo de pantalla del marcador
ld bc,#7f8c + SCOREBOARD_SCREEN_MODE
out (c),c

ld hl,color1_sb ; 3c
ld de,#1001 ; 2c
ld a,(color0) ; 4c

defb #ed,#71 ; 4c OUT (C),0
out (c),a ; 4c ; color 0

out (c),d
out (c),a ; 4c ; border

out (c),e ; 2c
inc b ; 1c
outi ; 5c ; color 1

inc e ; 1c
out (c),e ; 4c
inc b ; 1c
outi ; 5c ; color 2

inc e ; 1c
out (c),e ; 4c
inc b ; 1c
outi ; 5c ; color 3

; aprovecha para hacer cosas hasta la siguiente interrupcion

push ix
push iy
ex af,af'
push af
exx
push bc
push de
push hl

my50hzRutine_call
call 0 ; < 3073c (48 scanlines)

pop hl
pop de
pop bc
exx
pop af
ex af,af'
pop iy
pop ix

game_video_regs
ld de,0

ld bc,#bc04
out (c),c
crtc_param5
ld bc,#bd00+22

        LD a,#C9
        LD (#38),a
ei
halt

di

out (c),c ; reg4 = 22

ld bc,#bc0c ; 3c
out (c),c ; 4c
inc b ; 1c
out (c),d ; 4c ; reg 12
dec b ; 1c
inc c ; 1c
out (c),c ; 4c
inc b ; 1c
out (c),e ; 4c ; reg 13

crtc_param3
ld a,8 ; 1 en crtc 1

scroll_jump_ptr
jp 0

ALIGN #10

scroll_pixels_ptr
jr vlc0
jr vlc1
jr vlc2
jr vlc3
jr vlc4
jr vlc5
jr vlc6
jr vlc7

vlc0
ld (_y_bottom_adjust+1),a ; 4c
ld a,(color0) ; 4c

WAIT_CYCLES 24-4-4

ld bc,#7f01 ; 3c selecciona color 1
out (c),c ; 4c
ld c,#8c+2 ; 2c pone mode 2 para hacer invisible la scanline para que no se vea moralla
out (c),c ; 4c ; modo 2

WAIT_CYCLES 12

SET_CRTC_REG 9,7 ; caracter de 8 pixeles de alto

dec b ; 1c
defb #ed,#71 ; 4c OUT (C),0 selecciona reg 0

ld d,#8c+GAME_SCREEN_MODE ; 2c

jr hAdjust+2

vlc1
; defs 0,0
add 1 ; 2c
ld hl,hAdjust ; 3c
ld de,#2a00+21-1 ; 3c
jr vlc_selected ; 3c
vlc2
defs 1,0 ; 1c
add 2 ; 2c
ld hl,hAdjust+1 ; 3c
ld de,#2b00+10-1 ; 3c
jr vlc_selected ; 3c
vlc3
; defs 0,0
add 3 ; 2c
ld hl,hAdjust ; 3c
ld de,#2a00+7-1 ; 3c
jr vlc_selected ; 3c
vlc4
defs 1,0 ; 1c
add 4 ; 2c
ld hl,hAdjust+1 ; 3c
ld de,#2b00+5-1 ; 3c
jr vlc_selected ; 3c
vlc5
defs 1,0 ; 1c
add 5 ; 2c
ld hl,hAdjust+1 ; 3c
ld de,#2b00+4-1 ; 3c
jr vlc_selected ; 3c
vlc6
defs 3,0 ; 3c
add 6 ; 2c
ld hl,hAdjust+3 ; 3c
ld de,#2d00+3-1 ; 3c
jr vlc_selected ; 3c
vlc7
; defs 0,0
add 7 ; 2c
ld hl,hAdjust ; 3c
ld de,#2a00+3-1 ; 3c
jr vlc_selected ; 3c

LIST
vlc_selected
jr $+3 ; en type 0 1 y 2, tb funciona $+2, en type 3 y 4 solo $+3
NOLIST
defs 5-3,0

ld (_y_bottom_adjust+1),a ; 4c
ld a,(color0) ; 4c

ld bc,#7f01 ; 3c selecciona color 1
out (c),c ; 4c
ld c,#8c + 2 ; 2c pone mode 2 para hacer invisible la scanline para que no se vea moralla
out (c),c ; 4c ; modo 2

ld bc,#bc09 ; 3c
defb #ed,#71 ; 4c OUT (C),0 selecciona reg0
inc b ; 1c
out (c),d ; 4c reg0 = d

ld d,#8c+GAME_SCREEN_MODE ; 2c

dec b ; 1c
out (c),c ; 4c selecciona reg9
ld bc,#bd07 ; 3c
out (c),c ; 4c reg9 = 7

dec b ; 1c
defb #ed,#71 ; 4c OUT (C),0 selecciona reg 0
inc b ; 1c
; crtc 0 -> VCC = 16, VLC = 0, HCC = 29
out (c),e ; 4c reg0 = e *** OJO! momento critico!!!!
; crtc 0 -> VCC = 0, VLC = 0, HCC = 02

jp (hl) ; 1c
hAdjust
defs 3,0 ; intocable

ld b,#7f ; 2c
out (c),a ; 4c ; color1 invisible

ld hl,color3_game ; 3c
defs 1,0 ; 1c

ld bc,#bd3f ; 3c
out (c),c ; 4c

; cambio de colores de la paleta

ld bc,#7f03 ; 3c
out (c),d ; 4c mode 0 (no sera efectivo hasta la proxima scanline)

out (c),c ; 2c
inc b ; 1c
outd ; 5c ; color 3

dec c ; 1c
out (c),c ; 4c
inc b ; 1c
outd ; 5c ; color 2

dec c ; 1c
out (c),c ; 4c
inc b ; 1c
outd ; 5c ; color 1

ld a,#C3 ; 2c
        ld (#38),a ; 4c
ld hl,myInt2 ; 3c
ld (&39),hl ; 5c

pop de
pop hl
pop bc
pop af

ei
ret

; Int 2 ----------------------

myInt2
push af
push bc

SET_CRTC_REG 4,30
SET_CRTC_REG 6,32 ; 256-8 pixeles de alto pantalla

ld bc,myInt3
ld (&39),bc

pop bc
pop af

ei
ret

; Int 3 (no hace nada) ----------------------

; no hace nada

myInt3
push hl

ld hl,myInt4
ld (&39),hl

pop hl

ei
ret

; Int 4 (no hace nada) ----------------------

myInt4
push hl

ld hl,myInt5
ld (&39),hl

pop hl

ei
ret

; Int 5 ----------------------

myInt5
push af
push bc

ld bc,#bc07
out (c),c
crtc_param4
ld bc,#bd00 ; (1 en crtc 1)
out (c),c ; reg7

ld bc,#bc05
out (c),c
_y_bottom_adjust
ld bc,#bd07 ; (0 en crtc 1)
out (c),c ; reg5 para compensar por abajo

ld bc,myInt0
ld (&39),bc

pop bc
pop af

ei
ret

; Librerias ----------------------------------

read "waitVBlank.asm"
read "getCRTCType.asm"

; Variables ----------------------------------

scroll_y db 0 ; posicion y del scroll (de 0 a 255)

color0 db #54
color1_sb db #5c
color2_sb db #4e
color3_sb db #40
color1_game db #44
color2_game db #5c
color3_game db #58
db #56, #46, #5e, #40, #5f, #4e, #5a, #4b, #55, #5d, #4c, #45

IF PINBALL_PREVIEW

pinball_sb
incbin "bin\pd_sb.bin"

pinball_board
incbin "bin\pd_3.bin"

__end
ENDIF

LIST

__len equ __end-__ini

; end

NOLIST
Amstrad CPC 6128, ATARI STE.
Acer  SW5-173 & AMD RYZEN 7,RTX 3060TI

bigsofty

As Ian said, scrolling was easy, the CPC actually had hardware scrolling built in but it was character scrolling and it rotated where the screen buffer drew on the frame on monitor. TBH it was a PITA for games. They seem to be using a frame interrupt to reset(trick out) the hardware scrolling at pixel vertical positions on the screen, it needs to be done every frame of the screen would 'roll' back to its default character position. I could be wrong though my Z80 is very rusty :P

Very nice coding and it's good to see the CPC still getting new games after all these years!
Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)

spacefractal

#8
This also require a newer cpc hardware with 128kb ram. I'm also heard monitors can have different timing, so it's might not been safe for all machines.

Similar timing could been done on c64 as well when a similar trick was used for safe CPU, so it's only need to draw new tiles in the edges. But it's also can fails on some machines as well.

PS. Its look a Nice port and seens they are much more far from the c64 demo version released few years ago. I'm so still have my Amiga and this game is still fun.
Genius.Greedy Mouse - Karma Miwa - Spot Race - CatchOut - PowerUp Elevation - The beagle Jam - Cave Heroes 2023 - https://spacefractal.itch.io/

Paul Smith

I'm sure Rhino said it works on all 128k machines with any CRTC even 1 , but then again that's what the Preview was for.
Not only is it scrolling it using his special Resolution at the same time, timing must be perfect for the extra colours
this effect cause the graphics flicker under emulation, I've found the new version of caprice forever is better then winape for this type of effect.

Would love to see this and Batman Forever on a real CPC with CRT but sadly my Machine died years ago.
Amstrad CPC 6128, ATARI STE.
Acer  SW5-173 & AMD RYZEN 7,RTX 3060TI