This is superb!
http://www.indieretronews.com/2016/10/pinball-dreams-preview-fantastic-amiga.html
Does anyone here have the original computer still running?
That really is awesome. I got rid of my CPC many years ago. Still miss it from time to time.
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.
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
Excellent stuff, would really like a coding post-mortem on this, scrolling was a pretty hard trick on the CPC to pull off.
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.
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
; 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
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!
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.
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.