programmazione in assembly per commodore 64 e … · esempio di traduzione.c:0810 a9 02 ... sprites...
TRANSCRIPT
PROGRAMMAZIONE IN ASSEMBLY PER COMMODORE 64 E SUA APPLICAZIONE NELLA DEMOSCENE
ALESSIO COSENZA
INTERVIENE: DIEGO BARZON
TESTO
PRESENTAZIONI!
▸ Alessio Cosenza
▸ Di giorno sviluppatore servizi web / app
▸ Di notte sviluppatore su piattaforme "old school"
▸ Commodore 64, Vectrex
▸ Power Glove Ultra
TESTO
PRESENTAZIONI!
▸ Membro del gruppo Commodore 64 "Onslaught" con il nickname Elder0010
▸ Basati in Australia ma con membri in Europa e America
▸ Attivi dal 1995
▸ Cracking & Demo Group
▸ "Vandalism News" C64 Disk Magazine
▸ Operativi anche su Amiga e PC.
TESTO
CENNI DI STORIA
▸ La Demoscene Commodore è considerata l'originaria
▸ Nasce dalla Cracking Scene nei primi 80's
▸ Organizzata in gruppi di persone anonime con nicknames
▸ Scambio di informazione tramite "Snail Mail"
▸ Poca differenza tra crack intro e prime demo
TESTO
CRACK INTRO?
▸ Piccoli programmi "iniettati" prima dei giochi piratati
▸ Una sola schermata
▸ Elementi ricorrenti: logo, scrolltext, rasterbars!
▸ Max 16k (0x4000 bytes) inclusa grafica suono e codice
▸ Compatibili PAL/NTSC
TESTO
CRACK INTRO?
▸ Fairlight [FLT] #01
TESTO
CRACK INTRO?
▸ TRIAD "Raster Revenge intro"
TESTO
CRACK INTRO?
▸ Exodus #04
TESTO
CRACK INTRO?
▸ Alpha Flight #02
TESTO
FROM CRACK INTROS TO DEMOS!
▸ Effetti sempre più spettacolari e complicati
▸ Standalone, non più linkati ai giochi
▸ Utilizzo del 100% della memoria della macchina
▸ Compatibilità PAL/NTSC non più necessaria
▸ Commistione di arte e cultura informatica
▸ Demo making = disciplina hacker
STILL RULING.. IN 2016!
▸ Mekanix (Booze Design) - 2010
TESTO
STILL RULING.. IN 2016!
▸ Edge of Disgrace (Booze Design) - 2008
TESTO
STILL RULING.. IN 2016!
▸ We Are New - Fairlight [2010]
TESTO
STILL RULING.. IN 2016!
▸ Wonderland XII (Censor Design) - 2013
TESTO
C64 HARDWARE
▸ CPU: Mos Technology 6510 (8 bit / 0,985 MHz)
▸ RAM: 64Kb
▸ Video: VIC-II (16 colori)
▸ Audio: SID 6581 / 8580 (3 canali audio)
▸ 2 CIA (Complex Interface Adapter): controllo dei processi di I/O e i timer di sistema
COMMODORE 64 MEMORY MAP
C64 MEMORY MAP (FONTE: C64-WIKI.COM)
64 Kb Totali
• 0x1FFF - Kernal Rom
• 0xFFF - Char Rom / IO
• 0x1FFF - Basic Rom
• 0xAFFF - Free Ram
(DEC)
• 8kb - Kernal Rom
• 4kb - Char Rom / IO
• 8kb - Basic Rom
• 44kb - Free Ram
MOS 6510
▸ 3 Registri a 8 bit (A,X,Y)
▸ Indirizzamento a 16 bit (max 64kb)
▸ Derivato del 6502
MOS VIC-II
▸ Risoluzione max: 320x200 (hi res) / 160x200 (multicolour)
▸ Modalità testo o bitmap
▸ 16 colori
▸ 8 sprites (24x21 pixels ognuno)
▸ Raster Interrupt
▸ Smooth Scrolling
MOS VIC-II / PALETTE
▸ 16 colori contrassegnati da un valore 0x0 -> 0xF
▸ Ogni modalità grafica ha delle restrizioni
▸ Colour clash
COMMODORE 64 INSTRUCTION SET
▸ 18 Opcodes Aritmetici / Logici (ORA, AND, EOR, ADC, SBC..)
▸ 16 Opcodes per leggere o scrivere la memoria o i registri del processore / stack (LDA, STA, LDX, LDY, STX, TAX..)
▸ 22 Opcodes per effettuare salti e leggere i flags del processore (JMP, RTS, BPL, BMI, CLC..)
▸ 21 "Illegal" opcodes non documentati ufficialmente ma scoperti nel tempo (LAX, SAX, SLO, RLA, AXS..)
EX1.ASM
UN SEMPLICE ESEMPIO PER COMINCIARE!sei //fermiamo gli interrupt lda #$00 //carichiamo nell'accumulatore il valore #$00sta $d020 //scriviamo il valore in $d020 -> colore del bordo (diventa nero)
lda #$04 //carichiamo nell'accumulatore il valore #$04sta $d021 //scriviamo il valore in $d021 -> colore dello schermo (viola)jmp * //loop infinito
TESTO
TRADUZIONE DELLE ISTRUZIONI
*= $810lda #$02 sta $d020jmp *
▸ L'assembler "converte" 1:1 il sorgente con l'eseguibile
▸ Ogni istruzione è lunga da 1 a 3 bytes
▸ Esempio di traduzione
.C:0810 A9 02
.C:0812 8D 20 D0
.C:0815 4C 15 08
COMMODORE 64 CPU FLAGS (SOLO ALCUNI..)
▸ N = negative flag (1 quando il risultato di una operazione è negativo)
▸ V = overflow flag (1 su signed overflow)
▸ I = IRQ flag (quando è a 1 gli irq sono disabilitati.. quasi sempre)
▸ Z = zero flag (quando un operazione ritorna 0)
▸ C = carry flag (1su overflow unsigned)
LO SCHERMO
FONTE: CODEBASE64.ORG
Area Visibile
• Screen Area: 320x200
• Totale: 402x292
• 312 "Rasterlines"
• Registri $d011 e $d012
• Badline ogni 8 linee
• Standard line = 63 cicli CPU
• Bad line = 23 cicli CPU
LE BADLINES
▸ Ogni rasterline "dura" 63 cicli cpu (PAL)
▸ Nell'area schermo il VIC "ruba" 40 cicli ogni 8 linee per generare una badline
▸ Nelle badlines viene aggiornato il puntatore alla memoria schermo corrente
EX2.ASM
ESERCIZIETTO SUI RASTER!
* = $0810
lda #$00 tax tay jsr $1000 ; inizializzo musica
mainloop: lda #$85
wait: cmp $d012 ; confronto $d012 con #$85 bne wait ; se false, continuo a loopare
inc $d020 ; incremento il colore del bordo jsr $1003 ; play della musica dec $d020 ; decremento il colore del bordo jmp mainloop ; loop again!
EX_RASTERBARS.ASM
HELLO RASTERBARS!line_loop: lda colors,x //leggo la tabella dei colori usando il registro x come indice
cpy $d012 //attendo la rasterline giusta bne *-3 //
sta $d020 //se sono arrivato alla rasterline giusta cambio il colore del bordo
cpx #54 //loop finche' non ho finito di leggere la tabella beq play_music //se x=54 effetto finito, suoniamo la musica!
inx //incremento x -> leggero' il prossimo colore iny //incremento y -> attendero' la prossima rasterline jmp line_loop //loop sulla prossima linea!
colors:.byte $06,$06,$06,$0e,$06,$0e.byte $0e,$06,$0e,$0e,$0e,$03.byte $0e,$03,$03,$0e,$03,$03.byte $03,$01,$03,$01,$01,$03.byte $01,$01,$01,$03,$01,$01.byte $03,$01,$03,$03,$03,$0e.byte $03,$03,$0e,$03,$0e,$0e.byte $0e,$06,$0e,$0e,$06,$0e.byte $06,$06,$06,$00,$00,$00
TIMING IS THE KEY
▸ Effetti basati su features non documentate del VIC-II
▸ Manipolazione di registri del VIC al momento giusto!
▸ Il busy waiting spreca troppa cpu (siamo a < 1mhz!)
▸ Soluzione: INTERRUPTS!
COSA SONO GLI INTERRUPT?
▸ Permettono di eseguire un blocco di istruzioni in un preciso momento
▸ Eliminano la necessità del busy waiting (yeah!)
▸ Tipologia: Raster o Timer
▸ Forniscono una sorta di "multitasking"
▸ Paragonabili a una "sveglia"
RASTER INTERRUPT
▸ Generati dal VIC
▸ Il valore di $d012 e il bit 7 di $d011 decidono la linea dell'IRQ
▸ Quando il raster beam raggiunge la linea scelta in $d012/$d011 l'interrupt viene eseguito
▸ Terminato il blocco di istruzioni nell'IRQ il controllo torna nel "main"
EX3.ASM
OPTIMIZATION TIME!
▸ Possiamo scrivere l'esempio predecente facendo uso di IRQ
▸ Come: generare un raster interrupt una volta per frame per suonare la musica
▸ In questa maniera evitiamo il busy waiting e lasciamo la cpu libera per altri task!
IT'S VIC PORN TIME!
▸ Se si trova su una badline il VIC indirizza la memoria schermo e il charset corrente
▸ Il registro $d011 regola la generazione delle badline
▸ Se i bit 0/1/2 di $d011 coincidono con i bit 0/1/2 di $d012 all'inizio di una linea, viene generata una badline
▸ Utilizzando un interrupt possiamo "sincronizzarci" e manipolare la creazione delle badline per ottenere effetti nella demo!
SLIDE UN PÒ COMPLICATA, MA NE VALE LA PENA, PROMESSO!
FLEXIBLE LINE DISTANCE (FLD)
▸ Le badline segnalano al VIC "cosa" disegnare nelle prox 8 linee di grafica (fino alla prossima badline)
▸ Manipolando $d011 possiamo ritardare le badline, impedendo questo aggiornamento
▸ Questo ci permette di effettuare scrolling verticale senza toccare la memoria
▸ Questa tecnica si chiama "Flexible Line Distance"
UN EFFETTO DI ESEMPIO - FLEXIBLE LINE DISTANCE
UN EFFETTO DI ESEMPIO - FLEXIBLE LINE DISTANCE
▸ Se si trova su una badline il VIC indirizza la memoria schermo e il charset corrente (eventualmente aggiornando il puntamento)
▸ Il registro $d011 regola la generazione delle badline
▸ Se i bit 0/1/2 di $d011 coincidono con i bit 0/1/2 di $d012 all'inizio di una linea, viene generata una badline
▸ Utilizzando un interrupt possiamo "sincronizzarci" e manipolare la creazione delle badline per ottenere effetti nella demo!
UN EFFETTO DI ESEMPIO - FLEXIBLE LINE DISTANCE
start: sei
loop1: bit $d011 //Aspetto un nuovo frame bpl *-3 bit $d011 bmi *-3
lda #$13 //Ripristino $d011 (per fare l'fld piu' tardi) sta $d011
jsr CalcNumLines //Calcolo il valore di oscillazione dell'effetto
lda #$40 //busy waiting della linea giusta.. cmp $d012 bne *-3
ldx NumFLDLines beq loop1 //se vale 0 si torna all'inizio (niente effetto!)loop2: lda $d012 //Attendo la fine della rasterline.. cmp $d012 beq *-3
//a questo punto sono all'inizio della rasterline successiva clc //Faccio una linea di FLD alterando il valore dei bit 0-1-2 di $d011! lda $d011 adc #1 and #7 ora #$10 sta $d011
dex //Decremento il contatore bne loop2 //E continuo il loop finche' il contatore non e' 0!
jmp loop1 //Via al prossimo frame..
CalcNumLines: lda #0 bpl *+4 eor #$ff lsr sta NumFLDLines inc CalcNumLines+1 rts
$D011 TRICKERY
ANCORA SULLE BADLINES
▸ Moltissimi effetti sono basati sulla manipolazione di $d011 per ottenere badlines "a piacimento"
▸ Distorsione "realtime" di textures (Y-stretchers, linecrunchers)
▸ Modalità grafiche custom che superano le limitazioni standard del C64
▸ Esempi: FLI, FPP, Linecruncher
SPRITES TRICKERY IN PILLOLE
INTRODUZIONE AL MULTIPLEXING DEGLI SPRITES▸ Molti dicono: "Il C64 può gestire al massimo 8 sprites"
▸ Realtà: "L'hardware del Commodore 64 può gestire al massimo 8 sprites per rasterline"
▸ Regola: la coordinata Y di uno sprite deve essere impostata prima che il raster la raggiunga
▸ Dopo aver disegnato uno sprite possiamo però "riutilizzarlo", posizionandolo più in basso in tempo utile
▸ Possiamo anche cambiare colore e definizione per ottenere uno sprite totalmente diverso!
SPRITES TRICKERY IN PILLOLE
INTRODUZIONE AL MULTIPLEXING DEGLI SPRITES▸ Ogni volta che vogliamo riutilizzare uno sprite utilizziamo
un raster interrupt che ne aggiorna la posizione su Y
▸ su X il problema non sussiste
SOURCE COMPLETO: HTTPS://BITBUCKET.ORG/ELDER0010/TOY-MULTIPLEXER/* Questo irq viene triggerato una volta per piattaforma (quindi 3 nel nostro caso) ogni volta viene triggerato 8 linee prima della prox linea di sprite, cosi abbiamo il tempo di multiplexare tutto con calma */top_irq:
pha txa pha tya pha //salvo A,X,Y nello stack
asl $d019 //ack dell'interrupt
lda platformcmp #3beq goto_low//riposiziono gli spritejsr reposition_spriteinc platform
ldx platform
lda sprite_y,xsecsbc #8 //il prox irq sara'8 linee prima del prox spritesta $d012
jmp next_irqgoto_low:
lda #$e4sta $d012lda #$1bsta $d011
lda #<low_irq //lo byte addresssta $fffelda #>low_irq //hi byte addresssta $ffff
next_irq:.if(DEBUG_IRQ){
inc $d020ldy #30
!: deybne !-dec $d020
} pla tay pla tax pla //recupero A,X,Y dallo stackrti
IL NOME DEL GIOCO LO CONOSCETE..
ESEMPIO DI SPRITES MULTIPLEXING #1
PLAISIR DU MULTIPLEX BY JOE OF HOAXERS / WRATH DESIGNS (2013)
ESEMPIO DI SPRITES MULTIPLEXING #2
▸ Stock multicolour image + multiplexed sprites!
THE TAO OF DEMO CODING #1 - BREAKING THE RULES
▸ Moltissimi concetti della programmazione "moderna" non possono essere applicati (siamo a meno di 1mhz / 40kb di ram usabili)
▸ Codice quasi mai orientato al riuso e scritto di volta in volta
▸ Nessuna gestione degli errori: le routines fanno quello che solamente quello che si vede a schermo
▸ Self-modifying code
▸ Loop unrolling
▸ Stack "reaping"
▸ Uso di dati precalcolati
THE TAO OF DEMO CODING #2 - HOW TO DO IT ON THE 64
▸ Decidere cosa è HW based e cosa è SW based
▸ Individuare se è possibile precalcolare dati
▸ Se l'effetto richiede operazioni cycle-exact, calcolarne la fattibilità
▸ Ottimizzare "la matematica"
▸ Scrivere il codice e capire perchè non ha funzionato
▸ Ottimizzare anche "sporcando"
APPROFONDIMENTO: SELF MODIFYING CODE
Consideriamo il semplice esempio
*= $0801lda $3032sta $d020inc $d021jmp *
APPROFONDIMENTO: SELF MODIFYING CODE
Questo blocco di istruzioni in memoria è rappresentato così:
.C:0810 AD 32 30 LDA $3032
.C:0813 8D 20 D0 STA $D020
.C:0816 EE 21 D0 INC $D021
.C:0819 4C 19 08 JMP $0819
▸ Ogni istruzione è composta da 1 a 3 bytes in questa forma:
▸ OPCODE / LOaddr / HIaddr
▸ ogni opcode è associato al valore di un byte
▸ esempio: LDA = $AD, STA = $8D
APPROFONDIMENTO: SELF MODIFYING CODE
Cosa succede se alteriamo il valore del byte di un opcode?
*= $0801lda #$cesta $081B lda $3032sta $d020inc $d021jmp *
APPROFONDIMENTO: SELF MODIFYING CODE
Il nuovo blocco assemblato diventa:
Ma a runtime l'istruzione INC diventa DEC!
.C:0810 A9 CE LDA #$CE
.C:0812 8D 1B 08 STA $081B
.C:0815 AD 32 30 LDA $3032
.C:0818 8D 20 D0 STA $D020
.C:081b CE 21 D0 INC $D021
.C:081e 4C 1E 08 JMP $081E
.C:0810 A9 CE LDA #$CE
.C:0812 8D 1B 08 STA $081B
.C:0815 AD 32 30 LDA $3032
.C:0818 8D 20 D0 STA $D020
.C:081b CE 21 D0 DEC $D021
.C:081e 4C 1E 08 JMP $081E
Prima della STA $081b
Dopo della STA $081b
APPROFONDIMENTO: SELF MODIFYING CODE
SELF MODIFYING CODE - PROS & CONS
▸ PRO: Non serve replicare il codice per gestire casistiche diverse
▸ PRO: E' possibile generare interi blocchi di codice a runtime per risparmiare spazio!
▸ CONTRO: Debug MOLTO difficile
▸ CONTRO: Leggere codice degli altri ANCORA PIU' DIFFICILE!
SHOWCASE EFFETTI (BRUCE LEE)
X-ROTATOR (EDGE OF DISGRACE BY BOOZE DESIGN)
▸
SPRITE FPP (CREST & OXYRON - DEUS EX MACHINA)
REALTIME 3D CUBE(CENSOR DESIGN & OXYRON - COMA LIGHT 13)
DOUBLE TWISTER (APPARATUS BY MIRACLES)