Nes-emulaattori ja nippelitietoa nesistä.

Jaa meneillään olevat projektisi tai valmiit pelit muun yhteisön kanssa täällä.
Post Reply
atomimalli
Moderator
Moderator
Posts: 227
Joined: Wed Aug 29, 2007 3:55 pm

Nes-emulaattori ja nippelitietoa nesistä.

Post by atomimalli »

Aloitin muutama vuosi sitten nes-emulaattoria coolbasicilla, mutta se jäi kesken muutaman bugin ja yhteistyön puutteen vuoksi. Sain äskettäin kauan epäkunnossa olleen tietokoneen pälle ja jatkoin projektia. Nyt taas hiipui innostus, mutta katson parhaaksi esitellä projektin virallisesti ja antaa uudestaan mahdollisuuden muidenkin osallistua siihen. Tästä seuraa siis kattava selostus nesin toiminnasta emulaattorin kannalta, emulaattorin toteutuksesta ja toiminnasta, sekä sen suunnitelmista. Kysymyksiä saa esittää ja päivityksiä lähettää pätsiformaatissa(lisätietoa lopussa jos muistan pistää) muutoksien seuraamisen ja mergeyksen helpottamiseksi ja valmiina koodina rinnalla.

Koodia on tähän mennessä kertynyt vähän päälle 600 riviä. Se on aika tiivistä. Vähän ilmaa on jänyt kohtiin, joita ei ole vielä tehty, mutta on otettu silti huomioon.

Nykytilanne lyhyesti:
Vain kaksi romia toimii tiedetysti: fighter f8000 ja zelda title screen simulator. Jälkimmäinen toimii täysin spritejä lukuunottamatta ja ensimmäisestä toimii valikko ja pelin osalta tausta.Mikään ei toimi vielä pelattavasti. Fps on noin yksi, useinmiten alle sen. Nopeammilla koneilla on potentiaalia yksinkertaisempien pelien osalta jopa 60fpsään, mutta tavoitefps on noin kymmenen.
Pääoptimointikeino tulee olemaan HLE eli high level emulation. Se meinaa tiettyjen komentosarjojen korvaamista suoraan niiden toteuttamisella komennontarkan emuloinnin sijaan. Project64 on tunnettu hle:n hyödyntäjä. Se sai muistaakseni nelinkertaistettua suorituskykynsä sillä.
Tällä hetkellä noin kaksi kolmasosaa virallisista komennoista on lisätty. Värit toimivat. Taustan piirto toimii. Spritet eivät toimi. Ohjaimen luku antaa randomin nappiyhdistelmän. Yhtäkään mapperia ei ole lisätty. Ne fallbackaavat NOMAPPERiin, mutta niiden ohjelmointi on otettu huomioon.

Emulaattorin rakenne

Käynnistys.
-Emulaattori analysoi romin, merkitsee mapperin ja muut tiedot ylös, lataa ohjelman omaan muistipalaansa ja grafiikkamuistin toiseen.
-Muistia alustetaan romille, ramille, vromille ja vramille. spriterami puuttuu tällä hetkellä, kuten kaikki muukin niihin liittyvä.
-Alustetaan kaikki prosessorin ja näytönohjaimen tarvitsemat globaalit.
-Ladataan paletti ja valmistellaan maskit optimoitua renderöintitapaa varten, sekä varataan kuva ruudulle piirtoa varten.

Emulointi.

Prosessorin emulointi tapahtuu tapahtuu isolla selectillä komento kerrallaan. Loopin alussa luetaan komento ja select valitsee sen komennon koodin.
Koodin kuuluu lisätä program counteria komennon viemä määrä tavuja ja syklilaskuria myös oikea määrä. Ne ilmenevät 6502-prosessorin dokumentaatioista. Olen käyttänyt tätä: http://www.obelisk.demon.co.uk/6502/reference.html
esimerkki:

Code: Select all

            Case 169'A9 LDA Immediate
                A=readmem(pc+1)
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+2
                cycle+2
Komento LDA eli A-rekisterin asetus muistista Immediate-osoitustavlla. Osoitustavoista myöhemmin lisää, mutta immediate tarkoittaa sitä että arvo seuraa heti komennon perässä, eli osoitteessa pc+1. PC on se kohta, missä ohjelmassa ollaan menossa. koska komento oli kaksitavuinen, niin pc:iin lisätään 2. komento vie myös kaksi sykliä prosessoriaikaa, joten siihenkin lisätään kaksi. tuo mystinen flagirivi muuttaa prosessorissa olevaa muutamaa flagia komennon vaatimilla tavoilla. Muut komennot voivat käyttää niitä hyväksi esimerkiksi vertailussa. Miinuslaskussa esimerkiksi Z(=Zero) kertoo että olivatko luvut samat, kosta silloin lasku meni tasan. N taas on että tuliko negatiivinen. Negatiivisuus käy ilmi eniten merkitsevästä bitistä, joten shr on nopein tapa siirtää se tieto flagiin. Vertailuoperaattori voi käyttää myös tätä flagia. Näin nesillä hoitu iffit sun muut, silmukat vaikka.
Yleensä kirjoitan myös heksan ja komennon tiedot kommenttiin.

Jos komentoa ei ole lisätty, niin emulaattori printtaa komennon punaisena sekä desimaalina että heksana.

Code: Select all

            Default'jos komentoa ei löytynyt
                cycle+1
                pc+1
                Color 255,0,0
                Print op+" "+Right(Hex(op),2)
                'Wait 2000
debugggausta auttamaan waitin voi epäkommentoida tai komentoiin voi lisätä omi printtejä tai setwindoweita ja waitteja.

Prosessorin emuloinnin jälkeen on tällainen pätkä:

Code: Select all

    'Print op+" "+Right(Hex(op),2)
    Color 255,255,255
    ppu()
    'printstatus()
    'If N>1 Or N<0 Then Wait 5000
    'WaitKey
    'If KeyDown(cbkeyp) Then printstatus()
    'If Not KeyDown(cbkeyspace) Then WaitKey' Else Wait 1'50
    //vblankin generointi
    If cycle<4 Or cycle>(cycle2+1789773/60) Or KeyDown(cbkeyv) Then  cycle2=cycle:ppu_vblank=1:Print "vblank"':Wait 2000
Se on suurimmaksi osaksi kommentoitu nopeuden vuoksi, mutta debuggausta auttamaan voi laittaa komennon ja prosessorin tilan näkymän joka komennolla ja waitkeyn komentojen välille.
Vblankin generointi tarkoittaa sitä että kuva piirretään joka framen lopuksi tai poikkeuksellisesti silloin kun painetaan v-kirjainta. Tuosta voisi ehkä poistaa cycle2-jutun, se on siinä vain sen vuoksi että television päivitystaajuus ei mene tasan prosessorin taajuuden kanssa.
ppu() piirtää ruudun jos vblank on tullut. Sitä kutsutaan joka kierroksella sen vuoksi, että jotkut nes-ohjelmat muokkaavat videomuistia piirtämisenkin aikana tiettyjen tulosten saamiseksi. optimoitu rendermetodi ei toistaiseksi tue mitään niistä eikä sillä ole mahdollisuuksiakaan toteuttaa niistä kaikkia, mutta rendermethod0lla on mahdollisuus tukea niitä. Nyt kuitenkin koko ruutu piirretään vain vblankin yhteydessä, joten se on siellä melkein turhaan.

Muistin luku:
Nesissä on enemmän muistiosotteita, kuin muistia. Lisäksi muistialueet voivat osoittaa hyvin erilaisiin juttuihin ja monet muistipaikat osoittavatkin samaan. Osa muistipaikoista ei edes ole varsinaista muistia, vaan toimittavat jotain muuta tehtävää, kuten äänen tai näytön ohjaus tai ohjaimen tilan luku. Siksi muistin lukuun tarvitsee oman väliohjaimen. Sen tehtävää hoitaa readmem(). Siellä tapahtuu myös mapperikohtainen kikkailu, mistä lisää myöhemmin.
Jos muistialuetta ei ole niin palautuu väylään viimeksi työnnetty arvo eli muistipaikan ylempi tavu.
funktio on jaettu iffeillä selkeästi muistialueisiin. siellä on rekisterien käsittely paikallaan, vaikka niitä ei olekaan erityisemmin lisätty. Mappereillekin on paikka valmisteltu.

Muistin kirjoitus:
Rakenne on sama kuin muistin luvullakin. Muistin kirjoituksessa mappereilla on kuitenkin suurempi osa. Niiden kontrollointi nimittäin tapahtuu kirjoittamalla romin osotteisiin. Niitä ei ole vielä koodattu, mutta paikka on valmiina.


ppu() eli picture processing unit eli näytönohjain:

ppu:ssa on koodattuna kaksi ruudunpiirtotapaa. bitti kerrallaan toimiva, joka mahdollistaa monimutkaisten efektien toimimisen myöhemmin. Se tosin on niin hidas ettei sitä kannata vakavissaan käyttää :P
Toinen renderöintitapa toimii taas tile kerrallaan ja melko erikoisesti. Ojelman käynnistyessä koko videomuisti esilasketaan neljäksi maskikuvaksi jokaista paletin väriä kohden. sitten piirtäessä tehdään neljä palettikuvaa, joihin on piirretty palettia vastaavat värit kullekkin palettialueelle. Sitten esilasketuilla maskeille rajataan näistä näkyvät alueet ja piirretään päälekäin lopulliseen nkyvään kuvaan. Tämä tekniikka osoittautui kyllin nopeaksi reaaliaikaiseen kuvan näyttämiseen nesin virallisilla säännöillä. Epäviralliset saavat tyytyä hitaampaan.Kenties silläkin voisi yrittää jotain hiukan vastaavaa. en kyllä tiedä että kannattaako tämä enää yksirivisillä parin pikselin kuvilla.

Nesin toiminta tarkemmin:
Prosessori
Nes käyttää muokattua versiota 6502-prosessorista. Suurin ero on, ettei se käytä 10-järjestelmää, vaikka D-flagi olisikin päällä. Muuten toiminta taitaa täsmätä. Kyse on kuuluisasta ja hyvin suositusta 8-bittisestä prosesorista.
Prosessorissa on kolme rekisteriä A,X ja Y. A on yleisrekisteri ja X:ll ja Y:llä on hiukan erikoistehtäviä ja ne ovat vähemmän yleisiä. Prosessori käyttää näitä tavallaan sisäisinä muuttujina. Niihin yleensä ladataan dataa muistista prosessointia varten ja tuupataan sitten tulokset takas jos on tarvis.
Näiden lisäksi on tilaflageja, jotka vaikuttavat joidenkin komentojen toimintaan. Ne yleensä ilmoittavat jotain laskutoimitusten tulosten laadusta ja mahdollistavat ohjelman kulun ohjaamisen niiden mukaan ja esimerkiksi useampitavuisten laskujen suorittamisen. Flageja ovat C, Z, I, D, B, V, N ja niiden komentokohtainen toiminta selviää referenssistä.

Prosessorissa on muistaakseni vähän yli 80 komentoa virallisesti, sekä muutamia epävirallisia komentoja, jotka ovat syntyneet vahingossa suunnittelematta. Tämä meinaa sitä että noin puolet komennoista on käyttökelvottomia. Ne ovat siis vapaita HLE-käyttöön.
Epävirallisista komennoista: http://wiki.nesdev.com/w/index.php/Prog ... al_opcodes
ja tietysti viralliset myös: http://www.obelisk.demon.co.uk/6502/reference.html

Osoitusmoodit adressing modes

Monesta komennosta on useita versioita, jotka osoittavat muistiin eri tavoin.
Empä jaksanutkaan kirjoittaa: tässä ovat. koodista voi katsoa myös mallia.http://www.obelisk.demon.co.uk/6502/addressing.html
Zero page on ainakin hyvä tietää. Se tarkoittaa ensimmäistä 256 tavua muistissa. Siitä on hyötyä nopeammassa muistin käsittelyssä.

Muisti
Nesin muistin tarkempi jakautuminen näkyy wikipediasta: http://en.wikibooks.org/wiki/NES_Progra ... memory_map
Päähuomiot ovat Zero pagessa, stackissa eli pinossa, rekisterialueissa, muistialueiden peilauksessa ja romissa. lisäksi ON mapperista riippuvia erikoisalueita.
Pino sijaitsee ihan normaalisti muistissa, joten sitä voi erikoistarkoituksiin muokata muutenkin kuin pinokomennoilla.
Ramia ei ole ruhtinaallisesti, joten käyttämätöntä muistia jää paljon. Tämä alue on "korjattu" osoittamalla zero pageen, stackiin ja ramiin aina vain uudestaan. kaikkiaan neljästi.

Näiden jälkeen tulee rekistereitä ja niiden peilit ja lisää reksitereitä.
Sitten tulee mapperispesifiä tavaraa ja itse ohjelmamuisti. Se on jostain syystä kahdessa palassa. Varmaan sen takia että toista voi sulavasti vaihdella mapperilla kun toista suoritetaan. Näin mahdollistuu suurienkin ohjelmien suoritus vähäisellä osoitettavalla muistilla.

Aivan viimeisillä muistipaikoilla opn pari erikoisosoitetta. Ne ovat ohjelmaromissa. Tämän vuoksi yksimuistipankkinen ohjelma ladataan aina jäljempään pankkiin jos oikein muistan.
Ne ovat muistiosoitteita eri tapauksien käsittelyyn. Yksi niistä kertoo, mistä ohjelman suoritus pitää aloittaa.

Rekisterit
Nesillä on kaksi settiä rekistereitä muistialueilla $2000–$2007 ja $4000–$401F
niiden tarkoitukset selviävät pääosin täältä: http://en.wikibooks.org/wiki/NES_Progra ... .2FO_ports
Rekistereitä käytetään kirjoittamalla niihin tai lukemalla niistä. Niiden käytöstä ja koodauksesta löytyy jo hiukan esimerkkiä emulaattorista. PPU:n kirjoituksesta on nimittäin osa toteutettu ja ohjaimen luku palauttaa randomin näppäinasetelman. Ohjaimen luvussa jokainen bitti tarkoittaa yhtä nappia, joten randomitavu tarkoiottaa randomia näppäinyhdistelmää.


PPU
PPUhun pääsee käsiksi kirjoittelemalla ja lukemalla tiettyjä rekistereitä. Sen muisti on erillinen prosessorin muistista. Nesissä on kahdeksan neljän värin palettia, jotka valitaan 6-bittisestä pääpaletista. Puolet niistä on taustalle ja puolet spriteille. Kaikki grafiikka koostuu 8x8 tileistä, jotka tulevat romin mukana. Taustalle voi asettaa paletin 16x16 alueita kohden. Tilejä on kaksi 256 palikan pankkia käytettävissä. Toinen niistä on taustalle ja toinen spritelle. niiden paikkaa voi vaihdella keskenään ja mapperi voi vaihtaa niitä myös. Nesin käytössä on mapperista riippuen 2 tai 4 näytöllistä muistia taustan esittämiseen. tämä mahdollistaa skrollauksen.

MAPPERIT
Mapperit ovat nes-kaseteissa olevaa omaa rautaa, joka mahdollistaa enemmän ohjelma- ja grafiikkamuistin käytön, kuin osoitettavaa muistia olisi, sekä kaikenlaisia erityiskikkoja kuten jaetun ruudun ja muuta erikoista. Niitä ohjataan kirjoittamalla ohjelmaromiin. Mappereita on satoja, mutta muutamalla yleisimmällä pääsee jo pitkälle. Niiden päätehtävä on muistialueiden osoituksen muuttelu, mutta ne pystyvät kontrolloimaan myös ruudun jakamista(=mm. alapalkki) triggeröimällä keskeytyksen ajastetusti tai jopa laajentamaan näytönohjaimen kykyjä.

HLE
Hle on tarkoitus toteuttaa esikäsittelemällä koodi ja korvata hidastavat kooinpätkät omilla komennoilla. Siihen käytetään prosessorin käyttökelvottomia komentoja, jotka ovat vapaita. Tämä mahdollistaa reaaliaikasen nes-emulaattorin coolbasicilla.
Ensimmäinen ja helpoin optimoitava on zeldatitlescreenissä olevan idleloopin korvaaminen suoraan vblankiin hyppäämiseen. Toinen on taas vblankin odotus. sitten voi varmaan kirjoittaa jonkun analysointipätkän, joka selvittää että mihin koodinptkiin kuluu eniten aikaa ja niitä korvaamalla saa sitten enemmän nopeutta.

Äänet
Nes käyttää viittä äänikanavaa. Kahta kanttiaaltoa, yhtä kolmio-aaltoa, noisea ja sampleja soittavaa kanavaa.
Kantti-aalloista voi vaihtaa taajuutta, voimakkuutta ja kantin pituuksien suhdetta. Kolmio-aallossa voi muuttaa taajuutta. Noisessa voi muuttaa päivitysnopeutta(=korkeus) ja toistoa(=metallinen ääni). Samplet ovat yksibittisiä. Niitä käytetään enimmikseen rummuissa, mutta joissan tapauksissa jopa puheessa.
Kaikki nesin äänet on tehty näitä yhdistelemällä. Yhdeltä kanavalta voi kuulua vain yksi ääni kerralla. Eri kuuloisia ääniä saa aikaan säätelemällä taitavasti kanavien eri parametreja ja kenties yhdistelemällä. Myös tremolo on yleinen keino saada enempi irti nesin äänistä. Se tarkoittaa sointujen muodostamista toistamalla sävelet nopeasti peräkkäin samalla kanavalla.

Romit
Tämä emulaattori lukee romeja ines-muodossa. Se ei välitä päätteestä, kunhan sisältö on oikein.
Formaatin dokumentaatiota löytyy täältä: http://wiki.nesdev.com/w/index.php/INES http://nesdev.parodius.com/neshdr20.txt

Heksadesimaaleista
Kaikki nes-dokumentaatio käyttää heksadesimaaleja, koska ne ovat luonnollisia bittiympäristössä. Ne menevät tasan tavujen ja bittien kanssa ja niistä näkee suoraan yksittäisten bittien asennon vain katsomalla.
Heksadesimaalijärjestelmässä on kuusitoista merkkiä kymmenen sijaan. yksi heksanumero vastaa neljää bittiä. merkit ovat 0-9 ja näiden päälle vielä A-F. Laskusäännöt ja logiikka on täysin sama kuin kymmenjärjestelmässäkin. Kymmenjärjestelmässä tulee uusi numero lukuun mukaan aina kun tullaan uuteen kymmenpotenssiin. Heksa, eli 16-järjestelmässä sama ilmiö tuleekin kuudentoista potensseilla koska numeroita on 16. Vertaa: 9->10, F->10 ja 99->100, FF->100. F opn kymmenjärjestelmässä 15, joten 10 on 16 kun muutetaan heksajärjestelmästä kymmenjärjestelmään. FF on taas 255 (16^2-1) ja 100 on sitten 256. Merkkien lisääntyessä niiden painoarvo aina 16-kertaistuu. Heksa-arvo 543 muutetaan kymmenjärjestelmään näin: 5*16^2+4*16+3. Tästä tulee 1347. AB opn taas 10*16+11=171. Sen laskikin nopeasti päässä :P
Coolbasicissa ja windowsin laskimessa on kätevät muuntotoiminnot heksoille. Winxp:ssä muuntotoiminto löytyy kun laskimen valikosta valitseee funktiolaskin ja uudemmista taitaa löytyä ohjelmointilaskimena. Coolbasicissa on Hex()-funktio, jolla saa luvusta nopeasti heksan. Toiseen suuntaan toimiva funktio löytyy varmaan CBrepositorystä.
Heksat on nesin yhteydessä tapana merkitä edessä olevalla dollarimerkillä ettei niitä sekoita kun niissä ei ole aina kirjaimia mukana.
Coolbasic ei ymmärrä heksoja oikein vaikka se onkin ymmärtävinään. Osan se ymmärtää oikein ja osaa ei. Siksi olen käyttänyt koodissa kymmenjärjestelmää.


Todo
-Loput komennot
-Spritet
-Ohjaus
-HLE
-Mappereita
-Loogisten bittioperaatioiden vaihtaminen inlinestä esilaskettuihin. [TEHTY]

Pätsien postaaminen
Jotta ei tulisi sekaannuksia, niin minusta olisi parasta että uudet koodinpätkät lähetetään pelkilteen ja aihealuetain. Uudet komennot erikseen, mapperikoodinpätkät paikan mukaan ja rekisteritoiminnot paikan mukaan. Ei ole pakko, mutta se helpottaa koodien yhdistämistä jos useampi tekee jotain yhtäaikaa. Mukaan myös koko koodi ja kasvava versionumero ja se minkä pohjalta on tehnyt että on helppo jatkaa ja varmistaa että kaikki lisäykset säilyy.

Rev.1

Code: Select all

Jouduin poistamaan kun meni maksimimerkkimäärä yli
EDIT:

Hosuin tuon äskösen kanssa. Sillä ei enää toiminut zelda title screen. Samalla esimerkki muokkauksien lähetyksestä.
Rev.2

Code: Select all

    SCREEN 600,300

    'avaa rom
    file$="zeldatitlescreen\zelda.nes"
    'file$="random\city.nes"
    'file$="fighter_f8000\fighter_f8000.nes"
    'file$="soda cans\sprite.nes"
    'file$=CommandLine()


    //headerin käsittely
    f=OpenToRead(file$)
    size=FileSize(file$)

    Global rombanks,vrombanks,mapper
    headercheck=(ReadInt(f)=441664846)
    rombanks=ReadByte(f)
    vrombanks=ReadByte(f)
    t=ReadByte(f)
    mapper=t Shr 4 + ReadByte(f) Shr 4
    mirroring=t Mod 16


    'infoa
    Print "headerin tarkistus: "+headercheck
    Print "rombanks: "+rombanks
    Print "vrombanks: "+vrombanks
    Print "mapper: "+mapper
    Print "mirroring:  "+mirroring
    Print "tiedostokoko: "+size+"(muistia "+(rombanks*16384+vrombanks*8192)+")tavua"

    //luetaan muistipalikoihin
    SeekFile f,16
    Global rom,vrom,ram,ppuram
    rom=MakeMEMBlock(rombanks*16384+1)
    vrom=MakeMEMBlock(vrombanks*8192*2*2)
    For i=0 To 16384*rombanks
        PokeByte rom, i, ReadByte(f)
    Next i

    For i=0 To 2*8192*vrombanks-1
        PokeByte vrom, i, ReadByte(f)
    Next i

    ram=MakeMEMBlock(2048)

    ppuram=MakeMEMBlock(16380)

    //flagit ja rekisterit
    Global C,Z,I,D,B,V,N'flageja asmidokumentissa n on s statusrekisteristä
    Global A,X,Y'rekisterit
    Global S'stack
    Global ppu_vblank,ppuaddr_addr,ppuaddr_addr0,ppuaddr_addr1,ppuaddr_turn
    Global cycle,PC

    //piirto
    Global rendermethod
    rendermethod=1
    Dim renderlayers(7)
    Dim maskedtiles(3)

    Global scr
    scr=MakeImage(256,240)
    Dim palette(64)
    palette(0)=8158332:palette(1)=252:palette(2)=$188:palette(3)=4466876
    palette(4)=9699460:palette(5)=9699460:palette(6)=11014144:palette(7)=8918016
    palette(8)=5255168:palette(9)=30720:palette(10)=26624:palette(11)=22528
    palette(12)=16472:palette(13)=0:palette(14)=0:palette(15)=0
    palette(16)=12369084:palette(17)=30968:palette(18)=22776:palette(19)=6833404
    palette(20)=14155980:palette(21)=14942296:palette(22)=16267264:palette(23)=14965776
    palette(24)=11303936:palette(25)=47104:palette(26)=43008:palette(27)=43076
    palette(28)=34952:palette(29)=0:palette(30)=0:palette(31)=0
    palette(32)=16316664:palette(33)=3980540:palette(34)=6850812:palette(35)=9992440
    palette(36)=16283896:palette(37)=16275608:palette(38)=16283736:palette(39)=16556100
    palette(40)=16300032:palette(41)=12122136:palette(42)=5822548:palette(43)=5830808
    palette(44)=59608:palette(45)=7895160:palette(46)=000000:palette(47)=000000
    palette(48)=16579836:palette(49)=10806524:palette(50)=12105976:palette(51)=14203128
    palette(52)=16300280:palette(53)=16295104:palette(54)=15782064:palette(55)=16572584
    palette(56)=16308344:palette(57)=14219384:palette(58)=12122296:palette(59)=$12122328
    palette(60)=64764:palette(61)=16308472:palette(62)=$000000:palette(63)=$000000

    For i=0 To 3 'rendermethod 1 juttuja
        renderlayers(i)=MakeImage(256,240)
        MaskImage renderlayers(i),cbmagenta
    Next i
    For i=4 To 7
        renderlayers(i)=MakeImage(256,240)
        MaskImage renderlayers(i),cbblack
    Next i
    'prerendataan tilet
    For i=0 To 3
        maskedtiles(i)=MakeImage(512*8*vrombanks,8)
        drawtoimage maskedtiles(i)
            For j=1 To 512*vrombanks
                For y=0 To 7
                    byte1=PeekByte(vrom,j*16-1+y)
                    byte2=PeekByte(vrom,j*16+7+y)
                    For x=0 To 7
                        If Not (((byte1 Shr (7-x)) Mod 2))+((((byte2 Shr (7-x)) Mod 2))Shl 1)=i Then PutPixel j*8+x,y,255 Shl 16 +255
                        'PutPixel j*8+x,y,((byte1 Shl (25+x)) Shr 31 + (byte2 Shl (25+x)) Shr 30)*64
                    Next x
                Next y
                'If (j Mod 4) then Line j+i,0,j+i+8,8
            Next j
        DrawToScreen
    Next i


    Global aika

    //emulaattori
    pc=readmem(65533) Shl 8 + readmem(65532)'reset-osoitin, eli aloituskohta osoitteesta $FFFE
    Print "alkuosoitin: "+pc
    running=1
    While running
        op=readmem(pc)
            Select op
                Case 9'09 ORA
                    par=readmem(pc+1)
                    A=((A Shr 7) Or (par Shr 7)) Shl 7 +((A Shl 25 Shr 31) Or (par Shl 25 Shr 31)) Shl 6 +((A Shl 26 Shr 31) Or (par Shl 26 Shr 31)) Shl 5+((A Shl 27 Shr 31) Or (par Shl 27 Shr 31)) Shl 4+((A Shl 28 Shr 31) Or (par Shl 28 Shr 31)) Shl 3+((A Shl 29 Shr 31) Or (par Shl 29 Shr 31)) Shl 2+((A Shl 30 Shr 31) Or (par Shl 30 Shr 31)) Shl 1+((A Shl 31 Shr 31) Or (par Shl 31 Shr 31))
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7
                    pc+2
                    cycle+2
                Case 10'0A
                    C=A Shr 7
                    A=(A Shl 1) Mod 256
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7
                    pc+1
                    cycle+2
                Case 16'10 BPL Relative
                    If N=0 Then
                        param=readmem(pc+1)
                        oldpc=pc
                        pc=pc+1+(param Mod 128)-((param>127)*127)
                        cycle=cycle+3+(RoundDown((oldpc-32768)/16384.0)<>RoundDown((pc-32768)/16384.0))
                    Else
                        pc+2
                        cycle+2
                    EndIf
                Case 24'18 CLC
                    C=0
                    pc+1
                    cycle+2
                Case 32'JSR
                    writemem(256+S,(PC+2) Shr 8)
                    S=(256+S+1) Mod 256
                    writemem(256+S,(PC+2) Mod 256)
                    S=(256+S+1) Mod 256
                    pc=readmem(pc+2) Shl 8 + readmem(pc+1)
                    cycle+6
                    'pc+3
                    Color cbmagenta
                Case 41'29 AND Immediate
                    par=readmem(pc+1)
                    A=((A Shr 7) And (par Shr 7)) Shl 7 +((A Shl 25 Shr 31) And (par Shl 25 Shr 31)) Shl 6 +((A Shl 26 Shr 31) And (par Shl 26 Shr 31)) Shl 5+((A Shl 27 Shr 31) And (par Shl 27 Shr 31)) Shl 4+((A Shl 28 Shr 31) And (par Shl 28 Shr 31)) Shl 3+((A Shl 29 Shr 31) And (par Shl 29 Shr 31)) Shl 2+((A Shl 30 Shr 31) And (par Shl 30 Shr 31)) Shl 1+((A Shl 31 Shr 31) And (par Shl 31 Shr 31))
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+2
                Case 42'2A ROL
                    T=A Shr 7
                    A=A Shl 1 - T Shr 8 +C
                    C=T
                    pc+1
                    cycle+2
                Case 44'2C BIT
                    par=readmem(pc+1)
                    T=((A Shr 7) And (par Shr 7)) Shl 7 +((A Shl 25 Shr 31) And (par Shl 25 Shr 31)) Shl 6 +((A Shl 26 Shr 31) And (par Shl 26 Shr 31)) Shl 5+((A Shl 27 Shr 31) And (par Shl 27 Shr 31)) Shl 4+((A Shl 28 Shr 31) And (par Shl 28 Shr 31)) Shl 3+((A Shl 29 Shr 31) And (par Shl 29 Shr 31)) Shl 2+((A Shl 30 Shr 31) And (par Shl 30 Shr 31)) Shl 1+((A Shl 31 Shr 31) And (par Shl 31 Shr 31))
                    If T=0 Then Z=1 Else Z=0
                    N=par Shr 7
                    V=par Shl 25 Shr 31
                    pc+3
                    cycle+4
                Case 72'48 PHA
                    writemem(S,A)
                    S+1
                    pc+1
                    cycle+3
                Case 76'4C JMP absolute
                    pc=readmem(pc+2) Shl 8 + readmem(pc+1)
                    cycle+3
                Case 96'RTS
                    S=(256+S-1) Mod 256
                    tS=(256+S-1) Mod 256
                    pc=readmem(256+S)+1+readmem(256+tS)Shl 8
                    S=tS
                    cycle+6
                    Color cbmagenta
                Case 101'65 ADC Zero Page
                    A=A+readmem(readmem(pc+1))+c
                    If A>255 Then C=1:A= A Mod 256 Else C=0
                    If A=0 Then Z=1 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+3
                Case 104'68 PLA
                    S-1
                    A=readmem(S)
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+1
                    cycle+4
                Case 105'69 ADC Immediate
                    A=A+readmem(pc+1)+c
                    If A>255 Then C=1:A= A Mod 256 Else C=0
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+3
                Case 106'6A ROR
                    T=A Mod 2
                    A=A Shr 1 + C Shl 7
                    C=T
                    pc+1
                    cycle+2
                Case 109'6D ADC Absolute
                    A=A+readmem(readmem(pc+2) Shl 8 + readmem(pc+1))+c
                    If A>255 Then C=1:A= A Mod 256 Else C=0
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+3
                    cycle+4
                Case 120'78 SEI 1 -> I
                    I=1
                    pc+1
                    cycle+2
                Case 132'84 STY Zero page
                    writemem(readmem(pc+1),Y)
                    'Print "             STY "+readmem(pc+1)+" ("+readmem(readmem(pc+1))+")"
                    pc+2
                    cycle+3
                Case 133'85 STA Zero Page
                    writemem(readmem(pc+1),A)
                    pc+2
                    cycle+3
                Case 134'86 STX Zero Page
                    writemem(readmem(pc+1),X)
                    pc+2
                    cycle+3
                Case 136'88 DEY Y - 1 -> Y
                    Y=(256+Y-1) Mod 256
                    pc+1
                    cycle+2
                    N=Y Shr 7
                    Z=(Y=0)
                Case 138'8A TXA
                    A=X
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+1
                    cycle+2
                Case 140'8C STY Absolute
                    writemem(readmem(pc+2) Shl 8 + readmem(pc+1),Y)
                    pc+3
                    cycle+4
                Case 141'8D STA Absolute
                    writemem(readmem(pc+2) Shl 8 + readmem(pc+1),A)
                    pc+3
                    cycle+4
                Case 142'8E STX Absolute
                    writemem(readmem(pc+2) Shl 8 + readmem(pc+1),X)
                    'Print "            "+(readmem(pc+1) Shl 8 + readmem(pc+2))
                    pc+3
                    cycle+4
                Case 145'91 STA (Indirect),Y
                    par=readmem(pc+1)
                    writemem((readmem(par+1) Shl 8 + readmem(par) + Y),A)
                    'Print "    STA"+"("+par+"),Y="+Y+"  =STA "+(readmem(par+1) Shl 8 + readmem(par) + Y)
                    pc+2
                    cycle+6
                Case 149'95 STA zero page, X
                    writemem((X+readmem(pc+1))Mod 256,A)
                    pc+2
                    cycle+4
                Case 152'98 TYA
                    A=Y
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+1
                    cycle+2
                Case 153'99 STA Absolute, Y
                    writemem(readmem(pc+2) Shl 8 + readmem(pc+1)+Y,A)
                    pc+3
                    cycle+5
                Case 154'9A TXS X -> S
                    writemem(256+S,X)
                    S=(256+S+1) Mod 256
                    pc+1
                    cycle+2
                Case 157'9D STA Absolute, X
                    writemem(readmem(pc+2) Shl 8 + readmem(pc+1)+X,A)
                    pc+3
                    cycle+5
                Case 160'A0 LDY Immediate
                    Y=readmem(pc+1)
                    pc+2
                    cycle+2
                    N=Y Shr 7
                    Z=(Y=0)
                Case 162'A2 LDX Immediate
                    X=readmem(pc+1)
                    pc+2
                    cycle+2
                    N=X Shr 7
                    Z=(X=0)
                Case 164'A4 LDY Zero Page
                    Y=readmem(readmem(pc+1))
                    If Y=0 Then Z=1:N=0 Else Z=0:N=Y Shr 7'FLAGIT
                    pc+2
                    cycle+3
                Case 165'A5 LDA Zero Page
                    A=readmem(readmem(pc+1))
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+3
                Case 166'A6 LDX Zero Page
                    X=readmem(readmem(pc+1))
                    If X=0 Then Z=1:N=0 Else Z=0:N=X Shr 7'FLAGIT
                    pc+2
                    cycle+3
                Case 168'A8 TAY
                    Y=A
                    If Y=0 Then Z=1 Else Z=0:N=Y Shr 7
                    pc+1
                    cycle+2
                Case 169'A9 LDA Immediate
                    A=readmem(pc+1)
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+2
                Case 170'AA TAX
                    X=A
                    If X=0 Then Z=1 Else Z=0:N=X Shr 7
                    pc+1
                    cycle+2
                Case 173'AD LDA Absolute
                    A=readmem(readmem(pc+2) Shl 8 + readmem(pc+1))
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+3
                    cycle+4
                Case 177'B1 LDA (Indirect),Y
                    par=readmem(pc+1)
                    p2=readmem(par)
                    A=readmem(readmem(par+1) Shl 8 + p2 + Y)
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    If p2+Y>255 Then cycle=6 Else cycle=5
                    pc+2
                Case 185'B9 LDA Absolute, Y
                    addr=readmem(pc+2) Shl 8 + readmem(pc+1)
                    A=readmem(addr+ Y)
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+3   
                    cycle=cycle+4+((addr Shr 8) =((addr+Y)Shr 8) )
                Case 189'BD LDA Absolute,X
                    addr=readmem(pc+2) Shl 8 + readmem(pc+1)
                    A=readmem(addr+ X)
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+3   
                    cycle=cycle+4+((addr Shr 8) =((addr+X)Shr 8) )
                Case 198'C6 DEC Zero-page
                    par=readmem(pc+1)
                    par2=(256+(readmem(par)-1)) Mod 256
                    writemem(par,par2)
                    'Print "      DEC "+par+" ="+readmem(par)
                    pc+2
                    cycle+5
                    N=par2 Shr 7
                    Z=(par2=0)
                Case 200'C8 DEY
                    X=(256+Y-1) Mod 256
                    pc+1
                    cycles+2
                    N=Y Shr 7
                    Z=(Y=0)
                Case 201'C9 CMP
                    T=(256+A-readmem(pc+1)) Mod 256
                    C=(T>=0)
                    Z=(T=0)
                    N=T Shr 7
                    pc+2
                    cycle+2
                Case 202'CA DEX X - 1 -> X
                    X=(256+X-1) Mod 256
                    pc+1
                    cycles+2
                    N=X Shr 7
                    Z=(X=0)
                Case 208'D0 BNE Relative
                    If Z=0 Then
                        param=readmem(pc+1)
                        oldpc=pc
                        pc=pc+1+(param Mod 128)-((param>127)*127)
                        cycle=cycle+3+(RoundDown((oldpc-32768)/16384.0)<>RoundDown((pc-32768)/16384.0))'OPTIMOI!
                    Else
                        pc+2
                        cycle+2
                    EndIf
                    'Print "hyaa"
                Case 216'D8 CLD 0 -> D(D-flagilla ei nesissä ole mitään käyttöä)
                    D=0
                    pc+1
                    cycle+2
                Case 224'E0 CPX
                    T=(256+X-readmem(pc+1)) Mod 256
                    C=(T>=0)
                    Z=(T=0)
                    N=T Shr 7
                    pc+2
                    cycle+2
                Case 230'E6 INC Zero Page
                    addr=readmem(pc+1)
                    writemem(addr,(readmem(addr)+1) Mod 256)
                    pc+2
                    cycle+5
                Case 232'E8 INX
                    X=(X+1)*(X<>255)
                    If X=0 Then Z=1 Else Z=0:N=X Shr 7'FLAGIT
                    pc+1
                    cycle+2
                Case 234'EA NOP
                    pc+1
                    cycle+2
                Case 240'F0 BEQ
                    If Z=1 Then
                        param=readmem(pc+1)
                        oldpc=pc
                        pc=pc+1+(param Mod 128)-((param>127)*127)
                        cycle=cycle+3+(RoundDown((oldpc-32768)/16384.0)<>RoundDown((pc-32768)/16384.0))'OPTIMOI!
                    Else
                        pc+2
                        cycle+2
                    EndIf
                Case 253'FD SBC Absolute,X
                    'addr=readmem(pc+2) Shl 8 + readmem(pc+1)
                    par1=readmem(pc+2)
                    par2=readmem(pc+1)
                    addr=par1 Shl 8 + par2 +X
                    A=A-readmem(addr)
                    If A<0 Then A=0:C=1 Else C=0
                    'owerflowi puuttuu
                    pc+3
                    cycle=cycle+4+par2+X>255
                    Color cbmagenta
                Default'jos komentoa ei löytynyt
                    cycle+1
                    pc+1
                    Color 255,0,0
                    Print op+" "+Right(Hex(op),2)
                    'Wait 2000
            EndSelect
        'Print op+" "+Right(Hex(op),2)
        Color 255,255,255
        ppu()
        'printstatus()
        'If N>1 Or N<0 Then Wait 5000
        'WaitKey
        'If KeyDown(cbkeyp) Then printstatus()
        'If Not KeyDown(cbkeyspace) Then WaitKey' Else Wait 1'50
        //vblankin generointi
        If cycle<4 Or cycle>(cycle2+29829) Or KeyDown(cbkeyv) Then  cycle2=cycle:ppu_vblank=1:Print "vblank"':Wait 2000
    Wend



    Function readmem(address)//koko prossun muisti
        If address=>32768 Then//ROM
            Select mapper
                Case 0'NOMAPPER
                    If rombanks=1 Then 'käsittely yhdellä rompankilla, se peilautuu ylä ja alapankkiin.
                        'Print (address-32768) Mod 16384
                        Return PeekByte(rom,(address-32768) Mod 16384)
                    ElseIf rombanks=2 'kahdella rompankilla ne näkyy peräkkäin(ei testattu)
                        'Print address-32768
                        Return PeekByte(rom,address-32768)
                    Else
                        MakeError rombanks+" pankkisia NOMAPPER-romeja ei ole"+chr(10)+"(an error that was impossible to occur has just occured.)"
                    EndIf
                 Default
                    If rombanks=1 Then 'nomapper
                        Return PeekByte(rom,(address-32768) Mod 16384)
                    Else
                        Return PeekByte(rom,address-32768)
                    EndIf
                    'MakeError mapper+"ei ole tuettu mapperi"
            EndSelect
        ElseIf address<8192 Then//RAM
            Return PeekByte(ram,address)
        ElseIf address=>8192 And address<16383 Then//Registers
            Select address Mod 8
                Case 2'$2002
                    ret=ppu_vblank Shl 7//eivalmis
                    ppu_vblank=0
                    Return ret
                'Case 4'04 Sprite Memory Data
                Case 7
                    Print "PPUIO luku"
                Default
                    Print "hassu rekisteri(luku) "+Right(Hex(address),4)
                    Return 0
            EndSelect
        ElseIf address=>16384 And address<=16407 Then'toinen rekisterialue
            Select address - 16384
                Case 22
                    Print "Ohjain 1 luku"
                    Return Rand(255)
                Case 23
                    Print "Ohjain 2 luku"
                Default
                    Print "hassu rekisteri(luku) "+Right(Hex(address),4)
            EndSelect
        Else'jos osoitteella ei ole muistia niin toimitaan näin.
            Print "hassu lukuosoite "+ address
            Return address Shr 8
        EndIf
    EndFunction

    Function writemem(address, arvo)//koko prossun muisti
        If address<=8191 Then //RAM
            PokeByte ram,address Mod 2048, arvo
        ElseIf address=>8192 And address<16383 Then//Registers
            Select address mod 8 'korvaa printit lukemisella
                Case 0'$2000
                    Print "PPUCNT1 kirjoitus"
                Case 1'$2001
                    Print "PPUCNT2 kirjoitus"
                Case 3'$2003
                    Print "SPRADDR kirjoitus"
                Case 4'$2004
                    Print "SPRIO kirjoitus"
                Case 5'$2005
                    Print "BGSCROL kirjoitus"
                Case 6'$2006
                    Print "PPUADDR kirjoitus"
                    ppuaddr_turn=((ppuaddr_turn+1)<2)
                    If ppuaddr_turn=1 Then
                        ppuaddr_addr=ppuaddr_addr Shl 24 Shr 24 + arvo Shl 8
                        'ppuaddr_addr0=arvo
                    Else
                        ppuaddr_addr=ppuaddr_addr Shr 8 Shl 8 +arvo
                        'ppuaddr_addr1=arvo
                    EndIf
                Case 7'$2007
                    Print "PPUIO kirjoitus"
                    'huom:
                    'Write data into $2007. After each write, the address will auto-increment by 1, or 32 (if Bit #2 of $2000 is 1).
                    'muista mirroring tähän,lukuun ja näyttikseen kun mapperit lisääntyy
                   
                    PokeByte ppuram, ppuaddr_addr, arvo
                    ppuaddr_addr+1
                    'Print ppuaddr_addr+ " "+arvo
                    'Wait 400
                Default
                    Print "hassu rekisteri(kirjoitus) "+Right(Hex(address),4)
                    Return 0
            EndSelect
        ElseIf address=>16384 And address<=16407 Then
            Select address - 16384
                Case 22
                    Print "Ohjain 1 kirjoitus"
                Case 23
                    Print "Ohjain 2 kirjoitus"
                Default
                    Print "hassu rekisteri(kirjoitus) "+Right(Hex(address),4)
            EndSelect
        ElseIf address=>32768 Then//ROM
            Select mapper
                Case 0
                    Print "Ohjelmaromiin kirjoitus ei tee mitään tällä mapperilla"
                Default
                    Print "tuntematon mapperi tekee jotain"
            EndSelect
        Else'jos osoitteella ei ole muistia niin toimitaan näin.
            Print "hassu kirjoitusosoite "+address
        EndIf
    EndFunction

    Function ppu()
         //kuva kerrallaan piirto, korjataan scanline/pikseli kerrallaan piirrolla.
        'SetWindow ""+cycle
        If cycle>1789773/60 Or KeyDown(cbkeyv) Then 
            SetWindow (Timer()-aika)+" "+1000/60+"sta"
            cycle=0
            t=Timer()
            Select rendermethod
                Case 0
                    DrawToImage scr
                    Lock
                        For sy=0 To 239
                            For til=0 To 31
                                tilenumber=PeekByte(ppuram,8192+(RoundDown(sy/8.0)*32+til))+256
                                byte1=PeekByte(vrom,tilenumber*16+(sy Mod 8)-1)
                                byte2=PeekByte(vrom,tilenumber*16+(sy Mod 8)+7)
                                pal=PeekByte(ppuram,9152+RoundDown(sy/32.0)*8+RoundDown(til/4.0)) Shl (24+2*(RoundDown(til/2+1)Mod 2)+4*(RoundDown(sy/16+1) Mod 2)) Shr 30
                                For sx=0 To 7
                                    PutPixel2 til*8+sx,sy,palette(PeekByte(ppuram,16128+(((byte1 Shr (7-sx)) Mod 2))+((((byte2 Shr (7-sx)) Mod 2))Shl 1)+(pal) Shl 2))
                                    'SetWindow ""+(PeekByte(ppuram,16128+(byte1 Shr (7-sx)) Mod 2+((byte2 Shr (7-sx)) Mod 2)Shl 1 +pal Shl 2))+" "+((byte1 Shr (7-sx)) Mod 2+((byte2 Shr (7-sx)) Mod 2)Shl 1 +pal Shl 2)+ " " +pal
                                Next sx
                            Next til
                        Next sy
                    Unlock
                    DrawToScreen
                Case 1
                    Randomize 12342565
                    For l=4 To 7'tilet
                        DrawToImage renderlayers(l)
                            Color cbblack
                            Box 0,0,256,240
                            j=l-4
                            For i=0 To 959
                                'DrawImageBox maskedtiles(j),i*8 Mod 256,RoundDown(i/32.0)*8,(PeekByte(ppuram,8192+i)+256)Shl 3,0,8,8
                                CopyBox (PeekByte(ppuram,8192+i)+256)Shl 3,0,8,8,(i Shl 3) Mod 256,RoundDown(i/32.0)Shl 3,Image(maskedtiles(j)),Image(renderlayers(l))
                            Next i
                    Next l
                    For l=0 To 3'tausta
                        DrawToImage renderlayers(l)
                            Color cbblack
                            Box 0,0,256,240
                            For i=0 To 63
                                pal=PeekByte(ppuram,9152+i)
                                Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 30) Shr 28)+l))
                                Box i*32 Mod 256, RoundDown(i/8.0)*32,16,16
                                Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 28) Shr 30)Shl 2+l))
                                Box (i*32+16) Mod 256, RoundDown(i/8.0)*32,16,16
                                Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 26) Shr 30)Shl 2+l))
                                Box i*32 Mod 256, RoundDown(i/8.0)*32+16,16,16
                                Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 24) Shr 30)Shl 2+l))
                                Box (i*32+16) Mod 256, RoundDown(i/8.0)*32+16,16,16
                            Next i
                            DrawImage renderlayers(l+4),0,0
                        DrawToScreen
                    Next l
                    DrawToImage scr
                    Color cbblack
                    Box 0,0,256,240
                    DrawImage renderlayers(0),0,0
                    DrawImage renderlayers(1),0,0
                    DrawImage renderlayers(2),0,0
                    DrawImage renderlayers(3),0,0
                    DrawToScreen
            EndSelect
            Color cbblack
            Box 200,0,256,240
            DrawImage scr,200,0' kaksi piirtoa koska print pistää koordinaatit sekaisin
            'DrawImage scr,200,150
            DrawScreen OFF
            SetWindow ""+(Timer()-t)
            aika=Timer()
        EndIf
    EndFunction

    Function printstatus()
        Print "A:"+A+" X:"+X+" Y:"+Y+"  N:"+N+" V:"+V+" I:"+I+" Z:"+Z+" C:"+C+"  S(tack):"+S+" PC:"+Right(Hex(PC),4)+" ppuaddr:"+ppuaddr_addr
    EndFunction



    //relative (param Mod 128)-((param>127)*127)
    //absolute readmem(readmem(pc+2) Shl 8 + readmem(pc+1))


Vaihdoin ykkösestä

Code: Select all

    'If cycle<4 Or cycle>(cycle2+1789773/60) Or KeyDown(cbkeyv) Then  cycle2=cycle:ppu_vblank=1:Print "vblank"':Wait 2000
    If cycle>29829 Or KeyDown(cbkeyv) Then cycle=0:ppu_vblank=1:Print "vblank"':Wait 2000
->

Code: Select all

    If cycle<4 Or cycle>(cycle2+29829) Or KeyDown(cbkeyv) Then  cycle2=cycle:ppu_vblank=1:Print "vblank"':Wait 2000
[/edit]

Ainiin. kuvia tietysti:
Image
Image

Saa kysellä jos jokin on epäselvää.
Romit: http://nesdev.parodius.com/zelda.zip http://kahesi.net/nesomatohjelmat.php#fighter Nämä ovat tietysti laillisia. En kannata warea. En ole montaa itse testannut, joten muutkin saattavat toimia jotenkin.

Linkkejä muihin tietolähteisiin:
http://www.emutalk.net/threads/25700-Nes 'oikein hyvä.
http://nesdev.parodius.com/ 'paljon tavaraa
http://wiki.nesdev.com/w/index.php/Nesdev_Wiki 'wikimuodossa paljon tietoa.

ps. Jeesus elää ja on ainoa tie taivaaseen ja jumalan yhteyteen. Jeesus pelastaa ne jotka tunnustavat hänet herraksensa ja tekevät hänen tahtonsa mukaan. Jeesus on Jumalan poika ja kuoli ihmisten syntien tähden, jotta jokainen voisi pelastua hänen kauttansa. Jumala on täydellisen hyvä eikä voi sietää mitään pahaa. Jeesuksen täydellisen uhrin kautta ihminen voi saada sovituksen ja tulla taas jumalan silmissä täydelliseksi. Jumalan tahto on, että jokainen pelastuisi. Jumala loi kuitenkin ihmiselle vapaan tahdon, joten valinta pelastuksen hyväksymisestä on jokaisella itsellä.
"kaikki ovat tehneet syntiä ja ovat vailla Jumalan kirkkautta." Room. 3:23
Last edited by atomimalli on Wed Jan 04, 2012 1:19 am, edited 8 times in total.
MaGetzUb
Guru
Posts: 1715
Joined: Sun Sep 09, 2007 12:35 pm
Location: Alavus

Re: Nes-emulaattori ja yleistietoa nesistä.

Post by MaGetzUb »

Noo huhhuh, kyllä se atomimalli sai taas ällistettyä ainakin minut.. :D Kuinkas äänipuoli tulisi hoitaa CB:lä järkevästi DLL:nä, vai luodaanko äänet ja tallennetaan ne pelin kansioon?=)
Solar Eclipse
Meneillä olevat Projektit:
We're in a simulation, and God is trying to debug us.
atomimalli
Moderator
Moderator
Posts: 227
Joined: Wed Aug 29, 2007 3:55 pm

Re: Nes-emulaattori ja yleistietoa nesistä.

Post by atomimalli »

Jos toteutuksessa pääsee äänipuoleen, niin perussamplet luodaan etukäteen ja sitten niiden voimakkuutta ja nopeutta säädetään ohjausrekistereihin kirjoituksen yhteydessä. Voi olla että joissain tietyissä tapauksissa napsuu vähän.
Pcm-äänen toteutuksesta en osaa vielä sanoa. Voi olla ettei se noin vain onnistu. En ole siihen kovin tututunut.
EDIT:

pcm-soundit voisi osassa tapauksia pelikohtaisena hle:nä esirendata, mutta se on aika rumaa :P

Last edited by atomimalli on Sun Dec 18, 2011 4:49 pm, edited 1 time in total.
MaGetzUb
Guru
Posts: 1715
Joined: Sun Sep 09, 2007 12:35 pm
Location: Alavus

Re: Nes-emulaattori ja yleistietoa nesistä.

Post by MaGetzUb »

atomimalli wrote:Jos toteutuksessa pääsee äänipuoleen, niin perussamplet luodaan etukäteen ja sitten niiden voimakkuutta ja nopeutta säädetään ohjausrekistereihin kirjoituksen yhteydessä. Voi olla että joissain tietyissä tapauksissa napsuu vähän.
Pcm-äänen toteutuksesta en osaa vielä sanoa. Voi olla ettei se noin vain onnistu. En ole siihen kovin tututunut.
Onnistuisiko myös Musiikkipuolen asetuksiin laittaa niin että äänet on joko koneääniä, tai sitten vaikkapa ehkä midejä? :D
Solar Eclipse
Meneillä olevat Projektit:
We're in a simulation, and God is trying to debug us.
atomimalli
Moderator
Moderator
Posts: 227
Joined: Wed Aug 29, 2007 3:55 pm

Re: Nes-emulaattori ja yleistietoa nesistä.

Post by atomimalli »

Miksi ihmeessä midejä? Siitä ei aa millään hyvän kuulosta koska jokainen peli käyttää nesin ääniä erilailla, eikä sitä oikein pysty ohjelmallisesti päättelemään että milloin sen pitisi kuulostaa miltäkin midisoittimelta. Minun varmaan pitäisi joskus lisätä ääniosiokin. Luulin kyllä, ettei sille olisi tarvetta :P tuhoojabotti huomasi että joku minun viimehetkinen muokkaus sekoitti tuon niin ettei selda title screen simulatorista tule kuvaa. Korjailen.
EDIT:

Tehty.
Magezub: voit kokeilla famitrackerilla säätää midiksi jos löydät jostain chipin.

Last edited by atomimalli on Tue May 24, 2011 7:38 pm, edited 1 time in total.
MaGetzUb
Guru
Posts: 1715
Joined: Sun Sep 09, 2007 12:35 pm
Location: Alavus

Re: Nes-emulaattori ja yleistietoa nesistä.

Post by MaGetzUb »

atomimalli wrote:Miksi ihmeessä midejä? Siitä ei aa millään hyvän kuulosta koska jokainen peli käyttää nesin ääniä erilailla, eikä sitä oikein pysty ohjelmallisesti päättelemään että milloin sen pitisi kuulostaa miltäkin midisoittimelta. Minun varmaan pitäisi joskus lisätä ääniosiokin. Luulin kyllä, ettei sille olisi tarvetta :P tuhppjabotti huomasi että joku minun viimehetkinen muokkaus sekoitti tuon niin ettei selda title screen simulatorista tule kuvaa. Korjailen.
Ihan vain mielenkiinnosta olisin halunnut kuunnella, miltä esim joku JTS(Journey To Silus ftw \o/):n musiikit kuulostaisivat Midinä.. :) Mutta jooh jatketaas aihetta. ->
Solar Eclipse
Meneillä olevat Projektit:
We're in a simulation, and God is trying to debug us.
mikeful
Moderator
Moderator
Posts: 523
Joined: Mon Aug 27, 2007 8:36 pm
Location: Vaasa, Finalnd
Contact:

Re: Nes-emulaattori ja syvempää tietoa nesistä.

Post by mikeful »

Image
Pelejä: Pelasta puhe, Grinder
Muuta: Blogi, Twitter
atomimalli
Moderator
Moderator
Posts: 227
Joined: Wed Aug 29, 2007 3:55 pm

Re: Nes-emulaattori ja nippelitietoa nesistä.

Post by atomimalli »

Kiitokset saavutuksesta. Mikähän mahtaa olla validi tapa lisätä se allekirjoitukseen? Mitähän muita tässä on vuosien varrella kertynyt :o

Tein loogisten operaatioiden esilaskennan, koska se on liian hidasta silmukassa. Lisäsin myös aloituspostiin kohdat Muisti, rekisterit, äänet, romit ja heksadesimaalit. Selvensin myös muutamia muita kohtia. Muutos vaikuttaa vain nopeuteen.

Rev. 3

Code: Select all

    SCREEN 600,300

    'avaa rom
    file$="zeldatitlescreen\zelda.nes"
    'file$="random\city.nes"
    'file$="fighter_f8000\fighter_f8000.nes"
    'file$="fighter_f8000\f8000.nes"
    'file$="soda cans\sprite.nes"
    'file$=CommandLine()


    //headerin käsittely
    f=OpenToRead(file$)
    size=FileSize(file$)

    Global rombanks,vrombanks,mapper
    headercheck=(ReadInt(f)=441664846)
    rombanks=ReadByte(f)
    vrombanks=ReadByte(f)
    t=ReadByte(f)
    mapper=t Shr 4 + ReadByte(f) Shr 4
    mirroring=t Mod 16


    'infoa
    Print "headerin tarkistus: "+headercheck
    Print "rombanks: "+rombanks
    Print "vrombanks: "+vrombanks
    Print "mapper: "+mapper
    Print "mirroring:  "+mirroring
    Print "tiedostokoko: "+size+"(muistia "+(rombanks*16384+vrombanks*8192)+")tavua"
    Print "precalcing..."

    //luetaan muistipalikoihin
    SeekFile f,16
    Global rom,vrom,ram,ppuram
    rom=MakeMEMBlock(rombanks*16384+1)
    vrom=MakeMEMBlock(vrombanks*8192*2*2)
    For i=0 To 16384*rombanks
        PokeByte rom, i, ReadByte(f)
    Next i

    For i=0 To 2*8192*vrombanks-1
        PokeByte vrom, i, ReadByte(f)
    Next i

    ram=MakeMEMBlock(2048)

    ppuram=MakeMEMBlock(16380)

    //flagit ja rekisterit
    Global C,Z,I,D,B,V,N'flageja asmidokumentissa n on s statusrekisteristä
    Global A,X,Y'rekisterit
    Global S'stack
    Global ppu_vblank,ppuaddr_addr,ppuaddr_addr0,ppuaddr_addr1,ppuaddr_turn
    Global cycle,PC

    //loogisten operaattoreiden esilasku
    Print "logical operations"
    Dim _and(255,255)
    Dim _or(255,255)
    //esilasketaan kaikki kahden tavun väliset andit ja orit
    For x=0 To 255 
        For y=0 To 255
            _and(x,y)=((y Shr 7) and (x Shr 7)) Shl 7 +((y Shl 25 Shr 31) and (x Shl 25 Shr 31)) Shl 6 +((y Shl 26 Shr 31) and (x Shl 26 Shr 31)) Shl 5+((y Shl 27 Shr 31) and (x Shl 27 Shr 31)) Shl 4+((y Shl 28 Shr 31) and (x Shl 28 Shr 31)) Shl 3+((y Shl 29 Shr 31) and (x Shl 29 Shr 31)) Shl 2+((y Shl 30 Shr 31) and (x Shl 30 Shr 31)) Shl 1+((y Shl 31 Shr 31) and (x Shl 31 Shr 31))
        Next y
    Next x
    For x=0 To 255 
        For y=0 To 255
            _or(x,y)=((y Shr 7) Or (x Shr 7)) Shl 7 +((y Shl 25 Shr 31) Or (x Shl 25 Shr 31)) Shl 6 +((y Shl 26 Shr 31) Or (x Shl 26 Shr 31)) Shl 5+((y Shl 27 Shr 31) Or (x Shl 27 Shr 31)) Shl 4+((y Shl 28 Shr 31) Or (x Shl 28 Shr 31)) Shl 3+((y Shl 29 Shr 31) Or (x Shl 29 Shr 31)) Shl 2+((y Shl 30 Shr 31) Or (x Shl 30 Shr 31)) Shl 1+((y Shl 31 Shr 31) Or (x Shl 31 Shr 31))
        Next y
    Next x
    //piirto
    Global rendermethod
    rendermethod=1
    Dim renderlayers(7)
    Dim maskedtiles(3)

    Global scr
    scr=MakeImage(256,240)
    Dim palette(64)
    palette(0)=8158332:palette(1)=252:palette(2)=$188:palette(3)=4466876
    palette(4)=9699460:palette(5)=9699460:palette(6)=11014144:palette(7)=8918016
    palette(8)=5255168:palette(9)=30720:palette(10)=26624:palette(11)=22528
    palette(12)=16472:palette(13)=0:palette(14)=0:palette(15)=0
    palette(16)=12369084:palette(17)=30968:palette(18)=22776:palette(19)=6833404
    palette(20)=14155980:palette(21)=14942296:palette(22)=16267264:palette(23)=14965776
    palette(24)=11303936:palette(25)=47104:palette(26)=43008:palette(27)=43076
    palette(28)=34952:palette(29)=0:palette(30)=0:palette(31)=0
    palette(32)=16316664:palette(33)=3980540:palette(34)=6850812:palette(35)=9992440
    palette(36)=16283896:palette(37)=16275608:palette(38)=16283736:palette(39)=16556100
    palette(40)=16300032:palette(41)=12122136:palette(42)=5822548:palette(43)=5830808
    palette(44)=59608:palette(45)=7895160:palette(46)=000000:palette(47)=000000
    palette(48)=16579836:palette(49)=10806524:palette(50)=12105976:palette(51)=14203128
    palette(52)=16300280:palette(53)=16295104:palette(54)=15782064:palette(55)=16572584
    palette(56)=16308344:palette(57)=14219384:palette(58)=12122296:palette(59)=$12122328
    palette(60)=64764:palette(61)=16308472:palette(62)=$000000:palette(63)=$000000
    
    For i=0 To 3 'rendermethod 1 juttuja
        renderlayers(i)=MakeImage(256,240)
        MaskImage renderlayers(i),cbmagenta
    Next i
    For i=4 To 7
        renderlayers(i)=MakeImage(256,240)
        MaskImage renderlayers(i),cbblack
    Next i
    'prerendataan tilet
    Print "tiles"
    For i=0 To 3
        maskedtiles(i)=MakeImage(512*8*vrombanks,8)
        drawtoimage maskedtiles(i)
            For j=1 To 512*vrombanks
                For y=0 To 7
                    byte1=PeekByte(vrom,j*16-1+y)
                    byte2=PeekByte(vrom,j*16+7+y)
                    For x=0 To 7
                        If Not (((byte1 Shr (7-x)) Mod 2))+((((byte2 Shr (7-x)) Mod 2))Shl 1)=i Then PutPixel j*8+x,y,255 Shl 16 +255
                        'PutPixel j*8+x,y,((byte1 Shl (25+x)) Shr 31 + (byte2 Shl (25+x)) Shr 30)*64
                    Next x
                Next y
                'If (j Mod 4) then Line j+i,0,j+i+8,8
            Next j
        DrawToScreen
    Next i


    Print "ready."

    Global aika

    //emulaattori
    pc=readmem(65533) Shl 8 + readmem(65532)'reset-osoitin, eli aloituskohta osoitteesta $FFFE
    Print "alkuosoitin: "+pc
    running=1
    While running
        op=readmem(pc)
            Select op
                Case 9'09 ORA
                    A=_or(A,readmem(pc+1))
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7
                    pc+2
                    cycle+2
                Case 10'0A ASL
                    C=A Shr 7
                    A=(A Shl 1) Mod 256
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7
                    pc+1
                    cycle+2
                Case 16'10 BPL Relative
                    If N=0 Then
                        param=readmem(pc+1)
                        oldpc=pc
                        pc=pc+1+(param Mod 128)-((param>127)*127)
                        cycle=cycle+3+(RoundDown((oldpc-32768)/16384.0)<>RoundDown((pc-32768)/16384.0))
                    Else
                        pc+2
                        cycle+2
                    EndIf
                Case 24'18 CLC
                    C=0
                    pc+1
                    cycle+2
                Case 32'JSR
                    writemem(256+S,(PC+2) Shr 8)
                    S=(256+S+1) Mod 256
                    writemem(256+S,(PC+2) Mod 256)
                    S=(256+S+1) Mod 256
                    pc=readmem(pc+2) Shl 8 + readmem(pc+1)
                    cycle+6
                    'pc+3
                    Color cbmagenta
                Case 41'29 AND Immediate
                    A=_and(A,readmem(pc+1))
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+2
                Case 42'2A ROL
                    T=A Shr 7
                    A=A Shl 1 - T Shr 8 +C
                    C=T
                    pc+1
                    cycle+2
                Case 44'2C BIT
                    par=readmem(pc+1)
                    T=_and(A,par)
                    If T=0 Then Z=1 Else Z=0
                    N=par Shr 7
                    V=par Shl 25 Shr 31
                    pc+3
                    cycle+4
                Case 72'48 PHA
                    writemem(S,A)
                    S+1
                    pc+1
                    cycle+3
                Case 76'4C JMP absolute
                    pc=readmem(pc+2) Shl 8 + readmem(pc+1)
                    cycle+3
                Case 96'RTS
                    S=(256+S-1) Mod 256
                    tS=(256+S-1) Mod 256
                    pc=readmem(256+S)+1+readmem(256+tS)Shl 8
                    S=tS
                    cycle+6
                    Color cbmagenta
                Case 101'65 ADC Zero Page
                    A=A+readmem(readmem(pc+1))+c
                    If A>255 Then C=1:A= A Mod 256 Else C=0
                    If A=0 Then Z=1 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+3
                Case 104'68 PLA
                    S-1
                    A=readmem(S)
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+1
                    cycle+4
                Case 105'69 ADC Immediate
                    A=A+readmem(pc+1)+c
                    If A>255 Then C=1:A= A Mod 256 Else C=0
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+3
                Case 106'6A ROR
                    T=A Mod 2
                    A=A Shr 1 + C Shl 7
                    C=T
                    pc+1
                    cycle+2
                Case 109'6D ADC Absolute
                    A=A+readmem(readmem(pc+2) Shl 8 + readmem(pc+1))+c
                    If A>255 Then C=1:A= A Mod 256 Else C=0
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+3
                    cycle+4
                Case 120'78 SEI 1 -> I
                    I=1
                    pc+1
                    cycle+2
                Case 132'84 STY Zero page
                    writemem(readmem(pc+1),Y)
                    'Print "             STY "+readmem(pc+1)+" ("+readmem(readmem(pc+1))+")"
                    pc+2
                    cycle+3
                Case 133'85 STA Zero Page
                    writemem(readmem(pc+1),A)
                    pc+2
                    cycle+3
                Case 134'86 STX Zero Page
                    writemem(readmem(pc+1),X)
                    pc+2
                    cycle+3
                Case 136'88 DEY Y - 1 -> Y
                    Y=(256+Y-1) Mod 256
                    pc+1
                    cycle+2
                    N=Y Shr 7
                    Z=(Y=0)
                Case 138'8A TXA
                    A=X
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+1
                    cycle+2
                Case 140'8C STY Absolute
                    writemem(readmem(pc+2) Shl 8 + readmem(pc+1),Y)
                    pc+3
                    cycle+4
                Case 141'8D STA Absolute
                    writemem(readmem(pc+2) Shl 8 + readmem(pc+1),A)
                    pc+3
                    cycle+4
                Case 142'8E STX Absolute
                    writemem(readmem(pc+2) Shl 8 + readmem(pc+1),X)
                    'Print "            "+(readmem(pc+1) Shl 8 + readmem(pc+2))
                    pc+3
                    cycle+4
                Case 145'91 STA (Indirect),Y
                    par=readmem(pc+1)
                    writemem((readmem(par+1) Shl 8 + readmem(par) + Y),A)
                    'Print "    STA"+"("+par+"),Y="+Y+"  =STA "+(readmem(par+1) Shl 8 + readmem(par) + Y)
                    pc+2
                    cycle+6
                Case 149'95 STA zero page, X
                    writemem((X+readmem(pc+1))Mod 256,A)
                    pc+2
                    cycle+4
                Case 152'98 TYA
                    A=Y
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+1
                    cycle+2
                Case 153'99 STA Absolute, Y
                    writemem(readmem(pc+2) Shl 8 + readmem(pc+1)+Y,A)
                    pc+3
                    cycle+5
                Case 154'9A TXS X -> S
                    writemem(256+S,X)
                    S=(256+S+1) Mod 256
                    pc+1
                    cycle+2
                Case 157'9D STA Absolute, X
                    writemem(readmem(pc+2) Shl 8 + readmem(pc+1)+X,A)
                    pc+3
                    cycle+5
                Case 160'A0 LDY Immediate
                    Y=readmem(pc+1)
                    pc+2
                    cycle+2
                    N=Y Shr 7
                    Z=(Y=0)
                Case 162'A2 LDX Immediate
                    X=readmem(pc+1)
                    pc+2
                    cycle+2
                    N=X Shr 7
                    Z=(X=0)
                Case 164'A4 LDY Zero Page
                    Y=readmem(readmem(pc+1))
                    If Y=0 Then Z=1:N=0 Else Z=0:N=Y Shr 7'FLAGIT
                    pc+2
                    cycle+3
                Case 165'A5 LDA Zero Page
                    A=readmem(readmem(pc+1))
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+3
                Case 166'A6 LDX Zero Page
                    X=readmem(readmem(pc+1))
                    If X=0 Then Z=1:N=0 Else Z=0:N=X Shr 7'FLAGIT
                    pc+2
                    cycle+3
                Case 168'A8 TAY
                    Y=A
                    If Y=0 Then Z=1 Else Z=0:N=Y Shr 7
                    pc+1
                    cycle+2
                Case 169'A9 LDA Immediate
                    A=readmem(pc+1)
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+2
                Case 170'AA TAX
                    X=A
                    If X=0 Then Z=1 Else Z=0:N=X Shr 7
                    pc+1
                    cycle+2
                Case 173'AD LDA Absolute
                    A=readmem(readmem(pc+2) Shl 8 + readmem(pc+1))
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+3
                    cycle+4
                Case 177'B1 LDA (Indirect),Y
                    par=readmem(pc+1)
                    p2=readmem(par)
                    A=readmem(readmem(par+1) Shl 8 + p2 + Y)
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    If p2+Y>255 Then cycle=6 Else cycle=5
                    pc+2
                Case 185'B9 LDA Absolute, Y
                    addr=readmem(pc+2) Shl 8 + readmem(pc+1)
                    A=readmem(addr+ Y)
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+3   
                    cycle=cycle+4+((addr Shr 8) =((addr+Y)Shr 8) )
                Case 189'BD LDA Absolute,X
                    addr=readmem(pc+2) Shl 8 + readmem(pc+1)
                    A=readmem(addr+ X)
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+3   
                    cycle=cycle+4+((addr Shr 8) =((addr+X)Shr 8) )
                Case 198'C6 DEC Zero-page
                    par=readmem(pc+1)
                    par2=(256+(readmem(par)-1)) Mod 256
                    writemem(par,par2)
                    'Print "      DEC "+par+" ="+readmem(par)
                    pc+2
                    cycle+5
                    N=par2 Shr 7
                    Z=(par2=0)
                Case 200'C8 DEY
                    X=(256+Y-1) Mod 256
                    pc+1
                    cycles+2
                    N=Y Shr 7
                    Z=(Y=0)
                Case 201'C9 CMP
                    T=(256+A-readmem(pc+1)) Mod 256
                    C=(T>=0)
                    Z=(T=0)
                    N=T Shr 7
                    pc+2
                    cycle+2
                Case 202'CA DEX X - 1 -> X
                    X=(256+X-1) Mod 256
                    pc+1
                    cycles+2
                    N=X Shr 7
                    Z=(X=0)
                Case 208'D0 BNE Relative
                    If Z=0 Then
                        param=readmem(pc+1)
                        oldpc=pc
                        pc=pc+1+(param Mod 128)-((param>127)*127)
                        cycle=cycle+3+(RoundDown((oldpc-32768)/16384.0)<>RoundDown((pc-32768)/16384.0))'OPTIMOI!
                    Else
                        pc+2
                        cycle+2
                    EndIf
                    'Print "hyaa"
                Case 216'D8 CLD 0 -> D(D-flagilla ei nesissä ole mitään käyttöä)
                    D=0
                    pc+1
                    cycle+2
                Case 224'E0 CPX
                    T=(256+X-readmem(pc+1)) Mod 256
                    C=(T>=0)
                    Z=(T=0)
                    N=T Shr 7
                    pc+2
                    cycle+2
                Case 230'E6 INC Zero Page
                    addr=readmem(pc+1)
                    writemem(addr,(readmem(addr)+1) Mod 256)
                    pc+2
                    cycle+5
                Case 232'E8 INX
                    X=(X+1)*(X<>255)
                    If X=0 Then Z=1 Else Z=0:N=X Shr 7'FLAGIT
                    pc+1
                    cycle+2
                Case 234'EA NOP
                    pc+1
                    cycle+2
                Case 240'F0 BEQ
                    If Z=1 Then
                        param=readmem(pc+1)
                        oldpc=pc
                        pc=pc+1+(param Mod 128)-((param>127)*127)
                        cycle=cycle+3+(RoundDown((oldpc-32768)/16384.0)<>RoundDown((pc-32768)/16384.0))'OPTIMOI!
                    Else
                        pc+2
                        cycle+2
                    EndIf
                Case 253'FD SBC Absolute,X
                    'addr=readmem(pc+2) Shl 8 + readmem(pc+1)
                    par1=readmem(pc+2)
                    par2=readmem(pc+1)
                    addr=par1 Shl 8 + par2 +X
                    A=A-readmem(addr)
                    If A<0 Then A=0:C=1 Else C=0
                    'owerflowi puuttuu
                    pc+3
                    cycle=cycle+4+par2+X>255
                    Color cbmagenta
                Default'jos komentoa ei löytynyt
                    cycle+1
                    pc+1
                    Color 255,0,0
                    Print op+" "+Right(Hex(op),2)
                    'Wait 2000
            EndSelect
        'Print op+" "+Right(Hex(op),2)
        Color 255,255,255
        ppu()
        'printstatus()
        'If N>1 Or N<0 Then Wait 5000
        'WaitKey
        'If KeyDown(cbkeyp) Then printstatus()
        'If Not KeyDown(cbkeyspace) Then WaitKey' Else Wait 1'50
        //vblankin generointi
        If cycle<4 Or cycle>(cycle2+29829) Or KeyDown(cbkeyv) Then  cycle2=cycle:ppu_vblank=1:Print "vblank"':Wait 2000
    Wend



    Function readmem(address)//koko prossun muisti
        If address=>32768 Then//ROM
            Select mapper
                Case 0'NOMAPPER
                    If rombanks=1 Then 'käsittely yhdellä rompankilla, se peilautuu ylä ja alapankkiin.
                        'Print (address-32768) Mod 16384
                        Return PeekByte(rom,(address-32768) Mod 16384)
                    ElseIf rombanks=2 'kahdella rompankilla ne näkyy peräkkäin(ei testattu)
                        'Print address-32768
                        Return PeekByte(rom,address-32768)
                    Else
                        MakeError rombanks+" pankkisia NOMAPPER-romeja ei ole"+chr(10)+"(an error that was impossible to occur has just occured.)"
                    EndIf
                 Default
                    If rombanks=1 Then 'nomapper
                        Return PeekByte(rom,(address-32768) Mod 16384)
                    Else
                        Return PeekByte(rom,address-32768)
                    EndIf
                    'MakeError mapper+"ei ole tuettu mapperi"
            EndSelect
        ElseIf address<8192 Then//RAM
            Return PeekByte(ram,address)
        ElseIf address=>8192 And address<16383 Then//Registers
            Select address Mod 8
                Case 2'$2002
                    ret=ppu_vblank Shl 7//eivalmis
                    ppu_vblank=0
                    Return ret
                'Case 4'04 Sprite Memory Data
                Case 7
                    Print "PPUIO luku"
                Default
                    Print "hassu rekisteri(luku) "+Right(Hex(address),4)
                    Return 0
            EndSelect
        ElseIf address=>16384 And address<=16407 Then'toinen rekisterialue
            Select address - 16384
                Case 22
                    Print "Ohjain 1 luku"
                    Return Rand(255)
                Case 23
                    Print "Ohjain 2 luku"
                Default
                    Print "hassu rekisteri(luku) "+Right(Hex(address),4)
            EndSelect
        Else'jos osoitteella ei ole muistia niin toimitaan näin.
            Print "hassu lukuosoite "+ address
            Return address Shr 8
        EndIf
    EndFunction

    Function writemem(address, arvo)//koko prossun muisti
        If address<=8191 Then //RAM
            PokeByte ram,address Mod 2048, arvo
        ElseIf address=>8192 And address<16383 Then//Registers
            Select address mod 8 'korvaa printit lukemisella
                Case 0'$2000
                    Print "PPUCNT1 kirjoitus"
                Case 1'$2001
                    Print "PPUCNT2 kirjoitus"
                Case 3'$2003
                    Print "SPRADDR kirjoitus"
                Case 4'$2004
                    Print "SPRIO kirjoitus"
                Case 5'$2005
                    Print "BGSCROL kirjoitus"
                Case 6'$2006
                    Print "PPUADDR kirjoitus"
                    ppuaddr_turn=((ppuaddr_turn+1)<2)
                    If ppuaddr_turn=1 Then
                        ppuaddr_addr=ppuaddr_addr Shl 24 Shr 24 + arvo Shl 8
                        'ppuaddr_addr0=arvo
                    Else
                        ppuaddr_addr=ppuaddr_addr Shr 8 Shl 8 +arvo
                        'ppuaddr_addr1=arvo
                    EndIf
                Case 7'$2007
                    Print "PPUIO kirjoitus"
                    'huom:
                    'Write data into $2007. After each write, the address will auto-increment by 1, or 32 (if Bit #2 of $2000 is 1).
                    'muista mirroring tähän,lukuun ja näyttikseen kun mapperit lisääntyy
                   
                    PokeByte ppuram, ppuaddr_addr, arvo
                    ppuaddr_addr+1
                    'Print ppuaddr_addr+ " "+arvo
                    'Wait 400
                Default
                    Print "hassu rekisteri(kirjoitus) "+Right(Hex(address),4)
                    Return 0
            EndSelect
        ElseIf address=>16384 And address<=16407 Then
            Select address - 16384
                Case 22
                    Print "Ohjain 1 kirjoitus"
                Case 23
                    Print "Ohjain 2 kirjoitus"
                Default
                    Print "hassu rekisteri(kirjoitus) "+Right(Hex(address),4)
            EndSelect
        ElseIf address=>32768 Then//ROM
            Select mapper
                Case 0
                    Print "Ohjelmaromiin kirjoitus ei tee mitään tällä mapperilla"
                Default
                    Print "tuntematon mapperi tekee jotain"
            EndSelect
        Else'jos osoitteella ei ole muistia niin toimitaan näin.
            Print "hassu kirjoitusosoite "+address
        EndIf
    EndFunction

    Function ppu()
         //kuva kerrallaan piirto, korjataan scanline/pikseli kerrallaan piirrolla.
        'SetWindow ""+cycle
        If cycle>1789773/60 Or KeyDown(cbkeyv) Then 
            SetWindow (Timer()-aika)+" "+1000/60+"sta"
            cycle=0
            t=Timer()
            Select rendermethod
                Case 0
                    DrawToImage scr
                    Lock
                        For sy=0 To 239
                            For til=0 To 31
                                tilenumber=PeekByte(ppuram,8192+(RoundDown(sy/8.0)*32+til))+256
                                byte1=PeekByte(vrom,tilenumber*16+(sy Mod 8)-1)
                                byte2=PeekByte(vrom,tilenumber*16+(sy Mod 8)+7)
                                pal=PeekByte(ppuram,9152+RoundDown(sy/32.0)*8+RoundDown(til/4.0)) Shl (24+2*(RoundDown(til/2+1)Mod 2)+4*(RoundDown(sy/16+1) Mod 2)) Shr 30
                                For sx=0 To 7
                                    PutPixel2 til*8+sx,sy,palette(PeekByte(ppuram,16128+(((byte1 Shr (7-sx)) Mod 2))+((((byte2 Shr (7-sx)) Mod 2))Shl 1)+(pal) Shl 2))
                                    'SetWindow ""+(PeekByte(ppuram,16128+(byte1 Shr (7-sx)) Mod 2+((byte2 Shr (7-sx)) Mod 2)Shl 1 +pal Shl 2))+" "+((byte1 Shr (7-sx)) Mod 2+((byte2 Shr (7-sx)) Mod 2)Shl 1 +pal Shl 2)+ " " +pal
                                Next sx
                            Next til
                        Next sy
                    Unlock
                    DrawToScreen
                Case 1
                    Randomize 12342565
                    For l=4 To 7'tilet
                        DrawToImage renderlayers(l)
                            Color cbblack
                            Box 0,0,256,240
                            j=l-4
                            For i=0 To 959
                                'DrawImageBox maskedtiles(j),i*8 Mod 256,RoundDown(i/32.0)*8,(PeekByte(ppuram,8192+i)+256)Shl 3,0,8,8
                                CopyBox (PeekByte(ppuram,8192+i)+256)Shl 3,0,8,8,(i Shl 3) Mod 256,RoundDown(i/32.0)Shl 3,Image(maskedtiles(j)),Image(renderlayers(l))
                            Next i
                    Next l
                    For l=0 To 3'tausta
                        DrawToImage renderlayers(l)
                            Color cbblack
                            Box 0,0,256,240
                            For i=0 To 63
                                pal=PeekByte(ppuram,9152+i)
                                Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 30) Shr 28)+l))
                                Box i*32 Mod 256, RoundDown(i/8.0)*32,16,16
                                Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 28) Shr 30)Shl 2+l))
                                Box (i*32+16) Mod 256, RoundDown(i/8.0)*32,16,16
                                Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 26) Shr 30)Shl 2+l))
                                Box i*32 Mod 256, RoundDown(i/8.0)*32+16,16,16
                                Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 24) Shr 30)Shl 2+l))
                                Box (i*32+16) Mod 256, RoundDown(i/8.0)*32+16,16,16
                            Next i
                            DrawImage renderlayers(l+4),0,0
                        DrawToScreen
                    Next l
                    DrawToImage scr
                    Color cbblack
                    Box 0,0,256,240
                    DrawImage renderlayers(0),0,0
                    DrawImage renderlayers(1),0,0
                    DrawImage renderlayers(2),0,0
                    DrawImage renderlayers(3),0,0
                    DrawToScreen
            EndSelect
            Color cbblack
            Box 200,0,256,240
            DrawImage scr,200,0' kaksi piirtoa koska print pistää koordinaatit sekaisin
            'DrawImage scr,200,150
            DrawScreen OFF
            SetWindow ""+(Timer()-t)
            aika=Timer()
        EndIf
    EndFunction

    Function printstatus()
        Print "A:"+A+" X:"+X+" Y:"+Y+"  N:"+N+" V:"+V+" I:"+I+" Z:"+Z+" C:"+C+"  S(tack):"+S+" PC:"+Right(Hex(PC),4)+" ppuaddr:"+ppuaddr_addr
    EndFunction



    //relative (param Mod 128)-((param>127)*127)
    //absolute readmem(readmem(pc+2) Shl 8 + readmem(pc+1))


Muutokset kakkosesta:

Code: Select all

            Case 9'09 ORA
                par=readmem(pc+1)
                A=((A Shr 7) Or (par Shr 7)) Shl 7 +((A Shl 25 Shr 31) Or (par Shl 25 Shr 31)) Shl 6 +((A Shl 26 Shr 31) Or (par Shl 26 Shr 31)) Shl 5+((A Shl 27 Shr 31) Or (par Shl 27 Shr 31)) Shl 4+((A Shl 28 Shr 31) Or (par Shl 28 Shr 31)) Shl 3+((A Shl 29 Shr 31) Or (par Shl 29 Shr 31)) Shl 2+((A Shl 30 Shr 31) Or (par Shl 30 Shr 31)) Shl 1+((A Shl 31 Shr 31) Or (par Shl 31 Shr 31))
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7
                pc+2
                cycle+2
->

Code: Select all

            Case 9'09 ORA
                    A=_or(A,readmem(pc+1))
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7
                    pc+2
                    cycle+2

Code: Select all

            Case 41'29 AND Immediate
                par=readmem(pc+1)
                A=((A Shr 7) And (par Shr 7)) Shl 7 +((A Shl 25 Shr 31) And (par Shl 25 Shr 31)) Shl 6 +((A Shl 26 Shr 31) And (par Shl 26 Shr 31)) Shl 5+((A Shl 27 Shr 31) And (par Shl 27 Shr 31)) Shl 4+((A Shl 28 Shr 31) And (par Shl 28 Shr 31)) Shl 3+((A Shl 29 Shr 31) And (par Shl 29 Shr 31)) Shl 2+((A Shl 30 Shr 31) And (par Shl 30 Shr 31)) Shl 1+((A Shl 31 Shr 31) And (par Shl 31 Shr 31))
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+2
                cycle+2
->

Code: Select all

            Case 41'29 AND Immediate
                    A=_and(A,readmem(pc+1))
                    If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                    pc+2
                    cycle+2

Code: Select all

            Case 44'2C BIT
                par=readmem(pc+1)
                T=((A Shr 7) And (par Shr 7)) Shl 7 +((A Shl 25 Shr 31) And (par Shl 25 Shr 31)) Shl 6 +((A Shl 26 Shr 31) And (par Shl 26 Shr 31)) Shl 5+((A Shl 27 Shr 31) And (par Shl 27 Shr 31)) Shl 4+((A Shl 28 Shr 31) And (par Shl 28 Shr 31)) Shl 3+((A Shl 29 Shr 31) And (par Shl 29 Shr 31)) Shl 2+((A Shl 30 Shr 31) And (par Shl 30 Shr 31)) Shl 1+((A Shl 31 Shr 31) And (par Shl 31 Shr 31))
                If T=0 Then Z=1 Else Z=0
                N=par Shr 7
                V=par Shl 25 Shr 31
                pc+3
                cycle+4
->

Code: Select all

             Case 44'2C BIT
                par=readmem(pc+1)
                T=_and(A,par)
                If T=0 Then Z=1 Else Z=0
                N=par Shr 7
                V=par Shl 25 Shr 31
                pc+3
                cycle+4

Code: Select all

    //flagit ja rekisterit
    Global C,Z,I,D,B,V,N'flageja asmidokumentissa n on s statusrekisteristä
    Global A,X,Y'rekisterit
    Global S'stack
    Global ppu_vblank,ppuaddr_addr,ppuaddr_addr0,ppuaddr_addr1,ppuaddr_turn
    Global cycle,PC

->

Code: Select all

    //flagit ja rekisterit
    Global C,Z,I,D,B,V,N'flageja asmidokumentissa n on s statusrekisteristä
    Global A,X,Y'rekisterit
    Global S'stack
    Global ppu_vblank,ppuaddr_addr,ppuaddr_addr0,ppuaddr_addr1,ppuaddr_turn
    Global cycle,PC

    //loogisten operaattoreiden esilasku
    Print "logical operations"
    Dim _and(255,255)
    Dim _or(255,255)
    //esilasketaan kaikki kahden tavun väliset andit ja orit
    For x=0 To 255 
        For y=0 To 255
            _and(x,y)=((y Shr 7) and (x Shr 7)) Shl 7 +((y Shl 25 Shr 31) and (x Shl 25 Shr 31)) Shl 6 +((y Shl 26 Shr 31) and (x Shl 26 Shr 31)) Shl 5+((y Shl 27 Shr 31) and (x Shl 27 Shr 31)) Shl 4+((y Shl 28 Shr 31) and (x Shl 28 Shr 31)) Shl 3+((y Shl 29 Shr 31) and (x Shl 29 Shr 31)) Shl 2+((y Shl 30 Shr 31) and (x Shl 30 Shr 31)) Shl 1+((y Shl 31 Shr 31) and (x Shl 31 Shr 31))
        Next y
    Next x
    For x=0 To 255 
        For y=0 To 255
            _or(x,y)=((y Shr 7) Or (x Shr 7)) Shl 7 +((y Shl 25 Shr 31) Or (x Shl 25 Shr 31)) Shl 6 +((y Shl 26 Shr 31) Or (x Shl 26 Shr 31)) Shl 5+((y Shl 27 Shr 31) Or (x Shl 27 Shr 31)) Shl 4+((y Shl 28 Shr 31) Or (x Shl 28 Shr 31)) Shl 3+((y Shl 29 Shr 31) Or (x Shl 29 Shr 31)) Shl 2+((y Shl 30 Shr 31) Or (x Shl 30 Shr 31)) Shl 1+((y Shl 31 Shr 31) Or (x Shl 31 Shr 31))
        Next y
    Next x

Code: Select all

    Print "tiedostokoko: "+size+"(muistia "+(rombanks*16384+vrombanks*8192)+")tavua"
->

Code: Select all

    Print "tiedostokoko: "+size+"(muistia "+(rombanks*16384+vrombanks*8192)+")tavua"
    Print "precalcing..."
(printti lisää)

Code: Select all

    'prerendataan tilet
    Print "tiles"
    For i=0 To 3
        maskedtiles(i)=MakeImage(512*8*vrombanks,8)
        DrawToImage maskedtiles(i)
            For j=1 To 512*vrombanks
                For y=0 To 7
                    byte1=PeekByte(vrom,j*16-1+y)
                    byte2=PeekByte(vrom,j*16+7+y)
                    For x=0 To 7
                        If Not (((byte1 Shr (7-x)) Mod 2))+((((byte2 Shr (7-x)) Mod 2))Shl 1)=i Then PutPixel j*8+x,y,255 Shl 16 +255
                        'PutPixel j*8+x,y,((byte1 Shl (25+x)) Shr 31 + (byte2 Shl (25+x)) Shr 30)*64
                    Next x
                Next y
                'If (j Mod 4) then Line j+i,0,j+i+8,8
            Next j
        DrawToScreen
    Next i
->

Code: Select all

    'prerendataan tilet
    Print "tiles"
    For i=0 To 3
        maskedtiles(i)=MakeImage(512*8*vrombanks,8)
        DrawToImage maskedtiles(i)
            For j=1 To 512*vrombanks
                For y=0 To 7
                    byte1=PeekByte(vrom,j*16-1+y)
                    byte2=PeekByte(vrom,j*16+7+y)
                    For x=0 To 7
                        If Not (((byte1 Shr (7-x)) Mod 2))+((((byte2 Shr (7-x)) Mod 2))Shl 1)=i Then PutPixel j*8+x,y,255 Shl 16 +255
                        'PutPixel j*8+x,y,((byte1 Shl (25+x)) Shr 31 + (byte2 Shl (25+x)) Shr 30)*64
                    Next x
                Next y
                'If (j Mod 4) then Line j+i,0,j+i+8,8
            Next j
        DrawToScreen
    Next i

    Print "ready."
(Select op)

Code: Select all

                Case 10'0A
->

Code: Select all

                Case 10'0A ASL
Vihdoinkin löytyi tälle ominaisuudelle oikeaa käyttöä.
Jollain vertailuohjelmalla on helppo hakea muutokset jos huvittaa: http://www.quickdiff.com/index.php
atomimalli
Moderator
Moderator
Posts: 227
Joined: Wed Aug 29, 2007 3:55 pm

Re: Nes-emulaattori ja nippelitietoa nesistä.

Post by atomimalli »

Pitääpä komeasti tuplapostata, vaikka kuinka kauheaa se olisikin. On edellisestä kuitenkin aikaa jo puolisen vuotta.

En ole tehnyt projektille mitään, mutta tuosta on helppo kenen tahansa jatkaa. Rakenne näytti ainakin omaan silmään vieläkin aivan selkeältä :P Se selitetään alkuviestissä mielestäni varsin hyvin. Tällä hetkellä suurin ongelma on komentojen toteutuksen puuttuminen. Melko pian voisi myös ohjaimen luvusta randomin korvata näppäimistöltä otetulla syötteellä. Se ei ole vaikea juttu. Yksi bitti vastaa yhtä nappia. Sen koodin paikka löytyy muistinlukufunktiosta.

Varsinainen syy postaamiseeni on Bisqwitin tuore video, jossa valmistuu nes-emulaattori alusta loppuun puolessa tunnissa. Video on tehty niin, ettei tyhjät tauot tallennu. Video on sekä opettavainen, että muutenkin mielenkiintoista katseltavaa: http://www.youtube.com/watch?v=y71lli8MS8s
EDIT:

Tuo kuitenkin eroaa toiminnaltaan tästä siinä, että pyrkii nopeuden sijaan tarkkuuteen. Tuossa jokaisen komennon aikana päivitetään näyttö ja äänet useamman kerran(syklinlisäys kutsuu niitä ja muistikutsut kutsuu syklinlisäystä), kun taas tässä näyttö päivitetään kertaalleen tasan silloin kun se piirretään ja syklilaskuria päivitetään myös vain kerran komennossa. Syklilaskuri tarkistetaan myös vain komennon välein pääluupin lopussa. Tuo myös tekee komennon valinnan mystisellä taulukolla(se pitkä merkkijonohässäkkä kohtassa 1:35), joka on tulkittu suoraan prosessorin piiriltä. Se mahdollistaa osan alkupostissa kuvaulluista "laittomista" komennoista ja suorittaa komennot komentotason sijaan mikrokooditasolla. Tuo ei ole hirveän tehokasta, mutta joidenkin pelien kohdalla tuo vaikuttaa tarkkuuteen. Tuosta näkee kyllä jännästi, miten 6502 oikeastaan toimii. Tässä taas on vaan tuollainen select kaikkia komentoja varten. Jokainen tarkistaa rekisterit, tekee jotain ja päivittää laskurit.

Ilmeisesti tuo pääsee pian siihen varsinaiseen nesin toteutukseen jonkin verran kahden minuutin jälkeen, mutten vielä tiedä, kun en ole päässyt kahden minuutin yli ja nyt pitää nukkua.

Sitten voisin ehdottaa uutta testipeliä: Sack of flour, muistaakseni se oli tehty jollakin basic-kääntäjällä, mikä on hieman erikoista sillä yleensä halutaan noilla laitteilla käyttää konekieltä tai assemblyä nopeuden, optimoitavuuden ja muiden kikkojen vuoksi. Tässä on video siitä: http://www.youtube.com/watch?v=h8qaOsoj5Og, lataussivu: http://bobrost.com/nes/games.php, ja vielä linkki käytetyn basicin manuaaliin: http://bobrost.com/nes/files/nbasic_manual.html Tämä on ns. laillinen romi :P

Ohjelmointiopassarja nesille: http://www.youtube.com/watch?v=pASatutl2Ik
Selitystä nesin graffoista: http://www.youtube.com/watch?feature=pl ... nPg#t=194s

Ei nyt tähän väliin tule isompaa mieleen. Olisin voinut varmaan tuon ohjauksen itse pistää esimerkiksi mutta haluan päästä tekemään introa kun on enää pari päivää aikaa.
EDIT:

Jos jotakuta kiinnostaa lisätä tähän jotain muuta kuin tuon ohjauksen tai komentoja, niin voisi toteuttaa mmc1-mapperin. Se on se, mitä mm. Zeldat ja metroid käyttää. Sen toteutukseen voi katsoa mallia bisqwitin videosta kohdasta 10:30 tai nesdev-wikistä http://wiki.nesdev.com/w/index.php/INES_Mapper_001

atomimalli
Moderator
Moderator
Posts: 227
Joined: Wed Aug 29, 2007 3:55 pm

Re: Nes-emulaattori ja nippelitietoa nesistä.

Post by atomimalli »

En olisi vielä viitsinyt lisätä uutta versiota, kun tässä on vielä aika paljon tehtävää mielessä, mutta aion tähän väliin toteuttaa aika isoja rakenteellisia muutoksia seuraavaksi.
Muokattua tässä:
-korjattu latauksen, suorituksen ja piirtämisen bugeja
-lisätty muutama komento
-lisätty debug-työkaluja
-aloitettu siirros funktioiden käytöstä gosubiin
-lisätty MMC3-pankkienvaihtelu(banking modet lisäämättä). Sack of flourin valikko toimii.
-lisätty etappisuoritus, käyttöä tulee enemmän keskeytysten ja PPU:n kehityksen myötä.

Branchin kanssa (Branchista on ennenkin ollut apua tässä) eilen paikannettiin pahimmat pullonkaulat ja haettiin niihin ratkaisuja. Pahin oli funktiokutsut. Gosubeilla niihin kuluu monta kertaa vähemmän aikaa. Tällä menetetään rekursiivisuuskyky, mutta se ei hattaa tässä. Toinen suuri muutos on vaihtaa bankkien tarkistus iffeillä siihen että ne todella vaihdetaan memcopyllä. Memcopy ei vie käytännössä yhtään aikaa, vaikka useat pelit tarvii sitä satoja kertoja sekunnissa. Olin jo suunnitellut etappiajosysteemiä tälle ja nyt se tuli toteutettua. Sillä saa harvennettua tarkistuksia joita ei tarvi jokakierros. Siitä siis repeatti pääsilmukan sisällä. readmem-funktio tulee toivottavasti poistumaan kokonaan.

Testasin readmemin muuttamista gosubiksi muutamassa kohtaa, ja nopeusetu ainakin zeldatitlescreenin ikiloopin kohdalla on noin kaksinkertainen. Nyt sen fps on minulla noin 12, kun ennen se oli vain 4. Vastaavat pidemmät loopit saa esikäsittelyllä vaihdettua myöhemmin oikotiekomentoihin, jolloin fps riippuu vain ruudunpiirron nopeudesta.

Tällä hetkellä vapaata tehtävää olisi ohjaus ja uudet komennot(readmem-gosubeilla tehtynä). Skrollaus ja spritet olisi tarpeellisia, mutta niitä ei nyt kannata varmaan muutoksen keskellä näpelöidä. Niiden on luultavasti oleellista olla jotenkin synkassa rendauksen kanssa että kaikki menisi hyvin.
Mappereiden toimintaa aion muuttaa, joten siihen ei varmaan kannata koskea. Komentoja on tällä hetkellä päälle 60, eli vähän alle puolet on vielä jäljellä.

Debuggausta varten tein pienen muistiselaimen, jota pääsee käyttämään deetä painamalla. Sillä näkee myös näytön tilenumerot(kirkkaus ja binääri pienillä punaisilla pilkuilla) ja tilemuistin kun painaa ässää. Oikeastaan koko näyttömuisti ei näy vielä. Sitä on neljä näytöllistä skrollausta varten.

rev. 4

Code: Select all

'This is your first CoolBasic program!

SCREEN 800,300

'avaa rom
file$="zeldatitlescreen\zelda.nes"  'http://nesdev.parodius.com/zelda.zip
'file$="sof.nes" 'Sack of flour: Heart of gold http://bobrost.com/nes/games.php
'file$="fighter_f8000\fighter_f8000.nes" 'http://nesdev.parodius.com/fighter_f8000.zip
'file$="NEStress\NEStress.nes" 'http://nesdev.parodius.com/NEStress.zip
'file$="fighter_f8000\f8000.nes"
'file$="soda cans\sprite.nes"
'file$=CommandLine()


//headerin käsittely
f=OpenToRead(file$)
size=FileSize(file$)

Global rombanks,vrombanks,mapper
headercheck=(ReadInt(f)=441664846)
rombanks=ReadByte(f)
vrombanks=ReadByte(f)
t=ReadByte(f)
mapper=t Shr 4 + ReadByte(f) Shr 4
mirroring=t Mod 16


'infoa
Print "headerin tarkistus: "+headercheck
Print "rombanks: "+rombanks
Print "vrombanks: "+vrombanks
Print "mapper: "+mapper
Print "mirroring:  "+mirroring
Print "tiedostokoko: "+size+"(muistia "+(rombanks*16384+vrombanks*8192)+")tavua"
Print "precalcing..."

//luetaan muistipalikoihin
SeekFile f,16
Global rom,vrom,ram,ppuram
rom=MakeMEMBlock(rombanks*16384)
vrom=MakeMEMBlock(vrombanks*8192*2)'*2)
For i=0 To 16384*rombanks-1
    PokeByte rom, i, ReadByte(f)
Next i

For i=0 To 2*8192*vrombanks-1
    PokeByte vrom, i, ReadByte(f)
Next i

ram=MakeMEMBlock(2048)
ppuram=MakeMEMBlock(16384)


//mapperikohtaiset globaalit ja alustus
Global prgbank0addr,prgbank1addr,prgbank2addr,prgbank3addr, bankselect
Global chrbank0addr,chrbank1addr,chrbank2addr,chrbank3addr,chrbank4addr,chrbank5addr
Select mapper
    Case 4
        prgbank0addr=0
        prgbank1addr=0
        prgbank2addr=16384*rombanks-8192*2
        prgbank3addr=16384*rombanks-8192
EndSelect

//flagit ja rekisterit
Global C,Z,I,D,B,V,N'flageja asmidokumentissa n on s statusrekisteristä
Global A,X,Y'rekisterit
Global S'stack
Global ppu_vblank,ppuaddr_addr,ppuaddr_addr0,ppuaddr_addr1,ppuaddr_turn
Global ppu_ctrl_nametable_address,ppu_ctrl_increment,ppu_ctrl_sprite_patt_addr
Global ppu_ctrl_bg_patterntable_tileoffset,ppu_master_slave,ppu_ctrl_sprite_size,ppu_enable_vblank_nmi
Global cycle,PC

//loogisten operaattoreiden esilasku
Print "logical operations"
Dim _and(255,255)
Dim _or(255,255)
//esilasketaan kaikki kahden tavun väliset andit ja orit
For x=0 To 255 
    For y=0 To 255
        _and(x,y)=((y Shr 7) and (x Shr 7)) Shl 7 +((y Shl 25 Shr 31) and (x Shl 25 Shr 31)) Shl 6 +((y Shl 26 Shr 31) And (x Shl 26 Shr 31)) Shl 5+((y Shl 27 Shr 31) And (x Shl 27 Shr 31)) Shl 4+((y Shl 28 Shr 31) And (x Shl 28 Shr 31)) Shl 3+((y Shl 29 Shr 31) And (x Shl 29 Shr 31)) Shl 2+((y Shl 30 Shr 31) And (x Shl 30 Shr 31)) Shl 1+((y Shl 31 Shr 31) And (x Shl 31 Shr 31))
    Next y
Next x
For x=0 To 255 
    For y=0 To 255
        _or(x,y)=((y Shr 7) Or (x Shr 7)) Shl 7 +((y Shl 25 Shr 31) Or (x Shl 25 Shr 31)) Shl 6 +((y Shl 26 Shr 31) Or (x Shl 26 Shr 31)) Shl 5+((y Shl 27 Shr 31) Or (x Shl 27 Shr 31)) Shl 4+((y Shl 28 Shr 31) Or (x Shl 28 Shr 31)) Shl 3+((y Shl 29 Shr 31) Or (x Shl 29 Shr 31)) Shl 2+((y Shl 30 Shr 31) Or (x Shl 30 Shr 31)) Shl 1+((y Shl 31 Shr 31) Or (x Shl 31 Shr 31))
    Next y
Next x
//piirto
Global rendermethod
rendermethod=1
Dim renderlayers(7)
Dim maskedtiles(3)

Global scr
scr=MakeImage(256,240)
Dim palette(64)
palette(0)=8158332:palette(1)=252:palette(2)=$188:palette(3)=4466876
palette(4)=9699460:palette(5)=9699460:palette(6)=11014144:palette(7)=8918016
palette(8)=5255168:palette(9)=30720:palette(10)=26624:palette(11)=22528
palette(12)=16472:palette(13)=1:palette(14)=1:palette(15)=1
palette(16)=12369084:palette(17)=30968:palette(18)=22776:palette(19)=6833404
palette(20)=14155980:palette(21)=14942296:palette(22)=16267264:palette(23)=14965776
palette(24)=11303936:palette(25)=47104:palette(26)=43008:palette(27)=43076
palette(28)=34952:palette(29)=1:palette(30)=1:palette(31)=1
palette(32)=16316664:palette(33)=3980540:palette(34)=6850812:palette(35)=9992440
palette(36)=16283896:palette(37)=16275608:palette(38)=16283736:palette(39)=16556100
palette(40)=16300032:palette(41)=12122136:palette(42)=5822548:palette(43)=5830808
palette(44)=59608:palette(45)=7895160:palette(46)=1:palette(47)=1
palette(48)=16579836:palette(49)=10806524:palette(50)=12105976:palette(51)=14203128
palette(52)=16300280:palette(53)=16295104:palette(54)=15782064:palette(55)=16572584
palette(56)=16308344:palette(57)=14219384:palette(58)=12122296:palette(59)=$12122328
palette(60)=64764:palette(61)=16308472:palette(62)=1:palette(63)=1

For i=0 To 3 'rendermethod 1 juttuja
    renderlayers(i)=MakeImage(256,240)
    MaskImage renderlayers(i),cbmagenta
Next i
For i=4 To 7
    renderlayers(i)=MakeImage(256,240)
    MaskImage renderlayers(i),cbblack
Next i
'prerendataan tilet
Print "tiles"
For i=0 To 3
    maskedtiles(i)=MakeImage(512*8*vrombanks,8)
    DrawToImage maskedtiles(i)
        For j=0 To 512*vrombanks
            For y=0 To 7
                byte1=PeekByte(vrom,j*16+y)
                byte2=PeekByte(vrom,j*16+8+y)
                For x=0 To 7
                    If Not (((byte1 Shr (7-x)) Mod 2))+((((byte2 Shr (7-x)) Mod 2))Shl 1)=i Then PutPixel j*8+x,y,255 Shl 16 +255
                    'PutPixel j*8+x,y,((byte1 Shl (25+x)) Shr 31 + (byte2 Shl (25+x)) Shr 30)*64
                Next x
            Next y
            'If (j Mod 4) then Line j+i,0,j+i+8,8
        Next j
    DrawToScreen
Next i


Print "ready."
Locate 0,300

Global aika

//emulaattori
pc=readmem(65533) Shl 8 + readmem(65532)'reset-osoitin, eli aloituskohta osoitteesta $FFFE
ppu_ctrl_nametable_address=8192'$2000-täältä alkaa kuvamuisti oletuksena
ppu_ctrl_increment=1
Print "alkuosoitin: "+pc+" "+Right(Hex(PC),5)
running=1

While running
    Repeat
        address=pc
        Gosub readmem8
        Select ret
            Case 9'09 ORA
                A=_or(A,readmem(pc+1))
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7
                pc+2
                cycle+2
            Case 10'0A ASL
                C=A Shr 7
                A=(A Shl 1) Mod 256
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7
                pc+1
                cycle+2
            Case 16'10 BPL Relative
                If N=0 Then
                    param=readmem(pc+1)
                    oldpc=pc
                    pc=pc+2+(param-(param>127)*256)
                    cycle=cycle+3+(RoundDown((oldpc-32768)/16384.0)<>RoundDown((pc-32768)/16384.0))
                Else
                    pc+2
                    cycle+2
                EndIf
            Case 24'18 CLC
                C=0
                pc+1
                cycle+2
            Case 32'JSR
                writemem(256+S,(PC+2) Shr 8)
                S=(256+S+1) Mod 256
                writemem(256+S,(PC+2) Mod 256)
                S=(256+S+1) Mod 256
                pc=readmem(pc+2) Shl 8 + readmem(pc+1)
                cycle+6
                'pc+3
                Color cbmagenta
            Case 41'29 AND Immediate
                A=_and(A,readmem(pc+1))
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+2
                cycle+2
            Case 42'2A ROL
                T=A Shr 7
                A=A Shl 1 - T Shr 8 +C
                C=T
                pc+1
                cycle+2
            Case 44'2C BIT
                par=readmem(pc+1)
                T=_and(A,par)
                If T=0 Then Z=1 Else Z=0
                N=par Shr 7
                V=par Shl 25 Shr 31
                pc+3
                cycle+4
            Case 48'30 BMI Relative
                If N=1 Then
                    param=readmem(pc+1)
                    oldpc=pc
                    pc=pc+2+(param-(param>127)*256)
                    cycle=cycle+3+(RoundDown((oldpc-32768)/16384.0)<>RoundDown((pc-32768)/16384.0))
                Else
                    pc+2
                    cycle+2
                EndIf
            Case 72'48 PHA
                writemem(S,A)
                S+1
                pc+1
                cycle+3
            Case 76'4C JMP absolute
                address2=pc+1
                Gosub readmem16
                pc=ret
                cycle+3
            Case 96'RTS
                S=(256+S-1) Mod 256
                tS=(256+S-1) Mod 256
                pc=readmem(256+S)+1+readmem(256+tS)Shl 8
                S=tS
                cycle+6
                Color cbmagenta
            Case 101'65 ADC Zero Page
                A=A+readmem(readmem(pc+1))+c
                If A>255 Then C=1:A= A Mod 256 Else C=0
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+2
                cycle+3
            Case 104'68 PLA
                S-1
                A=readmem(S)
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+1
                cycle+4
            Case 105'69 ADC Immediate
                A=A+readmem(pc+1)+c
                If A>255 Then C=1:A= A Mod 256 Else C=0
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+2
                cycle+3
            Case 106'6A ROR
                T=A Mod 2
                A=A Shr 1 + C Shl 7
                C=T
                pc+1
                cycle+2
            Case 109'6D ADC Absolute
                A=A+readmem(readmem(pc+2) Shl 8 + readmem(pc+1))+c
                If A>255 Then C=1:A= A Mod 256 Else C=0
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+3
                cycle+4
            Case 120'78 SEI 1 -> I
                I=1
                pc+1
                cycle+2
            Case 132'84 STY Zero page
                writemem(readmem(pc+1),Y)
                'Print "             STY "+readmem(pc+1)+" ("+readmem(readmem(pc+1))+")"
                pc+2
                cycle+3
            Case 133'85 STA Zero Page
                writemem(readmem(pc+1),A)
                pc+2
                cycle+3
            Case 134'86 STX Zero Page
                writemem(readmem(pc+1),X)
                pc+2
                cycle+3
            Case 136'88 DEY Y - 1 -> Y
                Y=(Y+255) Mod 256
                If Y=0 Then Z=1:N=0 Else Z=0:N=Y Shr 7'FLAGIT
                pc+1
                cycle+2
            Case 138'8A TXA
                A=X
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+1
                cycle+2
            Case 140'8C STY Absolute
                writemem(readmem(pc+2) Shl 8 + readmem(pc+1),Y)
                pc+3
                cycle+4
            Case 141'8D STA Absolute
                writemem(readmem(pc+2) Shl 8 + readmem(pc+1),A)
                pc+3
                cycle+4
            Case 142'8E STX Absolute
                writemem(readmem(pc+2) Shl 8 + readmem(pc+1),X)
                'Print "            "+(readmem(pc+1) Shl 8 + readmem(pc+2))
                pc+3
                cycle+4
            Case 145'91 STA (Indirect),Y
                par=readmem(pc+1)
                writemem((readmem(par+1) Shl 8 + readmem(par) + Y),A)
                'Print "    STA"+"("+par+"),Y="+Y+"  =STA "+(readmem(par+1) Shl 8 + readmem(par) + Y)
                pc+2
                cycle+6
            Case 149'95 STA zero page, X
                writemem((X+readmem(pc+1))Mod 256,A)
                pc+2
                cycle+4
            Case 152'98 TYA
                A=Y
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+1
                cycle+2
            Case 153'99 STA Absolute, Y
                writemem(readmem(pc+2) Shl 8 + readmem(pc+1)+Y,A)
                pc+3
                cycle+5
            Case 154'9A TXS X -> S
                writemem(256+S,X)
                S=(256+S+1) Mod 256
                pc+1
                cycle+2
            Case 157'9D STA Absolute, X
                writemem(readmem(pc+2) Shl 8 + readmem(pc+1)+X,A)
                pc+3
                cycle+5
            Case 160'A0 LDY Immediate
                Y=readmem(pc+1)
                pc+2
                cycle+2
                N=Y Shr 7
                Z=(Y=0)
            Case 162'A2 LDX Immediate
                X=readmem(pc+1)
                pc+2
                cycle+2
                N=X Shr 7
                Z=(X=0)
            Case 164'A4 LDY Zero Page
                Y=readmem(readmem(pc+1))
                If Y=0 Then Z=1:N=0 Else Z=0:N=Y Shr 7'FLAGIT
                pc+2
                cycle+3
            Case 165'A5 LDA Zero Page
                A=readmem(readmem(pc+1))
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+2
                cycle+3
            Case 166'A6 LDX Zero Page
                X=readmem(readmem(pc+1))
                If X=0 Then Z=1:N=0 Else Z=0:N=X Shr 7'FLAGIT
                pc+2
                cycle+3
            Case 168'A8 TAY
                Y=A
                If Y=0 Then Z=1:N=0 Else Z=0:N=Y Shr 7
                pc+1
                cycle+2
            Case 169'A9 LDA Immediate
                A=readmem(pc+1)
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+2
                cycle+2
            Case 170'AA TAX
                X=A
                If X=0 Then Z=1:N=0 Else Z=0:N=X Shr 7
                pc+1
                cycle+2
            Case 172'AC LDY Absolute
                Y=readmem(readmem(pc+2) Shl 8 + readmem(pc+1))
                If Y=0 Then Z=1:N=0 Else Z=0:N=Y Shr 7'FLAGIT
                pc+3
                cycle+4
            Case 173'AD LDA Absolute
                A=readmem(readmem(pc+2) Shl 8 + readmem(pc+1))
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+3
                cycle+4
            Case 174'AE LDX ABSOLUTE
                X=readmem(readmem(pc+2) Shl 8 + readmem(pc+1))
                pc+3
                cycle+4
                N=X Shr 7
                Z=(X=0)
            Case 177'B1 LDA (Indirect),Y
                par=readmem(pc+1)
                p2=readmem(par)
                A=readmem(readmem(par+1) Shl 8 + p2 + Y)
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                If p2+Y>255 Then cycle=6 Else cycle=5
                pc+2
            Case 185'B9 LDA Absolute, Y
                addr=readmem(pc+2) Shl 8 + readmem(pc+1)
                A=readmem(addr+ Y)
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+3   
                cycle=cycle+4+((addr Shr 8) =((addr+Y)Shr 8) )
            Case 189'BD LDA Absolute,X
                addr=readmem(pc+2) Shl 8 + readmem(pc+1)
                A=readmem(addr+ X)
                If A=0 Then Z=1:N=0 Else Z=0:N=A Shr 7'FLAGIT
                pc+3   
                cycle=cycle+4+((addr Shr 8) =((addr+X)Shr 8) )
            Case 192'C0 CPY immediate
                T=(256+Y-readmem(pc+1)) Mod 256
                C=(T>=0)
                Z=(T=0)
                N=T Shr 7
                pc+2
                cycle+2
            Case 198'C6 DEC Zero-page
                par=readmem(pc+1)
                par2=(256+(readmem(par)-1)) Mod 256
                writemem(par,par2)
                'Print "      DEC "+par+" ="+readmem(par)
                pc+2
                cycle+5
                N=par2 Shr 7
                Z=(par2=0)
            Case 200'C8 INY
                X=(Y+1) Mod 256
                If X=0 Then Z=1:N=0 Else Z=0:N=X Shr 7'FLAGIT
                pc+1
                cycles+2
            Case 201'C9 CMP
                T=(256+A-readmem(pc+1)) Mod 256
                C=(T>=0)
                Z=(T=0)
                N=T Shr 7
                pc+2
                cycle+2
            Case 202'CA DEX X - 1 -> X
                X=(256+X-1) Mod 256
                pc+1
                cycles+2
                N=X Shr 7
                Z=(X=0)
            Case 205'CD CMP Absolute
                T=(256+A-readmem(readmem(pc+2) Shl 8 + readmem(pc+1))) Mod 256
                C=(T>=0)
                Z=(T=0)
                N=T Shr 7
                pc+3
                cycle+4
            Case 206'CE DEC Absolute
                par=readmem(pc+2) Shl 8 + readmem(pc+1)
                par2=(256+(readmem(par)-1)) Mod 256
                writemem(par,par2)
                pc+3
                cycle+6
                N=par2 Shr 7
                Z=(par2=0)
            Case 208'D0 BNE Relative
                If Z=0 Then
                    param=readmem(pc+1)
                    oldpc=pc
                    pc=pc+2+(param-(param>127)*256)
                    cycle=cycle+3+(RoundDown((oldpc-32768)/16384.0)<>RoundDown((pc-32768)/16384.0))'OPTIMOI!
                Else
                    pc+2
                    cycle+2
                EndIf
            Case 216'D8 CLD 0 -> D(D-flagilla ei nesissä ole mitään käyttöä)
                D=0
                pc+1
                cycle+2
            Case 224'E0 CPX
                T=(256+X-readmem(pc+1)) Mod 256
                C=(T>=0)
                Z=(T=0)
                N=T Shr 7
                pc+2
                cycle+2
            Case 230'E6 INC Zero Page
                addr=readmem(pc+1)
                result=(readmem(addr)+1) Mod 256
                N=result Shr 7
                Z=(result=0)
                writemem(addr,result)
                pc+2
                cycle+5
            Case 232'E8 INX
                X=(X+1) Mod 256
                If X=0 Then Z=1:N=0 Else Z=0:N=X Shr 7'FLAGIT
                pc+1
                cycle+2
            Case 234'EA NOP
                pc+1
                cycle+2
            Case 238'EE INX Absolute
                addr=readmem(pc+2) Shl 8 + readmem(pc+1)
                result=(readmem(addr)+1) Mod 256
                N=result Shr 7
                Z=(result=0)
                writemem(addr,result)
                pc+3
                cycle+6
            Case 240'F0 BEQ
                If Z=1 Then
                    param=readmem(pc+1)
                    oldpc=pc
                    pc=pc+2+(param-(param>127)*256)
                    cycle=cycle+3+(RoundDown((oldpc-32768)/16384.0)<>RoundDown((pc-32768)/16384.0))'OPTIMOI!
                Else
                    pc+2
                    cycle+2
                EndIf
            Case 253'FD SBC Absolute,X
                'addr=readmem(pc+2) Shl 8 + readmem(pc+1)
                par1=readmem(pc+2)
                par2=readmem(pc+1)
                addr=par1 Shl 8 + par2 +X
                A=A-readmem(addr)
                If A<0 Then A=0:C=1 Else C=0
                'owerflowi puuttuu
                Print "SBC Absolute, X: fixme"//TODO: fixme
                pc+3
                cycle=cycle+4+par2+X>255
                Color cbmagenta
            Default'jos komentoa ei löytynyt
                cycle+1
                pc+1
                Color 255,0,0
                Print op+" "+Right(Hex(op),2)
                printstatus()
                WaitKey
                'Wait 2000
        EndSelect

        'Print op+" "+Right(Hex(op),2)
        Color 255,255,255
        'printstatus()
        'WaitKey
        'If KeyDown(cbkeyp) Then printstatus()
        If KeyDown(cbkeyd) Then debugmem(pc)
        'If Not KeyDown(cbkeyspace) Then WaitKey' Else Wait 1'50
    Until cycle>nextStop
    //vblankin generointi
    If cycle>29829 Or KeyDown(cbkeyv) Then  cycle=cycle-29829:ppu_vblank=1:Print "vblank":ppu()':Wait 2000
    'If cycle>65535 Then  cycle=cycle-65535:ppu_vblank=1:Print "vblank":ppu()
    nextstop=cycle+100 'stoppisysteemi keskeytyksiä ja hienompaa rendausta
Wend

readmem8:
    If address=>32768 Then//ROM
        Select mapper
            Case 0'NOMAPPER
                If rombanks=1 Then 'käsittely yhdellä rompankilla, se peilautuu ylä ja alapankkiin.
                    'Print (address-32768) Mod 16384
                    ret=PeekByte(rom,(address-32768) Mod 16384)
                    Return 
                ElseIf rombanks=2 'kahdella rompankilla ne näkyy peräkkäin(ei testattu)
                    'Print address-32768
                    ret=PeekByte(rom,address-32768)
                    Return 
                Else
                    MakeError rombanks+" pankkisia NOMAPPER-romeja ei ole"+chr(10)+"(an error that was impossible to occur has just occured.)"
                EndIf
             Case 4'MMC3
                Select (address-32768) Shr 13
                    Case 0
                        ret=PeekByte(rom,address-32768+prgbank0addr)
                        Return 
                    Case 1
                        ret=PeekByte(rom,address-32768-8192+prgbank1addr)
                        Return 
                    Case 2
                        ret=PeekByte(rom,address-32768-8192*2+prgbank2addr)
                        Return 
                    Case 3
                        ret=PeekByte(rom,address-32768-8192*3+prgbank3addr)
                        Return 
                 EndSelect
             Default
                If rombanks=1 Then 'nomapper
                    ret=PeekByte(rom,(address-32768) Mod 16384)
                    Return 
                Else
                    ret=PeekByte(rom,address-32768)
                    Return 
                EndIf
                'MakeError mapper+"ei ole tuettu mapperi"
        EndSelect
    ElseIf address<8192 Then//RAM
        ret=PeekByte(ram,address)
        Return 
    ElseIf address=>8192 And address<16383 Then//Registers
        Select address Mod 8
            Case 2'$2002
                ret=ppu_vblank Shl 7//eivalmis
                ppu_vblank=0
                Return
            'Case 4'04 Sprite Memory Data
            Case 7
                Print "PPUIO luku"
            Default
                Print "hassu rekisteri(luku) "+Right(Hex(address),4)
                ret= 0
                Return
        EndSelect
    ElseIf address=>16384 And address<=16407 Then'toinen rekisterialue
        Select address - 16384
            Case 22
                Print "Ohjain 1 luku"
                ret=Rand(255)
                Return 
            Case 23
                Print "Ohjain 2 luku"
            Default
                Print "hassu rekisteri(luku) "+Right(Hex(address),4)
        EndSelect
    Else'jos osoitteella ei ole muistia niin toimitaan näin.
        Print "hassu lukuosoite "+ address
        ret= address Shr 8
        Return 
    EndIf
    
Return

readmem16: //readmemistä puolicopypasteversio voisi olla nopeampi
    address=address2
    Gosub readmem8
    t1=ret
    address=address2+1
    Gosub readmem8
    ret=ret Shl 8 +t1
Return


Function readmem(address)//koko prossun muisti
    If address=>32768 Then//ROM
        Select mapper
            Case 0'NOMAPPER
                If rombanks=1 Then 'käsittely yhdellä rompankilla, se peilautuu ylä ja alapankkiin.
                    'Print (address-32768) Mod 16384
                    Return PeekByte(rom,(address-32768) Mod 16384)
                ElseIf rombanks=2 'kahdella rompankilla ne näkyy peräkkäin(ei testattu)
                    'Print address-32768
                    Return PeekByte(rom,address-32768)
                Else
                    MakeError rombanks+" pankkisia NOMAPPER-romeja ei ole"+chr(10)+"(an error that was impossible to occur has just occured.)"
                EndIf
             Case 4'MMC3
                Select (address-32768) Shr 13
                    Case 0
                        Return PeekByte(rom,address-32768+prgbank0addr)
                    Case 1
                        Return PeekByte(rom,address-32768-8192+prgbank1addr)
                    Case 2
                        Return PeekByte(rom,address-32768-8192*2+prgbank2addr)
                    Case 3
                        Return PeekByte(rom,address-32768-8192*3+prgbank3addr)
                 EndSelect
             Default
                If rombanks=1 Then 'nomapper
                    Return PeekByte(rom,(address-32768) Mod 16384)
                Else
                    Return PeekByte(rom,address-32768)
                EndIf
                'MakeError mapper+"ei ole tuettu mapperi"
        EndSelect
    ElseIf address<8192 Then//RAM
        Return PeekByte(ram,address)
    ElseIf address=>8192 And address<16383 Then//Registers
        Select address Mod 8
            Case 2'$2002
                ret=ppu_vblank Shl 7//eivalmis
                ppu_vblank=0
                Return ret
            'Case 4'04 Sprite Memory Data
            Case 7
                Print "PPUIO luku"
            Default
                Print "hassu rekisteri(luku) "+Right(Hex(address),4)
                Return 0
        EndSelect
    ElseIf address=>16384 And address<=16407 Then'toinen rekisterialue
        Select address - 16384
            Case 22
                Print "Ohjain 1 luku"
                Return Rand(255)
            Case 23
                Print "Ohjain 2 luku"
            Default
                Print "hassu rekisteri(luku) "+Right(Hex(address),4)
        EndSelect
    Else'jos osoitteella ei ole muistia niin toimitaan näin.
        Print "hassu lukuosoite "+ address
        Return address Shr 8
    EndIf
EndFunction

Function writemem(address, arvo)//koko prossun muisti
    If address<=8191 Then //RAM
        PokeByte ram,address Mod 2048, arvo
    ElseIf address=>8192 And address<16383 Then//Registers
        Select address mod 8 'korvaa printit lukemisella
            Case 0'$2000
                Print "PPUCNT1 kirjoitus"
                //Global ppu_ctrl_nametable_address,ppu_ctrl_increment,ppu_ctrl_sprite_patt_addr
                //Global ppu_ctrl_bg_patterntable_addr,ppu_master_slave,ppu_ctrl_sprite_size,ppu_enable_vblank_nmi
                 ppu_ctrl_nametable_address=(arvo Mod 4)*1024+8192
                 ppu_ctrl_increment=((arvo Shr 2) Mod 2)*31+1
                 ppu_ctrl_sprite_patt_addr=((arvo Shr 3) Mod 2)*4096
                 ppu_ctrl_bg_patterntable_tileoffset=((arvo Shr 4) Mod 2)*255
                 ppu_master_slave=((arvo Shl 5) Mod 2)
                 ppu_ctrl_sprite_size=((arvo Shl 6) Mod 2)
                 ppu_enable_vblank_nmi=arvo Shl 7
                 Return 0
            Case 1'$2001
                Print "PPUCNT2 kirjoitus"
                Return 0
            Case 3'$2003
                Print "SPRADDR kirjoitus"
                Return 0
            Case 4'$2004
                Print "SPRIO kirjoitus"
                Return 0
            Case 5'$2005
                Print "BGSCROL kirjoitus"
                Return 0
            Case 6'$2006
                Print "PPUADDR kirjoitus"
                ppuaddr_turn=((ppuaddr_turn+1)<2)
                If ppuaddr_turn=1 Then
                    ppuaddr_addr=ppuaddr_addr Shl 24 Shr 24 + arvo Shl 8
                    'ppuaddr_addr0=arvo
                    Return 0
                Else
                    ppuaddr_addr=ppuaddr_addr Shr 8 Shl 8 +arvo
                    'ppuaddr_addr1=arvo
                    Return 0
                EndIf
                Return 0
            Case 7'$2007
                Print "PPUIO kirjoitus "+ppuaddr_addr+"= "+arvo
                'huom:
                'Write data into $2007. After each write, the address will auto-increment by 1, or 32 (if Bit #2 of $2000 is 1).
                'muista mirroring tähän,lukuun ja näyttikseen kun mapperit lisääntyy
                'If ppuaddr_addr<8192
                    addr=ppuaddr_addr//tässä olisi 
                'ElseIf
                PokeByte ppuram, ppuaddr_addr, arvo
                ppuaddr_addr=(ppuaddr_addr+ppu_ctrl_increment) Mod 16384
                
                'Print ppuaddr_addr+ " "+arvo
                'Wait 400
                Return 0
            Default
                Print "hassu rekisteri(kirjoitus) "+Right(Hex(address),4)
                Return 0
        EndSelect
    ElseIf address=>16384 And address<=16407 Then
        Select address - 16384
            Case 22
                Print "Ohjain 1 kirjoitus"
                Return 0
            Case 23
                Print "Ohjain 2 kirjoitus"
                Return 0
            Default
                Print "hassu rekisteri(kirjoitus) "+Right(Hex(address),4)
                Return 0
        EndSelect
    ElseIf address=>32768 Then//ROM =>$8000
        Select mapper
            Case 0
                Print "Ohjelmaromiin kirjoitus ei tee mitään tällä mapperilla"
            Case 1
                Print "MMC1, implementoimatta!(kirjoitus) "+Right(Hex(address),4)+" "+Right(Hex(arvo),2)
            Case 4
                If address<40959 Then 'bankswitching
                    Select (address Mod 2)
                        Case 0
                            Print "bankselect: "+arvo
                            bankselect=arvo
                            Return  0
                        Case 1
                            Print "bankdata: "+arvo
                            'TODO-mapping modes
                            Select bankselect Mod 8
                                Case 0
                                    chrbank0addr=arvo*2048
                                Case 1
                                    chrbank1addr=arvo*2048
                                Case 2
                                    chrbank2addr=arvo*1024
                                Case 3
                                    chrbank3addr=arvo*1024
                                Case 4
                                    chrbank4addr=arvo*1024
                                Case 5
                                    chrbank5addr=arvo*1024
                                Case 6
                                    chrbank0addr=8192*arvo
                                Case 7
                                    chrbank1addr=8192*arvo
                             EndSelect
                            
                    EndSelect
                Else
                    print "MMC3 kirjoitus implementoimatta: "+Right(Hex(address),4)+" "+Right(Hex(arvo),2)
                EndIf
            Default
                Print "tuntematon mapperi tekee jotain "+Right(Hex(address),4)+" "+Right(Hex(arvo),2)
        EndSelect
    Else'jos osoitteella ei ole muistia niin toimitaan näin.
        Print "hassu kirjoitusosoite "+address
    EndIf
EndFunction

Function nmi()

EndFunction

Function ppu()
     //kuva kerrallaan piirto, korjataan scanline/pikseli kerrallaan piirrolla.
    ulkoaika=Timer()-aika
    cycle=0
    t=Timer()
    Select rendermethod
        Case 0
            //TODO: MMC3 banking support
            DrawToImage scr
            Lock
                For sy=0 To 239
                    For til=0 To 31
                        tilenumber=PeekByte(ppuram,ppu_ctrl_nametable_address+(RoundDown(sy/8.0)*32+til))+1+ppu_ctrl_bg_patterntable_tileoffset
                        byte1=PeekByte(vrom,tilenumber*16+(sy Mod 8))
                        byte2=PeekByte(vrom,tilenumber*16+(sy Mod 8)+8)
                        pal=PeekByte(ppuram,9152+RoundDown(sy/32.0)*8+RoundDown(til/4.0)) Shl (24+2*(RoundDown(til/2+1)Mod 2)+4*(RoundDown(sy/16+1) Mod 2)) Shr 30
                        For sx=0 To 7
                            PutPixel2 til*8+sx,sy,palette(PeekByte(ppuram,16128+(((byte1 Shr (7-sx)) Mod 2))+((((byte2 Shr (7-sx)) Mod 2))Shl 1)+(pal) Shl 2))
                            'SetWindow ""+(PeekByte(ppuram,16128+(byte1 Shr (7-sx)) Mod 2+((byte2 Shr (7-sx)) Mod 2)Shl 1 +pal Shl 2))+" "+((byte1 Shr (7-sx)) Mod 2+((byte2 Shr (7-sx)) Mod 2)Shl 1 +pal Shl 2)+ " " +pal
                        Next sx
                    Next til
                Next sy
            Unlock
            DrawToScreen
        Case 1
            Select mapper
                Case 4// MMC3
                    For l=4 To 7'tilet
                        DrawToImage renderlayers(l)
                            Color cbblack
                            Box 0,0,256,240
                            j=l-4
                            For i=0 To 959
                                'DrawImageBox maskedtiles(j),i*8 Mod 256,RoundDown(i/32.0)*8,(PeekByte(ppuram,8192+i)+256)Shl 3,0,8,8
                                tile=PeekByte(ppuram,ppu_ctrl_nametable_address+i)+ppu_ctrl_bg_patterntable_tileoffset
                                If tile<=2047
                                    tile=chrbank0addr+tile
                                ElseIf tile<=4095
                                    tile=chrbank1addr-2048+tile
                                ElseIf tile<=5119
                                    tile=chrbank2addr-4096+tile
                                ElseIf tile<=6143
                                    tile=chrbank3addr-5120+tile
                                ElseIf tile<=7167
                                    tile=chrbank4addr-6144+tile
                                ElseIf tile<=8191
                                    tile=chrbank5addr-7168+tile
                                EndIf
                                Color cbwhite
                                'Print tile
                                CopyBox tile Shl 3,0,8,8,(i Shl 3) Mod 256,RoundDown(i/32.0)Shl 3,Image(maskedtiles(j)),Image(renderlayers(l))
                            Next i
                    Next l
                Default
                    For l=4 To 7'tilet
                        DrawToImage renderlayers(l)
                            Color cbblack
                            Box 0,0,256,240
                            j=l-4
                            For i=0 To 959
                                'DrawImageBox maskedtiles(j),i*8 Mod 256,RoundDown(i/32.0)*8,(PeekByte(ppuram,8192+i)+256)Shl 3,0,8,8
                                CopyBox (PeekByte(ppuram,ppu_ctrl_nametable_address+i)+ppu_ctrl_bg_patterntable_tileoffset+1)Shl 3,0,8,8,(i Shl 3) Mod 256,RoundDown(i/32.0)Shl 3,Image(maskedtiles(j)),Image(renderlayers(l))
                            Next i
                    Next l
            EndSelect
            For l=0 To 3'tausta
                DrawToImage renderlayers(l)
                    Color cbblack
                    Box 0,0,256,240
                    For i=0 To 63
                        pal=PeekByte(ppuram,9152+i)
                        Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 30) Shr 28)+l))
                        Box i*32 Mod 256, RoundDown(i/8.0)*32,16,16
                        Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 28) Shr 30)Shl 2+l))
                        Box (i*32+16) Mod 256, RoundDown(i/8.0)*32,16,16
                        Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 26) Shr 30)Shl 2+l))
                        Box i*32 Mod 256, RoundDown(i/8.0)*32+16,16,16
                        Color 0,0,palette(PeekByte(ppuram,16128+((pal Shl 24) Shr 30)Shl 2+l))
                        Box (i*32+16) Mod 256, RoundDown(i/8.0)*32+16,16,16
                    Next i
                    DrawImage renderlayers(l+4),0,0
                DrawToScreen
            Next l
            DrawToImage scr
            Color cbblack
            Box 0,0,256,240
            DrawImage renderlayers(0),0,0
            DrawImage renderlayers(1),0,0
            DrawImage renderlayers(2),0,0
            DrawImage renderlayers(3),0,0
            DrawToScreen
    EndSelect
    Color cbblack
    Box 200,0,256,240
    DrawImage scr,200,0' kaksi piirtoa koska print pistää koordinaatit sekaisin
    'DrawImage scr,200,150
    DrawScreen OFF
    Locate 300,0
    SetWindow "FPS: "+((10000/(ulkoaika+(Timer()-t)))/10.0)+" kierrosaika: "+(ulkoaika+(Timer()-t))+" Rendaus: "+(Timer()-t)
    aika=Timer()

EndFunction

Function printstatus()
    Print "A:"+A+" X:"+X+" Y:"+Y+"  N:"+N+" V:"+V+" I:"+I+" Z:"+Z+" C:"+C+"  Stack:"+S+" PC:"+Right(Hex(PC),4)+" "+pc+" ppuaddr:"+ppuaddr_addr
EndFunction

Function rendermem(alku,loppu)
    For i=alku To loppu
        Print Right(Hex(i),4)+": "+Right(Hex(readmem(i)),2)
        'Print Right(Hex(i),4)+": "+Right(hex(PeekByte(rom,i-32768)),2)
    Next i
EndFunction

Function debugmem(kohta)
    Repeat
        kohta=kohta+KeyDown(208)-KeyDown(200)+(KeyDown(209)-KeyDown(201))*8
        kohta=Min(65535,Max(kohta,0))
        rendermem(Max(kohta-8,0),Min(kohta+8,65535))
        Box 0,13*(8  ),200,1
        Box 0,13*(8+1),200,1
        Text 255,0,"S To see Chr-tables"
        if KeyDown(31) Then drawppu()
        DrawScreen
        Wait 1
        Cls
        
    Until KeyDown(57)'space
EndFunction

Function drawppu()
    Lock SCREEN()
    For sy=0 To 127'239
        For til=0 To 31
            tile=(RoundDown(sy/8.0)*32+til)*16+(sy Mod 8)'-1'+256
            'tile=PeekByte(ppuram,8192+i)+255
            Select mapper 
                Case 4
                    If tile<=2047
                        tile=chrbank0addr+tile
                    ElseIf tile<=4095
                        tile=chrbank1addr-2048+tile
                    ElseIf tile<=5119
                        tile=chrbank2addr-4096+tile
                    ElseIf tile<=6143
                        tile=chrbank3addr-5120+tile
                    ElseIf tile<=7167
                        tile=chrbank4addr-6144+tile
                    ElseIf tile<=8191
                        tile=chrbank5addr-7168+tile
                    EndIf
            EndSelect
            byte1=PeekByte(vrom,int(Max(0,tile)))
            byte2=PeekByte(vrom,tile+8)
            For sx=0 To 7
                PutPixel2 255+til*8+sx,sy,palette((((byte1 Shr (7-sx)) Mod 2))+((((byte2 Shr (7-sx)) Mod 2))Shl 1))
            Next sx
        Next til
    Next sy
    
    Unlock
    
    For sy=0 To 239 Step 8
        For til=0 To 31
            tilenumber=PeekByte(ppuram,ppu_ctrl_nametable_address+(RoundDown(sy/8.0)*32+til))
            Color 0,0,tilenumber Shl 16 +tilenumber Shl 8 + tilenumber
            Box 512+til*8,sy,8,8
            Color cbred
            For i=0 To 7
                if (tilenumber Shr (7-i))Mod 2 Then Dot 512+til*8+i,sy
            Next i

        Next til
    Next sy
    
    Color cbwhite
    Text 175,0,"$0000 chr0"
    Text 175,64,"$1000 chr1"
    Box  256,ppu_ctrl_bg_patterntable_tileoffset/4.0,256,64,0

EndFunction
diffitiedosto gnu diffillä: http://pastebin.com/raw.php?i=1SThjxqh

Code: Select all

diff -y -w --width=150 --left-column "coolnes r3.CB" "coolnes r4.CB">diff.txt
Ja lopuksi testikuvia:

Sack of flourista(MMC3-tuki): Image
debuugerista: Image

Kuvien fps:ät ovat liian isoja, koska pidin v-nappia pohjassa kuvien oton ajan. Se katoaa muuten aika äkkiä printtien takia.

Tein sellaisen huomion, että tuo debuggerin valkoinen ruutu piirtyy väärään tilesettiin sack of flourin kanssa, mutten nyt äkkiä keksinyt että mikä siinä on vikana. Kuva piirtyi kuitenkin oikein, vaikka käytän samaa muuttujaa setin valintaan :o
Ongelma saattaa poistua kun muistinhallinta muutetaan memcopyyn perustuvaksi.

Kysymyksiä ja keskustelua olisi kiva saada. Neljäs posti putkeen tuntuisi vähän ikävältä :S Tässä on kumminkin ollut välissä jo tarpeeksi aikaa ja asiaa on paljon, joten kehtasin :P
EDIT:

Korjasin diffistä väärät parametrit ja pasten, pari typoa ja loppuun lisäsin tämän kuvien jälkeen olevan osan.

User avatar
valscion
Moderator
Moderator
Posts: 1599
Joined: Thu Dec 06, 2007 7:46 pm
Location: Espoo
Contact:

Re: Nes-emulaattori ja nippelitietoa nesistä.

Post by valscion »

Täytyyhän sitä nyt atomia hädässä auttaa. Eli tässä viesti, joka katkaisee tupla/tripla/mikälie-viestikombosi.

Mielenkiinnolla seurailen että miten saat projektia edistettyä, on aina jännä nähdä miten cb-väki onnistuu repimään CoolBasicista aina vain enemmän irti :). Itseäni projektin koodaus ei houkuttele koska homma on mieleeni liian epäkäytännöllistä mutta eihän se onneksi tarkoita etteikö muita innostaisi ;)
cbEnchanted, uudelleenkirjoitettu runtime. Uusin versio: 0.4.1 — Nyt myös sorsat GitHubissa!
NetMatch - se kunnon nettimättö-deathmatch! Avoimella lähdekoodilla varustettu
vesalaakso.com
Branch of TarhaTarha

Re: Nes-emulaattori ja nippelitietoa nesistä.

Post by Branch of TarhaTarha »

http://paste.servut.us/plain/twja
Z80 prosessorin emuloinnin alku, bitmapped emulaatio. Alussa esirenderöidään kaikki mahdolliset bittisetit, kestänee 7sek, mahdollistaa todella nopean piirtämisen.
Kesken: flagien käsittely ei toimi ihan oikein, yli puolet käskyistä puuttuu, jne. Mitään tiettyä alustaa ei tällä hetkellä emuloida mutta video muisti on osoitettu $8000 alueelle renderöimään 320x200 kuva 50hz.

Jos joku haluaa jatkaa niin suosittelen http://z80.info/

En kykene halusta huolimatta koodaamaan enempää koska AAMUJA!!! Hyvää 1/12 muillekkin!
skorpioni-cb
Advanced Member
Posts: 364
Joined: Wed Dec 03, 2008 3:48 pm
Location: Turku

Re: Nes-emulaattori ja nippelitietoa nesistä.

Post by skorpioni-cb »

Branch of TarhaTarha wrote:http://paste.servut.us/plain/twja
Z80 prosessorin emuloinnin alku, bitmapped emulaatio. Alussa esirenderöidään kaikki mahdolliset bittisetit, kestänee 7sek, mahdollistaa todella nopean piirtämisen.
Kesken: flagien käsittely ei toimi ihan oikein, yli puolet käskyistä puuttuu, jne. Mitään tiettyä alustaa ei tällä hetkellä emuloida mutta video muisti on osoitettu $8000 alueelle renderöimään 320x200 kuva 50hz.

Jos joku haluaa jatkaa niin suosittelen http://z80.info/

En kykene halusta huolimatta koodaamaan enempää koska AAMUJA!!! Hyvää 1/12 muillekkin!
Ei kai haittaa, että käytän tuota z80-prosessorin koodia omassa Sega Gensis-emulaattorissa. Btw. Hyvä emu, atomi
En tiedä, mitä tiedän, mutta tiedän ettei se ole mitään kaunista.

I know not what I know, but I do know that it's not beautiful.
leal
Newcomer
Posts: 16
Joined: Wed May 30, 2012 1:46 pm

Re: Nes-emulaattori ja nippelitietoa nesistä.

Post by leal »

Post Reply